From e35cd82f5f4dab0c8edde626512aecb83825f175 Mon Sep 17 00:00:00 2001 From: Michael Brusegard <56915010+michaelbrusegard@users.noreply.github.com> Date: Fri, 18 Sep 2026 04:01:58 -0700 Subject: [PATCH 001/334] fix(auth): verify external identity providers during handoff Allow webmail-configured OAuth providers on separate hosts without trusting callback-supplied endpoints or broadly allowing sibling domains. Bind the client, JMAP server, issuer and token endpoint to HTTPS metadata from the selected webmail for both handoff and pairing. --- src/lib/__tests__/oauth-handoff.test.ts | 164 ++++++++++++++++++++++++ src/lib/oauth.ts | 62 ++++++++- 2 files changed, 222 insertions(+), 4 deletions(-) create mode 100644 src/lib/__tests__/oauth-handoff.test.ts diff --git a/src/lib/__tests__/oauth-handoff.test.ts b/src/lib/__tests__/oauth-handoff.test.ts new file mode 100644 index 00000000..328cc9b9 --- /dev/null +++ b/src/lib/__tests__/oauth-handoff.test.ts @@ -0,0 +1,164 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import * as WebBrowser from 'expo-web-browser'; + +const { mockSecureFetch } = vi.hoisted(() => ({ mockSecureFetch: vi.fn() })); +vi.mock('../client-cert', () => ({ secureFetch: mockSecureFetch })); + +import { redeemPairingCode, runWebmailHandoff } from '../oauth'; + +const WEBMAIL = 'https://webmail.example.com'; +const SERVER = 'https://mail.example.com'; +const ISSUER = 'https://id.example.net'; +const TOKEN_ENDPOINT = `${ISSUER}/token`; + +let config: Record; +let metadata: Record; +let bundle: Record; +let stateMismatch: boolean; + +function response(url: string, data: unknown): Response { + return { ok: true, status: 200, url, redirected: false, json: async () => data } as Response; +} + +beforeEach(() => { + vi.resetAllMocks(); + config = { + jmapServerUrl: SERVER, + oauthEnabled: true, + oauthClientId: 'stalwart', + oauthIssuerUrl: ISSUER, + }; + metadata = { issuer: ISSUER, token_endpoint: TOKEN_ENDPOINT }; + bundle = { + flow: 'oauth', server_url: SERVER, access_token: 'test-access', + refresh_token: 'test-refresh', client_id: 'stalwart', token_endpoint: TOKEN_ENDPOINT, + }; + stateMismatch = false; + vi.mocked(WebBrowser.openAuthSessionAsync).mockImplementation(async (url) => ({ + type: 'success', + url: `bulwarkmobile://auth/callback#${new URLSearchParams({ + ...bundle, + state: stateMismatch ? 'wrong-state' : new URL(url).searchParams.get('mobile_state')!, + })}`, + })); + mockSecureFetch.mockImplementation(async (url: string) => { + if (url === `${WEBMAIL}/api/auth/pair/redeem`) return response(url, bundle); + if (url === `${WEBMAIL}/api/config`) return response(url, config); + if (url === `${WEBMAIL}/api/auth/oauth/metadata`) return response(url, metadata); + throw new Error(`Unexpected fetch: ${url}`); + }); +}); + +afterEach(() => vi.useRealTimers()); + +for (const flow of ['handoff', 'pairing'] as const) { + const login = () => flow === 'handoff' + ? runWebmailHandoff(WEBMAIL) + : redeemPairingCode(WEBMAIL, 'test-pair-code'); + + describe(`${flow} external identity provider`, () => { + it('accepts an external token endpoint verified through the selected webmail', async () => { + await expect(login()).resolves.toMatchObject({ + flow: 'oauth', serverUrl: SERVER, + tokens: { tokenEndpoint: TOKEN_ENDPOINT, clientId: 'stalwart', source: flow }, + }); + const discoveryCalls = mockSecureFetch.mock.calls.filter(([url]) => !url.endsWith('/pair/redeem')); + expect(discoveryCalls.map(([url]) => url)).toEqual([ + `${WEBMAIL}/api/config`, `${WEBMAIL}/api/auth/oauth/metadata`, + ]); + for (const [, options] of discoveryCalls) { + expect(options).toMatchObject({ + headers: { Accept: 'application/json' }, redirect: 'error', timeoutMs: 8000, + }); + expect(options.signal).toBeInstanceOf(AbortSignal); + expect(options.body).toBeUndefined(); + } + }); + + it('also accepts a configured sibling-host provider', async () => { + config.oauthIssuerUrl = metadata.issuer = 'https://id.example.com'; + metadata.token_endpoint = bundle.token_endpoint = 'https://id.example.com/token'; + await expect(login()).resolves.toMatchObject({ flow: 'oauth' }); + }); + + it('keeps same-host login independent of webmail metadata', async () => { + bundle.token_endpoint = `${SERVER}/auth/token`; + await expect(login()).resolves.toMatchObject({ flow: 'oauth' }); + expect(mockSecureFetch.mock.calls.filter(([url]) => !url.endsWith('/pair/redeem'))).toHaveLength(0); + }); + + const mismatches: [string, () => void][] = [ + ['substituted token endpoint', () => { bundle.token_endpoint = 'https://attacker.test/token'; }], + ['lookalike token hostname', () => { bundle.token_endpoint = 'https://id.example.net.attacker.test/token'; }], + ['different token path', () => { bundle.token_endpoint = `${ISSUER}/other`; }], + ['different token query', () => { bundle.token_endpoint = `${TOKEN_ENDPOINT}?other=1`; }], + ['token URL with credentials', () => { bundle.token_endpoint = metadata.token_endpoint = 'https://user@id.example.net/token'; }], + ['token URL with a fragment', () => { bundle.token_endpoint = metadata.token_endpoint = `${TOKEN_ENDPOINT}#other`; }], + ['cleartext token endpoint', () => { bundle.token_endpoint = metadata.token_endpoint = 'http://id.example.net/token'; }], + ['different client', () => { bundle.client_id = 'different-client'; }], + ['different JMAP server', () => { bundle.server_url = 'https://other.example.com'; }], + ['disabled OAuth', () => { config.oauthEnabled = false; }], + ['non-boolean OAuth flag', () => { config.oauthEnabled = 'true'; }], + ['missing issuer', () => { delete config.oauthIssuerUrl; }], + ['cleartext issuer', () => { config.oauthIssuerUrl = metadata.issuer = 'http://id.example.net'; }], + ['issuer mismatch', () => { metadata.issuer = 'https://other-id.example.net'; }], + ['missing token endpoint', () => { delete metadata.token_endpoint; }], + ]; + it.each(mismatches)('rejects %s', async (_name, mutate) => { + mutate(); + await expect(login()).rejects.toThrow('token endpoint is not trusted'); + }); + + it.each(['/api/config', '/api/auth/oauth/metadata'])('fails closed when %s cannot be fetched', async (path) => { + const normal = mockSecureFetch.getMockImplementation()!; + mockSecureFetch.mockImplementation((url, options) => { + if (url.endsWith(path)) throw new Error('Network unavailable'); + return normal(url, options); + }); + await expect(login()).rejects.toThrow('token endpoint is not trusted'); + }); + + const badResponses: [string, Partial][] = [ + ['HTTP error', { ok: false, status: 503 }], + ['cross-origin redirect', { url: 'https://attacker.test/api/config', redirected: true }], + ['same-origin redirect', { url: `${WEBMAIL}/different`, redirected: true }], + ['redirect back to the requested URL', { url: `${WEBMAIL}/api/config`, redirected: true }], + ['missing final URL (native client-certificate bridge)', { url: '' }], + ['invalid JSON', { json: async () => { throw new SyntaxError('Invalid JSON'); } }], + ]; + it.each(badResponses)('fails closed for metadata with %s', async (_name, overrides) => { + const normal = mockSecureFetch.getMockImplementation()!; + mockSecureFetch.mockImplementation(async (url, options) => { + const result = await normal(url, options); + return url.endsWith('/pair/redeem') ? result : { ...result, ...overrides }; + }); + await expect(login()).rejects.toThrow('token endpoint is not trusted'); + }); + + it('aborts metadata discovery after its deadline', async () => { + vi.useFakeTimers(); + const normal = mockSecureFetch.getMockImplementation()!; + mockSecureFetch.mockImplementation((url, options) => { + if (!url.endsWith('/api/config')) return normal(url, options); + return new Promise((_resolve, reject) => options.signal.addEventListener('abort', () => reject(new Error('Aborted')))); + }); + const result = expect(login()).rejects.toThrow('token endpoint is not trusted'); + await vi.advanceTimersByTimeAsync(8000); + await result; + }); + }); +} + +it('rejects a mismatched callback state before fetching metadata', async () => { + stateMismatch = true; + await expect(runWebmailHandoff(WEBMAIL)).rejects.toThrow('State mismatch'); + expect(mockSecureFetch).not.toHaveBeenCalled(); +}); + +it('leaves password handoff unchanged', async () => { + bundle = { flow: 'password', server_url: SERVER, username: 'user@example.com', password: 'test-password' }; + await expect(runWebmailHandoff(WEBMAIL)).resolves.toEqual({ + flow: 'password', serverUrl: SERVER, username: 'user@example.com', password: 'test-password', + }); + expect(mockSecureFetch).not.toHaveBeenCalled(); +}); diff --git a/src/lib/oauth.ts b/src/lib/oauth.ts index 31ba28d2..01759fc4 100644 --- a/src/lib/oauth.ts +++ b/src/lib/oauth.ts @@ -63,8 +63,8 @@ function randomState(): string { } // Anything from the redirect fragment is attacker-influenced: another app can -// register the same custom scheme. Only accept https endpoints, and only a -// token endpoint that lives on the mail server's own host (or the webmail's). +// register the same custom scheme. External token endpoints need verification +// through the selected webmail, rather than trusting the redirect alone. function isHttpsUrl(value: string | null): value is string { return !!value && /^https:\/\/[^/?#\s]+/i.test(value); } @@ -92,6 +92,60 @@ export function isAcceptableTokenEndpoint( return allowed.some((h) => host === h || host.endsWith(`.${h}`) || h.endsWith(`.${host}`)); } +// External IdPs must be authorized by the selected webmail over TLS, never +// by callback fields or a shared DNS suffix. Bind all returned identifiers. +async function isTrustedHandoffEndpoint( + tokenEndpoint: string, + serverUrl: string, + webmailUrl: string, + clientId: string, +): Promise { + if (isAcceptableTokenEndpoint(tokenEndpoint, serverUrl, webmailUrl)) return true; + function httpsUrl(value: unknown): value is string { + if (typeof value !== 'string') return false; + try { + const url = new URL(value); + return url.protocol === 'https:' && !url.username && !url.password && !url.hash; + } catch { + return false; + } + } + if (!httpsUrl(webmailUrl) || !httpsUrl(tokenEndpoint) || !httpsUrl(serverUrl)) return false; + const base = webmailUrl.replace(/\/+$/, ''); + try { + const load = async (path: string) => { + const url = `${base}${path}`; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 8000); + try { + const response = await secureFetch(url, { + headers: { Accept: 'application/json' }, + redirect: 'error', + signal: controller.signal, + timeoutMs: 8000, + }); + // The native client-certificate bridge omits the final URL. Without + // transport evidence of its destination, external-IdP trust must fail. + if (!response.ok || response.url !== url || response.redirected) { + throw new Error('Untrusted metadata response'); + } + return await response.json(); + } finally { + clearTimeout(timeout); + } + }; + const config = await load('/api/config'); + if ( + config.oauthEnabled !== true || config.oauthClientId !== clientId || + config.jmapServerUrl !== serverUrl || !httpsUrl(config.oauthIssuerUrl) + ) return false; + const metadata = await load('/api/auth/oauth/metadata'); + return metadata.issuer === config.oauthIssuerUrl && metadata.token_endpoint === tokenEndpoint; + } catch { + return false; + } +} + function buildHandoffUrl(webmailUrl: string, state: string): string { const base = webmailUrl.replace(/\/+$/, ''); const params = new URLSearchParams({ @@ -154,7 +208,7 @@ export async function runWebmailHandoff(webmailUrl: string): Promise Date: Thu, 24 Sep 2026 09:47:15 +0200 Subject: [PATCH 002/334] fix(calendar): join the meeting and open the location from the event sheet Invitations - Teams above all - leave virtualLocations empty: LOCATION reads "Microsoft Teams Meeting" and the join URL sits in the description between help and dial-in links, so the sheet showed no meeting button. Find the join URL of known conferencing services (Teams, Zoom, Meet, Webex, Jitsi, Whereby, GoTo) in the location or description, unwrapping Outlook Safe Links, and offer it as the meeting link. Nothing is written to the event. The location was plain text. Tapping it now opens it: a URL opens as a link, a location that only names the service joins the meeting, anything else is searched in the maps app (geo: intent on Android, Google Maps universal link elsewhere). Long-press copies it. Co-Authored-By: Claude Opus 5.5 --- locales/rn/en.json | 3 + src/components/calendar/EventDetailSheet.tsx | 51 ++++++- src/lib/__tests__/event-links.test.ts | 123 ++++++++++++++++ src/lib/event-links.ts | 140 +++++++++++++++++++ 4 files changed, 313 insertions(+), 4 deletions(-) create mode 100644 src/lib/__tests__/event-links.test.ts create mode 100644 src/lib/event-links.ts diff --git a/locales/rn/en.json b/locales/rn/en.json index a629c21a..0cddce4a 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -10,6 +10,9 @@ "days_clear": "The next {count} days are clear.", "no_upcoming": "No upcoming events" }, + "detail": { + "location_hint": "Opens the location; long-press to copy it" + }, "drawer": { "my_calendars": "My calendars", "no_calendars": "No calendars yet.", diff --git a/src/components/calendar/EventDetailSheet.tsx b/src/components/calendar/EventDetailSheet.tsx index f5a5a839..ba1faf0b 100644 --- a/src/components/calendar/EventDetailSheet.tsx +++ b/src/components/calendar/EventDetailSheet.tsx @@ -4,14 +4,18 @@ import { Dimensions, Easing, Modal, + Platform, Pressable, ScrollView, StyleSheet, Text, + ToastAndroid, View, } from 'react-native'; +import * as Clipboard from 'expo-clipboard'; import { openExternalUrl } from '../../lib/open-url'; import { splitTextLinks } from '../../lib/linkify-text'; +import { findMeetingLink, locationAction, mapsUrl, primaryLocationName } from '../../lib/event-links'; import { SafeAreaView } from 'react-native-safe-area-context'; import { AlignLeft, @@ -194,8 +198,11 @@ export function EventDetailSheet({ const recurrence = recurrenceLabel(event, t, locale); const reminders = alertsToReminders(event.alerts); const participants = getParticipantList(event); - const location = event.locations ? Object.values(event.locations)[0]?.name : undefined; - const videoUri = event.virtualLocations ? Object.values(event.virtualLocations)[0]?.uri : undefined; + const location = primaryLocationName(event); + // Invitations (Teams above all) leave virtualLocations empty and bury the + // join URL in the description; surface it as the meeting link anyway. + const meeting = findMeetingLink(event); + const videoUri = meeting?.uri; const isCancelled = event.status === 'cancelled'; // Can the signed-in user RSVP? Only when they appear as a non-organizer @@ -217,6 +224,25 @@ export function EventDetailSheet({ ); const myStatus = myParticipantId ? event.participants?.[myParticipantId]?.participationStatus : undefined; + const openLocation = () => { + if (!location) return; + const action = locationAction(location, meeting); + if (action.kind === 'url') { + void openExternalUrl(action.uri, { confirm: true }); + } else { + void openExternalUrl(mapsUrl(action.query, Platform.OS)); + } + }; + + const copyLocation = () => { + if (!location) return; + void Clipboard.setStringAsync(location).then(() => { + if (Platform.OS === 'android') { + ToastAndroid.show(t('notifications.copied_to_clipboard', 'Copied to clipboard'), ToastAndroid.SHORT); + } + }).catch(() => {}); + }; + const doRsvp = async (status: RsvpStatus) => { if (!onRsvp || !myParticipantId || rsvpBusy) return; setRsvpBusy(true); @@ -267,7 +293,21 @@ export function EventDetailSheet({ > } text={range} /> {location ? ( - } text={location} /> + + + + + + {location} + + ) : null} {videoUri ? ( @@ -278,7 +318,10 @@ export function EventDetailSheet({ style={styles.joinBtn} onPress={() => { void openExternalUrl(videoUri, { confirm: true }); }} > - {t('calendar.detail.open_link', 'Open link')} + + {t('calendar.detail.open_link', 'Open link')} + {meeting?.provider ? ` · ${meeting.provider}` : ''} + ) : null} diff --git a/src/lib/__tests__/event-links.test.ts b/src/lib/__tests__/event-links.test.ts new file mode 100644 index 00000000..a987bc8f --- /dev/null +++ b/src/lib/__tests__/event-links.test.ts @@ -0,0 +1,123 @@ +import { describe, it, expect } from 'vitest'; +import { + findMeetingLink, + isMeetingLabel, + locationAction, + mapsUrl, + meetingProviderOf, + primaryLocationName, + unwrapSafeLink, +} from '../event-links'; + +/** + * A Teams invitation fills LOCATION with "Microsoft Teams Meeting" and leaves + * the join URL in the description, next to help and dial-in links. The sheet + * must still offer a single "Join" button, and a real address must open the + * maps app. + */ + +const TEAMS_JOIN = 'https://teams.microsoft.com/l/meetup-join/19%3ameeting_NjQ5%40thread.v2/0?context=%7b%22Tid%22%3a%22abc%22%7d'; + +const TEAMS_DESCRIPTION = [ + '________________________________________________________________________________', + 'Réunion Microsoft Teams', + 'Rejoindre sur votre ordinateur, application mobile ou appareil de salle', + `Cliquez ici pour participer à la réunion<${TEAMS_JOIN}>`, + 'ID de la réunion : 319 560 123 45', + 'Code secret : CTYa9', + 'Télécharger Teams | Rejoindre sur le web', + 'En savoir plus | Options de réunion', +].join('\n'); + +describe('findMeetingLink', () => { + it('prefers the structured virtual location', () => { + expect(findMeetingLink({ + virtualLocations: { v: { uri: 'https://zoom.us/j/123' } }, + description: TEAMS_DESCRIPTION, + })).toEqual({ uri: 'https://zoom.us/j/123', provider: 'Zoom', derived: false }); + }); + + it('digs the Teams join URL out of the description, skipping help links', () => { + expect(findMeetingLink({ + locations: { l: { name: 'Réunion Microsoft Teams' } }, + description: TEAMS_DESCRIPTION, + })).toEqual({ uri: TEAMS_JOIN, provider: 'Teams', derived: true }); + }); + + it('recognises the short Teams, Meet and Zoom forms', () => { + expect(findMeetingLink({ description: 'Join: https://teams.microsoft.com/meet/31956012345?p=CTYa9' })?.provider).toBe('Teams'); + expect(findMeetingLink({ description: 'Meet: https://meet.google.com/abc-defg-hij.' })?.uri).toBe('https://meet.google.com/abc-defg-hij'); + expect(findMeetingLink({ description: 'https://us02web.zoom.us/j/8812345?pwd=xyz' })?.provider).toBe('Zoom'); + }); + + it('takes a meeting URL used as the location', () => { + expect(findMeetingLink({ locations: { l: { name: 'https://meet.jit.si/standup' } } })?.uri).toBe('https://meet.jit.si/standup'); + }); + + it('unwraps Outlook Safe Links', () => { + const wrapped = `https://eur01.safelinks.protection.outlook.com/?url=${encodeURIComponent(TEAMS_JOIN)}&data=05`; + expect(findMeetingLink({ description: `Join<${wrapped}>` })?.uri).toBe(TEAMS_JOIN); + }); + + it('ignores ordinary links', () => { + expect(findMeetingLink({ description: 'Agenda: https://docs.example.com/agenda https://aka.ms/JoinTeamsMeeting' })).toBeNull(); + expect(findMeetingLink({})).toBeNull(); + }); + + it('skips a virtual location that is not a web URL', () => { + expect(findMeetingLink({ + virtualLocations: { v: { uri: 'tel:+33123456789' } }, + description: 'https://meet.google.com/abc-defg-hij', + })?.provider).toBe('Google Meet'); + }); +}); + +describe('meetingProviderOf / unwrapSafeLink', () => { + it('rejects look-alike hosts', () => { + expect(meetingProviderOf('https://teams.microsoft.com.evil.example/l/meetup-join/x')).toBeNull(); + expect(meetingProviderOf('https://notzoom.us/j/1')).toBeNull(); + }); + + it('leaves non-wrapped URLs alone', () => { + expect(unwrapSafeLink('https://example.com/?url=https://x.y')).toBe('https://example.com/?url=https://x.y'); + }); +}); + +describe('locationAction', () => { + const meeting = { uri: TEAMS_JOIN, provider: 'Teams', derived: true }; + + it('searches the maps for a postal address', () => { + expect(locationAction('12 rue de la Paix, 75002 Paris', meeting)).toEqual({ kind: 'maps', query: '12 rue de la Paix, 75002 Paris' }); + }); + + it('joins the meeting when the location only names the service', () => { + expect(locationAction('Réunion Microsoft Teams', meeting)).toEqual({ kind: 'url', uri: TEAMS_JOIN }); + expect(locationAction('Microsoft Teams Meeting', null)).toEqual({ kind: 'maps', query: 'Microsoft Teams Meeting' }); + }); + + it('opens a URL location', () => { + expect(locationAction(' https://example.com/room ', null)).toEqual({ kind: 'url', uri: 'https://example.com/room' }); + }); + + it('keeps a numbered room as a place', () => { + expect(isMeetingLabel('Salle Teams 3')).toBe(false); + expect(isMeetingLabel('Zoom')).toBe(true); + }); +}); + +describe('mapsUrl', () => { + it('uses the geo: intent on Android', () => { + expect(mapsUrl('12 rue de la Paix,\n75002 Paris', 'android')).toBe('geo:0,0?q=12%20rue%20de%20la%20Paix%2C%2075002%20Paris'); + }); + + it('uses the Google Maps universal link elsewhere', () => { + expect(mapsUrl('Gare de Lyon', 'ios')).toBe('https://www.google.com/maps/search/?api=1&query=Gare%20de%20Lyon'); + }); +}); + +describe('primaryLocationName', () => { + it('trims and drops blank names', () => { + expect(primaryLocationName({ locations: { a: { name: ' ' } } })).toBeUndefined(); + expect(primaryLocationName({ locations: { a: { name: ' Lyon ' } } })).toBe('Lyon'); + }); +}); diff --git a/src/lib/event-links.ts b/src/lib/event-links.ts new file mode 100644 index 00000000..b7b94eff --- /dev/null +++ b/src/lib/event-links.ts @@ -0,0 +1,140 @@ +/** + * What an event's place fields should do when tapped. + * + * Meeting invitations rarely fill `virtualLocations`: a Teams invite arrives + * with LOCATION set to "Microsoft Teams Meeting" and the join URL buried in + * the description, among dial-in numbers and help links. `findMeetingLink` + * digs it out so the sheet can offer one "Join" button without touching the + * event on the server (it is someone else's invitation). + * + * A physical address is handed to the platform's maps app instead. + */ + +import type { CalendarEvent } from '../api/types'; + +export interface MeetingLink { + uri: string; + /** Display name of the conferencing service, when recognised. */ + provider?: string; + /** True when the link was found in the description, not a structured field. */ + derived: boolean; +} + +// Hosts whose URLs are joinable meetings. The path test keeps help pages, +// "learn more" and dial-in links on the same host out of the result. +const PROVIDERS: Array<{ name: string; host: RegExp; path?: RegExp }> = [ + { name: 'Teams', host: /^teams\.(microsoft|live)\.com$/, path: /^\/(l\/meetup-join|meet)\//i }, + { name: 'Zoom', host: /(^|\.)zoom\.us$/, path: /^\/(j|w|my|s)\//i }, + { name: 'Google Meet', host: /^meet\.google\.com$/, path: /^\/[a-z]{3}-[a-z]{4}-[a-z]{3}/i }, + { name: 'Webex', host: /(^|\.)webex\.com$/, path: /^\/(meet|join|[^/]+\/j\.php)/i }, + { name: 'Jitsi', host: /^meet\.jit\.si$/, path: /^\/./ }, + { name: 'Whereby', host: /^whereby\.com$/, path: /^\/./ }, + { name: 'GoTo', host: /^(meet\.goto\.com|global\.gotomeeting\.com|app\.gotomeeting\.com)$/, path: /^\/./ }, + { name: 'Visio', host: /^(visio|webconf)\.numerique\.gouv\.fr$/, path: /^\/./ }, +]; + +const URL_RE = /https?:\/\/[^\s<>"']+/g; + +const isHttpUrl = (value: string | undefined | null): value is string => + !!value && /^https?:\/\/\S+$/i.test(value.trim()); + +/** + * Outlook's Safe Links and similar gateways wrap the real URL in a query + * parameter; unwrap one level so the provider can be recognised. + */ +export function unwrapSafeLink(uri: string): string { + try { + const u = new URL(uri); + if (/(^|\.)safelinks\.protection\.outlook\.com$/i.test(u.hostname)) { + const inner = u.searchParams.get('url'); + if (inner && isHttpUrl(inner)) return inner; + } + } catch { + // not a parseable URL - keep as is + } + return uri; +} + +/** The conferencing service a URL joins, or null for any other link. */ +export function meetingProviderOf(uri: string): string | null { + let u: URL; + try { + u = new URL(unwrapSafeLink(uri)); + } catch { + return null; + } + const host = u.hostname.toLowerCase(); + const provider = PROVIDERS.find((p) => p.host.test(host) && (!p.path || p.path.test(u.pathname))); + return provider ? provider.name : null; +} + +function firstMeetingUrl(text: string | undefined | null): MeetingLink | null { + if (!text) return null; + for (const raw of text.match(URL_RE) ?? []) { + // Trailing punctuation belongs to the sentence, not the URL. + const candidate = unwrapSafeLink(raw.replace(/[).,;:!?\]]+$/, '')); + const provider = meetingProviderOf(candidate); + if (provider) return { uri: candidate, provider, derived: true }; + } + return null; +} + +/** + * The link that joins the meeting: the event's own virtual location first, + * then a URL used as the location, then a known conferencing URL found in the + * location or the description. + */ +export function findMeetingLink(event: Pick): MeetingLink | null { + for (const vl of Object.values(event.virtualLocations ?? {})) { + if (isHttpUrl(vl?.uri)) { + return { uri: vl.uri.trim(), provider: meetingProviderOf(vl.uri) ?? undefined, derived: false }; + } + } + const locations = Object.values(event.locations ?? {}); + for (const loc of locations) { + const found = firstMeetingUrl(loc?.name) ?? firstMeetingUrl(loc?.description); + if (found) return found; + } + return firstMeetingUrl(event.description); +} + +/** The first location's display name, if any. */ +export function primaryLocationName(event: Pick): string | undefined { + const name = Object.values(event.locations ?? {})[0]?.name?.trim(); + return name || undefined; +} + +// "Microsoft Teams Meeting", "Réunion Microsoft Teams", "Zoom", "Google Meet"… +// A location like this names the service, not a place: tapping it should join +// the meeting, never search the maps for it. +const MEETING_LABEL_RE = /\b(microsoft teams|teams|zoom|google meet|webex|jitsi|whereby|gotomeeting|visio(conf[ée]rence)?|online|en ligne)\b/i; + +export function isMeetingLabel(location: string): boolean { + return MEETING_LABEL_RE.test(location) && !/\d/.test(location); +} + +export type LocationAction = + | { kind: 'url'; uri: string } + | { kind: 'maps'; query: string }; + +/** + * What tapping the location does: open it when it is a URL, join the meeting + * when it only names the service, search the maps otherwise. + */ +export function locationAction(location: string, meeting: MeetingLink | null): LocationAction { + const value = location.trim(); + if (isHttpUrl(value)) return { kind: 'url', uri: value }; + if (meeting && isMeetingLabel(value)) return { kind: 'url', uri: meeting.uri }; + return { kind: 'maps', query: value }; +} + +/** + * A maps URL for a free-text address. Android's `geo:` intent opens Google + * Maps (or the user's chosen maps app) straight on the search; elsewhere the + * Google Maps universal link opens the app when installed, the site otherwise. + */ +export function mapsUrl(query: string, platform: string): string { + const q = encodeURIComponent(query.replace(/\s+/g, ' ').trim()); + if (platform === 'android') return `geo:0,0?q=${q}`; + return `https://www.google.com/maps/search/?api=1&query=${q}`; +} From bd1b2f3fd3d7e7933330faacc04f680bf3b61d19 Mon Sep 17 00:00:00 2001 From: lucletoffe <15689941+lucletoffe@users.noreply.github.com> Date: Thu, 24 Sep 2026 09:59:58 +0200 Subject: [PATCH 003/334] fix(calendar): keep app-scheme meeting links and match service labels exactly A virtual location in an app scheme (msteams:, zoommtg:) is kept as a last resort instead of hiding the meeting row; script schemes never are. A location only joins the meeting when the whole label is a service name, so "Salle Teams" still opens the maps. Recognise Zoom's /wc/ join path. Co-Authored-By: Claude Opus 5.5 --- src/lib/__tests__/event-links.test.ts | 21 ++++++++++-- src/lib/event-links.ts | 49 +++++++++++++++++++++------ 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/lib/__tests__/event-links.test.ts b/src/lib/__tests__/event-links.test.ts index a987bc8f..72c78673 100644 --- a/src/lib/__tests__/event-links.test.ts +++ b/src/lib/__tests__/event-links.test.ts @@ -64,6 +64,17 @@ describe('findMeetingLink', () => { expect(findMeetingLink({})).toBeNull(); }); + it('falls back to an app-scheme virtual location, never a script one', () => { + expect(findMeetingLink({ virtualLocations: { v: { uri: 'msteams:/l/meetup-join/19%3a1' } } })) + .toEqual({ uri: 'msteams:/l/meetup-join/19%3a1', derived: false }); + expect(findMeetingLink({ virtualLocations: { v: { uri: 'javascript:alert(1)' } } })).toBeNull(); + expect(findMeetingLink({ virtualLocations: { v: { uri: 'data:text/html,x' } } })).toBeNull(); + }); + + it('recognises the Zoom web-client join path', () => { + expect(findMeetingLink({ description: 'https://acme.zoom.us/wc/join/8812345?pwd=x' })?.provider).toBe('Zoom'); + }); + it('skips a virtual location that is not a web URL', () => { expect(findMeetingLink({ virtualLocations: { v: { uri: 'tel:+33123456789' } }, @@ -99,9 +110,13 @@ describe('locationAction', () => { expect(locationAction(' https://example.com/room ', null)).toEqual({ kind: 'url', uri: 'https://example.com/room' }); }); - it('keeps a numbered room as a place', () => { - expect(isMeetingLabel('Salle Teams 3')).toBe(false); - expect(isMeetingLabel('Zoom')).toBe(true); + it('only treats a bare service name as a meeting', () => { + for (const label of ['Zoom', 'Réunion Microsoft Teams', 'Microsoft Teams Meeting', 'Google Meet', 'En ligne', 'Visioconférence']) { + expect(isMeetingLabel(label)).toBe(true); + } + for (const label of ['Salle Teams 3', 'Salle Teams', 'Studio Zoom', 'Salle Visio', 'Café en ligne droite, Paris']) { + expect(isMeetingLabel(label)).toBe(false); + } }); }); diff --git a/src/lib/event-links.ts b/src/lib/event-links.ts index b7b94eff..1bc03ad6 100644 --- a/src/lib/event-links.ts +++ b/src/lib/event-links.ts @@ -10,7 +10,12 @@ * A physical address is handed to the platform's maps app instead. */ -import type { CalendarEvent } from '../api/types'; +/** The place fields read here, loose enough for any event shape. */ +export interface EventPlaces { + locations?: Record | null; + virtualLocations?: Record | null; + description?: string | null; +} export interface MeetingLink { uri: string; @@ -24,7 +29,7 @@ export interface MeetingLink { // "learn more" and dial-in links on the same host out of the result. const PROVIDERS: Array<{ name: string; host: RegExp; path?: RegExp }> = [ { name: 'Teams', host: /^teams\.(microsoft|live)\.com$/, path: /^\/(l\/meetup-join|meet)\//i }, - { name: 'Zoom', host: /(^|\.)zoom\.us$/, path: /^\/(j|w|my|s)\//i }, + { name: 'Zoom', host: /(^|\.)zoom\.us$/, path: /^\/(j|w|wc|my|s)\//i }, { name: 'Google Meet', host: /^meet\.google\.com$/, path: /^\/[a-z]{3}-[a-z]{4}-[a-z]{3}/i }, { name: 'Webex', host: /(^|\.)webex\.com$/, path: /^\/(meet|join|[^/]+\/j\.php)/i }, { name: 'Jitsi', host: /^meet\.jit\.si$/, path: /^\/./ }, @@ -33,6 +38,8 @@ const PROVIDERS: Array<{ name: string; host: RegExp; path?: RegExp }> = [ { name: 'Visio', host: /^(visio|webconf)\.numerique\.gouv\.fr$/, path: /^\/./ }, ]; +const UNSAFE_SCHEMES = new Set(['javascript', 'data', 'vbscript', 'file', 'blob', 'about']); + const URL_RE = /https?:\/\/[^\s<>"']+/g; const isHttpUrl = (value: string | undefined | null): value is string => @@ -84,10 +91,11 @@ function firstMeetingUrl(text: string | undefined | null): MeetingLink | null { * then a URL used as the location, then a known conferencing URL found in the * location or the description. */ -export function findMeetingLink(event: Pick): MeetingLink | null { +export function findMeetingLink(event: EventPlaces): MeetingLink | null { for (const vl of Object.values(event.virtualLocations ?? {})) { - if (isHttpUrl(vl?.uri)) { - return { uri: vl.uri.trim(), provider: meetingProviderOf(vl.uri) ?? undefined, derived: false }; + const uri = vl?.uri; + if (isHttpUrl(uri)) { + return { uri: uri.trim(), provider: meetingProviderOf(uri) ?? undefined, derived: false }; } } const locations = Object.values(event.locations ?? {}); @@ -95,22 +103,43 @@ export function findMeetingLink(event: Pick): string | undefined { +export function primaryLocationName(event: Pick): string | undefined { const name = Object.values(event.locations ?? {})[0]?.name?.trim(); return name || undefined; } // "Microsoft Teams Meeting", "Réunion Microsoft Teams", "Zoom", "Google Meet"… // A location like this names the service, not a place: tapping it should join -// the meeting, never search the maps for it. -const MEETING_LABEL_RE = /\b(microsoft teams|teams|zoom|google meet|webex|jitsi|whereby|gotomeeting|visio(conf[ée]rence)?|online|en ligne)\b/i; +// the meeting, never search the maps for it. The whole label must be a service +// name, so "Salle Teams" or "Studio Zoom" stay places. +const MEETING_LABELS = new Set([ + 'microsoft teams', 'teams', 'zoom', 'google meet', 'meet', 'webex', 'jitsi', 'whereby', + 'gotomeeting', 'goto', 'visio', 'visioconference', 'online', 'en ligne', +]); export function isMeetingLabel(location: string): boolean { - return MEETING_LABEL_RE.test(location) && !/\d/.test(location); + const label = location + .normalize('NFD').replace(/[\u0300-\u036f]/g, '') + .toLowerCase() + .replace(/[^a-z ]+/g, ' ') + .replace(/\s+/g, ' ') + .trim() + .replace(/^(reunion|meeting|online meeting|appel|call) /, '') + .replace(/ (meeting|reunion|call)$/, ''); + return MEETING_LABELS.has(label); } export type LocationAction = From c165fef588f17b9aa39d2536e00ea0b228f006c8 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sat, 26 Sep 2026 18:53:25 +0200 Subject: [PATCH 004/334] feat: add android home-screen widgets for mail, calendar, tasks and files --- App.tsx | 37 +- android/app/src/main/AndroidManifest.xml | 227 ++++ .../bulwarkmobile/widget/WidgetProviders.kt | 40 + .../drawable-nodpi/agendawidget_preview.png | Bin 0 -> 39230 bytes .../allaccountswidget_preview.png | Bin 0 -> 25947 bytes .../attachmentswidget_preview.png | Bin 0 -> 20239 bytes .../birthdayswidget_preview.png | Bin 0 -> 19024 bytes .../countdownwidget_preview.png | Bin 0 -> 8823 bytes .../drawable-nodpi/datenextwidget_preview.png | Bin 0 -> 12960 bytes .../favouritepeoplewidget_preview.png | Bin 0 -> 20034 bytes .../foldercountswidget_preview.png | Bin 0 -> 11205 bytes .../drawable-nodpi/freetimewidget_preview.png | Bin 0 -> 15301 bytes .../drawable-nodpi/inboxwidget_preview.png | Bin 0 -> 22302 bytes .../invitationswidget_preview.png | Bin 0 -> 19864 bytes .../lastdraftwidget_preview.png | Bin 0 -> 10961 bytes .../latestmessagewidget_preview.png | Bin 0 -> 13580 bytes .../mailandnextwidget_preview.png | Bin 0 -> 20339 bytes .../mailsearchwidget_preview.png | Bin 0 -> 12578 bytes .../mailshortcutswidget_preview.png | Bin 0 -> 10084 bytes .../drawable-nodpi/monthwidget_preview.png | Bin 0 -> 29056 bytes .../nexteventwidget_preview.png | Bin 0 -> 10513 bytes .../drawable-nodpi/outboxwidget_preview.png | Bin 0 -> 22210 bytes .../recentfileswidget_preview.png | Bin 0 -> 18555 bytes .../drawable-nodpi/starredwidget_preview.png | Bin 0 -> 18993 bytes .../drawable-nodpi/storagewidget_preview.png | Bin 0 -> 8043 bytes .../res/drawable-nodpi/tagwidget_preview.png | Bin 0 -> 7701 bytes .../taskprogresswidget_preview.png | Bin 0 -> 7453 bytes .../drawable-nodpi/taskswidget_preview.png | Bin 0 -> 39025 bytes .../todayeventswidget_preview.png | Bin 0 -> 21401 bytes .../drawable-nodpi/todayhubwidget_preview.png | Bin 0 -> 41982 bytes .../drawable-nodpi/triagewidget_preview.png | Bin 0 -> 23121 bytes .../unreadcountwidget_preview.png | Bin 0 -> 9430 bytes .../drawable-nodpi/upnextwidget_preview.png | Bin 0 -> 18384 bytes .../drawable-nodpi/vacationwidget_preview.png | Bin 0 -> 13264 bytes .../res/drawable-nodpi/weekwidget_preview.png | Bin 0 -> 13198 bytes .../src/main/res/values/widget_strings.xml | 68 ++ .../res/xml/widgetprovider_agendawidget.xml | 16 + .../xml/widgetprovider_allaccountswidget.xml | 16 + .../xml/widgetprovider_attachmentswidget.xml | 16 + .../xml/widgetprovider_birthdayswidget.xml | 16 + .../xml/widgetprovider_countdownwidget.xml | 16 + .../res/xml/widgetprovider_datenextwidget.xml | 16 + .../widgetprovider_favouritepeoplewidget.xml | 16 + .../xml/widgetprovider_foldercountswidget.xml | 16 + .../res/xml/widgetprovider_freetimewidget.xml | 16 + .../res/xml/widgetprovider_inboxwidget.xml | 16 + .../xml/widgetprovider_invitationswidget.xml | 16 + .../xml/widgetprovider_lastdraftwidget.xml | 16 + .../widgetprovider_latestmessagewidget.xml | 16 + .../xml/widgetprovider_mailandnextwidget.xml | 16 + .../xml/widgetprovider_mailsearchwidget.xml | 16 + .../widgetprovider_mailshortcutswidget.xml | 16 + .../res/xml/widgetprovider_monthwidget.xml | 16 + .../xml/widgetprovider_nexteventwidget.xml | 16 + .../res/xml/widgetprovider_outboxwidget.xml | 16 + .../xml/widgetprovider_recentfileswidget.xml | 16 + .../res/xml/widgetprovider_starredwidget.xml | 16 + .../res/xml/widgetprovider_storagewidget.xml | 16 + .../main/res/xml/widgetprovider_tagwidget.xml | 16 + .../xml/widgetprovider_taskprogresswidget.xml | 16 + .../res/xml/widgetprovider_taskswidget.xml | 16 + .../xml/widgetprovider_todayeventswidget.xml | 16 + .../res/xml/widgetprovider_todayhubwidget.xml | 16 + .../res/xml/widgetprovider_triagewidget.xml | 16 + .../xml/widgetprovider_unreadcountwidget.xml | 16 + .../res/xml/widgetprovider_upnextwidget.xml | 16 + .../res/xml/widgetprovider_vacationwidget.xml | 16 + .../res/xml/widgetprovider_weekwidget.xml | 16 + index.ts | 13 +- locales/rn/en.json | 183 +++ package-lock.json | 17 + package.json | 4 +- scripts/sync-android-widgets.mjs | 193 +++ src/lib/reply-compose.ts | 50 + src/navigation/__tests__/linking.test.ts | 47 + src/navigation/linking.ts | 74 +- src/navigation/pending-mail-search.ts | 25 + src/screens/EmailListScreen.tsx | 13 + src/screens/EmailThreadScreen.tsx | 39 +- src/stores/email-store.ts | 5 +- src/widgets/__tests__/derive.test.ts | 117 ++ src/widgets/__tests__/jmap.test.ts | 63 + src/widgets/__tests__/layouts.test.ts | 125 ++ src/widgets/actions.ts | 163 +++ src/widgets/build.ts | 484 ++++++++ src/widgets/catalog.json | 40 + src/widgets/clicks.ts | 60 + src/widgets/derive.ts | 209 ++++ src/widgets/format.ts | 132 ++ src/widgets/icons.ts | 100 ++ src/widgets/jmap.ts | 450 +++++++ src/widgets/layouts/calendar.tsx | 1061 +++++++++++++++++ src/widgets/layouts/hubs.tsx | 806 +++++++++++++ src/widgets/layouts/mail.tsx | 291 +++++ src/widgets/layouts/more-mail.tsx | 738 ++++++++++++ src/widgets/layouts/plans.tsx | 665 +++++++++++ src/widgets/layouts/types.ts | 21 + src/widgets/local-state.ts | 26 + src/widgets/parts.tsx | 276 +++++ src/widgets/previews/AgendaWidget.png | Bin 0 -> 39230 bytes src/widgets/previews/AllAccountsWidget.png | Bin 0 -> 25947 bytes src/widgets/previews/AttachmentsWidget.png | Bin 0 -> 20239 bytes src/widgets/previews/BirthdaysWidget.png | Bin 0 -> 19024 bytes src/widgets/previews/CountdownWidget.png | Bin 0 -> 8823 bytes src/widgets/previews/DateNextWidget.png | Bin 0 -> 12960 bytes .../previews/FavouritePeopleWidget.png | Bin 0 -> 20034 bytes src/widgets/previews/FolderCountsWidget.png | Bin 0 -> 11205 bytes src/widgets/previews/FreeTimeWidget.png | Bin 0 -> 15301 bytes src/widgets/previews/InboxWidget.png | Bin 0 -> 22302 bytes src/widgets/previews/InvitationsWidget.png | Bin 0 -> 19864 bytes src/widgets/previews/LastDraftWidget.png | Bin 0 -> 10961 bytes src/widgets/previews/LatestMessageWidget.png | Bin 0 -> 13580 bytes src/widgets/previews/MailAndNextWidget.png | Bin 0 -> 20339 bytes src/widgets/previews/MailSearchWidget.png | Bin 0 -> 12578 bytes src/widgets/previews/MailShortcutsWidget.png | Bin 0 -> 10084 bytes src/widgets/previews/MonthWidget.png | Bin 0 -> 29056 bytes src/widgets/previews/NextEventWidget.png | Bin 0 -> 10513 bytes src/widgets/previews/OutboxWidget.png | Bin 0 -> 22210 bytes src/widgets/previews/RecentFilesWidget.png | Bin 0 -> 18555 bytes src/widgets/previews/StarredWidget.png | Bin 0 -> 18993 bytes src/widgets/previews/StorageWidget.png | Bin 0 -> 8043 bytes src/widgets/previews/TagWidget.png | Bin 0 -> 7701 bytes src/widgets/previews/TaskProgressWidget.png | Bin 0 -> 7453 bytes src/widgets/previews/TasksWidget.png | Bin 0 -> 39025 bytes src/widgets/previews/TodayEventsWidget.png | Bin 0 -> 21401 bytes src/widgets/previews/TodayHubWidget.png | Bin 0 -> 41982 bytes src/widgets/previews/TriageWidget.png | Bin 0 -> 23121 bytes src/widgets/previews/UnreadCountWidget.png | Bin 0 -> 9430 bytes src/widgets/previews/UpNextWidget.png | Bin 0 -> 18384 bytes src/widgets/previews/VacationWidget.png | Bin 0 -> 13264 bytes src/widgets/previews/WeekWidget.png | Bin 0 -> 13198 bytes src/widgets/primitives.tsx | 391 ++++++ src/widgets/render.tsx | 169 +++ src/widgets/sample-snapshot.ts | 206 ++++ src/widgets/snapshot.ts | 262 ++++ src/widgets/sync.ts | 95 ++ src/widgets/task-handler.tsx | 57 + src/widgets/theme.ts | 177 +++ 138 files changed, 8682 insertions(+), 49 deletions(-) create mode 100644 android/app/src/main/java/com/anonymous/bulwarkmobile/widget/WidgetProviders.kt create mode 100644 android/app/src/main/res/drawable-nodpi/agendawidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/allaccountswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/attachmentswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/birthdayswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/countdownwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/datenextwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/favouritepeoplewidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/foldercountswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/freetimewidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/inboxwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/invitationswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/lastdraftwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/latestmessagewidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/mailandnextwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/mailsearchwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/mailshortcutswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/monthwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/nexteventwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/outboxwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/recentfileswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/starredwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/storagewidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/tagwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/taskprogresswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/taskswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/todayeventswidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/todayhubwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/triagewidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/unreadcountwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/upnextwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/vacationwidget_preview.png create mode 100644 android/app/src/main/res/drawable-nodpi/weekwidget_preview.png create mode 100644 android/app/src/main/res/values/widget_strings.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_agendawidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_allaccountswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_attachmentswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_birthdayswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_countdownwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_datenextwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_favouritepeoplewidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_foldercountswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_freetimewidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_inboxwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_invitationswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_lastdraftwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_latestmessagewidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_mailandnextwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_mailsearchwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_mailshortcutswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_monthwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_nexteventwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_outboxwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_recentfileswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_starredwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_storagewidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_tagwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_taskprogresswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_taskswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_todayeventswidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_todayhubwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_triagewidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_unreadcountwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_upnextwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_vacationwidget.xml create mode 100644 android/app/src/main/res/xml/widgetprovider_weekwidget.xml create mode 100644 scripts/sync-android-widgets.mjs create mode 100644 src/lib/reply-compose.ts create mode 100644 src/navigation/pending-mail-search.ts create mode 100644 src/widgets/__tests__/derive.test.ts create mode 100644 src/widgets/__tests__/jmap.test.ts create mode 100644 src/widgets/__tests__/layouts.test.ts create mode 100644 src/widgets/actions.ts create mode 100644 src/widgets/build.ts create mode 100644 src/widgets/catalog.json create mode 100644 src/widgets/clicks.ts create mode 100644 src/widgets/derive.ts create mode 100644 src/widgets/format.ts create mode 100644 src/widgets/icons.ts create mode 100644 src/widgets/jmap.ts create mode 100644 src/widgets/layouts/calendar.tsx create mode 100644 src/widgets/layouts/hubs.tsx create mode 100644 src/widgets/layouts/mail.tsx create mode 100644 src/widgets/layouts/more-mail.tsx create mode 100644 src/widgets/layouts/plans.tsx create mode 100644 src/widgets/layouts/types.ts create mode 100644 src/widgets/local-state.ts create mode 100644 src/widgets/parts.tsx create mode 100644 src/widgets/previews/AgendaWidget.png create mode 100644 src/widgets/previews/AllAccountsWidget.png create mode 100644 src/widgets/previews/AttachmentsWidget.png create mode 100644 src/widgets/previews/BirthdaysWidget.png create mode 100644 src/widgets/previews/CountdownWidget.png create mode 100644 src/widgets/previews/DateNextWidget.png create mode 100644 src/widgets/previews/FavouritePeopleWidget.png create mode 100644 src/widgets/previews/FolderCountsWidget.png create mode 100644 src/widgets/previews/FreeTimeWidget.png create mode 100644 src/widgets/previews/InboxWidget.png create mode 100644 src/widgets/previews/InvitationsWidget.png create mode 100644 src/widgets/previews/LastDraftWidget.png create mode 100644 src/widgets/previews/LatestMessageWidget.png create mode 100644 src/widgets/previews/MailAndNextWidget.png create mode 100644 src/widgets/previews/MailSearchWidget.png create mode 100644 src/widgets/previews/MailShortcutsWidget.png create mode 100644 src/widgets/previews/MonthWidget.png create mode 100644 src/widgets/previews/NextEventWidget.png create mode 100644 src/widgets/previews/OutboxWidget.png create mode 100644 src/widgets/previews/RecentFilesWidget.png create mode 100644 src/widgets/previews/StarredWidget.png create mode 100644 src/widgets/previews/StorageWidget.png create mode 100644 src/widgets/previews/TagWidget.png create mode 100644 src/widgets/previews/TaskProgressWidget.png create mode 100644 src/widgets/previews/TasksWidget.png create mode 100644 src/widgets/previews/TodayEventsWidget.png create mode 100644 src/widgets/previews/TodayHubWidget.png create mode 100644 src/widgets/previews/TriageWidget.png create mode 100644 src/widgets/previews/UnreadCountWidget.png create mode 100644 src/widgets/previews/UpNextWidget.png create mode 100644 src/widgets/previews/VacationWidget.png create mode 100644 src/widgets/previews/WeekWidget.png create mode 100644 src/widgets/primitives.tsx create mode 100644 src/widgets/render.tsx create mode 100644 src/widgets/sample-snapshot.ts create mode 100644 src/widgets/snapshot.ts create mode 100644 src/widgets/sync.ts create mode 100644 src/widgets/task-handler.tsx create mode 100644 src/widgets/theme.ts diff --git a/App.tsx b/App.tsx index e00140b3..bc631fa8 100644 --- a/App.tsx +++ b/App.tsx @@ -69,7 +69,10 @@ import { PushOnboardingPrompt } from './src/components/PushOnboardingPrompt'; import { ToastHost } from './src/components/ToastHost'; import { UndoSnackbar } from './src/components/UndoSnackbar'; import { AppIconBadge } from './src/components/AppIconBadge'; -import { getEmails } from './src/api/email'; +import { getEmails, getFullEmail } from './src/api/email'; +import { signOutWidgets, startWidgetSync } from './src/widgets/sync'; +import { draftContextFromEmail } from './src/lib/draft-context'; +import { replyComposeParams } from './src/lib/reply-compose'; import { handleDeepLink, parseDeepLink, shareToDeepLink, type DeepLink } from './src/navigation/linking'; import { addShareListener, getInitialShare, shareAttachments } from './src/lib/share-intent'; import { OfflineCacheBanner } from './src/components/OfflineCacheBanner'; @@ -133,14 +136,34 @@ async function openCalendarReminder(target: CalendarReminderTarget): Promise { await handleDeepLink(link, { navigation: navigationRef, - resolveThreadId: async (emailId) => { + resolveThreadId: async (emailId, jmapAccountId) => { try { - const [email] = await getEmails([emailId]); + const [email] = await getEmails([emailId], jmapAccountId); return email?.threadId ?? null; } catch { return null; } }, + openReply: async (emailId, jmapAccountId) => { + try { + const params = replyComposeParams('reply', await getFullEmail(emailId, jmapAccountId), jmapAccountId); + if (!params || !navigationRef.isReady()) return false; + navigationRef.navigate('Compose', params); + return true; + } catch { + return false; + } + }, + openDraft: async (emailId, jmapAccountId) => { + try { + const draft = draftContextFromEmail(await getFullEmail(emailId, jmapAccountId), jmapAccountId); + if (!navigationRef.isReady()) return false; + navigationRef.navigate('Compose', { draft }); + return true; + } catch { + return false; + } + }, switchAccount: async (accountId) => { const auth = useAuthStore.getState(); if (auth.activeAccountId === accountId) return true; @@ -385,6 +408,14 @@ export default function App() { return useNetworkStore.getState().init(); }, []); + // Home-screen widgets follow the signed-in data; once the last account is + // gone they are wiped so no mail stays visible on the launcher. + React.useEffect(() => { + if (isAuthenticated) return startWidgetSync(); + if (hasRestoredSession) void signOutWidgets(); + return undefined; + }, [isAuthenticated, hasRestoredSession]); + // When the network flips back on while we're authenticated-but-offline // (no live JMAP session), retry the session so the user lands back on // live data without needing to relaunch. diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a1318459..1c2731e4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -66,5 +66,232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/widget/WidgetProviders.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/widget/WidgetProviders.kt new file mode 100644 index 00000000..3a4efe0b --- /dev/null +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/widget/WidgetProviders.kt @@ -0,0 +1,40 @@ +// Generated by scripts/sync-android-widgets.mjs from src/widgets/catalog.json. +// react-native-android-widget names a widget after its provider class, so each +// widget needs its own subclass; the JS side (src/widgets/task-handler.tsx) +// renders by that name. +package com.anonymous.bulwarkmobile.widget + +import com.reactnativeandroidwidget.RNWidgetProvider + +class InboxWidget : RNWidgetProvider() +class TriageWidget : RNWidgetProvider() +class UnreadCountWidget : RNWidgetProvider() +class LatestMessageWidget : RNWidgetProvider() +class FolderCountsWidget : RNWidgetProvider() +class MailShortcutsWidget : RNWidgetProvider() +class AllAccountsWidget : RNWidgetProvider() +class StarredWidget : RNWidgetProvider() +class FavouritePeopleWidget : RNWidgetProvider() +class MailSearchWidget : RNWidgetProvider() +class OutboxWidget : RNWidgetProvider() +class TagWidget : RNWidgetProvider() +class LastDraftWidget : RNWidgetProvider() +class AgendaWidget : RNWidgetProvider() +class WeekWidget : RNWidgetProvider() +class MonthWidget : RNWidgetProvider() +class TodayEventsWidget : RNWidgetProvider() +class NextEventWidget : RNWidgetProvider() +class DateNextWidget : RNWidgetProvider() +class UpNextWidget : RNWidgetProvider() +class InvitationsWidget : RNWidgetProvider() +class FreeTimeWidget : RNWidgetProvider() +class TasksWidget : RNWidgetProvider() +class TaskProgressWidget : RNWidgetProvider() +class CountdownWidget : RNWidgetProvider() +class BirthdaysWidget : RNWidgetProvider() +class TodayHubWidget : RNWidgetProvider() +class MailAndNextWidget : RNWidgetProvider() +class RecentFilesWidget : RNWidgetProvider() +class AttachmentsWidget : RNWidgetProvider() +class VacationWidget : RNWidgetProvider() +class StorageWidget : RNWidgetProvider() diff --git a/android/app/src/main/res/drawable-nodpi/agendawidget_preview.png b/android/app/src/main/res/drawable-nodpi/agendawidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..23530cdc36322c88601109c936322bd326eea61a GIT binary patch literal 39230 zcmaI71yCGM^ezencPF?7UnB_@T!TxHg~erYCs@!xaDrQa;O?-v2MJkNEcl|qZE^R@ z@4ol1dUfl)S9PYVr~6Ewo-;GuUw!9Hl%~1@9yS#=5)u-glA@dz5)umJzXS8tOAB4; z=*tOmg{G>GysDks$?4VY-NW7e!%KPp@OW{3cXD?1QaQW0zJGjrczE1CINL>>-@GW? zKWy)xuJ0ai?wu^IAHHZlKR>VUp4{F()HSpi zkMGsO%Esc-s;KlQStb3yv&(J2U=1CAvP+w1X5qy(oti*!O-uieLP%ako29LXK+x5n z(b=w^0d;dfB@L5uNRNxhS3P6<=GN}C%!1Dg8QPN}6;%yhe&Mx{w&1XY@R$_X=wCZ$ zU-PURV@ubV_#ghkF-4^{nK>o7g%yb@*~&53q4l?a*Y5XDAKItx%l_QC6yBtF-F_** zX+IoI*{ByEbq8AcU+XI-zdSrD4=r_VB+j=Uy}92}rigTGx+7dvbbL3#(h?G;$5%fi zRis|Duv`^EpOKJoi2pl~Czk^skdPRVl;os!d=?O05kZTJX~&DAiw#qsg_iwR16S_- zmE{&ul)fcUJ0s;V!bd*vXJs_Xak6o#bPNL_KMyl*3s;Xx6{C~N%wS4~B2$(`Z!{H6 ze~Of2uLgKWJ?v=&^+^Uj^#?tj)Au~DMQG)!L?&o31r*Z<-aN%WKja48oaY+M2Sl26 zNCsT*Jm1AjKIQR6s!h>EuM;wDYDZc(5Lpcq1U3)*kEUiG_~%RO4U1-)1Xcr#qn z;@@9CAB-RW0RW%QdjiLQ$hyOiB^eBkA1(;vzY;R$>KuvRaM=xtUZc+wJm1kxOh!Mi z5~!;sz1h_>dp^lA)*_9?B094}J4!-jQ4()AzFjk|b(plMmh?aV_exZEeo`H(wGd-Re^SZ8& zNyS^Wem_lJs|MrY96n41nB!sIYB4R+Kl$7ITYcKYmX9%g){9c&*;~<&e7@{vnyyUX zEp`+Lxhl;yeOi4E;Aprx%oU*IBp2*`gaXo(iDP%*mJgHfaX2Ih2Hq0OYF*=C$6|bp;|LOXcN(YX?>~<@r!PZLub3hsh zOsFgfv7~Pw*djfn45|6v<$w9GoW(YNB;osjfF}!$uXek-run^Jd=7fv<8OdY29DpQ z^eo*f5$B^Y<{I@c%){o6bHoELc276{cr(VIp8joMb1}8OaH#zi{O-i1^mEIm?`7N3 zYTFcBB-vScuvHv|i(=b~IP=Xm+~Hn+q+LCup=^0yc4kh)`|dXx*;!PS)k2JS{P@~J z(w}&qkTecS?=1A7yCc7kHFKtNVnApqu%0((HNfNA;>*MC5SIdP%tdRw}F_Gjee`ZURAv`OOMVULtaSCxRyn@Hj z4F->NyZD)DLcrt?{u)NwjJav^=`M83U2$UF_xoZEnN>~GRmI@r)!WKQDE4jRK(~*V zxW$mL&ymOuS?q=$=i67xMxT20az01#0U*_mBbz?sQ_j7heQpkh7fO}AqaSpKUD=6z`KLY#N} zO7i?4QX%2RiJoMu6E9T5iCY3zt6?4ktchusdz?rB+Dh1m&-{5^$l zZPBvE_yry$1_WTG#OHSU_)$)1AWHclz0Y?m*3WyJ8!J0` z*%mpdx!em11Pi4VQ>ruXkp1~QkJr_to59u6uuP!Bm(a>Yam$h6)izR@SuX*| za0WA$NjRdtB0XKV3BImj8P*@Wyx|5E6O$uEG6m*3Bb=+zIsSEg_7OhDb*4i&vluSw z&84O1a?0a?GURt!($lYF)O!)ta#!w5wI8n>;RtUY3|%_=^aThXZu6!b<0o4crcaS2 zJhEO|dIakwIo$4sq$@!t5#SaI-EU0olYz6-y33AlG6wK*Jt6Z&tCvo6$Py##g$qks zSRgSzGHl)5?ahQppT{Tr4tmot#Kzold{rON^L@{}7?*idzR|xAu{NHTCa-d-LjfUR z^BH~}d^$$1{z=`LI&&(x@GFS*hl4rK{^?6yhR$+JZew|NH4Mn+LAmw&cyubXm#Nbf z&HF{irgd6h*&L3~QE7hW1`|A*&jhLG2^8Z9&I2!%VMI(CiUMNXa;Y)IY zRYaa!t#F{eVC)bBW2N<+mzUs0UGQt!eB{ISu4FO350J1eoS<7&uO^I)2=b>Ha~s$+5xh|x)FcMLylFezTr|VSP zD;U4xpU3UzA8?Ix@anhP^eDfsClEbgarbUR^^^WjWi7qXBsJZVKW{7dcY=aPXmlD3$j0R~6xNioOvj^DG)UtGvYXArhT4VU-72Sy`@CX;>gn8aFsO#1E67z0sWu8m7AN z@b{|M9jM2K2IvXhxEM zigEppF15zQf@)ykMQ;F{;eLD7dY^j0P^nrm_y+f%AOz9~#HZ%V*41XX_Zyxwc$%|> zJ=$~oi(OZS0WOTEYN^FsJt-ksCp8c};ym~-$7U{Cu&#_3HPm`h-n>H!I9{rrU_7nC zcp!(XYHGA1!j8jzuZG@s)ZI-484h=@&|~Td=}aGX=+=B7<9UY`F5Lj*)Bo002cliq zq8IxHXbv~e^Gs0di=Nj67y=pms1s>h&94q-)No(vqr*65%FkG!7QfWU71P?lMAHV= zOOI5NZau(!tGUHf&Mkfj#~lUoyDCSko;Y3cEK4mN2AK06F8V@8d}dvM2Dq1k(Jv!y zOFg~4T#m6{Av8wt+(;0C#^05T$$h^4_ufNxv>&W<6vUR8JzuEWOZxAQnx9e_3Z7%I zKw6|qN3m5T%&+@2%OTcyj(~_6W0Nk-_^PT3vHN0nm?E^#3zHgsdOz41C=9`-txF4rlNcHZ7Kbs6)#+``Q9IDLv*>aa!1qnOp#ScHkPB`i=9+adAI;^jTjp*y71 z%An0n|B9Jwa9^>QaiA%9=D{r(Q4RRf?|7a9e)Bn3Vra#t31Md5L`Qm5GKB9)pDIvBXC2gMMZYl5ZDprE#jQijo26I5$ za}BDFUR61uhEZ3#U*6pKC^Fo4G~nil_vj3DfBsqhy$c;O;TO^FR>O&&?YnLtVz32~ zrt<#9+#E@kchXc1@rx5k(0H`N+aN0>c6J&0^@PxJxSDidnA>^Xfe-n*!)_|U7Sna0 zXMW~2IxMa7E*Ut>UX>-Q>)1Bz&B|5hMW$ZU&?P;G@xc1No8yP>&rI#xPY&GJZ2@Oj z-)v$&xKJ1ofE3NyzI@ytzEq&=yU$&cSg}9(8tfmt2OfK1^y8WjGK~xZdu>I z9zsl1vU!Z}zWWcly|E;!wBK}Sv6%ugG+K!Ob|%Tu`jhb(R(A*=kiVVPY|KcTgL z?5zh(ej60hF9*UQ%uz%0WW}BRIFTTvk6xT3omSv7dl?;mA zX7zIAeXVzicR$YKlF>sL;cmk80e^%GTr204D}&2u6%Y? zBUMII5=iJK3lu-^;;L(=YI!C88D87rKG$AK9*PO}W4t}vf<56}?tVR6AI%a9`l4~( zcT{joDX=H93%{q)pWB8Q;_x%fvtn7JLBzK%N&ekzV}(9-fB9>h-vKI4-16)&L)`1YDg2 zBs8g9+n`7!DNcHlHF*6o8$39_^Uo(rr^R_oZ_3^tnnMP?N@y9+Gt2$UC960k!3fq^ zY}D5@5qDfhG_p04(yPyQt`rs9U?7%by}0L#L=VDJjX!UE6)ZK!Zh0;EQ=!Al#@&$7&fZ7&~WDL~n z#Z8Af@-{+tD$PI9k64J*h{ric`9>BefYt^kxHTl_l*C#rKIOF-%0@fTC$;dNwSW`_ zPA8)%Vk*&be^ae0jDNx5lL(E8@N#BoOk05Bvj;TZB$)ce0%fq zLW6nnEgr)hK%$NNzs?mlqt>=8YO}A9cNP`Ahj{zP|7rL7<=O2W6|(V?L0Iad&(`6~ z3bj`EpnfwR!qj#yISo@_4_MnHYrL76Y)Be3TEfY4kM|@` zVYv=vc-ebdAlR?F?zaWz8hJ_A&yE^eYV#HIwNld=u7)ZpCTinXh95QPBm9%h5Xc2#T^Uu zS6a`izYb$Z2996JD2$4))m>jN!+er(yUEw<0-B3>FMiFC3GF7AEL+mv11s8WPZ{Imaw64wWhty zh#beepJ5S?MBP^%1hi-$VLeD5?y?C`o#J~-fB5&~>^E^WFFK7%&it$3Dyb_@aFosj zX0Z}NBJgx_y`DDxP`il2CAY`#n@hdcGxhrI8wtAa(917>!(i&=RGe zDij}Di{-MAr_fh5zLsFtZ?9Dm{uCtq56=}VtOSm#K%OSM@P0CX^61Ao`nJdbYZp^I zLqeAzL}QF$@h@Z;#>Qv5+8uZqr+=7iBIfsSL-w;yZznPJh+vi|Kcr&jNY4LvuaxbP zdR$TFzAfC3(i9R~y!?rvu>q`)wEj6nKN!*E2#Rj*mJ^wIXJ3sg`}|c7+Wpnwx3`la z5F)yYv}|+IJI3hX{YZl&t(GK}p}tJen|3IB1uMSvbcE|b4PE>UTh~Bjx*1$4@5s4M?WsQ%vq0Mx=_1!)JwPU;$>1lSP+3c` zR>j79hi=l(<5Q{0x_0EyLCcE7=nxzeisIdqP*FTGU#KU(j;6@s)1^`#nBHY@E*I!N z(6v&2pP7_-h0n}$kiF;K8qS=G*Up@^kR0M1^@bUff`j`7=w!Xb|{)G4;}W`wz=%G9y70wP9Ld z|8l^7FH$u71$#wLPX?=#!kn|*Ff7vK2IuF8`o8noqCqGa?g@jDYOF9wNy$!73^-+J zq^`d5=9OKY;wXLbOHa!Y@15jQ-szS(x@ub@sYF)rWDSv$2A3!=wXN@couj5aROp+{ zri=U1mA~gjI9sEJ%EYLGA|U7m;p0wtS=v7WeQ%GJv-H$WI=1{zX)H-sI*-`4ur{tf zaCiD&Z_{2>oJ9xvUMAGyfla0EyWJQ3;h#r7iJpD{mgP zi7;ECmy!Q9k2;tR`QN^gOS7`6lmRAw(kK(I4gmq+cE~*{0}ba}Dy7;o(_cAzgAp4x zj{y|?aZJtwn0E$N^?b}_a^%hw^0=COT$fmQoSW$VWF1V(ulb~iX2!H~LtReJk>;`k zk^=IYisp&(0**S14yVjHyOF@+b`g=LShua(EM-U8g%l#Q5o5b{r(7?M)sR_|{0AOTK*Nz;P1jv6Q(8TkE!em3rl4@sPM`9N04W$# zIm;%_sQ)~b{r$=vsMSQKn-F60PL9zc@C7!!?Dzj|`TqkB{%?G+DFgl;M(AFd5HP_j z2X!Knxf;{(4#jNa?n?}qr|ROAgU<0T@wJZcR?EEr5p+9d2NI@DugD3AjB|M!nV!u5 z30VHe#0vzuy-H~MkgcB8$|B>82QirIdBGcx(BZ0!>z-%s;g`MI-LMg{`g{s=>3P_2 z2>~&-pn#~UAGTUdgB~wif=E#tWSn0?-rXOs20m`3o4ypsBOJ~#3AOYfwtIRU`0A74 z3uyXJ{6rIG_z#nQ$8#*OIT}L$NHsfns-=y)_;15<4qm_po8r&L%lfy{FbE*>#+a${ zgQ%um^vCC)ZByz7M^)1X3ik*DAV;e5@Q_8FX0*xrGYI1*ctUGYW;C!Enxo}(1E9bj z-up>bO{gX(x(A5m*qQyJ0Tz(|WUxBirsGEYR7&`1&2=}*7^wcSLG~jj;$Z|+;+yje(m?b?z5cTE&lg94B3hJ0x|L%Io z(B?HT9eWY@=%=3O_$1Qg$k&!ga35id6I)EK+kbprc?k9wUXA~JKsZf&qV~g{H4ywB zt3GEwa^A9YQ_O;OVxPp&urpz743)X?tv;1^O= zlKl}VIwuwf3WLWC&i0kGtJ@pT5a_qadab9Qirc!+^|3$~HmQX@Wt_hQj*29{G*Zrf zfT#n?VI!1ah4A)BdXfXBrLNmLgQ0o`SeWPMpY=+{;?^h0dCh4Vj`zPJgC})HWqmGf z2l+1puoWEl_G>==8A!3!wA*`^P=r<}9qY*mi}K_ohm@jFa97R@MNcdXdtokq`yEr8 zt{aeHN>*jr#NJ}t;x&(A>TuZg%~9dmekrm46e|6|gQIp|>a(R*S=`lI01~yjxVrAsaAQ!)&kQ^71ml88J;*b@=5O;U%)> zM(iMX-;5Hd;AQDebAUGY*W$BxHonQiim^Ib92-~cR(6^|PmjJG9cf%JAZK=YSs>~z zWF63yz4&8U4`}1&5g~296Zqj;#d?DrLWKkLuuLv1-N0aO&y=(5;ekp{FbV#OG+wKWF*eQD~LD zz>}tz5$4F~a1;N7Nl7 zz>Zp>jBX5dIHk#lAN*>Tu}ulT--E-4h&G{pKk|b(ewu}dl+JX(V)S=f<+f38BcsyN zjOlE!+v6T)iPFsH>ddqWB4&nSmEJo7(CsL_u*r45G5dilxM<+avZkmID;fna@3w-7 z*jK@bbQP9Hj?lw}SV!W0fb@~35X4aG5EcG_!5s6a&<>2eA1o=VGjeymHoG_Y#`la& z)|7)UO@DfZdvII#J#N={9MX*$4%{-EzpP;^u)e4!VhH`~<0P&Z`Z40GzjzRzX&ygq z0G6TGx5f8?L1`o3WR1uBuI$zcerL`73+eml8~xZjV4Ve0fn|Qg0rdszX6*o zT2?aDAUnpg-sXaFYhT;*g4M)~T1lfi@u`)-f4U{W+!34nkPUh?(9%&r{|~E6K1PVe zruGdRZrT-|C)zl6T(Nn|pP{+MweFd2{Jg}+3L5DtT78Vc#8f+y1Vip*?$5-nA?Zz# z^HugK*Bk&9sPj)FGTYm5>;oq3boW?#QxgRMKz9~zvWb|P10_LNEU%;Y7-%dL->04Y=sw0D^qF+~?MoRxo zFvg=E?Zd!mn|S~2?|vE)U8rF(wSwDgl&|Kqn1)e6pVR#Zd}93BB-2Zc=ERwJbvux- zPMwoC$4|!G>YRif3ePFqVq(E652aVP*6*o-quS7IEchGWI8FsGP!|$NpZhx#=6=DO zgvUv!fg7AT`k10$?5n&q(-PezI}*dZCXoSlfn`pqXU_g$^u?MRsJmf?^|u!#KLXIW z$#W&_up3F?3OAbtumKYiYgX zu3zP8zJ-Z+JmCA**@S0;_#lKS;c*=*^69x?%s%Z}9IkxAzGTai-WgXz`l(FZeJjy# zfrZOLsOf)l0lbH9doLmWHhxym19=0d&+4`(tt8J zEj>s|0FFDQf&SAQ-!Y`}SF@{?Uu*3tkc!S4V4+luAph~P-Bgba{@$69L7=@-6Yz12 ztcQNnQkL}6Bcb?fNNB>)$BNPr4BnEqk|JT_Y608iFQ*&u0eG@5tr!gO$fA^ zt1VAt^kfseqA^pkuk;K(C2v5`bJ_8T@kw@z3lPa3@Wrb5JMIJq1-(J)+cJ=5qId#F zt=U=TYqq@)dGhTItwwo`H5ofI#*@fP{G1Y@Ig%mrDjF&na9Ie&Fk7$|L-?;f)*x&Z z;Uh9G=}s7)>C8M6%zv40oLR+8s0>*DwJ#5pb3Hn1S1I1~GPS(e)5fqJd7~>Bi{l0q z+`Mm9$koXYbeVV@&*!`Pnlen~9W{+$#3))Q$~2%mopXMd@X>)Gs{KyZN*eX|@5X6YYw32Yv3ytNiFGrR#XPwa>U}K&%Uv|)(Q9DE$}K_i+7 zVxQ6HI-w(pJvDf}dvEOykFNtYuI(`L4GsrNg6hxA=|snCsxzO+Xqq9kC6@42{x2X^ z&}eOcQA8?3p*DdHj)FYW5@Usg={O?Knbtgqm>1M)_dfpDjNMdH!H8_-!NGYW^08E7 z{^;eGees{CqnF?mmukD2Q#Xec9s|H>GxqsG<%b2`=>6AL{16ev7~SGjW@w~@*46fo9~%5I`UgaODcQGO7QIkcPHIBHE6Qrw8mZ)fl)oVa$H(zifB5=~6m? zwvTs(FLXMEe3}nJRUq=idIGEY3ql4ubOKmCl%EE2Z9!q=+ z_t9VK12G2bQ{2y57+pu^fhF=9#~2#cDo2#Th`Ct6XkKq(m}fd8`zOcsawW&HWH*RT z66=~-n7)(+iwmoA_d=xKrZrS1%TLBhT!b7sWpZg=@!A%m zgY=ZhvEn38mDS9km^KZZrPd|t+lL0e@E|AS(X*&_H(wHRNZls}-@Bn1$?0%KEdWmGPoa@p7K72Y#TU z{eH_gWVo!iQHvAFJF>)Y!M9IW&-qmT!3BB{r|M0YFt#X`4N3AUC38@sp(9R>*S=X2c)Jks7P|VIjY9Yz?L?$p*g1u;#W z0yp^dW*hw*N?GkSZF5(GQ+!!=l$<{@Ht|meQLoQn1r7e1ISNqpw*Owk#>if*j>N3)ie5k-omG!bdw*%Ngp5KT9}Azu2>4{w+>KdPzXS)GUHU)aEubUVBa% zs;qZodQ|W$kA}1Ef(wbpjRp_nS{fbewue%Fs4;j}CO9>{%T$j**>nwJ0+;%~9g&vw z!3XIxF|I}ma9}RgN4>dO*U;j}GfE~wCdk*tV~7)bmvr@IYUTCgCEm4gK*mg+LRf5D zL_~mq|7%Z;4IOK-;NndkX!ge-;#i+oD;FL!GQdo)KgxX~t+57agEw{%{_;f)`y+iBLO+~@L-!taCaGyM z1$$BD*SKJ0a`Ss{HXHED%#FJdcLhlMbYd05?A;< zIokHVy0`vf%4|{S)#XH{r>TLZU6G735k0>jx`H&`=&jlAM`j7G2?eNnEAt0w1h-T( z*(T&u0&Hl<#;V->X;`)zT@_mlTD&X<^&I3o3I7;bI|U6H|0-lhWEkygXkZyYX(oV( z0kJyqAgOpyP_Of5R$@eIWZ-pJI)Nizt@z@AsBywi?sxkzEtw;|b+IPpBAG|&p~7&; z2OM}<6p;p4bHLgK86uXdIbewOg;U!3_r;z-7~se0`Y2|i6#mA|7Ii5x0Bk6LTD5z5 zXK`xo((V^+W&7~mX2h3~$q!h%UQ{T<2HE8JSrMY#)1zr+LF*fW|!2mTq2XbqQ`i>36?(SSEIa zvRT?V@A)YH{{H9^hfsu3&#@!_JoRD9@$gpEwIs<=EDuANF=*Av7*Ev^eiWN}w>nQ5 zyx)The_z@<@R?CHCX866$a#^C>D7)ixNAidDKK{e^0Hv!RA0m6+nC>z5RIe$+~1Tz z|MbTHZL`ifdRa{D;8I3Z{a2TxaI3dIs7re~LrHx<{FFF8iUl88P)E=DZX8A{6vcdg znAxfdACZ~-bNTk}T=Snhx8FWdnct)mQhaUNQa@$%`wb<>uXi`v+UTXCYY^fTx5=Tr5ac?kYIv{Ps~>hJX&1^ z4+W52qH(k>e3W++@}43%h_n9jhErb2+?p7|(ohnqu3o~8M>&}0D$zu^=hoWb=^Tcb z`4F+~wstO}vsBz7H*#!s6-lK`GG2C;kZf#ej!%=$ChK^PlNmo)GCa;6>YS%h!FT#> z@glv+X08unL}3=Dh5k|+cw1JeC!PsSZ&?nvuF&Wr6fx^TRifMBQNg?=VwBa?Ai2?k zr6lr^mFNtD@ryD^`%+fcPWG7Rr%Q->F^gml7v6;CD&cD-ozHJ(NW*abEzwxXUP(kV zGAN6NFwwRuaYVzKnbEny;^K*095^nqoaR5o4$svS+ttGS%cOAQ(DR%)pjM13sYdF8 zug}O{;_y!WWKP+dT-rlzaJzOpFK5LV)l*%|` zK=|W zDEA9_9`_Q9R}h8}9N`eT8_Lc9$CBT({MVIl6&A^m{=eh;|6fde8iT`Uk!%Zf9>x|7 z5cNTzd?X>GsVGD+?fyRKS)AnunY+UaUmo=MLXZbOc9;R7++XziR!^J>I=R;7Qclpq zmg0maA6hOyX0ARxeKIL{BlnV8H0RgzLiwZp$NlGGfDQ#arH?kNedza!NiwCb`n*lA zI_GV=I|uRF?wVlq_jqoQ^9$*0<8FV;t?=pAwu~V`{F9e^dfIPOBgEvM9=%Le+}>#id0d1;4Qg& zbA#5<4p;JdX^p;To7RvXIRV*0z{y5Ka! z*_0%NLo$zp5jCqiu`|FJeVC4#<*7SZpt+%0a6DH>`;tDO6?p@E5WcTb(0kj|L|cLk z(q29&R%)qZmgLE;s4c3wkNmgfVa%}DW&;r+Ce=7`se5wcsb&usStnabGwqcr?_^c_`Ri z*Fb;$Fi39nmO;e;C~;^xEX=9xL|5f>9OhZ*={lQ2Vyx!PYv%->IXt@THV0=r!fP|l zlbK@<>dOu$igsB&Jt?}fwpbl+VzDWa4ckrP-g$v{@orEqJ8S3-4*Xw3J_N<%6G3Vbvwg zPYENKvk7oCAe3Ghk~I4vp1eM)vu4bl_a7e(kf1Hws%x)h1F2=a+;i~aGY+WD6Rq0` zY~_`ZZJa&6|Kiql^Pc+t5bQ<4Y5A{$9-ZZUR?NAprMw3v&T;R}s_EXcKm~Cv(~W5a zeR#oJd2c{(l#(_bA7RREItoO+S;H)(H4^}Es$302?oZY^2uRy>LxZS_LK}mW2E=a! zEiT|rCsqIyyQ3jc>8KYcX2{U)-O;m!Wq+m>ovf-DP^)yCX&0GM+sP-9cK7eLUi$xw zVQBV1w*q0;l-;2l1L6QU+wOl!^k#>E{l4T8H4FvKJJ-AhwI%o^Rqy*lqn(u&YiwXxp2EfDij++Q=lt?EcG*|2%o3h! zT!ZuqX1S_68vE|pZ|xLNSp54Cb%Uz1PB4R4$lAQ`W4c~nyIsvMxGF%SW`NSr7$jy9 zad9u*XW=tUH7#&sL_tCim4r0N( z{%JQ(FU3RRw3{hs&t*O2*BDgQ$0j#)miv?+M!+J`%hNAuhTnn3{o*3~uL40|5pEtG zQ3q~iSl2PH>kaI{1sbmc5zyPjeA>3unxxIs>BJt$wJtOJq^&2TZH=M4>;r4*Uz`uF>ZnT@aC2Q=ipYo41dg z63%xxeYA(?iNAJr5)g?-@Wf%n8Cu9+o)?a>-M%V27I^d@wPX$2fND`yegio4wvo*C zZw6MUQbe>;3~Fs`IDlpbjH@GVht*N>rebs4LlEUP5o~kKXC;48 z$-+v^w+Uvi{$)8qnk-*$!XFRa(kgd7+%?qRR|#IXP}d#hzKasqRcnAW^wrK+rA=EvEnH*QKOmbbO@z)}xJ zU_sJi+AAwkL#<@(t5Id=Cp^U^HGl&<(!lJ2bBkn{ND8CF{xm&Gs8UZ*EOW=;zxTiK z#6_pW03JNwyLFxSe~|}8M@py68vzmz1Ij}Bv`mBu+SFnqQRu>*WPk@Bfw9`(_vbZg z7E6@lG5w3(3UT1}a3}^P4Y#e%d=G{QyFDY3Pg2aC}wq!d^!rEsNmB$yN!kRp)}SLB}9W3WP_6 zPbnix3>`KMnw)FS$5O8pymiEXN=cFZ6<68I`3s56B#%5UMYo#>pc7{7z}&A51Mm#y z(BIQzSKeE_Qsc+-z+5))TEuq$JNB1Y46ann#alR%{HdT~Ew=f%0=01L%^1g6qPSUa zvkKAokA;3kO?ygjwranct>9pSH4=W$h@fMhFQbuq1&FfXb#B(eI4i2zxF!0}3IHV;7|fdp6Q8y{BLjALD+I9auFvhi!q z7?(Bw44ygRT*Zsq-8-`=HVT(_Wb7DCTqc3$$de1&DC-9AQqFpC_Vs0q<%4N zrrM_3(`Kq9f@pvsmLaZ>K5rp@k;0S{yc5?>t*heo{+K>57sNVh? z?%;1H!5&4BR;S9$mj__;*x@+a^C#!7;jhJPPjB_eXDAboA36L=U}oK~&XeGa@GA`9 z7;~>JBaE4D6IsZ08ZL%-9gHZ3oLbC!@>%Yl?6gtD5htZM0?H7Dg`8jv}u{jVK5^>_~G4-w{ z)r`*v3$h9ApICCdp&;sE0e@R~6ZTG&?w+$TV>0!jg9g;9o=gLmIY$LOfKK&hA)rkx zwnrE>DNQ#y#Mb&9wS)zY!HtbC&>XM5ndm|Ap~#h6Pk`*r)X$^oiIsh3si}_M=U;Pt1hY} z7zsMQ!gv> zjUy$z^KX&mhp?)16z~=qq@^6AQ2f9u<@KI#q7pHWeY&|>p+nqDk{VWj}P{u3cTDlAGRhSmZn(R}R&N(YxnvA?q&O7+vOkY%*JVEf| zIboC?HrOv^aNtwlP-1M_BOBzmJ6=x^{hXbDVh6zYDUyPj6?kLeBLtmSO9(p-tn1y9 zLRNww&d7#(_%a$IX^!^amXaM!8Ck#nw}?l!#V3a+gzLj*e&jS(TdQgPovGRP$tTcL z)FtKbw*=AVF1m=r`B$J=i&uAu?d@QAwbhr5ACx6!(V?SxtWAQDcTS%q4Cwcxss?{@ z3I9kf9KCe1)gW;*2c*wMXK~r02R8^p6f^&|TJP+%5P|YSUOo{@UoArnYP1@wIYE4U z{N`3iQld7H#2{Yd+nxPE1mfru%)w@O{|tc`>^z^$HhKlhcHd1o5h|vQzRPp(4Dp%e z0PChQJx7*Lwpa8h>58Q@Q_l@;vVwKvszOIFR}*`b$T-dwU;ggPbdY4KG({)z;qha@ zF-$_cE^}Twv4QzgKs2K(GIzF)6n|O0B!X}zctYN2@;_uzw3@P4&fI4Fd)?<@vGFC4 zU)Y5f9Tt_Q>-Fg2`Cu;A;I}D>30_rMAPcY78uQvznRPC+S}d@^slrqqMfCdg3&#V< z8UQx+CUi6UW3a^E0onX#pZ3GJ!Ji~ryA}P0j@eqfOjeQTtY4>sJXqz~Rc~y%66FtJ zUTOAS(Z+6|g*jF<$MX6wB!lI;Co)ir>xg+NQ*r658yzF^_(fe^}- zy!PC0$qW=CodF`l*2LQ$-KcD;41SsPq-0IRZ`owfD#vE8^^vho$I5o-$-tpCRB{2A zuD?k*J{j-d_!DPDY2~AGlW>#1=>`tnNwL(5p1akI|46QtT_rc5U%>llNMDf6%m0+e zA^ZE*XWDmDiY!xk`{#&zZ-K3l;yw4T$zOCrx_*95k4i zma8U^UPo~(p38ry`Rdbr`=7btup583POw85V$;4Jvs6%7LBnRXJg@gt0WAWnBf8mp z>XxAqA%MPa((H3Cy{3no>TKr$3Ou%>RHt%sTC(3)0Exnn!$t?H?T;+<>~FeiwBwa< z)pP5YHokT&B>omdM;v5um)vugurcSg4+duEf8jtT%if*Z|uu=(KOF$<$c$^u~Y&ch2hb9*%o2B@X!aPa_+$b%-f%tz#9~lPa92 zgoX?ncsT+Y;88~UUzELNP#jVF?VI3Eg1fuBySv*!@WBTU4j}{!KDY%E+--1o7~I|6 z3GRf;`=0-)x>fhotvXfxsjI8IdVkn^KYRVwdN!MgW2ms*v5EtHdWoz3ae};Y&w60+IP4yEOx=yEB7IB~WKX~FhJ2=O7j?yaqx4}Wp+EW$C zd*&r9oB>vkI*m{+x3h6D*>&gTEKdE$0QJ0M`hyF#|NHoaL_9ZOG_1!)i?TM8oC7QX zGt?`iXU$i=10JPbDzgV+?%m9J3Ri<)mpiXgt!yDI;C4flp_^Z~jJ(*FKHSt<&CKZ{ zsmKpfGrA^@sc9Hn3(avB1RoZgGZxOsO(wMeHw)nT%tbv#Y_uV6-;S!{lnsl0bxX;N z2`l3@Wx=(r7RMI4s$4Oo&dYa|#?e9hQ1~Ys3Hp+AMk3$j z{IT_<`$wU&u$ilrM@GK-x8b%+@#&I$$7%^;jIt8cH-m6Vl`Q?7P6bF|=5};clsdBV zQapw-WbO;j(JYBv=Y}PYlq!!fbLYsh?jNzL7DnFYm!kc)xrRY3$(IjQH9rTnqfEZF z)?oXJ>@hd7`94&NIa~vQt$VZc zs%Vvk-q5A!4DJ?6+ubSuft50XSmmWEv z$QO-%dP@i2!8X`Sf*3-R%H^Q%DCNEH$^e#wMX7MHrC9X^>lG%Qtbv&z3-lICxor^E)vX8^iVZUxa#m;S<(4d-U39DPUyL2cX=pg2u(tEHv#L%VHr@wn0 zBMcxK3|nEfjIqd4?>CObgghzy`ZCl<;y6&xLYA==A1~;XglX}h-@a&gNI|@d9fB8- z&VY)6sVw5lWk8~cvE8J7tLbd()YL^xz8mB4RYi9XhF4e`qXIqEXVfS!nCLbdpnW_M z6OAU%sw$GIv7Hx;2Wfh^ZC>U7T=g*Nwde^Bbwc-9!=@*awyf8Hs;&X>A(s4de2VtM zdr<{xJZE9JT-#zNW)FN7!#4iNEoO4#_AYiJL2r1asnb-wZxr0Feie}+CB&RlUETxwK=`UfFjF`{4;?5 zMN|aOl}q!IJ;`%5wF7R6)0Bb(TisfOk|HOjnY#Z;0V+D@1nSG8B%LRN_U~p0^YIf* zG;s%bw}h>nw@CUmCY5fZ_YWG+I3)taga)s!n_ zFhQkLZ?zoq{Y(t$ZbmjO{>j-r>kYD&(VYIOqL?<@y$(y#M%iXdw>73PbFo`z26q?r z;~~h)!rOZ}9G*T8R(WFdGeqM^JA1NG@^mbIFTaqgV{1l_EGg#YGdgT8TG-VeVl#@b z2zLr6CO^s-lwsdcKNEC;#s_id%qYONw`kg^Ys5Pr0^$?ug_3NiPqH@W-3PcUE(Nf` zy7o>^&n(R|As~kS7^p<4XpQm|zT!A-qW0&%X+TRssT6cyX&#ZBV~@PK?hjI$Zbg@$ zF)@Q>xw8`|vq~6GTtN?%83UdoA}0 z>$2b&LD>M%>DFJ$@NGP7@#WWfYSb_z2^|D+?z;l@Wdkl7Ca$(u;9- ziJHy9QPUs|YL?ILexe^4SVgcd$vy{TK=39xkP8hc;rfn9W%4QxN<&#r$0ZnWkYhgU z^RZ@>?ppw9=|u^@sVQ=)+erz!bw+0M6y^=$P>0wd*lQOKP7{ku@^Kg|FBEdE8zk3* zL#HbXmaj4E;$?Sk1mK#o>7LXKGA~rUmd?AliGrNa3)|uQC<{LcmH8MhxJIbt)r2yV zK?&|;_uGoJm|>Ps<3ojzyNvoBO4RC3v2bv$fA^iK#NSIKmTYTbu8XhALu>Mf{^VAKjG98T`8F`{)}_WoLQV3LFuW)RaHYEOBZ|PpQwD$?6$X?mM(Vm}<}E zkJd1;pOvp1g2E&y&lVv%N3wVfiG|dE1BJKw9~|ScN#W%zsKn5_&1kc-8hss1O1T%< zhJ_$W$)rRMH80u$2Y1!(P}j9v<`iCGfY07HRX9>VGpM7=vBkNO#aO3&DL# z5ov1XH2n!9A^Fg6_ps2be`lyq2Xf?qg_Vu$qO1NDOT2sk(+2NubpL-CsbIZ_JmrKkMTqh6+kO6kg(@ftu4YhBQjk746DL1j%Vl>w-aDtvZq3ba0SVdhEz}Z)PU(&&Z+0ZJ$cn`K0ef zvx7nftjCB6jC-n%9Ar!@D>=VgAB-kol2MM=IZzSjR|f@qw1&GObk01VTe*Q~%EkIC z)(rR|(jw85N{a)GN6uq=2+CV9z27_MXhQxZSrMSoX^YfnQmP-)R-F$^{p!cNU3u$0^(qUs=w|Xzz%B4po?K)N<(1|1L*anVT74x(m_l+ZAX>0c zY|>i2m5dI2paNEeoF!owabMDe10?H}MR6`vFV(3{ zxcHg`KW}sU4=|=o#gt1e&?RBJE{!N})mH)#jC`s*Srr#Z{9j1TmIn!+W$)pA?_ey9 za#$S>uvvjN3{d#hXgCUW z*YvBQGqkM>R5)Pd6H2lm_v>r1uPhD~(mSvMecD-^u5MuFvM~olpka?~dGv$<2U_2U z;RCX}u!jHaa{6ERTBO7G9Y>;mMg{o5Y#@jnA;C+2`rM0Pq7FwuX+Ba9>Jwc!2gu=x zko}%GxD1s$(%my**KG=BJ%!!W_M36?r4Wi)pl)l{c=_Rg>1RecJ-jc6$2A|1=pgp% z=b-R=5Sl=Dd`v4W)#=;xAT0ZxYCX*te>}Nq;lh{~~ zr|q1YbWW%iXZK74Hn!fjIdZ0oj*(oT5cWaLwV}KoQH9mCa%dEZ_Sd(WAw3U02;aif zxFCO)S{%B2a%OoyE3HUFAM5SYqQRDtMy)9-Aj6G>@qvvk>S5Qx-R*w1hw3$cK|DielwKepVJ+PIB zLXNf^e|dm@$A0BEkp9gf@7dp)`$V_(YABPWI6G4>Rh@3As2c=P^=vq4w~J^jtv3{p zkP=|6S?<+Zpvz($L^di~-9xucO@E{>%l6;yYB<1mR(*@hs-Jg5>D}axj7k1?N&Nlw zEU=PbkY?m+Fb(Cch0p76M5lP)!CIw6Up?Kf9rVLv!nqahZ4Z{;#!rd6~N)i=4kZ1zdSdnG&me}~JL;1^looucG?$nvPg?T! zskSNwsx5fFo~bby%j=(DMJn7T6if%eld3o;lBtMp%VF82Xzx%#lU<#Y=kFStkzB`jW{VWw1boJ_u zA?mXJPL1qbOj9r>6)MEz$kv^oftWpyZq1-D)mjxU75;za?DJeW>MKz;ddCecew8kQ zHSQnPtxE+m23n(%$^1@Cs#{Y;g3_CTV+W*Druao8AksK~3l<%TRY&)vT??~o4MegOV^GMN1BU)!De{Jm->7`1Y6A+k_1-HMRq|RRY)=dvS0YT zX~idB9_hWXoVc8h>pyk5xy2LPiT0u~qAM(VHT`<|r%v6_-hhnT4OI-}8)xkbT z?xY1lI!B1uSV6dYGwpf+i>>>V|MRZY*O6pchy!0p%hNN-kg)+u@>yx``o#RzV+A6y zL3VWQlUv6x;xZDqB}HJ*Kf$z2>%KTOFoe`NvbDlZ7z5vKKky6R%Ny zCg!>^J#S9hoH35N0?zSZyGESD>C#h^W=wN#Ko#qvitCN8NU z^dm_IjRm*M;}5PPBtKW*k64&X;99BSfpG+DE`#wUJ)gDbl(d*@SUaa|y98%qUd%(^ zXe}2i&vejmqe^{FKRAUEnP}<;%$O5BMOG3Gj@GBsdW~z0lx37T5p9+3D)GE-=^8~; zJiI=azm0y}m0G7o{CfAnPPdgA|5}EN^S5)~9>HCx#kvaJCi2`0X=hX&FaIYhaY^{Z zS92TcY@SnO*TGI+;MsC%WoFb0qK|Qf&huh}c)4|IqrQqZ|LEFTd$rxWhl6oWjGIv1HU$N6=Az}<~>frmon1dqe|2$9U#hsmZR! z?4*Xi`9YOlRds*2S1oP(tSwIevFaVr-X}2lm@JP`WVxh)uk6`?Zs7%Lc&;rqVYHLb zW3R%*NKrC3#;ig|F9b1{cBDa;N>$euXGm7UnkT*thS{;;frPE{GzEz7<%{j9HZl5A z5Y9BV8*;^tu2z4;S89mk1(yspSMNVDJ}(S!mn(5W1}a8zfa zSDv0F3zkPe0@~xt2wyzJ6MO>{!MUjj+y~=zO4<^&hkaDzZ3!(Nm%irP|)gwF6QAU`%-tq>iJIJi|F(gi8~{ilEi>+*)7Ha7V&pnxh(NsfMcG zj$w1d6j29r7a}L}w)R?cjmS$JkLFl*OVZwseB~)s2{wB2t!3pVoJ0lo^NSPDaSE>D zXF)7_p1YAVlyP2tw-h_d>R^>;Do+Z(IAJ5~>~tsPZM9Kub062~*KS|`o)g^W5a&-z z-x>bDk__NQ~Uh$t#Ah)_j?dwm$X$uFGmp;^c~X=hdUTAE{+znvh#^7 z|NJ^XC{KJR-TTwE>TI~;c5f^nWIDm-v@iOws@cI)tr$U@xLZ)Bt*y7`?MV(jc-pRa zQv}j$gC1N9^&~`KLW3RjpYL3~ajq4dS>>M#c30Aa#L3EepZBn_HqRgtL~|6YrH*V- zN#Bx9NX)KkpG{YY*^Op34@T5asG?oZ)Q0=JW=h;;dUT%GI%c(+wu8@_OdA@WJ%|96 zxkIl^uJ>i8nt!F42gx}OJy|u>3szpug`&I-yW?iguUZ?h=SHtGH*X;f zDWNnd&DFllbF?w6v$oRmyq^eg22S}2b|FTRYta-W~QZ{C5Z{49VO<-p#bFZeSsE?-8a>}n570zrtQm#vOP1dh7)w;$N zAkNeIQebj{@b>!Tuly~qc%=tDZREmAb|=T(_T)WZK%)}=_-Cc;~lQM56NHFk4g$u^gUIe{NF*9pkfD+n_U5vGLbpM zr5db;sE||b-fAj@7IE>z!-Z{Jhl1+lYF?E{uo9+9?`C_4@yclFSqQ7V0q?ACg{hJb z+M6DGlzW#ZDXL~P1)oLHqNIp4e9e(iqg5hj;~>6W5z49IH0gzpKTWp8@RT@Kj_*C|1h*FbDgri+(Y z#k2hu=;!=zy~%aY&kKvb!Dfi%zWpzz6(p;x@N-`fEZ4**BqZ>rS{R;Jx*vwSs-AP=+MqJ;{_u|wfhGxv;f4iiDhfK(B?s#Cq0D?}rvEEWce^hI| z--}$@c#&(93p%}qpob*9XWaZZSn2<>^ZQ@nP2l^Oa_>)4*|-qX06H0{`#Tdlv+fTRw^4EQY!R7{r*}BJ*&>w ziMOYT4UQa}P!W9AXVbTTWzOGjH{KZ2aIaXvw>9E#gZ!OOb5$LWUY&=iC_Wg_($lx+ z1=IMKlXsi2R5l{y?TqSq?9J+T)B@G6AjNym!hgh<|6DfB+H-!LI)6PH*Q3gZ_Z)}m zOX6CR5v{3^Q?fZA_7Y^@l4zf}J$xv>i$i!s5XF@tPu{F$r3oY&8#)2v5BncZSGdX| zIl=VNJ=WOjv9icHHMyp7ym0ae;BAP&M{gq_skCn}V zi8>^irOFsBjinP2Pn}peW_j-jEaq~?udxl99n7iE;PzPCBnB;GVJpmPwY^6mM1km$ zqu92jd2k%%XF@MGe}10jNeP`+#>z)Yz7%HB=)+lhYOe8Nz)i=UUSDlzs($ z7K?*EY$0odrnS$)?)_&P|9*B~td;liseG$DbWz1Fy>q7rl5sdAVwE3Q=Ub_dy~spO(*1LEtR(s zYk6MIX>#Z=a(aRnEHY<&57EJEnD*}VI@VieM#zkaDn!>*#l7qQE;KCq3(O9GI*f+a z1-<~2d%qUIFT7|&$>5|jQcMX7#tIYeRlQjUxDacPet13SMFATKQT4c2>&Vqrvf@Xf z=9~fp!K+C2_+BDhL8rmswFn)|DoZv9mhgNMx^D84hX$ZGM;y@Wr*x3q?DNt}G;P5< zz{j(fHpC^2ihxNTTCSB`!i>M6QP&(c(*4Ka*x)92CBSCy%}f) ziG8;|n|H}b?OIHfZsmR@Tjl9P_tNsf372KrbJZjc$kcd=#mDtBqgJ!2XQ+zL@lLCy z%}__GCMs&iRYd=Kc-ln4$n81#asgl8&HT;(-X$?F;HztV4g19WIL5XUK2G}I(`@oo zU@51-Gt3;fPe9EOUggAsC*M&*882;qd&_#>@B5L%(3a4$Gw1Rw(xqr4&C5J#rO*t9 z$E$6j(0`}rj~*XmV_%~T6v|ADJKL=&!K7^~z-VezhL4yn23ST}+WUI%>kLj1qc;iK zdBSJLEasd5J=hIJfq_L66@Zc`20*QCG#1yYL}l6DSq)jvY$f}FMz2hU$#3Nl*}l5~ zt(E44Hebd<_D+bP{K{H9E|FTv#W83o>NC1HA~iiVX&A5`TGMt&v{@Zm7XLCtqP0-R z{o{rtrq7dxU^b}${#(z~uiVLr1Nh>Z(J!BH6Wiyhh*N$1%NVK%gyR_6J{_>=#`IOD z9G-4^`mj%k{~A2{oXyr!LV-DZX?AD^>y;1Btms)P>%!~Xs2FrgEsF4g=P#uZ9K;XJ zX*pD|z-{=n)(jNa0_=8|gGIDmP(z?cG+?Z1@?4{iPX;Fx(sm=a_$;qJCKST6|7qE; z^A~HX6D|s+3J1A+A=CX`WoyGpwLKqFEM&~7-yuC_9GL4}Yr~tKZ-^VnaQy&l`Ak!M z#-UXvTlZPg%C0dt#B6eMEF|e&OpNSxbUPyLIlWN7Os95x$2 ze@?cSRji(kMH13a{Hlu>40$s~16m$%^;I67|4Afwr?% z4;w0GIO2S!;yj zvZpovEkzt=sH5sqLwa=fv6JfEUtMSzB=yg8)wbsgu7R+(%X)a)7H>Tj^9`0+Q& z8VdDO9MxAs(Hha&S|VW-J9ktdAm`0j4T{j{v+mvH$FQ27i%h@pEPF@mHPPk;jB+=; zNYLE)%R7x)9ZbN*(nTfkrzs4F1h)uHjSU_;Tv}vBd)O>}eA!ilBLBZx0B8ei9?vf4 zMVwa$in|ia7^oVlNk=W+?Z*kN=f~&o)Tl*B82s_yBDppWI)`fk=k6tlWuSyi5B;5qgGh&%y2HBM6J= ztf)qOcjJyrFbj}k_Tllpxw*MLb25LR_vgvnA`7C$YcM(*7+%KE_Hxc7Y_Mw8#VuZ2 z&xlGV5f{D^kgKxw4H)sQWPormyv+vnj}tGvjPlpskDirqap7T-3{4J+=k0L&t{y5Q zOx0JK1hYkxj8@!nImz%1g$%8TO`!|;xCP4+qDam2yiVvITJI60Ua=tmQA>@>utm=0 z_u-EVYM2nUC<`fnewy|Ae#$hk!B>(I@}rU=-kt=y|5r+{rHcWAHT>Hd!TQN+D@jy& z&b}Vz+%8atP|VTd6E&|?gTm~bORwU>)Ex@14cb!uLjaU696GGrowmIu#nGt*C3`1} z=pk4}yN=vpi&J)z!3v%q2XLv{kPABCljKO~Q;RFw`cCRLA`(&esn`F)JQ+nftk5{D z*xZRibH~fGPxZROu9U2U-r*>sd!$fXi6#`AL#!4nYT!NC-8Fl)biAw+@!`s%zs%Op zO>4<^Gt6e@gDSYs!mK-ORvH?>|-y2yA_8kp)Xe)iBFu@>olpo5gnzAh7>!IkZ% zW(9#HSWYh3!02|U=L`=-7A6>RsW|ZSWF{(b)_=&qhDs6B6H}_@jLXyzq3On%ZjCu@ z;Br2HCkU1$wXEU%M#Sm6MM|n?G;1CzVYosy$aM6W`E{Z<`<<|y{%B=1m66g`qtn39N>8~Q> zewlUzf9!&o9mKVp<~TIGI3MbNM=abiS7)Cp0xwB$9bN13@WJmBI~$AM0m^!L$hIP` zaSEx|;;X$=#lSO-yz(Km{OmH~2U8iTR^z^aM^TTrL>q_!FUpQ!Z)1NPd6d7HqT`R& z5OaJpPnj<7#85J3w>+9nARN99{^S{IcLj=^`sfH+js6XGG{zbIlF(Z}UPU zLiwP>&{k-!3L6C1zQh1sS4<{d=2M@YvjgYE5biYF&{i})0%&6o&B#rtKa_4)S8(uzpL#Z@4&SEaWJBa{OU*c^40*P}3`9PSS2I-DRII_JNwZ z6B|rncrk=ds-^Sq6**9fDQ+jDH=!gez_s8XHCb9Hz;t{Gk<x;i$XoZE5p0d~7q0kzw;;yo!h7%KBt!KG)HM19%{5A4GHG21|WEmQ7RWrN4>& zI@@M4I&$R(;bX*7_!YqWuoUWut%(aRsVByO{*sZD!*7NNd zwpbc(mxTq2FInzf{HPu-0xB*4bPRf+Ek5A5^OX`djx}eEyJ}dqMdd%!ZF{R#N-AY1 zIwW0CTnm?Nw+9sQx4=_Q*w+&%?qj7<$e_dNJjErlw6b7|D|}Q$0aNRHh99{`Z^dnK zAqf!i;UztI7j(r(m9vz5ytj{K!H`ETPWr+BKD6Mqej)^)7_N8x+{%JqY3LBIO(y;) zj}IOM3CXD+MjkUWwYhkA@SxPub~)5G2OgqM2>5z;KLGA855fLVN+~0Ct6qsy>{71F z>>292ZBgBOPr7-&xPOpL+1fmZ!S*vWvR~-ELfuGLm=1BbIM#Ttp>6*gl?<~~6jV-t zjRoz%g&d!KYIZ1y$vV*9z=dd!SiXaXV*W#ED~(iXUB}%&Md1M1kWO21f5yM^o?WwE zYdNjd(}&3OQb%@9A1?WzS_&Z70G}qPe_MLC!E{TyyPImJIsteFKwtCkdqEBPcNSS2 zUU$tAx2ModS3a!|obdN;K43|kwVJ|jn;k8~-0>zI*hWa9$O?(~E_n~ruoh@Kb`(-0 z>vVf2gD8GEgLiKHMCZN}J%Vn1BUvsynUi_-ewG>M@_31P8B^6qyI2D$ zQ|q*ZC3f#QPqD+;yKyr{wU$qYQaKHFl&OmwomTSh=MT3YiZ0!;6 z;vv1p=IK6*YaLgoAt{q{FT)db4^MNAm6oxo40)Eu`Cp^GgIcfSO10@uP-l$r;U^;x zkESA?f2`$oLTA~(4NId7Eb9eImRQwIb?w&{WHf8Tq$GXQy+%MbWv|kb4JSM!uPzyg z2mE=jIzp#wh9nc6Br(&&-x&vg*nzoiFD(Si7RX{)ELqn8cWApRdg{^6?cWn-8^2;( z%sG#saQnK^jZeiqflxSvtw(Y*%-r46=WeHU>diz&RniF}rbRs_*#?|}ofZuPR8eOQ z{iwgv4%C?=AFUW2%6RG_-c!ql&G<_hRxK7*8%OIXjsdUXKTcD<<4;HcPWC<;SKwRF;3mZkcmm z!_m%0QK@mg`BK8Up>oKOe0&{~;p{BwJsZ+M4yc=w>S7qrot^eZRk!+im&yEEp5ujZ z?H*45Acu%$v{CBU^c)s7Q*}rUO#gx@|2%SiR36c4K{yBl`PUJ(S`8GVtFl3zAzYK; zv#-yw)o&S}gd@&6CC)&780Q~xyHq8`5O3^#B~KXalAR9_7sXt<(UTgG{7qixDm6Ab z{MF65>}~NJA4w_;v)Y91xSaZf?|NkHkrIW`-~>;B)mrs_y3ok#=`kQvEvTkl%&^HM z>&{+dYDF42h%Q6nfOV#l<<+xRqk^E)c7suz;JNBxit#P|{p{(aW#*6eUTmyQU%YOS z%CxJD=;Ca;dZC%C)?=1c!B&G$uvSgxTXFC$+0so!Fw9#QxS&x)B1P<8B!-^fOfjVk zwt{8`lYglVa3DNS+t?B(gI8;ohk@3f;3UJk`Jtk;s0e&!S@==mB;5Ube_SZy2>hEj z5i$;8nH7|Qs;ichbABAyWJzrTZHl6wO8qtuANMVS`h*>C7>iiGL8ZAm%!=*09!O&w zAl|3~62GRD9G;FF)J}c*MHX19k@9FsM2UKx_yogq@X?wjgBl+&)KEfqDfI{V<=H$( z4xdRlIYPPCw}xa10V`2VUf5=?7@&QoGF&L)DoU4REp`Au3fv9`$Y_ZqUbU7FLv_ej z>2L)S8C(6zUcq&z1P~A!U|fx{ZJV;SCnX%(Ko$d<7&Z669644D4OzFGv|O7)y#eo) zjarddro%(zAkNX+V`Q2L?3hdu zsSSYJe0SMHxk{ZF;o%K(^e4EUWUlr#R(37PiaLYCTk5)eLux-uFMVmLdgk0lHJGUn zY3al0-uR!JenZ#qp!ZV9ViuZy>p9Mmc{sV>#K%tGqt+NB&i#}E3p_en(IjOAVr|K^ z;-Y&2l}4wq1qtg;)OL2W%Xo z(~Q!9tCRSbaAV!^+)|0S%m8tF$5$I{rk8V=opc&4u#f zT>p70OwusiT%#dct8L4r_d|vsAB1^}CDGm2uP||Bn2(#uv%0$#bg_&hJ15oJQBHsQ z5fXf!x?Yg2BVtbDOl4FT4YNlxWpZTPW@HO+tAHe=!(%W@;3@K)jQCr*jxg0C8#Tl- zhh2BW&Oq-TesJOOP|2rcz-4=K+Vi!=1o`RBlO>^$!Gh}Wz>YU4MkcV|6?;?hDYECQ!F@?R=a8e+gd=M=FM_ttzyD$`;x&eZ0rL$b2;opXx3b& zKf8)ZFD`}O4&Un`zx)|_L`vuijTOy{E#VnScCv~(>Fc(W_v7WaB^Ecym8@DURa+#U zpnfCpnR9#!`hDTOA)VKcUy3&9zBP=#sU&FOafD-1DbH5Ul6+Qavcl#x5E_nO$bj@U z2|cxW?1&Ic6)NVS#Jf*+w+IlEA&$09swn0nWBAMcmo(FPjt2#)%7}9DziCb-ZehtV z5U236pXS%6zQK1X4D{t;E81t;{P z=F!%@D7D{yLY-nLTj$7bz=xtVSuO&nH0@-VVn(Wu#uO(L35nqi?A0eN+%=Sm;Vcyb zaH;ev2t(4I57wGOW#@s44kR@Q(UDgxR7U&e`Im4eb-zgEC>6ND=o}B;iL*zFgfk7G zUa1`xZS&njy(ckXcmt@=lY7%)clQcoWi{p20!IY6XOWIm*J2^WcMV!f`(7l{z-zwb`TJAFq~d4t(8nJeCv*?o*Yyv=$d__0fazK;`UbuN>9vnW zr8KKRaA7?Q180*ynR5e>?sU)=(6)gWx;H72`5TX9?Tq=t;&5&#F{ z`-_#5PkdWsW0OfH2~DGV$K@l1ObImibc8`5~rHB%GO zVOW2jI4vHL4TJ8y?@tgQCrKeZ*Z^ojhcr=LKPb*Byd%?45^<~pEELAN2|$(Iy1rO>*e-0EvWEMWD*m#1d^o?;i)}bY^M#=t zN;}0GPFbAzh$KB2k3=^OGI=Bwgs1! z#a%%|n?lGVrlUnP--9l9E7MMfZnKj;n6g1<&+xV5=#PfxdCi&AZ9m$IyqZ2-Fe!IA zz4hrdgGZhk&oOWT73;_=Vqkyjs-Yse#ytVhhRUq}ql%pRQMR#P#YKV(Pr)d*<=^8i zL;XA{+CS~6rHDHOC>>*>ZEkX%Xe<3pP%z_iSgE57HVJ?|#yZK+f$z8UM0@730S8g0 zo}UPqbEfAP1#@P;L8=Z+{iih#GE1-vvnE~8bF@v)xX<^Doqg^R%kO>!ji-!MA8m|0 zloIF%+TGR|0`zx2Tw4e;|JIm(KX&!Z915|LS>OhcRNr2P(}UX|H{YJkxk1WH9Rc^1 zsMt|${#=C9^hH{C?boUdeazq9Uij~;#FJ4*BT;Uy3-0ZZzrB^4<1k54$V!@F`rP46 z8mJ7Nnm^BgYm8MM>9zKatGm707G>=Q+&@fM?e1Wo5QxAny**cvECs%f7@rUn-;cFv zkzcI9rr>lM`D>q~JkPEqXgE*&js3K^68Ue^j_cefv499O#A@fVoqPDTE;Z6y2>S1! z>gSU4znuv${QdZABXVm%=a91Y-)w##FVd^_s*<0z<9c)9H21ryd_GK2sef1G8~)d0 zfiI~B`qEHtNj%>>_Q1!H-|z87*)b{Y3u?GjyLOZ#vlHTPUp_JK68>K^kixQe*Bel& z5D`pDp!jRnC&~#q0Z8D3(ogUKRZj};l}fAWduO5Sa3oe{8G+`I|G(Bk6L0`iicc32 z`c%716eoEFu>tR~{uA(k)4+$bH-882?sD)!?z?h*Lf-jr$Fvi76vYP*`ed-BZu<7# z7x(3<%IZH)d@t|&cXlHFdKnk^GDH4`*ouYn`D!;Fz4~p3>iM=4`F~XEzz@(;%#83SEP2;(5RcrmUWob3Hj#78aPyKQQ5|d)cUj1o#;g-@if(5lFh+ zuo5E5qM7OFylRz5q2~?e00h)lj8VF+HWUc+5s+lFtu(OmdHIe*pLCAC@y&YsG~shP z5|YG~-M8H!Qj}?p^Ka=Cq?>N6s{6SI;m0&0eN0(5SydQYA0Sv^!++_`d+jf<{6)^5 zg<_aZ4f&EeoM}VoHF&do9>#ot-!Y}ud-=L{c2=Log9x)yYP#v-wb8+y_zd=7jp?~v z+WmL#^a>DILS8+PR<2-lFOrtAY9K_>i}(j(T;IWQiMC zfJ1w9!9fdYAZ-ugCOl;gxWPLxk(E73@yOJ67zd(_t7wsFi1fB{Gu$1I&QT90tU~OK zqNx&)%BFc2qtV!mO$X{eLWq@$c_8K^eztC~61U(bF6HfZc8-QJMtqgK+AK4dgIIn) za!8RO=zf!{P{kIIP6~fb?6JzXQ!Bp$`w|b?Lv8awoJX`ReW6 z6^S1nj(XhBZ2P=e)~g*0a{V`Vw|#yRVbKjqN7b9bEl6(Kl8HcGT5f zW~9RQ(ZwyK%ws5*BJKM?*4UenMOe$Y0%fDD66F2LKKlAd2)Yfde7ka}2?q1w+wfkr zE4_gBllx~^sDy26M0-Er*mqAJNw|`|4@rb0A~m9p-8j{AO1?|27kUjX2E3jYtW>6u~Y)>fYeYKtK|2jmb`*< zl+!ks@8BTSc(LhY(>}79!qQBV)0?MM#i0$D3sO0oq186OD^o}*GigYZCw3-QVb;^W zY}Qm_jj;4zQL?^eeECxI8R8x%ky2|w>hsgMdA@ZI7H;1(zBr zp@-}!6&?!$7%&k=bATL%r;EQ-^O^p5}(#~zl1J#cm# zuu41nJp9A#j*z0&#iYWdBHLtF`l11I2Wb;_ZgSH;ir3#?9jb<7O9W^w?fZTlfrZLg z?Hy+kPs%6bxyy+JA4lM;Hd!<`^#G^;Yj$lJ*}keNnLCz+IkawA1OVq3uMk?p9r}P+ zd!XcEipC@21AN~jY_f8`08snNk2$ZOcD-5i`|cL~r1gkmiPfo51<`tP@C>4V>6!;_ zEBL+FQSjsZ$Z~vOQX$&_7L&O;QG*YN84!Y^s`qsngUO-Ez^cEu*RoTz2!2QexeG5y zTn0KLuNQ`wK$|{SYN@6l(er0A2!YtvOdqjE z^`zb<$PQv@36^Mp+xcq1@wJMlf@!C^t+?r)$DDwKX&Hl^oczV;yYTMm_QS)&u1MkO zFxA(6LffSvX-~y&g;Wtj3!>jMb$RiFW|*)_{qnwOWI%$+CTZHHnD}P<8k||ZsHk{a zF$OQ}rBQU?RG-7L=he;4*KbBa+czcH&|w?Nvp!aG_@iIB><6(1h{@0dR&c;m1IX@g zv0Ta*`}j6ku12)eX=_GVxgTwXA(c(0p4d}Cr?-|b5a$q<;cC2+6ff!n7GC@|k+~#k ziYE%-lzTY?Co5)&nM#F}`DbjX8|s2=iYu_bo|~QHP-fI7@Cw?qMc5;edK?~QI#TqQ zDHGg+6WLDZ%wV?!EB53eg$F0siLv2(b3zBnBe=?JD35mC8{VVB2{faJJPTp1_%T4Z zR>CJ7C@AweBFc~9Numi6ikcQAzTf3x3Ed4*qh@MOU1k1s`>_##{IT$;_f^1m>TtZg z2i*e=@en4CvzUgQ*wO_S-~k*Pm%3xXF^t}ef3~9O%-_|X2@4V;2CQQLGML+m!iM%! zsf5_Ybz6Z&ERecc&QIGQ1*LdxWb9GkqR4XMA^0j`6PJH4AjKJ+!xj|l z*Mf(ef>MDfJrX}G8QI+spgAii&RaTKr{#+4l-e`B~X6@sZbkOFA+I) z7FdcBTNU5>Yi|Dk^mU%maJ_3Amytw>kPszm^k4>2BWm;(BoT}*M(;wjAle|>7`=`X zL}&CC(P9`Rx(TEA-rFc={^z{!`|Z5@<6dj;XRr0#&)REU_jTRB=W6V`c9_scl#$~@ z>W@z%`g`NYco?H}9HqHfAG7pv;H^GE>5nJeo&+mzglu!iiN|bNvBUYjP)s?lwEcA~ zSL*vhVR0xv7&cm)5)d}y8oFRsj1$}qYt|RKK!@ znUaVv80(rN=u2s1O9$^@F~in{2gx&DIFqK`xPeo%i{@1b{n=l3bE@#M4^~P~$$Du` z8RQe4077TRiAvfJxbx_QTWl^jiV~))tWTTY$UWYB(U&D%4AB2SFz3IcKL0U%;zz9i z1EKm4#S;%HBk>+bX@<0GRJ_y*Fe$bmBBu?&13^8YF;|S@VI`F=P!SaT1NDG^Q)l!#E|70yVJq|a^xc6W&O@i7Ch=I z9ir=|)UXu#F453xvhM-#wnn{2N24y^mNr3ACRVKm*(4I+e0C@?g1q-?w@`7bSoV`# zT9Ok-e~m=)uR&<_p`tS@wd3nqk_xnj3AyK2UW47(`x}T1g`h}lkfAy2#dwG;{JR^$ZJzL)w`N&9heXMk?9>~#N zRmU{;_bKsdBGTtq*lHbyRrV6>VpLUgkHxe(KF^qVTQqyTaq=0*0Zl4oKjEEmtoJpJ zJ;v8W@RGv7yjH{Rh9YhAgK9oXo7S@Hye3m=-MT1%W%@WMvO+S2Y;}nxaU(?;e{6uD zr?5j`aje9n8ee~>J6EoVD zl}q|(D^2$-VX^z{9jZuxkGP*bdc$psou1F*xQtVo+Q$nUH6BuopLrrneGQOdlgL>X z`LlZmOj|wu&}J>ut3!^f!U0@!B0C~1G2p?Kku4JaQE5~I63>TpP>dDr>H`2U+>k&U zYAc>!^?lGzy^fPs=1zjmPD6zad@^35C5mcxfFTN&W~trHZQUuH5GNGHRCXYT0$ACD z*YIpaQc$|kc?{rFhgzdwmb({L#0|TXy!5H-{YL6m++itR}+r1@ho z-dUy34c-V@WsY`8b1$6XJjC=*k~Qf97ZvXB$W<6`WLS3E(tw3X^2Yh6kpRpSR4zF) zNBZ+Wq2hD#^phLf#fNLb6i*T=rKX-*&$ukaD%DvjE|cM*qT|PJ2UN^J`5MA$u5pf? z`b;eLv`LmHp-K3{W^G>XrajUqX@-vtS#zCA%i8HHS?622RM0@*S8rZ@r;pvEg$$i> zm71`s`F08S>(X775dd6D(+tC@OQEiiiFjz-V|ky76Dl-;y}v8qEx%KVQq#jCq)PE)+d^3CNT9%r2wO>E<= zk5Pl9W{RhUDOK3#BMtR-W{uh>58zSOE$u};tW(0dN)Z#mF;v}qWok2KZY1#qu}M8~ zcQDtbS&7}7_^PVAH{TV0-9;nj?>8mo0B%P%ussN&gR&h`MKQL%w|YC={*;ep(}z?I zZ(Uhl+mst1f$XN?rLq<|70&e>dJUA0{I z2d|1e`X9_8UMKk9oaTQgEdS%UZ&8P|?FB60gP-2_Sa3yn1q46CIpFNzK3?{SxMzr8 zbK4*9;nz^WTMasTIsOf5{x5OQ|0f#?^wpc9KF|7@r2JtjpIH*=1Z?)jcSLTBs zbnUZAU$_7#>YL!cP@eOJx28rTsexMC>Y0yi zJ2i_@mIhd_m+4R0F9&@1CREjTnoq^58@GH~mRZqsDo>{dVLXoG6Ih_`2FHrCI0p^a z~9MDP4RD8r4^_y-$rcFK4isEr2w%|$Ul*O;v;HZ_>!?l#r-9BIb!NXj_pX# z^2OIy_yCnLWaPLGVXB)e?6-(Q64*O&G>G7y@S*DB{Hw-#_ZZN|kZ8AtWwk|W!^39? zY-nTjhPIQ3gVS(LlVacjrCc4e2#C$0e8dt{R#!sN`KINs_sGWDg)T9_xOG}me!55c z;8*L{jLMpt&hthd*x|D8Im*5-^f6x#>gnt2(ts9@mPDw6T&2Gt+DUw)Q2r)dB}l|> z-93L=1Qu1KoV<=sEK?9IbxyR0ni~|D3xC&6a?&p#(G@5So%fasd4085mHyx&}tJD)uvAzX0`+=_oCYDbG% z9{oICNzzb0NN*l-Od1A@Hc%?ZgeVnG;zA)_>l&LfO$ z*!zvj2sr)R*lgb5c45-ROEOwI+MATE5yD=H%0U{ zz}L`eNckRg4N#Nys$UOYSAV@F;p zM^NQve|0xasSJSwhwdXL_LWj)9yxs!*~m_2t9~hIeF^gX6&aXhRNoiueECM|wb7Ci z3W=mN$;mSqU1Le5Qa3%-T&)Avm;r7ULZFs|ok6Yvrw-scCe7Q~S`(c{=9)HOU8Kfv z5msRAq1CEO0S0x^Bny#uR(!CwFXgAVCI~M8Y+GWN$}c_d(~Y~ zt}rg2`IgxLfX0P8(O1gQ((y}RR=u6IIqmniJcq<>oXxdl<7&|OKx3G|$e9`)sbqVz zRR?ap(ZZ7G*n$Kj%d?o4CocR~(4LNV0mgy2c)PJ?yg_*49#%iNc9iq&thGIIg;hf9 zP67bzmrX{Dv8k9)7ptZ&($KB>!u5Pc_@jSDc-e^4 zbh?EY?|pLsOD8;S=4^kA)U9bZt!6H>;^**XI{*t8PW%3$JF`TG{`$;TKSpHl8pBr3 zPwulW4+-nk7J@Bnes?PyO7S#;3}kpV{T*tg-VOV>{8-gAjJej9vyTnLd&lkDhW*gI zHq*H!NE&XY?>9Bj_NHsBn`x|wV*RbnsE0o*45V~yB^ivMpVqK_SF2Bc>T@Kiw)TSL z1A+nh_LC^`4o!=k+(hQZ%jlQcw#T1!%H!|L)KMgrhds!?b7QHzi_-4Oj3`Jby;t(W ziuR}=PBD^vmH=MW!Gt_{fz;SYN$TXVTY)ZDCMp;eo=nW53CiH8gHCgFp+jJXtuYvtDEXaPuNHg3X4FEG&GfIJl7;h5NnKCaDU;X}W ztsq1QY0M!Fesot6ag&k~T{=1}#oJYQ0MKMbBj=C3Mo|JD3}CvUvapW`SkJfyDe!>_ z*8s4FtHQ?!-BDoU_2aJe3;0;FIs}_sr}jjO>wZm^Fp>wFaxvfa0@-~|8ZczL@wkdi z{LY-z0%0A{g4V@)o-PL12<M>cZZ@q@g=?qs3Om{bkJ9;vn7)(evSe2dMjtaho{BC0_$}p2R z-2@Vn_e=(b>V?4lnoyrqqX8}Wz4f|Mijim*VEU5f-s^rgr_E#6(R;@_0DAQH`6Et9 z+&A+#yk(l*u$*;cR}mxlQ+T_Kq!rKc6fwp!KEq5nO=2V|5jD5==C%2LKM;a`(>atb zPw3O%JI1O8ds)a1i?f`CSR4B`fM)T}=u>gWOJL1xWSL z!nvX8sGwI%@~}-^$|kI-;NO;NYg#2+Qt-1y-RYih=Qcq`&2Wo39_bEq_C7iE4;jd| zGVA?-*7v(hN8d%(pX*Ny7}ZC=F_f;UjcWg^iGI!)e+l3%SRbuk!#4clq-i|>cBNkPMT#$`&p9hfAv3XTt-Mk0| zqJasuw+?mQJKKQI;yk9KcsyOnx1D0#U!F@*6i}{mHOQO0JloW)&lE9hXElkcwSm!! zShDmf=ZCg1$C)IIh23F>7RD``c(D@ny%n3X#gC1lQsL70j9BxQ2mcAhWB-0AI=^#b z75u3&>Qx!JL8iqnj@-|XmLBHL_g=WG$OD@MDUhyVm>OyRlo%F5+)~Lr=5i>*P!|%= zMn%Vujm%>nSi9uLK?YW|H3!W6LriyAG%QrCeW*7Mn0zJ$v3CL)1u{F4g2(DfjXeCk zB3qiFQ@!gPfm^dWc-l-b(Vl?z%>&E-QiI3w6RLY$lm9A~Fa#Q7dI* zrBP)n!%bIa0Df32>P42=Hp z()(ygKoHu?vGLLA8-&wYb63SmUd0fxX@X>fazd>1uoPrvH4^R#XGk_B)6`MUkC%E4 z>b@#IH3I~do^qbZ<1zJuWBqHFMXAu?v-UY=(R&r!vlNQCKGFr2%Bw7b?`Sj`u)q;_ z|B+X%Qde2ec|2_1SHJqRTk61$AXsmXk2U%H^B#DXZS^f~=jM8)CT*WMBz68W4NO^U z?pL!l&(a*gx4q{(TV@S=1aFyTZSpOM(gJ?n!}h74v_~R7LfXV~R9F0v47;L@sW*(c zmE{?qpi^9^B=3F|qV4Pn9v2 zbQhX_pUq3EO}{$2iu2k1Ym_i@1rI0mia}j>Mk0z${Vx)aTKVIT7kl5M7MBY7rLWfx zwQiPiHS2$Z&zZ(4FZ|u84s70?z47}F?9 zb*aiW++ksd81K?L1+Kk^5;a`8Eps!^i|6P_zLy`r?q#p)+LF1R_hh~M1{I%>EbTmP z{}mH(v0xf9A&YvKsplI!NyD6b|73MG;;90QmR3*k=y6j7@CR;>Cg564fOm;l^X$Tf zx(Y(~1;Y=}C>7vX@_vYqZs7QTJS;gO_BJSSxmxI?seg#x#=o|+A~cKYAVddV+GIj`@)-+_eJ-mG*Rcez;w9C)@R`hC!Tg*xSz4(YgDV)|ele1h;V{K|1FRPt83 z>~(h!{|@1&Y@0TQ?Es(x>hu;c{A#Z7v}E#DHUIU54DMV}F66V;t2MyEfi+?T`WZB`0Xz&-kc`O+^8INhJ4mL82TM>f;S%QQU4@k zwcJ}>L|k|B2VA!+*8zpgV5j-!mbg4}|Lf|2v-KWkpGhp=jZACfhP*`cmlOj0rK+f@ JP%dW{^e;C20v!MV literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/allaccountswidget_preview.png b/android/app/src/main/res/drawable-nodpi/allaccountswidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..07288c8e422d9ccee5fea027841e5ce7361b718c GIT binary patch literal 25947 zcmb5WWl$YW)HRB`ySoK<2<{LZf*o8A4#C}my9WsnT!SARg4@B}JvbcP{mb*#eSh4l z@BX+yW@`2BJ>5Muvv+m(S~Jn=s`BV4Bq&f&Q0QM2WHh0mU_ek%(27W~|166M$_D=m z2kOdNvdXqD&#!MUuWwJ!um8@^FK>@eFSn16cMt#I+tbVIf0pOhrJ>5Kh+`W9fyu3ZU ze*8VUeE;}B$7edbetdfWxPAWT^|7^ozJGFcdG~qhy%6t^$tyq&#VrO&5ukjX#i|=jO?=uDs8^`_m9jLS2T3> zPxwXu68C#``JM)aK$85cCcCsbJhenE^3go)rFHVnx8!wp`|afFqq?p+yW{2L1hPiw zv2*&~xA;nJdb%MLb~dnHHS`iw_h_GaM`m=gO6`8RaVqF?wxgSV5Z!!IJPZYm%V_($ z=T^Q-;j&8VRGL_z;R2e&%j#T7<( z;k^CFM>ad*pChuf0>BLl3Ki$S1A2BN@D&P*3hIlDq?Y$8q%+#@uTJw(HpSdV+0^Z1 zz(ej!=Y?6CWta?>0y``5*f?I;GMZWLA;8qC&d6~osW|1gmSwKn^Lo%-4OZbcm`_8K&k86hK5AmRzO7XX6ub|g6CvpQ2_&PRfx25}S_agAE z%;fDrd@INN;j4_$IcCuNT=$2k8hW9Y>*+r?ROB)(n$i2fmy4k18_LbcFUwug7*B8A zuQO0}=R#-^Drr8}@+`5e-AOh{Hcbd6zGsOa_t^qva9x=(Z~e9J7P=K(4fc9#>oX1x z#ak(|=_bbJPPR6~2SKkVm>i`x*mWPb@^_nyi#3uZ8{Z>lNmt2#*?lDd1b)#tmJo*d zMr!pc{(cOf4Ry1WYimKA6(UBm;a*-B>B2LPXBEu8}F$MWZj&#az5UJeDE zY{+|zyPtl!PStmGAPtv)U}3{Ezxjneqv z(uW+9(kfYNWj07WXRRi>-*=vddb6Eg-@Q(%H>&yA^zho*O8hT?KHAgY&K#ycc7wzc zR|K{m7f6We6Mn_F@cw24qJmpg({*XU%FC-WZxwv19&cLm$+UoqVybvs?)TqSSx8dot)44xi7iN$e+CdhFYl1}x+K8C2mF8&TFm#c@2B3P1L!E; z-~aeHE&Pl+!-h00#A`cI;~xtIe73nTjPj7g>~m@}isY|OCg%X?O!KRMcvh(GObEPR z3O8AJF|rxJgQ`kVOW!wKx6Zo1pOz>&5$$Euj@e})oCE=Q(E?#_+823`Rh|~XAW+Qx z11k7k6}@K3K-g78Cw(DpYr+8ZQMJCY`o(^6e~o4gWlVgk3UJx^UcR}psW27+sYFWM zdZK5n*=#HF50;qyr7JvkKe@Siw+NxMrXXShG{HXGdI7&1KUZOA{`u3~{1!X1n$=s^ z;eqRhX%xLf_$MXXsLI02$cMba+K%8ym2}!>5-o_s4~b{{ITlsQ?BVk?^U_Rc!~F@o zncp2QL&Zw;E6R^5hUye*-!_c89eR+nW5xmb@G5h(*_)kJ5FaWbQwzW4)+zm3T?a=7 z?C?U2XMp1Z@YF{#bc^&HVz=q5s-WXz4Y=R?%rD;07=sy;$p9iqP}!sWCq_NrJs+kZJYYoE5b`SdN_9G)a+PZW?EP zxZg`azGJp+R=R^cE8=uCcY373Mo1w98Fw_42Jdh#T}1}HZ}xYyj`Tm|eyWWxte8@c z6vgw%eof{I^vF6(Svn){UyT{XuUUA`gI)>J6z1QWg%~Mq9R{^2Q;4-ExD8J}LoQWJ z?6=pn#G{)L(yBAK>AXw$Ug>ir^fa5^zV<7xWoL_H!t zsb1`x%nsQWng#+N_f06ojQP>JydN_-1}YOr8c}xHXPYiRRAa%Ip7Zyd*_5R7AD1zn z59WfdUiRc54dR+da`dHBDMWS>c2pO!EndBD?;pDaIK$dztKH~hYZ(4Z&TqQwo1Hn8 zCf!#LpM}H);{&m>(dWph^8q;KDc{6i2(x)2Vf&sk({#4aw6*ruS0albB2rD#tf1!!wKev?x5W%yU6orjmWRS z46imfbdX(>+P06>0rk>GG#>8m2;WYzTXQ25}=>?S0$^O#Gq$+tN`E#SxuQ0w~! zVcJ&=PkKmi%GnL43KprJ=dm?ZQ;NY?a?ZWVyRmR$3P=!mY^5Z-P_yejQ_z zKhq8I#mkNO<&L~X33ICXD*K`<(3-&{=wYB$G!e3)*SrI-E7#eiF; zC0SJHYK?y;>nC4BXIpa}MS4ckhNn9Q20M+P?KISrNw-e_^0(nFl9h;Pt~AFuMjt+q z-$`{~xgqHbMoVcdxl=Pi?NF(h0ffnT%r_A|HU2ai1wwjKQ&dfCa&&~O?*l;%xRv>Z z7XDj}Up%H|sEE9g zz)kRiMs<-ax#Pw0age-&xxvn>J)^f^)=@Qyk4m|;+6n{Xd`&w;uK|_%c^^D@@Nh9c z_F#{yf1gX>4~l<5M2z2XbjKF{y>Yy|zsy@yFJNzVlNF^COkTaVg2$MFBt6n>rh9w6 z+{H$|zicc;px=$RQqA&OyY*?Q=AkPP2jr4lard=KT@t!XDH)z&mp`{26vp-rcP#C< zB8A1;DGpDx?9vpGQH6W;c(ZH9}`MvZGPCw&8Y1XWK?i~)FQV1)E`c1wA-sm@~aeq#}KZL*GvDKji4#)mn*NW@p%Xw zARbm-B^R=_RIS4PmxyYS_xvIC;euX9TfpnWxTY*5RVe4q@8R4F>+gu6(OjsJFmo$+ zWJ7>jyZZtRXf5L9<>A~oq-r|~CrvB6soeqm?=%DEcSa0#F zJ4S>n*fQxHY0s|^${dMM!sJf>(O##mN47FW6Y)d)2nbp3sC^Tpz1_}Cl)U)OFrFou z{K{w1X)AgjZqcpW_yIYLep5{=TVy7tBGoKijhx=s64gmqv$B#ugwhIUm~Xkll^7H@ zvZkUVSw?`HeV=G}i8qjAkS(3g;VzJ;<)~xtV+mQYjO#L#xKDMW{1s}B=y<%`*0MEu zwk1$`PA!XAse1DEJn|0P0UNa`-j*S)zt{%r1i3%hQ#pfby~z7+H(7$dbjQHzV=_cZ z015FDdkbF8k@vJ-+QI6`w_K8l&1-xqNev~FdfeXY4Y0;hP6l4TzXw_gxPpQN<`C_l zb1|s>)K6N>R94L*gZz*m{ez6hzLD}`-jlX>2*}=lAObHkw^lX82v;yEME%?kbM)Sd ztfw|#YR8f{zLJ8%JRW7v+;NrNjRN37_$_`p-@6@2?JIwueG4f+qe@}(DX7K(AkE1` z?WYFwiLd)|Q(+bPmBZ6G=$go+-}R@~@9zSp>Pgl)(j~BAty3k_qkV_Bl%E82@mGAl z66~#K8X3SUQf8hpA|`M48lm@O8MyIcU?lX0z*{t-gU`2ALJmT=#DBu_dNQbxLS2W3 zc3`R|P5NO2yFa{>S1z0;XkU6Nz6`fx-eV@n3cl3$7lhobn)Q$DG}|=P&%=S%6l@BT&`eD&#AkM>n1 zF}$D#G72D?MF`vS!ZfWrFVc$urf3{J-Cds!YgL5{G00h{jsb3QL8lUv9@sKpPs4(S zb)2o13#qCtW%H996Z3Z`;K}s<^xb~d$8|d{2Z~Yn2+_-d#?ns>5!_78iVDtMhm9}e zQdW@$((ajOM4%~SK{SmdTi;q~6K_NEE_g8Q4 zdVKxdVYXDYB3ashkOjO;?;#Oa5aRa|{$6Y0wpOqlJ$QbWx144T{w7R=h?s0N5lXrS z@^gaJ6rJnuqbI$MnS2NEYU-#2U1r!PZG4UN<|l$KK4A#?I68?6E)s}IA2tidlDa&x zRvA{nuDKBL0pXM4DC^9|BIG+>(Bj%xD%B%K$$VGriejbAqBam;0vQe(Xt#Mi-9c>+OK}O=FpPB=51P zJDO8{oFCwbSr5TdBImt<$W|165AD;50kUMMNelmCKpyksbnJn$U$16Za1|H(W%Dpk z{LeZklvKY+>WL8U{Vqdq0`-BPk74r`=<3P;{f7iNLL%V4^x~U(lUc~8yhtuNQ(+(j zwJzao3t=*RsFxElAc|*z?McL}K3tv!Z{OE=z|@Vsc`vMWvGXkg+Bo;2S7HHDdQrH{ zYU)c_o+D2nb#+|f5+%YFb@+`0$Lm3<-CmB^kdo`s_mjDE3+}NT#MCw)>Z&)I&MT1~ zI3ruUW*QC2+w;UhMD)Nbk;Th8OxjooX$yGRd`tCV>p)MR9C`EXfa6!~v~Hb&# zo@YE0o8PazOyyFWQ&!ZT)P-YnZ{>_X?~|Xz_dBD zmMM7xv@KLHO>DlADzZ-$5AckoA2NHood4n~#{P0%)JDM)deZwIii_mFLq6?<_%MNS z3j;qDpn@8s<(2?>hwUj4J3Hgc3_iTY11#|EVsgYWz3lu-JUoNxH#hV=Y?jOlk9{ra z!0y0}UKEq{Bpmii`RRw%1W$X=1^tYQHEtj82HX+up%>~A_?Q3AHXC)(Wvw#WK}$jG zATK&A$`FMC<4^~(5amS?9E88B1;QCrsKHBuYH(|QJO-XtL`)^@OBH7MIrlx2#0WJ% z&AtnL$_=f8ixS;+>%Ws(sGPO$Xc3Xwo9VBebceL$aopF3=)ybnIm03~Q}PyXhT|Td zll+tsakmDCOrXfIXCllIoMH`c@KH6;Gyti-v0;cBl4aI4-@Fa;ScwN7zDzWQZ*tt0 zJE(2$OBkiNuM>EzVITuIwMZU>r@Oy6@!TGl%u$t*qp2A+}2zokv&Mq>M)`l+YlecH9~$f1w{!_LH52xQ;lSfNNDhVU9`pYvvZnh}9Z?1wq^3=M6K+S?fP#*Nk zwJ56o9Q8%<>thwA;FBKOSch++#@*e~g>YHq?w(~96o5JtZXp$?Ue3(8DeY?8{I{lw zY?V?UXf{;4lbOR55m|r3U{;jT>nbNrxrZ-58JRwKUl(q)lX&hCP zHUrhDy<|7L99!X0kbVvdw!3FxQy>~#lc_h2%t;9smrxzdxs=I_b{JKuNerF)lO9;p8= zcsca!-feG2^QK#40I;QyoUsPTFmOf(&A2!} zDx#OA(Ogi*e-?Gh)!cTQ;#wrYl0!QHu%iyZYHW%3Gsy}ZK8CB!?(ltpXyJ1RU628sTO zVm;wq=^D8mC8h2t3w+)*>GFTxNy&eQe+!3|yn}~F@Id)q)h`2=>%1ml4>Ok%;mvb+ zuDdaaML;Oy3?Cec5y@x~vZKO|YUW6zR=HE(r-M6n>7wwbkrBSJMH|T8Qqh;k!OroI2t#ksby3Opz>OEaF`#e`E zQXSxbZPcvf#yzOYKPN9YOX`=&uJPJXI?tTl$FEFvMtCYZbzfG&@v4qXem)ouR}tbf zzaLzj9Wh?QfJHP7wOS2MK6=2-ie9owyiJ{zZ)L!1SgtQphixwp1HkJ#P77L~GE94p z|Ln5m4ke|kltIC_Vq`~-p@bOqwLIMNQVNlB6GfJsOm5!}U*dAQss)YbsuTmnnL$r; z{xgSSZ!_q-NV(pkXx}*IOc+j9{15Dm-L8U^ewFXSpAIBk(FiEr?Za zUVi)&$4amKB4r76`>!7hzIL{i=&bGB zyYxe^t*KB+DLcJZ+y^n)QHiR&GOPFK2sfX|1-jzS#ty&#N?hL% zGx2-(*_oYAQxoSfS$BPjs5~55hXX7>-(DdS?zz38OQ;)XPwq~(`Q{ zX$@o{Oux5kXv|=+S0Yp0b6|ehTz?G}y_@~J*|!(A!peImg?bovVdMa`EMx6zhhwcA zfq2kLeT#sUtnI(b)hcaDk1(s(sDn!Ka$YXslVranq{|+hTdy=EEKqsE|KwZO_)tko z+iy}?LjGz{6E4x#q&O68cKVrf6x<~x+hM;P_^C%s0C#osw?v2IazM=%HSJ4GEIm=6 zh8~r7yGIcUB~xe04!&EgBK>-gMtX9o0B)=#Lc*ElX@%6)Ok~ z9;NvY{4;|8v;7bFe{BC#`MFz?kqS)xTD1;}s%$ zG1&frSOsvF3q1Wl6seqq&lS!J`a|)5YwQ2B^Pmf`Id1YrD{Ey4YpK9VEyHX%;g^(w zT2V}fuWVye5(IOn90p$aX_?7=Uo;2ebf=?s+x2L$=c51KaC~v%9qk_5N;TyKFGT%H zhEaMRL(&|-2=3$r;Zf%_lu=8@`<9X z2zhVia2gqVnOTsY$&~EFk82&%QYGSuUGn?XGgzJAfor3qABs9do2-@ zM2*Ke>pby@v`2VmF6@hlq5i&n0#SbBzN*}~c~+5LZq`#f6Ejj%Z#w;Sy7LDtV@-9G9(58DBpo&Uw2xzy57!OE&{$4r$;Y@$H35}P>Iak!ZUCgrtbTMwCJ%q)Oq2942Sx$ zaIp>hHMg6`AzR@Z61IgZO1U2B+?OYCgFo)De3VAZgbpS4%Hx~60bTL)co9n*3=B@6 zB4Bhsi)GlY3}bHn0wbz1*4ilhKkv8X`Cs3!IR#yhv`~t4cHSyJ=p%(RwK;qzrCb`P zNaQ&Uuj(f-_^dl6TU2jgU0F{L4282XPq(aM?e+bAyRWt~`vTM9`H!YBhSA8a=M#l9 zfTYTCVOs&z=7x}r?e6`|RxM1H>2jK7^tfHoGtw~m;pLEAhO%j5lczRCQ#lw`vkJRq z{;2MyV&2AjItSx z|J^JXKi%nw!mp1Dx-{O&B^2F3aGXSXYf}FVdph!m!hV|!;{pP z9-@}R!WD-+e6kRk%wnEJjsA zGa$KrrYWlh-f9Zur&e$2eu>(5bBdKnHCZ6Df5&%7N8$l7EWXCT)gSnkqdHTUn6IgY^J zWKNDS+~RtTx=U8F1+;=lp0hg%(lFCX^1grJowG*M^T{z3jlKcIJ*hkX>w%2fR<>sf z3EyL~52#&;twvDYTg+_>LXqz&GuIWBCD>f{*{m$>EpFN~&=oWlkkqNoZ9$dj3dqZm zPG7t8eNVr7R8nUBiR2_@P6`u0 zaLrUZ2_N^9ecjXyip4zhh5+9R?xHW2Jgou=s`n&`6a{uCV%?!RcB)r;H|v*L*zolqEu~H6GcxHKdl4Yqw|Lqc=O||)apsK~eBPk;{Yu!#m+im5-yy-?> zI*!QJePcX?dKl9=bdJfQIvsD{00@^UhaornL`_kzku{x`C`xYsbWSmlSUj7@*x4#3 zS(5uYC>hDRf)txE(56hCcAbT9JiUfxQiLNd(K0$>7OE|i&VxzZwcy5&!nzafysfaC zg6#zl5*Corkqmh3%`v(}<3DbD=nn9s23|T8aG`Axg|lIO-G9n=l9p^%flFS{kkeQQ z@pd4^qyj484Rq20UD7seBaf=f;m;s;N!H-LD}rovUf!g2{=Z7#6MeLc-xB4@sd03? z-lUKXv%Dc&YdZ5`bLogn;CHP*DECsIsl0Nl=|EcjgIK+saIz~@g2oM8>f&F=nDHKq zBCWw1Ax)>;|Q~IwY>~N*!Q&FtAwNUBMvP$4WtbcPPv@)E} zdE&Y7_MX^)ka}*@B$R3klKu=dSZ<)1&k~is;w&cuv|3N9-KPYWVO!3Ek#8t4fncwG zVU={7LFdl#v6B@LnOue@f9&>scGah6!;#X@LjS7;s5UekJCT75b(-YmJp`&WM^Um8 zH6#*7$^L@D!dvuYF@yG?cSf2|#`{qT^~cx&8F1A+q#JhWA~wZJgIdSGF@2|`CpC>) zAIC}}{|JI>Zj;k0DYQkk|3Q9rVN83b+V;I9_XbUyU@G2)7i^!KBq-R`^=U z$1IcMn&6ZB>4**}dBe;uPk`yo#yUgxvO6K$|D7tt(T^J=z$83O(21y6@6&6^h-EB{ zw}C%TStNCg(FK%)<#f|-7-;TCve8IQSkE-`-aPa8x;ZO4h6n2>ymbs#py2!qpU+($ zwY`>ZwSx+|dIigC#Rm%G^L4V}-^j#VQNP<;k!cRG=j*fDVlIS#T-<(W z&mhAs;2qt(*Ee*cX5YEZ+L4NHvD*{TZmMyQx3Q7`<{ArYTVnpN?$lbC;C7-TECfRv zc5@hDDmrQJqSkhxBke;amq+(xa&^cd9=b$i|27N=IY&n#Dh^%Vc|9R1ew%mcds5{Z zF-w{v@_G92H5j+Tnxj3}c$LY5igu*JQk77alMyCcQ6IQ--JJA|T5T??7mA_1v&P72 zr&!(%9#NPwJg`^ryw|v^ST=t5I)iJzV6Ow^C>rQ1em!GPZ{ro zm;aMCXI@$&-FYoBG-NySJjO_Uqm+!i1*sRKr4m8_=)%XbqOYTt5ES_Pv5C5H>Q9bDyQANc2Fq~~ zGGe)$u-R@@oY1Nq+Q$#rK8Ha#o%T;#2Efa3v~A(nnWoo)X?UYO)62${!od(iJ32jT z-jGUW$ta6@*;-*I6?(yTQC=>cqf?de=|^_q9N2U{-DWpZBk2HB3p1y$&9oLE{gB;l z;)(GunF}#f^SKItt$A=;Z`w?Xy}Nb@v+yuxs^WJQdCwDdUYJJ&ieTOp=P9diVQgl+ z4UyxgMF!W-)XV>d1=hN6oF*Rdrxd9(6=fylH&KR5(X;vbMb+Ce@y4j*p_$#jNJk#r zyT^87jLq1|*rjRS$!TdZ8QWf^qm9@hpX_I4oB7>i!=z#cDu=6bb7|nUDilr3&zobr6sd+AiB6~xgy;BL6igAQPzwsUmsp}1pooui z+vjgskMI*{I>xAh+w$ijBuFZc0*=emJF%Vb$O9Rk3AAUREzfEWM({s;co z_8%Z#$qR#sD<*htk+=R^{Fz{Ggr3Tn4b+Nz&@ykb zLFFZ~d160QnM^GDwRQLRQQyI50Yuw9Js>sH zaU5;+b%`Npa8_o_xOIBu@MixLNN#uiDgC~C`neBY1&7-k#f%hsM0InMuEh{(1U*T}WgEes(5Eobeg!|iAzu{Yot4AZ`g%5E5NZHR!# zRtMwM3L?V0Hjw6iQb)r!sut=rac&KhCBLhdj?Uc&aum>-4Y{pYU6IO8+R}!#$3c`N z2}%i@V>ox$6`HdrniW|5cFa{Q5P@!OQq!r|g^fd;TbyvHy*e0F1Fse4=g#Z5aEFH- zG1yK-_e^1sX}SIlR?3Xfa}4$@CBQNdB-@9v#ck&=Rtc_#PpoWE)_O(1XQJ?`eC>*2 zJ&|mtHOin4Or9+-`6~UE%@}PK z%Oi6_L-k+wJAVw%vG%tO$kbX#dd@P0WF!Xw^a)UTd>)Yp1ugDaZ1$r@Fgd3|QqZp0 zQu-~e7&!P0_DO%y(;dVbq`2zz`&D@5vpuE^G0?=(P}L_fQV7#60MiJqQTV~6;f07wB02X(Pf`>@|R+|Y|py%O)mo|Eu4#%5|j~8+5gsdEO zxdEf+frJ9Zfc(a}MV|+1LiLQG#?oS)6)h&FH%F-&6DHQQ6sr;3T%quSm2~ws8pgWP zs5r=&B*Csf3@+baeTDgy*>xQmYB$ouuv?keo>LFhs!G~T{zU5N*e!wP`qFkOE+BmK z(L*zpqcT$Vyhxt~EDLGP-YfeeHw=*a0>KQGKxhvq(=1i-dDLXV3Zr<@$_zEf4rz{j;11FawvOTsEe|zgw{TZ>8t~1j@91;znm508wpd~eu%fqHI_Xj){O`X$)WW_*`hxQML?pA z*1(5uqHvTtXAmw)$dejTSdRMrGjn?Buv&vYeoceGy4HzFLPykjLjr6ZWBRvId3{WZ zpO#aahmJptCWic*HCU=fw(N&?t`0CnM5DK@J=%l3B925Hk9;O3iEeYgw0%M#L6Vak-J8HfC3C_v0r4mvpqJk$=p7E9Nd8 zM&p`Z$B~9HsM}qwOY`#RAq*8UwNxF?ceodBMCn4~Dzk()$S$-{&jwQcy=NGEX>$Io z&dC{8IaUUvffIiWy@xGaXsaDxj{zfBf>|!mz}}pFzJOaWU0;_UZ}x(Y`$Ccx?sY?JMu5WcEk-se9` zw_>OF*c;8sm!SdwND@~;Ldr>%8o3A}euY$ea7QK-N}J#qTH|+C)X#76If-UQ>M_bj z)I?rry5Yo?YVp$Ks1fK&9(8BxgoRYu@En+cm`N!d1Ap%<$6w8O>dp>TS#WJsvqitL zeZ!(sBCT;r@ssrAlckt?OvS8jG8`+g@5Q(K*h5o~!C^Yh$Q5C3R?!M_ zBR=5-q+mn8;{wlOVu#rgX32UzIe7{iHP>Kc*Db*Z%BI|IymG>RZ$#+5bakHbaws|i z9i!e9C<07zyqv55(!;1YBL6WNNGxXvMk9i4U2_mXgKEVn$3cTC$u9VTl(dkX#NmO& zrJuzTEV-wZaidHix4hlc5XpluFAtLnV);)GvgFn#l*y&?_)bI^qN_=mfy><`@(_#4 z^~!_|bfcxoEJsof&$Wd($cpD>BcOjYJ9aE@&vU91RnYfSg^Q7Dul|BU|CDNg+usvGqbeYu zo`MF8(s^Gi(33?T<|+QpOSO(`hw79Lo(fG+`UT6 zVG>2x?)<`xsP)?5)L2kCzE3OSwW~`oy%=j;ID!z!+i-xY{3FGQjxw8O-Aqo zHCITU2lUDs6WH=f$X1A8l)_w>I{V~rjPzdMXKOIY2;4x1zG#|;!IX}5kzCJtcoKnG z1(f+c3`<<)mHLDRdhkUxG)KRb2f;qJHA69LV67H^c5xRpC zn%Ed`iTelM@ZoilozDZlMT%UTU#Ii^_WYnY4Qs^HY|e(4;sm| z5r5S4R}mB)$r&3cB5n8l-#uNz=ah)mJ9_|3)={CU(r25b5$s!0wAU+|7@X)wmn3}6 zE5Z2^>dAJT@^-{C&4`*XkWR5YzdL=M3|awFOx{iz7{?45K>FJcv0&?qE)1;#*q8d~ zxB5{JH|N4D09+gtQ6<=ng9`-Eloyr%?$OtO_fE1zB+V51X4#-#99Jb#wh9N}$M3Ju z3l2A&2o%@45DkGM{mFXG&_;4QM((fhGxq6gH#R!&L|!{zzJE@$$X6*a#*KL2w#W=c z3pOY7{AdShW1+L}i99we$BTta3~Nd3s$M0ZlOl3iZo+evD7t3}9JpOENgfd=Hz{4n zPeyTig_mFYT4jdpIzNK=C`fh*x|xG@WxcQBxT;@=4@I#<&o0>@-#viclUwW#%VYW7 zrn)yD&LZlwN7C01mPcfxjMHL=Vh%weDR z0tTophMO#(0sE^T`tDIa>bZA6LRBpkX@%KuHLe%X4jVk7r28XG7um~Z%)pHs#NiPo zFyD#VL0Vq1624mgoxX(PL#thOFM&cW zQXc#D3q2OvOJ&tk&!v!-$*3a^1F7J?LXjB~nwimskpV-`a5dC}zVBA}? zez%Fh0o&T5?YNT*DR_yY>C`9372lyW=#WZGWX3lnhe8X_tLj9zObw~Pf+!gHj@d%a zn62rEv*YNsqK1fa;J-q~6XZLXijDYh8(`fhzi~U_m1R&FIYFsLseGK2h&x-(4i-sI zhbuNQnfe1YEuFZ*#(BIo1`ejH&j=BdvMPzG{EEa6W!Aw3nX5p73T@?YY-nri$to)u z@erUOKgpITxxQ$NlnAMwC}~e;qay+V38N_SvSCrJaR6=*(6U5Q^E{(LlnCc2NyGz-^3}wd`EkwlV|`JUn)M)-96VGkM2g7o!UM@ zFM(p{t!ucoj08yEz>l%_Ni~zUx|`Sal-T`D0>)X>LLS1qo+wVT4&U_g5?ot$N9S+c zle5?1sC>^+R=4FOh+mtl3hP+99>b$~m)6lg2MvqMgwWDvG+c3l6mMoZSg=5nS1#qc z4D&dk20GdoDRAHP;vP#Yvr%Ub2oO@8WW=B&r*j3v(`<3KUVot7%7Qn3ItqOD3>T@> zmb(v!D;?`9d{==GM8_nLq?g4(VpkD-cUju$l(X(>LaG0k3;uue!?Y}8r;!l0PJc$A z&#$C~6JYYb2X>f)uk=4QnDnO&AnhOJ%g$YI=~B`9A4H&$M*}8@8vg&_o5z0BvXtqh z?rYO@Wcpmn!4$dH*x2mQGL-ct?IVDV5vV-*((Cl`s9_{T3k{F21{I#TfYA?$S{xIDaLknt8~6TVCGkkm+K@v#j^12 z4D{{&VVp>8tGskJu($QbM?eP3Np5iH{PE)k^7l0DEAy2tPVdu(Qw%EuNuHSB{gJey zOYW?$i~UXZl-LU-`^E(6byD@73ZyCLT^aMp6<2}jHH+KNcg!lVYeFJmQ z45_$`gwJR<%+PxK4&SeA^E``jMa@MUq~Ro^Md(^Uv3NS07`L-(+_|x2^o#Y58_0Dv zLuexA|G0^GacaGQ%BgtuKZ>Nee~P4c9*DWdf$!J=F5JAMhwj9De6l67fi*H9ZoJLA zD@_ibn+87ja3i22+sskkwS8$xi%HvK`!Cy|x6E(1l&E&cKNI9CtW=1D=2;>a`|81k zyDmwD3>{l!D{B|s4>fd{l;Xb}dcQfoUmh=+1ifAjn*_aWU}lfSOYE~N_@ggiAuk@x zbwYtkm6G`C;i&8f1V+`L#=iE=@s(dL{F5N44C>Vb;8W3m7t=e_TGnF64Fb)gbkLGL z(u*m7J7O#6gt~tHS(;Y2g2do&IkFWCHvh+CTukm=$xLW2KV1C#OhcwC@Gw&LSDaTU zYk9xTpg`TF`p)*N{=q1ssr9$j+pUW9!IDCLRDFU`?^0ROv(P@1W-Kl9lfj0R^V~4Z z(nyPCpBGS7%^|&~Xxyi)k#J-hlw9|4s*p5g`0rPe8rlx}!Gj7E(zYJ_7<|G4eLB=@ z~yGkW{KuV z4V#a&dfx;9)2DNRLQ6i2%CEfQs=UqfPKB z%b?7t@q;W2c^fO_H?0z8wUjD({BDmpe) z{K!tbES=#Me}w>P;N1zw!G8o}KE{WY>Ie*IE5Oa3Vnwj^%M27=Ut!~d27N~9?J!?r z_#s=(YJILwqB4U4_<|+`S)0!xH7B;A$>dQ9UM9eqrm3Vi8-y$7;<>dv< z4xkclybQOW%A1PQeW;}ldv)Ons1f6!Tq9dxMWZmL$F?o=wlym)ySl~WZ_R@7dI#v? zU%6ikig{_qW=|s97fi=yEh?Rhd1ptHok645Hlq!FSEjpl?uu6X~kKGs&_4>Rb}d>w;*4tEhc26qH(k$A<&@5Rf=_n zd;)n=@*Y=wJwl!bn)!ul>J)rV92pInp+zh379ksMR|FX}ua;KvSfbyjHTDrTp@^wM z4>tFA>ecq>{mvmmZOBE#gVTzymi2>=F>uT5a@dzw6v|J)S*%Kaw$&#{;!D~J>g<6m ziI3Bw?jP>ZeHWv;TCM5CJpwgYN{3$xOXk>IB?hN8$8IYY(}=R|ye} z(_AHhv4dVJ!s0G0+5DFp=_}F+zY{Y$WNU}_He}goiJ7&%u)lXO`WNM zPKG-@W3~bPMx)%9WOutTaLumXov!oM;&BgKtSc=sR|*KtDxNxdQd{KoF;ebg_{SJCSkNEkHk-E z(Bs`IB2mHaM`RZ;sJ69&S2AR?g^8_b9tM+qc~_!F^}+nGclc& zBbgm@O1HLigDT@4PRIb1Wt9M*TAg`sAMZ0YbBK5UP#kV@aUOLaa!0cAe2)ubAN>2g z*Blk$iNK@zV#eiu(ecZ3?F~5a^{wwa`<|;t&QaA%FEf0T>ZitqV8q|k+^pi#i?Nfc z^nbn)3R>!HyF{>l*~I~M`T&u;h{A$~Nn7e5v;XX-PJwW9;tv)9}Nl+B@ zq4fQJ$1gEYb1o%xYaHGxBaJnln`xG4WF`{!vRL10)!IT&*IS))g0^z_3=q7N9Ue^D z2fai~dakv@vMlE-c!C22%q7P`?1QVM1nTrp?9!xkEm%D)hlcPP@!}oPu1op9$N6?= zq5Wt>2=+R)B>)uARckVemVHC7>HZ9&dB0sbO#DBUomEgAK+~?1;O=h0-7N%ncVAd+ zSs=JO1Y6wQ-7T=VJ1h{K01JcwA-Dz!LH~UJSLfo~oT_v8cGXN(*Hq0#zw=D@kIxY? zwqQQV$Mb@uluF3wev{KDx}F#JuF|qWE^Q%4Tzc<2F%I+ks!!1jxCbyREBW{L0L{8N z-)M&vMl~mmI0e#jf1VX7g}b)%=UDuDnu*Es|3Ev13Ab|TJ*L92PHd4r-iFfA+Eo<% zcc`2FLdUUD)U%_>zhqS&fR22>1hiOVazvP+3LZDk@X@WQf zZVK`Hbj5~wljuhDX7*XFQ3uImavsFP9ZniUf~IrQLX-q!W_XV4&`5l+wYja$6^P(O z1gbcND2lzE%QIs8c=wWRy9wacLYn~HQ!&B{{YPgH7XQG_UV=Wv$;ywDc;PV7t zx6t&zp8SyvwCQXU&~Za3)dwPbBUX>hUCTAOl_y% z&9w=PE;51L{tpY_BfDJ=uT>y}ubRxyfgO;gGj5g2jKXj$>kfCwbQ4Dk8g*%_%C%=K z01=ibZuq8r&I{(g}m~Hk{wG6&F~vcoH9!3VIbd*4cf4N z1PXfKBl7XHOnZGw{fL$kEh?UA$XtPe`uH@Rbxa&AjA;2y>)%d|fUU=|2S#fot5^

*1K$aGNdW_QjA#pfk&k;8!wO5{v z`D41CdxvdLioKxlLP$Qq?+lhQFjnqLN|jfgX%a3yZN9p_NdMCb3m_3tCN{f&IqsX^ zQ05CEwHu8ZIo0+Tpq>bLYwk@Mm2zClBtWfLuv9l1@N(5@l^IzsQufo`JH0Wnpthms<5b6sZxztBq9I*MaKRwL%VxXS%jwxgV4OVB|nQu?(AL+ zh^+^9{hFS;kx^%PG4LaNT^I~P(mdvls*jG7{A(Dc+~a|O<*a*fG@2#ER_PJQ-7c+- zAp%>#7aF*-V*C9UIGcKR;$ns)g@dB|Y6PY#r;!b9V1Z_3C3V?+R+mxz-YYayo;qP2 z$mlGPhj8z?Po^eY=JgR%a-(`R2Z2uFbJ5|OAh!?JgZ20ac|1$^92X>9wZ+dBUw_## z_RVuJ$?QLRZ!4I~s71)=6Uo=$H?PWIy{Ib?4)Hvhu(ia}nRB%Y%Vma{bCdX{G#}3T zPPq&io5`$orSktwQK3k6k%2Bm&RzU5NK50e(yKqeeUzQ3Rc6xhP*^7wQRPL3Qw1() z`QUMI7$-cGl{IR2=cHFAV6(xiL`xF3OdYQnlSzlB#y2TN)HMB(`n_AoC`=2sfytP{ zdXzT?4v1e^%37+oZE256IB$chKsmPlNC6NFUr$W8G(0#937+l z*5Z_?N|>sx;fU%ynJcvZF$-R6sH$y9?H?_}EJ@S;JQ-LhEUGp&xU_4y37suS{G(yi7!FrM47o-*ww#5< zF}7l7pNLRuv5xwk%Kj0H$DMY+fa+O;zQ;T#v;|-^j8@dYe`t-VPUlb?nFR0>zZS9x zBz>I<$Q3fEE))kVP0Pe4Pxj?JVo!rY7HsJaZBf&R4Y4>Y*`~tWCnZSuf0?g2G;jT| zw%l1UyJ6urT^VjvfL`21h^A{dw|1xf#(bb_zd_KL^BKG4c%DtiX8Jf(a5FftFsQoR zBCL>^kz9~b(R0|ZMBh!+;W86QU+=R#F{|cj-)J-peY7tB>L%f7qT95`Qbr2J}m6)ai^V+<2N9n!h2=j)Pi*Nq`|sRT$$DMlxxzRlR78TH>Ojj z&gv@vm3DQU@@)i@ zDur(Y)EaG8u=G3HZVUV2y_HQ5P%-A(S&T`iI~ypfDu$ry-D6BU+hb*(VYiXsqN>&NT~_r8n!4;zJUg&Df^R-Y*WX|0qH2e(#Y>NU$~dk7d7dc^@qbvOmqbuq%HN8|+WicoeKb3<{( zW4+3TVA?n9jd5gALQ|%OB48|aTXoj#$Lm)4vK4Aw?Oi`3>xXc9%=qM|szbfqe_qce zQH7ge3gnNu1L#GkX!bOO$46y~+~?^%sv>6A^IiLwcYhb@-RiwQZ? zB9VX5cVis!QO9ib=X)A1C)9&V5WiGl;=a)6-KI8p zT%i4JH6}G-9@4WV6xYHaK4IKh(;wEetsZ8N>1)!aK!0*qBd1Q)SdHtFO=K-eO^ixw zI*sJ4n@efxIaH+0dZ_{Dl(PEfD5&S~SL=JMQ7e+JbP}LJdzHo;hn?5nUpjnpwea}T zj4ar;(^juW|GJW1QISjuFX9u1K{aq8*Mn<=O26njFI~tCbz2Z-UL z(8o`{@nb0vH|EaB$>Ss06_A@XD9RgH1lY5vcMEcrF3gNr>Y&KGgYa?<-|r*)n%6?GlsHHDa*g1q8<`Pa+aM6S?*3t&S;^?SN@nP$ z?5O@7g`RCoKECW4`&@UTR)Qgq&o+kvU4}IGe!gcsqvHcF>JBc^o?ukKbZ)Qi38sn` zPgNn08#)T1f4ZGU0W_Q`jmBl;ClhjGw3i=f>SKOb6v9t7G41w}ZxIt!JBf&&+62kL znt4E;KRXlniLm>m6`LvaEYA3aH2JJpSG&UI4CXnqWr555-}Kd66*Gl^7J;rRxus6p z3f}S*bN!K!$b3XC{W2cvHD3qQD&)jX4J92@1S+IqgM#N%ff_u@f32&wA+WZ zPVJq0((h%ntIEqN64R!$s3jM4D)}Q}HCnw*XOu#(5!LhkGMO#ItBywK9iuC6Y=!!X zl!)ab=;rJ5y8uKYizZ+F^sBJUTfX?ZQ!3eAT$VS>klsI+f(~3t*|rm7as=+`D^PWq zYHTu=tP^#jsEsA_pH7?IYs+FWvWU%@Hpf`pH4A(`0X4*=E+$UtZW_&^Fu4T-%aFyR z0~Bv`D6pmaI~}ENyi}tg0SGNA>hc2i0c)~Y?y1T7jWgIAK{#qBzkZDH`{hUm<^&SO zhx&)c&vvvN5cil!lkx7zpG`>TL|%v7s9LPWn=nK)f)Dwm%SIT?A0+qQ3t+W4V@wCo zPAJpEP|w|_N9Zn87NN-(iY#?3VyRZ@$vLPw;R+f2_Ldmj{5cPCbPhzM15C-t9sQ&~ z-xm}l54qigrnhT3%40<^5y|CY$EEMAb1W416o`e4bQUI9!%&apX`bFV0pF+5aVYYm z%+|+%bAFr5lG7wO*;R2JJ4p#-?j@i4{a9Ck{boNsx{T=WQ4%0Q?3CKp}IBo{~V6Nu@Ba8?i_>^ zS}`)iFLm{2@j>{(`jq4Om&=}I5deWsOz1$`FPg>og6A;CE8R@x64gCJ4` zIOu}5MQHDuzp307!SN~5ozKmc1(rNPs9!NstwYvTaX0i`A}($_lw4A010xgJZyySd zt>4yX9c8p;)Ra3d-ySlI@zz8UR`mJLM5A(E;KtboyTa;3ww@v z+N>5T4h_Q!yb)u;MxsC*#+l{wmFkwa_93t6j@-}}4$%-@m2XpC79ewg{!Jl2{lL`d zZDG&NpTrUFitVaQlHyX==Dc!zzyiSH%4Jqw=TH40B9$oxHpy_?`lzJ)tiw9Ilivt~ z>Mx$#7<2nt;BG0vsA+w4yXq1vj7;+KX{Wo%^9#d)enn;|w$4o70!nGNEI#RcQ5#hM zjr1YHQ9-=6hA!H-2>9b^XDopc9 zyBEVT>Fmv#yfrWHJ1^jN5k0UXLub<*J>@?kzr3I=%48Ye8p&{E?> zLkp7BL5a5xz%w4+D}P@RoD9oa*5cL=8ANJ`40_*VpFxrwk5O#mXZ3d%J7}N6w4KIH-!c zOfJ7rLziRwXD*x_0IyHri!3|?)75x4T+jQjEBj~B^XtVOi*vbJ(2z3rq?bILDK zHy#Kd*G&crvz%<5QIn;hE`LjxH#XK)n<}gfdw?BzWCz1f(&dksVpw`kXeF1>o+v?6 z3LH~R(NMd~DF2?4<(m3@gLd8J?vIrAA~?E3UWitKOyD5(=sSV8ZL`pIB5(&xWlB!w zQCmwWf2<0D%(6Ac0w9FX&GiwIuD>TS`m<545Q3$&qhG}eHcbCUb=a`QWW(8uE^Qp) zLhN(sVkekF2;?V57rHh8g(GL|^v{=&Ps-5ZaAZDGD2R#Ynk5aXPpt>3`^GBNB-!fs zwr*eS-r| zWW5&e#C{eYIRm4Uyz(#?d#PTHui;MF^|cMjyO!Yo9)JT@==V`~;(bG@Z1YP}yI>;& zvK#fWQ}S9_EuU0KUs;=412qaZoK#xCDUJGLgM&hCq1su2=~(w)@2HOEhv3xlB7= zM5$6<2mrDw9#&MHmH_dtwKLakl-oucCT<`=TF|N!AS~T2vNH%Lw?O@U4aLzc9DuJzqw}iF2)Ijg@OkrnC0=~6D&+SriC3rC<<4Ih7G!~pLAFqS((Y5I zsmzsycY{Ao{t|*q_9pB>V{bg7I#sj%!k}NMatU-N{gv$_O#>==M=*fxNM3y9t4ulu zb=yD62t2m&6CHGj)us+`nK84|y|jKU8CjBz6#NLeyv1M*K&_HbNoU@3qnkhc=z}qJ zx2==)fZ8+o+-BIX0GA_aB&d*z4ZVd5P4F#8^%ufz=0CXF14>)XWcRIYV4!n6e=w7JXt%fu!rP zab(3!-43q4bEj$ttd$t;pl8$ya?$c!``@xZL1cn#Ha#hVG!SNLEYvA@`hMDrwIJ0~I59=2U; zW#+K_KU(bo%*c7BwH-%EIXE(0ZHQyN1(5!YNrOv(?`?tr^Zv0F(RW_AJoeNgmbe_Y zJ$p-HepYr8sk!pl9GUj%)F3)=QgxObQ6DFNe~7X%;~U!*zt*zJUvB~GU7Xjd(uVq- z$b{+|!<`{JyeZHH^vxZXVU3Y@Q&vpZHC0;W8-&x!=Dsj2{(hzyV_&L{r7OB8r1R5t zfC2r!p6R`fO$3zG=PRvcwQzZCS&m|hZOw_wXttJlbt%|J^to7pZ?`; zbrtqh1AfgIVG}_e7kW1>(V1ZBWVjRZU6aM4XIj51F#+4B)jyiZjf&c8ad z9e9Oa`-=|gok-{*5gz$Em8$^JBX(5Lp2H8n6mNNVaA$r1h4@4S)Y5Rgs(z41kX%Pk zi`WI3T<<|j_qQz7!ZG;vU9Rd#qZijPMHq#E#rVZ}ujHUk~-_ZR7ti(=01k5nuY2zLWE!p;(vY!Rg zt7Qr+C#a4PZIEHl(c#*7e=BuFOCUsU5W>8RB(p?y^ezfQmz{TLH&RzXk@d=FYcwUvvwkcnIgRfA=|)Ag1zY52 zOIc_*`wCTi2}2}1gIa0B6q#?cIJxkV+ZsQY+lk1rP-eRh(?pJ`I}Mh1cTdj@YImBW zh_vxYxg!hq)ZKfwKLA;yZ^U4EmK|j9!Yd;6$?zp5XON3Fae_{itfwvnl(CLZO+;>7 zJB7nd$novc%(9b!o^|7@#NE$v;M?qSIFKJ1^Z;I>@Kt%Z*NcN5>OuUlWaEWG?GmlB zK3P!^Vh80NzH2IQDzgk=AZbf`pWtOu){F4nB*kEvkw|%RY=r9pi@N5Wk5Ioa<@zk~ z&I37#K>MS`Vx%t0<=J~V1aY3E3Oaa97OvQotq_&Mpf$v*=yINi(U`fVi3EPNRun@ zVYaUk^AwjXBq`=VK_l0YOqckM%}K@x2`=d11~Ub)xM~Z~NGx(n7gc>?Biy{2Y7@K` zm-Ch*J7)T58OioGB8;6@cOP|%oR@dafpF~hXHab1cy3HxkHqMs5dpT!59f6;uvyFK8i&|9_=b>Ne?8wv^VM6u zKX3l3z!Z=56Fq@HW*wE&nACb|JR~Vmy2KzaWi!p-d=)YxYdj>bKdyrhB8x4&FNUJG z*CAGdY(G|$oIkYSeV%dtS^R)3=rL|PKOS^cV?|5L_o-GM)YH|&Vs5g+n1LZr`6j`) z^~=d4n7z_W=_ndwqYNpZQ@`fys3x;2eQ|YXMC%DbJTf^f)y2}*|K}d8T6W6&R?kGA z8IR^O59hziUcr}YX}@$;wh0ZAqd+(mBHF<2?Ni5DDY13v_)fBdCg(NV9qv(EjQ z&p7hB@I_YhK!Yi5PqvL)F-`d*n-w}>UYGH%l^C_gXEMSS9d_QJ-7vtGLsOcsd1p_8 z_Ly-V?%_}6{l$yeiPsOphUPLE0HAQ{irHn~$`j>8Xv~+J+M1~_pGGTRWe{Atxlnpp zto8<+MN1Ny*XYgG37h=!`uJ-J&~TBJU-zNSBuC6szg}AH2K&*uCRdG1<*zlLG_6lV zzFH&O{w(0=SyYUx6}ve_fmyndg)?OnQ%fhJH-bP3b_Xc3c1N3v#5t`1a9dUqPQ5Ix z%j=^vjOCPL1NF#OyS`Y&b_gE7047(ev?YHU{GOzQ*Fop`k%IDaYiFymd#axA zJ_Qo9u}elW=&7tt*4|b&UZgYUXX)P>jN(%HFW1!#X373R`W6s~X1vh4!G2L+wBPj^E{Xy%Ux2``Aa@6MTy;Zc6b5l-!>{Anop-$-_C1>KhK5y7=WKE ztu$pfZVf0H%RlZ-cZ*7y_a(nIjB9>9PxvBqGE|%r#)+VK)HY*qbMgY(-o=RlSy3!J zI#Q%U-SC>8@$D!S5UFK~jN}lmr*-g7O}7sRC{+fGlg5BGR?|UFGhKS>6Y=3F&pFI9 z{)lm0eSN|QD*YSzxgNN&%v}mg&jLVzt@VR3`k@b^M#1UK!Q~~4dQPZ@a zR(Nr0VzJQz1_U%S9F-5`E{c((&3y3&wt)F;OPDBz942 zopU>55=7C>9I}|=%ZC2T=D(8kzv|vT9caU2gAfLFp8G;W8V?VVH?E2r%EsaWll@x} z&N^-J|E)H%V?_Q9nV9|0jDY`p_bWI(=#gDFlUMfMA`djhXM={(F2ekuIJ>3qUw$@m zP)%_)<8jQLnlqHSW1SF`FkT8*f9j3?x8O1{!g#VP65syjzTa|L2FBbLU(vzl@IsN- zu07y(v&C0ba1lH>m{yRzt5vsf#7N_faiQG%Lq;1u$2u8j+~aE7qPQzI0e92Ki;{jk zr2HWeTcOaZ_0%H8@SPk8A_75@nCfW`_-h#3BdqlFn#9iz`BcEdw^A(ea<^-8&2OXQ zcj~JlHU4-j2HXX5C7;g7JH3E7z8uh5X!1{=nEY;W$mIP+iebF;Y1_tgE)f(AOkG%+}yQ7d6DRdmA}uu5P-($AQ#WIQM!0! zpF1ONIRgJYd632xi9H^X%thgR1&q@u8O>je5o|o!@~kKf9t;2c*M!E-*{9$4W`Lo- zhHB06^WPVU#aR>thr^j{K+*(hhIl{Nm(S%fuwXl~7*ZW{JCo!gmK*;C(nVw5?=MmE zF{c2j|c4{@u0cMZNk2*KUmb#chz?oM!*h2Rz-Sa25ipjq7AEfCzDaQr8AS9f)H zQ!`UD{d(T3X{*=$zK&5>mBU0MLxX{V!Bmi!)`Wq9gTTPRDx$!@rz|8Y8@|6xs4HvB zDBHST-90?Lyx!bBKEM9EeRz6)dENSResObiefRkC^6&Ba<>~qLJ<<`O3^XuEz?pbHg;M@E0@89m<%YXmg-qmd#oIkz%J2<(%xW0dV{dabCfB*2b zv48fi<=@-e>%X^~yQi0z*Zrd_OhRT<+z&@*x45JLBFc{^7k3C4G>50xw|Dnf*Eh7x zg5>l()3ZyA>|*a0<`-AbFD|=#fAb0}n_0P;xkfLqZgTO<@9iG|xTTJcPp#~|9b5v! zqLRgBb%Z1|);D*gm4F9_N4+!AnV zo`Rb3@8JmpGe;NCkf6|b8=piKE%VXwX$?Kgw9GTFhsWmTaLv2coTsnKxA&!`ipJhY2aj{3mv&1|*Y6cf;-RUk z1!LyQIX%4n50S7i1k6`0rkox*TqUjasEUj4^qvRRMIPu51A~tDp9^+oJ?I4nh6YAK zT0+}r@vCqJ&)#+k9xxzZl?Xe`W$>q67zZM z$7Wi>r7I%G5xU%tI{$ic$#nj*-Iesw4ZEfw>g&tiJ@pJq@w}+-7#{9=G?m!29hc=? zRL|R95;i9)8{@0@_ii!Rx=&-_K@N6)eoudAHQseie^@x2MP<+Sd7Irdo|qF2IDnUp z`r?6L zsDFZsp{9bft9ZjGttLDlAo}QxJ^96A^BG2>*ulXAC77~MejT@qR)c=FfA{TjW7+GD zQY4$iyfi8*@;C4V9b7^0$2}0*Mv;|2((Rs}{EASIPygkcRQ6w7O?zo)qo<#fZ?D?i z&(mem=CY-~22^n}f)9D4$;sbZv@%~0@5KSv+CO|pQrhX-8OX98#HjORPf)l8i%FTx z;fhx2?=EA;LiIj=;`&KSMc*Xtks~c8+ROKVfFHN6uPG`ftF#b?-f8es?N`=`~&Z zxZgc<{qs-?oLL0K;&?tqEt zDn&M#8Jy^;W2_0P>}7A4O(pK2iF$5O-3LcpBwQh-n-$}*36dpb11n|jvHVHi*@WyI z;?qGm^Yp`}hJ3~!7q6wPG|<%yZmmx(MU_n_o)+$Whk<^ zzrM}qhv}OpFAgWJIjGb-<9N zI@GC%V=lJ8q0yqghNgaOPF-V*GjzN(!#vo2B8#~(G8TN^jJj%()-^ixZ>Ug7b*})M z6j8Zo+HDvMb`*v`*?ypU&L_1SD|27<)M6d!TTW?YCAtmB5&zg7MU#;9DvoDUA%QOeLTfVjSUrOD z8PW(BMBY4}Z}w}NpB(sa1gh!6+VgeS<2+O{nc?7RV**pZFFB1gIBn%Qk;cCZ8WjH< zw-LWTJNX)y+>O@3-g6odD??1l^przjr`QS2nU^PtOOai-pqvJLYV(XMnXo~KFLIUZ zx4~fywjhQ8Qs?jYB-Il8!kk7Hv?sR=t!>|QDV&gq;G;_f4PC=zPrR(W0_9XB&Nurv zXP)mLD$XbyPbbGl;xP@ENkakc3jsCQ)S^CWLU0nmCkL@+l`0E+6#_eGT=h2$+EUd$ z#QaHD`Cg_oq6l~gOJiCMW^bdvXU8f{Aw{s7h|5)OF6`^8Ifa6&vt2^>lF}?lpGU6I zM?xo#f~;6p>X0${q9n?i95WD~@lW-U5xsE|-ZUP94*#l8`!J!^>U8@u@k_dwH#G3{ z;eebi5Kg*~zekGTwUChC2b`DQ5T zEAXUs%UPXWRt@a_mS6ZS#fLlHDqJvC)8u8wQ%=SYs@CsG*2tQ>7!J->4B&tZ&DBC| zIRL=eSyk1EW75`no^f2msy~}QFZ|4^*-w?PHiO*uL`cq=+hDcFN(1*nJMvxb80Dj)_?(F&1-6F*{5 zU8#@yL1sA;gI!@NriccG5S>c9Uj|GwPL>)S!Uq5xA(-feq4zNSjn{B>)%bnf`E!KM zgkz6=$+-IR$OMN?mKVshI2QMjmr34p4rg2Y{m)1OY$f{2uD$Pxw?&OyCxr5@m4J-` z4c(u(8;X!)Gjgx%Tn3R%?fk85or8jchT0BRbkfhbDymDSSG+rXrsLgb8^igsHp3&H zc*@*uhyNOVspot)&ZY6kPwf2-Hv7mGAd5#FH3ONztcQ~lrzLV=$1xPC=bNC-$;ilN zX4*l4PjFy~8bBZI4Fhq&P)Rap#udP45offJ1px_&e)*JqFo|s@>Z5aZumH)JRInq^ zWJ%;o+2|0-h-$M+;Dknv6Tx}u8<%NRF<)G{En`!`tf{%u^X`X4WPiBYu3{kkcoo2$ z8T)H-D*=7Dlv%A8SB8xBvb5+uC2V zSs6<*D^g2tbV_{k*aLY2+) zy@S#1Vy`5pV}Egh-TlxQ);n?P;2=i^N(2r~{ZR#cOwn8E4#T0Ta2q)$4{^!+KS*0^ z6>%zHD#=k%``{8i<~%G=VdB~QS@65=vx2ZM;E2s;+fsYK;vcK#;L&K+t$J~^)v>__ zSm^$3%4$m7ZQyN)>%!=7ci$*`K@`XAxaN2v_wF{Ng>K}E23%}^a~=O2*q%I|=?QCd zuZXiADq#SRLyp8-1-n5)ZDvQyoO2L?NQ8ZUM1e}N2Up29Mw=*;URQ277I|f*NJoAvn`Ea!KF^=-L8Rm{PK?`pKf&?5^wfIZg?W4Yt;VSpBA~=r5UkfT4 z860xw0mAmsBwMk@C6^A5Y5Hv5$%@A(?2ke%ZS9SJ8pRw9!Z4hl_-6_I1G}CTu zVC$s7{DlR`+huOTI@Wne4+X+U#_jX*^5L3$sJr00gj*L^3wMlko(-iXrU_dy8*!Bs zCW;3(V+wQ-K@f*TgDzlxYq5lp4^StG`f|kD-G9cPND^GVzhz_$b&FPF!9s1mzo6BH zxfe%^5;sY1>8RKmov=881wNtxKjzWq(Lgocg6@`DPNmX~-On1MCjMjy8$&~_Smgh0ifhR^U$=fV_p+Wnwvk^h~8y+y>y@no2HtYV{bNt)7TpDM=2#N&nGa zHb<<=1))P-Q%d-C z1)KZL+97joU!XhcKT*A(h&(lYzKf6pk3ykm6m+9!V^z-94GSuvCkaQr(A~1{3Eu?? z)Cz%lxw6W*2L9;01&Sx)a}kvz`tz6L2WEB6^i+4kk$`Y>cyUjW95X9l2g_}nNByL< z>Ki(HWoo%*80a5&PnkA&`|S^)5bDtiF+kYQ_4DV#KKNL*T-8yqytG_Ua{|k zCODUtoBR+8gZWFA@RSr9^7%|y{%?Ya`4R(?E%MrZCHxkwXt0q^-aTggS~BTK84_F8 zr2HO~&k*gl-BAM-(ztvfY6#JVX)OI;ycQN<_$)b$iSEKdY|G zB0L#Gki9kTHr|X}d+OTmlOw?i08Vu@H!I95*x)iYjLyBXG{CPg{z%Yl!uuVCWSa6w>lc zX`&G`c}H(g5$v!8pQPFcu3E$`@6|A4?i~xOt)Lkt*K;+fRZaZ?y9y2cMgICT6@CY6 z|I0e3JT3yr^~SUsjs!a=D+{Pf;5xDwx<`uNJ@m8~4K-6{L+;;jYtS>hezAifr>C8>d{HL8K2mb7TnwDr64pF68u!c)y%ZEcR0 z%HYpKZOxa-4Xa#zOF801^0^V2sV#Um0!6=waQt`36I*lFshTLgVs-ny?pcwCwashW z;Ci`Ub?5A{LX3IXm%26-xp(V2%%=zwmf+N}IJFB=4@SKFVdx7uJ5EbT>|`%-3+X-0 zyou}iD!yZO#EppCu3-DOQL4a4RQURrhp1T59wqW#oA6hHKPlD)s}(P5r(BBF;h?1(v6yjHjqgbzP#?cZCxN9&r;IT-ZHUic~*?k(Pz=} z_i@qE7}5Vx^B+C$CjL(~|5y86x6l-p)98(x#?eKt+9F;58`Q7G0@+5eMdT9cLs|p& zZP!vt)9_a9^HdgU-(qCWlY%4Y$y~N^d2Dii+|JD>5Bk(2!F&IE-2N;3(PmdQ$CKhq zTvaJaThYDr3O>5EV*o0MWx!ICp{GZ?C6^dj0Yz_c*wo0BZCl&Bz$}PT>t&5)Z;d0J zx%OY(k8yT#Vw#&wvRKW@AwA~mGZG@*=S)j*UICo^RL@NSkn|TA&#@nQXtvgfQJZUP zOd&oeHwhxp{;~j(TS@49(dJa)A3zxbI|f%44kS7ll~vUW(Gv!qygME^J7N|&tAT3$ zCltErjtZfNSP`9_GzZ#Wo9)y6_rnYX4PvVuz%uJpY!Mh($(;4kvWP;Q;afGa{A1Jn z-A&~+|0reF)>?*>WLB@Q?N={F4*ZB*nQI7ibCo!LZ5;~QFXm2oq7gX?9(XtNW#syo zEiZE?mn4rv<4WQuGcJ`n+omL{R_k?uKa$Xlb<>X|(}@)U5p+Bz7p0eYYHNd@)JI?+ zx9UwI3(SrQl|u*eJF5j8$eEybfthhH;wi3q>7%cBx@2&nxh!UX}J z)D=BW0+D&)AXZ<%l2f+!06d=-?0mtD>-tDwts_BHPo);!eg z$RO)VbXx!N{i6nmhvJ`-0@zKsW${K5Ic6X#qVa9)x`{HK8Y}kKSpZj~Gb-dVi;%sf zB-zteG_)E+vr9$6MD_e1jB5 z6|Iuxo2SYYy$x4|bsTQlP+%xt>4ckp=SoQC>!F^oJj>IzJGH!PYYKSA{RP zNC}}q42m~Bt#G<_jV`CEY;CD5a-JM>U`;J8Req1uuoSypPe2L55jwUlFjKmkIwE~i z_~^2}un&$Sow>cxD+_`AnEkO&B-#Yt*A`^-qvYQ(jw@mmjVi+criMf^=BhxK=#zBl zwI$oJA=sy$@C!ym3JxzWXcKMP80thnQpelV-O=%x`oQnxVnL44hwGKY<#0ToklyigcW4 z=#f-x9Smq^a0EG4z4|bl%YO*hTtiO`qX8DYc@rfJ2Q!5-a6O*3sFC8ZK!X?(2T6j@ zkh9K|F9p)I7|muTcnG4o19^VMwY&#M?4+w&@h1SKZCCT5xwTs4@y?HBUOoD)rfx+X z&ZiJaY&>QFn44yL9?`*5{c~LMZ8i$zuv!stcxg$y6XA$Y_*;U5Ft*iw@Yf-2U0qF7FjJV^@AQRKXx~0P z5VSQW2{7k12_=8fu272o49T>I*{zq zuG0fXNli}oqCdEr0j(w^hQ^;0906Mkpb~+anVMa95Hh6k)E657gZYY}4e++eAD@;L zl=LAcw7+3gw|=j4z^ibnG?XjR>XGy-)U(8#%jjZ+s&&Aw*FFs=hNKY9e!RAT=>xrY zN@^g|73xhA)uWfS7Y;)0pBD4tjk;X~(=^X4k~z|vzID!Ak6(YQmsOU~j2XV6rx5kQ zo!@5A4ox1PFcwE;R*L9zNCkc1#xOFS@`eM1J!NE_Z z+zfz7L8w7oy9|%%0v@3Uj$hLfu_Z~}dX(dZjr&Zijc!$y_jK+DXg9lg%@~Jj@_Lv$ z>;UFYYh9xS+}l(GRv|~bBT>tNw~m-akgYdUUty(anz*GNrFqM$i2^WH8y>~F?KH&h zBLRndqZoygI)A*m0ZPYd;)Bs=3i$%vrS2r|E?&vIU)$<%Ct!=T3tet%w1cH8F{1_v z1i0qj7?g-?%ByZ5$ZaX3^1T!or_x*;t?5orgbi5qxj2n@Ke>^h&sxvRRczG~8X+Hz zOa{@efO)cvVkX>?BBN0QECcCak>;}H8;$V-7xq5UEM(X9ElJT8^3N*MnIG!=qH$_B zd@!fipXe>EYRBxZl`Vk&WUB_W&Fyj3zgK~JmopPdZd72%iNn=eZ`YMI%ZtOUxx{MQ zi%8OU50O1d<58(?oE^=7o-g=hVgqsCH&26Ja>yD}OaGoRef@qT{8U+UvXL&yB&@C~ zEp6iRU%jpWpDVVG3u-E^nNs<~l>FWQRX4pzApMZQbq!xLB(I_sQ8(Rcs8o(ADn@l- z?{E&m-w$ykBReyfDyLxcBK|YJRzDeL;T48$}lMiaJ1ZRF+zSWUQ98cqf2tB^nU^R+#8@Wjw<(vJ~u9- zoBe3Ty>kg&xS+pf*=3-L@oS3}B&1YSZFi}a4P_PN3*Z*a zLiDZ^=k_wmZOHm63N0V%-5Dm0&`YL^4o*4M#Mf+T?gfJIJuSALepH+;&IVn!s z+ajpufq^u*OPB5WzETriZnmZ6@NAF&$x-qS_vxAQ>%aZ41h2;#Jumy5 zwUZN?>t9)p9#%8ec!LSZ>OAk?taY6FeoS0{`$%Y{Cv~?<{k`YuvFyC#b~b4;KhWJi zH|O}W#+eXe`gY6trWuMz@pLZs9SXp=Rr}+CRZle9HUuEa+*;l{K1pR?`V@1<6I0{Y zGF16=_I;>zhxgm9jeXAWly6%1pl)=Xbys}*UL=c;S?PQcuqKcH_mKd&WD46f;1W~L zTM|+2kV%dCY2SJBYbTG@f?X^z9fsd1_%({`K=SSS!1Q0~M-%+{AUAddYuvNX6Rr@oCqnJ}$k_dMJ2=|?P2(`Ls0?)6C;tR1VN&VEOK1 zKtCI?Av$e6i9nm=MA#0y^xe7X8)emr>A$N{(|YHN@<~3J`1m2#B0np)DJpNe`84IG zigN?6wJ$o17HHA$QT~{5#gDdBisYvXBD+coj{ry?8F3YAtodMFn4vlzL!14lId(1s zOA@s>PBI*0;v&Sz;q>rqLQ=(0u|5(9bK53=MtsH9YY{WFVN1#}-w{C%!)RGOxjUPj zT9cQ%D+CbB?V{Ft#F=1d%-c(x!0qm~Waigdwtiuk*Bt8WH)P-QhcoO^cTrjAikP1p z{>{%hH@C3N(zfR?@2&2XxUTyB&Z)SzMSLa=at$d#-ngPz_3z(F0xXsmcXewue6Gk| zpPCCx9#bNh-`okK0NiNr{n^fzH?dyQ$ ziqa2bO}LuJBx+SiVl$r(l^~}p13uFy3Swra%m$ZMWOf+$54ZKnfd(1pl(Ml6T0!HNdPa)X00$LO2HYI6Kmt*6D1eNCK zf-k+HS6VE3>*vg?7*WW|E3cx$akU$q6NUOFkqRERv4&e=J}PievId!6H{xxf>FTXn z=aLJSd{!3=%E_B@^_E;Q=-tdq+of9{x89?Mf0UmEkM-Q^m6x|C+q3}=7rvWO$$5qs zSECIL3-66V{C=^=?h=K0zx(|&1a%CK!wgy5<300@QW~@oF zJ&V1)ax!Sb?@}ScCo^BdHLySynS2`-Lv1%F^HBx6D5T4RIzbAOn8iFN( z4oXdyg%MRju(>c;{E-6k_YogvjBspeuRG6bKR9g-tF&epCjq~qE2diRV~V6%DMo6-RgvriF30s z3v|u{x1G>#ktTpqadTP(o9#VE__(YBYp9eJgxvavNqHVw{><#+Cq=7<4gwy==d$9S zJlrtV*fADp=?AXQdhDr|KQP}s9EO)*^cCiA$ifh{8t9N{Y|>n%UW<}bDKxMg6h(^{@ zk5w8<-c;y^OjYnO3z9UlKHU1)acjdT3PI6^bURm|=ymvtqi%KJWfb%o(UA{Qk^)+- zk!QqD+k@BErovy5f&hMx^P|1c*cAFag(n$j*l7k0j1pdDii|KJ!EehgN-RTWin0+A zp%LRmgBB~=Hp5=zBUVQc*GimG-D385^EhYMk7^w!YPz#Bq8NYXG?SHFwYWKvNSi{z zAC5l7v5Q34w(UcH@Wm68#SFyQvXaY8gtA?I#%XwWOR z4i12Sd?eQDyHNgZ9wZFS&zcm$foY^1Q& z-|-b+k`N}~1)*B?S9^ii!FEXHk8(@$&m|O2BgTZz6f*7A4w_YFAoDvnT9`kAHe)R# zLEKBI!K5`rt+ory`qNmDcDyi^9!d(cV~FZy*0)a&6rURa#kKtzff(>+rY|@BV&7pK z#9poklQuD=b<4Mim~{z=^tE^TT}zzzd08BWM|VtKFj=$m%adLIsgODNJRAY%A47CNF#ewN=ui z^WMds+umHYSWgB8+`fI5&e~xF_sD0Roguxcmq{caNLDzL@ zg*VE>I5V~YapNR4WVz-sCIlDG_b#SgUJKM)j7N^U8o9@tuBhAb_Re_L4;IM@{45FR zE!NoaB%C!5{^@j0?bWsKwPFBa`{3pW_kj1Tqf5kh6z zKh~iQ){b^DLoQ;KC|x4@IKx&7>H>DBABe;iF3jbsHc16;c-tMpfWJ@4O$YRAM?A(hA8oxtO|kO^Q>o{oEe2 z$Es|Ypju8P+qTl>ynzlYdlO|NqGw1A%mz=?zd4Eb$Aw^ZB#f-mX)FyR`w?_&v_+{Q z$+9dA7N{hj4AwK5AhP>9l+{W{WO0=GyU5$+zMj~Q%qtX$$XX}#La(2Z_zsadCleQo z=fTpHd^uF07)xh5s9Uv#5`N7_;HI0baj8y^9QbYBQIY)7Yg`dsz(^znz;C!UNVGOJ zC#4wgR_q{-L9#+j|3x=hrgH1maOH&+iWE_R2cx=%%Jy8&w)yPAwknYh3(qW+S&Gq( z;|~YX7NSsHWbpUZ)u;hq1P4%v&VN7ogA_$BAGchMRB|_1WRd-??ho!9mC$IE`}6H< zfd=4o)xu{HAy!EvN%OCSIjl~C)1Yb{EG^>g7{O74d#S_Hv8^es>4R1BE2*H#=IrTd zPk4Cdck11@o`;Ha#Rp;?w$&(yFE9{uLSPL#kOnh%z5=&}KMV!m zc1SXo$oxY%KHnrQR@8-ApoT39hX#!lTVMI}1Bq--pAOj6rgqulQus)eq~Ftj-H99P zc1*J($DYgIRYa@G6k7PfH0*)Htd_qyHTx+uJ#IukulY9@lv|-NMV$_9G5VyI(jx70 zWwS1DaJ{}()dQoGkq~%70+UK$A{fMpyN6o-UTqI5_@FgmwDm zuob)#PmAs~-}VDTs=UAjy7wFYq-yj};l=rQyDOsBT=Exj*DaC)D$udy7M1|gTi*OOpN7tMQg)h<@qKu3@* zMP&2NIXrHfU=%%4aFr);px>9GqMcTM{n9cGwyJzF(xnHr-86Hmz11N)yx0DBA$zuL z)MVaYr6EWc$~xj<#~*&-KUSB1LiuFpZ_C;)fdA3<$mpO`1(jG zafP)Y+Qsec{#pMb&6hf&Wgsk^5W&)!I!3Oo?QQa>Y^(%80+mKp;StX3!>T^J3jxJm zpLuR5@kibVPySA<9c>j#k~f=oroGj+d0G|pb%t3?eh{GbR__KI%6kXaYi(#@NOz4ZEG5wWVOw~%`^ zRTf~IK|Is^3Z8>BCzrGe{1Cb=15L&I>x0X8Y*uC%e$X%6apA*G=dUgq7#pwKNvnf^ z;kR{ffY}DdN{r2^a{C+fuUTMi#nmGxU+SRw5Kz9#itcqMeA2nTo;9V(aL6NH>bjvk zsbX+KOA*}OlsFUlopR|(r-v~#DCrvGneFv?cQ}f{gw<&&8|N*`3MfPLe71G=!I7m_ zI12eiGa#s`;2yE~C7qoTEa@YSlbxzL+qW$tZlXAi2I)MpS>m-5rjn}H9$1OQtPrph zkmSu-?EMHovBi8vXj$C`OW*NN9glw0e^kM6g1wLDL?V3^oUrfFe$oXmh5B}V&k^|A zBKCF<19H8h9r?S03tw2}BjWzht=EqBHx@9Q(s7~zw?_g5RYzIsXFB)In|82W2PPS_TxyKOrY)Q_Gi zuK#ESjmB9bE{h{BD9HpU{FNo4L;K^Vq7I%nCIsf9T%VL>1U?=8HvM|6cFBP{3Rc6~ z9a6B_Cnj2j9^Felj7A+Ry2&!KUX20?fERtW+#IWQ-=xmTWrSDy3fWQC18n5IKUDr(JAAV z(Rp<$oBq7|{q3oRL!$4bC(*QX;D~h!l-(aQC!dp5>1HPzSuMgSF2ftTZ$yEeL1+3S zS)G-GTMx^r&7}|TmU%UkeX=xA4tXu}*oN@H&C2{udq&VKA-mRfDi2+}t$D?|zh~3~ z1~G?hvWb@$t=_AcF7ca)%RQSRCo-Rf-B~fJDBo>_08FOJgr%AaqS$2EXSJZ9yTPQl zmwLmufXkP8?AKKZW?m#Q*9o}avqVQ(SVpCka6D^nIC5w>O7kd9BN)wz1-VOX`^~qu)(Mo@=RIpH2q()Dr3A*ozHu99zY=v4SQmWO-YJ6kbU18O@>Ye=*8a)UwGK{> zg?a8ikmFUI|Gr=C?PS_{Gr;$yl>|ufsqpzo<)`iu^L_Two|!kR22TmtK3Pw)l7DS* zmqu^VBq?z{%3~At@CTYk^66S{9_FQK4QV{{tLTO+hT2ne*SF(o&(2*NGmFlYEoI@5sDZgd%D$x^Pjq*LuM$yqdE ztV03>#*9*JCT69v{v67c@>)%N4PL!#&I}5|_47A*fP?iV-*LLjGdj=@9kDo@$)MOl z3Ig8A%_S2^iEp&u6uR_v`fq)~;!ov2=5623+Wp|UYU*;M8>isp!vS1eeEQRWRPh&$ zFTXJ4IqCG6WV3bT8WQ8d&E@0GZUGp7lK50Ag8;mOOxXKJA^CBi|D}O$#KZ-5K{Xfe zglZl>P0f0pA62d+f!h3EHg1&co*KG|CmZhX**E%~;g&VV5ehFP*Yh=0?(NfBho22Y zF!I?Xp4*cn1cFOZB8hFI(*)T4SY2Mab%&Nz#@u}Je;zRoBQz6FKTO$R_kEGNZ-Og~ zp!$m6z}EL!cQ?F0?sH_|b-?;6TJcaMQx@N+)2mFYOrM5>7JwShkHCcO2a!D7)T}w> zuB@!SOBtGm`MgDlGUWlj-WIegxA%_G%2*T|^04;JZ)Y+ec`2D?Ja~fW0qwp9?r+RA zxn1657$9O&RD`QfL%J;5ifM?6r+=5?r??Yn=h}SykmR)#j3^`|Qbn2{@FXzJl1)WP z1!u3Arye48Uk+Es6p~TKx2KYi4ID_=cIJ}yh`mq|;gMuj{~0@7_Jxne* z0t|c}e+2yVnLf{UNNdJe-%OT#@-yr8Y8=q}h#P!9@ww-f+6e|o282sY`RjI20B)i} zDNa-CHlk7IHNHV7v8?vZ(~cS{36%2n49F^Q;Vb4joe(-WILPqA z$bf_#ND__dr)GrPRCQ6x%*towCfH>#H!Uqw5KYPDqtSE#VkI2#_~JVc)su=QY`t20 zu39?eDIHzH9jwiffdQI0ko42X4_EIvt>iPcV<&$v z)7;qwS~zE&zT+?BO-3;oE1XKz*JmdEvBsR^J)T z1?4rl5^5`|;hjsEWMm_C;^7@r7Cp#TOTdBG-~PE?jur9|Xh&~{J?`3_A~Z)MyoL%2 zM1!>6dY?KLL7&j#GTImJF3)J>08sqW#aG zisM6Ru0qW=%!Hstb|yF<=sD$K-+?$N<7wy?luiwC)u>2l>RvL^_#j`$(GC`Kb}S{- z!OChq7BKOcR+84`dC+-GkhDz!)r@9C>!T3diXgkoNR}b;-AGo7knY8VtwfZVX$!lB z`K4_IZYsrRt^8Y%MSS0poadPBD+*SRE^$vW!B2a|kfGmy+rRCYfFf-7B6 zY`YZVg`iUBL@!N6Sd~8drSNxMXLcohaOhUuK)>*Je~=5x5F=6wF%gE%d^sCwqd0pAgibPw)%gwDQO)=0~LXsuIQvLVOEUh_`|V#n|D~3a>}tQ?loT;jcHT<}Bks zpbwKFfCSCZy0Iw?@pl-KjGnComcTUMG`Amm6srs4PZ;lrL26Wkg3fl_S(AP!Nrb^Y z*>V$`whoS{Lw6;xi;?6)qDSllz_4UL+8{Lf)F-xPjz)Oj$#~-N)3ZWq>Qrm`adK-7 zK29%8uBTl#{?I5&6=LT#ukk*nZs>Pt?y0Dns4!hKQaG zs`Fr9eG%u0xC{gTPf`kRjY@yaAiK>>fc2eU<^cI>;Q7{-*3nX?BJ{w1waZ5AKxF80 z2`=5kB-|w_U}BZx|TGl*R>}8%2ijny&!8 z1s89`t*Ie)+ZFE{k#to%0LhKShI*a-YxGf!~96cKP1mn}GaYNA9DY0*E z7^1KecM~OpQ!(Sd)-P6IM8-lOUnodpE{){K1B8!dQrB`vK4=2+btX#+ykxh*cuJms zMs#jqEWTmSgO{{Lr$;I2!bz2P6xTb3j?6GmU&xK)SeIwH42UCGOIg&GtV zMsVQ1LqcwAT&+m&Ly`NnT?%bn*&2le~%`g4)st^2CB+HSw zLQQkIAD+f>R#w1kK^hz4iY_ok=!(~0qXEbhoEa)e+sCQ;`u*gFE^KoUv-|fJC6}Wl zl>3Pz=^eDMs!VWrAxj=yXEnLXFr*O+N5h&BC)&}IA?X)ycn6nX(2=))@t{cPSJ@J* zk)GU2f_t$+L*t!e2gY?K2wSxLIZL$tnPX&}m8VedEl4xdm%erTT z<&GZ@W9?*hi)UjeGs%x72t(fI9rjX0%H|rut_WV?mOzouPoqz>$%qAgU|I0r;~Wd_FNTq^kQb5e*H@sUJbWI-o0mpv0;=Rr`Ofc&eh=Yn za}UuJcu1Z;b#wml+E&G9cTBoY3WE7q#o z(N2Dq>R&9`o48L0-y-k$FXHRC>L=0CB65n@6t`J8LwAj`NX>R^> z9?oUZh)Qo_@2{jZamxX+u6d7gT6qfAB8HZKEJ&g=kyzA3av%iJV)90r{qmYlN?yky z?xGqfjqh}FohC^mTA#;)5K18btulyCia?;m7(H?~=}e{}r1ijvyC0Mhnz&%~A!eXx zmMqO?25=npIl&s5LLuxsu&dd{voV4aL-5VhGNMuUb;sesqOwR1=bGug6N1H~6Xz=* zGoJDo9y*Ck4Y4{K} zZ}*}ZvL7MWWm<6&7&uwIJQ^5{$IHaHiS|@b)o4`iwU7)9DNU=FwUU2S4r(b3? za!6AWy|3wpKB_lb8dUF(qAszB*+&d6Ke;hecz!lB8k>q;$mTcm0#6DlFSrF|38%SO zXO>`6#f^R`AQ0yNKDz&fg$<)D@k13_^hslg>>;b`as$y#t<@K*KaF850-dSz_+M0o zNdUY)cq8`i0_FY?IFtjudwa^)6)}7F5&mu zt0o@JGr|Atf2L>#Ox(Il8ZVIk^Rl2%9xOsnJ@fS$oM@m0laXmLaU^vZ2!6c zUXpBlw!W8T_vGPlDuPvl`?K&Mj?E?tWQ}0%-wjEB_pQZHDeTHmiO6))c^la>M%fAf z^7pO79zHfcp~9aok-+=;eL9_-?gScRb9Gj!BFzC7`rHCu66ZI=2sHw5%l2}*LN%mR z9df7SJntaL(OcI^xz!b(H#=W7X+BMAmlKHTb9TMI4%4p_mrc&nvM5H*`3Z4!o?*N( z6Hdt5`!-mSV=3XM&P|@o*2a2@_t}6gs&y?Pu+@P6zFIf9q5kY3_2=KhNlyROMBB)i ztiWohrk2$A%^tg~8Rc0K6w?j*eMX*vocHt@weu{d)kN%gFFEAQ?NOm(m&qjj2@@uW z*@v@jiZ%Lvk&UT=sad8B(S@a{&)X-SbfZ|Ca(eOPXKfn4Mg9FAy$_TFcoq5m$DyK( z&>&tFa3DIk#7_py0L5cd0yDJpg0^)UOoQfsaGHwJLh(S(T<>tl-4QUU+vjq2R7rGg zYFB#=3pB!kYQ)3$a@ZXGoP-otzrD3lpk& zxJum9{q#47D)4QUc(SLAL&$e|x*i|cA2D2-NPID%Id|L$Vd@&opz$XM2ZnY(?!-|) z-4_N0{oB#3fz?TIi|TM?_^Dc0HSXRmo8h@U6R|{3n2K9cAQI3fGOHp+SpPWjcFJLH znN>BocQrlmUjm{HUGmD_Mr3L9{jbO9q|i6dzWC}=-fTcfQJoz*zmP)A0$*m%bEs_D zEi6jWEoFAmc44)>r?2U+RS%6HGC1JCl=hmDJ$mbZc5lf!<6Pg=AFCIF>}^2yr(a)u zd$bSrvUk*lAbtfsB?keILciYh5dFg4IRwX&;O8@{2t4TybRFGA+qA3=Th3T0hdUboe z3w^Y)vV=Z_p&`Naq7=6(!$i1Ug$UxdB+*+jd#hdFM>(dp=a9|y)5?Q}-^>VP17-N;T{-OB#(#fvWoB74#yyOI_d_^cqSRSUpxuICtZ*E6c!JL0y! z1Qr=U_AN^r-Gv5XH;QvvSIC?h_*0o($k6u^7)pfu+ZSz-@+4>lQ3hc7L|n-@`RKE z1qGPl*$T(EgpZJg-5Y@<%=uet_;QtKaYLQG&lQBjCq3nqwXX)wiEMmkMMbtfA0Um^ zzS|+l-lQG@vf4``3yl5TNb={w$H2^=D%g;%#ztG;YpPd z19OKO&2lI#reGf1l&lo&BJO?(K^9NK?5G&MFx@Ro-iX*avtTqELp6SZZO=1mF)Q

o zFcIyB>~8q1X}h>ep{6XSBECXRkO0}H@vwlHvN#LKmPDag@VQ!|+k}wKER`+33>>S> zV*eAE%$URK?4hetZN#J&U`l6cnNgh7K4cg86yB20$9>tl8EiS8TDMq8L|{_q{Kb*& zwz5YHV6Jv+?@WON*=tAk1N8K>sZ-`-JQ3L+UOoTf`>%ff0+{{g#TT!>r;a0Qh?>DO z0H0DSfl;l)Y~h}$*uWkqKxQ=*u`PwE$U|UQtxADLrZBd$Ed|+tP|L#lNK|U#LJ809 z?5Xjsj+L!;klm1K`l_*w?1r?gR~qYnWCgIYtD-W!Gkh1gnN~&UsX-wqTmo6f=kq0Y zW_Gg)pUWg1txc27(aoI99 zW*g>U4Z8Gf(U?X}v6jG>Hx~AqEVxI{K?fduS+JODK@D=s{RmAT6u z{DyTITYNml`DKu`tb^vA&ZYJBCAG%|2XZcX`saWq$X;)}0KNO*kAUp7_QJ?MQ;1bc zzdd{Ygmt`}DzO1;+C?_9;?3{p+)q+7gLM=n*zb)PIH)s_1ur|mZ~1Cc`tr!Ckk0I}?Nfl= zou3~N2PVi~vt#hygNN@w9y;ya8UvC2`BxzJNhfoP{`vW{uMjZ%tk0LF0j5;Y2wkw2 zIbAfHaJQ$V?*zqcMuoyhij{2_PDD22bypQ53dnZ4U+qvVc9~mU1&GV`B1=)-zN~_* zZ<%lEi~Oo`$jc#1)0`SH;P9XxxW`M*ZA5DcvNucYhYue>|7+;9$G%VMetp}O67uc$ z-#;ESl)#~?H}9@NR#wtm3@=0bnUviYe2pm%cYy#h7%SEL0a+-dm4agsvir!Fmsat5 zwuO8QUP=<-qm$tHpK`!V}F3s2w zO;WsX+AsL{!2{@jX@(}y#WT66Cq0iUJ{go_05plZGR|2EiBb+;>h3EF3kkeDSl6>< zzUTq08#1^Kox+AJr3P79E+wC~k%*1dYS9SB=YRQDPlNYANzm!i^*QE+G4#`l-)8zu-{9H_u*udF%px ze|#a0g1i|!jC5I)Z8*RXw+qV~$<>+w!%vowP}y&m-IA1ov8pH(8f7=2eK?fBA0i85 zXEh-@*9_W4@d(+NL&$HXQ(mdgbr;$ZCriki!Talj(t;l8+sN|nV7lSpAz)0iWx+h# z+|OTjC;_3!+P0eZK(9ELb~aUI`y6_L>@{lSKLOc~-hVPQNnh-#gs)yb|M_K~zIyTN zZ{Hq+J1N<-7}X>n%_@DJLQ#Z(fidXA1Ew`$aZwC9gjpapL6b2(mX7;(GrhAojthPlhxj7kc)KLO(rw_Uc8iUOj*H z!|~nqg(|Id(QOvCJtS7x*l$`?=n~0;jST0pMbyHUQph6OWs5-StRH|92G#p=lIFw*%95l1-TdKNxkhI2mJic)m zWDhfMIhV!Nmc>e}?9J&@0QSLy_w=6+ja~3@#V6mtdj9`<^k2_@{NX&Qhjb^E_V6>+ zz2d|pP-@-@422z>@W2(-^9WeqSt&p^rF6gAEjiWBe1v1v1wV5+WXTh@uiMPN_dwAH zr&a1n?=g7!&Cfmh>4(RIXA7e{f2qSIbf*CQbsCS-(tQPW$>DOmdBNWRz_=~B9ezE2 zu-*bLwkPqTxkq4v9YP&D1~nG8p%u&TTL@a=SeY;cySOVp`$Zp#4A|bB-dBlz@D4g9 zGJC??VqWvx1}L1#RZQiIvOLFK_~aDwthQtvA3n6QO7E>AM1%E`SMVG94@s=-?Q~_| zX(Rja{V~Wt)L!fbM_RCG-J);$of%cYRhGgH=Hj;tM57=XHwX|mj79EfX{>C9Lz;t$ zV}~TjqT4u*@cu)U*pD8(`}ng_`qsabi;5)E8U5v6C*xZ^DBy-97Ps{L&_p~P_g(#Q zMS?7PO+nd@4$bWNXA`#_S?c;utYP6vwAS#6(k2wi9bWvv$5>)|m%+|`NwA1mc^ij|L~| zI77evl|wV*qnOR()|8$AlBdT;=<|*~b*{sdw%vs2T9?d;U>XG5Cd zdmzow(D2k?%0@<^5-xPC&oq<7E4YU6*2%;Iv;Q_eJ@|St<5TxUHeOz4M@1I=t42|~= zjK00S2?gGUGeDt#7~a?02j{P^Z}*QcrBuuP+~nScL;`?;l=Jap)vMZ?zNehL&I6w|abhI=*^6IJtUxc_ERGXHNp9`zFRyLUvIs9g z*URg=4-SvH1r^%+#$$e${;P%mQ&4wtc`Yfgmy}hdpk}O8)wE!|QtsZXNImhbp$|ITKz{9|&y_$GBO zJl~u={|Nb=onO}0(Px`||3`M?a3C;_XLWjJ;qiQJe)n~!&hTPX7c#N1mj5}Z=CN;J zWODN*Bd2)f;5Dc3smA$lq2Z2V^qpGV-S)}r-*)HIZtmgHspA%s$I}V9h&$=BD~Q?cc$Tft%y=TNpTkrKT$m zuNyYc8&;1Sfagt*rycM;w{TrGRkdIYzeT)`V8bm5Xvi$|jzN@G%^1$yf zFzC4dnXof!zRxf)R4@wC651ZiCmj!_AN8Kz;&jU0Ujn=^=5sLNKVsa6-_bG9VSlVc zW`czSqJ)1SfZiD=7$Zm?fq>1fx zRwcb_nO#z!CLuoDHimASD!<9E!E?44W}Q@hba|27)KM+?3bGdw@zl8nwi5D)tL$7NQk$9O;ETk8$GTC59*wg!s_F&qOpO1{g7?~ivJ5i_(p@3X`uY3P_BQt z?MXcfduGkq(b|U7gZ+@=uMjEG7s<$7R6vclDI@o8y4p2tKbn;o6a!9t_= zHMsw*JKrOdiYl9Y8X1>u4P0&ultTCB{~3Y%ao4cB*Ed~UlrImP;h0grseQe}PSBd4 z*4#pJ+vMCFwMLrKB%->>ah*V94Y94$Us$5ofwMN^(?fIkLXcY98I`+jKF-sjoO zgj`$t+NNp1Dbx{wVB-C8*had(Grn;8uM3E}Ed;kFW{s7R5@-6ct}b2jcL-Wo@r85lNj3Hk*|nrL7<%ltu{NI@ zbEn&oBLn8SosK)dW!R+hg4K)HBt`M_ZpHy$tf_ktWp7uEy%kt7a11tvp(jRp=!+dx zx63zd56a;SZNG2kyBjUkQu|G$xe`D5XE<66yq>vNbJC|vBuNwDJm$0j8_yfQ6lOUh zy}jQM8OIPV=oIeZ&ArQnxR#jM?*$B~`&13XC@nvj`mrNPc(Hhs`1t-?ocr0xH!$(1 z$Cv%=H-lIpC0LUAj8g87-uBluJIy%8F_j-pp1Q&h7;t>@T9Y?N9e2~_-C5V$>%P@7 zfV`19GjlOmE2ityAWKsld#~VbzVlrPE>72PCIBxf;T^G}QLbrxfXNF6#P@CXg8o-w zLT*c>PQ%?_fDQAJGVTu>gx6r(B|4lqS%ENJy+~8ZRn)u>AhD>vf#rVVHr<6KmACVk zyRMzL51BYG1$B>_I?SxKo$DCS!)wXXA|Hb*+&nz=BNFDdz|$y#mX(Gy3=mwI5(XcN zCaHSLOGNL~Nhhx8&CAJ+H<#l}weuizqR-ro82{WO2e^5|6V(%ac;a9C=@y{y@%XtE z9m{iJ1xi%qd8@jC01YA%A(z9Wn{UQp#LttK{*Fkp1e(Z9imm9hN*brjzi*c{%=Bh3 zcF5{~y1Dx<5vR+UO$jzg^Yu})Qdqqhm<9&?jCK?Swe-Vh`{ukLqxuZdz<%}M*N4T( ztKR#E63^89{DiH)Ir4h`5y9;K;>?eHe;89vSCegsNd!_*vWlV#kWEbtF#uvsn6WC! zVf}j^y4t5*7QY~rHue6&$nIo$$RqZXQbOzne|$u-%qhOpf{0w^ceK2K7`K;dP`w*1 zZQn@i<0G`w=5wvS(M>^WkFLA4=#JZ>SlyM0v$2kxTU&~hZust`+|im2ZJ%avYj%vV zr~i(imo*51@^nKE<7DtFhG-BHe0V2rs;a?Dh?b#P?IUr&SY*cOC3O@V9VWzjIiUtv zH1dhoL6p9{EKx!EWF8L@5~;M-2&@6?%yXk9Kf$bwn`izCG^>OcBFsdAmd_mbIn(XjFj)3Io^>2bhYtNindt{|c)4=|4){nmt(DGdlv!KWXD zxVM^zX%&ZMk4ris`@pvLTuuaaZQ(D&_yDU`n2SIrZ07ELwn9`-Csk~Dg>|(8l_@y4 z*R_;1R<lmd?k^Bc@H5L<#ULkIr(;}?|4*Qz@k{q=7Hbhz2o)Ku z?;6ivViz(_Fs*iJGc*TbmUTEoM#Mg%6>F0$z|vtlI!lw^&}opROnXgr6^fb9vC~=M z(Q)m8(71bpE;;1)a5Vq5ipcw6-n5G>X#3<2C$b3uj^BfWb$_Vy|3wKLah*VVLHabc zkKWbK(u5fsU?0>PCPKG}vG3te4$n$5JYEeq1UDZbq!HE1!~8XcjQNQAhqWC40Pa3O zRC|A{nqJqvPo5-=X%*~x5+!#_C{OtOJj-e%EdcL(JSk>mf(BH~gHz~lal3?JW!2rY z6n=dlsi)C}gJM1|oI8q!R34+QAl)A&nk-l|cm?@Xa zHMnqF73(O4uL%eSH3z3qKcT1b+&^%HzGhY~`@Z#E96&T5S-!YkF>$hZ)7(6PEmBmm z+=9(O^z#_PTo{np9D^k3g`j-SbPmg$pT4t(I@r$eR8jxtmX>ZIbdSWr(GM(&nH#Il zR3|*7N?)Hs-d0}NZ~;Mp^n`(iOWy@rkRSR38SPd_Nl_C}fgolZZlCo6HK4#*i`=z)p`qV@T@nn5`vgr5ht!o>mJP zDQAxG7S*FC#kbIM^w^$AE@6r|BsNc_BDJ^jd)?y5n9O^7+}h9fy+_pRh<%G4clyLS zN;8@x`jMVA_Yj|U1xxJcIX0l3BCobn#x!*>HmFmvdS>#97|!sA6Ynk=K~n?@SQPY@ zXD$v`wMrr6`%J`;I$_p)&Kj8#(hqa(ywl&EgfShNKpuKt4n&(KaXf_SA;yrKqTNrs#PU7MB^shGjCps^d|70^KX8cxt z9M$f+r$YQPLd~IKg^r0wEsvD*B_H&w(t4EU$8{3@Tr=gpd9>gdN66h?ui~(<5iN7{ z%YAzru8G>cgdZge@7!@MP{HPwc(kl=;7>Di>%w94T0$Gc7lJ;gz)W1bmC3c^tF`XJ zNx6>o&y_`|e_MdJGWW2UXHwAhWAc-vbZ_U0%^BT0p6a_I4N<=FbYL@v!xwfp4~<*hI1dz^s4#P z#6kZW&$Auky}}=Jlv)I+8?y_AY}t3K=9csVWEAiyRXUE&!Oz&oL%tME7EUnT5)EW9 zI3=XiD#N1TikLV+JU)haNPJ~c%Yi`^R)SAA9+MPJRXX1ItrIpYKlFJV1zc-o^`6@Z z!1rU};7Fu~hf^s@$J5i$KyYzzK<|pQ^t<*S@&9Q55&yRq{w}_2c{jnw4h$0V(s-o3 zkke%)X*?c_1bUiC@~^Q0oB`oXveLaAG>KeP|Ib@O%`$~OHDLlxcY}Pgaee9yv_Oc_ zRKH9tN;cy1*wLTA`jE;v1RpEFwmmOoy*y0@-k#YspNVcAcEA{FXuF>8Xlr1;GFHKY zD=2H;2C_N)etJC(p%ePHzg#b-y>%{~tw_}uc?s)4=i*<#{(Fw-VZ}dNA3;;KFfm>4 zblHXQZ}@z_KR3Vk+=V2dtC@B#NhzNg9IbCY$dwK=Q1BQ>}Q6`=I(cQ+k*0bL;#4U>-=6l4mq(MYp^Ig zuW7}jR6Dm6vsXtVu7wN*uPqHDH2j@E(ZN?=P}XWDX==@cN}pjslgMf|0J%y{Z);uK z959?R=#qqb3@)xCtzb7Px!NM zhRrh#(x2A=tR_##*4gLzG>w0~oj z;OD$>aQv*pCGR}n&%!6}wg0Vbyf39ISNhrUphb(ORI{pAEGMCdEvZGyq?gK}O3{cz zhB9GDeYRdAJSL3lSlpGT%>vXD^Rt!PA^@t?m1Vpz!EGy$gsb!qxWoyz(CNU$6k;x0 zZbX}rhQxDd9MVv+K9t0VEU}7M1yPY^UvEsUJb~e^S59bIUC=z@ZXqWdsX~k~S;PI@ zELQ>8Kr~v`>dMQBOJvR*{~dEy$zO=Nx^fC0AiLWfanOSkyZnwSjehjEBt)9TT^t_$0k3O-c0;+rUNCM*#0=0|2OvNyd+pQS>daW`K0pD0LT@2 z@M;)KKFl)bF~9afUQa76PssvJef(+tQb8z|uu|t7UkOlJ78o0(^brCf9-N_v=|vnT z^oe!qZ`Xf{{Zo>l$}eqr31iwQq?cEgO7p4wqmnk>^TsE*A3ur~6iT&|6(#p>LzbX2 zpoGElLAesT?Puh08QpAPS)fe`7G_?#jy6LEY4+R$OvEm&J*w5r=qA+QSZmIDJF18G zS}hpEp1tt;L7Q1re`g2w5DnKfZTil~Mb`1x;w*GHMdV*q%2>>mzC z0sY)bcxkAL+mQVCPJLxh9IvW0$miw#NkL`>b`P{ZRn)?2-|Y||ME#$06@ic%OOQ5W zXtJz+NGw&`X@b>M^+tO{>~l+SLW*95j{k--1N{*y7z_Y6Feolyh#m;5&MFp~a1~Z3 z(Rjv11WL1igau;y(ia}d$_ujOgVZ>|$rR3yk{&T0c+}#$eD;0UN=%S#(FFAgU_U5> zXIPm>-asWn#Rawq+mAWfAK-$COuD5SUoP=plsHnO%ii{G(S8v>-lFu2vbHAvbcB!? zEDzr!PcERAsrALQtqvc!tbT^9wpg)nK6=gD}a&)D&{ z;3`xRm{_`&^>5n4fd3)8$U3D&j_~K;qzHW6xa2))I6(^LfGOmbw~Gm6%d$y0kYO_o!Pu)aS z?!_rA{M5}x5M((b?SfG-ZV@pXz4OzPM@BG4v8L#O2&&2y1bapVHE)isZ%rO)$|p{e z&Uh-Aq}oj+teJ-&nz5yDycCSC@Ss4oJD%%Ru*3x(>?*D2KrCpV#?x`4#w)0}YZaUH z3y<(d4m*`m3#<>RKs?7a8ua_7NKRXNB=`cJUk{C zV6EWFMoI`l+tVF8fT&GVqsa8Ig^?(8BIRRQFH<%f&s#rzJ5R_xO<&N&`4&;qg~9b;x#F~xR80ncyp?}2P+|fgE!9eN0-~lRA#|=F zS36Tt;1_V%`3y!P>3D6cc zEP(HaIi3z?nIO&~ujAcYTZIlieI+Z1nvt|6vPK|~;rur(HN+(RS2`j;)rJkf)i2|7 zUibuj79pmHl}J*A?zErm*~q5u2=Mq*IR5tEzvnVhCDZWGm8}|VmFOo5VsHc?an&@1 zTE#cG8^HcNaZJHzL%{BC6qS&a4PS;`ZzYoDV`dhlf0%yAg_rSB^ROqHc9B-}(VS_M z!p%N4wtVJ;NQSb46GX`P{`_^h*ZSZuBTbN~zg_08>-Jzq(I%B>G#($98Rp=Sep7@+ z3ZuIBJ$Jrl?XJ<@NC5}2Q{#OJsDL?n;Iarknu%+Y3Y{ZsIp2vRloSanexl7xqPL{@x&}{c zb}$h_^#r47c8aXqC)g2uiT0b(MSiGV=UsLuYRO^)Au;fLAvtMyh_BCto$SS25y@OY zCXWxYH9g*q46iUYF=ZJhx2BpMZ9$mqPabnvSd#0m3yszX(4bqhwEzTP`Zp^{U|CxA z;3=Pg-eIVQX4eVnjjiA34ylFzJx#%*uq2!X5kV*is%2huj^f&qneK0lReLt3u+;Mh);m-3XXuS!hOmk`2GsNJi66Pj~3NW=Q* z0_ltff#S$s@y|Zv&b8fe;0DtZ#y*pV0{AKtHRnsrxHfbQ?Ef_VO?%xzVm$)9AoG*k z{<+b%vny3BN*`jcs2vIBS1KK2j2d4Z(df`e3{&^wFzioR_`B^S59h%ZJmIHJP6UJ( z_Gj-e^Hb!BX%>LR&XA?6{tiYi2TjGpoIzHplUx@26fDlf3X7ZMUEGVokJ`@r?4FDkP} z}QpF8Q>%|D8P z6`h)KDys(7JA^pJDee8EEQM*6%qo)O&$#Pji=NbetQiBy`>oj*6SYOfK^wuLVXZ#K z9k7Ibn-VGJFSO~2b1pxRP2iO2znhh8sgA61C79Wxkz`Gt%q!VzO zImD>kyQTHb>^YcU>nFT{8DDgm5)CP3_JTIFJMa%0hf0r)Q9anDO#4*(SXJPQ{dt0e z|FJ^-zPmK&%Nb#&Ox{WlIT)t`;BMU~3~#`aHu^H%^`WugUa%DAqN`?CH{Y1~vID!_ zZcaX^I6uMuSk?iiKYGUbIGMvlisanvA~6bk)2y!N@0ZgBl4y6s!EGT6ALOWopI7GJ z`Zn=58~%1n&SnA`oPhE$v0 zo}EtSS4=G;$o2sWa@OO%OMgMtFRoFQQ`Gd5@*+VOjym>>n<1qrsxfia6khk_kLbW? z>Y-?1fMmrG{ghzXJRYq(*q|MyA!Xx_S6Mn~*3Fc%_Ny(o{KB%er)bTLev{FbVooz&sexxnbG=nuY1!I zhSkZ|{@7=h$PsJJD#;QP9l2_i2;u&t04Rx4M>Qr>UO~~f*~JVc^ob$!*Pm7jKVL`t zEmcL$k-nqz8B)uXABloPo6;XezwgnS@$v3Wqq^H@u{hF;S;S=o(rRspfCC{Aiq3OV z4@AEm&C>*a2|f=n25cMZ8)~5YbdTJ$>pdRnHSSQbD9Q@XrG><%^*wNb16;~{co)B^ zyVs}1yeVv9F|F-2L$rD?Ri|9a%!xf($&ewZ(lHAo%OOhtGn%}XrqvA&3`Fi$-{|wJ zisag4Z`p$J4r+1xaK2Lsh^8TfpP1aRT#KydqAL@2GY+ou=kT_PCw}p>NFRf6Kid(sy{@dg4qJco!V*nt6iA3h!#pRJAHazO- z4hdmX!VhhXmSwpZ&d-+S%mLbCe-0F_q)7N341F#$xnRA7`8@LB^vGeLXww~B!VhNC z5qL{s>eF+Zu$xhG1U_3q0+f zDPG>i!R4HwdGUN)!SF+FUM-@44vvp`m0nKJaRSce;28o`UI9w$m;WMWLGqGPYYQ>C zkD+S&+j*Q{R7>No+g~4Aiq@kIFP@^V^&6L3yG%Izqmkv}Y*He1LOR@OU&m2gB8l0u ze+ZqpF2dE^Kz3N$qa^oWo$Z0@v#~t)eY}clSHLN6rWeWreoSFuh93q@HXIx*RL-)* z0%en|Orq&DH6R6lRUkWg&J{aLBn1O`0YgMNu;znG;XoX9k0ekNm(%x5^%KuiY=Tu1 zn6v)x%m%D8yu>ZMHAZ66S;nv4RCkQiJ#j~FakoP1z+x2VT}+fY1YW(+&D$94k8H`A zEKk*?C~M70t}t52vfdvyUS~rQ4q$FI&P*;I!2Zuv%5~R%m`&=K0`SE!tv&a6SWuz+ z0V^x3A7KQcrv+w1FeXVDRqzJ)9GK4$rFhNCj}q4cBKV!eQpF=9r`-wI`6}b*x)75{ z|6`?N+;5+go0UKEweG(K(NaFe8YP{xI!ePC&MaSD z-kz+)^K_R28ylz$?qz(ScRDY$K;MwvNp#NB zBp6i-DaAW!H?$Jb6T#l`C>)6{1YR7@e&2Uz3YFy6JrkpB8$^)esXBO=$^>C?M7mvR zH9uutA83$F>@XH$lS_Mta)R^z&{PD;H1#G_qRJ3R`gPYI!jQBJg<@rNg@|ZTKnr%S zb4ih91h8R}HbbddH`(E%gvHIm;wrj5cSCt0M4Ld1-bADLWRc7J?^^do8DL-dK7tOJL;$hHb30R0lSkNJxajZETfMB?zA5xHha#i@Fnj z+|yFh7gtGiIokH5#*|FFdXb`AM+teu+BXHRY?Ar6x!m5Op*2c7EcpoZ|A8V-_Y}Fn z54JYTERJek)YM2s>wYG6k5EYb5dSR%}qpy^>+8X5qWuFG@5;(_- zV5o3>2JG<^A^{m>G_@h$(Yj!ycv3=jfi-gQPv+q3ji0!rMi-Us zO$pUvek&A{m7#{b68e!#(1H}9r3F_(M7P0)m~Vv{t?>KC=%C-{5~%UXGHO4sw_iNe zx4&@suE5WFQlPRXb==Fxzf4_ysc@yjf&?q zka`R9a^?ItQA^Y!?5X@*&b*#!z-HdXIW34|tjAKQyuPfYlb6rUr2ys!VXVy@L<~b9 z)6$?6oRYdLHe5{*rs1Hf*#^ zdcLS&rQNZvhK$VkPU%N%AYA(3>HMZj$!$^0yzt--X%b6RBob^<{YU;tTwG_&7<_Zt z7v2pT0ig;TPDBMHCX4qR$2ml46{F$|z~~eZ?B&@5qH!Ya44Es}U~d?@6%x(Ng^Lrfwi}N-W$Po06{^{#JSa z;`iwweAvCVV-$RQD)}!g@&iT|)(h44i+CY!W=|D^b~OtQs>daRQ?5H{1X|LTc=7y; z_QOL|-OAJf&nPFsq>fyE7YENR&kg{BB#@vi;#AnowIsGjD zZjt0W>z(qH;0YfN@$WAmFPDh$u=sGnFB;>7(kD~A;mFX(rVA>v#+J5RK9RF@Bjb#|ibNF&+r14REx-%>S5zS*oP!gZIfg{bzE2T+o&;1`s1b`cbqPnfpu`ZtD2go_9|2q+Al@03ZD-uWB9(!{N% zu~b;kYzQ`nF63zSxYP_PJZd3c*P;9W4P=ZH#x9%WHU<$`65xC zFU`7$rOZbX^gz&$1-@U;P6SUg9z_d0JIHu3OD62|bJwdjBA99u4I8t4RUrae45AzF zVGxqnBBS{~1|D7v)FJ>Q9qD7A+DHsfMuW|0>^6PBn@?m=tMyQ$+w@4e6=qo|nTdy1 zL#WZue)TvA1_RhJ4T&|W4QBVg=yLUF*xUa@l1n8|_1ImTps}%aI^GULgz-rgbWl)@ zFcLMZMTe76x3*M=T7t~AyS>m6D??FO{pCf_T+|8$2LuI^ z5Mv?(&C;NQw5((76Wc{%t|}a@goBc4`DS2zVZ0i!@kghm*(5BNiv39RPXa3|hO72c zd}@Ysx+wYz zqvW&nvX-6_meqs$>wLehFfP=8f71&#N~Q#cW`6kZcx-IRezb zK{*Nl0CqCY#@%L4@GV^0kidGPaR!^7Dl5{t!}muKloY}yETUtAu+esRV+RDN4*S`~ zSs{OT$7`&+0hwgC;OY{C2q+c5f`;QqF=6$C{8i)faGPLm53YimCCIhXC9ey?(C8>5ld8=mK7>9kyXxEJ z18vu-oD%gB%b4Z-fIruGlAL&I%;pP(APt?39oz7SnWG|enB&wIUmUAiivV+FV>@RQ zmP{j=8f^Z!Mlv7_;9#}IrqcY^pFngNk%JctWR=1X2X5@$HtF|x{OX)*#ARnkoL23Y z=+Fl+k4kF%(xy$`6gy-8%lI22J-ekidM(js5-`nNIn(Z9(ZG+t0K3?7ql3v&BwF-n z1G11E*hvVlCSGg>(`WFqx+y`KYo!yX0#lW*IY@@h^TVr(nh*>SbT}!|sGEkLHU6Di|dxb!hx5^`IwZ$%JsO znd(_H)v$sL92KGNW%jLA>HOWul&EYcg5wJnMeTan8~IHp`uq=(m6Z*F6~kk>h67#0 z($BFT}`>)>a=U~l4JAiI9IcOK?qMldke5p%T={W%jRuj()mmSF@EkU~BBP-DH& z9Avj==;mcSd(ZMm5;DpSeW8F94J#Q982_3Z zcc`tU;u`qgm&|Ipnnd3K>`Y5`-DQtrmKnnVA!aJdfc5i`j-=T3#DI#7!NG6s0^fBF zB|Md)3Ukb;;DPAISyf*{C&iVU(UiREYW~*eN=%_8ApTg&|8>ASY4wDQFHkhfngy{x z4EaYUfJ7fDJ8HzC9T`>IL^aOpZr%kG!0bHEovwt|jZ$4`^TE?TFfM*+x4x8~z}y%U z43oDxFpHi*kiX?^!&TGODbVt)4*&G&nh2oVmGh^c`Unm*#E5ko-XbH20`3Y8kpHm%0Mv*BE6HMfS#iB>4i*pruT@hg7;6r6o9a3=y&B7H(|a31@66l$ohM->;p2Dyl)A8Sbs4sgG) zc>RFTwCivC`rm5am;CPAFls%=i!=0T6C*X=K1U#BC5x9k)x<;F5W%OhTVi8A=Z^lu ze@$ud==~D(wLr4|wv5Q3yAI1|PEn!tm8e4r1nru9-FWqRq~%ZQF?YTvTw0{eetjZE zy4|%8LqVDps{jBv!^oRG>S5&caY7kpU?Y76-#`$hSUn8rIDO~?&w1Qe_~rZS0tOeX z^E+D>YM#g-V2G$tSaEZ?B=%v>rza z=#KOqGbprZw9aS<4%^(a&&XlNsXtOc$c5$`=rwS{0blnXu7&I<3n|u|1ZrKR>M>%z z_yaBl%e^OFZ?^|F{JQQ>n(Dlpv9FD(`YXb2Oj8F)>@i}6N%J<|QZ&XpSoyJh-d(k$ z%}bOH957j9;O5!e;GZFRlkMS0n0}h;+DY1$cBr={$g_^cjOh9M&Uo7ZCo$!}nY=iC zR+x**__fWvVYzea{|nXlFXQo!c>I^~07d)<FPtA=8w~zvWH-5eo9IWpo2hBUe zQLF17C7kg8!En4|A^Ng>@cp+DJIK5FbO!s7P))@gdV@&_*k@)amR!u2*!B6o){+>9 ziGjv|5@NjxE$4~t`uzQ3`=z|N+F0o4&FesvjS0EUU4mM&?HV zFefZDwIxy1dHkk>@68(^dWh*^a3JT(%7Jfm9QO$ZxCyJU3&>Dy})!(?; zQJ7Il??-HFVE;MX=+)$(@($XQOeNCgbOyzZFkM&g#oM+2a7g@?3*e#!{S>G<1G=k| zI>^oit&03}t1ws&+gRwk<3ruJ?$H3AOvF6-&zZv5_R`c}y1{^$PdCZR(ezsTx?+UA zJo_M)i;scFgVKEP#+_YoCW6*+Cc>R$CRY#12vdm>%nH_Qb?+1%CYT&|tXx@EfezP3 zNLZ_!F#R72m=qU^aXmVpSw8hLs+5Ai8C%(Rdt>CJmSO_US`PVKoGDY~Hwl`@wb!|}~XVDY~2b9NO6?;cfu#JTq z@#=c5i3^>Pe#Ji*DFALq2tT5jJM=xq8zV$`Q;AEYM?wCQQrFU-oWgt;3cO>Bpkzb2 zsGg2YB$7U&gkrcJZ%=2I&rI`-Y054rXgISfJB(5lrn8J&Nzv{3OhS@cfr5e2L7vvu z`VPm$o~KRV`i8Sr$hbSapNZ#>wWNeBPn8-=|Ac{z+rxJ(lfdnEe9VQC6k9=ZsMl4`XuA`?KUc4UY6V^gx+-3Z3*9UW8>*cVDcH`o-2&>0u3ev&ZQ@65# zWP`KQkC&;RP=Jxcl?71CG;ID+GnIpB zb_?l>ls%56Qd6^MmFVI>#LFw^cRSj2=+lq7meD8Iao_=|wcA@^p)90jyrumj2fy|6 zs$?V!Ds@ZWTS;Ozni2=dTn2wJB;9IenN!uBJu6Yy17;EQDi?6AElGg#1R0|B-*3iD zVn_nHZg5$?^TD7a5nt)x?zilpkZE5zeR(QKGQ-ZvO zUwk>A0ZE#NpH?G0Ku|C|{L{cViZT;@xzw7f8KsYv6uSpH%J>oe_Ud$&wTr5&u69^; z!Nff8451Z#944|U+A0^NAn&RDIh)&Vo<4sD zX-gB7Y}XU_pu{^XCmp<$h*@3iZ?N?f(JZL9A&8nTRF`e=3FDcIuI?n0u(7`E|h;0$ZjxpelzfpE?Ej)B0k94nCvyUl7BdLd*M&!$CR z#o#O9_yi`zCSQB7woZ-hfA8?$(90w^HFxZJH+XDW?z9V`%YsKEQoPm+<`KX{)jU3w(O(1bj=I zG~n3i?Jv5ef8Vl#9FwO#=9435_I2kCf)Y}GUweJbPBxa-HB^ZZDp>o?B7nRG4}|?q zflgE7<5Y>!hOFthImy-u{|=haK80oh&c(4PK&=@2REIif5EvuR7DIdnNh?$uVcB0Uxgei;U=W(^^B#-V~ewh=64oWv_|QzB41^h=0kP%K#yR938(;~rh=FgU92Z?!PK zHuch?XOuG)XJU7ufVpBErMt|&{@PZ3D;a=fNl4r1nqUJLKU4DsPdU%}G9Un{>WRmWAr|+DxjJmWbbr%P+V_|6P=Ybn z?q?c*1K&~Y-A<-rq%L^*6x9DT zat9o4SPv$)ytucoBqi-AJYUSn{oF1L?i0GCZ!v=RUF>wv5I&-_85_W^J}@h0R=?Xj zl#2!JU^7rc16us(oM4Zq8)*-pz#FdMtb3H z=DAIax;rx!{w_@0zIF8Bn#x+5;>oHDSUr3AS+=z$z2@ALw*V`P$ok)!PF+b=e&5P4 z%?k30Y5X*y^m>hfTPR>)&lbXT7+$uC8;FIa_71kNbc=-Ridd zu#2CjkL@ljcPJBaA|+=fl-8vLx3y>8%j?q`R&egNQ;l7bwX z-o;4e-VT9!(L`7VW`t8SfI-U6f&_Xl+1oJs%kp+>p2vp$5<(FZ=(uYkxy#IbfUn#a zus)ALiesv*|AG>a>BMQqY7kIq+-~WQ$+BMio%fx6?r;F*UYsAizmyq(bOs6vRrlm9 zqiikO1p=nSWN&>1z(eO_kf9GR|E^X46S}q1+a@~q$rdEf9Z#oHx;F2trUJzUa)RFr z)Sfz5*#lWB2=32&Iz5*`|H)B`HDYGgHc(d;CH(CFyI56^kW`pr>NB4L_MH5hMV3YJ%Kc^S2mEtu$r( z!-vmkYBc&+=p!$i9gpaZOJkwhU3s4<2V3P601F1~xu&-Qr`tg(q5_)Ghd46=c5~U9 z-!Ti@C)qSSHRqI^4_j?aqi6?(`vq~2jR?ULabKv$bMuQYN`hyPdr=pw9tUYNI!XAr zx5f-ffS*5GQFBgH8w0<5ZP?>XF-$?&B_gc~CsgjW>|83e3q%g61U1tH>8BITF1qnrDcGObDrswUv1ldyHv2=)04H@63& z%_p#MR=?M9o~AREFu&-TPQgG6LtO)}ke~~A?sn!HHahC1GVM-%RVtU-vcFl9F=s>t zPAdsyrKIIw6DvA+nm9NpAy}frlL({5ab9*UMOOc{ZLlRk{#;*=Zxf=-ESKN%iYh2! zW31M3G05|UOyuKNOirSNsWRK~zuN4^(tO1^X{e1hgOUTFEXHbau4!Da$cqfJn2j$r zZhQ5uk#(M+J6C)-PtRKj#DIaVH~eMekK;gnv9JwTKy7rgHV2^1q}ZFl#!jbuOPjgL zq}V`E;pniFA)JshN6E}AMGoW8NHfWlHi{`2i3;zV{EJYi4V=Sm5CA{il{`ITAj>3R&7e#VVn&WeX>8U<+$? z5~C+wXjb|RMA8zCQVLBWfkrdE*5Vzv4*1qhp(XypQHMblU7h`rF ziYAOFEtV78S|;FASv8J=!3r^juZ5T0BWf^Z{>4*T$6F?j&KXPqtVZyqG?LrRm98#{ zXQ2o~!aXLJAaH-WgpI2rCPyS5;x~ajIH0!=zO7_L+2yU`x_Ae8Y%O`_+dc+ts zIm>`I4+H6^_!KWApyYV78zuS0vZh*Bh{R0$Yy0tF;IM^UOPqYE=I}R$dOX7=A>WqX zI!2BU(A2~2#=zk~M?~4irfwZhky2aUp!}(#o^sLT>n8gIZnbGX<4yaz57uJ4{a5Bv zt-8Cz>teCGn?x|+m`Jh`n0faCl@jPXI_`n zrU8+mMF7;Zgs*B1WMC-1Y7I6Ia%hQ$mF1J$8aV|oi$n%qeE9C@$qt55-U&?bvG09`mk>5Z6@~OX^o05EVw;x~)m7vM zFP9Wu(x&Tu>;Eq6UCn{8z@c3j(kvSxub>LY)V%*?q8BqE-?=4@8{T|IQp5$-B!` zp0mxcR2jCna>yy}KURIxtDv2ffXu)3Ol!o-=_q+yl6Ee7B~c+(>1&$IaHcFIHoo}0 zKiKW-q(N|eYeu4Q=Ta1Ym*k;p9d549FAU&Z=xfo;pJ7(|Tb`bbeIgl{K0MXJOVY(r zX0?XZJn!w+f85z=MxyL)o;TJ90&0lRp$hyRGP&T+ih0tGrJEukp55@NvU0*N=XWtH zGCrsW9%in|-Md-Ji}SZ=a8%p_+5nhs9(6Bz|C*0AyTWi!P=}mN-)r{9FC*s~uwR%c zf~JNjL@@e?Wv{zZbcZcVy|qY)vwGr}0pEoojM}jeKzMoPxtHM*=L+CRM!TiOb>vkl zT(k>*v{TPkl}8N?a_!CkoL5lLDh(7rtrH2_Iy2~#`aMRq2JUY%FZ$AhkaRodLY1O* zxJg5hXMW%-kAUTkWk`ZvObR3cXe`ZbGSS^VY~Y8_zf@qt17U3-pL9O2j3)eWQh*y_{A%2ajk&H4N=vaS6TBq2eD4x!E&y2A)s5qsolRx-e-PXm~>3*&fe>118$mMzf{e2WnL`V^{Q{4FPp*5v3)b-*e$4^ZFS; zmn)W=)WTSb(dbIjd01BDHNuEbg!Evf{N}n}1_t5ABi-&{wvj0%a=fH(!GCdN-Arhdyug0@IjJ6|`*M`PkjCazhB@w$dLL8(A zpCk@v;l+A5>>*{-p)1S-Vr3_mB^{Tj*G_g~*hO2QqwR`CO}4FTGY9S46!Oq;`O#}P zQH{|7BpRX~j5#Qyao$VAYv6==^#<6$tNyk}0ECal;HX-j`4t=(pbx=I)mE(y>F zJIMKptgP3EXCnf(e?QiOHS7Hl!FqhxKTr^t+WVg9i^F)R*9^!5A%6X{=_O@JzsAhX zRsI=>ZdDXi>nw<~uvTv$Uv5f>J90vnC479}A+BW=wG&}Nx<`GBsi*UtP?;U1KN5rO zE;q97HO46jJZ{M`& zLU|1{(QNn=>n{sqKmDop%eFqFeyW7yKk*ziYz)!3<(#afGwbjT&ZmZL^Q@!f&7xYp z{RTz9C!4=`wY}1H>=h%57!*@aUMxGJ|I`hZv$wvE&Zmyc(F>Hin;4sat$gU1<62GY zPqz<{Q(QcFm!$D;-|_u+)l0XzSoQRZ!M9HBM@2CdOHH`k2_*`J7gQ}NJzgG<;>W?D zn|uEYAO2rpq7HPp&3YH1NJ~w9{gISuggIokhZ;-N#F>q^M>84`(ki2>Bh*PsGv~4Q zI+hm+8HG~bk?bP-jWA|R>smZA^S~gD&z9LbNLRNLmP)LaTJ{_gP4CDliwuK5 z+EsLycNJ{#eIEa2@i`?RuIMfX7`5T1k7}$Z(bmywajN3PMi8l5{N>ab5Tn0|;gD@x zbU`Ru@bu!6q{J?fe7_kKtOI+&T9OJh_7r)U+z7qQ-?|Jw)+#>5h({2miTa$nz?g+xqt^8en4HY#FgWm)J?jgQRWj z@q5XN!ojSC9Be`=HJDTu$-gf9W=j;4gsI=0L}!{Cq{+JoTv(%3J>YeXmpkz|62kEM5U?!-}>(Q>XFhF;k_{c1^9>ap>_xfiWJ`r;S*J~91L7^rlj zkN%zAQQT7)I{w3`b3jd)o0J_}`#%Q`ag3?3_7hCHXxM4J_5vsM^?+QRJRC*#q=f$f D3URY8 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/countdownwidget_preview.png b/android/app/src/main/res/drawable-nodpi/countdownwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..2ef9baf14b5ce6beb09c33d7eea78163931656c8 GIT binary patch literal 8823 zcmb_?WlS7U(=KjB%HpmIi!Uz4wYU^0uEmPG7ARVvNMUh`yB2pTS{8SAXK{Bf@0i>;xX20>~=D&MRZ@7cVNUd3Ju;H#FTfFg`oKy0X6A_IEI?piV{8EIOrx zPegfQW;rl4W^?;scx0lkZNN7$a$;&$OW(Sxu_qy`YWwiwXIyIkz_5&x-qHC@bL-#r z&E3$*gzVC$k;z3jui&}mjk3Bf3-_OeRc%%t(Y8KumDP>bUNL4?t_G$KcFx}{Z9U_Y zGNEI0S-GXjX?e8`Z8|nVPVRxc{x|tWl_ll1hv!e}S%sH(FWx2h6+=%$YtN9@2d((K zFBx~LBelO!T{D`CSqSQ9a?V}_zDld-=r%m(;ruYPaw%k1O_vL8OF%-rd;-Y_duq%`+z+&PAHCFUBK;_DV05QvVL@Uu)Jq z>-h~WgZ?fw6i6q4j%Yepo&>?Tgarf~`~x(!gTWU+FtI({LY!K)?+S3<<#7o8^rr^5 zo0`<|pfC&ezc=|UZ9x3`ko9>^a9Coc%nA+R7NkDN9zxQd2`q+j^Kj833<3cSt#O!tKmC}^P5A_s^KLkux-a8R~K`2oqAu)WoE{%{P3}F!$*P#E=Lb$cx z%~8Qh!+jdO*$1NW9`gHk(AhJ>YFalR`-QwU!hKDHqw{%|yniOHR{G;joQc1!OR~u- zm*zC#$D8LUuzWw{|0mZmqG82<(0sjW!7M=d-|Gx3Pc3R|r)R#!-)J}~sR z#UzoF6?ay&pU%i^dC0;^?swly%^CYE1jbF+Ow6xi{FTV-Abk>rVOTag?jv zH@dgrT%C)5f0XcrRPMJrm-Q37GivT=cepQ8)j7{dW7Y3T;p1Q?>d!6$MEu<4qf9iOo%7Hn-6ZhcUx@S4u4G#kB*YB?81gKkLdIDKUp{w`geki z4~@7K`9-Hn>@6!-4cdzT4QISl?GF+Ak##HD=t=ZcKGi~-H|u(4El3C1RU@Lxs4}b& z*g|V;PtVey7{4}!gpC@5N$(%#jEy^UlE&VV6(Scge}BFNbHOFUSs6Mk#%_|xnBMb> zd{`m0=)yll0EYGwl+}AQk1Ld4jyg^FW=iqpCma-v)?*@?y*zCNP>H=<4rHyRp&kah z%;sA$iYt+qiJj4rvVCAgVpG7lC&E#+T7SLgl~|*+_@fV*%E(MFHe|1ra-XkntKnZt zEDRVJ0JPweCWm5DIKW_3Q~|HgTLZD30Z&_1mfsK|tjYIoeor<+^!v>OKLjoo4pK>M zk=w%JlqWz&X@4>izlcZF$ItfKV%&hg*2NDfZg^QB8ZXITVyJb`5XQ#F65$~Fu%~nl zs6jaoDgUsPF9dlCrwu6~sl1Plm1WzzH`S1mcc;D=8&?0d_DV8$Qj(pKf~uT%chr%| zl30})E?4PIC`zCuC!Z6RrPj7m@O@5<*}Yx+Q27$jmff3xStbTq$5eXe`sfM{B?sbV zcdT({1i2jPK~TQjwMqbVUUKT4kqc~AG3BQAmj{uf&{DUsQ9~#Ot54)K%ocza$=v0C z0I_5Q5LxGwFM_gj3&U(29}Ynqga&;HJA!^fB%`?~?yg&jD8M$|x|+WvP31nAFMPN= z1FbDd$3!d-%57DMqp8(lKryWHIz5}9gKOI-7fZliDqNWr;@_T4f3RzzpW?vZVbhy| z(t~Kw>yx44#ce0abfkuM-NpP@j6U47@38{G76JsqQXci zM;LG^G8HH!HWM6$*1*H(Npk~d>5(!r2Jhvir^=aj;A#o1nj^de$RILJqu9*doU?&b zUogn0{t1lK;66YY#pdZGUcKT*%vF8ef3MKzVJ*u^H^4PPZL?FzD z+z5O-@MCwj?pyqg=929uU|Bu8MQA*@b@VtqC>XWzEg^0(Reu$)X&7@`At=*=`!bw6 zCV_y_)=*#N$DRtG0P5b~M)#n*tOFL@>rP9^t6%#vQQr2vI0ijZqbOF^1pvsB(LZeY zOKt(bFgJu^G&L}m;9a#i#WB)+7fCrN9vglKQ@eJZDu+c{b&oDboU@EcMJ;wMX5X9+T2{1d%+Em#!WhvWUIQ@!!9#e9 z1Z@{dwkh*Z3j0mcHd+ScAj0f&pp*eL_Gg=E?3SfPU4PmQJ<{NKXlWvMpWs={KY=o$ zLKy-0WT=^p`E9-;Bo5Q?=;$azIeh&oE7AO?R}ZN)UuGs@6Wk5P*_t470q&sf`vVz# z(e;ppEb_m&53RM|=jvQU^N6^H^eVOyIKeW+uMqeh6*iJI-mPG49S3FX)7nCUzM)dk zImO57R5LTPrJruydtj$1T_XrIRH0?41gYFPlVEJ-LK&g&Frd6m7oh%8n6c>7H zDTtQlmPzHI603_J_&Dgn!N1S z&OqYDn-fje&JqpwkG8kJH2-k@EU@D}uKA%b%thHj5`8Dp2G17`TfFm!KbJyzlCpxg zMP1%@B6wNiG6Vg+Tl8e2S?IfH?a@#HnBFjJIL-JN6osHalaxnIalS-rVq7WARWbv& z0P&d@l-Q-koWy`2u>NcSL6LsFueRKJyGI}O$snd-{T9UH&6XX6z_lO^zOs(^((vEr z@RQ6L@{$h`^%|mJ-CLV4Afaz6D#Qf;sR3{@NvXX+|a+_2nv*3 zaxh~M1rOO!tvc4O+wsTh1~Xa`GVzgwxI8X|LJaMl!{cuehzVSY{sIFB*Ep(R1U zXX(L${16>688+xbLo;|oh?V-r%|LE6W{{|yVaBIlwNYL@ z-bd7~`*7@xkEtfRCYFMUso^?3d0w(iF9r_^Th5?b!r8PipW%aL7p7V&Iij3#vvz7y zQEcR3Ukd{RW+COj&8Q(4q&d;0#r2NmA!lJ4FDk~F+($aepdH8SVCz0iCV3`W=5RQ( zYVsjcTtr9jpDxU3{!N2@8)fxgl+EO>6Hrd2OBoWT-N2}B@1%;O%=?WzJB({j;h?Y1 z0|VL~eO^5_C4B5{^*5qJ%sr*yF*PF)gq_5k1J1gZ=5v09M}*|gddC@Vu%(Tlrmx@B z`G&_9Ccfn7O7x`nH(AI{YMAHy#`%eBTEi?rFM!AUj-_Y`da=NZtg|iDXdFTyKBpoc zIqEsg;kJ)j&LjvA+wk_{p0ZdQ8ddVF8q+!&8y9q*zoKmXu%^1JIuZz#72cav_A^|1j)c`baFz78JsRn=`0dfBW|2L9v zTK`dpNTdB%9Tf5Yzmbstst3iR{g0FXVx{HPF{}QGya=6|OD_b)(hB%W4T5~3FJ5*I zmh8Jup$EP%2m1p-06aW%#;Bsl5)BX@bHNRcB}b$Mo!tgHR6iRj&dqx-8{b4QMk{nn zz?Olx^=NY<;q$P5cF4EQ0U)ET*|lect@J+ABNslXHUi+_Ge03B_PeSPWi+H&4{ zIx*)Y!@T@B^AU9Nsz6QC!{#o#dz;!LLSU$+hED_A0I9NeCzbCcYpe8Np4pF?HR_|b zEvDKdY+$HafeN!Oj(GSB<8TcN1V8?!l^R{U!`iRqjeB2$oFA_q#6#BJg&c)sG4Pi* z4Wnj+`gcivVqf3Er;ps}x|wMa-#vc{){f9u_YU8!V;1XBlADKD#z+5K zWX46GAM{M~ck#Uk@_7XvjlC>AIUZy#50i@HYd$Y}Q5;L&NFpIR6dP<~#PXRUuwfEj zPf_g#v<~+}+d~XxP9Z(bDG+G7_h`LSa}5CQvBrNX_Jm9=B8hI(f_xcB9AkuJgKGHW zgtHj0zp0++pe$g89UxD+XsqwMcB_I8UzdoFx)mQ_FgP{Mek1YHNYIxhV5 z+GKIuVIc~=F=Yh9s7?1gaQy}r{0|9=R0*(J~R{ zV&*fG_)U}KB*C|CeG2gJluvM5q;QJc+c^_tS!RNV_6Dh1Qk6$o_;%!BinGSNYqSkqARF# z)t&2w(P{Q-M2nb0`hQ1_VKt|48X#|ctRo)J*@J+>gh`9uJr^~E?gVy#Gj(< zP?qUZF+7D`e|2c-G*6vAwQMx-*DXI-1AA< zM9;Y@h^xggdfi9}X7reZ8zJAy5q12*o+(6q_;_$bmy$DG?LfH50kj$9R;R1=WF%6R z53=FSQYxBqcqc3G=-sAEPjn=&Q^nzM zpiQ9%@nbbyaa*!$V3w87nHfAryPFsRtuAC-+Uqr}gce^BtCPmv#~tV9yd$$Fa_xGx z*Pi1AIbWUc6mBlqk&+2Or2LXFD6YMnA6f5WS!$3UT2kFa8qkcQ$bEg#wo5;``UsYYHuX7|KYlp^Q0Zk&?{!2B2j{CX!uVu_A zj*3yC;QP*?k=mHy&ObpZmTH*(chWe6g%C`uq0jCtri_-gN`(mrgQZKGBg`NQKE-Ra zjb(0wbGyd|zIZ-iC%-yFEz>$$qM|)S$vnRZJlb|{w>!br>Lm`iMJIjX%oT;)Nc9W3 z4*RG+b49yj6%{7XPTk6$#=F$XFf0YCq?Ie?=c-7dMqi~cLz*29qv4Zq1Qpir!%vHw z@=BHVu&&9b%zuo9(wEbvlQzRtgFCsp!F4|LOzC}f-|~z!xs&Lc{mgiu=CcHtpDFkM zu<(6iCA2crl_z0b6Lwx4FI1`2bxph0j@9EQrF`zuw}5J0`j!rb9b#G(*RKX5p>bkn z;ule-U*?kqlVR|IR=1giQH##n#KTNG#yF_PEt$UdTw_c7$84z}3|MoWjZ@U!X5Ia(!t^BmOXR z%SSe67ASPI>N9VhoN2 z2+!%XcZRFAy2z=^!^WL;1K8hcAeSo5rYqd6J`ME}&5!HqdM@KFI8m4|7zw$)Ns06D z@-of42X29FT=m6^`IKCKvzC4EJ< zSMNS>=1#CjF1YEg2NF*sayn%J7ncJ9O)DFM|Fw9|B(Ch%i4-~P)wkMc2S{>f*jG)y z(m13Wga#+1{~A?bW%NvNaBsik)w5YY_v|p7f@alD*C|4T3p`}p?WIR1zN{)y*jId7 zL_vueDK%agigVr4)^@XFNgcMRe%S?BJ~2$_=-GZek|YD5n1|!ww5OGp9WELirs;!P z+hHlU?x4PziZ8(UJ`l-{vJ0IoT>x#-IDe&s(m4($@coAxRR%WWHh@nWsFi?tI@MwDBDzbgioowUuSjqIB~WpBGyMeP=@W{n6kBwlL% zoEXU`S+gXA>{i%+Xj-bnx(zvJ^b~1bN*ZiazwjS4U%B-?j(F5D@Ml5Xqw)9gNQj`r zrS*z&yhtR7`oPRf*t;jvP|VU1+6wBo&pZsv%lNbKZnqF-LX;9l&IogNB(PiEK6GC> z>b5b_Y-rvR@NAC+r17da^bQH~$C$bFoqUKlxMyaa8S}-slOry48n!xtFIbjAs_LDa zo1=jFlUAJgT+Gu=%C`~%Xa5BCZ7v1!Vf`7!PJ3Z8Jj(vPi4rMAB;)t*mv7^*8%{_m zB>he?D9q!g)OV@l+X9J`p?Q4M7_l^Cb{GqCM1*BocwuLN}C3Kwa6fm2Jp>c1t{^R#%L_r-jUK1WoU_Ievzglf2YQm zD+-5`@W{K6DMeG*q;{QbvcX`sKJ#kYN3<)cn`mt()A1FVx@NPYlEv_WZx#fN%uUjCfft%^kN zwI(_Bx%CVS6NssAKcS|9Q{KOJwpWJd5^B;{=@8DryfHLK5YdfoVmM)1@J;2N!o1)f z6G&`8H))Z9%@ITVW7#};%&Y?uMc^00Q$jM?- zCoEw61z)R0V~ai0__{s=1<0{vS&{upV=h)!jMb28U61yz9>mmE1Zau#b_$kVV!!HY#Vu$yJ7<_3T44JLD5qm2YB;sR zoUZaM%$8n}J+p*FXN1Nu6#a<}ibL$N2!NMMTckNp`%WenJKIxMsqv{g+xv+ ziObudpU(DIB&0|1f$nW^#R=GcKJej`y(omL%Nt zV3_fT?sXPlCG4W@>%?_dthmLgLr{q^Tu`tvNy-xcU0HInU#7M4>$;+@WANnZ<@%_u zrUTvu^oQKR1!4HESDi*z>2?3}Imj3mmRfy|+(M@ii?UPrS*9d^U)J$h6f2u6eQ9ui zT65HP0G=Di=H7?IY#c>p!n4urJVXB=+JME%%P*ps78l)9UR7?h%qFVQ(Dflt-mxKU z(jYLuVZ<6Wk|J@Yq(UAid)SBVV4u0p{WAYNno?QM?{?b5O6T?kDAEC*gtwS~1Wt&1 zfAO5FyHKPgbyyPq+Qqp@^qi3t{u0 z*t>jN`{pXSsjq64NXe>~9WGVv%dVk=*C;gR2*!3qksfEit9;IzC*@S6=K>LL<}~~KTn&0a5^b4N?%Bkz!eK-rF*c<*}-`BnwLQwHA? zj$BBj1!e$Tja2B6>3Odqh!)oPiUs%6@PLb_unt$jTU&HuhqR?tPfVcHwmt{__M)hE z__*Mf4N+l?Q=81*kxLrtP{GKQ0H`@j&x7_$M@MgtDIEIb>v0drC*MW2LKhV*mwi79 z?x23@5)_>t`^q~6zIFXgbETpVl~}p_?r5@sBAvgNeQ8jCDzT#f&;wG9yNbD}P|#2x zs$NCF`NdG6IqgzjG#&qg*}Xth=O*@B9C|#ZT*Y%Ty|cr8z?L-IY09Tr-Is@GuOUj2 z9Wpj~a&P}fA$9;*Br{CUSTeY-cs-m zHm9B@eyCB&SaLT+^v+zGqt*A{{U#~Q;m^}f=b9{9NqTha>F-KB`OuxmwT@`DFY1?A zQ30r7j?^V6S;jdvoYH~re~xqb$K3h9YZAELH3E5fqgAQde+ z6)VSU`2EAvGyLxH@#*>e0{-uLbp>DB*tvl}{KJpVuKyvYm+;G*yOWEXt^M=$z4LYW z!Rza*Xu$1X_`%ET>(<@T$`AT_&ZgQ=CSo?LyjF96pU-@mjc7g|5gfJsVjn6t=19XRShU}rm{D#z7Zb2n zSbH?Mv3I_+cWC}2B5$uFajWk1{Ay@yIw~%0ZgK5P+8w8Wd}@B}By1tKq~YM`q`rM% z=kQWf&k__1w{nYhDY-woe0B>-@9LkBQ`VOYhueB5T*6=dqI1KOiv6k|wohOB2S=@P z?%KPDN`@X+4qi;HT&B04i)*`_{F1VJ9~)b`Dw}#C9gkjqk&#Ug^%GBn%g^PN4V^Pj zx6k(kz2MQqy=y}!$AABi;D?5qCW7Xf(=2ZPn-|{Pz=(ben!HYYY9*R~v*-?rdd>(4 zm<0bBh_F?kCj^A|2uiY2T5d}xS>Z+$N-ZJdJmF9qc;H7QijZQx-44D8tRTZ?KfiiK zyfDAP@bvNi{{G`5vz)X?$-i(d!LW$$0+z=QjM6TG*kYOG&eqe>A{IqKBH zKOg!pwiLD=WKuR2V&6$RJ}ks*N)m=AdOs?=Z9u!v)Tl+h4Qk|JIEOf<6f@Qb%SSOe z5Y!(i!6|o%+HCqx z8t)_A8Q%Tkr{;RyAAfc`NI>cfpGHsRXN~l4h}szVZfR+Of@3M|9`pe}r~y7Rfc}%Ge3dTe&=*7;#lAK!vl8lNJ%CHh-*aw;lKnXn zaTUufd%nSLzr*^v)(+n)Gpv(ce4(wL8$xF|=9Vbc(MWOc6F_v{^}O5_P2ZlBEN97m zGaSk5Q6+6}3d1&d+}WH632#t{Dc?eHKiO7J_%_peB@l zojfGA`&`_cW>lRe{?qAuvV)2wR}Xdq5(@IdPbJ@z&8pYM;yy9=^m1rJgwiP;`9#1D zbdBA(S~lTnJZiJqH6A6*yoM*nRFW$+u&?Utl6Hhq=Z!aK^0|;%;`xdrGR%%`tf8g* zuR%XPvK3O`*I*Kw)l9nex4RpDCHv&j%%`>wn5+vmWHSbGV#^V{E% zpG7uHr>Qkk6?}_huDahfnn51Au)=yRAC*>4Zu{jhn3_Vw@rDEK+Z2Vz z?aSc)FnPF0_3zV>+XhrYP^O^c@j4Z?q5JtJUOiuF zEaHw8KMF0%PvpzFY056Fddl52UV6coR+s&+hchLQ4?Yqx+!2OBdwy3J+!gO!6>G=- zO40}5M7pytJ0g(QrTxHNPEx^_Yw4`VLsZorpR` zDKr>+C-zj&J~hEZ)rc}#x2lySa#J-_V0oJ$Pb0Y;Qe+%Q@C^&7{(`;nhx<}w-Y5Lb zED`tJ*%HGt|86t8B>zCp7$*l6dxhAlt8_=f{8WPKX*7drn>o24J3HODQTq?8i0r4u z8iC^rXQQ^4<$L8rB=d3RhnVlKha(8k3{NKXWO_mS8?*4Vg)^TcwLPO(N)XMNs6=bx z{M6yvSs(*H)4S{It&i>pPr;a^6AeK_nCc!M_4afG%-g{dfVd2;{!}V1#YzP}>d><8+ zKpj3*^y~DqXtqRG%mZuF=fqC9+mF9G!u7#Nh)7vM8mN&JT9WQQ&oC^9v9696FEwn+ z4j$jcth9AeT$9S+Z8R|=Dlj}4+MSVATV8MU=25Q~Xye4j3 zF-4kQR`0K(5e*jP+y3NQ6Bbbe%7Tp2-CxGjrIzz&$|N(%4_6t58zW_$JlZ)SPSV-tzukEf4+r4yd3dklTbJ8CfqTI{=Pt`S;+Z=y@sL`mmaHRf>!4FzIk;3o#3re0154^3&+@BX~LO zv!A!bp&JPm^!T*1KfE9P*LSGZR1`=T!zRST$J74kR*DLwu3&suW|>AB@3>F$MKUkD zz(-aF`}Y$v2F=Z(6*T8BZIFjcPf)^eeyh57!S2K@K=R*b#Z%f#N_gI#FAlZTI}hFc z4(xH*-iR!T^(4NWDiI}epmrV$W4@W3Bm_!3Wi1KtYWg0!oN*Ym5-Egu;qflB+}*m9 z==Q}Led_1DDAD>g8!5!Dj z4zjnb6Q1w>GBIOQEhv{QYIefO3kS0W=?eKMyel zu*l|P{Bk%49s2y$GAY|llqYPew9iuP230I8{IC~%ESUOhZ|kqo4E*43G3V@_#!rPh zMFZ*Jso^Z$N?CXQLmXN>Nz6Z${{xZq*9ijes9|mlewE1DUGxZi-Jfm!V-rt1OQXAD z76zxplS|~T^%JrKn=f~{ueY1L7mZ!MBwzbwKJy6MJf}e_9pw56X5M<_T631EAT=bx z6H5WX{Hq&cS37kE&%hMwJvG15EiOS_GPrqeh~3=`6e_grvU1!etp=&% zR@#1z;R)2}jh+n)V_C^2S9dAGwFP(O8SHC9J&jbz0r|85Ulvi%bJ#>e<2!^O}p^75qk^7$@V#qHbn zhtAlS-r*4iuUpHW*k(~?$;#_<(e@8Z8-_CIC}r7(1NmGF0PBC2z1<~O#E{92k-vubKV=9#QO z7bt70qs*YfSX~Z>KIh3QF~2%jh+dL=l>hF3fy;EsyImXE^2`ZRaV+O(*WbxkoWu;3 zsq&V-xD}Kj`(jsUQFdSW-T8oUqH)BF-q(!k;YLfeGP+;p*@s+yE)jP>zlHSVNrsIN zH6il-@j%P)a%*t4J^v_m|FeP{MVumx`S{HPiy&>I{Z-)f61gyeo zux!Qu?$j1MwZgs&-6F<&-QA7pAG~C(w7s1ClmL1UgC#y6;4?_H4iuvUD{?|a{Nqn! zlP2<_0Y+Yq5L9EH<>u$4hjDQW`bvfUxp92q=m3WCI+!@M^}EFtqx)J*=4F55b8Qs+ zcTqSusp`ni&oXNE_E9TepOac78ga$fv^m@T1bz>M z-t#GWZ%e_Qm?;t#X2Z5to}opvvpa*Ug3B(mhr4$0Gn^fmltS4uUSXp4&$2iBO=GO{ zjL(l*L%qg{aOM({;+ktoJv)Pmg=Grpk6`=7^ps+5lfp76H=|H@>MB1C^_sP;qP+8$Wpz7v zB3PAEMbqLZ1yY!E1Dx`TPYMaNw@;>L^4dq7+(g96+3HsI=Vb9Jh(^AfF|`yfDKv{r;k^01fUaQ^Z}=Kj*h)amkclh~u-g+-)J zq(XD3go1OAnNl6+&4{Iui4UViZkKgur^#1L^@XR)!NbHw!5RwPo5PCeqMeim^;7lk z^vFpGz3j~PE<9bMH0zZf@yE;4RfQlYtDD2)+1bI0ogG0phMDtqWq8imCaPXP)$7ab z;LP#yChU?5q{h*e?jh{@-r~9YnXbX-MmL3`LU+&N+iFQN>zGgFYi3u67&gS!5`0@y z!nB8)x}3N9S}YZ~{=gjl$Qv>M)h;L9OJ+9erD<{=Eu}3c2UXeK67yUPKnD=31Y&@) zxR6mykLPv&)X0}}xO(~=Zo%UV(dUOUs9;FV z3{q_dF+q+=l6nh9FSf45UJ7O6sQTvJL=DMi;oxQc#h?RmbHDU}_Mdn#$BJQAq)h(q zh}TxLo#Q3;R=1F_I&E1tN4$9J+<754|9PpSZ+dlhcTXb%q-C}}FyL}+JpQ?6dC?@T zpfO!3mPFn3;P5@`LXB(8^HMz}6 z)!A>|q^*SBvoS1K*XK?4q^m^ee+?Ub#;i#`RaZJ_-iVHJ`ifn&aSL0$MQ#wy&-9sg zV>A+%ERj?MNZLjh7)PFlE-sRPzziwYrEGAnN#5H`Sko|AZ60BRFC%9n9;R^qv5(a7 zqYk%(wW03B zs^O{qB-s)LWB&gn1f+Ph3=d6@|iOowz< z&|yh-4_Lj&TIIuE?G5?L?&Df9!jAKW&wgsoB!=$>j0#gqVJ2 zyJVBlC{rdyam~bEg-5l?!;u$3P57RD!;b00;{7A1<+nEU7}c)+K7v*D6G>@HlCnA^ zN?D)X@r(oHb{Wav#W|kg6*c*A8DCGw)3139$!DQIdnW(vr=g0T?U{s4_Rn`0`FtGV zY9V~LZxaRcDUZhO#}*C4tF(em&7#`voTQ)ihS9+8?_lf>NPVj|KYeb^L(1GE;g-PrfYpRzzCUBYs{%Ktkkx@ zSm?rCDXzf912$uA#!}K3ET0jDC7BDZNjwFuo-FCyb5eG$_G`d5dq;+ce?ID(e4mYd zP+w$rINhl{HHu9@Y=>X;VhikZHM%Q{Hdh=P!6m9d2iGDuJ@1M-=g%S~?W&`Ip3l|T z(u$tXFBjlf{_iEed_aq|rO%w%62DkW^QIr8Z*%u?VPi(*LU4@h+{!xMa~x8&s`K@pATz)?`u1guFAzfb$(5@-2cf$UrL~2N1hy;Os=(3=lN^Q2TeQ6chG&nNTtj%B?&}vJ z8+;lrC3?Cy+z=f~y&@DnOb+Z{tn-Hp%|-KXEIi+Xo6}X1Yb#}ygYoFQb^L*@gdJ?w z_yAR<9{KKoHK_rfO*^(Q`zRRqu7CnP)tSpu)o$&;S?Sxc9d=-+p9vKGl2jK;9+IHP zICSwFi*aalASH3MZof3;WGj$e#jTboV;gN*~`~6!j#k@nMY$a;z}u6F4g*CF!p%amYdRw zUJeqV#3u)4Uf>)46m5^12#)I9{pIPuUr_zS-Gsj<_cGKpo4b1RV#Yu%lM>fkDmjOZ zi~#mi6u1!t&8lrbiisPtqiAptj-9n>NK>s7u{Rhc(x#GuYL8er^GoaKO)A*#Izm<> zmPtxA9r;D{?L6Nf%Pd|$N}G(D38bkAxEd02ERHihIq$yZwh>Grw8&Vr0fxHyRs=mR zzd=kCKd4GOXwh)5*c&5UXBlikk-fy7fiwWGT#>4s9npQSp-l$EPD)RGM$nOdvelz0MzZpco0LEqog#P9&e4 znL`K;@36;Czm*dNbkw%4G=Ca4wqYD$`CE%bT*lnDWiPz77s=hh64gGNT}m14qQ(Ew zs-CnRv~)37o;hR#bVt7MD`=D@*!|c{P#gSA!$sW^SR!fOQc~(EY~Hug7;a7lkutD~ zBv!fo6fY>k0EtgsUE1VfbZ#4NI?KokYabWTd~Zl3M$HK%S<%tWLzUIxgf7?T4C96l6Q%Uk0mvee1+fKtnhK+- z&SxH27I*%z9#{x}Hj)5%XH_BoVXku5S`lY{3c4{Ft-E1&oShGh15pV3b|rHN5i;F5 zhrpP>Jq7V>a(8@2YxF2E%Oj<0l$|gZ+g~M5rl7KXFyK2YeUH^YKk`W$+!0QNXb}A& z>1?F%S<7=#_10Q8u1dok`qpO3WY)&^;(Her8nVm{!;|9DkP<*UPk+(Q6?KIN*g)ur zCqo{UhQt9`s{&!rsSEg?gC9aOO3U;2Fnko6%_25<71(LDjHif{t#t9yQZva3{b64) z7OISUR+hky_$9(|zy$(fYx?-Kf)siaJ|~j1prl%;PI!W0xwaEOTpkinR++FANex+Z zL8gMR75N5eQ&0=b?&)|&lS+TP_@gVE7K()T>|;G}AyERInE+9)(2!Nc>%tl-W5VdG z5L<*#lFm}1(^o#I?Z9y@S9YBYrLEAlr}Q?6Lo+f!uw^<3zf&bPO|hSJaDIIdUf4eGDz1xAQNzKi(e++H$v4`TSu~_U^j;keamPDY_vZr!X3)^y7(KtzQ@Q zD(G@+s|1DeOqkgi?$N^eNh=xFW`(YLrTs;Ul&qevRx;*&Yf z{A5`k*AZ-6XP@LJ%!;BP6IqO^ZTZM(RNtHG$d9~|)vEs6Q?COh31yuazi#O3@f?QV z;;LQ6thG!YZiY>Q_R^o?i##>CSreBG8zt?x0=D^4S^AnPBK=#P2lE6iJ#4b{vM6(PLP%Hby9X(M=>e%4RaFt&}*MOu$NV^vjZdNwCh zZ5`QPmM1h&UYBT+x4iDtuanyuG5`8e=@wdulI#1*O?rNVFF?joM82L#WCNXSMbtOO zI=>RlMN(d!7$mp2($>d!og}PUX;TtCt%QxLg>|6!pXTvM^Hq``!lhdSs-w1@7C{mz zH{kGzWZOEi`B{a&`}(xHIxNrN2)Jd2DS6|Ez_dExpIA5=`VUHLGdjZF=dQNmiv6p+ zM|5JBLW#LCcSkwd_bpqaZ4NPymQg~NSsNT!T6%BW1{)mSSjLw>RI7NVMis1sDdsn| z(Hs)iPES#8->jJ!n}4DI^rtL5n>yw}0Qok*dfDNUm?S$T1bQz2t#g_jJC9GqZLKfz z6$?Vf$KSlnlC_~Z6^5cHYI$t76xec2LD5MZtVmer`o5tXA>v1~CsxDscaD16rCTA} z$EgKjdP55c&z8>I(&E*^j-(UNe|VOVsd1r4t35(=GFn!=&FsF%NwJeMG zH&A&Hs3-~j!f01n{_v*K&21Me58ihftY|0NweeTzIl`--^dTXgQ{1j%%=42Zu|lp*4hQe zIRW*|BMxK2@ewg~;ibeMW#M)_OR@?a?PS76JcrU7)t=?4=>ik)EikjGYI2XYf3cbB zy}C(D3*)=!$zGr;#*h<3xXQ)_u5{(6t^(5)H>NzP7fF@ z;&+yaDM*0R=v+(`&w|uJq5F71gzJqw`6dyhLryPl(4+(G6rp{J$cW+AtoiGJqB^`i z0o438sj}J(kksy^0Lf#Ome0i+EVqdX2^p2;&Y@}07%KQM76wFhZ>1I~&RwBlv=AEP zA;KayRT3a$(|4-l8{fH^&nF%OIm5i@jM+s_i6N~{qL5TOiEK1V_iRe_C}m@U22C|{ zbUNVX)3>)5tkwwTkhuvpKwIVX^!5Fr8pN3cW^H03%a%UoL2t~uC(Wc&^(bvx_ajad zWEae1V5fAYHYird+8!{-W!@4nhhw3@Y&!9SHaSFe)BDp#QuSaaIrv~=fxz4%e?mVX zK$16JP2Ft4Y&1MMw$p!VLSol$+FcK&I2e_O3;jss3axic7O7rygARpGo9s0MFA(^jp z>&zQSYWy2duKnBZpNo%49QArh*?E(6hFaV#&q)zKrbuO>C+h8rQuk?tKOy)E=I1*9 z-o8?oPl7vVe-WOuclE(!?FtoNXtemmq2Hxt@mU-$>YI9&-lxho*P@}mG`@Fd186fG z+4yn79;UrPYrNIicPaV##>0A!!4LuLrmF@JbcTVYACEkuPqm3vD|hm){q0ly-AveQWhTfdzm?f+ib zw$NFZb@6E3zsg4OOlQ%#wfY381o0DW!(vOWPcMfqVW?oG6ll)4I%Sh5FQRkVKAaa~ z!bN#K!Y(KADL`bHYY3YZ$-;T`VipW*&no2|MwDldzunE6`cX&nA<4=mTs|yFd`BZc z{el-#rBi8e%lG^paI)iP8_@ib*!uQk8nFZIj1#cNxP!NuHWfOAL4;bAaM6wLwy6n-SHm$)c4nX=%5>_}Clw}>adz2wmAN41#NOMZ z8?AhZqNT9=XsnH&EccU7fh0OQ!$!q?Qhw2Xvy>qwrtSD%{u)(;`V7tq+Bb^QyRZH% zN~uVuVzTcl(b=-lwB|5cg{ce16Qh&EhDcPAir$TYbPo7GuzxTbb>OEwG56(&b6uPq z2#{W|;Ye{DvQ=8Gc&He@)XhwNDpsxfaIwZSDrF~OcL*if}%9&tg;~*8?1?v=fJc#JOfmxrs_dM zoo?@NLHQ!@sp$y-6ccnX=|Ln;og|QcZJh`a16>^2FNv37h_g@0qDTy}+=7S1;)q4% zPfeEa&oVq8NrVXU1iwbiMtmV=Mk}`u;CWvVRE`6Z)*U`Nm$n>&BGq?5MFalOlPMs! zFw5YmlDTLFeU?F=iXy6H4rc?eX;RH(t!()y0I=~fy4iSpji-H`5h>MTp#bnlcLfgl zH~d>5oZ<7rfcFJ6=Xrc10f7=Ypn}V*yQe!kW33=YXO=s*`JiMXZviys!tENSpoE2j zH7%zYRDRZr_qmN0v}ayLn8lP_vbmWRcilYg*Bq>?<5xg9Hbh(Qi+k-`6R<~ z(5EOOzeS|Ow5+340aM1aP|UzYE-g@9SmlUJ0b#t6Irs{oFC<2WoH(FKf9VGDSO=T^ zZf*T-T~Ol>W50hdohAV77&h&3)mXREkD>&Vc(uJXko#wpX{7>aG9}(qC5)TJ>}cFJ zsr;k|>h1P3g`Y+B#$v7luCkr8RmP}(>W!wAS7o}`NGs8L2m%lB*F%HXKM~~%wAtff z*-De|=c>eOV^m()#7keR0`LLFw&4ylI@rPGJU|{33X2QR)cJy3(~vZbnipfN}k zfDbIT+0kMF;U=pPf}ix0slgU(Y3$Opn8F&)pML(DfOgI>C zZfS4ozK(cg-2qdY!tSV*Q7ad(%c4nC<~KtuLQ-HgR--<77Y%?pwJ#n&c|lvZJjk)4 z$?P)dPZVp%3IE%Z<$PHq&`3x-S$Ssuh$*jXV}_boOEvM0TqW$tGJG_plp2Y-6P@w< zr;qe(=1n0i)yLrpeI$fs#P}l+1U)iYNULG-!PXoPaShPSF7(JhA)ic<(s;g}A3|ru zNsFgyqAP@k)S9=yAg@OUDOc4g!`Ii*%rzkr%KH?e?NqLaL#g!8pw~!DHxNx7l+$1d z;0DX2mzqDLIh$0gz-*IN)KH5iKi6i6jX%gD1mwqaRE)WRq)8(PrIsMKPFlY-S>L_M zfxU&Ip&G2{7td%pnxeV)rKuUP6a>Y@BmG@HWN4!M26X|2yx%7K<^!1^2JA(T88Z`Owbyzy6mnneX;@%45u=?A@(WzEJ-kpF5J>%PEh~{~A zm{`rpA{Vn{)amp1s`MB`Ya=Dz@P~>3knKlsz#M8H_Z5j{I{ZTMyq0W@t%V4He4!Xw z*|fN)wB9L>1p~ok4Rjth0q}OS8OEAZjCdp?1W1`M|GfD+f z%uOY`?-eu-TP-BUtEY{{Rg#pIa*Hia0?%B3$s|Z5ak3Z`I3nz?I=P9C7-jtzqGY(@m_V45=lCcEa4B zT(#>Zn)Qt)6rJShx?7u@?Q%)GZeBUw!%y; zn~g0qV;F){g@S-2eEO7q-$yB+eKLlwRyo$nq*XVakw++q^X=O^x-Lg#d3!_K=&`}3 zObOqOj(k%%r+!HI*B!;2B!2Hxrc|I>b}U->UI_myT;#{FTqO`jyL4=x!Xvt+dE+@K zzVneKy_${CP{RF78j@$jd|;-W)crwlCef2n0a)r@xG^qju!coD@Bb;znrNrzMw)RQ zUS|IMJI)!7hbRDIskl+71*3jz^@dvfcoM`_iA4J!0mjjRUPvjmhizBzre)e!9m#S@ zx0iCbwmQ%za9mT)~8c z?4N&Fw3B*Vs6>0F<2YPxlgEs zo_7fSd^uqqXGsD6*Dwjq3P723N;Rzm-}(>JmSTHY2%cgGe!98D=td$(#F=Ny%WdN{ zo$M+XzKonRxh%|)%9X?^U51&!bYJwr3ZSIJmb?MQR60r5XV;0*{zfuJo5%r!Q_uTg z0HZV+3$|93JDp6c&`Wqt#eM){edvH|y2>-1t4ISg4wd~FQV`FwYB6@0+|Z+1DLj@S z&s-=T>%uUwc*eiilNRfDVPudDgW-?-Gd|G7wb(ybf=%A_CCrUGx_ah!Trezb4gKzi zUb~`;P+=t2;k5UV-{r9cSbFXQ^+sW^VQN$!fqr;;j-bBu!KZj7(mcd%aVnTx;GwCv z!VE9zr1k7$0>_A3<_#z)AvQ8f(27HUY-c*CuU6ZCvL8dPhZH6kFZ0LYpEvY6i-M}v zO|?Z3vO&s9+V3p0Ja5#6nMUKOk_5{zbexGJ*H1X8Bh%jA=c1&(;b+)IfEpyehh-(3GhbSY zI<2`gFQx z{USWD+SUA@zzfuVL5O1p_mv%#+Sr#o1f85*mmm-v^mk>SQ2*14m`P-|H7G(wiQWp0 z;`GXjq;15IjD(9G@eC1W@{zNz$z7tMeIqDM9sl~$%DGcx{mcC51ABI|t=+Te`BYk^ zNLod2b>E?&dY34hFm`!Yl?`s!d3G&Yx5N4Vma(p1&*y|39V!nUb6QWyHl)xGd^p?a zK~0)b3(05Hw=0id&hD`VQ17k@zD(7J9VE) zw=gorNzTp$6OTpF=bewzH4IM|cWmUtO_^U6jN1e=#JuZCl~^~wy6YIXeNOEEvjFYR zW*{Fuv(K7kf&Bd3-ogMq95tOsA?jEmR=k0KhMeMz(m%tAk~>P7VrdSRV;D^DuY6Ar zlD#{GM9@>YXqO!Ji-!9CbwG9R10gBvO^+D$Kc zIEyj5#^nCA05wd2eD#4($Li~Jv07zGz1eo{KwS)R-ySNs=EY(aLZ{y<6?HU=eZQlz z2ET$cld1k*H|97#sqwwO!!21dyG6X}&>T{Gr@JHxm@kRVqO0k`jXR;idAh%!i8n1# yr;;p}3`(~8^G@7n^SX|{#7BZ2f|8t?Y_;^)fd2wJ<>E-eH<>~q5`Q`QL<>mhI`TG9;>h|I0?qOr^^c}r>e13R({%_bj{POzz z^0NK!{Pgl}WB+vf;C$!cVr&0=>)-j_;pN8u+1mC=Rb%(t+nc~5!{RrFD27EVhQ-&{ zw|BGe!l$R_wcS(UklX*X{+*v+-#Q6%*C?d=txlx;Daj*3z6FODbj?XBqZ1|#Xx}3*$ zbM|cSmtyPW7ZQ;aoKP?@IJPo!3xh(IS5Q{p*siH(Ra9DYxm-5~U7K#c$|-8tUwUx& z2^+rYvj;AnY(BN`B-u!yIKlbwSu=V#&e3?D_IPvBF zd}QozQck5a&x&tgOn}__VD&}T_g|@*`F$hP)y;j5JEje($6FBHr>h-j_u$j#<1~>~ zV{@0S$Mv;h$>Tq1U~7;0`{~7{)y@5LX!rF-<(J#no2s$r6tkU{uF?G7r-S~$CZB&* zR=b^7E!P_z-<|fLnUwx}&M^mp4Hs1pC(yImLYb&LtNe$O)BM$gS3SG1hL&F7 zwrjHY*d=#UQ&xu=Fg%@Fm4zqdeok@-@hJLUZ8}pKcAS|wHkg0F{9(TNeITln0>~8x z1|9Fe4|aMr;28#n3Pw>zQp0QMq$65S9n^HEq6xy%G|*gaUKj4@+GoDG|3%Jer>YVr z1BXYGtRN+YafJg$;XzPEaPCHxA%K;Y{rc5;u;9Dpm^V{nc|*tQ-HTa(?O7_W0^G<5 zwbt3$j6upyn4PkiWp3kwp>I_Zj>@-?H*x#R%=MS^fnPOU(|-#K(W0)6{6!C9DLbEB zE*qG&j{R`G*Jnr8Uv_P|dQAbbrKNDc1~8Bp7Xlv~E@yG_YjZ=^xlfiAeeOkV8V$uA zr=%wRSjr`=v0kyy`5dR4q4Ubw+z$A{H)CDpxVn||e{-6hCRSP*#r+>i2{nUm{_wAl zw{{t?8FhUZ5|fS%ehI)ctxc`7dRi=?Weflix(`d!DKc)5+Q7z6YL$}LnqQns}@sue3h2*CvjQp3^ z5M21$*uDJc<&p3!i)fN3j$egn-^n&6zVW~1JfOe%UJ_^LKAr%7<%I{oe6%bul&bM7 zM@`b{I-ltkeB+&Kcjcr9FA<^mLA|efXmviu7VIb36#9jM(D&Eg&aBt6a`7&AwH3sb zx|~i+1Y<3xX5@IC`$JkaIP|Y6-}b^6nb)b41Ll7V<}0)R>Q?*m`x>3-^@-qavR7$c z>bvng4QJg|sgeXCM#^~m+ncxPEr$T4@qB0GVYXilF%|&47e9@DfsHcYe481O{fMMQ zD1z5&3GhXHd|=$4_-Pr)5(aa;Vq8PE|6(N}(^vANGoB$tfpx=7alFE#_(RzDRUCZK&&_Dh;z`U0LIbGHUcVSai9Cp#yS%xxU+9ptJp~)kVyW)joLn;lN{a)(Ok@rR5AJTJy4TUT?uq(N0fy~9Wd#J8rfYR|og!?3)k-%My zxa@UA;8|Psv!XUCUyeq*Kc&Q*e}4PbeEKIOH%ugP< z-|L}Q$u@sFXB&7G!b0^_T}a;gqC8rq=czP@H9pbINyNN`1=FsUsPP6{s@iE-*W<9^ z-`0#DHzbWf({HlehpGHw^L#Y-T4mZqs900!i7cH*7x0m1A1R;26DaLmc|RU&E=yk; zrm5wm$M=^Sjcex`Y$5nzLYmo=63?L}e1oUahWb#A%05Zn^7mPYm?3GnxR)tsOb)>NE4E zfzz~*FG3M5*f?JX7$B}cy2{oNx=L$H226X6tq*;5ZRWfnTI;josv4zzD#dr#I#J9< zGplgo;DtN45=%X#$IID9HnzsL-czA6jjwA46`?9$JsU7~_~tCP`g&YTPma$VdmW3{ z4=Go;?s@2l$8&@ij;4u(GIcW|_a)8Ctj(?OVq}L&AquAG3g+faV3*d<-}G6fZdkG* z!~i7KNUdTfJZkqkN$T{GU*!SkY00wG@Y;liRoZn+x@nm}bzV#+@F3Qg>d$#O({!n( zBS$a+>LqwxD#`j(!Bg(DJ@T`|zvN=SNyWbY^TlW2idCK@?mQ$)8jesw_GHT2^yc zb(RJeDy=dy&Vn=-7%jxvt?kKZ46X8y3)V$y_M$J!ZyS$7J4XYYPnT+;O5eMhxOuB| zLM^H~nn8)TMX;TD$!veel}9Efl&7e2TZ;#?(o+l^5p>)O#e(qHDk+jiyLCK>yYB({ z!$S=9Dm-YAtlQ3*-*$1848rxY^y=<8+_Ik&rOH{(1imH#)1`>36sUOtRv$Gdj;keAehnHkeF;YcsJ{^Z>RQenGfm%$=M#R3k#~E_asPK<~Nf|zlJ zt+)z{F|@R*CGk+bu(_?r6j;%J99}$+M%al;YQ@|1R8g@ze?qFT`FdA}Ce3E3smx_Q zvqT+uoh3O8OuqZb{!58j;ma7~kt&W5({f4VaJoELQ6Kgt z>d11uu*+H?{!iV`oII>-MDa3O^YT$1vRX87`-H_tGC<)U+In$zm=&rdEXu0QK{QH1 zUq*~_zBLh@_C0*R-HZN>(0biu#2n0=I=iw6e~Y6B0)L7?tYo3JEtS#&lVA^j%AtFY zJR~sT5?DDxSrl(qJ1gAIFsowuvEkPu zUUNQojA7jw2zn3%NT>EwETj^}JlHacTJyT!WSJ$kM}txd&g*eb8t`F5Cz%L*ajjI` z02SwM=d8d(cKLYRMV4;PpL`vFX?XC+!3O&eGzEgB8(Rf6*h)td-Es(0ni_Mx@e{N0~Y(bYbZx1!BU%wRA<$RZ!gtPvMKFtX{^ z3%X}IXn<1*)sP{NE+oMA57aD(N*WK)uYaal2++`_v6$u|I5_GoU^@efTUQfYsUpof zmUMz~bHfNJ6WOMG39P@vglL+psA;QZ-Hu_>=I4Xu;8qP(bRx9Y@*IX4H85^74AKK< z4r?yrBG*&@&*`d{`@b%%5CP2!73P|<lv3VVZB;M_9d3!M$@7fgIdbl&gNxo!^ z>T#E6g?S_Dmy&XrTu#4d9rm8ZSca>iH{R`=DpRxg=bzN%ves>>MYsHg^$O6uti=m!=oJhsM{h;IdLCh{ikg9)N^|vKcWnO0u%aMTP%?{|oyMd=Jk5 zEAl^Dqoj}&5Y4-NY2rl{*V0lkQWLPsL{Z_RoeWuMcp*qbBR~4zb!?0rM(Kb4{+G@F z(frTh|MvIp@PA|fP3GO>|C!AH--$j&5{VkTT@G{is37!t`6%2BNoJVzdvG~)T6%p0 zIo)MmVHkkHE4kM&0!9|W3^$&=G)Bt&Hd`>X89s!)yy_RfGOi@u0|P?9Oqwcp-PuUnI`htsOsRz&#^;nhFWqLn?R2d95zypT|Gvn3`%;$3a{6k6$Bh z3b1I_f}?XqrJB!>$e`1QMABUpYdGyRd>#>B>grgv`W*zGef}xxm6=fx`@90UYLwxu z9CMAx6og~h{vFU}F8MZBq8W9H;2p{1JlFd@T%K#{`7CpgTMw{Ht;h%=NYHIOKF=1X z^ylN@Z*Q024-7Ffyb>6>Tz7}h@q3usY=;noFbZ4hj{5Reak*H{EaYUy-P907!Yp$< zyZlyaDHfy15p~9o+k<=%x{n6>NG&R1bbnf6XdlOrkYXA6OJJfiOHCB42y#_0&WTg|tl`#@K^*%I!~v z^m;29H~vKCbpKNJEOu|>X+Agg8!1E2)Dy&}xlanO;V*f-$U|xVy;M73D0M~H za>MSmA#*%kq?AjUYlMSBwW00s?mOPs&>m`PgDoyu@bC0YQOUp`PPksY3%wtw$EFFc zb~9{&auuzftkn-$f>pBD7g=JXCkOcx-s;8S4-X&H=c)Zn&&xgX^Jt(Bba2SGyW6LC zuID76rRw$a3Xc?4DW&%K*3LS}OF|Hnh+n|FCa*N!e>o~^0Gq6T%UfG51%B<9_GhY~ zc%kV|dcTK63MY5tr|rvgwiEfJ?g?0dI$_k>|77S*9y8%5czM6!Uy~5=+iw=fqPXCM zc`Q^&rS6Q_oL9^p#l6lq=FXJh4n7z+o1*~juH)*gQPwWT*q$_H3n9(9xo1UPR9t6b zJq5zXCPQ#Q`S+DfSUV~S4+A({R%PaA_7C~)&wXN(@V$mgn&yNegOL8%2rQFrFVZ?e z>R4;+5NUl`M*1Bo)yPP=@~a}I;DU&8NFJ6@-jN$RG|==s`U^)s~<6P~)O)+wJ| z-hQXfeeoHnzk?I&Xo;!FpCPib7{WDo_S4KV4pbLOUFm#h`K0N7 zG?(3u4*1D4QGHU?{+kA!p~+tpyd&VXyAnD)%;t%OYGJOYo&SwOoWi&&qiLrXlYoeB zI7EFCiNRyM8?KJDCuFrN_hU{dKp583=*91eyl;cJbS`YQ^KXTE($&t1KoFw#EsacT z0Em0pT3~!jl?a^4a6$4hDEsZALU<6pa&Gdp#YYB57~Sx=?o0H{5GpX3FW@nFZ1_x5 z-!KW$6xk?Yj=uI0kz;vyXDsIUn8(M&mi8tgW5& z)7r+-G|dLO03BiUezCZ_Y@IDKJE8f*{M;K#5Y_FXSM&gEs47yf(H8LjmAW3L*a*ww z!IglNL+)=DmmwG#c5yxR`+|@h&66vWQNP@S60pC$Nk0=|x*%S989UB*jraQ^o_O{y z{Gw(15Z47n50tg^J^KU!NW8cn=xl(M^M@32#4$KQEDpg`_zfjlT>MGhp2Y#Kskuzm zG4^=KP;!QCtV70op`9PJTS1^1`w`*1tg|&HrE@#ZW=V@)eY7<&arB_%aIrmRD(*3e zL7C(aiyNYym%KZcX%I1Qf03gz4!o}qzDC1)LCDzgPG9+aZgw+o1g6%iwOrxLkiqz% z*x#KEELxLy;Fj@3#0~oG_nd zG9=?U{HXv*14oT#0VII-C$n4kRsTXjUmD*M4Yy~pP32Jml5=Bfwj7ruyKW)n{$5Js*&BLJ^?ZB-dk z9kHk_{V=()2_@-dy5*JOU1KxY!Uc&*4SoH0JvU7GfZK0blZzFV{-x!>R>Xs@KDq?! z(a!Qk3gA(E>X>(sN~0wf@v!hvG~bAT;@ES!aPoe};^8>Q#bV1hT{JrOPh`~<#OoEf z#vV`qTK8614Q`puB^SReIv>{bq=Q8TkbT*S*0Hn9e-c5w!UoOO`dWk~4sSIgL|Ve2 zH^rX4NnMX7z+(r%t{_MF(R;{UJD$H@vCG}`m}-M5&yB%7$p(ZZ6O{F1&6V4$yb2P? zl$Y|rh}+_tF^X3z^b7BNG7$TN6F(hEm{``I>_z&065q7DV9}wR#3aTma9^AiWpcbf zwkO;^hGSeQK13-sU~jWLL?g?PB6`5%_K-R+J6^hKsJHkk%1&o%id3u(>Gc7U=#L2C ziS?vkY0O?+-al#kbF@=&af4{ahSd?R(N z!DA2SHZ9jacx!+Sl zCG@T})BUgM)hk)=amr$SBp13c-aQqdx}5qg>iymta~p;3yfr0h>2(IFy;2RZHC{i2^3L4r9>S^o>%Fn7f!UbG6#3MYa3j2~AN zRYw5uz!_4qq+A?w9)fRj8Ad#$k@3K@;fR-=oa`I;#hz837j>EOJR35a``tM=seBbgN+-8 z4=~@ok|i<_9s7t@idil1`R*j%{IvBZ8cc zge|Xcei!7a?lFJRB7v2mVg@7F^?y|ItK@^f_8}Q2g|BW(&j~V7&0(tCv18-Zk9m(K zM2Gri-5&&VL89gThZ8PvL3o>6va0CLcZ-&DMYkjT>S*rV*;G+Zf&9I9thD*0rxvRt4hIJhBdUii@7yl7 zi9Ws_x?d8CHjQ#ksj8j+IVvFH?7!iZD^+?Di%cA|A+foWvJ+rj*mCYObibPy_Ip4y zWH2)ME+@iuy!drnAA2aDY?~BP%ez}v@&m|Dt3+VNL1pG2BEnyI3QSXWSnF`!&J!i& zKXou{y@cnm0Ek|ETf{_6((X^Vpl8bcz4>Re&k>t40N&l2AL9})O>}39qdOf_aPcli zJ_Y%ft#L=z^U2;@F(FzSFA8zK#(sZ2*do}IYW*rMcgs2N&svco9>+N%~GSSwr`beB28cO&F%Yp#AID{ zS&2ff?E)j=hq(yxqLzx}A}5m#3hy3=zE^;8YE`8^v@8KIbb;$?i(7PsvZgF`bkxbCd zQuCL+@CmN!(?9y;4P&us3;fA=bqXnc)$ps!p4_UcDq+XU0A;eM+5>ui>?H(p@dU{) zDtF_rf4PDyfp~%EVaK472Y2fQitljSpG>T_1Ma@AMrG|;`rRYiug&8sJm}C-d>~wu zHJ?f5)Sr4_Zo$ibdkwJLu@bw6wq2e=y%LZYK z0)>T^K7BEo_PMI$u;oW8WisMtCx9*kuT(4t(ru*k-Q%F;)7@F%{9J_gi7N%aZ)Lzv z7pm{(9ETMZ`EXN03c@KNNG-GPt+aAEcSB;I9;VTKwb9Tj+f4*5=KYm9j~Y}NMupB( zZyQ<5vvOn&8<+T?-5dUfdR6-s@W|vKe*3(6WyXJ=$X}%epfTf5Bp3E@y}8XLUdnM` zbTYZ%N04grY1RX42Xq@sE>MVM$9SO)5;iU4fCox#&3LTt@?Nrr`I>LAhdVh3-{y4x z4$BR7?up5Yj6zO{ll(XDaDJX2kFh!S%Sip9UExMOvsLV`BAm{5@iEc&R7Rb7mQk#4%ztA-uKb>WuWu6vb_W=>6!G7+Vk#yX>T#~4}~C2nx8e6BnBQi(l`b;0#? z8DuBSfLBgx%yu?#ld$XwY^IG}jWGY`)*flo@Y_x=%!d?t1p#BDI3Tq40smkt*$ZZU z%Q1wghZ>#N%K5FP)(q<2a{jhLC1BN0N$lTV*R&X1s9cgc_|uzgn(juVqr zeQiN$I}_y!iS2zgS9J$RX{7QWffsOIwZi$XGLc`_8mc5?|_+`eRkGjCsqD;|oZ z8#VKQK6K)#R1zZ8mcFTL3a%*sqdS7HThxPyAhxQnOq-8@>gKJ)$=@ExL_5915viic zIKXqr((;6~&h2IK6fKeo@gTxb0Ag(k=cbUyN9Q&k2-sgK#6j6J_82xesRM7jPJ#>p z;X}1uTrJ0fP!fFwA~<5tnGy?oIAN;*>+B%hldXG?ed{asI}7H2aRC_IhLOS8SHZ`Z zPAVX_G1a4@ii#ifOjtNU$HRsz5k^Sm$ z)V}lTYUA5#WTC4dQR#HK-4S*?ndTC=e?NA*y$LI{e+W#zN%0mys57eVSH+bR z;r=L$1YSQ6I@ZJ{K@yOK;fK{*SZhbStLlT$_@*PES4a4M+1r9&oSPA)@il3|Y@*hK zv(Lg0e2n&L5ytkC;B(kYj<*i@ou|hR6}rEt2W2uZbmGc;Cr2SRo!m8RHFN0J$Od+1I?z>{^uHwXgFTpoOZij6` z8n+|0^>27h!0DRc-{$Uo7y@=Xd5^OK3LGoxj9*jF(gzAHi@Wg#4roS`rnYu7Z%SEH zNd#4}JitOluc{!V&&%Mn8A{R<0o>`nHWcr7Z*>%Pv0P3}Zfbzli7*&MY(bFpXj2(J zT}cSnTlPf}LW!=-eA(b4t#NygD#P9yF;Xs?Aqk9ZWD3uO6YK^KbV<&>+!7zT(ME!*~w-L zt^nAz(NrPvIzlfwJbaC|M3k;y;P|8X@Src0GG`+WsCeKZ$p^cTf7Rgnxh1Lxx#kI% zO1Jijd51U%UASfHn^|pVK}AKyhm5@rLNzS6q*&YU#E<`Y=VgC8JoV@4321=>cVQPj z{zolnd4Xelo)87n;`yiP2*V56Q^dm*W8ch3>iZK_Ca>wnHG%R$lQ2?+WTtb|IG4(@ zYk2IK57dw})cng*f|n4}pt(7Xogh-XC%?G;&Ih0u>jnAbNh7I3ME9dy<&9v|7yg7? z6f>O{pM3mxxj-qQV%tLBJF7^sa1vs>sRib+-<-F_Hn?dU;d4?#by6BSYUJ-bmdUh( z+tg~DCky>TPv_#homN8N3n_%+!-0Ry6gg*xrJT#M(dA8rX~cDo~YZ>uBdy> zLYF1ql2?7EQlst2ZW&W2k8_$G*C{Am6C_LDh7CQ%Lzi4`Sk~gYv-Q|}2AYgxGjUo( z*zd9A?rz2dY`;duByhI8bpVvD2R4G2OSR_Q1~sH!BHO-v`lS( zpC;4$n;4bdUZIE=Z*~*=@ivGK0o&hh*aiWAa!anyJG2W04x_74v$-Rx(fuc??2#z- zQ&j$z+-cl#<4lPPWnJ-1#9Iq|iOXiVuAVN&#CpQj3Ts@ZXj2m4>io>a!8(+V&G+Mv zFyDU?d~svSZbIM&BZ%&ghAcVHk;wOY4D~=FO-n;od|xu+Usz8?l$%<8rEeh*@piqA z6qog+wfpD240u7l9i~Xi!S;Ue6=+lUQL1F}QMZ$9X%~Ubg~D4@p6HCY-PhxJfbEdV z_@S9#Ww9`roc8pJS}zn5x&{iR9gkUt5TeN#QKJ&XWzLE5wd);XJ0hlsTDPBiV3QgX zru%ZQZR#Bo!^KZ{#`;OCp@9yrM)ef6`(|S|sX8i>Ackw%Yb52VvkSt@XtKj+bs6vz}_L5{;2V&sV6{p(ggy#O`v_4)HR@}S@MA{Opg7nM)1_~ z5VDk}?B&yin#cMwz^&i`@3&k0CnZi_p8|E-~Xkth$!j5R;B$n*Hpn24UIoP<)z}5kVEr zllLDPoM7QAZyeg`HhusFYOr1@q7|(?;9D0R$^E!n=2nY2_k&=udH2s`e6oZ$|3pP5 z7?S*Y)-4^DJQ>&de9seusciW9U zqDr9&CGyZ*2gjpY`sb{lD-7ea>|uiDE}$!OlOD+}k4y*X+X_MDwcO80lECr7p}8VH z2Za<^s^z^jjCoTEfMq7l!GRKh0+ik`%Wi? z)nyK#dVjgAh1{!KFqRiuyHX^ViC!lHq=SwQczwpZXU3SuNOXvK8^PlHHS)Tn0E&y0 z4^(k$zfx)$jf~IpUD~D#O=rzL9B9oDA*+Hbr0s7^rvys}Ua6jQE}ihKK&moVTUETA z4KlnBaFkOp6mVOe_Qyj_qESwz^q2>EjW=r#hc00So(Il9OmZJM%5i8QVl6U`$CMtswS+8!wfFOZXEg5 z=C}AFCoNS>wVY{QgqM$AP||MQjZ1Icy#y6HGYUU~4B#5b*?-CN0I6p9jUARvZ_ZEL z*+#FAjjiB`oyTC5SKs<9b9+XVRJ%&Ja26fCkq=st_|6ZWitQFa!u(AF(pg^uBx$*K zM0%l&3WZz1n}VCsm&+5JyR441QzwyLqs&EsOOIyY_5E{xx#24;7zntI59?ySj}ON_ ztvA3&>{Sjmpvk}iBPct5ld;#F5==F2ca2XG`Og_~9GliRY^Z6BLc2SR5Au2sp}1qX zKOW(uq8LEv&e{DLv~q6h5_}GRNE`BB6^le?zxSX#M_-(-|BZ}EakYw9z4-;$)j!y2 zy?rGHW3+s*rqG!j-q0stsA5(zL&5R7G>aM!{9V>#X7qtF?pP_n873WTK<+B`n6}?D zRyOZxPV%(JnPL@gIpBlgA7G{E%JoJ0B{JymvWXwHhbK`VPhBm-Eo=U`i|~hgax;C6 z_L9MmV&fgBz-cWZEYL)-P1JM_WV1) zBcKqm!k>O#1$^;AhsUx1ltc!0>M@qp%N0EWZkFr$5i2hXQT|p9CTe%S+sg%WqnYf= zpDKVhr!Q3)J>i(g**IzUZ*C)R9coa4{eI-46C{=kXLw6jUjCu3zE<0>=?AzTwLX?h^(b{FN-EoaQEKNp*0?q_--S1;rV)DhXxt#mjdd?f6f5LUIdZ4KJ z{N27mK5=$gSo|3$XrM)bmcM(q9I-5#UHH=%yZc7zcm3FjjC)5*YNgu<%Kpbh;NsdJ z_J1LK8&^R81AV0Cz7~SE5=tZ|4z#{g)v? zL$&+KW7{@ZyP?&4Ame1e)J z7S__U3vY7KNvx~?`>I!2+#YB&yoIXVHA)1zH@`j&t@sEgS) zexhMZ4|?ryn%GUVa&bE{^af&RRAwS+a&3P~BCG!;#g7|ZT%@O3Rn2N@`G3@dJ!FUX zjcnpRk$?s97>so(A0X-{papbkZ=RSf zfeHfV3eH#SzJX>^Eu0!Y9~njVaa+6N9HU4iB}S{Lte*MplV#4vWLld6=fr|gsrw9j z&jg?}la16MN^G^Ws`@Qcv4@NS`;oMl^-LLh3xYkIV;JdD%|%Y`|L zz>GA)?jY{}t$d6lNKl{+XWVXsyq7kcGN48DA>f_9Cms+5ypsE}LA=e`(4q}D*z^m* z0u-&P2kMn>6`2e~!z^7~C!}1XAbh$mA$>cB+6ZO6Ad#yf{voox! zK@N49Z|40?+;;z-0yYZte|g;+kmo>JR!DRuK%kAI5wkgZ=f_kx8=ai2M$9e$M?Ptz zjm!YOL`G|oWu`S=A0RN>!PSkBn?)B!pTtNg?K`|O@_8nf)ez9h*|G6ss9@j>PB7$z_A`1dU9w?mw$6Jsz4_xz@iAF!gAJ* zCl+cKG6kW`rfQo)coY0SiF^6`F4lSUiIO^*xvTatOpZXq(kLz4QVOM@Wn;5vMv#p9 zi(#=tA<{pOu*dP=X@3zxzmzz}%XN7U6=!w08$K1)iTH0}e(U^s8|L2aO)?sQ6fM*y zycI%U@VR;LM3BrGUQYCrbX6^5n&jbld1ddlo#!}r>FAGRnUue9ps!lqkPuCJe$9BY znl^=jQ4N-PDA&|5cj@Htyg4nz$R)mxK{|vxb$5ihXOk23hxU#=6owY%?NW-D@iqq<~0Kok}!Z*xBCRz?LrbK0RqbrXj|=$FrLRn)V-18%~qulCf9%8 z*g@vP4>^toYq6XVP?JJ1Qg-w%$@^R=$Y{^MH23*n#kRN(bB+%yhM4*Bff0q%F}Qa} zHQ_-A42#p;HU9Ornwk7_xP~(Ygb~#M6 zOu}-w?$10bR=pZk67!L4y3gIzN7vytM(G#m;JOZtos;C89P9ttnq0DOZ zQ%&g+KyljF7)+>XgSEKjCh#Yh98Xf&@Dq7Om6T~6f--HziG+eSG`ed?7O#QSx|1#!vNPyRam1CXx4k_sVN zd|3J>hN-59p~n1q2m_I3Gx=ol6Dv25&Ep)D+o`jXxpnY|G-?}wEv`i_J+>>T9K$-k zQtq2R(~n$41cSeu(E)@NNxfA9sgegQt%B0Ge*@elAsWRgBMqxIA$Ea~v?>$KaJV>? zxXZ>$kn}{9P(>LHrMV}Ixf)^xXJ4gm{>4;M#l6*M>fspGPbF%~%KIO@es(I1uxuob zlwMx>T^^3@k?LrAo5Z}lR;1BuyJd!|XC#yZNw^0jRO*D*hxx2k*+do%^i29WxTI7% zeZZV|HYub+wbZpVIXw(VNiBx-V@!vq+9OEU8|R~q{;Rp1{UWeq(MuE5W*yYm_2r!* zk3wY)3h~qFYGpKwcFZ3~G1(eLky>$?*isZV_X=t1KZ*T7eHQ2;S564o|HT2NfzQ1_rBr&`X>Pw9PtIPs@u z_h3%mC>E_MOyA-5=HSi#ngq~%J*Q$lAm(JT`Lz^W4nlbq$cp2l0dp^azZkL4Nm?8r}Gm=rIp0!bYj?|42njF#6O*+pOmW)U!aHThiRAlV*=ew zJaXgmXnKa1GwN8sbrXS#mEWS5Ry`o^gR)^kU9nDxF5ROOC8!ZGVgMlCo5c{D; zZ?7iopH$ZSgKC|w4#JD0l%Z2)$JY<|OlkAB5cFbP6MaV8W8R;|N1* zx;a~2>^R7VZx8B^b~yjy_Bl(@qEE@RN)-)+jKm?f3LpUxZF~3)6=bH}t!$BGY&vwS z0I1Ddk`pGWST4qMIAt{>gqsgAl@?Zy5YaCHQzkd5hsQ@I)RbYHO$VAwcbmGbPs=4{35X+=7`mOe> zPO82cAJ@cixe9o8c2lWQox0X6h)BkKWI!`KJY18_nB$s#cngYw^_R#ZtN!DLcxBHN zajqhbu?}EsK+X^AVz$9TVx^F(`cqqSP5>@geZz;pCsmZ?0fDN5-Bgp1=4jdlQl`r4 z7*hA|P-o5wj1R0VL?-G6YK)F0m$`O_kl*;E{}#5QLBhy$nj?uDDt&z?^y z90O`I`A|7cjtEs&qB^*NgbdOp8GsNc!Ny??(fKS(15BMaUghB}Jy4x;Gea}dI6MNG z>x_HbcuX7%^){r*6$-mwOA69}h^4X@NsWG(wjbWoEb)nFUj>@wCa0qX1O23YxGPRZ zW`AkSZStUwDDS!Xd(`1^47Cd(38cdZjy+gB|Hq{b_HDE7Y-%}15d@DNX06IEjBi8*>H47(n250 z5t1%JRdoF0f9 zmZugiR1kh1Vw{atO)2Zr3MxtoNp@+3&_$G09`TP7pcfV)Wc|AZ zm!8utPbxm2_UhA*jmCY+zE7Ckal&w`O)E*So5oOh80AHc0frg6y3bbjR{|lR->>*#-xXU=VK5tXM&L;*$P}>i5cMFzEcm@uq5}u> zV@#wJAfee6%%YZgnO^<@0U|krxmET2GIk*QEUM7UC@Ygf^GhWMaANvjeGD}sTmX;` zOZO97+)-GV>+ zqlpTPl9^x6gh`Z{f9aXGB*FEDdoN@VokXJXGJ1|e>GKob`d+LkyG~G|c%w;@Wt`|* zT&=|IF&-%6`ot9OgX?ldmMQ6Q&p&VBn^98HhU8yZhnYY-l}l!r0n5lp*yrm~N{*`3 zx;~-*uRRq1-?}Sq9%#qnin9A^X~Q~o2KnR??N7p1;rN2hh=-V8J|~+`4iA#YF&Z-( zUy<{S!VjwN%SNmP-5*y*hE&qVE0}$ninb4gsg7d!fD|f#6(hW?O8EZ-?FrzY_ zflG2BJ&ej;m_WjfU<9vfk<%BV)=fY|NQrKdQz|0HcCVdJL%%&|}uZ2=WtL zY=*>?)oH6@0R=b;peaQ@k_c?MGAqm0>g(P6^8PbOe7W@*|gNPHm6Cy zD=n2n7LU>1y^gero6w0FeCw5|yYe);l)aUy1rqyU933DT{wD)`A&aXpvQS?O%A&GR zWn2Sw`+C&SC_}1fv9+~CMK!F^s3OQhj{>MungzENsGlcLnKs;uf>lCESxp~nX37iz z01Ey|L_t&w_2ZyA7#TrcGOMWPtSGnv$^WY|K>h9luEA}Gmep2ds>Q;i0LZ3U4Xe`w z!KXT2$GJDPk}Il~UpDeSz%YTB!SZKi2V^n}sWd8672@w23W`BpU!@#(iRx%60LRQy zSCnp15@bcVbbg%P+D1;y3hK##c8`Xb3o|Q&+R+6Monss=BJBN_U_x4zi1+n z-S>}OaZpo7jIwu??L~Qe$tXp6d%<|Yf0#8e+)Qy$T3K)t%`E9;Eb7E%8=QHO z0Tl()aZegt^)LmxRu(I*jzPDAYsLdr)caEeLN-e!%f+Pwq2mzvD^*%H1J~{=XQ9Ur zDpp`8TW=EK(*9Ott1#MO4`_gu4xSQ|h&F3l!vU$7G?xo%3>y>key{YUY>wA6d|Ht*&4^znvl3UqGi zz_(Lqr_(}^#YJVIelC<9hI=n}_ZLBZZ-Ojh;4tWSE5@ywg>h!N20-O{_{==gB+FDM z*Vao!9R@kn9!C~%X`vkuZV1F7%ix|`Y*DS*Xz1%;7~!pKUS%3CA1FrWQrE7*Ru;kt zsk`>BIk!1=tp<+RoL07LZ+byl>ON-Tkj>kiwl;;dT*pIQd(Vn&>N&)$O)cA8kiK_U z>gH@-zh9n^72^u6T;r)C=mdx@80Q!$FtU=`{vsH*7}p)QA`3l?I4>8FHRlP0Y&{^W z#>i%1N|`K!OxsssV71+(L2Q2(?iVPrSy^aoT_=|sR3B^PtTqcVf+L%-4FerPjEET{^5#xGyF32K4HXVbpIW0dpkf2yQ6f23}Omm zfsx&862X@(Mxp|khONQ$?#0e6L+-|#^YBwC_|}m=p~&LEV0v+7+P$)Ux)I2d2XJrL z7o5ntI0n!-r%5H3LrhbN8|#f?WwB#m^JUGMLLuwo7@SD1(ONW`T0@Pt(6PGZjBGN? z)+5}FTP`gZbp4Cr7|lPJHsH;N+2Lfh*)lRy!J0@4CpOd2e% zLEW%W$Hgw#K8aJv(~fL*h8*_{g)oPl&6o&eHFyFBlP{eWS?s)$_}1pM^dPaawy>+@ zTpCSEmX)4P8@XQ8m>YUGO=}x4rCdT*@|qvTvj&`$imcWEv$U)MI5qL#{eP+n>Vis z4rDcZ&t-FWSq;nyKPzNO*K&CaZfc!@+nh6awE!3~ve49&K_jdzg!8qyodKb_kBaRVsVi7TCvG4QtGKzEQLL;OLA$LA=(d>9B!|_qi9ie zI%w6CIaWnIfx?cg7@om@p04`ha_D0IRslfm+>#!}J2 zMHd}Q3He&wdXvf0%2XYKM+`8s%-XcFG;FvCvbtTV1+x&LW1lEEkX5g}w~M<+rk9oF zBNCQ_xk6f6;Ea-g7t7VdKSQB}Yp3#(uKTqajN z^f5E%LKaP*P+g62c!E`B^h5JJxT^3{f>c`vBfvooR4=VD;~r`lS(!C0M%*G}wRLq` znXF0pv>3A7m0p%g0$l`Il)gEwI8$?CS863R0z44}$l@f))XKe?DVeaou}hDWA@-R} zdn*HptSIDbhOgwq%v@E?$B)o0k}QGi&HoEX7KpIl3IQK-^lSgYE3#jOOhUZ1UDnk1^$%2Zh`H%lcX zXCh_aK5{ckFU}(wX8ZPO@VPYY+qG-$ISmQX?Au3jz)kz`E#Y^=hcDIVT!&BG{196#|| zjDk*Lk>k5$0zH@hBIM6#Az#QM(oRj{)D(_!{A_PmCbIucxNVv6t?I~XxUJ=yV+4$& zhn>i&ogH_?1bB%O?&R6l)Ou@@8>hjlQB8=n}Dda=a1vqK{0)8_m+ zTP*aqYF@L}8R1A2X+??Wb|g`XB8Dyq*y5;Qqv z8d~&ezKEU+D)f0&O_&xkbyDc3IkV`wp!z(B{d#Ih$Peh7PilHHs6G$B;D?ZqkUych zf15{72G!?*?Dv4|daDo4iZfZA^Jl9xB2vJP<;lP#1R|vCHjU&W~n|0SN2OQvQsC` z`(_b6Ayg>X*AXU7<%pdU@~6e$%wJ4T301Hg**}rV7e*GFSvmou3i=4wR4cOB%+C8X zpN3|sf?PHu%LNS<{Y_6#2~|+*ix6i`TX=E~^=7Gp+A)~sf^5il3uw@QDyTi#Z(V?e z8Q6c{|3qW6RKZK^7gL=*S#c4at5r&6QZp?7uCXOI-u1;9d6`Ml5Fb z-|x?*Q3I;rB=)aGBTf|*<>}C*Y2Jt>n-u;F^1W#)BGrUjSS0y&>R5<{L#F-Td1#`mmQwwT0}I4X znjA7^48(@uqo(}ZUuW5?+)`0Zgo*w7`zd22mNT?d|MXWBTm{#u{)M6h7}%*HA!7yB z2JOH76{j3XLaAFzC1k;U{NG7mjuTkUI{=^dzb2_kv3n79Y^emGm0%|N`p=W6gpA!M za51!CYA1jHm#^vBq!LPB|K+>MQ|d;<`|*xVyVsp}6}lu8Ui7x8m-F7I*i>VS(%So&0w< zmzm5Y@4d)OGWq4bBuYg|78`>M0}c)jTV75|4Gs<-_@9i1_)nsEzt0B;hX7JhRF_t? zak+bVIy$*}hP~X}Ki%CwKRi8OUERICy*)g^u5KST4=ygR@1LGu?jK>N7dLw+(0`Wy zZ2lSC+&vv%+&sZvUjJ3z-tHcsA7L+#Pq58{%iW`^?SspI!iUG_hiBNo!qx5lzYa%d z{vI8l*0c`x4^6xrpB|lE|5LsGC%xT2y&Rt1TtM%iV6W?Y=P&=#fBQv${|SqL0ruZf zP;h8)h?o#CsXnoMp=JXD-a5F1LT@s2OYZLcCW7BiL|Aa?_ z#wX`)@9u@YVf$Av(eY_Z%j+-CZ_)}n-*l|_MU;@j=T5KgPMEpml?@mFtW?*vF0{Pm z7L*STPq=%BCV_KzkFTH)uQ{5pYXfifO&up3f@zAcXE!fJr9W-cUrNgB3W_Sd!!u=n zz+h|Z68^C1O_-rmgna2bQNqH@!GUJ<%hSNnsgT5v(if2IYwE-)ecE4JpM*;z3(oB2 zB#BqC!nKUH*N5x3uG!bFxYwHDS0BjB!RhttF)TP9a%pM*t!nFtn$bAv1!DYKG=I^% zb@TJ@RV29NOjhY!UH3X9;`Qok{{$M+e`M9Xd;hbJ&Kj1GS$VSdmRZzro18jV_Xc(K zy2~%j)pc66Q;K_G?kiIZ2)(5r*ok3_gG|C*sY%jvkm!C~V4C&SOJ1;F6osNv+L#MQl* z&vG7($p0+7?U7u>Hi7 z8b%W#&(j}NE6o>c(3@^6stsL@uA?g3lbz{vKi~27vlY)s{xJ^g7D?z#ie3QB8yn%< z@5RNx_B>gWwuBh4Gds$+t5gOm1ZsZWMs=dkEh2Al@ED{ zn@W0le)b(=mL8wzk|R7XDl4R2bGLKMhRMju^EMh6Tdk&ZhYB*6E53KMRL9e0cb;v1 z-(1{`N4=x!$0ENtMQstTCB^m{79#1(9ePPQY>Uu>$Iokh))7E{A#=007v^=(AJe7X z2^rewF7Nc!TA2B8nC@5M`Q`Z%C`q5)^wPo8^lw&Ft;-MoJFkcf&{ZL(qO93{gUEUf zZ1{f42ioxZYOu z=Cr_;v7qI*J!<@!O}77Wk_836;aV5rSp~}7yN}iHJ`sc(bn6AXjl%EW+&qt`g7 zj#S!KQ2#n_!E==UD$;l`^~y>oBbt~VfZ?;^3#SkL5evs`(Gkz1^mbEV3XhLBmEfEn z3h)89?z+;iZ1BH?(6ICxaE!i7q@c0!L-sVDzQM(~bMZbe6|2Vp<|87_N3*!wAI?g! zeB3cken(%4Rs=?wsFX}tUl}aG=;IReB1<@@ zd`{SH2;J?%)zJC4lepeBCL;3K1>^3Pgp$O%grkkRYVU_B9Ik6D@b4wAcKJ4(FLg)m z;h-y;z>U5Td~C2SwC!&B;To~Kr7JulU`cB{RciAckZ8Y>JkuCY2VJb&JIejU(=L?MHjZhLbw>Fqosci!N=7?s zHM~;&7>B`rS&~CQ?Ke$)N;{5#k?VJ#a$cr!-;>#4q)u;XKkhD)ksPXjtZ&xtUVv09 zBaE@~AFY0@G_`XdXZ-;kMq{G_Gz5MlXxdF&t^x#pO0xGO5`SCQ$Ogxbk-Btmi-b{b z&@UN=L~QJ_a_AP$9&V==logoG%S4|gcUk{YnO)*3pIqE(5=3qoOMRvDMS1cF0NF81 zHk`kModMvr5U470j{;6S(F+UH__TQ?B|qzR2MPQ z_@qK(^r3$-s)SUgo(`}O+h+HE44t?`M^)|6W>f^(Jm=UHz{*m3bijHt+l&D!0&z#E zNs_w(7;Qv=!1mw$VS#-1cfJzPDufE4k1D7UXvK#{G>A_L+DzwU!le_Df+B-pbr0X1 zwYRVbhsZg3zR>z~Hd`;gSFxR?kqwRj@qB=M1|x$m+qpgjYmXN0PPY_Gx()~da1F*< z-jj{bgvY|`(dpU26+;Ms8H%Zu%#7v^z{7gPtyJy+4JrAL7iGLci8n$>HAO0RwO>w} zC&==}t?9E5KPZ`he@B^Z=3;H1v6gt{URqR9a;P63sm7KzUTczkR~u!i!BXAgT_A}G zuqd0ZHrED^r7U26=v*%hl}w$#{YWrV|I-DxknB`CC^lTP_j*FicBih$6IkIuQViiA zyL(KREoclgxzUNyt0`K+mXnG?5R@6ceXggNy!y~Ns>n<$Qmre0atfIF`Mblqt3Hqf z*`RTVFDcSF_NNEpQ*jYlCloH3XmYcvV|wHGzW+^(dx(+8ZC1sX=yTjuVQd@dC%UBI zXESwE5DH(R{RKwYkZ`OWL8n^zE{dr`dE(k+E5$p`l?jiEO%$xu@d9<1*g8LF=UO2M zX1+?QNuU2yq?>!s7h>in;PRiKyoEXL{A`}tnXHfO$OctxYXy7S2~<>f88#jmeLZ z!x>I@-V{Z8t{oY{WS}G}uHK^zPW%F{?tUgBd4r4*4&BryX9faRyY%WVQD?-e z?W8Z+k@drha#!Q7@Re$@fesz$b=SrUZ)8Yb zK#{Keea;Tob#rj1>QV08a71w^<{!=kK%K>US^o4gq9MFZT52Os=B>T}E{o4yGW}9X ziM@rCC``1`uF*DD6wj89YKX^U6l3cdZ6JKV#)QKD59Bp5H3)Ph>W7FZhexe_HWwoc zV3YWx`~C(MmF@O=!^!ku7@%R@BwdzKT3m_e7cMitudjbfVUY^Ps@e+o>f+B*9*1i> zU*M!O;_W1>E}iHyI=r(7#Hy9SWI;N5Hp7 z=f0bZZ!kOvCX0$6(BsqNK;m7Uo68ctaCFLYi{X^RT_<*%D6R^sOEqK` z8C~Kbbf1Xb+NHg4u(h3hu{bqrh~HGt^N?gKW(|Q-%7Q1kCK0pWvK8Am{~%ATq<`xf zFX+6NKzNOMlxn<$V=N0Zi$tGW)rn!3$p=Um+^>QRjfdt!od_uH5X68!0Tq1M-J;_)Q213%SxoS?=XrUw_%4mB z*ciwdT>ox#^pBy4drjBV30}P7dl0hf>l0YZ@MY5O(G$8S$$(qKbkdJ9zv=ygI1-`T zN}?aWN4AK^{N{SN$F-31sQHp6S(D2+7cOBCKebiN-lxRPm*f45O~S~(_#T6;&@b&AG1EVYr<#mi9X$uc3{*{i46Db5n zo8fHLm8LZa9F##~jM~z!s-aOCx=C_g9WVu59*Ij>kdO$HEZ5ieS5|3fq(#|khSoFS zWklP7xE;uT39|i|OXKJ(U|#8a)2XftNK+e4U%hP|=l00jwt=Bqd$sv7_wo6eeV?L? zi>&`TYQFcwRk7NYcqOol6J$M$K1b+lk+sZYO%XZlL7&JbHj-ET)z*vVh-+?5yHSd_ zLi3J+zHcqo2g%g}@ROVGBTLy1#xhe;OvqyS54C6+lA(wmJ*vpx4@p(_pg$E1cEsxV zs*^=GYg&dnl*LJ!s9SC%L!`8)A0pc4z?a|VX5>O06PRYSL$7Y(XDvUhtziXTX!2Fx zLJVn$^CFzPLTY&Q%lX-9p0iy zA2g>W`bIId%Klcb3t*dVN~xg`ym^dPS)4?VD$4RS26>!4pf$`VKz=&XGnV0cX#cGu z&c&fie~ne80%@rb5vxL;gYi0{GfswnV5=xUzC{1MUArCw(lOOsjl766eKGXtSRD<7 zCL0i~FxQN!1?}Vm`^MfrcXmxM5-G9I-s*QT^g8H*T}sao3$8L%t_Y&Y<8sq6;n0}2 zIOr}&5Hpk0gJ2|1mUJeCe)ki&QAMN&f zEJq|d2o2zSjOx&hFtEZ^Rf5_o-4nJJK3NfPk*NaYQ0O%jinSe+W;{sv2p=Y17MUdx zEa9BUTk0%w?8E^?5wawt#;}KGOi4?wQYU+fYLEkZ6(`l_unzoo8N|lRcyvHzr$tvdO?q!Ox50 z-$)K8QO$j7(nmdpHki0Ro224d%7kVCq>b&}lAH%l+kcap3{K9gn&5b_tLgN27#(jq zOp6#3lh2~?K6jev4-Ur3R6E(xz8Z8?&0HD4TT2LkJ{mjP-UPEPX!O}gi(sJMGa_=uPS9S)X9B_j-VY$FUGExx|5 zrY5cMs^daq-mIgQh@vbX|K;8o@!>v$|I4Jz{zJL7Bm)2|liy+>NYs8}fWCH`Z282P z#gLK6_6&MJV^&sK&A0~!_cRy&2-0%TG!_{Dsc5PV2%s<_36Twn zvXq>FI3(O_hjCSZ$?&YMNzuY}R|@0mV#v$^4@Q65an(`Cp>o(g4fd%?#f? zUdYNHu8G#b7A}ln{V|;{TNmuzV(H)*wdjDisf+HHHe-g@d#6C}tpF$EG}Wv@W+U%| zfzFqcH`oULc)dDHDy}Y0kvmru+w1EJexUCaUGMtiis;+zHQ^H-o!;zS7EWO0S~H_c z_YXt=mjiZ;8iiQ}hS%GtD5o?NM%=$bRhj|#M8(>cdaCW~2OM@+-Lugbd+G%rC@ZBN zWAb`J4S(q=ZeB&R=6gt0Kv} z33%MkD&6cUSK-jEltgDlJ9T=41=hS(Xyc2?_b>XCa_#0^&E)RWJ+p+3F=)M+x4yE8 zD0zDP8N3!$v6uxx;@PLW`4;(vq5DhCu3!Y>!q7tEx0?;)nzw5yd61>5ViBW+9>o6+ zp0hbI}$3j5Yg+8X(QjD*^5I7Ym#UD~pB~EmTCF+MUelaRD6`=l;UQYyg(^nIK zdsk`%JJ+HUlJgi45RtGE_8KGDtlnrrH6mZo(uWTJFc6CIdNw)LH5>LGK94yOtq!+bBOfckjV z1ax5#U0uvCo%oHE6KCB5wo2811Z0AByBH6pf-)2}ZE&n)URX0~lRT^_sP&h4N6fjn zaB|icNGpWVE#A=!JgXkQx;rHU5$FW1-(3-SlBu1qTs)04JP z!yVuM03iA6+NIwa%Mo-PHla&KvrqAUBz?loFG_B#BUEwyNo453Du6QwrjFzS zl7Him8z%K)JNr#nXP;uA@GO|9AHVE=o;$|8TK?2Pg?z4zVnhc-9-EO#97tEm6k8*N z=6z$!=A^NXFacF}YC!=$OJd7h;TnyShJo#KVnEgDtkS13!eO04t+DM+EhPXC?$YXe>|DHUA9PUVV!;F%`p~9{l^sT&hGcVoYYrh z8FWr|TE~<*0-HQLu%>?gso*VrM4-eq&g#Z!`^%^p$vE3&0GpBZ30c)mrXv(E1D-$Q+v? zMJ$Cz*>PdOc*{T2%t1wx{R8vYfIYw{0&s#~hhMW#zx2H_;b?Z*A4ODVD<#RGQZA7v zv)_(zF=}>sPlWc!g&)jPcyf4)@W^B$=Jg)N)s zT>>yprs8c`u$1_>f1$0X$d>zVEW*V@-N|67ses$kKW`lbz~Xg>x<;!>V2V@veHFR| z9~JY9;NtJ~4AL2Eer)zk3aw~>q{uP_PG9V5l`1;ouxd=wl@BwQ-WpBb>7U&CmY?N(KTm9f(*Z8O4Y(wg&iy0 z=$V9Xrauk!bNF$mZaID8SQNk`Z1B`u?ZdQXgB@}8N>{u zi;~u|+^vEC66qWCb~j!VEumZXy&UzMw@VW-G9GPO#eb;rza5MJg_Qs8b^K4H{9l!? zvMNjjVBeJ^RP18)g7l5|KdC7d1-Hlkv*T9v+u37s+oW0pZTN=vh_0~N_95JP+;`*@X5KaFH%wNX!cq9B9PBwNm|xpX6n=eBtCfq7)d?M2ktL%xCbBxpyKZ}a_K0ep>B_Sj z+qKobdmdA%N%CNMT+QEKFywp3x=7MoXLC4v3t~$Wa;R?_7*i&XUrq1y+a1|EvWuw8 zvEvWCzC^u5Bs|+OL-ZX&(vz?;D>WGl8vylc&#v;n6L2ed~fcf^C%m<6O#U`iY z?A?((rQ-?U1G05~7W2zxeH77HHYx$vLNuU?+Nss#XlibqPPO3%<1zVOePX0qY98oZ zAbICyGk#DYBe0gl!jPPC14ZR$npSY|Ut(%}G_|0*c-KJ}B+z`ClcJi0fzR3bWF{V< zU=DqLh>`U538%T| z)boNK!N!ar&RvhnDW z@{xJ-UHx1Di15;ZdBmRd_stz7Y4W3OE9Syuv8nTJ+&eaXV6p%Wn_yv{^WnhTMHl?Z zL(&FiY#Y>tN*@!n#Dr5#K_VXu;CRbyXdQ(7xW3i8I5+Bn`-F%&r#y+RyRb%_v35T$ zJS-V+CBnPU0Xh^nO4Hy#AXj`m{A_={lJqaa!8eVdn}EStKYf5$eDJi|Gp^YZZNxHYJI zdWpTt!qfQ_?8IKLTvM=z)c&GdJeNXxt^4@0{v(w`$%G=mG##sek>|{&Kl2BAaU1sQ zD!S#n(vMaQs5I=qlmr}yFAB_BO#!N8yGX;eri$T+q?BdqZJ*iNHxudu0FN1n7|8#~ zq4`sdIv)Jb#XP7c%dO0?QjU2E9QNF}k%B0#gunY|F{ZfY zbj|U)-o?!5az5Oc%2s`GyPwAu>GE#{8HMW(q26-aHQxO2H^onFOS(cS+s>{@x7b=p~mNhD|7YV>_7aK^%zBlk@ zyBFCp&Du%F73gw*zsC-s4%jP!3>3Yq#~Ee`p&-w2g$T$rd_D|sEG&^l5`2$x)3H+} z_v+?Be$$1BRm9PcRw zT6EFG_LARpSbeYk5eRnCx5vlvEQ;;>G}P|5N2Q$Oz~rG7yO1Bc5ZOXLwNdu6If8wf z<3(H}$wrWB7N3Cw3rKXC(*ZZrmi)e9Ip6dFs8aa5m>4hdj9BiDdpKK>_`4X^Q_3!& z>w8tR1CeXm0D3WM#v)@`vScD+f2R2Sqo+D&or~`bTLyFwfjhPQMkXGW?m?71=C%P8 zOJtR}EYbqecGLny^INiKh>g>53X&{?ZzrLwYS2CtTqfRIqgUE7 z1yEVI@qJ-KGw{h0XKzd4+ChKEn7OcNT866Jw3Q-6`Z6FqmWzf@wBHV!8Ol_bkV2`5 z79ehMoF|2^A~@WeWYhCQAy!q&wc~ent49NtnSPJ;?W1zlo$o&b;+G9grz_%we9fig zU}ngU>2!Tp*f=QgZOAS~sDmYhfGyCE6!B3o#=k!&w@X_js!n^u0Xl!q5D9=oF78sK%agIKsC8E!?aEBbsjR+>~MAl z3F+$x(kwZ0w-I&X(S&KDU!XaVkla#N_78aXky15oEn-_-`%!f~BN`>}1EPrc-VDY1Do!uh3 ztxY^(Q|!4eEpsnD6y-FGqY+CUh`wCz?=cU?e4Hrvu%*e(PKU+}Pwmf|bEa=w0bc6T zoxRZde=AbGRS+7+DL0Y~G`Y7-8jKoh zwzZdQ)n%wV$5_aM{@4BFe;qYW2PatB_Us++Ya_e~~%%eO(yJ{N$f8pW(bH)3g zfn`SwP?l1tO^%A%`0?pG5VQE`^N#p|14BipU%sd|0KVw@X3)v;`^$nAG5fn|7Am^L zj_+5C!?Hd?udjTixOnz^1xU%IJ>Bjvhs6q1fzYes9od8c*n&eN#a9S{wzE;hbwfe| z<=ex_w31Ql_pNO{Cj&uW*vUMzqAEEC{b1#9Py(LZ`cuuD;Ojw*fN1B-62ovpfa`m3 zcemio!pcbt&CyV~)?nAT`Xc}rGW`>AZ2mC+ua%1zYHF!MmyO-Qul%LctAz+&EP1$3 zm|?qR{$K>S`>WZLKM6g7g2a^NA&0!RoqH_netN>()vA@Z&yydQ;NZ|e|M?5Rq}A6g z4rD;CpKkPP_lW-qZQ`1#E*y613WOc%VLE!h%LzDnD2|a4q~#PU3FW(D$Oj5vDcRljr>)wLu~JL@Gfu?j!2nt-+$( zSF&3*hH8tYy;Z=jM%)an{z&qv7&}}=34$aqi6Z%no?_GmDH-)tmAS_J-Di?2!r^2I z4niR`p`b2AmK3Qes7gRw#s{g#0QrKNc&aXKBsxtDb+peCslDd1g4XahzwTZGHss*; zZtR{rQ1Q%QCHw2not1n7o1@`=Vr7hoxGby%(OKou77c~9^MnH0ixKA1+)EX}{80M9 zuGd$GY+IW0Xj=?_aXvA;{{u8Xt_D6vwsEg%Lr36*$QL4^GK9t#nKl{%fm z(Wc3?jhQsxJnr@{`E$L0r^{tUlXkciIMK|6V==ZWvB))&X!1Ea)IyDUnX@j<(BxR#$!8kyvoPQd&2}Y^Zd2s=bU~T} z>^Jn@{ar4`jary3o_`p;OjGfBT>^J5SW!aLMo&$=CPbr-+rfn>W@lRNuAp)nmr$ek z*9TbhrIq$>nrRiwlU}(AirY0|HJal`+(xYTWv1y;h85U)}m* zg0YapX<6bSo){Q(dN{#|<;r1?MBd#pQG<_FB5Lq%UT!hDn6zPdha(CgARb!hr9xVz z4Qc}k7HS<~1T^PZ$C$H|*{;?AqM_TxCoN^tb>EE95wG9c;!@j8FvBt@Dr#9V06D^i za3e@zD&&!{_#2-yF-5U*oA+JyuBuE}hE8Z8C9zxzg_BW?f>q`vL%;m1_|C&D{_F4+ z&D%lrsT zXV!{{!Np;+@hfyIlr>9k6Phpskr2`*Onzau2Rf2~cqZYaELEHEBo(!2Kv<(0Vx8d3 zVUyqAA`KKZK25JR#9%y@a!5mUB!)K0vX-^JLV;DY{3lTvKQb?H`s2&>>`q`;V%GcN zo!p*2r2S&kB)rD&JY%gIfDE;yb{7V33sH7=_JsLkj##|$N2teMNHCu5HriopLc|(H zwGA`K(-T4k{FLZLmRBP&h(8^6|L=T7Qif0=g}6+g>HUC`tE;DBs23ST{cK!VPIBeS z-?f?He5&1GR8FX+wht zJbpIL^i>8Wcz?UGf3mQuwH?nfZfnM(p6F9v z@?|T{<3?$6Lh&}(l3o<0MPa@BwG~=9G~H)k zUFO<~0Sq@#=U-GmEV1)M`>G0q=JL>;c#@o3(hai&~{F=Q=VcF0-oyMvxec zdq@$VE)3T` zS3fS@HOL=sjYk0}g#qf4m4+sB3r$Y;AfZ{emC)BNNvQK=E}q#ik7c*mYKn`aGYA*+ zQ=Og&UstK^8nC|XOKoq*y_Zz!?pnR=+Nvv;_7tN}>V|=6i$18GuSoT<1M`!MLtD=k ziOV>r(8HZHU~8)|rFh)kRGN0rEJnU>Cn7giYW5_N(?p{G?h-`PDmFVoH!T0>t z_Ahy#EZe*SX^*xmW!wB16mkako`=dPkzGO$9+T|AbZ?NQcz*9~{ zOErZ#&i;C7GZg2m^OV`&$ANQ=U;@9k-6Yw-X7EOGy&KoKg$gKE{aRDmAfvz+<@(j>MV}pZ$d31*8}ra7|Jdp2XP)VfR8vtvMx~`3JL}U1qH2$1pCpm5cgH@qtU7M z^}F0xtDlcgug@>9w+~OZ_m2-xFE6ieHxCatcaP7nuOH2)myh@Jhr-tW`OE9;&f(?W z@zvJB`P$y;+RpLw%*^HOjM@MD?B>&)z!`2-Tm3w#s0zZkDrmwfvEtSU`zM7xurFG-z3QD*3t1FdMJz*1?6Yv2A1L zh|X+SWJ+mXMJqYIV0%|T1H1T-g&5`$)84bc@tKv)Cu0iAdcSgO1H%$}ho++A)00zk ziK%(TWWPDN2kjnTSvz=FH}{YJSv0Y5afGBzP0y9?^<@?}zCFG_-oNws-WAn!xR*Va z)pz6cYgAM`qodD+gtY#KF}vSBl$Q(n*tLHAP68JVm2Xf^TSk1t*RTXL@`-_kNj0N8 zRgN`VyluHI9~#Jxp8?KLP^dWnJ`kOZOz91H~QKT;PvE~u1#&%Js51}rSjh8arfAn>tZGC8t@W47 zo=+v~D=seXxq6w%Uy}Lf`)yI~?w6a z>-~UC9UgiW;*DTf8xv1kk-0zc)s~shM`@}xCU=kEZ4012SuPZ+h?tx&w5{;mADSbE zh>}B2N~6A$>%|aaLA?a7g>RH^4i+X7!C(8r?~|QpElt#eY2A);g}G4;1BEbr8z}2j)PjrL9mM_D z7ZQD|u|+-!#@U6CFd@UdKgziifb`&4niuvK(3WJ&0)y`+L-h%7s6@RPNf`DPWNj(u zdLtR*b)WEZ;yXQ1#E=qLB}t=6WVv^&Y&K3y2ZhZtWh4mr^ZtQL_sN49)euefv%I5~ zgGs*Et4>ir|B^i5{}lMBGq-tU)xh|S>;TnbjQ-rI7F?xzw9YnY$QijsD-0@wzBe~o zpylEM0aq{loM80e`IjaHCfN8vv(C=nmYA4$rbi)ZoYvhZrezztSy{sZQt0g&zWSB7e^4%g9`FNSm9ru0MM+!pW`g63w|1eT;j+mtNc(9_%NL z=D@fZ66dZD(ut$DK;=&n#CO_{rNP=L0Yv@Utk6xR8?W%_>s@}S3||;7wNt2HeevON z1$NXG_G#Dxm{b1aPO9q(B^$;+4ATWpN@A(&Lk&(jEPbG zs^Tu-L&r~>J(|?GMh-$ae_KeEp4`xq6$nq;Levw;+MOq0wOKP_tC}dP5_W>Qv{&R2 z6M%FQ3%NVOFsC%R(|P)hDsGU__PI8^CH=Z;mPjVCKWltenZn~JbSeIEm6Eiva5sZJ z1xoZX@Y5C%OnvFzlE33!j_)Pi-y0lHXi7is0Rg*Zfbb?86T4-0p$g0Z6%IAll_<7j z!?;}_gl#dQm;fN4q#=Z+z*sFvnJp=8<}r@ukWrjM{~$yKVzHtOOWXYw@0g8Qax{Tc z(}C7vWg#Q@80M3*9@2Z}P$bfXt03?@gCr=Rc4Yo;3_IdreRRJPaTaKMLc)HaCL>F9 zq;XD2tLlNBu*;#KrO2`93Z(byWaqQjC3G-8J+rh>_j*lyORFudhr@{h>|DV7y2klu zV0aTnghIRTPt_dIK>9^+cj_vA7@r^V@|JKb+E6vQ-(V$X zOD1{+9jf(UN>1DS8tO}#9r&f*4j&3)3cn7tb<6!TJv@eBs`2A*PbUq3!kX%s1~pPX z4|2e^q8;akUDIH+eX8F4w5VFS@y!--`(o-{A1kVONu#xq|wy50!QU@_qBS7_L6pekLDT1-%z{R zY&-Ywf6mDYB#33EOjOS4QID0-?Z#~thhF&r2}Q@{9Dk(UiU>^WyWxV;k|&tidrh_b zgeVPOJn_v2GFq6c!m79r(Bl@nAt>>&vK>Htn&mF&-IE<@>t9~n;_Tya%K)}Dr{I$+0SxQ*h7hA zP0Tpc$K+c3#s~mO$s?KMRC@4FL7`zg7I(pgpP-a7)*Clf>=P%wL)gOE_a@V2C0Mek z&6?<>ej|oV1vp@_>FlI*xoxF2H}C?H0Vqmaa_r;QVsNsmRJMlUv~iDoAr|rcACn%S zI5SRXD==`jIWUm~K;dknLe*j~41z5eh6y-n7TNl4;Caf{s;KW;aS8j`o1(Si4o&n+ z2noW}8I&wQ!^vFmH~s~xbVV=&Q_Nx6Z24Z-4ed!_b8wVe14uOOE`WMI0o@bF%~~V) zvaDlR5dDph)$Z$ai`ZPbru$~AjOYB}4e)dtVck-+?$oJ5{o--UIUh}Q>bY=Jp_Tk6 z?wT@FW(%%6ALcszt9cmO=u$|U=&wO8Z9NF@!;5h_9U#?w3|g9C5-Wfa)%b8{M_dm2 zY|7lTm@JUVKsH8=MCvy5gY@bnqJ0h1ViDj^P^NllWf5jZF zak_#SA{SAZD%5VOUYekz&7SldgiYEy+$M$&$ka?PPDAz?pq9DBQrWc8=F|ZQWGf35 zLdYpcE7Vne7>^x?%e_zB+bT#k({yPD&s}5-S60+ZX_p*HcvK{)^jyQFHwQuO4pLQ` zrKT3N+i-7@30gl8<>oc9Z!S;`p@Kgr-Ug{_VOo-=97&PBimS@GD{K+ee{*Szf}rT* zUI=#Ky6*YH{PfmWS8zq-sE0jj7MX)1OCE;@hv}>5hE*}zhrjFR$hwK-dtrpZLKJA@ z*OmVm@a$EFofOnEw9cymsiBMv^qYf+_?525p*y_S$eL|6A}n#r>#)$yQlN{sOS~-r zNu*#!SKws123KoV(HwV_L+%r(L!d(nbx@c}3E4{iPHlYC`rb>DAc&3YrXB*5K1Tr5 z?$y=_!t1BeR?yPzIUwRn2xj4#mPpZ@M`s;r6-y8(XxT>@iKd^2-mj{V3*9job94V~q8&z06yF=~ zv@Mh?yH$Y2q==H^v+BcvnD;=2ovaK#zdfBHKWrdjE0SVA@>Bq~qH_@&vaqOBlDqx% zxYTGFKD!RAw`blX3?AUkuo-o&H;e|&JS0o?=cl8r~ z*RM&M1`}cF{q{nj=C9$q&#+a{EM={&_e$NNK^V$(H7DkJF}@7cm`t@V(`1LamE)u# zZL*2;(_AwS`=Q}x4~)uwaB9CUpGydRqp866>}w|~beN09!P#LIyoA@p~kfkLx8tCG}~Jv4wSs2GeuRwK(m5pO{@C#eB^tY-j9 z{j@+L)6L_A($oGeC$y7v6!}d`eG!QhSnH)kJ~tP|%r zc4M{9#7z@}q#YS^A{a!c*j6MLPQSqQ6)#PnguKFlE@YpHI`Yk{Wqz0|$d;f2cdyRs zOV)hVWdzD;J5ZrLJaVkv;D1A8{$|23XfhOwuH9A4-8K6(NuY~c zP~Ys$9A&(S=%yh-TgCrUlaq*VX(|r*Dmtj*xoM3tep#kM^;P|;%|2D{U0|J)uN?zBgQ`X) zdorDsT?*jxutl*3=mn7y{fN`A@FGzenzL?Vb@JWZzIzk z&V%taOHgvO{Nb>Wfh@p-&gzY%--bzY$5K=k$lnjxGt0jp%YXWU4zOe8 z=gc7&5yG(Q2;^<&o$8eK`I%;fN1d|E%)CHV)B~cSBCV!IZJyH!Q4!F(VX!nLNNNO7 zmXgWN$E2oB!oi6rXkQD+5+tP_C!{GuT%)}YV1f6^NsUewv3lwR?pe(!cyzojebV+< zNG#9}f5$WmtF@i7XB0V3-H{)<_zD|Ssx+MdoLWxge`M&>YRN&+2cLrsb=mPz*)*9! zru2lJ>7}nKa|-iIAvY{~4njyU$Ke=Z#TYz`s~Vk9<}|d8fk_q?>85h@<%GP%meg)rOUL4&_VpNG>%Ntz?iF`(Abgjhz@}Iz&Qc0(yM!)*%=sfq zby_St+-mf-`ImE5@i0#195|ScMnku@YRHx%vg(|Y0NDrJkJR`D=kRyyl8!1xl|{X) z?xMGTKp-W3Xj}K4J^hdDaA}%e1R`AN{-dA3u6dyw`3U;4 zTY36#x9UdCNulKUUHvlB0;@r6b)|FGTd7t<{z7SRwrudVTI5LS>HEnISQ!~2TxV3eJNg`p5?QnPb?(>&T0K9)WV!r)K8C;no@D=!}xO9Eo>uB|Zm zc7ACmHSpDU7O59ijp;TK6$xAJetpipw3{C0M|k6ZV{yw}5?xj^o=P$Q9V=4RqtXSZ zH$6P3k0^!w)YzA!EHd3mP~E?v{5p57>~I0^dm9~e3u$VJb$d#>Gia*aBDVipd)PSe zPrIp*f9Y_9SY>*EbI6`FCquJcQb2%q^0$oJx(V|fYk4&zSB0Fmtx~*;z`&F9_)Ajt zp&zQ*g4(&91YzJm_O7JL1@-0@nm>3>yQQ5S5*Cn!I_c$7E~_(B)=rgCNpGH<7iTgPt82igreKCrEl|` zdWbl#bT^>SQ@%LPkf!r0pEF^nQ52$qqG*@wt=nZAvM}CEj;C@*GpdgZzDt&_aYI^O z@dCt%A2=rZS@ASkR8!WS@|E~{d|J9pn=AaX^@4|FaqY^7iE1gLA|oVtj<_L=h_oSU zElH|pT&LCeG%z(AIY)W@Lx)mywm8`@CZ|Cm19Y%_yfTRnUJ6+Lz zLB@hS7m38gg`Me20@WkfP(r~4Y9As|E*}ek_aUf3On5ZW(jP*WAXFNt&Lo?<>Nvl; zvatDG`0D;0??X!$!upkncj;e=zxVUgPg}LAGeKB?6m5bHcYcBP zM~kqbCoLZ;4$on12e6BzprCVGYb)&3*S30{?Slc?yZC!bh|xVhq;@#euw2>XQSdvK+!H=EglV5TCChIr9~zO z#D>`4mJkAEI5cx<5Y6M=4sjK-^~f6ZRPSJEqQP!<=A~XT0)>-67+%65^0-a`+|)^f zOY7#9J+qjzgqCP4+SZ@41c|51S+CQiAkx1viS1B`HK%Y?s11POA2TyFw~ntE{@AJK zE8Sk9-`(&M<$xV&)2N^df0^Q0N01yMTLcQY|DR>PWM&Py)FSM%TR6|re6%u81RKqY z1Q3>kBmiE2MI7+~q)0@Dq)5G z6s1k0RDuPNZMON@MN=uuTlXfLxs;W#dlhFx9c_RdabiaV;Yg$x;c%xYS(r}{bF`br zuNq{U5b;F^o`(yHyqIxstRIj)MOOu$lSA@Y&&sdx0Ic;QJo0v42Q1#~z(`4v-tH%_ zJh|!KWd(wNb#@(-Spjl7 zq+l)dHO+CA$Zgn~^Y_Qvo({X$+tPD!jKR@ZzxQoQdk+l%_Zrm^p&^v6`Elu3Xf_B# z?>lkbf*ZdC2I`}&HyN__jbt7w)NGSMa89SIWz>4c#%ec$1w8H6TWm{HBV-uOJubKI2H0L5C_2VzSLZlGQ-IPIF&G88vwUx8qsEjczcMvZMco zYJOHApR$bQR4U)zBdSAzno$3pQy+@y`*Xbi3um+hEseJ--1z+4Y0ulSgBDMi1RagH zL&EF5zXA{&@ZnkD1>!6miPeJPQXf3P%G|?{JHy+{vxLiBZa3-sv_!1`eW_AhaiQP) z>`k~I#a=+!eoe@5RAx(E2*aOsr30wu=RGN;XEjgwWO3u=ERaS-nB`7` zKn4aC^=)nbb~iM%%eS79)Q0Gk6SUznyyHjpyoeZm!sP7?gm=O*1OO6&4@QbjHA#N# zMq3!etTZ5*V^SnB9Z2YR>jt(jZIj<^zgWpPH$V6+g8d{v+^oi?6GD;Dv}}9Q-`R-d zk1wA^tUX;4p1+MFo9c23zcG{tC+ntjo04C85lyZ4=`aMUS}!~rTG~hH*za@XvfH9* zR_UF6?1=#yiUN?bz`~IqBw9&3&b8w~-+~DZ0GcE3MFI?v_xtyhz0TqhJfHNAr zK4qIN*aXsRnAtlVJ!_{YJivg6d>*F(4UqcPx5QU%RE;M&yB|*ckCRp>b`5g2B7>x& zPW+ZSyS-f+JG^a=1^x`tfFf+&d7O4q6V~(q%-H|_K!XB7W+`#njogcX_3h)@4~KKR zsY$!f12ar|8MF&5E>j-(Ng8bPIsTgHW!u638S!H^U8nr1g#{B0PV(kA{fgMtkslI{3hN#T zCwtc%K`uiYt#!w6dBJ}LvX$T)@_A?>J(37vzWbs0a!UW@ALYjFKGnLu)t`>^kpR9@ z1TU1<*7D0szj@7<5&yTItV5%=T1xZFxq}-@VGi8O5zeP(t^No6>#2>n6tkroN%N6s z9h4i&_eV}anmZ#3J`lMQb@$=^6~*>fRWL{(SO67dK)I`VEHC6B%1YXuYsG|4vqA}c z?xdOYz)aB>z1&r5G#Sr~Cq?2+aJl?v%*k+kSTNO5Yr{khYOytY?mSxz{|H2Np2nke z7zA~$R!R(mw6-Gs8Bbu2(RE|les4}QOx-VD*TtT{}SZ;ER0C}sajsUMav#tyv>Dd)K zO5-i6@K@9lVEp_gvE?XKG53IQg`vtvc@6{kpID>t%oQIfx%j>lxDuRD3L}+1Apd#v zn+u|VJ|MMovcs+OtfCSXNc>*}8@J`gvNjV%85KS;wv7A(iyFid8G>B0!_qIkMa_@O z%#~Y+3WRq-!-Bj`j1DE*rKQpFh^CE37+QG6gy?G)j+(DIlJ~;=a}iUMSr7d$>M(5) z8gwu0VtV!FDtia{@x#&D8pH`PRXlz9yA_IA2WW~OMbr8CbXV+KEl0v_8_18Vaj=pC zuTV;I5q|@+zP=8*#5p2!i9Rv-N*D~rU<4V}i!Bh*@jNmWHU5%aNEAfc>1~oC>S7HP zMnJ>9=5QeVHjG616wQ`8v}y?WY9wOYbM&(sZn}A-3H=Ari>7<8Ym>-77X&YQs-&oP zSc26B>#X@<2R&)*IfU$$hUo?w!>P=0`a)ax99M<xb-g{ji z9reCYe4zscM@^n1whfm0C&Ap3_zK>2d~*d^swA)&$`0XXv79aeP@bJf2_-p%9~*K_22L^e;{pnc?`x{DT~2Sf>ZQ;=P+6g?ZKO zzKK3(O__jwNm8h^Ix|Hk+SjTK-8b2bF=0rG?t*Uu#fW(RJz#8Fs`f)c5eNR43osXe zSw#HOXY!(a8(`H+I61Q`im?B#!MSN`jQ-VzYO^W6AG`Sj)jdnvZ@E){1fO<6FiP3V zw8K-iUpA5d3iyt|;&S!Z4>DKOr?>N-XJ3iij@&0V>woyk9{2NOC^RlhexXMOls)gx z``z;$)0pqC^{PE&?Az@=x*t%;%sPeNVXc`W;*~3^og5mp@z$REbcg9{(3IZ-O3&;0 zex)6K^A7dX8^hi<;aS&5=mqR|EhllS?4L+}TK-U%^jVU>Me&_8F#%UT?3lx;i#6XS z2tbFP*cJLWN}JZkxuc6doN>o3+=m48g1(<5(*l)v+P&V^0xy-%Cvaxmh)8XLY`&{; zfcsZ*U)iL}q$TG>C@BnlXVz+h4!{0s!^Om)rEJ~-!Ok+X1 zzdFtx5uNlJnc^Y%8xR6+%&;LgAx13+<4=b%*^#Y>%iybYZb@JVW>5Nx+`@9qiAow6 z2VwHVU{fM_2QRQmeaqmEp$>df?Y4B6xMXxs3^8QLIS;9d1sd>~I&Pj&02&)wY|MI2 zbGsk*?kS&z5W&TR>u3;o_2zm-^hN0)H*H)#+y%#Q@v> z*#pHqusp6fPJmTcC2=^ec}ACfJ{l)incj2<<@=n`YqNjp!n20!ufN$Wh3IgYK;va1 z8vApmkk;tSuXlO6|Bz7lEn%%ZQs*V8>PYgQzWpt64Ea%q?vH`2?@y1`x5P=d8S%dp zDq0#CZuFuY&TBgRv0s?`6UX+4`^4BL>hbd2zb!)?8lY5nY$4u|sN3a`_CwSxW z_VT&s{bFeHiO>J#jrMMQP5wTO2`wfin`b?|X1eFyu^}kg0UP4SmOqCMLNMw*D+DbV zT|yk@wujc^*S%%kpyrh52@%Js>3A|(nuy!dP1fCmN8A2`;`7Dg7hP0(>f|fm0fJwx zQJATS%5@sopa4HVu(zvTz(d$gW2>fC8`VwI5-CW;^=Q?8x7UHz zzQw~A*rxtrD;~R9802#FQ>RjZvf9fyf7jy$A?^cO6v#hw$3x4xb)=@^^G=0G6eY;s zAZ&fCQ3X#tdT4S2_0U1CeM2$PS{})={}90I$(|p}W*<_I_|4vUd%<7JesvmK(TY_f z0L1|3_xr8I?+#$C)9><8RhPrybzBRAL~^vAO@mruSUZdzio`BT^N$`R<;3-bwFSk` zZU6OT0MH>>?2G~6IE+DEWD8G-VKp1I>B(m8XPLy;JM-vfC{U}`lpWg_-YKLa?T_f2E3 z>Fo$~t(FBAQr(gx6WQ}e1`rPD1@e`eOgpO_ZUsG5>wor!|L^t@HnwUSr06{BEQR$D z&J(CHvX5z=S)@b~*i(W26v?*1;}AZ5qi^{l_{;it)3jHC@}JOS_AbBo z*Cu3BcFiK%HsUP%MCG#sRjx!9`y-MB6!>aT%XN_VFB2R4l#PJGsPMl>%f`Xp21T08 zNbid7{1{l`XQ}z*`k7?&mpEDR?Y|5`P+7ECoWDJWlj~>knVYS=g%tY}3>yz)SgT(z z!pK!=axaS7GEsYr#eRBpORdnRJ4!wep2`xzv1YlMohs62A}wHyoCogb!-+C>do&g- zwHR306FEerv@+HVSIYLiRnM){hMs5@h=^VFg4H9n%rQh=SL!@#vea_OMkhb`UmdH;a@UpJC@Oo;L3Z_=hm=czngsC>=VLy6+X~OMy z4-+=|uDJ?(XJ~~KuX-tkMC=9(YoBzxcy}RO15rI0t^1=jma1BnE5~g6GU_AFhiq!g z2vL41afKXW#rW>-Y3kqAihtMCSVQ0ph}9z%4l7MorAP~;^9G-L0mM+O1G{!BbR-3KH`#vAk#b`8Fiv2hyy&%LM@^ypie44l@lpF;>?AO$yKn? z)Q4fUYrn?*S;W1HjnE1I2hEKn`17yUfpH#uZ2|%e#SXWa7R4oJ!qBo&~m@=Ux%VwoiT41Z{yxJ<^eu+*y}Ee4)>Onm_w^s&=CoOa_!g z8Juf+kdQ4r$qXk?3)`j?FebAL}++lTIg`DG<4c>osC*- z0e$&6vs!StkH>(#HEc~?_VIZVk1&DW16kyH1F)f55l#v5RZD8%J{RFmTL z(Q~-bI>n`RtlQr};oMONL@Pw?b`<|sHU8w$BYYL{WeWP^X#wdvqM0utl;XJ?dBsvC zG&*cu>NE0+Tj`|Bgk7av5n0;O5rQTfir{+n)d^yB-~;^r6jQ-ELeM&rv~LsM z(Hsk!Czi=N$P1s)4lYwpZAqS%s2V2jm4wY)-DiK_;FtEx1o#T`a>Vx2w>N4582@V} z;%c_2O(eblD8~Po#{Z=l{}caFjQ`(g@_#2IKbVvM;VVDH|MHdpYwZ8u`j5T*U(;n$ zvZ=)H4vVzPE7RPXbx@GNh`_dcXNJbQGC#%i^eim5&QttGi z*)-^Q>9x?!g}2K+aOU2+32wpD>7+`gecZ|O7usg3ia$>eyMvR723cey1RtYBx&0m~ z{tt@wTtiy#*0BBO^?(%32XeaF*uE399is(d!U2jKz2_>rdgVMkw#X9tb-cR|Ea2j{ z?LV_wG_99uyaht`5M!z&gO0DaP<5F(_2H64jKcfsPaiOKf5@C){iATFjXpFqRIOgF zXd@%Nx@?SYZ6&?>R3Z&_8ycDm zf$De~nDr?EfhP(;Pk9u5?{9%RX=9aAG*qC0I5x?oU#Q;CZ`UmBVUywPl-~s`*m#-M zOM_@fW+r0V;BDegzVIeOQKMc9N)dDy8gr*>SqnPbxGmrBe5xjsOu2 zwjpY$X;VCKFwCL55qidtPYnE&?Vlp2Gp9BeoZH}Ab!?B@(g~%xo*4YM#yM;~Q32Sp zcNWra4u(m)I+55qsre=V9R1%dpJx~rqpF5%Q&ah|;Qz&e&8-Y&n&a}$I9scsZsA$= zX!YL5QL}3)$A8$INeZQ5VCi0?NGqn^AJpKtoa-M+mie<t*&$lviQsn9ptb>N+jvJ8Rd_F%MO#~HlT@er zD+=Zz6vng39btV+TPtz_h(6cB)Y(rIx5jo&!Hn+t{wg{-6s4;3y1GO7MY3izw!fSZ z#=c@{x+GoWtxamtl@+#u)aR<^tgcCyH`;SY()K1yrWam3>{Zr+l(`qmykO|9r9N~(kwSE^w4eoIC(Em=F;|oW8hXMY;{jV zGsqG;2^X4+jGRbzJ}@|`Vyr5L<3L=d+6@$JpLCRMnYA> ze_+!xQPQJlI!_&gsCdp7VL=$ z_US3x$soGw+=oap28HGW!7TRCfn>S5#XZPva;HfjpaBY=9UT;tH*_@e#wan~zb=EfPL_%f| zuU$e4>aS}}fbQkM_3XvhDEhAxCJ29e)takL1e3Z(evIQh!0as93Q!{8k@_T>xy3X5 zapILUMCL-&3Z;J_`t~j*KtGCk7A|J~#Tq>JWf|2;m@^|yHuIqPhoXtcjIowx(`DF3EA0^0Qf|6KCR}6ln0GeU}OS)gJ+~Ljw3Q24DefyL}<3I z66*7qBaO(L`9b153KD?{wzY|sstqFWK7i!&WYi`l9M!4O`1rEoNU6ec8ZGK?PMsaH zp=ngGwD_2a4fD#Da$RMt57Z=)WCc`0_7`k|pa%NS&8OdrC$3f3J$n10n^@CQ7^$4u=OK*ZrXfO zW_Cih1Vgc>XGRE3Kxu7WxKVTQmF*JS=o%HNh)gPNf*N}w9-K0NPaZyiN=qk&&NGLF z?GM?CNfO=UjD`7l=d&V^_6z|e{*b^|>wr8J36t|YfdWMH8HhY-NK|-vMhvJ*-P*T? z++%<#us_oe{`%&Ky-C4p6W3QUg!4%hqC9Fq)v+FP!a=0tLADLQg#(;yi>N8u&`uZ_ zU2X`vR|Ia+>Z1d^6@^!Dd~}2Tn|>!-OfcW8nWN>zc9MAkKXp}Q{kTEYw*ZEI^W61F zHV2(xqeI{imeFB&HvJ4^Pt18ieZPwxt@GEJ4@1}hzb3|`l#}B_ffpx+rx~$!H-4^x zRp>A^h1Yt^cWKm{`Yq^%oX<`>%*5tEi({=jeL1bK_VH|Rs(byNo*8oqyQ!9+;jm|u zcH6;8^BImBIhcH|<;_&6fjxA!&ar3fnvhPPEuEDRBf^ATsMq3#{UNj^X^`w4;o`WT|C zJ?R9ra&X`_`NS8oN$tSVdatb=Hp9IM{x2`x>I#GmJijl4{6=7949ob$k`3%p2HeT`+r19Aw0MZ5K~?}NY`U#N+{>wgwS6hMwMdW zHHOH|Y@A$lP#wsO`&)VU;ao5Oz?F%NFE=|RyuZOSanGT}149JaUYy^NSi&2ym#q|2 zou(}Yv5nZYOIkF|+J0xT=i1_%n?l39JLrx?^oVvWOWx55_Iyob-*u|QgGm0!xy$bt zna+Zu;PrQ78X1L{Jq=3(4bPpQ3+zPJ9{70wRz1Z z!GU1y?9qng4*~ohC9av~w;#@DHOnJ8OE zUgvx$U1M4GhR#l~CHYI04yksTN0%DPz)Z`QTq=W`+~o;Qt;0xU!9v9CAE*g)t2C~C z`Lx58pH*N144Jx)F0U<`TMTCE*M~LkB?v*0~M$x%GOgpj*Y6mC6-OWP#n=j zLEKmHlVB)#y+W~cM2hYXqw1s->I4kN-5%W0lf7FAwJBQ{2TdenE31xhGMC8#9caQ# z6P83I?}}AUz^0#_BeS)Oq=4$L^3AVNd}N;bGIvAcwLLy7<>}*mk*rGvV5+L;q2x49 z7n9t0pR2v`d;O7ld}Si}ywACW^>!9aRvLGn=m2YT1(Dhx<0hm9Pb~H1E@b?mKcwbv zE>~UCp2d!~GT)7S0pFfOlaJ-KR7t+N$ADs-orwbSpUVH`zaJ@~mU-yqmC;GJIcee>Wv7 zd=0s@nuSAgZQ<$SN3!#_HZr11J-V)4tXqWQcZ4B*w$d~1n|_;Jv>^iKgL^f9z7@dM z|AHJMH#U^@jm=sQx`9Bneleik=58VLz4czwc{THDjaPI%g~?8wq6KS-$BK$D`( z-?KqOJwFYBKezNQWiF;Y>l*W(EkGhVf;z} literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/inboxwidget_preview.png b/android/app/src/main/res/drawable-nodpi/inboxwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef3d9928a77a8e38c5f279e3e831c6e8edf6b2c GIT binary patch literal 22302 zcmafabx<5_@FpYyg1fuBySux)FB&Alo#5{7?iQBCE%@T@76`7vb>aA~e)rc^-BtBW z&Ais#Z_P~g(@#&7nu;tk0s#UP6cn<&oRkI>)F&Vm6tu$E&mS#I@k)jtH_B>CTGC2( zKVDuTuaLLv`^TI6r-!GPyT|9-$H#}q=YQ8XxA#xaFR%BHA1BC%Jior}9A3UcAbZDG ze-1BNdxpFE$KT)IZ=c@pp5Jet-rnBc1%vNDM(!T{ySjhf-oLoGetLwwBVy7%yuLp^ zzkWrd+B>;A{r7Nqc7uXLfBo?G{QiD*^8kZP{`&rYd~tj8^nP-APr)FBj?c8QbHu_e z+uj4FV-;WC*n|E`NI=0gx4don&3kfgojc%OSX!Heh7XgF<-1q7lBT8okDszCCPtPn zoPtUo{xLz3Y3h15z}CLrp{ZJ6S9xvAz~~%!a;dPqF(xTLEvFoplp{PYE4#3UUrb#> z!9drWqJRN-{bY*@Wj3JWn>LfJNlZ`_EOmQ zn%(&tQ1cQ2eBL~`b;>)&{3THHvA;NO8Y-Gl8`EakZlj+e5Vl~Ek|g<*`q5hOgQ%k@ z>&FQA?{fOCP*8{%{~geCn*py-P-IZ@QsP=(YiHfZ8EbOD_symjVNTD3t!-D7((mM; zR492IHMujjvzML*lXb_3`}f{}9(rgPEUS!8o3rZt*!gn_Tj^&y4hjy=-5h5Tn(evw z_xl7<2qf_Bc|=sm-(@s(G*BvE2r@wW{wx~!J}>%qe~!~>N_ig;__nfZyOaCkIsbpL-E>{!2fKlkTv9 z{CRllfBhJCE?oE7;Vz*!-0ZiB2;9}yzZ=ErcpcRL)>;igy!Y#?d4EXo<#;&^G|@n2 zgwlIoOhLRq;Cv!DNk{cFz^Md z>7%oPR;^LBf96*|^=87R0F7ycUZE#GieBLJm!|DEV)MH3!+@1v8E5tEPbb$@-|u?2 zw~(22AoDP-j8NDwxmCI}sNyX7h@@8c?{tmk4ZL72rW95i=+GlGC}B4=4!%^y^Y_l2 zmG>fR2Q(*K%|@jjwEB@upDDA`_QXp7j!qvn>8(#eli}!I4Ai&xm+SKvUDANZoe|y3 z?Vek{m3$}Kx64)=>MLmd5k7Y3u(#t(54s5<(+}Ou^zGj>(=Q%SL!j5O zfc1U<-ST{o;fW6n-hWOtUYeS2%QH2jne>Vtdv$+@+^n!Y!V%jYJs_NUa6Ekq3Z9AJ zC52vUL^@Y(`oF2F}0 zcF#N4uhi8`(|op^g|ww&*p`a7Cwb)7YM%_>q4XlPIs7^*k@Cz}gN5ir~C ze?oZqRB(b3@O~n-_05z0^ok96Vy`P15~~02CP6e$%-{bghMP*;9od)wX)V7FiA@C_u5npM||~Ar1!9- z;`wUQb32dXWz(}YoW<`J?|h9T$9#D3Z}z@RrSlYBF6HK4QO+4AqOli?3ioma)9P{q zTc}POwx*Xm6f*K;z|G{l-x!M+>Uzwj$-9?5-s`^u!P`#-N#ojO96mnZ$fl|*u>_D- zWkXJf94q!k?(+{XU=mIN=75ymdm1~>Q$k!Ai5PoAxb#1)HllJ|{Nt|il>Cp4|&s=j4b9@cr+k6c^5PY1@nEd(Ad z)noPvBLjUYhSitP>CFEeL?y!Q=Se@_0U_v|0N0e-@w*7oz?;^YieMml^qFdXOiHTh z^@At5q0Zt7K7bZO^8O0k=|PFt1qj(S?kw22`M>*0G3u5w*snW zl?_7FacGLZ{e+2OnP5o(^AQ_=NB$|coUXSVf$R1Ond@9uhQQ0{y?ASx_8C2Z8^}A1 z)EtWfs+HUy!l%r?dL|QkQ4%SWE60jk&jz%p8)n6LQft={;kS(rpIN+IJylAyy6c~6 zY~T51H{&M9XF)tKNE047Samc$lj;hPO5PKI-@ZGJShfy!OojEuzp@JO?j#f)Zv7b&PQ;dsCwG2DMe-b!I*(^wZmTyw# z)DXs$#)wc*t_gc_wQD@+D4iZxr>+OUEfFH0qOH#VJVjaMMQNONu$T_*+v*Y)nk4Oc zlBD|sdCX-cTqV64am8}(e0-V*E9`q%tI=nV-OkDIG>%#6z-v~~7*8`+8|WP-cehVg z*O*55bRS_aj7G=N`$DPL69VAeOYA8P*^39dJ$E~Zcu;)8TTEPgIeeS~1FmoFthJ6w zV34-9Gzw6FN68Q80&&wO+r4<)0{r2VI($o(@Ut3}C zpC+fbe3ejh67f7qlAXHpgV9xJHBY{oJ0a;3ZhefkdUV`)@`6SB*}xnSvO==j4-LFr zs-dk=Sh7!t^+?>Y(56pQIFF&^ez<>T0u~;X3~ou>j}9%4j0{4pK5VOd-=wrPwV1xW z4!<2>f*IJo%gL?*Lko`@%ks*7>{6gT5HCP3+D zFq%XcU=SKWa}-HPr%aDtu!S|^Hucq@~Vj{o(pMhz0R^jq}7P9 zGcu21w(r8Gb=!HjOhS3u*6S6A-XthAHmd^xzV_q!vN#6!84vlLZcKaNba&|0LeptPW0Or7o_oYra<;v~oe1T9 z5xK9~`2Bs4c0Ie$6@UIML(Q+#LQgur4%I;@gQej=Di+7q1qn=Xcw`p$n>nOMzw{N( z3^QIK6-mbVlJ}b=tAEgeqt$@wmz@5+BC?z;!W*Rg>CWt=mGw`!oBpPz);E0e2;@)m5T8vDlfR>0!#U6H!#N@tf%`kw#4v|D%8`?E92IZq= za^H^81~Hvzp4}^VkX-XgN_cfS?PBeJDBoQinMrVN*(oB zVI};OyA)y|9=fad%7Lh(wW6|Yxqk-tSiSCFyRzA*L80u2`c~14{R-6JQze=#&C62} z@SJ9!kH0sQ5Yzf)a}&7b>i zx~Mf9^__jQGKxj=9@=)*j9Ot#Gdf8Oco1cJ_N-LPBQS;ZP1J_d1 z#FZ3b!r?-uunLx^2Qh6F2C2!K<-7v?^R?61xuV-PL6v+)IPy^{ERXbAP-<2${>=l+)5Yi{NyoAIU>V~|Y~ zkJ0d>15QQV2sPy2U>bh>U)AKMJD=Qjd@0CP2mARP7<^OTjT!(5Q+2>43skVfy&E(|{vP zhx4SZeAUI=^)XowwCM@muB)CYPpa420-`+=u*NxU!Pured`sQuJgP234;*w*Ctn-( zeQDd~Yu4s4N&3Z~q|~c7clo`1|N2~pRbL8U58=--qk(MgXHSJ^kR;)`*9c^&56seM zl-EjOR1m`SagAaf_$mZ&et=^GWcld>nhQijR!?1%0>hb7B2pDDN-W#(fJ!7S{Jv9( zR$z`Ngo&O&*WM1(>3B{r&W{<~w%gkyJZ&-;Q%(JeR@c}LA*#4fZ5|kI?0_tx#BBIp zBh$bx>wZ?&%i!dXIQbjf>D#|uCtLOPx6kb<@*heX`fCsO6B;fv^SvO?6&XdHNq`SE z3V-c|oCCdneA8e~H+}uLbkq2Q7wIEtMqSHjaIi-mChd9LSzMnPi7@)m<2QNLhD%5H zuoF>W+7$rVJtPhAyL&5p9W9FcZw{t`FG0H!SN`$_@X#a-?mu^?%&vTq?LPeR#yh#+ z`uq$3qb=)W1O5|pFo*evEC0#fI5}ZkTbEh?-jA=+OQ$ydd$W;oADfmS>o7nz!D)W2 zHz;$j;DJe_L=wRVPL9lhPZ=TqQ28NBA8IoH?-fJI$3x*I)Blr{ZD?RFCf>$Ni}PKt}!<+`jKT^(!Gc*aEl-XNFfQi!IY*LL{+2B0#jUePc}cnJn)LY&e1_rpuH*KI_dRFDRB=glvE zGTCaY;0#TOx&FbfyRa3Q0DD%0IKeU-dyt2QZ6ROC?np)_HlG^3wFZSYr|6z@mAW$N z9 z^WiQDLzkaRbuw{UK0UjaF2APiR~>O)PHnb((6P0V!hLSDt$UJ9zKc8b#3S7@Ne?0DAzs=K4SXOh9MiR*DP*bzB z(&f;vUSYy}mM_|}H&f}&-Fo%2o!&&T|0w_1vN23t*&8|NHSppVcJ60W-5cy1&0(1017!khbQ+@5I z2~~DTVY0j4qj{AWgfuVu+SUI;o$2grQiz6AZAld}X8vK|mupM1BS$mIhL$zbTXEC` zqmOU&oiPI__bOQ~7(1d#FN|3<0l+jJ)Av5I?8>2taqF3wf#T=#^vs0ewiXf&pN%AinU$FgJ{rx`h>ZA}MPN@~ioS)A^3|@~qAWY47U!fQuHAI+HXATu^eUW$?9W%t zqH?aIayzX}Ka1-f?}>~Uhk4Q^G$my~MqHaWZI;c8n=mdspt$cOpBuR9 zKAo(Zh+Rl6T~k{fR+LZFAmd1KDP=80Fyw7tGp*p(Kt-LI0y(#)HZ*YmbT9r@R4)yf zX7?K1?v(IZzjcOV&N$-_%0jH$`n(F`KH1J)zyjDbNvtWqS*l)m>WAcfr8+>kDK*vo zs_UDMQW{to`Sax6xVdr8GOPKdi%S~NR* z?w`5iGQ7#fP8e}i{aAy7)y_(b8&nGTS^A&^X#HKoQVW07%LBq;>$8)xv|2GPc23?C zd{r8IEZ;vVoo>k5g!(GExaaL_zbx%}fa+2G zLP~$UVtmxK4}Vb5e)}oX5=rq}4FLngsA8u%>$a;|@Uxu~2^Mg)OPw_I=h@mytkEWD z4ncCM^tV}=@&k@OP$i2pqx?7zq~6oP3ludX#p!MSYm}Wi&RCvp!u3}~ zf1!~2v;Hs5{5t}5O$E2tD|WyB!D_-oe|POHTFH>mB!vmsyfQn5$<0#>%Jnv>eCfu# zpXcjvzylQhMW(ABVChmsmeGKghJLSzVoi>N=;|b)tuesqtB2*}76SC2T?4cGvl20n z9_bc4tcrYbt!NaZjCvCVWh6J2)#i{!UT}vzrB!_kSZClGk{EZ?fa17n3UV}R(F_Yu z;hDW=fNy2QV$B?*IHZxlRML4w+hC&9>+UQUd?|Kw(Je4d~@2;OC#u&B6G!spZ>cnBPyQ7sclR8+nk@ z5yIrw6T$>fL_^<}^Xn@C4y`FQ zCqC=(&xQ1`Kly6?51iKcLIMDH3{^V&+H5uDvUpnj)#1B9Mj-kXB!{?ZS@itV(=V9E z4F`BmsH@d1L$O?Df(&#iWL7(neT|h>=qDlozd9aLPTU+uUo9m-d65n4)zB8O@fwwm z-LKE*(70CVh#o7B_LqH#NsDlxFO8*Rh=j-P3mPAJQ!vQxdV<5tp`~`}YSYh$L!Ya7 zIJg(z=6eJ)VOs_B>DEJlv1#wq)4tB=+Eb_0)Vg?#6GJ2PwnUC_p^1_$rEUriF=iF-=-Tcd zKgnO{C}3r3{}$za9R|o4*8x12ntVP2)(ckQ6T#DeFknrLHFa^ z=U#0fRjd|BH`kCRNnD$OK-s)qQim_5*0yhzRYx(-;Z|x~p)PlkRdSN29?Z;qx|CdF zH8H3L+a#U9GN`y192$f?dz?0v=pSkf58|yk#AvH2OO8Lqvx54ojmnDb8x)Cx=L_Io zo>8-B5&s@Hsr46S{_<6hrN7rd0i*5W{2`Z*DPvp`pR&t)S#K3@)y&EU&nS-@N$R%0 zsyUX-`nsaUA&OQY4tG#sml+@}O!X|#gwfyied;StflbG}4zzky%LN`RC?)|PHlgC- zn}V;sB>5nMh5Q;c)wmxJRJ5ZX|Aj;32~Xd_W-z%PaZV$3$KSWNg;M>`M3Jex4fupp zUV)h(>2i2B$blK^LDBe_AVGxhm;EsuBugu(GBCjF2m+Bnc>E!;RJiG@u>0 zFYR?~wwNA%I`4{Rj}7u@{u6U4zUtVV&Fxll0c`J~LE2AR52x4`S^?Xr3LJ)n96(xRStqk@PAP)HA&TJs8rP z)$%!7k`?0heku!L?z5>1U0%VpI^tNKbYn$!No&Aebf8p7cG`keF5(>~s78Fb+p6mm z^{uE9f-r>R7NwW1ps^aWX@7nn?e?^^>M7%vz{}W)(v8hVyncjyjRBdIz+Y?;i8u(u z*8EFs`(PoSijs_`bwahq?X*H?l2J%|Q8~13hv$2ekxdxcR;f%z+-!#sy+T+EofcJ7 zTV{L{pHJqHhwxeI+%BZtBX-;^zd|0xW!(>|7ozuz~K0&I#N!q7LUdxu0?gMMxoyGEWqGL=r z;qc3j-_Kd6Qz%9REEeY|cWnIK?6vEl=y*`XjGf|{mzL+I(S+VK{|cv=!WHgM*_D9) zN4qdf9MO4Ec`fbd7RMqJ85A5d;rIsGw`?r^!3K=-?4z+HZS)g&pgLOh5P`Xt+Jdcc zdU~uLp{A=rGDp;6UxUFFx@TKfxM})UWpuVON9UiUuRAGMCZ1?+T1cXihh3q3MV}b+ zS5a1O?q9Ob?}4rJ=R?d>laLnt7CbaTMp<@7adm`6eY}cP*R(Ij`C7i;Z>1TXOHcUm zOVDOD4ONR{s)hRGE~J|5@$8+K%>_A-OC*x0gQAN{)6NnsDM8P3}(RDId|4c*>-3_3?Det{#8(uhrpcy7@ z)IK7o^iziFozHg6W_qOL`ukwSCJ@9OB=*qR9(Vr5KA;aCA{Pm@L-THPQu-S-dH% zkK9`dKVt8RS@`)RmgD(ZE0^8x5!kD3!Gs{;aHFqqd|j-!N9eJw9*EE;ZmB|_G&-C@ zO4nJDaLIw)I>XligG{TeQb)2rbQIUn&=9QkO+NjlPq$PmG2+wS)L@{m;2J;-`fR8|2%I-2*f-f7lD(>~P;)fS^SV4{vAt=+siUbAAs_IFv@?B=YLSo-gewspyHcXI_2-9lCPv6w*Mj-0@+x94e zln@kvUmWdmS?b{STrK7N6jx`hl`f7Z z2x-+c!GSW&FXv03hnf#@cr?gVZRCsI;%r;7ujZVPFe(o+~hd~o*v2K zhGvy|ODm+-z-W&GkFiV<^bJa!UNZOs_n3hkbtbJ=ku;(!DrZIf=ec7Z8|o10B&VH& zmWaLFzdv%j-hBcETwG1!omvI4gdb*k3`M|wg3=T~B2JrLG+@{Ap|so2 zuObCv6=vqM%1MqfJ>sIr3tGE?sf<&bR}U$=ug;QPW4Es2F1GVmD-?$%t|Lc9uNO5& zD4L{EL=#9ZB95ZluszWk+X4S9B{GSokYI6~r{}w`pMq~2{%krm9-ocv?dwd;)A(mo@c}S|DSwr`q+*TM$bn6E6Pf?{~^|*$3howQ86R+_Ck>N%TX?DHZA8HzD#@k z(?+z`W;;>t#)!}l_%t*g?1F=nDj5s&^5P%De9AKlW@9utR4Onc3jx_kb_$Sc^1Aj- zAJ^I8wQ%!=VA~kqV=aoqHr34SPKBA#zY6H1hg`eHxVdHaL=G7mb3zH@TBUxj^)siF z4@q&JWdz`Ktorg&m%J%=*%N~^0Wovpv#j6E34_8w`v9j521BYVlCY$=tzTkJ_6+~0 z7J#*)0kK|T419R3K;2utF5B?8I#HZ}KHFAjPA{WysfXJ+M6lC?0Ym&Pl?zr?ot>+?76$QdPj#k7H~Sb)s_pW*iGs9UPyQSg8zXJhC)$$N zWG&(c0(f|sek0+%HMRLW7uj6tG6|0bG1%$m^SnY7KoLyq2UPWtnFTC&vA8Hq4I`TyWJWM-wt#C>9!@4NMD2D6G z0^=BjBfk~5aDAeO)Lf!bj63rh7nO{hLQUeIbC|5CSq*Qv$*(tf{X-wsTA7m2<1Sk6 zWO35!3)aRumtPlKEI!?Do0rQ!QPzsPIpuTLhE`R2ja(w}(IwwV8e%NIt+GUC@q;xw zr8yd-W)w>?uFxjF{Tm=1G&VyD<8PO8roWkf%-6$glY z_V&#v>fTN0-zZq8f|%Hyu!68E7mLs)QJR6Zv~Axiu*BqoLbXzY&<3zmW<@X4D zmhM!!q3|2UMf|BPEIV2$QckfF)X{C?8Ef718IZ(-KYAi7ZQ02BQgze5Ro!mF2X;#J zDfpMolTFu%e}7iQFz=SLN*4L$7I{vd>@!G6L6MEqI}U_t#_LA7`t?{^-CjzTxEm1o zC&WqT7>CWXgpEnEEHyi?`(TXgPVBd?8hpK7N+6N}{&H?JK8r zG|XT*6?4*F!Ez_zFGa^yCgyHlSYQWDTevi(vp0uYUztb4^6--TYk-Prk-WqkE$jxa zYwvBE#ZrMPsJ?nulH(=rhEFt;2E{zkO^fO?mE?Fa`r+PZfO<$HJFC(hrh7~+ zdxVY5qq{oyIq^8gnaC`!G1@a2V4&eD zrc*WR0S%tA`_d<~v>0LDNMAZ17L}}^#}%Tlt?t9reUuS9?z3xGvY?5XWu$eYQ|cz0 zs+uHia0!!kQoI2T9EJye?*-asE7s_r;;qUgrK=VS>fkmJ1KYcjj;zWiS0)Rj(*+Z1 zb8z(92v<)Uq?Rsz35V2Xxq@m#985&TaP-G&?i0qA{k-}@rHk^-q3c;AnL2E6tE71r zF+yFclU4h4MPzgyTC~b6tdiwlGrC*~#C1Yv3BKL<*}6s68s@0{siMjg?(Xz+t^94q zVktwsQ<>(7M>ZtPjr80RSDPaWv?3mWBK|DF#`JG~cZ!Wp?xxQCyC?w09&bkED1l5c z%>1`&yg6Fag&0nFP~)}JB`otS<6!4G+i5K%IUJhSCXUO9l|pq!B)O{mYCrBZ5zb}W<=0d{d|5L4E=jWmI6H-6WZ3MbBHVbE{8Kd(%V)O@JX z4;`;-z}7=NQa4ui5N9!bms$VH99FStki!wxA*>Zxb27)sbMav@A*FCzSYh3FUZ63y zqcYf|H+FwfAK<@>efHD+t30jn4&&yVG&%cVB&2x2LT&LgHZKiv@ORv6=MthRcAy6m z_a?||t99(Yy$Kvo^G{65v)3oGmWBcxCbbug<6sl9n!l`H-}R3aw`v8$|6wc*vUV5&>%VpME^ zrGFAdjnf(ru)LAxUyCw*w*yx?%x5c0wbMd7NA)Y;^EYHTg7LgNnP0k_1>N)#Z>VmzKhCq`;<2vcx9o)^?OnSta!ZRvQg_>vm& z1EQ7O=TIbCzUPk6`q1sN00%S(k8Fnb ziYOvfJKhY7GiO2NRbiw{j=)3>2j)(tG6gNd^cm%B1$=AnsZPTcJt&|ib=o$C;wNRd zOM)ufp3G=>BVl;`Ub|;M47_FKjuI@wOSoBEAJ5VciShT;#N4YY|IUH*rLG+iHcJ?VAeZJ-teg%8lM8#79KL4snLt|g zu2@!CR0-qFKz*nIgLY%H149}VFvw0zVaJ80E=W$$0sU@Zzh0xNqs}T#Ym@dGoYkv- zR%c(?9wFx1$L2Euc`lahnzb=B=7T}6i9y#E=rO=9g7#XTv=5Mp(+Fhk1Rokw3UFB= zUlw!_cV8D{ZI$t&pk(NY#@f`3w=tTSXys0kQMeK8`}M3vZgb`NRbpx*NLoY~+x2y9 z25k-sI+>f}G%y1@&Dk%`X~rAp5#ZLaeW)3I{5VXs?3mH&4D08_f{C2971Nb|wcU>g z4Hs{O-2aV;u^(VD65uJ1*`hmVR1WtTwJwSRu}XZPs}jTDWJ%T|_W0 z7mXR_mHHM-nqxZUXAz3BZg*f#&bHBWR{Mn`vPQN!NKkJCGIrUCRA7emGUzC}ZOZxYeXS_17ApDxm`1#U0oQH!& ztl7jLx}8619&ASGPP>a9f?tx|qRv>zcVD}U$Ge(JT&?bh=8caigxh2iSPFG@^3MA! z*OfisA{`Q@W?hrc1MU@Gc3W)v*34)!;9U$QO@8V9{u4u~co<3Y)_n&v$<6BQI=H>& zm2;Of#o26=LsBo!n$`3dc|!_MdBJ#6)fYAEOeU27#nmlgfP4!Gse3t4Xi;os2_v!Z z^#b8g>*oB3oiC?N@Q_%mBXufZw=iY7y>BnMoGxC~b>sYFXnm~Q!v~bb)SQBBsu1r? zIr*{m4s{o}y9D4MIp+JG;xYUk+i1VrI5x`2ai%;(@nXY21UxN;*=ts95uqrm%U7Qv z#%#VZS76$SxbW(|#lJ^?kd_`X$Be+?j6_<218De*-`r&+UFH0+=A^B? zIRK)9BRThJh2rL-CYB{%J{ZLRo9_GI7h}QpL?48qqyr!X`hzBnQ3G@PEimhHMd@F_xj6k@bZVlXB;alrpH=U@CC9qZA6);uaL_?W(c zPT{W`c|O-fK=h|`)a~B?$X(sFczqR=_!_&^H;qL%=74FYZ(=;v1TltS`^l(w@h~f@ zT<2xw`3cy+>Q;uU`JGHAx_jkpGI5?+!=q|Pir`4+q$2fTon@m%R z?SgiEHCCqGZ9M_^XV1@fsH}P1m2L|wa>-osxYXKJt(O^cDHPurT9fv(D?vyAZvF@k zcMbPH$0e1zi~}wmZzwR@E3p<_aSV2q97=xN@>x=}(H*zy3?6Bp_!jiF(~{#|G_N{} zaMa>!d~8Oy*TFJ_4-L?@%*&$u_UZG!gh$K!wo4N^LylFM#*RPzk<3h~e$zKLE(4t$ z4{gptXEMwxJCj9dP4tU@2btZP()fA>ky?enjsyb|w&=*(Gcg>8eJfX6!!zPs0vmtW z5T#6@S64`Z>!n;GaNwAVuxeWEv6`;#=eQU=R|{H`s9Afr(O=G3=5wdPZb(7X z!&BE+m=rh}rUhCClxqqr4(lihWpRkMz6rfX)*8HcpEyk$mr^tn>506?7URiihx$>( z;|n<7|FDxdbeq5*v{zx9-)i37ew;{5Wr` z1U&r!J0%~MAS329HzQOgEY5nwBEIPE;8gP(`8pXD5=EFw^J=SQ`sU+ZZ(VtXI-@m6 z;-XQn5h>Zra~V<5)bb|}an%v($k8i@Sb<21hFme20|_K6K9E|ATIGXUkH@Ityf>)Q z3E`=6dlvYl->k+|Uc;-B6T)yY2CC7YKI&nB(wcpksc0^K(9#eLFmcGQj(WX z_#~#Ifd~)t1NOMQ92i&2545M_kCzxDV3^svj#h^SViT43u>b1Ds2e&mmDk_PG`#mp zQa*@#hGG`#<>8^++{}^N?`bgQp^?mctkz&cNztkNAhfOJ79iDl2NMH`_00glrLE}) zCpo~(j94k~D^vZ&C-RX130j$y3X6L7EdH>%dI~h75zGvvuT1-)o`m!Ff}ki^Pc*ee z$Gvu8qIjZ>UvL4KW9?d8eZv>ooFqN?lLE2Z(1DxK8}kYz(W2iMaE$MJ+xr=iBlDwL zwto^NV0us{k>bI2S7Dr=KEZ$!QI9RHgIPgOPuQ_%yq~J^cRGRcv6OjSa7aWRwRxAi zjI9CG4WhpsK8fOP|2{bfDr~i>%G3a@#-q;AqMe?w6j@ZK;3tN`Z(z!q#r>4DTt>F0 z%u)S$5N({iDx=fbTx(HX7b`fYTv@*MFNJNtV-U04nv@xMBMIR4R+DY?k{n$Z_flUr zcMCqrCacF!wY>`I3ayd#)Qv!j_+KcIJ`d!7}crBmr2q_I@(1Uhk2L)>SjJk=x2bw z#$-_p?q79neFUV{ZFt~?xHdBgSSQh_l+`?fVT(-3{x!#BUhKj32Wf~{d|61RbQ_}S$^Ix>uX_$m@c;Jj3mbIMTE zRdBMhH3#xwcQOu0+wl|Em=!1Ywe7<>gd&M_Yh}hn)c0yh9jFq?75WQPi}@24zC`Wu zC-l!SZ^^r4ol)x$Lr+R)_-DQkA88p)Rrf^WNakaHT8LaW%^)KN2>F6J-_(AK&@A6AI*R2vdZ;EE zB%fAqA89h=J~bb@9T?(ZtHzrKq(14xvG_zP>A|^3L8Vuu9@1k7`0!3zFGr(@WZ++g zh>VMSNpx1GC6+s=VtiDpdgZ3jf^7^IlOO}zo9ie<3Y3`*sGBDA^IDHSK;O$aIW_mx zgYL@7*OS;5ZoF(*t>=! zTGH|v>kHraefK||ElIdnwp*F%$Ibp=Y&quFS->D>WxS`RUov=-zGzj}-5Ugqr&3vA z<`qiz)jrEFB>euGZ#=AG+D&nE z)>l;7&^lUti)nkpcI!4TWn_Py|Iw@wy#@;eYDZD@TP-wUQfnh0&M7cBQ|7S6?vt!G zSIa3!*D6K4Qhd+IA*^DM#)-h!urlU3VuG<<1tl?X0!Tdz5jooj6WfSon0=&tqgWz~ zW&${8_veVc+;xviN)#>x>4hk6LbN?r4VKD@gQO@z6L{0>&e-_Itr`9OV*_39`G9}e zI*rl=befVKpKE2P1&59#_?xDzM+8H%Pj!H4Es(jWJdGBOD8qRNkNvQR$S{%F$iTEo zl@uV24S^o{ubqel11=IusCGzjOM!UIbVN#Ha}M*yd%O@VLGahy?ZXH}Yt^7XQW&=g zUY2eX`LQeS0+*>^Uq~*bJCZm2?OU(FI;xY+SXuozFR8nQv+|jW%?n%SehMU0Xw7W7 zP73ORHOVJLMJjQ$ZvtQ7#w=_5x*ue}09`UI#9_4VJ9G{!Z*JX_?que@S_mrn)4`aTw8!UR%Uw9iOTbyH*uJ z`!i_lWSxX)1TQ7hrP}p-o1jEKG^g(92>2VDRlt&zT_K}BH34pC&GdSAc7=G3G-3UL z@JTMY=1T3ieoBvQ5o~*4gvZ8~JVk)M?PV0xaf6QV4rf4ICeuFQ5(LD{lYdvg7dr`; z95*Z*C*zt?mh#v!KkTyz&zB`Pu%0H=K6=4sU+*$v_9M%@sWE>)caVH=dst<@UfP3k zInH|8vrEyX$g;qcF*1K>%#(F?mbuT8GY<=)bI04 zf8R2c4_&vTxRfcmQMcY5ca0_I@3HuR=x{ z?s3fnMk^Q36H3qn!S!ycfX;9%Of{gjN+9FBnF(H!ktJQ)2sb+>8a;8xgPL zivtN6FgY1DKh~++&dr0AqLbU8GDVe@h_3A~f@kX*RU#ipl4X6d3cRvRgTk?1w;2I@ zsdQ=zcd0=2$lMqplxw0VT3(l;&Z4+mvL#N-JS@v^rO@*rJgbs6dIh0Lpj&@bq`1_q z9;GD#e@Kehb!_a#;Z@FFqudv(bW%-73I{)v2+$6ZGkF+&G<)HkJE0fT!7?~LLOZ6N zb{xoipmg3OyztujN8;KV9&q9Ocwv@QqRjn6EiUav%C*36Q-9FlbAO(YSjWrevg`G4 zd9x06o+uxO6096kj`?j!68E(sbEkWP$~OOq;g%rJMj`K?dl~_Ly3+3_FSdKtfclL$ zmyQU2t{PA(9(A3Gv33&ftnilHT~V4oZmPE|6=w&B(7lu`ER*uyp&0L%$D;fk89wojTLG`853DeV>iF1U>8pRttB7|aHGY4 zAH`umr}5(UfzJTPt|0upZq#KD$|Xr~Bem|mzkPb6s)TST@6JudiRRE~e^R`NAECtv<(Kj0QlkYeK_#lhKCH$kdR!K6Ue%!UZ&I)$E3jT#a!@bA^5kmJ7%BFc_8g&Q@w;E zp?-(m;3{l=rWHKgdg$gC&r7gRv1~^%+!i`|%l*T!2rW)7y*em6bo&|s4u>v}Nt&;B z-_s$0h%kB@VNMaPD?Otk$0FKALVj?w%U{e06!_xaS-2p`ckk4X?O#8Ip4a5WDu;Lx z(DL$!UodArRLqqzREo`Yh)7fmz};>RM#@U$*!Z$F;9zjP>#YSk3EQ=H+Xb8(r$$x*6@eR+I_va^dJl zF@K@*1%E+F69cBj-?_zQtiw>sY!Y>yxHk!9#3!9QCN|2*j1H#*_0fBR`l&vR$jU@7 zAlOpMSHpf)_4Bq2!8$|%5+&wgpf73yZ9q&Pm3sTq-0PoHfZ8?MuU8K4jUZ&J1wFC^bnI7^a9aoHhb2K9pfEx1 z$rV(|Q?r8o@4rm?W27<0rwAAs&-3h%Az8-p93?u-Z9+cBg~XIEhzI6PPtS(U0C zNlFT=FAzO6=kOxeGNcPSZDA%*$cr3+57IH8%i1I71pp!GO$#TNs2_h!Bu-KiOAEwH zn4h1H2%6Y4P0ama!Aj8J`H?bX%~}VgGq)CBnxS(r16;V)KIkrU0gQL(VkqmBCyB{( zFAKG&(QoZlSJRL|m@FjK zW31X0hz9k@_|$&OU5S&g(qf1^!CFY4tccZ?OQ_HYbI;x?lTejnhH?lo?Y5KFob|u= zSGg_IoWn5uDE3HP@;aYX&V^C2%(aNeT7VUH;=-2XlfqlC2nuX#p7H{GQ5dRbP$^D_ zvwg&&FAH-U!0uy^#&%$!&K6PS&66NZRp9>Q0@V1`8z1*tWIp@0rq^)w$Hhy|OYhgN zBE4mPMS@LAGvK76zq4P%=OuqO8yE)$P9WGQDyS;CXWFl4{lZpW0k!x;rIzhg@O zz3mk4<*d&l7v;hqR83XaNH+IrqEbCYdnY-{m@Mai1r8SR>7&RFqev&CUv5`cRI@_W zAva5JR>`u1tmEcsZXS+)i|P(xt(ql49MqNQ*yryeby-&jvQi7>8Qg)gVm9~gmk$bg zSyj?afoP{%);&dz{mL|p;t(hRlq({uNIqLjAkVHP6IAlx|hx@wybH4(D{DR+FIy9&U7*txhDf3AG%rD;xZX zm{eTzPJ~e?nZ*ji-MKt+vxjV}f)|o&)IYGq2diaAM$u^ad2g2Hh(S6#2{~d!{7J`U zn)I;T7lim+Ro1AJ6H{UxqC;8My8*tFm!z>qrW-V_OZP@*F*Z#B{=RslT{rIr;h_M%E+?B|muxCl8Swz0M&yS+7q=JofY z#cA-QVW3Xym5`wiH=|>nxyWQ-3e}i3{NdETh^+;y(2!zAlFvQ zO7)D+d)jF!4T2Dm8u2=s>9P+6Kcr0;WtLpi4K%9DMpnE+j=K`9d2tnst_74Jfz=~k zx<;@}_wi?$ubDHKRZinck$sp-noJSZIziU)M8N-M9YD(;EJ;wD$rg}B{+e8zN#^Z!V=~F4%EO$why(a)hipX}kDPCZVRZIb99~=8`lNeG5rx!sP;(KrGaw?q=xH<;yDtC)iEbz>m~9-9qiasTQcx zC*$(SYqVgrB0v&1X@ty_HnQ#jlx!_1YN6bmDry~Bcw)#WB*Tv_XF?t7uj=lGp)N8Z zn$41Cd9xii_^HSYJ6YLAKPP7LN)#!{`3x)^wAN}x0J69jyxUsR41E|H5y02k zu!F2yhDA16AwMvZO;Tei)V7h;EeQ4qa zahkUPY=?&KL$lVU&Myg%TCM!VocFW`j;x!$r8cs#(h@m@h6-^KTBVhik0$cYKu`1> zWL<|H+p@m2+HD2k#i*RhDJU|+5 zgIt6*o6V&pEpnt{sBuT9>}^@0x2zW?MHJoHAqyA8pQp&8mQ)lm$!%Qr2w52+6(HMl zQFYZvoMVSHz0LMne$O*5r2%Aue&7CGKQmPdV`4ZUx%jiijU17vXz z`Q`>!i8Gs1ygJYkg(2h(BUR?ID+IOPb9ESSbJ?Aanq!m#F={)$3vq%E?j zBb$Z!>PCj(VuGF%sn{w`-~-PC4Qj9-g4oDnDm}|RLeCa;zXA=oaC{IU z-KH+eq2WZzLs1iJ)v8yg@w@@p981I7GR#IOQ#hiVavw?n$tOs^E9MwjppaR+UF|t`C>W?NIneqD`aT{3<4ni$#Bg^lQrM+fA zcHwjFkjJxJYe-hbi`iNwfoBrjrmQ;;=WCc(iC<9lE(uosyQ|4?55ievYyOBDxgox>2uK$ zMJSGPeOq#Zlbx=+q>fRm998SB^6CnA%&HWnMTc4wsF0U1Xs8Tbx)UjjYD~meLX}6P zxChx+C=6j|*{8)ArH1V}K{zr~m8uU1y2_#-f@J1204r!POB7nn55teHd_fvaZ}}YK zSf(+Bc8KfGQ6dtLFf1P!24Q%Y1}g0${I1+a7R?NPEAo)H??!eIL;2RdVE_Zt7V3UE zhCv8eBdCf`)iWaBsApTsPwc< zUMkLz7f#fPEbd1RqSRA^_q9-@Q9wk1cuNQ?#EH`nnp`MUX%GRKXB#vg-k3=BwoKE0 z5q?^3QCT33eWEp^;VCD2)Q;i;P=U!>D2TICWM?2%gfg@1PeMKDx^3KT?E(i`mms&1 z1y|k7wqM)GF2f=8m(`Q-O3opRTI|Zy#dD#datE?q_)KWcb#903O^^%70wW%o(2cSe zt|=}PnIkE(w53wVa4BtMbKLRxtkZU_;G%>d5Ez_8kgDk8~n&BNUzUXU7 z#ip=zzo_Cwe+scHs!iaP0a@&fyM0wcINYRl+bOatS~6b8?I;YzZUG?&7s4<)VjTM( zVcgq%%s^0!6Q+yBl#VUn;2mTGj|5rRlgRFk8y==(2FsFE@uBT#Ay63fI6*Fq-!rN? zjAyA~h?4-DOvPE-T0^xB12-pJ`Ql6D%ircU zhp|yLkmecj_=W@pzl|{-A{!fo5O+9(tXMK6s_Uq3yaQQXuOK>>b8`WNgky(Y2?%mk z{V>V%s!_r;3RJl; zjAMkgU}BhAs8>GI>}m%^kx-OSZOAnIt-yvwQ%D=9smwjhkz5DJ7Hnj-P!EPAJa4Ks zdiX9on=Lmert7A0*?OPa{gT-l58v=@vB+=13(FGI`1`@4s z!2G(oZZ@0io*?2dliF-m#RwM!31-x6Z8$53f)j@gSu|pxAH-R-6{<>>hRr$+g1a@i zzBmLj=1V3W2t%&MjU~dU`OYrlFEwnHYjBe}xp#+A%vuZPhe5{jxI z$mJl5&=rvFAk@RNS)?LY%d1&sV=5=eqLkEZR>rPz5n*kqzUq!* zsM0frp@I5p*e5%o$jba0p?~~_W`5TP){mDDOwhd%bFwnSP|8%)%kRYZ0~pr z-MG2ZrnQRmXjRPo2vyj@{*@bwRGKWu9RbcV$VB*0w2_5I!8TC@70{=RH-3UgH>%== z6L$l5ctl~;M4Q@IIEFeO4Mz)=68-?)FU6SKT?whGjqJqO*jm<_BlG=$HV;IY797(O z9s(@^ig%EPsn8onzl@Hl@!E5i6m&Xlk;RsUdQ$IgY~PK9moQLi9&n6aO<`D-uyPKH zZcGGNvj;0%S8m=e43R9O@ARNu;_NYsbSHC}q;NIT&UF=QNqWh?dP)HR{k>waeL8y+ zWs)!r#2mJ^EZ$R$mqpDdJAFAu*%c6sQsdam2H0(SKeQKE-iGpOn+u@t`K4z5KvS_7B6Xya8O~3uH?6_>2 z)*XQAZew&!)sUcNY2~%D>HJ#6Tr}!};)Z2|Vtk=GN!YXYJ%Q#|6oBcb{n)n7v5_rI zY~P@oqusLG=ZTJLYk$=;^fNpKy3F@t+lN{2J-qR<5qT9Yw8Fe+94*wsbi8Ff>fC+O zNJO3Dw497!P$F80)iC}J@#sat9xqEel~^>IsA0f19o5w@&7;w1zPTT(3Pqi$1Q9r( zy@J%X+Ihx_L;=P*;g4`i0$BV;n6}inpSPu`8E| z*6jz{il)(}K!h1q#@79MVIG!+Wi&C=QNJfTr%`3!BASLm!Zr&Wps_kN3}6a0V$s5U z6{c@%oL*(QyT(t$Fvftr&l+|v;nJgXTY2 z>DwKF_a&{XlWJ3LtB0T z>idygj+Sk^Gx3h)bozHc3GVoJ}j9yM~ zTaAFXfBg0qV_pKz!0qn5k2voP!JZu-EKzY>RGhKLcElj)?b9c?oicZYM~CAd8mf2( z`@1a)^pHtRnTtF?WMw-gd_+D-Bu{oL}SJzM8axG%-#BE38YxJ`Bi*99+P2}zkPdU;$Vmp7ol(s@nW7kLbgjia%Ykxy-Hkx zMt8I9{ zzrC~nP1^_q@c9Gm8UjfsdDsHmX=z|kL7BF0s~RauJ84nMqP1!J!}94@^o}VIzOUUu$)%(8ZI|mCtY3|Eld27y6CKD9AuVCHw(jW*{Y=5~s5I>lErwf1S z+(G-Zv!r?{*XNg4mmhAPpZ$5iPbWc=3y>?=odyss~*S)_z#a6SVV#3^*<3v60JxszjA#)ytc|lbOr; zQbO?DlkwDmys;?S~)FZ*Ne#ID7jJWg*=%9-U0HX9muSoLo+C zk5(u^jrsc5o6d?ecVz6IPT%ymtkhqB{PO8?od4UjPNvf<4W6-vvO8c941HhT%ev5R zm)DLS9J4A_eEmXxcU!+&!_jDT7gyyk9JM(45N>y{M$zH|QNg9TQ@*0LFG#ogZvDOn z(cNyfwG_zt}sKC1&uI7)$hkWlm$Hq{i@jW$5L4UO_bde6{+lmsQ^uiFTWz1 z{v;lg7l1BNwkf_A9tAx(8E~67P9_|J17gTlohoW*({VglWoiiD=V!cVq>fJY$mIm_2Ogp(|}*r{+8>_8>^hPdL2>&DDS%0g0X3*E9uZRfN~mJTQ*j) zpKz-Il(Y0I6jlb>?-JH+p@haBtT0wN+Z*F7l+f5!QC2wn$pmMiWSeSN8k<^MnIt$1 zB@))Jtuu1$Rr7)kVCP)| zDQwLaVpC!7rB$~agl&Q??6Bfe!iJ{xFNMvr1DVtAo4K`Z!Yw?ut+{4xqfvLn<~hM! zpp~JvUOOGvN{;~cwg)%Hxo?GkC>u_A2OHO#%5Rrj0+b^QlL-V5E+ SxF^8?00006)v224uI_odo{3UZkwrx!M1p{TK$Vx1Qip(m0zp7PDj-0Aw#>&V>3vqZ)RewU zD_J`~zrH=cygj|V-90=#KEJ-azFprvzP!Et=lbpa_3i!X%{@N)O~ zYU}V~WA}V*_XIpXH#YI_^z!cG<3lj`e*5hG=X(75%cu?f`m!?^7?vyeXnmHwsrPy=bQNO z_=H8o`YCAT8Fzbk?-H2e9-7fP{~tFFkIziA9*e44&o8fGQ7L?)a!$@Jc?0e@w)Z)G z@0&Y^_AfsIWAnQQC-)9c=9f3weeQ{`iG}y7uIshn=*>) z>s$NzgYIjZdLvUx%l~vPuWhyWj)o={LBJ8NZtk*hOH(rmPt30TO#gjx_n{j1kdj|F zI<;6=-(qU@m#qQ^62<K&Cwr6v@Z>vY|MEb{sddD*xH|5x!l9@bLR;gQH% z^*Yy@&&EsVU!rIb0rs1E3N{B0r`(v70FaLWEGGj6lOZk+lPvS6anI`S`U`qjqr0XA zCZ^aI<-?8%Yk3;Ywo;>NUE8ych2vuZ&CZwitgbhwo#(qc-z3B&MT{lqx98Z8hrf=U zA2lb1E@v;FcXR0I{rozV()EAc-hBf%pNPJmz1ZSG+_yQ_ZiKl50lesF=z*)Iy}K^E zyd%|m9tbTyZ?y;i8W%cSD6lApJucADR!K?DE-pTzK24;(uef@^Ir`olRopKdIV5yu zeQorJaE!hG__rbIZENi5>6w6-G>S!z(m-V_DByH6Xe5HM`~d;2HNQ?k3~?sl?elm} z5~aeL2^je{AOecLhEc3cd&6>mo|=QByMw8 z>ip%j95me$kWJxv8B3tayjWM%_jSqK?D{Se*6lY5{`=8I8ODn$E9{Gd=k>LF2A%47^P(~@8X`857%IEpEG#!`w6jt50+BXjE0!i?3b?U zj5kQLKbsz?_3V&I_ZUa}0eQwafdqmA-f0Fjv}e@57a!JpUgG7O?jMIF?y>0&+LwWJ z%6@f@VC4*q(PHI0yCj_jk1de~Gsk*k<|N*K_5IFOs$0vbF0z8%KRz4Wdcd)^Lk6{3 zeLr<*?QIDY9ErxD2*L;B?$JSteZ}@T_8rzvA^9gf-L38E4YjM~MAN7iJz^jBQ3klQ z_RJsOc6=AV+`g zPi9Vvi*ogIJnsn-+SC-arO<3U%g1 zD-x&iod~v$#E8@ci!>ua@mTq>Mip6nNtLF`5lFCk>OdiO{YV^h!`Xsj(RK1ns=TC1 z>;{IcWAUm6mx$}-QN`Q7zx^?qbmCAMZgLHuLwH~~@|kc;5AYt{^ttVH@Y|j`&$5KT zbvt_mx9I=Lopkrk<`lfavbN#vsh?RsdZBtfVrPo1An=)77D$TzxYMSLzYkD|tn@fn z>ZYt;8>Pyds-u=CelJ=zcI5+Fs2(q_=``cv?@ zV4myj3!zdeZnGS-*(jaHaQeWm0*wa2>u@3ncXQo#=l6F`TXta5?_{IvF}e^>_a}XR z!HY%{-Pe1Ib zoU(Ow+8RfRd#(cJ%;NO1Rxp(FhNAjZ~55w9SoaFpn zaiYp1nMk{m&qU{Hp8m@Bj5v!iQDw?B#hSNNn3G2@BQGm>8Hib8%&pM7X=XmeqEdTUy0E{d5j%g#)h=Ic-##)4ggTe>7X zoUPUr4ubGm@bMkLr((h{aWFeDYxJ(> z#Bb9thsSv8>w|T-ddz(lqfpobJ4>`FA&Q_YATk3Bw`Zd0`5F=Mb@<4S#)G;ufdfG# zfLa6@W|%>o8zNVofi$Z7K!+N0I0+U~J{~{RPAI+U*iX!uI8(VdgrOwVlnW>fCw}?O zZ_M&`J}{bDNXmf|e^!sPB!7(>D5t9H2<{f4yplQ_Wu$E$>pz>(YwwZM`a)S35=u`G zroFpVGKWdplm^B~DABriFlb*Fvpg3o?Rs?#=cX%XBxtsvlwpO&3gISFtU!skFs+{O z-S~!f?wzaRUG|DVE}@hwFtzJpEY%fNO;XAt<5Ez417Z}HE6UlVJh-fzgAi^&AisW? z+rsc7umDH`qj4`-ya6j1tc%+%qjZ!-v66(#OgkMfdYKXB4WtSlMi#wfcopF)DBtvc zVJw9Qx_V=QjxV>MGNfHz=t0dH>(t%h`Q~>V!{4$a3#_xU*;6+22Z)6VmXnRa8o)46 zh8Z|__xi_hhb1YNE34_`L{)UvFnRG9H40cjOc2sCbQCca;1XI@8(pPsG?rfVWC5Na zRI3eYRiW?*tP&p+kRt)5EMiuD|97DNjJ=!`3%>5ohVFQQP2-6K!(~!3WyMU62p>Ma z+?fVbw&mLZU;!Z_1KNo!JGnysUC;qdTUz2FBC%jXCDWh;OvxsiP~U}dE~=HkzO^r$ z8JZ~2J(=9;+l(N@N|8P+4=*SYGD%#N@O0w`?5(gh7(lI6dWl_cj0hx{@KlS#Ai|D^ z9U`@?hrNwlSD{?_B+(6G9MIZnhYXIilMl8@T9yHN#f#@rl?w?&ox&&H^+j9oF;!$& z^CvZnC8j`X%=*fg3Zhxw0SJ{4+BT!62Wdxcpzx1Smat$FI$*)MThC6eYX>RyOZ7NF z%d^0g3`!!_6Eg?Sm^vOwUK4gH&$jJa9?C3?D)A8uNFE+?qM%a6;^?Z~_<>5@d67ia zHkN;@c2c~jnEUZ4Q4QsIVu(^b596Z&K!EnAhj{ zwDy*qkC{kYp(&;ZxqLI?g$Dg;SIq;4_Z*13pBp~Frk@E5f@tCu!F=?f_5~o~wnxtn zI2bZrGL6BMommaxByqezUMUe)H;U{>O3b0@!h=m3rcNbFlnxkGBx)iEG&d4 zhs2OenS}$2v;*A7l#)x{yOIme_+hmCUG=a4-dw8xl`-|^hEvcg*bI-yrw|CQ8mC(v zC%%?ZUXfw3{nk*WJqNl(uS%%00XY64{|z2=#$jL$WN=0S)GJ2LSuG)kY-lj1KyaQA3m%^3c6Uo}(AwSPkps5^4Gq>-1n2AsSBGE6ial%ttLEDf%r~n89u) zK%b!tgUF@x7FDr%16&a6KM=JLTQP2xX^^ccenPW&19pr^h@4~%Di~LZw!*h%HI%8A z9GP)my9%y_+LeY7bt6lEpb&Z6c!&z_3lqx>PT!MM^Yoj#_lRemc}bu-rM&%8J1I#y z+yinVNvKB`a>86INq*6j6~=9?{yg4M3t!c9u@)S#kJ^f307Yrk5F6m|6x^TN11l(z zI;OiB8yybWK3&a_gyVx6M}aI35NFF5n}qZ=inm**+)W@yNk(M^7f#>~43gu8C;xMY zNkbJVCk)o-S71PggOh@y_mRz#v?TrkOsHC?a|P6CI=B5K_x?wYH!05p{sl;~!W;qi z=%Sb=blYNvIH}`$t7C+$t6~E=rsz0O*dgR|t#GU_Z3nHc7Yi&{n_jW7Vma%*ooiga0_ zmgt5@0K$`L@@RkA=JaS1H3<0oTYQMBciw>I*yOXb=U`2@Q)kArrKt5)Vq9EPqki2)}Vy{jVM_ zY!JakyR|84TOe#fdVO1+o0t|l{AgcBByIR@A(ULYIgHq7lwFcu9qJt)!A^P|O>0FGJ-|JP!`ex6tf zJfzMnmv%nn66_TKZe^?aAD2eLVxF?vG@jfOfeuoCHkX*7cN!V*sPOD8Szb#yCGAro zL>J&y7(YaA+gpM|+=KgyStT(iz#_8O!kBq;_%P4h+;l?6X?}>7*6b zoZ&9pJsddK?X#Wn(__ABk%lyYcBc%Uxd-hiT`Fti;>g4)muv|ug;}Y(S`WYL(HQ!= zC0y%Q4C+~pb%ZU4huB+e<;92a=ueoM>jzg7TFLvL6iYsA8|{xe2HxIIm#r`DHmMUK zzg5D2`)=y`yK+00ZpAEj#h$n*cUOKSRw|qgHVPgzKN~FA8HE_Hp%I}~Zz)yU=6R+o zO~jqL*j5_9zSn-dtY`=j5z~csdEH(>KNp0_SZLs9_#y&*^d^%C@_e3#ibT)Nj!_ve zNQJ#`FgTr81w8_wuxRmQ-D^^;y)WKJhtS_WZ(p+LaGvg_i#9&jzbo(8*R`fkbcwx$ zJF(Dlwcz;f`R}{`$C)$}k6>>NUy`D1)7)!6`h~yO?IGUd#WdST4FVLh(cAS2vIPPA zlMOhITN^YA-zDmOJ5<}%+tsC2ay`d}ZuGU$GQ+%k7nyv;H2bt;!~bdTZ(40$;LF|h zorb2r&+Xyb+l!wELSuCv7$XN%qE1BW7u6}?aHT%DJ71!1Vgj*`A!y_n@^P_7hy4Ki?{P5XHY zjsmZr_+LG~CyMyJUGGIWgfl1AMxU;BBqh9+(@NAJyk7r#p$4XPHF1po>6~JUx3gt= z>0*=J&jf>quo61F-|>ci;WiO~Mp+VJwNK17yk2D$fo-Z;r0aa!d-cfiE+9+?{3n;5 ze+^2Vd>|GSXar;-0rA}I%8wXEW@%gJgZ``JvxXQ&#h0l1M*k)y^Cr(^lYd&^xW9+; zo>xp~M>dS{PCv%@3-#G$0(>m3-Q27#^?VpAkS)gBT?8!KtF8XAb_y3?`;HRrO*$d4 zzuB65n9rZHJ;A0?j@disBPf$MqUkfvhTGods2=U zdhN~`LFxu=zAT`@l7ondT6ampB8RV4hWJV_wKV}h@JGV8s^4-H44pb#m#3#qM_!8> zGkgtzf0)D*og?ivMe-J3<_H0s4s^xSRf9dzbA#GEggYv(X4h6|iXB5};g?KB4rW>Z zyd3XJfD?u0k_p@zOoH>4+?i^v4l{TGF8r7^P5SUj_c8e2y}J%){P5TPKCB*lo)Ye= zY<_SVn%^6F>z3(-5fi&O`EbQEtnjVe-#2kurobSboW!hfN+W7te$n&x6C}R9fHa@{ z>x{-^aT?s%PFk`o1HqeGGJf|%r;Tc3_H#W4LHM!(L1)Wc;rIF!U#;b@aRaWN*ZxyzjmYXqmv4MYid3oLG74&9_ zi1ct^y+fH*aSIy#Xy^{#2bJi#JN_cL%UHX4o5@#(Bb7c-D3UZZsN4+M*V>QaAK4_# z8oPNv<20ze7W~!}D(fB@ymhs>ZW50;6Q-@WzQi>H4|&*#BUA(w@lQZntR$mT2UWWU7PuZVOf&~`FT)B7VBYb zm6iG325H8;oXe*P9wh24B&3_Z%(=JN zM!?hAT}FaE|DfJF>_CN#5{1w7PnCM?(!tXI+75RQkh6=+2$ z1sQ!aTZEqW1#69c?1&d3EbV`qnxIsB^fE3dw`$=w0o7JZ5T|y~-$rahZcUOxE29 z%iIxQiRCT3{SbkZ#-r}>UHnX-YIOp!=w9LG(JLg7bMu{#!xShpI#n|vHiUHrmIn<`Rs4`&@Jh6jK2eYB4P=T|MT0rlzV}eM@fj_7}pKTt`USRl28UdT2Kiq@u>*h~zQ}!$DIVNQrK-?WG!2pq1wyB}95lfbF zL9%rUS2mI7oZb)x)!s12rmK zuYh`F58Jhk0#}Zx#QX`jz(%hPC*Vh+f69H%gh6jDzhoQXIOU;54(s@7a4r8s<+o9h zr?-GfOb#pI`??I1I`36>2=>0Chc=%|CVXDa#5K1CYe24Dhyj0nTT??r6AYULdlbf! z^zoba4oFa@L4HAU#I~d60U7pd{?mR_oey-TuI|cgK8twptU%yw$H5QQ0#fb7H5Un` z=2pTtiN9M;N`f98GHX5{{265~jr+=px@v-LWm)%)fQJ>uyBm4Q_6wH3>))^*MxBWP zzjQK#BE{)}LXZH_bm&$FMwJ8X_W5`UWPiM>dbTcjgEr-20iZ`7S*NBii%=>Fi44WR zrp-OpD`SFFdBj7K6$wEx>HC&?i^ksv#$ywQ7ENDcH73`XCQz>xc2;?~fL&iee&f$w z=WYRkS0MON&6Wq}_G{-|*q>>BOTOd_@*$>H36(Q*4X|?Uzdplen&uLm)--eES5!j- z$}f!Ns>Jak`&{H@{J(C00qIr{aF^cuXEf&PxwU;Cr3B6Tq%_S(rB;QwW$OGmF6H3x zU;)j|X>Bec)e5-9OuA)4d5QAWx9t8P1$|jqPV4T9CFJ$^!WOXVi1du; zirHPzTLV)+lR{o=UB0CwovNJ}_O<0o*jqA&Vl67X$8gnuUu;fgX2#vf_!V;xAjjP_CA8j(zY^ z@x;{WcxDSb9p49wgbNYHzgtmWzIBoLT9ulsj?5$8Rzo-&3N*n8`>i2izeLwSfgnAJ zMjc}#tL~@Dv%<63uJl+Er8kowgvX1cJ_O^Kb?}JYiyey#@?vRY%v!`(`%jbjmsKU*>1^~8Vse|U< zLKGtEo;r#2kVsACst7R{ke z;K*ufyL81hFz0^Z$^eNx@OEORR>HtDQy+!`hXG{06zskvAl=hl-J9O-gdS_))^T_Zf}-rg{S?=dx}hZ=04 zE`+TtZYAWQT;Gn=`x!!Gu^Dys8C{?IPPme0ap7(YiZO&_mSBps0MY6me#l2dZNuK{ z2^3~k>-&2i4SZE{*gA(Cjfs?pg5L}Xt8ia8V4WX@7SSAH0oT1^X=Sw|B5wnc{DVpRtAzTC%9vcjx5%fvE4cm6lcE;eAADZ z6#O^kX=pHBeYi2gHiOi#Ye7-N7R9{5$nwy!@AAG2+JD@dIcF94THq)BF}(h^wpNm( zzWuhmq8xpPR(0jR(p2AdFC=C+`LNJbfic;Q`z{kvE3;tIw)Q4nTs73z^0xu-|)zS4EG9K|Z4ZQtDS zVUJ}psOFZkaWVPM*iM`h^E>04>*>x_3ggO_njZ0&QJz{ANt=hobo63*A7!nbIPE!; z>w$z0r-I9Rx=Ss!;o53eo0D_jq)hqPbkc)p+X0XnR|oJP@uZd7itd_BY@2n{(BRxL zT`tEHXy8niRo|`UBFUu#L(i~vjL{u7{T3?6o-^cVgmQ&3

#`ItrWoHNlNbTQb1 zttYah7kqEfpDirQGNst~ZY*?uKC;flOMIz7<-_6XUc9!Lu=?$5%$bohkgfMr-^Yfl z)U1{dqy~AaKeleGXbRXonhaz<5{?%}c4syV8P=A#7aHR<&G1zQ%5UgDbg2Fu0x1|N zb<%=DiJq`SaL07CR1$|5q;a$B_UajR!7Kc|HuIcM$KYQgY7Gw)z=2%t0e@3*d|H$x zjgGpO+3FCVwSxvz{@(3UC6TWnW0JAhJRoVe>gYi|;-;8)Zx3vMZvjw*szHf@3pg37 zb+LNwd4q^>x%ap_-sxJQ(+Ql^MJ*iO9I=3+`^n8mSDx1nz`_c9=~v-7-=!6;$LMb) zj(wt&#kF6~60`R>8zXpj_VscoeppRo$ZkUNPWm(y?LB=UD(SB{af;@eHYYyncb!V? zQA68OE2?#<8py1^f~;wmHW$s!?%_<})FGAtp@2cH9L$8rumttwIYWuhIfMncvcQ%l z-^?eYwx>7|zNyo$9^d&RpQocZW}s~u>MAu(H&-7P6>}$uAlr9mhbE&KG=k9wl+hdB z;hWuZ^)w%MA=&>0(A#Jxrq_%^$BO6Myrc=Y+X*{8>+o^~?(UDTudgEyHV`vR8$Vd3 z6z03I(`33_|0&MB>7r~=e3ET^_>^Wxsr*$8%!4j4?@fuLw3G6IQw3<&5 zH0e!09R{v7*1NB9Xji-ot2gNC|A~acOq1)OcybA<7j_PkV0LccYc^)Xvpl&!XOnbY z3f6sN$h-ltbOIAB!G^!U{L^AQrBsq1p{}$VL5`cOY7IKoT1n*ToAD+1$*0>H2_=mB z4Y)B{(+OO8v5Uddbr$uPlM;7=48IrB^e9H*w{LKhzLX1R|IGK8+gA%dsMB#r42L2a z5V6J4U#b$P&zoRAFqCUml4yUH_FpGYXGU(6D?=@;J>|-9327Kq)v5#5*G0fcrs_|q zb;HJ_C;lmv=>?)rtwPB6%;}_R-0$8zsV^IkOrcw_SOK{9H!5(WD(91J)xPof{JQ5O zF8L|0q&*NqWAeYa03-jLQ#cLkc=uj>Y0B2lr4*Rj|CkXj@R= z*DOLjLg*+FWSw3TD)Ffrs2~jh>a*h!_bQX63;Ysb#SX&o4fS%zqH)LUAfKB3Bx-&m zq3WU{+vrk7GkdhUZaYU2L}&6+HCsuroJ+|@Qpx*`{j{Y)DUAfdZ@UxdV~r=m%2bu= zP}hh`;Myft4VOG}@!uK{(V32`-3c)+9EZ#<7ZGHWJrDqua)auc9|W_dB_1 z`qlOxK9R|cNF(VttkC!c8!;o6*|Xu;qCGoaDtIfieY+D;i2?q}6eOXW6Pr4Fa5|}L z4X73sl@oFUd(-{Ou*3#f_aF~k1;arrC#lXna|*@C`>Ds&;$$iQ%uD?Mn#_w6&qt@y z82-Vph66w5|Anc>9)N16_XHW&Ni^mL|HkkB8^KSrOC5e{&Yp#fGZaR7@(rJ%CDS%i zfF&zBO}TJ5QyewPqviVh&Jt=%CU7YIV+BTeYm{u2osHL#_0g%G`261vNaok2%Gdrl z+R!j$0va>r<|^$(GIDHaOq zOxudld4(J~VI0}LMP~Fmx{C$)sF8qEt(CTBh^=gB6QCR1aI2(HXL~Ziq=s!8MED{; zk&f~kC3NPJMi&&xs>leB7F){^ghE3u_YGo}OGVcffMj2tIyZQ0y)6ywvA>N{Lo8$8 znpFpe1oy}Q7o_z0V-}^(Z?n1=$g{pS3r$N5X5|m#_c3797)${#rv~k{Fmfk$LvzYb zf@RRTfNYQeR{&UUBqsys>Q25(>G*8M8XWz*oEd)q4qzNlshCMI`hsdYeZuijes9wc zjLK@Z@wp?gBUp5?oug!P?B5$=G5^iqhQie#5atLVMVvDHs50EQv6bV6ZR`rK+K#X z;zF0TEH4cMAUJnv-J|O$6vAW7KBi7X#2s{9D~+og1)yvc_zMRxoSwh|rWnogfW76K z;g*iA6|VF(Ps4il!7cWu!(Z|ZnaEG*mc6D{kw6mDrKlt&gfj=5yA=NSjck5Zl!&1?ui{XNlp-XWFw!}6!aBIbIMPh;uX@GngwiGA; z{foN?ce8~)^PjTv#Bxmxvp4Tz0$vAx=Q?{rhrFurmMnM>Hm?W{AU!G~s5yH5rMX-| z!lA~zq}aFs!}g|Kgdu6R!7i1;hXDh+_OP-uspl(ZhcTFj*)kG88w0>7tedsKm`qYk zj#R;mMe(R9oeI}rLlJc}Cni+&OZKB;JvC)F*Bf148dtJ;8>+@Aq^h@e+dnFBA(2H; z)I`*8Oi>tggZ9i|a717J*PQCEiP^P_ReBtbg*JdDhk{-2r_-X@9&+}2$`lOX#+M4j zZQ9S^k1qrdJN@el@0H&90Rt3QwOP$mm-JjXlg2o7n_5iLU-EbVbUIbV4`-=(J@Dip z+B?L6q=ek0U##>y4NORul>wGNkLl$p3Py-PAB3RVchM?SLCOAnDI#>8D{FbiOd?7+ zEORc&d-d1aWHq7fS(Kt2xn4$#h64#+{bDewH1BvudY<;S8YG$341zn~++r~tO&M{2 z#I~$3B&QH34UPL&T_#GjI<5(~$Z4EfAJV_GYnFsc41Uu3{CNH-q$vcMmsF8x1L7T~=lzjFaJ!Bbdhd zp0*1+(5g?b`e;EFH`~CxWtnnkd2lx$7Fn(C27gBu`qi_|pOk$Bkwe!R@p;EW6c%tQ z)kH6AG1Q&4-0=mAOGjCDx}6%TK<`#@&75QWbEX)|WWn#(K}1y%C=SaDOO!(%*S3$c z%cr+EWN9=Wg1N|O)Z~pyM}e*8T$gVco6+u!umFv-kaIn!r>?ubO)Dm|rB_9nDdql4 zQQLSa@%{G4oCI<)I$n8f^cG|K#X3G4Q8i>VzwBNf9B!uYgXysFoYIdj$UrgA=`bM3 zb!Z}nJ6EG!;;ZESO$@*NmuT25C@t=@;BTtSvxzviiF3<$T8>V*L*IMsXK7LA@M@M= z3DvKR*-gK0;!ZU(0pD`f+nE=g7lVxDXoc#(7)K{W3zPhb{L{?Hd$+Z%qw7esYx&bFqz#2jF}U0q+?*c?YC%-|D=y-g$(3>E!K1mmdum; z!leHD6Nv5BU(172bB|6M?VdAY;yBoaMvY;R1~&5mR$5uBip9js1+ z>{#?mjG@WQ3Vuk9naX)Dc&6|V7;%48dVu3s{ z`#q_?eavJod!C~|sU?~=@vm~6F}_X<>MCBPfJ#r#?(vxXxoGjhRGEcPE(lhElxHw2RQ90BAqe z{!`M~ff?*3U`~^naFQlp8V?HB?F@F7(NDRjdIBj+s(M}_QrF?ZfUERlyoFOsBE_DH z^M=|~)LmcpUm_=A-)5Y(IqYAObxBrJPS%XGn*>4Z)Pl4~xOhs;e*_~p7$g7GD?x6V zKe@)D2bMmvt#r{gE}NZM>73u>P<)0H5|ERfCM0Mn-AducWy;};OZDKt8`%<_UPHl& zkU0U7qb8SM{bWVd#*<4T4+EQxbJ*efNl}->KboJ2B#GPw8%7{*dwy_&xgGQO9q(Xu;9SiFadJ#xBg?Gn7nB`NHj1D6???0fiA` z%u`E4YAtZAzQ{@Bm$H?USH_lE*jn)Xn{>7cx8q^5bVCb`W;R+2wz#TT>y)46iHd73 zos;Ck(ozallOw3Wnyz5_NRyp?lB+N@tGGO3e-AP|!0B z#73{vjf} zpPboXhK(tu*wpGC48@U0D?fhLxZihQmY@Q9tqq2)qjG{ytJEK3`1bFds0x-7bx^qP zlvm9)ybU26gYDcZc<|!(i?$R9SoD$7_Mz(P!GyKkcHQM9aZ7se7Cxcdjl!Tp@|BuIH10vcv{>VuCZbEjq_c79W%QMQ3Sk_y#{? zgzwuF{Dr7Wd_LP1$I86gbE)EAfAP3L&zxR^iTx7yf%IE%}s7J87BVitpiUAh!p zLe982#$@4wzDBk69=bsT*F-n4=~|<&-KryfgJ-n{Ntm3}6W;KBaHyPhHBLI@7?y0(g1`8}OihA}w=Q4u#HCm{Vy_X|$H=38|hmD=Y6 z>C6bBR2Q>5zXgkrNf!{{CGTx_`{L<4XZ4xkDI=$_!1;Nz8Ck;jBxR2Yt|t6p9-qE+ zw{g z5;-to@x;`}TsYoeY{Q?X^}8baa&B@LD7H25U;LcRZd$&86)48(htryKriK}mPaxpGncDXt_(I4 zM_2oqenqs*`}7-g%nW1t$&x`|gA!Rsod0yz%$yMMSG(mnD)?Br(6AP-zziKV`j`Co zIJM}bt-A|6YaLYenG=I^D&QL`xVOdGZz}bR>lZ@?W>`ZTf?O%j!%IP3SnkP$x9vhV zc*}27L2&T{(^cqN)%ghPKU83H`W7gNzW$~S#OA$OMgckwC@o)or&0Ey0*Mnw4mgry z_=%yqO#k(dlLVgq`UQ_|)9W(jXEH1e1ycX^yL>2Gp}dzvnS=bKY*Zcvi zB90p`8TV|UBmScfVXjY)5KeZjY_0~z=HR&(qGHbAEVqUSB`*ByB|nbsqri4FEMbD1 zSq|M@mFMSl5e*(>y89~D%a6g(gK1EYdW`YIvI;vNGh%h65v-slrsvjWcLbGO)VHa% z(~Y-G_w$~?Jx{_au+>P163$eLj)IA1MuC_w4kxLAwD8zp8m8RN2aaWwNf&pOt%QJO zu0-=;w6ga5V?O__SFF^s{E6v3+J7LHs^? zjx+J_MWT&BW|+Z$62eiipC9J7GC(s4T>&KExW@9X*}z!~)I6#tct1b9JYsRA)J7W$ zk3%f2mTI%nSPW@|hJcea!oI++T^i}Z;y(u;gT0OV&*;TD08{h(rZ|AzR^xF^7Mnn(Jwy5mj;|{ml z0D7JXo9B26)@gNSi3f%OdG0AWta248+-M37mNs(yxz5< z_qR+_PaTK6PBC=RqOx_G?2uu2I_sdMe-g>_Rqo86V}TtsAON&p0+!DSBvvedroZ!x z$1$#1bSyj75cg!7n2UBQ?1d#y?kTd+&*$=UUbwcHzGqT$I5?tcrN?W_NPCfk_8E7^ zS=K3$%2m(OS8lm;od) z_-v{5jBC;jc_Oet5rOvDVrn{9{>R|F~h-O$o`Yw#mwwS7H0m8{x5-nOA+G1t?&b!x6Qu%q8Y+Sk!M6>3D`Osd3l z4#Ba%ty+$gX!$FKU{0_|(YnV-vQ}{;m;bG~xK+-Q}>)I5b-8R^(pTUNU&!&`l=O`YAYYZi4@Uqu_k z?Q!fBL~Dy&nt~`n)H6cnEygDqoIhBssAZh;mlb7fi(Y(YW@q- z{5PukKZGehq0RqP{EyauEB^0T=o1e8FBAX&!vF8S{T~maPYTkfLu%^@Fa!$TopV`z z^y@Zx6yNMxY~c91X1mmSH>;#hhB3=Nr{;-x-Q>^ij`xhByRz`G(5;b>HqTjeva|cj z+W56S3Wh-Ec&N)-ey!RQMF#mj7z*KM62G*}0_{$RBH$kmGNCI;I)#@v5;?tX)mFBZ zJw1#YBvmV19EzYlN+ttOXW^5~l0@J6{yAUo#3z|a!mo`tOs}oH75$BXzi`h)L8DCh zd(Pda`uUUH`*aYR(C_16)LE`(FB71V5b?_&Gjl7K>+uM)gCX?zxb;?_qzkEskS>1D zwvYSGE4^^lttc%?y6n{)6TX%Q2MWPs(uD8>7oABB(w^4w#;3% z`hW<)=-YH2!g|@+?JK);%nN#l{N3kxa`wIjS8x2|_8cdweVyd2%f|u8?{cxV8&ATP z1mAI&R(x|eeQEW!qR~3H#TM6^bka4RXEfT`4AuY6cYkg+&fwDb22HZTM7vg_UEv`k zd!dhGIy|1Fvi}{Jrw7Zo?|)de_zL8dEqrUu1gnS$ zd3s%5_L{+nEkgt1_}WimU|XK0t)2b+UUu#8B?`_aYobJ%K+9(Xcha>Bc}_QFv0q!+xE}hLEJ9Us&|g)Q;R;-xV;Q}qKe4qLF%zNU}rpZ zDqjbn1CKc4&5wArUzNlzuz>$7vxj<$3FMMn^b@;WrLiZo5OyZ zK)Q(BUoCh$3CQ9D_SZc-FSU7{d}?aM-Z$4rCogPdC=4f^UXY6M8(Y0SF=^;dR|7;k zH=BDWA~UEPN=@*fiMiO{JncO{OdK;QM-va{muF^P8Zy4D-29y^Y7Ra)TOr|+cE5R> znK3c6E~2~gwYNsC8q9TXw*J@2A?BQ_PfVf9uTeg7 z=V^uI=DxC|L7$5Y2OKmUTtk(hwr3JX|v|By2){z#vwtuZS|6rFlkV^#WN z;`dZ$R;YGq_K6f!y@`!YJUo!cmPp$MGn-A&i?FYdU6vhbF|04BdNkGB19Z zk9M_x$34VHZ%!XPugKJRN7i#|Umin@NVns_j zy36bWUxn)Sy}_THy)L{=-C?7^B}H*uks2bmHsf-(iEiM9OwN}i9?{2dKVNQzCaTrW6@B!oT>kO`8SFBk_2$|AnX&*xo4 zMBYO1`tPs#M2%;Y=tjnJTL)iv%D)Y*3rmCPF0f|&*tzfbdEosQ7Q4`%yHnq^+ZI6@ zig7pxxhS_RHryv!R--n{pL9!ManjL#HN4gV6UL)_N3e1>iFXy$3p-myktJwo1exO?bHTp{d<4+0y9t@f8{cHXP?yvzLXef7-bk z1YN6H(-M4*I9E&jz7u{0J7;kwbSS|qqz+l<%r=oYd1-0}aDL@99-@`B9nvjn&wFxT z5!uN*{&+|L01HD&L_t&`ByVVF^05+{orLhgQ*^|J-aLN%4u0fbqT@Am@c79?P{6$} z-#vKn?#(?+0|pyDuBvGj*|v$xI`_1R5QJvIkaMm9mz9WtBe=c23043oq%EE9Jt1p4 z-6DLi#ASKZuPYu<$HqgOCmPMK>O!UVL$(0O=J{7((MCOsHJFD`CM zJv)uadhk(Qn*uw*wgk%A4JWs`q1$kGAhKf*;AIaX*?)lvunZ0uTDD4$z$65*Q1>dq zF9>M;{NN)vWy>gFzP_1MkfqtQZa!&0WW&P(`*> zMYgo+)Xc!it*(H5Y=?$X?>CLa;+W`Y#5?BW(Aq#6(Pj0%@dB{!*@ zBps?~*`LRL1`A;1#epghe^ikjdj|oswnR1xq1o-w)N`}}D6;!gnthwh%d2P@Kv+Ph zJ3O2U(H@5ivhEe&&fIxHE}Dw?-UpPAH!-R@`D8>%usrnU$&+_OFF1^S!T%1P96SVd+V^-21!^2j zQe+|UH3qKP@lZLqQghp5B;J`gN7wFN}jttp?DR-6B%k z4T}k(htH{=zkH|zH>&chZPz(nK=;eGzP>4)gshu7VHfS#`-)ojC8Pj<@bKNccMsn^ zrSSl;X~CJAM5k)x;AaRYe0Tu;vOqS(5x~yHCCC&10J4BdWs2guUkLR%s0 z4{40fU5IWe$O57?=vtzN0Vk0)rv_axUg@yiLH7%ywQCXvK37V#b9B+%X2^0L!9K42tv>;bI(;o#kaC-?f4xa`P_2k#y)6Ih}n7`vam zd)P+Ea%cg-J(Cq=IeE{idGCq(_n_Ng6Omm6c8z{|Wg&tuN=^-OZ zA8jAGIRa1ihX<32b4KQ#OtufQJlSz-rri}Si-_tpGtd<@qtT>kcwj>ZK?6FBU_}=K zT4yvmCyjM(uR3+c=^2ePw9dELcv+`2w3ep^LINVo#_4IJGqeN8ygmbwfXc!N$ZAPp zLp{0a#g0Q3g0ft7pe!sv{}qhjdiP=uZsN#OWz4~wrz5bBxfkznCzEfU;vj?G4>)E9 zVz&360@}S#X)o>26ZmPQosgACeg;CbpiU{W5ae3l&}eiU>#v*|>;>b=uFk2NgW$b* zcR}gSyrsiRNdEma7!9FaFGt!)%fd@xW_4dRR&Yemh7k^OZxMS0q{Y1uyi``#<)bkM z&5(t1INNJ*a+;Z?3k;z6ddNb@z>&-J5PAt9JTNx&06GrdsI87YeDmb-lLtfU($Iq^ z_!|yCMEC0J*5?I8}jh{mkW zsLn;mZw5JYd~tDGQtybA_d-xU+%6-zVEC11a~mXYs}7Ka^DE&&I+Ve^a9WFtej8n| zZEu4u&qp`6-Ec7zo7*_zvd4pqxUDvrUSHfccj8E{Nvv;p1Pv@YR?(Al_>|ja(6iOFD}C`bRq)Bn-7i79l}>P zfLmE}7wM{88_lrgRpDyk!$andP&}fMS7%;um3bG{#bew3dGZ*%Al&1QofOWI!0t*2 zJ8OBCE#^;ytlIn4smPWzQ_ku1WfJ=*MpkZ5WZ%4e{Gfx84Zm7@wM|$~u1i3c?+W*| zC9*&N>tAD?o-KxB$X)`nQrGqNM7GX#>pWz^@s@a|WiJC+bJq>R(#Un5iv1HKdtF7A zFk}hKFWV8w-st)(8Td0G`}m3>%l%1_<;Y*V{sPDjoUrwSt5Fd2WgW7kT5{E@D?0jz z3AsYm6$e@k@%T&E_r%eE0nhZ+9jvQiONOsdZF{V4Kimr0@4LQ( zpHF&EUWwxNlQ49bt1GfiI=`6q=wQ9ly{)}6#7ejO@3b-uzU#Vny|=&hdlE)u4_yUQ z_Dyfy?d|XU+k0np^k&0d9=tN8!teHv@-2A0eyywP2h!hv!h5p!V65Yz#U!_NxA#`Z zSAy>y=x;CthOb2Z^UCAb(k}R+s|)b$xG!kO6l>z~S-aP^UXzuWOty)eyYh(H_|F=;$#s;O^}! z;(@{A*ixZrzXxQm-RS%2_DQFD^I)vw&4R|;+k3lzbg)x{vz&H#w3$W)jtA~^yghqo zV6!Rb3cLPF+dnx_};Z~y4% z?T(XCIPTlcg99Ac25?fhJ6A?Wf9k(|f}0_IN0IHiap!~qwaSd8k1%g=XzX7dCNN|2 z;00ttXnue14Bx`r-)S;>dtkWbOHmpexZQ#0RC{uRy(ge}Lm{^78}ifW&C^QmjeLBK z3E?_Qp1$txJ!wc4fAdZUiuObwi|f_i>0pt#6DQ<r_{z@5;p2 z|0Hr!@Cy486#0+dpKhHN9_dhn{tQ*GJ4?=3%DY45l&mNJQ6UyOZRdWv%bss0?cKvr zqhvwN-tE7|vx~x{z2!*Bg8XBcsaYm%E;nk4?fPz@|29*zOxoHo_^uAwu78gFG|a9F zlQy>>)Jnt`?c)CFlk$2Z(M6Yw(Gm-?E!XWn2>?9h1K|%*Ys9 zH(gESKbJ>kc5#@rC9(hPYAXLVcbl2BOxlsyzjn2d|6aZdX;he8IbwfkMQqo#{~jJ- z%ra>^Vy|_zmj7C2%ra@SV8LIzPL%%~7-q~eX{Vux|Li(N{_dG$J{Xg>F$%Qbbe$^S z{bNMShD9@J2Nlba8{c)EL9YGhtvP|NQ;^{q6nz{_*9X`B(gxe|~v8zq&s-y;<8mL&pc~99_M>y}iD^qu|hA-#zW0 zTq9!AU=cArzr5T%JYL`2!XlB2N~=FTJpZVPR|dHPS)19hDU!}+Iyd0Tn$2JiK)59CuaFXl@=CPHn#Vr zK5L&`-rwlec?hw$Chl+|JKde3>VwBX*} zVkW$8KQ0aZZSR?&)Oh+=kK*)M*A)f^4fj6@c4jr;1qOy1MnOhW!)xg@JJNW0WWEL^ zk$T4C{#9;3oLUO!v&%V#r-5@XqB(jKC!a|SW2<7y@4dAzO&#Y{Z`nWW%<=v~Xr{^@ zwHAupGuzzzStP$nFwhM?($dmi?B8D0r>cA~e&P5^=Idrg_Ov;;@iTQY!q^;_7M>qZ z!0GG8;YB%vcr!LGVG%|mRFo}U4t-}Txfqz8(%ZO$>7}aSQkQqrs0JNoZUHZ=QycZg3+5=o z;{Lo*Q!TTCqMU+E1U$SbHiDPLi2TFpj1`|_My+qQ=6FrEC~{xv>VW%;13UO8SsA$? zAz3Xh(gbxYXFEHefDfd<%=DgT%LBfAI^h)oboEnPZcL3v*Ip_|C#Wx_PR7h$7doyh zD`8>Y58^J}xzdxWnB+~dEfm`MxE;rUp~W~KR* z6F*@gkBFwVX9c+%ojlxE?NUTcab7ERu$~ zd7Q<#pC|B|S0j}GLA&1eLHC`Dp}g52*{ z$Dc4&u}{g>;YK+_N*t$^Cq#tDaD`%}&%qnczmP|52am6(7dlz;n+1>~T+}$r=)9_m zQ)l0+`RS4j;yrQ}N68k)YAIb&e(MWH7-C+o@|%GECb36*PE8^K@~ zyO4psW^KiT{U9~mbARR(dt1S%WEUXaX9qU%ruMjP6UJz@HMt0|yB{5~>ov2X2T-W)Un>qbSSBmM%T>81C+v)10 z{o}pBriVl1KE2ua@+mx z8_A-pT0R$mvVD-N~ z_l>FEzVCcSHh*;?QRLJWEPeFL{~9E#eO1bWGKMApdXGXh!kUmpwga%lGuw0TPM-{yJ6&~gq0z^A zFZb*w%-t_f3-A6d*!1TxVg|W+--C4LG0>3b;S%i387^@MAEfB5mI)bFz zD+A990H=Lh1)IHvVcp{Iulu=yZ^xsR&5;-Gaa+%Z7^Q<7GJ*VRSEgIj= z(3`9@`%z>aXHH}GerGw49{1)kI=UZnY>DuDxK^5lv8dS}h{xVnMjMk^WbU)Sx53fB zxLOl`_@kzaYHK6R={g>i^}B+TxY>$SAxvtlEst8KZ|GKSkp($qLMNvBnq?tT#hwmMd?)m=!AF3Vp+!Z7k2S{vL zHZQbTvMeSob+FKs`VYlKIJrUstr3g$+3Al^VOu=ywa?R2P%wSi9p4%*hM+xy-J2p=;? zfI1Sr)F523b=wy^-f`m~+)s4AncpqAhan&Lk1X6|15Y|qJyW}Ifb?-pF(qZ@74A>? z5ayx5;{yyo(+(K=n|W)1D2U(X@@IAAj1z;w`?QDn`K&)4_$Z4<l{sUH3jwNudf*W9DEK^?K&od_wR!M@H;T{v?BlvQ??xFTrxKLVCr>ac3gVu)>u8TRcQH# z3Krfjm2uY#C*KI?N_^nw?59$FH7BweDFmKVXoq5uB&y1Bqk<+Jccdj3oBr5LTcMkD@0{ExXU-UucuWU-Zswbh#E;fW z1r9n>JvHa@-e zhK4f5R^@~PU$MS^na5kq8#0+4IY2Zv7=d0=0fhTMh6aEF3`xqPllm`$^PlWL9vCZ>5->xO~%<3Y?<{&f7XRatA46k8v=bJ=$0XBhdc50t{T4$ zKGkcpq+}{wUM1;jaO;sMcSa5f&jL|h#Jq!jj4n$XaqpS7O+qFv%*=@o1$P(s&4)Y?J+tG|Of%$#9_u$QQB z4F||ZxljICIZpl;f_bBhc3xOvov9&?epcHAsw?DK7Dcdd<|aVIJK5CCQnil_rO^5H zTN^6im7GpLBNT3O&^$5~+*g1nW@P2n>5v9;{ImN!^ScYAx9!)G{1ERau-+1`*7T7= zeeCtNsBA4BKcG=F>7Ync~C1v zY&kh5@Xmnk4^XkR^`}Z2iCjkBD(`+(JB-#!s7wW&R&G?hI)CEz1PB$b$Q`0!54h7gu%vD3kCqg_2BL$=!A0A_X>WO-9*2)r76k6+8lHZkE1mQa3XQe;KHID zijN^Hzsq9`l75XIE%N$8>cxlSO3BmPA|*8vA?ux(TV!{dF?Fz)vZn!{g7)j;-_jCv zZ+W~bo;r2tX?Be)>^v`czR|;<(zpQJU4#4A73J!Gupz80r*+Y2c%*A0rJn&eibjC4EE3A@RvdIqRSZ ztv2i&(}I2iqLIYE=}v=0n=jIUELCt=K3GBi@}~)XXOgIVPBhj;^ny4;E_m`u3bui5 zm$UrhyrV?HwvN8*s?oj#FMH7Q|N68-SfP}v+P_g$N?kCVkYH>tR z>H{9#7tVQ&-`a}0mktCJ8aZ)KxHW1rCFXF93PSOvX!NmHpi3Giw7h_!if*Ima>4H2 zK)_}=%Jw+>$XA47 zjap>%34C?Q4-<7rEAe?LE6Pap#n+81a?*(2g;Q~jiyhojHAoOzC%9N~9t$wdIADz< zSkVwETTFtrS(=7`Iwgd~IE|UyFJlu44ADXD^%nq1V_-$2DsV5bKIp!-Ymwohss?FPJEz6Gg-Z{|qL-U;^48 zbRj`{B3GTtYB*rGInwJAb;-g9(}Q|-!Aw{$a?vGP^UwltpS&snwejmJHu|QXEPn7! zeG)i-ZyzDjo`P$Uniu&V-H9#Mz@WikKux)M>|CxDe%yxGq9den5#JrLXy79^QDk|Y zAPfXaxMYW-&@QhmJi!~JH%!(E7%~MD=p-`g!&^&fSEJ!P_AR!+x%|46(qIOV@ft6x&ToA%t?0e+5GUjBX{{ThT&QVx|PR z4)9($=6gj)hnO*bggM(L!D&z40IVfF-JtmhS{lnDZxD+`&(B?&{zg3XRZgJTaE4k( zYskDQ2@dejXTWhC5=^bjkp@?9@aC}o;*T@G@3_8&m{4Reicolae%}JXzUG&5%TiFh z`BFCGPusp-P~Kf0#2)+wE%Y@*LzpVIKrd}uu->87Do+R?ZCXEQ#z~gBpC9%h5M>WB z>1`rN+>P597T~4z(+6XcL5Jrs7%J>kC7Wu(T#%}pzk=!P#wG3wuop=5mm6I{TqNp9 z7Nk&?7@~ZcAH?RrgJi{IQA8mHUxk+ewuA?;O}3E|8i&h@hbFS3<=;vR)6O8DQs~bY znv$9}{dAR4zkYgwJV=TLtnMQ$|gkT^U1m&RT3JB6SSw-6R{iZVwth4E& zuqvEFN{!^6kUktj)7G=`;xRuLsSHz9jv}RxQpAQ=Xx*7AK=USl=9qE%Fww~m%yw5l z#ct*LrihYjr#GCwziEDR96SpJs0=;Sxo)4#!Vrp5eY3@|W<^3M%! zaFU#=h(uQm;3lqxG06PuY;}>$+gpHIw-+pfr?Kk?&NGmD=+S094EwjNGg1o+F{(aJ z1p(7ULemoR)EL>h#QjAnCME$WaLT`7|D4rWl`Y{8lN?hfH-G)ck~)}dLQRJBD2Mvl z#wl%C4y9LRMNRpa=SIiYpaZyO4XLp7!N7BN8Al)vmp3iMDaZ+)h&XOWi^(aDo_wCu z>yjNmgqYFc4{>M*%r{Wxc322zUFX7Yon%cENvr%Yi^Y|ZbSxLI9}>js$5QF>lJX4H z4DrQjaFo>p$E%#*%!xb7tK<}TDkKfdVw7r>Y0Xn?#lwu4hYwQJInfZ(gJV^nT0?j# zS)4>1!#~1k{IwYb&@@HTvXXwm7J8VXK>}w5srCVV0^B?DLR)g%M4=_Bl@n&%E`Snu z93|;uE%xnA^x5D~n6{WDzoVr^q;Q};Q#pFSiDSLKGBlu<|B+%p2vQy1>@yV&L6AF) z;3whtS}4G*Hfzs9-1aC-Hf>y5hYnY#&mD18I1NA55$gO6!eZlx5O^|~>P*0-?7-CH zGJiIvW2$*G1BBj(+(whGDMoOcz#?|2OEzcS1N)1;1)j0xN)7n85b@n07L9s^A+lB* zvZMi0Vi+0#mQ_l67=8AXl$u7=iqs+qN7nIRK?iAQnvtUbxi7EwF&hYSaIp^+^X~N_ zZo5jI0f5<43P*IA3F+~hd}>?5B3Ozl)v@ahNCq&E98%W_K0qZy}h6_uQ9E1_|%7j;6ewZC^{p@t>} zVJrhIF+K~y@fB%{9Wp8#z2kaMbsPIJPrA$>xnLbl32!0%3NAyt%0>!9$wU%jNcz@AnZGH4$iwxZ|8)8onh1A`(qhOOTw9IQ_sI#)w{UC>;Btq z_1fo~c9uBChkeqR3a^;`*!(#eZf%}&u&e4P+UPsy)_0_6zv?Zo-rCnkWt++^CQC2K zRNM|l?{m_zPAsbOikR;^)at#^e|B{UVvX9+u*}v-t{9|Kcerv0Ly6+|2T{G`qC)>-PzV}$LN{JY)ymf&n%i)qfdn7p6LNG{Mzzp@m z&$VM0)2k}Qh~UancdLmmn;OCFL(Nyx@mHhiKrZX-ADYiAy8Q~xu|0ub5?2DIuO+6^ zmz1dpzP!_FwXlO-I}hiTEzv9#n=Atze@GY9{RBldT<8>p>yprm)+1g~3rdq=*brw7^^pDyU1WbJsSr&Ii0U8lQ(?M?O8?90uW z8!1yOhR~ic$QHv?u1-#V4_b}EupDUL^YioH-mOkO%f7VPOKqJ$8Pel=THf>LeSO_J zbd@b_)qLJND)2{SXOL>PS#EahJttl@4#Rjp>I-~qy<4!|;301PTM6Q+vrScXNFONi zwDs5q9LZD>JfHm-tKmYMJiDtH@4}LuCA(8^p2bAIyG8rFGF3e~wpqbO*{Hg7%j~*V zxK7kN#=fyI*vbdB@AO9$TWV~ks!=X1{5_apq~c@TjRif{o!(yZIQ0Q}-FHz=TpVzV zy}eoQJ|`?wi3hA0)l6I2dl0D1G@dqtwiF(g>vL;Yp9R{3^}T(ZxntsQXYe}U&n=e$ z>oxXf`ji}5`}r#aj2cZCo&L%JtAwR|P|c21%iSBCakTOz+k1zt?S&g=oo0Bl`n+T` z59|I%h3>7l>Wr~2(9tP%Y#JHjg!GXP`%OWZ$_g&Q2-DgWyTJNnl|p^~f}TV1w)<>m z=7$k^0W3&S4^R6yV{l5dK+dXDlj?+nx|iU0!)NDZGQ@&Uqj1#+7qixfz0P7PR56Buur?4*zJKfZ2Z*?eFXIqP{Kn$tY zE%B}y5p<3ac$8BB-U5<39_+J8q#GH0uhYXp$mv;Xf;Yq<$6T9pY;o3P2^m9JmJ8Oj z8cbf0DP7G@UQsqd1o<@4$APTI)T-KrQ&Z$0%wDn@GDx6nyA&B)tIfftv-4F3mwtE)cP68=&a$pN~`DT{3^|7IPz`p zYGZR1d9f#X6lF47$l+o<0j^EIGuRdpN!m5GML%3=`RV@GGZA`ag@2OWMlEVz+TXdx zslJp>H2NF$d6Qi#WU%)9OvI^dl*pGE2V4DYY>K;(0MIexp>>QlEtZJo6u`j`|a8E<(leW@9muzZ`(lO8!S_|>H6UHuJ@8u;do+* zrD8RbAE2!^s(Tq|YkknU+5FsI>|p8Hw7YTogu%e&zXrY&HB%YhZqftX3j_Yv0|HZ3 zXGU&>-B>qq?)0A~KUV$4)Ym8TS#fp?1jq7$?(OjnGjooSyR>FkF3-^@XASgXMO>|| zMLxCNc}qfV_$vIxa$LtA&0L3b2}RCY-Goc-atK`!h)UxsUIRq-1uGXr6~RUcMcNB} zID>Ng9f`EZhEZF$HnQgi4TY9XH%sTX^1$ORfX`Txd@{c%2EYI#qm06m;8SKlh zsY$1-@@=ux^)>E}n0EK5d3KZXr)^eQEKL48e~m)j)fR8c3@N8%*!aI(g8vzPBs(t*X-`xy{~VVd zl9iT~@{i-ADLU@fjh~O^_-ikAmQC($ z^;6@NUi|>^0CGxlK-WAqIQ>ydnN^jUf5}LxdWiju@qQxI6AV1uOi=tqz`ex^Qck+_ zCmoAZwss75`EUaNLyLpE%*iP3hzWcHmQo;Y@_zuE#r@BW{+D@SQ>{yN2yg^gKi#a7 z6(p}q1fR(?@Bss6+6)9NW&?%+jQuleYM;^#Y}i`Q8l2m&-ukPIKWk2&EOu#wS!VbI z^2bBgs2yWAaYuzUlnGDq|DX_=F*c7u!hR>J+TZv~`#!1^uHbDUu@odOvvY8Uy5)D` zQ69{)rkvVTc6tO`_CEGI^5g^~It`sHJo3|L859f$7KL9MjdqUH)`9Ue%?sE9b^ z6(v-$sLto+E1jbmGL>nBdPN}~BWpMel^2jEAbLwyJL;p!`}$My$Hao2CxPLxh!Ys! zY=JckGOvXWoU@;4_+)v{2_%?PnJxe>SM`H;S;XAF1?ls+XSY;#b5S8cW8zU#8eME} z|GfS^G16p9RZijltF#6qdYSeL1B2Z0pICr~AJw({*D3Y7!%1cRd1oW$&qs`pwLn|u zR<#WKJ>Z92+T|h)dU?ZRCgC_GnrW-9OL74yWtdc}+?>X>CHHQL1`Pp|MSZKZ<6l*C zDj_Hrs_sjCwAupsHQO7ke+}mQLMddAdt36xgwzOMvhB1~ z)rSc-8|?^rBp3@!>tJ`VQA6g4^^pwsUw~0jZh6VApMY%FoX8zV^Ij`SPG8;QoL~U{ za$VTh&;3$am*P%frTl{^SJO}>H5Segc)d;2G-KtDDk;t6fFE}~eZp{76e8+BWy=cE zW4&?cDRSp80{BtC3J!H3$ft*Q0ECEp)llh0trUizCAGhRVz8`Lzorj&9K#is$Bv}4 zbEL!6?GoD##e%qcInyJnlARTMp~U4axF9G8(s%3|=Y)Em>7?+n444eYUR(47x{>GX zpn>EqmgE#IxL-P?;e8>3Ml*Fth%jY{U<@)3QŸ}`Mx+^9tf|3}or7BCvq4$a~p zwUUZ}2LNKkCIZ{;AP!ZT&VvTv=(g?Zn^*!=tM|u^Pv>YHc7+L8@FXLPp2q8=A@}zZ zmtF%&;mh5KniJ=zRviS8%e+ue{t^H_s1B9#1s_5cs(k#QgHLhvP=WOBf%MWZkHVCW zhNh$tU0v;98s?-_7=5^PB)XiFjACP=2%kI80Yu6xI;_KV6wI1jR#MTPy}h8BhjzQ>DD_59?Dk`P>KP?$`Q7y z_IvhJ^O6mkYBm*twWpeO|42HFf^tht1ubnrRBrObn17cM_ zIpwyY=8|I1!J+GCxl_*iK5O@BWS?vW5u55^POKM8AuHwiFqiOsNb?(r0%u z6pZ`P#SBqWG}II5m~}-4>M?&bukVX0p(rWOO#_Jr?wzQQSq4cIDVpEO@f-_xIDpBa zj3*==0y3!}z249vNT_k^DKC1=`KZnCWynJ$*P))m z5HHX?Py=K5GBKhuy+$Wo>ZIp8tS_dSq$CGJhH`1J<(ORgT-L2Sk!rmJJ7v5I8#hXv z#yCoETUedhS8{>A`yCeN-h3rCSGD0<>JdHbuV#xzUck1Nxuh|DFY#L^RX7wCx-Ey& zROY03vrY~YCW2g~QBj4Z?Q#vNJpFiI1Jm-i}%pS2qIEf0qmEOV$44o-@vGpmPm zXO=XXq3k<2w~4B1m6zvL%`Agk??P6WTFk^QR+rz{Nt+De$k(5P{Y-pcC>*3dXNo@8 z1CsQ_+?G4M6>UH7;wUj5%mdvb?`QJx958L;oY(5Eb-r0PNZkmtFL#-HOO6j-QmBeAsBh>v_uHq@hsj%v-6++cIJk|IoIWg=4ci?8H!pHsN9oO+w@)X+1{ZZ6|g_%(X+Wj8`U;K7PU+7xtJt&gk z_mjXnVhk+Lo@+!u!6&<8=_oTp&tz`C-jc^>HGX^9pIDx)R-`9Cu+cKs`n`(Jn)9H{ zSL@U`Iz6B!xlmov$Jv1p7U}IXecs$Qm=gcR6ueK02>ouNnaPX@dr}+a8w<7+XA;&m zmhsWNiqxj{PXe2Y`QOp9rfYvJMvac{@VadXOf2SzY)jgt^lu*`F6p&gK=^C33tw&A z)x%>Q<1@~dJ+y#TEzO>T)jYZavp2h`trGCjlc`5S%$ql^^A1CzNnT)Ma(2^_Js16r wqX$|U6f12q$UYx&{>wBd%BqUVS~@Kxmr`J!n53eVe5C4InkG+%Y zn}@f}gNwc6n}f64-Q%0f>-+7atDWQP2hf)V)$0Ar=kxp5!^_v>+t?WiuSN9+Qvk)7P3^6q~3<{aDmgU9mOW(+xu5HNp z?AqUnB`c5kw!U#$RpZUQGe|_@uAv!jAtgq3iGuQmsfA4qeOq8gNlVvoNp-7tP<&8C zimglF^5$VpQ;(Z(R8V}8j*+8)xJF!Zenfm$W81*o@=j%4$NbuUVs33#L3L?;pOvGZ zlSgP;PPr|^TBPQlGsi|`YQ@dPdu{t@f%oaZwhhc&0%UfkdmhgY-aG`hT!PZPB6I%M z-WglEx947`*&OAS)EN=1tk1lF!4bs&I;e@eFtzbo8hzeh2R+|^7~m~~LEz|rUuvjW z*Bx1D_&1BhF3R#MU^7nx=#2T0pC9iRt4Q_eOIu#mO|UTC zs&;!{LCbz$yMLSL+^%)Imfa4|vO6^XY`*M1p9gX3a82pK_)^D=m(D*J*Y*49Ijk7AdLEXZ+p?OI!b5=W zn<_hhX_i>9?-yLIJK?A9+}tc$3!vt-<)D7LPx^CjAy}D&n$f-Uw13i*q^oPi+MTP0 z(g~pCRNH-dyRA5!Z!yrv2`XW_=!hl?`sgea3}FoZR`FVQx*m($Tyo+%eeM|sJ^Uy*PdU90@XhnK8BcI6j;9vN9lipi<5r=gq)ko3yyx=sWmg2@=lXf=n9GktR)c=A%1K#4Yy{JU~bec3f%P7dvH1qPB z6LKchMuAK4)SFoC8asqe68>HV+An`vPT8l3f0g;jwuaPjZ&{^v)oFY5IxiG42NaUm z@UwK4`lPvcYic5@e9y(pcWBnFf(;>$qe+z9VOL_a3UoWRI@F$GIO)0B-ym!qcdP#W zFi|*}Zzpz_mm=Uj@SshoajeqZ;)gpg##}08!rS>aHAWBR z{n_*0eG0v+sz}k!=3uUAe)R|P&$pZ|JVG8!DJ19jQHhe$!9>vpl%4~*w)bYTaMRie zkWccDOE*RYvOmiXk-S;RFBCnfg%nDBDa;@?zqOIiG7eS|YrWTG zE-51#=ixQD|L8rvbdln)>z24|;pK2TL32qeVr8K}QN6>*I*%wv)KeML8zFPcDK9#B zssK#|gZSz_V83DRK#(NCUmbx3%FL!q5?BVi!jT%#Pq?6@wB$|bOCjm{MQlksH38DzV?HLGqP{C zfn!;ZY09{0+>VZYPP#$l&&{K0AuP@GlX7eZwIc0;G<;o1`GfS^&w&OQQYU8b%Jc>X zztgiQVTfpw82;M$O6dFNH_^nIYl%xWDfTZ_4SLki+GPCQB;6CXI$k^}&LhC{`7HVN zH(V0v_-dKK2KtHYu|WT}bik>Gm&W?w`aZKxTBllqc{dV|2R+^xab$t1fzz`v@JTDp zesA`XsEC4mdFqwlq>7o28#j5BHpLDLNVPpZy(ri~DDYluh&vD(b00dI9NABnOp}dsKp#uqOGV z;6qyXd08=V&f~@NpkDFMvt%l)3kR?{x(|gc){YbLcWwD!dlgABUAW>LpLXB1owbn> zNYM36YM!2aF(`*(;pZNtVB$eSHaRR$kC)4J768Q zJBw6-ZGm0@F)qq@sYdC-%`?_)jq+&s1qD!1g=JL1YbO(-z{9>F+9q8ocC1_G&0Oosel2W&>-;Lo1s4~vM;aW(d-23sO z!IQpP^q5*uR-Y3LEZEw`HN`#9n)pb)7-f%S1C4^mEzM$vzcbwpPQd&K^SyI z2-NVZeLS{A5&zKbQ^2hv(A)MVr`U%ekJ?8CyQrh?{u{XfOZecfNtcuua5Gnwkp0)H|VzTZz~a!%L!_^vUppS()s z+h2~UP`Qdr96n3rx7^D%FPQ=ra``-Bd5@>%Epc8~|4@`o%AXlOY`#5T=Z*xWh}1j_ z(sSw%N|Zg-Y@_1yxZ#@~K9n1H@miVH7=K-V{2X8w{HuP@!m>OA!0Kl%&(U!Vk<-of-Lv;HzxCM$ZrsVePDeQ-Hz4pJ^Rtb7Wu~Z{FJj3 zkY}dj0d-hLpOSq!meAEr_^PH7$K%nq!{Tfs#_t2V+gC!~x(JH1-x}!3o|O;%nC}*g z(4*s>zU=gJW;Wsfw`gW&cahpqQ3m=5&z=X&%lwq?m=x0ay+1$q3k9w>-|koECx3g` z9nTj^$PFPIfi7kXQH=84Roy4&Og-j_dTo!0Ua5OgpRaE?V}BG` zv&~WeT@)4I7AQqC1*=BMp($lky6}2}6_c(rP22#B{lmxK7Wv4%w!L6BuE^{c7q)`( z6A;zxXSfl@Qtq!`LMomII3=s|Y^X@v!f}RC;0E0c+C?MC^NDg=+MswX6jT+762{{c zrl?byo12$Wtz~}4VDS7@m>$A_S=`ovYYM#6%s7Z!nSQ>wJjj7A$1dkfus)gWxXo`U zY4Q40rg$YIKEYKKCQ^K`yzkSZ<#w;*_pZ1`O*9v#nLfPwd9BUtsJ}R=;M0)Tzp5xa zLVNqL8N`UWuxHZ~7q2bkp&b;FbNLhZ!;>M5Vqs^Zf_edAj0ygzbn zv9U*8hMQMfkqr3ah`Bc`&MQwBJEAqaY|s^a8`Z7-u|ohbO-qUMLS2?Hh(jerE>7j# zorG23$mJLDX^tKQDemDU%?fzEeiG`78+mtL>M-+vzCO;}hh;Jew3Yipdgucuy)h&| zPRRAijz;evVbO$}yHUmmZLWU$P>~@#?m~zki^C^IShAkXmU5!CM~@G4=H{Hz1RsbX zj5W*!BhrMTAM`V0S#?PO0Mn!QwP+tYjIqiz2-48SfyzSVSpP4TryBVmn5U|!408Ccs1J5Ue76B3kRRE3ytQuI*VRHxKY z)}xW7mlwR~1*JzrpufOOsF4}Kj#J{Qf|5JTts=0fpuM1ZSO8z436t$tKQW%NtRgz=3_lX_6W8CvFekyA=qB{L^={7IGrqv#cj(u&LWM?7n~&D6#*L~)4~ zM=To;A5pGjIv0cN90Bue_1;~I%yyx!FLv?Bl&1yx<-Mm~}7z|=1-Ps`esZNU*3%QFo((rpctUDt-9?x;&4W0;INQd4gsIp=7S zCfQ~CL90$FkQph&)1Xd{(6#=<8@YIBrdI0nlmr4&S5vCA^S3A&i`F_al=>15TELlD z>Ffx%`gntEnXV=@;UA+tmBZ7O2dk~<*ut!0QRN>Kfmj+iA>wVip)lZWzY5dx4Ofdl z7Ef2aJ!oSOOnncgUlbA92k2(@)+SsGThd&fZC+z)WK0*#59Xx6&v3Mn#K^QEEGsM{ z*k;8uoz)evW3tdSog(UgX(hfxBhu^fU9x8EwAyn9#TEubAB&PE?zN6V@QTP_R8@t# zu!)ls2qk!YtNIed*IP#is~Y#*Ws}W1NKUQ8IA_U3SBFiSV|T6@JJeX09y@!GaWUOy z4E(vGOJ07o$CZf+H@}so_`4OXcygB(FC|8~zz`kBh`{?WGHAbbbLrWx5Nfsg`x!}c z9YzO)Ora6557#@mWuX4HdF%(+kEwMUqcIWZ96_Wi`f2_jE8M;IE+89%OWHF}py@Y{ zP&}ffgnRC&21l4vDfCL*DW!=?U;hl^sC9Da_|#KfC-lOHZyPq~NTr+MJ{-^*i*flA zLJ*unZ$gPD^|9Ld>wtWpH1>-Qu60>)eE78YIo1{V#@Woc;RgQs^|b7m>4CVG%@OQ* z@hLW$gl(%FQdU$lA?HM<_$HO2ob>z{-AUn51C24Z(9k%xWHEIc>0*Fc>2V7=A05{1 z+Fu6t?YW(CYwIS<%vsb#z3LDO<^gk2|PK)QuzMm zlt>B1e&u0~OepN+`zq)NOaO>JExQ9f#)4)2Qr*NYBl`5vJ$_;E(y;~F7h5BROpG)M zBDrZM(`Z$VIBQmH2_nxb+J)9KyolScH+q3A_rc-Qqd`dgR6GSox)$?WDFWU4%mr?Y z*!M0jQK>gf#Y*mD8#JtOM_?%2Nd9DpUgbIsIH}C6I6mRjos;M=+1P8JWT~?v`OKxQ zMPA_qXCwwwN0{au0Yee@C~<8t!QVkf3$=tvcWzC?Hz;XDr)v#ejLYY@m|-R9Mo#AJ z&~4)_q$sMKs-3+m6;_Z?_E5be7$Fm{MUs@0heiR8I4^REEu2A7K+?~Ke$EJth^L_; zWwdT+GJK*bfEXE3p`VU8E(|bWS`onmRj$Nd)DnVUgzngI$f78hh|Y+qHU-oP{9h ziCZ+lvjsa}zc&BJ!}H~K$r?I{y>PJDjJ@DwN(8!v5?QQJ)Gs_qv@B3gY><;*#Ihn7 zU9H05JEc(0$tBh=MG8aemjaUm^}Pb$u@EI~$QjfTMF&pUJc<%;@p3bvs^6&t6^saz z!j!a$x41L+(6>65muaPa!V0ivWA{Yy{g#!rnk=}sbw=FK&tRE#9jwFpT23foSyF zCN|X=_}2_NEVm>uxhn3l$ zv~h*S>T2xf%~9`ebbFLrs?s|U+z3=lVzEyW%{V0VV48n!+ zFu+>pHFD0_F@)qL?)AH&tH)+%RPrIR~E9(H=}we-7>*NFc@BUyKOp zQ)Q~9=_rhn!X8B6k;fEV!_yZp02LA!1=d6dFhm~IATe-tu#ED{{a`d{Ex|N*_@X>6 zRL=-{+K=hvIppGm0>2nv{r>rwWWD=jSr@cwU_2<^R8ZbORZQs%O2FEu}xY`*YjN;il&vQ4l@ar9mKXOe6sh?lG zT4w!Oz)_pmSCL<8Gf;s&H-gW*WXbpZm(=^CDB^~JFW%zrJbLwX&@}J764;VZ^4L{s$@0s*km)Xm67aXjXMG+icxz0C%auWd)r*tR;su`ozMPP{P6{S%hqpg@U1xWe6zyMxId-Gu8~4Ad=~ zsZ-ksx}TK^vYct^xofg_?h?BcoBIfw3Dq~`C^Pp$C1r9)a3T)#P%o{12Smhr@m2Hs#v$`?xsv&ExR+O;vS0B9aD=KWM>QDL-wk|1wZkS> z6U7AH%pnRpZ1T`ieG;k_(yMcI)qnW#PF4bi0@)HJxo)`Nm^Qc=0?!t7Bf;K!~QkxN*C6q8(Xh6h}} zl}v371{LL|op}`jPd)R_Ik}M-W$qL7(idFgvuw7Hy`Axz7Ee6z01D8NNn`-F?H z8*N`(VCzXj;@o@v915K17#|FM&IS~@>&caY;3W&8*1Fs!9SQ_5mWDN*!K zzmOsNpdD8|4vCg?TE?<_si=w8?R?G=<{MpQH&iR#%*LP4`YWn;Y&!gUkgfbXBLS0$ z?M!kVb@;5ki{odb775dEl5Hx-WG#;CUno248=V^ec_4H&sY8%{H4bTS^b}b?G7-+7 zODhJ$LOl<`QK_sJT?WC{Als+BPwW`qyyEb&A$1sq_WYLcM6_KQ45}0k zzQzw8uncMk)?D_=wG)DddFCbBRlRhnj2gFZu>|%Xg3j{D_z-SPqgqu{{t8B(0i<}0 zQ*hMSH>+FYLhz2f>`4M8#*I6urVvWY#TK6FV8ATfF|wQ4#@U_%aP<9FwT9jl2ywVn z)}VpbQu+FRe)s+5HHRPbD6pzBLJf$FBDjL^pB+PrW(jZ}Lj2OvhTF-qEMDU*RC~q6 zizH6pxRN*v0>I$IyNtvxPF{tmr`}>1fWCiX5(TX}rc$w?D{?q+$889DOA4^8(;+x( za9fB1(RHnHaD1`l6PJ3Wyl{PwGx}cpM2&%HYzMk|nr`4GMk$K{Yr; z_n+z$Rb%9~U^D&}k=p@*I za&@9tl0UQ{*m@O%l9K9k&!aQ30XW_(eB6-5tP`)YEBl?Nq=;LZ$zt7D`zM!%b0X`J zWUVdj8k9VQR6Kc5VLN)A1KI~i_Ttg~K}!I&`O1|J|5}j?2zw#Nn-;k+WE?6;f$=j^ zX*BpCF_Qw0wdM(P5@A%2_3H z0qcF17r$_%#mRRp&PGN7q`k3MmCK#FJ|bmO z0z2yQx;+h%)vA~mp7*9u$I?vq^g+-KO}KKlQ#wJ6yt0&X)Q&Lkekd&oL53oWdDtKjSh3|_LsBKU7FO@gA*nZrw8;g^0;uN=4<04T z63i(FN)~&7WQ1CjO+l$cQ5sR<4{Kwzmz`VTTmMuOk)%D@`>Sci!~`|!GF9?m(QyFx z9|ZY8pa;ILHeq#Cu??MTDsoVdPM>l^dgreS1h6CscXyWGxuZ=R=#-eDXi@d_^D9_5 zbyX7n;1OoFKYnz`m18A%tW8f^=Z*bP(so)@$_-DuOJ8S`20=A_pw0E{y&oXasxb#c52T z!^ZnJ+(qgmCAG65~T`HgC*N>`9$fSMi?peM(HV4TO3j_vJYaF6{PNnjuyyc~3#`VhA>cN9$OyfpX>1=M zl!d&HN_H_TWWH9V6Ne*T8m+o*BkcRvBE?vc>X9k%iBaj!qim-^leTjuM0G400h179{(fC#v zPy5AalqLmzQvlQLdmrhL!Sq5vzM3u!IsgW@)t#k+yH+?~A;`F}O^pyd0iw}yd4jCg zfy+OljpZf&|R4)|W zr;Mmn{hNQH{yqaA+P)2%wKE1~2Us7*sny?ZE_}&r%u<;{??K(Mf4oe-Sqj8TKhnC) zBgjQoX0^YJ~dyGIUZxyzy{=;i|+xmVpcDL@QK+7ep%_Bz$spfvyEpo|l(QWwn<8 zAdw-)K`l$JJ0f`U{*^{7t_w*l1V0qAFO9*#@Pe}dFAywJC86B)T|dkZ-gzA~VBw5q zuA2o1p)!efc6UTLbkJKbNE6*EF=$=yY>C46@hw#`|{*|Qhv508v*sr znF_}A4UgT-f42*9g7t!zGl~(7>A_wH|6fL5pZqA;L{SfnDf?k-EhnI4d=P9 z7)m2asb3ri%MYa>j-<6Iv0b)2I7}B2Dg~N=I*`*Of(8ZxdLq0E+DOb0L0B=M-Q z-uU{<)7#28F%&bZSKXd4KN`RaHMBRu%#||Kex3kZ;)&gR8TCeThGL+wCL_OY2Fo~r zb^(hPB~Iy(UF}^iIKRJo4- zq9%rp_hz)g5SP`wtxD`DI9ysbLD`X+h+4TI!MCV}HMjchQpm-m=)3OgvQ3FA8qvFY zd(gV^ORYIox$)MO`@dKby~cCJ4kCiCqn!m~)a6gAg8(n@^9V8QImQ*SC`=nD!rvZ% zh#;-&fn`joz`Ow}R7*~YDgjqBhbs? ziVGi+_3*$M#qBCNp>G%_>hjvpTSm2-3}{FN!FMV>@vpZn+yf0l!fs|IC%$WJgitf{ zyl+IN52bJKZnyBqQBeLTO_7aVxs}VEhPo75xA?9u0qz@-P~g^;89k=zD##pxxhXg9 z;K!Qh948eTN-4_m1%~|hZepR4ZMI=@i6rL2Tbm47TcYs$$%~umrf=Iv(m~&?U8*&M z1$(cOY@e#AGbvBR{q~K8bV*QBG1IQ_*SCHnh@RLZ6G3LeodxT69vpD_4QsqUu|l{d ze4Hif(kvc4XZg0%0X?IJoZwe!O`P!(e4>>P$Pa}{oo6TbYd318%@4}tiA5bY{+x}E!uA7{98KYD>iNj{mp;OT(B1@7TWL`Q0VUjZ_P(tSq9O*^N*Y@ow z^l^oKi*E#%`^qxC@us$5XjF=6NB!;58L-6yFN}fQGF|Frhc0s86lKgRG|Q$qehMdX z%}$ba*5TmL=&|Ymjxx6@N3vJL=FiO1&SS=;O2qPkCBGu`Yn0412zQnRlYbG3Zu(uI$;wVIPWwwIsr#xSFiOrH?<^JkWGE+$OMZ2Z z!qHeY>?B)34=b<1ILA>9v$_Wd9w$R){Uy1zA!A3qFRI<$ZtjON#?6J{UXV>jOI(LZ z+oB!IQT$j}@3(2RHlsCo3dqa}&8uL}$JMl3FqCFQjz4lF>(lm~gs-78Z&H#AU=zh6 zb%?2xW8x$+Z#)%YOM%cz={vL<+E@nkc}yDC3JR+z1c$ZR-sm48U(i2oVWYbzw4e1e zPr0Jl(h4Ar0%PVf@(%__r#&EBujgWm_yXyS5(6gO);%s@iZ5%}gVrY*2Tr^fX8fP* zBbzkgZXxMij3n;GlZGQV0Q&i-K*BZ<+DjhJuQsTu=IOvU-%*+MB~`AwLX<1qx|`>9 zv`3na1BqLoqXKdWOMwO)eqpT zCYq@N$IXeqCSpO2k~A`rqWmwQ4erdZFh@yQ#MKw`5RWM!OdywDyPx>7_$XGdoAli- zgdD)V%l`=`HHYSiS6{W}oZlUvvh~Je4;Fy`owBtvOH?YEG{18?NQUPU+#Y+R;7rne z0I3|wyiw^+tZWH!Wk^hu&B)$f@bsO4vdUBNtztGy_w1Jgd};sCT?Q^X!Wf7209Kkf zR}{CDpqc`cVz!cS8I*e@9#o2{M|1iD278h^EkFlwzVm8bb>B~W2H?i(2B$ps- z(NS-8{f2O5RdDbx{9?C^VpIowTNS=P6O3|G7(U}w@shdNG91H2RheI)HPh&NNo~X^ zyYyB2@~x5B`660JTjV9?n^Cn9>;txk=QF$ zKI}%43K5+=)>EJm3p6t^K`WaSzy_L-#m#bGh%Tw)5$MvBjC`p-Ovk#oE*OYXnV^Jr2i#)tW8 zxqoBFZWOYxXTGxx+-W`oG%u<_`ALF5KJ#&{y53Nt>e@V7@d^6wz^)OVs$%hHv%~N|SF#g1n<^7!A6gL3-MVN*)swRBG8?@VmT#TOWk?aP zUIAa(+(=>;K7OX|SechuU)jh?Y&ytHkw}aB=yPR7+1JjfUSqB>5x~lA{%KZ(p*5i{ z<4n3XabPmSV0CBg3=5AX)i&bU2&&*8ITCKhd`r3vfnPWGO}Rh3xmzL}S@Mb7JYC`7 z*I+0ZS!oGMG`Zk)=K7$fsNTp}3EU8>GP5IF5SN0->JyCA&MGbchy)hn{_gIv1e z)81|eQ#xyz_}v9CWbGJiDSrdm5o+@DWc#CA3gerRQ+mAKR|rdCR^LT=KRfxly5N2=&`T@gAMb86ykhfv)gC2%tZ4O>&7x!#Xn0-+D-aMUuaO z@$0$@>26ex9CknP)6=Z2925Nb>KcAYpiEVwE1@!=~r;`)crb2!sG>ga|nx|Wb30C*0+h4n;0se3-zAF`15Ud zNpIk0(83XgA1U{7GEbB?{|AT+1#zkpaK2#zRbxG2&K!s{Hd>cmg2dGzh>o z-Nw}!vtW}k=B0#KgUx&9vJ_v6sf&r`=wzuYN)SjiNiuep118Csalp5<_Go&(8(pyU z1sj^veS^>7yvZhW#?;{{X=H0wt7oJKcMG1t$8@f(=XvM)8F+W5p?LNiPmY-tL7qy|wJ8*_Ai zrq%^~z)%3MfhhcWmHoxVg+ zY6yp#33(E)D^9m3%&8dG#&^UzB+8BPxXsjWhg<=yP-e*bJiq&Wppj@B1- zWpz5ZGmaT;brhBB8Lg-S72(1^12FXdg%J9oQ$kknQ#8uYh|0-N1sJ7grpcHQ^`sH3 zbN%CLs)B}K`#wJ+D6w)jBK9-MS_yDu+*#Trg}OiIrVjRphK53hs_Pg$pr4We!$(!_ z)c?B0G3X3u+=E!w{Jv#`N}V(VL7{c|*F;(Wppbe2CE^N6mqoM5-~@(D#?o>2^E)lR zFE+fDqaDN;#*LF-s1x0acsj6dPs~lwe+`g65mgtVu7%(l!Rmd0$5{t57_fa)qMHX3-stu^t@V#}5)7XIpt72Uwrn?uY-|D8`9ggr;RNtnLJd2Xh~0@LIJq zt^urfMoyJJF|BYhb|$kq3HEC`fK>q)JFqdPqZmrm7*Z`2MF`^kEC`7DZjYTKM0WUb2-kj{o|9 zr!gqh3A9xjbW=dMs*d3ErLd6o1S__9`L*K~H04bN1S9>P4V)ZTQA#}UVg3MUntB zUgl!91VL}TzxmzQX?;xzWIBFtui?Q9N5!?cQc)F#(NqXXv_ZFZ9aC`#7sF}JYBSGf z0)FME#YAME?DhN9?qwRm2Zk~Po@^B9_EEjVHz`~pe(};0gf{OeYB}cQ%t+Aj_}r7buk!$?^{X6 zi1q!U+a?(2d)e{DKF85E=odDPJh7}}Co5UaU4@}jX7?H4;V#d&M+Z1AJ{vPA;ShUr;3NA)ea_bka77zyRv^@SlFXl%4z4gAzefo)LH)C7UFahS+;N!MU&k} zHJS-2psZb4;LhlOC!uv}2n^Do*KQ$8`G!8032S7jBs=c>jeQYEy6t_p`%*nG%bsH3 zX1I6ONza($9Gm_)gV=f6H}d6|!f1GxxN%i1-!mt-e2JTu8moZKkO`T_l{`Zh zKz3o|v~R6O7;hLkh{=R3tw*xLV)vy=EUgWO?15q*YZZ5+fd4F2k>dCr$6mH@Yj+_P z3W}TO=Fs}QH>eItO};B0o7@I@?2H5Isy_`1%9Jg9W|<{It(SYmm9IoT_c_Rbbushj z9G0!G<2!=i4yoXdz833fyIB2K(TgI@KU-sIeh)kC2>5jjRf<9%9si-ZxY;CzZKqhR zIQ#--_9_>VWk|4`(JVO}4)+DHVf*vHOm7DQ1He&9p{dJkk|l9hDwlnU2)?&^h^N$V z{}uSN$?{zjduJiZ2LVX&=y?NUj2v32;oHvd(A@4*mGkwwA46vq6fj(8_#ufw8hVHf zvv(Ck>`O?{D%dB`JK1;M^X0I=w#lpUHX}G^(B=HR_RreG@9w-~&HnZU$%vQ10G^7G S`|r~Wn3R~j=pSLj!2bo7!)&Vn literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/mailandnextwidget_preview.png b/android/app/src/main/res/drawable-nodpi/mailandnextwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..14f5c077373831f2bb2070ddc872018b085d610d GIT binary patch literal 20339 zcmbTdWl&vD5a0{J-QC^YA!u-SxVT*0U4sPo3j_%kG`L;d9WEL?xCHm$4v+tCZM|>1 zZ{Jj%4`*h2rl-!-neP7e#Hg#vp&=6^LqS2IDacD}LP323LqS0+BEo#sEG8-&e!Lv1 zD{IRr+qpfzzCJy_KEJ%)-ap|1s>={y7>R zBPJosznh1RyMvvh%k6`U+o$)}x3|9!CmVa`505Xe@9)pAZx1i;XU|t3bL~H#BVy8C zJm27uu>ZSzrDqdoFz-@yzK8bk!I;Pg1l2cw`G2cB_vnJGm#P{V#Ub%Pp57ULXqs#ZY$(P}kx4HQx zxtROVjpFk9uB7Idw1WEfxtG7E?~|LaQjvE7F}V>b#h;ML3rp(~v#OL6?qkx+^lU@D zN*_J)UPBWLLTa9}e>GX8yy~PrSY$sy!4Y+j%&COG+UGy69lk*!k}mAN8D>0`4!=Ob zlVo*2t0&#lyFNfcGBmM1^HJ(j1YsutAj_Ah*I!#5bg;e^zs(W>*b zUMDmue&?H#e{>5X2RjdMX-i0cPPHzT{8U46L!-a zKK~ug!C?XLy|YLQx!~eHvEpM z7b49jhI_Y3YDZ_CMjA*Rjc8FAAr9XgI3sW8p0NVi#!e;*tj- z>lkZE3%Sl-E>rPJVOVL9Ra@?c&*B-KHxE`e>zmG1IGUI%}JC9voyH!?e} zQTck90dZAlI|#LiF0^%3?t<9{Cl~<qG7yPGqY2o`KU={)`G(r> zMzU0&sULqDiaLuua^jB6kd*s?in@Hxk3x@66F_Oq`M2R|jjh#0<%yp(@k0mgxsZp> zMNYwpV>4DK>;F-uFB4D!zx*2!m-TRX&;p226R7Jn8dP}fcORq6LxInP-reQ1lg>#-Y>3wuyW z@|_i(;6pML##mbpF#ob07u+ctnj<#A-ki5YOu7`->q*s+{BNI6Jp#w%*-<`kNDEk@Y#sDhig+ z*K{SF%%RnjIJOcmcEQ}?uZyJaLP?6L6XEFkgXz{+fVZ@+WxjKuF@u00I?GBAG+^nE zl+4kour3|Bb+zO~=?-S2w2f$t}O=AC~mfM{cqoy>BMoL`?#7cgq` zUY-`vEGQ;n77EyS9w6|`D`$SQOU|$N<7~UnKlw{1AZ**ksPHfm$?MoThH5o;V^;$z z_B;yAX-*`2mEb}T{>n9QnsgJ283se+ND^7gkq_hfBIB7__sj2|?%LG%w}Ww00mBB; zUO+9_D^iM^V$z*(d2-A1)&JnWk|VFH?QwsD<36BPtl25T^-@2#!aeqHy81nwIztLdo62#UN9OR*uBCzS{6ueXWv#Zr-NBtPE!xd2< zfB^JrHhh1(GSM?8P`rQ5c&})%R+z;!#1Pi>a*c%`lYFSW`{IAg46>wwxL}H(W{Y&G z+22;hw)*wlzX>F4wYyy;+G=)Usvw_qrNmr5^SFzUo6m1-Jey)Qe~EECU{BsVj)z+8 z#seG0TjvliuTxc0cel?4BvCsW_wI>u%47Z13vSXGh2;tPL019=WIRj7!_OaqVjf~pRc(Aso=U8gjbN%G?tkfSEfCXk|sd_I3QxQ~> zl79)`?744x-H)r37r_z8yV+=C0Ki)fNJOyXW+4{15Q`1v5YqJ3oVty7a0Q z$01`*xyCwpp*&&mjLJsTHaFL5%vYW;&dno1<|!Kr+;UVW+}0rO3)awXO_a-J)r zK;B$%12#Bry zX9c9vd~>+KSa8-r0{kttQ9ECDk%YP*)ia*;+j@?wyx;64_}S`JdM|Kzea+F;^AfM> zo%WqRwm|gpc6gFO{N>*%iPXJppp3!24dFRM;mLj~gTUFp^U45mGGf!ybxMc0_J~1X zFIy(8+KQtd6|l%+?nk7sT*}-pV`oXW*4GB>^tg_-^G=gd87XHf%xy?fXjMY%<(CT* zz-Br~=H~13q%LHm{pq>o_xa14NUMr~4jtR`WVNBdu9*CI(6!SAhUwh9<5UD{4ExZaROfE*6 z@oDrC;YoyrP8-b_kPb>8rE#&{eGYnb0XKFrG=6LY@nwAZmiqN-elgc#5e)@(IWWab8P3b@v)E#`A z%?;)VqVL7g#1i<}Ol@@F-j&LqUU#N_JMB6<$E%pZ_gDuZrMNro{=ZeVL1$m@nyyY5 zB=%B&rU_7Dq7H|ZWmo}&y9!<&7j$+Jg9ITJ&R-v&Gv1#r?pt<+2@_Jf9&6v{qP@=(ad-g6~2seuuddUR8G}mA5j&m7$Hogd7tH1$( z&`?vHs2`_xS4%~*jPUBye$WMnfEXn(rZ(vP)u)Wr(5)bkxooTz_G$&_OclmJVy&6iwpM>a10LK1 zpLOuST_Bp~WlGb_@jn!q>96;x6mMafB_OE!_s6$qvc@VAtLbD%To74$GM&O$iaNrV zZXtmSh-PtQar!o+>QSo>!el1YIfne`IVo3N&$kd=yoL~bMdp{gYi~%9aU82Ywpz`Y z+b1a!D5EH*s2^(`E=;z|KiPzD-X%^tb4*9y=#aP5@3#kL+Zt zTP}LSSX#IZA=D$@T=PYh0BgyHV@rCR9e{d@9K$ml@$JTi_|kaLtey|ZKce?fB}s=I zek;PC6PYv<*&}#)r}ZAM#$-Gi_|t@d_s6T?GEID}%2g_h{9gl@H7ns&L{!;dAp6ol zJ?LdL{DLZeu5+uBeMCCPzM4*q^w8>JlAiX_Sm~~1=CnFKFuxScIW8rm>fq7SQ|V7n z2zmGICz`mwVBzCo(KdDb=yfLc4nDB9&nM7i@p>l@Tmf{7A=H3b$|on3$WUT`a;gES zG;`6D2#L^1U1su-(w9o%Y9*wcg393nzHX54u6KoE5Y3 z5eKqzmcvoZPJa`ZZ>Yz_Xhxgdtx``4i%-aa%ea3lH}abDtqyX8ovOp^KyX#8-e|9N zr2GZgp$<7gqr>+y9C~4EH4Z0xv^4$GYAUBaE>+eW^aYnJ%d|rd7^YE@@MVVGYnC&f zx^8`XB7#VGTDE}pn+**|G*SeOPN46}>c*z?G4OZ!$s$IV-liv?^qm0Ga%I<^;D8E* zkk-BTC26Y)o-G#*xdVam`;rQ9-7{>l3gI5WLtit)_ zWL&R9F5R4TRNHxxV>%IQDi3ymFDqdXCgiv(ay%9hqbOWzJXwKGex9n~dNljz8S|c+ zspxa_{S=A4mQ|%b2TD1t5ad=#VZ5+P&&3XVYGIQKFF*W>P@zH#i5u(#WP5f(THd5( zd)|ea2NNHk1b=;WdF(JszIeahYapotlLLlnsaeXbfGVg`i{{vbO>=u(T*@YD?#WOp zC{vuf{mOo92s41DIi+L|=O&v_5d#P|c>^Y#v62Js+>c6;p_g#nE|SNd_Csq zah5?tNhegE`&&#jf66YnrY|&D9`s2LRuqCKlLx(*-Tnn<8Kd6@`h)&11EyG~K zuwHyJP`)cQz47wr{mq!+{rPS27oMfOJwLI^6QeU9u}WA%1#sJ}W`G}6Cz>n*NR64j z5PM(PzVj$enLN?&>G=$AL{kBD;ms1afE3DyzE+xEh;}fjZuL71zZgT9mw)C@RqJ*? z`d1dRz+|>pX;)2-Il)vy|7=zob$fEo{rsA)?OtW#urDo!Rx%}5IoRQEA0G?mH!{rU zeJt^2N7XPPR7jU~pj$B26ERxfq5!2Y)UDAkyVDD0k<+N9GEHVs?GbSo`+j4cEP}?X zV*UMdW`Ufvny+nI5svdWoWoL&&bV_^{MrVOj#q~5zgZIf4JxocCz$ZVV8{P3=<#Xd z;ArBKhaeVx5DOsJ|E}pj5m|hc|3s39zWpZxtT+1*Aq24ge{5P$?UlV>JX?8oQ;R>H z`O+L4%5Hh|k1W)@iMSdeQs|>@4qV>m`Q*J|-8wgZbR(~|Bhy-)jef6s(JLz~T25I- zxLg!hwIK3QD7Wh{jUmx;$i6&cbz#G_`)iGyh706wSI2>tIaT3&JU_y{N^N)+#Yc0} zAP-^3y@IlLh%^p#7UW4Z5x&&m#QXr&`M|lLqyPraW-0u;9NTEtt#nio*erg({!zPn zy;7+H+Z7%#ef%kwoVRNJ4>YW0AxT8|cxDOy6Wv;d_(ig0EAOPLv{o|inLxKL{argM zEXj3Qig>g3SkJG3Pnv|U9kD3NF=lV7@P>|e4K@Z`sN5Mkw{6sb`b)Onj-Yu z)c^_aFUO|A44!2bxAWfosKqRIG0Q&DtN{G@Q{7o2GvCrAPHYQu!&c;-QGspmV1g!Bm2IZ9+Az&s&lIalI56xO0 z1CGAq+F(W$eHX^2=>VjlYL=@=uM}G=pK!q)8ZxLLfZ9*vM_yFr1i2h!{U;T9{faCC zETOhps8Et#fQ*<18}H9b2-1=WNAjul+l5%7eA&*=EY;de6<9A4Y4bx$}IURgTfx5}*&hO0) z0Z&)uTM@spN+<$GS8~=395fS!&ksfmg!onyLfU+tFjvT^7#T1i(eo0t7sov9K~6d z4AR>@C7Wgu7-+;r?mHf)G2Ua!%)J?+}N1mfFR9Q8+OhTE# zHdW+%k`NK_v;K_r0Da5qo=@9Cd;pPGB6MeB-6piOB1B3{YEcp;dp=zTD47`q`AaU98{j)-Rm!s+6xD}YIsjdsWz?(pT2IpsA22PRNRLev|5bA3;B_G z$GF@e@@HJ?S2{*9oLDINF)GFQoFwkCqEPY(bIe?_IuuyP2{4`oLZKil<429h1emQg zpg3=ASA#51KY|S@DHRczwg;YY04S^wkEj%nQO@g7s_|onBJ+P#4l46(pas{%KTHlf z!$ioiQmVT#3J`x?K$TVH0;_(mYL7(G7AvEn%~E5YdVU!$VFXPJN{h=TJ1#O7H4J|B zrlGSBHIRsETW>BeVI`bRP(>NFAGzQNSxAUcBu7WS1PbL_mYiEiin*CeHldHZkvSkvXhY!3$%z0z>lidB*1~&01*J9-}tJuFz;lGvWx;{)h@n0$(jI*)yozWakQ_ z+)v|#nD8>I;pb6n0!RQzWCBb`(03Ru$Acg%#G?Aspdo4Cb^^es={W+~20i6jhFJ3) zx%SsEe5+9DA>H)5&vRG=~ zh=fRkh{pm+0E#=nkX_73;)i}+({N>DmHtJcE=^c#1&BquQk0}{(m}bOtxyGzO@s`S zrd5_2;n_*l?(PJUWY@aMhi$_ID+`$n@k*x`H-@I9 zSY7!mGRJPOU|aE#%u$e8`*0=8ZX@hq4>eUW{)M{{ra{E`*E637G#7?ZW0Y^AR+2m$ zE&atqqoKCNuc5f~$<6kuD>;zLoWk(egFuH?tC$D!SsVH zz((JeUS5gC8YvkC>Kx2D_S`w5za>6l+oTc6)hVB0b$q`Q!dWibVcRk}%1DT^(6oM{ zVpPNB+mD24`cj6bjR`8p!vUL=DkNY-(WuITKxY%#TTGcXKTi2T2(uZY?7JAe|P$YxMh6#Un__bnDpkJP-zsXM^p9~d8J06aB z%>W^^u=8K!TZ335q3)-wFep&*!xF;}*nVc9!pO;o{2oRLod{)$!*T+&+qy@lY{VO1 z*oUx=W)*Q~tDox%$+(ZY<5I=Yz&CjqNtw@(M!a7I^@y|ov}30IVxGbBy^{@2KcQt# zQzU}4auh_tOAD5<3<_pcT~mE{9Y@yCvk^t@DHklc=GP`{H!=_2V9{GsEq!Irpum0T zb)-B+9?@J5+58zfX6X9^FTwVKS7W9-GY_RN;o#Stc`!CwRVHe$dd#SNKZOB^27XzH z*J#DZH5LOJfU|>KEN5_vI|3l49&ZX)^bVyltcN-sEF^v8#>0_g;O!t8=oRMV%m=+s zAq(=Se`y}RoH&E{YN?0_u;;oEOVF{EI8ahnN{AO2;!vW%abzEa8zKa@xY$g)R%ou@ zovqHP{AGZwO+mg$7}xljNmQY=Fpp782nId#ueH8J`VNIG6=}(OXJz6)ljdN%%!1-3 ztycA8Eece1R}lfZYvQF;nnEMn;=FhbD&Ryw(M&=Om+tClIA{Tr(xMe2^iZ@ z8B$IZ+#x4voOCR}Tpqq2*!rqMr>~#5kT1lAiDz484IR*IZ$}d+acz#pfa~K3)!c0-aq0 z(JWyyTBUNxWThHMs3-)`9-xduq&inZlD{&1lY$9C0J{Et7W5#YLTT#y@tVWhy{cGq zl{0V#+T&M*%p!U4f!PXX>CQjgA>$bya^|Z}5uv)@5d63rm>5T|_IxhzxE0WH0ix+t zf*O|fV+hjC<8Ogp8tk02dgTje8;8h8kF~CfJV`GWc?+8%GsG&#I^09EKkbB+73~w*TRG{=ZJ= z|MV>X^F+xdfYqyV<=Rae#iH5suS{++$xYxn`u>+Qmc&mM-a@d+= zNrjM&s3PQV_}PXvo&FH>*_esk&X1hmRTXCinTgWBvd4_!xsxYCyjpts)7;A|uG07> zV#9pWdp=p4(l(f;tsry`_i9Rd&)hkK&i-}JlUE&IuG<23e2-`Bk$Nu|N{fnEk{WUs z*7g?3;}gYv**VHZF@syV+pne_HqdkzL$?nc*1@y90YfYf{CGTLVF$D|c*`R!MMY|Q zBN+S*!7g-v`WJ_yv<=%H)H4GaASsUKHU5XuCI7vzNRH)IjplEbOI%o3(jRgR^crUGmvLt9%aNj2jNRJl+^A&jfPWacM8QC-xC6y0COG1pq=JVHTZ2;9PYs>sK)(Upx#LIxN25wH`%Pf0Bl zyXsiAWlE3ViDAR>(A{3pFF$_;VkR49tCEzE7Q4x4PB7QzliA55i%%W`A&33_DS(~>7C+1S7x+?io` zj&jcpwz>ia=EIECC|hEO8Q$|`zANJHKu}3Ljv*V0Wy>UzW~T(B?Bv36eI5RV^^7}r z5SuNS|;mZyj9q6KtM?)%T=G2%hE+wzNIoro?YhQ;H7mvR^sHGWOh4OK$7FP8&N z{o?~sRS+d2z=aP+iPf&hYf+u4l=saMuX*4@ZU!dW`p`w54|OKpr>{j}ZDi$OViric zIfW@SXAER6ZCkO-N54@RYTtY`8ftvEj501gBKc2_z>FVV+YmK(zxyp7Zl;=3NEH>B z`vIGtPwdPzK(L*AW%Io(jE>3WeQcobt#uwxng`% zSVl4#IcaV>=quEd&I0e*39b{)PVWQniOE3%_BQBU6@0n|`wSjx(sbY1xaM5!sJf8PF)*a>igfQe?j_@edkS8LIOs~b|)K+RTe85M1I1E zLSUL$R)epVjVYxHWLH^eh>fj1oeT+;-hM^?Kniq!FT|sVtsVUEQ!QL3%d@yw3=b^Y z-;&RoRiWTrwJ)&jp zE|)YzBGTlM&+g|0SDTh;uee4t5f8iW6M$tSDnLi{y(&Zu7PuYc@VXU9MI_StETMEWsyl#$ z5=Emvo^OpQoRO?a8)H{+dV%5c7uIT@z}cmzii|AR**QnhimzQ%p`+E$CQj zRO@>XTs)?Z$#?UJ&jUrFqEsm5)e1oGGFf!?-DrI`r1MT-Et~ay*V>&^5kEI| zl2W4Tlae=1eO2Q*B!OyXbsyCXV}WFn`6ep5M$;5qDOAuMZ#2fRId1GswJdOKsf1qd z^7JkyaI{F`-yCfnDy2uPyRB0DE509g$S^W2LHIuel!mnDA z@}yQ4@9W+R1X8a1$uu|^g93%IiGt8WvoPu&)It2!oA#xyj5p1+WPiot-KkNLPKA=2 z=k*9|8K)U5@w>bq+}_IwcULsW!DbU;DqgF%$p;w7xPutv#f=6OyCmeEuC#;=EGBUq zSMGZt>SxkptCD5C#SDymBTQtAp%M3&4#^wgIzdFZ*w>;;O*`*xrsLUnUYqrW#_&@g z7BEBUnCSN!R>C1-Zf#k!Nl{`_X}4v*pG)FlSz8NU z6_7BWM(qRdbP_Q-n4CtX?8zPep|@WSB3gcnLR@|;)f6Hmfi9*HH4Wf=SQ=rbQ7gDS z>^oF^XFP=rHivk?>-;WaiJIpmG-3Pxk4N_J??3o*#h*@&bZS5ODeTW5uUs#Ddns#v z${+W*xM8=MeV~_9ye6oc+Zbv(aQkVij_SMfcRuX$^mt{(zJgcFJP_6^hETi{fJ7x{ zhp}~aUB0UcDQv&b5WFrI|^9THAyLBU+nnC zZNQ;N?ggQkXnBHa7sVX6f|yGL-blXe%^@(|upR0f5s?}L0v~UV3bNDc{J5*6%vn_k zaA9;IKu%`d#R|K~+}K~@ZJp+&g$>@nVS!1}VHshTuC*{dzJK$QBgAEja~x*`46d?w z+C5S|9`&iQ#yLhj{A(oBSWE8vWcv+rOJE|wEnf04-ah*IyXu=W)*N0uKD)>RWY&tT z^7FD=OU^DwhwNxvr*o?!!V|0}BO$#fgpPFZh6+o=C-YkHiZ+X3nF_8qqq0vJ-e~4R zilc7Pvav4&J6DmJ2smaB1d6mJFCD4$gk#HS(0 zr*zBvl2vob#OZhk*dXC`{s2yS>N`rWD}3s|4!2hB9HTiP^ig-0@@g7|P6O0MaIi{+ zkjS@h$)bS}a<~5@Df<7%m;TRE^r-(6RsCNVGE{V*WoA_`joP|L4`E-^Z{r zp}^zO1aN}rd?ZqULNEyL8mH{TlnQ9F_~RWi>GO5p8BZ4{QgZk^1O-~Q5;93Jq|zej zKB(OW6PQ@BoNds?W(SL>i4RPJl)HYs}4h?6EyU_ZjZcYoWlFAF9*2Nibc0QpZe&v~GQW!cZD zJ3q$tSHM)Mn956`ikb>;@Gzy)i1p1v0t34Rqs!XxOx7XPh?wHjAU??K)Q}W2Nzw;i zsH1$eEE6yk2GufOR$^Uf@FzO_{*P%U45}8Z(2H3MsFNmp%}L6^mKDgGQe}T~K|ORj zibjqX{yA#9+#LP;51TAPJ59M`UultjGTH%RrA~=TnToP@?J49Tkg?y)MG~P3zGlp7 zo4Mo0t~g+~eUBqFWubrB9Ho->1A{J~C981Li`C8_c@_&1BM!RbD#*-2XABU}mOZEbc1}VpRD&e_Pt`-9yVF)hpDlvQo8mEpi4ef|M#JO76fuYSY z8lS8@-$A2X?FEuKZ~x@w_9MFHdI(OpTulU&iibK|@a@HjgKGRpo=o7#>@Y<4BG}l| z2F-hlz<@(o3;s2y2vL^DwD^k|ht>eJSF!+CUm=5CfqXOF1ybxrw#adj{KvL$<^q32 z9p<)7O0@7}E&I;3-HF6quycYkR&hOj&lGxKKweL;snXxEGU4c2whG{EsQ0mz`@(ho z6ZJp~jBUJx4H{iy(mdo?%TNQYn$l|u**P;n#BNIHp9LbroagG?`>=56-nC*()AVYB zr<^lV-P!-q<`xupBKq9MtY5h?ZRQ=Caw(CY%N8V>=?J_~h9t60ei9IB8B{LdXqW|r z&PMqTcbOl6*(pWG7bqQ|p6HDwcBmJ~{@M`%vb;-BH6q<6z((}F(|HPp&`{_Pg5G@t zusBZq9Dn_m4^~6C$lMYK|C{l7-NU4(ms2^}ZO_9@r#akiMWZg6-zi$RqbrAvjkBLy zX4wkZK`?t{)zvqRgz2wa39|Yes?Un$%88l_C2A1n*hd}~{q4`N#s*OA$6w4M4N)6g zz|;W&>sYY?8!c1f&OYq)wD*5VeVyI3lGk%0g4F(B|AXtA^R+N#O@&^Hw~m4OoQLoi zY^pd?J}iK)0+FM?DZ8FLrIUeVx@@{#eXDBrS`6vwSNq zLbD|ezPk_^y%rhR?7Bkb+bFKn4#a;zBJC>d4zIu7Qy1gQ(ws9fYlM0bKMaAK6zUnp zzI;ktxb2E5=YQz{V@^tS`YFfUz;t7KDnd?H6ne4hFOXsA?e6Lu)P-b zk8oN-oD=v-K&@5Ua`=ceLR=>gMuDlK$N~0zVBI153<0eDmx*e zizGiMj;Ywku@E()To~9K5@+H;`jgf;$Hc(xWs|y4nus+P`_yzk5WLdo3!nZi&%%gV zjop9l{d-uMg_d_;7QoDDcsLI&G;zlO*uJ#TkP72+pV`LJty|7OR6=L7@fA&|5^;+L zH@{!#%dXI6<6pShW;DT8LLz%8R6wDw4Ro2r@7AU_V+{latwayDW&! z(3Q)R@!EU|%pAWdG6Vj;NTJO`vQ?!OiMR{(IRc+Ry3-v#eGb94X{+QIL`P3%FJ^Du z)~qfH#@xjwV;pXPfUTn={QknHL=TkJY%B97FG*Ozs@HGj(GcLo*bWY3%U4FaJLYMi z*lEYtq28iW$PY@$1af&ljuF_@&5se7629Btv7pgGPetU^7J;Bx_#L@I@``Z=oVsak zdnHf|YpuG_`962tuA50n1U91say7LZFx_=zRqVA`|6s@T>C|k?Kinq0eOuqi_55-b zqB!Sr)VNqP{zRpPUq6%!a?W(J%SNpv8rem_%FqEYq_gLm9@`CRvP_n)_SC&Koo2v& zXQB~&2}0PlT1R0eki2BJp+vQY{@m{s>4^uP`1A+s+meJJamXx8YMo;l52|P6CnIzs zHgs0=)Gg#tDl@5O#}y~+m(jLNm|LD*Df>PgB?c;LR@yVxVwQP(HN-KZd8x+i1=1j( zj`ZeyTf+gzO=I4$Cd1^Ujdn28^N^LrmbK(pehh0#Vrum@#6w~Ehq-u5B1J4T1so>> zYb@~R0hrka)hx+wF@2~LKQItG%5H=1QN<5u@|gx6v6wlt78ak1VZwrzvTxnm z@qgA%nlM{9vhc}cqklD!hh3$i*~rP8E9Aj^+Pi7PYE@-O7)vIDyO38MkBRPIKHtW= zs7o*Mq>#ESNX`@wz^7SNU#Sju(ri^7A(l4xy7Dk#5-1^t6)dj6@H~W)$9ev~zoX4J zO%=6IBp>EFOH@Wmpav0`i5ckQoK^8GUVGuRrX!3QKi~wfM&H)f;A{2VME+_Ah|xOG zyDa|n)T96Mj^a-@ztcMnfBdN5alv?= zS#;$Lm446Ue^;vm_G-IIOKs*j)r)7>@|%p?Z#{Wr?+z*B-j>excJ9a@OhpZ#^aAi~ zv;BFuTo5dtt*vVsow<|)!gMV~*TuMI5|c%lA^bE(Bi8i0)|(T1;~6$j_WtpG0oQtp zqQm z7Px9t16@O~m2hH7ms)UpH9yUF*+rL+dn-|0P%z%qP7D&&i*OwMgl9UF#C)yCAE5+k z*3E?m{xk3pf&vAhRu&*SjSJ%Y6 zoosFG6{VWLTG$<~yhy8UMwAzHOj6fbL^<$OpIH+8`nBKp()n=$uGUJ7u$-+@b}Zm@ zcHPy5pB{}Ng|jlROFmE;!Z6P&L3B+`?RYtIdjx}j;ju=KSr$~7PVzk`vKqi&V&aSw zd$;*hKkB^|1buTF>H10?MDRB&`sdoc$Rs4VI zE0L14X@BH2ye!L2Z=i+Ub+ddebWZ~rxASB4Rpiy5gg6GAyxewtEq$(m(wwBO^1*7= z%E-n#n$EHfcl3DfbQD}8`*v_QF+IQc=wJqA3YF&HPQ=SAOfJGkEfV}XTcV`2hXTE< zq-7IT6P;-yD8TEfD(!vx%@eJ?d)Btg^@zG`yP!ifBP!B*s&S*s(`lbu7~$SoOw`Q} znXJE@MRf8xsmd6SrCTKvqf$enNjf)l+jzn$gzh%14)*3}F5G45TJ7CkH{Ei2?%_!` z<|T|V^EVMbE&ukr2{klhvGqWr37Bx!92{uJkkdVn1NL6y6^Dws~mj`9m!$Weno{DXFIgUYH060C*K5r#RTC?oV#g%4hk`{y8!64QJIB z6gAjz+!zT4pI`mZt;05R#RLKxHzHsTy^q$~e%yySem6}bGXSE|NX>lLJ@THJLz&yW z#{&R)(m`oXJ&PT^%v&Dj_IAqgvoK?r{7}YA262uaUx2^!x>%?K&Z#L;D*mm!OhhOh zNNgskA?iJ?i+&~L&S9e zZ+gjTx9r?@8mK7o@Jy<~+rr3%Mpy#~R=^DJ2-aCJCd{FT2mFunYf)EgyT21Z>dUV) zYAD$P_9lMZbsX%rriY)VjfXfr+tq264od^gKTY!yLYYOJPq&Iu33Nl<`rG{~?}|Td zLlNcV4-^<)2{oNR467XqmNT|#!G2K~wy)_~odiLiRVTdk-Zp5cCneuFfwXdt_& zzF_q;8PBehf6j=^*r|(~$K!rw`j#cQpHl+zDfuuxy0Y6LUuLMxp^}iRm7Xe~hIp9> z%B!OGk^LqI%2=2OTJ8OWAU!?(*Hm7I;oPBJ^%vb(UlnL@zJ00sT7j1x1&FTAswa2~ zK9D}g57@mwgtre3$Rs>>ABr?5LQP0fmMMU~ZUN*@k|4_&;+w6uPur)pIou8BM~M?b z-^)>4M|Cz(>8+q=`%Xxz1qZb@M(MNf-@DXt#(XCEhajuJ7&R4DL{k?eC_ws;!R!*f z0#0ne!j4WzrCdqT;Epi`-w=@NDak4jCpK1S%o0>A(}?1_8+{1M>c5tBO{7~0 z^$GONC9Zz);iWhYJK;Cj!0HaZggbvAM_O{1`{i2G{;SlY>O}QB*6gzReXY0=R$`N& z>70o~`h8TK%bzfrVDwin0n(eN)fP$mZ5o}pm!zriqq7sU=>~n>@TbP8;I6lJ$a4d0 zsn;tc+>9fsGgm1h^Y3cQN#GJoz;$}>(@A&)otNEywZoFS@P+(IpOgFh;blKQKhb;A zP_ZO~Y3Loaq;XCIG1_S2gWY^#?zner@68Gh=3r7vJon*3mvWyla!t!QfTZbVr=6#- z$cM}=Th&grid^Fdhpj)Xa{ANL6k1|Cgth6xzsowREoNUQ%1Z<JMG;8NWTtKx`7cyaPERWeDi29Sb>bD7 z_mSNU?x(P8ahY@JzcfnBIm@NQhx<0M;NPN12CM!8E{829!Fdu*XmA_oO9jTR`OV^h*3L(0R7+A;=&aUWo06{lOK!3~>R(cfs80f6Nf&OqTk)_kQWKW%WyDccR8MIc#jkql~ZrTb$)Bp70 zQaIGn&U=<+pP*0Yn=*MX5_ww30G7US!3Ue_N3K#;8>5nh4y{RPtMw+DcG5UBgqy!7 z9m@B~mB10-un3S^gbN`_)?n_#>C-apq5A8ZH*qZSW$XoVLF-C`69m7ZBP~;ENu?XQ zUe^c??U@B>I2Py2X&Iu zR9vHdM-4H;X-)6dZ@nUhz1I1nOrI7XRi|%AxmbF|cx^RQm+}W#LWi=V=13puTCYN{wQ;wg#G; zTj>W2MdCl}dkPhRR0@MwWK?E zlUD!e5WV*CqxpXVDh<{0-JKU@21Q&iUA}VV0+3gYEI=eaL@gEo01W#{L_t(`8@qBB zOwEdxy;z4V1Yggc1@(D)>>ARkVl4N{7z}r9tf#604o@WFZ&^A7SjuQY9{n zvJgY_5VE@j;obR5Q)BJBMpn_a(_)>op0`okY|xQs+yyeU?#xm)F)j=1dd~u~KHD~aT_9QJ$Afnr^PxW&es7;{&k?>;ukXEx1lzjgeL>AcrI<%XYbfKGB4~u>rT5k?i;pA{Q z=$ItDuAs{cOBzw#pxk4I&B#J;KkpEiU50IN42HOaM;j@SR9k|lqaw2C0zlTiO288L ze%@V+Yzv%~kOiXwR%6uZHY1B1zWqdYY3jO!ENE0&rY`q@l>pf7vLgf7eL#d9YQq*SFS@f!NsAweTcG31Ms{6gDv&ky@+7Fa8@iqoVa!o zkUiaZSNsSSTNWxO;gi4@^elWE;KR)97TKm_$89@izd~T5`d_E*u^(e`&w7-z*C7i_ zBAXmeBrZpOD`ZJ-)=>oiCmk(mSqWL#P?7P-7X~7dmSy!(wK~7xDF!ksuQTWx0c6?j zk)1_=>0ozUZVP0y60)Ajt&ycl$Zi}AWRcC<*C*+rzKVdUH!;?S{?P4Eg>_|YqFtt{ zRch5+g4NeT0a)wU%*gNB1# zoao-kjdn4`jE?){5#(!|#f#u`i}!(Q#!6Yt*cg1Y3Ah&OgzN2s#>gH&_K|AG-4&CJ zI_Xw>A>0*?^6m{osm7p$>>L;dSzkmHpKUX;KoI!&QLRn1Wh=-UM!+zbD_v+VZO$<3 zDTV`ZOPwJFY)u+q0lBsWZOU(H7(mPc3{oe#a@s_?$-ufs@e`&4z}@CqAeSg8VZgz?|6o7b6HBW5^5*lRwg)Z zHFL1$+RBuGnkfq9SqW8>!fPiSY@XK8XjVeyg68TQHH*oPAt;f#a(M`nWcB~fnh&)L zWIsFhG10w~mi0Lq)9WayG=p~I{d#2`vW7OapFxgWH7y6ZG_cSOl1jUGuE4U|*@Yc8dBjU^ zR86;-I(Po!#k1$@rkCwKm7pR%KK2RGy@Qse%;2vJi#AxL8}{``qMbmabWuVUnDDV^ z*8?V;Oy^ze&J+Z>_%I*~Ii@PZ0B!A+T#mtxiSF4lvS>YTSiC-(PKIDpg>rF<0rv7R z39uO+$>sdCI-E}@fb=;XXMvUKp?Ayy0se9-0YV09xOc$Kz>c9P#$ZA%0lp$Al~6*K zFWIuy5d)r)k%b_aO}PrG1b;FKCJbL4TnPs@Fv=TW|*HNf|7(*?92nAL~vJZ<=GZ;UXhyS=e>KPxG&T{ISRc zQe|Xee-NH=0BUgP4WohAX?MOGz6Eh7Prv^0t_gYV=mbLAh2qtZ17$v150z~{0^tL^yp;00XQGaMxBnRy*uD= zE=VT~ktA%K;PB7+3>B?zhi7yGQKJDpo0kUGLp*`i#bLt*XTl4cD=ZtzgiBo#ULB>; zD5*kJY{8}u;J&#W{uI>Fz&;JGI6^kzaLsbyvN^(W12yY#1llO1gB`HDh>q1Sz(;Y- zrPmpMn}jSk0v|0SI(!aCTt#*yJc%|#B;bj58@#T{XTZqrtY=$AFKzd0*fcZ@P8~b( z5!Yhbq2hzIo5cVRE59o3X6ZmDy^}ZdmjhUe9=%6O7nyYxgV^pH_Q59LX0y3+ zIocAO(?^CQdP7BDL4isPys{FIoHjD-vm4O9Nu-i9N6;wMBa2;f!yTkS-o^oak`A5N zBH?bOk5cuK&_73~tb9Y`I>ID3nk({Sb%u-mzA3&rAP z>s}UJvNj<0_=#iSf3>vjv|S-3|8>~%t9*c*XSt*s?Q%d?y(ezhTve;UL6Isc zon$L#;2k(YRn@;zwp=&}B`##?yg0BtuK;ysNoDoOwOusQfRdF_{W009H>4L<{-CAT z_=dsOWdrk>iIuCiYX2?pk6=-`qc}VS3`mUZJ})C@1@G$^_+M=;=D)tf=8lt18lJDM z*VY$i0|FoGX16>~Zhf1YcmLG17m_MC-O)+!LP!k}yM|AEvhs7Muq@t@=C4SPwW0TQ z?AR%VzC(XdHid=V>qCqwSTTb4?Z)f~xL5*gaT;_iaGHJC_p1-@`_))l&ooNco&sb~ ze$>^WB@Yg=;kS%y@jdMA1&&)-SU0&6UT%N(u)Tn_Y-4F^@J0XVBoKS-^RAY*1GD`b zYxXYBM!5H&^)-ej-^iplXSR|1vxjXv-ov)xqMu(h`n7!yz#_|_yXD|4O2FD|D#LqG z`jF>^--%6W-=CerZNzE3Q+T7D!W(S|YD9Jc$Y;6^Eqh>?g=Mhbm(EAkV1M6YV>=)V zdbVlo@|7>nV5yZazAzGv3k)RrlxVRW=2hX8hjDXzky$Kz5|sLq!mcW9d{eOC`4x@&VJ#fT++<;h- zSv_VJBYOaeZKP$7o&1LxGmDWuV8MdM#ER5@g7qv$_CNuP?AUFHMX|099jrr%fy2li z45L8t>9)X1Ifef#?@EUSBl})eEEIL0+KE_klEOc<1|^PmVPxOCh9!)jpV$qt(B;(s znPG8YOf5$CJpv2FA{Dy}VvnI-CqMmXlTC;HFpTVbRI$fDIk`(>B}IGUlYbIjurDW8 zwHVp=5G6pso;Y@Fx4@R5{q&z^gNo8&3jrg$kGLX(fKYw(=^lZVtOKxV|M8KTAPhP? z;ueF8k$u;+T9JY^9{>E*$z!{>32GEAsM=GXe0-dPabVj{3R8%Yeb;jQ<4-<4QO562 z2)gd%iBq3`^6_UM9XDfl3?uulG;EXN_-7w~{^_X`CwIj4{{zcQX8Zu~wOjxI002ov JPDHLkV1nvm4?F+> literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/mailsearchwidget_preview.png b/android/app/src/main/res/drawable-nodpi/mailsearchwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..b74ce6dfdf73eed8d3c9ae885cff65119240d3b6 GIT binary patch literal 12578 zcmbVyRZv{f7G(p$EfAdG7ND^Z++71CNN60=SZFM`ySsY`!Ge>MQErg;9^l=0RRA8B}LiK001%+06=a)Bk_m7WP*Ed%;cbC^Mx&P!}QV)+$>pLeKyC?s4 zPq+5ZR=1CqHx8elpHDAupPrvzXeZ~_SGNx@?>mR*CzrQ3caLk^$LCjf%bQ1!k530D zmz(=%U!9_i?ZYl^?l*Q%@krS0eUo=~_m7TGUzWYSyI=daW9=U27LpN_RzgPmerA4k zVP&(XrH_$aw5ekNJ~Ey3qe@IhGbFK~qz2aBGdwx7&@(WuXA}HhNXa)kcVu!dE-BmH zC+ts6^RKdIR9xEN@I-l4{j8$8-(}FshHkL6i`{qMUquxe*@bFaX8NXfT6$K8=l8ab z1K(c`g~<7{nkJxc%@Ip~@=1`aBp@y;GefkFu;^`x!410Wj9<2C=iK9#A z@JhXncZiZGYcl_{wu_3eplaR(L>;qFI9@5j6IAsW9Y!H3Jo)sQ>X zT3>T|#ncRG`nH60AcOezzrf|fCJeJdLGhNi=SO*~afWJk$FpL7HudBHvQPG~QF)?g{5A726 z+-{3`D1c#ts;}wdTxB{n4(Vgid)W{Zd%bNstitZ$c!5OfaE&wrGqb2GQ%Vz>A-Y5z zagmxCI0RvRJ2~=fM=|wuH$+kgK&Fh&Koj~hsSr-?se%7|Zo^B;j#lemz))SUTF>A^ zR9jE}&=!&ZNEa>*oBW!WP65;PS5>%6|4lMpGqRFLvmgGbsRhJf43t`8E=v$M2rPNt z-W^4B$bOs}*5zjYgrhQ#%DQ*_d@Z}VdQYQRuEv>KD}$x`eErC3W{VxpH_)5t`df=q zci_Ny+{;9gj6ESkQAArr;|Y}FVU2{+r5gp2o#&7k0uv!v( z?lbUrqP3#Pz7?rV5Ep%sk?{0arIs=-Y=&lY+^)cCX$!i;>h_UMBMG^Wg_5bY8xsd( zVG}hgvQkqr!l01ag<)TPXDCl8Jh5kqb98j^0N(*>T1pb{x-z164{L|>3xWpQ6h*3X zjXvs_=n2ZIrPkgj1boSQRH^k~RB7-?a5ZYhJ`iCv_JlLq#9!r&Ugq?pVmVLiak)Y- zRxu_?)Mwp`4rChMm4$Y3?Ub6U{^1*Xj}sy5sTXPr%BOfrn>)aBDW!bUJZa z#W=~kRFP*(MF(G}bM?s>BXhxkB~B=D#oF@K`=FD7jmLJ%QB4Lq6OFp3^&krm`}Ut; zgtM-kGyE93lQvS6@eNW}_GBisz4dQ<3N?hHE=tz|pwn%b66P)~3j|;+Go`8u4rL=_ z{C4Oel&hB>CCSJzOQDt%TBIt4v>U`Q|1rZHJKnB5#|3o(?RJoO?~X>JQ%nnkDb32E21u=PDu7HCbHzVsF(Uxz%IwQX`hoM5gM z$JEyL;r24VP@HSh3+~GYW{qb$R1|EXgT6$tQ2g z#O2@E5EO9TrMRYc-7qa_gsfD8;;$M09Vzqz0cF?>LyMHbPYU@u>{fYbgaJv@$7I!g zpFosLK4i<3U&n?~ICCoT+Oadou(vGq6SD1pM}s3PC>j?I-Zc``l-@}jluN?g3CeH2 zcp>B!C%sZFW*J(4VZ3yudglnm*w zR61RBed8Bf4ju@ zUj5EebVipOo%!T7{t@hSt4#JbZ(?-2EJePapq8;lbLg~}HdPWFmEOB~mJ^mkrA=99 zRu-VCUy2yqhHCK2=i>L`yrZ;yv-ByBOZRY#Dk^@dDzQ@!JiC1+&p?5o29;q1NTJD( z)7CcIbzL^4L|HgtmY;M*9yM_Ovp_kSk>?}WWu{Lske|xp=0G>EGe}{`0+_tPd=TDm z)nHG6%;sdi_PshV+|Wl$;D; z8>Gm%GKGRvg*{lhwd%$F>i3pR==7)@<+Mq;ZV*;g6r@1pNXw-J8Kh51vM=8fiwc)k z*B99V^w%Mtv~-zy-l0+LBoA4b=9B9RhdIPDAOhZCyZ&^cqs@+gjD}aPH1mbas_boC zl;uf8lqVqleN*>Yf4g!9WA+zrqq2%TdzP)K2aQTm;+k`uLeVR@{5csWnHqy0w)Kgb z5^ZaQ5x`>P)Vo5z-Z48{qR=D;cvuA)1xC~461FebOIwU_m2tRSTQaH2_6qyvGg@c9j zR0l_MN`ROz=a=eV=ZRvKgeEk#WY5mQ4G;bx|=z+|S_0OLz3#B_1R6*a*z4wk_v6Lft{U1uG_nabdgb{oP_Hq1t*Jf2FD zs5Y$NVuHq;qN~3eq|+Dzlpmeu-Lj(aF1C1XbR3%BN!Ik0)vq?$^gF?&fkUMF`{q6g zb@Q0N-}}4GzFpCsq3o1&Yfx!<7#~f;k8;H4b=PU#XAisd4abr{QRk>_h8H3y^)E$J zDqDq@N7iv%i*VcY0(hvfQ7n9^K}I~k*Uc2ZmFaXvcJjk@@{b1fCP+a+0&|D+g)EXP z_riT9-P?9#YP|i&I>90^!u)zXh9=k;h5D;EukHC=$ga##3wV;VZ@9@>Ogdwb4-@ly zDJHzRO;CS62-A?gT{B}41K5~$>MW5U6kW@uPJZEge+4bcYTO$0sWt>Dc?l;*DN%+F zmGX`XG&kA#Y6MSq=qWo`#OaXmnDLRZEI0+`(O(!yv2+n=;8AIt?vxD1{;>y&yc6yv zlpWy41H_Ls2Ou58*wBL7DDa-=Ik{mSOQ{9M}wg(Kho`)(>Ky}DbMZ%R+ z_;nxpDEBaJboJyK%4;EW9mB)4ybg&dnN~+Fq`OAuv8|SIx_w1?0(s7V%^E8H&r$O- zK%1VQY4mAf0c%aE-_l2Zb3qfzqkySSV&J$uGXSug(-fcE)in4kPV9DTxsdQ^JrVpE zO3ailq9&M}pwx0>@^7}$&{5BFbB^N3w(NCq>P%0~XR=VRx#_ra!|q$t_Ix*!U={ua zs<78B>lp2f-`YOop|vWu+5OtM>i$-Or_!cKom0pXqAdY~o_HxYwg^+_!y z2n#&X$XlGc^I!C2Fz%{V>2aX7sZ=`>aY_fU7c?8Ypl|JlQX}`{ZkY2VMGZD zyE45EF$I?kT<~2uFon4+ehVf)(+RrlUPMA)n5G_%Hg^y+7@*>hqU4E+6@H}TM8nL+ zm(}r@aKibqwAq(IwOh6UYV-nN>!qGL2m}O}_8wrn&6DKXivfGLRXg;i{w`;Zoo*LV zMA5WTQFvosA79Ye&w6(@$tSq(KN?q^)uwC9W)>bej(V(o_~;B*)S0(7&XX<>!lf>|)B_O?j;;F0_AY#Brn?;j z9+QvSpIFXcb*Uy7#!7@?(NOv)mf$=b5gvx{PGg7q~{$F~;s_LSZU zyUIZS3iRFZt5WXr5VW{RJQw9En*$Ch`2@)_mLyG4k10?5p5mHoIW}1Wp3GuA$*(F% z%m7E;4%e<#DMSEhP_xTf+L#W)4Cfj{E-q?*7%b*ztL;sNpxVcBuZSCD9Lp=I&l1?) zz^jpzmKHLhwcNW&^g}(W9AkVG4YoIIIA=|jB83y(Oo@fwoC!l|iGnq^+%EqE$X-y_e0SXBOhwp)@*^sL;jR*4r>M#i3KRn83`_g6X9h&WZC-7JdYGY zcAW=i;(>2|C3q)9>^^i4M*>};6Yx2g_(H|n&+ELuKDlM!t+1LH4K~?;6%>8gMV4f1 zqgG7uo2;By?Lzp9v*JE@;_$;faow&#lAl%iV_V1<%W>GLWQd)!a4bdHs-o(OE)m1`+Qs0n0JX)jP&*@(-X9$3m>3FhiR^xq*dTVFsi!w^_l z?kzRoR1>q z7$S$xlM=@WMWlv7UI~0%$e;k=8Tk;6#7@OWF`2S+7=zDCKwtYO;F5udJKm;PRuX(r zWQ!fvQ6l=R(^!F5%|gDnXrfVy{Pg?7-!R@H6zkt#LUBNYv~o@*A`rgqa100Dgq=qC z{Q3Pc6Mep2ZEBy3AXIb>eFZ0JzM2QaY%Mw9m|;OKb>K`Cx(o6`>qo?f7k}nUpnAFB zyakne&*xkH5{oM@56@9_4u%DG2mNJX5~Upygigrgv7WU8IWtb*eWivfdT94y4!DY3 zVt^*7Wt|Q!{>E?#LR+i%=rMV^)743nP!t=y+sea$4x-@}oxI(HyJjLXVNb^E<~g@8 z|5F=VrRB|XOkOl=t(isAxRo}Tb>MWP@Nr86X+yWiHoIUuFiTb<__lvz&;5O zmN4~d9U_4ieZJ4|Tlq&FI7{QH=Q)>U#mLvh@JyRa2thT#o3ABVlTYcto=V=Yr9Y5&Iz76kDu0XQINd(1;()vW zixtxK0;QLWr~W8LxT}VkQ&K^?$vIdATCEq@-=EQrmqKE3+%(n1dCnpYoaDHx;G1^r zyrn5W9RM8Nli8|E#DTl;|ofA?#3fZ7tLuRI>1v0FcblhaSMD)@szjjFytgJu+3O>jC86l1s*y57JQPAwB?rnfrcb$$g7)5lcRw~+F1o% zBQsj zO&sPnUYE&nycG&A1fe$QlaEmZZ_6|r28Q%&C*eJG+~Dc|8z)Krc-(&0aT$dOd~DT+ zju(ClgiZ|F@u|K}SNznuEl<*aHsY6Pnb~oz{)6MafM)7;J$+^C7l%udEq<89kW#L<^QeiDTtHR@l|iT2|JRc{6{szqL9j3 z&s4H?ubhH2WHH9gfvwxOU#pYCXw}6_{h^!a@DcaQ=xWIaY~K6%QWtph6xrKVWNNPv zZKh&zL#$7UyL-JE-R~t*z_`iVA0K59Z*?)h zM6~IA?IhhbUHm~YcheZyX+q4Me%p@bRq3Ws}`GbaKdoqW-53Pa7BcOLqJGa5ex0r;>{1dQK8i zACEMpw_;?m6P{Ys>S_a$T-RTQAr=-*Lfa(9Kw2N1m79CS(V?la67siJmasfS?C4*8 ztKWaNO$2C95ZQ43M5FeRA-Gm~Q2sWb3ji^P2+(dC(!aXMRbSAm|M;`lQ^eS`+aq6J zUEqy6h4qTR+y?5n`}{IPS$YvR?f8@7XIhP`qWWR-k)hHQ9#%uEbLfs%jD zmtkl)r&-q_ORwxJdiwp=S~^<0k;T>h_&Aw?Q4W z|E}9Pec!Uu#*%8>|JO%u1`91_snz*5811!b<}scQ|hYhmv)4lURyz(6k25tOZJ{b+;Ysf?XP<661;Y$Iq&QxO3U*Kl=^)WX=%cW_$$j^(R* zL45+=FAZ{JhMcLKcY$6%)5n@M!NpE&fy5MpN@<>$c_DV6IxINt zymqrwQ(qfNx{h+jU3aExN%B*Z#4~yCx9oI&c*Y};yDo3X zuV+Djs_Wh0ce$3)=6jo|$Qy^Y0TH_eFkD*3q`;PHTXuRCCe53VN=#GUrSU|qL*o#k z)GALjazcAl(6E3b`gsQ_X=VKGi` z@Fl|oQ7eERv->!+OE^`y6vShX!{kFn^S&dCrPg71UIhFY3%HrvUyy!SugoQ3akETm zxnp`*{JUw`<}0A|YeB4~8G_HX*HEmab7=hc*gnyuAS;T}`AtTam+sN~kdnxyz$Vg1 zyv*B!Z<9@xxmadrXQg;$U;{8$*(~X08Lw#yX*H>tzC*GF7bN;`7M|l}JzB$hr_2Pm&k8i~k_?=w8bTl5F*a{Y# z1efvp2C_|O5F}6Cy&8-DOP;y<*VJtDj+(>A)l)kz&Auv1vd*T4tvLQNw87gj7UK9F z-*|r9^QKqsNk6I_S7mYCKIr8XK7$v@GBn-fi~QUOC7LMjWV<4tj5hd;2tXZxx1WSr zd(`*bSWoXB)(bS+|E#T&4qkdTIc&ln);u1s3h;&uR{q|0JQ5lCR08~mwbx#y?W6Q| zjVLwY?dN6({0TkvY}25|s-|m)QR$M$w{_$8487UP_2c0Fc57~IcZce**t7qodQUN=IpBx)AsdnO-nZ{EUHyqTbF2d__O3$mio$Q^ zRtdjdn+uDDwEB|nM}(ybU<_lo<=jS}B*`i;D(S}OQ){pvY*N5y&Ts8sJ-VYMq7H<) z@Lh-Nr;qw51Rkb-c)sH4=Ct-?A&ReZG})m2Ni=5mc#)iS8xK$8_Mh-> z7{mcT0r$2Jq2{WFl{Qd7f3KY&TN?D7N_TRIqwi2B9JAv3d7vig2DdT(yGJ5Xw(o;Diu^14?+C|WI?i}I!yffD z(Z)E^yF>83uy59>ljGHT$k53lasOT)6}>);*@Ahqt1!I@reRQ9wJz`rx!)~O^|Eq< zbUb=pi99|e*J(kSiT(11)xALa_I<|Q;vKs89zAp|p6*TuXLKk@73$=;9ELsl=&_PC(|rH=}#wO4Kc=w{aJK3|~kAuN3VLH?alt_*=J zWEQ*IY>=E}PwW)xZPYRBFcf*h}Ge=!#ijp zs|xPlJvBprub(}k(aI|eKeYC96otA`{jinX356RK%Pp4rZANg+nBqReydohF1t>9& zPBEt5@N4hSue>+jEPmaizWoPBHa!|+?cHZxdwM{VXux8Lqe{>ZOWEG5r5joqY8DA# zWMd+!8aL%7&_~YV*kBU!@cVoUVZ^c5x6*7RlB|hxalUt#YOFbpboA;r$kIL?>={A% zv%~rdZIe*G%$uTdk@`nsw_vJ)Y$46@f&+yScKVnU1@eseZleueC!>$<%JuttcT5B+ z7kjyM{RX{AFk3Qm9*mDm9^nJ@&G}(`J~_zkn@B($LPOawLvPTD8B_nw5?~G|24@Mm zRI`&2R7RxJN>28@;`S6{wOFK2+c8LH_vH^+w8ZA3?C6%CD|Wb9B?^KAEDQ$xH{}S~ ztpTX(Jqc|>9ngd%=Z;)R|GsE*o&5$!t~}5aL~_MDR}N=##abQoNG-uIX|?%O9!M%j z!h4vU_U`@ID<(k&u$4w$7~p&-)G>=k|5cVYOp0{$hApLR$T|Vr4B6q=L-7fhB>!7~ z%^R_Fo^xe0nz!(e+0T+6LRaa>9?C}dSTJ!&63id4L>#f1-}oYgI;5{LjR4-ee{3BKC*A5Smg|vcK8{l)9)J{mI-t}om{<@T!VMrWe~&By8aaE@}GQwx~nbHLQC}L zemTTsb#zjFomxOWX|k1_q5hHU>7ptp8kL*y?2<7T`d7sBg`S9Ckn&2`e<^?81%c?#cpwWPVq87t8AreW3k7_(Pu64B(op7XxIQ zZfaWJ6>^SBv!Bn_V`N0_varZBJA9~YBuo@m+Sh}hQ&U^iRX@GHxjLal0+ljxG<77R!<$~MSaSJ?Y ziAn<=8)R9ua3Mud8!Ig1AkIpzMM@#O;6tDCA!YXWnM~ld70qS3NK?o>DX5|9wdddj zIcb#%&nDJ66#yz827HV5!#Bq^@Zj@4(MshjKd|PPkipk@B0s=x*0B)m4b@oS2H6HG zX(@f-jpoM3)Gq4-uY_4TuLvPBgCPPC>iu=6hqUSHaMU%`4+xuRs-d(_?J@1^l%^53 z*W+h9L^EndGYtAU$g%S{AdY&k28{~28N9yaI(TII+eZGtTCXl0Pf$2%RhUu&dN}F8 zhcRl1gS!)42TLPx6|>tDoK}OfwZHy=f?{>*LUmpVa`BhK8fd-PA1+&9uBuGJoB72p zrgCroYd$f+8@E0fm_75Fk@u9k4d~qGpm8kPKbp42+?auec8CtvwBkjj#D!xW3E^*F zKZ3=es|)t-P-g$xIUJBR`f(uw%$Qb74GL!S#qy|h(EKgKv>=npL7UZCfbBWVvZ_t1 z*`|Wf5LACJGQ~AarytUsUx7iz^~fd~0yJm{zGOBE0RH{3@qmRCz&?`=`R$(iqK*i* z7SH8-c>3b^=ACYwD6D``s?(j;y|X zebo(a8@HQ-`}5r?w4Jos9K56qn;v=93>yXeaoONwYlv&u4aPU9-2s?&Nri{h!WuS_ zQz@y<5J^w01tyLdz*%>Mf*i-noDDfblN3aI+S68EZ@+OZ@J7)iG;-TKj~2U4J1J`) z`ER7$HovcounBdL#NXX*KP?1ORyu)`V?a)8p ziqGLt%5=xn`6vxn6mVMDl7zIYr&5WerQk(fHl3S4jlz|4B=Cl}9E>6uf4csi^vzQf z9_B&sh&GbJe688)1zip5!#+UkZ2278;0wQ7Oj%v&z=>sv$w_|;+VTWNr`k^)?as9K z8P3?BuoKODkQg=hS~M?F$YtYOCaU9S*MFGNxT3lbHL5?Li!y}dh|IZrxJSln0Dfq4c^RW)9r15Cpm&uzUbSq8pJAz~6;haN#EyA6KyvIr z=$VszEXc4i0Z{K4gg`@>>Qu`Pk2#PWFDSF<}u zU=KA)Ds$`0drR%zI!h;=lqwtMvrD)y;#vrTMBtTZq(QD*7}a^xC&#C4Al@fq&zsQG z^!2*-0ZPt&fv%g0cS|gX8@w~FkH?FDdmPHD>(T^5sv5JT9dqMGsHu~1X)>;(uXq23 z__c8j8`|+FYMVlQ>jfgIi`LvWO{pXd3P#kp=VlI$z4McAi)s*B{MSxfTkP%KjnuOM z+m3z0EJT#V%+gZhEOK@~$?)#sf??Rmk|B&^Y%6mkQ`xANFYBo9@qNeAF213#^zim5 zzBxUrl^qOUd08lDt}c)iZeF)8i7ccOoqOpp ziw@9Qv~F)8nXjCzfM0I?;tFcZvn(_e$2Z{m5voNj&gDZ;&XEjBo%YyX^7Zqnys4Kx z%?u_!(L&P%!PVe)?)>p3SWEF)C!=>!EXej5wp~qobj*-2kwbP?c?&qie7?uqlzsQR z;;rgk&s5@d=v;d3?6IjC2Sv6Z6O-9O&>!GdVf&{nqk(3+x3Kl~$ zKP7~|5iwcwZ83i72>!jeu%5-?#cuuW0`YCx?BBRB9RhxjIyxib;ReqyRINh-^-n+7 zvpV>F5Qm&dWf}XTUCXVn|9Y_>gtaGV>$J*4UdOE6e?{GliQQ$c%KMsVS^el(_uk2v zu-qqAAcXya4A#=HvUa*8J`+qzoi*n_vi?R*M^ylKbD9GCqXzKUc5|?gs|7U(L1S8d zn9EZ}Rus$6J6G({Bqo98@FHO!7J;pg*<|=!+-bPmRY1!RxFg=UOIk6rRuyy^7G!=; zBwV@mhCxzqmKRLMYL6>XTKJ%VihT=lc~i}Kwsed7o)RCqUgzm~wt0WSJO);EwPYQ!7~M{udXzpA%TApe-KwG;bCi&oc zBJ&}Lxfiv-N9wA-Gp`)28w(Lw%M#GJ)>)OS%x#gxp?EKKFl&sC?ib$qQ@)S(P{H7& zzZ2=7ac8{jOQdq;;^6Quoo=v0@t*Gfb^PN~Qb<>HwA4I0%l)|st=9=DHW~^TZ&G}R zi;;vLMeKJXkD?dtW;}z}NiWte7>pxAL>_Gosyc`45@vy6EcjWE@oII}&kx=DFVNa8;_=J}6A{$?9OA+@lGsDV zF@P0@9(U1QkbqW@b9}8g?&ph+`eINH;hR<~}d;NU>@p?Yd z^)$5V7`-fGIs!CVc{?}VgIx3c!2c51-OhF7(vmoFp9{SB2dGNzBK*WZ0{=EYS-yVR zu;ZVrxS%@Whbr@-za^6MNTk?7&G`8Iq#aJ@-% zWb7N;Ei+4k3J$`Wf;i_t#vMKqbMhVSKBt8Igu6>+uK)+R9eDNARW!y9a3oDQB60c8 z2EZ}N?`e)&SD@%|o28hfn#oPY(@RgtVsN$tAOA-8G}I;KjC3hU?U+R;eq9(RgjR7A^#yvlz)i)&CbVvj3`uI zRYu;@@!|35{_*MGesO*GZ>?+{U0q#YUfx~b-k)9F-rhZ)Ufk>+Uc9`#{6o$!Z|@$S zHg--o_D*UV+Wvt;f!C`$C(kc0JAcogpI@Gyp8m<--aoDHo(czD|GReY=;HeJ?(+Ih zH2C`9v0=*?ek7y?V!KR9d{f85&M{aISAu4~!VGoYV(yK(fa zZ|W!{pGU+REWCC?RiLSeVEyPcKdnP-r3hR_te)v+}hsL+}h3N zd4*d8Ao%y8Z(LLqzaV9guCaxpKLZ?eI-$K?^Zeq4d3Q8>R=WQLupH#HoRN@l0RJK6 ziDlnMB&4@UU}*_ekAGA z%S)#_qoFd^dv*q&bZ2`VBJqAE^PP-Tf!)Z#%M@=$za)}&Hot!wdti!riXj>+I-%|G z)qJk6v0!)RdwrKAqMp(!MQv7x+=!{(sC&owuQyY>RW)6muV?AN&QIRm*!{77>~ zl`X%LGy|H1cI#L6$ojaOp%FNX20f=xy5y9 zN1$mJgS?+=I8A&bKVZQua&1A3l$#9qB`Iz!XxU_Yl_jf4{;#~0lp4*6k{j&}re zcA*kZzeV*Pt-*a;s8smI85x<|LF?zV)p;aNW#krS4Vya;UmsasB^bL=K5rcc`uHt> z#nb){YnUZWkm{@$U4t86QXx-=?q3To`UR}6H104UIM`XE;$PO8Qx zH&46llIIQ8K7vr_+t<$K%Xj?K6!mB@c1Ik;xIG-W)HuHCh4u^$uTtC#SAr@K@hTg9 zBy0xlYE*QQilc!G{tT>7<{!UkHX9Uoe&^NhNU26|lEWP{KMihu4}p!Z2e>hqwyBJUsN1xM=0m&KIC&DX#y63 zCUwI+pTJnt{Mp}}(31>BGU_9D>8kJ(xx-P6xl!J4jLKORdlKqnOremGhV0XYt@jk@ zEf>?T)cZwY=SIuLDDdZq(4iOn(rXERzM606JF7opNYafbb}w_IXiFCoTq^7paqrZV zcR(U$f44@Iekis3BeJ3c&SgMtX21Hx@2udpFsI^jQ!i{72h32JIW4hs>kz<)J4x0d z@Zhj5@>adW;(GAJn0$h42Oi|*plLasLz*~GD%Y+wp%IyhmT5e zDxbqZfEKPek$*AKqk z3DqDy9c%iMP^D6KVZ+y+j_a)Q*xZ40zItp2BSIXxg}y1U-kP9Cv z7#0?SE21LY0KbYu=DU8*BU_K;JfSjOVUSSeQgFCTf)du|;rMkEUfUIoW!$9#IjmfG*g+;hZFAx37U@g)5 zcRgjq)3?{3`45({!NK8_LM#+{Tk&$L-(V#Nq(F6}kWfK^)6mXqevMjb=H5v*Uf8uu zRD3K$)?FCG-qy}R_z#d4Go|{QQ&vhZ+5P0*WvTevK896H(;wYc?mGfEs8m;J{G}gx zI`EHi?G{#pwDv@S-l)Cig{siSwu@#M$UvXI+6G0sP}4#%`q-KEsoj zBy2KO@&}gnZU+J6ux8PxXXR)L1(H0GziFsmf;TgLx!R@Cd1%m72sycacxskN#kvT5 z&5lbNb{Rd<)LvB>);hSSFY?Wj$?qF{gu-JRHf$dl-zg;e)=B>Ly*s<29uKk~2HZi0 zS2LGXjgQV31zzxZC~rx`yDRUcC0Dv?6{iruzz~WhFiR)l`!@t@{3N*gTqh@3rvBki zhFElXvt+7~z_N8=_2oA&lFQWu>Vf_CDG3bot^6?Q4CWfgaFT5t!LQS2-=)vax7u~P z;k&9|YNYB1H#R4n%7-0VNH)UW3sX*R^UZhVLTXMU%16^}S7hh(#L~#+cm(gcCSy^r zXNNzaIb$(5rj?l+OC)Qg$v(xeD;pF_1J9uEwMfuMTSb-c3HX$5j)(Sl&cc6#(p>|x zt1pMl7i%!^MwtGf4G&A5J@7`$U?fFB)NMb#O9vf=3r3qZK0Q1=a=TIMhPb^n?GOL@w14?Pz&ENi#!;{F zbT5e&u|8#6HZJ!j1xCN&#JV*TuA~wC({aMN=1@Nd(EY#;zB6B4-%r&y*G?@4&dCLr z3$w2IyTan(T}YA1{zNC4ctfvoF%*l=>zqBl-3o%;U80o`&D^7k9VyhNxd^N_mEw1p z)FIm*t7gIHdYUiGEL@|}%F+6rh{Y9}Lg&ojg0ECmv^F&FR%K%i=+(*}xlry{8*`kB zrAh@|Vh*iE*N?7s zdIa-4;SOo!1snMAvmR3Y9!hkTBM}m~5DH>3$5_NLKUcgtAeRRB08#HHhfl7r$+rY5 z4l^nVRnnwq+Yz^C%4lWXN@D7O67NWSwII+u$B&=2eEZr> zEB5cDjp-BSI-AfP-|dd{G0C+;ksm>{_xGeB&vlkk%GU#s`?2kMT@#eM`tSnL!P_^h z(tXM1dBZoi)d1;`{3AE?x`X)GBd{(%Q*`Zl^LqzFtl!AcJ}(9x?9YHP(}yrK3P69z zyn`eXe5AQIP6+~bQ2ra|kQgNwf|Y+|RfEE0oN?b@#X9PYDY4wxjKoOF(nZgIb+fZ~ zK#7e_~5xKzrdc#Fy}N=JqU zhilC9f&qrhJ9GCFV8x$r@{VW-C_+r(F2r8G=WY!wat?$smGi%^I6BemdVS6AAT}LJ zRxQNP$ld0UJwG*3WpV%bH;A(>lY+(45qx4Z0a|N?Q-cU%60G}=5e`e6_k>j0o>aOR z(!o=Lt(eISV)jgOitRPD658++-6oJ-Jk-yi(bv1i)v2|lbpvCDKuO4(5%Gj7hOm0a zZdR?0BBaQqRWBmWyo1UN+h)-H$)XzL;JTLZ>vWrByp2p(x<=T-w0Z#u1ARz}AcRXc z$S7~o=-A>P1jc9{~-MmZ)-$aaD}WFr9;_)D8<20y6F?@~67O9Os&)GMgQ#sf;PF>08r-xIsW93Hjw`V+5qZO*Wjl>rq$i81& z00cW@Svi6O0%Iw2B9rcH8qig?=d2CjK(x@G_}K%y-{Js`N(1VQpbxGJqFRP;;G7g; zCuh}IgBeU%w-E^9-3n_ zU31Qa0jilj$xU3#mGTi(xj~PHige5iA(by7RdXStEJ1@IlN|z65OPyOXrP#HrK8uY znxaXyKZu@5XDxOb)N(N#AnLBhoy0Sy52gK*ilaoh`8nI(8a-UCl8sqL8{~^&+74iD z3VWA~iTK_*!+p0?8DxI5im@G#fk53=9MY$b)x$YeSGV7)a>9m?v1InnWC!I2z!TKS zjD1cO=VAIHD@cnz!rn5#^2=0h@U%d2RG6rWBZwr4PakN9OD<7TeXus zn2dSh@g_fiepv`e9PwkP9d@#ZCX(<8_^ zY1k|*Bn|HKtXyM&-#2>_m=aIFGxWgyDKo5CO2JIj&@PKj)J6*t&>Hu3uJx(a2$o=z z8_4RM2A*o8b*NBpulHR~XMJ@`4)2LbFm_5i+8#$v9igT96(m%LrnoZ>K6BYcGX|y} zPodRMvL6;9GT=dZ?QznqX#og-dUzugxr2+St-_pggA9nl?BxS1z2<)4J!RpRXNn(@ z;clwz_tN@@#YP~gY2RW>y7EuO}hghIEO7+3ji>`&@z@d z9Ih@p(!L%k560NtR+MdA2g3F*XNIQR<`uYI-bKHiY)CMJDJ>bgp-fi*02#qggXFEw zPsDw0fkc2dPSR+Y^s?_Dh(0H=zOfUWSc&?JRBkNE>kZ$K?7_S7*}v|RP1V(H?wYlv z1oqKxTfOmL?hdFJcXG^hOtu~!+v{AVp({3D0i?_A?kM(6@W0DPHW@ba5jvbli~`=2 z$vSQxA@b82ZU)!*q)h2T&!p^6dFr^3Ki`SIdIE(r-}lU(x$)veP!&rDx)_*sB>BTJ zJ@r;%5f7YIKdcN>>^ID+N%1wlG_iVQsHS^Yx9i|Z$^33qZBr|^y(0k`qU~B;9Ej^( z(`ocOxUv?Folwzc4_Zd|_oJp0J%0r{pXPI`0DRQQZN}ZYg)FLVnz7NzJYXPgRLN*7K( zDaT`q+kQgWPET|phbDc^z?WA>?1R>43V+B_4c|mHYkN?u6U@YJ!7&7=flDpp@xY7| z!I0$Tqsj#f1-Wp&epT~BLik%I4Cl{JSwEe=AvK95F|)QsL;X`bjP8Z)oGlKdyorIA zXY_Jlm(f&4tdH7lf>pv?ml%@x)}6*hyWb}zTmBhO!MA(Pr$+U_qX4)xAb%$#ec-Mc zV^=3eHgTM$p@-1v8_ESy!?Ynj8V#7$j|zCn>Fp_z_cnL9$D2Vekd1@A$nDtC{6d== zKh#rCGHYow2tfpbS>1+z+$E($}*#DPqKm2zGj{#V{JSL~ka+dcNz+L>dGSw8;8 z3mKk!bqpQ&z=YLhR2Su$##sN&AjZIM#3{H;PxH|S4`LHa&3r5BOniRPs&vSteaOes zB|2snGQgO6(g*UXe|>0K7U0#g1N>J_456j)kVmP+5N}4IkpA2>AA3szwe+Di+LYQkg{N_+N4Nefm^STQiw^N+ty z8#hT7)F$~v+$4Ez>mBYXY;=TngS?QbgnCIv&zsv!m)`SX|VXj%zN0!xbP zDCE>nyzP$I8vJr1MlvQqZfK-i8|o65vYQF~FlOdvd_?<#3dt;pXf~JHpKcvY z8f+b2<)GE21;zdU%}u2QZby+^v9K?*6O zqIOa`#o_i?#V*uN-&=2DY?i^=oi%;_+(#q#t)sYDosYYP$o3WZ=u;|bWd;F+pRr8Y zkO{c-#BB9F{%%@9t}HFMG-~gVbvF=@FRrl0?<>hqqnO>ZkW&z^RmaGD1VGd@SSy0Q z#CNJA2ntEf1_=i=ao~*{XOfND>Iv8^v6CrP?8`jhF!FDaEq z1_lYtSsfk;^5c9n4Cs$KX)L>W-#{`cu!80(zZLu#Y6KsAPojGt0!0?|5Kv3jWQSEI zf#_8D2eE@w(<&u_K^6r<@bdWcj3IK)L<2X}#&{9>90&rd?PMl5sXQQJ zk+t8gh_yh=Ex@$DP^`R{-ds+As-1j|q<#Y&mG$)M& z&c`)-GG+~!)fm3MErNF~jCG}IqSezt1+(5Ns1ziO?^9997FKTHR<+#On3ynliZWTn zRBMGhI0omrQ!;M3G;^WGwCWYVCT&Y9qG&_N2{!*hsMbl>R)4V5C`=z`gOJ788J3lG z1>z($QA2WJwYsB77@3iYDWeSoQ4e_e;}tnQRe(XX>o+*QtI%#6jCef>zm4NZ;^UPvgQ-~1iFrwQ5sQ|n;#Zf$1F3Rfjje~su?6rVu z!g~0RYodG`EjFp$91pfjz*VGI$`^gIjXAyWaq}D;^7Onj#j9L+%TaQ0WZTF)%F8o1 zpK1lC8Vd4d+3LZX6_?B0S~9kL-zuwg@c{txjO)~|g8)_S-x`VpfJDW8uI@WM*KI+2hvyO+!TSfmjw0;E3dHd2d@dMmA z)AXlX1E;N&e|@^=1JUs?_&r#QLFmfd%WHY^L!P>PHHo&rFvxI{jMm5fBr1>OD*=ol z0>)#o*+d<6(?5IP3pZ%w1phoDo{}Z&E!&2QNXAPg1jbHaK*Cna&ohYeYvLRrR0MLH z<-fb$MBNvTUcHUJx6`N9GazP}g^O4WT~^(=f)ZYT;2anO=Lvp}mvUwm5-*`OG>i{DOi5^*9o z@+mmxq}JW{*7*Cet6Jg`{v1}RPfTTkFur$R9IfTR)kNb$Z9ND#;yeWmdD9M)ng>CH zOoRQK4dt2*szGNyA6JOxR9XpO`Q#p-isA?p<$kJ`qmb6lRcP=R6TN`x7wy`l4 z1Wy2yvpO&BL};k3O>4fgxNe}4zrRZ0c{`oPfzV0%J5immV>O#QN4Q+!Kq%U+cApn^ zO_y0Eoj3QEj}<>)WD$U>|6Dgu;oKb?NU}1@w4fTbs}*fd*{FrVg1-LuwUNCi zDIBupPZo;lxp#q$u+Xr?AU`RVO$ojA%zbg~cU{)RcCT(vV>X;b~)0`Mx-CX5@Hm)_e!ck{&5H zHj{ZXu*eS|SOJBE6MYlRs8w1o{PUYzJgCZs(OP(=4L~K%t%mStHW3r}2DA(lkL8^D zX~^=PaQ&C8*63i}EOttJ5THntgaV2AuXhxo^%k>n6}!J+*o721QSe;mogy7&ahF9{ zsy6ETn8VNBiIS0AG`S2Mb!qe#K$w;W;_a^v zs01-$eLyP&MBQ*F4!JV42jp#IV&(mIX+{J3A_0rn;R%ga#vc)_F=$F~#I{P)RC_33 zK^c6tc;u!LYX{!&Djd%)Hd>>MN*okY2QkTnoq!~f1e2v6Si!_?F@OnEM47G(KGe)B zZnUz`BaCZ#z=<$d<3Xkk1Hq8U_=>mSIA{2h-|)34n*<%290_{Q{LvTZx;5x?!4Kkh zh1^eHbs}>Zw8`mFTCpKojzh)zAttx@5O0h^wAMo1k6p6WA9kJ|A&*GF?DFY10r3(1 z6!dtp6dR9%{Eg9XvZl5I+cS?H5-r52z>k&oNM^|3Br>6(%40KKgbrD~dl18eH!v4KBjO$FBEk$q&2?;IxKeGVYj|+H7 zPbXiYB^JPxOx82@SWL&|I}bJ)r@-?PygmlINeQ^{*O1doxl~^_V}hMrf#6y@ z!;%DfQ1XOPO%IbBbIG60TXdAR%+l>to=6-=>{btE+3{3J0r8tYGlhYbb|yrNFYq9= z={@mxn;!^f_%F#0M;M<+o7g9KIL@FtL?k7*m${I~q)y%R&WSh*`ojc1tHrKs#~Evo za^%(>--{wr!j9Qyr?mk&bz4hg>+99k-0P#HPAcuF<3=4KFKdn%<!!-2({k(jwN7u7NQ@K2?7@b6Euc$({7#idL{zb^QrM|?9`a?c zTcWpuFNW~0n#-c=`{V`c8Mz$0^#k<^-1)~}w7{!R=Pf?*TXIi`?Q3LPxijix8DT;N^!+*w&J9^r9X zXzgKrDykVz_Yzjg%7n1*&a=KWTqq>=bdPfnyFF?cJU)VfNPBcr)YEux7kD43BYhl9 zf{DrgPY3l}NeWC3T15xi-fwY3+p!?ymxD$*-bC(*K*a&tQzlqxZuku*Ih8-1xL=~KICh45 zaQ#|g<6UDQ~{$yeKI zU&KG~2n3JLLj@VX;%r?AzFH_L6hVD@2w-AoPcgaE=4nwog4cilq{fWw0NNU#+mnC4 zy^0N0_2ktLmx^x87-Obk?emNPU37u!LnrcD8pFU<2Gb)`gkK6C!>o3@q%Rr3L*VPuM zlj#hU!P;s)mUd!otzrzy@JkhN7U{jHqhS*EW$mEP2D!GUky0xHd_%@T z{ku?dP0o9(W|kKZ>RjfTw6pJ2J5AyATV||EUhV-+AFUBTH|}?7Ad^Op2f~Cc!g>vh zf*g(*Ht(Lw96O0p%xhZtgvu%Gn?14%Ecbu$hVBfJcI5uqJ1N8fDd4P@+eHMJ?c`&L zvZXL?rOy&_&J9PNM}TFLW~pdUMkANMomJ4f^k0^R9HM8Xx;Tv&uxG!lCaLitjpQWI zil+al)^(<2T@7TY)YZM9V9j1^p`{1g#JG+a+_j>lktiq)GX+D{7?n$oBXMr+D@1vuu%OxU(blt6wd^yqc<9W>87Fyl$@&E=vi8*Wu4gq{ u6y}#>W^^`L)FH;|r=!UwQXKn2>vpP65?Dr2xxN literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/monthwidget_preview.png b/android/app/src/main/res/drawable-nodpi/monthwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..f386255dd040265a343764b831c3db9e11493e00 GIT binary patch literal 29056 zcmbTd1yCH(^Dc@6UED1MO_0TdOK=YmWMOex+=IIWw;;hKK#&Ayad)z~>*DSfTpz!C z-+fiD>fZmq-m983U303ZPtBY&r@!w0CQ?mB4jYpK69EAMTR~o09RUFe`k#i5{MtiZ zJo0*hSf!?{DWh!dd~tQVe|q`w_+$@udb78( zeR6a6cye*Ge7*bh^t66+ARK)CI^5pzzs=jDlZSuweMT3L*SmKo56@2z50BWyOmBO2 z2`JbQP{`5o=#c^MNV`F|cXyXpH)J&2d_(q!_vg%<60}T0vSXerYg@RTYGNZUtT6Mf z?L8SK-Kon}Zb1bE1blAaYi+BbFWTl}A=e_3>Mb4pdk07J@YSTv>htq|U427ilXJ%> zr-P$Ydd3d@gJVXH;R{z=Ll=t>Yxn6{xavfJ@oY@Qk75TG|A@8UKFdFg_d5E{X4+3D z$|~z>TKa1n+Ol^VlXI)AJ->flO#hxyT6Ng3Gac4&JXBiK`K!F8;CE#}%uh8_U)SK& ztfG3G`6S=KXjiY0@aW{|?`b*te^S!(6Mkd|hs9@P7b?fz?3_O9r{4bTez42Cs~LOj zhd&`80HW*f=XRcbO7Avqj)&(DNwJ)QU#F1DRb52`K@lswi|FUm6uV}JW4M*8?-m5| z)5YDjy=~VG+DQt0)x&U-2fMslA^1;2oLu&QL_nZHP>`0;^jtj3zSD!lpf_XgxGtk@ zr){U#t)B|na3sjX7^Ecu(BM#H_oFq>NRNo@HM=!&c_aor_e3xc&&xl(nM6`~3)Hd{ zw4GUx9gLJzQ0dkF!NEb|W$~w{qUYmx0ne2kFUQdwsNGTw>NJ?RzUO_b&(B5y&wo3f z1}dK~c3*BENTsld5@ltH%tNFxCDoZielQz7K7D$ABz@QUyjf`!f;dY%gc;6ZL``-Z zC5@?$oGM2K2sob$xU@2UxofEEl?@978S_4H5d7h27U3p%hJPA6p?!o0JjWtdO05qW zoqvUnqoA@U$#+?=ag*t)z_q20qF+SlYhNA!V5G`hZjO!8peo)1Sd26#Xc88`O+Iio zPWs|S^6uJ^KG7WeeAS#iX!eiF_WOU9AUsq_Y2bb9(3A6jOee(A^@W-CKeJYqT{&XYvkm9U}aIt3-||bH2Ipi@n8mFTU(dqGO#;D#1NuMhhN!JY+0kigI%`r^&#urOz&>vz5vGO45S>~Csb zx;SnzlZ?ch$<*gp?}U&FoU$7RQnB(0rG=Oy{mvZ&?*F1&yv^Ze0`NaHBZOaBGk)?r zB#o2K*RTIq5%9P_Hr(u6QYJMZJoMB#I~ZKB;)U%tH{n-Wy0|hlevX{L5EB1u@B6VUdYt3ZolshyVcL`UtBlBG{^lIz#O zO2Utmd>_6!^%P%=W+qFE%?{&h6xj1Mu@MwPTt5}JDQy$&^&aKOOxu1+nYHb*Yw|$= z1cj1a3#AMnz%e{O)U9h#a=*?AvMsI| zYxMg#v9z?Xdt$Uz?JbjaK2p?;Vj0eSnQ5Fvt_}^^2TexE3FZ+9SA8a4<<<}^tM4r{ z7!fA0Zz&5arjg`1?%sE2 zsP<(^jF2Al)S&`z9)UonDDZJlM&8e=zqFDU#5BoPxbhayfjBRdaTQ*tzjtzcXb9j9 z=eN@gv_AKgrd2Y>n>aPz>|LhtcQe>US+Q{(&zGT>Z~%sJx_VK$jqr%1#oQNr`ULwg zOhJ__Zl8@MPxOwRh4M-*Dv&)JY!62~n%hs>Z0H{%>487zGpDa?k7zJx_*TkNDOvEm zBLZdV!;1I@uVOe1~=glD{YuzyN>E<>)V{fz~ZZzv~tZth>|G3La}O78bJ zFyVm&eDe!b8)rXJWl^86wDua}%e(hyNY-7ur(uZfI3gYKr=NxDRoiKO`dM-H$(l%3 zsS}osQnzN&5-0E70N_6w>T_d-ssSrDLCEvq<`74|Gh&h^TPq9fPptDL6a$iTaXoS^ zI-rmFHupZ4rsq-!!h=kvPN*v+T@8y@GZ1b#GTL#>6xz0`iMab3zueM|)6OP3rCODJ z$=M<{Rkd$L7Qlz=4H&UY@5)qp=ck@JNkVxK;0CT(2K(e1Sx9|3{GQJ}k<(Xc`LKSzVD%bPpxVFA(#c{Bq5ZL#7R5|IZ&mynFaERh1tcZ1 zc#1ilN3)WVPKBYGaPzu>T2&gS+`yMLSwi?)ECFG&?EU0E8G-rn-}PVezF&5RPu>%p z>(_U67+RF+M6PLe#H~t0Ht*=46L$G~RJs44wZdmQd}g~bUrFq~N5@)IWSvR;NG|L$zTI*Bx)FjB9v5v= z7i!X?K4HhwKqnn(064k$%-kRIE2Nm4Tu9Q)GtUATzU3y{Yp52kVW;KTqN!ycGyE*e zYz|^K1)liBIc-FcX2w4sGnN0Vgl`wwUuY)?p*GQ&##<#{u9Rm(e|dMP?#9>1q)E=G zudx*CL!^dTKto#m40>n~LgSF3dW(YYrLW(1%|CXk^>`d)sni2tC$w$EQgx+vG#NuI zTZ7k0I<>Qw7S}&u{ZwSC^wFSHmp@g0Sz*E$S%t#rx)bY3|K#7i-!IWMKH^05=V*+_ zsDiG|B3;`ceVX=g3K! zUJ%Ci=ee1FCdU$bRA$?(>CFX*qNZ! z&~wB?&hZtyeQ7QCSE@JGg8PTEO|izIw(l#^6|%X;4`p#P0ZOJlC1Oau2fG!jH9T~Ek zJpPe2de}q6HXQ&BC(gM?x_C19RsvaZ@hl!&N`adLV~{;qBO|O3VP5ZZLo5p&fbW`z zIJ{(CnH9gMX_|wUwFcOTvcvZi_O`@oYIV+qa^7Q-WMN@77%`vYPg-sTc5yyZdk)<^ z*BYZdO%o3ACmW*Gjs!JNZMU@(cfLKu zz$fz!1d&hLy(1Q=vVptQc|iBriX=SL4{>6;>?46*SRzlaNfCKk-lIUe9<|q!ejIK) z{1R4|^D*k1$Ws6qcbHq$SQBDL(DiaK^ z3m0~9~G-pVP>Kp4!CzF^sV6?fk4kJ@2Ki;CqHfb+oSF7 zr;t&U6NJTDU2t!CV8Msu2p>}Q?6K@RQwgk2h{LX*BjTUzd-k|l0Za#Fy*Vp`7d~_* zx4(m<^C-#I-ei`vaK}PE@4#gBAv{#z!f(K`TT|c8C%%xH5-PA`8*V(W3Hd<{jPH&i z^4LTt@5+VV3+4y@buo}Ic%nEf=HZ+?CWpn~$iBzU&;qL82(#fge!*6}6l1J;_Cr3& zO}G5^;tFH&ihrXp!M&&?(8DYZ(#Y0TO1@ESNH!3$Hpd6kuRMFRdFVn&4Om;kW8AK1 zA5kFHt7KNK+aMBpgw@EQSu#}{;}(ZB@Yet&d@iTfrY@P6U;VIj(NJgCbS+t*hU76MyXQ@WFq(e>qNN*4S;dRQ=x1P1+h7Q z;U`_GxHyULbXdrci^%g}4VrN}G9Xwr!$B;6@B=s{70~}xAVGs>S+>73yIraM=udhq z?(Jae$c^}swRQ*RQzUMcQ=IJ*SG-fnB!z1!9AwL!7hzF0A2s9+ngvuLeOU5Y+71W+ zEU=sacf(xP*OX%vno9qM8@?hYNcB`k{g*@OKb+xc0jNf3|4N0pPQx>1-})t2eLLR7 z#8}k}iWhZT5_fzZad~2-Ms-6a(hgBOV`IchOs|66_rB1$o~TN1WY-K0zsAH1#P>{f|{Jf8thBM za{|6%ft8u$-qnHGHNS)kiR;K&ZX46+|COnEn=^wv#q&$OqM60`9Y?NSyV1r~SW}L& zf0uQ!%3|1<@51duu~V`B6VeUtJR;$nEaUiSO9bjp)3h$q*pGy%U_ofnfi!1Iu0N{cQuj zVJW$JgtF*osHPhH6^{-4X13<0@=He1=KJFAgFCwiC zuLY5%Yvpj@twd}#94m%>;5Z2!xh^YPr)eeQ;H65^1H-9SC|tsf~;==clzc@#;@2Lsh#^YkwW$cTra`f|Sy zwdOHLx-J`Sk%=_&9$fX(ZA}lP9-1m;hKv8TcjW%4xaM!JV|I49e;?a##0U*roFdr1@ z_gpV-4^j2qop&5{C(JWifP+uH86xip_Je63D0yDJ>vTy0AjB>sp3#5?V&B8S`WleI zx_M(C(9kGr=M|cnUc)z19B}g=&d4%gq%Ija3UqOUlJ38k5#V^dQxkBE6sxC=C&tmc)!q|5BDRJ3Bs8?5)|QP?*Vnd! zkJcmIFe|;=wXXJdBoS`bw1Ex2YTDR6DQO=VS=N0$_jrZ}qmKS@Q6!?suqwme){F%M zMV4J0%wy}`mFpO$>yg?ar1Gr>?(Ce6?WE8RGYpnBHUXPBR@$AF${i|Lf0ll`w5az6 zLDsePT1Q=2AIAtv$0~F-63W}xw|{*#d}|Ie0-wF85qoOdn3y=YpNyz0T#7SPA5mmY zCC`*F=I_;j7Z?MXm%N3_TZQ-=fRQ3%;_l{zZ^F@DDuB__d<{^7&A#%HipCG-cgKsC zgL07FK~b8P;X{#z?w7LarKO#CPY)++cZVqrw1w^?i-FT`%8VdgPvM5D<|^(MuMNR= z0#?U6FW=_gubGb8B%sydkqTWT{lyx(7H(}PT=bmBHYoQY9ji+dwgBDcW#L?tT;oU1 z`0wSz#OpH5z`FO6D@K2I6T8A%(>Lp>CsTJ`=JmI0Z*^RkbY_g)~M z1=F*d3_PY!C=RJ!Du689X#v~6h#KnEp4GSxQs3O(%^qc1c`{`ggabZOvvu1p6~cj)d~9D-15X2!(+#wO z(-cizhMT^tk8Q3uhO{waIj55ok6^y)P#EhM5y$Imf@7uk(ZlaICQN zij%Oj_7f%C{F|;fSbe{EuUoOJ5IB6nT2qBv?*fq0u_9spwC2x>$;$y*^CL)b?-<@nWZPw8BCkk_xhrU{w;B(+_B=<3rPPNjIcS z&;y_1t+cZJlEC`c0j%#-GSu*9;cbSdhf0Dh4~L9ZiRw{6q=W2srrTVyfoK;bgR6^K z|IEyL>{&B|+`WWbFeOqh;GZ43Fda!Bhs6b;6)jz&nGJ`Tj;ka(3zUO?w3L~8e_G;{ z#6YI4<#}}`iD;;AP>UKe@2EY3KuBdk5SXR!!j2e0y%VdveS~vqe&g(is?j}pf?o@X z+XrI;y|nU_uGHj-E`zAPS&#+K{4e8!TA~rQvGQJTkzNM~sf7WAbSEkXJ5Y&e1j$sX zjdyuR|Ly4CK?{Lzn^`fUh9>4^zOH^GC4kD^`mYm($%3@7ia_MxLKC-W7e~*`YWq9E-++BZs<`$jz|RF0G5!`48lJLTJ0aRc!(sw#u{r4cI*0Tx<3ufl$oIG z#FzPu7VBY4J^Nued~aq(Ic*8`L%7Mr(%)g-Q0%PeOf(Twb0DrP89X1nHStKz63^=;(>zDwc=>Gda(xbx3CoGoq4XxN#QaAow*5n z^mQ|is6Ajs@8>d0-uMKHuX7Zw!ZRJ}TS&N5WoMEaM0+Xp%^q5yUAAg(C{1)itU6*h z)CG_Q5`0%m8lAbu@*qpH$z5H?uXagN#_t~t%rZt9vo?mYEVTy@b#w|qN$xY36qrZZ znb@J~crcLmj>T%eg~>}B4d9nPbA7Oi`T221EtbAyKxXE@!K`Qrpu~yW)nN@xcO&5# z=PM#q#u+C0134PTZa*p+3U+^6ly5v~i9*AA&S)&yjN(s@TFGaBVl)xsX zW@B!fZCg68f!yt>R$)q5eKeRkN%^rM@F>$w6T!yO{w0hyurpp@GG~N+wLRJ zLYz@nqPm%qq7ZyXebrV37^;c+cDMJaQhEpmy^5M_6%1t+*1cC>3;GIwGrCF8TX)+m z9gx5_Rza7*O$Sf+A1Eao#g7KwJh$aalN1|8P5(0`DEK}s(XE(s-ncY-)%c*-H@tL_V+h<&xT0yTxUH|6Guuu!i zZ_W-ibNCdYmGxJE1@mFfz^h=b{)bJHjIxDfWxmX=G(mMB0ysw2sYg4jPyB`A5-(z? zA7r8Jf?QqKl$B(5(YKqieiieaBVTcsIRJ4zve%EQCx(LphjuFQ0B^PmzwH zXG@Gp$Y=C<3m>DPF~@Vprv_HNc>!OE0QYrlGM)Mi-Ax#Ks{tnZ!k&6Uu|qP?qw7{m z)C&AiFaBt)sIPH|ERA}qGz?Q|MH)b8Qa?MSIPv-WfVN3#{HYh1twl41t4XRG*_zQy zTxD!l*I+|>Yu4CN-TBYoJ_|CfRPLx$|7OkjWxtEwoukRwdv(4J&1;W8l0@HgXCzAI zSx(sBKuDwnQ3M_fn{8DN&zc1Y3^vI5tLw@OSA0+q2uC{8^`e@sOLbhk22FIac3{Sh z7%xyU!v{B!JGFwiu{zy>aLJ^d7LE7cm_Ak2W0b`ie5Q>aaQ+cT%yz@u*OFXP)`-zj zZX3+0*O0tXPW1xU+pPN+Db)%YV8u@T&G>GvRpD6CO>3;&m-RU9Wz}byQAY3_^P0Jd z(X79AgTHmbK)f{FH+Av7(#dFNf6;XY50x1@ zLov&C#sg)#x;X!j_Oy0f2`xYgKSpTvF=4Ov=rA0&^93hSZ@%F#T=c3)3J)WY4WmHQNbwD1v_#4iRah8R6SB2S>3E_27S6hb!^r>>S`n6Q;W>6p>~ov3`aRB;fg>X9STL zRvJjdCn0136b&7Ea#9gwjd_Ox6NTHrUpkVX(AO@d{A>q)wS5nw(VL+YwCL&~Mn<0; zC@cDlb~X^}hx0WQt^34G2?2as?Z8Q?gLgdi^^^t=(zRfBNSQp4H6L@kP?a>y>?LmT+UDaPpvzbc=#1gnIi!662jID;3yNKavO! zdc44*%%^^@LerDGA&81{UihseeVl*(3n%R4&{yLyIlNrIZYl2jH7jfz zTsXPdt^2T`CZV&i7H{8RA~996vCHXefJv9N4`B>!tQ@BB9>GKfN82AphS_Jh7t&;b zLubT5CCpND+1WauzUi~)&3rYnHL(vgfI!Ssz8}^Z?~q4_hsTB(4xp|_@?)dr7?Q^) zm+#w5svy*IN+ctpPtOl{vf1zLQ>P!lT`1?TN~r>wk}eDef2#ZS!4w$3&9Q67{ihhZ zv5b^%KDFaR5|bSIegQ-nU9EoE$(O~HB&~rOjtqyN=mjvrHAGJ{O}V( zr(CS8M#N2@2y6>jnE%G4B1dF7e9%x7hvnygp~lgjj-j7`Nm@ov-}U5wcO_%m^8fH| z5Uitk#@QAKL#n1^Nh=MMjQ>psWQ2rrXcB#FiCY(pAj5hy#YZ4^wOq1iJpt>V93ZM; z`0YEvb?swwzY_2E^8r&M<_N?Jb3;r>HW90Qhf>35&P>J?NJuzw(|e7f;}dg;osggY zzqkM`c>LJEl4BLR>8+rq1BR}GS^Jz!^VqHs+u9WW6e znpXMi6eh8AhNyZD`D_P6kms(Grr7>%!(ael370SlACOn(gmS`{-4nE`%ez3Oq zHw~wd%>IauEbIP?A47z)0b@UDUf{%}(Zh$c5`nnvVgc&qSiJ1l=xZX>4sXoO^G-pX zTxueo9uGTY@^;4~%`syiQW>kDq*JNMUSA~)yVvy+WFBDW-CoZsl)Tz5IP+s<(4lISt;JkC865lee~E(#4n0-&j07 z?vo{ao#xxvUZn-xR=O$=bt^SDcj=-jMNKlpt4JG_Z3-+QIf%Nwn(meTNVy!b_owQ1 zoH>WY7CqgX4Da22i~+1 zbtf?^nI4?$tNjz!0Y8yp(6O^RQZ{Etka-tSg%k%A9H?|p z#2!EIkX1Q(`Pq8ewo#pIrAjx<{@X}kdr!AblOj^OFRhmlM(2wFd|N ze6nrvg+z?F`g;^NRjR(=-z~jL-wU{*3|BW_W!_7mZpgF`^XQnIP(x@?eInTaCzG2_ zz|uDw9zfA|LpT1}_G8ZA8w#g=7i};=?Cz`64W||BZ`PlM;z8I4VyF(8XdsUj?l$e^ zzv_~K{1&%n@`;52eGfr3VfG2^IQbX{C+Yu99;xolf0IJ`|L-^dN6PAdAz`e8Bn6WEPg&dwiDM(Vmeb5n6u6$`=}-e{CyII>i=5x@vCDS zaW~zN`)#4i>(;X{f@X{f_J?!1PF3`cE?IS~;OqPWPeeJynuB6(ozUH-dZmP5U14nInax8U?f3&+d(y_H&c@34Uy>+dOUOgnn6z39QUR6oCF!7R7D?`5J zRwnk}kJ%uXMqwTl+96X);!c9$KUA+!X4AVF z2l+6+{PbRyB&@DBLGvwrx7=G4Hi462ypW?W(yEJb(LjNZ%%Ta1sWc$2x|r4GZ-ihM z*X1q?H7B0TaSg#+J>^5^ZMzv#?x#eZE~ot9KkD;2)Xe#4kiy|cV^fo9r8GIU#_nru zy&uV@B=FC&6f-hYG9U|bWR(Ua>fMBN*!^;JcVPbU(w9k@S<`TZVvrR@aD4JxdOxyq z20l?9UH8+4kE~E1-)bruYZ@D?Pr(OZtxlO;8cGM-8<0B}u(}O0 zG-!bXZRC-FvG|~%j1`0*jsBGvds8DEI+N)9{(;w~dRb_=2o7_A^8y-!aPYbK?<4G5 zm`f>iSQzmjO{%4E#%fU{WzWg%URYITBMgHqe~Wf#D{94QOoz|(BwFB&f;IhNT^lE4 zc~TU@SpQ|=>hXM0ZWZS3hV27G>S`(*UX7Byyz!Gux#@`-uQ|!NLlz}4PhnKR7G+;b z7FB!?8T<3OsBh#1f?^jQ4%A5&&~f4y!JqSZI(?&fDoY}1m?#b*2HWT@ROFT=PkKDU z=_CFs*hBQba5!OQWEdO^Lor!}Y-9zXdkzQKfM<4_{(Mh4!Spkc?q85*hL58&5`0epfKdWbUR%#WU|Z!EVnrdygs&>Y!YJp~%kqn8|%d$JBo(2Qqb6S3UT{ z^WBLqetBxD5FtLa5EH$nK;2nS7^)**o4(9DlWoOZfBIW?kq+`H8ff>9LRVv)%#~n4 zk!rNOtEa|K24sqip(7g~N(eCV8hHbv#W_uD*|2Za`Zn$^a#5|W5S5PiGC_sx&qP_` zMZ-ekI?2`fi@8%>vVBYmgR`37#FQiY29rkeM{s0O-zHS>90P0pTZoOUdwDkGvsGXP z15EC9K?GWC43sUPz7_Tj+fEMn*%!U*2e;4az~K)DP>KZ z$+mRfC2Z3hvLTonumT)@V43`G{&j*j!RlnZaYgzz%XXKnQ{5XQ4w0lchNO=a1x!s^ zxy4A;P7DS@xs?LSj89OqR$RaC+U?HrL!H<$yQCY9`w4JeQk~{%ql5GX5D}X*_R+7C zS`ye6QGaGA65r^s<3Qtmi23{8`?*sg>-c`8-Ntai%BY_>r9w`l2M4W%O!)s&XW#zS zQs1J^kuj%lVJ4gp|J;Sz`VF|Fuy^gx_ip*iSjMBXHw2p(0fJZ=MQ%Mi^xIVMgZq_I z_jIr;Pv&Z;V-E2BT+-g|x7N+Gml1i=9GL99>nUUOsoDaUs8Af@~=2J$xc8^az z#SRU|OQ#H?h5$87mLldevUO{N8{=gcBQ@OtAcX9^HR%Jx4NTT!#x=Z+=&0PMz<@Hh zJ2HadgY10qSI)=CP-?6+ol0-=a(o{R13~awaH|7N6&$Fp!TlmtRgTY6{)$m z-mgz1FgSy?+DZZE5|2Iq*4%Ew?ssJ>E2>x5b#*r~T*kP;Xpg1N4P9Ma+abCGsDD3f zz=C9(Mr{+Ya!NutFDiQPg~bh?*H}&pdIbiiuHWeYzm}W++v3xI>E8d#O4fgCI(Ta{ zGcg|>VwBiySWhIqUv7YI2fBl_h0eCMESQ56`Y5t0CK;4%%ne2c4l<;&(B`@iP zJ$d#MXmGTGRQ_<@-G*%$X-NNI?>V=q$+j0D`a^^bfHLTK1IPGL^Q%*OrwulGbf#^7x)tVFr}P9<39lT1 zK2BSdYFd$)D$DN^%+@UcA#l+kbHIqQilEEBC>`LpVs)&m{=t1=cfHnTTi|9=uB)KO z-VtXX8~-n%iX+dIf#)D|kQaPqkD@IaZY{?eA;23EAf{0ht(G>tV(YG_!f@z=rn7lu zDl%bFn?8%Bp+u!4d4TgGxB7wQktVnEEa6v1NsR`9hCpTM!F0Y(*07?mT613Epe0)y zKZG&Gk&jAB)dQOW^OYj!&$Me042$>epB#gc1nLQ!31GO}%9uVl_ELYyZyP zTXr0hGfmYNdlW=n+03HbF6>jb!{7#=6=h{z|HH*XdG(vI`-i56ySm56IvAW*|Ki8@ zz9)Wc*PzCXk5u%D9@tvTTpC{@v5(05^8=sjGdZgl3?Sz9<-(Mh8hmHcA-;rqJUIBGujsBeXTD-C(VCf|ZdG`CiY^`nSCMBjyvcQFQ1J-e z5R)JVr8#BscYQ+gLV`mF=|{indBe#+<|LnS7e=|vW8{Y&rDfw1X`MZlnx8xKb z``?n6^{|xeiUHur8T;~mbP2uy%|NhhYbX%Fdr5C4!v3;i{KSNYV*+^fnK;$1_!Cs2 zytrLIh;(6!P}V@UCbrHbzmA0PdXEH@)UpI+9+^a<`eNbdzv$-tW&>p#bfs0;^oidl ziAj_)eU#6=RM2X_`{-z=6Z5tW=*;kOkN4zRPmXgd1_bM4*6RSkxBP|gz;tD0{K!eK zBMg^#_C-bEcgM7c0tw=A8Z1>zH!{)*K_rhTBkVjf!<-Wm^(irCd6-yEDmu!IzrG!P z;f|%}69;{3c%;j;FK8TNsiu;1UzL}3)-&OCB`R1;K*@B)dVODqkwL9Aki5&dWE4vt z95is01u^ZpTm+$8eCRXY+$s-a%Fv*s5apW}F$t24l9aN@JpyjN2gAQy^6#O;P==pA>a!bB8RiGpyjaa#H=TGC)Jvay?MNBPV7#(z{ z*ZVqroKduJj;*A&Zvd0kqOVEd!)j5-BVHDHjijUEGS1%}2soH~{mO(}p_1~_PH|8h z9@RrdI8E}9(?~Uam{7}D1QdnQS>&$Al~euEAnE!&tcLQ24?b@_`>SrgAJc2)M$1cPIc={O@IK#Tn0R*8j%?U z9!kPg?0Ra-sTOKv*h6Um_LdB$L9}`ev6}C-9gDEDdN5^3Y${5;%XKJz7kw^f4_Qcp zN&a*h?t2A10es3&n|})sYcv#IopQ7+O=D<z3`> zYu8`V6Tvk1f`HuBjVwuAO`diz+32A^oLw9 zZ)M3aErL57%NjF22Nr zHhQFulFQXncuBxQ#J9kRiFcW~#iZ%@`qH-YWr{?@eU#Hk8Uw z85kk&DOYbz_T?Nl2+W%G=yZh~YaiPLB=i|68V)L=gF0~X4kO}*`sut&GI0OKeSDI4(oaF#yL6@f zS=sL^vrA8(urAFmMj_Ap9uGrcg zSus^J9Lr4g!A~e!Mnrq0#ui<29P9u@Bdvzb^9TpfMR&0NJ2*aap~Zm$j~21IA8gNk z5s6fPk?%@gr2JsNMrhDKewk1GLwxM-hwk{#5h?e0K1pJf zoO~_AnTyD2THxp}=Vv(A&JIYdrZwnC+NTD68)EO4&%S0DVh6u!bZ_krA4D$pA$r%<(cL&CkE)U zjUf1Qc$D}1;inx>=YJhLUe*uspV_}C3H^jA($n%?T-|&MxOKW263%V$_~iSK-jp>q zK3rTV92Oke_V~n)ewb?H?{#xxa7)U6;(ybFPOdw7C&kB5op9Q*-k39l zN(&gXmNP!}R|JwR33~)QM}D~tj2BgXHS>fIJlc(rvFd1bx$%M4gd7Prf zUAw9nlg0=c!+;Vz+_wmKAe9|XJ_|?=6F_&7!I9WceAO^uHlO6RJ`E*i^-om^Am@YV zwlp4evWXMAniHjSW))ye?|Oi~u&PIY6waYq+~OhT``JAuUKCBE(ow}4614Dhe;6Oy z5#W1R>wq|i3$egh;K`H$Cv0yz4){kr-S<={vvV}9{eyd)`tI7wNT?Hm@B2P?Bz|I3 z0ZAv1Wf~kP4%#Uw?+*z`LE@cL9%0|3(MwKi1HTHlwPUF>1qzdJLEIXw7Dn1v&b&#A(+}i`2B6Z9i0- zzJJd&P_8*jlwYRhU0+@mvHzxCns?S3o3Y(jBVHFk2cfIJrHr()MozS85!Y#{3$&-Rmp-VOF*67%(i$<4{`ic7fdlgPRpZ zr8Pq*3`RVuW?)9c0E(WsBbRrV;MdVTa2Q$?+FQBLL|QI&mq`~C#yGaHzy6@pm88oI zO=07(*Ce=3i17M0jOi@Lo11MmPSX@}TuW4)X<6d)x*}%e_jHA*8{F)_ovJWX7hy}q zUVA#lJI@=+ARr&i%1OSET(md~Z`lV--GLkrF~<9DG(`!%e^3_9>1&^Lb^SoCTIwyd z^DrG4S)Oh|NBw=z;#VKAHJy8;kuLYrN?$u~;jMwFv9HdQ@=WsVWpr6DCjPR@->D3T zmPe^wl{AIVgh6G!*SssEVL5>kbfn*FeKJcdk>p55CG=Gxoq*r(FXf4ZKIfT!aMP=M z4OtG9+dGR)$KCddD<`h~R~Mj z5*I2e-tI7xl&1H~QZ68C@MyFrg#OgNgwcKU;i`a}m)RNA+?0~DcbL67`#CYg3o7yi}Ea(Sd$ZM`d*$}m@|v+ zwW5gKOc^3Dm;sKA#C*@Ee1tZB0uKW@&XFt2EiQ#i0jL%R{|X!#1aC%vpd2=R@Zm~l zyN7ILMe>%mH$#+uZaSv_=`^n!G?*JHT74GyR6c~GPO}iMMj4EYnVls zZ|6yc6=%oUG^Im=%*WKFJ~Ej)bdEQLKhaXLG1GMiWKZ&xfb6tLEl7@&#($#dk)dP% zbwTi>d-&|3Nx}i@MXA6SV?DG}5(ybs$V~QidyHCHLv(PE*8ygiMe3?J?Q%cFtj6XQ z@e^^u+%wv8t+i^Qbr2hl{DsKnvc^I{)v^T=@|Qnb?iENb2e)C}Aqp1QZIh9OOjSwG zK*q*sJMN*Pyau0W3bFXmLrXC#qbz%JEmvn%hXyHBQB(_qp22JEG`E&-GDS_EY2zAP zv_aqoF89kfVSW=SJ(ntYP=^|`aUgizCo)r3z3%g}vb0Qcg)tV2oJy9s(17$}kY{#o z%OB(Y&!6odjMlQ;(wD?^z`v!jduS8UA*cTDzf&Tiyz_U4*LAmLCPK}cRfzC%(~BzR zy1RkIx0NM}Th949-JKGk++-9|JIWTsj~zsF^k_4tMMb9b+VRA1is>EB*TPL?Nz4kp zM>tn_WYl`33GL~_g-eZll@9cDv4*3t*`}P8q`_cPdFsjg3c`D#_E@Z!C1GSI$;p-B z&~*v|`8p59;N0LKc$W?-`#66mwW8!(WU@vrw+Z`e^`g;{xwt$w%zY=d&9_N^n|0Vt z-yiI;glc#T0ShjwGEkrLmWR1n;Rg%Ft!?xGsnSJab;8}DuaW^FCN>4OGBJ)X^VvCj zJ*lC|_nH^CrrOr;B$rGlu^=E-hNg}%7woL5CJ-p44-Y==w3f2?k0OF7=(r@3GerC- zXMi~vU6G-w2N)wILDWuD;A$~*S+T}1eVix?#Jq=X>1vKFtIS=$c=E(Lq^3u0EiuuNz1UbF6JK*)z#f4`2yj_-C7mO-=Vqw>Q%LLkrXGAR)fJXE z!=`kR|4slmIyypTU~Qdp35ynbk3XIDSLD(Ukq!Av;oCNV|K4KUgj81>U9=Kc;~I0M zM2Z`w8jWj&5ymwDF17(RJW!PK82k%YOzBS8s)*3RXYEUJ7W?)$nOay&Lhy{vZSSR( zRbKtLz+AoaB?3U!;tZY=xx4^$a-D6|E=XU*gOTogDjYoS5F4{$H*9x!b68hlGgrb! zFzObDpxrKU@2+gs`qQXJPDJm8gp*odr_x1NR<>&NF~PIXK%Fz!xSwR{jLI|8`(YMo zYcslZMI2Q+v_3<0oO{`Dlm%NfIm+LnW)`hm$Z{iPLEPFtJxEy~Qj zgWc==m+}*AZ&AyMv!Ehcl7;2nbQDQx~4t*E3-C6(SymZ)F1VnK1xA-)bl9U z-e@HNopO##0w+neVnkqU5KcW<>L-BKH7|^IbwA$#%0_!0{dW<&!LB3t%67*vZAsO+!+$L)0gSBa=R1GoMe}>Nw$cl!V&<>PM_soj(Oh$U&2^@!qxvPDpd{3bvavl*> z$TWNt0+0DvV_0KpuHPB`@gJx$eQ$tSY3^rU>cP*+bZ}%?@6d>s!}vKWQ_Dq!IJixw8c1 z!#JlE+V_@a~s_m?T;)uR5Pap(`-~@*t!8N$M1lPd^ z84`lKOK=SX!9#!m!DV1@w*Z5?Ge}@?cW3#p)Yk69ZtcTX^+VsfUESy5c2}Q!e&2T# zvR4uUAPYvSg?4bCpV4@1OQT_^Y@+=CzS?Z=<1sfJ*B*grFZsu?`b9kq=xlHSksp`1 zzr!k@FO7bE_EHg?qylwuC8N^XHDEQ2ko)p2w4qPKzup`3 zs=mN1%VSkxTAY=4y_liH$d~;4u zgs>tkdv6PR$ncIJpMvu7X!z-3eW<1A{Z$KHGb1UTA9ecUda}S3MY91jou9g+*z;qS z4&3yLnf~QJo*8_d(B5`#-2O`JA=YtP8J0~7lGWb1=L*UKK&!(JntYE)<#Yo(mz*C5 ztvbAoNTUQ76*o;x5C?)yBW@0^z3JxR8rkNt88wn|2?kQI z?1sptjmNn5$8XmMM?&Z)1d}b2{*rQdrc7CqAi9qvjtuu3f;7u^ZyP=2%{uNqqk>-4 zA!8I!$Gkz9#_AKc*UoE`j<1X9zPDQ{B% zW}A}>DJ%$fP2T$*tZTaGDUl)r2w0FnvVu~M>Jp`Wg2j{)0>|**XtRU)Bc}4FlcXOWIm~%i@L2o1pa+q z`Y6&V_tCF|V2~7=n{ej;+27LgVbQ9qs84hs46)ed8*@Uhp?5y)5qwZkUackQma#uW zT^$)=rn6{CT$n03jW*ucfNX`ofC1#UBxfP!fRldxpd{I`LKnd~YTQ`6ps%o{kp|g+ zhwSVQPs2;kJ=bj{M`#OglV~(dHhy|D{sPcm1smVp{Iv=;kXZIe3o-uMz)O*7d$^TY zY=vH(HrEG_$Y|m!9#6Q(CK8;h%uIF6f_ zp1t1Z6}4w6V!{tE1rFL1+IVJ!`S{Z0-iu03Rj=#NNy9uP6DVmqoI~Nqogbf6h@cKb zIFD;hw4jz_wX;`T^Q~QG?w)*4+AznP@Ao1t?+E;~!5ML8G%TAa3vYEki@?-}W5q=> z;O#TQG<<@uKQ4exVNbBcfZ4jtOv}B%(6)Ud-~B||bHV9=95>Ph11gY{IBD3y96T)! z(+otu+YIJ5A-EJZUdS{EsfFTkrl$mmO&lW7zF7AU3}$O(@$sE%;(tH5M9Z3_iN+#x z|3s|L8Q(H|{g*Oy*bE%&$tx|H;|+u5c?eq%b;`{Ry=rxAXI z{o3Qqs&Eg{xKrXk=hMFw(A@GBjj7KG(^#!>f*R_=%Eg*@Adh`iUv!pbhQp_r5smX{ z`SHvV2|sk!`uQbUzqz}1#x@@mS&8{In70Atea+B1DgV7f3UB52%jRhtN$B{vtdrpQ zAxNvGNt|z8KfqU8SCDLdY;&DX&8d{Y2xz9<>Br}kOa|=s$%FCN-0e;->2nhs$m*K4>4HOw89o-R4 zSB{_7phWl7kja~~o`wqdPwDv*YlqL4yx-vdG@ZS}Rc24`fyVcRU$BnG(ojDu0#{nD zK@thAWFOp~2J-#5`sF;)Y-(uQAFq}wRXt?c-6_2ISOogl?rOHd*^+H^^+&*<=fj37 zF)ok2z{`&ZTkSBoQqGaWhnh(Z%{#ode)@;P-rDQ~f=^6o{`Sl=e9_G{xgqmv`<~sd zNC2D3q81F)7K+n?$iR+IoWkS&fF{XXs37wE_vsveK*pK1GJ^u?WWxWlN%j9Mjl0AE z0Q5I2*+dzaNfg`5e|<0ot$)&vCq~PR84hVd5=r=B4frgbN?k>~-*(kT&(`F{X>&X; zE~8bkWBRyiD-hlB)>9R!AvG(>)&I*YF@MIY{^r-UC=K)Hci%dx6T^?wV!~a^;hu5w zLo+r?ae!bMD8iO!)mF6ctk+hYz1X?&(WBgC1dzptQjn&ar-2{zR#u|8#X#xSke>t> z)3Avs);fhYHzkj!3<4W0uT84@D5}kJmZQR+C$d(MOoyN6E5n5X!Dx_3g5i=4UfkE zRJZ@~Q~W?r+mM>d;wSpT4|25BG4z$0nbJ!122^*??sYd)B0 zm9<`k&%{v@RWBfB!fk01Q_@o=-;Z$X@M4G)SYfg)erD}A6t}ju?r``}Z6gOZASPL9 zxJ*XwZ^&Od?kTUW_<0;a;gVs^Uqh0@!0w15`j?u^)Xe?jr&LgVdwp|Nb89n6R7yBu zJ4xspRxYk`mQS^kNyet8tjgN$Rm}-lHcub8IM}(8?{~;P*lVHgm%=dZ7PsiIY9g{> zJn|M3VGAd!DZ}x@O1LNl_8NLvaR^E`uttWhZ+C}QBHKV?Z;>mDIO>rui>0HLR0tKB zIzFyiLOn@pTAnIa7i}g0F{@`8J~NZP6KTL=H;-3UV8rzoOmI0e$RkGUxT0B1tL%wK%n2O!IsQ%VR=RGf#_)K+D7w7tIpHho!0l ze1UBp!a1&Mx2NtnYYaKwq4C-9l;s&bv}CSViE>vrJAla2FNn})K5o$?a~(}~)l)}e zc*$ynn}R5O<$qaySpQy+0)1I0P`@Lqp?_4Nvm)|Q92J#p(!*`8WkZa!UqGVNGCCf6 zC(QXJ{;V~{rD_)2N)|1?zO;WU*hgd8^_2@BWD%TnN^75YcF=XjG#S$o1>N+ou&@^@ zo#EZShBvs&Z!oKO&SVl%PnHo6BFRfmY(vX_m$L2xl^gC@h49k0n(FF`cP-r{Kw8{& zsi;n5XF{I2`%jD(-3%BuwKBO)+dJR673*JCivD#7?Y=-loI*WWSfaH)KluwG4oarA z1sWPA-GlAR)@ij9JjSjoE18qXQuV?z68udd-%y51$x;#~Qbn7Q*kT#?N=syqLogWr z;&4(!%}&=Z!x@F&?o;{IG*?v_rG|L7D!O|J4+YYIy7s8Zji$0p1PB!v2wbM!6~x0t8N4(SdH0K6 z`NMJW3;m;e-ibpbu0JI+I}vrLbXT#_zz{pOXAr;8Sh)_g zNRWi?Y*Qan)oDL6n{+iQ#p<(30f-_|avZjg?6pV;5WZ~eAB+^Xw|l$? z&`dYIiw-FSz}X72Q9oD#mwWhPj3^6lC8g$%lQ%I|^$RykCE>byz2oD#$e4;C>GBkp0(v3*FcmdP{CYrhdv!Rg#3pb4^0_v9jCFzAjv3J)(9EyC>_#GMf* zrmJC&d^ZuGDnS{wU-D|!H^G+u1vYCmFH_8WI=9?QD0QVbHnCjz`7ZiagK)@d3pc3F zPK4)E?0TP`yV0iv*qI(gMw5^W#sh1FV0N+3^Kv;A=d`nbuKF3x3aWHVA)=j;Uf}I)+LMm5unaJuKr?s+!Heshpr}pJw5oj zL4WSe8Hx|*dIsA}IKtLjscX8=7AeiY$*L$EA4Pr-$TZiUD}tesC?26a@0+Ksy#KlP z`VYmKmD=vrbgk7UsXeT z-k5_^Nl47&Ut?^lu6Ksm1jG+@)0UQ&uB)K7VCvjG*5>Xj+;u)n50-zf#T3N;)boZ* z|GOXw+Fv<;fcJY`kB@|CK3k?r8|lrCf^ulWp|YMhKl+zmRH^lPCjGsT3MjG3e8aIS zhJJ}I`H3#(xn_1snJ4F~i(NGjOqvgXhtiH##^~m5l9=FT5?70372{$aGoOqUhIP94 zjFg|+cN1n9g}*hHYsV;5BT!oVW}(Te*RvEAQyc@^C*9XT^pu<%Hq>{Ut(rd!*Ssvu z13EsK?CD<{ce|>A(>R4v-Jq4PUdM8Z)o(XQ@4Qib5hiWY|Njvt{a-Pu|6|GSe@<6av+K}< zLkyYJ=`T3I%kTq^|2Q1{_rm4>utY##IFDlo24C@`Q-lmL={}vnJa10V-l_AB=bE4J z0e89`t1j2K+h-;=X9gy^W<@S$$~wlszln^D3~Ku|wu70hR{T(%NJZH`y|q0#uP3x& z(A>f6eb8pX@NaK^;O8sPB+j5YNQmR%1(fhnE`A73q-u4ycDG;I_~Pt-aoMH_&R#vb8(rHu8S(=kiD!7NQnWl$ z)|SD1XL(38Yjy;&)S*uaRE6O?bf(%OrI-hTilo)U=0g=&H z)zBO$-uEM#s>g&ok4E_B_Gt*WW1)g=UoiC^yZwv_CmIiUDh zKfy1idr0w$3W0EHDr&zgbKgC9tbxuH7;HSwhS%2*1moapv}ZP6A$8oifA(*-?gziS z9HSL|5(ec$mVc!;-mZ0B-2AQEw(7CMWB#1|$rtzBWglKAj1C+9LqaS(|x`bo>LPH*E0nJs4(_<+M^b&%ct=lz34n^#B-bL3wPriUEFp8i(d)6 zS)14=txTb6$25`1_|?Jt;Ec(RD41AR%|{xYo}Q|{t-i-Aq%k9JdoVfy163V%IpM5;AM7IC8^Ine3ekEmT>2e7$W7+J;Bc}D z@O@aUDeLLUPr_qz6whh>2Uu92m{>0wa|DvhKCKPq=bw*dn~{4206zpBNHjprSR5Hp z1o}APL7sl@$?0u7@m(pma}8Xk0SEM=LU)#T#5tnkKeK&>eGvm?Ue}7-KsCv~O$%br z2Mg0g-mgRRHGpaz>Bli_C+ps}FOjZP*zpl^Rhl%PVN#*G2YmVbxsV3OFOb`DITV9R zk)#{<*l|?smmH{qRM1I626%HjlHrwo?d(Q*a49B4acw5YuZ(>(LcyGPT*+VQzJfgB z-qt5uHzP|1OgX^Jbh4cEbZ}m9F(HuSiG4S^<(M`A2v7?$;^WGxJ z8N50IL=6=d5XPW;(~4>HulKS@hmdfbqmTJWdNrfn<9w^ar7_Q8Yhfp4N$2E9=<1ym zNn;xxterZ?n_F0t&*hvv@MWJyKyCO5NxE{K1e#9`T@DJ6@I>HC#hi1*vpcr^@;{q>14^&wp<=_RXiwB-#W5@lXuF3UvP5xbJT_YcfvJ&Xe%^`;WWcm_J}v9MpXo z>+g4Jr#|;z=<1i++?*deJvFGP>fAYh$6t(h1Xn1!5&6_%W=7NYXnWW7SY~BF6R#om z4h6)V4Ww$p?eOLHq@avWs~Ub~cKIbC00$Tg5KCHU0G(OQK{BS z-h#ZgVyEZqe6Y~+^@6c5C)A5OW{RtLdVce=tfygn^*dM=f(!y`jDlA>1SC8zG03k} z&#lHJVh^S!ZrRpahg&J%^WCEai~)~5ds^WE%7gmqP}ws`LIuv^B7Q#@ls`L3JtvGL8$u%DD*^+muO*{f5?9aOU} zl28z+4)^63xb^?Hl~iJk=cv&h28YW87x*vMy~^hodNapV3HP6cLg%)-YO=fogZv-X zQ&PWYwPFsd;YtR0;Vdun-~rH$*0Ajg!w=N3kwa6m+Mjk7Ino=~Lmbc+2Ie+6p@PR& zLjO)H?5-;bph1)%m)!jsow&ZUsNhU@=jWLV(KsCJ^Qf+&k`hHS9wfhqqbm#$&3B-m zm)pa@=1xuK240a<8&W?}-0FLcb1sO7z)?Pz==s{>-_Uh74v z3f;15z7@pz?j>YyEWcCtzys8uD^{?)lq@+mKkS9*)D%>ZONfI065Oxj%na;`!h6UScHc)cHAk)?j)?4F(L8-^b+{qpw$MJ{bKA&#C zk0=G<_|u1qSF`p5zPd5Qy(!+%u?K(k5;|xtGVScIZ2A?cr^<~qx1tZZ4V|G1_mgV` zk`v_!K;e{|zf?#|X{a8wPtM`R3=*E^7cQH=WO>`|>}+~OAJi3ZN*l{5(uyv;{R=fM zkr6CCpLv3X1W*grGRZK3FoPWK{-jWc(qIFi$gSm-`si!j8e@Cx%v@>P9@aBkv}q+E z1a5#{=q_6@1NT?M?w{<{ncr#!T2=a>z?6*D4@c@&5(*tiC5VD8qapqDY%Pd`8Y(Ap z56fM9FEV?1IxZM6WkBrXW$^fI3dL=iem@a7dp6k4#9aQys(Wuy0(XuY1!9tN!ll%4 zfEUhgjY(U=b{=A`L}pnTOiN0x*V^#M-7vi+IMTlIt51F8!6Z{b^o%i)(I3a{KSCUs zdR>UpL;iollW(vGH$zfHkN}%QrEY4dpGA!{W6j0QmF`M>kgemMZc#gHWrzCDuyQxd z_>XT?Za6oAoBw0$)56l2U)D%arCAP58+?=BAY2={5<_eN^tF zv`2*$zG_OXcM#d82M{ml+Sp8M_LBVO)gs(ebTp*C_bYoZzj0pda3A)4pq0JXPHjVL zwGg(d;`|<^IhN}Cdv5(Ywv@8z^z%PXiR>?5)uK)sRTEB_SMDuztpuReekrdlJQ~0MH3=g2)zB)G zGj#Y8xj~{+%6`d6%1i#oTBkiLGMQ$-&>8Xb;PLS~xdm|z-1D<3E2aFvwccl{0m0K) zYpw|AdjGpP`?bDA|Jpvru5U1?*a&cL#>m1rU_*c{qDdlqdG0FXk`ucwo&$9IX z@Q4DgRR;+P%d*6OH~(A6OEc0R^wKp=WDVi8#xZ8mP+^z!F?6|4Lj9p^UEs{3!Qd@& zx%)2|md$>`ZmAQKT$Fg;`!f9DKr263lek3NA|x&>EE?>1 z&1`hlpqPf_BK?{(Pk9zMpU4U#mX|4Q2Izl|KT4qg77$EgMW^-nsknaM*TfgM;j9rH z-7>Nw7ZK@i2CtDJ8KVScrs-n5s^jzWQWo+C4D!>1;-*#mqIDO7NR|GxMND%w) z@in))17xF4Q3_RA-r>!y?)aBb_LEI9qqLbd<)4L#_2)3VX`kqm*=Bm2x>EjigF=#m zi7aZo*qG6HK9f5vHZn2odMkY(nR|Ibet}C6mu@mty;$P-=GQpqMJb#7gvlr5nzc`D zBXkIh@u8S}a9nw}i8Zm86`V`00xkJ+FUG<8)ifTL$k_Rr(M<7?0a1PmPTF2z?pA7~ z#Fal(sgal5#^9D{;1DE!T3s=?PKA89oY2dy5@6tfZwRhW_S z%Ns~Lb=RzhL$0DXN9raYCr00L-W7EaF$wRR{^Ep${YywpO@h5)o_4E+@A7n6#GPR(-ahX2A)q?1bR8Yr#nX zh}iKb=Oc&dlw6bg6zJ#ZX^sd}>3&?MyYL$*)m83pX5#CD2-8^0ymHc}A1Oi^X@L7E(Nt?^rH4*r!EYDS58@_A)frnqn+4Rg1WkRbZ>>k+u~-HkW3HkmS<&R9 zY*X^j9&{3Viq=Jp`am)13^$@MeSXuq^!bKvXZuh6uBz5dbKJ}xGM-N8(FX=|P%RUK zJ3~(ZqHP6BD}B z#*sW0Uuv#2e4SXP*+nKAOe1X;lh(MwLZw=l2zi~*j~vu0MDDs1k(Wb-?i-9}QIUBF zDS-lY7hbB!7_7FLmSRKOZYje(R*ipOU*Iced%dX{PUH2p2>ODWx3Eh6`9D#AuJ5bu z0eUX)C=$a59aOcxWYltqz2-JnRZ!Ly?X4@({np{ zykWmO6gv-C#kDiA)^c2{qv3p4Tb$x?@yeFJ%ns!6g=JP-D3h`Sf6g{FO2X4HN?HB; z{C9SmR1{B(H|;g0XRLHR( zC!Uw23MXlp3p2(yu;;QFC#U2E(k&zKNj`rh`nBDwOC~Vbz*f7TQ1?9oWuXuHFP6M+ z=es{THY*1NyPg=k789&ya>J2NAvPbYqY-Qm?p|Lr1>>~5MkdbO&cjon1Ijcw)*8un zH0OF~p;D@FN@4gp?ZBX&pQ=a4BCuP~>0MIv;a$A?zv_y~s=IG34Zla5vX3x+4%Jej zp3e&s0x(T8MmRvrw?w9jQyn)K6b$(JeMVI&QxC&=WnNQ+o&$-E$#iUQ;%$mptH(=80t&#xjFihC;!sS$GW#+J8;n% z*SV|BM=OZU)-r>7iqFN%zZf3*;eQ3xDCFo~>=KAa*!CfPBrmN@;=oc}s40(0sQcNp z%W2`{>}!W1+f>EzMNWU6r@EX=+NBFdtZDYQaXE81jAJQ@!)^fwdq3pU!S1-Bm9b}f ze@a=2`)QTv=*Z%4Afvn66okGUZaZadUcU)a+Ds==)1F{}XnZUGol5LXR>gDm=X+ZJ zI7X{N@8m-u%qSy+f$74oWFvmSP@}4nE`O|Wpi{lJqFYyRi0fnh_aEmHwGCfL4P zBu-hKbLjXoOfLfAfXCr!Zf-b=)KRX0N(*l?78Y7vo~#$6>Z;LevGlLpAd1Sh=IG!j z#A)C?o)b%hNiadhu0nz-MSgpof&0{wdXnWe53*rf?zv9vCeGEZrJBFZR1Mfr>&qKF zcaCW@3vo?qoChP?idATWmztfHBm@t#$*tMQN&bDxXY&ZPN8e@vz7lTU6m-9P1;GH6 zH;OIwWUR4&=e7>cNg8AV3DfQW0M2$Mq$MDtvV=&~Ii+ysr1bh+WSMa3GS@dly%=`) zg+^f{FJ?asp;Xr^svxJ}gKfw+c4yf?c3=glHT-p{*T8YCJs+L%Kc-d9atCt(uzF<~ ztkOBxQ@imOulgy*Tl;9@Oefj!g4}AyH5q>lfbAz3Nd&O7Xl$}G+a3@XAD%^i|E>yB z_BN*_Es^+fN*r_wBW!0icl$jnWb9$NZ3jYI&x%DcE~irju_V(bwoyTgYI3OK*&no{ z=8oR{lEcMLwfR=7dRzNo8D@WB4T@BS&9cwamZ?oZ*kD(V82N+-7wMc`QY6{zPRYL^ zp5ztI1uNu$UWZ&1wDME5AV7E37En$&fIPxaZiG^F}rb_Q;00Mu8yr41zZHbJCUF!jov0F!R3OIu)p!;|<0geV;*6y1FaX2_8%S|o`62mC3;m5t$YH8RtE$c!S*VFt z9b$Jo?QE%rsBNnJYLF1#fgnw!D3gheRQ1qXV$f+QLf6bi zfI1p|wb>**(>ve#7PG(2wYoIP=xP+!p52BDm4sjX8Qo9R`&OtZqhTgt^oBP^IhQ#8|2L-G(7fzpS zPWB#3YY|$GPk*TwE;$q$ifnL(R7!DF%{?UEQAvxi-{Ld%V1e5PWk@k8E@@|cDZ5Kxi&GqahDktgEJ)w92fZu7~CES65~ zBvw8DCT;h+#4M&qCZ^m;$VV}?&dLH67C8! zO>a5x!%$h1Z8Ez_Yly3N5MBIhtgt9?wcD6LdOED@MCH3~ZPn=+x3bj^+*g^5!lC*L z_FWyDPe-0FPn4G|c8dAK86GT(ndpKX+i#sd9y zftyVIAk1P7TL77M`Me^vwj4o0u)sy@*L>r4WqCSvuW@`-X|3f_9Zx3Axzuc;$~I~F zK|a+WDzF!kzVk|;WQH~E2zi{~hKZRaNf;878$HFUu#En^&!Si;AKHWI5L*~+z-b-Z zU!h=V9y&2NuG=UsO%}UH7At|%u@_c2PC8P>u4n)8V~ln#XDbkQh**)!UV(y-huzhc zh;)Hf4bWwkU=KC&0VMOkW@5WCi59ndGhd6?!Q&YHAyip6QUY6S?8>cmXU5-(ho_Wh z;g%8a3TnamMn#}LGHRU-_F2qq{c<``_rfmAZ-QbecRXUQkIV|e4Fj|LIGzGV%*sx8 z#e+;9eh|98oHvV9xPqxTCvlgX8gKw}X{N09wa!FC{(;&Tw~Ne+3m!NB?W(^9@3jYJ z;J2keW-}858iaiLdhYgLn#0kGKEH}BC534W^H$scP(ZlJT@jT*77pfmSC1uCLH&?Y z=bd^EtAyyXG2#Va@ImYt4PFcOlLgFHjI~-lOh=F4Ud&-3mQ>2q)7{hi@lwODGqF2i zqz942oy`I*iV8?3%=q=ZrXzo(;hgodu+H9(<~2B9UGKqvc~5e)GsXfWD;Gx@^lP z{>lTRpP!rPK5n^N%=+|yWRf2wM4!VWsd^l^tJZ8*gEuLt@!^DvyX|m2#ftq%I|m8%F}hr7&2-cy&3X3LA}ozPV7g z1+a|itZ+!sj8(#7dotc=|M9m21!bGQ?+ALh00ataXBPlYwhxSM=lVOw5OePl@Al>m zYEaICXe5GrfUb9(S} zc6Kw@pvViQMU-sr974FvMyXky;(-tMhx_?s+uBnqKI}MtPq#alOOim!)o$t=n%;La WL#3W@iTYu<- z9w8$y3(Fhjm0!IhbJ7ayPR}o^>zW(Bb+oki7WTgkjs1RndGF}z4@oKtk4}yMoPBa} zH?;hAcJ<&B7!CQ_bAAV{8-MxUKa$e^oRM8z-PC96@QV#E;-}%Do*2B#tgj0F1N}WG<-g`!!Ovv>&Y-$0 z8hF9LpyU3>VCU9DUtwUVVU*;gwf$GlzQh^fEwA*0%fdt4p1$9pbOkVGPE@F|phjck zQiEXTMCWQ1FLbI|;v*4P4ewoh_g{$m9t!jLP_X+?&XD{YekY6;xyGShj*bTl4j_^> z^6^aW*#eKhGOw`aI~3C;xOkD3VxMvS0|jVY(abkeyF-~-vbS2NSv6>3QJ;yK6tp9MuvCsU zgocG3zmh1OLRkX#Mdn!Wc44fqwP|IKTymm~_VqH|-uK5O5>bt&hpBC78`U_9XJwNr z{@;gp@%EtM=Hl;Z~J# z$1}C2&DPeAg+DarB}s!j|*ReErvB*$&wG$XwTn{t5?X zUtwPS$pHgXOqZQ+87J!KENb7CvTwf3$@&B@d+HpbjDD z2gTL9l)T>sQIZ$RlHb1JLp%W%d&yPVa?E&cv+>s`j!%-2hB8I)K&9S?bp7Z0eV;PW zpF?tK=#Cl*Uccs?=ZpqD($8$6*;y^+0BvD4q!uZ9KKbuw)zAEhdRCF87nXw1^zXb+%_(C97+xM`@VH7g1RJ_X9U-sbO4Xl1h04NFDyd@P0 za+6gT_2-B_wx98eylqY1tV>CRarLH0hn@)#J@F~~!{@Jb9aEcg;4I0xQc?`|#@Q{t=(Na+g!Oq~B3nY*&R zP?i`Cb?;#z&B%?e-V4FKTy14vkKVKQd**{$6SA@XE?wgSDr4KA*b3Rxi1vcJy9YB& zqwb#1fxdCnZoa5((Kqw@UGO^o!oMQCXw2|VNMO(vzm8d`C=ucq6`l9|(dLs*4RJJu z^mCRjy9ix80HJAwqB4+c#STZi_A6 z-hN^Z-}KQIyNdhm0?*KIcXYHSq^`nden$CbQK)sT_>t(x_NM+gat@>CRRJ)e5~98C+mkDaHv6!^ zkSx=94Z|iq!VNZjTRTqrLYR7Ee@(iN>ELyCemQB9Tk2j>+OgAHy&19Mz9#)wLJdV{ zO{XSybTIudhf!CKi8lR(At3^AOPzGu9SkqI{KKO8G|bp-N;j&#cL%&6+`*%hjq+KD zM;tN*VFZJ{jf*viou8Oe?#klZ)L$87*EWnmZeydH?Wf-{1n7@9;kp%){ei268_8B2 z{uEBqAaet}{&B>MZ6R+%mew(Bqa;1Or7*jIP~T z2lrkuKe1d3KoUPv5@+0z%F$6`Yl|#6dGq-TC1{JK>niH;y5}VdKpM7y zoz)KyPTCQj*r-2_;<@@a(3$qe%@C0F0sXIuhWTMR*Ym?y`E>4Mp zAFGnapRMo%YU~en-|`E&yiguep=b#T_I0U_Fp(=pZFOvovd_%52eAa+Jf_mE+Xj!Qp#%v-;qg+NWV@+$W;Q zpMSK&>JVz-coM6bzWa+)04NZ#Vj?w<-w?LLU##~5zz)tj(iokIQTr_+`amtMCAf?? z$4B8ZPk)fyZ&S6tfXmA}blZk0T}Bz2kRYnvl&^A(J(pyGj0camdmlgZJaTz^kEFL| zAjp7riKLZc%E|%Ez{CtRc_j@^kT&3-`!7;>%MxZce_gsLR@+MmP_m3@ehGiLJNZY=9zm_V}#sn^l>7{*Kc;r`JEi}#{ zS!^s*YFfYWQ^SJmGMF>6KkF-JMd9J$ox|4>*+aCSCrhknPj|~xGcWON`9RE>HQA$M zIeBv+0q~_WhCN2}8Q?u1-iaZpB}s{=nys>?y5GBU!%64aUrRzT#!@_XJ^G2tn$pz~m|!2U#fL@pN{wh4qDOV#xj8spi=lxIR5euG6a?`#;BmM5gGZIgV+r!QIBIF(?qc1H zsHKJ|%@h`0ow@Rye*6L;v+f7=NZc3|aYf3-Q`fq>uWb2(h(%?t_MX1JJ1KN^5##B$ zjUHf+Mtvy<&6I(5g+1&yeXHHr)l<7h6f`r4wCCFWIiwm=OS$`{OsB5ePk{U}E0=PB z!0raQ@CU{BHT$05QOSN=9OuNt-3yW|`Cq;iE}|HYj;)iqBQ_0xn_Ut@qWPh2wVO8Q zr6%-u+#3F+G}@?PEVCvSX1~XWh|&0`l^H)z+#l>ki;S<$@~QTmhKd2R1=tCMTfZ@ zElWk$l0KC?7n~MB)4$=TK{f*<#4I1RJ`T38Lx2!qLX-$PD8^^aD^$K6r)uomOMGJd zRO4+)2gv;>q%FM?#peYBYLsKts^T5sXZA!w0P*-3tyqzKuPGZst>wcDM>%IvL%T57 z1I?AU;1hH{6@KH~*?W*@R>t+29PBw|I69@vndK2XdIiBR@5}*W!6riI>QG{>C2tg)f{+O({buR8x-( z1~J=4q-bmG_gg5z><521h57Cvf$0pU`}6r;+fJq%UdQOmUyFqKOK|i<*S8Lo@92fo zb&>vdNUIC;BEe$DKpjh&yUQt$f|xHJf!{>Of`qy>bq8^KVD4){yeu|LCY&`oE|fD6 z(9}+%YK$X6j}7-!0A>2+Te{)%F3%Nnz&NQTQBedh3EeakYxR%%D+o5llg7O)K8!S7 zcNNkqw&_OW2mbwE*Wv$K$s$xljr=!hh%Wd_D!4q{9|NqNW#2d=gI0RnQYOuhUEdG* zREC_ItjP@n7|uU+$$|y1=X>v{gvjQ@SgB8r%Mzy z=2IC7H0O`yLM7@9vMi0_HE?s%7ZuM1=vSE|*_&;h2#DuP8;NnU>r)wPxAq#VHAu>` zth>Fa`vNvbq_Ok4s9C+gPwz^9|A81dB)`eLNQVv;5gHdVJb>s_Z z-Qa1gQe^n~O4KEb=x0Uwn((FZn^H^AvN|l#ShdlzEM+OB5-{zmbjLi1)8>u>P|tdP zlNjpZ@!)mZQHqiwawpRO`29q9byBzY8b?w!O{)KWkSvYBRUco#Dx|c4pcPGPhm?ls zCRd&N4ln9YF_Yjv<%B1%{&nj5=Ug$;i1TN*OA zD?WjXIcKDU%mLW&t^^wkiV|60I3faB5UGui*L2=Gg&I~vk1@&9*bkI4Gs>xoznMdR zmJ1yQtR>uy<G*2XJ zhHtcu5W=II1oS_S8Yc6ufB~fOEgO3pYP?9A&S0Fm zVguu~A5V_)$cqIAzW1z1vK8dW@POBZ4XtvXT@%i2T@dkFr5#NfpF zyK8F#Q4%cHdCOPD@sKnUYE0&Lp z3YkKSliqmUl>tbUWEpM4V;L%IZ2KJ{K#0qHQs(r^9~S%B2=X;8S}7FyWq5fe0K880 zU6Qc-$2?sgQH{>z-?QYFicE@;hQg@@J$%g$R$vk2pJ;eecF4fOKeFkTw0)>dY2kCp zrk18@K;0-M)H0B-DqG}(W-ajeJ1(#z3m9XFc|?WpHj$R0Jm1tNLes>pt0Zk^j);$! zuc#A3&4k!xW9}ypTtOFHmF*l=O6PBG)S~pZJjq~{Ug81VQR(@GXsRxL65JU zBUK7<0AT)7TC=vf-SrGenTDRLPpCBsr|C?2DoqP)07w#i05&1bo=H$gDSQ$lQeWwi#Mgb3mjR zO4U!HjKS|7xM(TO?$19{dF^iIB}0v4jJ-9kxYerIv4E1H^q+q6rdN!OD=Uq2M!po$ zGw(Ii2mhF`S$eTb2T;tvUTxu*=X=zCr9lt!Xq>x) zpXM7{~v|CP-I^MVwUlvbYqm>9{I_Y> zmDcC0D^cyNIP9Glz@TZ?r#ANcgzTEu->Xp20pR?mfNFH4?5Ree5an}egX9PrSQ`h# zWT8$CI_J@{W-f0(oR(UC0<+u!H^eLY)~W}|0M5$GYw=&WabiVUsH-+07ub7d;$iOF~pRl!y}+J3Lg|Jp|k2R?5saGTZb zFE3@^<|;Rb3#pK44ukjNF?z=|W=au*=qB}&fLP~*(A3>L*~v>sdk6gD+VDfRFu6_d z+T8cE#vcbwUSfKThABKz4YfZlUWTUANr;B1yFy}<0~JD$HjPp*yw+WdS3UWhJxy6H zQy1xN_vV*hvQI-nAEE|A;ETQ`t={?(G}$X1{@8^(HtyoEkD2ldhCl!6vb8Dv5VXQG zwUz%;*Nl32m3(94uf5vUt4D5xl$bx>YH2@gY6444@Cf8ue8D)kipAwNQ8r?%jmItJ z;2=^%lpXOyS{u+g4X3Gnx?q3JqaA2hnzG#m*JirT({!Q%KLor|kO9u>pi>?O#>C>k)8 zxk>jJ{uu+16qVjBF|Mn7+1ZYfI)(tU#o4zz9F;yQ-XV$1s8x7Y1niq2b(_VzB}8kI zBsOh^X>ARekXP_Ot90EAzgr$CdtQ}lR!T`y_1dsbm=El84_F8H(s#; zUBjrhIr2yzd)CQkYzsQ@pIE`mMk>C!hYq33!&1!G+*RO>|54yfR5RQd+ms$t$*o7) zC)pnQkLM86H)%1U0u7O$NaBxebPA^z)M9^G&a|Gz{nxKP#2Q0vWJ+1LD))s7`7%1@ z?75`qCNAUn5Fv=C5s{_3CJ|fd+1{qT`p&#Mw@xt#_jr4PC_j`+7iLSG+9{ntLQux$ zt>ckSpD@U+zT}REpC!>?t+8d9frdTAM zZ|qNWoX^4>vQM9M-m&6w3#v@70dz8vlRF@T0_n#cwK^SfU&TA~9QNW6r?6hsGV3(( zqAHXxZkHl>XB*PEW_1{2-3=QQy0^UNLT|FUCdv9Hpz$n(18Frm^vUNlI_rW{OrE5x zH(I(_!fXrG`ZlS0?U|Y5C9J}z@b#-29wyKR2GSJn<>H|7kuwK9FFDw0s(yIXH>h{s z+Q-e)%ba0Sd9B09%9HSNcWPI63R|5N_;xo|XX?7Px;Wl>nJKmNH)dJ&nIURm--pB> zU@4OmX_fi%=kRUl^Mf3J-NYZ$;|4v_)y@rx3p=O_;b39{9Z#3P*;m2EpAYGMth%#t zeJMA+C)ZZX?O=zn!%L#b_MVI2@6+|FnsG6nNO}FgXEshwT-s56ycRSrmk(b++wtcX zStPwD5s?8WM#UEMA6cx^V)_N9m?6j(##>fzt09leD5{t}c3P zW~ch%e#M3~#X&O@CfcSIE0a2=J&t>91{H4Pb3xc5*^=Hl_9AY10M#kV^F42Gq%wFD z01z1YKWQ1uf7sZ6(=wTV?tjy=|9|m+lDGe6arg`b|0j#n&@BBwK;8eF7|aa9Aj41e zbOT)`WtCB9v;0d;)=Ah|Cxb*!HIS}VHo%CI-F2F#doY!I6-~}za`Lj!vbLKONGNO) z8~*5g8T6}8*t@xP+MH;pr1_!H5)T`349+B|7GN5+9wpYcz~2V#vwn5NN2J>-?w;Fz=$sBn@`U`A4$5`U zEL-i+E*hg?m)046vj1qD^OdIKCnhdpUiuY<9iHL$Iep2D{VOBb&x7Jk^J=QcG}!C& zUkl}^(gusd!;WdXH=BzsueEyd7{IU%kzR@PgenMS`O)vAOcO^A*@a>8jYmD>rQ@4s(6`g#X7gocL4Q)Zsn+m;^(v$-;;mZof?z##yy2^G{R54`ag~NEZvnLO}aT ziNSdLJJJpeOalNk&H`Ob;_8@fg(o@4+~_Orh3&3Iu~x#I`7N^S3dYxq=Bb%lGSpSb z6vSyh>9EnG91@imJZ?7t6%bw?ws;I)(U4)w8}|u7sQyKI7*yybv8FGZ!Zq!2Mi8pk zGxdO~p+3wMgeCgZ#{qRL5dPG&OXEl8s{ePrG(TUHAnrNRWK z_2E(=fzmPF=kJd*CP7?0Sft+NL2TXi;2o`92)yucVTOml%6QEX7=%o)y;fKjCFE1O9t(ItvQh9_)) zG3?|KCJ7UksgUn9;ze^N@Ym?u*!P&QxkUj)gc{`BfXZGEU$E08d*9jDusMzn%#%B# zF2;HKg;=$>>EKbsO^bLh*X#tHe>)%daD{>+N`|5;d^Vt3EB)^%T4N7=(IX zpm$yb#Sqf`z#>!~7dYR;ZC);aIYN@0U?kx)%OsZDxyF2=kA?}cghcT z3`h5SHOk&kXZuGHelhGFnA1ZJ*E^JMu*~BMivzEE#F6rPdN9G>Ngfc^C^EkyeI`n} z&hQThz7yL87IW~cz*~;RbN3|+Uj&uU*tW`C{hh1`kch+1_7S|K>!v-ClEP)W#iuv3 zQp2a;4Hjpa@*iI+JerBrBH4Dy7CJFbr{zKRqCnT?FX&Xmp&5IkqXtsKt$G@Ni%ZX5 z+R0YgzBsHc?vD?9d65Uhs<01~&e-w56HUiQ69eKi9$W$hIw6@Pm77v_3|B9uf1rdY zbDQtqc+*C?oL9OV;{;+L=lH z>)mE~?$ux)l%y26V7@i%uvL$APBqMBfMFYxvP;;&w)L!H0mxD`{TN$SEC*Vo&&f3! z-9P9Z&ysw94(ofpUTu(U7Ns_~8g+404Duyw-1xTS<+Xb3yN~{fm)GZxFvg>$X@&p> z)${HCL6G?~9pje1CBxy&yywb~RBM)L97u9jVW)dR+a*}|3$M&eO4d-SX>XoXIypmC zGJ(-!9{NMWhz7laF0d@yLvOs=kkarKsH6|IiLKc$WhGgRqPIAj)8^3`mrZ>7*JTu5 zt22e_D+fq`=-JGo8C9*u2h(29Es24J643g>3b8mY5VzhBrHsNuTOOAkWQ71%Q6*fQ zNqQ~LP(E4aGK585t*@2-AIdWix!Y0Ltlo&*`YG~ULoTl(M`g$^PJsYm*)Qi_Pf1dFwDVnO zC%{*mwrX$xaS5P=#>H9mLN(B%iONC`t%smr*_9!agg@mFm(AeY!9+cEdm$q*w|S0c zJ)-9xV0ONaeExmfurEht#ZlO|$K-{QOT;-`+^Z7A(l1r?xi2=eKsNkVPiU){H|Ohc y@!Pp)@;$!e0jA5u{zdo>g!db5%KH90>|m>Z`eBQI*}u^=7$tdCxvw(jk^cjG50bC| literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/outboxwidget_preview.png b/android/app/src/main/res/drawable-nodpi/outboxwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..e3530b98cc6fcde3e9b59b585326d9bd86d5ae20 GIT binary patch literal 22210 zcmY(KWl$YKyRC6|4=%yo-8Hzw#%1H~1b5e9AwaO;zHxWhxNC5C4Q`k3eCJl(TQyxZ z)2n+;|9EG*`>m&=Rh4B?kqD6>ARth`$w{e0KtO>YARy%tpg(IC6BPA7D~MGUHKi48 zT;AT_UtZrHpI;xJUO#W1U*7&bKHlCx{kwm9etmm-dHv7z`gZm2Vf*m%vu6MJ>frR| z;N)ig;C$=wa&>+CU0vNie7f!5zi;ecJiorbe44($e;V!`Uw^ha zdiXdzyWP8cKYjWbIeQyAeVaIc-?)8$eS15;_-F1GdvWvd@cfF5P4oQxa`XB@%PDjJ z@OX4`E+hvSICP8(?C!m9GOnOO8OMhg-PyevQKUM994iVM$Z9j*1 zipuKB>wD%-AG`x&n>xX`WSqZpOJjdzx%x&rdWNz^v{`#6bgW;dW&Np~J9Y|4fk7sN zFe%6{t{y+Q%b(b9SU!{Z(W{s-0B2vG*t-E0I^)~8=vF(2u|*TqY+gK}nLW&&wFTe0Oza4T#B7(V=Y@Xk2!qpv2|;Bt?_GG3TK}52oXt&nAe@ zasW372xP4P6v)~2fL90za)@tI;+o#8=Q)waM8k^@IkvQ|tq&qoFI~CPu8Nf&A~8TB z0DE4D7>P%Ij1+t(<*%d82SqaNXSt>4n3%gB@ zpQSNc9Wy*%R!nuhAIwd4zwIC&qf!=hOU<%&J>K7{bU!`U*HnX+-@a`7EWN)>lo(Ir zMD^!8-9$ybW3B(;Czks1HdwOx`mmZN8h);&xnB|J?~81*AXn7f^>R#lB>U+lFPazV z=u)b``TkO0!4!OHX;$O^)*2xh&DHGa?{yh@x>OUWBK6PfDDu+L|KVwAX=7rV(xm$} zY-gjzyYUe|>uG8L_n8>ol;t<2ii;GWGe)4J|$hcBf|ZsO|(eRk^V#V=F7I|(#9FFLFdiF z;e7lHuj3m6>7WX4%ibEMKbs;jMX|6I+|}GgZ#S*5eLrN6JTX6?T?TyJ8R|bG&U&lJYK*(3C$VN= zu~5LzQwx6*&Ui86!xNU!)m?u?v1nff9lRd?5k#J0K8cWd-n}E^+QY##ipA*=q7nnawD_CoDKi$vzz8tH1i9Nc%Nf*#@rMxf5XPt&5*8Gm!?i;^@0 zMwgCZW_2P(vjTxm9j3qVBlX@pA+--Ab|#&Cz?EzT=y`$pXv)SVy}!;nf8a&^ ze!Dx|e2c;t1y>O41t^rNQQhST2d7?)%$* zjp)bgZB6C$-;8cwx(ge;^nwM3G@NdQ4&}4n`}-!|$StP)`-F6CC>OzZLWIr#uolG&d&wo07&IP)9{V=*D(c2S4-(sTj%oGLpw5? z?Hpn$%f+G+ukI%_RndDkr{jT#U32s*>eQpu{>s2BHm56`JbI{e+2FI1B-zJwx6?CI zkip5jQ}^rZO|x;}`yFrPX5h;gk*?RLj7=vRr>4o5Eeh?8RK{R(G3_7kCOZ@0#r-0E zcojKcN&rD_2=_03_rls#0z;j zq#t1Q>Lz1T{fBptSmjsYw_xS6DFu~4-NGoVd&49pON&iRK#Y!FHm3&TWSPiB4tX1J zJiueW8=NNWHlL3`CBObk(qb{q*75osLXnl+4-4>1oqv{WFJ2etUSK#B{&+&@F(#gL zIvW9Wgp5XV=TFaoWhxdx%Nl3TbrG~Fm27c3V2?vBxu~xH*Uk+f@KSCOwvyP#`N&F$ zb-Vkpspce;eqTvwEi-?0RyCId`+_iel1iwiS*#dn?K)^8J3|>~? zpD8jpI-AZiHxnl!5Nc#)0OsGP>~?cuUbxUdHSy`1q`?H-h);w z(paV#xzu=gyAZci=;(yZ8X|tGMbWwd?!N_pzKruzJ2Vy7dtebmOFLx{kx)6629YcH ze|5ta+4l2#8|I80So>qCM!+KjjWg;O?BvUs$$H8W%=)*mhlNS6 z_{h*{2wM@w6WJ{C6P#B{y__V%bC1z9M3qGTx3Wx;YX1>w0yDg~9$w3X zDy;+=hqpJ#Kqhhq2_zG;mt_&tH+o*iqDF=C#Dgg!E+RJtRqMH=ICAno* z=Q@XoB1X84qTUr@qd9TPG(kr|tLlAwUM9AzqlLcaO8ZT!4&gzAOqku{-u;8hPKHeN zDvmepJx_9`A0nFz9KH@^)mo<`ne--S2B&jzivh}Rjv9+u#=!Sak^k~|zCX69?MQ8f zXP~RJ-2%;DbSw^Q!Cq9NvZb4)1zo zz16%8#D%oQ#2*^em8ndU|CuNM?->1Xr`}jg6J{mASz!(!oN6a|jLw*Yu+qkv2Q;XN5|6CecUM%Q3O_L zM22|7k=GNQZ@O`m6Q6Q8XxPjT3gDhn16r~76nrgAf+ed>=mmfmB_|j(VVBh0vo8pO z23b+}3hkl3xV$GyYfGU*ls#AlzAtQjf4Z%yN7ORi%HQ3VOv%~lrPH#7Nm?lH;0tCf z;C%L#S;b10W>Wt4Win3`){x#p@nH;3U)I|sOU1pLhRTDh^aU0Z(MWw*Oh3z5Cg32D z3&cwies#*Q+KusmE2k51~YpyoXW=fc<-|0|3<1MiG6td2C{E4e};c}62@ z{^9S_!+h{kXgSIehtf8A#u)q?BaT`QkphD2@v|DqA#q1TeDF+oWu>4u2a6qx5uJx% z-JiXscUj7@d_FdKdjg6VLB7WBSLIx1Gst=P@8`RB9&Q_T@aVFCQl)d>#kw?S4+s=8 z8orWe1^ZZ`e-&n4?Y(FJCb}PDYO*-e=0lE0Q4vA6s9W1rjnuL9Qxgh_Q!M8Pb*YBR zids%`n@^CaOI&dK^MayCh5F9)dw)`AG%SDEX26rjm#*i1>c;0&)-2i`b1p5YMK1qw zTM}Og%nFVx+TmN$;=^N;hH+g^`DGD;te!cee?d%%JLh_8HikyelGJfcCriS9Y?k3n z{mal?1MQ@Bw&8(8q;c%v6BAv6JI-gEihmIPtHs8EB|j!1O1f>@nP0?DG` zRfVk(jK)k=!%gD2(3L?d+i4yRPT-^rm!{iue;rNvaX(@(@gWSO@Ts5|Nm7f&C-I5TdgiSU+Hsoa7>kwQKe z)jcj3MI5dV&5BvuJ+Vnkn{;GGNz^?};de7$Jjm@qNL)1#t3#sAel#sAg* zBmS@U9}$f8UnP(qR6ry|SujQsc6LV=6~z+I_UV-&yUgA)+#VPjb>tf5-W?@35cEH; z|LuwS>AE4GohS>l5(rcK(a;@1?t47gnVHG>N|2iM6eU@CHaulVBRY}f|8PH?*S-?L z6+jb7{^A0imG1w7HY+Q}^vnrzv?{T1MFf#mks^Y&Wo3p&s9Iicw*E>l=Gadug`CNj zD?&d9yOP!{FKPA7d!dZQbh>dHyWNxw=d;eaSXQfFmta3(>TLLhlX1-sh(@|`@# zG~(B^TqFa)iSM9Ks(5$Car7A9A9YqTk<>Kzu_9^(?$Co^COKc18!AOX*a3FV2&k1h zvBs?QAU|k>I9}1bv?6YGn;}@oOV<<2)pI6E%DTSsr^bo#wY4?bdOLmkR{D&LmCC<1 zYodvjbkGCs)G1|ujtnD?941Tk(=Y=@TN)}lG>~f?vn2i{@QRY2Jq~D$<#)PP9pxr4 zeK=}+QYOFdM=ar<*4Nippz6QBJy|5OZH&Pp!aVNRl(0C`(%uuiNQ3&ft*q7$at|iE z5COk9nnQeuDk=o^>yXNL*Q;#GYdhTJ2D@}JVqYTY5_G)4&Li?3i^lT1f}5u}81M`??25a1kE;0+4nyw;o<~S;N?!()~Irnft2`>}}#(B)l|Hq})+ssrBC!tU41YbTFHcy;x0P-Eh(&NtQUn>5-b4$YDyr0d?l}s(= z3jYm6s;Q9ZHI}b5y&ZgCJ^cKVoILArm5rFb@Wao#|n&KHHO$^9T)7d+|IW1_Q4w>z%q(2E?Rr z@uKkU)#RjGh-7F*^G}p_6^A{+1gi^i~5#R4Wi{)U4l#o5m^#2cc_>T|MQ-Pq7$U0vRU zo466C2Zt-h3uMrTTp@>=#=S&$p=C%g$*yV2^kHjzx)mOc^c(~X?gC6lM@osMI1vX zHnN7->&1QHAt%+VAH?;wGo58lRnyfj6gAwVhG5e z4C8kw&@W}ne~J<2P*A4epf$lt>?ppjPi3Z(XI~O{r=9CJYjpJ|aVIYyn>E5z@W^T` zk%)dXSCQ2v@^kF>{@|>tzDnqggRitxJi#yh}dSwS5^` z>Ony;BF@8TlDP;r9oYoFu*ccPiLRkHa%G{0wT*x@27@o#{ zrl`;Lj`d)V%5|mCroFWf=-o%8$P~FNOC?p}Sj#hump_s|3^LK9GO@rzE-J<TlbsU+Z}`fN`>s-u|AysMS-<#*jris%4UYGsS9SueV@mO#u)BUXMYz5zQKND zS$iI#!1bi|orjt|pRlnhhTm2^Vk3$wF;)&Pn<)gN;7NHwT427L7BkPJrWXrAJhEQQ zP9t6iR5SMpy*lV5J5!=fC!14BqYg|Mj{s1po6%hZ2%C@FxT|Z*=v1j?Z)UPi0;Xq6 z003$J4GK*Ix_t^pqEb{GokREm{+uAw4BZYk4SiJnkh` zJCm_PWBNa1!?&f9wDMIKf*{fIhv`YQ!dM!tE4B7c)~!^H6nz*WL~CKtr0x2RK$ z_xI>Mx!D`5tbHsrxuWd}LgNL;xILFHga391I~OKmLf_(JxD!i`*~`B_5)&uJ$Kz6T zZ@UcIp^Kh>8`OgVu+?NG3eiWD;7&!j<+6&{fpG|n@t1@%^<&?QWtHAhub>|L?PBdR zZv|sZ|7<<&m`iqKD#2PUI*Wj3HDxMhe%7RwjS377Kbg=YSHwng&A>@%+BlHq{i$Lu zP0ydyf&s9T$AWP)GRlBOgFCD02wQkWc98}QI;^SM15`NT+MGCScp0ObO+!KKn-NL4 zOZPhhJ9@}6JktO=NoGP{eocUjvP+pB0})m?LzBi@-%fU>c7{BAn@RQ;SVrXFMB94B z;t6~rBG#%jVbm4;TT^D_nH=F>Q534IU!YcvhNf1;-{I?pOA9TO;`& z(I$`KfrocJc-~p{5@L(p&G#LqbLa>)PKa>#uP<59tUNIZkEm0x`tVaf2Dv zy#{c9A;-d*jep1WAGBu+xQBX{}UQ3-5Hd5sNxf*lH=X*~j3We4pL@n2+4SApNzXMtVs&wik~2Lh`xAki=HPSS$Htxj;zkyOg$peGbsbY>mCS3 z(z%amdP<^Q7g{F35Hiem+$!<;%_1>RFMS1LB61^Y1Z(V0@RS>L=9c>Ca6^VB2JdiJ z_rWiU`$aua!%}0get-Ko=dHkD`(UUZZu=Y1D$3SGIiXLG~d)z%dFhL&AVUbHX{YiI(z|1zom-BU_m_-0^Q`q1-;_j*JHfP`V?( zhrNpTaoK~^=;LK}F+!Or!MGpwZjh)~Sm#NubmP1+vd1vM zZR2a}h8E9W0u$i)F{2laWP192%wW}*$5;-CTBaC#b0!cm9$-~lWKM@o!$24aZ_SL% z|A?{OVrtztEgQl_-H|245}T~F&#UNUKOySd)pn4mv?7!2z_8j6uD*C`E&yN1CL79v zkrME7Cpf^x#U?=5zTkp>r7$MwuXuJp8%GZttSRV{GB4hQ>1^$t>I-PV!E)*DCcfhiRtejto7620_iNuTz! zn^s4evpIVs9Mhl!2VRwar+`JumYpzs)G^1$5W&@F_V$Rdzf%`Fk-Yu8TAI4prvgFO z?#~#~ZOc)R_V%M;&#VmhsP&2XiKO3!=(D7yZ?8x|z3-IHg6iodj7Gr83nqzg!H0vC zVM!X;a9@C%AIQYe*X*xnh$(12G^F@!A$gjnP$2d)wmp@fv5ELh4B?728gw`eJXz#( z>A%+Py-|{2hx5o=&6CzfC6LuKT9Kb4AQ<`JkSonq->&)K*pg1etb&qpx7LE3w@fD3 zx|{0-x`QvTXp;>6;b#2u*(}+ zj$V9`stn57k#VzZJ0ejNvh_ZjmD zXqKz(kxf}+o9m~CufB3IVyNk!_)K>L~6pZNVy=%e-%m*Rn4$KRo!5B{VnDX|Ncd3W#&b=*{%`9FrG<;c9B zK3C>%mmZmD5F|3Stz2@WqED&loJkTw@<_lCd`FaKh4~#^44@!w+YeT~3^M(0S7=R+ zfANtB?;L)-zqVghY)SzGEYU$7Q(^G541Yb06@-r1uA=-9^Ak(RP4}P;7pNi6Kn*b{ z0!!E87G?=06@jC^JZRY&&|%voakLFYH9nGOH1U)WSPjZpxsDdi%G9;Yg?p#ij`cwk zAoc1W6|lF<;IvDNSF>QI^auI2KQjSyX0_!))D*ocnqWf=h9G!432WaMK#6cGvMT^Y zOYJ1&<`?{_T+zT)Ai+kB_YAork@uO?4y_al&#$X0-)=K22Zx%Sm6+)(uKX8RmzrY9 zkNP`*0y;J(DPGSwd)ThK5;2oAX9p7LuIW6ys5sO;N_3OLZ+t?i-=WYGRDy9g_4Nfy zgODS-LGbM%Nbp&&YsE3pY_(fGB1X9^H)KV9k07g9!BbV&^(3~22OdJ)etvvlYicR? zunoTmj%xyd;^K>X9&t#7*fC2@g zxehEq5u+t!+nk{d?+Wo9oCG^CXvhLEeNse|DxJ3yge%;_uwEeaI+j2K#;IAF7S}0S zl7*N<(mtDu<7KX#g<%Hv*P!!?1t9Q>_XIuYNx%&=Q z>ttCqh65VEEMb7AC|-ZrEoX}?^LaOQq9`iin=-b4zajvv%%`rDsNOoG_E}wZpNHBU z$s?NVGOX<}5&%tQZ|$7ve5cgb6ly_D(75-!6lA>4EzLiW54;J{Zm)T|AlayMc?RMm z6#Z)eh?qdPfkFE|?HPbG3Q47zIhlM?j9wygUk)428;Y0@Wc=@7RXVAL1pT6up&s_o zN&;$oYqN^bTy*(fr z_??1+q3ziAF0W4`q{?-yBCp$1w-?fawf-5^cC13=70DRSs*VQ+m{{NeMEcK8ILaZp zQSG}CY$sVcQPB$Jx2!8CixG)P$tuGN;poRy1Jlmc^7})O{4^bLajoZtEVuhh?Ae7qq!ggHFXA3471hz*vm@2sbzHFk+kE^@6` z;b7b|H&xbd;?bGIiu@(!8AryypxR$M&5-)d6MWkUuyzSmlgYO_> z015wh`Mc1}qUp4*;s`=rAkf09>kviXSyw@!QF%x+bQi!UF#p#=ZJYUGR?QdY95g!% zE)`)pdC3^{4lgQF6|-ew*4nz%O0V9?pW$dCV4m~5yH6+}5Z7ctUJceBG7S}&&x837i>tI!q3j;{!OUC!)-Zl@{gK(c79~SC2NO=(8%6i=JKNKI=t^Pb4UYr zq?vuHmX4 z2H8vK@wE9kyv~kYBilQxfzwgfT>H26(Rdxt^;sVF)LZPc)*eYXsLk>A@>ZhZvadGG zDqMC>)RXB^)_zJBvb_8<@VX!r!ne4F>}};TYsvD6tQ@(UIY~ZbG1{2zXHmHlzuSlm zS`-Ph7`j-y6DmLekyRY6aRuQqv-m#TATzTg_Yk_;fV$3xE3;amI-(wO##lSzn#mt% zq_wZ#!Gq$7@^argUqj?{5B|EdeWq4%zF*+~ze(3Nd+>X-#m>m+jZ`2kXf15u*qEAS zHn&6XeMkeY%!nm2>6Tp=VCg>Au8o<~Z!RVhWbGQ*(gTk1E&%i4%zUxK%&Hj5p`BRI z6s_uP<>#o)!i$#rmuv8%buQvXsj~UiV=nLzt8s7DTL)-rWH&`D7?uV4$JMUFyu@1O z*DOBA)~>*kIIf+Fc2BOQgn}o(IdbAg=er=cy}3u!!>*!S&`-?jqQP~I&O-UQp)o!* zSENvC1ay|DW#RyI+x3yD`Zu8fbYDOxY2?uxORvgDbDGiLW>ummF%`6;wR2uEtZms; zpk&LI#zLtJy8R%i#={u--(awSDw5ilQRIx*^vhcHyNld?8l9A2ndV-<0r^O3(7%^x z1~njms-X3GX-&=LCMv_-x})d2xjpUy^sV%s9pOZtrlMv(6)-VYzH~&>3~ux{pS?hv zy#HQG)a>)B!#KCt@RHae`P96&@g|R2SxWN^EunlVwlJK)z3S1QK>cx~ry0pQK86XL zYt#))z_hIqQNJsAa~PJb?itJXI~>bgL^<;h%rozS{5F5Yd`mJu{q zm4(`$doU{x(&_4B^QrTWP5k{OIVE&V8fte;pwv|(-Qu1%uEw@OMkkjx8?Gra=prCx zQ;eLpcBxz~=)2Y#w1aKsL|n#E1MccVvPk+m3fUhH?n>=T?QdV;b@%bMsgF&H6(dH! zDL8~HzYe!hadR{94yNGZ6;KuA@VGUIlM28IQO6ZVXkl?9aT@t0?0POScH$K)z+0%s zO95wAxi3aKFgV2{J^NQwUHX@*ao$y7>*EUbX)MtD==)^B;galyIkS#gD+g%2qZ(CB9VGI41ilWZOcWyioi-&<82{>~ENma6&FF7|>@lH#4(?<@HUnIzV40 zOzl0r?|f=%h~5G$u+iwaClvwiXp7$a39avcz?O>pcX{6Z4E}fqbAJ+bHMM{{P7182>kKD1=zCMvGHDx{( zWbMs=EWavzT_tYk@he~5lFu|z-hxRaSbdQ2?9)5G^$gn+&>%8eGLbKEx-3lU^Kx$(BN>GO)ETOWndGv2wHNbUXB_+=Y%l}|Q0 zf2wr8p&F@DQ2C3EU%*ze&6C)uR^?j5j(v%!!lYvmCDy(z$49XVKmPhbE0^<(0r6y!#pVl1+e`tPotI28tZ$ z%u>ro>2rpcZ1u<_X0?-HLtCpH6%b>A=43!hnK2x%v`#$m&vaS2o7}-xu*@5cg0T*M<(O8{WNK9;4KXWSeTVycQqjj0yyZ!k_55ieKRwEv!-}A?fa9 zZ$d9N4y%#A(*dcZO8*j~T?;-gy=R9<7^}0aT(PM1;Uwuz^Wn(0fg!C)l9#f`8yoAJ z67q9OrE`r=8Qk60WDtOO{z*?$SwBtL2=CgX9}HC~Y%c!TY+SJB!H;|AdJ3aQJ7Ai_ zl!D7dYQu@^@Vo*43Y&KCFhv<`vpw5t0># zy&Cmtyqxtfpx5`e>>Bx_3%CIFig0SK$M+748 z&EV}WB*JMbDe~0pB;NRNZZvE$LC<)5p7W>M0^;Jhtj7VJqM$Z32((FW6;cyGP0B8O zh*}(L#SjS}>s{qkXW_sn0-DxYcz09Zohz;=4LE!%HLP*JCQncbZWD;rwRRA4YlU1%2FA6K zWX}Jpvy6ey<(d#5LjdF ztwIK+Q3TxS((*En1ei>iIBDDy0yGN)q(t}KJ%hw$4P76=U%)=v^qbd^SJJn**ubFvL*lnb!=0{t_7~*&=^#0D%XM2Og7Uorz z5qhJ@7HlI)7j@s{_veCnTbiyNhyVX>Fak2~yR!GO#+OZ=g!N!R7h{-xu~gZW4209Fkajx)`QO zT&bjU6)8`ln96dQrAbmpEzv}BUo`OIap(5?&5vqzyhDPZzm0ZPro_&eHpfPTby*Sz4L=uqr%$xus z0}$^%ys5tTe$Gm*4{6eK#UsfJJ#}`J74tb#czRoEpr(K|Xw7i)0P4?y=AhcB7*WUR z247^5e6U}P3N4FAod;O_n-a%P`R=C56Iwo6@ky5*4SH@_NpQ-#VF6~}e3q_3V%w=P zor$M#F64Nytr!UiP&!4`Utn9;xaq*u;a36-Nk25+?9 z-rZN2)~idptl4eNWEIf_tgO^3(tz^-9qq9M3=7+^PHaUbBG9J-WDg6a*AhKrcVKa1}+#vf2&~(`jIb_ z%#Q-8xwZMWxB8Bib}d-k(y(OhX})gsa_AeX*0_~UgEr9o>q%x`nmi2=q-TcrNw)On zNA9#Nbyl(selVq}(njVc&5j@MyJ0*#Ay)`c8@K`S1Mq^!IFgnOkI*#EI^alun( zgGXaVo-=zsM9vt_i2sAV`dL1zRytWa5JxC*p(MAS5uyH)Cs}6xAmt|mqrd#V<`;q8 z34z?HzZ)z|-%qciCK)<6bdJ*^ll=3~NU)0fO>e^8X;SeQ{vD-?05Kc6eKaiMC67ay zM5sx4zjdUE@cZF&8r_Sd-%bkuh{Q4iIi_srXxOOV=Xsnro?~hFwl6U*6+R|)N|scH z{p*(03B_)b53Bs~;6~W6`yGIEFJ)Y$K3V=c^^Ef21k*Ow#4lXKS9K7 z4sUewCv1!Wh?2-8Ubp5Q7iXm9N=Ej!QKW@GPCI{7Y))ymn-#QWxEp2QE$&j^3HMhB zIdb(>IsTWHW~5Ox89QD!Frd4Vz&^xz*}UVDQ~j+IOOGw{R}LK%BtKgV*0`%*jvTEA zjWO=G<*wZ|*59qIL)*@ZcNJG}6)OToRptM*lx;J!ifrlkoZaUE62eE0PPsuK4ZSf< z4B2mEJ`DbJw$gtOB+!g4aIslKTo}=0Cw_bfGVFMiXEF9?`Z?4>zv*4HjmYOxEhk3u z^;=^E6Os`r^mg^0DUg+{XmNr$qG5=J5B73TcvQ_U33i~r1fnKm@sTAzYzLn+B$7TEiP@0=$*mBb*jPfsX(q6lr1(I#yI&7YaO z9^@5)!qbaG?*S)hIejp&7+(VV3;gAb+q_lB%4o{#_{%8v39FhT0|7}RInbM>WtB3w`;RfgGuWzw0* zk%*H+6(+DnfyCE`W6d^6vp@ce8KYKe>)JN%FVndEi3^-Mea@?3sf3i)qA>LENR=;c z0@L3Le@dE$SlZsNl{)Znux{rh$CDh#C420e_^ip<3OuRW3jm1b^(P(#`EjxkU9X*0 z3F^P)e($lG`X_iwumi78yN=FWkaEd9^4)tbwLfg#zxL^ZZ0^t1ax2AW+G$`u`DZSg z|IWZT1>$WQpU2nt<~X}V1?nt0DhaSMuy#qIA^0)&U{8vWVdPIbHnt5G^2`X$tW;Nl zieK6pP+H`x!J_-xS6I&GmLxM?to=fPaqHJT502|R#wh8rV-)}H~@?w;;Z8jkX3*t*RJ8g1b zIej)$UZN7eKd%&QuJ7&f_YN-W_NP5<3Rc1XR56^YUQLH3JZC7v5Yju&ml^Almf@Bt!AJ>r-q#2sQVq2JO5;bH|t7oF1*`w~c z(OIYFV%t7KHX%&Y>&x@+MeD68VwGY&DuD08StIyJZil%wNKa&OpMY4%!}pFz1bS%g z>dBtVjS!|}&8o$Yx#!%b{0%&-S$|nic=YR?{9$`r1cyV{53IGW6#t#y_SU*J)*MXe z=;)#~-oo~B5*jB@-dJTN9abK^r;Zmk7iU>X;oL8>;>?EeRaJ&Z>!ojJ9!1+T9N~qX zRaKIDU*7J)>5F)pjLKY0jd2O-(awd0-hw9u2JugZ>Y&=Y))rWRmj*T+hke)bCZkVN z6S~h?V8=ahFvU8e@S`hwgj2q5BHNoxV@EkXSm6x-Y&U`(ATOMWG8*77w8EDBONc+f z4+}e#X1bhy=t=x9|$UC_|F%NmpR zvC_eYSF9;Q_Pza8s!!Rcvn&LMxKUo2lWbwjycxPv>>LLFYZ^L!>J<*>izQIj3Qw$2 z%#Jx=&XtGV;%Rs&bLVvj_WD|a{?b2rLLA)Ji+ey6zrZ7Pp;!{~xpez&@2TxskbKus zs=K-NAZqx9fIt^8eA(<~4%k7ju@?>O;&zhD!}Qyf!k45YFalsAscUFA{p!;@s24(W z>I(2_Fxx7Ds*6ZFFR9^;e4?GOCp?^b1|h|+*wdelVossp7Bqvv<`{h2!z$Biqx1X2 zJ3DIail)ceBlIk>_x)zSlX{i>I%JqY1{a9H?Oxp+GZzt`(v108Gb-0EbCL2c1#EYY%=ll@sGRA1C=L?d+fX5gdyPu4ATQQ@!$cstZ+L*bS`j-TKc z#BW|*a--Uu`-V&cHvj_f72YcowN4j%iI!vz;rUKiRocF4xlEmRzGmtx-EkDQ(m%AE z8o_2*+T?Wtu)|NXq2`nQG>hK!4;!2n&4B)Z!R_an)5iD$S~&7J4++S=;`q)LUAHs9 z_d1=u8t-IAxqaSF?~s2xE`#+Jdlm@lD6Dv1za6Wb9uv22wWVibt4aiMPk(z0j;V~b z<-C+6hr7cR9xJ$u#CQq}WQL(fHNxw4bSeUh?YmS#mlHyefeFwY^>y3RxH)DFiJu zarnVVVcmu_FKToEWawG(@D{sCkysQX+6qoU%7^n`avT=XJ2I4>hd!J{S#lUSSbf~c z`Z{~`8m+VQPaZ_7dzV#3$NpC1kyKp%;#GWxM-;!uzazW-wZ&{b33t9QN@NnQ7L%qy*{oVWKKrc%MweAXa_m!ar4w$dZOn!Ne{OB}$9> z#C&+HsqF|+P7db1X-aV;W)I+((RLfM%aGY4gvzU zzwWMxel5Pj|AihiJ%Cs_uUc3*jzIvGLG_TJxQw1Hiv4o7bEy$O8ZlVmBk*qcAD+sU z=$;|LRmB_5<}i#&s1syhfB~M$r3a6|8*9z$P3xS+d=k(>E%kBu9$E`k!Mm#`lV;}P zb+8U1)rM?@iGn%Rw&s%(R(BH0;9f*M=Z>J}Tg4X?dD?yWbwuYna5#Z3PO3C)sm9Uadq zfj(if(sQUG__1@OVolpCJ9dNyF>qpwwM{L7SSx3@;&|%FgF`*mEhSn=QwEK=L(j=4 z`LO1}buDhCaV{Q_H@A_0rh}U9`94uC;cAwYo1mdrTjm{*PWIf96j(z4>Tzj9%(#RT z4&Y){!iPbCBsvRCi~t8P?+R%=#wZCN4mc#T(nA1U#-0t07G722O}*aFojC6Z65y=vJL$QM;63iT2CPL%RN9|)w{ay5W$X~MS zNyI&$zk(ueB|PW74bH>R@CNO;@3}3rkGky|_AydmX&V7b>5^kYzo5 z@q2&&m3XDD$S+v+uY}L9(!Mnr=AkOTpYRjkaN*~a`X#2WZfZ@uz2ECTTBo7kKr7xl zTxft`=Y0r%!rE8^3Q@R~k`gN4fUeJTG1@7&yM#f!s9ry0P~gH8|kpe4HFaFLFc(c(WF6F1q&peQW!NsQ1+*hiO~t*{{#_az<{7^IkF zL(jVc#~H=s0^E{n?%Dv z8U(#yt;hwto9DP{yIWi9v9bN+!?`L|`GjgOMTx;VhJTm6n-Y_8v*Rm+x_yqEu!w#A%W=^8Tm4UkLj|W5QosUQ)IiU ziFVdOawbkS@pB@|^yQ`j&$uY`FnOH)xLha!kOK3yVF&PkN;$7+IJkF>>orC%(V|Bu zdI-rd2r_ybH5gqm2ANMy#xQF19?_%5FhlejqDHR~gy=m{B4Ieb|IJzFe|zrsUhlrz zd#z`!cfZf`BMy@y*fy#j=|USW*!9g8gP9&2AEV1y?R)vDD^RW3TkCAldUk5d{wEwt zT6qO-vFY!pt>mVuA3wI6W*-=|Fc~-yAAZu|r4M6+Jhw8>lbsTa$m(%B$R1!HE|hfv zIyt^|Dm@%$pcqwC4 zc9n_)RQ3m1PcM(ycMYYz=tOvH`KwxY7g&bBYL%B?1 zC)_AV!Ik1_HNNof&u6TOmJ9G_;Xrj$U%z-DV<7Rx{E$NRW5}BZY}9>b74RprPrrYF7_>~X59xdFSS^ffnmeQZR7PgIGvD3nJ`kEMvFN~gH4Zv_~DE(>7DMLQaxg$ zeuD8yNv8GUQvwuMysH)DkvJKaRqL%I&w%lD&~_jJ0TcbdT!1Y_Ng)-Oz#RNW4lq>n ze&lH1D`+xG@g=~bNaw>$=uP+a)RuI)b7%KS+uHNcV0`Vs4RXw0L^LN%ZSnZxr^kC9 zdWbZ3jK#i0{iK3ub+5=3*X%wfrZC$$cg10?PPoo{b-5@7xH4G7+*9MU-OT3*Q^Ez?g2G8$&Gd4Yr~c zUiFq&TF{3!Y$3ud8V_=Kg2PrT(-(45DPRnE#zc{1c4{Ee5CwuSY8mxk72UMm_m)*Z_dnyl&@bikJ2} z>c0HMIvk^Qewr=kli>BHDGNNWK7ZZR)JC5|)KF^kG>Z9jLz~=TqL}*gkI2zqziv&* z5v?dY`h+nR@0ct> z!S8u1Nx19;TJ|Rx&j$C)?QOTQ18C1Pw|@8}Q;HSnSg{9_@8FJ3DmjF-3LJ zI}Bd59zNknbXp^eO8;WEFN82xB+nxJ*ac%8fj@DAHh#?ARCSYMTOkH}e_&gSIfLdY zpB{51h5pC$VKNV~KHfF6CrEf+xXlZ*ZxGkji~0!X-D37)k&^pbX#4UAS$8y36*+s= zVPX5qYvm44T#8$sYI3STqZgvLdTkNP261~|S5WZL)KvL|D#=Xo zoU73hI&9BH`(U7Bm*ZC7_S#Urgwf5-h^W_eG)m90_LMjVKlmR*3JyC5$$QuNm%^!u z%b&V165Dy2V424!Mv_0~Wi;6~9mt#R@>cfTdHf24%7zucps$m@zly)L-7&ksHM>k3 z>N7GG0~hKh%uW@o)~KrL+Y@S==ZZepE={Vhn#Z-&H<;zaK1N7?~zOvB(W2K~SxQhI$!`Gsw9T~TCLWuz7QZMt@BI3FVaGM{d@MSdz4gc%c zH7byoQ~EW~B=&5rEa;HK$eqRc!H*RppRBFlr8T|_64J zcsJdc)r4oMYU`@c`kgN>`E)qStOxbB^EtQ%M_Fh5${N)aIV0FEJAa_PC zXuY`V`fzu;X>%W=_CdhhffeYe79L;?U$73Qiw&br^!wfTbfQ~X?{jNwzx6!vTGMWB zl@QsmCCQRt>(UZjqE377(5EI*MW!OKt<|HZZ_vv8+mqY`XH#1liy76YvllHT13fmt zW;3G6o%L>(qKJ z$Ihs)0S7;aUC1oB|FQfki1J*_@mh(xz$+(Th@hTOF(9c%c z_0+OX;=ttan6av|8PG#2E(vldGcsj>F5 zq+Mo}Gq%0mNhit`);I?W(PCLhsSq^7n+9J(xe9i6v&Esar5|>Iq_(ZMIFAa2yPtg> zCy|F!lPT|V&c{*NXhM_5`1(fjOr{5~Nn1;6?U$Dwyy|G4P!xg2F;vmeXzsZ2$W^Bue&?-aL^p{n=Dou!c*)-6% zR@0Yr6tKhqJeN@+^Jjw$sTP_Bh3e|ismj%W%fwnH>$=<)4{6;%D(p>wO78@h2y{lo4 z_Z|hUZCGmyK(Ulc=j%8)a%g+7iVu%T{PZAe>Yv5^nigF#KQ3EFc%@SRD?JGj!R1Z$ zV7-I}uK1l4l9v%OLIGcDvuqM1O}DVWRs2(H2bbS`1CORm%2~XxjepANJ2yA7(;g9M z5!zbQ&vmG(a~T{YnqF90$?gm`XcPV~PlTo$mE($+*3k zHDtk5+-vks(N@*r=~yJ+n?B0ftqp*-ys7~VaSaz+rQ4Ay*Vw@7OAc%%FBl73Yw*e|r2ncwzwT8)!+1&17C^tN2*jz2uD^T_p07 zvsRI&J_Iz;_Lu(sG_c!!0VFRUA@2}8MGLeNJn0lDDPl?=0I1X0rF%~f@-1S>6A4vC zsyqC8!18-Y?{!7~$?aoe&VQZ*7Z1|>NhZde6Ni^1c6_^k3h)ejzfGF{j$Vq*I0fF1 z6SuPO-Z9nrk)*jmG7~L1mg7pZFtK8W$!zSJjR(e*pq0on86g;bq%+Ox_@wEwY4%-r z-8qR^S)2n@kW1zYL4w8tEZNiil|1yhRWsFtAsxgJH@4=Dg5Ds?Mr22538R;*%a+f9k%27M!f1BV)WlN(XIA>a zjA1@%DYfs@hY=LQyD6Cu@Z29L)tsR6ODXTRiw~h(%G+O8X9f9JF*BmB?e4}Kl0O6H z^E_rv++>0AEST_nz>G6zht=XR#wOPCEV!|ZzARq+$*mKJL_ThfD zbgKl+j05-6D2R-cXH^1Az37@2_mcL&YG@10>BGN2spS3JzsU`JqF&Ac>q;;_D4$;K zP(#uVA;8jf1R*FYO8a-mt)P`XGD-2V{+-f;lAKN7S2GOvU;9abqk^n>`pvbI z+IyFzxKE3HPqmi{bBpDl8oO(ypkDA{$Lr?E%~a`N>IG|dAxc?^$&r!^B`S{2X28L> zLf@2wP1~B!lXMoiE0g!cGzyggLzU@>^j3Bkd@u)9KOgi!O-B`A;2!!HMiWSFdSLEl z>F|iKxBst*LcrXMJf;aZeBWjp4~kP@NaowCDF?1zpY1y|O>2Isc|5@}5x05k#s*`; z&TWcVyZ~Or0BF8>e6C`c82_585G1dMlp%v02f%Q2gylFlgz7B4``YR;q7vY9w|{}y zi6Dy?&4TWLBmk)7R|vV}bBL)!huY~5){HlG1B)rtJ5p8opQbDSfzq-$iF+8@^J{$9 zSoS31w`PkIkDT&vbJ>fI^6!*{+nAHk6WGckD3Y>VgA@W~slJ|oeALb53NfZ{cyRX* zCcqWr7U#NLO3crCyY?f(d9@1M%>%0{!UR!*h79rUs^2avQ9Pa1#Y>~}+gNry$=DbykF>~c+j=eJEj)36D43Fu0Mjc|a9J-`ZANBq6_g<-2yqrlp%&jtr;P7xfsYbK>k?g1wfe(2BE(oTrxf;=!)p#?n)D|6gGJd z^m!&`F*+!BsGQGDddJ2j1t8lx!SzShaSbb+r%*)A+~W|x?ZHFKcQE_#SW?BG3vrw| zZ8tkvJpCh#W1{qqUnI_6QrnLC>NxU1`H$&Xi3L)|@V=B_yyT90b^6SlgP}98 z3k6&JU(eKASECju135dAR0kCn^~4RMQNgK=mw?kuwS*bdC%_~`cxA1N_FdEz_XOS; z(wLWXqgzi4--$d_=1$8^z3Ab%%r;8!oq3g>{+Yn7X8Mm-kf>3?-GwVa9_4|tMTN7Z zINAVnLgPVEm&pWirFqr&jW=^0F#V&^;36%k)QMGdRWAhbojYPb%}+8y<80W0P3)JA zMhDBLR)<*r^YC!H!8998=5Nca({v1=nI!fhc|B`ori?y;ce|ejvp36MogZq_&1Zo3 zH=uyrHX(Dr=_-@P1`Qf}S(mnj^>no2p;iHhseT{$>x0BDhb*LE6ONSJFowNc^v+h0 z*!`Eo!fOL|voUgf*>Ryr@lvA#LXo64QLAhzTksYYp66UeZbEXihLWV==PwE z;oOxQVZJg{bjvYtp-LP(*MX0LGDb^=q4wD0#AR^fg#3k&BI=sjWM?E-Jo(xe23xoO?>HqWCL~qo{S=+8o58s~tV^AC|KsnjgdT1!1EXFxS7e zW%8q7yae^k?^GsmLwIQ16uRHL2s{Kl?*7jXdX6q5)?&*GDuq zG11!^+4E8zw_M3ynW*!xH~6;)we)>{?1s5?b{ik=H0Uz4r z7@l@HICn%L{)6k4!DYS=eS=N%Q_zUSggsa+?(R`sczEF1(BdTtEQ;mXf$K`hU($!U zpzzF&i__d2ySVSa@1u5Y+s_x~ZYzVV#@u!Fpz!p_4V})5^(mi@>;0-KpWd?@;yBNH W8paYwB)23{0v%0#P>uR??S@An65&;qv6cp+Y8A(+rs81j$C}>$km=DW*ti0}r zfmB&uT}s~4@#*FD;pydL|NQcLd;j=xeEslvb^Gvf`ag|N&oB26Pg@7)oBQW4udjQ@ zS38H7AM3{c+4TI{`+qH42N!Q|@2_v~9}V8#-#@Ose|%ZnJ-fMkynA@s+`l-zyxTpx zJiB`Mi2wHXc5(f%fAW#%@#AVte8$7G8&qssW2eaH=VwF=s-N~@XIFQ(clQ^U*Sq^i zSJ$_vXBTE}(FaE-uqb3C)I7MPY*Y+_mL73iy9YKt3A#4H{xNxjqqA|D6;g6KJi-dc zCuedh22cou>sxzMGYi6!YOFlpdxxgz*~F*jRw^2Ll5=Z3Lo-~1(jEO%LK2H2Q%j3V ztCv^S!NcRp>3Manf5GGPIr-%sUH!)9Ezda#wp=`dk2Dc2av%JIW3dWkG%W%Sm7szn5}#(e(eflOqO~Go zQcoBds> z`mmPxUkfG`$f!_Bslsq6V{%P6Q}HFLN@=KRI3|`{+Afti+f8qF-Q#xoLd6nT>be!J zUF+Hg2W>*Z3fr7?PQb5BI%HIDzIWeV9ou(Z9^Eo%opEAd2;r`_I_*AH*mpcWjo9lm z7~gZMHhG`!^8E4$5hJU*`=%gyljWjTF_6rr<@c~RQo|*P-L!D=P0OIw55aJbYpvD! z{S+b3Jt90O?#!t3`HYyU&G|K@Vj&V%_;-Zw>8+N%B}Ry&ufzFZOuM^17Dgx3YWuJK zw~CtXTVN>8yzg=G@qwAzOqGCrxA*qh=|`K6=Z(|*wRR=Ro0prlW&0pkSgUhki?+9E z>stwAoW~`(jUI09)lbhz4Qt-d<4ggnf;toh`+qVyxs&eC*8CjyhgkG0W(52jtGoVm zLKZ!ag%+>Qf}RzkOBQoAB|f4SF$Iipu6DaWuOf(ewknz#co%=xUA$lG^u|45kUH}z zI2W7EMsO!xdG56~7eHA2TTr5_;10D&XwPE_Zl!$6`O7KP;`mOZHDP?G(|@crnO!o7 zz+<=WUGw?*)6Oi0FUz!ltBu#ic-;Foidju(#KB;R0J@8i1%-Lcfp^-=T@b0h0KSg) z4U(0Ojo0(ws<*pLp#OpRL&N<#-&nryX_)9q6@vSDmz+G~l$6R$)w|EBB*Qa&+uSUo z+}^Dcx`Y3bx5XDsGBn2a&JBK}&1sc$f|JD6yqO~a_A>cT9z*{d!qnI6 z)H(gvRWuNLB9xJZQ-EBVCcDP?_vgY?)ici-=J(VCaqs#CISf2JfCKVc=c|7fHib(* zhrTHXvUJmBXFHD!9m*8k_dBn0;XPOCJFmDbtb(XBf{vfExEA)SG|0H4hkG#p)atS=AUP(h4SUPj$0c@&VBKcXPkhkFJo55yMg!Fx@fx_k7;jK z)UVRrIjK7!9{@~ssW12eiSM-e=NE7fY3hd~5#ink|7NhKYgbC@+kH#+>Yj7JrZKfw zF0Go|+A#JlQ3k^zrI=>8heMBGnDz1jbbcG0YpcxG(Q**?Zj|`*!&E*f->Zw9{LaH> zWPTj=9yaAoQ%+=)1WMY~(Oj5tV4ja(E3%lSmQRfSwDrK}>hiUkBM;i+tGmbJ{B2ud zatZc9YJ`=lraF$dWEckYV~D2K3x5VB8zEU=*=#7jhrrjgqgpB&?Du4`=L~V7tL6YC zuV`qIM^?RvIth*N4sbQh#qwhxY1)#Tdo8^$C15mD`qp#2|xHCD4XShQ`ohbTts zuz?bxwH8d;Ip_fRL+LEVddP{qxzo2BmvtB8BdMmbED;(Yb)d~FyJ^6pCg%~`{Ulc6 z*2Mg#TnFrw)^_J|OzF=lU$pob$==!NFH$Me^Go*P*P6?Fmtd#ZuxIbQ*KBN|Bd>-T zaSh55=TIH{V0;VN?$Bd`FAM6-c(O$o-0W7s}pHzp#)j?fxcom|%mFxx$P8BYt26 z?EEL#27kzkF$FC};VZClx0pUWV;WxGRKPVU<2L>3Q%oq?GnHMyz-JO@ZALhNG!Omh zlL~QpwH9Hu^#pAMv_UF%JNgk(DqKvLy`Dy{6x2}9lshJ6rqs{@nd+Q%iyxS&lz38j1BjJ-0iD4dOSutp{3hy z3H1YRC|p7s@x_zL95J~UC0WfvB5i+^{}_xnl)eZP@5%AXG&P&=k;z)i18GqIsnZKA zgqzj>Eagl2t(K5|6-PfF;`jHL@>-bD-{qW)^gu^k(fL`VPNhm5+@1V;66hxFF*MGx zO==7ai+Cfl^&EqFC8|pystMDjRJaxFpzJIZ|rAHCg~idiRM-5^qSkhED51Cw>1=xaxlS? zC#hnp!ta@i;?)K*^WTE!Yd!cu4eP(}`Gq`v>>C>8%A?bG_IvDw;-`PqgZ{$FX{wRX zobVjGv_ZWW?S!5_nsSkk?|1I`WRN`vuZiJ9rm?Q&f#snBHvlfSuSfNp;m7g3iYUxP zt5K0Bl=&+tsA83x(wZJ%r@ObcwZQE9dkW9c?LcBC{bjdUT@fqO%-U+a)jLqop7L1yMlro^KK z*ne6#I&GyN!U9`8@_%`Gwf(ZtAJlA$u8fAyuV_T~haPkrZNUC|dTbb>FX84^(#8P? zf&Pu@pUv4V79f#{+DjvG(cWYvrY35LLSp2FKnOgAVTb)3F!(8yd=Z0@kt}mQX7e77S z7ApGXWCk}v$e-5^!FP}W@`X)B{8{XVL4GAXS#)g2xc){k(jr!P70MU)#+yODp_c00 zZJSqOrmIaD<@nufyHcvweU}ToiFBIOv^3`ttd4cAUKX{1#skxO9)$WdtW(9@Kt?jt ztDxN~sZ>Q7EY9?!&-$1CMt5R-<@rDq!+wA>e5RL)lChx+=c!6U$|Zr2kF=7Khf`z# zJP?)k@RG$O=HhVj>I_yY6q6Yfv|7m<%Ya_jN|!Q*w83%$u%)4HMcS4PZsM%05K9e& zAAxLkTuYpD)gL*|M+s~@cK_4`I~aa(ki zq;a-r-G?b?HMLh~Q!DAJ)rV{vA=I16(7KmKt`8bwscg~Epd-WJF}vdsn{&n4 zZ`@Hf9qohDe>T&gf)qZ}A>&DPj3)ppN!Ee1mTR<-@8rGaI_BONfh0*jNT4Uibt_Ay zlo`y8b46l%Q+-drzlNN{)I)jEmGL(=P_?E&8noHhaqc}g1Wwtm7_w&<&8Gd1s!H~! zIs9K8vBAI1ZWCC9N$RtNpNdry^?0lkipAl;95>9^LzMiE_o-c-CHBSns%9dGaVv5OGw&}p)EnNKOIie|-wj5oJMt^1PE2Z4}x9$64 z`oTw2P*h(X+_-~@VNR%%LlgGhUO)K>#G+8oj#>Uy&dIt+AJI(x*_ibOv^ad^7MjOx zCWx+%eL9&PX)~uR-pEp+yZu+x^rhFL?x2@4eyFgLmzY0`tp}!wRl3g`)q~7S{c|fV z^p~*56pMemq;pDq%QP6(D(DCOU`d+VG{~v$91s4S@`X56vUbQn(J4G3{Kr7=>EBl{_KH?4iq^{3OBEs`Tk}CS z2n^y0`BRd}U)6K}u>140+qt3$&l2zr!kw;K_=HA#`{Tav8J9# zJP0d;@TdBQuVKwj1t!p$h-4$nSr&wuzay5UmWT7H7HG%2wnoiqqe^#L1D zb&L{r!fi&p89huZlsnp%aAzbV%F3OLf2H)Ij6`Hld%!j5D?hkLOvaOR+?8mD`d?Dc zt#|~_2hz#8XL&8xi=)?)qoz{`tYOs zt6|mH*qElb2#^eAIi3Y7;_^|#o|LsDynHc)&bYTa*b-D~K3B#(t8_Q|ho#CQo`~-7fY0>?GutbZ&kJPH+9ig)tSFas9kO|h8522c@-)WPv zHt;a!J*@%F8&a+!G9q=y@N2r7t|&)06H$U=%LgX29zc~UA>A6oJ;VU|JI!N{xbw&Z zSRfCTut1}ic7i6)c|#Tgh-|fQdXN0pR;Y17I+O89*0$N^Gx2j^%-%)v9V~O>+&jsW ziq9O+7e@RG%kqBedL7%{(_mSIqu6BWVs4`7%V5&n2^o?rU3EvEr9Yh(=#M9A*|B0p z#B2GMB9#rl@2Nt}m7M5s!uaakPFlW@&UO8(N+#Qys^8LUY&Op2irJ?^wYrKC_xU!f z#o)5#87i;K&zI<8LX(C!Yp$l76DG4&OXWQ6vR7U;Gi1mw4 z|1yANLrQ%wdGK2xk*CA5S>OvsW~%6YG&_^hL!8Czf(uEZ!8^|QjFok!4+-(VBm}i@ zearWM-ig8UZ5HPSS->vXXJiv;p+R zjtdh*AJYhUzSi<#1{9Hb$AuqE%e*dN<7b2)B!&kYNK&!<&rJUn?Y}bpC)Edv|Az5_ z;(x*TK=I!&{_ha=&4BoSbab4K!kKwN@=QfKeT-8B(wb3SuQvdo%=+}Sak&em}A z34n#nQV^Lg8z}XQ@DscE6t9-3+LZ_tV3hF^HJq6dlVGjp-_z${|X{m_ZPc* zc*4{eGCJVCe|9l9XTsat2|+%-hW{JS@YFCt8)|H&_Io#}ika>k|40REhMItYZ%996 z<_LL2c|W3(LiBF?%iD!+2&7)mP)(%yTh?dHpc^~dsC-5%HW?~+B}boPoA!af;TxGF*ljA!Z0 zCS%P@Yb|S_XJwT+yJ=k}l^Vn?)am6g2_Ko|)Olm;OjxDnMEpfayMJRjJrgxWXYyz9 z1TkPUj@0*Rd3_rXWxq}FyPJ+63lN`EP{@TM85Uwrq1nBu)QaU$oH}FbSsICJ*R)C+ z_yg7HaRx?uyG$35b!878a;AI$+0{d`6csHezG;qoW}DRuY^yD#lwz($l&E?7I05r- z$A+v1kCWD4a`?xx2f)S*$knp{0OhT=ybM3Y3>x7~${+sU4hu1ySyhvB?^DHLa`C@yj)o?x&WGIm_$R>6pKz0r{dv`K zKK!?H=4)$=PE_dIto!OsLYju9WCbO_ib%A#p~Ng$IF41i~kG_qLjsPNVxim zS37-~JE$5KoAU&xqxF6D`TO=RF`YWrT@U*3J5DQ#v8u?TqA7j4P<3%dH38Jgn>?}Y zdsV7yu%&uYD4uI!HFT7jKqL;i&kfUt^c~c0H0my6r|?u?q8t7>?}t~OE!T8C?q!O+ zY!YhZ78oo9*z_)SW7gJHnpj{klC2WwYLfC~_KG6!+170s0=)Bh{1Z%-_uWRvAttMx zb>z{Mjl8F|^>-UN$hI+%6C&&85MA{!?0F{M`Pw+CiJ?MxH5R8=H@pfxL^_TlH%c zFX3EU8OfqhBIMTuYLPM(pPchC?1htRlheOC(d8Pui7PfEe}4KFJdOo8<;nGD@kd5U ze)oH{)dxq$dQ`GwXhnVQ~!-U{FRPb7h>A%tyqU`%3ux7^c~`ZM^J25a)on| zW+hC*cc#zeDMibpEEmcg_sI5X+WBS;>{M(7b!gSJ{rdR8ybP`TsA`yAOqIHlkJQcd zv)dkpN4$QcI&s{@1jS29%YEmYnyC>JzPqt4MLqGLelnv4Qw)nNjw`$#x#2NJ6YvB)EiRrnNPvM6Jo-WVli0Z$?DsqrC-V4up5K((D(sBD0u3mLC%6bb$=f&zy^1VsYJ<1e1?H!5&cU$?EXi5t{b zu!1IZRy(1VK>@a_>p`JXa&~@NkaC0g>M9((aL`+{D#f7gR_goXwBKjsHYnJsxA+tWI7==Rc zD%&=tsKswQH+8fe12KT`)rWy`5-I|w?E4k{;6i%ZAAKZL9fcI1=PG6 zTmsqO=Bj63z=F_mf8+;Tzf1wpG{q&gi)r8eYK=RK3r~NXiG_tm2*M8P3#QS+dN(bC z)regi$A^f#L4WIdJgTVidoBVKJZB>F|G|MwDqm+P{#z*rpXmnaYJg*xdB4ji^dr@N zuxjn_VpD4h*2?$*piZ3gouN_Zh_=t9OQ_Txxg)Zo7&pv>jb6IbhNkovvcs)AO)b2~ zte3L2oixyJeLJ3?WrCwR0h7qJi$d5l*O(b~&A&M`Zh!{PqaZ!#2GOHhBmSXyY7nIB zSPd4pgr-6kev-tOdN?RF5WxUB{kons0y07UQRYg9Iuk8If+!H;M&V%p6vTmy`-l5R zAc1NNd$S!@#s`{lXRF2G67*^Q=2)5Prd)IUmJNW+l6fX8sXrSCmlC3!Smlt$Gk5}8 z&V>J23(BGu^KflWF*6CCJ@RR_I~NU=mQ_VpvMP(=IPGEO$X2?KgT;S0vnxJd4h`qh zNZ^z_9ZG~Rd77lv*_*33v^}KIA7`$eBJ^hGnmFCqg&{+47AHS45shB#>7)Xe-LfNl~n6r1wq}n_4}d3CF>TeB00< zs4_qgts5_!t|;2+u>I7HXgGh9jE942JRPx~=FeQ#z-S)oT3l5&Yu}fHSTZ?s{XnHg zIxusEXH_uGvPT1ti7J80C|c|>Chcb<+;O5wX{30#!A(`E6`#5t_|nDN{`@L}2wrnw z_ao9bVb@mSDiIU($^OHhwBv>3HECQy#l3-=BnGC!GSdjN;5KXsJ zBHKPx@m#bgJ`ZKNi|!bNejbkuD|rAfxudeWPKCR%DU&C57jEiNt<--%tR3>}CsteOm%e4@NcJW7_oShX75g^H1T3 zTqzfE*?G-nT!vOo+O3ptz{2$;f)Lf}=o!I3BEvW>eSD=ex!~PIMUG?+ao-qk7ZVtI z95&G2&EG+gYLkY%W#ezpbFhCwRyFUMcPtnDuNPD|6KpM<_o|6ur%DrVABWx? zS%vY$2VXY`;5%6c&w5ci!ryyVd+1|(+g6*JI9KKfp^pDV=Z+m*k~&H&ot*rzI)Z-7 zI)sKnGeP~`P~+wpC!jWZecw>Je}mOQxb@4u{a1f*ER$)nub&>v@%ZSb!&poH*S}At z77i$?Bc3uxUcN5Hp@XNISV(&vm4B(Ke6CjF=X`Fd=MfD4JXPs^0hx@=%(!Lt+cloj z{`SRd2@#|tWOU%Brf%FvKSj&z6uD?2&HJhuSi5NSrJ}#8%z11OvV}G;e}T;S-*W+) zLi^dbDK#4;$^%Ug*zgn@&@IqdL`ko5=H{U_n;^o7O#QJEkYLms=|5zg$kJz*I%!>?8EpHVqfc`a;1 zw1{e1j_2^rbj?vy5OVyiTtQ{wOSyj2Jy}D#pP4=S?{s&b8f4cCYJ?xhL>fxdJP6xM zpHZ$u99n*<`7%i?G~=K}3lW@UK+gV7*l+L2^C};3xqS7idnZE?CPI!rWjdh)f4a)7 z_3|*IF1o>i5F(F$3@l}hi0v+Vf4@0$&D_>bG8Oh7a+!vYw7}{IBd;P~tk$MBTKePr zWt-O2np0Thv=c@v`tDYg009+dD3ei7vAxalh`yQA)B}tWZK6S-6D>tWq{+Z z_&Hu6|3Fm@vDG2XVKGGmPlwE}*YccMtDDAYD{3rbi;z;|+&61c{}h>4Hc<}$)!51{ z&oo`)HT&^RrjwNSX)h^G^4I zqwuq!@BNY_px80?qd7v{(l3s2RxK=D$eIu#^7}wC@Ax}pmJ1H2+0ylw&G@dTyN+LT zvMVHhE%&seOYSoMac`$2Ij5`X_?VCd)4*dKr-+CgY)GMpaF`M) z`^DYX+tctT&&K$J#24Mq(TRv;)ihJPkNZNifw+(nb2j1*&vJyKH2MIOtb)(zgV`{qsF+)MS$%&|m^(+WtCz$j2B5axHXHc9 zz5O!je4lxz7{%200lx3Q-mkywO+BXr6jH{Ix?ei^x0KO4X@Oh^<4WuAFXw*@E-LQ7 zKmZD>wCtl%FZw904eO;}8E&bLQ{$&Kd2&b~A|D}Z-ioTPv_?-1-YbgMS6e;dc~b|* z1~xZS6_Z4(x8M2aOvfAMxTV#GLT|~Q+b7oF>8dndp+-+?=${VXSNN*j30(gYUwR1Q z5H6F>clIPnA%Ci?U(XFz2F{rl92?^Ja}j7#rVbkGa#vOpxRegceT-C#?h4p-iznXK zTx8!lb@nB{a}q??>s1Qe^pPx#UW~Ve@FJ|IX`%lBuAI5JK~&=$`O;-tS04j3Le{}h zcF}l>?QX8R5DHMwi4sGgWZ(WTAyX3q*ZQWj@sOtKHS+e?gmM?EF#YU5Zk}EI`J*f> zLJj)2n)0UQp+LaVuBi}1<%;Dj;%u28YlwO*L0VU6 z@;k+qhsmh9=l~y@mqT~#*Votk_gKy)wfNmF{aSB$e7BRkpZVW7`ETy2s%4`Xu(&En zkE9Ca?;>O2umPceO<3mt7AR}VN=LijB>Bc2_R^OBg+aZGac_ zi>jVVfhZ)OjCqpjb{;9*EKIzw{{}PniI9k^fM;2xaas9L3Q+o^^mX|g8CX}ufgX;$ z6dNJ@TIMo`u}v-nw2~qGOv!c?)^!8H`c&z7O{~$SebI%0ws|b$s-@$px zhiY%UGv(U(8ic!OnVc`@U(VsSVmC(fxIu9=B|LMy($WYzmybL3flnfovv5oSeC#ir91j;lJf{Y5off_ZNegetYv2hk|9H_Ei83~aQPW3#L zqxPG>YJ0O10U>XSJv?M*bl{AqOk^)Ak*_Au-x&!^ssdvDyafsBqv{koRu)e;wxAD8 z5y5GsfWlWagr5Y&lKP_DL;PDDVhdy27czHG+Z%s`f zTGx$#v>({1n_qRt+iW74ZDg0L8lR0(3Jv+rNT?m5jdN#{CDhey2suL3DJ8$nJFSI~ zHj1){ISvNz5XZ)xvS{-!5N}C{nLAWP#R|)XONX*qdrVi|_tE;D~2(KX>?#y{l#| zFKjkd!Gvkv@Dm=Z#+&|0*d_AyFjW&75}w|iyIb5~71uQc)vW@(PA`Q;H5h% zR??B(DnboVbfRJYT=1}eyG!bg#!Y40Sp66C!{bQ;aeMn3zJ)baa2;mGE}9}cNwqkb zM&bhu*J+L-_mXlgH0 zu)|ggSP2$eAF>n0F&2xBcC60By#_eCEe<7IOcLOs;WNU^ndf_k_JO5qxr)6Y+EE|n zl>?#P_JxP!x38U(PoIjS7@wD^ou@+>e>&T_S~)qflRrWOYK(bYeoQzuG8kp`9M&R- zGghVdrKY=frHgAfKzOFaYg^mtmD~kEIww$nFE&Im0KVOGY?{hn?V0hQA$cF-UHKT9 zIkNqI6J7+(v$%C4-l`P^_2aILtP?%^HGa%QAq4}92c`zYv2>9AMF{*su%nx?0dkm^H%^OB$k3OqiHD$1bMz~BWAPVi)0iQ2>Md!7K>`< zo-lfXyrqFo>zZClGj`vyH1_s4ya07pM>4fq8mGPKXPT4czFU$N?(bCyGP<9N1p~bu zVO%}vSi^l~>cV=|i3s#M7Emmu;mg$ELjk(@dZeer!u$7tqOP7ra&Q+mn##h#YmS9l z+;g9C`g0`NKMgk&?qRL0-`i@kIH}@7?LSRRCeMC9y(NSgOd6SM8CF910J1X{d3Q$G zL6vF+bkzT_k60)Oh0y?>Ri2FM_`O2-0O`j#KMq<&thl=nnK%hL>Xp=_9N1HqZw(Gs zQ1Np{nBu2C)Gt@RmrEn1ZRZZrHW`CPvYN8OO!D~tczy4t)k_1RNOGAd zD|5hyet!+(CA>usWF%mcRFc3Mt9Q&2^{0ZC$_-|WaUtgD@I71YT&hacx6l6*# z5;1g99@GE{<9?W>YAR0p45R3DyOH;eW;Y5(0L4nKz4x=B|2GG#`WzxNUm(vvK@L!N zn`=kPYwpDR^SO+-8z5R5kJ%wsL6B1`J59f4c!a%k@jzUU%dDr#amJ4x#H3PZK&Mie z=7mb512B4atd*kE_n0N(LI)bi9GJ>pl}k>{iZKt+#K`0(UpZGK5qe%Tl8uyde=EgQ z^edEP3^oTCR1^S+we`%=5|Pc!mpM`gI<713Mc$Wx5tnwq-HggI&)2PSwY0S2ekERI zXHJ}~sk=Ob>({ir8R6`Q6W~Y9?qtJ}?w;hgUbcql9E5}PepRs$R*I)$w%MMH=8e>{ zl18y?PO*rl(vdUdInH;j;ZNoFk_mJk^kq96c0~L{si&l?haB$A6h2(qr8{J<+BsWE zzz>lLLBlk42#}}6XD@T%4(yxOc3YC9HrwZ!(_hXPWGWkLztW3KT|8~!--494#`uZ2 zQ|rTcT&;)~=A*M?(BN#8P5foK-~;l@va=z5mka2B@0A14k#$lCU>Jb-WGqp8t%)g( zb(xcOmS3z;1`Rou6%iNkVAUA}CI;9*@o|k(3n(%Rg!@EBE02?(*g!0@H%Eiuo}Mdp z$}A_QM02c3Z4F;QSpvSy=#i|60XSt@oB2=U=d6hGl^#($OrdMSre9Bo`{%iN2Cp%~ zuTmL!nl1;MvPFUmtfX#>L{pt1#*WAHUQkP$o>5oGy7R_px;`cLB5&yQt@9==EpVE; zpJSP5!D#S-z8ID8Nrka6~hb5_oxEqul;_Eu2X3ntl&4_g@ zv-8t)N^ZryW%w|tzE2@}ht3Z-q42Bk%Sv$z>+fEJXwmSM@jtcnZN`Fi;KTIAJ<(<- z+#0phRo~pi=z``5KCMl*2b%0S7j4*`FSa$aG+Z&XbG;#a(9*xfUKiyYQ3 zB_Zl~*R9@GIY;?(M;**>z-}JvJj*37WQ`%jWBHqM;g5ysgio`yGjnc&fM=bwH;(o& z>R;9&pZINMUA1=Q@=LlmEx2qxPL(-1&&FZNv1j9*-5TV}u0TG@j-EJIt2pddbje#v zYsf)9_}(-E{jmq*|3wh||Ij7>_hWZlWC%ixdw4i=1S`nh9vwrUfDYW{{VqgNAV;w^ zf|+m;^?$B{{_nG;kpT9932HJf&YJ=(};|Hz62B~7d9wFvK_-cE&q<$V<)r@&qwZgMP z0A87VDTMaGGV`T#hv-w$zIVm;0rmGeqKwC{?hI<~xKgqzkEvz-D1y)+aRsa6D3%Qo zB+x-tf~_zipx2_vI=)dz0CZU@rb$V-EBB+kgTH(@L@ASbdRxE&Tj`K#g;hP<;1yWp zAtj%bBt4vG(`?1N8#a+7^Qp&p;?oAezrGl}zijaVARHgM(ZY?3 z9Ng47Dxk%(9mYGnE!fPJ{#+BN_(?l8pkXILm5;J3l?zo$=&%xwz`^!q{Bx&cU%Aci ztLB^@D%TBnZ$VWrV^DQazLxV(UZjED^pWdYBXQyHKyu%UTioW$6 zdko9@bF=x)OZ-5k-$NS9#V?VU>YofFv;5oy3RN`f+Yebbrj038ANpxM6U7rA!${Vy zsijIvw_}{Dfl2TYf4yVh-e2Y`bnYlu{Q@4^}`oDe;_1!)(5wFfdn`yO^rlP4`v9V&E z(@7N-Qix2qZgfSivG>(w*K^OdwS^`sq^j|A%{^|BS`Cwp#b;!&QE3+qb;Z_kjqc42 z-~oLtUC5;I_w$E9ulumjFooe@l|u!)p!XwO0sbfP;&tTq6XNqqZGeP<{gB~q1mb;! zAD&x%Eu_6Q&!xut5Ja}1YX2Q~t+m3Mb&|;Ta7jE{U;o=KY)B}A0LxohFb-2%C9N{Y zFb(YkgVM!FBdU5xWr<_3l2JuDS=70*TIf12TB;^pO_-2HJoUtLvhxqZkRx8nG93U3 zu@JfsNi<`KjynfuS60O8XBVfnjUVwE;jbtCUoPK&J(~N3jEkmVmxYgzmhNf~ z#RRxbb8&SEBxyzo2h6#r%_D&yq$7N~<> zzeRFjYbayev>ia%VSgMAM=tsH%I8b5J+>pi%OStb>!m1$lb^7fH7yaW^jmT8D5A1* z>&we7hv;1zQn@Rc0M#JzWu16(qjcD2MC}A*%Z9P(dwxi*#g~b%#s#)NJ!u)?tD1UT z7LDqo{7)R>GqrNrP3CP|7+hDm*|>$U3UW9q9}iDHV?~I3H%ir#lx0L^%=F%$wuI|F7w6Xu}XL8)B48;ZF>8( zSB7naCafZKOq%e1BH!0xA__?A@UDAso(-_4m?|CETn>wAWaDIC}AWWek*A4+tp z5Z))swME*5FXnt{vjXyDo$>9aVOKF~Cv`xyT3Z?X)++ggDt5H+#Sqe%6BDK%Iq$3! zCX&Ya)lNH-KWmg3SSkc&I}W2WoIPf4?WkLrFTtx1lf{km^1q83qw0QodDZqs|N z=N|rQSTcXA*WulZE&k-k*7QlFtUlM7k#0{8MLhE`G7<@-;*9|z$DnNAF>Kbq&~3A2 zCG`Qc`=YnU7rS_1K&S~3n$WMSD!I-a%D_#qfCF0wMo5<3ktru+LnZ{m_LcQ-iQhWXr(plLl4XsKMBSE<7UByM&hifqJ^=wO7kD5?!j@Qca~PTVH}sE6IU0kNm86+oGqp-u zv)hcAgDgY(s)@@+bhKo3&7I1mhx1mtR9^;NnRcZEJ}Yh+jeI$}iXnyq+Kx~!CQP&O z$EkK9v*`R1%eFp>z~bZdV*wO^0a#~vr&-%dwz0v=NR}bq=i*FsCv_`=whfctOBB^v zrq#frABc{kcUJkgl!I<9i7QXWyIrw(=lJl|FOKY`UY{ z#oa32$T%;UCEY-TrTCkU!dE{pW;p;65ng?c=FaxzD^&QEomR?kCWAQDCRILVA%I8~_?&nqly0qA8G+<^wKVB@>q#+R_Y5h0^Ct0rJTWsq_8RyrdCYX^v zZrlU)7saTpk$JKT2`08CLO-PVDO^}woEQ_}rtl=gbczoEU|)10-4~55|HKBqlTZs1 zf1URmC2OuR6Ke|$fO88Tcc7m97fPvC<+jq?N~dl~|O^_;8}h;TTMR$)?$ZGfAguK591X z`FV|h{i*SDF3ttCI=#5erYJYWCTy9jXn=sj?fAy(lmxh;g>g-%cHVz*<@7G--O?-cl}M+f{}Q*b3_dBn+sDLIZ}sghjXcsPwC3P@)**|~q|jobNcC%U%+ z)jkiw{{-KZ<~j=%)pA_{91!5nDZ(r8AHuV~|44*#U>74XgD$`jcxkW-=qcI@dXk8{K=xCXu) z$a(@yc_(RhW3V+xxHSho83YmWeq1_NyIq4Tzf{~prNm>y$4cRRw@wYp(cs+u!A@g1 zkjF#t^AnIXYxl_qHKOWV_MF`3zGixsMdTUM-BpGKP|L?Bk`Z-7rX-BuK4<%rV(aM8 z$!oYYpM8R718VwXV~!7zC^VQA+&@5%>^f0l&<(2ee>yo)ZJpqcCvNB^bA$uP-CXut zF+?eMr(l;_j+x>?6;cSN^<@kk3&)Rm0xUn_J4%d=#Il!}-HhyW+i&!y81v&uk6ok( z>|J;iTK0&9v=`EPyv3z=j0ZXC?J@Ad`%Or59h(B9DDw*2N5X~w`E<=*uzNY&_W~j$ z_i}T6wf(;2Tm4DT72un{eA`b_S)r zFzPKa!9sYi)A^Ii&d|F0R=FW6G#&aKU(6Hr;26*<$-E5sRei#%PVf|C5B7++ICs2o zjpxJg#9yOt@qoX73Wh^}H$1XO%Zw$fIOZg)GB!2;I#O#H1bM(mSKk^6$>V|=DkiJg zc{R&0(FJ4Lb+0* zU3jhV2k-xn0G9@6`3Nv1)xR{{w{7DvvS^>RuPqKQt=TvhkA8KIiNSg3W=M?Tml)rHu*X?hWGPtjp)E~|;6EHfR?y7H z3lZ4UAXE_)`W9Nc9oFC$M%Ky&JK7dDVH9i}k2UTzJ9>xVDY40l*iV3}$c{@9fyvA5 zp50wO+osg=j5o54S z{O<0qk~AKVS@-Ca+6YqdXthY(a`HK@&F&W}!>79G9O&;aq*joWwX zeOU<71Yl*q7QPVbdASE8D+Z#h&B0pjkMy|40AF||-7j~?t*pZaaJ8u%ZQGk!hn<^_ zDuI}5nzPy+EEkUAfh?$6852bZbQ2s`#v9i}(H>|i*BJEKffSrEV4FE$eyAV@TQJ#C zs|b}VEK~?6H3kqmDZA=_JcM1`g&`=Bfj(n6KilO{n8T4F#8tFB{TNcg0uH|l4%Xoi ztudS#24urgI1sMczK&B3Fjm&g*&m_>$7983r>7r-$9oyvStiuh>YFS^{6eIsb++^( zg4(G14aQYhlgb$FS~ch_t5^|(@nnl-Te9CmkQ0c&RPzwuAeOJPF{7H zs8a*_mjKz9<+pD?sPA1X=AZxi?mI|q{qno-eo@cc2#RB{W>^mdqeGj@eY6r@Q%CuL z_u+@pV6f0(Qm!xz0<08*!ElGEJ1~#QG3&|p`~yzwvKuOW0+LPe-xtK zm!WF5rds(>$?}O0&_iE~IMHTN)hjT?7Na2BboRrUIqi-YUIt{ZUB7wz9#xmq4j(l5 zu&Dq0i!V@p$U>5f*6WWPCvFKQBU4o}&FJ;ji-nNB)|)n(sLFXVhIGfhN5tq&HNQm; z$)|egMw4*DDa3Cl6q8)E4);>bXMm$ine>%y=tGBzF4Zfx)eUWRLv!4hCA4@_eer`j z?IFt??VKqlxwOYTo*C+9Wr*Xn7i=kX{TdM4@XF2Gcbn=$_KWX6E9;*WQ!ldkwkf_} z0#QFxb2n8^hccz17>LfxwQtTiKKR-ovwN+svtppQ_8V*RM60C1i_hm8|UH<0LrNhwQ97`;5p6 zR~r<)dgu1LPxQWf{RdeZ{ko*8va;3468xSna#2a zS)%j39IqFVeeL>vs6W&`k4J&rH{W-OeLLmTNn2Hw2*%CsF7gesXQ>~1rmJ!NHI>+F z4KLli{VoX_h%USVc}WSrFtV>*f2Xd|gr`fTT=Mh8{CoYL%j_0_k-b(nvv-J@CA#n` z8Cf-H@Z8KS5z#s6)rM+aeQ@_4F|$M$+A+9VgKWc_w;*XiM09aI*;i_Sg$V2?@6EgYl+3o{>R%l?vryvbYaK64iSr){n0x& zNY;SpViNo2QDU)v@XkGfoF1Z!7X4_<8;6S;Q(YDg5Bwo22rd==-;@h>f3<^%=sBC%D=%HElUUWzzV;ulUb3&B5d9lv2>{qP zUu$Tn7uX84jq3MZ$Vnu6fuCK%|GobDTTce8>K%Yj`-AHWwG>Seb4&ERX)XTy>%x^+ zu3l@X-zTUsv|wsq{?QMwkh4kjqIBhlZ@&Cm8Na6x-tO94FTeig8?RouA`nYU^t>#r naOKLYZ@l^X%Wqvf6W9L_wKC~G5yYD;00000NkvXXu0mjfD_TS< literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/starredwidget_preview.png b/android/app/src/main/res/drawable-nodpi/starredwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..dbe92bdd2aa51a8b46eae341e33d1ee1455bb105 GIT binary patch literal 18993 zcmZ^~bx<8a)98&uaCditySpX9Aps5qcXxMp5AMN&!@=G4;C^s-cjx1I-+SxU`_;XF z%x>-UOwY{jR84pPdLxt-Wl<0b5g{NTP`-bYQiXtk0zyDQ{y>2KtXYVa*ZzD6RhIuL zEpOrQ_Wp5m|Md9$`ttgAd~x;o^m_O7cK7gf_wamk_w@Ag`uzHK^YD3p`z)W=t%Hl* zqpPRqmyeH+_5HK$!^@!waDHjs0(kwi=Hui2vwT)QJ-=SwJRF|guJ4^czrMe`yx!hF zzQ4bJ_I-VOqve!&czAs2qI!IK*0T#UaEvf=in_YKJ->dqy}PGmm3Vl1W#W@NJ-gUH zJUPF-J~}xQl+_fM*Fnan+ul1|S=(CQ+LKi=U>8;jj7(l!-r$k==^Y#=uWs7d*7Nf6 zV(J=G@UPay)~jb=>~n$BbKrrINs_N35y>TS$+>fjtJ=m+aT(>7p7DAXZf(8eD%w`+ z`gZp2!4Yv8)s5ZBnT4*tky(GsN~;<=dIl|>{DG|le{!ow$7gJPk}`|yf7^t(hi3Rh z<`B^E2gUuJn4Yf!wwBa(x&)>DEpKV+8W|j$15GVq5wki5q-a_M!XS}-zPFF()3e%L+Grks0J~iC$oh(2#Lq(VdD=jj7?7-sjjFh z-+J7uWZw1pZ1u(d8^8$y0txd!1#)K1_Z0$y9OAo__)qucv+M`274?~m%=vm z4X+C{**FtAa%6k3Jbs1YMMosI7ZyWV;@^z3weggX*9qd9AB9XU{i@!bleY4R-H`5= zJ^H4K+S+a6ab%*QA3{(2CuDx_=OwanKZ)e!bEV2;<9wgb56HUS_eXZvT`#Ek&aak} z?0jE0WQD|OFuNT8)2NFU6Px3G`s4j^91G$?_+Qce-HYf)VW1HTUsJ3nMC*ZXaaQ33{K^Z9jPw#07Zy|@2X@Nj^0zV4#aSAwEFD#GWw zPlLMl>Nxp$@214g&*KG`)IXI~PsGUZ*%_+0TL7xTF8AYk7EQ_4&U`xJ4U4bFa-6ZI z+HOO{=SuZ15(_&nCE`f!YmbfjPgcG5x4o9*60*(?n);f>hYcD`1SaCA`=>e-5q9SI zh@)8xM3%0r(*f4W+>c4#CIeibKvBDRjcVNvZ`AW{x#aE~@8U^v@w?Zv8$3UsCO0t! zCa`-0Bz(Kivm;f{aqJOi#72#f&%NkIR}f(7xgp`7ZfUOQ+ui^OMcqa|cd{JTskFBJ zb?<~IJ*4?Wwzrh$C%Ut+SvQA|ulV3`*iiYbPTXSW8`88t-plP75{?`?+Zs~tJs8yG z+ZG8RzxPX6IvDHcSflFjyz|pwL+XE**R_aWO8rkAnL`(g8E)(3E36h;cZ|<-^gQOQ z@QlF6iC*ecUEHECOf^KfPrhMtzjJl}&2HGqu!Jj87990GPB~?<7yMbZo}LM=7CI>n z+qL1RHd*75f(yL#5cTDYJlL(Ll@daAVfhDk7^)5CQ2n^YclkLY)(sI8U#8i&KVt7f zaZJsy85)F#%>$0G@(+tsEUj=G2^zgk3pi9WCp;zkMrP#{av@~J9-TP(RRmWgq5wOPY zLu))_io4P=SFPLb;~{8x^pTb(5@p3^0sRtdp0mbruSEw%m2y;fRPLates@xi*VW!m zQv30KM&@VcnsVd>28@jx_NDHFXIDIcC%a^RFDqU6K>1v``Y)&2!}h{ml?iVbq96B7 z8@48<9_Zx;#c|mO&$vQCcF|pBx~pYQJpx-j!|Q`%*Te(A9?fmApvGc{a{O6!PmFhn z#O~pT?cc5?nzQL%l%(#nO>s{d?YPqCZf9j{n!j++r!w*b3(<8U*waSvk`A6B{X~<6ge5u)#_DU+dwfq4+$*w&-*c1l*kJE1h2?Wj_*=Gl{PHb!TvR);e1QtS&GOCDJI5_FNmr*?ClWwTa8f~KWq#6l@9m!RoiFg-XB&sw zB*bmARpdL~SSH}{=2_KnV0>KRdkPva@5b&p+kq^d&sL+if6VU?CiXk3X9;nQf2H8H zSJkvP-!mfX3LmrTzPqlEm#&ZJNm07t_xoJGw}%ZsJ(XgH<`$17KAtv-o~WpCCfT`x zab906j6qwLHH+gL#Vj2T7q=sL-&Ul!>Y%;hGTGLkW>A9@0oauHMo#>mD7lvI*R6if4aJxGe4e=yWVS>nQ zz4kAA>_W>9crM#pV77Nf1|qb@u{cjAu->rN$c0h+BK>czA|?yNDroRJ`JWua#1iVR zk%E=1@*9Bw{=XcNwdT^D1<~{13NcH46C#5Qup{w?nm&g>e4(!EDx7X4I-l#1-+LO;zI< z^&tP8pbO(%ikG6pwuW3xUbhGUo!^HIckQAe@nyfkN;@BN8v8GZ4=P2sXDD=eIfZp} z=vN`}=!tG8F=K;SB3QsH%&?Z=Fr#Azsn{m2n0}9?oh}nf*Py_>4g1ih+;l;MY=oPX z-pQ+_X{t-Y*7qCxo9$jK^GtW^4^=@&6NstI_`#8?#=uW37-;JbB=>K6rgi*sSRqXOOx<^ke}oxid6EL#2R^U1Q=z)7E83{G&9qLYl_0+ z#$JqYZV9nJLob#|={H?Mps(o2Y9Q-SrjFX%^MnX{_o=Giy=ZTwI$Ng(M(N8}jf9^W z?A_FBb>)@J67;3W89ac$BCw}g<%>${i7UhmUVyN3Mw&T=ckAxqbq zq(;@2tS3LAe}?mLZu=XQuA{)zon8)au{l?pn4TQMBwkJM&w2Ioep1zV4Gsn~fw`33 zK|753iI(1~t=XJ7=&te~`&t7O;F~hO{6Zb{T09EsyJ7u(q!ze;E&UCxD^l~YEYaVW zy(1Kc#hgb-GUfI3Nj*>)=`u$ zULB7W=W9sbUceG!XEP=MHkm6wE+QH$^nC8xddI1_WG)Es9N()J-TuseW^BS@{5qmB zEQkMPIY0N@0>?O+{i>t!Q+5Zk$d`DY<}vn94Hu(MZ(({Eo*op8+P@?{qxiN4SOgh> zQ_P`!KZgRd3%=o>}->ms#Wp*o>=V zDmoN6pGDdTU^V$H%d(8 zW!<%G`?dW07=&1=v(~e)5OF^|a!xJjdW24h+(t{Lj#9GqMM}Yr2W@sq@r5>A0iL<0 z49|j*v(w@~rBv zt~M_=t~#QT_zs?WSL9RC_qUB&jhD$^RNdVZJVHNM$e-W4?K7ROAfhI`AN=UOz(ac2Z7L zd-Y{O#-Beu&nRp4$ttT! zBFjQeRl6PJarRW6&1jQfoPw}m-brR@*b)hVa<|of?>k-CnkMIcT^}c(XQH{>X0mH3 z7y;TqNEt2wr^Z7*?-tQ$R^WxpVm5=znn6}ER^PCd8wiDZr6cGG-E{ALUSi`upQe3=M}l*p1WEK2KqJWB_d_6Bd?aN<;(&=zq&T zh4{e#x8%Qi6=%ld@bJ+Vhfzq~pCQo}!&60`s{QA7jIY_OLHHj5t}lCSH01wP`G2YS z&&dC4;Qx#~>?=Woi+jI&;ms!c)?Ei5Paz0kx^HA#H8lbF+M-Og9sjs`eMy|Sh1CQE z=yky=Uj)SEsh77{d?CCW6a?YzS4kPwW*p9J8w8D5s&G19ls*m=g>P6HY-?8=l=Gx> z5&bFE-mK&SD3rOK8aWGKOJQN#otT$jWTlOo32K+Dq70SEtM_Ha>^r^%y}hYP@HiF} zyuAgTH-=1K4QPXnd6SOfWcp^aBHMSebM4P=IcpwbcS1{I@WWE!59;Uwo9`P^50YY1 zm~HgxUjJfB95fx z2(O)6Jkp{WmgLRm8aKvvZ=MZ?BpVaSq`ojbI=j}inRMEk-1Xta1~_O=9kIm4-ct$a z48p!#?=t;ulP~?1Wm>pMmPLe&o2t}z*qpiymzkI;R<=HK%t5%PP3f?w^NbNce{}DJ znrbIluqfR~N7u1WNgaM8g?IEEO=!e{HBG|zN4Vr_>9@Y=cY&)lGFEdm)8XqmX?SBQ z!CtQD%q~8IHi$hsX1K+P2*r(tO^gx3B@(9QT6kdfv_3AfYV-U>(<2l++{ zNbWB6Kk_a#O#tWCCLSDfN3rt3bBNEjWa~3m1Ancs7(qy;dK2PvYu^m!m=Eiphs7u0 zC!oCf0QYFVO=AWd>0f4a+tAI9DCq=4OA~(G-L;-m`@o3| zM1YzbO7xT|q#LOcnBD(`+!O@rmdBjlUlCB0sa^Utw0>H)a7G7p6oQdfO+Rm$+dCG8 zXQ6@pQ(<`?CsJ;^l*C|PpPGiu5$t-|6r8bj0zg0(-YtfH72H;`ZdfqWpRAR6Sq>4~ z(`f4Mp#^Eg=eIFmscJz!WfAiiIC(^Kr4|Y)i|*@b22*6pkYRDS&XX|9vA~$yR?s;D z`NrDTA0!w?pTf-Mav_IobGOOpz95~=TY4vF2>)ZZIF9d*Ws4@83BvGp4;)TE^F&yu z^?xIjgn<;<7dLMJfE(ZbunB)tE7oKwJ#~CbG&a7Ya}(slGs`$fT+q{=m1VSOY_d!C zVZ%W<6FK*?qRvRCWhi=X_U+^tVG-b02dvmUvHB1_=CpL^Wj@*T*-N;E99mA*z)!}* z`y^Uu^76%O>&(hd=I~`U(xB-1@1uD@)H^S_S^A0gYG!Mtzx0gW{5Z(_82hO5rsY_j z7y67#JkkrTMKRV-^8064J_bZNMRGa@hiom~PW&94rlNR29hb8^1ZF)Jpq0EyU zk+=q$?SU@hL$mYt8o`z>84}>8Q<3PP2YTvchtM~>E2D$(ymzI;o(&3*BS4);qC@{O zpj46qoo;EijvXizthY=>V+3&y8LB&?b7q42jvi;;bh);9NNU4RH~iN?L1DsJAddiw zw5l@773^5kDW`;wds(>4IekGp^(caPanxH znd^^tUl|!=VFPH4#FEUQ*mq7y-~Iayw1Xk;)r9lszVjx+vKnNtLj)(giAblAS-SY3 zX4FQMw{g#DQe@l96wKz^n#`mgB~i5%?Kf`TLrY`ed5@bvPM>5XE{~p(;1>_u`pFxU z=%cA|O)T>=Z-6&fMoy2(le}1zhaeCjH)smxjes~~tH@vvM!8H(81Dx)ZHsYzwH3t) z%CYpza`i?C8pQC5A5S86Kw2Y>a+#Q3=?jr>?U;jZaZBU<$OoE~i%Wh<}C5VV4L= zm&C(8b1oFXnd1&NHSm-Z*y=`J_RDJqS7Ig%YlqiZA*Rmlf^6O=d5!4}NCG5$ z*T3~&e9zf{_C230V5qAYiCUZP;4 z+qqDA>Adp{3#5>jJJ2{Cesvo0S;ICU4Fc+D`9g8FJ$E|b0Fy3}T)EEXwh3=3Rio1< z*Y%@@E%Vl{XSl_234@+-eRZ75x}3eOjWLkUiI=V7DsF}mO;5(m4RrNE^4h$=;g?{y zafeBN%i>x)ivrsueFCUX`Poof{MD?51WQWK>B0{c*7q zra2V;nBd5^u`29FZJLdUQ+}!I64VoJM$mWv!$Hp`evekX?9DfmU_> z%>^1rYj4VVpgwdYk`=&1Bh@%jsUD7mmNl0ACKM_AM=+Toi%N1Of%(*E^YlW7G6Xr8 zX>;nhksj#aE$vGVR6b-S)OHZD_CN#=jH8!r(Jya>!x(WFH1&p zUc)G~y()jf(HL8r?%@c}br!c!y6Cm>s!q0pFFhVGWob^OV;d<1ewy9a|IocZyd#xf z@T`ZoKv6TbLu;dTTuIeBJE>ZBxo*uP_j=ND5E!S@inZ-(WJ`>ab9KAANC+aKVP=}U z*JYeMAF(RZIr-V<7QANYs0~X@S}V%6rN>=bfS|j0ZX8W8E3C4e6#c{A#?EeN=c+Et zWtoAdGkb38_VB3Gr%gTCj#IG%Huay9_M8fy&ybqVHdCSCxX*LG@ ziAFVA0eWN+dbEsDH^e<_!D>|Y8fvAmJ}pb3<%COnoA2KAb4gSr_7+QlZMFg{1gJ_r zNrRj>1X-4J7W(ntEDCTpN+0Py9oHrOqZpwV?00Z}H+qMkF;2^^g*dd5nF`N)p2N+{ z{`tC|Jcq5d9i$osNo6Zcr?*x9hK#+YVLXmFal;B}N?U_Nv!UNl_AbqnyDzJdjJiY- zG}gKd+a0?6^45V=wEu0X|Nk6>|6{}7#}#gPrNidj(ZBQ$cbcJWloQ=xR7+d*4gyYa zGl3&!dh{WBYg8uB%smySWMT#l`))q1!Ey6)W~4(VYKhlTe%@1cDeEYJy@4H379e?Qybkl}?Qc%brRq@U<~ z7s~=hvR>bJ(0E*z2xf);DVd&Q#MWgSj;*LqDuJvJU=0U;2=nq(cX}4#Bh8WSTcf&( zsgvufd5O|Ce^GZ0QOPClLXJ#t zzymg`@p5yE!t*r>nWHo`lVABVXiOH%NE32nIp5Oc}dU}U2 zkPWJsx=3*FyPt)xuXHPnpNmiHk@527^Nl*mj=47wY3%D_?V`JHRo;o*>Uig7*b$FA z+^Y0}HSN&VAecSEYUr{qXeUPf@khWc=Z%`N8N7Uz9U%D@u0f05GIiAEk1MO8#@xd|Vr1TGSxa=s5$9 z{Vq*BW_=z$(8#IL#rM6bhMyp3+t7{`?VN zQtuso8fkpUpdjY3ywh4aSfrCgUlpw_LV4@7UHhi$QXxdAKkLdIt3sB zO`;-g_8Q78CLMp*O0nm#ZyrZx;t4}XD%uzRYX0gA7!v>IggL7 zSv6pgh9mhXX^A3P7RXAg1-j)gp}s=8-r%Tb+Fyykz*^eg+a zgVY(M>=G~{;gdzE>u+RiaJ`;Ktct5q3$l@jy=jDegmnaqH#HUl32bb!S@6yc0Fm#t zmDZd((`i4HsrV!VspxTSiZlVZrP|;>tKwelrm3$AS#siQ^&>@!)2tfygqeRfT#*d| zhIH#wjc8OV2*1(e{*l%e(Wg-X>pJ2Y*^a2)Nd$Ow{Vcpmd@%$H!Dhp>Z#kqL;`@0pHTS1tG^Hg+lYv+fo7V#NNFAG+ z0xz;MDfrhG|AH)ADfBlt>E&{YXOF{|3%^!465o9rXa+Bre{-#!Ep_K)(@La(zq8kY(G34NNJbq0Y zz(2*FlRKWoE1(Var}y$v=Y*Nc*@U32uip0XP|&f) zze4$rB)>OH;=WQA@Eic`nt%a#wSmkXAav|YMwgRMWIQw4nA7OOlpd#u!jVcyzHW9$ z6T)Rk+T%;9>Mp>w@K;{;y{jH&zOcddn`yAD;?a5~zLPNKf|GtFmsw1sifcw%iY;8d2#94~S(>in6 z&Ua{8?Eap8Wx-!-Ul62uNF=?!&PhK)TX^OXZccV4{@kl+?YNysI2^h+lGrGE;_q22Wqd`$N-r0*q zn&d@avu9Uq)9h3JGQPLO`Y##E*|u9im(<<7hf(B^7|4JIGt6m>Gai4pz{Ovk(WD z2CeZUwl~{(cMmtCJO#cN=CKZm=^r6lSriH5G#7QX*ufpWC!9GpBW;#M=b0=$24F)7 zi2f+Sm2X>E{zH}dqO>OyKL{Dp4UW`+6NYyD3vg53nW>rm9KH@ZI=P~`m_Rn2?avk+ zrJkbTE3z05n;f5mX$cb7gQ{)-(_;!=#sAI)P-+h&@g!d2(5K5}`71cm7HjZ~Q6d{X z`y62;J&k}(wr>Q6mVwL#P%W5SXK-`Rm@GodkJyCxCEEIV^(X;iDz0VzO;BXMe!5Jk ztD6&QZPysn!CKhT)K#ry-}BuF}*2wv@nm6i;N4 zujUerhS|9kCq+bR=Kb888;(iTsj#-CyD_tlr;aEa_~R{1zT}!L$)gOEtI-u9_O*SK z9U02VxrIjdQklY5X{4BVXQNPmehiX}_jW3CYFCE?u`bIge9IJD`OUt~w?LkvC!Jo} zvrxBu2A8TSUSwU6`MRgppkKBTJa*@VfXAVKP@sX*uwXupDQreunvO3Ei=KkUJzS{6 z*Z?y?qRmgB<{k#%PGMLz$ET>?FrcXr9mJ;-b!zVnE-=OIS;jU3g=U zze^tY>sGDmz1yP(S}Uw0Fz)ngN3Og3I{BsepBbm@1Pjb$dYcWYc1JMDD^uCY;&=rXM zn#GQC_|!~%aJ}3^dDg{A!PhPoPY+@X33vrRoMTb1&pzjG;#@s6voh+q6oc)WZh8gPSIlvd&gSGhPC7&>`*t7zU z&!{W<4pf~E(O+1PshB;pnEjibmRe*Ql)H-N%fbr!xViz}IltpWqm@vdhlXJ%#LT#Z z%#xO!wlug=cu9JzNl{ogea*Pb6NK@NCE)N@Cy5)%q?PIyvl$qt^8L!f2QDacHh-Uq#|UkQRMB*Hgbkdyp?+2 zWv*95VxGKJwq0XvMk@x;)SraR9ByLhXIO(E#ygI&~h~zG02- zvOsI(UA=ZLwjzU7GHihSM`1w{?n$?StgwRP6#!%G*gKICsnC_0hO*4yuNglue%CVL z?$@vcWlt4;#LpFbM&m8c`XBv6L--fL))SS~Et@8hfI{n5skluEhw}w>TytAuIP(-P zzwqx>ji)N>k{ytRqd!Io$N@+%5Y0GRq9_@=i1byodDM(8Z55{&f^`)zi~PvJ7EwF+ z;7y#l&uLdjd7M5&SqL3xmDcL4{KlqO?{1?qQfd#7zhRNUa(nBeTuIp~En(F`&8&^J zP?K1*aOAd8CHcKlEIZni&yY`LFp4;pSk9khv^ki1uA2&XkPPipeoIO71W&0s7)o=nXHaU#o>uEDZ3j43KuaU_)^V5zXm#w&! zq;f}}_!4<_!ZGx!w3_pG(mNPZhY-NV6(x;@I48N<29Mi^xz`et;1#1 zx#JXB5`|`U<#gVfEa%81pyxT)KlED9`3nz9&yH7t2uu;d!Jx2E)v|0 z%W;ADAh|9xtX%Sa=(g`2)SiCLEXUU1?~;HsQ<6>IfVG$?iU2}?AknFX;C{mls^h$7 zmKIoi(9@;sP^g!#>*6ivx zXPHh6V8*2S7G(` zQ_XRL;V|q2#;A4BtEJ`W9t}|~cFNvu3T}MJs(LNE$B@-_Orqctfu{MlW%}a7udDX1 z|I%`R!$@w?cv{g~AD^ZiOo4-~bS6rH$_PIGZ^e^PjzXAaOTO}1?z7*rT&B+`Nc+(; zkRRoLt0{DCemY87hGCi3q7fxycm;ZTBgUvy)>k0mrq0USytJBn4;LaE2K3VLIs-B2 zByvcSl$GMxCvr86l<6+1qlmuf3f&H8W@U2nUzJ?l)?AJ!7x2hC=@N!bntZbl9bsi_ z_TE`fe}v^#JQ><{mf(f-^jv}~siVF`#6n0b4!}5<7*eXxuAticDU&)|FSc^)HdR@N zg#l`2?JYDMsr04cTyc_?F*9zK)gQRx4Fm7GVD*nj!hnS0-?pa;!E7%>G(|M>XmY zMHBHir##0;`ZdqqC1s2)IS5N zvA_0ZWfzF+nhxSgmI!IpL1fYjaaneC5pg2#*6f64j=qrmYBM;d$xkY~2n{;Onmj!i zSdzHQ|L`QV0tn!)YiT7vK{*HG|K!&HO{X6u)Hl=9NJHs?lez5d!q^l*yIc--VSEO4 z^@09d<^En)+7x#E|03c4MZ5n`Fzdfb_41mQjWaJ}IlE%|Y|D*k%Hk*La3@In9XM5bJZd9Zfm$&IsC8r(RQ08pcgA^>cjO~)2D`p}llrCFLN&IHG4@mKY33B_yWe;gv zclV4TcKQiv~9 z8+3KRFMms=ouAi|l0e~#iwzZ0*XP5yuHu($*r7H0VGcj(k-1M}-+R_Hw}qz8AHv?C z*G8VQ&rU1xnl@q&sk9GF>+I^w7Z(l%EKW4=W;gK?bXu1OvOqDzP^AY42WH$gn;|xr zhLu;%7T_nWD3A>`EhB``rpeb%O`32At7~9s%XBSHWM>&MxZ#~}^_Zo>i)^(vU&E~ux zx9?XxN$e4mM!Pw_yexOvXw^{tB0A3k91%W@%(e7jlMQlnGL}2oELxWE?=&ONK26l% zx>;0FFm{8VX2b8hzw;dcM$>C@iY8**71~cu$HG8%rP<1JJi;kX#S>$pH_Hr~J182& zRhz5z6MXKF5p#G%ev_v(>x2oEsrQHl~O#C2LTcKXzaMC-!8GvMg;AFvR`YX09$?t-ZeE_5GvQ zyX|>vlNj^nO>8hqB{jKHv!%y)u2$Wp9`$6?+qL=!TXv~^(^a+`@Me7U<`1`R=}xf$ zm+|o~IFx3$v#rzC6eZQ1&ZT#=i#jNuc$6zR{>%E&;3pk*1e@%1>Wapcj)~+#FZ?By zu1_c!>r`y>LiHv!NBZR8#K8S3T_P}iv9oM*`|(qRx?hdU&nX$1&~kNL8@G0U^@XKH zi=6DBKB2x++I*u~hxIQ9$iZRME`Arr zD?+*t7|LsbAGDhrBIM<>7j}ZgB_SQ0l-^U@_pmMPaHjc ztc|xuDtpk;{0W+3cIR@0EidfwaZma=s?zw1=h;YOpJz^f9Q7b)!>-@J#o+qRT(gRDpcI%$|5m!He1+D%zh zf5ekiTh!n^;A7WOJ5APg#gHRCs{m>o|KGFc83J*J&n@OR_rh6}K5F=(L5F}nEvc3H ze{Gjch0$3x?5m>xdNLlUVbP+Zicmr&L*w{T-E_k-#ciFoiswMbL=u_ zkT;CjKeZ^GhwLQd^`-hXL$}Q&->NOg9F5q&;y%3Upz=2)-%V<(Pb2YG&=04r*4YjW zW1cw2#rW~5-@W%IEDsnJ)`qoMQTifba2nyk-RU9JX^n8TA20wImwH8=WE=Or%+0oQ z$3KpQkBkamJed)BX3!m?nMh~3<8O5ONnf?saH;JklS*D%ld|9{PE&qdIq?lW-5*(W zb;tX-yW(CyFZ`5gyLgoP4}o+IK^!{N*QlO7y7i2#sQPp{fSV4-mXU5gjpZQ^jYdd; zFTFhru~FLAZ_DtENLzC$khvXSfK|}QgO;8?3J|;+@F=bSpqeKSGOAcO3d=EXszz`i zMv%c_1uhNj@=j^}4mBDtQp1ES2OMy?bzmqnkXYe52}&F68a}v=-xx(l$e+?nF@dId zwlZjVU3Ztf3V_hHJ&7DT8;C0GPXM&ML{^>J+QbynnoUvETOvE5|DLdS8JS(js6Q-1 zZIk7iBn5c%zHOCb7@%I9kd?yq#D@rb`_8#=d1!7HBxdfsyMwKFF8~}~R~F|XohD7c zDi|doBay049bqJZh6J{H+PJNMrXnt~c?5bu*8`WU?3hZ#i^xG#3(w4SLq_g{#6e-y zRUfUQ6ZRCqs}5wFk=5t$;XqgCR-d}WTVC-KjTrn`XDx&s*zFaaP^R{|hsNmJAED13 zRnwdHnwy*lL7{-X29ea%fr^T4jp&2{IKwmXuDp!^yXSdSuG(NrNp+e zR3A-WYYx?K7TtK8-OJWVzjrY+>|lq^*FP7w3UP@nGfL!BlE?aJ-tH1QTwL6F3I#l9 z+i~mILC^_4drQKgEkr1^dXd@lo?T!)b^f;~UdSzp+uQI>-0!F_ru(KS5yX8kw@DM1 z5V9^-PXYy;9AkIa&k<)V4;-JB0ha|CmP3M;h50fD2SMg%`@&6R~+{c8L@(@naRHf@$)60oiEzF4*2d^7|0)Bby$sc^{`LZdMxbW`dFhZT?KuDrnu0HZJ+_OSV}& z7sq)ch^h{0h-n~vWn&NIMd!_{SnaMuD>A|s#blDC7lAd~;x?Cvv*xenZ-0j!r-FF@ z{)Sxk2t5r-JbJBf#B%$Jnrqb7m;}^RRXlY546U%v{kiFD9ncr8-TDIa>gCl6>LhsJ zm5^|~xI0;U54d+hX>~i39{gT~>>D;q!wGwKvKUXfG}x(GJY95tN9rvsfLCm?&+D{nvr*GRceN+pd)#<9$`m(#US0Wkc~{Cu(CF5FbEOV&$!ffN_KkWiVxg1xJ&;v z^CCpyc6RL@R;!m)y|A`?j5;}#AOBp@)i5v0TP!oBH&(+*$e)i-Y|nX2XA!V)=Id6j z?Zl8;AVSvL^j_*wRg%}TK-My-{^~rpq3&Q-@SZOAu^DtRn*&X&HOKa4y`ENtP~{k_ zKR!^`vY^nUO;o_hr-d}l$zMk?#G%CJ6+R=M9F^{iIKCa8hqZ%9-o&s$(ha_67Q4uBJme2OTFd>3>(8IbYiR@1OjCF%jT^MZ zRK~@*m=Py!dL7N+=8P+YI#H%*N2RfBhjV-$6exqB-VKurOt%bnsvcI~9npIs4rKNJ zk@d*k^FLA_<>k@7fZI&&v0k4$NR=Lr=zyJ~Mc$uQELu?PBuDMDV0Uj0R`9Qlg9c!E z+lF`an%_pOOmA4G$micWWLPI>_hEXa`FlpgEQ{CL!=(pZEq|dwXOkp~+y-TWl;-ET z*_&~hHn!4A5i5`&tDjr^lEdXOt2pK!m_Uyacr7ISe?PxI;{!paFflv@M)SyZqG9m! zyT5tY;^_|C&!P}L7vTEKLN7}#1-&=6Me|u)xSG;qH*m5o*s|AB^&T3^)=#_HpNcw) z7q#t(s>Uo$8TUlO)%BbqoOBngNrqB)Nmfw-0rfN6nu%Uo;!r}{Ve3aPXz|Gxp%uIH z=tkt(ylYKg@s5uOfbHNWLl*>8->X91{`&FK_Q*V5Z$|ov8XB?^QF)z*C?Jz3)WVa; z-*Fo43rSyIC9Oo1eaf9<$6s%->|R#JG)j$C^p<&coiKxVgO&_ju5Fnb7 zRf$*e2le`%_xq7L zX6lVNa(E8i=%mPZtvJrAcT`+BLt~ z$cPur2vLb)06i$5`0wd)m4nYFJbCx${Rq^<><9eL48ah zWaw#i&p_i%lq+}Wn{dbU9pm(;B6CI-!3Ps5ooGn#86ev~u_3vbAr#l~bvlxJIo9F* zncR0B4>J*Jc}l%rv>Sy;2$7}ys)0Plt{Hh+@eI=pi(6+JNJm`7qs{H4zF^*1Hsqyf zi^*&EjfqV*(a>Z_8iDN!dF;pRS>)}m| zMHMyH-rnVEPNq?s^1U2~-O|W^(&z$0QW=?VX}z+`t{?Bd0v^eC%bpx-GYXTgFc@{E zHr)f6e*d5`#aqjU6Z^*HuAt20hJs$@xD-s%TqcaP!=+8(jm?j#fM-xP1H$&}JGJ%cN9BevfON!DxXByvn1kBHl>y$40z~iFd3Yzs)}tHx72m zG3h}c6}55Yttb&;*s}d7GywnWk>Z$X6G-(4|KRF+1Uyg=)I^ zDhU+Bt(^4=dnerWc*w0S!z6%T?N73CD}c}Ty6-VE`~7dSi0vVvo_bI&?4JP@>=wbM*t_^)0i*?K&yoCIqi!bsnlG_@!ejV>ZQGN)oP>oziH zijw>bCWIRvE7e^0w{OaW4JEH)+G#%_0BrdNn8Hjkl3o>4X$j|ZDZ(x#PtBCzt$gzD zO+UZ$XQ{O9avN@|-}1sya0>DMEtxHA>4mH{>e*0B(SC&iY;M?1vKv=Z5h?wR)%haD zLz0*75Xn@#Uj$JlV>zZ~tnK?ehV+ZUltQs-uD7K#$k99lv0yi!AzN1cwq(lCxJXDo z3E!=RNT!`zoJr3-(nFryq6~ADO;!c*S-oGyvieX~>buS#!YvEPt=6CF#Sg#{X0{)O z;9!Ri`19tX#89*E-5bjAR<@}Yj@=fSAdq8#bIODUX}?bQZ_im8kau#=yRVXCer-q; zn%1wj5(a9%PpT!uqmxl+x>#;eyJq z`Bdt!Z;dn-w&N&g@D#Q3jD%kSww%V*G%dzN$2&*6i6jl1!jH=h(n#8;u@r!5DAPSwO#MM_@4? z_Mt9;v@Lht8CKYeg&^?ACyS?6K`H;dkOGW|#;Z$D86^r~x&CWV15YEF&^&kmm`rIk z0fOEK>B{oxNhQ~XSA=zj)2cVpB+LI$W}CpT$c$UbcNKHg#!z3vQ@$#rtMhUxtl+4Y z*M|f%nVM~}1S}#9x5gbshofM4PaDN~1Fg zOsblsK)PQU*idY(x-MQC5N0N=Hxg_bYMv z@A)}fW+O(%TPAb6o!FC`Z@txw!wY!vH2bmU4~>>KtXcrbGl}Li20_{j04Jpv9pg&l z2#w`-RP7IMNB~4nGBiJm+K_%=#hZJmwK*x+D6uAGGbB@b@=v+k`)l)QsH7T!S_>5T znZtOC0=d{qBVg;whcr2GGi^2+l5$0e$w{Mv$(#zzB*9X^1tGU0vEm606r-bdTRcI} zq5#go08C9bl6PAJt$&Vc@5fil6%i}>0K`%x+ctyRTiA528A2;oVZndKb0{rQzEz^? ziQj8Ix{!d`g3s>@1Febl8ld2twLtj)u2f3Rakzef+iBxbE2L6xNMn;eG7xR%GaBwD zd%r%%saO#BKa#Dqb>|;$7Kwp+rLAWa5lj}$;>&gNV-b`v{IwkE%^RfU32LNzTB^1XPGav( z&rPXV)l%Cr6C&%T_tpLM&3*m<|DWgG|HborzF&3_oLNkM^gWTnLHi1O%69e5i-_*j9FC%#C;Bl_X{s)o`y3 zq0uZ0Wm~~tqW?;m%umQ>U&GXdd|mdG!3Ow3a31S zss0a|)wt(eazx$wwtcIa@Kz;XBj2IIbjP(B*b|<At=OdM!&Kq+hZYHNC)Rf0F5rk#^0D;T&M9qw_Yaget0D1RL-}B!Blil#v zgc*H5)V^HJaP}baa#ejRP(%T9fuBIIK`wP45eis#EmxQ1OyaH7{A=5%YaupTiex#}@t_*i|SVyW!i&L3xI8$ z^H2cRRY{tcdnK1M=*6T+-H8uLw&3z1kgK_S#cpmS)-wmP@%WWfEpkc7&o}T)Y_P1~ zlcmy0+fRzIBMzf|=c@eA^b98qf*4_E=Lcp{~lYrrUS!wEvGg`eb4_&X(h?de0b zJey`5GwlFI0q1qkChJ4BlvQ2CXItB)tby}_8Hwwwxs2fHoUKZy^IP{;9pKGs6S)8e z7drOGrVRK@CQ}Ag(ij<3R&v%WKU{}1T_&SG>w)&egdyOmv6%;WJ@j1rZ|EPvQ%%dI z0|8UE1a38%qPIhs9A5)8407G(oMJjRNF|cH|?$V&~05uy+ArtF0tXx=3Ogw({9_HO)_F2c{K$d_e3CUrr1__ zd|v6B=U(^J#oMB4=G4QLwWWmenUxZ;h}Y<61s~uFVu|MCyJi0r3IEJ7u- zspdgWJz$lnC5eI1u?(iG#mJPP zm0G__Yrj&rOg0@3{cP!Ggx@2w}KB2ut+=9p#n=t(yOL1N#(o7k|2H@L zdG^c0|AU@(IC(n@K+98m~MfZkjr!n^b)ey)Q(=fMe40%x-O#l0GanWsOpeMu@*6xo`!|A*Y zCTWIsxT6QV&}e%+HBhUNr$>VfXb_P$;&dP7hN10c5T;Y0F#tv|?Ro_Q)v1WX>v45) z4!XXt|H+W5qKZ1=BNQ9IvL>qpG0x&d6P<=nA8E8mVVlCGKLB z0iJP)kXTQ2*q7Oyy3Ss$vsVXEn#5hnr}Fi5h=(cEz4ctZb$$|V@GFa~YF#78J2tO} z3~(DP;LBnjFyQ=4_mjtJk+g9k7A00Bw}0{{R3O!wwD00048P)t-sOlfiM z^7i`t{qghn@bUNi{Qdg;{qgbi`~3a%_xbw!{rLL)`~3av@b>TW_UP^N;OFq-=kVj` z@aOFFzQM+>vAX~N|MB(tBE??+{{Q>@{{R2~_xk+l?)2~S_TJ_0|NsB}{{Q{`{`vd- z5Ev^A6esri`}6ktV{?iK5hV2Z`X9tx`~3Xy_4)7c^7r@oUT%io;N!Hq#Np@g(A3%U z_W9=M>+|&W7alQ^ovql~-X0}4>g?{t$j!;k)Z^sno~X8%oTLQ}A>``vNl#vgl%-T! zXFx_(thK?q!OVb;prx+7DltNLg_&MtaXQOz8^l>SKTp2E#b#@FBP%?ptFmf&k2E_= z-uCDL2_CSrxNUNN>G|(4Hb+_0hAzlwba;fIqpER$l}XQgdw+_*?$M*>y@!jGdx@Ov z^7(?^rO)!+frXD}){&9nu#J(LvFgWl+M56c8I{ko8=ww#0000MbW%=J09v4V55n&Q z)G%vCip3m@yrk;#`Rqf~fY1N{010qNS#tmY3labT3lag+-G2N403DM_L_t(|+U=eB zQ{v3l$8pDTW}f@rTh)#fXeA+GF@o$nC?dEZD&U6WxQyGVqchL{|HFGa36QWTh$Jk1 zs%A<;Xw8RjpL4o9>7&!VW>fgz*XfzD(FwhN(q|kx^G)jY6Qg4@(_g0~9PhUAnNOp7 z27yr&#WM^)Y(`P|;t``C{WLTFK892H8<_sApTzAroj%|EylIGS9^^Es|2$nL(+)`x zSa%2)YjU4#MHJkttlyz47D z<6FauB-ol7J-@JK8k>3zn&q(Tt{FWA*M5vLK4# zz?rUb1X1fbrfID%CxXm;62^)qLb5&MfBm;ES0A51b{xCy1W$XqwESnhF63~`yvJi9 zaN4suBR8fbIc`QNkx9AMBzcs8biRm$9`3! z_>^v3kGNTnha{Bm8$zNl<5f{2CudqP5i0L8il!>EghYs&CmH2O$Z`bXVGfDj9k3j! zhHnrhPmsLdzDPOcq`_!fAfoKVCWZSpV4!0JQIg5tj_e;Cc%~o|BH_DJXQ{^_d}h)B z%VK02qqAkm$r(WTNeDV*$k?GQ;qDjRD1zGuNYLrb3}{rR`>IFKze#%D;hYB2f7MMJ z@bH`T?~ZL+H-nt>WOnTXoaGFxGdhW@{0Nj|5OwTp3nUfl+*yvo034#uTSFaMG(zkZ zo6za0H>WbH4CxA3g09x|0?OB;4o#sz{9twU!0SW=VN;vbO&Uy|HwNI&gH$AlEu9xo z@;uGY5q34k>wNs{a~U)WoSIo%^5i7RvcXl{+b;`+>i`jGEhwFld3~;uMt7{4J-fbK zJ&l;zUCPl!a>6(-1%6;8Kgpx=3i23AB6jbiygpDU$X_hU2bOR8D06U`iR^n(^d*%I2D6zv><4J%J%zK*3hvXRqXYb< z%`CWGdrjfLYhif33hdGc>E5@c;SZe7Aj;-?aW#PThaaIM!#Gn;*i1wws{qy zQWn0Oi>GvuHQzA<#IgmH(VUHC*=+jGON-v^!fb=E!=qT1P395^MGN3O^C*18nuCSB zHyl1=Z3Q6jO2PY4R4THzoC{7nHDi>~G;7O%${iO$+fjI!P~s4B2h6}f7Eisx>r=BW z8wHei062!A(|ga%$qVS<=41-Hzd+udhpx+`QyZIdq3iN#B>`nV!Uiw#cj7&>MGl4v zCBE43ODY1MkIf#y9Wnw+h{Vnun@kJAJAi)*0LqgBl2XS&I3CNVKz}abO+O}kAfQ|g zvc*f6%as5Z8hoJ#hh+<@6q0F2{%PL^DY_d~P)1YclN=*tNvEtW<;SYLz+`C@eo7xj zz*Cw5Wi)M0;_l8%s(ga~iy5%}2+Lmc!-TS&Q}eDT2+BA`mr(jsq6$H$7dBfa0Vr+P zf-3VFHW@7L9|bW=eA=l~Kp6y-bJq~0M8naF86uQStW=6I6eh%{ZRVi2f)eC*l5=8~ zf-0kBl)#u}GbO*nS+KE-ZU`(m6~|#G&1?ZFUvMPD> z0{Y71Q(`bUe?k?Mmk~AsAsg{IP-j?>p}QJ(@nMd*g2Tml}R4m~1rXkUc!%GQe36k$82OWU(ZGaNePdLpHh^GaV4#+|ycP?kZ z^>p;&7_Jn36~aU?lg?!zikNXCcnL_3(yLCK*RW@~!V&cQ7am4CW>!Gyf>K-akLZLw?TV_+`c&p!}X!cE182MVe0q91_>dKxUNjnX8X6`M9!Cp zFehw_+yx*5$|nE45F&UfYqP=mXArxwZ%q`;__QEoKY$Vfe9A0-?5M;HQ3n^?rE4{P zbar->y7TgaPUAUv`2raD*y;Y+*?u99SA7z>%>F7~2|_eJe3Xmj)BBkcd_D>jNN1VL zgQ9>E&J^0qJb8y|l1)k~8GktJ3Q*K*q$4OhF=zbp`r>Id7C;1Lr@F*Dvof@B&kRA?8B2Nq1GY9O5q>;%;BSmk3*`_P zK}k>&lmsO)f|8&lC<#h}lAt6g2})uFB|%A0qRuDe%0Jn^({q#omVg-}BNRsK<*B-! zLn*>y?8i5^55Ir>`2GAii){)2=kxh2cDs?`dvZ3u2UW>`f4u$sb9eXr+uq*Z;~{N&tD@oTJde=V#wCJ3|jWO6CSYzD9EI$MKu06uhR! z%G4Gm2{D6bMGzg{{3^@CIkax z>6PfC1xgA%{-{?ZfV_FnmZY@N-Uz1pEHzQ(&Cdpj_`~CSx51_xDpPfbsR2s*Pu=)t z@BH^oCvlfH_5e%uQPQ`yBp1K^{PP`kSZmE*AI2bBvQ!&ot)E;xe>ncI#(9gD{!(R>wVGVqz5U2N*?IetcDhs-<;~C9 z#l)MBy;Qnx_`^{HiSqczqB1N0@%Z8CKU2ffXBH_A-cu;d31CG${2 z^5J_|eot-i@fijUC3SmVN3uJ=mbPdTdQ^Mz7TqY)pss0BU|Ex`bAyDEt_?Zg{(kS~ zudC4|Gf1j5Djg*z+0EZs<1bYSJ!+x+qjZ$NkGuRUYgnq`E>%K#tjNvxem-{n!kC(w z;6Xvj{Z)<@Z`8=Wlq(eUfb8g^=-!Lxw`#a9N_l@PHwY-<-T`Iweo&{X9HmH!EQW#T z;ELpY@AolM#|%DIBaa#=6){I0N7=w#`j`Pg`K-JfTD|3dic_$p`y3@KdMiZv`#Vz4 zc%O<1kqSEnK77-rdkcds6}@G*QPMS{d{Fr|GrM|2@$RegS><6)O&3=rL{wj+{H_=> zKC6BA3C*b{%dVr8=alfQ15(=*k*BL`X5B_Kk|Kxl2*)}Yaz-SZ)xmf zRX%7fN@bqj&3ve0TX=U_kN6k<&vwe?}|1FE( zs?M&}1RkFnvzA>&`Tgf!Rh8QHZ)Fc>`VysVsH65oWerL-20UFvNu%d;*`V>ehA4S8 zrZl>$%D>hAz4uqkovP%{a+dBxl#1o=XQU-cy9%>I-9xEZifGwYQuRg4YLC39su`!k z{BJi=-t4wJl~FAiS9_#dWsg>#(0*oV>^qb^`Xj$3gKKTC944AbIQUB8LR^4Ml?}lEGOb85ad4VSrP3GfsCZ}&`AZl0$aN_poduOiQ>j8J zfZ7tL{5IfjEtYjN>^v{FcTWbNPe)i>TF+c8h_&_TSPrT=3rEsDG9sizvCj?agJ(mG)Vv*^G^4(=LoEpAnD1|F-=+E&%%yWHT41S=$+YMaN@I z`*#R->u&0>Ii)C{N`m^edAdIZmDvlpj6Dycv*9Z2FU4Xp z6~@6O26Tg~Jc<{SX$VRApe+|b`7`z+(4xzfqcrv%%H#G>dNbx=ZT7Qzn|TW#Z@^9lQ@w%I1Yy$k6=dNm0D^-zx9J1EPAuAz|lDRgk?$JHawmx!4a;2sohk<5P3 zmI-?^ww%A^j7Wuvbd^xHabE_F@>%Q{%P+(EeCayN+KQ_TyysXw3YyKqRNisH3JK9r zpnR;Zk+n8kNl}!Lg{N`m;}UDKBj;)MC~IcT`>PJ&2vl{xdpU5SQqg?pY<+4&{*-uM zt8%a9heJVkIPuAr$}a<{e_0#o@H8Qi#IT`;1SFwJ9NSy2O5EQlWqIXZtM~E)I_wrB zFV4#AoH+<=*+@Z*L!t}=LQ3g$dBf4xSoU)Vhbw%R%~sB}I7D);7Qs-G969NOt?NHsH2DXPaaY%UtkB==*8xeKySt)N`$ZWakibJP$C)Ke#Mv&|ocirUlyLEpqv`DEoXtqRm{6FW6YT@F3XpzQNyGTEIvOZFC28H~s*C=i16#cU8Po4fSa ze!zE-3YvrE6*V-#i?<3XEWIrSDjZW-*omVx?T;N?UY24}y1|VH;nL-0K05ff6eypV zdFL`8UB|EfO?u#ObZPqnU|1tJx_S>0-DECJc!L`J+pmbla0_D4TE!nlK z!luf7{oQ>q&!WNRrh|9`HJEHe=p{x&l=i;AtzR*O_`qP-~EbG@mRZN>CiwtE~>~vH0$AVk8SVU=fM@Zu~8e63NK^q-@P-qSj;shN@j$xotAj|C@O; zvl>Mnb-zS4P%5`4xA5BRKRpmz%qwN7UKHnN2d?k|n&Z^2eN$gmtNY7z>eSn*sTIbk z<0uEONKBpAQfDET8^qE@b+vEQs8VTEy=I--o@eNo+%y_$D*s*JLp zUqTU8eGJLlLU3bE`^HeU$amS+W>ELYTaLR>R({3Cae#oV7D_$4kUM zZ8fP0$_Bv!F1w>pPBZmi&#Y19+ZVJzS#y_xlu2{%c9|5@;JfjuO;Q__jaV*WnDo08 zX>TA&i~9F+YJ(D@ZM|y)C2{TgOp0%$$d6C`dl+S-98hGy@h$%?b2P*G8n&Xz<5rrX zZ0IoYz9qAEBj3W4$+W<6sZkpiMuW@I5@nN4E@_i7P_Oz&BiHo9T0NMmAxh}vTr(Vy zw{x740ieRBmX-Z2DcZmojhwx)90f@lwR${AQ=vKk z1e%&%yJw}fMF~Tn=E5ukU261m6G<`}KESUv%K9ar4vf=sCxjkAiF53a;OOJD+y|jI zP<{wZUmN-8AfxOYrq+t=A(R5lcREd<#n=NNdk7^Szi{u6rUl0BG$JVLH^4HsN7HWX z4UD~qQW_IlW@q0>An}|W=+S_B$a)T?un!xE6E?PO?i?+4+^y#jA}Cv$iR2k!tSB!? z4dX~WH0Np5gVwVsE5%0fu7M0Tyn7%`qn@=MN7+wCP!f~`B|%Aypd=^>N`jK0Bq#|= zf|3|PNl+4$1SK(olAt6gi4l|pC2C$FmnJA1qoJx~GNMxwe+KLK^`|e%!{6X!qW#z!TLrDHPbglG^n4XF)C65ntI^Ufz)Z z^#M3!U0zY3^xiLDg|RcNlag8`S1$cP8Frt z<_c~TE)Lqf65Bl7?87>a6pF4}t@k@tIZ8*!YTdcFDo`f2tSbqQVf-uBt%PujI{=ng zvF^C4{!*;JP74h?SF9U+73KW;VLT9ZfHCDmiLK>O!YQb!b|_wVGJp@5aOj)0Oe+tF^b;r z)bOD8lA;(h@<1Dk)I7)p~en-2wM$T(N>3UWLU^Q7C-19t}9E(V2!Lm458= z!xt)7Wvnl&i-HVlREZDOpo}eVIm%G*KS`AgxQ6v;S$NTqb=@0vZ6996q1L*0-MZpR zRN9CiZf&pcY;A`t-ZQ>@Z>qK_b#UC~hIPx|f4)+@D85#es21hs@_L|pbpp!u6|gNU zMCr0^S_R+7Z(zJiUkPs>E?ci4F96JCxXW7sIWdUfTT+{0)UO@9Wt34bHYl(Tmo1pp zx>6~l9tes46e9B>@CvM0-Ljmb8M9W~T;JSqZ^lB_ z^#ShSlCN}aY;T0b%a)QM_yMvTGVSy8Wps7gU-(r5=M zPzob$35_$fyfG*E!yU|2i4q)X7|%FCT;G;{6~BSor0`J%N`#f}>vxoNv^=v`gA#{} z6^8(9!z%0Gy!XBur(=irIEw*>AiNrs+{*H{Q^?B?y_Esb7H$(>Fm9`?gY)5g>qejZ zO1RLiEtmLv0Oht~W+;ATUElP2A!HxAokCvecDpx1SIePFiVpDZH^bhLbw?uVmSv*h z9qWoWym`O8Qgwsz9qX3V<_06=mC&Xe+U(~DzAU1QTPvE`z>{pUUrBpIheFo3wdoWl zAjG`#e%oJd$FY5dkF9&>1eD8)WXQXPv&O>}86}t;mbV#_wMqAD9YKG;V{vR=zv7yC7bJ#phxBtiq4<+caY#hNPUU2ZtWBnIn*+E^ zMx%zSIr2py`nC0*MsGH06VZKK$5DE?Q#J)g*FEnkyeXYQ36C|_J(9g7QO**Xaq$kFiOe9ubruVtU<# zfRb3pHlZ5@lqLeAQ=3Dhx-o+CV}`zCx*25h5R@N)az-~z&3o2(!lGlF)_v6*JZlUg z(W$G{f7L44U(F8_k zqSQ|TN!@3I$73WoIERs(opF)ml7@EK0$?{95o4B=4Nb zn9#{>+T!$4G+sJ3ZxRB}v`*mtSP6?Oh}%#+4+ebkqM`BSW5dILgUs zdCU=6j+i{8i?@$*q?RORIrhs#qImo3jCCZ1(DREd^0cYR&$`Ao^?!tAn+rNqy5=lR zgk@VKo3Mm8{`qAh^BS$%=cXjZ*t7PF=&~g@iN>09lyai~S_sP)I``y=?sb&#_SgT| znK`nU`>G^i3S;^$lEN5r?0*JC<~CmI+x+KoT?;k^gUJ8Pqn@s)rs=+!NRZ@Ii!5~` zUpPcqHkPFRldeUQQZFC*($xn7`%;j&2B-ehxUMam5@1d?-ner(`!&YEPmGM~bW?4z z#OHqZU{{N|VUE{gP1s5O=V@JgeOuAbr$3GA$pRYm>(Ng$Kw3d!mr}YL>F)mc zz3=KIc5={B!4=XYQOxu!a%|Aw3}m1_p_WvVs-{1}5mAgOBxKQ9J)D z`~ZT%YC1qQJJ*Gkjf3O!>zmty!}F_~+lPI4cyfP#e{z1ge{}X>zq-CXy|{X~oL^o) zShn|1&o6Hl*AJH052_lwADCoMxbGg;{r$%7$^6>hLxu;!{?YmE?cJq24GA^ifkQ~i zadUG^LC^QjE$ZU($|E?1m0Q9&AQ6|0q9k=~K3?b9-2P0yDR#b2REtk^X?S=Qn)g~7+8DPVvdDf8VO_s8I&_GdQey1h zZplBqwRIhQU=X+{8@gj)5K;VdFo)*?ZZI$yX;c(sb-br{Ga}8WC(nNTX&N8*w4Ps= zud+w0NpgU<9-JUFL|J0uw^1TiVB#o66CAgEAow)vbJ+`=gClDK3us#-T)0u~@&{LDA2{y=x zy*th895QR!+Wc%G-TEl_#dR1)?w=KcidY-gsRT~$k_1<9QBGTOFtxyLJa(wxy!bDQ zaZ2I}Ya4584i1ia3B%NEICLs!eqGY({5bPTYegeU({P1VQY$0I2N04b{@&bRXJ9X!K558WogyEl<*cjP`d{7+M0PfQhE1UTix*^Z=Z_CcO zA0llTji2U(d8&n+DOk-&!|k3R_QA zZjM)H{?UC|#j3cfr+=}NmYT44r}+HZT%h~aib(OQNc0bS)MUTH8C281YbQ|gv!x|# zF`N%yTp;0ApA~MYNEY9pI|Gx|W2o{|kx<-f?0KxMy8KDXtY?Kh?=j^$(E zyD#ugrmKY#D(wAqs6VaS>;1*Tg1@hMf@a`t81Mep_o3IVO=H$(>sbxX3v<2Kfx;H3 z*&g7l9ZQlv-#7kE*79`icc)ASZT@7^C9iEtaF5IRuR6(|hP~-@w5;rmU%1O>Tev%& zXhs?w<&;?(&PpYGQ(yFU6+RK6tYjV-qAI8$Kzr>k+#YXNw%=a+Ya@wYRnRU+DcBSR zIZf#zFRV{zbJuy0#6t&Utt(`~h{HZke(TR3u$hdU(5WcY;Zw=WWH#DFpE%)Z6!H9>UDj#r4 z6g>*6sW1$l_ftztn$zrZoA&Y|T?JEAb`$Si~$Cg0NUNs4v2kS_jT8uxQriee>Zs(0q!r(uV<)uUIvzh!;3f$uoex&HGJ&<_7`Ob#)=NUR53X? z#)b#DVl7^-W~{Ge%up4W$=aFbm|hT{X*`~^3w?sbwy|Tqhy|8;2whByiS+J!KkqFi-3+@)A2+45(Ph{tET!>UnXX>`Tg-74B@;#4kBGvi5&_4niW()TN+#N6 zRSXu!)-nx;fJ4trU+RPx$khZy3?8gvt%`B7$op-l-(7JS>Nv9ZZ776`uO1E1(}J~p8F8*%!$hn(oUh4Y*=Sy0jn3# zxDYyBp?V2GMm}HD*Ju#?IbV<`badlF>oj*sCC)3+c2@Bjd`~> z^9k8$RfHA7o5^cr@|hM+e{$|k849ZmY}N6Qmndlv6Orq~fTB%_ReMQODclvSXB6bK z&c8$j*<|_Yv41v7D@b$itq;GQBa+rayy4wWL_qAz*BtRAK3@`Eu@)Cu>{(g<8KSG; zGURd0*y+lll$UFBt#zmPre!?n`5ZnymlCrria%y2RT5KSBBrSM{LQqa_)N?&_}10) zfF2niw=maCjrc(HOrHutdf(AAMWrhv*l=*T67(j3B5bLrwzvk`Rye@Ikd%>~o-h>} zYTBiDMRFof>WE>xM3uk$%;Lvj18oDp3!_uIZ#qn=3qJ4eXRgV)=9)zrc%r3~x!+`V zKIEV6-9U5ur#SFThZHbH*1TO+=lxX;c4(sOB^P&g%}GrrlKWD2tj)on2ITLtyciT{(XQ}A*!OG;eW0sz_J(4Fa zbMsFwg7V0`iz6`XD)UXtL`kM#vH#Y`*=iMY9>CO$)1KJbpIoby>GkP5^ZW!oske5< zJng|?1d+9ZN?TW+S_LX*7n?cah~Wp79Y-V}t5iYBN3hAH99}fT zB8BhMyc^$Y8l=*fjO={XC~VVb8buWi4V*)Rm8)B}-}MZy*LDFtWE0Wb*0nW433THg zBBDfx__gYY?1Y~qUB#HCncIDj#)j$+Pv>}8EgoBo7s#ZpP}iDOhAz)cNBt0gPCw_7 zr&S*dXRqHaCO&dlBHmtKPTXHD&$Y61nrxQU`jf^997WSa`;3fLZ^1`9wD2bKB5DdK zj=2o1@)BeSPRcmvyNpNCf9B|6MpJ=hvt&>L=qjDU)O(W15_)lz>rrVku7i5Auo?^c zFHf4tP3d0wdVg)=y0Nh~!?yh~S0%FRImpyBmgouIE z=y2&;C-7tBe7#qh6s$EXKsGb&EUJSwwn$20J7tLNFr2PQN@`2ko{D(pU%=)ghH>tf zgI{BPimAuSmfs8KI(`<&-|CtLsfYEF>mu@n9huSH0L?L}^>1M`UvqsaIZ)f?ENDf* z3tbRhIf-{ReG6g188JkNG>oQ9by^l=*&I4-p_kJ#&WsMjKco2yU6SJWh52~tQ`OaY zAb9Rd6&kvX0lHEu&LY5u5n5=6X;MmDBUHIkr(WO=6XJQJjQQ2jhceH7n_pqdN<^N$ z;oRr!L^$&IyLfU16)_V!8&&GI;EA70pGhGdN8UW#1tHY1$Ix(qp@F*@b=&@tn8Fkt z2%mfXq%=*uMZln11Rd}E;wmEjI|60_%!ua+JD@o&l=nyXIGur z)0=BDjg&a$G!+99l;yrC&FrYTvb^XA+W_cTV86y1SPBnZ&j!H)#L^{EfYZZ1&ie!1 zZ~!xPcyqn*t#Lc8Y{3pWdx&hQ4Z(NGCsML=bfDfRdFg1Mp~eI&8-StP+C-TWSKMRE zhkR~dSqKaVska{NOP;$;_7U)C7&B@R+~Hr{NDp5Zc~dEfIEeq9d6lYrOce|ems82w z)0fmN5jL-#Y!#P7!4IN*Lj>BK6yO;Hu3zaoKDyRXGQfB6+0|TUb*23(%+;7tr0XKS zLZfVrhR^x%!c;lYPjhOnIhIgMp6h7KIk#O` zyOo1bed>d*bvBmsv3oO$^eX0@j+=?7vACcA9$FJn9)$C+O8}==~%WD7ME#N;L;s4V=&_y2wQY463(az6_57lSfBaYIt z-}?wQ-kTa9Wk7JjJkI)o@hBa9-T4hz0CGmh6KxDd6eqo5nGPxqACwfTJ}Ru4rAc>w zZL3X_=~A?7ns;&1r{a&fK2oB9lX zU!v6y1&=>$)cActzD7G}dN5AZDAFaOCZo=ZB1ECE;F0QtlcyzKrR?VHc`wH(Oa+Ya z+;XFJV>XzTN*-EQ#mf%w+>~ew5!b09Vx9&phQ+;)-+hjzN8okSBVeKkUVOSrUXI8! zJ589)^t%SU5aR65hH3;CVMR>(^-(o|$<~o5-s=d1DjT7R;et1YJeUPokW#SkUj~P@ zmKV0#3T&|GRw}EK@_c|ijVe9_ox=(&VU^W3MI4xGywv3huE}4VeXS+E0*73wX=x}< zo&JI}sim@!cxfRO0sb600E&!QZ$?=pRYuzDOtx@0yz|w9rwo!n6fAH!GS6ltRnsSE zso*0;t|}BkgHr%Bj1J}^M9QZD6o(7HDTC-#xhOubs>y}z4NmRih=i`xp5*uN7)I2F zUKaQ}|5%pL3Sy>%s*R&Vfa9PG>6#bV$cb?wL5|*0?LN4_&%K~JoBN49BUB(wRh%R7 zHZqPl5=Z-a;$h)A7Es2AN(2rGQ!^7aeXXl!6GPSVyFZf*)oFK)l<98En*H`4a3$YP`JHxZzFA`rJkjRYT9G5+n)Lt>f+h+VW_#LtnDw! zEvk5C>n%hYnJJ#WWTa|?K1#a;{Jdb4{GD#WCOhDjJZFpKkImfrp~03RR$o7#b!%|- zb-sjT_~3LxE5=-5Rv(~%HX*GTOxUMm6Ql}%Qn-6)>SjT5Ik}b4B!)CFfgedB&-ql9 zSPo^%DW45_Ie}DJB!fzj1C-b@DlB~+vQcmTs!3Y~ISV2rBDn-x*L=__U4CO<8%#1+ zf7MWzV(p#_#keC?3DKWX7R*BI3^ zA8{ZZk@uR)CXmaSjf{M)>&ZT#uVeO=D+*qSQ#p($j<-ogVkf6mt-~A$ppb&hPKkni z(E#nx+l)GF?Pxe3_RbT81~-XPqDv0d`)9j5ogXya?AEiX8Y!UyFEetJxpwM$-bR&{ zM8nuNbjFmIe&l=uO(&Cafu#6k3|XOp6Xy;8{Hgl&3U3W;j;9vUA3{Hy#urk)ygu9L zyD2<@_E_>7g~-UDKf34CyV)@br576Tq2)h3=hJ+u{zJ{}X(W_ayil#1i?5k%C-pPi zTBoo!OR7Csn*tfE!{d|&p6>A4(=;I{M4#30G z9ikcExLz^57fEzkjbcM)^d0;$lBr@$lJheXcp9c1&Ou;+39=B~1i`fW&^nN5I8qIQ zAi+`d!$rUn-*SsI5ppoUlqnszeo~HTS=W*W%@ZYk89u{mGm5rTulmg`{}ZYjd)K&F zEcq=M3(8bVo5IMAc(NGmgl6+QSfnaocac4(#-b?S=zbE~Qa(G6)KKEa^pc%C)PW3Z zIj42i4SmWO9jIKd?+>R&fOj_BL#Fqt&hfGtf8X>AMe^8*$|SCS3*?9JMSpj6Q-|Ae zo-E`F4h276^RIx|6T*Xpl_4Bi3NgSGEJ>q6gQ}ui`Pq!XcX|t0YSOi8-Ju~ zoo{mK#0T2)Pk(OaBi6qBQ#*8&~8U^dnK9s4C|qCcC|(>q{G$+;{ctu!0H?0} zMBOIalZlQGKaH#4f&hR9CpOT5;FTpr2Ys(rv7xDd#Ue9ZHMFrQL>0nYG^RtM z>YVJ@UweWPM-E14{h)sM@Hmp^X^;sol(&n>T=d)x5_g_K*@HP;?gM0@WQ}>31819P z&w=1kkr;V0{fT)m(hTCkrfOd7BMMb^{rU6^w|@*3{X^(2a#PeWN^SZ)!J+Gq2Kyb=Hl z=dKt@i!ErS!~CXANB0PEOr!gfjm$>Is}38KL_W7%4oq#tV#jB6qau8YV;jmc1h)cl zQjiZ;(lK|RbY$bGh8Dq;G>|L>c>@?REA98sXR`mE9&G&jtW0mDTB(pHdW{(ZQ=C$o zPX62evF58kYQ&ftJIsEubCX?svOnTHdiiIooGuU1;1K#*&yxa~uU|y20HBvu0_gOS z4@G6`% zM=3e5_Y~h0H4-_5VsC%+OL(6ohxTDkzQCBeuhgpgesUs;8 ztVrR5y+95AGX%OT=;(Qm#nGM_>O086p};NX;E#fDxq;hnOn=L-I=7GDP0GGhGpVXK z5v5?PMUU5Co>(mp*moK~`r714>E1IySr}6`@e`-rr$q0Sy-9iWB__Ze1FbJVB|OE! zp5|@j6v-D{u)oAQY2{N-|6%wbv8Xe)Mc}OWpk?px1*nAM>5lH%5Aj@>{#08qv81T+ zT=5;po3UJOrIF+N2nKQaN(^XV*4{vWTx0c#G%Hqq^lTxy{^s{=@?0pT$THsJI^t9pp z9o-O^b;tv+*+vc0KGrlAyVP=TIJxH$XJN_qgl(KS&{zF3Y2kx=7<9z$?cj67mxKJ_ zQTwT=ch8)6rEI`- zLX`kvLKTm)ePCQ6-)6D}DT**gq>L8VH|yN?^{rD){;s*#ISEQz3zzmU*R?~JGx2FF z8eKD`h$=bF8#>FL0sOHE8U%sCG&zP=|@G@{O XSUXNnYa!xcY>1%()KI99dmHpWQe+2d literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/taskprogresswidget_preview.png b/android/app/src/main/res/drawable-nodpi/taskprogresswidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..db963368e5f4300a31455281bcd8ac1167c34645 GIT binary patch literal 7453 zcmV+&9pd7NP)00Bw}0{{R3O!wwD00048P)t-sOlfiY z{Qdj<{rdd<^!NGj@%Q`u{qytp`TG0y_W1k!{p|4d`1<_&{Qdg-{QCU;`uzRx^7iZR z_2}*M+~e%v=kVg_@Z{<7;^*+~@AJC8#sB~R@%8y4#a{pa|Nj2}`1<_&{QcEkTniK@`uqG8953+o`5PlO=I86-=kN&;C4GvW>g?`r zeUco+TFTAS&eGX*gO`bomeSVUXm^fca*5&O=+@fc?(gyB>hU{5QStKhThoZV!O6<; z+1=peCoVt4$Id;>a+aB*GC4`8=)tM7zpb#gU2TRd$7M>-dbdOA+uh-ho2%;j@S~};b$Nu?^y64wYnY?6hTf>Q zxxZm&a{>z=e}j#C+@I|7`WVAk00cpZijS>xnn)YP{Uodk6n9QZP9 zs9!hX^iSVLSd6g9vPRS5;^Or5^iEc~eFP!QFzlphkuaQI zG?^y*RZO5uG6a(q4ScTyCOPrjfWV222>J+L$D+w7@&ZQvE1bZK#>quum_~l!yH=M8 zXAO@C9K-NV6DYQ6l7EB9^`pH0_gFFlktwHjWLOhRuGdhegQ<4%Wm|Y*j^=oLV$}9<9z20@D}Be|QL0i$+X=)%2N*(=W9g=I9V& zMBYSIQiEXA^vi!n)mehN4v`uWNKVqQtwD^0rJ9YyV_$$IiQGntNu!a!{H4YcA`T%5 zZjz;tGzw#S`CMt@kOgs!2+nj-q=>ZVn5M;lm$QV-d=QJ^Cl_hJsf*>yKf|Rgp;{b4 za_pu$S*ML#|1M35NXUVzdwSYR;nd4gEOD1$-Ym~RjWbEn)GhpUv5Aiwz`P;km}rU9 zNH6p6H@0bTixhPREjmXQt3M?V!3?RlxQ}aekNc6K1c|a+_d77q7luBb3j&WWs z6{Vb{8D;Zo33U!AC5_%qu`JX)5WXjA%xOw~7II3=o2IBzHY0;1x0HKp)KfMqe-R5G zg`h)~vRP+2Kz!x+Acm5W7T?VcLk^Au%CEA0(~_}CwfqVwhY)L`nNX7=pdo|d#~5N4 zr)kh>*22er7=|$^zo~sUEyIR!;wnvaLQB)mavY+LNL^)X;?58Z=T5b3M2Ad-?^9RUcGRglbCXJmj2H$u$SQ>veKTxg<003lD8~$Ac(Uf0PBcv-8L1UGh{G;3(%D0z$J4jJhq692a|HFenT7VRXducaGn%=ZB zp34Q@1Zg}VAEa8Tf)aSmXHyPGD!a-OXu{PlxfgqKgPJNR;r3u{?REhsdR^_V$p<48 zWd%x%BDa=bmXyo0V|gMh=Us3w<=C;`CQ%+GieFoHQX3cv^R69o@Rxlz*Zb>M>O=|W zJ$dYF`+om5Jo120I^{{t2ZH-+7WqH4kx#n?P z!7X!Z-+5xtljYX|19$f=zhaR*%2k)$wJJb6bl$OZz>MD3ya0WkoUszHk9}9yoR$<7fkH`8wg|UaK9~ z?V80S)5U1RB_HgQUQq)PJomJ_1{EZfKY)_Gws>}2*@M+2QX;s;ZgJRGm86rscDx>> zC@Uj)ItF-Rx3?)7C5m5VU6wz89BYsp$tW#59(ycE&gF^@#%Tnv@Ri=I$MP76vv^)M zaCj*0Uws6lc0i;dC>^m>-onW<3kT;J>MP45_=CekqA^BKGD-mxQ^q;)oUtUJwCu#( z4-j`&$;ntgD98tM6lJ+q?mIj&21^3E^>`jx_d)h?0al@*0Hlz9Su5v}@MN*Cr4fo; zXCH&|!L^-KnxZU6xnZ%V5}=h2V8)MZJp0QtFA-%;?W`>*O*S}t*W%fAqv-B0`xBEt z7@#N%C@pydPp)|^v8VZi)Q-b`8-;ww<2u;in2%XVHm>w51(YDEB~x-RPEqDjTK;%8 zl&HLs^6dQi^Cxv{`ClrDnAMc$kH_+7$0FC@**|u_INgy zP2_`e8rnWn`;&4Yc4vUoBaTa6@T>6G=v7Uk_tshQBsr?B^8R2qNFIPP?QuUMM+Uop(rUzic$x`ijbvMI6oLn zQKI@U--D~cr6}vy$;-2wo8{%@b0Tnib9O0Et<-MOd5FvB>-Wz~+uPeKE5}>r3G>$0 z@yhnn(#iel`tr?D$;lL@no$!zM`!2vOWP~QW}nYSWTGJCj?Xu-1xQa$m(Px5m>fl^ zE=M=#r%NkaGQ|nAst}|;8|df~;KWobRmUc!<#`_HbhLcGy=Aj02CP}g**^2}()x`+wNhET zcXWPoY%Yo~^$C0fpqjYTX5ML(#4wN6PmYOQH%OA0x@}_Rd0FbLnRi|*FV~lv0+}~- z+r;t7veeI72_T7!oM>7|NI!mF7Wxt;I7z&GvSpiSo!D0HFZ)v~&+b>Wral_&+-5#G z>qC@B>q};v1}|7v>bzs-6H7OJhw|pRrfuiR$yIkjSzisBNl;!LO`j+eWC(!Ni{q|MMpUl4PWzi@K{etqQYT%YR+K!i=*P)4~+Kv>U zlX?2Qv_hIkD&L0Lwz4cbjaI2YP@XTDtEkk3&u3ma0gO_~Xz*@ zLo@fNzJAi_lG#@Z({?gxlu_!{g#TG1dVJABuT7fnE4lla_8P;ETGhvrLHJ3x_sVJulJVLFB>*TPSH&AQogPG zvsa}gET!H=2_#Ry_9+frF37J2H8&9GjyP)mwJRAolveHmzg(o*`CkZ^k!7#$aOu+qZ56ypm%d3sq0Zf!B|#zZ!e?v3$J$p*>02=ot2e2Fl2D9 zNAbb&I!B(iYo$=0oZGfe^=ZfA%8r}R)hH`sj_p~md3)HZYc1Yxl){OvtgWWwOtZ?+ zBXliFjwod(9A!0kDR!||K9?s#r+QB2sdSe@m!ibwo)XOa4CyJ7j%wLnt;F|b8RgbG z(pNojR#y z8&PuTykz22oqJ^0_RhJnh6eTZ6vV5pbsb%|63MXZw5S?1aSy_n-U54_qG=BenKI$0C^6HWAGL)tH`NY;a>YS8W zxY$)FxzhUE{9EVlS_dq=eOh_qE4A-VUJ0n&!lf=k$(5Em<_^YhrFhiAS+)r!23J|q z$vAFpeqQg6NW2a$jkcjYEsfvXm-Su5l6&vcjAeh-=`3+K`O5o-OC64IRDlFntQEMz zH3g&Yh?^BO?|O#27-eYzK4Ct^noSpev4Q;rE_1%Q)%kgOheO2WHm-NG+3fApMugM$ z(aRgaLf4?=(AiQkO54`5Mh^(TnT4x^HP^gQ@}Xd}U6;dg5G9+C@54^no*j?J<8Wp7 zqYaHq@Lhwltd{{33A9#?LUQ98NWng@B`9IH%>$3cLAG#DUc2nDb+*Lq*59Hm=s}6}}R!x-1rA-+@QSg0lOVBYTxcA9ny{HlpR@mt{jz7ooJ(ixM^=f4bht z#Ze$uj*{57o!B?*0_`QjDu?0+eSq?4siawH)6}eF-HaToRZzlX#J+K<8oO@^%G+qK zqb!L#=bCFBK^5gHh29K7Z{0!q7nK@kq8xgOI(Lmf+|I}N<9DQuM}p@DI0QQ~(2Wx^pB;TFr< zhNSVccP+}2yb{L9E}KFL_U(xUiwxdRQcqH=J7V+h9yjok!m+L~GPSTn^78|>C=9t* zudlC(uY|V7wZ_&^*P<*MB_r24#f_tc_wr4uODSQmhZGpE8%+D zCfGj^E+%cQ%gdLe#3%vwot5nyxRhvLP*~K$%WiJh6p>JHBJ2tzWp;S+cv*aY;Z=SjMDLj(z^@! z`NmsoBl;w>w8M-7StnEbOamzSE=I}MgVF<|js=t?euI7Ex8CLanp}R%JT2KPO~c^j z6D);pntsJMxWEHYW*shvYd$|DOYHl)1Q(mQj=`iCkksycK-;9DZk~QsW>NJl;fkjd zAW0<^a+Cagqj*pUav(PN0CTZc_Y+A;<5ae;o)jpwV){3RBz$KlyHGklCVyB#!moeV zeX)XWnyT8&0uN3gl23l1x;dzDhnzB%6>HvUy7brnswO< z3BDs2C)ZZ%;4WLl+?oX*b)J zPV7F=n@j5}F5)H73qG_;8+i0FE<=L44`q4avTJ2 z@-9Lryrq6uU2N|~7b~u{i#x8AU3$^QN|n&PdK8R3aw*VCwUj!2fm*IxQDWBE(Mnv> zaqok6U(A~~b~5r*TvD~2&bsZ}aacQO;uRT0TffM*?;*h(J7?m24{dy)rj}0KqIl&_ zP<(f9T~xUz27@}au?g;Ol$8!kA9u-Bgq$|NuMQh(Roa;@wK%W$K5u&Ln8$Zwn`xD8 z-h}Sod=Ojp-#3-le}7S3w!~D?5K;GcY`Z1sf>LDz^!BDiY8$l*y|EKB)^~4YwJpN= zUfI-IFzU!sZG&(f>H@uXt(Wv;sSFW&6V#O7$aB4gvI@&qI!sPde+zwDQ97bj4Lz+ia;$3H?XRplfU*ip zBaho?+`5s}7o~c1H5yroYnMvu*tFLdWi^&YzDX;`>RwXUxivbYtj5x5ZN|zuux)3j zN$QTWs$Lo!hKZ{eUI9YOUZBZQ|e8O-_(_Yt2{Dn`CEDLhz}L z0W2MGR#}n<*S(T>@x2$&e}Zv`P}VYX#nOR>RTOz%!bKwRJWnoNWOO=fKV9s=(zQ;Z zR2`!z$N9?3O7K-aTs;*)sE<%WXYqZQjfvQqMJ=5}DMue?%Q-^b)S)H5okUqPy>3vf zV#gNvb`&M)=U98ewm$;Os$Is$RFd!9|M3A(woWrU3-Uvt1kL1IVA?!Jh@vDgtxZI8 zr_tJRkRJvmF>|p&JFlYy(jO=lnvG;X(uer)9}6W(G-cg%YBX!GYe_&KMLrfvxeEz? z)2hX+A<$s-f#k!XEHuYOYprIx_%YmL7ob!&wwA!$NlPscRsM^#-30srKki3H`P%0t zeM%6yVaa{i5Bl*@c9Bq&6eUGTQBt8ODN2fxqNFG(N{W)Aq(V_rloTaJNu{SzDxF@A zqbRFyR>D!OYz4@Yn<|Q^h``1LayiksL{X};ibrxew^-YznTQ+kMfUcR2)Qq2f)u5S zu<480aCq)2Qd4mk=4Jq5ZfsouP484_Qnj!ngXdyJ?etlQ>;oZeu!i!KKa}k=o%jDpZ!>GD}a}jcKIx25}n@L3dFo-B~PTr*yG<__-Qp7oW2*UidI{h^D3_MzZRI5Zmy?=2v3q~TC#HjM(Ixri!C z!7ra2x=4Z>2m<%HP-ruJ5rGrf!`U#r%v?f3-kI>#9=z7(OrYnh9p);0i1V82-V0wP zy}^ski)b`*7@iA6;w0>-p#)z$7m`m7@3|4g`S9FfB$=4oykn7&2+bwjk;4$2?cEE9 z?t<>X-sT0{vncan!XKK6yzaal0{4i48!%9OC9Z-J9K=O9L-qp)RvGXTv!MieJUt6% zL}o&MI3i@icN}Qt9ogMAK;DMyVXY+CLTOZlGD#RD7osG>&v)5)Kw{d%C^0eV4D)KUUa;0gE{gNXQVo=dODisx z0E0s^N}wlY!kKs`d=Xd(1{czEbGi6TD2QO{AfOEPv{pth!gukaw@luJHbZ-v1qK+_ zMu`s=Y{ecqv-4z6w(qeMq@M3mz_K@M;MH#As@(NJy z9p;4?c<-a+kO-DooSnmLX<#FHGa>l5-$9994LnzD;EZD68L99d-oDUE&>$$k@f%vk zp9zvc!dz@7(0gGgNg5pF$%kQJ3MeB4Wppo`K#A~PRIU_ppP zPKF{iP_kEwn}rkrxI;2@2(=AllruTN7Cw~oq)fUWP(ngE8@fv<5?~6IcKE`d3*5Limyh5Tqegvl?F@Mdjv}~dwG)8JuWNz^9O>E0M`4j3SNQz~ z3{E6ZD4-O`QwpyGf4@CZemjnigKN>xGOa{(Nvgh@#OTPKv^^`!-gMY!t|m{2T zk|gr{e4*82LnKE&m7pXh{?8&Uc^Yt(gTtjH3CsVQrm2b7kMdhNNy75;%QQ>j)M?(I zD@htgA@sZ~G89i;I{AfgQ(-y!pBEDyFso}oXH<%VWUXlh?k}}J`UQD#Fp;f+A zB`N#M&xUdEV1D}7 zPsP1bc~eMYrvqO}ZY_~T2Y-_@G400000NkvXXu0mjf#|W2Z literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/taskswidget_preview.png b/android/app/src/main/res/drawable-nodpi/taskswidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..758c181a629d5248939593cd12978d72a1b6f970 GIT binary patch literal 39025 zcmZs>1yCGM_$`VDhmha|CnT_FaDoMg;JUah?ye!YI{|_d2=0r!!{Q!T+;wqS93H>- z-u>6TRd=eoYNmVUbWivBy1%baM<^>w<6x3uA|N2($jV5lA|N1v|4Zn|uQ`-OWB)z; zQvR+k`Q6It;qmF=@%i@Q@%85J;qm_Q>Fo068h&^G@bvWbe0C1Mx%;o^;pzGC^m^~; z{P5&z_wama^LXpvY-8_aefM;I_XILB)jv1}u5Z1#zKe`adwIQ{pI@F|1OwsEFE6i{ zxA%|x$Cr;!Pqh68ub(_WKb>5_S9eZs@9v+TUN-hlw-3(YcaLj3C$IRM`)9DT>!Z`_ z`}+s&LA0Hp}?L(2UsJaIy5zxpUpB_8=$9spSpI%<@h?syjLGQ>|%-o{k zH@BJ={{Nt>CeD#p*EiPQ@$}3>9~gMgFD^-_I0wh(j2y#-g5ZEJ;*)dBZb84rq%{!` z-a(f)9Dn{Aor0>F`FV$D9-p4??jN0=T^t>satX)@im9w`?QicM>>nQgjLBVD-w960 zzqqsE8fe(dcZu4?K#hrjHA3CRoodg7q;1!o0gXxzy3fsR+fF8$28UsL`)!c1+{=9aSDJosRaI0&fQCusI3MG4 zOmx$AnyU|bhpL12b6Pu|PGP0H_=sm-uPdTONi4eevb$U)CjT?V(OlY zu=YEjck?MX87}u%q^rU!Kh`!@A7-56WwX>{Q9!C1LuU1^a$vv+nJq}3l0iP@4e1#+ zCHpz`SATNTAjBDxpa5iJ?D%+g_!15MUz!=O`?0Z3m-)&>kZgll2F=3wvoMCLLaW?` zblE=xn`8Z|_J+UD8#h01ZaVL_sQj*VM+R8b)!D?zSvsHQsGe^#I$v7XUQX)w=1$}P z8QP)D8giw7XDUslA2U=B29FxjG)@E^pZ7KT~JPSG~tF&{qUychp zm?1sF@j9QMzK|{ayw6uaj$SnJIeHq)SX;T;&2V^0i#*&pIBs0?b(twtIJM$x5P6;% zRyaO=KJP@doV8xkb->qI^YyqCJng(8BAgqU@i}e39N1NG=zM|~da(9TJv)U=K5tQx zXjW*3t*x~kj45QSHI~$;0bX06SCd9Q*Ko7CaJ8GW*3op1_tHc_1&>Q3LxPcK;Cn7f zDMV`${s&6YlybxKa(Apt{L-YC1NZ1`=pw4WX9<2Q!yYWKHTwVjq@W6Z&!<#ytwnycpJO1Iipz4P*9DPwI$^-}J- zG@2!FaN)Ize=qk`@^2q*$YRX$?JhGiR{wOVwmVy{d0)oxK384!J*PvPoVIjNJDQ#a6QmDwVKt=Gzz$qp{*bk&w_ngt z+wLmULotgD8l?Rp!tE|@4R@1&c6V{e8|+smm!nUsGJiM&#VLYAklrjty_}xzy0rty z*Osm-LkQ%r!TQ>eHs9yrQ1LNcGdt?ZVTbgL#_`qW@aOKH(ZZ(M&PGsa@2S0alwZC& zo6z+At@UivNBKhdB&y&(Gs|Qnia|wfmpV zsxj6KZ79EIlW6REPCf@PLrNhAFeeYkw9nGVsFCp4 z8^p1dTZGRuUF)8zDy1Cme8jJ$)FG|W&8YgCJyEV25}sAXyXqI`SE0`K4jRTxpTaKq zppRLL{TP7%;L$m^z%#RnRGLZ-(EeN<3>DYn9@4g6Z|u}!YOT4H3>#N&A2sQ{9GmE| zz1(^)Kiso6g4_Xcm>egbljjC|@Y;HQ1XUGMmj$h@Z?ktIEwyH(ocPwXpgdu5534gf zf_Sq)vQ;Zz^Aa= zK0B$k=EHBU_e|8_a}V`SSeus4C)xI!O|c-xAqeNfOV8^3ZeuFRd4J#>&0%wsrwWy2 zP+bRN(B%v(1~c-p>6c#pYG1aXDU~?Tv0KNrd3j|L`UwCm#%J2@BP!WK;Z0RLHMLfynaIT4@8C zk!Wh5Y(Z- z!SwtcFuvQrqq_keES@Lt1U=4mA{wvmi2Vkl0U}n!M}roy&OHpk2jIx}8Oi%mT z{sH+sZ$Yw}HLL*}bp0RI#-{|@I6sZ78LH(ZC)c)(2smXKr%{xxM;(?tkT<;*X-&gYJ7lm*Qrf=4181*RjEYUF& z_e%mkfhPxLP5SU0$48yqy-fcy6?hRHUNChDgaNQNj(6^)@n+MVhtDKX3#X^2pIR>! z3g$fTTQ-*$ky~AyJrF5V40Gyz3`0Iwp<6275K=$v4}Zg8%?y@*E5>u*;FW;LJrP-s zlbOG@C2rSLhXitS6kG3GZ75BY5=(4BAoJ{=A%w@t1vW~Gxue9UF|e|Sj7d4>&jI=U z$y?R7(7*rCeEXhoAVH(s$li{-3=A7)x<-7zZFbyHW9w#jVvh#6E)>HW(YdTGpG?Wo z&>5@VuxpksN&7(v5jL1~&u)J=M+a#m`r+eq^bNA=XAa6@_s3xWL6=uY3wk(4R3h%k zPN4roRZ4-h7AK!#-19u_&Cx$5ivjZ_Wp%(pF;F525ry3Pr!V zE~AW!uyK(uU}IM2-HVEQ5S zFw++cLsI*a>G3^OD*zT)39DyO+2{(28E%bPW0_Q;K84Gk5Oe{vWF?M>q z$J@naTKX2UPfDUdI9zhGL#Z-vF?X`s@#Epd4%lxk!2@6%zhGke46f?oSZIl7YrQVd zgSI-JTf?3LJ77t~lTSS#16QS1F5>&~wc!!%wB(!i}m3Oc8S_t*^bVPCC`vVkYG2R@ga#m$mk;^@j*n8W94{v^!{Uf zLh?XRravNbzt`ON?~U;>$QW;tVb&U-n8rpvv!*04bijBvM>zXZaTssG&8XG7aI<78 zeo44S5y7~KA=8FEX4Zs1ASxxL`R@R8f-9c8RT zU3_?eA4MT*EgxI}nPyq$F{UAxn&%@!mv+XONzH=MI7he?HW&Im+!?!*=r>aj$QMrv z`J5TdG&q^1x;$3Se%(tEZ*4wF!V&Ek!t_OlXBu9Q&av zu^zXM_R6qOrvy1ts4UDq!u$`Ok^kPYMy`e_Z?tZ)&mg^fr#3+L_vegH5}qtri8(~9db9y~y4wSc6MM}N(djvxt=OqG{q$aFeSgdXqm)yn@yB-!Eb{(#MgGPN2D zN!KSR;rLCYr^jYgb{>O;QzgcnVVJ@1p^>uH9|GiW-62E1GF!5)#dK+S*d_~_?C@Q# z!*l7GE9mD0GZb9Evd3Dkd0bd2!lRVARS(p9ok(h97W5RZAi)-k;}qD;rhg1C){67+ zW$Ct^W;tv(7!>V0s<-*Fpvb!?nI^=DKPQXrrx279Lh2jDzc{rp@MR=7O5NG*c3X#> z6d0Wwk~y4(P{h)B>uas!)FgmuJ_DQ*Jdk3sU2$Y^GpO95J+hs2$%FU=g-fK84>HTi zz&6EL0auh{B+61;CR#Ri7KN=?tX$k~_X)eoKS#bR-Grwv3^W7;6!IkAUf^J3U(=YV zoySyX25{F92io>OmICEq)@jTiW2(F#U~+{!Iv>G(4z|{35?>Q!dw{7~bOzndSjzdP z*uQUAgor$~{X0T1WmWG64WtnMu(Li{7`}WvBgM|2jI3>_eLbjt&c>kUnO#&bPQ?$J^TKJVvH?C z9}!&Ac(0)asBoJeOsN=~v=taL$Xr|eLc_e5SzMg$!9JGgLc0d^AGHv7=Z=IoXx69w zKm$!BwbxfB6zGgCHf`=r;h)}|d`m)B29>N^q&=Mt%cZS&?{5Su85TlRE5@7mh=*xk z%QUM5Tf^z(6AJE0e*`6eEytzuc<>Z!^rw}-cEMHB=Bbr|tw@F!KvbW6E4%v{$3`wK zUSpN^HjOsn z%EE~@wruR64SD-+681qufci67UBrPAaCvcuGe%CtQ_oi+c9|U3m`J50olxLiKaLnn z(CI^T)Ur1>M1btGKb)j{A^ckD+%`qmfK8)CM)DN8OKl$)Rg3d8jbeE5^k0YnTFumA ze0Fuw3vEtoJ{wCeo2N9Ros`d$c|{~lNztB;QKDLFzuZ2AUN%x0v_9_VeQe04m9J+q zt-Bm6k*Wn?zqYE1z21L&nD#WqheR^PIwpcvUNWH|FAt)sq5>-U1Xfpn4`gDB1Q1H5 zL>?wnGZ#@67yt7|2WRl8sZ@{-LF~PgBCRXJsUP4Eq0bhzXDVR8-nwC&cw& zbuLzU$^QiiDk=W|0blX{1C0FtD$^l{fH|fd;v#i`Or^w;<+j>x8h;`P%IYf@_vR85Vkp6Df8mnC!z7L5b#PaLXZB zGi0U-q6&P&U8`P(4>9II~zecsh-4gH_y3=TPt% zQL=kLcBEFDktkH615wjYkZSKP?N=`eiBklk;o^uWC@7DMRD?hzkXkB!%D#O83tO<9 zSoagSj1Vd}eR;w5{KlLn*oTD0%Igv{FI8p&BcalQ8gGo8jjK>3K4kbgU|hK;8Jue~ zc_b9StYKm#IuWJWb`ysk-N5$PjSs}zOIH+a7-VjF3U0|{uVc~fe`QzN7)@l_{GYR+{IVsiEsNQFc5nc8| z41}x~$F&@t6$@bQtT9%4SEi}t3bJxxMoe?A%ta6DhNri)Kd-`puw!aJX)Onck9+tkh0HWdYwOsB11E<(H( z7SMoT;Sjc;QfyT(G4^xXSb!W&P;_Y|O)$T;_OhZpx}0KOH4{kQY#RwXlx`^IURu{J z&Eg5FaibLii+vcWmif-h$o~<94fqp*Z%(7FyaXUZ#med@Fef-!SLWx~p~o4eiAtH~ z{tHNx>S|U@b6+?eT_i9NDqd% z(aa)b^&+H{g~yEm>=~nhFM?+HI%X6=B3snof9F_tECpmIMc<5wHLTD?>`i)W;BukC zPC`G@=an^beDjt2fX&5K*Vk$RsJromoyf?O*ZfuiO~H7%n~u9TjzGM9?NwU#Xe*-~ z1Jn+?K`~nZ!P!a2-S0s{1c6gBz5ceDH7t!QUuG14i_9#rk81mn^L^RdKW4GA3Xk3t z#;)V#f-_||1j1^tVmwQ;37nr3LPMRb&E}E7+?FCsDYzzy0BsWWgLt#|1B);uk-*<* zlI@U)y!|wUdfn6GkM61;W_@(2PnfO*uV^Qt@*Yqs)#A-^M}ppL%9TV=yrtZQBpO`( z5^dD_MIUj4hCp`p_*=iqL#ZoTO9cBj-+t|g-GTno?Fiq%(T7nCgSb`~gQ@w=IiH>` z%j(o(yic7ZsL)*K$d;J>rfu}$wb^X={CyC1` zB?0!SeW=nIvz|J{{1pteGAqLeJK+I9*DGx7*3q^%`hkjK;mV+S1hA!WYQkdA8G$#N z-9h}0kh32#S8wKecA#R_ys#K7i@v#hk^w_ikbs@~BEeFhq=jD0V87er87=!LmNu%g z+5j^VIRgckWec^IS~_ohf#H&?Wfsxk!0-AZk!wa{ORcvF5`$=JgWA9vz&51k++kt56Lfdjoi15J9p( zW-ge3rQbVXd6-?pZ}-l*lR|(4JydGCo8QC>4bAvnSa;q`kA$deYF9k=II4l8I){K> zRQIFb*_nw*Vi20M>yE!nCdED>k^6)Uk>Jf`z}-_d`)6l+Zb|nFYMWjr;1MThIp8A&<}@uc1j<&$%oqIjb8tDxh@|z*U6vN2 zbDY_ZbuZEVPFgDLBQN_u!DYc^OJaNpRH&b#GtLO6%(6d~ILaV;q;pr4tb}H``__9< zs~3_E*W>VCYAh5@`}c*gOek4}e>iHh48=y_r=}n8q4j~bB&`|@@^mAK6ln>vTO=}Y zd-a+9Yq=g`KR9W1Gn@GKP*e-e&6hQfW%iVTwS{1IGC|becw1Ry(gSC~n z>-j6(s>|uGQ+f19+NB;BpJ6Mkn;=OSVDU}PP2n=Y{mxWdINr>uFkh*g5PjtnXpAV571s<(;2qx}e|I;fZHft=;RF*)H22CR=!ODijkwoS?b3(}m-AYOXyAu=^WVIM^7tr=Xw&8VtqdG%U9yDnCX|LB z_HB9U?VUi`2a3 zI2OBXim(a30>fyUGC)Sm^W!KzlE1!JG%r;D{<-P%?C{{M5s|;~&fnBP+Xx_M+$UyP zCcEGBjq6i7zu^+;zfQDT2{>e=;1p~NWe}@O92^|(A&B*m68aBN7&0Bo_iw;xf3r|) zy#NErU@7c>`g#r4B~7-RU|YHfMdOZ=B*qRvOX>`gc^*4EyT=V{4c0Or&AWS_A5%?$ zk*i30)+lN>ZVK^y8zLC}0DkPg?7l5+*XP)8OyFco&ekC-_Ao-_!T7m9Bg{p(BfnH?xe&zg2R? z)3uR}hQGQoF72>B)fG18e?A&TPE>&8i61o&d7-1DQ3ADbBedsNL;p4ntLZ1e@qn*FaMw(F%o!;*&VU|&^wV4HPM-%w4+LE?jjM5G?9ot+I2N|%gT~g0{H0c-3znr5E zVilhpIpDAxP^SKe3TBEnGM;oxrBW{qY52Cnsv6cMj0YBuTl#V}{1JOvG){>9pQ0U1 zAU%m$Z&JXsIycC*b#G!w!;WOx?9;UHyl+gMMP z4fBMcQg*cRd+?ZKde7W2=t`3%t3Si~T8LRp;p2srW$nn~nK4FkM?@kLI5=r)-pX1~xqI)8|hWpQY<>Ta-i; z=(HKig-COLzHqR%#~)2B)LL-s+T!H@ ziEh6bWUbvOO;^@;E6$&8Ah=9N^jV)FH{9&>S$`lG8M4_uM#8G_t5Kb%VM=BiGxaX{ z_YTa&LwA1KiZmv5~$89y2v=WLf|LZKk^Pudg zx#&Pp&WT~m%a)c;ii72s^~(p|4jn`=Qw`m%@_>O5c!GBG{qoC26uF`A-HejRkGr(T=OWPT0-MKb(@XyoIj`TNFLaau#8!a- z#Q7@hQw;ofT;u|O|3+Y&a5wkD*+m}9z{n$U#X;a3scsp>FTxMj=wt|&zTFtGh$v3@ zaPss1oR{k7OMgrpr%V-7i@|~ZF-@fVrioA`b~B^L&pj>t6|@LlqQm5p^~KQL3SC~? zg%-D?KTzp&r5MQWuauCFrALwO7KB8fx5$6*_Y!_#Px!v;jFzbSuloG?J!>Cjb$AsM zCr|;v4XIAw2i!ckd8sa43|Pp&o8vI_8P868RQYJ z?_NF&TW~~mf8Iw`@IAqgge^+)1W`X&5#-52XhTH^q>b@vDsxtEO(uBPL>B1OZq^^- zhW^Fnp}7+_`!@e;TPAI5Lj_D)#8;q*%1`bak=@bw8%|U7)hBtp4yc#x$E!;= zd*8V3P&T#%;iX2%Wh1ZfJ$xW0?yTXZ_lMU9F?e@OR!$OKXUlm%Z=X?8_G%0pkbtj-^W2TsXY8-!VX$}yOW-77-w@DiTM?e;_T@$K0v_ys#!k{u+cG#_|i+114ytgadk!-lII2g2#7FN-F9`$ zePSA2GGkfHNmdI(^N_ZGmR+VHj;X~wyu7#+`7EH3pc{uJ84?o2z|eXD#(Z^`R)+n~ zRq{Gyq=;~~vw!zLp|7l-$}&ci%sC*gF%Mi4=&U+;oIL*eZ5W;QqvVl5HR0*;%Vr?H z($)xwQ43iqJ?Zy~qZcl)KehkG{kL_mvRZT(hfxWeNC<@J-%_~oior7mbZ39feQWe@ z$Q=l6W;DNfX|kY_jlRp2id6*V`;R*(6C1o;`+P{HsbLCWoaTYp813Ep0|}_C zRhp+^EkFN(lt3Q8^QAfZXpXNTv67X&ayKZ1e5>z)hEnY1qF^ zX1a*nj<9iU>JB z%ov4^fjhsw{V6W90me2uw&ebo%;FI{d3Pjed#Xs%Wdt9@%>mM2kiPsL;uR87@EUzM zn9WeX(oO)WYQn7(4r`9fJ|eyqIX4!0P;v4XiJ_BE*CheMJS10ksFEHGnHKmmNVWxB z1wTV}GIYPencN+uk5~PEX#+*>{MhA`>+*%GzFJJ8%@y2nV}gTr1QQcNz?tRw(%=!a z+TvvW3C$GOB77kR$n`k`^sArOR+s~bZ9Tv@Y=RnCC8PrSGCBNQSDgIB)8HXxtp{g4 zf3t}gq?;SK9gf%wiThY<%Ui#hpR8=neQx%2emb~o!!ovls?pESAj5~tx0>UF&lLeF zjXKvRg!g00lfF)FjbKua0@~I$vJVM=2{>hbt=D3IJBi8ZVA$o!ULRIIBqjt+-7u_F zMe`dH#XSZD6G13RtJt*h3IhelepjzBG1^w?DatI2_nYnXf7p2=>LDrkp$m{Br3Idf+UMKS%xN z_CtK~;b1^Ihxwp#Vrzx%`;i};n%pK>j?x5AY7if2yYB-vcsVojdzN?eAQ~~@;i3|7 zeLi0fQxr>~;V9?XWVF;3cLH$~OiC4gU>mBa?s}_MkrWJ${T<1Hm=bKO=*P`}CCV!4 zKBndT1HQh6@+4DkeSw?m*CW)?9@ps0PjYq;lO-~YPnS*gqiyf<^mNS=Us5x6VfAGr z!@;jfDSqr0o&05Yi;ZolB-p`0?|$P_i0Y?o`SD}svB$jqgaQA{_DO$mk9MH#IR*IQ zsiUbYuR_q<6aU-YU8JcaSK~ix6bsWF$V-!Uk_=%x_SEuG9|FdP#kwkH`394 zyacZY;!<|$gutFOhZo2sHF;8fc7Jhn^07B$p5H%qZ(ojJBx#y~=xw72a?IbSSp&tt zU@Y4X3#7eLTrsKRe7KGdzV;!Y#q<${_6%qxn%#9`7Z7C5 zdPwsozK#>uMata4!rhl(E6^vmnxPhTkiP9MJ0ZMJI}685SEd#mYb);oYw)ETZH*psjI8o zh|dnIPEiy8@7l-z72H~ZrpyG0$v?L!%5yM8_+GD6BMyMbqh?pbpT!fUgM4+asb01^ zeE2vgX@&}|KrE zo0HMDlXOgOL=_I@l&=>^xF*DZu(ljyWd^7G(vBL&t^h%mHDx=cN;SgDFXx0O4agpGg!pQk;oPbbnG<$sm{vzv(!{iHI;9!3J_Wz)m>%$)zOD63iC(A+Nfyn zswb1=RRF|u;JF*u^=HPw6z`SJ_n@@AEx}ll#RXJBGs#dT_smjVG**e*x*M%s@oLe0 z@=g+vyiSa?`)v0&sYuj@egAIDGnS<+v)@|R#-yDg&;36!AYlA&Ke?A$!b3~~Y7&pf z>l89ctZB(evKkIWFRg9ME0?Aw(XgNLt9~-uaFBs?Z^)Jup^jd<%4LApdCMC9yNmWaJS~RBQPn09+BQwNWzbS0|NP#)l-u7mk&t@1ceW zV4hJg(%u6A9oC1wQO+nH@%SAi{bH9b*4mDQS$__doD>F!g#`ld7FiKoSk-}J2v`-W zR%AQ>Nmtc9nm99|-!lrtY@eBK8|N4Ow_PLsr>ylWUFb%>VQgxHe_`}06T`a}Tp`{v zH!%S;Dvr9THs;WN%|7qI8U!cT>o>3-p8}g;{VR~V&CKv&{DAF}D#(o_xQ$f&4}P0Vi3*Wdx?=K-uC-_UvnTYOp=%S3wvf3`UE5s;A_AurW*bbN+A zaO4Wk%+5?hLu>aEOtWTIn$(fFtXo;*&Hiv^c!H}Xgi}3hQBw*MH0Z#YPuQZJuNy6m ze?o-Y@zvM1>1#(bW>~|KIeZ2=k@p`WO>);*&_1pQibr2?XQUZF6N>;mgWQhj$0$W_jO=% zztS=6UbLZdZh=haFm5Pu(#~eb848y#J)AeMr)2tX7gp?$k_WZq4P zRaC=1jMc`!FV)6J3LHO~j#{-xDAV1hSBl?~h{JoEb+K-OA2Dm1@%L@EhXkz2o@}f+ z!-MbaQy?G?Vr@)IePOQTPuxc&ai5t+{I&NM3x80180PJWEi*Z{uSW)?5~y{vs<6%d zz|DEG86vFANOR=$jrZJ*l;z6Nw~8oF*gQjgooxK}qrkBW2sOfigro`KyTtpzc^ls( zdlMlyb~?;}BjL5by`$I2x67IAw#Sx73Ag@u2SoH!GhB{NS5T1 zC4)nj4|;8U3cVOdh(J5i6ynmCcfYM>m-c6vJIh%GYZ10%86EWvSLi-oA zn-YBq7180l`|gUat^q$ukkJz9CiTAw8Xlnld%^J*hRCi(Jvnw^nidWkksn1AQj2##>6nm|;`Y4=&}rmhi~7vK&tN z4?!T%j#G^FKH(k5u`fs()u~YFeW`Z#1WZI;zJM-t~lC0N%ZGf zME=pp*zTI)&yq9*+j8Ij$@+1oMC`jqMOzJy6;kW0xNw=AjU4!uq`>@Wz}8usMH*XU zhMC5>6968gMuQKMM0Ta})zT;@B%=i;Q)jsTL^F6_W>mkl8U*Ss?w~fy8Ig9t_``r} zD2}83&5RIzzt@No<7j89-&L!>X6g?HL&hXQoOF;LC)iT=!BM*0cg{FijD0Qm3u~+= z{z3y}iG{rvs~ZW(Z~Q^ZC=Hmwom)$tncZTS>MbGo!}6+#jCsj zGeof&wKiURf+>i#DF|_NaX2 zDk~+$urK?un>qa$hj?2q1>YQzy&m5xu?= zZ~CEPU_w<&z%qAbbFtw_bLR|!?9g@H9lW6N3e2EZb{|&kJW|YkWXr@$$J#JWUKbt1?WX5=-i`mA%gsYd|2-Nr+Th@TPAo8Q%Y2Tm$#jIjV3@*& z=D~YlV4!H|U+L5dV!`srKd6v;9v{)X(`oiN8AIPB&!TvGo(o5L2El0y1Dd;}xG^*| zcE_!W+g8=d{YtVdgrl8~f0TYgu*>Ae#(#@$Xp2@=zKvTp>YN4*%P?N*UDcq%LR!j5 zWkvwk;w!-0`h*SR10Cbp$ht*O&xroUXwBZu;du<9V_e~4`L8%w`d?_iO~2Q~xiWGU zEBiN~)NSp?q*)Y$7dmwiZ>MjY(A_vy0iYy&02a@EhrJ0@W3o)U9jBt_lB|b^G2iRd z;-EIT2E7$@RQ7}qGyi=D(;18qF8?IMRG=!xSB!_N$2WDbArl`U>v9_yReCj(P(~#3 zdJ>2(>JQM=2Ab#MY{C;6P1e|R8SR%fpsLNWuhfBmja_upqDKO9jGnA~JBL}g+Ev5F z7eY~MS?A9OMorlh|HqsrbZLGwso)LMwy-%p&hz?QN~jG?WMuaP=Swtd&mVP_SU6!N zPOuBGA1l@W{d;L9#N$jO;jpaEoOM@yypB7%q@$Yk$}={Iu-m%l7#4d_>D@ajj?Zrq zm`crP%eXUpqLI=-!mCv(yT3K&6EMuYD{@eJ)L6UQ< z%&4eijA4v`{b0a-74=u=$AsM3=cLiC{G?2xE9?4(Ve7Jst6(I?V+xfDVGxI$+{ZLt zG6-EK=I}5kX7sH#S^ihjCvZ`UMnT)F!!N-c6eu_(EVYPnI9)=+M(>OWW+1$MFvDDl6z18ju52P(u;woXP zGjottI%lqC6N5EJxV_~DRxubyDe|Mq13u-!T>qQt_T(g=@I$VgpMu5!7VOKWn&RQ% zzB+o@Ma_t;ei7k^>$BDQBBh*X9iooa*7UUMyMNqyVU1M7RgFZ}KxCOmOv>6^q`8?9 zj}tw=bmuG@m|$LHDK{JYcmT4KGx*SQ9ppy)5dCwywdf{Wae6h(=~>+o2&~tF?p=HX zS+B!aFGr*`Q5*k@&RzG1VaMBsU94(~G)!!rtGAo@GyYY4o$0i#vGHRPtK)fTU-24Q zR|5roES$V}5TU1)1S^6+l>C0s8^$r+vF7;0IFWGo*(Z2-9bWgb#a%98D-zQlX-w|9 zaIN29cNVUnjn;n-9eD~IIiQ`X2`mMW$}ga*lOIU)9kK469UVCHa06M6G%~){8z#_4 zfm|mKuBqXVwkj;UIhO0^|8^_T0pFq=C>b-YwNCsP?wvTRlvzK>rFKdmnalUdnf7_Pt;n9A^FJ7s@?Ql2VwRA_c_Ft9ezqclHps?mLoTbg> z!%QNEp9%^3VUX3;i>pmF$;NE^=Gxq3p(<@4j;{y#gXImS0e$hp2sGOON8;~PjqIgT6qtR z`Q78e#hiwfrpgZN#!_vCsRoK=F~6q2Rqh#Z_0RT9-@a-nx3h_`73FCtRKV^f;72|Y z_X0RzCQo07)A?F0#nY^IN%xi(kJEPM{rx;aZ1RcC6yUNiX*R6Ah=wq#c3zX);hU4+o z0VA;nvBRlQ3`7_jmHqjvP*i*4pkdC(f0rc}iOc`)=s_71HoG)fqrw;CsfM0%I2+x| zI#|AmsTwvo8e&a7SAS6Jq%kk!w*t!_k+>Idi3p01F0fo8XIs*}H}SPn(uE~X{{T9x z(LaHY-a^Y7DaQ`}_4X&sb@jI2*eCp1@=(f=61H&S7O2#PFzdBD<8XfAV1i#uLfh=S zZ+i(94&Ol9z%;U2obTdSSRVceh$|AM+?_Csh-Oc%mqVBWlL}3!NNWxBe+Tt}TUPGS zl;?M|6SL;>cVw*e6%7zc1G2L;s6t!p9^62ORrxz_K7)_y9tvc;E*JA4v{@E6tD)b^ zWf%COrgWi^=B1qPHo+wLd_V3#2N1Se?m6wLcZ$03K!>vxs=%2oVv+JL%{Z zeC+~4pvLz!tN6LD3g+Po0}O&(u^)iJf%hPd)T-<(NfF|Nc1fe>6H1*;ulW~xQYa%v zhpnaozsfjIQ2O%0*8t~aJ&j40v7i4hO!#3%>9!0Pg)W@ z-`zz)%tMo#Fyz+8Q&yHD$TPU2JXF~;O)n8mXdZZld-CIz3H3W)3#Vi(uoIGT09KZM z@kct&ed{{k;z!gC!!X^nK`v>AmWj!!je?=&n0p?TW1NIUr{BIRrNuJYhgFj3|Dy+W z>m+Ds>*(sH%O^Y)(D@u0j1CKTQzPm?rJX=ESw8eRQTl8W@RL=a1!f<<(+iFZ!3_LV zyX^L4lGGI{K^n5D#7AFKeOX;y-H3XD&~n&esE)_pUS6*?`|-cwKU}(2+1ziTOnajngMZp0!=-B|1(p}3kykUpK*H1CUw!Sg66C*$)_B;fzr+dZlP+r;a*gFf!tU_;DJ0LE;z@}D}u9`0L6sa1ikS|;3RT8TE;CSt4% zKQ;c_slJ7<*PG!&)e2fgL)5mZ_*|!?SDcV0=Y*(_DLV)Ji`jxe3-~S1+S^yEIeOnRL`kz}%AHqb6rs46q3|JSh< zfz+$X-5d?(0{`|r(3YgOC#&8j2Ln!cESZ*{HY)Eg7Rj)k?HHfY+21%{-h7}a|MdS* z_Lf0$1#P!BF2NzVyK8WFcOP5^cXtV%;1JxM;0*2#gS)#0CqQuhc+T^ls#D*ubAENz zp02*9rl$7pz1DTFu9XsI?r{5u=I_lp?VbSa!K|)o;BIl>{{BVd_m5&KdAHAhJ|f_} z$3ST+@FGWv`9Qgxy%%Z?u<>^LB;z`LM8S7|I}kajYDW?n3!m5JZ^5b^ZeccIf`fza zjg{q3W8wA%ls{R+cKGMdIYNV*7|VE~8R$|prPCS2$+N6!3e-pp&)oE((de${s$5bz zvsfksh2{Tcp*@|2Ds>sJW_I!CYJFjb6?i>5IUL6&Pqts`9U4jNiM~|$*+ar&bl~9{+A95&VLvauku-ct zw6Rg1J>}Me!T$?B{~rbSlXU+Nnf^Z|dg$3W|2GTp|1T%zvm|wnU-t=~69cfTcEn^g zHERg~t92E273)t&bnu**F1y=c4O1+u`=ph9&dCslhpIkrc)(9xW z-h>V<0OkZKRVkMh)m4!Lwa^KOG-GVk6&0jX{lPciliy1+^AQm$^3VV{6`%$*)YMfR zg#NwEv2$qJ2Bg%B=^8OxE!EYnt(ua?Dr_L24tr2Ex1W75nExsi6yC~LYrw=OReE-iaBz;9 zK7QD-(N*}s%9&w;f#{Du-7C|h32TMO^8}r}E=k`wv}|2NPi<%{C1z;i=pa$aCF)K| zV7!M{{LIRIv^(+`I>7k3M+foJObOm2nQm2Ibsr^ZaJ?e|YHz**Pns{nLKU%_1rXOV zY`dpmKc}X+XH=ha=)q8ulD-{NJF6m_IY-qPKAs0}Zisy}vi?!)*1OBRazYG9AhR6*-@{ zv7MnFuq|IX6iyjlBILjpTEU`3qr}dxHYr1`#lyn`EXwviCeO@UZHN^!mkqa~s-u%7 zby;N1$oQr>Ea@kEIq=lpPSYqyYaypVjiaig9bUh#t91e#m8qk1stb3P^V5kHH);iq z!A;e$G-(K3U=qO(pp8c~WhCsp6*{4V1AH5RAq5I$vmvMgR6u;<~b zbf8z}?)ow{#?YD2>{&^@*3}=+H@k>!cDNn!DrZt5G^iO7$yc+iEfvjWgbaF=8J2YD z!-OQk$+or}Z%n`eWQY;F7i4sy9#QnA{$_8f#QF$sAC+zy$2cdCU=hTzPd|QGF}yU| z6DF(Yi3~->y|b>T)mIo`lb=k%gk#w#-11J;-E zdOw@&L?a>A2to%mHX(m^6wOi=Q1pFUnCT`127ulSKpL?p>=NGfo|vD)`6SB~B^$F( zC`MN~Y47@RYd$WlGAmp=8v)LOf|xSvRi|}YhmBH_k>rQYbi56|wU^H(itp??G@IG- z8K2nKlG(?+%0R^%~O6aK82UT)Vy#(E^M$42o@CaYjbB+7g&6Fcy(K76la{Sach>jR^t8u20;%B05Uj5wo~;Zl zg`-+d&yJ;UHw2eM;A5d0KRaWOzqDrMZ}w$T7QJ;@inXJ6@dM|mWiJc59R>dG`QhOu zJ(|L4+5*$;7DAfEIN@KEZ(*s0qjqEQ`E^;h$3P`XpZasUKw%`zjUr?7WH>1365>`P)0O#HKS{XLCxa)UE9+uqR zF8wQW)Q5QKiEK_3lO_e0*_b@4jb&1F7_X0Gn3C%uvgk!vDw;~YFRSvm$nqv9m$5Nl`^Me`N{nV$*S=P=A|>Itv;9p1Fyw6N zKV*M+LWVJS%2L z>Y|HZhQysr4m$A4|wo&JsWu#AdC5!lNj;L2}kfwZ3%lK0)S$A}1Y${|5O%e8vg zR4SqEoG0M+XCnL^VQmz$v$hDbBQ8jlxzX95a?5dc|LdTUnZ=NF9hvY7y$wt&pAbF; z$yHReO!^wJCF_=UeY_J6(FF{MLEtDe;bV&-+%Zyk`KWTIWjeJxDgNf^3)m7cFOe^|! zbN?_g~0;v<++kxnwO!VnOulHlSZ00cYjkMUaLBJiDhQb+5f z)_42gF~pXXZ_rm}@iD0*{Z*j}hK2C~RWQyNZV{GWT?`-q$h_`6a2G6{5;BQejw9{7 zpnzjWa4dH#bq!(Sq0nG#Jf%XQN`Uk5L|Y|h0(B3*8RZDOA%(HVAWN&XNyNTB*qiT4 zUHGGXS1nfRKq?95%$Evr|)3?eBaNbSq`OeA3S=V)2Gi6ol54!Oc-)Dx$SVd^H;H^X6el z8MR4?7rTQ#U=Fs6;mysy{I3$8bg-++F|>5mKeKt`z&}xTVSxB}GeuT2%)8vdd_ZE_ z10+1GNrUsuFu9QP*4H6hVyK`hXs?cZvUREg=rC~M`lKq*I3#7#C^u!Ov8KS}*X1DFW?}jy%brB^rEk&oUTm-y?Qbz;?Bd``ofC(ncuOEedBwye^EgH@q!+JuI7|_B=QB`q+^c%!0oLfAkH98QrKE@Z2 zJ$3{_O)~Ovy(F-nL-9b6-o_`!rGQW2&ceeDjC#VympR7t^PF9|Zlojgn0BH8q!_xj zh&kiOZ+4U`fg|uvv5oga)(RQ0ivI*!n@PK%=&pKY_c$K^$$apzUkdL23T(0#igs6c zfo(3KfSc76M~AIT?ooE(<2CW!+$%*sStEc8}F<>50(8IVZ)^`ZYnUxb=V=)kVlh2On zIj??`vj5X~WN>?WuRCd*J(N$Jr!0Xy23VHDX>!9Sw0c)L<$Zyeo z-`^b-MH7j%U^;e{(h26Xth=^PWYjmDmKeD98hjr~Lp&B_z7k6t@1bqiaQb$3!)Tguus|ypd!T1N*^QWcT zxF~MPHS8judxE;Ir<_hI46?nAE%L3|&lIVJ!bA#U>0>FTcbuUu0?dCD*ic4YWJv~) zrPfknfA4NE6lZe9M8rOyRd_2+(SnZXcbB^TI6BJmf{~1djv-9Gor0q$eufwgB-Kpw zlx7UuXC8juw?cKHtPI-PHSoOzq4}_9dQcQLqO9#S?2n8mp9q`q&+}r@e&Y#)K?t`_ zmXeneNMsVm<*%w$OiOqsN&+&S*j4OViM2`xlSMbNqA^}QH0QcY!StB6*2Xa@DW;mN zeC+U$`VEiwkPVDUVdowlZ3PUOO>>4$?)idmAl7KHn$h5a-BW6IvU3fT{ZdgYi^1@x zsrz6?I{3C74?3H^8`j7U3$q%)C*9&~SeygXCM#r1nySnvNL#Z`eIEI}B$?^H`7Xmy zcXDRmm5V;IqL**qHJ$LsGP*6r%Zk+ueYjjlo0`~t$YwaA0Vc4zuP~yf?VH(Z!pLD9 zYJ)d%yYIPo)1@OR{l;)qUk2POS#F{=^)e8KXRSUdnwJq$M-D&bPf$;G#QlxD}`tpD7ga$Rw@{L(6pbpX(qB| z1f04z+tADyPi!^Db!g$+IKBD!soNv<0mf-f9D2IBSK&K+hvUV*V$@qHEA<>KYIqWz zks(%jM^QQZ;$o>8`dN>ELk84WeV9+oz@PH$*~by8i}bn-Gw#L)N1;KBVknbA8+~--x~2ppD8|A_t(|y8wHxQ z6NXk9e;&yF8L!BxZ&Sf@uprEcA)%ElO^vk07G@M%j!=;%L%XA^Yt~wRtXMx7p-&t= zmf*xuZek!UELP-T6^Smz#tz6cw9@Y;#mnhcDk+H4cq0Nsow0IQCesGt$4Wo_HeQuh zoY25hX!6agi5gNZD1?e3&K7r$wb0Jf;$;-@?~ao%^R2`nq8F@$(OC-0czPxoBgqaU3rQ} zTf$vJCE;~8arV<5iVH1R_z{CI?V#}uNy+W?X6PMBT6DpdMh_1!S(%U-QhNk0on6l| zF-+g{n#?0nyDTN#%7+MZ2~{&4HOm!*igmbGN#Da#pvkHkUBNoZSWR2IDDtrV4z2n& z67;^@^YIe&b`;bLPdZ_E``wIkizs2_qOI(}*KPd!PY^6?+;=oICukIu^%{BLZU*SR zy7Z^Uy5hQ?2w&jMLZHUQZ{PWDZ)(V?EM56u$!VbR2E>g^b7l|Ibsep|<o* zc$8Y6omDemH|*Zlo}X1KNul*$9^~g#q(PPKrRJbBbz~k~8vWMwjo}Fc0l8?7ZI&IP zUMOP)`A*TD#%Iyr?BlF9=($Ct;Qc}+=&hEwf$6J-7O&jTW~Q>b=A=&gG3F)uSnEF| zE5SOE)a|sNB2a|qz!D0GwqV~IrD& zNpGA{#6`eEf@S(^E~K#_X7ZNVkZ( zQm!y`w}zB`A1ZVuvvrdu>4sE<*t7EPH8GX5g%CD{CLr$DXe(~csVo=h>3+2{T7;yxO3ePxm#s&zWkb_IbYiKwhj=AN-zZn#wz`t-F!cyWe{>D>1?w`Y zhY^d=Nyvab-LF-gaBBLE?Zep-qmDE+HCZUO)dfCc!Fg^u|NFT9Ee2W0P$Zr2^jn> zP7J;KZttk=Q>a+_A8U$5Hi+tBLb(naa3?M4}QS)Kb;GCw|`&Vg~; zvS_ddmr-BD+eAi@io1gjg#7E^;A^56X(j_3dFQ6)*)P8{fHf^f`zsM6@0lAKg`mkK zD-#skSDZjurrL&dh#&Llhey8>5zbsR>PhW~@Km6!q;h}2D98VrM#{7O5W-&#`oN7s ziTFJ=-DhL*kdkJ41@ZP%qqJznw)yu;sZ)<$C7!I2T#^Paj{{Y|l^r8*>X6;Kd#!tl|V6Gk<>Mv3<^hI05!l}rQAdNu<9XB>k zbR`vtB2`j-gX-s3dU;O;4yskoM)78PKq|5R%~rp6`^Xm~BNNWAJCiSc+;Gq^LTGrs zo3YX#e1A9e4XD+V{5G0_Ijl+C^hTcuNDlwQhTC*_EyPD(&b+&Fvv{g+z1gp0%0!qS z@|AVeSJtrV&fBIXUEhy5F^*Y|)r5{2`;PzyC-*4go>_R|KW~{S;EMEmyUCjEV6{^> zih`M72=iMUnK-_UCRcUDR<$Xl@P19-;iexEh6J9lg#}82?6%9g>1@hA0H+G9-*L~P z%R35e5Ao`*kMc`-?_c|A^w-A@@| zgu(%n*`0JN*;-KlfKXSP89>wz%@ODMcye46pKf)}-fXwiUqW)K%h*H1dIa85yTPKE z1!A~a5UHnoPj{7P4{!4!^#i}A00UhpJ|H$!;w)a#MxXVrS`Ao)yy~EK4`O2x_n1gz zUZS*PrCdh^3zt&}G@*gSmF1~=^-49dpDoS=E_RTg+WW?GD`4Bozg5z>-Jp|oFvmvr z`d||zA&9V-stzCHSNAD<~z#wwtqw5H5uno-(=!_yek6q|;6LMlL8QAh zMlgs>CyUN+hRfUhQy~+o%7W`0fKDZ!~+PuYQN8C61 zUY3j$0H*RB>$pNOe#0{>IWyet=qBsk9bbbW%F`Hm^R-s(`QH#ZQJ~?^dfBBU0r;vV zO2z~&FXSQvCs~^1c7Zee3XNIAwQzexBIRmX=h14oYJuu$Ed?gT)}%e z)$1B1>LT_Bj^H^O?F6kk0;5Y=KMq)El>Ie*HOC@^c(9oUC2!GwUclY$XmXjcP?in& z2F@6eO3C7oLMJt)72(p5TQgXWK*B@DXoC?dpq|L$p6iz8?QXRV0WA3Cj11R#x^T3O zh=%OeH&c;{Q^e}CS$J^oq+Y4*-94PZEVvDXkj{;t7h8+c$J`tPp&2;|>oiI29uTfs zSGj0eZU>3JsW}rlJ_3Ab9$VT_vUeG5SYG3N5TC)ZW)z^0B8RW7XYZ*2x^$_*Y3Aqsl0WUZ-|h7>PU z?ViT5$tv z;F%k1sVh<@WzrPRG#bl>T9hnTE--U}hSHdN`XfUYTQET5MpU+CRmoP5Xzh8DHm8QA zt2oRN-Oa~I$yDc!&+rDNs=|zwL+}2dit7nIkBCyxSGshqxZ^!BypCV$7RA0rYE=*fe$xk_u7XY)e$tn*Dr}uIe^>rbfkdhQto+~aurMp8 ztpixZswFu^AX~3lgd`4i|kh zs@XO9v8gy7vWc*vr~hEqhcE7%@9*!MxjOHYh$cb0>AQU*KA*s~rT>`na(wMt_r`|1o^_4;B)CR$3%lanJ5 zxL+5N#|!P_9dEI*@R|00`EzWdu?`*dFJ^6hWd7w%)+7?vn8eI%qox`Ue0zTW3K@65 zcYgjx+{2|pBb?dg?&k#Mo%CpZUlHjH@Bam_)~fq3Qqr}5@gU(N*>5L4{6ZK4#Gh1} z_`Lupw$~_Z*UisgWWeKouVi;<)h_L>Baku9AJ}9l&^wE&Uv1^-pGA2DrF|sK>3ZNB z$?P2O3{S+)hjkpN80IuRoU}eLwMs>><}^9>eB9c1C&a>r3u;*{dd(G4Uv5$+Og8s4 zJ1XUM?K30!TrZb91ILqc-4yXW-oE9x8!6By3E7V(W`=Io;WB44oXKl*Kf{+k9TB)? ze-Z$rl12>Ac6e>@@kfA7*;{aAN0=7@WFmP%LflhUi=+gnZ_J$AYUzMuC8MI^)e9uX z+X&wbLIuo2CS0uG0={pYIHG5%5d;6BGg^CHcs$%tDNk0_A6oKqIC=0-k3ep=p+??e{Ib5^DugYDS5BQp*yCrjrcpjGmIluo%_{c~;# z1cas>jwht~S)Mx_!mrPdY1`CBrXCSg+m|Fxsa`y5B{B4k!A%HVw_hx8iY$!fatjbv z*?Wj=Oc6H9^;QMy3q0qh%)>FBu}!<8EEnKFEjwoC9O=G?#*vu#&l0u9Xs6ai26otO zM`FUD2)3sZ6X?W`x<(xTrp`D5eS=}y+R_o)rfTPtZDSlOx)#2eX{@8%HK_{cEu6FdAGAO7oRxF0?-fx~Q859cp zgh)D8wVUUKenK6ft0)8GHhzJ7^vtb1LT)}{+%i&({loC^hN|nfZ#q`2ipKEs9s%!) zy{4xbS<3Yot0e!M1z2kP63=3k*~QHx<;Ba}<(%hz+&wkhQqz{=qhm|L{U)0}J9T`2 z8@?K^-H+Vgcan0#A}(wM;Dvu_37;bxlOfGV>I3(I!Q8KlCFIq*4kG+jzos8RL zbfWf1c7&}-`aXE1^T(k3Uv`RF@3?G=odBDXglx;UA?8k( z4wP2a{BPkqO_>Adl&W+pCFz6y1b5|cw}P#m&GY<@6W-~nDp7-@zKrG!W+AIxIK8@C zqt;c(4tBmBfq^cX$N|&Sx2RU-^{QLAf3bWMU0vK7CPPvrSyi_hS-lxU* z4v%w6duM-}c!}ypVVmpQ0^8Z=VK?1_jc7*jPvsIv-e^4I+)1&Bn=e%&PgZp!HGo^g zsC6w3XDENaU&No!c)xg?^;AM!LUTs9yg$ZFp5Xmse^8R3-*DoB#la=U(ML?6KygK_WA)?Ik3=!7feDVv1BH!(m(>Xsr0A{sS*`NNmn8s zfWCre(afE^!V+M{u~La2{Zq%r=+|dikBsbjM-Ni%MS7L3WOP{Zt*nO&Ou z9aeN{A{PCsQ!2$R(2>7kB&;EzI|&IyFu~r(?1d0`H_D2d8XuuHl%I zkI9XV4xxH->3vtZVcs{k*@#VlVzcaJ_qYa;h|T5%23VUxw7Jx~7d&Ko**$4&h^$ML zI`lou-8={Nn?3SYo~LdjZv3&^kPU~&Om(^rLPjv1Y@|al3HQc83l98*vc|}s{d+p) zc;~|X>pR%87Z0uJlA_Gl9#!UJ$ZduX9p>4(69}u2HZ5}LoT*I*Ql6JtP99AOp68c| zc`aPzI3M-egh3@4KnPjHNROu-Xs`e(DJ4+5@?{qS(yyvl*Wy-*?Mi+ zqW~Xk1`9`B6629lzI89z5hF-MdCbDR$>k%@`2ITgT`}jl8<7LSJNCC}E)O!LJCZ$< z>-N@53OAuU_aZ`pXixxUPjiwSe5B56+TdT;okNZ3r#G{)8E}`>9hZ0d*+>{#AfI2UqAl6*W;|Nl>#W{nqY?|`wWZa-OQnd{YvsP=i!q>JZ|Vj@wOLg2#~$GU`8`PGt%u4esW2q`Nu_J zn#~e&u0rboWE_US_cRbOY`65H9*zwRE23F9NwRlO%I*q#ai8E=dp_jDCWDjQv~QOC z&daTVmJeh4GCz=Z6I^w|xnd-QXKbq^T=-GavpJw_7ylVFvfDZ7D?#i>)?p1&YWTb1 zzGm6xN_$y|QjSFi_E?fm6tV-kTdv47(0KdT26wPa@d~vv|1m8Ry@Uq+CIH1V$Hk2_ zdUP{76^pSnm|N%|%aO(NPeQls$`a$ORUVM@nsq^dGxH^W!xK#FDnZ{`HUuJREO9cS^IlYHGu{PjGyR!|l33bnRosaZZsOb73fI3B##8?u9Qti~%4N%@1E#wQ z))BQ;!SUNjgyLN~G~PkTZNDd&Sx}Up2_AgO1p(K4NE-jWE3CW!E4jJ$Jhdgdw{GvK zE}|>`sMfRJS>)uO)$xCB|9Ga|)1R>(lu{ihJsTzOiAHa;s&Fso|7QQ?WYvcI&Z5ew z1m8Ht!6*O^o?=Itf1mg{z;72uNrt5TuWn0}rGZQb7SPvZ-QYekmG>=mAG&NZ?wJht z_K-w5@8h_??z7h~NAR4@D-vJXocoN47tS&&dWLN{pt58gHp9@{FC=su^N$IZ>n45ibrKD?zPSy1>+I*>wKA_~wZw8eH3o zD3p6;|BJvazqkY(b2YmaC&5Y6#I~M0uY(TaMu8jcOBy{k1~fKB@lzp(>*;-R;E#b`$Coq4`%VDyX#*8 zfg-@&qsCjiZ2(d0uL^R%Zq!xSD`Gzey%l(X_ulCd%N?&hjopLL{jx96=Z4p$8QD0y z!yl!pDx&I0UwR4pt$K~Gy@nFx#|!r|-Wq8Rq4PRd!am%9Q6q5i&A1`622^QhZD!GD z7e=BuKAMZ*c3ynxNbj1<-Q1sW`YF8Kd*fpC}tJzUd`0Ys|pR`UqkGWA1kpY zv%?6iIjj0x_yp3S2f=MEuDqSH^T;U*YMcZL0XJ*?*xEG%fC$V9*kj;!!3PrM*}!ag zU+s9g)iHvn8#X(wJw1^+!rH*!DEKx?N4f%HQzfo7CX%ygMRWtGiWSxuSgcv|`F`n^nDvb2T@ROKT@p$P1WE~)Ykc@Q| z+(>s8pDRZH<9OsgB-8m4Vt~6lYSNrtBdCI7Se=m5oE{mjt=+7lG8H=T)$+*uiQOim z3agqaClTUlee^97lMH1e*YWMzCu`towoB61tbG8=g^?s1Fw$H0RP(Z_ykiz=E*fU~ z=xr-rCcV*Ckx{)is6=D^9~QrB-5f5Bk8z zhBt}d(}X384xkR%B4jSrsAEIJHwdw20MfZkFp;r_$`Hx~G@uS}^n@)Cd_wt}71^lh+Yr>CIL51Yg(Qiq6eMl=-$AA2ov{wFo+axH6l2 zu!MC+&8Z9)bqASm-Sor?EX*PB!Uf!g>UDT*^^c?RjlB9l2p`c)ObYuz|5hC**6sj-I#oQ}dTPA47rOP8aE+%#fh({|06@aCn`7EK>$O zh|LxkMmuEC{}O*MLwW8n2scNVA>*mwFgKxl8}`gJg3Tj_M-yRlecNd}Ev|)ZOBm!0 zBWh^g8eyr9>=}d6+dl-`+CApwAIj$49$YT%u6RV-oY-a~g7*=_0&$I-RnPru^&qyd9Y=#K>KP=>)0>loun3y?l^)3M zAtu3laOc(eC~@_;28SB599-Jq5@YORvP?KwnB=fAeG=)VNBQ3{=?`_3V*|5Y&h7&i zGo~7y%{(=r2iN@JqK8aM%EzC5*;1Zhn*B$rXgsoYV4 z&aH;{-{pL%-;QmWq;Wz7Zp0cNcO!>&N3hUU6w@d~yx$?|O{0{){yQG-pF-`W1;aZE zdQfTa8_&jX!9nmBwQ_7+u4c0(Q9tYIP|(OHk!2TIh6(`7%K(jWLGI}g35`gT`X{nF zgw> zodw5Ci1x-JC6X0X^|^Z*G~~CH#I`0*LwVl?%O%3A(2EZ(*5`-U=H&RLYgsqo3~MK6 z>U$Czp`^`!#SLUV-qobA$<8*yV|BNahKw7RVJBW(xO66bs&q`qZ*2S{rYFUkMz!Bs z+c|d2n|;MO z;?Sv!HE#l2$_g*tboF4SQ!Qo$TiMd0`^+`*&?nsEd^~T`%A1il1e_*zqNB<vj zCdR6hQ0gA>J~F2F6lZ+Y+@A%Z#`LEx4JWEt?*et?n{mc!J7X2o3ai*L%lI(-xq-s6MQ zIqyGYMB?wq&&Q{srIypi#OxUoN8$C?lciR37~%6gIvra&X1vkcwMoLc9TBvf$;d&Gq^qOd0wxrp_AEjC z&2oiij_@F_Q?#HkH{`P~dp5#Z33ZOHd`VG5S|?IbTh<8_}O_WT%je zZXAYi4v|-#!AZ>SK{bk<=ViV1kJiwk+%}M<^uyntztlhf{*}84h&sm*^Zd=2IA$$R z-9GF^-eD*`Bsynn?<}eL4XCu`D5+flo?Fh{<8O>(eUefm<~arb%ABn7m2zUyv>;C> zvb>7o?PQ7xE7H(HpEGFORdc6zNx-b#Q1Bm)g-S-PtLvo?vRQc4&B zQWnI2#@sV=k)4gpQ+M5aTg1QH8&_%GcE}*!L20(N7Rbxj&1!`c_L6=_?04bfz;;9{ z^#R#QrHgbf2cL_46yJE)k|7n;lB-2LKkh~ewD9kxESYZAAIHm{$|fN{ZMMxtYzv8_ zT(nYPTZd}#a^*4L3~sn8CzAB?l~7)u+sa*xLmm9**V4XUQrJQQ0Dgq+sUkU!b}h*2 zsA3?ArEx#RJ5ydJ#pfZ)6w~3~GSMnL#&V68+G|a07`VkQnQr6q34TDun@v z1Le8HgRWXU7n!9~i|kQ~%Mq^Gt~{4F#;JB6e9NOV)Cv6nGh>7qg^T=Mm$EKRJBwAO z(4dc3(@?9c-MrHTtpe{dF=yB}N4&a@eHH}%^l`n@bqeyKzXcaD6#|M1E)LScLuT7_ z#O#!w^1oT(gyfxr<^zK=4|I5v8iSWDjUf|-#5Y+bgyaMgv4?~cUVKoc(Sbdw_6=Bo z_K7n+<@01S2Xs5b(=6cTQ%sVF(o>g-A!LHZ74_-7(;Nl?bT}8%C6n5}bryyD;oiPn z?WM|?&xB1LP?8_M{Q<%Y%OKhY`Ey@bZPdA`TBjzmufIuhWK!NO4i`*$lXrRJsj6o=h^j|SqtOjr(b2sh8{<^a(ef$pvCdJFJ?y9Xp>N6An_2BZgL4}$d1b&~N*V(hatt7=M+k`a&ss2w(Sm09a*f@CMSYYs~(gKW&K0Y>zOG4_fKkETPmoq4Jqf;EfW}WN-;t!QVz$;pj|f2wxvVQDkvp>w%h%& z3oy7J7;d!>chZw(#MW3UYa~WhXVj0yKa#KmjFLud;WmKyZ7p~R6YnfGi`=PmJtgn0 z`QZGe#Ib-a4=tcih63Sgp|(ze&lKBtJ0+3&IB*E?y-$5H4c1lL>8pY24ALdkTh1Y} z1ivN<)B@Z4zDVEkveWU-veV~X94)rU*zmTTOJ`9R!noNrrho(VTpXVp;WS`KHToHx z$oc}}0uEq?jhnQUdfZxUlQDL%g3#~XB@m)5Fk{4jif0ib|M@h+$7BhT8xA>s6+O@{Sra!FZy_O8{#N3&$>{+R!GZz0Yw@BoL<$IEiUo$2YUje$>P=U6wt2kI_MlLAG; zx_63`%W2{Q#?M)^-^yvH^WlW2?%Ol|FJSzsRDDW5Bf|VH- z^h@}l45(r1NJ1yU;rXI@uWXL2KezVC3c$YHUuUyWv^Lkt@iE=TpAJ+}0M^m;g_sRI zwZz)F;`40DaF*}jHh+m;Z! zxj7*3I9n)H>4R1)oBq&9*3sNVRxnyI$BGn>)GBrG=5%RiL^(|C@5ryM2?=Gs|Nb znh-LaEKehOXz%U&zVK4w1smT<0txhhe^hWwGn?;HdvIP_lrS`hN>EBcT5B=bxTaRz z+soQQY6=Q0wrffrCemL>tOJGBE`P^=nvD_>BT%}k`GN2iK#;a+)$poN=h#lOZk>%X z>-rh<8BYY-Rvn#y>{SNuea&Q(OeM-Zt$yK_Jp{>xEW)9$4nUrZ#n7hx<=CnH@gML1 z{H$r{{}BfOfC;?6o?1syyv*l)VE&f{IJXj!Dv0~ZIT9=~TA(T;vGOKvItcN92N{^A zT>6rqln=x)@iai2FiFPnPk{R?hT8K4Uv3)8j0JsehUTZcpNW=m576}%Or>z-JA$sx z$61vPf1fVk8VhGZ>Oitmo}yC-3`{3jsi*|F9%|;)_`5zdOuJ-2EtpDbk0eg#ZM_{V zY-*si`yD3J=6%L!@h%idct%hfZ+C&PbWzqARdN&2uH*|*u|ajWcWGrc*|r6YpVT?t zUvXj@pjFgabZW1IHw)i3uC>OAZ3u{ECJkD%ka4bu%CV)-umu`Tpnh@PLFO zB55c4dCR4jHE`|$bC&>n2%{fKN{xsb>iv(CAZQ}7%gfo#ezp+NsBs`A z@Zl)=dFjyKB?-iisJi*|Buqx&@0clH7HMBF6$%2DG9`%t$v~+>^{>AZR+9m_Knk<% zgKM+9}tG zVmj#buTL{}E}6=Dt18J@On4iON8!&MG&drXk@Gn!o)`G+LInAGq?w5Y){8!A)EA5@*nRC{oqWIq&tQg#q}Db;z^EqlAJpWXSX%RO5O?<4wP z%AVVadgeLzrOjo^hZOGx!l$jG~R3*c!q*sO8mtC>qo zCLBi5;?ScyJnpi>yrQol-6Gli{Xr|n%f!cxiXzWq7GF-tV#^#F=n-~DSOn`U+vZt! z1aafJ#M-aJ19_J8Wm{tbjH!FvXR*lWyO_x4+gwYuiUv;e!&_xEp7Pg*{{JdF>!>Ec zua7$#>Fx%p!9WK{Ne&4WBt|p94HzLIAuZhq2ofq%(m4c?7>sg&N=QvQB}PgJBVPXe zo%g)IKi=oB=iYOl^PF@4y!U>-_gsI7)`AB`#4jmL>?UeZBsOTLml+k!tXr;r^vInuC_Ng#w$A;Nkvite>}& zzLRBz=p7JP^njbX8piFYshKa-`J-^7(( z-Q9S*z^`+5`$219%0YcV{)u-d&`F?Wdr;yqx}TVzsv0}wKb`Z4gFB-mT_CnvkYFt! zIfPmrM(m}Dxrg=6qXmW>Gt)GtGg?{NnV1lWUdSZNr^RAu^W($y!)TJKlq`CIcemDq zf?SyAaeCmELhNDloYoPk-cQJ{!uNQy6&Gii6zX2@mxo5I@iq5QV5jd<}D0(;K6zI<6j?|Uyx9le%bC9zb$$9S6Z;w zT2gMuGRnu>uL)cvk*Otwu($^kfPCK^K$QbA%Kw~G(%NJ3rK>UDRKS$MSvhq7ZdDH4 zRf(V5H+pW;l_itLiQ;txXZUKr{Y{%#&2@nwK9CtK^q!?V$$u^G_3}MNSH8p+>z{7) z$vaX0j%*@OM)CeUD-n#8mt>0>??BJXj|^IT8a%^)_hc}=V}_l-#rPp`AxG-P8r5?U zyyLbmNj$GnFooS>rKd#xV4Ps05=Fns0FfL6Y1sB%6q%R5f(4VIHh|;{WKVvyD%6cs z;eM_`M!s_wQXL-TnnaV3$djM42Afw2iOK4V)Bb& zc4{hDm-eIrhnqtYThxfg6!j6G4Q%{yH2dJKJyS62T zLbO0##S~>o43d$?FPe_eB*Sw z;@d>Ry>Hv%%wM|~^Mpd3-ZBKAj$>+o+hN|XUR6gA#eQbz{$i5yMdL_S%%JaMd7_G0 z6AB$E$SUy|FYd)W%P@>hp`l>a{ZzloQ}6jUaDh-AztXfF)+|wltji!li4!`#mlyG_do}23v~bCgwY< z7~|s5Bm%8&^x#g#4nu`5IR_a2l`$pHr%5Bld!UnnX(!tK>x9TCS4|^|1vW{6#!n_$ zSn{-B7nAp!lDR>?tJEXaH^b~(X|_y#-MbC5UXxU~6gf%j2hNwJu-FEq_Q|mhHG8As zJKS_=l#Q=gMBF$7j;n-$Sg+KUW#hUfBe~EF0!5xgMASj3;rr&KE=7i8tPT_}0)@mi zRp{dn&;QEhAba_#r3Oe;%A&xp$k27SRL?@wf zH_C~a+Wf%`o24!}7E?S)DnN)g7uRfM{Rm6_M7hcv?Qmax)E&Wr6Q8JJ=Km3WT2rY7 zjyAYl;O0*+0qL}m!TUtmov~~X5Irk-m3ia`KbD&|Ng4TS%2SwnxG0epq7ZouB<22rV;`;HR{L()4M^oyP1mP^Fq8m{zB#*&S$1=b= z9}g~br~UWKFb@$JJ`r@}R;6N3WoS4GY6d87(`y-J^Cnj&2IoF-i(%JhlF!k~1HBHz zK_=nir7aI#PRWAweg6T1{$_Nhl6o8&0NrZw~OWEeoKUK0fakwM=spXSX?YYvYNQ z(R}XvzD%Qb+Nr*SU$Pn$7u2Lk2HSfQbGC*2g-Z%J)lJIvHINlrc`BwW*r-9*p1865 zqDPobO2L1q=2h`vgBQ^cK6+ociL`?FBaLo}$0GFPmQ&n}eLZF$GtN+z5*k{N$Rko> zy>j}H$S;bW+ApY~B9s@-LU57l$?9ssF%3YFiO!`jC^Un;xOtX1PNNlCfxXrt#_x>Qwq=Y#p&_!ju zA#9gUD0=Z1c1i5ik2QZyw{}_GNo3GnwlZWL2f}?kDz+pna8J$VDy?u{kA5HC5L&5k>_)>^fW%BLbPie)^wji=jBW?pXBC$GJQz(!bINYV=MaY^!Jl)Z|Cj4 z4B&Jps)ol5u0s{|avTY>Mlfr&7d_-ww zFdgS6Bf|ZXjCyG~3Va3?%2{O^VTj5kc10|C_{a<>cs{zu57mq39Er%_xuUDBcsw$> z;SZy*Xdk%1-C%x4!%@ZNi+qza*Sf(9T*yvmO5OOo$o@WkK!-{$!$w|}q%dv?yw%r7L_5#1$(Ji&DtH%=1 z$q^aMai3G$Zpl|y#5)h9^Rs*~PU`TS(|MW~``s?k=g{v1F=Ub}_-n$=bWYB+57>G7 zCtgvuFcCimUvCnaxufs8B_gDJ+ks4o$VWR}^=mjC^|}PQ*>Rri?q~3M*Qs&3T-wG) zIgh~*_h_EYgB!z9+NXM}xw+1UR_9~^hM|W? z9E?dyHZgw`^L$=zJ%3TQIOxoM<8dDf{;uA-G-sHnObT04(l766i62Ye#W@?*w*4HR z@1WY-3p+cDcnpNqqWfQvwI7yyhE^z9WRkI71rbSx+d6zEs{J?{N;DL<`9Pg3N$EXR zvqVg;D zj3q-)%)aquuy7gGIeQaw>9%}e4;FpNLEay7fPL)^Ys)&`ie=?o#Y?gA(R8c&Pee95 zAJm?`9B*wuB6=Ss)YJChdbUqN+H-d(`=LWy=^zJ5Z95v03Ad4UKyRl%e2WuJB`gx} z#>BR5oRjZzWdP?3e0n+=nL*`vLO=q{bYs;*MvrmD(gBktOFY5it0jqwl@FxDSWfy8&8f3U$G2eZ~}J&Z8|fa zN8ovs)~GoY^Rb#9Khbh1%Ih+;d%0&8iGvdK2Vn8=EQGDoySO(1Bk3Wojb^2aGH<{6 z0_64E*xy%2G0Kwu-yD)5tB{WUebt0(Y=y0FhC_H8 zzXk{H&dfs|DmY(SiR#bI6dlaelkI*M|B`4ezalP!tnU5Tb6_}ne(*3lEmYZ)hA9fE zcj5ZC9R`O`PR%mNYQMIh>9Cpk^=NKV63XhW+KipAkZkPU(Pmw?CgP{^BXf~g8xN;5 ze5)V8i106MUP?ItKql4i2IvnJKNXT~t_!pPR3GKdO}r$#Q3)(HDRWh%?~ZRv392VW z{P>YLmD;lN867DLZnfcb4qOf)z(c%{ir>>>mbnwi-$YUD%4$!3u(q=0nfTn=3LYCD zglUP&1xW0iU~_=#p3$YcvBTKEO0GmYPF))o0FK6Li)Rp_TbV!nbx-|qE`1I1GjRo- zKa}wvbcHq{{(a%6wVb@aK-*{eXrAX$x<-Q9DJ%#KfG2lKy64(zd#EjdODwp6>HxN}%O z$b0j=YtY6{{Vnske4%5J>#Vb}*X7|8nJAkxJO)7s6nrV z#tC5DlXZfc(^C5+CjCRDpn)XzaZoJd(=GNi|A2^OVmN`BudFI(*i*feW5oBpOk=A!17sMK8FH5EN$n5`hKSL`F-$t4aAE>xQMvq|p9BuZVf`s1uA zhIQNm>Oi%^%73CB0Eo$p+X}U$@WCu6jZSXI7SG$jD-^4S#HZ7r)e~F3{$9;nJ@84B zV_8HBOX|iV{ic?Js{63&Gof!wvh5uwCYR%?s!b+>sPu`-Sz?u%T0V=>NxeqANX$f? zg8EkuFUux=wtB1AK=uI2Ioz|{0n+s0a#2$g%nkAe^b=NeqR1A$Ubhd@W2lHQs7yPP z8>d}sG5{BFTAqIHsdNY%WCzYr>LuI!W!@d`yuwrEC>{jLy(mCM#{nPOSfwVL|3roZ zTPN%t9W65oxuvq-fZQHB3OVe0O`ARhK@k!h0Q~`E`mfHSc@6RXwtR1~@zRW%7 zOs*_ z{XX}#QYsc5a@ryp^e{`)g@geoB*=ODMbaAzpkT0XwR?yUv&YTtsOtt;dx%w7Otbh8 zTut!*39sESZ2vcf_TNdk|IMrYmq@=GrMP(DmRN$?K{Fs7!Xv<8*naG$&FU9@bdtcg zL2o(EFlH@!^`0M9E>yyo|Bv@<4Ri$nk!-oR#D?tko(>Q?|FPKJzG3DbqQjTlR?XEe zMb1gfiv{vSPJ7#8J1;j0+;&vs=_9WP+C;DLHE+}|&kXmi3Q}Vw36mc`ytEOo?v7(4_5<7C_J~mnp#(me!5AgMAVS+Kd;;IaIh&foVxKw>Y3Cw& z<}}lS;@Pi{4%bwE!bd!@__j0H75MedV-k2swm|UmaQJD!-pYh}dU2Y4!6wMBv+HIo zMy!|W3YCmkR{O_u=S5+v=AZ2kcTu8O%c~1ZIZ|8uLIgIJo4W4R!JZcw;OCnR%&}Ym z&3_vKZlzT@!I2}2ufzXz8$=4mDGIF*Zzrf8>=MFX3)z=zNiEHh!3{5H$Y_jKjmTTj zzjW~l0ashE@4F@`_4Z!vf8fF9|7*NHxgxkAp2kh2hZ5$P+KyoJ*M!bKwZ}XKx8K{Uif~0A`u6>H10BD z+cLsggn=9}3jZeRMt&C2n%6HXi=K?bt}i@CZ_IR)k;*@2Y}@CaoyrO3H%i6vp#+d2 zzDkY2*Qu=7vofLC5%$qO_F{jIiF`ykoQC7>O^%KUB>j8sFP80tPQx#xmI6yyk2xbq zFKC?{htBK5KZ&aRtkLK|-vjg>zm2<`72Doyz0S^skf810h3j2q(W}OwKXZFw*THHb+a5wzR^wj~4k@0U$65u)bHt11*MRi> W#)iJ~6^~mtM-TEuyH?Xa@_ztcFMYBA literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/todayeventswidget_preview.png b/android/app/src/main/res/drawable-nodpi/todayeventswidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..18ed45db1539ad9e548978e980936be6d10f1495 GIT binary patch literal 21401 zcmV*OKw-a$P)+;~|?&0R|;^^@I|Nr3k z>E!PI|NsBr_v!ll{{H{}iX~_#a`w4?fw7$y`1$Vg_CU>aV{?i| z&w1oUDb)$_2c{d{3^#}$MM&t#{6Dyhqvv_Hh=c6>c#Hx@;-$2Fv)3zvHHO8 z((m>E)AZnt;jB~Af@pY-g5RZO*N^P`@=VZucH5lJ^4xrhoqDSIbb^;S%WmlE?lXP% zvbe*h=)Y~*l>`kTp{KH?t+{cf`Ix@@ZG4iCxcVX~JK^N$<>%{FTWHPS{h#K!-r?on z>;BNx+c10ftFpdVl=#Hj{j<;fNKRako2*=y_|@6pG&@U}qO!Zx{X&QLW@~rL&(#_s zGrPgc)a3rz-Qumw{MzXLiIt|dy~l!&qF8By#mm!ndWF8k&S0AOgNKn%kN0e$`BRek zM~e54lbitw9mU7YWSsb%pQmV_`N7E1ON{p`c=Z4T82|wm-SFsVmA}~l000qmQchC< za)+te5?cc~$swA~gRGg4Mu9KT00009a7bBm000XT000XT0n*)m`~UzT07*naRCwC$ zorzys$j10G zX4B~&`0sMza(2>&f9~F`Q3|^2|GM$JOyJk4*k6CUnWGeLgI3+$yMJ=wa!zu$#-_XZ z+aF)2XbIS#ZVD0jXGlY*^B;4|0_Jv*Yre z#FXRzvxXGyam40`zA;=&^v%pmAgWXE#Hi_1l{X9fShIfdV^{d!Yiu>!7Rxm-kX z2{-qLy2y}yz=`E@VeMJr=S;)k24xzy@mK+u3#@2wWFQN(fDBn4kL7Z4&Dopjmh4YT zGGy<%_y@z~0?SVcYZkuOb-||NhAfv0Zp~g#cwkVnl3Bn%6)qQ9Oer->0WL%fctn=V z1-E9eC-P%{V@mb`|4g`C^x*7oiEx1uO16#rUR*A`+)Tt|Z|86&%jMFd z^2(V$T&|q_%=U$W^L6!lHjj3AO)ly2>EyVQKUnVY>Ql#azhg$|e zQH1O6Roh|Xukx59`cSLpa{0ulzh^pB&He;rf4PEofzlFko6W@!Em>SHp(wKSe;tPG zFW0X9ab2mqV$%pF;jf_CU@#QxIkJ~jEc(N~+UtK@yP-t9piOnPkgaYtaAYsHqO~0s z2D(90Uk|QU)*&fb14s69i&lS_W$+t$Uw5xoYB!LL<#S{&IkLvX{V!}@(Dskh4wWjE zN_l~hE#b&sc4WoFzTAHy!QdG0>)=gmFSeHD$X-=sDb3)f^1(k}j&OTza#F*Q<#GMZR+t)x({k@QGZCyyNd*{k(>ed4sjsDCIb4*Z6+St5k~o?H~7{-K^D zdknHlp{DnfckFZf(yKMnr8(Vu-uBNGU9YV`Rw+z=s2KZ z@I13|4@K6Z6^&~7kU}k5oK8!YnEo*(j%lS2O-(IP)PAAaq$66VW#5s5h;=s9#cD-) zMki2GS8hC@uQtn=WvQ)oDvazGf&mpLnINi;)rFjDEl2i9EqlAl>b1U}Sn_%&p1_DL zgIl&YvFEi`w3jJ`!3muE@O;VY1+2YR>+?M9bl+UzZO*v>$ZEAlqq8~?P)iER8I6^c zGz6git&W(_LIgs2iYLi-^0nqS)^cQzL{=qiR9Gu|pJ=9_ zR-11NwBd?GA<@>Wa=!pLiH#fI_%ASO0ywg(bhWhE6bS$gC2=7$yi`b zb`3qW7|UA;S$P}<^TjOeiM19jN0uS064qW@y;BtC8i9T-!_D9Ot_Fv5nfs-@EhuX= zdABBCdMg$Mp|^rg!jPz{xd6y&iwg@&8XB3FRfyqOVM#d*g|LyH3q`R*aTMGHSm)axZ}n`kH! z$Z8e(hYyVsvUnsiUkpd2Zzo)6b{CV?TwG~(l-?8T%&pb;MEZR^G)lw3i-V_S)ElK1 z&ghNCdkk6f!PGPg^*}cxo*QSi99aq3$vx}dpn$k*O_z17TWGd=VICynvJ@TYn%G-{ zeOs{z%XZKEY3N-aVW~1n5Oz=!^z}0iTwv2(3QZkde(& z`-X~r3|Vz_D2m&@LaZJdD%VPi8-1WHVbV6{8=}7ALUUy>T8e;0^-vJQSVXPb87(fi zR2NeVeBulqR+C}ibVi4K@WI4z6vH`v#pO<4AXr?jRx3EN3|W||gDh0gZ2jCz$W|qh zg+=hn+irb1`?`W5Tl5Nw7#8j9`Pv1C83s|IvcNMy8nU{iFcvmem%w6G4;3~R z16eh7a$16gaPDzEEQn$;!-6a}qYC;yKEUW~CW~2L!SrJT? z)PfL0up~dGH%9Z#F}=F9&}=SjY&Dx35MfOHb=>U>V4K1(#}Ya0sP1_NG;G2k{=mXu(akdu154V8{oX!dB9h#!EV)q(#pWX*LF zvSvaS-CC(zYgEmVC1jsWBy8E&vMu{OWy`L?zTH4e^O}%V2|#!6lXYq8oVQ3g1X+b5 zg{-AmMm9{y8pDCu5WKI##wbG;nzyz#CL>GcLS_in>BujvzE_-ISsg-*$ryy4U=$nR zi-n!lU@w|%G@9K}BO&W7Z-r)6Nbc7?+|CQDv-Csm{067|_4shUvLsM$F_ME7ZZx<0 zEQ)YzzB}r);2cyikTo<^J4;IphLEBbPZKM3R+q@grfXU0Oap!vmpYbkdJzK^6lqrt z)1cm$YrMbQ^w~rwULq#8TLyv=EM>T64U>H=E>nZ$& zXB8D3S;BG2TG2eXR`#-@9ae1abE|b?V{K}d#brlkt=6u=yea&tn4->Q6;@{KUUX!& zWY$_vimVtgeM6+I$uOuam%-G+VhB024n@{z!TT~- z4*lTBTIJa02TJ+dA!CeV)8}bW{)No4;hXsZKUW%C%YC_B6?p;`0 z?lzaG6$*J`&Z1UhX0Fa{HUypO1hUTJx=K>M60-S@>IAYuH#u&s@g=f_^s=);u${#p z+LnDa_W6U9V~|re;YH@`Qq>5xZg0G%M%jn4&p@`OcdxyD>{It_`{!oTt+zjTz0Wll z5!p2ND^@DaZdp#|e$nhetGO{?c9)7GjM;!2npTgyyt@^YwzgI_8WWAm*2+33Axrms zFeoFd6&nl&U%+fAPE4yI+A30(PHhklSG7?>7849^b8AT`RG2_E0j45o$PbA7k%h`l zBCAjX*}#vCEV5=NC)ebpSJlX3S=rQJj-1K$WU6dq?e@sz)U>f0L->AVTUJ^bo(`>$(NYS^6v0vp!G)R;*a@#R7Z6#6nr3C; z2vH(I6y8^Pv{WP`p`=`|$6PKZ5lGj@O7(iMT@ubr)6yD!6zd0jgcEXN@GMR^^ua8> z-V%(3#f*j&mo3Lkf=`qU1G;U~0P?UH4;H|X1@o*FzJ>SH2v_T&9=f!_sncH?fyJfZ23hdqgD*&Llr)>XpKV# zv%4f1M5GiNI@vH6>%-FQ7v>Kd!}nrDI&4~>8~F*c4UHcV*u$hjC`3U6gcd~1kJW{I z)!?@w7{hb9hbUsun3ynds*y(18Vi+Rlscy+h8GhG)wyM44V4YS2Aa9}p(kv=30cj{ z&+XQ8&WJe{Sx0M3Le^Q_C?^`?{_b=-IDtlA7MG=8f+_>=6rTkhL8gk_||UFBpp3>xS_15XRK|}9_V(9Mx%6GZ8RDaE5Jl&IGk9RBc>X1SJqQxy^*n1lHZtVYKlfnIE)yC z+;WC%*~8A9<{<&n_3Tu zN63_sWBGELD;)IXG1ft?)gGj*6v=V4X^+ynr#0jb>=Nm*Ry~C2tIYK|>;iRSFlw+r zS#*dUO`ej5U@u-eqewO683(QU>()JTWQFg$3CGD7g&#npLUDNOI8kWR`e!sUzKhyG ztd(Y8wO{-U(yo>+Rvi8U6zRVuxk1QM_JQ{DudVq`$O?QPgM-$^LK7k*=4`%are%#+ zU^N&=_9A^3wIN4IvGXj(#u{PfrEz3AvZwoPIBSvXct&Gm>jf=eVat&{WjS(pnSVTZ zRu)H=BYWES@3VA|6dc)0iEIPk`S6OC_qeji@*+x?9@&RK60%^nypkh($&vl*2SfH= zD4!#nb*T%2ntmu`!K@RjqB*jcRTSDIh`pM~LiELTtwX$0_@xxlc<-nJwO15bxL`)- zL)TwiE~A)^#PO*kxLkE)p=eDe{`GQXxp3jga^b>}<-(D@l>QfNStXZCDL*E% z-1XvG_R`JSmPTv;y2s@*%3r1*4_T8b9BXyco?2WkpQuIsUq2qQzCxa3aH$i#?+HIx zCYQRz%rm(zcZT<=W^$QKDkdlMaOBP^(>c!7nugpwU-ptSd+*ec1%U@XK1pxWxuwL= zW|$n=OO34lbdZ&1?Okod>!Wk){W??X*x_*|+raSL=-lvt?c8EdeC-!s`<139Rm0;i z9zU9%o}Zqce)RawY8xFs`1Xv$uDQ_RAa!xC3fu?Tt@qAxFO78bf zzgW&WP|*T!|9EEamHFv6bFQ;jv>aKEtPFzb?c=^X1qFA~1k}#ISU-SSlVFQHLhn2K zd%x*Fb7ncR99gC{1^=7*Oaf&N?@UMbr=~(754`9*u=o8(Z@HG`at_Fvgn`E^2Qf$( z_I0Wv6&Tq$T>kO`1D!t?#a+(0rT-xSE8v=@iP- zaldrfwfs2E6n>#Pf5U)^V4#Fzbpn~NZ)hl5Zb>1jP-j&~hlYHmYDSmb3X5NC?j9W~ zSA5-WJpqF|kg**+_S=pY2YhmYCNLch6#FbkA;4prJBwhCAHPkvMZlTZ&rMe)lrArzB_dDi? zWn@*t`;`>2%e0_H>fQf1)%(%F8LHVskVS!_S|9dhmrK>5rWWeZl?0MVYN?nOYSlG1 zFep%3o?588rQXNiP^CMj{TiDR$mSb@nZF-nwZmX&m8;`g^u^fsuH>jvU8wF6sS60!>IMMM_*G09rTV_QaOpfjmB3*Ja4T(EnSO7?{< zefeOz57&9_$ZDhD)zQdm*eb;85=Y5UxHekisAKAe)GDfmH~4D9(YnHtYGYPuNnuH} zHjKKVp$x6F4_URmi#+>7mAlxzS1Y54ySbX`hDyk?Mmn-)2cy)Yr_DtrWKno5d3}~V z82L-3t@(weMnzUN><9Kl3hF2sxKg*Qtkiz?iAU<>Yjj47OJP@_s^gGRMm8&PJ+nJd zeY`bTU0ROPq=i&tTpm=B7*0(uUV3$84=Wh5S;;OJ4B1G^^@<3p^fSXrnE&2Y%3Ah) z%81xW2h){u;_i36Id@xDI|M5rAXN&_vJ^WU0V+!uz+dWxFUT2iG!7Y28xHlBqk0iF zm;)k?#vF|qRf+Z?3on~}Wz79Y10@+tODz%$utZA3aF-H>LAVsv92IHE8Vpg1_n=uq zR-3p+;vg}ALaSy4z3`;cruBF>P*@t4Tq^buMtL#UAu}tZhf=1cr6>W-+%=0FAw(j<<0P`sze?t+?WQbNe|m3~(Elij>wH){mW5U{-HFaXR;#9gj4%&lbtPeaps}HPs08)K zqlAL!i^0ff7_wwBWPLbMS8S0OA!N<1t>zdR4`OX2Dkz(&L?|lZlEpGaiapgZmNqoH z4Q@P@GgQ|QHilz$!SW!^#o$tD)!_g=MCsZP^WouZ;e0JX4xwf=EZ1ONEbJ?RjT)@O z?*tW`fNbGVF^%m@zO5J`NNoC0P2HkaRAYQcU686$mp0S|a6bx>1JLN8>qP}=*ySl#@q%2B<#_;vUj=0bvce15$_274GE1hc)EhGnFEZ}_Y4Z{HJ9cg7A2GmPtX$Tz z+S2N3CzKcI(qbb_g2D!=crw&XR_kaN=9rH+E#stEsGmv1L`N^Y1D=JhSG-( z`IU8U)af=?`ivH1F-q0LpK%ABK-LW=al&0JY7@xj*TwP+VVG)-QMWmWW>_so9qgJ) zpr%bVvdSyz4VewmT1S3msUA*Pp~HYvhB|~d!VP#%GkyU1l9>y`w~oq!%GE)|>cafG zTD<5G3Y?QRH-~_1rNhl?jVFpclA?z_gbxSBcWbp-zD7KqA>U9}t`^HH>DPrpPz~Me z#=lgBJVnrn#$8qMy8;__N}dzG<3 z5J3ysP*;dPJWpMq+5+oTjgsn(`R>qAaf!hYa~gpxDd*sjJHIe66et9xdP^|h;3iAU zV6Hoh+IaskE;~)LT_pP;RAwOSk_?0XdAaw>eB^K~dzOLOlOQXbgvE`HAo{>2C{;^$ z)EJ@&p=zkESPI!-)`^h?tD>k0jF@JUD#==HpIV|W>Le?Fu z4u=Dkg#pnh23XDLU_M70-IfY} zy+6Y>y(d9d5(Ece5GWE#7v~EhjRBN~|5juJ_wbz1thHbaa9KoWiK9Uq0Lu?87Clh$ zrI2-~LogvkOf(pxK(?_K2p3jAG(NP{f%(!@qmU>CvIue|kgbFTt@l;tmsp@gOTr>+ z$jBn-TWF3s!*%XfLtWU?fZM->ELp6TLvZTw`QXDrH~JkU97$iS5Qa!)BjU0Oa=k>0 zr5dlH9Ptdi70lGZ^~w^R77W$k%mT78ym1R`4Bv%A_Ne@>WMFpX*szsLSWd(j3i`Ir zKQ6l;S%tbBkuIO4mn3gWT{~19K!_md6u%K!1KaE#jaq!qb>!hI!lm%b^35S5kZmxV zefku#PIL-pIZN^z(cRqW^uZ+y1tA!qI;{ad8M0yu*}`g3MwXPGGs^;_B8e=i(Z*U| zWnr+j(udyxwDf*tE2G8?WGT4dEDx5DwMssl5w-~KIF+PB@!AlLgR?4@2SN=Eu%a<5 z5u~^sL)Lu(ku@bnxsQ#+v{-uDK&totOoc=g_kPEj=5n17S+TsqQ5Q|CUyprCC{$S* zDr}7E!{27hvIZgR^ttm(=&>R>l^`J-P0gvz7ytku07*naR6{ll4NBaCXCEOOrSPmI zhVrzA+qWNC@~dQIVamb?0Dp~1WMSU|wSc*#y2Km+`a!jEKeDa9Ok_o)UWDrz_QhZT z4~dp&9YPdtw?Rgh><5G%3JuahhAiT;7Z%yWB^l5vc%(Z9Lc|gtCwuSOzPn){R)g3f zZ5oZ_cPO%0;47$b$`J$g$sz`d*8RwqyYuT9vPPpQ$7S~;i~D`C!Qcy$7L}1Tx5i`% zr!_*=Um)99nucspk%25mkGkE#5N_o0(hL|isXaoNCuaandDBKD$0sa9RN57}BE*;t!}0m^;l#zT?CghGkKT;eoF z3E6T&5w*UZ7A^erR0Ge$6l>N&$QD~k}(o8L@{Q_C7 z(dVdafJJPv7>C@jjRO=*KuSYneiB)A7*|Enb?DL}O9@`7l?KDdD%rMV-gm*Hw(pIs z7!Dz>pQSfK0HnnU`!9xgtWktQf?my{h;0xJ%zo7!F#9DMnPll>aAfX@`r7@-hP8G0 zE+2~eaAReO_Aq2IgKEZowm6Gv*;;2E0=F1eAND!LT55<=1Fx6p(i;yz7Qr;QNaDk2 zGGYKT-AUs%qGzMQZH`&8(vj60zeHB7cI3AX(Yq1@(6UgqhJaoqSd++#)s3*A^`bFo zzO2Z)gz;2f>>JgwyAHict{q#63*Q-8br>lyv2si(K;UZyVnev^Ign!zF2$4%(4`!M zx}k8Xlw+_@%bM$=J|@)<@%`16mzFoc$jd;s^kIOVsg5Crf2d)g#U0!L5Y(cN>_t6X zslsAR^v7BQPGfpQti_#rsM;BJe1WX9!E9(KFZGoqR#JriPX+VQ+VO#&EN3&~kaLl#b#8zz8=@PBK$h^aVten?bMOx4{Q3a5}Ql#o@Z zOYv+NrWJ1lfj`)?^4qG6rrWZ3J@_qvtL=I0puEjQ<_7^#N`mt+ZgjH_9ed-lhaJ%HpDPh059558LTr@0$KWk z@th7rB@(5>A6VGLCKq zsRm|H4ThoGMgwVCSgH)!Rs(L)%)2)fHn7O8R?dD6!-Pdpfs9)41!OA?q-9HE(CRVh z%R)jH<0Rv%ZnL@+5o*F2PVxNrEHgkQ`ah09Y?D`H_uDa-1v_R*>Yi)08`O3^A0f05uH z_9X>ZX{+J3mE%g7pZFhrweYxnq^F(bI7LV3l2S@(pbl!;+JswXk=EEaDP0v-2AXP1 zbuO9HZHva_QtXN zp;}r5+`T`IC(@NO_L z#$o@X>{g?ZVQBSQv9=8B;w*~f(}_raP_xUD-HH2d!t_cdeEM z@7bS3YIZV3TOKlTM4L9;zC)?MsV7yWH?-2f@^dCHLP9}n$xwND07*2(2fWkN)1+P# zyKevMk)J8Cb|U$TSR-jA^SGf`~C~v znQwCcqwN;s94cvbyIUIrXSql_>oRWEnX$a!&WTOa%-xR8IKK6RZg5n~yhg2>Z*X+z zJXUH;f`aMMh`(;2jwSB>{tGj7`t`y&oM$cpC`5#Zq zY84PI-$w2*t*P5h+lXWJI33%cu926|t5yQv`H&-(f|WN%?-C0e;!TQJ@U1+*s-q_C48W1S z%t4S-s$pwFhHP>g>&}a#m(C~!*Zb7&Tr2Nw!ugd16@+_HY#qTXgq=#5(=_dXpgKk) zCv&>*+&_lzvoL4L@K$oUjB?C(dtzj_$!*Tqd-E@y)*Z5ZEie_s{lFXc|hG$ET_4D86>k2$hj&Jfvx`*)6$`v)U?Xm5@z z7mh3!jx0x(%jHG(cqw?4K=#f_dUIsCoFTH0{@Zc9{8!ep{nLM+q&G*F%UL0dT}<-&y{%Y`G$g$qZP3m1;;k)lc$9#G@Ra^b>}JzdH1Lk8B@*9Yi8h;PdMZ}T45Xg@1l9pY$ zbAOs1yr;YeA(7W$Qn3d8tCapEQX>*wl;p9oe^e^@yeZk!B)fCcA0>D0II^cAE;3=4 zr#n{OyV}T>HKq3O%*gi1*xmDh9w$4?g(G`9s&?Q}$4XpadAY8+&NnaKFSCsnmOICX z{o`+5bPfxy<@Yb%tV%?;Mc%x46Jf<4+v1&X{gD@)s@2Z%w$V2)`UPP){)Y9$=1rZQ zw8>c8n>WK!>n#VFBYO(GnoP8!r(>m4a6y;q-cPUm{r8>u7XwH@e^Jo);xTO9l}B$o z=LkyRV19R95SCZ&KgK3{ ztA7`~!O!76N=67gTb(0&I$D;HWm?wuzOQ3tej0Tiu$#DR_wV~Fee?6EtTW%&H$9JS z@m2-e?)lFA^gJu4C%i@NlKXd79=WFfURfzvSy_JD*Kr?b3kn_$2yOEn^L{~i(@`*u zC)xdbg=<-k>?tFQkk@=i-<#q7_g-;JE3^qz|NzB`!8q>@tw!Z1Iv#K z3Le{l>>X(2_v5x{(Ea%R`_bu+`@8D{tH2svLFqD9a26Ew%?bYLjy^v}mLq%0TGsUj zJd(ls220U(rrnN$T^ya8?^qd!HTu5b&Ld%TzF>YC2bbpy`j$z{_HDtquuY?@4IwjZ zlG}!^<0~E01H${i0seX)R=&R%=2ki$bA~vwr;V)tF=0AY>v+a7f=r6~-ztbj|1HQP79C! zZ#Pmf?|)Npf9KpQmvCgSKC)1bs|jRxfGjLhhHT$*64^TpS(E7nog`#!naFn1S=XbE zJ8?nS{l9s5YvcaDJFAcXo^In>mLq!>hC!SRgHbs9a|a^Zxk7H2N=d%en=g=sVep2o z_cQi|&Vs((zP^6p(SLX6|L-xMU*O1|I6%PV(+ek=D`UKL7L*5QF@ERySs0Qbl3ac zT{eK=dP~>8XN66b_yz1n1nJVZJj8Wjwd}W-F;)%l46DAk#uZFDzv~F+o_>oqzwMA7 zc=INkKZgegE(fv?MoE^ReO#tLbGEOh#TM_0f2Zw_O{R}X(qy-9(%*6a z*K65}fh?CF#xQW{Ahs=g&$^8ss19ECr#xsW;67vz>dA%68C443v9}y*61Z$RDO9oR zeA+@Om(Gdut8=6A9{U!s%!z~D<4{*4bW8n5nlu`>Z{9`1 zQ%ik>)~@ct+Els;`**E>dTu;v^ zSp)-H&-{cV4kKJELEv(p$d1<6kNVr%+Wa4T zaKDGHof&P*VS5Nckv@&HOLG!IE|Y&?czAsihhg<@ld^?fu+HrQF6W8tGdNzGByg>0 zTMGQyjDPGWLP>+B$7N0ZTQf~9Ej{&&WLMm?@IZ)oX3j2oPpRSxoFn@Mva9y`mL^HI zhXti_II`Jew>L%nbkm0(RVI-Y);H}f3*+PCO@u7=7U*f|x6RmBxsK&>F34i{pcbZO z?M-uS15Da%(*AAh9~XM;<2K>Jj3<)RvgCewW`=bxlWRdj*1v84xZcvze`ZSdw&qFE zZ7!EnP8Hc_6y#F5g!OvKmhDMFXYvoX(Y&lJVn@^!epaW8W+4zA0J6h9^>gH!nPg-U zR#=GCFPxcOkXzO@u~Cz!;d1#Tx2Q&SYRFQ^iXI@n*j*oA)^pN}I8pqUaytApN=P8>+0PUD!u$_&L6&Clb6&%^iEZI5# zSj*NowMm`YKqNw2kYtOQT3!9i&z_C8 z38}{NfNVUg26mod-Isrk<$2kL(HbzxBay}1G;mhPHn&%BWG^?exu>@%TzT;F5~Xr~ zW8cyf)BfwWC|gE(Hpq63ab$mVWT`ewa*33Q!UD-sEOaJWmPLgrCzVN=TGmJPUsQ>r zE@;Uk(Ufid^4UbVII$vq4OtvnE|&_~@1te2vs0f6BzylS1<7b-AM|nNU)ud?ulFnMf~uuYQ(yiR z^1i`Z$i9`$k-d1x<~>>LZhrT=N+W5g-r3pb&u1q-OeM5vwq~%39h>c1)ZlAh?3!S$ z=D`fy@`}o=UP^8BdS7=x(H!}yZ!}j;=8n`fd&jyn$H?8Ns+!cK4rOcBX1(pRYnrdX zO*7Jklwi$1#oJxgy{7qE{zl5GdM7n`9N7zo>}1u4iHX?>3E8}|+2>VLlVu+!x{_LS zqD>21<+GY6U7z6O64U-HoI)%U31kthAfm?^F-HVQEp!M?DH~wSLAbP z2D>Jj*NTcpdMBPw6=|lPPxOxD=B{;3yvk6sglv0rUf#%)jo01^x|l8>8Ij#Fa-wos z>(;HBsfydxPOljtlb(-_v?34MflFUXJ;34v-cxQV-%Gw zd7nSgs2y_30#Ze?q?ZpmjBQ{T_z z#8~sI_SchOF)w$qw`_2+x2g(V^E7D^uB&IN!oQ| zb~eLcOd<=?-?cB{emc_q+B-JZ{;78a=RRATyR2xv+^Bi?878lHtU0Hu{c~AOULLx6 zy<v%d9S^F%=@|* z(S^KbuNS|nS7WcMCeXq9YHe|E3~w6>d~&b-UDp!5sP3{^yg2Xd6nrbR`1KM=GR_pcWzPHSVhI& ztLBM{vFA^^yIy*&&zs9?s;pyWMX*-A6IH#hD#l)|;f$5e$X0Y7jLEUrgM%;IU%_<( zrQV8)*Inr2ZGMv0z^`fz9@j)O#;xdvPH(nW0NF0@QpH5qt4~&MH_Y`V>)xxX$;nUF zioJKu&zHPQOP^kKO;ogZt!bY0!U)Gm-n}v)Yptkw`Ks%6g|}<4`_(7!(!1u3D}?Oa z3>L&-wamisv1zpr+H@zjA0qa1MK)Vfpc1lW60*0}nqMO#@T9DFioCCPQyIvT4a>}h zTL&Tw-Dn-FDw@Q-cq%V%@%7lmT20xQb#H3q*5qsN(qe9I^H{}3(TIesrg>}+zlgOD z-ioPQ4Q$zzua#G{xaaN48)^3LElS9G-%aL?YyhrBYJ5mGx|h7KsPWqSaxsOhw+qMU z)LQP~MEmo>Te)~mOWjYh2VddWv@w#mIN^Q0STps>`+RW(7q@>H%_hP`-L4fTRctUtE=%QS`m9RE4Ik?s_TLx%S_mVk%gxMSL9oeZEw!)ZnyT< zXf!#!UhBr~vN7-Tn(REdSKf`HqBLY{M!LrKrZk$GMKVB(nwd$FyaDvWk<(~k8IWNO zO2>MO;5Ic|p}1M&mL{7TP_lbdc(oen%ifV&No3&!77e~`pB*e>$ZB#YU$(yC7J=6x0@R9 z^{N(U$Dl*A;h-&fyPhPGrJ-^MXWJ*zk)0UK9g%_?Fyot{+M(Y+(Fr_ z?LxC}6p@y_I>@SM(~KQy_xa`edU*rm99_EOWYXzeaw(4m`*>2z+H|hWG9fC$NojlF zO4G6~oh{9huw{&!Ilx&UtC5kF!?OF3oje-Zk(yp;K+SV6%t9ugdvjnIR2_^gejQ6* z>(Xo$?1p{Fl9~HqadL7J^p$A{Su0UmL}2EjQ6@Foo2Oony@I=+R|F&xtB{dx|1|Xl zvY*=Dt!W5Z0#ibEaMminP*UE#P`L=~;mp$2Lw1xlHKZk7LfcFe?ItVuR~Kd$BK;bnbB{FtepVa?1cL@2=_~#bIL=+0LEq>Lw01ON{Sd9 z;eO@i;`U#bi+I%pCdn{``?|a4P-JUHU@+S$8*BaimqdbDAX<5lsaA!+>kKNOQ z9lJ++$W5!qp0R@KBUMJQ14>#;>uFkOLCe#$gRKp4sXhLHY1!?W9=f(^l)yBNvT^O( zbFd@7W;JL{vJ^|WmVwu`a1 zjE~Me+xBb^NL$h`j8&X_G!y>+$CH*sL@udZ=9cS_TW-0OORkYim_%b{wiu}>%15Q- zewpi3ZjoGu*)S{Dxr@ZyYbXpEMwtEf`JUhRoZs)9_n*)6`s@AA>-Bg(pL}YAL#~YA zzqIY#lutiuO+|EB{94MnrSEowDz)}Ac;0+eM;CJx`4QrV2b@4Qx7TT#-D*&hI;`kX z3_d^<3`CJop`r2L>FQ0v3fO;$E`Gl=JY6(ikYH<~06$_aaawjzAg4cbO3m=TODnS{ z<2)R4Y!35w$^HS!{srxLE8gZr+kyxT^O@3jjbTmjuEPbNDHfI~WN#1o=NANSa6eP zgymxZq`K6QcymSngW1co;ex9BMGt;@QLJX}2-AJlsn02DMd>#?l`Q2Y7SRPU-qQ+23;jUoq3E31LN660>=OL z0{CQwD>`v>`QZ|Dfw?j_vejuXA(y&WRk6a(?G4Z`&uh!^`RP@4rKL_~uSfi6R#c7p z8g81?Ohi4xw>>w{*|QI8|M1sJmN(`?p&Ci;d@^5-Debu2Q%eL2$Qzv3toI@ktNaV@ z(FW(stJi=X@T%#(9DAVB`t(Y9U{nI-scqZal1ccJ=~2}t@3^yu>#LO$oHrW+8$X3v zv^haUjDdy#s%)+q*LS)1b*=i7Q+@vY*yLH?bcy0iwNuTViJn(~T8{aj9Jt@i46qYy)A|oVMOs9f*T(+d5 z%Q{*LZ3EqvoZED)6GxDM56rLft2onTA)jqLwiOTh?QLzF3oj_2%4H5ilG2H-?_Ti}(_3CH^*{I);gXq+IR$~u+^=}T?UA)M zo|7_zp3zv(x*z_Jd=OC(d-p~~&X-@e*MITrxy%fjFu40}Rn?16D~SFejP4WS#lD{F zd*{-<$TqMs%BRX1uPF|ztG8{(Q!{Qn^X7sFkQtrS@b%IeZ52;i)daNot%1(gxK0Z10ek; zZg)X^fyX*QGLYVqV2SK1p)0qnwX%dAUv!bf-&N2OW@bzq^HT2yzZcz!>?d0Yr-qF+ zM)uw*AF;{z-5l8x8deUl_|9wLL0k1oADmlmHhQOrz=m`+{}IGs8l{!o}Y`fb4DsUpcq5-T1I(NRo6NDwK4f z4$l%==ux@LIr~24;v@3_e|TUZiZb0&^1=*CAUIf{mSVs?`)WPnk5V67WublsfKp%1%WQR%Tck-g-J)@{@ z0QaDf@lIS^=raerBmNox%0cS56B&n?EGVv1Avj@we~_krC9iFnR9Vn+nRI*>{IU$B zc0^=oxR7>DDdRwjdDc@batNDFGjtywtCZ5uMB~id4&aDrRLe2}uBw1LMp<)ZwS zXN%7*J-!_wbKmFo#B-QK_hGdby$_wjK;I5vaHt7wwzo{{{YiVdhMFt3`3VDY*MClw(LJHYrxYn|NaX3Tp|1;-J(<1f_Th8$Nf zV=XM<)^WpZ+hV^2&kK|)n=3)|t-f|Dk19sQT4ShVbz754)!a6c>~8+F4qJK?#;6uk zd42JlT6>M&&YzM}fSfRHH4aeJmF@H>Yfs*1-x`}a#WH3Gr z#f#bQvmzreg>rb{xM~4eK5^?$$M!NexP~XVT*Kc%{q4;)eeG7*p$b}qgIrzzD(wwp za=wJSe_@*oyRQvBe|yG#OisrDGbuJ&^DXw!F`U~XYG}3-dm{AsUNJH%{PXO3mtcPW zLX_LYh>DF2WsfW9Kf?mqxs$?~XNpT>9|cV-o&Cf8$EcYLIeZdm^v)dlaO4TEt!!D( zWCcu6kX&BmN_V!9WZcO3fKT3qr?Lk1pJ*T_8)@Y4dHqO@B@c!f0-_$* zE|5qo&djU5iu)xk9odu%r`V`cv|A}s@h862j^g6+-fv0*`FU=$sJft zk~O-s1EX3T+ju&>2Yxd2Sa>|s5%;VoOk-z35cQ{%0Jhf4>HJvkS2cD1Cu%gMxadM6 zJD|_TsVbr%_Nk2h*q|AV4`!!t{%-(hq4mGu_5X%n&{$dGFI3s?%bt{s_C<&9FRo}O1e$4g_t$M4 zAXqp0%YUl&g1v$va;+GI?sfXcR-oS7SWKxUI;c*2o0nDl#Vgmr4S@Vt!Ca^rOB-*9 z9l`JHby1_r4H5UQ11D!U9eOuD_+zfHQ8A==*qeu8<;gm74IK`LvXlS}62#9i%_+Ow zB{M>y=ykRLCfp(VD5brld|MA~7?bQuRMEg*&$u0Q(O@53XR->_jdmkLSCc;cEJ%&l0WmyCjE%6t?$$Tf0iisgWw z<*!l7NydUn_v*PTw>JjxU}kr{eb);Ll|XJZ9wyjIT0|FVwrD4RMm$tbj9Byxjji}p z0Ak$i--3EZ?ahZtQu8;Uhq5aMDIBd>#N%|~P3GB+o2$!p9Fwlp4RWK>s|py1ZrkO4 z@Fhij9J%y>5>%Okl;V3>Iu@)FF+0$KA9*J1bW}N- zNZeCN4(a3SrWoYFslI+g#Blf1=&imF!PB^W0peyD(DI=LCtufU*cWiT(UqyBg8sxg zX~^@hBD-;5`!^MwM{p@2QP!-f{0J@$}N+O^Se2)+fu<_q|=Is5(8$Z2PrdT#y4i%XWx zn9B_(?Ln3Bu?#m&(8tYOD?Mby&psJ+iuTuIuKcwMToDm_2u%ib_}6LR==qm|vG2n= zjCh!`g+{7v*u@LKuS?koaQbLHSE*ZM3_JyS*&tIS^2_ddRyS~SGVH{eB7&TJkGFy< zN2os#0W)F_k2jZM61sTpK{&W>hMP{8$%2!j+p4CmV@dMry+C&dsgIMLkRxeHVWk%# z0)Sf8hjVu{$_fnl-0|L;j8gVm6+3FnaeS}@_7-|`JZAau_bVvx6d*JC=s3A%q%xG7|R?hX+QY2#;31c`3@!KU0WWjfg~xOtItE-Ri%? zXWY54{fA~E8kYpqhi%Wl2(aNDV1NGn+~b#PFKRJj z3o4oNyR7EO?dGbG!5DiKNqR}(A0~OAI0nqg4e^P`N!i8&I2+^g&V7(I+~*G`;V{U_ zfM&ohm>Z&j*hn7^*-Js!zAj{{4lN30eU8mS?!ydUV*Ix$h!-mU%Xqy=6ePMS{OaGi z!KO;(Qhxz`kF4`1$?ztY0>q0No?{(HzR4|7;%C>R8p+&Muk_cyjb1%dg$ z{nV|gsjF^?bnurx$`Etl*F#n!^p0j;I6Wd-XcXRDw{1U&v&S$!N()Yi{3Ft$Y+xzXd-~%0SSYldLV7FsH zQ%&O1f4HVd_j@)^ACWhf=P?x0xP``)E!-V`DozR0*`zz?r0xXZJmf&rVncyNF4Gg;j=ly>hz%=zQGmy(H$&;envecOVP zJJuL_gomHEgd?dRMZmYu9B5Cc%K6{hnkDvLtp&%%H)bp(*!sSr%t{20JnaQwb|x%U zH1-RmCA8;Izz#WR@Dm{G=C{S4lKf~MsPKo_Xz>#&LaQw0#@_4&)J7Oh?d55kzAJNm zC_=D41MOYKu9vG~9vXuOQ39axljgSoF9Y3g>=!igU#61f_XSea!h|cSqF|?7l{|HWNqzpzse9=tukij-V1D9*hdgU znWf%UUIvQQ+yT-14XX8UAyLpT&5+MM9J2?jNmZP#SiVZHeQI=-W?)$z4@--Xr}v3h zb8I_$dY3uTjfmkj=W&Uo8k4}k3gpq7sVk&|gUPDv2Q8+*8)sSgjmbgMR5LO(#IARt qLaU-ToGBkb+~`{G`nkvApnDFvw%_5&WA`o|xo&QMtMurL6g-0 literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/todayhubwidget_preview.png b/android/app/src/main/res/drawable-nodpi/todayhubwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..4edb42efff6f7edeb9b85f3d1f71fc34f0deccc9 GIT binary patch literal 41982 zcmbrlbyOTr6fTInySoKox_X6)9cOsi=Cs( z&HeN3gR||!%bUly|Blz!*SELBv+Iq$^XrHIYELh(_s}`*-*Jerz3H;*oK{Ad^48yb@FMvhvC? zuuB-&2b(yDQ85bf2&=%r6Dw<3^bgOBO)puvM_D^~Nh=xY7}<-5-Cf_@9-m(9AD%os zJOV6TFD|cyrL=87$3oV3*R~E9mN&cpPB^;z4~@_NsOy+n+Gyz<+TJ~kiq9(gUNfzfVFJ=SJyW`hi1+%K%9M(bG|p? z_v$eYT0>u->zli`56;{F^bHM<*EY0{P0Vn7ySsgS=^GfXZRu-j?Q}1GLezf#Gy7te z@dyKhUE2RlX#PCE^SXWd_IWu+E#^^oCN#JPI<)-y(A4^Ne}9BSn%Mk&!pH#~n>Z7Z zX&8OE%PDw0J$>HTJXg~d^Lb%&dA^B=rLlj$a{F>=0fd2hHxGokt!mqagr?KsinjSR z2AYI-`ylwDtEmTL5>?{Xqcw}mQSevNFZ$16VR7-dA`R5{>!=>efX^^6=y?AX*qOD! zXBZe77!`RL{jbZXosUN3fXoZid2<86eCtN1Pv?PN2KpDl@3ZS2HK-)QUr|RSV6!qLIwcdPec>nfrCsKlnK~hnGGz0B$(Z+~HDbYGz za=7?>_c-n@St<2;gxw8o-3YqY81ul4cv1jH2V9R|yq)<3J3%!6?4HKQ zsrXf;U1_OImROd4)cv@)@dB-|f>s1QG9fe^VTM{TTJ@mjL5O0JOznixaz|hqFGB`kfv)I#R(dHTOMHo1h_cc`Z1+=a4 zq|!eH6HrWkyYYN(>%X*7EsTPVfoX=kDd+1PWr!8=v^NL8$Sv;scoqHc{w>g?Xwwp7 zmkg0;HN-m1AcT+u<~XiWW4W0Y?dL!}?I38$1(&?`NmD0o& ze~^nOsUz)SB%z!s`=(Hk@eA=%y<~cn{tb#vO?T%*#)gBQPzQ@Z*$E^(@7V z59MMy{+nn)YTg3_drWb-e0aO{_6_gXo8pQ&{MbNgh(bZsV7Axh6Zp-K?w2n-j#xMo zk(V1myVTVi^>6NI9IN@$9sD*9>BZ+Kd3UUI2kYRg&3<3vCQ|` zuXv(6-6DGGf$X<e@gn^+bfY>`sjwp0zU~jl#EO~EM?yg~ zktJ;K&oOpliJaO#gQyPAuU!iWOBa=FP|_=uT%UT&pnDzR&kKPsHbwvF69cU_cGkfQ z*VJa(nJK>}&&OV|MqEi0xrzw3pI+B|iWj9`Z)TmJw%6{J&PH+PN13%O{CUt_68@@J zgf(=&eXOfV$g}CeX1}>Pn1F=_vUWefoBEDcPCDDYoe6ax$TBx~317{ok+=G`|J!9Q z6(xh1wn}0_w0RM)>^(2}L|mULPq3gPv*A+ng>CUd0V5UBsC7SZ+%{65UL#&dHT^n2 z?ol1Y&=w4TXI?SF3)5p{UbzT1C^;h7Cy?q0e9Lne1~24l${SkWM#`Qa$YwH&)p^pT zc!&nX$U}@`abIndIa=wc1L3t~O&J&03#-$G0zBLc)s03cBpmphi(3Q?Y|%~AqKZ2K zA@EPJq)SE25v56-yNA8C-+Zg&fAvfN8E45e7Cv&~-F|x77us;tbP4#h+d=))V!i#- z$)48Dyw7EN#D|`mHE$Ag-A@ug2(KTWo_mHEQ+`j1SuL6qBkN^j3$E|m=w6bW>p$^D zxlFNNl!AcInfE@|^Wn}LRmDD5_pgYF>Oy82YcUDopQ(v-IgCDG5}A=B*H@mao?g@t zn%i;xHW<#VZfP|9EU4Siu&3&BdP-DK%YQkNw!-1irkg+uDl$#$j`J#TkZ6KH3Uhaxj9gJ%$*idw#i z-Xm=w8?Vo+!_G56zMSbYr%@@l`B(+DO!?Re97s0*U6-d*QJ!v-8FysT(Y&o}Ow||J zDwjVObG{s_7ZpM=Tac92^h6!3>e{aX2|eLKNSJzqV2^@qwK0Q!YmLDIMM_`JZ=D?@ zy}D8^HaY|EBU!+3Kk2`mmrXNRr!10U=bE;K% zKZCyW5KmjOF;5X$A63R&+|;#|DD-XEHQnswk{|Av=e+#Cx~1Vyg%rYrrw>cXn7qTdfW}ni| zDdwtqEX|T7TB%BBH5~~PFk;JKos=63h_|zT8Ankdisv}hui8gZS-i8o5uskLEblN( zp=adY^4net#ZO;$;Yh8?V4a^>7grH-y1_66ZLQKp7>@T4-4)z+iN$WH z)_mZoHDpskn@E)noXub&-$NCq^#YLL${tU!eiq|)x_rF-1(p>%QCddvgBz9lQ;yXe z$1LR5L)SFP0;-}eTJ`NSaS~cj6}p5nink5>2swV=VgS~DZ*NtQKydb|*IX+5qvIr^ zW_1GooK+8C?NB9{Q6A1<5eQI-WgiwWOHiHN8%^`*0~9Ac=T}pl%e5R=Y4BRY{&8v; zz74O}hwCOPd?PffU1iNEa7BR3Nf<`f{X7o!tLTJb5}Lh8G|`CtsikA;>sSAyhC`H3 zeq%lLj!-U~?Fo&6moi6waZ{W|&9c+~mY)jl%K z9i};a{Of>;xpa93M^^)0LmL55Gvrq6EAdyPFs`0uGOajv9EFyA1AKPaHW3iwD?Ldk zkW_pyv_7L$Yt=l~r9{7ZCR%2?4M|wH1w$dL6&o*6y(vFt+i+$iKN3x#$d`AkDqA@v z63)teI_E6NH_W{J)t$LXR0VsyQ_F=@rs(zC@~Lie=g<6k`9EFu7|-dRZF|*j>G?5m zPe=}l?Al*5?6QSuSYqA0{_mULsZ=9V%2n*t)H-X&zvbx*ksC_%8uZiIyWi4n-da! zn$IdOF!&;y9=kZ}O}gQS%wGhWSOzjv4Y~?fUrdtq>RkY%nvbw((QzXpx#W5^9$2Tu z=Po)}X`31yPDzrBdaAmtF%yb7+ z^W=|YQzTC9Qv$-DfhGTL`)TAdLmyr`OvlvDXrcQRSxo{+D=Z-Co3slsdV9={W<*U4 zU7p)xzMnoPb(6=Bdwfkxe6o9aORu$4TD2L4qQ+In^ba6J%MbFEmR14vX|U9Rnm&-L z$0eS7|1MNrn|J@Mnm@Ixq@h0uQbXLCK>_vw ztQW0oMSW6!H7aJdm$SAUQ!3ou(gHW6FS5zGEV~{;Xh%scWh3v$F^{KBhL37p+UG}z z|LEiMqy3HIgK7}-pTk1dus}G(uI-?$VU>LnV`er96!mijV_;+fmOF9 z?`6^);Hiei;{xG8UXEEh(oVrOR3;xy@i&Q$J8=DJDpKe5`aZ7I66O?ZwBH$A{%x^M z{%3%(wh#z9z4ne7XKZ(^uuz4OJj`q)(WS#dG-BpFu~k`4kQn3@)7ESA5p*=g^J*Ed zUt}jJ4m}W$@Y&)pp7LpMY}N$={7#trey)J4P+X$?;aq5U;lcJHiYOQZCOGzp9R^YI z88P;qG-Ao$RoI;2pZoGAVl!y79TkknHqe9(U;y|GHLT_G-Ig#qCe&(?!@|JgFDKCY8TY8qKhF;KlOD`Q6HyVjAuTe*RI_<%--tl)8CHkM4=||J@I0t1T z?UBH)M)XPvD`fnCoc*8_x1ytVWU;r?cUeD`!^|cNnj|I;ewLw;5CwFl#^nC?6@oSV zEIWg)DIw4I#$;ToPFgj^SG*m@jZnObm{ukewdVGfZRTd}R1~9iarrFp@NAn+p?$(_ zz75-zo$buYsg?;50Vixm?xh@gCFfWHt9;$ z_I$2W?X>vPY&N%?nm2`yC~gG4bN|8;ub*F=@Vsm|3kk2|oYin(EE>+b>yfEs@E?C3 zFdPM(kWH1==eVMWT6O8V({Aal5o9O6efE3r61!% zC5wo6Kn~IDep6v0<%ZXcUl@M>+f`cLir{>O(FrAI&$y)5i0u5T0$t>d3Nf);((yVa zRrM2sw4xQ@s%ah+x~r)yX~LD_a98S@X4J_j&2;H3zZS9VdcMisz*!9g)b;3BR%UX;%Ul^Y?S|t7?@p6nq*@FA*sj@tSE!At z!%SNPb@hlhJIJ*Tx6$or#=&YW|DuS63ZILLDl-C};Fbb?{?wvswJQCCu@n)-py8*q zQTz2}kbJuFE+eo#StV+vv#u{feMXuNmuv^S{74KVh5Jl^`4w$oR^QYO7z`;CT7C9VWQ%Cre zLRM1Qo(U|-g~gw*|5{wJh%LImK3?r5xkEa7PG3=EY8Q`vxGbI#*f^&XxYo&n{Qqq? z#|~cgO1@q0^RLO(yu7rpBz&1zCfE4;uM^kcMG8`9W$sh?Ei#dB+>mQ#{!GI8OpN6$ zrdl3DlTUYX1dreuHOn7(pz`{gqmnE1lr|8xf>OuJ_miHwl70YrE&Aa37C*m=_sI}` zS3CR;<`PL5XOmfL|A)zYMe0kJS`hfx?PXe|lN##6hCp}L#hdU;)DdMgt@Ac^g3jU3 zR;xYQ-jyuZ!xbe`g6Si_K*!M#XT<_okV}KQ5a!5Obu$;+GL_jZ=@=R}TO50X$Icp; zo9n~M6f)yzPpo>rO)g&7?<|os1pqopW&5UKi+RG%=JvFj);l_3C)Nu^uijTbjcT8+ zz@|5aRPrcqou{Zw4LNgwsrkdElV`W`0V&MSE({C z^c0-L75JP8#S2qOPvN50yGM!oO#@<Ag80)0|NT)K6Huf~*#OQq% zy2w)&G%xG9JG)wOG%ud;aV`Qy4DJ6fgTK%5KL4>S-Kc5pnUUdBY@_!8{!@1&_M|B^ zJP0tCV*+?}aFs2YfCD5B(BXDoiHcLU;K?y$VJTJirOl>9hq|!f#Y7xdPiUa9H(1{rtZ;kWZO8CNa4`H$M{|apI#IWmH2=_FC{X+6U(*X1o zRHN;0Eb4@feJul6zYz(IY;$`?qD%Ij2y7ZK&CTWjCJ=eLln^pyO5kY2ls6fjh7qIU zah-fYEpJLyBf%gO>2XbptDry3kLI{jtm!=IbjVtGHI>^VYg5SJH3kHw_A=uA`AJ2Q zCT86FZ~tIJ5;MvpTMnHG@MA)40MjF-6NsQX`>&%0n*Z&SJ8u2E3axb@K#>%Wv+IHa zT)-3QlhDs_p-stv?)0&Hi&aS1wK47;-arM-GVENR$tJ7CkPtMIlaoWgf%z_R zIF!=^@Lg5^>noF4-%hr*?AOJv)M6HsSx+iwN#r$=%s7j=7&yd}O~X`vg8m!CVd*-4 zup<2qs9*r_hoK{OmcJ6%(LJZ@%mggyG@R-@v35X}*AX=thjCq4p!dDo83KuMY9wYF zf&TmJw4g)czt2EoQ=^2pY-=~IU%bAhbgtQTmN(X1Y*pm>@eTQ1X#s3_Z!$M>#c=Y) zPn&H%l%sVmR6$!Atz_e?cv6UQl?oHY7q zAb0mnov%Kb`B6`(p0n50OSNAg6uH}dyE;3qG5Y4^6Zoo0-V1>^FsldJ(qXTqzG&Vf z(T%3=ins-3za5>QK9B5=g)L2jN%O#e9RKBcFcp4|VOP3II*`cZhx!W7=F6or#JSE- zLEFDxal^1N@_fx2CnCyi1z1rJT}>)2$d~S@>5cT3W&YFH1BQGWeMZ%b&jH&;2`?no zuuw#?YyB{CNmb?&YZzU}g$qCc@qrM)HblG6w4-TrpdoAAzhXBX2gCU+VEaaP;~ymG zHxPG5AIenrm3ItC(cw3JHBrtPeJ%d%r2K-a{kW`uY9SAhI9yoFXNd!y*%UH#u17bT zCiW?g28@+Q6L&=y^37*q=ijZKsfM(nG;op;WC(Ie%VM|ZlOs~c_y1d+a+$9o7=*sj zn9*Kuh?jwoBy{JYo+)U3R6?_o**{wiI{obzs_`6byNP&uG1V{CG#r}H>1$8qg6n zE^NRnykVv!3Gh?q8EM=ootL;FqP4}R!1j{Zu-$8HK5Ys-X)KWkV^DhT%s_PQu6TYNA;o1wpBZ$JkdZ5}EU?s?DLZvf{962AD zwfZSqjV*l4l)>IpLr15O9=DZ7^2f8ka~j-MQ5?JLd7MlsbIQY4ac-*YaC#(pbYoLq zkqd6MR0t2>FveSduto!l;H2#mK*kWeqGkA6UPch}^_add3?Dxu;sYCW^HRP6wIvFe zYV-AAx(wOp6ht!`3i#f2wVka&LF{Uq@<>6U1p3L8#2-Ld)9mqLn@eyHPvBDs zjT*VR>-$DvRl!^R`^I^}ty1u@Jvrcsy~sh#C)t1wMG6~06R-PQ(#`Jyj8OxdpNgEH z@=e8KY`u*Sc`M8vHOD&|^!BeHN(O)yNdo!^ql8n2bo`AENKDI(1Fug#+-F=BAkm28q;T^ubrIn zwyM}0x0z^#+@7U?mUWlluY7}K6EO+dI^d96d~B3xi-KyK@73zaL08+s;b z14wt3oFL+XA04TQ9!U=`oLgC#GuMxx*G>vuc2EvyOwXg)qYdY9iOKi3fh?`W%ha!6 zN57urHHNfJN)ZH)7+Pg^4!lk;JUBlkdbaV9C>MdppF*Wd%M&Dk*9O~5bQ8cG4YEm* zO8TsUOjq^2@*FXwCAzQ!n_ANUnFgz*`J*tTY^dSTTmYValSCK}XDUFwW{rlp3BFe- zA;qE8D(SZT6lMZB@6)9*;}E=4Dk;bX%!Miu$S>A$j^nZc2O{|LMBC%d#ak3cWfnvM zn#gv)p7v?MwFK?D{IFL@1^)-!<-la5Q7%u68OBF&G%uzRx2QJ*5jh!UB{` z?;NJcQrFH9Td6tGudsGDM>OwrQ9YX3QtdLNSH12x=cl}h!zotnqb0u7urFD3JO276 zcp&7eWDGcFpJhg-xCR1`t{CY27QlT%Tf{WE*dIfcN=Y2aajo!8SejI*!o0Dv2Vh>@ zG}5LPQJ)cC9V+k~k9&A+siPu^r79c$4{)5tPAlxhTBKIEi>bvYjHra2_z-t&4e%h=0m zcg3sV zW$j=h4q8qd5@I*W;EWMD(W^DO;(a;DnUm|kKw%?qK_~KcbPNQDf@NW>L2)%OoY7Qd zQEX$~@1{!zc&HkZhg=`_rnCR1)e|EnT-!)ham171zk=|JTW5Wg#c!XGr}if&82EKL zc}7%r6CY9`iODZhcLy7OXm^MYU*Gc$k@bcNFVL~{8yqaC&$l5p>7qd&j$!X+3Ik~I zMa)JT^TRIcu7i0bO%Kh;7vlC$h!+2zx2J+< z(kjC$gB_KEs7w(>MB85e{<)&jA&!X+r4Ow;1<U$LD!AYrco$4C+2#H6m||Z3~l92?DxJ zVfIe4`!SR^n56Jk$~1P5yN8y`Bw^=3NX2TsMS|Lu;N7k$b6B4kyzaNIZatQb?o_PT zW9os2>y*xuqUC(U^Oz1cJ-rWhGzdA*-mE0?Zc=VVY!djGNH2{%$BOPxMmbSrko>F3 zROHtWf>SUvWJI(c4Y1fzkhR2_9dQP9G^dF- zCngocrf-*SVuLaV!F1J^bvfMm-vUwOWu$om~Q)^g%8|JJG>piDj=qC0m&|+wHSPpnk2gYAqYw8d^`9@5U!OZGNv>1*h9ZPwSR6iG?M#ujFtwpi|-V={%fUp=(gW75Qt9hG=|s0 zB7c{b9I(Pw*e7U*7q|8w-iYPvB zESZ%F9@>9HYymA?y8ZP@>x!Xoi>x=h6G$^}B;$M z%`vWOZ+#P?fHySzO>D4-ZT4BXz@<7lCLpqV6e#+0mE=df(T5|E9CflpxFuE4<>M4w zku6XJbWvq5m@jrYqIx{R?i%AN?B3{(sYrNka0Cwo{>r!ky(3WKrMuEPk`al9KAdfzMYytg5Q818c z*=L*LoEJGjB7ozRWMh{1`&v3$7-Fu2>to(#!clLJl$3y{J*^_?1GK(bw`(F0czb*Llf^Swu2A2wQuyJod+?5@{_Bp#^hY{vIo z+MQkmQ_FZ${#5g5EYlm^EOZ&?8BE@jD~m?MtdAen4vnFue9EXy5#S#kC#uyXuVz3) zJAQf%;rW7tAV)-Rejv`dA|tqPD%WydPT0~C8-q#Gl=|7AZ8EwBZyS_a5I#AdfBEDT z#nuQ=soLup-Y=&8Rjbv9^8X2_(oDlS)mxStk&8GqD-*!n zCGu~s;Zv&SXCRYn5X~=lSDA(lH@G2Sj<==ag z*+PDDs%!46P8=xtB9Z&ypieLQrzp3<4UNsLoeP;kvIWw$q|5O0OT)SHG~dIP@5z^E zG{1;pAcVTTF_Y7L{mn;QrQm?L$)OvbwK40I>44{(fh#cvY<3!1iu!kkTK z@b#tv4rPC;r@qq-%Zq4mv1-YUS{eEj{GI3|sx}Z)!TvhY5vwgxX9h||`;Agk%BBSF z`n;iv4bAUj*7tP%5@r?wawd-HL;>e6ejVFvRCZ??3ot>_{YuAOKFu!4- z6Pi<@NW3E=hAx31l(UD>BoT~FuBIMK;pvgPd-%kMSO9n#?k~QdP~H>*eTpI5Uo7$} z=?URE1)|)r61-KtL(=>M9n=){-?nSKkAHlkuSj#18!aQ&p3c&5C*y;`ngK#Su{j)? zikb9Jm_?2B%Q@4&m#HN&2k=tOrodl{8VwmtFecV0#ZR6tKhF^?x91XNDrZ;)bZb0q zO0!vJuywwlqf&rRoFku-%e7J{&Q^l!dKE~Zf2leD4tHC2r@dj6;i;#5sjK}cK@PSR zU2;VNA^q^?a_1iDG7M3ut*q9t)onTq0HG!@MN{Xa0j44+jevyNmUT^OPEnx`^Ey%+0Lda z(C3QrF93zGVEk#qFwP54oC9R&NuDTkf)6Nhw$1UXYjP7FH5I%4f$wrI1FW5M<MM4Pp`m@SWZSYlOQ zbZfgg(xJ~=`9cx{6a)dnHR;0_&xzr~p9t^zU9-h5bg=UDh-$W?yWD9og}YNw1be!e zfNwkXK_yQoQc|NRD~U?35~T#M4q~TDNqBNEvB`Vjps<*im5tY{*44?U^ES!u5UI9@ zmrvbVWM^w{$4l^^vfrL^lM!ErEM-ESw|uRGl-#&Z!eL=o+dV9%g8 z=*GWwnUakE2hQt%(o+AE9h)Sl6Xbk{{?%D3v?_4;* z>L&niK8h<>YY?Wg&6bdmHbF3=ABUD5=`wrZq+hY(@();HrrOJusLr3OqU+A@eZ<5D z;aZGTuG4V}Wu2$Q=Sex>Fv{T+GzHu(czmp{qR1d9m{4rgUqQoX3#_-~12KUvSUav< zfsGTH)mZf!!12W^#uc!k8KHE`UKLePx_(Q#hv4m)q)|baF5^~BQ!T%icflUNocH+N zXfH=9i(Y>1_ZFiV+pDw!DncCSSerx#35m}SLFj`w${qc~-^brPRTJBPE36w_3wMJ6IJ0j%f5d_Vl>jguBKx&`WrkJxMsKj zV0+KqbZsy)hhj1}g1j6UA*$-!Q;Bj5%g%p>ihni9K(?(fIsLmhOAkh5pF8! z9z&UC^Gm?B_LTO^%$yeW$SeM~FE$qUlq^ zt|iO3NM!l>yip*yrm!xazW~q#vTV!X(9AL;{hHxJNIy5y2M<9$`~Igor8McZEM-sG zlVeUzu&I{a!xC-wpL}S{8nn6liv+1uH05SCDDB7q!IOIX^LXamh z8%L4QMurT>;)kMSPeQ9wYQqKdp@w9NRpcg|vFyU7a=`YZ8oh@^d7>KgYS-6gUu?j^ zC$M0ue}w3FN;Xsfu8i6Y^G)_CkI>KV?vtc7GkN8zFI&L=HbqKUM1b~RfSkh}n^Fsb z+M@|>DqHK1egXiVb7-x=2h5*4DXtHJg8yyDBrZ$<#N5Yfb9C}rEB4K&gM)*CkyNT7 zgcRw+b+$dYdJ&xTpm&3}T307rCpXJ>E;DNe_h^#5n{)Q7A_kr#>s%LwWFIa0x8Ar7s5y-cmWK>wwDI<=p2RO zQ+of-k{J{-xmx<6sNC5VE`eV3NCo?g?_9N$5{8?o>Bl<|HiSFa$c4TGDNe&E#lCw< zF>R)HaJeJCwk|_JrX3^SLOnR(7W~5`0n?IRKBcxlNnUL@m&wY28pKrnlQKwjZm}90 z(A8qZ$Rv@i>?rU-q&dD3!}>#YjLRe5tSjrVD|L_YM-DKlm8R9tEZYP0f6mbs=s9O%`y}O9D}j+}mmRSLD3k`V9?v(l1?v2nz)8#p*NE z<=RIFL@~a|)d!o6FUn+!*6@IF29zz6(Xa3)3`5M328d}?Ms-mK(z)cevz59x*^~rj z!J4HQRP+kouAmu}&B#3dW+#>>$G(;vmicS1sqr%G)-5(3%g^n5tiH2Q{!MpRzfK(F zN)3VqR!L)}781~xZcka2rMhW-l84Dn8el*zrf=%=HJmW3g42(W3`G8T&WzLk7L!B) z1gxpsnV)IM1_ZE@(D_2dGon8~PPt#SZ&zC=3(8-iGwaOsFjnl%nF0Xk4;JxTHKVjGN z0}fK0CwMi>-2&TfM3Hd`KxDSn;6LwKf)hLr-jTH1l!Lk+4h5cpWeI{Wygj!xaIATA zM4__^Xf%Jv=jMM69sL5T@RDU+4s9|ji%|du zp;Gcx;V6-ndT0y(Z!=U5C?RZR@GNr*xN>(Rb_B#g3U;~*9r)C`huf+^bsmSxZGjTkt7c!qfyqtak!dVI!t4iO!>+N-%QN#VVI8n`Y zy#7IG(VdKcxh#4d0RfjGy0`k+RxNWpIxiCzNFXkxE7cDs_)}A$0ARY$!*_NHUn%bQLp7@ z(*=HBi<&(k+ay0Q#v@2qD{lATV*GJ8n_7UAc$W z7RHh+8p5|8vDTg+ne~Ws#*brHHdBY37C&`R7p}H{+F3{=mm(#!Zx-E2SKvkmv4id7 z)}Vz20;wP301d93T$4jtZ95Y`EO5tq1e7xwtqlTD&ra*t2C)!^6d>%z;a&*~;4{%d z1sJQ<&qcVyU4iCqYw#2sK=q#oj4FboseikR#UwysRTu6s4eDAJ@05Ytf=%Y$;p%CX zc>@4$M50)Llbyva#?VI!W-)cXlE13jNKDJ0-bL#2#z?DI^PeL(i3f@xAfIutu71t0 z7pv+_LVzefpib)|y%OCTxX7{pb*zj`R$PORI&a%z7#~nWl20gaM zP=I#yyiL|yo#-G@!2g22ny>2=$8Tp4+ub1S#=eb=V7yQKSCz7xdV@;bfbxrzNrN2x zJ=#~ApFA=*;n615VWiglBb16lpIOgffp*_AvtP%`B~mre5)bDnhIpi86)Z8lH|7J` z9e=iB9ZY=`tMflduZ*(>?vy?1=RM33Ak`Qf#6J1`hves)hB{kAOVRx5aDP z=`;E)+W7{}xjRK&%(r?s5wvVQJsm&W=$4U-2cJ@q@^B`{YY?4&OkJsRXAE&Nx2_Jv zwh*6Rot&ttbI^;|tvx;(xH^7b4Ns8}=*iIP`;g^MCICMD%MC)5At9gR^blIh+zj;{ zq2o%6K-?x~Z_MZ1yWcf9p1U)9`Y8R~kjP~t+mX|eXfEaVF8F6tg}k9!;Hli*f?8Q( zDW52k{5r#B^1`k?v4ea6{P!O&n{3&xy`idlK73M(OiJ@2jT#uoO`^9olz)gIrxujQ z>s+pE=tL%%iQ%Vrsibi$yvVO1P8LlN(520UH4#JaX<>-ZM}xLv2z!iuBXd6$IsA&7 zaJh?~zt`NOQVVCa4x#_&BObNqtMW=<2Qtv}GswP?xOs z+-Yum;`5*$J#U-`4T>ekf7#$Og*%Y`!Bz`#UU++B0!^Nyt{@@ydInUO2B6D&r-wVb5*QgOW#F>n6xX-N+mY2_syvPrq|=kiYLHTlsAx%kDnTG&HIS!qa@MZ_6JV=0XJj zq0c-a*8WpYvX~WF7;Wi+CohdyB_NY>--}lz(+;qDF1Ns5k6oerQ5wR;@i{7`AmTGS z9z5Vn#~vx>!+GdX@m8d`;EHkX*CTV-S%{>0h@KnueTen}Ap{PB2lTOU9uot8#HU)P z(D@D32XY-LKG7iwt+()5nQ-Da0ciS|MqZl+D*??*;Q(HcG!si{)*NE&X|T6UfAajt zdgfUb_yQc#(D%F-@Q5Heg+lUBnNjlvEqd>oD`7q9rEjOo8n+kKANev(`#%w|ZUEjm zzRgbORFAP=)uQ~3#A72)M5RO!B1|-*Mt*D&h~~{zS_-9pz@oz4uvS5S?LsBbsaulD zR52)C!vp6Aj{;Gds9F&|6z3hCQ1CW(-CYLDesSWL>iBb-wIOF1{3RUVU5`~sghhYS z+}pDGbPj_sG?!eMJl3}~Vxy_R&syNCknY++w!^pvC(%aS2|?c;ty=DKF)3G7VEpL+ zfh2v%LFMA)gx2&>?(U|GeLV2x%L!5W-9l{^eF2BbS!kx7c^7d6MHT_cZ!}7@!5(~T zTvJk)S8L$>y%43~C;yMA6wz|vcG^EMP!j!3OA1*-3FkC<6_1q)-l__dc{^-|5r#Z^ zZXp=SZ~6qQ82TK)2hIhsB*LzfPBUqq=Fg&KRGW**hU+JRUN|Dz#W`?iv_J!u4up=6 zUi8$2JXYtYE5uT(zE}ODi^rPk8Ih=8q%##iWB<_}tr5inTl@eg%0p&5JYXPd$0t;% zvYv%jJ^tVkRlWPPI2;S^3#XL9Qa9a7se-)D!PUnh2Ad8?MD{7`z z8RSQt(g_2$N65uD-ejU6nrzTS-(s5AyIPXK0(`e%?&k!x<90a0zsX3DB_{^OQ>{H} zm%Y5Mb+o$w_AWH4&b@`ce7l4%ZWEGnYjhzGq4v;p>>H;?n-UatwBwQ~onU%Us^z!y zfB_Kgh9>6&Ff7`?({!xL-Q~tM?%NB&^Mmsi#SMOfd(PG8kUljQn;amojPOn?{#K(P z#ia8)Pl@>2<#>%p4c!zdwVRJ=pu3h!{aBaV*$j^o}z4b3#JZ zbXF&fz4yEg7E?lpmBdMKjoVU(m@V;lLc&|P(0{e?GMu@-!Af_zJd_0Q_`;#>)cFpk zpK(kI39=_&{Y>u!;uub@UqZwd=9X6nCQ{*tnGwT5 z^eI9Su=1>ON^4oib0O@Bqij=UzObcDXRijqH7@p(TDfvKE?BKrF-cX;MZ9QwC+a3M=-wu~-yX$%T1W5cZZ)aRyzRZUO;<2X_zd?hxEv8<)l!3GNV_;O_2jjk~)`6WrYit{L8M z=38^ltaaw}sz2RN^{Y#4BW7#%dLdXsXQpq0tdIaWLZn%$$Z;-q@g1gqz+T-aLPW<&kR9NgxZ5XB zI)oj*=3Lqz%#O22hgbu6-|zEV>cy;`#v@<^#pHcF>aq#Q$8L0+n|etne*L!>0K!$Y z66WcvUL{^p?O~B(@iSCW+AiBMh>>S*73H%ehBS6>owtmXtv8|Pj?VYqt`TxjQ;}9f z0<;!xXf|xDzJ8O)SG%B#DmKVm(NmF>w>%m*JI|%!Btd>oi3%>8oJ0!Z&OYGY z{!XupIHQDf5wy@oGR2yp>A3~M2HTGv$*1Q=b!tP~M0E;1uV1{t8Qedg*RR6~OA$eH zsj6B8t%|P`3w>&yR%@Y7wIxxXuU_H^uP6{~0E9}kg#MP4djVF!wm)dNiAZezce5!}i5sV%CvitUPGdh8 z+y5cpUJnvK(P9LkMl4x!l8YaP73v)mt$8R#+D#jhQ2LxEs7qf#>Q!pJ6{^E?>0&6L zuX}{an2S-fHxrgSR6lHUsZn;m`7#3&FNN(-?v-9Z(ujeXn4463=1PFcN+h|t6+yPj zUEu$Be5no$a&f!m@#a6-h_-RKvbWG`d{*7u`|4cxHuQekzDldIcmxi-#>+AhXMc`#^~$NmlhSQOQl3(f8x( zNgD&hnMslD5rNPVUoxNzOM<^5o^7%DZqIMoI$!g`If%@>{wfeulDfMhf*K#FoO?T z&_SM{s*B9-O%cIH@N(ZFrukaE>=@~hXRtl_W9_H>ctC6H!xE9d1XoD{$eqckIi_l_ z^g4WK*pXM5p&I1@k|{GaY@GB<6s9ODd3Y!}L4jIUFI3J<+m9j4a1PW0_ETs#NWTqQtH7v z?LhyPuP0>=@CXR8ca(C8m_~MAx*tJ=;TTL#%8l&%apJgBjBPh8{R#c#3ay5WBJ1wI zh9~-1978Sh#5(e>dNsiCP1GomzoDFBYUGL14ZjZn`-l*bdGhxcv(m6wobL{yky!3?+ zav_fNc{^o8u;8lvOBeA2@s<*AtVlX0!T!`4YQa zpXLtmwz%?K*f>do5VEZ!0ThQW;|GjT+K2%b($F`lORqO7sXaCZMFb)9>!u^&%6L|J z=e~*}{?kbws7r6FnXb4eZakWrf&mOY;y{_tAZAa^lC@eI7)|bT<#ls)I2fhlAVi?+ z8WqBrvno*ugnK>D}g+C(<>P7Gyhb;n$sw(=VUo4zoZ7J^gFh+6^C+$ z((T93%R%AK?8Q(i4! zRRG6b-KcShIm~8`YQ=brd3NXoV`YF9doTV5_}QESBiDVKbipEN1s;pbT?pm+jd!Z1!A)%r~8 zC#6#+@Jy4el_W=VuRN8^6!Ns4Ng)j0OI*^q%o2=z_Z}tfBM`vnrh$F3w=ca&F6KX6 zvBreyE-ywo9I5b$#h4tu)Cukv($xbI1M+9ek?m;7=B;tE3SxNKCMX>DMs#mZm|hoS z-FbM&ufhi*d&pw}u@jK%;OF90u(+Vy}!@aFu4a5g6~KOb0hsBH|C$ zuXMPf%rL|+m2pRdKCJf^KUI zELa#0j33M54&SkB{|sB?z<7F6`n@LMg{5eZyeh8{fIaw#Hq!U@CDceQdybfauqZ^( z-{owN*FRYXI3lYnMUHsRfGpAUX(|vGSA)o(L0dND4CNzHc`<*o5B>Ss9uO4u0Ko+G zX3qVPU<+Z$3azOn%y=7PY)9 zMTY25ZnhR+w>VBSEoT$d0R9MAr-+m*MEm}3IB z^Zq%-L40rlt0F-D94iK~l93Qvzta+F)B>S`B!y^3m=Xht(W|A$*HoDF`OsK}A>%$h z+Y>e#7Y`AyFJR>egOD=o+b&ejpSDRuEnI1-u_C^~C)#?fj&LFF&TnoU{kq6*@vRaPPG2qShP!Q!Pho7TQn zAA8@{++Ke6WCdc+xIhLmlDX^JGYt^&2jzlPx9nn!1lO@qC?46vQisO}Z{l;xm-IyE zi-jqR0KRvgzq{l2tlEB-pTFQafhGmv$@RA|LCD?SY-30s;7Eh2W2#@4yeKO_moiz2m42wY_0U^H4$3{$+XlJR742JuB;UX@3o zSb2-dR7AVQ1~2)+fE>FWW5RZOjlu9ThPB|7#J%F+uG#EMs?bOv3{}@C`pQ`wNuqY2 z=B?P@uK{xLM*al@MFxGE^D@}u;~e>WQF7?I=_>O`CqaqEJ=~ko6JA599)RYyPvaEG-|_=;ipPl4>i%7E*KP+`Xs~86jC=*$WL8# zZTQy$Kn*u(LyHSG2wV`OZaf0~54jPtAIAbB`oK!UmX#K)AnxcYs#agA z0rD8iBwCV?C4a#W3{m8ygfW@6wp+UvzpXvbts~*0uYD!oE_^)c8ge7{F;?uz0d_bv`X7tK0F7p_pkCY7IV>5lJm7VqyuLfm`(H@0T#u+8N?7d)=4;o9NM zXAvR(221B?pFl~~%@JVdH8T+E>xnJIaT(C3d#oO(`X`9Q;nO2-Xta=f%VF!^;UizF zDinI&oas4&l^lN31JYk7eS*UB@E&*Z_ebYa#QU9~O<{{Mjd~9dy)aJbhA?%snE+%K z)qs6v$%bK5m-$TbNic@@|D{uYuzfgw zi|T*-kD*9}>3^TB|Gx_O4?iu>C)f3Wa7vXVl3mk0P%A`I3wH~)R579nRdlVde%JzN z%uyvG9m4Yr%eKb3@Vzv&qK3MP1=0R!% zaKh*Snoo}h=d0X->AhWnj-!JR+J6i38}=(;KbH2{mX1APfr{LzRDDB(kT9d;#LN2A z@lz*?W51*7f~+@A^oqsre=g1=9RtTA2@p3t*ZC)iYD0QfkL-L*KBp3SkF6WFbF1K)=lDu z4oB(ag)uq``+q)aNmZJA4l+fL`}1-I1{D)K>kipwSDwsNI-b@fukrGc$Xik|F{D99 zl<%eqLVe^U{vR?X<56O}823)|SMyXqg^zk5FGW(oC|fgL_pqD#$|@3MV{1srnE^W^DX%Y@UlKz zy8e35Vwz$WU{kNN*{=$WXanpF_|{#hzh&3P$Wm}1DDw9RyE?wqC~e=l%yKI`ys1}e z7kfJO%SbsQ3awC8z%cW~u#k6V-G@nt@u?1VS(iD=2DW*YHT8=JGV!&fhNpuyYk`Jv zmQ$)3+5yM=zf~`s8m?;K=H_8$nvCN?<_8~@v56pd!J&jwj2pE`HzpTObZ(yv`29OE z`qwzzC~e$fBSdzTd!1qCYNDM12XKeE4YkA0fXC!(QuojE^ zCo39_6?Es(cC@tPT^>2U%>$}vtv65(1Bf)YqX zte%Wn2PI9V!&-F~nS%{QBi*Rl5}YRj!O?K(Yu<^FM$!HTQX!d@nSZo>eV<%SuoPX$?FrYlW&Oxk-TB&Jvt zSmsnmdp5Qv$jpIHOnxA8^XHnq$r|nB#lD$@CJ%Zsdt$vZXa5w*n8Z6o($xSCrd%=x z@SBZRLjhD<-4KHT`ZAUG$#r~7FpSo?I&9oH!J?eyur65374WGrVF?`3*5cU3eSdEq zKmAfLv_q+e8BD=GH#PtOZ(M+!n=L1}y+@RI?1|bz9e?GXtY`ePwv}66@TC?dKn&w; z0In}tu9iSuNLCX=D-zQej;#{7{0ha?(UZ*W8^wAuF1T(j01PTbQ5^?7t z=1G?u5@K$5*uN7}3(9nLlp{5gcSfd49VBELCiLh>*#wZL4zF5JDa7!gbuY0h#*%b)IkBGSx<#X=uXM8UGjbE@gp8BQqw zC~8j{xtcY-mGUOH&io?5X^u!zxzeO9O;-z0*vwxzR|Skij`f&6U*!`5F2CL_x=P=d z?dVm%~@T2e60NX~}^Ws80tE?y2s|4d=}}DRb_Th@r!MeXem(&JExHs#w?}K0Q#r zG!~VLL6Ao_u_v$bXlXr_(XXmy{q;I=FW;a9jL) zpMC3o?S9GeKC#!}j?LJ@CM+#)2)at)om6E-W@>haqOyVpD;}3?sdQ|+le_08Pr8M2 z)}V(-eN5rTSeLIrLl0oR-vCUvP0c1!j(s8r1@k1&xg05a7gVW;=4+@)2q`!(G*N{o z^uzsPHlD7ko&y6|g00H&dAr->FY+u_5JFR6FkTAYgqe_YO52yO(E~NvEGq!@&t-ys zM|?GpV{AtSZzzY!+!(a9Vy8Nx0)vx%_Tz$u4X-7)ptO}6(Cbqac>6!zsxWK%g}E#3 z=O7RtDlJn_g$Ay5@5qq<#DbN;$STp4#dwNse&t|#_+U*DOU0zQ_Thv)e}?2DgiQd7 z?d;ff7$V8YPr(?_c%(apvHmD>USr0+1YZ`}?O(Y<8~e6i4k)d@dJLn?*~#j`&{>3@ z*Pk@?U7x6kcIMG;X#bWtZ21P9xGh@N{bUDhNM%Xve1q|tlC>-fgRaWB?8x_W`6p1n zyd*Dr6XH09+ozwh6)h?%1`W1>0`TC;nCbia820;>43esbe*8HS=zn&pFrOM{Z{Y2JY^uRc?_?~~lT zsgnazdL$NY_>UhP8Tvi-J{Ze(E^9I0F4h)6n9pj=7cmSS?b1RViM$%5FE1MPyYQrz zuw&GOiTc5X)(lVdfg>yU?4m_FbXpOY)!Y#_7$t)5s1%xtmnm^REyv*_5%6j@*xsTQ zbiDil!z`(9zR07oj@qgTkF>K_a+5u75j0}b+#O>ax~fn*iA#im$xI8e5?z(tpMbh` z?Xc&9!GzE?@*~ek>rx|3&&aI?$y*3KE=c@ zH%%F6fE13ex-`mf#%a($`Pzzq3-5T;j2St|>-yE!%UA2MlB|ql=bhFY3T;F3Hn8BK zGyTg9d&jD=dN+(hv+;lG@2#YnKtiAbn_5fm}Rv`tui3nwYcgy zT+ypgwVFYL5v1kj+p0=jQeXNbaq>m6)^Wsx zUo`s2dvIv%)1vBWbin6Sw^h#(HmJ~Q)2*|R(=n0JB@|!9k}O`mk%5D!rK;N+PK!l& zrNFFKyIJWOiLzBYAauWrP9Vg9y`vESBD_^X@?~Tz}%b6JSwQk(M^jGy(l{Y)vG5Td3Qi5K0K&-k+ zuBR6)jp47FWcgGn-^ic<|7oIMz@kB4fx-W2Xd?({vx8XR}XAmm&S1}u5SD}tSYK5M9(4o$9q#J{5%$=lwyqVpLvC} zE17LqTcj>fUF}<&a`NwCEKTR{>2lqjD%U)TQ`YT-foTUP)rt#qPk%g&G-@0l&?FY^ zH0A{w8dhqGEKn*s;#z|Tm>Sa&4~(J})hD_&U~nh#%xTOf0)C!Ko)#<XGZdnz4WL-{wTgNSZ0t!#tvM1kE_4^; zEPNhW4r*uHHM+l`2&+5=6|ub`6&bJ+@nDxa6JLB)muASr?1#*eYlz0GI^oHV+cxLM zlfwXy4~D8#kySzdKLu2%r)aSX(pnjBShMb<5yxl6X$={_fwJ;D`XF8N8-@(VWm+Hx z?w!=p-3~-X*aTK*$Linx_Y#nt-{&bG8>fi`#+OHHii3-y{kZC@tVlN~u z-ue%E(oGs_s7*!ZZA`n&%l5Byb{h}P>u8edPisgK?-=zqJO3EwGBd&oB{5bjiiQ@v z{SI0)-pLJh5OfIo8&#uI^)SCq!s?zyne}B9ux--YX+Q&N6}K7Z*F3!Fs;`QICz;nz zEETVkMn?{)FOof>!OV`SHJ^a2a6bo`C>7X`1w=uMvl|&vnfB-2G`Hp@=6cio74+5f z8jG(_n@yP~WN|L)n7n6}M~jUQfu)ePO$;Fl9f(iW3rg@+VRqO!hHCp}1n8-QEdGPCycgT*u2yL87P5z6i$clEwFDLqP_x+`0@&+dPA2(D9p8*jRIXF=k zBN?p!?FGOE_J^vC9At*eA{wiqeorX<#->$LN6<*T_sNyDe~QWi8k~A>Wd%5lzSb?! zqz0OqNoe3eZDg7@U$#C3E-hQW+VP+FiV+=Z&bMyB=4wh;Y280do(z^&RJ1RrnrKf# zXYb$Gyd)`7De%Td=6$dAz2mM@@X%(|oyCl1_}4vdkd?OO%;n4Zdg{o-@FNT4*|lz0 zH8@s0DR|m$!-XkFdFNoIr&z4q&KkKXna*2`EH)${p`IAWeqKpmO@E}l8%gWw9>jZ4 zJNp5C1WFA4xE}sM$!lZQdk?EyL1E0_OoXLBIlkgs$DDvK)5UaVod11kZ}i)FrMM%X z{1YI%MQ4p-?Qc-#MGaQBuDr*}gwSu5?`}JC@aCHNSI);x#xst8NZm^r`1cOH&8Qev zSk60qKT$#}u0=T^F?CGmT=`j$8_g7*r_tZ7D^{6v2(_~OnF_jH(4dAOWm(96<>rrf zx5GoLMxlF@fUJ%$5$2CI<*Ia%AiwV8gnhg?30l-pv2()Xe7oP(I>WrFl5{_1i?gNm z+XqXO^0PBJ4f@I0%d8bZcWuj%sfAUh#%&zCe(qO2<4 zj4>A~>lCdJu;PZ6X}A=PO{Mf$FiMYNc6L_d2D8+PFv^ncDn5VjDj%=ripgvk z0zHbJm$@eI=wMSwrQeJFDUxY)D>AQCL|qgrC&H<@UYvu7m5o?4HIA;xo!H5^lqd5> zSuaNhpRhVxt*YrqPfnq&5xQs}R{1dK9hrmFkB|Cv_>LKq2T?gB`gJN@_2sgZWk@zm zaO8PC&@r;@qjjF>r0CamA=l>PRVO8O`7tv#TBp-Qr(8GO^{W;;RN=6bxpTEPNv8bi zBl*G!)6H89aBuJKi^e$!)PV))5bD1$yEC!(Po>j`N=$lcWkT9)xP$e>Wr|7OzQ0)h z1VlhQ<)VPKiJ8{bzoaaHWz9Cv)SkO}k4LIEFx@yVnJR;2K#&St273|@ZGxh@Dxs-F zcgjipw6tYo0@>JNui?BqK&jZnKoPG!#Np|`dsQ34!! zMuSFY<+)9Zq3SXa6cz;}E5zTFQr6IzS0SnlUBr+ptLHCUPIg%_yKi?*Y$H;jD2ui| zZ@OvkRz`tC<+KW(kRBA_C4Es!VBMq;eQF)a?VUl?HYgIHQBsoZpzkwV3BuyTLgL%SVjVxarx8*V_QbN`l0IhC%$Ht0q_WBA-D@`M zF9Yc{>icX~Eg9Gg^GEu7K=_d2Fy<7eOHf?ZMEdsCIzVl)^xa|tY2Whw%yA@AY>?q7 zoR>f~aSs!Oy>G_uF}cC0k4&?2R+$eoTs9#x8&MRSKiFQ$EaQ46EDzACol`cSG*R;` zgh?3Aj4mzKC=E|Y?vZfeRZ;WscZrH@us3DTvob4{=0IU@SewIUJay)L0p8I0%Aoi1 z1%uiV=+>ud2@IAq%yCs!lp653LJk3t%>ISORPw_ttibX$Zd)-|$-xJNU~^97#+h~^ zk@-m2izyJ>E!&^z^nm9WmKrRiZrSuS2c9-0h0!B7vG7VpwdM+s?!Jd>t7mg7P{|2< zW9^4cH1Fgq(^mbndJUO=#!iNU|R~smW?DORPqpr!X(ZJJGw7^uYx>7t@N)+=*!Btljvr1no{engAC?7 zC>XHkO!)qp<7rjQZ&%Jx_nb5676+*54BG9|6mstTvG?B$P?fAga>Z~7Oea#1!Uli> zj~MgHEAx`_pj~(ccEJT|(JH`u@-gyZC#>$sE+d6XX;R)Z8WM_7M6-n-JtLA?QqAr5 z)jJ8T@eScj@)r4ELV5{k9Y@}J@=i0inF2oaTyxIo@Q-8I9f`(Xfb;ki_nfkjAAYkv z@&Jn{Nqy`-vJY7_HfaR50#B&YwME`D2W4`W%<%pUIB=>WmqCU)W6_BJPb2_8|H&jg z?6Ya-;ZH0;dk{9(#@v!Ky5V-vxc?;xc03qYmhU5%NgmmEfTxG5P5mQT9-Wln2ik^q z9#RI3A{)*ak8uuEm%m+ZQ_dZ2ujMyLRxM7`m|W@DDI`*y6?J?j1MgJwHH%TJaZo6$I7s{1#YRK^u=iWt0$|PPeTbP7_ zJR3047SLQX$&A5bdWgE@KkDrS>FY>jwcFTc2Fk^KF`;<>5k$9kl^0P#>yNPaD3Zbe z%+PDuHUn`<611yi3yk>~@WM2nR%pdi<<4U|N|k>gs!;bw3U#Svk66*g_bGAd6qI{c z2z8?KoZ}6ou!Yew4R4ZFcrlGDt8_FOk3APIjBk_+JIe!89OHA_&5HUK5RHy~$`rz_ z371u|008*xT6R5n^@+TD>kY({e?QdF!H}3Tbg*ZxM7*R4TUR+BwU;S1p4^LbJx!gN zyQC+ji#gs!LPOAOo}T}Hnp6bVc-C+nCw-+t);zL@dQrk?@a@Qif;vby{(V?w3#&vs zXcesPp?rTQJ0}nSYjv})x7IAJfT(*8rm+l0=O7>}ma{nZmaEzh2^N)UDd3IK<+H{- z!*q-q-H#->EQW%#@dbFa_de2BwvE1txZCl?Sk9y?nFb_0u?&heL(){O^A=*2c3HD? zN%f^&_uPRD#@L1A+)6A!$-Du|V=-W0rgL}V!d%7KyE5A6N>{-Hn!;w`>msHeJyUca zeaug>#Zm5Zv*4I}wCW#&H5YW?YHQO$)&|s5jb<|be)5@X_l?<_fw0Y3;)xZa0+CkN z7Y~iM)H4l}{^*OT1?fLr{iK|noPImr@k*N2=wKSbi^})Z^syEOI*3vgJ}f72G;cQi zZ|u=DKQsn)I7!%w=!h(%>GvZ?bJU$QIjXS+(P!dIwow~94qP@IZ6~D5zBY@c{IZvM zBw?gY8S`e=#BPJ~#+0vlY>9lkKQ_6|{9}^>f6CC&Nz#_maGGBJ9sSz?GW-m-64#t< zDwRk6G-sLVh((5q)ZM1T4}j@T#yF$m8!5^)!}50bCSIrx5ahHSW93LNZB{nqrw0fp zcnH9&fwbgz3l{JqQ;m5>wwWLvg>evcj^e&Q2psL|-ZepcU&ry8#1u7mnA+y$489GD zfQ;#V2dCe}8RN<@U7r6MXlH4UQO(%Y;Ipfo9|@zAn0v7IZtPI0&|gAr2lKWef|qzg zqe>&;pt%*-^U6oc3j{{&x#^;8M6f3dz3o3jZ9dPivQk~>Z0}ji9*)ur1DD6f9W_jW zz?4nZdgEVHP_06*jw}xgOShtU>IPdQicT+CLI8_C)pwK&R!ec$r1FIRIu-LjTfqKH z3_kdgsR9|)f2nv_BXen2T(419D@zx##IzZ-rp>hDKYzZU2_!52q zp8BVixtTy5a<)1ud#J<1p43!SlGk3%uhqD2JsmCK!%@isBE)RNjgB1Q+$fS~QxsKZ zjKDmzjEn_ZB>OhimU>1M@UAQhad~hIo2rEC=s3TxBOLs*fT|KIH2HWv9)0qGPDd@~ zV*Fs3J53WGS6{nNE>(7<b+1mEWGcG$bbgcZtnMTPqfzwJBEyZD7t zvp2wE0NrHp)M6U1I^&?UBoQLb!Fs#U0!qYch09M>_e$*WM|EEHwkin9Dxq&w2sGF# zf94hixGFM|IUokV+Hm#-kBM*@?RKE!hp`~yvIldRK^a*D4f;1g65%eqa1y(i?kS-M z@9V96DY>pHSIufbTNp#s(kM?j1{hzI;#T3xsW_AiaK#CPe9emC2&EUJ>ken|y=4{n z!f%Q{R6_lA8Oo;~dr8QYK_qGM$%`(pqe#->#l<@hM$O=W3;)SB>kk`ld!~H{`AA#3 z4hGrfs|G~_(w|Qsq}Vj_b}~5)V;*o9DbBCu?LD-AJWmM9?kFx;Vy`Pa4zUc(((8M~ zbk@kk&|zE#cVoFho~GOOYoYj&qXX_QbSYm2F(xTB?{LMniFYd#S~5VX*||Z| zyo5}K<7tk|V1*e1+;(Rc%b#y_`Q8xOh>y5j=iSP&QXp$pVqAzFTrkIZYO2U-E0L@K z!$}E9%Lrs_hh9cXOH5Yd#OXtH? zzCY|k>9!QQ_=vi6;g6Vj=x{k!Bb%yHw~inv@+9RS#)TAU({RwZr8$hWJYvwTc`6Rz zMnW$p^g)Cfdi{$6R64W5wITP4s;$O@EdV9xyrqHyjr~GJC<{a3aF@Y4fyOM;af5J( zO}pS(7wej5vsUA+a(vi(I&d`80Nf2{KPiYT!;S^X93L6lG2IKxFl?-M+S#e!Px9+A zDIP2?cWArrRq~%|jm?B=b)<|}S;wLMlgw~=PPN!5K1U_F8U!%#AytILt>^EymTcBG zm(T7MVpR@*)`>HN9+2LB@s=$-(xoEWQ?p>)V$Iz8^Q8{TnQ z8mL4tSkh?WwF8GBg|=r8q^hREgtunE+m+d*djb1;QGh#9Vfjt4WLv86K%VlzQq-L6 zB6;7{rH@fL_PAO3Z5fE^JI}|)c3HD)Bc5zMioCeC(1i%DUb&(_T~`}-HLb);YO`3d zPEM$xq9gI`UFQnj){5H6bpj`K7ze_m_=c zILKDFF?%S^n)0P}+$Y=jz$pxF$}o4ajfhGNlx;FxaC;ZpJ2LXyG9Y^SWF$n?IpkOw z#EzJf5U5bTxCio$GbinV~OP-N(gafyr@F{K-+qpSH;uu*{*F3k%Z-tr0M;q~ENvX9ENHFe!0ngH7 zlr+nT(ocZe4^!Iwx3))D^%NDY=&mMrO1>)F-%Y= z^K}V!%VS@$x^kc`Wps2uD131iIw1y*ru^Gkp-z{*BbjEjH_DL~oX$wEg-0I+Z*@rh zT?amAfEzn72zA>^{QF+|_qfX`e!E{Vv4cruKmf7eA;YQ6eqIpNAiT+BRNfbuyrA6b zAPaXI5fn`X${f5>GgNY+;-GD>)T-Ebdy#Hj$+fwhYYBdiqA8*SzP1w%PHww14H+~s zjF?_QNTbJO>{a?WL~-g)J`Z!Ik#w!OWjTxoi-JV$l$(4X8gKkj`{;>A zil0yy33_4e&)a@5k_6(AU*(!#Jjz&G3kECFgf9MC zf^YoJD>V_9LgIh+8~o@=)&z}m#K``=Io}Ry=BHF!HmAH;G7@WQ!`D!tw z34!g5TnZ5-k-N`(GZ>Zqab`|$fH6z|f@uP-ce{i|D`r?it3{qf0~aaNO!3k7Nm8T2 zaX6_Yv6*ukSvvyz4{Y#w1?dHh2oUi38*G&GK9$9*wV(endCb28J|-$-X8mDO2EJ5d z7arlf&O7nxH^&}K@^d2x`u$3VYU<@cT?%=GQCBm5zGyMRB7R^p4*ivQhFLoZ{|&`} zlv~D){CkF5oZ(E>(CkEKb9m)Nf(MIl+QnHF6inE4_B&$rB*SDYz$e-5n_rd1)%2GP zkIN7#W(UmxiqIfr*$5F&II{K=#C&!PTkZQNgjXTbLzq?qAw$Q!h?2oCKJa$iF)0Ds zX3pLP&&wvARq8+SN04w-y1Evld^r{n4utH@;!TyEFUjeXUycbR z$}vtJujteCi&4#s1|Pj8PFKkpLlhhKS|t9|Bep+o*J-q4S0XBH5{UJd=8*WEc`6hO z6jBMj(#Z!o)ch6j|BDUIJQAe0I$k^oP7i-F5wH&v(nV{l?-~fU3%&U|{Z_8<*-2Tq z_GE!UvmQQ<>`$8K4fga{f#aqC1cldyy$m}=T}~)*ChaMx&~CAKo$Wdz{NQJJzpVeM zET@+{!rFaQ<1T?tq6UePsv8{yki`(N6?@k`yzA%i9N))Rx?=Ofm4VpuVL#5@k2_%FK0px;#NSD{ zDm15zD&+DDCA!k%Q7KPT!YTK#$k&T7@{HLnIMJzMK6@dKhQ{w#S0CufMkT1|ltFnV zVbklPmpJpr93ZWFVmCXTRxXAN>}zI|bx_*2B) zjdE%tMeI&AJ#{H3p4V-mTrPBdjNd(JLONFf*0kDj7P@o$u1?ZVsP(()BhQ_E$nFiU zno^4wYrZwHwIndE=aB!o^__=Q8!v?l+O%swxd=5U@pOz)Jwl8B#q5&F6caA zH3hLd1+>sO6Emr)n)x8&(&JXuS@qY4i4CLfodtkqicA?+2PSH%z|%$g3-;0_ zMs=Jkk;d+2d=mZ?WL2(*FYvdw{XF}OTD!5#2b}k^9JYD=S%9TRCcFBLKSpSi*{#(WRkg%R` zB`IteCj2B$7)Hm7|*PciH}r}69VLi%|9ODt4b_Vz@z;I{hk4f8YnQ* z|4-dZoRnVt_Yckgv~T{E|34zx|Jk{l72Fi^tJ${B_PzbkVYOM7%Rw&am2RYM{X!wt z=I&2W=49Y6fmPRLRszN2>oXWKB8ycp0m8brgruI9iVe=tY$H7U}Vhn;cfbhvFv$la?q ziiS!`)2SI^N5&L_CC^tVmzg=aoLcYC*}>0A-Pt7O7M@3w$8RYg1B0>%-mNIOi8y?j zQi`0aku-v>brLLj0b}!Pc`E9s$v$Ks^Ai~peLO}V)c#@dPrkBO5`SS1Fj=*SPi?Jw*+4xHC z(FV~DzlZk8YuUKZVXa@u({We5T_yhOR?Vd&(00P7>2d+xsUB;tW6SG$!H~L%0`Z5^ z1H-ICiq}R>RbIb<&Cys{w!xy`8Gn$?bXtOFCju`A7(FV%&jwCXl1rKM`Vae!U<%ym z-WQJwjt^jYms?Q*@`&eBa97s@>%EDbU5Sd{@3l=D8@8BFuAYw=-a`)5GM z1<$-&@h@s{|I+Gi9lTwzKt*z6U9_9m28nOYp&0%ks^JAc`;TYW((XXyIOE#6KTg$w z4c1MEm|D5n(eHU##Ww%;NRc~MgpTl5%guT{4L3&=fiJV06`fAVl~bi7IV+ zVL&g|{yOrWPP(2SV6j?$ck@!rin!&D?pW);u)Xg+D(kvd&`pj_T2eRMJW>QwE1@8v zq9TR8Q0}GK0+4=hoLVf;Z!J>P<<+Tu%o3sIB_;Etr!IDq%#-#$yoNdv9*pOyZsGlE z;mKjePD3w(j8M#zJvc$F!t!9fKj5;iuA=ji`>Yd+r_<7zM$v4%<#$vgjM}`14ZMDx zQL3U%VN^p%+E=S(HPX|Cedna*ybY(4tx-OH?fpvRshYeYCsDL+ZX;WPZ_78AK3Bb7 zDfJ(Ve)>0q$EWAwrEl$X51(0sm6L3Fye^!l_i5dc6tnn*UAqqT$1eton?}RfOSrQ1 z;+&R;)KIccd4*Ni7yQoL4L46v`nNhq-;IB>AO#8VgJ;|=rn30m-kp`2@W1rrY~1`N z|Fqm?z_s{?UGp$j)TCq+ur9%EGM^cbMv^~ zbpuU$*C}#jHR@Az7>KgHhyF2s!IuaZi>SjCYHDg0qnFR`{DR7fpN<(C@-srwwNIQ$ z7}RmwjiU0Af4Fkb2%YhFg2U_;#zjZ7FIs!Hj-jTt@!$P?@kGZFo{C21eji7M5_~;Y zD`Bb~9rw9ZKz=~2F*peM{0hbd9Z@(-eN2Ze!V8ig9M%qScIfBMW=8f#Mn<&-(PuBv z2^5z(7anf7+Z(8!_B#m)7B5EDv5XnSncsP7?NVd=DON>t@9*?;OIqR3g59Q&dY55_ z&)P|DZ@puoz=9T5S`V#VT^@_D1@{YSZp;t2ndeK<3<5?eFNz-{>49#C?gV^M%n7*J zCXA`L%kB%C&TL{sF|y7)aFi_^{Q9xhd=4!iITzhL!F(ru2}IS)qYuVw*?j-rNhkiG zN{^$*kYw@+kFJm(D)t}VZHWJ40J^}0ug5e<4B8i7)F`v>%-TWybzii|-*S{64$B9p=cgt-(JYaU49Xpu%l6cjAq z`nC9qMl*nE+Ox){VybVeKP}GA4h|-53_cB(8PBfjj>L&$>!Dak;dp6D9vWkc7n1`% z@rMr%CQ4~Hh(_lpHzHR@R#!&e7gNLWt*MvP{<8lr%GG!D7<&~9&kGvK_nN?D@E=Ty z^DXOz>=REw?GAo`GcAG%KiDl?@9*N5N9w}N=9U%7l;7b*L1sm6Hu+*l05Yiw7HP(* z7{;R2rN#B{49L;ZJ}tNv>WYQ`?FI0-ervJmuH&C<@+z7+8Lovt+(46ZHFBtPqwPa8 zTU}j1Rg4upuw08C^EaK4u8e)t(eU=|)X^w8 z?RIR%kE`3-iR6Y}g5Ul}U*{POSM;v&h=k}O2u2q~iQan&58|_XzrUWn&f06Qz20@+XaAn(EO8Il1UzVl zz6*k6EeifxPq#l`Q$v^t46G>R8v5sjMN_~$NsKa7Fr}Q#6xX?TKR<^6U+gV`&oBEH zy)-57;pSh)p58-VVA5(MWLFuSW}U~>MHe2>^!dd2S2;zW;7F7&wID`g$2eE0mS-)t z(xsG_<~h|^R-W$unRxrmmi)&19?$EhthKQbd7@JyBCGhy2?O}MdXnA6j~o)H_g(PY zeG9F7j6rmwXl#3xX~3ibuOYJFVz=YD*Ieg>g5BPW{Y`Q@x(sdz5%-twCy|$$^qBEe z{W<1?P+k*h#Et_gUuPz@c2?qN7WrbL9VzgN2V#qCULT4oSzf(#y9$1U5juC0f9>^b z)-mFB8x7EHBTYT>EgyynfqOvcFH618wF2MmRh4OCNBP=O!#MDF40mE8$n06uuyI}* z7In`@8~O`0fqRqh=gp1m7U;A*%f|S;HIYgaO%=mtM>0%0VzUL}z#1hn8lb0&*8vE< zcH@FRe4)wCe^{;n*-Otjps-)vD2lPPeAq^U;k}0Ha z&$bs0Vk#C_8m_I3U3D*Ge{#9OuVc65rL@tiv^|s8Iw_^MsKjU+E;ap%yQ*f#{#hHltkqu+N!BV&dZUvsIc-=a>pVDLYHP zY7c}XcTTF{V8uH*wGG#}>1Ur@Xyf3q12BtR(si@^S_`Su((TZFfvy)qFB9LkwPHpU zEY{@;OP%UoboMj<(EJXOuE4YzjIKvt=5&~SZGQ{R*&Y|na){)Wnx*3ZWXatn0$a9; zyny^(*RDeAnQ%rQ&1mOK; zY4}{aCyXAM*lkhxySFN=xyBKhD zO5`N=cvx#gCUr5p5l$E%VEYwZ&`>1PjN+}S{&O2>JfzL@DFJ>}E?&OKjKJw*wY7HJ zkMI6CkxS z%GpzNvHR#MQI&$67eE5Tk+KusABd!ql5=ASed_3;7SWQaci(KpzKTRY)PduAj7^f$ zMD;M=T6K&T3#z`o0xsFhf`N9KN0$66Gh+-LPowg2qaPwW8J^HtNR>P1N_%3zSR4z( zN==#oBq$T_&(aDO-3-Ve>0!$l%gh7OHui5~lUrm|qYZ)!;D=ki{YGG#@6*1Mub` zsTKhfY)FKM;Sav+fp&(mvBq^v-zO28l3)5ymsCmZ*@0CLeMXmV^eD=)cLRi1sb~d) z0%JQ?Q=S?OMgvh*MYn`4rinET0+gK$02fbopd2gY=qBTOkZ7G$$cyPgw`Xefav#hV zbAp-*62;Cpk$ms5i59edZgY^JU9w+_n=?`Faz8UUc&(WS#i6pvU8#%qMTdBJyDOs*eNN#G> z0LHu}fxjl*zS0;cqGiEUFa}%+=_us|(DOz>AptmRNGfP)zLt^zb@1^UY)kR7G5-;H zhp8wbq$Lr$F8TUe!evsR6=3RMc4%nEmIO&DLat9)9MDqBgg)sAFe^bBebF} zzs(Km**EBmTXcX&%|RBn4$IzpIU+9e`+1lRkk=@0hjN5zfP+W$t{bTyQX=foU{7Fs zGUG7ssKZQ!nP9y%-VS}#a9OSueT`fJG+NEB^OX}CXnjRcu)AjQ`n-A~Wh8l2R4Jbt zY)p|c1qk8(5wb^J8x}V`Ws`DU0kF9kD3SNEvwMM+`*ijelcF|q11%HfRO+N$`MANE!Xazrnx)>q@J00XJD zu5Idq7bAGit2&Q+)#6AqjrPiOqjpWKgA=B@E8-GCM^(RJLgyPk^&nv&g7(ZgN}{q~ z%x_lZOvepn`113q60E`5U?zM0@)NHs8)Fab*IU1 zd#$W_cm^IYd3Mr{XI8q54y4Jxv*Bg5tmW}6YqYe_+a%}hn77pl<`ycD2Lft!#P|xH zui@(w`{lsmr_N5-@AdTgq3@`PP9s=ZF_WqSxGq2H#9}o|Z&5a>LDL&bLb%@B{;4Bx z?b1e%5nw8J%O8mH<>+B!EXo~+(-(5$qh-Wzzca>^c$-Sl}eT+NNiH3R9 z7Y*plmduDTz;DT0&9X`zX__uX%MXB?C{of^l`7}kUYh^-%<6~T70B)^GUCF`^q8wp z$kTCrW+UafvAc9@skh~sOjTT{bob!58ndY91 z@MdCIAw9R4@-5-bcg?B!Cw0dIEXg|Wq87+eD3$_#-?~~Rr~E6k>w7ByVOdp3Dzw&x zntBx_^Qg9V?3CuDU&V`LGO0jbAcQw{FT1F{us0n-D)+k?>m8Yr0C1n*?Q{vf`x`F< z4(J!h6x)I7x5(t~fa!80Pfn~c9hVbRU27x}Uf!ptBLXnI$VBI`N&%U0K67vLke~X* zKv`-Ar;5qL*vRs89tznfPeQOsPZWH*+w%w8UPG{5)p zsJQeGH?E4?9ambj*rn!K9tV3$_3<^<4J-{wp+B?vcvA zb5ax=dpkedUD|JqgI&E}*G6(?;u7C#AJQi%R+FHUL7PHL$)72}fa&^P;>uob2K@X> z9RMD7@iOQ!;g)sp1&)OE<}%S{bTb^kn41w3y+7u}$$CzabctW9+zn-1sVQzeoNgP> zb?W1~0N3b8pgPNX4!-u7^ttl?lrz@tTljQaoxgjnPJ~B#K>Bu^l+DxDM@fsu_HuDw z_$xC#+c#*0Fo&Nwp(hPf)S@O+4%&g5*wWkg6*O8E2Vb}xK&yTVJOcZU@=!i`z1Xhl z%fgR6cWNqir~W%vJ^ljc;>Rhk=)6kPgkBJJy>wbpGWKEn_*6f2Z#=(d0;c;!S8Fzr znKJpU4BCv0i*wnVmuC6zlH6w{pB-Ce!pY#!qhT)24^g3cPgmaib=7?dF38uH9%Q~d z$uYl6Mq683f9yLL8o(EGkhPW$t)~loob`hMB>xk-d{5ZHVG-~3^O>W#u5-!=7$cM>YSfJj`fY!GFxugT6V*=l~PK1lq*3Nrv-N}jlGc;Tf@VzC)K)pdpDTN<+Pq`FW`(EGkkg7y;tfN zXwsZ|&R6#S(Xv!^EDi&swoeZ3E9i^Yr9Gn2M;~iB#9gZyH9bA!)dAIEGcz5xi{#pk zwfA-cLl+A=mY|Y*=rhj*`h|C%#~*ig+z%;zM>mI_o~?d~*;L`6Q})}4ha@Vtj>9Va z7m*yaMU@eE!2Bu~!)o!WU4i<4HLg{90a{^c%wA})1g3HCD+MP`ti}0ZW?zp}mi!L5 zrf{IOBj8?HqOHQmX8RgAXtidn?BC`9NX>{OmWsdEXn0y}hOQ&b@F=pi5cK4ZvZM1_ zqF@!yA$Y=E{8CH&>>4=5e)}D0w5>PCeg9avUz1=a(YF=A69=86aVWT~^9bl)D9Epz zTFi`17q&D*k7U}-wWs!wjt4dESsMQoW@G4H5Fe?r{FJ>CK;L(Xs}Akb)BP&78M%A% z#3LX;D&Zf@((Yr%r^w#p+yyV-@oZm8LTe=At&5$IP`#OF0vxlS`SU zn?17(fMjToVeZ|98}Dz6$xgzke=rQ1)=*EnD)nWKwmXudWzUX04mvA=q?K z^^3wzPqFpkt7fSsamHFm*;~=GejR9Mk_gJ{LDra=rlM$_;e5P~$l4PRRKH#I>Y{QH z+m`RO2RwacM22Ok`Vi8dtd(xJaw;b*32)EC`5rPh)3|)q4+z%yj}AhM@;@wOt$4H8 z;$g#Zh0f7xK{dXm?T146Tv!an>e3&*qw@~@cGJq;?-h1%GWp?x(3%^MaszCx>%*e$ zm9bc+s;6?vx6Qi`b4~&#Jr^kqZ71eKJ-cVvy~82R|qeZb!>clzzN8CY^y=Ks+w=yX>p zvfN%d?C`rQ14bi`QcPR)^x0$1w+=vAHbL~T2J_7by-JnqqJCw8>;YBcA4$%c**V%Wt9-jM*FeWxS0$nK%X^Zv9e4aA1~ARJwT4XT%=(RhC!-IWL+d zqwl(FXX9&_%#4E2Z2d^Ag~xC+WYahY`TBWvz53@P6YZ&+IYZU46w!eU`Q+`+i$ewf z{=<(JBllefBI^0B9gus&TruIhOO+=FN3IMszg4HcJTstzcBP0ip9p+K{rJ!3YZ*3B-dy4UKi@!dO%&jZL4|u@aJi5dHr?t z&%*i_Vn*hUlO$OAS5bUFZLbYyqw8XwV3cM|Nz(qo?wOkOL2K&w8$TmMU8XS^*)aso-HJhe&q;&U>UTPh zY@oCX|MWC>^MNnSmQD!}R>5!NQc}y<<&uh^Pg0CiKQ-)330loq*H7$kgD!W5l|yim zR|#?AQA9>66ZIAd@dGn1YM=ESI~>81zS2+`q823v=1Vt}-}s4F&<~j&|F$XT4eYON z(&WA0YnDDdb(o=`QdbZQtbs(0q^|fTIwKG43R><8yh;@>as(4XhI-orcYQ&m=*mLu zWg$Fs8^2e$+f_^sv<~){37GJU^y>{7zrn&IGP>@lrk+cI&?gZ|j7xc@W1b^FR}MnT zA;ci-8uKuBh-uNp;h)YiX`9R-Dxfy4?}Fv7tKu|%b*uEYw&10&aQF?jrdqG95lw|A z8AWQNNFf0cMefux$L|2lV7wRmuB7+B=!?iR>CPM$$3PfE;xMX^A|Z>{f_R(v{Ci<# z@#?QjV#Jn-+`-{5J(-KMwO<)r*3~oG`A1ZQ?ZQgwxl`X8=N%U?nP#Pjlb+YV!w5=9 z^)Z}Y4F$kNuU9|kOVZq!AJyx#W#^Yx)>57GGHZdB1wUdbV9WmU!z~n>{XNX$$OP6X zcu<7gN8<3AUG};?0Fq4_w{VTtW}K*z?p6_6_jzjpKAVKX7)(5kMa4s~0rQZ5<4N zl}xpdA*oTyz=Dup0r^_3mG5Z!wfc_*&_Z=?!iu?^SCbxj1_X$a z3*B1p^X9I3T(h}~U}jn@SY<#S-IjanZ_lk35@8wIVfPSLymb1lAEeNm#M;*nRqjyX zirHW7;fm?jL-x&v6e(#O>YXpY8w{lmrwetPpCS*VKEbEH0;COdAa2S$7i(0sP4(e4 zN3J$ptJtT5y5mJr7?k7Q4W$-BfVhHg<2&vL@e01rsK$=;h zQb+3JW}Q7CLTvxMxNs51BxBkQfU!tYO{!fhX!kY)+pqkH5bIdAG~eu&(xit}B)#0xSq4m7Fd%$@l9F_aJ}dHZdIU zbtMN*P5D%wfpPHcWN&X~hV)TzQP$RAJ(2TxzVp4p^O)<@e+3p)D)?)@{ zayl*Kj^<%a^4)3vG&ECOH;LJdgkMw2S8`&hp=xU1pj?RW`Kp!AfV|#9elV~xbD8DKtvHMl>$ibVQ4XcI_S^OE z#71fH)sG{dzt;OZd#nIEJ~d6FChb}a3(xS4{1u71+fFKQtgNFa{&SJ16_ifW=Da8T zRYX#EJiNnDVJXowo6o?Xbg_M8;ppPc({jEww_-qxsTLEewBhFa*uRzvtXl4@DR=Eh zUyCz#jvy}>L3}X-;T^?08acG-2LU{!cUbApaQn*)2IO1044I)kH@>*`4~Dr0xy{BzU6?y>vH?S!MYh~uK`5GYV^`~7LRB!a*HWOKX> znhpp|->9stG*Gj(NFIITe(TG0rn|Zd5fJIYa#+XU<)?&?N*kEBniQ1x08YU>>s*oI zi;3FI;d!;O!M$#7E7AKhTe|L10p9(tx*^`x>{`W!T)*~YBwoFs`}jbg`(uYU&0rn5(b(^-*}DS>F^z35D60_Z}aeI zTeBNC@uemtG4YKr%mp@Zy0Y{Z7KS+(Z`}-<7sE;i^OhA5rTHasS0nvNIbcZlj?xRc;^xp7uN=OoaPWwpZPH>?s>4vn(?9%$yZIk$GRaxYj~yzD$Us2`ySp6F3P`g6k=9B(^u4HPXZ z)`W9+DX7ayhSFE&gsK#T=i9AruCLsdED_994lUbja(vmXkrJSga*-45hq6=})O9B< zc<$wO0iN3EP;L1>R)**K7izV;*xq#G-^01y56{oHW53O((?rldd+%xg_8+GUxqK>o z!unPDv)ZN*_kW6l+?~D~j)@=Vq^_7a&1`s4-8CD;t7i~bAc%m< z4L=Xz)K*TngUom37P8tSwd`&-*7rnX z=VBT>eS7)E5+N3vDp>V&j=>YmP7bZV0*Jg%n#ctU+WT}V|4nvoF@0^k#vsYaPOggp+>?8#xJIf&TV1=;Nm_o2zse3}V zp?&P~$_p=Vven`wf`|5l#|YmQLHl~*%AE8F=8E7tV)pBr*dS0c_w`0&f$K-=#E#;& z5r#R+(;dI{AoOC)MVl)*66St&E^Ayn_K4b=M|?@$-a+ng9uD`%=8j^nj*_k-jUF?{ zUzqRD$6Tzy?bHd~`)7cdv%#3ly_WR*|0mCVZ8?{qpLmBle^{X*})5 P^?0gd{J2ib{>}dY8?0%D literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/triagewidget_preview.png b/android/app/src/main/res/drawable-nodpi/triagewidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..d3c6f1c9145431bc47f23a404ab94123c19f49a5 GIT binary patch literal 23121 zcmZ5{1yCGK)GbbMcXxMpch_WbS==Q!1PN}7y9E!vxI?fdc+iF53GVLv`Ck36Ue&8Q z(>2r6J#*Wt``kJ=R$EgE1C^W)R2c*GqF4#S_L%kBM( zmzNg=bn5$u$ET;~v#UEC5;g*IE=!MCpm+S?@u|?4yW6{ak+8edvkOONsHscT#pU(h z!Lglh;_CX2rLC7n(*4!-?e5=$`u3sWpYuoOw>Njs-@*NV&faKPME_YRt!SvMY0@<~ zxpn*&9tCoA3$&(M35dz}`I;>vrNzc4*FQS5wf$#&W;rRhhJjrI29cztXAEHGw6wAo znpnKDwi%gPHa)-U?H7@qUzVJfKfkb|ZD?Im(@8-um{wTN%q1P0nB@|b$|tJo8Jgkf zpWN2b7Ze(wo?SdMyD&HenV6c3&8#G$=BsGvDXeOnU*2eF>c}i<;uKIUuWH~Ax%<8K zx45h(uJvgQG9@2#-_tjgSKczV_UayzUR~R4XdjkeTxp*3P&V@1@$2RE<}J@{FVWi)UhlRmg4*%QL z@jJ1wyRwue7st}PZ}moYC+0W4{r`p^*IQRp4`#F?#roGad@w$*gvv}Qli*Lm<&{$bs?wE4a zSkaW)qQJt&Dl2|NL4c$AdeBQ0!9$!t;Vb45q_@%Ye&lOw*sGIZl_JJ;Z{^Tx<8#+; zH!b32yZ19g)|24yYHgOdbQS_sb&~79mCJpvM?X6od*iSkpPIRDU}{v$mp?8E3JPZG zmFUt}59*q2lu2ip2i;9A5{KF8HJJy$sbs)qRNrJ|Bnl7^)Ya8({wo+%?Z2x3IKFRQ zpfP7<(GVP#Zqa?@G{I)n@8%LoRNU0omWrCOqr$=eSAanJbZYd7tG)o%XcqEO3K`ze zz`y`tHot15Gye&|z&re{T(-Ol4pWby<*05`NbvP|by3d#mXS=OuIKM$jqwTj zqn4m`{~LmVbs7wN_^KmAofs<>no(2J&SC%CrplyQ?^P#8!Y2c31t$#`fi^_@MV&`s zLyju7rl!m187+&uBLV#-eNz<8kJbWp0<2V~H;IWWXcG-q4Ixpx2(86dY}FWQ<@jG4 zf}W%zvtRM?&_AmKknNQ8Y5~52(hs`2y2iNDD6y8N6T8ys17i!MGL_0)3=(5$Nl%K zTlOd3QmyTyj0Q>A=Hi}KS%sQtRTR`@$?=o!k@}C;PKd46)#+6Zp^|Ds=zW7ZQ37@R zhSKH0@{?nJpp=Zk_uM5#(&Td8t}7Lz7HJ(sTNwLjBmzV!HaM+f;#RMy+A6C(2wXFx5`$;fFA@XKxf>k=cu zO*?{}RB|*LzCkfiE6=S82*$BtZqt7oEgoEc0?)GnD$w83U!V5R&&?zP-(H~2AJ0SH zk3TXBLG-IkS_)rth0Tk$-C;k0=vKl|OpF&Zv5-OdYpM~W-P*SoojUN`8Ixl3isvpqP;c^?eT4FT}uh;eE6 zO8tF-C_PpLd7SMHo4s!@_pI`j=_R@*IQ@vghLjIPUZ@ntk7s{xyP-X!CB3gN8U(GF zRrq53fq#EiDw~IZuMrgo^Y)j~Cf3(x-ik+IGrrM2vz#euAF#O^7IZk>)~xxunM~16ZqKsyEC{AB}Vj{J)tF6#r}2=sSI9@atVSqa(RC3vIKx`6FR9sqsV(UTt`P z;bw0LDeU*6SJj+yUGa%6R3mJpOk)DKFxqZ}NCCEY>prd1;J@)qWfwK!JeV?e>9ONt7xz%Tv5KEU(7mxx>> z{Wv_nj&EE}>>xb&sFR7~eNgvIm55roFi!-V!D8a(G9e_^-}lu7()XH1t5(kJS^mA2 z9Asctn;NOS%qLpIn~@yK9${uGRjDnY_Lq(tjbxqcyburCJtGM;LWm;rPpLmp{#y&- zJy>5~+c#{E3`asw>FF<2l;ZO!!MaTCdfO-G{PY1pynoA11a5a@y6(}Fvb$sAMjRCy zRx7x+Lj=!YM(+zf-B$M8$2Vw1>Co?krXS79oJ-v>e~R4Sp;((-xw4oPMW(!-y3}tY z1G9Hr{AZWfABsoFGi3^SP&@c5dJ{{bIArLPQ=j>w8u@;Dn0T`SW&Pu(51woee_aw` z)CCDfY1NHSvT+CP1uB(H1s$WEQy-nQ<#&(a)?ZVa6GMf3o8@5JH~WI#uKv1QO{`_f zZ`dYVe5Ejd%bel;^g-jXbdIp!X)QCc43|XKQF8|YBuxp-h$%WJX8P4cfO2nsK?+K! z?%y1q$ciGE?^a}QyCJ0)UVG@^0;)z_Bc^GrimiwbXW>?ga0kB|`Rpx4TxqhGFmo zb1fFG$^FD62qM3%8G|Rohw9HYG`JpJwWU-AD)PtFd za>}&1U>OC&YEC0ePE7RDfnos?R=OiJjH)iwh^cO{tLT%cNz@-MAgI`%3)4}rf(^b8 z`>1yg@;zU03{VVg$?a=)0x&%7`WRYc9j<-p4R|}z5TN(71kM$mZ}dLD3ULSWjd6f_ zpycu^;+MO5%!QL{!yzI^~qo($HbR_GhDmEkoJnZ4b^kG}LrClCD}~U9Jfq2*kDV}M;xwyn+f8l!-1g9to;QK9-^wN*|z!Rz;8{h}Y10H$tHmz+kP>VjB{ZQ$7xd(255 zJFtl6W=ySfPRZ>9xWOF)gC*~m)p~us=}F_{0sg&2Ypd}0kRV`P{=KFvGc!D(N5?I{ z01im>S=5X_M;)nfMIq{~yqwb@Rfq(Ng1EtZYzGi7F{|VwxeNwj0fmMjzhtwR1GO#Q zeh55aFuQ)EU5EUNi5;&ZL^@6M>Z9h@*OYaLv6gd>GKKk+K-I~0 z6XJWsB-kp6<8*qJ+8F`66L6P_7ZP*GvOMtT=61_;+@aBuhMBQmsEu*btkv+}+_4Ym zN+v0$E8~=6sCgx*!{^2YyKmJoYqZA2gCffd51CG`^aTJU476Hg)#%^sM2ezWvtX&X!i4WIp*x7T3iw_B`m}4ro8Xx zWCc78x1UG92&_(kSd&gxN+rLVZcZyrbn2O-(mr!ehzBG3z^W5_JwL7O&lGJY(FKzE zetvzt;}33={)CWUBSOc2-Sg9)|7dW#(0;p7Y_wX!)ZV(Bh>q>1%bPAl(>(_!<-^_5 zmhXEHYyF`WWG7jaZQ@C27U$&8Aht^l;y8w%!Y7$f7n2Euj&D2M`3ctBU+N`-l7cIP zG?#?(rb*5T4flA!N{-rjGDR?~S1NnLQY zEjy@)7F_SnqTnr)OXdS0v|6F1Br}vJ;RN+1Jc06qG6JJ4MePDOF=nP+KnO=i%^5*| zAO_2%hh-VxFSM^5e->Yv0QXz>@Wv-t;{jZ`CBp3mr5IQ>TUl4Oa1Y=;17~PP_#kv` zPKzVrkF}Zi1}ui>sn99S2B!{F$H(tPhw}%KN|<|$aftmZwvnJ; z$jq2$Jx&J32xvW%TG~lTust`!N~fWNBVGzBhpSyGN2W^(Ml;R_BG$lp*JIB{oTW?S zMc~j_e6GgwNL^iZJBhXiB(oZj9O!{Cs?a%Ux?rQiXnv|#I3uj%F+PUTx16wl>O_|@ zyx`IXUONChn%`iSn_Y|hU>#NV3x2D(rS3qEc)%U99IHdWo1l20sY32EDAfah|J&n9 zTfzyI+eXhL8dAo-HdrJjQbV-8C_=)v_0!W}F za{JN$B!Np~9Gel8Q87S>`LMtq?Jz0rGdEK$Ck;CEv)X|~ksuN!D#hdBZ6hv7Arit0 zI-Y9EkeVyYEv1FmVuU>-HbnR#a6x=j@p)oTkQDBKz>;rFOdKDS$i)OAuw!`qFPFbj z=drE-g_4OQd(MmDW+f`Qf0@bn_#9Yx9T!MbWE5KE>^+UZAuR83?-8vl)p&$DnnJJ( z*|8$)4j(+lB-~94XU~VF{WklOMqbd8miZrtGwXzSfcfw=qd>Xv` z?F}6K@?$PiA*X;JIKq7fyik%=679ixLLO+l1u$hcN>U+}i)fKPSU=!vy1?mTz_Ve8 zEQIy)FXLC5{j9dpLyx$`iF20ZSTpQr!NqkkI}M-#8mJiGX@4;^Hq5cc)?jx#J~NJ) zcvGN7I^XDvta66dm{=3Ov!?&8GrtNgS_C(EspL0PWi46Qh+LHNrWjDTZJPlEynV}Q z*qZe4t|1!5;T&`zPzDy8oskRFAeseo{JED3-X^slxwe&;#1d z_5QLuljR5Q=$^@Iw9SYx@iosFr$DcanV_`=`uOz6w={-U4M(1$aMVnc+nlaI?ShBY zUe_p0x-t!BE+NElHclg9bLc<U*NXY@v!E0gVWPJR-n@ z+oKMXOxyF^fQYE`drtxE_S&X9>gg>%Q0Bbvm2IqXG{4szCNxk112oV~q(clUXbz;W zk5~Z@@*4}*+l+|B_*0DiZtw;&ZPg7M8{6uEY^;n38#bs{?drDX0e|_~Mg@7XEb9)~ zn{4)DroAJcHZ(OBhgUNYjOxg{DX^*&g*!8QTY7OJq=P_W*rC7(0yuuyCeU-MTyE>v z=L{_9qEabvEK12~|KRnfW>(}zgOq#i)?YHXmRg&V=n|HXNiy{%{3`)pEAU+vRf4Eo zHHSS`QGOXyP2up&lGNb}MfyXNY&*=$*klBl*3c~U;qqoPD#B|?rG11fZnx6BJB3-S zz=5@4yV<9xk8E_sjRhTO#>o9oM87P)Qp-*>GE+WwvizD|>$>{^8s!d4HoN$0Rba;h z-2U*P2%rJk*;%o6B*I7atg}?GAf}h8hl3yq%s-41uO3HF=Uazr-(J_Yy=jqWzFup6 z3ah}a9N|XSJV;%1wv|Tr&G@T&8nNGScLM!)=dIZF!dHL0>4c0_zT_4^aZosFPmkJ1($zOy*oABN1H&}r`zS2 z%e;g%nQE4-fADS@=*YFkQ)xzrhA;)e$e&o6t4(y+as9a;OZ^0qCjFw6%?@IU zG9G=k&^)rEbJyKJ>uV^dOOV5JwL>E(T46+q9>riYDIF1Nmq*tOvBcC|$i@XUd~>jL8|U0BPLT(v?(S!|B>z0Q{QQs>j)bO3 z`Y_4OGGyPZ^tBR0l%{zviZt%|aF}4~oo_0;*~1PboXnPARvd zw3YpS8c`;qjD8A0=p9sK@kC6OJtH~Hd0LprG$TqC#R)LnBRZq*>4(5cz)Cg3Eqsit z+SIqoXw<4NnVN|>zjf+A3CF50i3{HUl(zTvw=1iDTs*s=UBjm2#7>;|A{Q$a5wy9k z;e&fWTj{Op-%>|_`(UA|RQb2a6twfIV}-wTbny`Ei}?|Zv%VlbMGOEd6$L<9@Zt4z z9gl!x();FxYt==o9KWSjcl_n?CBRYvZD1!SDP4a|mjxC$zBTfZ>zvSY=*;ZPn^@w% zJQ5&J>leuE#xwyVjK!voMRhtpcy=cXwx#_NRW8Swg75XdS>Q`yy5HJ z9`w51cUP|W50+a||I_uy5BSN6%|;{|j(?ScN@z}Bdf_ZWta;Yeuxe0VQaV?a1n zz_MSpPQiLvWvSS^CQ#weUPscGf$uBax_(QQQ4Hfbdc9Rc;S1oe4_NeuU+_`o;R-GB zOcyWR6f$kv-(Vp>eznnjvI^S0fjmiJ1CVmWIoO##)Qa-T=5TO=St#LU2%Vtbf$a05 z6Roaoyw*&Syq6I>@?_fntjmfz2ie2A{g;TL$)extQzlzo|9-Q<7YK8H@*D&RIt0l* z)s1GdMf%EHK|S^}hdp7*Z0@Y>p$}fM4b*wlY-z`q6%7pq$6GyWnnhd9WkpO;hO4=) zS4G1isAcBjq~iv0C$u9e1ZV`}nzlQ-huxR<<1?Q1ul41j!}aXC!1yp{-B!|DfRSPO zep)vVD*@Me1E`aFb6yK^O+G5GcB+-sECMb3EvvKZ_$h)Qe(9-^XNP_)G=XdXXvmOAXbM^-(}C+oA4$ z<|ZG8?g7D9)!dK{sqLnyAAzhd{$k?&7NU1w8_`^ihow%8cEoAdeS~`~hd_w~w^>_{ z$>h+b~SsQ)k;SV;=&} zf|>K-rV*1sN6g~=W9Qp-@w6hJ`p=`RiO{vx?WnLB23 zGa#X5L?G5CtVy6GaleO&$XZ90+R#|HsPM;+`c~^PB{~uDN`^w^hUc;b$0ogOpTur_ zmAC3UMB3<1;A+&3P;la&Bsd&1)4ruB`1u*O{vo;BHZQddzS4iC?9bi(^f}tNQr?u4 zh4f0kGfCoWm+bDt7QucL@!FzA2K_!<&=%8z?if2S<+CBZ@WT1v9;BzN&}caM`4h)u zY8_iM)u5OPM;L<%{cfXHVloMn+lY_;RU}>6Fy+LHaFBL#^!7T5S;}Y@Xl|tb>yO=w znt&i8D(CF$n(aKU76yfTn`E^m@b(9_X_+(8gV@yYXCH+QdHGcFZNk`$vdqN#P*TaN zf--u-PESLVXHPCT!@j^`o8;87~|U<8DJs z)C1IyJbrzM(wWw;Isz}O9&fKJ)|McONugFYn55_c*~qT^{I4h*t8zYA;(eKz!hTMI zN%t>tK~{R91SY0d9g#L4MYodiVMYk{R_YYKJudFzXG&Ir5Tgki(n% zE}p3BuZMpz^=NJEa6b_y7pvVf;s%HFQyL^k9PL&Nkm0RAOusx>WVY;j02euMMUcgk z1sFz``w^WR(1$QNlUx8g$qkLE+<$irMK7XZKB4%4$vXSXJIXDZRo`Q{Em3IEKDp=X z&A>1^(pXr4jCbIW64;N|OpQF17zV#91;yBup7lmqQ-3iD-Lcyu)gym0tC7RdRq85X+1azcJ`&crcXJ@MZ%Q* z^ysu%!d@d4UbyT`O|n0G0>$`M{My$!s@QbeCSA@5G7Gj?0u^L@w5Ae~Q#b~ioY5A( zgyI$d^LDzDkvSA26qwpqM~qa3d+K{p(1o&-nSNDr_V`iCH*BCHj7uY7+wC|l6`x`= z=KDE-4D|;So8oO1Lw$M^p%uUmyM3)u za1@GQca?|@9D2Yv#3V}*2M?*?Xj=kfGZ9A;Pov8@k_xP`2Z$xY*)C{g3ZiO5O&K$k zEZW)ahiAZ)&NwGBWdih0P|g#;pmCE`7}lD5HQj>Lv?rx=-?^$Y{@DB$LiP$Mj1{^= z?d0icF?`qFDB0Q=->=gA$wT=E<<7(w)G9*+-7$*$U#-E?HZXaJkE%gv{ISN#_XG3* za+hq=?Qd(Lwe<4N57~*#N*B^T9lyk>)`Eq8wmMVOT@cP$(!uF-{%M#@@;cfziGqz7 zMzPAc=r9-9;Q{1_#4Ve@Q82Rf2ZSs{)$phA>~vE*M3!KA<78YpKFtzwCs@5 zhOQw%O&dwXMOB?sKnj#$UZpOEtG6hWvz6go%AkxpZvI&>vBl)Qfm}2SEUa@)X)(#< zl+%HYCVLG9LYkqSCNX@t2979p2SypbX2;{p%L|VcV4uDUpNj;4`FJA zk`0`Pv+2azIe1vDAfED#4V#_8Ol>SYNyUl_kv1D(m@_7uBjusxmm(fS0MpV}qqEGK16n)6ERE0)RXviZ>?Nb7VAQ*&=cD}@C zSha~LJ8O@{wE8Vp(5$hOG6CjS_52gZRCb`%uOS5J`N`Cx6c6*7P-|#`v}_jw(q-;C~o?1WH+0JCl>o(S^J)G5C&}J*d+%KQv~+p3}GQ{TlNNK zKn5pV=vxmY6?5$c(&b5)*CAz2eD2-?H!7&{ht=#nYmUWRJt2ZZ?AKsaEu8p+I#B1s zSml$_)AO^EqM~{yXhafCSpS(e*>*m~=Eqjj=_nR3h&<;1Y5~qvZx|6w^H)*nBXjgf zkxH^HVQ5%!!abo;gViY-#7OEDmY^u-TubhaA}qklloVOQdJ)Av(6}x6-XOjE+h5{~ zV#Wmu-7z>_M1(KV+%`YueGsOav!mtJvhV=$@dN|9BWE93!JqaU=hi1}aWO}cu26*# zO)&_euRrQ@i;SU>^$2Yd!u;3JGollIULGQ(ER5e=5+W6m#x!Tk)hPy0;C(z18g1T1 zO}cxLWUwUAaz6tg4{!8^3XFbf^7Vu8PrTWtApnVuC>UGD=m;d7{~7hNFxG++_*_tn z{euR+Jw1sX({sF6p02}5&;yDQL!%l@B*zWcwOHL`^!1ixFpiGfutSM>S?m zBZ-9bu@VK&`qx5;IArmQAh0QrWMsPc0OyJhZH{dzPA>3SM+MXK@4uFYn{euipq+(0 zZYUCd#_zVGMT6yx{nQwUU2Yle;i-fKej)e9RcIr+AJRlI+0KWMQiIoc`dbc0DFEFp z5Z;c;34xvtsdHT=T$Oi=2sp(uV-z!lM)drlc(Ov~LlNU}(08|x_YXU7ED%}o%v1OC znW*A)S17MLC70``?GtPjNeM76bvMk?_0sSg(pfhoo*m1g!OxZ`Ik=38?OzXt%$s(1 zc&>yd9R%AUBYdmI=X%sebmWecFd^fgJt`*#8?!)Y#crg)9L5xE#ii&m2zn@B=eXzA z^&d@h)tC;E2)8tlk)LhszakB{vXU?#$dpN_2c((9@@O@B{Z7UL9~fZJqIy?kK`Pj% zG2l6RFtwURSiN^b&A6uOB6r^gq2wt1xah3@17C`b4e%8TiJN7D>+KC8{FzGxIMCP= zb_ino=_${k1P0yK@PJo%{`~pbMC!4%#M<~0GRj@t!x#OP04E`9Oe}nY|>zWAZ2zj{Dk3sttp_nu!(wG z>3BbanJI3~oD(7jh6_~I^D2uxZLfFFFTR^pkyXM4j1(?%6*0!l6T7U{6TnjKj-C&4 zvdPNF2>n{AI`qQ_wVHO*tDnPzC!?I1UvC^X)76&_nisa&u8?gMW%y9XhLcs+ze~r5(UC1>ZJi?P zv&ao)-;l;q)lM2~05a&l6dTGoOcHusIpZc=r6co^d4)0WYmq(w%(v}JNZ{p;^AaHG zis5$s+$~`-E4xH;Z7D=S_M#lv{WgwAjx&RS#2r-jBA!1(il~(I;MuKDqYKpHV?a9B zQEHdXOt!rt5vuJ=x@4(j62wnHRk)*9Xm9E6wtvZH;7&T3;Y4y^g;8i@zV0Tl_WaSD z^Zg$wKCqs#8|U`c)c9Yj{J(7Z|4?P%dnbeywevL&E%^g?+%I!N+3}b86pcJd0~W25 zz(ECV)|@YYa@C0LY4Phq(oy$H)r&$LTAk;(ejap*Q9!FvRllM8KR+R2{ot)zlUhRi zHYp(y@cp9VGoIX8?xT_uK#uzopSXJjrW;D0iPmRh+HR_oZh}}5=&vibUT|F=6^4eq zF&*D)wfV4d}hS&50_0$7db)g#TlPU~d>&gFa46M~OaxAgTc7nGQ$ z2~YYixk|T8Ey{l~Q+?}~v;IooNjF!>j`CjU72)z{slx2Ge>BZ9H*-XfT3BDM)lyaW(y}<*&$LWobDpGAC2ZBrjUzioI1C|4Ld*kw$ro+>I4!?nf6XOyB~Wm%}73MI8d^@6Y+U z$apz}zBAb5$d8yZf|p|FU9vOJVH3XW@q6=z z-p zAQSb)c5^Uh@-0QZ6A<#Y?I3+BX`}A6d)3~@4B^m7idyWW;Z-;Ns+OwC`*}x?RJ1!W zGs@Pg`^RnfGtyC(*f!kojvIBPc7+KMT@7g*fzsWBSYF<9IBk7GL0fTMcPEX_*NXu2 zYsXE|&d(hNc+E*O_Gw@KY*Xe@JOx28Z&P?>rVc45Kd&%+%H# zy(%RhrL!%JL1SMyfKyZjLo&Ltw-324Vzl-1ZIq|d=5~`h8;8ypxmXu^R#swwVP_x) zX4OaAN*7c4AZ$(RK^vaoTvs)7ujV291M;6XAnr^-E1!#F7t<@3mK0IK2zL&@y;wSK zlz^)q%kREuZ*!F0MW3(SK9VgQU^k(tyw=P9y{>Gm)lw8JjHygbIbA4Q0gGAlt&-IF zHJPhb;TG?MW9Fl%+<*SfV8zaC**2N3IwrD;rbAcaWyUyY^ZqC7Wm<4| zZO$+6mgt*}wM`5ZtOAUkm-r|860ExX5xB>VwWa;z$?}y~y(_V+wUG*3=g@_|KYnWAg}{<6?4)!Kc>iLmW!r_OT=6Gb~i@+{kW?IAq0L`M$)bd7ekq z!F9-`$JW!+UBi~-I8>)2@$77l#QBZ3sWK$sskqrokIi_<#pv{t@&3_`dax^yqT{E9 zV-(30bs>9?@jT`}B61&vqitgd3@M#nx6hUP^#JURQ{_jB0&AlV2L*C~1|eA6p`%Sy zXiD3MEMI|d9kc*0P19B>w!z5Dz3X!w3-kX~qP`3s)p2>oz6-JewnoqAxfCGtts>Pz z#F$zqf(vR+(?n7Xe&D%|VR?-iYf<{SkD~b&yDe!B?H_=39l2F7@vU!DD-s+*;lK#FzH;stsIQw&qW%&tf7;%P1L=;MgKfD`Eg zzXco?KlV`=my_R8@)mXd8$0kA*%w#sk4#45VuRJU8>d_AMtveY=VhxO#5;GE_cbA- zVFa35uZ#`SU9eN%3oZ47N&Vn{+Sb`FDrb(-33W+YN8z&R*WAKO=dF~^`CKLFVmT6M zVei(NHxK%E3*`QWkXE!FyARrGpZR1Y}ZO&J)k{33{_jMmbYBpipmp+W$%~&9?d(cbG9^gf# z4zVB~VUp{znX1qnzmS{HHTg$a!qTaVe2`XkE^KKOW4r7yyC>2UTx;J#RU8582=Gj` z(G%P~a$=1&f^1_G3rTDVrs7kz#T;eeylhuz>mL5 zgHM?;f(PB6TzRfmM12|?Nx4KIwy54W`K2{s7WyPVVjpQ%TAU$c+% ziR~rSmqaXRw%BKAQwEp7_Tn=JF%;hUl{@dYbohci_i`1DraQz>d9|=$QBvGoE5c_I z5ipPP>%|!*Oit1NMicr5cPqVzcp})IXN9;t#@3-#diZsbZ)+WC$r`dFn1^n6Mi#loL*dwc zSy-{ShbHag*{PKIcbpv<%{Ya@C}Bp7cU&qM^qx6Bb*92x9UMT)yc+iHAW^Y2#D0|w z!En{r`C@9x&TPqs8m;0x{J8_!RlxFm95+9T7IJh%i9vMt$OcaAZi@{~ps++bsZB~s zVzA(WcZG(tUIlfH19gKQ*&8NKlZIz7g(T`3nNeqr#; z-j1%n8>HcC5sC8AE|)H?&=_7MqIYCTa*ohdxml$5??;c=vL6Q7NSFAKDfD==dIzp# z9tW~3N^T~|+O^uafDbVKc^uSiH< zeY;1=d}}ERz6c)BOx44MBz*@0Wf~5)Js=(_!eCXCwJmWCpV9C+#`6-yqo74Wq?Y;_ z3ni`(h)j#ieAZ{`GKAwA>`Ogy{J@l8ZNARmCynu7vn$7nK+-O)jJFHeGomDbh+wbn z>CGN?5>6;zA7|3kNwI-8$<#2dyR@H$5uv+wUhAIR;{=WC%RK0_I^+F`CL0{FKZxya5GBWSqmgr&<-;TYiVf}jk?CTz9`o8O8^F4cTOBT_BViN>vQXLqNTOBG7c-(umI#qJ$t%F9;v9}D6hM9jAGo$-=tQ@gO z3Zm6>#Zh2Q$xWgU6_UiVrvg*L>%QWuqj=onbpFWJ!(kRWk5aq45W*H zZSe-ez92zhc-n>V__RsLDF#X^CCOMlL1@M69{lZ2J#v0}UcsA5zOzwLEy$xbJMkj6 z6%=BG0B7}PTXzU9y}6G*3B*TB)AtP04@fw)f$5&&<%jpxKyNZJYSYN00x&-?n=BW9 zJY1$Em<3PVI$a*;;U7Z6@5B($AQd|tBA_}^m5CoxQ^~6*Y1o{^zsO_Y>4z8VB-9rY zc*30B11dN=1?+}lf><9p4em;i?0Y^0bz(%EjSoqtY0L=!0xf2Lr1{PL71bW94&-aH zpRZj--dc)?o@mp!;PLZQOmi`f9jg>30sQtnTw23q6p*-j`{q#9Fji}7qj{ns?DbI= z72G2eJ!DVo_XuJL-8HffCZ@H14z%~^q(4zmt#$=Gm{vWsT~l% z`+>H}Cg)QTJ#~C3uj+|3fvh1DGBb#rYAT{OMDK3ON@9})A}DUe=r~>o4}fNln5NbI zo8AoioAa>|U_rO%*+10v2=UOKwAvhDFk=HF-6cB#K*HnEojA7X(9z>douxs%MlCr+ zRviu#Kv#aYR6*U1az^PcWpUS#QMm$E&RGph+%P)-6c}{Y zf26`7lx{l)4QIo=!a>5TQC6;n8Et{t>Odu$?Z_~1#29NEx;4PL06_xrR=cgObOd~6 z=_=)E)$fK-C%k}dm8s#B$QUk6C)$jO z`&fmGVI%nQ0PfezFkTxeVw`Hcl7hzq@|~D6LOHcp&-~HYVFuipl=iDOK%6ALsg^@a zTHR+xZ);{`2wS2C}~>Kw`+Q<`LbL}dr@HpC-lAQ=~`Sr{W9|LA>#_0wDW zm7-P4W8wTTbzK8a;Ae?k4ZLCH?#gQtq!_pQEQyY-GM^}*J1aHoY{d_fKY2CZ(^4X5 z=8Gq{6n$4c?7e$8PGB}N*7r!%a9V5gL8!feS!BNXI#9EdM>1?|LfQd2rnM8+vS?H1 z8awOkbTLwwoT!q_8r;mF!>O$~_eKV2MXx(f&TO7VA=(we8FtI6>@Ea$!feXZe9gYQ zvyo%1wg7qpZ~dhrM4djW+6S+zb3O%;DhO9I>e;cl+Q61NJ}@Bnd8sGOR)ZT4K&jtX zQQ6?)62NP2l8LOu@b` z`{QiWU(-|XT1}^150P%BDW_nu*vS=*e@e6Qb+ai8ugwbI)EmtT+W2FHN$7uPJTmO% z@4k#$(Hw`V3*;A`_xmlsQ|0#mmYKLiaV5vOwc8w~W}z#{t4Lg8-r86asYysNOBW;Z zg!f&yq5s$zR?GOjopF!nv!S4olp({IlJ`{EF?3D32W4WA9=a(bTK#d#=m46UVv`ye zUf--vS+UCR!spqjMN^5E-5QN-TBcl3CKU8&0k#+(;1;c(ll`)j`y1&%iN>lqHCp)AoWe9(exR-)T;1&zi zbA-tkgDs*tWK9cVe+SqWl%dzmmk#>3983gf1IaaMHvml-HG%%;O$^ zZN<11;1{-fCZoW>XvBO;8ltuH1c;K6y07B26kST<1IYPe`4uATyBkW})(K}s!qddO zqt0mZ#}(cxMs>xyQ$I$svlm#$EHng}daTpj|I8ylrngqH^t=(c|C~0q@b^Q77~Y*` z5KRcADZ(oS66;}@k$;Q;{pen-x|}mvYbdhguj47x-R%P2TgFlzjsCkLhH3zlOKOtl+ozvCtE5j|pM!IIbP_Gm~(KCydb)gT0l4@uKZq;Smv}rz=)OR(Kn-k9~H+R|&_N zf0VThgSf$QE#mJj3~1Vg3s;jlXnOOHe0NMP!B5}d{6r8N*D99vj2G_?Edp3lE8~4b z*;MEibL`&aMG)#OQg@lMCr8k6m+d??dx^==Qp}LmS0QM`zYFgKS-lpmjUpM}%FL2g z$j)IX;H+~Ebm}Wx=5W~T)q(@_`Bg6F2`4%gHNNPLNkZ>m3nj}ySEws zKTYiEl?KrY+yuUgM{4;Grp*01tUCvK8h&0~7Ds(JX%3T~neg14#V(6UD)@Z#-u&HL zVKhzOWCv<>qoU^;EL{UQd*U)3LHvDxNJ4)1PF94~4DSz3oBh%UHONi-knw_`B$hW( zBlcdWW+QX2rZtoM*DXY}tz>Vi$sihMPNZ{Mpp&39n!Njc18_#1m-7BW&lg8YRVAg| zbHm?F@+T_+`XYEg*y4R1&7Sq(sYHL`|JQn{9@69;;Hqo3S56vnq;bkBmjhebF##!tbQ!7Agy6S;OkZUy&-*RdLev_k&yBsooyn61 zc85@UJh{^$s9xgS@UIsI6+l7sNNSyTd9 za{*<#v~4Gjuhb-&i%H0*D{IGNOBh{eu-TUal5->xl|biWqfAu?YP4!MqvlBa1qZys zhxi2f$U`H`wy*fWW0rprsYrlb3yH$tp9yR&6ciyz$h!p0kq&6i|9to5_QYEvOar`) z7z;z>T7x+`6#m8S@H0TAlycov2wdecbJfVdfD)&_VXNHN9sc0>e_@LJDFm15=~Kx| z)Mg-g!R)9LUWH6ml+PBr+hM;DJs8A`7)C04B&0lOHT8FJN8M`K_)%1vkq^Ki&Ah zu2T1`Tm-R9U)+4jZRO0H`xUXO>(neF5O$lFk=DM|Zy{=8Yn?wC)X*rA21UNVv`3 zQ}`+Rb1ae9vq5^0GhRChs>uwm=zT8sNk^N9eVT6Gw>VIYH5;d2w4ThY@tqZ9Ri>x? z94cb6K@SxwFg5-aKQg2R!*o)k-L^}GihNxgcCs~})EdMV`35EbboI+};WR2dVl^x@ zZr~t$UkJO!T<-Y)#tv^_#sB3M|AQ6(uNRpAv+zGy@qc;6|7QOGPR6RK!y|7NrkXJo zomXCUF~=Hw0O|d)e(n0sIje7`1hm^8aVQ2LwLCfgVEn1Xk)qI4n#ks9d)|u@t;l(a zb_1uRWzGT3n>+MOEo~N>O_t5!>CRo@=B~RR^7mA_OdJl+P6!_$M#@J8m0Nnmv^m-I zP*&tjI`*a*FDC|0TIvX@|62DzuE8-q;ifEKWF7X_SZk1kgk|PGFF@&D_F!@D57F?D z!?VFExVQ;ca2BlP-+b<7Cw&?QX?ZS2V788`2&Q@0C}VmX7z_$^TGIE>(8m_Jirg1-pnsz3J{4~) z1Vl#(vkA;J760llD=I5)vgdUx@|?^q3aD&Tu{v$qX66&Yq@4syiE?9e zMmGzwiSrO^ikBnq0+48T?73~KF@vf8g1*^|m9$t1k`J415@~H{c8HoSg5NHW%RZ`5 z++XrNvSGk1;MgVnyh8l-qeZJmOYcgUp67_ZJYz!SFP&8%r*t0aQtRq-|8sc%$Qw=q zr%>2r1ms`k$^Ps>c)T|^;w|P0zGloq{!$9e>NCckVmIXC$KrQ_^iw8d*bqFyn14B9 zyrCt0!MH&|J6TT5ApKKd^8ph)4y2L6r<+oQGDbW=GGXfZ(WslPfU==Od}Jg-=?yw+uIsfNIp{Ai{!H;N3bC*mzGD< zb~P5G8%w#r*4^CEqA~_43>e50EisykGsuCbFrUdNaYiJ~Wb{`xuVTsxg{kvj;6FD6 zHft#Q8?C*d1FbokxEf_F#o+cInF#Qp>|yBWUZE3FJ0t^6?3flQP(-z>#zouvbbChO-wo@ULPzCkdejV_m zz1ZKs*Agf!W$S6A+sMQBzk7W1;|<;(gQ#?!weEAB<}(Ccubxnyf9$!2JU?F_9A~)! znc94s%k5~H0q`&Jx)h)$Q%5Xcu@!2}r?V?XjY3}wIJSQGox)5I_tobV#~(4mj8x>g zOL2gXn&*IKM_P5hSxlv+BB=>W&Qr@-xN`kXc(A+0UB}D`=gCmT0X7d29G z+*xahRlDn+t^~2Th#hqa)s#=PuWnrf*!kGX(^T1Cx)!~t_&$!;6ke@?5``v{_4|o| zObdrN=bbkbsAUp|1No)4e)HX2lvDRLXEN8k zfH4k%K6L3F@^LTr&h_HWOjU~O^MYB=zAzc&_N0OLy>&M&*Ss**jxu5+`>bFM`ptm? zYfPt3-G3g(>@|;?M6$mGJJea?m%MycnjhJ>?7%o>v-;sUoxnS^(!(xa?)S^TchE-G&9rhMFIzB4w&G ztaMvrW`gH|+}n^w{oeM~gGRP*!&&8y9*1*8dXo|p=IKC;-J=MR$PB^$s|^j`<-#X&TfY33{@2S@ zmZzS*JbNV)J-L^yd$)IpPw8Z)FRjc1)(+O5sLU1d5FVg{#8^Pd$Wy7NZ_?h$R&1oEq@Kw*Obq-hVBkL z+t;?w+-*<=+c?SANOo=OvE& z!o}~&-cH8m*0O<~&r#-%j#AkpXpVIjv)~9){KkXZ%XJ=FBm3TsrEEcyX70RLd z(3b4OM~)j(M(<%YfZcCizUH{ofDZw6Q4POPrxF)?|Ltt-Mzn&++QJHGZ_>V<@tf}b zZEYrG;0+p&+o@_W0v)PCaKBJd?yFN(ZFg`vc4ib=fPk>B0PYSX_ewnJS1ikkbs$X6G%cFP+ z26zr6i;Qll)uW-I&y(j>f4SBTD5tMDkIcI0Q0d$FmO5-oIjk|a;PF962bJ2i7Z{E&)n0=>03{aM8Vsk8Sc z+lea9-yfU7d66J>LJ~hn@?4hOX-0?#lc`xtS};OP_K)8=Dk$YAjzRHu!|$OuM*eMQ!R=Iok{w^4Fuj9`K(C&}#vPz%On{~QnUFzg z-N@c46&Mg1E^o#{y4Xi88!;^UJ-ipSWgV#%jUQ}%(NF^O;(s)LE*_n1sv++REKDXz z;E2*)m|%Ve#FUUT^3PlUr9H4iBm4N4TBP_YcO1k5JLDWh0?C|{SeJq?xu=h>mahk~ zz<+n}0x#^UKkg9N>A4gGFBu{T{`5I_AY4Qo?T^N8#VgxNN8H>cE=qW2^Y3=BG}2#k zmY|ho{Z~WgV*HEc&7U25t5q+y6DJtJ$;1f{wj0?v(ueP!r+oS3{k8%vxZ+K!3EQW5 zLHBVc28!BrrTS=R30yE>-pXgpRb_)4hVsf%o4c#Sn_Va=ISK16%&vQ@v}C4FDOqp& zpi4_>oeW5ar^&L(W8)sT!uhfpdfrRjSRerIl(#BAnq(pGA>?}MEunt5R$D2^uO}(^ z0U4X&Jjd5O6WtX3m;3UU@{yIRLm%k5uMVLRVSz@cKQCV%@Ar1*<9ZFhmqFe}^ z??iFdJ07sq@~nqNw*hqGBWk-$SNR5$qCkpU;P`>bfwdMhFoLabD@Jcmass(Vng(>L z%rn!qcK}*sXwt;;usmw8`!j+fME9O&QVmC2T_#c1(HK zS`^9KTl9ntGr&C7+)~@Xlxui8Hjwh=-akK3lYNtF?;KeeG=a_B-$5+qkyZQ*s`D*& z2uG(1+_mipl+}^#VbCyYL|B}Fr3f^K^tgu}+`kHr8QDXlMiMg*OK>;sS)oNWl}nnH zJb8(EHg|ZGsa+w4$lBx{o=r%lhp$aUcE}bNb*{cAA7Q>uvdJ5>V*G}H#-7Iyd{@!{&l-6JbhYC+|rJ@U`S z_2TiaD4oe_QkJ(HK~nU{a)rVdUN=e7Lsks)wR}dE&-MU6`kjKL<|kBKius&oS4@P` zNHzIHQ@^Hm;p5`3H+vsukg7}FyVXgK^sAg^T-@sR7S}EIZNF{-r(7L&RsQv-`(ALN zr0&t1dij@*idv*1Z*cvxY6%q?8T#g0sR;lU1?$G_Uda1kOkCjmy}b%)!K%Eo4|xx% z5T9!wvoWEo9Hscgx6UO2nji~OGj}8Bp~<2(31!>Yneq6cWb0nks(UpNr4|7nD^jbs ziwyX@Zp^l-3QppoMGH0tlx4{y?}6l;lRO%k`Oo)Zl~darZI-7z@Amf%=)RK#2rdOr zkT+F)+=0|^qkV-<(V~o~umCdeORB{-Vdm9_PFpDljbo!^P6dqQ+X7HKb2cDa&F@xB z$Lm6QvkTTs&KDfL!alyo|jtp{CeoAS6@7j}DRU5@jk;sMP(o5fo@VSY{JI zXDdBHDDp!tfyp0!%^lA|{fX6}>0{7%&cXf+2oi|#KPYL7pw~U94kn5BB)%>DcPf4V zh?7yW#ec1nvE8=WJ7B)EEm+DD47xf zyvXn60qMTq$wY9bRW&HlGEJCLiKI% zuF#G*9epC}eklNy!{0H(wb#AU;x3wX%B4d5{lL5e_wXM1Dkmk0%zUxwX|KWMEz{0$ z@9o)ur2LL6I)1|1cJOIf*RM1h-w3(acj`++e76S&5Wb_#4b(RWTJ;-8DSh;q=FB4e zGsN!0<0z@%0JT-cCIRFR>z=kmyewbp{R`h_GhbeS(NCX@?d1IKPbPO{@%Rl33nz)k z;P_u0R9a{L*4~2m)Sm(ed$kz-c+eNZe}omoQc1}pK$L8y&j`WwFi&*e=4IrDv`j~H z0L>sp?wGhwM1ml5_8o|agAYz5^1G|vMDoCtlXB=8pO{ekQ=sq!k0Qlpl<-?TM8c-~ z+UVwmB(E>DvY^f&fuHa*0B65+F!iaVMM)5uXY-?Ob%?E6es#mjZ;#2xmy;h}?Eak5 zdwYDvUNqh^(u-=B5NzF_q47A$Vg)9@x&7$(a*Yie#ynZq!4aVkPNZc;T@jS&@Xi#n zKWxbngv;dY^vPR{>9W2&!2&*kuUON*Kjsr2c*>pEQ%%V*Cryg%6mL02uh=W7Rsr7~ z(6&Smh@ej*b!OXVyz6Ua9#@Ly*^>m3;c(qy-VzWYmRaOKjS|5U&QlC!Z`9l|ThfPT z;UU(ieu@L^#lMLWtgS3*A*{JL3B}547nItcILj}}7rG=k(aR7LE1reBkPfnk#RJ`d z&_glUH(~?rP<4h&yxTm9T-`+7>RoNtfB_1FQ{vefSQQwa`odmkT4m!qVSF2`I7Gt2 zjO6JX`YyRna?O_L%?VJTiNg4(kduX^w1$QfoA8R96E3>zlxIy8un?ft%f;9I+zGD( zfzQEn`>^=?zv|`LS(7w}s^ayGJm8y`ZJ93$Xm|*&u%Rj1g?@tRMUs zw&Sw1#9t=WrcFJ2UPNKp^!GuN_^LG^Gju1DBji`2yosQ1TGbQvZ@pO|dvBstljJa~ zvGpjR)s`IN5Qmr0?e`(?MqHA|2Ycup_!u2oV{zdVcgdDKcx#QV%mZ)vS!+JBlPYIT zLtSZfxY`eY<+Yiwe4@#L`8X;ORtn4083uA=ZbWc}1yd-; z(WYdkKi%#I)k~-=nqiwbTbul1l5jF)KF`O>NWS<@BC`K=Cs~;JpXe>F?r`7hS8npm zoES)T2sKLf5ByCmAHi1s=(jirJ36J=8}}+xLK;bZ{5Re0CY9KU{p9^q{wKN3fL4a; zTy+SbLRMB(S13H2~=oBjStdQF!Iy8i8Dk!?9$vPav$ zD3~)(Fk41>yN}P!*Pg;6x`a!auJDkC>CPb3yod29i&iA z-&A8EckCgwkzScmG+c|EBGCz%K^ScLv18^yc6K?sX-7I7lWASuB~lOm8*1HpSjF|I zRytqG|1(ST2RX3Jop`Uxmg)G#>kUSbXE?pnW5Z$)pOw$mz;1=-pH*ujFuC(0`jBv6 zh1e^AG~3b9rVPrZel%AH+Gc zn2^ci(4YwZ3JJkFeP+4(5Us9*Ny&=0a^P>;ByRYFs#GWZ?4=&896de+dK4qms`);Y z?_Mz0?>Kb%2)68ARo3y!o=n5vV|0|`R6tFC+9uM>&}e2p`)8Nx50c4i9cGKHYWxX3 z+~|QR#lGL{`?gACu}tRLV8QqcRM?4zV;%xrS+%m6y->V`)a7?Db$p?}JvwORWiL}; zc;o*x9l_SXOf%*hnr1VwfqeAbzX*GpRL1ceX4Sz5&k~o*%MWLBWD@jCY7Rt|N^@rK zr}Jqs{f@6eIMmpjckB>|dDf;u8g6deqq+iyR))o`LtclCGJ@f9{}5X{02FyJE}`L4 zLN_CS6Ww9QUPsMH8GhvcxcOQjZg4C2SyT1h4FEr3h6U0+j>AZmFC1pap#`u7T1U4x zYU#kci)9gqW*Y!Am`cd?#aL8ZYAVjIcJ0&k`sYPfsg}Xbi$3?)fq4e)xdX-s63Cwc(oZ{6-}@45wImgCz$t?OrQ(Hk7@G zUJXCSPbv00IJvP0S!h6Y^oB_5&Kp3p^=WWX!9PJ*;S9szs=ADYdZ

#)XBdgHPo) zq2#~8Zw>?F08-to%hT@ca^?n;2pw&NWaz<(c7LiW^_*QIeK!9y>^C>_{&4(^YfQBD z)vs<*BW-%FRN6gu^ji(OP_!v3@6x_TI&x38liHK7Dk(r!OW<-M&N^8#_9BtN8# wv8=er7(^>p-bGxWa?1}t!c_>bUR>Y8Ph}m@<2UMVY``Si8V2gsYIc$T1Gbh63IG5A literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/unreadcountwidget_preview.png b/android/app/src/main/res/drawable-nodpi/unreadcountwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..19e9e6beeb5159ac4249805efa5a133a594d79b7 GIT binary patch literal 9430 zcmb7q1yEc|7bXOEf@^RX+$FfnKwyC20fM``yIUXwJlx&g0wMSe4#C~s-Iw?8zg1gX zTeY=yyQ@!s=X9U0+ugUmbL&Kbl;to`Nl;;6U@#QqrPW|yV1fTuWVm-k>28-F2IfN( zNa?GLl9kil-SgA)%kBN+{^8lX@LnG7pI%;GuWsLwi?hpj&GW<4zc%mS-Q&~0-N&cX z%e&XNw|DtJ<>~o-)YCiuynS%7dwB6~xxRP)^76KIaQ=>L?w!BAy)ADZ@10zoUfjOR ztJ^1MSNF@?N2e!u?_DfHrnlFBpGf+JyN8!|r~4<@kIyfwJEsB*^p^As*hI`PukQ}u z&~WK7@BvW_3w#T-ABkCzKhnaXQCI;MBIxIDZf~ESUf$OhL9<}z?DsyMk%bEh6_1Vg zk8gHi-<=}4<|!Qf!FTtMSVT-G=T{~!F=3SRe`n?;e4lAqL^k%$MknW{=2kw@3-k~F z#Uo>Xet8q}eC81XuB`7g{u=BX9Bpa;Ehqs>Ny}s9kzQQc+}uAuJUZ>}g_uyx&n<1_ zRkYn-y=dxLNy=;O?jK!V-)bK4Zk(l zWDnOKr)n?cmjx764VA;5J%m=w++u55dSid2nbRyJ@-1&{?Piy@ob5gB9$&?>Enc5K zYehfLY`*!$WCz9PMOVMHBp&+)#+={0LPsXdH??QGugiL0!+#c;fnSVB=TeU1)+g?! z?#4Lb5ytPS2)+w9IRtOUxhi!HInq?We$>|5G-4PO6o-?8@0e+h|5+?%=IIC{H# zy|bWQNHW=;ZMo`CImyT_PHBEUzI=4|33uRF>MlGlsqHMP{*^4cI$Ch{=kIICzGp&4 zMXvfrWldAnAF+XJW&r`idJzs`<>c9y_ABCW1JR z_jAnRs-~T|Q*a7^D0C)&E^=J>}Q z$b-de+n8ZLIESD~Qqlw9sU%_2BCxSdHKDX2Uje0fa_|U^x~i1|qI-F6rA<|KYv-xH zPs>XDG!lZ;rJHojZXb#)!mC`r9VIA{1NT?$_(@K zYJklS4n+V>eCo%5fNZg|rsT8EHr+t+U>*|KUgTe2cqHyq<$`A%x>_!IVK&7A(q9!f zx^^lw?|bGFnebp?senaXe@Tl7U$w#m1s|%lo8X5=w0Jiz=T^_JW+*X0f0=!l(bUw{ zdw*%Jr>d~FmQLP3c5ZNs^@qmz`5#0CZq*0=0Ob;;=z@sLzlM931b)8NG?oEkV2CsO z3t|0p-(y%~ZW;D9534Hlw_K50%@0(|?am%Td3;!?L8UtE6#j3wIkO%O#Z`idXtO_$ z2CckbDJtuH6&&!c98|brp~1Daov{9j%f=|GKFRE;ngGf51Q0QaI2APkiW-2qw}mXaXTcoX#l^yVf3o8e>I_=qUzJ&>O-?RLrc$f53?7@c zlT1S4V1M8LR9VbD|Br>L$R8-@LxCuKC6bK2-cn-c9%EBEEw1<&1LSIHy16AN99sFM zN}cx|bD5u&C?Du`i;)r87Zkrbv*jft5Wdy^aM%Y!L}=Js?}g-Eai}r1`QbC6FdrbJwCamK7jQnf5i9^?CUJ2!38FUEsfks? zh5FFo#SRjLdj&~n|A)u^HTD*BlmZzsJW7l&tdz8S48ymt1A=rtR2FGGv`vBc5U&RG zS5iW{zri1J^EJ~*Bqv7J42zw=Ea&V+I1ZuENoA%+ZUwJtgV=Un)CyqabQA^z*y#x0 z;F04553VqE2|z!9WIMw%d=oie5|O|-BOUxKV2~H%hI)Dc(O(>}GlE*^4`Ish6w)6jke$|(j7unqg7zimg2XkT6mRi3($;{gp*SWzq2 zEYAoiX(BP^jDWep?8gh6Y38Hw(UGG;0qRf600I z?y$`hnR-}6;UI$@OM*~cFkO z*F5;fOrD3=0I-S6H1>$2CYux}LGd0^C4A;{d*n$4u>i#^78D zO>)5|-V$E>+4J%SPp6gwc2!W7nQYESjnSMX*fC|+Nr6d%GC|9r7UzRK7tcx7&-gbF z4_o-~MZD0ZwlpWw-;+gsCHwwK^+s|$XcYVY_ zYKPii8Br094ITR_UtBIiQFMY|-E=7#4g$=MVm3+30yoP!q4El=e?iOUc>wjeAA(x6 z%27fh$qWQLu)T4Dbpq9b=0L#Ma*L-zd&K2dfJ3mv-6xk3aWUUV2%+NyK2YHsp``wf zq{6A}td_avpZuDti_M{FJZ@6P#b>-{Q{`-V9)z^{S@g+WyB2k6V!bq(Z;sALJ6L+J zPZUIM6!Rh431t#uzKArJ0dIF}Z_e>tT!#Q-qQ6vNQ*$`zP${G6Xl@TdUSW|Svld>f zK>|^_7?=vU5CM{D)6T5GjfOV-h+vQsvwa*VAaXTHvBiLww|~6vPI|0%w1RKuUnoZ4h)LLhC9SOA!#J7p}m=9uY& z;RCLa(i&pdB_G~`N{Cb>ArdCzFQaw9gt%sx2Ntu~Id8g`brAqEoGEZ(OXe=K+ z7Hrzscd!|TqmN>G%8KLUJN$KcS3C+6d}$R;H0U(6#{afG$tbuF5u;l5HKmE)b%&Ar z3O?sUH>6d)K&%2w!%oL}sS@vaaUiD3bGbOSs;0^T&*kc>k)z4mOG{H&GD{WnooBLz z5hGmERm4_J87Tbf=KQ(t-R;@%JSOb)?PEv^Liu-|K>pwCz1&s5nH%gr`h{;g^dkU} z%$i--H%8UR12r3Ih>gOiXY6hJs&c)I^KAA^(rqoDqu2-SaVa%m<1WAp!`e0Y;lnC3ZIz}P2@ zo+1~Rg%?TL3kFSrNmar*c#=~Hb{)P}@SEdvrI^4MA<)W#1AKB^<6{aRmXrsZ_MKf^ z+}cxG<1^U?9N8tTIBn@|mTc{xZDT8j*I{uQ+8Bv(q9XC5XlZ0T1oeU}gQqGJN~ta6 z2`L!kbt$&;17dP5ndsDLanMGo38F&18mzaSI1E3~9Z^;^sH95C@j87EM+4)jxo;yx z!-e&T$9FS0<(^u_meggk17~Y*4y$&&h^bRl3thlGhq@H(IJzM`6X;lzm#YAlKC#WBXiE0U^;T?}PK9e_b0s5)PyOprTgUs=%6R%Oq~Zpw{R zgL=E?$GcU`GNd|JLbC{MT1;2$n5?|QJi2<%kOg-0KD(nS#iAKH6+LR!K$&w8{F&5e z+9+2t39^Dv`(!6$#ZU-QIfp-*x5+Z%@Wl`&)J1y?jxKfWn^D9E6|1q3&`n0mh}fmU zs|%Vu*InGiq!hLqS;knr=suVHV~*)~aCd_tK#~4@+PrR|N4?pL6-d@4qq-@~jWGzI zDq(2}b+}+*XrCyZcz*i;Eyf7(ZuX=k$mr-Z5lpu@M4U>pTs^W`wVFuY<; zBw(nroGwUL>^V#pUoa7J5lxfeO%7c^U-_#-TES?d(aQ^z4=WZ}Fo~EksjpN<-2B<7 zl~?GJ9Y6SwHHoD?7??$O7-_pSpkBgihWSxgs%IF)rODd9C!N+$9Ze;d% zX-@Zr^A7qcZu+il+a!)oALBwZAZI`iB$=e_b+tT}3D`K0PNB5Bip20V{)l8|E-G<= zW2TIXltKhK<(0AOTlWAAU5P3)jL&lT(B4gF#W%G z_5W;k(ACE7i9t!!OCJ<}lY!INREtYj#33!-POjZT^t4?1%JSQB<8et@JB9bVgBT4B zZsA$Bje`ZtgA!I^U);eVYI~OHu?o}iE@x2+HfR0@<*cgzU;?H|DMZ?n^d)?{G!D3D(ANn0(PICw-P^bc@1US*%g6>S$y754IdS1w6U!*H z_50flg4Rv~9_FC1hMo8rIG=_Lg4?X+<39h+ya><;EnMXv8V{sLY-ugdLKj8-=PZUA08w>V6bTzIVOxtxNG?q*Fr>*UK=x7EqiRDSmX@O56BhU*^IKi7ENzc`T`S$v zB;lBYJg_0*JMLu=j*D@8BTBt(NKnZPgj7WAn5eK+ScYgv3Z6PcRYEq(l!W@K{EljS4=2sCL`)xEW}2JouJV+j@J z$4Fblhcd%<4q__9;Xh|5a3F)%KyD^k-?G7lNesyZY>#+;Hf=h( zY=Cbn1OvxQBqhU3WXL`qbUmYK?X76m7MpSl5c1 znBND~G)>6dgW;mWe8lYDi%#B_`>unN`Oicao#GFr&4wwbxq5iL9AF)E549P5e4zP- z$|_r9Sk*LU6aFGok)Gkt@iZ#KW55HCNP_Ip0NUQKY~y9a>z=)z=Btsexvy{7q7wfv zh9VjKzY`U(H1a!{`Io48V*{;ZQnPcPiQ#9@>?!1F009JGeT;I~;7bb$oh8Gw6~LDx zSB!+HKR(;6texl09%H@d0*VDz6)NF;)CLJt_y!sH8+=u1wyA|a{J{Cc?34j}?)-+o ztq8iT&w7al8sK6JkDbDz1GMQT{re9x{_Yy?QBI0D8hO>_z;Ujdhp7^LjUqz#n-(ul zAZqeZ(!9RH$Ze>swyR;fBd<^H7rL4=%^pZ)JMD?QUNFI#XqQ@vHMbowx-8h{YkB)E zw$%P<3PhtkXdv7>I4MNUAeCpTx>JYU!9sPG0YXooJC1s4JE`r~gn7oaJH6<=P>`R$ z$JLg?>G}$MG*}X#tVsO`CIGLLhgQtsRRv^oy#ES_-_Sc87O7@lHc51NvryH-%5X&h~V`~O}ZH%J}N?QbMqfewT}f_ z;GwS*$feE7)#hG49`GK4^?F9)X?^)Tx7QVFkS_L<(^IxA5U;oeVmWo^_3n_o6sc@l z3OKaI1sCiNpjgNY?O4)aN>hEzBb8c|*IOCt8~sR7q^p@KBP&D(o8kAaG{zTV}@`IJIk|myA$D=!ar8~pgxTZ+N0$fCv@+n{fIt~}nyXuFB<%Dz; z5c^{koWr;b5#fk`>19lE^H?jjLe?2DO6rW{#F8e4h}!eL`GGs)k~G!2g~K@20&>Jx zem(MpY;Is-EwtPk+NGAf&0-)zmFR=6h7^e}LcdGR7&Gk9OlYC3o41u(=Q1O(GUeqm zsJyZ97E!nx;_w5@a(W5raOvxOBO1dt#>wIFiGrO$eEm6nZ0pjYLp<%b{sMAl@Pool zG2-AB?hTWU2zrN;Q2Dk0XY?Ubk|UhP)&lESX;R;)LN#{w7s;)m@+mp3=^LvPIvCa} zk+>V5FpXCJkoxlSi7AOUXu;_%PVW_RePhweiGH-JT{=tv8w=Bky;6+A+^n}-C(f9v z+8t{8vOmU$f#Os+Nz;MJa{+W|_3%MsBB6r1gx*|6We0*xLtWtM@mulqKti~MwWk|? zmdg0=R(lt$FSu3z_vqk%#T5U&n1Cqkf03wp3rwQEg$K%lU&k-2!AXkUA2HY@TFn& zo>xxmOkyp%T`A~tRIn1<3FPDgh7$Mp$2Mn&D~*FPgzE$BHg!p(`H zAK?;qADt&w`F_9#qo=rYjGRg1W$Q1p!)$Gb^7fAPHZ!}@-(`v{cA_`o$tX(Rh* z4q-)T1KqUG-*#=zqsmDZr@^=N-fmeRoLZ1|upKce)b0Cw_24VR+$yZxXJ6LY&kD9% zCL%FeMS1`#H0vrS^OgR+M{Mt9@IJcf=IbExjj)ozmhS8*Xowjk>I8Itobm!erIaa-{Pero|FB zI%_`ONOPdek=9>i-q&1N*dluF!Tw!fql+aKtmER?h~$R` z_!FWWfR;9~Kx$qj9Cey0-v@(b1dck#`Wui2aia&roN=fZr|E1V@9-i?eX2cDd%*AI zKDFwF>v_g}D_Qm-;aYC~U3oJl2=*_v` zaD_D8cLiOJbt^@HwkPo*11lS_^HLWhgXy6b=WItv{a&k8A+8_s4aTS6Jbvfv60w)x zEjR%H@7gu8ZY&8u%QqB&x^dVq53Cgtw+LA?tT`QR1GzZ|UMaGADHP8Ab}6uy#y7Ap z*dU;RWHJVCr^ssBvE#;<}MRzuf4DW=p_}1rHy3sAR!|?^pp04-qwVDqI$)TfrH;&~sBEFx|A(p{a9AMg%20w1Wk5o1D?2&}Yjzdi>+4U^KM9l{!BsgHyG9oo z=(;@4qS+t%XuDn~3!W$cMCv1;GoZA&y)4VzdxHK;1Xj8_X^XIGdge zrYoz8eM~?2A=5=bQOmqLor4$`ev>$s2X~7g3 zalO^wK6*Xsi2_-fuKG);xIdQMIWpie7?*=i3ftWrH174o#Pw?1abcY#^NnbkzWLS$ zlcx0=mH$}223mtkP@{PnC*{aTOd>kx?OPk`Z6qH)uUq7W9RD+qrX)Sx{}-ui$d}0r zSOGB;rOBTje}B*EwNMM^$5ttP4?WK8`8l0zVNr}i*0Ub`(>u#{ar zF~Z$-_x%nII#~{0NcQbUdrcAgHF}ocGm%y>2ArJj5f90#oolj***br3Z||JG+#r31 zst#8FTHUZVE~d&S<_ww|cmxM6k#|xB$Qv+^72+L4O5n{Q_IE}pwvG66S|CPtj3#NQ zTc1a-op`YWkrFb3-qJQdMUvXajS3*jThBM|K; zfy*;riv492s0{*1O&oWZCDh{$T+VxEXVW(iu!OKaRsKm{8ym)!x2M%D?xb;-+zygN zG{_8+dKA$nJw&ouL8hdDZFv$G=3Kk^LJ@_B$L+JJ+PTj--^(^{W8*8M(_9!by7)s5l>2fnJ<9i^=P_|FD9 z;z!Ka1N6tG`G(mq^D$2-_WqcpO(+XT}C^`A+a$c!e_9M`~dtyQO>i%|+&1%5)mwVS* z;m;}Y=%%bfp5eJBwV5@*Ii!fLt@Pm)A@FbV8XY<3OeDOne@NFGSXCQ z(0B|9lB}q{Fm!ho^r36RkyFojTA_EX%E7UPreCQmAlu-WHe}*RRCqN5z&lf=!|+S{ z#*={sgU4*|zNw-3Cdw%vn_bf`x>k9D%5W`;u$)Bhi;%rU+7y#YLfneT0W6vZEE^LM01-#$278T#vLO7;#a0jjwSzY-2evsc}_m6(>VlViv!ay+UXT{swh%52bqKV$ZC zyu?1{2_jx@VsPhMX}vPTwrD}=sz07z@3rZ0X9ytd@R@t5I~r}YIOe<1M**Z(PlRer zT`d4L-90@AcZP++6K)(eumABy99pno_}%4lHU^IKc>Sd;#2I`#BK)Vu^G%CX&S#CcZ!C4iQ-O6`KsrDfzc~@>I@VgRz4) zGHm^i+%1#Zv!Y*ys6E~Y@Q~qxXI60@?bdN2!j+M!s8%(gCM(=BlNGiT%ll6cx{6c> z+$P@#V0exKgU)E1SoM3FOvt~v0AR&e15g>2wgFqow;l-;hc_Ih;Cn@HsK&RX^Vg;p z_Tr@FOEdY$PV+DvEQ)|NQsB_Ynrc_aayqT~L&d}gsB3lKU z1`d#D*h^7Ai6@roO*2=baC;ZpoLY6cF^VwteS0_nH@GzcgRY0u4#f4F&V`6Sg6>Qu zK(m(rW1SXcF5u{lUIA`o?)9SiPbW?tfHr$BU{8eNP9pvL>!F>YW*&+%cet6q!w7C6 z75K_?SWG*}3Q-k&HL$#jUBjqJ;kTyK1V0`+`m<1iQ-U}3*WIo)mpUxwWr9OhAFsjN zLkLef{qp`#XDDXn7>qf0mWFZ@T{s1S;Qt zNPn|(e0hC)g1o-IzQ4S^L0;bO9-kkdp6?!>9-bi2FR#CjuaD0D{yw?+cYcDr?ESg? z`1siTeZBqbqN!_aYH90VQ!wb^{r%$~^Zn!FpSR(vBD^{V<{aETEJ2XkZ`y zJ>d}=g~HV+;UCpYgw_0RJQE0M8j@tm8DO`X#V z8$n?SURBRt5jk4cLD_j_3CTJ6MO6veHIvixMKzt3pgz#d>*n#>(!pER_)GuTTv$>` zbb3WwN8i8@I74zLCBMF<2i!R@wSWGe)%TKH(Kp;N~C$v9L6<$fqxIsbV2kw*%TrHFw_blD*nXe|Wc|8>L-$6lPl%#Za z*R0tDm4$$gBKs>&#jolA1qy+Ss*)O%&(Feju+BR^e?nT&`0u)!n&)`PSQ9Ny+dl@f zgPgWA6cj4Xe-7w{ZNFD2C@QG0QsO^6HZF3X4M|QwAGw)ybk3JOe|BEp2ErJ`LlK}4 zsgbeRktpy8;T*-c9`z6MMp#Da$AP=kdCm-6&PTS7@vkel+|?kPqNAlhJh-JNc33<5 z`pW9U_rDKImruCLdtdf{N8%bp;)FtIlAe@u_@9T|%V%ZQJ#3G zsFhj_#VN>)?@!y6Z${onW4HtX>hy_PQSv@cc&7@L0bg0!Qn4o^DQACM8!1--rC1uZ zqJG1xR!TTxghEi5EI+XbWCm9XziodV{H)HT5ZSHgs8pCh09KHfk5Z{%W28c>p;ggk z)qG#|2O?iQ?)A(6sG{jeSx|MN{n?@LcAOvYbn+_!f~JZN%FOuTM`oq6H$;}}dnJ?>djmZIkHK?=WP(v5^KW>6 z{@lz+dihiw3@D>iGC#M9p`exXWL}_&rLuN^BulEoEOf&XVShY^E589thC);QZ-3=d z+6TeG^dQ#cPWg&)s63(-K3B!Xd3FD_kCKLb>l^8jEWi*NEm*(;hxyaPwNNIBF z&!q2yUz+?w{L0-8A{SgkNWkbTp`&3f;L|hn0 z015)^6P@GTqXcN-`$<(4TUg)@cg}c3S5oQ*3>U@a@&DRPR^#_gN|C66>Va9BCkskU zE`P-6NuiemoL0yy`fVK&hzYIGsN?O+1`B|~cz1aCzaGyi#NB8u%`cC)N7-e{BTKST zh!QMeYkZr2Ji(Pxr&BjVfp#tFS;w8nUVD^MWfEl^Q-*gp-`yP~!d|J_kuur!3H4`NU5Z9wJMsMjLqgunnA1*^69j9RX@5Qh}DHD`? z7RGh^p`rKwaN-749AN)3@e2%XlPhe8iUph=Px{E9O{C>8_y1stB1@k;$CRQOLn1uQ zpn8E-T$unYI04RO5)YCT`DxYgKbIixct*SBFvmq5G^pd8ie(5(su|KFYP)_q09Nvk zic?to=M8)osBhgXknb932#cw#*@ui&^Ii763US}P9=?%kvh8#bJP1cc&+iq<%^25R z9Um)H@h3cUagG*QUk|RR=&HN=_9&&-bL5E{noq5soSs!z>*O2uh#>Wx8_PDgROpch zQx^31(+CI9uo63K@Ud;#SF^&Y#>&Jph>G?ld=@o?4N`pWJZ+uTtVgcgM#PIMVdCQd z%*?d==mIxtGqhaDq@S5sl=XweR9pLgZdqJTkL(Y2CY2G{OM?r|mDEkItYJz|7;22> zd_@_SQ2pmcb##5(J@9*``XYU?MBlAB7v<$56b`2p8-)mUa9SsfJpo)xCgFo%0@6qD zgiMltC)BMy;HGzCQUb533yWUTH$tYs;I~`1{44<8C~ktgoAM`Fx0p zDL)QZA?4eyd)mSv#p@lKaOlL- zh|X7)46RF4JiKK6xB7BOE@qN=-Mr7>${ukTZb7qh8L~3apQsHmwGubwKlm=6YaiHoJHbUU#xHC5JL_`m0Pkk3``f}TTzA-?JnzC)RY>DD7Sn+2T zw&qb(kJ47yD()yCRICZvApG*GY~ zw@abEUaJ(mkP|v=6NhoT&#UASEk*UKsjH@js>B)1!((S-lIsPr#QL6V_*;%&wT4YR zTrFHAp#jy37>Y~w7c%1ji={i3CskeTHP*%o4m39huv19i_n%wS%)gNt+E_6d1a*gU zNt2h2f)9#~Lq{RAd-D@c98J}!UOkgzdfLKNx{U}(=$gm(u_pcf>zbRJ>l_QVaXz<0 zT;x4HEsGQ~`b65->@o~845Ly&i-_3jklM>!*bpkvI_Of&0G>wFfko+Z7XG0 z^wqd6ccnJ6GTko4O^61KZLPDPdj}dLBZ&n)*U+y_Hi$t>Guj@g+qXMVl4){YMwAb3 zX4SZ~+YO}{2DgQCLgJWzshSW&0Q~T?u8y9p`n~g7yKeWLOn8-Bp++9Te>axLy%e>( zT~{o1*zo=sSsHWG#r47UlJ3DqD<~dSDcT=5_iIiIyJQ+!)~{;1anT~B{e*J}Q```v zjHIz)c$guPM291m0B`8`o!C_5S20wVkyNwbu^&Wqz}DCz>eb!V>9@5$IAknc26(Ox zModYakf3IuX;cpwp@_8;`n>IAk!%h{%2ylMFLwo*2o06;9HXy9Vb4$C#c>Bq{(q5q#E(?T2+k$-g;x)vf@mnsFB_ip%Q) zY{pJ0%K~EzB8r~T*@3}}THeb+{3c65jE$y2u*9HTouBlQ8XDDn%iUBZ9WZg6%l)oU zA3UwJO^5*iS=LcOcqrSmH$|@`m+u;lJ~E!hs+t7h-7=4=slld36dWf%-H~0*ezM8j zEoG*<1Zcz4%5RXOGbEo~+Op8^!4nA^lgPZJk~gslDC(RFI8X!Wadk!;uv$TTx?*%e zoQMo_C0D4DuvQ^ZF@L|+trzrHz{6#~FxawVWuKhg;$V_TxYkU^4=}IZsb00DtNzK) zAh#+Z9AV#rHL|T}yYKaw{~C`|3+UcWNDUpji~R61&T*R-_rm103<|c@g*n3!0xYr}@ zgWFkW)j&Y95iNa6c<&3j5y3#tci=#JJF27JCV*#Xe?PrsvB1o#(fnHNZ61ms7H;T* zto8n3ZRC3{6-J*BH`W~Nqd)tMkaiPv7)4mk_#FSYdP>pmYzhfs;)vLJB(nCA&j~!! za#TmWFjq0}EZ~#g1b7qsVeD)}jaiVEcCMrh32Q4piECr4mlOLYASY`|^BA2~P_&2- zF6ROtK^K!aEU_Ea0|g;t)`)k=cUQi+ErEH_M$oq*bP6`IpP&iU#2ylyEzE{X8Ow_g5w~*At8oLN&5&mDn&8 zE|IdinV^+Njqa3?R04XC@dIKc;kUv{HilI(@?VWJD9kx3^*?>UO6G{nq{;Fsx;Y{Z ze;902e!`6_F>;yaJ?Si;6oFpn6+%ap~y}>0EOY_Izni$+xlZa4b&1H4^ z@lu&U2;%DI-&l^Bz{YzWHv=BhyEA6ZVEnKNiqka`7!d(TkzZbYSLKX?Th%cVr>(6QN0CDF46)b3lymh3YO@lWYJZO{; zsg(eLU=*E+1>`6d-$t>RLl#UpEu2|G;L-d9ozWz~LDS(j#ukW57^~tEKo%pnT3=$S zv&WE0dTTu#DK|>+1bCT9l)z^`9sT9gNKiUU@1l834_p`t2($*{iL`5f5%N`eThqam zSyrHSLkx?v!JWFmtUAI{$_T6>!wYvL2_Dop!0aF6M+m^&+Muq0V%Tc!WF%&&uwpVs z6EbB%Jq=5MM-ik>IlUsvM99OXWflkaae_u18-pZF@56uPENC=h?+g^aTK*PG{^;N) z6-2ca&*6e{_1QKj$mJQhf*6p5=9VIF9X&UkQW+YB20l4>3^lO*CW6y*sTq-PqyeLF z^VnpSZ9zCR^k1&Lx0xxb1n_n@yL*VAt7(AQt0pq8W*&A3X|`O?L;k;Az=s4 zgc-V<%6{N!OBU3ThrO01u2H3bQ>?(Qg>r8ftP^P)ZCE@u^J7z%fohof1iDVjiAG|2^DaT0S2{L#a~M^OOTI`t78-4=h75v<`p6O=*j zd(9^2SXs;7A6NP1Q!$P5uyS+}ZEjhND%sO}b~B4t{v^Hr*Tf!NA?D#{66yVyHfF-t(ZlMd8&+lF&BhTrbTX}oPWI4( z=uWljQR1wqQi9FCcb}BSC`ma2sL~Pbh+9SIcr}4vilXiXCpYR6VIv7AelgkhZ?-3d z3X+aCFDliRXw8n8JYtCkT)c~16Fwa&5yV#oTPXJ2jb3Cw3<1L&1~o21y@g!Eic|F* z>g=+@ml1s$!SY@<-@HCD((_D4hgA+DS1}!$iu70G#y1q*gH=O@djSdwCpP;fZeWdw zR6Qfz+JwJUNyJ2mVQR=5Iu%7XZbL1p{0R{%4B zhNZx?oj&+R7+uo1QG|7eNp;;#ZRpMZstGTn6#KuAZtieUx5PLs(Z+v{3EC}h3dOjz z5^Tw-C0?VK=bRth?hpdls-sg`0|*NqAKh9dUkRD8ysnhmKTWT+Nc92=36nlUjoFNZ zuuN|?vXHZS3|I~lv@Uhp-jN%{Flzz}8wy0i)>fv2vnf$$bZjMRubq$RIbm4h;iJ5U zlPVeI52(}`DqA-0sDZiA zI=^s4Lu2xAX|%j_OiOvRV3%?vYtg3nPi>A0|1D9}H)guo@>Yxz7lQ{;LlSt_sKTL; zYn58t{K|`9w&Kin#>B!y8%jriAZv=Z14c*7PLC_!q)nV936AJx$hEIc>yu%pXoBn; zN~2{uPua}RN&5BMTg#3tJ{pK7D4X88i*qsj#|L!!3P({|eCs&4HmS5-*Y_HiRlY0O zx247Hx@jFjAvlUQCA+-l-?w2Wsv0BMt<~xCeBL{VgR{NM;qmhS`RVZRVs0=kf1d>D zVbGaJ<3sDho~Q4A@G`gWLd%;7gxg2_UY{o8BTSxpN6syBzP1ki2fD_^oAC$#pm)~A z`!vL#tB7s$i3ZBPXLKddPW)1%)#DS+9ism&#y&>ta?+`%i;=8;aLl%LVULBI+cFN5 z%z1Y1#jR8x7v`*zi~DSiK=E=E=9Sy3($tK?v811$TdOSKvO0LzD~Nrv+d^ZjKam4G z5qF+j&g`MFkGD@sXlrt(#8xFX3(3u2)iY7$<&{dITW zl6h&D(JZpXrC?(tHpAhy#BLSty8$0&+i!qq@B6!uFmf7STc-zKZQJXnAYG&2W<5h? z)#7mAjyfo%Zx|3gRkFWVJ_Xoce6R}SCj1f0SXE?V!KpAMaYvS@S*VPH!}WEwOR_9# zlhLOei|HOaF?66nn~L9*Lla2twLvmnLvlswW0e?V+bR=glywTIi)?3=zFE}$@<8R5 z{3J-22PDn4V}2i%F5g{%z!2uU{IYqCmLVZPrgjM3_8#BkW8E-gXjVSIx#r$x45|@; zhMD)I=n4DrdoT0QVEnuMo6ZplcMdlB{r@CCxA@9*|Hf0NZ;PYuD}j-qCAV6*y>kW6Vb&eI;%DlryuI9EGp#I z7SEC~Y{+Y3cK-kJicR8ia@o5V6JmU;VXaZ^r#;G#X1Wwd>ypg3hJppURO!CkZh6gJ zuY;T)Oe_Oe=&86M_HAYp;H;=-8QJ-Uw-q+--BX|{{&B7qfvz>=^RV%Wtm-Jk)S)}8 z-jiWvv!0HD(dZRoDlCX2k_{=ntcoR|>fMedn`N80p|Q@chLAqDan>9@Y9X$q_j4Lv zK*=|PS3i|6)ZUDoF`}4xH2`>!iWMfjH|~X-GG8%3ze!nyw}9Az<*|PDr{qg*{_3o=n}S}yCIv*vw(+gXKosgW2FV(9`J>U5U8#` zDp#eEy1?eZ8$}Mc9F)mwTEO#TPNc5+PikPRiYal}o$3Ap66lz?z)6gtN}VQ??Lgzy zK^*I-Sc~*btJ2TAI08^IU~Tm%+DN9OQjXdB_iutJmHJb|l>n0sFnU~rKeB3GUpaU1 zZl=Dr7F}U2#md@j1Yt9z^#j#oS*cu2g7Y&J3o@;)LBX&+QYt+tK{f*|wB0Do1a`c= z^?>!;AlKYJVPm@yPHJ`e=JN5W?|k0CKq)--Yyn;$^^Y|0!e_&Mtkq}IlT)d|ue3Zr zQ;{2kc~;EP6GBhibvWLalY3pn$Zya}q}vPlSsPR6L330Lobf8@Cn0PbZlR+X!){l5 z%Mu3AM+e&4hA}72$vI~88G(BI0a>HmnR8FE>+}ktXi9mG3~999E8~X^O|}>s5AV+n zhAJ(Y9H1tw0v_&}roc5H+9^sNo=yR)N2p^0{(RmoGi2QAPkUU)K~$SvS#m|{Nkh>@ zj>rYLzuac=bnIr=4=__k@`dhB$YUJw00zi3+3bN;XkDOAQB3?vP8Z;qQq_&B{b4L2@#zorp)U{H4a=a=^mj~B? zJVTxy4*ZJAW6Cf&viG%8*CJUjmX>y5Jl#vfh*ipY(tW6(TJ_W^(0TvmkKJvA?Wyh0 z^|zQ9K(mo<@jT4Nh3MT|<%Rf7^+jP%m7eQKL_}D}g0wlb+Mg@`movkU2k(;|f*2R` zTIUn1cHDp(J7Z64>r&A=kBwK4IeDCwt0%}ogR3iumQbjX zclp(WTuG!2j|ubdj!tHl^*RPapo5j!{NPTDS>a*pTH5~2gvs}$o*w5*zdSJ3IvjpY zp{jNUvL7`q(3nDG`jchXkUlG|v*L2|_blFEDXniXHFW5WNqujSa?!U#Wq-(hy}vS+ zi;ZE1wNp&EL7nf!_A*TTYA%N~|C?f5Wg4`srf8FSo|(Ivf`8Z{bFuQYfKUX2IQ+I7Ddh#f75^McwOS8Ry&&P%#*3fg|vS_|0 z4sP+nl8c7qRDRc&GpR|v1cUh6*yM9_-@EX8ciNZ7zqO@x4XHgBL`JNGVWvz@k7>4D ztJ+OZpsUR@9#_}HhSJo7F+*8QCGpNAeY>3BTY?jml#6P72L!(ciZVi-WONeAX{C8 zZQ#DbS;?^5igehor03=3%J!er=m~!+yFDSc8GhLT;zo~0~QR|Z>CN>PxGl-6; z$+B(NT}wRbV2*CK^(e+r65$cM{kIZ%b22`by(TH%<8Wv-=!}YL2f+wDDZ_t65I#%t zp7=^jhNJxAq5s{o!IH1~bX1rcxPRs3>uj)FR05>TE$E~2` zIOQ*8t%Pah`fWykK|LcJdqhOXnADT@Ew1*TZ#Urjx%FP2@vgC2#!I5wn_fGX?| zj0;TW0$=Mxxw$`NHb7jfCtKCl($5B$SAC60` zE#Cc%Gz#uq^$lGDLg#&;q!~vG%eD~T^^SZ3%Xq20El4H2pMbsh@T59RzjLm!&KEMf(I9G-+y|T8d4#HIFPM+KTf=& zO|5fGdxt21W^`+ue`5F%UW2@|5p@O4!jA`O$l(Wt1MUQMg!ahisA<|e^9UYuLPBT| zUN4}>ir~WyyDoccv4LDjlS(0F?xDb{z*9+{%CqJ0Kg@$5EmP3lA8dI}Fnvi2s4@W_ zcgifJ#_J}E7T})F_^y!$w=VY~Cl~yw=iVQxI3PNR&};R)CRXC+DzMPFDCsYx~FIPm%)vCfGckMg_?M5U=u#6WYGDnZ|vA2uRgChDI)V|tSQs|)kDMIIlG;p9_Bu=t* zJeMvx3>=kgdCFypFq`epyim45NM`q_g*1LHLLjd3J;hV+p}zEULd#qP^joMQ;Q>s| zMPpZV)@N2c4z2iDq7qqgIUJ5P{fV_5xJ+mc6Dm{e;Li$!O|TW3kTG&*Zi3d|29{Sa zvH=?_DiDnGpF*IxRI|swefgP+SCe6?$fKuWTU;9)==I27*nxqmeIvlW_nXHa{a>cP zK+c8jHS{TrSXx@h3wn3;K~S~fU@Ed|^DinS1=A?&Bh1b)sGT6F+9wOhXt2o{Hjc>2 zxBsIB_!B@n3c-qNX&?-R`stDZ9zp+Or@wCCz{Y7<^)LBzAf~q`?~tz%2g71xwHXDa5)W7%LD6DV4R)z;wf9(~)aN#BL?A%6eY=ZrFgAP8yM?HIn5zp1y%~rIeKx%Ip*lsM|7J%VNe8x3vT367% z1tGdd%VKkAXGTW;0-emS+ZI0YZ6!ek4;D;-V8yhiB@9YRO zvq&S*{5#;=Iu*co^KcwPdpAH!%wcoCAg^Lwi7?O(Ml(cX7qDQPHd*ryAO+#(Qj6LW#Ly2$T3Y}|H64Z6CHad-5L+_oiG4u05E)8T~0hl#!n zMDMb&iozcU1_AIu5i)i+@~dIn?;i3slxvoO4NPsvm#6hjtM`17{Pz4=5a9>MPY-_? zglYLJP=yYBy2r)GjL8~>T$O)%oVRIK>~@lB|H>{_H~g3PXgls^`b7NSS4ZjvHdE6r zB5c3uyZ-{Ivb$3A1uWI_XCWYvDWWdLc$WF;AXYtYVBXsYp)?=vyQh zz+p@s$Mn}JfBEl5%l%NE zdmqZr`N!lFwvn{RiKtNb(Mgn02i1SQv{hv*M%zZhjEY754XWShq%RvS;2s(`9Jgyh z-AwfE?(z5aJUqdveoWfZ=~Mi$?)5b`^R%NTazgSYJ!57*Bke^C=YDfO*jF8*;dk?l;wP8tl9`#~FmMZbuhORobUe$7*YKK}~(- z{#{Cdgv~XIa{H{Vo}NANF)9r0&&B0rS)IN;?)a6z1@>C?aPryC6K|8Q0r@of^yYBI zf1jOvzBbJgW+D@abnN}pH64@ppWlCV;)v*8O)q)~9U=+l*PbA5Xx@OF^)RcKJgLl3 z{!ZfaE}sqSy>u}1_rM;_=~_ZXZgrIStM4StGw^kvE*Kfc@JioIfO$V!&dpXi4jlD( z{7Kl54Ju=Qiw%z^8yp+;fT!ZyL!=3$QAGBWt^g#)YUrl>w2|BPKBbbDQs9Xh;x#Di4Szmh_~x~dzt=NZ`k*7xXnyYC`wo?GpnSF7&>!;jTbx7UC6<;n zSt(#IIRQpn7RRua85XF3>V!cEJFdKy@^V)?FMD-aSol_! z7^_Xk;JE}vwSCt!u{!_%a&Ph23}|8k zp-v&l3B{Si{qOa|CgeQ72RPqgdtz;E?)(IpN{7`02Q>+@=bIP z7o|;(VqpzJf{-|p1x7wLL;1VA!|W^7^>sN+i%ur;(s4*RLbim%^6j!GXf;- zjkS>B!Bz{0D@G$zT;a(KMPrMDI*uuFN?G6m!rdV~;-u|)N2zu$X_&?pE-vcjDO>+W zHgH(goOtmoT_d6}K}0Hc^lS{paHAA&E0A8HZh>$-O`-IQh6BDonc}ze(#d!Oafh>? zswS1>e%@^Bl$2X}rzBdGe+oO?QMp48aWagEgM5Y)@lEuDVSySNOzB5iQhVTPI*mIV z9uV0xeI{}okqPU~>Al)zSsJVRDgq{RFn`*CsWZ#~Le?0Jwu6P~MUCDw{p;HKFI}vJ zHf;RqRc0*^$Dj^=H?YzmD8d00nz#3bByrt!NKWdgkyhYiwQ*8Ap@7 zI`X^EEw8>+B1<$JGZpgDJ+lJGPJr{eFrUFH2FB3OJM=gKP$Mz?sk_d@OYCRg1z%#j z_xr<$0W#mWoA1MUy>ExYXuUgICCY{Z-nWZm@s$3aA2IYJB0b)JZ!Rp?Hg; z7E|LZgk9&PlLGf*iGcPU=_djd4X)(V=ZUhsBDN~MyW(0~{is`}&aH>K-wpA7RyFDH zgrR&y6Z!T^qx_(6wJ5v23cO(DM7@fA?vE&9W26_+y@i9}JA()TE=~Ie4-2kGTk*vY zcm!W@JSp6jpXa71Jb|G&RzhZ_9q#LU(I^uKwlz#=QA7WNFnqo1&On>&ukB~hR<(DC zKc)D?a)M zgX?@dKi#nTl13LbxzX0OW#_)CmjG#j-Ry;%Ob{$L3e>YDbwf)ir3V~#32iLc+Q)ZG z_mcWJvnqJLIeo#Yg_J(sALDxUl}qae{L@Mey8E9_?sz-Y6D74>jk6U(Nesg)y{_tR z5VjH82h%b)Kep-OVA<@r+20-$lA!|KM>?ghsF*ENy#uCMIG{CnO@Q{jp4aE+-W*N+ z35U^SL`kk=E*hnYgUrF_tjPDEzDaSZ#*0*x!9O48rkQ+W%trao5eXgmO zAO$S@GUqrU6+>RUn$FiO@9W8OcNlb=NR0;yz&nL2nt^bq=j}z0X7Ln9fYinG{Es+j zB5iNM)G-Ckq*VULR2v^`$~ckkq3`{{r&Rz-Z??4)T?GzyCDgo1?R!5 z_Pf1rrB4UbjIU=+eEE2yo>;76ScyH{5Xeq-Hs5ZBp<(CUx#GP(QN*3;kc1 z(R;fyy`m{!dNaoW>0l7|jw`XeF@=!Vw5SK-aBtW-VM_%KXe*o11ZMvt*jst0hcjFP z-6#(?#nZcN>+C?h>LHY&p)Bp@i+jugQ*afr(4(dgA+)j^-Rqz+Tf|=(j6=jQ+n1pFRaHh*)4=Asc=-Tq( z_4L585V~RGMV6^j^<$C zTTcGs-S~ZWua<~2;)kO?hP3{G-{3Nq(s9^ndj>AHmjD^*W^_$ysg=-Rxqa1VB=Cdb zI)*Z0a80_YQAem1Y3v1uWE!|%P^IJ-Cw$``?zy+Q1ZdGA>UZxQSPoSp6zSV=2-d<+eEMcnzb(%Sk*F`DxegMr3D{iuYhR_ zGW{z9Z~G4T?k7JA$eqjBG-QB+6(x%x;rpfTeML4vjo#y(4`P#+iH^nlvJX$xT zRa;wl+U9t^*-=T<{=A-NY(_h35H)2WpQKxwypptkVMA3Yn?^ro+TCK4LOSgLfOp_w ze#|?O`DA=EJKZ!t3@3BdJ}Oa5idO@a!7)LYZ!6(V)924kqDZ{ub9bUn_X|bTcyzGo zc(HhHR5x_n410YDZ%^~}biBMRK5x7G`F#G&Fi!}}B%!2lKmz6JWO0_F$M@qB{pWsc zQ!Cc`P-q&)<(06<{poTr;^QFcuY8Rt<=q3vuOw0r5n4KNblMVDs|?Kz5$D)Nyd$bE z?JDFn(f=kPpYZ+x@cSnzg3x~u0YC`_7peS%MCn2-ZM=W*zbXIVzm)&Le<}Z8%>O$3 zkI(-~;Qs+bEiQ|vo%t<{65o$|3`rYO^LHZ`wL-tsS@G+Fk6Vzcdenpp z6aFkY639pfK~$)|YSl``abda5uXn9qq=4=h3q8ClJd+y!znu!pFS2R0=$e}@e`2$I zespt_96Uo>t0#a{Rnfl>QLLbuOuv?*RCcFqGJ?d;E%Y;GG$SGy5|q@+Hm&7epFba} zN{dHIj~8>MXsg5JXEw|AC^J`+aayBbuhB69mWV_N89_}xff70Q?67NpH zRPNoe0WKmll+H*qe246CEjYiDhpP?CrX-L2jfhNB(T)U0I{{D2>uf& z5dfNKFwOsf#Q!$_4}|{L=HdU&Ez@H~W@y+akm#`@QcpjCK- z=C60I?V1N>e31ik@of#RHr_;>^92HeNBBMl%@#?U%Luy?@duL*T{sF-x9Jt5WRV4R zEV+qu7~cabdc}dnYI+W>ZG%R0Hi}^~$|%e)hfhtjv)&JRxw!wXR*URLoI0yhXMfFg zDeEZftgUVFBlINM{=GbVkLfCp$5-6wh#iiRiFwe0EvIi$S~0p2 zz5_7)Rj0W5a~w)4@GhgK#XI4!#~||hC$&vYFYHg;`f%4+M#8h$`=7eHwViZ^A1bix z-$H7bs6nBltpjV1ABxlp+U%Ih?ixXoD)TQi{70*r8)$+0et}s_@HJkrU~BdtaZezw z5H1GapJCvQyY1J@YI?0r4%7Ftgix#R!5~ugWn*<;s^HpMN|K6}Wx7TX`gP8C7s`Gf z{B`)TJ+qcDUOXH=!cl&l**1fqR)d{gx>_qcUvVNu%xdsDRqKGhlL54Xp=shx{vV%tTDPu)~HR;;$wp>tx)ub=Rk z8kBd@2lWk+gei%mBjbxrn}~ozjHRTjwtYNa(s3isC}R}sx5?<}$$w&vb17B-u9C_* z21ji&eTxOOV`f3Ta@U;ZJ$AGZ?4{KZK1goQ&P>C!v#vY#`@`m9_U00QF^g>i$jPWl z>}YcWMOKJME;?Z?Po7E>Qn}svHmqw>10&5Tp_Vj_E6z_=G`O!ok$BqGeVH@|)jZnz z$cPqX*h?U!glkEbbBe0(a?AU%g~B*m+HM#;jF-ws%;?Y}Ff|aWF#quQnkr*XL(Na} z<(_pRnxx&3SAmW~h1eRIRmk7d<(K58cI#8dd`$JgbdJ03+FsYQ)8#+vU2@Dz%?7k1 zd@dxsS~hP!2d8g^bJ?puly~~P4$PQ+mDKLZbPh2F4!&M1A3qdYPtQvc5J89cFKG!L zmGnmrd%=i43gA-f-Xvr!3uW4uTzf&hRw{&>YaaRsPR*ZIy9BsyO!DG3&wYh&j~><| z6FCHZR|k6YjCnZfr|Td-rnI<=*J2D1T3oK`86#!GW0Z(SkP`o@oU%#*qVgIV{-P}< z^%`vWY<3?y(6qo~e9^Ckv)Rn}X2kbj9UV>^s%WvQ zmaK5dQ@oZTWlj6vE{|>86#PW};uj9@E*VI(XcwSid-2J6M2N%vUGJu7sJY4X{_)28 z@0|HX?t`wo^|u~ZhvDXN6l|pWGm8(Gdbje?IewYh1K)kV^YaWZ!MhEPqBu#7o)xRj z%F21Z8F_H?K-KE0yAdT#=Fg=eg&*JeA8jn-i9n&cBNNp6nT<^3pyzwdFGU;Oyx@7F z;Koydt-IUFjg*`u`}S7k>EOE2v2GMm=J7o`@3#BLFGV4=%RpR#sPAW!JbGPaCgDVe zIA)GFdo2SdgP05XVz(OF`}55pTHYQO2R=Ct<6$LKpW0>79HRAb2U-h_R>!=U^_pCq zDBJ!G{%=z~UZiliF!&5!?Ap~!8%)eFv8i5faIg1Gyl-$+n^s6yi%3+}7C$wzMfdpD zn_E4A1NhgvKkGJT@>S0Gi@LLD0T3Rz{Y$=8pWx<)y+1qAA=Zn840KS&241cvt2 z^+Fm5n^~Jrt5|~u>UJ!uyV(vWy)0lE*uCBN3AQu~>vMRM_hoGA zzfgmWbO*9i`h7bWDWqEyC9E%bj^9iToSnt&vqPcPN_Up9xdmmJ&{+$(f@)eFmuWoL zEGy1~N&Whb&K@!@?uypU%;?E^%8;XtI&2o)<@x!usrQO0zQV6LM3#Ips?d9RNccGo zwGM(rhjd|@mVm~^8}c~7cCC4{Y_MX8y2aoiLh7P1p^)m3t}5o>T!eih$d%FNvrq9Q zLOa~OneO?n&jEqQlKP+9*|y5P7&BR1?Q6kuScc%jn-P6$)BUoY;+Ff1alXcgz*=ut z*BZoM$f*vdyTO2a1~|@QKImMV)TM-~uL;Dzkw6-{n6|$e(OQWmAOW%tL6J@K|7rk} zwvXS)_rK2B-+Loj)iopZ*Pv`8ETWM Fm%hxpill}@>>$b)Yk1Svx{y$Ty4FrRm# zsH$n)3-)Wn%n3GTWW^VMem=Pb(Dl2)BUQTV`s^aj+=hC274ec>y>U@_r^?`jd2k|S zTm5jquArH$v_fMtst$Y=KjgmNyWpAI1IDoN|0I;jS6C21x zQ2}lpLEyby*0xkSO~>T4l_0Fi*hB&vf}Mm(;(%*;T&)7z)2jZn5o{m|0S6U^HuUtY z0JF*p=ZJ9~TE@QPkq0Lp9Z`tVvIv95Yic5F`EFc^m^6t&J>CiQ14_bZ&3e#30iYqj z;53F^Dn*OyD=u>fx_{+DBGRhg_2Ma2g@Ij^vypYZod)UIsio9G(<)szfSPkhv9yLK zp)EI9S9UVz<@Qq?+DTPpIGKwCIrb~ub>v@W8fglaW-R0yYGV!CsDZHlHK{EcQ+M}w z_xE>qHS%%3gRLIb280B2JZl?n&^WExH^{oCXZHx!WT=yQ)!_RCHeCh?=yfs$Y~4@8 zYDWbxBX&C%BtoBAK$0=A5!+h4UHnS1vanPy* z%gfxcBL(Ard4&9XIvXmaKXuU%(4740K~-S1%-eLz2%14R2|$WGya&jf zQ>qpK7lW?TI8|-;)K06b#oIyr=vF{hrlN)bxSVKP@lP1%&DYcvcS4<;w!nu4uqu4| z-H09R0n}Eh-hT(m)pn}rwf0_Mm@OeGC#&db@cckqvriaA{DnVUNgGS3HApy$?W3@EI))&YI0!j4QjgjS1#R zL$Mx!-f&j`+ncya(5H_#!Ngxff{)dIann6)HlThUQk9f~rNK4Ur}kn&*3P(k5ofg| zrmDbohw6-Yys4m$21E0SrwKo^IR4c@O8 zYYaY6WS>L+EE1PJf5ef6`*4rV2Bn9?iD-bVKwyWt3=n|ov>G4|R@VU~;ns?g#vN8; zuwxM8vIc918EOoE4lraJe}+VrI#Od`60K~Fz8lVC?(yquYAQQiG04uEYHDfFt+tY$ zd!lp4HVa{lu?mP3%tEZT4^nkCH73{UH!zW=Fk`ouRa~{{LIs3{v>s~6hW&peL!-pL8)*I&O&RK7gZ%W|J<`5}rvze#G*1Vei#~#45>hN>$6#JL3%|du137i0W7Svyj8uf5*rS-SpthT^If(?!k+5OD7{c3hrvNqD&+1+P9gvND< zEIJ1JxjYV`F(@L7$mUp$L&Xtv3=mn={*aX*vWV<)LROvUEk3dLpJ%*h%6#|gplRP4|(4Kvhoy1w<59` zO>oIYA{{DTg>+4)YVZn@_*FuQRH2X-pfZ-U{G6g7#SnmJ zegSv}4XN^9gnlE}Uba>1%Fd(WWYkr+ZEYqc7NjbXOUo7KP?-s?Z+THFU+SbtEF{3r zNmFJQW$>k_uHCtC5f!HkcdjW@0n6-yQdNq|R4TKOvi-=Z5VEJi%%&*jVurRD2L?%^ zfLxji4^*}_G0C5yc?414tIx^PO_Aq{Q3;d*k}@1tWh`0hNx$ERiG`$X!#NClqmH)J zF-8{5EDXQ|{7_IwTTvrRg9at%FjIp%+Bz9aY~C4pd4U=~6x30649>(MoA;wC)pQyZ z>d-yeALEJ5JN=)7IP(K_go!JXa1`|F2j!oMUG~$R&pk9k4}}87vX1vI-*AQ zR9*u8$DmY#9}enB68l|VLj9qk0O#JLjv}!?c}_+lJE1<>db$t%p4{a zP)ESTa^&QhybN^u{}mJyEJZ{ex`id`A5Z6HrZfL(fd$HtTGSx|3&dt-Vj~`{k{yIX z9hQkb@m*e4I{p1goNb3Xya|gZzB`?ltxlghDPaqlXLxtJ7yrefPu36B098 pTGW@aux81LlRtcS>P!Z>{(nvxx$0%kR__1+002ovPDHLkV1oE#^ql|z literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/vacationwidget_preview.png b/android/app/src/main/res/drawable-nodpi/vacationwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..55cbcf1fc03b7572e3f44c8e8efdfcdd0cb1b0a5 GIT binary patch literal 13264 zcmZ9z1yEeUwuXzl1@~YBgS!NGcPF?z!QB}K3pNZ82rfYq+zGD1U4y&3>&vMcukNm0)q7WWt*=+dsH@6hqLHG(z`$TC$V+R&z`!d0$57z^d6e(>_+en+oYj@J zWt8pR?jN3>UtaGYpD(ZOpI%=7mB;(%_xJa`ql$}H`>-&etm&fOqr{~vy{@vrN zqw|~lhv)5s^Sj5Vjs1(A!^_S6^S8J6?fuKi+2y{$(UbF=>X!b0o!9U8g~IRtRo?!` z?w?){POsN?&z@f1&aduY-`+3@fNS@AIK<4aZ|`WhfS1=-OOM#+=NB{AXj||2t;eH( z?|yuILc*fKCuh68dk_WP?>?QJoL;c=$*ynh9UY&YonO&130+>@5P#%y^iR6Jx%(7$ zw|{uz7xg`(ux@328^|th>;m4}IXHj0b_q&>=2m@njM&`2SX$kppyPu@rFeOL-`P9r zADPWAZB$LT=MTM0$ox4z^S7sesI0cL;Ae|BD8ty|i(5$Q{PHF!8uFv6{a1Z=ZNu-_ zw349Moc6x4p^5qS&c4))d>cofuEEJjNYT#GmAiL1Pw-t>LjK9+oo`?iN5CDAkW$ms zOY`rp#FVVrxuuYZ_@kGL`nJKD#r2r@bdRt!++d+!RSm$eDuNOk<5LTAIkux?QwvKg zCFM2JN(Q_s`r@)WQ}e6jASoxepuwRrT|@hV;$Jp74_EhZwJrS>jXmi8yj4xT8hX~s znr1$wk9wtEQFTw5IX|W^R&HM(ic6}NZnj#wN18f@*6#Ne)QnHAUq_DTtDE}_QXg{i z%j(y9{;a&ZH^u4LhNQPYPi?)~REG^5PL~Wm|6KaLdGhvsqB5W@rG0y(XK$i;W1wUH zW$)rGZXh4Qn{8?T4b+oaG}j0nDXkuRVez|l%!w6K)9qWH$||hNDlP3nO921-|2}$Z zs%pV_<#OSkPryDSEE%$N#oBd8{Snt!ZaXeiEPi|_x((GTPW;yu)m7fW0|o{i_dfg4MLWyEehXBW!A2A(pZf)R? zTozsmTts|6YV}@qZ(5lMxeXdu#>G`6jtwXr^*dFBe41JFTlO30W`KniT@I5%hJ(X8 zUA?|H{+)Ot)}}_$ww>v}zWFNWeg5>E(~g`az&C?Mjv|0legEC~H+HW0q;C%t58un8 zgqJ!o>in&5bM<$}61q5+Q`TOXRQ3A4-2FLxG+K+1v*^<7EPk!!Uj>|!py$LhSS~UG zm4lA^I@)JYH1y}(-jCMSRuBlZEz}6un1@1Y&;Qs4@Y8>r^gAaqrGXZxm#Tp$G4PHg z#aYzO`ec7vQeaZ=&@8|ABbjX_6gxS zKX;lra^)}N&@zDB{WwtQ0zWWBC@g&N>&aeRp^;WUq7U_Z-%1wwr62C{F}2XF1$k0L9Pm7|<=?N|1+Kb|TUk=8qS`dM3*^FB5i(jGYJcU~G!|ncYvpde;i92dP zj%sA|G8o$K?M1)1;-oOY_uSk^ZTH2-IJt7Q((W!WP{75JulCT__)jIgW1TRL>j5wy zMK=o}nFo)Y!&Jp2-Szz-zX5|jX(b;2Y6C}=&VYx8Zu6~k-pp?UCJ96LPnZ?nexm`8 zN6+iKj~y>X)I<+}?X@7r>#k}4Zp^U(c;Zw$nzLwET)IBl8 z`%||HDr#2tihvTk2WktIS9On3x+_Gg&TTzqqihQqE`jRAU9y>aoL)cYVGws%3f2^b zfa6bDp#aBsdj>Ih5L<5DM@Ch5uzSiW)zk8>pEyvWT`D2URhw@PgdC|_QxvHg42c6a zv88d0=Lcc!w>Nc7^X`3;LkbbtX-%U;=&08L~f?^l%qx*bStBwb4b>o zlZm&)c?k0Rwtmg>>Gfz3vg;a>_pfR<*me<;cm?=eEC(@+1^I# z4YrQ!IUTJ*pU6R&Nm)P6U%_k$iTZGG5AO8{ryELCfWQtMAD57kCe+xOn-3O@^YFlo zTbgTNn(O7}c>z+Qu+A|TQwlg3=$xEllk-Zcp0G)7re?KQLcE_wNkZ#I|5#$K%SL94 zV(L<{9Q_4XbA&#Up|h^zUHH&_S=RTC@5)APSNRAs4Z+uu5x&_*Oybn7*jz09$)5Y- z{oePwp3~O$bf7P_Si@b?DJ8rnNc#5W#7mNo#}1)=ezkJqH$P|Y0;dX>*(>GlL?Y>8 zm@uB#?Y=1pBiM<^B*cgMNO`fQ+P1qxiu}Z<>v)!8UEDuj{Hi=nzF59Un@I-TJ?Jj| z&@TU@O*n}DE5#7{x^bTx$40GUYcp+g!t3O+B{it87;XzW9)3#o8E6Do`y> z9UJH;?TDPWBbWBzCG59FqO=h7Z~xO#ut+1S;sa&3xUQ!gQDUg)+F4|eP-$48oAy^x zN>n7nX*dycn$Arq!A`NDEI5F-dQ@51h$7!49%#`)5REwqQ=Ggd#)3ehHl^qoHAQEGRu|I zrZTzm2hIV*6d}^dg_awBD3qI3X;(w`-f3J%f)jdpXjS~=asGSgvjgfq#Toz)nq={Q zwM+@SSa#BSf~!^+0N>*k9*~ z8w~t~WPuue24XUdpl?aV>L!?XF_5Tm>Q>>?k(e*|fNDC>vVU~=!wX$1(1JAn>JhBi zDf75(@BJopRQv_fb+y}Mj>+DH2T(P~DHX#Ft!z9!25iM)D?1OrX;cWu-mrPEz{ zdjtba)T;c%2t(Jh@6rm`TKeGq?qfy)_J^aULCW?uU1$>7Fy3WK?c^j>B4%*2z(!1^ zVN(N52#UEdB}nBbxpw=dSM|NI0;o_8WwPc5NK5PHzy!8*hfoLrt%;SOjK}%X3-9bF zi;0fU+3^9cXovJvZ}w5-95JJX5mZpLcbUg%UPWp$D5hE?CSa0Q9UG(VBQ2WnCuO-J z-CO+^ADG}vNq8khQ#GJp8}#QoIMO*?duxbC$x(*|1l2jE%lS_A5}27>QDG#+T#v73 z(#QG4x1{)OE+Z#1^uVZC6VNcaI%)YO!g?k|1Ig5u45QtIpVyRzHR$ zn#-<9&%i7bn0#b{x|tbzvtWgEY-PPTaST3CE1%BE*ecXB}=+Xj)g) z09pNW*&{fBE4-LtX}<0pgn{*$B{zy?cQ*Proot8@9K^XI`pU7a%{O}&pG5sC+v*uh z5j`+NQ5Q(!D6hlt6Kza@+~P`#>PN&7*q$Zzjpf_BwLMGFzdaDyTt~lmuA(HEu_97B ztvnc!0{ifn1HIVnTAEC0gP>UM7b~Ko?V+Ux3N59(XuF}Gbg$obLkt9SO645)m#BL04wUtKy>h?J%PHgyGIhxJ5$zm? z6&2(J_a?G-m#>k+)_gGiB(T*aI37%l#q{QHEfF>dvc$*m0vSsC!7s*CPjJ$Fs-(l| z-bnx!;O5(xPKG)UaZoY5w2nqrw7fX;ruE?8kln2{&qg-MK+~Su8gmF;7cA$lecyg> z%TmI`kB!8IB3wL{CtPy}zm87Cq?EIkygJ;Alkz*(n;S$Nb zxNmQ@`0E9JN>KHC4$f+O5Mq!=B5dC4gjH3+(=~?sz3!W;atMPMf*;QW35Tk|=*N(i zE@E;TYW=%(t0J5O##x8QL5=G;uNMP-c*C^89#zwzyQ_T$vCcs6TORx}aJLqLUWmcR zDKMARLmZ9pdJqd8wam?GBsZ1GiCOE&d||b;EN@n@cdm@i99Xl{q@3kNUh&D8$Fwuh z^9QW-*J4*Lwd}50pWee`u^cmaJFV-DkCqW7J;4Fqo1VQ*H`@YEq`|)Xf2-vdHc5cG zYO-g<&y!_bwWgy@AqD477Died7fB0XYY60DY>1NGr_@E+sAk) zHNgqq4EcR=$=9MQS`$KOy94Ro>nM-e;464OEI<@QYAAn)^nXR&9ry`Ku80L#5cnU7 zf;j%?>Axa1#QVP^{#Rn=%Zbi;uhk;rW4|PBG#*EW#O+Ep17k(GLWzi&k+&`q096HwfI+U!tzZ9yPb;aEwc8fihGy7&umHh+xVb; zn=fTX*Wry6fESy-Jb50Qe4>ET@WHO17eD*n8haXpopC&Lw4YCNddLi9_b9rZ>U)ag zwF&7_p>u%L%)C1YL{X&Oz;IeuU&o?%BCDVd0FWv|6m8~|_G9*?etjt_PzP|`(CV*e zR&TC;_EN@1%&I9V=S>Yp@og=l(+F-XuP&cHxEfE@i4-ZMy(tgi6R6S1(a~AdJv90w zr#C&rZE0Ma+N!~d;9U}m(RJA6jpl#})Sd)}?UgP6t~NBDeA(_xMxLC$HX-d#nh zJo@L5;T+u%qqOM{tsY070@P&U8|>vnFBKG=v1Q`_<^sq)4I%;BpW zngLifFOa~{=4?XjZKd88X30IaUQsp5>WS&A}t+Ej{g-1*_W%rrG+ z_8$xNs?^KP>;uIW?gqE^mp2PAny9ZLBG(MH6?YerB~ZKRSjNO`kbXYu18}+>4w^QZ zN@~GRn{eh5o|FE;R=0KvbrN0Y9EDTzfKF0A)h^kJhT1V0w6)J=M0e6#0CtVeF!pbq z8k&gTEe$_#A{No2J%8Coq(fs=D?7=rMldid)6m}`Z5W6}xOaipcc*4GbxTNt6Y}rg zP?OzhJ{*qsG8tLwyy{rO1y5Uo3{Yv%lx(hOs9lRDfqgm(bA_)DPiF>6EmCyRqrX?z z%|j7cV0Lj>tT^>`+%3OOMD=UeLaGVlv)5+}OpdY1BT;mH zsoHXmh+H1;MN7M~(MPvHAn1`LzgdByMTXmP7m~(iO>Mmve_+8Ay!sh`L5BZQ^fymm zs=C>(VSM)k$MDmK@AH{DxqbU;hsl&rn5wkYg!x6mv(#G?hUO9(8EK0#EMGNexl}%C z(Fs&Zh%j}SBMBGsVSAH?vr|^(!og`^uZ|aZ>ti}&>Gq{JO=&80w{M<9MQsUWhZ37B zR2tMj+F&Hq(jbQOf$Afu%w5+T?~=7Ic!B60T^bMOSTF1cTT73Gc{^*wLu#amP@Ggr zjvdJiM5OX~(7hG%uQE{tpbXgiz`2sp-0fOCgNKt39A9Y{jPg+ie<{L;dpxWv34WNc z4fprk$&CR;y!$8$vIb8?Y18pO6z&0)s(BnYLf#7=pZvEXVwergXFwlqLDAm_cexEs zLG>f5K*xGG?Fhc)83o@>3&?}|Ax2T>pk7XTA#U{dFEg<8_RiU5<>g4wCW%fNB!AW~ z9?whEB6S~-g6Hz?xY?jCWo_AXJtI@wk#n0msInyJ%&Rn?^0H?93yMOj(5)JSQGAeq za}|?{dx+r<9?Wgo2M1sHxjS)s@<1M=8--2qS(5oUgV=bA63DWQ&0$0dWW^eduxE9| z*{QOhFw?e$Mcu6_^(jU2KqER%Y-|?ERuKgD_G2zcm|=f+^ON_w^$ARZKNNyo0rN+V zERqxP%o>d{T#hVw)#L(cSmK}Tj0vnYrKJ(xcwr7GK$H=vS|qOUoFY{kO(b2Y{aAY< z^IHIsEa0IjGAC5IcidSYTm49+*ev!48GX4ER15$03(Z-Ud?DzI3{_qtMy2bdJE5v} z!xG}NejX^&t;=d%H)1Tefa0-D-2PitWv5&rf=c{c>maS2m1@6}c15InG?oVEFv-`e z9A_QqE$w`tWHp6z^(1dAET@juuevZ5-ZX7&dd3u)WRDpTgJJ}pH9`DzxCRppAi9%C z@ff0=M(0QFM_H)7`Vb04Ey}?IEa=Hzna9X9N850W6_O%4{F|?^@NHMsKPZXVsr7-ONd(um2N}`sC9!h|~!f?ig?Hvm7$^nM+2${xy(1^cKofOI|#PcuvIcSACX5bpvQ1 zu2c3MJ%DL{uB4Yz+J8YV{8h~-w-r4cgA4aWsh&jrxngXwhNHX#WA`hJ93lQ8boj%; zj53={{YO#&o$p3la`_I{iA+UAxRt-(8xyIu&@Z`FQvq6tgd#5ybwFim_cAZbX#jIqDmCKrdLEXlGaiCI6GXltD&*qlsu3%q31kvmfhXnx*h%*lDoNc_7 zj%EJ82?hD^Lk>o9P(yS)UTJAlZ)-eg2ONs+;0ERz6Xai>?OLyKJoS1xOJK01KYN;# zSy`1bq_Kx0KFaEt0D0#W!}2ejp6$@_3bv!|x9&l(<-I#{r&Ku*G(xhH1Yr=;c z2KT+_;}+p}cSXoVNNn}#vzU@hDKb|&7NS{bh;&Rux7hEHpDtdR?H;Ao5E37#bWi<2 zc=1C5^gSv84ei>&Rbxn|)L((-VIqsv-cA^!i1J1NGrLc@{E4SoU|B{S>8}l;*d55a zhL|0gPg^y|#PU2}y|aLba5$RUB>DJsn5uNBH1<%NXz>SdO#lZJ<{WB`6X*(!0b5yf zW#1qI9QmRlDJis>w2+i*DtyrZ$~JrVT89FJwF9AV@wdK(u z9@w}RYX^0e7?fO`1H4N`^r} zWRyr{B?0BvS3f8X535I>h;wTSbtd{!U)quf=ltfJX-97v$G19e3(J5g9S3_)ST7U> zb+@;X0<(EPHsOwxZeCq+1!V+g!HF2@*tjzq>%wEfX=YkLzqLo5NL4vMdJh89>`aAn zE;5mj7K*j*7+=!t z6=tFe8@A*JM@6(~f=8Qs!yKWE^GEg?S(oLX*8>c0*-`tffTMq(cMlGA0@adwb6Y;A zj`6SdRrSyjM)<`z7rMxTt)Vye$aAGvBH4F5pGp+D=A!9iG4gl* zFC3|Krd14Y^E#g0`&(g17lq5eR-u)>S=-e$snXabp25;~JySeH3J8TFT2LC=`=S|M$K8rK0bySs50&%4>*xEEKFEqaP!G+z^knZkY@c5bwc{|p6C0xw9m5*W_>2Qu z&L?62^LMp>B1Mi=|e-C5RV(Mj=gwJ3b$rfm-Q}FdOij927}@U3 z6}m?%Y-0|1e{R^^+z$b*`3VJ9e%V<5+r__-)A8{3*r2!gqoe)q?#Ida$BC$@s@FHO z{$y{`;mK6h{pDRpb}+jsdVlNN*=N&r7><|g@rxFZp#5><6p`17F12%{x9gkn)e9f^ z^xQSnSFb$mqUQ6*h+V;8hp7Cv-;%)2j|a~U*QdLeK2&KwIro@tLAN#!R}X6EZ{(fA zFGnR8CFz5m*zNQDZ<6%S*DEbzg73r!WWm=*hb`DXoidd%lsEw`#-}v z)~$(||75eg%ysRG3Dx{fj`X&GZo+(6dARP_B+8$F_3(Yjc1({-kJ(j~YBB1T=!{|) z>x8%K8U%U2p}8X)pS?WYJ$Dg>a@}CX*IuvsuK(>q-cDa1Uc)w}41V8wjVKA)US~)D zIE+wVQrR3SFvRxckJ zb=2TYmkk5KkaHpAT0+vNjcyerK;cGM-a?RC)bkn`{LT~nxE!!z($-v8Cv}5YyDMS6 z-m?G26&^DL?`^PacfHp-o4qo=GhbsEjs$U#wjeAlOfO8plQd+3WR2wEZM;^>>&lx@ zv0%oKu^TV36D7*cY+nsNXhE_YIlj~H9nb8QmfQrp(|=!@Sx#wlpJ7(%>GacC>0oG73#m9H40rgjpz!$ZpRlS7)WF&A%JL*kvE1#>2 z06xP~HG{v9=o{V}bHK&%1&5^eqU?sj*wf? zi@j%LpKm#%>%VK&A6AyE8I4JTE%sI9S@4wZJ*Di-1dJu=Qd%_aZj|n=B7fTgX8<>6 zB_cs1HUpPwwgM=+SCu})3pz%-Pjs_Fr(_4K{VI^fWB_85)n8-Hiy@VhYBtXnz+W3^ zE|o&#u3l4P-R!Q*2BSLz5u!H|*v0gc;0wco-qjzi&A(Eb^N$K?2ri|enSu?eSBL5Y zxA5}eKV^`I`sNM{?XycnH+$YNMV-P!u0mIwp8G=D?JR+Q(T*#$>i-`S^M73Czl-Jn zahd;;oc{uy`2XM?b@%@hV9UV&EUW(~pP)wD|7AY^KN9r6Bc2>2!=(=KcBdhH2-HSR ziQmiREQ5H-4q0zbnCaf`na`a{w2_kO5>pgE0vYR;9)s{~Xd5(n4p{wZ+a`;JG_&Ps z=4|Zz<0;=cwNCmlR1pJ84HVORN~gXVJK7JK%YB;Y)WH4onbdONFRGO?JF9q~Ly%K^ zF(Ih_QPbu(j)8?@e(J8=wipTGN&Vkxo#pQzPvyC550n9WJbU+P-iHSw6`!o{4!*ez zS7|_iT9B&2P?VyWv?9NJ1PLP+J*ixU6p;_qhMr$&+Ox&W8roZ0T7C#HzGtf; zB^S}2w#HG8%bML#8c$#iaY8dt?Eh5epww8e92cF4S}eCHRRE_|Kjqb#1I)m-*|wZ? zh%%XzpEbk%X7EF#Gt^wwmkoZu7)y^1i#Je`Mc$FMRMseNckv_%KSWwH1H2<;?;fFFI6h=Iw@5sX#4n#vC(N(cI*)!!kN_J0QTe@HIkc&?`YLCd8FRO z@JBEUXLn*pxQn)$n?DC)d;Wyo9Bs%?)kFm<2=K&A(^=cJ70Vi-!4U9TThvG!EwkQ8 zw@}$D_m{I5Ijsr`7-)1zvvBT)ni}9N5bnkS+}e#D((fB>91gPSPvZqYxfZV$5O}

7REYJh>knf^~MfJ-gWzbpfJ zQfH-oL>%ia;)-xi6e^#?t%1xl_v-I9^=7#HHnuSNRY&`0earIvw$*3-DTON2A<7r_ql5-sQ1ozrvFz93Wyj1L!45VlVWmy@-7Vkw zZ?xkL!B(2~{b7m@_3dWdz*}uW5F25fjFvfjo!{E0KgK@Zg^c4SAi#olVT5X{go#&R2v#W%5k1uNB*c<$RmP@Oc?wl%TJExlc6vp;G^@ z%Xbu)!LMCE{}!lZoKcHsitx{5ulK%PM+p4It|sm7%(M%wrBPs1okE?ms$`7kXD(|nPi{y%R&U`UJy#LI zXJ6_WU0Fjr$EnNH&Qf{2XWx^!pdXT-S!Uv*Ye{+w!&!wdX8pWeJok&DV2}eCPXhU> z{MYI$s#0-*{K{{;85O}k87hVGnjD-Z#r}A7#O1zzF4TwJSxp7|NI=7nQ62=h;%3e< zi{BPLhx`}@BYg?^%B=>}-qS^_13Ph+H^Dhih@52vaVqpR^@MU>nQITyEQMw5Kj8Wb z-RUe2;ZSVZF~}PpXw{%gVw0MvRp0`EW=egNZa8wn6V2~Mt0=2XOqJLXo=RgUPVXB0 zam(25;H6CJ)Mb^e7dCErYnO^u7s$8Rm5fp5OWcwuqTWzgoh}n=V_E5k=M}8!g&hI% zPrS0f+>Y2#o~!e`#dY&fFDtkpOE7BH?y&BqP%$41!6#Z7o zy~#O0yupWT8d}PPg97AB84gd`9rP4ybm1dNO(~x#;f4OJ4xX_Mv$;1S9#m$Zl1}Hn zvnuF%2tA-NqE)z+;g95J`iMdJ9CrwWjXtowmc!*!O=+4Wh%$N$kz64D`hpz!v6qLW z1zK(m?p(B=_ON)+q84%ZA%=GKh9y7kp@fhY)jC60qwMyQ=NQAiVfjzK`q=g|k z@UQ2$PMYkRqdy?cFT9@glb>azk56pt?Z+HH%I42}rh1h)a{eqL^PmNZpI)pl^LJQc zx4^ZVGNJPf`O!!c6~*@xp1TM%ko9_?_W{0ntRBdWFq3=72=KRuE>EJM{-7@^ z#<~~A*3=fbx-`#fC@*VpYa#h08zf6U>zF3-inPg&hl^-^`Iz7tCDNgBJeQc`iqooL zqhvAbNP*1NuQ%hOq_d6LCT-$2c;N8i44*^pto$w5NW?skVL z_}D_ls02C=*UX}BFw!%7i`z=MwIz>lxWtOff|LkuDUG>^6dTj#fiQ|@62GJKu&L~^ z-ddQX{DF|=qK0J2l#E-NhHtUPFK3|@M`pM*DGegh)h_oV<{*kMS__pBY50gj&6}Gv z&@G+|WGfo+l}^>)8KFc9vA|LVme`TY>dPE#N3-pk*UD@a1qt>X$FQX=qf4C6Y^ko> zMOC9Dbp)Va^9CiV{-d3@nGOdTADbUBwreHhXKMq$H)*#rN9E&J>o)B;)KA^qkc@iZ zD>=B0nPV9*$?nOs`T6mjYahH96y@zjamD5!mAIe8+11)bSF(Q_&FL#HJsh~Y`9@wU zg~+i)px=(-=%zgSD=nG*R0`M~+Cq9-oE6A9x~v`>>FGlOJ^ zZ*%qC6wmbn7 zL1vgv@aiv&*^4iB+;im zb>|S7@G;ihq7=L&Mxi!r&z%<`eX_U*S9t4O+`?1o^k)>rMmLsNlN9kmvQ}~ z!8Woevnbg1S8*b&q<`RqQ9PA9`rRoq8eT|~9ePlEisRp-ZCJ!Nv@`c-t`2hj9 z!(zBe*2(;nkJI*LqX~r?%Wg%mU%IHh`Xyc!nV|U=UQDq{3^xrB8A`Sp{@oum#)b7} z8NBtknYhNl-&;z zgoKMNGWPe0^Z_vSF5VEmfpaZ=#5NVrH-BfakWQ2g7%D*lBjc%kQgihrtWu~h$iEdY z8tGXhYRQu(b&Bs~RhoD{N9|+pa>l0+0|+IA;kc(?Rj)^5 z6G#)pZTkJnqyAk-np9@*PEdVyQHLvQMDc?2R6Ya1zPx}dRPJlIGE?3CG?j5A3#94Y zW00$D&Q=JEfQ|Ayjy!PvtkOcukLBPWO#t*m*v?^wDVCBTRIot9#lx#0cx<0{uZf_1 zzrc4Gj-0+GyOLf)w1zt7<4*(j-34R+@JO7BX;aL7UIA)}sE2ElCJQ2ysnMyGhzP{j z4(PPxsi(}ZypXaQSz5Hx*$UkTnEuPEda`s44|ZS^?9lCm12lgBlLf1$U)hHGn1Dom2>__K)>E-TbKlZd?5Xslb(ah2 z?lM?2h%uY*oetJqHUS-Auv50tDq9jB@jB#fD`JN@SjE^63ICnqB}uM0;}D0f&de(3 zUxM5;Td95#wdMJ=$yr(l7rh3|=U`hsVNn`S3V$?2w9c%DYPc#&(A@<`Z-r1UQ&c-r zK(8>!(>FI^3Gi~^0al!z71pc~)A?OqgoyONWm}TO@cFYg@2s z?)|us@|)r#x1Dyg_UQ;}`5UIGhc3#3Z!S6BhX-&L3&;i+^gw?a6BN&_{VZlL7G8C5 zLLgYaJK2+{XWlrM&&BDv?yfbGB%0YW1Q}L*;NKFEcC-km#tS~s$e~1jk}9m1iThp6 zL>k9jy=UBA#a-LQ|m|{HlvY)30;XXj;uX%9Z|D}gJOTb z0LlrNN%yl{y1L{5rdVohMUVyUe9WAB&vv2=J_Sa`FLkdK`RDghr?ESLAXY&L^2vuB zkw~q^O=)t*m zVd;CGhC~6yaW{2Y323bLUx!#3vIt>1k_MU&wqonEUYdzTD6YQ6(sP?fXJ8|?i?h?G zFrtFr2p*?B1+**aV?gdKLevj5@jOR)&Z{@U3kOIu>NWR*RwMO}(`&+YM+ z@VyV>hrT=i?9k9Jcf1r|uAGD2><-z4`pxNw@UtN|rqtsKTRYZ*F`c##NpI^dg_DoC z=TYsqwKArzdyB@Ciu=h5;Y)_{7j1aC2=2wQk(Jv4+LdUiJ=+{V-`vgzA{Hce8j+-* zJ7A$vcj$==&bQUE%GJr~ld8tGhg8cUDDp_;Dhl!_Yg_ogK7bDz2zQ9o-3G`pRxp7BXjYulQ?G9~wW$C-LwtygM zBeeHH*pw}Mv~q5JfE1o#5o*+bO}gFpzImW@Q^YTyl-JABu1XmcPW(DLa`QI2oWgg~ z1KQQMHn+u&C7UPRx54h~`)hQZBb||Su1p=>gnb?f9%(fzbI>lo%Xr&BY{XyUsFnM{ z6t-;q^eJ+jO*;CNPYbU&v9$HcsM%6Kllz#04@_0aM=+By^i%1^u9y=5&y9Qv%O+># zLPJYRx*9mkUK?GRboOZ>&-?v&i4Txya;K z3e)x+$C@xg3-j}*5pwrPzmOpsR}=43AS_Bc0f(Pud?-ul0rMIz*lDmK&z}FSofnD^PJJznY#{V^1Sqe=$8TM9l0x9B{oDnczc#xb4Ei26Qeb z+FxN&?p%7;c8bc6Y{TYdJ>TD z&K{pDS4}C`d|Z(g>CZPhdQH#PZqG#mum#+Ht-gsR3{pDMr0+ZZyZ!*9AfqZ>DQOn| F{{UzS<75B; literal 0 HcmV?d00001 diff --git a/android/app/src/main/res/drawable-nodpi/weekwidget_preview.png b/android/app/src/main/res/drawable-nodpi/weekwidget_preview.png new file mode 100644 index 0000000000000000000000000000000000000000..2ad380dcd70830df57213df12516b065ee165054 GIT binary patch literal 13198 zcmbt*bx<5pvo8`nxCVlUgup_O;E>?%!4{WA0!s+)?hs^gcL@%QJAn`uw;+o8S5=n5#iqnYLPElola*9QLPCKeAtB3Sq9Q2M z(TaMA$(*X9hLqx0``i16yZeXh+xv^Fo3qQC`-jK#>+7rQ=l=-e&D}kM;rY0`U;B5o zzH_{?eYmoDxVn9Gcyh6{eo$0W{q*#-arn4$we$G&ggCBlZiNCaxA)KP?jP6w9WQMj zK0H1ipIz-AUmyf-9Y0<^+^iowA_NKtUT*H5EMNUQJh^22V|0G~bba@9aQ<|3dUh|$&W&3F7_IT;)-|o=`Lbs*Me>H#*4-*4^guWjszhh8P;)lkrKS-M6AR^N8b zJe-|hjI2D^dO=q99yQ~ym#=nW(#w7{+`5INC8y;lwcVB0cI_P;<(4(~k4ztZ`gEC? zRNwO3!9Qtn=TSfTdS-4(HuCDOzrSzc0lxiMK5*~*Biq9_{O`u&8-R%7m^aZcb(NT_ zh2`~$skv_0cw5h~qiZ0C=Ot`lG<~x{SVCQ1)u^kxuNvCidN>jsSD>k9m7Q0LFzg3j z86%LrwVh{Bc%1R{kBFq==(zO1=PLlWi>lq<0)uuz%SC-B)A!5EXSTK{K;8ADtGk|_ zm8*a5>650old1@3lRK#^e?hV@lH%%{MtvY1-GCVSnO7knc%pKCD)rQ1{E{G9Z7LBqF%Ws&U^_> z6owZdatdx68@VkV^Zx;5PdU z5{;w%v$CS}p>SGWVV93%IHqZS z(fVUlx^3g!+_xn+x8nK>UTpb`0#ceyB|iVT^aHl!4gr+99LQcLGOAYL_=3;ui#p!L zov|?CS}QfxQWg|^>r>?~^A(rT3d?_KCj|YBGDSwQkie`2%hNusYAdIk_RP#v2@Fk9 zjp^SOtv>r+#ts>_O~`(XN}Hy#waqb7YtHi`be=z{u4vz>BL8gn`tMApkP0PF6gATR zuu#Ck`&R_1>#gJXR(u|dZATy1`Q8?* zlRz1-?0w#_>jS({$ZP%U01dLONegSB{UrG`8UhF;N z2so}jc)P2UkmmFYc5~JD?bn5MTc8U-emD(%L%!MMJKm}BH@^DCKSg3Cnv>v3K1 zVS+b|$oEUQAJX-1M>Fjtc~5S-LY~cqcEHfcSESqmiM|$hj?Jm zj?6BKYH~K@7xbPgZ6+%CwTqM zdHkOl5kUp!wy2)PGZ}OR%e#x-NOI}?@!Rg(GLes$-AQ?K)qI!TMM{Mgf5;Un9c1i; zM@0)l8H7GjzRIlHTxhpJ$03ZJ%>&O#5>zK+O=5I~;$LKB%cL>cQ0YQ_`-u$pibAHT z%XvXqJZ)c9d&ci?F^U{|3lb_9Q8>U&Kl}!H&WX|}r|g3wCvj-&7V8-FmvQH$&AH%R z;4l576rPyhWRUBBfxQ%;Df~d=zYN5g1-SMVdlrz<%EYKS+nU6iluVCVz zah)%QLwdr4VO;<-8230SMF4k0cZr9Wfdso7uJ+anzU!Tb)wn-0e)9tjw4AmnpMFr9 zcw{_hU+M7}hR)!RwllQ4ScyqTWq6=#Y%56aAMw@8z`yd146Tlg44H21ww^4nw4CkN zRVWiIASttE?r%Zxt6I{!WpQ$EVW*d_X#d!lUOF@7Ix^6d)EhGy8eL(EXwJ@vXb@^4 zX}Z#`p0(8fG{;DofHL}j;;drTZ$;L^})x&hm@R1 zx=*Nrda*^+n{Ki2Z6=!+bMapWc730*?jyuSyKarM@K}kI+MTqmU#J5^P$gJ9lP?5Gmb;M(2U>332|q3M!XAcHc0v}knm-_;S;dGK+ADSf1SncE_@q$FE3JxDITlyBHR*vR!X z4CbQ#gws}4lwUxN=N$6`Dc?BIP5PY&Gt~HDJ6l^L1{unTWx?`M3mLA%EQB_pFV}2r zKC0GZJ3{=*B9l!W)W>5&kfVepWRRbR9DFopDNy5xL1U>J&zKp+Py3i|xcF6KCAsP^s+J-Ac^vhDw9Z zPZ>vy$3X%K#-V!4WeXqoY9=O7P^qx$o0;G*p~!E&fJhw*M;{|<%O^? z4|XsLv?6Ng%?lMZMlDp}hcKTaE|3hvCoZZJwNaa-F{&tr);Sq-oiEYls^epdSWtfh ztx7v_IZ$g0iUt`Hkjx{(Nofen0aCaocj)3Q6C(20BUlUN&P=%5OQRVUAdsYADD@5| z1`E;t^^u%E|w- z3f~Z&sSN#tf_j#>WuJ#Xumz$*CzvDDwG5SRHG%C&^dKGn5G{~#{s_`^PQDScJy;lz zojON*!;_e^>h7TyXTq^O_0FmwD;r<&oH-a!9CnB1-Aa{uHcsb0D!IJ>uQWuKFwo%& z2bAKD23`Ch=6+R1v9X&^h&rq~LqOdIsWOLWd)``+e0L$C5pMM+c)WUVOtB$jCZdr} z<5!Nvf58+TQm#`>G#`^dECDH`SN`77O#~%)7yIX*Ns39>1|(SF;s(T6)&T5-25rk( z{ITX!1=Sap;0tkJR+KS2xa;gZIF3^;0c;q2~* z+Q3+x?l0%)36cB!6n%gz8b#P`RaFNFF`9+=Cq!a4v(`Ukwi9ov$$Dn}XuC zbHdCw(VDwba1~!xTYf4a=0NqLg%kDA!53$K0-%*x99gT%j9IXsGBtF`qM;1*X+Pt_ z!O;1>S{Z&FnkZ^-2%0tn7i1XJYC=xqFxJJ;hJ`QMCLx|g84r{OKEEY<8L^QEq)~iptO#)E7t&?b2nRmK33}WM<}h zqnc#!!@aEUHQ-9kG}$icN$(!TICj}cI5o{Bu9M1;=1a}1!%&#G?n}C95zd6yx(9|z z8cR=%1_(b3Ok2vqwsU?-0;jiNRyi?Kcwu=B)QkR+f@MxGiJ>nxnvBjMejid_r{x@y zQ)AOo$iK~H>&mbvjdfOX!YKU#Xx)>f_2g=PI~%B6k0gL+fGU@~(HXsAR}(H;2#e;C z)g@C5%or}4DLyzP`~Jri@CS`6zU(KORJF0xd*Yxp=oniWvKyJ`{!g23)MS!>*gp>0 zKVpe*s*(oiqn396BX5_JHMK%g<`ztdT@{fZFK)XdeP#K_-jt6k?^QI+vGW>e6f`v= z5eQv(C&w2gLht=3fh=&C%#rm$@K>CATpcMxkH|zyx=c%%3`+i#lvZNS!JiWSkMtVf zn-MBwUi}ztI4w`^uJqEKeH@3(kLBu=S+5tr0v-6QX)w`}WU$_MLie_yT2tu%e4%PnooyoztuuT>SY za;T}VH*OXs?@6JXp)dQ+EQl=Ccsdb$mOxBS1@$W$9VPYqBRHV}XJqtDNY%y0ykaV% z(XJYr%P8!j{xaSR#D_+dBLNR{*5hzx1<+*KA0{7$l|C=**R7Fx`RXm568Bf~_E8@b z78^e$8(&X)9)>W=#RCMtzYfL=gyJ*U1{3 z&AA$xbrfZr!(iqK2l(}yuXE;1&5n&B)pm)ka8M7?)iKwH0aQyO`zR_mO%*Xb1%7;^ zgVu)tW?#^!FEPKL;Tq?ouzWoX12E{>=r+J1pD=aME5@mQFw};D+30a5tpW_qdir{; z2I&xl0}iAz@GRx0JX4$qm$!Y4bz$IPf-+|x`CfSs-swmf1GHWo?AY0{T5DQo4gpFw z@P=(tFNO4kO$H4i&Y8ge{J8C*+Z#5Sp@$~gj$S^pTLcK#(z!8H`7~1FEPiLYS4OIUmfwl#ZCi?>nWM|@46U$k}@ZDb7 zyrj6yN4rJW@xYldx$J9~Uk*s4E}KQoS4b4??e*&YTA_*6f*zft*k?l&UajhZ(S@x_ zlgJR?W#&#zcnUuZ8Jbtr*625fb5vO`7b1&q`9cpKFdXB8jqicG1u@sUD~4()_GuLxK=8tTs+vUR2gFZ%ll znR2GykOO;n7(CK0dWnFaJx&sBfKy9#y=zx`fWezX!aj_dBDN})r4$@32wT5zf5G3D z^7#_W*xiG}a^=i{^J1K#-3n!i;L(udr8+puMG< zA&X+1Pp#1xr0G-0oqJH^4VLfp*WE_bxAMY zhnv+a>JX>Jyn&>*&BZCOTpfhQ^{)}!?r}qO>l^M8%G%tHRRI)$qHt~=pa^zMW$=-% z8CIjWJ=|P?I-SX*7P0$?S5`UxT}kw6yA%x%I?1#rDh;0g8BE?&*eVoYa2-5=Go5X% z3=C=yS`2!e^zZt6n(B7ok-OeM+Wx%WsB@1x_;cql*O@p|%LuirXBwPXxgU%&A3R6T zmtB~xyH0+GLE1F+F`n$nl!)b!uc|rB!rYtjL(chhOh=DyFsyshsUMn|L*BUBxK99S zLs5b@93%&Vt9~=m$$PW*XpqRG{4hbbL#1Z}3V!g9N#-|Bq!(%g0SoplBldirXv!U1 z%nfNQk-*)~utd4DEro{SGpPG}HI9^=z70lJ%~)$upV1?u;?1`Y>D2&U;%4(uayEtX zHOIjua|@34v!iGe!v&`KBPL*PA?WL(dX}O*La~OO0{?-HG<7AANHga? zXYG8)`W47dbAGG7x()V9PBGn8ryA3vhXLp>3_P)8Pa_p|i4#HpN4YV$B^A*Xk}dK! z;d{&$FPo>ZlomyuXlF!$&L+2;4Z5oiotF~66VL1^flLl#W+^txfVK(McE!41xUaoi42dT+uH!$(u?ShEf?Ky^e zBv-&ZOUX9Krsua;?poD-j(NDvJoM*4s|hr)U-hKzrba){JltYX_=X7`T+%0&UGC&1 zQ+BIjfuqJ^IHcSBbRGSVX|TB=4Ef$Y@BeKl?Ks+_8Q*t=|>)!{*>Vz~zrab1 zQjllOJJLzAI1_4mMXYo*47fbcUD3S%aSiMAuY&mC+4`CCTr?Hn{NXBF~utAE>uu#l|6L zJNweP@t+1WNuAC&JzF1ySn*&GOSm^hauUbjcu(mTu?$qMnNlWT(d@r!@Ryw2L zTO-Lqc9&n6MAxTcle(KL9op28i>mUF<4_3k3Z)zZ^bJYXGJ|+qAo68YzM19fa z8Z?0fgO=vSnbYH#MDmbLX-Ezp&O_BH`E)7~9RrID`?H|Y!Ydi@<}B10CVpQNW~Jh= zIYsk>dFcJbiBm%GtMs8B9gP!~i2c`uq&sey839Z2l1yCO7~(&LG8jVkqkTP47l<&O zZ+28MBVXt?5Jmg|#Q;*T6o`J-w;2MPpIBr4~4&@#U#rF4%u{(o5>iO zEal(AH7Gb?pOG4e6mBB6$(4x2JZ)`Ac3!)c9%2RSG>2}vi2cryc_qS+BWU{Vb9QKN zBaw~zm_maMe&bwDNYWtwglaP86>88^GKrgLwXs8LRkfwz>1!b^4AYMzU*@r?6iNCtn&bwJtyK<#@n|MZ{ zQp@tO3OOV*DXLs_y{)j3(piCTJDLhOOX(G(`^nrr_k;N+g(9hgVan=UoI+=HhucZ_ ztnuGIdYdxWeKb;=e*H9TtTEAEBT|MBtO^rd1zfNRl8}JU*oHH6NpBR$o+hUAq`$#e zshg%EPJaF>Y*T(|*U$GdMXxc1?nZQO@arIYE@KmiU+Wv+^3>h1#D@$vvICv+k0pj7 zCP-hI_DpxCU{lcEmv$|x$m4%96-{GdXycVz8RyemY#VMWnpPvCpN7tT^uGlcS#RJ5 z4g+sW^ar=<^Wy1CHCUpgiHiv zm5m#+9fMjusNIFMrGok3w0gX)DnMD}bmQR_vMEPo-hw2OE~OIk`}COt)!0a*>?}j_ zt9MG{`hvTE-Mct^-#4zU6TC_ZPGt{{ka$T-WC@J5kuZe6cHYyGHp>O`ZnQ~7yEEVp zuBI<#C4lTj^fB=V#T&Fws|FnE_+?Oo&?8!9<;`%MY%BFQQ*+aMBkX>51#y2k0GF$L z8yHPy0*$X3r2}B#j&u^Sq+T9AavV}$v^i7iQ`9S7^cjGnD~u=ZinW7V*(zwdv%aHa zGntELld_+Qlw4<|we!^LHXi4S}omm6Y=SK(OOv?bmmH_=cN7Oxt}1<+L4!Q^6TWHzqkJv zVX6=HMGS&8E^#Mb59JeRZY0v`Vq|;odCZi_&@^JG+>0W^lnWmP*n_me%@kx)Y9&Fs zi|&t;Jmi`TCGx{xEwa$_tTI&F2jbW991<~$htzRj%fayqEuy|Q{_yI*()Nipd?8js z`W8<|Q=A|{momTnxKv9&6%4<^`JnV#h&s|aDXQPc%RAqVTa3P`OHC;Sz1gY9@}m5c zRhQ-l0bCcdE$&nA#!-+ z51XEg{>XNia$yG3owTnrBZSY*U$L&AQ&KI>Zw78rQ&NEs?I1dm4d|}gADfYN%WA4& z08dWJ3tky)zI-u|jT63Jpb&pjQRKjc!QRT$ZWP>6jpnLa3S{F_{Jm0Y`!JNp!o-~M zXedP_Wsn!=r-_<1DKO5csV{aHx7jgQQEPBpP8uPH1UT^SD!h?746KB$O|s9DKPm)s8ce9-y%gZ?~OgErZZ%K-0 z5RQSx#dAEUk~a>66s>zD%XoOsf-9I8Ms!pwG_x^tNw*2$b9^{lF74wKTstHO-hp+c z(eJaqRSQnNlYJ@7ptt8z9?ou7sZ9y2_aC+hewaLehXc$+hEzEB*W4TTLZ)~XY7eNC zDWKN2I1GC0mC1F-`D?IUW@zqtzl4_eF-kXLKRAoF9Ic536g&&FK8_D5AMt}t^FwQ_ z>2c_Rp@fKCFNhE*Nd}Sn&#*ZgJH`2)hBX4*(R50F^Wz}$ww;?EYJ>{aeXE{rqeL5B zC!ODN^*M}&3|OI&F|Bc{?A`S~FI2Qlhfvoj{@3qP1Ad5HB+Q2gW>NgJK+!LemnzMp zN~j(vGiIP1&6pze20C8yeju-sPr@hxrS^Yl2Sr8vFT!F@8V z#%qX3J8XB&g=NGogr+$2PGd*3VyCX1*SqaU(a#5|r`6agiwqb3sD_zOWE0(ylZT&l zy#HJ@ER8bYY~xb?iz&Lbb-B~=K!2=nW!j`XxI!m{SE0dwE|y+-m?U$j>rvr$ckB%1 z1V7c&7jsKOY{SThrElY)Pn5zW(Bno668aZ!=pKoQgun-B6`mo?f>E zIP2aHGG`<+DA+MnY1@JQbUmr}N1euVrYhMr^h04;E+C`X1$tMnKf6QL#S86!*+2T1 z{0ylWZTl7<&QMmA#CTWVZyZKL1212s7_!cXHKhBMxi@_7+5T`yBIUxe{r!s`VXRA&Cw?U0+To)=5)vWn^Sc1vhF{2#_00fg z1Ara3W05!&>Rir{i2)XtL zG2VU+d8lMk+6!)a3Ff3MxoyQSyk@VDoo3r^>)49(dA|;(UiNPNtM-N*gQ}P^F_ogsUnxr-Iv}OoS9T*C$8&+VK5RnM zn-=GnITgtFH5#2+N$9287z1uBX54dJZ?9be{|b0nGWggswbMU;0MC76hRQ;MS!D$( zM4KF0uFDL{qNy&pM^=6$mVzZOHCm09KFS8Rlb55i((N5n2BwxI@Fpv7tNV>a)+pA^ zd~=pw{OT=Cwl5)r!(B2@-5qv`U^mgN|9RCONIU*OCOHh$p1@!Y^O3hJ=^9qAHyIYIXC+beNWfo%CdJ^B=P0DAxF)fk?b~4N#x;-Cu{_yG=~CR`XQDH z1qNDCWZZ@!hb>y|X)3^WEx8DUOkopr$u+bgnat2GDXn556sTZ)kJVtWX}enM%xc|b znUX-ej|CVEc-WgI`PVl)t21tnlGi}fq=3IcKKwja)8mi zBr}a}(ku*>P6;ZB6x@-bgBCA8I0Sy1+JaECGL$8)L3bITyQcbD$+WXG-q^q^MZiC2 zi>VbFc$X*|sNIa0MNE`;I2vrvK}|WaR2N83WtI(>IB#=j!wIq0cu-^8D_Blspppcs ze1;0{zAvn39;L&1`?l%mrU^j3{NeuT zOfdMcyU-lr)DuZ`3ZpomUwfVifT+4KwR_q_4`;r{Q2Y+~m9uE$tXwh}i`)CdMaG2| z{+HzbBTb`Ntb*l0?|%E2%T)bR-y$RVuLAr=yoctc0()R9OzE#=f!aq-{QEmiF4*GQ zVlrL;GWA+Q3{n2d5XZT-Xu1Ny1iJLkNB&}!4xlLB&>o8@li0mF#=*zqCjsAA!dy%ia3>)5;(q;bj=}oYcUQSI$OLCO(zgyq>!qaWG8R)dQeiFR- zrU~rvG7jHV;I%yeVygZ(j70sgPs3EZ`N+K8$boa= zx#hG{w_D#H64w$L5ApQ;n7e0Yd-+?uL_qK*&T{YF`eA}rvVGH+{bB;kz%S>MpZlR_ zyDM#im-IqMpCXS1E^6}jJukV({{7+PBcYjC^*ea4a>6SY_j%-ru~2=l;5?z5@0>Wz zaJa4V{&TPJULa&D4Lh7I_zjqtNANVeBp+}mWYm;EhH6yHfd9!(uDP&zai5J&$ULl6 zDTVAzQ?UT9!VXAH$eYj3%VT{}=d}B~gV z$!j2{a>LR-%{AwX*(trUw($E&(ldBe%32gGiv+}dqAwp=OGE&V4+ICF1wL2z8BS3| znTJ;a-8`LR&20=@Cf^YN^~-Cgd&hIC=Hg($bT!|#>EvD}h|MCG>U{Yv3*8`D);4w1 zu`srpmjoqDgp(z@yhLg)jQ$@_767D#!k~8-(J=Gsj3u)CeJ4FG2hJBO6#{5cjIC@S zY?`R%HRpLq-LSlTq-vdzu6pj$7BM5+>u;K&Q~^W_1SEx&ckGUuK}ixIHP!!axc^u8 zU3G(jwe%x1vyraGy$NI!_Rx_2Jo&cx=r_wzK`oM%7{gn2o@~j>G90Y7vp9= z`-x15loUa%K!+aq-rrnxa^Bo!`!d^1yU;cSR=0i25vMUvUo?pL>=+TW3EbIF zciW$!Pxsr)BniHc%bneb+mtz>KHBK3DJpCnA!d4{pzVR!ht_~ozGvADCdb>KNfJLl z1~(winO`Yrd<6N4u}fZ31n5&FBbLrS%>H***+XH$xj`elKYZ*na;{w@!vJ`*T6SGY z5^Rw$Eqb=Cr>!O7);k?~!hw2lByS?uu{>5N!f+_fcely3wD+f5JKRzmJMpdJ$iV3D z@FrV}x0YjHNeRDj$H06pU=ADG%h{iwcKY~6J6emw?vMA!KS2ds34fb}v9yc}GTiYn z4nmH6y&hM7If`B`IR|g-tOuBM37q44v>S?nriRBP!}e!>l#3cZJ&KxuvKQIt28Aka zLqgEeAXBfNZSOw;Gwj4k{uOIm)2T`cBxbmqV!YduDTH|I{(W|@UNGIvV&eeAUC)IK z3c}f1+^~!6#!lAo{aTqAtY?DrPL#}%&)vaAeye=AC_-jKDU3{|-vtB$Vi!v8qRuN-4<-p6`7L{k^$KiNOFFns)bx+m`O!$#oqf_zIq2X0AoN*sHH( z(eI6t7*&?qeI7Jc$)3{c_FIj7e2*tbRXVh-{C4{d@U%}JUqD|I8cIZdemW=-mk5Zx ze$=ENgcN`Nd_$tA$H!Q6_GeO!Pdl>&h zvp>FEnjq801IEf2kwZI~qxP><_$4oiVV@q=zJiWK#6X)sbd?v9mpYIKy#L-u{9lLO zTmKtg?|WagQsHK6=oQbL8Q9B&Tl7WUOzivl7lYB zsMGuIa+t9&h=~~icYw0CB*NAVP$v?T9j zby8HvWn6vATHbXsw`nf6zk7yL+^%ijd1Zv&6g@O-A8Wq!|H(?v8?x6A-Qyo5FZmIv z@ai*5hS@Rdd_M}pwq*!#K(bBFNMRcumy7uFuy)!kk#0ryXH+gd93(IMK5gC=kCG71 zuF5~-(fV8K5aA)A(hyk!QNVL)*gYgMOe`^aM((g}vV90*7ttLng^1?=&UQ3@U>*P^ zB0>BY%^gJB>HaWpQ#kF?bVu>5KmOAOmrk!QR^ z{hzS;->Uqd^RK7ZyythsBY2v|ZvH2;VgsWW?hX#-U0rG4Gv0tLo)cVF(!;?O#^-Sn zFzKA~;rqdt2)n{dZCe3_AuNptry>pf^oUyyh->xwdO6-Tj*>1dJcdGt4-jRuA%J+= z01-k6$OGobufF~TYTYSg>wp*#WLcLHCoaOviKT4IBzl9t$iH^46-w7rARdbxTi`C6WiO)zv*#@Gqruey@I*Nw6x4kI@|4g zU~O5+%kO!DIIw9WqHjFj8xc$6Oa57%#bNrs4p%y-<^l|DArYUoTh=DxV6WZJ=SS^) zwOGQhA$7ep44(WNr|BeaUMN8;h;3^4hP-vz{hk zE^(|#V1q>gP0#s*7&Rs3ElJAUG@MaKu&5%Z7${~8ZF@H*y^Ub{^}P%gfejl;bZoqj2X`g~Rsghp}ALVDaal0HZI3_fWHsg++@M+V$c zL{R5Fd=|qAa(CEb{LN)CFZDcS0*UpZ)X+{soiBWO>d&52f&|Pb zLPz@GscxD4SraV;d^YCZg%{RTW?3LQMj#thp#RTGA0nNvimP6ZX78R;BEpW!vb&$I z1We&?f}hX}ULa&YKw;vEH>~9N5`g=Sg&RDicb0s9Qxlihew}>g9*T=#k0A%5F9zF?@9pB@?0>5JxGB2gj*yM0okH;1 z_Opp8Mif>chVkU%ML+~;4^HOu0~mn(wL((eRtMm>Ej{xu%p=G&4&YBBj<4zw*+O6U z@;@IOke!r?{#%|h685~9OI1kLG<}Egnid9JS + + + Inbox + Newest messages with sender, time and subject. Taller sizes add avatars and a line of preview. + Triage + One unread message at a time with archive, delete and reply buttons. + Unread count + Unread count for the inbox, with the latest senders. + Latest message + Only the newest unread message. + Folder counts + Unread counts for up to four folders. + Shortcuts + Compose, calendar, contacts and files. + All accounts + The unified inbox across every signed-in account. + Starred + Starred messages, newest first. + Favourite people + The people you hear from most, with their unread count. + Search mail + Search bar with your recent searches. + Scheduled and outbox + Scheduled messages and anything waiting in the outbox. + Tag + Unread count for your busiest tag. + Last draft + Back into the draft you left. + Agenda + Upcoming events by day with time, colour and place. + This week + The week at a glance from 8:00 to 18:00. + Month + Month grid with a dot per calendar and the next two events. + Today + Today\'s date with the rest of today and tomorrow. + Next event + What\'s next, when and where. + Date and next two + Today\'s date with the next two events. + Up next + Countdown to the next event with join and running-late buttons. + Invitations + Pending invitations with accept, maybe and decline. + Free time + When you\'re free this week. + Tasks + Open tasks with tick boxes that work from the home screen. + Task progress + How many tasks are done. + Countdown + Days until your next all-day event. + Birthdays + Upcoming birthdays from your contacts. + Today overview + Next event, newest unread mail and tasks due, in one widget. + Mail and next event + Unread mail on one side, the next event on the other. + Recent files + Recently changed items in Files. + Recent attachments + The newest attachments from your mail. + Vacation responder + Vacation responder status with an on/off switch. + Storage + How much of your mailbox quota is used. + diff --git a/android/app/src/main/res/xml/widgetprovider_agendawidget.xml b/android/app/src/main/res/xml/widgetprovider_agendawidget.xml new file mode 100644 index 00000000..dbe5aaa1 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_agendawidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_allaccountswidget.xml b/android/app/src/main/res/xml/widgetprovider_allaccountswidget.xml new file mode 100644 index 00000000..6a7f0518 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_allaccountswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_attachmentswidget.xml b/android/app/src/main/res/xml/widgetprovider_attachmentswidget.xml new file mode 100644 index 00000000..8a5a3539 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_attachmentswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_birthdayswidget.xml b/android/app/src/main/res/xml/widgetprovider_birthdayswidget.xml new file mode 100644 index 00000000..050009b7 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_birthdayswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_countdownwidget.xml b/android/app/src/main/res/xml/widgetprovider_countdownwidget.xml new file mode 100644 index 00000000..cb6f174f --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_countdownwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_datenextwidget.xml b/android/app/src/main/res/xml/widgetprovider_datenextwidget.xml new file mode 100644 index 00000000..2c97b57e --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_datenextwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_favouritepeoplewidget.xml b/android/app/src/main/res/xml/widgetprovider_favouritepeoplewidget.xml new file mode 100644 index 00000000..5195a43f --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_favouritepeoplewidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_foldercountswidget.xml b/android/app/src/main/res/xml/widgetprovider_foldercountswidget.xml new file mode 100644 index 00000000..d6acc336 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_foldercountswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_freetimewidget.xml b/android/app/src/main/res/xml/widgetprovider_freetimewidget.xml new file mode 100644 index 00000000..dfb312ec --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_freetimewidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_inboxwidget.xml b/android/app/src/main/res/xml/widgetprovider_inboxwidget.xml new file mode 100644 index 00000000..5602c37a --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_inboxwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_invitationswidget.xml b/android/app/src/main/res/xml/widgetprovider_invitationswidget.xml new file mode 100644 index 00000000..8a19f0f2 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_invitationswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_lastdraftwidget.xml b/android/app/src/main/res/xml/widgetprovider_lastdraftwidget.xml new file mode 100644 index 00000000..d6868790 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_lastdraftwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_latestmessagewidget.xml b/android/app/src/main/res/xml/widgetprovider_latestmessagewidget.xml new file mode 100644 index 00000000..b3f29e33 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_latestmessagewidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_mailandnextwidget.xml b/android/app/src/main/res/xml/widgetprovider_mailandnextwidget.xml new file mode 100644 index 00000000..5e36edbe --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_mailandnextwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_mailsearchwidget.xml b/android/app/src/main/res/xml/widgetprovider_mailsearchwidget.xml new file mode 100644 index 00000000..7fb3e15e --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_mailsearchwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_mailshortcutswidget.xml b/android/app/src/main/res/xml/widgetprovider_mailshortcutswidget.xml new file mode 100644 index 00000000..d0dbca8f --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_mailshortcutswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_monthwidget.xml b/android/app/src/main/res/xml/widgetprovider_monthwidget.xml new file mode 100644 index 00000000..0e298659 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_monthwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_nexteventwidget.xml b/android/app/src/main/res/xml/widgetprovider_nexteventwidget.xml new file mode 100644 index 00000000..6f6a8e8c --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_nexteventwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_outboxwidget.xml b/android/app/src/main/res/xml/widgetprovider_outboxwidget.xml new file mode 100644 index 00000000..8635b1df --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_outboxwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_recentfileswidget.xml b/android/app/src/main/res/xml/widgetprovider_recentfileswidget.xml new file mode 100644 index 00000000..afa8712b --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_recentfileswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_starredwidget.xml b/android/app/src/main/res/xml/widgetprovider_starredwidget.xml new file mode 100644 index 00000000..a69ef5d7 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_starredwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_storagewidget.xml b/android/app/src/main/res/xml/widgetprovider_storagewidget.xml new file mode 100644 index 00000000..98fd3fd9 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_storagewidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_tagwidget.xml b/android/app/src/main/res/xml/widgetprovider_tagwidget.xml new file mode 100644 index 00000000..4e9275f6 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_tagwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_taskprogresswidget.xml b/android/app/src/main/res/xml/widgetprovider_taskprogresswidget.xml new file mode 100644 index 00000000..3a15f164 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_taskprogresswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_taskswidget.xml b/android/app/src/main/res/xml/widgetprovider_taskswidget.xml new file mode 100644 index 00000000..ecb6a9a4 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_taskswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_todayeventswidget.xml b/android/app/src/main/res/xml/widgetprovider_todayeventswidget.xml new file mode 100644 index 00000000..961a96be --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_todayeventswidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_todayhubwidget.xml b/android/app/src/main/res/xml/widgetprovider_todayhubwidget.xml new file mode 100644 index 00000000..c053e67a --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_todayhubwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_triagewidget.xml b/android/app/src/main/res/xml/widgetprovider_triagewidget.xml new file mode 100644 index 00000000..0d731a32 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_triagewidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_unreadcountwidget.xml b/android/app/src/main/res/xml/widgetprovider_unreadcountwidget.xml new file mode 100644 index 00000000..c39dcd8d --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_unreadcountwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_upnextwidget.xml b/android/app/src/main/res/xml/widgetprovider_upnextwidget.xml new file mode 100644 index 00000000..cc956c56 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_upnextwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_vacationwidget.xml b/android/app/src/main/res/xml/widgetprovider_vacationwidget.xml new file mode 100644 index 00000000..18a0ee3f --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_vacationwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/android/app/src/main/res/xml/widgetprovider_weekwidget.xml b/android/app/src/main/res/xml/widgetprovider_weekwidget.xml new file mode 100644 index 00000000..6541f415 --- /dev/null +++ b/android/app/src/main/res/xml/widgetprovider_weekwidget.xml @@ -0,0 +1,16 @@ + + + + diff --git a/index.ts b/index.ts index c68d2075..e1ae8026 100644 --- a/index.ts +++ b/index.ts @@ -1,13 +1,22 @@ import { createElement } from 'react'; import { AppRegistry } from 'react-native'; import { registerRootComponent } from 'expo'; +import { registerWidgetTaskHandler } from 'react-native-android-widget'; import { pushBackgroundTask } from './src/lib/push-background-task'; +import { widgetTaskHandler } from './src/widgets/task-handler'; +import { refreshWidgetsInBackground } from './src/widgets/sync'; // Runs in a fresh headless JS runtime when BulwarkPushTaskService is started // from BulwarkMessagingService on an FCM data message. Must be registered -// before the native service tries to invoke the task. -AppRegistry.registerHeadlessTask('BulwarkPushTask', () => pushBackgroundTask); +// before the native service tries to invoke the task. New mail also refreshes +// any home-screen widgets (a no-op when none are placed). +AppRegistry.registerHeadlessTask('BulwarkPushTask', () => async (data: Parameters[0]) => { + await Promise.allSettled([pushBackgroundTask(data), refreshWidgetsInBackground()]); +}); + +// Home-screen widgets (Android): every widget event runs this headless task. +registerWidgetTaskHandler(widgetTaskHandler); // A headless push task evaluates this file too, so App is required when the // UI first renders instead of imported above: importing it here would load diff --git a/locales/rn/en.json b/locales/rn/en.json index ecbfe47f..cabbed7d 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -1038,5 +1038,188 @@ "severity_deprecated": "Required", "severity_security": "Security", "title": "App updates" + }, + "widgets": { + "attachments": { + "empty": "No recent attachments", + "from_mail": "from mail", + "title": "Attachments", + "unknown_sender": "Unknown sender" + }, + "birthdays": { + "empty": "No birthdays coming up", + "empty_body": "Birthdays come from your contacts.", + "in_days": "{count, plural, one {in # day} other {in # days}}", + "send_wishes": "Send wishes", + "title": "Birthdays", + "wishes_subject": "Happy birthday!" + }, + "calendar": { + "agenda": "Agenda", + "agenda_more": "{day}: {events}", + "all_day": "All day", + "join": "Join", + "join_named": "Join {name}", + "more_this_week": "{count, plural, one {# more this week} other {# more this week}}", + "no_more_today": "No more events today", + "nothing_planned": "Nothing on the calendar", + "now": "Now", + "open": "Open", + "running_late": "Running late", + "running_late_body": "I'm running a few minutes late for {title}.", + "running_late_subject": "Running late: {title}", + "today": "Today", + "tomorrow": "Tomorrow", + "unit_days": "{count, plural, one {day} other {days}}", + "unit_hours": "{count, plural, one {hour} other {hours}}", + "unit_minutes": "{count, plural, one {min} other {min}}", + "unsupported": "Calendar isn't available on this server", + "untitled": "(no title)", + "video_call": "Video call" + }, + "countdown": { + "days": "{count, plural, one {day} other {days}}", + "empty": "No upcoming events" + }, + "draft": { + "edited_at": "Edited {when}", + "edited_minutes": "{count, plural, one {Edited # min ago} other {Edited # min ago}}", + "edited_now": "Edited just now", + "empty": "No drafts", + "empty_body": "Messages you start and do not send wait here.", + "keep_writing": "Keep writing", + "title": "Drafts" + }, + "files": { + "empty": "No files yet", + "recent": "Recently changed", + "unsupported": "Files aren't available on this server" + }, + "free": { + "busy_until": "Busy until {time}", + "free_rest": "Free for the rest of the day", + "free_until": "Free until {time}", + "mail": "Mail my free times", + "mail_subject": "My free times this week", + "no_calendar": "No calendar on this account", + "then": "then {title}", + "then_free": "then free" + }, + "hub": { + "all_clear": "You're all caught up", + "mail_unread": "Mail · {count} unread", + "next": "Next", + "next_on": "Next · {day}", + "no_events": "No upcoming events", + "now": "Now", + "tasks_due": "Tasks · {count} due" + }, + "invites": { + "accept": "Accept", + "decline": "Decline", + "empty": "No invitations waiting", + "label": "Invitation · {name}", + "label_plain": "Invitation", + "maybe": "Maybe", + "position": "{index} of {count}" + }, + "mail": { + "all_accounts": "All accounts", + "all_caught_up": "All caught up", + "archive": "Archive", + "delete": "Delete", + "empty_all_accounts": "Your inboxes are empty", + "empty_inbox": "Your inbox is empty", + "folders": "Folders", + "in_outbox": "{count, plural, one {# change waiting} other {# changes waiting}}", + "in_outbox_short": "{count} waiting", + "inbox": "Inbox", + "more_unread": "+{count} more unread", + "no_starred": "No starred mail", + "no_starred_body": "Star a message to keep it here.", + "no_subject": "(no subject)", + "no_unread": "No unread messages in your inbox.", + "position": "{index} of {count}", + "reply": "Reply", + "scheduled": "Scheduled", + "starred": "Starred", + "unread": "unread" + }, + "people": { + "empty": "No favourites yet", + "empty_body": "The people you hear from most show up here.", + "favourites": "Favourites" + }, + "relative": { + "days": "{count, plural, one {in # day} other {in # days}}", + "hours": "in {count} h", + "minutes": "in {count} min", + "now": "now" + }, + "scheduled": { + "day_at": "{day}, {time}", + "empty": "Nothing scheduled", + "empty_body": "Messages you schedule to send later show up here.", + "no_recipient": "(no recipient)", + "sending": "Sending now", + "to": "To: {name}", + "today_at": "Today, {time}", + "tomorrow_at": "Tomorrow, {time}", + "waiting": "Waiting for network", + "waiting_body": "Changes you made offline go out when you are back online." + }, + "search": { + "no_recent": "Your recent searches show up here.", + "placeholder": "Search mail…", + "recent": "Recent searches" + }, + "shortcuts": { + "calendar": "Calendar", + "compose": "Compose", + "contacts": "Contacts", + "files": "Files" + }, + "state": { + "error": "Couldn't show this widget", + "loading": "Loading…", + "open_app": "Open the app once if this stays empty.", + "open_to_load": "Open Bulwark to load this", + "signed_out": "Sign in to Bulwark" + }, + "storage": { + "no_limit": "No storage limit", + "title": "Storage", + "unavailable": "No quota information", + "used_of": "{used} of {total}" + }, + "tag": { + "empty": "No tags yet", + "empty_body": "Tags you add in the app show up here.", + "messages": "{count, plural, one {message} other {messages}}", + "total": "{count, plural, one {# message} other {# messages}}" + }, + "tasks": { + "all_done": "All done", + "empty": "No open tasks", + "mark_complete": "Mark as done", + "mark_incomplete": "Mark as not done", + "no_title": "(no title)", + "off": "Tasks are off", + "off_body": "Turn on tasks in the app's calendar settings", + "open_count": "{count} open", + "overdue": "Overdue", + "still_open": "{count} still open", + "title": "Tasks", + "today": "Today", + "tomorrow": "Tomorrow" + }, + "vacation": { + "active": "Active", + "inactive": "Inactive", + "subject": "Subject: {subject}", + "title": "Vacation Responder", + "unsupported": "Vacation replies aren't available", + "until": "Until {date}" + } } } diff --git a/package-lock.json b/package-lock.json index 0880af09..533444d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "postal-mime": "^3.0.0", "react": "19.1.0", "react-native": "0.81.5", + "react-native-android-widget": "0.22.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", "react-native-sse": "^1.2.1", @@ -9004,6 +9005,22 @@ } } }, + "node_modules/react-native-android-widget": { + "version": "0.22.1", + "resolved": "https://registry.npmjs.org/react-native-android-widget/-/react-native-android-widget-0.22.1.tgz", + "integrity": "sha512-BBMuqShRrxV23Z09Rn5zNHJaPegCnH3DhhZQgXbhw2GMh45Gez0RbwKTIWHftuW4nLrqWy6esz8XwecUZLiZKA==", + "license": "MIT", + "peerDependencies": { + "expo": ">=54.0.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "expo": { + "optional": true + } + } + }, "node_modules/react-native-is-edge-to-edge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz", diff --git a/package.json b/package.json index 5d9fb4e1..02c2662e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "bump": "node scripts/bump-version.mjs", "sync-locales": "node scripts/sync-locales.mjs", "i18n:harvest": "node scripts/harvest-rn-keys.mjs", - "i18n:check": "node scripts/harvest-rn-keys.mjs --check" + "i18n:check": "node scripts/harvest-rn-keys.mjs --check", + "widgets:sync": "node scripts/sync-android-widgets.mjs" }, "dependencies": { "@react-native-async-storage/async-storage": "2.2.0", @@ -45,6 +46,7 @@ "postal-mime": "^3.0.0", "react": "19.1.0", "react-native": "0.81.5", + "react-native-android-widget": "0.22.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", "react-native-sse": "^1.2.1", diff --git a/scripts/sync-android-widgets.mjs b/scripts/sync-android-widgets.mjs new file mode 100644 index 00000000..28678dc6 --- /dev/null +++ b/scripts/sync-android-widgets.mjs @@ -0,0 +1,193 @@ +// Generate the Android side of the home-screen widgets from +// src/widgets/catalog.json. Run from the RN repo root: +// node scripts/sync-android-widgets.mjs [--check] +// +// android/ is committed and hand-maintained (no `expo prebuild`), so the +// react-native-android-widget config plugin never runs for it. This script +// does the same job: one RNWidgetProvider subclass per widget (the library +// uses the class name as the widget name), a provider XML per widget, the +// picker labels and descriptions, and the manifest receivers between the +// BEGIN/END WIDGETS markers. With --check it only reports drift and exits 1. +import { readFile, writeFile, readdir, unlink, mkdir } from 'node:fs/promises'; +import { existsSync } from 'node:fs'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..'); +const CATALOG = join(ROOT, 'src', 'widgets', 'catalog.json'); +const MAIN = join(ROOT, 'android', 'app', 'src', 'main'); +const PACKAGE = 'com.anonymous.bulwarkmobile'; +const KOTLIN = join(MAIN, 'java', ...PACKAGE.split('.'), 'widget', 'WidgetProviders.kt'); +const XML_DIR = join(MAIN, 'res', 'xml'); +const STRINGS = join(MAIN, 'res', 'values', 'widget_strings.xml'); +// Optional picker previews: src/widgets/previews/.png is copied to +// res/drawable-nodpi and set as the widget's previewImage. +const PREVIEWS = join(ROOT, 'src', 'widgets', 'previews'); +const PREVIEW_DIR = join(MAIN, 'res', 'drawable-nodpi'); +const MANIFEST = join(MAIN, 'AndroidManifest.xml'); +const CHECK_ONLY = process.argv.includes('--check'); + +// Android's sizing table: n cells is roughly 70n - 30 dp in either direction. +const dp = (cells) => 70 * cells - 30; +// Launchers refresh at most every 30 minutes; the app pushes fresher data +// itself whenever it has some (see src/widgets/sync.ts). +const UPDATE_PERIOD_MS = 30 * 60 * 1000; + +const escapeXml = (s) => + s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, "\\'"); +const resName = (name) => `widgetprovider_${name.toLowerCase()}`; + +const hasPreview = (w) => existsSync(join(PREVIEWS, `${w.name}.png`)); +const previewRes = (w) => `${w.name.toLowerCase()}_preview`; + +function providerXml(w) { + const preview = hasPreview(w) ? `\n android:previewImage="@drawable/${previewRes(w)}"` : ''; + return ` + + + +`; +} + +function kotlinFile(widgets) { + const classes = widgets.map((w) => `class ${w.name} : RNWidgetProvider()`).join('\n'); + return `// Generated by scripts/sync-android-widgets.mjs from src/widgets/catalog.json. +// react-native-android-widget names a widget after its provider class, so each +// widget needs its own subclass; the JS side (src/widgets/task-handler.tsx) +// renders by that name. +package ${PACKAGE}.widget + +import com.reactnativeandroidwidget.RNWidgetProvider + +${classes} +`; +} + +function stringsFile(widgets) { + const lines = widgets.flatMap((w) => [ + ` ${escapeXml(w.label)}`, + ` ${escapeXml(w.description)}`, + ]); + return ` + + +${lines.join('\n')} + +`; +} + +function manifestBlock(widgets) { + const receivers = widgets.map( + (w) => ` + + + + + + `, + ); + return ` + +${receivers.join('\n')} + `; +} + +function withManifestBlock(manifest, block) { + const begin = manifest.indexOf(' '; + const end = manifest.indexOf(endMarker); + if (begin !== -1 && end !== -1) { + return manifest.slice(0, begin) + block + manifest.slice(end + endMarker.length); + } + const close = manifest.lastIndexOf(' '); + if (close === -1) throw new Error('AndroidManifest.xml has no '); + return manifest.slice(0, close) + block + '\n' + manifest.slice(close); +} + +const { widgets } = JSON.parse(await readFile(CATALOG, 'utf8')); +const names = new Set(); +for (const w of widgets) { + if (!/^[A-Z][A-Za-z0-9]*Widget$/.test(w.name)) throw new Error(`Bad widget name ${w.name}`); + if (names.has(w.name)) throw new Error(`Duplicate widget name ${w.name}`); + names.add(w.name); +} + +const outputs = new Map(); +outputs.set(KOTLIN, kotlinFile(widgets)); +outputs.set(STRINGS, stringsFile(widgets)); +for (const w of widgets) outputs.set(join(XML_DIR, `${resName(w.name)}.xml`), providerXml(w)); +outputs.set(MANIFEST, withManifestBlock(await readFile(MANIFEST, 'utf8'), manifestBlock(widgets))); + +// Provider XML for widgets that were removed from the catalog. +const stale = existsSync(XML_DIR) + ? (await readdir(XML_DIR)) + .filter((f) => f.startsWith('widgetprovider_')) + .map((f) => join(XML_DIR, f)) + .filter((f) => !outputs.has(f)) + : []; + +// Preview images are binary; compare them byte for byte. +const binaryOutputs = new Map(); +for (const w of widgets) { + if (hasPreview(w)) binaryOutputs.set(join(PREVIEW_DIR, `${previewRes(w)}.png`), await readFile(join(PREVIEWS, `${w.name}.png`))); +} +const stalePreviews = existsSync(PREVIEW_DIR) + ? (await readdir(PREVIEW_DIR)) + .filter((f) => /widget_preview\.png$/.test(f)) + .map((f) => join(PREVIEW_DIR, f)) + .filter((f) => !binaryOutputs.has(f)) + : []; +stale.push(...stalePreviews); + +let drift = stale.length; +for (const [path, bytes] of binaryOutputs) { + const current = existsSync(path) ? await readFile(path) : null; + if (current && current.equals(bytes)) continue; + drift++; + if (CHECK_ONLY) { + console.log(`out of date: ${path}`); + } else { + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, bytes); + console.log(`wrote ${path}`); + } +} +// Checkouts with core.autocrlf have CRLF files: compare without line endings +// and write back in whatever the file already uses. +const lf = (text) => text.replace(/\r\n/g, '\n'); +for (const [path, content] of outputs) { + const current = existsSync(path) ? await readFile(path, 'utf8') : null; + if (current !== null && lf(current) === lf(content)) continue; + drift++; + if (CHECK_ONLY) { + console.log(`out of date: ${path}`); + } else { + await mkdir(dirname(path), { recursive: true }); + await writeFile(path, current?.includes('\r\n') ? lf(content).replace(/\n/g, '\r\n') : lf(content)); + console.log(`wrote ${path}`); + } +} +for (const path of stale) { + if (CHECK_ONLY) console.log(`stale: ${path}`); + else { + await unlink(path); + console.log(`removed ${path}`); + } +} + +if (CHECK_ONLY && drift > 0) { + console.error(`${drift} widget file(s) out of date; run node scripts/sync-android-widgets.mjs`); + process.exit(1); +} +console.log(drift === 0 ? 'Android widget files are up to date.' : `${widgets.length} widgets synced.`); diff --git a/src/lib/reply-compose.ts b/src/lib/reply-compose.ts new file mode 100644 index 00000000..dc762589 --- /dev/null +++ b/src/lib/reply-compose.ts @@ -0,0 +1,50 @@ +import type { Email } from '../api/types'; +import type { RootStackParamList } from '../navigation/types'; +import { pickEmailBody, plainTextBody } from './email-body'; + +/** + * Composer params for a reply / reply-all / forward of `source`. Shared by + * the thread viewer and the `mail/message/?action=reply` deep link (the + * triage widget). Null when there is nobody to reply to. + */ +export function replyComposeParams( + mode: 'reply' | 'replyAll' | 'forward', + source: Email, + ownerAccountId?: string, +): NonNullable | null { + const from = source.from?.[0]; + if (!from && mode !== 'forward') return null; + // Quote the HTML part when there is one so layout and inline images + // survive (#163). RFC 8621 §4.1.4: an HTML-only message exposes the same + // part in `textBody` and `htmlBody`, so the text part is only a real + // alternative when its partId differs - otherwise the composer would be + // handed raw HTML source (#649, native #46). + const picked = pickEmailBody(source); + const quoteHtml = picked.html ?? undefined; + const body = !quoteHtml || picked.text ? plainTextBody(source) : undefined; + return { + mode, + replyTo: { + from: from ?? { email: '' }, + to: source.to, + cc: source.cc, + // RFC 5322: a reply goes to Reply-To when the sender set one. + replyToAddresses: source.replyTo, + subject: source.subject ?? '', + body, + htmlBody: quoteHtml, + receivedAt: source.receivedAt, + sentAt: source.sentAt, + // Threading needs the RFC Message-ID, never the JMAP object id (#234). + messageId: source.messageId ?? undefined, + references: source.references ?? undefined, + // Forward carries the original attachments as blob refs; cid-embedded + // inline images are already part of the quoted HTML. + attachments: mode === 'forward' + ? (source.attachments ?? []).filter((a) => !(a.disposition === 'inline' && a.cid)) + : undefined, + originalEmailId: source.id, + jmapAccountId: ownerAccountId, + }, + }; +} diff --git a/src/navigation/__tests__/linking.test.ts b/src/navigation/__tests__/linking.test.ts index 07a64684..a701f962 100644 --- a/src/navigation/__tests__/linking.test.ts +++ b/src/navigation/__tests__/linking.test.ts @@ -2,6 +2,7 @@ import { describe, it, expect, vi } from 'vitest'; import { handleDeepLink, parseDeepLink, shareToDeepLink } from '../linking'; import { usePendingSettingsTab } from '../pending-settings-tab'; import { usePendingCalendarOpen } from '../pending-calendar-open'; +import { usePendingMailSearch } from '../pending-mail-search'; describe('parseDeepLink', () => { it('parses app-scheme mail links', () => { @@ -75,6 +76,18 @@ describe('parseDeepLink', () => { }); }); + it('parses the widget links: reply, draft, unified views, scheduled and search', () => { + expect(parseDeepLink('bulwarkmobile://mail/message/M1?account=a%40b&jmapAccount=team&action=reply')).toEqual({ + kind: 'message', emailId: 'M1', accountId: 'a@b', jmapAccountId: 'team', action: 'reply', + }); + expect(parseDeepLink('bulwarkmobile://mail/draft/D1')).toEqual({ kind: 'draft', emailId: 'D1', accountId: undefined }); + expect(parseDeepLink('bulwarkmobile://mail/unified?view=starred')).toEqual({ kind: 'unified', view: 'starred' }); + expect(parseDeepLink('bulwarkmobile://mail/unified?role=drafts&view=bogus')).toEqual({ kind: 'unified', role: 'drafts' }); + expect(parseDeepLink('bulwarkmobile://mail/scheduled')).toEqual({ kind: 'scheduled' }); + expect(parseDeepLink('bulwarkmobile://mail/search?q=from%3Aada')).toEqual({ kind: 'search', query: 'from:ada' }); + expect(parseDeepLink('bulwarkmobile://mail/search')).toEqual({ kind: 'search', query: '' }); + }); + it('rejects unknown links', () => { expect(parseDeepLink('bulwarkmobile://whatever')).toBeNull(); expect(parseDeepLink('garbage')).toBeNull(); @@ -166,6 +179,40 @@ describe('handleDeepLink', () => { })); }); + it('opens a group-mailbox message in its JMAP account and hands replies to the composer opener', async () => { + const navigation = nav(); + const resolveThreadId = vi.fn(async () => 'T1'); + await handleDeepLink( + { kind: 'message', emailId: 'M1', jmapAccountId: 'team' }, + { navigation: navigation as never, resolveThreadId }, + ); + expect(resolveThreadId).toHaveBeenCalledWith('M1', 'team'); + expect(navigation.navigate).toHaveBeenCalledWith('EmailThread', { emailId: 'M1', threadId: 'T1', jmapAccountId: 'team' }); + + const openReply = vi.fn(async () => true); + const ok = await handleDeepLink( + { kind: 'message', emailId: 'M2', action: 'reply' }, + { navigation: nav() as never, resolveThreadId, openReply }, + ); + expect(ok).toBe(true); + expect(openReply).toHaveBeenCalledWith('M2', undefined); + }); + + it('opens drafts, unified views, scheduled mail and searches', async () => { + const openDraft = vi.fn(async () => true); + const navigation = nav(); + const deps = { navigation: navigation as never, resolveThreadId: async () => 'T', openDraft }; + expect(await handleDeepLink({ kind: 'draft', emailId: 'D1' }, deps)).toBe(true); + expect(openDraft).toHaveBeenCalledWith('D1', undefined); + await handleDeepLink({ kind: 'unified', view: 'starred' }, deps); + expect(navigation.navigate).toHaveBeenCalledWith('UnifiedInbox', { view: 'starred' }); + await handleDeepLink({ kind: 'scheduled' }, deps); + expect(navigation.navigate).toHaveBeenCalledWith('Scheduled'); + await handleDeepLink({ kind: 'search', query: 'from:ada' }, deps); + expect(usePendingMailSearch.getState().consume()).toBe('from:ada'); + expect(navigation.navigate).toHaveBeenCalledWith('MainTabs', { screen: 'Mail' }); + }); + it('refuses when the linked account is not signed in', async () => { const navigation = nav(); const ok = await handleDeepLink( diff --git a/src/navigation/linking.ts b/src/navigation/linking.ts index 14a8bf48..9149f94d 100644 --- a/src/navigation/linking.ts +++ b/src/navigation/linking.ts @@ -8,13 +8,25 @@ import { parseMailtoUrl } from '../lib/mailto'; import type { RootStackParamList } from './types'; import { setPendingSettingsTab } from './pending-settings-tab'; import { setPendingCalendarOpen } from './pending-calendar-open'; +import { setPendingMailSearch } from './pending-mail-search'; export const APP_SCHEME = 'bulwarkmobile'; +const UNIFIED_ROLES = ['inbox', 'sent', 'drafts', 'junk', 'archive', 'trash'] as const; +const UNIFIED_VIEWS = ['all', 'unread', 'starred'] as const; +type UnifiedRole = typeof UNIFIED_ROLES[number]; +type UnifiedView = typeof UNIFIED_VIEWS[number]; + export type DeepLink = - | { kind: 'message'; emailId: string; accountId?: string } + // `jmapAccountId`: the JMAP account owning a message in a group/shared + // mailbox (`?jmapAccount=`); `action: 'reply'` opens a reply to it. + | { kind: 'message'; emailId: string; accountId?: string; jmapAccountId?: string; action?: 'reply' } + | { kind: 'draft'; emailId: string; accountId?: string; jmapAccountId?: string } | { kind: 'thread'; threadId: string; accountId?: string } | { kind: 'folder'; ref: string; accountId?: string } + | { kind: 'unified'; role?: UnifiedRole; view?: UnifiedView } + | { kind: 'scheduled' } + | { kind: 'search'; query: string } // `jmapAccountId`: the JMAP account owning the event (`?account=`), for // one on a calendar shared with the user. | { kind: 'calendar'; eventId?: string; date?: string; jmapAccountId?: string } @@ -82,7 +94,30 @@ export function parseDeepLink(url: string): DeepLink | null { switch (area) { case 'mail': { - if (kind === 'message' && value) return { kind: 'message', emailId: decodeSegment(value), accountId }; + const jmapAccountId = search.get('jmapAccount') ?? undefined; + if (kind === 'message' && value) { + return { + kind: 'message', + emailId: decodeSegment(value), + accountId, + ...(jmapAccountId ? { jmapAccountId } : {}), + ...(search.get('action') === 'reply' ? { action: 'reply' as const } : {}), + }; + } + if (kind === 'draft' && value) { + return { kind: 'draft', emailId: decodeSegment(value), accountId, ...(jmapAccountId ? { jmapAccountId } : {}) }; + } + if (kind === 'unified') { + const role = search.get('role'); + const view = search.get('view'); + return { + kind: 'unified', + ...(role && (UNIFIED_ROLES as readonly string[]).includes(role) ? { role: role as UnifiedRole } : {}), + ...(view && (UNIFIED_VIEWS as readonly string[]).includes(view) ? { view: view as UnifiedView } : {}), + }; + } + if (kind === 'scheduled') return { kind: 'scheduled' }; + if (kind === 'search') return { kind: 'search', query: search.get('q') ?? '' }; if (kind === 'thread' && value) return { kind: 'thread', threadId: decodeSegment(value), accountId }; if (kind === 'folder' && value) return { kind: 'folder', ref: decodeSegment(value), accountId }; // Legacy `?email=` from the webmail's older service worker. @@ -131,8 +166,13 @@ export function parseDeepLink(url: string): DeepLink | null { export interface DeepLinkNavigator { navigation: NavigationContainerRefWithCurrent; // Resolve a message id to its thread (EmailThread needs both). Returns - // null when the message cannot be loaded. - resolveThreadId: (emailId: string) => Promise; + // null when the message cannot be loaded. `jmapAccountId` names the + // group/shared account holding it, when it is not the user's own. + resolveThreadId: (emailId: string, jmapAccountId?: string) => Promise; + // Open the composer on a reply to / on a server draft of this message. + // Resolve false when the message cannot be loaded. + openReply?: (emailId: string, jmapAccountId?: string) => Promise; + openDraft?: (emailId: string, jmapAccountId?: string) => Promise; // Switch to the account a permalink names (`?account=`); resolves false // when that account is not signed in on this device. switchAccount?: (accountId: string) => Promise; @@ -149,11 +189,33 @@ export async function handleDeepLink(link: DeepLink, nav: DeepLinkNavigator): Pr switch (link.kind) { case 'message': { - const threadId = await nav.resolveThreadId(link.emailId); + if (link.action === 'reply' && nav.openReply) { + return nav.openReply(link.emailId, link.jmapAccountId); + } + const threadId = await nav.resolveThreadId(link.emailId, link.jmapAccountId); if (!threadId) return false; - navigation.navigate('EmailThread', { emailId: link.emailId, threadId }); + navigation.navigate('EmailThread', { + emailId: link.emailId, + threadId, + ...(link.jmapAccountId ? { jmapAccountId: link.jmapAccountId } : {}), + }); return true; } + case 'draft': + return nav.openDraft ? nav.openDraft(link.emailId, link.jmapAccountId) : false; + case 'unified': + navigation.navigate('UnifiedInbox', { + ...(link.role ? { role: link.role } : {}), + ...(link.view ? { view: link.view } : {}), + }); + return true; + case 'scheduled': + navigation.navigate('Scheduled'); + return true; + case 'search': + setPendingMailSearch(link.query); + navigation.navigate('MainTabs', { screen: 'Mail' } as never); + return true; case 'thread': // The reader keys on the message; without one, open the list. navigation.navigate('MainTabs', { screen: 'Mail' } as never); diff --git a/src/navigation/pending-mail-search.ts b/src/navigation/pending-mail-search.ts new file mode 100644 index 00000000..1f5677ae --- /dev/null +++ b/src/navigation/pending-mail-search.ts @@ -0,0 +1,25 @@ +import { create } from 'zustand'; + +// A `mail/search` deep link (the search widget) arrives before the mail list +// is mounted, or while it shows a folder. The query is parked here and +// consumed by EmailListScreen, which runs it like a submitted search; an +// empty query just opens the search field. +interface PendingMailSearchState { + query: string | null; + set: (query: string | null) => void; + consume: () => string | null; +} + +export const usePendingMailSearch = create((set, get) => ({ + query: null, + set: (query) => set({ query }), + consume: () => { + const query = get().query; + if (query !== null) set({ query: null }); + return query; + }, +})); + +export function setPendingMailSearch(query: string | null): void { + usePendingMailSearch.getState().set(query); +} diff --git a/src/screens/EmailListScreen.tsx b/src/screens/EmailListScreen.tsx index 969a31f1..9a399042 100644 --- a/src/screens/EmailListScreen.tsx +++ b/src/screens/EmailListScreen.tsx @@ -49,6 +49,7 @@ import { isPermanentDelete, confirmPermanentDelete } from '../lib/delete-confirm import { draftContextFromEmail, isDraftEmail } from '../lib/draft-context'; import { getFullEmail, emptyMailbox as apiEmptyMailbox } from '../api/email'; import type { RootStackParamList } from '../navigation/types'; +import { usePendingMailSearch } from '../navigation/pending-mail-search'; import type { Attachment, Email } from '../api/types'; function getSenderName(email: Email, unknownLabel: string): string { @@ -824,6 +825,17 @@ export default function EmailListScreen({ onEmailPress, onComposePress }: EmailL if (value.trim()) addRecentSearch(value); setSearchFocused(false); }, [setSearchQuery, addRecentSearch]); + // A search handed over by a deep link (the search widget): run it, or with + // no query just put the cursor in the search field. + const searchInputRef = React.useRef(null); + const pendingSearch = usePendingMailSearch((s) => s.query); + React.useEffect(() => { + if (pendingSearch === null) return; + const query = usePendingMailSearch.getState().consume(); + if (query === null) return; + if (query.trim()) submitSearch(query); + else setTimeout(() => searchInputRef.current?.focus(), 300); + }, [pendingSearch, submitSearch]); // People whose name/address matches what is being typed (#845); picking // one turns the search into a `from:` filter like the webmail. const contactSuggestions = React.useMemo(() => { @@ -1120,6 +1132,7 @@ export default function EmailListScreen({ onEmailPress, onComposePress }: EmailL { const source = target ?? email; if (!source) return; - const from = source.from?.[0]; - if (!from && mode !== 'forward') return; - // Quote the HTML part when there is one so layout and inline images - // survive (#163). RFC 8621 §4.1.4: an HTML-only message exposes the same - // part in `textBody` and `htmlBody`, so the text part is only a real - // alternative when its partId differs - otherwise the composer would be - // handed raw HTML source (#649, native #46). - const picked = pickEmailBody(source); - const quoteHtml = picked.html ?? undefined; - const body = !quoteHtml || picked.text ? plainTextBody(source) : undefined; - navigation.navigate('Compose', { - mode, - replyTo: { - from: from ?? { email: '' }, - to: source.to, - cc: source.cc, - // RFC 5322: a reply goes to Reply-To when the sender set one. - replyToAddresses: source.replyTo, - subject: source.subject ?? '', - body, - htmlBody: quoteHtml, - receivedAt: source.receivedAt, - sentAt: source.sentAt, - // Threading needs the RFC Message-ID, never the JMAP object id (#234). - messageId: source.messageId ?? undefined, - references: source.references ?? undefined, - // Forward carries the original attachments as blob refs; cid-embedded - // inline images are already part of the quoted HTML. - attachments: mode === 'forward' - ? (source.attachments ?? []).filter((a) => !(a.disposition === 'inline' && a.cid)) - : undefined, - originalEmailId: source.id, - jmapAccountId: ownerAccountId, - }, - }); + const params = replyComposeParams(mode, source, ownerAccountId); + if (params) navigation.navigate('Compose', params); }, [email, navigation, ownerAccountId]); // Forward the raw message as a message/rfc822 attachment (webmail 1.8.1). diff --git a/src/stores/email-store.ts b/src/stores/email-store.ts index 47d51270..f1009c4f 100644 --- a/src/stores/email-store.ts +++ b/src/stores/email-store.ts @@ -552,8 +552,11 @@ export function spansAccounts(state: Pick */ export function spannedAccounts(mailboxes: Mailbox[]): Array { const out: Array = [undefined]; + // The drawer renders the persisted mailboxes before the session is back, + // and `accountId` throws while the client is not connected. + const own = jmapClient.isConnected ? jmapClient.accountId : undefined; for (const m of mailboxes) { - if (m.isShared && m.accountId && m.accountId !== jmapClient.accountId && !out.includes(m.accountId)) { + if (m.isShared && m.accountId && m.accountId !== own && !out.includes(m.accountId)) { out.push(m.accountId); } } diff --git a/src/widgets/__tests__/derive.test.ts b/src/widgets/__tests__/derive.test.ts new file mode 100644 index 00000000..154ff5cd --- /dev/null +++ b/src/widgets/__tests__/derive.test.ts @@ -0,0 +1,117 @@ +import { describe, expect, it } from 'vitest'; +import { + agendaDays, + busyBlocks, + countdownTarget, + dueKind, + eventsOnDay, + freeTime, + monthGrid, + nextEvent, + sortTasks, + startOfWeek, +} from '../derive'; +import type { EventItem, TaskItem } from '../snapshot'; + +// Monday 28 September 2026, 09:05 local time. +const NOW = new Date(2026, 8, 28, 9, 5).getTime(); +const at = (day: number, h: number, m = 0) => new Date(2026, 8, day, h, m).getTime(); + +function ev(id: string, start: number, end: number, extra: Partial = {}): EventItem { + return { + id, + serverId: id, + title: id, + start, + end, + allDay: false, + color: '#22c55e', + calendarName: 'Work', + participants: [], + ...extra, + }; +} + +const standup = ev('standup', at(28, 9, 30), at(28, 10)); +const dentist = ev('dentist', at(29, 10), at(29, 11), { color: '#3b82f6' }); +const lunch = ev('lunch', at(30, 12, 30), at(30, 13, 30)); +const party = ev('party', new Date(2026, 9, 1).getTime(), new Date(2026, 9, 2).getTime(), { allDay: true, color: '#3b82f6' }); +const review = ev('review', new Date(2026, 9, 1, 14).getTime(), new Date(2026, 9, 1, 16).getTime()); +const trip = ev('trip', new Date(2026, 9, 4).getTime(), new Date(2026, 9, 5).getTime(), { allDay: true, color: '#3b82f6' }); +const earlier = ev('earlier', at(28, 7), at(28, 8)); +const EVENTS = [earlier, standup, dentist, lunch, party, review, trip]; + +describe('calendar views', () => { + it('finds the next timed event and whether it is running', () => { + expect(nextEvent(EVENTS, NOW)?.event.id).toBe('standup'); + expect(nextEvent(EVENTS, NOW)?.running).toBe(false); + expect(nextEvent(EVENTS, at(28, 9, 45))?.running).toBe(true); + }); + + it('lists a day with all-day events first', () => { + const thursday = new Date(2026, 9, 1).getTime(); + expect(eventsOnDay(EVENTS, thursday).map((e) => e.id)).toEqual(['party', 'review']); + }); + + it('drops finished events from today in the agenda', () => { + const days = agendaDays(EVENTS, NOW, 3); + expect(days.map((d) => d.events.map((e) => e.id))).toEqual([['standup'], ['dentist'], ['lunch']]); + }); + + it('starts the week on the configured day', () => { + expect(new Date(startOfWeek(NOW, 1)).getDate()).toBe(28); + expect(new Date(startOfWeek(NOW, 0)).getDate()).toBe(27); + }); + + it('builds the month as whole weeks with dots and today', () => { + const weeks = monthGrid(EVENTS, NOW, 1); + expect(weeks).toHaveLength(5); + expect(weeks[0][0].day).toBe(31); + expect(weeks[0][0].inMonth).toBe(false); + const today = weeks.flat().find((c) => c.isToday); + expect(today?.day).toBe(28); + expect(today?.dots).toEqual(['#22c55e']); + }); + + it('places busy blocks inside working hours', () => { + const [block] = busyBlocks(EVENTS, new Date(2026, 8, 29).getTime()); + expect(block.from).toBeCloseTo(0.2); + expect(block.to).toBeCloseTo(0.3); + }); + + it('works out free time and the longest gap left today', () => { + const free = freeTime(EVENTS, NOW); + expect(free.busyNow).toBe(false); + expect(free.next?.id).toBe('standup'); + expect(free.longestGap).toEqual({ from: at(28, 10), to: at(28, 18) }); + }); + + it('counts down to the next all-day or multi-day event', () => { + expect(countdownTarget(EVENTS, NOW)?.id).toBe('party'); + }); +}); + +describe('tasks', () => { + const task = (id: string, due: number | undefined, done = false, dueHasTime = false): TaskItem => ({ + id, serverId: id, title: id, due, dueHasTime, done, calendarName: 'Personal', color: '#3b82f6', + }); + + it('classifies due dates', () => { + expect(dueKind(task('a', at(26, 0)), NOW)).toBe('overdue'); + expect(dueKind(task('b', at(28, 0)), NOW)).toBe('today'); + expect(dueKind(task('c', at(28, 8), false, true), NOW)).toBe('overdue'); + expect(dueKind(task('d', at(29, 0)), NOW)).toBe('tomorrow'); + expect(dueKind(task('e', undefined), NOW)).toBe('none'); + }); + + it('puts overdue first and done last', () => { + const sorted = sortTasks([ + task('done', at(20, 0), true), + task('later', new Date(2026, 9, 3).getTime()), + task('none', undefined), + task('today', at(28, 0)), + task('overdue', at(26, 0)), + ], NOW); + expect(sorted.map((t) => t.id)).toEqual(['overdue', 'today', 'later', 'none', 'done']); + }); +}); diff --git a/src/widgets/__tests__/jmap.test.ts b/src/widgets/__tests__/jmap.test.ts new file mode 100644 index 00000000..132b14b2 --- /dev/null +++ b/src/widgets/__tests__/jmap.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from 'vitest'; +import type { Email, Mailbox } from '../../api/types'; +import { folderCounts, rankPeople, toMailItem } from '../jmap'; + +function email(id: string, from: string, name: string, seen: boolean, receivedAt = '2026-09-28T08:00:00Z'): Email { + return { + id, + threadId: `t-${id}`, + mailboxIds: { inbox: true }, + keywords: seen ? { $seen: true } : {}, + receivedAt, + from: [{ email: from, name }], + subject: `Subject ${id}`, + preview: ' line one\n line two ', + hasAttachment: false, + size: 1, + } as Email; +} + +describe('toMailItem', () => { + it('maps a list email to what the widgets draw', () => { + const item = toMailItem(email('1', 'sofia.russo@example.com', 'Sofia Russo', false), 'acc', { threadSize: 2 }); + expect(item).toMatchObject({ + id: '1', + accountId: 'acc', + fromName: 'Sofia Russo', + initials: 'SR', + unread: true, + threadSize: 2, + preview: 'line one line two', + }); + // Same hue as the webmail's Avatar for this name. + expect(item.color).toBe('#6826d9'); + }); +}); + +describe('rankPeople', () => { + it('ranks senders by volume, skips the user and no-reply senders, and counts unread', () => { + const people = rankPeople([ + email('1', 'ada@example.com', 'Ada', false), + email('2', 'ada@example.com', 'Ada', true), + email('3', 'bob@example.com', 'Bob', false), + email('4', 'me@example.com', 'Me', false), + email('5', 'noreply@shop.example', 'Shop', false), + ], ['Me@example.com']); + expect(people.map((p) => [p.email, p.unread])).toEqual([['ada@example.com', 1], ['bob@example.com', 1]]); + }); +}); + +describe('folderCounts', () => { + it('orders the roles like the sidebar and finds junk by role or name', () => { + const boxes = [ + { id: 'j', name: 'Spam', role: 'junk', unreadEmails: 3, totalEmails: 3 }, + { id: 'i', name: 'Inbox', role: 'inbox', unreadEmails: 10, totalEmails: 23 }, + { id: 'd', name: 'Drafts', role: 'drafts', unreadEmails: 0, totalEmails: 2 }, + ] as Mailbox[]; + expect(folderCounts(boxes).map((f) => [f.role, f.unread, f.total])).toEqual([ + ['inbox', 10, 23], + ['drafts', 0, 2], + ['junk', 3, 3], + ]); + }); +}); diff --git a/src/widgets/__tests__/layouts.test.ts b/src/widgets/__tests__/layouts.test.ts new file mode 100644 index 00000000..c7b24767 --- /dev/null +++ b/src/widgets/__tests__/layouts.test.ts @@ -0,0 +1,125 @@ +import { describe, expect, it, vi } from 'vitest'; + +// The package entry pulls in the native module, so hand the layouts the plain +// widget components and run them through the library's own tree builder: it +// is what throws on the device when a layout returns null, puts null in a +// mapped list or uses a Fragment. Colours are checked on the way in, because +// the library quietly turns any it cannot parse (a name, `transparent`) into +// white. +vi.mock('react-native-android-widget', async () => { + const COLOR = /^(#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})|rgba\(.*\))$/i; + const checkColors = (widget: string, style: Record = {}) => { + for (const [key, value] of Object.entries(style)) { + if (/colou?r$/i.test(key) && !(typeof value === 'string' && COLOR.test(value))) { + throw new Error(`${widget} ${key}: ${JSON.stringify(value)} is not a colour the widget renderer reads`); + } + } + }; + type Widget = { __name__: string; convertProps: (props: { style?: Record }) => unknown }; + const checked = (module: Record) => { + for (const value of Object.values(module)) { + if (typeof value !== 'function' || !('__name__' in value)) continue; + const widget = value as unknown as Widget; + const convert = widget.convertProps; + widget.convertProps = (props) => { + checkColors(widget.__name__, props.style); + return convert(props); + }; + } + return module; + }; + // The compiled SvgWidget requires react-native (past the stub) to resolve + // bundled images; the widgets only draw inline SVG strings, so rebuild it + // from the library's own style and click converters. + const { convertCommonStyle } = await import('react-native-android-widget/lib/commonjs/widgets/utils/style.utils' as string); + const { convertClickAction } = await import('react-native-android-widget/lib/commonjs/widgets/utils/click-action' as string); + function SvgWidget() { + return null; + } + SvgWidget.__name__ = 'SvgWidget'; + SvgWidget.convertProps = (props: { style?: object; svg: unknown }) => { + if (typeof props.svg !== 'string') throw new Error('widgets draw inline SVG strings only'); + return { ...convertCommonStyle(props.style ?? {}), ...convertClickAction(props), svgString: props.svg }; + }; + return { + ...checked(await import('react-native-android-widget/lib/commonjs/widgets/FlexWidget' as string)), + ...checked(await import('react-native-android-widget/lib/commonjs/widgets/ListWidget' as string)), + ...checked(await import('react-native-android-widget/lib/commonjs/widgets/OverlapWidget' as string)), + ...checked(await import('react-native-android-widget/lib/commonjs/widgets/TextWidget' as string)), + ...checked({ SvgWidget }), + getWidgetInfo: vi.fn(async () => []), + requestWidgetUpdate: vi.fn(async () => undefined), + }; +}); + +import { createElement } from 'react'; +import { FlexWidget } from 'react-native-android-widget'; +import catalog from '../catalog.json'; +import { renderFor } from '../render'; +import { PREVIEW_NOW, sampleSnapshot } from '../sample-snapshot'; +import { emptySnapshot, type WidgetSnapshot } from '../snapshot'; + +const { buildWidgetTree } = (await import( + 'react-native-android-widget/lib/commonjs/api/build-widget-tree' as string +)) as { buildWidgetTree: (tree: unknown) => { type: string } }; + +// Launcher cells to dp, roughly what a phone launcher reports. +const dp = (cells: number) => cells * 90 - 15; + +function build(name: string, s: WidgetSnapshot, cols: number, rows: number, local = {}) { + const trees = renderFor(name, s, { width: dp(cols), height: dp(rows), widgetId: 1 }, local, PREVIEW_NOW); + for (const tree of 'light' in trees ? [trees.light, trees.dark] : [trees]) { + expect(buildWidgetTree(tree).type).toBeTruthy(); + } +} + +const sizes = (w: (typeof catalog.widgets)[number]) => [ + [w.minCols, w.minRows], + [w.cols, w.rows], + [4, 4], + [5, 5], +]; + +describe('widget layouts', () => { + it('uses a builder that rejects what the device rejects', () => { + const Nothing = () => null; + expect(() => buildWidgetTree(createElement(Nothing))).toThrow(); + expect(() => buildWidgetTree(createElement(FlexWidget, null, createElement(FlexWidget), [null]))).toThrow(); + expect(() => buildWidgetTree(createElement(FlexWidget, { style: { backgroundColor: 'teal' } } as never))).toThrow(); + }); + + const sample = { ...sampleSnapshot(PREVIEW_NOW), theme: 'system' as const }; + const empty: WidgetSnapshot = { + ...emptySnapshot(), + generatedAt: PREVIEW_NOW, + appDataAt: PREVIEW_NOW, + signedIn: true, + theme: 'system', + accounts: [{ id: 'a', label: 'Work', color: '#2563eb', unread: 0 }], + activeAccountId: 'a', + calendar: { supported: true, events: [], invitations: [], birthdays: [] }, + tasks: { supported: true, items: [] }, + files: { supported: true, items: [] }, + }; + + it.each(catalog.widgets.map((w) => [w.name, w] as const))('%s builds with demo data at every size', (name, w) => { + for (const [cols, rows] of sizes(w)) build(name, sample, cols, rows); + }); + + it.each(catalog.widgets.map((w) => [w.name, w] as const))('%s builds with nothing to show', (name, w) => { + for (const [cols, rows] of sizes(w)) build(name, empty, cols, rows); + }); + + it('builds the signed-out, loading and not-loaded-yet states', () => { + for (const w of catalog.widgets) { + build(w.name, { ...emptySnapshot(), generatedAt: PREVIEW_NOW }, w.cols, w.rows); + build(w.name, emptySnapshot(), w.cols, w.rows); + build(w.name, { ...sample, appDataAt: 0 }, w.cols, w.rows); + } + }); + + it('builds the triage card on a message that is gone', () => { + build('TriageWidget', sample, 4, 2, { triageId: 'no-such-message' }); + build('TriageWidget', { ...sample, mail: { ...sample.mail, inbox: [] } }, 4, 2, { triageId: 'm1' }); + }); +}); diff --git a/src/widgets/actions.ts b/src/widgets/actions.ts new file mode 100644 index 00000000..ec506ddc --- /dev/null +++ b/src/widgets/actions.ts @@ -0,0 +1,163 @@ +// Buttons that act without opening the app: archive / delete / next on the +// triage card, RSVP on the invitation card, ticking a task. Each one updates +// the stored snapshot first so every widget redraws at once, then makes the +// JMAP call, then refreshes from the server; a failed call is undone by that +// refresh. + +import { jmapClient } from '../api/jmap-client'; +import type { JMAPClient } from '../api/jmap-client'; +import { noteLocalChange, refreshSnapshot, singletonServes } from './build'; +import { markRead, moveTo, openClient, rsvp, setTaskDone } from './jmap'; +import { loadLocal, saveLocal } from './local-state'; +import { updateAllWidgets } from './render'; +import { emptySnapshot, loadSnapshot, saveSnapshot, type MailItem, type WidgetSnapshot } from './snapshot'; + +async function clientFor(registryAccountId: string | null | undefined): Promise { + if (!registryAccountId) return null; + if (singletonServes(registryAccountId)) return jmapClient; + return openClient(registryAccountId); +} + +async function applyLocally(mutate: (s: WidgetSnapshot) => void): Promise { + noteLocalChange(); + const s = (await loadSnapshot()) ?? emptySnapshot(); + mutate(s); + await saveSnapshot(s); + await updateAllWidgets(s); + return s; +} + +function withoutMessage(s: WidgetSnapshot, id: string): void { + const removed = s.mail.inbox.find((m) => m.id === id); + const drop = (list: MailItem[]) => list.filter((m) => m.id !== id); + s.mail.inbox = drop(s.mail.inbox); + s.mail.unified = drop(s.mail.unified); + s.mail.starred = drop(s.mail.starred); + const inbox = s.mail.folders.find((f) => f.role === 'inbox'); + if (inbox && removed) { + inbox.total = Math.max(0, inbox.total - 1); + if (removed.unread) inbox.unread = Math.max(0, inbox.unread - 1); + } +} + +async function afterServerCall(ok: boolean): Promise { + if (!ok) console.warn('[widgets] action was not applied on the server'); + const fresh = await refreshSnapshot({ after: 'change' }); + await updateAllWidgets(fresh); +} + +type Data = Record; +const str = (v: unknown): string | undefined => (typeof v === 'string' && v ? v : undefined); + +export async function handleWidgetAction(name: string, data: Data, widgetId: number): Promise { + switch (name) { + case 'refresh': + await afterServerCall(true); + return; + + case 'triageNext': { + const s = (await loadSnapshot()) ?? emptySnapshot(); + const unread = s.mail.inbox.filter((m) => m.unread); + if (unread.length === 0) return; + const index = unread.findIndex((m) => m.id === data.id); + const next = unread[(index + 1) % unread.length]; + const local = await loadLocal(widgetId); + await saveLocal(widgetId, { ...local, triageId: next.id }); + await updateAllWidgets(s); + return; + } + + case 'archive': + case 'trash': + case 'markRead': { + const id = str(data.id); + if (!id) return; + if (name === 'markRead') { + await applyLocally((s) => { + const hit = s.mail.inbox.find((m) => m.id === id); + const inbox = s.mail.folders.find((f) => f.role === 'inbox'); + if (hit?.unread && inbox) inbox.unread = Math.max(0, inbox.unread - 1); + for (const list of [s.mail.inbox, s.mail.unified, s.mail.starred]) { + for (const m of list) if (m.id === id) m.unread = false; + } + }); + } else { + // The triage card moves on to the next unread message, not back to + // the first one. + const before = (await loadSnapshot()) ?? emptySnapshot(); + const unread = before.mail.inbox.filter((m) => m.unread); + const index = unread.findIndex((m) => m.id === id); + const next = index >= 0 ? unread[index + 1] ?? unread[index - 1] : undefined; + if (typeof data.widgetId === 'number') { + await saveLocal(data.widgetId, { ...(await loadLocal(data.widgetId)), triageId: next?.id ?? null }); + } + await applyLocally((s) => withoutMessage(s, id)); + } + let ok = false; + try { + const client = await clientFor(str(data.accountId)); + if (client) { + ok = name === 'markRead' + ? await markRead(client, id, str(data.jmapAccountId)) + : await moveTo(client, id, name, str(data.jmapAccountId)); + } + } catch (err) { + console.warn(`[widgets] ${name} failed`, err); + } + await afterServerCall(ok); + return; + } + + case 'rsvp': { + const id = str(data.id); + const status = data.status; + if (!id || (status !== 'accepted' && status !== 'tentative' && status !== 'declined')) return; + const before = (await loadSnapshot()) ?? emptySnapshot(); + const invitation = before.calendar.invitations.find((i) => i.id === id); + if (!invitation) return; + await applyLocally((s) => { + s.calendar.invitations = s.calendar.invitations.filter((i) => i.id !== id); + for (const e of s.calendar.events) if (e.serverId === invitation.serverId) e.myStatus = status; + }); + let ok = false; + try { + const client = await clientFor(before.activeAccountId); + if (client) ok = await rsvp(client, invitation.serverId, invitation.participantId, status, invitation.jmapAccountId); + } catch (err) { + console.warn('[widgets] rsvp failed', err); + } + await afterServerCall(ok); + return; + } + + case 'toggleTask': { + const id = str(data.id); + if (!id) return; + const before = (await loadSnapshot()) ?? emptySnapshot(); + const task = before.tasks.items.find((t) => t.id === id); + if (!task) return; + const done = !task.done; + await applyLocally((s) => { + for (const t of s.tasks.items) if (t.id === id) t.done = done; + }); + let ok = false; + try { + const client = await clientFor(before.activeAccountId); + if (client) ok = await setTaskDone(client, task.serverId, done, task.jmapAccountId); + // In the live app the snapshot's tasks come from the calendar store, + // which did not see this change; reload it so the refresh keeps it. + if (ok && before.activeAccountId && singletonServes(before.activeAccountId)) { + const { useCalendarStore } = require('../stores/calendar-store') as typeof import('../stores/calendar-store'); + await useCalendarStore.getState().fetchTasks(); + } + } catch (err) { + console.warn('[widgets] task toggle failed', err); + } + await afterServerCall(ok); + return; + } + + default: + console.warn(`[widgets] unknown action ${name}`); + } +} diff --git a/src/widgets/build.ts b/src/widgets/build.ts new file mode 100644 index 00000000..29aad5ed --- /dev/null +++ b/src/widgets/build.ts @@ -0,0 +1,484 @@ +// Builds the widget snapshot. Mail comes from JMAP on a client of the widgets' +// own (./jmap.ts) and works from a cold headless start. Calendar, tasks, +// birthdays, files and scheduled sends reuse the app's helpers, which are +// bound to the `jmapClient` singleton; they only run when that singleton is +// already connected to the active account (the app's JS runtime is alive). +// Otherwise those parts carry over from the previous snapshot, and the +// time-dependent views are still recomputed when a widget draws. + +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { jmapClient } from '../api/jmap-client'; +import { CAPABILITIES, type CalendarEvent, type ContactCard } from '../api/types'; +import { generateAccountId } from '../lib/account-utils'; +import { generateEmailAvatarColor, getEmailInitials } from '../lib/avatar-utils'; +import { getEventEndDate, getEventStartDate } from '../lib/calendar-utils'; +import { getUserParticipantId, getUserStatus } from '../lib/calendar-participants'; +import { getBirthday, getContactDisplayName, getDateParts } from '../lib/contact-utils'; +import { notificationLocale } from '../lib/push-background-task'; +import { useSettingsStore } from '../stores/settings-store'; +import { useKeywordsStore, keywordToken } from '../stores/keywords-store'; +import { LIGHT_COLORS } from '../theme/tokens'; +import { addDays, startOfDay } from './format'; +import { startOfWeek } from './derive'; +import { fetchInboxPreview, fetchMailSection, openClient, type MailSection } from './jmap'; +import { normalizeHex } from './theme'; +import { + emptySnapshot, + loadSnapshot, + saveSnapshot, + type AccountSummary, + type Birthday, + type EventItem, + type FileItem, + type Invitation, + type ScheduledItem, + type TagCount, + type TaskItem, + type WidgetSnapshot, +} from './snapshot'; + +interface RegistryAccount { + id: string; + email?: string; + displayName?: string; + username?: string; + avatarColor?: string; +} + +export async function readRegistry(): Promise<{ accounts: RegistryAccount[]; activeAccountId: string | null }> { + try { + const raw = await AsyncStorage.getItem('account-registry'); + const state = raw ? (JSON.parse(raw) as { state?: { accounts?: RegistryAccount[]; activeAccountId?: string | null } }).state : null; + const accounts = Array.isArray(state?.accounts) ? state!.accounts! : []; + const active = state?.activeAccountId && accounts.some((a) => a.id === state.activeAccountId) + ? state.activeAccountId + : accounts[0]?.id ?? null; + return { accounts, activeAccountId: active }; + } catch { + return { accounts: [], activeAccountId: null }; + } +} + +async function readRecentSearches(): Promise { + try { + const raw = await AsyncStorage.getItem('search-history-storage'); + const list = raw ? (JSON.parse(raw) as { state?: { recentSearches?: unknown } }).state?.recentSearches : null; + return Array.isArray(list) ? list.filter((s): s is string => typeof s === 'string').slice(0, 8) : []; + } catch { + return []; + } +} + +async function readPendingChanges(accountId: string): Promise { + try { + const raw = await AsyncStorage.getItem(`webmail:outbox:v1:${accountId}`); + const list = raw ? JSON.parse(raw) : null; + return Array.isArray(list) ? list.length : 0; + } catch { + return 0; + } +} + +/** True when the app's singleton is signed in to `registryAccountId` (live runtime). */ +export function singletonServes(registryAccountId: string): boolean { + if (!jmapClient.isConnected) return false; + const { username, serverUrl } = jmapClient; + return !!username && !!serverUrl && generateAccountId(username, serverUrl) === registryAccountId; +} + +function tagColor(color: string): string { + const tags = LIGHT_COLORS.tags; + return (color in tags ? tags[color as keyof typeof tags] : tags.gray).dot; +} + +function tagCounts(section: MailSection): TagCount[] { + const { keywords } = useKeywordsStore.getState(); + const out: TagCount[] = []; + for (const kw of keywords) { + const counts = section.tagCounts.get(keywordToken(kw.id)); + if (!counts || counts.total === 0) continue; + out.push({ + keyword: keywordToken(kw.id), + name: kw.label, + color: tagColor(kw.color), + unread: counts.unread, + total: counts.total, + ...(counts.latest ? { latestFrom: counts.latest.fromName, latestAt: counts.latest.receivedAt } : {}), + }); + } + return out.sort((a, b) => b.unread - a.unread || b.total - a.total); +} + +// ── App-backed sections (live runtime only) ────────────────────────────── + +function participantAddress(p: { email?: string; calendarAddress?: string; sendTo?: Record }): string { + const raw = p.email || p.calendarAddress || p.sendTo?.imip || ''; + return raw.replace(/^mailto:/i, ''); +} + +function toEventItem( + event: CalendarEvent, + calendarsById: Map, + selfEmails: string[], +): EventItem { + const calId = Object.keys(event.calendarIds ?? {}).find((id) => event.calendarIds[id]); + const cal = calId ? calendarsById.get(calId) : undefined; + const start = getEventStartDate(event).getTime(); + const end = Math.max(start, getEventEndDate(event).getTime()); + const location = Object.values(event.locations ?? {}).find((l) => l?.name)?.name; + const video = Object.values(event.virtualLocations ?? {}).find((v) => /^https?:\/\//i.test(v?.uri ?? '')); + const self = new Set(selfEmails.map((e) => e.toLowerCase())); + // Other people only: "who is coming" and the running-late mail leave the + // user out. + const participants = Object.values(event.participants ?? {}) + .filter((p) => !self.has(participantAddress(p).toLowerCase())) + .map((p) => { + const email = participantAddress(p); + const name = p.name?.trim() || email; + return { + name, + email, + initials: getEmailInitials(p.name ?? '', email) || '?', + color: normalizeHex(generateEmailAvatarColor(p.name ?? '', email)), + }; + }) + .filter((p) => p.name); + const organizer = Object.values(event.participants ?? {}).find((p) => p.roles?.owner); + const status = getUserStatus(event, selfEmails); + // A timed event from midnight to midnight is a whole-day event in all but + // name (some clients write holidays that way); show it as all-day rather + // than as "00:00 - 00:00". + const wholeDays = !event.showWithoutTime + && start === startOfDay(start) + && end > start + && end === startOfDay(end); + return { + id: event.id, + serverId: event.originalId || event.id, + ...(event.accountId ? { jmapAccountId: event.accountId } : {}), + title: event.title || '', + start, + end, + allDay: !!event.showWithoutTime || wholeDays, + color: normalizeHex(event.color || cal?.color), + calendarName: cal?.name ?? '', + ...(location ? { location } : {}), + ...(video ? { videoUrl: video.uri, videoName: video.name || '' } : {}), + participants, + ...(status === 'needs-action' || status === 'accepted' || status === 'tentative' || status === 'declined' + ? { myStatus: status } + : {}), + ...(organizer ? { organizerName: organizer.name || participantAddress(organizer) } : {}), + }; +} + +async function buildCalendar(now: number, selfEmails: string[], previous: WidgetSnapshot): Promise { + const calendarsSupported = jmapClient.hasAccountCapability(CAPABILITIES.CALENDARS); + if (!calendarsSupported) return { supported: false, events: [], invitations: [], birthdays: await buildBirthdays(now) }; + const { useCalendarStore, loadEventsInRange } = require('../stores/calendar-store') as typeof import('../stores/calendar-store'); + // Always refetch: the persisted list can predate a calendar deleted and + // re-created on the server under the same id, which would put events under + // the old name and colour. + try { + await useCalendarStore.getState().fetchCalendars(); + } catch { + // fall back to the cached list + } + const { calendars, hiddenCalendarIds } = useCalendarStore.getState(); + const visible = calendars.filter((c) => !hiddenCalendarIds.includes(c.id)); + const byId = new Map(calendars.map((c) => [c.id, { name: c.name, color: c.color }])); + + const first = new Date(now); + first.setDate(1); + const rangeStart = startOfWeek(first.getTime(), useSettingsStore.getState().calendarFirstDayOfWeek === 0 ? 0 : 1); + const rangeEnd = Math.max(addDays(startOfDay(now), 36), addDays(rangeStart, 42)); + let raw: CalendarEvent[]; + try { + raw = visible.length === 0 + ? [] + : await loadEventsInRange(calendars, visible.map((c) => c.id), new Date(rangeStart).toISOString(), new Date(rangeEnd).toISOString()); + } catch (err) { + console.warn('[widgets] calendar refresh failed', err); + return { ...previous.calendar, birthdays: await buildBirthdays(now) }; + } + const live = raw.filter((e) => e.status !== 'cancelled'); + const events = live + .map((e) => toEventItem(e, byId, selfEmails)) + .filter((e) => e.end >= rangeStart && e.start < rangeEnd) + .sort((a, b) => a.start - b.start); + + // One entry per series: answering an invitation answers the whole event. + const invitations: Invitation[] = []; + const seen = new Set(); + for (const e of live) { + if (getUserStatus(e, selfEmails) !== 'needs-action') continue; + const participantId = getUserParticipantId(e, selfEmails); + const key = e.originalId || e.id; + if (!participantId || seen.has(key)) continue; + const item = toEventItem(e, byId, selfEmails); + if (item.end < now) continue; + seen.add(key); + invitations.push({ ...item, participantId }); + } + + return { + supported: true, + events, + invitations: invitations.sort((a, b) => a.start - b.start).slice(0, 10), + birthdays: await buildBirthdays(now), + }; +} + +async function buildBirthdays(now: number): Promise { + const { useContactsStore } = require('../stores/contacts-store') as typeof import('../stores/contacts-store'); + // The Contacts tab loads the address book lazily; the widget may be first. + if (jmapClient.isConnected) { + try { + await useContactsStore.getState().fetchContactsIfStale(); + } catch { + // use whatever is cached + } + } + const contacts: ContactCard[] = useContactsStore.getState().contacts ?? []; + const today = startOfDay(now); + const out: Birthday[] = []; + for (const contact of contacts) { + const date = getBirthday(contact); + if (!date) continue; + const { month, day } = getDateParts(date); + if (!month || !day) continue; + const thisYear = new Date(new Date(today).getFullYear(), month - 1, day).getTime(); + const next = thisYear >= today ? thisYear : new Date(new Date(today).getFullYear() + 1, month - 1, day).getTime(); + const name = getContactDisplayName(contact); + const email = Object.values(contact.emails ?? {})[0]?.address; + out.push({ + name, + ...(email ? { email } : {}), + initials: getEmailInitials(name, email) || '?', + color: normalizeHex(generateEmailAvatarColor(name, email)), + date: next, + }); + } + return out.sort((a, b) => a.date - b.date).slice(0, 20); +} + +async function buildTasks(previous: WidgetSnapshot): Promise { + if (!useSettingsStore.getState().enableCalendarTasks) return { supported: false, items: [] }; + const { useCalendarStore } = require('../stores/calendar-store') as typeof import('../stores/calendar-store'); + try { + const store = useCalendarStore.getState(); + if (store.tasks.length === 0) await store.fetchTasks(); + const { tasks, calendars } = useCalendarStore.getState(); + const byId = new Map(calendars.map((c) => [c.id, c])); + const items: TaskItem[] = tasks + .filter((t) => t.progress !== 'cancelled') + .map((t) => { + const calId = Object.keys(t.calendarIds ?? {}).find((id) => t.calendarIds[id]); + const cal = calId ? byId.get(calId) : undefined; + // A LocalDateTime without an offset parses as device-local time. + const due = t.due ? Date.parse(t.due) : NaN; + return { + id: t.id, + serverId: t.originalId || t.id, + ...(t.accountId ? { jmapAccountId: t.accountId } : {}), + title: t.title || '', + ...(Number.isFinite(due) ? { due } : {}), + dueHasTime: !!t.due && !t.showWithoutTime && !/T00:00(:00)?$/.test(t.due), + done: t.progress === 'completed', + calendarName: cal?.name ?? '', + color: normalizeHex(t.color || cal?.color), + }; + }); + return { supported: true, items: items.slice(0, 40) }; + } catch { + return previous.tasks; + } +} + +async function buildFiles(previous: WidgetSnapshot): Promise { + const files = require('../api/files') as typeof import('../api/files'); + if (!files.supportsFiles()) return { supported: false, items: [] }; + try { + const nodes = await files.getAllFileNodes(); + const items: FileItem[] = nodes + .filter((n) => !!n.name) + .map((n) => ({ + id: n.id, + name: n.name, + isFolder: files.isFolder(n), + size: n.size ?? 0, + modified: Date.parse(n.modified || n.created || '') || 0, + type: n.type || '', + })) + .sort((a, b) => b.modified - a.modified) + .slice(0, 6); + return { supported: true, items }; + } catch { + return previous.files; + } +} + +async function buildScheduled(previous: WidgetSnapshot): Promise { + try { + if (!jmapClient.hasDelayedSend()) return []; + const { listScheduledEmails } = require('../api/email') as typeof import('../api/email'); + const list = await listScheduledEmails(); + return list.slice(0, 6).map((s) => { + const to = s.to?.[0]; + return { + id: s.emailSubmissionId, + to: to?.name || to?.email || '', + subject: s.subject ?? '', + sendAt: Date.parse(s.sendAt) || 0, + }; + }); + } catch { + return previous.mail.scheduled; + } +} + +// ── Assembly ───────────────────────────────────────────────────────────── + +async function buildSnapshot(previous: WidgetSnapshot): Promise { + const now = Date.now(); + const { accounts, activeAccountId } = await readRegistry(); + if (!activeAccountId) return { ...emptySnapshot(), generatedAt: now }; + + const settings = useSettingsStore.getState(); + if (!settings.hydrated) await settings.hydrate(); + const keywordsState = useKeywordsStore.getState(); + if (!keywordsState.hydrated) await keywordsState.hydrate(); + const { theme, timeFormat, calendarFirstDayOfWeek } = useSettingsStore.getState(); + + const live = singletonServes(activeAccountId); + const client = live ? jmapClient : await openClient(activeAccountId); + const next: WidgetSnapshot = { + ...previous, + version: 1, + generatedAt: now, + signedIn: true, + theme, + locale: await notificationLocale(), + hour12: timeFormat === '12h', + weekStart: calendarFirstDayOfWeek === 0 ? 0 : 1, + activeAccountId, + }; + if (previous.activeAccountId !== activeAccountId) { + // Nothing of another account's data may linger in a widget. + const blank = emptySnapshot(); + next.mail = blank.mail; + next.calendar = blank.calendar; + next.tasks = blank.tasks; + next.files = blank.files; + next.vacation = null; + next.quota = null; + next.appDataAt = 0; + } + if (!client) return next; + + const keywordTokens = useKeywordsStore.getState().keywords.map((k) => keywordToken(k.id)); + const section = await fetchMailSection(client, activeAccountId, keywordTokens); + + const others = accounts.filter((a) => a.id !== activeAccountId); + const previews = await Promise.all(others.map(async (a) => { + try { + const c = await openClient(a.id); + return c ? { account: a, ...(await fetchInboxPreview(c, a.id)) } : null; + } catch { + return null; + } + })); + const inboxUnread = section.folders.find((f) => f.role === 'inbox')?.unread ?? 0; + const summaries: AccountSummary[] = accounts.map((a) => { + const preview = previews.find((p) => p?.account.id === a.id); + return { + id: a.id, + label: a.displayName || a.email || a.username || a.id, + color: normalizeHex(a.avatarColor), + unread: a.id === activeAccountId ? inboxUnread : preview?.unread ?? 0, + }; + }); + const unified = [ + ...section.inbox.slice(0, 6), + ...previews.flatMap((p) => p?.items ?? []), + ].sort((a, b) => b.receivedAt - a.receivedAt).slice(0, 8); + + next.accounts = summaries; + next.mail = { + ...next.mail, + folders: section.folders, + inbox: section.inbox, + unified, + starred: section.starred, + starredCount: section.starredCount, + drafts: section.drafts, + draftCount: section.draftCount, + favourites: section.favourites, + recentSearches: await readRecentSearches(), + tags: tagCounts(section), + attachments: section.attachments, + pendingChanges: await readPendingChanges(activeAccountId), + }; + next.vacation = section.vacation; + next.quota = section.quota; + + if (live) { + const [calendar, tasks, files, scheduled] = await Promise.all([ + buildCalendar(now, section.selfEmails, next), + buildTasks(next), + buildFiles(next), + buildScheduled(next), + ]); + next.calendar = calendar; + next.tasks = tasks; + next.files = files; + next.mail.scheduled = scheduled; + next.appDataAt = now; + } + return next; +} + +let inflight: Promise | null = null; +let localChanges = 0; + +/** + * A widget button just edited the stored snapshot. A refresh that started + * before it read the server before the change, so its result is dropped + * rather than saved over the edit. + */ +export function noteLocalChange(): void { + localChanges++; +} + +/** + * Fetch fresh data and store it. Concurrent callers share one run: every + * placed widget gets its own update broadcast, and they would otherwise each + * start a refresh. On failure the previous snapshot is returned unchanged. + * + * `after` is for callers that just changed something on the server (a widget + * button): a run already in flight may have read the server before that + * change, so they wait for it and start (or join) the next one instead. + */ +export async function refreshSnapshot(opts?: { after?: 'change' }): Promise { + if (opts?.after === 'change' && inflight) { + await inflight.catch(() => undefined); + return refreshSnapshot(); + } + if (inflight) return inflight; + inflight = (async () => { + const changesAtStart = localChanges; + const previous = (await loadSnapshot()) ?? emptySnapshot(); + try { + const next = await buildSnapshot(previous); + if (localChanges !== changesAtStart) return (await loadSnapshot()) ?? next; + await saveSnapshot(next); + return next; + } catch (err) { + console.warn('[widgets] refresh failed', err); + return previous; + } finally { + inflight = null; + } + })(); + return inflight; +} diff --git a/src/widgets/catalog.json b/src/widgets/catalog.json new file mode 100644 index 00000000..319c0bfd --- /dev/null +++ b/src/widgets/catalog.json @@ -0,0 +1,40 @@ +{ + "$comment": "Home-screen widgets. scripts/sync-android-widgets.mjs turns this into the Android provider classes, provider XML, labels and manifest receivers; run it after editing. cols/rows are launcher cells (Android 2x2 is roughly the iOS small widget, 4x2 medium, 4x4 large).", + "widgets": [ + { "name": "InboxWidget", "label": "Inbox", "description": "Newest messages with sender, time and subject. Taller sizes add avatars and a line of preview.", "cols": 4, "rows": 2, "minCols": 2, "minRows": 2, "board": "mail" }, + { "name": "TriageWidget", "label": "Triage", "description": "One unread message at a time with archive, delete and reply buttons.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "mail" }, + { "name": "UnreadCountWidget", "label": "Unread count", "description": "Unread count for the inbox, with the latest senders.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "mail" }, + { "name": "LatestMessageWidget", "label": "Latest message", "description": "Only the newest unread message.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "mail" }, + { "name": "FolderCountsWidget", "label": "Folder counts", "description": "Unread counts for up to four folders.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "mail" }, + { "name": "MailShortcutsWidget", "label": "Shortcuts", "description": "Compose, calendar, contacts and files.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "mail", "static": true }, + { "name": "AllAccountsWidget", "label": "All accounts", "description": "The unified inbox across every signed-in account.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "mail" }, + { "name": "StarredWidget", "label": "Starred", "description": "Starred messages, newest first.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "mail" }, + { "name": "FavouritePeopleWidget", "label": "Favourite people", "description": "The people you hear from most, with their unread count.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "mail" }, + { "name": "MailSearchWidget", "label": "Search mail", "description": "Search bar with your recent searches.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 1, "board": "mail" }, + { "name": "OutboxWidget", "label": "Scheduled and outbox", "description": "Scheduled messages and anything waiting in the outbox.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "mail" }, + { "name": "TagWidget", "label": "Tag", "description": "Unread count for your busiest tag.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "mail" }, + { "name": "LastDraftWidget", "label": "Last draft", "description": "Back into the draft you left.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "mail" }, + + { "name": "AgendaWidget", "label": "Agenda", "description": "Upcoming events by day with time, colour and place.", "cols": 4, "rows": 4, "minCols": 3, "minRows": 2, "board": "calendar" }, + { "name": "WeekWidget", "label": "This week", "description": "The week at a glance from 8:00 to 18:00.", "cols": 4, "rows": 2, "minCols": 4, "minRows": 2, "board": "calendar" }, + { "name": "MonthWidget", "label": "Month", "description": "Month grid with a dot per calendar and the next two events.", "cols": 4, "rows": 4, "minCols": 3, "minRows": 3, "board": "calendar" }, + { "name": "TodayEventsWidget", "label": "Today", "description": "Today's date with the rest of today and tomorrow.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "calendar" }, + { "name": "NextEventWidget", "label": "Next event", "description": "What's next, when and where.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "calendar" }, + { "name": "DateNextWidget", "label": "Date and next two", "description": "Today's date with the next two events.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "calendar" }, + { "name": "UpNextWidget", "label": "Up next", "description": "Countdown to the next event with join and running-late buttons.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "calendar" }, + + { "name": "InvitationsWidget", "label": "Invitations", "description": "Pending invitations with accept, maybe and decline.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "plans" }, + { "name": "FreeTimeWidget", "label": "Free time", "description": "When you're free this week.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "plans" }, + { "name": "TasksWidget", "label": "Tasks", "description": "Open tasks with tick boxes that work from the home screen.", "cols": 4, "rows": 4, "minCols": 3, "minRows": 2, "board": "plans" }, + { "name": "TaskProgressWidget", "label": "Task progress", "description": "How many tasks are done.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "plans" }, + { "name": "CountdownWidget", "label": "Countdown", "description": "Days until your next all-day event.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "plans" }, + { "name": "BirthdaysWidget", "label": "Birthdays", "description": "Upcoming birthdays from your contacts.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "plans" }, + + { "name": "TodayHubWidget", "label": "Today overview", "description": "Next event, newest unread mail and tasks due, in one widget.", "cols": 4, "rows": 4, "minCols": 3, "minRows": 3, "board": "hubs" }, + { "name": "MailAndNextWidget", "label": "Mail and next event", "description": "Unread mail on one side, the next event on the other.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "hubs" }, + { "name": "RecentFilesWidget", "label": "Recent files", "description": "Recently changed items in Files.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "hubs" }, + { "name": "AttachmentsWidget", "label": "Recent attachments", "description": "The newest attachments from your mail.", "cols": 4, "rows": 2, "minCols": 3, "minRows": 2, "board": "hubs" }, + { "name": "VacationWidget", "label": "Vacation responder", "description": "Vacation responder status with an on/off switch.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "hubs" }, + { "name": "StorageWidget", "label": "Storage", "description": "How much of your mailbox quota is used.", "cols": 2, "rows": 2, "minCols": 2, "minRows": 2, "board": "hubs" } + ] +} diff --git a/src/widgets/clicks.ts b/src/widgets/clicks.ts new file mode 100644 index 00000000..b88f2a6c --- /dev/null +++ b/src/widgets/clicks.ts @@ -0,0 +1,60 @@ +// What a tap on part of a widget does. Either it opens the app on a deep link +// (handled by src/navigation/linking.ts, no JS needed in the widget), or it +// names an action the widget task performs in the background (./actions.ts). + +import { APP_SCHEME } from '../navigation/linking'; +import type { EventItem, MailItem } from './snapshot'; + +export interface WidgetClick { + clickAction?: string; + clickActionData?: Record; +} + +export type WidgetActionName = + | 'archive' + | 'trash' + | 'markRead' + | 'triageNext' + | 'rsvp' + | 'toggleTask' + | 'refresh'; + +export function action(name: WidgetActionName, data: Record = {}): WidgetClick { + return { clickAction: name, clickActionData: data }; +} + +export function open(uri: string): WidgetClick { + return { clickAction: 'OPEN_URI', clickActionData: { uri } }; +} + +const base = `${APP_SCHEME}://`; +const enc = encodeURIComponent; + +function query(params: Record): string { + const parts = Object.entries(params) + .filter(([, v]) => v !== undefined && v !== '') + .map(([k, v]) => `${k}=${enc(v!)}`); + return parts.length ? `?${parts.join('&')}` : ''; +} + +export const links = { + inbox: () => `${base}mail/folder/inbox`, + message: (m: Pick) => + `${base}mail/message/${enc(m.id)}${query({ account: m.accountId, jmapAccount: m.jmapAccountId })}`, + reply: (m: Pick) => + `${base}mail/message/${enc(m.id)}${query({ account: m.accountId, jmapAccount: m.jmapAccountId, action: 'reply' })}`, + draft: (m: Pick) => + `${base}mail/draft/${enc(m.id)}${query({ account: m.accountId, jmapAccount: m.jmapAccountId })}`, + unified: (params: { view?: 'all' | 'unread' | 'starred'; role?: string } = {}) => + `${base}mail/unified${query({ view: params.view, role: params.role })}`, + scheduled: () => `${base}mail/scheduled`, + search: (q = '') => `${base}mail/search${query({ q })}`, + compose: (params: { to?: string; subject?: string; body?: string } = {}) => + `${base}compose${query({ to: params.to, subject: params.subject, body: params.body })}`, + calendar: () => `${base}calendar`, + event: (e: Pick) => + `${base}calendar/event/${enc(e.serverId)}${query({ account: e.jmapAccountId })}`, + contacts: () => `${base}contacts`, + files: () => `${base}files`, + settings: (tab?: string) => `${base}settings${tab ? `/${enc(tab)}` : ''}`, +}; diff --git a/src/widgets/derive.ts b/src/widgets/derive.ts new file mode 100644 index 00000000..71e8682a --- /dev/null +++ b/src/widgets/derive.ts @@ -0,0 +1,209 @@ +// Views that depend on the current time, computed when a widget draws rather +// than when the snapshot was fetched. Pure functions of (snapshot, now), so +// they are unit-tested without the widget runtime. + +import { addDays, startOfDay } from './format'; +import type { EventItem, TaskItem, WidgetSnapshot } from './snapshot'; + +const DAY = 86400000; + +/** Events overlapping [dayStart, dayStart + 1 day), all-day first, then by start. */ +export function eventsOnDay(events: EventItem[], dayStart: number): EventItem[] { + const dayEnd = addDays(dayStart, 1); + return events + .filter((e) => e.start < dayEnd && (e.end > dayStart || (e.end === e.start && e.start >= dayStart))) + .sort((a, b) => (a.allDay === b.allDay ? a.start - b.start : a.allDay ? -1 : 1)); +} + +/** Timed events that have not ended yet, soonest first. */ +export function upcomingTimed(events: EventItem[], now: number): EventItem[] { + return events.filter((e) => !e.allDay && e.end > now).sort((a, b) => a.start - b.start); +} + +/** The next timed event (running or upcoming) and whether it has started. */ +export function nextEvent(events: EventItem[], now: number): { event: EventItem; running: boolean } | null { + const [event] = upcomingTimed(events, now); + if (!event) return null; + return { event, running: event.start <= now }; +} + +export interface AgendaDay { + dayStart: number; + events: EventItem[]; +} + +/** + * Days from today with at least one event still relevant (today drops the + * events that already ended), up to `maxDays` days with events. + */ +export function agendaDays(events: EventItem[], now: number, maxDays: number, horizonDays = 14): AgendaDay[] { + const out: AgendaDay[] = []; + const today = startOfDay(now); + for (let i = 0; i < horizonDays && out.length < maxDays; i++) { + const day = addDays(today, i); + const list = eventsOnDay(events, day).filter((e) => i > 0 || e.allDay || e.end > now); + if (list.length > 0) out.push({ dayStart: day, events: list }); + } + return out; +} + +/** Start of the week containing `ms` for the given first weekday (0 Sun, 1 Mon). */ +export function startOfWeek(ms: number, weekStart: 0 | 1): number { + const day = startOfDay(ms); + const dow = new Date(day).getDay(); + const diff = (dow - weekStart + 7) % 7; + return addDays(day, -diff); +} + +export interface MonthCell { + dayStart: number; + day: number; + inMonth: boolean; + isToday: boolean; + /** Distinct calendar colours with events that day, at most three. */ + dots: string[]; +} + +/** The month containing `now` as whole weeks (5 or 6 rows). */ +export function monthGrid(events: EventItem[], now: number, weekStart: 0 | 1): MonthCell[][] { + const first = new Date(now); + first.setDate(1); + first.setHours(0, 0, 0, 0); + const month = first.getMonth(); + const today = startOfDay(now); + let cursor = startOfWeek(first.getTime(), weekStart); + const weeks: MonthCell[][] = []; + do { + const week: MonthCell[] = []; + for (let i = 0; i < 7; i++) { + const d = new Date(cursor); + const dots: string[] = []; + for (const e of eventsOnDay(events, cursor)) { + if (!dots.includes(e.color)) dots.push(e.color); + if (dots.length === 3) break; + } + week.push({ + dayStart: cursor, + day: d.getDate(), + inMonth: d.getMonth() === month, + isToday: cursor === today, + dots, + }); + cursor = addDays(cursor, 1); + } + weeks.push(week); + } while (new Date(cursor).getMonth() === month); + return weeks; +} + +export interface BusyBlock { + /** Fraction of the visible window, 0..1. */ + from: number; + to: number; + color: string; +} + +/** + * Timed events of one day clipped to [startHour, endHour) as fractions of that + * window - the week and free-time bars. + */ +export function busyBlocks(events: EventItem[], dayStart: number, startHour = 8, endHour = 18): BusyBlock[] { + const winStart = dayStart + startHour * 3600000; + const winEnd = dayStart + endHour * 3600000; + const span = winEnd - winStart; + return eventsOnDay(events, dayStart) + .filter((e) => !e.allDay && e.end > winStart && e.start < winEnd) + .map((e) => ({ + from: (Math.max(e.start, winStart) - winStart) / span, + to: (Math.min(e.end, winEnd) - winStart) / span, + color: e.color, + })); +} + +export interface FreeTime { + /** The next timed event today, if any. */ + next: EventItem | null; + /** True when an event is running right now. */ + busyNow: boolean; + /** Longest free stretch left today inside working hours, if any. */ + longestGap: { from: number; to: number } | null; +} + +export function freeTime(events: EventItem[], now: number, startHour = 8, endHour = 18): FreeTime { + const today = startOfDay(now); + const winEnd = today + endHour * 3600000; + const timed = eventsOnDay(events, today).filter((e) => !e.allDay); + const busyNow = timed.some((e) => e.start <= now && e.end > now); + const next = timed.find((e) => e.start > now) ?? null; + + let cursor = Math.max(now, today + startHour * 3600000); + let longest: { from: number; to: number } | null = null; + for (const e of timed.filter((x) => x.end > cursor).sort((a, b) => a.start - b.start)) { + if (e.start > cursor) { + const gap = { from: cursor, to: Math.min(e.start, winEnd) }; + if (gap.to > gap.from && (!longest || gap.to - gap.from > longest.to - longest.from)) longest = gap; + } + cursor = Math.max(cursor, e.end); + if (cursor >= winEnd) break; + } + if (cursor < winEnd) { + const gap = { from: cursor, to: winEnd }; + if (!longest || gap.to - gap.from > longest.to - longest.from) longest = gap; + } + return { next, busyNow, longestGap: longest }; +} + +export type DueKind = 'overdue' | 'today' | 'tomorrow' | 'later' | 'none'; + +export function dueKind(task: TaskItem, now: number): DueKind { + if (task.due === undefined) return 'none'; + const today = startOfDay(now); + const dueDay = startOfDay(task.due); + if (task.dueHasTime ? task.due < now : dueDay < today) return 'overdue'; + if (dueDay === today) return 'today'; + if (dueDay === addDays(today, 1)) return 'tomorrow'; + return 'later'; +} + +/** Open tasks first (overdue, then by due date, undated last), completed after. */ +export function sortTasks(tasks: TaskItem[], now: number): TaskItem[] { + const rank: Record = { overdue: 0, today: 1, tomorrow: 2, later: 3, none: 4 }; + return [...tasks].sort((a, b) => { + if (a.done !== b.done) return a.done ? 1 : -1; + const ra = rank[dueKind(a, now)]; + const rb = rank[dueKind(b, now)]; + if (ra !== rb) return ra - rb; + return (a.due ?? Number.MAX_SAFE_INTEGER) - (b.due ?? Number.MAX_SAFE_INTEGER); + }); +} + +/** Tasks that count for "today": overdue, due today, or completed today-ish. */ +export function tasksDueNow(tasks: TaskItem[], now: number): TaskItem[] { + return tasks.filter((t) => { + const kind = dueKind(t, now); + return kind === 'overdue' || kind === 'today'; + }); +} + +/** + * The event a countdown should point at: the next all-day or multi-day event + * that starts after today, else the next timed event on a later day. + */ +export function countdownTarget(events: EventItem[], now: number): EventItem | null { + const tomorrow = addDays(startOfDay(now), 1); + const later = events.filter((e) => e.start >= tomorrow).sort((a, b) => a.start - b.start); + return later.find((e) => e.allDay || e.end - e.start >= DAY) ?? later[0] ?? null; +} + +export function daysUntil(ms: number, now: number): number { + return Math.round((startOfDay(ms) - startOfDay(now)) / DAY); +} + +/** Birthdays in the next `days` days, soonest first. */ +export function upcomingBirthdays(snapshot: WidgetSnapshot, now: number, days = 45) { + const today = startOfDay(now); + const until = addDays(today, days); + return snapshot.calendar.birthdays + .filter((b) => b.date >= today && b.date < until) + .sort((a, b) => a.date - b.date); +} diff --git a/src/widgets/format.ts b/src/widgets/format.ts new file mode 100644 index 00000000..54402ebc --- /dev/null +++ b/src/widgets/format.ts @@ -0,0 +1,132 @@ +// Strings and date formatting for widget layouts. Widgets render in a headless +// task with no React tree, so this uses the plain translate() lookup instead +// of the useTranslation hook. Keys live under `widgets.` in locales/rn/*.json +// (added by `npm run i18n:harvest`); each call repeats the English text as +// the fallback so a missing key never shows up raw on the home screen. + +import { isSupportedLocale, translate, type LocaleCode, type MessageParams } from '../i18n'; + +export interface Fmt { + locale: string; + hour12: boolean; + t: (key: string, fallback: string, params?: MessageParams) => string; + /** Clock time in the user's 12h/24h setting. */ + time: (ms: number) => string; + weekdayShort: (ms: number) => string; + weekdayLong: (ms: number) => string; + monthLong: (ms: number) => string; + /** "Sep 28" */ + dayMonth: (ms: number) => string; + /** "Mon, Sep 28" */ + weekdayDayMonth: (ms: number) => string; + /** "Sep 28, 2026" */ + fullDate: (ms: number) => string; + /** Time today, weekday this week, date otherwise - the list's receivedAt style. */ + listDate: (ms: number, now: number) => string; + /** "in 25 min", "in 2 h", "now". */ + relative: (ms: number, now: number) => string; + bytes: (n: number) => string; +} + +function safeFormat(locale: string, options: Intl.DateTimeFormatOptions): (ms: number) => string { + let formatter: Intl.DateTimeFormat | null = null; + try { + formatter = new Intl.DateTimeFormat(locale, options); + } catch { + try { + formatter = new Intl.DateTimeFormat('en', options); + } catch { + formatter = null; + } + } + return (ms: number) => { + const d = new Date(ms); + if (formatter) { + try { + return formatter.format(d); + } catch { + // fall through + } + } + return d.toDateString(); + }; +} + +const pad = (n: number) => String(n).padStart(2, '0'); + +export function startOfDay(ms: number): number { + const d = new Date(ms); + d.setHours(0, 0, 0, 0); + return d.getTime(); +} + +export function addDays(ms: number, days: number): number { + const d = new Date(ms); + d.setDate(d.getDate() + days); + return d.getTime(); +} + +export function sameDay(a: number, b: number): boolean { + return startOfDay(a) === startOfDay(b); +} + +export function makeFmt(locale: string, hour12: boolean): Fmt { + const lang: LocaleCode = isSupportedLocale(locale) ? locale : 'en'; + const t = (key: string, fallback: string, params?: MessageParams) => + translate(lang, key, fallback, params); + + const weekdayShort = safeFormat(locale, { weekday: 'short' }); + const weekdayLong = safeFormat(locale, { weekday: 'long' }); + const monthLong = safeFormat(locale, { month: 'long' }); + const dayMonth = safeFormat(locale, { month: 'short', day: 'numeric' }); + const weekdayDayMonth = safeFormat(locale, { weekday: 'short', month: 'short', day: 'numeric' }); + const fullDate = safeFormat(locale, { year: 'numeric', month: 'short', day: 'numeric' }); + + const time = (ms: number) => { + const d = new Date(ms); + const h = d.getHours(); + const m = pad(d.getMinutes()); + if (!hour12) return `${pad(h)}:${m}`; + const suffix = h < 12 ? 'AM' : 'PM'; + return `${h % 12 === 0 ? 12 : h % 12}:${m} ${suffix}`; + }; + + return { + locale, + hour12, + t, + time, + weekdayShort, + weekdayLong, + monthLong, + dayMonth, + weekdayDayMonth, + fullDate, + listDate: (ms, now) => { + if (sameDay(ms, now)) return time(ms); + const days = Math.round((startOfDay(now) - startOfDay(ms)) / 86400000); + if (days > 0 && days < 7) return weekdayShort(ms); + return dayMonth(ms); + }, + relative: (ms, now) => { + const minutes = Math.round((ms - now) / 60000); + if (minutes <= 0) return t('widgets.relative.now', 'now'); + if (minutes < 60) return t('widgets.relative.minutes', 'in {count} min', { count: minutes }); + const hours = Math.round(minutes / 60); + if (hours < 24) return t('widgets.relative.hours', 'in {count} h', { count: hours }); + const days = Math.round((startOfDay(ms) - startOfDay(now)) / 86400000); + return t('widgets.relative.days', '{count, plural, one {in # day} other {in # days}}', { count: days }); + }, + bytes: (n) => { + if (!Number.isFinite(n) || n <= 0) return '0 B'; + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let v = n; + let i = 0; + while (v >= 1024 && i < units.length - 1) { + v /= 1024; + i++; + } + return `${v >= 10 || i === 0 ? Math.round(v) : v.toFixed(1)} ${units[i]}`; + }, + }; +} diff --git a/src/widgets/icons.ts b/src/widgets/icons.ts new file mode 100644 index 00000000..88ae2455 --- /dev/null +++ b/src/widgets/icons.ts @@ -0,0 +1,100 @@ +// Tabler Icons (MIT), the set the webmail draws since it moved off Lucide. +// Path data is copied from @tabler/icons tabler-nodes-outline.json so the +// widgets use the same glyphs as the web UI. Widgets render them through +// SvgWidget, which rasterises an SVG string. + +const OUTLINE = { + mail: '', + inbox: '', + send: '', + file: '', + fileText: '', + fileSpreadsheet: '', + presentation: '', + photo: '', + folder: '', + calendarClock: '', + calendar: '', + calendarPlain: '', + archive: '', + trash: '', + ban: '', + search: '', + edit: '', + addressBook: '', + server: '', + paperclip: '', + star: '', + message: '', + mapPin: '', + video: '', + clock: '', + check: '', + plus: '', + chevronLeft: '', + chevronRight: '', + users: '', + lock: '', + cake: '', + beach: '', + reply: '', + checklist: '', + upload: '', + refresh: '', + tag: '', +} as const; + +const FILLED = { + star: '', + tag: '', +} as const; + +export type IconName = keyof typeof OUTLINE; +export type FilledIconName = keyof typeof FILLED; + +/** Splits `#rrggbbaa` into an SVG colour plus opacity; rgba() is passed through. */ +function paint(color: string): { value: string; opacity: string } { + const m = /^#([0-9a-f]{6})([0-9a-f]{2})$/i.exec(color); + if (m) return { value: `#${m[1]}`, opacity: (parseInt(m[2], 16) / 255).toFixed(3) }; + return { value: color, opacity: '1' }; +} + +/** Outline Tabler icon as an SVG string (24 x 24 viewBox, stroke 2 like the web UI). */ +export function icon(name: IconName, color: string, strokeWidth = 2): string { + const { value, opacity } = paint(color); + return ( + `` + + `${OUTLINE[name]}` + ); +} + +/** Filled Tabler icon (star, tag) as an SVG string. */ +export function filledIcon(name: FilledIconName, color: string): string { + const { value, opacity } = paint(color); + return ( + `${FILLED[name]}` + ); +} + +/** + * A progress ring (quota, task progress, countdown). `fraction` is clamped to + * 0..1; the arc starts at 12 o'clock like the webmail's quota ring. + */ +export function ring(fraction: number, track: string, fill: string, strokeWidth = 6): string { + const f = Math.max(0, Math.min(1, Number.isFinite(fraction) ? fraction : 0)); + const r = 32 - strokeWidth / 2; + const c = 2 * Math.PI * r; + const t = paint(track); + const p = paint(fill); + const arc = f > 0 + ? `` + : ''; + return ( + `` + + `` + + `${arc}` + ); +} diff --git a/src/widgets/jmap.ts b/src/widgets/jmap.ts new file mode 100644 index 00000000..1891802c --- /dev/null +++ b/src/widgets/jmap.ts @@ -0,0 +1,450 @@ +// JMAP access for the widgets. Every call goes through a JMAPClient instance +// of its own, never the app's `jmapClient` singleton: the widget task can run +// inside the live React Native instance, where re-binding the singleton would +// send the UI's requests with another account's credentials. The api/* +// helpers are all bound to the singleton, so the few calls the widgets need +// are written out here. + +import { AuthenticationError, JMAPClient, NetworkError } from '../api/jmap-client'; +import { CAPABILITIES, type Attachment, type Email, type Mailbox } from '../api/types'; +import { realAttachments } from '../lib/list-attachments'; +import { findArchiveMailbox, findJunkMailbox, findTrashMailbox } from '../lib/mailbox-tree'; +import { generateEmailAvatarColor, getEmailInitials } from '../lib/avatar-utils'; +import { normalizeHex } from './theme'; +import type { AttachmentGroup, FolderCount, MailItem, Person, QuotaState, VacationState } from './snapshot'; + +type Call = [string, Record, string]; + +const LIST_PROPERTIES = [ + 'id', 'threadId', 'mailboxIds', 'keywords', 'receivedAt', 'from', 'subject', 'preview', 'hasAttachment', +]; +const MAILBOX_PROPERTIES = ['id', 'name', 'role', 'parentId', 'unreadEmails', 'totalEmails', 'sortOrder']; +const NEWEST_FIRST = [{ property: 'receivedAt', isAscending: false }]; + +/** A client for one signed-in account, or null when it has no stored credentials. */ +export async function openClient(registryAccountId: string): Promise { + const client = new JMAPClient(); + try { + return (await client.loadAccount(registryAccountId)) ? client : null; + } catch (err) { + // A revoked password must not log the user out from a widget: the app + // handles that when it next opens. Offline is expected; skip quietly. + if (err instanceof AuthenticationError || err instanceof NetworkError) return null; + throw err; + } +} + +function responseOf(res: { methodResponses: Array<[string, any, string]> }, callId: string): any { + const hit = res.methodResponses.find((r) => r[2] === callId && r[0] !== 'error'); + return hit ? hit[1] : null; +} + +export function toMailItem( + email: Email, + registryAccountId: string, + opts?: { jmapAccountId?: string; threadSize?: number }, +): MailItem { + const from = email.from?.[0]; + const name = from?.name?.trim() || from?.email || ''; + return { + id: email.id, + threadId: email.threadId, + accountId: registryAccountId, + ...(opts?.jmapAccountId ? { jmapAccountId: opts.jmapAccountId } : {}), + fromName: name, + fromEmail: from?.email ?? '', + initials: getEmailInitials(from?.name ?? '', from?.email) || '?', + color: normalizeHex(generateEmailAvatarColor(from?.name ?? '', from?.email)), + subject: email.subject ?? '', + preview: (email.preview ?? '').replace(/\s+/g, ' ').trim(), + receivedAt: Date.parse(email.receivedAt) || 0, + unread: !email.keywords?.$seen, + starred: !!email.keywords?.$flagged, + hasAttachment: !!email.hasAttachment, + threadSize: Math.max(1, opts?.threadSize ?? 1), + }; +} + +const ROLE_ORDER: FolderCount['role'][] = ['inbox', 'drafts', 'scheduled', 'sent', 'archive', 'junk', 'trash']; + +export function folderCounts(mailboxes: Mailbox[]): FolderCount[] { + const out: FolderCount[] = []; + for (const role of ROLE_ORDER) { + const box = role === 'junk' + ? findJunkMailbox(mailboxes) + : role === 'trash' + ? findTrashMailbox(mailboxes) + : role === 'archive' + ? findArchiveMailbox(mailboxes) + : mailboxes.find((m) => m.role === role); + if (box) out.push({ role, name: box.name, unread: box.unreadEmails ?? 0, total: box.totalEmails ?? 0 }); + } + return out; +} + +/** The people who wrote most among recent inbox mail, with their unread count. */ +export function rankPeople(emails: Email[], selfEmails: string[], limit = 4): Person[] { + const self = new Set(selfEmails.map((e) => e.toLowerCase())); + const byEmail = new Map(); + for (const email of emails) { + const from = email.from?.[0]; + const address = from?.email?.toLowerCase(); + if (!address || self.has(address) || /(^|[.+-])(no-?reply|notifications?|mailer-daemon)@/.test(address)) continue; + const entry = byEmail.get(address) ?? { + name: from?.name?.trim() || address, + email: address, + initials: getEmailInitials(from?.name ?? '', address) || '?', + color: normalizeHex(generateEmailAvatarColor(from?.name ?? '', address)), + unread: 0, + lastAt: 0, + count: 0, + }; + entry.count++; + if (!email.keywords?.$seen) entry.unread++; + entry.lastAt = Math.max(entry.lastAt, Date.parse(email.receivedAt) || 0); + byEmail.set(address, entry); + } + return [...byEmail.values()] + .sort((a, b) => b.count - a.count || b.lastAt - a.lastAt) + .slice(0, limit) + .map(({ count: _count, ...person }) => person); +} + +export interface MailSection { + mailboxes: Mailbox[]; + folders: FolderCount[]; + inbox: MailItem[]; + starred: MailItem[]; + starredCount: number; + drafts: MailItem[]; + draftCount: number; + favourites: Person[]; + attachments: AttachmentGroup[]; + tagCounts: Map; + quota: QuotaState | null; + vacation: VacationState | null; + selfEmails: string[]; +} + +/** + * Everything mail-related the widgets show for one account, in three + * requests: mailboxes, then the lists (inbox, starred, drafts, attachments, + * sender ranking, tag counts), then quota and vacation where supported. + */ +export async function fetchMailSection( + client: JMAPClient, + registryAccountId: string, + tagKeywords: string[], +): Promise { + const accountId = client.accountId; + + const boxesRes = await client.request([ + ['Mailbox/get', { accountId, properties: MAILBOX_PROPERTIES }, 'm'], + ['Identity/get', { accountId, properties: ['email'] }, 'i'], + ], [CAPABILITIES.CORE, CAPABILITIES.MAIL, CAPABILITIES.SUBMISSION]); + const mailboxes = (responseOf(boxesRes, 'm')?.list ?? []) as Mailbox[]; + const selfEmails = [ + client.username ?? '', + ...((responseOf(boxesRes, 'i')?.list ?? []) as Array<{ email?: string }>).map((i) => i.email ?? ''), + ].filter(Boolean); + + const inbox = mailboxes.find((m) => m.role === 'inbox'); + const drafts = mailboxes.find((m) => m.role === 'drafts'); + const excluded = [findTrashMailbox(mailboxes), findJunkMailbox(mailboxes)] + .filter((m): m is Mailbox => !!m) + .map((m) => m.id); + + const calls: Call[] = []; + const getList = (queryId: string, getId: string, properties = LIST_PROPERTIES) => { + calls.push(['Email/get', { + accountId, + '#ids': { resultOf: queryId, name: 'Email/query', path: '/ids' }, + properties, + }, getId]); + }; + if (inbox) { + calls.push(['Email/query', { + accountId, filter: { inMailbox: inbox.id }, sort: NEWEST_FIRST, collapseThreads: true, limit: 12, + }, 'qi']); + getList('qi', 'gi'); + calls.push(['Thread/get', { + accountId, '#ids': { resultOf: 'gi', name: 'Email/get', path: '/list/*/threadId' }, + }, 'ti']); + calls.push(['Email/query', { + accountId, filter: { inMailbox: inbox.id }, sort: NEWEST_FIRST, limit: 60, + }, 'qp']); + getList('qp', 'gp', ['id', 'from', 'keywords', 'receivedAt']); + calls.push(['Email/query', { + accountId, + filter: { operator: 'AND', conditions: [{ inMailbox: inbox.id }, { hasAttachment: true }] }, + // Read receipts and invitations also count as attachments to the + // server; ask for a few more and keep the ones with real files. + sort: NEWEST_FIRST, collapseThreads: true, limit: 10, + }, 'qa']); + calls.push(['Email/get', { + accountId, + '#ids': { resultOf: 'qa', name: 'Email/query', path: '/ids' }, + properties: ['id', 'from', 'receivedAt', 'attachments'], + bodyProperties: ['name', 'type', 'disposition', 'cid', 'size'], + }, 'ga']); + } + calls.push(['Email/query', { + accountId, + filter: excluded.length + ? { operator: 'AND', conditions: [{ hasKeyword: '$flagged' }, { inMailboxOtherThan: excluded }] } + : { hasKeyword: '$flagged' }, + sort: NEWEST_FIRST, collapseThreads: true, limit: 6, calculateTotal: true, + }, 'qs']); + getList('qs', 'gs'); + if (drafts) { + calls.push(['Email/query', { + accountId, filter: { inMailbox: drafts.id }, sort: NEWEST_FIRST, limit: 1, calculateTotal: true, + }, 'qd']); + getList('qd', 'gd'); + } + const listRes = await client.request(calls); + + const threadSizes = new Map( + ((responseOf(listRes, 'ti')?.list ?? []) as Array<{ id: string; emailIds: string[] }>) + .map((t) => [t.id, t.emailIds?.length ?? 1]), + ); + const inboxEmails = (responseOf(listRes, 'gi')?.list ?? []) as Email[]; + const inboxItems = inboxEmails + .map((e) => toMailItem(e, registryAccountId, { threadSize: threadSizes.get(e.threadId) })) + .sort((a, b) => b.receivedAt - a.receivedAt); + const starred = ((responseOf(listRes, 'gs')?.list ?? []) as Email[]) + .map((e) => toMailItem(e, registryAccountId)) + .sort((a, b) => b.receivedAt - a.receivedAt); + const draftItems = ((responseOf(listRes, 'gd')?.list ?? []) as Email[]).map((e) => toMailItem(e, registryAccountId)); + const attachments: AttachmentGroup[] = ((responseOf(listRes, 'ga')?.list ?? []) as Array; + }>) + .map((e) => ({ + emailId: e.id, + accountId: registryAccountId, + fromName: e.from?.[0]?.name || e.from?.[0]?.email || '', + receivedAt: Date.parse(e.receivedAt) || 0, + files: realAttachments(e.attachments as Attachment[] | undefined) + .map((a) => ({ name: a.name ?? '', type: a.type || 'application/octet-stream' })), + })) + .filter((g) => g.files.length > 0) + .sort((a, b) => b.receivedAt - a.receivedAt) + .slice(0, 3); + + const tagCounts = await fetchTagCounts(client, tagKeywords, excluded, registryAccountId); + const { quota, vacation } = await fetchAccountStatus(client); + + return { + mailboxes, + folders: folderCounts(mailboxes), + inbox: inboxItems, + starred, + starredCount: responseOf(listRes, 'qs')?.total ?? starred.length, + drafts: draftItems, + draftCount: responseOf(listRes, 'qd')?.total ?? draftItems.length, + favourites: rankPeople((responseOf(listRes, 'gp')?.list ?? []) as Email[], selfEmails), + attachments, + tagCounts, + quota, + vacation, + selfEmails, + }; +} + +async function fetchTagCounts( + client: JMAPClient, + keywords: string[], + excluded: string[], + registryAccountId: string, +): Promise { + const out: MailSection['tagCounts'] = new Map(); + const wanted = keywords.slice(0, 8); + if (wanted.length === 0) return out; + const accountId = client.accountId; + const base = (keyword: string) => + excluded.length ? [{ hasKeyword: keyword }, { inMailboxOtherThan: excluded }] : [{ hasKeyword: keyword }]; + const calls: Call[] = []; + wanted.forEach((keyword, i) => { + calls.push(['Email/query', { + accountId, + filter: { operator: 'AND', conditions: base(keyword) }, + sort: NEWEST_FIRST, limit: 1, calculateTotal: true, + }, `t${i}`]); + calls.push(['Email/get', { + accountId, + '#ids': { resultOf: `t${i}`, name: 'Email/query', path: '/ids' }, + properties: LIST_PROPERTIES, + }, `l${i}`]); + calls.push(['Email/query', { + accountId, + filter: { operator: 'AND', conditions: [...base(keyword), { notKeyword: '$seen' }] }, + limit: 0, calculateTotal: true, + }, `u${i}`]); + }); + const max = Math.max(3, client.getMaxCallsInRequest() - (client.getMaxCallsInRequest() % 3)); + for (let start = 0; start < calls.length; start += max) { + const res = await client.request(calls.slice(start, start + max)); + wanted.forEach((keyword, i) => { + const total = responseOf(res, `t${i}`)?.total; + if (total === undefined) return; + const latest = (responseOf(res, `l${i}`)?.list ?? [])[0] as Email | undefined; + out.set(keyword, { + total, + unread: responseOf(res, `u${i}`)?.total ?? 0, + latest: latest ? toMailItem(latest, registryAccountId) : undefined, + }); + }); + } + return out; +} + +async function fetchAccountStatus(client: JMAPClient): Promise<{ quota: QuotaState | null; vacation: VacationState | null }> { + const accountId = client.accountId; + const calls: Call[] = []; + const using: string[] = [CAPABILITIES.CORE]; + if (client.hasAccountCapability(CAPABILITIES.QUOTA)) { + calls.push(['Quota/get', { accountId }, 'q']); + using.push(CAPABILITIES.QUOTA); + } + if (client.hasAccountCapability(CAPABILITIES.VACATION)) { + calls.push(['VacationResponse/get', { accountId, ids: ['singleton'] }, 'v']); + using.push(CAPABILITIES.MAIL, CAPABILITIES.VACATION); + } + if (calls.length === 0) return { quota: null, vacation: null }; + try { + const res = await client.request(calls, using); + const quotas = (responseOf(res, 'q')?.list ?? []) as Array<{ + resourceType?: string; scope?: string; used?: number; hardLimit?: number; limit?: number; types?: string[]; + }>; + const coversMail = (q: (typeof quotas)[number]) => !q.types?.length || q.types.some((t) => t === 'Email' || t === 'Mail'); + const mailQuota = quotas.find((q) => q.resourceType === 'octets' && coversMail(q)) + ?? quotas.find((q) => q.resourceType === 'mail' || q.scope === 'mail'); + const limit = mailQuota ? mailQuota.hardLimit ?? mailQuota.limit ?? 0 : 0; + const v = (responseOf(res, 'v')?.list ?? [])[0] as + | { isEnabled?: boolean; fromDate?: string | null; toDate?: string | null; subject?: string | null } + | undefined; + return { + quota: mailQuota && limit > 0 ? { used: mailQuota.used ?? 0, limit } : null, + vacation: v + ? { + enabled: !!v.isEnabled, + ...(v.fromDate ? { from: Date.parse(v.fromDate) } : {}), + ...(v.toDate ? { to: Date.parse(v.toDate) } : {}), + ...(v.subject ? { subject: v.subject } : {}), + } + : null, + }; + } catch { + return { quota: null, vacation: null }; + } +} + +/** Newest inbox messages of one account, for the all-accounts widget. */ +export async function fetchInboxPreview(client: JMAPClient, registryAccountId: string, limit = 5): Promise<{ + items: MailItem[]; + unread: number; +}> { + const accountId = client.accountId; + const boxes = await client.request([ + ['Mailbox/query', { accountId, filter: { role: 'inbox' } }, 'mq'], + ['Mailbox/get', { + accountId, + '#ids': { resultOf: 'mq', name: 'Mailbox/query', path: '/ids' }, + properties: MAILBOX_PROPERTIES, + }, 'mg'], + ]); + const inbox = (responseOf(boxes, 'mg')?.list ?? [])[0] as Mailbox | undefined; + if (!inbox) return { items: [], unread: 0 }; + const res = await client.request([ + ['Email/query', { accountId, filter: { inMailbox: inbox.id }, sort: NEWEST_FIRST, collapseThreads: true, limit }, 'q'], + ['Email/get', { accountId, '#ids': { resultOf: 'q', name: 'Email/query', path: '/ids' }, properties: LIST_PROPERTIES }, 'g'], + ]); + return { + items: ((responseOf(res, 'g')?.list ?? []) as Email[]).map((e) => toMailItem(e, registryAccountId)), + unread: inbox.unreadEmails ?? 0, + }; +} + +// ── Actions ────────────────────────────────────────────────────────────── + +function emailAccount(client: JMAPClient, jmapAccountId?: string): string { + return jmapAccountId ?? client.accountId; +} + +async function setEmail(client: JMAPClient, accountId: string, update: Record>): Promise { + const res = await client.request([['Email/set', { accountId, update }, 's']]); + const body = responseOf(res, 's'); + return !!body && !body.notUpdated; +} + +export async function markRead(client: JMAPClient, emailId: string, jmapAccountId?: string): Promise { + return setEmail(client, emailAccount(client, jmapAccountId), { [emailId]: { 'keywords/$seen': true } }); +} + +/** + * Move a message out of its mailboxes into the archive (or trash). Reads the + * message's current mailboxes first so the patch only drops those. + */ +export async function moveTo( + client: JMAPClient, + emailId: string, + target: 'archive' | 'trash', + jmapAccountId?: string, +): Promise { + const accountId = emailAccount(client, jmapAccountId); + const res = await client.request([ + ['Mailbox/get', { accountId, properties: MAILBOX_PROPERTIES }, 'm'], + ['Email/get', { accountId, ids: [emailId], properties: ['id', 'mailboxIds'] }, 'e'], + ]); + const mailboxes = (responseOf(res, 'm')?.list ?? []) as Mailbox[]; + const email = (responseOf(res, 'e')?.list ?? [])[0] as Pick | undefined; + const destination = target === 'archive' ? findArchiveMailbox(mailboxes) : findTrashMailbox(mailboxes); + if (!email || !destination) return false; + const patch: Record = { [`mailboxIds/${destination.id}`]: true }; + for (const id of Object.keys(email.mailboxIds ?? {})) { + if (id !== destination.id) patch[`mailboxIds/${id}`] = null; + } + if (target === 'trash') patch['keywords/$seen'] = true; + return setEmail(client, accountId, { [emailId]: patch }); +} + +export async function rsvp( + client: JMAPClient, + eventId: string, + participantId: string, + status: 'accepted' | 'tentative' | 'declined', + jmapAccountId?: string, +): Promise { + const accountId = jmapAccountId ?? client.getPrimaryAccountId(CAPABILITIES.CALENDARS); + const res = await client.request([ + ['CalendarEvent/set', { + accountId, + update: { [eventId]: { [`participants/${participantId}/participationStatus`]: status } }, + sendSchedulingMessages: true, + }, 's'], + ], [CAPABILITIES.CORE, CAPABILITIES.CALENDARS]); + const body = responseOf(res, 's'); + return !!body && !body.notUpdated; +} + +/** Same flip as calendar-store's toggleTaskComplete; progressUpdated is left to the server. */ +export async function setTaskDone( + client: JMAPClient, + taskId: string, + done: boolean, + jmapAccountId?: string, +): Promise { + const accountId = jmapAccountId ?? client.getPrimaryAccountId(CAPABILITIES.CALENDARS); + const res = await client.request([ + ['CalendarEvent/set', { + accountId, + update: { + [taskId]: done + ? { progress: 'completed', percentComplete: 100 } + : { progress: 'needs-action', percentComplete: 0 }, + }, + }, 's'], + ], [CAPABILITIES.CORE, CAPABILITIES.CALENDARS]); + const body = responseOf(res, 's'); + return !!body && !body.notUpdated; +} diff --git a/src/widgets/layouts/calendar.tsx b/src/widgets/layouts/calendar.tsx new file mode 100644 index 00000000..ced4a0fe --- /dev/null +++ b/src/widgets/layouts/calendar.tsx @@ -0,0 +1,1061 @@ +// Board "Calendar": the agenda, this week, the month grid, today, the next +// event, the date with the next two events and the up-next countdown. They +// copy the webmail's agenda, week and month views as the mockup drew them. +// +// RemoteViews have no absolute positioning, so everything that sits at a +// computed place (week blocks, the now line) is built from spacers whose dp +// heights come from the widget size the launcher reports. + +import React from 'react'; +import { FlexWidget, OverlapWidget, SvgWidget, TextWidget, type ColorProp } from 'react-native-android-widget'; +import { links, open } from '../clicks'; +import { + agendaDays, + busyBlocks, + daysUntil, + eventsOnDay, + monthGrid, + nextEvent, + startOfWeek, + type AgendaDay, + type BusyBlock, + type MonthCell, +} from '../derive'; +import { addDays, sameDay, startOfDay, type Fmt } from '../format'; +import { ring, type IconName } from '../icons'; +import { AGENDA_ROW_HEIGHT, AgendaRow, DAY_BAND_HEIGHT, DayBand } from '../parts'; +import { Avatar, Button, Dot, EventChip, Fab, Header, Icon, Placeholder, Spacer, Surface, Txt } from '../primitives'; +import type { EventItem, WidgetSnapshot } from '../snapshot'; +import { chipBackground, normalizeHex, type WidgetPalette } from '../theme'; +import type { Layout } from './types'; + +/** Surface's 1dp frame; Android adds a border to the padding, so it eats space. */ +const FRAME = 1; +const HOUR = 3600000; +const GRID_START_HOUR = 8; +const GRID_END_HOUR = 18; + +// --------------------------------------------------------------------------- +// Shared helpers + +function unsupportedText(f: Fmt): string { + return f.t('widgets.calendar.unsupported', "Calendar isn't available on this server"); +} + +function nothingText(f: Fmt): string { + return f.t('widgets.calendar.nothing_planned', 'Nothing on the calendar'); +} + +function Unsupported({ p, f }: { p: WidgetPalette; f: Fmt }) { + return ( + + + + ); +} + +function NothingPlanned({ p, f }: { p: WidgetPalette; f: Fmt }) { + return ( + + + + ); +} + +/** Appends an alpha byte to a `#rrggbb` palette colour; other forms pass through. */ +function withAlpha(color: ColorProp, alpha: string): ColorProp { + return /^#[0-9a-f]{6}$/i.test(color) ? (`${color}${alpha}` as ColorProp) : color; +} + +function upper(text: string, locale: string): string { + try { + return text.toLocaleUpperCase(locale); + } catch { + return text.toUpperCase(); + } +} + +function capitalize(text: string, locale: string): string { + if (!text) return text; + return upper(text.charAt(0), locale) + text.slice(1); +} + +function eventTitle(f: Fmt, e: EventItem): string { + return e.title || f.t('widgets.calendar.untitled', '(no title)'); +} + +function timeRange(f: Fmt, e: EventItem): string { + return `${f.time(e.start)} – ${f.time(e.end)}`; +} + +/** Nothing for today, the weekday for the coming six days, a date after that. */ +function dayPrefix(f: Fmt, ms: number, now: number): string { + const diff = daysUntil(ms, now); + if (diff <= 0) return ''; + return diff < 7 ? f.weekdayShort(ms) : f.dayMonth(ms); +} + +/** "Tue 09:30 – 10:00" for another day, the bare range for today. */ +function whenRange(f: Fmt, e: EventItem, now: number): string { + const prefix = dayPrefix(f, e.start, now); + return prefix ? `${prefix} ${timeRange(f, e)}` : timeRange(f, e); +} + +/** How the month view's chips label an event: "09:30", "Tue 10:00", "All day". */ +function chipTime(f: Fmt, e: EventItem, now: number): string { + const today = startOfDay(now); + const prefix = dayPrefix(f, Math.max(e.start, today), now); + if (e.allDay) return prefix || f.t('widgets.calendar.all_day', 'All day'); + if (e.start < today) return f.t('widgets.calendar.now', 'Now'); + return prefix ? `${prefix} ${f.time(e.start)}` : f.time(e.start); +} + +/** "Today", "Tomorrow", the weekday this week, else weekday and date. */ +function dayLabel(f: Fmt, dayStart: number, now: number): string { + const diff = daysUntil(dayStart, now); + if (diff === 0) return f.t('widgets.calendar.today', 'Today'); + if (diff === 1) return f.t('widgets.calendar.tomorrow', 'Tomorrow'); + return diff > 1 && diff < 7 ? f.weekdayLong(dayStart) : f.weekdayDayMonth(dayStart); +} + +/** + * Events still ahead or running, soonest first, all-day events leading their + * day. An all-day event that began before today is not "next" any more, so a + * week-long trip does not hold the top spot all week. + */ +function upcoming(events: EventItem[], now: number): EventItem[] { + const today = startOfDay(now); + const day = (e: EventItem) => startOfDay(Math.max(e.start, today)); + return events + .filter((e) => (e.allDay ? e.start >= today : e.end > now)) + .sort((a, b) => day(a) - day(b) || (a.allDay === b.allDay ? a.start - b.start : a.allDay ? -1 : 1)); +} + +/** Video link or place, as the agenda row shows it. */ +function placeOf(f: Fmt, e: EventItem): { icon: IconName; text: string } | null { + if (e.videoUrl) return { icon: 'video', text: e.videoName || f.t('widgets.calendar.video_call', 'Video call') }; + if (e.location) return { icon: 'mapPin', text: e.location }; + return null; +} + +/** The signed-in addresses, where an account label is one. */ +function selfAddresses(s: WidgetSnapshot): Set { + return new Set( + s.accounts.map((a) => a.label.trim().toLowerCase()).filter((label) => label.includes('@')), + ); +} + +function monthYear(f: Fmt, ms: number): string { + try { + return new Intl.DateTimeFormat(f.locale, { month: 'long', year: 'numeric' }).format(new Date(ms)); + } catch { + return `${f.monthLong(ms)} ${new Date(ms).getFullYear()}`; + } +} + +// --------------------------------------------------------------------------- +// Agenda (4x4, down to 3x2) + +const AGENDA_FOOTER_HEIGHT = 30; + +interface AgendaPlan { + sections: AgendaDay[]; + /** The first day with rows that did not fit, summarised in the footer. */ + hidden: AgendaDay | null; +} + +function planAgenda(days: AgendaDay[], room: number): AgendaPlan { + const full = days.reduce((h, d) => h + DAY_BAND_HEIGHT + d.events.length * AGENDA_ROW_HEIGHT, 0); + const budget = full <= room ? room : room - AGENDA_FOOTER_HEIGHT; + const sections: AgendaDay[] = []; + let used = 0; + for (const day of days) { + const fit = Math.floor((budget - used - DAY_BAND_HEIGHT) / AGENDA_ROW_HEIGHT); + const take = Math.max(0, Math.min(day.events.length, fit)); + if (take > 0) { + sections.push({ dayStart: day.dayStart, events: day.events.slice(0, take) }); + used += DAY_BAND_HEIGHT + take * AGENDA_ROW_HEIGHT; + } + if (take < day.events.length) { + return { sections, hidden: { dayStart: day.dayStart, events: day.events.slice(take) } }; + } + } + return { sections, hidden: null }; +} + +export const AgendaLayout: Layout = ({ s, p, f, now, height }) => { + if (!s.calendar.supported) return ; + const compact = height < 220; + const headerHeight = compact ? 38 : 44; + const header = ( +

} + /> + ); + const days = agendaDays(s.calendar.events, now, 14); + if (days.length === 0) { + return ( + + {header} + + + ); + } + // Header bar plus its 1dp rule. + const room = height - 2 * FRAME - headerHeight - 1; + const { sections, hidden } = planAgenda(days, room); + const rows: React.JSX.Element[] = []; + sections.forEach((day, d) => { + rows.push(); + day.events.forEach((e, i) => { + const last = !hidden && d === sections.length - 1 && i === day.events.length - 1; + rows.push(); + }); + }); + return ( + + {header} + {rows} + {hidden ? ( + + eventTitle(f, e)).join(', '), + })} + color={p.muted} + size={13} + /> + + ) : null} + + ); +}; + +// --------------------------------------------------------------------------- +// This week (4x2) + +const WEEK_PAD = 10; +const WEEK_LABEL_HEIGHT = 16; +const WEEK_DAY_SIZE = 24; +const WEEK_STRIP_HEIGHT = 20; +const WEEK_GAP = 4; +const WEEK_BLOCK_MIN = 4; +const WEEK_MAX_LANES = 3; +const WEEK_LANE_GAP = 1; + +interface StripSegment { + span: number; + event: EventItem | null; +} + +/** All-day events across the week; a multi-day event becomes one wide chip. */ +function allDaySegments(events: EventItem[], days: number[]): StripSegment[] { + const segments: StripSegment[] = []; + let previousId: string | null = null; + for (const day of days) { + const list = eventsOnDay(events, day).filter((e) => e.allDay); + const keepId: string | null = previousId; + const event: EventItem | null = list.find((e) => e.id === keepId) ?? list[0] ?? null; + const last = segments[segments.length - 1]; + if (last && (last.event?.id ?? null) === (event?.id ?? null)) last.span += 1; + else segments.push({ span: 1, event }); + previousId = event?.id ?? null; + } + return segments; +} + +interface PlacedBlock { + /** dp from the top of the grid. */ + top: number; + height: number; + color: string; +} + +/** + * Stacks one day's busy blocks into side-by-side lanes, the way the week view + * splits overlapping events. Once the lanes run out, a block starts where the + * lane that frees up first ends. + */ +function laneBlocks(blocks: BusyBlock[], gridHeight: number): PlacedBlock[][] { + const lanes: Array<{ end: number; items: PlacedBlock[] }> = []; + for (const b of [...blocks].sort((x, y) => x.from - y.from || y.to - x.to)) { + let top = b.from * gridHeight; + let lane = lanes.find((l) => l.end <= top + 0.5); + if (!lane && lanes.length < WEEK_MAX_LANES) { + lane = { end: 0, items: [] }; + lanes.push(lane); + } + if (!lane) { + lane = lanes.reduce((a, c) => (c.end < a.end ? c : a)); + top = lane.end; + } + top = Math.max(top, lane.end); + const bottom = Math.min(gridHeight, Math.max(b.to * gridHeight, top + WEEK_BLOCK_MIN)); + if (bottom - top < WEEK_BLOCK_MIN) top = Math.max(lane.end, bottom - WEEK_BLOCK_MIN); + if (bottom - top <= 0) continue; + lane.items.push({ top, height: bottom - top, color: b.color }); + lane.end = bottom; + } + return lanes.map((l) => l.items); +} + +function BlockLanes({ lanes, width, height }: { lanes: PlacedBlock[][]; width: number; height: number }) { + const count = Math.max(1, lanes.length); + const laneWidth = Math.max(1, (width - 4 - (count - 1) * WEEK_LANE_GAP) / count); + const columns: React.JSX.Element[] = []; + lanes.forEach((items, l) => { + if (l > 0) columns.push(); + const parts: React.JSX.Element[] = []; + let cursor = 0; + items.forEach((b, j) => { + if (b.top > cursor) parts.push(); + const color = normalizeHex(b.color); + parts.push( + , + ); + cursor = b.top + b.height; + }); + columns.push( + + {parts} + , + ); + }); + return ( + + {columns} + + ); +} + +/** The red now line with its dot, `top` dp down the grid. */ +function NowLine({ p, top, width, height }: { p: WidgetPalette; top: number; width: number; height: number }) { + const offset = Math.max(0, Math.min(height - 6, top - 3)); + return ( + + + + + + + + ); +} + +export const WeekLayout: Layout = ({ s, p, f, now, width, height }) => { + if (!s.calendar.supported) return ; + const events = s.calendar.events; + const first = startOfWeek(now, s.weekStart); + const days = Array.from({ length: 7 }, (_, i) => addDays(first, i)); + const colWidth = (width - 2 * FRAME - 2 * WEEK_PAD) / 7; + const segments = allDaySegments(events, days); + const strip = segments.some((seg) => seg.event !== null); + const gridHeight = Math.max( + 0, + height - 2 * FRAME - WEEK_PAD - WEEK_LABEL_HEIGHT - 2 - WEEK_DAY_SIZE - WEEK_GAP + - (strip ? WEEK_STRIP_HEIGHT + WEEK_GAP : 0) - 1, + ); + const windowStart = startOfDay(now) + GRID_START_HOUR * HOUR; + const nowFraction = (now - windowStart) / ((GRID_END_HOUR - GRID_START_HOUR) * HOUR); + + const headCells = days.map((day) => { + const isToday = sameDay(day, now); + return ( + + + + {isToday ? ( + + + + ) : ( + + + + )} + + ); + }); + + const stripCells = segments.map((seg, i) => { + const e = seg.event; + const color = e ? normalizeHex(e.color) : null; + return ( + + {e && color ? ( + + + + ) : null} + + ); + }); + + const gridColumns = days.map((day, i) => { + const ruled = i < days.length - 1; + const inner = colWidth - (ruled ? 1 : 0); + const lanes = laneBlocks(busyBlocks(events, day, GRID_START_HOUR, GRID_END_HOUR), gridHeight); + const blocks = ; + const showNow = sameDay(day, now) && nowFraction >= 0 && nowFraction <= 1; + return ( + + {showNow ? ( + + {blocks} + + + ) : blocks} + + ); + }); + + return ( + + {headCells} + + {strip ? ( + {stripCells} + ) : null} + {strip ? : null} + + {gridColumns} + + + ); +}; + +// --------------------------------------------------------------------------- +// Month (4x4) + +const MONTH_PAD = 8; +const MONTH_LABEL_HEIGHT = 18; +const MONTH_GRID_TOP = 4; +const MONTH_CHIP_HEIGHT = 24; +const MONTH_CELL_MIN = 30; +const MONTH_CELL_MAX = 48; +/** Rule above the chips: 6dp margin, 1dp border, 6dp padding. */ +const MONTH_CHIPS_TOP = 13; +const MONTH_CHIPS_BOTTOM = 8; + +function NavButton({ p, iconName }: { p: WidgetPalette; iconName: IconName }) { + return ( + + + + ); +} + +function MonthDay({ + p, + cell, + width, + height, + circle, + showDots, +}: { + p: WidgetPalette; + cell: MonthCell; + width: number; + height: number; + circle: number; + showDots: boolean; +}) { + const color = cell.isToday ? p.primaryFg : cell.inMonth ? p.fg : withAlpha(p.muted, 'b3'); + const dots: React.JSX.Element[] = []; + if (showDots) { + cell.dots.forEach((c, i) => { + if (i > 0) dots.push(); + dots.push(); + }); + } + return ( + + + = 22 ? 14 : 13, fontWeight: cell.isToday ? '700' : '400' }} + /> + + {dots.length > 0 ? : null} + {dots.length > 0 ? {dots} : null} + + ); +} + +export const MonthLayout: Layout = ({ s, p, f, now, width, height }) => { + if (!s.calendar.supported) return ; + const weeks = monthGrid(s.calendar.events, now, s.weekStart); + const next = upcoming(s.calendar.events, now); + const headerHeight = height >= 340 ? 44 : 38; + const cellWidth = (width - 2 * FRAME - 2 * MONTH_PAD) / 7; + const chipsHeight = (n: number) => + n === 0 ? 0 : MONTH_CHIPS_TOP + n * MONTH_CHIP_HEIGHT + (n - 1) * 4 + MONTH_CHIPS_BOTTOM; + const gridRoom = (n: number) => + height - 2 * FRAME - headerHeight - MONTH_LABEL_HEIGHT - MONTH_GRID_TOP - chipsHeight(n); + // Two chips when the weeks still get a comfortable row, else one, else none. + let chips = Math.min(2, next.length); + while (chips > 0 && gridRoom(chips) / weeks.length < MONTH_CELL_MIN) chips -= 1; + const cellHeight = Math.max(18, Math.min(MONTH_CELL_MAX, Math.floor(gridRoom(chips) / weeks.length))); + const circle = Math.min(26, Math.max(16, cellHeight - 12)); + const showDots = cellHeight - circle >= 8; + + const chipRows: React.JSX.Element[] = []; + next.slice(0, chips).forEach((e, i) => { + if (i > 0) chipRows.push(); + chipRows.push( + , + ); + }); + + return ( + + + + + + + + + + + {weeks[0].map((cell) => ( + + + + ))} + + + {weeks.map((week) => ( + + {week.map((cell) => ( + + ))} + + ))} + {chipRows.length > 0 ? ( + + {chipRows} + + ) : null} + + ); +}; + +// --------------------------------------------------------------------------- +// Today (4x2) + +const TODAY_PAD = 14; +const TODAY_CHIP = 24; +const TODAY_GAP = 4; +const TODAY_LINE = 16; +/** 6dp extra space, the "Tomorrow" label and one chip, with their gaps. */ +const TOMORROW_BLOCK = 6 + TODAY_GAP + TODAY_LINE + TODAY_GAP + TODAY_CHIP; + +function chipLabel(f: Fmt, e: EventItem): string { + return e.allDay ? f.t('widgets.calendar.all_day', 'All day') : f.time(e.start); +} + +function TodayColumn({ + s, + p, + f, + now, + height, +}: { + s: WidgetSnapshot; + p: WidgetPalette; + f: Fmt; + now: number; + height: number; +}) { + if (!s.calendar.supported) { + return ( + + + + ); + } + const events = s.calendar.events; + const today = startOfDay(now); + const todayList = eventsOnDay(events, today).filter((e) => e.allDay || e.end > now); + const tomorrowList = eventsOnDay(events, addDays(today, 1)); + + // The "N more this week" line is always reserved; chips share the rest. + const room = height - 2 * FRAME - 2 * TODAY_PAD - TODAY_LINE; + const row = TODAY_CHIP + TODAY_GAP; + let withTomorrow = tomorrowList.length > 0; + let slots = Math.floor((room - (withTomorrow ? TOMORROW_BLOCK : 0) + TODAY_GAP) / row); + if (slots < 1 && withTomorrow && todayList.length > 0) { + withTomorrow = false; + slots = Math.floor((room + TODAY_GAP) / row); + } + const shown = todayList.slice(0, Math.max(1, slots)); + + const shownIds = new Set(shown.map((e) => e.id)); + if (withTomorrow) shownIds.add(tomorrowList[0].id); + const weekEnd = addDays(startOfWeek(now, s.weekStart), 7); + const rest = new Set(); + for (let day = today; day < weekEnd; day = addDays(day, 1)) { + for (const e of eventsOnDay(events, day)) { + if (day === today && !e.allDay && e.end <= now) continue; + if (!shownIds.has(e.id)) rest.add(e.id); + } + } + const more = rest.size; + + if (todayList.length === 0 && tomorrowList.length === 0 && more === 0) { + return ( + + + + ); + } + + const items: React.JSX.Element[] = []; + if (shown.length === 0) { + items.push( + , + ); + } + shown.forEach((e, i) => { + if (i > 0) items.push(); + items.push( + , + ); + }); + if (withTomorrow) { + const e = tomorrowList[0]; + items.push(); + items.push( + , + ); + items.push(); + items.push( + , + ); + } + return ( + + {items} + + {more > 0 ? ( + + ) : null} + + ); +} + +export const TodayEventsLayout: Layout = ({ s, p, f, now, height }) => { + // The date block is ~108dp tall at 40sp; a short 4x2 gets a smaller number + // and, if still tight, loses the month line. + const room = height - 2 * FRAME - 2 * TODAY_PAD; + const big = room >= 108 ? 40 : 32; + const showMonth = room >= 3 * 18 + Math.ceil(big * 1.33); + return ( + + + + + + {showMonth ? : null} + + + + + ); +}; + +// --------------------------------------------------------------------------- +// Next event (2x2) + +export const NextEventLayout: Layout = ({ s, p, f, now, height }) => { + if (!s.calendar.supported) return ; + const next = nextEvent(s.calendar.events, now); + if (!next) return ; + const { event: e, running } = next; + const when = running ? f.t('widgets.calendar.now', 'Now') : capitalize(f.relative(e.start, now), f.locale); + // Status line, gap, one title line and the time range always show; then the + // place, the calendar and a second title line, as long as they fit. + const room = height - 2 * FRAME - 28; + let used = 16 + 6 + 22 + 18; + const place = placeOf(f, e); + const showPlace = !!place && used + 16 <= room; + if (showPlace) used += 16; + const showCalendar = !!e.calendarName && used + 16 + (showPlace ? 2 : 0) <= room; + if (showCalendar) used += 16 + (showPlace ? 2 : 0); + const titleLines = used + 22 <= room ? 2 : 1; + return ( + + + + + + + + + + + {showPlace && place ? ( + + + + + + ) : null} + {showPlace && showCalendar ? : null} + {showCalendar ? ( + + + + + + ) : null} + + ); +}; + +// --------------------------------------------------------------------------- +// Date and next two (2x2) + +const DATE_PAD = 12; +const DATE_CIRCLE = 36; +const DATE_CHIP = 36; + +function TwoLineChip({ f, e, now }: { f: Fmt; e: EventItem; now: number }) { + const color = normalizeHex(e.color); + return ( + + + + + ); +} + +export const DateNextLayout: Layout = ({ s, p, f, now, height }) => { + const room = height - 2 * FRAME - 2 * DATE_PAD - DATE_CIRCLE - 8; + const count = room >= 2 * DATE_CHIP + 4 ? 2 : 1; + const next = s.calendar.supported ? upcoming(s.calendar.events, now).slice(0, count) : []; + const chips: React.JSX.Element[] = []; + next.forEach((e, i) => { + if (i > 0) chips.push(); + chips.push(); + }); + return ( + + + + + + + + + + + + + {chips.length > 0 ? chips : ( + + )} + + ); +}; + +// --------------------------------------------------------------------------- +// Up next (4x2) + +interface Countdown { + value: string; + unit: string; + /** Share of the ring to fill: minutes to go out of an hour. */ + fraction: number; +} + +function countdown(f: Fmt, e: EventItem, now: number, running: boolean): Countdown { + const minutes = Math.round((e.start - now) / 60000); + if (running || minutes <= 0) return { value: f.t('widgets.calendar.now', 'Now'), unit: '', fraction: 0 }; + if (minutes < 60) { + return { + value: String(minutes), + unit: f.t('widgets.calendar.unit_minutes', '{count, plural, one {min} other {min}}', { count: minutes }), + fraction: minutes / 60, + }; + } + const hours = Math.round(minutes / 60); + if (hours < 24) { + return { + value: String(hours), + unit: f.t('widgets.calendar.unit_hours', '{count, plural, one {hour} other {hours}}', { count: hours }), + fraction: 1, + }; + } + const days = daysUntil(e.start, now); + return { + value: String(days), + unit: f.t('widgets.calendar.unit_days', '{count, plural, one {day} other {days}}', { count: days }), + fraction: 1, + }; +} + +function People({ p, e }: { p: WidgetPalette; e: EventItem }) { + const faces = e.participants.slice(0, 3); + return ( + + {faces.map((person, i) => ( + + + + ))} + + + + + + ); +} + +export const UpNextLayout: Layout = ({ s, p, f, now }) => { + if (!s.calendar.supported) return ; + const next = nextEvent(s.calendar.events, now); + if (!next) return ; + const { event: e, running } = next; + const title = eventTitle(f, e); + const left = countdown(f, e, now, running); + const meta = [whenRange(f, e, now), e.calendarName].filter(Boolean).join(' · '); + + const self = selfAddresses(s); + const recipients = Array.from( + new Set(e.participants.map((x) => x.email.trim()).filter((x) => x && !self.has(x.toLowerCase()))), + ); + const late = links.compose({ + to: recipients.length > 0 ? recipients.join(',') : undefined, + subject: f.t('widgets.calendar.running_late_subject', 'Running late: {title}', { title }), + body: f.t('widgets.calendar.running_late_body', "I'm running a few minutes late for {title}.", { title }), + }); + const joinLabel = e.videoName && e.videoName.length <= 12 + ? f.t('widgets.calendar.join_named', 'Join {name}', { name: e.videoName }) + : f.t('widgets.calendar.join', 'Join'); + + return ( + + + + + + 3 ? 13 : 18, fontWeight: '700' }} + /> + {left.unit ? ( + + ) : null} + + + + + + + + + + + + {e.participants.length > 0 ? : null} + {e.participants.length > 0 ? : null} + + + + + {e.videoUrl ? ( + + + + + + + + ); +} + +function makeStyles(c: ThemePalette) { + return StyleSheet.create({ + container: { flex: 1, backgroundColor: c.background }, + flex: { flex: 1 }, + header: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'space-between', + paddingHorizontal: spacing.lg, + paddingTop: Platform.OS === 'android' ? spacing.xl : spacing.sm, + paddingBottom: spacing.md, + }, + closeButton: { width: 32, height: 32, alignItems: 'center', justifyContent: 'center' }, + title: { ...typography.h3, color: c.text }, + content: { paddingHorizontal: spacing.lg, paddingBottom: spacing.xxxl, gap: spacing.lg }, + hint: { ...typography.body, color: c.textSecondary }, + input: { + ...typography.body, + color: c.text, + minHeight: 96, + borderWidth: 1, + borderColor: c.border, + borderRadius: radius.sm, + backgroundColor: c.background, + paddingHorizontal: spacing.md, + paddingTop: spacing.sm, + paddingBottom: spacing.sm, + }, + inputFocused: { borderColor: c.borderFocus, borderWidth: 2 }, + inputError: { borderColor: c.error }, + error: { ...typography.caption, color: c.error, marginTop: -spacing.sm }, + }); +} diff --git a/src/components/__tests__/screens-i18n.test.ts b/src/components/__tests__/screens-i18n.test.ts index 4be30f12..8bc4791c 100644 --- a/src/components/__tests__/screens-i18n.test.ts +++ b/src/components/__tests__/screens-i18n.test.ts @@ -41,6 +41,8 @@ const TRANSLATED_FILES: Record = { 'components/TemplateSheet.tsx': [], 'components/IdentitySheet.tsx': [], 'components/QrScanModal.tsx': [], + // The input's placeholder is the link's own format. + 'components/PasteSignInLinkModal.tsx': ['bulwarkmail://pair?server=…&code=…'], 'components/email/ActionSheet.tsx': [], 'components/email/AddressActionSheet.tsx': [], 'components/email/CalendarInvitationBanner.tsx': [], diff --git a/src/navigation/__tests__/linking.test.ts b/src/navigation/__tests__/linking.test.ts index 3468282b..363a3cfa 100644 --- a/src/navigation/__tests__/linking.test.ts +++ b/src/navigation/__tests__/linking.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; -import { handleDeepLink, parseDeepLink, shareToDeepLink } from '../linking'; +import { acceptSignInLink, handleDeepLink, parseDeepLink, parseSignInLink, shareToDeepLink } from '../linking'; +import { setPendingSignInLink, usePendingSignInLinkStore } from '../pending-sign-in-link'; import { usePendingSettingsTab } from '../pending-settings-tab'; import { usePendingCalendarOpen } from '../pending-calendar-open'; import { usePendingMailSearch } from '../pending-mail-search'; @@ -257,3 +258,44 @@ describe('shareToDeepLink', () => { }); }); }); + +describe('sign-in links', () => { + const code = 'c0ffee'.repeat(10) + 'beef'; + const pair = `bulwarkmail://pair?server=${encodeURIComponent('https://mail.example.com/webmail')}&code=${code}`; + + it('are never navigation links', () => { + expect(parseDeepLink(pair)).toBeNull(); + expect(parseDeepLink('bulwarkmail://connect?server=https%3A%2F%2Fmail.example.com')).toBeNull(); + // The scheme is not an alias of the app scheme. + expect(parseDeepLink('bulwarkmail://mail/message/M1')).toBeNull(); + }); + + it('parses pair and connect links, and nothing else', () => { + expect(parseSignInLink(pair)).toEqual({ kind: 'pair', webmailUrl: 'https://mail.example.com/webmail', code }); + expect(parseSignInLink(pair.replace('://pair?', '://pair/?'))).toEqual({ + kind: 'pair', webmailUrl: 'https://mail.example.com/webmail', code, + }); + expect(parseSignInLink('bulwarkmail://connect?server=https%3A%2F%2Fmail.example.com')) + .toEqual({ kind: 'connect', webmailUrl: 'https://mail.example.com' }); + // A plain webmail address is a QR convenience, not a link that signs in. + expect(parseSignInLink('https://mail.example.com')).toBeNull(); + expect(parseSignInLink('bulwarkmobile://mail/message/M1')).toBeNull(); + expect(parseSignInLink(`bulwarkmail://pair?server=http%3A%2F%2Fmail.example.com&code=${code}`)).toBeNull(); + expect(parseSignInLink(null)).toBeNull(); + }); + + it('parks a sign-in link for the login screen, which takes it once', () => { + setPendingSignInLink(null); + expect(acceptSignInLink('bulwarkmobile://mail')).toBe(false); + expect(acceptSignInLink('mailto:a@b.co')).toBe(false); + expect(usePendingSignInLinkStore.getState().payload).toBeNull(); + + expect(acceptSignInLink(pair)).toBe(true); + expect(usePendingSignInLinkStore.getState().take()).toEqual({ + kind: 'pair', webmailUrl: 'https://mail.example.com/webmail', code, + }); + // Taken means cleared: a second delivery of the same state signs in nothing. + expect(usePendingSignInLinkStore.getState().payload).toBeNull(); + expect(usePendingSignInLinkStore.getState().take()).toBeNull(); + }); +}); diff --git a/src/navigation/linking.ts b/src/navigation/linking.ts index 537b48e0..f45af146 100644 --- a/src/navigation/linking.ts +++ b/src/navigation/linking.ts @@ -9,8 +9,14 @@ import type { RootStackParamList } from './types'; import { setPendingSettingsTab } from './pending-settings-tab'; import { setPendingCalendarOpen } from './pending-calendar-open'; import { setPendingMailSearch } from './pending-mail-search'; +import { setPendingSignInLink } from './pending-sign-in-link'; +import { parseQrLoginPayload, type QrLoginPayload } from '../lib/oauth'; export const APP_SCHEME = 'bulwarkmobile'; +// The webmail's "Link Mobile App" links (`bulwarkmail://pair?server=…&code=…`) +// and server-bootstrap links (`bulwarkmail://connect?server=…`). They sign in +// rather than navigate, so they never become a DeepLink. +export const SIGN_IN_SCHEME = 'bulwarkmail'; const UNIFIED_ROLES = ['inbox', 'sent', 'drafts', 'junk', 'archive', 'trash'] as const; const UNIFIED_VIEWS = ['all', 'unread', 'starred'] as const; @@ -74,6 +80,7 @@ function splitUrl(url: string): { segments: string[]; search: URLSearchParams } export function parseDeepLink(url: string): DeepLink | null { if (!url) return null; + if (isSignInSchemeUrl(url)) return null; if (/^mailto:/i.test(url)) { const parsed = parseMailtoUrl(url); if (!parsed) return null; @@ -166,6 +173,28 @@ export function parseDeepLink(url: string): DeepLink | null { } } +function isSignInSchemeUrl(url: string): boolean { + return url.trim().toLowerCase().startsWith(`${SIGN_IN_SCHEME}:`); +} + +/** A `bulwarkmail://pair|connect?…` link, parsed; null for any other URL. */ +export function parseSignInLink(url: string | null | undefined): QrLoginPayload | null { + if (!url || !isSignInSchemeUrl(url)) return null; + return parseQrLoginPayload(url); +} + +/** + * Park a tapped sign-in link for the login screen, which takes it (the + * signed-out one, or Add account when someone is signed in). Returns whether + * `url` was one. + */ +export function acceptSignInLink(url: string | null | undefined): boolean { + const payload = parseSignInLink(url); + if (!payload) return false; + setPendingSignInLink(payload); + return true; +} + export interface DeepLinkNavigator { navigation: NavigationContainerRefWithCurrent; // Resolve a message id to its thread (EmailThread needs both). Returns diff --git a/src/navigation/pending-sign-in-link.ts b/src/navigation/pending-sign-in-link.ts new file mode 100644 index 00000000..72046404 --- /dev/null +++ b/src/navigation/pending-sign-in-link.ts @@ -0,0 +1,26 @@ +import { create } from 'zustand'; +import type { QrLoginPayload } from '../lib/oauth'; + +// A tapped `bulwarkmail://` sign-in link arrives before the login screen is +// up (cold start, or signed in with Add account not open yet). It is parked +// here and taken by LoginScreen, which runs it like a scanned code. Taking +// clears it, so one link signs in once however often it is delivered. +interface PendingSignInLinkState { + payload: QrLoginPayload | null; + set: (payload: QrLoginPayload | null) => void; + take: () => QrLoginPayload | null; +} + +export const usePendingSignInLinkStore = create((set, get) => ({ + payload: null, + set: (payload) => set({ payload }), + take: () => { + const payload = get().payload; + if (payload) set({ payload: null }); + return payload; + }, +})); + +export function setPendingSignInLink(payload: QrLoginPayload | null): void { + usePendingSignInLinkStore.getState().set(payload); +} diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx index 12d28cae..49a87a34 100644 --- a/src/screens/LoginScreen.tsx +++ b/src/screens/LoginScreen.tsx @@ -5,7 +5,9 @@ import { useAccountStore } from '../stores/account-store'; import { useLocaleStore } from '../stores/locale-store'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { QrScanModal } from '../components/QrScanModal'; -import { parseQrLoginPayload } from '../lib/oauth'; +import { PasteSignInLinkModal } from '../components/PasteSignInLinkModal'; +import { parseQrLoginPayload, type QrLoginPayload } from '../lib/oauth'; +import { usePendingSignInLinkStore } from '../navigation/pending-sign-in-link'; import { discoverServerForEmail, emailDomain, @@ -69,6 +71,10 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo const [searching, setSearching] = React.useState(false); const [busy, setBusy] = React.useState(null); const [scannerVisible, setScannerVisible] = React.useState(false); + const [pasteVisible, setPasteVisible] = React.useState(false); + // The webmail a sign-in code is being redeemed at, for the waiting screen + // (in add mode the server fields still name the signed-in account's host). + const [pairingWebmailUrl, setPairingWebmailUrl] = React.useState(null); // Addresses typed before on this device (last 5) plus the registry's, so a // returning user taps instead of retyping. Never contains passwords. @@ -131,6 +137,10 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo setScannerVisible(false); return true; } + if (pasteVisible) { + setPasteVisible(false); + return true; + } if (history.length > 0) { goBack(); return true; @@ -142,7 +152,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo return false; }); return () => subscription.remove(); - }, [busy, searching, scannerVisible, history.length, goBack, isAddMode, onCancel]); + }, [busy, searching, scannerVisible, pasteVisible, history.length, goBack, isAddMode, onCancel]); // ── flows ────────────────────────────────────────────── @@ -160,6 +170,27 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo [isAddMode, onLogin], ); + // The webmail hands back the password even when the account needs a second + // factor (a hand-off, or a sign-in code on a server without app passwords); + // finish the sign-in here with a code instead of failing (the user already + // proved the password once). Returns whether it took over. + const continueWithSecondFactor = React.useCallback( + (err: unknown, target: string): boolean => { + const pending = useAuthStore.getState().pendingTotpLogin; + if (!(err instanceof Error && err.name === 'TotpRequiredError' && pending)) return false; + setEmail(pending.username); + setPassword(pending.password); + setServerUrl(pending.serverUrl); + setTotpRequired(true); + setBusy(null); + setNotice(describeLoginError(err, { serverUrl: target, t })); + setHistory((prev) => [...prev, step]); + setStep('password'); + return true; + }, + [step, t], + ); + const runHandoff = React.useCallback( async (target: string) => { setNotice(null); @@ -171,27 +202,13 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo // that's a cancellation, not a failure, so leave them where they were. finishIfSignedIn(wasAuthenticated); } catch (err) { - // The webmail hands back the password even when the account needs a - // second factor; finish the sign-in here with a code instead of - // failing (the user already proved the password once). - const pending = useAuthStore.getState().pendingTotpLogin; - if (err instanceof Error && err.name === 'TotpRequiredError' && pending) { - setEmail(pending.username); - setPassword(pending.password); - setServerUrl(pending.serverUrl); - setTotpRequired(true); - setBusy(null); - setNotice(describeLoginError(err, { serverUrl: target, t })); - setHistory((prev) => [...prev, step]); - setStep('password'); - return; - } + if (continueWithSecondFactor(err, target)) return; setNotice(describeLoginError(err, { serverUrl: target, t })); } finally { setBusy(null); } }, - [finishIfSignedIn, isAddMode, loginViaWebmail, step, t], + [continueWithSecondFactor, finishIfSignedIn, isAddMode, loginViaWebmail, t], ); const handleEmailContinue = React.useCallback(async () => { @@ -275,17 +292,12 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo } }, [email, finishIfSignedIn, isAddMode, login, password, serverInput, serverUrl, totp, totpRequired, t]); - const handleScanned = React.useCallback( - async (data: string) => { + // A sign-in code, however it arrived: scanned, pasted, or a tapped + // `bulwarkmail://` link. + const handleSignInLink = React.useCallback( + async (payload: QrLoginPayload) => { setScannerVisible(false); - const payload = parseQrLoginPayload(data); - if (!payload) { - setNotice({ - title: t('login.mobile.notice_bad_qr', "That code isn't a Bulwark sign-in code"), - detail: t('login.mobile.notice_bad_qr_detail', 'Open Bulwark on the web, then Settings → Security → Link Mobile App to show one.'), - }); - return; - } + setPasteVisible(false); if (payload.kind === 'connect') { // Server-bootstrap code: it carries the address, the user still @@ -296,34 +308,80 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo return; } - // Cross-device pairing code: redeem it for tokens, no browser needed. + // Cross-device pairing code: redeem it, no browser needed. setNotice(null); + setPairingWebmailUrl(payload.webmailUrl); setBusy('pairing'); const wasAuthenticated = useAuthStore.getState().isAuthenticated; try { await loginViaPairing(payload.webmailUrl, payload.code, { addAccount: isAddMode }); finishIfSignedIn(wasAuthenticated); } catch (err) { + if (continueWithSecondFactor(err, payload.webmailUrl)) return; + // The error names its own host: the webmail for a failed redeem, the + // mail server when connecting afterwards failed. setNotice(describeLoginError(err, { serverUrl: payload.webmailUrl, t })); } finally { setBusy(null); + setPairingWebmailUrl(null); } }, - [finishIfSignedIn, isAddMode, loginViaPairing, runHandoff, t], + [continueWithSecondFactor, finishIfSignedIn, isAddMode, loginViaPairing, runHandoff, t], ); + const handleScanned = React.useCallback( + (data: string) => { + setScannerVisible(false); + const payload = parseQrLoginPayload(data); + if (!payload) { + setNotice({ + title: t('login.mobile.notice_bad_qr', "That code isn't a Bulwark sign-in code"), + detail: t('login.mobile.notice_bad_qr_detail', 'Open Bulwark on the web, then Settings → Security → Link Mobile App to show one.'), + }); + return; + } + void handleSignInLink(payload); + }, + [handleSignInLink, t], + ); + + // A tapped sign-in link is parked by App until a login screen is free to + // run it; taking it clears it, so one link signs in once. + const signInLinkPending = usePendingSignInLinkStore((s) => s.payload !== null); + React.useEffect(() => { + if (!signInLinkPending || busy || searching) return; + const payload = usePendingSignInLinkStore.getState().take(); + if (payload) void handleSignInLink(payload); + }, [busy, handleSignInLink, searching, signInLinkPending]); + // ── render ───────────────────────────────────────────── if (busy) { + const pairing = busy === 'pairing'; return ( ); } + const modals = ( + <> + setScannerVisible(false)} + onScanned={handleScanned} + /> + setPasteVisible(false)} + onSubmit={(payload) => void handleSignInLink(payload)} + /> + + ); + const canGoBack = history.length > 0; const shellProps = { onBack: canGoBack ? goBack : undefined, @@ -344,6 +402,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo knownServerUrl={isAddMode ? knownServerUrl : null} notice={chooseNotice} onScan={() => setScannerVisible(true)} + onPaste={() => setPasteVisible(true)} onUseEmail={() => goTo('email')} onUseKnownServer={() => { if (!knownServerUrl) return; @@ -358,11 +417,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo }} /> - setScannerVisible(false)} - onScanned={(data) => void handleScanned(data)} - /> + {modals} ); } @@ -398,6 +453,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo }} onSubmit={handleServerContinue} onScan={() => setScannerVisible(true)} + onPaste={() => setPasteVisible(true)} failedDomain={failedDomain} notice={notice} /> @@ -442,11 +498,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo ) : null} - setScannerVisible(false)} - onScanned={(data) => void handleScanned(data)} - /> + {modals} ); } diff --git a/src/screens/login/ChooseStep.tsx b/src/screens/login/ChooseStep.tsx index fac602aa..b5f9b042 100644 --- a/src/screens/login/ChooseStep.tsx +++ b/src/screens/login/ChooseStep.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { View, Text, Pressable, StyleSheet, Image } from 'react-native'; -import { QrCode, Mail, Plus, Server } from 'lucide-react-native'; +import { ClipboardPaste, QrCode, Mail, Plus, Server } from 'lucide-react-native'; import { spacing, radius, typography, type ThemePalette } from '../../theme/tokens'; import { useColors, useResolvedTheme } from '../../theme/colors'; import type { AccountEntry } from '../../stores/account-store'; @@ -19,6 +19,8 @@ interface ChooseStepProps { /** Host of the signed-in account, offered as a shortcut in add mode. */ knownServerUrl: string | null; onScan: () => void; + /** Paste the sign-in link instead of scanning its code. */ + onPaste: () => void; onUseEmail: () => void; onUseKnownServer: () => void; onManualSetup: () => void; @@ -46,6 +48,7 @@ export default function ChooseStep({ accounts, knownServerUrl, onScan, + onPaste, onUseEmail, onUseKnownServer, onManualSetup, @@ -57,6 +60,16 @@ export default function ChooseStep({ const styles = React.useMemo(() => makeStyles(c), [c]); const t = useLocaleStore((s) => s.t); + // Sits right under the scan tile, wherever that is. + const pasteTile = ( + } + onPress={onPaste} + disabled={disabled} + /> + ); + return ( {isAddMode ? ( @@ -115,14 +128,17 @@ export default function ChooseStep({ disabled={disabled} /> ) : ( - } - onPress={onScan} - disabled={disabled} - /> + <> + } + onPress={onScan} + disabled={disabled} + /> + {pasteTile} + )} {isAddMode && knownServerUrl ? ( - } - onPress={onScan} - disabled={disabled} - /> + <> + } + onPress={onScan} + disabled={disabled} + /> + {pasteTile} + ) : null} diff --git a/src/screens/login/ServerStep.tsx b/src/screens/login/ServerStep.tsx index 4fe74a3b..aadae708 100644 --- a/src/screens/login/ServerStep.tsx +++ b/src/screens/login/ServerStep.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { View, Text, Pressable, StyleSheet } from 'react-native'; -import { Globe, QrCode } from 'lucide-react-native'; +import { ClipboardPaste, Globe, QrCode } from 'lucide-react-native'; import { spacing, typography, type ThemePalette } from '../../theme/tokens'; import { useColors } from '../../theme/colors'; import { Button, Input } from '../../components'; @@ -12,6 +12,7 @@ interface ServerStepProps { onChange: (value: string) => void; onSubmit: () => void; onScan: () => void; + onPaste: () => void; /** Set when we got here because discovery came back empty for this domain. */ failedDomain?: string | null; notice?: { title: string; detail?: string } | null; @@ -27,6 +28,7 @@ export default function ServerStep({ onChange, onSubmit, onScan, + onPaste, failedDomain, notice, }: ServerStepProps) { @@ -67,12 +69,20 @@ export default function ServerStep({ {t('login.mobile.continue', 'Continue')} - - - - {t('login.mobile.server_scan_hint', "If you're not sure, scan a sign-in code instead. It carries the server address.")} - - + + + + + {t('login.mobile.server_scan_hint', "If you're not sure, scan a sign-in code instead. It carries the server address.")} + + + + + + {t('login.mobile.server_paste_hint', 'Have a sign-in link instead? Paste it here.')} + + + ); } From a948738506db0be975438d0f6cd5761f2c8f74f9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 04:23:22 +0200 Subject: [PATCH 020/334] feat: map contacts and groups to android's contacts provider #34 This is the planner for contacts (`src/device-sync/contacts`), the last piece the sync task was waiting for: it turns JSContact cards into raw contacts and data rows, and device edits back into JMAP writes. - Names, nicknames, organizations and titles, emails, phones, postal addresses, websites, birthdays and anniversaries (also without a year), relations, notes and photos map both ways, with their types and custom labels. Group cards become groups, and their members become group memberships. - Device edits upload as patches of the fields whose rows changed; a whole map goes up only where Stalwart needs one. Whatever a card holds that Android can't show (pronouns, online services, keywords, a second note, an address's coordinates) is never touched, so an edit on the phone leaves it intact on the server. - Rows are matched to their card's entries by the keys they carry and, where an editor re-inserted them without (Fossify saves that way), by their values. An entry the editor didn't show is never read as deleted, and the keys Stalwart assigns by position to cards from other clients are followed when they shift. - Contacts in read-only address books are put back when the device changes them. Text beyond what the provider stores is cut on the device and never uploaded. Checked: 151 contacts tests (mappings both ways, one test per edited field, Fossify and AOSP editing, re-keyed cards, merges, groups, read-only books, a full lifecycle), the engine's tests with both real planners, and `npm run typecheck`, `npm test` and `npm run i18n:check` on this commit. --- .../__tests__/contacts/accepted.test.ts | 95 +++++ .../__tests__/contacts/download.test.ts | 243 ++++++++++++ .../__tests__/contacts/fixtures.ts | 142 +++++++ .../__tests__/contacts/groups.test.ts | 203 ++++++++++ src/device-sync/__tests__/contacts/harness.ts | 234 ++++++++++++ .../__tests__/contacts/lifecycle.test.ts | 123 ++++++ .../__tests__/contacts/merge.test.ts | 164 ++++++++ .../__tests__/contacts/units.test.ts | 155 ++++++++ .../__tests__/contacts/upload-editors.test.ts | 358 ++++++++++++++++++ .../__tests__/contacts/upload-fields.test.ts | 229 +++++++++++ .../__tests__/engine/real-planners.test.ts | 221 +++++++++++ src/device-sync/contacts/cells.ts | 76 ++++ src/device-sync/contacts/columns.ts | 113 ++++++ src/device-sync/contacts/diff.ts | 61 +++ src/device-sync/contacts/download.ts | 340 +++++++++++++++++ src/device-sync/contacts/entries.ts | 348 +++++++++++++++++ src/device-sync/contacts/groups.ts | 252 ++++++++++++ src/device-sync/contacts/keys.ts | 65 ++++ src/device-sync/contacts/matching.ts | 131 +++++++ src/device-sync/contacts/members.ts | 149 ++++++++ src/device-sync/contacts/name.ts | 213 +++++++++++ src/device-sync/contacts/ops.ts | 75 ++++ src/device-sync/contacts/photo.ts | 91 +++++ src/device-sync/contacts/planner.ts | 78 ++++ src/device-sync/contacts/postal.ts | 129 +++++++ src/device-sync/contacts/project.ts | 328 ++++++++++++++++ src/device-sync/contacts/types-map.ts | 223 +++++++++++ src/device-sync/contacts/upload.ts | 292 ++++++++++++++ src/device-sync/task.ts | 9 +- 29 files changed, 5132 insertions(+), 8 deletions(-) create mode 100644 src/device-sync/__tests__/contacts/accepted.test.ts create mode 100644 src/device-sync/__tests__/contacts/download.test.ts create mode 100644 src/device-sync/__tests__/contacts/fixtures.ts create mode 100644 src/device-sync/__tests__/contacts/groups.test.ts create mode 100644 src/device-sync/__tests__/contacts/harness.ts create mode 100644 src/device-sync/__tests__/contacts/lifecycle.test.ts create mode 100644 src/device-sync/__tests__/contacts/merge.test.ts create mode 100644 src/device-sync/__tests__/contacts/units.test.ts create mode 100644 src/device-sync/__tests__/contacts/upload-editors.test.ts create mode 100644 src/device-sync/__tests__/contacts/upload-fields.test.ts create mode 100644 src/device-sync/__tests__/engine/real-planners.test.ts create mode 100644 src/device-sync/contacts/cells.ts create mode 100644 src/device-sync/contacts/columns.ts create mode 100644 src/device-sync/contacts/diff.ts create mode 100644 src/device-sync/contacts/download.ts create mode 100644 src/device-sync/contacts/entries.ts create mode 100644 src/device-sync/contacts/groups.ts create mode 100644 src/device-sync/contacts/keys.ts create mode 100644 src/device-sync/contacts/matching.ts create mode 100644 src/device-sync/contacts/members.ts create mode 100644 src/device-sync/contacts/name.ts create mode 100644 src/device-sync/contacts/ops.ts create mode 100644 src/device-sync/contacts/photo.ts create mode 100644 src/device-sync/contacts/planner.ts create mode 100644 src/device-sync/contacts/postal.ts create mode 100644 src/device-sync/contacts/project.ts create mode 100644 src/device-sync/contacts/types-map.ts create mode 100644 src/device-sync/contacts/upload.ts diff --git a/src/device-sync/__tests__/contacts/accepted.test.ts b/src/device-sync/__tests__/contacts/accepted.test.ts new file mode 100644 index 00000000..3635346f --- /dev/null +++ b/src/device-sync/__tests__/contacts/accepted.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, it } from 'vitest'; +import { RawContacts } from '../../android-columns'; +import { sha256Hex } from '../../../lib/sha256'; +import { base64ToBytes } from '../../contacts/photo'; +import { appleCard, JPEG2, probeCard } from './fixtures'; +import { Harness } from './harness'; + +async function patchOf(h: Harness, rawId: number) { + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + return plan.kind === 'upload' && plan.actions[0].kind === 'update' ? plan.actions[0].patch : plan.kind; +} + +describe('contacts planAccepted', () => { + it('writes the new shadow and baselines and clears DIRTY behind the VERSION read before the upload', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + const email = h.rowsByKey(rawId)['emails:k1']._id as number; + h.device.user.updateData(email, { data1: 'new@example.com' }); + const local = await h.contact(rawId); + const plan = h.planner.planUpload(local, h.ctx); + if (plan.kind !== 'upload') throw new Error(plan.kind); + h.send(plan.actions); + const accepted = h.planner.planAccepted(local, h.card(id), h.ctx); + expect(accepted.ops.ops[0]).toEqual({ op: 'assert', table: 'raw_contacts', id: rawId, values: { version: local.version }, expectCount: 1 }); + await h.applyOk(accepted.ops); + const after = await h.contact(rawId); + expect(after.dirty).toBe(false); + expect(after.shadow?.emails).toEqual(h.card(id).emails); + expect(after.rows.find((r) => r.id === email)?.baseline?.data1).toBe('new@example.com'); + expect(h.planner.planDownload(h.card(id), after, h.ctx).effect).toBe('none'); + expect(h.planner.planBaselineHeal(after)).toBeNull(); + }); + + it('keeps DIRTY and uploads only the newer edit when the contact was edited during the upload', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + const rows = h.rowsByKey(rawId); + h.device.user.updateData(rows['emails:k1']._id as number, { data1: 'first@example.com' }); + await h.upload(rawId, () => h.device.user.updateData(rows['phones:k1']._id as number, { data1: '+1 555 0142' })); + expect(h.dirty(rawId)).toBe(true); + expect(await patchOf(h, rawId)).toEqual({ 'phones/k1/number': '+1 555 0142' }); + }); + + it('uploads a second edit of the same field made during the upload, and a revert to the old value', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + const email = h.rowsByKey(rawId)['emails:k1']._id as number; + h.device.user.updateData(email, { data1: 'a1@example.com' }); + await h.upload(rawId, () => h.device.user.updateData(email, { data1: 'a2@example.com' })); + expect(await patchOf(h, rawId)).toEqual({ 'emails/k1/address': 'a2@example.com' }); + + const h2 = new Harness(); + const seeded = await h2.seed(appleCard()); + const email2 = h2.rowsByKey(seeded.rawId)['emails:k1']._id as number; + h2.device.user.updateData(email2, { data1: 'a1@example.com' }); + await h2.upload(seeded.rawId, () => h2.device.user.updateData(email2, { data1: 'anna@example.com' })); + expect(await patchOf(h2, seeded.rawId)).toEqual({ 'emails/k1/address': 'anna@example.com' }); + }); + + it('writes the identity of a create even when the contact changed meanwhile', async () => { + const h = new Harness(); + const rawId = h.device.user.insertContact('usera@example.org', [ + { mimetype: 'vnd.android.cursor.item/name', data1: 'Late Edit', data2: 'Late', data3: 'Edit' }, + ]); + await h.upload(rawId, () => h.device.user.star(rawId)); + const local = await h.contact(rawId); + expect(local.sourceId).toMatch(/^c\//); + expect(local.pending).toBeNull(); + }); + + it('keeps the device photo after uploading it and records the server hash', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(probeCard()); + h.device.user.setPhoto(rawId, JPEG2); + h.devicePhotos.set(rawId, JPEG2); + const fileId = h.rowsByKey(rawId)['media:ph'].data14; + await h.upload(rawId); + const photo = h.rowsByKey(rawId)['media:ph']; + expect(photo.data14).toBe(fileId); + expect(photo.data_sync2).toBe(`sha256:${sha256Hex(base64ToBytes(JPEG2)!)}`); + expect(h.dirty(rawId)).toBe(false); + const local = await h.contact(rawId); + expect(h.planner.planDownload(h.card(id), local, h.ctx).effect).toBe('none'); + expect(h.planner.planBaselineHeal(local)).toBeNull(); + }); + + it('clears the poison marker and the pending create once accepted', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + await h.applyOk({ ref: 'x', ops: [{ op: 'update', table: 'raw_contacts', id: rawId, values: { [RawContacts.SYNC4]: JSON.stringify({ fp: 'old', type: 'invalidProperties', n: 1, until: 1 }) } }] }); + h.device.user.updateData(h.rowsByKey(rawId)['emails:k1']._id as number, { data1: 'z@example.com' }); + await h.upload(rawId); + expect(h.rawContact(rawId)[RawContacts.SYNC4]).toBeNull(); + }); +}); diff --git a/src/device-sync/__tests__/contacts/download.test.ts b/src/device-sync/__tests__/contacts/download.test.ts new file mode 100644 index 00000000..3bdf68f2 --- /dev/null +++ b/src/device-sync/__tests__/contacts/download.test.ts @@ -0,0 +1,243 @@ +import { describe, expect, it } from 'vitest'; +import { Data, MimeType, RawContacts } from '../../android-columns'; +import { sha256Hex } from '../../../lib/sha256'; +import { base64ToBytes } from '../../contacts/photo'; +import type { Row } from '../../types'; +import { appleCard, googleCard, JPEG, probeCard } from './fixtures'; +import { Harness, JMAP } from './harness'; + +/** Cells as text, so the fixtures don't depend on how the provider types a column. */ +function cells(row: Row | undefined, columns: string[]): Record { + if (!row) throw new Error('missing row'); + return Object.fromEntries(columns.map((c) => [c, row[c] === null || row[c] === undefined ? null : String(row[c])])); +} + +const NAME = ['data1', 'data2', 'data3', 'data4', 'data5', 'data6', 'data7', 'data8', 'data9']; +const TYPED = ['data1', 'data2', 'data3']; +const PRIMARY = ['data1', 'data2', 'data3', 'is_primary']; +const POSTAL = ['data1', 'data2', 'data3', 'data4', 'data5', 'data6', 'data7', 'data8', 'data9', 'data10']; +const ORG = ['data1', 'data4', 'data5', 'data6']; + +const photoHash = (b64: string) => `sha256:${sha256Hex(base64ToBytes(b64)!)}`; + +describe('contacts download: golden rows', () => { + it('writes the probe card as one row per entry with its keys', async () => { + const h = new Harness(); + h.names.set('urn:uuid:other-person', 'Other Person'); + const { id, rawId } = await h.seed(probeCard()); + const rows = h.rowsByKey(rawId); + expect(Object.keys(rows).sort()).toEqual([ + 'addresses:home', 'anniversaries:bday', 'anniversaries:wed', 'emails:work1', 'emails:x/y', 'emails:zz9', + 'links:l1', 'media:ph', 'name', 'nicknames:nk', 'notes:n1', 'organizations:o1|titles:t1,r1', 'phones:fax7', + 'phones:p-a', 'relatedTo:urn:uuid:other-person', + ]); + expect(cells(rows.name, NAME)).toEqual({ + data1: 'Dr. Probe Person', data2: 'Probe', data3: 'Person', data4: 'Dr.', data5: null, data6: null, + data7: 'Proob', data8: null, data9: null, + }); + expect(cells(rows['nicknames:nk'], ['data1', 'data2'])).toEqual({ data1: 'Probby', data2: '1' }); + expect(cells(rows['emails:work1'], PRIMARY)).toEqual({ data1: 'probe@work.example', data2: '0', data3: 'Office', is_primary: '1' }); + expect(cells(rows['emails:x/y'], PRIMARY)).toEqual({ data1: 'slash@example.org', data2: '3', data3: null, is_primary: '0' }); + expect(cells(rows['emails:zz9'], PRIMARY)).toEqual({ data1: 'private@example.org', data2: '1', data3: null, is_primary: '0' }); + expect(cells(rows['phones:p-a'], TYPED)).toEqual({ data1: '+49 30 1234', data2: '2', data3: null }); + expect(cells(rows['phones:fax7'], TYPED)).toEqual({ data1: '+49 30 9999', data2: '4', data3: null }); + expect(cells(rows['addresses:home'], POSTAL)).toEqual({ + data1: 'Main St 12, 10115 Berlin, Germany', data2: '1', data3: null, data4: 'Main St 12 4b', data5: null, + data6: null, data7: 'Berlin', data8: null, data9: '10115', data10: 'Germany', + }); + expect(cells(rows['organizations:o1|titles:t1,r1'], ORG)).toEqual({ data1: 'ACME', data4: 'Chief Prober', data5: 'R&D', data6: 'Lead' }); + expect(cells(rows['links:l1'], TYPED)).toEqual({ data1: 'https://probe.example', data2: '5', data3: null }); + expect(cells(rows['anniversaries:bday'], TYPED)).toEqual({ data1: '--03-14', data2: '3', data3: null }); + expect(cells(rows['anniversaries:wed'], TYPED)).toEqual({ data1: '2010-06-01', data2: '1', data3: null }); + expect(cells(rows['relatedTo:urn:uuid:other-person'], TYPED)).toEqual({ data1: 'Other Person', data2: '6', data3: null }); + expect(cells(rows['notes:n1'], ['data1'])).toEqual({ data1: 'first note' }); + expect(rows['media:ph'].data_sync2).toBe(photoHash(JPEG)); + expect(h.device.photo(Number(rows['media:ph'].data14))).toBe(JPEG); + + const raw = h.rawContact(rawId); + expect(raw).toMatchObject({ + [RawContacts.SOURCE_ID]: `${JMAP}/${id}`, + [RawContacts.SYNC1]: `${JMAP}/${h.book}`, + [RawContacts.SYNC3]: null, + [RawContacts.DIRTY]: 0, + }); + expect(Number(raw[RawContacts.RAW_CONTACT_IS_READ_ONLY])).toBe(0); + }); + + it('keeps the photo out of the shadow: its data URI becomes the hash of its bytes', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(probeCard()); + const shadow = JSON.parse(h.rawContact(rawId)[RawContacts.SYNC2] as string); + expect(shadow.media.ph.uri).toBe(photoHash(JPEG)); + expect({ ...shadow, media: undefined, '~memberOf': undefined }).toEqual({ ...h.card(id), media: undefined }); + }); + + it('records each row as stored, so a clean contact needs no baseline heal', async () => { + const h = new Harness(); + for (const card of [probeCard(), appleCard(), googleCard(), { name: { full: 'Madonna' } }]) { + const { rawId } = await h.seed(card); + const local = await h.contact(rawId); + expect(h.planner.planBaselineHeal(local)).toBeNull(); + for (const row of local.rows) if (row.mimetype !== MimeType.PHOTO) expect(row.baseline).not.toBeNull(); + } + }); + + it('maps an Apple card with positional keys, filling the formatted address itself', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + const rows = h.rowsByKey(rawId); + expect(cells(rows['emails:k1'], PRIMARY)).toEqual({ data1: 'anna@example.com', data2: '1', data3: null, is_primary: '1' }); + expect(cells(rows['emails:k2'], PRIMARY)).toEqual({ data1: 'anna@work.example', data2: '2', data3: null, is_primary: '0' }); + expect(cells(rows['phones:k1'], TYPED)).toMatchObject({ data2: '2' }); + expect(cells(rows['phones:k2'], TYPED)).toMatchObject({ data2: '3' }); + expect(cells(rows['phones:k3'], TYPED)).toMatchObject({ data2: '4' }); + expect(cells(rows['addresses:k1'], POSTAL)).toEqual({ + data1: '1 Infinite Loop, 95014 Cupertino, CA, USA', data2: '2', data3: null, data4: '1 Infinite Loop', data5: null, + data6: null, data7: 'Cupertino', data8: 'CA', data9: '95014', data10: 'USA', + }); + expect(cells(rows['anniversaries:k1'], TYPED)).toEqual({ data1: '1980-05-04', data2: '3', data3: null }); + expect(Object.keys(rows).some((k) => k.startsWith('onlineServices'))).toBe(false); + }); + + it('maps a Google card: custom display name, legacy cell phone, labelled link, department list, relation by name', async () => { + const h = new Harness(); + const { rawId } = await h.seed(googleCard()); + const rows = h.rowsByKey(rawId); + expect(cells(rows.name, NAME)).toEqual({ + data1: 'Groß, Günther', data2: 'Günther', data3: 'Groß', data4: 'Prof.', data5: 'Karl', data6: 'PhD', + data7: null, data8: null, data9: null, + }); + expect(cells(rows['nicknames:k1'], ['data1'])).toEqual({ data1: 'Gü' }); + expect(cells(rows['nicknames:k2'], ['data1'])).toEqual({ data1: 'GKG' }); + expect(cells(rows['phones:k1'], TYPED)).toEqual({ data1: '0171 2345678', data2: '2', data3: null }); + expect(cells(rows['links:k1'], TYPED)).toEqual({ data1: 'https://example.org/~guenther', data2: '0', data3: 'profile' }); + expect(cells(rows['organizations:k1|titles:k1,'], ORG)).toEqual({ data1: 'Uni Beispiel', data4: 'Professor', data5: 'Informatik, AG Sync', data6: null }); + expect(cells(rows['relatedTo:Maria Groß'], TYPED)).toEqual({ data1: 'Maria Groß', data2: '14', data3: null }); + expect(cells(rows['anniversaries:k1'], TYPED)).toEqual({ data1: '--08-15', data2: '1', data3: null }); + }); + + it('writes a name with only `full` as the display name and lets the provider split it', async () => { + const h = new Harness(); + const { rawId } = await h.seed({ name: { full: 'Madonna' } }); + expect(cells(h.rowsByKey(rawId).name, ['data1', 'data2', 'data3'])).toEqual({ data1: 'Madonna', data2: 'Madonna', data3: null }); + }); + + it('writes text longer than the provider keeps already cut, and never uploads the cut column', async () => { + const h = new Harness(); + const long = 'x'.repeat(12_000); + const { id, rawId } = await h.seed({ name: { full: 'Long Note' }, notes: { n1: { note: long } }, phones: { p1: { number: '1'.repeat(1200) } } }); + const rows = h.rowsByKey(rawId); + expect((rows['notes:n1'].data1 as string).length).toBe(10 * 1024); + expect((rows['phones:p1'].data1 as string).length).toBe(1000); + expect(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).effect).toBe('none'); + h.device.user.updateData(rows['notes:n1']._id as number, { data1: `${rows['notes:n1'].data1 as string}!` }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx).kind).toBe('clean'); + }); + + it('marks a card of read-only books read-only', async () => { + const h = new Harness(); + h.readOnlyBooks.add(h.book); + const { rawId } = await h.seed(appleCard()); + expect(Number(h.rawContact(rawId)[RawContacts.RAW_CONTACT_IS_READ_ONLY])).toBe(1); + }); +}); + +describe('contacts download: echoes and updates', () => { + it.each([['probe', probeCard], ['apple', appleCard], ['google', googleCard]])('writes nothing for an unchanged %s card', async (_n, make) => { + const h = new Harness(); + const { id, rawId } = await h.seed(make()); + const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); + expect(plan).toMatchObject({ effect: 'none', writes: 0, conflicts: 0 }); + expect(plan.ops.ops).toEqual([]); + }); + + it('writes nothing when the shadow itself comes back (a re-plan after a group change)', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + const local = await h.contact(rawId); + const plan = h.planner.planDownload(local.shadow!, local, h.ctx); + expect(plan.effect).toBe('none'); + }); + + it('rewrites only the row of the entry the server changed, behind VERSION and DIRTY', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + h.server.serverUpdate('ContactCard', JMAP, id, { 'emails/k2/address': 'anna@new-work.example' }); + const local = await h.contact(rawId); + const plan = h.planner.planDownload(h.card(id), local, h.ctx); + const [assert, raw, ...data] = plan.ops.ops; + expect(assert).toEqual({ op: 'assert', table: 'raw_contacts', id: rawId, values: { version: local.version, dirty: 0 }, expectCount: 1 }); + expect(raw).toMatchObject({ op: 'update', table: 'raw_contacts', values: { [RawContacts.SYNC2]: expect.any(String) } }); + expect(data).toHaveLength(1); + expect(data[0]).toMatchObject({ op: 'update', table: 'data', expectCount: 1, values: { data1: 'anna@new-work.example', data_sync1: 'emails:k2' } }); + await h.applyOk(plan.ops); + expect(h.rowsByKey(rawId)['emails:k2'].data1).toBe('anna@new-work.example'); + expect(h.dirty(rawId)).toBe(false); + }); + + it('inserts entries the server added and deletes the ones it removed', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + h.server.serverUpdate('ContactCard', JMAP, id, { 'phones/k3': null, 'links': { k1: { uri: 'https://anna.example' } } }); + await h.download(id); + const rows = h.rowsByKey(rawId); + expect(rows['phones:k3']).toBeUndefined(); + expect(cells(rows['links:k1'], TYPED)).toEqual({ data1: 'https://anna.example', data2: '7', data3: null }); + expect(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).effect).toBe('none'); + }); + + it('follows keys Stalwart shifted (a CardDAV insert at the front) by content, with no rewrite of the rows', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + const before = h.rowsByKey(rawId); + h.server.serverUpdate('ContactCard', JMAP, id, { + emails: { + k1: { address: 'first@example.com' }, + k2: { address: 'anna@example.com', contexts: { private: true }, pref: 1 }, + k3: { address: 'anna@work.example', contexts: { work: true } }, + }, + }); + const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); + const data = plan.ops.ops.filter((o) => o.op !== 'assert' && o.op !== 'syncState' && o.table === 'data'); + expect(data).toHaveLength(3); + const renames = data.filter((o) => o.op === 'update'); + for (const r of renames) expect(Object.keys((r as { values: object }).values).sort()).toEqual(['data_sync1', 'data_sync3']); + await h.applyOk(plan.ops); + const rows = h.rowsByKey(rawId); + expect(rows['emails:k2']._id).toBe(before['emails:k1']._id); + expect(rows['emails:k3']._id).toBe(before['emails:k2']._id); + expect(rows['emails:k1'].data1).toBe('first@example.com'); + }); + + it('fails as a whole when the user edits the contact between read and write', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + h.server.serverUpdate('ContactCard', JMAP, id, { 'notes/k1/note': 'from the server' }); + const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); + const phone = h.rowsByKey(rawId)['phones:k1']._id as number; + h.device.beforeNextBatch(() => h.device.user.updateData(phone, { [Data.DATA1]: '+1 555 0199' })); + const res = await h.apply(plan.ops); + expect(res).toMatchObject({ ok: false, reason: 'assert' }); + expect(h.rowsByKey(rawId)['notes:k1'].data1).toBe('Met at WWDC'); + }); + + it('adopts its own create whose identity was never written, keeping the local rows', async () => { + const h = new Harness(); + const rawId = h.device.user.insertContact('usera@example.org', [ + { [Data.MIMETYPE]: MimeType.STRUCTURED_NAME, [Data.DATA1]: 'New Person', [Data.DATA2]: 'New', [Data.DATA3]: 'Person' }, + { [Data.MIMETYPE]: MimeType.EMAIL, [Data.DATA1]: 'new@example.org', [Data.DATA2]: 1 }, + ]); + const plan = await h.planUpload(rawId); + if (plan.kind !== 'upload' || plan.actions[0].kind !== 'create') throw new Error(`expected a create, got ${plan.kind}`); + const cardId = h.send(plan.actions)!; + // The response was lost: the next download finds the card and the pending uid. + const local = await h.contact(rawId); + const adopt = h.planner.planDownload(h.card(cardId), local, h.ctx); + expect(adopt.effect).toBe('update'); + expect(adopt.stillDirty).toBe(true); + await h.applyOk(adopt.ops); + const after = await h.contact(rawId); + expect(after).toMatchObject({ sourceId: `${JMAP}/${cardId}`, pending: null, dirty: true }); + expect(h.planner.planUpload(after, h.ctx).kind).toBe('clean'); + }); +}); diff --git a/src/device-sync/__tests__/contacts/fixtures.ts b/src/device-sync/__tests__/contacts/fixtures.ts new file mode 100644 index 00000000..ba39dadd --- /dev/null +++ b/src/device-sync/__tests__/contacts/fixtures.ts @@ -0,0 +1,142 @@ +/** + * Server cards for the contacts planner tests. `probeCard` is what the local + * Stalwart returned for a card created over JMAP with client keys (live probe + * output); the others are shaped like cards synced in over CardDAV from + * Apple and Google clients, whose keys Stalwart generates by position. + */ + +/** A 1×1 JPEG. */ +export const JPEG = '/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wgALCAABAAEBAREA/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQABPxA='; +/** Another picture's bytes (any base64 does for the fakes). */ +export const JPEG2 = '/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAACf/EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8AKp//2Q=='; + +export function probeCard(): Record { + return { + uid: 'urn:uuid:probe-1790463389675', + kind: 'individual', + name: { + full: 'Dr. Probe Person', + phoneticSystem: 'ipa', + components: [ + { kind: 'title', value: 'Dr.' }, + { kind: 'given', value: 'Probe', phonetic: 'Proob' }, + { kind: 'surname', value: 'Person' }, + ], + isOrdered: true, + }, + nicknames: { nk: { name: 'Probby' } }, + emails: { + work1: { address: 'probe@work.example', contexts: { work: true }, pref: 1, label: 'Office' }, + 'x/y': { address: 'slash@example.org' }, + zz9: { address: 'private@example.org', contexts: { private: true } }, + }, + phones: { + 'p-a': { number: '+49 30 1234', contexts: { private: true }, features: { voice: true, mobile: true } }, + fax7: { number: '+49 30 9999', contexts: { work: true }, features: { fax: true } }, + }, + addresses: { + home: { + contexts: { private: true }, + coordinates: 'geo:52.5,13.4', + timeZone: 'Europe/Berlin', + countryCode: 'DE', + full: 'Main St 12, 10115 Berlin, Germany', + components: [ + { kind: 'name', value: 'Main St' }, + { kind: 'separator', value: ' ' }, + { kind: 'number', value: '12' }, + { kind: 'locality', value: 'Berlin' }, + { kind: 'postcode', value: '10115' }, + { kind: 'country', value: 'Germany' }, + { kind: 'apartment', value: '4b' }, + ], + isOrdered: true, + }, + }, + organizations: { o1: { name: 'ACME', units: [{ name: 'R&D' }] } }, + titles: { + t1: { name: 'Chief Prober', kind: 'title', organizationId: 'o1' }, + r1: { name: 'Lead', kind: 'role', organizationId: 'o1' }, + }, + links: { l1: { uri: 'https://probe.example', contexts: { work: true } } }, + onlineServices: { im1: { uri: 'xmpp:probe@example.org', service: 'XMPP' } }, + anniversaries: { + bday: { date: { '@type': 'PartialDate', month: 3, day: 14 }, kind: 'birth' }, + wed: { date: { '@type': 'PartialDate', year: 2010, month: 6, day: 1 }, kind: 'wedding' }, + }, + relatedTo: { 'urn:uuid:other-person': { relation: { friend: true } } }, + notes: { n1: { note: 'first note' }, n2: { note: 'second note' } }, + media: { ph: { uri: `data:image/jpeg;base64,${JPEG}`, kind: 'photo', mediaType: 'image/jpeg' } }, + keywords: { vip: true }, + personalInfo: { pi1: { value: 'probing', kind: 'hobby' } }, + preferredLanguages: { lang1: { language: 'de', pref: 1 } }, + speakToAs: { grammaticalGender: 'neuter' }, + }; +} + +/** Apple Contacts over CardDAV: no PROP-IDs, so `k1…kN`; an address without `full`. */ +export function appleCard(): Record { + return { + uid: 'urn:uuid:5F2C9B1E-APPLE', + kind: 'individual', + prodId: '-//Apple Inc.//iPhone OS 18.0//EN', + name: { + components: [{ kind: 'given', value: 'Anna' }, { kind: 'surname', value: 'Apfel' }], + isOrdered: true, + full: 'Anna Apfel', + }, + emails: { + k1: { address: 'anna@example.com', contexts: { private: true }, pref: 1 }, + k2: { address: 'anna@work.example', contexts: { work: true } }, + }, + phones: { + k1: { number: '+1 555 0100', features: { mobile: true, voice: true } }, + k2: { number: '+1 555 0101', contexts: { work: true }, features: { voice: true } }, + k3: { number: '+1 555 0102', contexts: { work: true }, features: { fax: true } }, + }, + addresses: { + k1: { + components: [ + { kind: 'name', value: '1 Infinite Loop' }, + { kind: 'locality', value: 'Cupertino' }, + { kind: 'region', value: 'CA' }, + { kind: 'postcode', value: '95014' }, + { kind: 'country', value: 'USA' }, + ], + isOrdered: true, + contexts: { work: true }, + }, + }, + anniversaries: { k1: { kind: 'birth', date: { '@type': 'PartialDate', year: 1980, month: 5, day: 4 } } }, + notes: { k1: { note: 'Met at WWDC' } }, + onlineServices: { k1: { service: 'Skype', user: 'anna.apfel' } }, + keywords: { friends: true }, + }; +} + +/** Google Contacts over CardDAV: a legacy `cell` feature, a custom display name, two nicknames. */ +export function googleCard(): Record { + return { + uid: 'google-8a7d2f', + kind: 'individual', + name: { + components: [ + { kind: 'title', value: 'Prof.' }, + { kind: 'given', value: 'Günther' }, + { kind: 'given2', value: 'Karl' }, + { kind: 'surname', value: 'Groß' }, + { kind: 'credential', value: 'PhD' }, + ], + isOrdered: true, + full: 'Groß, Günther', + }, + nicknames: { k1: { name: 'Gü' }, k2: { name: 'GKG' } }, + phones: { k1: { number: '0171 2345678', features: { cell: true } } }, + links: { k1: { uri: 'https://example.org/~guenther', label: 'profile' } }, + organizations: { k1: { name: 'Uni Beispiel', units: [{ name: 'Informatik' }, { name: 'AG Sync' }] } }, + titles: { k1: { name: 'Professor', kind: 'title', organizationId: 'k1' } }, + relatedTo: { 'Maria Groß': { relation: { spouse: true } } }, + anniversaries: { k1: { kind: 'wedding', date: { '@type': 'PartialDate', month: 8, day: 15 } } }, + speakToAs: { grammaticalGender: 'masculine' }, + }; +} diff --git a/src/device-sync/__tests__/contacts/groups.test.ts b/src/device-sync/__tests__/contacts/groups.test.ts new file mode 100644 index 00000000..74ffb7dd --- /dev/null +++ b/src/device-sync/__tests__/contacts/groups.test.ts @@ -0,0 +1,203 @@ +import { describe, expect, it } from 'vitest'; +import { Data, Groups, MimeType } from '../../android-columns'; +import type { ContactCardWire, OpGroup, UploadPlan } from '../../planner'; +import { appleCard, googleCard } from './fixtures'; +import { Harness, JMAP } from './harness'; + +function actionsOf(plan: UploadPlan) { + if (plan.kind !== 'upload') throw new Error(`expected an upload, got ${plan.kind}`); + return plan.actions; +} + +function opsOf(plan: UploadPlan): OpGroup { + if (plan.kind === 'upload' || plan.kind === 'skip') throw new Error(`expected ops, got ${plan.kind}`); + return plan.ops; +} + +async function localGroup(h: Harness, groupId: string) { + const g = (await h.groups()).find((x) => x.sourceId === `${JMAP}/${groupId}`); + if (!g) throw new Error(`no group row for ${groupId}`); + return g; +} + +/** A group card holding Anna (apple card), both on the device. */ +async function withGroup(members: Record = { [appleCard().uid as string]: true }) { + const h = new Harness(); + const gid = h.addCard({ uid: 'urn:uuid:group-friends', kind: 'group', name: { full: 'Friends' }, members }); + await h.download(gid); + const { id, rawId } = await h.seed(appleCard()); + return { h, gid, id, rawId }; +} + +describe('contacts groups: download', () => { + it('writes a group card as a visible group row, and nothing for its echo', async () => { + const { h, gid } = await withGroup(); + const row = h.device.rows('groups', 'sourceid = ?', [`${JMAP}/${gid}`])[0]; + expect(row).toMatchObject({ [Groups.TITLE]: 'Friends', [Groups.GROUP_VISIBLE]: 1, [Groups.DIRTY]: 0 }); + const plan = h.planner.planGroupDownload(h.card(gid), await localGroup(h, gid), h.ctx); + expect(plan).toMatchObject({ effect: 'none', ops: { ops: [] } }); + }); + + it('writes one membership row per group card that lists the contact', async () => { + const { h, gid, rawId } = await withGroup(); + expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); + const local = await h.contact(rawId); + expect(local.rows.find((r) => r.mimetype === MimeType.GROUP_MEMBERSHIP)?.key).toBe(`members:${JMAP}/${gid}`); + expect(local.shadow?.['~memberOf']).toEqual([`${JMAP}/${gid}`]); + }); + + it('leaves memberships alone when the engine has no group index', async () => { + const h = new Harness(); + h.groupIndex = false; + const gid = h.addCard({ uid: 'urn:uuid:g', kind: 'group', name: { full: 'G' }, members: { [appleCard().uid as string]: true } }); + await h.download(gid); + const { rawId } = await h.seed(appleCard()); + expect(h.membershipGroups(rawId)).toEqual([]); + expect((await h.contact(rawId)).shadow?.['~memberOf']).toBeUndefined(); + }); + + it('follows the group card when members change on the server (a re-plan of the unchanged contact)', async () => { + const { h, gid, rawId } = await withGroup(); + h.server.serverUpdate('ContactCard', JMAP, gid, { [`members/${appleCard().uid as string}`]: null }); + await h.download(gid); + let local = await h.contact(rawId); + await h.applyOk(h.planner.planDownload(local.shadow!, local, h.ctx).ops); + expect(h.membershipGroups(rawId)).toEqual([]); + h.server.serverUpdate('ContactCard', JMAP, gid, { members: { [appleCard().uid as string]: true } }); + await h.download(gid); + local = await h.contact(rawId); + await h.applyOk(h.planner.planDownload(local.shadow!, local, h.ctx).ops); + expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); + }); + + it('lets the server win when both sides renamed the group', async () => { + const { h, gid } = await withGroup(); + const g = await localGroup(h, gid); + h.device.user.updateGroup(g.groupId, { [Groups.TITLE]: 'Mates' }); + h.server.serverUpdate('ContactCard', JMAP, gid, { 'name/full': 'Pals' }); + const plan = h.planner.planGroupDownload(h.card(gid), await localGroup(h, gid), h.ctx); + expect(plan.conflicts).toBe(1); + await h.applyOk(plan.ops); + expect((await localGroup(h, gid)).title).toBe('Pals'); + }); +}); + +describe('contacts groups: membership edits', () => { + it('adds the contact to a group card with `members/: true`, keeping it dirty until the server has it', async () => { + const h = new Harness(); + const other = h.addCard({ uid: 'urn:uuid:g2', kind: 'group', name: { full: 'Work' }, members: { 'urn:uuid:someone': true } }); + await h.download(other); + const { rawId } = await h.seed(appleCard()); + const g = await localGroup(h, other); + h.device.user.addToGroup(rawId, g.groupId); + expect((await h.upload(rawId)).kind).toBe('clean'); + expect(h.dirty(rawId)).toBe(true); + const actions = h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx); + expect(actions).toEqual([{ kind: 'update', id: other, patch: { [`members/${appleCard().uid as string}`]: true } }]); + h.send(actions); + const accepted = h.planner.planGroupAccepted(g, h.card(other), h.ctx); + await h.applyOk(accepted.ops); + await h.upload(rawId); + expect(h.dirty(rawId)).toBe(false); + expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([]); + }); + + it('sends the whole map for the first member of an empty group', async () => { + const h = new Harness(); + const empty = h.addCard({ uid: 'urn:uuid:g3', kind: 'group', name: { full: 'Empty' } }); + await h.download(empty); + const { rawId } = await h.seed(appleCard()); + h.device.user.addToGroup(rawId, (await localGroup(h, empty)).groupId); + const actions = h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx); + expect(actions).toEqual([{ kind: 'update', id: empty, patch: { members: { [appleCard().uid as string]: true } } }]); + }); + + it('removes the contact with `members/: null` when its membership row was deleted in place', async () => { + const { h, gid, rawId } = await withGroup({ [appleCard().uid as string]: true, 'urn:uuid:someone': true }); + const membership = (await h.contact(rawId)).rows.find((r) => r.mimetype === MimeType.GROUP_MEMBERSHIP)!; + h.device.user.deleteData(membership.id); + const actions = h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx); + expect(actions).toEqual([{ kind: 'update', id: gid, patch: { [`members/${appleCard().uid as string}`]: null } }]); + }); + + it('uploads nothing when an editor re-inserted the same memberships without keys', async () => { + const { h, rawId } = await withGroup(); + const rows = h.device.rows('data', 'raw_contact_id = ?', [rawId]).filter((r) => r[Data.MIMETYPE] === MimeType.GROUP_MEMBERSHIP); + h.device.user.fossifySave(rawId, null, rows.map((r) => ({ [Data.MIMETYPE]: MimeType.GROUP_MEMBERSHIP, data1: r.data1 }))); + expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([]); + }); + + it('does not take a server-side addition for a local removal while the contact is dirty', async () => { + const { h, gid } = await withGroup({}); + const { rawId } = await h.seed(googleCard()); + h.device.user.updateData(h.rowsByKey(rawId)['phones:k1']._id as number, { data1: '0171 999' }); + h.server.serverUpdate('ContactCard', JMAP, gid, { members: { [googleCard().uid as string]: true } }); + await h.download(gid); + const local = await h.contact(rawId); + await h.applyOk(h.planner.planDownload(local.shadow!, local, h.ctx).ops); + expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); + expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([]); + }); +}); + +describe('contacts groups: device edits of groups', () => { + it('uploads a rename as `name/full`', async () => { + const { h, gid } = await withGroup(); + const g = await localGroup(h, gid); + h.device.user.updateGroup(g.groupId, { [Groups.TITLE]: 'Close friends' }); + const plan = h.planner.planGroupUpload(await localGroup(h, gid), h.ctx); + expect(plan).toEqual({ kind: 'upload', actions: [{ kind: 'update', id: gid, patch: { 'name/full': 'Close friends' } }] }); + h.send(actionsOf(plan)); + const accepted = h.planner.planGroupAccepted(await localGroup(h, gid), h.card(gid), h.ctx); + await h.applyOk(accepted.ops); + expect(await localGroup(h, gid)).toMatchObject({ dirty: false, title: 'Close friends' }); + expect(h.planner.planGroupDownload(h.card(gid), await localGroup(h, gid), h.ctx).effect).toBe('none'); + }); + + it('claims and creates a group made on the device', async () => { + const h = new Harness(); + const groupId = h.device.user.insertGroup('usera@example.org', 'Book club'); + const decode = async () => (await h.groups()).find((g) => g.groupId === groupId)!; + const claim = h.planner.planGroupUpload(await decode(), h.ctx); + expect(claim.kind).toBe('claim'); + await h.applyOk(opsOf(claim)); + const pending = (await decode()).pending!; + const plan = h.planner.planGroupUpload(await decode(), h.ctx); + expect(plan).toEqual({ + kind: 'upload', + actions: [{ + kind: 'create', uid: pending.uid, collectionId: h.book, + object: { '@type': 'Card', version: '1.0', uid: pending.uid, kind: 'group', addressBookIds: { [h.book]: true }, name: { full: 'Book club' } }, + }], + }); + const id = h.send(actionsOf(plan))!; + await h.applyOk(h.planner.planGroupAccepted(await decode(), h.card(id), h.ctx).ops); + expect(await decode()).toMatchObject({ sourceId: `${JMAP}/${id}`, dirty: false, pending: null }); + }); + + it('destroys a group deleted on the device', async () => { + const { h, gid } = await withGroup(); + h.device.user.deleteGroup((await localGroup(h, gid)).groupId); + expect(h.planner.planGroupUpload(await localGroup(h, gid), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'destroy', id: gid, uid: 'urn:uuid:group-friends' }], + }); + }); + + it('reverts a rename of a read-only group', async () => { + const { h, gid } = await withGroup(); + h.readOnlyBooks.add(h.book); + const g = await localGroup(h, gid); + h.device.user.updateGroup(g.groupId, { [Groups.TITLE]: 'Mine now' }); + const plan = h.planner.planGroupUpload(await localGroup(h, gid), h.ctx); + expect(plan.kind).toBe('revert'); + await h.applyOk(opsOf(plan)); + expect(await localGroup(h, gid)).toMatchObject({ title: 'Friends', dirty: false }); + }); + + it('deletes the group row for a server delete', async () => { + const { h, gid, rawId } = await withGroup(); + await h.applyOk(h.planner.planGroupLocalDelete(await localGroup(h, gid))); + expect(h.device.rows('groups', 'sourceid = ?', [`${JMAP}/${gid}`])).toEqual([]); + expect(h.membershipGroups(rawId)).toEqual([]); + }); +}); diff --git a/src/device-sync/__tests__/contacts/harness.ts b/src/device-sync/__tests__/contacts/harness.ts new file mode 100644 index 00000000..095a1c9b --- /dev/null +++ b/src/device-sync/__tests__/contacts/harness.ts @@ -0,0 +1,234 @@ +/** + * Test rig for the contacts planner: the fake device providers, the fake + * Stalwart, and a ContactsContext wired between them. Plans are applied the + * way the engine applies them (one op group per batch); uploads go through + * the fake server's patch handling and come back through its normalisation. + */ +import { GroupMembership, Groups, RawContacts } from '../../android-columns'; +import { makeKeyMinter, parseObjectRef, uuidFrom } from '../../common/ids'; +import { contactsPlanner } from '../../contacts/planner'; +import type { + ContactCardWire, + ContactsContext, + LocalContact, + LocalGroup, + OpGroup, + UploadAction, + UploadPlan, +} from '../../planner'; +import { CONTACTS_AUTHORITY, type BatchResult, type ProviderPort, type Row } from '../../types'; +import { FakeJmapServer } from '../fakes/fake-jmap-server'; +import { FakeDeviceProviders } from '../fakes/fake-provider'; + +export const ACCOUNT = 'usera@example.org'; +export const JMAP = 'c'; + +export function seeded(seed = 7): () => number { + return () => { + seed = (seed * 1103515245 + 12345) % 2147483648; + return seed / 2147483648; + }; +} + +function rowsOf(res: { columns: string[]; rows: unknown[][] }): Row[] { + return res.rows.map((cells) => Object.fromEntries(res.columns.map((c, i) => [c, cells[i]])) as Row); +} + +export class Harness { + readonly device = new FakeDeviceProviders(); + readonly port: ProviderPort = this.device.port(ACCOUNT, CONTACTS_AUTHORITY); + readonly server = new FakeJmapServer(); + readonly book: string; + readonly planner = contactsPlanner; + /** Base64 of blob-backed photos by blobId, and the device photo per raw contact. */ + readonly blobs = new Map(); + readonly devicePhotos = new Map(); + readonly names = new Map(); + readonly readOnlyBooks = new Set(); + readonly unselected = new Set(); + /** When false the context has no `groupsOf` (memberships not synced). */ + groupIndex = true; + readonly ctx: ContactsContext; + + constructor() { + this.server.addAccount(JMAP, { name: ACCOUNT }); + this.book = this.server.addAddressBook(JMAP, { name: 'Personal' }); + const random = seeded(); + const mintKey = makeKeyMinter(random); + const self = this; + this.ctx = { + jmapAccountId: JMAP, + now: 1_800_000_000_000, + mintKey, + mintUid: () => `urn:uuid:${uuidFrom(random)}`, + selectedCollections: (card) => + Object.keys(card.addressBookIds ?? {}).map((id) => `${JMAP}/${id}`).filter((k) => !self.unselected.has(k)), + isReadOnly: (card) => Object.keys(card.addressBookIds ?? {}).every((id) => self.readOnlyBooks.has(id)), + isSelected: (key) => !self.unselected.has(key), + createTarget: () => `${JMAP}/${self.book}`, + photoBytes: (entry) => (entry.blobId ? self.blobs.get(entry.blobId) ?? null : null), + devicePhoto: (id) => self.devicePhotos.get(id) ?? null, + nameForUid: (uid) => self.names.get(uid) ?? null, + groupRowIdBySourceId: (sourceId) => { + const row = self.device.rows('groups', 'sourceid = ?', [sourceId])[0]; + return row ? Number(row[Groups._ID]) : null; + }, + groupsOf(uid: string) { + return self.server + .all('ContactCard', JMAP) + .filter((c) => c.kind === 'group' && (c.members as Record | undefined)?.[uid] === true) + .map((c) => `${JMAP}/${c.id as string}`); + }, + }; + } + + /** The context as the engine may build it without a group index. */ + context(): ContactsContext { + if (this.groupIndex) return this.ctx; + const { groupsOf: _drop, ...rest } = this.ctx; + return rest as ContactsContext; + } + + async apply(group: OpGroup): Promise { + if (!group.ops.length) return { ok: true, results: [] }; + return this.port.applyBatch(group.ops); + } + + async applyOk(group: OpGroup): Promise { + const res = await this.apply(group); + if (!res.ok) throw new Error(`batch failed: ${res.reason} ${res.message}`); + } + + async contacts(): Promise { + const raw = rowsOf(await this.port.query({ table: 'raw_contacts', columns: [...this.planner.rawContactColumns] })); + const data = rowsOf(await this.port.query({ table: 'data', columns: [...this.planner.dataColumns] })); + return raw.map((r) => this.planner.decodeContact(r, data.filter((d) => d.raw_contact_id === r._id))); + } + + async contact(rawContactId: number): Promise { + const found = (await this.contacts()).find((c) => c.rawContactId === rawContactId); + if (!found) throw new Error(`no raw contact ${rawContactId}`); + return found; + } + + async bySource(cardId: string): Promise { + return (await this.contacts()).find((c) => c.sourceId === `${JMAP}/${cardId}`) ?? null; + } + + async groups(): Promise { + const rows = rowsOf(await this.port.query({ table: 'groups', columns: [...this.planner.groupColumns] })); + return rows.map((r) => this.planner.decodeGroup(r)); + } + + card(id: string): ContactCardWire { + const card = this.server.get('ContactCard', JMAP, id); + if (!card) throw new Error(`no card ${id}`); + return card as ContactCardWire; + } + + addCard(card: Record): string { + return this.server.addCard(JMAP, { addressBookIds: { [this.book]: true }, ...card }); + } + + /** Downloads a server card into its local contact (or a new one); returns the raw contact id. */ + async download(cardId: string): Promise { + const card = this.card(cardId); + const local = await this.bySource(cardId); + const plan = card.kind === 'group' + ? this.planner.planGroupDownload(card, (await this.groups()).find((g) => g.sourceId === `${JMAP}/${cardId}`) ?? null, this.context()) + : this.planner.planDownload(card, local, this.context()); + await this.applyOk(plan.ops); + if (card.kind === 'group') return -1; + const after = await this.bySource(cardId); + if (!after) throw new Error('download wrote no contact'); + const heal = this.planner.planBaselineHeal(after); + if (heal) await this.applyOk(heal); + return after.rawContactId; + } + + /** A card on the server and on the device, clean. */ + async seed(card: Record): Promise<{ id: string; rawId: number }> { + const id = this.addCard(card); + return { id, rawId: await this.download(id) }; + } + + /** Sends upload actions to the fake server the way the engine would; returns the touched card id. */ + send(actions: UploadAction[]): string | null { + let touched: string | null = null; + for (const a of actions) { + if (a.kind === 'update') { + this.server.serverUpdate('ContactCard', JMAP, a.id, a.patch); + touched = a.id; + } else if (a.kind === 'create') { + touched = this.server.addCard(JMAP, a.object as Record); + } else if (a.id) { + this.server.serverDestroy('ContactCard', JMAP, a.id); + } + } + return touched; + } + + /** Plans an upload for a contact, claiming it first when it is new. */ + async planUpload(rawContactId: number): Promise> { + let plan = this.planner.planUpload(await this.contact(rawContactId), this.context()); + if (plan.kind === 'claim') { + await this.applyOk(plan.ops); + plan = this.planner.planUpload(await this.contact(rawContactId), this.context()); + } + return plan; + } + + /** + * One upload round for a contact: plan, send, re-read the card, apply the + * accepted plan (or its keep-dirty fallback). Returns the plan. + */ + async upload(rawContactId: number, between?: () => void): Promise> { + const plan = await this.planUpload(rawContactId); + const local = await this.contact(rawContactId); + if (plan.kind === 'upload') { + const touched = this.send(plan.actions); + between?.(); + if (local.deleted) { + const accepted = this.planner.planAccepted(local, {} as ContactCardWire, this.context()); + await this.applyOk(accepted.ops); + return plan; + } + const server = this.card(touched!); + const accepted = this.planner.planAccepted(local, server, this.context()); + const res = await this.apply(accepted.ops); + if (!res.ok) await this.applyOk(accepted.keepDirtyOps); + } else if (plan.kind !== 'skip') { + between?.(); + await this.applyOk(plan.ops); + } + return plan; + } + + cardIdOf(local: LocalContact): string { + return parseObjectRef(local.sourceId)!.id; + } + + /** Data rows of a raw contact as the provider holds them, keyed by DATA_SYNC1 (or `row:`). */ + rowsByKey(rawContactId: number): Record { + const out: Record = {}; + for (const r of this.device.rows('data', 'raw_contact_id = ?', [rawContactId])) { + out[(r.data_sync1 as string | null) ?? `row:${r._id}`] = r; + } + return out; + } + + rawContact(rawContactId: number): Row { + return this.device.row('raw_contacts', rawContactId)!; + } + + dirty(rawContactId: number): boolean { + return Number(this.rawContact(rawContactId)[RawContacts.DIRTY]) === 1; + } + + membershipGroups(rawContactId: number): string[] { + return this.device + .rows('data', 'raw_contact_id = ? AND mimetype = ?', [rawContactId, 'vnd.android.cursor.item/group_membership']) + .map((r) => r[GroupMembership.GROUP_SOURCE_ID] as string) + .sort(); + } +} diff --git a/src/device-sync/__tests__/contacts/lifecycle.test.ts b/src/device-sync/__tests__/contacts/lifecycle.test.ts new file mode 100644 index 00000000..94c414ff --- /dev/null +++ b/src/device-sync/__tests__/contacts/lifecycle.test.ts @@ -0,0 +1,123 @@ +import { describe, expect, it } from 'vitest'; +import { Data, MimeType } from '../../android-columns'; +import type { LocalContact } from '../../planner'; +import { JPEG, probeCard } from './fixtures'; +import { Harness, JMAP } from './harness'; + +/** The engine's view after every write: a clean contact whose rows the server card describes. */ +async function expectSettled(h: Harness, id: string, rawId: number): Promise { + const local = await h.contact(rawId); + expect(local.dirty).toBe(false); + expect(h.planner.planDownload(h.card(id), local, h.ctx).effect).toBe('none'); + expect(h.planner.planBaselineHeal(local)).toBeNull(); + return local; +} + +describe('contacts lifecycle', () => { + it('settles after device edits, an editor rewrite and server edits in turn', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(probeCard()); + let rows = h.rowsByKey(rawId); + + // Several fields at once in AOSP Contacts. + h.device.user.updateData(rows.name._id as number, { data2: 'Probert', data1: 'Dr. Probert Person' }); + h.device.user.updateData(rows['emails:zz9']._id as number, { data1: 'home@example.org' }); + h.device.user.updateData(rows['addresses:home']._id as number, { data9: '10117' }); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: MimeType.PHONE, data1: '+49 30 7777', data2: 3 }); + await h.upload(rawId); + const card = h.card(id); + expect(card.name).toMatchObject({ full: 'Dr. Probert Person' }); + expect(card.emails!.zz9.address).toBe('home@example.org'); + expect(card.addresses!.home.components).toContainEqual({ kind: 'postcode', value: '10117' }); + expect(Object.values(card.phones!).map((p) => p.number)).toContain('+49 30 7777'); + expect(card.onlineServices).toEqual(probeCard().onlineServices); + expect(card.notes!.n2).toEqual({ note: 'second note' }); + await expectSettled(h, id, rawId); + + // Fossify rewrites everything but the name; nothing uploads, the rows come back whole. + rows = h.rowsByKey(rawId); + h.device.user.fossifySave(rawId, null, Object.values(rows) + .filter((r) => r[Data.MIMETYPE] === MimeType.EMAIL || r[Data.MIMETYPE] === MimeType.PHONE) + .map((r) => ({ [Data.MIMETYPE]: r[Data.MIMETYPE], data1: r.data1, data2: r.data2 }))); + expect((await h.upload(rawId)).kind).toBe('clean'); + await expectSettled(h, id, rawId); + expect(h.rowsByKey(rawId)['organizations:o1|titles:t1,r1'].data5).toBe('R&D'); + + // The server edits and removes things. + h.server.serverUpdate('ContactCard', JMAP, id, { 'nicknames/nk/name': 'Probo', 'links/l1': null, 'addresses/home/components': [{ kind: 'locality', value: 'Hamburg' }] }); + await h.download(id); + await expectSettled(h, id, rawId); + rows = h.rowsByKey(rawId); + expect(rows['nicknames:nk'].data1).toBe('Probo'); + expect(rows['links:l1']).toBeUndefined(); + expect(rows['addresses:home']).toMatchObject({ data7: 'Hamburg', data4: null }); + }); + + it('follows Stalwart re-keying the whole card by position, without rewriting a row', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(probeCard()); + const before = h.rowsByKey(rawId); + h.server.rekeyPositionally(JMAP, id, 'ContactCard'); + const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); + for (const op of plan.ops.ops) { + if (op.op === 'update' && op.table === 'data') expect(Object.keys(op.values).every((c) => c.startsWith('data_sync'))).toBe(true); + expect(op.op === 'insert' || op.op === 'delete').toBe(false); + } + await h.applyOk(plan.ops); + const after = h.rowsByKey(rawId); + expect(after['organizations:k1|titles:k1,k2']._id).toBe(before['organizations:o1|titles:t1,r1']._id); + expect(after['emails:k2']._id).toBe(before['emails:x/y']._id); + await expectSettled(h, id, rawId); + + // The next device edit patches the entry under its new key. + h.device.user.updateData(after['emails:k2']._id as number, { data1: 'slash3@example.org' }); + const upload = h.planner.planUpload(await h.contact(rawId), h.ctx); + expect(upload).toMatchObject({ kind: 'upload', actions: [{ patch: { 'emails/k2/address': 'slash3@example.org' } }] }); + }); + + it('writes a blob-backed photo from the bytes the engine fetched, and nothing for its echo', async () => { + const h = new Harness(); + h.blobs.set('B1', JPEG); + const { id, rawId } = await h.seed({ name: { full: 'Blob Photo' }, media: { p: { kind: 'photo', blobId: 'B1', mediaType: 'image/jpeg' } } }); + const photo = h.rowsByKey(rawId)['media:p']; + expect(h.device.photo(Number(photo.data14))).toBe(JPEG); + expect((await h.contact(rawId)).shadow?.media).toEqual({ p: { kind: 'photo', blobId: 'B1', mediaType: 'image/jpeg' } }); + await expectSettled(h, id, rawId); + }); + + it('keeps the photo row when the photo bytes could not be fetched, and never reads it as deleted', async () => { + const h = new Harness(); + h.blobs.set('B1', JPEG); + const { id, rawId } = await h.seed({ name: { full: 'Blob Photo' }, emails: { e1: { address: 'a@example.org' } }, media: { p: { kind: 'photo', blobId: 'B1' } } }); + const photoRow = h.rowsByKey(rawId)['media:p']._id; + h.blobs.delete('B1'); + h.server.serverUpdate('ContactCard', JMAP, id, { 'emails/e1/address': 'b@example.org' }); + await h.download(id); + expect(h.rowsByKey(rawId)['media:p']?._id).toBe(photoRow); + h.device.user.updateData(h.rowsByKey(rawId)['emails:e1']._id as number, { data1: 'c@example.org' }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toMatchObject({ kind: 'upload', actions: [{ patch: { 'emails/e1/address': 'c@example.org' } }] }); + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + expect(plan.kind === 'upload' && Object.keys((plan.actions[0] as { patch: object }).patch)).toEqual(['emails/e1/address']); + }); + + it('heals a baseline the provider stored differently from the prediction', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed({ name: { full: 'Dr. Jane Q. Public' } }); + const name = h.rowsByKey(rawId).name; + // The real NameSplitter knows titles and middle names; the prediction does not. + await h.applyOk({ ref: 'split', ops: [{ op: 'update', table: 'data', id: name._id as number, values: { data4: 'Dr.', data2: 'Jane', data5: 'Q.', data3: 'Public' } }] }); + const heal = h.planner.planBaselineHeal(await h.contact(rawId)); + expect(heal?.ops.map((o) => o.op)).toEqual(['assert', 'update']); + await h.applyOk(heal!); + await expectSettled(h, id, rawId); + h.device.user.star(rawId); + expect((await h.upload(rawId)).kind).toBe('clean'); + }); + + it('does not heal a dirty contact: its edit must still count', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.updateData(h.rowsByKey(rawId)['notes:n1']._id as number, { data1: 'edited' }); + expect(h.planner.planBaselineHeal(await h.contact(rawId))).toBeNull(); + }); +}); diff --git a/src/device-sync/__tests__/contacts/merge.test.ts b/src/device-sync/__tests__/contacts/merge.test.ts new file mode 100644 index 00000000..709f3d92 --- /dev/null +++ b/src/device-sync/__tests__/contacts/merge.test.ts @@ -0,0 +1,164 @@ +import { describe, expect, it } from 'vitest'; +import { Data, MimeType } from '../../android-columns'; +import type { ContactCardWire } from '../../planner'; +import { appleCard, googleCard } from './fixtures'; +import { Harness, JMAP } from './harness'; + +/** A seeded card, edited on the device (in place, like AOSP) and on the server. */ +async function bothEdited( + make: () => Record, + device: (h: Harness, rawId: number) => void, + server: Record, +) { + const h = new Harness(); + const { id, rawId } = await h.seed(make()); + device(h, rawId); + h.server.serverUpdate('ContactCard', JMAP, id, server); + const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); + await h.applyOk(plan.ops); + return { h, id, rawId, plan }; +} + +const edit = (key: string, values: Record) => (h: Harness, rawId: number) => + h.device.user.updateData(h.rowsByKey(rawId)[key]._id as number, values); + +async function patchOf(h: Harness, rawId: number) { + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + return plan.kind === 'upload' && plan.actions[0].kind === 'update' ? plan.actions[0].patch : plan.kind; +} + +describe('contacts merge: a dirty contact meets a new server version', () => { + it('lets the server win a real conflict and counts it', async () => { + const { h, rawId, plan } = await bothEdited(appleCard, edit('emails:k1', { data1: 'mine@example.com' }), { 'emails/k1/address': 'theirs@example.com' }); + expect(plan).toMatchObject({ conflicts: 1, stillDirty: false, effect: 'update' }); + expect(h.rowsByKey(rawId)['emails:k1'].data1).toBe('theirs@example.com'); + expect(h.dirty(rawId)).toBe(true); + expect(await patchOf(h, rawId)).toBe('clean'); + }); + + it('keeps a local edit of one unit and takes the server edit of another', async () => { + const { h, rawId, plan } = await bothEdited(appleCard, edit('phones:k1', { data1: '+1 555 0199' }), { 'emails/k2/address': 'anna@new.example' }); + expect(plan).toMatchObject({ conflicts: 0, stillDirty: true }); + expect(h.rowsByKey(rawId)['emails:k2'].data1).toBe('anna@new.example'); + expect(await patchOf(h, rawId)).toEqual({ 'phones/k1/number': '+1 555 0199' }); + }); + + it('treats the same change on both sides as converged', async () => { + const { h, rawId, plan } = await bothEdited(appleCard, edit('emails:k1', { data1: 'same@example.com' }), { 'emails/k1/address': 'same@example.com' }); + expect(plan).toMatchObject({ conflicts: 0, stillDirty: false }); + expect(await patchOf(h, rawId)).toBe('clean'); + }); + + it('does not count a server change to a sub-field Android does not show', async () => { + const { h, id, rawId, plan } = await bothEdited(appleCard, edit('emails:k1', { data1: 'mine@example.com' }), { 'emails/k1/contexts': { private: true, school: true } }); + expect(plan.conflicts).toBe(0); + expect(await patchOf(h, rawId)).toEqual({ 'emails/k1/address': 'mine@example.com' }); + await h.upload(rawId); + expect(h.card(id).emails!.k1).toMatchObject({ address: 'mine@example.com', contexts: { private: true, school: true } }); + }); + + it('deletes a row the server deleted, over a local edit (counted)', async () => { + const { h, rawId, plan } = await bothEdited(appleCard, edit('phones:k3', { data1: '+1 555 0177' }), { 'phones/k3': null }); + expect(plan.conflicts).toBe(1); + expect(h.rowsByKey(rawId)['phones:k3']).toBeUndefined(); + }); + + it('puts back an entry deleted on the device that the server changed (counted)', async () => { + const { h, rawId, plan } = await bothEdited( + appleCard, + (hh, raw) => hh.device.user.deleteData(hh.rowsByKey(raw)['phones:k3']._id as number), + { 'phones/k3/number': '+1 555 0188' }, + ); + expect(plan.conflicts).toBe(1); + expect(h.rowsByKey(rawId)['phones:k3'].data1).toBe('+1 555 0188'); + }); + + it('keeps a device deletion of an entry the server left alone', async () => { + const { h, rawId, plan } = await bothEdited( + appleCard, + (hh, raw) => hh.device.user.deleteData(hh.rowsByKey(raw)['phones:k3']._id as number), + { 'emails/k2/address': 'anna@new.example' }, + ); + expect(plan).toMatchObject({ conflicts: 0, stillDirty: true }); + const patch = await patchOf(h, rawId) as Record; + expect(Object.keys(patch)).toEqual(['phones']); + expect(Object.keys(patch.phones as object)).toEqual(['k1', 'k2']); + }); + + it('matches an entry both sides added instead of duplicating it', async () => { + const { h, rawId, plan } = await bothEdited( + appleCard, + (hh, raw) => hh.device.user.insertData(raw, { [Data.MIMETYPE]: MimeType.EMAIL, data1: 'both@example.com', data2: 3 }), + { 'emails/k3': { address: 'both@example.com' } }, + ); + expect(plan.conflicts).toBe(0); + const emails = h.device.rows('data', 'raw_contact_id = ? AND mimetype = ?', [rawId, MimeType.EMAIL]); + expect(emails.filter((r) => r.data1 === 'both@example.com')).toHaveLength(1); + expect(await patchOf(h, rawId)).toBe('clean'); + }); + + it('inserts what the server added next to a pending local edit', async () => { + const { h, rawId, plan } = await bothEdited(appleCard, edit('phones:k1', { data1: '+1 555 0199' }), { 'emails/k3': { address: 'third@example.com' } }); + expect(plan.stillDirty).toBe(true); + expect(h.rowsByKey(rawId)['emails:k3'].data1).toBe('third@example.com'); + expect(await patchOf(h, rawId)).toEqual({ 'phones/k1/number': '+1 555 0199' }); + }); + + it('follows shifted keys and uploads the local edit under the new key', async () => { + const { h, rawId, plan } = await bothEdited(appleCard, edit('emails:k2', { data1: 'anna@work2.example' }), { + emails: { + k1: { address: 'first@example.com' }, + k2: { address: 'anna@example.com', contexts: { private: true }, pref: 1 }, + k3: { address: 'anna@work.example', contexts: { work: true } }, + }, + }); + expect(plan.conflicts).toBe(0); + const rows = h.rowsByKey(rawId); + expect(rows['emails:k3'].data1).toBe('anna@work2.example'); + expect(rows['emails:k1'].data1).toBe('first@example.com'); + expect(await patchOf(h, rawId)).toEqual({ 'emails/k3/address': 'anna@work2.example' }); + }); + + it('merges the name as one unit: a typed name on the server beats a local component edit', async () => { + const { h, rawId, plan } = await bothEdited(appleCard, edit('name', { data2: 'Annie', data1: 'Annie Apfel' }), { 'name/full': 'A. Apfel' }); + expect(plan.conflicts).toBe(1); + expect(h.rowsByKey(rawId).name).toMatchObject({ data1: 'A. Apfel', data2: 'Anna' }); + }); + + it('never gives keys to a kind an editor rewrote, so a nickname it could not show is not deleted later', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(googleCard()); + // Fossify keeps one nickname, re-inserted without its key. + h.device.user.fossifySave(rawId, null, [{ [Data.MIMETYPE]: MimeType.NICKNAME, data1: 'Gü' }]); + h.server.serverUpdate('ContactCard', JMAP, id, { 'nicknames/k1/name': 'Guenther' }); + const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); + await h.applyOk(plan.ops); + const nick = h.device.rows('data', 'raw_contact_id = ? AND mimetype = ?', [rawId, MimeType.NICKNAME]); + expect(nick.map((r) => [r.data1, r.data_sync1])).toEqual([['Guenther', null]]); + const patch = await patchOf(h, rawId); + expect(typeof patch === 'object' && Object.keys(patch).some((k) => k.startsWith('nicknames'))).toBe(false); + }); + + it('leaves a contact deleted on the device alone: the delete wins and uploads', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + h.device.user.deleteContact(rawId); + h.server.serverUpdate('ContactCard', JMAP, id, { 'emails/k1/address': 'x@example.com' }); + const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); + expect(plan).toMatchObject({ effect: 'none', stillDirty: true, ops: { ops: [] } }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toMatchObject({ kind: 'upload', actions: [{ kind: 'destroy', id }] }); + }); + + it('replaces the rows of a different object behind the same id (another uid)', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + edit('emails:k1', { data1: 'mine@example.com' })(h, rawId); + const other = { ...(googleCard() as object), id, addressBookIds: { [h.book]: true } } as unknown as ContactCardWire; + const plan = h.planner.planDownload(other, await h.contact(rawId), h.ctx); + await h.applyOk(plan.ops); + const rows = h.rowsByKey(rawId); + expect(rows['emails:k1']).toBeUndefined(); + expect(rows['nicknames:k1'].data1).toBe('Gü'); + expect(h.dirty(rawId)).toBe(false); + }); +}); diff --git a/src/device-sync/__tests__/contacts/units.test.ts b/src/device-sync/__tests__/contacts/units.test.ts new file mode 100644 index 00000000..497dc2c1 --- /dev/null +++ b/src/device-sync/__tests__/contacts/units.test.ts @@ -0,0 +1,155 @@ +import { describe, expect, it } from 'vitest'; +import { Data, MimeType, PhoneType, RelationType } from '../../android-columns'; +import { contactsPlanner } from '../../contacts/planner'; +import { entryKey, orgKey, parseEntryKey, parseOrgKey } from '../../contacts/keys'; +import { editPostalComponents, postalCells } from '../../contacts/postal'; +import { base64ToBytes, dataUriBase64, shadowOf } from '../../contacts/photo'; +import { fullIsDerived, nameCells } from '../../contacts/name'; +import { phoneFlags, phoneType, relationName, relationType } from '../../contacts/types-map'; +import type { ContactCardWire } from '../../planner'; +import { JPEG } from './fixtures'; + +describe('contacts decode', () => { + it('queries the columns the planner reads', () => { + expect(contactsPlanner.rawContactColumns).toEqual(expect.arrayContaining([ + '_id', 'sourceid', 'version', 'dirty', 'deleted', 'sync1', 'sync2', 'sync3', 'sync4', 'raw_contact_is_read_only', + ])); + expect(contactsPlanner.dataColumns).toEqual(expect.arrayContaining([ + '_id', 'raw_contact_id', 'mimetype', 'is_primary', 'data1', 'data14', 'data_sync1', 'data_sync2', 'data_sync3', 'group_sourceid', + ])); + expect(contactsPlanner.dataColumns).not.toContain('data15'); + expect(contactsPlanner.groupColumns).toEqual(expect.arrayContaining(['_id', 'sourceid', 'version', 'dirty', 'deleted', 'title', 'sync2', 'sync3', 'sync4'])); + }); + + it('reads numbers given as text (data columns are TEXT) and tolerates what other apps left in sync columns', () => { + const local = contactsPlanner.decodeContact( + { _id: '5', sourceid: 'c/e1', version: '3', dirty: '1', deleted: null, sync1: 'c/b,garbage,,c/x', sync2: '{"not":"a card"}', sync3: '{"uid":1}', sync4: 'nope', raw_contact_is_read_only: 0 }, + [ + { _id: '9', raw_contact_id: '5', mimetype: MimeType.EMAIL, data1: 'a@b', data2: '2', data_sync1: 'emails:e1', data_sync2: '', data_sync3: '[1]' }, + { _id: 10, raw_contact_id: 6, mimetype: MimeType.NOTE, data1: 'another contact' }, + ], + ); + expect(local).toMatchObject({ + rawContactId: 5, sourceId: 'c/e1', version: 3, dirty: true, deleted: false, readOnly: false, + collections: ['c/b', 'c/x'], shadow: null, pending: null, poison: null, + }); + expect(local.rows).toEqual([{ id: 9, mimetype: MimeType.EMAIL, cells: { data1: 'a@b', data2: '2' }, key: 'emails:e1', photoHash: null, baseline: null }]); + }); + + it('decodes a pending create, a poison marker and a group', () => { + const local = contactsPlanner.decodeContact({ + _id: 1, sourceid: null, version: 1, dirty: 1, deleted: 0, + sync3: JSON.stringify({ uid: 'urn:uuid:x', target: 'c/b' }), + sync4: JSON.stringify({ fp: 'f', type: 'invalidProperties', n: 2, until: 5 }), + }, []); + expect(local.pending).toEqual({ uid: 'urn:uuid:x', target: 'c/b' }); + expect(local.poison).toEqual({ fp: 'f', type: 'invalidProperties', n: 2, until: 5 }); + expect(contactsPlanner.decodeGroup({ _id: 4, sourceid: 'c/g', version: 2, dirty: 0, deleted: 1, title: 'T', sync2: '{"id":"g","kind":"group"}' })) + .toMatchObject({ groupId: 4, sourceId: 'c/g', deleted: true, title: 'T', shadow: { id: 'g', kind: 'group' } }); + }); +}); + +describe('contacts keys', () => { + it('round-trips entry keys with any character', () => { + for (const key of ['work1', 'x/y', 'a,b|c%d', 'urn:uuid:1', 'k:1']) { + expect(parseEntryKey(entryKey('emails', key))).toEqual({ map: 'emails', key }); + } + expect(parseEntryKey('name')).toBeNull(); + expect(parseEntryKey('garbage')).toBeNull(); + }); + + it('round-trips organization keys, titles positional as title then role', () => { + expect(orgKey({ org: 'o1', title: 't1', role: 'r1' })).toBe('organizations:o1|titles:t1,r1'); + expect(orgKey({ org: null, title: null, role: 'r1' })).toBe('titles:,r1'); + expect(orgKey({ org: 'o1', title: null, role: null })).toBe('organizations:o1'); + for (const parts of [{ org: 'o|1', title: 't,1', role: null }, { org: null, title: 't1', role: 'r1' }, { org: 'o', title: null, role: null }]) { + expect(parseOrgKey(orgKey(parts))).toEqual(parts); + } + expect(parseOrgKey('emails:e1')).toBeNull(); + }); +}); + +describe('contacts type tables', () => { + it.each([ + [{ features: { mobile: true }, contexts: { work: true } }, PhoneType.WORK_MOBILE], + [{ features: { cell: true } }, PhoneType.MOBILE], + [{ features: { fax: true }, contexts: { private: true } }, PhoneType.FAX_HOME], + [{ features: { fax: true } }, PhoneType.OTHER_FAX], + [{ features: { pager: true }, contexts: { work: true } }, PhoneType.WORK_PAGER], + [{ features: { textphone: true } }, PhoneType.TTY_TDD], + [{ features: { 'main-number': true } }, PhoneType.MAIN], + [{ features: { voice: true }, contexts: { work: true } }, PhoneType.WORK], + [{ contexts: { private: true } }, PhoneType.HOME], + [{ features: { video: true } }, PhoneType.OTHER], + [{ features: { mobile: true }, label: 'Boat' }, PhoneType.CUSTOM], + ])('phone %j → TYPE %i', (entry, type) => { + expect(phoneType(entry).type).toBe(type); + }); + + it('reads the phone table right to left, OTHER meaning no features', () => { + expect(phoneFlags(PhoneType.OTHER, null)).toEqual({ contexts: [], features: [], label: null }); + expect(phoneFlags(PhoneType.FAX_WORK, null)).toEqual({ contexts: ['work'], features: ['fax'], label: null }); + expect(phoneFlags(PhoneType.CAR, null)).toEqual({ contexts: [], features: ['voice'], label: 'car' }); + }); + + it('maps relations both ways, device-only types to the nearest RFC type', () => { + expect(relationType({ friend: true })).toEqual({ type: RelationType.FRIEND, label: null }); + expect(relationType({ 'co-worker': true })).toEqual({ type: RelationType.CUSTOM, label: 'co-worker' }); + expect(relationType({ agent: true })).toEqual({ type: RelationType.ASSISTANT, label: null }); + expect(relationName(RelationType.BROTHER, null)).toBe('sibling'); + expect(relationName(RelationType.REFERRED_BY, null)).toBe('contact'); + expect(relationName(RelationType.CUSTOM, 'Emergency')).toBe('emergency'); + }); +}); + +describe('contacts name and postal helpers', () => { + it('fills the family name from surname and surname2, the suffix from the first of its kinds, and a missing `full` with deriveFullName', () => { + // deriveFullName (the app's) leaves out surname2 and credential. + expect(nameCells({ components: [{ kind: 'surname', value: 'García' }, { kind: 'surname2', value: 'López' }, { kind: 'credential', value: 'MD' }] })) + .toMatchObject({ data3: 'García López', data6: 'MD', data1: 'García' }); + }); + + it('knows a derived `full` in the app’s and in Stalwart’s form', () => { + const components = [{ kind: 'given', value: 'A' }, { kind: 'surname', value: 'B' }, { kind: 'surname2', value: 'C' }]; + expect(fullIsDerived({ components, full: 'A B' })).toBe(true); + expect(fullIsDerived({ components, full: 'A B C' })).toBe(true); + expect(fullIsDerived({ components, full: 'B, A' })).toBe(false); + }); + + it('keeps separators between street parts and falls back to the legacy flat fields', () => { + expect(postalCells({ street: 'Old Rd 1', locality: 'Town' })).toMatchObject({ data4: 'Old Rd 1', data7: 'Town' }); + const address = { components: [{ kind: 'number', value: '12' }, { kind: 'separator', value: '-' }, { kind: 'name', value: 'Rue X' }] }; + expect(postalCells(address).data4).toBe('12-Rue X'); + expect(editPostalComponents(address, { data4: 'Rue Y 3' }, new Set(['data4']))).toEqual([{ kind: 'name', value: 'Rue Y 3' }]); + }); +}); + +describe('contacts photos', () => { + it('decodes base64 and data URIs, also without a media type', () => { + expect(base64ToBytes('AAECAw==')).toEqual(new Uint8Array([0, 1, 2, 3])); + expect(base64ToBytes('!!')).toBeNull(); + expect(dataUriBase64(`data:image/jpeg;base64,${JPEG}`)).toBe(JPEG); + expect(dataUriBase64(`data:base64,${JPEG}`)).toBe(JPEG); + expect(dataUriBase64('https://example.org/p.jpg')).toBeNull(); + }); + + it('keeps hashes, not bytes, in the shadow and leaves blob-backed entries alone', () => { + const card = { id: 'e1', media: { a: { kind: 'photo', uri: `data:image/jpeg;base64,${JPEG}` }, b: { kind: 'logo', blobId: 'B1' } } } as unknown as ContactCardWire; + const shadow = shadowOf(card); + expect((shadow.media as Record).a.uri).toMatch(/^sha256:[0-9a-f]{64}$/); + expect((shadow.media as Record).b).toEqual({ kind: 'logo', blobId: 'B1' }); + expect((card.media as Record).a.uri).toContain('base64'); + }); +}); + +describe('contacts rows of kinds the planner does not map', () => { + it('ignores them on download and upload', async () => { + const { Harness } = await import('./harness'); + const { appleCard } = await import('./fixtures'); + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: 'vnd.android.cursor.item/im', data1: 'anna@jabber.example' }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx).kind).toBe('clean'); + expect(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).effect).toBe('none'); + }); +}); diff --git a/src/device-sync/__tests__/contacts/upload-editors.test.ts b/src/device-sync/__tests__/contacts/upload-editors.test.ts new file mode 100644 index 00000000..9bf3fe16 --- /dev/null +++ b/src/device-sync/__tests__/contacts/upload-editors.test.ts @@ -0,0 +1,358 @@ +import { describe, expect, it } from 'vitest'; +import { Data, MimeType, RawContacts } from '../../android-columns'; +import { contactFingerprint } from '../../contacts/planner'; +import type { Row } from '../../types'; +import { appleCard, googleCard, JPEG2, probeCard } from './fixtures'; +import { ACCOUNT, Harness, JMAP } from './harness'; + +/** + * The rows Fossify Contacts writes back on save: every kind it models + * re-inserted without DATA_SYNC, IS_PRIMARY or the columns it has no field + * for (a department, a job description, a website's type), one nickname and + * one organization only. + */ +function fossifyRows(h: Harness, rawId: number, edit: (rows: Row[]) => void = () => {}): Row[] { + const out: Row[] = []; + let nickname = false; + let organization = false; + for (const r of h.device.rows('data', 'raw_contact_id = ?', [rawId])) { + const pick = (...columns: string[]) => { + const row: Row = { [Data.MIMETYPE]: r[Data.MIMETYPE] }; + for (const c of columns) row[c] = r[c]; + out.push(row); + }; + switch (r[Data.MIMETYPE]) { + case MimeType.NICKNAME: if (!nickname) pick('data1'); nickname = true; break; + case MimeType.PHONE: case MimeType.EMAIL: pick('data1', 'data2', 'data3'); break; + case MimeType.STRUCTURED_POSTAL: pick('data1', 'data2'); break; + case MimeType.EVENT: pick('data1', 'data2'); break; + case MimeType.NOTE: pick('data1'); break; + case MimeType.ORGANIZATION: if (!organization) pick('data1', 'data4'); organization = true; break; + case MimeType.WEBSITE: out.push({ [Data.MIMETYPE]: MimeType.WEBSITE, data1: r.data1, data2: 1 }); break; + case MimeType.GROUP_MEMBERSHIP: pick('data1'); break; + } + } + edit(out); + return out; +} + +async function uploadPlan(h: Harness, rawId: number) { + return h.planner.planUpload(await h.contact(rawId), h.ctx); +} + +async function patchOf(h: Harness, rawId: number) { + const plan = await uploadPlan(h, rawId); + if (plan.kind !== 'upload' || plan.actions[0].kind !== 'update') throw new Error(`expected an update, got ${plan.kind}`); + return plan.actions[0].patch; +} + +describe('contacts upload: Fossify re-inserts rows', () => { + it('uploads nothing for a save without changes, then restores what it dropped', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.fossifySave(rawId, null, fossifyRows(h, rawId)); + const plan = await uploadPlan(h, rawId); + expect(plan.kind).toBe('clean'); + await h.upload(rawId); + const rows = h.rowsByKey(rawId); + expect(rows['organizations:o1|titles:t1,r1']).toMatchObject({ data1: 'ACME', data4: 'Chief Prober', data5: 'R&D', data6: 'Lead' }); + expect(Number(rows['links:l1'].data2)).toBe(5); + expect(Number(rows['emails:work1'].is_primary)).toBe(1); + expect(h.dirty(rawId)).toBe(false); + expect(h.planner.planBaselineHeal(await h.contact(rawId))).toBeNull(); + }); + + it('never clears the department it drops (a value-matched row has no baseline)', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.fossifySave(rawId, null, fossifyRows(h, rawId, (rows) => { + rows.find((r) => r[Data.MIMETYPE] === MimeType.ORGANIZATION)!.data4 = 'Chief Tester'; + })); + expect(await patchOf(h, rawId)).toEqual({ 'titles/t1/name': 'Chief Tester' }); + }); + + it('uploads the one email it changed, matching the others by value', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + h.device.user.fossifySave(rawId, null, fossifyRows(h, rawId, (rows) => { + rows.find((r) => r.data1 === 'anna@work.example')!.data1 = 'anna@new.example'; + })); + expect(await patchOf(h, rawId)).toEqual({ 'emails/k2/address': 'anna@new.example' }); + }); + + it('keeps the second nickname it could not show, and puts it back on the device', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(googleCard()); + h.device.user.fossifySave(rawId, null, fossifyRows(h, rawId)); + expect((await uploadPlan(h, rawId)).kind).toBe('clean'); + await h.upload(rawId); + expect(h.rowsByKey(rawId)['nicknames:k2'].data1).toBe('GKG'); + expect(h.card(id).nicknames).toEqual(googleCard().nicknames); + }); + + it('does not delete an entry it rewrote away (a documented limitation)', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + h.device.user.fossifySave(rawId, null, fossifyRows(h, rawId, (rows) => { + rows.splice(rows.findIndex((r) => r.data1 === 'anna@work.example'), 1); + })); + expect((await uploadPlan(h, rawId)).kind).toBe('clean'); + }); +}); + +describe('contacts upload: deletions (in-place editor)', () => { + it('resends the map minus the entry, its other entries as the server has them', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(probeCard()); + h.device.user.deleteData(h.rowsByKey(rawId)['emails:zz9']._id as number); + const { emails } = h.card(id) as { emails: Record }; + expect(await patchOf(h, rawId)).toEqual({ emails: { work1: emails.work1, 'x/y': emails['x/y'] } }); + }); + + it('deletes an address with a null (clean on Stalwart)', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.deleteData(h.rowsByKey(rawId)['addresses:home']._id as number); + expect(await patchOf(h, rawId)).toEqual({ 'addresses/home': null }); + }); + + it('keeps the notes after the first when the note is deleted', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.deleteData(h.rowsByKey(rawId)['notes:n1']._id as number); + expect(await patchOf(h, rawId)).toEqual({ notes: { n2: { note: 'second note' } } }); + }); + + it('removes a map whose last entry went, when other rows show an in-place editor', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.deleteData(h.rowsByKey(rawId)['nicknames:nk']._id as number); + expect(await patchOf(h, rawId)).toEqual({ nicknames: null }); + }); + + it('deletes an organization with its titles', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.deleteData(h.rowsByKey(rawId)['organizations:o1|titles:t1,r1']._id as number); + expect(await patchOf(h, rawId)).toEqual({ organizations: null, titles: null }); + }); + + it('deletes the photo', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.deleteData(h.rowsByKey(rawId)['media:ph']._id as number); + expect(await patchOf(h, rawId)).toEqual({ media: null }); + }); + + it('deletes a relation by resending relatedTo (Stalwart ignores a null there)', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.deleteData(h.rowsByKey(rawId)['relatedTo:urn:uuid:other-person']._id as number); + expect(await patchOf(h, rawId)).toEqual({ relatedTo: null }); + }); + + it('folds an edit into a map it resends', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(probeCard()); + const rows = h.rowsByKey(rawId); + h.device.user.deleteData(rows['emails:zz9']._id as number); + h.device.user.updateData(rows['emails:work1']._id as number, { data1: 'new@work.example' }); + const { emails } = h.card(id) as unknown as { emails: Record> }; + expect(await patchOf(h, rawId)).toEqual({ emails: { work1: { ...emails.work1, address: 'new@work.example' }, 'x/y': emails['x/y'] } }); + }); +}); + +describe('contacts upload: additions', () => { + it('adds an entry to an existing map under a new key', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: MimeType.EMAIL, data1: 'added@example.org', data2: 2 }); + const patch = await patchOf(h, rawId); + const [pointer] = Object.keys(patch); + expect(pointer).toMatch(/^emails\/b[0-9a-z]{8}$/); + expect(patch[pointer]).toEqual({ address: 'added@example.org', contexts: { work: true } }); + }); + + it('sends the whole map for the first entry of a map', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: MimeType.WEBSITE, data1: 'https://anna.example', data2: 7 }); + const patch = await patchOf(h, rawId); + expect(Object.keys(patch)).toEqual(['links']); + expect(Object.values(patch.links as object)).toEqual([{ uri: 'https://anna.example' }]); + }); + + it('adds a work phone with its features and contexts', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: MimeType.PHONE, data1: '+49 89 1', data2: 3 }); + expect(Object.values(await patchOf(h, rawId))).toEqual([{ number: '+49 89 1', features: { voice: true }, contexts: { work: true } }]); + }); + + it('adds an organization and a title that points at it', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: MimeType.ORGANIZATION, data1: 'Apple', data4: 'Engineer' }); + const patch = await patchOf(h, rawId) as { organizations: Record; titles: Record> }; + const [orgKey] = Object.keys(patch.organizations); + expect(patch.organizations[orgKey]).toEqual({ name: 'Apple' }); + expect(Object.values(patch.titles)).toEqual([{ name: 'Engineer', kind: 'title', organizationId: orgKey }]); + }); + + it('adds a relation keyed by its name', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: MimeType.RELATION, data1: 'Jane Doe', data2: 14 }); + expect(await patchOf(h, rawId)).toEqual({ 'relatedTo/Jane Doe': { relation: { spouse: true } } }); + }); + + it('adds a photo from the device as a JPEG data URI', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + h.device.user.setPhoto(rawId, JPEG2); + h.devicePhotos.set(rawId, JPEG2); + const patch = await patchOf(h, rawId); + expect(Object.values(patch.media as object)).toEqual([{ kind: 'photo', uri: `data:image/jpeg;base64,${JPEG2}`, mediaType: 'image/jpeg' }]); + }); + + it('replaces a changed photo in place', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.setPhoto(rawId, JPEG2); + h.devicePhotos.set(rawId, JPEG2); + expect(await patchOf(h, rawId)).toEqual({ 'media/ph/uri': `data:image/jpeg;base64,${JPEG2}`, 'media/ph/mediaType': 'image/jpeg' }); + }); +}); + +describe('contacts upload: new contacts', () => { + function insertNew(h: Harness): number { + return h.device.user.insertContact(ACCOUNT, [ + { [Data.MIMETYPE]: MimeType.STRUCTURED_NAME, data1: 'Neu Person', data2: 'Neu', data3: 'Person' }, + { [Data.MIMETYPE]: MimeType.EMAIL, data1: 'neu@example.org', data2: 1, is_primary: 1 }, + { [Data.MIMETYPE]: MimeType.PHONE, data1: '+49 1', data2: 2 }, + { [Data.MIMETYPE]: MimeType.STRUCTURED_POSTAL, data1: 'Weg 1, 12345 Ort', data2: 1, data4: 'Weg 1', data7: 'Ort', data9: '12345' }, + { [Data.MIMETYPE]: MimeType.EVENT, data1: '1990-01-02', data2: 3 }, + { [Data.MIMETYPE]: MimeType.NOTE, data1: 'hello' }, + { [Data.MIMETYPE]: MimeType.NICKNAME, data1: 'Neuling' }, + ]); + } + + it('claims a uid and target first, then creates the card from its rows', async () => { + const h = new Harness(); + const rawId = insertNew(h); + const claim = h.planner.planUpload(await h.contact(rawId), h.ctx); + expect(claim.kind).toBe('claim'); + await h.applyOk((claim as { ops: import('../../planner').OpGroup }).ops); + const pending = (await h.contact(rawId)).pending!; + expect(pending.target).toBe(`${JMAP}/${h.book}`); + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + if (plan.kind !== 'upload' || plan.actions[0].kind !== 'create') throw new Error(plan.kind); + const { object, uid, collectionId } = plan.actions[0]; + expect(uid).toBe(pending.uid); + expect(collectionId).toBe(h.book); + const values = (map: unknown) => Object.values(map as object); + expect(object).toMatchObject({ '@type': 'Card', version: '1.0', uid: pending.uid, addressBookIds: { [h.book]: true } }); + expect(object.name).toEqual({ components: [{ kind: 'given', value: 'Neu' }, { kind: 'surname', value: 'Person' }], full: 'Neu Person' }); + expect(values(object.emails)).toEqual([{ address: 'neu@example.org', contexts: { private: true }, pref: 1 }]); + expect(values(object.phones)).toEqual([{ number: '+49 1', features: { mobile: true } }]); + expect(values(object.addresses)).toEqual([{ + components: [{ kind: 'name', value: 'Weg 1' }, { kind: 'locality', value: 'Ort' }, { kind: 'postcode', value: '12345' }], + full: 'Weg 1, 12345 Ort', + contexts: { private: true }, + }]); + expect(values(object.anniversaries)).toEqual([{ kind: 'birth', date: { '@type': 'PartialDate', year: 1990, month: 1, day: 2 } }]); + expect(values(object.notes)).toEqual([{ note: 'hello' }]); + expect(values(object.nicknames)).toEqual([{ name: 'Neuling' }]); + expect(Object.keys(object).sort()).toEqual([ + '@type', 'addressBookIds', 'addresses', 'anniversaries', 'emails', 'name', 'nicknames', 'notes', 'phones', 'uid', 'version', + ]); + }); + + it('takes the identity after the create and writes nothing when the card comes back', async () => { + const h = new Harness(); + const rawId = insertNew(h); + await h.upload(rawId); + const local = await h.contact(rawId); + expect(local).toMatchObject({ dirty: false, pending: null }); + expect(local.sourceId).toMatch(/^c\//); + expect(local.rows.every((r) => r.key !== null)).toBe(true); + const plan = h.planner.planDownload(h.card(h.cardIdOf(local)), local, h.ctx); + expect(plan.effect).toBe('none'); + }); + + it('is skipped when no writable book is selected', async () => { + const h = new Harness(); + const rawId = insertNew(h); + const plan = h.planner.planUpload(await h.contact(rawId), { ...h.ctx, createTarget: () => null }); + expect(plan).toEqual({ kind: 'skip', reason: 'noWritableAddressBook' }); + }); +}); + +describe('contacts upload: deleted contacts', () => { + it('destroys the card, then purges the rows', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed(appleCard()); + h.device.user.deleteContact(rawId); + const plan = await uploadPlan(h, rawId); + expect(plan).toEqual({ kind: 'upload', actions: [{ kind: 'destroy', id, uid: appleCard().uid }] }); + await h.upload(rawId); + expect(h.device.row('raw_contacts', rawId)).toBeUndefined(); + expect(h.server.get('ContactCard', JMAP, id)).toBeUndefined(); + }); + + it('only leaves the synced book of a card that is also in an unsynced one', async () => { + const h = new Harness(); + const other = h.server.addAddressBook(JMAP, { name: 'Other' }); + h.unselected.add(`${JMAP}/${other}`); + const { rawId } = await h.seed({ ...appleCard(), addressBookIds: { [h.book]: true, [other]: true } }); + h.device.user.deleteContact(rawId); + expect(await patchOf(h, rawId)).toEqual({ [`addressBookIds/${h.book}`]: null }); + }); + + it('purges a new contact that never reached the server, and destroys a claimed one by uid', async () => { + const h = new Harness(); + const fresh = h.device.user.insertContact(ACCOUNT, [{ [Data.MIMETYPE]: MimeType.NOTE, data1: 'x' }]); + h.device.user.deleteContact(fresh); + expect((await uploadPlan(h, fresh)).kind).toBe('purge'); + const claimed = h.device.user.insertContact(ACCOUNT, [{ [Data.MIMETYPE]: MimeType.NOTE, data1: 'y' }]); + await h.planUpload(claimed); + const uid = (await h.contact(claimed)).pending!.uid; + h.device.user.deleteContact(claimed); + expect(await uploadPlan(h, claimed)).toEqual({ kind: 'upload', actions: [{ kind: 'destroy', id: null, uid }] }); + }); +}); + +describe('contacts upload: read-only and poisoned contacts', () => { + it('reverts an edit of a read-only contact from the shadow', async () => { + const h = new Harness(); + h.readOnlyBooks.add(h.book); + const { rawId } = await h.seed(appleCard()); + h.device.user.updateData(h.rowsByKey(rawId)['emails:k1']._id as number, { data1: 'hacked@example.org' }); + const plan = await uploadPlan(h, rawId); + expect(plan.kind).toBe('revert'); + await h.upload(rawId); + expect(h.rowsByKey(rawId)['emails:k1'].data1).toBe('anna@example.com'); + expect(h.dirty(rawId)).toBe(false); + }); + + it('purges a deleted read-only contact and asks for it again', async () => { + const h = new Harness(); + h.readOnlyBooks.add(h.book); + const { rawId } = await h.seed(appleCard()); + h.device.user.deleteContact(rawId); + expect(await uploadPlan(h, rawId)).toMatchObject({ kind: 'revert', refetch: true }); + }); + + it('skips a poisoned contact until it changes or the back-off ends', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + const email = h.rowsByKey(rawId)['emails:k1']._id as number; + h.device.user.updateData(email, { data1: 'bad@example.org' }); + const poison = { fp: contactFingerprint(await h.contact(rawId)), type: 'invalidProperties', n: 1, until: h.ctx.now + 3_600_000 }; + await h.applyOk({ ref: 'x', ops: [{ op: 'update', table: 'raw_contacts', id: rawId, values: { [RawContacts.SYNC4]: JSON.stringify(poison) } }] }); + expect(await uploadPlan(h, rawId)).toEqual({ kind: 'skip', reason: 'poisoned:invalidProperties' }); + expect(h.planner.planUpload(await h.contact(rawId), { ...h.ctx, now: poison.until + 1 }).kind).toBe('upload'); + h.device.user.updateData(email, { data1: 'good@example.org' }); + expect((await uploadPlan(h, rawId)).kind).toBe('upload'); + }); +}); diff --git a/src/device-sync/__tests__/contacts/upload-fields.test.ts b/src/device-sync/__tests__/contacts/upload-fields.test.ts new file mode 100644 index 00000000..62fb106d --- /dev/null +++ b/src/device-sync/__tests__/contacts/upload-fields.test.ts @@ -0,0 +1,229 @@ +import { describe, expect, it } from 'vitest'; +import { Data, MimeType } from '../../android-columns'; +import { deepEqual } from '../../common/json'; +import { applyPatch, assertPatchWellFormed, type PatchObject } from '../../common/patch'; +import type { ContactCardWire } from '../../planner'; +import type { Row } from '../../types'; +import { appleCard, googleCard, probeCard } from './fixtures'; +import { Harness } from './harness'; + +/** Card data Android has no column for: every patch must leave it as it was. */ +const UNMAPPED = [ + 'onlineServices', 'speakToAs', 'keywords', 'personalInfo', 'preferredLanguages', 'notes/n2', 'name/phoneticSystem', + 'addresses/home/coordinates', 'addresses/home/timeZone', 'addresses/home/countryCode', 'kind', 'uid', +]; + +function at(card: unknown, path: string): unknown { + return path.split('/').reduce((v, k) => (v && typeof v === 'object' ? (v as Record)[k] : undefined), card); +} + +/** Seeds a card, edits one row in place like AOSP Contacts, and returns the upload patch. */ +async function editedPatch(make: () => Record, key: string, values: Row): Promise<{ patch: PatchObject; card: ContactCardWire }> { + const h = new Harness(); + const { id, rawId } = await h.seed(make()); + const row = h.rowsByKey(rawId)[key]; + if (!row) throw new Error(`no row ${key}`); + h.device.user.updateData(row._id as number, values); + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + if (plan.kind !== 'upload' || plan.actions[0].kind !== 'update') throw new Error(`expected an update, got ${plan.kind}`); + return { patch: plan.actions[0].patch, card: h.card(id) }; +} + +const probeEdits: Array<[string, string, Row, PatchObject]> = [ + ['email address', 'emails:work1', { data1: 'new@work.example' }, { 'emails/work1/address': 'new@work.example' }], + ['email type (home → work)', 'emails:zz9', { data2: 2 }, { 'emails/zz9/contexts': { work: true } }], + ['email custom label', 'emails:work1', { data3: 'Desk' }, { 'emails/work1/label': 'Desk' }], + ['a key with a slash', 'emails:x/y', { data1: 'slash2@example.org' }, { 'emails/x~1y/address': 'slash2@example.org' }], + ['phone number', 'phones:p-a', { data1: '+49 30 5555' }, { 'phones/p-a/number': '+49 30 5555' }], + ['phone type (mobile → work mobile)', 'phones:p-a', { data2: 17 }, { 'phones/p-a/contexts': { work: true }, 'phones/p-a/features': { mobile: true } }], + ['nickname', 'nicknames:nk', { data1: 'Probster' }, { 'nicknames/nk/name': 'Probster' }], + ['street', 'addresses:home', { data4: 'Hauptstr. 1' }, { + 'addresses/home/components': [ + { kind: 'name', value: 'Hauptstr. 1' }, + { kind: 'locality', value: 'Berlin' }, + { kind: 'postcode', value: '10115' }, + { kind: 'country', value: 'Germany' }, + ], + }], + ['city', 'addresses:home', { data7: 'Potsdam' }, { + 'addresses/home/components': [ + { kind: 'name', value: 'Main St' }, { kind: 'separator', value: ' ' }, { kind: 'number', value: '12' }, + { kind: 'locality', value: 'Potsdam' }, { kind: 'postcode', value: '10115' }, { kind: 'country', value: 'Germany' }, + { kind: 'apartment', value: '4b' }, + ], + }], + ['region, which the address did not have', 'addresses:home', { data8: 'Berlin' }, { + 'addresses/home/components': [ + { kind: 'name', value: 'Main St' }, { kind: 'separator', value: ' ' }, { kind: 'number', value: '12' }, + { kind: 'locality', value: 'Berlin' }, { kind: 'region', value: 'Berlin' }, { kind: 'postcode', value: '10115' }, + { kind: 'country', value: 'Germany' }, { kind: 'apartment', value: '4b' }, + ], + }], + ['formatted address', 'addresses:home', { data1: 'Main St 12\n10115 Berlin' }, { 'addresses/home/full': 'Main St 12\n10115 Berlin' }], + ['address type (home → work)', 'addresses:home', { data2: 2 }, { 'addresses/home/contexts': { work: true } }], + ['company', 'organizations:o1|titles:t1,r1', { data1: 'ACME Corp' }, { 'organizations/o1/name': 'ACME Corp' }], + ['department', 'organizations:o1|titles:t1,r1', { data5: 'QA, Tools' }, { 'organizations/o1/units': [{ name: 'QA' }, { name: 'Tools' }] }], + ['job title', 'organizations:o1|titles:t1,r1', { data4: 'Chief Tester' }, { 'titles/t1/name': 'Chief Tester' }], + ['job description (role)', 'organizations:o1|titles:t1,r1', { data6: 'Mentor' }, { 'titles/r1/name': 'Mentor' }], + ['website', 'links:l1', { data1: 'https://probe2.example' }, { 'links/l1/uri': 'https://probe2.example' }], + ['website type (work → home)', 'links:l1', { data2: 4 }, { 'links/l1/contexts': { private: true } }], + ['birthday date', 'anniversaries:bday', { data1: '--03-15' }, { 'anniversaries/bday/date': { '@type': 'PartialDate', month: 3, day: 15 } }], + ['anniversary with a year', 'anniversaries:wed', { data1: '2011-06-01' }, { 'anniversaries/wed/date': { '@type': 'PartialDate', year: 2011, month: 6, day: 1 } }], + ['anniversary type (birthday → other)', 'anniversaries:bday', { data2: 2 }, { 'anniversaries/bday/kind': 'other' }], + ['relation type (friend → spouse)', 'relatedTo:urn:uuid:other-person', { data2: 14 }, { 'relatedTo/urn:uuid:other-person/relation': { spouse: true } }], + ['relation type without an RFC twin (brother)', 'relatedTo:urn:uuid:other-person', { data2: 2 }, { 'relatedTo/urn:uuid:other-person/relation': { sibling: true } }], + ['note', 'notes:n1', { data1: 'edited note' }, { 'notes/n1/note': 'edited note' }], +]; + +describe('contacts upload: one edited field, one pointer', () => { + it.each(probeEdits)('%s', async (_name, key, values, expected) => { + const { patch, card } = await editedPatch(probeCard, key, values); + expect(patch).toEqual(expected); + assertPatchWellFormed(patch); + const applied = applyPatch(card, patch); + expect(applied).not.toBeNull(); + for (const path of UNMAPPED) expect(deepEqual(at(applied, path), at(card, path))).toBe(true); + }); + + it('moves IS_PRIMARY as pref', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + const rows = h.rowsByKey(rawId); + h.device.user.updateData(rows['emails:x/y']._id as number, { is_primary: 1 }); + h.device.user.updateData(rows['emails:work1']._id as number, { is_primary: 0 }); + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + expect(plan).toMatchObject({ kind: 'upload', actions: [{ kind: 'update', patch: { 'emails/x~1y/pref': 1, 'emails/work1/pref': null } }] }); + }); + + it('renames a relation by moving it to the new key (relatedTo is keyed by value)', async () => { + const { patch } = await editedPatch(probeCard, 'relatedTo:urn:uuid:other-person', { data1: 'Jane Roe' }); + expect(patch).toEqual({ relatedTo: { 'Jane Roe': { relation: { friend: true } } } }); + }); + + it('keeps a free-text date the server cannot take on the device only', async () => { + const h = new Harness(); + const { rawId } = await h.seed(probeCard()); + h.device.user.updateData(h.rowsByKey(rawId)['anniversaries:bday']._id as number, { data1: 'next spring' }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx).kind).toBe('clean'); + }); +}); + +describe('contacts upload: the name', () => { + const NAME_KEY = 'name'; + + it('re-derives a derived `full` when a component changes (and the provider recomputed the display name)', async () => { + const { patch } = await editedPatch(probeCard, NAME_KEY, { data2: 'Probert', data1: 'Dr. Probert Person' }); + expect(patch).toEqual({ + 'name/components': [ + { kind: 'title', value: 'Dr.' }, + { kind: 'given', value: 'Probert', phonetic: 'Proob' }, + { kind: 'surname', value: 'Person' }, + ], + 'name/full': 'Dr. Probert Person', + }); + }); + + it('uploads a typed display name as `full` alone', async () => { + const { patch } = await editedPatch(probeCard, NAME_KEY, { data1: 'Probe P.' }); + expect(patch).toEqual({ 'name/full': 'Probe P.' }); + }); + + it('leaves a custom `full` alone when only components change', async () => { + const { patch } = await editedPatch(googleCard, NAME_KEY, { data3: 'Gross', data1: 'Prof. Günther Karl Gross, PhD' }); + expect(patch).toEqual({ + 'name/components': [ + { kind: 'title', value: 'Prof.' }, + { kind: 'given', value: 'Günther' }, + { kind: 'given2', value: 'Karl' }, + { kind: 'surname', value: 'Gross' }, + { kind: 'credential', value: 'PhD' }, + ], + }); + }); + + it('adds a component the name did not have in display order', async () => { + const { patch } = await editedPatch(appleCard, NAME_KEY, { data5: 'Maria', data1: 'Anna Maria Apfel' }); + expect(patch).toEqual({ + 'name/components': [{ kind: 'given', value: 'Anna' }, { kind: 'given2', value: 'Maria' }, { kind: 'surname', value: 'Apfel' }], + 'name/full': 'Anna Maria Apfel', + }); + }); + + it('removes a component whose column was emptied', async () => { + const { patch } = await editedPatch(probeCard, NAME_KEY, { data4: null, data1: 'Probe Person' }); + expect(patch['name/components']).toEqual([{ kind: 'given', value: 'Probe', phonetic: 'Proob' }, { kind: 'surname', value: 'Person' }]); + expect(patch['name/full']).toBe('Probe Person'); + }); + + it('uploads a phonetic name as the component phonetic', async () => { + const { patch } = await editedPatch(probeCard, NAME_KEY, { data9: 'Pörson' }); + expect(patch).toEqual({ + 'name/components': [ + { kind: 'title', value: 'Dr.' }, + { kind: 'given', value: 'Probe', phonetic: 'Proob' }, + { kind: 'surname', value: 'Person', phonetic: 'Pörson' }, + ], + }); + }); + + it('uploads a retyped name the server holds as `full` alone whole: `full` and every component', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed({ name: { full: 'Ada' } }); + // A typed display name: the provider splits it (the fake does so when no component is given). + h.device.user.updateData(h.rowsByKey(rawId).name._id as number, { data1: 'Ada King', data2: null, data3: null, data4: null, data5: null, data6: null }); + expect(h.rowsByKey(rawId).name).toMatchObject({ data1: 'Ada King', data2: 'Ada', data3: 'King' }); + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + expect(plan).toMatchObject({ + kind: 'upload', + actions: [{ patch: { 'name/components': [{ kind: 'given', value: 'Ada' }, { kind: 'surname', value: 'King' }], 'name/full': 'Ada King' } }], + }); + await h.upload(rawId); + expect(h.card(id).name).toMatchObject({ full: 'Ada King', components: [{ kind: 'given', value: 'Ada' }, { kind: 'surname', value: 'King' }] }); + const local = await h.contact(rawId); + expect(local.dirty).toBe(false); + expect(h.rowsByKey(rawId).name).toMatchObject({ data1: 'Ada King', data2: 'Ada', data3: 'King' }); + expect(h.planner.planDownload(h.card(id), local, h.ctx).effect).toBe('none'); + }); + + it('re-derives a derived `full` when a retyped display name adds a component', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed({ name: { components: [{ kind: 'given', value: 'Ada' }], full: 'Ada' } }); + h.device.user.updateData(h.rowsByKey(rawId).name._id as number, { data1: 'Ada King', data2: null, data3: null, data4: null, data5: null, data6: null }); + await h.upload(rawId); + expect(h.card(id).name).toMatchObject({ full: 'Ada King', components: [{ kind: 'given', value: 'Ada' }, { kind: 'surname', value: 'King' }] }); + }); + + it('drops surname2 only when the family name changed', async () => { + const card = () => ({ name: { components: [{ kind: 'given', value: 'Ana' }, { kind: 'surname', value: 'García' }, { kind: 'surname2', value: 'López' }], full: 'Ana García López' } }); + const h = new Harness(); + const { rawId } = await h.seed(card()); + expect(h.rowsByKey(rawId).name.data3).toBe('García López'); + const { patch } = await editedPatch(card, NAME_KEY, { data3: 'García Pérez', data1: 'Ana García Pérez' }); + expect(patch['name/components']).toEqual([{ kind: 'given', value: 'Ana' }, { kind: 'surname', value: 'García Pérez' }]); + }); +}); + +describe('contacts upload: nothing mapped changed', () => { + it.each([['probe', probeCard], ['apple', appleCard], ['google', googleCard]])('clears DIRTY without a request for a starred %s card', async (_n, make) => { + const h = new Harness(); + const { rawId } = await h.seed(make()); + const before = JSON.stringify(h.device.rows('data', 'raw_contact_id = ?', [rawId])); + h.device.user.star(rawId); + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + expect(plan.kind).toBe('clean'); + if (plan.kind !== 'clean') return; + expect(plan.ops.ops.filter((o) => o.op !== 'assert' && o.op !== 'syncState' && o.table === 'data')).toEqual([]); + await h.applyOk(plan.ops); + expect(h.dirty(rawId)).toBe(false); + expect(JSON.stringify(h.device.rows('data', 'raw_contact_id = ?', [rawId]))).toBe(before); + }); + + it('never uploads IS_PRIMARY of a row an editor re-inserted', async () => { + const h = new Harness(); + const { rawId } = await h.seed(appleCard()); + const rows = h.rowsByKey(rawId); + h.device.user.deleteData(rows['emails:k1']._id as number); + h.device.user.insertData(rawId, { [Data.MIMETYPE]: MimeType.EMAIL, [Data.DATA1]: 'anna@example.com', [Data.DATA2]: 1, is_primary: 0 }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx).kind).toBe('clean'); + }); +}); diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts new file mode 100644 index 00000000..7c3c00c6 --- /dev/null +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -0,0 +1,221 @@ +// The engine with the real contacts and calendar planners (not the toy +// ones): the contract between them holds end to end. Mapping details are +// the planners' own tests; these check what the engine guarantees. + +import { describe, expect, it } from 'vitest'; +import { Data, MimeType } from '../../android-columns'; +import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { + addDeviceContact, + addServerCards, + CALENDAR_AUTHORITY, + createHarness, + rowWrites, + type Harness, +} from './harness'; + +function real(options: Parameters[0] = {}): Harness { + const h = createHarness(options); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + return h; +} + +/** A device edit of the display name only, as AOSP Contacts makes it (the provider fills the components). */ +function retypeName(h: Harness, rawContactId: number, name: string): void { + const row = h.nameDataRow(rawContactId)!; + h.device.user.updateData(Number(row._id), { [Data.DATA1]: name }); +} + +function serverUids(h: Harness): string[] { + return h.server.all('ContactCard', 'a').map((c) => String(c.uid)); +} + +describe('device sync engine with the real planners', () => { + it('syncs contacts both ways and writes nothing for its own echo', async () => { + const h = real(); + const [, grace] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper']); + expect((await h.run()).outcome).toBe('ok'); + expect(h.contacts().map((c) => c.name).sort()).toEqual(['Ada Lovelace', 'Grace Hopper']); + + addDeviceContact(h, 'Hedy Lamarr'); + h.device.user.deleteContact(h.contactNamed('Grace Hopper')!.id); + const up = await h.run(); + expect(up).toMatchObject({ outcome: 'ok', itemErrors: [] }); + expect(up.stats.uploaded).toMatchObject({ created: 1, deleted: 1 }); + expect(h.server.get('ContactCard', 'a', grace)).toBeUndefined(); + expect(h.contacts().every((c) => c.sourceId && !c.dirty)).toBe(true); + expect(new Set(serverUids(h)).size).toBe(2); + + const before = h.batches.log.length; + const echo = await h.run(); + expect(echo.outcome).toBe('ok'); + expect(rowWrites(h.batches.log.slice(before))).toEqual([]); + }); + + it('converges with the real contacts planner after a crash at any checkpoint', async () => { + const setup = async () => { + const h = real(); + addServerCards(h, ['Ada Lovelace', 'Grace Hopper', 'Linus Torvalds']); + await h.run(); + retypeName(h, h.contactNamed('Grace Hopper')!.id, 'Grace B. Hopper'); + addDeviceContact(h, 'Hedy Lamarr'); + h.device.user.deleteContact(h.contactNamed('Linus Torvalds')!.id); + h.server.serverUpdate('ContactCard', 'a', h.contactNamed('Ada Lovelace')!.sourceId!.split('/')[1], { 'name/full': 'Ada King' }); + addServerCards(h, ['Margaret Hamilton']); + h.checkpoints.count = 0; + return h; + }; + const probe = await setup(); + await probe.run(); + const final = probe.contacts().map((c) => c.name).sort(); + const finalServer = probe.serverNames(); + const checkpoints = probe.checkpoints.count; + + for (let n = 1; n <= checkpoints; n++) { + const h = await setup(); + h.checkpoints.crashAt = n; + await h.run(); + h.checkpoints.crashAt = null; + const report = await h.run(); + expect(report.outcome, `crash at checkpoint ${n}`).toBe('ok'); + expect(h.contacts().map((c) => c.name).sort(), `device after crash ${n}`).toEqual(final); + expect(h.serverNames(), `server after crash ${n}`).toEqual(finalServer); + expect(h.contacts().every((c) => !c.dirty && !c.deleted)).toBe(true); + expect(new Set(serverUids(h)).size).toBe(serverUids(h).length); + } + }); + + it('adopts a lost create by uid with the real contacts planner', async () => { + const h = real(); + addServerCards(h, ['Ada Lovelace']); + await h.run(); + h.server.setUidIndexLag(3); + const id = addDeviceContact(h, 'Hedy Lamarr'); + h.server.applyThenLoseResponse({ match: 'ContactCard/set' }); + + expect((await h.run()).outcome).toBe('io'); + expect((await h.run()).outcome).toBe('ok'); + + expect(h.serverNames()).toEqual(['Ada Lovelace', 'Hedy Lamarr']); + expect(h.contacts()).toHaveLength(2); + expect(h.contacts().find((c) => c.id === id)).toMatchObject({ dirty: false, sourceId: expect.stringMatching(/^a\//) }); + expect(h.device.rows('data').filter((d) => d.mimetype === MimeType.STRUCTURED_NAME)).toHaveLength(2); + }); + + it("follows a group's members when only the group card changed on the server", async () => { + const h = real(); + const [ada, grace] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper']); + const uid = (id: string) => h.server.get('ContactCard', 'a', id)!.uid as string; + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { [uid(ada)]: true } }); + const members = () => + h.device + .rows('data') + .filter((d) => d.mimetype === MimeType.GROUP_MEMBERSHIP) + .map((d) => h.contacts().find((c) => c.id === Number(d.raw_contact_id))?.name) + .sort(); + expect((await h.run()).outcome).toBe('ok'); + expect(members()).toEqual(['Ada Lovelace']); + + h.server.serverUpdate('ContactCard', 'a', group, { members: { [uid(grace)]: true } }); + expect((await h.run()).outcome).toBe('ok'); + + expect(members()).toEqual(['Grace Hopper']); + expect(h.contacts().every((c) => !c.dirty)).toBe(true); + }); + + it('creates a device event that carries the uid of a synced event (an app copied it) under a fresh uid', async () => { + const h = real(); + const standup = h.server.addEvent('a', { + uid: 'standup-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const rowId = Number(h.device.rows('calendars')[0]._id); + + const copy = h.device.user.insertEvent(rowId, { + title: 'Standup (copy)', + dtstart: Date.UTC(2026, 8, 29, 7), + dtend: Date.UTC(2026, 8, 29, 7, 15), + eventTimezone: 'Europe/Berlin', + uid2445: 'standup-uid', + }); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 1 } } }); + expect(h.server.get('CalendarEvent', 'a', standup)).toMatchObject({ title: 'Standup', uid: 'standup-uid' }); + const created = h.server.all('CalendarEvent', 'a').find((e) => e.title === 'Standup (copy)')!; + expect(created.uid).not.toBe('standup-uid'); + expect(h.events().find((e) => Number(e._id) === copy)).toMatchObject({ _sync_id: `a/${created.id}`, uid2445: created.uid }); + expect(h.events().every((e) => Number(e.dirty ?? 0) === 0)).toBe(true); + }); + + it('uploads "this and following" in one run: the capped rule and the new series', async () => { + const h = real(); + const series = h.server.addEvent('a', { + uid: 'series-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'daily' }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const master = h.events()[0]; + expect(master).toMatchObject({ rrule: 'FREQ=DAILY' }); + const rowId = Number(master.calendar_id); + + // Etar: the master's rule ends before the split, a new series starts there (it copies the uid). + h.device.user.updateEvent(Number(master._id), { rrule: 'FREQ=DAILY;UNTIL=20260930T235959Z' }); + const later = h.device.user.insertEvent(rowId, { + title: 'Standup', + dtstart: Date.UTC(2026, 9, 1, 7), + duration: 'P900S', + rrule: 'FREQ=DAILY', + eventTimezone: 'Europe/Berlin', + uid2445: 'series-uid', + }); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 1, updated: 1 } } }); + const capped = h.server.get('CalendarEvent', 'a', series)!; + expect(capped.recurrenceRule).toMatchObject({ frequency: 'daily', until: expect.any(String) }); + const next = h.server.all('CalendarEvent', 'a').find((e) => e.id !== series)!; + expect(next).toMatchObject({ start: '2026-10-01T09:00:00', timeZone: 'Europe/Berlin', recurrenceRule: { frequency: 'daily' } }); + expect(next.uid).not.toBe('series-uid'); + expect(h.events().find((e) => Number(e._id) === later)).toMatchObject({ _sync_id: `a/${next.id}` }); + expect(h.events().every((e) => Number(e.dirty ?? 0) === 0)).toBe(true); + }); + + it('syncs events both ways and writes nothing for its own echo', async () => { + const h = real(); + const standup = h.server.addEvent('a', { + uid: 'e1', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + calendarIds: { [h.calendar]: true }, + }); + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + const rowId = Number(h.device.rows('calendars')[0]._id); + + h.device.user.insertEvent(rowId, { title: 'Lunch', dtstart: Date.UTC(2026, 8, 28, 10), dtend: Date.UTC(2026, 8, 28, 11), eventTimezone: 'Europe/Berlin' }); + h.device.user.updateEvent(Number(h.events().find((e) => e.title === 'Standup')!._id), { title: 'Daily standup' }); + const up = await h.run(CALENDAR_AUTHORITY); + + expect(up).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 1, updated: 1 } } }); + expect(h.server.get('CalendarEvent', 'a', standup)).toMatchObject({ title: 'Daily standup' }); + expect(h.server.all('CalendarEvent', 'a').find((e) => e.title === 'Lunch')).toMatchObject({ start: '2026-09-28T12:00:00', timeZone: 'Europe/Berlin' }); + expect(h.events().every((e) => Number(e.dirty ?? 0) === 0 && String(e._sync_id).startsWith('a/'))).toBe(true); + + const before = h.batches.log.length; + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + expect(rowWrites(h.batches.log.slice(before))).toEqual([]); + }); +}); diff --git a/src/device-sync/contacts/cells.ts b/src/device-sync/contacts/cells.ts new file mode 100644 index 00000000..daa2ee3c --- /dev/null +++ b/src/device-sync/contacts/cells.ts @@ -0,0 +1,76 @@ +/** + * Data-row cells as the contacts planner compares them. The bridge hands + * numbers back as numbers or text, "" and NULL mean the same (the design's + * rule for names, and what editors do to every field), and IS_PRIMARY 0 is + * the same as unset. Baselines (DATA_SYNC3) keep text over 1 KB as its hash. + */ +import { sha256Hex } from '../../lib/sha256'; +import { Data } from '../android-columns'; +import { parseJsonColumn, utf8 } from '../common/json'; +import type { Cell, Row } from '../types'; + +const LONG_TEXT = 1024; +const HASH_PREFIX = '#sha256:'; + +/** Comparable text of a cell; null for NULL, "" and anything that is not a cell. */ +export function cellText(value: unknown, column?: string): string | null { + let text: string | null = null; + if (typeof value === 'string') text = value === '' ? null : value; + else if (typeof value === 'number' && Number.isFinite(value)) text = String(value); + if (column === Data.IS_PRIMARY && text === '0') return null; + return text; +} + +export function sameCell(a: unknown, b: unknown, column?: string): boolean { + return cellText(a, column) === cellText(b, column); +} + +function baselineValue(value: unknown, column: string): string | null { + const text = cellText(value, column); + if (text !== null && text.length > LONG_TEXT) return `${HASH_PREFIX}${sha256Hex(utf8(text))}`; + return text; +} + +/** DATA_SYNC3 of a row: its mapped cells, long text hashed. */ +export function encodeBaseline(cells: Row, columns: readonly string[]): string { + const out: Record = {}; + for (const c of columns) if (c in cells) out[c] = baselineValue(cells[c], c); + return JSON.stringify(out); +} + +/** A baseline read back from DATA_SYNC3; null when another app left something else there. */ +export function decodeBaseline(text: unknown): Row | null { + const parsed = parseJsonColumn(text); + if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null; + const out: Row = {}; + for (const [k, v] of Object.entries(parsed as Record)) { + if (v !== null && typeof v !== 'string' && typeof v !== 'number') return null; + out[k] = v as Cell; + } + return out; +} + +/** Whether a cell still holds what the baseline recorded for it. */ +function matchesBaseline(value: unknown, baseline: Row, column: string): boolean { + const recorded = cellText(baseline[column], column); + if (recorded !== null && recorded.startsWith(HASH_PREFIX)) return baselineValue(value, column) === recorded; + return cellText(value, column) === recorded; +} + +/** Mapped columns that differ from the baseline; columns the baseline never recorded count as unchanged. */ +export function changedFromBaseline(cells: Row, baseline: Row, columns: readonly string[]): string[] { + return columns.filter((c) => c in baseline && !matchesBaseline(cells[c], baseline, c)); +} + +/** The cell as text for building card values; null when blank. */ +export function text(value: unknown): string | null { + const t = cellText(value); + return t === null || t.trim() === '' ? null : t; +} + +export function num(value: unknown): number | null { + const t = cellText(value); + if (t === null) return null; + const n = Number(t); + return Number.isFinite(n) ? n : null; +} diff --git a/src/device-sync/contacts/columns.ts b/src/device-sync/contacts/columns.ts new file mode 100644 index 00000000..87701327 --- /dev/null +++ b/src/device-sync/contacts/columns.ts @@ -0,0 +1,113 @@ +/** + * The columns the engine queries for contacts, and the decoders that turn + * rows into `LocalContact`/`LocalGroup`. Sync columns are read defensively: + * any app holding WRITE_CONTACTS can write them, so what does not parse is + * treated as absent. + */ +import { Data, GroupMembership, Groups, RawContacts } from '../android-columns'; +import { parseCollectionKey } from '../common/ids'; +import { parseJsonColumn } from '../common/json'; +import type { ContactCardWire, LocalContact, LocalDataRow, LocalGroup, PendingCreate, PoisonMarker } from '../planner'; +import type { Row } from '../types'; +import { cellText, decodeBaseline, num } from './cells'; + +const DATA_CELLS = [ + Data.IS_PRIMARY, Data.DATA1, Data.DATA2, Data.DATA3, Data.DATA4, Data.DATA5, Data.DATA6, Data.DATA7, + Data.DATA8, Data.DATA9, Data.DATA10, Data.DATA11, Data.DATA12, Data.DATA13, Data.DATA14, + GroupMembership.GROUP_SOURCE_ID, +]; + +export const RAW_CONTACT_COLUMNS: readonly string[] = [ + RawContacts._ID, RawContacts.SOURCE_ID, RawContacts.VERSION, RawContacts.DIRTY, RawContacts.DELETED, + RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4, RawContacts.RAW_CONTACT_IS_READ_ONLY, +]; + +export const DATA_COLUMNS: readonly string[] = [ + Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE, ...DATA_CELLS, Data.DATA_SYNC1, Data.DATA_SYNC2, Data.DATA_SYNC3, +]; + +export const GROUP_COLUMNS: readonly string[] = [ + Groups._ID, Groups.SOURCE_ID, Groups.VERSION, Groups.DIRTY, Groups.DELETED, Groups.TITLE, + Groups.SYNC2, Groups.SYNC3, Groups.SYNC4, +]; + +const flag = (v: unknown) => num(v) === 1; + +function stringOrNull(v: unknown): string | null { + return typeof v === 'string' && v !== '' ? v : null; +} + +export function decodeShadow(v: unknown): ContactCardWire | null { + const card = parseJsonColumn(v); + return card && typeof card === 'object' && !Array.isArray(card) && typeof (card as { id?: unknown }).id === 'string' + ? (card as ContactCardWire) + : null; +} + +export function decodePending(v: unknown): PendingCreate | null { + const p = parseJsonColumn>(v); + if (!p || typeof p !== 'object' || typeof p.uid !== 'string' || !p.uid || typeof p.target !== 'string') return null; + return parseCollectionKey(p.target) ? { uid: p.uid, target: p.target } : null; +} + +export function decodePoison(v: unknown): PoisonMarker | null { + const p = parseJsonColumn>(v); + if (!p || typeof p !== 'object' || typeof p.fp !== 'string' || typeof p.type !== 'string') return null; + if (typeof p.n !== 'number' || typeof p.until !== 'number') return null; + return { + fp: p.fp, + type: p.type, + n: p.n, + until: p.until, + ...(typeof p.description === 'string' ? { description: p.description } : {}), + }; +} + +function decodeDataRow(row: Row): LocalDataRow { + const cells: Row = {}; + for (const c of DATA_CELLS) if (c in row) cells[c] = row[c]; + return { + id: Number(row[Data._ID]), + mimetype: String(row[Data.MIMETYPE] ?? ''), + cells, + key: stringOrNull(row[Data.DATA_SYNC1]), + photoHash: stringOrNull(row[Data.DATA_SYNC2]), + baseline: decodeBaseline(row[Data.DATA_SYNC3]), + }; +} + +export function decodeContact(rawContact: Row, data: Row[]): LocalContact { + const id = Number(rawContact[RawContacts._ID]); + const own = data.filter((d) => !(Data.RAW_CONTACT_ID in d) || Number(d[Data.RAW_CONTACT_ID]) === id); + const collections = (cellText(rawContact[RawContacts.SYNC1]) ?? '') + .split(',') + .map((s) => s.trim()) + .filter((s) => parseCollectionKey(s)); + return { + rawContactId: id, + sourceId: stringOrNull(rawContact[RawContacts.SOURCE_ID]), + version: num(rawContact[RawContacts.VERSION]) ?? 0, + dirty: flag(rawContact[RawContacts.DIRTY]), + deleted: flag(rawContact[RawContacts.DELETED]), + readOnly: flag(rawContact[RawContacts.RAW_CONTACT_IS_READ_ONLY]), + collections, + shadow: decodeShadow(rawContact[RawContacts.SYNC2]), + pending: decodePending(rawContact[RawContacts.SYNC3]), + poison: decodePoison(rawContact[RawContacts.SYNC4]), + rows: own.map(decodeDataRow), + }; +} + +export function decodeGroup(group: Row): LocalGroup { + return { + groupId: Number(group[Groups._ID]), + sourceId: stringOrNull(group[Groups.SOURCE_ID]), + version: num(group[Groups.VERSION]) ?? 0, + dirty: flag(group[Groups.DIRTY]), + deleted: flag(group[Groups.DELETED]), + title: typeof group[Groups.TITLE] === 'string' ? (group[Groups.TITLE] as string) : null, + shadow: decodeShadow(group[Groups.SYNC2]), + pending: decodePending(group[Groups.SYNC3]), + poison: decodePoison(group[Groups.SYNC4]), + }; +} diff --git a/src/device-sync/contacts/diff.ts b/src/device-sync/contacts/diff.ts new file mode 100644 index 00000000..e0dac5d5 --- /dev/null +++ b/src/device-sync/contacts/diff.ts @@ -0,0 +1,61 @@ +/** + * What changed in a unit, on either side (docs/device-sync.md, "Row + * matching"). A row matched by its key is compared column by column with its + * baseline, so an emptied column is a real deletion. A row matched by value + * has no baseline: its values count only where they are non-empty and differ + * from the shadow's projection, TYPE and LABEL only for phones, emails, + * addresses and events, and IS_PRIMARY never. + */ +import { CommonKind, Data, MimeType, StructuredPostal as SP } from '../android-columns'; +import type { Row } from '../types'; +import { changedFromBaseline, sameCell, text } from './cells'; +import type { Match } from './matching'; +import { POSTAL_PART_COLUMNS } from './postal'; +import { SPECS, type Unit } from './project'; + +/** + * A postal row whose parts are only its formatted address copied into STREET: + * the provider's split of an editor that wrote FORMATTED_ADDRESS alone. + */ +function providerSplitPostal(cells: Row): boolean { + const street = text(cells[SP.STREET]); + return street !== null + && street === text(cells[SP.FORMATTED_ADDRESS]) + && POSTAL_PART_COLUMNS.every((c) => c === SP.STREET || text(cells[c]) === null); +} + +export function localChanges(m: Match): string[] { + const { row, unit } = m; + const changed = rawChanges(m).filter((c) => !unit.truncated?.includes(c)); + if (row.mimetype === MimeType.STRUCTURED_POSTAL && providerSplitPostal(row.cells)) { + // A multi-part formatted address in STREET was the provider's split, not a typed street. + const split = m.how !== 'key' || !row.baseline || /[,\n]/.test(text(row.cells[SP.FORMATTED_ADDRESS]) ?? ''); + if (split) return changed.filter((c) => !(POSTAL_PART_COLUMNS as readonly string[]).includes(c)); + } + return changed; +} + +function rawChanges(m: Match): string[] { + const spec = SPECS[m.row.mimetype]; + const { row, unit } = m; + if (m.how === 'key' && row.baseline) return changedFromBaseline(row.cells, row.baseline, spec.columns); + if (row.mimetype === MimeType.PHOTO) { + // A photo row without our baseline was put there by an editor: its picture counts as new. + return m.how === 'key' ? [] : [Data.DATA14]; + } + const out = spec.valueColumns.filter((c) => text(row.cells[c]) !== null && !sameCell(row.cells[c], unit.cells[c], c)); + if (spec.typed && text(row.cells[CommonKind.TYPE]) !== null && !sameCell(row.cells[CommonKind.TYPE], unit.cells[CommonKind.TYPE])) { + out.push(CommonKind.TYPE, CommonKind.LABEL); + } else if (spec.typed && text(row.cells[CommonKind.LABEL]) !== null && !sameCell(row.cells[CommonKind.LABEL], unit.cells[CommonKind.LABEL])) { + out.push(CommonKind.LABEL); + } + return out; +} + +/** Whether two units show the same on the device (sub-fields Android can't hold don't count). */ +export function sameUnit(a: Unit, b: Unit): boolean { + if (a.mimetype !== b.mimetype) return false; + // A photo whose bytes could not be had compares as unchanged. + if (a.mimetype === MimeType.PHOTO) return a.photoHash === undefined || b.photoHash === undefined || a.photoHash === b.photoHash; + return SPECS[a.mimetype].columns.every((c) => sameCell(a.cells[c], b.cells[c], c)); +} diff --git a/src/device-sync/contacts/download.ts b/src/device-sync/contacts/download.ts new file mode 100644 index 00000000..4cf57042 --- /dev/null +++ b/src/device-sync/contacts/download.ts @@ -0,0 +1,340 @@ +/** + * Server cards into rows (docs/device-sync.md, "Change detection and merge + * rules"): inserts, writes that make a contact's rows the card's (a clean + * contact, an accepted upload, a revert), and the per-unit merge of a dirty + * contact with a new server version. + * + * Rows are matched to the shadow's units after its keys were mapped to the + * new version's (Stalwart re-keys cards written over CardDAV). A clean write + * then touches only rows that differ, so an echo of our own upload writes + * nothing. A merge writes only units the server changed, and never gives a + * key to a row of a kind an editor rewrote (all its rows keyless): a keyed + * row there would later make the entries the editor could not show look + * deleted. + */ +import { Data, MimeType, RawContacts } from '../android-columns'; +import { canonicalJson } from '../common/json'; +import { objectRef } from '../common/ids'; +import type { ContactCardWire, ContactsContext, DownloadPlan, LocalContact, LocalDataRow } from '../planner'; +import type { ProviderOp, WriteRow } from '../types'; +import { changedFromBaseline, encodeBaseline, sameCell } from './cells'; +import { localChanges, sameUnit } from './diff'; +import { deletionsInferred, inPlaceEvidence, matchKind, reconcileKeys, type KindMatch } from './matching'; +import { planMemberships, remoteMemberships, shadowMemberOf, withMemberOf, withoutMemberOf } from './members'; +import { assertContact, deleteData, insertUnit, updateData, updateRawContact, updateUnit } from './ops'; +import { shadowOf } from './photo'; +import { KIND_ORDER, projectCard, rowMatchesUnit, SPECS, type Unit } from './project'; + +const ENTRY_KINDS = KIND_ORDER.filter((k) => k !== MimeType.GROUP_MEMBERSHIP); + +type Ctx = ContactsContext; + +export function cardUnits(card: ContactCardWire | null, ctx: Pick): Unit[] { + return card ? projectCard(withoutMemberOf(card), ctx, { memberOf: null }) : []; +} + +/** Raw contact columns that describe the server card: collections, shadow, read-only flag. */ +function cardColumns(card: ContactCardWire, shadow: ContactCardWire, ctx: Ctx): WriteRow { + return { + [RawContacts.SYNC1]: ctx.selectedCollections(card).join(',') || null, + [RawContacts.SYNC2]: JSON.stringify(shadow), + [RawContacts.RAW_CONTACT_IS_READ_ONLY]: ctx.isReadOnly(card) ? 1 : 0, + }; +} + +/** Only the raw contact columns that differ from what the local item holds. */ +function changedRawColumns(local: LocalContact, values: WriteRow): WriteRow { + const current: Record = { + [RawContacts.SOURCE_ID]: local.sourceId, + [RawContacts.SYNC1]: local.collections.join(',') || null, + [RawContacts.SYNC2]: local.shadow ? JSON.stringify(local.shadow) : null, + [RawContacts.SYNC3]: local.pending ? JSON.stringify(local.pending) : null, + [RawContacts.SYNC4]: local.poison ? JSON.stringify(local.poison) : null, + [RawContacts.RAW_CONTACT_IS_READ_ONLY]: local.readOnly ? 1 : 0, + [RawContacts.DIRTY]: local.dirty ? 1 : 0, + }; + const out: WriteRow = {}; + for (const [column, value] of Object.entries(values)) { + if (column === RawContacts.SYNC2) { + const before = local.shadow ? canonicalJson(local.shadow) : null; + const after = typeof value === 'string' ? canonicalJson(JSON.parse(value)) : null; + if (before !== after) out[column] = value; + } else if (!sameCell(current[column], value as unknown)) { + out[column] = value; + } + } + return out; +} + +/** Row keys after the server re-keyed: mapped, or marked gone so they can't meet a new entry's key. */ +function renameRows(rows: LocalDataRow[], base: Unit[], keyMap: Map): LocalDataRow[] { + const baseKeys = new Set(base.map((u) => u.key)); + return rows.map((r) => { + if (!r.key) return r; + const key = keyMap.get(r.key) ?? (baseKeys.has(r.key) ? `~gone:${r.key}` : r.key); + return key === r.key ? r : { ...r, key }; + }); +} + +function renameUnits(base: Unit[], keyMap: Map): Unit[] { + return base.map((u) => ({ ...u, key: keyMap.get(u.key) ?? `~gone:${u.key}` })); +} + +interface RowPlan { + ops: ProviderOp[]; + conflicts: number; + stillDirty: boolean; + evidence: boolean; +} + +/** + * Makes the rows of every entry kind the card's: rows matched to a unit are + * rewritten when they differ (or only get their key and baseline), rows no + * unit accounts for go, units without a row are inserted. + */ +function cleanRows(local: LocalContact, remote: Unit[], keyMap: Map, base: Unit[], accepted: boolean): RowPlan { + const ops: ProviderOp[] = []; + const rows = renameRows(local.rows, base, keyMap); + const kinds = ENTRY_KINDS.map((k) => matchKind(k, rows, remote)); + for (const kind of kinds) { + for (const m of kind.matched) { + const original = local.rows.find((r) => r.id === m.row.id)!; + const { unit } = m; + const columns = SPECS[unit.mimetype].columns; + if (unit.mimetype === MimeType.PHOTO) { + if (accepted && unit.photoHash && original.photoHash !== unit.photoHash && localChanges({ ...m, row: original }).length) { + // The device's own photo just uploaded: record the server's hash, keep the picture. + // (A photo that could not be sent keeps its old baseline, so it still counts as changed.) + ops.push(updateData(m.row.id, { + [Data.DATA_SYNC1]: unit.key, + [Data.DATA_SYNC2]: unit.photoHash ?? null, + [Data.DATA_SYNC3]: encodeBaseline(m.row.cells, columns), + })); + } else if (original.photoHash !== unit.photoHash && unit.extra) { + ops.push(updateUnit(m.row.id, unit, true)); + } else if (original.key !== unit.key) { + ops.push(updateData(m.row.id, { [Data.DATA_SYNC1]: unit.key })); + } + continue; + } + if (!rowMatchesUnit(unit, m.row.cells, sameCell)) ops.push(updateUnit(m.row.id, unit, true)); + else if (original.key !== unit.key || !original.baseline || changedFromBaseline(m.row.cells, original.baseline, columns).length) { + // Same content: only the key and the baseline (what the provider holds now) are written. + ops.push(updateData(m.row.id, { [Data.DATA_SYNC1]: unit.key, [Data.DATA_SYNC3]: encodeBaseline(m.row.cells, columns) })); + } + } + for (const row of kind.fresh) ops.push(deleteData(row.id)); + for (const unit of kind.missing) { + if (unit.mimetype === MimeType.PHOTO && !unit.extra) continue; + ops.push(insertUnit(local.rawContactId, unit, true)); + } + } + return { ops, conflicts: 0, stillDirty: false, evidence: inPlaceEvidence(kinds) }; +} + +/** Per-unit merge of a dirty contact's rows with a new server version; the server wins real conflicts. */ +function mergeRows(local: LocalContact, base: Unit[], remote: Unit[], keyMap: Map): RowPlan { + const ops: ProviderOp[] = []; + let conflicts = 0; + let stillDirty = false; + const rows = renameRows(local.rows, base, keyMap); + const renamedBase = renameUnits(base, keyMap); + const kinds: KindMatch[] = ENTRY_KINDS.map((k) => matchKind(k, rows, renamedBase)); + const evidence = inPlaceEvidence(kinds); + const mapped = new Set(keyMap.values()); + + for (const kind of kinds) { + const remoteOf = new Map(remote.filter((u) => u.mimetype === kind.mimetype).map((u) => [u.key, u])); + const hasKeyed = kind.matched.some((m) => m.how === 'key'); + const hasKeyless = kind.fresh.length > 0 || kind.matched.some((m) => m.how !== 'key'); + const keyed = hasKeyed || !hasKeyless; + const deletions = kind.mimetype !== MimeType.STRUCTURED_NAME && deletionsInferred(kind, evidence); + const write = (row: LocalDataRow, unit: Unit) => { + if (unit.mimetype === MimeType.PHOTO && !unit.extra) return; + ops.push(updateUnit(row.id, unit, keyed)); + }; + + for (const m of kind.matched) { + const original = local.rows.find((r) => r.id === m.row.id)!; + const remoteUnit = remoteOf.get(m.unit.key); + const changedHere = localChanges({ ...m, row: original }).length > 0; + if (!remoteUnit) { + // Removed on the server: the delete wins, over a local edit too. + if (changedHere) conflicts++; + ops.push(deleteData(m.row.id)); + } else if (sameUnit(m.unit, remoteUnit)) { + if (changedHere) stillDirty = true; + if (m.how === 'key' && original.key !== remoteUnit.key) ops.push(updateData(m.row.id, { [Data.DATA_SYNC1]: remoteUnit.key })); + } else if (!changedHere) { + write(m.row, remoteUnit); + } else if (remoteUnit.mimetype !== MimeType.PHOTO && rowMatchesUnit(remoteUnit, m.row.cells, sameCell)) { + // Both sides made the same change: converged, new baseline. + if (keyed) { + ops.push(updateData(m.row.id, { [Data.DATA_SYNC1]: remoteUnit.key, [Data.DATA_SYNC3]: encodeBaseline(m.row.cells, SPECS[kind.mimetype].columns) })); + } + } else { + conflicts++; + write(m.row, remoteUnit); + } + } + + for (const unit of kind.missing) { + const remoteUnit = remoteOf.get(unit.key); + if (!deletions) continue; + if (!remoteUnit) continue; + if (sameUnit(unit, remoteUnit)) stillDirty = true; + else if (!(remoteUnit.mimetype === MimeType.PHOTO && !remoteUnit.extra)) { + conflicts++; + ops.push(insertUnit(local.rawContactId, remoteUnit, keyed)); + } + } + + // Entries the server added: a new local row with the same value converged with it. + const fresh = [...kind.fresh]; + const spec = SPECS[kind.mimetype]; + for (const unit of remoteOf.values()) { + if (mapped.has(unit.key)) continue; + const p = spec.primary(unit.cells); + const twin = p === null ? -1 : fresh.findIndex((r) => spec.primary(r.cells) === p); + if (twin >= 0) { + const [row] = fresh.splice(twin, 1); + if (keyed) ops.push(updateUnit(row.id, unit, true)); + } else if (!(unit.mimetype === MimeType.PHOTO && !unit.extra)) { + ops.push(insertUnit(local.rawContactId, unit, keyed)); + } + } + if (fresh.length) stillDirty = true; + } + return { ops, conflicts, stillDirty, evidence }; +} + +export interface CleanWriteOptions { + /** DIRTY the assert expects; null asserts VERSION only (an accepted upload, a revert). */ + assertDirty: boolean | null; + /** Clear DIRTY (unless membership changes still wait for the server). */ + clearDirty: boolean; + /** Extra raw contact columns: identity, SYNC3/SYNC4 resets, DELETED. */ + raw?: WriteRow; + /** The rows' changes were just uploaded (planAccepted): a changed photo is the server's now. */ + accepted?: boolean; +} + +/** + * The ops that make a local contact's rows, shadow and raw columns the + * server card's, behind the VERSION assert. Membership changes that the + * server does not have yet stay (and keep the contact dirty). + */ +export function cleanWrite(local: LocalContact, card: ContactCardWire, ctx: Ctx, options: CleanWriteOptions) { + const server = withoutMemberOf(card); + const base = cardUnits(local.shadow, ctx); + const remote = cardUnits(server, ctx); + const keyMap = reconcileKeys(base, remote); + const rows = cleanRows(local, remote, keyMap, base, options.accepted ?? false); + const members = planMemberships({ + rows: local.rows, + base: shadowMemberOf(local.shadow), + remote: remoteMemberships(server, ctx), + evidence: rows.evidence, + restore: true, + parent: local.rawContactId, + groupRowIdBySourceId: (g) => ctx.groupRowIdBySourceId(g), + }); + const shadow = withMemberOf(shadowOf(server), members.memberOf); + const raw = changedRawColumns(local, { + ...cardColumns(server, shadow, ctx), + ...(options.raw ?? {}), + ...(options.clearDirty && !members.pending ? { [RawContacts.DIRTY]: 0 } : {}), + }); + const dataOps = [...rows.ops, ...members.ops]; + const ops: ProviderOp[] = []; + if (Object.keys(raw).length) ops.push(updateRawContact(local.rawContactId, raw)); + ops.push(...dataOps); + return { + ops: ops.length ? [assertContact(local, options.assertDirty), ...ops] : [], + writes: ops.length, + pendingMembers: members.pending, + }; +} + +function insertPlan(card: ContactCardWire, ctx: Ctx): DownloadPlan { + const identity = objectRef(ctx.jmapAccountId, card.id); + const units = cardUnits(card, ctx); + const remote = remoteMemberships(card, ctx); + const members = planMemberships({ + rows: [], + base: remote === null ? null : [], + remote, + evidence: false, + restore: true, + parent: { ref: 0 }, + groupRowIdBySourceId: (g) => ctx.groupRowIdBySourceId(g), + }); + const shadow = withMemberOf(shadowOf(card), members.memberOf); + const ops: ProviderOp[] = [ + { op: 'insert', table: 'raw_contacts', values: { [RawContacts.SOURCE_ID]: identity, [RawContacts.DIRTY]: 0, ...cardColumns(card, shadow, ctx) } }, + ...units.filter((u) => u.mimetype !== MimeType.PHOTO || u.extra).map((u) => insertUnit({ ref: 0 }, u, true)), + ...members.ops, + ]; + return { ops: { ref: identity, ops }, conflicts: 0, stillDirty: false, effect: 'insert', writes: ops.length }; +} + +export function planContactDownload(card: ContactCardWire, local: LocalContact | null, ctx: Ctx): DownloadPlan { + const server = withoutMemberOf(card); + const identity = objectRef(ctx.jmapAccountId, server.id); + if (!local) return insertPlan(server, ctx); + const none = (stillDirty: boolean): DownloadPlan => ({ ops: { ref: identity, ops: [] }, conflicts: 0, stillDirty, effect: 'none', writes: 0 }); + // Deleted on the device: the delete wins and uploads as a destroy. + if (local.deleted) return none(true); + + if (!local.sourceId) { + // Our own create whose identity was never written: adopt it. The rows stay + // as they are and upload against the card as their new shadow. + const members = shadowMemberOf(local.shadow) ?? []; + const shadow = withMemberOf(shadowOf(server), remoteMemberships(server, ctx) === null ? null : members); + const ops = [ + assertContact(local, local.dirty), + updateRawContact(local.rawContactId, { [RawContacts.SOURCE_ID]: identity, [RawContacts.SYNC3]: null, ...cardColumns(server, shadow, ctx) }), + ]; + return { ops: { ref: identity, ops }, conflicts: 0, stillDirty: true, effect: 'update', writes: 1 }; + } + + const uidChanged = typeof local.shadow?.uid === 'string' && typeof server.uid === 'string' && local.shadow!.uid !== server.uid; + if (!local.dirty || uidChanged) { + // A different object behind the same id is replaced, never merged into. + const target = uidChanged ? { ...local, shadow: null, rows: local.rows.map((r) => ({ ...r, key: null })) } : local; + const write = cleanWrite(target, server, ctx, { assertDirty: local.dirty, clearDirty: uidChanged }); + return { + ops: { ref: identity, ops: write.ops }, + conflicts: 0, + stillDirty: write.pendingMembers, + effect: write.writes ? 'update' : 'none', + writes: write.writes, + }; + } + + const base = cardUnits(local.shadow, ctx); + const remote = cardUnits(server, ctx); + const keyMap = reconcileKeys(base, remote); + const rows = mergeRows(local, base, remote, keyMap); + const members = planMemberships({ + rows: local.rows, + base: shadowMemberOf(local.shadow), + remote: remoteMemberships(server, ctx), + evidence: rows.evidence, + restore: false, + parent: local.rawContactId, + groupRowIdBySourceId: (g) => ctx.groupRowIdBySourceId(g), + }); + const shadow = withMemberOf(shadowOf(server), members.memberOf); + const raw = changedRawColumns(local, cardColumns(server, shadow, ctx)); + const ops: ProviderOp[] = []; + if (Object.keys(raw).length) ops.push(updateRawContact(local.rawContactId, raw)); + ops.push(...rows.ops, ...members.ops); + return { + ops: { ref: identity, ops: ops.length ? [assertContact(local, true), ...ops] : [] }, + conflicts: rows.conflicts, + stillDirty: rows.stillDirty || members.pending, + effect: ops.length ? 'update' : 'none', + writes: ops.length, + }; +} diff --git a/src/device-sync/contacts/entries.ts b/src/device-sync/contacts/entries.ts new file mode 100644 index 00000000..cc0444c1 --- /dev/null +++ b/src/device-sync/contacts/entries.ts @@ -0,0 +1,348 @@ +/** + * Rows back to JSContact: the sub-field edits of a changed unit, and the + * entries a new row stands for. Only what the row can say is touched; a + * phone's other features, an entry's other contexts and every property + * Android has no column for stay as the server has them. + */ +import type { ContactAddress, ContactTitle } from '../../api/types'; +import { stringToPartialDate } from '../../lib/contact-utils'; +import { + CommonKind, + ContactEventType, + Data, + EmailType, + MimeType, + Organization as Org, + PhoneType, + PostalType, + RelationType, + StructuredPostal as SP, + WebsiteType, +} from '../android-columns'; +import type { ContactCardWire } from '../planner'; +import type { Row } from '../types'; +import { num, text } from './cells'; +import { parseEntryKey, parseOrgKey } from './keys'; +import { editPostalComponents, postalComponentsFromRow, POSTAL_PART_COLUMNS } from './postal'; +import { + emailFlags, + eventKind, + PHONE_TYPE_FEATURES, + phoneFlags, + postalFlags, + relationName, + relationTypesOf, + TYPED_CONTEXTS, + websiteFlags, + type EntryFlags, +} from './types-map'; + +/** A pointer (raw member names) and the value it should get; null removes. */ +export interface Edit { + path: string[]; + value: unknown; +} + +/** An entry a row adds (or, for a relation, a key it moves to). */ +export interface NewEntry { + map: string; + /** Fixed key (relatedTo is keyed by its value); otherwise a new key is minted. */ + key?: string; + /** Reference into another new entry's key, e.g. a title's organization. */ + value: Record; + /** Entries created together share a group id so titles can point at their organization. */ + role?: 'organization' | 'title'; +} + +type Entry = Record; + +const flagsOf = (value: unknown): Record => + value && typeof value === 'object' && !Array.isArray(value) ? { ...(value as Record) } : {}; + +/** Replaces the flags a TYPE speaks for; null when nothing is left. */ +function swapFlags(current: unknown, owned: string[], wanted: string[]): Record | null { + const out = flagsOf(current); + for (const k of owned) delete out[k]; + for (const k of wanted) out[k] = true; + return Object.keys(out).length ? out : null; +} + +function same(a: unknown, b: unknown): boolean { + return JSON.stringify(a ?? null) === JSON.stringify(b ?? null); +} + +function pushIf(out: Edit[], entry: Entry, path: string[], field: string, value: unknown): void { + if (!same(entry[field], value)) out.push({ path: [...path, field], value }); +} + +/** + * A new TYPE replaces the contexts and features it speaks for; a new LABEL of + * the same custom TYPE is only the label (the contexts it hides stay). + */ +function typeEdits(out: Edit[], entry: Entry, path: string[], flags: EntryFlags, changed: ReadonlySet, custom: boolean): void { + if (changed.has(CommonKind.TYPE)) { + pushIf(out, entry, path, 'contexts', swapFlags(entry.contexts, TYPED_CONTEXTS, flags.contexts)); + if (flags.features) pushIf(out, entry, path, 'features', swapFlags(entry.features, PHONE_TYPE_FEATURES, flags.features)); + pushIf(out, entry, path, 'label', flags.label); + } else if (changed.has(CommonKind.LABEL) && custom) { + pushIf(out, entry, path, 'label', flags.label); + } +} + +function prefEdit(out: Edit[], entry: Entry, path: string[], cells: Row): void { + const primary = num(cells[Data.IS_PRIMARY]) === 1; + if (primary && entry.pref !== 1) out.push({ path: [...path, 'pref'], value: 1 }); + else if (!primary && entry.pref === 1) out.push({ path: [...path, 'pref'], value: null }); +} + +const typeOf = (cells: Row) => num(cells[CommonKind.TYPE]); +const labelOf = (cells: Row) => text(cells[CommonKind.LABEL]); + +function mapEntry(card: ContactCardWire, map: string, key: string): Entry | null { + const m = card[map]; + const e = m && typeof m === 'object' ? (m as Record)[key] : undefined; + return e && typeof e === 'object' ? (e as Entry) : null; +} + +function partialDate(value: unknown): Record | null { + const t = text(value); + const pd = t ? stringToPartialDate(t) : null; + return pd ? { '@type': 'PartialDate', ...pd } : null; +} + +function splitUnits(value: string | null): Array<{ name: string }> | null { + const units = (value ?? '').split(',').map((s) => s.trim()).filter(Boolean); + return units.length ? units.map((name) => ({ name })) : null; +} + +export interface UnitEdits { + edits: Edit[]; + /** Entries to add (a company typed into a title-only row creates its organization). */ + added: NewEntry[]; + /** Entries this unit no longer has (a relation moved to another key, a title emptied). */ + removed: Array<{ map: string; key: string }>; +} + +/** + * Edits for a changed unit. `changed` are the row's changed columns, `key` + * the unit's DATA_SYNC1 key in the shadow, `baseCells` the shadow's + * projection of it (the TYPE a relation had), `byKey` whether the row was + * matched by its key. + */ +export function unitEdits( + mimetype: string, + key: string, + card: ContactCardWire, + cells: Row, + baseCells: Row, + changed: ReadonlySet, + shownName: string | null, + byKey: boolean, +): UnitEdits { + const edits: Edit[] = []; + const added: NewEntry[] = []; + const removed: Array<{ map: string; key: string }> = []; + const result = { edits, added, removed }; + if (mimetype === MimeType.ORGANIZATION) return orgEdits(key, card, cells, changed, result); + const parsed = parseEntryKey(key); + if (!parsed) return result; + const entry = mapEntry(card, parsed.map, parsed.key); + if (!entry) return result; + const path = [parsed.map, parsed.key]; + const typeChanged = changed.has(CommonKind.TYPE) || changed.has(CommonKind.LABEL); + switch (mimetype) { + case MimeType.NICKNAME: + if (changed.has(Data.DATA1)) pushIf(edits, entry, path, 'name', text(cells[Data.DATA1])); + break; + case MimeType.EMAIL: + if (changed.has(Data.DATA1)) pushIf(edits, entry, path, 'address', text(cells[Data.DATA1])); + if (typeChanged) typeEdits(edits, entry, path, emailFlags(typeOf(cells), labelOf(cells)), changed, typeOf(cells) === EmailType.CUSTOM); + if (changed.has(Data.IS_PRIMARY)) prefEdit(edits, entry, path, cells); + break; + case MimeType.PHONE: + if (changed.has(Data.DATA1)) pushIf(edits, entry, path, 'number', text(cells[Data.DATA1])); + if (typeChanged) typeEdits(edits, entry, path, phoneFlags(typeOf(cells), labelOf(cells)), changed, typeOf(cells) === PhoneType.CUSTOM); + if (changed.has(Data.IS_PRIMARY)) prefEdit(edits, entry, path, cells); + break; + case MimeType.STRUCTURED_POSTAL: { + const parts = new Set(POSTAL_PART_COLUMNS.filter((c) => changed.has(c))); + if (parts.size) { + const comps = editPostalComponents(entry as ContactAddress, cells, parts); + pushIf(edits, entry, path, 'components', comps.length ? comps : null); + } + if (changed.has(SP.FORMATTED_ADDRESS)) pushIf(edits, entry, path, 'full', text(cells[SP.FORMATTED_ADDRESS])); + if (typeChanged) typeEdits(edits, entry, path, postalFlags(typeOf(cells), labelOf(cells)), changed, typeOf(cells) === PostalType.CUSTOM); + break; + } + case MimeType.WEBSITE: + if (changed.has(Data.DATA1)) pushIf(edits, entry, path, 'uri', text(cells[Data.DATA1])); + if (typeChanged) typeEdits(edits, entry, path, websiteFlags(typeOf(cells), labelOf(cells)), changed, typeOf(cells) === WebsiteType.CUSTOM); + break; + case MimeType.EVENT: + if (changed.has(Data.DATA1)) { + const date = partialDate(cells[Data.DATA1]); + // Free text Stalwart can't take stays on the device only. + if (date) pushIf(edits, entry, path, 'date', date); + } + if (changed.has(CommonKind.TYPE) || (changed.has(CommonKind.LABEL) && typeOf(cells) === ContactEventType.CUSTOM)) { + pushIf(edits, entry, path, 'kind', eventKind(typeOf(cells), labelOf(cells))); + } + break; + case MimeType.RELATION: { + const name = text(cells[Data.DATA1]); + // Only a row we wrote can be renamed: a re-inserted row showing another name may just + // show a related card that was renamed since. + const renamed = byKey && changed.has(Data.DATA1) && name !== null && name !== shownName && name !== parsed.key; + const relationChanged = changed.has(CommonKind.TYPE) || (changed.has(CommonKind.LABEL) && typeOf(cells) === RelationType.CUSTOM); + const relation = relationChanged + ? swapFlags(entry.relation, relationTypesOf(typeOf(baseCells), labelOf(baseCells)), relationTypes(cells)) ?? {} + : flagsOf(entry.relation); + if (renamed) { + // relatedTo is keyed by its value: another name is another entry. + removed.push({ map: 'relatedTo', key: parsed.key }); + added.push({ map: 'relatedTo', key: name!, value: { ...entry, relation } }); + } else if (relationChanged) { + pushIf(edits, entry, path, 'relation', relation); + } + break; + } + case MimeType.NOTE: + if (changed.has(Data.DATA1)) pushIf(edits, entry, path, 'note', text(cells[Data.DATA1])); + break; + } + return result; +} + +function relationTypes(cells: Row): string[] { + const name = relationName(typeOf(cells), labelOf(cells)); + return name ? [name] : []; +} + +function orgEdits(key: string, card: ContactCardWire, cells: Row, changed: ReadonlySet, out: UnitEdits): UnitEdits { + const parts = parseOrgKey(key); + if (!parts) return out; + const org = parts.org ? mapEntry(card, 'organizations', parts.org) : null; + const orgPath = parts.org ? ['organizations', parts.org] : null; + const company = text(cells[Org.COMPANY]); + const department = text(cells[Org.DEPARTMENT]); + let newOrg: NewEntry | null = null; + if (org && orgPath) { + if (changed.has(Org.COMPANY)) pushIf(out.edits, org, orgPath, 'name', company); + if (changed.has(Org.DEPARTMENT)) pushIf(out.edits, org, orgPath, 'units', splitUnits(department)); + } else if ((changed.has(Org.COMPANY) && company) || (changed.has(Org.DEPARTMENT) && department)) { + const value: Record = {}; + if (company) value.name = company; + const units = splitUnits(department); + if (units) value.units = units; + newOrg = { map: 'organizations', value, role: 'organization' }; + out.added.push(newOrg); + } + const titleEdit = (column: string, titleKey: string | null, kind: 'title' | 'role') => { + const title = titleKey ? mapEntry(card, 'titles', titleKey) : null; + const value = text(cells[column]); + if (title && titleKey) { + if (changed.has(column)) { + if (value === null) out.removed.push({ map: 'titles', key: titleKey }); + else pushIf(out.edits, title, ['titles', titleKey], 'name', value); + } + if (newOrg) out.edits.push({ path: ['titles', titleKey, 'organizationId'], value: newOrg }); + } else if (changed.has(column) && value !== null) { + const t: Record = { name: value, kind }; + if (parts.org) t.organizationId = parts.org; + out.added.push({ map: 'titles', value: t, role: 'title' }); + } + }; + titleEdit(Org.TITLE, parts.title, 'title'); + titleEdit(Org.JOB_DESCRIPTION, parts.role, 'role'); + // A new organization's titles point at its minted key (resolved by the patch builder). + if (newOrg) for (const e of out.added) if (e.role === 'title') e.value.organizationId = newOrg; + return out; +} + +/** The entries a new row adds. */ +export function newEntries(mimetype: string, cells: Row, shownName?: string | null): NewEntry[] { + const typeFlags = (flags: EntryFlags, withFeatures = false): Record => { + const out: Record = {}; + const contexts = swapFlags(undefined, [], flags.contexts); + if (contexts) out.contexts = contexts; + if (withFeatures) { + const features = swapFlags(undefined, [], flags.features ?? []); + if (features) out.features = features; + } + if (flags.label) out.label = flags.label; + return out; + }; + const primary = num(cells[Data.IS_PRIMARY]) === 1 ? { pref: 1 } : {}; + const value = text(cells[Data.DATA1]); + switch (mimetype) { + case MimeType.NICKNAME: + return value ? [{ map: 'nicknames', value: { name: value } }] : []; + case MimeType.EMAIL: + return value ? [{ map: 'emails', value: { address: value, ...typeFlags(emailFlags(typeOf(cells), labelOf(cells))), ...primary } }] : []; + case MimeType.PHONE: + return value ? [{ map: 'phones', value: { number: value, ...typeFlags(phoneFlags(typeOf(cells), labelOf(cells)), true), ...primary } }] : []; + case MimeType.STRUCTURED_POSTAL: { + const address: Record = {}; + const comps = postalComponentsFromRow(cells); + if (comps.length) address.components = comps; + const full = text(cells[SP.FORMATTED_ADDRESS]); + if (full) address.full = full; + if (!comps.length && !full) return []; + return [{ map: 'addresses', value: { ...address, ...typeFlags(postalFlags(typeOf(cells), labelOf(cells))) } }]; + } + case MimeType.ORGANIZATION: { + const out: NewEntry[] = []; + const company = text(cells[Org.COMPANY]); + const units = splitUnits(text(cells[Org.DEPARTMENT])); + let org: NewEntry | null = null; + if (company || units) { + const v: Record = {}; + if (company) v.name = company; + if (units) v.units = units; + org = { map: 'organizations', value: v, role: 'organization' }; + out.push(org); + } + const title = (column: string, kind: ContactTitle['kind']) => { + const name = text(cells[column]); + if (!name) return; + const v: Record = { name, kind }; + if (org) v.organizationId = org; + out.push({ map: 'titles', value: v, role: 'title' }); + }; + title(Org.TITLE, 'title'); + title(Org.JOB_DESCRIPTION, 'role'); + return out; + } + case MimeType.WEBSITE: + return value ? [{ map: 'links', value: { uri: value, ...typeFlags(websiteFlags(typeOf(cells), labelOf(cells))) } }] : []; + case MimeType.EVENT: { + const date = partialDate(cells[Data.DATA1]); + return date ? [{ map: 'anniversaries', value: { kind: eventKind(typeOf(cells), labelOf(cells)), date } }] : []; + } + case MimeType.RELATION: { + if (!value || value === shownName) return []; + const type = relationName(typeOf(cells), labelOf(cells)); + return [{ map: 'relatedTo', key: value, value: { relation: type ? { [type]: true } : {} } }]; + } + case MimeType.NOTE: + return value ? [{ map: 'notes', value: { note: value } }] : []; + default: + return []; + } +} + +/** Keys (map, key) a unit stands for, for deleting it. */ +export function unitEntries(mimetype: string, key: string): Array<{ map: string; key: string }> { + if (mimetype === MimeType.ORGANIZATION) { + const parts = parseOrgKey(key); + if (!parts) return []; + const out: Array<{ map: string; key: string }> = []; + if (parts.org) out.push({ map: 'organizations', key: parts.org }); + if (parts.title) out.push({ map: 'titles', key: parts.title }); + if (parts.role) out.push({ map: 'titles', key: parts.role }); + return out; + } + const parsed = parseEntryKey(key); + return parsed ? [parsed] : []; +} + diff --git a/src/device-sync/contacts/groups.ts b/src/device-sync/contacts/groups.ts new file mode 100644 index 00000000..6e9d8dae --- /dev/null +++ b/src/device-sync/contacts/groups.ts @@ -0,0 +1,252 @@ +/** + * Group cards (`kind: "group"`) ↔ Groups rows, and membership edits made on + * the device ↔ the group cards' `members` (docs/device-sync.md, "Contacts + * mapping", "Groups"). Group rows are written before the contacts' plans run, + * so memberships can name them. `members` takes `members/: null` as a + * clean delete; the first member of a group without any is sent as the whole + * map. + */ +import type { ContactName } from '../../api/types'; +import { deriveFullName } from '../../lib/contact-utils'; +import { Groups } from '../android-columns'; +import { collectionKey, objectRef, parseCollectionKey, parseObjectRef } from '../common/ids'; +import { canonicalJson, jsonHash } from '../common/json'; +import { ptr, type PatchObject } from '../common/patch'; +import type { + AcceptedPlan, + ContactCardWire, + ContactsContext, + DownloadPlan, + LocalContact, + LocalGroup, + OpGroup, + UploadAction, + UploadPlan, +} from '../planner'; +import type { ProviderOp, WriteRow } from '../types'; +import { sameCell } from './cells'; +import { cardUnits } from './download'; +import { inPlaceEvidence, matchKind } from './matching'; +import { planMemberships, shadowMemberOf } from './members'; +import { deleteWhereId } from './ops'; +import { shadowOf } from './photo'; +import { KIND_ORDER } from './project'; + +type Ctx = ContactsContext; + +/** The title a group card shows. */ +function groupTitle(card: ContactCardWire | null): string | null { + const name = card?.name as ContactName | undefined; + if (typeof name?.full === 'string' && name.full.trim()) return name.full; + return deriveFullName(name?.components) || null; +} + +const ref = (local: LocalGroup) => local.sourceId ?? `group:${local.groupId}`; + +function assertGroup(local: LocalGroup, dirty: boolean | null): ProviderOp { + const values: Record = { [Groups.VERSION]: local.version }; + if (dirty !== null) values[Groups.DIRTY] = dirty ? 1 : 0; + return { op: 'assert', table: 'groups', id: local.groupId, values, expectCount: 1 }; +} + +const updateGroup = (local: LocalGroup, values: WriteRow): ProviderOp => ({ + op: 'update', table: 'groups', id: local.groupId, values, expectCount: 1, +}); + +function cardColumns(card: ContactCardWire, ctx: Ctx): WriteRow { + return { + [Groups.SYNC2]: JSON.stringify(shadowOf(card)), + [Groups.GROUP_IS_READ_ONLY]: ctx.isReadOnly(card) ? 1 : 0, + }; +} + +/** + * Only the values that differ from what the local group holds. The read-only + * flag is not decoded (LocalGroup has no field for it), so it only goes along + * with other writes, which keeps an echo free of writes. + */ +function changed(local: LocalGroup, values: WriteRow): WriteRow { + const out: WriteRow = {}; + for (const [column, value] of Object.entries(values)) { + if (column === Groups.SYNC2) { + const before = local.shadow ? canonicalJson(local.shadow) : null; + if (typeof value !== 'string' || before !== canonicalJson(JSON.parse(value))) out[column] = value; + } else if (column === Groups.TITLE) { + if (!sameCell(local.title, value as unknown)) out[column] = value; + } else if (column !== Groups.GROUP_IS_READ_ONLY) { + out[column] = value; + } + } + if (Object.keys(out).length && Groups.GROUP_IS_READ_ONLY in values) out[Groups.GROUP_IS_READ_ONLY] = values[Groups.GROUP_IS_READ_ONLY]; + return out; +} + +export function planGroupDownload(card: ContactCardWire, local: LocalGroup | null, ctx: Ctx): DownloadPlan { + const identity = objectRef(ctx.jmapAccountId, card.id); + const remoteTitle = groupTitle(card); + const plan = (ops: ProviderOp[], effect: DownloadPlan['effect'], conflicts = 0, stillDirty = false): DownloadPlan => ({ + ops: { ref: identity, ops }, conflicts, stillDirty, effect, writes: ops.filter((o) => o.op !== 'assert').length, + }); + if (!local) { + return plan([{ + op: 'insert', + table: 'groups', + values: { [Groups.SOURCE_ID]: identity, [Groups.TITLE]: remoteTitle ?? '', [Groups.GROUP_VISIBLE]: 1, [Groups.DIRTY]: 0, ...cardColumns(card, ctx) }, + }], 'insert'); + } + if (local.deleted) return plan([], 'none', 0, true); + if (!local.sourceId) { + // Our own create whose identity was never written. + return plan([ + assertGroup(local, local.dirty), + updateGroup(local, { [Groups.SOURCE_ID]: identity, [Groups.SYNC3]: null, ...cardColumns(card, ctx) }), + ], 'update', 0, true); + } + const base = groupTitle(local.shadow); + const localChanged = local.dirty && !sameCell(local.title, base); + const remoteChanged = !sameCell(remoteTitle, base); + let conflicts = 0; + const values: WriteRow = { ...cardColumns(card, ctx) }; + if (remoteChanged && (!localChanged || !sameCell(local.title, remoteTitle))) { + if (localChanged) conflicts++; + values[Groups.TITLE] = remoteTitle ?? ''; + } + const diff = changed(local, values); + if (!Object.keys(diff).length) return plan([], 'none', 0, localChanged && !remoteChanged); + return plan([assertGroup(local, local.dirty), updateGroup(local, diff)], 'update', conflicts, localChanged && !remoteChanged); +} + +export function planGroupLocalDelete(local: LocalGroup): OpGroup { + return { ref: ref(local), ops: [deleteWhereId('groups', local.groupId)] }; +} + +/** A poison marker's `fp` for a group, as the engine takes it (engine/poison.ts). */ +export function groupFingerprint(local: LocalGroup): string { + return jsonHash({ deleted: local.deleted, title: local.title, shadow: local.shadow }); +} + +export function planGroupUpload(local: LocalGroup, ctx: Ctx): UploadPlan { + const group: OpGroup = { ref: ref(local), ops: [] }; + if (!local.deleted && !local.dirty && local.sourceId) return { kind: 'clean', ops: group }; + const p = local.poison; + if (p && p.until > ctx.now && p.fp === groupFingerprint(local)) return { kind: 'skip', reason: `poisoned:${p.type}` }; + const parsed = parseObjectRef(local.sourceId); + const readOnly = !!local.shadow && ctx.isReadOnly(local.shadow); + + if (local.deleted) { + if (parsed) { + if (readOnly) return { kind: 'revert', ops: { ...group, ops: [deleteWhereId('groups', local.groupId)] }, refetch: true }; + const books = Object.entries(local.shadow?.addressBookIds ?? {}).filter(([, on]) => on).map(([id]) => id); + const selected = books.filter((id) => ctx.isSelected(collectionKey(parsed.accountId, id))); + if (selected.length && selected.length < books.length) { + const patch: PatchObject = {}; + for (const id of selected) patch[ptr('addressBookIds', id)] = null; + return { kind: 'upload', actions: [{ kind: 'update', id: parsed.id, patch }] }; + } + return { kind: 'upload', actions: [{ kind: 'destroy', id: parsed.id, uid: typeof local.shadow?.uid === 'string' ? local.shadow.uid : null }] }; + } + if (local.pending) return { kind: 'upload', actions: [{ kind: 'destroy', id: null, uid: local.pending.uid }] }; + return { kind: 'purge', ops: { ...group, ops: [deleteWhereId('groups', local.groupId)] } }; + } + if (local.sourceId && !parsed) return { kind: 'skip', reason: 'invalidSourceId' }; + + const title = local.title?.trim() ? local.title : null; + if (!local.sourceId) { + if (!local.pending) { + const target = ctx.createTarget(); + if (!target) return { kind: 'skip', reason: 'noWritableAddressBook' }; + const pending = { uid: ctx.mintUid(), target }; + return { kind: 'claim', ops: { ...group, ops: [assertGroup(local, null), updateGroup(local, { [Groups.SYNC3]: JSON.stringify(pending) })] } }; + } + const target = parseCollectionKey(local.pending.target); + if (!target) return { kind: 'skip', reason: 'invalidPendingTarget' }; + const object: Partial = { + '@type': 'Card', version: '1.0', uid: local.pending.uid, kind: 'group', addressBookIds: { [target.id]: true }, + ...(title ? { name: { full: title } } : {}), + }; + return { kind: 'upload', actions: [{ kind: 'create', uid: local.pending.uid, collectionId: target.id, object }] }; + } + + const shadowTitle = groupTitle(local.shadow); + if (readOnly) { + return { kind: 'revert', ops: { ...group, ops: [assertGroup(local, null), updateGroup(local, { [Groups.TITLE]: shadowTitle ?? '', [Groups.DIRTY]: 0 })] } }; + } + if (sameCell(title, shadowTitle) || title === null) { + return { kind: 'clean', ops: { ...group, ops: [assertGroup(local, true), updateGroup(local, { [Groups.DIRTY]: 0 })] } }; + } + const patch: PatchObject = local.shadow?.name && typeof local.shadow.name === 'object' ? { 'name/full': title } : { name: { full: title } }; + return { kind: 'upload', actions: [{ kind: 'update', id: parsed!.id, patch }] }; +} + +export function planGroupAccepted(local: LocalGroup, server: ContactCardWire, ctx: Ctx): AcceptedPlan { + if (local.deleted) { + const purge = planGroupLocalDelete(local); + return { ops: purge, keepDirtyOps: purge }; + } + const account = parseObjectRef(local.sourceId)?.accountId ?? parseCollectionKey(local.pending?.target)?.accountId ?? ctx.jmapAccountId; + const identity = objectRef(account, server.id); + const raw: WriteRow = { [Groups.SOURCE_ID]: identity, [Groups.SYNC3]: null, [Groups.SYNC4]: null, ...cardColumns(server, ctx) }; + const title = groupTitle(server); + const ops: WriteRow = { ...raw, [Groups.DIRTY]: 0 }; + if (!sameCell(local.title, title)) ops[Groups.TITLE] = title ?? ''; + return { + ops: { ref: identity, ops: [assertGroup(local, null), updateGroup(local, ops)] }, + keepDirtyOps: { ref: identity, ops: [updateGroup(local, raw)] }, + }; +} + +/** + * Membership edits of the given contacts as patches of the group cards' + * `members`. A contact's edits are its rows against the base kept in its + * shadow; what the group shadows already show is not sent again. + */ +export function planMembershipUploads(contacts: LocalContact[], groups: LocalGroup[], ctx: Ctx): UploadAction[] { + const bySource = new Map(groups.filter((g) => g.sourceId).map((g) => [g.sourceId!, g])); + const members = (g: LocalGroup) => { + const m = g.shadow?.members; + return m && typeof m === 'object' ? (m as Record) : {}; + }; + const edits = new Map>(); + for (const contact of contacts) { + if (contact.deleted) continue; + const uid = typeof contact.shadow?.uid === 'string' ? contact.shadow.uid : contact.pending?.uid; + const account = parseObjectRef(contact.sourceId)?.accountId; + if (!uid || !account) continue; + const remote = groups + .filter((g) => g.sourceId && !g.deleted && members(g)[uid] === true) + .map((g) => g.sourceId!); + const base = contact.shadow ? shadowMemberOf(contact.shadow) : []; + if (base === null) continue; + const units = cardUnits(contact.shadow, ctx); + const evidence = inPlaceEvidence(KIND_ORDER.map((k) => matchKind(k, contact.rows, units))); + const plan = planMemberships({ + rows: contact.rows, base, remote, evidence, restore: false, parent: contact.rawContactId, + groupRowIdBySourceId: () => null, + }); + const put = (gsid: string, on: boolean) => { + const g = bySource.get(gsid); + const gref = parseObjectRef(gsid); + if (!g || g.deleted || !g.shadow || !gref || gref.accountId !== account) return; + if (ctx.isReadOnly(g.shadow)) return; + if (!edits.has(g)) edits.set(g, new Map()); + edits.get(g)!.set(uid, on); + }; + for (const g of plan.added) put(g, true); + for (const g of plan.removed) put(g, false); + } + const actions: UploadAction[] = []; + for (const [g, changes] of edits) { + const current = members(g); + const patch: PatchObject = {}; + if (!Object.keys(current).length) { + const whole: Record = {}; + for (const [uid, on] of changes) if (on) whole[uid] = true; + if (!Object.keys(whole).length) continue; + patch.members = whole; + } else { + for (const [uid, on] of changes) patch[ptr('members', uid)] = on ? true : null; + } + actions.push({ kind: 'update', id: parseObjectRef(g.sourceId)!.id, patch }); + } + return actions; +} diff --git a/src/device-sync/contacts/keys.ts b/src/device-sync/contacts/keys.ts new file mode 100644 index 00000000..547af4b2 --- /dev/null +++ b/src/device-sync/contacts/keys.ts @@ -0,0 +1,65 @@ +/** + * DATA_SYNC1 of a contacts data row: which card entry (or entries) the row + * was written from. Keys are hints, not identity (docs/device-sync.md, "Row + * matching"): editors that re-insert rows drop them. + * + * name the StructuredName row + * emails:work1 one entry of a map + * organizations:o1|titles:t1,r1 an organization with its title and role key + * titles:,r1 a role without an organization + * relatedTo:urn:uuid:… relatedTo is keyed by its value + * members:/ a group membership + * + * Map keys may hold any character; `%`, `|` and `,` are escaped so the + * organization form stays parseable. + */ + +export const NAME_KEY = 'name'; + +const escapeKey = (key: string) => key.replace(/%/g, '%25').replace(/\|/g, '%7C').replace(/,/g, '%2C'); + +function unescapeKey(text: string): string | null { + try { + return decodeURIComponent(text); + } catch { + return null; + } +} + +export function entryKey(map: string, key: string): string { + return `${map}:${escapeKey(key)}`; +} + +/** `{ map, key }` of a simple entry key; null for the name, organizations and anything unreadable. */ +export function parseEntryKey(text: string | null): { map: string; key: string } | null { + if (!text) return null; + const colon = text.indexOf(':'); + if (colon <= 0) return null; + const map = text.slice(0, colon); + if (map === 'organizations' || map === 'titles') return null; + const key = unescapeKey(text.slice(colon + 1)); + return key ? { map, key } : null; +} + +export interface OrgKeyParts { + org: string | null; + title: string | null; + role: string | null; +} + +export function orgKey({ org, title, role }: OrgKeyParts): string { + const titles = title || role ? `titles:${title ? escapeKey(title) : ''},${role ? escapeKey(role) : ''}` : ''; + if (org === null) return titles; + return titles ? `organizations:${escapeKey(org)}|${titles}` : `organizations:${escapeKey(org)}`; +} + +export function parseOrgKey(text: string | null): OrgKeyParts | null { + if (!text) return null; + const m = /^(?:organizations:([^|]*))?(?:\|?titles:([^,]*),([^,]*))?$/.exec(text); + if (!m || (m[1] === undefined && m[2] === undefined)) return null; + const part = (s: string | undefined) => (s ? unescapeKey(s) : null); + const org = part(m[1]); + if (m[1] !== undefined && !org) return null; + const parts = { org, title: part(m[2]), role: part(m[3]) }; + return parts.org || parts.title || parts.role ? parts : null; +} diff --git a/src/device-sync/contacts/matching.ts b/src/device-sync/contacts/matching.ts new file mode 100644 index 00000000..b1e321e0 --- /dev/null +++ b/src/device-sync/contacts/matching.ts @@ -0,0 +1,131 @@ +/** + * Row matching and key reconciliation (docs/device-sync.md, "Row matching" + * and "Key reconciliation"). Keys are hints: Fossify re-inserts every row but + * the name on each save, without DATA_SYNC columns, dropping what it does not + * model; AOSP edits in place. Stalwart regenerates `k1…kN` keys by position + * for cards written over CardDAV, so keys can shift under us. + */ +import type { LocalDataRow } from '../planner'; +import { isEmptyRow, SPECS, type Unit } from './project'; + +export interface Match { + row: LocalDataRow; + unit: Unit; + how: 'key' | 'value' | 'pair'; +} + +export interface KindMatch { + mimetype: string; + matched: Match[]; + /** Rows no unit accounts for: new entries. */ + fresh: LocalDataRow[]; + /** Units without a row. */ + missing: Unit[]; + /** Non-empty rows of the kind. */ + rows: LocalDataRow[]; +} + +/** + * Matches one kind's non-empty rows to units: by key, then by the kind's + * primary value, then (only when the counts agree) pairwise in order. + */ +export function matchKind(mimetype: string, rows: LocalDataRow[], units: Unit[]): KindMatch { + const spec = SPECS[mimetype]; + const live = rows.filter((r) => r.mimetype === mimetype && !isEmptyRow(mimetype, r.cells)); + const kindUnits = units.filter((u) => u.mimetype === mimetype); + const matched: Match[] = []; + const takenUnits = new Set(); + const takenRows = new Set(); + const byKey = new Map(kindUnits.map((u) => [u.key, u])); + + for (const row of live) { + const u = row.key ? byKey.get(row.key) : undefined; + if (u && !takenUnits.has(u)) { + matched.push({ row, unit: u, how: 'key' }); + takenUnits.add(u); + takenRows.add(row); + } + } + for (const row of live) { + if (takenRows.has(row)) continue; + const p = spec.primary(row.cells); + if (p === null) continue; + const u = kindUnits.find((x) => !takenUnits.has(x) && spec.primary(x.cells) === p); + if (u) { + matched.push({ row, unit: u, how: 'value' }); + takenUnits.add(u); + takenRows.add(row); + } + } + const restRows = live.filter((r) => !takenRows.has(r)); + const restUnits = kindUnits.filter((u) => !takenUnits.has(u)); + if (restRows.length && restRows.length === restUnits.length) { + restRows.forEach((row, i) => matched.push({ row, unit: restUnits[i], how: 'pair' })); + return { mimetype, matched, fresh: [], missing: [], rows: live }; + } + return { mimetype, matched, fresh: restRows, missing: restUnits, rows: live }; +} + +/** + * Whether the units of a kind that lost their rows were deleted on the + * device. Only when the kind still has rows matched by key (an in-place + * editor removed one); when every row is keyless an editor rewrote them all + * and a missing entry may just be one it could not show. A kind left without + * any row is judged by the rest of the contact: keyed rows of kinds that + * re-inserting editors rewrite mean an in-place editor did it. + */ +export function deletionsInferred(kind: KindMatch, inPlaceEvidence: boolean): boolean { + const spec = SPECS[kind.mimetype]; + if (spec.inPlaceOnly) return true; + if (kind.matched.some((m) => m.how === 'key')) return true; + return kind.rows.length === 0 && inPlaceEvidence; +} + +/** Keyed rows of rewritten kinds: evidence that the last editor worked in place. */ +export function inPlaceEvidence(kinds: KindMatch[]): boolean { + return kinds.some((k) => SPECS[k.mimetype].rewritten && k.matched.some((m) => m.how === 'key')); +} + +/** + * Maps the keys of the shadow's units to the new server version's keys by + * content: equal entries first, then equal primary values, then an + * unchanged key (an entry edited in place). Unmapped units were removed on + * the server; unmapped new units were added. + */ +export function reconcileKeys(base: Unit[], remote: Unit[]): Map { + const out = new Map(); + const kinds = new Set([...base, ...remote].map((u) => u.mimetype)); + for (const mimetype of kinds) { + const spec = SPECS[mimetype]; + const b = base.filter((u) => u.mimetype === mimetype); + const r = remote.filter((u) => u.mimetype === mimetype); + const takenB = new Set(); + const takenR = new Set(); + const link = (x: Unit, y: Unit) => { + out.set(x.key, y.key); + takenB.add(x); + takenR.add(y); + }; + for (const x of b) { + const y = r.find((u) => !takenR.has(u) && u.key === x.key && u.source === x.source); + if (y) link(x, y); + } + for (const x of b) { + if (takenB.has(x)) continue; + const y = r.find((u) => !takenR.has(u) && u.source === x.source); + if (y) link(x, y); + } + for (const x of b) { + if (takenB.has(x)) continue; + const p = spec.primary(x.cells); + const y = p === null ? undefined : r.find((u) => !takenR.has(u) && spec.primary(u.cells) === p); + if (y) link(x, y); + } + for (const x of b) { + if (takenB.has(x)) continue; + const y = r.find((u) => !takenR.has(u) && u.key === x.key); + if (y) link(x, y); + } + } + return out; +} diff --git a/src/device-sync/contacts/members.ts b/src/device-sync/contacts/members.ts new file mode 100644 index 00000000..0a07983b --- /dev/null +++ b/src/device-sync/contacts/members.ts @@ -0,0 +1,149 @@ +/** + * Group memberships of a contact: one GroupMembership row per group card + * whose `members` holds the card's uid (docs/device-sync.md, "Contacts + * mapping"). The server keeps them on the group cards, so the rows are + * compared with three sets: + * + * - base: the memberships the rows held after our last write, kept in the + * contact's shadow under `~memberOf` (the card itself has no such field); + * - local: the rows now; + * - remote: the group cards that list the uid now (`groupsOf`, from the + * engine's group shadows). + * + * Per group, a local change wins where the server did not change, the server + * wins elsewhere; both sides can only flip a membership the same way, so + * memberships never conflict. A local change stays in the base until the + * server has it, which keeps it visible to `planMembershipUploads`. + */ +import { Data, GroupMembership, MimeType } from '../android-columns'; +import type { ContactCardWire, ContactsContext, LocalDataRow } from '../planner'; +import type { ProviderOp } from '../types'; +import { text } from './cells'; +import { entryKey } from './keys'; +import { deleteData, insertUnit, updateData } from './ops'; +import { MEMBER_OF, type Unit } from './project'; + +/** Remote memberships of a card, or null when the engine can't tell (memberships are then left alone). */ +export function remoteMemberships(card: ContactCardWire, ctx: ContactsContext): string[] | null { + if (typeof ctx.groupsOf !== 'function' || typeof card.uid !== 'string' || !card.uid) return null; + return [...new Set(ctx.groupsOf(card.uid).filter((g) => typeof g === 'string'))].sort(); +} + +export function shadowMemberOf(shadow: ContactCardWire | null): string[] | null { + const v = shadow?.[MEMBER_OF]; + return Array.isArray(v) && v.every((g) => typeof g === 'string') ? [...v] : null; +} + +/** A card without the `~memberOf` a shadow carries. */ +export function withoutMemberOf(card: ContactCardWire): ContactCardWire { + if (!(MEMBER_OF in card)) return card; + const { [MEMBER_OF]: _drop, ...rest } = card; + return rest as ContactCardWire; +} + +export function withMemberOf(shadow: ContactCardWire, memberOf: string[] | null): ContactCardWire { + const out = withoutMemberOf(shadow); + return memberOf === null ? out : { ...out, [MEMBER_OF]: [...memberOf].sort() }; +} + +function membershipUnit(gsid: string, groupRowId: number): Unit { + return { + mimetype: MimeType.GROUP_MEMBERSHIP, + key: entryKey('members', gsid), + cells: { [GroupMembership.GROUP_SOURCE_ID]: gsid }, + extra: { [GroupMembership.GROUP_ROW_ID]: groupRowId }, + source: JSON.stringify(gsid), + }; +} + +/** The group SOURCE_ID a membership row points at; null for a group not yet on the server. */ +export function rowGroup(row: LocalDataRow): string | null { + return text(row.cells[GroupMembership.GROUP_SOURCE_ID]); +} + +export interface MemberPlan { + ops: ProviderOp[]; + writes: number; + /** The new base (`~memberOf`); null when memberships are not synced. */ + memberOf: string[] | null; + /** Local membership changes the server does not have yet. */ + pending: boolean; + /** Groups the device added the contact to, or removed it from, that the server does not show yet. */ + added: string[]; + removed: string[]; +} + +export interface MemberInput { + rows: LocalDataRow[]; + base: string[] | null; + remote: string[] | null; + /** Deleted rows count as removals (keyed rows of the kind, or in-place evidence). */ + evidence: boolean; + /** Clean writes put back memberships an editor did not show; merges leave them. */ + restore: boolean; + parent: number | { ref: number }; + groupRowIdBySourceId(sourceId: string): number | null; +} + +export function planMemberships(input: MemberInput): MemberPlan { + const live = input.rows.filter((r) => r.mimetype === MimeType.GROUP_MEMBERSHIP); + if (input.remote === null) { + // Without the engine's group index memberships are not synced: nothing is written or kept pending. + return { ops: [], writes: 0, memberOf: input.base, pending: false, added: [], removed: [] }; + } + const ops: ProviderOp[] = []; + const rowOf = new Map(); + let unsynced = false; + for (const r of live) { + const g = rowGroup(r); + if (g === null) unsynced = true; + else if (!rowOf.has(g)) rowOf.set(g, r); + } + const keyed = live.some((r) => r.key !== null && r.key === entryKey('members', rowGroup(r) ?? '')); + const deletions = keyed || (live.length === 0 && input.evidence); + const base = new Set(input.base ?? rowOf.keys()); + const remote = new Set(input.remote); + const after = new Set(); + const pendingAdded = new Set(); + const pendingRemoved = new Set(); + + const keep = (g: string, row: LocalDataRow) => { + after.add(g); + const key = entryKey('members', g); + if (row.key !== key) ops.push(updateData(row.id, { [Data.DATA_SYNC1]: key })); + }; + const insert = (g: string) => { + const groupRowId = input.groupRowIdBySourceId(g); + if (groupRowId === null) return; + ops.push(insertUnit(input.parent, membershipUnit(g, groupRowId), true)); + after.add(g); + }; + + for (const g of [...new Set([...base, ...remote, ...rowOf.keys()])].sort()) { + const inBase = base.has(g); + const inRemote = remote.has(g); + const row = rowOf.get(g); + const change = inBase ? (!row && deletions ? 'removed' : null) : row ? 'added' : null; + if (change === 'added') { + keep(g, row!); + if (!inRemote) pendingAdded.add(g); + } else if (change === 'removed') { + if (inRemote) pendingRemoved.add(g); + } else if (row) { + if (inRemote) keep(g, row); + else ops.push(deleteData(row.id)); + } else if (inRemote && (!inBase || input.restore)) { + insert(g); + } + } + const memberOf = [...after].filter((g) => !pendingAdded.has(g)); + for (const g of pendingRemoved) memberOf.push(g); + return { + ops, + writes: ops.length, + memberOf: memberOf.sort(), + pending: unsynced || pendingAdded.size > 0 || pendingRemoved.size > 0, + added: [...pendingAdded], + removed: [...pendingRemoved], + }; +} diff --git a/src/device-sync/contacts/name.ts b/src/device-sync/contacts/name.ts new file mode 100644 index 00000000..7704e7b8 --- /dev/null +++ b/src/device-sync/contacts/name.ts @@ -0,0 +1,213 @@ +/** + * `name` ↔ the StructuredName row (docs/device-sync.md, "Name"). + * + * Download writes DISPLAY_NAME and the components together, so the provider + * leaves both alone; a name with `full` only is split by the provider, which + * the baseline predicts roughly and the baseline heal corrects. Upload + * replaces the first component of each edited column's kinds, keeps every + * other component, and sends the whole `name/components` array (JSON Pointer + * can't address array elements). `name/full` goes along when the display name + * was typed, or re-derived when only components changed and the server's + * `full` was the derived one (Stalwart never re-derives it by itself). + */ +import type { ContactName, NameComponent } from '../../api/types'; +import { deriveFullName } from '../../lib/contact-utils'; +import { StructuredName as SN } from '../android-columns'; +import { clone } from '../common/json'; +import type { Row } from '../types'; +import { text } from './cells'; + +type Component = NameComponent & { phonetic?: string }; + +export const NAME_COLUMNS = [ + SN.DISPLAY_NAME, SN.GIVEN_NAME, SN.FAMILY_NAME, SN.PREFIX, SN.MIDDLE_NAME, SN.SUFFIX, + SN.PHONETIC_GIVEN_NAME, SN.PHONETIC_MIDDLE_NAME, SN.PHONETIC_FAMILY_NAME, +] as const; + +/** The component columns the provider splits a lone display name into, or joins into one. */ +const NAME_PART_COLUMNS = [SN.PREFIX, SN.GIVEN_NAME, SN.MIDDLE_NAME, SN.FAMILY_NAME, SN.SUFFIX] as const; + +interface ColumnSpec { + column: string; + kinds: string[]; + /** Rank in display order, for placing a component the name did not have. */ + rank: number; +} + +const PARTS: ColumnSpec[] = [ + { column: SN.PREFIX, kinds: ['title', 'prefix'], rank: 0 }, + { column: SN.GIVEN_NAME, kinds: ['given'], rank: 1 }, + { column: SN.MIDDLE_NAME, kinds: ['given2', 'middle', 'additional'], rank: 2 }, + { column: SN.FAMILY_NAME, kinds: ['surname'], rank: 3 }, + { column: SN.SUFFIX, kinds: ['generation', 'credential', 'suffix'], rank: 5 }, +]; + +const PHONETICS: Array<{ column: string; part: string }> = [ + { column: SN.PHONETIC_GIVEN_NAME, part: SN.GIVEN_NAME }, + { column: SN.PHONETIC_MIDDLE_NAME, part: SN.MIDDLE_NAME }, + { column: SN.PHONETIC_FAMILY_NAME, part: SN.FAMILY_NAME }, +]; + +const RANK: Record = { surname2: 4 }; +for (const p of PARTS) for (const k of p.kinds) RANK[k] = p.rank; + +function components(name: ContactName | undefined): Component[] { + return Array.isArray(name?.components) + ? (name!.components as Component[]).filter((c) => c && typeof c.kind === 'string') + : []; +} + +const firstOf = (comps: Component[], kinds: string[]) => + comps.find((c) => kinds.includes(c.kind) && typeof c.value === 'string'); + +/** StructuredName cells of a card's name; null when there is no name to show. */ +export function nameCells(name: ContactName | undefined): Row | null { + if (!name || typeof name !== 'object') return null; + const comps = components(name); + const cells: Row = {}; + for (const p of PARTS) cells[p.column] = text(firstOf(comps, p.kinds)?.value); + const surname2 = comps.filter((c) => c.kind === 'surname2').map((c) => text(c.value)).filter(Boolean); + if (surname2.length) cells[SN.FAMILY_NAME] = [cells[SN.FAMILY_NAME], ...surname2].filter(Boolean).join(' '); + for (const ph of PHONETICS) { + const spec = PARTS.find((p) => p.column === ph.part)!; + cells[ph.column] = text(firstOf(comps, spec.kinds)?.phonetic); + } + const full = typeof name.full === 'string' && name.full.trim() ? name.full : deriveFullName(comps); + cells[SN.DISPLAY_NAME] = text(full); + return Object.values(cells).some((v) => v !== null) ? cells : null; +} + +/** + * What ContactsProvider stores for written name cells: a display name without + * components is split into them (roughly as its NameSplitter does for simple + * names; the baseline heal corrects the rest). + */ +export function predictStoredName(cells: Row): Row { + const out = { ...cells }; + const display = text(cells[SN.DISPLAY_NAME]); + if (display && NAME_PART_COLUMNS.every((c) => text(cells[c]) === null)) { + const words = display.trim().split(/\s+/); + out[SN.GIVEN_NAME] = words[0] ?? null; + out[SN.FAMILY_NAME] = words.length > 1 ? words.slice(1).join(' ') : null; + } + return out; +} + +/** Whether a stored name row is what writing `wanted` gives (the provider's split allowed). */ +export function nameMatches(wanted: Row, stored: Row, same: (a: unknown, b: unknown) => boolean): boolean { + const split = NAME_PART_COLUMNS.every((c) => text(wanted[c]) === null); + return NAME_COLUMNS.every((c) => (split && (NAME_PART_COLUMNS as readonly string[]).includes(c)) || same(wanted[c], stored[c])); +} + +const squash = (s: string) => s.replace(/\s+/g, ' ').trim(); + +/** The display names the provider (or an editor) computes from the components. */ +function joins(cells: Row): string[] { + const [prefix, given, middle, family, suffix] = NAME_PART_COLUMNS.map((c) => text(cells[c])); + const western = [prefix, given, middle, family].filter(Boolean).join(' '); + return [ + [prefix, given, middle, family, suffix].filter(Boolean).join(' '), + suffix ? `${western}, ${suffix}` : western, + [prefix, family, middle, given, suffix].filter(Boolean).join(''), + [prefix, family, given, middle].filter(Boolean).join(' ') + (suffix ? `, ${suffix}` : ''), + ].map(squash); +} + +const NAME_KINDS = new Set(['surname', 'given', 'given2', 'title', 'credential', 'surname2', 'generation']); + +/** `full` as Stalwart derives it (components in order, separators verbatim). */ +function serverDerivedFull(name: ContactName): string { + const sep = typeof name.defaultSeparator === 'string' && name.defaultSeparator ? name.defaultSeparator : ' '; + let full = ''; + let afterValue = false; + for (const c of components(name)) { + if (typeof c.value !== 'string') continue; + if (c.kind === 'separator') { + full += c.value; + afterValue = false; + } else if (NAME_KINDS.has(c.kind)) { + if (afterValue) full += sep; + full += c.value; + afterValue = true; + } + } + return full; +} + +/** Whether the server's `full` only repeats its components (so it follows them). */ +export function fullIsDerived(name: ContactName | undefined): boolean { + if (!name) return true; + const full = typeof name.full === 'string' ? squash(name.full) : ''; + if (!full) return true; + const comps = components(name); + return full === squash(deriveFullName(comps)) || full === squash(serverDerivedFull(name)); +} + +function place(comps: Component[], component: Component): void { + const rank = RANK[component.kind] ?? 99; + const at = comps.findIndex((c) => (RANK[c.kind] ?? -1) > rank); + if (at < 0) comps.push(component); + else comps.splice(at, 0, component); +} + +/** The server's components with the edited columns applied. */ +function editComponents(name: ContactName | undefined, cells: Row, changed: ReadonlySet): Component[] { + const comps = clone(components(name)); + for (const p of PARTS) { + if (!changed.has(p.column)) continue; + const value = text(cells[p.column]); + if (p.column === SN.FAMILY_NAME) { + for (let i = comps.length - 1; i >= 0; i--) if (comps[i].kind === 'surname2') comps.splice(i, 1); + } + const target = firstOf(comps, p.kinds); + if (target && value !== null) target.value = value; + else if (target) comps.splice(comps.indexOf(target), 1); + else if (value !== null) place(comps, { kind: p.kinds[0], value }); + } + for (const ph of PHONETICS) { + if (!changed.has(ph.column)) continue; + const spec = PARTS.find((p) => p.column === ph.part)!; + const value = text(cells[ph.column]); + const target = firstOf(comps, spec.kinds); + if (target && value !== null) target.phonetic = value; + else if (target) delete target.phonetic; + else if (value !== null) place(comps, { kind: spec.kinds[0], value: '', phonetic: value }); + } + return comps; +} + +/** + * The name part of a patch for edited name columns: `name/components`, + * `name/full`, or the whole `name` when the card has none. + */ +export function namePatch(name: ContactName | undefined, cells: Row, changed: ReadonlySet): Record { + const out: Record = {}; + const partsChanged = [...PARTS.map((p) => p.column), ...PHONETICS.map((p) => p.column)].some((c) => changed.has(c)); + const displayChanged = changed.has(SN.DISPLAY_NAME); + if (!partsChanged && !displayChanged) return out; + const display = text(cells[SN.DISPLAY_NAME]); + if (name && typeof name === 'object' && components(name).length === 0) { + // The server holds this name as `full` alone, so the device's components are the + // provider's split of it: the name goes up whole from the row. + const all = editComponents(undefined, cells, new Set(NAME_COLUMNS)); + const full = display ?? (deriveFullName(all) || null); + if (all.length) out['name/components'] = all; + if (full !== (typeof name.full === 'string' ? name.full : null)) out['name/full'] = full; + return out; + } + const comps = partsChanged ? editComponents(name, cells, changed) : components(name); + let full: string | null | undefined; + if (displayChanged && display && !joins(cells).includes(squash(display))) full = display; + else if (fullIsDerived(name)) full = deriveFullName(comps) || display || null; + const hasName = !!name && typeof name === 'object'; + if (!hasName) { + const whole: Record = {}; + if (comps.length) whole.components = comps; + if (full) whole.full = full; + if (Object.keys(whole).length) out.name = whole; + return out; + } + if (partsChanged) out['name/components'] = comps.length ? comps : null; + if (full !== undefined && full !== (typeof name!.full === 'string' ? name!.full : null)) out['name/full'] = full; + return out; +} diff --git a/src/device-sync/contacts/ops.ts b/src/device-sync/contacts/ops.ts new file mode 100644 index 00000000..063b7b0b --- /dev/null +++ b/src/device-sync/contacts/ops.ts @@ -0,0 +1,75 @@ +/** + * Provider ops the contacts planner emits. Every write into an existing item + * is guarded: the group asserts the VERSION (and DIRTY) the plan was made + * from, and ops that address one row by id expect exactly one row + * (docs/device-sync.md, "Merge rules"). + */ +import { Data, GroupMembership, MimeType, RawContacts } from '../android-columns'; +import type { LocalContact } from '../planner'; +import type { ProviderOp, WriteRow } from '../types'; +import { encodeBaseline } from './cells'; +import { predictStored, SPECS, type Unit } from './project'; + +export function assertContact(local: LocalContact, dirty: boolean | null): ProviderOp { + const values: Record = { [RawContacts.VERSION]: local.version }; + if (dirty !== null) values[RawContacts.DIRTY] = dirty ? 1 : 0; + return { op: 'assert', table: 'raw_contacts', id: local.rawContactId, values, expectCount: 1 }; +} + +export function updateRawContact(id: number, values: WriteRow): ProviderOp { + return { op: 'update', table: 'raw_contacts', id, values, expectCount: 1 }; +} + +/** The cells a unit writes (a membership names its group by row id: GROUP_SOURCE_ID is read-only here). */ +function unitValues(u: Unit): WriteRow { + const out: WriteRow = {}; + for (const [column, value] of Object.entries(u.cells)) { + if (column !== GroupMembership.GROUP_SOURCE_ID) out[column] = value; + } + return { ...out, ...(u.extra ?? {}) }; +} + +/** DATA_SYNC1..3 for a row written from a unit: key, photo hash and the predicted stored cells. */ +export function syncValues(u: Unit): WriteRow { + const out: WriteRow = { + [Data.DATA_SYNC1]: u.key, + [Data.DATA_SYNC3]: encodeBaseline(predictStored(u), SPECS[u.mimetype].columns), + }; + if (u.mimetype === MimeType.PHOTO) out[Data.DATA_SYNC2] = u.photoHash ?? null; + return out; +} + +/** Inserts a unit's row; `parent` is the raw contact id, or `{ ref }` to an insert earlier in the group. */ +export function insertUnit(parent: number | { ref: number }, u: Unit, keyed: boolean): ProviderOp { + const values: WriteRow = { [Data.MIMETYPE]: u.mimetype, ...unitValues(u), ...(keyed ? syncValues(u) : {}) }; + if (typeof parent === 'number') { + values[Data.RAW_CONTACT_ID] = parent; + return { op: 'insert', table: 'data', values }; + } + return { op: 'insert', table: 'data', values, refs: { [Data.RAW_CONTACT_ID]: parent.ref } }; +} + +/** Rewrites a row from a unit; photo bytes only go when the hash differs. */ +export function updateUnit(rowId: number, u: Unit, keyed: boolean, withBytes = true): ProviderOp { + const values = unitValues(u); + if (!withBytes) delete values[Data.DATA15]; + return { op: 'update', table: 'data', id: rowId, values: { ...values, ...(keyed ? syncValues(u) : {}) }, expectCount: 1 }; +} + +export function updateData(rowId: number, values: WriteRow): ProviderOp { + return { op: 'update', table: 'data', id: rowId, values, expectCount: 1 }; +} + +export function deleteData(rowId: number): ProviderOp { + return { op: 'delete', table: 'data', id: rowId, expectCount: 1 }; +} + +/** A delete that tolerates a row that is already gone (purges, server deletes). */ +export function deleteWhereId(table: 'raw_contacts' | 'groups' | 'data', id: number): ProviderOp { + return { op: 'delete', table, where: '_id = ?', args: [id] }; +} + +/** A write that tolerates a row that is already gone (keep-dirty baselines of rows edited meanwhile). */ +export function updateWhereId(table: 'data', id: number, values: WriteRow): ProviderOp { + return { op: 'update', table, where: '_id = ?', args: [id], values }; +} diff --git a/src/device-sync/contacts/photo.ts b/src/device-sync/contacts/photo.ts new file mode 100644 index 00000000..05eb171e --- /dev/null +++ b/src/device-sync/contacts/photo.ts @@ -0,0 +1,91 @@ +/** + * The contact photo: the card's first `media` entry of kind `photo` + * (docs/device-sync.md, "Contacts mapping"). Rows get the full-size bytes + * and DATA_SYNC2 the hash of what was applied; the shadow keeps the hash + * instead of the bytes (`sha256:`), so a raw contact row stays small. + */ +import type { ContactMedia } from '../../api/types'; +import { normalizeContactPhotoUri } from '../../lib/contact-utils'; +import { sha256Hex } from '../../lib/sha256'; +import { clone } from '../common/json'; +import type { ContactCardWire, ContactsContext } from '../planner'; + +const HASH_URI = 'sha256:'; +const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +const B64_INDEX = new Map([...B64].map((c, i) => [c, i])); + +/** Bytes of standard or URL-safe base64 (padding optional); null for anything else. */ +export function base64ToBytes(b64: string): Uint8Array | null { + const clean = b64.replace(/\s+/g, '').replace(/-/g, '+').replace(/_/g, '/').replace(/=+$/, ''); + if (clean.length % 4 === 1) return null; + const out = new Uint8Array(Math.floor((clean.length * 3) / 4)); + let bits = 0; + let value = 0; + let o = 0; + for (const ch of clean) { + const v = B64_INDEX.get(ch); + if (v === undefined) return null; + value = (value << 6) | v; + bits += 6; + if (bits >= 8) { + bits -= 8; + out[o++] = (value >> bits) & 0xff; + } + } + return out.subarray(0, o); +} + +/** The base64 payload of a `data:…;base64,` URI (Stalwart may leave out the media type, #307). */ +export function dataUriBase64(uri: string): string | null { + const m = /^data:[^,]*;base64,(.*)$/is.exec(normalizeContactPhotoUri(uri.trim())); + return m ? m[1].replace(/\s+/g, '') : null; +} + +export function hashBase64(b64: string): string | null { + const bytes = base64ToBytes(b64); + return bytes ? `${HASH_URI}${sha256Hex(bytes)}` : null; +} + +/** The photo entry the device shows: the first `kind: "photo"` entry in map order. */ +export function photoEntry(card: ContactCardWire): { key: string; entry: ContactMedia } | null { + const media = card.media; + if (!media || typeof media !== 'object') return null; + for (const [key, entry] of Object.entries(media)) { + if (entry && typeof entry === 'object' && entry.kind === 'photo' && (entry.uri || (entry as { blobId?: unknown }).blobId)) { + return { key, entry }; + } + } + return null; +} + +/** Bytes (base64) and hash of a photo entry; bytes are null for a shadow's hash-only entry. */ +export function photoPayload(entry: ContactMedia, ctx: Pick): { b64: string | null; hash: string } | null { + const uri = typeof entry.uri === 'string' ? entry.uri : ''; + if (uri.startsWith(HASH_URI)) return { b64: null, hash: uri }; + const b64 = dataUriBase64(uri) ?? ctx.photoBytes(entry as { uri?: string; blobId?: string; mediaType?: string }); + const hash = b64 ? hashBase64(b64) : null; + return b64 && hash ? { b64, hash } : null; +} + +/** The shadow of a card: every `data:` photo URI replaced by the hash of its bytes. */ +export function shadowOf(card: ContactCardWire): ContactCardWire { + const out = clone(card); + const media = out.media; + if (media && typeof media === 'object') { + for (const entry of Object.values(media)) { + if (!entry || typeof entry.uri !== 'string') continue; + const b64 = dataUriBase64(entry.uri); + const hash = b64 ? hashBase64(b64) : null; + if (hash) entry.uri = hash; + } + } + return out; +} + +export function isHashUri(uri: unknown): boolean { + return typeof uri === 'string' && uri.startsWith(HASH_URI); +} + +export function jpegDataUri(b64: string): string { + return `data:image/jpeg;base64,${b64}`; +} diff --git a/src/device-sync/contacts/planner.ts b/src/device-sync/contacts/planner.ts new file mode 100644 index 00000000..f7c79f2d --- /dev/null +++ b/src/device-sync/contacts/planner.ts @@ -0,0 +1,78 @@ +/** + * The contacts planner (docs/device-sync.md, "Contacts mapping" and "Change + * detection and merge rules"): JSContact cards ↔ ContactsContract rows for + * individual and org cards, group cards ↔ Groups rows. Pure: the engine does + * every query, batch and request. + * + * Memberships are synced only when the context has `groupsOf`. + */ +import { Data, MimeType } from '../android-columns'; +import type { ContactsPlanner, LocalContact, OpGroup } from '../planner'; +import type { ProviderOp } from '../types'; +import { changedFromBaseline, encodeBaseline } from './cells'; +import { DATA_COLUMNS, decodeContact, decodeGroup, GROUP_COLUMNS, RAW_CONTACT_COLUMNS } from './columns'; +import { cardUnits, planContactDownload } from './download'; +import { + groupFingerprint, + planGroupAccepted, + planGroupDownload, + planGroupLocalDelete, + planGroupUpload, + planMembershipUploads, +} from './groups'; +import { entryKey } from './keys'; +import { rowGroup } from './members'; +import { assertContact, deleteWhereId, updateData } from './ops'; +import { SPECS } from './project'; +import { contactFingerprint, planContactAccepted, planContactUpload } from './upload'; + +export { contactFingerprint, groupFingerprint }; + +/** Enough context to project a shadow for its keys. */ +const OFFLINE = { nameForUid: () => null, photoBytes: () => null, groupRowIdBySourceId: () => null }; + +function planLocalDelete(local: LocalContact): OpGroup { + return { ref: local.sourceId ?? `row:${local.rawContactId}`, ops: [deleteWhereId('raw_contacts', local.rawContactId)] }; +} + +/** + * Baselines of a clean contact's rows that no longer describe the rows (the + * provider normalised a write, or the read-back after a chunk never ran): + * rewritten to the rows' current cells. Photo rows get the file id the + * provider assigned. + */ +function planBaselineHeal(local: LocalContact): OpGroup | null { + if (local.dirty || local.deleted || !local.sourceId) return null; + const known = new Set(cardUnits(local.shadow, OFFLINE).map((u) => u.key)); + const ops: ProviderOp[] = []; + for (const row of local.rows) { + const spec = SPECS[row.mimetype]; + if (!spec || !row.key) continue; + const ours = row.mimetype === MimeType.GROUP_MEMBERSHIP ? row.key === entryKey('members', rowGroup(row) ?? '') : known.has(row.key); + if (!ours) continue; + const stale = !row.baseline + || changedFromBaseline(row.cells, row.baseline, spec.columns).length > 0 + || spec.columns.some((c) => !(c in row.baseline!)); + if (stale) ops.push(updateData(row.id, { [Data.DATA_SYNC3]: encodeBaseline(row.cells, spec.columns) })); + } + if (!ops.length) return null; + return { ref: local.sourceId, ops: [assertContact(local, false), ...ops] }; +} + +export const contactsPlanner: ContactsPlanner = { + rawContactColumns: RAW_CONTACT_COLUMNS, + dataColumns: DATA_COLUMNS, + groupColumns: GROUP_COLUMNS, + decodeContact, + decodeGroup, + planDownload: planContactDownload, + planLocalDelete, + planBaselineHeal, + planUpload: planContactUpload, + planAccepted: planContactAccepted, + planGroupDownload, + planGroupLocalDelete, + planGroupUpload, + planGroupAccepted, + planMembershipUploads, +}; diff --git a/src/device-sync/contacts/postal.ts b/src/device-sync/contacts/postal.ts new file mode 100644 index 00000000..fd7d590e --- /dev/null +++ b/src/device-sync/contacts/postal.ts @@ -0,0 +1,129 @@ +/** + * `addresses/` ↔ a StructuredPostal row (docs/device-sync.md, "Postal + * components"). Each column joins the components of its kinds in order; an + * edited column replaces all components it was built from with one + * component of its first kind and keeps the rest (separators between other + * components, unknown kinds, phonetics). + */ +import type { AddressComponent, ContactAddress } from '../../api/types'; +import { StructuredPostal as SP } from '../android-columns'; +import { clone } from '../common/json'; +import type { Row } from '../types'; +import { text } from './cells'; + +export const POSTAL_PART_COLUMNS = [SP.STREET, SP.POBOX, SP.NEIGHBORHOOD, SP.CITY, SP.REGION, SP.POSTCODE, SP.COUNTRY] as const; + +const PARTS: Array<{ column: string; kinds: string[]; legacy?: keyof ContactAddress }> = [ + { column: SP.STREET, kinds: ['name', 'number', 'building', 'floor', 'apartment', 'room', 'block', 'direction', 'landmark'], legacy: 'street' }, + { column: SP.POBOX, kinds: ['postOfficeBox'] }, + { column: SP.NEIGHBORHOOD, kinds: ['district', 'subdistrict'] }, + { column: SP.CITY, kinds: ['locality'], legacy: 'locality' }, + { column: SP.REGION, kinds: ['region'], legacy: 'region' }, + { column: SP.POSTCODE, kinds: ['postcode'], legacy: 'postcode' }, + { column: SP.COUNTRY, kinds: ['country'], legacy: 'country' }, +]; + +type Component = AddressComponent; + +const RANK: Record = {}; +PARTS.forEach((p, i) => p.kinds.forEach((k) => (RANK[k] = i))); + +function components(address: ContactAddress): Component[] { + if (Array.isArray(address.components) && address.components.length) { + return address.components.filter((c) => c && typeof c.kind === 'string'); + } + // Legacy flat fields are read when there are no components (never written). + const out: Component[] = []; + for (const p of PARTS) { + const v = p.legacy ? address[p.legacy] : undefined; + if (typeof v === 'string' && v.trim()) out.push({ kind: p.kinds[0], value: v }); + } + return out; +} + +/** Joins the components of `kinds` in order; a separator between two of them is kept verbatim. */ +function joinKinds(comps: Component[], kinds: string[], defaultSeparator: string): string | null { + let out = ''; + let prevWasPart = false; + for (let i = 0; i < comps.length; i++) { + const c = comps[i]; + if (!kinds.includes(c.kind) || typeof c.value !== 'string' || !c.value.trim()) { + if (c.kind !== 'separator') prevWasPart = false; + continue; + } + if (out) { + const prev = comps[i - 1]; + out += prevWasPart && prev?.kind === 'separator' && typeof prev.value === 'string' ? prev.value : defaultSeparator; + } + out += c.value; + prevWasPart = true; + } + return text(out); +} + +/** The single-line address written when the card has no `full`, so the provider leaves the row alone. */ +function joinPostal(cells: Row): string | null { + const [street, pobox, neighborhood, city, region, postcode, country] = POSTAL_PART_COLUMNS.map((c) => text(cells[c])); + const place = [postcode, city].filter(Boolean).join(' '); + return text([street, pobox, neighborhood, place, region, country].filter(Boolean).join(', ')); +} + +/** Postal cells (without TYPE/LABEL) of an address. */ +export function postalCells(address: ContactAddress): Row { + const comps = components(address); + const sep = typeof address.defaultSeparator === 'string' && address.defaultSeparator ? address.defaultSeparator : ' '; + const cells: Row = {}; + for (const p of PARTS) cells[p.column] = joinKinds(comps, p.kinds, p.column === SP.STREET ? sep : ', '); + const full = typeof address.full === 'string' && address.full.trim() ? address.full + : typeof address.fullAddress === 'string' && address.fullAddress.trim() ? address.fullAddress + : joinPostal(cells); + cells[SP.FORMATTED_ADDRESS] = text(full); + return cells; +} + +/** What the provider stores: a formatted address without components is copied into STREET. */ +export function predictStoredPostal(cells: Row): Row { + const out = { ...cells }; + if (text(cells[SP.FORMATTED_ADDRESS]) && POSTAL_PART_COLUMNS.every((c) => text(cells[c]) === null)) { + out[SP.STREET] = cells[SP.FORMATTED_ADDRESS]; + } + return out; +} + +/** The address's components with the edited columns applied (a list for `addresses//components`). */ +export function editPostalComponents(address: ContactAddress, cells: Row, changed: ReadonlySet): Component[] { + const comps = clone(components(address)); + for (const p of PARTS) { + if (!changed.has(p.column)) continue; + const value = text(cells[p.column]); + const first = comps.findIndex((c) => p.kinds.includes(c.kind)); + const drop = new Set(); + comps.forEach((c, i) => { + if (p.kinds.includes(c.kind)) drop.add(i); + }); + // Separators only between removed parts (or at their edge) go with them. + comps.forEach((c, i) => { + if (c.kind !== 'separator') return; + const before = i > 0 && drop.has(i - 1); + const after = i < comps.length - 1 && drop.has(i + 1); + if ((before && (after || i === comps.length - 1)) || (after && i === 0)) drop.add(i); + }); + const kept = comps.filter((_, i) => !drop.has(i)); + if (value !== null) { + let at = comps.slice(0, Math.max(first, 0)).filter((_, i) => !drop.has(i)).length; + if (first < 0) { + // A part the address did not have goes before the first part that follows it in display order. + const later = kept.findIndex((c) => (RANK[c.kind] ?? -1) > (RANK[p.kinds[0]] ?? 0)); + at = later < 0 ? kept.length : later; + } + kept.splice(at, 0, { kind: p.kinds[0], value }); + } + comps.splice(0, comps.length, ...kept); + } + return comps; +} + +/** Components for a new address from its row. */ +export function postalComponentsFromRow(cells: Row): Component[] { + return editPostalComponents({}, cells, new Set(POSTAL_PART_COLUMNS)); +} diff --git a/src/device-sync/contacts/project.ts b/src/device-sync/contacts/project.ts new file mode 100644 index 00000000..b548fd13 --- /dev/null +++ b/src/device-sync/contacts/project.ts @@ -0,0 +1,328 @@ +/** + * A card as the data rows the device should hold for it: one `Unit` per row, + * with its DATA_SYNC1 key, its mapped cells and whatever else the write needs + * (docs/device-sync.md, "Contacts mapping"). Units are what changes are + * detected, merged and uploaded by. + */ +import type { + ContactAddress, + ContactAnniversary, + ContactEmail, + ContactLink, + ContactNickname, + ContactNote, + ContactOrganization, + ContactPhone, + ContactRelation, + ContactTitle, +} from '../../api/types'; +import { partialDateToString } from '../../lib/contact-utils'; +import { + CommonKind, + Data, + GroupMembership, + MimeType, + NicknameType, + Organization as Org, + StructuredPostal as SP, +} from '../android-columns'; +import { canonicalJson } from '../common/json'; +import type { ContactCardWire, ContactsContext } from '../planner'; +import type { Row, WriteRow } from '../types'; +import { cellText, text } from './cells'; +import { NAME_KEY, entryKey, orgKey } from './keys'; +import { NAME_COLUMNS, nameCells, nameMatches, predictStoredName } from './name'; +import { photoEntry, photoPayload } from './photo'; +import { POSTAL_PART_COLUMNS, postalCells, predictStoredPostal } from './postal'; +import { emailType, eventType, phoneType, postalType, relationType, websiteType } from './types-map'; + +/** The group SOURCE_IDs the rows' memberships were written from, kept in the shadow (see `memberOf`). */ +export const MEMBER_OF = '~memberOf'; + +export interface KindSpec { + mimetype: string; + /** Mapped columns: recorded in the baseline, compared for changes. */ + columns: readonly string[]; + /** Columns a row matched by value may upload when non-empty and different (not TYPE/LABEL/IS_PRIMARY). */ + valueColumns: readonly string[]; + /** Whether a row matched by value uploads its TYPE/LABEL too (phones, emails, addresses, events). */ + typed: boolean; + /** Value-match key of a row; null when the row has nothing to match by. */ + primary(cells: Row): string | null; + /** Editors that re-insert rows (Fossify) rewrite this kind on every save. */ + rewritten: boolean; + /** Missing rows of this kind are always deletions: no editor re-inserts it. */ + inPlaceOnly?: boolean; +} + +const low = (v: unknown) => text(v)?.trim().toLowerCase() ?? null; +const squash = (v: unknown) => low(v)?.replace(/[\s,]+/g, ' ').trim() || null; + +const TYPED = [CommonKind.TYPE, CommonKind.LABEL]; + +export const SPECS: Record = { + [MimeType.STRUCTURED_NAME]: { + mimetype: MimeType.STRUCTURED_NAME, columns: NAME_COLUMNS, valueColumns: NAME_COLUMNS, typed: false, + primary: () => NAME_KEY, rewritten: false, + }, + [MimeType.NICKNAME]: { + mimetype: MimeType.NICKNAME, columns: [Data.DATA1], valueColumns: [Data.DATA1], typed: false, + primary: (c) => low(c[Data.DATA1]), rewritten: true, + }, + [MimeType.EMAIL]: { + mimetype: MimeType.EMAIL, columns: [Data.DATA1, ...TYPED, Data.IS_PRIMARY], valueColumns: [Data.DATA1], typed: true, + primary: (c) => low(c[Data.DATA1]), rewritten: true, + }, + [MimeType.PHONE]: { + mimetype: MimeType.PHONE, columns: [Data.DATA1, ...TYPED, Data.IS_PRIMARY], valueColumns: [Data.DATA1], typed: true, + primary: (c) => { + const digits = text(c[Data.DATA1])?.replace(/\D/g, ''); + return digits || low(c[Data.DATA1]); + }, + rewritten: true, + }, + [MimeType.STRUCTURED_POSTAL]: { + mimetype: MimeType.STRUCTURED_POSTAL, + columns: [SP.FORMATTED_ADDRESS, ...TYPED, ...POSTAL_PART_COLUMNS], + valueColumns: [SP.FORMATTED_ADDRESS, ...POSTAL_PART_COLUMNS], + typed: true, + primary: (c) => squash(c[SP.FORMATTED_ADDRESS]) ?? squash(POSTAL_PART_COLUMNS.map((k) => text(c[k])).filter(Boolean).join(' ')), + rewritten: true, + }, + [MimeType.ORGANIZATION]: { + mimetype: MimeType.ORGANIZATION, + columns: [Org.COMPANY, Org.TITLE, Org.DEPARTMENT, Org.JOB_DESCRIPTION], + valueColumns: [Org.COMPANY, Org.TITLE, Org.DEPARTMENT, Org.JOB_DESCRIPTION], + typed: false, + primary: (c) => low(c[Org.COMPANY]) ?? low(c[Org.TITLE]) ?? low(c[Org.JOB_DESCRIPTION]), + rewritten: true, + }, + [MimeType.WEBSITE]: { + mimetype: MimeType.WEBSITE, columns: [Data.DATA1, ...TYPED], valueColumns: [Data.DATA1], typed: false, + primary: (c) => low(c[Data.DATA1])?.replace(/\/+$/, '') ?? null, rewritten: true, + }, + [MimeType.EVENT]: { + mimetype: MimeType.EVENT, columns: [Data.DATA1, ...TYPED], valueColumns: [Data.DATA1], typed: true, + primary: (c) => (text(c[Data.DATA1]) ? `${text(c[Data.DATA1])!.trim()}|${cellText(c[Data.DATA2]) ?? ''}` : null), + rewritten: true, + }, + [MimeType.RELATION]: { + mimetype: MimeType.RELATION, columns: [Data.DATA1, ...TYPED], valueColumns: [Data.DATA1], typed: false, + primary: (c) => low(c[Data.DATA1]), rewritten: false, + }, + [MimeType.NOTE]: { + mimetype: MimeType.NOTE, columns: [Data.DATA1], valueColumns: [Data.DATA1], typed: false, + primary: (c) => text(c[Data.DATA1])?.trim() ?? null, rewritten: true, + }, + [MimeType.PHOTO]: { + mimetype: MimeType.PHOTO, columns: [Data.DATA14], valueColumns: [], typed: false, + primary: () => 'photo', rewritten: false, inPlaceOnly: true, + }, + [MimeType.GROUP_MEMBERSHIP]: { + mimetype: MimeType.GROUP_MEMBERSHIP, columns: [GroupMembership.GROUP_SOURCE_ID], valueColumns: [], typed: false, + primary: (c) => text(c[GroupMembership.GROUP_SOURCE_ID]), rewritten: true, + }, +}; + +/** Kinds in the order rows are written and compared. */ +export const KIND_ORDER = [ + MimeType.STRUCTURED_NAME, MimeType.NICKNAME, MimeType.EMAIL, MimeType.PHONE, MimeType.STRUCTURED_POSTAL, + MimeType.ORGANIZATION, MimeType.WEBSITE, MimeType.EVENT, MimeType.RELATION, MimeType.NOTE, MimeType.PHOTO, + MimeType.GROUP_MEMBERSHIP, +]; + +/** One data row the card calls for. */ +export interface Unit { + mimetype: string; + /** DATA_SYNC1. */ + key: string; + /** The mapped cells, as written. */ + cells: Row; + /** Written but not mapped: a nickname's TYPE, photo bytes, a membership's group row id. */ + extra?: WriteRow; + /** Photos: the hash of the server photo (DATA_SYNC2); unknown when its bytes are not at hand. */ + photoHash?: string; + /** Canonical JSON of the card entries the row comes from, for key reconciliation. */ + source: string; + /** Columns cut to the provider's limit: the device never held all of them, so they never upload. */ + truncated?: string[]; +} + +export interface ProjectOptions { + /** Group SOURCE_IDs whose `members` hold the card's uid; null leaves memberships alone. */ + memberOf: string[] | null; +} + +const entries = (map: unknown): Array<[string, T]> => + map && typeof map === 'object' && !Array.isArray(map) + ? Object.entries(map as Record).filter(([, v]) => v && typeof v === 'object') + : []; + +/** ContactsProvider cuts text columns to 10 KiB and phone numbers to 1000 characters, and keeps the cut. */ +const MAX_TEXT = 10 * 1024; +const MAX_NUMBER = 1000; + +function unit(mimetype: string, key: string, cells: Row, source: unknown, extra?: WriteRow): Unit { + const out: Unit = { mimetype, key, cells, extra, source: canonicalJson(source) }; + for (const [column, value] of Object.entries(cells)) { + const max = mimetype === MimeType.PHONE && column === Data.DATA1 ? MAX_NUMBER : MAX_TEXT; + if (typeof value === 'string' && value.length > max) { + cells[column] = value.slice(0, max); + (out.truncated ??= []).push(column); + } + } + return out; +} + +/** The first entry of a pref-ordered map that is preferred (`pref: 1`) marks IS_PRIMARY. */ +function primaryKey(list: Array<[string, T]>): string | null { + return list.find(([, e]) => e.pref === 1)?.[0] ?? null; +} + +function organizationUnits(card: ContactCardWire): Unit[] { + const orgs = entries(card.organizations); + const titles = entries(card.titles).filter(([, t]) => text(t.name)); + const used = new Set(); + const pick = (org: string | null, role: boolean) => { + const hit = titles.find(([k, t]) => !used.has(k) && (t.organizationId ?? null) === org && (t.kind === 'role') === role); + if (hit) used.add(hit[0]); + return hit ?? null; + }; + const out: Unit[] = []; + const push = (org: [string, ContactOrganization] | null, title: [string, ContactTitle] | null, role: [string, ContactTitle] | null) => { + const units = Array.isArray(org?.[1].units) + ? org![1].units!.map((u) => text(u?.name)).filter(Boolean).join(', ') + : null; + const cells: Row = { + [Org.COMPANY]: text(org?.[1].name), + [Org.TITLE]: text(title?.[1].name), + [Org.DEPARTMENT]: text(units), + [Org.JOB_DESCRIPTION]: text(role?.[1].name), + }; + if (Object.values(cells).every((v) => v === null)) return; + const key = orgKey({ org: org?.[0] ?? null, title: title?.[0] ?? null, role: role?.[0] ?? null }); + out.push(unit(MimeType.ORGANIZATION, key, cells, [org?.[1] ?? null, title?.[1] ?? null, role?.[1] ?? null])); + }; + for (const org of orgs) { + const orgId = org[0]; + push(org, pick(orgId, false), pick(orgId, true)); + } + // Titles of no (or an unknown) organization get rows of their own. + for (const t of titles) { + if (used.has(t[0])) continue; + used.add(t[0]); + if (t[1].kind === 'role') push(null, null, t); + else push(null, t, null); + } + return out; +} + +/** Every data row a card maps to, in `KIND_ORDER`. */ +export function projectCard(card: ContactCardWire, ctx: Pick, options: ProjectOptions): Unit[] { + const out: Unit[] = []; + const name = nameCells(card.name); + if (name) out.push(unit(MimeType.STRUCTURED_NAME, NAME_KEY, name, card.name)); + + for (const [k, e] of entries(card.nicknames)) { + if (!text(e.name)) continue; + out.push(unit(MimeType.NICKNAME, entryKey('nicknames', k), { [Data.DATA1]: text(e.name) }, e, { [Data.DATA2]: NicknameType.DEFAULT })); + } + + const emails = entries(card.emails).filter(([, e]) => text(e.address)); + const primaryEmail = primaryKey(emails); + for (const [k, e] of emails) { + const t = emailType(e); + out.push(unit(MimeType.EMAIL, entryKey('emails', k), { + [Data.DATA1]: text(e.address), [Data.DATA2]: t.type, [Data.DATA3]: t.label, [Data.IS_PRIMARY]: k === primaryEmail ? 1 : 0, + }, e)); + } + + const phones = entries(card.phones).filter(([, e]) => text(e.number)); + const primaryPhone = primaryKey(phones); + for (const [k, e] of phones) { + const t = phoneType(e); + out.push(unit(MimeType.PHONE, entryKey('phones', k), { + [Data.DATA1]: text(e.number), [Data.DATA2]: t.type, [Data.DATA3]: t.label, [Data.IS_PRIMARY]: k === primaryPhone ? 1 : 0, + }, e)); + } + + for (const [k, e] of entries(card.addresses)) { + const cells = postalCells(e); + if (Object.values(cells).every((v) => v === null)) continue; + const t = postalType(e); + out.push(unit(MimeType.STRUCTURED_POSTAL, entryKey('addresses', k), { ...cells, [SP.TYPE]: t.type, [SP.LABEL]: t.label }, e)); + } + + out.push(...organizationUnits(card)); + + for (const [k, e] of entries(card.links)) { + if (!text(e.uri)) continue; + const t = websiteType(e); + out.push(unit(MimeType.WEBSITE, entryKey('links', k), { [Data.DATA1]: text(e.uri), [Data.DATA2]: t.type, [Data.DATA3]: t.label }, e)); + } + + for (const [k, e] of entries(card.anniversaries)) { + const date = text(partialDateToString(e.date)); + if (!date) continue; + const t = eventType(e.kind); + out.push(unit(MimeType.EVENT, entryKey('anniversaries', k), { [Data.DATA1]: date, [Data.DATA2]: t.type, [Data.DATA3]: t.label }, e)); + } + + for (const [uri, e] of entries(card.relatedTo)) { + if (!uri.trim()) continue; + const shown = (uri.startsWith('urn:uuid:') ? ctx.nameForUid(uri) : null) ?? uri; + const t = relationType(e.relation); + out.push(unit(MimeType.RELATION, entryKey('relatedTo', uri), { [Data.DATA1]: shown, [Data.DATA2]: t.type, [Data.DATA3]: t.label }, e)); + } + + const note = entries(card.notes).find(([, e]) => text(e.note)); + if (note) out.push(unit(MimeType.NOTE, entryKey('notes', note[0]), { [Data.DATA1]: text(note[1].note) }, note[1])); + + const photo = photoEntry(card); + if (photo) { + // Without its bytes (a blob the engine could not fetch) the photo still counts, so an + // existing row is kept, not taken for a deletion; it just can't be written. + const payload = photoPayload(photo.entry, ctx); + const u = unit(MimeType.PHOTO, entryKey('media', photo.key), {}, payload?.hash ?? photo.entry); + if (payload) u.photoHash = payload.hash; + if (payload?.b64) u.extra = { [Data.DATA15]: { b64: payload.b64 } }; + out.push(u); + } + + for (const gsid of options.memberOf ?? []) { + const groupRowId = ctx.groupRowIdBySourceId(gsid); + // A membership naming an unknown group would make the provider create an empty one. + if (groupRowId === null) continue; + out.push(unit(MimeType.GROUP_MEMBERSHIP, entryKey('members', gsid), { [GroupMembership.GROUP_SOURCE_ID]: gsid }, gsid, { + [GroupMembership.GROUP_ROW_ID]: groupRowId, + })); + } + return out; +} + +/** What the provider stores for a unit's cells (it splits a lone display name or address). */ +export function predictStored(u: Pick): Row { + if (u.mimetype === MimeType.STRUCTURED_NAME) return predictStoredName(u.cells); + if (u.mimetype === MimeType.STRUCTURED_POSTAL) return predictStoredPostal(u.cells); + return u.cells; +} + +/** Whether a stored row is what writing the unit gives, allowing for the provider's normalisation. */ +export function rowMatchesUnit(u: Pick, stored: Row, same: (a: unknown, b: unknown, c?: string) => boolean): boolean { + if (u.mimetype === MimeType.STRUCTURED_NAME) return nameMatches(u.cells, stored, same); + const spec = SPECS[u.mimetype]; + if (u.mimetype === MimeType.STRUCTURED_POSTAL && POSTAL_PART_COLUMNS.every((c) => text(u.cells[c]) === null)) { + return spec.columns.filter((c) => !(POSTAL_PART_COLUMNS as readonly string[]).includes(c)).every((c) => same(u.cells[c], stored[c], c)); + } + if (u.mimetype === MimeType.PHOTO) return true; + return spec.columns.every((c) => same(u.cells[c], stored[c], c)); +} + +/** Whether a row has nothing in it (an editor's blank nickname or note). */ +export function isEmptyRow(mimetype: string, cells: Row): boolean { + const spec = SPECS[mimetype]; + if (!spec) return true; + if (mimetype === MimeType.PHOTO) return cellText(cells[Data.DATA14]) === null; + if (mimetype === MimeType.GROUP_MEMBERSHIP) return cellText(cells[GroupMembership.GROUP_SOURCE_ID]) === null && cellText(cells[Data.DATA1]) === null; + return spec.valueColumns.every((c) => text(cells[c]) === null); +} diff --git a/src/device-sync/contacts/types-map.ts b/src/device-sync/contacts/types-map.ts new file mode 100644 index 00000000..bd39d86c --- /dev/null +++ b/src/device-sync/contacts/types-map.ts @@ -0,0 +1,223 @@ +/** + * JSContact contexts, features, kinds and relation types ↔ the TYPE/LABEL + * columns of Android's typed data kinds (docs/device-sync.md, "Contacts + * mapping"). Uploads replace only the vocabulary a TYPE stands for and keep + * the rest (a phone's `text` feature, a `school` context). + */ +import { + ContactEventType, + EmailType, + PhoneType, + PostalType, + RelationType, + WebsiteType, +} from '../android-columns'; + +const on = (flags: unknown, name: string) => + !!flags && typeof flags === 'object' && (flags as Record)[name] === true; + +/** TYPE + LABEL as the row holds them. */ +export interface TypeCells { + type: number; + label: string | null; +} + +/** What a TYPE says about an entry: the contexts, features and label to upload. */ +export interface EntryFlags { + contexts: string[]; + features?: string[]; + /** The label a custom type carries; null removes the entry's label. */ + label: string | null; +} + +function labelled(label: unknown, type: number, custom: number): TypeCells { + return typeof label === 'string' && label.trim() ? { type: custom, label } : { type, label: null }; +} + +// ─── Emails ──────────────────────────────────────────── + +export function emailType(entry: { contexts?: unknown; label?: unknown }): TypeCells { + const type = on(entry.contexts, 'private') ? EmailType.HOME : on(entry.contexts, 'work') ? EmailType.WORK : EmailType.OTHER; + return labelled(entry.label, type, EmailType.CUSTOM); +} + +export function emailFlags(type: number | null, label: string | null): EntryFlags { + switch (type) { + case EmailType.HOME: return { contexts: ['private'], label: null }; + case EmailType.WORK: return { contexts: ['work'], label: null }; + case EmailType.MOBILE: return { contexts: [], label: 'mobile' }; + case EmailType.CUSTOM: return { contexts: [], label }; + default: return { contexts: [], label: null }; + } +} + +// ─── Phones ──────────────────────────────────────────── + +/** First match wins, as in the design's table; `cell` is read as `mobile`. */ +export function phoneType(entry: { features?: unknown; contexts?: unknown; label?: unknown }): TypeCells { + const f = (n: string) => on(entry.features, n); + const c = (n: string) => on(entry.contexts, n); + const mobile = f('mobile') || f('cell'); + let type: number = PhoneType.OTHER; + if (mobile && c('work')) type = PhoneType.WORK_MOBILE; + else if (mobile) type = PhoneType.MOBILE; + else if (f('fax') && c('work')) type = PhoneType.FAX_WORK; + else if (f('fax') && c('private')) type = PhoneType.FAX_HOME; + else if (f('fax')) type = PhoneType.OTHER_FAX; + else if (f('pager') && c('work')) type = PhoneType.WORK_PAGER; + else if (f('pager')) type = PhoneType.PAGER; + else if (f('textphone')) type = PhoneType.TTY_TDD; + else if (f('main-number')) type = PhoneType.MAIN; + else if (c('work')) type = PhoneType.WORK; + else if (c('private')) type = PhoneType.HOME; + return labelled(entry.label, type, PhoneType.CUSTOM); +} + +/** The features a phone TYPE stands for; uploads swap these and keep the others. */ +export const PHONE_TYPE_FEATURES = ['mobile', 'cell', 'fax', 'pager', 'textphone', 'main-number', 'voice']; + +const PHONE_LABELS: Record = { + [PhoneType.CALLBACK]: 'callback', + [PhoneType.CAR]: 'car', + [PhoneType.ISDN]: 'isdn', + [PhoneType.RADIO]: 'radio', + [PhoneType.TELEX]: 'telex', + [PhoneType.ASSISTANT]: 'assistant', +}; + +export function phoneFlags(type: number | null, label: string | null): EntryFlags { + switch (type) { + case PhoneType.WORK_MOBILE: return { contexts: ['work'], features: ['mobile'], label: null }; + case PhoneType.MOBILE: return { contexts: [], features: ['mobile'], label: null }; + case PhoneType.FAX_WORK: return { contexts: ['work'], features: ['fax'], label: null }; + case PhoneType.FAX_HOME: return { contexts: ['private'], features: ['fax'], label: null }; + case PhoneType.OTHER_FAX: return { contexts: [], features: ['fax'], label: null }; + case PhoneType.WORK_PAGER: return { contexts: ['work'], features: ['pager'], label: null }; + case PhoneType.PAGER: return { contexts: [], features: ['pager'], label: null }; + case PhoneType.TTY_TDD: return { contexts: [], features: ['textphone'], label: null }; + case PhoneType.MAIN: return { contexts: [], features: ['main-number'], label: null }; + case PhoneType.COMPANY_MAIN: return { contexts: ['work'], features: ['main-number'], label: null }; + case PhoneType.WORK: return { contexts: ['work'], features: ['voice'], label: null }; + case PhoneType.HOME: return { contexts: ['private'], features: ['voice'], label: null }; + case PhoneType.MMS: return { contexts: [], features: ['text'], label: null }; + case PhoneType.CUSTOM: return { contexts: [], features: [], label }; + case null: + case PhoneType.OTHER: return { contexts: [], features: [], label: null }; + default: return { contexts: [], features: ['voice'], label: PHONE_LABELS[type] ?? null }; + } +} + +// ─── Addresses and websites ──────────────────────────── + +export function postalType(entry: { contexts?: unknown; label?: unknown }): TypeCells { + const type = on(entry.contexts, 'private') ? PostalType.HOME : on(entry.contexts, 'work') ? PostalType.WORK : PostalType.OTHER; + return labelled(entry.label, type, PostalType.CUSTOM); +} + +export function postalFlags(type: number | null, label: string | null): EntryFlags { + switch (type) { + case PostalType.HOME: return { contexts: ['private'], label: null }; + case PostalType.WORK: return { contexts: ['work'], label: null }; + case PostalType.CUSTOM: return { contexts: [], label }; + default: return { contexts: [], label: null }; + } +} + +export function websiteType(entry: { contexts?: unknown; label?: unknown }): TypeCells { + const type = on(entry.contexts, 'private') ? WebsiteType.HOME : on(entry.contexts, 'work') ? WebsiteType.WORK : WebsiteType.OTHER; + return labelled(entry.label, type, WebsiteType.CUSTOM); +} + +const WEBSITE_LABELS: Record = { + [WebsiteType.HOMEPAGE]: 'homepage', + [WebsiteType.BLOG]: 'blog', + [WebsiteType.PROFILE]: 'profile', + [WebsiteType.FTP]: 'ftp', +}; + +export function websiteFlags(type: number | null, label: string | null): EntryFlags { + switch (type) { + case WebsiteType.HOME: return { contexts: ['private'], label: null }; + case WebsiteType.WORK: return { contexts: ['work'], label: null }; + case WebsiteType.CUSTOM: return { contexts: [], label }; + case null: + case WebsiteType.OTHER: return { contexts: [], label: null }; + default: return { contexts: [], label: WEBSITE_LABELS[type] ?? null }; + } +} + +/** Contexts the TYPEs above speak for; others are kept on upload. */ +export const TYPED_CONTEXTS = ['private', 'work']; + +// ─── Anniversaries ───────────────────────────────────── + +export function eventType(kind: unknown): TypeCells { + switch (kind) { + case 'birth': return { type: ContactEventType.BIRTHDAY, label: null }; + case 'wedding': return { type: ContactEventType.ANNIVERSARY, label: null }; + case 'other': return { type: ContactEventType.OTHER, label: null }; + default: return { type: ContactEventType.CUSTOM, label: typeof kind === 'string' && kind ? kind : null }; + } +} + +/** The anniversary kind for a device TYPE; a custom label becomes the kind itself. */ +export function eventKind(type: number | null, label: string | null): string { + switch (type) { + case ContactEventType.BIRTHDAY: return 'birth'; + case ContactEventType.ANNIVERSARY: return 'wedding'; + case ContactEventType.CUSTOM: return label?.trim() ? label.trim().toLowerCase() : 'other'; + default: return 'other'; + } +} + +// ─── Relations ───────────────────────────────────────── + +/** RFC 9553 relation types in the order they pick the device TYPE. */ +const RELATION_TO_TYPE: Array<[string, number]> = [ + ['friend', RelationType.FRIEND], + ['spouse', RelationType.SPOUSE], + ['child', RelationType.CHILD], + ['parent', RelationType.PARENT], + ['sibling', RelationType.RELATIVE], + ['kin', RelationType.RELATIVE], + ['agent', RelationType.ASSISTANT], +]; + +export function relationType(relation: unknown): TypeCells { + const types = relation && typeof relation === 'object' + ? Object.keys(relation as object).filter((k) => on(relation, k)) + : []; + for (const [name, type] of RELATION_TO_TYPE) if (types.includes(name)) return { type, label: null }; + return { type: RelationType.CUSTOM, label: types[0] ?? null }; +} + +/** Device types without an RFC 9553 twin upload as the nearest one. */ +const TYPE_TO_RELATION: Record = { + [RelationType.SPOUSE]: 'spouse', + [RelationType.CHILD]: 'child', + [RelationType.PARENT]: 'parent', + [RelationType.FRIEND]: 'friend', + [RelationType.RELATIVE]: 'kin', + [RelationType.ASSISTANT]: 'agent', + [RelationType.BROTHER]: 'sibling', + [RelationType.SISTER]: 'sibling', + [RelationType.MOTHER]: 'parent', + [RelationType.FATHER]: 'parent', + [RelationType.MANAGER]: 'colleague', + [RelationType.DOMESTIC_PARTNER]: 'spouse', + [RelationType.PARTNER]: 'spouse', + [RelationType.REFERRED_BY]: 'contact', +}; + +export function relationName(type: number | null, label: string | null): string | null { + if (type === RelationType.CUSTOM || type === null) return label?.trim() ? label.trim().toLowerCase() : null; + return TYPE_TO_RELATION[type] ?? null; +} + +/** The relation types a device TYPE covers, removed when the TYPE changes. */ +export function relationTypesOf(type: number | null, label: string | null): string[] { + const covered = RELATION_TO_TYPE.filter(([, t]) => t === type).map(([name]) => name); + const own = relationName(type, label); + if (own && !covered.includes(own)) covered.push(own); + return covered; +} diff --git a/src/device-sync/contacts/upload.ts b/src/device-sync/contacts/upload.ts new file mode 100644 index 00000000..b9d221dc --- /dev/null +++ b/src/device-sync/contacts/upload.ts @@ -0,0 +1,292 @@ +/** + * Device changes to the server (docs/device-sync.md, "Uploads"): per changed + * unit a patch of the sub-fields whose columns changed; a whole map only + * where Stalwart needs one (the first entry of a map, and deletions, which + * resend the map from the fresh shadow minus the entry, except `addresses`, + * where `addresses/: null` is clean); never a property Android can't + * represent. New contacts are claimed (a uid and a target book, written + * before anything is sent) and then created. + */ +import { Data, MimeType, RawContacts } from '../android-columns'; +import { collectionKey, parseCollectionKey, parseObjectRef, objectRef } from '../common/ids'; +import { jsonHash } from '../common/json'; +import { applyPatch, ptr, setInPatch, type PatchObject } from '../common/patch'; +import type { + AcceptedPlan, + ContactCardWire, + ContactsContext, + LocalContact, + OpGroup, + UploadAction, + UploadPlan, +} from '../planner'; +import type { ProviderOp } from '../types'; +import { encodeBaseline } from './cells'; +import { localChanges } from './diff'; +import { cardUnits, cleanWrite } from './download'; +import { newEntries, unitEdits, unitEntries, type Edit, type NewEntry } from './entries'; +import { parseEntryKey } from './keys'; +import { deletionsInferred, inPlaceEvidence, matchKind, type KindMatch } from './matching'; +import { shadowMemberOf, withMemberOf, withoutMemberOf } from './members'; +import { NAME_COLUMNS, namePatch } from './name'; +import { assertContact, deleteWhereId, updateRawContact, updateWhereId } from './ops'; +import { isHashUri, jpegDataUri, shadowOf } from './photo'; +import { KIND_ORDER, SPECS } from './project'; + +type Ctx = ContactsContext; + +const ENTRY_KINDS = KIND_ORDER.filter((k) => k !== MimeType.GROUP_MEMBERSHIP); + +/** + * What a poison marker's `fp` is taken of: the engine writes the markers + * (engine/poison.ts) with this same content, so a skip here agrees with them. + */ +export function contactFingerprint(local: LocalContact): string { + return jsonHash({ + deleted: local.deleted, + shadow: local.shadow, + rows: local.rows.map((r) => JSON.stringify([r.mimetype, r.cells])).sort(), + }); +} + +function backedOff(local: LocalContact, ctx: Ctx, actions?: UploadAction[]): boolean { + const p = local.poison; + if (!p || !(p.until > ctx.now)) return false; + return p.fp === contactFingerprint(local) || (actions !== undefined && p.fp === jsonHash(actions)); +} + +interface Changes { + edits: Edit[]; + added: NewEntry[]; + removed: Array<{ map: string; key: string }>; + name: Record; + /** Rows whose keyed units uploaded, for the keep-dirty baselines. */ + uploadedRows: number[]; +} + +/** Everything the rows changed against a shadow (an empty card for a new contact). */ +function collectChanges(local: LocalContact, shadow: ContactCardWire, ctx: Ctx): Changes { + const base = cardUnits(shadow, ctx); + const kinds: KindMatch[] = ENTRY_KINDS.map((k) => matchKind(k, local.rows, base)); + const evidence = inPlaceEvidence(kinds); + const out: Changes = { edits: [], added: [], removed: [], name: {}, uploadedRows: [] }; + for (const kind of kinds) { + if (kind.mimetype === MimeType.STRUCTURED_NAME) { + const m = kind.matched[0]; + const row = m?.row ?? kind.fresh[0]; + if (!row) continue; + const changed = m ? localChanges(m) : [...NAME_COLUMNS]; + out.name = namePatch(m ? shadow.name : undefined, row.cells, new Set(changed)); + if (m && changed.length) out.uploadedRows.push(row.id); + continue; + } + if (kind.mimetype === MimeType.PHOTO) { + photoChanges(local, kind, evidence, ctx, out); + continue; + } + for (const m of kind.matched) { + const changed = localChanges(m); + if (!changed.length) continue; + const shown = kind.mimetype === MimeType.RELATION ? (m.unit.cells[Data.DATA1] as string | null) : null; + const e = unitEdits(kind.mimetype, m.unit.key, shadow, m.row.cells, m.unit.cells, new Set(changed), shown, m.how === 'key'); + out.edits.push(...e.edits); + out.added.push(...e.added); + out.removed.push(...e.removed); + if (m.how === 'key') out.uploadedRows.push(m.row.id); + } + for (const row of kind.fresh) out.added.push(...newEntries(kind.mimetype, row.cells)); + if (deletionsInferred(kind, evidence)) { + for (const unit of kind.missing) out.removed.push(...unitEntries(kind.mimetype, unit.key)); + } + } + return out; +} + +function photoChanges(local: LocalContact, kind: KindMatch, evidence: boolean, ctx: Ctx, out: Changes): void { + const m = kind.matched[0]; + const row = m?.row ?? kind.fresh[0]; + if (row && (!m || localChanges(m).length)) { + const jpeg = ctx.devicePhoto(local.rawContactId); + if (!jpeg) return; + const uri = jpegDataUri(jpeg); + if (m) { + const key = parseEntryKey(m.unit.key)!.key; + out.edits.push({ path: ['media', key, 'uri'], value: uri }, { path: ['media', key, 'mediaType'], value: 'image/jpeg' }); + out.uploadedRows.push(row.id); + } else { + out.added.push({ map: 'media', value: { kind: 'photo', uri, mediaType: 'image/jpeg' } }); + } + } else if (!row && deletionsInferred(kind, evidence)) { + for (const unit of kind.missing) out.removed.push(...unitEntries(kind.mimetype, unit.key)); + } +} + +const mapOf = (card: ContactCardWire, map: string): Record | null => { + const v = card[map]; + return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record) : null; +}; + +/** The PatchObject for a set of changes against the shadow. */ +function assemble(changes: Changes, shadow: ContactCardWire, ctx: Ctx): PatchObject { + const patch: PatchObject = {}; + for (const [pointer, value] of Object.entries(changes.name)) setInPatch(patch, pointer, value); + + // Keys for new entries; titles may point at an organization minted here. + const minted = new Map(); + const taken = new Map>(); + const takenOf = (map: string) => { + if (!taken.has(map)) taken.set(map, new Set(Object.keys(mapOf(shadow, map) ?? {}))); + return taken.get(map)!; + }; + const added = changes.added.filter((e) => { + const keys = takenOf(e.map); + if (e.key !== undefined) { + if (keys.has(e.key) && !changes.removed.some((r) => r.map === e.map && r.key === e.key)) return false; + minted.set(e, e.key); + } else { + minted.set(e, ctx.mintKey(keys)); + } + keys.add(minted.get(e)!); + return true; + }); + const resolve = (value: unknown): unknown => (value && typeof value === 'object' && minted.has(value as NewEntry) ? minted.get(value as NewEntry) : value); + const entryValue = (e: NewEntry) => { + const v: Record = {}; + for (const [k, x] of Object.entries(e.value)) v[k] = resolve(x); + return v; + }; + + const maps = new Set([...added.map((e) => e.map), ...changes.removed.map((r) => r.map)]); + for (const map of maps) { + const current = mapOf(shadow, map); + const removed = changes.removed.filter((r) => r.map === map).map((r) => r.key); + const adds = added.filter((e) => e.map === map); + const remaining = Object.keys(current ?? {}).filter((k) => !removed.includes(k)); + // A map with a placeholder photo URI (the shadow keeps hashes) can't be resent whole. + const resendable = map !== 'media' || remaining.every((k) => !isHashUri((current![k] as { uri?: unknown })?.uri)); + const empty = !current || !Object.keys(current).length; + if (!empty && (map === 'addresses' || (removed.length && !resendable))) { + for (const key of removed) setInPatch(patch, ptr(map, key), null); + for (const e of adds) setInPatch(patch, ptr(map, minted.get(e)!), entryValue(e)); + continue; + } + if (removed.length || empty) { + const whole: Record = {}; + for (const k of remaining) whole[k] = current![k]; + for (const e of adds) whole[minted.get(e)!] = entryValue(e); + setInPatch(patch, ptr(map), Object.keys(whole).length ? whole : null); + continue; + } + for (const e of adds) setInPatch(patch, ptr(map, minted.get(e)!), entryValue(e)); + } + + for (const edit of changes.edits) { + const [map, key] = edit.path; + if (changes.removed.some((r) => r.map === map && r.key === key)) continue; + setInPatch(patch, ptr(...edit.path), resolve(edit.value)); + } + return patch; +} + +/** A new contact's card from its rows: only RFC 9553 properties. */ +export function createObject(local: LocalContact, uid: string, bookId: string, ctx: Ctx): Partial { + const empty = {} as ContactCardWire; + const changes = collectChanges(local, empty, ctx); + changes.removed = []; + const body = applyPatch(empty, assemble(changes, empty, ctx)) ?? {}; + return { '@type': 'Card', version: '1.0', ...body, uid, addressBookIds: { [bookId]: true } }; +} + +/** The patch for a dirty contact against its shadow; empty when nothing mapped changed. */ +function updatePatch(local: LocalContact, ctx: Ctx): PatchObject { + const shadow = withoutMemberOf(local.shadow!); + return assemble(collectChanges(local, shadow, ctx), shadow, ctx); +} + +const group = (local: LocalContact, ops: ProviderOp[]): OpGroup => ({ ref: local.sourceId ?? `row:${local.rawContactId}`, ops }); + +function deletedPlan(local: LocalContact, ctx: Ctx): UploadPlan { + const purge = (): OpGroup => group(local, [deleteWhereId('raw_contacts', local.rawContactId)]); + const ref = parseObjectRef(local.sourceId); + if (ref) { + if (local.readOnly) return { kind: 'revert', ops: purge(), refetch: true }; + const books = Object.entries(local.shadow?.addressBookIds ?? {}).filter(([, on]) => on).map(([id]) => id); + const selected = books.filter((id) => ctx.isSelected(collectionKey(ref.accountId, id))); + // A card that is also in books this device does not sync only leaves the synced ones. + if (selected.length && selected.length < books.length) { + const patch: PatchObject = {}; + for (const id of selected) patch[ptr('addressBookIds', id)] = null; + return { kind: 'upload', actions: [{ kind: 'update', id: ref.id, patch }] }; + } + return { kind: 'upload', actions: [{ kind: 'destroy', id: ref.id, uid: typeof local.shadow?.uid === 'string' ? local.shadow.uid : null }] }; + } + if (local.pending) return { kind: 'upload', actions: [{ kind: 'destroy', id: null, uid: local.pending.uid }] }; + return { kind: 'purge', ops: purge() }; +} + +export function planContactUpload(local: LocalContact, ctx: Ctx): UploadPlan { + if (!local.deleted && !local.dirty && local.sourceId) return { kind: 'clean', ops: group(local, []) }; + if (backedOff(local, ctx)) return { kind: 'skip', reason: `poisoned:${local.poison!.type}` }; + if (local.deleted) return deletedPlan(local, ctx); + if (local.sourceId && !parseObjectRef(local.sourceId)) return { kind: 'skip', reason: 'invalidSourceId' }; + + if (!local.sourceId) { + if (!local.pending) { + const target = ctx.createTarget(); + if (!target) return { kind: 'skip', reason: 'noWritableAddressBook' }; + const pending = { uid: ctx.mintUid(), target }; + return { kind: 'claim', ops: group(local, [assertContact(local, null), updateRawContact(local.rawContactId, { [RawContacts.SYNC3]: JSON.stringify(pending) })]) }; + } + const target = parseCollectionKey(local.pending.target); + if (!target) return { kind: 'skip', reason: 'invalidPendingTarget' }; + const object = createObject(local, local.pending.uid, target.id, ctx); + const actions: UploadAction[] = [{ kind: 'create', uid: local.pending.uid, collectionId: target.id, object }]; + return { kind: 'upload', actions }; + } + + if (!local.shadow) return { kind: 'skip', reason: 'noShadow' }; + if (local.readOnly) { + const write = cleanWrite(local, local.shadow, ctx, { assertDirty: null, clearDirty: true }); + return { kind: 'revert', ops: group(local, write.ops.length ? write.ops : [assertContact(local, null)]) }; + } + const patch = updatePatch(local, ctx); + if (!Object.keys(patch).length) { + // Nothing mapped changed (a star, a ringtone, an edit the server can't take): the rows + // become the shadow's again and DIRTY is cleared behind the VERSION assert. + const write = cleanWrite(local, local.shadow, ctx, { assertDirty: true, clearDirty: true }); + return { kind: 'clean', ops: group(local, write.ops.length ? write.ops : [assertContact(local, true)]) }; + } + const actions: UploadAction[] = [{ kind: 'update', id: parseObjectRef(local.sourceId)!.id, patch }]; + if (backedOff(local, ctx, actions)) return { kind: 'skip', reason: `poisoned:${local.poison!.type}` }; + return { kind: 'upload', actions }; +} + +/** The JMAP account a local contact belongs to. */ +function accountOf(local: LocalContact, ctx: Ctx): string { + return parseObjectRef(local.sourceId)?.accountId ?? parseCollectionKey(local.pending?.target)?.accountId ?? ctx.jmapAccountId; +} + +export function planContactAccepted(local: LocalContact, server: ContactCardWire, ctx: Ctx): AcceptedPlan { + if (local.deleted) { + const purge = group(local, [deleteWhereId('raw_contacts', local.rawContactId)]); + return { ops: purge, keepDirtyOps: purge }; + } + const identity = objectRef(accountOf(local, ctx), server.id); + const raw = { [RawContacts.SOURCE_ID]: identity, [RawContacts.SYNC3]: null, [RawContacts.SYNC4]: null }; + const write = cleanWrite(local, server, ctx, { assertDirty: null, clearDirty: true, accepted: true, raw }); + const ops = group(local, write.ops.length ? write.ops : [assertContact(local, null)]); + ops.ref = identity; + + // Edited again during the upload: identity and shadow, and the baselines of + // the uploaded units set to what was uploaded; rows and DIRTY stay. + const changes = local.shadow ? collectChanges(local, withoutMemberOf(local.shadow), ctx) : null; + const shadow = withMemberOf(shadowOf(withoutMemberOf(server)), shadowMemberOf(local.shadow) ?? (local.shadow ? null : [])); + const keep: ProviderOp[] = [ + updateRawContact(local.rawContactId, { ...raw, [RawContacts.SYNC2]: JSON.stringify(shadow) }), + ]; + for (const id of changes?.uploadedRows ?? []) { + const row = local.rows.find((r) => r.id === id)!; + keep.push(updateWhereId('data', id, { [Data.DATA_SYNC3]: encodeBaseline(row.cells, SPECS[row.mimetype].columns) })); + } + return { ops, keepDirtyOps: { ref: identity, ops: keep } }; +} diff --git a/src/device-sync/task.ts b/src/device-sync/task.ts index 6a1149d9..4500875b 100644 --- a/src/device-sync/task.ts +++ b/src/device-sync/task.ts @@ -26,10 +26,10 @@ import { type TeardownOptions, } from './engine'; import { calendarPlanner } from './calendar/planner'; +import { contactsPlanner } from './contacts/planner'; import { emptyStats } from './engine/report'; import { openJmapPort } from './jmap/client-port'; import { createProviderPort, finishRun, getSyncSettings, isRunCancelled, showSyncProblem } from './native'; -import type { ContactsPlanner } from './planner'; import type { Authority, RunPayload, RunReport } from './types'; // Mirrors the persist names of the locale and feed-subscription stores; read @@ -39,13 +39,6 @@ const SUBSCRIPTIONS_STORAGE_KEY = 'calendar-subscriptions'; /** Where "sign in again" leads (the accounts pane), as for a registry eviction. */ const SIGN_IN_URI = 'bulwarkmobile://settings/account'; -// The contacts planner lands in the next -// commit. Until then a run fails before it touches a row. -function notWired(): never { - throw new Error('Device sync planners are not wired into this build yet'); -} -const contactsPlanner = new Proxy({}, { get: notWired }) as ContactsPlanner; - /** Random numbers from the platform CSPRNG, buffered (uids and map keys of device-made items). */ function secureRandom(): RandomSource { let buffer: Uint8Array = new Uint8Array(0); From e67e2408407f946fb5d5bac675fc80890cc16fb3 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 04:36:51 +0200 Subject: [PATCH 021/334] feat: ask android to sync when contacts or calendars change #34 With device sync on, Android should hear about a change as soon as the app does, not only at the next periodic sync. This adds what tells it: - The app's own contact and calendar edits, StateChanges on the live stream for `AddressBook`/`ContactCard` and `Calendar`/`CalendarEvent`, and the app coming back to the foreground request a sync of that account and authority. Requests within 5 s fold into one, and a StateChange the engine caused itself (its own upload echoing back) is skipped. - While the app is closed, the account's push subscription carries those types too. The FCM and UnifiedPush services hand every push to the native router from f743ab0, which requests the syncs, and start the mail push task only when a push has `EmailDelivery`; the task also returns early for such pushes, so a contact change can never show the "newest unread mail" notification. - At launch, on foreground and when Android's accounts change, the app checks Android's accounts against its own: an account removed in Android Settings shows sync off and is not recreated, and one whose app account is gone stops syncing and posts "Sign in again", keeping its data. The functions that turn sync on and off (`src/device-sync/app/lifecycle.ts`) come with this; the settings that call them land in a later commit, so nothing here runs before an account has device sync on. Checked: the new store, lifecycle, trigger and push tests, `npm run typecheck`, `npm test` and `npm run i18n:check` on this commit, and the debug build. --- App.tsx | 4 + .../bulwarkmobile/BulwarkMessagingService.kt | 7 +- .../BulwarkUnifiedPushService.kt | 6 +- locales/rn/en.json | 106 ++- .../__tests__/app/lifecycle.test.ts | 485 ++++++++++++++ .../__tests__/app/triggers.test.ts | 285 ++++++++ src/device-sync/app/available.ts | 17 + src/device-sync/app/confirm.ts | 40 ++ src/device-sync/app/lifecycle.ts | 622 ++++++++++++++++++ src/device-sync/app/permissions.ts | 54 ++ src/device-sync/app/prefs.ts | 80 +++ src/device-sync/app/request.ts | 92 +++ src/device-sync/app/triggers.ts | 119 ++++ .../__tests__/push-background-task.test.ts | 53 ++ src/lib/__tests__/push-notifications.test.ts | 133 ++++ src/lib/platform-capabilities.ts | 12 + src/lib/push-background-task.ts | 18 +- src/lib/push-notifications.ts | 83 ++- .../__tests__/device-sync-store.test.ts | 268 ++++++++ src/stores/calendar-store.ts | 18 +- src/stores/contacts-store.ts | 13 + src/stores/device-sync-store.ts | 290 ++++++-- 22 files changed, 2742 insertions(+), 63 deletions(-) create mode 100644 src/device-sync/__tests__/app/lifecycle.test.ts create mode 100644 src/device-sync/__tests__/app/triggers.test.ts create mode 100644 src/device-sync/app/available.ts create mode 100644 src/device-sync/app/confirm.ts create mode 100644 src/device-sync/app/lifecycle.ts create mode 100644 src/device-sync/app/permissions.ts create mode 100644 src/device-sync/app/prefs.ts create mode 100644 src/device-sync/app/request.ts create mode 100644 src/device-sync/app/triggers.ts create mode 100644 src/stores/__tests__/device-sync-store.test.ts diff --git a/App.tsx b/App.tsx index 31b0ddc7..1eba8d4d 100644 --- a/App.tsx +++ b/App.tsx @@ -18,6 +18,7 @@ import { startCalendarReminderTapHandling, } from './src/lib/calendar-notifications'; import { startLivenessMonitor } from './src/lib/connection-liveness'; +import { startDeviceSyncTriggers } from './src/device-sync/app/triggers'; import { setPendingCalendarOpen, type CalendarReminderTarget, @@ -399,6 +400,9 @@ export default function App() { // Calendar reminders are kept scheduled from launch, not only once the // Calendar tab has been opened. startCalendarNotificationSync(); + // Device sync (Android, #34): live changes, the app's own edits and the + // foreground ask Android to sync; accounts are checked against Android's. + startDeviceSyncTriggers(); return useNetworkStore.getState().init(); }, []); diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkMessagingService.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkMessagingService.kt index 41c553db..3959e5b6 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkMessagingService.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkMessagingService.kt @@ -11,6 +11,7 @@ import android.os.Bundle import android.os.PowerManager import android.util.Log import androidx.core.app.NotificationCompat +import com.anonymous.bulwarkmobile.sync.DeviceSyncPushRouter import com.facebook.react.bridge.Arguments import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage @@ -26,12 +27,16 @@ class BulwarkMessagingService : FirebaseMessagingService() { val data = message.data + // Contact and calendar changes become syncs of the Android accounts + // they feed (device sync, #34); only mail needs the push task. + val mail = DeviceSyncPushRouter.route(applicationContext, data) + // Hand off to JS via a headless task only when the app isn't already // running in the foreground - HeadlessJsTaskContext throws if started // while foreground. When the app is open, the main JS instance already // receives JMAP push directly, so the fcm:message event below is // sufficient for it to refresh state. - if (!isAppInForeground()) { + if (mail && !isAppInForeground()) { startHeadlessTask(applicationContext, data) } diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkUnifiedPushService.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkUnifiedPushService.kt index 81f9f196..5fac3d44 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkUnifiedPushService.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkUnifiedPushService.kt @@ -1,6 +1,7 @@ package com.anonymous.bulwarkmobile import android.content.Context +import com.anonymous.bulwarkmobile.sync.DeviceSyncPushRouter import com.facebook.react.bridge.Arguments import org.json.JSONArray import org.json.JSONObject @@ -31,7 +32,10 @@ class BulwarkUnifiedPushService : PushService() { BulwarkMessagingService.ensureChannel(this) val data = parsePayload(message.content) ?: return - if (!BulwarkMessagingService.isAppInForeground()) { + // Same routing as FCM: contact and calendar changes request device + // syncs, and only mail starts the push task. + val mail = DeviceSyncPushRouter.route(applicationContext, data) + if (mail && !BulwarkMessagingService.isAppInForeground()) { BulwarkMessagingService.startHeadlessTask(applicationContext, data) } diff --git a/locales/rn/en.json b/locales/rn/en.json index b880e06a..ff5d09ef 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -93,6 +93,8 @@ "until_date": "Until {date}" }, "settings": { + "device_sync_desc": "Show your calendars in the Calendar app and other apps on this device. Changes sync both ways.", + "device_sync_title": "Sync to this device", "show_week_numbers_mobile_desc": "Display week numbers in the month view.", "time_zone": "Time zone", "time_zone_auto": "Device time zone", @@ -255,7 +257,14 @@ "device_sync": { "auth_problem_text": "The server no longer accepts the sign-in of {account}, so its contacts and calendars on this device stopped syncing. Sign in again to resume. Changes made on this device are kept.", "auth_problem_title": "Sign in again to keep syncing", - "problem_channel": "Sync problems" + "problem_channel": "Sync problems", + "sign_out_anyway": "Sign out", + "sign_out_pending_message": "{count, plural, one {# change made on this device hasn't reached the server. It is lost if you sign out now.} other {# changes made on this device haven't reached the server. They are lost if you sign out now.}}", + "sign_out_pending_title": "Sign out anyway?", + "sign_out_unknown_message": "Changes made on this device may not have reached the server. They are lost if you sign out now.", + "signed_out_text": "{account} was signed out, so its contacts and calendars on this device no longer sync. Sign in again to resume. Changes made on this device are kept.", + "signed_out_title": "Sign in to keep syncing", + "signing_out": "Removing synced contacts and calendars from this device…" }, "email_composer": { "attach_camera": "Camera", @@ -613,11 +622,106 @@ "book_count": "{count, plural, one {# contact} other {# contacts}}", "default_book": "Default address book", "description_mobile": "Display preferences and address-book tools.", + "device_sync_desc": "Show your address books in the Contacts app and other apps on this device. Changes sync both ways.", + "device_sync_title": "Sync to this device", "export_description_count": "{count, plural, one {Export # contact as a single vCard file.} other {Export # contacts as a single vCard file.}}", "export_empty": "No contacts to export", "import_description_mobile": "Import contacts from a vCard (.vcf) file with duplicate detection.", "manage_description_mobile": "Create, rename, and remove the address books that organize your contacts." }, + "device_sync": { + "also_synced_by": "Also synced for {accounts}, so it shows twice on this device.", + "android_settings": "Android account settings", + "blocked_calendar": "Android no longer asks for access to calendars. Allow it in the app's settings, then turn sync on again.", + "blocked_contacts": "Android no longer asks for access to contacts. Allow it in the app's settings, then turn sync on again.", + "blocked_title": "Access is turned off", + "books_default": "Your own address books sync; shared ones only when you pick them.", + "books_hint": "Your own address books sync unless you turn them off; shared ones only when you turn them on.", + "books_label": "Address books", + "calendars_default": "Your own calendars sync; shared ones only when you pick them.", + "calendars_hint": "Your own calendars sync unless you turn them off; shared ones only when you turn them on. Task lists stay in Bulwark.", + "calendars_label": "Calendars", + "choose": "Choose", + "choose_books_title": "Address books to sync", + "choose_calendars_title": "Calendars to sync", + "collections_count": "{selected} of {total} sync", + "continue": "Continue", + "denied_calendar": "Without access to calendars, Bulwark cannot put your calendars on this device.", + "denied_contacts": "Without access to contacts, Bulwark cannot put your address books on this device.", + "denied_title": "Sync stays off", + "failed_title": "Sync could not be turned on", + "grant_access": "Grant access", + "interval_15m": "Every 15 minutes", + "interval_1h": "Every hour", + "interval_30m": "Every 30 minutes", + "interval_6h": "Every 6 hours", + "interval_desc": "How often Android syncs on its own. Edits in Bulwark and pushed changes sync sooner.", + "interval_label": "Sync interval", + "interval_manual": "No regular sync", + "keep_syncing": "Keep syncing", + "list_error_auth": "The server did not accept the sign-in.", + "list_error_network": "Could not reach the server.", + "list_error_server": "The server could not list them.", + "list_error_signed_out": "This account is not signed in on this device.", + "master_off": "Auto-sync is off on this device, so syncs only run when you tap Sync now.", + "new_contacts_default": "Default address book", + "new_contacts_desc": "The address book for contacts created in other apps on this device.", + "new_contacts_label": "New contacts go to", + "no_books": "This account has no address books.", + "no_calendars": "This account has no calendars to sync.", + "ok": "OK", + "open_app_settings": "Open app settings", + "own_collections": "Your own", + "rationale_calendar": "To show your calendars in the Calendar app, Bulwark needs access to the calendars on this device. It only changes the calendars of its own account.", + "rationale_contacts": "To show your address books in the Contacts app, Bulwark needs access to the contacts on this device. It only changes the contacts of its own account.", + "rationale_title_calendar": "Allow access to calendars", + "rationale_title_contacts": "Allow access to contacts", + "read_only": "Read-only", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Calendar app", + "reminder_question_message": "Synced events can remind you from the calendar app on this device or from Bulwark. Pick one so you are not reminded twice. You can change it later.", + "reminder_question_title": "Who should remind you?", + "reminders_desc": "Who reminds you of synced events. The other stays quiet, so you are not reminded twice.", + "reminders_label": "Reminders", + "reminders_nobody": "Bulwark's event notifications are off, so nothing reminds you of synced events.", + "remove": "Remove", + "remove_from_device": "Remove from this device", + "remove_message": "The account's contacts and calendars are removed from this device. Changes made here that haven't reached the server are lost.", + "remove_title": "Remove from this device?", + "review_deletions": "Review deletions", + "shared_by": "Shared by {name}", + "sign_in_again": "Sign in again", + "status_auth": "The server no longer accepts the sign-in. Sign in again to keep syncing.", + "status_cancelled": "The last sync stopped early. It continues next time.", + "status_failed": "The last sync failed: {message}", + "status_failed_plain": "The last sync failed.", + "status_io": "Could not reach the server {when}. Android tries again.", + "status_item_errors": "{count, plural, one {# item could not be synced.} other {# items could not be synced.}}", + "status_off": "Off", + "status_paused": "Paused in Android settings", + "status_permission_calendar": "Access to calendars was turned off, so nothing syncs.", + "status_permission_contacts": "Access to contacts was turned off, so nothing syncs.", + "status_removed": "Removed in Android settings. Turn on to add the account again.", + "status_safety_abort": "Stopped: the server returned nothing while this device still holds items. Check the server, then sync again.", + "status_synced": "Synced {when}", + "status_syncing": "Syncing…", + "status_too_many_deletions": "Many items were deleted on this device. They are not deleted on the server until you confirm.", + "status_unsupported_calendar": "The server offers no calendars for this account.", + "status_unsupported_contacts": "The server offers no address books for this account.", + "status_waiting": "Waiting for the first sync", + "status_waiting_next": "Waiting for the next sync", + "suspended_desc": "Signed out. Its contacts and calendars stay on this device, with the changes made here, and sync again once you sign in.", + "switch_label": "Sync {account} to this device", + "sync_now": "Sync now", + "turn_off_anyway": "Turn off", + "turn_off_failed": "The synced data could not be removed from this device. Sync is paused; try again later.", + "turn_off_failed_title": "Sync could not be turned off", + "turn_off_pending": "{count, plural, one {# change made on this device hasn't reached the server yet. It is lost if you turn sync off now.} other {# changes made on this device haven't reached the server yet. They are lost if you turn sync off now.}}", + "turn_off_title": "Turn off anyway?", + "turn_off_unknown": "Bulwark could not check whether changes made on this device reached the server. They are lost if you turn sync off now.", + "turning_off": "Uploading changes, then removing the data from this device…", + "turning_on": "Setting up…" + }, "downloads": { "attachment_template": { "description_mobile": "Template used when saving or sharing an attachment." diff --git a/src/device-sync/__tests__/app/lifecycle.test.ts b/src/device-sync/__tests__/app/lifecycle.test.ts new file mode 100644 index 00000000..d328abe6 --- /dev/null +++ b/src/device-sync/__tests__/app/lifecycle.test.ts @@ -0,0 +1,485 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { + CALENDAR_AUTHORITY, + CONTACTS_AUTHORITY, + type Authority, +} from '../../types'; + +// A fake of the native module: Android's accounts of our type and their sync +// settings, with every call logged in order. +const h = vi.hoisted(() => { + type Settings = { automatic: boolean; periodicSeconds: number }; + const state = { + available: true, + permitted: true, + master: true, + accounts: [] as Array<{ name: string; registryId: string | null; sync: Record }>, + log: [] as string[], + failSetSyncEnabled: false, + }; + const find = (name: string) => state.accounts.find((a) => a.name === name); + const settingsOf = (name: string, authority: string) => { + const account = find(name); + if (!account) return { automatic: false, periodicSeconds: 0 }; + account.sync[authority] ??= { automatic: false, periodicSeconds: 0 }; + return account.sync[authority]; + }; + const teardown = async ( + _registryId: string, + name: string, + authority: string, + options?: { uploadFirst?: boolean; timeoutMs?: number; force?: boolean }, + ) => { + state.log.push(`teardown ${name} ${authority} ${JSON.stringify(options ?? {})}`); + return { pending: 0 }; + }; + return { state, find, settingsOf, teardown }; +}); + +vi.mock('../../native', () => ({ + isDeviceSyncAvailable: () => h.state.available, + listAndroidAccounts: vi.fn(async () => h.state.accounts.map((a) => ({ name: a.name, registryId: a.registryId }))), + ensureAndroidAccount: vi.fn(async (name: string, registryId: string) => { + h.state.log.push(`ensure ${name}`); + const existing = h.find(name); + if (existing && existing.registryId !== registryId) throw new Error('conflict'); + if (existing) return false; + h.state.accounts.push({ name, registryId, sync: {} }); + return true; + }), + removeAndroidAccount: vi.fn(async (name: string) => { + h.state.log.push(`remove ${name}`); + h.state.accounts = h.state.accounts.filter((a) => a.name !== name); + return true; + }), + getSyncSettings: vi.fn(async (name: string) => ({ + masterAutomatic: h.state.master, + authorities: { + 'com.android.contacts': { syncable: 1, active: false, pending: false, ...h.settingsOf(name, 'com.android.contacts') }, + 'com.android.calendar': { syncable: 1, active: false, pending: false, ...h.settingsOf(name, 'com.android.calendar') }, + }, + })), + setSyncEnabled: vi.fn(async (name: string, authority: string, enabled: boolean) => { + if (h.state.failSetSyncEnabled) throw new Error('boom'); + h.state.log.push(`auto ${name} ${authority} ${enabled}`); + h.settingsOf(name, authority).automatic = enabled; + }), + setPeriodicSync: vi.fn(async (name: string, authority: string, seconds: number) => { + h.state.log.push(`periodic ${name} ${authority} ${seconds}`); + h.settingsOf(name, authority).periodicSeconds = seconds; + }), + requestSync: vi.fn(async (name: string, authority: string, options?: Record) => { + h.state.log.push(`sync ${name} ${authority} ${JSON.stringify(options ?? {})}`); + }), + openAccountSettings: vi.fn(async () => undefined), + setPushRoutes: vi.fn(async () => undefined), + showSyncProblem: vi.fn(async (name: string) => { h.state.log.push(`problem ${name}`); }), + clearSyncProblem: vi.fn(async (name: string) => { h.state.log.push(`clear ${name}`); }), + onAccountsChanged: vi.fn(() => () => undefined), +})); + +vi.mock('../../task', () => ({ teardownAuthority: vi.fn(h.teardown) })); + +vi.mock('../../app/permissions', () => ({ + hasSyncPermissions: vi.fn(async () => h.state.permitted), +})); + +vi.mock('../../../lib/push-notifications', () => ({ + refreshPushSubscriptionTypes: vi.fn(async () => undefined), +})); + +import * as native from '../../native'; +import * as task from '../../task'; +import { refreshPushSubscriptionTypes } from '../../../lib/push-notifications'; +import { useAccountStore, type AccountEntry } from '../../../stores/account-store'; +import { + accountDeviceSync, + useDeviceSyncStore, + waitForDeviceSyncHydration, +} from '../../../stores/device-sync-store'; +import { + changeReminderOwner, + changeSyncInterval, + deviceSyncSignedIn, + disableDeviceSync, + enableDeviceSync, + preferredAndroidAccountName, + reconcileDeviceAccounts, + releaseDeviceSyncBeforeSignOut, + removeSuspendedAccount, + resumeDeviceSync, + TEARDOWN_TIMEOUT_MS, +} from '../../app/lifecycle'; + +const ALICE = 'alice@mail.example.org'; +const ALICE2 = 'alice@other.example.net'; +const BOB = 'bob@mail.example.org'; + +function entry(id: string, username: string): AccountEntry { + return { + id, + serverUrl: `https://${id.split('@').pop()}`, + username, + displayName: username, + email: `${username}@example.org`, + avatarColor: '#000', + lastLoginAt: 0, + isConnected: true, + hasError: false, + isDefault: false, + }; +} + +async function ready(registry: AccountEntry[]): Promise { + await waitForDeviceSyncHydration(); + if (!useAccountStore.persist.hasHydrated()) { + await new Promise((resolve) => { + const off = useAccountStore.persist.onFinishHydration(() => { off(); resolve(); }); + }); + } + useAccountStore.setState({ accounts: registry, activeAccountId: registry[0]?.id ?? null }); +} + +const teardown = vi.mocked(task.teardownAuthority); +const setSyncEnabled = vi.mocked(native.setSyncEnabled); + +beforeEach(async () => { + vi.clearAllMocks(); + h.state.available = true; + h.state.permitted = true; + h.state.master = true; + h.state.accounts = []; + h.state.log = []; + h.state.failSetSyncEnabled = false; + teardown.mockImplementation(h.teardown); + await AsyncStorage.clear(); + useDeviceSyncStore.setState({ accounts: {} }); + await ready([entry(ALICE, 'alice'), entry(BOB, 'bob')]); +}); + +describe('enable', () => { + it('creates the Android account under the login, turns automatic and periodic sync on and syncs', async () => { + useDeviceSyncStore.getState().setIntervalSeconds(ALICE, CONTACTS_AUTHORITY, 1800); + const outcome = await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + expect(outcome).toEqual({ kind: 'enabled', accountName: 'alice' }); + expect(h.state.log).toEqual([ + 'ensure alice', + `auto alice ${CONTACTS_AUTHORITY} true`, + `periodic alice ${CONTACTS_AUTHORITY} 1800`, + `sync alice ${CONTACTS_AUTHORITY} {"manual":true}`, + ]); + const stored = accountDeviceSync(ALICE); + expect(stored.androidAccountName).toBe('alice'); + expect(stored.enabled).toEqual({ [CONTACTS_AUTHORITY]: true }); + await vi.waitFor(() => expect(refreshPushSubscriptionTypes).toHaveBeenCalledWith(ALICE)); + }); + + it('uses the default hourly interval', async () => { + await enableDeviceSync(ALICE, CALENDAR_AUTHORITY); + expect(h.state.log).toContain(`periodic alice ${CALENDAR_AUTHORITY} 3600`); + }); + + it('does nothing without the runtime permissions', async () => { + h.state.permitted = false; + expect(await enableDeviceSync(ALICE, CONTACTS_AUTHORITY)).toEqual({ kind: 'permission' }); + expect(h.state.log).toEqual([]); + expect(accountDeviceSync(ALICE).enabled).toBeUndefined(); + }); + + it('reuses the Android account of the app account', async () => { + h.state.accounts.push({ name: 'alice', registryId: ALICE, sync: {} }); + await enableDeviceSync(ALICE, CALENDAR_AUTHORITY); + expect(h.state.log.filter((l) => l.startsWith('ensure'))).toEqual([]); + expect(accountDeviceSync(ALICE).androidAccountName).toBe('alice'); + }); + + it('names the account after its registry id when another app account has the login', async () => { + await ready([entry(ALICE, 'alice'), entry(ALICE2, 'alice')]); + h.state.accounts.push({ name: 'alice', registryId: ALICE, sync: {} }); + expect(preferredAndroidAccountName(ALICE2, await native.listAndroidAccounts())).toBe(ALICE2); + const outcome = await enableDeviceSync(ALICE2, CONTACTS_AUTHORITY); + expect(outcome).toEqual({ kind: 'enabled', accountName: ALICE2 }); + }); + + it('falls back to the registry id when the login turns out to be taken', async () => { + const list = vi.mocked(native.listAndroidAccounts); + // A stale list: the clash only shows when the account is added. + list.mockResolvedValueOnce([]); + h.state.accounts.push({ name: 'alice', registryId: ALICE2, sync: {} }); + const outcome = await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + expect(outcome).toEqual({ kind: 'enabled', accountName: ALICE }); + }); + + it('leaves no account behind when turning sync on fails', async () => { + h.state.failSetSyncEnabled = true; + const outcome = await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + expect(outcome.kind).toBe('failed'); + expect(h.state.accounts).toEqual([]); + expect(accountDeviceSync(ALICE).enabled).toBeUndefined(); + }); + + it('keeps the account of the other authority when turning one on fails', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + vi.mocked(native.setPeriodicSync).mockRejectedValueOnce(new Error('boom')); + expect((await enableDeviceSync(ALICE, CALENDAR_AUTHORITY)).kind).toBe('failed'); + expect(h.find('alice')?.sync[CALENDAR_AUTHORITY].automatic).toBe(false); + expect(h.find('alice')?.sync[CONTACTS_AUTHORITY].automatic).toBe(true); + expect(accountDeviceSync(ALICE)).toMatchObject({ + androidAccountName: 'alice', + enabled: { [CONTACTS_AUTHORITY]: true }, + }); + }); + + it('is a no-op where device sync is not available', async () => { + h.state.available = false; + expect((await enableDeviceSync(ALICE, CONTACTS_AUTHORITY)).kind).toBe('failed'); + expect(h.state.log).toEqual([]); + }); +}); + +describe('disable', () => { + beforeEach(async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + await enableDeviceSync(ALICE, CALENDAR_AUTHORITY); + await changeReminderOwner(ALICE, 'bulwark'); + h.state.log = []; + }); + + it('turns automatic sync off before the teardown uploads and deletes', async () => { + const result = await disableDeviceSync(ALICE, CALENDAR_AUTHORITY); + expect(result).toEqual({ done: true, pending: 0 }); + expect(h.state.log).toEqual([ + `auto alice ${CALENDAR_AUTHORITY} false`, + `teardown alice ${CALENDAR_AUTHORITY} {"uploadFirst":true,"timeoutMs":${TEARDOWN_TIMEOUT_MS}}`, + ]); + const stored = accountDeviceSync(ALICE); + expect(stored.enabled).toEqual({ [CONTACTS_AUTHORITY]: true, [CALENDAR_AUTHORITY]: false }); + // Contacts still sync: the account stays; the reminder question comes again next time. + expect(h.find('alice')).toBeDefined(); + expect(stored.reminderOwner).toBeNull(); + }); + + it('removes the Android account once both authorities are off', async () => { + await disableDeviceSync(ALICE, CALENDAR_AUTHORITY); + await disableDeviceSync(ALICE, CONTACTS_AUTHORITY); + expect(h.state.accounts).toEqual([]); + expect(h.state.log).toContain('remove alice'); + expect(accountDeviceSync(ALICE).androidAccountName).toBeUndefined(); + }); + + it('deletes nothing and reports the waiting changes when they could not be uploaded', async () => { + teardown.mockResolvedValueOnce({ pending: 3 }); + const result = await disableDeviceSync(ALICE, CONTACTS_AUTHORITY); + expect(result).toEqual({ done: false, pending: 3 }); + expect(accountDeviceSync(ALICE).enabled?.[CONTACTS_AUTHORITY]).toBe(true); + + // The user keeps syncing: automatic sync comes back. + h.state.log = []; + await resumeDeviceSync(ALICE, CONTACTS_AUTHORITY); + expect(h.state.log).toEqual([ + `auto alice ${CONTACTS_AUTHORITY} true`, + `sync alice ${CONTACTS_AUTHORITY} {"manual":true}`, + ]); + }); + + it('deletes on the user\'s word with force', async () => { + teardown.mockResolvedValueOnce({ pending: 2 }); + await disableDeviceSync(ALICE, CONTACTS_AUTHORITY); + h.state.log = []; + const result = await disableDeviceSync(ALICE, CONTACTS_AUTHORITY, { force: true }); + expect(result.done).toBe(true); + expect(h.state.log).toContain(`teardown alice ${CONTACTS_AUTHORITY} {"force":true,"timeoutMs":5000}`); + expect(accountDeviceSync(ALICE).enabled?.[CONTACTS_AUTHORITY]).toBe(false); + }); + + it('reports a failed teardown as unknown changes', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined); + teardown.mockRejectedValueOnce(new Error('provider gone')); + expect(await disableDeviceSync(ALICE, CONTACTS_AUTHORITY)).toEqual({ done: false, pending: 0, failed: true }); + warn.mockRestore(); + }); +}); + +describe('sign-out', () => { + it('lets an account without device sync go at once', async () => { + const confirm = vi.fn(async () => true); + expect(await releaseDeviceSyncBeforeSignOut([BOB], confirm)).toBe(true); + expect(teardown).not.toHaveBeenCalled(); + expect(confirm).not.toHaveBeenCalled(); + }); + + it('uploads, deletes and removes the Android account before the credentials go', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + h.state.log = []; + const confirm = vi.fn(async () => true); + expect(await releaseDeviceSyncBeforeSignOut([ALICE], confirm)).toBe(true); + expect(confirm).not.toHaveBeenCalled(); + expect(teardown).toHaveBeenCalledWith(ALICE, 'alice', CONTACTS_AUTHORITY, { uploadFirst: true, timeoutMs: TEARDOWN_TIMEOUT_MS }); + expect(teardown).toHaveBeenCalledWith(ALICE, 'alice', CALENDAR_AUTHORITY, { uploadFirst: true, timeoutMs: TEARDOWN_TIMEOUT_MS }); + expect(h.state.accounts).toEqual([]); + expect(useDeviceSyncStore.getState().accounts[ALICE]).toBeUndefined(); + // Automatic sync went off before the first teardown. + expect(h.state.log.indexOf(`auto alice ${CONTACTS_AUTHORITY} false`)) + .toBeLessThan(h.state.log.findIndex((l) => l.startsWith('teardown'))); + }); + + it('asks before losing changes, and keeps syncing when the user stays', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + teardown.mockImplementation(async (_r: string, _n: string, authority: string) => ({ pending: authority === CONTACTS_AUTHORITY ? 4 : 0 })); + const confirm = vi.fn(async () => false); + expect(await releaseDeviceSyncBeforeSignOut([ALICE], confirm)).toBe(false); + expect(confirm).toHaveBeenCalledWith(4); + expect(h.find('alice')?.sync[CONTACTS_AUTHORITY].automatic).toBe(true); + expect(accountDeviceSync(ALICE).androidAccountName).toBe('alice'); + }); + + it('deletes what is left once the user agrees', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + teardown.mockImplementation(async (_r: string, _n: string, authority: string, options?: { force?: boolean }) => + ({ pending: authority === CONTACTS_AUTHORITY && !options?.force ? 4 : 0 })); + const confirm = vi.fn(async () => true); + expect(await releaseDeviceSyncBeforeSignOut([ALICE], confirm)).toBe(true); + expect(teardown).toHaveBeenCalledWith(ALICE, 'alice', CONTACTS_AUTHORITY, { force: true, timeoutMs: 5_000 }); + expect(h.state.accounts).toEqual([]); + }); + + it('asks once for several accounts', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + await enableDeviceSync(BOB, CALENDAR_AUTHORITY); + teardown.mockImplementation(async (_r: string, _n: string, _a: string, options?: { force?: boolean }) => ({ pending: options?.force ? 0 : 1 })); + const confirm = vi.fn(async () => true); + expect(await releaseDeviceSyncBeforeSignOut([ALICE, BOB], confirm)).toBe(true); + expect(confirm).toHaveBeenCalledTimes(1); + expect(confirm).toHaveBeenCalledWith(4); + expect(h.state.accounts).toEqual([]); + }); +}); + +describe('reconcile', () => { + it('suspends an Android account whose app account the registry dropped, once', async () => { + h.state.accounts.push({ + name: 'carol', + registryId: 'carol@mail.example.org', + sync: { [CONTACTS_AUTHORITY]: { automatic: true, periodicSeconds: 3600 } }, + }); + await reconcileDeviceAccounts(); + expect(h.find('carol')?.sync[CONTACTS_AUTHORITY].automatic).toBe(false); + expect(h.find('carol')).toBeDefined(); + expect(native.showSyncProblem).toHaveBeenCalledTimes(1); + expect(vi.mocked(native.showSyncProblem).mock.calls[0][3]).toBe('bulwarkmobile://settings/account'); + const carol = accountDeviceSync('carol@mail.example.org'); + expect(carol.suspended).toBe(true); + expect(carol.enabled).toEqual({ [CONTACTS_AUTHORITY]: true }); + + await reconcileDeviceAccounts(); + expect(native.showSyncProblem).toHaveBeenCalledTimes(1); + }); + + it('drops a suspended account and its data when the user removes it', async () => { + h.state.accounts.push({ name: 'carol', registryId: 'carol@mail.example.org', sync: {} }); + await reconcileDeviceAccounts(); + h.state.log = []; + await removeSuspendedAccount('carol@mail.example.org'); + expect(h.state.log).toEqual(['remove carol', 'clear carol']); + expect(useDeviceSyncStore.getState().accounts['carol@mail.example.org']).toBeUndefined(); + }); + + it('never acts on an empty registry', async () => { + await ready([]); + h.state.accounts.push({ name: 'carol', registryId: 'carol@mail.example.org', sync: {} }); + await reconcileDeviceAccounts(); + expect(native.showSyncProblem).not.toHaveBeenCalled(); + expect(setSyncEnabled).not.toHaveBeenCalled(); + }); + + it('shows sync off for an account removed in Android Settings and never recreates it', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + h.state.accounts = []; + vi.mocked(native.ensureAndroidAccount).mockClear(); + await reconcileDeviceAccounts(); + const alice = accountDeviceSync(ALICE); + expect(alice.removedInAndroidSettings).toBe(true); + expect(alice.androidAccountName).toBeUndefined(); + expect(alice.enabled).toEqual({}); + await reconcileDeviceAccounts(); + expect(native.ensureAndroidAccount).not.toHaveBeenCalled(); + expect(h.state.accounts).toEqual([]); + }); + + it('forgets an account that is neither signed in nor on the device', async () => { + useDeviceSyncStore.getState().update('gone@mail.example.org', { androidAccountName: 'gone', enabled: { [CONTACTS_AUTHORITY]: true } }); + await reconcileDeviceAccounts(); + expect(useDeviceSyncStore.getState().accounts['gone@mail.example.org']).toBeUndefined(); + }); + + it('takes an authority turned on in Android Settings as on', async () => { + h.state.accounts.push({ + name: 'alice', + registryId: ALICE, + sync: { [CALENDAR_AUTHORITY]: { automatic: true, periodicSeconds: 3600 } }, + }); + await reconcileDeviceAccounts(); + const alice = accountDeviceSync(ALICE); + expect(alice.androidAccountName).toBe('alice'); + expect(alice.enabled).toEqual({ [CALENDAR_AUTHORITY]: true }); + }); + + it('hands the push router the accounts that feed each Android account', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + useDeviceSyncStore.getState().rememberPrimaryJmapAccount(ALICE, CONTACTS_AUTHORITY, 'c'); + await reconcileDeviceAccounts(); + expect(native.setPushRoutes).toHaveBeenLastCalledWith({ + c: [{ accountName: 'alice', authorities: [CONTACTS_AUTHORITY] }], + }); + }); +}); + +describe('sign-in', () => { + it('clears the sign-in notice and resumes a suspended account where it synced', async () => { + h.state.accounts.push({ + name: 'alice', + registryId: ALICE, + sync: { [CONTACTS_AUTHORITY]: { automatic: false, periodicSeconds: 3600 } }, + }); + useDeviceSyncStore.getState().update(ALICE, { + androidAccountName: 'alice', + suspended: true, + enabled: { [CONTACTS_AUTHORITY]: true }, + }); + await deviceSyncSignedIn(ALICE); + expect(h.state.log).toEqual([ + 'clear alice', + `auto alice ${CONTACTS_AUTHORITY} true`, + `sync alice ${CONTACTS_AUTHORITY} {"manual":true}`, + ]); + expect(accountDeviceSync(ALICE).suspended).toBeUndefined(); + }); + + it('does nothing for an account without an Android account', async () => { + await deviceSyncSignedIn(BOB); + expect(h.state.log).toEqual([]); + }); +}); + +describe('settings actions', () => { + it('applies a new interval to Android while the authority syncs', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + h.state.log = []; + await changeSyncInterval(ALICE, CONTACTS_AUTHORITY, 0); + expect(h.state.log).toEqual([`periodic alice ${CONTACTS_AUTHORITY} 0`]); + await changeSyncInterval(ALICE, CALENDAR_AUTHORITY, 900); + expect(h.state.log).toEqual([`periodic alice ${CONTACTS_AUTHORITY} 0`]); + expect(accountDeviceSync(ALICE).intervalSeconds[CALENDAR_AUTHORITY as Authority]).toBe(900); + }); + + it('syncs the calendar when the reminder owner changes', async () => { + await enableDeviceSync(ALICE, CALENDAR_AUTHORITY); + h.state.log = []; + await changeReminderOwner(ALICE, 'device'); + expect(h.state.log).toEqual([`sync alice ${CALENDAR_AUTHORITY} {"manual":true}`]); + h.state.log = []; + await changeReminderOwner(ALICE, 'device'); + expect(h.state.log).toEqual([]); + }); +}); diff --git a/src/device-sync/__tests__/app/triggers.test.ts b/src/device-sync/__tests__/app/triggers.test.ts new file mode 100644 index 00000000..ab057388 --- /dev/null +++ b/src/device-sync/__tests__/app/triggers.test.ts @@ -0,0 +1,285 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import AsyncStorage from '@react-native-async-storage/async-storage'; + +const h = vi.hoisted(() => ({ + available: true, + appState: [] as Array<(state: string) => void>, + accountsChanged: [] as Array<() => void>, +})); + +vi.mock('react-native', () => ({ + Platform: { OS: 'android', Version: 34, select: (s: { android?: T; default?: T }) => s.android ?? s.default }, + NativeModules: {}, + NativeEventEmitter: class { + addListener() { + return { remove: () => undefined }; + } + }, + AppState: { + currentState: 'active', + addEventListener: (_type: string, listener: (state: string) => void) => { + h.appState.push(listener); + return { remove: () => { h.appState = h.appState.filter((l) => l !== listener); } }; + }, + }, +})); + +vi.mock('../../native', () => ({ + isDeviceSyncAvailable: () => h.available, + requestSync: vi.fn(async () => undefined), + onAccountsChanged: vi.fn((listener: () => void) => { + h.accountsChanged.push(listener); + return () => { h.accountsChanged = h.accountsChanged.filter((l) => l !== listener); }; + }), +})); + +vi.mock('../../app/lifecycle', () => ({ + reconcileDeviceAccounts: vi.fn(async () => undefined), + refreshPushRoutes: vi.fn(async () => undefined), +})); + +import * as native from '../../native'; +import * as lifecycle from '../../app/lifecycle'; +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY } from '../../types'; +import { dispatchStateChange } from '../../../lib/state-change-bus'; +import { useAccountStore, type AccountEntry } from '../../../stores/account-store'; +import { useDeviceSyncStore, waitForDeviceSyncHydration } from '../../../stores/device-sync-store'; +import { cancelDeviceSyncRequests, requestDeviceSync, TRIGGER_DELAY_MS } from '../../app/request'; +import { + FOREGROUND_MIN_INTERVAL_MS, + handleForeground, + handleStateChange, + startDeviceSyncTriggers, +} from '../../app/triggers'; + +const ALICE = 'alice@mail.example.org'; +const BOB = 'bob@mail.example.org'; +const requestSync = vi.mocked(native.requestSync); + +function account(id: string): AccountEntry { + return { + id, + serverUrl: 'https://mail.example.org', + username: id.split('@')[0], + displayName: id, + email: id, + avatarColor: '#000', + lastLoginAt: 0, + isConnected: true, + hasError: false, + isDefault: false, + }; +} + +// Alice syncs contacts and calendars; Bob only calendars. +function syncing(): void { + useDeviceSyncStore.setState({ + accounts: { + [ALICE]: { + contactsSelection: {}, + calendarSelection: { 'team/cal': true }, + reminderOwner: null, + intervalSeconds: {}, + lastRun: {}, + androidAccountName: 'alice', + enabled: { [CONTACTS_AUTHORITY]: true, [CALENDAR_AUTHORITY]: true }, + }, + [BOB]: { + contactsSelection: {}, + calendarSelection: {}, + reminderOwner: null, + intervalSeconds: {}, + lastRun: {}, + androidAccountName: 'bob', + enabled: { [CALENDAR_AUTHORITY]: true }, + }, + }, + }); +} + +let stop: (() => void) | null = null; + +beforeEach(async () => { + await AsyncStorage.clear(); + await waitForDeviceSyncHydration(); + vi.clearAllMocks(); + vi.useFakeTimers(); + h.available = true; + cancelDeviceSyncRequests(); + useAccountStore.setState({ accounts: [account(ALICE), account(BOB)], activeAccountId: ALICE }); + syncing(); +}); + +afterEach(() => { + stop?.(); + stop = null; + cancelDeviceSyncRequests(); + vi.useRealTimers(); +}); + +describe('requestDeviceSync', () => { + it('makes one sync of the edits and echoes of 5 seconds', async () => { + requestDeviceSync(CONTACTS_AUTHORITY); + await vi.advanceTimersByTimeAsync(2000); + requestDeviceSync(CONTACTS_AUTHORITY, ALICE); + requestDeviceSync(CONTACTS_AUTHORITY, ALICE, { jmapAccountId: 'c', type: 'ContactCard', state: 's2' }); + expect(requestSync).not.toHaveBeenCalled(); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS - 2000); + expect(requestSync).toHaveBeenCalledTimes(1); + expect(requestSync).toHaveBeenCalledWith('alice', CONTACTS_AUTHORITY, {}); + + // The next change arms a new request. + requestDeviceSync(CONTACTS_AUTHORITY); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledTimes(2); + }); + + it('keeps accounts and authorities apart', async () => { + requestDeviceSync(CONTACTS_AUTHORITY, ALICE); + requestDeviceSync(CALENDAR_AUTHORITY, ALICE); + requestDeviceSync(CALENDAR_AUTHORITY, BOB); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync.mock.calls.map(([name, authority]) => `${name} ${authority}`).sort()).toEqual([ + `alice ${CALENDAR_AUTHORITY}`, + `alice ${CONTACTS_AUTHORITY}`, + `bob ${CALENDAR_AUTHORITY}`, + ]); + }); + + it('asks nothing for an authority the account does not sync, or without device sync', async () => { + requestDeviceSync(CONTACTS_AUTHORITY, BOB); + h.available = false; + requestDeviceSync(CALENDAR_AUTHORITY, BOB); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).not.toHaveBeenCalled(); + }); + + it('drops a request when sync was turned off meanwhile', async () => { + requestDeviceSync(CONTACTS_AUTHORITY, ALICE); + useDeviceSyncStore.getState().setEnabled(ALICE, CONTACTS_AUTHORITY, false); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).not.toHaveBeenCalled(); + }); + + it('can be cancelled', async () => { + requestDeviceSync(CONTACTS_AUTHORITY, ALICE); + requestDeviceSync(CALENDAR_AUTHORITY, ALICE); + cancelDeviceSyncRequests(ALICE, CONTACTS_AUTHORITY); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledTimes(1); + expect(requestSync).toHaveBeenCalledWith('alice', CALENDAR_AUTHORITY, {}); + }); +}); + +describe('StateChanges of the live stream', () => { + it('syncs the active account on a contact or calendar change', async () => { + handleStateChange(CALENDAR_AUTHORITY, 'CalendarEvent', 'c', 'e7'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledWith('alice', CALENDAR_AUTHORITY, {}); + }); + + it('skips the echo of what the engine synced itself', async () => { + useDeviceSyncStore.getState().recordKnownState(ALICE, 'c', 'ContactCard', 's9'); + handleStateChange(CONTACTS_AUTHORITY, 'ContactCard', 'c', 's9'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).not.toHaveBeenCalled(); + }); + + it('skips an echo that arrived before the engine recorded its state', async () => { + handleStateChange(CONTACTS_AUTHORITY, 'ContactCard', 'c', 's10'); + // The engine's upload returns and records the state it moved the account to. + useDeviceSyncStore.getState().recordKnownState(ALICE, 'c', 'ContactCard', 's10'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).not.toHaveBeenCalled(); + }); + + it('still syncs when an edit in the app joined the echo', async () => { + handleStateChange(CONTACTS_AUTHORITY, 'ContactCard', 'c', 's11'); + requestDeviceSync(CONTACTS_AUTHORITY); + useDeviceSyncStore.getState().recordKnownState(ALICE, 'c', 'ContactCard', 's11'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledTimes(1); + }); + + it('ignores shared accounts nothing is synced from, once the personal account is known', async () => { + handleStateChange(CALENDAR_AUTHORITY, 'CalendarEvent', 'other', 'x1'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + // Unknown yet: anything may feed the device. + expect(requestSync).toHaveBeenCalledTimes(1); + + useDeviceSyncStore.getState().rememberPrimaryJmapAccount(ALICE, CALENDAR_AUTHORITY, 'c'); + handleStateChange(CALENDAR_AUTHORITY, 'CalendarEvent', 'other', 'x2'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledTimes(1); + + // The shared account a calendar was picked in does feed it. + handleStateChange(CALENDAR_AUTHORITY, 'Calendar', 'team', 'x3'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledTimes(2); + }); + + it('does nothing for an authority the active account does not sync', async () => { + useAccountStore.setState({ activeAccountId: BOB }); + handleStateChange(CONTACTS_AUTHORITY, 'AddressBook', 'b', 'x'); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).not.toHaveBeenCalled(); + }); +}); + +describe('foreground', () => { + it('reconciles the accounts, then syncs everything that syncs, at most once a minute', async () => { + await handleForeground(1_000_000); + expect(lifecycle.reconcileDeviceAccounts).toHaveBeenCalledTimes(1); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync.mock.calls.map(([name, authority]) => `${name} ${authority}`).sort()).toEqual([ + `alice ${CALENDAR_AUTHORITY}`, + `alice ${CONTACTS_AUTHORITY}`, + `bob ${CALENDAR_AUTHORITY}`, + ]); + + await handleForeground(1_000_000 + FOREGROUND_MIN_INTERVAL_MS - 1); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledTimes(3); + + await handleForeground(1_000_000 + FOREGROUND_MIN_INTERVAL_MS); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledTimes(6); + }); +}); + +describe('startDeviceSyncTriggers', () => { + it('listens to the stream, the foreground, account changes and the preferences', async () => { + stop = startDeviceSyncTriggers(); + // Launch counts as coming to the foreground. + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(lifecycle.reconcileDeviceAccounts).toHaveBeenCalledTimes(1); + requestSync.mockClear(); + + dispatchStateChange({ '@type': 'StateChange', changed: { c: { AddressBook: 'a2', Email: 'm1' } } }); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(requestSync).toHaveBeenCalledWith('alice', CONTACTS_AUTHORITY, {}); + + for (const listener of h.appState) listener('background'); + for (const listener of h.appState) listener('active'); + await vi.advanceTimersByTimeAsync(0); + expect(lifecycle.reconcileDeviceAccounts).toHaveBeenCalledTimes(2); + + for (const listener of h.accountsChanged) listener(); + expect(lifecycle.reconcileDeviceAccounts).toHaveBeenCalledTimes(3); + + useDeviceSyncStore.getState().rememberPrimaryJmapAccount(ALICE, CONTACTS_AUTHORITY, 'c'); + await vi.advanceTimersByTimeAsync(1000); + expect(lifecycle.refreshPushRoutes).toHaveBeenCalled(); + + // Started once only. + expect(startDeviceSyncTriggers()).toBe(stop); + }); + + it('starts nothing without device sync', async () => { + h.available = false; + startDeviceSyncTriggers()(); + await vi.advanceTimersByTimeAsync(TRIGGER_DELAY_MS); + expect(lifecycle.reconcileDeviceAccounts).not.toHaveBeenCalled(); + expect(native.onAccountsChanged).not.toHaveBeenCalled(); + }); +}); diff --git a/src/device-sync/app/available.ts b/src/device-sync/app/available.ts new file mode 100644 index 00000000..dde03815 --- /dev/null +++ b/src/device-sync/app/available.ts @@ -0,0 +1,17 @@ +/** + * The gate of every app-side device sync feature (docs/device-sync.md, "App + * integration"): Android, and a build that carries the native module. iOS, + * and an Android build without the module, behave as if device sync did not + * exist. + */ +import { supportsDeviceSync } from '../../lib/platform-capabilities'; +import { isDeviceSyncAvailable } from '../native'; + +export function deviceSyncAvailable(): boolean { + if (!supportsDeviceSync) return false; + try { + return isDeviceSyncAvailable(); + } catch { + return false; + } +} diff --git a/src/device-sync/app/confirm.ts b/src/device-sync/app/confirm.ts new file mode 100644 index 00000000..ddd357cc --- /dev/null +++ b/src/device-sync/app/confirm.ts @@ -0,0 +1,40 @@ +/** + * The questions device sync asks outside the settings screen, as system + * alerts: signing out while changes made on the device have not reached the + * server (docs/device-sync.md, "Lifecycle"). + */ +import { Alert } from 'react-native'; +import { t } from '../../stores/locale-store'; + +/** + * Asks whether to sign out although `pending` device changes (unknown when + * the upload could not even be tried) would be lost. Resolves true to go on. + */ +export function confirmSignOutLosingChanges(pending: number): Promise { + return new Promise((resolve) => { + let settled = false; + const answer = (value: boolean) => { + if (settled) return; + settled = true; + resolve(value); + }; + Alert.alert( + t('device_sync.sign_out_pending_title', 'Sign out anyway?'), + pending > 0 + ? t( + 'device_sync.sign_out_pending_message', + "{count, plural, one {# change made on this device hasn't reached the server. It is lost if you sign out now.} other {# changes made on this device haven't reached the server. They are lost if you sign out now.}}", + { count: pending }, + ) + : t( + 'device_sync.sign_out_unknown_message', + "Changes made on this device may not have reached the server. They are lost if you sign out now.", + ), + [ + { text: t('common.cancel', 'Cancel'), style: 'cancel', onPress: () => answer(false) }, + { text: t('device_sync.sign_out_anyway', 'Sign out'), style: 'destructive', onPress: () => answer(true) }, + ], + { cancelable: true, onDismiss: () => answer(false) }, + ); + }); +} diff --git a/src/device-sync/app/lifecycle.ts b/src/device-sync/app/lifecycle.ts new file mode 100644 index 00000000..f87e9397 --- /dev/null +++ b/src/device-sync/app/lifecycle.ts @@ -0,0 +1,622 @@ +/** + * Turning device sync on and off, signing out, and keeping Android's accounts + * in step with the app's account registry (docs/device-sync.md, "Lifecycle"). + * + * - Enable: permissions (asked by the settings first) → the Android account + * → automatic sync on → the periodic sync → a manual sync → push routes and + * push types. + * - Disable an authority: automatic sync off, then the engine's teardown + * uploads what the device changed and deletes the authority's rows. When + * changes could not be uploaded nothing is deleted and the caller asks the + * user; only a confirmed turn-off deletes them. With both authorities off + * the Android account goes. + * - Sign-out: the same for every authority, before the credentials go. + * - Reconcile (launch, foreground, accounts changed): an Android account whose + * app account the registry dropped is suspended (sync off, one notification, + * data kept); an app account whose Android account was removed in Android + * Settings shows sync off and is never recreated on its own. + * + * The engine (`../task`) is imported on first use only, so starting the app + * does not load it. + */ +import { + clearSyncProblem, + ensureAndroidAccount, + getSyncSettings, + listAndroidAccounts, + openAccountSettings, + removeAndroidAccount, + requestSync, + setPeriodicSync, + setPushRoutes, + setSyncEnabled, + showSyncProblem, +} from '../native'; +import { + AUTHORITIES, + CALENDAR_AUTHORITY, + type AndroidAccount, + type Authority, + type ReminderOwner, +} from '../types'; +import { useAccountStore } from '../../stores/account-store'; +import { + accountDeviceSync, + anySyncOnInApp, + computePushRoutes, + intervalFor, + syncOnInApp, + useDeviceSyncStore, + waitForDeviceSyncHydration, + type AccountDeviceSync, +} from '../../stores/device-sync-store'; +import { t } from '../../stores/locale-store'; +import { toast } from '../../stores/toast-store'; +import { refreshPushSubscriptionTypes } from '../../lib/push-notifications'; +import { deviceSyncAvailable } from './available'; +import { confirmSignOutLosingChanges } from './confirm'; +import { hasSyncPermissions } from './permissions'; +import { STATE_TYPES_BY_AUTHORITY } from './prefs'; +import { cancelDeviceSyncRequests } from './request'; + +/** How long the teardown may spend uploading before it gives up (and asks). */ +export const TEARDOWN_TIMEOUT_MS = 30_000; + +/** After the user agreed to lose what is waiting: one short last try to upload it. */ +export const FORCED_TEARDOWN_TIMEOUT_MS = 5_000; + +/** Where the "sign in again" notification of a suspended account leads: the accounts pane. */ +export const SIGN_IN_AGAIN_URI = 'bulwarkmobile://settings/account'; + +const REGISTRY_HYDRATION_TIMEOUT_MS = 5000; + +const noop = () => undefined; + +function errorMessage(err: unknown): string { + return err instanceof Error && err.message ? err.message : String(err); +} + +/** The registry, once read; null when it could not be read in time. */ +async function readRegistry(): Promise['accounts'] | null> { + const persist = useAccountStore.persist; + if (!persist.hasHydrated()) { + const hydrated = await new Promise((resolve) => { + const timer = setTimeout(() => { + unsubscribe(); + resolve(false); + }, REGISTRY_HYDRATION_TIMEOUT_MS); + const unsubscribe = persist.onFinishHydration(() => { + clearTimeout(timer); + unsubscribe(); + resolve(true); + }); + }); + if (!hydrated) return null; + } + return useAccountStore.getState().accounts; +} + +// ─── Android account names ───────────────────────────── + +/** + * The Android account name for an app account: its login, or its registry id + * when another app account's Android account already has that name (the same + * login on two servers). + */ +export function preferredAndroidAccountName(registryId: string, androidAccounts: readonly AndroidAccount[]): string { + const login = useAccountStore.getState().getAccountById(registryId)?.username?.trim(); + if (!login) return registryId; + const takenOnDevice = androidAccounts.some((a) => a.name === login && a.registryId !== registryId); + const takenInApp = Object.entries(useDeviceSyncStore.getState().accounts) + .some(([id, entry]) => id !== registryId && entry.androidAccountName === login); + return takenOnDevice || takenInApp ? registryId : login; +} + +/** Creates the app account's Android account if needed; resolves its name. */ +async function ensureAccountFor(registryId: string): Promise { + const androidAccounts = await listAndroidAccounts(); + const own = androidAccounts.find((a) => a.registryId === registryId); + if (own) return own.name; + const name = preferredAndroidAccountName(registryId, androidAccounts); + try { + await ensureAndroidAccount(name, registryId); + return name; + } catch (err) { + // The name belongs to another app account after all (the list was stale). + if (name === registryId) throw err; + await ensureAndroidAccount(registryId, registryId); + return registryId; + } +} + +async function androidAccountOf(registryId: string): Promise { + try { + return (await listAndroidAccounts()).find((a) => a.registryId === registryId) ?? null; + } catch { + return null; + } +} + +// ─── Push ────────────────────────────────────────────── + +let lastRoutesJson: string | null = null; + +/** Hands the native push router the JMAP accounts that feed each Android account. */ +export async function refreshPushRoutes(): Promise { + if (!deviceSyncAvailable()) return; + await waitForDeviceSyncHydration(); + const routes = computePushRoutes(useDeviceSyncStore.getState().accounts); + const json = JSON.stringify(routes); + if (json === lastRoutesJson) return; + try { + await setPushRoutes(routes); + lastRoutesJson = json; + } catch { + // Kept for the next refresh. + } +} + +/** Push routes plus the account's push subscription types, after a toggle changed. */ +async function refreshPushState(registryId: string): Promise { + await refreshPushRoutes(); + await refreshPushSubscriptionTypes(registryId).catch(noop); +} + +// ─── Enable ──────────────────────────────────────────── + +export type EnableOutcome = + | { kind: 'enabled'; accountName: string } + /** The runtime permissions are missing: the caller asks for them first. */ + | { kind: 'permission' } + | { kind: 'failed'; message: string }; + +/** + * Turns device sync on for one authority of an app account. The caller has + * asked for the permissions (and, for calendars, who reminds the user). + */ +export async function enableDeviceSync(registryId: string, authority: Authority): Promise { + if (!deviceSyncAvailable()) return { kind: 'failed', message: 'Device sync is not available' }; + if (!(await hasSyncPermissions(authority))) return { kind: 'permission' }; + await waitForDeviceSyncHydration(); + let accountName: string | null = null; + try { + accountName = await ensureAccountFor(registryId); + useDeviceSyncStore.getState().update(registryId, { + androidAccountName: accountName, + removedInAndroidSettings: undefined, + suspended: undefined, + }); + await setSyncEnabled(accountName, authority, true); + await setPeriodicSync(accountName, authority, intervalFor(accountDeviceSync(registryId), authority)); + const entry = accountDeviceSync(registryId); + useDeviceSyncStore.getState().update(registryId, { enabled: { ...entry.enabled, [authority]: true } }); + await requestSync(accountName, authority, { manual: true }).catch(noop); + void refreshPushState(registryId); + return { kind: 'enabled', accountName }; + } catch (err) { + if (accountName) { + await setSyncEnabled(accountName, authority, false).catch(noop); + // Don't leave an account behind that syncs nothing. + if (!anySyncOnInApp(accountDeviceSync(registryId))) { + useDeviceSyncStore.getState().update(registryId, { androidAccountName: undefined }); + await removeAndroidAccount(accountName).catch(noop); + } + } + return { kind: 'failed', message: errorMessage(err) }; + } +} + +/** Automatic sync back on after the user kept syncing instead of losing changes. */ +export async function resumeDeviceSync(registryId: string, authority: Authority): Promise { + if (!deviceSyncAvailable()) return; + await waitForDeviceSyncHydration(); + const accountName = accountDeviceSync(registryId).androidAccountName ?? (await androidAccountOf(registryId))?.name; + if (!accountName) return; + await setSyncEnabled(accountName, authority, true).catch(noop); + await requestSync(accountName, authority, { manual: true }).catch(noop); +} + +// ─── Disable ─────────────────────────────────────────── + +export interface TurnOffResult { + /** Rows deleted and the authority off in the app. */ + done: boolean; + /** Device changes that could not be uploaded (nothing was deleted). */ + pending: number; + /** The teardown itself failed: whether changes are waiting is unknown. */ + failed?: boolean; +} + +interface TeardownResult { + pending: number; + failed: boolean; +} + +// The engine, loaded once on first use (sign-outs tear several accounts down +// at the same time). +let engine: Promise | null = null; + +function loadEngine(): Promise { + engine ??= import('../task').catch((err: unknown) => { + engine = null; + throw err; + }); + return engine; +} + +async function teardown( + registryId: string, + accountName: string, + authority: Authority, + force: boolean, +): Promise { + try { + const { teardownAuthority } = await loadEngine(); + const result = await teardownAuthority( + registryId, + accountName, + authority, + force + ? { force: true, timeoutMs: FORCED_TEARDOWN_TIMEOUT_MS } + : { uploadFirst: true, timeoutMs: TEARDOWN_TIMEOUT_MS }, + ); + return { pending: Math.max(0, result?.pending ?? 0), failed: false }; + } catch (err) { + console.warn('[device-sync] teardown failed', errorMessage(err)); + return { pending: 0, failed: true }; + } +} + +/** The authority's JMAP states, dropped with its rows so no old state passes for an echo. */ +function withoutAuthorityStates(entry: AccountDeviceSync, authority: Authority): AccountDeviceSync['knownStates'] { + if (!entry.knownStates) return undefined; + const types = STATE_TYPES_BY_AUTHORITY[authority]; + const out: Record> = {}; + for (const [jmapAccountId, states] of Object.entries(entry.knownStates)) { + const kept = Object.fromEntries(Object.entries(states ?? {}).filter(([type]) => !types.includes(type))); + if (Object.keys(kept).length > 0) out[jmapAccountId] = kept; + } + return Object.keys(out).length > 0 ? out : undefined; +} + +/** + * Turns device sync off for one authority. Uploads what the device changed + * first; when that is not possible, nothing is deleted and the result says + * how many changes are waiting: call again with `force` once the user agreed + * to lose them, or `resumeDeviceSync` to keep syncing. + */ +export async function disableDeviceSync( + registryId: string, + authority: Authority, + options: { force?: boolean } = {}, +): Promise { + if (!deviceSyncAvailable()) return { done: true, pending: 0 }; + await waitForDeviceSyncHydration(); + const store = useDeviceSyncStore.getState(); + const accountName = accountDeviceSync(registryId).androidAccountName ?? (await androidAccountOf(registryId))?.name; + cancelDeviceSyncRequests(registryId, authority); + if (!accountName) { + store.setEnabled(registryId, authority, false); + return { done: true, pending: 0 }; + } + await setSyncEnabled(accountName, authority, false).catch(noop); + const result = await teardown(registryId, accountName, authority, !!options.force); + if (result.failed || result.pending > 0) { + return { done: false, pending: result.pending, failed: result.failed || undefined }; + } + + const before = accountDeviceSync(registryId); + const { [authority]: _lastRun, ...lastRun } = before.lastRun; + useDeviceSyncStore.getState().update(registryId, { + enabled: { ...before.enabled, [authority]: false }, + lastRun, + knownStates: withoutAuthorityStates(before, authority), + // Asked again the next time calendar sync is turned on. + ...(authority === CALENDAR_AUTHORITY ? { reminderOwner: null } : {}), + }); + if (!anySyncOnInApp(accountDeviceSync(registryId))) { + // Forget the name first: the removal fires `accountsChanged`, and the + // reconcile must not take it for a removal in Android Settings. + useDeviceSyncStore.getState().update(registryId, { androidAccountName: undefined, knownStates: undefined }); + await removeAndroidAccount(accountName).catch(noop); + await clearSyncProblem(accountName).catch(noop); + } + void refreshPushState(registryId); + return { done: true, pending: 0 }; +} + +// ─── Sign-out ────────────────────────────────────────── + +/** + * Before app accounts sign out (`auth-store` logout, logoutAll, + * removeAccount): uploads what their devices changed, deletes the synced + * rows and removes their Android accounts, while the credentials are still + * there. When changes could not be uploaded, `confirm` asks whether to sign + * out anyway. Resolves false when the user chose to stay signed in; their + * accounts then keep syncing. + */ +export async function releaseDeviceSyncBeforeSignOut( + registryIds: readonly string[], + confirm: (pending: number) => Promise = confirmSignOutLosingChanges, +): Promise { + if (!deviceSyncAvailable() || registryIds.length === 0) return true; + try { + return await release(registryIds, confirm); + } catch (err) { + // Never keep the user from signing out over a bug here; an Android + // account left behind is suspended by the next reconcile. + console.warn('[device-sync] release before sign-out failed', errorMessage(err)); + return true; + } +} + +async function release( + registryIds: readonly string[], + confirm: (pending: number) => Promise, +): Promise { + await waitForDeviceSyncHydration(); + let androidAccounts: AndroidAccount[] = []; + try { + androidAccounts = await listAndroidAccounts(); + } catch { + androidAccounts = []; + } + const targets = registryIds.flatMap((registryId) => { + const accountName = androidAccounts.find((a) => a.registryId === registryId)?.name + ?? accountDeviceSync(registryId).androidAccountName; + return accountName ? [{ registryId, accountName }] : []; + }); + if (targets.length > 0) { + toast.info(t('device_sync.signing_out', 'Removing synced contacts and calendars from this device…')); + } + + // Accounts in parallel (each has its own server and run lock), the + // authorities of one account one after the other. + const leftovers: Array<{ registryId: string; accountName: string; authority: Authority }> = []; + let pending = 0; + await Promise.all(targets.map(async ({ registryId, accountName }) => { + cancelDeviceSyncRequests(registryId); + for (const authority of AUTHORITIES) { + await setSyncEnabled(accountName, authority, false).catch(noop); + const result = await teardown(registryId, accountName, authority, false); + if (result.failed || result.pending > 0) { + leftovers.push({ registryId, accountName, authority }); + pending += result.pending; + } + } + })); + + if (leftovers.length > 0) { + if (!(await confirm(pending))) { + // Staying signed in: sync goes on where it was on. + for (const { registryId, accountName } of targets) { + const entry = accountDeviceSync(registryId); + for (const authority of AUTHORITIES) { + if (syncOnInApp(entry, authority)) await setSyncEnabled(accountName, authority, true).catch(noop); + } + } + return false; + } + for (const { registryId, accountName, authority } of leftovers) { + await teardown(registryId, accountName, authority, true); + } + } + + // Forgotten before the removal: its `accountsChanged` must not read as a + // removal in Android Settings. + for (const registryId of registryIds) { + cancelDeviceSyncRequests(registryId); + useDeviceSyncStore.getState().forget(registryId); + } + for (const { accountName } of targets) { + await removeAndroidAccount(accountName).catch(noop); + await clearSyncProblem(accountName).catch(noop); + } + void refreshPushRoutes(); + return true; +} + +// ─── Sign-in and reconcile ───────────────────────────── + +/** + * After an app account signed in successfully: clears its "sign in again" + * notification, resumes a suspended account where it synced, and syncs what + * a failed sign-in held back. + */ +export async function deviceSyncSignedIn(registryId: string): Promise { + if (!deviceSyncAvailable()) return; + try { + await waitForDeviceSyncHydration(); + const account = await androidAccountOf(registryId); + if (!account) return; + await clearSyncProblem(account.name).catch(noop); + const entry = accountDeviceSync(registryId); + useDeviceSyncStore.getState().update(registryId, { androidAccountName: account.name, suspended: undefined }); + const resumed = accountDeviceSync(registryId); + for (const authority of AUTHORITIES) { + if (!syncOnInApp(resumed, authority)) continue; + if (entry.suspended) await setSyncEnabled(account.name, authority, true).catch(noop); + await requestSync(account.name, authority, { manual: true }).catch(noop); + } + await refreshPushRoutes(); + } catch { + // Best effort: the next reconcile catches up. + } +} + +/** An Android account whose app account the registry dropped: sync off, one notification, data kept. */ +async function suspendAccount(account: AndroidAccount & { registryId: string }): Promise { + const entry = useDeviceSyncStore.getState().accounts[account.registryId]; + if (entry?.suspended) return; + // Remember what synced, so signing in again resumes exactly that. + const enabled: Partial> = { ...entry?.enabled }; + try { + const settings = await getSyncSettings(account.name); + for (const authority of AUTHORITIES) { + if (settings.authorities?.[authority]?.automatic) enabled[authority] = true; + } + } catch { + // What the app knows will do. + } + for (const authority of AUTHORITIES) await setSyncEnabled(account.name, authority, false).catch(noop); + cancelDeviceSyncRequests(account.registryId); + useDeviceSyncStore.getState().update(account.registryId, { + androidAccountName: account.name, + enabled, + suspended: true, + }); + await showSyncProblem( + account.name, + t('device_sync.signed_out_title', 'Sign in to keep syncing'), + t( + 'device_sync.signed_out_text', + '{account} was signed out, so its contacts and calendars on this device no longer sync. Sign in again to resume. Changes made on this device are kept.', + { account: account.name }, + ), + SIGN_IN_AGAIN_URI, + t('device_sync.problem_channel', 'Sync problems'), + ).catch(noop); +} + +let reconciling: Promise | null = null; + +/** + * Brings the app's view in step with Android's accounts: at launch, on + * foreground and when accounts of our type change. Concurrent calls share + * one pass. + */ +export function reconcileDeviceAccounts(): Promise { + if (!deviceSyncAvailable()) return Promise.resolve(); + if (!reconciling) { + reconciling = reconcile() + .catch((err) => console.warn('[device-sync] reconcile failed', errorMessage(err))) + .finally(() => { + reconciling = null; + }); + } + return reconciling; +} + +async function reconcile(): Promise { + await waitForDeviceSyncHydration(); + const registry = await readRegistry(); + // Never act on an empty or unreadable registry: a failed read must not + // suspend or forget anything. + if (!registry || registry.length === 0) return; + const androidAccounts = await listAndroidAccounts(); + const registryIds = new Set(registry.map((a) => a.id)); + const store = () => useDeviceSyncStore.getState(); + + // Android accounts that are gone: removed in Android Settings (or by the + // system). Sync shows off and the app does not recreate them. + for (const [registryId, entry] of Object.entries(store().accounts)) { + const name = entry.androidAccountName; + if (!name || androidAccounts.some((a) => a.name === name && (a.registryId ?? registryId) === registryId)) continue; + cancelDeviceSyncRequests(registryId); + if (!registryIds.has(registryId)) { + // Neither signed in nor on the device any more: nothing left to keep. + store().forget(registryId); + continue; + } + store().update(registryId, { + androidAccountName: undefined, + enabled: {}, + suspended: undefined, + knownStates: undefined, + removedInAndroidSettings: anySyncOnInApp(entry) || entry.removedInAndroidSettings || undefined, + }); + void refreshPushSubscriptionTypes(registryId).catch(noop); + } + + for (const account of androidAccounts) { + const registryId = account.registryId; + if (!registryId) continue; + if (!registryIds.has(registryId)) { + await suspendAccount({ ...account, registryId }); + continue; + } + const entry = accountDeviceSync(registryId); + if (entry.suspended) { + // Signed in again through a path that did not report it. + await deviceSyncSignedIn(registryId); + continue; + } + // Android is the source of truth: an authority turned on in Android + // Settings counts as on in the app too. + const enabled = { ...entry.enabled }; + let changed = entry.androidAccountName !== account.name || !!entry.removedInAndroidSettings; + try { + const settings = await getSyncSettings(account.name); + for (const authority of AUTHORITIES) { + if (settings.authorities?.[authority]?.automatic && !enabled[authority]) { + enabled[authority] = true; + changed = true; + } + } + } catch { + // Keep what the app knows. + } + if (changed) { + store().update(registryId, { androidAccountName: account.name, enabled, removedInAndroidSettings: undefined }); + } + } + await refreshPushRoutes(); +} + +// ─── Settings actions ────────────────────────────────── + +/** The periodic sync interval (0 = manual only). */ +export async function changeSyncInterval(registryId: string, authority: Authority, seconds: number): Promise { + await waitForDeviceSyncHydration(); + useDeviceSyncStore.getState().setIntervalSeconds(registryId, authority, seconds); + const entry = accountDeviceSync(registryId); + if (!deviceSyncAvailable() || !entry.androidAccountName || !syncOnInApp(entry, authority)) return; + await setPeriodicSync(entry.androidAccountName, authority, intervalFor(entry, authority)).catch(noop); +} + +/** Who reminds the user of synced events; a change rewrites the device's reminders with a calendar sync. */ +export async function changeReminderOwner(registryId: string, owner: ReminderOwner): Promise { + await waitForDeviceSyncHydration(); + const before = accountDeviceSync(registryId).reminderOwner; + useDeviceSyncStore.getState().setReminderOwner(registryId, owner); + const entry = accountDeviceSync(registryId); + if (before === owner || !deviceSyncAvailable() || !entry.androidAccountName) return; + if (!syncOnInApp(entry, CALENDAR_AUTHORITY)) return; + await requestSync(entry.androidAccountName, CALENDAR_AUTHORITY, { manual: true }).catch(noop); +} + +/** A sync right away: manual and expedited. Resolves false when the account has no Android account. */ +export async function syncNow(registryId: string, authority: Authority): Promise { + const accountName = accountDeviceSync(registryId).androidAccountName; + if (!deviceSyncAvailable() || !accountName) return false; + await requestSync(accountName, authority, { manual: true, expedited: true }); + return true; +} + +/** Android's sync screen of the account ("Android account settings", "Review deletions"). */ +export async function openAndroidAccountSettings(registryId: string): Promise { + const accountName = accountDeviceSync(registryId).androidAccountName; + if (!deviceSyncAvailable() || !accountName) return false; + await openAccountSettings(accountName); + return true; +} + +/** + * Drops a suspended account (one the app signed out while it synced) from + * this device: its Android account and, with it, every synced row. Changes + * made on the device that never reached the server are lost; the caller + * asks first. + */ +export async function removeSuspendedAccount(registryId: string): Promise { + if (!deviceSyncAvailable()) return; + await waitForDeviceSyncHydration(); + const accountName = accountDeviceSync(registryId).androidAccountName ?? (await androidAccountOf(registryId))?.name; + cancelDeviceSyncRequests(registryId); + // Forgotten first, so the removal's `accountsChanged` finds nothing to mark. + useDeviceSyncStore.getState().forget(registryId); + if (accountName) { + await removeAndroidAccount(accountName).catch(noop); + await clearSyncProblem(accountName).catch(noop); + } + void refreshPushRoutes(); +} diff --git a/src/device-sync/app/permissions.ts b/src/device-sync/app/permissions.ts new file mode 100644 index 00000000..ff54d7ff --- /dev/null +++ b/src/device-sync/app/permissions.ts @@ -0,0 +1,54 @@ +/** + * Runtime permissions device sync needs (docs/device-sync.md, "Settings + * sections"): read and write access to contacts or calendars. Without them + * Android fails every sync before our code runs, so the settings check them + * themselves. + */ +import { Linking, PermissionsAndroid } from 'react-native'; +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, type Authority } from '../types'; + +type Permission = Parameters[0]; + +export const SYNC_PERMISSIONS: Record = { + [CONTACTS_AUTHORITY]: ['android.permission.READ_CONTACTS', 'android.permission.WRITE_CONTACTS'], + [CALENDAR_AUTHORITY]: ['android.permission.READ_CALENDAR', 'android.permission.WRITE_CALENDAR'], +}; + +/** + * `granted`: both permissions held. `denied`: the user said no; asking again + * shows the dialog again. `blocked`: Android no longer asks ("don't ask + * again", or denied twice), only the app's system settings can grant them. + */ +export type PermissionOutcome = 'granted' | 'denied' | 'blocked'; + +export async function hasSyncPermissions(authority: Authority): Promise { + try { + const held = await Promise.all(SYNC_PERMISSIONS[authority].map((p) => PermissionsAndroid.check(p))); + return held.every(Boolean); + } catch { + return false; + } +} + +export async function requestSyncPermissions(authority: Authority): Promise { + if (await hasSyncPermissions(authority)) return 'granted'; + const wanted = SYNC_PERMISSIONS[authority]; + try { + const results = (await PermissionsAndroid.requestMultiple([...wanted])) as Record; + const answers = wanted.map((p) => results[p]); + if (answers.every((a) => a === 'granted')) return 'granted'; + if (answers.some((a) => a === 'never_ask_again')) return 'blocked'; + return 'denied'; + } catch { + return 'denied'; + } +} + +/** The app's page in Android's settings, where blocked permissions are granted. */ +export async function openAppSystemSettings(): Promise { + try { + await Linking.openSettings(); + } catch { + // Nothing else to offer. + } +} diff --git a/src/device-sync/app/prefs.ts b/src/device-sync/app/prefs.ts new file mode 100644 index 00000000..12190c3d --- /dev/null +++ b/src/device-sync/app/prefs.ts @@ -0,0 +1,80 @@ +/** + * Pure helpers over the persisted device sync preferences, shared by the + * store (src/stores/device-sync-store.ts) and by code that must not create + * the store: the push setup reads the persisted JSON straight from + * AsyncStorage (docs/device-sync.md, "Triggers"). + * + * Types only; nothing here may import react-native or a store. + */ +import { + AUTHORITIES, + CALENDAR_AUTHORITY, + CONTACTS_AUTHORITY, + type Authority, +} from '../types'; + +/** AsyncStorage key of the device sync store (zustand `persist` name). */ +export const DEVICE_SYNC_STORAGE_KEY = 'device-sync:v1'; + +/** The fields that say whether an account syncs an authority. */ +export interface SyncSwitches { + /** Authorities the user turned on in the app. */ + enabled?: Partial>; + /** The Android account was removed in Android Settings. */ + removedInAndroidSettings?: boolean; + /** The app dropped the account from its registry; automatic sync is off until the user signs in again. */ + suspended?: boolean; +} + +/** + * Whether the app has device sync on for this authority: turned on in the + * app and not removed in Android Settings since. Android's own toggle can + * still pause it (see `getSyncAutomatically`). + */ +export function syncOnInApp(entry: SyncSwitches | undefined | null, authority: Authority): boolean { + return !!entry && !!entry.enabled?.[authority] && !entry.removedInAndroidSettings && !entry.suspended; +} + +/** JMAP data types whose changes a synced authority cares about. */ +export const STATE_TYPES_BY_AUTHORITY: Record = { + [CONTACTS_AUTHORITY]: ['ContactCard', 'AddressBook'], + [CALENDAR_AUTHORITY]: ['CalendarEvent', 'Calendar'], +}; + +/** The authority a JMAP data type belongs to, if any. */ +export function authorityOfType(type: string): Authority | null { + for (const authority of AUTHORITIES) { + if (STATE_TYPES_BY_AUTHORITY[authority].includes(type)) return authority; + } + return null; +} + +/** + * Push types device sync adds to an account's push subscription: + * `ContactCard`, `AddressBook` while contacts sync, `CalendarEvent`, + * `Calendar` while calendars sync. + */ +export function deviceSyncPushTypes(entry: SyncSwitches | undefined | null): string[] { + const types: string[] = []; + for (const authority of AUTHORITIES) { + if (syncOnInApp(entry, authority)) types.push(...STATE_TYPES_BY_AUTHORITY[authority]); + } + return types; +} + +/** + * The persisted accounts map from the store's JSON (`{ state: { accounts } }`), + * or an empty map when it is missing or unreadable. + */ +export function persistedAccounts(raw: string | null): Record { + if (!raw) return {}; + try { + const parsed = JSON.parse(raw) as { state?: { accounts?: unknown } } | null; + const accounts = parsed?.state?.accounts; + return accounts && typeof accounts === 'object' && !Array.isArray(accounts) + ? (accounts as Record) + : {}; + } catch { + return {}; + } +} diff --git a/src/device-sync/app/request.ts b/src/device-sync/app/request.ts new file mode 100644 index 00000000..4d24e891 --- /dev/null +++ b/src/device-sync/app/request.ts @@ -0,0 +1,92 @@ +/** + * Asks Android to sync one account's contacts or calendars because something + * changed while the app runs (docs/device-sync.md, "Triggers"): a StateChange + * on the live stream, the app's own contact or calendar edit, the app coming + * back to the foreground. + * + * Requests are coalesced per account and authority: the first one arms a + * 5-second timer and the rest ride along, so an edit and the StateChange + * echoing it make one sync. A request caused only by StateChanges is dropped + * when the timer fires if the engine has synced exactly those states in the + * meantime (its own uploads echo back). + * + * The request is an ordinary (not manual) one: Android drops it while the + * user has paused the account or the device's auto-sync is off. + */ +import { requestSync } from '../native'; +import type { Authority } from '../types'; +import { useAccountStore } from '../../stores/account-store'; +import { isKnownState, syncOnInApp, useDeviceSyncStore } from '../../stores/device-sync-store'; +import { deviceSyncAvailable } from './available'; + +export const TRIGGER_DELAY_MS = 5000; + +/** The StateChange that asked for a sync. */ +export interface StateReason { + jmapAccountId: string; + type: string; + state: string; +} + +interface Pending { + timer: ReturnType; + reasons: StateReason[]; + /** Asked for by something other than a StateChange: never skipped as an echo. */ + unconditional: boolean; +} + +const pending = new Map(); + +const slotKey = (registryId: string, authority: Authority) => `${registryId}\n${authority}`; + +/** + * Request a sync of `authority` for an app account (the active one when + * `registryId` is omitted), coalesced for 5 seconds. A no-op unless the + * account has device sync on for the authority. + */ +export function requestDeviceSync(authority: Authority, registryId?: string | null, reason?: StateReason): void { + if (!deviceSyncAvailable()) return; + const id = registryId ?? useAccountStore.getState().activeAccountId; + if (!id) return; + if (!syncOnInApp(useDeviceSyncStore.getState().accounts[id], authority)) return; + const key = slotKey(id, authority); + let slot = pending.get(key); + if (!slot) { + slot = { + timer: setTimeout(() => fire(key, id, authority), TRIGGER_DELAY_MS), + reasons: [], + unconditional: false, + }; + pending.set(key, slot); + } + if (reason) slot.reasons.push(reason); + else slot.unconditional = true; +} + +function fire(key: string, registryId: string, authority: Authority): void { + const slot = pending.get(key); + pending.delete(key); + if (!slot) return; + const entry = useDeviceSyncStore.getState().accounts[registryId]; + const accountName = entry?.androidAccountName; + if (!accountName || !syncOnInApp(entry, authority)) return; + const echoesOnly = !slot.unconditional + && slot.reasons.every((r) => isKnownState(registryId, r.jmapAccountId, r.type, r.state)); + if (echoesOnly) return; + void Promise.resolve() + .then(() => requestSync(accountName, authority, {})) + .catch(() => undefined); +} + +/** + * Drops waiting requests: of one authority of an account, of every authority + * of an account, or all of them. Used before sync is turned off. + */ +export function cancelDeviceSyncRequests(registryId?: string, authority?: Authority): void { + for (const [key, slot] of pending) { + if (registryId && authority && key !== slotKey(registryId, authority)) continue; + if (registryId && !key.startsWith(`${registryId}\n`)) continue; + clearTimeout(slot.timer); + pending.delete(key); + } +} diff --git a/src/device-sync/app/triggers.ts b/src/device-sync/app/triggers.ts new file mode 100644 index 00000000..73a29c6a --- /dev/null +++ b/src/device-sync/app/triggers.ts @@ -0,0 +1,119 @@ +/** + * What makes the running app ask Android for device syncs + * (docs/device-sync.md, "Triggers"), started once from the app's launch + * effect: + * + * - StateChanges on the live stream for `AddressBook`/`ContactCard` and + * `Calendar`/`CalendarEvent` (the stream serves the active account), except + * the states the engine synced itself; + * - the app returning to the foreground, and the launch itself: every account + * with sync on, after the accounts were reconciled with Android's; + * - accounts of our type changing (removed in Android Settings); + * - the preferences changing: the native push router's routes follow them. + * + * The app's own contact and calendar edits call `requestDeviceSync` from the + * stores. Every request is coalesced per account and authority (./request). + */ +import { AppState, type AppStateStatus } from 'react-native'; +import { onAccountsChanged } from '../native'; +import { AUTHORITIES, type Authority } from '../types'; +import { onStateChangeType } from '../../lib/state-change-bus'; +import { useAccountStore } from '../../stores/account-store'; +import { + feedingJmapAccounts, + isKnownState, + syncOnInApp, + useDeviceSyncStore, +} from '../../stores/device-sync-store'; +import { deviceSyncAvailable } from './available'; +import { reconcileDeviceAccounts, refreshPushRoutes } from './lifecycle'; +import { STATE_TYPES_BY_AUTHORITY } from './prefs'; +import { requestDeviceSync } from './request'; + +/** Foreground syncs of one account and authority at most this often. */ +export const FOREGROUND_MIN_INTERVAL_MS = 60_000; +const ROUTES_DELAY_MS = 1000; + +let stopTriggers: (() => void) | null = null; +const lastForegroundRequest = new Map(); + +/** A StateChange of the active account's stream. */ +export function handleStateChange(authority: Authority, type: string, jmapAccountId: string, state: string): void { + const registryId = useAccountStore.getState().activeAccountId; + if (!registryId) return; + const entry = useDeviceSyncStore.getState().accounts[registryId]; + if (!entry || !syncOnInApp(entry, authority)) return; + // The engine's own upload echoing back. + if (isKnownState(registryId, jmapAccountId, type, state)) return; + // A shared account nothing is synced from, once the app knows which + // accounts feed the device. + if (entry.primaryJmapAccounts?.[authority] && !feedingJmapAccounts(entry, authority).includes(jmapAccountId)) return; + requestDeviceSync(authority, registryId, { jmapAccountId, type, state }); +} + +/** Launch and every return to the foreground. */ +export async function handleForeground(now: number = Date.now()): Promise { + await reconcileDeviceAccounts(); + const { accounts } = useDeviceSyncStore.getState(); + for (const account of useAccountStore.getState().accounts) { + for (const authority of AUTHORITIES) { + if (!syncOnInApp(accounts[account.id], authority)) continue; + const key = `${account.id}\n${authority}`; + const last = lastForegroundRequest.get(key); + if (last !== undefined && now - last < FOREGROUND_MIN_INTERVAL_MS) continue; + lastForegroundRequest.set(key, now); + requestDeviceSync(authority, account.id); + } + } +} + +/** Starts the triggers; idempotent. Returns the stop function (for tests). */ +export function startDeviceSyncTriggers(): () => void { + if (stopTriggers) return stopTriggers; + if (!deviceSyncAvailable()) return () => undefined; + const cleanups: Array<() => void> = []; + + for (const authority of AUTHORITIES) { + for (const type of STATE_TYPES_BY_AUTHORITY[authority]) { + cleanups.push(onStateChangeType(type, (jmapAccountId, state) => { + handleStateChange(authority, type, jmapAccountId, state); + })); + } + } + + let appState: AppStateStatus = AppState.currentState; + const appStateSubscription = AppState.addEventListener('change', (next) => { + const cameBack = next === 'active' && appState !== 'active'; + appState = next; + if (cameBack) void handleForeground(); + }); + cleanups.push(() => appStateSubscription.remove()); + + try { + cleanups.push(onAccountsChanged(() => { void reconcileDeviceAccounts(); })); + } catch { + // Without the event, launch and foreground still reconcile. + } + + let routesTimer: ReturnType | null = null; + cleanups.push(useDeviceSyncStore.subscribe((state, prev) => { + if (state.accounts === prev.accounts) return; + if (routesTimer) clearTimeout(routesTimer); + routesTimer = setTimeout(() => { + routesTimer = null; + void refreshPushRoutes(); + }, ROUTES_DELAY_MS); + })); + cleanups.push(() => { + if (routesTimer) clearTimeout(routesTimer); + }); + + void handleForeground(); + + stopTriggers = () => { + for (const cleanup of cleanups) cleanup(); + stopTriggers = null; + lastForegroundRequest.clear(); + }; + return stopTriggers; +} diff --git a/src/lib/__tests__/push-background-task.test.ts b/src/lib/__tests__/push-background-task.test.ts index ee1c02ae..c8984ad9 100644 --- a/src/lib/__tests__/push-background-task.test.ts +++ b/src/lib/__tests__/push-background-task.test.ts @@ -15,6 +15,7 @@ import { NativeModules } from 'react-native'; import { jmapClient } from '../../api/jmap-client'; import { secureFetch } from '../client-cert'; import { + carriesNoMail, matchAccountsForPush, parseRelayPushData, pushBackgroundTask, @@ -186,6 +187,58 @@ describe('pushBackgroundTask notifications', () => { }); }); +describe('pushes for device sync (#34)', () => { + const LOCAL = 'alice@mail.example.com'; + const showNotification = vi.fn(async () => undefined); + const stateChange = (changed: Record>) => ({ + kind: 'jmap-state-change', + accountLabel: 'alice', + accountId: Object.keys(changed)[0], + emailIds: '[]', + changed: JSON.stringify(changed), + }); + + beforeEach(async () => { + vi.clearAllMocks(); + await AsyncStorage.clear(); + await AsyncStorage.setItem('push:accountIds:v1', JSON.stringify([LOCAL])); + await AsyncStorage.setItem('push:jmapAccountIds:v1', JSON.stringify({ [LOCAL]: 'jmap-primary' })); + (NativeModules as Record).BulwarkFcm = { showNotification }; + (jmapClient.getStoredCredentials as ReturnType).mockResolvedValue({ + serverUrl: 'https://mail.example.com', + username: 'alice', + password: 'secret', + }); + (secureFetch as ReturnType).mockResolvedValue({ ok: false, status: 500 }); + }); + + afterEach(() => { + delete (NativeModules as Record).BulwarkFcm; + }); + + it('tells contact and calendar changes from new mail', () => { + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { ContactCard: 's1', AddressBook: 's2' } })))).toBe(true); + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { CalendarEvent: 'e1' }, b: { Calendar: 'c1' } })))).toBe(true); + // Mail in any account of the map, an EmailPush, or a map it can't read: as before. + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { ContactCard: 's1' }, b: { EmailDelivery: 'm1' } })))).toBe(false); + expect(carriesNoMail(parseRelayPushData({ kind: 'jmap-email-push', accountId: 'a', emailIds: '["m1"]' }))).toBe(false); + expect(carriesNoMail(parseRelayPushData({ kind: 'jmap-state-change', emailIds: '[]', changed: '{}' }))).toBe(false); + expect(carriesNoMail(parseRelayPushData({ kind: 'jmap-state-change', emailIds: '[]' }))).toBe(false); + }); + + it('neither looks for mail nor notifies for a change without new mail', async () => { + await pushBackgroundTask(stateChange({ 'jmap-primary': { ContactCard: 's1' }, team: { CalendarEvent: 'e1' } })); + expect(jmapClient.getStoredCredentials).not.toHaveBeenCalled(); + expect(secureFetch).not.toHaveBeenCalled(); + expect(showNotification).not.toHaveBeenCalled(); + }); + + it('still checks for mail when the map carries new mail', async () => { + await pushBackgroundTask(stateChange({ 'jmap-primary': { EmailDelivery: 'm1', ContactCard: 's1' } })); + expect(jmapClient.getStoredCredentials).toHaveBeenCalledWith(LOCAL); + }); +}); + describe('selectNotifiableEmails', () => { const email = (id: string, keywords: Record = {}): Email => ({ id, threadId: 't', keywords, mailboxIds: {}, size: 0, receivedAt: '', hasAttachment: false } as Email); diff --git a/src/lib/__tests__/push-notifications.test.ts b/src/lib/__tests__/push-notifications.test.ts index a324a717..519b47ce 100644 --- a/src/lib/__tests__/push-notifications.test.ts +++ b/src/lib/__tests__/push-notifications.test.ts @@ -42,6 +42,12 @@ const { CREATED } = vi.hoisted(() => ({ CREATED: { id: 'new-server-id', expires: null as string | null }, })); +// A client of another signed-in account (device sync patches its push types +// without the singleton). +const { DETACHED } = vi.hoisted(() => ({ + DETACHED: { loaded: true, types: ['EmailDelivery'] as string[], log: [] as unknown[] }, +})); + vi.mock('../../api/jmap-client', () => ({ jmapClient: { username: 'user@example.com', @@ -49,6 +55,20 @@ vi.mock('../../api/jmap-client', () => ({ accountId: 'jmap-primary', currentSession: { capabilities: { 'urn:ietf:params:jmap:core': {} } }, }, + JMAPClient: class { + async loadAccount(id: string) { + DETACHED.log.push(['load', id]); + return DETACHED.loaded; + } + async request(calls: Array<[string, Record, string]>) { + const [name, args, id] = calls[0]; + DETACHED.log.push([name, args]); + if (name === 'PushSubscription/get') { + return { methodResponses: [[name, { list: [{ id: args.ids[0], types: DETACHED.types }] }, id]] }; + } + return { methodResponses: [[name, { updated: { [Object.keys(args.update)[0]]: null } }, id]] }; + } + }, })); vi.mock('../../api/email', () => ({ @@ -77,6 +97,8 @@ import { readPushAccountIds, readPushJmapAccountIds, PushSetupError, + pushTypesFor, + refreshPushSubscriptionTypes, resyncPushNotifications, revokePushDevice, teardownPushNotificationsForAccount, @@ -354,6 +376,117 @@ describe('setupPushNotifications subscription shape', () => { }); }); +describe('push types for device sync (#34)', () => { + const DEVICE_SYNC_KEY = 'device-sync:v1'; + const CONTACTS = 'com.android.contacts'; + const CALENDAR = 'com.android.calendar'; + const healthy = (types: string[]) => ({ + id: 'existing', + deviceClientId: OUR_DCID, + expires: new Date(Date.now() + 80 * 86400000).toISOString(), + types, + }); + + async function syncing(enabled: Record, extra: Record = {}): Promise { + await AsyncStorage.setItem(DEVICE_SYNC_KEY, JSON.stringify({ + state: { accounts: { [ACCOUNT_ID]: { enabled, ...extra } } }, + version: 0, + })); + } + + beforeEach(async () => { + vi.clearAllMocks(); + await AsyncStorage.clear(); + await AsyncStorage.setItem(deviceClientIdKey(ACCOUNT_ID), OUR_DCID); + (jmapClient as { currentSession: unknown }).currentSession = { + capabilities: { 'urn:ietf:params:jmap:core': {} }, + }; + listMock.mockResolvedValue([]); + updateMock.mockResolvedValue(true); + installFetch({}); + }); + + it('adds the contact and calendar types of what the account syncs to the device', async () => { + expect(await pushTypesFor(ACCOUNT_ID)).toEqual(['EmailDelivery']); + await syncing({ [CONTACTS]: true, [CALENDAR]: false }); + expect(await pushTypesFor(ACCOUNT_ID)).toEqual(['EmailDelivery', 'ContactCard', 'AddressBook']); + expect(await pushTypesFor('someone@else.example')).toEqual(['EmailDelivery']); + // Removed in Android Settings: nothing to route any more. + await syncing({ [CONTACTS]: true }, { removedInAndroidSettings: true }); + expect(await pushTypesFor(ACCOUNT_ID)).toEqual(['EmailDelivery']); + }); + + it('subscribes with them', async () => { + await syncing({ [CALENDAR]: true }); + await setupPushNotifications({ relayBaseUrl: RELAY }); + expect(createMock.mock.calls[0][0].types).toEqual(['EmailDelivery', 'CalendarEvent', 'Calendar']); + }); + + it('patches a subscription made before sync was turned on', async () => { + await AsyncStorage.setItem(SUB_KEY, 'existing'); + listMock.mockResolvedValue([healthy(['EmailDelivery'])]); + await syncing({ [CONTACTS]: true, [CALENDAR]: true }); + await setupPushNotifications({ relayBaseUrl: RELAY }); + expect(createMock).not.toHaveBeenCalled(); + expect(updateMock.mock.calls[0][1].types) + .toEqual(['EmailDelivery', 'ContactCard', 'AddressBook', 'CalendarEvent', 'Calendar']); + }); + + it('re-applies the types after a toggle', async () => { + await AsyncStorage.setItem(SUB_KEY, 'existing'); + listMock.mockResolvedValue([healthy(['EmailDelivery'])]); + await syncing({ [CONTACTS]: true }); + await refreshPushSubscriptionTypes(ACCOUNT_ID); + expect(updateMock).toHaveBeenCalledWith('existing', { types: ['EmailDelivery', 'ContactCard', 'AddressBook'] }); + + // An account without a push subscription has nothing to patch. + updateMock.mockClear(); + DETACHED.log = []; + await refreshPushSubscriptionTypes('someone@else.example'); + expect(updateMock).not.toHaveBeenCalled(); + expect(DETACHED.log).toEqual([]); + + // Already what it should be, in another order: nothing to send. + listMock.mockResolvedValue([healthy(['AddressBook', 'EmailDelivery', 'ContactCard'])]); + await refreshPushSubscriptionTypes(ACCOUNT_ID); + expect(updateMock).not.toHaveBeenCalled(); + }); + + it("patches another account's subscription through a client of its own", async () => { + const OTHER = 'bob@other.example.net'; + await AsyncStorage.setItem(`push:subscriptionId:v2:${OTHER}`, 'bob-sub'); + await AsyncStorage.setItem(DEVICE_SYNC_KEY, JSON.stringify({ + state: { accounts: { [OTHER]: { enabled: { [CALENDAR]: true } } } }, + version: 0, + })); + DETACHED.log = []; + DETACHED.types = ['EmailDelivery']; + await refreshPushSubscriptionTypes(OTHER); + expect(DETACHED.log).toEqual([ + ['load', OTHER], + ['PushSubscription/get', { ids: ['bob-sub'], properties: ['id', 'types'] }], + ['PushSubscription/set', { update: { 'bob-sub': { types: ['EmailDelivery', 'CalendarEvent', 'Calendar'] } } }], + ]); + // The singleton's subscriptions were not touched. + expect(updateMock).not.toHaveBeenCalled(); + + // Already right: read, nothing written. + DETACHED.log = []; + DETACHED.types = ['Calendar', 'CalendarEvent', 'EmailDelivery']; + await refreshPushSubscriptionTypes(OTHER); + expect(DETACHED.log.map((entry) => (entry as unknown[])[0])).toEqual(['load', 'PushSubscription/get']); + }); + + it('leaves the subscription of an account that turned push off alone', async () => { + await AsyncStorage.setItem(SUB_KEY, 'existing'); + await AsyncStorage.setItem(`push:optedOut:v1:${ACCOUNT_ID}`, '1'); + listMock.mockResolvedValue([healthy(['EmailDelivery'])]); + await syncing({ [CONTACTS]: true }); + await refreshPushSubscriptionTypes(ACCOUNT_ID); + expect(updateMock).not.toHaveBeenCalled(); + }); +}); + describe('setupPushNotifications with ACL-shared accounts (B18)', () => { const REFUSED_KEY = 'push:emailPushRefused:v1:' + ACCOUNT_ID; const sharedMock = getSharedMailboxes as ReturnType; diff --git a/src/lib/platform-capabilities.ts b/src/lib/platform-capabilities.ts index 785786c6..60489183 100644 --- a/src/lib/platform-capabilities.ts +++ b/src/lib/platform-capabilities.ts @@ -14,3 +14,15 @@ import { Platform } from 'react-native'; * check) is hidden when this is false. */ export const supportsSideloadUpdates = Platform.OS === 'android'; + +/** + * Device sync: address books and calendars in Android's Contacts and Calendar + * providers, under an account of the app's own type (#34, + * docs/device-sync.md). + * + * Android only: iOS has no third-party sync-provider API for either. The UI + * also needs the native module (`isDeviceSyncAvailable()` in + * `device-sync/native`), so a build without it hides the feature instead of + * failing. + */ +export const supportsDeviceSync = Platform.OS === 'android'; diff --git a/src/lib/push-background-task.ts b/src/lib/push-background-task.ts index dacd2778..776b39c4 100644 --- a/src/lib/push-background-task.ts +++ b/src/lib/push-background-task.ts @@ -378,12 +378,29 @@ async function detachedNewestUnreadInboxIds( return (qBody.ids as string[]) ?? []; } +/** + * A StateChange push in which no account got new mail: only contact or + * calendar changes, which the native push router already turned into device + * syncs (#34, docs/device-sync.md "Triggers"). Without `EmailDelivery` there + * is nothing to notify, and the legacy path (the newest unread mail) must + * not run for it. An empty or missing `changed` map keeps the old behaviour. + */ +export function carriesNoMail(payload: RelayPushData): boolean { + if (payload.kind !== 'jmap-state-change' || !payload.changed) return false; + const accounts = Object.values(payload.changed); + if (accounts.length === 0) return false; + return !accounts.some((types) => !!types && typeof types === 'object' && 'EmailDelivery' in types); +} + // Fired by BulwarkPushTaskService when a data FCM message arrives. Runs in a // short-lived headless JS runtime - keep it fast, catch all errors, and always // resolve so the native service can release its wake lock. Never touches the // singleton jmapClient's session (see "Detached JMAP access" above). export async function pushBackgroundTask(data: unknown): Promise { try { + const payload = parseRelayPushData(data); + if (carriesNoMail(payload)) return; + if (!(await emailNotificationsAllowed())) return; await migrateLegacyPushKeys(); @@ -392,7 +409,6 @@ export async function pushBackgroundTask(data: unknown): Promise { if (accountIds.length === 0) return; const registry = await readAccountRegistry(); - const payload = parseRelayPushData(data); const jmapAccountIds = await readPushJmapAccountIds(); const accountsToCheck = matchAccountsForPush(payload, accountIds, jmapAccountIds, registry); diff --git a/src/lib/push-notifications.ts b/src/lib/push-notifications.ts index e25a79b8..4151665b 100644 --- a/src/lib/push-notifications.ts +++ b/src/lib/push-notifications.ts @@ -9,12 +9,13 @@ import { } from '../api/push'; import { getMailboxes, getSharedMailboxes } from '../api/email'; import { loadedMailboxes } from './mailbox-source'; -import { jmapClient } from '../api/jmap-client'; -import { JMAPMethodError } from '../api/jmap-result'; +import { jmapClient, JMAPClient } from '../api/jmap-client'; +import { JMAPMethodError, requireMethodResult } from '../api/jmap-result'; import { CAPABILITIES } from '../api/types'; import type { EmailPushConfig, JMAPAccountInfo, Mailbox } from '../api/types'; import { generateAccountId } from './account-utils'; import { t } from '../stores/locale-store'; +import { DEVICE_SYNC_STORAGE_KEY, deviceSyncPushTypes, persistedAccounts } from '../device-sync/app/prefs'; import { getUnifiedPushDistributors, isUnifiedPushSupported, @@ -206,6 +207,25 @@ export function isValidRelayUrl(value: string): boolean { // In-app sync uses the separate SSE channel and is unaffected. export const PUSH_TYPES = ['EmailDelivery'] as const; +/** + * The push types of one account's subscription: `EmailDelivery`, plus the + * contact and calendar types while the account syncs them to the device + * (#34, docs/device-sync.md "Triggers"): the native push router turns those + * into device syncs, and a push carrying only them never notifies. + * + * Read from the device sync store's persisted JSON rather than the store, so + * code that runs without it (the push task) never creates it. + */ +export async function pushTypesFor(accountId: string): Promise { + let extra: string[] = []; + try { + extra = deviceSyncPushTypes(persistedAccounts(await AsyncStorage.getItem(DEVICE_SYNC_STORAGE_KEY))[accountId]); + } catch { + extra = []; + } + return [...PUSH_TYPES, ...extra]; +} + // draft-ietf-jmap-emailpush (Stalwart >= 0.16.16). `EmailDelivery` alone // still fires for every ingested message - including spam the server files // straight into Junk - because the server can't know which folders a client @@ -927,6 +947,7 @@ async function setupPushNotificationsInner( : null; const subKey = subscriptionIdKey(accountId); const storedServerId = await AsyncStorage.getItem(subKey); + const types = await pushTypesFor(accountId); let jmapAccountId: string | null = null; try { jmapAccountId = jmapClient.accountId; @@ -939,7 +960,7 @@ async function setupPushNotificationsInner( if (match) { if (match.expires) await AsyncStorage.setItem(subscriptionExpiresKey(accountId), match.expires); if (!params.forceRecreate) { - const refreshed = await refreshSubscriptionExpires(match, emailPush); + const refreshed = await refreshSubscriptionExpires(match, emailPush, types); if (refreshed) { await rememberRefusedEmailPushAccounts(accountId, refusedBefore, emailPush, refreshed.emailPush); await addPushAccountId(accountId); @@ -989,7 +1010,7 @@ async function setupPushNotificationsInner( createPushSubscription({ deviceClientId, url: buildRelayUrl(relayBaseUrl, `/api/push/jmap/${encodeURIComponent(deviceClientId)}`), - types: [...PUSH_TYPES], + types: [...types], expires: expiresFromNow(SUBSCRIPTION_EXPIRES_DAYS), ...(filter ? { emailPush: filter } : {}), }), @@ -1040,10 +1061,10 @@ async function addPushAccountId(accountId: string): Promise { // Push the subscription's expires forward when it's getting close to the // server's ceiling, and re-sync `types` / the delivery filter when they drift // from what this client wants (a subscription created by an older build still -// listens to `Email`/`Mailbox`; a Junk mailbox id can change under us). -// Returns false if the server rejects the update, which the caller treats as -// "replace"; otherwise the emailPush map the update installed (null when it -// left the filter alone). +// listens to `Email`/`Mailbox`; device sync was turned on or off; a Junk +// mailbox id can change under us). Returns false if the server rejects the +// update, which the caller treats as "replace"; otherwise the emailPush map +// the update installed (null when it left the filter alone). async function refreshSubscriptionExpires( sub: { id: string; @@ -1053,8 +1074,9 @@ async function refreshSubscriptionExpires( }, // null when the server has no emailPush support - leave the property alone. desiredEmailPush: Record | null, + types: readonly string[], ): Promise | null }> { - const typesNeedUpdate = !sameTypes(sub.types, PUSH_TYPES); + const typesNeedUpdate = !sameTypes(sub.types, types); const emailPushNeedsUpdate = desiredEmailPush !== null && !sameEmailPush(sub.emailPush, desiredEmailPush); if (!typesNeedUpdate && !emailPushNeedsUpdate && sub.expires) { @@ -1069,7 +1091,7 @@ async function refreshSubscriptionExpires( const patch: { expires?: string; types?: string[] } = { expires: expiresFromNow(SUBSCRIPTION_EXPIRES_DAYS), }; - if (typesNeedUpdate) patch.types = [...PUSH_TYPES]; + if (typesNeedUpdate) patch.types = [...types]; const { emailPush } = await writeWithEmailPush( emailPushNeedsUpdate ? desiredEmailPush : null, (filter) => updatePushSubscription(sub.id, filter ? { ...patch, emailPush: filter } : patch), @@ -1198,6 +1220,47 @@ export async function resyncPushNotifications( return setupPushNotifications(params); } +/** + * Re-apply an account's push types to its subscription after device sync was + * turned on or off for it (#34). The account the client serves goes through + * the api/push helpers; another signed-in account through a client of its + * own, never the singleton. Best effort: the next resync of the account + * applies them anyway. + */ +export async function refreshPushSubscriptionTypes(accountId: string): Promise { + if (await AsyncStorage.getItem(optedOutKey(accountId))) return; + const subscriptionId = await AsyncStorage.getItem(subscriptionIdKey(accountId)); + if (!subscriptionId) return; + const types = await pushTypesFor(accountId); + const username = jmapClient.username; + const serverUrl = jmapClient.serverUrl; + const served = !!username && !!serverUrl && generateAccountId(username, serverUrl) === accountId; + try { + if (served) { + const current = (await listPushSubscriptions()).find((s) => s.id === subscriptionId); + if (!current || sameTypes(current.types, types)) return; + await updatePushSubscription(subscriptionId, { types: [...types] }); + return; + } + const client = new JMAPClient(); + if (!(await client.loadAccount(accountId))) return; + const using = [CAPABILITIES.CORE]; + const res = await client.request( + [['PushSubscription/get', { ids: [subscriptionId], properties: ['id', 'types'] }, '0']], + using, + ); + const current = (requireMethodResult(res, '0', 'PushSubscription/get').list as Array<{ id: string; types?: string[] | null }> | undefined) + ?.find((s) => s.id === subscriptionId); + if (!current || sameTypes(current.types, types)) return; + await client.request( + [['PushSubscription/set', { update: { [subscriptionId]: { types: [...types] } } }, '0']], + using, + ); + } catch { + // The next resync re-applies them. + } +} + /** * Tear down push for ALL accounts on this device. Used by the logout-all * flow; best-effort because we typically aren't authenticated to every diff --git a/src/stores/__tests__/device-sync-store.test.ts b/src/stores/__tests__/device-sync-store.test.ts new file mode 100644 index 00000000..9484c300 --- /dev/null +++ b/src/stores/__tests__/device-sync-store.test.ts @@ -0,0 +1,268 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, type RunStatus } from '../../device-sync/types'; +import { + DEVICE_SYNC_STORAGE_KEY, + deviceSyncPushTypes, + persistedAccounts, + syncOnInApp, +} from '../../device-sync/app/prefs'; + +type StoreModule = typeof import('../device-sync-store'); + +// A fresh store module, hydrated from whatever AsyncStorage holds now. +async function freshStore(): Promise { + vi.resetModules(); + const mod = await import('../device-sync-store'); + await mod.waitForDeviceSyncHydration(); + return mod; +} + +async function stored(): Promise> { + const raw = await AsyncStorage.getItem(DEVICE_SYNC_STORAGE_KEY); + return raw ? JSON.parse(raw).state.accounts : {}; +} + +const ALICE = 'alice@mail.example.org'; +const BOB = 'bob@mail.example.org'; + +const run = (outcome: RunStatus['outcome']): RunStatus => ({ + at: 1, + outcome, + durationMs: 10, + conflicts: 0, + itemErrors: 0, + stats: { + downloaded: { created: 0, updated: 0, deleted: 0 }, + uploaded: { created: 0, updated: 0, deleted: 0 }, + entries: 0, + skipped: 0, + }, +}); + +beforeEach(async () => { + await AsyncStorage.clear(); +}); + +describe('selection', () => { + it("syncs the personal account's collections unless turned off and shared ones only when turned on", async () => { + const { isCollectionSelected } = await freshStore(); + expect(isCollectionSelected({}, CONTACTS_AUTHORITY, { key: 'c/b1', isPersonal: true })).toBe(true); + expect(isCollectionSelected({}, CONTACTS_AUTHORITY, { key: 'd/b9', isPersonal: false })).toBe(false); + expect(isCollectionSelected({ 'c/b1': false }, CONTACTS_AUTHORITY, { key: 'c/b1', isPersonal: true })).toBe(false); + expect(isCollectionSelected({ 'd/b9': true }, CONTACTS_AUTHORITY, { key: 'd/b9', isPersonal: false })).toBe(true); + // The webmail's allow-list book stays off unless the user picks it. + const trusted = { key: 'c/b2', isPersonal: true, name: 'Trusted Senders' }; + expect(isCollectionSelected({}, CONTACTS_AUTHORITY, trusted)).toBe(false); + expect(isCollectionSelected({ 'c/b2': true }, CONTACTS_AUTHORITY, trusted)).toBe(true); + expect(isCollectionSelected({}, CALENDAR_AUTHORITY, trusted)).toBe(true); + }); + + it('lists the collections that sync, per authority, from explicit choices and the default', async () => { + const m = await freshStore(); + const store = m.useDeviceSyncStore.getState(); + store.setCollectionSelected(ALICE, CONTACTS_AUTHORITY, 'c/b2', false); + store.setCollectionSelected(ALICE, CONTACTS_AUTHORITY, 'd/b9', true); + store.setCollectionSelected(ALICE, CALENDAR_AUTHORITY, 'c/cal1', false); + const entry = m.accountDeviceSync(ALICE); + const books = [ + { key: 'c/b1', isPersonal: true }, + { key: 'c/b2', isPersonal: true }, + { key: 'd/b9', isPersonal: false }, + { key: 'd/b8', isPersonal: false }, + ]; + expect(m.effectiveSelection(entry, CONTACTS_AUTHORITY, books)).toEqual(['c/b1', 'd/b9']); + // The calendar choice does not leak into the contacts selection. + expect(m.selectionFor(entry, CONTACTS_AUTHORITY)).toEqual({ 'c/b2': false, 'd/b9': true }); + expect(m.selectionFor(entry, CALENDAR_AUTHORITY)).toEqual({ 'c/cal1': false }); + }); + + it('gives the engine its preferences, with the calendar app reminding until the user chose', async () => { + const m = await freshStore(); + expect(m.accountSyncPrefs(ALICE)).toEqual({ + contactsSelection: {}, + calendarSelection: {}, + newContactsAddressBook: undefined, + reminderOwner: 'device', + }); + m.useDeviceSyncStore.getState().setReminderOwner(ALICE, 'bulwark'); + m.useDeviceSyncStore.getState().setNewContactsAddressBook(ALICE, 'c/b2'); + expect(m.accountSyncPrefs(ALICE)).toMatchObject({ reminderOwner: 'bulwark', newContactsAddressBook: 'c/b2' }); + m.useDeviceSyncStore.getState().setNewContactsAddressBook(ALICE, null); + expect(m.accountSyncPrefs(ALICE).newContactsAddressBook).toBeUndefined(); + }); +}); + +describe('actions', () => { + it('records what the user turned on in the app, apart from removals and suspensions', async () => { + const m = await freshStore(); + const store = m.useDeviceSyncStore.getState(); + store.setEnabled(ALICE, CONTACTS_AUTHORITY, true); + expect(syncOnInApp(m.accountDeviceSync(ALICE), CONTACTS_AUTHORITY)).toBe(true); + expect(syncOnInApp(m.accountDeviceSync(ALICE), CALENDAR_AUTHORITY)).toBe(false); + store.setRemovedInAndroidSettings(ALICE, true); + expect(syncOnInApp(m.accountDeviceSync(ALICE), CONTACTS_AUTHORITY)).toBe(false); + store.setRemovedInAndroidSettings(ALICE, false); + expect(m.accountDeviceSync(ALICE).removedInAndroidSettings).toBeUndefined(); + store.update(ALICE, { suspended: true }); + expect(m.anySyncOnInApp(m.accountDeviceSync(ALICE))).toBe(false); + }); + + it('keeps an interval per authority and defaults to an hour', async () => { + const m = await freshStore(); + const store = m.useDeviceSyncStore.getState(); + expect(m.intervalFor(m.accountDeviceSync(ALICE), CONTACTS_AUTHORITY)).toBe(m.DEFAULT_INTERVAL_SECONDS); + store.setIntervalSeconds(ALICE, CONTACTS_AUTHORITY, 900); + store.setIntervalSeconds(ALICE, CALENDAR_AUTHORITY, 0); + expect(m.intervalFor(m.accountDeviceSync(ALICE), CONTACTS_AUTHORITY)).toBe(900); + expect(m.intervalFor(m.accountDeviceSync(ALICE), CALENDAR_AUTHORITY)).toBe(0); + store.setIntervalSeconds(ALICE, CALENDAR_AUTHORITY, -5); + expect(m.intervalFor(m.accountDeviceSync(ALICE), CALENDAR_AUTHORITY)).toBe(0); + }); + + it('records run statuses and the states the engine synced, and knows its own echo', async () => { + const m = await freshStore(); + const store = m.useDeviceSyncStore.getState(); + store.recordRunStatus(ALICE, CONTACTS_AUTHORITY, run('ok')); + store.recordRunStatus(ALICE, CALENDAR_AUTHORITY, run('io')); + store.recordKnownState(ALICE, 'c', 'ContactCard', 's1'); + store.recordKnownState(ALICE, 'c', 'CalendarEvent', 'e1'); + const entry = m.accountDeviceSync(ALICE); + expect(entry.lastRun[CONTACTS_AUTHORITY]?.outcome).toBe('ok'); + expect(entry.lastRun[CALENDAR_AUTHORITY]?.outcome).toBe('io'); + expect(m.isKnownState(ALICE, 'c', 'ContactCard', 's1')).toBe(true); + expect(m.isKnownState(ALICE, 'c', 'ContactCard', 's2')).toBe(false); + expect(m.isKnownState(ALICE, 'd', 'ContactCard', 's1')).toBe(false); + expect(m.isKnownState(BOB, 'c', 'ContactCard', 's1')).toBe(false); + }); + + it('forgets an account entirely', async () => { + const m = await freshStore(); + m.useDeviceSyncStore.getState().setEnabled(ALICE, CONTACTS_AUTHORITY, true); + m.useDeviceSyncStore.getState().setEnabled(BOB, CONTACTS_AUTHORITY, true); + m.useDeviceSyncStore.getState().forget(ALICE); + expect(Object.keys(m.useDeviceSyncStore.getState().accounts)).toEqual([BOB]); + }); +}); + +describe('persistence', () => { + it('writes every change at once under device-sync:v1', async () => { + const m = await freshStore(); + m.useDeviceSyncStore.getState().setEnabled(ALICE, CALENDAR_AUTHORITY, true); + // No write delay: the change is in AsyncStorage before any timer runs. + await Promise.resolve(); + expect((await stored())[ALICE].enabled).toEqual({ [CALENDAR_AUTHORITY]: true }); + }); + + it('reads the stored preferences back on the next start', async () => { + let m = await freshStore(); + m.useDeviceSyncStore.getState().setCollectionSelected(ALICE, CONTACTS_AUTHORITY, 'd/b9', true); + m.useDeviceSyncStore.getState().update(ALICE, { androidAccountName: 'alice' }); + await Promise.resolve(); + m = await freshStore(); + const entry = m.accountDeviceSync(ALICE); + expect(entry.contactsSelection).toEqual({ 'd/b9': true }); + expect(entry.androidAccountName).toBe('alice'); + }); + + it('waits for the stored state before a headless writer may set anything', async () => { + await AsyncStorage.setItem(DEVICE_SYNC_STORAGE_KEY, JSON.stringify({ + state: { accounts: { [ALICE]: { contactsSelection: { 'c/b1': false }, calendarSelection: {}, reminderOwner: 'bulwark', intervalSeconds: {}, lastRun: {} } } }, + version: 0, + })); + vi.resetModules(); + const m = await import('../device-sync-store'); + await m.waitForDeviceSyncHydration(); + expect(m.useDeviceSyncStore.persist.hasHydrated()).toBe(true); + // A run status recorded after hydration keeps the stored preferences. + m.useDeviceSyncStore.getState().recordRunStatus(ALICE, CONTACTS_AUTHORITY, run('ok')); + await Promise.resolve(); + const entry = (await stored())[ALICE]; + expect(entry.contactsSelection).toEqual({ 'c/b1': false }); + expect(entry.reminderOwner).toBe('bulwark'); + expect(entry.lastRun[CONTACTS_AUTHORITY].outcome).toBe('ok'); + }); + + it('drops malformed entries and fields instead of failing to start', async () => { + await AsyncStorage.setItem(DEVICE_SYNC_STORAGE_KEY, JSON.stringify({ + state: { + accounts: { + [ALICE]: { + contactsSelection: 'nope', + reminderOwner: 'someone', + lastRun: [], + enabled: { [CONTACTS_AUTHORITY]: true }, + knownStates: { c: { ContactCard: 's1' }, d: 'broken' }, + primaryJmapAccounts: 7, + androidAccountName: 12, + }, + [BOB]: 42, + }, + }, + version: 0, + })); + const m = await freshStore(); + expect(Object.keys(m.useDeviceSyncStore.getState().accounts)).toEqual([ALICE]); + const entry = m.accountDeviceSync(ALICE); + expect(entry.contactsSelection).toEqual({}); + expect(entry.calendarSelection).toEqual({}); + expect(entry.reminderOwner).toBeNull(); + expect(entry.lastRun).toEqual({}); + expect(entry.enabled).toEqual({ [CONTACTS_AUTHORITY]: true }); + expect(entry.knownStates).toEqual({ c: { ContactCard: 's1' } }); + expect(entry.primaryJmapAccounts).toBeUndefined(); + expect(entry.androidAccountName).toBeUndefined(); + // Readers built on the entry keep working. + expect(m.computePushRoutes(m.useDeviceSyncStore.getState().accounts)).toEqual({}); + }); +}); + +describe('routes and push types', () => { + it('knows which JMAP accounts feed an authority', async () => { + const m = await freshStore(); + const store = m.useDeviceSyncStore.getState(); + store.rememberPrimaryJmapAccount(ALICE, CONTACTS_AUTHORITY, 'c'); + store.recordKnownState(ALICE, 'g', 'AddressBook', 'x'); + store.recordKnownState(ALICE, 'h', 'CalendarEvent', 'y'); + store.setCollectionSelected(ALICE, CONTACTS_AUTHORITY, 'd/b9', true); + store.setCollectionSelected(ALICE, CONTACTS_AUTHORITY, 'e/b1', false); + const entry = m.accountDeviceSync(ALICE); + expect(m.feedingJmapAccounts(entry, CONTACTS_AUTHORITY)).toEqual(['c', 'd', 'g']); + expect(m.feedingJmapAccounts(entry, CALENDAR_AUTHORITY)).toEqual(['h']); + }); + + it('routes every feeding JMAP account to the Android accounts that sync it', async () => { + const m = await freshStore(); + const store = m.useDeviceSyncStore.getState(); + store.update(ALICE, { + androidAccountName: 'alice', + enabled: { [CONTACTS_AUTHORITY]: true, [CALENDAR_AUTHORITY]: true }, + primaryJmapAccounts: { [CONTACTS_AUTHORITY]: 'c', [CALENDAR_AUTHORITY]: 'c' }, + }); + store.setCollectionSelected(ALICE, CALENDAR_AUTHORITY, 'team/cal', true); + // Bob syncs nothing yet, a removed account and a suspended one route nowhere. + store.update(BOB, { androidAccountName: 'bob', enabled: {} }); + store.update('carol@x', { androidAccountName: 'carol', enabled: { [CONTACTS_AUTHORITY]: true }, removedInAndroidSettings: true, primaryJmapAccounts: { [CONTACTS_AUTHORITY]: 'k' } }); + store.update('dave@x', { androidAccountName: 'dave', enabled: { [CONTACTS_AUTHORITY]: true }, suspended: true, primaryJmapAccounts: { [CONTACTS_AUTHORITY]: 'k' } }); + expect(m.computePushRoutes(m.useDeviceSyncStore.getState().accounts)).toEqual({ + c: [{ accountName: 'alice', authorities: [CONTACTS_AUTHORITY, CALENDAR_AUTHORITY] }], + team: [{ accountName: 'alice', authorities: [CALENDAR_AUTHORITY] }], + }); + }); + + it('adds the contact and calendar push types while they sync', () => { + expect(deviceSyncPushTypes(undefined)).toEqual([]); + expect(deviceSyncPushTypes({ enabled: { [CONTACTS_AUTHORITY]: true } })).toEqual(['ContactCard', 'AddressBook']); + expect(deviceSyncPushTypes({ enabled: { [CONTACTS_AUTHORITY]: true, [CALENDAR_AUTHORITY]: true } })) + .toEqual(['ContactCard', 'AddressBook', 'CalendarEvent', 'Calendar']); + expect(deviceSyncPushTypes({ enabled: { [CALENDAR_AUTHORITY]: true }, removedInAndroidSettings: true })).toEqual([]); + }); + + it('reads the persisted accounts without the store, tolerating garbage', () => { + expect(persistedAccounts(null)).toEqual({}); + expect(persistedAccounts('{not json')).toEqual({}); + expect(persistedAccounts(JSON.stringify({ state: { accounts: [] } }))).toEqual({}); + expect(persistedAccounts(JSON.stringify({ state: { accounts: { [ALICE]: { enabled: {} } } } }))) + .toEqual({ [ALICE]: { enabled: {} } }); + }); +}); diff --git a/src/stores/calendar-store.ts b/src/stores/calendar-store.ts index bfac097a..3bdcbbe4 100644 --- a/src/stores/calendar-store.ts +++ b/src/stores/calendar-store.ts @@ -47,6 +47,8 @@ import { } from '../lib/recurrence-instances'; import { findTasksOnlyCalendarIds, isTaskLikeObject } from '../lib/calendar-component-detection'; import { mergeRangeEvents, mergedSpanLimit, planRangeLoad, sameRange } from '../lib/calendar-range-cache'; +import { requestDeviceSync } from '../device-sync/app/request'; +import { CALENDAR_AUTHORITY } from '../device-sync/types'; // extendRange merges up to twice the requested range (at least half a year, // at most about three years); past that it starts over with the request. @@ -777,6 +779,7 @@ export const useCalendarStore = create()( ? expandRecurringEvents([mapped], loadedRange.after, loadedRange.before) : [mapped]; set({ events: [...get().events, ...inserted] }); + requestDeviceSync(CALENDAR_AUTHORITY); return full; }, @@ -818,6 +821,7 @@ export const useCalendarStore = create()( set({ events: get().events.map((e) => (e.id === id ? { ...e, ...(changes as Partial) } : e)), }); + requestDeviceSync(CALENDAR_AUTHORITY); // A series mutation (an occurrence override, a truncated/changed rule, a // master edit) touches every expanded sibling, and the optimistic merge // above only updated the tapped one; server-expanded occurrences also @@ -853,6 +857,7 @@ export const useCalendarStore = create()( && !(seriesIdOf(e) === realId && (e.accountId ?? undefined) === (accountId ?? undefined))), }); } + requestDeviceSync(CALENDAR_AUTHORITY); if (touchesSeries) { await get().refresh(); } @@ -915,6 +920,7 @@ export const useCalendarStore = create()( : undefined; await apiRsvpEvent(realId, participantId, status, repair, accountId); } + requestDeviceSync(CALENDAR_AUTHORITY); set({ events: get().events.map((e) => { if (e.id !== eventId || !e.participants?.[participantId]) return e; @@ -996,7 +1002,10 @@ export const useCalendarStore = create()( } } finally { // Also when a chunk was refused: show what did get in. - if (count > 0 || linked > 0) await get().refresh(); + if (count > 0 || linked > 0) { + requestDeviceSync(CALENDAR_AUTHORITY); + await get().refresh(); + } } if (count + linked === 0 && refused.length > 0) throw new ImportRefusedError(refused); return { imported: count + linked, refused }; @@ -1005,6 +1014,7 @@ export const useCalendarStore = create()( createCalendar: async (name, color, description) => { const created = await apiCreateCalendar(name, color, description); set({ calendars: [...get().calendars, created] }); + requestDeviceSync(CALENDAR_AUTHORITY); return created; }, @@ -1014,6 +1024,7 @@ export const useCalendarStore = create()( set({ calendars: get().calendars.map((c) => (c.id === id ? { ...c, ...updates } as Calendar : c)), }); + requestDeviceSync(CALENDAR_AUTHORITY); }, removeCalendar: async (id) => { @@ -1025,12 +1036,15 @@ export const useCalendarStore = create()( tasks: get().tasks.filter((t) => !t.calendarIds?.[id]), hiddenCalendarIds: get().hiddenCalendarIds.filter((x) => x !== id), }); + requestDeviceSync(CALENDAR_AUTHORITY); }, clearCalendarEvents: async (id) => { const cal = get().calendars.find((c) => c.id === id); try { - return await apiClearCalendarEvents(cal?.originalId || id, cal?.accountId); + const cleared = await apiClearCalendarEvents(cal?.originalId || id, cal?.accountId); + requestDeviceSync(CALENDAR_AUTHORITY); + return cleared; } finally { // Also after a refused batch: earlier batches may have gone through. await get().refresh(); diff --git a/src/stores/contacts-store.ts b/src/stores/contacts-store.ts index b4a588fb..366f399f 100644 --- a/src/stores/contacts-store.ts +++ b/src/stores/contacts-store.ts @@ -33,6 +33,8 @@ import { matchesContactSearch, } from '../lib/contact-utils'; import { sanitizeDisplayName, splitMailbox } from '../lib/rfc5322-mailbox'; +import { requestDeviceSync } from '../device-sync/app/request'; +import { CONTACTS_AUTHORITY } from '../device-sync/types'; const SELECTED_CATEGORY_STORAGE_KEY = 'webmail:contacts:category:v1'; @@ -403,6 +405,7 @@ export const useContactsStore = create()( const { originalId, accountId, book } = bookTarget(addressBookId); const created = tagCreated(await apiCreateContact(contact, originalId, accountId), book); set({ contacts: [...get().contacts, created] }); + requestDeviceSync(CONTACTS_AUTHORITY); return created; }, @@ -422,12 +425,14 @@ export const useContactsStore = create()( return merged; }), }); + requestDeviceSync(CONTACTS_AUTHORITY); }, deleteContact: async (id) => { const { originalId, accountId } = contactTarget(id); await apiDeleteContacts([originalId], accountId); set({ contacts: cleanGroupMembers(get().contacts, new Set([id])).filter((c) => c.id !== id) }); + requestDeviceSync(CONTACTS_AUTHORITY); }, bulkDelete: async (ids) => { @@ -460,6 +465,7 @@ export const useContactsStore = create()( contacts: cleanGroupMembers(get().contacts, removed).filter((c) => !removed.has(c.id)), ...(failed > 0 ? { error: `Failed to delete ${failed} contact${failed === 1 ? '' : 's'}` } : {}), }); + if (removed.size > 0) requestDeviceSync(CONTACTS_AUTHORITY); }, importContacts: async (incoming, addressBookId) => { @@ -478,6 +484,7 @@ export const useContactsStore = create()( console.warn('[contacts-store] import failed for one contact', err); } } + if (imported > 0) requestDeviceSync(CONTACTS_AUTHORITY); return { imported, failed }; }, @@ -544,6 +551,7 @@ export const useContactsStore = create()( set({ contacts: get().contacts.map((c) => (c.id === id ? created : c)) }); } } + requestDeviceSync(CONTACTS_AUTHORITY); }, addContactsToGroup: async (groupId, contactIds) => { @@ -602,6 +610,7 @@ export const useContactsStore = create()( if (!trimmed) throw new Error('Address book name is required'); const book = await apiCreateAddressBook(trimmed); set({ addressBooks: [...get().addressBooks, book] }); + requestDeviceSync(CONTACTS_AUTHORITY); return book; }, @@ -613,6 +622,7 @@ export const useContactsStore = create()( set({ addressBooks: get().addressBooks.map((b) => (b.id === id ? { ...b, name: trimmed } : b)), }); + requestDeviceSync(CONTACTS_AUTHORITY); }, deleteAddressBook: async (id) => { @@ -624,6 +634,7 @@ export const useContactsStore = create()( addressBooks: get().addressBooks.filter((b) => b.id !== id), contacts: get().contacts.filter((c) => !c.addressBookIds?.[id]), }); + requestDeviceSync(CONTACTS_AUTHORITY); const category = get().selectedCategory; if (category.type === 'addressBook' && category.addressBookId === id) { get().setSelectedCategory({ type: 'all' }); @@ -799,6 +810,7 @@ export const useContactsStore = create()( bookId, ); set({ trustedSenderEmails: [...get().trustedSenderEmails, email] }); + requestDeviceSync(CONTACTS_AUTHORITY); }, removeFromTrustedSendersBook: async (input) => { @@ -810,6 +822,7 @@ export const useContactsStore = create()( c.emails && Object.values(c.emails).some((e) => e.address?.toLowerCase().trim() === email)); if (matches.length > 0) await apiDeleteContacts(matches.map((m) => m.id)); set({ trustedSenderEmails: get().trustedSenderEmails.filter((e) => e !== email) }); + if (matches.length > 0) requestDeviceSync(CONTACTS_AUTHORITY); }, isTrustedAddressBookSender: (email) => diff --git a/src/stores/device-sync-store.ts b/src/stores/device-sync-store.ts index 2350fb4d..bfa98ce4 100644 --- a/src/stores/device-sync-store.ts +++ b/src/stores/device-sync-store.ts @@ -2,9 +2,11 @@ * Device sync preferences and status per app account (Android only, #34; * docs/device-sync.md, "App integration"). Keyed by `AccountEntry.id`. * - * Whether sync is on is not stored here: Android's sync settings + * Whether sync runs is not stored here: Android's sync settings * (`getSyncAutomatically` per authority) are the source of truth, so the - * toggles in Settings → Accounts and the app always agree. + * toggles in Settings → Accounts and the app always agree. `enabled` only + * records what the user turned on in the app, which tells "paused in Android + * settings" (on here, off there) from "off". * * Persisted under `device-sync:v1` without the write delay the caches use: a * headless sync run may be the only thing alive, and its status must survive @@ -15,13 +17,23 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { createPersistStorage } from './persist-storage'; -import type { - AccountSyncPrefs, - Authority, - CollectionKey, - ReminderOwner, - RunStatus, +import { + AUTHORITIES, + CONTACTS_AUTHORITY, + type AccountSyncPrefs, + type Authority, + type CollectionKey, + type ReminderOwner, + type RunStatus, } from '../device-sync/types'; +import { + DEVICE_SYNC_STORAGE_KEY, + STATE_TYPES_BY_AUTHORITY, + syncOnInApp, +} from '../device-sync/app/prefs'; +import { collectionDefaultOn } from '../device-sync/engine/selection'; + +export { syncOnInApp }; /** Periodic sync interval when the user has not picked one: push covers freshness. */ export const DEFAULT_INTERVAL_SECONDS = 3600; @@ -47,6 +59,16 @@ export interface AccountDeviceSync { * our own echo and triggers no sync. */ knownStates?: Record>; + /** Authorities the user turned on in the app (Android may still pause them). */ + enabled?: Partial>; + /** + * The app dropped the account from its registry (its sign-in failed) while + * it synced: automatic sync was turned off and the device data kept until + * the user signs in again. + */ + suspended?: boolean; + /** The JMAP account holding the personal collections, per authority, as last seen. */ + primaryJmapAccounts?: Partial>; } export function emptyAccountDeviceSync(): AccountDeviceSync { @@ -65,53 +87,114 @@ interface DeviceSyncState { recordRunStatus: (registryId: string, authority: Authority, status: RunStatus) => void; recordKnownState: (registryId: string, jmapAccountId: string, type: string, state: string) => void; forget: (registryId: string) => void; + + /** Turns an authority on or off in the app (not in Android). */ + setEnabled: (registryId: string, authority: Authority, on: boolean) => void; + /** An explicit choice for one address book or calendar. */ + setCollectionSelected: (registryId: string, authority: Authority, key: CollectionKey, on: boolean) => void; + /** Where contacts created on the device go; null = the server's default book. */ + setNewContactsAddressBook: (registryId: string, key: CollectionKey | null) => void; + setIntervalSeconds: (registryId: string, authority: Authority, seconds: number) => void; + setReminderOwner: (registryId: string, owner: ReminderOwner | null) => void; + setRemovedInAndroidSettings: (registryId: string, removed: boolean) => void; + setAndroidAccountName: (registryId: string, name: string | undefined) => void; + rememberPrimaryJmapAccount: (registryId: string, authority: Authority, jmapAccountId: string) => void; +} + +function selectionField(authority: Authority): 'contactsSelection' | 'calendarSelection' { + return authority === CONTACTS_AUTHORITY ? 'contactsSelection' : 'calendarSelection'; +} + +// Persisted entries are trusted only as far as their shape: a field of the +// wrong type falls back to the empty default instead of crashing a reader. +function sanitizeEntry(value: unknown): AccountDeviceSync | null { + if (!value || typeof value !== 'object' || Array.isArray(value)) return null; + const v = value as Record; + const isRecord = (x: unknown): x is Record => !!x && typeof x === 'object' && !Array.isArray(x); + const record = (x: unknown) => (isRecord(x) ? x : {}); + const optionalRecord = (x: unknown) => (isRecord(x) ? x : undefined); + const knownStates = isRecord(v.knownStates) + ? Object.fromEntries(Object.entries(v.knownStates).filter(([, states]) => isRecord(states))) + : undefined; + return { + ...(v as Partial), + contactsSelection: record(v.contactsSelection) as Record, + calendarSelection: record(v.calendarSelection) as Record, + reminderOwner: v.reminderOwner === 'device' || v.reminderOwner === 'bulwark' ? v.reminderOwner : null, + intervalSeconds: record(v.intervalSeconds) as Partial>, + lastRun: record(v.lastRun) as Partial>, + androidAccountName: typeof v.androidAccountName === 'string' && v.androidAccountName ? v.androidAccountName : undefined, + enabled: optionalRecord(v.enabled) as AccountDeviceSync['enabled'], + knownStates: knownStates as AccountDeviceSync['knownStates'], + primaryJmapAccounts: optionalRecord(v.primaryJmapAccounts) as AccountDeviceSync['primaryJmapAccounts'], + }; } export const useDeviceSyncStore = create()( persist( - (set) => ({ - accounts: {}, - update: (registryId, patch) => - set((s) => ({ - accounts: { - ...s.accounts, - [registryId]: { ...(s.accounts[registryId] ?? emptyAccountDeviceSync()), ...patch }, - }, - })), - recordRunStatus: (registryId, authority, status) => + (set) => { + // Applies `change` to one account's entry, creating it when missing. + const edit = (registryId: string, change: (current: AccountDeviceSync) => Partial) => set((s) => { const current = s.accounts[registryId] ?? emptyAccountDeviceSync(); - return { - accounts: { - ...s.accounts, - [registryId]: { ...current, lastRun: { ...current.lastRun, [authority]: status } }, - }, - }; - }), - recordKnownState: (registryId, jmapAccountId, type, state) => - set((s) => { - const current = s.accounts[registryId] ?? emptyAccountDeviceSync(); - const known = current.knownStates ?? {}; - return { - accounts: { - ...s.accounts, - [registryId]: { - ...current, - knownStates: { ...known, [jmapAccountId]: { ...(known[jmapAccountId] ?? {}), [type]: state } }, - }, - }, - }; - }), - forget: (registryId) => - set((s) => { - const { [registryId]: _gone, ...rest } = s.accounts; - return { accounts: rest }; - }), - }), + return { accounts: { ...s.accounts, [registryId]: { ...current, ...change(current) } } }; + }); + + return { + accounts: {}, + update: (registryId, patch) => edit(registryId, () => patch), + recordRunStatus: (registryId, authority, status) => + edit(registryId, (current) => ({ lastRun: { ...current.lastRun, [authority]: status } })), + recordKnownState: (registryId, jmapAccountId, type, state) => + edit(registryId, (current) => { + const known = current.knownStates ?? {}; + return { + knownStates: { ...known, [jmapAccountId]: { ...(known[jmapAccountId] ?? {}), [type]: state } }, + }; + }), + forget: (registryId) => + set((s) => { + const { [registryId]: _gone, ...rest } = s.accounts; + return { accounts: rest }; + }), + + setEnabled: (registryId, authority, on) => + edit(registryId, (current) => ({ enabled: { ...current.enabled, [authority]: on } })), + setCollectionSelected: (registryId, authority, key, on) => + edit(registryId, (current) => { + const field = selectionField(authority); + return { [field]: { ...current[field], [key]: on } }; + }), + setNewContactsAddressBook: (registryId, key) => + edit(registryId, () => ({ newContactsAddressBook: key ?? undefined })), + setIntervalSeconds: (registryId, authority, seconds) => + edit(registryId, (current) => ({ + intervalSeconds: { ...current.intervalSeconds, [authority]: Math.max(0, Math.round(seconds)) }, + })), + setReminderOwner: (registryId, owner) => edit(registryId, () => ({ reminderOwner: owner })), + setRemovedInAndroidSettings: (registryId, removed) => + edit(registryId, () => ({ removedInAndroidSettings: removed || undefined })), + setAndroidAccountName: (registryId, name) => edit(registryId, () => ({ androidAccountName: name })), + rememberPrimaryJmapAccount: (registryId, authority, jmapAccountId) => + edit(registryId, (current) => ({ + primaryJmapAccounts: { ...current.primaryJmapAccounts, [authority]: jmapAccountId }, + })), + }; + }, { - name: 'device-sync:v1', + name: DEVICE_SYNC_STORAGE_KEY, storage: createPersistStorage({ writeDelayMs: 0 }), partialize: (state) => ({ accounts: state.accounts }), + merge: (persisted, current) => { + const stored = (persisted as { accounts?: unknown } | undefined)?.accounts; + if (!stored || typeof stored !== 'object' || Array.isArray(stored)) return current; + const accounts: Record = {}; + for (const [id, value] of Object.entries(stored as Record)) { + const entry = sanitizeEntry(value); + if (entry) accounts[id] = entry; + } + return { ...current, accounts }; + }, }, ), ); @@ -127,9 +210,14 @@ export function waitForDeviceSyncHydration(): Promise { }); } +/** One account's entry, or the empty default. */ +export function accountDeviceSync(registryId: string): AccountDeviceSync { + return useDeviceSyncStore.getState().accounts[registryId] ?? emptyAccountDeviceSync(); +} + /** The engine's view of one account's preferences. */ export function accountSyncPrefs(registryId: string): AccountSyncPrefs { - const a = useDeviceSyncStore.getState().accounts[registryId] ?? emptyAccountDeviceSync(); + const a = accountDeviceSync(registryId); return { contactsSelection: a.contactsSelection, calendarSelection: a.calendarSelection, @@ -137,3 +225,111 @@ export function accountSyncPrefs(registryId: string): AccountSyncPrefs { reminderOwner: a.reminderOwner ?? 'device', }; } + +// ─── Selection ───────────────────────────────────────── + +/** The explicit choices for an authority's collections. */ +export function selectionFor(entry: AccountDeviceSync, authority: Authority): Record { + return entry[selectionField(authority)] ?? {}; +} + +/** What the default rule needs to know about a collection. */ +export interface SelectableCollection { + key: CollectionKey; + isPersonal: boolean; + name?: string | null; +} + +/** + * Whether a collection syncs: the explicit choice when there is one, else the + * engine's default (`collectionDefaultOn`): the personal account's + * collections on, shared ones and the Trusted Senders address book off. + */ +export function isCollectionSelected( + selection: Record, + authority: Authority, + collection: SelectableCollection, +): boolean { + const explicit = selection[collection.key]; + return typeof explicit === 'boolean' ? explicit : collectionDefaultOn(collection.isPersonal, authority, collection.name); +} + +/** The keys of the collections that sync, out of those the server lists. */ +export function effectiveSelection( + entry: AccountDeviceSync, + authority: Authority, + collections: ReadonlyArray, +): CollectionKey[] { + const selection = selectionFor(entry, authority); + return collections.filter((c) => isCollectionSelected(selection, authority, c)).map((c) => c.key); +} + +/** The periodic sync interval of an authority in seconds (0 = manual only). */ +export function intervalFor(entry: AccountDeviceSync, authority: Authority): number { + const seconds = entry.intervalSeconds?.[authority]; + return typeof seconds === 'number' && Number.isFinite(seconds) && seconds >= 0 ? seconds : DEFAULT_INTERVAL_SECONDS; +} + +// ─── Echoes and routes ───────────────────────────────── + +/** True when the engine already synced exactly this state (a StateChange that is our own echo). */ +export function isKnownState(registryId: string, jmapAccountId: string, type: string, state: string): boolean { + return useDeviceSyncStore.getState().accounts[registryId]?.knownStates?.[jmapAccountId]?.[type] === state; +} + +/** + * The JMAP accounts whose data feeds this account's Android account for an + * authority, as far as the app knows: the personal one, every account the + * engine synced a state of, and every account a collection was explicitly + * picked in. + */ +export function feedingJmapAccounts(entry: AccountDeviceSync, authority: Authority): string[] { + const out = new Set(); + const primary = entry.primaryJmapAccounts?.[authority]; + if (primary) out.add(primary); + const types = STATE_TYPES_BY_AUTHORITY[authority]; + for (const [jmapAccountId, states] of Object.entries(entry.knownStates ?? {})) { + if (states && types.some((type) => type in states)) out.add(jmapAccountId); + } + for (const [key, on] of Object.entries(selectionFor(entry, authority))) { + const slash = key.indexOf('/'); + if (on && slash > 0) out.add(key.slice(0, slash)); + } + return [...out].sort(); +} + +export interface PushRoute { + accountName: string; + authorities: Authority[]; +} + +/** + * Routes for the native push router: every JMAP account id that feeds an + * Android account, with the account and the authorities to sync. + */ +export function computePushRoutes(accounts: Record): Record { + const routes: Record = {}; + for (const registryId of Object.keys(accounts).sort()) { + const entry = accounts[registryId]; + const accountName = entry.androidAccountName; + if (!accountName) continue; + for (const authority of AUTHORITIES) { + if (!syncOnInApp(entry, authority)) continue; + for (const jmapAccountId of feedingJmapAccounts(entry, authority)) { + const list = (routes[jmapAccountId] ??= []); + let route = list.find((r) => r.accountName === accountName); + if (!route) { + route = { accountName, authorities: [] }; + list.push(route); + } + if (!route.authorities.includes(authority)) route.authorities.push(authority); + } + } + } + return routes; +} + +/** Whether any authority of the account is on in the app. */ +export function anySyncOnInApp(entry: AccountDeviceSync | undefined): boolean { + return AUTHORITIES.some((authority) => syncOnInApp(entry, authority)); +} From eb8b33160c82b54b9995024e932e956e4cd525b6 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 04:38:34 +0200 Subject: [PATCH 022/334] feat: remove synced contacts and calendars from the device on sign-out #34 Signing out of an account (or of all of them, or removing one) now first uploads what the device changed in its synced contacts and calendars, then deletes them and removes its Android account, while the credentials still work. When changes could not be uploaded (offline, server errors) the app asks before signing out and losing them; staying signed in keeps the account syncing. Several accounts are released in parallel, and an internal error never keeps anyone from signing out: the next check against Android's accounts stops what was left from syncing. A successful sign-in clears the account's "Sign in again" notification and resumes an account that stopped syncing when it was signed out. Checked: `npm run typecheck` and `npm test` on this commit (the release and resume paths are covered by the lifecycle tests of e67e240). --- src/stores/auth-store.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/stores/auth-store.ts b/src/stores/auth-store.ts index 67bf018a..b4e28c56 100644 --- a/src/stores/auth-store.ts +++ b/src/stores/auth-store.ts @@ -19,6 +19,7 @@ import { teardownPushNotifications, teardownPushNotificationsForAccount, } from '../lib/push-notifications'; +import { deviceSyncSignedIn, releaseDeviceSyncBeforeSignOut } from '../device-sync/app/lifecycle'; // Persist middleware hydrates asynchronously on cold start. Without this // guard, restoreSession() can read the account-store before AsyncStorage has @@ -245,6 +246,8 @@ async function completeOAuthHandoff( // mounted; the screen joins this load. void useEmailStore.getState().fetchMailboxes(); void syncAccountDisplayName(accountId); + // Device sync (#34): drop a "sign in again" notice, resume a suspended account. + void deviceSyncSignedIn(accountId); } function applyConnectedState( @@ -323,6 +326,8 @@ export const useAuthStore = create((set, get) => ({ // mounted; the screen joins this load. void useEmailStore.getState().fetchMailboxes(); void syncAccountDisplayName(accountId); + // Device sync (#34): drop a "sign in again" notice, resume a suspended account. + void deviceSyncSignedIn(accountId); } catch (err) { if (err instanceof Error && err.name === 'TotpRequiredError') { // Keep what the user (or the webmail hand-off) supplied so the code @@ -463,6 +468,11 @@ export const useAuthStore = create((set, get) => ({ const accountStore = useAccountStore.getState(); const currentId = get().activeAccountId; + // Device sync (#34): upload what this device changed and remove the + // Android account while the credentials are still here. When changes + // could not be uploaded the user is asked, and may stay signed in. + if (currentId && !(await releaseDeviceSyncBeforeSignOut([currentId]))) return; + // Best-effort: revoke this account's JMAP PushSubscription and drop its // relay mapping before we lose credentials. Other logged-in accounts' // push setups remain untouched. Do not abort logout on failure. @@ -513,6 +523,8 @@ export const useAuthStore = create((set, get) => ({ logoutAll: async () => { const accountStore = useAccountStore.getState(); const ids = accountStore.accounts.map((a) => a.id); + // Device sync (#34): as in logout, for every account. + if (!(await releaseDeviceSyncBeforeSignOut(ids))) return; await teardownPushNotifications().catch(() => undefined); for (const id of ids) await revokeStoredRefreshToken(id); await jmapClient.clearAllCredentials(ids); @@ -629,6 +641,8 @@ export const useAuthStore = create((set, get) => ({ } const accountStore = useAccountStore.getState(); if (!accountStore.getAccountById(accountId)) return; + // Device sync (#34): as in logout. + if (!(await releaseDeviceSyncBeforeSignOut([accountId]))) return; await teardownPushNotificationsForAccount(accountId).catch(() => undefined); await revokeStoredRefreshToken(accountId); await jmapClient.clearAccountCredentials(accountId).catch(() => undefined); From 1c574a5a934bbb31a33a9419f4fd432d579a9f31 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 04:39:53 +0200 Subject: [PATCH 023/334] feat: leave reminders of synced events to the device's calendar app when asked #34 When an account's calendars sync to the device and the user chose the device's calendar app to remind them, that app gets the events' reminders. Bulwark's own reminder scheduler now skips the events of those calendars, which would otherwise remind the user twice. Tasks never sync and keep Bulwark's reminders, and a change of who reminds, or of which calendars sync, reschedules right away. Checked: the reminder filter and scheduler tests, `npm run typecheck` and `npm test` on this commit. --- .../__tests__/app/reminders.test.ts | 104 ++++++++++++++++++ src/device-sync/app/reminders.ts | 79 +++++++++++++ .../__tests__/calendar-notifications.test.ts | 55 +++++++++ src/lib/calendar-notifications.ts | 20 +++- 4 files changed, 257 insertions(+), 1 deletion(-) create mode 100644 src/device-sync/__tests__/app/reminders.test.ts create mode 100644 src/device-sync/app/reminders.ts diff --git a/src/device-sync/__tests__/app/reminders.test.ts b/src/device-sync/__tests__/app/reminders.test.ts new file mode 100644 index 00000000..744081c4 --- /dev/null +++ b/src/device-sync/__tests__/app/reminders.test.ts @@ -0,0 +1,104 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +const h = vi.hoisted(() => ({ available: true })); + +vi.mock('../../native', () => ({ + isDeviceSyncAvailable: () => h.available, +})); + +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY } from '../../types'; +import type { CalendarEvent } from '../../../api/types'; +import { useDeviceSyncStore, waitForDeviceSyncHydration } from '../../../stores/device-sync-store'; +import { onDeviceRemindersChange, remindedOnDevice } from '../../app/reminders'; + +const ALICE = 'alice@mail.example.org'; + +function event(id: string, calendarIds: Record): CalendarEvent { + return { id, calendarIds, title: id, start: '2026-09-24T12:00:00', duration: 'PT1H' } as CalendarEvent; +} + +function calendarSync(extra: Record = {}): void { + useDeviceSyncStore.setState({ + accounts: { + [ALICE]: { + contactsSelection: {}, + calendarSelection: { 'c/work': false, 'team/shared-on': true }, + reminderOwner: 'device', + intervalSeconds: {}, + lastRun: {}, + androidAccountName: 'alice', + enabled: { [CALENDAR_AUTHORITY]: true }, + ...extra, + }, + }, + }); +} + +beforeEach(async () => { + await waitForDeviceSyncHydration(); + h.available = true; + calendarSync(); +}); + +describe('remindedOnDevice', () => { + it("picks the events of the account's synced calendars", () => { + const onDevice = remindedOnDevice(ALICE, 'c')!; + // Own calendars sync unless turned off. + expect(onDevice(event('a', { personal: true }))).toBe(true); + expect(onDevice(event('b', { work: true }))).toBe(false); + expect(onDevice(event('c', { work: true, personal: true }))).toBe(true); + // Shared calendars: namespaced ids in calendarIds, raw ones in originalCalendarIds. + expect(onDevice({ + ...event('d', { 'team:shared-on': true }), + accountId: 'team', + originalCalendarIds: { 'shared-on': true }, + })).toBe(true); + expect(onDevice({ + ...event('e', { 'team:other': true }), + accountId: 'team', + originalCalendarIds: { other: true }, + })).toBe(false); + }); + + it('leaves everything to Bulwark when Bulwark reminds, or calendars do not sync', () => { + calendarSync({ reminderOwner: 'bulwark' }); + expect(remindedOnDevice(ALICE, 'c')).toBeNull(); + calendarSync({ enabled: { [CONTACTS_AUTHORITY]: true } }); + expect(remindedOnDevice(ALICE, 'c')).toBeNull(); + calendarSync({ removedInAndroidSettings: true }); + expect(remindedOnDevice(ALICE, 'c')).toBeNull(); + expect(remindedOnDevice('someone@else', 'c')).toBeNull(); + expect(remindedOnDevice(null, 'c')).toBeNull(); + }); + + it('counts the calendar app as the owner until the user chose', () => { + calendarSync({ reminderOwner: null }); + expect(remindedOnDevice(ALICE, 'c')?.(event('a', { personal: true }))).toBe(true); + }); + + it('does nothing where device sync is not available', () => { + h.available = false; + expect(remindedOnDevice(ALICE, 'c')).toBeNull(); + }); +}); + +describe('onDeviceRemindersChange', () => { + it('fires for the owner and the calendar selection, not for sync bookkeeping', () => { + const listener = vi.fn(); + const off = onDeviceRemindersChange(listener); + const store = useDeviceSyncStore.getState(); + + store.recordKnownState(ALICE, 'c', 'CalendarEvent', 's1'); + store.setIntervalSeconds(ALICE, CALENDAR_AUTHORITY, 900); + expect(listener).not.toHaveBeenCalled(); + + store.setReminderOwner(ALICE, 'bulwark'); + expect(listener).toHaveBeenCalledTimes(1); + store.setReminderOwner(ALICE, 'device'); + store.setCollectionSelected(ALICE, CALENDAR_AUTHORITY, 'c/work', true); + expect(listener).toHaveBeenCalledTimes(3); + off(); + store.setEnabled(ALICE, CALENDAR_AUTHORITY, false); + expect(listener).toHaveBeenCalledTimes(3); + }); +}); diff --git a/src/device-sync/app/reminders.ts b/src/device-sync/app/reminders.ts new file mode 100644 index 00000000..023bac16 --- /dev/null +++ b/src/device-sync/app/reminders.ts @@ -0,0 +1,79 @@ +/** + * Who reminds the user of synced events (docs/device-sync.md, "Reminder + * owner"). When an account's calendars sync to the device and the calendar + * app owns the reminders, the device fires them from its Reminders rows, so + * Bulwark's own scheduler skips the events of those calendars. Tasks are not + * synced and keep Bulwark's reminders. + */ +import type { CalendarEvent } from '../../api/types'; +import { + isCollectionSelected, + selectionFor, + syncOnInApp, + useDeviceSyncStore, + type AccountDeviceSync, +} from '../../stores/device-sync-store'; +import { collectionKey } from '../common/ids'; +import { CALENDAR_AUTHORITY } from '../types'; +import { deviceSyncAvailable } from './available'; + +/** Whether the device's calendar app fires the reminders of an account's synced events. */ +function deviceOwnsReminders(entry: AccountDeviceSync | undefined): entry is AccountDeviceSync { + // Unasked means the calendar app, as the engine writes Reminders rows then. + return !!entry && syncOnInApp(entry, CALENDAR_AUTHORITY) && (entry.reminderOwner ?? 'device') === 'device'; +} + +/** + * A test for the events whose reminders the device's calendar app fires for + * this app account, so Bulwark schedules none; null when there are none. + * `primaryJmapAccountId`: the JMAP account the app's calendar store loads + * events from when they carry no `accountId` (the user's own). + */ +export function remindedOnDevice( + registryId: string | null | undefined, + primaryJmapAccountId: string | null | undefined, +): ((event: CalendarEvent) => boolean) | null { + if (!registryId || !deviceSyncAvailable()) return null; + const entry = useDeviceSyncStore.getState().accounts[registryId]; + if (!deviceOwnsReminders(entry)) return null; + const selection = selectionFor(entry, CALENDAR_AUTHORITY); + const personal = entry.primaryJmapAccounts?.[CALENDAR_AUTHORITY] ?? primaryJmapAccountId ?? null; + return (event) => { + // Events of shared calendars carry their account and a namespaced copy + // of calendarIds; the raw ids are in originalCalendarIds. + const jmapAccountId = event.accountId ?? primaryJmapAccountId; + if (!jmapAccountId) return false; + const calendarIds = event.originalCalendarIds ?? event.calendarIds ?? {}; + return Object.entries(calendarIds).some(([calendarId, member]) => + !!member && isCollectionSelected(selection, CALENDAR_AUTHORITY, { + key: collectionKey(jmapAccountId, calendarId), + isPersonal: jmapAccountId === personal, + })); + }; +} + +// What decides which events the device reminds of, as one comparable string. +function reminderSignature(accounts: Record): string { + const parts: unknown[] = []; + for (const registryId of Object.keys(accounts).sort()) { + const entry = accounts[registryId]; + if (!deviceOwnsReminders(entry)) continue; + parts.push([registryId, entry.calendarSelection, entry.primaryJmapAccounts?.[CALENDAR_AUTHORITY] ?? null]); + } + return JSON.stringify(parts); +} + +/** + * Calls `listener` whenever the set of events the device reminds of may have + * changed (reminder owner, calendar sync on or off, the calendar selection). + * Returns the unsubscribe. + */ +export function onDeviceRemindersChange(listener: () => void): () => void { + let last = reminderSignature(useDeviceSyncStore.getState().accounts); + return useDeviceSyncStore.subscribe((state) => { + const next = reminderSignature(state.accounts); + if (next === last) return; + last = next; + listener(); + }); +} diff --git a/src/lib/__tests__/calendar-notifications.test.ts b/src/lib/__tests__/calendar-notifications.test.ts index e678dbbc..4b995773 100644 --- a/src/lib/__tests__/calendar-notifications.test.ts +++ b/src/lib/__tests__/calendar-notifications.test.ts @@ -21,6 +21,9 @@ const h = vi.hoisted(() => ({ client: { isConnected: true, accountId: 'acc-1' }, loadEventsInRange: vi.fn(), calendarListener: null as Listener | null, + // Device sync (#34): which events the device's calendar app reminds of. + onDevice: null as ((event: { id: string }) => boolean) | null, + reminderListener: null as (() => void) | null, })); vi.mock('react-native', () => ({ @@ -63,6 +66,13 @@ vi.mock('../../stores/calendar-store', () => ({ loadEventsInRange: h.loadEventsInRange, })); vi.mock('../capabilities', () => ({ hasCalendarCapability: () => true })); +vi.mock('../../device-sync/app/reminders', () => ({ + remindedOnDevice: vi.fn(() => h.onDevice), + onDeviceRemindersChange: vi.fn((listener: () => void) => { + h.reminderListener = listener; + return () => undefined; + }), +})); const NOW = new Date('2026-09-24T08:00:00Z').getTime(); const TAG = 'bulwark-calendar-alert'; @@ -103,6 +113,8 @@ beforeEach(async () => { h.calendar.loadedRange = null; h.client.isConnected = true; h.calendarListener = null; + h.onDevice = null; + h.reminderListener = null; h.loadEventsInRange.mockResolvedValue([upcoming]); mod = await import('../calendar-notifications'); N = await import('expo-notifications'); @@ -249,6 +261,49 @@ describe('calendar reminders (B17)', () => { }); }); +describe('reminders of events synced to the device (#34)', () => { + const task = { + id: 'task-1', + '@type': 'Task', + title: 'Pay rent', + calendarIds: { 'cal-1': true }, + alerts: { a1: { trigger: { '@type': 'AbsoluteTrigger', when: '2026-09-24T09:30:00Z' }, action: 'display' } }, + }; + + it("leaves synced events to the device's calendar app; tasks keep their reminders", async () => { + const reminders = await import('../../device-sync/app/reminders'); + h.calendar.tasks = [task]; + h.onDevice = (event) => event.id === 'ev1'; + + await mod.rescheduleCalendarNotifications(); + + expect(reminders.remindedOnDevice).toHaveBeenCalledWith('app-1', 'acc-1'); + expect(N.scheduleNotificationAsync).toHaveBeenCalledTimes(1); + const request = vi.mocked(N.scheduleNotificationAsync).mock.calls[0][0]; + expect(request.content.data).toMatchObject({ kind: 'task', eventId: 'task-1' }); + }); + + it('cancels the reminders the calendar app took over', async () => { + h.pending = [reminder('ours', upcomingKey)]; + h.onDevice = () => true; + + await mod.rescheduleCalendarNotifications(); + + expect(N.cancelScheduledNotificationAsync).toHaveBeenCalledWith('ours'); + expect(N.scheduleNotificationAsync).not.toHaveBeenCalled(); + }); + + it('reschedules when who reminds, or what syncs, changes', async () => { + mod.startCalendarNotificationSync(); + await vi.advanceTimersByTimeAsync(1500); + expect(h.loadEventsInRange).toHaveBeenCalledTimes(1); + + h.reminderListener!(); + await vi.advanceTimersByTimeAsync(1500); + expect(h.loadEventsInRange).toHaveBeenCalledTimes(2); + }); +}); + describe('tapping a calendar reminder', () => { const DEFAULT = 'expo.modules.notifications.actions.DEFAULT'; function response(identifier: string, data: Record, action = DEFAULT) { diff --git a/src/lib/calendar-notifications.ts b/src/lib/calendar-notifications.ts index 19cb47de..b7f4312b 100644 --- a/src/lib/calendar-notifications.ts +++ b/src/lib/calendar-notifications.ts @@ -10,6 +10,7 @@ import { hasCalendarCapability } from './capabilities'; import { onStateChangeType } from './state-change-bus'; import { getUpcomingAlerts, type ScheduledAlert } from './calendar-alert-scheduler'; import type { CalendarReminderTarget } from '../navigation/pending-calendar-open'; +import { onDeviceRemindersChange, remindedOnDevice } from '../device-sync/app/reminders'; // Local reminders for calendar events and tasks. The webmail polls // `getPendingAlerts` every minute while a tab is open; a phone is mostly @@ -147,6 +148,16 @@ function isSignedOut(): boolean { return hasRestoredSession && !isAuthenticated; } +// The JMAP account the upcoming events are loaded from when they carry no +// account of their own. +function primaryJmapAccountId(): string | null { + try { + return jmapClient.accountId; + } catch { + return null; + } +} + /** * The events whose alerts can fire within the horizon, straight from the * server. `null` when that can't be known (offline, no session yet, the @@ -219,10 +230,15 @@ export async function rescheduleCalendarNotifications(): Promise { if (!events) return; await ensureTasksLoaded(); const { tasks, calendars } = useCalendarStore.getState(); + // Events of calendars synced to this device get their reminders from + // the device's calendar app when the user chose it to remind them + // (#34); tasks are not synced and keep theirs. + const onDevice = remindedOnDevice(useAuthStore.getState().activeAccountId, primaryJmapAccountId()); + const ownEvents = onDevice ? events.filter((event) => !onDevice(event)) : events; // Reminders fire while the app is closed, so they are worded now, in // the app language. const { locale, t } = useLocaleStore.getState(); - const wanted = getUpcomingAlerts(events, tasks, calendars, { + const wanted = getUpcomingAlerts(ownEvents, tasks, calendars, { now, horizonMs: HORIZON_MS, limit: MAX_SCHEDULED, @@ -382,6 +398,8 @@ export function startCalendarNotificationSync(options?: { askPermission?: boolea }); onStateChangeType('CalendarEvent', scheduleSoon); onStateChangeType('Calendar', scheduleSoon); + // Who reminds of synced events changed, or which calendars sync (#34). + onDeviceRemindersChange(scheduleSoon); AppState.addEventListener('change', (state) => { if (state === 'active') scheduleSoon(); }); From 434c55403837542d339dd002742885b4e6534451 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:06:36 +0200 Subject: [PATCH 024/334] fix: keep syncing when a shared account refuses access to its address books or calendars #34 Stalwart lists an account shared with the user for mail only with every capability, and answers `forbidden` when the user asks for its address books or calendars. The engine treated that like any failed request, so on the local test server every sync of usera ended with an I/O error and never reached the device, only because userb shares a mailbox. An account that refuses to list its collections (`forbidden`, `accountNotFound`, `accountNotSupportedByMethod`) is now left out of the run and reported as an item error; the other accounts sync as before. The rows written for such an account stay on the device: a refusal can pass, and dropping the account's contacts and events for one refused request would throw away device edits that are still waiting. Checked: three new engine tests (a shared account that never listed, one that stops listing its address books, one that stops listing its calendars), which fail without the fix, `npm run typecheck` and `npm test` on this commit, and a contacts sync of usera on the API 32 emulator, which now ends `ok` with one item error. --- .../__tests__/engine/failures.test.ts | 62 +++++++++++++++++++ src/device-sync/engine/calendar-sync.ts | 4 +- src/device-sync/engine/contacts-sync.ts | 4 +- src/device-sync/engine/item-sync.ts | 34 +++++++++- 4 files changed, 100 insertions(+), 4 deletions(-) diff --git a/src/device-sync/__tests__/engine/failures.test.ts b/src/device-sync/__tests__/engine/failures.test.ts index e27ec3f4..efa05ce6 100644 --- a/src/device-sync/__tests__/engine/failures.test.ts +++ b/src/device-sync/__tests__/engine/failures.test.ts @@ -9,6 +9,7 @@ import type { JmapInvocation, JmapResponse } from '../../types'; import { addDeviceContact, addServerCards, + CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, createHarness, CrashError, @@ -608,3 +609,64 @@ describe('device sync engine: deletion guards', () => { expect(new CrashError('x').name).toBe('CrashError'); }); }); + +describe('device sync engine: accounts that refuse access', () => { + // Stalwart lists an account shared for mail only with every capability, + // and its /get calls answer `forbidden`. + it('syncs the personal account when a shared one refuses to list its address books', async () => { + const h = createHarness({ accounts: 'withShared' }); + addServerCards(h, ['Ada']); + h.server.failNextMethod('AddressBook/get', { type: 'forbidden', description: 'You do not have access to account team' }, { accountId: 'team' }); + + const report = await h.run(); + + expect(report.outcome).toBe('ok'); + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: 'team', side: 'download', type: 'forbidden' })]); + expect(h.contactNamed('Ada')).toBeDefined(); + }); + + it('keeps the contacts of an account that stops listing its address books, and syncs it again once it does', async () => { + const h = createHarness({ accounts: 'withShared' }); + const teamBook = h.server.all('AddressBook', 'team')[0].id as string; + h.prefs.contactsSelection[`team/${teamBook}`] = true; + const [mate] = addServerCards(h, ['Teammate'], 'team', teamBook); + await h.run(); + expect(h.contactNamed('Teammate')).toBeDefined(); + + addServerCards(h, ['Ada']); + h.server.serverUpdate('ContactCard', 'team', mate, { 'name/full': 'Team mate' }); + h.server.failNextMethod('AddressBook/get', { type: 'forbidden' }, { accountId: 'team' }); + const report = await h.run(); + + expect(report.outcome).toBe('ok'); + expect(h.contactNamed('Ada')).toBeDefined(); + expect(h.contactNamed('Teammate')).toBeDefined(); + + expect((await h.run()).itemErrors).toEqual([]); + expect(h.contactNamed('Team mate')).toBeDefined(); + }); + + it('keeps the calendar and events of an account that refuses to list its calendars', async () => { + const h = createHarness({ accounts: 'withShared' }); + const teamCalendar = h.server.all('Calendar', 'team')[0].id as string; + h.prefs.calendarSelection[`team/${teamCalendar}`] = true; + h.server.addEvent('team', { + uid: 'uid-team', + title: 'Team meeting', + start: '2026-09-28T09:00:00', + duration: 'PT1H', + timeZone: 'Etc/UTC', + calendarIds: { [teamCalendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + expect(h.events()).toHaveLength(1); + + h.server.failNextMethod('Calendar/get', { type: 'forbidden' }, { accountId: 'team' }); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report.outcome).toBe('ok'); + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: 'team', type: 'forbidden' })]); + expect(h.events()).toHaveLength(1); + expect(h.device.rows('calendars').some((c) => c._sync_id === `team/${teamCalendar}`)).toBe(true); + }); +}); diff --git a/src/device-sync/engine/calendar-sync.ts b/src/device-sync/engine/calendar-sync.ts index 38997b03..1f78fecf 100644 --- a/src/device-sync/engine/calendar-sync.ts +++ b/src/device-sync/engine/calendar-sync.ts @@ -219,7 +219,9 @@ export class CalendarSync extends ItemSync { this.readOnlyKeys.clear(); const selected: Array<{ jmapAccountId: string; calendar: CalendarLike; readOnly: boolean; accountName: string }> = []; for (const account of this.env.accounts) { - const { list, state } = await this.env.jmap.getContainers('Calendar', account.id, CALENDAR_PROPERTIES); + const containers = await this.containersOf('Calendar', account.id, CALENDAR_PROPERTIES); + if (!containers) continue; + const { list, state } = containers; this.calendars.set(account.id, list); if (state) { this.collectionStates.set(account.id, state); diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 2064a4b4..3d5c55ee 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -286,7 +286,9 @@ export class ContactsSync extends ItemSync { protected async collections(write: boolean): Promise { this.syncedKeys.clear(); for (const account of this.env.accounts) { - const { list, state } = await this.env.jmap.getContainers('AddressBook', account.id, ADDRESS_BOOK_PROPERTIES); + const containers = await this.containersOf('AddressBook', account.id, ADDRESS_BOOK_PROPERTIES); + if (!containers) continue; + const { list, state } = containers; this.books.set(account.id, list); for (const book of list) { if (isCollectionSelected(this.env.prefs.contactsSelection, account, CONTACTS_AUTHORITY, book)) { diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 7f0f731c..942e1204 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -66,6 +66,9 @@ type Removal = 'destroyed' | 'outside'; /** Method-level refusals of a whole `/set` that concern the account, not the objects. */ const ACCOUNT_REFUSALS = new Set(['accountReadOnly', 'forbidden', 'accountNotFound', 'accountNotSupportedByMethod']); +/** Refusals to list an account's collections: the account is not readable for this user. */ +const ACCOUNT_UNREADABLE = new Set(['forbidden', 'accountNotFound', 'accountNotSupportedByMethod']); + export abstract class ItemSync { /** Per account: object ids put on `stale` (true) or fetched fine again (false), for the next state op. */ private readonly staleMarks = new Map>(); @@ -75,6 +78,8 @@ export abstract class ItemSync { private readonly extraCreated = new Map>(); /** Per account: `ifInState` of the next `/set` (the download's state, then our last `newState`). */ private readonly setStates = new Map(); + /** Accounts that refused to list their collections this run: left out of it (see `containersOf`). */ + private readonly unreadable = new Set(); constructor(protected readonly env: RunEnv) {} @@ -171,12 +176,37 @@ export abstract class ItemSync { } protected accountIds(): string[] { - return this.env.accounts.map((a) => a.id); + return this.env.accounts.map((a) => a.id).filter((id) => !this.unreadable.has(id)); } /** The session's accounts plus accounts the rows were written for that are gone from it. */ protected knownAccountIds(): string[] { - return [...new Set([...this.accountIds(), ...Object.keys(this.env.store.committed.accounts)])]; + return [...new Set([...this.accountIds(), ...Object.keys(this.env.store.committed.accounts)])] + .filter((id) => !this.unreadable.has(id)); + } + + /** + * One account's collections, or null when the account refuses to list them + * (`forbidden`, `accountNotFound`, `accountNotSupportedByMethod`): Stalwart + * lists an account shared for mail only with every capability, and access + * can go away. Such an account is left out of the run and reported; the + * rows written for it stay as they are, since a refusal can pass and must + * not drop anything from the device. + */ + protected async containersOf( + type: 'AddressBook' | 'Calendar', + accountId: string, + properties: readonly string[], + ): Promise<{ list: T[]; state: string | null } | null> { + try { + return await this.env.jmap.getContainers(type, accountId, properties); + } catch (error) { + if (!isMethodError(error) || !ACCOUNT_UNREADABLE.has(error.type)) throw error; + this.unreadable.add(accountId); + this.env.report.itemError({ ref: accountId, side: 'download', type: error.type, description: error.message }); + this.env.log(`account ${accountId} left out: ${error.type}`); + return null; + } } // ── State ── From ad0f83c72fc31c34c75a35af284c003af0ff7503 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:07:58 +0200 Subject: [PATCH 025/334] fix: stop reading the read-only flag the contacts provider never returns #34 The contacts planner read RAW_CONTACT_IS_READ_ONLY with every raw contact, but ContactsProvider takes that column only on writes: every projection and selection that names it fails with "Invalid column", on API 32 and API 35 alike. Every contacts sync on a device therefore failed before it wrote a row. The fake provider accepted the column, so no test noticed. The flag is now only written, along with any raw contact write that happens anyway, so echoes stay write-free. Whether a contact's device edits are put back is decided from the server's current rights on the card, which is also right when only the rights changed. The fake now refuses the column in queries and selections like the real providers, and every other column the planners query was checked against the real providers on both emulators. Checked: the contacts, engine and fake provider tests, `npm run typecheck` and `npm test` on this commit, and a contacts sync of usera on the API 32 emulator, which now completes. --- src/device-sync/__tests__/contacts/units.test.ts | 8 +++++--- src/device-sync/__tests__/engine/toy-planners.ts | 7 +++---- src/device-sync/__tests__/fake-provider.test.ts | 13 +++++++++++++ src/device-sync/__tests__/fakes/fake-provider.ts | 6 ++++++ src/device-sync/contacts/columns.ts | 7 +++++-- src/device-sync/contacts/download.ts | 7 ++++++- src/device-sync/contacts/upload.ts | 13 +++++++++++-- src/device-sync/planner.ts | 1 - 8 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/device-sync/__tests__/contacts/units.test.ts b/src/device-sync/__tests__/contacts/units.test.ts index 497dc2c1..7738244e 100644 --- a/src/device-sync/__tests__/contacts/units.test.ts +++ b/src/device-sync/__tests__/contacts/units.test.ts @@ -12,8 +12,10 @@ import { JPEG } from './fixtures'; describe('contacts decode', () => { it('queries the columns the planner reads', () => { expect(contactsPlanner.rawContactColumns).toEqual(expect.arrayContaining([ - '_id', 'sourceid', 'version', 'dirty', 'deleted', 'sync1', 'sync2', 'sync3', 'sync4', 'raw_contact_is_read_only', + '_id', 'sourceid', 'version', 'dirty', 'deleted', 'sync1', 'sync2', 'sync3', 'sync4', ])); + // The providers refuse it in every projection ("Invalid column"): it is only written. + expect(contactsPlanner.rawContactColumns).not.toContain('raw_contact_is_read_only'); expect(contactsPlanner.dataColumns).toEqual(expect.arrayContaining([ '_id', 'raw_contact_id', 'mimetype', 'is_primary', 'data1', 'data14', 'data_sync1', 'data_sync2', 'data_sync3', 'group_sourceid', ])); @@ -23,14 +25,14 @@ describe('contacts decode', () => { it('reads numbers given as text (data columns are TEXT) and tolerates what other apps left in sync columns', () => { const local = contactsPlanner.decodeContact( - { _id: '5', sourceid: 'c/e1', version: '3', dirty: '1', deleted: null, sync1: 'c/b,garbage,,c/x', sync2: '{"not":"a card"}', sync3: '{"uid":1}', sync4: 'nope', raw_contact_is_read_only: 0 }, + { _id: '5', sourceid: 'c/e1', version: '3', dirty: '1', deleted: null, sync1: 'c/b,garbage,,c/x', sync2: '{"not":"a card"}', sync3: '{"uid":1}', sync4: 'nope' }, [ { _id: '9', raw_contact_id: '5', mimetype: MimeType.EMAIL, data1: 'a@b', data2: '2', data_sync1: 'emails:e1', data_sync2: '', data_sync3: '[1]' }, { _id: 10, raw_contact_id: 6, mimetype: MimeType.NOTE, data1: 'another contact' }, ], ); expect(local).toMatchObject({ - rawContactId: 5, sourceId: 'c/e1', version: 3, dirty: true, deleted: false, readOnly: false, + rawContactId: 5, sourceId: 'c/e1', version: 3, dirty: true, deleted: false, collections: ['c/b', 'c/x'], shadow: null, pending: null, poison: null, }); expect(local.rows).toEqual([{ id: 9, mimetype: MimeType.EMAIL, cells: { data1: 'a@b', data2: '2' }, key: 'emails:e1', photoHash: null, baseline: null }]); diff --git a/src/device-sync/__tests__/engine/toy-planners.ts b/src/device-sync/__tests__/engine/toy-planners.ts index 8bb7136a..10a01ad7 100644 --- a/src/device-sync/__tests__/engine/toy-planners.ts +++ b/src/device-sync/__tests__/engine/toy-planners.ts @@ -97,7 +97,7 @@ function membershipOps(local: LocalContact | null, card: ContactCardWire, ctx: T } export const toyContactsPlanner: ContactsPlanner = { - rawContactColumns: ['_id', 'sourceid', 'version', 'dirty', 'deleted', 'sync1', 'sync2', 'sync3', 'sync4', 'raw_contact_is_read_only'], + rawContactColumns: ['_id', 'sourceid', 'version', 'dirty', 'deleted', 'sync1', 'sync2', 'sync3', 'sync4'], dataColumns: ['_id', 'raw_contact_id', 'mimetype', 'data1', 'data_sync1', 'data_sync3', 'group_sourceid'], groupColumns: ['_id', 'sourceid', 'version', 'dirty', 'deleted', 'title', 'sync2', 'sync3', 'sync4'], @@ -108,7 +108,6 @@ export const toyContactsPlanner: ContactsPlanner = { version: num(rc.version), dirty: flag(rc.dirty), deleted: flag(rc.deleted), - readOnly: flag(rc.raw_contact_is_read_only), collections: (str(rc.sync1) ?? '').split(',').filter(Boolean), shadow: parseJsonColumn(rc.sync2), pending: parseJsonColumn(rc.sync3), @@ -207,7 +206,7 @@ export const toyContactsPlanner: ContactsPlanner = { stillDirty = true; } ops.push(...membershipOps(local, card, ctx, local.rawContactId)); - const shadowSame = deepEqual(local.shadow, card) && local.collections.join(',') === raw.sync1 && local.readOnly === !!raw.raw_contact_is_read_only; + const shadowSame = deepEqual(local.shadow, card) && local.collections.join(',') === raw.sync1; if (!shadowSame) ops.push({ op: 'update', table: 'raw_contacts', id: local.rawContactId, values: raw }); const writes = ops.length - 1; return { ops: { ref, ops }, conflicts, stillDirty, effect: writes > 0 ? 'update' : 'none', writes }; @@ -263,7 +262,7 @@ export const toyContactsPlanner: ContactsPlanner = { actions: [{ kind: 'create', uid: local.pending.uid, collectionId: target.id, object: { uid: local.pending.uid, name: { full: value } } }], }; } - if (local.readOnly) { + if (local.shadow && ctx.isReadOnly(local.shadow)) { const shadowName = nameOf(local.shadow); return { kind: 'revert', diff --git a/src/device-sync/__tests__/fake-provider.test.ts b/src/device-sync/__tests__/fake-provider.test.ts index 335c8047..98803ef9 100644 --- a/src/device-sync/__tests__/fake-provider.test.ts +++ b/src/device-sync/__tests__/fake-provider.test.ts @@ -441,6 +441,19 @@ describe('FakeDeviceProviders: the native bridge rules', () => { expect(fake.readSyncState(ME, CONTACTS_AUTHORITY)).toBeNull(); }); + it('takes raw_contact_is_read_only on writes but refuses it in projections and selections', async () => { + const fake = new FakeDeviceProviders(); + const port = fake.port(ME, CONTACTS_AUTHORITY); + const res = await port.applyBatch([{ op: 'insert', table: 'raw_contacts', values: { [RawContacts.RAW_CONTACT_IS_READ_ONLY]: 1 } }]); + expect(res.ok).toBe(true); + await expect(port.query({ table: 'raw_contacts', columns: [RawContacts.RAW_CONTACT_IS_READ_ONLY] })).rejects.toMatchObject({ + code: 'provider', + }); + await expect( + port.query({ table: 'raw_contacts', columns: [RawContacts._ID], where: `${RawContacts.RAW_CONTACT_IS_READ_ONLY} = 1` }), + ).rejects.toMatchObject({ code: 'provider' }); + }); + it('returns no id for a settings insert, which cannot be addressed by id either', async () => { const fake = new FakeDeviceProviders(); const port = fake.port(ME, CONTACTS_AUTHORITY); diff --git a/src/device-sync/__tests__/fakes/fake-provider.ts b/src/device-sync/__tests__/fakes/fake-provider.ts index 1acb2f6a..8fbad6e2 100644 --- a/src/device-sync/__tests__/fakes/fake-provider.ts +++ b/src/device-sync/__tests__/fakes/fake-provider.ts @@ -106,6 +106,11 @@ const COLUMNS: Record> = { colors: new Set(Object.values(Colors)), }; +/** Columns the providers take on writes but refuse in every projection and selection ("Invalid column"). */ +const WRITE_ONLY_COLUMNS: Partial>> = { + raw_contacts: new Set([RawContacts.RAW_CONTACT_IS_READ_ONLY]), +}; + /** Columns only the provider computes; writes to them are refused like SQLite would for a view. */ const READ_ONLY_COLUMNS: Partial>> = { data: new Set([GroupMembership.GROUP_SOURCE_ID]), @@ -557,6 +562,7 @@ function tablesFor(authority: Authority): readonly ProviderTable[] { function checkColumns(table: ProviderTable, columns: Iterable, write: boolean): void { for (const c of columns) { if (!COLUMNS[table].has(c)) throw new FakeProviderError('provider', `no such column: ${c} in ${table}`); + if (!write && WRITE_ONLY_COLUMNS[table]?.has(c)) throw new FakeProviderError('provider', `Invalid column ${c}`); if (write && READ_ONLY_COLUMNS[table]?.has(c)) throw new FakeProviderError('provider', `${c} is read-only in ${table}`); } } diff --git a/src/device-sync/contacts/columns.ts b/src/device-sync/contacts/columns.ts index 87701327..d7bb176a 100644 --- a/src/device-sync/contacts/columns.ts +++ b/src/device-sync/contacts/columns.ts @@ -17,9 +17,13 @@ const DATA_CELLS = [ GroupMembership.GROUP_SOURCE_ID, ]; +/** + * RAW_CONTACT_IS_READ_ONLY is not among them: the providers take it on + * writes but refuse it in every projection ("Invalid column"). + */ export const RAW_CONTACT_COLUMNS: readonly string[] = [ RawContacts._ID, RawContacts.SOURCE_ID, RawContacts.VERSION, RawContacts.DIRTY, RawContacts.DELETED, - RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4, RawContacts.RAW_CONTACT_IS_READ_ONLY, + RawContacts.SYNC1, RawContacts.SYNC2, RawContacts.SYNC3, RawContacts.SYNC4, ]; export const DATA_COLUMNS: readonly string[] = [ @@ -89,7 +93,6 @@ export function decodeContact(rawContact: Row, data: Row[]): LocalContact { version: num(rawContact[RawContacts.VERSION]) ?? 0, dirty: flag(rawContact[RawContacts.DIRTY]), deleted: flag(rawContact[RawContacts.DELETED]), - readOnly: flag(rawContact[RawContacts.RAW_CONTACT_IS_READ_ONLY]), collections, shadow: decodeShadow(rawContact[RawContacts.SYNC2]), pending: decodePending(rawContact[RawContacts.SYNC3]), diff --git a/src/device-sync/contacts/download.ts b/src/device-sync/contacts/download.ts index 4cf57042..e6c0eafb 100644 --- a/src/device-sync/contacts/download.ts +++ b/src/device-sync/contacts/download.ts @@ -50,11 +50,11 @@ function changedRawColumns(local: LocalContact, values: WriteRow): WriteRow { [RawContacts.SYNC2]: local.shadow ? JSON.stringify(local.shadow) : null, [RawContacts.SYNC3]: local.pending ? JSON.stringify(local.pending) : null, [RawContacts.SYNC4]: local.poison ? JSON.stringify(local.poison) : null, - [RawContacts.RAW_CONTACT_IS_READ_ONLY]: local.readOnly ? 1 : 0, [RawContacts.DIRTY]: local.dirty ? 1 : 0, }; const out: WriteRow = {}; for (const [column, value] of Object.entries(values)) { + if (column === RawContacts.RAW_CONTACT_IS_READ_ONLY) continue; if (column === RawContacts.SYNC2) { const before = local.shadow ? canonicalJson(local.shadow) : null; const after = typeof value === 'string' ? canonicalJson(JSON.parse(value)) : null; @@ -63,6 +63,11 @@ function changedRawColumns(local: LocalContact, values: WriteRow): WriteRow { out[column] = value; } } + // The providers never return the read-only flag, so it can't be compared: + // it goes along whenever the raw contact is written anyway. + if (Object.keys(out).length && RawContacts.RAW_CONTACT_IS_READ_ONLY in values) { + out[RawContacts.RAW_CONTACT_IS_READ_ONLY] = values[RawContacts.RAW_CONTACT_IS_READ_ONLY]; + } return out; } diff --git a/src/device-sync/contacts/upload.ts b/src/device-sync/contacts/upload.ts index b9d221dc..debff260 100644 --- a/src/device-sync/contacts/upload.ts +++ b/src/device-sync/contacts/upload.ts @@ -205,11 +205,20 @@ function updatePatch(local: LocalContact, ctx: Ctx): PatchObject { const group = (local: LocalContact, ops: ProviderOp[]): OpGroup => ({ ref: local.sourceId ?? `row:${local.rawContactId}`, ops }); +/** + * Whether the card's address books take no writes now. Asked of the server's + * rights, not of RAW_CONTACT_IS_READ_ONLY on the device, which the providers + * never return (and which lags when only the rights changed). + */ +function readOnlyOnServer(local: LocalContact, ctx: Ctx): boolean { + return !!local.shadow && ctx.isReadOnly(withoutMemberOf(local.shadow)); +} + function deletedPlan(local: LocalContact, ctx: Ctx): UploadPlan { const purge = (): OpGroup => group(local, [deleteWhereId('raw_contacts', local.rawContactId)]); const ref = parseObjectRef(local.sourceId); if (ref) { - if (local.readOnly) return { kind: 'revert', ops: purge(), refetch: true }; + if (readOnlyOnServer(local, ctx)) return { kind: 'revert', ops: purge(), refetch: true }; const books = Object.entries(local.shadow?.addressBookIds ?? {}).filter(([, on]) => on).map(([id]) => id); const selected = books.filter((id) => ctx.isSelected(collectionKey(ref.accountId, id))); // A card that is also in books this device does not sync only leaves the synced ones. @@ -245,7 +254,7 @@ export function planContactUpload(local: LocalContact, ctx: Ctx): UploadPlan Date: Sun, 27 Sep 2026 05:10:50 +0200 Subject: [PATCH 026/334] fix: keep the account limit from leaving the app signed in as the wrong account Signing in connects the shared client to the new account and stores its credentials before the account registry takes it. With ten accounts the registry refused, but nothing undid the connect: every request then went out as the new account while the app still showed the old one. A sign-in link made this easy to reach, since it opened "Add account" without the limit check the menus have. - A failed registration now restores the previous connection and drops credentials the registry never took. - Adding an account to a full registry is refused before a sign-in code is spent or a browser sign-in starts, with "Remove an account in Settings to add another one." --- locales/rn/ar.json | 1 + locales/rn/ca.json | 1 + locales/rn/cs.json | 1 + locales/rn/da.json | 1 + locales/rn/de.json | 1 + locales/rn/en.json | 1 + locales/rn/es.json | 1 + locales/rn/fa.json | 1 + locales/rn/fr.json | 1 + locales/rn/he.json | 1 + locales/rn/hu.json | 1 + locales/rn/it.json | 1 + locales/rn/ja.json | 1 + locales/rn/ko.json | 1 + locales/rn/lv.json | 1 + locales/rn/mn.json | 1 + locales/rn/nb.json | 1 + locales/rn/nl.json | 1 + locales/rn/pl.json | 1 + locales/rn/pt.json | 1 + locales/rn/ro.json | 1 + locales/rn/ru.json | 1 + locales/rn/sk.json | 1 + locales/rn/tr.json | 1 + locales/rn/uk.json | 1 + locales/rn/zh-TW.json | 1 + locales/rn/zh.json | 1 + src/lib/__tests__/login-errors.test.ts | 11 + src/lib/account-utils.ts | 10 + src/lib/login-errors.ts | 11 +- .../auth-store-account-limit.test.ts | 253 ++++++++++++++++++ src/stores/account-store.ts | 4 +- src/stores/auth-store.ts | 109 ++++++-- 33 files changed, 394 insertions(+), 31 deletions(-) create mode 100644 src/stores/__tests__/auth-store-account-limit.test.ts diff --git a/locales/rn/ar.json b/locales/rn/ar.json index 17e2a88b..6c1d9564 100644 --- a/locales/rn/ar.json +++ b/locales/rn/ar.json @@ -460,6 +460,7 @@ "email_step_title": "ما عنوان بريدك الإلكتروني؟", "email_step_title_add": "ما العنوان الآخر؟", "email_title": "استخدام عنوان بريدي الإلكتروني", + "err_account_limit_detail": "أزِل حسابًا من الإعدادات لإضافة حساب آخر.", "err_bad_detail": "تحقق من بريدك الإلكتروني وكلمة المرور. إذا كان حسابك يستخدم تسجيل الدخول بخطوتين، فأنشئ كلمة مرور تطبيق في بريد الويب واستخدمها هنا.", "err_bad_title": "لم ينجح ذلك", "err_cert_detail": "تم رفض شهادة الأمان الخاصة بالخادم. إذا كان هذا خادمك الخاص، فتحقق من أن الشهادة صالحة وغير منتهية الصلاحية.", diff --git a/locales/rn/ca.json b/locales/rn/ca.json index 7b04a14a..f081bbff 100644 --- a/locales/rn/ca.json +++ b/locales/rn/ca.json @@ -460,6 +460,7 @@ "email_step_title": "Quina és la vostra adreça electrònica?", "email_step_title_add": "Quina és l'altra adreça?", "email_title": "Utilitza la meva adreça electrònica", + "err_account_limit_detail": "Suprimiu un compte a la configuració per afegir-ne un altre.", "err_bad_detail": "Comproveu el correu electrònic i la contrasenya. Si el vostre compte utilitza l'autenticació de dos factors, creeu una contrasenya d'aplicació al webmail i utilitzeu-la aquí.", "err_bad_title": "No ha funcionat", "err_cert_detail": "S'ha rebutjat el certificat de seguretat del servidor. Si és el vostre propi servidor, comproveu que el certificat sigui vàlid i no hagi caducat.", diff --git a/locales/rn/cs.json b/locales/rn/cs.json index 19a093e9..f05efcf4 100644 --- a/locales/rn/cs.json +++ b/locales/rn/cs.json @@ -460,6 +460,7 @@ "email_step_title": "Jaká je vaše e-mailová adresa?", "email_step_title_add": "Jaká je ta druhá adresa?", "email_title": "Použít moji e-mailovou adresu", + "err_account_limit_detail": "Chcete-li přidat další účet, odeberte některý v Nastavení.", "err_bad_detail": "Zkontrolujte e-mail a heslo. Pokud váš účet používá dvoufázové přihlášení, vytvořte ve webmailu heslo aplikace a použijte ho zde.", "err_bad_title": "To nevyšlo", "err_cert_detail": "Bezpečnostní certifikát serveru byl odmítnut. Pokud jde o váš vlastní server, zkontrolujte, zda je certifikát platný a nevypršel.", diff --git a/locales/rn/da.json b/locales/rn/da.json index 066ad2f4..f61a818f 100644 --- a/locales/rn/da.json +++ b/locales/rn/da.json @@ -460,6 +460,7 @@ "email_step_title": "Hvad er din e-mailadresse?", "email_step_title_add": "Hvad er den anden adresse?", "email_title": "Brug min e-mailadresse", + "err_account_limit_detail": "Fjern en konto i Indstillinger for at tilføje en ny.", "err_bad_detail": "Tjek din e-mail og adgangskode. Hvis din konto bruger to-faktor-godkendelse, så opret en app-adgangskode i webmailen, og brug den her.", "err_bad_title": "Det virkede ikke", "err_cert_detail": "Serverens sikkerhedscertifikat blev afvist. Hvis det er din egen server, så tjek, at certifikatet er gyldigt og ikke udløbet.", diff --git a/locales/rn/de.json b/locales/rn/de.json index fc010201..3610618f 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -460,6 +460,7 @@ "email_step_title": "Wie lautet Ihre E-Mail-Adresse?", "email_step_title_add": "Wie lautet die andere Adresse?", "email_title": "Meine E-Mail-Adresse verwenden", + "err_account_limit_detail": "Entfernen Sie in den Einstellungen ein Konto, um ein weiteres hinzuzufügen.", "err_bad_detail": "Prüfen Sie E-Mail-Adresse und Passwort. Wenn Ihr Konto die Zwei-Faktor-Anmeldung nutzt, erstellen Sie im Webmail ein App-Passwort und verwenden Sie es hier.", "err_bad_title": "Das hat nicht funktioniert", "err_cert_detail": "Das Sicherheitszertifikat des Servers wurde abgelehnt. Wenn dies Ihr eigener Server ist, prüfen Sie, ob das Zertifikat gültig und nicht abgelaufen ist.", diff --git a/locales/rn/en.json b/locales/rn/en.json index 570da195..c1e98852 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -470,6 +470,7 @@ "email_step_title": "What's your email address?", "email_step_title_add": "What's the other address?", "email_title": "Use my email address", + "err_account_limit_detail": "Remove an account in Settings to add another one.", "err_bad_detail": "Check your email and password. If your account uses two-factor sign-in, create an app password in the webmail and use that here.", "err_bad_title": "That didn't work", "err_cert_detail": "The server's security certificate was rejected. If this is your own server, check the certificate is valid and not expired.", diff --git a/locales/rn/es.json b/locales/rn/es.json index c042e29e..a0ee968e 100644 --- a/locales/rn/es.json +++ b/locales/rn/es.json @@ -460,6 +460,7 @@ "email_step_title": "¿Cuál es tu dirección de correo?", "email_step_title_add": "¿Cuál es la otra dirección?", "email_title": "Usar mi dirección de correo", + "err_account_limit_detail": "Elimina una cuenta en Ajustes para añadir otra.", "err_bad_detail": "Comprueba tu correo y tu contraseña. Si tu cuenta usa autenticación de dos factores, crea una contraseña de aplicación en el webmail y úsala aquí.", "err_bad_title": "No ha funcionado", "err_cert_detail": "Se rechazó el certificado de seguridad del servidor. Si es tu propio servidor, comprueba que el certificado sea válido y no haya caducado.", diff --git a/locales/rn/fa.json b/locales/rn/fa.json index ac2b4ae7..8b82e9b6 100644 --- a/locales/rn/fa.json +++ b/locales/rn/fa.json @@ -460,6 +460,7 @@ "email_step_title": "نشانی ایمیل شما چیست؟", "email_step_title_add": "نشانی دیگر چیست؟", "email_title": "استفاده از نشانی ایمیل من", + "err_account_limit_detail": "برای افزودن حساب دیگر، یکی از حساب‌ها را در تنظیمات حذف کنید.", "err_bad_detail": "ایمیل و رمز عبور خود را بررسی کنید. اگر حساب شما از ورود دو مرحله‌ای استفاده می‌کند، در ایمیل تحت وب یک رمز برنامه بسازید و آن را اینجا وارد کنید.", "err_bad_title": "موفق نشد", "err_cert_detail": "گواهی امنیتی سرور رد شد. اگر این سرور خودتان است، بررسی کنید گواهی معتبر باشد و منقضی نشده باشد.", diff --git a/locales/rn/fr.json b/locales/rn/fr.json index 7190c456..99d821bf 100644 --- a/locales/rn/fr.json +++ b/locales/rn/fr.json @@ -460,6 +460,7 @@ "email_step_title": "Quelle est votre adresse e-mail ?", "email_step_title_add": "Quelle est l'autre adresse ?", "email_title": "Utiliser mon adresse e-mail", + "err_account_limit_detail": "Supprimez un compte dans les Paramètres pour en ajouter un autre.", "err_bad_detail": "Vérifiez votre adresse e-mail et votre mot de passe. Si votre compte utilise l'authentification à deux facteurs, créez un mot de passe d'application dans le webmail et utilisez-le ici.", "err_bad_title": "Cela n'a pas fonctionné", "err_cert_detail": "Le certificat de sécurité du serveur a été refusé. S'il s'agit de votre propre serveur, vérifiez que le certificat est valide et n'a pas expiré.", diff --git a/locales/rn/he.json b/locales/rn/he.json index 5f2a00f5..a8aac6f6 100644 --- a/locales/rn/he.json +++ b/locales/rn/he.json @@ -460,6 +460,7 @@ "email_step_title": "מה כתובת האימייל שלך?", "email_step_title_add": "מה הכתובת האחרת?", "email_title": "השתמש בכתובת האימייל שלי", + "err_account_limit_detail": "הסר חשבון בהגדרות כדי להוסיף חשבון נוסף.", "err_bad_detail": "בדוק את האימייל והסיסמה. אם החשבון שלך משתמש באימות דו-שלבי, צור סיסמת אפליקציה בדואר האינטרנט והשתמש בה כאן.", "err_bad_title": "זה לא עבד", "err_cert_detail": "תעודת האבטחה של השרת נדחתה. אם זה השרת שלך, ודא שהתעודה תקפה ושתוקפה לא פג.", diff --git a/locales/rn/hu.json b/locales/rn/hu.json index 11625133..5cd2e5b7 100644 --- a/locales/rn/hu.json +++ b/locales/rn/hu.json @@ -460,6 +460,7 @@ "email_step_title": "Mi az e-mail-címed?", "email_step_title_add": "Mi a másik cím?", "email_title": "E-mail-címem használata", + "err_account_limit_detail": "Másik fiók hozzáadásához távolíts el egyet a Beállításokban.", "err_bad_detail": "Ellenőrizd az e-mail-címet és a jelszót. Ha a fiókod kétlépcsős bejelentkezést használ, hozz létre alkalmazásjelszót a webmailben, és azt használd itt.", "err_bad_title": "Ez nem sikerült", "err_cert_detail": "A szerver biztonsági tanúsítványát elutasítottuk. Ha ez a saját szervered, ellenőrizd, hogy a tanúsítvány érvényes és nem járt le.", diff --git a/locales/rn/it.json b/locales/rn/it.json index 1776a421..6d9ff388 100644 --- a/locales/rn/it.json +++ b/locales/rn/it.json @@ -460,6 +460,7 @@ "email_step_title": "Qual è il tuo indirizzo email?", "email_step_title_add": "Qual è l'altro indirizzo?", "email_title": "Usa il mio indirizzo email", + "err_account_limit_detail": "Rimuovi un account in Impostazioni per aggiungerne un altro.", "err_bad_detail": "Controlla email e password. Se il tuo account usa l'autenticazione a due fattori, crea una password per le app nella webmail e usala qui.", "err_bad_title": "Non ha funzionato", "err_cert_detail": "Il certificato di sicurezza del server è stato rifiutato. Se è il tuo server, verifica che il certificato sia valido e non scaduto.", diff --git a/locales/rn/ja.json b/locales/rn/ja.json index 20a58c57..14374f3c 100644 --- a/locales/rn/ja.json +++ b/locales/rn/ja.json @@ -460,6 +460,7 @@ "email_step_title": "メールアドレスを入力してください", "email_step_title_add": "もう一つのメールアドレスを入力してください", "email_title": "メールアドレスを使用", + "err_account_limit_detail": "別のアカウントを追加するには、設定でアカウントを削除してください。", "err_bad_detail": "メールアドレスとパスワードを確認してください。アカウントで二要素認証を使用している場合は、ウェブメールでアプリパスワードを作成し、ここで使用してください。", "err_bad_title": "サインインできませんでした", "err_cert_detail": "サーバーのセキュリティ証明書が拒否されました。ご自身のサーバーの場合は、証明書が有効で期限切れでないことを確認してください。", diff --git a/locales/rn/ko.json b/locales/rn/ko.json index 5b66094c..581e9516 100644 --- a/locales/rn/ko.json +++ b/locales/rn/ko.json @@ -460,6 +460,7 @@ "email_step_title": "이메일 주소가 무엇인가요?", "email_step_title_add": "다른 주소는 무엇인가요?", "email_title": "내 이메일 주소 사용", + "err_account_limit_detail": "다른 계정을 추가하려면 설정에서 계정을 삭제하세요.", "err_bad_detail": "이메일과 비밀번호를 확인해 주세요. 계정에 2단계 인증을 사용 중이라면 웹메일에서 앱 비밀번호를 만들어 여기에 입력해 주세요.", "err_bad_title": "로그인하지 못했어요", "err_cert_detail": "서버의 보안 인증서가 거부되었어요. 직접 운영하는 서버라면 인증서가 유효하고 만료되지 않았는지 확인해 주세요.", diff --git a/locales/rn/lv.json b/locales/rn/lv.json index 2e5d2c77..a129314a 100644 --- a/locales/rn/lv.json +++ b/locales/rn/lv.json @@ -460,6 +460,7 @@ "email_step_title": "Kāda ir jūsu e-pasta adrese?", "email_step_title_add": "Kāda ir otra adrese?", "email_title": "Izmantot manu e-pasta adresi", + "err_account_limit_detail": "Lai pievienotu citu kontu, noņemiet kādu kontu iestatījumos.", "err_bad_detail": "Pārbaudiet e-pasta adresi un paroli. Ja jūsu kontā tiek izmantota divpakāpju pierakstīšanās, izveidojiet lietotnes paroli tīmekļa pastā un izmantojiet to šeit.", "err_bad_title": "Tas neizdevās", "err_cert_detail": "Servera drošības sertifikāts tika noraidīts. Ja tas ir jūsu serveris, pārbaudiet, vai sertifikāts ir derīgs un tā termiņš nav beidzies.", diff --git a/locales/rn/mn.json b/locales/rn/mn.json index 0f1fda37..9762e509 100644 --- a/locales/rn/mn.json +++ b/locales/rn/mn.json @@ -460,6 +460,7 @@ "email_step_title": "Таны имэйл хаяг юу вэ?", "email_step_title_add": "Нөгөө хаяг юу вэ?", "email_title": "Имэйл хаягаа ашиглах", + "err_account_limit_detail": "Өөр бүртгэл нэмэхийн тулд Тохиргооноос нэг бүртгэлийг хасна уу.", "err_bad_detail": "Имэйл болон нууц үгээ шалгана уу. Хэрэв таны бүртгэл хоёр шаттай нэвтрэлт ашигладаг бол вэбмэйлд аппын нууц үг үүсгээд энд ашиглана уу.", "err_bad_title": "Амжилтгүй боллоо", "err_cert_detail": "Серверийн аюулгүй байдлын гэрчилгээг татгалзсан. Хэрэв энэ таны өөрийн сервер бол гэрчилгээ хүчинтэй, хугацаа нь дуусаагүй эсэхийг шалгана уу.", diff --git a/locales/rn/nb.json b/locales/rn/nb.json index f8d7b79a..a9ea2598 100644 --- a/locales/rn/nb.json +++ b/locales/rn/nb.json @@ -460,6 +460,7 @@ "email_step_title": "Hva er e-postadressen din?", "email_step_title_add": "Hva er den andre adressen?", "email_title": "Bruk e-postadressen min", + "err_account_limit_detail": "Fjern en konto i Innstillinger for å legge til en ny.", "err_bad_detail": "Sjekk e-postadressen og passordet. Hvis kontoen din bruker tofaktorautentisering, oppretter du et app-passord i nettposten og bruker det her.", "err_bad_title": "Det fungerte ikke", "err_cert_detail": "Serverens sikkerhetssertifikat ble avvist. Hvis dette er din egen server, må du sjekke at sertifikatet er gyldig og ikke utløpt.", diff --git a/locales/rn/nl.json b/locales/rn/nl.json index f264fb2b..2b7d732d 100644 --- a/locales/rn/nl.json +++ b/locales/rn/nl.json @@ -460,6 +460,7 @@ "email_step_title": "Wat is uw e-mailadres?", "email_step_title_add": "Wat is het andere adres?", "email_title": "Mijn e-mailadres gebruiken", + "err_account_limit_detail": "Verwijder een account in Instellingen om er nog een toe te voegen.", "err_bad_detail": "Controleer uw e-mailadres en wachtwoord. Als uw account tweefactorauthenticatie gebruikt, maak dan in de webmail een app-wachtwoord aan en gebruik dat hier.", "err_bad_title": "Dat is niet gelukt", "err_cert_detail": "Het beveiligingscertificaat van de server is geweigerd. Als dit uw eigen server is, controleer dan of het certificaat geldig en niet verlopen is.", diff --git a/locales/rn/pl.json b/locales/rn/pl.json index a45818d9..43fa5e4f 100644 --- a/locales/rn/pl.json +++ b/locales/rn/pl.json @@ -460,6 +460,7 @@ "email_step_title": "Jaki jest Twój adres e-mail?", "email_step_title_add": "Jaki jest drugi adres?", "email_title": "Użyj mojego adresu e-mail", + "err_account_limit_detail": "Usuń konto w Ustawieniach, aby dodać kolejne.", "err_bad_detail": "Sprawdź adres e-mail i hasło. Jeśli Twoje konto używa logowania dwuskładnikowego, utwórz hasło aplikacji w webmailu i użyj go tutaj.", "err_bad_title": "To nie zadziałało", "err_cert_detail": "Certyfikat bezpieczeństwa serwera został odrzucony. Jeśli to Twój własny serwer, sprawdź, czy certyfikat jest ważny i nie wygasł.", diff --git a/locales/rn/pt.json b/locales/rn/pt.json index 7d9462ca..67b05864 100644 --- a/locales/rn/pt.json +++ b/locales/rn/pt.json @@ -460,6 +460,7 @@ "email_step_title": "Qual é o seu endereço de e-mail?", "email_step_title_add": "Qual é o outro endereço?", "email_title": "Usar meu endereço de e-mail", + "err_account_limit_detail": "Remova uma conta em Configurações para adicionar outra.", "err_bad_detail": "Verifique seu e-mail e sua senha. Se sua conta usa autenticação de dois fatores, crie uma senha de aplicativo no webmail e use-a aqui.", "err_bad_title": "Não funcionou", "err_cert_detail": "O certificado de segurança do servidor foi rejeitado. Se o servidor for seu, verifique se o certificado é válido e não expirou.", diff --git a/locales/rn/ro.json b/locales/rn/ro.json index fd3c3984..42372486 100644 --- a/locales/rn/ro.json +++ b/locales/rn/ro.json @@ -460,6 +460,7 @@ "email_step_title": "Care este adresa dvs. de e-mail?", "email_step_title_add": "Care este cealaltă adresă?", "email_title": "Folosiți adresa mea de e-mail", + "err_account_limit_detail": "Eliminați un cont din Setări pentru a adăuga altul.", "err_bad_detail": "Verificați adresa de e-mail și parola. Dacă contul dvs. folosește autentificarea în doi pași, creați o parolă pentru aplicații în webmail și folosiți-o aici.", "err_bad_title": "Nu a funcționat", "err_cert_detail": "Certificatul de securitate al serverului a fost respins. Dacă este serverul dvs., verificați dacă certificatul este valid și nu a expirat.", diff --git a/locales/rn/ru.json b/locales/rn/ru.json index 052c7e2f..816c6d31 100644 --- a/locales/rn/ru.json +++ b/locales/rn/ru.json @@ -460,6 +460,7 @@ "email_step_title": "Какой у вас адрес электронной почты?", "email_step_title_add": "Какой у вас другой адрес?", "email_title": "Использовать мой адрес электронной почты", + "err_account_limit_detail": "Удалите аккаунт в настройках, чтобы добавить новый.", "err_bad_detail": "Проверьте адрес электронной почты и пароль. Если в аккаунте включена двухфакторная аутентификация, создайте пароль приложения в веб-почте и используйте его здесь.", "err_bad_title": "Не получилось", "err_cert_detail": "Сертификат безопасности сервера отклонён. Если это ваш собственный сервер, проверьте, что сертификат действителен и не истёк.", diff --git a/locales/rn/sk.json b/locales/rn/sk.json index 05631eeb..98b8a709 100644 --- a/locales/rn/sk.json +++ b/locales/rn/sk.json @@ -460,6 +460,7 @@ "email_step_title": "Aká je vaša e-mailová adresa?", "email_step_title_add": "Aká je tá druhá adresa?", "email_title": "Použiť moju e-mailovú adresu", + "err_account_limit_detail": "Ak chcete pridať ďalší účet, odstráňte niektorý v Nastaveniach.", "err_bad_detail": "Skontrolujte e-mail a heslo. Ak váš účet používa dvojfaktorové prihlásenie, vytvorte vo webmaile heslo aplikácie a použite ho tu.", "err_bad_title": "To nevyšlo", "err_cert_detail": "Bezpečnostný certifikát servera bol odmietnutý. Ak ide o váš vlastný server, skontrolujte, či je certifikát platný a nevypršal.", diff --git a/locales/rn/tr.json b/locales/rn/tr.json index e38480cb..07934f97 100644 --- a/locales/rn/tr.json +++ b/locales/rn/tr.json @@ -460,6 +460,7 @@ "email_step_title": "E-posta adresiniz nedir?", "email_step_title_add": "Diğer adres nedir?", "email_title": "E-posta adresimi kullan", + "err_account_limit_detail": "Başka bir hesap eklemek için Ayarlar'dan bir hesabı kaldırın.", "err_bad_detail": "E-posta adresinizi ve parolanızı kontrol edin. Hesabınız iki aşamalı giriş kullanıyorsa webmail'de bir uygulama parolası oluşturun ve burada onu kullanın.", "err_bad_title": "Bu işe yaramadı", "err_cert_detail": "Sunucunun güvenlik sertifikası reddedildi. Bu sizin sunucunuzsa sertifikanın geçerli olduğunu ve süresinin dolmadığını kontrol edin.", diff --git a/locales/rn/uk.json b/locales/rn/uk.json index 94aaffcf..3aac14cf 100644 --- a/locales/rn/uk.json +++ b/locales/rn/uk.json @@ -460,6 +460,7 @@ "email_step_title": "Яка ваша адреса електронної пошти?", "email_step_title_add": "Яка ваша інша адреса?", "email_title": "Використати мою адресу електронної пошти", + "err_account_limit_detail": "Видаліть обліковий запис у налаштуваннях, щоб додати новий.", "err_bad_detail": "Перевірте адресу електронної пошти та пароль. Якщо у вашому обліковому записі ввімкнено двофакторну автентифікацію, створіть пароль програми у веб-пошті й використайте його тут.", "err_bad_title": "Не вдалося", "err_cert_detail": "Сертифікат безпеки сервера відхилено. Якщо це ваш власний сервер, перевірте, що сертифікат дійсний і не прострочений.", diff --git a/locales/rn/zh-TW.json b/locales/rn/zh-TW.json index 9be70b84..0e0273e5 100644 --- a/locales/rn/zh-TW.json +++ b/locales/rn/zh-TW.json @@ -460,6 +460,7 @@ "email_step_title": "您的電子郵件地址是什麼?", "email_step_title_add": "另一個電子郵件地址是什麼?", "email_title": "使用我的電子郵件地址", + "err_account_limit_detail": "請先在設定中移除一個帳號,再新增其他帳號。", "err_bad_detail": "請檢查您的電子郵件地址與密碼。如果您的帳號使用雙因素驗證登入,請在網頁郵件中建立應用程式密碼,並在此使用。", "err_bad_title": "無法登入", "err_cert_detail": "伺服器的安全性憑證遭到拒絕。如果這是您自己的伺服器,請確認憑證有效且未過期。", diff --git a/locales/rn/zh.json b/locales/rn/zh.json index 2a616106..ed7bd8af 100644 --- a/locales/rn/zh.json +++ b/locales/rn/zh.json @@ -460,6 +460,7 @@ "email_step_title": "您的邮箱地址是什么?", "email_step_title_add": "另一个邮箱地址是什么?", "email_title": "使用我的邮箱地址", + "err_account_limit_detail": "请在设置中移除一个账户后再添加新账户。", "err_bad_detail": "请检查您的邮箱地址和密码。如果您的账户使用双因素身份验证登录,请在网页邮箱中创建应用密码并在此使用。", "err_bad_title": "无法登录", "err_cert_detail": "服务器的安全证书被拒绝。如果这是您自己的服务器,请检查证书是否有效且未过期。", diff --git a/src/lib/__tests__/login-errors.test.ts b/src/lib/__tests__/login-errors.test.ts index 412576b6..c6ad607e 100644 --- a/src/lib/__tests__/login-errors.test.ts +++ b/src/lib/__tests__/login-errors.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect } from 'vitest'; import { describeLoginError } from '../login-errors'; import { PairingError, type PairingErrorReason } from '../oauth'; +import { AccountLimitError } from '../account-utils'; function named(name: string, message: string): Error { const err = new Error(message); @@ -20,6 +21,16 @@ describe('describeLoginError', () => { expect(describeLoginError('Session expired for this account').title).toBe('Your session has expired. Please sign in again.'); }); + it('says the account limit is reached and what to do about it', () => { + const want = { + title: 'Maximum of 10 accounts reached.', + detail: 'Remove an account in Settings to add another one.', + }; + expect(describeLoginError(new AccountLimitError())).toEqual(want); + // As the store keeps it: the message alone. + expect(describeLoginError('Maximum of 10 accounts reached')).toEqual(want); + }); + it('names the host it could not reach', () => { const copy = describeLoginError(named('NetworkError', 'Network request failed'), { serverUrl: 'https://mail.example.com', diff --git a/src/lib/account-utils.ts b/src/lib/account-utils.ts index fec56c1b..ff15c030 100644 --- a/src/lib/account-utils.ts +++ b/src/lib/account-utils.ts @@ -3,6 +3,16 @@ // (MAX_SSE_STREAMS), so nothing scales with this number at runtime. export const MAX_ACCOUNTS = 10; +/** A new account would take the registry past MAX_ACCOUNTS. */ +export class AccountLimitError extends Error { + readonly limit = MAX_ACCOUNTS; + + constructor() { + super(`Maximum of ${MAX_ACCOUNTS} accounts reached`); + this.name = 'AccountLimitError'; + } +} + export function generateAccountId(username: string, serverUrl: string): string { let host = serverUrl; try { diff --git a/src/lib/login-errors.ts b/src/lib/login-errors.ts index 7f186ba3..9ee7eaef 100644 --- a/src/lib/login-errors.ts +++ b/src/lib/login-errors.ts @@ -5,6 +5,7 @@ // // Errors are matched by `name` and message text rather than `instanceof` so // this module stays free of the api/ and expo dependency graph. +import { MAX_ACCOUNTS } from './account-utils'; export interface LoginErrorCopy { title: string; @@ -221,8 +222,14 @@ export function describeLoginError(err: unknown, context: LoginErrorContext = {} }; } - if (lower.includes('maximum of') && lower.includes('accounts')) { - return { title: message }; + // No room for another account (AccountLimitError, or its message as the + // store keeps it). + if (name === 'AccountLimitError' || /maximum of \d+ accounts/i.test(message)) { + const count = Number(/maximum of (\d+)/i.exec(message)?.[1]) || MAX_ACCOUNTS; + return { + title: t('settings.account.accounts.limit', 'Maximum of {count} accounts reached.', { count }), + detail: t('login.mobile.err_account_limit_detail', 'Remove an account in Settings to add another one.'), + }; } // Unrecognised: show what we were told rather than inventing a cause. diff --git a/src/stores/__tests__/auth-store-account-limit.test.ts b/src/stores/__tests__/auth-store-account-limit.test.ts new file mode 100644 index 00000000..ee9c00af --- /dev/null +++ b/src/stores/__tests__/auth-store-account-limit.test.ts @@ -0,0 +1,253 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// A full account registry (MAX_ACCOUNTS): adding an account is refused before +// a pairing code is spent or a browser sign-in starts, and a sign-in that +// finds the registry full only once it has connected hands the live account +// its connection back instead of leaving the shared client signed in as the +// new account. + +const session = { apiUrl: 'https://mail.example.com/jmap/' }; + +const request = vi.fn(async (calls: Array<[string, Record, string]>) => ({ + methodResponses: calls.map(([name, , id]) => { + if (name === 'Identity/get') return [name, { list: [{ id: 'i1', name: 'Ada', email: 'ada@example.com' }] }, id]; + if (name === 'Mailbox/get') return [name, { list: [{ id: 'mb-in', name: 'Inbox', role: 'inbox' }], state: 'mbs-1' }, id]; + return ['error', { type: 'forbidden' }, id]; + }), +})); + +const LIVE_SNAPSHOT = { session: 'live-session', credentials: 'live-credentials', accountId: 'live-jmap' }; + +vi.mock('../../api/jmap-client', () => ({ + jmapClient: { + connect: vi.fn(async () => ({ apiUrl: 'https://mail.example.com/jmap/' })), + connectWithOAuth: vi.fn(async () => ({ + session: { apiUrl: 'https://mail.example.com/jmap/' }, + username: 'ada@example.com', + accountId: 'ada@example.com@mail.example.com', + })), + snapshot: vi.fn(() => LIVE_SNAPSHOT), + restoreSnapshot: vi.fn(), + reset: vi.fn(), + clearAccountCredentials: vi.fn(async () => undefined), + onAuthFailure: vi.fn(() => () => undefined), + onTokenRefresh: vi.fn(() => () => undefined), + hasAccountCapability: vi.fn(() => false), + getAccountName: () => undefined, + getSharedMailAccounts: () => [], + request: (calls: Array<[string, Record, string]>) => request(calls), + accountId: 'acc-1', + isConnected: true, + currentSession: { apiUrl: 'https://mail.example.com/jmap/' }, + username: 'ada@example.com', + serverUrl: 'https://mail.example.com', + }, + AuthenticationError: class AuthenticationError extends Error {}, + NetworkError: class NetworkError extends Error {}, +})); + +vi.mock('../../lib/push-notifications', () => ({ + teardownPushNotifications: vi.fn(async () => undefined), + teardownPushNotificationsForAccount: vi.fn(async () => undefined), +})); + +vi.mock('../../lib/oauth', async (importOriginal) => ({ + ...(await importOriginal()), + redeemPairingCode: vi.fn(), +})); + +vi.mock('../../lib/oauth-native', async (importOriginal) => ({ + ...(await importOriginal()), + discoverOAuthMetadata: vi.fn(), + loginWithPkce: vi.fn(), + probeWebmail: vi.fn(), +})); + +import * as WebBrowser from 'expo-web-browser'; +import { jmapClient } from '../../api/jmap-client'; +import { redeemPairingCode, type HandoffResult } from '../../lib/oauth'; +import { discoverOAuthMetadata, loginWithPkce, probeWebmail } from '../../lib/oauth-native'; +import { AccountLimitError, MAX_ACCOUNTS } from '../../lib/account-utils'; +import { useAccountStore, type AccountEntry } from '../account-store'; +import { useAuthStore } from '../auth-store'; + +const WEBMAIL = 'https://webmail.example.org/mail'; +const SERVER = 'https://mail.example.com'; +const NEW_ID = 'ada@example.com@mail.example.com'; +const redeem = redeemPairingCode as ReturnType; +const connect = jmapClient.connect as unknown as ReturnType; +const connectWithOAuth = jmapClient.connectWithOAuth as unknown as ReturnType; + +const oauthResult: HandoffResult = { + flow: 'oauth', + serverUrl: SERVER, + tokens: { + accessToken: 'at', + refreshToken: 'sealed-refresh', + tokenEndpoint: `${WEBMAIL}/api/auth/pair/token`, + clientId: 'bulwark-webmail', + source: 'pairing', + }, +}; + +let codeCounter = 0; +function newCode(): string { + codeCounter += 1; + return `ffff${codeCounter.toString(16).padStart(60, '0')}`; +} + +function entry(i: number): AccountEntry { + return { + id: `user${i}@mail${i}.example.net`, + serverUrl: `https://mail${i}.example.net`, + username: `user${i}`, + displayName: `User ${i}`, + email: `user${i}@example.net`, + avatarColor: '#336699', + lastLoginAt: 0, + isConnected: true, + hasError: false, + isDefault: i === 0, + }; +} + +// `count` accounts, the first one live. +function registry(count: number, extra: AccountEntry[] = []): void { + const accounts = [...Array.from({ length: count - extra.length }, (_, i) => entry(i)), ...extra]; + useAccountStore.setState({ accounts, activeAccountId: accounts[0].id, defaultAccountId: accounts[0].id }); + useAuthStore.setState({ + isAuthenticated: true, + isLoading: false, + error: null, + session: session as never, + activeAccountId: accounts[0].id, + username: accounts[0].username, + serverUrl: accounts[0].serverUrl, + }); +} + +// What a sign-in finishing elsewhere meanwhile does: takes the last free slot. +function fillLastSlotDuring(result: T): () => Promise { + return async () => { + const { accounts } = useAccountStore.getState(); + useAccountStore.setState({ accounts: [...accounts, entry(99)] }); + return result; + }; +} + +beforeEach(() => { + vi.clearAllMocks(); + (probeWebmail as ReturnType).mockResolvedValue(true); + (discoverOAuthMetadata as ReturnType).mockResolvedValue(null); +}); + +describe('a full registry refuses before anything is spent', () => { + it('keeps a pairing code unredeemed when adding an account', async () => { + registry(MAX_ACCOUNTS); + const code = newCode(); + + await expect(useAuthStore.getState().loginViaPairing(WEBMAIL, code, { addAccount: true })) + .rejects.toBeInstanceOf(AccountLimitError); + expect(redeem).not.toHaveBeenCalled(); + expect(jmapClient.snapshot).not.toHaveBeenCalled(); + expect(connectWithOAuth).not.toHaveBeenCalled(); + expect(useAuthStore.getState()).toMatchObject({ + isAuthenticated: true, + isLoading: false, + activeAccountId: entry(0).id, + error: `Maximum of ${MAX_ACCOUNTS} accounts reached`, + }); + + // Not spent: once there is room, the same code still works. + registry(MAX_ACCOUNTS - 1); + redeem.mockResolvedValueOnce(oauthResult); + await useAuthStore.getState().loginViaPairing(WEBMAIL, code, { addAccount: true }); + expect(redeem).toHaveBeenCalledWith(WEBMAIL, code); + expect(useAccountStore.getState().accounts).toHaveLength(MAX_ACCOUNTS); + expect(useAuthStore.getState().activeAccountId).toBe(NEW_ID); + }); + + it('does not open the webmail or OAuth sign-in when adding an account', async () => { + registry(MAX_ACCOUNTS); + + await expect(useAuthStore.getState().loginViaWebmail(SERVER, { addAccount: true })) + .rejects.toBeInstanceOf(AccountLimitError); + await expect(useAuthStore.getState().loginViaOAuth(SERVER, { addAccount: true })) + .rejects.toBeInstanceOf(AccountLimitError); + expect(probeWebmail).not.toHaveBeenCalled(); + expect(loginWithPkce).not.toHaveBeenCalled(); + expect(WebBrowser.openAuthSessionAsync).not.toHaveBeenCalled(); + expect(useAuthStore.getState()).toMatchObject({ isLoading: false, activeAccountId: entry(0).id }); + }); + + it('refuses a password sign-in for a new account before connecting', async () => { + registry(MAX_ACCOUNTS); + + await expect(useAuthStore.getState().login(SERVER, 'ada@example.com', 'pw', { addAccount: true })) + .rejects.toBeInstanceOf(AccountLimitError); + expect(connect).not.toHaveBeenCalled(); + expect(jmapClient.restoreSnapshot).not.toHaveBeenCalled(); + expect(useAuthStore.getState()).toMatchObject({ isLoading: false, activeAccountId: entry(0).id }); + }); + + it('still signs in again to an account it already holds', async () => { + const ada = { ...entry(5), id: NEW_ID, serverUrl: SERVER, username: 'ada@example.com' }; + registry(MAX_ACCOUNTS, [ada]); + + await useAuthStore.getState().login(SERVER, 'ada@example.com', 'pw', { addAccount: true }); + expect(connect).toHaveBeenCalled(); + expect(useAccountStore.getState().accounts).toHaveLength(MAX_ACCOUNTS); + expect(useAuthStore.getState().activeAccountId).toBe(NEW_ID); + }); +}); + +describe('a registry that fills up while connecting', () => { + it('gives the live account its connection back after an OAuth sign-in (pairing)', async () => { + registry(MAX_ACCOUNTS - 1); + redeem.mockResolvedValueOnce(oauthResult); + connectWithOAuth.mockImplementationOnce(fillLastSlotDuring({ + session: { apiUrl: 'https://mail.example.com/jmap/' }, + username: 'ada@example.com', + accountId: NEW_ID, + })); + + await expect(useAuthStore.getState().loginViaPairing(WEBMAIL, newCode(), { addAccount: true })) + .rejects.toBeInstanceOf(AccountLimitError); + + expect(jmapClient.restoreSnapshot).toHaveBeenCalledWith(LIVE_SNAPSHOT); + // The new account's stored credentials don't outlive it. + expect(jmapClient.clearAccountCredentials).toHaveBeenCalledWith(NEW_ID); + expect(useAccountStore.getState().accounts.map((a) => a.id)).not.toContain(NEW_ID); + expect(useAccountStore.getState().activeAccountId).toBe(entry(0).id); + expect(useAuthStore.getState()).toMatchObject({ isAuthenticated: true, isLoading: false, activeAccountId: entry(0).id }); + }); + + it('gives the live account its connection back after a password sign-in', async () => { + registry(MAX_ACCOUNTS - 1); + connect.mockImplementationOnce(fillLastSlotDuring({ apiUrl: 'https://mail.example.com/jmap/' })); + + await expect(useAuthStore.getState().login(SERVER, 'ada@example.com', 'pw', { addAccount: true })) + .rejects.toBeInstanceOf(AccountLimitError); + + expect(jmapClient.restoreSnapshot).toHaveBeenCalledWith(LIVE_SNAPSHOT); + expect(jmapClient.clearAccountCredentials).toHaveBeenCalledWith(NEW_ID); + expect(useAuthStore.getState()).toMatchObject({ isAuthenticated: true, isLoading: false, activeAccountId: entry(0).id }); + expect(useAuthStore.getState().error).toBe(`Maximum of ${MAX_ACCOUNTS} accounts reached`); + }); + + it('keeps the credentials of an account it already held', async () => { + const ada = { ...entry(5), id: NEW_ID, serverUrl: SERVER, username: 'ada@example.com' }; + registry(MAX_ACCOUNTS - 1, [ada]); + // Held, so the update fits; make registering fail another way. + const addAccount = useAccountStore.getState().addAccount; + useAccountStore.setState({ addAccount: () => { throw new AccountLimitError(); } }); + try { + await expect(useAuthStore.getState().login(SERVER, 'ada@example.com', 'pw', { addAccount: true })) + .rejects.toBeInstanceOf(AccountLimitError); + } finally { + useAccountStore.setState({ addAccount }); + } + expect(jmapClient.restoreSnapshot).toHaveBeenCalledWith(LIVE_SNAPSHOT); + expect(jmapClient.clearAccountCredentials).not.toHaveBeenCalled(); + }); +}); diff --git a/src/stores/account-store.ts b/src/stores/account-store.ts index e147754b..58e9f946 100644 --- a/src/stores/account-store.ts +++ b/src/stores/account-store.ts @@ -1,7 +1,7 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; import { createPersistStorage } from './persist-storage'; -import { generateAccountId, MAX_ACCOUNTS } from '../lib/account-utils'; +import { AccountLimitError, generateAccountId, MAX_ACCOUNTS } from '../lib/account-utils'; import { generateAvatarColor } from '../lib/avatar-utils'; export interface AccountEntry { @@ -72,7 +72,7 @@ export const useAccountStore = create()( } if (state.accounts.length >= MAX_ACCOUNTS) { - throw new Error(`Maximum of ${MAX_ACCOUNTS} accounts reached`); + throw new AccountLimitError(); } const isDefault = state.accounts.length === 0; diff --git a/src/stores/auth-store.ts b/src/stores/auth-store.ts index 767374b2..86e11bc3 100644 --- a/src/stores/auth-store.ts +++ b/src/stores/auth-store.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { jmapClient, AuthenticationError, NetworkError } from '../api/jmap-client'; +import { jmapClient, AuthenticationError, NetworkError, type ClientSnapshot } from '../api/jmap-client'; import type { JMAPSession } from '../api/types'; import { fetchAccountDisplayName, isStalwartSupported } from '../api/account-security'; import { useAccountStore } from './account-store'; @@ -12,7 +12,7 @@ import { flushPersistedWrites } from './persist-storage'; import { clearEmailDetailCache } from '../lib/email-detail-cache'; import { clearBodyDocuments } from '../lib/email-body-document'; import { clearBodyHeights } from '../lib/body-heights'; -import { generateAccountId } from '../lib/account-utils'; +import { AccountLimitError, generateAccountId, MAX_ACCOUNTS } from '../lib/account-utils'; import { runWebmailHandoff, redeemPairingCode, @@ -168,6 +168,39 @@ function hostOfUrl(url: string): string { return url.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '').split(/[/?#]/)[0]; } +// Room in the registry for the account a sign-in is about to add. One it +// already holds is an update and always fits; without an id (a code not yet +// redeemed, a browser sign-in not yet back) a full registry means no. +function assertRoomForAccount(accountId?: string): void { + const { accounts } = useAccountStore.getState(); + if (accountId && accounts.some((a) => a.id === accountId)) return; + if (accounts.length >= MAX_ACCOUNTS) throw new AccountLimitError(); +} + +// Adding an account to a full registry: refuse before a sign-in code is +// spent or a browser sign-in started, since what they buy can't be kept. +function refuseAddWhenFull(set: (partial: Partial) => void, opts?: { addAccount?: boolean }): void { + if (!opts?.addAccount) return; + try { + assertRoomForAccount(); + } catch (err) { + set({ isLoading: false, error: err instanceof Error ? err.message : String(err) }); + throw err; + } +} + +// connect() points the shared client at the new account and stores its +// credentials before the registry has taken it. When registering it fails +// (the account limit), undo both: the live account, when one is being added +// to, gets the client back, and an account the registry never took keeps no +// credentials behind. Otherwise every request would go out as the new +// account while the app still shows the old one. +async function undoConnect(previous: ClientSnapshot | null, accountId: string, wasRegistered: boolean): Promise { + if (previous) jmapClient.restoreSnapshot(previous); + else jmapClient.reset(); + if (!wasRegistered) await jmapClient.clearAccountCredentials(accountId).catch(() => undefined); +} + // Best-effort RFC 7009 revocation of an account's refresh token on sign-out. // QR-paired bundles are left alone: webmail up to 1.11 handed the phone the // desktop's own refresh token, and newer ones hand out a separate grant whose @@ -239,21 +272,29 @@ async function completeOAuthHandoff( throw err; } const { session, username, accountId } = connected; + + const accountStore = useAccountStore.getState(); + const wasRegistered = !!accountStore.getAccountById(accountId); + try { + accountStore.addAccount({ + serverUrl: result.serverUrl.replace(/\/+$/, ''), + username, + displayName: username, + email: username, + lastLoginAt: Date.now(), + isConnected: true, + hasError: false, + }); + } catch (err) { + await undoConnect(previous, accountId, wasRegistered); + throw err; + } + // Contacts/calendar are still single-bucket, so wipe those now that the + // new account is registered and the one the client serves. if (previous) { useContactsStore.getState().reset(); useCalendarStore.getState().reset(); } - - const accountStore = useAccountStore.getState(); - accountStore.addAccount({ - serverUrl: result.serverUrl.replace(/\/+$/, ''), - username, - displayName: username, - email: username, - lastLoginAt: Date.now(), - isConnected: true, - hasError: false, - }); accountStore.setActiveAccount(accountId); useEmailStore.getState().setActiveAccount(accountId); @@ -303,7 +344,12 @@ export const useAuthStore = create((set, get) => ({ // Adding an additional account: keep the live connection until the new // sign-in succeeded so a typo doesn't kill the current session. const previous = opts?.addAccount && get().isAuthenticated ? jmapClient.snapshot() : null; + const accountId = generateAccountId(username, serverUrl.replace(/\/+$/, '')); try { + // A new account with no room left fails here, before connect swaps + // the live client over or stores credentials for it. + assertRoomForAccount(accountId); + const wasRegistered = !!useAccountStore.getState().getAccountById(accountId); let session: JMAPSession; try { session = await jmapClient.connect(serverUrl, username, password, opts?.totp); @@ -311,24 +357,28 @@ export const useAuthStore = create((set, get) => ({ if (previous) jmapClient.restoreSnapshot(previous); throw err; } + + const accountStore = useAccountStore.getState(); + try { + accountStore.addAccount({ + serverUrl: serverUrl.replace(/\/+$/, ''), + username, + displayName: username, + email: username, + lastLoginAt: Date.now(), + isConnected: true, + hasError: false, + }); + } catch (err) { + await undoConnect(previous, accountId, wasRegistered); + throw err; + } // Contacts/calendar are still single-bucket, so wipe those now that - // the new account is the one the client serves. + // the new account is registered and the one the client serves. if (previous) { useContactsStore.getState().reset(); useCalendarStore.getState().reset(); } - const accountId = generateAccountId(username, serverUrl.replace(/\/+$/, '')); - - const accountStore = useAccountStore.getState(); - accountStore.addAccount({ - serverUrl: serverUrl.replace(/\/+$/, ''), - username, - displayName: username, - email: username, - lastLoginAt: Date.now(), - isConnected: true, - hasError: false, - }); accountStore.setActiveAccount(accountId); // Swap the email store's active view to the new account so the rest of // this function (and refetchFeatureStores) writes to the right bucket. @@ -360,6 +410,7 @@ export const useAuthStore = create((set, get) => ({ loginViaWebmail: async (webmailUrl, opts) => { set({ isLoading: true, error: null }); + refuseAddWhenFull(set, opts); // Discovery finds the *JMAP* host; a Bulwark webmail is not necessarily // served there. Opening `/login?mobile_redirect_uri=…` on a bare Stalwart // lands on a 404 or the admin page, so check first and fall back to the @@ -415,6 +466,7 @@ export const useAuthStore = create((set, get) => ({ loginViaOAuth: async (serverUrl, opts) => { set({ isLoading: true, error: null }); + refuseAddWhenFull(set, opts); const base = serverUrl.replace(/\/+$/, ''); let tokens; try { @@ -453,6 +505,9 @@ export const useAuthStore = create((set, get) => ({ host: hostOfUrl(webmailUrl), }); } + // With no room for another account the code stays unspent: redeeming it + // would only buy a sign-in the registry can't keep. + refuseAddWhenFull(set, opts); pairingCodesSeen.add(code); set({ isLoading: true, error: null }); @@ -475,7 +530,7 @@ export const useAuthStore = create((set, get) => ({ // keeps for that step), and the account limit, which a new code won't fix. const serverHost = hostOfUrl(result.serverUrl); const connectFailed = (err: unknown): unknown => { - if (err instanceof Error && (err.name === 'TotpRequiredError' || /maximum of \d+ accounts/i.test(err.message))) { + if (err instanceof Error && (err.name === 'TotpRequiredError' || err instanceof AccountLimitError)) { // `login` has settled the store already; the OAuth path has not. if (get().isLoading) set({ isLoading: false, error: err.message }); return err; From a27d014c59ed6737d408b15808f8fa6f9892fbe2 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:11:07 +0200 Subject: [PATCH 027/334] fix: ask before a sign-in link from another app signs in Any web page (one tap) or installed app (no tap) could fire a `bulwarkmail://pair` link at the app, which then silently added the sender's mailbox and switched to it, so the user might send or save mail into an attacker's account. A `connect` link likewise opened any server's sign-in page. - Links that arrive from outside the app now ask first, naming the webmail's host and whether the link adds an account. Cancel discards the link for good; a link delivered again while the question is open is dropped. Codes scanned or pasted inside the app still go straight through. - With the account registry full, a link shows the limit instead of opening "Add account". - A pairing link for a plain-http webmail says the connection would be unencrypted instead of "That code isn't a Bulwark sign-in code", or nothing at all for a tapped link. --- App.tsx | 38 +++-- locales/rn/ar.json | 6 + locales/rn/ca.json | 6 + locales/rn/cs.json | 6 + locales/rn/da.json | 6 + locales/rn/de.json | 6 + locales/rn/en.json | 6 + locales/rn/es.json | 6 + locales/rn/fa.json | 6 + locales/rn/fr.json | 6 + locales/rn/he.json | 6 + locales/rn/hu.json | 6 + locales/rn/it.json | 6 + locales/rn/ja.json | 6 + locales/rn/ko.json | 6 + locales/rn/lv.json | 6 + locales/rn/mn.json | 6 + locales/rn/nb.json | 6 + locales/rn/nl.json | 6 + locales/rn/pl.json | 6 + locales/rn/pt.json | 6 + locales/rn/ro.json | 6 + locales/rn/ru.json | 6 + locales/rn/sk.json | 6 + locales/rn/tr.json | 6 + locales/rn/uk.json | 6 + locales/rn/zh-TW.json | 6 + locales/rn/zh.json | 6 + src/components/PasteSignInLinkModal.tsx | 10 +- src/lib/__tests__/login-errors.test.ts | 10 +- src/lib/__tests__/oauth-pairing.test.ts | 67 ++++++++ src/lib/oauth.ts | 45 ++++++ src/navigation/__tests__/linking.test.ts | 31 +++- .../__tests__/pending-sign-in-link.test.ts | 148 ++++++++++++++++++ src/navigation/linking.ts | 39 ++++- src/navigation/pending-sign-in-link.ts | 115 ++++++++++++-- src/screens/LoginScreen.tsx | 124 +++++++++++++-- 37 files changed, 742 insertions(+), 47 deletions(-) create mode 100644 src/navigation/__tests__/pending-sign-in-link.test.ts diff --git a/App.tsx b/App.tsx index 28c60920..f8d60275 100644 --- a/App.tsx +++ b/App.tsx @@ -73,8 +73,16 @@ import { AppIconBadge } from './src/components/AppIconBadge'; import { getEmails } from './src/api/email'; import { signOutWidgets, startWidgetSync } from './src/widgets/sync'; import { draftContextFromEmail } from './src/lib/draft-context'; -import { acceptSignInLink, handleDeepLink, parseDeepLink, shareToDeepLink, type DeepLink } from './src/navigation/linking'; +import { + acceptSignInLink, + handleDeepLink, + parseDeepLink, + routeParkedSignInLink, + shareToDeepLink, + type DeepLink, +} from './src/navigation/linking'; import { usePendingSignInLinkStore } from './src/navigation/pending-sign-in-link'; +import { MAX_ACCOUNTS } from './src/lib/account-utils'; import { addShareListener, getInitialShare, shareAttachments } from './src/lib/share-intent'; import { OfflineCacheBanner } from './src/components/OfflineCacheBanner'; import { useOfflineCacheStore } from './src/stores/offline-cache-store'; @@ -573,8 +581,10 @@ export default function App() { // Sign-in links (`bulwarkmail://pair?…` from the webmail's Link Mobile App, // `bulwarkmail://connect?…`) work signed out too, so unlike the links above - // they are listened for from launch. They are parked for the login screen: - // the signed-out one takes them as it mounts. + // they are listened for from launch. They are parked for the login screen + // (the signed-out one takes them as it mounts), marked as coming from + // outside the app: any page or app can fire one, so the login screen asks + // before it redeems or opens anything. React.useEffect(() => { if (!initialSignInLinkRead) { initialSignInLinkRead = true; @@ -585,9 +595,10 @@ export default function App() { }, []); // Signed in, a sign-in link adds an account: open Add account, whose login - // screen takes the parked link. On a cold start the navigator mounts right - // after the auth gate flips, so wait for it like a reminder tap does. - const signInLinkPending = usePendingSignInLinkStore((s) => s.payload !== null); + // screen takes the parked link (or says why a refused one won't be used). + // On a cold start the navigator mounts right after the auth gate flips, so + // wait for it like a reminder tap does. + const signInLinkPending = usePendingSignInLinkStore((s) => s.pending !== null || s.refusal !== null); const addAccountForLink = isAuthenticated && signInLinkPending; React.useEffect(() => { if (!addAccountForLink) return; @@ -598,9 +609,18 @@ export default function App() { if (cancelled) return; } if (cancelled || !navigationRef.isReady()) return; - // Taken meanwhile (Add account was already open). - if (!usePendingSignInLinkStore.getState().payload) return; - navigationRef.navigate('AddAccount'); + const route = routeParkedSignInLink(useAccountStore.getState().accounts.length); + if (route === 'account-limit') { + // Said like the Add account buttons say it, instead of opening a + // screen whose sign-in can only fail. + const t = useLocaleStore.getState().t; + toast.error( + t('settings.account.accounts.limit', 'Maximum of {count} accounts reached.', { count: MAX_ACCOUNTS }), + t('login.mobile.err_account_limit_detail', 'Remove an account in Settings to add another one.'), + ); + return; + } + if (route === 'add-account') navigationRef.navigate('AddAccount'); })(); return () => { cancelled = true; }; }, [addAccountForLink]); diff --git a/locales/rn/ar.json b/locales/rn/ar.json index 6c1d9564..baa642b5 100644 --- a/locales/rn/ar.json +++ b/locales/rn/ar.json @@ -505,6 +505,12 @@ "know_server": "أعرف عنوان خادمي", "known_server_desc": "نعرف هذا الخادم بالفعل، لذا لا حاجة إلى إعداد", "known_server_title": "حساب آخر على {host}", + "link_confirm_connect": "يطلب رابط تسجيل الدخول في هذه الصفحة. لا تتابع إلا إذا كنت تستخدم {host} لبريدك.", + "link_confirm_connect_add": "يطلب رابط إضافة حساب بتسجيل الدخول في هذه الصفحة. لا تتابع إلا إذا كنت تستخدم {host} لبريدك.", + "link_confirm_connect_title": "فتح صفحة تسجيل الدخول في {host}؟", + "link_confirm_pair": "يسجّل هذا الرابط الدخول إلى حساب من {host}. لا تتابع إلا إذا كنت قد عرضت للتو رمز تسجيل دخول على حاسوبك.", + "link_confirm_pair_add": "يضيف هذا الرابط حسابًا من {host} وينقل التطبيق إليه. لا تتابع إلا إذا كنت قد عرضت للتو رمز تسجيل دخول على حاسوبك.", + "link_confirm_title": "تسجيل الدخول برابط؟", "looking_up": "جارٍ البحث عن خادمك…", "manual": "إدخال تفاصيل الخادم يدويًا", "notice_bad_qr": "هذا الرمز ليس رمز تسجيل دخول Bulwark", diff --git a/locales/rn/ca.json b/locales/rn/ca.json index f081bbff..3d08d711 100644 --- a/locales/rn/ca.json +++ b/locales/rn/ca.json @@ -505,6 +505,12 @@ "know_server": "Conec l'adreça del meu servidor", "known_server_desc": "Ja coneixem aquest servidor, així que no cal configurar res", "known_server_title": "Un altre compte a {host}", + "link_confirm_connect": "Un enllaç demana iniciar la sessió en aquesta pàgina. Continueu només si utilitzeu {host} per al correu.", + "link_confirm_connect_add": "Un enllaç demana afegir un compte iniciant la sessió en aquesta pàgina. Continueu només si utilitzeu {host} per al correu.", + "link_confirm_connect_title": "Voleu obrir la pàgina d'inici de sessió de {host}?", + "link_confirm_pair": "Aquest enllaç inicia la sessió en un compte de {host}. Continueu només si acabeu de mostrar un codi d'inici de sessió a l'ordinador.", + "link_confirm_pair_add": "Aquest enllaç afegeix un compte de {host} i hi canvia l'aplicació. Continueu només si acabeu de mostrar un codi d'inici de sessió a l'ordinador.", + "link_confirm_title": "Voleu iniciar la sessió amb un enllaç?", "looking_up": "S'està cercant el vostre servidor…", "manual": "Introdueix les dades del servidor manualment", "notice_bad_qr": "Aquest codi no és un codi d'inici de sessió del Bulwark", diff --git a/locales/rn/cs.json b/locales/rn/cs.json index f05efcf4..6b719370 100644 --- a/locales/rn/cs.json +++ b/locales/rn/cs.json @@ -505,6 +505,12 @@ "know_server": "Znám adresu svého serveru", "known_server_desc": "Tento server už známe, takže není třeba nic nastavovat", "known_server_title": "Další účet na {host}", + "link_confirm_connect": "Odkaz žádá o přihlášení na této stránce. Pokračujte, jen pokud {host} používáte pro svůj e-mail.", + "link_confirm_connect_add": "Odkaz žádá o přidání účtu přihlášením na této stránce. Pokračujte, jen pokud {host} používáte pro svůj e-mail.", + "link_confirm_connect_title": "Otevřít přihlašovací stránku {host}?", + "link_confirm_pair": "Tento odkaz vás přihlásí k účtu z {host}. Pokračujte, jen pokud jste právě na počítači zobrazili přihlašovací kód.", + "link_confirm_pair_add": "Tento odkaz přidá účet z {host} a přepne na něj aplikaci. Pokračujte, jen pokud jste právě na počítači zobrazili přihlašovací kód.", + "link_confirm_title": "Přihlásit se pomocí odkazu?", "looking_up": "Hledání vašeho serveru…", "manual": "Zadat údaje serveru ručně", "notice_bad_qr": "Toto není přihlašovací kód Bulwark", diff --git a/locales/rn/da.json b/locales/rn/da.json index f61a818f..fdd4c96d 100644 --- a/locales/rn/da.json +++ b/locales/rn/da.json @@ -505,6 +505,12 @@ "know_server": "Jeg kender min serveradresse", "known_server_desc": "Vi kender allerede denne server, så der skal ikke konfigureres noget", "known_server_title": "En anden konto på {host}", + "link_confirm_connect": "Et link beder om at logge ind på denne side. Fortsæt kun, hvis du bruger {host} til din mail.", + "link_confirm_connect_add": "Et link beder om at tilføje en konto ved at logge ind på denne side. Fortsæt kun, hvis du bruger {host} til din mail.", + "link_confirm_connect_title": "Åbn login-siden for {host}?", + "link_confirm_pair": "Dette link logger ind på en konto fra {host}. Fortsæt kun, hvis du lige har vist en loginkode på din computer.", + "link_confirm_pair_add": "Dette link tilføjer en konto fra {host} og skifter appen til den. Fortsæt kun, hvis du lige har vist en loginkode på din computer.", + "link_confirm_title": "Log ind med et link?", "looking_up": "Slår din server op…", "manual": "Indtast serveroplysninger manuelt", "notice_bad_qr": "Den kode er ikke en Bulwark-loginkode", diff --git a/locales/rn/de.json b/locales/rn/de.json index 3610618f..542eb303 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -505,6 +505,12 @@ "know_server": "Ich kenne meine Serveradresse", "known_server_desc": "Diesen Server kennen wir bereits, daher ist keine Einrichtung nötig", "known_server_title": "Weiteres Konto auf {host}", + "link_confirm_connect": "Ein Link möchte Sie auf dieser Seite anmelden. Fahren Sie nur fort, wenn Sie {host} für Ihre E-Mails verwenden.", + "link_confirm_connect_add": "Ein Link möchte über die Anmeldung auf dieser Seite ein Konto hinzufügen. Fahren Sie nur fort, wenn Sie {host} für Ihre E-Mails verwenden.", + "link_confirm_connect_title": "Anmeldeseite von {host} öffnen?", + "link_confirm_pair": "Dieser Link meldet Sie bei einem Konto von {host} an. Fahren Sie nur fort, wenn Sie gerade auf Ihrem Computer einen Anmeldecode angezeigt haben.", + "link_confirm_pair_add": "Dieser Link fügt ein Konto von {host} hinzu und wechselt die App zu diesem Konto. Fahren Sie nur fort, wenn Sie gerade auf Ihrem Computer einen Anmeldecode angezeigt haben.", + "link_confirm_title": "Mit einem Link anmelden?", "looking_up": "Ihr Server wird gesucht…", "manual": "Serverdaten manuell eingeben", "notice_bad_qr": "Das ist kein Bulwark-Anmeldecode", diff --git a/locales/rn/en.json b/locales/rn/en.json index c1e98852..460f309d 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -515,6 +515,12 @@ "know_server": "I know my server address", "known_server_desc": "We already know this server, so there's no setup", "known_server_title": "Another account on {host}", + "link_confirm_connect": "A link asks to sign in on this page. Only continue if you use {host} for your mail.", + "link_confirm_connect_add": "A link asks to add an account by signing in on this page. Only continue if you use {host} for your mail.", + "link_confirm_connect_title": "Open the sign-in page of {host}?", + "link_confirm_pair": "This link signs in to an account from {host}. Only continue if you just showed a sign-in code on your computer.", + "link_confirm_pair_add": "This link adds an account from {host} and switches the app to it. Only continue if you just showed a sign-in code on your computer.", + "link_confirm_title": "Sign in with a link?", "looking_up": "Looking up your server…", "manual": "Enter server details manually", "notice_bad_qr": "That code isn't a Bulwark sign-in code", diff --git a/locales/rn/es.json b/locales/rn/es.json index a0ee968e..296b91ea 100644 --- a/locales/rn/es.json +++ b/locales/rn/es.json @@ -505,6 +505,12 @@ "know_server": "Conozco la dirección de mi servidor", "known_server_desc": "Ya conocemos este servidor, así que no hay nada que configurar", "known_server_title": "Otra cuenta en {host}", + "link_confirm_connect": "Un enlace pide iniciar sesión en esta página. Continúa solo si usas {host} para tu correo.", + "link_confirm_connect_add": "Un enlace pide añadir una cuenta iniciando sesión en esta página. Continúa solo si usas {host} para tu correo.", + "link_confirm_connect_title": "¿Abrir la página de inicio de sesión de {host}?", + "link_confirm_pair": "Este enlace inicia sesión en una cuenta de {host}. Continúa solo si acabas de mostrar un código de inicio de sesión en tu ordenador.", + "link_confirm_pair_add": "Este enlace añade una cuenta de {host} y cambia la aplicación a ella. Continúa solo si acabas de mostrar un código de inicio de sesión en tu ordenador.", + "link_confirm_title": "¿Iniciar sesión con un enlace?", "looking_up": "Buscando tu servidor…", "manual": "Introducir los datos del servidor manualmente", "notice_bad_qr": "Ese código no es un código de inicio de sesión de Bulwark", diff --git a/locales/rn/fa.json b/locales/rn/fa.json index 8b82e9b6..5c63c316 100644 --- a/locales/rn/fa.json +++ b/locales/rn/fa.json @@ -505,6 +505,12 @@ "know_server": "نشانی سرورم را می‌دانم", "known_server_desc": "این سرور را از قبل می‌شناسیم، پس نیازی به راه‌اندازی نیست", "known_server_title": "حسابی دیگر در {host}", + "link_confirm_connect": "یک پیوند می‌خواهد در این صفحه وارد شوید. فقط اگر برای ایمیل‌تان از {host} استفاده می‌کنید ادامه دهید.", + "link_confirm_connect_add": "یک پیوند می‌خواهد با ورود در این صفحه حسابی اضافه کنید. فقط اگر برای ایمیل‌تان از {host} استفاده می‌کنید ادامه دهید.", + "link_confirm_connect_title": "صفحه ورود {host} باز شود؟", + "link_confirm_pair": "این پیوند شما را به حسابی از {host} وارد می‌کند. فقط اگر همین حالا روی رایانه‌تان کد ورود نمایش داده‌اید ادامه دهید.", + "link_confirm_pair_add": "این پیوند حسابی از {host} را اضافه می‌کند و برنامه را به آن حساب می‌برد. فقط اگر همین حالا روی رایانه‌تان کد ورود نمایش داده‌اید ادامه دهید.", + "link_confirm_title": "با پیوند وارد می‌شوید؟", "looking_up": "در حال جستجوی سرور شما…", "manual": "وارد کردن دستی مشخصات سرور", "notice_bad_qr": "این کد، کد ورود Bulwark نیست", diff --git a/locales/rn/fr.json b/locales/rn/fr.json index 99d821bf..63a547b9 100644 --- a/locales/rn/fr.json +++ b/locales/rn/fr.json @@ -505,6 +505,12 @@ "know_server": "Je connais l'adresse de mon serveur", "known_server_desc": "Nous connaissons déjà ce serveur, aucune configuration n'est donc nécessaire", "known_server_title": "Un autre compte sur {host}", + "link_confirm_connect": "Un lien demande à vous connecter sur cette page. Ne continuez que si vous utilisez {host} pour vos e-mails.", + "link_confirm_connect_add": "Un lien demande à ajouter un compte en vous connectant sur cette page. Ne continuez que si vous utilisez {host} pour vos e-mails.", + "link_confirm_connect_title": "Ouvrir la page de connexion de {host} ?", + "link_confirm_pair": "Ce lien vous connecte à un compte de {host}. Ne continuez que si vous venez d'afficher un code de connexion sur votre ordinateur.", + "link_confirm_pair_add": "Ce lien ajoute un compte de {host} et fait basculer l'application sur ce compte. Ne continuez que si vous venez d'afficher un code de connexion sur votre ordinateur.", + "link_confirm_title": "Se connecter avec un lien ?", "looking_up": "Recherche de votre serveur…", "manual": "Saisir les informations du serveur manuellement", "notice_bad_qr": "Ce code n'est pas un code de connexion Bulwark", diff --git a/locales/rn/he.json b/locales/rn/he.json index a8aac6f6..127b80a6 100644 --- a/locales/rn/he.json +++ b/locales/rn/he.json @@ -505,6 +505,12 @@ "know_server": "אני יודע את כתובת השרת שלי", "known_server_desc": "השרת הזה כבר מוכר לנו, כך שאין צורך בהגדרה", "known_server_title": "חשבון נוסף ב-{host}", + "link_confirm_connect": "קישור מבקש להתחבר בדף הזה. המשך רק אם אתה משתמש ב-{host} לדואר שלך.", + "link_confirm_connect_add": "קישור מבקש להוסיף חשבון באמצעות התחברות בדף הזה. המשך רק אם אתה משתמש ב-{host} לדואר שלך.", + "link_confirm_connect_title": "לפתוח את דף ההתחברות של {host}?", + "link_confirm_pair": "הקישור הזה מחבר לחשבון מ-{host}. המשך רק אם הצגת עכשיו קוד התחברות במחשב.", + "link_confirm_pair_add": "הקישור הזה מוסיף חשבון מ-{host} ומעביר אליו את היישום. המשך רק אם הצגת עכשיו קוד התחברות במחשב.", + "link_confirm_title": "להתחבר עם קישור?", "looking_up": "מחפש את השרת שלך…", "manual": "הזן את פרטי השרת ידנית", "notice_bad_qr": "הקוד הזה אינו קוד התחברות של Bulwark", diff --git a/locales/rn/hu.json b/locales/rn/hu.json index 5cd2e5b7..6affa044 100644 --- a/locales/rn/hu.json +++ b/locales/rn/hu.json @@ -505,6 +505,12 @@ "know_server": "Ismerem a szerverem címét", "known_server_desc": "Ezt a szervert már ismerjük, így nincs mit beállítani", "known_server_title": "Másik fiók itt: {host}", + "link_confirm_connect": "Egy hivatkozás bejelentkezést kér ezen az oldalon. Csak akkor folytasd, ha a(z) {host} címet használod a leveleidhez.", + "link_confirm_connect_add": "Egy hivatkozás egy fiók hozzáadását kéri bejelentkezéssel ezen az oldalon. Csak akkor folytasd, ha a(z) {host} címet használod a leveleidhez.", + "link_confirm_connect_title": "Megnyitod a(z) {host} bejelentkezési oldalát?", + "link_confirm_pair": "Ez a hivatkozás bejelentkeztet egy fiókba itt: {host}. Csak akkor folytasd, ha az imént jelenítettél meg bejelentkezési kódot a számítógépeden.", + "link_confirm_pair_add": "Ez a hivatkozás hozzáad egy fiókot innen: {host}, és átváltja rá az alkalmazást. Csak akkor folytasd, ha az imént jelenítettél meg bejelentkezési kódot a számítógépeden.", + "link_confirm_title": "Bejelentkezel egy hivatkozással?", "looking_up": "Szerver keresése…", "manual": "Szerveradatok kézi megadása", "notice_bad_qr": "Ez nem Bulwark bejelentkezési kód", diff --git a/locales/rn/it.json b/locales/rn/it.json index 6d9ff388..14b57121 100644 --- a/locales/rn/it.json +++ b/locales/rn/it.json @@ -505,6 +505,12 @@ "know_server": "Conosco l'indirizzo del mio server", "known_server_desc": "Conosciamo già questo server, quindi non c'è nulla da configurare", "known_server_title": "Un altro account su {host}", + "link_confirm_connect": "Un link chiede di accedere su questa pagina. Continua solo se usi {host} per la tua posta.", + "link_confirm_connect_add": "Un link chiede di aggiungere un account accedendo su questa pagina. Continua solo se usi {host} per la tua posta.", + "link_confirm_connect_title": "Aprire la pagina di accesso di {host}?", + "link_confirm_pair": "Questo link ti fa accedere a un account di {host}. Continua solo se hai appena mostrato un codice di accesso sul computer.", + "link_confirm_pair_add": "Questo link aggiunge un account di {host} e passa l'app a quell'account. Continua solo se hai appena mostrato un codice di accesso sul computer.", + "link_confirm_title": "Accedere con un link?", "looking_up": "Ricerca del tuo server…", "manual": "Inserisci i dati del server manualmente", "notice_bad_qr": "Questo codice non è un codice di accesso Bulwark", diff --git a/locales/rn/ja.json b/locales/rn/ja.json index 14374f3c..69ef4a74 100644 --- a/locales/rn/ja.json +++ b/locales/rn/ja.json @@ -505,6 +505,12 @@ "know_server": "サーバーアドレスがわかる", "known_server_desc": "このサーバーは登録済みのため、設定は不要です", "known_server_title": "{host}の別のアカウント", + "link_confirm_connect": "リンクがこのページでのサインインを求めています。メールに {host} を使っている場合にのみ続行してください。", + "link_confirm_connect_add": "リンクがこのページでサインインしてアカウントを追加するよう求めています。メールに {host} を使っている場合にのみ続行してください。", + "link_confirm_connect_title": "{host} のサインインページを開きますか?", + "link_confirm_pair": "このリンクは {host} のアカウントにサインインします。パソコンでサインインコードを表示したばかりの場合にのみ続行してください。", + "link_confirm_pair_add": "このリンクは {host} のアカウントを追加し、アプリをそのアカウントに切り替えます。パソコンでサインインコードを表示したばかりの場合にのみ続行してください。", + "link_confirm_title": "リンクでサインインしますか?", "looking_up": "サーバーを検索しています…", "manual": "サーバー情報を手動で入力", "notice_bad_qr": "このコードは Bulwark のサインインコードではありません", diff --git a/locales/rn/ko.json b/locales/rn/ko.json index 581e9516..025365af 100644 --- a/locales/rn/ko.json +++ b/locales/rn/ko.json @@ -505,6 +505,12 @@ "know_server": "서버 주소를 알고 있어요", "known_server_desc": "이미 알고 있는 서버라서 설정이 필요 없어요", "known_server_title": "{host}의 다른 계정", + "link_confirm_connect": "링크가 이 페이지에서 로그인하도록 요청하고 있어요. 메일에 {host}을(를) 사용하는 경우에만 계속하세요.", + "link_confirm_connect_add": "링크가 이 페이지에서 로그인해 계정을 추가하도록 요청하고 있어요. 메일에 {host}을(를) 사용하는 경우에만 계속하세요.", + "link_confirm_connect_title": "{host}의 로그인 페이지를 열까요?", + "link_confirm_pair": "이 링크는 {host}의 계정으로 로그인해요. 방금 컴퓨터에서 로그인 코드를 표시한 경우에만 계속하세요.", + "link_confirm_pair_add": "이 링크는 {host}의 계정을 추가하고 앱을 해당 계정으로 전환해요. 방금 컴퓨터에서 로그인 코드를 표시한 경우에만 계속하세요.", + "link_confirm_title": "링크로 로그인할까요?", "looking_up": "서버를 찾는 중…", "manual": "서버 정보 직접 입력", "notice_bad_qr": "Bulwark 로그인 코드가 아니에요", diff --git a/locales/rn/lv.json b/locales/rn/lv.json index a129314a..016b11ec 100644 --- a/locales/rn/lv.json +++ b/locales/rn/lv.json @@ -505,6 +505,12 @@ "know_server": "Es zinu sava servera adresi", "known_server_desc": "Šis serveris mums jau ir zināms, tāpēc nekas nav jāiestata", "known_server_title": "Vēl viens konts: {host}", + "link_confirm_connect": "Saite aicina pierakstīties šajā lapā. Turpiniet tikai tad, ja izmantojat {host} savam e-pastam.", + "link_confirm_connect_add": "Saite aicina pievienot kontu, pierakstoties šajā lapā. Turpiniet tikai tad, ja izmantojat {host} savam e-pastam.", + "link_confirm_connect_title": "Atvērt {host} pierakstīšanās lapu?", + "link_confirm_pair": "Šī saite pierakstās kontā no {host}. Turpiniet tikai tad, ja tikko datorā parādījāt pierakstīšanās kodu.", + "link_confirm_pair_add": "Šī saite pievieno kontu no {host} un pārslēdz lietotni uz to. Turpiniet tikai tad, ja tikko datorā parādījāt pierakstīšanās kodu.", + "link_confirm_title": "Pierakstīties ar saiti?", "looking_up": "Meklē jūsu serveri…", "manual": "Ievadīt servera datus manuāli", "notice_bad_qr": "Šis nav Bulwark pierakstīšanās kods", diff --git a/locales/rn/mn.json b/locales/rn/mn.json index 9762e509..2a1f8ec4 100644 --- a/locales/rn/mn.json +++ b/locales/rn/mn.json @@ -505,6 +505,12 @@ "know_server": "Би серверийн хаягаа мэднэ", "known_server_desc": "Бид энэ серверийг аль хэдийн мэддэг тул тохируулах шаардлагагүй", "known_server_title": "{host} дээрх өөр бүртгэл", + "link_confirm_connect": "Холбоос энэ хуудсанд нэвтрэхийг хүсэж байна. Та имэйлдээ {host}-ийг ашигладаг бол л үргэлжлүүлнэ үү.", + "link_confirm_connect_add": "Холбоос энэ хуудсанд нэвтэрч бүртгэл нэмэхийг хүсэж байна. Та имэйлдээ {host}-ийг ашигладаг бол л үргэлжлүүлнэ үү.", + "link_confirm_connect_title": "{host}-ийн нэвтрэх хуудсыг нээх үү?", + "link_confirm_pair": "Энэ холбоос {host} дээрх бүртгэлд нэвтрүүлнэ. Та саяхан компьютер дээрээ нэвтрэх код харуулсан бол л үргэлжлүүлнэ үү.", + "link_confirm_pair_add": "Энэ холбоос {host} дээрх бүртгэлийг нэмээд програмыг түүн рүү шилжүүлнэ. Та саяхан компьютер дээрээ нэвтрэх код харуулсан бол л үргэлжлүүлнэ үү.", + "link_confirm_title": "Холбоосоор нэвтрэх үү?", "looking_up": "Таны серверийг хайж байна…", "manual": "Серверийн мэдээллийг гараар оруулах", "notice_bad_qr": "Энэ код Bulwark-ийн нэвтрэх код биш байна", diff --git a/locales/rn/nb.json b/locales/rn/nb.json index a9ea2598..5f05000d 100644 --- a/locales/rn/nb.json +++ b/locales/rn/nb.json @@ -505,6 +505,12 @@ "know_server": "Jeg kjenner serveradressen min", "known_server_desc": "Vi kjenner allerede denne serveren, så du trenger ikke å sette opp noe", "known_server_title": "En annen konto på {host}", + "link_confirm_connect": "En lenke ber om å logge på på denne siden. Fortsett bare hvis du bruker {host} til e-posten din.", + "link_confirm_connect_add": "En lenke ber om å legge til en konto ved å logge på på denne siden. Fortsett bare hvis du bruker {host} til e-posten din.", + "link_confirm_connect_title": "Åpne påloggingssiden til {host}?", + "link_confirm_pair": "Denne lenken logger på en konto fra {host}. Fortsett bare hvis du nettopp viste en påloggingskode på datamaskinen.", + "link_confirm_pair_add": "Denne lenken legger til en konto fra {host} og bytter appen til den. Fortsett bare hvis du nettopp viste en påloggingskode på datamaskinen.", + "link_confirm_title": "Logge på med en lenke?", "looking_up": "Slår opp serveren din…", "manual": "Skriv inn serverdetaljer manuelt", "notice_bad_qr": "Koden er ikke en påloggingskode for Bulwark", diff --git a/locales/rn/nl.json b/locales/rn/nl.json index 2b7d732d..567917eb 100644 --- a/locales/rn/nl.json +++ b/locales/rn/nl.json @@ -505,6 +505,12 @@ "know_server": "Ik ken mijn serveradres", "known_server_desc": "We kennen deze server al, dus er hoeft niets te worden ingesteld", "known_server_title": "Nog een account op {host}", + "link_confirm_connect": "Een link vraagt om op deze pagina in te loggen. Ga alleen door als u {host} voor uw e-mail gebruikt.", + "link_confirm_connect_add": "Een link vraagt om een account toe te voegen door op deze pagina in te loggen. Ga alleen door als u {host} voor uw e-mail gebruikt.", + "link_confirm_connect_title": "Inlogpagina van {host} openen?", + "link_confirm_pair": "Deze link logt u in op een account van {host}. Ga alleen door als u zojuist een inlogcode op uw computer hebt getoond.", + "link_confirm_pair_add": "Deze link voegt een account van {host} toe en schakelt de app daarnaar over. Ga alleen door als u zojuist een inlogcode op uw computer hebt getoond.", + "link_confirm_title": "Inloggen met een link?", "looking_up": "Uw server opzoeken…", "manual": "Servergegevens handmatig invoeren", "notice_bad_qr": "Die code is geen Bulwark-inlogcode", diff --git a/locales/rn/pl.json b/locales/rn/pl.json index 43fa5e4f..e077fdca 100644 --- a/locales/rn/pl.json +++ b/locales/rn/pl.json @@ -505,6 +505,12 @@ "know_server": "Znam adres swojego serwera", "known_server_desc": "Ten serwer jest już znany, więc nie trzeba niczego konfigurować", "known_server_title": "Kolejne konto na {host}", + "link_confirm_connect": "Link prosi o zalogowanie się na tej stronie. Kontynuuj tylko, jeśli używasz {host} do poczty.", + "link_confirm_connect_add": "Link prosi o dodanie konta przez zalogowanie się na tej stronie. Kontynuuj tylko, jeśli używasz {host} do poczty.", + "link_confirm_connect_title": "Otworzyć stronę logowania {host}?", + "link_confirm_pair": "Ten link loguje do konta z {host}. Kontynuuj tylko, jeśli kod logowania został właśnie wyświetlony na Twoim komputerze.", + "link_confirm_pair_add": "Ten link dodaje konto z {host} i przełącza na nie aplikację. Kontynuuj tylko, jeśli kod logowania został właśnie wyświetlony na Twoim komputerze.", + "link_confirm_title": "Zalogować się za pomocą linku?", "looking_up": "Wyszukiwanie serwera…", "manual": "Wprowadź dane serwera ręcznie", "notice_bad_qr": "To nie jest kod logowania Bulwark", diff --git a/locales/rn/pt.json b/locales/rn/pt.json index 67b05864..14bbf396 100644 --- a/locales/rn/pt.json +++ b/locales/rn/pt.json @@ -505,6 +505,12 @@ "know_server": "Sei o endereço do meu servidor", "known_server_desc": "Já conhecemos este servidor, então não há nada para configurar", "known_server_title": "Outra conta em {host}", + "link_confirm_connect": "Um link pede para fazer login nesta página. Só continue se você usa {host} para o seu e-mail.", + "link_confirm_connect_add": "Um link pede para adicionar uma conta fazendo login nesta página. Só continue se você usa {host} para o seu e-mail.", + "link_confirm_connect_title": "Abrir a página de login de {host}?", + "link_confirm_pair": "Este link faz login em uma conta de {host}. Só continue se você acabou de mostrar um código de login no computador.", + "link_confirm_pair_add": "Este link adiciona uma conta de {host} e muda o aplicativo para ela. Só continue se você acabou de mostrar um código de login no computador.", + "link_confirm_title": "Entrar com um link?", "looking_up": "Procurando seu servidor…", "manual": "Inserir os dados do servidor manualmente", "notice_bad_qr": "Esse código não é um código de login do Bulwark", diff --git a/locales/rn/ro.json b/locales/rn/ro.json index 42372486..2444166b 100644 --- a/locales/rn/ro.json +++ b/locales/rn/ro.json @@ -505,6 +505,12 @@ "know_server": "Cunosc adresa serverului meu", "known_server_desc": "Cunoaștem deja acest server, deci nu este nimic de configurat", "known_server_title": "Alt cont pe {host}", + "link_confirm_connect": "Un link cere autentificarea pe această pagină. Continuați doar dacă folosiți {host} pentru e-mail.", + "link_confirm_connect_add": "Un link cere adăugarea unui cont prin autentificare pe această pagină. Continuați doar dacă folosiți {host} pentru e-mail.", + "link_confirm_connect_title": "Deschideți pagina de autentificare a {host}?", + "link_confirm_pair": "Acest link vă autentifică într-un cont de la {host}. Continuați doar dacă tocmai ați afișat un cod de autentificare pe computer.", + "link_confirm_pair_add": "Acest link adaugă un cont de la {host} și comută aplicația pe el. Continuați doar dacă tocmai ați afișat un cod de autentificare pe computer.", + "link_confirm_title": "Vă autentificați cu un link?", "looking_up": "Se caută serverul dvs.…", "manual": "Introduceți manual detaliile serverului", "notice_bad_qr": "Acesta nu este un cod de autentificare Bulwark", diff --git a/locales/rn/ru.json b/locales/rn/ru.json index 816c6d31..d958ba18 100644 --- a/locales/rn/ru.json +++ b/locales/rn/ru.json @@ -505,6 +505,12 @@ "know_server": "Я знаю адрес своего сервера", "known_server_desc": "Этот сервер уже известен, настройка не нужна", "known_server_title": "Ещё один аккаунт на {host}", + "link_confirm_connect": "Ссылка предлагает войти на этой странице. Продолжайте, только если вы пользуетесь почтой на {host}.", + "link_confirm_connect_add": "Ссылка предлагает добавить аккаунт, войдя на этой странице. Продолжайте, только если вы пользуетесь почтой на {host}.", + "link_confirm_connect_title": "Открыть страницу входа {host}?", + "link_confirm_pair": "Эта ссылка выполняет вход в аккаунт на {host}. Продолжайте, только если вы только что показали код входа на своём компьютере.", + "link_confirm_pair_add": "Эта ссылка добавляет аккаунт с {host} и переключает на него приложение. Продолжайте, только если вы только что показали код входа на своём компьютере.", + "link_confirm_title": "Войти по ссылке?", "looking_up": "Ищем ваш сервер…", "manual": "Ввести данные сервера вручную", "notice_bad_qr": "Это не код входа Bulwark", diff --git a/locales/rn/sk.json b/locales/rn/sk.json index 98b8a709..3bef1695 100644 --- a/locales/rn/sk.json +++ b/locales/rn/sk.json @@ -505,6 +505,12 @@ "know_server": "Poznám adresu svojho servera", "known_server_desc": "Tento server už poznáme, takže nie je potrebné nič nastavovať", "known_server_title": "Ďalší účet na {host}", + "link_confirm_connect": "Odkaz žiada o prihlásenie na tejto stránke. Pokračujte, iba ak {host} používate na svoj e-mail.", + "link_confirm_connect_add": "Odkaz žiada o pridanie účtu prihlásením na tejto stránke. Pokračujte, iba ak {host} používate na svoj e-mail.", + "link_confirm_connect_title": "Otvoriť prihlasovaciu stránku {host}?", + "link_confirm_pair": "Tento odkaz vás prihlási do účtu z {host}. Pokračujte, iba ak ste práve na počítači zobrazili prihlasovací kód.", + "link_confirm_pair_add": "Tento odkaz pridá účet z {host} a prepne naň aplikáciu. Pokračujte, iba ak ste práve na počítači zobrazili prihlasovací kód.", + "link_confirm_title": "Prihlásiť sa pomocou odkazu?", "looking_up": "Hľadá sa váš server…", "manual": "Zadať údaje servera ručne", "notice_bad_qr": "Toto nie je prihlasovací kód Bulwark", diff --git a/locales/rn/tr.json b/locales/rn/tr.json index 07934f97..497bd659 100644 --- a/locales/rn/tr.json +++ b/locales/rn/tr.json @@ -505,6 +505,12 @@ "know_server": "Sunucu adresimi biliyorum", "known_server_desc": "Bu sunucuyu zaten tanıyoruz, kurulum gerekmiyor", "known_server_title": "{host} üzerinde başka bir hesap", + "link_confirm_connect": "Bir bağlantı bu sayfada giriş yapmanızı istiyor. Yalnızca e-postanız için {host} kullanıyorsanız devam edin.", + "link_confirm_connect_add": "Bir bağlantı bu sayfada giriş yaparak hesap eklemenizi istiyor. Yalnızca e-postanız için {host} kullanıyorsanız devam edin.", + "link_confirm_connect_title": "{host} giriş sayfası açılsın mı?", + "link_confirm_pair": "Bu bağlantı {host} üzerindeki bir hesapta oturum açar. Yalnızca bilgisayarınızda az önce bir giriş kodu gösterdiyseniz devam edin.", + "link_confirm_pair_add": "Bu bağlantı {host} üzerindeki bir hesabı ekler ve uygulamayı bu hesaba geçirir. Yalnızca bilgisayarınızda az önce bir giriş kodu gösterdiyseniz devam edin.", + "link_confirm_title": "Bağlantıyla giriş yapılsın mı?", "looking_up": "Sunucunuz aranıyor…", "manual": "Sunucu bilgilerini elle girin", "notice_bad_qr": "Bu bir Bulwark giriş kodu değil", diff --git a/locales/rn/uk.json b/locales/rn/uk.json index 3aac14cf..3f70488f 100644 --- a/locales/rn/uk.json +++ b/locales/rn/uk.json @@ -505,6 +505,12 @@ "know_server": "Я знаю адресу свого сервера", "known_server_desc": "Цей сервер уже відомий, налаштування не потрібне", "known_server_title": "Ще один обліковий запис на {host}", + "link_confirm_connect": "Посилання пропонує увійти на цій сторінці. Продовжуйте, лише якщо ви користуєтеся поштою на {host}.", + "link_confirm_connect_add": "Посилання пропонує додати обліковий запис, увійшовши на цій сторінці. Продовжуйте, лише якщо ви користуєтеся поштою на {host}.", + "link_confirm_connect_title": "Відкрити сторінку входу {host}?", + "link_confirm_pair": "Це посилання виконує вхід в обліковий запис на {host}. Продовжуйте, лише якщо ви щойно показали код входу на своєму комп'ютері.", + "link_confirm_pair_add": "Це посилання додає обліковий запис з {host} і перемикає на нього застосунок. Продовжуйте, лише якщо ви щойно показали код входу на своєму комп'ютері.", + "link_confirm_title": "Увійти за посиланням?", "looking_up": "Шукаємо ваш сервер…", "manual": "Ввести дані сервера вручну", "notice_bad_qr": "Це не код входу Bulwark", diff --git a/locales/rn/zh-TW.json b/locales/rn/zh-TW.json index 0e0273e5..89ea4dc7 100644 --- a/locales/rn/zh-TW.json +++ b/locales/rn/zh-TW.json @@ -505,6 +505,12 @@ "know_server": "我知道伺服器地址", "known_server_desc": "我們已知道此伺服器,不需要設定", "known_server_title": "{host} 上的其他帳號", + "link_confirm_connect": "有連結要求在此頁面登入。只有在您的郵件使用 {host} 時才繼續。", + "link_confirm_connect_add": "有連結要求透過在此頁面登入來新增帳號。只有在您的郵件使用 {host} 時才繼續。", + "link_confirm_connect_title": "要開啟 {host} 的登入頁面嗎?", + "link_confirm_pair": "此連結會登入 {host} 上的帳號。只有在您剛在電腦上顯示登入碼時才繼續。", + "link_confirm_pair_add": "此連結會新增 {host} 上的帳號,並將應用程式切換到該帳號。只有在您剛在電腦上顯示登入碼時才繼續。", + "link_confirm_title": "要使用連結登入嗎?", "looking_up": "正在尋找您的伺服器…", "manual": "手動輸入伺服器資訊", "notice_bad_qr": "此代碼不是 Bulwark 登入碼", diff --git a/locales/rn/zh.json b/locales/rn/zh.json index ed7bd8af..f07891c2 100644 --- a/locales/rn/zh.json +++ b/locales/rn/zh.json @@ -505,6 +505,12 @@ "know_server": "我知道服务器地址", "known_server_desc": "我们已经知道此服务器,无需设置", "known_server_title": "{host} 上的其他账户", + "link_confirm_connect": "有链接请求在此页面登录。仅当您的邮箱使用 {host} 时才继续。", + "link_confirm_connect_add": "有链接请求通过在此页面登录来添加账户。仅当您的邮箱使用 {host} 时才继续。", + "link_confirm_connect_title": "打开 {host} 的登录页面?", + "link_confirm_pair": "此链接将登录 {host} 上的账户。仅当您刚刚在电脑上显示了登录码时才继续。", + "link_confirm_pair_add": "此链接将添加 {host} 上的账户,并将应用切换到该账户。仅当您刚刚在电脑上显示了登录码时才继续。", + "link_confirm_title": "使用链接登录?", "looking_up": "正在查找您的服务器…", "manual": "手动输入服务器信息", "notice_bad_qr": "该代码不是 Bulwark 登录码", diff --git a/src/components/PasteSignInLinkModal.tsx b/src/components/PasteSignInLinkModal.tsx index 403fd964..b962f585 100644 --- a/src/components/PasteSignInLinkModal.tsx +++ b/src/components/PasteSignInLinkModal.tsx @@ -17,7 +17,8 @@ import { spacing, radius, typography, type ThemePalette } from '../theme/tokens' import { useColors } from '../theme/colors'; import Button from './Button'; import { useLocaleStore } from '../stores/locale-store'; -import { parsePastedSignInLink, type QrLoginPayload } from '../lib/oauth'; +import { insecurePairingLinkError, parsePastedSignInLink, type QrLoginPayload } from '../lib/oauth'; +import { describeLoginError } from '../lib/login-errors'; interface PasteSignInLinkModalProps { visible: boolean; @@ -63,6 +64,13 @@ export function PasteSignInLinkModal({ visible, onClose, onSubmit }: PasteSignIn const submit = React.useCallback(() => { const payload = parsePastedSignInLink(text); if (!payload) { + // A pairing link for a plain-http webmail: say why it won't be used. + const insecure = insecurePairingLinkError(text); + if (insecure) { + const copy = describeLoginError(insecure, { t }); + setError(copy.detail ?? copy.title); + return; + } setError(t('login.mobile.paste_invalid', "That isn't a Bulwark sign-in link")); return; } diff --git a/src/lib/__tests__/login-errors.test.ts b/src/lib/__tests__/login-errors.test.ts index c6ad607e..02aca1e2 100644 --- a/src/lib/__tests__/login-errors.test.ts +++ b/src/lib/__tests__/login-errors.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { describeLoginError } from '../login-errors'; -import { PairingError, type PairingErrorReason } from '../oauth'; +import { PairingError, insecurePairingLinkError, type PairingErrorReason } from '../oauth'; import { AccountLimitError } from '../account-utils'; function named(name: string, message: string): Error { @@ -31,6 +31,14 @@ describe('describeLoginError', () => { expect(describeLoginError('Maximum of 10 accounts reached')).toEqual(want); }); + it('explains a scanned, pasted or tapped pairing link for a plain-http webmail', () => { + const link = `bulwarkmail://pair?server=${encodeURIComponent('http://webmail.example.org/mail')}&code=${'a1'.repeat(32)}`; + expect(describeLoginError(insecurePairingLinkError(link))).toEqual({ + title: "This sign-in can't be trusted", + detail: 'webmail.example.org wants to use an unencrypted connection.', + }); + }); + it('names the host it could not reach', () => { const copy = describeLoginError(named('NetworkError', 'Network request failed'), { serverUrl: 'https://mail.example.com', diff --git a/src/lib/__tests__/oauth-pairing.test.ts b/src/lib/__tests__/oauth-pairing.test.ts index 5f89b175..978ce3b0 100644 --- a/src/lib/__tests__/oauth-pairing.test.ts +++ b/src/lib/__tests__/oauth-pairing.test.ts @@ -12,10 +12,13 @@ import { PAIRING_REDEEM_TIMEOUT_MS, PairingError, TransientRefreshError, + insecurePairingLinkError, + isInsecurePairingUrl, parsePastedSignInLink, parseQrLoginPayload, redeemPairingCode, refreshOAuthAccessToken, + signInLinkHost, } from '../oauth'; const CODE = 'a1'.repeat(32); @@ -110,6 +113,64 @@ describe('parsePastedSignInLink', () => { }); }); +describe('insecurePairingLinkError', () => { + const httpLink = `bulwarkmail://pair?server=${encodeURIComponent('http://Mail.Example.com:8080/webmail')}&code=${CODE}`; + + it('says why a pairing link for a plain-http webmail is refused', () => { + // Still refused by the parser: nothing can redeem it. + expect(parseQrLoginPayload(httpLink)).toBeNull(); + expect(parsePastedSignInLink(`Open ${httpLink} on your phone.`)).toBeNull(); + + for (const text of [httpLink, `Open ${httpLink}.`, `bulwarkmail://pair/?code=${CODE}&server=http://mail.example.com:8080`]) { + const err = insecurePairingLinkError(text); + expect(err).toBeInstanceOf(PairingError); + expect(err).toMatchObject({ reason: 'insecure', host: 'mail.example.com:8080' }); + } + }); + + it('names the host the code would really go to', () => { + const spoof = `bulwarkmail://pair?server=${encodeURIComponent('http://webmail.example.org@attacker.example')}&code=${CODE}`; + expect(insecurePairingLinkError(spoof)).toMatchObject({ host: 'attacker.example' }); + }); + + it('is null for links that are fine or broken for another reason', () => { + expect(insecurePairingLinkError(`bulwarkmail://pair?server=${encodeURIComponent(WEBMAIL)}&code=${CODE}`)).toBeNull(); + expect(insecurePairingLinkError(`bulwarkmail://pair?server=http%3A%2F%2Flocalhost%3A3000&code=${CODE}`)).toBeNull(); + expect(insecurePairingLinkError('bulwarkmail://pair?server=http://mail.example.com&code=has%20space')).toBeNull(); + expect(insecurePairingLinkError('bulwarkmail://pair?server=http://mail.example.com')).toBeNull(); + expect(insecurePairingLinkError('bulwarkmail://connect?server=http://mail.example.com')).toBeNull(); + expect(insecurePairingLinkError(`bulwarkmail://pair?server=ftp://mail.example.com&code=${CODE}`)).toBeNull(); + expect(insecurePairingLinkError('http://mail.example.com')).toBeNull(); + expect(insecurePairingLinkError('')).toBeNull(); + }); +}); + +describe('isInsecurePairingUrl', () => { + it('allows https and loopback http only', () => { + expect(isInsecurePairingUrl('https://mail.example.com')).toBe(false); + expect(isInsecurePairingUrl('http://localhost:3000')).toBe(false); + expect(isInsecurePairingUrl('http://10.0.2.2:3000')).toBe(false); + expect(isInsecurePairingUrl('http://mail.example.com')).toBe(true); + expect(isInsecurePairingUrl('http://192.168.1.5:3000')).toBe(true); + }); +}); + +describe('signInLinkHost', () => { + it('names the host the link talks to', () => { + expect(signInLinkHost('https://mail.example.com/webmail')).toBe('mail.example.com'); + expect(signInLinkHost('https://Mail.Example.com:8443')).toBe('mail.example.com:8443'); + expect(signInLinkHost('http://127.0.0.1:3000/?x=1')).toBe('127.0.0.1:3000'); + }); + + it('is not fooled by user info or backslashes', () => { + expect(signInLinkHost('https://mail.example.com@attacker.example/webmail')).toBe('attacker.example'); + expect(signInLinkHost('https://a@mail.example.com@attacker.example')).toBe('attacker.example'); + expect(signInLinkHost('https://attacker.example\\@mail.example.com')).toBe('attacker.example'); + expect(signInLinkHost('https://attacker.example#@mail.example.com')).toBe('attacker.example'); + expect(signInLinkHost('https://attacker.example?@mail.example.com')).toBe('attacker.example'); + }); +}); + describe('redeemPairingCode', () => { it('posts the code to the webmail and returns an OAuth bundle for its token proxy', async () => { mockSecureFetch.mockResolvedValueOnce(json(200, { @@ -184,6 +245,12 @@ describe('redeemPairingCode', () => { await expect(redeemPairingCode('http://10.0.2.2:3000', CODE)).resolves.toMatchObject({ serverUrl: 'http://10.0.2.2:8080' }); }); + it('never sends a code to a plain-http webmail', async () => { + await expect(redeemPairingCode('http://webmail.example.org/mail', CODE)) + .rejects.toMatchObject({ name: 'PairingError', reason: 'insecure', host: 'webmail.example.org' }); + expect(mockSecureFetch).not.toHaveBeenCalled(); + }); + it('tells an unknown code from an expired or a used one', async () => { expect((await redeemFailure(json(400, { error: 'invalid_code' }))).reason).toBe('invalid'); expect((await redeemFailure(json(410, { error: 'expired_code' }))).reason).toBe('expired'); diff --git a/src/lib/oauth.ts b/src/lib/oauth.ts index 745737ad..c66be1d8 100644 --- a/src/lib/oauth.ts +++ b/src/lib/oauth.ts @@ -275,6 +275,47 @@ export function parseQrLoginPayload(raw: string): QrLoginPayload | null { return null; } +/** + * The code is redeemed at the webmail, and the answer carries credentials: + * only over TLS, or plain http on loopback for development. + */ +export function isInsecurePairingUrl(webmailUrl: string): boolean { + return !isHttpsUrl(webmailUrl) && !isLoopbackHttp(webmailUrl); +} + +/** + * The host a sign-in link talks to, for the user to check. Read the way the + * network stack reads the address: `https://mail.example.com@evil.example` + * and `https://evil.example\@mail.example.com` both go to evil.example, so + * that is the host named. + */ +export function signInLinkHost(url: string): string { + const authority = url.trim().replace(/^[a-z][a-z0-9+.-]*:\/\//i, '').split(/[/\\?#]/)[0]; + return authority.slice(authority.lastIndexOf('@') + 1).toLowerCase(); +} + +/** + * Why parseQrLoginPayload refused a pairing link, when the reason is its + * plain-http webmail (the code, and the credentials it buys, would cross the + * network in clear), so a scanned, pasted or tapped link can say so instead + * of "not a Bulwark sign-in code". Only ever an error to show: the parser + * stays the one gate and never yields such a link. Takes the link alone or + * pasted text around it; null for anything else. + */ +export function insecurePairingLinkError(text: string): PairingError | null { + const link = (/bulwarkmail:\/\/\S+/i.exec(text)?.[0] ?? '').replace(/[)\]>.,;'"]+$/, ''); + const match = SIGN_IN_LINK_RE.exec(link); + if (!match || match[1].toLowerCase() !== 'pair') return null; + const params = new URLSearchParams(match[2]); + const server = params.get('server')?.trim(); + const code = params.get('code')?.trim(); + if (!server || !/^http:\/\/[^/?#\s]+/i.test(server) || !isInsecurePairingUrl(server)) return null; + if (!code || !PAIRING_CODE_RE.test(code)) return null; + return new PairingError('insecure', 'Pairing needs an https webmail address', { + host: signInLinkHost(server), + }); +} + /** * Pasted text: the link on its own, or a message that contains it ("Open * bulwarkmail://pair?… on your phone"). Falls back to the whole text so a @@ -340,6 +381,10 @@ function redeemFailure(status: number, body: Record | null, hos export async function redeemPairingCode(webmailUrl: string, code: string): Promise { const base = webmailUrl.replace(/\/+$/, ''); const host = displayHost(base); + // The login screen refuses these links already; never send a code in clear. + if (isInsecurePairingUrl(base)) { + throw new PairingError('insecure', 'Pairing needs an https webmail address', { host }); + } // secureFetch's native (client-certificate) path ignores `signal`, so the // deadline is also passed as `timeoutMs` and enforced here with a race; the diff --git a/src/navigation/__tests__/linking.test.ts b/src/navigation/__tests__/linking.test.ts index 363a3cfa..6ed59783 100644 --- a/src/navigation/__tests__/linking.test.ts +++ b/src/navigation/__tests__/linking.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import { acceptSignInLink, handleDeepLink, parseDeepLink, parseSignInLink, shareToDeepLink } from '../linking'; -import { setPendingSignInLink, usePendingSignInLinkStore } from '../pending-sign-in-link'; +import { usePendingSignInLinkStore } from '../pending-sign-in-link'; import { usePendingSettingsTab } from '../pending-settings-tab'; import { usePendingCalendarOpen } from '../pending-calendar-open'; import { usePendingMailSearch } from '../pending-mail-search'; @@ -284,18 +284,35 @@ describe('sign-in links', () => { expect(parseSignInLink(null)).toBeNull(); }); - it('parks a sign-in link for the login screen, which takes it once', () => { - setPendingSignInLink(null); + it('parks a sign-in link from the OS as external, for the login screen to take once', () => { + usePendingSignInLinkStore.getState().clear(); expect(acceptSignInLink('bulwarkmobile://mail')).toBe(false); expect(acceptSignInLink('mailto:a@b.co')).toBe(false); - expect(usePendingSignInLinkStore.getState().payload).toBeNull(); + expect(usePendingSignInLinkStore.getState().pending).toBeNull(); expect(acceptSignInLink(pair)).toBe(true); - expect(usePendingSignInLinkStore.getState().take()).toEqual({ - kind: 'pair', webmailUrl: 'https://mail.example.com/webmail', code, + expect(usePendingSignInLinkStore.getState().pending).toMatchObject({ + source: 'external', + payload: { kind: 'pair', webmailUrl: 'https://mail.example.com/webmail', code }, + }); + // Anyone can fire the link, so it is not run until the user confirms. + expect(usePendingSignInLinkStore.getState().take()).toMatchObject({ + needsConfirmation: true, + payload: { kind: 'pair', webmailUrl: 'https://mail.example.com/webmail', code }, }); // Taken means cleared: a second delivery of the same state signs in nothing. - expect(usePendingSignInLinkStore.getState().payload).toBeNull(); + expect(usePendingSignInLinkStore.getState().pending).toBeNull(); expect(usePendingSignInLinkStore.getState().take()).toBeNull(); + usePendingSignInLinkStore.getState().clear(); + }); + + it('parks connect links as external too', () => { + usePendingSignInLinkStore.getState().clear(); + expect(acceptSignInLink('bulwarkmail://connect?server=https%3A%2F%2Fmail.example.com')).toBe(true); + expect(usePendingSignInLinkStore.getState().take()).toMatchObject({ + needsConfirmation: true, + payload: { kind: 'connect', webmailUrl: 'https://mail.example.com' }, + }); + usePendingSignInLinkStore.getState().clear(); }); }); diff --git a/src/navigation/__tests__/pending-sign-in-link.test.ts b/src/navigation/__tests__/pending-sign-in-link.test.ts new file mode 100644 index 00000000..d51b2e9c --- /dev/null +++ b/src/navigation/__tests__/pending-sign-in-link.test.ts @@ -0,0 +1,148 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { acceptSignInLink, routeParkedSignInLink } from '../linking'; +import { + setPendingSignInLink, + signInLinkNeedsConfirmation, + usePendingSignInLinkStore, +} from '../pending-sign-in-link'; +import type { QrLoginPayload } from '../../lib/oauth'; +import { MAX_ACCOUNTS } from '../../lib/account-utils'; + +// A `bulwarkmail://` link from outside the app (any web page, one tap; any +// installed app, no tap) names a webmail and a code of the sender's choosing. +// It must not be redeemed or opened until the user says Continue; a scanned +// or pasted one is the user's own doing in the app and runs straight away. + +const code = 'c0ffee'.repeat(10) + 'beef'; +const link = (server: string) => + `bulwarkmail://pair?server=${encodeURIComponent(server)}&code=${code}`; +const ATTACKER = link('https://attacker.example'); +const OTHER = link('https://other.example'); + +const store = () => usePendingSignInLinkStore.getState(); + +beforeEach(() => store().clear()); + +describe('sign-in link confirmation', () => { + it('asks for links from outside the app, not for scanned or pasted ones', () => { + expect(signInLinkNeedsConfirmation('external')).toBe(true); + expect(signInLinkNeedsConfirmation('in-app')).toBe(false); + }); + + it('holds an external link until the user continues, then runs it once', () => { + acceptSignInLink(ATTACKER); + const taken = store().take(); + expect(taken).toMatchObject({ needsConfirmation: true, payload: { kind: 'pair', webmailUrl: 'https://attacker.example', code } }); + // Taken for the dialog, not handed out to run. + expect(store().pending).toBeNull(); + expect(store().awaitingConfirmation?.id).toBe(taken!.id); + expect(store().take()).toBeNull(); + + expect(store().confirm(taken!.id)).toEqual({ kind: 'pair', webmailUrl: 'https://attacker.example', code }); + expect(store().awaitingConfirmation).toBeNull(); + // A second Continue (or a stale dialog) runs nothing. + expect(store().confirm(taken!.id)).toBeNull(); + }); + + it('forgets a cancelled link, so nothing redeems it later', () => { + acceptSignInLink(ATTACKER); + const taken = store().take()!; + expect(store().discard(taken.id)).toBe(true); + expect(store().confirm(taken.id)).toBeNull(); + expect(store().take()).toBeNull(); + expect(store().pending).toBeNull(); + expect(store().awaitingConfirmation).toBeNull(); + expect(store().discard(taken.id)).toBe(false); + }); + + it('drops links delivered while the dialog is open instead of swapping them in', () => { + acceptSignInLink(ATTACKER); + const shown = store().take()!; + // A second 'url' event, or getInitialURL delivering again. + expect(acceptSignInLink(OTHER)).toBe(true); + expect(acceptSignInLink(ATTACKER)).toBe(true); + expect(store().pending).toBeNull(); + expect(store().take()).toBeNull(); + // Continue runs the link the dialog named, nothing else. + expect(store().confirm(shown.id)).toMatchObject({ webmailUrl: 'https://attacker.example' }); + expect(store().take()).toBeNull(); + }); + + it('asks again when a link is delivered again after the dialog closed', () => { + acceptSignInLink(ATTACKER); + const first = store().take()!; + store().discard(first.id); + + acceptSignInLink(ATTACKER); + const again = store().take()!; + expect(again.needsConfirmation).toBe(true); + expect(again.id).not.toBe(first.id); + // The cancelled dialog's id cannot confirm the new delivery. + expect(store().confirm(first.id)).toBeNull(); + expect(store().awaitingConfirmation?.id).toBe(again.id); + }); + + it('keeps the newest of two links parked before the login screen takes one', () => { + acceptSignInLink(ATTACKER); + acceptSignInLink(OTHER); + expect(store().take()).toMatchObject({ needsConfirmation: true, payload: { webmailUrl: 'https://other.example' } }); + }); + + it('runs an in-app link without a dialog', () => { + const payload: QrLoginPayload = { kind: 'connect', webmailUrl: 'https://mail.example.com' }; + expect(setPendingSignInLink(payload, 'in-app')).toBe(true); + expect(store().take()).toMatchObject({ needsConfirmation: false, payload }); + expect(store().awaitingConfirmation).toBeNull(); + expect(store().pending).toBeNull(); + }); +}); + +describe('refused sign-in links', () => { + it('parks a pairing link for a plain-http webmail as a refusal, with nothing to run', () => { + expect(acceptSignInLink(link('http://mail.example.com'))).toBe(true); + expect(store().pending).toBeNull(); + expect(store().take()).toBeNull(); + const refusal = store().takeRefusal(); + expect(refusal).toMatchObject({ name: 'PairingError', reason: 'insecure', host: 'mail.example.com' }); + expect(store().takeRefusal()).toBeNull(); + }); + + it('ignores other broken sign-in links, as before', () => { + expect(acceptSignInLink(`bulwarkmail://pair?server=${encodeURIComponent('https://mail.example.com')}`)).toBe(false); + expect(acceptSignInLink('bulwarkmail://unlock?server=https://mail.example.com')).toBe(false); + expect(store().refusal).toBeNull(); + expect(store().pending).toBeNull(); + }); +}); + +describe('routeParkedSignInLink (signed in)', () => { + it('opens Add account while there is room', () => { + acceptSignInLink(ATTACKER); + expect(routeParkedSignInLink(MAX_ACCOUNTS - 1)).toBe('add-account'); + // Still parked for Add account's login screen. + expect(store().pending).not.toBeNull(); + }); + + it('drops the link and reports the limit when the registry is full, so no code is spent', () => { + acceptSignInLink(ATTACKER); + expect(routeParkedSignInLink(MAX_ACCOUNTS)).toBe('account-limit'); + expect(store().pending).toBeNull(); + expect(store().take()).toBeNull(); + + acceptSignInLink(link('http://mail.example.com')); + expect(routeParkedSignInLink(MAX_ACCOUNTS)).toBe('account-limit'); + expect(store().refusal).toBeNull(); + }); + + it('does nothing when no link is parked', () => { + expect(routeParkedSignInLink(0)).toBe('none'); + expect(routeParkedSignInLink(MAX_ACCOUNTS)).toBe('none'); + }); + + it('leaves a link the dialog is asking about alone', () => { + acceptSignInLink(ATTACKER); + const shown = store().take()!; + expect(routeParkedSignInLink(MAX_ACCOUNTS)).toBe('none'); + expect(store().confirm(shown.id)).not.toBeNull(); + }); +}); diff --git a/src/navigation/linking.ts b/src/navigation/linking.ts index f45af146..7c621b4e 100644 --- a/src/navigation/linking.ts +++ b/src/navigation/linking.ts @@ -9,8 +9,9 @@ import type { RootStackParamList } from './types'; import { setPendingSettingsTab } from './pending-settings-tab'; import { setPendingCalendarOpen } from './pending-calendar-open'; import { setPendingMailSearch } from './pending-mail-search'; -import { setPendingSignInLink } from './pending-sign-in-link'; -import { parseQrLoginPayload, type QrLoginPayload } from '../lib/oauth'; +import { setPendingSignInLink, usePendingSignInLinkStore } from './pending-sign-in-link'; +import { insecurePairingLinkError, parseQrLoginPayload, type QrLoginPayload } from '../lib/oauth'; +import { MAX_ACCOUNTS } from '../lib/account-utils'; export const APP_SCHEME = 'bulwarkmobile'; // The webmail's "Link Mobile App" links (`bulwarkmail://pair?server=…&code=…`) @@ -184,17 +185,41 @@ export function parseSignInLink(url: string | null | undefined): QrLoginPayload } /** - * Park a tapped sign-in link for the login screen, which takes it (the - * signed-out one, or Add account when someone is signed in). Returns whether - * `url` was one. + * Park a sign-in link the OS delivered (a tapped link, or another app firing + * the intent) for the login screen, which takes it (the signed-out one, or + * Add account when someone is signed in) and asks the user before running + * it: whoever sent it chose the webmail and the code. A pairing link for a + * plain-http webmail is parked as a refusal instead, for the login screen to + * say why nothing happens. Returns whether `url` was a sign-in link. */ export function acceptSignInLink(url: string | null | undefined): boolean { const payload = parseSignInLink(url); - if (!payload) return false; - setPendingSignInLink(payload); + if (payload) { + setPendingSignInLink(payload, 'external'); + return true; + } + const refusal = url && isSignInSchemeUrl(url) ? insecurePairingLinkError(url) : null; + if (!refusal) return false; + usePendingSignInLinkStore.getState().refuse(refusal); return true; } +/** + * Signed in, where a parked sign-in link goes: Add account, whose login + * screen takes it, or, with no room for another account, nowhere: the link + * is dropped before a code is spent and the caller shows the limit. 'none' + * when nothing is parked (taken meanwhile by an Add account already open). + */ +export function routeParkedSignInLink(accountCount: number): 'add-account' | 'account-limit' | 'none' { + const links = usePendingSignInLinkStore.getState(); + if (!links.pending && !links.refusal) return 'none'; + if (accountCount >= MAX_ACCOUNTS) { + links.dropParked(); + return 'account-limit'; + } + return 'add-account'; +} + export interface DeepLinkNavigator { navigation: NavigationContainerRefWithCurrent; // Resolve a message id to its thread (EmailThread needs both). Returns diff --git a/src/navigation/pending-sign-in-link.ts b/src/navigation/pending-sign-in-link.ts index 72046404..c8c5fa53 100644 --- a/src/navigation/pending-sign-in-link.ts +++ b/src/navigation/pending-sign-in-link.ts @@ -1,26 +1,115 @@ import { create } from 'zustand'; -import type { QrLoginPayload } from '../lib/oauth'; +import type { PairingError, QrLoginPayload } from '../lib/oauth'; // A tapped `bulwarkmail://` sign-in link arrives before the login screen is // up (cold start, or signed in with Add account not open yet). It is parked // here and taken by LoginScreen, which runs it like a scanned code. Taking // clears it, so one link signs in once however often it is delivered. -interface PendingSignInLinkState { - payload: QrLoginPayload | null; - set: (payload: QrLoginPayload | null) => void; - take: () => QrLoginPayload | null; +// +// Where a link came from decides whether the user is asked first: +// - `external`: the OS handed it to the app. Any web page (one tap) or any +// installed app (no tap at all) can fire one, with a webmail and a code of +// its own choosing, so the user confirms before it is redeemed or opened. +// - `in-app`: scanned with the app's scanner or pasted into its paste box. +// The user just did that in the app, so it runs straight away. +export type SignInLinkSource = 'external' | 'in-app'; + +export interface PendingSignInLink { + // Tells one delivery from the next, so a dialog can only ever confirm the + // link it named. + id: number; + payload: QrLoginPayload; + source: SignInLinkSource; } +export interface TakenSignInLink { + id: number; + payload: QrLoginPayload; + // Show the confirmation and run the link only on Continue (`confirm`). + needsConfirmation: boolean; +} + +export function signInLinkNeedsConfirmation(source: SignInLinkSource): boolean { + return source !== 'in-app'; +} + +interface PendingSignInLinkState { + // Parked, not taken by a login screen yet. + pending: PendingSignInLink | null; + // Taken and shown to the user, waiting for Continue or Cancel. + awaitingConfirmation: PendingSignInLink | null; + // A tapped link that is refused outright (a pairing code for a plain-http + // webmail): nothing to run or confirm, the login screen says why. + refusal: PairingError | null; + /** Park a link. Returns false when it was dropped (a dialog is open). */ + park: (payload: QrLoginPayload, source: SignInLinkSource) => boolean; + /** + * Take the parked link. One that needs confirmation moves to + * `awaitingConfirmation`; run it only with what `confirm` returns. + */ + take: () => TakenSignInLink | null; + /** Continue: the payload to run, or null when that link is gone. */ + confirm: (id: number) => QrLoginPayload | null; + /** + * Cancel: forget the link, so nothing redeems it later. Returns whether it + * was still waiting. + */ + discard: (id: number) => boolean; + refuse: (refusal: PairingError) => void; + takeRefusal: () => PairingError | null; + /** Drop what is parked (not a link a dialog is asking about). */ + dropParked: () => boolean; + clear: () => void; +} + +let nextId = 1; + export const usePendingSignInLinkStore = create((set, get) => ({ - payload: null, - set: (payload) => set({ payload }), - take: () => { - const payload = get().payload; - if (payload) set({ payload: null }); - return payload; + pending: null, + awaitingConfirmation: null, + refusal: null, + park: (payload, source) => { + // While the user is asked about one link, another is dropped rather + // than queued behind it or swapped in: Continue must run the link the + // dialog names and nothing else, and a link fired while the dialog is + // open must not pop up right after Cancel. + if (get().awaitingConfirmation) return false; + set({ pending: { id: nextId++, payload, source } }); + return true; }, + take: () => { + const { pending, awaitingConfirmation } = get(); + if (!pending || awaitingConfirmation) return null; + const needsConfirmation = signInLinkNeedsConfirmation(pending.source); + set({ pending: null, awaitingConfirmation: needsConfirmation ? pending : null }); + return { id: pending.id, payload: pending.payload, needsConfirmation }; + }, + confirm: (id) => { + const link = get().awaitingConfirmation; + if (!link || link.id !== id) return null; + set({ awaitingConfirmation: null }); + return link.payload; + }, + discard: (id) => { + if (get().awaitingConfirmation?.id !== id) return false; + set({ awaitingConfirmation: null }); + return true; + }, + refuse: (refusal) => set({ refusal }), + takeRefusal: () => { + const refusal = get().refusal; + if (refusal) set({ refusal: null }); + return refusal; + }, + dropParked: () => { + const { pending, refusal } = get(); + if (!pending && !refusal) return false; + set({ pending: null, refusal: null }); + return true; + }, + clear: () => set({ pending: null, awaitingConfirmation: null, refusal: null }), })); -export function setPendingSignInLink(payload: QrLoginPayload | null): void { - usePendingSignInLinkStore.getState().set(payload); +export function setPendingSignInLink(payload: QrLoginPayload, source: SignInLinkSource): boolean { + return usePendingSignInLinkStore.getState().park(payload, source); } diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx index 49a87a34..982d9389 100644 --- a/src/screens/LoginScreen.tsx +++ b/src/screens/LoginScreen.tsx @@ -1,12 +1,17 @@ import React from 'react'; -import { BackHandler } from 'react-native'; +import { Alert, BackHandler } from 'react-native'; import { useAuthStore } from '../stores/auth-store'; import { useAccountStore } from '../stores/account-store'; -import { useLocaleStore } from '../stores/locale-store'; +import { useLocaleStore, type TranslateFn } from '../stores/locale-store'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { QrScanModal } from '../components/QrScanModal'; import { PasteSignInLinkModal } from '../components/PasteSignInLinkModal'; -import { parseQrLoginPayload, type QrLoginPayload } from '../lib/oauth'; +import { + insecurePairingLinkError, + parseQrLoginPayload, + signInLinkHost, + type QrLoginPayload, +} from '../lib/oauth'; import { usePendingSignInLinkStore } from '../navigation/pending-sign-in-link'; import { discoverServerForEmail, @@ -33,6 +38,31 @@ type StepName = 'choose' | 'email' | 'server' | 'confirm' | 'password'; const RECENT_EMAILS_KEY = 'login:recentEmails:v1'; +// The question asked before running a sign-in link from outside the app: +// which webmail it talks to, and whether it adds an account (and switches to +// it) or signs in. +function signInLinkConfirmation( + payload: QrLoginPayload, + isAddMode: boolean, + t: TranslateFn, +): { title: string; message: string } { + const host = signInLinkHost(payload.webmailUrl); + if (payload.kind === 'connect') { + return { + title: t('login.mobile.link_confirm_connect_title', 'Open the sign-in page of {host}?', { host }), + message: isAddMode + ? t('login.mobile.link_confirm_connect_add', 'A link asks to add an account by signing in on this page. Only continue if you use {host} for your mail.', { host }) + : t('login.mobile.link_confirm_connect', 'A link asks to sign in on this page. Only continue if you use {host} for your mail.', { host }), + }; + } + return { + title: t('login.mobile.link_confirm_title', 'Sign in with a link?'), + message: isAddMode + ? t('login.mobile.link_confirm_pair_add', 'This link adds an account from {host} and switches the app to it. Only continue if you just showed a sign-in code on your computer.', { host }) + : t('login.mobile.link_confirm_pair', 'This link signs in to an account from {host}. Only continue if you just showed a sign-in code on your computer.', { host }), + }; +} + /** * Sign-in, one question per screen, cheapest question first. * @@ -334,6 +364,12 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo setScannerVisible(false); const payload = parseQrLoginPayload(data); if (!payload) { + // A pairing code for a plain-http webmail: say why it won't be used. + const insecure = insecurePairingLinkError(data); + if (insecure) { + setNotice(describeLoginError(insecure, { t })); + return; + } setNotice({ title: t('login.mobile.notice_bad_qr', "That code isn't a Bulwark sign-in code"), detail: t('login.mobile.notice_bad_qr_detail', 'Open Bulwark on the web, then Settings → Security → Link Mobile App to show one.'), @@ -345,14 +381,84 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo [handleSignInLink, t], ); - // A tapped sign-in link is parked by App until a login screen is free to - // run it; taking it clears it, so one link signs in once. - const signInLinkPending = usePendingSignInLinkStore((s) => s.payload !== null); + // A sign-in link from outside the app (a tapped link, or one another app + // fired) is parked by App until a login screen is free to take it; taking + // clears it, so one link runs once. Whoever sent it chose the webmail and + // the code, so the user confirms first: nothing is redeemed or opened + // before Continue, and Cancel (or dismissing the dialog) forgets the link. + // Scanned and pasted links skip this: they call handleSignInLink directly. + const signInLinkPending = usePendingSignInLinkStore((s) => s.pending !== null); + // The dialog answers after later renders; run the current callbacks. + const latestRef = React.useRef({ handleSignInLink, onCancel }); + React.useEffect(() => { + latestRef.current = { handleSignInLink, onCancel }; + }, [handleSignInLink, onCancel]); + const mountedRef = React.useRef(false); + const confirmingLinkIdRef = React.useRef(null); + React.useEffect(() => { + mountedRef.current = true; + return () => { + mountedRef.current = false; + // Gone while the dialog is up: forget its link, so a late Continue runs + // nothing and later links are not dropped as "dialog open". Checked a + // tick later, as a StrictMode rehearsal remounts straight away. + setTimeout(() => { + const id = confirmingLinkIdRef.current; + if (!mountedRef.current && id !== null) usePendingSignInLinkStore.getState().discard(id); + }, 0); + }; + }, []); React.useEffect(() => { if (!signInLinkPending || busy || searching) return; - const payload = usePendingSignInLinkStore.getState().take(); - if (payload) void handleSignInLink(payload); - }, [busy, handleSignInLink, searching, signInLinkPending]); + const taken = usePendingSignInLinkStore.getState().take(); + if (!taken) return; + if (!taken.needsConfirmation) { + void handleSignInLink(taken.payload); + return; + } + // Add account opened for this link closes again on Cancel; one the user + // was already working through stays where it was. + const leaveOnCancel = isAddMode && history.length === 0; + confirmingLinkIdRef.current = taken.id; + let answered = false; + const answer = (proceed: boolean) => { + if (answered) return; + answered = true; + confirmingLinkIdRef.current = null; + const store = usePendingSignInLinkStore.getState(); + if (!mountedRef.current) { + store.discard(taken.id); + return; + } + if (proceed) { + // Only the link this dialog named, and only if it is still waiting. + const payload = store.confirm(taken.id); + if (payload) void latestRef.current.handleSignInLink(payload); + return; + } + if (store.discard(taken.id) && leaveOnCancel) latestRef.current.onCancel?.(); + }; + const { title, message } = signInLinkConfirmation(taken.payload, isAddMode, t); + Alert.alert( + title, + message, + [ + { text: t('common.cancel', 'Cancel'), style: 'cancel', onPress: () => answer(false) }, + { text: t('login.mobile.continue', 'Continue'), onPress: () => answer(true) }, + ], + // Android back or a tap outside the dialog counts as Cancel. + { cancelable: true, onDismiss: () => answer(false) }, + ); + }, [busy, handleSignInLink, history.length, isAddMode, searching, signInLinkPending, t]); + + // A tapped link that is refused outright (a pairing code for a plain-http + // webmail) ran nothing; say why, as for a scanned one. + const signInLinkRefused = usePendingSignInLinkStore((s) => s.refusal !== null); + React.useEffect(() => { + if (!signInLinkRefused || busy || searching) return; + const refusal = usePendingSignInLinkStore.getState().takeRefusal(); + if (refusal) setNotice(describeLoginError(refusal, { t })); + }, [busy, searching, signInLinkRefused, t]); // ── render ───────────────────────────────────────────── From 9d83b2fb6221bb7312097c00604070defa2f0611 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:14:04 +0200 Subject: [PATCH 028/334] fix: stop warning about shared accounts that share only mail #34 434c554 left an account that refuses to list its address books or calendars out of the run, but reported it as an item error. The sync settings then said "1 item could not be synced" after every run for an account that merely shares a mailbox: nothing the user can fix, and no item behind it. Such an account is now only logged. Checked: the engine tests, `npm run typecheck` and `npm test` on this commit, and the sync settings of usera on the API 32 emulator after a new run. --- src/device-sync/__tests__/engine/failures.test.ts | 4 ++-- src/device-sync/engine/item-sync.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/device-sync/__tests__/engine/failures.test.ts b/src/device-sync/__tests__/engine/failures.test.ts index efa05ce6..af4903cc 100644 --- a/src/device-sync/__tests__/engine/failures.test.ts +++ b/src/device-sync/__tests__/engine/failures.test.ts @@ -621,7 +621,7 @@ describe('device sync engine: accounts that refuse access', () => { const report = await h.run(); expect(report.outcome).toBe('ok'); - expect(report.itemErrors).toEqual([expect.objectContaining({ ref: 'team', side: 'download', type: 'forbidden' })]); + expect(report.itemErrors).toEqual([]); expect(h.contactNamed('Ada')).toBeDefined(); }); @@ -665,7 +665,7 @@ describe('device sync engine: accounts that refuse access', () => { const report = await h.run(CALENDAR_AUTHORITY); expect(report.outcome).toBe('ok'); - expect(report.itemErrors).toEqual([expect.objectContaining({ ref: 'team', type: 'forbidden' })]); + expect(report.itemErrors).toEqual([]); expect(h.events()).toHaveLength(1); expect(h.device.rows('calendars').some((c) => c._sync_id === `team/${teamCalendar}`)).toBe(true); }); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 942e1204..e4c96911 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -189,9 +189,10 @@ export abstract class ItemSync { * One account's collections, or null when the account refuses to list them * (`forbidden`, `accountNotFound`, `accountNotSupportedByMethod`): Stalwart * lists an account shared for mail only with every capability, and access - * can go away. Such an account is left out of the run and reported; the - * rows written for it stay as they are, since a refusal can pass and must - * not drop anything from the device. + * can go away. Such an account is left out of the run, and not reported + * as a problem: for a mail-only share there is nothing to fix, and the + * warning would never go away. The rows written for it stay as they are, + * since a refusal can pass and must not drop anything from the device. */ protected async containersOf( type: 'AddressBook' | 'Calendar', @@ -203,7 +204,6 @@ export abstract class ItemSync { } catch (error) { if (!isMethodError(error) || !ACCOUNT_UNREADABLE.has(error.type)) throw error; this.unreadable.add(accountId); - this.env.report.itemError({ ref: accountId, side: 'download', type: error.type, description: error.message }); this.env.log(`account ${accountId} left out: ${error.type}`); return null; } From 77fd494633c5624f4c9eb97b67206da34225dad0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:15:32 +0200 Subject: [PATCH 029/334] feat: turn on contacts and calendar sync to this device in settings #34 On Android, Settings > Contacts and Settings > Calendar get a "Sync to this device" section with a row per signed-in account. This makes device sync reachable: - The switch asks for the contacts or calendar permission after a short explanation (denied: sync stays off and says why, with a link to the app's system settings once Android stops asking), creates the account's Bulwark account in Android and syncs right away. Turning it off uploads what the device changed first and asks before anything that could not go up is lost. The switch shows Android's own state, so sync turned off in Android Settings shows as paused. - Which address books or calendars sync: the personal account's are on by default, shared ones and the Trusted Senders book off; read-only ones are marked, and a note says when another signed-in account already syncs the same shared one. - The address book new contacts from other apps go to, who reminds of synced events (the calendar app or Bulwark, asked when calendar sync is turned on while Bulwark's reminders are on), and how often Android syncs on its own (every 15 minutes to every 6 hours, or not at all). - "Sync now", the last sync with its result, or the error with what to do about it ("Sign in again", "Grant access", "Review deletions"), and a link to the account in Android Settings. - Accounts the app signed out while they synced are listed with "Sign in again" and "Remove from this device". The settings search finds both sections. iOS, and Android builds without the native module, show nothing new. Checked: the collection listing and status tests, the settings search and i18n tests, `npm run typecheck`, `npm test` and `npm run i18n:check` on this commit, and on the API 32 emulator with usera on the local server: the section, turning contacts sync on (Android account created, 152 contacts on the device, one per card) and the status after a run. --- .../__tests__/settings-i18n.test.ts | 5 + src/components/settings/CalendarSettings.tsx | 247 +++++---- src/components/settings/ContactsSettings.tsx | 16 + .../device-sync/DeviceSyncAccountRow.tsx | 502 ++++++++++++++++++ .../DeviceSyncCollectionsSheet.tsx | 310 +++++++++++ .../device-sync/DeviceSyncSection.tsx | 118 ++++ .../device-sync/__tests__/status.test.ts | 81 +++ src/components/settings/device-sync/status.ts | 133 +++++ .../settings/device-sync/use-android-sync.ts | 101 ++++ .../device-sync/use-sync-collections.ts | 61 +++ .../__tests__/app/collections.test.ts | 230 ++++++++ src/device-sync/app/collections.ts | 334 ++++++++++++ src/lib/__tests__/settings-search.test.ts | 15 + src/lib/settings-search.ts | 10 +- 14 files changed, 2047 insertions(+), 116 deletions(-) create mode 100644 src/components/settings/device-sync/DeviceSyncAccountRow.tsx create mode 100644 src/components/settings/device-sync/DeviceSyncCollectionsSheet.tsx create mode 100644 src/components/settings/device-sync/DeviceSyncSection.tsx create mode 100644 src/components/settings/device-sync/__tests__/status.test.ts create mode 100644 src/components/settings/device-sync/status.ts create mode 100644 src/components/settings/device-sync/use-android-sync.ts create mode 100644 src/components/settings/device-sync/use-sync-collections.ts create mode 100644 src/device-sync/__tests__/app/collections.test.ts create mode 100644 src/device-sync/app/collections.ts diff --git a/src/components/__tests__/settings-i18n.test.ts b/src/components/__tests__/settings-i18n.test.ts index 5e1fadb3..cfd9ce8b 100644 --- a/src/components/__tests__/settings-i18n.test.ts +++ b/src/components/__tests__/settings-i18n.test.ts @@ -40,6 +40,11 @@ const TRANSLATED_PANES: Record = { 'settings/UpdatesSettings.tsx': [], 'settings/VacationSettings.tsx': ['YYYY-MM-DD HH:MM'], 'settings/settings-section.tsx': [], + // Device sync (Android, #34). + 'settings/device-sync/DeviceSyncSection.tsx': [], + 'settings/device-sync/DeviceSyncAccountRow.tsx': [], + 'settings/device-sync/DeviceSyncCollectionsSheet.tsx': [], + 'settings/device-sync/status.ts': [], 'filters/FilterRuleModal.tsx': [], 'filters/SieveEditorSheet.tsx': [], }; diff --git a/src/components/settings/CalendarSettings.tsx b/src/components/settings/CalendarSettings.tsx index fa50d6b8..dab6f860 100644 --- a/src/components/settings/CalendarSettings.tsx +++ b/src/components/settings/CalendarSettings.tsx @@ -8,6 +8,9 @@ import { } from '../../stores/settings-store'; import { useLocaleStore } from '../../stores/locale-store'; import { AUTO_TIME_ZONE, getDeviceTimeZone, isValidTimeZone } from '../../lib/calendar-timezone'; +import { deviceSyncAvailable } from '../../device-sync/app/available'; +import { CALENDAR_AUTHORITY } from '../../device-sync/types'; +import { DeviceSyncSection } from './device-sync/DeviceSyncSection'; // A compact list of IANA zones for the picker (#755). The device zone and // any previously stored value are always offered too. @@ -46,6 +49,9 @@ export function CalendarSettings() { const tasksEnabled = useSettingsStore((s) => s.enableCalendarTasks); const showTasksOnCal = useSettingsStore((s) => s.showTasksOnCalendar); + // Android with the native module only (#34). + const deviceSync = React.useMemo(() => deviceSyncAvailable(), []); + useEffect(() => { if (!hydrated) void hydrate(); }, [hydrated, hydrate]); @@ -64,130 +70,143 @@ export function CalendarSettings() { }, [deviceZone, timeZone, t]); return ( - - - update('calendarDefaultView', v as CalendarView)} + options={[ + { value: 'month', label: t('calendar.views.month', 'Month') }, + { value: 'week', label: t('calendar.views.week', 'Week') }, + { value: 'day', label: t('calendar.views.day', 'Day') }, + { value: 'agenda', label: t('calendar.views.agenda', 'Agenda') }, + ]} + /> + - - update('calendarFirstDayOfWeek', Number(v) as FirstDayOfWeek)} + options={[ + { value: '1', label: t('calendar.days.monday', 'Monday') }, + { value: '6', label: t('calendar.days.saturday', 'Saturday') }, + { value: '0', label: t('calendar.days.sunday', 'Sunday') }, + ]} + /> + - - update('calendarTimeFormat', v as TimeFormat)} - options={[ - { value: '12h', label: t('calendar.settings.time_format_12h', '12-hour') }, - { value: '24h', label: t('calendar.settings.time_format_24h', '24-hour') }, - ]} - /> - + + update('calendarTimeFormat', v as TimeFormat)} + options={[ + { value: '12h', label: t('calendar.settings.time_format_12h', '12-hour') }, + { value: '24h', label: t('calendar.settings.time_format_24h', '24-hour') }, + ]} + /> + - - update('calendarTimeZone', v)} + options={timeZoneOptions} + /> + + + update('showTasksOnCalendar', v)} + checked={showTimeInMonth} + onChange={(v) => update('calendarShowTimeInMonth', v)} /> + + + update('calendarShowWeekNumbers', v)} + /> + + + + update('calendarFreeScroll', v)} + /> + + + + update('showBirthdayCalendar', v)} + /> + + + + update('enableCalendarTasks', v)} + /> + + + {tasksEnabled && ( + + update('showTasksOnCalendar', v)} + /> + + )} + + + {deviceSync && ( + )} - + ); } diff --git a/src/components/settings/ContactsSettings.tsx b/src/components/settings/ContactsSettings.tsx index da8cb4bc..04b468c4 100644 --- a/src/components/settings/ContactsSettings.tsx +++ b/src/components/settings/ContactsSettings.tsx @@ -9,11 +9,14 @@ import { SettingsSection, SettingItem, ToggleSwitch } from './settings-section'; import Button from '../Button'; import Dialog from '../Dialog'; import { ContactImportSheet, AddressBookPickerSheet } from '../contacts'; +import { DeviceSyncSection } from './device-sync/DeviceSyncSection'; import { useSettingsStore } from '../../stores/settings-store'; import { useLocaleStore } from '../../stores/locale-store'; import { useContactsStore, selectAddressBooksWithCount } from '../../stores/contacts-store'; import { contactsToVCard } from '../../lib/vcard'; import { isGroup } from '../../lib/contact-utils'; +import { deviceSyncAvailable } from '../../device-sync/app/available'; +import { CONTACTS_AUTHORITY } from '../../device-sync/types'; import { spacing, radius, typography, componentSizes, type ThemePalette } from '../../theme/tokens'; import { useColors } from '../../theme/colors'; @@ -53,6 +56,8 @@ export function ContactsSettings() { const [newName, setNewName] = React.useState(''); const [busy, setBusy] = React.useState(false); const [deleteTarget, setDeleteTarget] = React.useState<{ id: string; name: string; count: number } | null>(null); + // Android with the native module only (#34). + const deviceSync = React.useMemo(() => deviceSyncAvailable(), []); const exportable = React.useMemo(() => contacts.filter((cc) => !isGroup(cc)), [contacts]); const exportLabel = @@ -356,6 +361,17 @@ export function ContactsSettings() { + {deviceSync && ( + + )} + setImportOpen(false)} diff --git a/src/components/settings/device-sync/DeviceSyncAccountRow.tsx b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx new file mode 100644 index 00000000..dc3d68c3 --- /dev/null +++ b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx @@ -0,0 +1,502 @@ +// One signed-in account in "Sync to this device" (docs/device-sync.md, +// "Settings sections"): the switch (Android's automatic sync for the +// authority), the status or the error with what to do about it, and, while +// sync is on, which collections sync, where new contacts go or who reminds of +// events, the interval, "Sync now" and Android's account settings. +import React from 'react'; +import { Alert, StyleSheet, Text, View, ActivityIndicator } from 'react-native'; +import { useNavigation } from '@react-navigation/native'; +import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { AlertTriangle, RefreshCw, Settings as SettingsIcon, XCircle } from 'lucide-react-native'; +import { SettingItem, Select, RadioGroup, ToggleSwitch } from '../settings-section'; +import Button from '../../Button'; +import Dialog from '../../Dialog'; +import { spacing, typography, type ThemePalette } from '../../../theme/tokens'; +import { useColors } from '../../../theme/colors'; +import { useLocaleStore, type TranslateFn } from '../../../stores/locale-store'; +import { useSettingsStore } from '../../../stores/settings-store'; +import type { AccountEntry } from '../../../stores/account-store'; +import { + effectiveSelection, + emptyAccountDeviceSync, + intervalFor, + syncOnInApp, + useDeviceSyncStore, +} from '../../../stores/device-sync-store'; +import { + changeReminderOwner, + changeSyncInterval, + disableDeviceSync, + enableDeviceSync, + openAndroidAccountSettings, + resumeDeviceSync, + syncNow, +} from '../../../device-sync/app/lifecycle'; +import { hasSyncPermissions, openAppSystemSettings, requestSyncPermissions } from '../../../device-sync/app/permissions'; +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, type Authority, type ReminderOwner } from '../../../device-sync/types'; +import type { RootStackParamList } from '../../../navigation/types'; +import { DeviceSyncCollectionsSheet } from './DeviceSyncCollectionsSheet'; +import { describeSyncStatus, type StatusAction } from './status'; +import { useAndroidSync } from './use-android-sync'; +import { useSyncCollections } from './use-sync-collections'; + +interface Props { + account: AccountEntry; + authority: Authority; +} + +type Busy = 'on' | 'off' | 'sync' | null; + +type Prompt = + | { kind: 'rationale' } + | { kind: 'blocked' } + | { kind: 'pending'; pending: number }; + +/** Asks who reminds of synced events; null when the user backed out. */ +function askReminderOwner(t: TranslateFn): Promise { + return new Promise((resolve) => { + let settled = false; + const answer = (owner: ReminderOwner | null) => { + if (settled) return; + settled = true; + resolve(owner); + }; + Alert.alert( + t('settings.device_sync.reminder_question_title', 'Who should remind you?'), + t( + 'settings.device_sync.reminder_question_message', + 'Synced events can remind you from the calendar app on this device or from Bulwark. Pick one so you are not reminded twice. You can change it later.', + ), + [ + { text: t('common.cancel', 'Cancel'), style: 'cancel', onPress: () => answer(null) }, + { text: t('settings.device_sync.reminder_owner_bulwark', 'Bulwark'), onPress: () => answer('bulwark') }, + { text: t('settings.device_sync.reminder_owner_device', 'Calendar app'), onPress: () => answer('device') }, + ], + { cancelable: true, onDismiss: () => answer(null) }, + ); + }); +} + +export function DeviceSyncAccountRow({ account, authority }: Props) { + const c = useColors(); + const styles = React.useMemo(() => makeStyles(c), [c]); + const t = useLocaleStore((s) => s.t); + const navigation = useNavigation>(); + const storedEntry = useDeviceSyncStore((s) => s.accounts[account.id]); + const entry = React.useMemo(() => storedEntry ?? emptyAccountDeviceSync(), [storedEntry]); + const calendarReminders = useSettingsStore((s) => s.calendarNotificationsEnabled); + const isContacts = authority === CONTACTS_AUTHORITY; + + const onInApp = syncOnInApp(entry, authority); + const { view, permitted, refresh } = useAndroidSync(entry.androidAccountName, authority); + const [busy, setBusy] = React.useState(null); + const [prompt, setPrompt] = React.useState(null); + const [chooserOpen, setChooserOpen] = React.useState(false); + + // The switch follows Android; until Android answered, what the app knows. + const androidOn = view ? view.exists && view.automatic : onInApp; + const checked = busy === 'on' ? true : busy === 'off' ? false : androidOn; + const paused = onInApp && !!view?.exists && !view.automatic; + const showDetails = (checked || paused) && busy !== 'off'; + + const { state: collectionsState } = useSyncCollections(account.id, authority, showDetails); + const collections = collectionsState.kind === 'loaded' ? collectionsState.collections : null; + const selectedKeys = React.useMemo( + () => (collections ? effectiveSelection(entry, authority, collections) : null), + [collections, entry, authority], + ); + + const accountLabel = account.email || account.username; + const status = describeSyncStatus( + { + authority, + on: checked, + paused, + removed: !!entry.removedInAndroidSettings, + permitted, + syncing: !!view?.active, + lastRun: entry.lastRun[authority], + now: Date.now(), + }, + t, + ); + + const alertFailed = (message: string) => { + Alert.alert( + t('settings.device_sync.failed_title', 'Sync could not be turned on'), + message, + [{ text: t('settings.device_sync.ok', 'OK') }], + ); + }; + + const alertDenied = () => { + Alert.alert( + t('settings.device_sync.denied_title', 'Sync stays off'), + isContacts + ? t('settings.device_sync.denied_contacts', 'Without access to contacts, Bulwark cannot put your address books on this device.') + : t('settings.device_sync.denied_calendar', 'Without access to calendars, Bulwark cannot put your calendars on this device.'), + [{ text: t('settings.device_sync.ok', 'OK') }], + ); + }; + + const turnOn = async () => { + setPrompt(null); + setBusy('on'); + try { + const permission = await requestSyncPermissions(authority); + if (permission === 'blocked') { + setPrompt({ kind: 'blocked' }); + return; + } + if (permission === 'denied') { + alertDenied(); + return; + } + if (authority === CALENDAR_AUTHORITY && entry.reminderOwner == null) { + // Bulwark reminds nobody while its event notifications are off: the + // calendar app does, without asking. + const owner = calendarReminders ? await askReminderOwner(t) : 'device'; + if (!owner) return; + await changeReminderOwner(account.id, owner); + } + const outcome = await enableDeviceSync(account.id, authority); + if (outcome.kind === 'permission') alertDenied(); + else if (outcome.kind === 'failed') alertFailed(outcome.message); + } finally { + setBusy(null); + void refresh(); + } + }; + + const startTurnOn = async () => { + // Explain first, unless access was granted before (another account). + if (await hasSyncPermissions(authority)) void turnOn(); + else setPrompt({ kind: 'rationale' }); + }; + + const turnOff = async (force: boolean) => { + setPrompt(null); + setBusy('off'); + try { + const result = await disableDeviceSync(account.id, authority, { force }); + if (result.done) return; + if (!force) { + setPrompt({ kind: 'pending', pending: result.pending }); + return; + } + Alert.alert( + t('settings.device_sync.turn_off_failed_title', 'Sync could not be turned off'), + t( + 'settings.device_sync.turn_off_failed', + 'The synced data could not be removed from this device. Sync is paused; try again later.', + ), + [{ text: t('settings.device_sync.ok', 'OK') }], + ); + } finally { + setBusy(null); + void refresh(); + } + }; + + const keepSyncing = async () => { + setPrompt(null); + await resumeDeviceSync(account.id, authority); + void refresh(); + }; + + const handleSyncNow = async () => { + setBusy('sync'); + try { + await syncNow(account.id, authority); + } catch (err) { + alertFailed(err instanceof Error ? err.message : String(err)); + } finally { + setBusy(null); + void refresh(); + } + }; + + const grantAccess = async () => { + const permission = await requestSyncPermissions(authority); + if (permission === 'blocked') setPrompt({ kind: 'blocked' }); + else if (permission === 'denied') alertDenied(); + else if (checked) await syncNow(account.id, authority).catch(() => false); + void refresh(); + }; + + const runAction = (action: StatusAction) => { + if (action === 'signIn') navigation.navigate('AddAccount'); + else if (action === 'grantAccess') void grantAccess(); + else void openAndroidAccountSettings(account.id); + }; + + const closeChooser = (changed: boolean) => { + setChooserOpen(false); + // A new selection is loaded (or dropped) by the next sync: run it now. + // (A sync Android paused would only report that it is off.) + if (changed && checked) void syncNow(account.id, authority).catch(() => false); + }; + + const intervalOptions = [ + { value: '900', label: t('settings.device_sync.interval_15m', 'Every 15 minutes') }, + { value: '1800', label: t('settings.device_sync.interval_30m', 'Every 30 minutes') }, + { value: '3600', label: t('settings.device_sync.interval_1h', 'Every hour') }, + { value: '21600', label: t('settings.device_sync.interval_6h', 'Every 6 hours') }, + { value: '0', label: t('settings.device_sync.interval_manual', 'No regular sync') }, + ]; + + const writableSelected = (collections ?? []).filter((col) => !col.readOnly && selectedKeys?.includes(col.key)); + const newContactsOptions = [ + { value: '', label: t('settings.device_sync.new_contacts_default', 'Default address book') }, + ...writableSelected.map((col) => ({ + value: col.key, + label: col.isPersonal ? col.name : `${col.name} (${col.accountName})`, + })), + ]; + const newContactsValue = entry.newContactsAddressBook && newContactsOptions.some((o) => o.value === entry.newContactsAddressBook) + ? entry.newContactsAddressBook + : ''; + + const collectionsDescription = collections && selectedKeys + ? t('settings.device_sync.collections_count', '{selected} of {total} sync', { + selected: selectedKeys.length, + total: collections.length, + }) + : isContacts + ? t('settings.device_sync.books_default', 'Your own address books sync; shared ones only when you pick them.') + : t('settings.device_sync.calendars_default', 'Your own calendars sync; shared ones only when you pick them.'); + + const statusStyle = status.tone === 'error' ? styles.statusError : status.tone === 'warning' ? styles.statusWarning : styles.statusMuted; + const busyText = busy === 'on' + ? t('settings.device_sync.turning_on', 'Setting up…') + : busy === 'off' + ? t('settings.device_sync.turning_off', 'Uploading changes, then removing the data from this device…') + : null; + + return ( + + + + {(busy === 'on' || busy === 'off') && } + { + if (next) void startTurnOn(); + else void turnOff(false); + }} + disabled={busy !== null} + accessibilityLabel={t('settings.device_sync.switch_label', 'Sync {account} to this device', { account: accountLabel })} + /> + + + + {!busyText && ( + + {status.tone === 'error' && } + {status.tone === 'warning' && } + {status.text} + + )} + {!busyText && status.action && ( + + + + )} + {showDetails && view && !view.masterAutomatic && ( + + {t( + 'settings.device_sync.master_off', + 'Auto-sync is off on this device, so syncs only run when you tap Sync now.', + )} + + )} + + {showDetails && ( + + + + + + {isContacts ? ( + + { void changeSyncInterval(account.id, authority, Number(v)); }} + options={intervalOptions} + /> + + + + + + + + )} + + + + { void turnOn(); }} + onCancel={() => setPrompt(null)} + /> + + { + setPrompt(null); + void openAppSystemSettings(); + }} + onCancel={() => setPrompt(null)} + /> + + 0 + ? t( + 'settings.device_sync.turn_off_pending', + "{count, plural, one {# change made on this device hasn't reached the server yet. It is lost if you turn sync off now.} other {# changes made on this device haven't reached the server yet. They are lost if you turn sync off now.}}", + { count: prompt.pending }, + ) + : t( + 'settings.device_sync.turn_off_unknown', + 'Bulwark could not check whether changes made on this device reached the server. They are lost if you turn sync off now.', + )} + confirmText={t('settings.device_sync.turn_off_anyway', 'Turn off')} + cancelText={t('settings.device_sync.keep_syncing', 'Keep syncing')} + onConfirm={() => { void turnOff(true); }} + onCancel={() => { void keepSyncing(); }} + /> + + ); +} + +function makeStyles(c: ThemePalette) { + return StyleSheet.create({ + block: { + borderBottomWidth: 1, + borderBottomColor: c.border, + paddingBottom: spacing.md, + }, + row: { flexDirection: 'row', alignItems: 'center', gap: spacing.sm }, + statusRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 6 }, + statusText: { ...typography.caption, flexShrink: 1 }, + statusMuted: { color: c.mutedForeground }, + statusWarning: { color: c.warning }, + statusError: { color: c.error }, + details: { + marginTop: spacing.sm, + marginStart: spacing.sm, + paddingStart: spacing.md, + borderStartWidth: 2, + borderStartColor: c.muted, + }, + actions: { flexDirection: 'row', flexWrap: 'wrap', gap: spacing.sm, marginTop: spacing.sm }, + }); +} diff --git a/src/components/settings/device-sync/DeviceSyncCollectionsSheet.tsx b/src/components/settings/device-sync/DeviceSyncCollectionsSheet.tsx new file mode 100644 index 00000000..7f440ed1 --- /dev/null +++ b/src/components/settings/device-sync/DeviceSyncCollectionsSheet.tsx @@ -0,0 +1,310 @@ +// Which address books or calendars of one account sync to this device +// (docs/device-sync.md, "Settings sections"). The account's own collections +// are on unless turned off, shared ones off unless turned on; read-only ones +// are marked, and a collection another signed-in account already syncs gets +// a warning (it would show twice on the phone). +import React from 'react'; +import { + ActivityIndicator, Animated, Easing, Modal, Pressable, ScrollView, StyleSheet, Text, View, +} from 'react-native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { AlertTriangle, Check, X } from 'lucide-react-native'; +import Button from '../../Button'; +import { spacing, radius, typography, type ThemePalette } from '../../../theme/tokens'; +import { useColors } from '../../../theme/colors'; +import { useSheetDrag } from '../../../lib/use-sheet-drag'; +import { useLocaleStore } from '../../../stores/locale-store'; +import { useAccountStore } from '../../../stores/account-store'; +import { + isCollectionSelected, + selectionFor, + useDeviceSyncStore, + emptyAccountDeviceSync, +} from '../../../stores/device-sync-store'; +import { otherAccountsSyncing, type SyncCollection } from '../../../device-sync/app/collections'; +import { CONTACTS_AUTHORITY, type Authority } from '../../../device-sync/types'; +import { useSyncCollections } from './use-sync-collections'; + +interface Props { + visible: boolean; + /** `changed`: the selection was edited while the sheet was open. */ + onClose: (changed: boolean) => void; + registryId: string; + authority: Authority; + /** The account's address, under the title. */ + accountLabel: string; +} + +export function DeviceSyncCollectionsSheet({ visible, onClose, registryId, authority, accountLabel }: Props) { + const c = useColors(); + const styles = React.useMemo(() => makeStyles(c), [c]); + const t = useLocaleStore((s) => s.t); + const insets = useSafeAreaInsets(); + const { state, reload } = useSyncCollections(registryId, authority, visible); + const entry = useDeviceSyncStore((s) => s.accounts[registryId]); + const allEntries = useDeviceSyncStore((s) => s.accounts); + const setCollectionSelected = useDeviceSyncStore((s) => s.setCollectionSelected); + const registry = useAccountStore((s) => s.accounts); + const changed = React.useRef(false); + + const slideY = React.useRef(new Animated.Value(600)).current; + const overlayOpacity = React.useRef(new Animated.Value(0)).current; + const close = React.useCallback(() => { + const wasChanged = changed.current; + changed.current = false; + onClose(wasChanged); + }, [onClose]); + const dragHandlers = useSheetDrag({ slideY, closedY: 600, onClose: close }); + + React.useEffect(() => { + if (visible) { + changed.current = false; + Animated.parallel([ + Animated.timing(slideY, { toValue: 0, duration: 220, easing: Easing.out(Easing.cubic), useNativeDriver: true }), + Animated.timing(overlayOpacity, { toValue: 1, duration: 220, useNativeDriver: true }), + ]).start(); + } else { + Animated.parallel([ + Animated.timing(slideY, { toValue: 600, duration: 180, easing: Easing.in(Easing.cubic), useNativeDriver: true }), + Animated.timing(overlayOpacity, { toValue: 0, duration: 180, useNativeDriver: true }), + ]).start(); + } + }, [visible, slideY, overlayOpacity]); + + const selection = selectionFor(entry ?? emptyAccountDeviceSync(), authority); + const collections = state.kind === 'loaded' || state.kind === 'loading' ? state.collections ?? [] : []; + const groups = React.useMemo(() => { + const out: Array<{ jmapAccountId: string; isPersonal: boolean; accountName: string; items: SyncCollection[] }> = []; + for (const collection of collections) { + let group = out.find((g) => g.jmapAccountId === collection.jmapAccountId); + if (!group) { + group = { + jmapAccountId: collection.jmapAccountId, + isPersonal: collection.isPersonal, + accountName: collection.accountName, + items: [], + }; + out.push(group); + } + group.items.push(collection); + } + return out; + }, [collections]); + + const toggle = (collection: SyncCollection) => { + const on = isCollectionSelected(selection, authority, collection); + setCollectionSelected(registryId, authority, collection.key, !on); + changed.current = true; + }; + + const errorText = state.kind !== 'error' + ? '' + : state.error === 'signedOut' + ? t('settings.device_sync.list_error_signed_out', 'This account is not signed in on this device.') + : state.error === 'auth' + ? t('settings.device_sync.list_error_auth', 'The server did not accept the sign-in.') + : state.error === 'network' + ? t('settings.device_sync.list_error_network', 'Could not reach the server.') + : t('settings.device_sync.list_error_server', 'The server could not list them.'); + + return ( + + + + + + + + + + + + + {authority === CONTACTS_AUTHORITY + ? t('settings.device_sync.choose_books_title', 'Address books to sync') + : t('settings.device_sync.choose_calendars_title', 'Calendars to sync')} + + {accountLabel} + + + + + + + + + {state.kind === 'error' && ( + + {errorText} + + + )} + {(state.kind === 'loading' || state.kind === 'idle') && collections.length === 0 && ( + + + + )} + {state.kind === 'loaded' && collections.length === 0 && ( + + + {authority === CONTACTS_AUTHORITY + ? t('settings.device_sync.no_books', 'This account has no address books.') + : t('settings.device_sync.no_calendars', 'This account has no calendars to sync.')} + + + )} + + {groups.map((group) => ( + + + {group.isPersonal + ? t('settings.device_sync.own_collections', 'Your own') + : t('settings.device_sync.shared_by', 'Shared by {name}', { name: group.accountName })} + + {group.items.map((collection) => { + const on = isCollectionSelected(selection, authority, collection); + const others = on ? otherAccountsSyncing(registryId, authority, collection, registry, allEntries) : []; + const tint = collection.color ?? c.primary; + return ( + toggle(collection)} + style={({ pressed }) => [styles.row, pressed && styles.rowPressed]} + accessibilityRole="checkbox" + accessibilityState={{ checked: on }} + accessibilityLabel={collection.name} + > + + {on && } + + + + {collection.name} + + {(collection.readOnly || collection.isDefault) && ( + + {[ + collection.isDefault ? t('contacts.address_books.default', 'Default') : null, + collection.readOnly ? t('settings.device_sync.read_only', 'Read-only') : null, + ].filter(Boolean).join(' · ')} + + )} + {others.length > 0 && ( + + + + {t( + 'settings.device_sync.also_synced_by', + 'Also synced for {accounts}, so it shows twice on this device.', + { accounts: others.join(', ') }, + )} + + + )} + + + ); + })} + + ))} + + + {authority === CONTACTS_AUTHORITY + ? t( + 'settings.device_sync.books_hint', + 'Your own address books sync unless you turn them off; shared ones only when you turn them on.', + ) + : t( + 'settings.device_sync.calendars_hint', + 'Your own calendars sync unless you turn them off; shared ones only when you turn them on. Task lists stay in Bulwark.', + )} + + + + + ); +} + +function makeStyles(c: ThemePalette) { + return StyleSheet.create({ + overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(0,0,0,0.5)' }, + sheet: { + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + maxHeight: '80%', + backgroundColor: c.popover, + borderTopLeftRadius: radius.lg, + borderTopRightRadius: radius.lg, + borderTopWidth: 1, + borderColor: c.border, + paddingTop: spacing.sm, + }, + handleHit: { alignItems: 'center', paddingTop: spacing.xs, paddingBottom: spacing.sm }, + handle: { width: 36, height: 4, borderRadius: 2, backgroundColor: c.border }, + header: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.sm, + paddingHorizontal: spacing.lg, + paddingBottom: spacing.sm, + borderBottomWidth: 1, + borderBottomColor: c.border, + }, + title: { ...typography.bodySemibold, color: c.text }, + subtitle: { ...typography.caption, color: c.mutedForeground }, + close: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center', borderRadius: radius.xs }, + body: { paddingBottom: spacing.md }, + message: { alignItems: 'center', gap: spacing.sm, padding: spacing.lg }, + errorText: { ...typography.caption, color: c.error, textAlign: 'center' }, + muted: { ...typography.caption, color: c.mutedForeground }, + group: { paddingTop: spacing.sm }, + groupTitle: { + ...typography.captionMedium, + color: c.mutedForeground, + paddingHorizontal: spacing.lg, + paddingVertical: spacing.xs, + }, + row: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.md, + paddingHorizontal: spacing.lg, + paddingVertical: spacing.sm + 2, + minHeight: 44, + }, + rowPressed: { backgroundColor: c.surfaceHover }, + box: { + width: 18, + height: 18, + borderRadius: radius.xs, + borderWidth: 2, + alignItems: 'center', + justifyContent: 'center', + }, + rowName: { ...typography.body, color: c.text }, + rowNameOff: { color: c.textSecondary }, + warningRow: { flexDirection: 'row', alignItems: 'flex-start', gap: 4, marginTop: 2 }, + warningText: { ...typography.caption, color: c.warning, flex: 1 }, + hint: { + ...typography.caption, + color: c.mutedForeground, + paddingHorizontal: spacing.lg, + paddingTop: spacing.md, + }, + }); +} diff --git a/src/components/settings/device-sync/DeviceSyncSection.tsx b/src/components/settings/device-sync/DeviceSyncSection.tsx new file mode 100644 index 00000000..46382501 --- /dev/null +++ b/src/components/settings/device-sync/DeviceSyncSection.tsx @@ -0,0 +1,118 @@ +// "Sync to this device" in Settings → Contacts and Settings → Calendar +// (Android only, #34; docs/device-sync.md "Settings sections"): one block per +// signed-in account, then the accounts the app signed out while they synced +// (their data stays until the user signs in again or removes it). The panes +// pass the title and description, so the settings search indexes them where +// they are rendered. +import React from 'react'; +import { StyleSheet, View } from 'react-native'; +import { useNavigation } from '@react-navigation/native'; +import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; +import { SettingsSection, SettingItem } from '../settings-section'; +import Button from '../../Button'; +import Dialog from '../../Dialog'; +import { spacing, type ThemePalette } from '../../../theme/tokens'; +import { useColors } from '../../../theme/colors'; +import { useAccountStore } from '../../../stores/account-store'; +import { useDeviceSyncStore } from '../../../stores/device-sync-store'; +import { useLocaleStore } from '../../../stores/locale-store'; +import { removeSuspendedAccount } from '../../../device-sync/app/lifecycle'; +import type { Authority } from '../../../device-sync/types'; +import type { RootStackParamList } from '../../../navigation/types'; +import { DeviceSyncAccountRow } from './DeviceSyncAccountRow'; + +interface Props { + authority: Authority; + title: string; + description: string; +} + +export function DeviceSyncSection({ authority, title, description }: Props) { + const c = useColors(); + const styles = React.useMemo(() => makeStyles(c), [c]); + const accounts = useAccountStore((s) => s.accounts); + const syncEntries = useDeviceSyncStore((s) => s.accounts); + const suspended = React.useMemo( + () => Object.keys(syncEntries) + .filter((id) => syncEntries[id].suspended && !accounts.some((a) => a.id === id)) + .sort(), + [syncEntries, accounts], + ); + if (accounts.length === 0 && suspended.length === 0) return null; + return ( + + + {accounts.map((account) => ( + + ))} + {suspended.map((registryId) => ( + + ))} + + + ); +} + +// An account the app signed out (its sign-in failed) while it synced: sync is +// off, the data stays. Signing in again resumes it; removing drops it. +function SuspendedAccountRow({ registryId, styles }: { registryId: string; styles: ReturnType }) { + const t = useLocaleStore((s) => s.t); + const navigation = useNavigation>(); + const [confirming, setConfirming] = React.useState(false); + const [removing, setRemoving] = React.useState(false); + + const remove = async () => { + setConfirming(false); + setRemoving(true); + try { + await removeSuspendedAccount(registryId); + } finally { + setRemoving(false); + } + }; + + return ( + + + + + + + { void remove(); }} + onCancel={() => setConfirming(false)} + /> + + ); +} + +function makeStyles(c: ThemePalette) { + return StyleSheet.create({ + section: { marginTop: spacing.xxxl }, + block: { + borderBottomWidth: 1, + borderBottomColor: c.border, + paddingBottom: spacing.md, + }, + actions: { flexDirection: 'row', flexWrap: 'wrap', gap: spacing.sm }, + }); +} diff --git a/src/components/settings/device-sync/__tests__/status.test.ts b/src/components/settings/device-sync/__tests__/status.test.ts new file mode 100644 index 00000000..9de1f544 --- /dev/null +++ b/src/components/settings/device-sync/__tests__/status.test.ts @@ -0,0 +1,81 @@ +import { describe, it, expect } from 'vitest'; +import { translate, type MessageParams } from '../../../../i18n'; +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, type RunStatus } from '../../../../device-sync/types'; +import { describeSyncStatus, relativeTime, type StatusInput } from '../status'; + +const t = (key: string, fallback?: string, params?: MessageParams) => translate('en', key, fallback, params); + +const NOW = Date.UTC(2026, 8, 27, 12, 0, 0); + +const run = (outcome: RunStatus['outcome'], extra: Partial = {}): RunStatus => ({ + at: NOW - 5 * 60_000, + outcome, + durationMs: 100, + conflicts: 0, + itemErrors: 0, + stats: { + downloaded: { created: 0, updated: 0, deleted: 0 }, + uploaded: { created: 0, updated: 0, deleted: 0 }, + entries: 0, + skipped: 0, + }, + ...extra, +}); + +const base: StatusInput = { + authority: CONTACTS_AUTHORITY, + on: true, + paused: false, + removed: false, + permitted: true, + syncing: false, + now: NOW, +}; + +describe('describeSyncStatus', () => { + it('says off, or that the account was removed in Android settings', () => { + expect(describeSyncStatus({ ...base, on: false }, t)).toEqual({ text: 'Off', tone: 'muted' }); + expect(describeSyncStatus({ ...base, on: false, removed: true }, t).tone).toBe('warning'); + }); + + it('puts a revoked permission first, with a way to grant it again', () => { + const line = describeSyncStatus({ ...base, permitted: false, lastRun: run('ok') }, t); + expect(line).toMatchObject({ tone: 'error', action: 'grantAccess' }); + expect(line.text).toContain('contacts'); + expect(describeSyncStatus({ ...base, authority: CALENDAR_AUTHORITY, permitted: false }, t).text).toContain('calendars'); + }); + + it('says when Android paused the account', () => { + expect(describeSyncStatus({ ...base, on: false, paused: true }, t)) + .toEqual({ text: 'Paused in Android settings', tone: 'warning' }); + }); + + it('shows a running sync, then when it last synced', () => { + expect(describeSyncStatus({ ...base, syncing: true, lastRun: run('ok') }, t).text).toBe('Syncing…'); + expect(describeSyncStatus({ ...base, lastRun: run('ok') }, t)).toEqual({ text: 'Synced 5 min ago', tone: 'muted' }); + expect(describeSyncStatus({ ...base, lastRun: run('ok', { itemErrors: 2 }) }, t)).toEqual({ + text: 'Synced 5 min ago · 2 items could not be synced.', + tone: 'warning', + }); + expect(describeSyncStatus(base, t).text).toBe('Waiting for the first sync'); + }); + + it('offers what fixes a failed run', () => { + expect(describeSyncStatus({ ...base, lastRun: run('auth') }, t)).toMatchObject({ tone: 'error', action: 'signIn' }); + expect(describeSyncStatus({ ...base, lastRun: run('tooManyDeletions') }, t)).toMatchObject({ action: 'reviewDeletions' }); + expect(describeSyncStatus({ ...base, lastRun: run('permission') }, t)).toMatchObject({ action: 'grantAccess' }); + expect(describeSyncStatus({ ...base, lastRun: run('io') }, t).text).toBe('Could not reach the server 5 min ago. Android tries again.'); + expect(describeSyncStatus({ ...base, lastRun: run('internal', { message: 'Boom' }) }, t).text) + .toBe('The last sync failed: Boom'); + // A run that found sync off says nothing new. + expect(describeSyncStatus({ ...base, lastRun: run('disabled') }, t).text).toBe('Waiting for the next sync'); + }); +}); + +describe('relativeTime', () => { + it('counts minutes, hours and days', () => { + expect(relativeTime(NOW - 10_000, NOW, t)).toBe('just now'); + expect(relativeTime(NOW - 3 * 3600_000, NOW, t)).toBe('3 h ago'); + expect(relativeTime(NOW - 2 * 86_400_000, NOW, t)).toBe('2 d ago'); + }); +}); diff --git a/src/components/settings/device-sync/status.ts b/src/components/settings/device-sync/status.ts new file mode 100644 index 00000000..57d6a940 --- /dev/null +++ b/src/components/settings/device-sync/status.ts @@ -0,0 +1,133 @@ +// The status line of one account's device sync in Settings: off, paused in +// Android settings, syncing, when it last synced, or what went wrong and what +// the user can do about it (docs/device-sync.md, "Settings sections", +// "Failure matrix"). +import { CONTACTS_AUTHORITY, type Authority, type RunStatus } from '../../../device-sync/types'; +import type { TranslateFn } from '../../../stores/locale-store'; + +/** What the status line offers to do. */ +export type StatusAction = 'signIn' | 'grantAccess' | 'reviewDeletions'; + +export interface StatusLine { + text: string; + tone: 'muted' | 'warning' | 'error'; + action?: StatusAction; +} + +export interface StatusInput { + authority: Authority; + /** The switch: Android syncs this authority automatically. */ + on: boolean; + /** On in the app but off in Android's settings for the account. */ + paused: boolean; + /** The Android account was removed in Android Settings. */ + removed: boolean; + /** The runtime permissions; null while unknown. */ + permitted: boolean | null; + syncing: boolean; + lastRun?: RunStatus; + now: number; +} + +/** "just now", "5 min ago", … */ +export function relativeTime(at: number, now: number, t: TranslateFn): string { + const diff = Math.max(0, now - at); + if (diff < 60_000) return t('settings.offline.just_now', 'just now'); + if (diff < 60 * 60_000) return t('settings.offline.minutes_ago', '{count} min ago', { count: Math.round(diff / 60_000) }); + if (diff < 24 * 60 * 60_000) { + return t('settings.offline.hours_ago', '{count} h ago', { count: Math.round(diff / (60 * 60_000)) }); + } + return t('settings.offline.days_ago', '{count} d ago', { count: Math.round(diff / (24 * 60 * 60_000)) }); +} + +function permissionLine(authority: Authority, t: TranslateFn): StatusLine { + return { + text: authority === CONTACTS_AUTHORITY + ? t('settings.device_sync.status_permission_contacts', 'Access to contacts was turned off, so nothing syncs.') + : t('settings.device_sync.status_permission_calendar', 'Access to calendars was turned off, so nothing syncs.'), + tone: 'error', + action: 'grantAccess', + }; +} + +function runLine(run: RunStatus, input: StatusInput, t: TranslateFn): StatusLine | null { + const when = relativeTime(run.at, input.now, t); + switch (run.outcome) { + case 'ok': { + const synced = t('settings.device_sync.status_synced', 'Synced {when}', { when }); + if (run.itemErrors <= 0) return { text: synced, tone: 'muted' }; + const skipped = t( + 'settings.device_sync.status_item_errors', + '{count, plural, one {# item could not be synced.} other {# items could not be synced.}}', + { count: run.itemErrors }, + ); + return { text: `${synced} · ${skipped}`, tone: 'warning' }; + } + case 'auth': + return { + text: t('settings.device_sync.status_auth', 'The server no longer accepts the sign-in. Sign in again to keep syncing.'), + tone: 'error', + action: 'signIn', + }; + case 'permission': + return permissionLine(input.authority, t); + case 'tooManyDeletions': + return { + text: t( + 'settings.device_sync.status_too_many_deletions', + 'Many items were deleted on this device. They are not deleted on the server until you confirm.', + ), + tone: 'warning', + action: 'reviewDeletions', + }; + case 'io': + return { + text: t('settings.device_sync.status_io', 'Could not reach the server {when}. Android tries again.', { when }), + tone: 'warning', + }; + case 'unsupported': + return { + text: input.authority === CONTACTS_AUTHORITY + ? t('settings.device_sync.status_unsupported_contacts', 'The server offers no address books for this account.') + : t('settings.device_sync.status_unsupported_calendar', 'The server offers no calendars for this account.'), + tone: 'error', + }; + case 'safetyAbort': + return { + text: t( + 'settings.device_sync.status_safety_abort', + 'Stopped: the server returned nothing while this device still holds items. Check the server, then sync again.', + ), + tone: 'error', + }; + case 'cancelled': + return { text: t('settings.device_sync.status_cancelled', 'The last sync stopped early. It continues next time.'), tone: 'muted' }; + case 'internal': + return { + text: run.message + ? t('settings.device_sync.status_failed', 'The last sync failed: {message}', { message: run.message }) + : t('settings.device_sync.status_failed_plain', 'The last sync failed.'), + tone: 'error', + }; + case 'disabled': + default: + return null; + } +} + +export function describeSyncStatus(input: StatusInput, t: TranslateFn): StatusLine { + if (!input.on && !input.paused) { + return input.removed + ? { + text: t('settings.device_sync.status_removed', 'Removed in Android settings. Turn on to add the account again.'), + tone: 'warning', + } + : { text: t('settings.device_sync.status_off', 'Off'), tone: 'muted' }; + } + if (input.permitted === false) return permissionLine(input.authority, t); + if (input.paused) return { text: t('settings.device_sync.status_paused', 'Paused in Android settings'), tone: 'warning' }; + if (input.syncing) return { text: t('settings.device_sync.status_syncing', 'Syncing…'), tone: 'muted' }; + if (!input.lastRun) return { text: t('settings.device_sync.status_waiting', 'Waiting for the first sync'), tone: 'muted' }; + return runLine(input.lastRun, input, t) + ?? { text: t('settings.device_sync.status_waiting_next', 'Waiting for the next sync'), tone: 'muted' }; +} diff --git a/src/components/settings/device-sync/use-android-sync.ts b/src/components/settings/device-sync/use-android-sync.ts new file mode 100644 index 00000000..22f5fc75 --- /dev/null +++ b/src/components/settings/device-sync/use-android-sync.ts @@ -0,0 +1,101 @@ +// Android's side of one account's device sync for the settings: whether the +// Android account exists, its automatic sync for the authority (the switch), +// the device's master auto-sync, whether a sync runs or waits, and whether +// the runtime permissions are still granted. Re-read on mount, when the app +// returns to the foreground, when accounts of our type change, and every few +// seconds while a sync runs or waits. +import React from 'react'; +import { AppState } from 'react-native'; +import { getSyncSettings, listAndroidAccounts, onAccountsChanged } from '../../../device-sync/native'; +import { hasSyncPermissions } from '../../../device-sync/app/permissions'; +import type { Authority } from '../../../device-sync/types'; + +export interface AndroidSyncView { + /** The Android account exists. */ + exists: boolean; + /** Automatic sync for the authority: the toggle in Settings → Accounts. */ + automatic: boolean; + /** The device's "Auto-sync data". */ + masterAutomatic: boolean; + active: boolean; + pending: boolean; +} + +const POLL_MS = 3000; + +export function useAndroidSync(accountName: string | undefined, authority: Authority): { + view: AndroidSyncView | null; + permitted: boolean | null; + refresh: () => Promise; +} { + const [view, setView] = React.useState(null); + const [permitted, setPermitted] = React.useState(null); + const alive = React.useRef(true); + React.useEffect(() => { + alive.current = true; + return () => { alive.current = false; }; + }, []); + // Read at call time: turning sync on creates the account while the + // caller still holds the refresh of the render before. + const nameRef = React.useRef(accountName); + nameRef.current = accountName; + // Only the latest read may land; an older one resolving late is stale. + const reads = React.useRef(0); + + const refresh = React.useCallback(async () => { + const read = ++reads.current; + const accountName = nameRef.current; + const granted = await hasSyncPermissions(authority); + let next: AndroidSyncView = { exists: false, automatic: false, masterAutomatic: true, active: false, pending: false }; + if (accountName) { + try { + const accounts = await listAndroidAccounts(); + if (accounts.some((a) => a.name === accountName)) { + const settings = await getSyncSettings(accountName); + const own = settings.authorities?.[authority]; + next = { + exists: true, + automatic: !!own?.automatic, + masterAutomatic: settings.masterAutomatic !== false, + active: !!own?.active, + pending: !!own?.pending, + }; + } + } catch { + // Shown as off until the next read. + } + } + if (!alive.current || read !== reads.current) return; + setPermitted(granted); + setView(next); + }, [authority]); + + React.useEffect(() => { + void refresh(); + }, [refresh, accountName]); + + React.useEffect(() => { + const subscription = AppState.addEventListener('change', (state) => { + if (state === 'active') void refresh(); + }); + let unsubscribe: () => void = () => undefined; + try { + unsubscribe = onAccountsChanged(() => { void refresh(); }); + } catch { + // Foreground reads still catch up. + } + return () => { + subscription.remove(); + unsubscribe(); + }; + }, [refresh]); + + const busy = !!view?.active || !!view?.pending; + React.useEffect(() => { + if (!busy) return undefined; + const timer = setInterval(() => { void refresh(); }, POLL_MS); + return () => clearInterval(timer); + }, [busy, refresh]); + + return { view, permitted, refresh }; +} diff --git a/src/components/settings/device-sync/use-sync-collections.ts b/src/components/settings/device-sync/use-sync-collections.ts new file mode 100644 index 00000000..3f5baf6b --- /dev/null +++ b/src/components/settings/device-sync/use-sync-collections.ts @@ -0,0 +1,61 @@ +// The address books or calendars of one account for the device sync settings, +// listed through the account's own client (device-sync/app/collections) and +// cached for a little while, so the chooser, the counts and the "new contacts +// go to" picker share one request. +import React from 'react'; +import { + cachedSyncCollections, + CollectionsError, + listSyncCollections, + type CollectionsErrorKind, + type SyncCollection, +} from '../../../device-sync/app/collections'; +import { CALENDAR_AUTHORITY, type Authority } from '../../../device-sync/types'; +import { useAuthStore } from '../../../stores/auth-store'; +import { useCalendarSubscriptionsStore } from '../../../stores/calendar-subscriptions-store'; + +export type CollectionsState = + | { kind: 'idle' } + | { kind: 'loading'; collections: SyncCollection[] | null } + | { kind: 'loaded'; collections: SyncCollection[] } + | { kind: 'error'; error: CollectionsErrorKind }; + +export function useSyncCollections(registryId: string, authority: Authority, active: boolean): { + state: CollectionsState; + reload: () => void; +} { + const [state, setState] = React.useState(() => { + const cached = cachedSyncCollections(registryId, authority); + return cached ? { kind: 'loaded', collections: cached } : { kind: 'idle' }; + }); + const [attempt, setAttempt] = React.useState(0); + // Calendars mirroring subscribed feeds are read-only on the device. Only + // the active account's subscriptions are known here. + const isActive = useAuthStore((s) => s.activeAccountId === registryId); + const subscriptions = useCalendarSubscriptionsStore((s) => s.subscriptions); + const feeds = React.useMemo( + () => (authority === CALENDAR_AUTHORITY && isActive + ? subscriptions.map((sub) => ({ calendarId: sub.calendarId, accountId: sub.accountId })) + : []), + [authority, isActive, subscriptions], + ); + + React.useEffect(() => { + if (!active) return undefined; + let cancelled = false; + const force = attempt > 0; + setState((prev) => ({ kind: 'loading', collections: prev.kind === 'loaded' ? prev.collections : null })); + listSyncCollections(registryId, authority, { force, feeds }) + .then((collections) => { + if (!cancelled) setState({ kind: 'loaded', collections }); + }) + .catch((err: unknown) => { + if (cancelled) return; + setState({ kind: 'error', error: err instanceof CollectionsError ? err.kind : 'server' }); + }); + return () => { cancelled = true; }; + }, [registryId, authority, active, attempt, feeds]); + + const reload = React.useCallback(() => setAttempt((n) => n + 1), []); + return { state, reload }; +} diff --git a/src/device-sync/__tests__/app/collections.test.ts b/src/device-sync/__tests__/app/collections.test.ts new file mode 100644 index 00000000..023268f9 --- /dev/null +++ b/src/device-sync/__tests__/app/collections.test.ts @@ -0,0 +1,230 @@ +import { describe, it, expect, beforeEach } from 'vitest'; +import { + CollectionsError, + forgetSyncCollections, + listSyncCollections, + otherAccountsSyncing, + type CollectionsClient, +} from '../../app/collections'; +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, JMAP_CALENDARS, JMAP_CONTACTS } from '../../types'; +import { useDeviceSyncStore, waitForDeviceSyncHydration } from '../../../stores/device-sync-store'; +import type { JMAPMethodCall } from '../../../api/types'; + +type Handler = (args: Record, calls: JMAPMethodCall[]) => any; + +// A client for one account with the personal JMAP account `c` and a shared +// account `team`, answering the calls the listing makes. +function fakeClient(handlers: Record, options: { loaded?: boolean | Error; maxCalls?: number } = {}) { + const requests: JMAPMethodCall[][] = []; + const client: CollectionsClient = { + async loadAccount() { + if (options.loaded instanceof Error) throw options.loaded; + return options.loaded ?? true; + }, + get currentSession() { + return { + primaryAccounts: { [JMAP_CONTACTS]: 'c', [JMAP_CALENDARS]: 'c' }, + accounts: { + c: { name: 'alice@example.org', isPersonal: true, accountCapabilities: { [JMAP_CONTACTS]: {}, [JMAP_CALENDARS]: {} } }, + team: { name: 'Team', isPersonal: false, accountCapabilities: { [JMAP_CONTACTS]: {}, [JMAP_CALENDARS]: {} } }, + mailonly: { name: 'Archive', isPersonal: false, accountCapabilities: {} }, + }, + }; + }, + async request(calls) { + requests.push(calls); + const responses: Array<[string, any, string]> = []; + for (const [name, args, id] of calls) { + const handler = handlers[`${name} ${args.accountId}`] ?? handlers[name]; + if (!handler) { + responses.push(['error', { type: 'unknownMethod' }, id]); + continue; + } + const body = handler(args, calls); + responses.push(body === null ? ['error', { type: 'forbidden' }, id] : [name, body, id]); + } + return { methodResponses: responses }; + }, + getMaxCallsInRequest: () => options.maxCalls ?? 16, + }; + return { client, requests }; +} + +beforeEach(async () => { + await waitForDeviceSyncHydration(); + useDeviceSyncStore.setState({ accounts: {} }); + forgetSyncCollections(); +}); + +describe('listSyncCollections', () => { + it('lists the personal address books first, then the shared ones, and marks read-only ones', async () => { + const { client, requests } = fakeClient({ + 'AddressBook/get c': () => ({ list: [ + { id: 'b2', name: 'Work', sortOrder: 2, myRights: { mayWrite: true } }, + { id: 'b1', name: 'Personal', isDefault: true, sortOrder: 1, myRights: { mayWrite: true } }, + ] }), + 'AddressBook/get team': () => ({ list: [{ id: 'b1', name: 'Team contacts', myRights: { mayWrite: false } }] }), + }); + const list = await listSyncCollections('alice@x', CONTACTS_AUTHORITY, { client }); + expect(list.map((c) => [c.key, c.name, c.isPersonal, c.readOnly, c.isDefault])).toEqual([ + ['c/b1', 'Personal', true, false, true], + ['c/b2', 'Work', true, false, false], + ['team/b1', 'Team contacts', false, true, false], + ]); + expect(list[2].accountName).toBe('Team'); + // Explicit properties, the contacts capability, and never the mail-only account. + const [calls] = requests; + expect(calls.map(([name, args]) => `${name} ${args.accountId}`)).toEqual(['AddressBook/get c', 'AddressBook/get team']); + expect(calls[0][1].properties).toEqual(expect.arrayContaining(['id', 'name', 'myRights'])); + }); + + it('remembers the personal JMAP account for the push routes', async () => { + const { client } = fakeClient({ 'AddressBook/get': () => ({ list: [] }) }); + await listSyncCollections('alice@x', CONTACTS_AUTHORITY, { client }); + expect(useDeviceSyncStore.getState().accounts['alice@x']?.primaryJmapAccounts).toEqual({ [CONTACTS_AUTHORITY]: 'c' }); + }); + + it('keeps the list when a shared account refuses', async () => { + const { client } = fakeClient({ + 'AddressBook/get c': () => ({ list: [{ id: 'b1', name: 'Personal' }] }), + 'AddressBook/get team': () => null, + }); + const list = await listSyncCollections('alice@x', CONTACTS_AUTHORITY, { client }); + expect(list.map((c) => c.key)).toEqual(['c/b1']); + }); + + it('leaves out calendars it may not read and calendars holding only tasks', async () => { + // What each calendar's scan finds: `todo` holds only tasks, `empty` nothing. + const scans: Record = { + cal1: [{ id: 'e', '@type': 'Event' }], + todo: [{ id: 't', '@type': 'Task' }, { id: 'u', due: '2026-10-01T00:00:00' }], + empty: [], + }; + const { client } = fakeClient({ + 'Calendar/get c': () => ({ list: [ + { id: 'cal1', name: 'Personal', color: '#ff0000', myRights: { mayReadItems: true, mayWriteAll: true } }, + { id: 'todo', name: 'Tasks', myRights: { mayReadItems: true, mayWriteAll: true } }, + { id: 'empty', name: 'New', myRights: { mayReadItems: true, mayWriteAll: true } }, + { id: 'hidden', name: 'Free/busy only', myRights: { mayReadItems: false } }, + ] }), + 'Calendar/get team': () => ({ list: [ + { id: 'cal1', name: 'Team', myRights: { mayReadItems: true, mayWriteAll: false, mayWriteOwn: false } }, + ] }), + 'CalendarEvent/query': (args) => ({ ids: (scans[args.filter.inCalendar] ?? []).map((o) => o.id) }), + 'CalendarEvent/get': (args, calls) => { + // The get reads the ids of the query it references. + const query = calls.find(([, , id]) => id === args['#ids'].resultOf)!; + return { list: scans[(query[1].filter as { inCalendar: string }).inCalendar] ?? [] }; + }, + }); + const list = await listSyncCollections('alice@x', CALENDAR_AUTHORITY, { client }); + // By name within an account (no sort order given). + expect(list.map((c) => [c.key, c.readOnly])).toEqual([ + ['c/empty', false], + ['c/cal1', false], + ['team/cal1', true], + ]); + expect(list.find((c) => c.key === 'c/cal1')?.color).toBe('#ff0000'); + }); + + it('marks calendars mirroring a subscribed feed read-only', async () => { + const { client } = fakeClient({ + 'Calendar/get c': () => ({ list: [ + { id: 'feed', name: 'Holidays', myRights: { mayReadItems: true, mayWriteAll: true } }, + ] }), + 'Calendar/get team': () => ({ list: [] }), + 'CalendarEvent/query': () => ({ ids: [] }), + 'CalendarEvent/get': () => ({ list: [] }), + }); + const list = await listSyncCollections('alice@x', CALENDAR_AUTHORITY, { client, feeds: [{ calendarId: 'feed' }] }); + expect(list.map((c) => [c.key, c.readOnly])).toEqual([['c/feed', true]]); + }); + + it('keeps a query and the get that reads it in one request', async () => { + const { client, requests } = fakeClient({ + 'Calendar/get': () => ({ list: [ + { id: 'a', name: 'A' }, { id: 'b', name: 'B' }, { id: 'c', name: 'C' }, + ] }), + 'CalendarEvent/query': () => ({ ids: [] }), + 'CalendarEvent/get': () => ({ list: [] }), + }, { maxCalls: 5 }); + await listSyncCollections('alice@x', CALENDAR_AUTHORITY, { client }); + const scanRequests = requests.filter((calls) => calls[0][0] === 'CalendarEvent/query'); + for (const calls of scanRequests) { + expect(calls.length % 2).toBe(0); + expect(calls.length).toBeLessThanOrEqual(5); + } + }); + + it('says why the list could not be loaded', async () => { + const signedOut = fakeClient({}, { loaded: false }).client; + await expect(listSyncCollections('alice@x', CONTACTS_AUTHORITY, { client: signedOut })) + .rejects.toMatchObject({ kind: 'signedOut' }); + const auth = Object.assign(new Error('Invalid credentials'), { name: 'AuthenticationError' }); + await expect(listSyncCollections('alice@x', CONTACTS_AUTHORITY, { client: fakeClient({}, { loaded: auth }).client })) + .rejects.toBeInstanceOf(CollectionsError); + await expect(listSyncCollections('alice@x', CONTACTS_AUTHORITY, { client: fakeClient({}, { loaded: auth }).client })) + .rejects.toMatchObject({ kind: 'auth' }); + const offline = Object.assign(new Error('Server unreachable'), { name: 'NetworkError' }); + await expect(listSyncCollections('alice@x', CONTACTS_AUTHORITY, { client: fakeClient({}, { loaded: offline }).client })) + .rejects.toMatchObject({ kind: 'network' }); + }); +}); + +describe('otherAccountsSyncing', () => { + const registry = [ + { id: 'alice@mail.example.org', serverUrl: 'https://mail.example.org', email: 'alice@example.org', username: 'alice' }, + { id: 'bob@mail.example.org', serverUrl: 'https://mail.example.org/', email: 'bob@example.org', username: 'bob' }, + { id: 'bob@other.example.net', serverUrl: 'https://other.example.net', email: 'bob@example.net', username: 'bob' }, + ]; + + it('names the other accounts on the same server that sync the same collection', () => { + const accounts = { + 'bob@mail.example.org': { + contactsSelection: { 'team/b1': true }, + calendarSelection: {}, + reminderOwner: null, + intervalSeconds: {}, + lastRun: {}, + enabled: { [CONTACTS_AUTHORITY]: true }, + }, + // Another server: the same JMAP ids mean other data. + 'bob@other.example.net': { + contactsSelection: { 'team/b1': true }, + calendarSelection: {}, + reminderOwner: null, + intervalSeconds: {}, + lastRun: {}, + enabled: { [CONTACTS_AUTHORITY]: true }, + }, + }; + const collection = { key: 'team/b1', jmapAccountId: 'team' }; + expect(otherAccountsSyncing('alice@mail.example.org', CONTACTS_AUTHORITY, collection, registry, accounts)) + .toEqual(['bob@example.org']); + // Not for another authority, nor once Bob's contacts sync is off. + expect(otherAccountsSyncing('alice@mail.example.org', CALENDAR_AUTHORITY, collection, registry, accounts)).toEqual([]); + accounts['bob@mail.example.org'].enabled = { [CONTACTS_AUTHORITY]: false }; + expect(otherAccountsSyncing('alice@mail.example.org', CONTACTS_AUTHORITY, collection, registry, accounts)).toEqual([]); + }); + + it("counts the other account's own collections, which sync by default", () => { + const accounts = { + 'bob@mail.example.org': { + contactsSelection: {}, + calendarSelection: {}, + reminderOwner: null, + intervalSeconds: {}, + lastRun: {}, + enabled: { [CALENDAR_AUTHORITY]: true }, + primaryJmapAccounts: { [CALENDAR_AUTHORITY]: 'bobacct' }, + }, + }; + expect(otherAccountsSyncing( + 'alice@mail.example.org', + CALENDAR_AUTHORITY, + { key: 'bobacct/cal1', jmapAccountId: 'bobacct' }, + registry, + accounts, + )).toEqual(['bob@example.org']); + }); +}); diff --git a/src/device-sync/app/collections.ts b/src/device-sync/app/collections.ts new file mode 100644 index 00000000..923faf4b --- /dev/null +++ b/src/device-sync/app/collections.ts @@ -0,0 +1,334 @@ +/** + * The address books and calendars an app account can sync, for the chooser in + * Settings (docs/device-sync.md, "Settings sections"). + * + * Listed through a JMAPClient of the account's own, never the UI's + * singleton: the chooser covers every signed-in account, and re-binding the + * singleton would send the UI's requests with another account's credentials. + * Collections of the capability's primary account are "personal" (on by + * default); every other session account that advertises the capability holds + * shared ones (off by default). Calendars without `mayReadItems` and + * calendars that hold only tasks are not offered (the engine does not sync + * them); the birthday calendar is the app's own and never on the server. + */ +import { AuthenticationError, JMAPClient, NetworkError } from '../../api/jmap-client'; +import type { JMAPMethodCall } from '../../api/types'; +import { + findTasksOnlyCalendarIds, + SCAN_PROPERTIES, + type ScannedCalendarObject, +} from '../../lib/calendar-component-detection'; +import { + isCollectionSelected, + selectionFor, + syncOnInApp, + useDeviceSyncStore, + type AccountDeviceSync, +} from '../../stores/device-sync-store'; +import { collectionKey } from '../common/ids'; +import { + CALENDAR_AUTHORITY, + CONTACTS_AUTHORITY, + JMAP_CALENDARS, + JMAP_CONTACTS, + JMAP_CORE, + type Authority, + type CollectionKey, +} from '../types'; + +export interface SyncCollection { + /** `/`, as in the selections. */ + key: CollectionKey; + jmapAccountId: string; + id: string; + name: string; + /** The JMAP account's name (who shares it, for a shared collection). */ + accountName: string; + /** In the capability's primary account: synced unless the user turned it off. */ + isPersonal: boolean; + /** No write rights, or (calendars) a subscribed feed. */ + readOnly: boolean; + isDefault: boolean; + color?: string; +} + +/** Why the list could not be loaded. */ +export type CollectionsErrorKind = 'signedOut' | 'auth' | 'network' | 'server'; + +export class CollectionsError extends Error { + constructor(readonly kind: CollectionsErrorKind, message: string) { + super(message); + this.name = 'CollectionsError'; + } +} + +/** The part of JMAPClient the listing uses. */ +export interface CollectionsClient { + loadAccount(registryId: string): Promise; + readonly currentSession: { + primaryAccounts: Record; + accounts: Record }>; + } | null; + request(calls: JMAPMethodCall[], using?: string[]): Promise<{ methodResponses: Array<[string, any, string]> }>; + getMaxCallsInRequest(): number; +} + +export interface ListOptions { + /** Skip the cache. */ + force?: boolean; + /** For tests: the client to list with. */ + client?: CollectionsClient; + /** Calendars mirroring subscribed iCal feeds (read-only on the device); `accountId` absent = the primary account. */ + feeds?: ReadonlyArray<{ calendarId: string; accountId?: string }>; +} + +const CACHE_TTL_MS = 2 * 60_000; +/** How many objects of a calendar tell whether it holds only tasks (the engine checks the same). */ +const TASK_SCAN_LIMIT = 50; + +const ADDRESS_BOOK_PROPERTIES = ['id', 'name', 'isDefault', 'sortOrder', 'myRights']; +const CALENDAR_PROPERTIES = ['id', 'name', 'color', 'isDefault', 'sortOrder', 'isSubscribed', 'myRights']; + +const cache = new Map(); +const inFlight = new Map>(); + +const cacheKey = (registryId: string, authority: Authority) => `${registryId}\n${authority}`; + +/** The last list loaded for an account, if it is still fresh. */ +export function cachedSyncCollections(registryId: string, authority: Authority): SyncCollection[] | null { + const hit = cache.get(cacheKey(registryId, authority)); + return hit && Date.now() - hit.at < CACHE_TTL_MS ? hit.collections : null; +} + +/** Drops the cached lists of one account, or of all (tests). */ +export function forgetSyncCollections(registryId?: string): void { + for (const key of [...cache.keys()]) { + if (!registryId || key.startsWith(`${registryId}\n`)) cache.delete(key); + } +} + +/** The collections of one app account for an authority, personal ones first. */ +export function listSyncCollections( + registryId: string, + authority: Authority, + options: ListOptions = {}, +): Promise { + const key = cacheKey(registryId, authority); + if (!options.force && !options.client) { + const hit = cachedSyncCollections(registryId, authority); + if (hit) return Promise.resolve(hit); + const running = inFlight.get(key); + if (running) return running; + } + const run = load(registryId, authority, options).then((collections) => { + cache.set(key, { at: Date.now(), collections }); + return collections; + }); + if (!options.client) { + inFlight.set(key, run); + void run.finally(() => { + if (inFlight.get(key) === run) inFlight.delete(key); + }).catch(() => undefined); + } + return run; +} + +async function openClient(registryId: string, client: CollectionsClient): Promise { + let ok: boolean; + try { + ok = await client.loadAccount(registryId); + } catch (err) { + if (err instanceof AuthenticationError || (err instanceof Error && err.name === 'AuthenticationError')) { + throw new CollectionsError('auth', err.message); + } + if (err instanceof NetworkError || (err instanceof Error && err.name === 'NetworkError')) { + throw new CollectionsError('network', err.message); + } + throw new CollectionsError('server', err instanceof Error ? err.message : String(err)); + } + if (!ok) throw new CollectionsError('signedOut', 'No stored credentials'); + return client; +} + +async function send( + client: CollectionsClient, + calls: JMAPMethodCall[], + using: string[], +): Promise> { + const out: Array<[string, any, string]> = []; + // Result references only point inside one request, so a query and the + // get that reads it always go together. + const perRequest = Math.max(2, client.getMaxCallsInRequest() - (client.getMaxCallsInRequest() % 2)); + for (let i = 0; i < calls.length; i += perRequest) { + try { + const res = await client.request(calls.slice(i, i + perRequest), using); + out.push(...(res.methodResponses ?? [])); + } catch (err) { + if (err instanceof Error && err.name === 'AuthenticationError') throw new CollectionsError('auth', err.message); + throw new CollectionsError('network', err instanceof Error ? err.message : String(err)); + } + } + return out; +} + +function responseOf(responses: Array<[string, any, string]>, callId: string): any { + const hit = responses.find((r) => r[2] === callId); + return hit && hit[0] !== 'error' ? hit[1] : null; +} + +interface RawCollection { + id: string; + name?: string; + color?: string; + isDefault?: boolean; + sortOrder?: number; + myRights?: Record; +} + +async function load(registryId: string, authority: Authority, options: ListOptions): Promise { + const client = await openClient(registryId, options.client ?? new JMAPClient()); + const session = client.currentSession; + if (!session) throw new CollectionsError('server', 'No session'); + const capability = authority === CONTACTS_AUTHORITY ? JMAP_CONTACTS : JMAP_CALENDARS; + const using = [JMAP_CORE, capability]; + const capable = Object.entries(session.accounts ?? {}) + .filter(([, info]) => !!info?.accountCapabilities?.[capability]) + .map(([id]) => id); + const primary = session.primaryAccounts?.[capability] ?? capable[0]; + if (!primary) return []; + const accountIds = [primary, ...capable.filter((id) => id !== primary)]; + // For the push routes and the trigger filter; only into a store that has + // been read, which would otherwise be overwritten. + if (useDeviceSyncStore.persist.hasHydrated() + && useDeviceSyncStore.getState().accounts[registryId]?.primaryJmapAccounts?.[authority] !== primary) { + useDeviceSyncStore.getState().rememberPrimaryJmapAccount(registryId, authority, primary); + } + + const method = authority === CONTACTS_AUTHORITY ? 'AddressBook/get' : 'Calendar/get'; + const properties = authority === CONTACTS_AUTHORITY ? ADDRESS_BOOK_PROPERTIES : CALENDAR_PROPERTIES; + const responses = await send( + client, + accountIds.map((accountId, i) => [method, { accountId, properties }, `g${i}`]), + using, + ); + + const listed: Array<{ jmapAccountId: string; raw: RawCollection }> = []; + accountIds.forEach((jmapAccountId, i) => { + const body = responseOf(responses, `g${i}`); + // An account that refuses (a revoked share) must not hide the others. + if (!body) { + if (jmapAccountId === primary) throw new CollectionsError('server', `${method} failed`); + return; + } + for (const raw of (body.list ?? []) as RawCollection[]) { + if (raw && typeof raw.id === 'string') listed.push({ jmapAccountId, raw }); + } + }); + + let offered = listed; + if (authority === CALENDAR_AUTHORITY) { + offered = listed.filter(({ raw }) => raw.myRights?.mayReadItems !== false); + const taskOnly = await taskOnlyCalendars(client, offered, using); + offered = offered.filter(({ jmapAccountId, raw }) => !taskOnly.has(collectionKey(jmapAccountId, raw.id))); + } + + const feeds = new Set((options.feeds ?? []).map((f) => collectionKey(f.accountId ?? primary, f.calendarId))); + const collections = offered.map(({ jmapAccountId, raw }): SyncCollection => { + const key = collectionKey(jmapAccountId, raw.id); + const rights = raw.myRights; + const readOnly = authority === CONTACTS_AUTHORITY + ? rights?.mayWrite === false + : feeds.has(key) || (!!rights && !rights.mayWriteAll && !rights.mayWriteOwn); + return { + key, + jmapAccountId, + id: raw.id, + name: raw.name?.trim() || raw.id, + accountName: session.accounts?.[jmapAccountId]?.name ?? jmapAccountId, + isPersonal: jmapAccountId === primary, + readOnly, + isDefault: !!raw.isDefault, + ...(typeof raw.color === 'string' && raw.color ? { color: raw.color } : {}), + }; + }); + const order = new Map(accountIds.map((id, i) => [id, i])); + const sortOf = new Map(offered.map(({ jmapAccountId, raw }) => [collectionKey(jmapAccountId, raw.id), raw.sortOrder ?? 0])); + return collections.sort((a, b) => + (order.get(a.jmapAccountId)! - order.get(b.jmapAccountId)!) + || (sortOf.get(a.key)! - sortOf.get(b.key)!) + || a.name.localeCompare(b.name)); +} + +/** + * Calendars whose first objects are all tasks. One query and one get per + * calendar; a calendar whose scan fails is kept (offered). + */ +async function taskOnlyCalendars( + client: CollectionsClient, + calendars: Array<{ jmapAccountId: string; raw: RawCollection }>, + using: string[], +): Promise> { + const calls: JMAPMethodCall[] = []; + calendars.forEach(({ jmapAccountId, raw }, i) => { + calls.push(['CalendarEvent/query', { accountId: jmapAccountId, filter: { inCalendar: raw.id }, limit: TASK_SCAN_LIMIT }, `q${i}`]); + calls.push([ + 'CalendarEvent/get', + { + accountId: jmapAccountId, + '#ids': { resultOf: `q${i}`, name: 'CalendarEvent/query', path: '/ids' }, + properties: SCAN_PROPERTIES, + }, + `o${i}`, + ]); + }); + const out = new Set(); + if (calls.length === 0) return out; + let responses: Array<[string, any, string]>; + try { + responses = await send(client, calls, using); + } catch { + return out; + } + calendars.forEach(({ jmapAccountId, raw }, i) => { + const body = responseOf(responses, `o${i}`); + if (!body) return; + const objects = ((body.list ?? []) as ScannedCalendarObject[]) + .map((o) => ({ ...o, calendarIds: { [raw.id]: true } })); + if (findTasksOnlyCalendarIds(objects, [raw.id]).has(raw.id)) out.add(collectionKey(jmapAccountId, raw.id)); + }); + return out; +} + +function serverOf(serverUrl: string): string { + try { + return new URL(serverUrl).host.toLowerCase(); + } catch { + return serverUrl.replace(/^https?:\/\//i, '').replace(/\/.*$/, '').toLowerCase(); + } +} + +/** + * The other signed-in accounts on the same server that already sync this + * collection (the same JMAP account and id): the chooser warns about the + * second copy on the device, it does not refuse it. + */ +export function otherAccountsSyncing( + registryId: string, + authority: Authority, + collection: Pick & Partial>, + registry: ReadonlyArray<{ id: string; serverUrl: string; email?: string; username: string }>, + accounts: Record, +): string[] { + const self = registry.find((a) => a.id === registryId); + if (!self) return []; + const server = serverOf(self.serverUrl); + return registry + .filter((other) => other.id !== registryId && serverOf(other.serverUrl) === server) + .filter((other) => { + const entry = accounts[other.id]; + if (!entry || !syncOnInApp(entry, authority)) return false; + const isPersonal = entry.primaryJmapAccounts?.[authority] === collection.jmapAccountId; + return isCollectionSelected(selectionFor(entry, authority), authority, { ...collection, isPersonal }); + }) + .map((other) => other.email || other.username); +} diff --git a/src/lib/__tests__/settings-search.test.ts b/src/lib/__tests__/settings-search.test.ts index d2ceb492..0b08cb57 100644 --- a/src/lib/__tests__/settings-search.test.ts +++ b/src/lib/__tests__/settings-search.test.ts @@ -93,6 +93,7 @@ describe('settings search index (English catalog)', () => { downloads: 'DownloadsSettings.tsx', security: 'AccountSecuritySettings.tsx', calendar: 'CalendarSettings.tsx', + contacts: 'ContactsSettings.tsx', files: 'FilesSettings.tsx', sidebar_apps: 'SidebarAppsSettings.tsx', about_data: 'AboutDataSettings.tsx', @@ -120,6 +121,20 @@ describe('settings search index (English catalog)', () => { expect(labelsFor('calendar')).not.toContain('Free scrolling'); expect(labelsFor('notifications')).not.toContain('Notification sound'); }); + + it('finds device sync in Contacts and Calendar on Android (#34)', () => { + // The tests run as Android (src/test-setup.ts). + expect(labelsFor('contacts')).toContain('Sync to this device'); + expect(labelsFor('calendar')).toContain('Sync to this device'); + const index = buildSettingsSearchIndex(en, tEn); + expect(subResultsForQuery(index, 'contacts', 'contacts app')).toEqual([ + { + label: 'Sync to this device', + description: 'Show your address books in the Contacts app and other apps on this device. Changes sync both ways.', + }, + ]); + expect(tabMatchesQuery(index, 'calendar', 'Calendar', 'android')).toBe(true); + }); }); describe('buildSettingsSearchIndex', () => { diff --git a/src/lib/settings-search.ts b/src/lib/settings-search.ts index c8bdb69a..86c0f776 100644 --- a/src/lib/settings-search.ts +++ b/src/lib/settings-search.ts @@ -11,6 +11,8 @@ // through t() for the current locale, with the English fallback. // Kept free of React so it can be unit-tested. +import { supportsDeviceSync } from './platform-capabilities'; + export type SettingsTabId = | 'account' | 'language' | 'notifications' | 'appearance' | 'layout' @@ -166,6 +168,8 @@ export const SETTINGS_SEARCH_PATHS: Record = { 'calendar.settings.show_birthday_calendar', 'calendar.settings.enable_tasks', 'calendar.settings.show_tasks_on_calendar', + // Android only: calendars in the phone's Calendar app (#34). + ...(supportsDeviceSync ? ['calendar.settings.device_sync_title'] : []), ], contacts: [ 'settings.contacts.group_by_letter_label', @@ -173,6 +177,8 @@ export const SETTINGS_SEARCH_PATHS: Record = { 'settings.contacts.import_label', 'settings.contacts.export_label', 'settings.contacts.manage_title', + // Android only: address books in the phone's Contacts app (#34). + ...(supportsDeviceSync ? ['settings.contacts.device_sync_title'] : []), ], files: [ 'files.settings_display', @@ -238,8 +244,8 @@ export const SETTINGS_SEARCH_KEYWORDS: Record = { security: 'password 2fa two-factor totp app password mfa api key certificate encryption', encryption: 's/mime smime certificate sign encrypt', content_senders: 'block sender remote images privacy tracking', - calendar: 'event schedule appointment meeting timezone', - contacts: 'address book contact', + calendar: `event schedule appointment meeting timezone${supportsDeviceSync ? ' android phone device sync' : ''}`, + contacts: `address book contact${supportsDeviceSync ? ' android phone device sync' : ''}`, files: 'attachments cloud drive storage upload', sidebar_apps: 'apps webview iframe', about_data: 'export import backup offline cache debug logs reset version', From 417ed5a326dbef66ee49314bc8e31db200f0e4ee Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:27:13 +0200 Subject: [PATCH 030/334] fix: log why a device sync run failed #34 The sync adapter logged only the SyncResult counters of a run, so a failed sync showed up in logcat as `ioErrors=1` and nothing else. The reason was in the run's report, and JS warnings from a headless run don't reach logcat; finding out that a run failed on "Invalid column raw_contact_is_read_only" took reading the app's storage. The log line now carries the report's outcome, its message (up to 300 characters) and the number of failed items. Checked: 55 JVM tests (`gradlew :app:testDebugUnitTest`, one new) and the debug build. --- .../bulwarkmobile/sync/DeviceSyncAdapter.kt | 2 +- .../bulwarkmobile/sync/SyncReports.kt | 22 +++++++++++++++++++ .../bulwarkmobile/sync/SyncReportsTest.kt | 15 +++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/DeviceSyncAdapter.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/DeviceSyncAdapter.kt index aa4b851d..8df852ae 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/DeviceSyncAdapter.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/DeviceSyncAdapter.kt @@ -91,7 +91,7 @@ class DeviceSyncAdapter(context: Context) : val outcome = SyncReports.outcome(run.report, run.endReason) SyncReports.apply(outcome, syncResult) Log.i(TAG, "Sync ${run.runId} of $authority ended (${run.endReason}) after " + - "${SystemClock.elapsedRealtime() - startedAt} ms: $outcome") + "${SystemClock.elapsedRealtime() - startedAt} ms: ${SyncReports.summary(run.report)}; $outcome") } catch (e: InterruptedException) { // onSyncCanceled interrupts this thread. JS sees the run as // cancelled at its next checkpoint (isRunCancelled). diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/SyncReports.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/SyncReports.kt index 280523ef..37e6dc38 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/SyncReports.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/SyncReports.kt @@ -74,6 +74,28 @@ object SyncReports { } } + /** + * One line for logcat: the report's outcome, its message and how many + * items failed. The reason a run failed is otherwise only in the report, + * and JS warnings from a headless run don't reach logcat. + */ + fun summary(reportJson: String?): String { + if (reportJson == null) return "no report" + val report = try { + JSONObject(reportJson) + } catch (e: JSONException) { + return "unreadable report" + } + val parts = mutableListOf("outcome=" + report.optString("outcome", "?")) + val items = report.optJSONArray("itemErrors")?.length() ?: 0 + if (items > 0) parts += "itemErrors=$items" + val message = report.optString("message", "") + if (message.isNotEmpty()) parts += "message=" + message.take(MAX_LOGGED_MESSAGE) + return parts.joinToString(" ") + } + + private const val MAX_LOGGED_MESSAGE = 300 + fun apply(outcome: SyncOutcome, result: SyncResult) { result.stats.numIoExceptions += outcome.ioErrors result.stats.numAuthExceptions += outcome.authErrors diff --git a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/SyncReportsTest.kt b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/SyncReportsTest.kt index 273e53b8..3594a338 100644 --- a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/SyncReportsTest.kt +++ b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/SyncReportsTest.kt @@ -81,4 +81,19 @@ class SyncReportsTest { fun `an outcome this build does not know is retried`() { assertEquals(1L, SyncReports.outcome(report("somethingNew"), "reported").ioErrors) } + + @Test + fun `the log summary names the outcome, failed items and the message`() { + assertEquals("outcome=ok", SyncReports.summary(report("ok"))) + assertEquals( + "outcome=internal message=Invalid column x", + SyncReports.summary(report("internal", ",\"message\":\"Invalid column x\"")), + ) + val withItems = report("ok").replace("\"itemErrors\":[]", "\"itemErrors\":[{\"ref\":\"a\"},{\"ref\":\"b\"}]") + assertEquals("outcome=ok itemErrors=2", SyncReports.summary(withItems)) + assertEquals("no report", SyncReports.summary(null)) + assertEquals("unreadable report", SyncReports.summary("{")) + val long = SyncReports.summary(report("io", ",\"message\":\"" + "m".repeat(400) + "\"")) + assertEquals("outcome=io message=".length + 300, long.length) + } } From d135bb24492f2e93894f9a02aaf07f68b7f53960 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 05:41:26 +0200 Subject: [PATCH 031/334] docs: describe device sync as it was built #34 The design in f666699 was written before the code, and building it changed a number of details, several of them after runs against the real providers on the emulators. The doc now says what the code does: - a sync run's steps in the order they run, the stale list, the reconcile cursor, the owner check, the time margins, and accounts that are left out when they refuse to list their collections; - uploads in phases across accounts, the `ifInState` catch-up, account refusals, series splits and copied events, creates adopting only in their target collection, and poison markers; - the contacts and calendar mappings as built (membership rows, text limits, postal and relation rules, read-only rows and calendars, exceptions, reminders under the old owner), and the columns the providers refuse to return; - the native module's scoping, the app's store, lifecycle and triggers, and the failure matrix; - decisions and limitations, with the questions still open. Checked: every statement against the code of this branch. --- docs/device-sync.md | 636 ++++++++++++++++++++++++++++++-------------- 1 file changed, 437 insertions(+), 199 deletions(-) diff --git a/docs/device-sync.md b/docs/device-sync.md index 16a0e106..ef5ad0dc 100644 --- a/docs/device-sync.md +++ b/docs/device-sync.md @@ -60,8 +60,9 @@ Why this split: second owner outside JS's de-duplication could evict the account. - Mappers and merge rules are pure TypeScript with no React Native import, so vitest covers them with golden fixtures and property tests. A test walks the - import graph of `src/device-sync/{contacts,calendar,engine}` and fails on - `react-native`, expo, stores or `jmap-client`: the global mocks in + import graph of every module in `src/device-sync` except the React Native + boundary (`native.ts`, `task.ts`, `jmap/client-port.ts` and `app/`) and + fails on `react-native`, expo, stores or `jmap-client`: the global mocks in `src/test-setup.ts` would otherwise hide such an import. - Kotlin only translates: URIs, `ContentProviderOperation`s, `SyncResult`. Its logic has JVM unit tests (`android/app/src/test`). @@ -87,15 +88,17 @@ mutex coordinates runs with app-side operations. | `android/app/src/main/res/xml/{authenticator,sync_contacts,sync_calendar,contacts}.xml` | framework metadata | | `android/app/src/debug/java/…/sync/DeviceSyncDebugReceiver.kt` | adb hooks for device tests (debug builds only) | | `src/device-sync/types.ts`, `android-columns.ts` | contracts: bridge API, row model, ops, payload, report; provider column names | +| `src/device-sync/planner.ts`, `wire.ts` | the contract between the engine and the two planners; JMAP objects as Stalwart sends them | | `src/device-sync/native.ts` | typed wrapper around `NativeModules.BulwarkDeviceSync` | -| `src/device-sync/common/` | pure helpers: canonical JSON, hashing, JSON Pointer patches, zoned time, durations | +| `src/device-sync/common/` | pure helpers: identities and minted keys, canonical JSON, hashing, JSON Pointer patches | | `src/device-sync/contacts/` | JSContact ↔ contacts rows, row matching, contact merge rules (pure) | -| `src/device-sync/calendar/` | JSCalendar ↔ calendar rows, RRULE, exceptions, merge rules (pure) | -| `src/device-sync/jmap/` | the JMAP side on a detached client: `/changes`, full reconcile, `/set`, uid lookup | -| `src/device-sync/engine/` | orchestration: run entry, mutex, checkpoints, batches, SyncState, report | -| `src/device-sync/task.ts` | the headless task entry (registered in `index.ts`) | +| `src/device-sync/calendar/` | JSCalendar ↔ calendar rows, RRULE, exceptions, zoned time, durations, merge rules (pure) | +| `src/device-sync/jmap/` | the JMAP side on a detached client: session accounts, `/get`, `/changes`, `/query` paging, `/set`, uid lookup, the auth rebuild | +| `src/device-sync/engine/` | orchestration: run entry, mutex, checkpoints, batches, SyncState, poison markers, default selection, teardown, report | +| `src/device-sync/task.ts` | the headless task entry (registered in `index.ts`) and the teardown the app calls | +| `src/device-sync/app/` | the app side: turning sync on and off, sign-out, reconciling Android's accounts, triggers, the chooser's collection list, the reminder filter | | `src/stores/device-sync-store.ts` | per-account preferences and the last run status | -| `src/components/settings/ContactsSettings.tsx`, `CalendarSettings.tsx` | the "Sync to this device" sections | +| `src/components/settings/device-sync/` | the "Sync to this device" sections, rendered by `ContactsSettings.tsx` and `CalendarSettings.tsx` | ## The Android shell @@ -236,24 +239,26 @@ marker: raw contacts of a sync account are always soft-deleted. A **new** local item is a raw contact without `SOURCE_ID`, or a master event (`ORIGINAL_ID` and `ORIGINAL_SYNC_ID` both null) whose `_SYNC_ID` is null or -pending. DIRTY is not the test: a raw contact inserted without data rows is not -dirty. A row with `ORIGINAL_*` set is always an exception of its master and -never uploads as an event of its own. +pending, or the newer of two masters sharing a `_SYNC_ID` (a +`CONTENT_EXCEPTION_URI` split, see [Recurrence](#recurrence)). DIRTY is not +the test: a raw contact inserted without data rows is not dirty. A row with +`ORIGINAL_*` set is always an exception of its master and never uploads as an +event of its own. ### Contacts columns | Column | Holds | |---|---| | `RawContacts.SOURCE_ID` | identity (above) | -| `RawContacts.SYNC1` | the card's collections: `/` joined by `,` | -| `RawContacts.SYNC2` | the shadow: the last server card, wire format, with each `data:` photo URI replaced by `sha256:` of its bytes | +| `RawContacts.SYNC1` | the card's selected address books: `/` joined by `,` | +| `RawContacts.SYNC2` | the shadow: the last server card, wire format, with each `data:` photo URI replaced by `sha256:` of its bytes, plus a `~memberOf` member: the group cards whose memberships the rows held after our last write, so a removal on the device and an addition on the server can be told apart (stripped before any projection or patch) | | `RawContacts.SYNC3` | the pending create: `{"uid":…,"target":"/"}`, written before the `/set` and kept until the identity is | | `RawContacts.SYNC4` | poison marker `{"fp":…,"type":…,"n":…,"until":…}` | -| `RawContacts.RAW_CONTACT_IS_READ_ONLY` | 1 when none of the card's address books grants `mayWrite` | +| `RawContacts.RAW_CONTACT_IS_READ_ONLY` | 1 when none of the card's address books grants `mayWrite`. Only written, and only along with other raw contact writes, so an echo stays write-free: ContactsProvider refuses the column in every projection and selection ("Invalid column", API 32 and 35). Whether device edits are put back is decided from the server's current rights on the stored card | | `Data.DATA_SYNC1` | the entry key(s), e.g. `emails:work1`; see [units](#units) | | `Data.DATA_SYNC2` | photo rows: the hash of the server photo applied to the row | | `Data.DATA_SYNC3` | the baseline: the row's mapped columns as the provider stored them after our last write (JSON; text over 1 KB as its hash) | -| `Groups.SOURCE_ID`, `SYNC2`, `SYNC3`, `SYNC4` | as for raw contacts, for group cards (`SHOULD_SYNC` is never written: an update carrying it makes ContactsProvider request a sync) | +| `Groups.SOURCE_ID`, `SYNC2`, `SYNC3`, `SYNC4` | as for raw contacts, for group cards; `GROUP_IS_READ_ONLY` is written like `RAW_CONTACT_IS_READ_ONLY`, for groups in read-only books (`SHOULD_SYNC` is never written: an update carrying it makes ContactsProvider request a sync) | | `Settings` | one row per account: `UNGROUPED_VISIBLE=1`, `SHOULD_SYNC=1` (the default 0 hides every contact without a visible group) | Writing `DIRTY=0`, `SOURCE_ID` or `SYNC1-4` does not bump @@ -269,7 +274,7 @@ VERSION without DIRTY; the assert then fails once and the row is re-read.) | `Calendars._SYNC_ID` | identity | | `Calendars.CAL_SYNC1` | unused: CalendarProvider sends it as the `feed` extra of the sync it requests whenever a calendar with `SYNC_EVENTS=1` is inserted or changed, so anything there splits those requests apart | | `Calendars.CAL_SYNC2` | the shadow: the last server `Calendar` object | -| `Calendars.CAL_SYNC3` | flags JSON: `readOnly` reason (`rights`, `subscription`), `taskOnly` | +| `Calendars.CAL_SYNC3` | flags JSON: the `readOnly` reason (`rights`, `subscription`) | | `Events._SYNC_ID` | identity | | `Events.SYNC_DATA1` | masters and single events: the shadow, the full server event with its `recurrenceOverrides` | | `Events.SYNC_DATA2` | exceptions: the recurrence id, exactly as the server's override key | @@ -282,9 +287,9 @@ Attendees and reminders have no sync columns, which is why their baseline sits on the event row. `Events.MUTATORS` (the packages that changed the event since our last write) tells which app made a device edit. -A description longer than 64 KB is written truncated, marked in the baseline, -and never uploaded: an event row carries the description, the shadow and the -baseline, and one Binder transaction holds 1 MB. +A description longer than 64 KB of UTF-8 is written truncated, marked in the +baseline, and never uploaded: an event row carries the description, the +shadow and the baseline, and one Binder transaction holds 1 MB. ### SyncState @@ -308,26 +313,34 @@ JSON, versioned. } }, "deviceZone": "Europe/Berlin", - "deviceZonePending": null + "deviceZonePending": null, + "reminderOwner": "device" } ``` - `owner`: the registry account and the server origin the rows were written - for. A run for another owner stops with `internal` and the app offers to - start over (after uploading what it can). + for. A run for another owner stops with `internal`, and a teardown uploads + nothing for it; turning sync off and on again starts over. - `itemsState` is the `ContactCard` or `CalendarEvent` state the rows describe. It is written in the same `applyBatch` as the last item group of a `/changes` page: the `syncState` op is the last op of that batch, is never `yieldAllowed`, and the native side refuses a batch that breaks either rule, so a provider yield can never commit the state ahead of its rows. - No stored `itemsState` is newer than the shadow of an object changed before - it: an object whose rows could not be written (an item error) goes on - `stale` in the same batch, is fetched again at the start of every run, and - uploads nothing until it has been. + it: an object whose rows could not be written (a planner error, a provider + refusal that re-planning did not fix) goes on `stale` in the same batch, is + fetched again first in every run, and uploads nothing until it has been. + The state still advances, so one bad object never blocks the pages after + it. - `reconcile` marks a full reconcile in progress: `{ "from": , "phase": "ids" | "objects", "position": n }`, written with every - chunk. The next run continues it; its deletions are applied only when the - complete id list is known, and `itemsState` becomes `from` when it ends. + first>, "phase": "ids" | "objects", "position": n, "after": }`, + written with every chunk of objects. `after` is the last id processed in + the reconcile's sorted order: the next run lists the ids again and + continues after it (`phase` and `position` are informative only). `from: + null` asks for a reconcile that has not started (after + `cannotCalculateChanges`, or when local deletions were discarded). Its + deletions are applied only when the complete id list is known, and + `itemsState` becomes `from` when it ends. - `selected` is the selection the rows were written for; a collection is added in the batch with its last loaded chunk, and removed with its last deleted row. @@ -337,44 +350,52 @@ JSON, versioned. - `deviceZone` (calendar) is the zone floating events were written in; `deviceZonePending` is set while a zone-change pass runs and cleared, with `deviceZone` updated, in the batch of its last chunk. -- The providers delete the row with the account. +- `reminderOwner` (calendar) is who the Reminders rows were written for + (`device` or `bulwark`), so a run notices a changed choice; see + [Reminder owner](#reminder-owner). +- The providers delete the row with the account. A teardown clears it by + writing `''`. ## A sync run One run handles one Android account and one authority. A module-level mutex per (registry account, authority) serialises it with other runs and with app-side -operations such as turning sync off. +operations such as turning sync off; a run that cannot get it before its +deadline reports `cancelled` with `moreRecordsToGet`. -1. **Preflight.** - - Permissions for the authority (a revoked one → `permission`). - - The Android account and that the authority is still enabled +1. **Preflight**, in this order: + - The authority is still enabled for the Android account (`getSyncAutomatically`) → otherwise `disabled`. - - The SyncState's `owner` matches this registry account and server origin - (see [Identity](#identity)). + - The SyncState is read; without the runtime permission this first + provider read fails with `permission`. A blob that cannot be understood + (another version, garbage) counts as none: every JMAP account starts over + with a full reconcile. Its `owner` must be this registry account (see + [Identity](#identity)) → otherwise `internal`. + - An upload-only sync (`extras.upload`) with nothing dirty, deleted or new + for our account ends here with `ok`, before JMAP is opened: + ContactsProvider and CalendarProvider schedule an upload sync for every + account 30 s after any app write, other accounts' included. A pending + reconcile or objects on `stale` make it a full run. - `new JMAPClient().loadAccount(registryId)`: `false` or `AuthenticationError` → rebuild once (another client may have rotated the refresh token); still failing → `auth`, plus one notification that - deep-links to sign-in. Nothing local is touched. + deep-links to sign-in. Nothing local is touched. The SyncState's `owner` + must also name this server's origin → otherwise `internal`. - The capability on at least one JMAP account → else `unsupported`. - - An upload-only sync (`extras.upload`) with nothing dirty, deleted or new - for our account ends here with `ok`: ContactsProvider and CalendarProvider - schedule an upload sync for every account 30 s after any app write, other - accounts' included. -2. **Local checks.** Calendar: two masters sharing a `_SYNC_ID` (a - `CONTENT_EXCEPTION_URI` split, which also caps the old master's rule - without DIRTY) are resolved before any merge or baseline heal: the newer row - becomes a new event and the older master's rule is uploaded as it stands. - Objects on `stale` are fetched again. -3. **Collections.** `AddressBook/get` or `Calendar/get` (explicit properties) +2. **Collections.** `AddressBook/get` or `Calendar/get` (explicit properties) for the primary JMAP account of the capability (`primaryAccounts[capability]`) and every shared account that advertises it; calendar rows are inserted and - updated to match. Newly selected collections are loaded after step 4 has + updated to match. An account that refuses to list its collections + (`forbidden`, `accountNotFound`, `accountNotSupportedByMethod`) is left out + of the run: nothing downloads or uploads for it, its rows stay on the + device untouched, and it is only logged, without an item error (Stalwart + lists an account shared for mail only with every capability, and a + refusal can pass). Newly selected collections are loaded after step 3 has brought the account up to date, restricted to them, so the stored state never skips changes of the other collections. Deselected collections are - dropped after step 5, and only their clean rows go: dirty items there are - uploaded first (they are still valid objects on the server), and an item - whose upload failed keeps its rows and is reported. -4. **Download.** + dropped in step 5. +3. **Download.** + - Objects on `stale` are fetched again first. - With a stored `itemsState`: `/changes` with `maxChanges: 256` and a `hasMoreChanges` loop; each page's changed ids are fetched with explicit `properties` in batches of `maxObjectsInGet`. @@ -382,37 +403,63 @@ operations such as turning sync off. checkpointed in `reconcile` (see [SyncState](#syncstate)). The state is taken first (`/get` with `ids: []`), then every selected collection's ids (`/query`, `inAddressBook`/`inCalendar`, `limit` 5000 with `position` - paging), then the objects in batches. Rows without an identity or with a - pending one (and exceptions of pending masters) are never reconcile - deletions. A reconcile that finds zero remote objects while the device - holds more than 10 rows for that JMAP account stops with `safetyAbort` - instead of wiping the device. + paging; pages overlap by up to 100 ids, so a deletion between two pages + skips nothing), then the objects in batches. Rows without an identity or + with a pending one (and exceptions of pending masters) are never + reconcile deletions. A reconcile that finds zero remote objects while the + device holds more than 10 rows for that JMAP account stops with + `safetyAbort` instead of wiping the device. + - Contacts: a page's group cards are written before its contacts. When a + group card's `members` changed, its old and new member contacts are + fetched in the same page, before the page's state op: their membership + rows change although their own cards did not. - An object without rows first looks for a local item it may already be: - a new row of the same JMAP account whose pending uid is the object's - `uid` (our own create whose identity never got written) is adopted: it - takes the identity and shadow and is merged as a dirty item. + a new row whose pending create targets a collection the object is in and + whose pending uid is the object's `uid` (our own create whose identity + never got written) is adopted: it takes the identity and shadow and is + merged as a dirty item. - Each object is [merged](#merge-rules) into its rows. An object that left every selected collection loses its rows only when they are clean; dirty units are uploaded first. + - An object whose rows could not be written (a planner error, a provider + refusal that re-planning did not fix) goes on `stale`, and the page's + state still advances. - Rows are written in chunks; the new state goes into the last chunk of each page, so every page is a checkpoint. -5. **Upload**, always after download so merges see the latest server truth; see +4. **Upload**, always after download so merges see the latest server truth; see [Uploads](#uploads). +5. **After the upload.** Deselected collections are dropped, and only their + clean rows go: dirty items there were uploaded first (they are still valid + objects on the server), and an item whose upload failed keeps its rows, + and its collection stays in `selected`. Rows of objects that left every + selected collection go the same way. Calendar: then the device-zone pass + and the reminder-owner pass rewrite clean events (see [Timing](#timing) + and [Reminder owner](#reminder-owner)). 6. **Report** counts, conflicts, per-item errors and duration; persist the status for the settings UI; call `finishRun` before the task's promise settles (JS timers stop once the task is over). `/get` always lists `properties` explicitly: with `properties: null` Stalwart leaves out JMAP-only fields such as `useDefaultAlerts`, and `ids: null` returns -at most 500 objects without saying so. +at most 500 objects without saying so (only collections, of which Stalwart +allows 250 per account, are read with `ids: null`). Checkpoints sit between chunks and phases. At each one the run checks -`isRunCancelled`, its deadline and the time since the last JMAP request. When -the deadline is near it stops cleanly and reports `moreRecordsToGet`, which -the adapter turns into `fullSyncRequested` (SyncManager ignores -`moreRecordsToGet` itself). The step from a create's `/set` to its identity -write is not interruptible, and a create batch starts only while at least two -request timeouts (60 s) of budget remain. +`isRunCancelled` (and whether a teardown asked it to stop), its deadline and +the time since the last JMAP request. Less than 30 s before the deadline it +stops cleanly and reports `moreRecordsToGet`, which the adapter turns into +`fullSyncRequested` (SyncManager ignores `moreRecordsToGet` itself). The step +from a create's `/set` to its identity write is not interruptible, and a +create batch starts only while at least two request timeouts (60 s) of budget +remain before the deadline (`deadlineMarginMs` and `createBudgetMs` in +`DEFAULT_TUNING`, `engine/deps.ts`). An expedited sync gets 2.5 minutes, of +which the adapter keeps 50 s in reserve; when it first has to start React +Native, little of the rest is left in which a create batch may start. + +Failures stay contained. A planner that throws on one item affects only that +item: a download puts the object on `stale`, an upload skips the item and +reports it as `plannerError`. An account-level refusal of a whole `/set` holds +back only that account (see [Uploads](#uploads)). ### Invariants @@ -441,8 +488,8 @@ Every reviewer checks against these. written in the same `applyBatch` as the last chunk of rows it describes, as that batch's last op. Creates carry a client-minted `uid`, written to the row with the create's target before the `/set`; a download adopts a pending - row by uid, and an upload looks the uid up and adopts what it finds, so a - retry never duplicates on either side. + row by uid, and an upload looks the uid up and adopts what it finds in the + create's target collection, so a retry never duplicates on either side. 4. **No feedback loops.** Adapter writes use `CALLER_IS_SYNCADAPTER`, which neither sets DIRTY nor schedules an upload sync, and never touch the columns that make a provider request a sync (`CAL_SYNC1`, `Groups.SHOULD_SYNC`). @@ -488,9 +535,9 @@ exceptions: - the read-back also reads DIRTY (and VERSION): a row that turned dirty in the meantime keeps its old baseline, so the user's edit still counts; -- `timing` and `rule` of a recurring master are never healed: a - `CONTENT_EXCEPTION_URI` split changes them without DIRTY (see the - [local checks](#a-sync-run)). +- `timing` and `rule` of a recurring master are never healed, and the rows of + a split are not healed at all: a `CONTENT_EXCEPTION_URI` split changes them + without DIRTY (see [Recurrence](#recurrence)). ### Units @@ -525,9 +572,14 @@ per data kind: base36 characters (Stalwart parses numeric keys as array indexes and generates `k` itself, so neither is used). 5. Shadow entries still without a row count as deleted on the device **only - when the kind still has rows that carry keys** (an in-place editor removed - one). When every row of the kind is keyless, an editor rewrote them all, and - a missing entry may just be one it could not show: it stays on the server. + when the kind still has rows matched by their key** (an in-place editor + removed one), or when the kind has no row left at all and some other kind + that re-inserting editors rewrite still has rows matched by their key (the + editor worked in place: AOSP deleting a contact's only email, note or + birthday). When every row of the kind is keyless, an editor rewrote them + all, and a missing entry may just be one it could not show: it stays on the + server. A missing name row is never a deletion; a missing photo row always + is (no editor re-inserts photo rows). A row matched by its key is compared column by column with its baseline; an emptied column is a real deletion. A row matched by value has no baseline: its @@ -536,6 +588,15 @@ and LABEL only for phones, emails, addresses and events (editors that re-insert rows write a constant type for websites), and never `IS_PRIMARY`; an empty column never clears anything. +A merge into a dirty contact never gives keys to a kind whose rows are all +keyless: a keyed row there would later make the entries the editor could not +show look deleted. Every write that leaves a contact clean (a download into a +clean contact, an accepted upload, an upload with nothing to send, a revert) +makes its rows match the card: rows are rewritten, re-inserted or deleted, not +just given new baselines. Otherwise an entry Fossify could not show (a second +nickname) would read as deleted at the next in-place edit; it also puts back a +department Fossify dropped. + The name: `""` and NULL are the same. `name/full` uploads when DISPLAY_NAME changed against the baseline and is not simply the provider's join of the current components (a typed display name); when only components changed, @@ -600,17 +661,36 @@ times); after that the item goes on `stale`. ## Uploads -Per JMAP account, in this order: pairs (below), creates, updates, deletions, -then group memberships; in `/set` calls of at most `maxObjectsInSet` objects -(creates: 50) and about 1 MB of JSON (Stalwart splits bigger calls into -several commits, which can apply partially), one request in flight, and one -`sendSchedulingMessages` value per call (it is a request-level flag). +The upload runs phase by phase across the JMAP accounts: new rows are claimed +(below), each account resolves its series splits and pairs, then every +account's creates go up, then every account's updates, then every account's +deletions, then (contacts) groups deleted by absence and group memberships. So +a move between accounts never destroys before it created. Changes go in `/set` +calls of at most `maxObjectsInSet` objects (creates: 50) and about 1 MB of +JSON (Stalwart splits bigger calls into several commits, which can apply +partially), one request in flight, and one `sendSchedulingMessages` value per +call (it is a request-level flag). - **ifInState.** Every `/set` carries `ifInState` = the item state the merge was based on (then the `newState` of our previous `/set`). A `stateMismatch` - means the server changed since the download: the run downloads again and - retries the upload, at most three times, then reports `io` without progress - (SyncManager would otherwise retry at once, without back-off). + (or Stalwart's `serverUnavailable`, a concurrent write that failed the whole + call) means the server changed since the download: the run downloads that + account again and repeats the phase. When `/changes` from the stored state + then shows nothing new, the next `/set` uses the type's current state + instead: Stalwart can name one point of its history two ways (a `/changes` + state `s…` against the type's state `n` before the first item change), and + `ifInState` compares them as text. After three retries the run reports `io` + without progress (SyncManager would otherwise retry at once, without + back-off). +- **Account refusals.** A method error on a whole `/set` that concerns the + account (`accountReadOnly`, `forbidden`, `accountNotFound`, + `accountNotSupportedByMethod`) holds back only that account: its items wait, + the report lists the account, and the other accounts go on. +- **Splits.** A `CONTENT_EXCEPTION_URI` split (two masters sharing a + `_SYNC_ID`, see [Recurrence](#recurrence)) is resolved first: the older + master's capped rule is planned while the split is still visible, the newer + row is claimed as a new event, then the rule is sent. The new event is + created with the other creates. - **Pairs.** Some apps turn an edit into a delete plus an insert: Etar moves an event to another calendar that way, and turns a recurring series into a single event (or edits "this and following" from its first instance) the @@ -618,25 +698,32 @@ several commits, which can apply partially), one request in flight, and one match apart from the calendar (or the rule) are uploaded as a patch of the existing object (`calendarIds` swapped; `recurrenceRule: null` with its overrides removed); the new row takes the identity and exceptions of the old - one, which is purged. Across JMAP accounts a move is a create in the target - account first, identity written, then a destroy in the source (the engine - records its own destroys so the next download treats them as echoes). - Unpaired rows are ordinary creates and deletions. + one, which is purged. Rows of different JMAP accounts never pair: that move + is a create in the target account, identity written, and later in the run a + destroy in the source, whose echo finds no rows left to change. Unpaired + rows are ordinary creates and deletions. - **New rows** (see [Identity](#identity) for what counts as new): 1. Claim: write the pending create (`SYNC3` / `SYNC_DATA3`: the uid and the target collection; for events also `UID_2445` and `_SYNC_ID = ~pending/`). A row keeps a uid it already carries unless another row, synced or pending, uses the same uid (a - `CONTENT_EXCEPTION_URI` split clones `UID_2445`); a server object with - that uid does not count, because it may be this row's own earlier create. - The target is fixed at the claim: contacts go to the address book chosen - in the settings if it is selected and writable, else the first selected - writable book of the personal account (none → the item is poisoned with - `noWritableAddressBook`); events go to the calendar of their row. + `CONTENT_EXCEPTION_URI` split clones `UID_2445`, an app's "duplicate" + copies it): then it gets a fresh one, and the engine loads every master + that shares a new master's `UID_2445` so the planner sees the collision. + A server object with that uid does not count, because it may be this + row's own earlier create. The target is fixed at the claim: contacts go + to the address book chosen in the settings if it is selected and + writable, else to the personal account's default book (or its first by + sort order) among the selected writable ones; with none, the item is + skipped and reported as `noWritableAddressBook` in every run. Events go + to the calendar of their row. 2. Look the uid up: contacts in the target address book (card uids are unique per book), events in the whole account (event uids are unique per - account). A hit is adopted: the row takes its identity and is merged as a - dirty item. + account). A hit in the create's target collection is adopted: the row + takes its identity and is merged as a dirty item. A hit elsewhere (an + event with the same uid in another calendar) is not this row's create, + and adopting it would overwrite an unrelated event: the item is poisoned + as `uidConflict`. 3. Otherwise `create`. 4. `invalidProperties` on `uid` means the object exists: Stalwart's duplicate check is synchronous but the `uid` query reads an asynchronous @@ -648,8 +735,8 @@ several commits, which can apply partially), one request in flight, and one create batch starts only with at least two request timeouts (60 s) of budget left. - **Exception rows** (`ORIGINAL_*` set) upload only as - `recurrenceOverrides/` of their master, after the master's create when - the master is new. + `recurrenceOverrides/` of their master; those of a new master go up + inside its create. - **Dirty rows**: the patch from the changed units. An empty patch clears DIRTY without a request. - **Deleted rows** (`DELETED=1`): an object that belongs to several @@ -662,25 +749,39 @@ several commits, which can apply partially), one request in flight, and one - **Afterwards**: a `/get` of every touched id (a separate request for creates: Stalwart does not resolve result references to `/set`). The result becomes the new shadow, and the rows' baselines are refreshed from it, in one group - per item that first asserts the VERSION/projection read before the upload - and clears DIRTY. If the assert fails (edited again meanwhile), the shadow and - identity are still written, the baselines of the uploaded units are set to - the values that were uploaded, and DIRTY stays: the newer edit uploads next - time, and so does a revert to the old value. + per item that first asserts the VERSION/projection read before the upload, + clears DIRTY and clears a poison marker the item carried. If the assert + fails (edited again meanwhile), the shadow and identity are still written, + the baselines of the uploaded units are set to the values that were + uploaded, and DIRTY stays: the newer edit uploads next time, and so does a + revert to the old value. For contacts these baseline writes address their + data rows with `where _id = ?`, so a row deleted meanwhile does not fail the + group. - **SetErrors**: - - `notFound` on update → treated as a remote delete (the rows go); + - `notFound` on update → treated as a remote delete (the rows go); on a + destroy it counts as done; on a create the item is poisoned; - `forbidden` in a call that asked for scheduling messages → the item is retried once without them and the report notes "invitations not sent" (Stalwart refuses scheduling when it is disabled, when the account has no - calendar address, or without the scheduling permission); `forbidden` - otherwise → the rows are rewritten from the shadow (read-only on the server - after all); + calendar address, or without the scheduling permission). `forbidden` + otherwise: on a create the item is poisoned (a revert would delete the + user's new item); on an update or a deletion the object is read-only on + the server after all, and its rows are rewritten from the server's + current version, fetched for it (the shadow holds photos only as hashes), + or removed when the object is gone or outside the selection; - `stateMismatch` → as above; - `invalidProperties` on `uid` → as above; - `invalidProperties`, `invalidPatch`, `tooLarge`, `overQuota` and the rest → - the item is poisoned: its marker records a fingerprint of the patch and a - back-off (1 h, doubling to 1 day). It is not retried until the back-off - ends or the row changes, and the report lists it. + the item is poisoned. +- **Poison markers.** The engine writes the marker (`SYNC4` / `SYNC_DATA5`, + `{"fp":…,"type":…,"n":…,"until":…}`): `fp` is a fingerprint of what the + item uploads from (its flags, shadow and mapped rows; for events also the + attendees, reminders and exception rows), `until` the end of a back-off of + 1 h that doubles up to 24 h while the item stays the same. The engine checks + the marker before `planUpload`: until the back-off ends or the item + changes, the item is skipped and the report lists it. The marker is cleared + once an upload of the item is accepted. The planners check the same marker + with the same fingerprint content, a duplicate of the engine's check. ### Deletion threshold @@ -688,16 +789,22 @@ More than 50 local deletions of an authority in one run, and more than 20 % of that authority's synced objects, set `tooManyDeletions` unless the run carries `overrideTooManyDeletions`. Deletions inferred from absence (groups) count. No deletion of that run is uploaded; everything else proceeds. With -`discardLocalDeletions`, the deleted rows are purged and, in the same batch, the -affected JMAP accounts get a `reconcile` marker, so the next runs download the -objects again whatever their extras. +`discardLocalDeletions` (the user chose "undo" in Android's too-many-deletions +notification), the affected JMAP accounts first get a `reconcile` marker in a +batch of its own, then the deleted rows are purged, so the next runs download +the objects again whatever their extras; a crash in between only asks the +question again. A teardown applies the same threshold: deletions above it stay +on the device and count as waiting changes, so the app asks before turning +sync off (see [Lifecycle](#lifecycle)). ## Contacts mapping JSContact (RFC 9553) as Stalwart stores it, to ContactsContract. Anything not listed is preserved through patching. Data rows are written with both the display name and the components (the provider recomputes whichever is missing), -and every entry row stores its key(s) in `DATA_SYNC1`. +and every entry row stores its key(s) in `DATA_SYNC1`. Text is cut to what +ContactsProvider keeps (10 KiB; phone numbers 1000 characters), and a column +that was cut never uploads. ### Name (StructuredName, one row, key `name`) @@ -717,7 +824,9 @@ component (separators, repeats, unknown kinds) in place, and sends the whole `name/components` array (JSON Pointer can't address array elements). When `name/full` goes along is in [row matching](#row-matching-contacts): a typed display name uploads; a recomputed one re-derives `full` only when the server's -was derived too. +was derived too. A name the server holds without components uploads whole: +the components from the row and `full` = the display name, so retyping "Ada" +as "Ada King" on the device sends both. ### Entries @@ -726,15 +835,15 @@ was derived too. | `nicknames/` | Nickname | `NAME`, `TYPE=DEFAULT` | | `emails/` | Email | `ADDRESS`; `TYPE` from contexts: `private`→HOME, `work`→WORK, none→OTHER, `label`→CUSTOM+`LABEL`; `pref=1`→`IS_PRIMARY` | | `phones/` | Phone | `NUMBER`; `TYPE` from features + contexts (table below); `label`→CUSTOM+`LABEL`; `pref=1`→`IS_PRIMARY` | -| `addresses/` | StructuredPostal | components → `STREET`, `POBOX`, `NEIGHBORHOOD`, `CITY`, `REGION`, `POSTCODE`, `COUNTRY` (table below), `full`→`FORMATTED_ADDRESS`; contexts → TYPE HOME/WORK/OTHER, `label`→CUSTOM | +| `addresses/` | StructuredPostal | components → `STREET`, `POBOX`, `NEIGHBORHOOD`, `CITY`, `REGION`, `POSTCODE`, `COUNTRY` (table below), `full`→`FORMATTED_ADDRESS` (a card without `full` gets a one-line address joined from the components, so the provider leaves the row as written); contexts → TYPE HOME/WORK/OTHER, `label`→CUSTOM | | `organizations/` + `titles` pointing at it | Organization | `name`→`COMPANY`, `units` joined with `, `→`DEPARTMENT`, title with `kind: title`→`TITLE`, title with `kind: role`→`JOB_DESCRIPTION`; key `organizations:|titles:,`; titles without an organization get their own row | | `links/` | Website | `URL` ← `uri`; contexts `private`→HOME, `work`→WORK, else OTHER; `label`→CUSTOM | -| `anniversaries/` | Event | `START_DATE`: `YYYY-MM-DD`, `--MM-DD` without year (`partialDateToString`); kind `birth`→BIRTHDAY, `wedding`→ANNIVERSARY, `other`→OTHER, anything else → CUSTOM + its kind as `LABEL` | +| `anniversaries/` | Event | `START_DATE`: `YYYY-MM-DD`, `--MM-DD` without year (`partialDateToString`); kind `birth`→BIRTHDAY, `wedding`→ANNIVERSARY, `other`→OTHER, anything else → CUSTOM + its kind as `LABEL`; a custom type uploads its label, lowercased, as the kind | | `relatedTo/` | Relation | `NAME` ← the key (a `urn:uuid:` of a synced card shows that card's name), `TYPE` from the relation set (table below) | | first entry of `notes` | Note | `NOTE` ← `note` | | first `media` entry with `kind: "photo"` | Photo | full-size bytes into `PHOTO`; the provider scales them, stores the display photo and sets `PHOTO_FILE_ID` | | group cards (`kind: "group"`) | Groups | `TITLE` ← `name.full`; `GROUP_VISIBLE=1`; `members` keys are uids of the member cards | -| membership | GroupMembership | one row per group card whose `members` holds this card's `uid` (`GROUP_SOURCE_ID`) | +| membership | GroupMembership | one row per group card whose `members` holds this card's `uid`, written with `GROUP_ROW_ID` (`data1`, the group's row id); `GROUP_SOURCE_ID` is only read | Phone types (first match wins; reverse: the table read right to left, and `TYPE_OTHER` → no features): @@ -768,7 +877,8 @@ built from with one component of the first kind, keeps the rest): | `COUNTRY` | `country` | Legacy flat address fields (`street`, `locality`, …) are read when there are no -components and never written. +components and never written. When an editor wrote only the formatted address +and the provider copied it into `STREET`, only `full` uploads. Relations: `friend`↔FRIEND, `spouse`↔SPOUSE, `child`↔CHILD, `parent`↔PARENT, `sibling`→RELATIVE, `kin`→RELATIVE, `co-worker`/`colleague`→CUSTOM with the @@ -776,7 +886,14 @@ type as label, `agent`→ASSISTANT, `emergency`→CUSTOM. Device types without a RFC 9553 equivalent (BROTHER, SISTER, MOTHER, FATHER, MANAGER, ASSISTANT, DOMESTIC_PARTNER, PARTNER, REFERRED_BY) upload as the nearest RFC type (`sibling`, `parent`, `colleague`, `agent`, `spouse`, `spouse`, `contact`) and are -only sent when the user changed the type. +only sent when the user changed the type. RELATIVE uploads as `kin`; a custom +type uploads its label, lowercased, as the relation type. Only a row matched by +its key can rename a relation (`relatedTo` is keyed by its value, so another +name is another entry): a re-inserted row showing another name may just show a +related card that was renamed since. + +For emails, phones, addresses and websites, changing only the label of a custom +type uploads only `label`; the contexts the entry had stay. Deleting an entry resends its whole map (Stalwart turns `map/key: null` into a valueless vCard line or, for `relatedTo`, ignores it). Only `addresses`, @@ -796,13 +913,26 @@ most 512 px, JPEG, and sent as a `data:image/jpeg;base64,…` URI in `media//uri`, matching the app's picker. `DATA_SYNC2` holds the hash of the server photo last applied, the baseline holds `PHOTO_FILE_ID`; a new file id means the user changed the photo. Photos travel in batches of their own -(Binder budget). +(Binder budget). A photo whose bytes could not be fetched keeps its row and +counts as unchanged. -Groups: created before memberships (a membership naming an unknown -`GROUP_SOURCE_ID` makes the provider create an empty group). A group deleted on -the device is found by `DELETED`, or by its absence from the rows while -`SyncState.groups` still lists it (Fossify hard-deletes groups through a -sync-adapter URI); both count toward the deletion threshold. +Groups: group cards are written before the contacts of the same download, and +membership rows name their group by row id (`GROUP_ROW_ID`, looked up by the +group's `SOURCE_ID`), never by `GROUP_SOURCE_ID`: a membership naming an +unknown `GROUP_SOURCE_ID` makes the provider create an empty group. A +membership whose group has no row is not written. A contact's memberships are +merged from three sets: what the rows held after our last write (`~memberOf` +in the shadow), the rows now, and the group cards that list the card's uid +now; both sides can only flip a membership the same way, so memberships never +conflict. Membership edits upload after the deletions, as patches of the +group cards' `members` (`members/: true` or `null`; the whole map for a +group without members). A contact stays dirty until the server's group cards +show its membership edits (the clean and accepted writes keep DIRTY); the +next run's clean write clears it. + +A group deleted on the device is found by `DELETED`, or by its absence from the +rows while `SyncState.groups` still lists it (Fossify hard-deletes groups +through a sync-adapter URI); both count toward the deletion threshold. ## Calendar mapping @@ -811,12 +941,12 @@ sync-adapter URI); both count toward the deletion threshold. | JMAP `Calendar` | Android `Calendars` | |---|---| | `id` | `_SYNC_ID` = `/` | -| `name` | `NAME`, `CALENDAR_DISPLAY_NAME` (shared accounts append the account's name) | +| `name` | `NAME`, `CALENDAR_DISPLAY_NAME`; a shared account's calendars show ` ()` (the JMAP account's name, unless it is one of the user's addresses), the personal account's get no suffix | | `color` | `CALENDAR_COLOR` (CSS color → ARGB; absent → the app's default palette colour) | | `myRights` | `CALENDAR_ACCESS_LEVEL`: `mayWriteAll` → OWNER (700); `mayWriteOwn` → CONTRIBUTOR (500); `mayRSVP` → RESPOND (300); else READ (200); subscribed iCal feed → READ | | the user's calendar address | `OWNER_ACCOUNT` (never null: Etar crashes on it) | | `timeZone` | `CALENDAR_TIME_ZONE` (device zone when null) | -| — | `SYNC_EVENTS=1` (the default 0 hides every instance), `VISIBLE=1` on insert only (local afterwards), `ALLOWED_REMINDERS="1,2"`, `ALLOWED_AVAILABILITY="0,1"`, `ALLOWED_ATTENDEE_TYPES="0,1,2,3"`, `MAX_REMINDERS=5` (0 when Bulwark owns reminders), `CAN_ORGANIZER_RESPOND=0`, `CAN_PARTIALLY_UPDATE=0` | +| — | `SYNC_EVENTS=1` (the default 0 hides every instance; written again when an app turns it off), `VISIBLE=1` on insert only (local afterwards), `ALLOWED_REMINDERS="1,2"`, `ALLOWED_AVAILABILITY="0,1"`, `ALLOWED_ATTENDEE_TYPES="0,1,2,3"`, `MAX_REMINDERS=5` (0 when Bulwark owns reminders), `CAN_ORGANIZER_RESPOND=0`, `CAN_PARTIALLY_UPDATE=0` | Not synced: the virtual birthday calendar (client-side only), calendars without `mayReadItems`, and task-only calendars: a calendar whose first 50 @@ -826,6 +956,14 @@ reconcile. Deleting a calendar row deletes its events (provider trigger); the engine first uploads the calendar's dirty events. +A read-only calendar (without `mayWriteAll` and `mayWriteOwn`, or a subscribed +feed) takes no device changes: a dirty event is rewritten from its shadow, a +deleted one is purged and fetched again, a new one is removed. The one +exception is the user's own RSVP on the master +(`participants//participationStatus`), which uploads when the calendar +grants `mayRSVP`. Edits of an event Stalwart holds only as an instance (no +series) are reverted too, in any calendar. + ### Timing | JSCalendar | Android | @@ -840,8 +978,9 @@ first uploads the calendar's dirty events. Android's Duration parser takes `[+-]P` then `` (with `M` always minutes, no fractions, no `Y`); all-day durations must be `PD` or the provider throws. Server durations are parsed in full (weeks included, `P1W` -comes from CalDAV DTEND). A duration with a time part on an all-day event is -rounded up to whole days on the device and never uploaded unless changed. +comes from CalDAV DTEND). All-day events are at least one day long on the +device: a duration with a time part is rounded up to whole days, a zero one +becomes one day, and neither is uploaded unless the timing changes. Upload of timing: only when a timing column differs from the baseline. The local start is recomputed in the event's zone (floating events stay floating, @@ -863,15 +1002,16 @@ an upload: the offset is not a whole hour): same instant, same wall time, only the zone name changes; - a recurring event whose start or an override key falls in an overlap is not - uploaded: the item is poisoned with `dstAmbiguous`; -- an `until` in a gap or overlap moves one hour later. + uploaded: the item stays dirty and is reported as `dstAmbiguous`; +- an `until` in a gap or overlap moves one hour later (better than Stalwart + dropping it and the series running forever). ### Recurrence | JSCalendar | Android | |---|---| | `recurrenceRule` | `RRULE`: `FREQ`, `INTERVAL` (>1), `COUNT`, `UNTIL` (UTC `…Z` for timed events, `YYYYMMDD` for all-day), every `BY*` part, `BYSETPOS`, `WKST` (when not Monday) | -| `rscale` other than gregorian, `skip`, leap months (`byMonth` with `L`) | not representable: the rule is written without them, the event's timing and rule are never uploaded, and the item is marked `ruleNotRepresentable` | +| `rscale` other than gregorian, `skip`, leap months (`byMonth` with `L`) | not representable: the rule is written without them, the event's timing and rule are never uploaded, and an item whose only changes are to them is reported as `ruleNotRepresentable` | | `excludedRecurrenceRule` | not supported by Stalwart; an `EXRULE` written by a device app is ignored | | override `{excluded: true}` | `EXDATE`: comma list of UTC `YYYYMMDDTHHMMSSZ`, or `YYYYMMDD` for all-day (no prefix means UTC) | | any other override, including `{}` | an exception row: master ⊕ override (applied as a PatchObject); `ORIGINAL_SYNC_ID`, `ORIGINAL_INSTANCE_TIME` = the key's instant in the master's zone, `ORIGINAL_ALL_DAY`; `SYNC_DATA2` = the key | @@ -893,11 +1033,18 @@ Device-side changes map back as follows: | exception row inserted (Etar "this event": `ORIGINAL_SYNC_ID` + `ORIGINAL_INSTANCE_TIME`, `DIRTY=1`) or edited | `recurrenceOverrides/`; the key is `ORIGINAL_INSTANCE_TIME` in the master's zone (a date at midnight for all-day). A new override carries the master's title, locations, participants and sequence (Stalwart stores a bare override as a separate event otherwise); an existing one is patched one level deep (`recurrenceOverrides//`), never deeper: Stalwart turns a deeper pointer into a partial override (other attendees vanish for that instance) | | exception row with `STATUS_CANCELED` (Etar and Fossify "delete this event"), a deleted exception row, or an EXDATE added to the master | `recurrenceOverrides/: {excluded: true}` | | EXDATE removed | `recurrenceOverrides/: null` | -| master `RRULE` capped with `UNTIL`/`COUNT` + a new event without identity (Etar "this and following") | `recurrenceRule` (whole object) with the new `until`/`count`, overrides after it removed (`recurrenceOverrides/: null`), and the new event created with a fresh uid | +| master `RRULE` capped with `UNTIL`/`COUNT` + a new event without identity (Etar "this and following") | the master uploads only `recurrenceRule` (whole object) with the new `until`/`count` and removes the overrides after the new end (`recurrenceOverrides/: null`; for a `COUNT` the end is the start of the last instance, CalendarProvider's `LAST_DATE` − `DURATION`); the new series is a row of its own, claimed and created as a new event with a fresh uid | | master start moved (Etar "all events") | `start`/`duration`/`timeZone`, and every override key moved by the same local offset (overrides whose `start` equalled their old key move too), sent as the whole `recurrenceOverrides` map | -| two masters with the same `_SYNC_ID` (a `CONTENT_EXCEPTION_URI` split clones `_SYNC_ID`, `SYNC_DATA*` and `UID_2445`, and caps the old master's rule without `DIRTY`) | found before any merge or baseline heal: the newer row becomes a new event (fresh uid), the older master's current RRULE is uploaded | -| event moved to another calendar, or a series turned into a single event (Etar deletes and re-inserts both) | paired with the deleted row and uploaded as a patch of the same object (see [Uploads](#uploads)) | -| an RSVP for one instance (Etar goes through `CONTENT_EXCEPTION_URI`) | the override's whole `participants` map with the user's new status | +| two masters with the same `_SYNC_ID` (a `CONTENT_EXCEPTION_URI` split clones `_SYNC_ID`, `SYNC_DATA*` and `UID_2445`, and caps the old master's rule without `DIRTY`) | both masters are always loaded together. The download merges into the older master as a dirty item, leaves the newer one alone and heals no baseline of either. The upload resolves the split in its first step: the older master's current RRULE is uploaded, and the newer row is claimed as a new event with a fresh uid; that claim points the source's exception rows' `ORIGINAL_SYNC_ID` back at the source and sets the source `DIRTY=1`, so its capped rule uploads | +| a new master carrying another master's `UID_2445` (an app's "duplicate" copies it) | a clone without a source: claimed with a fresh uid and created as a new event, never adopting the other master's object (the engine loads every master that shares a new master's `UID_2445`) | +| event moved to another calendar, or a series turned into a single event (Etar deletes and re-inserts both) | paired with the deleted row and uploaded as a patch of the same object (see [Uploads](#uploads)); never across JMAP accounts, where the move is a create in the target account and a destroy in the source | +| `CALENDAR_ID` changed in place to a calendar of another JMAP account | not uploaded; the next download of the object puts the row back | +| an RSVP for one instance (Etar goes through `CONTENT_EXCEPTION_URI`) | the override's whole `participants` map with the user's new status. An exception row's status uploads only when the user organizes the event: Etar also puts `STATUS_CONFIRMED` on the instance, and Stalwart refuses an attendee's change to an event's status | + +On an exception row an app created, an empty title, description, location or +colour, or no attendee or reminder rows, means "inherit from the series", never +a deletion. An override never carries `privacy`: Stalwart drops it from +overrides. The first override of an event with none sends the whole `recurrenceOverrides` map (a patch below a missing property fails). @@ -943,8 +1090,10 @@ calendar app would notify about: - the user is the organizer and the event has other participants: `true` when title, description, timing, rule, overrides, location, status or attendees - changed, and on destroy (CANCEL); reminders, colour, availability and - privacy alone send nothing; + changed, on a create, and on destroy (CANCEL); reminders, colour, + availability and privacy alone send nothing. An event without an organizer + counts as the user's: once it has attendees, Stalwart makes the account's + first address its organizer; - the user is an attendee: `true` for their own RSVP change and on destroy (Stalwart sends REPLY DECLINED); other edits are stored for the user only; - events in the past: Stalwart sends nothing anyway. @@ -961,10 +1110,13 @@ app asks who should remind the user, per account: so apps offer no reminder editor; reminder rows an app adds anyway are neither uploaded nor deleted. -Changing the choice first uploads pending reminder edits (a normal run), then -rewrites the Reminders rows of that account's events together with their -baselines, one group per event that asserts the projection it read; no server -writes. +Changing the choice requests a calendar sync. The calendar SyncState records +who the rows were written for (`reminderOwner`), so that run notices the +change: its uploads still work for the old owner, so pending reminder edits +go up first. After the upload it rewrites the Reminders rows of that account's +clean events together with their baselines, one group per event that asserts +the projection it read, and sets the calendars' `MAX_REMINDERS`; no server +writes. A reminder edit that has not reached the server is never overwritten. ## Headless payload, report and SyncResult @@ -991,13 +1143,16 @@ tooManyDeletions?: {count, threshold}, delayUntil?, moreRecordsToGet? }`. | `cancelled` | framework cancel or out of time | stats only | | `disabled` | sync is off | nothing | -`delayUntil` (a 429's `Retry-After`, epoch seconds) and `moreRecordsToGet` -(→ `fullSyncRequested`) pass through. A run without a report is a soft error. -The stats count only rows actually written and objects actually sent, so a run -that repeated a page without writing anything is not "progress" to SyncManager. -A change pushed while a run is active (SyncManager drops a `requestSync` that -matches a running sync) flags the run; the adapter reads the flag after the run -is closed and then sets `fullSyncRequested`. +A 429 ends the run with `io` and `delayUntil` from its `Retry-After` (epoch +seconds; 60 s when the server sends none). `delayUntil` and +`moreRecordsToGet` (→ `fullSyncRequested`) pass through. A run without a +report is a soft error. The stats count only rows actually written and objects +actually sent, so a run that repeated a page without writing anything is not +"progress" to SyncManager. A change pushed while a run is active (SyncManager +drops a `requestSync` that matches a running sync) flags the run; the adapter +reads the flag after the run is closed and then sets `fullSyncRequested`. The +push of our own upload arrives while the run is still open, so every run that +uploads gets one follow-up sync (see [Triggers](#triggers)). ## Native module API @@ -1009,7 +1164,7 @@ nested maps. Every provider call is scoped to one account of our type. |---|---| | `getInfo()` | `{ accountType, sdkInt }` | | `listAccounts()` | our accounts with their `registryId` | -| `ensureAccount(name, registryId)` | adds the account (both authorities syncable, automatic off); true when created, false when it exists for the same registry id; rejects (`conflict`) when an account of that name belongs to another registry id | +| `ensureAccount(name, registryId)` | adds the account (both authorities syncable, automatic off); true when created, false when it exists for the same registry id or without one (it is adopted); rejects (`conflict`) when an account of that name belongs to another registry id | | `removeAccount(name)` | removes it; the providers drop its rows | | `getSyncSettings(name)` | master auto-sync, and per authority: syncable, automatic, periodic seconds, active, pending | | `setSyncEnabled(name, authority, on)` | syncable = 1 and `setSyncAutomatically` | @@ -1064,7 +1219,10 @@ Scoping, enforced in Kotlin for every op (`data1` of a membership row) checks it: gone fails with `assert` (re-read and re-plan), another account's with `scope`. A back-reference must point to an earlier insert of the right table; settings inserts return no id. -- `account_name`/`account_type`/`data_set` may only be written to restate the +- Inserts into the tables with account columns (raw contacts, groups, + settings, calendars, colors) get `account_name` and `account_type` added, + so calendar inserts carry them too. Otherwise + `account_name`/`account_type`/`data_set` may only be written to restate the account, on the tables that have them (`scope` otherwise). - Tables are an enumerated set per authority; anything else is refused (`scope`). @@ -1098,12 +1256,17 @@ What the providers do with values, which the planners account for: 1-second delayed storage) and not part of the settings export. Per account: `contactsSelection` / `calendarSelection` (explicit choices only; unlisted collections follow the default: the personal account's on, shared accounts' -off), `newContactsAddressBook`, `reminderOwner`, `intervalSeconds` per -authority, the last `RunStatus` per authority, and `removedInAndroidSettings`. -Whether sync is on is not stored: Android's `getSyncAutomatically` is the -source of truth. Headless runs await hydration before any `set()` (zustand 5 -writes on every `set()`, even before hydration, and would overwrite the stored -preferences). +off, and the webmail's "Trusted Senders" address book off too: it is the +allow-list of senders whose images load, not an address book people expect +in their phone's contacts), `newContactsAddressBook`, `reminderOwner`, +`intervalSeconds` per authority, the last `RunStatus` per authority, +`androidAccountName` (the Android account it uses), `knownStates` (the latest +states the engine synced, per JMAP account and type, for the echo skip), +`enabled` (what the user turned on in the app), `removedInAndroidSettings`, +`suspended` and `primaryJmapAccounts`. Android's `getSyncAutomatically` stays +the source of truth for whether an authority syncs (see Lifecycle). Headless +runs await hydration before any `set()` (zustand 5 writes on every `set()`, +even before hydration, and would overwrite the stored preferences). ### Settings sections @@ -1115,8 +1278,9 @@ native module present). Settings → Contacts and Settings → Calendar each get settings" when the account exists but the authority is off there; a hint when the device's master auto-sync is off); - a chooser (sheet) for address books or calendars, with personal ones on and - shared ones off by default, read-only ones marked, and a warning when another - signed-in account already syncs the same shared collection; + shared ones (and Trusted Senders) off by default, read-only ones marked, and + a warning when another signed-in account already syncs the same shared + collection; - contacts: the address book for new contacts; - calendar: who reminds you (calendar app or Bulwark); - the interval: 15 min, 30 min, 1 h (default), 6 h, manual; @@ -1134,27 +1298,54 @@ app's system settings when Android no longer asks. - **Enable:** permissions → `ensureAccount` → `setSyncEnabled(true)` → `setPeriodicSync(interval)` → `requestSync(manual)`; push routes and push types are refreshed. -- **Disable an authority:** the engine's teardown runs under the run mutex: - it uploads the authority's dirty, deleted and new items (bounded to 30 s; - it is not a sync run, so the "disabled" preflight does not stop it), then - deletes the authority's rows and SyncState. When items could not be uploaded - (offline, errors), nothing is deleted and the app asks: "N changes made on - this device haven't reached the server. Turn off anyway?" Only then - `setSyncEnabled(false)`; when both authorities are off the Android account - is removed. +- **Disable an authority:** automatic sync goes off first + (`setSyncEnabled(false)`), so no queued sync downloads everything again + right after the rows are gone. Then the engine's teardown runs under the + run mutex; a sync of that account and authority that holds it is asked to + stop at its next checkpoint. The teardown uploads the authority's dirty, + deleted and new items (bounded to 30 s; it is not a sync run, so the + "disabled" preflight does not stop it), with the [deletion + threshold](#deletion-threshold) applied, and never uploads rows written for + another registry account or server (SyncState `owner`). Then it deletes the + authority's rows and clears the SyncState by writing `''`. When items could + not be uploaded (offline, errors, deletions above the threshold), nothing is + deleted and the app asks: "N changes made on this device haven't reached + the server. Turn off anyway?" Turning off anyway still makes one short + upload attempt (5 s) before the rows go; "Keep syncing" turns automatic sync + back on. With nothing waiting the rows go without a question: the server + keeps everything. When both authorities are off the Android account is + removed. Turning calendar sync off also forgets who reminds of synced + events, so the question comes again the next time. - **Logout** (`auth-store.logout`, `logoutAll`, `removeAccount`): the same as disabling both, before the credentials go, with the same question when - changes would be lost. + changes would be lost; "stay signed in" keeps the accounts syncing. Several + accounts are released in parallel, a toast says it is working, and an + internal error never blocks signing out (the next reconcile suspends what + was left). - **Registry evictions** (the app drops an account on its own, e.g. after an `AuthenticationError`): reconciling Android accounts against the registry at - every engine start and on foreground finds an Android account whose registry - id is gone. It is not removed: automatic sync is turned off and one - notification says how many device changes are waiting ("Sign in again"). - Signing in again resumes it; removing it in the app or in Android Settings - drops it. An empty or unreadable registry is never acted on. + launch, on foreground and whenever accounts of our type change finds an + Android account whose registry id is gone. It is not removed: automatic + sync is turned off and one notification says it stopped syncing ("Sign in + again"; it gives no count of waiting changes, which would need a provider + read). Signing in again resumes exactly the authorities that synced; the + section lists the account with "Sign in again" and "Remove from this + device", and removing it there or in Android Settings drops it. An empty or + unreadable registry is never acted on. - **Account removed in Android Settings:** detected by the accounts listener and on foreground; the store marks it, the switches show off, and the app does not recreate the account. +- The store keeps what the user turned on in the app (`enabled`) next to + Android's switches, which stay the source of truth: an authority switched + on in Android Settings is adopted as on, one switched off there shows as + "Paused in Android settings" (not "Off"), and "Sync now" is disabled while + paused, since a manual run would only report `disabled`. `suspended` marks + an evicted account, `primaryJmapAccounts` the personal JMAP account per + authority (push routes and the trigger filter). +- An Android account that exists without a registry id (created, then the + app died before writing it) is adopted by the next `ensureAccount`; one + with another registry id is refused (`conflict`) and the app uses the + registry id as the account name instead. - **Uninstall:** Android removes the accounts of an uninstalled authenticator, and the providers delete their rows. - **Permission revoked:** Android fails the sync before our code runs @@ -1171,20 +1362,36 @@ app's system settings when Android no longer asks. `Calendar`/`CalendarEvent` on a synced account (`onStateChangeType` in `state-change-bus.ts`; the stream serves the active account), the app's own successful contact/calendar mutations (store actions), and returning to the - foreground → `requestSync` for that authority, debounced 5 s. A StateChange - whose state equals the one the engine last recorded is our own echo and is - skipped. + foreground → `requestSync` for that authority. The first request of an + account and authority arms a 5 s timer and later ones ride along (the timer + is not restarted), so an edit and the StateChange echoing it make one sync. + A StateChange whose state equals the one the engine last recorded is our + own echo and is skipped, also when the engine recorded it while the timer + ran. Foreground requests go out at most once a minute per account and + authority, and the launch counts as one. Edits that change no device row + (tasks, calendar sharing, the default collections) request nothing. These + are ordinary requests, which Android drops while the account is paused. +- A push with no readable `changed` map (older relays) starts the mail task + as before and requests no syncs; periodic syncs cover those setups. +- Every run that uploads gets one follow-up sync: the server's push of our + own change arrives while the run is open, and SyncManager drops a request + matching the running sync, so the run asks for another one when it ends. + The follow-up finds nothing to do, so it stays one. - **App closed:** while an account has device sync on, its push subscription adds `ContactCard`, `AddressBook` (contacts) and `CalendarEvent`, `Calendar` - (calendar) to `EmailDelivery`, per account. The relay forwards the - StateChange's `changed` map verbatim (FCM data key `changed`; only its first - account key is repeated as `accountId`). A native router in the FCM and - UnifiedPush services walks every account key of `changed`, maps contact and - calendar types to `requestSync` through the routes JS stored, and starts the - mail push task only when some account's map has `EmailDelivery`. A guard at - the top of `pushBackgroundTask` returns for StateChanges without - `EmailDelivery`, so a contact change can never take the legacy "notify the - newest unread mail" path. + (calendar) to `EmailDelivery`, per account. Turning sync on or off updates + the subscription right away; for a signed-in account other than the one the + UI serves, through a client of its own, never the UI's singleton. The relay + forwards the StateChange's `changed` map verbatim (FCM data key `changed`; + only its first account key is repeated as `accountId`). A native router in + the FCM and UnifiedPush services walks every account key of `changed`, maps + contact and calendar types to `requestSync` through the routes JS stored + (`setPushRoutes`; the device-sync layer refreshes them whenever the device + sync preferences change), and starts the mail push task only when some + account's map has `EmailDelivery`. A guard at the top of + `pushBackgroundTask` returns for StateChanges without `EmailDelivery`, so a + contact change can never take the legacy "notify the newest unread mail" + path. ## Failure matrix @@ -1197,20 +1404,23 @@ app's system settings when Android no longer asks. | Password revoked | `auth` (hard, no retry) + "Sign in again" | user signs in; the next trigger syncs | | Permission revoked | Android fails the sync (`databaseError`) | "Grant access" in settings | | `cannotCalculateChanges` | full reconcile of that JMAP account | automatic | -| Server `SetError` on an item | notFound → local delete; forbidden with scheduling → retried without it ("invitations not sent"); forbidden → revert; others → poisoned with back-off, listed in the report | automatic / user fixes the item | -| `stateMismatch` | re-download, retry up to 3 times, then `io` without progress | automatic (back-off) | -| 429 | `delayUntil` = Retry-After | automatic | +| Server `SetError` on an item | notFound on an update → local delete; forbidden with scheduling → retried without it ("invitations not sent"); forbidden on an update or deletion → rows rewritten from the server's current version; forbidden on a create, and the rest → poisoned with back-off, listed in the report | automatic / user fixes the item | +| A whole `/set` refused for an account (`accountReadOnly`, `forbidden`, `accountNotFound`, `accountNotSupportedByMethod`) | that account's items wait, the report lists the account; the other accounts go on | automatic once the account takes writes | +| A shared account refuses to list its address books or calendars (`forbidden`, `accountNotFound`, `accountNotSupportedByMethod`) | the account is left out of the run and only logged; its rows stay untouched | automatic once it lists them again | +| A planner throws on an item | download: the object goes on `stale`; upload: the item is skipped (`plannerError`); the rest of the run goes on | fixed in code; listed in the report | +| `stateMismatch` or `serverUnavailable` on a `/set` | re-download, retry up to 3 times, then `io` without progress | automatic (back-off) | +| 429 | `io` with `delayUntil` = Retry-After (60 s without one) | automatic | | Too many local deletions | `tooManyDeletions` + system notification | user picks delete or undo | | Empty server on a full reconcile | `safetyAbort`, device untouched | user checks the server; turning sync off and on starts over | | Edit during an upload | clearing assert fails → DIRTY stays, shadow updated | next run uploads the newer edit | | JS crash or reload mid-run | task ends without report → soft error | automatic | | React fails to start (debug without Metro) | boot fault → soft error in ~2 s | automatic | | Account removed in Settings | providers drop rows; app turns sync off | user turns it on again | -| Logout / turning sync off | pending changes uploaded first; if that fails the user is asked before anything is deleted | — | -| Provider refuses a batch (`assert`/`tooLarge`/`provider`) | per-item retry / split; an item that still fails goes on `stale` and blocks its uploads | automatic, or listed in the report | -| App drops the account from its registry (e.g. after an auth error) | Android account kept, sync off, notification with the number of waiting changes | user signs in again, or removes it | -| Rows written for another server or registry account (SyncState `owner`) | the run stops with `internal` | the app offers to start over after uploading what it can | -| DST-ambiguous start on a recurring event, unrepresentable rule | item poisoned with a reason; device shows it, never uploads its timing | limitation | +| Logout / turning sync off | pending changes uploaded first; if that fails (or deletions exceed the threshold) the user is asked before anything is deleted | — | +| Provider refuses a batch (`assert`/`tooLarge`/`provider`) | per-item retry / split; an item that still fails goes on `stale` (the state still advances) and uploads nothing until it was fetched again | automatic, or listed in the report | +| App drops the account from its registry (e.g. after an auth error) | Android account kept, sync off, one "Sign in again" notification without a count of waiting changes; the section lists the account | user signs in again, or removes it from the device | +| Rows written for another server or registry account (SyncState `owner`) | the run stops with `internal`; the settings show its message | turning sync off and on starts over; the teardown uploads nothing for the other owner and asks before its changes are lost | +| DST-ambiguous start on a recurring event, unrepresentable rule | item skipped and reported with a reason (`dstAmbiguous`, `ruleNotRepresentable`); device shows it, never uploads its timing | limitation | ## Limits and performance @@ -1219,12 +1429,19 @@ app's system settings when Android no longer asks. in flight), `maxSizeRequest` (10 MB; the engine targets 1 MB), queries of at most 5000 ids per call, `/changes` pages of 256 (Stalwart caps at 5000), cards and events of at most 512 KiB. -- Provider: ≤ 499 ops between yield points, ~300 KB per `applyBatch`, photos in - their own batch; 50 raw contacts or 50 events per chunk. +- Provider: ≤ 499 ops between yield points (the engine packs at most 400 per + batch), ~300 KB per `applyBatch`, photos in their own batch; 50 raw contacts + or 50 events per chunk. - JS thread: a `setTimeout(0)` yield between chunks, so the UI keeps rendering when the app is open; JSON crosses the bridge as strings. - Time: 9.5 min per run (2.5 min expedited), checkpoints between chunks, - continuation through `fullSyncRequested`. + continuation through `fullSyncRequested`. Checkpoints stop 30 s before the + payload's deadline and a create batch needs 60 s left (see [A sync + run](#a-sync-run)), so an expedited sync that cold-starts React Native has + little room for creates. +- Contacts: the first card with a relation to name, or the first change of a + group's members, reads every synced contact's shadow (`SYNC2`) once per run + to index the cards by uid. The 2,000 + 2,000 scale run is recorded in the parity notes when #34 closes. @@ -1276,6 +1493,10 @@ Decisions made where the brief left a choice (safe defaults): - An app account dropped from the registry keeps its Android account (sync off) until the user signs in again or removes it: its device changes would otherwise be lost. +- A shared account that refuses to list its address books or calendars is + left out of the run and only logged, not reported: Stalwart lists an account + shared for mail only with every capability, so the warning would never go + away. Its rows stay on the device, since a refusal can pass. - The user's own address for "me" and `OWNER_ACCOUNT`: the session login when it is an email address, else the first identity's address; aliases from identities also count as "me". @@ -1287,8 +1508,9 @@ Known limitations: - Fossify Contacts re-inserts rows on save: a field or entry it does not model (a second nickname or organization, a department, a label) is kept on the server, but an entry deleted or a field cleared in Fossify is not deleted or - cleared on the server. Fossify Calendar's rule edits upload only in the parts - its editor models. + cleared on the server, and comes back on the device with the contact's next + clean write. Fossify Calendar's rule edits upload only in the parts its + editor models. - Android shows extra instances for BYSETPOS rules other than monthly-BYDAY, and none for a daily series older than about 5.5 years (AOSP's 2000-period expansion cap). @@ -1296,11 +1518,18 @@ Known limitations: uploaded. `EXRULE` is not supported by Stalwart. - Recurring events whose start or override falls in a DST overlap cannot be uploaded (Stalwart drops ambiguous local times). +- An `UNTIL` in a DST gap or overlap moves one hour later, so an Etar "this + and following" split exactly at an instance inside the repeated hour can + include that instance in the old series again (better than Stalwart + dropping the `UNTIL`). - A single occurrence that Stalwart holds only as an instance (no master) is shown but read-only. +- In a calendar that grants RSVP but no writes, only the user's answer to the + whole series uploads; an answer for one instance is reverted. - Moving an event to a calendar of another JMAP account uploads as a create in the target account and a destroy in the source (a new uid; participants are - notified accordingly). + notified accordingly). An app that moves it by changing `CALENDAR_ID` in + place gets the row put back by the next download of the event. - Descriptions over 64 KB are shown truncated and are not uploaded when edited. - A shared address book or calendar that two signed-in accounts can both see appears once per account when both sync it (each Android account holds its @@ -1310,6 +1539,15 @@ Known limitations: - Photos travel as 512 px JPEG `data:` URIs; a larger original is replaced by its scaled copy only when the user changes the photo on the device. +Open: + +- Contacts: a custom `name/full` is kept even when a component edit makes it + stale; a group title cleared on the device is not uploaded; clearing company + and department leaves an organization entry without a name on the server. +- Calendar: a recurrence id in a repeated hour uses the first instant (RFC + 5545) for `ORIGINAL_INSTANCE_TIME`; to be checked against CalendarProvider's + instance expansion on a device. + ## Out of scope Possible follow-ups: From 49c5d05c3077c79fe4b1beaeb43d20b997a7ae8c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 06:12:08 +0200 Subject: [PATCH 032/334] fix: keep every provider write of device sync inside its own account #34 The native side ANDs the account's scope onto each selection the engine sends, as `(where) AND (scope)`. A selection such as `1) OR (1` closed the scope's parenthesis itself: on the calendar tables, where CalendarProvider adds no account filter of its own, an update or delete built that way would have reached the events, attendees and calendars of other accounts, e.g. the user's Google calendar. The engine only sends constant selections, so this guards against bugs rather than anything an outside app could send, which is what the scope is for. - Selections and sort orders must balance their parentheses outside quoted literals, and may not end the statement, carry comments or query other tables (`SELECT`, `UNION`, `ATTACH`, `PRAGMA`). - An update of a data row by id that writes `data1` without restating the mimetype has the row's mimetype looked up: on a membership row `data1` names a group, which must be the account's, as on insert. Such writes by selection are refused. - `data14`, a photo row's file id, is never written: another account's file id would have let `readPhoto` read that account's photo. Checked: 58 JVM tests (`gradlew :app:testDebugUnitTest`, three new), the debug build, and every selection the engine and planners send against the new rules. --- .../bulwarkmobile/sync/ProviderIo.kt | 8 +++ .../bulwarkmobile/sync/ProviderOps.kt | 60 +++++++++++++++- .../bulwarkmobile/sync/ProviderScope.kt | 68 +++++++++++++++++-- .../bulwarkmobile/sync/ProviderOpsTest.kt | 47 +++++++++++++ 4 files changed, 177 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderIo.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderIo.kt index cb0e250d..37f23290 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderIo.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderIo.kt @@ -199,6 +199,14 @@ class ProviderIo(context: Context, accountName: String, private val authority: S override fun dataRawContacts(ids: Set): Map = idPairs(ProviderTable.DATA, "raw_contact_id", ids) + override fun dataMimetypes(ids: Set): Map { + val mimetypes = HashMap() + forEachRow(ProviderTable.DATA, arrayOf(ID, "mimetype"), ID, ids) { c -> + if (!c.isNull(1)) mimetypes[c.getLong(0)] = c.getString(1) + } + return mimetypes + } + override fun eventCalendars(ids: Set): Map = idPairs(ProviderTable.EVENTS, "calendar_id", ids) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderOps.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderOps.kt index 167bbd4d..b5d9555b 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderOps.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderOps.kt @@ -158,6 +158,61 @@ sealed interface ProviderOp { const val GROUP_MEMBERSHIP_MIMETYPE = "vnd.android.cursor.item/group_membership" +/** + * Checks a selection or sort order before the account's scope is ANDed onto + * it as `(text) AND (scope)`: its parentheses must balance outside quoted + * literals, or `1) OR (1` would close the scope's own parenthesis. It may + * not end or comment out the statement, nor query other tables. The engine's + * selections are constants with `?` arguments; this guards against bugs. + */ +object SqlFragments { + private val FORBIDDEN_WORDS = Regex("\\b(SELECT|UNION|ATTACH|DETACH|PRAGMA)\\b", RegexOption.IGNORE_CASE) + + fun check(text: String, what: String) { + var depth = 0 + var quote: Char? = null + val outside = StringBuilder(text.length) + var i = 0 + while (i < text.length) { + val c = text[i] + if (quote != null) { + if (c == quote) { + // A doubled quote is an escaped one inside the literal. + if (i + 1 < text.length && text[i + 1] == quote) i++ else quote = null + } + outside.append(' ') + } else { + when (c) { + '\'', '"' -> { + quote = c + outside.append(' ') + } + '(' -> { + depth++ + outside.append(c) + } + ')' -> { + if (--depth < 0) refuse(what, "closes a parenthesis it did not open") + outside.append(c) + } + ';' -> refuse(what, "ends the statement") + '\u0000' -> refuse(what, "contains a NUL character") + else -> outside.append(c) + } + } + i++ + } + if (quote != null) refuse(what, "leaves a quote open") + if (depth != 0) refuse(what, "leaves a parenthesis open") + val bare = outside.toString() + if ("--" in bare || "/*" in bare) refuse(what, "contains a comment") + FORBIDDEN_WORDS.find(bare)?.let { refuse(what, "uses ${it.value.uppercase()}") } + } + + private fun refuse(what: String, why: String): Nothing = + throw ProviderRefusal(BatchFailure.SCOPE, "$what $why") +} + /** * The table a written column points into when the row it names must belong to * the account as well: the parent of a row, the master of an exception event, @@ -191,7 +246,8 @@ object ProviderOpParser { } if (columns.isEmpty()) throw invalid("query needs at least one column") val (where, args) = o.whereArgs("query") - return ProviderQuery(table, columns, where, args, o.optionalString("orderBy")) + val orderBy = o.optionalString("orderBy")?.also { SqlFragments.check(it, "query: orderBy") } + return ProviderQuery(table, columns, where, args, orderBy) } fun parseBatch(authority: String, json: String): List { @@ -323,7 +379,7 @@ object ProviderOpParser { } private fun JSONObject.whereArgs(where: String): Pair> { - val sql = optionalString("where") + val sql = optionalString("where")?.also { SqlFragments.check(it, "$where: where") } val args = when (val raw = opt("args")) { null, JSONObject.NULL -> emptyList() is JSONArray -> (0 until raw.length()).map { i -> diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderScope.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderScope.kt index 01729241..7706b0a1 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderScope.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/ProviderScope.kt @@ -45,21 +45,45 @@ class ScopePlanner(private val account: AccountRef, private val lookup: ScopeLoo } fun planBatch(ops: List): List { - val owners = Owners(ownershipRequests(ops)) - return ops.mapIndexed { i, op -> plan(i, op, owners) } + val memberships = membershipUpdates(ops) + val owners = Owners(ownershipRequests(ops, memberships)) + return ops.mapIndexed { i, op -> plan(i, op, owners, memberships) } } - private fun plan(i: Int, op: ProviderOp, owners: Owners): PlannedOp = when (op) { + /** + * Membership rows among the data rows that updates by id write `data1` + * of without restating the mimetype: their `data1` names a group, which + * must be the account's like on insert. + */ + private fun membershipUpdates(ops: List): Set { + val ids = ops.asSequence() + .filterIsInstance() + .filter { it.table == ProviderTable.DATA && writesUntypedData1(it.values) } + .mapNotNull { it.target.id } + .toSet() + if (ids.isEmpty()) return emptySet() + return lookup.dataMimetypes(ids).filterValues { it == GROUP_MEMBERSHIP_MIMETYPE }.keys + } + + private fun writesUntypedData1(values: Map) = + MIMETYPE !in values && (values[DATA1] ?: Cell.Null) != Cell.Null + + private fun plan(i: Int, op: ProviderOp, owners: Owners, memberships: Set): PlannedOp = when (op) { is ProviderOp.SetSyncState -> PlannedOp.SetSyncState(op.value) is ProviderOp.Insert -> { checkAccountColumns(i, op.table, op.values) + checkPhotoFile(i, op.table, op.values) checkReferences(i, op.table, op.values - op.refs.keys, owners) val values = if (op.table.scope == TableScope.ACCOUNT_COLUMNS) op.values + accountCells() else op.values PlannedOp.Insert(op.table, values, op.refs, op.yieldAllowed) } is ProviderOp.Update -> { checkAccountColumns(i, op.table, op.values) + checkPhotoFile(i, op.table, op.values) checkReferences(i, op.table, op.values, owners) + if (op.table == ProviderTable.DATA && writesUntypedData1(op.values)) { + checkMembershipUpdate(i, op, owners, memberships) + } checkTarget(i, op.table, op.target, owners) if (op.table == ProviderTable.EXTENDED_PROPERTIES) { // CalendarProvider updates extended properties only through @@ -146,6 +170,35 @@ class ScopePlanner(private val account: AccountRef, private val lookup: ScopeLoo } } + /** + * `data1` written by an update that doesn't restate the mimetype: on a + * membership row it names a group. Rows chosen by a selection could be + * memberships the lookup didn't see, so only updates by id may do it. + */ + private fun checkMembershipUpdate(i: Int, op: ProviderOp.Update, owners: Owners, memberships: Set) { + val id = op.target.id + ?: throw ProviderRefusal(BatchFailure.SCOPE, "op $i: data1 may only be written to a data row by id") + if (id !in memberships) return + val group = op.values[DATA1]?.rowId() + ?: throw ProviderRefusal(BatchFailure.PROVIDER, "op $i: a membership's data1 must be a group id") + when (owners.of(ProviderTable.GROUPS, group)) { + Owner.OURS -> Unit + Owner.FOREIGN -> throw ProviderRefusal(BatchFailure.SCOPE, "op $i: data.data1 names group $group of another account") + Owner.MISSING -> throw ProviderRefusal(BatchFailure.ASSERT, "op $i: data.data1 names group $group, which no longer exists") + } + } + + /** + * A photo row's file id (`data14`) is the provider's: it points into the + * photo store, where another account's file id would let `readPhoto` + * read that account's photo. Photos are written as bytes (`data15`). + */ + private fun checkPhotoFile(i: Int, table: ProviderTable, values: Map) { + if (table == ProviderTable.DATA && (values[DATA14] ?: Cell.Null) != Cell.Null) { + throw ProviderRefusal(BatchFailure.SCOPE, "op $i: data.data14 (a photo file id) is written by the provider only") + } + } + /** A row named by id must not belong to another account; a missing one fails on the expected count instead. */ private fun checkTarget(i: Int, table: ProviderTable, target: RowTarget, owners: Owners) { val id = target.id ?: return @@ -192,7 +245,7 @@ class ScopePlanner(private val account: AccountRef, private val lookup: ScopeLoo } /** Every row id the batch names, by table: op targets and referenced rows. */ - private fun ownershipRequests(ops: List): Map> { + private fun ownershipRequests(ops: List, memberships: Set): Map> { val requests = HashMap>() fun request(table: ProviderTable, id: Long) { requests.getOrPut(table) { HashSet() } += id @@ -209,6 +262,7 @@ class ScopePlanner(private val account: AccountRef, private val lookup: ScopeLoo is ProviderOp.Update -> { requestReferences(op.table, op.values) op.target.id?.let { request(op.table, it) } + if (op.target.id in memberships) op.values[DATA1]?.rowId()?.let { request(ProviderTable.GROUPS, it) } } is ProviderOp.Delete -> op.target.id?.let { request(op.table, it) } is ProviderOp.Assert -> op.target.id?.let { request(op.table, it) } @@ -275,6 +329,9 @@ class ScopePlanner(private val account: AccountRef, private val lookup: ScopeLoo const val ACCOUNT_NAME = "account_name" const val ACCOUNT_TYPE = "account_type" const val DATA_SET = "data_set" + private const val MIMETYPE = "mimetype" + private const val DATA1 = "data1" + private const val DATA14 = "data14" /** Ids per `IN (…)` list: a few KB of selection, far from SQLite's and Binder's limits. */ const val ID_LIST_CHUNK = 500 @@ -307,6 +364,9 @@ interface ScopeLookup { /** The raw contact of each data row that exists. */ fun dataRawContacts(ids: Set): Map + /** The mimetype of each data row that exists. */ + fun dataMimetypes(ids: Set): Map + /** The calendar of each event that exists. */ fun eventCalendars(ids: Set): Map diff --git a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/ProviderOpsTest.kt b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/ProviderOpsTest.kt index 751f0ad5..2d1f6930 100644 --- a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/ProviderOpsTest.kt +++ b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/ProviderOpsTest.kt @@ -23,6 +23,8 @@ private class FakeLookup(private val me: AccountRef = ME) : ScopeLookup { val colors = HashMap() /** data id → raw contact */ val data = HashMap() + /** data id → mimetype */ + val mimetypes = HashMap() /** event id → calendar */ val events = HashMap() /** child table → child id → event */ @@ -42,6 +44,8 @@ private class FakeLookup(private val me: AccountRef = ME) : ScopeLookup { override fun dataRawContacts(ids: Set) = data.filterKeys { it in ids } + override fun dataMimetypes(ids: Set) = mimetypes.filterKeys { it in ids } + override fun eventCalendars(ids: Set) = events.filterKeys { it in ids } override fun childEvents(table: ProviderTable, ids: Set): Map = @@ -306,6 +310,49 @@ class ProviderOpsTest { plan(CONTACTS, """{"op":"insert","table":"data","values":{"raw_contact_id":1,"mimetype":"x","data1":11}}""") } + @Test + fun `a membership row's group is checked on updates by id too`() { + lookup.mimetypes[100] = GROUP_MEMBERSHIP_MIMETYPE + plan(CONTACTS, """{"op":"update","table":"data","id":100,"values":{"data1":10}}""") + refused(BatchFailure.SCOPE) { plan(CONTACTS, """{"op":"update","table":"data","id":100,"values":{"data1":11}}""") } + refused(BatchFailure.ASSERT) { plan(CONTACTS, """{"op":"update","table":"data","id":100,"values":{"data1":12}}""") } + // data1 by a selection could reach memberships the lookup never saw. + refused(BatchFailure.SCOPE) { + plan(CONTACTS, """{"op":"update","table":"data","where":"raw_contact_id = ?","args":[1],"values":{"data1":11}}""") + } + // data1 of other kinds is plain data. + lookup.mimetypes[100] = "vnd.android.cursor.item/phone_v2" + plan(CONTACTS, """{"op":"update","table":"data","id":100,"values":{"data1":11}}""") + } + + @Test + fun `photo file ids are never written`() { + val photo = """"raw_contact_id":1,"mimetype":"vnd.android.cursor.item/photo"""" + plan(CONTACTS, """{"op":"insert","table":"data","values":{$photo,"data14":null,"data15":{"b64":"AAAA"}}}""") + refused(BatchFailure.SCOPE) { plan(CONTACTS, """{"op":"insert","table":"data","values":{$photo,"data14":5}}""") } + refused(BatchFailure.SCOPE) { plan(CONTACTS, """{"op":"update","table":"data","id":100,"values":{"data14":5}}""") } + } + + @Test + fun `a selection can't close the scope's parentheses or query elsewhere`() { + for (where in listOf("1) OR (1", "dirty = 1) OR (1 = 1", "(1", "1; DELETE FROM events", "1 -- x", "1 /* x */", + "_id IN (SELECT _id FROM events)", "1 UNION ALL x", "title = 'open")) { + val quoted = JSONObject.quote(where) + refused(BatchFailure.SCOPE) { plan(CALENDAR, """{"op":"update","table":"events","where":$quoted,"values":{"dirty":1}}""") } + refused(BatchFailure.SCOPE) { plan(CALENDAR, """{"op":"delete","table":"attendees","where":$quoted}""") } + refused(BatchFailure.SCOPE) { + ProviderOpParser.parseQuery(CALENDAR, """{"table":"attendees","columns":["_id"],"where":$quoted}""") + } + } + refused(BatchFailure.SCOPE) { + ProviderOpParser.parseQuery(CONTACTS, """{"table":"raw_contacts","columns":["_id"],"orderBy":"_id); DROP TABLE x; --"}""") + } + // Parentheses and keywords inside quoted literals are data. + plan(CALENDAR, """{"op":"update","table":"events","where":"title = '(x' OR title = 'select'","values":{"dirty":1}}""") + plan(CALENDAR, """{"op":"assert","table":"events","where":"(dirty = 1 OR deleted = 1) AND _sync_id LIKE '~pending/%'","expectCount":0}""") + ProviderOpParser.parseQuery(CONTACTS, """{"table":"raw_contacts","columns":["_id"],"orderBy":"_id DESC"}""") + } + @Test fun `moving a row to another parent is checked like an insert`() { refused(BatchFailure.SCOPE) { plan(CALENDAR, """{"op":"update","table":"events","id":30,"values":{"calendar_id":22}}""") } From 232e892328bbc13c18562ca5eee1a9d6b1630093 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 06:57:28 +0200 Subject: [PATCH 033/334] fix: keep an occurrence's description or location empty once cleared #34 Clearing the description or location of one occurrence of a series in a calendar app removed the value from the occurrence's override. An override without a value of its own shows the series' one, so the write after the upload put "Daily agenda" or "Room 1" back into the occurrence and made it clean: the user's edit was gone without a trace. An existing override now gets an explicit empty value while the series has one to fall back to: the empty text for the description, and a location without a name, the only empty forms Stalwart keeps (calcard writes no LOCATION line for an empty map, which then reads back as the series' locations). Without a series value the override's own is removed as before. New exception rows keep treating an empty value as "inherit". Checked with a planner test and a run through the engine and the fake server: the row stays cleared and clean, and the echo writes nothing. --- .../__tests__/calendar/engine.test.ts | 47 +++++++++++++++++++ .../__tests__/calendar/exceptions.test.ts | 43 +++++++++++++++++ src/device-sync/calendar/upload.ts | 20 +++++++- 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 src/device-sync/__tests__/calendar/engine.test.ts diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts new file mode 100644 index 00000000..d7d30337 --- /dev/null +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -0,0 +1,47 @@ +// The calendar planner through the engine and the fake JMAP server: device +// edits reach the server as the user made them and the rows stay as the user +// left them, end to end. Mapping details are the planner's own tests. + +import { describe, expect, it } from 'vitest'; +import { Events } from '../../android-columns'; +import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { CALENDAR_AUTHORITY, createHarness, type Harness } from '../engine/harness'; + +function real(options: Parameters[0] = {}): Harness { + const h = createHarness(options); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + return h; +} + +type Overrides = Record>; + +describe('calendar device edits through the engine', () => { + it('keeps a description and location cleared on one occurrence cleared', async () => { + const h = real(); + const id = h.server.addEvent('a', { + uid: 'standup-uid', + title: 'Standup', + description: 'Daily agenda', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'daily', count: 5 }, + locations: { locA: { '@type': 'Location', name: 'Room 1' } }, + recurrenceOverrides: { + '2026-09-29T09:00:00': { title: 'Standup', description: 'Special agenda', locations: { locB: { '@type': 'Location', name: 'Room 2' } } }, + }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const exception = () => h.events().find((e) => e[Events.ORIGINAL_SYNC_ID] === `a/${id}`)!; + expect(exception()).toMatchObject({ [Events.DESCRIPTION]: 'Special agenda', [Events.EVENT_LOCATION]: 'Room 2' }); + + h.device.user.updateEvent(Number(exception()._id), { [Events.DESCRIPTION]: null, [Events.EVENT_LOCATION]: null }); + expect(await h.run(CALENDAR_AUTHORITY)).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); + + expect(exception()).toMatchObject({ [Events.DESCRIPTION]: null, [Events.EVENT_LOCATION]: null, [Events.DIRTY]: 0 }); + const overrides = h.server.get('CalendarEvent', 'a', id)!.recurrenceOverrides as Overrides; + expect(overrides['2026-09-29T09:00:00']).toMatchObject({ description: '', locations: { locB: { name: '' } } }); + }); +}); diff --git a/src/device-sync/__tests__/calendar/exceptions.test.ts b/src/device-sync/__tests__/calendar/exceptions.test.ts index 540f73a6..82007b54 100644 --- a/src/device-sync/__tests__/calendar/exceptions.test.ts +++ b/src/device-sync/__tests__/calendar/exceptions.test.ts @@ -103,6 +103,49 @@ describe('calendar planner: recurrence edits made on the device', () => { expect(h.row(x.eventId)).toBeUndefined(); }); + it('clears an occurrence\'s own description and location instead of letting the series\' come back', async () => { + const event = weekly(); + event.description = 'Daily agenda'; + event.recurrenceOverrides!['2026-10-07T09:00:00'] = { + ...event.recurrenceOverrides!['2026-10-07T09:00:00'], + description: 'Special agenda', + locations: { locB: { '@type': 'Location', name: 'Room 2' } }, + }; + const { h } = await synced(event); + const x = (await h.local('bl'))!.exceptions[0]; + expect(h.row(x.eventId)).toMatchObject({ [Events.DESCRIPTION]: 'Special agenda', [Events.EVENT_LOCATION]: 'Room 2' }); + h.fake.user.updateEvent(x.eventId, { [Events.DESCRIPTION]: null, [Events.EVENT_LOCATION]: null }); + const local = (await h.local('bl'))!; + const plan = calendarPlanner.planUpload(local, h.ctx); + const server = await accept(h, event, local, plan); + expect(h.row(x.eventId)).toMatchObject({ [Events.DESCRIPTION]: null, [Events.EVENT_LOCATION]: null, [Events.DIRTY]: 0 }); + expect(calendarPlanner.planDownload(server, (await h.local('bl'))!, h.ctx).effect).toBe('none'); + // Without a value of its own the override would show the series' "Daily agenda" and "Room 1" again, so it + // gets the empty ones Stalwart keeps (an empty map would read back as no locations, i.e. the series' ones). + expect(patchOf(plan).patch).toEqual({ + 'recurrenceOverrides/2026-10-07T09:00:00/description': '', + 'recurrenceOverrides/2026-10-07T09:00:00/locations': { locB: { '@type': 'Location', name: '' } }, + }); + }); + + it('removes an occurrence\'s own description and location when the series has none to show instead', async () => { + const event = weekly(); + delete event.description; + delete event.locations; + event.recurrenceOverrides!['2026-10-07T09:00:00'] = { + ...event.recurrenceOverrides!['2026-10-07T09:00:00'], + description: 'Special agenda', + locations: { locB: { '@type': 'Location', name: 'Room 2' } }, + }; + const { h } = await synced(event); + const x = (await h.local('bl'))!.exceptions[0]; + h.fake.user.updateEvent(x.eventId, { [Events.DESCRIPTION]: null, [Events.EVENT_LOCATION]: null }); + expect(patchOf(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx)).patch).toEqual({ + 'recurrenceOverrides/2026-10-07T09:00:00/description': null, + 'recurrenceOverrides/2026-10-07T09:00:00/locations': null, + }); + }); + it('patches an existing override one level deep, never deeper', async () => { const { h } = await synced(weekly()); const x = (await h.local('bl'))!.exceptions[0]; diff --git a/src/device-sync/calendar/upload.ts b/src/device-sync/calendar/upload.ts index d312f3b8..fb877a51 100644 --- a/src/device-sync/calendar/upload.ts +++ b/src/device-sync/calendar/upload.ts @@ -454,10 +454,10 @@ function exceptionChanges( break; case 'description': if (x.baseline && isTruncatedBaseline(x.baseline.cells[Events.DESCRIPTION])) continue; - values.description = descriptionValue(cells); + values.description = descriptionValue(cells) ?? clearedInOverride('description', instance, shadow); break; case 'location': - values.locations = locationChanges(instance, cells[Events.EVENT_LOCATION], ctx.mintKey).locations; + values.locations = locationChanges(instance, cells[Events.EVENT_LOCATION], ctx.mintKey).locations ?? clearedInOverride('location', instance, shadow); break; case 'status': values.status = statusFromDevice(cells[Events.STATUS]); @@ -530,6 +530,22 @@ function isEmptyUnit(unit: string, cells: Row): boolean { return v === null || v === undefined || v === ''; } +/** + * A description or location cleared on an existing override. An override + * without one of its own shows the series' again, so while the series has + * one the override gets the empty value Stalwart keeps: the empty text, or a + * location without a name (an empty map writes no LOCATION line and reads + * back as none of its own). Null, a clean removal, when nothing would come + * back. + */ +function clearedInOverride(unit: 'description' | 'location', instance: CalendarEventWire, master: CalendarEventWire): unknown { + if (unit === 'description') return typeof master.description === 'string' && master.description ? '' : null; + const series = Object.keys(master.locations ?? {}); + if (!series.length) return null; + const key = Object.keys(instance.locations ?? {})[0] ?? series[0]; + return { [key]: { '@type': 'Location', name: '' } }; +} + /** * A new override: the changes plus the details Stalwart needs on it (title, * locations, participants, sequence, …; `withNewOverrideDetails`), minus what From 2870a5997165f0147c2f99b8a6cec26c52bf9e36 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 06:58:16 +0200 Subject: [PATCH 034/334] fix: stop events from Google Calendar arriving as tentative #34 Google Calendar leaves STATUS empty on the events and exceptions it creates, and CalendarProvider has no default for it. Device sync read the empty value as 0, STATUS_TENTATIVE, so every event and every "this event" edit made there uploaded as "tentative" and came back to the phone that way; a copy made with "Copy to" also never paired with the deleted original. An empty STATUS now sets no status: a create leaves `status` out, so the server keeps its default of confirmed, an override gets none of its own, an edited event never uploads one, a merge takes the server's without counting a conflict, and a pair ignores it. After the upload the rows hold the server's status. A new series from Etar's "this and following" also no longer uploads the old series' exclusions from before its start, which Etar copies into the new row's EXDATE. Checked with planner tests for each path and a run through the engine and the fake server. --- .../__tests__/calendar/engine.test.ts | 41 ++++++++++++++ .../__tests__/calendar/exceptions.test.ts | 15 +++++ .../__tests__/calendar/lifecycle.test.ts | 56 +++++++++++++++++++ src/device-sync/calendar/create.ts | 10 +++- src/device-sync/calendar/merge.ts | 3 +- src/device-sync/calendar/pairs.ts | 3 + src/device-sync/calendar/units.ts | 11 ++++ src/device-sync/calendar/upload.ts | 5 +- src/device-sync/calendar/values.ts | 13 ++++- 9 files changed, 150 insertions(+), 7 deletions(-) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index d7d30337..9729c4cf 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -17,6 +17,47 @@ function real(options: Parameters[0] = {}): Harness { type Overrides = Record>; describe('calendar device edits through the engine', () => { + it('creates an event and an occurrence edit an app left without a status as confirmed ones', async () => { + const h = real(); + const series = h.server.addEvent('a', { + uid: 'weekly-uid', + title: 'Weekly', + start: '2026-10-05T08:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'weekly', count: 8 }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const master = h.events().find((e) => e[Events._SYNC_ID] === `a/${series}`)!; + const rowId = Number(master[Events.CALENDAR_ID]); + + // Google Calendar leaves STATUS NULL on the events and exceptions it inserts. + const created = h.device.user.insertEvent(rowId, { + [Events.TITLE]: 'New from Google Calendar', + [Events.DTSTART]: Date.UTC(2026, 9, 14, 13), + [Events.DTEND]: Date.UTC(2026, 9, 14, 14), + [Events.EVENT_TIMEZONE]: 'Europe/Berlin', + [Events.STATUS]: null, + }); + h.device.user.insertException(Number(master._id), Date.UTC(2026, 10, 9, 7), { + [Events.TITLE]: 'Only Nov 9', + [Events.DTSTART]: Date.UTC(2026, 10, 9, 7), + [Events.DTEND]: Date.UTC(2026, 10, 9, 8), + [Events.STATUS]: null, + }); + expect(await h.run(CALENDAR_AUTHORITY)).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 1, updated: 1 } } }); + + const event = h.server.all('CalendarEvent', 'a').find((e) => e.title === 'New from Google Calendar')!; + expect(event.status).toBeUndefined(); + const overrides = h.server.get('CalendarEvent', 'a', series)!.recurrenceOverrides as Overrides; + expect(overrides['2026-11-09T08:00:00']).toMatchObject({ title: 'Only Nov 9' }); + expect(overrides['2026-11-09T08:00:00'].status).toBeUndefined(); + // The rows hold the default, confirmed, and are clean. + expect(h.events().find((e) => Number(e._id) === created)).toMatchObject({ [Events.STATUS]: 1, [Events.DIRTY]: 0 }); + expect(h.events().filter((e) => e[Events.ORIGINAL_SYNC_ID] === `a/${series}`).map((e) => [e[Events.STATUS], e[Events.DIRTY]])).toEqual([[1, 0]]); + }); + it('keeps a description and location cleared on one occurrence cleared', async () => { const h = real(); const id = h.server.addEvent('a', { diff --git a/src/device-sync/__tests__/calendar/exceptions.test.ts b/src/device-sync/__tests__/calendar/exceptions.test.ts index 82007b54..0b35614d 100644 --- a/src/device-sync/__tests__/calendar/exceptions.test.ts +++ b/src/device-sync/__tests__/calendar/exceptions.test.ts @@ -103,6 +103,21 @@ describe('calendar planner: recurrence edits made on the device', () => { expect(h.row(x.eventId)).toBeUndefined(); }); + it('does not make an occurrence an app left without a status (Google Calendar\'s "this event") tentative', async () => { + const event = single({ recurrenceRule: { frequency: 'weekly', count: 5 } }); + const { h, id } = await synced(event); + h.fake.user.insertException(id, utc('2026-10-13T10:00:00Z'), { + [Events.TITLE]: 'Only this Tuesday', [Events.DTSTART]: utc('2026-10-13T10:00:00Z'), [Events.DTEND]: utc('2026-10-13T11:00:00Z'), [Events.STATUS]: null, + }); + const local = (await h.local('e1'))!; + const plan = calendarPlanner.planUpload(local, h.ctx); + const overrides = patchOf(plan).patch.recurrenceOverrides as Record>; + expect(overrides['2026-10-13T12:00:00']).toEqual({ title: 'Only this Tuesday', start: '2026-10-13T12:00:00', duration: 'PT1H' }); + await accept(h, event, local, plan); + const [x] = (await h.local('e1'))!.exceptions; + expect(x.cells).toMatchObject({ [Events.STATUS]: 1, [Events.DIRTY]: 0 }); + }); + it('clears an occurrence\'s own description and location instead of letting the series\' come back', async () => { const event = weekly(); event.description = 'Daily agenda'; diff --git a/src/device-sync/__tests__/calendar/lifecycle.test.ts b/src/device-sync/__tests__/calendar/lifecycle.test.ts index 994f2972..5acc036e 100644 --- a/src/device-sync/__tests__/calendar/lifecycle.test.ts +++ b/src/device-sync/__tests__/calendar/lifecycle.test.ts @@ -100,6 +100,37 @@ describe('calendar planner: new events', () => { expect(calendarPlanner.planDownload(server, after, h.ctx).effect).toBe('none'); }); + it('creates an event an app left without a status (Google Calendar) without making it tentative', async () => { + const h = await new Harness().setup(); + // CalendarProvider has no default for STATUS: an app that does not write it leaves it NULL. + const id = h.fake.user.insertEvent(h.rowIds.get('b')!, { + [Events.TITLE]: 'From Google Calendar', [Events.DTSTART]: utc('2026-10-14T15:00:00Z'), [Events.DTEND]: utc('2026-10-14T16:00:00Z'), [Events.EVENT_TIMEZONE]: 'Europe/Berlin', [Events.STATUS]: null, + }); + const { local, create } = await claimAndCreate(h, id); + expect(create.object).not.toHaveProperty('status'); + const server = { ...(create.object as CalendarEventWire), id: 'g1' } as CalendarEventWire; + await h.apply(calendarPlanner.planAccepted(local, server, h.ctx).ops); + // Written as JSCalendar's default, confirmed. + expect(h.row(id)).toMatchObject({ [Events.STATUS]: 1, [Events.DIRTY]: 0 }); + expect(calendarPlanner.planDownload(server, (await h.local('g1'))!, h.ctx).effect).toBe('none'); + }); + + it('leaves out exclusions a new series inherited from before its start (Etar\'s split copies the EXDATE)', async () => { + const h = await new Harness().setup(); + const id = h.fake.user.insertEvent(h.rowIds.get('b')!, { + [Events.TITLE]: 'Weekly (new time)', + [Events.DTSTART]: utc('2026-11-16T09:00:00Z'), + [Events.DURATION]: 'P3600S', + [Events.RRULE]: 'FREQ=WEEKLY;COUNT=3;BYDAY=MO', + [Events.EVENT_TIMEZONE]: 'Europe/Berlin', + // The old series' exclusions (Oct 12, Nov 2) and one of this series (Nov 23). + [Events.EXDATE]: '20261012T060000Z,20261102T070000Z,20261123T090000Z', + [Events.STATUS]: 1, + }); + const { create } = await claimAndCreate(h, id); + expect(create.object.recurrenceOverrides).toEqual({ '2026-11-23T10:00:00': { excluded: true } }); + }); + it('invites the attendees of a new event as its organizer', async () => { const h = await new Harness().setup(); const id = h.fake.user.insertEvent( @@ -232,6 +263,20 @@ describe('calendar planner: accepted uploads, merges and deletions', () => { expect(h.row(id)).toMatchObject({ [Events.TITLE]: 'Server title', [Events.DIRTY]: 0 }); }); + it('never reads a status an app left NULL as an edit, in uploads or merges', async () => { + const { h, id } = await synced(weekly()); + expect(h.row(id)[Events.STATUS]).toBe(0); + // A row an app wrote without STATUS (only an insert can leave it NULL), then edited. + h.fake.table('events').get(id)![Events.STATUS] = null; + h.fake.user.updateEvent(id, { [Events.TITLE]: 'Renamed' }); + expect(actionsOf(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx))[0]).toMatchObject({ kind: 'update', patch: { title: 'Renamed' } }); + expect(Object.keys((actionsOf(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx))[0] as { patch: object }).patch)).toEqual(['title']); + // The server's status change meanwhile is no conflict: the row takes it, the title stays for the upload. + const plan = await h.download({ ...weekly(), status: 'cancelled' }); + expect(plan).toMatchObject({ conflicts: 0, stillDirty: true }); + expect(h.row(id)).toMatchObject({ [Events.STATUS]: 2, [Events.TITLE]: 'Renamed', [Events.DIRTY]: 1 }); + }); + it('treats the same change on both sides as converged', async () => { const { h, id } = await synced(weekly()); h.fake.user.updateEvent(id, { [Events.TITLE]: 'Same' }); @@ -362,6 +407,17 @@ describe('calendar planner: pairs', () => { expect(pairs.map((p) => p.actions)).toEqual([[{ kind: 'update', id: 'e1', patch: { recurrenceRule: null } }]]); }); + it('pairs a copy an app inserted without a status (Google Calendar\'s "Copy to" + delete) with the original', async () => { + const { h, id } = await synced(single()); + h.fake.user.deleteEvent(id); + h.fake.user.insertEvent(h.rowIds.get('w')!, { + [Events.TITLE]: 'Lunch', [Events.DTSTART]: utc('2026-10-06T10:00:00Z'), [Events.DTEND]: utc('2026-10-06T11:00:00Z'), [Events.EVENT_TIMEZONE]: 'Europe/Berlin', [Events.STATUS]: null, [Events.AVAILABILITY]: 0, [Events.ACCESS_LEVEL]: 0, + }); + const events = await h.events(); + const pairs = calendarPlanner.planPairs(events.filter((e) => e.deleted), events.filter((e) => !e.syncId), h.ctx); + expect(pairs.map((p) => p.actions)).toEqual([[{ kind: 'update', id: 'e1', patch: { 'calendarIds/b': null, 'calendarIds/w': true } }]]); + }); + it('does not pair unrelated rows', async () => { const { h, id } = await synced(single()); h.fake.user.deleteEvent(id); diff --git a/src/device-sync/calendar/create.ts b/src/device-sync/calendar/create.ts index bad4ec4f..e1ed3fa3 100644 --- a/src/device-sync/calendar/create.ts +++ b/src/device-sync/calendar/create.ts @@ -54,9 +54,11 @@ export function computeCreate(local: LocalEvent, uid: string, calendarId: string title: titleValue(cells), start: t.start, duration: t.duration, - status: statusFromDevice(cells[Events.STATUS]), freeBusyStatus: availabilityFromDevice(cells[Events.AVAILABILITY]), }; + // A row without STATUS gets JSCalendar's default, confirmed. + const status = statusFromDevice(cells[Events.STATUS]); + if (status) object.status = status; if (t.showWithoutTime) object.showWithoutTime = true; else object.timeZone = t.timeZone; const description = descriptionValue(cells); @@ -88,8 +90,10 @@ export function computeCreate(local: LocalEvent, uid: string, calendarId: string return key; }; for (const entry of exdateEntries(sideOfRow(local))) { - const key = checked(exdateEntryKey(entry, zone)); - if (key) overrides[key] = { excluded: true }; + const key = exdateEntryKey(entry, zone); + // Etar's "this and following" copies the old series' EXDATE: dates before the start are none of this series. + if (!key || key < t.start) continue; + overrides[checked(key)!] = { excluded: true }; } for (const x of local.exceptions) { const key = checked(x.recurrenceId); diff --git a/src/device-sync/calendar/merge.ts b/src/device-sync/calendar/merge.ts index 53715e5a..dc65b48e 100644 --- a/src/device-sync/calendar/merge.ts +++ b/src/device-sync/calendar/merge.ts @@ -42,6 +42,7 @@ import { attendeesByKey, baselineOfImage, columnUnitDiffers, + deviceChangedUnit, exdateEntries, isLossyEditor, remindersDiffer, @@ -135,7 +136,7 @@ export function mergeRow(input: RowMergeInput): RowMerge { }; for (const unit of input.isException ? EXCEPTION_COLUMN_UNITS : MASTER_COLUMN_UNITS) { - const local = (dirty || forced.has(unit)) && columnUnitDiffers(unit, cur, bl); + const local = (dirty || forced.has(unit)) && deviceChangedUnit(unit, cur, bl); const choice = decide(unit, local, columnUnitDiffers(unit, bs, rm), !columnUnitDiffers(unit, cur, rm)); if (choice === 'local') { for (const c of COLUMN_UNITS[unit]) { diff --git a/src/device-sync/calendar/pairs.ts b/src/device-sync/calendar/pairs.ts index 8f4eeb81..80513bd2 100644 --- a/src/device-sync/calendar/pairs.ts +++ b/src/device-sync/calendar/pairs.ts @@ -21,6 +21,7 @@ import { GroupBuilder, assertRow, deleteRow, updateRow } from './rows'; import { rruleParts } from './rrule'; import { sendsSchedulingMessages } from './scheduling'; import { put } from './upload'; +import { isUnsetStatus } from './values'; /** What must be equal for two rows to be one event (the calendar and the rule aside). */ const PAIR_COLUMNS = [ @@ -58,6 +59,8 @@ export interface PairKind { /** How a deleted and a new row are one edit, or null. */ export function pairKind(deleted: LocalEvent, fresh: LocalEvent): PairKind | null { for (const c of PAIR_COLUMNS) { + // A copy inserted without STATUS (Google Calendar) says nothing about it. + if (c === Events.STATUS && (isUnsetStatus(deleted.cells[c]) || isUnsetStatus(fresh.cells[c]))) continue; if (String(deleted.cells[c] ?? '') !== String(fresh.cells[c] ?? '')) return null; } const allDay = Number(fresh.cells[Events.ALL_DAY] ?? 0) === 1; diff --git a/src/device-sync/calendar/units.ts b/src/device-sync/calendar/units.ts index b397857e..2046f8fa 100644 --- a/src/device-sync/calendar/units.ts +++ b/src/device-sync/calendar/units.ts @@ -12,6 +12,7 @@ import { MASTER_CELLS, EXCEPTION_CELLS, makeBaseline, sameCell } from './columns import { exdateSet } from './exdate'; import { sameReminders } from './reminders'; import type { RowImage } from './image'; +import { isUnsetStatus } from './values'; export const COLUMN_UNITS: Record = { title: [Events.TITLE], @@ -73,6 +74,16 @@ export function columnUnitDiffers(unit: string, a: Side, b: Side): boolean { return (COLUMN_UNITS[unit] ?? []).some((c) => !sameCell(c, a.cells[c], b.cells[c])); } +/** + * Whether the device changed a column unit against what it is measured by + * (the baseline, or the plain instance of a new exception row). A STATUS an + * app left NULL is no change: it sets no status (`statusFromDevice`). + */ +export function deviceChangedUnit(unit: string, current: Side, reference: Side): boolean { + if (unit === 'status' && isUnsetStatus(current.cells[Events.STATUS])) return false; + return columnUnitDiffers(unit, current, reference); +} + /** Attendee rows by address key (the user's aliases are one key). */ export function attendeesByKey(rows: Row[], ctx: SelfContext): Map { const out = new Map(); diff --git a/src/device-sync/calendar/upload.ts b/src/device-sync/calendar/upload.ts index fb877a51..012f611b 100644 --- a/src/device-sync/calendar/upload.ts +++ b/src/device-sync/calendar/upload.ts @@ -58,6 +58,7 @@ import { MASTER_COLUMN_UNITS, baselineOfImage, columnUnitDiffers, + deviceChangedUnit, exdateEntries, isLossyEditor, remindersDiffer, @@ -219,7 +220,7 @@ export function computeUpdate(local: LocalEvent, ctx: CalendarContext): UploadCo const changed = (unit: string) => { if (!bl) return false; const forced = local.split === 'source' && unit === 'rule'; - return (local.dirty || forced) && columnUnitDiffers(unit, cur, bl); + return (local.dirty || forced) && deviceChangedUnit(unit, cur, bl); }; for (const unit of MASTER_COLUMN_UNITS) { @@ -444,7 +445,7 @@ function exceptionChanges( if (unit === 'privacy') continue; // Stalwart drops privacy from overrides. // Etar answers one instance with STATUS_CONFIRMED on it; an attendee may not change an event's status. if (unit === 'status' && !organizer) continue; - if (unit !== 'timing' && !columnUnitDiffers(unit, cur, reference)) continue; + if (unit !== 'timing' && !deviceChangedUnit(unit, cur, reference)) continue; // A new exception row holds what its app copied: an empty value there is one it did not model, not a deletion. if (!existing && INHERITED_WHEN_EMPTY.has(unit) && isEmptyUnit(unit, x.cells)) continue; const cells = x.cells; diff --git a/src/device-sync/calendar/values.ts b/src/device-sync/calendar/values.ts index 402d0639..49449ac9 100644 --- a/src/device-sync/calendar/values.ts +++ b/src/device-sync/calendar/values.ts @@ -27,13 +27,24 @@ export function statusToDevice(status: unknown): number { } } -export function statusFromDevice(value: unknown): 'confirmed' | 'tentative' | 'cancelled' { +/** + * STATUS → `status`; null when the row has none. CalendarProvider has no + * default for STATUS, and apps that do not model it (Google Calendar) leave + * it NULL on the events and exceptions they insert: that sets no status. + */ +export function statusFromDevice(value: unknown): 'confirmed' | 'tentative' | 'cancelled' | null { + if (isUnsetStatus(value)) return null; const n = Number(value); if (n === EventStatus.TENTATIVE) return 'tentative'; if (n === EventStatus.CANCELED) return 'cancelled'; return 'confirmed'; } +/** A STATUS cell an app left NULL (see `statusFromDevice`). */ +export function isUnsetStatus(value: unknown): boolean { + return value === null || value === undefined || value === ''; +} + /** `freeBusyStatus` → AVAILABILITY; JSCalendar's default is busy. */ export function availabilityToDevice(freeBusy: unknown): number { return String(freeBusy ?? '').toLowerCase() === 'free' ? EventAvailability.FREE : EventAvailability.BUSY; From 7736b3b55176fa18e9626957b3c5e1bd88d1ad20 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 06:59:02 +0200 Subject: [PATCH 035/334] fix: keep a reminder changed on one occurrence of a series #34 In a series that uses the calendar's default reminders, a reminder set on one occurrence went into the occurrence's override, but the occurrence still used the defaults it inherits from the series, which ignore an event's own alerts. The phone showed the default reminder again after the sync while the server held the new one. The override now also leaves the defaults (`useDefaultAlerts: false`), so only that occurrence uses its own alerts and the series keeps the calendar's defaults. Stalwart stores the flag of an override with the override (as a JSPROP) and serves it back. Checked with a planner test (an existing override and a new "this event" edit) and a run through the engine and the fake server. --- .../__tests__/calendar/engine.test.ts | 38 ++++++++++++++++++- .../__tests__/calendar/exceptions.test.ts | 35 ++++++++++++++++- src/device-sync/calendar/upload.ts | 3 ++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index 9729c4cf..e24bef26 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -3,7 +3,7 @@ // left them, end to end. Mapping details are the planner's own tests. import { describe, expect, it } from 'vitest'; -import { Events } from '../../android-columns'; +import { Events, Reminders } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; import { CALENDAR_AUTHORITY, createHarness, type Harness } from '../engine/harness'; @@ -58,6 +58,42 @@ describe('calendar device edits through the engine', () => { expect(h.events().filter((e) => e[Events.ORIGINAL_SYNC_ID] === `a/${series}`).map((e) => [e[Events.STATUS], e[Events.DIRTY]])).toEqual([[1, 0]]); }); + it('keeps a reminder changed on one occurrence of a series on the calendar\'s default alerts', async () => { + const h = real(); + h.server.serverUpdate('Calendar', 'a', h.calendar, { + defaultAlertsWithTime: { d1: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT15M' }, action: 'display' } }, + }); + const id = h.server.addEvent('a', { + uid: 'standup-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'daily', count: 5 }, + recurrenceOverrides: { '2026-09-29T09:00:00': { title: 'Standup (late)', start: '2026-09-29T10:00:00' } }, + useDefaultAlerts: true, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const minutes = (eventId: unknown) => h.device.rows('reminders').filter((r) => r[Reminders.EVENT_ID] === Number(eventId)).map((r) => Number(r[Reminders.MINUTES])); + const master = h.events().find((e) => e[Events._SYNC_ID] === `a/${id}`)!; + const exception = () => h.events().find((e) => e[Events.ORIGINAL_SYNC_ID] === `a/${id}`)!; + expect([minutes(master._id), minutes(exception()._id)]).toEqual([[15], [15]]); + + h.device.user.setReminders(Number(exception()._id), [{ minutes: 60 }]); + expect(await h.run(CALENDAR_AUTHORITY)).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); + + expect(exception()[Events.DIRTY]).toBe(0); + expect([minutes(master._id), minutes(exception()._id)]).toEqual([[15], [60]]); + const event = h.server.get('CalendarEvent', 'a', id)!; + expect(event.useDefaultAlerts).toBe(true); + expect((event.recurrenceOverrides as Overrides)['2026-09-29T09:00:00']).toMatchObject({ useDefaultAlerts: false }); + // Its echo writes nothing. + const before = h.batches.log.length; + await h.run(CALENDAR_AUTHORITY); + expect(h.batches.log.slice(before).filter((ops) => ops.some((op) => op.op !== 'syncState' && op.op !== 'assert'))).toEqual([]); + }); + it('keeps a description and location cleared on one occurrence cleared', async () => { const h = real(); const id = h.server.addEvent('a', { diff --git a/src/device-sync/__tests__/calendar/exceptions.test.ts b/src/device-sync/__tests__/calendar/exceptions.test.ts index 0b35614d..7e371a89 100644 --- a/src/device-sync/__tests__/calendar/exceptions.test.ts +++ b/src/device-sync/__tests__/calendar/exceptions.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { Attendees, Events } from '../../android-columns'; +import { Attendees, Events, Reminders } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import type { LocalEvent, UploadPlan } from '../../planner'; import type { CalendarEventWire } from '../../wire'; @@ -161,6 +161,39 @@ describe('calendar planner: recurrence edits made on the device', () => { }); }); + it('keeps a reminder changed on one occurrence of a series that uses the calendar\'s default alerts', async () => { + const event = weekly(); + delete event.alerts; + event.useDefaultAlerts = true; + const { h, id } = await synced(event); + const minutes = (eventId: number) => h.fake.rows('reminders', `${Reminders.EVENT_ID} = ?`, [eventId]).map((r) => Number(r[Reminders.MINUTES])); + const x = (await h.local('bl'))!.exceptions[0]; + // The calendar's default (10 minutes) on the series and on its occurrences. + expect([minutes(id), minutes(x.eventId)]).toEqual([[10], [10]]); + h.fake.user.setReminders(x.eventId, [{ minutes: 60 }]); + // A new occurrence edit with its own reminder, too (Etar "this event"). + const added = h.fake.user.insertException(id, utc('2026-10-14T13:00:00Z'), { + [Events.TITLE]: 'Probe weekly', [Events.DTSTART]: utc('2026-10-14T13:00:00Z'), [Events.DTEND]: utc('2026-10-14T14:30:00Z'), + }); + h.fake.user.setReminders(added, [{ minutes: 5 }]); + const local = (await h.local('bl'))!; + const plan = calendarPlanner.planUpload(local, h.ctx); + const server = await accept(h, event, local, plan); + const after = (await h.local('bl'))!; + expect(after.exceptions.map((e) => [e.recurrenceId, minutes(e.eventId), e.dirty])).toEqual([ + ['2026-10-07T09:00:00', [60], false], + ['2026-10-14T09:00:00', [5], false], + ]); + // The series and its other occurrences keep the calendar's defaults. + expect(server.useDefaultAlerts).toBe(true); + expect(minutes(id)).toEqual([10]); + expect(calendarPlanner.planDownload(server, after, h.ctx).effect).toBe('none'); + // Only the occurrences leave the defaults: `useDefaultAlerts` is a property an override can patch. + const patch = patchOf(plan).patch; + expect(patch['recurrenceOverrides/2026-10-07T09:00:00/useDefaultAlerts']).toBe(false); + expect(patch['recurrenceOverrides/2026-10-14T09:00:00']).toMatchObject({ useDefaultAlerts: false }); + }); + it('patches an existing override one level deep, never deeper', async () => { const { h } = await synced(weekly()); const x = (await h.local('bl'))!.exceptions[0]; diff --git a/src/device-sync/calendar/upload.ts b/src/device-sync/calendar/upload.ts index 012f611b..4476719e 100644 --- a/src/device-sync/calendar/upload.ts +++ b/src/device-sync/calendar/upload.ts @@ -509,6 +509,9 @@ function exceptionChanges( } if (ctx.reminderOwner === 'device' && (existing || cur.reminders!.length) && remindersDiffer(cur, reference)) { values.alerts = reminderChanges(instance, ctx.calendar(calendarId), cur.reminders!, ctx.mintKey).alerts; + // An occurrence on the calendar's defaults ignores its own alerts: it leaves them, as a series does + // (Stalwart keeps the override's `useDefaultAlerts` as a JSPROP; the series stays on the defaults). + if (instance.useDefaultAlerts) values.useDefaultAlerts = false; units.add('reminders'); } From d3262ff387fd38a322575ce07974e6e615a039f6 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 06:59:48 +0200 Subject: [PATCH 036/334] fix: send an answer to one occurrence in an RSVP-only calendar #34 In a calendar the user may only answer (mayRSVP without write rights), an answer to the whole series was uploaded, but an answer to a single occurrence goes into the occurrence's override and counted as an edit: it was undone on the next sync without a word. A change that holds nothing but the user's own answer, to the series or to occurrences, now counts as an RSVP and is uploaded. Stalwart 0.16.23 refuses every update in a shared calendar without write rights, answers included; the engine then puts the rows back from the server and reports the refusal, as it already did for an answer to the series, so the answer is no longer lost silently. Checked with a planner test (a new and an existing override, and an extra edit that is still reverted) and a run through the engine and the fake server. --- .../__tests__/calendar/engine.test.ts | 37 +++++++++++++++++++ .../__tests__/calendar/exceptions.test.ts | 27 ++++++++++++++ src/device-sync/calendar/planner.ts | 5 +-- src/device-sync/calendar/upload.ts | 15 ++++++-- 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index e24bef26..836abbb3 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -94,6 +94,43 @@ describe('calendar device edits through the engine', () => { expect(h.batches.log.slice(before).filter((ops) => ops.some((op) => op.op !== 'syncState' && op.op !== 'assert'))).toEqual([]); }); + it('sends an answer to one occurrence in a calendar that grants only RSVP, and reports a refusal', async () => { + const h = real({ accounts: 'withShared' }); + const team = h.server.addCalendar('team', { + name: 'Team events', + myRights: { mayReadFreeBusy: true, mayReadItems: true, mayWriteAll: false, mayWriteOwn: false, mayUpdatePrivate: false, mayRSVP: true, mayShare: false, mayDelete: false }, + }); + h.prefs.calendarSelection[`team/${team}`] = true; + const id = h.server.addEvent('team', { + uid: 'team-weekly', + title: 'Team weekly', + start: '2026-10-05T09:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'weekly', count: 5 }, + organizerCalendarAddress: 'mailto:boss@example.net', + participants: { + boss: { '@type': 'Participant', calendarAddress: 'mailto:boss@example.net', roles: { owner: true }, participationStatus: 'accepted' }, + me: { '@type': 'Participant', calendarAddress: 'mailto:alice@example.com', roles: { attendee: true }, participationStatus: 'needs-action' }, + }, + calendarIds: { [team]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const master = h.events().find((e) => e[Events._SYNC_ID] === `team/${id}`)!; + + // Etar answers the Oct 12 occurrence through CONTENT_EXCEPTION_URI. + h.device.user.exceptionViaUri(Number(master._id), Date.UTC(2026, 9, 12, 7), { [Events.SELF_ATTENDEE_STATUS]: 2, [Events.STATUS]: 1 }); + const sets = () => h.server.requests.filter((r) => r.methods.includes('CalendarEvent/set')).length; + const before = sets(); + const report = await h.run(CALENDAR_AUTHORITY); + + // The answer was sent (this server takes no RSVP without write rights), the refusal is reported and the row put back. + expect(sets()).toBeGreaterThan(before); + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `team/${id}`, side: 'upload', type: 'forbidden' })]); + expect(h.events().filter((e) => e[Events.ORIGINAL_SYNC_ID] === `team/${id}`)).toEqual([]); + expect(h.events().every((e) => Number(e[Events.DIRTY] ?? 0) === 0)).toBe(true); + }); + it('keeps a description and location cleared on one occurrence cleared', async () => { const h = real(); const id = h.server.addEvent('a', { diff --git a/src/device-sync/__tests__/calendar/exceptions.test.ts b/src/device-sync/__tests__/calendar/exceptions.test.ts index 7e371a89..965bbf38 100644 --- a/src/device-sync/__tests__/calendar/exceptions.test.ts +++ b/src/device-sync/__tests__/calendar/exceptions.test.ts @@ -225,6 +225,33 @@ describe('calendar planner: recurrence edits made on the device', () => { expect(action.sendSchedulingMessages).toBe(true); }); + it('lets an answer to one occurrence through in a calendar that grants only RSVP, and nothing more', async () => { + const invite = weekly(); + invite.calendarIds = { r: true }; + invite.organizerCalendarAddress = 'mailto:boss@example.net'; + invite.participants = { + boss: { calendarAddress: 'mailto:boss@example.net', roles: { owner: true }, participationStatus: 'accepted' }, + me: { calendarAddress: 'mailto:usera@example.org', roles: { attendee: true }, participationStatus: 'needs-action' }, + }; + const { h, id } = await synced(invite); + // Etar answers Oct 19 through CONTENT_EXCEPTION_URI (a new override)... + const added = h.fake.user.exceptionViaUri(id, utc('2026-10-19T13:00:00Z'), { [Events.SELF_ATTENDEE_STATUS]: 2, [Events.STATUS]: 1 }); + // ... and another app answers the existing override of Oct 7 on its row. + const existing = (await h.local('bl'))!.exceptions.find((x) => x.recurrenceId === '2026-10-07T09:00:00')!; + h.fake.table('attendees').forEach((a) => { + if (a.event_id === existing.eventId && a[Attendees.ATTENDEE_EMAIL] === ME) a[Attendees.ATTENDEE_STATUS] = 1; + }); + h.fake.user.updateEvent(existing.eventId, {}); + const action = patchOf(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx)); + type Answer = { participants: Record }; + expect((action.patch['recurrenceOverrides/2026-10-19T09:00:00'] as Answer).participants.me.participationStatus).toBe('declined'); + expect((action.patch['recurrenceOverrides/2026-10-07T09:00:00/participants'] as Answer['participants']).me.participationStatus).toBe('accepted'); + expect(action.sendSchedulingMessages).toBe(true); + // Anything more than the answer is still the calendar owner's to change. + h.fake.user.updateEvent(added, { [Events.TITLE]: 'Mine now' }); + expect(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx).kind).toBe('revert'); + }); + it('turns EXDATE entries an app adds or removes into exclusions and re-inclusions', async () => { const { h, id } = await synced(weekly()); h.fake.user.updateEvent(id, { [Events.EXDATE]: '20261012T130000Z\nAmerica/New_York;20261021T090000' }); diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index 31e11f68..4d6157ed 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -308,9 +308,8 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { return m && computation.patch[k] === true && ctx.isReadOnly(m[1]); }); if ((readOnly && keys.length) || movedToReadOnly) { - // RSVP is the one change a calendar without write rights may take. - const rsvpOnly = keys.length > 0 && keys.every((k) => /^participants\/[^/]+\/participationStatus$/.test(k)); - if (!(rsvpOnly && ctx.calendar(calendar.calendarId)?.myRights?.mayRSVP && !movedToReadOnly)) { + // RSVP, to the series or to occurrences, is the one change a calendar without write rights may take. + if (!(computation.rsvpOnly && ctx.calendar(calendar.calendarId)?.myRights?.mayRSVP && !movedToReadOnly)) { const ops = revertGroup(local, ctx); return ops ? { kind: 'revert', ops } : { kind: 'skip', reason: 'readOnly' }; } diff --git a/src/device-sync/calendar/upload.ts b/src/device-sync/calendar/upload.ts index 4476719e..269924f9 100644 --- a/src/device-sync/calendar/upload.ts +++ b/src/device-sync/calendar/upload.ts @@ -169,6 +169,8 @@ export interface UploadComputation { /** Exception rows whose instance is excluded by the patch, or already was. */ removedExceptions: Set; sendSchedulingMessages: boolean; + /** The patch holds nothing but the user's own answer, to the series or to occurrences (what `mayRSVP` allows). */ + rsvpOnly: boolean; /** Units changed on the device that can't be uploaded (reported when nothing else is). */ dropped: string[]; } @@ -362,6 +364,7 @@ export function computeUpdate(local: LocalEvent, ctx: CalendarContext): UploadCo } // Exception rows. + let answersOnly = true; if (!ruleRemoved && shadow.recurrenceRule) { for (const x of local.exceptions) { const units = exceptionChanges(x, local, shadow, calendarId, zone, overrides, removedExceptions, ctx, lossy); @@ -369,6 +372,7 @@ export function computeUpdate(local: LocalEvent, ctx: CalendarContext): UploadCo exceptionUnits.set(x.eventId, units.units); overridesChanged = true; rsvp = rsvp || units.rsvp; + answersOnly &&= units.rsvp && !units.others && units.units.size === 1 && units.units.has('attendees'); } } } @@ -377,8 +381,9 @@ export function computeUpdate(local: LocalEvent, ctx: CalendarContext): UploadCo const notifying = NOTIFYING_UNITS.some((u) => masterUnits.has(u)) || overridesChanged || attendeeOthers; const sendSchedulingMessages = sendsSchedulingMessages(shadow, { kind: 'update', notifying, rsvp, addsAttendees: attendeeOthers }, ctx); + const rsvpOnly = rsvp && answersOnly && !attendeeOthers && [...masterUnits].every((u) => u.startsWith('attendee:')); - return { patch, masterUnits, exceptionUnits, removedExceptions, sendSchedulingMessages, dropped }; + return { patch, masterUnits, exceptionUnits, removedExceptions, sendSchedulingMessages, dropped, rsvpOnly }; } /** Timing properties that differ from the server's. */ @@ -413,7 +418,7 @@ function exceptionChanges( removed: Set, ctx: CalendarContext, lossy: boolean, -): { units: Set; rsvp: boolean } { +): { units: Set; rsvp: boolean; others?: boolean } { const units = new Set(); const key = x.recurrenceId; if (!key) return { units, rsvp: false }; @@ -439,6 +444,7 @@ function exceptionChanges( const cur = sideOfRow(x); const values: Record = {}; let rsvp = false; + let others = false; const organizer = userIsOrganizer(shadow, ctx); for (const unit of EXCEPTION_COLUMN_UNITS) { @@ -504,6 +510,7 @@ function exceptionChanges( if (result.changes.length) { values.participants = result.participants; rsvp = result.rsvp; + others = result.others; units.add('attendees'); } } @@ -517,12 +524,12 @@ function exceptionChanges( if (existing) { if (units.size) work.props.set(key, { ...(work.props.get(key) ?? {}), ...values }); - return { units, rsvp }; + return { units, rsvp, others }; } // A new override: nothing but its pinned time is still an override (an app saving the instance unchanged). if (!units.size) units.add('new'); work.whole.set(key, newOverride(shadow, key, values)); - return { units, rsvp }; + return { units, rsvp, others }; } /** Units a new exception row inherits from its series when it leaves them empty. */ From 6785276377ab0771a3864f09700cf3cb2d5d2cca Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:00:45 +0200 Subject: [PATCH 037/334] fix: put back an event moved in place to another account's calendar #34 An app that moves an event by changing its calendar in place can move it into a calendar of another account on the device. A patch cannot move an event between accounts, so the move was dropped; with nothing else changed, the row was then marked clean where it stood. The phone showed the event in the other account's calendar while the server kept it in the old one, until the next server change moved it back. The row now goes back to its calendar at once, rewritten from the last server version. Changes made together with the move still upload, and their accepted write puts the row back as before. Checked with a run through the engine and the fake server with a shared account: the event is back in its calendar and clean, and the server and the other account are untouched. --- .../__tests__/calendar/engine.test.ts | 35 +++++++++++++++++++ src/device-sync/calendar/planner.ts | 7 ++++ 2 files changed, 42 insertions(+) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index 836abbb3..76f4c661 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -131,6 +131,41 @@ describe('calendar device edits through the engine', () => { expect(h.events().every((e) => Number(e[Events.DIRTY] ?? 0) === 0)).toBe(true); }); + it('puts an event an app moved in place to a calendar of another account back into its calendar', async () => { + const h = real({ accounts: 'withShared' }); + const team = String(h.server.all('Calendar', 'team')[0].id); + h.prefs.calendarSelection[`team/${team}`] = true; + const id = h.server.addEvent('a', { + uid: 'lunch-uid', + title: 'Lunch', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const calendarRow = (key: string) => Number(h.device.rows('calendars').find((c) => c._sync_id === key)!._id); + const row = () => h.events().find((e) => e[Events._SYNC_ID] === `a/${id}`)!; + expect(row()[Events.CALENDAR_ID]).toBe(calendarRow(`a/${h.calendar}`)); + + // An app moves it by changing CALENDAR_ID in place (a move across JMAP accounts can't be a patch). + h.device.user.updateEvent(Number(row()._id), { [Events.CALENDAR_ID]: calendarRow(`team/${team}`) }); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(row()).toMatchObject({ [Events.CALENDAR_ID]: calendarRow(`a/${h.calendar}`), [Events.DIRTY]: 0 }); + expect(h.server.get('CalendarEvent', 'a', id)!.calendarIds).toEqual({ [h.calendar]: true }); + expect(h.server.all('CalendarEvent', 'team')).toEqual([]); + // Nothing was sent; the change counts as skipped (a report entry needs the engine to report reverts). + expect(report).toMatchObject({ outcome: 'ok', stats: { uploaded: { created: 0, updated: 0, deleted: 0 }, skipped: 1 } }); + + // Together with another edit: that one uploads, and the row goes back all the same. + h.device.user.updateEvent(Number(row()._id), { [Events.CALENDAR_ID]: calendarRow(`team/${team}`), [Events.TITLE]: 'Team lunch' }); + expect(await h.run(CALENDAR_AUTHORITY)).toMatchObject({ outcome: 'ok', stats: { uploaded: { updated: 1 } } }); + expect(h.server.get('CalendarEvent', 'a', id)).toMatchObject({ title: 'Team lunch', calendarIds: { [h.calendar]: true } }); + expect(row()).toMatchObject({ [Events.CALENDAR_ID]: calendarRow(`a/${h.calendar}`), [Events.TITLE]: 'Team lunch', [Events.DIRTY]: 0 }); + expect(h.server.all('CalendarEvent', 'team')).toEqual([]); + }); + it('keeps a description and location cleared on one occurrence cleared', async () => { const h = real(); const id = h.server.addEvent('a', { diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index 4d6157ed..abda5068 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -316,6 +316,13 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { } if (!keys.length) { if (computation.dropped.some((u) => u === 'timing' || u === 'rule')) return { kind: 'skip', reason: SKIP.ruleNotRepresentable }; + // A move into a calendar of another JMAP account is no patch (only new rows are created across accounts): + // the row goes back to its calendar instead of staying there unsynced. With other changes, their accepted + // write puts it back. + if (computation.dropped.includes('calendar')) { + const ops = revertGroup(local, ctx); + if (ops) return { kind: 'revert', ops }; + } return { kind: 'clean', ops: cleanGroup(local, computation) }; } const action: UploadAction = { kind: 'update', id: ref.id, patch: computation.patch }; From c777dc4089c13911ddcec473ff4ce6310db607ef Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:01:24 +0200 Subject: [PATCH 038/334] fix: keep syncing a big meeting after it was edited on the phone #34 After an edit on the phone, every write into a recurring event asserted each attendee and reminder row of every occurrence. For a meeting with 25 attendees and 52 occurrences with details of their own that alone made the write 1.6 MB, over Android's 1 MB transaction limit: the event could no longer be written, went stale in every run and kept its edit. Only rows that are dirty themselves now assert their attendees and reminders. A clean row's DIRTY flag already catches an app edit of them, since CalendarProvider sets it on every app write to an event, its attendees or reminders, so such a write still fails and is planned again. The same meeting's writes now take about 0.65 MB. Checked with the meeting above through the planner, and with an app edit of a clean occurrence's attendees between planning and writing. --- .../__tests__/calendar/big-events.test.ts | 90 +++++++++++++++++++ src/device-sync/calendar/merge.ts | 11 +-- src/device-sync/calendar/planner.ts | 5 +- 3 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 src/device-sync/__tests__/calendar/big-events.test.ts diff --git a/src/device-sync/__tests__/calendar/big-events.test.ts b/src/device-sync/__tests__/calendar/big-events.test.ts new file mode 100644 index 00000000..d367ab0f --- /dev/null +++ b/src/device-sync/__tests__/calendar/big-events.test.ts @@ -0,0 +1,90 @@ +// Big meetings (many attendees on many overrides): a write into such an event +// has to fit one provider transaction (Binder: about 1 MB; a Parcel carries +// the JSON as UTF-16, estimated at 2 bytes per character plus ~300 per op). +import { describe, expect, it } from 'vitest'; +import { Attendees, Events } from '../../android-columns'; +import { calendarPlanner } from '../../calendar/planner'; +import type { OpGroup } from '../../planner'; +import type { CalendarEventWire } from '../../wire'; +import { Harness, ME, serverApply } from './harness'; + +const TRANSACTION = 1_000_000; +const estimate = (group: OpGroup) => 2 * JSON.stringify(group.ops).length + 300 * group.ops.length; + +/** A daily meeting with `attendees` guests whose `overrides` occurrences each carry the whole participants map. */ +function meeting(attendees: number, overrides: number): CalendarEventWire { + const participants: Record = { + me: { '@type': 'Participant', calendarAddress: `mailto:${ME}`, roles: { owner: true, attendee: true }, participationStatus: 'accepted' }, + }; + for (let i = 0; i < attendees; i++) { + participants[`p${i}`] = { '@type': 'Participant', calendarAddress: `mailto:person${i}@example.com`, name: `Person ${i}`, roles: { attendee: true }, participationStatus: 'needs-action', expectReply: true }; + } + const recurrenceOverrides: Record = {}; + for (let i = 0; i < overrides; i++) { + const day = new Date(Date.UTC(2026, 9, 6) + i * 86_400_000).toISOString().slice(0, 10); + recurrenceOverrides[`${day}T09:00:00`] = { title: `Standup #${i}`, participants }; + } + return { + id: 'big', + '@type': 'Event', + uid: 'big@example.org', + calendarIds: { b: true }, + title: 'Standup', + description: 'Daily standup', + start: '2026-10-05T09:00:00', + timeZone: 'Europe/Berlin', + duration: 'PT15M', + recurrenceRule: { frequency: 'daily', count: 300 }, + organizerCalendarAddress: `mailto:${ME}`, + participants, + recurrenceOverrides, + } as unknown as CalendarEventWire; +} + +async function synced(event: CalendarEventWire) { + const h = await new Harness().setup(); + await h.download(event); + return { h, id: Number(h.masterRow(event.id)._id) }; +} + +describe('calendar planner: big meetings', () => { + it('fits the writes after an edit of a big meeting into one transaction', async () => { + const event = meeting(25, 52); + const { h, id } = await synced(event); + // Only the series row is edited; its 52 occurrences with 26 attendees each stay clean. + h.fake.user.updateEvent(id, { [Events.TITLE]: 'Daily standup' }); + const local = (await h.local('big'))!; + + const plan = calendarPlanner.planUpload(local, h.ctx); + if (plan.kind !== 'upload' || plan.actions[0].kind !== 'update') throw new Error(plan.kind); + const server = serverApply(event, plan.actions[0].patch); + const accepted = calendarPlanner.planAccepted(local, server, h.ctx); + expect(estimate(accepted.ops)).toBeLessThan(TRANSACTION); + expect(estimate(accepted.keepDirtyOps)).toBeLessThan(TRANSACTION); + + // A download into the edited event (the server changed one occurrence meanwhile). + const remote = { ...event, recurrenceOverrides: { ...event.recurrenceOverrides, '2026-10-09T09:00:00': { title: 'Moved to Friday' } } } as CalendarEventWire; + expect(estimate(calendarPlanner.planDownload(remote, local, h.ctx).ops)).toBeLessThan(TRANSACTION); + + // A save that changed nothing mapped is cleared the same way. + h.fake.user.updateEvent(id, { [Events.TITLE]: 'Standup' }); + const clean = calendarPlanner.planUpload((await h.local('big'))!, h.ctx); + expect(clean.kind).toBe('clean'); + if (clean.kind === 'clean') expect(estimate(clean.ops)).toBeLessThan(TRANSACTION); + }); + + it('still fails a write when an app changed a clean occurrence\'s attendees meanwhile', async () => { + const event = meeting(3, 4); + const { h, id } = await synced(event); + h.fake.user.updateEvent(id, { [Events.TITLE]: 'Daily standup' }); + const local = (await h.local('big'))!; + const x = local.exceptions[0]; + const plan = calendarPlanner.planUpload(local, h.ctx); + if (plan.kind !== 'upload' || plan.actions[0].kind !== 'update') throw new Error(plan.kind); + const accepted = calendarPlanner.planAccepted(local, serverApply(event, plan.actions[0].patch), h.ctx); + // The occurrence's attendees are not asserted one by one: CalendarProvider marks the event DIRTY on any app write to them. + h.fake.beforeNextBatch(() => h.fake.user.setAttendeeStatus(x.eventId, 'person1@example.com', 1)); + expect(await h.tryApply(accepted.ops)).toMatchObject({ ok: false, reason: 'assert' }); + expect(h.fake.rows('attendees', `${Attendees.EVENT_ID} = ? AND ${Attendees.ATTENDEE_EMAIL} = ?`, [x.eventId, 'person1@example.com'])[0][Attendees.ATTENDEE_STATUS]).toBe(1); + }); +}); diff --git a/src/device-sync/calendar/merge.ts b/src/device-sync/calendar/merge.ts index dc65b48e..eadd6b71 100644 --- a/src/device-sync/calendar/merge.ts +++ b/src/device-sync/calendar/merge.ts @@ -15,7 +15,8 @@ * * Every write into an existing item is guarded: the group first asserts * DIRTY and the projection the plan was made from (with attendees and - * reminders when the item is dirty) and the number of exception rows. + * reminders for the rows that are dirty: a clean row's DIRTY catches any app + * edit of them) and the number of exception rows. */ import { Events } from '../android-columns'; import type { CalendarContext, EventBaseline, LocalEvent, LocalEventRow, LocalException } from '../planner'; @@ -284,11 +285,11 @@ export function writeEvent( const baseTarget = options.base ? { calendarRowId: local.calendarRowId, calendarId: ctx.calendarIdOfRow(local.calendarRowId)?.calendarId ?? target.calendarId } : target; const baseImage = options.base ? eventImage(baseEvent, baseTarget.calendarRowId, baseTarget.calendarId, ctx) ?? image : image; const lossy = isLossyEditor(local.mutators); - const itemDirty = local.dirty || local.exceptions.some((x) => x.dirty || x.deleted); - // Asserts first: the rows as read. - assertRow(group, local, false, itemDirty); - for (const x of local.exceptions) assertRow(group, x, true, itemDirty); + // Asserts first: the rows as read. Attendees and reminders only for dirty rows (see `assertRow`): a big + // meeting's occurrences would otherwise assert every attendee and outgrow one provider transaction. + assertRow(group, local, false, local.dirty || local.deleted); + for (const x of local.exceptions) assertRow(group, x, true, x.dirty || x.deleted); assertExceptionCount(group, local.eventId, linkedExceptionCount(local)); let conflicts = 0; diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index abda5068..08a2ac15 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -223,8 +223,9 @@ function revertGroup(local: LocalEvent, ctx: CalendarContext): OpGroup | null { /** Clears DIRTY where nothing uploadable changed, taking the rows as they are as the new baselines. */ function cleanGroup(local: LocalEvent, computation: UploadComputation | null): OpGroup { const group = new GroupBuilder(refOf(local)); - assertRow(group, local, false, true); - for (const x of local.exceptions) assertRow(group, x, true, true); + // Attendees and reminders of the dirty rows only (see `assertRow`), so a big meeting fits one transaction. + assertRow(group, local, false, local.dirty || local.deleted); + for (const x of local.exceptions) assertRow(group, x, true, x.dirty || x.deleted); const settle = (row: LocalEventRow, isException: boolean) => { if (!row.dirty) return; const truncated = isTruncatedBaseline(row.baseline?.cells[Events.DESCRIPTION]); From 2b9d19521fcd85f2e6c83c869f443c43eff8b3b0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:12:12 +0200 Subject: [PATCH 039/334] fix: keep sync on when it is turned back on during a turn-off #34 Turning device sync off uploads what the device changed and then deletes --- .../__tests__/app/lifecycle.test.ts | 20 +++++++++++ src/device-sync/app/lifecycle.ts | 36 +++++++++++++++++-- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/device-sync/__tests__/app/lifecycle.test.ts b/src/device-sync/__tests__/app/lifecycle.test.ts index d328abe6..019dd5c9 100644 --- a/src/device-sync/__tests__/app/lifecycle.test.ts +++ b/src/device-sync/__tests__/app/lifecycle.test.ts @@ -260,6 +260,26 @@ describe('disable', () => { expect(stored.reminderOwner).toBeNull(); }); + it('lets a turn-on wait for a turn-off that is still tearing down, instead of being undone by it', async () => { + await disableDeviceSync(ALICE, CALENDAR_AUTHORITY); + let finishTeardown: () => void = () => undefined; + teardown.mockImplementationOnce(async (_registryId, name, authority, options) => { + h.state.log.push(`teardown ${name} ${authority} ${JSON.stringify(options ?? {})}`); + await new Promise((resolve) => { finishTeardown = resolve; }); + return { pending: 0 }; + }); + const turningOff = disableDeviceSync(ALICE, CONTACTS_AUTHORITY); + await vi.waitFor(() => expect(h.state.log).toContain(`teardown alice ${CONTACTS_AUTHORITY} {"uploadFirst":true,"timeoutMs":${TEARDOWN_TIMEOUT_MS}}`)); + // The user opens the settings again and turns contacts sync back on. + const turningOn = enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + finishTeardown(); + expect(await turningOff).toEqual({ done: true, pending: 0 }); + expect(await turningOn).toMatchObject({ kind: 'enabled' }); + expect(h.find('alice')?.sync[CONTACTS_AUTHORITY]?.automatic).toBe(true); + expect(accountDeviceSync(ALICE).enabled?.[CONTACTS_AUTHORITY]).toBe(true); + expect(accountDeviceSync(ALICE).androidAccountName).toBe('alice'); + }); + it('removes the Android account once both authorities are off', async () => { await disableDeviceSync(ALICE, CALENDAR_AUTHORITY); await disableDeviceSync(ALICE, CONTACTS_AUTHORITY); diff --git a/src/device-sync/app/lifecycle.ts b/src/device-sync/app/lifecycle.ts index f87e9397..91fd5364 100644 --- a/src/device-sync/app/lifecycle.ts +++ b/src/device-sync/app/lifecycle.ts @@ -162,6 +162,24 @@ async function refreshPushState(registryId: string): Promise { await refreshPushSubscriptionTypes(registryId).catch(noop); } +// ─── One step at a time ──────────────────────────────── + +// Turning sync on and off for one app account runs one step after the other: +// a turn-on that starts while a turn-off still tears down (the user left the +// settings and came back) would otherwise be undone by it, down to the +// Android account being removed right after it was turned on again. +const accountSteps = new Map>(); + +function oneAtATime(registryId: string, step: () => Promise): Promise { + const next = (accountSteps.get(registryId) ?? Promise.resolve()).catch(noop).then(step); + const settled = next.catch(noop); + accountSteps.set(registryId, settled); + void settled.then(() => { + if (accountSteps.get(registryId) === settled) accountSteps.delete(registryId); + }); + return next; +} + // ─── Enable ──────────────────────────────────────────── export type EnableOutcome = @@ -174,7 +192,11 @@ export type EnableOutcome = * Turns device sync on for one authority of an app account. The caller has * asked for the permissions (and, for calendars, who reminds the user). */ -export async function enableDeviceSync(registryId: string, authority: Authority): Promise { +export function enableDeviceSync(registryId: string, authority: Authority): Promise { + return oneAtATime(registryId, () => enable(registryId, authority)); +} + +async function enable(registryId: string, authority: Authority): Promise { if (!deviceSyncAvailable()) return { kind: 'failed', message: 'Device sync is not available' }; if (!(await hasSyncPermissions(authority))) return { kind: 'permission' }; await waitForDeviceSyncHydration(); @@ -207,7 +229,11 @@ export async function enableDeviceSync(registryId: string, authority: Authority) } /** Automatic sync back on after the user kept syncing instead of losing changes. */ -export async function resumeDeviceSync(registryId: string, authority: Authority): Promise { +export function resumeDeviceSync(registryId: string, authority: Authority): Promise { + return oneAtATime(registryId, () => resume(registryId, authority)); +} + +async function resume(registryId: string, authority: Authority): Promise { if (!deviceSyncAvailable()) return; await waitForDeviceSyncHydration(); const accountName = accountDeviceSync(registryId).androidAccountName ?? (await androidAccountOf(registryId))?.name; @@ -285,11 +311,15 @@ function withoutAuthorityStates(entry: AccountDeviceSync, authority: Authority): * how many changes are waiting: call again with `force` once the user agreed * to lose them, or `resumeDeviceSync` to keep syncing. */ -export async function disableDeviceSync( +export function disableDeviceSync( registryId: string, authority: Authority, options: { force?: boolean } = {}, ): Promise { + return oneAtATime(registryId, () => disable(registryId, authority, options)); +} + +async function disable(registryId: string, authority: Authority, options: { force?: boolean }): Promise { if (!deviceSyncAvailable()) return { done: true, pending: 0 }; await waitForDeviceSyncHydration(); const store = useDeviceSyncStore.getState(); From 7a53cd5d84ba90ccf0a608cabf00486d94f76d7b Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:13:08 +0200 Subject: [PATCH 040/334] fix: let the user delete or restore held-back deletions from the sync settings #34 When a sync held back many deletions made on the device (Android's --- locales/rn/en.json | 4 ++++ .../device-sync/DeviceSyncAccountRow.tsx | 23 ++++++++++++++++++- .../__tests__/app/lifecycle.test.ts | 14 +++++++++++ src/device-sync/app/lifecycle.ts | 18 ++++++++++++++- 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/locales/rn/en.json b/locales/rn/en.json index ff5d09ef..dff3fc6b 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -646,6 +646,10 @@ "choose_calendars_title": "Calendars to sync", "collections_count": "{selected} of {total} sync", "continue": "Continue", + "deletions_delete": "Delete on server", + "deletions_message": "Many items were deleted on this device at once, so they were not deleted on the server. Delete them there as well, or bring them back on this device.", + "deletions_restore": "Bring them back", + "deletions_title": "Delete them on the server too?", "denied_calendar": "Without access to calendars, Bulwark cannot put your calendars on this device.", "denied_contacts": "Without access to contacts, Bulwark cannot put your address books on this device.", "denied_title": "Sync stays off", diff --git a/src/components/settings/device-sync/DeviceSyncAccountRow.tsx b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx index dc3d68c3..a2b35c30 100644 --- a/src/components/settings/device-sync/DeviceSyncAccountRow.tsx +++ b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx @@ -29,6 +29,7 @@ import { disableDeviceSync, enableDeviceSync, openAndroidAccountSettings, + resolveDeletions, resumeDeviceSync, syncNow, } from '../../../device-sync/app/lifecycle'; @@ -224,10 +225,30 @@ export function DeviceSyncAccountRow({ account, authority }: Props) { void refresh(); }; + const reviewDeletions = () => { + const resolve = (choice: 'delete' | 'restore') => { + void resolveDeletions(account.id, authority, choice) + .catch((err: unknown) => alertFailed(err instanceof Error ? err.message : String(err))) + .finally(() => { void refresh(); }); + }; + Alert.alert( + t('settings.device_sync.deletions_title', 'Delete them on the server too?'), + t( + 'settings.device_sync.deletions_message', + 'Many items were deleted on this device at once, so they were not deleted on the server. Delete them there as well, or bring them back on this device.', + ), + [ + { text: t('common.cancel', 'Cancel'), style: 'cancel' }, + { text: t('settings.device_sync.deletions_restore', 'Bring them back'), onPress: () => resolve('restore') }, + { text: t('settings.device_sync.deletions_delete', 'Delete on server'), style: 'destructive', onPress: () => resolve('delete') }, + ], + ); + }; + const runAction = (action: StatusAction) => { if (action === 'signIn') navigation.navigate('AddAccount'); else if (action === 'grantAccess') void grantAccess(); - else void openAndroidAccountSettings(account.id); + else reviewDeletions(); }; const closeChooser = (changed: boolean) => { diff --git a/src/device-sync/__tests__/app/lifecycle.test.ts b/src/device-sync/__tests__/app/lifecycle.test.ts index 019dd5c9..c61d4a85 100644 --- a/src/device-sync/__tests__/app/lifecycle.test.ts +++ b/src/device-sync/__tests__/app/lifecycle.test.ts @@ -108,6 +108,7 @@ import { reconcileDeviceAccounts, releaseDeviceSyncBeforeSignOut, removeSuspendedAccount, + resolveDeletions, resumeDeviceSync, TEARDOWN_TIMEOUT_MS, } from '../../app/lifecycle'; @@ -483,6 +484,19 @@ describe('sign-in', () => { }); describe('settings actions', () => { + it('deletes held-back deletions on the server, or brings the items back, with a sync', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + h.state.log = []; + expect(await resolveDeletions(ALICE, CONTACTS_AUTHORITY, 'delete')).toBe(true); + expect(await resolveDeletions(ALICE, CONTACTS_AUTHORITY, 'restore')).toBe(true); + expect(h.state.log).toEqual([ + `sync alice ${CONTACTS_AUTHORITY} {"manual":true,"overrideTooManyDeletions":true}`, + `sync alice ${CONTACTS_AUTHORITY} {"manual":true,"discardLocalDeletions":true}`, + ]); + // Without an Android account there is nothing to resolve. + expect(await resolveDeletions(BOB, CONTACTS_AUTHORITY, 'delete')).toBe(false); + }); + it('applies a new interval to Android while the authority syncs', async () => { await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); h.state.log = []; diff --git a/src/device-sync/app/lifecycle.ts b/src/device-sync/app/lifecycle.ts index 91fd5364..e873595e 100644 --- a/src/device-sync/app/lifecycle.ts +++ b/src/device-sync/app/lifecycle.ts @@ -623,7 +623,23 @@ export async function syncNow(registryId: string, authority: Authority): Promise return true; } -/** Android's sync screen of the account ("Android account settings", "Review deletions"). */ +/** + * After a run held back many deletions made on the device + * (`tooManyDeletions`): delete those items on the server too, or bring them + * back on the device from the server. Android's own screens offer neither. + */ +export async function resolveDeletions(registryId: string, authority: Authority, choice: 'delete' | 'restore'): Promise { + const accountName = accountDeviceSync(registryId).androidAccountName; + if (!deviceSyncAvailable() || !accountName) return false; + await requestSync( + accountName, + authority, + choice === 'delete' ? { manual: true, overrideTooManyDeletions: true } : { manual: true, discardLocalDeletions: true }, + ); + return true; +} + +/** Android's sync screen of the account ("Android account settings"). */ export async function openAndroidAccountSettings(registryId: string): Promise { const accountName = accountDeviceSync(registryId).androidAccountName; if (!deviceSyncAvailable() || !accountName) return false; From 13264d64791a95375744f4effe42724314f8fb76 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:14:50 +0200 Subject: [PATCH 041/334] fix: keep contacts the server also filed in unsynced books when deleted on the device #34 A contact or group deleted on the device was uploaded against the shadow of its last sync: a download into a deleted item wrote nothing. When the server had meanwhile also filed the card in an address book this device does not sync, the deletion still saw only the synced book and destroyed the card, its copy in the other book included. A download into a deleted contact or group now writes the new server card into its shadow, and takes over an identity that was never written, so deleting our own lost create goes by id rather than by a uid lookup. The rows and the group title stay as they are: the device delete still wins. The upload then removes only the synced memberships of a card that is also in other books, as the calendar planner already does for events. Checked with planner tests for contacts and groups and end to end through the engine with the real planner: the card stays in the unsynced book, the device rows are purged, and an echo writes nothing. --- .../__tests__/contacts/groups.test.ts | 15 ++++++ .../__tests__/contacts/merge.test.ts | 13 +++-- .../__tests__/contacts/real-engine.test.ts | 54 +++++++++++++++++++ .../__tests__/contacts/upload-editors.test.ts | 30 +++++++++++ src/device-sync/contacts/download.ts | 15 +++++- src/device-sync/contacts/groups.ts | 8 ++- 6 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 src/device-sync/__tests__/contacts/real-engine.test.ts diff --git a/src/device-sync/__tests__/contacts/groups.test.ts b/src/device-sync/__tests__/contacts/groups.test.ts index 74ffb7dd..f29efe9b 100644 --- a/src/device-sync/__tests__/contacts/groups.test.ts +++ b/src/device-sync/__tests__/contacts/groups.test.ts @@ -183,6 +183,21 @@ describe('contacts groups: device edits of groups', () => { }); }); + it('only leaves the synced book when the server filed the group in an unsynced one after the device deleted it', async () => { + const { h, gid } = await withGroup(); + const archive = h.server.addAddressBook(JMAP, { name: 'Archive' }); + h.unselected.add(`${JMAP}/${archive}`); + h.device.user.deleteGroup((await localGroup(h, gid)).groupId); + h.server.serverUpdate('ContactCard', JMAP, gid, { [`addressBookIds/${archive}`]: true, 'name/full': 'Old friends' }); + const plan = h.planner.planGroupDownload(h.card(gid), await localGroup(h, gid), h.ctx); + await h.applyOk(plan.ops); + // The delete wins over the rename; only the shadow follows the card. + expect(await localGroup(h, gid)).toMatchObject({ deleted: true, title: 'Friends' }); + expect(h.planner.planGroupUpload(await localGroup(h, gid), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id: gid, patch: { [`addressBookIds/${h.book}`]: null } }], + }); + }); + it('reverts a rename of a read-only group', async () => { const { h, gid } = await withGroup(); h.readOnlyBooks.add(h.book); diff --git a/src/device-sync/__tests__/contacts/merge.test.ts b/src/device-sync/__tests__/contacts/merge.test.ts index 709f3d92..2d07db5a 100644 --- a/src/device-sync/__tests__/contacts/merge.test.ts +++ b/src/device-sync/__tests__/contacts/merge.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { Data, MimeType } from '../../android-columns'; +import { Data, MimeType, RawContacts } from '../../android-columns'; import type { ContactCardWire } from '../../planner'; import { appleCard, googleCard } from './fixtures'; import { Harness, JMAP } from './harness'; @@ -139,13 +139,20 @@ describe('contacts merge: a dirty contact meets a new server version', () => { expect(typeof patch === 'object' && Object.keys(patch).some((k) => k.startsWith('nicknames'))).toBe(false); }); - it('leaves a contact deleted on the device alone: the delete wins and uploads', async () => { + it('leaves the rows of a contact deleted on the device alone: the delete wins and uploads', async () => { const h = new Harness(); const { id, rawId } = await h.seed(appleCard()); h.device.user.deleteContact(rawId); h.server.serverUpdate('ContactCard', JMAP, id, { 'emails/k1/address': 'x@example.com' }); const plan = h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx); - expect(plan).toMatchObject({ effect: 'none', stillDirty: true, ops: { ops: [] } }); + // Only the shadow follows the card, for the deletion's upload. + expect(plan).toMatchObject({ effect: 'update', stillDirty: true, conflicts: 0 }); + expect(plan.ops.ops.filter((o) => o.op !== 'assert')).toEqual([{ + op: 'update', table: 'raw_contacts', id: rawId, expectCount: 1, + values: { [RawContacts.SYNC2]: expect.any(String) }, + }]); + await h.applyOk(plan.ops); + expect(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx)).toMatchObject({ effect: 'none', ops: { ops: [] } }); expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toMatchObject({ kind: 'upload', actions: [{ kind: 'destroy', id }] }); }); diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts new file mode 100644 index 00000000..f42ba6ae --- /dev/null +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -0,0 +1,54 @@ +// The contacts planner driven by the engine (fake providers, fake Stalwart): +// cases that only show end to end, over several runs. The planner's rules +// themselves are tested in the other files of this folder. + +import { describe, expect, it } from 'vitest'; +import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { addServerCards, createHarness, type Harness } from '../engine/harness'; + +function real(): Harness { + const h = createHarness(); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + return h; +} + +/** An address book of the personal account that this device does not sync. */ +function unsyncedBook(h: Harness, name = 'Archive'): string { + const id = h.server.addAddressBook('a', { name }); + h.prefs.contactsSelection[`a/${id}`] = false; + return id; +} + +describe('contacts through the engine', () => { + it('keeps a card deleted on the device that the server meanwhile also filed in an unsynced book', async () => { + const h = real(); + const archive = unsyncedBook(h); + const [ada] = addServerCards(h, ['Ada Lovelace']); + expect((await h.run()).outcome).toBe('ok'); + + h.device.user.deleteContact(h.contactNamed('Ada Lovelace')!.id); + h.server.serverUpdate('ContactCard', 'a', ada, { [`addressBookIds/${archive}`]: true }); + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { deleted: 1 } } }); + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ addressBookIds: { [archive]: true } }); + expect(h.contacts()).toEqual([]); + }); + + it('keeps a group deleted on the device that the server meanwhile also filed in an unsynced book', async () => { + const h = real(); + const archive = unsyncedBook(h); + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, addressBookIds: { [h.book]: true } }); + expect((await h.run()).outcome).toBe('ok'); + + const row = h.device.rows('groups').find((g) => g.sourceid === `a/${group}`)!; + h.device.user.deleteGroup(Number(row._id)); + h.server.serverUpdate('ContactCard', 'a', group, { [`addressBookIds/${archive}`]: true }); + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { deleted: 1 } } }); + expect(h.server.get('ContactCard', 'a', group)).toMatchObject({ addressBookIds: { [archive]: true } }); + expect(h.device.rows('groups')).toEqual([]); + }); +}); diff --git a/src/device-sync/__tests__/contacts/upload-editors.test.ts b/src/device-sync/__tests__/contacts/upload-editors.test.ts index 9bf3fe16..0fcc6a4c 100644 --- a/src/device-sync/__tests__/contacts/upload-editors.test.ts +++ b/src/device-sync/__tests__/contacts/upload-editors.test.ts @@ -309,6 +309,36 @@ describe('contacts upload: deleted contacts', () => { expect(await patchOf(h, rawId)).toEqual({ [`addressBookIds/${h.book}`]: null }); }); + it('only leaves the synced book when the server filed the card in an unsynced one after the device deleted it', async () => { + const h = new Harness(); + const archive = h.server.addAddressBook(JMAP, { name: 'Archive' }); + h.unselected.add(`${JMAP}/${archive}`); + const { id, rawId } = await h.seed(appleCard()); + h.device.user.deleteContact(rawId); + h.server.serverUpdate('ContactCard', JMAP, id, { [`addressBookIds/${archive}`]: true, 'emails/k1/address': 'new@example.com' }); + const rows = JSON.stringify(h.device.rows('data', 'raw_contact_id = ?', [rawId])); + await h.applyOk(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).ops); + // The delete wins over the server's edit: the rows stay as they were, only the shadow follows. + expect(JSON.stringify(h.device.rows('data', 'raw_contact_id = ?', [rawId]))).toBe(rows); + expect(await uploadPlan(h, rawId)).toEqual({ kind: 'upload', actions: [{ kind: 'update', id, patch: { [`addressBookIds/${h.book}`]: null } }] }); + await h.upload(rawId); + expect(h.server.get('ContactCard', JMAP, id)).toMatchObject({ addressBookIds: { [archive]: true } }); + expect(h.device.row('raw_contacts', rawId)).toBeUndefined(); + }); + + it('deletes its own create by id once the download found it, not by a uid lookup', async () => { + const h = new Harness(); + const rawId = h.device.user.insertContact(ACCOUNT, [{ [Data.MIMETYPE]: MimeType.NOTE, data1: 'y' }]); + const plan = await h.planUpload(rawId); + if (plan.kind !== 'upload') throw new Error(plan.kind); + // The create went through but its response was lost; then the user deleted the contact. + const id = h.send(plan.actions)!; + h.device.user.deleteContact(rawId); + await h.applyOk(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).ops); + expect(await h.contact(rawId)).toMatchObject({ sourceId: `${JMAP}/${id}`, pending: null, deleted: true }); + expect(await uploadPlan(h, rawId)).toEqual({ kind: 'upload', actions: [{ kind: 'destroy', id, uid: h.card(id).uid }] }); + }); + it('purges a new contact that never reached the server, and destroys a claimed one by uid', async () => { const h = new Harness(); const fresh = h.device.user.insertContact(ACCOUNT, [{ [Data.MIMETYPE]: MimeType.NOTE, data1: 'x' }]); diff --git a/src/device-sync/contacts/download.ts b/src/device-sync/contacts/download.ts index e6c0eafb..066605f6 100644 --- a/src/device-sync/contacts/download.ts +++ b/src/device-sync/contacts/download.ts @@ -288,8 +288,19 @@ export function planContactDownload(card: ContactCardWire, local: LocalContact | const identity = objectRef(ctx.jmapAccountId, server.id); if (!local) return insertPlan(server, ctx); const none = (stillDirty: boolean): DownloadPlan => ({ ops: { ref: identity, ops: [] }, conflicts: 0, stillDirty, effect: 'none', writes: 0 }); - // Deleted on the device: the delete wins and uploads as a destroy. - if (local.deleted) return none(true); + if (local.deleted) { + // Deleted on the device: the delete wins over a server edit, so the rows stay as they are. + // Only the shadow (and an identity never written) follows the card: the deletion uploads + // against the card as it is now, which may also sit in books this device does not sync. + const shadow = withMemberOf(shadowOf(server), shadowMemberOf(local.shadow)); + const raw = changedRawColumns(local, { + ...(local.sourceId ? {} : { [RawContacts.SOURCE_ID]: identity, [RawContacts.SYNC3]: null }), + [RawContacts.SYNC2]: JSON.stringify(shadow), + }); + if (!Object.keys(raw).length) return none(true); + const ops = [assertContact(local, local.dirty), updateRawContact(local.rawContactId, raw)]; + return { ops: { ref: identity, ops }, conflicts: 0, stillDirty: true, effect: 'update', writes: 1 }; + } if (!local.sourceId) { // Our own create whose identity was never written: adopt it. The rows stay diff --git a/src/device-sync/contacts/groups.ts b/src/device-sync/contacts/groups.ts index 6e9d8dae..c9d0b3f7 100644 --- a/src/device-sync/contacts/groups.ts +++ b/src/device-sync/contacts/groups.ts @@ -94,7 +94,13 @@ export function planGroupDownload(card: ContactCardWire, local: LocalGroup | nul values: { [Groups.SOURCE_ID]: identity, [Groups.TITLE]: remoteTitle ?? '', [Groups.GROUP_VISIBLE]: 1, [Groups.DIRTY]: 0, ...cardColumns(card, ctx) }, }], 'insert'); } - if (local.deleted) return plan([], 'none', 0, true); + if (local.deleted) { + // Deleted on the device: the delete wins over a server edit. Only the shadow (and an identity + // never written) follows the card, so the deletion uploads against the card as it is now. + const diff = changed(local, { ...(local.sourceId ? {} : { [Groups.SOURCE_ID]: identity, [Groups.SYNC3]: null }), ...cardColumns(card, ctx) }); + if (!Object.keys(diff).length) return plan([], 'none', 0, true); + return plan([assertGroup(local, local.dirty), updateGroup(local, diff)], 'update', 0, true); + } if (!local.sourceId) { // Our own create whose identity was never written. return plan([ From c30b4a23d21cba6e659fe81babd1622863baa93d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:15:25 +0200 Subject: [PATCH 042/334] fix: stop contacts waiting forever on group memberships the server can't take #34 A contact added to or removed from a group this device can't change stayed dirty on every run: the membership upload skips such groups, while the contact's clean write kept the change pending until the server showed it. Turning sync off and signing out then always warned about changes waiting for the server. The clean write now puts such a membership back to what the server has, as it does for any edit of a read-only contact, and clears DIRTY; when that was the contact's only change the upload counts it as skipped. Memberships of writable groups in the contact's own account still wait until the group card shows them. This covers groups of another JMAP account. For groups in read-only address books the planner is ready, but the engine does not tell it a group's rights yet; that follows separately. Checked with planner tests for additions, removals, both kinds of group and a change next to an edit that uploads, and end to end through the engine for a group of a shared account, where turning sync off afterwards finds nothing waiting. --- .../__tests__/contacts/groups.test.ts | 74 +++++++++++++++++++ src/device-sync/__tests__/contacts/harness.ts | 13 +++- .../__tests__/contacts/real-engine.test.ts | 31 +++++++- src/device-sync/contacts/download.ts | 14 +++- src/device-sync/contacts/members.ts | 42 ++++++++++- src/device-sync/contacts/upload.ts | 11 +-- 6 files changed, 166 insertions(+), 19 deletions(-) diff --git a/src/device-sync/__tests__/contacts/groups.test.ts b/src/device-sync/__tests__/contacts/groups.test.ts index f29efe9b..c83c2637 100644 --- a/src/device-sync/__tests__/contacts/groups.test.ts +++ b/src/device-sync/__tests__/contacts/groups.test.ts @@ -20,6 +20,15 @@ async function localGroup(h: Harness, groupId: string) { return g; } +/** A group card in an address book this user may only read, on the device. */ +async function readOnlyGroup(h: Harness, members: Record = {}) { + const shared = h.server.addAddressBook(JMAP, { name: 'Shared' }); + h.readOnlyBooks.add(shared); + const gid = h.addCard({ uid: 'urn:uuid:group-shared', kind: 'group', name: { full: 'Shared' }, members, addressBookIds: { [shared]: true } }); + await h.download(gid); + return gid; +} + /** A group card holding Anna (apple card), both on the device. */ async function withGroup(members: Record = { [appleCard().uid as string]: true }) { const h = new Harness(); @@ -127,6 +136,71 @@ describe('contacts groups: membership edits', () => { expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([]); }); + it('puts back an addition to a group in read-only books instead of keeping the contact dirty', async () => { + const h = new Harness(); + const gid = await readOnlyGroup(h, { 'urn:uuid:someone': true }); + const { rawId } = await h.seed(appleCard()); + h.device.user.addToGroup(rawId, (await localGroup(h, gid)).groupId); + expect((await h.upload(rawId)).kind).toBe('revert'); + expect(h.membershipGroups(rawId)).toEqual([]); + expect(h.dirty(rawId)).toBe(false); + expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([]); + }); + + it('puts back a removal from a group in read-only books', async () => { + const h = new Harness(); + const gid = await readOnlyGroup(h, { [appleCard().uid as string]: true }); + const { rawId } = await h.seed(appleCard()); + const membership = (await h.contact(rawId)).rows.find((r) => r.mimetype === MimeType.GROUP_MEMBERSHIP)!; + h.device.user.deleteData(membership.id); + expect((await h.upload(rawId)).kind).toBe('revert'); + expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); + expect(h.dirty(rawId)).toBe(false); + }); + + it('puts back an addition to a group of another JMAP account', async () => { + const h = new Harness(); + h.server.addAccount('t', { name: 'Team' }); + const book = h.server.addAddressBook('t', { name: 'Team contacts' }); + const team = h.server.addCard('t', { uid: 'urn:uuid:team', kind: 'group', name: { full: 'Team' }, addressBookIds: { [book]: true } }); + const card = h.server.get('ContactCard', 't', team) as unknown as ContactCardWire; + await h.applyOk(h.planner.planGroupDownload(card, null, { ...h.ctx, jmapAccountId: 't' }).ops); + const { rawId } = await h.seed(appleCard()); + h.device.user.addToGroup(rawId, (await h.groups()).find((g) => g.sourceId === `t/${team}`)!.groupId); + expect(h.membershipGroups(rawId)).toEqual([`t/${team}`]); + expect((await h.upload(rawId)).kind).toBe('revert'); + expect(h.membershipGroups(rawId)).toEqual([]); + expect(h.dirty(rawId)).toBe(false); + }); + + it('still uploads a read-only contact joining a writable group (the group card is what changes)', async () => { + const h = new Harness(); + const other = h.server.addAddressBook(JMAP, { name: 'Shared' }); + h.readOnlyBooks.add(other); + const gid = h.addCard({ uid: 'urn:uuid:g5', kind: 'group', name: { full: 'Mine' }, members: { 'urn:uuid:someone': true } }); + await h.download(gid); + const { rawId } = await h.seed({ ...appleCard(), addressBookIds: { [other]: true } }); + h.device.user.addToGroup(rawId, (await localGroup(h, gid)).groupId); + expect((await h.upload(rawId)).kind).toBe('revert'); + expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); + expect(h.dirty(rawId)).toBe(true); + expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([ + { kind: 'update', id: gid, patch: { [`members/${appleCard().uid as string}`]: true } }, + ]); + }); + + it('uploads the edits it can next to a membership it puts back, and settles', async () => { + const h = new Harness(); + const gid = await readOnlyGroup(h); + const { id, rawId } = await h.seed(appleCard()); + h.device.user.addToGroup(rawId, (await localGroup(h, gid)).groupId); + h.device.user.updateData(h.rowsByKey(rawId)['emails:k1']._id as number, { data1: 'anna@new.example' }); + const plan = await h.upload(rawId); + expect(actionsOf(plan)).toEqual([{ kind: 'update', id, patch: { 'emails/k1/address': 'anna@new.example' } }]); + expect(h.membershipGroups(rawId)).toEqual([]); + expect(h.dirty(rawId)).toBe(false); + }); + it('does not take a server-side addition for a local removal while the contact is dirty', async () => { const { h, gid } = await withGroup({}); const { rawId } = await h.seed(googleCard()); diff --git a/src/device-sync/__tests__/contacts/harness.ts b/src/device-sync/__tests__/contacts/harness.ts index 095a1c9b..663dfbd0 100644 --- a/src/device-sync/__tests__/contacts/harness.ts +++ b/src/device-sync/__tests__/contacts/harness.ts @@ -6,6 +6,7 @@ */ import { GroupMembership, Groups, RawContacts } from '../../android-columns'; import { makeKeyMinter, parseObjectRef, uuidFrom } from '../../common/ids'; +import type { GroupRights } from '../../contacts/members'; import { contactsPlanner } from '../../contacts/planner'; import type { ContactCardWire, @@ -48,7 +49,7 @@ export class Harness { readonly unselected = new Set(); /** When false the context has no `groupsOf` (memberships not synced). */ groupIndex = true; - readonly ctx: ContactsContext; + readonly ctx: ContactsContext & GroupRights; constructor() { this.server.addAccount(JMAP, { name: ACCOUNT }); @@ -79,14 +80,20 @@ export class Harness { .filter((c) => c.kind === 'group' && (c.members as Record | undefined)?.[uid] === true) .map((c) => `${JMAP}/${c.id as string}`); }, + groupReadOnly(sourceId: string) { + const ref = parseObjectRef(sourceId); + const card = ref ? self.server.get('ContactCard', ref.accountId, ref.id) : undefined; + const books = Object.keys((card?.addressBookIds as Record | undefined) ?? {}); + return books.length > 0 && books.every((id) => self.readOnlyBooks.has(id)); + }, }; } /** The context as the engine may build it without a group index. */ - context(): ContactsContext { + context(): ContactsContext & GroupRights { if (this.groupIndex) return this.ctx; const { groupsOf: _drop, ...rest } = this.ctx; - return rest as ContactsContext; + return rest as ContactsContext & GroupRights; } async apply(group: OpGroup): Promise { diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index f42ba6ae..79207b23 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -3,16 +3,21 @@ // themselves are tested in the other files of this folder. import { describe, expect, it } from 'vitest'; +import { MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; -import { addServerCards, createHarness, type Harness } from '../engine/harness'; +import { addServerCards, CONTACTS_AUTHORITY, createHarness, type Harness } from '../engine/harness'; -function real(): Harness { - const h = createHarness(); +function real(options: Parameters[0] = {}): Harness { + const h = createHarness(options); h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; return h; } +function memberships(h: Harness, rawContactId: number) { + return h.device.rows('data').filter((d) => Number(d.raw_contact_id) === rawContactId && d.mimetype === MimeType.GROUP_MEMBERSHIP); +} + /** An address book of the personal account that this device does not sync. */ function unsyncedBook(h: Harness, name = 'Archive'): string { const id = h.server.addAddressBook('a', { name }); @@ -51,4 +56,24 @@ describe('contacts through the engine', () => { expect(h.server.get('ContactCard', 'a', group)).toMatchObject({ addressBookIds: { [archive]: true } }); expect(h.device.rows('groups')).toEqual([]); }); + + it('puts back a membership in a group of another account, so no change waits for ever', async () => { + const h = real({ accounts: 'withShared' }); + const book = h.server.all('AddressBook', 'team')[0].id as string; + h.prefs.contactsSelection[`team/${book}`] = true; + const team = h.server.addCard('team', { uid: 'g-team', kind: 'group', name: { full: 'Team' }, addressBookIds: { [book]: true } }); + addServerCards(h, ['Ada Lovelace']); + expect((await h.run()).outcome).toBe('ok'); + + const ada = h.contactNamed('Ada Lovelace')!.id; + h.device.user.addToGroup(ada, Number(h.device.rows('groups').find((g) => g.sourceid === `team/${team}`)!._id)); + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', stats: { skipped: 1 } }); + expect(h.contactNamed('Ada Lovelace')).toMatchObject({ dirty: false }); + expect(memberships(h, ada)).toEqual([]); + expect(h.server.get('ContactCard', 'team', team)?.members).toBeUndefined(); + // Turning sync off finds nothing waiting. + expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); + }); }); diff --git a/src/device-sync/contacts/download.ts b/src/device-sync/contacts/download.ts index 066605f6..feacece2 100644 --- a/src/device-sync/contacts/download.ts +++ b/src/device-sync/contacts/download.ts @@ -14,13 +14,13 @@ */ import { Data, MimeType, RawContacts } from '../android-columns'; import { canonicalJson } from '../common/json'; -import { objectRef } from '../common/ids'; +import { objectRef, parseCollectionKey, parseObjectRef } from '../common/ids'; import type { ContactCardWire, ContactsContext, DownloadPlan, LocalContact, LocalDataRow } from '../planner'; import type { ProviderOp, WriteRow } from '../types'; import { changedFromBaseline, encodeBaseline, sameCell } from './cells'; import { localChanges, sameUnit } from './diff'; import { deletionsInferred, inPlaceEvidence, matchKind, reconcileKeys, type KindMatch } from './matching'; -import { planMemberships, remoteMemberships, shadowMemberOf, withMemberOf, withoutMemberOf } from './members'; +import { editableGroups, planMemberships, remoteMemberships, shadowMemberOf, withMemberOf, withoutMemberOf } from './members'; import { assertContact, deleteData, insertUnit, updateData, updateRawContact, updateUnit } from './ops'; import { shadowOf } from './photo'; import { KIND_ORDER, projectCard, rowMatchesUnit, SPECS, type Unit } from './project'; @@ -224,10 +224,16 @@ export interface CleanWriteOptions { accepted?: boolean; } +/** The JMAP account a local contact belongs to. */ +export function accountOf(local: LocalContact, ctx: Ctx): string { + return parseObjectRef(local.sourceId)?.accountId ?? parseCollectionKey(local.pending?.target)?.accountId ?? ctx.jmapAccountId; +} + /** * The ops that make a local contact's rows, shadow and raw columns the * server card's, behind the VERSION assert. Membership changes that the - * server does not have yet stay (and keep the contact dirty). + * server does not have yet stay (and keep the contact dirty), unless it can + * never take them: those are put back (`revertedMembers`). */ export function cleanWrite(local: LocalContact, card: ContactCardWire, ctx: Ctx, options: CleanWriteOptions) { const server = withoutMemberOf(card); @@ -243,6 +249,7 @@ export function cleanWrite(local: LocalContact, card: ContactCardWire, ctx: Ctx, restore: true, parent: local.rawContactId, groupRowIdBySourceId: (g) => ctx.groupRowIdBySourceId(g), + editable: editableGroups(ctx, accountOf(local, ctx)), }); const shadow = withMemberOf(shadowOf(server), members.memberOf); const raw = changedRawColumns(local, { @@ -258,6 +265,7 @@ export function cleanWrite(local: LocalContact, card: ContactCardWire, ctx: Ctx, ops: ops.length ? [assertContact(local, options.assertDirty), ...ops] : [], writes: ops.length, pendingMembers: members.pending, + revertedMembers: members.reverted, }; } diff --git a/src/device-sync/contacts/members.ts b/src/device-sync/contacts/members.ts index 0a07983b..118f3cb7 100644 --- a/src/device-sync/contacts/members.ts +++ b/src/device-sync/contacts/members.ts @@ -13,9 +13,12 @@ * Per group, a local change wins where the server did not change, the server * wins elsewhere; both sides can only flip a membership the same way, so * memberships never conflict. A local change stays in the base until the - * server has it, which keeps it visible to `planMembershipUploads`. + * server has it, which keeps it visible to `planMembershipUploads`; one the + * server can't take (a group in read-only books, or in another JMAP account) + * is put back by the next clean write instead. */ import { Data, GroupMembership, MimeType } from '../android-columns'; +import { parseObjectRef } from '../common/ids'; import type { ContactCardWire, ContactsContext, LocalDataRow } from '../planner'; import type { ProviderOp } from '../types'; import { text } from './cells'; @@ -23,6 +26,24 @@ import { entryKey } from './keys'; import { deleteData, insertUnit, updateData } from './ops'; import { MEMBER_OF, type Unit } from './project'; +/** + * Group rights the engine can add to the context (not part of + * ContactsContext yet): whether a group card, by SOURCE_ID, is in read-only + * address books only. Without it every synced group counts as writable. + */ +export interface GroupRights { + groupReadOnly?(sourceId: string): boolean; +} + +/** + * The groups whose members an upload for a contact of `account` can change: + * that account's groups outside read-only books. A membership change of any + * other group can never reach the server (`planMembershipUploads` skips it). + */ +export function editableGroups(ctx: ContactsContext & GroupRights, account: string): (sourceId: string) => boolean { + return (sourceId) => parseObjectRef(sourceId)?.accountId === account && !(ctx.groupReadOnly?.(sourceId) ?? false); +} + /** Remote memberships of a card, or null when the engine can't tell (memberships are then left alone). */ export function remoteMemberships(card: ContactCardWire, ctx: ContactsContext): string[] | null { if (typeof ctx.groupsOf !== 'function' || typeof card.uid !== 'string' || !card.uid) return null; @@ -71,6 +92,8 @@ export interface MemberPlan { /** Groups the device added the contact to, or removed it from, that the server does not show yet. */ added: string[]; removed: string[]; + /** Local changes of groups that are not `editable` were put back to what the server has. */ + reverted: boolean; } export interface MemberInput { @@ -83,13 +106,19 @@ export interface MemberInput { restore: boolean; parent: number | { ref: number }; groupRowIdBySourceId(sourceId: string): number | null; + /** + * Groups whose members the device may change (see `editableGroups`); a local + * change of another group is put back to what the server has rather than + * waiting for an upload that never comes. Without it every group counts. + */ + editable?(sourceId: string): boolean; } export function planMemberships(input: MemberInput): MemberPlan { const live = input.rows.filter((r) => r.mimetype === MimeType.GROUP_MEMBERSHIP); if (input.remote === null) { // Without the engine's group index memberships are not synced: nothing is written or kept pending. - return { ops: [], writes: 0, memberOf: input.base, pending: false, added: [], removed: [] }; + return { ops: [], writes: 0, memberOf: input.base, pending: false, added: [], removed: [], reverted: false }; } const ops: ProviderOp[] = []; const rowOf = new Map(); @@ -119,11 +148,17 @@ export function planMemberships(input: MemberInput): MemberPlan { after.add(g); }; + let reverted = false; for (const g of [...new Set([...base, ...remote, ...rowOf.keys()])].sort()) { const inBase = base.has(g); const inRemote = remote.has(g); const row = rowOf.get(g); - const change = inBase ? (!row && deletions ? 'removed' : null) : row ? 'added' : null; + let change = inBase ? (!row && deletions ? 'removed' : null) : row ? 'added' : null; + if (change && input.editable && !input.editable(g)) { + // A change the server can't take (a group in read-only books, or another account's): its state comes back. + if (change === 'added' ? !inRemote : inRemote) reverted = true; + change = null; + } if (change === 'added') { keep(g, row!); if (!inRemote) pendingAdded.add(g); @@ -145,5 +180,6 @@ export function planMemberships(input: MemberInput): MemberPlan { pending: unsynced || pendingAdded.size > 0 || pendingRemoved.size > 0, added: [...pendingAdded], removed: [...pendingRemoved], + reverted, }; } diff --git a/src/device-sync/contacts/upload.ts b/src/device-sync/contacts/upload.ts index debff260..34430e1b 100644 --- a/src/device-sync/contacts/upload.ts +++ b/src/device-sync/contacts/upload.ts @@ -23,7 +23,7 @@ import type { import type { ProviderOp } from '../types'; import { encodeBaseline } from './cells'; import { localChanges } from './diff'; -import { cardUnits, cleanWrite } from './download'; +import { accountOf, cardUnits, cleanWrite } from './download'; import { newEntries, unitEdits, unitEntries, type Edit, type NewEntry } from './entries'; import { parseEntryKey } from './keys'; import { deletionsInferred, inPlaceEvidence, matchKind, type KindMatch } from './matching'; @@ -263,18 +263,15 @@ export function planContactUpload(local: LocalContact, ctx: Ctx): UploadPlan[] = [{ kind: 'update', id: parseObjectRef(local.sourceId)!.id, patch }]; if (backedOff(local, ctx, actions)) return { kind: 'skip', reason: `poisoned:${local.poison!.type}` }; return { kind: 'upload', actions }; } -/** The JMAP account a local contact belongs to. */ -function accountOf(local: LocalContact, ctx: Ctx): string { - return parseObjectRef(local.sourceId)?.accountId ?? parseCollectionKey(local.pending?.target)?.accountId ?? ctx.jmapAccountId; -} - export function planContactAccepted(local: LocalContact, server: ContactCardWire, ctx: Ctx): AcceptedPlan { if (local.deleted) { const purge = group(local, [deleteWhereId('raw_contacts', local.rawContactId)]); From 20effab062d3f36cf3b853a598a0f5e0fb5e13a1 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:16:01 +0200 Subject: [PATCH 043/334] fix: keep an address's parts in step with a one-line edit on the device #34 AOSP Contacts edits a postal address as one formatted line; the provider then copies it into the street and empties the other parts. The upload sent only the new `full`, so the components on the server kept the old values: CardDAV clients and the app's editor, which show the parts, still showed the old city. A changed line now uploads with the parts it implies. They are read back along the old line: each of its segments must be some of the old parts, and a segment whose change is confined to one part rewrites that part like an in-place edit of its field. When the layout changed or a change spans two parts, the address keeps the whole line as its street, which is what the device holds, rather than an address without parts that the app's editor would show empty and drop on its next save. An unchanged line still uploads nothing, and editors that write the parts themselves are handled as before. Checked with planner tests for the edit from the device test, a street-only edit, the fallback, a re-inserted line and an editor that keeps the parts, and with unit tests of the read-back. --- .../__tests__/contacts/units.test.ts | 20 +++- .../__tests__/contacts/upload-fields.test.ts | 107 ++++++++++++++++++ src/device-sync/contacts/diff.ts | 50 +++++--- src/device-sync/contacts/postal.ts | 74 ++++++++++++ src/device-sync/contacts/upload.ts | 4 +- 5 files changed, 237 insertions(+), 18 deletions(-) diff --git a/src/device-sync/__tests__/contacts/units.test.ts b/src/device-sync/__tests__/contacts/units.test.ts index 7738244e..e9bb609c 100644 --- a/src/device-sync/__tests__/contacts/units.test.ts +++ b/src/device-sync/__tests__/contacts/units.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { Data, MimeType, PhoneType, RelationType } from '../../android-columns'; import { contactsPlanner } from '../../contacts/planner'; import { entryKey, orgKey, parseEntryKey, parseOrgKey } from '../../contacts/keys'; -import { editPostalComponents, postalCells } from '../../contacts/postal'; +import { editPostalComponents, postalCells, resplitPostal } from '../../contacts/postal'; import { base64ToBytes, dataUriBase64, shadowOf } from '../../contacts/photo'; import { fullIsDerived, nameCells } from '../../contacts/name'; import { phoneFlags, phoneType, relationName, relationType } from '../../contacts/types-map'; @@ -118,6 +118,24 @@ describe('contacts name and postal helpers', () => { expect(fullIsDerived({ components, full: 'B, A' })).toBe(false); }); + it('reads the parts of a rewritten one-line address back along the old line', () => { + // Apple's layout: street / city region postcode / country. + const old = { data1: '1 Infinite Loop\nCupertino CA 95014\nUSA', data4: '1 Infinite Loop', data7: 'Cupertino', data8: 'CA', data9: '95014', data10: 'USA' }; + const parts = (line: string) => { + const out = resplitPostal(old, line); + return out && { street: out.data4, city: out.data7, region: out.data8, postcode: out.data9, country: out.data10 }; + }; + expect(parts('1 Apple Park Way\nCupertino CA 95014\nUSA')).toEqual({ street: '1 Apple Park Way', city: 'Cupertino', region: 'CA', postcode: '95014', country: 'USA' }); + expect(parts('1 Infinite Loop\nSan Jose CA 95014\nUSA')).toMatchObject({ city: 'San Jose', region: 'CA', postcode: '95014' }); + expect(parts('1 Infinite Loop\nCupertino 95014\nUSA')).toMatchObject({ city: 'Cupertino', region: null, postcode: '95014' }); + expect(parts('1 Infinite Loop, Cupertino CA 95014, USA')).toMatchObject({ street: '1 Infinite Loop', city: 'Cupertino' }); + // Two parts of one segment changed, or the layout changed: nothing to pin the words to. + expect(parts('1 Infinite Loop\nSan Jose NV 95014\nUSA')).toBeNull(); + expect(parts('1 Infinite Loop\nCupertino CA 95014\nCalifornia\nUSA')).toBeNull(); + // An old line that isn't made of the parts (a `full` written apart from its components). + expect(resplitPostal({ ...old, data1: 'Apple HQ, Cupertino' }, 'Apple Park, Cupertino')).toBeNull(); + }); + it('keeps separators between street parts and falls back to the legacy flat fields', () => { expect(postalCells({ street: 'Old Rd 1', locality: 'Town' })).toMatchObject({ data4: 'Old Rd 1', data7: 'Town' }); const address = { components: [{ kind: 'number', value: '12' }, { kind: 'separator', value: '-' }, { kind: 'name', value: 'Rue X' }] }; diff --git a/src/device-sync/__tests__/contacts/upload-fields.test.ts b/src/device-sync/__tests__/contacts/upload-fields.test.ts index 62fb106d..e1582aa1 100644 --- a/src/device-sync/__tests__/contacts/upload-fields.test.ts +++ b/src/device-sync/__tests__/contacts/upload-fields.test.ts @@ -108,6 +108,113 @@ describe('contacts upload: one edited field, one pointer', () => { }); }); +describe('contacts upload: an address edited as one line (AOSP Contacts)', () => { + const munich = () => ({ + name: { full: 'Maria Muster' }, + addresses: { + home: { + contexts: { private: true }, + coordinates: 'geo:48.137,11.575', + components: [ + { kind: 'name', value: 'Marienplatz 1' }, + { kind: 'postcode', value: '80331' }, + { kind: 'locality', value: 'Munich' }, + { kind: 'region', value: 'Bavaria' }, + { kind: 'country', value: 'Germany' }, + ], + }, + }, + }); + const LINE = 'Marienplatz 1, 80331 Munich, Bavaria, Germany'; + + /** AOSP writes the formatted address alone; the provider keeps it in STREET and empties the other parts. */ + async function editLine(line: string) { + const h = new Harness(); + const { id, rawId } = await h.seed(munich()); + const row = h.rowsByKey(rawId)['addresses:home']; + expect(row.data1).toBe(LINE); + h.device.user.updateData(row._id as number, { data1: line, data4: null, data5: null, data6: null, data7: null, data8: null, data9: null, data10: null }); + expect(h.rowsByKey(rawId)['addresses:home']).toMatchObject({ data1: line, data4: line, data7: null }); + return { h, id, rawId }; + } + + async function patchOf(h: Harness, rawId: number): Promise { + const plan = h.planner.planUpload(await h.contact(rawId), h.ctx); + if (plan.kind !== 'upload' || plan.actions[0].kind !== 'update') throw new Error(`expected an update, got ${plan.kind}`); + return plan.actions[0].patch; + } + + const components = (street: string, locality = 'Munich', postcode = '80331') => [ + { kind: 'name', value: street }, + { kind: 'postcode', value: postcode }, + { kind: 'locality', value: locality }, + { kind: 'region', value: 'Bavaria' }, + { kind: 'country', value: 'Germany' }, + ]; + + it('reads the parts back from the new line, so `full` and the components agree', async () => { + const line = 'Industrieweg 42, 80331 Augsburg, Bavaria, Germany'; + const { h, id, rawId } = await editLine(line); + expect(await patchOf(h, rawId)).toEqual({ + 'addresses/home/components': components('Industrieweg 42', 'Augsburg'), + 'addresses/home/full': line, + }); + await h.upload(rawId); + expect(h.card(id).addresses!.home).toEqual({ + contexts: { private: true }, coordinates: 'geo:48.137,11.575', isOrdered: true, full: line, + components: components('Industrieweg 42', 'Augsburg'), + }); + // The device gets its parts back and settles. + expect(h.rowsByKey(rawId)['addresses:home']).toMatchObject({ data1: line, data4: 'Industrieweg 42', data7: 'Augsburg', data9: '80331' }); + const local = await h.contact(rawId); + expect(local.dirty).toBe(false); + expect(h.planner.planDownload(h.card(id), local, h.ctx).effect).toBe('none'); + }); + + it('changes only the part whose words changed', async () => { + const { h, rawId } = await editLine('Marienplatz 2, 80331 Munich, Bavaria, Germany'); + expect(await patchOf(h, rawId)).toEqual({ + 'addresses/home/components': components('Marienplatz 2'), + 'addresses/home/full': 'Marienplatz 2, 80331 Munich, Bavaria, Germany', + }); + }); + + it('keeps the whole line as the street when the change can not be pinned to one part', async () => { + const line = 'Industrieweg 42, 86150 Augsburg, Bavaria, Germany'; + const { h, id, rawId } = await editLine(line); + expect(await patchOf(h, rawId)).toEqual({ 'addresses/home/components': [{ kind: 'name', value: line }], 'addresses/home/full': line }); + await h.upload(rawId); + expect(h.card(id).addresses!.home).toMatchObject({ contexts: { private: true }, coordinates: 'geo:48.137,11.575', full: line, components: [{ kind: 'name', value: line }] }); + const local = await h.contact(rawId); + expect(local.dirty).toBe(false); + expect(h.planner.planDownload(h.card(id), local, h.ctx).effect).toBe('none'); + }); + + it('uploads nothing for the same line saved again', async () => { + const { h, rawId } = await editLine(LINE); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx).kind).toBe('clean'); + }); + + it('reads the parts back from a line an editor re-inserted without its key', async () => { + const line = 'Marienplatz 1, 80331 Munich, Upper Bavaria, Germany'; + const h = new Harness(); + const { rawId } = await h.seed(munich()); + h.device.user.fossifySave(rawId, null, [{ [Data.MIMETYPE]: MimeType.STRUCTURED_POSTAL, data1: line, data2: 1 }]); + expect(await patchOf(h, rawId)).toEqual({ + 'addresses/home/components': components('Marienplatz 1').map((c) => (c.kind === 'region' ? { ...c, value: 'Upper Bavaria' } : c)), + 'addresses/home/full': line, + }); + }); + + it('edits the parts an editor kept as parts, whatever its line says', async () => { + const line = 'Marienplatz 1, 80331 Augsburg, Bavaria, Germany'; + const h = new Harness(); + const { rawId } = await h.seed(munich()); + h.device.user.updateData(h.rowsByKey(rawId)['addresses:home']._id as number, { data7: 'Augsburg', data1: line }); + expect(await patchOf(h, rawId)).toEqual({ 'addresses/home/components': components('Marienplatz 1', 'Augsburg'), 'addresses/home/full': line }); + }); +}); + describe('contacts upload: the name', () => { const NAME_KEY = 'name'; diff --git a/src/device-sync/contacts/diff.ts b/src/device-sync/contacts/diff.ts index e0dac5d5..7e9bd855 100644 --- a/src/device-sync/contacts/diff.ts +++ b/src/device-sync/contacts/diff.ts @@ -10,29 +10,49 @@ import { CommonKind, Data, MimeType, StructuredPostal as SP } from '../android-c import type { Row } from '../types'; import { changedFromBaseline, sameCell, text } from './cells'; import type { Match } from './matching'; -import { POSTAL_PART_COLUMNS } from './postal'; +import { POSTAL_PART_COLUMNS, providerSplitPostal, resplitPostal } from './postal'; import { SPECS, type Unit } from './project'; +const isPart = (column: string) => (POSTAL_PART_COLUMNS as readonly string[]).includes(column); + +/** A postal row holding the provider's split of a formatted address an editor wrote alone. */ +function isProviderSplit(m: Match): boolean { + const { row } = m; + if (row.mimetype !== MimeType.STRUCTURED_POSTAL || !providerSplitPostal(row.cells)) return false; + // A multi-part formatted address in STREET was the provider's split, not a typed street. + return m.how !== 'key' || !row.baseline || /[,\n]/.test(text(row.cells[SP.FORMATTED_ADDRESS]) ?? ''); +} + +/** The address as the device showed it before the edit: a keyed row's baseline, else the shadow's. */ +function postalBefore(m: Match): Row { + return m.how === 'key' && m.row.baseline ? m.row.baseline : m.unit.cells; +} + /** - * A postal row whose parts are only its formatted address copied into STREET: - * the provider's split of an editor that wrote FORMATTED_ADDRESS alone. + * The cells an upload takes a row's values from: its own, except for a + * provider split whose line changed. Its parts are read back from the new + * line along the old one (`resplitPostal`), else they stay what the device + * holds, the whole line as the street: `full` and the components never + * contradict each other on the server. */ -function providerSplitPostal(cells: Row): boolean { - const street = text(cells[SP.STREET]); - return street !== null - && street === text(cells[SP.FORMATTED_ADDRESS]) - && POSTAL_PART_COLUMNS.every((c) => c === SP.STREET || text(cells[c]) === null); +export function uploadCells(m: Match): Row { + if (!isProviderSplit(m)) return m.row.cells; + const before = postalBefore(m); + const line = text(m.row.cells[SP.FORMATTED_ADDRESS]); + if (!line || sameCell(line, before[SP.FORMATTED_ADDRESS])) return m.row.cells; + return { ...m.row.cells, ...(resplitPostal(before, line) ?? {}) }; } export function localChanges(m: Match): string[] { - const { row, unit } = m; + const { unit } = m; const changed = rawChanges(m).filter((c) => !unit.truncated?.includes(c)); - if (row.mimetype === MimeType.STRUCTURED_POSTAL && providerSplitPostal(row.cells)) { - // A multi-part formatted address in STREET was the provider's split, not a typed street. - const split = m.how !== 'key' || !row.baseline || /[,\n]/.test(text(row.cells[SP.FORMATTED_ADDRESS]) ?? ''); - if (split) return changed.filter((c) => !(POSTAL_PART_COLUMNS as readonly string[]).includes(c)); - } - return changed; + if (!isProviderSplit(m)) return changed; + // The provider's split is no typed street: only a new line changes the parts, to what it says. + const others = changed.filter((c) => !isPart(c)); + if (!others.includes(SP.FORMATTED_ADDRESS)) return others; + const before = postalBefore(m); + const after = uploadCells(m); + return [...others, ...POSTAL_PART_COLUMNS.filter((c) => !sameCell(after[c], before[c]))]; } function rawChanges(m: Match): string[] { diff --git a/src/device-sync/contacts/postal.ts b/src/device-sync/contacts/postal.ts index fd7d590e..c3795f7d 100644 --- a/src/device-sync/contacts/postal.ts +++ b/src/device-sync/contacts/postal.ts @@ -90,6 +90,80 @@ export function predictStoredPostal(cells: Row): Row { return out; } +/** + * A postal row whose parts are only its formatted address copied into STREET: + * the provider's split of an editor that wrote FORMATTED_ADDRESS alone. + */ +export function providerSplitPostal(cells: Row): boolean { + const street = text(cells[SP.STREET]); + return street !== null + && street === text(cells[SP.FORMATTED_ADDRESS]) + && POSTAL_PART_COLUMNS.every((c) => c === SP.STREET || text(cells[c]) === null); +} + +/** Lines and comma-separated segments of a formatted address. */ +const SEGMENT = /\s*(?:,|\r?\n)\s*/; + +const wordsOf = (value: string) => value.split(/\s+/).filter(Boolean); + +function wordsAt(words: string[], part: string[], at: number): boolean { + return at >= 0 && at + part.length <= words.length && part.every((w, i) => words[at + i] === w); +} + +/** The parts one segment of the old line is made of, in their order there (each part used once). */ +function segmentParts(segment: string[], parts: Map, used: Set): string[] | null { + if (!segment.length) return []; + for (const [column, words] of parts) { + if (used.has(column) || !wordsAt(segment, words, 0)) continue; + used.add(column); + const rest = segmentParts(segment.slice(words.length), parts, used); + if (rest) return [column, ...rest]; + used.delete(column); + } + return null; +} + +/** + * The parts of an address whose formatted line an editor rewrote alone (the + * provider then empties them on the device), read back along the old line: + * each segment of the old line must be the words of some old parts in a row, + * and the new line must have as many segments. A changed segment gives its + * words to its one part, or to the one part left once the parts at its start + * and end are found unchanged there. Null when the layout changed, or a change + * can't be pinned to one part. + */ +export function resplitPostal(old: Row, line: string): Row | null { + const before = text(old[SP.FORMATTED_ADDRESS]); + if (!before) return null; + const parts = new Map(); + for (const c of POSTAL_PART_COLUMNS) { + const value = text(old[c]); + if (value) parts.set(c, wordsOf(value)); + } + const oldSegments = before.split(SEGMENT).map(wordsOf).filter((w) => w.length); + const newSegments = line.split(SEGMENT).map(wordsOf).filter((w) => w.length); + if (oldSegments.length !== newSegments.length) return null; + const out: Row = { [SP.FORMATTED_ADDRESS]: line }; + for (const c of POSTAL_PART_COLUMNS) out[c] = text(old[c]); + const used = new Set(); + for (let i = 0; i < oldSegments.length; i++) { + const columns = segmentParts(oldSegments[i], parts, used); + if (!columns) return null; + const words = newSegments[i]; + if (words.join(' ') === oldSegments[i].join(' ')) continue; + const size = (k: number) => parts.get(columns[k])!.length; + let first = 0; + let start = 0; + while (first < columns.length - 1 && wordsAt(words, parts.get(columns[first])!, start)) start += size(first++); + let last = columns.length; + let end = words.length; + while (last - 1 > first && end - size(last - 1) >= start && wordsAt(words, parts.get(columns[last - 1])!, end - size(last - 1))) end -= size(--last); + if (last - first !== 1) return null; + out[columns[first]] = words.slice(start, end).join(' ') || null; + } + return out; +} + /** The address's components with the edited columns applied (a list for `addresses//components`). */ export function editPostalComponents(address: ContactAddress, cells: Row, changed: ReadonlySet): Component[] { const comps = clone(components(address)); diff --git a/src/device-sync/contacts/upload.ts b/src/device-sync/contacts/upload.ts index 34430e1b..dbd8b9a1 100644 --- a/src/device-sync/contacts/upload.ts +++ b/src/device-sync/contacts/upload.ts @@ -22,7 +22,7 @@ import type { } from '../planner'; import type { ProviderOp } from '../types'; import { encodeBaseline } from './cells'; -import { localChanges } from './diff'; +import { localChanges, uploadCells } from './diff'; import { accountOf, cardUnits, cleanWrite } from './download'; import { newEntries, unitEdits, unitEntries, type Edit, type NewEntry } from './entries'; import { parseEntryKey } from './keys'; @@ -88,7 +88,7 @@ function collectChanges(local: LocalContact, shadow: ContactCardWire, ctx: Ctx): const changed = localChanges(m); if (!changed.length) continue; const shown = kind.mimetype === MimeType.RELATION ? (m.unit.cells[Data.DATA1] as string | null) : null; - const e = unitEdits(kind.mimetype, m.unit.key, shadow, m.row.cells, m.unit.cells, new Set(changed), shown, m.how === 'key'); + const e = unitEdits(kind.mimetype, m.unit.key, shadow, uploadCells(m), m.unit.cells, new Set(changed), shown, m.how === 'key'); out.edits.push(...e.edits); out.added.push(...e.added); out.removed.push(...e.removed); From 05e3605ec7f54db3a974d251ca6749dfc3aa2bc0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:16:43 +0200 Subject: [PATCH 044/334] fix: keep a group rename when a membership change of the group uploads first #34 Accepting a membership patch of a group card wrote the server's title over the device's and cleared DIRTY. A rename that had not reached the server in the same run, because the server refused it or the user made it after the group's own upload was planned, was silently undone. The accepted write now keeps a pending rename that the accepted card does not show: it stores the new shadow but leaves the title and DIRTY, so the rename uploads against it. A card that shows the device's title, or a different one from a concurrent server rename, is taken as before. Checked with a planner test and end to end through the engine with a refused rename and a membership change in the same run. --- .../__tests__/contacts/groups.test.ts | 19 +++++++++++++ .../__tests__/contacts/real-engine.test.ts | 27 ++++++++++++++++++- src/device-sync/contacts/groups.ts | 13 ++++++--- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/device-sync/__tests__/contacts/groups.test.ts b/src/device-sync/__tests__/contacts/groups.test.ts index c83c2637..d9ac7869 100644 --- a/src/device-sync/__tests__/contacts/groups.test.ts +++ b/src/device-sync/__tests__/contacts/groups.test.ts @@ -111,6 +111,25 @@ describe('contacts groups: membership edits', () => { expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([]); }); + it('keeps a rename the server does not have when a membership patch of the group is accepted', async () => { + const h = new Harness(); + const gid = h.addCard({ uid: 'urn:uuid:g2', kind: 'group', name: { full: 'Work' }, members: { 'urn:uuid:someone': true } }); + await h.download(gid); + const { rawId } = await h.seed(appleCard()); + const g = await localGroup(h, gid); + h.device.user.addToGroup(rawId, g.groupId); + await h.upload(rawId); + // Renamed after the group's own upload was planned, before the membership phase read it. + h.device.user.updateGroup(g.groupId, { [Groups.TITLE]: 'Colleagues' }); + const read = await localGroup(h, gid); + h.send(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)); + await h.applyOk(h.planner.planGroupAccepted(read, h.card(gid), h.ctx).ops); + expect(await localGroup(h, gid)).toMatchObject({ title: 'Colleagues', dirty: true, shadow: { members: { [appleCard().uid as string]: true } } }); + expect(h.planner.planGroupUpload(await localGroup(h, gid), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id: gid, patch: { 'name/full': 'Colleagues' } }], + }); + }); + it('sends the whole map for the first member of an empty group', async () => { const h = new Harness(); const empty = h.addCard({ uid: 'urn:uuid:g3', kind: 'group', name: { full: 'Empty' } }); diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index 79207b23..758b5e3c 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -3,10 +3,11 @@ // themselves are tested in the other files of this folder. import { describe, expect, it } from 'vitest'; -import { MimeType } from '../../android-columns'; +import { Groups, MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; import { addServerCards, CONTACTS_AUTHORITY, createHarness, type Harness } from '../engine/harness'; +import type { SetTarget } from '../fakes/fake-jmap-server'; function real(options: Parameters[0] = {}): Harness { const h = createHarness(options); @@ -76,4 +77,28 @@ describe('contacts through the engine', () => { // Turning sync off finds nothing waiting. expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); }); + + it('keeps a group rename the server refused when a membership patch of that group goes through', async () => { + const h = real(); + const [ada] = addServerCards(h, ['Ada Lovelace']); + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { other: true }, addressBookIds: { [h.book]: true } }); + expect((await h.run()).outcome).toBe('ok'); + const row = () => h.device.rows('groups').find((g) => g.sourceid === `a/${group}`)!; + const groupId = Number(row()._id); + h.device.user.updateGroup(groupId, { [Groups.TITLE]: 'Best friends' }); + h.device.user.addToGroup(h.contactNamed('Ada Lovelace')!.id, groupId); + const rename = (t: SetTarget) => t.op === 'update' && t.id === group && !!t.object && 'name/full' in t.object; + h.server.setErrorFor('ContactCard', 'a', rename, { type: 'invalidProperties', properties: ['name'] }); + + const report = await h.run(); + expect(report.itemErrors).toMatchObject([{ ref: `a/${group}`, type: 'invalidProperties' }]); + const uid = h.server.get('ContactCard', 'a', ada)!.uid as string; + expect(h.server.get('ContactCard', 'a', group)).toMatchObject({ name: { full: 'Friends' }, members: { other: true, [uid]: true } }); + expect(row()).toMatchObject({ [Groups.TITLE]: 'Best friends', [Groups.DIRTY]: 1 }); + + // The rename is still the device's and goes up once the server takes it. + expect((await h.run()).outcome).toBe('ok'); + expect(h.server.get('ContactCard', 'a', group)).toMatchObject({ name: { full: 'Best friends' } }); + expect(row()).toMatchObject({ [Groups.TITLE]: 'Best friends', [Groups.DIRTY]: 0 }); + }); }); diff --git a/src/device-sync/contacts/groups.ts b/src/device-sync/contacts/groups.ts index c9d0b3f7..45829807 100644 --- a/src/device-sync/contacts/groups.ts +++ b/src/device-sync/contacts/groups.ts @@ -193,12 +193,17 @@ export function planGroupAccepted(local: LocalGroup, server: ContactCardWire, ct const identity = objectRef(account, server.id); const raw: WriteRow = { [Groups.SOURCE_ID]: identity, [Groups.SYNC3]: null, [Groups.SYNC4]: null, ...cardColumns(server, ctx) }; const title = groupTitle(server); + const keepDirtyOps = { ref: identity, ops: [updateGroup(local, raw)] }; + // A rename the server still lacks (the upload it accepted was another one, a membership + // patch) stays the device's: title and DIRTY are kept, and it uploads against the new shadow. + const shadowTitle = groupTitle(local.shadow); + const renamed = local.dirty && !!local.title?.trim() && !sameCell(local.title, shadowTitle); + if (renamed && sameCell(title, shadowTitle)) { + return { ops: { ref: identity, ops: [assertGroup(local, null), updateGroup(local, raw)] }, keepDirtyOps }; + } const ops: WriteRow = { ...raw, [Groups.DIRTY]: 0 }; if (!sameCell(local.title, title)) ops[Groups.TITLE] = title ?? ''; - return { - ops: { ref: identity, ops: [assertGroup(local, null), updateGroup(local, ops)] }, - keepDirtyOps: { ref: identity, ops: [updateGroup(local, raw)] }, - }; + return { ops: { ref: identity, ops: [assertGroup(local, null), updateGroup(local, ops)] }, keepDirtyOps }; } /** From 00f1a66095e428ccef881152548f124458418c19 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:17:21 +0200 Subject: [PATCH 045/334] fix: keep phone, email and website types as picked on the device #34 Types Android has but RFC 9553 lacks uploaded as a label naming them, yet every label read back as a custom type, and MMS and company main read back as Other and Main. A number typed Car showed up as "Custom: car" right after the sync, an MMS number as Other, a mobile email as "Custom: mobile" and a blog link as "Custom: blog". Such a label now names its type again, for phones only next to the feature the type uploads with, so a custom label stays custom. MMS uploads its `mms` label next to `text`, which keeps a text-capable number from reading as MMS, and `main-number` with `work` reads as company main. Checked with a round trip of every phone, email and website type, both for a contact created on the device and for a type changed on a synced entry. --- .../__tests__/contacts/download.test.ts | 5 ++- .../__tests__/contacts/units.test.ts | 15 +++++++- .../__tests__/contacts/upload-fields.test.ts | 38 ++++++++++++++++++- src/device-sync/contacts/types-map.ts | 29 ++++++++++++-- 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/device-sync/__tests__/contacts/download.test.ts b/src/device-sync/__tests__/contacts/download.test.ts index 3bdf68f2..4fd6e639 100644 --- a/src/device-sync/__tests__/contacts/download.test.ts +++ b/src/device-sync/__tests__/contacts/download.test.ts @@ -99,7 +99,7 @@ describe('contacts download: golden rows', () => { expect(Object.keys(rows).some((k) => k.startsWith('onlineServices'))).toBe(false); }); - it('maps a Google card: custom display name, legacy cell phone, labelled link, department list, relation by name', async () => { + it('maps a Google card: custom display name, legacy cell phone, profile link, department list, relation by name', async () => { const h = new Harness(); const { rawId } = await h.seed(googleCard()); const rows = h.rowsByKey(rawId); @@ -110,7 +110,8 @@ describe('contacts download: golden rows', () => { expect(cells(rows['nicknames:k1'], ['data1'])).toEqual({ data1: 'Gü' }); expect(cells(rows['nicknames:k2'], ['data1'])).toEqual({ data1: 'GKG' }); expect(cells(rows['phones:k1'], TYPED)).toEqual({ data1: '0171 2345678', data2: '2', data3: null }); - expect(cells(rows['links:k1'], TYPED)).toEqual({ data1: 'https://example.org/~guenther', data2: '0', data3: 'profile' }); + // The label names a type Android has. + expect(cells(rows['links:k1'], TYPED)).toEqual({ data1: 'https://example.org/~guenther', data2: '3', data3: null }); expect(cells(rows['organizations:k1|titles:k1,'], ORG)).toEqual({ data1: 'Uni Beispiel', data4: 'Professor', data5: 'Informatik, AG Sync', data6: null }); expect(cells(rows['relatedTo:Maria Groß'], TYPED)).toEqual({ data1: 'Maria Groß', data2: '14', data3: null }); expect(cells(rows['anniversaries:k1'], TYPED)).toEqual({ data1: '--08-15', data2: '1', data3: null }); diff --git a/src/device-sync/__tests__/contacts/units.test.ts b/src/device-sync/__tests__/contacts/units.test.ts index e9bb609c..87ed4457 100644 --- a/src/device-sync/__tests__/contacts/units.test.ts +++ b/src/device-sync/__tests__/contacts/units.test.ts @@ -1,11 +1,11 @@ import { describe, expect, it } from 'vitest'; -import { Data, MimeType, PhoneType, RelationType } from '../../android-columns'; +import { Data, EmailType, MimeType, PhoneType, RelationType, WebsiteType } from '../../android-columns'; import { contactsPlanner } from '../../contacts/planner'; import { entryKey, orgKey, parseEntryKey, parseOrgKey } from '../../contacts/keys'; import { editPostalComponents, postalCells, resplitPostal } from '../../contacts/postal'; import { base64ToBytes, dataUriBase64, shadowOf } from '../../contacts/photo'; import { fullIsDerived, nameCells } from '../../contacts/name'; -import { phoneFlags, phoneType, relationName, relationType } from '../../contacts/types-map'; +import { emailType, phoneFlags, phoneType, relationName, relationType, websiteType } from '../../contacts/types-map'; import type { ContactCardWire } from '../../planner'; import { JPEG } from './fixtures'; @@ -94,6 +94,17 @@ describe('contacts type tables', () => { expect(phoneFlags(PhoneType.CAR, null)).toEqual({ contexts: [], features: ['voice'], label: 'car' }); }); + it('reads back the types RFC 9553 lacks from the label they upload with', () => { + expect(phoneType({ features: { 'main-number': true }, contexts: { work: true } }).type).toBe(PhoneType.COMPANY_MAIN); + expect(phoneType({ features: { voice: true }, label: 'car' })).toEqual({ type: PhoneType.CAR, label: null }); + expect(phoneType({ features: { text: true }, label: 'mms' })).toEqual({ type: PhoneType.MMS, label: null }); + // A custom label uploads without the feature, so it stays custom; a lone `text` is no MMS. + expect(phoneType({ label: 'car' })).toEqual({ type: PhoneType.CUSTOM, label: 'car' }); + expect(phoneType({ features: { text: true } }).type).toBe(PhoneType.OTHER); + expect(emailType({ label: 'mobile' })).toEqual({ type: EmailType.MOBILE, label: null }); + expect(websiteType({ label: 'blog', contexts: { work: true } })).toEqual({ type: WebsiteType.BLOG, label: null }); + }); + it('maps relations both ways, device-only types to the nearest RFC type', () => { expect(relationType({ friend: true })).toEqual({ type: RelationType.FRIEND, label: null }); expect(relationType({ 'co-worker': true })).toEqual({ type: RelationType.CUSTOM, label: 'co-worker' }); diff --git a/src/device-sync/__tests__/contacts/upload-fields.test.ts b/src/device-sync/__tests__/contacts/upload-fields.test.ts index e1582aa1..cca03bb9 100644 --- a/src/device-sync/__tests__/contacts/upload-fields.test.ts +++ b/src/device-sync/__tests__/contacts/upload-fields.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { Data, MimeType } from '../../android-columns'; +import { Data, EmailType, MimeType, PhoneType, WebsiteType } from '../../android-columns'; import { deepEqual } from '../../common/json'; import { applyPatch, assertPatchWellFormed, type PatchObject } from '../../common/patch'; import type { ContactCardWire } from '../../planner'; @@ -108,6 +108,42 @@ describe('contacts upload: one edited field, one pointer', () => { }); }); +describe('contacts upload: every device type comes back as the device wrote it', () => { + const cases = (types: Record, custom: number) => + Object.entries(types).map(([name, type]) => [name, type, type === custom ? 'Boat' : null] as const); + const kinds = [ + { mimetype: MimeType.PHONE, value: '+49 30 1', map: 'phones', entry: { number: '+49 30 1', features: { voice: true } }, types: cases(PhoneType, PhoneType.CUSTOM) }, + { mimetype: MimeType.EMAIL, value: 'typed@example.org', map: 'emails', entry: { address: 'typed@example.org' }, types: cases(EmailType, EmailType.CUSTOM) }, + { mimetype: MimeType.WEBSITE, value: 'https://typed.example', map: 'links', entry: { uri: 'https://typed.example' }, types: cases(WebsiteType, WebsiteType.CUSTOM) }, + ]; + const typeOf = (row: Row) => [Number(row.data2), row.data3 ?? null]; + + describe.each(kinds)('$map', ({ mimetype, value, map, entry, types }) => { + it.each(types)('%s, on a contact created on the device', async (_name, type, label) => { + const h = new Harness(); + const rawId = h.device.user.insertContact('usera@example.org', [ + { [Data.MIMETYPE]: MimeType.STRUCTURED_NAME, data1: 'Typed Person', data2: 'Typed', data3: 'Person' }, + { [Data.MIMETYPE]: mimetype, data1: value, data2: type, data3: label }, + ]); + await h.upload(rawId); + const [row] = h.device.rows('data', 'raw_contact_id = ? AND mimetype = ?', [rawId, mimetype]); + expect(typeOf(row)).toEqual([type, label]); + const local = await h.contact(rawId); + expect(local.dirty).toBe(false); + expect(h.planner.planDownload(h.card(h.cardIdOf(local)), local, h.ctx).effect).toBe('none'); + }); + + it.each(types)('%s, chosen on the device for a synced entry', async (_name, type, label) => { + const h = new Harness(); + const { id, rawId } = await h.seed({ name: { full: 'Typed Person' }, [map]: { e1: entry } }); + h.device.user.updateData(h.rowsByKey(rawId)[`${map}:e1`]._id as number, { data2: type, data3: label }); + await h.upload(rawId); + expect(typeOf(h.rowsByKey(rawId)[`${map}:e1`])).toEqual([type, label]); + expect(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).effect).toBe('none'); + }); + }); +}); + describe('contacts upload: an address edited as one line (AOSP Contacts)', () => { const munich = () => ({ name: { full: 'Maria Muster' }, diff --git a/src/device-sync/contacts/types-map.ts b/src/device-sync/contacts/types-map.ts index bd39d86c..d8d9c290 100644 --- a/src/device-sync/contacts/types-map.ts +++ b/src/device-sync/contacts/types-map.ts @@ -34,9 +34,22 @@ function labelled(label: unknown, type: number, custom: number): TypeCells { return typeof label === 'string' && label.trim() ? { type: custom, label } : { type, label: null }; } +/** + * Android types RFC 9553 has no word for upload as a label naming them; that + * label (exactly as written) names the type again on the way back. + */ +function namedType(labels: Record, label: unknown): number | null { + const hit = Object.entries(labels).find(([, name]) => name === label); + return hit ? Number(hit[0]) : null; +} + // ─── Emails ──────────────────────────────────────────── +const EMAIL_LABELS: Record = { [EmailType.MOBILE]: 'mobile' }; + export function emailType(entry: { contexts?: unknown; label?: unknown }): TypeCells { + const named = namedType(EMAIL_LABELS, entry.label); + if (named !== null) return { type: named, label: null }; const type = on(entry.contexts, 'private') ? EmailType.HOME : on(entry.contexts, 'work') ? EmailType.WORK : EmailType.OTHER; return labelled(entry.label, type, EmailType.CUSTOM); } @@ -45,7 +58,7 @@ export function emailFlags(type: number | null, label: string | null): EntryFlag switch (type) { case EmailType.HOME: return { contexts: ['private'], label: null }; case EmailType.WORK: return { contexts: ['work'], label: null }; - case EmailType.MOBILE: return { contexts: [], label: 'mobile' }; + case EmailType.MOBILE: return { contexts: [], label: EMAIL_LABELS[EmailType.MOBILE] }; case EmailType.CUSTOM: return { contexts: [], label }; default: return { contexts: [], label: null }; } @@ -53,10 +66,16 @@ export function emailFlags(type: number | null, label: string | null): EntryFlag // ─── Phones ──────────────────────────────────────────── -/** First match wins, as in the design's table; `cell` is read as `mobile`. */ +/** + * First match wins, as in the design's table; `cell` is read as `mobile`. A + * label naming a type RFC 9553 lacks, next to the feature that type uploads + * with, is that type (a custom "car" uploads without the feature). + */ export function phoneType(entry: { features?: unknown; contexts?: unknown; label?: unknown }): TypeCells { const f = (n: string) => on(entry.features, n); const c = (n: string) => on(entry.contexts, n); + const named = namedType(PHONE_LABELS, entry.label); + if (named !== null && (phoneFlags(named, null).features ?? []).every(f)) return { type: named, label: null }; const mobile = f('mobile') || f('cell'); let type: number = PhoneType.OTHER; if (mobile && c('work')) type = PhoneType.WORK_MOBILE; @@ -67,6 +86,7 @@ export function phoneType(entry: { features?: unknown; contexts?: unknown; label else if (f('pager') && c('work')) type = PhoneType.WORK_PAGER; else if (f('pager')) type = PhoneType.PAGER; else if (f('textphone')) type = PhoneType.TTY_TDD; + else if (f('main-number') && c('work')) type = PhoneType.COMPANY_MAIN; else if (f('main-number')) type = PhoneType.MAIN; else if (c('work')) type = PhoneType.WORK; else if (c('private')) type = PhoneType.HOME; @@ -83,6 +103,7 @@ const PHONE_LABELS: Record = { [PhoneType.RADIO]: 'radio', [PhoneType.TELEX]: 'telex', [PhoneType.ASSISTANT]: 'assistant', + [PhoneType.MMS]: 'mms', }; export function phoneFlags(type: number | null, label: string | null): EntryFlags { @@ -99,7 +120,7 @@ export function phoneFlags(type: number | null, label: string | null): EntryFlag case PhoneType.COMPANY_MAIN: return { contexts: ['work'], features: ['main-number'], label: null }; case PhoneType.WORK: return { contexts: ['work'], features: ['voice'], label: null }; case PhoneType.HOME: return { contexts: ['private'], features: ['voice'], label: null }; - case PhoneType.MMS: return { contexts: [], features: ['text'], label: null }; + case PhoneType.MMS: return { contexts: [], features: ['text'], label: PHONE_LABELS[PhoneType.MMS] }; case PhoneType.CUSTOM: return { contexts: [], features: [], label }; case null: case PhoneType.OTHER: return { contexts: [], features: [], label: null }; @@ -124,6 +145,8 @@ export function postalFlags(type: number | null, label: string | null): EntryFla } export function websiteType(entry: { contexts?: unknown; label?: unknown }): TypeCells { + const named = namedType(WEBSITE_LABELS, entry.label); + if (named !== null) return { type: named, label: null }; const type = on(entry.contexts, 'private') ? WebsiteType.HOME : on(entry.contexts, 'work') ? WebsiteType.WORK : WebsiteType.OTHER; return labelled(entry.label, type, WebsiteType.CUSTOM); } From d34641d1f9a2495a877bb1d2812d106f6377ce82 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:17:56 +0200 Subject: [PATCH 046/334] fix: sync contacts whose photo is too large for the device #34 A contact's rows are written in one provider batch, and one batch is one Binder transaction of at most 1 MB. A contact with a larger photo failed with `tooLarge` on every run, stayed on `stale`, never showed on the device, and its device edits never uploaded. Photos above about 512 KiB are now kept on the server only: the contact is written without the photo. The shadow records that the device holds no row for the card's photo, so the missing row is not read as a deletion. The same applies to a photo whose bytes could not be fetched, which an unrelated edit of the contact used to delete from the server. A photo set on the device still replaces the server's, and one deleted there is still deleted on the server. Checked with planner tests for the insert, the size limit, a photo that could not be fetched and a merge, and end to end through the engine with the probe's 900 KB photo. --- .../__tests__/contacts/lifecycle.test.ts | 83 ++++++++++++++++++- .../__tests__/contacts/merge.test.ts | 12 ++- .../__tests__/contacts/real-engine.test.ts | 24 +++++- src/device-sync/contacts/download.ts | 34 ++++++-- src/device-sync/contacts/members.ts | 7 +- src/device-sync/contacts/photo.ts | 32 ++++++- src/device-sync/contacts/upload.ts | 14 +++- 7 files changed, 186 insertions(+), 20 deletions(-) diff --git a/src/device-sync/__tests__/contacts/lifecycle.test.ts b/src/device-sync/__tests__/contacts/lifecycle.test.ts index 94c414ff..9e69cd74 100644 --- a/src/device-sync/__tests__/contacts/lifecycle.test.ts +++ b/src/device-sync/__tests__/contacts/lifecycle.test.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from 'vitest'; import { Data, MimeType } from '../../android-columns'; +import { MAX_PHOTO_BASE64 } from '../../contacts/photo'; import type { LocalContact } from '../../planner'; -import { JPEG, probeCard } from './fixtures'; +import { JPEG, JPEG2, probeCard } from './fixtures'; import { Harness, JMAP } from './harness'; /** The engine's view after every write: a clean contact whose rows the server card describes. */ @@ -100,6 +101,86 @@ describe('contacts lifecycle', () => { expect(plan.kind === 'upload' && Object.keys((plan.actions[0] as { patch: object }).patch)).toEqual(['emails/e1/address']); }); + it('keeps a photo too large for a provider batch on the server only, and never reads it as deleted', async () => { + const h = new Harness(); + // About 900 KB of JPEG: the fake provider refuses a batch over 1 MB, as Binder does. + const big = 'A'.repeat(1_200_000); + const { id, rawId } = await h.seed({ + name: { full: 'Big Photo' }, + emails: { e1: { address: 'a@example.org' } }, + media: { p: { kind: 'photo', uri: `data:image/jpeg;base64,${big}`, mediaType: 'image/jpeg' } }, + }); + // The contact is there, without the photo; its echo writes nothing. + expect(Object.keys(h.rowsByKey(rawId)).sort()).toEqual(['emails:e1', 'name']); + await expectSettled(h, id, rawId); + + h.device.user.updateData(h.rowsByKey(rawId)['emails:e1']._id as number, { data1: 'b@example.org' }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id, patch: { 'emails/e1/address': 'b@example.org' } }], + }); + await h.upload(rawId); + expect(h.card(id).media).toEqual({ p: { kind: 'photo', uri: `data:image/jpeg;base64,${big}`, mediaType: 'image/jpeg' } }); + await expectSettled(h, id, rawId); + + // A photo set on the device replaces it. + h.device.user.setPhoto(rawId, JPEG2); + h.devicePhotos.set(rawId, JPEG2); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id, patch: { 'media/p/uri': `data:image/jpeg;base64,${JPEG2}`, 'media/p/mediaType': 'image/jpeg' } }], + }); + await h.upload(rawId); + expect(h.device.photo(Number(h.rowsByKey(rawId)['media:p'].data14))).toBe(JPEG2); + await expectSettled(h, id, rawId); + }); + + it('writes a photo up to the budget of a provider batch, and none beyond it', async () => { + const h = new Harness(); + const seedPhoto = (b64: string) => h.seed({ name: { full: 'Sized' }, media: { p: { kind: 'photo', uri: `data:image/jpeg;base64,${b64}` } } }); + const fits = 'A'.repeat(MAX_PHOTO_BASE64); + const within = await seedPhoto(fits); + expect(h.device.photo(Number(h.rowsByKey(within.rawId)['media:p'].data14))).toBe(fits); + await expectSettled(h, within.id, within.rawId); + const beyond = await seedPhoto('A'.repeat(MAX_PHOTO_BASE64 + 4)); + expect(h.rowsByKey(beyond.rawId)['media:p']).toBeUndefined(); + await expectSettled(h, beyond.id, beyond.rawId); + }); + + it('never reads a photo whose bytes could not be fetched as deleted, and writes it once they can be', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed({ + name: { full: 'Blob Photo' }, + emails: { e1: { address: 'a@example.org' } }, + media: { p: { kind: 'photo', blobId: 'B1', mediaType: 'image/jpeg' } }, + }); + expect(h.rowsByKey(rawId)['media:p']).toBeUndefined(); + h.device.user.updateData(h.rowsByKey(rawId)['emails:e1']._id as number, { data1: 'b@example.org' }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id, patch: { 'emails/e1/address': 'b@example.org' } }], + }); + await h.upload(rawId); + expect(h.card(id).media).toEqual({ p: { kind: 'photo', blobId: 'B1', mediaType: 'image/jpeg' } }); + + // With the bytes at hand, the next write of the card brings the photo. + h.blobs.set('B1', JPEG); + h.server.serverUpdate('ContactCard', JMAP, id, { 'emails/e1/address': 'c@example.org' }); + await h.download(id); + expect(h.device.photo(Number(h.rowsByKey(rawId)['media:p'].data14))).toBe(JPEG); + await expectSettled(h, id, rawId); + }); + + it('does not read a server photo it could not write as deleted after merging it into an edited contact', async () => { + const h = new Harness(); + const { id, rawId } = await h.seed({ name: { full: 'Merge Photo' }, emails: { e1: { address: 'a@example.org' } }, phones: { p1: { number: '1' } } }); + // The server adds a photo too large to write while the device edits the phone. + h.device.user.updateData(h.rowsByKey(rawId)['phones:p1']._id as number, { data1: '2' }); + const big = 'A'.repeat(1_200_000); + h.server.serverUpdate('ContactCard', JMAP, id, { media: { p: { kind: 'photo', uri: `data:image/jpeg;base64,${big}` } } }); + await h.applyOk(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).ops); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id, patch: { 'phones/p1/number': '2' } }], + }); + }); + it('heals a baseline the provider stored differently from the prediction', async () => { const h = new Harness(); const { id, rawId } = await h.seed({ name: { full: 'Dr. Jane Q. Public' } }); diff --git a/src/device-sync/__tests__/contacts/merge.test.ts b/src/device-sync/__tests__/contacts/merge.test.ts index 2d07db5a..2879454b 100644 --- a/src/device-sync/__tests__/contacts/merge.test.ts +++ b/src/device-sync/__tests__/contacts/merge.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from 'vitest'; import { Data, MimeType, RawContacts } from '../../android-columns'; import type { ContactCardWire } from '../../planner'; -import { appleCard, googleCard } from './fixtures'; +import { appleCard, googleCard, probeCard } from './fixtures'; import { Harness, JMAP } from './harness'; /** A seeded card, edited on the device (in place, like AOSP) and on the server. */ @@ -85,6 +85,16 @@ describe('contacts merge: a dirty contact meets a new server version', () => { expect(Object.keys(patch.phones as object)).toEqual(['k1', 'k2']); }); + it('still uploads a photo deleted on the device after merging a server edit of another entry', async () => { + const { h, rawId, plan } = await bothEdited( + probeCard, + (hh, raw) => hh.device.user.deleteData(hh.rowsByKey(raw)['media:ph']._id as number), + { 'emails/zz9/address': 'new@example.org' }, + ); + expect(plan).toMatchObject({ conflicts: 0, stillDirty: true }); + expect(await patchOf(h, rawId)).toEqual({ media: null }); + }); + it('matches an entry both sides added instead of duplicating it', async () => { const { h, rawId, plan } = await bothEdited( appleCard, diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index 758b5e3c..f6a160a8 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it } from 'vitest'; import { Groups, MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; -import { addServerCards, CONTACTS_AUTHORITY, createHarness, type Harness } from '../engine/harness'; +import { addServerCards, CONTACTS_AUTHORITY, createHarness, rowWrites, type Harness } from '../engine/harness'; import type { SetTarget } from '../fakes/fake-jmap-server'; function real(options: Parameters[0] = {}): Harness { @@ -78,6 +78,28 @@ describe('contacts through the engine', () => { expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); }); + it('syncs a contact whose photo is too large for a provider batch, without the photo', async () => { + const h = real(); + // About 900 KB of JPEG as a data: URI (a camera photo set over CardDAV). + const media = { p1: { '@type': 'Media', kind: 'photo', uri: `data:image/jpeg;base64,${'A'.repeat(1_200_000)}`, mediaType: 'image/jpeg' } }; + const card = h.server.addCard('a', { uid: 'u-photo', name: { full: 'Photo Person' }, emails: { e1: { address: 'photo@example.org' } }, addressBookIds: { [h.book]: true }, media }); + addServerCards(h, ['Plain Person']); + + expect(await h.run()).toMatchObject({ outcome: 'ok', itemErrors: [] }); + expect(h.contacts().map((c) => c.name).sort()).toEqual(['Photo Person', 'Plain Person']); + expect(h.state()?.accounts.a.stale).toEqual([]); + const before = h.batches.log.length; + expect((await h.run()).outcome).toBe('ok'); + expect(rowWrites(h.batches.log.slice(before))).toEqual([]); + + // Its device edits upload, and the photo stays on the server. + const id = h.contactNamed('Photo Person')!.id; + const email = h.device.rows('data').find((d) => Number(d.raw_contact_id) === id && d.mimetype === MimeType.EMAIL)!; + h.device.user.updateData(Number(email._id), { data1: 'photo2@example.org' }); + expect(await h.run()).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); + expect(h.server.get('ContactCard', 'a', card)).toMatchObject({ emails: { e1: { address: 'photo2@example.org' } }, media }); + }); + it('keeps a group rename the server refused when a membership patch of that group goes through', async () => { const h = real(); const [ada] = addServerCards(h, ['Ada Lovelace']); diff --git a/src/device-sync/contacts/download.ts b/src/device-sync/contacts/download.ts index feacece2..0d8e41b3 100644 --- a/src/device-sync/contacts/download.ts +++ b/src/device-sync/contacts/download.ts @@ -22,7 +22,7 @@ import { localChanges, sameUnit } from './diff'; import { deletionsInferred, inPlaceEvidence, matchKind, reconcileKeys, type KindMatch } from './matching'; import { editableGroups, planMemberships, remoteMemberships, shadowMemberOf, withMemberOf, withoutMemberOf } from './members'; import { assertContact, deleteData, insertUnit, updateData, updateRawContact, updateUnit } from './ops'; -import { shadowOf } from './photo'; +import { isPhotoAbsent, shadowOf, withPhotoAbsent } from './photo'; import { KIND_ORDER, projectCard, rowMatchesUnit, SPECS, type Unit } from './project'; const ENTRY_KINDS = KIND_ORDER.filter((k) => k !== MimeType.GROUP_MEMBERSHIP); @@ -90,6 +90,8 @@ interface RowPlan { conflicts: number; stillDirty: boolean; evidence: boolean; + /** The card's photo is left without a row because it could not be written (the shadow's `~noPhoto`). */ + photoAbsent: boolean; } /** @@ -99,6 +101,7 @@ interface RowPlan { */ function cleanRows(local: LocalContact, remote: Unit[], keyMap: Map, base: Unit[], accepted: boolean): RowPlan { const ops: ProviderOp[] = []; + let photoAbsent = false; const rows = renameRows(local.rows, base, keyMap); const kinds = ENTRY_KINDS.map((k) => matchKind(k, rows, remote)); for (const kind of kinds) { @@ -130,11 +133,14 @@ function cleanRows(local: LocalContact, remote: Unit[], keyMap: Map matchKind(k, rows, renamedBase)); @@ -149,11 +156,14 @@ function mergeRows(local: LocalContact, base: Unit[], remote: Unit[], keyMap: Ma const mapped = new Set(keyMap.values()); for (const kind of kinds) { + const start = ops.length; const remoteOf = new Map(remote.filter((u) => u.mimetype === kind.mimetype).map((u) => [u.key, u])); const hasKeyed = kind.matched.some((m) => m.how === 'key'); const hasKeyless = kind.fresh.length > 0 || kind.matched.some((m) => m.how !== 'key'); const keyed = hasKeyed || !hasKeyless; - const deletions = kind.mimetype !== MimeType.STRUCTURED_NAME && deletionsInferred(kind, evidence); + // A photo the device never held (`~noPhoto`) was not deleted there. + const deletions = kind.mimetype !== MimeType.STRUCTURED_NAME && deletionsInferred(kind, evidence) + && !(kind.mimetype === MimeType.PHOTO && isPhotoAbsent(local.shadow)); const write = (row: LocalDataRow, unit: Unit) => { if (unit.mimetype === MimeType.PHOTO && !unit.extra) return; ops.push(updateUnit(row.id, unit, keyed)); @@ -209,8 +219,15 @@ function mergeRows(local: LocalContact, base: Unit[], remote: Unit[], keyMap: Ma } } if (fresh.length) stillDirty = true; + + if (kind.mimetype === MimeType.PHOTO) { + // The server's photo is left without a row it could not get, unless a local deletion of it waits for its upload. + const photo = [...remoteOf.values()][0]; + const deletionWaits = !!photo && deletions && kind.missing.some((u) => sameUnit(u, photo)); + photoAbsent = !!photo && !kind.rows.length && !ops.slice(start).some((o) => o.op === 'insert') && !deletionWaits; + } } - return { ops, conflicts, stillDirty, evidence }; + return { ops, conflicts, stillDirty, evidence, photoAbsent }; } export interface CleanWriteOptions { @@ -251,7 +268,7 @@ export function cleanWrite(local: LocalContact, card: ContactCardWire, ctx: Ctx, groupRowIdBySourceId: (g) => ctx.groupRowIdBySourceId(g), editable: editableGroups(ctx, accountOf(local, ctx)), }); - const shadow = withMemberOf(shadowOf(server), members.memberOf); + const shadow = withPhotoAbsent(withMemberOf(shadowOf(server), members.memberOf), rows.photoAbsent); const raw = changedRawColumns(local, { ...cardColumns(server, shadow, ctx), ...(options.raw ?? {}), @@ -282,7 +299,8 @@ function insertPlan(card: ContactCardWire, ctx: Ctx): DownloadPlan { parent: { ref: 0 }, groupRowIdBySourceId: (g) => ctx.groupRowIdBySourceId(g), }); - const shadow = withMemberOf(shadowOf(card), members.memberOf); + // A photo without bytes to write (too large, or not fetched) stays on the server only. + const shadow = withPhotoAbsent(withMemberOf(shadowOf(card), members.memberOf), units.some((u) => u.mimetype === MimeType.PHOTO && !u.extra)); const ops: ProviderOp[] = [ { op: 'insert', table: 'raw_contacts', values: { [RawContacts.SOURCE_ID]: identity, [RawContacts.DIRTY]: 0, ...cardColumns(card, shadow, ctx) } }, ...units.filter((u) => u.mimetype !== MimeType.PHOTO || u.extra).map((u) => insertUnit({ ref: 0 }, u, true)), @@ -349,7 +367,7 @@ export function planContactDownload(card: ContactCardWire, local: LocalContact | parent: local.rawContactId, groupRowIdBySourceId: (g) => ctx.groupRowIdBySourceId(g), }); - const shadow = withMemberOf(shadowOf(server), members.memberOf); + const shadow = withPhotoAbsent(withMemberOf(shadowOf(server), members.memberOf), rows.photoAbsent); const raw = changedRawColumns(local, cardColumns(server, shadow, ctx)); const ops: ProviderOp[] = []; if (Object.keys(raw).length) ops.push(updateRawContact(local.rawContactId, raw)); diff --git a/src/device-sync/contacts/members.ts b/src/device-sync/contacts/members.ts index 118f3cb7..3a331d1a 100644 --- a/src/device-sync/contacts/members.ts +++ b/src/device-sync/contacts/members.ts @@ -24,6 +24,7 @@ import type { ProviderOp } from '../types'; import { text } from './cells'; import { entryKey } from './keys'; import { deleteData, insertUnit, updateData } from './ops'; +import { NO_PHOTO } from './photo'; import { MEMBER_OF, type Unit } from './project'; /** @@ -55,10 +56,10 @@ export function shadowMemberOf(shadow: ContactCardWire | null): string[] | null return Array.isArray(v) && v.every((g) => typeof g === 'string') ? [...v] : null; } -/** A card without the `~memberOf` a shadow carries. */ +/** A card without the device-only members a shadow carries: `~memberOf`, and `~noPhoto` (photo.ts). */ export function withoutMemberOf(card: ContactCardWire): ContactCardWire { - if (!(MEMBER_OF in card)) return card; - const { [MEMBER_OF]: _drop, ...rest } = card; + if (!(MEMBER_OF in card) && !(NO_PHOTO in card)) return card; + const { [MEMBER_OF]: _drop, [NO_PHOTO]: _photo, ...rest } = card; return rest as ContactCardWire; } diff --git a/src/device-sync/contacts/photo.ts b/src/device-sync/contacts/photo.ts index 05eb171e..30ccac2e 100644 --- a/src/device-sync/contacts/photo.ts +++ b/src/device-sync/contacts/photo.ts @@ -11,6 +11,14 @@ import { clone } from '../common/json'; import type { ContactCardWire, ContactsContext } from '../planner'; const HASH_URI = 'sha256:'; + +/** + * The largest photo (base64 characters, about 512 KiB of JPEG) written to the + * device: one applyBatch is one Binder transaction of at most 1 MB, and a + * contact's rows travel in one op group, which is never split. A larger photo + * stays on the server only (docs/device-sync.md, "Limits and performance"). + */ +export const MAX_PHOTO_BASE64 = 700_000; const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; const B64_INDEX = new Map([...B64].map((c, i) => [c, i])); @@ -58,13 +66,33 @@ export function photoEntry(card: ContactCardWire): { key: string; entry: Contact return null; } -/** Bytes (base64) and hash of a photo entry; bytes are null for a shadow's hash-only entry. */ +/** + * Bytes (base64) and hash of a photo entry; bytes are null for a shadow's + * hash-only entry and for a photo too large to write (MAX_PHOTO_BASE64). + */ export function photoPayload(entry: ContactMedia, ctx: Pick): { b64: string | null; hash: string } | null { const uri = typeof entry.uri === 'string' ? entry.uri : ''; if (uri.startsWith(HASH_URI)) return { b64: null, hash: uri }; const b64 = dataUriBase64(uri) ?? ctx.photoBytes(entry as { uri?: string; blobId?: string; mediaType?: string }); const hash = b64 ? hashBase64(b64) : null; - return b64 && hash ? { b64, hash } : null; + if (!b64 || !hash) return null; + return { b64: b64.length > MAX_PHOTO_BASE64 ? null : b64, hash }; +} + +/** + * Set in a shadow whose card's photo has no row on the device because it + * could not be written (too large, or its bytes could not be fetched), so the + * missing row is no deletion. Device-only, like `~memberOf`: stripped before + * any projection or patch. + */ +export const NO_PHOTO = '~noPhoto'; + +export function isPhotoAbsent(shadow: ContactCardWire | null): boolean { + return shadow?.[NO_PHOTO] === true; +} + +export function withPhotoAbsent(shadow: ContactCardWire, absent: boolean): ContactCardWire { + return absent ? { ...shadow, [NO_PHOTO]: true } : shadow; } /** The shadow of a card: every `data:` photo URI replaced by the hash of its bytes. */ diff --git a/src/device-sync/contacts/upload.ts b/src/device-sync/contacts/upload.ts index dbd8b9a1..67542358 100644 --- a/src/device-sync/contacts/upload.ts +++ b/src/device-sync/contacts/upload.ts @@ -30,8 +30,8 @@ import { deletionsInferred, inPlaceEvidence, matchKind, type KindMatch } from '. import { shadowMemberOf, withMemberOf, withoutMemberOf } from './members'; import { NAME_COLUMNS, namePatch } from './name'; import { assertContact, deleteWhereId, updateRawContact, updateWhereId } from './ops'; -import { isHashUri, jpegDataUri, shadowOf } from './photo'; -import { KIND_ORDER, SPECS } from './project'; +import { isHashUri, isPhotoAbsent, jpegDataUri, photoEntry, shadowOf, withPhotoAbsent } from './photo'; +import { isEmptyRow, KIND_ORDER, SPECS } from './project'; type Ctx = ContactsContext; @@ -116,7 +116,8 @@ function photoChanges(local: LocalContact, kind: KindMatch, evidence: boolean, c } else { out.added.push({ map: 'media', value: { kind: 'photo', uri, mediaType: 'image/jpeg' } }); } - } else if (!row && deletionsInferred(kind, evidence)) { + } else if (!row && deletionsInferred(kind, evidence) && !isPhotoAbsent(local.shadow)) { + // (A photo the device could not be given was never there to delete.) for (const unit of kind.missing) out.removed.push(...unitEntries(kind.mimetype, unit.key)); } } @@ -286,7 +287,12 @@ export function planContactAccepted(local: LocalContact, server: ContactCardWire // Edited again during the upload: identity and shadow, and the baselines of // the uploaded units set to what was uploaded; rows and DIRTY stay. const changes = local.shadow ? collectChanges(local, withoutMemberOf(local.shadow), ctx) : null; - const shadow = withMemberOf(shadowOf(withoutMemberOf(server)), shadowMemberOf(local.shadow) ?? (local.shadow ? null : [])); + // The rows stay, so a photo the device could not be given is still not there. + const photoRow = local.rows.some((r) => r.mimetype === MimeType.PHOTO && !isEmptyRow(MimeType.PHOTO, r.cells)); + const shadow = withPhotoAbsent( + withMemberOf(shadowOf(withoutMemberOf(server)), shadowMemberOf(local.shadow) ?? (local.shadow ? null : [])), + isPhotoAbsent(local.shadow) && !photoRow && photoEntry(server) !== null, + ); const keep: ProviderOp[] = [ updateRawContact(local.rawContactId, { ...raw, [RawContacts.SYNC2]: JSON.stringify(shadow) }), ]; From 6f574de0b9625e67df79b2113e2126ea6e243250 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:18:33 +0200 Subject: [PATCH 047/334] fix: keep small contact photos on the device and on the server #34 ContactsProvider keeps a photo within its 96 px thumbnail as the thumbnail alone, without a PHOTO_FILE_ID, and the thumbnail does not read back. Such a Photo row looked empty to the planner. Every download of the card inserted the photo again (two, then three rows), and any edit of the contact on the device deleted the photo on the server. A Photo row now always counts as the contact's photo. Photo rows that earlier downloads added again are deleted by the contact's next clean write, one row is kept, and nothing reaches the server for them. A photo set on the device is still uploaded. Checked with planner tests and end to end through the engine, with a provider that stores small photos as thumbnails as the device does. --- src/device-sync/__tests__/contacts/harness.ts | 44 ++++++++++++- .../__tests__/contacts/lifecycle.test.ts | 61 +++++++++++++++++++ .../__tests__/contacts/real-engine.test.ts | 26 +++++++- src/device-sync/contacts/project.ts | 9 ++- 4 files changed, 134 insertions(+), 6 deletions(-) diff --git a/src/device-sync/__tests__/contacts/harness.ts b/src/device-sync/__tests__/contacts/harness.ts index 663dfbd0..27f1a243 100644 --- a/src/device-sync/__tests__/contacts/harness.ts +++ b/src/device-sync/__tests__/contacts/harness.ts @@ -4,7 +4,7 @@ * way the engine applies them (one op group per batch); uploads go through * the fake server's patch handling and come back through its normalisation. */ -import { GroupMembership, Groups, RawContacts } from '../../android-columns'; +import { Data, GroupMembership, Groups, RawContacts } from '../../android-columns'; import { makeKeyMinter, parseObjectRef, uuidFrom } from '../../common/ids'; import type { GroupRights } from '../../contacts/members'; import { contactsPlanner } from '../../contacts/planner'; @@ -17,7 +17,7 @@ import type { UploadAction, UploadPlan, } from '../../planner'; -import { CONTACTS_AUTHORITY, type BatchResult, type ProviderPort, type Row } from '../../types'; +import { CONTACTS_AUTHORITY, type BatchResult, type OpResult, type ProviderOp, type ProviderPort, type Row } from '../../types'; import { FakeJmapServer } from '../fakes/fake-jmap-server'; import { FakeDeviceProviders } from '../fakes/fake-provider'; @@ -35,6 +35,40 @@ function rowsOf(res: { columns: string[]; rows: unknown[][] }): Row[] { return res.rows.map((cells) => Object.fromEntries(res.columns.map((c, i) => [c, cells[i]])) as Row); } +/** + * ContactsProvider keeps a photo no larger than its 96 px thumbnail as the + * thumbnail only: PHOTO_FILE_ID (DATA14) stays NULL, and the thumbnail + * (DATA15) does not read back. The fake stores every photo with a display + * photo; this makes the photo rows a batch wrote thumbnail-only, as a device + * does with the test fixtures' 1×1 JPEGs. + */ +export function keepThumbnailsOnly(device: FakeDeviceProviders, ops: ProviderOp[], results: OpResult[]): void { + ops.forEach((op, i) => { + if ((op.op !== 'insert' && op.op !== 'update') || op.table !== 'data') return; + const bytes = op.values[Data.DATA15]; + if (!bytes || typeof bytes !== 'object') return; + const id = op.op === 'insert' ? results[i]?.id : op.id; + const row = id === undefined ? undefined : device.table('data').get(id); + if (row) row[Data.DATA14] = null; + }); +} + +/** A provider port whose photo writes come out thumbnail-only (see `keepThumbnailsOnly`). */ +export function thumbnailOnlyPort(device: FakeDeviceProviders, port: ProviderPort): ProviderPort { + return { + accountName: port.accountName, + authority: port.authority, + query: (q) => port.query(q), + readSyncState: () => port.readSyncState(), + readPhoto: (id, px) => port.readPhoto(id, px), + applyBatch: async (ops) => { + const res = await port.applyBatch(ops); + if (res.ok) keepThumbnailsOnly(device, ops, res.results); + return res; + }, + }; +} + export class Harness { readonly device = new FakeDeviceProviders(); readonly port: ProviderPort = this.device.port(ACCOUNT, CONTACTS_AUTHORITY); @@ -49,6 +83,8 @@ export class Harness { readonly unselected = new Set(); /** When false the context has no `groupsOf` (memberships not synced). */ groupIndex = true; + /** When true the photos the planner writes come out thumbnail-only (see `keepThumbnailsOnly`). */ + thumbnailPhotos = false; readonly ctx: ContactsContext & GroupRights; constructor() { @@ -98,7 +134,9 @@ export class Harness { async apply(group: OpGroup): Promise { if (!group.ops.length) return { ok: true, results: [] }; - return this.port.applyBatch(group.ops); + const res = await this.port.applyBatch(group.ops); + if (res.ok && this.thumbnailPhotos) keepThumbnailsOnly(this.device, group.ops, res.results); + return res; } async applyOk(group: OpGroup): Promise { diff --git a/src/device-sync/__tests__/contacts/lifecycle.test.ts b/src/device-sync/__tests__/contacts/lifecycle.test.ts index 9e69cd74..8d66eff4 100644 --- a/src/device-sync/__tests__/contacts/lifecycle.test.ts +++ b/src/device-sync/__tests__/contacts/lifecycle.test.ts @@ -181,6 +181,67 @@ describe('contacts lifecycle', () => { }); }); + describe('a small photo the provider keeps as its thumbnail only (no PHOTO_FILE_ID)', () => { + const photo = { ph: { kind: 'photo', uri: `data:image/jpeg;base64,${JPEG}`, mediaType: 'image/jpeg' } }; + const small = () => ({ name: { full: 'Photo Smalltest' }, emails: { e1: { address: 'small@example.org' } }, notes: { n1: { note: 'v1' } }, media: photo }); + const photos = (h: Harness, rawId: number) => h.device.rows('data', 'raw_contact_id = ? AND mimetype = ?', [rawId, MimeType.PHOTO]); + + it('keeps one row through downloads and merges, and never reads it as deleted', async () => { + const h = new Harness(); + h.thumbnailPhotos = true; + const { id, rawId } = await h.seed(small()); + expect(photos(h, rawId)).toMatchObject([{ data14: null, data_sync1: 'media:ph' }]); + await expectSettled(h, id, rawId); + for (const note of ['v2', 'v3']) { + h.server.serverUpdate('ContactCard', JMAP, id, { 'notes/n1/note': note }); + await h.download(id); + expect(photos(h, rawId)).toHaveLength(1); + } + // A merge into an edited contact. + h.device.user.updateData(h.rowsByKey(rawId)['emails:e1']._id as number, { data1: 'small2@example.org' }); + h.server.serverUpdate('ContactCard', JMAP, id, { 'notes/n1/note': 'v4' }); + await h.applyOk(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).ops); + expect(photos(h, rawId)).toHaveLength(1); + // An edit of another field uploads alone. + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id, patch: { 'emails/e1/address': 'small2@example.org' } }], + }); + await h.upload(rawId); + expect(h.card(id).media).toEqual(photo); + expect(photos(h, rawId)).toHaveLength(1); + await expectSettled(h, id, rawId); + // A photo set on the device (it gets a display photo) replaces it. + h.device.user.setPhoto(rawId, JPEG2); + h.devicePhotos.set(rawId, JPEG2); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id, patch: { 'media/ph/uri': `data:image/jpeg;base64,${JPEG2}`, 'media/ph/mediaType': 'image/jpeg' } }], + }); + }); + + it('heals the photo rows the same photo was inserted as again, keeping one and the server photo', async () => { + const h = new Harness(); + h.thumbnailPhotos = true; + const { id, rawId } = await h.seed(small()); + const [row] = photos(h, rawId); + const again = { [Data.MIMETYPE]: MimeType.PHOTO, [Data.DATA15]: { b64: JPEG }, data_sync1: row.data_sync1, data_sync2: row.data_sync2, data_sync3: row.data_sync3 }; + for (let i = 0; i < 2; i++) await h.applyOk({ ref: 'again', ops: [{ op: 'insert', table: 'data', values: { ...again, [Data.RAW_CONTACT_ID]: rawId } }] }); + expect(photos(h, rawId)).toHaveLength(3); + // An edit on the device uploads without touching the photo, and its accepted write keeps one row. + h.device.user.updateData(h.rowsByKey(rawId)['notes:n1']._id as number, { data1: 'v2 edited on device' }); + expect(h.planner.planUpload(await h.contact(rawId), h.ctx)).toEqual({ + kind: 'upload', actions: [{ kind: 'update', id, patch: { 'notes/n1/note': 'v2 edited on device' } }], + }); + await h.upload(rawId); + expect(photos(h, rawId)).toHaveLength(1); + expect(h.card(id).media).toEqual(photo); + await expectSettled(h, id, rawId); + // An unchanged card downloaded again (a full reconcile) heals too. + for (let i = 0; i < 2; i++) await h.applyOk({ ref: 'again', ops: [{ op: 'insert', table: 'data', values: { ...again, [Data.RAW_CONTACT_ID]: rawId } }] }); + await h.applyOk(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).ops); + expect(photos(h, rawId)).toHaveLength(1); + }); + }); + it('heals a baseline the provider stored differently from the prediction', async () => { const h = new Harness(); const { id, rawId } = await h.seed({ name: { full: 'Dr. Jane Q. Public' } }); diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index f6a160a8..df779301 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -3,11 +3,13 @@ // themselves are tested in the other files of this folder. import { describe, expect, it } from 'vitest'; -import { Groups, MimeType } from '../../android-columns'; +import { Data, Groups, MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; import { addServerCards, CONTACTS_AUTHORITY, createHarness, rowWrites, type Harness } from '../engine/harness'; import type { SetTarget } from '../fakes/fake-jmap-server'; +import { JPEG } from './fixtures'; +import { thumbnailOnlyPort } from './harness'; function real(options: Parameters[0] = {}): Harness { const h = createHarness(options); @@ -78,6 +80,28 @@ describe('contacts through the engine', () => { expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); }); + it('keeps one row for a small photo the provider stores as its thumbnail, and never deletes it on the server', async () => { + const h = real(); + const base = h.deps.provider; + h.deps.provider = (name, authority) => thumbnailOnlyPort(h.device, base(name, authority)); + const media = { ph: { kind: 'photo', uri: `data:image/jpeg;base64,${JPEG}`, mediaType: 'image/jpeg' } }; + const card = h.server.addCard('a', { uid: 'u-small', name: { full: 'Photo Smalltest' }, notes: { n1: { note: 'v1' } }, addressBookIds: { [h.book]: true }, media }); + expect((await h.run()).outcome).toBe('ok'); + const id = h.contactNamed('Photo Smalltest')!.id; + const rowsOf = (mimetype: string) => h.device.rows('data').filter((d) => Number(d.raw_contact_id) === id && d.mimetype === mimetype); + expect(rowsOf(MimeType.PHOTO)).toMatchObject([{ data14: null }]); + + for (const note of ['v2', 'v3']) { + h.server.serverUpdate('ContactCard', 'a', card, { 'notes/n1/note': note }); + expect((await h.run()).outcome).toBe('ok'); + expect(rowsOf(MimeType.PHOTO)).toHaveLength(1); + } + h.device.user.updateData(Number(rowsOf(MimeType.NOTE)[0]._id), { [Data.DATA1]: 'v4 edited on device' }); + expect(await h.run()).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); + expect(h.server.get('ContactCard', 'a', card)).toMatchObject({ notes: { n1: { note: 'v4 edited on device' } }, media }); + expect(rowsOf(MimeType.PHOTO)).toHaveLength(1); + }); + it('syncs a contact whose photo is too large for a provider batch, without the photo', async () => { const h = real(); // About 900 KB of JPEG as a data: URI (a camera photo set over CardDAV). diff --git a/src/device-sync/contacts/project.ts b/src/device-sync/contacts/project.ts index b548fd13..1ff0a655 100644 --- a/src/device-sync/contacts/project.ts +++ b/src/device-sync/contacts/project.ts @@ -318,11 +318,16 @@ export function rowMatchesUnit(u: Pick, stored: Row, return spec.columns.every((c) => same(u.cells[c], stored[c], c)); } -/** Whether a row has nothing in it (an editor's blank nickname or note). */ +/** + * Whether a row has nothing in it (an editor's blank nickname or note). A + * photo row always holds a photo: ContactsProvider keeps one within its 96 px + * thumbnail as the thumbnail alone, without PHOTO_FILE_ID, and the thumbnail + * (DATA15, a blob) does not read back. + */ export function isEmptyRow(mimetype: string, cells: Row): boolean { const spec = SPECS[mimetype]; if (!spec) return true; - if (mimetype === MimeType.PHOTO) return cellText(cells[Data.DATA14]) === null; + if (mimetype === MimeType.PHOTO) return false; if (mimetype === MimeType.GROUP_MEMBERSHIP) return cellText(cells[GroupMembership.GROUP_SOURCE_ID]) === null && cellText(cells[Data.DATA1]) === null; return spec.valueColumns.every((c) => text(cells[c]) === null); } From 534cb8239eb33804ba88218d75c82eb8be7c787e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:19:16 +0200 Subject: [PATCH 048/334] fix: keep the groups of a contact created on the device #34 A contact created on the device and put into a group before its first sync lost that group: the write after its create took the rows' own memberships as what the server already had, found them missing there and deleted the rows, so the group card was never patched. The create's write now starts from no memberships, as its fallback already did: the device's memberships count as additions, stay on the device, keep the contact dirty until the group cards show them, and upload as patches of the group cards. Checked with a planner test and end to end through the engine. --- .../__tests__/contacts/groups.test.ts | 24 ++++++++++++++++++- .../__tests__/contacts/real-engine.test.ts | 17 ++++++++++++- src/device-sync/contacts/download.ts | 4 +++- src/device-sync/contacts/upload.ts | 4 +++- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/device-sync/__tests__/contacts/groups.test.ts b/src/device-sync/__tests__/contacts/groups.test.ts index d9ac7869..786271b9 100644 --- a/src/device-sync/__tests__/contacts/groups.test.ts +++ b/src/device-sync/__tests__/contacts/groups.test.ts @@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'; import { Data, Groups, MimeType } from '../../android-columns'; import type { ContactCardWire, OpGroup, UploadPlan } from '../../planner'; import { appleCard, googleCard } from './fixtures'; -import { Harness, JMAP } from './harness'; +import { ACCOUNT, Harness, JMAP } from './harness'; function actionsOf(plan: UploadPlan) { if (plan.kind !== 'upload') throw new Error(`expected an upload, got ${plan.kind}`); @@ -130,6 +130,28 @@ describe('contacts groups: membership edits', () => { }); }); + it('keeps the groups of a contact created on the device and adds it to their cards', async () => { + const h = new Harness(); + const gid = h.addCard({ uid: 'urn:uuid:g7', kind: 'group', name: { full: 'Team' }, members: { 'urn:uuid:someone': true } }); + await h.download(gid); + const g = await localGroup(h, gid); + const rawId = h.device.user.insertContact(ACCOUNT, [ + { [Data.MIMETYPE]: MimeType.STRUCTURED_NAME, data1: 'New Person', data2: 'New', data3: 'Person' }, + { [Data.MIMETYPE]: MimeType.GROUP_MEMBERSHIP, [Data.DATA1]: g.groupId }, + ]); + await h.upload(rawId); + expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); + expect(h.dirty(rawId)).toBe(true); + const uid = (await h.contact(rawId)).shadow!.uid as string; + const actions = h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx); + expect(actions).toEqual([{ kind: 'update', id: gid, patch: { [`members/${uid}`]: true } }]); + h.send(actions); + await h.applyOk(h.planner.planGroupAccepted(g, h.card(gid), h.ctx).ops); + await h.upload(rawId); + expect(h.dirty(rawId)).toBe(false); + expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); + }); + it('sends the whole map for the first member of an empty group', async () => { const h = new Harness(); const empty = h.addCard({ uid: 'urn:uuid:g3', kind: 'group', name: { full: 'Empty' } }); diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index df779301..2edfebe5 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it } from 'vitest'; import { Data, Groups, MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; -import { addServerCards, CONTACTS_AUTHORITY, createHarness, rowWrites, type Harness } from '../engine/harness'; +import { addServerCards, ANDROID_ACCOUNT, CONTACTS_AUTHORITY, createHarness, rowWrites, type Harness } from '../engine/harness'; import type { SetTarget } from '../fakes/fake-jmap-server'; import { JPEG } from './fixtures'; import { thumbnailOnlyPort } from './harness'; @@ -80,6 +80,21 @@ describe('contacts through the engine', () => { expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); }); + it('puts a contact created on the device into the group it was added to', async () => { + const h = real(); + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { other: true }, addressBookIds: { [h.book]: true } }); + expect((await h.run()).outcome).toBe('ok'); + const id = h.device.user.insertContact(ANDROID_ACCOUNT, [ + { [Data.MIMETYPE]: MimeType.STRUCTURED_NAME, [Data.DATA1]: 'New Person' }, + { [Data.MIMETYPE]: MimeType.GROUP_MEMBERSHIP, [Data.DATA1]: Number(h.device.rows('groups')[0]._id) }, + ]); + + expect(await h.run()).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 1, updated: 1 } } }); + const uid = h.server.all('ContactCard', 'a').find((c) => c.kind !== 'group')!.uid as string; + expect(h.server.get('ContactCard', 'a', group)?.members).toEqual({ other: true, [uid]: true }); + expect(memberships(h, id)).toHaveLength(1); + }); + it('keeps one row for a small photo the provider stores as its thumbnail, and never deletes it on the server', async () => { const h = real(); const base = h.deps.provider; diff --git a/src/device-sync/contacts/download.ts b/src/device-sync/contacts/download.ts index 0d8e41b3..1e1575e7 100644 --- a/src/device-sync/contacts/download.ts +++ b/src/device-sync/contacts/download.ts @@ -239,6 +239,8 @@ export interface CleanWriteOptions { raw?: WriteRow; /** The rows' changes were just uploaded (planAccepted): a changed photo is the server's now. */ accepted?: boolean; + /** The memberships the rows held at the last write, when there is no shadow to say: a new contact's are none. */ + memberBase?: string[]; } /** The JMAP account a local contact belongs to. */ @@ -260,7 +262,7 @@ export function cleanWrite(local: LocalContact, card: ContactCardWire, ctx: Ctx, const rows = cleanRows(local, remote, keyMap, base, options.accepted ?? false); const members = planMemberships({ rows: local.rows, - base: shadowMemberOf(local.shadow), + base: shadowMemberOf(local.shadow) ?? options.memberBase ?? null, remote: remoteMemberships(server, ctx), evidence: rows.evidence, restore: true, diff --git a/src/device-sync/contacts/upload.ts b/src/device-sync/contacts/upload.ts index 67542358..3c1e7c1c 100644 --- a/src/device-sync/contacts/upload.ts +++ b/src/device-sync/contacts/upload.ts @@ -280,7 +280,9 @@ export function planContactAccepted(local: LocalContact, server: ContactCardWire } const identity = objectRef(accountOf(local, ctx), server.id); const raw = { [RawContacts.SOURCE_ID]: identity, [RawContacts.SYNC3]: null, [RawContacts.SYNC4]: null }; - const write = cleanWrite(local, server, ctx, { assertDirty: null, clearDirty: true, accepted: true, raw }); + // Just created: the groups it was put in on the device are all still to upload. + const memberBase = local.shadow ? undefined : []; + const write = cleanWrite(local, server, ctx, { assertDirty: null, clearDirty: true, accepted: true, raw, memberBase }); const ops = group(local, write.ops.length ? write.ops : [assertContact(local, null)]); ops.ref = identity; From c635ccd586913dc9055aab535526ab4b19b2bb8b Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:49:53 +0200 Subject: [PATCH 049/334] fix: open the account's sync page from the sync settings #34 "Android account settings" opened Android's page for the Bulwark account through `ACCOUNT_SYNC_SETTINGS` with the account as the `account` extra. The Settings app of Android 12L and later passes only `:settings:show_fragment_args` on to that page, which then found no account and closed at once ("Account provided does not exist: null"). Since starting it succeeded, the fallback to the list of synced accounts never ran, and the button seemed to do nothing. The account now also goes in the fragment arguments, where those Settings apps read it; older ones still read the plain extra. Checked on the API 32 and API 34 emulators: the button opens the account's "Account sync" page with its Contacts and Calendar switches. --- .../bulwarkmobile/sync/BulwarkDeviceSyncModule.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/BulwarkDeviceSyncModule.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/BulwarkDeviceSyncModule.kt index 34b95d3f..230141db 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/BulwarkDeviceSyncModule.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/BulwarkDeviceSyncModule.kt @@ -7,6 +7,7 @@ import android.content.ActivityNotFoundException import android.content.ContentResolver import android.content.Intent import android.os.Build +import android.os.Bundle import android.os.Handler import android.os.Looper import android.provider.Settings @@ -162,7 +163,12 @@ class BulwarkDeviceSyncModule(reactContext: ReactApplicationContext) : @ReactMethod fun openAccountSettings(name: String, promise: Promise) = settle(promise) { val account = DeviceSyncAccounts.account(reactApplicationContext, name) - val perAccount = Intent(ACTION_ACCOUNT_SYNC_SETTINGS).putExtra(EXTRA_ACCOUNT, account) + val perAccount = Intent(ACTION_ACCOUNT_SYNC_SETTINGS) + .putExtra(EXTRA_ACCOUNT, account) + // Newer Settings apps pass only these arguments on to the account's + // screen; without them it closes at once ("Account provided does + // not exist: null", Android 12L), and no exception tells us so. + .putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, Bundle().apply { putParcelable(EXTRA_ACCOUNT, account) }) val overview = Intent(Settings.ACTION_SYNC_SETTINGS) .putExtra(Settings.EXTRA_ACCOUNT_TYPES, arrayOf(account.type)) val activity = reactApplicationContext.currentActivity @@ -309,6 +315,8 @@ class BulwarkDeviceSyncModule(reactContext: ReactApplicationContext) : /** Settings' per-account sync screen; public on AOSP-based settings apps, but not in the SDK. */ private const val ACTION_ACCOUNT_SYNC_SETTINGS = "android.settings.ACCOUNT_SYNC_SETTINGS" private const val EXTRA_ACCOUNT = "account" + /** SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS: the arguments of the screen it shows. */ + private const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" @Volatile private var currentInstance: BulwarkDeviceSyncModule? = null From 78684e2492162c71d51378db7923c8595509712c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:52:12 +0200 Subject: [PATCH 050/334] fix: keep the last sync time current while the sync settings stay open #34 --- .../settings/device-sync/DeviceSyncAccountRow.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/components/settings/device-sync/DeviceSyncAccountRow.tsx b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx index a2b35c30..e83b9910 100644 --- a/src/components/settings/device-sync/DeviceSyncAccountRow.tsx +++ b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx @@ -78,6 +78,9 @@ function askReminderOwner(t: TranslateFn): Promise { }); } +/** How often the status line's "… ago" is brought up to date. */ +const RELATIVE_TIME_REFRESH_MS = 30_000; + export function DeviceSyncAccountRow({ account, authority }: Props) { const c = useColors(); const styles = React.useMemo(() => makeStyles(c), [c]); @@ -91,6 +94,12 @@ export function DeviceSyncAccountRow({ account, authority }: Props) { const onInApp = syncOnInApp(entry, authority); const { view, permitted, refresh } = useAndroidSync(entry.androidAccountName, authority); const [busy, setBusy] = React.useState(null); + // "Synced 2 min ago" keeps counting while the settings stay open. + const [now, setNow] = React.useState(() => Date.now()); + React.useEffect(() => { + const timer = setInterval(() => setNow(Date.now()), RELATIVE_TIME_REFRESH_MS); + return () => clearInterval(timer); + }, []); const [prompt, setPrompt] = React.useState(null); const [chooserOpen, setChooserOpen] = React.useState(false); @@ -117,7 +126,7 @@ export function DeviceSyncAccountRow({ account, authority }: Props) { permitted, syncing: !!view?.active, lastRun: entry.lastRun[authority], - now: Date.now(), + now, }, t, ); From 7dbbd1ce25d36ad1cc5266785d8208c8b6026b9b Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:40:50 +0200 Subject: [PATCH 051/334] fix: move events between calendars without recreating them on the server #34 Etar moves an event to another calendar, and turns a series into a single event, by deleting the row and inserting a copy. The engine was meant to pair the two rows and upload a patch of the existing event, but it claimed every new row (a uid and a pending create) before it asked the planner for pairs, and the planner pairs only unclaimed rows. No pair ever formed: the event was destroyed and created again under a new uid, which lost what the device cannot show (keywords, virtual locations, links, the other attendees' answers, alerts relative to the end) and sent every attendee a cancellation and a new invitation. Pairs now upload before new rows are claimed, and a paired deletion no longer counts toward the deletion threshold, since it is a move. When an app changes the rows while the patch is on its way, they are read and paired again; rows that no longer pair are left without the identity instead of having the server's version written into the new row, which overwrote the edit and left two rows with one identity. Checked with the real planners: a move and a series turned single keep their uid, keywords and attendee answers and send nothing; an edit made during the upload is kept; an unchanged rewrite still pairs. --- .../__tests__/engine/real-planners.test.ts | 180 +++++++++++++++++- src/device-sync/engine/calendar-sync.ts | 96 +++++++--- src/device-sync/engine/item-sync.ts | 20 +- 3 files changed, 262 insertions(+), 34 deletions(-) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 7c3c00c6..d44e3e06 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -3,9 +3,10 @@ // the planners' own tests; these check what the engine guarantees. import { describe, expect, it } from 'vitest'; -import { Data, MimeType } from '../../android-columns'; +import { Attendees, Data, Events, MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; +import type { Row } from '../../types'; import { addDeviceContact, addServerCards, @@ -21,6 +22,36 @@ function real(options: Parameters[0] = {}): Harness { return h; } +/** The event columns Etar copies into the row it inserts when it moves an event or ends its recurrence. */ +const ETAR_COPY = [ + Events.TITLE, Events.DESCRIPTION, Events.EVENT_LOCATION, Events.STATUS, Events.AVAILABILITY, Events.ACCESS_LEVEL, + Events.EVENT_COLOR, Events.DTSTART, Events.DTEND, Events.DURATION, Events.EVENT_TIMEZONE, Events.ALL_DAY, Events.RRULE, +]; + +/** Etar's move or "series to single event": the row is deleted and a copy (with its attendees) inserted. */ +function etarReinsert(h: Harness, old: Row, calendarRowId: number, changes: Row = {}): number { + const values: Row = {}; + for (const c of ETAR_COPY) if (old[c] !== undefined) values[c] = old[c]; + const attendees = h.device.rows('attendees') + .filter((a) => Number(a.event_id) === Number(old._id)) + .map((a) => ({ + [Attendees.ATTENDEE_EMAIL]: a[Attendees.ATTENDEE_EMAIL], + [Attendees.ATTENDEE_NAME]: a[Attendees.ATTENDEE_NAME], + [Attendees.ATTENDEE_RELATIONSHIP]: a[Attendees.ATTENDEE_RELATIONSHIP], + [Attendees.ATTENDEE_TYPE]: a[Attendees.ATTENDEE_TYPE], + [Attendees.ATTENDEE_STATUS]: a[Attendees.ATTENDEE_STATUS], + })); + h.device.user.deleteEvent(Number(old._id)); + return h.device.user.insertEvent(calendarRowId, { ...values, ...changes }, { attendees }); +} + +/** Masters (no ORIGINAL_*) that share a `_SYNC_ID`: a split the next run would resolve by destroy + create. */ +function sharedIdentities(h: Harness): string[] { + const masters = h.events().filter((e) => e[Events.ORIGINAL_ID] === null && e[Events.ORIGINAL_SYNC_ID] === null && e._sync_id); + const ids = masters.map((e) => String(e._sync_id)); + return ids.filter((id, i) => ids.indexOf(id) !== i); +} + /** A device edit of the display name only, as AOSP Contacts makes it (the provider fills the components). */ function retypeName(h: Harness, rawContactId: number, name: string): void { const row = h.nameDataRow(rawContactId)!; @@ -192,6 +223,153 @@ describe('device sync engine with the real planners', () => { expect(h.events().every((e) => Number(e.dirty ?? 0) === 0)).toBe(true); }); + it("uploads Etar's move to another calendar as a patch of the same event, keeping what the device can't show", async () => { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + const lunch = h.server.addEvent('a', { + uid: 'lunch-uid', + title: 'Lunch', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + keywords: { important: true }, + virtualLocations: { v1: { '@type': 'VirtualLocation', uri: 'https://meet.example.com/lunch' } }, + organizerCalendarAddress: 'mailto:alice@example.com', + participants: { + me: { '@type': 'Participant', calendarAddress: 'mailto:alice@example.com', roles: { owner: true, attendee: true }, participationStatus: 'accepted' }, + bob: { '@type': 'Participant', calendarAddress: 'mailto:bob@example.com', name: 'Bob', roles: { attendee: true }, participationStatus: 'accepted' }, + }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const old = h.events().find((e) => e._sync_id === `a/${lunch}`)!; + const workRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${work}`)!._id); + + const moved = etarReinsert(h, old, workRow); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, updated: 1, deleted: 0 } } }); + expect(h.server.all('CalendarEvent', 'a')).toHaveLength(1); + const event = h.server.get('CalendarEvent', 'a', lunch)!; + expect(event.calendarIds).toEqual({ [work]: true }); + expect(event).toMatchObject({ + uid: 'lunch-uid', + keywords: { important: true }, + virtualLocations: { v1: { uri: 'https://meet.example.com/lunch' } }, + participants: { bob: { participationStatus: 'accepted' } }, + }); + // A move tells nobody. + expect(h.server.scheduling).toEqual([]); + expect(h.events()).toEqual([expect.objectContaining({ _id: moved, _sync_id: `a/${lunch}`, uid2445: 'lunch-uid', calendar_id: workRow, dirty: 0 })]); + + const before = h.batches.log.length; + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + expect(rowWrites(h.batches.log.slice(before))).toEqual([]); + }); + + it('uploads a series Etar turned into a single event as the removal of its rule', async () => { + const h = real(); + const series = h.server.addEvent('a', { + uid: 'series-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + keywords: { team: true }, + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'daily' }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const old = h.events().find((e) => e._sync_id === `a/${series}`)!; + + const single = etarReinsert(h, old, Number(old.calendar_id), { + [Events.RRULE]: null, + [Events.DURATION]: null, + [Events.DTEND]: Number(old[Events.DTSTART]) + 15 * 60_000, + }); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, updated: 1, deleted: 0 } } }); + expect(h.server.all('CalendarEvent', 'a')).toHaveLength(1); + const event = h.server.get('CalendarEvent', 'a', series)!; + expect(event).toMatchObject({ uid: 'series-uid', title: 'Standup', keywords: { team: true } }); + expect(event.recurrenceRule).toBeUndefined(); + expect(h.events()).toEqual([expect.objectContaining({ _id: single, _sync_id: `a/${series}`, rrule: null, dirty: 0 })]); + }); + + it('never leaves two rows with one identity when the moved event is edited while its move uploads', async () => { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + const lunch = h.server.addEvent('a', { + uid: 'lunch-uid', + title: 'Lunch', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const old = h.events().find((e) => e._sync_id === `a/${lunch}`)!; + const workRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${work}`)!._id); + const moved = etarReinsert(h, old, workRow); + let edited = false; + const stop = h.server.onAfterRequest((request) => { + if (edited || !request.methods.includes('CalendarEvent/set')) return; + edited = true; + h.device.user.updateEvent(moved, { [Events.TITLE]: 'Team lunch' }); + }); + + await h.run(CALENDAR_AUTHORITY); + stop(); + + expect(edited).toBe(true); + expect(sharedIdentities(h)).toEqual([]); + expect(h.events().find((e) => Number(e._id) === moved)).toMatchObject({ title: 'Team lunch' }); + + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + expect(sharedIdentities(h)).toEqual([]); + const server = h.server.all('CalendarEvent', 'a'); + expect(server.map((e) => [e.title, e.calendarIds])).toEqual([['Team lunch', { [work]: true }]]); + expect(h.events()).toEqual([expect.objectContaining({ _id: moved, _sync_id: `a/${server[0].id}`, dirty: 0 })]); + }); + + it('pairs the rows again when an app rewrote the moved event unchanged while its move uploaded', async () => { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + const lunch = h.server.addEvent('a', { + uid: 'lunch-uid', + title: 'Lunch', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + keywords: { important: true }, + organizerCalendarAddress: 'mailto:alice@example.com', + participants: { + me: { '@type': 'Participant', calendarAddress: 'mailto:alice@example.com', roles: { owner: true, attendee: true }, participationStatus: 'accepted' }, + bob: { '@type': 'Participant', calendarAddress: 'mailto:bob@example.com', name: 'Bob', roles: { attendee: true }, participationStatus: 'accepted' }, + }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const old = h.events().find((e) => e._sync_id === `a/${lunch}`)!; + const workRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${work}`)!._id); + const moved = etarReinsert(h, old, workRow); + const stop = h.server.onAfterRequest((request) => { + if (!request.methods.includes('CalendarEvent/set')) return; + stop(); + // Fossify's save: the same attendees, deleted and inserted again. + const attendees = h.device.rows('attendees').filter((a) => Number(a.event_id) === moved).map(({ _id, event_id, ...a }) => a); + h.device.user.fossifySaveEvent(moved, {}, attendees, []); + }); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, updated: 1, deleted: 0 } } }); + expect(h.server.all('CalendarEvent', 'a')).toHaveLength(1); + expect(h.server.get('CalendarEvent', 'a', lunch)).toMatchObject({ uid: 'lunch-uid', calendarIds: { [work]: true }, keywords: { important: true } }); + expect(h.events()).toEqual([expect.objectContaining({ _id: moved, _sync_id: `a/${lunch}`, dirty: 0 })]); + }); + it('syncs events both ways and writes nothing for its own echo', async () => { const h = real(); const standup = h.server.addEvent('a', { diff --git a/src/device-sync/engine/calendar-sync.ts b/src/device-sync/engine/calendar-sync.ts index 1f78fecf..e14d8264 100644 --- a/src/device-sync/engine/calendar-sync.ts +++ b/src/device-sync/engine/calendar-sync.ts @@ -5,6 +5,7 @@ */ import { findTasksOnlyCalendarIds, isTaskLikeObject, SCAN_PROPERTIES, type ScannedCalendarObject } from '../../lib/calendar-component-detection'; import { collectionKey, parseCollectionKey, parseObjectRef, pendingUidOf } from '../common/ids'; +import { deepEqual } from '../common/json'; import type { CalendarContext, CalendarEventWire, CalendarLike, CalendarPlanner, LocalCalendar, LocalEvent, OpGroup } from '../planner'; import { CALENDAR_AUTHORITY, type ReminderOwner, type Row } from '../types'; import { CALENDAR_EVENT_PROPERTIES, CALENDAR_PROPERTIES } from '../wire'; @@ -15,7 +16,7 @@ import type { SubscriptionCalendar } from './deps'; import { ItemSync, type Pending } from './item-sync'; import { accountOfRef, idInAccount, refOf, type Held, type Kind, type ServerObject } from './kinds'; import { poisonFingerprint } from './poison'; -import { flag, num, str } from './provider'; +import { flag, hasWrites, num, str } from './provider'; import { isCollectionSelected } from './selection'; import { accountOf, type SyncState } from './sync-state'; @@ -24,6 +25,8 @@ const WITH_IDENTITY = "_sync_id IS NOT NULL AND _sync_id NOT LIKE '~pending/%'"; /** CalendarProvider leaves DIRTY (and may leave DELETED) NULL on sync-adapter inserts: NULL is clean. */ const CLEAN = '(dirty IS NULL OR dirty = 0) AND (deleted IS NULL OR deleted = 0)'; +type PairPlan = ReturnType[number]; + function onIds(map: Record | undefined | null): string[] { return Object.entries(map ?? {}) .filter(([, on]) => on === true) @@ -411,41 +414,47 @@ export class CalendarSync extends ItemSync { /** * Before the creates: a split's source uploads its rule while the split is - * still visible, then its clone is claimed as a new event; and deleted + - * new masters of the run that are one edit (a move, a series turned - * single) upload as patches of the existing object. + * still visible, then its clone is claimed as a new event. */ protected async prePhase(acct: string): Promise { - let items = (await this.loadUploadItems()).filter((h) => this.accountOfItem(h) === acct); + const items = (await this.loadUploadItems()).filter((h) => this.accountOfItem(h) === acct); const sources = items.filter((h) => (h.local as LocalEvent).split === 'source'); const clones = items.filter((h) => (h.local as LocalEvent).split === 'clone'); - if (sources.length || clones.length) { - const works: Work[] = []; - const ready: Pending[] = []; - for (const held of sources) { - const pending = await this.planItem(acct, held, works); - if (pending) ready.push(pending); - } - // A clone (of a split, or a new master whose uid another row carries) needs no source: - // it claims a fresh uid here and is created with the other new events. - for (const held of clones) { - const pending = await this.planItem(acct, held, works); - if (pending) ready.push(pending); - } - await this.env.writer.write(works); - if (ready.length) await this.send(acct, ready); - items = (await this.loadUploadItems()).filter((h) => this.accountOfItem(h) === acct); + if (!sources.length && !clones.length) return; + const works: Work[] = []; + const ready: Pending[] = []; + for (const held of sources) { + const pending = await this.planItem(acct, held, works); + if (pending) ready.push(pending); } + // A clone (of a split, or a new master whose uid another row carries) needs no source: + // it claims a fresh uid here and is created with the other new events. + for (const held of clones) { + const pending = await this.planItem(acct, held, works); + if (pending) ready.push(pending); + } + await this.env.writer.write(works); + if (ready.length) await this.send(acct, ready); + } + + /** + * Before new rows are claimed: deleted and new masters of the run that are + * one edit (a move, a series turned single) upload as patches of the + * existing object, and the new row takes the old one's identity. The + * planner pairs only a new row that has not claimed a uid of its own. + */ + protected async pairPhase(acct: string): Promise { + const items = (await this.loadUploadItems()).filter((h) => this.accountOfItem(h) === acct); const deleted = items.map((h) => h.local as LocalEvent).filter((e) => { const m = this.eventKind.meta(e); return m.deleted && !!m.sourceId; }); const fresh = items.map((h) => h.local as LocalEvent).filter((e) => { const m = this.eventKind.meta(e); - return m.isNew && !m.deleted && !!m.pending; + return m.isNew && !m.deleted && !m.pending; }); if (!deleted.length || !fresh.length) return; - let pairs: ReturnType; + let pairs: PairPlan[]; try { pairs = this.planner.planPairs(deleted, fresh, this.ctx(acct)); } catch (error) { @@ -465,16 +474,25 @@ export class CalendarSync extends ItemSync { actions: pair.actions as Pending['actions'], accept: async (objects) => { // The new row takes the identity and exceptions of the old one, which is purged. - await this.env.writer.write([{ group: pair.ops }]); - const [moved] = await this.eventKind.loadByRowIds([pair.fresh.eventId]); + let paired = false; + await this.env.writer.write([this.pairWork(acct, pair, () => { + paired = true; + })]); + const [moved] = paired ? await this.eventKind.loadByRowIds([pair.fresh.eventId]) : []; const server = ownId ? objects.get(ownId) : undefined; if (!moved || !server) { + // Fetched again first next run. Rows that no longer pair (the new one was edited + // meanwhile) upload as a create and a deletion; neither takes the identity here. if (ownId) this.markStale(acct, ownId); return []; } const held: Held = { kind: this.eventKind, local: moved }; try { - return [this.acceptWork(acct, { held, meta: this.eventKind.meta(moved), fingerprint: '', actions: [] }, server, false)]; + const work = this.acceptWork(acct, { held, meta: this.eventKind.meta(moved), fingerprint: '', actions: [] }, server, false); + if (hasWrites(work.group.ops)) return [work]; + // The pair's ops already wrote what the server holds. + this.env.report.stats.uploaded.updated++; + return []; } catch (error) { this.plannerFailed(acct, refOf(acct, server.id), 'upload', error, server.id); return []; @@ -485,6 +503,32 @@ export class CalendarSync extends ItemSync { await this.send(acct, pending); } + /** + * A pair's rows after the server took its patch. When their assert fails + * (an app changed them during the upload), both rows are read and paired + * again; only a pair that uploads the same patch is written. + */ + private pairWork(acct: string, pair: PairPlan, applied: () => void): Work { + return { + group: pair.ops, + applied, + replan: async () => { + const rows = await this.eventKind.loadByRowIds([pair.deleted.eventId, pair.fresh.eventId]); + const deleted = rows.find((e) => e.eventId === pair.deleted.eventId); + const fresh = rows.find((e) => e.eventId === pair.fresh.eventId); + if (!deleted || !fresh) return null; + let again: PairPlan | undefined; + try { + [again] = this.planner.planPairs([deleted], [fresh], this.ctx(acct)); + } catch (error) { + this.plannerFailed(acct, pair.deleted.syncId ?? `pairs:${acct}`, 'upload', error); + return null; + } + return again && deepEqual(again.actions, pair.actions) ? this.pairWork(acct, again, applied) : null; + }, + }; + } + // ── After the upload ── /** A master with nothing waiting for an upload. */ diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index e4c96911..f390a57f 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -142,7 +142,9 @@ export abstract class ItemSync { const meta = held.kind.meta(held.local); return accountOfRef(meta.sourceId) ?? accountOfRef(meta.pending?.target ?? null); } - /** Before the creates (calendar: split series and pairs). */ + /** Before new rows are claimed (calendar: deleted and new rows that are one edit, uploaded as patches). */ + protected async pairPhase(_acct: string): Promise {} + /** Before the creates (calendar: split series). */ protected async prePhase(_acct: string): Promise {} /** After the deletions (contacts: groups deleted by absence, memberships). */ protected async extraPhase(_acct: string, _deletionsAllowed: boolean): Promise {} @@ -691,16 +693,20 @@ export abstract class ItemSync { // ── Uploads ── /** - * Local changes to the server, per phase across the accounts: pairs and - * splits, creates, updates, deletions, then memberships. Creates of every - * account come before deletions of any, so a move between accounts never - * destroys first. A `stateMismatch` downloads the account again and - * repeats the phase, at most three times. + * Local changes to the server, per phase across the accounts: pairs, the + * claims of new rows, splits, creates, updates, deletions, then + * memberships. Pairs go first: the new row of a pair takes the deleted + * row's identity, so it must not have claimed a uid of its own, and a + * paired deletion is a move, not a deletion. Creates of every account come + * before deletions of any, so a move between accounts never destroys + * first. A `stateMismatch` downloads the account again and repeats the + * phase, at most three times. */ protected async uploadAll(): Promise { + const accounts = this.accountIds(); + for (const acct of accounts) await this.withRetries(acct, () => this.pairPhase(acct)); await this.claimNewItems(); const deletionsAllowed = await this.deletionPolicy(await this.loadUploadItems()); - const accounts = this.accountIds(); for (const acct of accounts) await this.withRetries(acct, () => this.prePhase(acct)); for (const phase of ['create', 'update', 'delete'] as const) { for (const acct of accounts) await this.withRetries(acct, () => this.phase(acct, phase, deletionsAllowed)); From 7187cab904bbbfc84058ffa77f87a303e2d88934 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:41:27 +0200 Subject: [PATCH 052/334] fix: never undo server changes to a card moved out of synced books #34 When another client moved a contact or an event out of every address book or calendar the device syncs while the device had an unsent edit of it, the download only noted it for dropping after the upload. Its shadow stayed at the old version while the stored state moved past the change, so the upload passed `ifInState` and resent whole maps built from the old shadow: a number or an occurrence added on the server meanwhile was lost. A full reconcile that found such an object did the same. The download now merges the new version into the dirty rows like any other change, so the upload builds on it, and the rows still go once it is accepted. A contact's collections are read from its shadow: SYNC1 lists only the selected books and is empty for a card that left them all, so such a contact was never dropped after its upload. The calendar planner cannot merge an event that is in no synced calendar yet; until it can, such an event waits on the device and is reported instead of overwriting the server. Checked with the real planners through `/changes` and through a full reconcile. --- .../__tests__/engine/real-planners.test.ts | 84 +++++++++++++++++++ src/device-sync/engine/contacts-sync.ts | 3 +- src/device-sync/engine/item-sync.ts | 19 ++++- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index d44e3e06..2a20e232 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -155,6 +155,90 @@ describe('device sync engine with the real planners', () => { expect(h.contacts().every((c) => !c.dirty)).toBe(true); }); + describe('a device edit of an object the server moved out of every synced collection', () => { + /** Ada with two numbers in the synced book; the device deletes the second in place (AOSP). */ + async function adaWithDeletedNumber() { + const h = real(); + const archive = h.server.addAddressBook('a', { name: 'Archive' }); + h.prefs.contactsSelection[`a/${archive}`] = false; + const ada = h.server.addCard('a', { + uid: 'ada-uid', + name: { full: 'Ada Lovelace' }, + phones: { p1: { '@type': 'Phone', number: '+1 111' }, p2: { '@type': 'Phone', number: '+1 222' } }, + addressBookIds: { [h.book]: true }, + }); + await h.run(); + const contact = h.contactNamed('Ada Lovelace')!; + const p2 = h.device.rows('data').find((d) => Number(d.raw_contact_id) === contact.id && d[Data.DATA1] === '+1 222')!; + h.device.user.deleteData(Number(p2._id)); + // Meanwhile another client files Ada in Archive only and adds a number. + h.server.serverUpdate('ContactCard', 'a', ada, { addressBookIds: { [archive]: true }, 'phones/p3': { '@type': 'Phone', number: '+1 333' } }); + return { h, ada, archive }; + } + + const numbers = (card: Record) => + Object.values((card.phones ?? {}) as Record).map((p) => p.number).sort(); + + it('uploads it against the server version, then drops the rows', async () => { + const { h, ada, archive } = await adaWithDeletedNumber(); + + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); + const card = h.server.get('ContactCard', 'a', ada)!; + expect(numbers(card)).toEqual(['+1 111', '+1 333']); + expect(card.addressBookIds).toEqual({ [archive]: true }); + expect(h.contacts()).toEqual([]); + }); + + it('does the same when a full reconcile finds the object outside', async () => { + const { h, ada, archive } = await adaWithDeletedNumber(); + h.server.truncateChangeLog('a', 'contacts'); + + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [] }); + const card = h.server.get('ContactCard', 'a', ada)!; + expect(numbers(card)).toEqual(['+1 111', '+1 333']); + expect(card.addressBookIds).toEqual({ [archive]: true }); + expect(h.contacts()).toEqual([]); + }); + + it('never uploads an event edit against an outdated shadow', async () => { + const h = real(); + const archive = h.server.addCalendar('a', { name: 'Archive' }); + h.prefs.calendarSelection[`a/${archive}`] = false; + const series = h.server.addEvent('a', { + uid: 'series-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'daily' }, + recurrenceOverrides: { '2026-09-29T09:00:00': { title: 'Planning' } }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const master = h.events().find((e) => e._sync_id === `a/${series}`)!; + const later = Number(master[Events.DTSTART]) + 3_600_000; + // The device moves the series by an hour; another client files it in Archive and changes another instance. + h.device.user.updateEvent(Number(master._id), { [Events.DTSTART]: later }); + h.server.serverUpdate('CalendarEvent', 'a', series, { + calendarIds: { [archive]: true }, + 'recurrenceOverrides/2026-10-01T09:00:00': { title: 'Retro' }, + }); + + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + + const event = h.server.get('CalendarEvent', 'a', series)!; + const titles = Object.values((event.recurrenceOverrides ?? {}) as Record).map((o) => o.title).sort(); + expect(titles).toEqual(['Planning', 'Retro']); + // The move went up, or it still waits on the device: it is not lost. + const row = h.events().find((e) => Number(e._id) === Number(master._id)); + expect(event.start === '2026-09-28T10:00:00' || (Number(row?.dirty) === 1 && Number(row?.[Events.DTSTART]) === later)).toBe(true); + }); + }); + it('creates a device event that carries the uid of a synced event (an app copied it) under a fresh uid', async () => { const h = real(); const standup = h.server.addEvent('a', { diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 3d5c55ee..40257ee4 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -151,7 +151,8 @@ export class ContactsSync extends ItemSync { deleted: c.deleted, isNew: !c.sourceId, poison: c.poison, - collections: c.collections, + // The card's books as last seen: SYNC1 lists only the selected ones, none for a card that left them all. + collections: c.shadow ? this.cardKeys(accountOfRef(c.sourceId), c.shadow) : c.collections, }), fingerprint: (c) => poisonFingerprint({ diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index f390a57f..e93ec84a 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -408,14 +408,16 @@ export abstract class ItemSync { if (flags.dirty || flags.deleted) verify.push(id); else gone.add(id); } + const outside: string[] = []; if (verify.length) { - // Destroyed, or moved out of the synced collections? Only a destroy takes dirty rows. + // Destroyed, or moved out of the synced collections? Only a destroy takes dirty rows; the + // others are downloaded like any change, so a dirty item merges the server's version. const { list, notFound } = await this.env.jmap.get(this.itemType, acct, verify, ['id']); for (const id of notFound) gone.add(id); - for (const object of list) this.outsideOf(acct).add(refOf(acct, object.id)); + for (const object of list) outside.push(object.id); } const selected = collections.map((c) => collectionKey(acct, c)); - await this.processIds(acct, [], [...gone], (next) => { + await this.processIds(acct, outside, [...gone], (next) => { const account = accountOf(next, acct); account.itemsState = from; account.reconcile = null; @@ -555,7 +557,14 @@ export abstract class ItemSync { written: Map> | null, ): Work | null { if (!kind || !this.inSelection(acct, object)) { - return found ? this.removeWork(acct, object.id, found, 'outside') : null; + if (!found) return null; + const meta = found.kind.meta(found.local); + if (found.kind === kind && meta.dirty && !meta.deleted && !meta.isNew) { + // Uploaded first, dropped afterwards: merged like any dirty item, so the upload builds on the server's version. + this.outsideOf(acct).add(refOf(acct, object.id)); + return this.downloadWork(kind, acct, object, found.local, written); + } + return this.removeWork(acct, object.id, found, 'outside'); } if (found && found.kind !== kind) { const meta = found.kind.meta(found.local); @@ -1263,6 +1272,8 @@ export abstract class ItemSync { const counted = () => { if (created) this.env.report.stats.uploaded.created++; else this.env.report.stats.uploaded.updated++; + // In no synced collection any more (moved away on the server): the rows go after the upload. + if (!this.inSelection(acct, server)) this.outsideOf(acct).add(refOf(acct, server.id)); this.onAccepted(kind, acct, server); }; const failed = (reason: string, message: string) => { From 58b74527b221cf7286e1acabe58f43dd1a5e1f59 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:42:08 +0200 Subject: [PATCH 053/334] fix: keep group cards on the server when a sync stops while dropping them #34 The contacts SyncState lists the group rows on the device, so a group an app hard-deleted (Fossify does) is found by its absence and deleted on the server. The list changed only with a later state op, not with the batch that inserted or deleted the rows. When a run stopped in between (cancelled, out of time, a crash, or turning sync off while a deselected book was being dropped), a group the engine had removed was still listed, taken for a hard delete by the next run or by the teardown's upload, and its card destroyed on the server; a group it had inserted was never listed, so a later hard delete never went up. A batch that inserts or deletes a group row now ends with a state op holding that change, retries included, so the list and the rows never disagree. A group a download finds unchanged is listed too, for rows whose batch lost its state op at a provider yield point. And an absent group whose card is no longer a group or is in no synced book is only forgotten, never destroyed. Checked with a crash at every checkpoint of a deselection, a teardown and a deadline stop during one, a server-side move in a page of several chunks, and a group written before an interruption and then deleted in Fossify. --- .../__tests__/engine/group-listing.test.ts | 195 ++++++++++++++++++ .../__tests__/engine/internals.test.ts | 32 ++- src/device-sync/engine/batch.ts | 44 +++- src/device-sync/engine/contacts-sync.ts | 30 ++- src/device-sync/engine/item-sync.ts | 41 +++- src/device-sync/engine/run.ts | 10 +- src/device-sync/engine/sync-state.ts | 20 +- 7 files changed, 350 insertions(+), 22 deletions(-) create mode 100644 src/device-sync/__tests__/engine/group-listing.test.ts diff --git a/src/device-sync/__tests__/engine/group-listing.test.ts b/src/device-sync/__tests__/engine/group-listing.test.ts new file mode 100644 index 00000000..16e47c49 --- /dev/null +++ b/src/device-sync/__tests__/engine/group-listing.test.ts @@ -0,0 +1,195 @@ +// SyncState.groups lists the group cards present on the device, so a group an +// app hard-deleted (Fossify) is found by its absence and deleted on the server. +// The list must change in the same batch as the rows: a group row the engine +// removed but still listed reads as a hard delete and destroys the card, and a +// row it inserted but never listed hides a later hard delete. + +import { describe, expect, it } from 'vitest'; +import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { serializeSyncState } from '../../engine/sync-state'; +import { addServerCards, ANDROID_ACCOUNT, CONTACTS_AUTHORITY, createHarness, RUN_BUDGET, type Harness } from './harness'; + +function real(): Harness { + const h = createHarness(); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + return h; +} + +/** The personal book with Ada, and a Work book with Boss, Colleague and the group "Team" (Boss). */ +async function withWorkGroup() { + const h = real(); + const work = h.server.addAddressBook('a', { name: 'Work' }); + addServerCards(h, ['Ada']); + const [boss] = addServerCards(h, ['Boss', 'Colleague'], 'a', work); + const bossUid = h.server.get('ContactCard', 'a', boss)!.uid as string; + const group = h.server.addCard('a', { + uid: 'g-team', + kind: 'group', + name: { full: 'Team' }, + addressBookIds: { [work]: true }, + members: { [bossUid]: true }, + }); + expect((await h.run()).outcome).toBe('ok'); + expect(h.state()?.accounts.a.groups).toEqual([`a/${group}`]); + h.checkpoints.count = 0; + return { h, group, work }; +} + +/** Replaces the stored SyncState's `groups` of account `a` (a list that drifted from the rows). */ +async function storeGroups(h: Harness, groups: string[]): Promise { + const state = h.state()!; + state.accounts.a.groups = groups; + await h.device.port(ANDROID_ACCOUNT, CONTACTS_AUTHORITY).applyBatch([{ op: 'syncState', value: serializeSyncState(state) }]); +} + +describe('device sync engine: the groups a SyncState lists', () => { + it('keeps a group card on the server after a deselection stopped at any checkpoint', async () => { + const probe = await withWorkGroup(); + probe.h.prefs.contactsSelection[`a/${probe.work}`] = false; + await probe.h.run(); + const total = probe.h.checkpoints.count; + expect(probe.h.server.get('ContactCard', 'a', probe.group)).toBeDefined(); + + for (let n = 1; n <= total; n++) { + const { h, group, work } = await withWorkGroup(); + h.prefs.contactsSelection[`a/${work}`] = false; + h.checkpoints.crashAt = n; + await h.run(); + h.checkpoints.crashAt = null; + expect((await h.run()).outcome, `run after a crash at checkpoint ${n}`).toBe('ok'); + expect(h.server.get('ContactCard', 'a', group), `group card after a crash at checkpoint ${n}`).toBeDefined(); + expect(h.device.rows('groups')).toEqual([]); + expect(h.state()?.accounts.a.groups).toEqual([]); + } + }); + + it('keeps a group card on the server when its book is selected again after a stopped deselection', async () => { + const { h, group, work } = await withWorkGroup(); + h.prefs.contactsSelection[`a/${work}`] = false; + // Checkpoint 3 comes right after the deselected book's group rows went. + h.checkpoints.crashAt = 3; + await h.run(); + h.checkpoints.crashAt = null; + expect(h.device.rows('groups')).toEqual([]); + + h.prefs.contactsSelection[`a/${work}`] = true; + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', stats: { uploaded: { deleted: 0 } } }); + expect(h.server.get('ContactCard', 'a', group)).toBeDefined(); + }); + + it('keeps a group card on the server when sync is turned off while a deselection drops it', async () => { + const { h, group, work } = await withWorkGroup(); + h.prefs.contactsSelection[`a/${work}`] = false; + let teardown: Promise<{ pending: number }> | null = null; + h.checkpoints.onCheckpoint = (n) => { + // Right before the run drops the deselected book's rows. + if (n === 2 && !teardown) teardown = h.teardown(CONTACTS_AUTHORITY); + }; + + const run = await h.run(); + h.checkpoints.onCheckpoint = undefined; + + expect(run.outcome).toBe('cancelled'); + expect(await teardown!).toEqual({ pending: 0 }); + expect(h.contacts()).toHaveLength(0); + expect(h.server.get('ContactCard', 'a', group)).toBeDefined(); + }); + + it('keeps a group card on the server when the deselecting run runs out of time after dropping the group', async () => { + const { h, group, work } = await withWorkGroup(); + h.prefs.contactsSelection[`a/${work}`] = false; + h.checkpoints.onCheckpoint = (n) => { + if (n === 2) h.clock.now += RUN_BUDGET; + }; + + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + expect((await h.run()).outcome).toBe('ok'); + + expect(h.server.get('ContactCard', 'a', group)).toBeDefined(); + }); + + it('keeps a group card the server moved to a book that does not sync when the run stops within the page', async () => { + const h = real(); + const archive = h.server.addAddressBook('a', { name: 'Archive' }); + h.prefs.contactsSelection[`a/${archive}`] = false; + const ids = addServerCards(h, Array.from({ length: 60 }, (_, i) => `P${String(i).padStart(2, '0')}`)); + const group = h.server.addCard('a', { uid: 'g-team', kind: 'group', name: { full: 'Team' }, addressBookIds: { [h.book]: true }, members: {} }); + await h.run(); + expect(h.device.rows('groups')).toHaveLength(1); + // Another client moves the group to Archive and renames 60 contacts: one /changes page, several chunks. + h.server.serverUpdate('ContactCard', 'a', group, { addressBookIds: { [archive]: true } }); + ids.forEach((id, i) => h.server.serverUpdate('ContactCard', 'a', id, { 'name/full': `Q${i}` })); + // Checkpoints: 'changes', 'download' (the group's chunk), 'download' (the first contacts chunk) … + h.checkpoints.count = 0; + h.checkpoints.crashAt = 3; + + await h.run(); + h.checkpoints.crashAt = null; + expect(h.device.rows('groups')).toHaveLength(0); + expect((await h.run()).outcome).toBe('ok'); + + expect(h.server.get('ContactCard', 'a', group)).toBeDefined(); + expect(h.state()?.accounts.a.groups).toEqual([]); + }); + + it('lists a group written before an interruption, so a hard delete by an app still reaches the server', async () => { + const h = real(); + addServerCards(h, Array.from({ length: 60 }, (_, i) => `P${String(i).padStart(2, '0')}`)); + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: {} }); + // First sync: 'reconcile', 'reconcile:ids', 'reconcile:objects' (the group's chunk), then out of time. + h.checkpoints.onCheckpoint = (n) => { + if (n === 3) h.clock.now += RUN_BUDGET; + }; + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + expect(h.device.rows('groups')).toHaveLength(1); + expect(h.state()?.accounts.a.groups).toEqual([`a/${group}`]); + expect((await h.run()).outcome).toBe('ok'); + expect(h.contacts()).toHaveLength(60); + + // Fossify hard-deletes the group through a sync-adapter URI. + h.device.user.fossifyDeleteGroup(Number(h.device.rows('groups')[0]._id)); + expect((await h.run()).outcome).toBe('ok'); + + expect(h.server.get('ContactCard', 'a', group)).toBeUndefined(); + }); + + it('lists a group again when it comes back as an echo', async () => { + const h = real(); + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: {} }); + await h.run(); + // A list that lost the group (a write committed without its state op), then a download of the unchanged card. + await storeGroups(h, []); + addServerCards(h, ['Ada']); + h.server.truncateChangeLog('a', 'contacts'); + expect((await h.run()).outcome).toBe('ok'); + expect(h.state()?.accounts.a.groups).toEqual([`a/${group}`]); + + h.device.user.fossifyDeleteGroup(Number(h.device.rows('groups')[0]._id)); + await h.run(); + + expect(h.server.get('ContactCard', 'a', group)).toBeUndefined(); + }); + + it('never destroys a listed group card that is in no synced book or no longer a group, and forgets it', async () => { + const h = real(); + const archive = h.server.addAddressBook('a', { name: 'Archive' }); + h.prefs.contactsSelection[`a/${archive}`] = false; + const filed = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Filed' }, addressBookIds: { [archive]: true }, members: {} }); + const person = h.server.addCard('a', { uid: 'p1', name: { full: 'Was a group' }, addressBookIds: { [h.book]: true } }); + await h.run(); + // The list names both as groups whose rows are gone (the engine removed them, and the batch storing that was lost). + await storeGroups(h, [`a/${filed}`, `a/${person}`]); + + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', stats: { uploaded: { deleted: 0 } } }); + expect(h.server.get('ContactCard', 'a', filed)).toBeDefined(); + expect(h.server.get('ContactCard', 'a', person)).toBeDefined(); + expect(h.state()?.accounts.a.groups).toEqual([]); + }); +}); diff --git a/src/device-sync/__tests__/engine/internals.test.ts b/src/device-sync/__tests__/engine/internals.test.ts index 8b58b263..9609e1b4 100644 --- a/src/device-sync/__tests__/engine/internals.test.ts +++ b/src/device-sync/__tests__/engine/internals.test.ts @@ -8,7 +8,7 @@ import { classifyFailure, RunAbort, StateMismatch, StopRun } from '../../engine/ import { acquireLock } from '../../engine/mutex'; import { isBackedOff, nextMarker } from '../../engine/poison'; import { collectionDefaultOn, isCollectionSelected } from '../../engine/selection'; -import { emptySyncState, parseSyncState, serializeSyncState, StateStore } from '../../engine/sync-state'; +import { accountOf, emptySyncState, parseSyncState, serializeSyncState, StateStore, type SyncState } from '../../engine/sync-state'; import { classifySetError, existingIdFromUidError, JmapCaller } from '../../jmap/caller'; import { openWithAuthRebuild } from '../../jmap/connection'; import { JMAPMethodError } from '../../jmap/errors'; @@ -127,6 +127,36 @@ describe('provider batches', () => { await expect(writer(scope).write([{ group: { ref: 'g', ops: [insert('raw_contacts')] } }])).rejects.toThrow(/not our account/); }); + it("stores a group's state change in the batch that applies it, its retry included, and never without it", async () => { + const store = new StateStore(emptySyncState(), { registryId: 'r', origin: 'o' }); + let failures = 1; + const p = port((ops) => (ops.some((op) => op.op === 'assert') && failures-- > 0 ? { ok: false, reason: 'assert', message: 'stale' } : ok(ops))); + const listed = (ref: string) => (next: SyncState) => { + accountOf(next, 'a').groups = [...(accountOf(next, 'a').groups ?? []), ref]; + }; + const groupOps = (id: number): ProviderOp[] => [ + { op: 'assert', table: 'groups', id, values: { version: 1 } }, + { op: 'update', table: 'groups', id, values: { sourceid: `a/g${id}` } }, + ]; + const plain: Work = { group: { ref: 'c', ops: [insert('raw_contacts'), insert('data')] } }; + const retried: Work = { group: { ref: 'a/g1', ops: groupOps(1) }, state: listed('a/g1') }; + retried.replan = async () => ({ group: retried.group, state: retried.state }); + const w = new BatchWriter(p, { maxOps: 2, maxBytes: 300_000, maxReplans: 3 }, store); + + await w.write([plain, retried]); + + // The plain group alone, without a state op; the group with its state op, refused; its retry, with the state op. + expect(p.sent.map((b) => b.map((op) => op.op))).toEqual([['insert', 'insert'], ['assert', 'update', 'syncState'], ['assert', 'update', 'syncState']]); + expect(store.committed.accounts.a.groups).toEqual(['a/g1']); + + failures = Infinity; + const given: Work = { group: { ref: 'a/g2', ops: groupOps(2) }, state: listed('a/g2'), replan: async () => null }; + await w.write([given], store.tail(() => undefined)); + + expect(p.sent.at(-1)!.map((op) => op.op)).toEqual(['syncState']); + expect(store.committed.accounts.a.groups).toEqual(['a/g1']); + }); + it('skips groups that only assert', async () => { const p = port(ok); await writer(p).write([{ group: { ref: 'g', ops: [{ op: 'assert', table: 'raw_contacts', id: 1, values: { dirty: 0 } }] } }]); diff --git a/src/device-sync/engine/batch.ts b/src/device-sync/engine/batch.ts index 2b4095ee..2375b86f 100644 --- a/src/device-sync/engine/batch.ts +++ b/src/device-sync/engine/batch.ts @@ -15,12 +15,16 @@ * a committed group fail harmlessly); a group that fails an assert is * re-read and re-planned up to `maxReplans` times, then given up. `tooLarge` * splits the batch; `permission` ends the run. + * + * A group whose rows the SyncState describes (a group row: `groups`) carries + * that `state` change: every batch that applies such a group, a retry + * included, ends with a state op holding it. */ import type { OpGroup } from '../planner'; import type { BatchFailure, OpResult, ProviderOp, ProviderPort } from '../types'; import { RunAbort } from './errors'; import { hasWrites } from './provider'; -import type { Tail } from './sync-state'; +import type { StateChange, StateStore, Tail } from './sync-state'; export interface Work { group: OpGroup; @@ -30,6 +34,8 @@ export interface Work { replan?(): Promise; /** Given up: the item's rows stay as they are. */ failed?(reason: BatchFailure, message: string): void | Promise; + /** What the SyncState lists changes with these rows: stored by the batch that applies them. */ + state?: StateChange; } export interface BatchWriterOptions { @@ -100,6 +106,8 @@ export class BatchWriter { constructor( private readonly port: ProviderPort, private readonly options: BatchWriterOptions, + /** Where the `state` of works goes; without it, it is ignored. */ + private readonly store?: StateStore, ) {} /** @@ -162,9 +170,26 @@ export class BatchWriter { return result; } + /** The works' state changes go into the state op of the batch being sent: `tail`, or one of its own. */ + private stage(works: readonly Work[], tail?: Tail): { staged: Work[]; tail?: Tail } { + const store = this.store; + const staged = store ? works.filter((w) => w.state) : []; + for (const work of staged) store!.stage(work, work.state!); + return { staged, tail: tail ?? (staged.length ? store!.tail(() => undefined) : undefined) }; + } + + private unstage(staged: readonly Work[]): void { + for (const work of staged) this.store!.unstage(work); + } + private async applyBatch(works: Work[], tail?: Tail): Promise { - const tailOp = tail?.op(); - const result = await this.send(buildBatch(works.map((w) => w.group), tailOp)); + const stated = this.stage(works, tail); + let result; + try { + result = await this.send(buildBatch(works.map((w) => w.group), stated.tail?.op())); + } finally { + this.unstage(stated.staged); + } if (result.ok) { let offset = 0; for (const work of works) { @@ -172,7 +197,7 @@ export class BatchWriter { await work.applied?.(result.results.slice(offset, offset + n)); offset += n; } - tail?.applied(); + stated.tail?.applied(); return; } if (result.reason === 'tooLarge' && works.length > 1) { @@ -191,9 +216,16 @@ export class BatchWriter { private async applyAlone(work: Work, attempt: number): Promise { if (!hasWrites(work.group.ops)) return; - const result = await this.send(buildBatch([work.group])); + const stated = this.stage([work]); + let result; + try { + result = await this.send(buildBatch([work.group], stated.tail?.op())); + } finally { + this.unstage(stated.staged); + } if (result.ok) { - await work.applied?.(result.results); + await work.applied?.(result.results.slice(0, work.group.ops.length)); + stated.tail?.applied(); return; } await this.retryAlone(work, result.reason, result.message, attempt); diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 40257ee4..9a35eb32 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -24,7 +24,7 @@ import { accountOfRef, idInAccount, refOf, type Held, type Kind, type ServerObje import { poisonFingerprint } from './poison'; import { flag, num, str } from './provider'; import { isCollectionSelected } from './selection'; -import { accountOf, type SyncState } from './sync-state'; +import { accountOf, type StateChange, type SyncState } from './sync-state'; /** * The context the engine hands the contacts planner. `groupsOf` is @@ -496,10 +496,29 @@ export class ContactsSync extends ItemSync { else this.indexCard(acct, object as ContactCardWire); } + /** A group whose row is there already is listed too: the batch that listed it may never have been stored. */ + protected onEcho(kind: Kind, acct: string, object: ServerObject): void { + const ref = refOf(acct, object.id); + if (kind === this.groupKind && !(this.env.store.account(acct).groups ?? []).includes(ref)) this.groupChange(acct, ref, true); + } + protected onRemoved(kind: Kind, acct: string, id: string): void { if (kind === this.groupKind) this.groupChange(acct, refOf(acct, id), false); } + /** `groups` names a group row from the batch that inserts it until the batch that deletes it. */ + protected listing(kind: Kind, acct: string, id: string, present: boolean): StateChange | undefined { + if (kind !== this.groupKind) return undefined; + const ref = refOf(acct, id); + return (next) => { + const account = accountOf(next, acct); + const groups = new Set(account.groups ?? []); + if (present) groups.add(ref); + else groups.delete(ref); + account.groups = [...groups]; + }; + } + protected onAccepted(kind: Kind, acct: string, server: ServerObject): void { if (kind === this.groupKind) this.groupChange(acct, refOf(acct, server.id), true); else this.indexCard(acct, server as ContactCardWire); @@ -628,21 +647,24 @@ export class ContactsSync extends ItemSync { /** * A group hard-deleted on the device: destroyed on the server, or, when * it is also in address books that do not sync, removed from the synced - * ones only. + * ones only. A card that is no longer a group, or in no book that syncs, + * is not what the device showed: it is only forgotten. */ private async deleteAbsentGroups(acct: string): Promise { const refs = (this.absent.get(acct) ?? []).filter((ref) => this.groupChanges.get(acct)?.get(ref) !== false); if (!refs.length) return; await this.env.checkpoints.check('upload:groups'); const ids = refs.map((ref) => idInAccount(ref, acct)).filter((id): id is string => !!id); - const { list, notFound } = await this.env.jmap.get('ContactCard', acct, ids, ['id', 'addressBookIds']); + const { list, notFound } = await this.env.jmap.get('ContactCard', acct, ids, ['id', 'addressBookIds', 'kind']); for (const id of notFound) this.groupChange(acct, refOf(acct, id), false); const update: Record> = {}; const destroy: string[] = []; for (const card of list) { const books = onIds(card.addressBookIds); const synced = books.filter((id) => this.syncedKeys.has(collectionKey(acct, id))); - if (synced.length && synced.length < books.length) { + if (card.kind !== 'group' || !synced.length) { + this.groupChange(acct, refOf(acct, card.id), false); + } else if (synced.length < books.length) { update[card.id] = Object.fromEntries(synced.map((id) => [`addressBookIds/${id}`, null])); } else { destroy.push(card.id); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index e93ec84a..84e9c254 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -32,10 +32,18 @@ import { } from './kinds'; import { isBackedOff, nextMarker } from './poison'; import { hasWrites } from './provider'; -import { accountOf, type SyncState, type Tail } from './sync-state'; +import { accountOf, type StateChange, type SyncState, type Tail } from './sync-state'; type Action = UploadAction; +function both(first: StateChange | undefined, second: StateChange | undefined): StateChange | undefined { + if (!first || !second) return first ?? second; + return (next) => { + first(next); + second(next); + }; +} + /** An item's upload, on its way to the server. */ export interface Pending { held: Held; @@ -122,8 +130,18 @@ export abstract class ItemSync { /** Authority changes folded into a state op of the account; the callback runs once that op is stored. */ protected decorateState(_acct: string, _next: SyncState): (() => void) | void {} protected onDownloaded(_kind: Kind, _acct: string, _object: ServerObject): void {} + /** A download found the object's rows as they should be already. */ + protected onEcho(_kind: Kind, _acct: string, _object: ServerObject): void {} protected onRemoved(_kind: Kind, _acct: string, _id: string): void {} protected onAccepted(_kind: Kind, _acct: string, _server: ServerObject): void {} + /** + * What the SyncState lists changes when a row of `kind` is inserted + * (`present`) or deleted (contacts: `groups`): stored by the batch that + * writes the row, so the list never disagrees with the rows. + */ + protected listing(_kind: Kind, _acct: string, _id: string, _present: boolean): StateChange | undefined { + return undefined; + } protected onDiscard(_next: SyncState, _accounts: Set): void {} /** Whether a pending create of `target` may adopt this object (contacts: the target book holds it). */ protected adoptionTargetMatches(acct: string, _object: ServerObject, target: string): boolean { @@ -588,6 +606,7 @@ export abstract class ItemSync { const ref = refOf(acct, object.id); const plan = kind.planDownload(object, local ?? null, acct); if (plan.effect === 'none' || !hasWrites(plan.ops.ops)) { + if (local) this.onEcho(kind, acct, object); if (!local || kind.meta(local).dirty) return null; const heal = kind.planBaselineHeal(local); return heal && hasWrites(heal.ops) ? { group: heal } : null; @@ -595,6 +614,7 @@ export abstract class ItemSync { const group = local ? plan.ops : prependOps(plan.ops, [insertGuard(kind, ref)]); return { group, + state: this.listing(kind, acct, object.id, true), applied: () => { if (plan.effect === 'insert') this.env.report.stats.downloaded.created++; else this.env.report.stats.downloaded.updated++; @@ -629,6 +649,7 @@ export abstract class ItemSync { const plan = kind.planDownload(object, null, acct); return { group: concatGroups(ref, found.kind.planLocalDelete(found.local), plan.ops), + state: both(this.listing(found.kind, acct, object.id, false), this.listing(kind, acct, object.id, true)), applied: () => { this.env.report.stats.downloaded.updated++; this.markResolved(acct, object.id); @@ -657,6 +678,7 @@ export abstract class ItemSync { const conflict = reason === 'destroyed' && meta.dirty && !meta.deleted; return { group: found.kind.planLocalDelete(found.local), + state: this.listing(found.kind, acct, id, false), applied: () => { this.env.report.stats.downloaded.deleted++; if (conflict) this.env.report.conflicts++; @@ -952,6 +974,7 @@ export abstract class ItemSync { const id = idInAccount(meta.sourceId, acct); return { group: held.kind.planLocalDelete(held.local), + state: id ? this.listing(held.kind, acct, id, false) : undefined, applied: () => { if (uploaded) this.env.report.stats.uploaded.deleted++; if (id) this.onRemoved(held.kind, acct, id); @@ -1280,10 +1303,12 @@ export abstract class ItemSync { this.markStale(acct, server.id); this.env.report.itemError({ ref: refOf(acct, server.id), side: 'upload', type: reason, description: message }); }; + const state = this.listing(kind, acct, server.id, true); return { group: withExtra(plan.ops), + state, applied: counted, - replan: async () => ({ group: withExtra(plan.keepDirtyOps), applied: counted, failed }), + replan: async () => ({ group: withExtra(plan.keepDirtyOps), state, applied: counted, failed }), failed, }; } @@ -1310,11 +1335,19 @@ export abstract class ItemSync { const object = id ? (await this.env.jmap.get(this.itemType, acct, [id], this.itemProperties)).list[0] : undefined; const objectKind = object ? this.kindOf(object) : null; if (!object || !objectKind || !this.inSelection(acct, object)) { - return [{ group: remove, applied: () => (id ? this.onRemoved(kind, acct, id) : undefined) }]; + return [{ + group: remove, + state: id ? this.listing(kind, acct, id, false) : undefined, + applied: () => (id ? this.onRemoved(kind, acct, id) : undefined), + }]; } await this.beforePlanning(acct, [object]); const insert = objectKind.planDownload(object, null, acct); - return [{ group: concatGroups(ref, remove, insert.ops), applied: () => this.onDownloaded(objectKind, acct, object) }]; + return [{ + group: concatGroups(ref, remove, insert.ops), + state: both(this.listing(kind, acct, object.id, false), this.listing(objectKind, acct, object.id, true)), + applied: () => this.onDownloaded(objectKind, acct, object), + }]; } /** diff --git a/src/device-sync/engine/run.ts b/src/device-sync/engine/run.ts index d037c332..39c06178 100644 --- a/src/device-sync/engine/run.ts +++ b/src/device-sync/engine/run.ts @@ -124,11 +124,11 @@ export async function createEnv(options: EnvOptions): Promise<{ env: RunEnv; syn authority, extras: options.extras, reader: options.reader, - writer: new BatchWriter(options.reader.port, { - maxOps: tuning.maxBatchOps, - maxBytes: tuning.maxBatchBytes, - maxReplans: tuning.maxReplans, - }), + writer: new BatchWriter( + options.reader.port, + { maxOps: tuning.maxBatchOps, maxBytes: tuning.maxBatchBytes, maxReplans: tuning.maxReplans }, + store, + ), report: options.report, checkpoints, store, diff --git a/src/device-sync/engine/sync-state.ts b/src/device-sync/engine/sync-state.ts index f9df3106..46e1e73a 100644 --- a/src/device-sync/engine/sync-state.ts +++ b/src/device-sync/engine/sync-state.ts @@ -132,14 +132,20 @@ export interface Tail { applied(): void; } +/** A change of the SyncState that has to be stored together with the rows it describes. */ +export type StateChange = (next: SyncState) => void; + /** * The committed SyncState of a run. Changes are proposed as a tail: the * batch writer calls `op()` right before sending the last batch (so the * change sees what earlier batches decided, e.g. items put on `stale`), and - * the state is only adopted after that batch applied. + * the state is only adopted after that batch applied. Changes that describe + * the rows of one batch (contacts: the groups present) are staged by the + * batch writer while it sends that batch, so its state op carries them. */ export class StateStore { private state: SyncState; + private readonly staged = new Map(); constructor( initial: SyncState, @@ -157,7 +163,16 @@ export class StateStore { return this.state.accounts[jmapAccountId] ?? emptyAccountState(); } - /** A tail applying `mutate` to a copy of the committed state (plus the owner). */ + /** Every state op built until `unstage(key)` applies `change` too, after its own changes. */ + stage(key: object, change: StateChange): void { + this.staged.set(key, change); + } + + unstage(key: object): void { + this.staged.delete(key); + } + + /** A tail applying `mutate` (then the staged changes) to a copy of the committed state (plus the owner). */ tail(mutate: (next: SyncState) => void): Tail { let next: SyncState | null = null; return { @@ -165,6 +180,7 @@ export class StateStore { next = clone(this.state); if (this.owner) next.owner = { ...this.owner }; mutate(next); + for (const change of this.staged.values()) change(next); return { op: 'syncState', value: serializeSyncState(next) }; }, applied: () => { From d088dd14e9e77fd829cb2143cf5535aa483abd3e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:42:50 +0200 Subject: [PATCH 054/334] fix: stop asking about waiting changes after a group edit reached the server #34 A contact stays marked as changed until the group cards on the server show its membership edits. The engine uploaded those edits as patches of the group cards after the contacts, but never looked at the contact again in that run, so it stayed marked until the next sync. Turning contacts sync off or signing out right after adding a contact to a group therefore asked whether to drop one waiting change, although nothing was waiting. Once the membership patches are accepted, the contacts that have nothing else to upload are now cleared in the same run, behind the same version check as any other clean write. --- .../__tests__/engine/real-planners.test.ts | 32 +++++++++++++++++++ src/device-sync/engine/contacts-sync.ts | 26 ++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 2a20e232..fc4e91d5 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -11,6 +11,7 @@ import { addDeviceContact, addServerCards, CALENDAR_AUTHORITY, + CONTACTS_AUTHORITY, createHarness, rowWrites, type Harness, @@ -155,6 +156,37 @@ describe('device sync engine with the real planners', () => { expect(h.contacts().every((c) => !c.dirty)).toBe(true); }); + describe('a membership edit on the device', () => { + async function adaJoinsFriends() { + const h = real(); + const [ada] = addServerCards(h, ['Ada Lovelace']); + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: {} }); + await h.run(); + const contact = h.contactNamed('Ada Lovelace')!; + h.device.user.addToGroup(contact.id, Number(h.device.rows('groups')[0]._id)); + return { h, ada, group, contact }; + } + + it('leaves the contact clean once the group card shows it', async () => { + const { h, ada, group, contact } = await adaJoinsFriends(); + + expect((await h.run()).outcome).toBe('ok'); + + const uid = h.server.get('ContactCard', 'a', ada)!.uid as string; + expect(h.server.get('ContactCard', 'a', group)!.members).toEqual({ [uid]: true }); + expect(h.contacts().find((c) => c.id === contact.id)).toMatchObject({ dirty: false }); + }); + + it('leaves nothing waiting for the teardown once it is uploaded', async () => { + const { h, ada, group } = await adaJoinsFriends(); + + expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); + + const uid = h.server.get('ContactCard', 'a', ada)!.uid as string; + expect(h.server.get('ContactCard', 'a', group)!.members).toEqual({ [uid]: true }); + }); + }); + describe('a device edit of an object the server moved out of every synced collection', () => { /** Ada with two numbers in the synced book; the device deletes the second in place (AOSP). */ async function adaWithDeletedNumber() { diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 9a35eb32..fe917a84 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -710,7 +710,31 @@ export class ContactsSync extends ItemSync { } pending.actions.push(action as Pending['actions'][number]); } - if (byGroup.size) await this.send(acct, [...byGroup.values()]); + if (!byGroup.size) return; + await this.send(acct, [...byGroup.values()]); + await this.cleanAfterMemberships(acct, rows); + } + + /** + * A contact stays dirty until the group cards show its membership edits. + * Those that now have nothing else to upload are cleaned right away, so + * nothing looks like it still waits (turning sync off would ask about it). + */ + private async cleanAfterMemberships(acct: string, rowIds: number[]): Promise { + await this.refreshGroups(); + const works: Work[] = []; + for (const contact of await this.contactKind.loadByRowIds(rowIds)) { + if (!contact.dirty || contact.deleted || this.isStale(acct, contact.sourceId)) continue; + let plan; + try { + plan = this.contactKind.planUpload(contact, acct); + } catch (error) { + this.plannerFailed(acct, contact.sourceId ?? `row:${contact.rawContactId}`, 'upload', error); + continue; + } + if (plan.kind === 'clean') works.push({ group: plan.ops }); + } + await this.env.writer.write(works); } // ── Deselection ── From 09c2ff1f67a0fe981989a8ec891d85d66eb65a21 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:43:31 +0200 Subject: [PATCH 055/334] fix: upload a contact's removal from a group made on the phone #34 Whether a membership edit still has to go up is decided against the group cards on the server, which the engine indexes from their shadows. The index was only read before planning a download that held contacts, so in a run that downloaded no contact, such as the upload sync after an edit, it was empty: removing a contact from a group looked already done on the server, the contact was marked clean and the removal was never sent. The engine now reads the index before planning uploads too. --- .../__tests__/engine/real-planners.test.ts | 18 ++++++++++++++++++ src/device-sync/engine/contacts-sync.ts | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index fc4e91d5..097c1f2b 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -185,6 +185,24 @@ describe('device sync engine with the real planners', () => { const uid = h.server.get('ContactCard', 'a', ada)!.uid as string; expect(h.server.get('ContactCard', 'a', group)!.members).toEqual({ [uid]: true }); }); + + it('uploads a removal from a group in a run that downloads no contact', async () => { + const h = real(); + // A keyed phone row shows the editor works in place, so a missing membership row is a removal. + h.server.addCard('a', { uid: 'ada-uid', name: { full: 'Ada Lovelace' }, phones: { p1: { '@type': 'Phone', number: '+1 111' } } }); + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { 'ada-uid': true } }); + await h.run(); + const contact = h.contactNamed('Ada Lovelace')!; + const membership = h.device.rows('data').find((d) => Number(d.raw_contact_id) === contact.id && d.mimetype === MimeType.GROUP_MEMBERSHIP)!; + h.device.user.deleteData(Number(membership._id)); + + // The upload sync Android requests after the edit: the server has nothing new. + expect((await h.run(CONTACTS_AUTHORITY, { upload: true })).outcome).toBe('ok'); + + expect(h.server.get('ContactCard', 'a', group)!.members ?? {}).toEqual({}); + expect(h.contacts().find((c) => c.id === contact.id)).toMatchObject({ dirty: false }); + expect(h.device.rows('data').filter((d) => d.mimetype === MimeType.GROUP_MEMBERSHIP)).toEqual([]); + }); }); describe('a device edit of an object the server moved out of every synced collection', () => { diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index fe917a84..87854e03 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -576,8 +576,14 @@ export class ContactsSync extends ItemSync { for (const held of items) if (held.kind === this.contactKind) rows.add(held.kind.meta(held.local).rowId); } - /** The device photo of every contact about to be planned that has a photo row (JPEG, ≤ 512 px). */ + /** + * The group index `groupsOf` answers from (a run that downloaded no + * contact never read it: every removal from a group would look done), and + * the device photo of every contact about to be planned that has a photo + * row (JPEG, ≤ 512 px). + */ protected async beforeUploadPlanning(items: Held[]): Promise { + if (this.groupsStale && items.some((held) => held.kind === this.contactKind)) await this.refreshGroups(); for (const held of items) { if (held.kind !== this.contactKind) continue; const contact = held.local as LocalContact; From 34236f677b3ff06beb6e5220ef42802f43816859 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:44:10 +0200 Subject: [PATCH 056/334] fix: never resend a group's members from an outdated copy #34 A group whose latest server version the device could not store waits on the stale list, and nothing uploads for it until it has been fetched again. Membership edits bypassed that check: adding a contact to such a group sent the whole member list as the device last knew it, which removed members another client had added meanwhile. Membership edits for a stale group now wait for that fetch, and the report lists them. --- .../__tests__/engine/real-planners.test.ts | 30 +++++++++++++++++++ src/device-sync/engine/contacts-sync.ts | 5 ++++ 2 files changed, 35 insertions(+) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 097c1f2b..a493d065 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -186,6 +186,36 @@ describe('device sync engine with the real planners', () => { expect(h.server.get('ContactCard', 'a', group)!.members).toEqual({ [uid]: true }); }); + it('waits with a membership edit while the group card is stale, instead of resending its members', async () => { + const { h, ada, group } = await adaJoinsFriends(); + const [bob] = addServerCards(h, ['Bob']); + const bobUid = h.server.get('ContactCard', 'a', bob)!.uid as string; + // Another client adds Bob; this device cannot store the new version of the group this run. + h.server.serverUpdate('ContactCard', 'a', group, { members: { [bobUid]: true } }); + h.deps.planners = { + ...h.deps.planners, + contacts: { + ...contactsPlanner, + planGroupDownload: (card, local, ctx) => { + if (card.id === group) throw new Error('cannot store this group now'); + return contactsPlanner.planGroupDownload(card, local, ctx); + }, + }, + }; + + await h.run(); + + const adaUid = h.server.get('ContactCard', 'a', ada)!.uid as string; + expect(h.server.get('ContactCard', 'a', group)!.members).toEqual({ [bobUid]: true }); + expect(h.contacts().find((c) => c.name === 'Ada Lovelace')).toMatchObject({ dirty: true }); + + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + expect((await h.run()).outcome).toBe('ok'); + + expect(h.server.get('ContactCard', 'a', group)!.members).toEqual({ [bobUid]: true, [adaUid]: true }); + expect(h.contacts().find((c) => c.name === 'Ada Lovelace')).toMatchObject({ dirty: false }); + }); + it('uploads a removal from a group in a run that downloads no contact', async () => { const h = real(); // A keyed phone row shows the editor works in place, so a missing membership row is a removal. diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 87854e03..3c8660ba 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -708,6 +708,11 @@ export class ContactsSync extends ItemSync { this.env.report.itemError({ ref: id ? refOf(acct, id) : 'membership', side: 'upload', type: 'unknownGroup' }); continue; } + if (this.isStale(acct, group.sourceId)) { + // Its shadow is behind the server: members built from it could undo server changes. The contact stays dirty. + this.env.report.itemError({ ref: group.sourceId as string, side: 'upload', type: 'stale', description: 'Waits until the server version could be stored' }); + continue; + } let pending = byGroup.get(group.sourceId as string); if (!pending) { const held: Held = { kind: this.groupKind, local: group }; From fad7b30dbf67de28dff85ea815a1558d3147fe5a Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:44:47 +0200 Subject: [PATCH 057/334] fix: upload new contacts whose address book was deleted meanwhile #34 A new contact is claimed for an address book before its create is sent, so a retry after a lost response finds it again. When that book was deleted on the server before the create went up, every attempt failed and was retried after up to a day, forever. A book that is gone can hold nothing of an earlier attempt, so the contact is now claimed again for the book the settings choose. An event cannot move on its own to another calendar: a create into a calendar that is gone is no longer sent, and the report says why, until the user moves the event in the calendar app. --- .../__tests__/engine/real-planners.test.ts | 49 +++++++++++++++++++ src/device-sync/engine/calendar-sync.ts | 6 +++ src/device-sync/engine/contacts-sync.ts | 8 +++ src/device-sync/engine/item-sync.ts | 22 +++++++-- src/device-sync/engine/kinds.ts | 2 + 5 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index a493d065..9a040a0d 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -235,6 +235,55 @@ describe('device sync engine with the real planners', () => { }); }); + describe('a create whose target the server deleted after the claim', () => { + it('claims a new contact again for a book that exists', async () => { + const h = real(); + const work = h.server.addAddressBook('a', { name: 'Work' }); + h.prefs.newContactsAddressBook = `a/${work}`; + await h.run(); + const id = addDeviceContact(h, 'Hedy Lamarr'); + // The claim names Work, the create is lost; then another client deletes Work. + h.server.failNextRequest('network', { match: 'ContactCard/set' }); + expect((await h.run()).outcome).toBe('io'); + h.server.serverDestroy('AddressBook', 'a', work); + + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 1 } } }); + const hedy = h.server.all('ContactCard', 'a').find((c) => (c.name as { full?: string }).full === 'Hedy Lamarr')!; + expect(hedy.addressBookIds).toEqual({ [h.book]: true }); + expect(h.contacts().find((c) => c.id === id)).toMatchObject({ sourceId: `a/${hedy.id}`, dirty: false }); + expect(h.contacts().find((c) => c.id === id)!.row.sync4).toBeNull(); + }); + + it('keeps a new event whose calendar is gone, without poisoning it, until the user moves it', async () => { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + await h.run(CALENDAR_AUTHORITY); + const rowOf = (calendarId: string) => Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${calendarId}`)!._id); + const workRow = rowOf(work); + const dentist = { title: 'Dentist', dtstart: Date.UTC(2026, 9, 1, 8), dtend: Date.UTC(2026, 9, 1, 9), eventTimezone: 'Europe/Berlin' }; + const id = h.device.user.insertEvent(workRow, dentist); + h.server.failNextRequest('network', { match: 'CalendarEvent/set' }); + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('io'); + h.server.serverDestroy('Calendar', 'a', work); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `row:${id}`, type: 'collectionGone' })]); + expect(h.server.all('CalendarEvent', 'a')).toEqual([]); + expect(h.events().find((e) => Number(e._id) === id)).toMatchObject({ title: 'Dentist', sync_data5: null }); + + // The user moves it to the personal calendar (Etar: delete + insert). + h.device.user.deleteEvent(id); + h.device.user.insertEvent(rowOf(h.calendar), dentist); + expect(await h.run(CALENDAR_AUTHORITY)).toMatchObject({ outcome: 'ok', itemErrors: [] }); + + expect(h.server.all('CalendarEvent', 'a').map((e) => [e.title, e.calendarIds])).toEqual([['Dentist', { [h.calendar]: true }]]); + expect(h.device.rows('calendars').map((c) => c._sync_id)).toEqual([`a/${h.calendar}`]); + }); + }); + describe('a device edit of an object the server moved out of every synced collection', () => { /** Ada with two numbers in the synced book; the device deletes the second in place (AOSP). */ async function adaWithDeletedNumber() { diff --git a/src/device-sync/engine/calendar-sync.ts b/src/device-sync/engine/calendar-sync.ts index e14d8264..fab6327c 100644 --- a/src/device-sync/engine/calendar-sync.ts +++ b/src/device-sync/engine/calendar-sync.ts @@ -257,6 +257,12 @@ export class CalendarSync extends ItemSync { await this.refreshCalendarRows(); } + protected collectionGone(key: string): boolean { + const parsed = parseCollectionKey(key); + const list = parsed ? this.calendars.get(parsed.accountId) : undefined; + return !!list && !list.some((c) => c.id === parsed!.id); + } + protected syncedCollections(acct: string): string[] { return (this.calendars.get(acct) ?? []) .filter((c) => this.syncedKeys.has(collectionKey(acct, c.id))) diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 3c8660ba..228443e8 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -172,6 +172,7 @@ export class ContactsSync extends ItemSync { planBaselineHeal: (c) => planner.planBaselineHeal(c), planUpload: (c, acct) => planner.planUpload(c, this.ctx(acct)), planAccepted: (c, card, acct) => planner.planAccepted(c, card, this.ctx(acct)), + unclaimed: (c) => ({ ...c, pending: null }), }; } @@ -205,6 +206,7 @@ export class ContactsSync extends ItemSync { planBaselineHeal: () => null, planUpload: (g, acct) => planner.planGroupUpload(g, this.ctx(acct)), planAccepted: (g, card, acct) => planner.planGroupAccepted(g, card, this.ctx(acct)), + unclaimed: (g) => ({ ...g, pending: null }), }; } @@ -284,6 +286,12 @@ export class ContactsSync extends ItemSync { return !!book && book.myRights?.mayWrite !== false; } + protected collectionGone(key: string): boolean { + const parsed = parseCollectionKey(key); + const books = parsed ? this.books.get(parsed.accountId) : undefined; + return !!books && !books.some((b) => b.id === parsed!.id); + } + protected async collections(write: boolean): Promise { this.syncedKeys.clear(); for (const account of this.env.accounts) { diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 84e9c254..4bda226a 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -155,6 +155,10 @@ export abstract class ItemSync { protected claimable(_held: Held): boolean { return true; } + /** Whether a collection key names a collection its account no longer has (listed this run). */ + protected collectionGone(_key: string): boolean { + return false; + } /** The JMAP account an item uploads to. */ protected accountOfItem(held: Held): string | null { const meta = held.kind.meta(held.local); @@ -792,12 +796,18 @@ export abstract class ItemSync { return this.setStates.get(acct) ?? null; } - /** New items get their uid and target written before anything is sent. */ + /** + * New items get their uid and target written before anything is sent. A + * claim whose collection is gone from the server is made again where the + * kind allows it (contacts: nothing of it can exist in a deleted book). + */ private async claimNewItems(): Promise { const works: Work[] = []; for (const held of await this.loadUploadItems()) { const meta = held.kind.meta(held.local); - if (!meta.isNew || meta.pending || meta.deleted || !this.claimable(held)) continue; + if (!meta.isNew || meta.deleted || !this.claimable(held)) continue; + const reclaim = !!meta.pending && !!held.kind.unclaimed && this.collectionGone(meta.pending.target); + if (meta.pending && !reclaim) continue; const ref = `row:${meta.rowId}`; const acct = this.claimAccount(held); if (!acct) { @@ -807,7 +817,7 @@ export abstract class ItemSync { } let plan; try { - plan = held.kind.planUpload(held.local, acct); + plan = held.kind.planUpload(reclaim ? held.kind.unclaimed!(held.local) : held.local, acct); } catch (error) { this.plannerFailed(acct, ref, 'upload', error); continue; @@ -915,6 +925,12 @@ export abstract class ItemSync { report.itemError({ ref, side: 'upload', type: 'stale', description: 'Waits until the server version could be stored' }); return null; } + if (meta.isNew && !meta.deleted && meta.pending && this.collectionGone(meta.pending.target)) { + // Nothing can be created there (an event stays in its calendar until the user moves it). + report.stats.skipped++; + report.itemError({ ref, side: 'upload', type: 'collectionGone', description: `${meta.pending.target} no longer exists on the server` }); + return null; + } const fingerprint = kind.fingerprint(local); if (meta.poison && isBackedOff(meta.poison, fingerprint, this.env.now())) { report.stats.skipped++; diff --git a/src/device-sync/engine/kinds.ts b/src/device-sync/engine/kinds.ts index 7ef6538f..10881a05 100644 --- a/src/device-sync/engine/kinds.ts +++ b/src/device-sync/engine/kinds.ts @@ -57,6 +57,8 @@ export interface Kind { planBaselineHeal(local: L): OpGroup | null; planUpload(local: L, jmapAccountId: string): UploadPlan; planAccepted(local: L, server: O, jmapAccountId: string): AcceptedPlan; + /** The item without its pending create, so `planUpload` claims it again; absent when the target can't change. */ + unclaimed?(local: L): L; } export function refOf(jmapAccountId: string, id: string): string { From 2e9e5bb7855ad96eaab75b0fd609de1fc66a5397 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:45:24 +0200 Subject: [PATCH 058/334] fix: remove items the server deleted right after this device created them #34 After an upload the stored state stays where the download left it, and the engine relied on /changes to report its own creates back. /changes leaves out an object that was created and destroyed after the state it is asked from (Stalwart does, RFC 8620 allows it), so an event or contact created on the device and deleted on the server before the next download stayed on the device for good. The SyncState now lists the objects our uploads created, written with their identity, and the next run fetches them first: one that is gone loses its rows, an unchanged one writes nothing. An upload-only sync runs in full while the list is not empty. --- .../__tests__/engine/own-creates.test.ts | 77 +++++++++++++++++++ src/device-sync/engine/item-sync.ts | 42 ++++++++-- src/device-sync/engine/run.ts | 2 +- src/device-sync/engine/sync-state.ts | 6 ++ 4 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 src/device-sync/__tests__/engine/own-creates.test.ts diff --git a/src/device-sync/__tests__/engine/own-creates.test.ts b/src/device-sync/__tests__/engine/own-creates.test.ts new file mode 100644 index 00000000..76eb8c94 --- /dev/null +++ b/src/device-sync/__tests__/engine/own-creates.test.ts @@ -0,0 +1,77 @@ +// Objects our uploads created. The stored state stays where the download left +// it, and `/changes` omits an object created and destroyed after its +// `sinceState` (Stalwart does, RFC 8620 allows it): such an object must still +// leave the device when another client destroys it before the next download. + +import { describe, expect, it } from 'vitest'; +import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { addDeviceContact, addServerCards, CALENDAR_AUTHORITY, createHarness, type Harness } from './harness'; + +function real(): Harness { + const h = createHarness(); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + return h; +} + +describe('device sync engine: objects our uploads created', () => { + it('removes a device-created contact that another client destroyed before the next download', async () => { + const h = real(); + addServerCards(h, ['Ada Lovelace']); + await h.run(); + const id = addDeviceContact(h, 'Hedy Lamarr'); + expect((await h.run()).stats.uploaded.created).toBe(1); + const hedy = h.contacts().find((c) => c.id === id)!.sourceId!.split('/')[1]; + h.server.serverDestroy('ContactCard', 'a', hedy); + + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', stats: { downloaded: { deleted: 1 } } }); + expect(h.contacts().map((c) => c.name)).toEqual(['Ada Lovelace']); + expect(h.state()?.accounts.a.created ?? []).toEqual([]); + }); + + it('removes a device-created event that another client destroyed before the next download', async () => { + const h = real(); + await h.run(CALENDAR_AUTHORITY); + const rowId = Number(h.device.rows('calendars')[0]._id); + const id = h.device.user.insertEvent(rowId, { title: 'Dentist', dtstart: Date.UTC(2026, 9, 1, 8), dtend: Date.UTC(2026, 9, 1, 9), eventTimezone: 'Europe/Berlin' }); + expect((await h.run(CALENDAR_AUTHORITY)).stats.uploaded.created).toBe(1); + const created = String(h.events().find((e) => Number(e._id) === id)!._sync_id).split('/')[1]; + h.server.serverDestroy('CalendarEvent', 'a', created); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', stats: { downloaded: { deleted: 1 } } }); + expect(h.events()).toEqual([]); + }); + + it('fetches a created object once, and writes nothing when it is unchanged', async () => { + const h = real(); + await h.run(); + addDeviceContact(h, 'Hedy Lamarr'); + await h.run(); + expect(h.state()?.accounts.a.created).toHaveLength(1); + + const before = h.batches.log.length; + expect((await h.run()).outcome).toBe('ok'); + + expect(h.state()?.accounts.a.created).toEqual([]); + expect(h.batches.log.slice(before).flat().filter((op) => op.op !== 'syncState')).toEqual([]); + expect(h.contacts().map((c) => c.name)).toEqual(['Hedy Lamarr']); + }); + + it('turns an upload-only sync into a full one while a created object waits to be checked', async () => { + const h = real(); + await h.run(); + const id = addDeviceContact(h, 'Hedy Lamarr'); + await h.run(); + const hedy = h.contacts().find((c) => c.id === id)!.sourceId!.split('/')[1]; + h.server.serverDestroy('ContactCard', 'a', hedy); + + // Android's upload sync 30 s after an app wrote anything, with nothing dirty in this account. + expect((await h.run(undefined, { upload: true })).outcome).toBe('ok'); + + expect(h.contacts()).toEqual([]); + }); +}); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 4bda226a..b0eae42d 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -44,6 +44,19 @@ function both(first: StateChange | undefined, second: StateChange | undefined): }; } +/** The work with one more state change, also after a re-plan. */ +function withState(work: Work, change: StateChange): Work { + const replan = work.replan; + return { + ...work, + state: both(work.state, change), + replan: replan && (async () => { + const next = await replan(); + return next && withState(next, change); + }), + }; +} + /** An item's upload, on its way to the server. */ export interface Pending { held: Held; @@ -324,10 +337,20 @@ export abstract class ItemSync { // ── Downloads ── - /** Objects put on `stale` by an earlier run: fetched again first (invariant 10). */ + /** + * Objects put on `stale` by an earlier run (invariant 10), and objects our + * uploads created since the stored state (`/changes` from it omits one that + * was created and destroyed since): fetched again first. + */ protected async refetchStale(acct: string): Promise { - const ids = [...this.env.store.account(acct).stale]; - if (ids.length) await this.processIds(acct, ids, [], () => undefined); + const account = this.env.store.account(acct); + const created = account.created ?? []; + const ids = [...new Set([...account.stale, ...created])]; + if (!ids.length) return; + await this.processIds(acct, ids, [], (next) => { + const a = accountOf(next, acct); + a.created = (a.created ?? []).filter((id) => !created.includes(id)); + }); } /** `/changes` pages from the stored state; a full reconcile without one or after `cannotCalculateChanges`. */ @@ -1070,7 +1093,8 @@ export abstract class ItemSync { } await this.beforePlanning(acct, [object]); const work = this.downloadWork(p.held.kind, acct, object, p.held.local, null); - return work ? [work] : []; + // Our own earlier create: remembered like one of this run. + return work ? [withState(work, this.rememberCreated(acct, id))] : []; } /** Sends the items' actions in `/set` calls and settles each item once all its actions have an answer. */ @@ -1319,7 +1343,7 @@ export abstract class ItemSync { this.markStale(acct, server.id); this.env.report.itemError({ ref: refOf(acct, server.id), side: 'upload', type: reason, description: message }); }; - const state = this.listing(kind, acct, server.id, true); + const state = both(this.listing(kind, acct, server.id, true), created ? this.rememberCreated(acct, server.id) : undefined); return { group: withExtra(plan.ops), state, @@ -1329,6 +1353,14 @@ export abstract class ItemSync { }; } + /** An object our upload created is remembered with its identity until the next run fetched it (see `refetchStale`). */ + private rememberCreated(acct: string, id: string): StateChange { + return (next) => { + const account = accountOf(next, acct); + account.created = [...new Set([...(account.created ?? []), id])]; + }; + } + private poisonWork(p: Pending, error: SetErrorShape): Work { const marker = nextMarker(p.meta.poison, p.fingerprint, error, this.env.now(), { firstMs: this.env.tuning.poisonBackoffMs, diff --git a/src/device-sync/engine/run.ts b/src/device-sync/engine/run.ts index 39c06178..883be9c5 100644 --- a/src/device-sync/engine/run.ts +++ b/src/device-sync/engine/run.ts @@ -48,7 +48,7 @@ function itemTypeOf(authority: Authority): string { /** Rows waiting for an upload (dirty, deleted, new), or state work a run must do whatever its extras. */ export async function hasLocalWork(reader: ProviderReader, authority: Authority, state: SyncState): Promise { for (const account of Object.values(state.accounts)) { - if (account.reconcile || account.stale.length) return true; + if (account.reconcile || account.stale.length || account.created?.length) return true; } if (authority === CALENDAR_AUTHORITY) { const rows = await reader.rows('events', ['_id'], "dirty = 1 OR deleted = 1 OR _sync_id IS NULL OR _sync_id LIKE '~pending/%'"); diff --git a/src/device-sync/engine/sync-state.ts b/src/device-sync/engine/sync-state.ts index 46e1e73a..2d07f9ab 100644 --- a/src/device-sync/engine/sync-state.ts +++ b/src/device-sync/engine/sync-state.ts @@ -29,6 +29,11 @@ export interface AccountState { selected: string[]; /** Object ids whose rows could not be written; fetched again at the start of every run, nothing uploads for them meanwhile. */ stale: string[]; + /** + * Objects our uploads created after `itemsState`: fetched again at the start of the next run, since `/changes` + * omits an object created and destroyed after its `sinceState`. + */ + created?: string[]; reconcile: ReconcileMarker | null; /** Contacts: refs of the group cards present on the device (a hard-deleted group is found by its absence). */ groups?: string[]; @@ -81,6 +86,7 @@ function parseAccount(value: unknown): AccountState { stale: strings(a.stale), reconcile: parseReconcile(a.reconcile), }; + if (a.created !== undefined) out.created = strings(a.created); if (a.groups !== undefined) out.groups = strings(a.groups); if (a.taskOnly !== undefined) out.taskOnly = strings(a.taskOnly); return out; From 7701a67dff89b50bd855087560d03d97ff09af65 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:46:01 +0200 Subject: [PATCH 059/334] fix: keep favourites and joins when the server refuses an edit #34 When the server refused an edit made on the device (a book or calendar that turned read-only, an event in a calendar where only one's own events may be changed), the engine put the item back by deleting its rows and inserting a fresh copy. A contact lost its star, ringtone and the link to the contacts it was joined with; an event got a new row id. The rows are now rewritten in place from the server's version, as after an accepted upload. A refused deletion still brings the item back as a new copy. --- .../__tests__/engine/real-planners.test.ts | 57 +++++++++++++++++++ src/device-sync/engine/item-sync.ts | 19 +++++++ 2 files changed, 76 insertions(+) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 9a040a0d..03291e0b 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -13,6 +13,7 @@ import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, createHarness, + renameDeviceContact, rowWrites, type Harness, } from './harness'; @@ -235,6 +236,62 @@ describe('device sync engine with the real planners', () => { }); }); + describe('a device edit the server refuses (forbidden)', () => { + it('puts a contact back in place, keeping what only the device knows', async () => { + const h = real(); + const [ada] = addServerCards(h, ['Ada Lovelace']); + await h.run(); + const before = h.contactNamed('Ada Lovelace')!; + h.device.user.star(before.id); + renameDeviceContact(h, before.id, 'Ada King'); + h.server.setErrorFor('ContactCard', 'a', ada, { type: 'forbidden' }); + + const report = await h.run(); + + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `a/${ada}`, type: 'forbidden' })]); + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ name: { full: 'Ada Lovelace' } }); + expect(h.contacts()).toEqual([expect.objectContaining({ id: before.id, name: 'Ada Lovelace', dirty: false })]); + expect(h.contacts()[0].row.starred).toBe(1); + }); + + it('brings a contact back whose deletion the server refuses', async () => { + const h = real(); + const [ada] = addServerCards(h, ['Ada Lovelace']); + await h.run(); + h.device.user.deleteContact(h.contactNamed('Ada Lovelace')!.id); + h.server.setErrorFor('ContactCard', 'a', ada, { type: 'forbidden' }); + + const report = await h.run(); + + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `a/${ada}`, type: 'forbidden' })]); + expect(h.server.get('ContactCard', 'a', ada)).toBeDefined(); + expect(h.contacts()).toEqual([expect.objectContaining({ sourceId: `a/${ada}`, name: 'Ada Lovelace', dirty: false, deleted: false })]); + }); + + it('puts an event back in place', async () => { + const h = real(); + const standup = h.server.addEvent('a', { + uid: 'standup-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const row = h.events()[0]; + h.device.user.updateEvent(Number(row._id), { [Events.TITLE]: 'Mine', [Events.CUSTOM_APP_PACKAGE]: 'org.example.app' }); + h.server.setErrorFor('CalendarEvent', 'a', standup, { type: 'forbidden' }); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `a/${standup}`, type: 'forbidden' })]); + expect(h.events()).toEqual([ + expect.objectContaining({ _id: row._id, title: 'Standup', dirty: 0, [Events.CUSTOM_APP_PACKAGE]: 'org.example.app' }), + ]); + }); + }); + describe('a create whose target the server deleted after the claim', () => { it('claims a new contact again for a book that exists', async () => { const h = real(); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index b0eae42d..ee169339 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -1390,6 +1390,8 @@ export abstract class ItemSync { }]; } await this.beforePlanning(acct, [object]); + // An edit is undone in place, so what only the device keeps stays: a star, a ringtone, a joined contact, row ids. + if (objectKind === kind && !p.meta.deleted) return [this.overwriteWork(acct, kind, local, object)]; const insert = objectKind.planDownload(object, null, acct); return [{ group: concatGroups(ref, remove, insert.ops), @@ -1398,6 +1400,23 @@ export abstract class ItemSync { }]; } + /** The rows become `object` where they are and DIRTY is cleared (the planner's write of an accepted version). */ + private overwriteWork(acct: string, kind: Kind, local: unknown, object: ServerObject): Work { + return { + group: kind.planAccepted(local, object, acct).ops, + state: this.listing(kind, acct, object.id, true), + applied: () => this.onDownloaded(kind, acct, object), + replan: async () => { + const again = await this.reload(kind, local); + return again === null ? null : this.overwriteWork(acct, kind, again, object); + }, + failed: (reason, message) => { + this.markStale(acct, object.id); + this.env.report.itemError({ ref: refOf(acct, object.id), side: 'upload', type: reason, description: message }); + }, + }; + } + /** * `invalidProperties` on `uid`: the object exists (a lost response, or the * client's automatic retry of the create). Cards name it in the error; From 474eae9992a9f72856015aa5fef267c42f654758 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:46:39 +0200 Subject: [PATCH 060/334] fix: never duplicate an event's reminders after a busy calendar database #34 A provider that is busy may commit a batch up to a yield point and then fail it. The engine retried every group of such a batch and relied on the groups' asserts to refuse one that was already committed, but a download into an unchanged event asserts only the event's own columns: when it had only added a reminder or an attendee, the retry added it a second time. A download is now planned again from a new read after a failed batch instead of being sent again, which turns a committed group into an echo. The fake provider can commit a batch up to a yield point to test it. --- .../__tests__/engine/internals.test.ts | 12 ++++++++++ .../__tests__/engine/real-planners.test.ts | 23 ++++++++++++++++++ .../__tests__/fakes/fake-provider.ts | 24 +++++++++++++++++++ src/device-sync/engine/batch.ts | 24 +++++++++++++++---- src/device-sync/engine/item-sync.ts | 1 + 5 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/device-sync/__tests__/engine/internals.test.ts b/src/device-sync/__tests__/engine/internals.test.ts index 9609e1b4..4fee104b 100644 --- a/src/device-sync/__tests__/engine/internals.test.ts +++ b/src/device-sync/__tests__/engine/internals.test.ts @@ -157,6 +157,18 @@ describe('provider batches', () => { expect(store.committed.accounts.a.groups).toEqual(['a/g1']); }); + it('plans a download again after a failed batch, which may have been committed up to a yield point', async () => { + let failures = 1; + const p = port((ops) => (failures-- > 0 ? { ok: false, reason: 'provider', message: 'failed after a yield point' } : ok(ops))); + const planned: Work = { group: { ref: 'a/1', ops: [{ op: 'update', table: 'events', id: 1, values: { title: 'Planned from the new read' } }] } }; + const download: Work = { group: { ref: 'a/1', ops: [insert('data', { stale: 'group' })] }, fresh: true, replan: async () => planned }; + const other: Work = { group: { ref: 'a/2', ops: [insert('raw_contacts')] } }; + + await writer(p).write([download, other]); + + expect(p.sent.slice(1)).toEqual([planned.group.ops.map((op) => ({ ...op, yieldAllowed: true })), buildBatch([other.group])]); + }); + it('skips groups that only assert', async () => { const p = port(ok); await writer(p).write([{ group: { ref: 'g', ops: [{ op: 'assert', table: 'raw_contacts', id: 1, values: { dirty: 0 } }] } }]); diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 03291e0b..bf494cdd 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -236,6 +236,29 @@ describe('device sync engine with the real planners', () => { }); }); + describe('a batch the provider committed up to a yield point before it failed', () => { + const alert = { al1: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT15M' }, action: 'display' } }; + + it("never inserts a clean event's new reminder twice", async () => { + const h = real(); + const event = (uid: string, title: string) => + h.server.addEvent('a', { uid, title, start: '2026-10-06T12:00:00', duration: 'PT1H', timeZone: 'Europe/Berlin', calendarIds: { [h.calendar]: true } }); + const lunch = event('lunch-uid', 'Lunch'); + const retro = event('retro-uid', 'Retro'); + await h.run(CALENDAR_AUTHORITY); + // Only an alert for Lunch (its row's columns stay), a new title for Retro: one download batch, two groups. + h.server.serverUpdate('CalendarEvent', 'a', lunch, { alerts: alert }); + h.server.serverUpdate('CalendarEvent', 'a', retro, { title: 'Retrospective' }); + h.device.partialCommitNextBatch(1, 'assert', (ops) => ops.some((op) => op.op === 'insert' && op.table === 'reminders')); + + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + + const row = h.events().find((e) => e.title === 'Lunch')!; + expect(h.device.rows('reminders').filter((r) => Number(r.event_id) === Number(row._id)).map((r) => r.minutes)).toEqual([15]); + expect(h.events().map((e) => e.title).sort()).toEqual(['Lunch', 'Retrospective']); + }); + }); + describe('a device edit the server refuses (forbidden)', () => { it('puts a contact back in place, keeping what only the device knows', async () => { const h = real(); diff --git a/src/device-sync/__tests__/fakes/fake-provider.ts b/src/device-sync/__tests__/fakes/fake-provider.ts index 8fbad6e2..100d5229 100644 --- a/src/device-sync/__tests__/fakes/fake-provider.ts +++ b/src/device-sync/__tests__/fakes/fake-provider.ts @@ -258,6 +258,7 @@ export class FakeDeviceProviders { private nextId = 1; private nextPhotoFile = 1000; private pendingFailure: { reason: BatchFailure; message: string } | null = null; + private partialFailure: { groups: number; reason: BatchFailure; when: (ops: ProviderOp[]) => boolean } | null = null; private beforeBatch: Array<() => void> = []; /** Every batch applied or refused, for assertions on write counts. */ readonly batches: Array<{ account: string; authority: Authority; ops: ProviderOp[]; ok: boolean }> = []; @@ -301,6 +302,16 @@ export class FakeDeviceProviders { this.beforeBatch.push(fn); } + /** + * The next applyBatch that `when` matches behaves like a provider that + * yielded under contention: the ops before its `groups + 1`-th yield point + * (its first `groups` groups) are committed, then the batch fails with + * `reason` and the rest is rolled back. + */ + partialCommitNextBatch(groups: number, reason: BatchFailure = 'assert', when: (ops: ProviderOp[]) => boolean = () => true): void { + this.partialFailure = { groups, reason, when }; + } + /** Removes an account the way AccountManager does: the providers drop all its rows and SyncState. */ removeAccount(accountName: string): void { for (const table of ['raw_contacts', 'groups', 'settings', 'calendars', 'colors'] as const) { @@ -517,6 +528,19 @@ export class FakeDeviceProviders { } for (const hook of this.beforeBatch.splice(0)) hook(); + if (this.partialFailure?.when(ops)) { + const { groups, reason } = this.partialFailure; + this.partialFailure = null; + const starts = ops.flatMap((op, i) => (i === 0 || (op as { yieldAllowed?: boolean }).yieldAllowed ? [i] : [])); + const cut = starts[groups] ?? 0; + if (cut > 0) { + const committed = this.applyBatch(accountName, authority, ops.slice(0, cut)); + if (!committed.ok) return committed; + } + record(false); + return { ok: false, reason, message: `injected ${reason} after a yield point` }; + } + const snapshot = { tables: cloneTables(this.tables), syncState: new Map(this.syncState), diff --git a/src/device-sync/engine/batch.ts b/src/device-sync/engine/batch.ts index 2375b86f..a7a454ea 100644 --- a/src/device-sync/engine/batch.ts +++ b/src/device-sync/engine/batch.ts @@ -11,10 +11,12 @@ * and never a yield point: the stored state can't get ahead of its rows. * * A failed batch may have been committed up to a yield point the provider - * took, so its groups are retried one by one (their asserts make a retry of - * a committed group fail harmlessly); a group that fails an assert is - * re-read and re-planned up to `maxReplans` times, then given up. `tooLarge` - * splits the batch; `permission` ends the run. + * took, so its groups are retried one by one: a group planned from the rows + * as they are (`fresh`, a download) is planned again from a new read, any + * other is sent again (its asserts make a retry of a committed group fail); + * a group that fails an assert is re-read and re-planned up to `maxReplans` + * times, then given up. `tooLarge` splits the batch; `permission` ends the + * run. * * A group whose rows the SyncState describes (a group row: `groups`) carries * that `state` change: every batch that applies such a group, a retry @@ -36,6 +38,11 @@ export interface Work { failed?(reason: BatchFailure, message: string): void | Promise; /** What the SyncState lists changes with these rows: stored by the batch that applies them. */ state?: StateChange; + /** + * Planned from the rows as they are (a download), so `replan` gives the same group or its echo: after a failed + * batch it is planned again instead of sent again (its asserts need not tell a committed group). + */ + fresh?: boolean; } export interface BatchWriterOptions { @@ -210,7 +217,14 @@ export class BatchWriter { await this.retryAlone(works[0], result.reason, result.message); return; } - for (const work of works) await this.applyAlone(work, 0); + for (const work of works) { + if (work.fresh && work.replan) { + const next = await work.replan(); + if (next) await this.applyAlone(next, 1); + } else { + await this.applyAlone(work, 0); + } + } if (tail) await this.applyTail(tail); } diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index ee169339..6b4c4c2c 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -642,6 +642,7 @@ export abstract class ItemSync { return { group, state: this.listing(kind, acct, object.id, true), + fresh: true, applied: () => { if (plan.effect === 'insert') this.env.report.stats.downloaded.created++; else this.env.report.stats.downloaded.updated++; From ecbd218c346ef58bd2b96fd2ce21dcd283d7d138 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:47:17 +0200 Subject: [PATCH 061/334] fix: sync only the primary account's collections by default #34 The settings chooser and the reminder filter treat only the capability's primary account as the user's own, while the engine also counted any account the server marks as personal. On a server that lists a second such account, its address books and calendars synced although the chooser showed them off. The engine now uses the same rule as the app. --- src/device-sync/__tests__/engine/internals.test.ts | 13 +++++++++++++ src/device-sync/jmap/session.ts | 8 ++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/device-sync/__tests__/engine/internals.test.ts b/src/device-sync/__tests__/engine/internals.test.ts index 4fee104b..07ec9348 100644 --- a/src/device-sync/__tests__/engine/internals.test.ts +++ b/src/device-sync/__tests__/engine/internals.test.ts @@ -297,6 +297,19 @@ describe('JMAP side', () => { expect(accountsWithCapability(session, JMAP_CALENDARS).map((a) => a.id)).toEqual(['a']); }); + it("counts only the capability's primary account as personal, like the app's chooser", () => { + const server = new FakeJmapServer(); + server.addAccount('a', { name: 'alice@example.com' }); + // Another account the server calls personal (a second mailbox of the same login, say). + server.addAccount('home', { name: 'alice-home@example.com', isPersonal: true }); + const session = server.port().session(); + + expect(accountsWithCapability(session, JMAP_CONTACTS).map((a) => [a.id, a.primary, a.personal])).toEqual([ + ['a', true, true], + ['home', false, false], + ]); + }); + it('takes the login as the owner address and survives a server without identities', async () => { const server = new FakeJmapServer(); server.addAccount('a', { name: 'Alice@Example.com' }); diff --git a/src/device-sync/jmap/session.ts b/src/device-sync/jmap/session.ts index caf93550..b6d6743b 100644 --- a/src/device-sync/jmap/session.ts +++ b/src/device-sync/jmap/session.ts @@ -12,7 +12,11 @@ export interface CapabilityAccount { name: string; /** The capability's primary account (the user's own). */ primary: boolean; - /** Collections of personal accounts are selected by default, shared ones are not. */ + /** + * Collections of the personal account are selected by default, those of the others are not. It is the + * capability's primary account only, whatever `isPersonal` says: one rule with the app's chooser and + * reminder filter, which know only the primary account per authority. + */ personal: boolean; readOnly: boolean; } @@ -31,7 +35,7 @@ export function accountsWithCapability(session: JmapSessionView, capability: str id, name: account.name || id, primary: isPrimary, - personal: isPrimary || account.isPersonal, + personal: isPrimary, readOnly: !!account.isReadOnly, }); }; From 2e1cb5854806322e142d2402e6bfd4278aed5769 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:47:55 +0200 Subject: [PATCH 062/334] fix: finish loading or removing a book after an interrupted sync #34 The SyncState's `selected` said which collections were on the device, and changed only when a load or a removal had finished. A book turned off and on again while the device was still removing it looked loaded, so the contacts removed so far never came back; a calendar came back empty. A book turned on and off again while it was still loading never looked loaded, so the contacts loaded so far were never removed. `selected` now names only collections whose rows are all on the device: a collection leaves it before its first row is removed, and the contacts SyncState lists collections that are loaded or removed only in part, so the next run loads them again when they are selected, or removes the rest when they are not. A book that still holds a change waiting for its upload stays in that list instead of `selected`. --- .../__tests__/engine/calendar-flow.test.ts | 9 +- .../__tests__/engine/selection-state.test.ts | 143 ++++++++++++++++++ src/device-sync/engine/calendar-sync.ts | 22 ++- src/device-sync/engine/contacts-sync.ts | 19 ++- src/device-sync/engine/item-sync.ts | 15 +- src/device-sync/engine/sync-state.ts | 8 +- 6 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 src/device-sync/__tests__/engine/selection-state.test.ts diff --git a/src/device-sync/__tests__/engine/calendar-flow.test.ts b/src/device-sync/__tests__/engine/calendar-flow.test.ts index 9025737e..8f391630 100644 --- a/src/device-sync/__tests__/engine/calendar-flow.test.ts +++ b/src/device-sync/__tests__/engine/calendar-flow.test.ts @@ -125,7 +125,14 @@ describe('device sync engine: calendar', () => { expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `a/${review}`, type: 'tooLarge' })]); expect(calendarRow(h, work)).toBeDefined(); expect(titles(h)).toEqual(['Code review']); - expect(h.state(CALENDAR_AUTHORITY)?.accounts.a.selected).toEqual([`a/${h.calendar}`, `a/${work}`]); + // Not complete any more (selected again, it is loaded again); its row is dropped again next run. + expect(h.state(CALENDAR_AUTHORITY)?.accounts.a.selected).toEqual([`a/${h.calendar}`]); + + h.clock.now += 2 * 3_600_000; + await h.run(CALENDAR_AUTHORITY); + + expect(h.server.get('CalendarEvent', 'a', review)).toMatchObject({ title: 'Code review' }); + expect(calendarRow(h, work)).toBeUndefined(); }); it('marks a calendar that mirrors a feed subscription read-only', async () => { diff --git a/src/device-sync/__tests__/engine/selection-state.test.ts b/src/device-sync/__tests__/engine/selection-state.test.ts new file mode 100644 index 00000000..01c683ed --- /dev/null +++ b/src/device-sync/__tests__/engine/selection-state.test.ts @@ -0,0 +1,143 @@ +// SyncState `selected` names the collections whose rows are all on the +// device: a newly selected collection is loaded, a deselected one dropped. +// A load or a drop that stopped half-way must neither hide the collection +// from the next load nor keep its rows from the next drop. + +import { describe, expect, it } from 'vitest'; +import { Events } from '../../android-columns'; +import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { addServerCards, CALENDAR_AUTHORITY, createHarness, RUN_BUDGET, type Harness } from './harness'; + +function real(): Harness { + const h = createHarness(); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + return h; +} + +/** Runs out of time right after checkpoint `n` of the next run. */ +function outOfTimeAt(h: Harness, n: number): void { + h.checkpoints.count = 0; + h.checkpoints.onCheckpoint = (count) => { + if (count === n) h.clock.now += RUN_BUDGET; + }; +} + +describe('device sync engine: collections loaded or dropped in part', () => { + it('brings every card back when a book whose drop stopped half-way is selected again', async () => { + const h = real(); + const work = h.server.addAddressBook('a', { name: 'Work' }); + addServerCards(h, ['Ada']); + addServerCards(h, Array.from({ length: 120 }, (_, i) => `W${String(i).padStart(3, '0')}`), 'a', work); + await h.run(); + expect(h.contacts()).toHaveLength(121); + h.prefs.contactsSelection[`a/${work}`] = false; + // 'changes', 'deselect' (sync), 'deselect' (chunk 1): out of time after the first chunk went. + outOfTimeAt(h, 3); + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + const left = h.contacts().length; + expect(left).toBeGreaterThan(1); + expect(left).toBeLessThan(121); + + h.prefs.contactsSelection[`a/${work}`] = true; + expect((await h.run()).outcome).toBe('ok'); + + expect(h.contacts()).toHaveLength(121); + expect(h.state()?.accounts.a.selected.sort()).toEqual([`a/${h.book}`, `a/${work}`].sort()); + }); + + it('finishes the drop of a book in the next run when it stopped half-way', async () => { + const h = real(); + const work = h.server.addAddressBook('a', { name: 'Work' }); + addServerCards(h, ['Ada']); + addServerCards(h, Array.from({ length: 120 }, (_, i) => `W${String(i).padStart(3, '0')}`), 'a', work); + await h.run(); + h.prefs.contactsSelection[`a/${work}`] = false; + outOfTimeAt(h, 3); + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + + expect((await h.run()).outcome).toBe('ok'); + + expect(h.contacts().map((c) => c.name)).toEqual(['Ada']); + expect(h.state()?.accounts.a.selected).toEqual([`a/${h.book}`]); + }); + + it('keeps a deselected book partial while a dirty card of it waits, and drops it once that is uploaded', async () => { + const h = real(); + const work = h.server.addAddressBook('a', { name: 'Work' }); + addServerCards(h, ['Ada']); + const [boss] = addServerCards(h, ['Boss', 'Colleague'], 'a', work); + await h.run(); + const bossRow = h.contactNamed('Boss')!.id; + const name = h.nameDataRow(bossRow)!; + h.device.user.updateData(Number(name._id), { data1: 'The Boss' }); + h.server.setErrorFor('ContactCard', 'a', boss, { type: 'tooLarge' }); + h.prefs.contactsSelection[`a/${work}`] = false; + + await h.run(); + + expect(h.contacts().map((c) => c.name).sort()).toEqual(['Ada', 'The Boss']); + expect(h.state()?.accounts.a).toMatchObject({ selected: [`a/${h.book}`], partial: [`a/${work}`] }); + + h.clock.now += 2 * 3_600_000; + await h.run(); + + expect(h.server.get('ContactCard', 'a', boss)).toMatchObject({ name: { full: 'The Boss' } }); + expect(h.contacts().map((c) => c.name)).toEqual(['Ada']); + expect(h.state()?.accounts.a).toMatchObject({ selected: [`a/${h.book}`], partial: [] }); + }); + + it('drops the cards of a book deselected again while its load had stopped half-way', async () => { + const h = real(); + const work = h.server.addAddressBook('a', { name: 'Work' }); + h.prefs.contactsSelection[`a/${work}`] = false; + addServerCards(h, ['Ada']); + addServerCards(h, Array.from({ length: 120 }, (_, i) => `W${String(i).padStart(3, '0')}`), 'a', work); + await h.run(); + expect(h.contacts()).toHaveLength(1); + h.prefs.contactsSelection[`a/${work}`] = true; + // 'changes', 'select', 'download' (chunk 1): out of time after the first chunk came. + outOfTimeAt(h, 3); + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + expect(h.contacts().length).toBeGreaterThan(1); + + h.prefs.contactsSelection[`a/${work}`] = false; + expect((await h.run()).outcome).toBe('ok'); + + expect(h.contacts().map((c) => c.name)).toEqual(['Ada']); + }); + + it('loads the events of a calendar dropped by a stopped run when it is selected again', async () => { + const h = real(); + const holidays = h.server.addCalendar('a', { name: 'Holidays' }); + const sports = h.server.addCalendar('a', { name: 'Sports' }); + for (const [calendar, title] of [[holidays, 'Easter'], [holidays, 'Christmas'], [sports, 'Match']] as const) { + h.server.addEvent('a', { uid: `u-${title}`, title, start: '2026-10-01T09:00:00', duration: 'PT1H', timeZone: 'Europe/Berlin', calendarIds: { [calendar]: true } }); + } + await h.run(CALENDAR_AUTHORITY); + expect(h.events()).toHaveLength(3); + // Both deselected; the run is cancelled once the first calendar's rows are gone. + h.prefs.calendarSelection[`a/${holidays}`] = false; + h.prefs.calendarSelection[`a/${sports}`] = false; + const original = h.deps.isCancelled; + let cancelled = false; + h.deps.isCancelled = async (runId) => { + const left = h.events().length; + if (!cancelled && left > 0 && left < 3) cancelled = true; + return cancelled || original(runId); + }; + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('cancelled'); + h.deps.isCancelled = original; + const titles = () => h.events().map((e) => String(e[Events.TITLE])).sort(); + const dropped = titles().includes('Match') ? holidays : sports; + const expected = dropped === holidays ? ['Christmas', 'Easter'] : ['Match']; + + h.prefs.calendarSelection[`a/${dropped}`] = true; + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + + expect(titles()).toEqual(expected); + }); +}); diff --git a/src/device-sync/engine/calendar-sync.ts b/src/device-sync/engine/calendar-sync.ts index fab6327c..1cb3d787 100644 --- a/src/device-sync/engine/calendar-sync.ts +++ b/src/device-sync/engine/calendar-sync.ts @@ -546,7 +546,10 @@ export class CalendarSync extends ItemSync { /** * Deselected calendars lose their rows (the provider deletes their events) * once nothing in them waits for an upload; until then only their clean - * events go. Events that left every synced calendar go when clean. + * events go, and the calendar row is dropped again next run. A calendar + * leaves `selected` before its first row goes: stopped half-way and + * selected again, it is loaded again. Events that left every synced + * calendar go when clean. */ protected async dropOutsideSelection(acct: string): Promise { const synced = new Set(this.syncedCollections(acct).map((id) => collectionKey(acct, id))); @@ -554,12 +557,15 @@ export class CalendarSync extends ItemSync { const rows = this.deleteRows.filter((row) => accountOfRef(this.keyByRow.get(row) ?? null) === acct); const outside = [...(this.outside.get(acct) ?? [])]; if (!deselected.length && !rows.length && !outside.length) return; - const kept = new Set(); + if (deselected.length) { + await this.env.writer.write([], this.tail([acct], (next) => { + const a = accountOf(next, acct); + a.selected = a.selected.filter((key) => !deselected.includes(key)); + })); + } for (const rowId of rows) { await this.env.checkpoints.check('deselect'); - const key = this.keyByRow.get(rowId) as string; - if (await this.dropCalendarRow(rowId)) continue; - kept.add(key); + await this.dropCalendarRow(rowId); } const works: Work[] = []; for (const local of (await this.eventKind.loadByRefs(outside)).values()) { @@ -569,9 +575,11 @@ export class CalendarSync extends ItemSync { const work = this.removeWork(acct, id, { kind: this.eventKind, local }, 'outside'); if (work) works.push(work); } - const selected = [...synced, ...kept]; + // Every synced calendar was loaded before this pass; the others go with their calendar rows. await this.env.writer.write(works, this.tail([acct], (next) => { - accountOf(next, acct).selected = selected; + const a = accountOf(next, acct); + a.selected = [...synced]; + if (a.partial) a.partial = []; })); this.outside.delete(acct); } diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 228443e8..0567e7bc 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -761,12 +761,15 @@ export class ContactsSync extends ItemSync { /** * After the upload: rows of contacts in no synced address book go, when * clean (a deselected book, or a card moved away on the server). Dirty - * ones stay until their upload succeeds, and keep their book selected. + * ones stay until their upload succeeds, and keep their book `partial`, + * so the next run drops it again. A book is `partial` from before its + * first dropped row until the last: a stop half-way leaves it to load + * again when it is selected again, or to drop again when it is not. */ protected async dropOutsideSelection(acct: string): Promise { const synced = new Set(this.syncedCollections(acct).map((id) => collectionKey(acct, id))); const committed = this.env.store.account(acct); - const deselected = committed.selected.filter((key) => !synced.has(key)); + const deselected = [...new Set([...committed.selected, ...(committed.partial ?? [])])].filter((key) => !synced.has(key)); const outside = [...(this.outside.get(acct) ?? [])]; if (!deselected.length && !outside.length) return; @@ -781,6 +784,11 @@ export class ContactsSync extends ItemSync { else for (const key of meta.collections) if (deselected.includes(key)) kept.add(key); }; if (deselected.length) { + await this.env.writer.write([], this.tail([acct], (next) => { + const a = accountOf(next, acct); + a.selected = a.selected.filter((key) => !deselected.includes(key)); + a.partial = [...new Set([...(a.partial ?? []), ...deselected])]; + })); const groupWorks: Work[] = []; for (const group of await this.loadGroupsWhere('sourceid LIKE ?', [`${acct}/%`])) consider({ kind: this.groupKind, local: group }, groupWorks); await this.env.writer.write(groupWorks); @@ -794,10 +802,11 @@ export class ContactsSync extends ItemSync { } await this.env.writer.write(works); } - const selected = [...synced, ...kept]; - // The book leaves `selected` with its last dropped row; one still holding a dirty item stays. + // Every synced book was loaded before this pass; a book still holding a dirty item stays partial. await this.env.writer.write([], this.tail([acct], (next) => { - accountOf(next, acct).selected = selected; + const a = accountOf(next, acct); + a.selected = [...synced]; + a.partial = [...kept]; })); } else { const works: Work[] = []; diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 6b4c4c2c..362d5686 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -467,10 +467,16 @@ export abstract class ItemSync { account.itemsState = from; account.reconcile = null; account.selected = selected; + if (account.partial) account.partial = account.partial.filter((key) => !selected.includes(key)); }); } - /** Collections selected since the rows were written: loaded once the account is up to date. */ + /** + * Collections selected since the rows were written (or loaded or dropped + * only in part): loaded once the account is up to date. A collection is + * `partial` from before its first chunk until its last one makes it + * `selected`, so deselected again after a stop, its rows are dropped. + */ protected async loadNewlySelected(acct: string): Promise { const account = this.env.store.account(acct); if (!account.itemsState || account.reconcile) return; @@ -479,9 +485,16 @@ export abstract class ItemSync { if (this.env.store.account(acct).selected.includes(key)) continue; await this.env.checkpoints.check('select'); const ids = await this.env.jmap.queryAll(this.itemType, acct, { [this.parentFilter]: collection }); + if (!(this.env.store.account(acct).partial ?? []).includes(key)) { + await this.env.writer.write([], this.tail([acct], (next) => { + const a = accountOf(next, acct); + a.partial = [...new Set([...(a.partial ?? []), key])]; + })); + } await this.processIds(acct, ids, [], (next) => { const a = accountOf(next, acct); if (!a.selected.includes(key)) a.selected.push(key); + a.partial = (a.partial ?? []).filter((k) => k !== key); }); } } diff --git a/src/device-sync/engine/sync-state.ts b/src/device-sync/engine/sync-state.ts index 2d07f9ab..99ad9089 100644 --- a/src/device-sync/engine/sync-state.ts +++ b/src/device-sync/engine/sync-state.ts @@ -25,8 +25,13 @@ export interface AccountState { collectionsState: string | null; /** ContactCard / CalendarEvent state the rows describe. */ itemsState: string | null; - /** Collection keys the rows were written for. */ + /** Collection keys whose rows are all on the device. */ selected: string[]; + /** + * Collection keys whose rows may be on the device in part: a load or a drop that has not ended. Selected, such + * a collection is loaded again; deselected, it is dropped again. + */ + partial?: string[]; /** Object ids whose rows could not be written; fetched again at the start of every run, nothing uploads for them meanwhile. */ stale: string[]; /** @@ -86,6 +91,7 @@ function parseAccount(value: unknown): AccountState { stale: strings(a.stale), reconcile: parseReconcile(a.reconcile), }; + if (a.partial !== undefined) out.partial = strings(a.partial); if (a.created !== undefined) out.created = strings(a.created); if (a.groups !== undefined) out.groups = strings(a.groups); if (a.taskOnly !== undefined) out.taskOnly = strings(a.taskOnly); From 78700401a2442c7006120c4d266115422b3c9f26 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:48:34 +0200 Subject: [PATCH 063/334] test: keep a contact's groups when an upload runs without a download #34 A sync with nothing to download, and the upload before sync is turned off, used to plan uploads with an empty group index, so the write after the upload deleted the uploaded contact's membership rows on the phone. The previous commit's refresh of the index before planning uploads fixes both; these tests cover the two paths so it stays fixed. Checked: both tests fail without that refresh. --- .../__tests__/engine/real-planners.test.ts | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index bf494cdd..48fa36d9 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -217,6 +217,42 @@ describe('device sync engine with the real planners', () => { expect(h.contacts().find((c) => c.name === 'Ada Lovelace')).toMatchObject({ dirty: false }); }); + describe('an upload that downloads nothing first', () => { + /** Ada in Friends (and Grace); Ada's name edited in place (AOSP); nothing new on the server. */ + async function adaRenamedInFriends() { + const h = real(); + const [ada, grace] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper']); + const uid = h.server.get('ContactCard', 'a', ada)!.uid as string; + h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { [uid]: true } }); + expect((await h.run()).outcome).toBe('ok'); + const row = h.contactNamed('Ada Lovelace')!.id; + h.device.user.updateData(Number(h.nameDataRow(row)!._id), { [Data.DATA1]: 'Ada King' }); + const memberships = () => h.device.rows('data').filter((d) => Number(d.raw_contact_id) === row && d.mimetype === MimeType.GROUP_MEMBERSHIP); + return { h, ada, grace, memberships }; + } + + it("keeps the uploaded contact's memberships in a sync run", async () => { + const { h, ada, memberships } = await adaRenamedInFriends(); + + expect((await h.run()).outcome).toBe('ok'); + + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ name: { full: 'Ada King' } }); + expect(memberships()).toHaveLength(1); + }); + + it("keeps the uploaded contact's memberships in a teardown's upload", async () => { + const { h, ada, grace, memberships } = await adaRenamedInFriends(); + // Grace's edit cannot go up, so the teardown keeps every row and asks. + renameDeviceContact(h, h.contactNamed('Grace Hopper')!.id, 'Grace B. Hopper'); + h.server.setErrorFor('ContactCard', 'a', grace, { type: 'tooLarge' }); + + expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 1 }); + + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ name: { full: 'Ada King' } }); + expect(memberships()).toHaveLength(1); + }); + }); + it('uploads a removal from a group in a run that downloads no contact', async () => { const h = real(); // A keyed phone row shows the editor works in place, so a missing membership row is a removal. From fe0aaa124745d9e3d5fd6f14818480fdd03ddfd3 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:49:14 +0200 Subject: [PATCH 064/334] fix: load a book selected while the first sync was cut short #34 A full reconcile that ran out of time resumes after the last object it processed, in the sorted order of every listed id. It listed the collections selected at the time it resumed and marked them all loaded at its end, so a book turned on in between lost the objects that sorted before that point, and was never loaded again. The reconcile now keeps the collections it started with; one selected since is loaded after it like any newly selected collection. --- .../__tests__/engine/selection-state.test.ts | 24 +++++++++++++++++++ src/device-sync/engine/item-sync.ts | 10 +++++--- src/device-sync/engine/sync-state.ts | 6 ++++- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/device-sync/__tests__/engine/selection-state.test.ts b/src/device-sync/__tests__/engine/selection-state.test.ts index 01c683ed..429450a3 100644 --- a/src/device-sync/__tests__/engine/selection-state.test.ts +++ b/src/device-sync/__tests__/engine/selection-state.test.ts @@ -110,6 +110,30 @@ describe('device sync engine: collections loaded or dropped in part', () => { expect(h.contacts().map((c) => c.name)).toEqual(['Ada']); }); + it('downloads every card of a book selected while a first sync was cut short', async () => { + const h = createHarness(); + const extra = h.server.addAddressBook('a', { name: 'Friends' }); + const friends = addServerCards(h, Array.from({ length: 10 }, (_, i) => `F${i}`), 'a', extra); + addServerCards(h, Array.from({ length: 120 }, (_, i) => `P${String(i).padStart(3, '0')}`)); + h.prefs.contactsSelection[`a/${extra}`] = false; + // The first sync (a full reconcile): 'reconcile', 'reconcile:ids', 'reconcile:objects': out of time after chunk 1. + outOfTimeAt(h, 3); + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + const marker = h.state()!.accounts.a.reconcile!; + expect(friends.filter((id) => id <= (marker.after as string)).length).toBeGreaterThan(0); + + // The user turns Friends on before the next sync. + h.prefs.contactsSelection[`a/${extra}`] = true; + expect((await h.run()).outcome).toBe('ok'); + + const onDevice = new Set(h.contacts().map((c) => c.sourceId)); + expect(friends.filter((id) => !onDevice.has(`a/${id}`))).toEqual([]); + expect(h.contacts()).toHaveLength(130); + expect(h.state()!.accounts.a).toMatchObject({ reconcile: null }); + expect(h.state()!.accounts.a.selected.sort()).toEqual([`a/${h.book}`, `a/${extra}`].sort()); + }); + it('loads the events of a calendar dropped by a stopped run when it is selected again', async () => { const h = real(); const holidays = h.server.addCalendar('a', { name: 'Holidays' }); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 362d5686..af3880b6 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -404,10 +404,11 @@ export abstract class ItemSync { */ protected async fullReconcile(acct: string): Promise { let marker = this.env.store.account(acct).reconcile; + const synced = this.syncedCollections(acct); if (!marker?.from) { await this.env.checkpoints.check('reconcile'); const from = await this.env.jmap.takeState(this.itemType, acct); - const started = { from, phase: 'ids' as const, position: 0, after: null }; + const started = { from, phase: 'ids' as const, position: 0, after: null, collections: synced.map((c) => collectionKey(acct, c)) }; await this.env.writer.write([], this.tail([acct], (next) => { accountOf(next, acct).reconcile = { ...started }; })); @@ -415,7 +416,10 @@ export abstract class ItemSync { } const from = marker.from as string; await this.beforeReconcile(acct); - const collections = this.syncedCollections(acct); + // A resumed reconcile lists the collections it started with (their ids up to `after` are done): + // one selected since then is loaded afterwards, from its first id. + const keys = marker.collections; + const collections = keys ? synced.filter((c) => keys.includes(collectionKey(acct, c))) : synced; const listed = new Set(); for (const collection of collections) { await this.env.checkpoints.check('reconcile:ids'); @@ -441,7 +445,7 @@ export abstract class ItemSync { await this.env.checkpoints.check('reconcile:objects'); const part = todo.slice(i, i + size); position += part.length; - const mark = { from, phase: 'objects' as const, position, after: part[part.length - 1] }; + const mark = { from, phase: 'objects' as const, position, after: part[part.length - 1], ...(keys ? { collections: keys } : {}) }; await this.processChunk(acct, part, [], (next) => { accountOf(next, acct).reconcile = { ...mark }; }); diff --git a/src/device-sync/engine/sync-state.ts b/src/device-sync/engine/sync-state.ts index 99ad9089..896d9d3d 100644 --- a/src/device-sync/engine/sync-state.ts +++ b/src/device-sync/engine/sync-state.ts @@ -18,6 +18,8 @@ export interface ReconcileMarker { position: number; /** The last id processed in that (sorted) order: a resumed reconcile continues after it. */ after: string | null; + /** Keys of the collections the reconcile lists; one selected since is loaded after it. Absent: every synced one. */ + collections?: string[]; } export interface AccountState { @@ -74,12 +76,14 @@ const stringOrNull = (value: unknown): string | null => (typeof value === 'strin function parseReconcile(value: unknown): ReconcileMarker | null { if (!value || typeof value !== 'object') return null; const r = value as Record; - return { + const marker: ReconcileMarker = { from: stringOrNull(r.from), phase: r.phase === 'objects' ? 'objects' : 'ids', position: typeof r.position === 'number' && r.position >= 0 ? r.position : 0, after: stringOrNull(r.after), }; + if (Array.isArray(r.collections)) marker.collections = strings(r.collections); + return marker; } function parseAccount(value: unknown): AccountState { From 1ee6bc459d45fb8a1f0225184af711423d3d0855 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:49:53 +0200 Subject: [PATCH 065/334] fix: move group members on the phone after an interrupted sync #34 When a group's members change on the server, the download fetches the contacts whose membership flipped in a chunk after the group's. The group's chunk already stored its new version, so when the sync stopped before the members' chunk, the next sync found no flip any more and the phone kept the old members. Those contacts are now listed with the group's rows, as objects to fetch first, until their own chunk ran. --- .../__tests__/engine/group-listing.test.ts | 29 +++++++++++++ src/device-sync/engine/contacts-sync.ts | 41 +++++++++++++------ src/device-sync/engine/item-sync.ts | 24 ++++++++++- 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/src/device-sync/__tests__/engine/group-listing.test.ts b/src/device-sync/__tests__/engine/group-listing.test.ts index 16e47c49..5dadacb4 100644 --- a/src/device-sync/__tests__/engine/group-listing.test.ts +++ b/src/device-sync/__tests__/engine/group-listing.test.ts @@ -5,6 +5,7 @@ // row it inserted but never listed hides a later hard delete. import { describe, expect, it } from 'vitest'; +import { MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; import { serializeSyncState } from '../../engine/sync-state'; @@ -175,6 +176,34 @@ describe('device sync engine: the groups a SyncState lists', () => { expect(h.server.get('ContactCard', 'a', group)).toBeUndefined(); }); + it("moves a group's membership on the device even when time ran out after the group's chunk", async () => { + const h = real(); + const [ada, grace] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper']); + const uid = (id: string) => h.server.get('ContactCard', 'a', id)!.uid as string; + const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { [uid(ada)]: true } }); + await h.run(); + const members = () => + h.device.rows('data') + .filter((d) => d.mimetype === MimeType.GROUP_MEMBERSHIP) + .map((d) => h.contacts().find((c) => c.id === Number(d.raw_contact_id))?.name) + .sort(); + expect(members()).toEqual(['Ada Lovelace']); + // Another client moves the membership from Ada to Grace: only the group card changes. + h.server.serverUpdate('ContactCard', 'a', group, { members: { [uid(grace)]: true } }); + // 'changes', 'download' (the group's chunk), then out of time before the members' chunk. + h.checkpoints.count = 0; + h.checkpoints.onCheckpoint = (n) => { + if (n === 2) h.clock.now += RUN_BUDGET; + }; + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + + expect((await h.run()).outcome).toBe('ok'); + + expect(members()).toEqual(['Grace Hopper']); + expect(h.state()?.accounts.a.stale).toEqual([]); + }); + it('never destroys a listed group card that is in no synced book or no longer a group, and forgets it', async () => { const h = real(); const archive = h.server.addAddressBook('a', { name: 'Archive' }); diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 0567e7bc..4642c817 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -123,6 +123,8 @@ export class ContactsSync extends ItemSync { private uidRefs: Map | null = null; /** Per account: raw contacts the upload phases looked at, for their memberships. */ private readonly uploadRows = new Map>(); + /** Group ref → ids of the contacts whose membership in it flips with the version being downloaded. */ + private readonly refreshes = new Map(); constructor(env: RunEnv) { super(env); @@ -409,27 +411,42 @@ export class ContactsSync extends ItemSync { }; } - /** Contacts of `acct` whose membership in one of these group cards flipped since the group's shadow. */ + /** + * Contacts of `acct` whose membership in one of these group cards flipped + * since the group's shadow; kept per group for `dependents`. + */ private async membersToRefresh(acct: string, groups: Array<{ id: string; members?: Record }>): Promise { if (!groups.length) return []; const locals = await this.groupKind.loadByRefs(groups.map((g) => refOf(acct, g.id))); - const flipped = new Set(); + const flippedBy = new Map>(); for (const group of groups) { - const before = new Set(onIds(locals.get(refOf(acct, group.id))?.shadow?.members as Record | undefined)); + const ref = refOf(acct, group.id); + const before = new Set(onIds(locals.get(ref)?.shadow?.members as Record | undefined)); const after = new Set(onIds(group.members)); - for (const uid of before) if (!after.has(uid)) flipped.add(uid); - for (const uid of after) if (!before.has(uid)) flipped.add(uid); + const flipped = new Set([...before].filter((uid) => !after.has(uid)).concat([...after].filter((uid) => !before.has(uid)))); + this.refreshes.delete(ref); + if (flipped.size) flippedBy.set(ref, flipped); } - if (!flipped.size) return []; + if (!flippedBy.size) return []; await this.ensureShadowIndex(); - const out: string[] = []; - for (const uid of flipped) { - for (const ref of this.uidRefs?.get(uid) ?? []) { - const id = idInAccount(ref, acct); - if (id) out.push(id); + const out = new Set(); + for (const [ref, flipped] of flippedBy) { + const ids: string[] = []; + for (const uid of flipped) { + for (const member of this.uidRefs?.get(uid) ?? []) { + const id = idInAccount(member, acct); + if (id) ids.push(id); + } } + this.refreshes.set(ref, ids); + for (const id of ids) out.add(id); } - return out; + return [...out]; + } + + /** The members a group card gained or lost: refreshed in this page, or first thing next run. */ + protected dependents(kind: Kind, acct: string, object: ServerObject): string[] { + return kind === this.groupKind ? this.refreshes.get(refOf(acct, object.id)) ?? [] : []; } protected async beforePlanning(acct: string, objects: ServerObject[]): Promise { diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index af3880b6..88c0bbe0 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -155,6 +155,23 @@ export abstract class ItemSync { protected listing(_kind: Kind, _acct: string, _id: string, _present: boolean): StateChange | undefined { return undefined; } + /** + * Objects whose rows a download of `object` makes outdated (contacts: the + * members a group card gained or lost). They go on `stale` in the batch + * that writes it, so a stop before their own chunk leaves them to the next + * run's first fetch. + */ + protected dependents(_kind: Kind, _acct: string, _object: ServerObject): string[] { + return []; + } + + private staleWith(acct: string, ids: string[]): StateChange | undefined { + if (!ids.length) return undefined; + return (next) => { + const account = accountOf(next, acct); + account.stale = [...new Set([...account.stale, ...ids])]; + }; + } protected onDiscard(_next: SyncState, _accounts: Set): void {} /** Whether a pending create of `target` may adopt this object (contacts: the target book holds it). */ protected adoptionTargetMatches(acct: string, _object: ServerObject, target: string): boolean { @@ -658,7 +675,7 @@ export abstract class ItemSync { const group = local ? plan.ops : prependOps(plan.ops, [insertGuard(kind, ref)]); return { group, - state: this.listing(kind, acct, object.id, true), + state: both(this.listing(kind, acct, object.id, true), this.staleWith(acct, this.dependents(kind, acct, object))), fresh: true, applied: () => { if (plan.effect === 'insert') this.env.report.stats.downloaded.created++; @@ -694,7 +711,10 @@ export abstract class ItemSync { const plan = kind.planDownload(object, null, acct); return { group: concatGroups(ref, found.kind.planLocalDelete(found.local), plan.ops), - state: both(this.listing(found.kind, acct, object.id, false), this.listing(kind, acct, object.id, true)), + state: both( + both(this.listing(found.kind, acct, object.id, false), this.listing(kind, acct, object.id, true)), + this.staleWith(acct, this.dependents(kind, acct, object)), + ), applied: () => { this.env.report.stats.downloaded.updated++; this.markResolved(acct, object.id); From b86e0a118ea6e12bfd6ed6076df8f8aa80afcd5b Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:50:31 +0200 Subject: [PATCH 066/334] fix: size provider batches by what the Binder transaction carries #34 The batch budget counted JSON characters, but a Parcel carries strings as UTF-16 plus a few hundred bytes per operation, so a full batch was about 700 KB of a 1 MB Binder buffer the whole process shares. Batches are now weighed at two bytes per character plus 300 bytes per op and kept under 500 KB of that. The state op no longer joins a batch whose last item already fills a provider yield window of 499 operations, which ContactsProvider refused. --- .../__tests__/engine/internals.test.ts | 29 ++++++++++++++++- src/device-sync/engine/batch.ts | 31 ++++++++++++++----- src/device-sync/engine/deps.ts | 4 +-- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/device-sync/__tests__/engine/internals.test.ts b/src/device-sync/__tests__/engine/internals.test.ts index 07ec9348..974f9c9b 100644 --- a/src/device-sync/__tests__/engine/internals.test.ts +++ b/src/device-sync/__tests__/engine/internals.test.ts @@ -3,7 +3,7 @@ // rebuild and how failures map to outcomes. import { describe, expect, it, vi } from 'vitest'; -import { BatchWriter, buildBatch, prependOps, type Work } from '../../engine/batch'; +import { BatchWriter, buildBatch, estimateBatchBytes, MAX_OPS_PER_YIELD, prependOps, type Work } from '../../engine/batch'; import { classifyFailure, RunAbort, StateMismatch, StopRun } from '../../engine/errors'; import { acquireLock } from '../../engine/mutex'; import { isBackedOff, nextMarker } from '../../engine/poison'; @@ -169,6 +169,33 @@ describe('provider batches', () => { expect(p.sent.slice(1)).toEqual([planned.group.ops.map((op) => ({ ...op, yieldAllowed: true })), buildBatch([other.group])]); }); + it('weighs a batch as the Parcel carries it: two bytes per character of its JSON and 300 per op', () => { + const ops: ProviderOp[] = [insert('data', { data1: 'x'.repeat(1000) }), insert('raw_contacts')]; + + expect(estimateBatchBytes(ops)).toBe(2 * JSON.stringify(ops).length + 2 * 300); + expect(estimateBatchBytes(ops)).toBeGreaterThan(2 * 1000); + }); + + it('packs batches by that weight', async () => { + const p = port(ok); + const group = (i: number): Work => ({ group: { ref: `g${i}`, ops: [insert('data', { data1: 'x'.repeat(1000) })] } }); + + await writer(p, { maxBytes: 2 * estimateBatchBytes(group(0).group.ops) }).write([group(0), group(1), group(2)]); + + expect(p.sent.map((b) => b.length)).toEqual([2, 1]); + }); + + it('sends the state op alone after a group that fills a yield window, and with a group that leaves room', async () => { + const tail = () => ({ op: () => ({ op: 'syncState', value: 's' }) as ProviderOp, applied: () => undefined }); + const group = (n: number): Work => ({ group: { ref: `g${n}`, ops: Array.from({ length: n }, () => insert('data')) } }); + const p = port(ok); + + await writer(p, { maxOps: 1000 }).write([group(MAX_OPS_PER_YIELD)], tail()); + await writer(p, { maxOps: 1000 }).write([group(MAX_OPS_PER_YIELD - 1)], tail()); + + expect(p.sent.map((b) => b.length)).toEqual([MAX_OPS_PER_YIELD, 1, MAX_OPS_PER_YIELD]); + }); + it('skips groups that only assert', async () => { const p = port(ok); await writer(p).write([{ group: { ref: 'g', ops: [{ op: 'assert', table: 'raw_contacts', id: 1, values: { dirty: 0 } }] } }]); diff --git a/src/device-sync/engine/batch.ts b/src/device-sync/engine/batch.ts index a7a454ea..334a4419 100644 --- a/src/device-sync/engine/batch.ts +++ b/src/device-sync/engine/batch.ts @@ -4,9 +4,11 @@ * * - a group is never split; `refs` are rebased to the batch and the group's * first op is its yield point, so the provider may commit between groups - * but never inside one; - * - at most `maxOps` ops and about `maxBytes` of JSON per batch; groups that - * carry photo bytes travel alone (Binder budget); + * but never inside one (a group of 499 ops or more is the planner's to + * avoid: ContactsProvider refuses it); + * - at most `maxOps` ops and about `maxBytes` per batch, as + * `estimateBatchBytes` counts them; groups that carry photo bytes travel + * alone (Binder budget); * - the SyncState op, when there is one, is the last op of the last batch * and never a yield point: the stored state can't get ahead of its rows. * @@ -47,12 +49,22 @@ export interface Work { export interface BatchWriterOptions { maxOps: number; + /** Per batch, as `estimateBatchBytes` counts them. */ maxBytes: number; maxReplans: number; } -function sizeOf(ops: readonly ProviderOp[]): number { - return JSON.stringify(ops).length; +/** Ops a provider takes up to and including one yield point (ContactsProvider throws at the 500th). */ +export const MAX_OPS_PER_YIELD = 499; + +/** + * What ops weigh in the Binder transaction of an `applyBatch`, for the batch + * budget and for planners that split big items: a Parcel carries strings as + * UTF-16 (two bytes per character of their JSON) plus about 300 bytes per op + * (URI, flags, value types, back-reference classes). + */ +export function estimateBatchBytes(ops: readonly ProviderOp[]): number { + return 2 * JSON.stringify(ops).length + 300 * ops.length; } function carriesBlob(ops: readonly ProviderOp[]): boolean { @@ -131,8 +143,11 @@ export class BatchWriter { for (let i = 0; i < batches.length; i++) { const last = i === batches.length - 1; const batch = batches[i]; - // A photo batch is already at the Binder budget: its state op goes alone. - const tailHere = last && tail && !(batch.length === 1 && carriesBlob(batch[0].group.ops)) ? tail : undefined; + // A photo batch is already at the Binder budget, and a group that fills a yield window leaves no room: + // the state op goes alone. + const photo = batch.length === 1 && carriesBlob(batch[0].group.ops); + const full = batch[batch.length - 1].group.ops.length >= MAX_OPS_PER_YIELD; + const tailHere = last && tail && !photo && !full ? tail : undefined; await this.applyBatch(batch, tailHere); if (last && tail && !tailHere) await this.applyTail(tail); } @@ -156,7 +171,7 @@ export class BatchWriter { continue; } const n = work.group.ops.length; - const b = sizeOf(work.group.ops); + const b = estimateBatchBytes(work.group.ops); if (current.length && (ops + n > this.options.maxOps || bytes + b > this.options.maxBytes)) flush(); current.push(work); ops += n; diff --git a/src/device-sync/engine/deps.ts b/src/device-sync/engine/deps.ts index f7bbf4a2..8f5bb227 100644 --- a/src/device-sync/engine/deps.ts +++ b/src/device-sync/engine/deps.ts @@ -58,7 +58,7 @@ export interface Tuning { chunkSize: number; /** Provider ops per applyBatch, with a yield point at every item group. */ maxBatchOps: number; - /** Serialized bytes per applyBatch (the Binder budget is 1 MB for the whole transaction). */ + /** Bytes per applyBatch as `estimateBatchBytes` counts them (the Binder budget is 1 MB for the whole transaction). */ maxBatchBytes: number; /** Re-reads and re-plans of an item whose group failed an assert. */ maxReplans: number; @@ -94,7 +94,7 @@ export interface Tuning { export const DEFAULT_TUNING: Tuning = { chunkSize: 50, maxBatchOps: 400, - maxBatchBytes: 300_000, + maxBatchBytes: 500_000, maxReplans: 3, maxCreatesPerSet: 50, maxSetBytes: 1_000_000, From 153e85cb80e38a802c50a9e1c3e9420ad9557559 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:51:09 +0200 Subject: [PATCH 067/334] fix: finish turning sync off when the app goes to the background #34 Turning sync off first uploads what the device changed, then removes the synced rows. Started from the settings screen, that work yielded the JS thread with timers between its steps, and React Native pauses timers while the app is in the background and no headless task runs: leaving the app stopped it halfway, holding the account's sync lock, until the app came back. The teardown now waits without timers. --- .../__tests__/engine/calendar-flow.test.ts | 22 ++++++++++++++++++- src/device-sync/engine/teardown.ts | 4 +++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/device-sync/__tests__/engine/calendar-flow.test.ts b/src/device-sync/__tests__/engine/calendar-flow.test.ts index 8f391630..165bbbf2 100644 --- a/src/device-sync/__tests__/engine/calendar-flow.test.ts +++ b/src/device-sync/__tests__/engine/calendar-flow.test.ts @@ -1,9 +1,10 @@ // Engine runs for calendars, and the teardown the app calls when sync is // turned off (docs/device-sync.md, "Calendar mapping", "App integration"). -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { Events } from '../../android-columns'; import { parseJsonColumn } from '../../common/json'; +import { teardownAuthority } from '../../engine/teardown'; import type { PoisonMarker } from '../../planner'; import { addDeviceContact, @@ -12,6 +13,7 @@ import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, createHarness, + REGISTRY_ID, renameDeviceContact, type Harness, } from './harness'; @@ -239,6 +241,24 @@ describe('device sync engine: teardown', () => { expect(h.serverNames()).toHaveLength(80); }); + it("finishes while JS timers are paused (the UI's teardown with the app in the background)", async () => { + const h = createHarness(); + const [ada] = addServerCards(h, ['Ada']); + await h.run(); + renameDeviceContact(h, h.contactNamed('Ada')!.id, 'Ada King'); + // The app passes neither a yield nor a sleep: the engine's own must not need a timer here. + const { yieldThread: _yield, sleep: _sleep, ...deps } = h.deps; + vi.useFakeTimers(); + try { + await expect(teardownAuthority(deps, REGISTRY_ID, ANDROID_ACCOUNT, CONTACTS_AUTHORITY)).resolves.toEqual({ pending: 0 }); + } finally { + vi.useRealTimers(); + } + + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ name: { full: 'Ada King' } }); + expect(h.contacts()).toEqual([]); + }, 3_000); + it('removes the calendar rows, and their events with them', async () => { const h = createHarness(); addServerEvent(h, 'Standup'); diff --git a/src/device-sync/engine/teardown.ts b/src/device-sync/engine/teardown.ts index 507280a9..8e0bbc20 100644 --- a/src/device-sync/engine/teardown.ts +++ b/src/device-sync/engine/teardown.ts @@ -80,7 +80,9 @@ export async function teardownAuthority( if (!stored.owner || stored.owner.origin === connection.origin) { const report = new ReportBuilder('teardown', authority, started, tuning.maxItemErrors); const { sync } = await createEnv({ - deps, + // Started from the UI: JS timers stop while the app is in the background and no headless task + // runs, so the teardown's own waits (checkpoint yields, retry back-offs) must not need one. + deps: { ...deps, yieldThread: deps.yieldThread ?? (async () => undefined), sleep: deps.sleep ?? (async () => undefined) }, tuning: { ...tuning, deadlineMarginMs: 0, createBudgetMs: 0 }, mode: 'teardown', registryId, From 654675d90b99d3f73fa259e2ac246d7d7db100bb Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:51:49 +0200 Subject: [PATCH 068/334] fix: keep unsynced changes waiting when sync is turned off before the first sync ended #34 Turning sync off uploads what the device changed without downloading first; every upload carries the state the device last synced, so the server refuses it when something changed meanwhile and the engine downloads first. Before the first sync of an account had ended there was no such state, and edits and deletions went up unguarded, able to overwrite what another client had changed. They now wait, and the app asks before they are dropped; new items still go up. --- .../__tests__/engine/real-planners.test.ts | 57 +++++++++++++++++++ src/device-sync/engine/item-sync.ts | 13 +++-- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 48fa36d9..d72a6541 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -15,6 +15,7 @@ import { createHarness, renameDeviceContact, rowWrites, + RUN_BUDGET, type Harness, } from './harness'; @@ -295,6 +296,62 @@ describe('device sync engine with the real planners', () => { }); }); + describe('a deletion the teardown uploads', () => { + /** The contacts planner as fixed for data finding 4: a deleted contact's shadow follows the server. */ + function refreshingDeletedShadows(h: Harness): void { + h.deps.planners = { + ...h.deps.planners, + contacts: { + ...contactsPlanner, + planDownload: (card, local, ctx) => { + if (!local?.deleted || !local.sourceId) return contactsPlanner.planDownload(card, local, ctx); + const shadow = JSON.stringify({ ...card, '~memberOf': local.shadow?.['~memberOf'] ?? [] }); + const ops = [{ op: 'update' as const, table: 'raw_contacts' as const, id: local.rawContactId, values: { sync2: shadow } }]; + return { ops: { ref: local.sourceId, ops }, conflicts: 0, stillDirty: true, effect: 'update' as const, writes: 1 }; + }, + }, + }; + } + + it('is decided on the server version when the server changed since the last sync', async () => { + const h = real(); + const archive = h.server.addAddressBook('a', { name: 'Archive' }); + h.prefs.contactsSelection[`a/${archive}`] = false; + const [ada] = addServerCards(h, ['Ada Lovelace']); + await h.run(); + refreshingDeletedShadows(h); + h.device.user.deleteContact(h.contactNamed('Ada Lovelace')!.id); + // Meanwhile another client files Ada in Archive too. + h.server.serverUpdate('ContactCard', 'a', ada, { [`addressBookIds/${archive}`]: true }); + + expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); + + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ addressBookIds: { [archive]: true } }); + expect((h.server.get('ContactCard', 'a', ada)!.addressBookIds as object)).not.toHaveProperty(h.book); + }); + + it('waits, with the edits, while no state guards the upload (the first sync never ended)', async () => { + const h = real(); + const [ada] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper', ...Array.from({ length: 60 }, (_, i) => `P${i}`)]); + // The first sync (a full reconcile) runs out of time after its first chunk (Ada and Grace): no state yet. + h.checkpoints.onCheckpoint = (n) => { + if (n === 3) h.clock.now += RUN_BUDGET; + }; + expect((await h.run()).outcome).toBe('cancelled'); + h.checkpoints.onCheckpoint = undefined; + expect(h.state()?.accounts.a.itemsState).toBeNull(); + renameDeviceContact(h, h.contactNamed('Ada Lovelace')!.id, 'Ada King'); + h.device.user.deleteContact(h.contactNamed('Grace Hopper')!.id); + addDeviceContact(h, 'Hedy Lamarr'); + // Meanwhile another client changes Ada. + h.server.serverUpdate('ContactCard', 'a', ada, { 'name/full': 'Ada, Countess of Lovelace' }); + + expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 2 }); + + expect(h.serverNames().filter((n) => !/^P\d+$/.test(n))).toEqual(['Ada, Countess of Lovelace', 'Grace Hopper', 'Hedy Lamarr']); + }); + }); + describe('a device edit the server refuses (forbidden)', () => { it('puts a contact back in place, keeping what only the device knows', async () => { const h = real(); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 88c0bbe0..31c75eaa 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -800,14 +800,19 @@ export abstract class ItemSync { */ protected async uploadAll(): Promise { const accounts = this.accountIds(); - for (const acct of accounts) await this.withRetries(acct, () => this.pairPhase(acct)); + // A teardown downloads nothing first: `ifInState` is what keeps its uploads from overwriting a server change. + // An account without a stored state (its first sync never ended) uploads only new items; the rest waits. + const guarded = accounts.filter((acct) => this.env.mode !== 'teardown' || !!this.env.store.account(acct).itemsState); + for (const acct of guarded) await this.withRetries(acct, () => this.pairPhase(acct)); await this.claimNewItems(); const deletionsAllowed = await this.deletionPolicy(await this.loadUploadItems()); - for (const acct of accounts) await this.withRetries(acct, () => this.prePhase(acct)); + for (const acct of guarded) await this.withRetries(acct, () => this.prePhase(acct)); for (const phase of ['create', 'update', 'delete'] as const) { - for (const acct of accounts) await this.withRetries(acct, () => this.phase(acct, phase, deletionsAllowed)); + for (const acct of phase === 'create' ? accounts : guarded) { + await this.withRetries(acct, () => this.phase(acct, phase, deletionsAllowed)); + } } - for (const acct of accounts) await this.withRetries(acct, () => this.extraPhase(acct, deletionsAllowed)); + for (const acct of guarded) await this.withRetries(acct, () => this.extraPhase(acct, deletionsAllowed)); for (const acct of accounts) await this.adoptExtraCreated(acct); } From 0faee568cc6d46a606370650f39d20db688fb5c8 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 07:52:29 +0200 Subject: [PATCH 069/334] fix: let a contact deleted on the phone and moved away meanwhile survive #34 When another client moved a card out of every address book the device syncs while the device deleted the contact, the engine noted the card for dropping but never showed its new version to the planner, so the deletion was decided on the old copy and destroyed the card. A deleted item is now merged like any changed one first; the contacts planner then leaves a card that is in no synced book. When the planner cannot place the object, the deletion stays as it was. --- .../__tests__/engine/real-planners.test.ts | 50 +++++++++++++++++-- src/device-sync/engine/item-sync.ts | 31 ++++++++---- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index d72a6541..cae21f0c 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -296,7 +296,7 @@ describe('device sync engine with the real planners', () => { }); }); - describe('a deletion the teardown uploads', () => { + describe('a deletion decided on the server version', () => { /** The contacts planner as fixed for data finding 4: a deleted contact's shadow follows the server. */ function refreshingDeletedShadows(h: Harness): void { h.deps.planners = { @@ -313,7 +313,7 @@ describe('device sync engine with the real planners', () => { }; } - it('is decided on the server version when the server changed since the last sync', async () => { + it('in a teardown, when the server changed since the last sync', async () => { const h = real(); const archive = h.server.addAddressBook('a', { name: 'Archive' }); h.prefs.contactsSelection[`a/${archive}`] = false; @@ -330,7 +330,51 @@ describe('device sync engine with the real planners', () => { expect((h.server.get('ContactCard', 'a', ada)!.addressBookIds as object)).not.toHaveProperty(h.book); }); - it('waits, with the edits, while no state guards the upload (the first sync never ended)', async () => { + it('lets the planner see the server version of a contact deleted on the device and filed away meanwhile', async () => { + const h = real(); + const archive = h.server.addAddressBook('a', { name: 'Archive' }); + h.prefs.contactsSelection[`a/${archive}`] = false; + const [ada] = addServerCards(h, ['Ada Lovelace']); + await h.run(); + refreshingDeletedShadows(h); + const planned: string[][] = []; + const contacts = h.deps.planners.contacts; + h.deps.planners = { + ...h.deps.planners, + contacts: { + ...contacts, + planUpload: (local, ctx) => { + if (local.deleted) planned.push(Object.keys(local.shadow?.addressBookIds ?? {})); + return contacts.planUpload(local, ctx); + }, + }, + }; + h.device.user.deleteContact(h.contactNamed('Ada Lovelace')!.id); + // Another client files Ada in Archive only: she left every synced book. + h.server.serverUpdate('ContactCard', 'a', ada, { addressBookIds: { [archive]: true } }); + + expect((await h.run()).outcome).toBe('ok'); + + expect(planned).toEqual([[archive]]); + }); + + it('still uploads the deletion of an event whose calendar the planner cannot place', async () => { + const h = real(); + const archive = h.server.addCalendar('a', { name: 'Archive' }); + h.prefs.calendarSelection[`a/${archive}`] = false; + const lunch = h.server.addEvent('a', { uid: 'lunch-uid', title: 'Lunch', start: '2026-10-06T12:00:00', duration: 'PT1H', timeZone: 'Europe/Berlin', calendarIds: { [h.calendar]: true } }); + await h.run(CALENDAR_AUTHORITY); + h.device.user.deleteEvent(Number(h.events()[0]._id)); + h.server.serverUpdate('CalendarEvent', 'a', lunch, { calendarIds: { [archive]: true } }); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { deleted: 1 } } }); + expect(h.server.get('CalendarEvent', 'a', lunch)).toBeUndefined(); + expect(h.events()).toEqual([]); + }); + + it('waits in a teardown, with the edits, while no state guards the upload (the first sync never ended)', async () => { const h = real(); const [ada] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper', ...Array.from({ length: 60 }, (_, i) => `P${i}`)]); // The first sync (a full reconcile) runs out of time after its first chunk (Ada and Grace): no state yet. diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 31c75eaa..8473f1e5 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -164,14 +164,6 @@ export abstract class ItemSync { protected dependents(_kind: Kind, _acct: string, _object: ServerObject): string[] { return []; } - - private staleWith(acct: string, ids: string[]): StateChange | undefined { - if (!ids.length) return undefined; - return (next) => { - const account = accountOf(next, acct); - account.stale = [...new Set([...account.stale, ...ids])]; - }; - } protected onDiscard(_next: SyncState, _accounts: Set): void {} /** Whether a pending create of `target` may adopt this object (contacts: the target book holds it). */ protected adoptionTargetMatches(acct: string, _object: ServerObject, target: string): boolean { @@ -279,6 +271,15 @@ export abstract class ItemSync { if (this.marks(acct).has(id) || this.env.store.account(acct).stale.includes(id)) this.marks(acct).set(id, false); } + /** Puts `ids` on `stale` in the batch whose rows make them outdated (see `dependents`). */ + private staleWith(acct: string, ids: string[]): StateChange | undefined { + if (!ids.length) return undefined; + return (next) => { + const account = accountOf(next, acct); + account.stale = [...new Set([...account.stale, ...ids])]; + }; + } + protected isStale(acct: string, sourceId: string | null): boolean { const id = idInAccount(sourceId, acct); if (!id) return false; @@ -638,10 +639,18 @@ export abstract class ItemSync { if (!kind || !this.inSelection(acct, object)) { if (!found) return null; const meta = found.kind.meta(found.local); - if (found.kind === kind && meta.dirty && !meta.deleted && !meta.isNew) { - // Uploaded first, dropped afterwards: merged like any dirty item, so the upload builds on the server's version. + if (found.kind === kind && (meta.dirty || meta.deleted) && !meta.isNew) { + // Uploaded first, dropped afterwards: merged like any changed item (a deleted one's shadow follows + // too), so the upload builds on the server's version. this.outsideOf(acct).add(refOf(acct, object.id)); - return this.downloadWork(kind, acct, object, found.local, written); + if (!meta.deleted) return this.downloadWork(kind, acct, object, found.local, written); + try { + return this.downloadWork(kind, acct, object, found.local, written); + } catch (error) { + // A planner that cannot place it (no synced calendar) leaves the deletion as it was planned. + this.env.log(`deleted ${refOf(acct, object.id)} outside the selection keeps its shadow`, error); + return null; + } } return this.removeWork(acct, object.id, found, 'outside'); } From f24ff7da9ead0bc9bb20f00aac35d9f9e8e5d5a9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:40:14 +0200 Subject: [PATCH 070/334] fix: keep the other edits of an event moved to another calendar #34 Etar moves an event, or turns a series into a single event, by deleting the row and inserting a copy. The pair uploaded the move as a patch of the event, but then took the copy as it stood for the new baseline and wrote the server's version over it, so everything else saved with the move was dropped: a changed reminder came back, and an edit made before the move never reached the server. The synced occurrences, which Etar does not copy, were deleted with the old row, losing an occurrence edit that was still waiting. The copy now starts from the old row's baseline with only what the move changed, so any other difference stays dirty and uploads right after the move as an ordinary update. The old row's occurrences move to the new row with what they wait to upload. Attendee rows of the copy are the app's re-insert (Etar writes everyone as required, without a status) and are not read as edits. Checked with planner tests and runs through the engine and the fake server: a reminder saved with the move and a pending occurrence edit both reach the server, the event keeps its uid, and the echo writes nothing. --- .../__tests__/calendar/engine.test.ts | 85 ++++++++++++++++++ .../__tests__/calendar/lifecycle.test.ts | 88 +++++++++++++++++++ src/device-sync/calendar/pairs.ts | 85 +++++++++++++++--- src/device-sync/calendar/planner.ts | 2 +- src/device-sync/engine/calendar-sync.ts | 6 ++ 5 files changed, 254 insertions(+), 12 deletions(-) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index 76f4c661..c0e3dcc5 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -166,6 +166,91 @@ describe('calendar device edits through the engine', () => { expect(h.server.all('CalendarEvent', 'team')).toEqual([]); }); + describe("Etar's move (delete + insert) with other edits", () => { + /** The event columns Etar copies into the row it inserts when it moves an event. */ + const ETAR_COPY = [ + Events.TITLE, Events.DESCRIPTION, Events.EVENT_LOCATION, Events.STATUS, Events.AVAILABILITY, Events.ACCESS_LEVEL, + Events.EVENT_COLOR, Events.DTSTART, Events.DTEND, Events.DURATION, Events.EVENT_TIMEZONE, Events.ALL_DAY, Events.RRULE, Events.EXDATE, + ]; + function etarMove(h: Harness, masterId: number, calendarRowId: number, reminders: Array<{ minutes: number }>): number { + const old = h.events().find((e) => Number(e._id) === masterId)!; + const values = Object.fromEntries(ETAR_COPY.filter((c) => old[c] !== undefined).map((c) => [c, old[c]])); + h.device.user.deleteEvent(masterId); + return h.device.user.insertEvent(calendarRowId, values, { + reminders: reminders.map((r) => ({ [Reminders.MINUTES]: r.minutes, [Reminders.METHOD]: 1 })), + }); + } + const minutes = (h: Harness, eventId: number) => + h.device.rows('reminders').filter((r) => Number(r[Reminders.EVENT_ID]) === eventId).map((r) => Number(r[Reminders.MINUTES])); + + it('uploads a reminder saved with the move, and keeps the event', async () => { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + const id = h.server.addEvent('a', { + uid: 'lunch-uid', + title: 'Lunch', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + keywords: { important: true }, + alerts: { al1: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT15M' }, action: 'display' } }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const master = Number(h.events().find((e) => e[Events._SYNC_ID] === `a/${id}`)!._id); + const workRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${work}`)!._id); + + const moved = etarMove(h, master, workRow, [{ minutes: 60 }]); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, deleted: 0 } } }); + const event = h.server.get('CalendarEvent', 'a', id)!; + expect(event).toMatchObject({ uid: 'lunch-uid', calendarIds: { [work]: true }, keywords: { important: true } }); + const alerts = Object.values(event.alerts as Record).map((a) => a.trigger.offset); + expect(alerts).toEqual(['-PT1H']); + expect(h.events()).toEqual([expect.objectContaining({ _id: moved, _sync_id: `a/${id}`, [Events.DIRTY]: 0 })]); + expect(minutes(h, moved)).toEqual([60]); + + const before = h.batches.log.length; + await h.run(CALENDAR_AUTHORITY); + expect(h.batches.log.slice(before).filter((ops) => ops.some((op) => op.op !== 'syncState' && op.op !== 'assert'))).toEqual([]); + }); + + it('keeps an occurrence edit that had not reached the server when the series moves', async () => { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + const id = h.server.addEvent('a', { + uid: 'standup-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'daily', count: 5 }, + recurrenceOverrides: { '2026-09-29T09:00:00': { title: 'Planning' } }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const master = Number(h.events().find((e) => e[Events._SYNC_ID] === `a/${id}`)!._id); + const exception = Number(h.events().find((e) => e[Events.ORIGINAL_SYNC_ID] === `a/${id}`)!._id); + const workRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${work}`)!._id); + // The occurrence is renamed on the device; before it syncs, Etar moves the series (it copies only unsynced exceptions). + h.device.user.updateEvent(exception, { [Events.TITLE]: 'Sprint planning' }); + + const moved = etarMove(h, master, workRow, []); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, deleted: 0 } } }); + const event = h.server.get('CalendarEvent', 'a', id)!; + expect(event.calendarIds).toEqual({ [work]: true }); + expect((event.recurrenceOverrides as Overrides)['2026-09-29T09:00:00']).toMatchObject({ title: 'Sprint planning' }); + const rows = h.events(); + expect(rows.filter((e) => e[Events.ORIGINAL_ID] === null)).toEqual([expect.objectContaining({ _id: moved, _sync_id: `a/${id}`, [Events.DIRTY]: 0 })]); + expect(rows.filter((e) => e[Events.ORIGINAL_ID] !== null)).toEqual([ + expect.objectContaining({ [Events.ORIGINAL_ID]: moved, [Events.CALENDAR_ID]: workRow, [Events.TITLE]: 'Sprint planning', [Events.DIRTY]: 0 }), + ]); + }); + }); + it('keeps a description and location cleared on one occurrence cleared', async () => { const h = real(); const id = h.server.addEvent('a', { diff --git a/src/device-sync/__tests__/calendar/lifecycle.test.ts b/src/device-sync/__tests__/calendar/lifecycle.test.ts index 5acc036e..a3cb9182 100644 --- a/src/device-sync/__tests__/calendar/lifecycle.test.ts +++ b/src/device-sync/__tests__/calendar/lifecycle.test.ts @@ -425,6 +425,94 @@ describe('calendar planner: pairs', () => { const events = await h.events(); expect(calendarPlanner.planPairs(events.filter((e) => e.deleted), events.filter((e) => !e.syncId), h.ctx)).toEqual([]); }); + + /** Etar's move of `single()` (delete + insert into Work), with `extra` columns and children on the new row. */ + const lunchColumns = { + [Events.TITLE]: 'Lunch', [Events.DTSTART]: utc('2026-10-06T10:00:00Z'), [Events.DTEND]: utc('2026-10-06T11:00:00Z'), [Events.EVENT_TIMEZONE]: 'Europe/Berlin', [Events.STATUS]: 1, [Events.AVAILABILITY]: 0, [Events.ACCESS_LEVEL]: 0, + }; + async function pairAndApply(h: Harness) { + const events = await h.events(); + const pairs = calendarPlanner.planPairs(events.filter((e) => e.deleted), events.filter((e) => !e.syncId && !e.deleted), h.ctx); + expect(pairs).toHaveLength(1); + await h.apply(pairs[0].ops); + return pairs[0]; + } + const alert15 = { al1: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT15M', relativeTo: 'start' }, action: 'display' } } as CalendarEventWire['alerts']; + + it('keeps a reminder saved with the move for the next upload instead of taking it as the baseline', async () => { + const { h, id } = await synced(single({ alerts: alert15 })); + h.fake.user.deleteEvent(id); + const moved = h.fake.user.insertEvent(h.rowIds.get('w')!, lunchColumns, { reminders: [{ [Reminders.MINUTES]: 60, [Reminders.METHOD]: 1 }] }); + + const pair = await pairAndApply(h); + + expect(pair.actions).toEqual([{ kind: 'update', id: 'e1', patch: { 'calendarIds/b': null, 'calendarIds/w': true } }]); + const after = (await h.local('e1'))!; + expect(after).toMatchObject({ eventId: moved, dirty: true }); + const [update] = actionsOf(calendarPlanner.planUpload(after, h.ctx)); + if (update.kind !== 'update') throw new Error('not an update'); + // Only the reminder is left to send: the move went up with the pair. + expect(Object.keys(update.patch).every((k) => k.startsWith('alerts/'))).toBe(true); + const alerts = serverApply(after.shadow!, update.patch).alerts!; + expect(Object.values(alerts).map((a) => (a.trigger as { offset?: string }).offset)).toEqual(['-PT1H']); + }); + + it('uploads an edit made before the move, which the new row copied', async () => { + const { h, id } = await synced(single({ description: 'Bring salad' })); + h.fake.user.updateEvent(id, { [Events.DESCRIPTION]: 'Bring soup' }); + h.fake.user.deleteEvent(id); + h.fake.user.insertEvent(h.rowIds.get('w')!, { ...lunchColumns, [Events.DESCRIPTION]: 'Bring soup' }); + + await pairAndApply(h); + + const after = (await h.local('e1'))!; + expect(after.dirty).toBe(true); + expect(actionsOf(calendarPlanner.planUpload(after, h.ctx))).toEqual([{ kind: 'update', id: 'e1', patch: { description: 'Bring soup' } }]); + }); + + it('takes attendee rows an app re-inserted with constant details as they are, uploading nothing for them', async () => { + const participants = { + bob: { '@type': 'Participant', calendarAddress: 'mailto:bob@example.net', name: 'Bob', roles: { attendee: true, optional: true }, participationStatus: 'accepted' }, + carol: { '@type': 'Participant', calendarAddress: 'mailto:carol@example.net', name: 'Carol', roles: { attendee: true }, participationStatus: 'declined' }, + } as CalendarEventWire['participants']; + const { h, id } = await synced(single({ participants, organizerCalendarAddress: 'mailto:boss@example.net' })); + const attendees = h.fake.rows('attendees', `${Attendees.EVENT_ID} = ?`, [id]).map((a) => ({ + // Etar writes every attendee it re-inserts as a required attendee without a status. + [Attendees.ATTENDEE_EMAIL]: a[Attendees.ATTENDEE_EMAIL], [Attendees.ATTENDEE_NAME]: a[Attendees.ATTENDEE_NAME], + [Attendees.ATTENDEE_RELATIONSHIP]: 1, [Attendees.ATTENDEE_TYPE]: 1, [Attendees.ATTENDEE_STATUS]: 0, + })); + const organizer = h.row(id)[Events.ORGANIZER]; + h.fake.user.deleteEvent(id); + h.fake.user.insertEvent(h.rowIds.get('w')!, { ...lunchColumns, [Events.ORGANIZER]: organizer }, { attendees }); + + await pairAndApply(h); + + const after = (await h.local('e1'))!; + expect(after.dirty).toBe(false); + expect(calendarPlanner.planUpload({ ...after, dirty: true }, h.ctx).kind).toBe('clean'); + }); + + it('keeps the timing of a series turned single against the series, so a start moved before uploads', async () => { + const { h, id } = await synced(single({ recurrenceRule: { frequency: 'daily' } })); + // Moved an hour later in place, then turned into a single event (Etar: delete + insert). + h.fake.user.updateEvent(id, { [Events.DTSTART]: utc('2026-10-06T11:00:00Z') }); + h.fake.user.deleteEvent(id); + h.fake.user.insertEvent(h.rowIds.get('b')!, { ...lunchColumns, [Events.DTSTART]: utc('2026-10-06T11:00:00Z'), [Events.DTEND]: utc('2026-10-06T12:00:00Z') }); + + const pair = await pairAndApply(h); + + expect(pair.actions).toEqual([{ kind: 'update', id: 'e1', patch: { recurrenceRule: null } }]); + const after = (await h.local('e1'))!; + expect(after.dirty).toBe(true); + expect(actionsOf(calendarPlanner.planUpload(after, h.ctx))).toEqual([{ kind: 'update', id: 'e1', patch: { start: '2026-10-06T13:00:00' } }]); + + // Without the earlier edit, the single event matches what the server holds after the pair: nothing left. + const plain = await synced(single({ recurrenceRule: { frequency: 'daily' } })); + plain.h.fake.user.deleteEvent(plain.id); + plain.h.fake.user.insertEvent(plain.h.rowIds.get('b')!, lunchColumns); + await pairAndApply(plain.h); + expect((await plain.h.local('e1'))!.dirty).toBe(false); + }); }); describe('calendar planner: baselines, zones and reminder owners', () => { diff --git a/src/device-sync/calendar/pairs.ts b/src/device-sync/calendar/pairs.ts index 80513bd2..1788bfcb 100644 --- a/src/device-sync/calendar/pairs.ts +++ b/src/device-sync/calendar/pairs.ts @@ -9,17 +9,20 @@ * target account and a destroy in the source. */ import { Attendees, Events } from '../android-columns'; -import type { CalendarContext, LocalEvent, LocalEventRow, OpGroup } from '../planner'; +import type { CalendarContext, EventBaseline, LocalEvent, LocalEventRow, OpGroup } from '../planner'; import type { Row } from '../types'; import type { CalendarEventWire } from '../wire'; import { exceptionRef, parseObjectRef } from '../common/ids'; +import { clone } from '../common/json'; import { applyPatch, type PatchObject } from '../common/patch'; import { EXCEPTION_CELLS, MASTER_CELLS, encodeBaseline, makeBaseline } from './columns'; import { durationTextSeconds } from './duration'; import { isExcluded, overridesOf } from './exceptions'; -import { GroupBuilder, assertRow, deleteRow, updateRow } from './rows'; +import { linkedExceptionCount } from './merge'; +import { GroupBuilder, assertExceptionCount, assertRow, deleteRow, updateRow } from './rows'; import { rruleParts } from './rrule'; import { sendsSchedulingMessages } from './scheduling'; +import { MASTER_COLUMN_UNITS, deviceChangedUnit, exdateEntries, remindersDiffer, sideOfBaseline, sideOfRow } from './units'; import { put } from './upload'; import { isUnsetStatus } from './values'; @@ -101,24 +104,80 @@ export function pairPatch( return { patch, sendSchedulingMessages: scheduling }; } +const children = (row: LocalEventRow) => ({ attendees: row.attendees.map((a) => a.cells), reminders: row.reminders.map((r) => r.cells) }); + /** - * Applied once the pair's patch is accepted: the old row and its exception - * rows go, the new row takes the identity (its copied exception rows the - * overrides that still exist), and the shadow is the old one with the patch - * applied until the next download brings the server's. + * The new row's baseline: the old row's (what the server held, as last + * written) with what the patch changed, the calendar and, for a series turned + * single, its rule and its timing as a single event. So an edit saved with the + * move, or made before it, still differs from the baseline and uploads next. + * Attendee rows are the app's re-insert (Etar writes every attendee it copies + * as required, without a status), so their details are no edits: they are + * taken as they are, like the reminders of a row that copied none. */ -export function pairOps(deleted: LocalEvent, fresh: LocalEvent, patch: PatchObject): OpGroup { +function pairedBaseline(deleted: LocalEvent, fresh: LocalEvent, kind: PairKind, ctx: CalendarContext): EventBaseline { + const own = makeBaseline(fresh.cells, MASTER_CELLS, children(fresh).attendees, children(fresh).reminders); + if (!deleted.baseline) return own; + const next = clone(deleted.baseline); + next.cells[Events.CALENDAR_ID] = own.cells[Events.CALENDAR_ID]; + next.attendees = own.attendees; + if (ctx.reminderOwner !== 'device' || !fresh.reminders.length) next.reminders = own.reminders; + if (kind.unrecur) { + const start = num(deleted.baseline.cells[Events.DTSTART]); + const length = lengthMs(deleted.baseline.cells); + next.cells[Events.DTEND] = start !== null && length !== null ? start + length : own.cells[Events.DTEND]; + next.cells[Events.DURATION] = null; + next.cells[Events.RRULE] = own.cells[Events.RRULE]; + next.cells[Events.EXDATE] = own.cells[Events.EXDATE]; + } + return next; +} + +/** Whether a master holds a change against `baseline` that an upload would look at. */ +function changedSince(row: LocalEventRow, baseline: EventBaseline, ctx: CalendarContext): boolean { + const cur = sideOfRow(row); + const bl = sideOfBaseline(baseline); + if (MASTER_COLUMN_UNITS.some((unit) => deviceChangedUnit(unit, cur, bl))) return true; + const [now, before] = [exdateEntries(cur), exdateEntries(bl)]; + if (now.size !== before.size || [...now].some((e) => !before.has(e))) return true; + return ctx.reminderOwner === 'device' && remindersDiffer(cur, bl); +} + +/** + * Applied once the pair's patch is accepted: the new row takes the identity + * and the old row goes. The old row's exception rows move to the new row with + * what they wait to upload (Etar copies only the exceptions it never synced; + * an app's copy of one the old row has goes). A copied exception takes the + * identity of its override. The shadow is the old one with the patch applied + * until the server's comes back. A row that still differs from its baseline + * (see `pairedBaseline`) stays dirty and uploads next. + */ +export function pairOps(deleted: LocalEvent, fresh: LocalEvent, kind: PairKind, patch: PatchObject, ctx: CalendarContext): OpGroup { const syncId = deleted.syncId!; const shadow: CalendarEventWire = applyPatch(deleted.shadow!, patch) ?? deleted.shadow!; const group = new GroupBuilder(syncId); assertRow(group, deleted, false, false); + for (const x of deleted.exceptions) assertRow(group, x, true, x.dirty || x.deleted); + assertExceptionCount(group, deleted.eventId, linkedExceptionCount(deleted)); assertRow(group, fresh, false, true); for (const x of fresh.exceptions) assertRow(group, x, true, true); - for (const x of deleted.exceptions) deleteRow(group, x.eventId); + assertExceptionCount(group, fresh.eventId, linkedExceptionCount(fresh)); + const kept = new Set(); + for (const x of deleted.exceptions) { + // A series turned single has no occurrences left. + if (kind.unrecur || !x.recurrenceId) { + deleteRow(group, x.eventId); + continue; + } + kept.add(x.recurrenceId); + const values: Row = { [Events.ORIGINAL_ID]: fresh.eventId, [Events.CALENDAR_ID]: fresh.calendarRowId }; + if (x.baseline) values[Events.SYNC_DATA4] = encodeBaseline({ ...x.baseline, cells: { ...x.baseline.cells, [Events.CALENDAR_ID]: fresh.calendarRowId } }); + group.write({ op: 'update', table: 'events', id: x.eventId, values, expectCount: 1 }); + } deleteRow(group, deleted.eventId); - const children = (row: LocalEventRow) => ({ attendees: row.attendees.map((a) => a.cells), reminders: row.reminders.map((r) => r.cells) }); const cells: Row = {}; for (const c of MASTER_CELLS) cells[c] = fresh.cells[c] ?? null; + const baseline = pairedBaseline(deleted, fresh, kind, ctx); updateRow( group, { ...fresh, shadow: null }, @@ -126,18 +185,22 @@ export function pairOps(deleted: LocalEvent, fresh: LocalEvent, patch: PatchObje isException: false, cells, ...children(fresh), - baseline: makeBaseline(fresh.cells, MASTER_CELLS, children(fresh).attendees, children(fresh).reminders), + baseline, syncId, uid: typeof shadow.uid === 'string' ? shadow.uid : null, shadow, pending: null, - clearDirty: true, + clearDirty: !changedSince(fresh, baseline, ctx), hasParticipants: fresh.attendees.length > 0, }, ); const overrides = overridesOf(shadow); for (const x of fresh.exceptions) { const key = x.recurrenceId; + if (key && kept.has(key)) { + deleteRow(group, x.eventId); + continue; + } const values: Row = { [Events.ORIGINAL_SYNC_ID]: syncId }; if (key && overrides[key] && !isExcluded(overrides[key])) { Object.assign(values, { diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index 08a2ac15..e5051242 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -385,7 +385,7 @@ function planPairs( if (!pair) continue; const action: UploadAction = { kind: 'update', id: ref.id, patch: pair.patch }; if (pair.sendSchedulingMessages) action.sendSchedulingMessages = true; - pairs.push({ deleted: d, fresh: f, actions: [action], ops: pairOps(d, f, pair.patch) }); + pairs.push({ deleted: d, fresh: f, actions: [action], ops: pairOps(d, f, kind, pair.patch, ctx) }); used.add(f.eventId); break; } diff --git a/src/device-sync/engine/calendar-sync.ts b/src/device-sync/engine/calendar-sync.ts index 1cb3d787..a4112297 100644 --- a/src/device-sync/engine/calendar-sync.ts +++ b/src/device-sync/engine/calendar-sync.ts @@ -492,6 +492,12 @@ export class CalendarSync extends ItemSync { if (ownId) this.markStale(acct, ownId); return []; } + if (this.eventKind.meta(moved).dirty) { + // Edits saved with the move, or made before it, are still to go up: the update phase + // sends them as the moved event's, and its accepted write brings the server's version. + this.env.report.stats.uploaded.updated++; + return []; + } const held: Held = { kind: this.eventKind, local: moved }; try { const work = this.acceptWork(acct, { held, meta: this.eventKind.meta(moved), fingerprint: '', actions: [] }, server, false); From 1a348792c0c6c84ec191731d78ec613e3af0af09 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:40:57 +0200 Subject: [PATCH 071/334] fix: let the server's deletion win over an event moved on the phone #34 Etar moves an event by deleting the row and inserting a copy. When the event was deleted on the server meanwhile, the sync removed the deleted row but kept the copy, which then uploaded as a new event: the event the server had deleted came back in the target calendar, for everyone. That happened whether the download found the event gone first, which is the usual case, or the patch of the move did. A move is an edit, and the server's deletion wins over an edit: the copy that pairs with the deleted row now goes with it, and the report counts the conflict. A copy edited beyond the move stays a new event. Checked through the engine with the event destroyed before the run, while the move was sent, and with the patch answered notFound. --- .../__tests__/calendar/engine.test.ts | 59 +++++++++++++++++++ src/device-sync/engine/calendar-sync.ts | 34 +++++++++++ src/device-sync/engine/item-sync.ts | 20 +++++-- 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index c0e3dcc5..6aacfcf8 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -216,6 +216,65 @@ describe('calendar device edits through the engine', () => { expect(h.batches.log.slice(before).filter((ops) => ops.some((op) => op.op !== 'syncState' && op.op !== 'assert'))).toEqual([]); }); + describe('when the server deleted the event meanwhile', () => { + async function movedLunch() { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + const id = h.server.addEvent('a', { + uid: 'lunch-uid', + title: 'Lunch', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const master = Number(h.events().find((e) => e[Events._SYNC_ID] === `a/${id}`)!._id); + const workRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${work}`)!._id); + etarMove(h, master, workRow, [{ minutes: 30 }]); + return { h, id }; + } + const creates = (h: Harness) => + h.server.calls('CalendarEvent/set').filter(([, args]) => Object.keys((args as { create?: object }).create ?? {}).length); + + it('removes the moved event when the patch of the move finds it gone', async () => { + const { h, id } = await movedLunch(); + h.server.setErrorFor('CalendarEvent', 'a', id, { type: 'notFound' }); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', conflicts: 1, stats: { uploaded: { created: 0 } } }); + expect(creates(h)).toEqual([]); + expect(h.events()).toEqual([]); + }); + + it('removes the moved event when the download finds it destroyed', async () => { + const { h, id } = await movedLunch(); + h.server.serverDestroy('CalendarEvent', 'a', id); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], conflicts: 1, stats: { uploaded: { created: 0 } } }); + expect(h.server.all('CalendarEvent', 'a')).toEqual([]); + expect(h.events()).toEqual([]); + }); + + it('removes the moved event when it is destroyed while the move is on its way', async () => { + const { h, id } = await movedLunch(); + const stop = h.server.onBeforeRequest((request) => { + if (!request.methods.includes('CalendarEvent/set')) return; + stop(); + h.server.serverDestroy('CalendarEvent', 'a', id); + }); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0 } } }); + expect(h.server.all('CalendarEvent', 'a')).toEqual([]); + expect(h.events()).toEqual([]); + }); + }); + it('keeps an occurrence edit that had not reached the server when the series moves', async () => { const h = real(); const work = h.server.addCalendar('a', { name: 'Work' }); diff --git a/src/device-sync/engine/calendar-sync.ts b/src/device-sync/engine/calendar-sync.ts index a4112297..12dcb233 100644 --- a/src/device-sync/engine/calendar-sync.ts +++ b/src/device-sync/engine/calendar-sync.ts @@ -515,6 +515,40 @@ export class CalendarSync extends ItemSync { await this.send(acct, pending); } + /** + * A deleted row whose event the server destroyed may be half of a move the + * device made (a pair not uploaded yet): the server's deletion wins over the + * move, an edit, so the new row goes too instead of being created (a conflict). + */ + protected async alongDestroyed(acct: string, removed: Held[]): Promise { + const deleted = removed.map((h) => h.local as LocalEvent).filter((e) => e.deleted); + if (!deleted.length) return []; + const fresh = (await this.eventKind.loadNew()).filter((e) => { + const m = this.eventKind.meta(e); + return !m.deleted && !m.pending && this.accountOfItem({ kind: this.eventKind, local: e }) === acct; + }); + if (!fresh.length) return []; + let pairs: PairPlan[]; + try { + pairs = this.planner.planPairs(deleted, fresh, this.ctx(acct)); + } catch (error) { + this.plannerFailed(acct, `pairs:${acct}`, 'download', error); + return []; + } + const conflict = (work: Work): Work => ({ + ...work, + applied: async (results) => { + await work.applied?.(results); + this.env.report.conflicts++; + }, + replan: work.replan && (async () => { + const next = await work.replan!(); + return next && conflict(next); + }), + }); + return pairs.map((pair) => conflict(this.purgeWork(acct, { kind: this.eventKind, local: pair.fresh }, false))); + } + /** * A pair's rows after the server took its patch. When their assert fails * (an app changed them during the upload), both rows are read and paired diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 8473f1e5..76ad8fa6 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -165,6 +165,14 @@ export abstract class ItemSync { return []; } protected onDiscard(_next: SyncState, _accounts: Set): void {} + /** + * Local items that go with the rows of objects the server destroyed + * (calendar: the new row of a move the device made of a deleted event; the + * server's deletion wins over the move). Written with those rows. + */ + protected async alongDestroyed(_acct: string, _removed: Held[]): Promise { + return []; + } /** Whether a pending create of `target` may adopt this object (contacts: the target book holds it). */ protected adoptionTargetMatches(acct: string, _object: ServerObject, target: string): boolean { return accountOfRef(target) === acct; @@ -582,6 +590,7 @@ export abstract class ItemSync { if (work) works.push(work); else this.markResolved(acct, object.id); } + const removed: Held[] = []; for (const id of allGone) { const found = held.get(refOf(acct, id)); let work: Work | null; @@ -591,9 +600,12 @@ export abstract class ItemSync { this.plannerFailed(acct, refOf(acct, id), 'download', error, id); continue; } - if (work) works.push(work); - else this.markResolved(acct, id); + if (work && found) { + works.push(work); + removed.push(found); + } else this.markResolved(acct, id); } + if (removed.length) works.push(...(await this.alongDestroyed(acct, removed))); await this.env.writer.write(works, mutate ? this.tail([acct], mutate) : undefined); await this.readBack(written); } @@ -1320,10 +1332,10 @@ export abstract class ItemSync { return this.succeeded(acct, p, done, objects); } if (failure.action.kind === 'update') { - // Deleted on the server meanwhile: the server delete wins, the rows go. + // Deleted on the server meanwhile: the server delete wins, the rows go (a pair's new row too). const id = idInAccount(p.meta.sourceId, acct); const work = id ? this.removeWork(acct, id, p.held, 'destroyed') : null; - return work ? [work] : []; + return work ? [work, ...(await this.alongDestroyed(acct, [p.held]))] : []; } return [this.poisonWork(p, failure.error)]; case 'forbidden': From 1c75da873343224f06b670d68efc199e687d9e8a Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:41:39 +0200 Subject: [PATCH 072/334] fix: remove an event created in a read-only calendar and say so #34 An event created on the phone in a calendar the user may only read was meant to be removed, since the server takes no changes there, but the claim step ignored that answer: the event stayed on the phone, was never sent or reported, and turning sync off always warned that a change had not reached the server. The claim step now applies it: the row goes, the report lists it as read-only, and nothing is left waiting. --- .../__tests__/calendar/engine.test.ts | 38 +++++++++++++++++++ src/device-sync/engine/item-sync.ts | 5 +++ 2 files changed, 43 insertions(+) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index 6aacfcf8..fd298087 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -310,6 +310,44 @@ describe('calendar device edits through the engine', () => { }); }); + it('removes an event created in a read-only calendar and reports it', async () => { + const h = real({ accounts: 'withShared' }); + const team = h.server.addCalendar('team', { + name: 'Team events', + myRights: { mayReadFreeBusy: true, mayReadItems: true, mayWriteAll: false, mayWriteOwn: false, mayUpdatePrivate: false, mayRSVP: false, mayShare: false, mayDelete: false }, + }); + h.prefs.calendarSelection[`team/${team}`] = true; + await h.run(CALENDAR_AUTHORITY); + const teamRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `team/${team}`)!._id); + + const id = h.device.user.insertEvent(teamRow, { + [Events.TITLE]: 'Mine', + [Events.DTSTART]: Date.UTC(2026, 9, 14, 13), + [Events.DTEND]: Date.UTC(2026, 9, 14, 14), + [Events.EVENT_TIMEZONE]: 'Europe/Berlin', + }); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', stats: { uploaded: { created: 0 }, skipped: 1 } }); + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `row:${id}`, side: 'upload', type: 'readOnly' })]); + expect(h.events().find((e) => Number(e._id) === id)).toBeUndefined(); + expect(h.server.all('CalendarEvent', 'team')).toEqual([]); + // Nothing is left waiting: the next run has nothing to say about it. + expect((await h.run(CALENDAR_AUTHORITY)).itemErrors).toEqual([]); + }); + + it('leaves nothing waiting for turning sync off after an event was created in a read-only calendar', async () => { + const h = real({ accounts: 'withShared' }); + const team = h.server.addCalendar('team', { name: 'Team events', myRights: { mayReadItems: true, mayWriteAll: false, mayWriteOwn: false, mayRSVP: false } }); + h.prefs.calendarSelection[`team/${team}`] = true; + await h.run(CALENDAR_AUTHORITY); + const teamRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `team/${team}`)!._id); + h.device.user.insertEvent(teamRow, { [Events.TITLE]: 'Mine', [Events.DTSTART]: Date.UTC(2026, 9, 14, 13), [Events.DTEND]: Date.UTC(2026, 9, 14, 14), [Events.EVENT_TIMEZONE]: 'Europe/Berlin' }); + + expect(await h.teardown(CALENDAR_AUTHORITY)).toEqual({ pending: 0 }); + expect(h.server.all('CalendarEvent', 'team')).toEqual([]); + }); + it('keeps a description and location cleared on one occurrence cleared', async () => { const h = real(); const id = h.server.addEvent('a', { diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index 76ad8fa6..f4c8275f 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -912,6 +912,11 @@ export abstract class ItemSync { if (plan.kind === 'skip') { this.env.report.stats.skipped++; this.env.report.itemError({ ref, side: 'upload', type: plan.reason }); + } else if (plan.kind === 'revert') { + // It may not be created where it was made (a read-only calendar): the row goes, and the report says so. + this.env.report.stats.skipped++; + this.env.report.itemError({ ref, side: 'upload', type: 'readOnly' }); + works.push(this.planWork(acct, held, 'revert', plan.ops)); } else if (plan.kind === 'claim' || plan.kind === 'purge' || plan.kind === 'clean') { works.push(this.planWork(acct, held, plan.kind, plan.ops)); } From d1ae713b1f12cbe65e2824e5e17e532e8c8726cb Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:42:23 +0200 Subject: [PATCH 073/334] fix: report changes the phone puts back because the server can't take them #34 A change the server cannot take was put back on the phone without a word: an edit in a read-only calendar or address book, an answer where answers are not allowed, an event moved to another account's calendar, an occurrence the server keeps without its series, a contact added to a group this device can't change. The change just vanished. The planners now say why they put a change back, and the sync report lists the item with that reason (`readOnly`, `rsvpRefused`, `instanceOnly`, `crossAccountMove`, `groupNotWritable`). A contact whose only change still uploads through a group card is not listed. A refused edit was also reported as "invitations not sent", as if it had been stored without them; the note now appears only when the change was stored without its scheduling messages. --- .../__tests__/calendar/edge-cases.test.ts | 4 +-- .../__tests__/calendar/engine.test.ts | 33 ++++++++++++++++++- .../__tests__/calendar/exceptions.test.ts | 2 +- .../__tests__/calendar/lifecycle.test.ts | 2 +- .../__tests__/calendar/upload.test.ts | 6 +++- .../__tests__/contacts/groups.test.ts | 13 +++++--- .../__tests__/contacts/real-engine.test.ts | 1 + .../__tests__/contacts/upload-editors.test.ts | 4 +-- src/device-sync/calendar/planner.ts | 14 ++++---- src/device-sync/calendar/upload.ts | 7 +++- src/device-sync/contacts/groups.ts | 6 ++-- src/device-sync/contacts/upload.ts | 9 +++-- src/device-sync/engine/item-sync.ts | 13 +++++--- src/device-sync/planner.ts | 5 +-- 14 files changed, 88 insertions(+), 31 deletions(-) diff --git a/src/device-sync/__tests__/calendar/edge-cases.test.ts b/src/device-sync/__tests__/calendar/edge-cases.test.ts index b32cb6e4..2f9b1da3 100644 --- a/src/device-sync/__tests__/calendar/edge-cases.test.ts +++ b/src/device-sync/__tests__/calendar/edge-cases.test.ts @@ -15,7 +15,7 @@ describe('calendar planner: edge cases', () => { const { h, id } = await synced(single({ calendarIds: { r: true } })); h.fake.user.deleteEvent(id); const plan = calendarPlanner.planUpload((await h.local('e1'))!, h.ctx); - expect(plan).toMatchObject({ kind: 'revert', refetch: true }); + expect(plan).toMatchObject({ kind: 'revert', refetch: true, reason: 'readOnly' }); if (plan.kind === 'revert') await h.apply(plan.ops); expect(await h.events()).toEqual([]); }); @@ -24,7 +24,7 @@ describe('calendar planner: edge cases', () => { const { h, id } = await synced(single({ recurrenceId: '2026-10-06T12:00:00', recurrenceIdTimeZone: 'Europe/Berlin' })); h.fake.user.updateEvent(id, { [Events.TITLE]: 'Mine' }); const plan = calendarPlanner.planUpload((await h.local('e1'))!, h.ctx); - expect(plan.kind).toBe('revert'); + expect(plan).toMatchObject({ kind: 'revert', reason: 'instanceOnly' }); if (plan.kind === 'revert') await h.apply(plan.ops); expect(h.row(id)).toMatchObject({ [Events.TITLE]: 'Lunch', [Events.DIRTY]: 0 }); }); diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index fd298087..10418fd6 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -129,6 +129,36 @@ describe('calendar device edits through the engine', () => { expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `team/${id}`, side: 'upload', type: 'forbidden' })]); expect(h.events().filter((e) => e[Events.ORIGINAL_SYNC_ID] === `team/${id}`)).toEqual([]); expect(h.events().every((e) => Number(e[Events.DIRTY] ?? 0) === 0)).toBe(true); + // Nothing was stored at all: the report does not claim it was, without invitations. + expect(report.message ?? '').not.toContain('invitations not sent'); + }); + + it('notes that invitations were not sent only when the change was stored without them', async () => { + const h = real(); + const id = h.server.addEvent('a', { + uid: 'review-uid', + title: 'Review', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + organizerCalendarAddress: 'mailto:alice@example.com', + participants: { + me: { '@type': 'Participant', calendarAddress: 'mailto:alice@example.com', roles: { owner: true, attendee: true }, participationStatus: 'accepted' }, + bob: { '@type': 'Participant', calendarAddress: 'mailto:bob@example.com', roles: { attendee: true }, participationStatus: 'needs-action' }, + }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const row = Number(h.events().find((e) => e[Events._SYNC_ID] === `a/${id}`)!._id); + h.device.user.updateEvent(row, { [Events.TITLE]: 'Design review' }); + // The server refuses the scheduling messages once (e.g. scheduling turned off): the change goes up without them. + h.server.setErrorFor('CalendarEvent', 'a', id, { type: 'forbidden' }); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); + expect(report.message).toContain('invitations not sent'); + expect(h.server.get('CalendarEvent', 'a', id)).toMatchObject({ title: 'Design review' }); }); it('puts an event an app moved in place to a calendar of another account back into its calendar', async () => { @@ -155,8 +185,9 @@ describe('calendar device edits through the engine', () => { expect(row()).toMatchObject({ [Events.CALENDAR_ID]: calendarRow(`a/${h.calendar}`), [Events.DIRTY]: 0 }); expect(h.server.get('CalendarEvent', 'a', id)!.calendarIds).toEqual({ [h.calendar]: true }); expect(h.server.all('CalendarEvent', 'team')).toEqual([]); - // Nothing was sent; the change counts as skipped (a report entry needs the engine to report reverts). + // Nothing was sent; the change counts as skipped, and the report says why. expect(report).toMatchObject({ outcome: 'ok', stats: { uploaded: { created: 0, updated: 0, deleted: 0 }, skipped: 1 } }); + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `a/${id}`, side: 'upload', type: 'crossAccountMove' })]); // Together with another edit: that one uploads, and the row goes back all the same. h.device.user.updateEvent(Number(row()._id), { [Events.CALENDAR_ID]: calendarRow(`team/${team}`), [Events.TITLE]: 'Team lunch' }); diff --git a/src/device-sync/__tests__/calendar/exceptions.test.ts b/src/device-sync/__tests__/calendar/exceptions.test.ts index 965bbf38..2163d4c1 100644 --- a/src/device-sync/__tests__/calendar/exceptions.test.ts +++ b/src/device-sync/__tests__/calendar/exceptions.test.ts @@ -249,7 +249,7 @@ describe('calendar planner: recurrence edits made on the device', () => { expect(action.sendSchedulingMessages).toBe(true); // Anything more than the answer is still the calendar owner's to change. h.fake.user.updateEvent(added, { [Events.TITLE]: 'Mine now' }); - expect(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx).kind).toBe('revert'); + expect(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx)).toMatchObject({ kind: 'revert', reason: 'readOnly' }); }); it('turns EXDATE entries an app adds or removes into exclusions and re-inclusions', async () => { diff --git a/src/device-sync/__tests__/calendar/lifecycle.test.ts b/src/device-sync/__tests__/calendar/lifecycle.test.ts index a3cb9182..52546c45 100644 --- a/src/device-sync/__tests__/calendar/lifecycle.test.ts +++ b/src/device-sync/__tests__/calendar/lifecycle.test.ts @@ -189,7 +189,7 @@ describe('calendar planner: new events', () => { const h = await new Harness().setup(); const id = h.fake.user.insertEvent(h.rowIds.get('r')!, { [Events.TITLE]: 'x', [Events.DTSTART]: 0, [Events.DTEND]: 1000, [Events.EVENT_TIMEZONE]: 'UTC', [Events.STATUS]: 1 }); const plan = calendarPlanner.planUpload(await h.byRow(id), h.ctx); - expect(plan.kind).toBe('revert'); + expect(plan).toMatchObject({ kind: 'revert', reason: 'readOnly' }); if (plan.kind === 'revert') await h.apply(plan.ops); expect(h.row(id)).toBeUndefined(); }); diff --git a/src/device-sync/__tests__/calendar/upload.test.ts b/src/device-sync/__tests__/calendar/upload.test.ts index 5e22fcfb..edd940c6 100644 --- a/src/device-sync/__tests__/calendar/upload.test.ts +++ b/src/device-sync/__tests__/calendar/upload.test.ts @@ -285,12 +285,16 @@ describe('calendar planner: uploads of edited events', () => { const { h, id } = await synced(event); h.fake.user.updateEvent(id, { [Events.TITLE]: 'Mine now' }); const plan = calendarPlanner.planUpload((await h.local('e1'))!, h.ctx); - expect(plan.kind).toBe('revert'); + expect(plan).toMatchObject({ kind: 'revert', reason: 'readOnly' }); if (plan.kind === 'revert') await h.apply(plan.ops); expect(h.row(id)).toMatchObject({ [Events.TITLE]: 'Lunch', [Events.DIRTY]: 0 }); h.fake.user.setAttendeeStatus(id, ME, 2); expect(updateOf(calendarPlanner.planUpload((await h.local('e1'))!, h.ctx)).patch).toEqual({ 'participants/me/participationStatus': 'declined' }); + + // Without mayRSVP, the answer is put back too, and says why. + const noRsvp = { ...h.ctx, calendar: (cid: string) => (cid === 'r' ? { ...h.ctx.calendar(cid)!, myRights: { mayReadItems: true } } : h.ctx.calendar(cid)) }; + expect(calendarPlanner.planUpload((await h.local('e1'))!, noRsvp)).toMatchObject({ kind: 'revert', reason: 'rsvpRefused' }); }); it('skips a poisoned item until it changes or the back-off ends', async () => { diff --git a/src/device-sync/__tests__/contacts/groups.test.ts b/src/device-sync/__tests__/contacts/groups.test.ts index 786271b9..14323ae2 100644 --- a/src/device-sync/__tests__/contacts/groups.test.ts +++ b/src/device-sync/__tests__/contacts/groups.test.ts @@ -182,7 +182,7 @@ describe('contacts groups: membership edits', () => { const gid = await readOnlyGroup(h, { 'urn:uuid:someone': true }); const { rawId } = await h.seed(appleCard()); h.device.user.addToGroup(rawId, (await localGroup(h, gid)).groupId); - expect((await h.upload(rawId)).kind).toBe('revert'); + expect(await h.upload(rawId)).toMatchObject({ kind: 'revert', reason: 'groupNotWritable' }); expect(h.membershipGroups(rawId)).toEqual([]); expect(h.dirty(rawId)).toBe(false); expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([]); @@ -194,7 +194,7 @@ describe('contacts groups: membership edits', () => { const { rawId } = await h.seed(appleCard()); const membership = (await h.contact(rawId)).rows.find((r) => r.mimetype === MimeType.GROUP_MEMBERSHIP)!; h.device.user.deleteData(membership.id); - expect((await h.upload(rawId)).kind).toBe('revert'); + expect(await h.upload(rawId)).toMatchObject({ kind: 'revert', reason: 'groupNotWritable' }); expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); expect(h.dirty(rawId)).toBe(false); }); @@ -209,7 +209,7 @@ describe('contacts groups: membership edits', () => { const { rawId } = await h.seed(appleCard()); h.device.user.addToGroup(rawId, (await h.groups()).find((g) => g.sourceId === `t/${team}`)!.groupId); expect(h.membershipGroups(rawId)).toEqual([`t/${team}`]); - expect((await h.upload(rawId)).kind).toBe('revert'); + expect(await h.upload(rawId)).toMatchObject({ kind: 'revert', reason: 'groupNotWritable' }); expect(h.membershipGroups(rawId)).toEqual([]); expect(h.dirty(rawId)).toBe(false); }); @@ -222,7 +222,10 @@ describe('contacts groups: membership edits', () => { await h.download(gid); const { rawId } = await h.seed({ ...appleCard(), addressBookIds: { [other]: true } }); h.device.user.addToGroup(rawId, (await localGroup(h, gid)).groupId); - expect((await h.upload(rawId)).kind).toBe('revert'); + const plan = await h.upload(rawId); + expect(plan.kind).toBe('revert'); + // Nothing of its own was put back: nothing to report. + expect(plan).not.toHaveProperty('reason'); expect(h.membershipGroups(rawId)).toEqual([`${JMAP}/${gid}`]); expect(h.dirty(rawId)).toBe(true); expect(h.planner.planMembershipUploads([await h.contact(rawId)], await h.groups(), h.ctx)).toEqual([ @@ -319,7 +322,7 @@ describe('contacts groups: device edits of groups', () => { const g = await localGroup(h, gid); h.device.user.updateGroup(g.groupId, { [Groups.TITLE]: 'Mine now' }); const plan = h.planner.planGroupUpload(await localGroup(h, gid), h.ctx); - expect(plan.kind).toBe('revert'); + expect(plan).toMatchObject({ kind: 'revert', reason: 'readOnly' }); await h.applyOk(opsOf(plan)); expect(await localGroup(h, gid)).toMatchObject({ title: 'Friends', dirty: false }); }); diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index 2edfebe5..373c3aa6 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -73,6 +73,7 @@ describe('contacts through the engine', () => { const report = await h.run(); expect(report).toMatchObject({ outcome: 'ok', stats: { skipped: 1 } }); + expect(report.itemErrors).toEqual([expect.objectContaining({ side: 'upload', type: 'groupNotWritable' })]); expect(h.contactNamed('Ada Lovelace')).toMatchObject({ dirty: false }); expect(memberships(h, ada)).toEqual([]); expect(h.server.get('ContactCard', 'team', team)?.members).toBeUndefined(); diff --git a/src/device-sync/__tests__/contacts/upload-editors.test.ts b/src/device-sync/__tests__/contacts/upload-editors.test.ts index 0fcc6a4c..d4783bef 100644 --- a/src/device-sync/__tests__/contacts/upload-editors.test.ts +++ b/src/device-sync/__tests__/contacts/upload-editors.test.ts @@ -359,7 +359,7 @@ describe('contacts upload: read-only and poisoned contacts', () => { const { rawId } = await h.seed(appleCard()); h.device.user.updateData(h.rowsByKey(rawId)['emails:k1']._id as number, { data1: 'hacked@example.org' }); const plan = await uploadPlan(h, rawId); - expect(plan.kind).toBe('revert'); + expect(plan).toMatchObject({ kind: 'revert', reason: 'readOnly' }); await h.upload(rawId); expect(h.rowsByKey(rawId)['emails:k1'].data1).toBe('anna@example.com'); expect(h.dirty(rawId)).toBe(false); @@ -370,7 +370,7 @@ describe('contacts upload: read-only and poisoned contacts', () => { h.readOnlyBooks.add(h.book); const { rawId } = await h.seed(appleCard()); h.device.user.deleteContact(rawId); - expect(await uploadPlan(h, rawId)).toMatchObject({ kind: 'revert', refetch: true }); + expect(await uploadPlan(h, rawId)).toMatchObject({ kind: 'revert', refetch: true, reason: 'readOnly' }); }); it('skips a poisoned contact until it changes or the back-off ends', async () => { diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index e5051242..8348e6ef 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -261,12 +261,12 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { return { kind: 'upload', actions: [{ kind: 'destroy', id: null, uid }] }; } if (!parseObjectRef(local.syncId)) return { kind: 'purge', ops: purgeGroup(local) }; - if (ctx.isReadOnly(calendar.calendarId)) return { kind: 'revert', ops: purgeGroup(local), refetch: true }; + if (ctx.isReadOnly(calendar.calendarId)) return { kind: 'revert', ops: purgeGroup(local), refetch: true, reason: SKIP.readOnly }; return { kind: 'upload', actions: deleteActions(local, ctx) }; } if (isNew) { - if (ctx.isReadOnly(calendar.calendarId)) return { kind: 'revert', ops: purgeGroup(local) }; + if (ctx.isReadOnly(calendar.calendarId)) return { kind: 'revert', ops: purgeGroup(local), reason: SKIP.readOnly }; const uid = claimedUid(local); const target = createTarget(local, ctx); if (!target) return { kind: 'skip', reason: SKIP.notOurCalendar }; @@ -292,7 +292,7 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { // An instance Stalwart holds without its series can't be changed on its own. if (shadow.recurrenceId && !shadow.recurrenceRule) { const ops = revertGroup(local, ctx); - return ops ? { kind: 'revert', ops } : { kind: 'skip', reason: SKIP.instanceOnly }; + return ops ? { kind: 'revert', ops, reason: SKIP.instanceOnly } : { kind: 'skip', reason: SKIP.instanceOnly }; } let computation: UploadComputation; @@ -310,9 +310,11 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { }); if ((readOnly && keys.length) || movedToReadOnly) { // RSVP, to the series or to occurrences, is the one change a calendar without write rights may take. - if (!(computation.rsvpOnly && ctx.calendar(calendar.calendarId)?.myRights?.mayRSVP && !movedToReadOnly)) { + const rsvp = computation.rsvpOnly && !movedToReadOnly; + if (!(rsvp && ctx.calendar(calendar.calendarId)?.myRights?.mayRSVP)) { const ops = revertGroup(local, ctx); - return ops ? { kind: 'revert', ops } : { kind: 'skip', reason: 'readOnly' }; + const reason = rsvp ? SKIP.rsvpRefused : SKIP.readOnly; + return ops ? { kind: 'revert', ops, reason } : { kind: 'skip', reason }; } } if (!keys.length) { @@ -322,7 +324,7 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { // write puts it back. if (computation.dropped.includes('calendar')) { const ops = revertGroup(local, ctx); - if (ops) return { kind: 'revert', ops }; + if (ops) return { kind: 'revert', ops, reason: SKIP.crossAccountMove }; } return { kind: 'clean', ops: cleanGroup(local, computation) }; } diff --git a/src/device-sync/calendar/upload.ts b/src/device-sync/calendar/upload.ts index 269924f9..f39210cc 100644 --- a/src/device-sync/calendar/upload.ts +++ b/src/device-sync/calendar/upload.ts @@ -79,12 +79,17 @@ import { wallMs, } from './zoned-time'; -/** Why an item is not uploaded now. */ +/** Why an item is not uploaded now (a skip), or why its change was put back (a revert). */ export const SKIP = { dstAmbiguous: 'dstAmbiguous', ruleNotRepresentable: 'ruleNotRepresentable', instanceOnly: 'instanceOnly', notOurCalendar: 'notOurCalendar', + readOnly: 'readOnly', + /** The user's answer, in a calendar that takes no answers either. */ + rsvpRefused: 'rsvpRefused', + /** Moved in place to a calendar of another JMAP account: no patch can do that. */ + crossAccountMove: 'crossAccountMove', } as const; export class SkipUpload extends Error { diff --git a/src/device-sync/contacts/groups.ts b/src/device-sync/contacts/groups.ts index 45829807..9dde6caa 100644 --- a/src/device-sync/contacts/groups.ts +++ b/src/device-sync/contacts/groups.ts @@ -141,7 +141,7 @@ export function planGroupUpload(local: LocalGroup, ctx: Ctx): UploadPlan on).map(([id]) => id); const selected = books.filter((id) => ctx.isSelected(collectionKey(parsed.accountId, id))); if (selected.length && selected.length < books.length) { @@ -175,7 +175,9 @@ export function planGroupUpload(local: LocalGroup, ctx: Ctx): UploadPlan const purge = (): OpGroup => group(local, [deleteWhereId('raw_contacts', local.rawContactId)]); const ref = parseObjectRef(local.sourceId); if (ref) { - if (readOnlyOnServer(local, ctx)) return { kind: 'revert', ops: purge(), refetch: true }; + if (readOnlyOnServer(local, ctx)) return { kind: 'revert', ops: purge(), refetch: true, reason: 'readOnly' }; const books = Object.entries(local.shadow?.addressBookIds ?? {}).filter(([, on]) => on).map(([id]) => id); const selected = books.filter((id) => ctx.isSelected(collectionKey(ref.accountId, id))); // A card that is also in books this device does not sync only leaves the synced ones. @@ -257,7 +257,10 @@ export function planContactUpload(local: LocalContact, ctx: Ctx): UploadPlan[] = [{ kind: 'update', id: parseObjectRef(local.sourceId)!.id, patch }]; if (backedOff(local, ctx, actions)) return { kind: 'skip', reason: `poisoned:${local.poison!.type}` }; diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index f4c8275f..c5856617 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -913,9 +913,9 @@ export abstract class ItemSync { this.env.report.stats.skipped++; this.env.report.itemError({ ref, side: 'upload', type: plan.reason }); } else if (plan.kind === 'revert') { - // It may not be created where it was made (a read-only calendar): the row goes, and the report says so. + // It may not be created where it was made (a read-only calendar): the row goes, and the report says why. this.env.report.stats.skipped++; - this.env.report.itemError({ ref, side: 'upload', type: 'readOnly' }); + if (plan.reason) this.env.report.itemError({ ref, side: 'upload', type: plan.reason }); works.push(this.planWork(acct, held, 'revert', plan.ops)); } else if (plan.kind === 'claim' || plan.kind === 'purge' || plan.kind === 'clean') { works.push(this.planWork(acct, held, plan.kind, plan.ops)); @@ -1047,6 +1047,7 @@ export abstract class ItemSync { } case 'revert': report.stats.skipped++; + if (plan.reason) report.itemError({ ref, side: 'upload', type: plan.reason }); works.push(this.planWork(acct, held, 'revert', plan.ops)); if (plan.refetch) { const id = idInAccount(meta.sourceId, acct); @@ -1328,7 +1329,11 @@ export abstract class ItemSync { retry: Pending[], ): Promise { const failure = outcomes.find((o): o is Extract => !o.ok); - if (!failure) return this.succeeded(acct, p, outcomes, objects); + if (!failure) { + // Stored without the scheduling messages it asked for (see `forbidden` below). + if (p.noScheduling) this.env.report.note('invitations not sent'); + return this.succeeded(acct, p, outcomes, objects); + } switch (classifySetError(failure.error, failure.action.kind)) { case 'notFound': if (failure.action.kind === 'destroy') { @@ -1346,7 +1351,7 @@ export abstract class ItemSync { case 'forbidden': if (failure.scheduling && !p.noScheduling) { // Scheduling is off, the account has no calendar address, or no permission: store the change without it. - this.env.report.note('invitations not sent'); + // A second refusal is about rights, not invitations: the note waits for the change to be stored. retry.push({ ...p, noScheduling: true }); return []; } diff --git a/src/device-sync/planner.ts b/src/device-sync/planner.ts index 93349e8d..4b56cc5d 100644 --- a/src/device-sync/planner.ts +++ b/src/device-sync/planner.ts @@ -76,7 +76,8 @@ export type UploadAction = * - `claim`: a new item without a pending uid: apply `ops` (they write the uid), then re-read and plan again. * - `upload`: send `actions`; `local` is what they were computed from. * - `purge`: a deleted item that never reached the server: apply `ops`. - * - `revert`: the item may not be changed on the server (read-only): apply `ops` (rewrite from the shadow). + * - `revert`: the item may not be changed on the server (read-only): apply `ops` (rewrite from the shadow); + * `reason`, set when a device change is put back, goes to the report. * - `skip`: poisoned and backed off, or not representable; `reason` goes to the report. */ export type UploadPlan = @@ -84,7 +85,7 @@ export type UploadPlan = | { kind: 'claim'; ops: OpGroup } | { kind: 'upload'; actions: UploadAction[] } | { kind: 'purge'; ops: OpGroup } - | { kind: 'revert'; ops: OpGroup; refetch?: boolean } + | { kind: 'revert'; ops: OpGroup; refetch?: boolean; reason?: string } | { kind: 'skip'; reason: string }; export interface DownloadPlan { From 7453513e08a45b7384bd380923cf4c570589ce36 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:43:08 +0200 Subject: [PATCH 074/334] fix: stop waiting for group memberships in read-only address books #34 A contact added on the phone to a group in a read-only address book (one shared with the user) could never reach the server, yet it stayed waiting on every run: the planner puts such a membership back, but the engine never told it which groups are read-only, so only groups of another account were recognised. Turning sync off or signing out then always asked about a change that could never go up. The engine now tells the planner which synced groups are in read-only books only, from the group cards it holds and the books' rights. The membership is put back and reported, and nothing is left waiting. --- src/device-sync/__tests__/contacts/harness.ts | 7 +++---- .../__tests__/contacts/real-engine.test.ts | 21 +++++++++++++++++++ src/device-sync/contacts/members.ts | 16 ++++---------- src/device-sync/engine/contacts-sync.ts | 8 ++++++- src/device-sync/planner.ts | 6 ++++++ 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/device-sync/__tests__/contacts/harness.ts b/src/device-sync/__tests__/contacts/harness.ts index 27f1a243..8705ef99 100644 --- a/src/device-sync/__tests__/contacts/harness.ts +++ b/src/device-sync/__tests__/contacts/harness.ts @@ -6,7 +6,6 @@ */ import { Data, GroupMembership, Groups, RawContacts } from '../../android-columns'; import { makeKeyMinter, parseObjectRef, uuidFrom } from '../../common/ids'; -import type { GroupRights } from '../../contacts/members'; import { contactsPlanner } from '../../contacts/planner'; import type { ContactCardWire, @@ -85,7 +84,7 @@ export class Harness { groupIndex = true; /** When true the photos the planner writes come out thumbnail-only (see `keepThumbnailsOnly`). */ thumbnailPhotos = false; - readonly ctx: ContactsContext & GroupRights; + readonly ctx: ContactsContext; constructor() { this.server.addAccount(JMAP, { name: ACCOUNT }); @@ -126,10 +125,10 @@ export class Harness { } /** The context as the engine may build it without a group index. */ - context(): ContactsContext & GroupRights { + context(): ContactsContext { if (this.groupIndex) return this.ctx; const { groupsOf: _drop, ...rest } = this.ctx; - return rest as ContactsContext & GroupRights; + return rest as ContactsContext; } async apply(group: OpGroup): Promise { diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index 373c3aa6..d39e6bb4 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -81,6 +81,27 @@ describe('contacts through the engine', () => { expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); }); + it('puts back a membership in a group of a read-only book, so no change waits for ever', async () => { + const h = real(); + const shared = h.server.addAddressBook('a', { name: 'Shared', myRights: { mayRead: true, mayWrite: false, mayShare: false, mayDelete: false } }); + h.prefs.contactsSelection[`a/${shared}`] = true; + const team = h.server.addCard('a', { uid: 'g-team', kind: 'group', name: { full: 'Team' }, members: { other: true }, addressBookIds: { [shared]: true } }); + addServerCards(h, ['Ada Lovelace']); + expect((await h.run()).outcome).toBe('ok'); + + const ada = h.contactNamed('Ada Lovelace')!.id; + h.device.user.addToGroup(ada, Number(h.device.rows('groups').find((g) => g.sourceid === `a/${team}`)!._id)); + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', stats: { skipped: 1 } }); + expect(report.itemErrors).toEqual([expect.objectContaining({ side: 'upload', type: 'groupNotWritable' })]); + expect(h.contactNamed('Ada Lovelace')).toMatchObject({ dirty: false }); + expect(memberships(h, ada)).toEqual([]); + expect(h.server.get('ContactCard', 'a', team)?.members).toEqual({ other: true }); + // Turning sync off finds nothing waiting. + expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); + }); + it('puts a contact created on the device into the group it was added to', async () => { const h = real(); const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { other: true }, addressBookIds: { [h.book]: true } }); diff --git a/src/device-sync/contacts/members.ts b/src/device-sync/contacts/members.ts index 3a331d1a..e5e9218b 100644 --- a/src/device-sync/contacts/members.ts +++ b/src/device-sync/contacts/members.ts @@ -27,21 +27,13 @@ import { deleteData, insertUnit, updateData } from './ops'; import { NO_PHOTO } from './photo'; import { MEMBER_OF, type Unit } from './project'; -/** - * Group rights the engine can add to the context (not part of - * ContactsContext yet): whether a group card, by SOURCE_ID, is in read-only - * address books only. Without it every synced group counts as writable. - */ -export interface GroupRights { - groupReadOnly?(sourceId: string): boolean; -} - /** * The groups whose members an upload for a contact of `account` can change: - * that account's groups outside read-only books. A membership change of any - * other group can never reach the server (`planMembershipUploads` skips it). + * that account's groups outside read-only books (`groupReadOnly`). A + * membership change of any other group can never reach the server + * (`planMembershipUploads` skips it). */ -export function editableGroups(ctx: ContactsContext & GroupRights, account: string): (sourceId: string) => boolean { +export function editableGroups(ctx: ContactsContext, account: string): (sourceId: string) => boolean { return (sourceId) => parseObjectRef(sourceId)?.accountId === account && !(ctx.groupReadOnly?.(sourceId) ?? false); } diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 4642c817..2e3469ba 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -112,6 +112,8 @@ export class ContactsSync extends ItemSync { /** Group SOURCE_ID → Groups row id, and member uid → group SOURCE_IDs; rebuilt after group writes. */ private readonly groupRows = new Map(); private readonly membership = new Map>(); + /** Group SOURCE_IDs whose cards are in read-only address books only: the device may not change their members. */ + private readonly readOnlyGroups = new Set(); private groupsStale = true; /** Per account: group refs that appeared (true) or went (false), for `SyncState.groups`. */ private readonly groupChanges = new Map>(); @@ -255,13 +257,16 @@ export class ContactsSync extends ItemSync { return rows.map((r) => this.planner.decodeGroup(r)); } - /** Group rows by SOURCE_ID and who is a member of which group, from the group cards' shadows. */ + /** Group rows by SOURCE_ID, who is a member of which group, and which groups are read-only, from the group cards' shadows. */ private async refreshGroups(): Promise { this.groupRows.clear(); this.membership.clear(); + this.readOnlyGroups.clear(); for (const group of await this.loadGroupsWhere()) { if (!group.sourceId || group.deleted) continue; this.groupRows.set(group.sourceId, group.groupId); + const keys = this.cardKeys(accountOfRef(group.sourceId), group.shadow); + if (keys.length > 0 && keys.every((k) => this.book(k)?.myRights?.mayWrite === false)) this.readOnlyGroups.add(group.sourceId); for (const uid of onIds(group.shadow?.members as Record | undefined)) { let set = this.membership.get(uid); if (!set) this.membership.set(uid, (set = new Set())); @@ -379,6 +384,7 @@ export class ContactsSync extends ItemSync { nameForUid: (uid) => this.uidNames?.get(uid) ?? null, groupRowIdBySourceId: (sourceId) => this.groupRows.get(sourceId) ?? null, groupsOf: (uid) => [...(this.membership.get(uid) ?? [])], + groupReadOnly: (sourceId) => this.readOnlyGroups.has(sourceId), }; } diff --git a/src/device-sync/planner.ts b/src/device-sync/planner.ts index 4b56cc5d..97de9ef4 100644 --- a/src/device-sync/planner.ts +++ b/src/device-sync/planner.ts @@ -189,6 +189,12 @@ export interface ContactsContext extends MapContext { * it; without it the planner leaves memberships as they are. */ groupsOf?(uid: string): string[]; + /** + * Whether a synced group card (by SOURCE_ID) is in read-only address books + * only, so the device may not change its members. The engine provides it + * from the group rows' shadows; without it every group counts as writable. + */ + groupReadOnly?(sourceId: string): boolean; } export interface ContactsPlanner { From 84f6b5777e4793e9d4250bffc661180ff8a132b9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:50:15 +0200 Subject: [PATCH 075/334] feat: translate device sync into all 26 languages #34 The strings of device sync were English only: the "Sync to this device" sections in the Contacts and Calendar settings with their dialogs, and the notifications about signing in again and about sync problems. Each language keeps the form of address its file already uses, the app's own terms, and Android's names for its settings, auto-sync and the Contacts and Calendar apps. Checked: every message parses as ICU with the same arguments as the English one and the plural categories of its language, `node scripts/rn-missing-translations.mjs ` lists no device sync key for any language, `npm run i18n:check` and the i18n tests pass. --- locales/rn/ar.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/ca.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/cs.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/da.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/de.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/es.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/fa.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/fr.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/he.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/hu.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/it.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/ja.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/ko.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/lv.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/mn.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/nb.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/nl.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/pl.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/pt.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/ro.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/ru.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/sk.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/tr.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/uk.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/zh-TW.json | 113 ++++++++++++++++++++++++++++++++++++++++++ locales/rn/zh.json | 113 ++++++++++++++++++++++++++++++++++++++++++ 26 files changed, 2938 insertions(+) diff --git a/locales/rn/ar.json b/locales/rn/ar.json index 502ef925..cedb796f 100644 --- a/locales/rn/ar.json +++ b/locales/rn/ar.json @@ -93,6 +93,8 @@ "until_date": "حتى {date}" }, "settings": { + "device_sync_desc": "اعرض تقويماتك في تطبيق التقويم والتطبيقات الأخرى على هذا الجهاز. تتم مزامنة التغييرات في الاتجاهين.", + "device_sync_title": "المزامنة مع هذا الجهاز", "show_week_numbers_mobile_desc": "عرض أرقام الأسابيع في عرض الشهر.", "time_zone": "المنطقة الزمنية", "time_zone_auto": "المنطقة الزمنية للجهاز", @@ -249,6 +251,18 @@ "color_tag": "وسم", "remove_color": "إزالة الوسم" }, + "device_sync": { + "auth_problem_text": "لم يعد الخادم يقبل تسجيل الدخول إلى {account}، لذلك توقفت مزامنة جهات الاتصال والتقويمات الخاصة به على هذا الجهاز. سجّل الدخول مرة أخرى لاستئناف المزامنة. يتم الاحتفاظ بالتغييرات التي أُجريت على هذا الجهاز.", + "auth_problem_title": "سجّل الدخول مرة أخرى لمواصلة المزامنة", + "problem_channel": "مشكلات المزامنة", + "sign_out_anyway": "تسجيل الخروج", + "sign_out_pending_message": "{count, plural, zero {# تغيير أُجري على هذا الجهاز لم يصل إلى الخادم. سيُفقد إذا سجّلت الخروج الآن.} one {تغيير واحد أُجري على هذا الجهاز لم يصل إلى الخادم. سيُفقد إذا سجّلت الخروج الآن.} two {تغييران أُجريا على هذا الجهاز لم يصلا إلى الخادم. سيُفقدان إذا سجّلت الخروج الآن.} few {# تغييرات أُجريت على هذا الجهاز لم تصل إلى الخادم. ستُفقد إذا سجّلت الخروج الآن.} many {# تغييرًا أُجريت على هذا الجهاز لم تصل إلى الخادم. ستُفقد إذا سجّلت الخروج الآن.} other {# تغيير أُجريت على هذا الجهاز لم تصل إلى الخادم. ستُفقد إذا سجّلت الخروج الآن.}}", + "sign_out_pending_title": "تسجيل الخروج على أي حال؟", + "sign_out_unknown_message": "ربما لم تصل التغييرات التي أُجريت على هذا الجهاز إلى الخادم. ستُفقد إذا سجّلت الخروج الآن.", + "signed_out_text": "تم تسجيل الخروج من {account}، لذلك توقفت مزامنة جهات الاتصال والتقويمات الخاصة به على هذا الجهاز. سجّل الدخول مرة أخرى لاستئناف المزامنة. يتم الاحتفاظ بالتغييرات التي أُجريت على هذا الجهاز.", + "signed_out_title": "سجّل الدخول لمواصلة المزامنة", + "signing_out": "جارٍ إزالة جهات الاتصال والتقويمات المتزامنة من هذا الجهاز…" + }, "email_composer": { "attach_camera": "الكاميرا", "attachment_reminder_body": "تذكر رسالتك \"{keyword}\" لكن لم يتم إرفاق أي ملف. هل تريد الإرسال على أي حال؟", @@ -598,11 +612,110 @@ "book_count": "{count, plural, zero {# جهة اتصال} one {جهة اتصال واحدة} two {جهتا اتصال} few {# جهات اتصال} many {# جهة اتصال} other {# جهة اتصال}}", "default_book": "دفتر العناوين الافتراضي", "description_mobile": "تفضيلات العرض وأدوات دفاتر العناوين.", + "device_sync_desc": "اعرض دفاتر عناوينك في تطبيق جهات الاتصال والتطبيقات الأخرى على هذا الجهاز. تتم مزامنة التغييرات في الاتجاهين.", + "device_sync_title": "المزامنة مع هذا الجهاز", "export_description_count": "{count, plural, zero {تصدير # جهة اتصال في ملف vCard واحد.} one {تصدير جهة اتصال واحدة في ملف vCard واحد.} two {تصدير جهتي اتصال في ملف vCard واحد.} few {تصدير # جهات اتصال في ملف vCard واحد.} many {تصدير # جهة اتصال في ملف vCard واحد.} other {تصدير # جهة اتصال في ملف vCard واحد.}}", "export_empty": "لا توجد جهات اتصال للتصدير", "import_description_mobile": "استيراد جهات الاتصال من ملف vCard (.vcf) مع اكتشاف التكرارات.", "manage_description_mobile": "أنشئ دفاتر العناوين التي تنظّم جهات اتصالك، وأعد تسميتها، واحذفها." }, + "device_sync": { + "also_synced_by": "تتم مزامنته أيضًا عبر {accounts}، لذلك يظهر مرتين على هذا الجهاز.", + "android_settings": "إعدادات حساب Android", + "blocked_calendar": "لم يعد Android يطلب الوصول إلى التقويمات. اسمح به من إعدادات التطبيق، ثم فعّل المزامنة مرة أخرى.", + "blocked_contacts": "لم يعد Android يطلب الوصول إلى جهات الاتصال. اسمح به من إعدادات التطبيق، ثم فعّل المزامنة مرة أخرى.", + "blocked_title": "الوصول متوقف", + "books_default": "تتم مزامنة دفاتر عناوينك، أما المشتركة فلا تتم مزامنتها إلا إذا اخترتها.", + "books_hint": "تتم مزامنة دفاتر عناوينك ما لم توقفها، أما المشتركة فلا تتم مزامنتها إلا إذا فعّلتها.", + "books_label": "دفاتر العناوين", + "calendars_default": "تتم مزامنة تقويماتك، أما المشتركة فلا تتم مزامنتها إلا إذا اخترتها.", + "calendars_hint": "تتم مزامنة تقويماتك ما لم توقفها، أما المشتركة فلا تتم مزامنتها إلا إذا فعّلتها. تبقى قوائم المهام في Bulwark.", + "calendars_label": "التقويمات", + "choose": "اختيار", + "choose_books_title": "دفاتر العناوين المراد مزامنتها", + "choose_calendars_title": "التقويمات المراد مزامنتها", + "collections_count": "تتم مزامنة {selected} من {total}", + "continue": "متابعة", + "deletions_delete": "الحذف من الخادم", + "deletions_message": "حُذفت عناصر كثيرة دفعة واحدة على هذا الجهاز، لذلك لم تُحذف من الخادم. احذفها من هناك أيضًا، أو استعدها على هذا الجهاز.", + "deletions_restore": "استعادتها", + "deletions_title": "هل تريد حذفها من الخادم أيضًا؟", + "denied_calendar": "لا يستطيع Bulwark وضع تقويماتك على هذا الجهاز بدون الوصول إلى التقويمات.", + "denied_contacts": "لا يستطيع Bulwark وضع دفاتر عناوينك على هذا الجهاز بدون الوصول إلى جهات الاتصال.", + "denied_title": "تبقى المزامنة متوقفة", + "failed_title": "تعذّر تشغيل المزامنة", + "grant_access": "منح الوصول", + "interval_15m": "كل 15 دقيقة", + "interval_1h": "كل ساعة", + "interval_30m": "كل 30 دقيقة", + "interval_6h": "كل 6 ساعات", + "interval_desc": "عدد مرات المزامنة التي يجريها Android من تلقاء نفسه. تتم مزامنة التعديلات في Bulwark والتغييرات الواردة عبر الإشعارات الفورية بشكل أسرع.", + "interval_label": "فترة المزامنة", + "interval_manual": "بدون مزامنة منتظمة", + "keep_syncing": "مواصلة المزامنة", + "list_error_auth": "لم يقبل الخادم تسجيل الدخول.", + "list_error_network": "تعذّر الوصول إلى الخادم.", + "list_error_server": "تعذّر على الخادم عرض القائمة.", + "list_error_signed_out": "لم يتم تسجيل الدخول إلى هذا الحساب على هذا الجهاز.", + "master_off": "المزامنة التلقائية متوقفة على هذا الجهاز، لذلك لا تتم المزامنة إلا عند النقر على \"مزامنة الآن\".", + "new_contacts_default": "دفتر العناوين الافتراضي", + "new_contacts_desc": "دفتر العناوين لجهات الاتصال التي تُنشأ في تطبيقات أخرى على هذا الجهاز.", + "new_contacts_label": "حفظ جهات الاتصال الجديدة في", + "no_books": "لا توجد دفاتر عناوين في هذا الحساب.", + "no_calendars": "لا توجد تقويمات للمزامنة في هذا الحساب.", + "ok": "حسنًا", + "open_app_settings": "فتح إعدادات التطبيق", + "own_collections": "الخاصة بك", + "rationale_calendar": "لعرض تقويماتك في تطبيق التقويم، يحتاج Bulwark إلى الوصول إلى التقويمات على هذا الجهاز. ولا يغيّر إلا تقويمات حسابه.", + "rationale_contacts": "لعرض دفاتر عناوينك في تطبيق جهات الاتصال، يحتاج Bulwark إلى الوصول إلى جهات الاتصال على هذا الجهاز. ولا يغيّر إلا جهات الاتصال في حسابه.", + "rationale_title_calendar": "السماح بالوصول إلى التقويمات", + "rationale_title_contacts": "السماح بالوصول إلى جهات الاتصال", + "read_only": "للقراءة فقط", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "تطبيق التقويم", + "reminder_question_message": "يمكن أن تصلك تذكيرات الأحداث المتزامنة من تطبيق التقويم على هذا الجهاز أو من Bulwark. اختر أحدهما حتى لا تصلك التذكيرات مرتين. يمكنك تغيير ذلك لاحقًا.", + "reminder_question_title": "من يجب أن يذكّرك؟", + "reminders_desc": "الجهة التي تذكّرك بالأحداث المتزامنة. يبقى الآخر صامتًا، فلا تصلك التذكيرات مرتين.", + "reminders_label": "التذكيرات", + "reminders_nobody": "إشعارات الأحداث في Bulwark متوقفة، لذلك لن يذكّرك شيء بالأحداث المتزامنة.", + "remove": "إزالة", + "remove_from_device": "إزالة من هذا الجهاز", + "remove_message": "ستُزال جهات الاتصال والتقويمات الخاصة بالحساب من هذا الجهاز. وستُفقد التغييرات التي أُجريت هنا ولم تصل إلى الخادم.", + "remove_title": "إزالة من هذا الجهاز؟", + "review_deletions": "مراجعة المحذوفات", + "shared_by": "تمت المشاركة بواسطة {name}", + "sign_in_again": "تسجيل الدخول مرة أخرى", + "status_auth": "لم يعد الخادم يقبل تسجيل الدخول. سجّل الدخول مرة أخرى لمواصلة المزامنة.", + "status_cancelled": "توقفت آخر مزامنة قبل اكتمالها. وستُستكمل في المرة القادمة.", + "status_failed": "فشلت آخر مزامنة: {message}", + "status_failed_plain": "فشلت آخر مزامنة.", + "status_io": "تعذّر الوصول إلى الخادم {when}. سيحاول Android مرة أخرى.", + "status_item_errors": "{count, plural, zero {تعذّرت مزامنة # عنصر.} one {تعذّرت مزامنة عنصر واحد.} two {تعذّرت مزامنة عنصرين.} few {تعذّرت مزامنة # عناصر.} many {تعذّرت مزامنة # عنصرًا.} other {تعذّرت مزامنة # عنصر.}}", + "status_off": "متوقفة", + "status_paused": "متوقفة مؤقتًا في إعدادات Android", + "status_permission_calendar": "تم إيقاف الوصول إلى التقويمات، لذلك لا تتم مزامنة أي شيء.", + "status_permission_contacts": "تم إيقاف الوصول إلى جهات الاتصال، لذلك لا تتم مزامنة أي شيء.", + "status_removed": "تمت الإزالة في إعدادات Android. فعّل المزامنة لإضافة الحساب مرة أخرى.", + "status_safety_abort": "تم الإيقاف: لم يُرجع الخادم أي شيء رغم أن هذا الجهاز لا يزال يحتوي على عناصر. تحقّق من الخادم، ثم أعد المزامنة.", + "status_synced": "آخر مزامنة: {when}", + "status_syncing": "جارٍ المزامنة…", + "status_too_many_deletions": "حُذفت عناصر كثيرة على هذا الجهاز. لن تُحذف من الخادم حتى تؤكد ذلك.", + "status_unsupported_calendar": "لا يوفّر الخادم أي تقويمات لهذا الحساب.", + "status_unsupported_contacts": "لا يوفّر الخادم أي دفاتر عناوين لهذا الحساب.", + "status_waiting": "بانتظار المزامنة الأولى", + "status_waiting_next": "بانتظار المزامنة التالية", + "suspended_desc": "تم تسجيل الخروج. تبقى جهات الاتصال والتقويمات الخاصة به على هذا الجهاز مع التغييرات التي أُجريت هنا، وستتم مزامنتها مجددًا بعد تسجيل الدخول.", + "switch_label": "مزامنة {account} مع هذا الجهاز", + "sync_now": "مزامنة الآن", + "turn_off_anyway": "إيقاف", + "turn_off_failed": "تعذّرت إزالة البيانات المتزامنة من هذا الجهاز. المزامنة متوقفة مؤقتًا؛ حاول مرة أخرى لاحقًا.", + "turn_off_failed_title": "تعذّر إيقاف المزامنة", + "turn_off_pending": "{count, plural, zero {# تغيير أُجري على هذا الجهاز لم يصل إلى الخادم بعد. سيُفقد إذا أوقفت المزامنة الآن.} one {تغيير واحد أُجري على هذا الجهاز لم يصل إلى الخادم بعد. سيُفقد إذا أوقفت المزامنة الآن.} two {تغييران أُجريا على هذا الجهاز لم يصلا إلى الخادم بعد. سيُفقدان إذا أوقفت المزامنة الآن.} few {# تغييرات أُجريت على هذا الجهاز لم تصل إلى الخادم بعد. ستُفقد إذا أوقفت المزامنة الآن.} many {# تغييرًا أُجريت على هذا الجهاز لم تصل إلى الخادم بعد. ستُفقد إذا أوقفت المزامنة الآن.} other {# تغيير أُجريت على هذا الجهاز لم تصل إلى الخادم بعد. ستُفقد إذا أوقفت المزامنة الآن.}}", + "turn_off_title": "الإيقاف على أي حال؟", + "turn_off_unknown": "تعذّر على Bulwark التحقق مما إذا كانت التغييرات التي أُجريت على هذا الجهاز قد وصلت إلى الخادم. ستُفقد إذا أوقفت المزامنة الآن.", + "turning_off": "جارٍ تحميل التغييرات ثم إزالة البيانات من هذا الجهاز…", + "turning_on": "جارٍ الإعداد…" + }, "downloads": { "attachment_template": { "description_mobile": "القالب المستخدم عند حفظ مرفق أو مشاركته." diff --git a/locales/rn/ca.json b/locales/rn/ca.json index 1f3d0c8e..df83812e 100644 --- a/locales/rn/ca.json +++ b/locales/rn/ca.json @@ -93,6 +93,8 @@ "until_date": "Fins al {date}" }, "settings": { + "device_sync_desc": "Mostra els vostres calendaris a l'aplicació Calendari i a altres aplicacions d'aquest dispositiu. Els canvis se sincronitzen en tots dos sentits.", + "device_sync_title": "Sincronitza amb aquest dispositiu", "show_week_numbers_mobile_desc": "Mostra els números de setmana a la vista de mes.", "time_zone": "Zona horària", "time_zone_auto": "Zona horària del dispositiu", @@ -249,6 +251,18 @@ "color_tag": "Etiqueta", "remove_color": "Elimina l'etiqueta" }, + "device_sync": { + "auth_problem_text": "El servidor ja no accepta l'inici de sessió de {account}, de manera que els seus contactes i calendaris d'aquest dispositiu han deixat de sincronitzar-se. Torneu a iniciar la sessió per reprendre la sincronització. Els canvis fets en aquest dispositiu es conserven.", + "auth_problem_title": "Torneu a iniciar la sessió per continuar sincronitzant", + "problem_channel": "Problemes de sincronització", + "sign_out_anyway": "Tanca la sessió", + "sign_out_pending_message": "{count, plural, one {# canvi fet en aquest dispositiu no ha arribat al servidor. Es perdrà si tanqueu la sessió ara.} other {# canvis fets en aquest dispositiu no han arribat al servidor. Es perdran si tanqueu la sessió ara.}}", + "sign_out_pending_title": "Voleu tancar la sessió igualment?", + "sign_out_unknown_message": "És possible que els canvis fets en aquest dispositiu no hagin arribat al servidor. Es perdran si tanqueu la sessió ara.", + "signed_out_text": "S'ha tancat la sessió de {account}, de manera que els seus contactes i calendaris d'aquest dispositiu ja no se sincronitzen. Torneu a iniciar la sessió per reprendre la sincronització. Els canvis fets en aquest dispositiu es conserven.", + "signed_out_title": "Inicieu la sessió per continuar sincronitzant", + "signing_out": "S'estan eliminant d'aquest dispositiu els contactes i calendaris sincronitzats…" + }, "email_composer": { "attach_camera": "Càmera", "attachment_reminder_body": "El vostre missatge esmenta «{keyword}», però no hi ha cap fitxer adjunt. Voleu enviar-lo igualment?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# contacte} other {# contactes}}", "default_book": "Llibreta d'adreces predeterminada", "description_mobile": "Preferències de visualització i eines de la llibreta d'adreces.", + "device_sync_desc": "Mostra les vostres llibretes d'adreces a l'aplicació Contactes i a altres aplicacions d'aquest dispositiu. Els canvis se sincronitzen en tots dos sentits.", + "device_sync_title": "Sincronitza amb aquest dispositiu", "export_description_count": "{count, plural, one {Exporta # contacte com un únic fitxer vCard.} other {Exporta # contactes com un únic fitxer vCard.}}", "export_empty": "No hi ha contactes per exportar", "import_description_mobile": "Importa contactes des d'un fitxer vCard (.vcf) amb detecció de duplicats.", "manage_description_mobile": "Creeu, canvieu el nom i suprimiu les llibretes d'adreces que organitzen els contactes." }, + "device_sync": { + "also_synced_by": "També se sincronitza per a {accounts}, per això apareix dues vegades en aquest dispositiu.", + "android_settings": "Configuració del compte d'Android", + "blocked_calendar": "Android ja no demana accés als calendaris. Permeteu-lo a la configuració de l'aplicació i torneu a activar la sincronització.", + "blocked_contacts": "Android ja no demana accés als contactes. Permeteu-lo a la configuració de l'aplicació i torneu a activar la sincronització.", + "blocked_title": "L'accés està desactivat", + "books_default": "Les vostres llibretes d'adreces se sincronitzen; les compartides, només si les trieu.", + "books_hint": "Les vostres llibretes d'adreces se sincronitzen tret que les desactiveu; les compartides, només si les activeu.", + "books_label": "Llibretes d'adreces", + "calendars_default": "Els vostres calendaris se sincronitzen; els compartits, només si els trieu.", + "calendars_hint": "Els vostres calendaris se sincronitzen tret que els desactiveu; els compartits, només si els activeu. Les llistes de tasques es queden al Bulwark.", + "calendars_label": "Calendaris", + "choose": "Tria", + "choose_books_title": "Llibretes d'adreces per sincronitzar", + "choose_calendars_title": "Calendaris per sincronitzar", + "collections_count": "Sincronització: {selected} de {total}", + "continue": "Continua", + "deletions_delete": "Suprimeix al servidor", + "deletions_message": "S'han suprimit molts elements alhora en aquest dispositiu, de manera que no s'han suprimit al servidor. Suprimiu-los també allà o recupereu-los en aquest dispositiu.", + "deletions_restore": "Recupera'ls", + "deletions_title": "Voleu suprimir-los també al servidor?", + "denied_calendar": "Sense accés als calendaris, el Bulwark no pot posar els vostres calendaris en aquest dispositiu.", + "denied_contacts": "Sense accés als contactes, el Bulwark no pot posar les vostres llibretes d'adreces en aquest dispositiu.", + "denied_title": "La sincronització continua desactivada", + "failed_title": "No s'ha pogut activar la sincronització", + "grant_access": "Concedeix l'accés", + "interval_15m": "Cada 15 minuts", + "interval_1h": "Cada hora", + "interval_30m": "Cada 30 minuts", + "interval_6h": "Cada 6 hores", + "interval_desc": "Amb quina freqüència sincronitza Android pel seu compte. Els canvis fets al Bulwark i els que arriben per push se sincronitzen abans.", + "interval_label": "Interval de sincronització", + "interval_manual": "Sense sincronització periòdica", + "keep_syncing": "Continua sincronitzant", + "list_error_auth": "El servidor no ha acceptat l'inici de sessió.", + "list_error_network": "No s'ha pogut connectar amb el servidor.", + "list_error_server": "El servidor no n'ha pogut mostrar la llista.", + "list_error_signed_out": "Aquest compte no té la sessió iniciada en aquest dispositiu.", + "master_off": "La sincronització automàtica està desactivada en aquest dispositiu, de manera que només se sincronitza quan toqueu «Sincronitza ara».", + "new_contacts_default": "Llibreta d'adreces predeterminada", + "new_contacts_desc": "La llibreta d'adreces per als contactes creats en altres aplicacions d'aquest dispositiu.", + "new_contacts_label": "Desa els contactes nous a", + "no_books": "Aquest compte no té cap llibreta d'adreces.", + "no_calendars": "Aquest compte no té cap calendari per sincronitzar.", + "ok": "D'acord", + "open_app_settings": "Obre la configuració", + "own_collections": "Personals", + "rationale_calendar": "Per mostrar els vostres calendaris a l'aplicació Calendari, el Bulwark necessita accés als calendaris d'aquest dispositiu. Només modifica els calendaris del seu propi compte.", + "rationale_contacts": "Per mostrar les vostres llibretes d'adreces a l'aplicació Contactes, el Bulwark necessita accés als contactes d'aquest dispositiu. Només modifica els contactes del seu propi compte.", + "rationale_title_calendar": "Permet l'accés als calendaris", + "rationale_title_contacts": "Permet l'accés als contactes", + "read_only": "Només lectura", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Aplicació Calendari", + "reminder_question_message": "Els recordatoris dels esdeveniments sincronitzats poden venir de l'aplicació de calendari d'aquest dispositiu o del Bulwark. Trieu-ne un perquè no rebeu els recordatoris dues vegades. Ho podeu canviar més endavant.", + "reminder_question_title": "Qui us ha d'enviar els recordatoris?", + "reminders_desc": "Qui us recorda els esdeveniments sincronitzats. L'altre no avisa, perquè no rebeu els recordatoris dues vegades.", + "reminders_label": "Recordatoris", + "reminders_nobody": "Les notificacions d'esdeveniments del Bulwark estan desactivades, de manera que res no us recorda els esdeveniments sincronitzats.", + "remove": "Elimina", + "remove_from_device": "Elimina d'aquest dispositiu", + "remove_message": "Els contactes i calendaris del compte s'eliminen d'aquest dispositiu. Els canvis fets aquí que no hagin arribat al servidor es perden.", + "remove_title": "Voleu eliminar-ho d'aquest dispositiu?", + "review_deletions": "Revisa les supressions", + "shared_by": "Compartit per {name}", + "sign_in_again": "Torna a iniciar la sessió", + "status_auth": "El servidor ja no accepta l'inici de sessió. Torneu a iniciar la sessió per continuar sincronitzant.", + "status_cancelled": "L'última sincronització s'ha aturat abans d'hora. Continuarà la propera vegada.", + "status_failed": "Ha fallat l'última sincronització: {message}", + "status_failed_plain": "Ha fallat l'última sincronització.", + "status_io": "No s'ha pogut connectar amb el servidor {when}. Android ho tornarà a provar.", + "status_item_errors": "{count, plural, one {No s'ha pogut sincronitzar # element.} other {No s'han pogut sincronitzar # elements.}}", + "status_off": "Desactivada", + "status_paused": "En pausa a la configuració d'Android", + "status_permission_calendar": "S'ha desactivat l'accés als calendaris, de manera que no se sincronitza res.", + "status_permission_contacts": "S'ha desactivat l'accés als contactes, de manera que no se sincronitza res.", + "status_removed": "S'ha eliminat a la configuració d'Android. Activeu la sincronització per tornar a afegir el compte.", + "status_safety_abort": "S'ha aturat: el servidor no ha retornat res, però aquest dispositiu encara conté elements. Comproveu el servidor i torneu a sincronitzar.", + "status_synced": "Sincronitzat {when}", + "status_syncing": "S'està sincronitzant…", + "status_too_many_deletions": "S'han suprimit molts elements en aquest dispositiu. No se suprimiran al servidor fins que no ho confirmeu.", + "status_unsupported_calendar": "El servidor no ofereix calendaris per a aquest compte.", + "status_unsupported_contacts": "El servidor no ofereix llibretes d'adreces per a aquest compte.", + "status_waiting": "S'està esperant la primera sincronització", + "status_waiting_next": "S'està esperant la propera sincronització", + "suspended_desc": "Sessió tancada. Els seus contactes i calendaris es queden en aquest dispositiu, amb els canvis fets aquí, i es tornaran a sincronitzar quan inicieu la sessió.", + "switch_label": "Sincronitza {account} amb aquest dispositiu", + "sync_now": "Sincronitza ara", + "turn_off_anyway": "Desactiva", + "turn_off_failed": "No s'han pogut eliminar les dades sincronitzades d'aquest dispositiu. La sincronització està en pausa; torneu-ho a provar més tard.", + "turn_off_failed_title": "No s'ha pogut desactivar la sincronització", + "turn_off_pending": "{count, plural, one {# canvi fet en aquest dispositiu encara no ha arribat al servidor. Es perdrà si desactiveu la sincronització ara.} other {# canvis fets en aquest dispositiu encara no han arribat al servidor. Es perdran si desactiveu la sincronització ara.}}", + "turn_off_title": "Voleu desactivar-la igualment?", + "turn_off_unknown": "El Bulwark no ha pogut comprovar si els canvis fets en aquest dispositiu han arribat al servidor. Es perdran si desactiveu la sincronització ara.", + "turning_off": "S'estan pujant els canvis i després s'eliminaran les dades d'aquest dispositiu…", + "turning_on": "S'està configurant…" + }, "downloads": { "attachment_template": { "description_mobile": "Plantilla utilitzada en desar o compartir un fitxer adjunt." diff --git a/locales/rn/cs.json b/locales/rn/cs.json index 9c527427..d8cd5e19 100644 --- a/locales/rn/cs.json +++ b/locales/rn/cs.json @@ -93,6 +93,8 @@ "until_date": "Do {date}" }, "settings": { + "device_sync_desc": "Zobrazí vaše kalendáře v aplikaci Kalendář a dalších aplikacích v tomto zařízení. Změny se synchronizují oběma směry.", + "device_sync_title": "Synchronizovat s tímto zařízením", "show_week_numbers_mobile_desc": "Zobrazovat čísla týdnů v měsíčním zobrazení.", "time_zone": "Časové pásmo", "time_zone_auto": "Časové pásmo zařízení", @@ -249,6 +251,18 @@ "color_tag": "Štítek", "remove_color": "Odebrat štítek" }, + "device_sync": { + "auth_problem_text": "Server už nepřijímá přihlášení účtu {account}, proto se jeho kontakty a kalendáře v tomto zařízení přestaly synchronizovat. Chcete-li pokračovat, přihlaste se znovu. Změny provedené v tomto zařízení zůstanou zachovány.", + "auth_problem_title": "Pro další synchronizaci se znovu přihlaste", + "problem_channel": "Problémy se synchronizací", + "sign_out_anyway": "Odhlásit se", + "sign_out_pending_message": "{count, plural, one {# změna provedená v tomto zařízení se nedostala na server. Pokud se teď odhlásíte, bude ztracena.} few {# změny provedené v tomto zařízení se nedostaly na server. Pokud se teď odhlásíte, budou ztraceny.} many {# změny provedené v tomto zařízení se nedostalo na server. Pokud se teď odhlásíte, budou ztraceny.} other {# změn provedených v tomto zařízení se nedostalo na server. Pokud se teď odhlásíte, budou ztraceny.}}", + "sign_out_pending_title": "Přesto se odhlásit?", + "sign_out_unknown_message": "Změny provedené v tomto zařízení se možná nedostaly na server. Pokud se teď odhlásíte, budou ztraceny.", + "signed_out_text": "Účet {account} byl odhlášen, proto se jeho kontakty a kalendáře v tomto zařízení už nesynchronizují. Chcete-li pokračovat, přihlaste se znovu. Změny provedené v tomto zařízení zůstanou zachovány.", + "signed_out_title": "Pro další synchronizaci se přihlaste", + "signing_out": "Odebírání synchronizovaných kontaktů a kalendářů z tohoto zařízení…" + }, "email_composer": { "attach_camera": "Fotoaparát", "attachment_reminder_body": "Vaše zpráva obsahuje slovo „{keyword}“, ale není k ní připojen žádný soubor. Přesto odeslat?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# kontakt} few {# kontakty} many {# kontaktu} other {# kontaktů}}", "default_book": "Výchozí adresář", "description_mobile": "Předvolby zobrazení a nástroje pro adresáře.", + "device_sync_desc": "Zobrazí vaše adresáře v aplikaci Kontakty a dalších aplikacích v tomto zařízení. Změny se synchronizují oběma směry.", + "device_sync_title": "Synchronizovat s tímto zařízením", "export_description_count": "{count, plural, one {Exportovat # kontakt do jednoho souboru vCard.} few {Exportovat # kontakty do jednoho souboru vCard.} many {Exportovat # kontaktu do jednoho souboru vCard.} other {Exportovat # kontaktů do jednoho souboru vCard.}}", "export_empty": "Žádné kontakty k exportu", "import_description_mobile": "Importovat kontakty ze souboru vCard (.vcf) s rozpoznáním duplicit.", "manage_description_mobile": "Vytvářejte, přejmenovávejte a odstraňujte adresáře, ve kterých máte uspořádané kontakty." }, + "device_sync": { + "also_synced_by": "Synchronizuje se také pro {accounts}, proto se v tomto zařízení zobrazuje dvakrát.", + "android_settings": "Nastavení účtu v Androidu", + "blocked_calendar": "Android už o přístup ke kalendářům nežádá. Povolte ho v nastavení aplikace a pak synchronizaci znovu zapněte.", + "blocked_contacts": "Android už o přístup ke kontaktům nežádá. Povolte ho v nastavení aplikace a pak synchronizaci znovu zapněte.", + "blocked_title": "Přístup je vypnutý", + "books_default": "Vaše vlastní adresáře se synchronizují, sdílené jen tehdy, když je vyberete.", + "books_hint": "Vaše vlastní adresáře se synchronizují, pokud je nevypnete; sdílené jen tehdy, když je zapnete.", + "books_label": "Adresáře", + "calendars_default": "Vaše vlastní kalendáře se synchronizují, sdílené jen tehdy, když je vyberete.", + "calendars_hint": "Vaše vlastní kalendáře se synchronizují, pokud je nevypnete; sdílené jen tehdy, když je zapnete. Seznamy úkolů zůstávají v Bulwarku.", + "calendars_label": "Kalendáře", + "choose": "Vybrat", + "choose_books_title": "Adresáře k synchronizaci", + "choose_calendars_title": "Kalendáře k synchronizaci", + "collections_count": "Synchronizováno: {selected} z {total}", + "continue": "Pokračovat", + "deletions_delete": "Smazat na serveru", + "deletions_message": "V tomto zařízení bylo najednou smazáno mnoho položek, proto nebyly smazány na serveru. Smažte je i tam, nebo je v tomto zařízení obnovte.", + "deletions_restore": "Obnovit", + "deletions_title": "Smazat je i na serveru?", + "denied_calendar": "Bez přístupu ke kalendářům nemůže Bulwark přenést vaše kalendáře do tohoto zařízení.", + "denied_contacts": "Bez přístupu ke kontaktům nemůže Bulwark přenést vaše adresáře do tohoto zařízení.", + "denied_title": "Synchronizace zůstává vypnutá", + "failed_title": "Synchronizaci se nepodařilo zapnout", + "grant_access": "Udělit přístup", + "interval_15m": "Každých 15 minut", + "interval_1h": "Každou hodinu", + "interval_30m": "Každých 30 minut", + "interval_6h": "Každých 6 hodin", + "interval_desc": "Jak často Android synchronizuje sám. Úpravy v Bulwarku a změny oznámené přes push se synchronizují dříve.", + "interval_label": "Interval synchronizace", + "interval_manual": "Bez pravidelné synchronizace", + "keep_syncing": "Dál synchronizovat", + "list_error_auth": "Server nepřijal přihlášení.", + "list_error_network": "Nepodařilo se spojit se serverem.", + "list_error_server": "Server nedokázal poskytnout seznam.", + "list_error_signed_out": "Tento účet není v tomto zařízení přihlášen.", + "master_off": "Automatická synchronizace je v tomto zařízení vypnutá, proto se synchronizuje jen po klepnutí na „Synchronizovat nyní“.", + "new_contacts_default": "Výchozí adresář", + "new_contacts_desc": "Adresář pro kontakty vytvořené v jiných aplikacích v tomto zařízení.", + "new_contacts_label": "Nové kontakty ukládat do", + "no_books": "Tento účet nemá žádné adresáře.", + "no_calendars": "Tento účet nemá žádné kalendáře k synchronizaci.", + "ok": "OK", + "open_app_settings": "Otevřít nastavení", + "own_collections": "Vlastní", + "rationale_calendar": "Aby mohl Bulwark zobrazit vaše kalendáře v aplikaci Kalendář, potřebuje přístup ke kalendářům v tomto zařízení. Mění jen kalendáře svého vlastního účtu.", + "rationale_contacts": "Aby mohl Bulwark zobrazit vaše adresáře v aplikaci Kontakty, potřebuje přístup ke kontaktům v tomto zařízení. Mění jen kontakty svého vlastního účtu.", + "rationale_title_calendar": "Povolit přístup ke kalendářům", + "rationale_title_contacts": "Povolit přístup ke kontaktům", + "read_only": "Jen pro čtení", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Aplikace Kalendář", + "reminder_question_message": "Připomenutí synchronizovaných událostí může zobrazovat aplikace kalendáře v tomto zařízení, nebo Bulwark. Vyberte jednu možnost, abyste nedostávali připomenutí dvakrát. Později to můžete změnit.", + "reminder_question_title": "Kdo vám má připomínat události?", + "reminders_desc": "Kdo vám připomíná synchronizované události. Druhá možnost mlčí, abyste nedostávali připomenutí dvakrát.", + "reminders_label": "Připomenutí", + "reminders_nobody": "Oznámení událostí v Bulwarku jsou vypnutá, takže vám synchronizované události nic nepřipomene.", + "remove": "Odebrat", + "remove_from_device": "Odebrat z tohoto zařízení", + "remove_message": "Kontakty a kalendáře účtu budou z tohoto zařízení odebrány. Změny provedené zde, které se nedostaly na server, budou ztraceny.", + "remove_title": "Odebrat z tohoto zařízení?", + "review_deletions": "Zkontrolovat smazání", + "shared_by": "Sdílí {name}", + "sign_in_again": "Znovu se přihlásit", + "status_auth": "Server už nepřijímá přihlášení. Pro další synchronizaci se znovu přihlaste.", + "status_cancelled": "Poslední synchronizace skončila předčasně. Příště bude pokračovat.", + "status_failed": "Poslední synchronizace selhala: {message}", + "status_failed_plain": "Poslední synchronizace selhala.", + "status_io": "Server byl {when} nedostupný. Android to zkusí znovu.", + "status_item_errors": "{count, plural, one {# položku se nepodařilo synchronizovat.} few {# položky se nepodařilo synchronizovat.} many {# položky se nepodařilo synchronizovat.} other {# položek se nepodařilo synchronizovat.}}", + "status_off": "Vypnuto", + "status_paused": "Pozastaveno v nastavení Androidu", + "status_permission_calendar": "Přístup ke kalendářům byl vypnut, proto se nic nesynchronizuje.", + "status_permission_contacts": "Přístup ke kontaktům byl vypnut, proto se nic nesynchronizuje.", + "status_removed": "Odebráno v nastavení Androidu. Chcete-li účet znovu přidat, zapněte synchronizaci.", + "status_safety_abort": "Zastaveno: server nic nevrátil, přestože toto zařízení stále obsahuje položky. Zkontrolujte server a pak synchronizujte znovu.", + "status_synced": "Synchronizováno {when}", + "status_syncing": "Synchronizace…", + "status_too_many_deletions": "V tomto zařízení bylo smazáno mnoho položek. Na serveru budou smazány až po vašem potvrzení.", + "status_unsupported_calendar": "Server pro tento účet nenabízí žádné kalendáře.", + "status_unsupported_contacts": "Server pro tento účet nenabízí žádné adresáře.", + "status_waiting": "Čeká se na první synchronizaci", + "status_waiting_next": "Čeká se na další synchronizaci", + "suspended_desc": "Odhlášeno. Kontakty a kalendáře účtu zůstanou v tomto zařízení i se zde provedenými změnami a po přihlášení se znovu synchronizují.", + "switch_label": "Synchronizovat {account} s tímto zařízením", + "sync_now": "Synchronizovat nyní", + "turn_off_anyway": "Vypnout", + "turn_off_failed": "Synchronizovaná data se nepodařilo z tohoto zařízení odebrat. Synchronizace je pozastavena; zkuste to později.", + "turn_off_failed_title": "Synchronizaci se nepodařilo vypnout", + "turn_off_pending": "{count, plural, one {# změna provedená v tomto zařízení se ještě nedostala na server. Pokud teď synchronizaci vypnete, bude ztracena.} few {# změny provedené v tomto zařízení se ještě nedostaly na server. Pokud teď synchronizaci vypnete, budou ztraceny.} many {# změny provedené v tomto zařízení se ještě nedostalo na server. Pokud teď synchronizaci vypnete, budou ztraceny.} other {# změn provedených v tomto zařízení se ještě nedostalo na server. Pokud teď synchronizaci vypnete, budou ztraceny.}}", + "turn_off_title": "Přesto vypnout?", + "turn_off_unknown": "Bulwark nemohl ověřit, zda se změny provedené v tomto zařízení dostaly na server. Pokud teď synchronizaci vypnete, budou ztraceny.", + "turning_off": "Nahrávání změn a poté odebírání dat z tohoto zařízení…", + "turning_on": "Nastavování…" + }, "downloads": { "attachment_template": { "description_mobile": "Šablona použitá při ukládání nebo sdílení přílohy." diff --git a/locales/rn/da.json b/locales/rn/da.json index e80afe4d..a2866552 100644 --- a/locales/rn/da.json +++ b/locales/rn/da.json @@ -93,6 +93,8 @@ "until_date": "Indtil {date}" }, "settings": { + "device_sync_desc": "Vis dine kalendere i Kalender-appen og andre apps på denne enhed. Ændringer synkroniseres begge veje.", + "device_sync_title": "Synkronisér med denne enhed", "show_week_numbers_mobile_desc": "Vis ugenumre i månedsvisningen.", "time_zone": "Tidszone", "time_zone_auto": "Enhedens tidszone", @@ -249,6 +251,18 @@ "color_tag": "Tag", "remove_color": "Fjern tag" }, + "device_sync": { + "auth_problem_text": "Serveren accepterer ikke længere login for {account}, så kontoens kontakter og kalendere på denne enhed synkroniseres ikke længere. Log ind igen for at fortsætte. Ændringer foretaget på denne enhed bevares.", + "auth_problem_title": "Log ind igen for at fortsætte synkroniseringen", + "problem_channel": "Synkroniseringsproblemer", + "sign_out_anyway": "Log ud", + "sign_out_pending_message": "{count, plural, one {# ændring foretaget på denne enhed er ikke nået frem til serveren. Den går tabt, hvis du logger ud nu.} other {# ændringer foretaget på denne enhed er ikke nået frem til serveren. De går tabt, hvis du logger ud nu.}}", + "sign_out_pending_title": "Vil du logge ud alligevel?", + "sign_out_unknown_message": "Ændringer foretaget på denne enhed er måske ikke nået frem til serveren. De går tabt, hvis du logger ud nu.", + "signed_out_text": "{account} blev logget ud, så kontoens kontakter og kalendere på denne enhed synkroniseres ikke længere. Log ind igen for at fortsætte. Ændringer foretaget på denne enhed bevares.", + "signed_out_title": "Log ind for at fortsætte synkroniseringen", + "signing_out": "Fjerner synkroniserede kontakter og kalendere fra denne enhed…" + }, "email_composer": { "attach_camera": "Kamera", "attachment_reminder_body": "Din besked nævner \"{keyword}\", men ingen fil er vedhæftet. Send alligevel?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# kontakt} other {# kontakter}}", "default_book": "Standardadressebog", "description_mobile": "Visningsindstillinger og værktøjer til adressebøger.", + "device_sync_desc": "Vis dine adressebøger i Kontakter-appen og andre apps på denne enhed. Ændringer synkroniseres begge veje.", + "device_sync_title": "Synkronisér med denne enhed", "export_description_count": "{count, plural, one {Eksportér # kontakt som én vCard-fil.} other {Eksportér # kontakter som én vCard-fil.}}", "export_empty": "Ingen kontakter at eksportere", "import_description_mobile": "Importér kontakter fra en vCard-fil (.vcf) med registrering af dubletter.", "manage_description_mobile": "Opret, omdøb og fjern de adressebøger, der organiserer dine kontakter." }, + "device_sync": { + "also_synced_by": "Synkroniseres også for {accounts}, så den vises to gange på denne enhed.", + "android_settings": "Android-kontoindstillinger", + "blocked_calendar": "Android spørger ikke længere om adgang til kalendere. Tillad det i appens indstillinger, og slå derefter synkronisering til igen.", + "blocked_contacts": "Android spørger ikke længere om adgang til kontakter. Tillad det i appens indstillinger, og slå derefter synkronisering til igen.", + "blocked_title": "Adgang er slået fra", + "books_default": "Dine egne adressebøger synkroniseres; delte kun, når du vælger dem.", + "books_hint": "Dine egne adressebøger synkroniseres, medmindre du slår dem fra; delte kun, når du slår dem til.", + "books_label": "Adressebøger", + "calendars_default": "Dine egne kalendere synkroniseres; delte kun, når du vælger dem.", + "calendars_hint": "Dine egne kalendere synkroniseres, medmindre du slår dem fra; delte kun, når du slår dem til. Opgavelister bliver i Bulwark.", + "calendars_label": "Kalendere", + "choose": "Vælg", + "choose_books_title": "Adressebøger, der skal synkroniseres", + "choose_calendars_title": "Kalendere, der skal synkroniseres", + "collections_count": "{selected} af {total} synkroniseres", + "continue": "Fortsæt", + "deletions_delete": "Slet på serveren", + "deletions_message": "Mange elementer blev slettet på én gang på denne enhed, så de blev ikke slettet på serveren. Slet dem også der, eller hent dem tilbage på denne enhed.", + "deletions_restore": "Hent dem tilbage", + "deletions_title": "Vil du også slette dem på serveren?", + "denied_calendar": "Uden adgang til kalendere kan Bulwark ikke lægge dine kalendere på denne enhed.", + "denied_contacts": "Uden adgang til kontakter kan Bulwark ikke lægge dine adressebøger på denne enhed.", + "denied_title": "Synkronisering forbliver slået fra", + "failed_title": "Synkronisering kunne ikke slås til", + "grant_access": "Giv adgang", + "interval_15m": "Hvert 15. minut", + "interval_1h": "Hver time", + "interval_30m": "Hvert 30. minut", + "interval_6h": "Hver 6. time", + "interval_desc": "Hvor ofte Android synkroniserer af sig selv. Ændringer i Bulwark og ændringer, der meldes via push, synkroniseres hurtigere.", + "interval_label": "Synkroniseringsinterval", + "interval_manual": "Ingen regelmæssig synkronisering", + "keep_syncing": "Fortsæt synkronisering", + "list_error_auth": "Serveren accepterede ikke login.", + "list_error_network": "Serveren kunne ikke nås.", + "list_error_server": "Serveren kunne ikke vise listen.", + "list_error_signed_out": "Denne konto er ikke logget ind på denne enhed.", + "master_off": "Automatisk synkronisering er slået fra på denne enhed, så der synkroniseres kun, når du trykker på \"Synkronisér nu\".", + "new_contacts_default": "Standardadressebog", + "new_contacts_desc": "Adressebogen til kontakter, der oprettes i andre apps på denne enhed.", + "new_contacts_label": "Gem nye kontakter i", + "no_books": "Denne konto har ingen adressebøger.", + "no_calendars": "Denne konto har ingen kalendere at synkronisere.", + "ok": "OK", + "open_app_settings": "Åbn appindstillinger", + "own_collections": "Dine egne", + "rationale_calendar": "For at vise dine kalendere i Kalender-appen skal Bulwark have adgang til kalenderne på denne enhed. Bulwark ændrer kun kalenderne for sin egen konto.", + "rationale_contacts": "For at vise dine adressebøger i Kontakter-appen skal Bulwark have adgang til kontakterne på denne enhed. Bulwark ændrer kun kontakterne for sin egen konto.", + "rationale_title_calendar": "Giv adgang til kalendere", + "rationale_title_contacts": "Giv adgang til kontakter", + "read_only": "Skrivebeskyttet", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Kalender-app", + "reminder_question_message": "Påmindelser om synkroniserede begivenheder kan komme fra kalenderappen på denne enhed eller fra Bulwark. Vælg én af dem, så du ikke får to påmindelser. Du kan ændre det senere.", + "reminder_question_title": "Hvem skal minde dig om begivenheder?", + "reminders_desc": "Hvem der minder dig om synkroniserede begivenheder. Den anden forholder sig tavs, så du ikke får to påmindelser.", + "reminders_label": "Påmindelser", + "reminders_nobody": "Bulwarks notifikationer om begivenheder er slået fra, så intet minder dig om synkroniserede begivenheder.", + "remove": "Fjern", + "remove_from_device": "Fjern fra denne enhed", + "remove_message": "Kontoens kontakter og kalendere fjernes fra denne enhed. Ændringer foretaget her, som ikke er nået frem til serveren, går tabt.", + "remove_title": "Fjern fra denne enhed?", + "review_deletions": "Gennemgå sletninger", + "shared_by": "Delt af {name}", + "sign_in_again": "Log ind igen", + "status_auth": "Serveren accepterer ikke længere login. Log ind igen for at fortsætte synkroniseringen.", + "status_cancelled": "Den seneste synkronisering stoppede før tid. Den fortsætter næste gang.", + "status_failed": "Den seneste synkronisering mislykkedes: {message}", + "status_failed_plain": "Den seneste synkronisering mislykkedes.", + "status_io": "Serveren kunne ikke nås {when}. Android prøver igen.", + "status_item_errors": "{count, plural, one {# element kunne ikke synkroniseres.} other {# elementer kunne ikke synkroniseres.}}", + "status_off": "Fra", + "status_paused": "Sat på pause i Android-indstillinger", + "status_permission_calendar": "Adgangen til kalendere blev slået fra, så intet synkroniseres.", + "status_permission_contacts": "Adgangen til kontakter blev slået fra, så intet synkroniseres.", + "status_removed": "Fjernet i Android-indstillinger. Slå synkronisering til for at tilføje kontoen igen.", + "status_safety_abort": "Stoppet: Serveren returnerede intet, selvom denne enhed stadig har elementer. Tjek serveren, og synkronisér derefter igen.", + "status_synced": "Sidst synkroniseret: {when}", + "status_syncing": "Synkroniserer…", + "status_too_many_deletions": "Mange elementer blev slettet på denne enhed. De slettes ikke på serveren, før du bekræfter.", + "status_unsupported_calendar": "Serveren tilbyder ingen kalendere til denne konto.", + "status_unsupported_contacts": "Serveren tilbyder ingen adressebøger til denne konto.", + "status_waiting": "Venter på den første synkronisering", + "status_waiting_next": "Venter på den næste synkronisering", + "suspended_desc": "Logget ud. Kontoens kontakter og kalendere bliver på denne enhed sammen med de ændringer, der er foretaget her, og synkroniseres igen, når du logger ind.", + "switch_label": "Synkronisér {account} med denne enhed", + "sync_now": "Synkronisér nu", + "turn_off_anyway": "Slå fra", + "turn_off_failed": "De synkroniserede data kunne ikke fjernes fra denne enhed. Synkroniseringen er sat på pause; prøv igen senere.", + "turn_off_failed_title": "Synkronisering kunne ikke slås fra", + "turn_off_pending": "{count, plural, one {# ændring foretaget på denne enhed er endnu ikke nået frem til serveren. Den går tabt, hvis du slår synkronisering fra nu.} other {# ændringer foretaget på denne enhed er endnu ikke nået frem til serveren. De går tabt, hvis du slår synkronisering fra nu.}}", + "turn_off_title": "Vil du slå fra alligevel?", + "turn_off_unknown": "Bulwark kunne ikke kontrollere, om ændringer foretaget på denne enhed er nået frem til serveren. De går tabt, hvis du slår synkronisering fra nu.", + "turning_off": "Uploader ændringer og fjerner derefter data fra denne enhed…", + "turning_on": "Konfigurerer…" + }, "downloads": { "attachment_template": { "description_mobile": "Skabelon, der bruges, når en vedhæftning gemmes eller deles." diff --git a/locales/rn/de.json b/locales/rn/de.json index 67f7861e..6528fd2d 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -93,6 +93,8 @@ "until_date": "Bis {date}" }, "settings": { + "device_sync_desc": "Zeigt Ihre Kalender in der Kalender-App und in anderen Apps auf diesem Gerät an. Änderungen werden in beide Richtungen synchronisiert.", + "device_sync_title": "Mit diesem Gerät synchronisieren", "show_week_numbers_mobile_desc": "Kalenderwochen in der Monatsansicht anzeigen.", "time_zone": "Zeitzone", "time_zone_auto": "Gerätezeitzone", @@ -249,6 +251,18 @@ "color_tag": "Label", "remove_color": "Label entfernen" }, + "device_sync": { + "auth_problem_text": "Der Server akzeptiert die Anmeldung von {account} nicht mehr, daher werden die Kontakte und Kalender dieses Kontos auf diesem Gerät nicht mehr synchronisiert. Melden Sie sich erneut an, um fortzufahren. Auf diesem Gerät vorgenommene Änderungen bleiben erhalten.", + "auth_problem_title": "Erneut anmelden, um weiter zu synchronisieren", + "problem_channel": "Synchronisierungsprobleme", + "sign_out_anyway": "Abmelden", + "sign_out_pending_message": "{count, plural, one {# auf diesem Gerät vorgenommene Änderung hat den Server nicht erreicht. Sie geht verloren, wenn Sie sich jetzt abmelden.} other {# auf diesem Gerät vorgenommene Änderungen haben den Server nicht erreicht. Sie gehen verloren, wenn Sie sich jetzt abmelden.}}", + "sign_out_pending_title": "Trotzdem abmelden?", + "sign_out_unknown_message": "Auf diesem Gerät vorgenommene Änderungen haben den Server möglicherweise nicht erreicht. Sie gehen verloren, wenn Sie sich jetzt abmelden.", + "signed_out_text": "{account} wurde abgemeldet, daher werden die Kontakte und Kalender dieses Kontos auf diesem Gerät nicht mehr synchronisiert. Melden Sie sich erneut an, um fortzufahren. Auf diesem Gerät vorgenommene Änderungen bleiben erhalten.", + "signed_out_title": "Anmelden, um weiter zu synchronisieren", + "signing_out": "Synchronisierte Kontakte und Kalender werden von diesem Gerät entfernt…" + }, "email_composer": { "attach_camera": "Kamera", "attachment_reminder_body": "Ihre Nachricht erwähnt \"{keyword}\", aber es ist keine Datei angehängt. Trotzdem senden?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# Kontakt} other {# Kontakte}}", "default_book": "Standardadressbuch", "description_mobile": "Anzeigeeinstellungen und Adressbuch-Werkzeuge.", + "device_sync_desc": "Zeigt Ihre Adressbücher in der Kontakte-App und in anderen Apps auf diesem Gerät an. Änderungen werden in beide Richtungen synchronisiert.", + "device_sync_title": "Mit diesem Gerät synchronisieren", "export_description_count": "{count, plural, one {# Kontakt als eine vCard-Datei exportieren.} other {# Kontakte als eine vCard-Datei exportieren.}}", "export_empty": "Keine Kontakte zum Exportieren", "import_description_mobile": "Kontakte aus einer vCard-Datei (.vcf) importieren, mit Duplikaterkennung.", "manage_description_mobile": "Die Adressbücher erstellen, umbenennen und entfernen, in denen Ihre Kontakte organisiert sind." }, + "device_sync": { + "also_synced_by": "Wird auch für {accounts} synchronisiert und erscheint daher doppelt auf diesem Gerät.", + "android_settings": "Android-Kontoeinstellungen", + "blocked_calendar": "Android fragt nicht mehr nach dem Zugriff auf Kalender. Erlauben Sie ihn in den App-Einstellungen und schalten Sie die Synchronisierung dann wieder ein.", + "blocked_contacts": "Android fragt nicht mehr nach dem Zugriff auf Kontakte. Erlauben Sie ihn in den App-Einstellungen und schalten Sie die Synchronisierung dann wieder ein.", + "blocked_title": "Zugriff ist ausgeschaltet", + "books_default": "Ihre eigenen Adressbücher werden synchronisiert, geteilte nur, wenn Sie sie auswählen.", + "books_hint": "Ihre eigenen Adressbücher werden synchronisiert, außer Sie schalten sie aus; geteilte nur, wenn Sie sie einschalten.", + "books_label": "Adressbücher", + "calendars_default": "Ihre eigenen Kalender werden synchronisiert, geteilte nur, wenn Sie sie auswählen.", + "calendars_hint": "Ihre eigenen Kalender werden synchronisiert, außer Sie schalten sie aus; geteilte nur, wenn Sie sie einschalten. Aufgabenlisten bleiben in Bulwark.", + "calendars_label": "Kalender", + "choose": "Auswählen", + "choose_books_title": "Zu synchronisierende Adressbücher", + "choose_calendars_title": "Zu synchronisierende Kalender", + "collections_count": "{selected} von {total} synchronisiert", + "continue": "Weiter", + "deletions_delete": "Auf dem Server löschen", + "deletions_message": "Auf diesem Gerät wurden viele Elemente auf einmal gelöscht, daher wurden sie auf dem Server nicht gelöscht. Löschen Sie sie auch dort oder stellen Sie sie auf diesem Gerät wieder her.", + "deletions_restore": "Wiederherstellen", + "deletions_title": "Auch auf dem Server löschen?", + "denied_calendar": "Ohne Zugriff auf Kalender kann Bulwark Ihre Kalender nicht auf dieses Gerät bringen.", + "denied_contacts": "Ohne Zugriff auf Kontakte kann Bulwark Ihre Adressbücher nicht auf dieses Gerät bringen.", + "denied_title": "Synchronisierung bleibt aus", + "failed_title": "Synchronisierung konnte nicht eingeschaltet werden", + "grant_access": "Zugriff erlauben", + "interval_15m": "Alle 15 Minuten", + "interval_1h": "Jede Stunde", + "interval_30m": "Alle 30 Minuten", + "interval_6h": "Alle 6 Stunden", + "interval_desc": "Wie oft Android von selbst synchronisiert. Änderungen in Bulwark und per Push gemeldete Änderungen werden früher synchronisiert.", + "interval_label": "Synchronisierungsintervall", + "interval_manual": "Keine regelmäßige Synchronisierung", + "keep_syncing": "Weiter synchronisieren", + "list_error_auth": "Der Server hat die Anmeldung nicht akzeptiert.", + "list_error_network": "Der Server war nicht erreichbar.", + "list_error_server": "Der Server konnte sie nicht auflisten.", + "list_error_signed_out": "Dieses Konto ist auf diesem Gerät nicht angemeldet.", + "master_off": "Die automatische Synchronisierung ist auf diesem Gerät ausgeschaltet, daher wird nur synchronisiert, wenn Sie auf \"Jetzt synchronisieren\" tippen.", + "new_contacts_default": "Standardadressbuch", + "new_contacts_desc": "Das Adressbuch für Kontakte, die in anderen Apps auf diesem Gerät erstellt werden.", + "new_contacts_label": "Neue Kontakte speichern in", + "no_books": "Dieses Konto hat keine Adressbücher.", + "no_calendars": "Dieses Konto hat keine Kalender zum Synchronisieren.", + "ok": "OK", + "open_app_settings": "App-Einstellungen öffnen", + "own_collections": "Ihre eigenen", + "rationale_calendar": "Um Ihre Kalender in der Kalender-App anzuzeigen, benötigt Bulwark Zugriff auf die Kalender auf diesem Gerät. Bulwark ändert nur die Kalender seines eigenen Kontos.", + "rationale_contacts": "Um Ihre Adressbücher in der Kontakte-App anzuzeigen, benötigt Bulwark Zugriff auf die Kontakte auf diesem Gerät. Bulwark ändert nur die Kontakte seines eigenen Kontos.", + "rationale_title_calendar": "Zugriff auf Kalender erlauben", + "rationale_title_contacts": "Zugriff auf Kontakte erlauben", + "read_only": "Schreibgeschützt", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Kalender-App", + "reminder_question_message": "Synchronisierte Termine können Sie über die Kalender-App auf diesem Gerät oder über Bulwark erinnern. Wählen Sie eines davon, damit Sie nicht doppelt erinnert werden. Sie können dies später ändern.", + "reminder_question_title": "Wer soll Sie erinnern?", + "reminders_desc": "Wer Sie an synchronisierte Termine erinnert. Die jeweils andere App bleibt still, damit Sie nicht doppelt erinnert werden.", + "reminders_label": "Erinnerungen", + "reminders_nobody": "Die Terminbenachrichtigungen von Bulwark sind ausgeschaltet, daher erinnert Sie nichts an synchronisierte Termine.", + "remove": "Entfernen", + "remove_from_device": "Von diesem Gerät entfernen", + "remove_message": "Die Kontakte und Kalender des Kontos werden von diesem Gerät entfernt. Hier vorgenommene Änderungen, die den Server noch nicht erreicht haben, gehen verloren.", + "remove_title": "Von diesem Gerät entfernen?", + "review_deletions": "Löschungen prüfen", + "shared_by": "Von {name} geteilt", + "sign_in_again": "Erneut anmelden", + "status_auth": "Der Server akzeptiert die Anmeldung nicht mehr. Melden Sie sich erneut an, um weiter zu synchronisieren.", + "status_cancelled": "Die letzte Synchronisierung wurde vorzeitig beendet. Sie wird beim nächsten Mal fortgesetzt.", + "status_failed": "Die letzte Synchronisierung ist fehlgeschlagen: {message}", + "status_failed_plain": "Die letzte Synchronisierung ist fehlgeschlagen.", + "status_io": "Der Server war {when} nicht erreichbar. Android versucht es erneut.", + "status_item_errors": "{count, plural, one {# Element konnte nicht synchronisiert werden.} other {# Elemente konnten nicht synchronisiert werden.}}", + "status_off": "Aus", + "status_paused": "In den Android-Einstellungen pausiert", + "status_permission_calendar": "Der Zugriff auf Kalender wurde ausgeschaltet, daher wird nichts synchronisiert.", + "status_permission_contacts": "Der Zugriff auf Kontakte wurde ausgeschaltet, daher wird nichts synchronisiert.", + "status_removed": "In den Android-Einstellungen entfernt. Schalten Sie die Synchronisierung ein, um das Konto wieder hinzuzufügen.", + "status_safety_abort": "Angehalten: Der Server hat nichts zurückgegeben, obwohl dieses Gerät noch Elemente enthält. Prüfen Sie den Server und synchronisieren Sie dann erneut.", + "status_synced": "Zuletzt synchronisiert: {when}", + "status_syncing": "Wird synchronisiert…", + "status_too_many_deletions": "Auf diesem Gerät wurden viele Elemente gelöscht. Auf dem Server werden sie erst gelöscht, wenn Sie dies bestätigen.", + "status_unsupported_calendar": "Der Server bietet für dieses Konto keine Kalender an.", + "status_unsupported_contacts": "Der Server bietet für dieses Konto keine Adressbücher an.", + "status_waiting": "Warten auf die erste Synchronisierung", + "status_waiting_next": "Warten auf die nächste Synchronisierung", + "suspended_desc": "Abgemeldet. Die Kontakte und Kalender des Kontos bleiben mit den hier vorgenommenen Änderungen auf diesem Gerät und werden wieder synchronisiert, sobald Sie sich anmelden.", + "switch_label": "{account} mit diesem Gerät synchronisieren", + "sync_now": "Jetzt synchronisieren", + "turn_off_anyway": "Ausschalten", + "turn_off_failed": "Die synchronisierten Daten konnten nicht von diesem Gerät entfernt werden. Die Synchronisierung ist pausiert; versuchen Sie es später erneut.", + "turn_off_failed_title": "Synchronisierung konnte nicht ausgeschaltet werden", + "turn_off_pending": "{count, plural, one {# auf diesem Gerät vorgenommene Änderung hat den Server noch nicht erreicht. Sie geht verloren, wenn Sie die Synchronisierung jetzt ausschalten.} other {# auf diesem Gerät vorgenommene Änderungen haben den Server noch nicht erreicht. Sie gehen verloren, wenn Sie die Synchronisierung jetzt ausschalten.}}", + "turn_off_title": "Trotzdem ausschalten?", + "turn_off_unknown": "Bulwark konnte nicht prüfen, ob auf diesem Gerät vorgenommene Änderungen den Server erreicht haben. Sie gehen verloren, wenn Sie die Synchronisierung jetzt ausschalten.", + "turning_off": "Änderungen werden hochgeladen, dann werden die Daten von diesem Gerät entfernt…", + "turning_on": "Wird eingerichtet…" + }, "downloads": { "attachment_template": { "description_mobile": "Vorlage für das Speichern oder Teilen eines Anhangs." diff --git a/locales/rn/es.json b/locales/rn/es.json index b601d8a3..f0f42034 100644 --- a/locales/rn/es.json +++ b/locales/rn/es.json @@ -93,6 +93,8 @@ "until_date": "Hasta el {date}" }, "settings": { + "device_sync_desc": "Muestra tus calendarios en la app Calendario y en otras apps de este dispositivo. Los cambios se sincronizan en ambos sentidos.", + "device_sync_title": "Sincronizar con este dispositivo", "show_week_numbers_mobile_desc": "Mostrar los números de semana en la vista mensual.", "time_zone": "Zona horaria", "time_zone_auto": "Zona horaria del dispositivo", @@ -249,6 +251,18 @@ "color_tag": "Etiqueta", "remove_color": "Eliminar etiqueta" }, + "device_sync": { + "auth_problem_text": "El servidor ya no acepta el inicio de sesión de {account}, así que sus contactos y calendarios de este dispositivo han dejado de sincronizarse. Vuelve a iniciar sesión para reanudar la sincronización. Los cambios hechos en este dispositivo se conservan.", + "auth_problem_title": "Vuelve a iniciar sesión para seguir sincronizando", + "problem_channel": "Problemas de sincronización", + "sign_out_anyway": "Cerrar sesión", + "sign_out_pending_message": "{count, plural, one {# cambio hecho en este dispositivo no ha llegado al servidor. Se perderá si cierras sesión ahora.} other {# cambios hechos en este dispositivo no han llegado al servidor. Se perderán si cierras sesión ahora.}}", + "sign_out_pending_title": "¿Cerrar sesión de todos modos?", + "sign_out_unknown_message": "Puede que los cambios hechos en este dispositivo no hayan llegado al servidor. Se perderán si cierras sesión ahora.", + "signed_out_text": "Se cerró la sesión de {account}, así que sus contactos y calendarios de este dispositivo ya no se sincronizan. Vuelve a iniciar sesión para reanudar la sincronización. Los cambios hechos en este dispositivo se conservan.", + "signed_out_title": "Inicia sesión para seguir sincronizando", + "signing_out": "Quitando de este dispositivo los contactos y calendarios sincronizados…" + }, "email_composer": { "attach_camera": "Cámara", "attachment_reminder_body": "Tu mensaje menciona \"{keyword}\" pero no hay ningún archivo adjunto. ¿Enviar de todos modos?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# contacto} other {# contactos}}", "default_book": "Libreta de direcciones predeterminada", "description_mobile": "Preferencias de visualización y herramientas de la libreta de direcciones.", + "device_sync_desc": "Muestra tus libretas de direcciones en la app Contactos y en otras apps de este dispositivo. Los cambios se sincronizan en ambos sentidos.", + "device_sync_title": "Sincronizar con este dispositivo", "export_description_count": "{count, plural, one {Exporta # contacto como un único archivo vCard.} other {Exporta # contactos como un único archivo vCard.}}", "export_empty": "No hay contactos para exportar", "import_description_mobile": "Importa contactos desde un archivo vCard (.vcf) con detección de duplicados.", "manage_description_mobile": "Crea, renombra y elimina las libretas de direcciones que organizan tus contactos." }, + "device_sync": { + "also_synced_by": "También se sincroniza para {accounts}, por lo que aparece dos veces en este dispositivo.", + "android_settings": "Ajustes de cuenta de Android", + "blocked_calendar": "Android ya no pide acceso a los calendarios. Permítelo en los ajustes de la app y vuelve a activar la sincronización.", + "blocked_contacts": "Android ya no pide acceso a los contactos. Permítelo en los ajustes de la app y vuelve a activar la sincronización.", + "blocked_title": "El acceso está desactivado", + "books_default": "Tus libretas de direcciones se sincronizan; las compartidas, solo si las eliges.", + "books_hint": "Tus libretas de direcciones se sincronizan salvo que las desactives; las compartidas, solo si las activas.", + "books_label": "Libretas de direcciones", + "calendars_default": "Tus calendarios se sincronizan; los compartidos, solo si los eliges.", + "calendars_hint": "Tus calendarios se sincronizan salvo que los desactives; los compartidos, solo si los activas. Las listas de tareas se quedan en Bulwark.", + "calendars_label": "Calendarios", + "choose": "Elegir", + "choose_books_title": "Libretas de direcciones que sincronizar", + "choose_calendars_title": "Calendarios que sincronizar", + "collections_count": "Sincronización: {selected} de {total}", + "continue": "Continuar", + "deletions_delete": "Eliminar en el servidor", + "deletions_message": "Se eliminaron muchos elementos a la vez en este dispositivo, así que no se eliminaron en el servidor. Elimínalos también allí o recupéralos en este dispositivo.", + "deletions_restore": "Recuperarlos", + "deletions_title": "¿Eliminarlos también en el servidor?", + "denied_calendar": "Sin acceso a los calendarios, Bulwark no puede poner tus calendarios en este dispositivo.", + "denied_contacts": "Sin acceso a los contactos, Bulwark no puede poner tus libretas de direcciones en este dispositivo.", + "denied_title": "La sincronización sigue desactivada", + "failed_title": "No se pudo activar la sincronización", + "grant_access": "Conceder acceso", + "interval_15m": "Cada 15 minutos", + "interval_1h": "Cada hora", + "interval_30m": "Cada 30 minutos", + "interval_6h": "Cada 6 horas", + "interval_desc": "Con qué frecuencia sincroniza Android por su cuenta. Los cambios hechos en Bulwark y los que llegan por push se sincronizan antes.", + "interval_label": "Intervalo de sincronización", + "interval_manual": "Sin sincronización periódica", + "keep_syncing": "Seguir sincronizando", + "list_error_auth": "El servidor no aceptó el inicio de sesión.", + "list_error_network": "No se pudo conectar con el servidor.", + "list_error_server": "El servidor no pudo mostrar la lista.", + "list_error_signed_out": "Esta cuenta no tiene la sesión iniciada en este dispositivo.", + "master_off": "La sincronización automática está desactivada en este dispositivo, así que solo se sincroniza cuando tocas \"Sincronizar ahora\".", + "new_contacts_default": "Libreta de direcciones predeterminada", + "new_contacts_desc": "La libreta de direcciones para los contactos creados en otras apps de este dispositivo.", + "new_contacts_label": "Guardar contactos nuevos en", + "no_books": "Esta cuenta no tiene libretas de direcciones.", + "no_calendars": "Esta cuenta no tiene calendarios que sincronizar.", + "ok": "Aceptar", + "open_app_settings": "Abrir ajustes de la app", + "own_collections": "Personales", + "rationale_calendar": "Para mostrar tus calendarios en la app Calendario, Bulwark necesita acceso a los calendarios de este dispositivo. Solo modifica los calendarios de su propia cuenta.", + "rationale_contacts": "Para mostrar tus libretas de direcciones en la app Contactos, Bulwark necesita acceso a los contactos de este dispositivo. Solo modifica los contactos de su propia cuenta.", + "rationale_title_calendar": "Permitir acceso a los calendarios", + "rationale_title_contacts": "Permitir acceso a los contactos", + "read_only": "Solo lectura", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "App Calendario", + "reminder_question_message": "Los recordatorios de los eventos sincronizados pueden venir de la app de calendario de este dispositivo o de Bulwark. Elige una opción para no recibirlos dos veces. Puedes cambiarlo más tarde.", + "reminder_question_title": "¿Quién debe avisarte?", + "reminders_desc": "Quién te avisa de los eventos sincronizados. El otro se queda en silencio para que no recibas dos avisos.", + "reminders_label": "Recordatorios", + "reminders_nobody": "Las notificaciones de eventos de Bulwark están desactivadas, así que nada te avisará de los eventos sincronizados.", + "remove": "Quitar", + "remove_from_device": "Quitar de este dispositivo", + "remove_message": "Los contactos y calendarios de la cuenta se quitarán de este dispositivo. Los cambios hechos aquí que no hayan llegado al servidor se perderán.", + "remove_title": "¿Quitar de este dispositivo?", + "review_deletions": "Revisar eliminaciones", + "shared_by": "Compartido por {name}", + "sign_in_again": "Volver a iniciar sesión", + "status_auth": "El servidor ya no acepta el inicio de sesión. Vuelve a iniciar sesión para seguir sincronizando.", + "status_cancelled": "La última sincronización se detuvo antes de tiempo. Continuará la próxima vez.", + "status_failed": "La última sincronización falló: {message}", + "status_failed_plain": "La última sincronización falló.", + "status_io": "No se pudo conectar con el servidor {when}. Android lo volverá a intentar.", + "status_item_errors": "{count, plural, one {No se pudo sincronizar # elemento.} other {No se pudieron sincronizar # elementos.}}", + "status_off": "Desactivada", + "status_paused": "En pausa en los ajustes de Android", + "status_permission_calendar": "Se desactivó el acceso a los calendarios, así que no se sincroniza nada.", + "status_permission_contacts": "Se desactivó el acceso a los contactos, así que no se sincroniza nada.", + "status_removed": "Se quitó en los ajustes de Android. Activa la sincronización para volver a añadir la cuenta.", + "status_safety_abort": "Detenida: el servidor no devolvió nada, aunque este dispositivo aún tiene elementos. Revisa el servidor y vuelve a sincronizar.", + "status_synced": "Sincronizado {when}", + "status_syncing": "Sincronizando…", + "status_too_many_deletions": "Se eliminaron muchos elementos en este dispositivo. No se eliminarán en el servidor hasta que lo confirmes.", + "status_unsupported_calendar": "El servidor no ofrece calendarios para esta cuenta.", + "status_unsupported_contacts": "El servidor no ofrece libretas de direcciones para esta cuenta.", + "status_waiting": "Esperando la primera sincronización", + "status_waiting_next": "Esperando la próxima sincronización", + "suspended_desc": "Sesión cerrada. Sus contactos y calendarios se quedan en este dispositivo, con los cambios hechos aquí, y se volverán a sincronizar cuando inicies sesión.", + "switch_label": "Sincronizar {account} con este dispositivo", + "sync_now": "Sincronizar ahora", + "turn_off_anyway": "Desactivar", + "turn_off_failed": "No se pudieron quitar los datos sincronizados de este dispositivo. La sincronización está en pausa; inténtalo de nuevo más tarde.", + "turn_off_failed_title": "No se pudo desactivar la sincronización", + "turn_off_pending": "{count, plural, one {# cambio hecho en este dispositivo aún no ha llegado al servidor. Se perderá si desactivas la sincronización ahora.} other {# cambios hechos en este dispositivo aún no han llegado al servidor. Se perderán si desactivas la sincronización ahora.}}", + "turn_off_title": "¿Desactivar de todos modos?", + "turn_off_unknown": "Bulwark no pudo comprobar si los cambios hechos en este dispositivo llegaron al servidor. Se perderán si desactivas la sincronización ahora.", + "turning_off": "Subiendo los cambios y quitando después los datos de este dispositivo…", + "turning_on": "Configurando…" + }, "downloads": { "attachment_template": { "description_mobile": "Plantilla usada al guardar o compartir un adjunto." diff --git a/locales/rn/fa.json b/locales/rn/fa.json index e5afa8f0..19841e58 100644 --- a/locales/rn/fa.json +++ b/locales/rn/fa.json @@ -93,6 +93,8 @@ "until_date": "تا {date}" }, "settings": { + "device_sync_desc": "تقویم‌های خود را در برنامه تقویم و برنامه‌های دیگر این دستگاه نشان دهید. تغییرات در هر دو جهت همگام‌سازی می‌شوند.", + "device_sync_title": "همگام‌سازی با این دستگاه", "show_week_numbers_mobile_desc": "نمایش شماره هفته‌ها در نمای ماه.", "time_zone": "منطقه زمانی", "time_zone_auto": "منطقه زمانی دستگاه", @@ -249,6 +251,18 @@ "color_tag": "برچسب", "remove_color": "حذف برچسب" }, + "device_sync": { + "auth_problem_text": "سرور دیگر ورود {account} را نمی‌پذیرد، بنابراین همگام‌سازی مخاطبین و تقویم‌های این حساب در این دستگاه متوقف شد. برای ادامه، دوباره وارد شوید. تغییراتی که در این دستگاه ایجاد شده‌اند حفظ می‌شوند.", + "auth_problem_title": "برای ادامه همگام‌سازی دوباره وارد شوید", + "problem_channel": "مشکلات همگام‌سازی", + "sign_out_anyway": "خروج", + "sign_out_pending_message": "{count, plural, one {# تغییری که در این دستگاه ایجاد شده به سرور نرسیده است. اگر اکنون خارج شوید، از دست می‌رود.} other {# تغییری که در این دستگاه ایجاد شده به سرور نرسیده است. اگر اکنون خارج شوید، از دست می‌روند.}}", + "sign_out_pending_title": "به هر حال خارج می‌شوید؟", + "sign_out_unknown_message": "ممکن است تغییراتی که در این دستگاه ایجاد شده‌اند به سرور نرسیده باشند. اگر اکنون خارج شوید، از دست می‌روند.", + "signed_out_text": "خروج از {account} انجام شد، بنابراین مخاطبین و تقویم‌های این حساب در این دستگاه دیگر همگام‌سازی نمی‌شوند. برای ادامه، دوباره وارد شوید. تغییراتی که در این دستگاه ایجاد شده‌اند حفظ می‌شوند.", + "signed_out_title": "برای ادامه همگام‌سازی وارد شوید", + "signing_out": "در حال حذف مخاطبین و تقویم‌های همگام‌سازی‌شده از این دستگاه…" + }, "email_composer": { "attach_camera": "دوربین", "attachment_reminder_body": "در پیام شما به «{keyword}» اشاره شده اما فایلی پیوست نشده است. با این حال ارسال شود؟", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# مخاطب} other {# مخاطب}}", "default_book": "دفترچه آدرس پیش‌فرض", "description_mobile": "تنظیمات نمایش و ابزارهای دفترچه آدرس.", + "device_sync_desc": "دفترچه‌های آدرس خود را در برنامه مخاطبین و برنامه‌های دیگر این دستگاه نشان دهید. تغییرات در هر دو جهت همگام‌سازی می‌شوند.", + "device_sync_title": "همگام‌سازی با این دستگاه", "export_description_count": "{count, plural, one {خروجی گرفتن از # مخاطب در قالب یک فایل vCard.} other {خروجی گرفتن از # مخاطب در قالب یک فایل vCard.}}", "export_empty": "مخاطبی برای خروجی گرفتن نیست", "import_description_mobile": "وارد کردن مخاطبین از فایل vCard (.vcf) با تشخیص موارد تکراری.", "manage_description_mobile": "دفترچه‌های آدرسی را که مخاطبین شما را سازمان‌دهی می‌کنند ایجاد، تغییر نام و حذف کنید." }, + "device_sync": { + "also_synced_by": "برای {accounts} هم همگام‌سازی می‌شود، به همین دلیل در این دستگاه دو بار نمایش داده می‌شود.", + "android_settings": "تنظیمات حساب Android", + "blocked_calendar": "Android دیگر برای دسترسی به تقویم‌ها درخواست نمی‌دهد. آن را در تنظیمات برنامه مجاز کنید، سپس همگام‌سازی را دوباره روشن کنید.", + "blocked_contacts": "Android دیگر برای دسترسی به مخاطبین درخواست نمی‌دهد. آن را در تنظیمات برنامه مجاز کنید، سپس همگام‌سازی را دوباره روشن کنید.", + "blocked_title": "دسترسی خاموش است", + "books_default": "دفترچه‌های آدرس خودتان همگام‌سازی می‌شوند؛ دفترچه‌های اشتراکی فقط وقتی آن‌ها را انتخاب کنید.", + "books_hint": "دفترچه‌های آدرس خودتان همگام‌سازی می‌شوند، مگر اینکه آن‌ها را خاموش کنید؛ دفترچه‌های اشتراکی فقط وقتی آن‌ها را روشن کنید.", + "books_label": "دفترچه‌های آدرس", + "calendars_default": "تقویم‌های خودتان همگام‌سازی می‌شوند؛ تقویم‌های اشتراکی فقط وقتی آن‌ها را انتخاب کنید.", + "calendars_hint": "تقویم‌های خودتان همگام‌سازی می‌شوند، مگر اینکه آن‌ها را خاموش کنید؛ تقویم‌های اشتراکی فقط وقتی آن‌ها را روشن کنید. فهرست‌های وظایف در Bulwark می‌مانند.", + "calendars_label": "تقویم‌ها", + "choose": "انتخاب", + "choose_books_title": "دفترچه‌های آدرس برای همگام‌سازی", + "choose_calendars_title": "تقویم‌ها برای همگام‌سازی", + "collections_count": "{selected} از {total} همگام‌سازی می‌شوند", + "continue": "ادامه", + "deletions_delete": "حذف در سرور", + "deletions_message": "موارد زیادی یکجا در این دستگاه حذف شدند، بنابراین در سرور حذف نشدند. آن‌ها را در سرور هم حذف کنید یا در این دستگاه بازگردانید.", + "deletions_restore": "بازگرداندن", + "deletions_title": "در سرور هم حذف شوند؟", + "denied_calendar": "بدون دسترسی به تقویم‌ها، Bulwark نمی‌تواند تقویم‌های شما را در این دستگاه قرار دهد.", + "denied_contacts": "بدون دسترسی به مخاطبین، Bulwark نمی‌تواند دفترچه‌های آدرس شما را در این دستگاه قرار دهد.", + "denied_title": "همگام‌سازی خاموش می‌ماند", + "failed_title": "روشن کردن همگام‌سازی ممکن نشد", + "grant_access": "اعطای دسترسی", + "interval_15m": "هر 15 دقیقه", + "interval_1h": "هر ساعت", + "interval_30m": "هر 30 دقیقه", + "interval_6h": "هر 6 ساعت", + "interval_desc": "اینکه Android هر چند وقت یک بار خودش همگام‌سازی کند. ویرایش‌ها در Bulwark و تغییراتی که با push اعلام می‌شوند زودتر همگام‌سازی می‌شوند.", + "interval_label": "فاصله همگام‌سازی", + "interval_manual": "بدون همگام‌سازی منظم", + "keep_syncing": "ادامه همگام‌سازی", + "list_error_auth": "سرور ورود را نپذیرفت.", + "list_error_network": "اتصال به سرور ممکن نشد.", + "list_error_server": "سرور نتوانست فهرست را ارائه کند.", + "list_error_signed_out": "در این دستگاه به این حساب وارد نشده‌اید.", + "master_off": "همگام‌سازی خودکار در این دستگاه خاموش است، بنابراین همگام‌سازی فقط وقتی انجام می‌شود که روی «همگام‌سازی اکنون» ضربه بزنید.", + "new_contacts_default": "دفترچه آدرس پیش‌فرض", + "new_contacts_desc": "دفترچه آدرس برای مخاطبینی که در برنامه‌های دیگر این دستگاه ایجاد می‌شوند.", + "new_contacts_label": "محل ذخیره مخاطبین جدید", + "no_books": "این حساب هیچ دفترچه آدرسی ندارد.", + "no_calendars": "این حساب هیچ تقویمی برای همگام‌سازی ندارد.", + "ok": "تأیید", + "open_app_settings": "باز کردن تنظیمات برنامه", + "own_collections": "متعلق به شما", + "rationale_calendar": "Bulwark برای نمایش تقویم‌های شما در برنامه تقویم به تقویم‌های این دستگاه دسترسی نیاز دارد. فقط تقویم‌های حساب خودش را تغییر می‌دهد.", + "rationale_contacts": "Bulwark برای نمایش دفترچه‌های آدرس شما در برنامه مخاطبین به مخاطبین این دستگاه دسترسی نیاز دارد. فقط مخاطبین حساب خودش را تغییر می‌دهد.", + "rationale_title_calendar": "اجازه دسترسی به تقویم‌ها", + "rationale_title_contacts": "اجازه دسترسی به مخاطبین", + "read_only": "فقط خواندنی", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "برنامه تقویم", + "reminder_question_message": "یادآور رویدادهای همگام‌سازی‌شده می‌تواند از برنامه تقویم این دستگاه یا از Bulwark بیاید. یکی را انتخاب کنید تا دو بار یادآوری دریافت نکنید. بعداً می‌توانید آن را تغییر دهید.", + "reminder_question_title": "چه کسی به شما یادآوری کند؟", + "reminders_desc": "چه کسی رویدادهای همگام‌سازی‌شده را به شما یادآوری می‌کند. دیگری ساکت می‌ماند تا دو بار یادآوری دریافت نکنید.", + "reminders_label": "یادآورها", + "reminders_nobody": "اعلان‌های رویداد Bulwark خاموش است، بنابراین هیچ چیز رویدادهای همگام‌سازی‌شده را به شما یادآوری نمی‌کند.", + "remove": "حذف", + "remove_from_device": "حذف از این دستگاه", + "remove_message": "مخاطبین و تقویم‌های این حساب از این دستگاه حذف می‌شوند. تغییراتی که اینجا ایجاد شده و به سرور نرسیده‌اند از دست می‌روند.", + "remove_title": "از این دستگاه حذف شود؟", + "review_deletions": "بررسی حذف‌ها", + "shared_by": "به اشتراک گذاشته شده توسط {name}", + "sign_in_again": "ورود دوباره", + "status_auth": "سرور دیگر ورود را نمی‌پذیرد. برای ادامه همگام‌سازی دوباره وارد شوید.", + "status_cancelled": "آخرین همگام‌سازی زودتر متوقف شد. دفعه بعد ادامه می‌یابد.", + "status_failed": "آخرین همگام‌سازی ناموفق بود: {message}", + "status_failed_plain": "آخرین همگام‌سازی ناموفق بود.", + "status_io": "اتصال به سرور ممکن نشد ({when}). Android دوباره تلاش می‌کند.", + "status_item_errors": "{count, plural, one {# مورد همگام‌سازی نشد.} other {# مورد همگام‌سازی نشد.}}", + "status_off": "خاموش", + "status_paused": "در تنظیمات Android متوقف شده", + "status_permission_calendar": "دسترسی به تقویم‌ها خاموش شد، بنابراین چیزی همگام‌سازی نمی‌شود.", + "status_permission_contacts": "دسترسی به مخاطبین خاموش شد، بنابراین چیزی همگام‌سازی نمی‌شود.", + "status_removed": "در تنظیمات Android حذف شد. برای افزودن دوباره حساب، همگام‌سازی را روشن کنید.", + "status_safety_abort": "متوقف شد: سرور چیزی برنگرداند، در حالی که این دستگاه هنوز موارد دارد. سرور را بررسی کنید، سپس دوباره همگام‌سازی کنید.", + "status_synced": "آخرین همگام‌سازی: {when}", + "status_syncing": "در حال همگام‌سازی…", + "status_too_many_deletions": "موارد زیادی در این دستگاه حذف شدند. تا وقتی تأیید نکنید، در سرور حذف نمی‌شوند.", + "status_unsupported_calendar": "سرور هیچ تقویمی برای این حساب ارائه نمی‌کند.", + "status_unsupported_contacts": "سرور هیچ دفترچه آدرسی برای این حساب ارائه نمی‌کند.", + "status_waiting": "در انتظار نخستین همگام‌سازی", + "status_waiting_next": "در انتظار همگام‌سازی بعدی", + "suspended_desc": "خارج شده. مخاطبین و تقویم‌های این حساب همراه با تغییراتی که اینجا ایجاد شده در این دستگاه می‌مانند و پس از ورود دوباره همگام‌سازی می‌شوند.", + "switch_label": "همگام‌سازی {account} با این دستگاه", + "sync_now": "همگام‌سازی اکنون", + "turn_off_anyway": "خاموش کردن", + "turn_off_failed": "حذف داده‌های همگام‌سازی‌شده از این دستگاه ممکن نشد. همگام‌سازی متوقف شده است؛ بعداً دوباره تلاش کنید.", + "turn_off_failed_title": "خاموش کردن همگام‌سازی ممکن نشد", + "turn_off_pending": "{count, plural, one {# تغییری که در این دستگاه ایجاد شده هنوز به سرور نرسیده است. اگر اکنون همگام‌سازی را خاموش کنید، از دست می‌رود.} other {# تغییری که در این دستگاه ایجاد شده هنوز به سرور نرسیده است. اگر اکنون همگام‌سازی را خاموش کنید، از دست می‌روند.}}", + "turn_off_title": "به هر حال خاموش شود؟", + "turn_off_unknown": "Bulwark نتوانست بررسی کند که آیا تغییراتی که در این دستگاه ایجاد شده‌اند به سرور رسیده‌اند یا نه. اگر اکنون همگام‌سازی را خاموش کنید، از دست می‌روند.", + "turning_off": "در حال بارگذاری تغییرات و سپس حذف داده‌ها از این دستگاه…", + "turning_on": "در حال راه‌اندازی…" + }, "downloads": { "attachment_template": { "description_mobile": "الگویی که هنگام ذخیره یا اشتراک‌گذاری پیوست استفاده می‌شود." diff --git a/locales/rn/fr.json b/locales/rn/fr.json index a58dbf6d..2577ac4a 100644 --- a/locales/rn/fr.json +++ b/locales/rn/fr.json @@ -93,6 +93,8 @@ "until_date": "Jusqu'au {date}" }, "settings": { + "device_sync_desc": "Affichez vos calendriers dans l'application Agenda et dans les autres applications de cet appareil. Les modifications sont synchronisées dans les deux sens.", + "device_sync_title": "Synchroniser avec cet appareil", "show_week_numbers_mobile_desc": "Afficher les numéros de semaine dans la vue mensuelle.", "time_zone": "Fuseau horaire", "time_zone_auto": "Fuseau horaire de l'appareil", @@ -249,6 +251,18 @@ "color_tag": "Étiquette", "remove_color": "Retirer l'étiquette" }, + "device_sync": { + "auth_problem_text": "Le serveur n'accepte plus la connexion de {account} : ses contacts et calendriers ne sont donc plus synchronisés sur cet appareil. Reconnectez-vous pour reprendre la synchronisation. Les modifications effectuées sur cet appareil sont conservées.", + "auth_problem_title": "Reconnectez-vous pour continuer la synchronisation", + "problem_channel": "Problèmes de synchronisation", + "sign_out_anyway": "Se déconnecter", + "sign_out_pending_message": "{count, plural, one {# modification effectuée sur cet appareil n'a pas atteint le serveur. Elle sera perdue si vous vous déconnectez maintenant.} other {# modifications effectuées sur cet appareil n'ont pas atteint le serveur. Elles seront perdues si vous vous déconnectez maintenant.}}", + "sign_out_pending_title": "Se déconnecter quand même ?", + "sign_out_unknown_message": "Les modifications effectuées sur cet appareil n'ont peut-être pas atteint le serveur. Elles seront perdues si vous vous déconnectez maintenant.", + "signed_out_text": "{account} a été déconnecté : ses contacts et calendriers ne sont donc plus synchronisés sur cet appareil. Reconnectez-vous pour reprendre la synchronisation. Les modifications effectuées sur cet appareil sont conservées.", + "signed_out_title": "Connectez-vous pour continuer la synchronisation", + "signing_out": "Suppression des contacts et calendriers synchronisés de cet appareil…" + }, "email_composer": { "attach_camera": "Appareil photo", "attachment_reminder_body": "Votre message mentionne « {keyword} » mais aucun fichier n'est joint. Envoyer quand même ?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# contact} other {# contacts}}", "default_book": "Carnet d'adresses par défaut", "description_mobile": "Préférences d'affichage et outils pour les carnets d'adresses.", + "device_sync_desc": "Affichez vos carnets d'adresses dans l'application Contacts et dans les autres applications de cet appareil. Les modifications sont synchronisées dans les deux sens.", + "device_sync_title": "Synchroniser avec cet appareil", "export_description_count": "{count, plural, one {Exporter # contact dans un seul fichier vCard.} other {Exporter # contacts dans un seul fichier vCard.}}", "export_empty": "Aucun contact à exporter", "import_description_mobile": "Importer des contacts depuis un fichier vCard (.vcf) avec détection des doublons.", "manage_description_mobile": "Créez, renommez et supprimez les carnets d'adresses qui organisent vos contacts." }, + "device_sync": { + "also_synced_by": "Également synchronisé pour {accounts} : il apparaît donc deux fois sur cet appareil.", + "android_settings": "Paramètres du compte Android", + "blocked_calendar": "Android ne demande plus l'accès aux calendriers. Autorisez-le dans les paramètres de l'application, puis réactivez la synchronisation.", + "blocked_contacts": "Android ne demande plus l'accès aux contacts. Autorisez-le dans les paramètres de l'application, puis réactivez la synchronisation.", + "blocked_title": "L'accès est désactivé", + "books_default": "Vos propres carnets d'adresses sont synchronisés ; les carnets partagés, uniquement si vous les choisissez.", + "books_hint": "Vos propres carnets d'adresses sont synchronisés, sauf si vous les désactivez ; les carnets partagés, uniquement si vous les activez.", + "books_label": "Carnets d'adresses", + "calendars_default": "Vos propres calendriers sont synchronisés ; les calendriers partagés, uniquement si vous les choisissez.", + "calendars_hint": "Vos propres calendriers sont synchronisés, sauf si vous les désactivez ; les calendriers partagés, uniquement si vous les activez. Les listes de tâches restent dans Bulwark.", + "calendars_label": "Calendriers", + "choose": "Choisir", + "choose_books_title": "Carnets d'adresses à synchroniser", + "choose_calendars_title": "Calendriers à synchroniser", + "collections_count": "Synchronisation : {selected} sur {total}", + "continue": "Continuer", + "deletions_delete": "Supprimer sur le serveur", + "deletions_message": "De nombreux éléments ont été supprimés d'un coup sur cet appareil ; ils n'ont donc pas été supprimés sur le serveur. Supprimez-les aussi sur le serveur ou restaurez-les sur cet appareil.", + "deletions_restore": "Restaurer", + "deletions_title": "Les supprimer aussi sur le serveur ?", + "denied_calendar": "Sans accès aux calendriers, Bulwark ne peut pas placer vos calendriers sur cet appareil.", + "denied_contacts": "Sans accès aux contacts, Bulwark ne peut pas placer vos carnets d'adresses sur cet appareil.", + "denied_title": "La synchronisation reste désactivée", + "failed_title": "Impossible d'activer la synchronisation", + "grant_access": "Autoriser l'accès", + "interval_15m": "Toutes les 15 minutes", + "interval_1h": "Toutes les heures", + "interval_30m": "Toutes les 30 minutes", + "interval_6h": "Toutes les 6 heures", + "interval_desc": "Fréquence à laquelle Android synchronise de lui-même. Les modifications faites dans Bulwark et celles signalées par push sont synchronisées plus tôt.", + "interval_label": "Fréquence de synchronisation", + "interval_manual": "Pas de synchronisation régulière", + "keep_syncing": "Continuer à synchroniser", + "list_error_auth": "Le serveur n'a pas accepté la connexion.", + "list_error_network": "Impossible de joindre le serveur.", + "list_error_server": "Le serveur n'a pas pu en fournir la liste.", + "list_error_signed_out": "Ce compte n'est pas connecté sur cet appareil.", + "master_off": "La synchronisation automatique est désactivée sur cet appareil : la synchronisation n'a lieu que lorsque vous appuyez sur « Synchroniser maintenant ».", + "new_contacts_default": "Carnet d'adresses par défaut", + "new_contacts_desc": "Le carnet d'adresses des contacts créés dans d'autres applications de cet appareil.", + "new_contacts_label": "Enregistrer les nouveaux contacts dans", + "no_books": "Ce compte n'a aucun carnet d'adresses.", + "no_calendars": "Ce compte n'a aucun calendrier à synchroniser.", + "ok": "OK", + "open_app_settings": "Ouvrir les paramètres", + "own_collections": "Les vôtres", + "rationale_calendar": "Pour afficher vos calendriers dans l'application Agenda, Bulwark doit accéder aux calendriers de cet appareil. Il ne modifie que les calendriers de son propre compte.", + "rationale_contacts": "Pour afficher vos carnets d'adresses dans l'application Contacts, Bulwark doit accéder aux contacts de cet appareil. Il ne modifie que les contacts de son propre compte.", + "rationale_title_calendar": "Autoriser l'accès aux calendriers", + "rationale_title_contacts": "Autoriser l'accès aux contacts", + "read_only": "Lecture seule", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Application Agenda", + "reminder_question_message": "Les événements synchronisés peuvent vous être rappelés par l'application d'agenda de cet appareil ou par Bulwark. Choisissez l'un des deux pour ne pas recevoir deux rappels. Vous pourrez modifier ce choix plus tard.", + "reminder_question_title": "Qui doit vous envoyer les rappels ?", + "reminders_desc": "Qui vous rappelle les événements synchronisés. L'autre reste silencieux pour que vous ne receviez pas deux rappels.", + "reminders_label": "Rappels", + "reminders_nobody": "Les notifications d'événements de Bulwark sont désactivées : rien ne vous rappelle donc les événements synchronisés.", + "remove": "Supprimer", + "remove_from_device": "Supprimer de cet appareil", + "remove_message": "Les contacts et calendriers du compte sont supprimés de cet appareil. Les modifications faites ici qui n'ont pas atteint le serveur sont perdues.", + "remove_title": "Supprimer de cet appareil ?", + "review_deletions": "Vérifier les suppressions", + "shared_by": "Partagé par {name}", + "sign_in_again": "Se reconnecter", + "status_auth": "Le serveur n'accepte plus la connexion. Reconnectez-vous pour continuer la synchronisation.", + "status_cancelled": "La dernière synchronisation s'est arrêtée avant la fin. Elle reprendra la prochaine fois.", + "status_failed": "Échec de la dernière synchronisation : {message}", + "status_failed_plain": "Échec de la dernière synchronisation.", + "status_io": "Le serveur était injoignable {when}. Android va réessayer.", + "status_item_errors": "{count, plural, one {# élément n'a pas pu être synchronisé.} other {# éléments n'ont pas pu être synchronisés.}}", + "status_off": "Désactivée", + "status_paused": "En pause dans les paramètres Android", + "status_permission_calendar": "L'accès aux calendriers a été désactivé : rien n'est synchronisé.", + "status_permission_contacts": "L'accès aux contacts a été désactivé : rien n'est synchronisé.", + "status_removed": "Supprimé dans les paramètres Android. Activez la synchronisation pour ajouter à nouveau le compte.", + "status_safety_abort": "Arrêtée : le serveur n'a rien renvoyé alors que cet appareil contient encore des éléments. Vérifiez le serveur, puis relancez la synchronisation.", + "status_synced": "Synchronisé {when}", + "status_syncing": "Synchronisation…", + "status_too_many_deletions": "De nombreux éléments ont été supprimés sur cet appareil. Ils ne seront pas supprimés sur le serveur tant que vous n'aurez pas confirmé.", + "status_unsupported_calendar": "Le serveur ne propose aucun calendrier pour ce compte.", + "status_unsupported_contacts": "Le serveur ne propose aucun carnet d'adresses pour ce compte.", + "status_waiting": "En attente de la première synchronisation", + "status_waiting_next": "En attente de la prochaine synchronisation", + "suspended_desc": "Déconnecté. Ses contacts et calendriers restent sur cet appareil, avec les modifications faites ici, et seront de nouveau synchronisés dès que vous vous connecterez.", + "switch_label": "Synchroniser {account} avec cet appareil", + "sync_now": "Synchroniser maintenant", + "turn_off_anyway": "Désactiver", + "turn_off_failed": "Les données synchronisées n'ont pas pu être supprimées de cet appareil. La synchronisation est en pause ; réessayez plus tard.", + "turn_off_failed_title": "Impossible de désactiver la synchronisation", + "turn_off_pending": "{count, plural, one {# modification effectuée sur cet appareil n'a pas encore atteint le serveur. Elle sera perdue si vous désactivez la synchronisation maintenant.} other {# modifications effectuées sur cet appareil n'ont pas encore atteint le serveur. Elles seront perdues si vous désactivez la synchronisation maintenant.}}", + "turn_off_title": "Désactiver quand même ?", + "turn_off_unknown": "Bulwark n'a pas pu vérifier si les modifications effectuées sur cet appareil ont atteint le serveur. Elles seront perdues si vous désactivez la synchronisation maintenant.", + "turning_off": "Envoi des modifications, puis suppression des données de cet appareil…", + "turning_on": "Configuration…" + }, "downloads": { "attachment_template": { "description_mobile": "Modèle utilisé pour enregistrer ou partager une pièce jointe." diff --git a/locales/rn/he.json b/locales/rn/he.json index e4ded47f..7cfd2cd3 100644 --- a/locales/rn/he.json +++ b/locales/rn/he.json @@ -93,6 +93,8 @@ "until_date": "עד {date}" }, "settings": { + "device_sync_desc": "הצג את לוחות השנה שלך באפליקציית היומן ובאפליקציות אחרות במכשיר הזה. השינויים מסתנכרנים בשני הכיוונים.", + "device_sync_title": "סנכרון למכשיר הזה", "show_week_numbers_mobile_desc": "הצג מספרי שבועות בתצוגת החודש.", "time_zone": "אזור זמן", "time_zone_auto": "אזור הזמן של המכשיר", @@ -249,6 +251,18 @@ "color_tag": "תג", "remove_color": "הסר תג" }, + "device_sync": { + "auth_problem_text": "השרת כבר לא מקבל את ההתחברות של {account}, ולכן אנשי הקשר ולוחות השנה שלו במכשיר הזה הפסיקו להסתנכרן. התחבר מחדש כדי לחדש את הסנכרון. שינויים שבוצעו במכשיר הזה נשמרים.", + "auth_problem_title": "התחבר מחדש כדי להמשיך לסנכרן", + "problem_channel": "בעיות סנכרון", + "sign_out_anyway": "התנתק", + "sign_out_pending_message": "{count, plural, one {שינוי אחד שבוצע במכשיר הזה לא הגיע לשרת. הוא יאבד אם תתנתק עכשיו.} two {שני שינויים שבוצעו במכשיר הזה לא הגיעו לשרת. הם יאבדו אם תתנתק עכשיו.} other {# שינויים שבוצעו במכשיר הזה לא הגיעו לשרת. הם יאבדו אם תתנתק עכשיו.}}", + "sign_out_pending_title": "להתנתק בכל זאת?", + "sign_out_unknown_message": "ייתכן ששינויים שבוצעו במכשיר הזה לא הגיעו לשרת. הם יאבדו אם תתנתק עכשיו.", + "signed_out_text": "בוצעה התנתקות מ-{account}, ולכן אנשי הקשר ולוחות השנה שלו במכשיר הזה כבר לא מסתנכרנים. התחבר מחדש כדי לחדש את הסנכרון. שינויים שבוצעו במכשיר הזה נשמרים.", + "signed_out_title": "התחבר כדי להמשיך לסנכרן", + "signing_out": "מסיר מהמכשיר הזה את אנשי הקשר ולוחות השנה המסונכרנים…" + }, "email_composer": { "attach_camera": "מצלמה", "attachment_reminder_body": "ההודעה שלך מזכירה \"{keyword}\" אבל לא צורף קובץ. לשלוח בכל זאת?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {איש קשר אחד} two {שני אנשי קשר} other {# אנשי קשר}}", "default_book": "ספר כתובות ברירת מחדל", "description_mobile": "העדפות תצוגה וכלים לספרי כתובות.", + "device_sync_desc": "הצג את ספרי הכתובות שלך באפליקציית אנשי הקשר ובאפליקציות אחרות במכשיר הזה. השינויים מסתנכרנים בשני הכיוונים.", + "device_sync_title": "סנכרון למכשיר הזה", "export_description_count": "{count, plural, one {ייצוא איש קשר אחד כקובץ vCard יחיד.} two {ייצוא שני אנשי קשר כקובץ vCard יחיד.} other {ייצוא # אנשי קשר כקובץ vCard יחיד.}}", "export_empty": "אין אנשי קשר לייצוא", "import_description_mobile": "ייבוא אנשי קשר מקובץ vCard (.vcf) עם זיהוי כפילויות.", "manage_description_mobile": "צור, שנה שם והסר את ספרי הכתובות שמארגנים את אנשי הקשר שלך." }, + "device_sync": { + "also_synced_by": "מסתנכרן גם עבור {accounts}, ולכן מופיע פעמיים במכשיר הזה.", + "android_settings": "הגדרות החשבון ב-Android", + "blocked_calendar": "Android כבר לא מבקש גישה ללוחות השנה. אפשר את הגישה בהגדרות האפליקציה ואז הפעל שוב את הסנכרון.", + "blocked_contacts": "Android כבר לא מבקש גישה לאנשי הקשר. אפשר את הגישה בהגדרות האפליקציה ואז הפעל שוב את הסנכרון.", + "blocked_title": "הגישה כבויה", + "books_default": "ספרי הכתובות שלך מסתנכרנים; ספרים משותפים רק אם תבחר בהם.", + "books_hint": "ספרי הכתובות שלך מסתנכרנים אלא אם תכבה אותם; ספרים משותפים רק אם תפעיל אותם.", + "books_label": "ספרי כתובות", + "calendars_default": "לוחות השנה שלך מסתנכרנים; לוחות משותפים רק אם תבחר בהם.", + "calendars_hint": "לוחות השנה שלך מסתנכרנים אלא אם תכבה אותם; לוחות משותפים רק אם תפעיל אותם. רשימות המשימות נשארות ב-Bulwark.", + "calendars_label": "לוחות שנה", + "choose": "בחירה", + "choose_books_title": "ספרי כתובות לסנכרון", + "choose_calendars_title": "לוחות שנה לסנכרון", + "collections_count": "מסתנכרנים: {selected} מתוך {total}", + "continue": "המשך", + "deletions_delete": "מחיקה בשרת", + "deletions_message": "פריטים רבים נמחקו בבת אחת במכשיר הזה, ולכן הם לא נמחקו בשרת. מחק אותם גם שם או החזר אותם למכשיר הזה.", + "deletions_restore": "החזרה", + "deletions_title": "למחוק אותם גם בשרת?", + "denied_calendar": "ללא גישה ללוחות השנה, Bulwark לא יכולה להעביר את לוחות השנה שלך למכשיר הזה.", + "denied_contacts": "ללא גישה לאנשי הקשר, Bulwark לא יכולה להעביר את ספרי הכתובות שלך למכשיר הזה.", + "denied_title": "הסנכרון נשאר כבוי", + "failed_title": "לא ניתן היה להפעיל את הסנכרון", + "grant_access": "מתן גישה", + "interval_15m": "כל 15 דקות", + "interval_1h": "כל שעה", + "interval_30m": "כל 30 דקות", + "interval_6h": "כל 6 שעות", + "interval_desc": "באיזו תדירות Android מסנכרן מעצמו. עריכות ב-Bulwark ושינויים שמתקבלים ב-Push מסתנכרנים מוקדם יותר.", + "interval_label": "תדירות סנכרון", + "interval_manual": "ללא סנכרון קבוע", + "keep_syncing": "המשך לסנכרן", + "list_error_auth": "השרת לא קיבל את ההתחברות.", + "list_error_network": "לא ניתן היה להתחבר לשרת.", + "list_error_server": "השרת לא הצליח להציג את הרשימה.", + "list_error_signed_out": "החשבון הזה לא מחובר במכשיר הזה.", + "master_off": "הסנכרון האוטומטי כבוי במכשיר הזה, ולכן הסנכרון מתבצע רק כשאתה מקיש על \"סנכרן עכשיו\".", + "new_contacts_default": "ספר כתובות ברירת מחדל", + "new_contacts_desc": "ספר הכתובות לאנשי קשר שנוצרים באפליקציות אחרות במכשיר הזה.", + "new_contacts_label": "יעד לאנשי קשר חדשים", + "no_books": "לחשבון הזה אין ספרי כתובות.", + "no_calendars": "לחשבון הזה אין לוחות שנה לסנכרון.", + "ok": "אישור", + "open_app_settings": "פתיחת הגדרות האפליקציה", + "own_collections": "שלך", + "rationale_calendar": "כדי להציג את לוחות השנה שלך באפליקציית היומן, Bulwark צריכה גישה ללוחות השנה במכשיר הזה. היא משנה רק את לוחות השנה של החשבון שלה.", + "rationale_contacts": "כדי להציג את ספרי הכתובות שלך באפליקציית אנשי הקשר, Bulwark צריכה גישה לאנשי הקשר במכשיר הזה. היא משנה רק את אנשי הקשר של החשבון שלה.", + "rationale_title_calendar": "מתן גישה ללוחות השנה", + "rationale_title_contacts": "מתן גישה לאנשי הקשר", + "read_only": "קריאה בלבד", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "אפליקציית היומן", + "reminder_question_message": "תזכורות לאירועים מסונכרנים יכולות להגיע מאפליקציית היומן במכשיר הזה או מ-Bulwark. בחר אחת מהן כדי שלא תקבל תזכורות כפולות. אפשר לשנות זאת מאוחר יותר.", + "reminder_question_title": "מי ישלח לך תזכורות?", + "reminders_desc": "מי מזכיר לך על אירועים מסונכרנים. השני שותק, כך שלא תקבל תזכורות כפולות.", + "reminders_label": "תזכורות", + "reminders_nobody": "התראות האירועים של Bulwark כבויות, ולכן שום דבר לא יזכיר לך על אירועים מסונכרנים.", + "remove": "הסרה", + "remove_from_device": "הסרה מהמכשיר הזה", + "remove_message": "אנשי הקשר ולוחות השנה של החשבון יוסרו מהמכשיר הזה. שינויים שבוצעו כאן ועדיין לא הגיעו לשרת יאבדו.", + "remove_title": "להסיר מהמכשיר הזה?", + "review_deletions": "בדיקת המחיקות", + "shared_by": "משותף על ידי {name}", + "sign_in_again": "התחברות מחדש", + "status_auth": "השרת כבר לא מקבל את ההתחברות. התחבר מחדש כדי להמשיך לסנכרן.", + "status_cancelled": "הסנכרון האחרון נעצר מוקדם. הוא ימשיך בפעם הבאה.", + "status_failed": "הסנכרון האחרון נכשל: {message}", + "status_failed_plain": "הסנכרון האחרון נכשל.", + "status_io": "לא ניתן היה להתחבר לשרת {when}. Android ינסה שוב.", + "status_item_errors": "{count, plural, one {לא ניתן היה לסנכרן פריט אחד.} two {לא ניתן היה לסנכרן שני פריטים.} other {לא ניתן היה לסנכרן # פריטים.}}", + "status_off": "כבוי", + "status_paused": "מושהה בהגדרות Android", + "status_permission_calendar": "הגישה ללוחות השנה כובתה, ולכן שום דבר לא מסתנכרן.", + "status_permission_contacts": "הגישה לאנשי הקשר כובתה, ולכן שום דבר לא מסתנכרן.", + "status_removed": "הוסר בהגדרות Android. הפעל את הסנכרון כדי להוסיף את החשבון מחדש.", + "status_safety_abort": "נעצר: השרת לא החזיר דבר, אף שבמכשיר הזה עדיין יש פריטים. בדוק את השרת ואז סנכרן שוב.", + "status_synced": "סונכרן {when}", + "status_syncing": "מסנכרן…", + "status_too_many_deletions": "פריטים רבים נמחקו במכשיר הזה. הם לא יימחקו בשרת עד שתאשר.", + "status_unsupported_calendar": "השרת לא מציע לוחות שנה לחשבון הזה.", + "status_unsupported_contacts": "השרת לא מציע ספרי כתובות לחשבון הזה.", + "status_waiting": "ממתין לסנכרון הראשון", + "status_waiting_next": "ממתין לסנכרון הבא", + "suspended_desc": "מנותק. אנשי הקשר ולוחות השנה שלו נשארים במכשיר הזה, עם השינויים שבוצעו כאן, ויסתנכרנו שוב אחרי שתתחבר.", + "switch_label": "סנכרון {account} למכשיר הזה", + "sync_now": "סנכרן עכשיו", + "turn_off_anyway": "כיבוי", + "turn_off_failed": "לא ניתן היה להסיר את הנתונים המסונכרנים מהמכשיר הזה. הסנכרון מושהה; נסה שוב מאוחר יותר.", + "turn_off_failed_title": "לא ניתן היה לכבות את הסנכרון", + "turn_off_pending": "{count, plural, one {שינוי אחד שבוצע במכשיר הזה עדיין לא הגיע לשרת. הוא יאבד אם תכבה את הסנכרון עכשיו.} two {שני שינויים שבוצעו במכשיר הזה עדיין לא הגיעו לשרת. הם יאבדו אם תכבה את הסנכרון עכשיו.} other {# שינויים שבוצעו במכשיר הזה עדיין לא הגיעו לשרת. הם יאבדו אם תכבה את הסנכרון עכשיו.}}", + "turn_off_title": "לכבות בכל זאת?", + "turn_off_unknown": "Bulwark לא הצליחה לבדוק אם השינויים שבוצעו במכשיר הזה הגיעו לשרת. הם יאבדו אם תכבה את הסנכרון עכשיו.", + "turning_off": "מעלה שינויים ואז מסיר את הנתונים מהמכשיר הזה…", + "turning_on": "מגדיר…" + }, "downloads": { "attachment_template": { "description_mobile": "התבנית שמשמשת בעת שמירה או שיתוף של קובץ מצורף." diff --git a/locales/rn/hu.json b/locales/rn/hu.json index 41a1e34e..62af8f21 100644 --- a/locales/rn/hu.json +++ b/locales/rn/hu.json @@ -93,6 +93,8 @@ "until_date": "Eddig: {date}" }, "settings": { + "device_sync_desc": "A naptáraid megjelennek a Naptár alkalmazásban és az eszköz más alkalmazásaiban. A módosítások mindkét irányban szinkronizálódnak.", + "device_sync_title": "Szinkronizálás ezzel az eszközzel", "show_week_numbers_mobile_desc": "Hétszámok megjelenítése a havi nézetben.", "time_zone": "Időzóna", "time_zone_auto": "Eszköz időzónája", @@ -249,6 +251,18 @@ "color_tag": "Címke", "remove_color": "Címke eltávolítása" }, + "device_sync": { + "auth_problem_text": "A szerver már nem fogadja el ennek a fióknak a bejelentkezését: {account}. Emiatt a fiók névjegyei és naptárai ezen az eszközön már nem szinkronizálódnak. A folytatáshoz jelentkezz be újra. Az ezen az eszközön végzett módosítások megmaradnak.", + "auth_problem_title": "Jelentkezz be újra a szinkronizálás folytatásához", + "problem_channel": "Szinkronizálási problémák", + "sign_out_anyway": "Kijelentkezés", + "sign_out_pending_message": "{count, plural, one {Ezen az eszközön # módosítás nem jutott el a szerverre. Elvész, ha most kijelentkezel.} other {Ezen az eszközön # módosítás nem jutott el a szerverre. Elvesznek, ha most kijelentkezel.}}", + "sign_out_pending_title": "Mindenképp kijelentkezel?", + "sign_out_unknown_message": "Előfordulhat, hogy az ezen az eszközön végzett módosítások nem jutottak el a szerverre. Elvesznek, ha most kijelentkezel.", + "signed_out_text": "Ez a fiók már nincs bejelentkezve: {account}. Emiatt a fiók névjegyei és naptárai ezen az eszközön már nem szinkronizálódnak. A folytatáshoz jelentkezz be újra. Az ezen az eszközön végzett módosítások megmaradnak.", + "signed_out_title": "Jelentkezz be a szinkronizálás folytatásához", + "signing_out": "A szinkronizált névjegyek és naptárak eltávolítása erről az eszközről…" + }, "email_composer": { "attach_camera": "Kamera", "attachment_reminder_body": "Az üzeneted említi a(z) „{keyword}” kifejezést, de nincs fájl csatolva. Elküldöd így is?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# névjegy} other {# névjegy}}", "default_book": "Alapértelmezett címjegyzék", "description_mobile": "Megjelenítési beállítások és címjegyzék-eszközök.", + "device_sync_desc": "A címjegyzékeid megjelennek a Névjegyek alkalmazásban és az eszköz más alkalmazásaiban. A módosítások mindkét irányban szinkronizálódnak.", + "device_sync_title": "Szinkronizálás ezzel az eszközzel", "export_description_count": "{count, plural, one {# névjegy exportálása egyetlen vCard-fájlba.} other {# névjegy exportálása egyetlen vCard-fájlba.}}", "export_empty": "Nincs exportálható névjegy", "import_description_mobile": "Névjegyek importálása vCard (.vcf) fájlból, a duplikátumok felismerésével.", "manage_description_mobile": "Itt hozhatod létre, nevezheted át és távolíthatod el a névjegyeidet rendező címjegyzékeket." }, + "device_sync": { + "also_synced_by": "Más fiókhoz is szinkronizálódik ({accounts}), ezért kétszer jelenik meg ezen az eszközön.", + "android_settings": "Android-fiókbeállítások", + "blocked_calendar": "Az Android már nem kér hozzáférést a naptárakhoz. Engedélyezd az alkalmazás beállításaiban, majd kapcsold be újra a szinkronizálást.", + "blocked_contacts": "Az Android már nem kér hozzáférést a névjegyekhez. Engedélyezd az alkalmazás beállításaiban, majd kapcsold be újra a szinkronizálást.", + "blocked_title": "A hozzáférés ki van kapcsolva", + "books_default": "A saját címjegyzékeid szinkronizálódnak, a megosztottak csak akkor, ha kiválasztod őket.", + "books_hint": "A saját címjegyzékeid szinkronizálódnak, hacsak ki nem kapcsolod őket; a megosztottak csak akkor, ha bekapcsolod őket.", + "books_label": "Címjegyzékek", + "calendars_default": "A saját naptáraid szinkronizálódnak, a megosztottak csak akkor, ha kiválasztod őket.", + "calendars_hint": "A saját naptáraid szinkronizálódnak, hacsak ki nem kapcsolod őket; a megosztottak csak akkor, ha bekapcsolod őket. A feladatlisták a Bulwarkban maradnak.", + "calendars_label": "Naptárak", + "choose": "Kiválasztás", + "choose_books_title": "Szinkronizálandó címjegyzékek", + "choose_calendars_title": "Szinkronizálandó naptárak", + "collections_count": "Szinkronizálva: {selected}/{total}", + "continue": "Tovább", + "deletions_delete": "Törlés a szerveren", + "deletions_message": "Ezen az eszközön egyszerre sok elemet töröltek, ezért a szerveren nem lettek törölve. Töröld őket ott is, vagy állítsd vissza őket ezen az eszközön.", + "deletions_restore": "Visszaállítás", + "deletions_title": "A szerveren is törlöd őket?", + "denied_calendar": "A naptárakhoz való hozzáférés nélkül a Bulwark nem tudja a naptáraidat erre az eszközre hozni.", + "denied_contacts": "A névjegyekhez való hozzáférés nélkül a Bulwark nem tudja a címjegyzékeidet erre az eszközre hozni.", + "denied_title": "A szinkronizálás kikapcsolva marad", + "failed_title": "Nem sikerült bekapcsolni a szinkronizálást", + "grant_access": "Hozzáférés megadása", + "interval_15m": "15 percenként", + "interval_1h": "Óránként", + "interval_30m": "30 percenként", + "interval_6h": "6 óránként", + "interval_desc": "Milyen gyakran szinkronizál az Android magától. A Bulwarkban végzett szerkesztések és a push útján jelzett módosítások hamarabb szinkronizálódnak.", + "interval_label": "Szinkronizálási időköz", + "interval_manual": "Nincs rendszeres szinkronizálás", + "keep_syncing": "Szinkronizálás folytatása", + "list_error_auth": "A szerver nem fogadta el a bejelentkezést.", + "list_error_network": "Nem sikerült elérni a szervert.", + "list_error_server": "A szerver nem tudta visszaadni a listát.", + "list_error_signed_out": "Ez a fiók nincs bejelentkezve ezen az eszközön.", + "master_off": "Az automatikus szinkronizálás ki van kapcsolva ezen az eszközön, ezért szinkronizálás csak akkor történik, ha a „Szinkronizálás most” gombra koppintasz.", + "new_contacts_default": "Alapértelmezett címjegyzék", + "new_contacts_desc": "A címjegyzék az eszköz más alkalmazásaiban létrehozott névjegyekhez.", + "new_contacts_label": "Új névjegyek helye", + "no_books": "Ennek a fióknak nincs címjegyzéke.", + "no_calendars": "Ennek a fióknak nincs szinkronizálható naptára.", + "ok": "OK", + "open_app_settings": "Beállítások megnyitása", + "own_collections": "Saját", + "rationale_calendar": "Ahhoz, hogy a naptáraid megjelenjenek a Naptár alkalmazásban, a Bulwarknak hozzá kell férnie az eszköz naptáraihoz. Csak a saját fiókja naptárait módosítja.", + "rationale_contacts": "Ahhoz, hogy a címjegyzékeid megjelenjenek a Névjegyek alkalmazásban, a Bulwarknak hozzá kell férnie az eszköz névjegyeihez. Csak a saját fiókja névjegyeit módosítja.", + "rationale_title_calendar": "Hozzáférés engedélyezése a naptárakhoz", + "rationale_title_contacts": "Hozzáférés engedélyezése a névjegyekhez", + "read_only": "Csak olvasható", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Naptár alkalmazás", + "reminder_question_message": "A szinkronizált eseményekre az eszköz naptáralkalmazása vagy a Bulwark emlékeztethet. Válassz egyet, hogy ne kapj kétszer emlékeztetőt. Később módosíthatod.", + "reminder_question_title": "Ki emlékeztessen?", + "reminders_desc": "Ki emlékeztet a szinkronizált eseményekre. A másik csendben marad, így nem kapsz kétszer emlékeztetőt.", + "reminders_label": "Emlékeztetők", + "reminders_nobody": "A Bulwark eseményértesítései ki vannak kapcsolva, így semmi sem emlékeztet a szinkronizált eseményekre.", + "remove": "Eltávolítás", + "remove_from_device": "Eltávolítás erről az eszközről", + "remove_message": "A fiók névjegyei és naptárai törlődnek erről az eszközről. Az itt végzett, a szerverre még el nem jutott módosítások elvesznek.", + "remove_title": "Eltávolítod erről az eszközről?", + "review_deletions": "Törlések áttekintése", + "shared_by": "Megosztotta: {name}", + "sign_in_again": "Újbóli bejelentkezés", + "status_auth": "A szerver már nem fogadja el a bejelentkezést. Jelentkezz be újra a szinkronizálás folytatásához.", + "status_cancelled": "A legutóbbi szinkronizálás idő előtt leállt. Legközelebb folytatódik.", + "status_failed": "A legutóbbi szinkronizálás sikertelen: {message}", + "status_failed_plain": "A legutóbbi szinkronizálás sikertelen.", + "status_io": "A szerver nem volt elérhető ({when}). Az Android újra megpróbálja.", + "status_item_errors": "{count, plural, one {# elemet nem sikerült szinkronizálni.} other {# elemet nem sikerült szinkronizálni.}}", + "status_off": "Kikapcsolva", + "status_paused": "Szüneteltetve az Android-beállításokban", + "status_permission_calendar": "A naptárakhoz való hozzáférés ki lett kapcsolva, ezért semmi sem szinkronizálódik.", + "status_permission_contacts": "A névjegyekhez való hozzáférés ki lett kapcsolva, ezért semmi sem szinkronizálódik.", + "status_removed": "Eltávolítva az Android-beállításokban. Kapcsold be a szinkronizálást a fiók újbóli hozzáadásához.", + "status_safety_abort": "Leállítva: a szerver semmit sem adott vissza, pedig ezen az eszközön még vannak elemek. Ellenőrizd a szervert, majd szinkronizálj újra.", + "status_synced": "Szinkronizálva: {when}", + "status_syncing": "Szinkronizálás…", + "status_too_many_deletions": "Ezen az eszközön sok elemet töröltek. A szerveren csak akkor törlődnek, ha megerősíted.", + "status_unsupported_calendar": "A szerver nem kínál naptárakat ehhez a fiókhoz.", + "status_unsupported_contacts": "A szerver nem kínál címjegyzékeket ehhez a fiókhoz.", + "status_waiting": "Várakozás az első szinkronizálásra", + "status_waiting_next": "Várakozás a következő szinkronizálásra", + "suspended_desc": "Kijelentkezve. A fiók névjegyei és naptárai az itt végzett módosításokkal együtt ezen az eszközön maradnak, és bejelentkezés után újra szinkronizálódnak.", + "switch_label": "{account} szinkronizálása ezzel az eszközzel", + "sync_now": "Szinkronizálás most", + "turn_off_anyway": "Kikapcsolás", + "turn_off_failed": "A szinkronizált adatokat nem sikerült eltávolítani erről az eszközről. A szinkronizálás szünetel; próbáld újra később.", + "turn_off_failed_title": "Nem sikerült kikapcsolni a szinkronizálást", + "turn_off_pending": "{count, plural, one {Ezen az eszközön # módosítás még nem jutott el a szerverre. Elvész, ha most kikapcsolod a szinkronizálást.} other {Ezen az eszközön # módosítás még nem jutott el a szerverre. Elvesznek, ha most kikapcsolod a szinkronizálást.}}", + "turn_off_title": "Mindenképp kikapcsolod?", + "turn_off_unknown": "A Bulwark nem tudta ellenőrizni, hogy az ezen az eszközön végzett módosítások eljutottak-e a szerverre. Elvesznek, ha most kikapcsolod a szinkronizálást.", + "turning_off": "Módosítások feltöltése, majd az adatok eltávolítása erről az eszközről…", + "turning_on": "Beállítás folyamatban…" + }, "downloads": { "attachment_template": { "description_mobile": "Csatolmány mentésekor vagy megosztásakor használt sablon." diff --git a/locales/rn/it.json b/locales/rn/it.json index dd947280..a9ead67c 100644 --- a/locales/rn/it.json +++ b/locales/rn/it.json @@ -93,6 +93,8 @@ "until_date": "Fino al {date}" }, "settings": { + "device_sync_desc": "Mostra i tuoi calendari nell'app Calendario e nelle altre app di questo dispositivo. Le modifiche vengono sincronizzate in entrambe le direzioni.", + "device_sync_title": "Sincronizza con questo dispositivo", "show_week_numbers_mobile_desc": "Mostra i numeri di settimana nella vista mensile.", "time_zone": "Fuso orario", "time_zone_auto": "Fuso orario del dispositivo", @@ -249,6 +251,18 @@ "color_tag": "Etichetta", "remove_color": "Rimuovi etichetta" }, + "device_sync": { + "auth_problem_text": "Il server non accetta più l'accesso di {account}, quindi i suoi contatti e calendari su questo dispositivo non vengono più sincronizzati. Accedi di nuovo per riprendere. Le modifiche fatte su questo dispositivo vengono mantenute.", + "auth_problem_title": "Accedi di nuovo per continuare a sincronizzare", + "problem_channel": "Problemi di sincronizzazione", + "sign_out_anyway": "Esci", + "sign_out_pending_message": "{count, plural, one {# modifica fatta su questo dispositivo non ha raggiunto il server. Andrà persa se esci ora.} other {# modifiche fatte su questo dispositivo non hanno raggiunto il server. Andranno perse se esci ora.}}", + "sign_out_pending_title": "Uscire comunque?", + "sign_out_unknown_message": "Le modifiche fatte su questo dispositivo potrebbero non aver raggiunto il server. Andranno perse se esci ora.", + "signed_out_text": "{account} è stato disconnesso, quindi i suoi contatti e calendari su questo dispositivo non vengono più sincronizzati. Accedi di nuovo per riprendere. Le modifiche fatte su questo dispositivo vengono mantenute.", + "signed_out_title": "Accedi per continuare a sincronizzare", + "signing_out": "Rimozione dei contatti e dei calendari sincronizzati da questo dispositivo…" + }, "email_composer": { "attach_camera": "Fotocamera", "attachment_reminder_body": "Il tuo messaggio menziona \"{keyword}\" ma nessun file è allegato. Inviare comunque?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# contatto} other {# contatti}}", "default_book": "Rubrica predefinita", "description_mobile": "Preferenze di visualizzazione e strumenti per le rubriche.", + "device_sync_desc": "Mostra le tue rubriche nell'app Contatti e nelle altre app di questo dispositivo. Le modifiche vengono sincronizzate in entrambe le direzioni.", + "device_sync_title": "Sincronizza con questo dispositivo", "export_description_count": "{count, plural, one {Esporta # contatto come un unico file vCard.} other {Esporta # contatti come un unico file vCard.}}", "export_empty": "Nessun contatto da esportare", "import_description_mobile": "Importa contatti da un file vCard (.vcf) con rilevamento dei duplicati.", "manage_description_mobile": "Crea, rinomina e rimuovi le rubriche che organizzano i tuoi contatti." }, + "device_sync": { + "also_synced_by": "Si sincronizza anche per {accounts}, quindi compare due volte su questo dispositivo.", + "android_settings": "Impostazioni account Android", + "blocked_calendar": "Android non chiede più l'accesso ai calendari. Consentilo nelle impostazioni dell'app, poi riattiva la sincronizzazione.", + "blocked_contacts": "Android non chiede più l'accesso ai contatti. Consentilo nelle impostazioni dell'app, poi riattiva la sincronizzazione.", + "blocked_title": "L'accesso è disattivato", + "books_default": "Le tue rubriche vengono sincronizzate; quelle condivise solo se le scegli.", + "books_hint": "Le tue rubriche vengono sincronizzate a meno che tu non le disattivi; quelle condivise solo se le attivi.", + "books_label": "Rubriche", + "calendars_default": "I tuoi calendari vengono sincronizzati; quelli condivisi solo se li scegli.", + "calendars_hint": "I tuoi calendari vengono sincronizzati a meno che tu non li disattivi; quelli condivisi solo se li attivi. Gli elenchi di attività restano in Bulwark.", + "calendars_label": "Calendari", + "choose": "Scegli", + "choose_books_title": "Rubriche da sincronizzare", + "choose_calendars_title": "Calendari da sincronizzare", + "collections_count": "Sincronizzazione: {selected} su {total}", + "continue": "Continua", + "deletions_delete": "Elimina dal server", + "deletions_message": "Molti elementi sono stati eliminati contemporaneamente su questo dispositivo, quindi non sono stati eliminati sul server. Eliminali anche lì o ripristinali su questo dispositivo.", + "deletions_restore": "Ripristina", + "deletions_title": "Eliminarli anche sul server?", + "denied_calendar": "Senza l'accesso ai calendari, Bulwark non può portare i tuoi calendari su questo dispositivo.", + "denied_contacts": "Senza l'accesso ai contatti, Bulwark non può portare le tue rubriche su questo dispositivo.", + "denied_title": "La sincronizzazione resta disattivata", + "failed_title": "Impossibile attivare la sincronizzazione", + "grant_access": "Consenti l'accesso", + "interval_15m": "Ogni 15 minuti", + "interval_1h": "Ogni ora", + "interval_30m": "Ogni 30 minuti", + "interval_6h": "Ogni 6 ore", + "interval_desc": "La frequenza con cui Android sincronizza da solo. Le modifiche fatte in Bulwark e quelle notificate via push vengono sincronizzate prima.", + "interval_label": "Intervallo di sincronizzazione", + "interval_manual": "Nessuna sincronizzazione periodica", + "keep_syncing": "Continua a sincronizzare", + "list_error_auth": "Il server non ha accettato l'accesso.", + "list_error_network": "Impossibile raggiungere il server.", + "list_error_server": "Il server non è riuscito a fornire l'elenco.", + "list_error_signed_out": "Su questo dispositivo non hai eseguito l'accesso a questo account.", + "master_off": "La sincronizzazione automatica è disattivata su questo dispositivo, quindi si sincronizza solo quando tocchi \"Sincronizza ora\".", + "new_contacts_default": "Rubrica predefinita", + "new_contacts_desc": "La rubrica per i contatti creati in altre app su questo dispositivo.", + "new_contacts_label": "Salva i nuovi contatti in", + "no_books": "Questo account non ha rubriche.", + "no_calendars": "Questo account non ha calendari da sincronizzare.", + "ok": "OK", + "open_app_settings": "Apri impostazioni app", + "own_collections": "Personali", + "rationale_calendar": "Per mostrare i tuoi calendari nell'app Calendario, Bulwark ha bisogno dell'accesso ai calendari di questo dispositivo. Modifica solo i calendari del proprio account.", + "rationale_contacts": "Per mostrare le tue rubriche nell'app Contatti, Bulwark ha bisogno dell'accesso ai contatti di questo dispositivo. Modifica solo i contatti del proprio account.", + "rationale_title_calendar": "Consenti l'accesso ai calendari", + "rationale_title_contacts": "Consenti l'accesso ai contatti", + "read_only": "Sola lettura", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "App Calendario", + "reminder_question_message": "I promemoria degli eventi sincronizzati possono arrivare dall'app di calendario di questo dispositivo o da Bulwark. Scegline uno per non riceverli due volte. Puoi cambiare idea in seguito.", + "reminder_question_title": "Chi deve inviarti i promemoria?", + "reminders_desc": "Chi ti ricorda gli eventi sincronizzati. L'altro resta in silenzio, così non ricevi due promemoria.", + "reminders_label": "Promemoria", + "reminders_nobody": "Le notifiche degli eventi di Bulwark sono disattivate, quindi niente ti ricorda gli eventi sincronizzati.", + "remove": "Rimuovi", + "remove_from_device": "Rimuovi da questo dispositivo", + "remove_message": "I contatti e i calendari dell'account vengono rimossi da questo dispositivo. Le modifiche fatte qui che non hanno raggiunto il server andranno perse.", + "remove_title": "Rimuovere da questo dispositivo?", + "review_deletions": "Controlla eliminazioni", + "shared_by": "Condiviso da {name}", + "sign_in_again": "Accedi di nuovo", + "status_auth": "Il server non accetta più l'accesso. Accedi di nuovo per continuare a sincronizzare.", + "status_cancelled": "L'ultima sincronizzazione si è interrotta prima del termine. Riprenderà la prossima volta.", + "status_failed": "Ultima sincronizzazione non riuscita: {message}", + "status_failed_plain": "Ultima sincronizzazione non riuscita.", + "status_io": "Il server non era raggiungibile {when}. Android riproverà.", + "status_item_errors": "{count, plural, one {Impossibile sincronizzare # elemento.} other {Impossibile sincronizzare # elementi.}}", + "status_off": "Disattivata", + "status_paused": "In pausa nelle impostazioni di Android", + "status_permission_calendar": "L'accesso ai calendari è stato disattivato, quindi non viene sincronizzato nulla.", + "status_permission_contacts": "L'accesso ai contatti è stato disattivato, quindi non viene sincronizzato nulla.", + "status_removed": "Rimosso nelle impostazioni di Android. Attiva la sincronizzazione per aggiungere di nuovo l'account.", + "status_safety_abort": "Interrotta: il server non ha restituito nulla mentre questo dispositivo contiene ancora elementi. Controlla il server, poi sincronizza di nuovo.", + "status_synced": "Sincronizzato {when}", + "status_syncing": "Sincronizzazione…", + "status_too_many_deletions": "Molti elementi sono stati eliminati su questo dispositivo. Non verranno eliminati sul server finché non confermi.", + "status_unsupported_calendar": "Il server non offre calendari per questo account.", + "status_unsupported_contacts": "Il server non offre rubriche per questo account.", + "status_waiting": "In attesa della prima sincronizzazione", + "status_waiting_next": "In attesa della prossima sincronizzazione", + "suspended_desc": "Disconnesso. I suoi contatti e calendari restano su questo dispositivo, con le modifiche fatte qui, e verranno sincronizzati di nuovo quando accedi.", + "switch_label": "Sincronizza {account} con questo dispositivo", + "sync_now": "Sincronizza ora", + "turn_off_anyway": "Disattiva", + "turn_off_failed": "Impossibile rimuovere i dati sincronizzati da questo dispositivo. La sincronizzazione è in pausa; riprova più tardi.", + "turn_off_failed_title": "Impossibile disattivare la sincronizzazione", + "turn_off_pending": "{count, plural, one {# modifica fatta su questo dispositivo non ha ancora raggiunto il server. Andrà persa se disattivi ora la sincronizzazione.} other {# modifiche fatte su questo dispositivo non hanno ancora raggiunto il server. Andranno perse se disattivi ora la sincronizzazione.}}", + "turn_off_title": "Disattivare comunque?", + "turn_off_unknown": "Bulwark non è riuscito a verificare se le modifiche fatte su questo dispositivo hanno raggiunto il server. Andranno perse se disattivi ora la sincronizzazione.", + "turning_off": "Caricamento delle modifiche, poi rimozione dei dati da questo dispositivo…", + "turning_on": "Configurazione…" + }, "downloads": { "attachment_template": { "description_mobile": "Modello usato quando salvi o condividi un allegato." diff --git a/locales/rn/ja.json b/locales/rn/ja.json index 0b517a77..1d661c4d 100644 --- a/locales/rn/ja.json +++ b/locales/rn/ja.json @@ -93,6 +93,8 @@ "until_date": "{date}まで" }, "settings": { + "device_sync_desc": "カレンダーアプリやこのデバイスの他のアプリにカレンダーを表示します。変更は双方向に同期されます。", + "device_sync_title": "このデバイスに同期", "show_week_numbers_mobile_desc": "月表示に週番号を表示する", "time_zone": "タイムゾーン", "time_zone_auto": "デバイスのタイムゾーン", @@ -249,6 +251,18 @@ "color_tag": "ラベル", "remove_color": "ラベルを削除" }, + "device_sync": { + "auth_problem_text": "サーバーが {account} のサインインを受け付けなくなったため、このデバイス上のこのアカウントの連絡先とカレンダーの同期が停止しました。再開するには、もう一度サインインしてください。このデバイスで行った変更は保持されます。", + "auth_problem_title": "同期を続けるには再度サインインしてください", + "problem_channel": "同期の問題", + "sign_out_anyway": "サインアウト", + "sign_out_pending_message": "{count, plural, other {このデバイスで行った#件の変更がサーバーに届いていません。今サインアウトすると失われます。}}", + "sign_out_pending_title": "それでもサインアウトしますか?", + "sign_out_unknown_message": "このデバイスで行った変更がサーバーに届いていない可能性があります。今サインアウトすると失われます。", + "signed_out_text": "{account} からサインアウトされたため、このデバイス上のこのアカウントの連絡先とカレンダーは同期されなくなりました。再開するには、もう一度サインインしてください。このデバイスで行った変更は保持されます。", + "signed_out_title": "同期を続けるにはサインインしてください", + "signing_out": "同期した連絡先とカレンダーをこのデバイスから削除しています…" + }, "email_composer": { "attach_camera": "カメラ", "attachment_reminder_body": "メッセージに「{keyword}」が含まれていますが、ファイルが添付されていません。このまま送信しますか?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, other {#件の連絡先}}", "default_book": "デフォルトのアドレス帳", "description_mobile": "表示設定とアドレス帳のツールです。", + "device_sync_desc": "連絡帳アプリやこのデバイスの他のアプリにアドレス帳を表示します。変更は双方向に同期されます。", + "device_sync_title": "このデバイスに同期", "export_description_count": "{count, plural, other {#件の連絡先を1つの vCard ファイルとしてエクスポートします。}}", "export_empty": "エクスポートする連絡先がありません", "import_description_mobile": "重複を検出しながら、vCard(.vcf)ファイルから連絡先をインポートします。", "manage_description_mobile": "連絡先を整理するアドレス帳の作成、名前変更、削除ができます。" }, + "device_sync": { + "also_synced_by": "{accounts} でも同期されているため、このデバイスには2回表示されます。", + "android_settings": "Android のアカウント設定", + "blocked_calendar": "Android はカレンダーへのアクセスを求めなくなりました。アプリの設定で許可してから、同期をもう一度オンにしてください。", + "blocked_contacts": "Android は連絡先へのアクセスを求めなくなりました。アプリの設定で許可してから、同期をもう一度オンにしてください。", + "blocked_title": "アクセスがオフになっています", + "books_default": "自分のアドレス帳は同期されます。共有されたアドレス帳は選択した場合のみ同期されます。", + "books_hint": "自分のアドレス帳はオフにしない限り同期されます。共有されたアドレス帳はオンにした場合のみ同期されます。", + "books_label": "アドレス帳", + "calendars_default": "自分のカレンダーは同期されます。共有されたカレンダーは選択した場合のみ同期されます。", + "calendars_hint": "自分のカレンダーはオフにしない限り同期されます。共有されたカレンダーはオンにした場合のみ同期されます。タスクリストは Bulwark に残ります。", + "calendars_label": "カレンダー", + "choose": "選択", + "choose_books_title": "同期するアドレス帳", + "choose_calendars_title": "同期するカレンダー", + "collections_count": "{total}件中{selected}件を同期", + "continue": "続行", + "deletions_delete": "サーバーで削除", + "deletions_message": "このデバイスで多数の項目が一度に削除されたため、サーバーでは削除されていません。サーバーでも削除するか、このデバイスに復元してください。", + "deletions_restore": "復元", + "deletions_title": "サーバーでも削除しますか?", + "denied_calendar": "カレンダーへのアクセスがないと、Bulwark はカレンダーをこのデバイスに追加できません。", + "denied_contacts": "連絡先へのアクセスがないと、Bulwark はアドレス帳をこのデバイスに追加できません。", + "denied_title": "同期はオフのままです", + "failed_title": "同期をオンにできませんでした", + "grant_access": "アクセスを許可", + "interval_15m": "15分ごと", + "interval_1h": "1時間ごと", + "interval_30m": "30分ごと", + "interval_6h": "6時間ごと", + "interval_desc": "Android が自動で同期する頻度です。Bulwark での編集やプッシュで通知された変更は、より早く同期されます。", + "interval_label": "同期の間隔", + "interval_manual": "定期的に同期しない", + "keep_syncing": "同期を続ける", + "list_error_auth": "サーバーがサインインを受け付けませんでした。", + "list_error_network": "サーバーに接続できませんでした。", + "list_error_server": "サーバーが一覧を返せませんでした。", + "list_error_signed_out": "このデバイスではこのアカウントにサインインしていません。", + "master_off": "このデバイスでは自動同期がオフになっているため、「今すぐ同期」をタップしたときにのみ同期されます。", + "new_contacts_default": "デフォルトのアドレス帳", + "new_contacts_desc": "このデバイスの他のアプリで作成した連絡先の保存先となるアドレス帳です。", + "new_contacts_label": "新しい連絡先の保存先", + "no_books": "このアカウントにはアドレス帳がありません。", + "no_calendars": "このアカウントには同期できるカレンダーがありません。", + "ok": "OK", + "open_app_settings": "アプリの設定を開く", + "own_collections": "自分のもの", + "rationale_calendar": "カレンダーアプリにカレンダーを表示するには、Bulwark にこのデバイスのカレンダーへのアクセスを許可する必要があります。Bulwark が変更するのは自分のアカウントのカレンダーだけです。", + "rationale_contacts": "連絡帳アプリにアドレス帳を表示するには、Bulwark にこのデバイスの連絡先へのアクセスを許可する必要があります。Bulwark が変更するのは自分のアカウントの連絡先だけです。", + "rationale_title_calendar": "カレンダーへのアクセスを許可", + "rationale_title_contacts": "連絡先へのアクセスを許可", + "read_only": "読み取り専用", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "カレンダーアプリ", + "reminder_question_message": "同期した予定のリマインダーは、このデバイスのカレンダーアプリか Bulwark のどちらかから通知できます。重複して通知されないよう、どちらか一方を選んでください。後で変更できます。", + "reminder_question_title": "どちらから通知しますか?", + "reminders_desc": "同期した予定のリマインダーを通知するアプリです。もう一方は通知しないため、重複して通知されません。", + "reminders_label": "リマインダー", + "reminders_nobody": "Bulwark の予定の通知がオフになっているため、同期した予定のリマインダーは通知されません。", + "remove": "削除", + "remove_from_device": "このデバイスから削除", + "remove_message": "このアカウントの連絡先とカレンダーがこのデバイスから削除されます。ここで行った変更のうち、サーバーに届いていないものは失われます。", + "remove_title": "このデバイスから削除しますか?", + "review_deletions": "削除を確認", + "shared_by": "{name} が共有", + "sign_in_again": "再度サインイン", + "status_auth": "サーバーがサインインを受け付けなくなりました。同期を続けるには、再度サインインしてください。", + "status_cancelled": "前回の同期は途中で停止しました。次回に続きから同期します。", + "status_failed": "前回の同期に失敗しました:{message}", + "status_failed_plain": "前回の同期に失敗しました。", + "status_io": "サーバーに接続できませんでした({when})。Android が再試行します。", + "status_item_errors": "{count, plural, other {#件の項目を同期できませんでした。}}", + "status_off": "オフ", + "status_paused": "Android の設定で一時停止中", + "status_permission_calendar": "カレンダーへのアクセスがオフになったため、何も同期されません。", + "status_permission_contacts": "連絡先へのアクセスがオフになったため、何も同期されません。", + "status_removed": "Android の設定で削除されました。アカウントを再度追加するには、同期をオンにしてください。", + "status_safety_abort": "停止しました:このデバイスに項目が残っているのに、サーバーから何も返されませんでした。サーバーを確認してから、もう一度同期してください。", + "status_synced": "最終同期:{when}", + "status_syncing": "同期中…", + "status_too_many_deletions": "このデバイスで多数の項目が削除されました。確認するまでサーバーでは削除されません。", + "status_unsupported_calendar": "サーバーはこのアカウントのカレンダーを提供していません。", + "status_unsupported_contacts": "サーバーはこのアカウントのアドレス帳を提供していません。", + "status_waiting": "初回の同期を待っています", + "status_waiting_next": "次回の同期を待っています", + "suspended_desc": "サインアウトしました。このアカウントの連絡先とカレンダーは、ここで行った変更とともにこのデバイスに残り、サインインすると再び同期されます。", + "switch_label": "{account} をこのデバイスに同期", + "sync_now": "今すぐ同期", + "turn_off_anyway": "オフにする", + "turn_off_failed": "同期したデータをこのデバイスから削除できませんでした。同期は一時停止しています。後でもう一度お試しください。", + "turn_off_failed_title": "同期をオフにできませんでした", + "turn_off_pending": "{count, plural, other {このデバイスで行った#件の変更がまだサーバーに届いていません。今同期をオフにすると失われます。}}", + "turn_off_title": "それでもオフにしますか?", + "turn_off_unknown": "このデバイスで行った変更がサーバーに届いたかどうかを Bulwark で確認できませんでした。今同期をオフにすると失われます。", + "turning_off": "変更をアップロードしてから、このデバイスのデータを削除しています…", + "turning_on": "設定しています…" + }, "downloads": { "attachment_template": { "description_mobile": "添付ファイルを保存または共有するときに使用するテンプレートです。" diff --git a/locales/rn/ko.json b/locales/rn/ko.json index e6f26abf..b32fa6f1 100644 --- a/locales/rn/ko.json +++ b/locales/rn/ko.json @@ -93,6 +93,8 @@ "until_date": "{date}까지" }, "settings": { + "device_sync_desc": "캘린더 앱과 이 기기의 다른 앱에 내 캘린더를 표시해요. 변경 사항은 양방향으로 동기화돼요.", + "device_sync_title": "이 기기에 동기화", "show_week_numbers_mobile_desc": "월간 보기에 주 차 번호를 표시해요.", "time_zone": "시간대", "time_zone_auto": "기기 시간대", @@ -249,6 +251,18 @@ "color_tag": "태그", "remove_color": "태그 제거" }, + "device_sync": { + "auth_problem_text": "서버가 더 이상 {account} 계정의 로그인을 허용하지 않아서 이 기기에 있는 이 계정의 연락처와 캘린더 동기화가 중단되었어요. 다시 시작하려면 다시 로그인해 주세요. 이 기기에서 변경한 내용은 유지돼요.", + "auth_problem_title": "계속 동기화하려면 다시 로그인해 주세요", + "problem_channel": "동기화 문제", + "sign_out_anyway": "로그아웃", + "sign_out_pending_message": "{count, plural, other {이 기기에서 변경한 내용 #개가 서버에 전달되지 않았어요. 지금 로그아웃하면 사라져요.}}", + "sign_out_pending_title": "그래도 로그아웃할까요?", + "sign_out_unknown_message": "이 기기에서 변경한 내용이 서버에 전달되지 않았을 수 있어요. 지금 로그아웃하면 사라져요.", + "signed_out_text": "{account} 계정에서 로그아웃되어 이 기기에 있는 이 계정의 연락처와 캘린더가 더 이상 동기화되지 않아요. 다시 시작하려면 다시 로그인해 주세요. 이 기기에서 변경한 내용은 유지돼요.", + "signed_out_title": "계속 동기화하려면 로그인해 주세요", + "signing_out": "동기화된 연락처와 캘린더를 이 기기에서 삭제하는 중…" + }, "email_composer": { "attach_camera": "카메라", "attachment_reminder_body": "메시지에 \"{keyword}\"이(가) 언급되어 있지만 첨부된 파일이 없어요. 그래도 보낼까요?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, other {연락처 #개}}", "default_book": "기본 주소록", "description_mobile": "표시 설정과 주소록 도구예요.", + "device_sync_desc": "연락처 앱과 이 기기의 다른 앱에 내 주소록을 표시해요. 변경 사항은 양방향으로 동기화돼요.", + "device_sync_title": "이 기기에 동기화", "export_description_count": "{count, plural, other {연락처 #개를 하나의 vCard 파일로 내보내요.}}", "export_empty": "내보낼 연락처가 없어요", "import_description_mobile": "vCard(.vcf) 파일에서 연락처를 가져오고 중복을 감지해요.", "manage_description_mobile": "연락처를 정리하는 주소록을 만들고, 이름을 바꾸고, 삭제할 수 있어요." }, + "device_sync": { + "also_synced_by": "{accounts} 계정에서도 동기화되어 이 기기에 두 번 표시돼요.", + "android_settings": "Android 계정 설정", + "blocked_calendar": "Android가 더 이상 캘린더 접근 권한을 요청하지 않아요. 앱 설정에서 허용한 후 동기화를 다시 켜 주세요.", + "blocked_contacts": "Android가 더 이상 연락처 접근 권한을 요청하지 않아요. 앱 설정에서 허용한 후 동기화를 다시 켜 주세요.", + "blocked_title": "접근 권한이 꺼져 있어요", + "books_default": "내 주소록은 동기화되고, 공유된 주소록은 선택한 경우에만 동기화돼요.", + "books_hint": "내 주소록은 끄지 않는 한 동기화되고, 공유된 주소록은 켠 경우에만 동기화돼요.", + "books_label": "주소록", + "calendars_default": "내 캘린더는 동기화되고, 공유된 캘린더는 선택한 경우에만 동기화돼요.", + "calendars_hint": "내 캘린더는 끄지 않는 한 동기화되고, 공유된 캘린더는 켠 경우에만 동기화돼요. 할 일 목록은 Bulwark에 남아요.", + "calendars_label": "캘린더", + "choose": "선택", + "choose_books_title": "동기화할 주소록", + "choose_calendars_title": "동기화할 캘린더", + "collections_count": "{total}개 중 {selected}개 동기화", + "continue": "계속", + "deletions_delete": "서버에서 삭제", + "deletions_message": "이 기기에서 많은 항목이 한꺼번에 삭제되어 서버에서는 삭제되지 않았어요. 서버에서도 삭제하거나 이 기기에 다시 복원해 주세요.", + "deletions_restore": "복원", + "deletions_title": "서버에서도 삭제할까요?", + "denied_calendar": "캘린더 접근 권한이 없으면 Bulwark가 내 캘린더를 이 기기에 추가할 수 없어요.", + "denied_contacts": "연락처 접근 권한이 없으면 Bulwark가 내 주소록을 이 기기에 추가할 수 없어요.", + "denied_title": "동기화가 꺼진 상태로 유지돼요", + "failed_title": "동기화를 켤 수 없어요", + "grant_access": "접근 권한 허용", + "interval_15m": "15분마다", + "interval_1h": "1시간마다", + "interval_30m": "30분마다", + "interval_6h": "6시간마다", + "interval_desc": "Android가 자동으로 동기화하는 주기예요. Bulwark에서 수정한 내용과 푸시로 알려진 변경 사항은 더 빨리 동기화돼요.", + "interval_label": "동기화 주기", + "interval_manual": "정기 동기화 안 함", + "keep_syncing": "계속 동기화", + "list_error_auth": "서버가 로그인을 허용하지 않았어요.", + "list_error_network": "서버에 연결할 수 없어요.", + "list_error_server": "서버에서 목록을 가져올 수 없어요.", + "list_error_signed_out": "이 기기에서 이 계정에 로그인되어 있지 않아요.", + "master_off": "이 기기에서 자동 동기화가 꺼져 있어서 \"지금 동기화\"를 탭할 때만 동기화돼요.", + "new_contacts_default": "기본 주소록", + "new_contacts_desc": "이 기기의 다른 앱에서 만든 연락처를 저장할 주소록이에요.", + "new_contacts_label": "새 연락처 저장 위치", + "no_books": "이 계정에는 주소록이 없어요.", + "no_calendars": "이 계정에는 동기화할 캘린더가 없어요.", + "ok": "확인", + "open_app_settings": "앱 설정 열기", + "own_collections": "내 항목", + "rationale_calendar": "캘린더 앱에 내 캘린더를 표시하려면 Bulwark에 이 기기의 캘린더 접근 권한이 필요해요. Bulwark는 자기 계정의 캘린더만 변경해요.", + "rationale_contacts": "연락처 앱에 내 주소록을 표시하려면 Bulwark에 이 기기의 연락처 접근 권한이 필요해요. Bulwark는 자기 계정의 연락처만 변경해요.", + "rationale_title_calendar": "캘린더 접근 허용", + "rationale_title_contacts": "연락처 접근 허용", + "read_only": "읽기 전용", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "캘린더 앱", + "reminder_question_message": "동기화된 일정 알림은 이 기기의 캘린더 앱이나 Bulwark에서 받을 수 있어요. 알림을 두 번 받지 않도록 하나를 선택해 주세요. 나중에 변경할 수 있어요.", + "reminder_question_title": "어디에서 알림을 받을까요?", + "reminders_desc": "동기화된 일정 알림을 보내는 앱이에요. 다른 쪽은 알림을 보내지 않아 알림을 두 번 받지 않아요.", + "reminders_label": "알림", + "reminders_nobody": "Bulwark의 일정 알림이 꺼져 있어서 동기화된 일정 알림을 받을 수 없어요.", + "remove": "제거", + "remove_from_device": "이 기기에서 제거", + "remove_message": "이 계정의 연락처와 캘린더가 이 기기에서 제거돼요. 여기에서 변경한 내용 중 서버에 전달되지 않은 내용은 사라져요.", + "remove_title": "이 기기에서 제거할까요?", + "review_deletions": "삭제 항목 검토", + "shared_by": "{name} 님이 공유함", + "sign_in_again": "다시 로그인", + "status_auth": "서버가 더 이상 로그인을 허용하지 않아요. 계속 동기화하려면 다시 로그인해 주세요.", + "status_cancelled": "마지막 동기화가 도중에 멈췄어요. 다음번에 이어서 진행해요.", + "status_failed": "마지막 동기화 실패: {message}", + "status_failed_plain": "마지막 동기화에 실패했어요.", + "status_io": "서버에 연결할 수 없었어요({when}). Android가 다시 시도해요.", + "status_item_errors": "{count, plural, other {항목 #개를 동기화할 수 없었어요.}}", + "status_off": "끔", + "status_paused": "Android 설정에서 일시중지됨", + "status_permission_calendar": "캘린더 접근 권한이 꺼져서 아무것도 동기화되지 않아요.", + "status_permission_contacts": "연락처 접근 권한이 꺼져서 아무것도 동기화되지 않아요.", + "status_removed": "Android 설정에서 삭제되었어요. 계정을 다시 추가하려면 동기화를 켜 주세요.", + "status_safety_abort": "중지됨: 이 기기에 아직 항목이 있는데 서버가 아무것도 반환하지 않았어요. 서버를 확인한 후 다시 동기화해 주세요.", + "status_synced": "마지막 동기화: {when}", + "status_syncing": "동기화 중…", + "status_too_many_deletions": "이 기기에서 많은 항목이 삭제되었어요. 확인하기 전까지 서버에서는 삭제되지 않아요.", + "status_unsupported_calendar": "서버가 이 계정에 캘린더를 제공하지 않아요.", + "status_unsupported_contacts": "서버가 이 계정에 주소록을 제공하지 않아요.", + "status_waiting": "첫 동기화를 기다리는 중", + "status_waiting_next": "다음 동기화를 기다리는 중", + "suspended_desc": "로그아웃됨. 이 계정의 연락처와 캘린더는 여기에서 변경한 내용과 함께 이 기기에 남아 있고, 로그인하면 다시 동기화돼요.", + "switch_label": "{account} 계정을 이 기기에 동기화", + "sync_now": "지금 동기화", + "turn_off_anyway": "끄기", + "turn_off_failed": "동기화된 데이터를 이 기기에서 제거할 수 없었어요. 동기화가 일시중지되었어요. 나중에 다시 시도해 주세요.", + "turn_off_failed_title": "동기화를 끌 수 없어요", + "turn_off_pending": "{count, plural, other {이 기기에서 변경한 내용 #개가 아직 서버에 전달되지 않았어요. 지금 동기화를 끄면 사라져요.}}", + "turn_off_title": "그래도 끌까요?", + "turn_off_unknown": "이 기기에서 변경한 내용이 서버에 전달되었는지 Bulwark에서 확인할 수 없었어요. 지금 동기화를 끄면 사라져요.", + "turning_off": "변경 사항을 업로드한 후 이 기기에서 데이터를 제거하는 중…", + "turning_on": "설정하는 중…" + }, "downloads": { "attachment_template": { "description_mobile": "첨부 파일을 저장하거나 공유할 때 사용하는 템플릿이에요." diff --git a/locales/rn/lv.json b/locales/rn/lv.json index e5c449c7..ea5131ef 100644 --- a/locales/rn/lv.json +++ b/locales/rn/lv.json @@ -93,6 +93,8 @@ "until_date": "Līdz {date}" }, "settings": { + "device_sync_desc": "Rādiet savus kalendārus lietotnē Kalendārs un citās šīs ierīces lietotnēs. Izmaiņas tiek sinhronizētas abos virzienos.", + "device_sync_title": "Sinhronizēt ar šo ierīci", "show_week_numbers_mobile_desc": "Rādīt nedēļu numurus mēneša skatā.", "time_zone": "Laika josla", "time_zone_auto": "Ierīces laika josla", @@ -249,6 +251,18 @@ "color_tag": "Tags", "remove_color": "Noņemt tagu" }, + "device_sync": { + "auth_problem_text": "Serveris vairs nepieņem konta {account} pierakstīšanos, tāpēc tā kontakti un kalendāri šajā ierīcē vairs netiek sinhronizēti. Lai turpinātu, pierakstieties vēlreiz. Šajā ierīcē veiktās izmaiņas tiek saglabātas.", + "auth_problem_title": "Pierakstieties vēlreiz, lai turpinātu sinhronizāciju", + "problem_channel": "Sinhronizācijas problēmas", + "sign_out_anyway": "Izrakstīties", + "sign_out_pending_message": "{count, plural, zero {# izmaiņas, kas veiktas šajā ierīcē, nav nonākušas serverī. Tās tiks zaudētas, ja tagad izrakstīsieties.} one {# izmaiņa, kas veikta šajā ierīcē, nav nonākusi serverī. Tā tiks zaudēta, ja tagad izrakstīsieties.} other {# izmaiņas, kas veiktas šajā ierīcē, nav nonākušas serverī. Tās tiks zaudētas, ja tagad izrakstīsieties.}}", + "sign_out_pending_title": "Tomēr izrakstīties?", + "sign_out_unknown_message": "Šajā ierīcē veiktās izmaiņas, iespējams, nav nonākušas serverī. Tās tiks zaudētas, ja tagad izrakstīsieties.", + "signed_out_text": "Konts {account} tika izrakstīts, tāpēc tā kontakti un kalendāri šajā ierīcē vairs netiek sinhronizēti. Lai turpinātu, pierakstieties vēlreiz. Šajā ierīcē veiktās izmaiņas tiek saglabātas.", + "signed_out_title": "Pierakstieties, lai turpinātu sinhronizāciju", + "signing_out": "Sinhronizētie kontakti un kalendāri tiek noņemti no šīs ierīces…" + }, "email_composer": { "attach_camera": "Kamera", "attachment_reminder_body": "Jūsu ziņojumā minēts \"{keyword}\", bet nav pievienots neviens fails. Vai tomēr sūtīt?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, zero {# kontakti} one {# kontakts} other {# kontakti}}", "default_book": "Noklusējuma adrešu grāmata", "description_mobile": "Attēlošanas preferences un adrešu grāmatu rīki.", + "device_sync_desc": "Rādiet savas adrešu grāmatas lietotnē Kontakti un citās šīs ierīces lietotnēs. Izmaiņas tiek sinhronizētas abos virzienos.", + "device_sync_title": "Sinhronizēt ar šo ierīci", "export_description_count": "{count, plural, zero {Eksportēt # kontaktus vienā vCard failā.} one {Eksportēt # kontaktu vienā vCard failā.} other {Eksportēt # kontaktus vienā vCard failā.}}", "export_empty": "Nav kontaktu, ko eksportēt", "import_description_mobile": "Importēt kontaktus no vCard (.vcf) faila ar dublikātu noteikšanu.", "manage_description_mobile": "Izveidojiet, pārdēvējiet un noņemiet adrešu grāmatas, kurās kārtoti jūsu kontakti." }, + "device_sync": { + "also_synced_by": "Sinhronizējas arī kontiem {accounts}, tāpēc šajā ierīcē parādās divreiz.", + "android_settings": "Android konta iestatījumi", + "blocked_calendar": "Android vairs neprasa piekļuvi kalendāriem. Atļaujiet to lietotnes iestatījumos un pēc tam atkal ieslēdziet sinhronizāciju.", + "blocked_contacts": "Android vairs neprasa piekļuvi kontaktiem. Atļaujiet to lietotnes iestatījumos un pēc tam atkal ieslēdziet sinhronizāciju.", + "blocked_title": "Piekļuve ir izslēgta", + "books_default": "Jūsu adrešu grāmatas tiek sinhronizētas, koplietotās — tikai tad, ja tās izvēlaties.", + "books_hint": "Jūsu adrešu grāmatas tiek sinhronizētas, ja vien tās neizslēdzat; koplietotās — tikai tad, ja tās ieslēdzat.", + "books_label": "Adrešu grāmatas", + "calendars_default": "Jūsu kalendāri tiek sinhronizēti, koplietotie — tikai tad, ja tos izvēlaties.", + "calendars_hint": "Jūsu kalendāri tiek sinhronizēti, ja vien tos neizslēdzat; koplietotie — tikai tad, ja tos ieslēdzat. Uzdevumu saraksti paliek Bulwark.", + "calendars_label": "Kalendāri", + "choose": "Izvēlēties", + "choose_books_title": "Sinhronizējamās adrešu grāmatas", + "choose_calendars_title": "Sinhronizējamie kalendāri", + "collections_count": "Sinhronizē: {selected} no {total}", + "continue": "Turpināt", + "deletions_delete": "Dzēst serverī", + "deletions_message": "Šajā ierīcē vienlaikus tika izdzēsts daudz vienumu, tāpēc tie netika izdzēsti serverī. Izdzēsiet tos arī tur vai atjaunojiet tos šajā ierīcē.", + "deletions_restore": "Atjaunot", + "deletions_title": "Vai dzēst tos arī serverī?", + "denied_calendar": "Bez piekļuves kalendāriem Bulwark nevar ievietot jūsu kalendārus šajā ierīcē.", + "denied_contacts": "Bez piekļuves kontaktiem Bulwark nevar ievietot jūsu adrešu grāmatas šajā ierīcē.", + "denied_title": "Sinhronizācija paliek izslēgta", + "failed_title": "Sinhronizāciju neizdevās ieslēgt", + "grant_access": "Piešķirt piekļuvi", + "interval_15m": "Ik pēc 15 minūtēm", + "interval_1h": "Ik pēc stundas", + "interval_30m": "Ik pēc 30 minūtēm", + "interval_6h": "Ik pēc 6 stundām", + "interval_desc": "Cik bieži Android sinhronizē patstāvīgi. Labojumi Bulwark un ar push paziņotās izmaiņas tiek sinhronizēti ātrāk.", + "interval_label": "Sinhronizācijas intervāls", + "interval_manual": "Bez regulāras sinhronizācijas", + "keep_syncing": "Turpināt sinhronizāciju", + "list_error_auth": "Serveris nepieņēma pierakstīšanos.", + "list_error_network": "Neizdevās sazināties ar serveri.", + "list_error_server": "Serveris nespēja sniegt sarakstu.", + "list_error_signed_out": "Šis konts šajā ierīcē nav pierakstīts.", + "master_off": "Automātiskā sinhronizācija šajā ierīcē ir izslēgta, tāpēc sinhronizācija notiek tikai tad, kad pieskaraties \"Sinhronizēt tagad\".", + "new_contacts_default": "Noklusējuma adrešu grāmata", + "new_contacts_desc": "Adrešu grāmata kontaktiem, kas izveidoti citās šīs ierīces lietotnēs.", + "new_contacts_label": "Kur saglabāt jaunos kontaktus", + "no_books": "Šim kontam nav adrešu grāmatu.", + "no_calendars": "Šim kontam nav sinhronizējamu kalendāru.", + "ok": "Labi", + "open_app_settings": "Atvērt iestatījumus", + "own_collections": "Jūsu", + "rationale_calendar": "Lai rādītu jūsu kalendārus lietotnē Kalendārs, Bulwark nepieciešama piekļuve šīs ierīces kalendāriem. Bulwark maina tikai sava konta kalendārus.", + "rationale_contacts": "Lai rādītu jūsu adrešu grāmatas lietotnē Kontakti, Bulwark nepieciešama piekļuve šīs ierīces kontaktiem. Bulwark maina tikai sava konta kontaktus.", + "rationale_title_calendar": "Atļaut piekļuvi kalendāriem", + "rationale_title_contacts": "Atļaut piekļuvi kontaktiem", + "read_only": "Tikai lasāms", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Lietotne Kalendārs", + "reminder_question_message": "Atgādinājumus par sinhronizētajiem notikumiem var sūtīt šīs ierīces kalendāra lietotne vai Bulwark. Izvēlieties vienu, lai nesaņemtu atgādinājumus divreiz. Vēlāk to var mainīt.", + "reminder_question_title": "Kas jums atgādinās?", + "reminders_desc": "Kas jums atgādina par sinhronizētajiem notikumiem. Otrs klusē, lai jūs nesaņemtu atgādinājumus divreiz.", + "reminders_label": "Atgādinājumi", + "reminders_nobody": "Bulwark notikumu paziņojumi ir izslēgti, tāpēc nekas neatgādina par sinhronizētajiem notikumiem.", + "remove": "Noņemt", + "remove_from_device": "Noņemt no šīs ierīces", + "remove_message": "Konta kontakti un kalendāri tiks noņemti no šīs ierīces. Šeit veiktās izmaiņas, kas nav nonākušas serverī, tiks zaudētas.", + "remove_title": "Vai noņemt no šīs ierīces?", + "review_deletions": "Pārskatīt dzēšanas", + "shared_by": "Kopīgoja: {name}", + "sign_in_again": "Pierakstīties vēlreiz", + "status_auth": "Serveris vairs nepieņem pierakstīšanos. Pierakstieties vēlreiz, lai turpinātu sinhronizāciju.", + "status_cancelled": "Pēdējā sinhronizācija apstājās priekšlaicīgi. Nākamreiz tā turpināsies.", + "status_failed": "Pēdējā sinhronizācija neizdevās: {message}", + "status_failed_plain": "Pēdējā sinhronizācija neizdevās.", + "status_io": "Serveris {when} nebija sasniedzams. Android mēģinās vēlreiz.", + "status_item_errors": "{count, plural, zero {# vienumus neizdevās sinhronizēt.} one {# vienumu neizdevās sinhronizēt.} other {# vienumus neizdevās sinhronizēt.}}", + "status_off": "Izslēgta", + "status_paused": "Apturēta Android iestatījumos", + "status_permission_calendar": "Piekļuve kalendāriem tika izslēgta, tāpēc nekas netiek sinhronizēts.", + "status_permission_contacts": "Piekļuve kontaktiem tika izslēgta, tāpēc nekas netiek sinhronizēts.", + "status_removed": "Noņemts Android iestatījumos. Ieslēdziet sinhronizāciju, lai kontu pievienotu vēlreiz.", + "status_safety_abort": "Apturēts: serveris neatgrieza neko, lai gan šajā ierīcē joprojām ir vienumi. Pārbaudiet serveri un pēc tam sinhronizējiet vēlreiz.", + "status_synced": "Sinhronizēts {when}", + "status_syncing": "Sinhronizē…", + "status_too_many_deletions": "Šajā ierīcē tika izdzēsts daudz vienumu. Serverī tie netiks izdzēsti, kamēr jūs to neapstiprināsiet.", + "status_unsupported_calendar": "Serveris šim kontam nepiedāvā kalendārus.", + "status_unsupported_contacts": "Serveris šim kontam nepiedāvā adrešu grāmatas.", + "status_waiting": "Gaida pirmo sinhronizāciju", + "status_waiting_next": "Gaida nākamo sinhronizāciju", + "suspended_desc": "Izrakstīts. Konta kontakti un kalendāri paliek šajā ierīcē kopā ar šeit veiktajām izmaiņām un tiks atkal sinhronizēti, tiklīdz pierakstīsieties.", + "switch_label": "Sinhronizēt {account} ar šo ierīci", + "sync_now": "Sinhronizēt tagad", + "turn_off_anyway": "Izslēgt", + "turn_off_failed": "Sinhronizētos datus neizdevās noņemt no šīs ierīces. Sinhronizācija ir apturēta; vēlāk mēģiniet vēlreiz.", + "turn_off_failed_title": "Sinhronizāciju neizdevās izslēgt", + "turn_off_pending": "{count, plural, zero {# izmaiņas, kas veiktas šajā ierīcē, vēl nav nonākušas serverī. Tās tiks zaudētas, ja tagad izslēgsiet sinhronizāciju.} one {# izmaiņa, kas veikta šajā ierīcē, vēl nav nonākusi serverī. Tā tiks zaudēta, ja tagad izslēgsiet sinhronizāciju.} other {# izmaiņas, kas veiktas šajā ierīcē, vēl nav nonākušas serverī. Tās tiks zaudētas, ja tagad izslēgsiet sinhronizāciju.}}", + "turn_off_title": "Tomēr izslēgt?", + "turn_off_unknown": "Bulwark nevarēja pārbaudīt, vai šajā ierīcē veiktās izmaiņas ir nonākušas serverī. Tās tiks zaudētas, ja tagad izslēgsiet sinhronizāciju.", + "turning_off": "Augšupielādē izmaiņas, pēc tam noņem datus no šīs ierīces…", + "turning_on": "Notiek iestatīšana…" + }, "downloads": { "attachment_template": { "description_mobile": "Veidne, ko izmanto, saglabājot vai kopīgojot pielikumu." diff --git a/locales/rn/mn.json b/locales/rn/mn.json index eaf3f827..c6df1948 100644 --- a/locales/rn/mn.json +++ b/locales/rn/mn.json @@ -93,6 +93,8 @@ "until_date": "{date} хүртэл" }, "settings": { + "device_sync_desc": "Хуанлиудаа энэ төхөөрөмжийн Хуанли апп болон бусад аппуудад харуулна. Өөрчлөлтүүд хоёр чиглэлд синк хийгдэнэ.", + "device_sync_title": "Энэ төхөөрөмжтэй синк хийх", "show_week_numbers_mobile_desc": "Сарын харагдацад долоо хоногийн дугаарыг харуулах.", "time_zone": "Цагийн бүс", "time_zone_auto": "Төхөөрөмжийн цагийн бүс", @@ -249,6 +251,18 @@ "color_tag": "Таг", "remove_color": "Тагийг хасах" }, + "device_sync": { + "auth_problem_text": "Сервер {account} бүртгэлийн нэвтрэлтийг хүлээн авахаа больсон тул энэ төхөөрөмж дээрх түүний харилцагчид болон хуанлиуд синк хийгдэхээ болилоо. Үргэлжлүүлэхийн тулд дахин нэвтэрнэ үү. Энэ төхөөрөмж дээр хийсэн өөрчлөлтүүд хадгалагдана.", + "auth_problem_title": "Синкийг үргэлжлүүлэхийн тулд дахин нэвтэрнэ үү", + "problem_channel": "Синкийн асуудлууд", + "sign_out_anyway": "Гарах", + "sign_out_pending_message": "{count, plural, one {Энэ төхөөрөмж дээр хийсэн # өөрчлөлт серверт хүрээгүй байна. Одоо гарвал энэ нь алга болно.} other {Энэ төхөөрөмж дээр хийсэн # өөрчлөлт серверт хүрээгүй байна. Одоо гарвал эдгээр нь алга болно.}}", + "sign_out_pending_title": "Ямар ч байсан гарах уу?", + "sign_out_unknown_message": "Энэ төхөөрөмж дээр хийсэн өөрчлөлтүүд серверт хүрээгүй байж магадгүй. Одоо гарвал тэдгээр нь алга болно.", + "signed_out_text": "{account} бүртгэлээс гарсан тул энэ төхөөрөмж дээрх түүний харилцагчид болон хуанлиуд синк хийгдэхээ болилоо. Үргэлжлүүлэхийн тулд дахин нэвтэрнэ үү. Энэ төхөөрөмж дээр хийсэн өөрчлөлтүүд хадгалагдана.", + "signed_out_title": "Синкийг үргэлжлүүлэхийн тулд нэвтэрнэ үү", + "signing_out": "Синк хийсэн харилцагчид болон хуанлиудыг энэ төхөөрөмжөөс устгаж байна…" + }, "email_composer": { "attach_camera": "Камер", "attachment_reminder_body": "Таны имэйлд \"{keyword}\" гэж дурдсан ч файл хавсаргаагүй байна. Ямар ч байсан илгээх үү?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# харилцагч} other {# харилцагч}}", "default_book": "Өгөгдмөл хаягийн дэвтэр", "description_mobile": "Харагдах байдлын тохиргоо болон хаягийн дэвтрийн хэрэгслүүд.", + "device_sync_desc": "Хаягийн дэвтрүүдээ энэ төхөөрөмжийн Харилцагчид апп болон бусад аппуудад харуулна. Өөрчлөлтүүд хоёр чиглэлд синк хийгдэнэ.", + "device_sync_title": "Энэ төхөөрөмжтэй синк хийх", "export_description_count": "{count, plural, one {# харилцагчийг нэг vCard файл болгон экспортлох.} other {# харилцагчийг нэг vCard файл болгон экспортлох.}}", "export_empty": "Экспортлох харилцагч алга", "import_description_mobile": "vCard (.vcf) файлаас давхардлыг илрүүлэн харилцагчдыг импортлох.", "manage_description_mobile": "Харилцагчдаа цэгцлэх хаягийн дэвтрүүдийг үүсгэх, нэрийг нь өөрчлөх, устгах." }, + "device_sync": { + "also_synced_by": "{accounts} бүртгэлд мөн синк хийгддэг тул энэ төхөөрөмж дээр хоёр удаа харагдана.", + "android_settings": "Android бүртгэлийн тохиргоо", + "blocked_calendar": "Android хуанлид хандах зөвшөөрөл дахин асуухгүй. Аппын тохиргооноос зөвшөөрөөд синкийг дахин асаана уу.", + "blocked_contacts": "Android харилцагчдад хандах зөвшөөрөл дахин асуухгүй. Аппын тохиргооноос зөвшөөрөөд синкийг дахин асаана уу.", + "blocked_title": "Хандалт унтарсан байна", + "books_default": "Таны өөрийн хаягийн дэвтрүүд синк хийгдэнэ; хуваалцсан нь зөвхөн та сонгосон үед.", + "books_hint": "Таны өөрийн хаягийн дэвтрүүд таныг унтраахаас бусад тохиолдолд синк хийгдэнэ; хуваалцсан нь зөвхөн та асаасан үед.", + "books_label": "Хаягийн дэвтрүүд", + "calendars_default": "Таны өөрийн хуанлиуд синк хийгдэнэ; хуваалцсан нь зөвхөн та сонгосон үед.", + "calendars_hint": "Таны өөрийн хуанлиуд таныг унтраахаас бусад тохиолдолд синк хийгдэнэ; хуваалцсан нь зөвхөн та асаасан үед. Даалгаврын жагсаалтууд Bulwark-д үлдэнэ.", + "calendars_label": "Хуанлиуд", + "choose": "Сонгох", + "choose_books_title": "Синк хийх хаягийн дэвтрүүд", + "choose_calendars_title": "Синк хийх хуанлиуд", + "collections_count": "Синк хийгдэж буй: {selected}/{total}", + "continue": "Үргэлжлүүлэх", + "deletions_delete": "Сервер дээр устгах", + "deletions_message": "Энэ төхөөрөмж дээр олон зүйлийг нэг дор устгасан тул сервер дээр устгаагүй. Тэдгээрийг тэнд ч мөн устгах эсвэл энэ төхөөрөмж дээр сэргээнэ үү.", + "deletions_restore": "Сэргээх", + "deletions_title": "Сервер дээр ч мөн устгах уу?", + "denied_calendar": "Хуанлид хандах эрхгүйгээр Bulwark таны хуанлиудыг энэ төхөөрөмж дээр байршуулж чадахгүй.", + "denied_contacts": "Харилцагчдад хандах эрхгүйгээр Bulwark таны хаягийн дэвтрүүдийг энэ төхөөрөмж дээр байршуулж чадахгүй.", + "denied_title": "Синк унтраалттай хэвээр байна", + "failed_title": "Синкийг асааж чадсангүй", + "grant_access": "Хандалт олгох", + "interval_15m": "15 минут тутамд", + "interval_1h": "Цаг бүр", + "interval_30m": "30 минут тутамд", + "interval_6h": "6 цаг тутамд", + "interval_desc": "Android өөрөө хэр олон удаа синк хийхийг заана. Bulwark дахь засварууд болон push-аар мэдэгдсэн өөрчлөлтүүд илүү эрт синк хийгдэнэ.", + "interval_label": "Синкийн давтамж", + "interval_manual": "Тогтмол синк хийхгүй", + "keep_syncing": "Синкийг үргэлжлүүлэх", + "list_error_auth": "Сервер нэвтрэлтийг хүлээн аваагүй.", + "list_error_network": "Сервертэй холбогдож чадсангүй.", + "list_error_server": "Сервер жагсаалтыг гаргаж чадсангүй.", + "list_error_signed_out": "Энэ төхөөрөмж дээр энэ бүртгэлээр нэвтрээгүй байна.", + "master_off": "Энэ төхөөрөмж дээр автомат синк унтарсан байгаа тул зөвхөн \"Одоо синк хийх\" дээр товшиход синк хийгдэнэ.", + "new_contacts_default": "Өгөгдмөл хаягийн дэвтэр", + "new_contacts_desc": "Энэ төхөөрөмжийн бусад аппад үүсгэсэн харилцагчдыг хадгалах хаягийн дэвтэр.", + "new_contacts_label": "Шинэ харилцагчдыг хадгалах газар", + "no_books": "Энэ бүртгэлд хаягийн дэвтэр алга.", + "no_calendars": "Энэ бүртгэлд синк хийх хуанли алга.", + "ok": "OK", + "open_app_settings": "Аппын тохиргоог нээх", + "own_collections": "Таных", + "rationale_calendar": "Таны хуанлиудыг Хуанли аппад харуулахын тулд Bulwark-д энэ төхөөрөмжийн хуанлид хандах эрх хэрэгтэй. Энэ нь зөвхөн өөрийн бүртгэлийн хуанлиудыг өөрчилнө.", + "rationale_contacts": "Таны хаягийн дэвтрүүдийг Харилцагчид аппад харуулахын тулд Bulwark-д энэ төхөөрөмжийн харилцагчдад хандах эрх хэрэгтэй. Энэ нь зөвхөн өөрийн бүртгэлийн харилцагчдыг өөрчилнө.", + "rationale_title_calendar": "Хуанлид хандахыг зөвшөөрөх", + "rationale_title_contacts": "Харилцагчдад хандахыг зөвшөөрөх", + "read_only": "Зөвхөн унших", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Хуанли апп", + "reminder_question_message": "Синк хийсэн үйл явдлын сануулгыг энэ төхөөрөмжийн хуанли апп эсвэл Bulwark илгээж болно. Сануулга хоёр удаа ирэхгүйн тулд аль нэгийг нь сонгоно уу. Үүнийг дараа нь өөрчилж болно.", + "reminder_question_title": "Хэн танд сануулах вэ?", + "reminders_desc": "Синк хийсэн үйл явдлын талаар хэн танд сануулах. Нөгөө нь чимээгүй байх тул сануулга хоёр удаа ирэхгүй.", + "reminders_label": "Сануулгууд", + "reminders_nobody": "Bulwark-ын үйл явдлын мэдэгдэл унтарсан тул синк хийсэн үйл явдлын талаар юу ч танд сануулахгүй.", + "remove": "Хасах", + "remove_from_device": "Энэ төхөөрөмжөөс хасах", + "remove_message": "Бүртгэлийн харилцагчид болон хуанлиуд энэ төхөөрөмжөөс хасагдана. Энд хийсэн, серверт хүрээгүй өөрчлөлтүүд алга болно.", + "remove_title": "Энэ төхөөрөмжөөс хасах уу?", + "review_deletions": "Устгалуудыг хянах", + "shared_by": "Хуваалцсан: {name}", + "sign_in_again": "Дахин нэвтрэх", + "status_auth": "Сервер нэвтрэлтийг хүлээн авахаа больсон. Синкийг үргэлжлүүлэхийн тулд дахин нэвтэрнэ үү.", + "status_cancelled": "Сүүлийн синк эрт зогссон. Дараагийн удаа үргэлжилнэ.", + "status_failed": "Сүүлийн синк амжилтгүй: {message}", + "status_failed_plain": "Сүүлийн синк амжилтгүй боллоо.", + "status_io": "Сервертэй холбогдож чадсангүй ({when}). Android дахин оролдоно.", + "status_item_errors": "{count, plural, one {# зүйлийг синк хийж чадсангүй.} other {# зүйлийг синк хийж чадсангүй.}}", + "status_off": "Унтраалттай", + "status_paused": "Android-ийн тохиргоонд түр зогсоосон", + "status_permission_calendar": "Хуанлид хандах эрх унтарсан тул юу ч синк хийгдэхгүй.", + "status_permission_contacts": "Харилцагчдад хандах эрх унтарсан тул юу ч синк хийгдэхгүй.", + "status_removed": "Android-ийн тохиргооноос хасагдсан. Бүртгэлийг дахин нэмэхийн тулд синкийг асаана уу.", + "status_safety_abort": "Зогссон: энэ төхөөрөмж дээр зүйлс байсаар байхад сервер юу ч буцаасангүй. Серверийг шалгаад дахин синк хийнэ үү.", + "status_synced": "Сүүлийн синк: {when}", + "status_syncing": "Синк хийж байна…", + "status_too_many_deletions": "Энэ төхөөрөмж дээр олон зүйл устгагдсан. Таныг баталгаажуулах хүртэл сервер дээр устгахгүй.", + "status_unsupported_calendar": "Сервер энэ бүртгэлд хуанли санал болгодоггүй.", + "status_unsupported_contacts": "Сервер энэ бүртгэлд хаягийн дэвтэр санал болгодоггүй.", + "status_waiting": "Анхны синкийг хүлээж байна", + "status_waiting_next": "Дараагийн синкийг хүлээж байна", + "suspended_desc": "Гарсан. Түүний харилцагчид болон хуанлиуд энд хийсэн өөрчлөлтүүдийн хамт энэ төхөөрөмж дээр үлдэх бөгөөд таныг нэвтэрмэгц дахин синк хийгдэнэ.", + "switch_label": "{account} бүртгэлийг энэ төхөөрөмжтэй синк хийх", + "sync_now": "Одоо синк хийх", + "turn_off_anyway": "Унтраах", + "turn_off_failed": "Синк хийсэн өгөгдлийг энэ төхөөрөмжөөс хасаж чадсангүй. Синк түр зогссон; дараа дахин оролдоно уу.", + "turn_off_failed_title": "Синкийг унтрааж чадсангүй", + "turn_off_pending": "{count, plural, one {Энэ төхөөрөмж дээр хийсэн # өөрчлөлт серверт хараахан хүрээгүй байна. Одоо синкийг унтраавал энэ нь алга болно.} other {Энэ төхөөрөмж дээр хийсэн # өөрчлөлт серверт хараахан хүрээгүй байна. Одоо синкийг унтраавал эдгээр нь алга болно.}}", + "turn_off_title": "Ямар ч байсан унтраах уу?", + "turn_off_unknown": "Энэ төхөөрөмж дээр хийсэн өөрчлөлтүүд серверт хүрсэн эсэхийг Bulwark шалгаж чадсангүй. Одоо синкийг унтраавал тэдгээр нь алга болно.", + "turning_off": "Өөрчлөлтүүдийг илгээж, дараа нь өгөгдлийг энэ төхөөрөмжөөс хасаж байна…", + "turning_on": "Тохируулж байна…" + }, "downloads": { "attachment_template": { "description_mobile": "Хавсралтыг хадгалах эсвэл хуваалцахад ашиглах загвар." diff --git a/locales/rn/nb.json b/locales/rn/nb.json index 54042dde..f790ab68 100644 --- a/locales/rn/nb.json +++ b/locales/rn/nb.json @@ -93,6 +93,8 @@ "until_date": "Til {date}" }, "settings": { + "device_sync_desc": "Vis kalenderne dine i Kalender-appen og andre apper på denne enheten. Endringer synkroniseres begge veier.", + "device_sync_title": "Synkroniser med denne enheten", "show_week_numbers_mobile_desc": "Vis ukenummer i månedsvisningen.", "time_zone": "Tidssone", "time_zone_auto": "Enhetens tidssone", @@ -249,6 +251,18 @@ "color_tag": "Etikett", "remove_color": "Fjern etiketten" }, + "device_sync": { + "auth_problem_text": "Serveren godtar ikke lenger påloggingen for {account}, så kontaktene og kalenderne til kontoen på denne enheten synkroniseres ikke lenger. Logg på igjen for å fortsette. Endringer som er gjort på denne enheten, beholdes.", + "auth_problem_title": "Logg på igjen for å fortsette synkroniseringen", + "problem_channel": "Synkroniseringsproblemer", + "sign_out_anyway": "Logg ut", + "sign_out_pending_message": "{count, plural, one {# endring som er gjort på denne enheten, har ikke nådd serveren. Den går tapt hvis du logger ut nå.} other {# endringer som er gjort på denne enheten, har ikke nådd serveren. De går tapt hvis du logger ut nå.}}", + "sign_out_pending_title": "Vil du logge ut likevel?", + "sign_out_unknown_message": "Endringer som er gjort på denne enheten, har kanskje ikke nådd serveren. De går tapt hvis du logger ut nå.", + "signed_out_text": "{account} ble logget ut, så kontaktene og kalenderne til kontoen på denne enheten synkroniseres ikke lenger. Logg på igjen for å fortsette. Endringer som er gjort på denne enheten, beholdes.", + "signed_out_title": "Logg på for å fortsette synkroniseringen", + "signing_out": "Fjerner synkroniserte kontakter og kalendere fra denne enheten…" + }, "email_composer": { "attach_camera": "Kamera", "attachment_reminder_body": "Meldingen din nevner «{keyword}», men ingen fil er vedlagt. Sende likevel?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# kontakt} other {# kontakter}}", "default_book": "Standardadressebok", "description_mobile": "Visningsvalg og verktøy for adressebøker.", + "device_sync_desc": "Vis adressebøkene dine i Kontakter-appen og andre apper på denne enheten. Endringer synkroniseres begge veier.", + "device_sync_title": "Synkroniser med denne enheten", "export_description_count": "{count, plural, one {Eksporter # kontakt som én vCard-fil.} other {Eksporter # kontakter som én vCard-fil.}}", "export_empty": "Ingen kontakter å eksportere", "import_description_mobile": "Importer kontakter fra en vCard-fil (.vcf) med gjenkjenning av duplikater.", "manage_description_mobile": "Opprett, gi nytt navn til og fjern adressebøkene som organiserer kontaktene dine." }, + "device_sync": { + "also_synced_by": "Synkroniseres også for {accounts}, så den vises to ganger på denne enheten.", + "android_settings": "Android-kontoinnstillinger", + "blocked_calendar": "Android spør ikke lenger om tilgang til kalendere. Gi tilgang i appinnstillingene, og slå deretter på synkronisering igjen.", + "blocked_contacts": "Android spør ikke lenger om tilgang til kontakter. Gi tilgang i appinnstillingene, og slå deretter på synkronisering igjen.", + "blocked_title": "Tilgangen er slått av", + "books_default": "Dine egne adressebøker synkroniseres; delte bare når du velger dem.", + "books_hint": "Dine egne adressebøker synkroniseres med mindre du slår dem av; delte bare når du slår dem på.", + "books_label": "Adressebøker", + "calendars_default": "Dine egne kalendere synkroniseres; delte bare når du velger dem.", + "calendars_hint": "Dine egne kalendere synkroniseres med mindre du slår dem av; delte bare når du slår dem på. Oppgavelister blir værende i Bulwark.", + "calendars_label": "Kalendere", + "choose": "Velg", + "choose_books_title": "Adressebøker som skal synkroniseres", + "choose_calendars_title": "Kalendere som skal synkroniseres", + "collections_count": "{selected} av {total} synkroniseres", + "continue": "Fortsett", + "deletions_delete": "Slett på serveren", + "deletions_message": "Mange elementer ble slettet på én gang på denne enheten, så de ble ikke slettet på serveren. Slett dem der også, eller hent dem tilbake på denne enheten.", + "deletions_restore": "Hent dem tilbake", + "deletions_title": "Vil du slette dem på serveren også?", + "denied_calendar": "Uten tilgang til kalendere kan ikke Bulwark legge kalenderne dine på denne enheten.", + "denied_contacts": "Uten tilgang til kontakter kan ikke Bulwark legge adressebøkene dine på denne enheten.", + "denied_title": "Synkroniseringen forblir av", + "failed_title": "Kunne ikke slå på synkronisering", + "grant_access": "Gi tilgang", + "interval_15m": "Hvert 15. minutt", + "interval_1h": "Hver time", + "interval_30m": "Hvert 30. minutt", + "interval_6h": "Hver 6. time", + "interval_desc": "Hvor ofte Android synkroniserer av seg selv. Endringer i Bulwark og endringer som varsles via push, synkroniseres tidligere.", + "interval_label": "Synkroniseringsintervall", + "interval_manual": "Ingen jevnlig synkronisering", + "keep_syncing": "Fortsett synkroniseringen", + "list_error_auth": "Serveren godtok ikke påloggingen.", + "list_error_network": "Kunne ikke nå serveren.", + "list_error_server": "Serveren kunne ikke vise listen.", + "list_error_signed_out": "Denne kontoen er ikke pålogget på denne enheten.", + "master_off": "Automatisk synkronisering er slått av på denne enheten, så synkronisering skjer bare når du trykker på «Synkroniser nå».", + "new_contacts_default": "Standardadressebok", + "new_contacts_desc": "Adresseboken for kontakter som opprettes i andre apper på denne enheten.", + "new_contacts_label": "Lagre nye kontakter i", + "no_books": "Denne kontoen har ingen adressebøker.", + "no_calendars": "Denne kontoen har ingen kalendere å synkronisere.", + "ok": "OK", + "open_app_settings": "Åpne appinnstillingene", + "own_collections": "Dine egne", + "rationale_calendar": "For å vise kalenderne dine i Kalender-appen trenger Bulwark tilgang til kalenderne på denne enheten. Bulwark endrer bare kalenderne til sin egen konto.", + "rationale_contacts": "For å vise adressebøkene dine i Kontakter-appen trenger Bulwark tilgang til kontaktene på denne enheten. Bulwark endrer bare kontaktene til sin egen konto.", + "rationale_title_calendar": "Gi tilgang til kalendere", + "rationale_title_contacts": "Gi tilgang til kontakter", + "read_only": "Skrivebeskyttet", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Kalender-appen", + "reminder_question_message": "Påminnelser om synkroniserte hendelser kan komme fra kalenderappen på denne enheten eller fra Bulwark. Velg én av dem, så du ikke får to påminnelser. Du kan endre dette senere.", + "reminder_question_title": "Hvem skal minne deg på hendelser?", + "reminders_desc": "Hvem som minner deg på synkroniserte hendelser. Den andre er stille, så du ikke får to påminnelser.", + "reminders_label": "Påminnelser", + "reminders_nobody": "Hendelsesvarslene i Bulwark er slått av, så ingenting minner deg på synkroniserte hendelser.", + "remove": "Fjern", + "remove_from_device": "Fjern fra denne enheten", + "remove_message": "Kontoens kontakter og kalendere fjernes fra denne enheten. Endringer som er gjort her og ikke har nådd serveren, går tapt.", + "remove_title": "Vil du fjerne fra denne enheten?", + "review_deletions": "Gå gjennom slettinger", + "shared_by": "Delt av {name}", + "sign_in_again": "Logg på igjen", + "status_auth": "Serveren godtar ikke lenger påloggingen. Logg på igjen for å fortsette synkroniseringen.", + "status_cancelled": "Den siste synkroniseringen stoppet før den var ferdig. Den fortsetter neste gang.", + "status_failed": "Den siste synkroniseringen mislyktes: {message}", + "status_failed_plain": "Den siste synkroniseringen mislyktes.", + "status_io": "Kunne ikke nå serveren {when}. Android prøver igjen.", + "status_item_errors": "{count, plural, one {# element kunne ikke synkroniseres.} other {# elementer kunne ikke synkroniseres.}}", + "status_off": "Av", + "status_paused": "Satt på pause i Android-innstillingene", + "status_permission_calendar": "Tilgangen til kalendere ble slått av, så ingenting synkroniseres.", + "status_permission_contacts": "Tilgangen til kontakter ble slått av, så ingenting synkroniseres.", + "status_removed": "Fjernet i Android-innstillingene. Slå på synkronisering for å legge til kontoen igjen.", + "status_safety_abort": "Stoppet: Serveren returnerte ingenting, selv om denne enheten fortsatt har elementer. Sjekk serveren, og synkroniser deretter på nytt.", + "status_synced": "Synkronisert {when}", + "status_syncing": "Synkroniserer…", + "status_too_many_deletions": "Mange elementer ble slettet på denne enheten. De slettes ikke på serveren før du bekrefter.", + "status_unsupported_calendar": "Serveren tilbyr ingen kalendere for denne kontoen.", + "status_unsupported_contacts": "Serveren tilbyr ingen adressebøker for denne kontoen.", + "status_waiting": "Venter på den første synkroniseringen", + "status_waiting_next": "Venter på neste synkronisering", + "suspended_desc": "Logget ut. Kontoens kontakter og kalendere blir liggende på denne enheten sammen med endringene som er gjort her, og synkroniseres igjen når du logger på.", + "switch_label": "Synkroniser {account} med denne enheten", + "sync_now": "Synkroniser nå", + "turn_off_anyway": "Slå av", + "turn_off_failed": "De synkroniserte dataene kunne ikke fjernes fra denne enheten. Synkroniseringen er satt på pause. Prøv igjen senere.", + "turn_off_failed_title": "Kunne ikke slå av synkronisering", + "turn_off_pending": "{count, plural, one {# endring som er gjort på denne enheten, har ikke nådd serveren ennå. Den går tapt hvis du slår av synkronisering nå.} other {# endringer som er gjort på denne enheten, har ikke nådd serveren ennå. De går tapt hvis du slår av synkronisering nå.}}", + "turn_off_title": "Vil du slå av likevel?", + "turn_off_unknown": "Bulwark kunne ikke sjekke om endringer som er gjort på denne enheten, har nådd serveren. De går tapt hvis du slår av synkronisering nå.", + "turning_off": "Laster opp endringer og fjerner deretter dataene fra denne enheten…", + "turning_on": "Konfigurerer…" + }, "downloads": { "attachment_template": { "description_mobile": "Malen som brukes når et vedlegg lagres eller deles." diff --git a/locales/rn/nl.json b/locales/rn/nl.json index c657a088..859934ab 100644 --- a/locales/rn/nl.json +++ b/locales/rn/nl.json @@ -93,6 +93,8 @@ "until_date": "Tot {date}" }, "settings": { + "device_sync_desc": "Toon uw agenda's in de Agenda-app en andere apps op dit apparaat. Wijzigingen worden in beide richtingen gesynchroniseerd.", + "device_sync_title": "Synchroniseren met dit apparaat", "show_week_numbers_mobile_desc": "Weeknummers weergeven in de maandweergave.", "time_zone": "Tijdzone", "time_zone_auto": "Tijdzone van apparaat", @@ -249,6 +251,18 @@ "color_tag": "Label", "remove_color": "Label verwijderen" }, + "device_sync": { + "auth_problem_text": "De server accepteert de aanmelding van {account} niet meer, dus de contacten en agenda's van dit account op dit apparaat worden niet meer gesynchroniseerd. Meld u opnieuw aan om verder te gaan. Wijzigingen op dit apparaat blijven behouden.", + "auth_problem_title": "Meld u opnieuw aan om te blijven synchroniseren", + "problem_channel": "Synchronisatieproblemen", + "sign_out_anyway": "Afmelden", + "sign_out_pending_message": "{count, plural, one {# wijziging op dit apparaat heeft de server niet bereikt. Die gaat verloren als u zich nu afmeldt.} other {# wijzigingen op dit apparaat hebben de server niet bereikt. Die gaan verloren als u zich nu afmeldt.}}", + "sign_out_pending_title": "Toch afmelden?", + "sign_out_unknown_message": "Wijzigingen op dit apparaat hebben de server mogelijk niet bereikt. Ze gaan verloren als u zich nu afmeldt.", + "signed_out_text": "{account} is afgemeld, dus de contacten en agenda's van dit account op dit apparaat worden niet meer gesynchroniseerd. Meld u opnieuw aan om verder te gaan. Wijzigingen op dit apparaat blijven behouden.", + "signed_out_title": "Meld u aan om te blijven synchroniseren", + "signing_out": "Gesynchroniseerde contacten en agenda's worden van dit apparaat verwijderd…" + }, "email_composer": { "attach_camera": "Camera", "attachment_reminder_body": "Uw bericht vermeldt \"{keyword}\" maar er is geen bestand bijgevoegd. Toch verzenden?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# contact} other {# contacten}}", "default_book": "Standaardadresboek", "description_mobile": "Weergavevoorkeuren en hulpmiddelen voor adresboeken.", + "device_sync_desc": "Toon uw adresboeken in de Contacten-app en andere apps op dit apparaat. Wijzigingen worden in beide richtingen gesynchroniseerd.", + "device_sync_title": "Synchroniseren met dit apparaat", "export_description_count": "{count, plural, one {# contact exporteren als één vCard-bestand.} other {# contacten exporteren als één vCard-bestand.}}", "export_empty": "Geen contacten om te exporteren", "import_description_mobile": "Contacten importeren uit een vCard-bestand (.vcf) met detectie van duplicaten.", "manage_description_mobile": "Maak, hernoem en verwijder de adresboeken waarin uw contacten zijn ingedeeld." }, + "device_sync": { + "also_synced_by": "Wordt ook gesynchroniseerd voor {accounts} en verschijnt daarom twee keer op dit apparaat.", + "android_settings": "Android-accountinstellingen", + "blocked_calendar": "Android vraagt niet meer om toegang tot agenda's. Sta de toegang toe in de app-instellingen en zet synchronisatie daarna weer aan.", + "blocked_contacts": "Android vraagt niet meer om toegang tot contacten. Sta de toegang toe in de app-instellingen en zet synchronisatie daarna weer aan.", + "blocked_title": "Toegang staat uit", + "books_default": "Uw eigen adresboeken worden gesynchroniseerd; gedeelde alleen als u ze kiest.", + "books_hint": "Uw eigen adresboeken worden gesynchroniseerd, tenzij u ze uitzet; gedeelde alleen als u ze aanzet.", + "books_label": "Adresboeken", + "calendars_default": "Uw eigen agenda's worden gesynchroniseerd; gedeelde alleen als u ze kiest.", + "calendars_hint": "Uw eigen agenda's worden gesynchroniseerd, tenzij u ze uitzet; gedeelde alleen als u ze aanzet. Takenlijsten blijven in Bulwark.", + "calendars_label": "Agenda's", + "choose": "Kiezen", + "choose_books_title": "Te synchroniseren adresboeken", + "choose_calendars_title": "Te synchroniseren agenda's", + "collections_count": "{selected} van {total} gesynchroniseerd", + "continue": "Doorgaan", + "deletions_delete": "Op server verwijderen", + "deletions_message": "Er zijn op dit apparaat veel items tegelijk verwijderd, daarom zijn ze niet op de server verwijderd. Verwijder ze daar ook of zet ze terug op dit apparaat.", + "deletions_restore": "Terugzetten", + "deletions_title": "Ook op de server verwijderen?", + "denied_calendar": "Zonder toegang tot agenda's kan Bulwark uw agenda's niet op dit apparaat zetten.", + "denied_contacts": "Zonder toegang tot contacten kan Bulwark uw adresboeken niet op dit apparaat zetten.", + "denied_title": "Synchronisatie blijft uit", + "failed_title": "Synchronisatie kon niet worden aangezet", + "grant_access": "Toegang geven", + "interval_15m": "Elke 15 minuten", + "interval_1h": "Elk uur", + "interval_30m": "Elke 30 minuten", + "interval_6h": "Elke 6 uur", + "interval_desc": "Hoe vaak Android uit zichzelf synchroniseert. Wijzigingen in Bulwark en via push gemelde wijzigingen worden eerder gesynchroniseerd.", + "interval_label": "Synchronisatie-interval", + "interval_manual": "Geen regelmatige synchronisatie", + "keep_syncing": "Blijven synchroniseren", + "list_error_auth": "De server heeft de aanmelding niet geaccepteerd.", + "list_error_network": "Kan de server niet bereiken.", + "list_error_server": "De server kon ze niet weergeven.", + "list_error_signed_out": "Dit account is niet aangemeld op dit apparaat.", + "master_off": "Automatisch synchroniseren staat uit op dit apparaat, dus er wordt alleen gesynchroniseerd als u op \"Nu synchroniseren\" tikt.", + "new_contacts_default": "Standaardadresboek", + "new_contacts_desc": "Het adresboek voor contacten die in andere apps op dit apparaat worden gemaakt.", + "new_contacts_label": "Nieuwe contacten opslaan in", + "no_books": "Dit account heeft geen adresboeken.", + "no_calendars": "Dit account heeft geen agenda's om te synchroniseren.", + "ok": "OK", + "open_app_settings": "App-instellingen openen", + "own_collections": "Uw eigen", + "rationale_calendar": "Om uw agenda's in de Agenda-app te tonen, heeft Bulwark toegang nodig tot de agenda's op dit apparaat. Bulwark wijzigt alleen de agenda's van zijn eigen account.", + "rationale_contacts": "Om uw adresboeken in de Contacten-app te tonen, heeft Bulwark toegang nodig tot de contacten op dit apparaat. Bulwark wijzigt alleen de contacten van zijn eigen account.", + "rationale_title_calendar": "Toegang tot agenda's toestaan", + "rationale_title_contacts": "Toegang tot contacten toestaan", + "read_only": "Alleen lezen", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Agenda-app", + "reminder_question_message": "Gesynchroniseerde evenementen kunnen u herinneren via de agenda-app op dit apparaat of via Bulwark. Kies er één, zodat u niet dubbel wordt herinnerd. U kunt dit later wijzigen.", + "reminder_question_title": "Wie moet u herinneren?", + "reminders_desc": "Wie u herinnert aan gesynchroniseerde evenementen. De andere blijft stil, zodat u niet dubbel wordt herinnerd.", + "reminders_label": "Herinneringen", + "reminders_nobody": "De evenementmeldingen van Bulwark staan uit, dus niets herinnert u aan gesynchroniseerde evenementen.", + "remove": "Verwijderen", + "remove_from_device": "Verwijderen van dit apparaat", + "remove_message": "De contacten en agenda's van het account worden van dit apparaat verwijderd. Wijzigingen die hier zijn gemaakt en de server nog niet hebben bereikt, gaan verloren.", + "remove_title": "Verwijderen van dit apparaat?", + "review_deletions": "Verwijderingen bekijken", + "shared_by": "Gedeeld door {name}", + "sign_in_again": "Opnieuw aanmelden", + "status_auth": "De server accepteert de aanmelding niet meer. Meld u opnieuw aan om te blijven synchroniseren.", + "status_cancelled": "De laatste synchronisatie is voortijdig gestopt. Die gaat de volgende keer verder.", + "status_failed": "Laatste synchronisatie mislukt: {message}", + "status_failed_plain": "Laatste synchronisatie mislukt.", + "status_io": "De server was {when} niet bereikbaar. Android probeert het opnieuw.", + "status_item_errors": "{count, plural, one {# item kon niet worden gesynchroniseerd.} other {# items konden niet worden gesynchroniseerd.}}", + "status_off": "Uit", + "status_paused": "Gepauzeerd in Android-instellingen", + "status_permission_calendar": "Toegang tot agenda's is uitgezet, dus er wordt niets gesynchroniseerd.", + "status_permission_contacts": "Toegang tot contacten is uitgezet, dus er wordt niets gesynchroniseerd.", + "status_removed": "Verwijderd in Android-instellingen. Zet synchronisatie aan om het account opnieuw toe te voegen.", + "status_safety_abort": "Gestopt: de server gaf niets terug terwijl dit apparaat nog items bevat. Controleer de server en synchroniseer daarna opnieuw.", + "status_synced": "Laatst gesynchroniseerd: {when}", + "status_syncing": "Synchroniseren…", + "status_too_many_deletions": "Er zijn veel items verwijderd op dit apparaat. Ze worden pas op de server verwijderd als u dat bevestigt.", + "status_unsupported_calendar": "De server biedt geen agenda's aan voor dit account.", + "status_unsupported_contacts": "De server biedt geen adresboeken aan voor dit account.", + "status_waiting": "Wachten op de eerste synchronisatie", + "status_waiting_next": "Wachten op de volgende synchronisatie", + "suspended_desc": "Afgemeld. De contacten en agenda's van het account blijven met de hier gemaakte wijzigingen op dit apparaat en worden weer gesynchroniseerd zodra u zich aanmeldt.", + "switch_label": "{account} synchroniseren met dit apparaat", + "sync_now": "Nu synchroniseren", + "turn_off_anyway": "Uitzetten", + "turn_off_failed": "De gesynchroniseerde gegevens konden niet van dit apparaat worden verwijderd. Synchronisatie is gepauzeerd; probeer het later opnieuw.", + "turn_off_failed_title": "Synchronisatie kon niet worden uitgezet", + "turn_off_pending": "{count, plural, one {# wijziging op dit apparaat heeft de server nog niet bereikt. Die gaat verloren als u synchronisatie nu uitzet.} other {# wijzigingen op dit apparaat hebben de server nog niet bereikt. Die gaan verloren als u synchronisatie nu uitzet.}}", + "turn_off_title": "Toch uitzetten?", + "turn_off_unknown": "Bulwark kon niet controleren of wijzigingen op dit apparaat de server hebben bereikt. Ze gaan verloren als u synchronisatie nu uitzet.", + "turning_off": "Wijzigingen uploaden en daarna de gegevens van dit apparaat verwijderen…", + "turning_on": "Instellen…" + }, "downloads": { "attachment_template": { "description_mobile": "Sjabloon voor het opslaan of delen van een bijlage." diff --git a/locales/rn/pl.json b/locales/rn/pl.json index 317a88c0..4e514042 100644 --- a/locales/rn/pl.json +++ b/locales/rn/pl.json @@ -93,6 +93,8 @@ "until_date": "Do {date}" }, "settings": { + "device_sync_desc": "Pokazuj swoje kalendarze w aplikacji Kalendarz i innych aplikacjach na tym urządzeniu. Zmiany są synchronizowane w obie strony.", + "device_sync_title": "Synchronizuj z tym urządzeniem", "show_week_numbers_mobile_desc": "Wyświetlaj numery tygodni w widoku miesiąca.", "time_zone": "Strefa czasowa", "time_zone_auto": "Strefa czasowa urządzenia", @@ -249,6 +251,18 @@ "color_tag": "Etykieta", "remove_color": "Usuń etykietę" }, + "device_sync": { + "auth_problem_text": "Serwer nie akceptuje już logowania konta {account}, więc jego kontakty i kalendarze na tym urządzeniu przestały się synchronizować. Zaloguj się ponownie, aby wznowić synchronizację. Zmiany wprowadzone na tym urządzeniu zostaną zachowane.", + "auth_problem_title": "Zaloguj się ponownie, aby dalej synchronizować", + "problem_channel": "Problemy z synchronizacją", + "sign_out_anyway": "Wyloguj się", + "sign_out_pending_message": "{count, plural, one {# zmiana wprowadzona na tym urządzeniu nie dotarła do serwera. Zostanie utracona, jeśli wylogujesz się teraz.} few {# zmiany wprowadzone na tym urządzeniu nie dotarły do serwera. Zostaną utracone, jeśli wylogujesz się teraz.} many {# zmian wprowadzonych na tym urządzeniu nie dotarło do serwera. Zostaną utracone, jeśli wylogujesz się teraz.} other {# zmiany wprowadzonej na tym urządzeniu nie dotarło do serwera. Zostaną utracone, jeśli wylogujesz się teraz.}}", + "sign_out_pending_title": "Wylogować się mimo to?", + "sign_out_unknown_message": "Zmiany wprowadzone na tym urządzeniu mogły nie dotrzeć do serwera. Zostaną utracone, jeśli wylogujesz się teraz.", + "signed_out_text": "Konto {account} zostało wylogowane, więc jego kontakty i kalendarze na tym urządzeniu nie są już synchronizowane. Zaloguj się ponownie, aby wznowić synchronizację. Zmiany wprowadzone na tym urządzeniu zostaną zachowane.", + "signed_out_title": "Zaloguj się, aby dalej synchronizować", + "signing_out": "Usuwanie zsynchronizowanych kontaktów i kalendarzy z tego urządzenia…" + }, "email_composer": { "attach_camera": "Aparat", "attachment_reminder_body": "Twoja wiadomość wspomina o „{keyword}”, ale nie załączono żadnego pliku. Wysłać mimo to?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# kontakt} few {# kontakty} many {# kontaktów} other {# kontaktu}}", "default_book": "Domyślna książka adresowa", "description_mobile": "Preferencje wyświetlania i narzędzia książek adresowych.", + "device_sync_desc": "Pokazuj swoje książki adresowe w aplikacji Kontakty i innych aplikacjach na tym urządzeniu. Zmiany są synchronizowane w obie strony.", + "device_sync_title": "Synchronizuj z tym urządzeniem", "export_description_count": "{count, plural, one {Eksportuj # kontakt jako jeden plik vCard.} few {Eksportuj # kontakty jako jeden plik vCard.} many {Eksportuj # kontaktów jako jeden plik vCard.} other {Eksportuj # kontaktu jako jeden plik vCard.}}", "export_empty": "Brak kontaktów do eksportu", "import_description_mobile": "Importuj kontakty z pliku vCard (.vcf) z wykrywaniem duplikatów.", "manage_description_mobile": "Twórz, zmieniaj nazwy i usuwaj książki adresowe, w których porządkujesz kontakty." }, + "device_sync": { + "also_synced_by": "Synchronizuje się także dla {accounts}, więc pojawia się na tym urządzeniu dwa razy.", + "android_settings": "Ustawienia konta w Androidzie", + "blocked_calendar": "Android nie prosi już o dostęp do kalendarzy. Zezwól na niego w ustawieniach aplikacji, a potem ponownie włącz synchronizację.", + "blocked_contacts": "Android nie prosi już o dostęp do kontaktów. Zezwól na niego w ustawieniach aplikacji, a potem ponownie włącz synchronizację.", + "blocked_title": "Dostęp jest wyłączony", + "books_default": "Twoje własne książki adresowe są synchronizowane, a udostępnione tylko wtedy, gdy je wybierzesz.", + "books_hint": "Twoje własne książki adresowe są synchronizowane, chyba że je wyłączysz, a udostępnione tylko wtedy, gdy je włączysz.", + "books_label": "Książki adresowe", + "calendars_default": "Twoje własne kalendarze są synchronizowane, a udostępnione tylko wtedy, gdy je wybierzesz.", + "calendars_hint": "Twoje własne kalendarze są synchronizowane, chyba że je wyłączysz, a udostępnione tylko wtedy, gdy je włączysz. Listy zadań pozostają w Bulwark.", + "calendars_label": "Kalendarze", + "choose": "Wybierz", + "choose_books_title": "Książki adresowe do synchronizacji", + "choose_calendars_title": "Kalendarze do synchronizacji", + "collections_count": "Synchronizowane: {selected} z {total}", + "continue": "Dalej", + "deletions_delete": "Usuń na serwerze", + "deletions_message": "Na tym urządzeniu usunięto naraz wiele elementów, więc nie zostały usunięte na serwerze. Usuń je także tam albo przywróć je na tym urządzeniu.", + "deletions_restore": "Przywróć je", + "deletions_title": "Usunąć je także na serwerze?", + "denied_calendar": "Bez dostępu do kalendarzy Bulwark nie może umieścić Twoich kalendarzy na tym urządzeniu.", + "denied_contacts": "Bez dostępu do kontaktów Bulwark nie może umieścić Twoich książek adresowych na tym urządzeniu.", + "denied_title": "Synchronizacja pozostaje wyłączona", + "failed_title": "Nie udało się włączyć synchronizacji", + "grant_access": "Przyznaj dostęp", + "interval_15m": "Co 15 minut", + "interval_1h": "Co godzinę", + "interval_30m": "Co 30 minut", + "interval_6h": "Co 6 godzin", + "interval_desc": "Jak często Android synchronizuje samodzielnie. Zmiany w Bulwark i zmiany zgłoszone przez push są synchronizowane szybciej.", + "interval_label": "Częstotliwość synchronizacji", + "interval_manual": "Bez regularnej synchronizacji", + "keep_syncing": "Synchronizuj dalej", + "list_error_auth": "Serwer nie zaakceptował logowania.", + "list_error_network": "Nie udało się połączyć z serwerem.", + "list_error_server": "Serwer nie mógł udostępnić listy.", + "list_error_signed_out": "To konto nie jest zalogowane na tym urządzeniu.", + "master_off": "Autosynchronizacja jest wyłączona na tym urządzeniu, więc synchronizacja odbywa się tylko po stuknięciu „Synchronizuj teraz”.", + "new_contacts_default": "Domyślna książka adresowa", + "new_contacts_desc": "Książka adresowa dla kontaktów utworzonych w innych aplikacjach na tym urządzeniu.", + "new_contacts_label": "Zapisuj nowe kontakty w", + "no_books": "To konto nie ma książek adresowych.", + "no_calendars": "To konto nie ma kalendarzy do synchronizacji.", + "ok": "OK", + "open_app_settings": "Otwórz ustawienia", + "own_collections": "Własne", + "rationale_calendar": "Aby pokazywać Twoje kalendarze w aplikacji Kalendarz, Bulwark potrzebuje dostępu do kalendarzy na tym urządzeniu. Zmienia tylko kalendarze własnego konta.", + "rationale_contacts": "Aby pokazywać Twoje książki adresowe w aplikacji Kontakty, Bulwark potrzebuje dostępu do kontaktów na tym urządzeniu. Zmienia tylko kontakty własnego konta.", + "rationale_title_calendar": "Zezwól na dostęp do kalendarzy", + "rationale_title_contacts": "Zezwól na dostęp do kontaktów", + "read_only": "Tylko do odczytu", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Aplikacja Kalendarz", + "reminder_question_message": "Przypomnienia o zsynchronizowanych wydarzeniach mogą pochodzić z aplikacji kalendarza na tym urządzeniu albo z Bulwark. Wybierz jedno źródło, aby nie dostawać przypomnień podwójnie. Możesz to później zmienić.", + "reminder_question_title": "Kto ma Ci przypominać?", + "reminders_desc": "Kto przypomina Ci o zsynchronizowanych wydarzeniach. Drugie źródło milczy, więc nie dostajesz przypomnień podwójnie.", + "reminders_label": "Przypomnienia", + "reminders_nobody": "Powiadomienia o wydarzeniach w Bulwark są wyłączone, więc nic nie przypomina Ci o zsynchronizowanych wydarzeniach.", + "remove": "Usuń", + "remove_from_device": "Usuń z tego urządzenia", + "remove_message": "Kontakty i kalendarze tego konta zostaną usunięte z tego urządzenia. Zmiany wprowadzone tutaj, które nie dotarły do serwera, zostaną utracone.", + "remove_title": "Usunąć z tego urządzenia?", + "review_deletions": "Przejrzyj usunięcia", + "shared_by": "Udostępnione przez: {name}", + "sign_in_again": "Zaloguj się ponownie", + "status_auth": "Serwer nie akceptuje już logowania. Zaloguj się ponownie, aby dalej synchronizować.", + "status_cancelled": "Ostatnia synchronizacja zatrzymała się przedwcześnie. Będzie kontynuowana następnym razem.", + "status_failed": "Ostatnia synchronizacja nie powiodła się: {message}", + "status_failed_plain": "Ostatnia synchronizacja nie powiodła się.", + "status_io": "Nie udało się połączyć z serwerem {when}. Android spróbuje ponownie.", + "status_item_errors": "{count, plural, one {Nie udało się zsynchronizować # elementu.} few {Nie udało się zsynchronizować # elementów.} many {Nie udało się zsynchronizować # elementów.} other {Nie udało się zsynchronizować # elementu.}}", + "status_off": "Wyłączona", + "status_paused": "Wstrzymana w ustawieniach Androida", + "status_permission_calendar": "Dostęp do kalendarzy został wyłączony, więc nic nie jest synchronizowane.", + "status_permission_contacts": "Dostęp do kontaktów został wyłączony, więc nic nie jest synchronizowane.", + "status_removed": "Usunięte w ustawieniach Androida. Włącz synchronizację, aby ponownie dodać konto.", + "status_safety_abort": "Zatrzymano: serwer nic nie zwrócił, choć na tym urządzeniu wciąż są elementy. Sprawdź serwer, a potem zsynchronizuj ponownie.", + "status_synced": "Zsynchronizowano {when}", + "status_syncing": "Synchronizowanie…", + "status_too_many_deletions": "Na tym urządzeniu usunięto wiele elementów. Nie zostaną usunięte na serwerze, dopóki tego nie potwierdzisz.", + "status_unsupported_calendar": "Serwer nie udostępnia kalendarzy dla tego konta.", + "status_unsupported_contacts": "Serwer nie udostępnia książek adresowych dla tego konta.", + "status_waiting": "Oczekiwanie na pierwszą synchronizację", + "status_waiting_next": "Oczekiwanie na następną synchronizację", + "suspended_desc": "Wylogowano. Kontakty i kalendarze tego konta pozostają na tym urządzeniu razem ze zmianami wprowadzonymi tutaj i zostaną ponownie zsynchronizowane po zalogowaniu.", + "switch_label": "Synchronizuj {account} z tym urządzeniem", + "sync_now": "Synchronizuj teraz", + "turn_off_anyway": "Wyłącz", + "turn_off_failed": "Nie udało się usunąć zsynchronizowanych danych z tego urządzenia. Synchronizacja jest wstrzymana; spróbuj ponownie później.", + "turn_off_failed_title": "Nie udało się wyłączyć synchronizacji", + "turn_off_pending": "{count, plural, one {# zmiana wprowadzona na tym urządzeniu nie dotarła jeszcze do serwera. Zostanie utracona, jeśli teraz wyłączysz synchronizację.} few {# zmiany wprowadzone na tym urządzeniu nie dotarły jeszcze do serwera. Zostaną utracone, jeśli teraz wyłączysz synchronizację.} many {# zmian wprowadzonych na tym urządzeniu nie dotarło jeszcze do serwera. Zostaną utracone, jeśli teraz wyłączysz synchronizację.} other {# zmiany wprowadzonej na tym urządzeniu nie dotarło jeszcze do serwera. Zostaną utracone, jeśli teraz wyłączysz synchronizację.}}", + "turn_off_title": "Wyłączyć mimo to?", + "turn_off_unknown": "Bulwark nie mógł sprawdzić, czy zmiany wprowadzone na tym urządzeniu dotarły do serwera. Zostaną utracone, jeśli teraz wyłączysz synchronizację.", + "turning_off": "Wysyłanie zmian, a potem usuwanie danych z tego urządzenia…", + "turning_on": "Konfigurowanie…" + }, "downloads": { "attachment_template": { "description_mobile": "Szablon używany przy zapisywaniu lub udostępnianiu załącznika." diff --git a/locales/rn/pt.json b/locales/rn/pt.json index bfd322e6..13a601b1 100644 --- a/locales/rn/pt.json +++ b/locales/rn/pt.json @@ -93,6 +93,8 @@ "until_date": "Até {date}" }, "settings": { + "device_sync_desc": "Mostre seus calendários no app Agenda e em outros apps deste dispositivo. As alterações são sincronizadas nos dois sentidos.", + "device_sync_title": "Sincronizar com este dispositivo", "show_week_numbers_mobile_desc": "Exibir números da semana na visualização mensal.", "time_zone": "Fuso horário", "time_zone_auto": "Fuso horário do dispositivo", @@ -249,6 +251,18 @@ "color_tag": "Etiqueta", "remove_color": "Remover etiqueta" }, + "device_sync": { + "auth_problem_text": "O servidor não aceita mais o login de {account}, então os contatos e calendários dessa conta neste dispositivo pararam de ser sincronizados. Faça login novamente para retomar. As alterações feitas neste dispositivo são mantidas.", + "auth_problem_title": "Faça login novamente para continuar sincronizando", + "problem_channel": "Problemas de sincronização", + "sign_out_anyway": "Sair", + "sign_out_pending_message": "{count, plural, one {# alteração feita neste dispositivo não chegou ao servidor. Ela será perdida se você sair agora.} other {# alterações feitas neste dispositivo não chegaram ao servidor. Elas serão perdidas se você sair agora.}}", + "sign_out_pending_title": "Sair mesmo assim?", + "sign_out_unknown_message": "As alterações feitas neste dispositivo podem não ter chegado ao servidor. Elas serão perdidas se você sair agora.", + "signed_out_text": "A sessão de {account} foi encerrada, então os contatos e calendários dessa conta neste dispositivo não são mais sincronizados. Faça login novamente para retomar. As alterações feitas neste dispositivo são mantidas.", + "signed_out_title": "Faça login para continuar sincronizando", + "signing_out": "Removendo contatos e calendários sincronizados deste dispositivo…" + }, "email_composer": { "attach_camera": "Câmera", "attachment_reminder_body": "Sua mensagem menciona \"{keyword}\", mas nenhum arquivo está anexado. Enviar mesmo assim?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# contato} other {# contatos}}", "default_book": "Catálogo de endereços padrão", "description_mobile": "Preferências de exibição e ferramentas do catálogo de endereços.", + "device_sync_desc": "Mostre seus catálogos de endereços no app Contatos e em outros apps deste dispositivo. As alterações são sincronizadas nos dois sentidos.", + "device_sync_title": "Sincronizar com este dispositivo", "export_description_count": "{count, plural, one {Exporta # contato como um único arquivo vCard.} other {Exporta # contatos como um único arquivo vCard.}}", "export_empty": "Nenhum contato para exportar", "import_description_mobile": "Importa contatos de um arquivo vCard (.vcf) com detecção de duplicados.", "manage_description_mobile": "Crie, renomeie e remova os catálogos de endereços que organizam seus contatos." }, + "device_sync": { + "also_synced_by": "Também é sincronizado para {accounts}, por isso aparece duas vezes neste dispositivo.", + "android_settings": "Configurações de conta do Android", + "blocked_calendar": "O Android não pede mais acesso aos calendários. Permita o acesso nas configurações do app e ative a sincronização de novo.", + "blocked_contacts": "O Android não pede mais acesso aos contatos. Permita o acesso nas configurações do app e ative a sincronização de novo.", + "blocked_title": "O acesso está desativado", + "books_default": "Seus catálogos de endereços são sincronizados; os compartilhados, só quando você os escolher.", + "books_hint": "Seus catálogos de endereços são sincronizados, a menos que você os desative; os compartilhados, só quando você os ativar.", + "books_label": "Catálogos de endereços", + "calendars_default": "Seus calendários são sincronizados; os compartilhados, só quando você os escolher.", + "calendars_hint": "Seus calendários são sincronizados, a menos que você os desative; os compartilhados, só quando você os ativar. As listas de tarefas ficam no Bulwark.", + "calendars_label": "Calendários", + "choose": "Escolher", + "choose_books_title": "Catálogos de endereços para sincronizar", + "choose_calendars_title": "Calendários para sincronizar", + "collections_count": "{selected} de {total} sincronizados", + "continue": "Continuar", + "deletions_delete": "Excluir no servidor", + "deletions_message": "Muitos itens foram excluídos de uma vez neste dispositivo, por isso não foram excluídos no servidor. Exclua-os lá também ou restaure-os neste dispositivo.", + "deletions_restore": "Restaurar", + "deletions_title": "Excluir também no servidor?", + "denied_calendar": "Sem acesso aos calendários, o Bulwark não consegue colocar seus calendários neste dispositivo.", + "denied_contacts": "Sem acesso aos contatos, o Bulwark não consegue colocar seus catálogos de endereços neste dispositivo.", + "denied_title": "A sincronização continua desativada", + "failed_title": "Não foi possível ativar a sincronização", + "grant_access": "Conceder acesso", + "interval_15m": "A cada 15 minutos", + "interval_1h": "A cada hora", + "interval_30m": "A cada 30 minutos", + "interval_6h": "A cada 6 horas", + "interval_desc": "Com que frequência o Android sincroniza por conta própria. Edições no Bulwark e alterações avisadas por push são sincronizadas antes.", + "interval_label": "Intervalo de sincronização", + "interval_manual": "Sem sincronização periódica", + "keep_syncing": "Continuar sincronizando", + "list_error_auth": "O servidor não aceitou o login.", + "list_error_network": "Não foi possível acessar o servidor.", + "list_error_server": "O servidor não conseguiu listá-los.", + "list_error_signed_out": "Esta conta não está conectada neste dispositivo.", + "master_off": "A sincronização automática está desativada neste dispositivo, então só há sincronização quando você toca em \"Sincronizar agora\".", + "new_contacts_default": "Catálogo de endereços padrão", + "new_contacts_desc": "O catálogo de endereços para contatos criados em outros apps deste dispositivo.", + "new_contacts_label": "Salvar novos contatos em", + "no_books": "Esta conta não tem catálogos de endereços.", + "no_calendars": "Esta conta não tem calendários para sincronizar.", + "ok": "OK", + "open_app_settings": "Abrir configurações do app", + "own_collections": "Seus", + "rationale_calendar": "Para mostrar seus calendários no app Agenda, o Bulwark precisa de acesso aos calendários deste dispositivo. Ele só altera os calendários da própria conta.", + "rationale_contacts": "Para mostrar seus catálogos de endereços no app Contatos, o Bulwark precisa de acesso aos contatos deste dispositivo. Ele só altera os contatos da própria conta.", + "rationale_title_calendar": "Permitir acesso aos calendários", + "rationale_title_contacts": "Permitir acesso aos contatos", + "read_only": "Somente leitura", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "App Agenda", + "reminder_question_message": "Os lembretes de eventos sincronizados podem vir do app de agenda deste dispositivo ou do Bulwark. Escolha um para não receber lembretes em dobro. Você pode mudar isso depois.", + "reminder_question_title": "Quem deve lembrar você?", + "reminders_desc": "Quem lembra você dos eventos sincronizados. O outro fica em silêncio, para que você não receba lembretes em dobro.", + "reminders_label": "Lembretes", + "reminders_nobody": "As notificações de eventos do Bulwark estão desativadas, então nada vai lembrar você dos eventos sincronizados.", + "remove": "Remover", + "remove_from_device": "Remover deste dispositivo", + "remove_message": "Os contatos e calendários da conta são removidos deste dispositivo. As alterações feitas aqui que não chegaram ao servidor são perdidas.", + "remove_title": "Remover deste dispositivo?", + "review_deletions": "Revisar exclusões", + "shared_by": "Compartilhado por {name}", + "sign_in_again": "Fazer login novamente", + "status_auth": "O servidor não aceita mais o login. Faça login novamente para continuar sincronizando.", + "status_cancelled": "A última sincronização parou antes do fim. Ela continua na próxima vez.", + "status_failed": "Falha na última sincronização: {message}", + "status_failed_plain": "Falha na última sincronização.", + "status_io": "Não foi possível acessar o servidor {when}. O Android vai tentar de novo.", + "status_item_errors": "{count, plural, one {Não foi possível sincronizar # item.} other {Não foi possível sincronizar # itens.}}", + "status_off": "Desativada", + "status_paused": "Pausada nas configurações do Android", + "status_permission_calendar": "O acesso aos calendários foi desativado, então nada é sincronizado.", + "status_permission_contacts": "O acesso aos contatos foi desativado, então nada é sincronizado.", + "status_removed": "Conta removida nas configurações do Android. Ative a sincronização para adicioná-la de novo.", + "status_safety_abort": "Interrompida: o servidor não retornou nada, mas este dispositivo ainda tem itens. Verifique o servidor e sincronize de novo.", + "status_synced": "Sincronizado {when}", + "status_syncing": "Sincronizando…", + "status_too_many_deletions": "Muitos itens foram excluídos neste dispositivo. Eles só serão excluídos no servidor quando você confirmar.", + "status_unsupported_calendar": "O servidor não oferece calendários para esta conta.", + "status_unsupported_contacts": "O servidor não oferece catálogos de endereços para esta conta.", + "status_waiting": "Aguardando a primeira sincronização", + "status_waiting_next": "Aguardando a próxima sincronização", + "suspended_desc": "Sessão encerrada. Os contatos e calendários da conta continuam neste dispositivo, com as alterações feitas aqui, e voltam a sincronizar quando você fizer login.", + "switch_label": "Sincronizar {account} com este dispositivo", + "sync_now": "Sincronizar agora", + "turn_off_anyway": "Desativar", + "turn_off_failed": "Não foi possível remover os dados sincronizados deste dispositivo. A sincronização está pausada; tente novamente mais tarde.", + "turn_off_failed_title": "Não foi possível desativar a sincronização", + "turn_off_pending": "{count, plural, one {# alteração feita neste dispositivo ainda não chegou ao servidor. Ela será perdida se você desativar a sincronização agora.} other {# alterações feitas neste dispositivo ainda não chegaram ao servidor. Elas serão perdidas se você desativar a sincronização agora.}}", + "turn_off_title": "Desativar mesmo assim?", + "turn_off_unknown": "O Bulwark não conseguiu verificar se as alterações feitas neste dispositivo chegaram ao servidor. Elas serão perdidas se você desativar a sincronização agora.", + "turning_off": "Enviando alterações e depois removendo os dados deste dispositivo…", + "turning_on": "Configurando…" + }, "downloads": { "attachment_template": { "description_mobile": "Modelo usado ao salvar ou compartilhar um anexo." diff --git a/locales/rn/ro.json b/locales/rn/ro.json index 0123cafc..0403fef9 100644 --- a/locales/rn/ro.json +++ b/locales/rn/ro.json @@ -93,6 +93,8 @@ "until_date": "Până la {date}" }, "settings": { + "device_sync_desc": "Afișați calendarele în aplicația Calendar și în alte aplicații de pe acest dispozitiv. Modificările se sincronizează în ambele sensuri.", + "device_sync_title": "Sincronizare cu acest dispozitiv", "show_week_numbers_mobile_desc": "Afișați numerele săptămânilor în vizualizarea lunară.", "time_zone": "Fus orar", "time_zone_auto": "Fusul orar al dispozitivului", @@ -249,6 +251,18 @@ "color_tag": "Etichetă", "remove_color": "Eliminați eticheta" }, + "device_sync": { + "auth_problem_text": "Serverul nu mai acceptă autentificarea pentru {account}, așa că contactele și calendarele acestui cont de pe acest dispozitiv nu se mai sincronizează. Autentificați-vă din nou pentru a relua sincronizarea. Modificările făcute pe acest dispozitiv se păstrează.", + "auth_problem_title": "Autentificați-vă din nou pentru a continua sincronizarea", + "problem_channel": "Probleme de sincronizare", + "sign_out_anyway": "Deconectare", + "sign_out_pending_message": "{count, plural, one {# modificare făcută pe acest dispozitiv nu a ajuns pe server. Se va pierde dacă vă deconectați acum.} few {# modificări făcute pe acest dispozitiv nu au ajuns pe server. Se vor pierde dacă vă deconectați acum.} other {# de modificări făcute pe acest dispozitiv nu au ajuns pe server. Se vor pierde dacă vă deconectați acum.}}", + "sign_out_pending_title": "Vă deconectați totuși?", + "sign_out_unknown_message": "Este posibil ca modificările făcute pe acest dispozitiv să nu fi ajuns pe server. Se vor pierde dacă vă deconectați acum.", + "signed_out_text": "{account} a fost deconectat, așa că contactele și calendarele acestui cont de pe acest dispozitiv nu se mai sincronizează. Autentificați-vă din nou pentru a relua sincronizarea. Modificările făcute pe acest dispozitiv se păstrează.", + "signed_out_title": "Autentificați-vă pentru a continua sincronizarea", + "signing_out": "Se elimină de pe acest dispozitiv contactele și calendarele sincronizate…" + }, "email_composer": { "attach_camera": "Cameră", "attachment_reminder_body": "Mesajul dvs. menționează „{keyword}”, dar nu este atașat niciun fișier. Doriți să trimiteți oricum?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# contact} few {# contacte} other {# de contacte}}", "default_book": "Agendă implicită", "description_mobile": "Preferințe de afișare și instrumente pentru agende.", + "device_sync_desc": "Afișați agendele de adrese în aplicația Contacte și în alte aplicații de pe acest dispozitiv. Modificările se sincronizează în ambele sensuri.", + "device_sync_title": "Sincronizare cu acest dispozitiv", "export_description_count": "{count, plural, one {Exportă # contact ca un singur fișier vCard.} few {Exportă # contacte ca un singur fișier vCard.} other {Exportă # de contacte ca un singur fișier vCard.}}", "export_empty": "Nu există contacte de exportat", "import_description_mobile": "Importă contacte dintr-un fișier vCard (.vcf), cu detectarea duplicatelor.", "manage_description_mobile": "Creați, redenumiți și eliminați agendele care vă organizează contactele." }, + "device_sync": { + "also_synced_by": "Se sincronizează și pentru {accounts}, așa că apare de două ori pe acest dispozitiv.", + "android_settings": "Setările contului Android", + "blocked_calendar": "Android nu mai solicită accesul la calendare. Permiteți-l din setările aplicației, apoi activați din nou sincronizarea.", + "blocked_contacts": "Android nu mai solicită accesul la contacte. Permiteți-l din setările aplicației, apoi activați din nou sincronizarea.", + "blocked_title": "Accesul este dezactivat", + "books_default": "Agendele dvs. de adrese se sincronizează; cele partajate doar dacă le alegeți.", + "books_hint": "Agendele dvs. de adrese se sincronizează dacă nu le dezactivați; cele partajate doar dacă le activați.", + "books_label": "Agende de adrese", + "calendars_default": "Calendarele dvs. se sincronizează; cele partajate doar dacă le alegeți.", + "calendars_hint": "Calendarele dvs. se sincronizează dacă nu le dezactivați; cele partajate doar dacă le activați. Listele de sarcini rămân în Bulwark.", + "calendars_label": "Calendare", + "choose": "Alegeți", + "choose_books_title": "Agende de adrese de sincronizat", + "choose_calendars_title": "Calendare de sincronizat", + "collections_count": "Sincronizate: {selected} din {total}", + "continue": "Continuați", + "deletions_delete": "Ștergeți de pe server", + "deletions_message": "Multe elemente au fost șterse deodată pe acest dispozitiv, așa că nu au fost șterse de pe server. Ștergeți-le și acolo sau readuceți-le pe acest dispozitiv.", + "deletions_restore": "Readuceți-le", + "deletions_title": "Le ștergeți și de pe server?", + "denied_calendar": "Fără acces la calendare, Bulwark nu poate aduce calendarele dvs. pe acest dispozitiv.", + "denied_contacts": "Fără acces la contacte, Bulwark nu poate aduce agendele dvs. de adrese pe acest dispozitiv.", + "denied_title": "Sincronizarea rămâne dezactivată", + "failed_title": "Sincronizarea nu a putut fi activată", + "grant_access": "Acordați acces", + "interval_15m": "La fiecare 15 minute", + "interval_1h": "La fiecare oră", + "interval_30m": "La fiecare 30 de minute", + "interval_6h": "La fiecare 6 ore", + "interval_desc": "Cât de des sincronizează Android de la sine. Modificările din Bulwark și cele anunțate prin push se sincronizează mai repede.", + "interval_label": "Interval de sincronizare", + "interval_manual": "Fără sincronizare periodică", + "keep_syncing": "Continuați sincronizarea", + "list_error_auth": "Serverul nu a acceptat autentificarea.", + "list_error_network": "Serverul nu a putut fi contactat.", + "list_error_server": "Serverul nu a putut furniza lista.", + "list_error_signed_out": "Acest cont nu este autentificat pe acest dispozitiv.", + "master_off": "Sincronizarea automată este dezactivată pe acest dispozitiv, așa că sincronizarea are loc doar când atingeți „Sincronizați acum”.", + "new_contacts_default": "Agendă implicită", + "new_contacts_desc": "Agenda de adrese pentru contactele create în alte aplicații de pe acest dispozitiv.", + "new_contacts_label": "Contactele noi se salvează în", + "no_books": "Acest cont nu are agende de adrese.", + "no_calendars": "Acest cont nu are calendare de sincronizat.", + "ok": "OK", + "open_app_settings": "Deschideți setările", + "own_collections": "Ale dvs.", + "rationale_calendar": "Pentru a afișa calendarele dvs. în aplicația Calendar, Bulwark are nevoie de acces la calendarele de pe acest dispozitiv. Modifică doar calendarele propriului cont.", + "rationale_contacts": "Pentru a afișa agendele dvs. de adrese în aplicația Contacte, Bulwark are nevoie de acces la contactele de pe acest dispozitiv. Modifică doar contactele propriului cont.", + "rationale_title_calendar": "Permiteți accesul la calendare", + "rationale_title_contacts": "Permiteți accesul la contacte", + "read_only": "Doar citire", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Aplicația Calendar", + "reminder_question_message": "Mementourile pentru evenimentele sincronizate pot veni din aplicația de calendar de pe acest dispozitiv sau din Bulwark. Alegeți una dintre ele ca să nu primiți mementouri de două ori. Puteți schimba ulterior.", + "reminder_question_title": "Cine să vă trimită mementouri?", + "reminders_desc": "Cine vă amintește de evenimentele sincronizate. Cealaltă aplicație rămâne silențioasă, ca să nu primiți mementouri de două ori.", + "reminders_label": "Mementouri", + "reminders_nobody": "Notificările de evenimente din Bulwark sunt dezactivate, așa că nimic nu vă amintește de evenimentele sincronizate.", + "remove": "Eliminați", + "remove_from_device": "Eliminați de pe acest dispozitiv", + "remove_message": "Contactele și calendarele contului vor fi eliminate de pe acest dispozitiv. Modificările făcute aici care nu au ajuns pe server se vor pierde.", + "remove_title": "Eliminați de pe acest dispozitiv?", + "review_deletions": "Verificați ștergerile", + "shared_by": "Distribuit de {name}", + "sign_in_again": "Autentificați-vă din nou", + "status_auth": "Serverul nu mai acceptă autentificarea. Autentificați-vă din nou pentru a continua sincronizarea.", + "status_cancelled": "Ultima sincronizare s-a oprit prea devreme. Va continua data viitoare.", + "status_failed": "Ultima sincronizare a eșuat: {message}", + "status_failed_plain": "Ultima sincronizare a eșuat.", + "status_io": "Serverul nu a putut fi contactat {when}. Android va încerca din nou.", + "status_item_errors": "{count, plural, one {# element nu a putut fi sincronizat.} few {# elemente nu au putut fi sincronizate.} other {# de elemente nu au putut fi sincronizate.}}", + "status_off": "Dezactivată", + "status_paused": "Întreruptă în setările Android", + "status_permission_calendar": "Accesul la calendare a fost dezactivat, așa că nu se sincronizează nimic.", + "status_permission_contacts": "Accesul la contacte a fost dezactivat, așa că nu se sincronizează nimic.", + "status_removed": "Eliminat în setările Android. Activați sincronizarea pentru a adăuga din nou contul.", + "status_safety_abort": "Oprită: serverul nu a returnat nimic, deși acest dispozitiv încă are elemente. Verificați serverul, apoi sincronizați din nou.", + "status_synced": "Sincronizat {when}", + "status_syncing": "Se sincronizează…", + "status_too_many_deletions": "Multe elemente au fost șterse pe acest dispozitiv. Nu vor fi șterse de pe server până nu confirmați.", + "status_unsupported_calendar": "Serverul nu oferă calendare pentru acest cont.", + "status_unsupported_contacts": "Serverul nu oferă agende de adrese pentru acest cont.", + "status_waiting": "Se așteaptă prima sincronizare", + "status_waiting_next": "Se așteaptă următoarea sincronizare", + "suspended_desc": "Deconectat. Contactele și calendarele contului rămân pe acest dispozitiv, împreună cu modificările făcute aici, și se vor sincroniza din nou după ce vă autentificați.", + "switch_label": "Sincronizați {account} cu acest dispozitiv", + "sync_now": "Sincronizați acum", + "turn_off_anyway": "Dezactivați", + "turn_off_failed": "Datele sincronizate nu au putut fi eliminate de pe acest dispozitiv. Sincronizarea este întreruptă; încercați din nou mai târziu.", + "turn_off_failed_title": "Sincronizarea nu a putut fi dezactivată", + "turn_off_pending": "{count, plural, one {# modificare făcută pe acest dispozitiv nu a ajuns încă pe server. Se va pierde dacă dezactivați acum sincronizarea.} few {# modificări făcute pe acest dispozitiv nu au ajuns încă pe server. Se vor pierde dacă dezactivați acum sincronizarea.} other {# de modificări făcute pe acest dispozitiv nu au ajuns încă pe server. Se vor pierde dacă dezactivați acum sincronizarea.}}", + "turn_off_title": "Dezactivați totuși?", + "turn_off_unknown": "Bulwark nu a putut verifica dacă modificările făcute pe acest dispozitiv au ajuns pe server. Se vor pierde dacă dezactivați acum sincronizarea.", + "turning_off": "Se încarcă modificările, apoi se elimină datele de pe acest dispozitiv…", + "turning_on": "Se configurează…" + }, "downloads": { "attachment_template": { "description_mobile": "Șablon utilizat la salvarea sau partajarea unui atașament." diff --git a/locales/rn/ru.json b/locales/rn/ru.json index 62bd0b80..71c16dbc 100644 --- a/locales/rn/ru.json +++ b/locales/rn/ru.json @@ -93,6 +93,8 @@ "until_date": "До {date}" }, "settings": { + "device_sync_desc": "Ваши календари будут видны в приложении «Календарь» и других приложениях на этом устройстве. Изменения синхронизируются в обе стороны.", + "device_sync_title": "Синхронизация с этим устройством", "show_week_numbers_mobile_desc": "Показывать номера недель в представлении «Месяц».", "time_zone": "Часовой пояс", "time_zone_auto": "Часовой пояс устройства", @@ -249,6 +251,18 @@ "color_tag": "Тег", "remove_color": "Убрать тег" }, + "device_sync": { + "auth_problem_text": "Сервер больше не принимает вход в аккаунт {account}, поэтому его контакты и календари на этом устройстве больше не синхронизируются. Войдите снова, чтобы возобновить синхронизацию. Изменения, сделанные на этом устройстве, сохранятся.", + "auth_problem_title": "Войдите снова, чтобы продолжить синхронизацию", + "problem_channel": "Проблемы синхронизации", + "sign_out_anyway": "Выйти", + "sign_out_pending_message": "{count, plural, one {# изменение, сделанное на этом устройстве, не дошло до сервера. Оно будет потеряно, если вы сейчас выйдете.} few {# изменения, сделанные на этом устройстве, не дошли до сервера. Они будут потеряны, если вы сейчас выйдете.} many {# изменений, сделанных на этом устройстве, не дошли до сервера. Они будут потеряны, если вы сейчас выйдете.} other {# изменения, сделанного на этом устройстве, не дошли до сервера. Они будут потеряны, если вы сейчас выйдете.}}", + "sign_out_pending_title": "Всё равно выйти?", + "sign_out_unknown_message": "Изменения, сделанные на этом устройстве, могли не дойти до сервера. Они будут потеряны, если вы сейчас выйдете.", + "signed_out_text": "Выполнен выход из аккаунта {account}, поэтому его контакты и календари на этом устройстве больше не синхронизируются. Войдите снова, чтобы возобновить синхронизацию. Изменения, сделанные на этом устройстве, сохранятся.", + "signed_out_title": "Войдите, чтобы продолжить синхронизацию", + "signing_out": "Удаление синхронизированных контактов и календарей с этого устройства…" + }, "email_composer": { "attach_camera": "Камера", "attachment_reminder_body": "В письме упоминается «{keyword}», но файл не прикреплён. Всё равно отправить?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# контакт} few {# контакта} many {# контактов} other {# контакта}}", "default_book": "Адресная книга по умолчанию", "description_mobile": "Параметры отображения и инструменты адресных книг.", + "device_sync_desc": "Ваши адресные книги будут видны в приложении «Контакты» и других приложениях на этом устройстве. Изменения синхронизируются в обе стороны.", + "device_sync_title": "Синхронизация с этим устройством", "export_description_count": "{count, plural, one {Экспортировать # контакт в один файл vCard.} few {Экспортировать # контакта в один файл vCard.} many {Экспортировать # контактов в один файл vCard.} other {Экспортировать # контакта в один файл vCard.}}", "export_empty": "Нет контактов для экспорта", "import_description_mobile": "Импорт контактов из файла vCard (.vcf) с поиском дубликатов.", "manage_description_mobile": "Создавайте, переименовывайте и удаляйте адресные книги, в которых хранятся ваши контакты." }, + "device_sync": { + "also_synced_by": "Также синхронизируется для {accounts}, поэтому отображается на этом устройстве дважды.", + "android_settings": "Настройки аккаунта Android", + "blocked_calendar": "Android больше не запрашивает доступ к календарям. Разрешите его в настройках приложения, а затем снова включите синхронизацию.", + "blocked_contacts": "Android больше не запрашивает доступ к контактам. Разрешите его в настройках приложения, а затем снова включите синхронизацию.", + "blocked_title": "Доступ отключён", + "books_default": "Ваши собственные адресные книги синхронизируются, а общие — только если вы их выберете.", + "books_hint": "Ваши собственные адресные книги синхронизируются, если вы их не отключите, а общие — только если вы их включите.", + "books_label": "Адресные книги", + "calendars_default": "Ваши собственные календари синхронизируются, а общие — только если вы их выберете.", + "calendars_hint": "Ваши собственные календари синхронизируются, если вы их не отключите, а общие — только если вы их включите. Списки задач остаются в Bulwark.", + "calendars_label": "Календари", + "choose": "Выбрать", + "choose_books_title": "Адресные книги для синхронизации", + "choose_calendars_title": "Календари для синхронизации", + "collections_count": "Синхронизируется: {selected} из {total}", + "continue": "Продолжить", + "deletions_delete": "Удалить на сервере", + "deletions_message": "На этом устройстве сразу было удалено много элементов, поэтому на сервере они не удалены. Удалите их и там или верните их на это устройство.", + "deletions_restore": "Вернуть", + "deletions_title": "Удалить их и на сервере?", + "denied_calendar": "Без доступа к календарям Bulwark не сможет перенести ваши календари на это устройство.", + "denied_contacts": "Без доступа к контактам Bulwark не сможет перенести ваши адресные книги на это устройство.", + "denied_title": "Синхронизация остаётся выключенной", + "failed_title": "Не удалось включить синхронизацию", + "grant_access": "Предоставить доступ", + "interval_15m": "Каждые 15 минут", + "interval_1h": "Каждый час", + "interval_30m": "Каждые 30 минут", + "interval_6h": "Каждые 6 часов", + "interval_desc": "Как часто Android синхронизирует данные самостоятельно. Изменения в Bulwark и изменения, о которых сообщает push, синхронизируются раньше.", + "interval_label": "Интервал синхронизации", + "interval_manual": "Без регулярной синхронизации", + "keep_syncing": "Продолжить синхронизацию", + "list_error_auth": "Сервер не принял вход.", + "list_error_network": "Не удалось связаться с сервером.", + "list_error_server": "Сервер не смог вернуть список.", + "list_error_signed_out": "На этом устройстве не выполнен вход в этот аккаунт.", + "master_off": "Автосинхронизация на этом устройстве выключена, поэтому синхронизация выполняется только при нажатии «Синхронизировать».", + "new_contacts_default": "Адресная книга по умолчанию", + "new_contacts_desc": "Адресная книга для контактов, созданных в других приложениях на этом устройстве.", + "new_contacts_label": "Сохранять новые контакты в", + "no_books": "В этом аккаунте нет адресных книг.", + "no_calendars": "В этом аккаунте нет календарей для синхронизации.", + "ok": "ОК", + "open_app_settings": "Открыть настройки", + "own_collections": "Ваши", + "rationale_calendar": "Чтобы показывать ваши календари в приложении «Календарь», Bulwark нужен доступ к календарям на этом устройстве. Он изменяет только календари своего аккаунта.", + "rationale_contacts": "Чтобы показывать ваши адресные книги в приложении «Контакты», Bulwark нужен доступ к контактам на этом устройстве. Он изменяет только контакты своего аккаунта.", + "rationale_title_calendar": "Разрешить доступ к календарям", + "rationale_title_contacts": "Разрешить доступ к контактам", + "read_only": "Только для чтения", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Приложение «Календарь»", + "reminder_question_message": "Напоминания о синхронизированных событиях может показывать приложение календаря на этом устройстве или Bulwark. Выберите что-то одно, чтобы не получать напоминания дважды. Это можно изменить позже.", + "reminder_question_title": "Кто будет напоминать о событиях?", + "reminders_desc": "Кто напоминает о синхронизированных событиях. Второе приложение молчит, чтобы напоминания не приходили дважды.", + "reminders_label": "Напоминания", + "reminders_nobody": "Уведомления о событиях в Bulwark выключены, поэтому о синхронизированных событиях ничто не напомнит.", + "remove": "Удалить", + "remove_from_device": "Удалить с этого устройства", + "remove_message": "Контакты и календари аккаунта будут удалены с этого устройства. Сделанные здесь изменения, которые не дошли до сервера, будут потеряны.", + "remove_title": "Удалить с этого устройства?", + "review_deletions": "Проверить удаления", + "shared_by": "Владелец: {name}", + "sign_in_again": "Войти снова", + "status_auth": "Сервер больше не принимает вход. Войдите снова, чтобы продолжить синхронизацию.", + "status_cancelled": "Последняя синхронизация прервалась. Она продолжится в следующий раз.", + "status_failed": "Ошибка последней синхронизации: {message}", + "status_failed_plain": "Последняя синхронизация не удалась.", + "status_io": "Не удалось связаться с сервером {when}. Android повторит попытку.", + "status_item_errors": "{count, plural, one {Не удалось синхронизировать # элемент.} few {Не удалось синхронизировать # элемента.} many {Не удалось синхронизировать # элементов.} other {Не удалось синхронизировать # элемента.}}", + "status_off": "Выкл.", + "status_paused": "Приостановлено в настройках Android", + "status_permission_calendar": "Доступ к календарям отключён, поэтому ничего не синхронизируется.", + "status_permission_contacts": "Доступ к контактам отключён, поэтому ничего не синхронизируется.", + "status_removed": "Удалено в настройках Android. Включите синхронизацию, чтобы снова добавить аккаунт.", + "status_safety_abort": "Остановлено: сервер ничего не вернул, хотя на этом устройстве ещё есть элементы. Проверьте сервер и повторите синхронизацию.", + "status_synced": "Синхронизировано {when}", + "status_syncing": "Синхронизация…", + "status_too_many_deletions": "На этом устройстве удалено много элементов. На сервере они будут удалены только после вашего подтверждения.", + "status_unsupported_calendar": "Сервер не предоставляет календари для этого аккаунта.", + "status_unsupported_contacts": "Сервер не предоставляет адресные книги для этого аккаунта.", + "status_waiting": "Ожидание первой синхронизации", + "status_waiting_next": "Ожидание следующей синхронизации", + "suspended_desc": "Выполнен выход. Контакты и календари аккаунта остаются на этом устройстве вместе со сделанными здесь изменениями и снова синхронизируются после входа.", + "switch_label": "Синхронизировать {account} с этим устройством", + "sync_now": "Синхронизировать", + "turn_off_anyway": "Выключить", + "turn_off_failed": "Не удалось удалить синхронизированные данные с этого устройства. Синхронизация приостановлена; повторите попытку позже.", + "turn_off_failed_title": "Не удалось выключить синхронизацию", + "turn_off_pending": "{count, plural, one {# изменение, сделанное на этом устройстве, ещё не дошло до сервера. Оно будет потеряно, если сейчас выключить синхронизацию.} few {# изменения, сделанные на этом устройстве, ещё не дошли до сервера. Они будут потеряны, если сейчас выключить синхронизацию.} many {# изменений, сделанных на этом устройстве, ещё не дошли до сервера. Они будут потеряны, если сейчас выключить синхронизацию.} other {# изменения, сделанного на этом устройстве, ещё не дошли до сервера. Они будут потеряны, если сейчас выключить синхронизацию.}}", + "turn_off_title": "Всё равно выключить?", + "turn_off_unknown": "Bulwark не удалось проверить, дошли ли изменения, сделанные на этом устройстве, до сервера. Они будут потеряны, если сейчас выключить синхронизацию.", + "turning_off": "Отправка изменений, затем удаление данных с этого устройства…", + "turning_on": "Настройка…" + }, "downloads": { "attachment_template": { "description_mobile": "Шаблон имени при сохранении вложения или при отправке его в другое приложение." diff --git a/locales/rn/sk.json b/locales/rn/sk.json index befaa934..ca165034 100644 --- a/locales/rn/sk.json +++ b/locales/rn/sk.json @@ -93,6 +93,8 @@ "until_date": "Do {date}" }, "settings": { + "device_sync_desc": "Zobrazí vaše kalendáre v aplikácii Kalendár a ďalších aplikáciách v tomto zariadení. Zmeny sa synchronizujú oboma smermi.", + "device_sync_title": "Synchronizovať s týmto zariadením", "show_week_numbers_mobile_desc": "Zobrazovať čísla týždňov v mesačnom zobrazení.", "time_zone": "Časové pásmo", "time_zone_auto": "Časové pásmo zariadenia", @@ -249,6 +251,18 @@ "color_tag": "Štítok", "remove_color": "Odstrániť štítok" }, + "device_sync": { + "auth_problem_text": "Server už neprijíma prihlásenie účtu {account}, preto sa jeho kontakty a kalendáre v tomto zariadení prestali synchronizovať. Ak chcete pokračovať, znova sa prihláste. Zmeny vykonané v tomto zariadení zostanú zachované.", + "auth_problem_title": "Ak chcete ďalej synchronizovať, znova sa prihláste", + "problem_channel": "Problémy so synchronizáciou", + "sign_out_anyway": "Odhlásiť sa", + "sign_out_pending_message": "{count, plural, one {# zmena vykonaná v tomto zariadení sa nedostala na server. Ak sa teraz odhlásite, stratí sa.} few {# zmeny vykonané v tomto zariadení sa nedostali na server. Ak sa teraz odhlásite, stratia sa.} many {# zmeny vykonanej v tomto zariadení sa nedostalo na server. Ak sa teraz odhlásite, stratia sa.} other {# zmien vykonaných v tomto zariadení sa nedostalo na server. Ak sa teraz odhlásite, stratia sa.}}", + "sign_out_pending_title": "Napriek tomu sa odhlásiť?", + "sign_out_unknown_message": "Zmeny vykonané v tomto zariadení sa možno nedostali na server. Ak sa teraz odhlásite, stratia sa.", + "signed_out_text": "Účet {account} bol odhlásený, preto sa jeho kontakty a kalendáre v tomto zariadení už nesynchronizujú. Ak chcete pokračovať, znova sa prihláste. Zmeny vykonané v tomto zariadení zostanú zachované.", + "signed_out_title": "Ak chcete ďalej synchronizovať, prihláste sa", + "signing_out": "Odstraňujú sa synchronizované kontakty a kalendáre z tohto zariadenia…" + }, "email_composer": { "attach_camera": "Fotoaparát", "attachment_reminder_body": "Vaša správa spomína „{keyword}“, ale nie je priložený žiadny súbor. Odoslať aj tak?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# kontakt} few {# kontakty} many {# kontaktu} other {# kontaktov}}", "default_book": "Predvolený adresár", "description_mobile": "Predvoľby zobrazenia a nástroje pre adresáre.", + "device_sync_desc": "Zobrazí vaše adresáre v aplikácii Kontakty a ďalších aplikáciách v tomto zariadení. Zmeny sa synchronizujú oboma smermi.", + "device_sync_title": "Synchronizovať s týmto zariadením", "export_description_count": "{count, plural, one {Exportovať # kontakt do jedného súboru vCard.} few {Exportovať # kontakty do jedného súboru vCard.} many {Exportovať # kontaktu do jedného súboru vCard.} other {Exportovať # kontaktov do jedného súboru vCard.}}", "export_empty": "Žiadne kontakty na export", "import_description_mobile": "Importovať kontakty zo súboru vCard (.vcf) s rozpoznaním duplicít.", "manage_description_mobile": "Vytvárajte, premenúvajte a odstraňujte adresáre, v ktorých máte kontakty usporiadané." }, + "device_sync": { + "also_synced_by": "Synchronizuje sa aj pre {accounts}, preto sa v tomto zariadení zobrazuje dvakrát.", + "android_settings": "Nastavenia účtu v Androide", + "blocked_calendar": "Android už o prístup ku kalendárom nežiada. Povoľte ho v nastaveniach aplikácie a potom synchronizáciu znova zapnite.", + "blocked_contacts": "Android už o prístup ku kontaktom nežiada. Povoľte ho v nastaveniach aplikácie a potom synchronizáciu znova zapnite.", + "blocked_title": "Prístup je vypnutý", + "books_default": "Vaše vlastné adresáre sa synchronizujú, zdieľané iba vtedy, keď ich vyberiete.", + "books_hint": "Vaše vlastné adresáre sa synchronizujú, ak ich nevypnete; zdieľané iba vtedy, keď ich zapnete.", + "books_label": "Adresáre", + "calendars_default": "Vaše vlastné kalendáre sa synchronizujú, zdieľané iba vtedy, keď ich vyberiete.", + "calendars_hint": "Vaše vlastné kalendáre sa synchronizujú, ak ich nevypnete; zdieľané iba vtedy, keď ich zapnete. Zoznamy úloh zostávajú v Bulwarku.", + "calendars_label": "Kalendáre", + "choose": "Vybrať", + "choose_books_title": "Adresáre na synchronizáciu", + "choose_calendars_title": "Kalendáre na synchronizáciu", + "collections_count": "Synchronizované: {selected} z {total}", + "continue": "Pokračovať", + "deletions_delete": "Odstrániť na serveri", + "deletions_message": "V tomto zariadení sa naraz odstránilo veľa položiek, preto sa neodstránili na serveri. Odstráňte ich aj tam, alebo ich v tomto zariadení obnovte.", + "deletions_restore": "Obnoviť", + "deletions_title": "Odstrániť ich aj na serveri?", + "denied_calendar": "Bez prístupu ku kalendárom nemôže Bulwark preniesť vaše kalendáre do tohto zariadenia.", + "denied_contacts": "Bez prístupu ku kontaktom nemôže Bulwark preniesť vaše adresáre do tohto zariadenia.", + "denied_title": "Synchronizácia zostáva vypnutá", + "failed_title": "Synchronizáciu sa nepodarilo zapnúť", + "grant_access": "Udeliť prístup", + "interval_15m": "Každých 15 minút", + "interval_1h": "Každú hodinu", + "interval_30m": "Každých 30 minút", + "interval_6h": "Každých 6 hodín", + "interval_desc": "Ako často Android synchronizuje sám. Úpravy v Bulwarku a zmeny oznámené cez push sa synchronizujú skôr.", + "interval_label": "Interval synchronizácie", + "interval_manual": "Bez pravidelnej synchronizácie", + "keep_syncing": "Ďalej synchronizovať", + "list_error_auth": "Server neprijal prihlásenie.", + "list_error_network": "Nepodarilo sa spojiť so serverom.", + "list_error_server": "Server nedokázal poskytnúť zoznam.", + "list_error_signed_out": "Tento účet nie je v tomto zariadení prihlásený.", + "master_off": "Automatická synchronizácia je v tomto zariadení vypnutá, preto sa synchronizuje iba po klepnutí na „Synchronizovať teraz“.", + "new_contacts_default": "Predvolený adresár", + "new_contacts_desc": "Adresár pre kontakty vytvorené v iných aplikáciách v tomto zariadení.", + "new_contacts_label": "Nové kontakty ukladať do", + "no_books": "Tento účet nemá žiadne adresáre.", + "no_calendars": "Tento účet nemá žiadne kalendáre na synchronizáciu.", + "ok": "OK", + "open_app_settings": "Otvoriť nastavenia", + "own_collections": "Vlastné", + "rationale_calendar": "Aby mohol Bulwark zobraziť vaše kalendáre v aplikácii Kalendár, potrebuje prístup ku kalendárom v tomto zariadení. Mení iba kalendáre svojho vlastného účtu.", + "rationale_contacts": "Aby mohol Bulwark zobraziť vaše adresáre v aplikácii Kontakty, potrebuje prístup ku kontaktom v tomto zariadení. Mení iba kontakty svojho vlastného účtu.", + "rationale_title_calendar": "Povoliť prístup ku kalendárom", + "rationale_title_contacts": "Povoliť prístup ku kontaktom", + "read_only": "Iba na čítanie", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Aplikácia Kalendár", + "reminder_question_message": "Pripomenutia synchronizovaných udalostí môže zobrazovať aplikácia kalendára v tomto zariadení alebo Bulwark. Vyberte jednu možnosť, aby ste nedostávali pripomenutia dvakrát. Neskôr to môžete zmeniť.", + "reminder_question_title": "Kto vám má pripomínať udalosti?", + "reminders_desc": "Kto vám pripomína synchronizované udalosti. Druhá možnosť mlčí, aby ste nedostávali pripomenutia dvakrát.", + "reminders_label": "Pripomenutia", + "reminders_nobody": "Oznámenia udalostí v Bulwarku sú vypnuté, takže vám synchronizované udalosti nič nepripomenie.", + "remove": "Odstrániť", + "remove_from_device": "Odstrániť z tohto zariadenia", + "remove_message": "Kontakty a kalendáre účtu sa z tohto zariadenia odstránia. Zmeny vykonané tu, ktoré sa nedostali na server, sa stratia.", + "remove_title": "Odstrániť z tohto zariadenia?", + "review_deletions": "Skontrolovať odstránenia", + "shared_by": "Zdieľané od {name}", + "sign_in_again": "Znova sa prihlásiť", + "status_auth": "Server už neprijíma prihlásenie. Ak chcete ďalej synchronizovať, znova sa prihláste.", + "status_cancelled": "Posledná synchronizácia sa skončila predčasne. Nabudúce bude pokračovať.", + "status_failed": "Posledná synchronizácia zlyhala: {message}", + "status_failed_plain": "Posledná synchronizácia zlyhala.", + "status_io": "Server bol {when} nedostupný. Android to skúsi znova.", + "status_item_errors": "{count, plural, one {# položku sa nepodarilo synchronizovať.} few {# položky sa nepodarilo synchronizovať.} many {# položky sa nepodarilo synchronizovať.} other {# položiek sa nepodarilo synchronizovať.}}", + "status_off": "Vypnuté", + "status_paused": "Pozastavené v nastaveniach Androidu", + "status_permission_calendar": "Prístup ku kalendárom bol vypnutý, preto sa nič nesynchronizuje.", + "status_permission_contacts": "Prístup ku kontaktom bol vypnutý, preto sa nič nesynchronizuje.", + "status_removed": "Odstránené v nastaveniach Androidu. Ak chcete účet znova pridať, zapnite synchronizáciu.", + "status_safety_abort": "Zastavené: server nič nevrátil, hoci toto zariadenie stále obsahuje položky. Skontrolujte server a potom synchronizujte znova.", + "status_synced": "Synchronizované {when}", + "status_syncing": "Synchronizuje sa…", + "status_too_many_deletions": "V tomto zariadení sa odstránilo veľa položiek. Na serveri sa odstránia až po vašom potvrdení.", + "status_unsupported_calendar": "Server pre tento účet neponúka žiadne kalendáre.", + "status_unsupported_contacts": "Server pre tento účet neponúka žiadne adresáre.", + "status_waiting": "Čaká sa na prvú synchronizáciu", + "status_waiting_next": "Čaká sa na ďalšiu synchronizáciu", + "suspended_desc": "Odhlásené. Kontakty a kalendáre účtu zostanú v tomto zariadení aj so zmenami vykonanými tu a po prihlásení sa znova synchronizujú.", + "switch_label": "Synchronizovať {account} s týmto zariadením", + "sync_now": "Synchronizovať teraz", + "turn_off_anyway": "Vypnúť", + "turn_off_failed": "Synchronizované údaje sa nepodarilo z tohto zariadenia odstrániť. Synchronizácia je pozastavená; skúste to neskôr.", + "turn_off_failed_title": "Synchronizáciu sa nepodarilo vypnúť", + "turn_off_pending": "{count, plural, one {# zmena vykonaná v tomto zariadení sa ešte nedostala na server. Ak teraz synchronizáciu vypnete, stratí sa.} few {# zmeny vykonané v tomto zariadení sa ešte nedostali na server. Ak teraz synchronizáciu vypnete, stratia sa.} many {# zmeny vykonanej v tomto zariadení sa ešte nedostalo na server. Ak teraz synchronizáciu vypnete, stratia sa.} other {# zmien vykonaných v tomto zariadení sa ešte nedostalo na server. Ak teraz synchronizáciu vypnete, stratia sa.}}", + "turn_off_title": "Napriek tomu vypnúť?", + "turn_off_unknown": "Bulwark nemohol overiť, či sa zmeny vykonané v tomto zariadení dostali na server. Ak teraz synchronizáciu vypnete, stratia sa.", + "turning_off": "Nahrávajú sa zmeny a potom sa z tohto zariadenia odstránia údaje…", + "turning_on": "Nastavuje sa…" + }, "downloads": { "attachment_template": { "description_mobile": "Šablóna použitá pri ukladaní alebo zdieľaní prílohy." diff --git a/locales/rn/tr.json b/locales/rn/tr.json index cd1c496e..456ae1fe 100644 --- a/locales/rn/tr.json +++ b/locales/rn/tr.json @@ -93,6 +93,8 @@ "until_date": "{date} tarihine kadar" }, "settings": { + "device_sync_desc": "Takvimlerinizi bu cihazdaki Takvim uygulamasında ve diğer uygulamalarda gösterin. Değişiklikler iki yönlü senkronize edilir.", + "device_sync_title": "Bu cihazla senkronize et", "show_week_numbers_mobile_desc": "Ay görünümünde hafta numaralarını göster.", "time_zone": "Saat dilimi", "time_zone_auto": "Cihaz saat dilimi", @@ -249,6 +251,18 @@ "color_tag": "Etiket", "remove_color": "Etiketi kaldır" }, + "device_sync": { + "auth_problem_text": "Sunucu artık {account} hesabının girişini kabul etmiyor, bu nedenle hesabın bu cihazdaki kişileri ve takvimleri artık senkronize edilmiyor. Devam etmek için tekrar giriş yapın. Bu cihazda yapılan değişiklikler korunur.", + "auth_problem_title": "Senkronizasyona devam etmek için tekrar giriş yapın", + "problem_channel": "Senkronizasyon sorunları", + "sign_out_anyway": "Çıkış yap", + "sign_out_pending_message": "{count, plural, one {Bu cihazda yapılan # değişiklik sunucuya ulaşmadı. Şimdi çıkış yaparsanız kaybolur.} other {Bu cihazda yapılan # değişiklik sunucuya ulaşmadı. Şimdi çıkış yaparsanız kaybolur.}}", + "sign_out_pending_title": "Yine de çıkış yapılsın mı?", + "sign_out_unknown_message": "Bu cihazda yapılan değişiklikler sunucuya ulaşmamış olabilir. Şimdi çıkış yaparsanız kaybolur.", + "signed_out_text": "{account} hesabından çıkış yapıldı, bu nedenle hesabın bu cihazdaki kişileri ve takvimleri artık senkronize edilmiyor. Devam etmek için tekrar giriş yapın. Bu cihazda yapılan değişiklikler korunur.", + "signed_out_title": "Senkronizasyona devam etmek için giriş yapın", + "signing_out": "Senkronize edilen kişiler ve takvimler bu cihazdan kaldırılıyor…" + }, "email_composer": { "attach_camera": "Kamera", "attachment_reminder_body": "İletinizde \"{keyword}\" geçiyor ancak hiçbir dosya eklenmemiş. Yine de gönderilsin mi?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# kişi} other {# kişi}}", "default_book": "Varsayılan adres defteri", "description_mobile": "Görüntüleme tercihleri ve adres defteri araçları.", + "device_sync_desc": "Adres defterlerinizi bu cihazdaki Kişiler uygulamasında ve diğer uygulamalarda gösterin. Değişiklikler iki yönlü senkronize edilir.", + "device_sync_title": "Bu cihazla senkronize et", "export_description_count": "{count, plural, one {# kişiyi tek bir vCard dosyası olarak dışa aktarın.} other {# kişiyi tek bir vCard dosyası olarak dışa aktarın.}}", "export_empty": "Dışa aktarılacak kişi yok", "import_description_mobile": "Kişileri, yinelenenleri algılayarak bir vCard (.vcf) dosyasından içe aktarın.", "manage_description_mobile": "Kişilerinizi düzenleyen adres defterlerini oluşturun, yeniden adlandırın ve kaldırın." }, + "device_sync": { + "also_synced_by": "{accounts} için de senkronize ediliyor, bu nedenle bu cihazda iki kez görünüyor.", + "android_settings": "Android hesap ayarları", + "blocked_calendar": "Android artık takvimlere erişim istemiyor. Uygulama ayarlarından erişime izin verin, ardından senkronizasyonu tekrar açın.", + "blocked_contacts": "Android artık kişilere erişim istemiyor. Uygulama ayarlarından erişime izin verin, ardından senkronizasyonu tekrar açın.", + "blocked_title": "Erişim kapalı", + "books_default": "Kendi adres defterleriniz senkronize edilir; paylaşılanlar yalnızca siz seçtiğinizde.", + "books_hint": "Kendi adres defterleriniz siz kapatmadığınız sürece senkronize edilir; paylaşılanlar yalnızca siz açtığınızda.", + "books_label": "Adres defterleri", + "calendars_default": "Kendi takvimleriniz senkronize edilir; paylaşılanlar yalnızca siz seçtiğinizde.", + "calendars_hint": "Kendi takvimleriniz siz kapatmadığınız sürece senkronize edilir; paylaşılanlar yalnızca siz açtığınızda. Görev listeleri Bulwark'ta kalır.", + "calendars_label": "Takvimler", + "choose": "Seç", + "choose_books_title": "Senkronize edilecek adres defterleri", + "choose_calendars_title": "Senkronize edilecek takvimler", + "collections_count": "Senkronize edilen: {selected}/{total}", + "continue": "Devam", + "deletions_delete": "Sunucuda sil", + "deletions_message": "Bu cihazda aynı anda çok sayıda öğe silindi, bu nedenle sunucuda silinmediler. Bunları sunucuda da silin veya bu cihaza geri getirin.", + "deletions_restore": "Geri getir", + "deletions_title": "Sunucuda da silinsin mi?", + "denied_calendar": "Takvimlere erişim olmadan Bulwark takvimlerinizi bu cihaza getiremez.", + "denied_contacts": "Kişilere erişim olmadan Bulwark adres defterlerinizi bu cihaza getiremez.", + "denied_title": "Senkronizasyon kapalı kalıyor", + "failed_title": "Senkronizasyon açılamadı", + "grant_access": "Erişim izni ver", + "interval_15m": "Her 15 dakikada bir", + "interval_1h": "Her saat", + "interval_30m": "Her 30 dakikada bir", + "interval_6h": "Her 6 saatte bir", + "interval_desc": "Android'in kendi kendine ne sıklıkla senkronize edeceği. Bulwark'taki düzenlemeler ve push ile bildirilen değişiklikler daha erken senkronize edilir.", + "interval_label": "Senkronizasyon aralığı", + "interval_manual": "Düzenli senkronizasyon yok", + "keep_syncing": "Senkronizasyona devam et", + "list_error_auth": "Sunucu girişi kabul etmedi.", + "list_error_network": "Sunucuya ulaşılamadı.", + "list_error_server": "Sunucu listeyi sağlayamadı.", + "list_error_signed_out": "Bu cihazda bu hesapla giriş yapılmamış.", + "master_off": "Bu cihazda otomatik senkronizasyon kapalı, bu nedenle senkronizasyon yalnızca \"Şimdi senkronize et\" düğmesine dokunduğunuzda yapılır.", + "new_contacts_default": "Varsayılan adres defteri", + "new_contacts_desc": "Bu cihazdaki diğer uygulamalarda oluşturulan kişilerin kaydedileceği adres defteri.", + "new_contacts_label": "Yeni kişilerin kaydedileceği yer", + "no_books": "Bu hesabın adres defteri yok.", + "no_calendars": "Bu hesabın senkronize edilecek takvimi yok.", + "ok": "Tamam", + "open_app_settings": "Uygulama ayarlarını aç", + "own_collections": "Size ait", + "rationale_calendar": "Takvimlerinizi Takvim uygulamasında göstermek için Bulwark'ın bu cihazdaki takvimlere erişmesi gerekir. Yalnızca kendi hesabının takvimlerini değiştirir.", + "rationale_contacts": "Adres defterlerinizi Kişiler uygulamasında göstermek için Bulwark'ın bu cihazdaki kişilere erişmesi gerekir. Yalnızca kendi hesabının kişilerini değiştirir.", + "rationale_title_calendar": "Takvimlere erişim izni verin", + "rationale_title_contacts": "Kişilere erişim izni verin", + "read_only": "Salt okunur", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Takvim uygulaması", + "reminder_question_message": "Senkronize edilen etkinlikler için hatırlatmalar bu cihazdaki takvim uygulamasından veya Bulwark'tan gelebilir. İki kez hatırlatılmamak için birini seçin. Bunu daha sonra değiştirebilirsiniz.", + "reminder_question_title": "Size kim hatırlatsın?", + "reminders_desc": "Senkronize edilen etkinlikleri size kimin hatırlatacağı. Diğeri sessiz kalır, böylece iki kez hatırlatılmazsınız.", + "reminders_label": "Hatırlatıcılar", + "reminders_nobody": "Bulwark'ın etkinlik bildirimleri kapalı, bu nedenle senkronize edilen etkinlikleri size hiçbir şey hatırlatmaz.", + "remove": "Kaldır", + "remove_from_device": "Bu cihazdan kaldır", + "remove_message": "Hesabın kişileri ve takvimleri bu cihazdan kaldırılır. Burada yapılan ve sunucuya ulaşmamış değişiklikler kaybolur.", + "remove_title": "Bu cihazdan kaldırılsın mı?", + "review_deletions": "Silmeleri incele", + "shared_by": "Paylaşan: {name}", + "sign_in_again": "Tekrar giriş yap", + "status_auth": "Sunucu artık girişi kabul etmiyor. Senkronizasyona devam etmek için tekrar giriş yapın.", + "status_cancelled": "Son senkronizasyon erken durdu. Bir dahaki sefere devam edecek.", + "status_failed": "Son senkronizasyon başarısız: {message}", + "status_failed_plain": "Son senkronizasyon başarısız oldu.", + "status_io": "Sunucuya {when} ulaşılamadı. Android tekrar deneyecek.", + "status_item_errors": "{count, plural, one {# öğe senkronize edilemedi.} other {# öğe senkronize edilemedi.}}", + "status_off": "Kapalı", + "status_paused": "Android ayarlarında duraklatıldı", + "status_permission_calendar": "Takvimlere erişim kapatıldı, bu nedenle hiçbir şey senkronize edilmiyor.", + "status_permission_contacts": "Kişilere erişim kapatıldı, bu nedenle hiçbir şey senkronize edilmiyor.", + "status_removed": "Android ayarlarında kaldırıldı. Hesabı yeniden eklemek için senkronizasyonu açın.", + "status_safety_abort": "Durduruldu: bu cihazda hâlâ öğeler varken sunucu hiçbir şey döndürmedi. Sunucuyu kontrol edin, ardından yeniden senkronize edin.", + "status_synced": "Son senkronizasyon: {when}", + "status_syncing": "Senkronize ediliyor…", + "status_too_many_deletions": "Bu cihazda çok sayıda öğe silindi. Siz onaylayana kadar sunucuda silinmezler.", + "status_unsupported_calendar": "Sunucu bu hesap için takvim sunmuyor.", + "status_unsupported_contacts": "Sunucu bu hesap için adres defteri sunmuyor.", + "status_waiting": "İlk senkronizasyon bekleniyor", + "status_waiting_next": "Sonraki senkronizasyon bekleniyor", + "suspended_desc": "Çıkış yapıldı. Hesabın kişileri ve takvimleri, burada yapılan değişikliklerle birlikte bu cihazda kalır ve giriş yaptığınızda yeniden senkronize edilir.", + "switch_label": "{account} hesabını bu cihazla senkronize et", + "sync_now": "Şimdi senkronize et", + "turn_off_anyway": "Kapat", + "turn_off_failed": "Senkronize edilen veriler bu cihazdan kaldırılamadı. Senkronizasyon duraklatıldı; daha sonra tekrar deneyin.", + "turn_off_failed_title": "Senkronizasyon kapatılamadı", + "turn_off_pending": "{count, plural, one {Bu cihazda yapılan # değişiklik henüz sunucuya ulaşmadı. Senkronizasyonu şimdi kapatırsanız kaybolur.} other {Bu cihazda yapılan # değişiklik henüz sunucuya ulaşmadı. Senkronizasyonu şimdi kapatırsanız kaybolur.}}", + "turn_off_title": "Yine de kapatılsın mı?", + "turn_off_unknown": "Bulwark, bu cihazda yapılan değişikliklerin sunucuya ulaşıp ulaşmadığını kontrol edemedi. Senkronizasyonu şimdi kapatırsanız kaybolur.", + "turning_off": "Değişiklikler yükleniyor, ardından veriler bu cihazdan kaldırılıyor…", + "turning_on": "Ayarlanıyor…" + }, "downloads": { "attachment_template": { "description_mobile": "Bir eki kaydederken veya paylaşırken kullanılan şablon." diff --git a/locales/rn/uk.json b/locales/rn/uk.json index a885fa36..968dc329 100644 --- a/locales/rn/uk.json +++ b/locales/rn/uk.json @@ -93,6 +93,8 @@ "until_date": "До {date}" }, "settings": { + "device_sync_desc": "Ваші календарі відображатимуться в застосунку «Календар» та інших застосунках на цьому пристрої. Зміни синхронізуються в обидва боки.", + "device_sync_title": "Синхронізація з цим пристроєм", "show_week_numbers_mobile_desc": "Показувати номери тижнів у поданні «Місяць».", "time_zone": "Часовий пояс", "time_zone_auto": "Часовий пояс пристрою", @@ -249,6 +251,18 @@ "color_tag": "Тег", "remove_color": "Прибрати тег" }, + "device_sync": { + "auth_problem_text": "Сервер більше не приймає вхід в обліковий запис {account}, тому його контакти й календарі на цьому пристрої більше не синхронізуються. Увійдіть знову, щоб відновити синхронізацію. Зміни, внесені на цьому пристрої, збережуться.", + "auth_problem_title": "Увійдіть знову, щоб продовжити синхронізацію", + "problem_channel": "Проблеми із синхронізацією", + "sign_out_anyway": "Вийти", + "sign_out_pending_message": "{count, plural, one {# зміна, внесена на цьому пристрої, не дійшла до сервера. Її буде втрачено, якщо ви зараз вийдете.} few {# зміни, внесені на цьому пристрої, не дійшли до сервера. Їх буде втрачено, якщо ви зараз вийдете.} many {# змін, внесених на цьому пристрої, не дійшли до сервера. Їх буде втрачено, якщо ви зараз вийдете.} other {# зміни, внесеної на цьому пристрої, не дійшли до сервера. Їх буде втрачено, якщо ви зараз вийдете.}}", + "sign_out_pending_title": "Усе одно вийти?", + "sign_out_unknown_message": "Зміни, внесені на цьому пристрої, могли не дійти до сервера. Їх буде втрачено, якщо ви зараз вийдете.", + "signed_out_text": "Виконано вихід з облікового запису {account}, тому його контакти й календарі на цьому пристрої більше не синхронізуються. Увійдіть знову, щоб відновити синхронізацію. Зміни, внесені на цьому пристрої, збережуться.", + "signed_out_title": "Увійдіть, щоб продовжити синхронізацію", + "signing_out": "Видалення синхронізованих контактів і календарів із цього пристрою…" + }, "email_composer": { "attach_camera": "Камера", "attachment_reminder_body": "У листі згадано «{keyword}», але файл не вкладено. Усе одно надіслати?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, one {# контакт} few {# контакти} many {# контактів} other {# контакту}}", "default_book": "Адресна книга за замовчуванням", "description_mobile": "Параметри відображення та інструменти адресних книг.", + "device_sync_desc": "Ваші адресні книги відображатимуться в застосунку «Контакти» та інших застосунках на цьому пристрої. Зміни синхронізуються в обидва боки.", + "device_sync_title": "Синхронізація з цим пристроєм", "export_description_count": "{count, plural, one {Експортувати # контакт в один файл vCard.} few {Експортувати # контакти в один файл vCard.} many {Експортувати # контактів в один файл vCard.} other {Експортувати # контакту в один файл vCard.}}", "export_empty": "Немає контактів для експорту", "import_description_mobile": "Імпорт контактів із файлу vCard (.vcf) з виявленням дублікатів.", "manage_description_mobile": "Створюйте, перейменовуйте та видаляйте адресні книги, у яких зберігаються ваші контакти." }, + "device_sync": { + "also_synced_by": "Також синхронізується для {accounts}, тому відображається на цьому пристрої двічі.", + "android_settings": "Налаштування облікового запису Android", + "blocked_calendar": "Android більше не запитує доступ до календарів. Дозвольте його в налаштуваннях застосунку, а потім знову ввімкніть синхронізацію.", + "blocked_contacts": "Android більше не запитує доступ до контактів. Дозвольте його в налаштуваннях застосунку, а потім знову ввімкніть синхронізацію.", + "blocked_title": "Доступ вимкнено", + "books_default": "Ваші власні адресні книги синхронізуються, а спільні — лише якщо ви їх виберете.", + "books_hint": "Ваші власні адресні книги синхронізуються, якщо ви їх не вимкнете, а спільні — лише якщо ви їх увімкнете.", + "books_label": "Адресні книги", + "calendars_default": "Ваші власні календарі синхронізуються, а спільні — лише якщо ви їх виберете.", + "calendars_hint": "Ваші власні календарі синхронізуються, якщо ви їх не вимкнете, а спільні — лише якщо ви їх увімкнете. Списки завдань залишаються в Bulwark.", + "calendars_label": "Календарі", + "choose": "Вибрати", + "choose_books_title": "Адресні книги для синхронізації", + "choose_calendars_title": "Календарі для синхронізації", + "collections_count": "Синхронізується: {selected} з {total}", + "continue": "Продовжити", + "deletions_delete": "Видалити на сервері", + "deletions_message": "На цьому пристрої одночасно видалено багато елементів, тому на сервері їх не видалено. Видаліть їх і там або поверніть їх на цей пристрій.", + "deletions_restore": "Повернути", + "deletions_title": "Видалити їх і на сервері?", + "denied_calendar": "Без доступу до календарів Bulwark не зможе перенести ваші календарі на цей пристрій.", + "denied_contacts": "Без доступу до контактів Bulwark не зможе перенести ваші адресні книги на цей пристрій.", + "denied_title": "Синхронізація залишається вимкненою", + "failed_title": "Не вдалося ввімкнути синхронізацію", + "grant_access": "Надати доступ", + "interval_15m": "Кожні 15 хвилин", + "interval_1h": "Щогодини", + "interval_30m": "Кожні 30 хвилин", + "interval_6h": "Кожні 6 годин", + "interval_desc": "Як часто Android синхронізує дані самостійно. Зміни в Bulwark і зміни, про які повідомляє push, синхронізуються швидше.", + "interval_label": "Інтервал синхронізації", + "interval_manual": "Без регулярної синхронізації", + "keep_syncing": "Продовжити синхронізацію", + "list_error_auth": "Сервер не прийняв вхід.", + "list_error_network": "Не вдалося зв'язатися із сервером.", + "list_error_server": "Сервер не зміг повернути список.", + "list_error_signed_out": "На цьому пристрої не виконано вхід у цей обліковий запис.", + "master_off": "Автосинхронізацію на цьому пристрої вимкнено, тому синхронізація виконується лише після натискання «Синхронізувати».", + "new_contacts_default": "Адресна книга за замовчуванням", + "new_contacts_desc": "Адресна книга для контактів, створених в інших застосунках на цьому пристрої.", + "new_contacts_label": "Зберігати нові контакти в", + "no_books": "У цьому обліковому записі немає адресних книг.", + "no_calendars": "У цьому обліковому записі немає календарів для синхронізації.", + "ok": "OK", + "open_app_settings": "Відкрити налаштування", + "own_collections": "Ваші", + "rationale_calendar": "Щоб показувати ваші календарі в застосунку «Календар», Bulwark потрібен доступ до календарів на цьому пристрої. Він змінює лише календарі свого облікового запису.", + "rationale_contacts": "Щоб показувати ваші адресні книги в застосунку «Контакти», Bulwark потрібен доступ до контактів на цьому пристрої. Він змінює лише контакти свого облікового запису.", + "rationale_title_calendar": "Дозволити доступ до календарів", + "rationale_title_contacts": "Дозволити доступ до контактів", + "read_only": "Лише для читання", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "Застосунок «Календар»", + "reminder_question_message": "Нагадування про синхронізовані події може показувати застосунок календаря на цьому пристрої або Bulwark. Виберіть щось одне, щоб не отримувати нагадування двічі. Це можна змінити пізніше.", + "reminder_question_title": "Хто нагадуватиме про події?", + "reminders_desc": "Хто нагадує про синхронізовані події. Інший застосунок мовчить, щоб нагадування не надходили двічі.", + "reminders_label": "Нагадування", + "reminders_nobody": "Сповіщення про події в Bulwark вимкнено, тому про синхронізовані події ніщо не нагадає.", + "remove": "Видалити", + "remove_from_device": "Видалити з цього пристрою", + "remove_message": "Контакти й календарі облікового запису буде видалено з цього пристрою. Внесені тут зміни, які не дійшли до сервера, буде втрачено.", + "remove_title": "Видалити з цього пристрою?", + "review_deletions": "Переглянути видалення", + "shared_by": "Власник: {name}", + "sign_in_again": "Увійти знову", + "status_auth": "Сервер більше не приймає вхід. Увійдіть знову, щоб продовжити синхронізацію.", + "status_cancelled": "Остання синхронізація перервалася. Вона продовжиться наступного разу.", + "status_failed": "Помилка останньої синхронізації: {message}", + "status_failed_plain": "Остання синхронізація не вдалася.", + "status_io": "Не вдалося зв'язатися із сервером {when}. Android повторить спробу.", + "status_item_errors": "{count, plural, one {Не вдалося синхронізувати # елемент.} few {Не вдалося синхронізувати # елементи.} many {Не вдалося синхронізувати # елементів.} other {Не вдалося синхронізувати # елемента.}}", + "status_off": "Вимкнено", + "status_paused": "Призупинено в налаштуваннях Android", + "status_permission_calendar": "Доступ до календарів вимкнено, тому нічого не синхронізується.", + "status_permission_contacts": "Доступ до контактів вимкнено, тому нічого не синхронізується.", + "status_removed": "Видалено в налаштуваннях Android. Увімкніть синхронізацію, щоб знову додати обліковий запис.", + "status_safety_abort": "Зупинено: сервер нічого не повернув, хоча на цьому пристрої ще є елементи. Перевірте сервер і повторіть синхронізацію.", + "status_synced": "Синхронізовано {when}", + "status_syncing": "Синхронізація…", + "status_too_many_deletions": "На цьому пристрої видалено багато елементів. На сервері їх буде видалено лише після вашого підтвердження.", + "status_unsupported_calendar": "Сервер не надає календарів для цього облікового запису.", + "status_unsupported_contacts": "Сервер не надає адресних книг для цього облікового запису.", + "status_waiting": "Очікування першої синхронізації", + "status_waiting_next": "Очікування наступної синхронізації", + "suspended_desc": "Виконано вихід. Контакти й календарі облікового запису залишаються на цьому пристрої разом із внесеними тут змінами й знову синхронізуються після входу.", + "switch_label": "Синхронізувати {account} з цим пристроєм", + "sync_now": "Синхронізувати", + "turn_off_anyway": "Вимкнути", + "turn_off_failed": "Не вдалося видалити синхронізовані дані з цього пристрою. Синхронізацію призупинено; спробуйте пізніше.", + "turn_off_failed_title": "Не вдалося вимкнути синхронізацію", + "turn_off_pending": "{count, plural, one {# зміна, внесена на цьому пристрої, ще не дійшла до сервера. Її буде втрачено, якщо зараз вимкнути синхронізацію.} few {# зміни, внесені на цьому пристрої, ще не дійшли до сервера. Їх буде втрачено, якщо зараз вимкнути синхронізацію.} many {# змін, внесених на цьому пристрої, ще не дійшли до сервера. Їх буде втрачено, якщо зараз вимкнути синхронізацію.} other {# зміни, внесеної на цьому пристрої, ще не дійшли до сервера. Їх буде втрачено, якщо зараз вимкнути синхронізацію.}}", + "turn_off_title": "Усе одно вимкнути?", + "turn_off_unknown": "Bulwark не вдалося перевірити, чи дійшли зміни, внесені на цьому пристрої, до сервера. Їх буде втрачено, якщо зараз вимкнути синхронізацію.", + "turning_off": "Надсилання змін, потім видалення даних із цього пристрою…", + "turning_on": "Налаштовування…" + }, "downloads": { "attachment_template": { "description_mobile": "Шаблон назви під час збереження вкладення або передавання його в інший застосунок." diff --git a/locales/rn/zh-TW.json b/locales/rn/zh-TW.json index c8d7e800..07f72b5d 100644 --- a/locales/rn/zh-TW.json +++ b/locales/rn/zh-TW.json @@ -93,6 +93,8 @@ "until_date": "直到 {date}" }, "settings": { + "device_sync_desc": "在此裝置上的日曆應用程式和其他應用程式中顯示您的行事曆。變更會雙向同步。", + "device_sync_title": "同步到此裝置", "show_week_numbers_mobile_desc": "在月檢視中顯示週數", "time_zone": "時區", "time_zone_auto": "裝置時區", @@ -249,6 +251,18 @@ "color_tag": "標籤", "remove_color": "移除標籤" }, + "device_sync": { + "auth_problem_text": "伺服器不再接受 {account} 的登入,因此此帳號在這部裝置上的聯絡人和行事曆已停止同步。請重新登入以繼續同步。在此裝置上所做的變更會保留。", + "auth_problem_title": "請重新登入以繼續同步", + "problem_channel": "同步問題", + "sign_out_anyway": "登出", + "sign_out_pending_message": "{count, plural, other {在此裝置上所做的 # 項變更尚未傳送到伺服器。如果現在登出,這些變更將會遺失。}}", + "sign_out_pending_title": "仍要登出嗎?", + "sign_out_unknown_message": "在此裝置上所做的變更可能尚未傳送到伺服器。如果現在登出,這些變更將會遺失。", + "signed_out_text": "{account} 已登出,因此此帳號在這部裝置上的聯絡人和行事曆不再同步。請重新登入以繼續同步。在此裝置上所做的變更會保留。", + "signed_out_title": "請登入以繼續同步", + "signing_out": "正在從此裝置移除已同步的聯絡人和行事曆…" + }, "email_composer": { "attach_camera": "相機", "attachment_reminder_body": "您的郵件提到「{keyword}」,但沒有附加檔案。仍要傳送嗎?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, other {# 位聯絡人}}", "default_book": "預設通訊錄", "description_mobile": "顯示偏好設定與通訊錄工具。", + "device_sync_desc": "在此裝置上的聯絡人應用程式和其他應用程式中顯示您的通訊錄。變更會雙向同步。", + "device_sync_title": "同步到此裝置", "export_description_count": "{count, plural, other {將 # 位聯絡人匯出為單一 vCard 檔案。}}", "export_empty": "沒有可匯出的聯絡人", "import_description_mobile": "從 vCard(.vcf)檔案匯入聯絡人,並偵測重複項目。", "manage_description_mobile": "建立、重新命名及移除用來整理聯絡人的通訊錄。" }, + "device_sync": { + "also_synced_by": "也為 {accounts} 同步,因此會在此裝置上顯示兩次。", + "android_settings": "Android 帳號設定", + "blocked_calendar": "Android 不會再要求行事曆存取權。請在應用程式設定中允許,然後重新開啟同步。", + "blocked_contacts": "Android 不會再要求聯絡人存取權。請在應用程式設定中允許,然後重新開啟同步。", + "blocked_title": "存取權已關閉", + "books_default": "您自己的通訊錄會同步;共用的通訊錄只有在您選擇後才會同步。", + "books_hint": "您自己的通訊錄會同步,除非您將其關閉;共用的通訊錄只有在您開啟後才會同步。", + "books_label": "通訊錄", + "calendars_default": "您自己的行事曆會同步;共用的行事曆只有在您選擇後才會同步。", + "calendars_hint": "您自己的行事曆會同步,除非您將其關閉;共用的行事曆只有在您開啟後才會同步。任務清單會保留在 Bulwark 中。", + "calendars_label": "行事曆", + "choose": "選擇", + "choose_books_title": "要同步的通訊錄", + "choose_calendars_title": "要同步的行事曆", + "collections_count": "同步 {selected} 個,共 {total} 個", + "continue": "繼續", + "deletions_delete": "在伺服器上刪除", + "deletions_message": "此裝置上一次刪除了許多項目,因此未在伺服器上刪除。請同時在伺服器上刪除,或在此裝置上還原這些項目。", + "deletions_restore": "還原", + "deletions_title": "也要在伺服器上刪除嗎?", + "denied_calendar": "沒有行事曆存取權,Bulwark 就無法將您的行事曆放到此裝置上。", + "denied_contacts": "沒有聯絡人存取權,Bulwark 就無法將您的通訊錄放到此裝置上。", + "denied_title": "同步維持關閉", + "failed_title": "無法開啟同步", + "grant_access": "授予存取權", + "interval_15m": "每 15 分鐘", + "interval_1h": "每小時", + "interval_30m": "每 30 分鐘", + "interval_6h": "每 6 小時", + "interval_desc": "Android 自動同步的頻率。在 Bulwark 中的編輯和透過推送通知的變更會更快同步。", + "interval_label": "同步間隔", + "interval_manual": "不進行定期同步", + "keep_syncing": "繼續同步", + "list_error_auth": "伺服器未接受登入。", + "list_error_network": "無法連線到伺服器。", + "list_error_server": "伺服器無法提供清單。", + "list_error_signed_out": "此帳號未在此裝置上登入。", + "master_off": "此裝置的自動同步已關閉,因此只有在您輕觸「立即同步」時才會同步。", + "new_contacts_default": "預設通訊錄", + "new_contacts_desc": "用來儲存在此裝置上其他應用程式中建立的聯絡人的通訊錄。", + "new_contacts_label": "新聯絡人儲存位置", + "no_books": "此帳號沒有通訊錄。", + "no_calendars": "此帳號沒有可同步的行事曆。", + "ok": "確定", + "open_app_settings": "開啟應用程式設定", + "own_collections": "您自己的", + "rationale_calendar": "如要在日曆應用程式中顯示您的行事曆,Bulwark 需要存取此裝置上的行事曆。它只會變更自己帳號的行事曆。", + "rationale_contacts": "如要在聯絡人應用程式中顯示您的通訊錄,Bulwark 需要存取此裝置上的聯絡人。它只會變更自己帳號的聯絡人。", + "rationale_title_calendar": "允許存取行事曆", + "rationale_title_contacts": "允許存取聯絡人", + "read_only": "唯讀", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "日曆應用程式", + "reminder_question_message": "已同步活動的提醒可以來自此裝置上的日曆應用程式或 Bulwark。請擇一使用,以免收到兩次提醒。您之後可以變更。", + "reminder_question_title": "要由誰提醒您?", + "reminders_desc": "由誰提醒您已同步的活動。另一方會保持靜音,您就不會收到兩次提醒。", + "reminders_label": "提醒", + "reminders_nobody": "Bulwark 的活動通知已關閉,因此已同步的活動不會有任何提醒。", + "remove": "移除", + "remove_from_device": "從此裝置移除", + "remove_message": "此帳號的聯絡人和行事曆將從此裝置移除。在此處所做但尚未傳送到伺服器的變更將會遺失。", + "remove_title": "要從此裝置移除嗎?", + "review_deletions": "檢查刪除項目", + "shared_by": "由 {name} 分享", + "sign_in_again": "重新登入", + "status_auth": "伺服器不再接受登入。請重新登入以繼續同步。", + "status_cancelled": "上次同步提前停止。下次會繼續。", + "status_failed": "上次同步失敗:{message}", + "status_failed_plain": "上次同步失敗。", + "status_io": "無法連線到伺服器({when})。Android 會重試。", + "status_item_errors": "{count, plural, other {有 # 個項目無法同步。}}", + "status_off": "關閉", + "status_paused": "已在 Android 設定中暫停", + "status_permission_calendar": "行事曆存取權已關閉,因此不會同步任何內容。", + "status_permission_contacts": "聯絡人存取權已關閉,因此不會同步任何內容。", + "status_removed": "已在 Android 設定中移除。開啟同步即可重新新增此帳號。", + "status_safety_abort": "已停止:此裝置上仍有項目,但伺服器未傳回任何內容。請檢查伺服器,然後重新同步。", + "status_synced": "上次同步:{when}", + "status_syncing": "正在同步…", + "status_too_many_deletions": "此裝置上刪除了許多項目。在您確認之前,這些項目不會在伺服器上刪除。", + "status_unsupported_calendar": "伺服器未為此帳號提供行事曆。", + "status_unsupported_contacts": "伺服器未為此帳號提供通訊錄。", + "status_waiting": "正在等待首次同步", + "status_waiting_next": "正在等待下次同步", + "suspended_desc": "已登出。此帳號的聯絡人和行事曆會連同在此處所做的變更一起保留在此裝置上,並在您登入後重新同步。", + "switch_label": "將 {account} 同步到此裝置", + "sync_now": "立即同步", + "turn_off_anyway": "關閉", + "turn_off_failed": "無法從此裝置移除已同步的資料。同步已暫停;請稍後再試。", + "turn_off_failed_title": "無法關閉同步", + "turn_off_pending": "{count, plural, other {在此裝置上所做的 # 項變更尚未傳送到伺服器。如果現在關閉同步,這些變更將會遺失。}}", + "turn_off_title": "仍要關閉嗎?", + "turn_off_unknown": "Bulwark 無法確認在此裝置上所做的變更是否已傳送到伺服器。如果現在關閉同步,這些變更將會遺失。", + "turning_off": "正在上傳變更,然後從此裝置移除資料…", + "turning_on": "正在設定…" + }, "downloads": { "attachment_template": { "description_mobile": "儲存或分享附件時使用的範本。" diff --git a/locales/rn/zh.json b/locales/rn/zh.json index 6bb8f899..2768f649 100644 --- a/locales/rn/zh.json +++ b/locales/rn/zh.json @@ -93,6 +93,8 @@ "until_date": "直到 {date}" }, "settings": { + "device_sync_desc": "在此设备上的日历应用和其他应用中显示您的日历。更改会双向同步。", + "device_sync_title": "同步到此设备", "show_week_numbers_mobile_desc": "在月视图中显示周数", "time_zone": "时区", "time_zone_auto": "设备时区", @@ -249,6 +251,18 @@ "color_tag": "标签", "remove_color": "删除标签" }, + "device_sync": { + "auth_problem_text": "服务器不再接受 {account} 的登录,因此该账户在此设备上的联系人和日历已停止同步。请重新登录以恢复同步。在此设备上所做的更改会保留。", + "auth_problem_title": "请重新登录以继续同步", + "problem_channel": "同步问题", + "sign_out_anyway": "退出登录", + "sign_out_pending_message": "{count, plural, other {在此设备上所做的 # 项更改尚未到达服务器。如果现在退出登录,这些更改将丢失。}}", + "sign_out_pending_title": "仍要退出登录?", + "sign_out_unknown_message": "在此设备上所做的更改可能尚未到达服务器。如果现在退出登录,这些更改将丢失。", + "signed_out_text": "{account} 已退出登录,因此该账户在此设备上的联系人和日历不再同步。请重新登录以恢复同步。在此设备上所做的更改会保留。", + "signed_out_title": "请登录以继续同步", + "signing_out": "正在从此设备移除已同步的联系人和日历…" + }, "email_composer": { "attach_camera": "相机", "attachment_reminder_body": "您的邮件中提到了“{keyword}”,但未附加任何文件。仍然发送吗?", @@ -598,11 +612,110 @@ "book_count": "{count, plural, other {# 位联系人}}", "default_book": "默认通讯录", "description_mobile": "显示偏好设置和通讯录工具。", + "device_sync_desc": "在此设备上的联系人应用和其他应用中显示您的通讯录。更改会双向同步。", + "device_sync_title": "同步到此设备", "export_description_count": "{count, plural, other {将 # 位联系人导出为单个 vCard 文件。}}", "export_empty": "没有可导出的联系人", "import_description_mobile": "从 vCard (.vcf) 文件导入联系人,并检测重复项。", "manage_description_mobile": "创建、重命名和删除用于整理联系人的通讯录。" }, + "device_sync": { + "also_synced_by": "也为 {accounts} 同步,因此会在此设备上显示两次。", + "android_settings": "Android 账户设置", + "blocked_calendar": "Android 不再请求日历访问权限。请在应用设置中允许,然后重新开启同步。", + "blocked_contacts": "Android 不再请求联系人访问权限。请在应用设置中允许,然后重新开启同步。", + "blocked_title": "访问权限已关闭", + "books_default": "您自己的通讯录会同步;共享的通讯录仅在您选择后同步。", + "books_hint": "您自己的通讯录会同步,除非您将其关闭;共享的通讯录仅在您开启后同步。", + "books_label": "通讯录", + "calendars_default": "您自己的日历会同步;共享的日历仅在您选择后同步。", + "calendars_hint": "您自己的日历会同步,除非您将其关闭;共享的日历仅在您开启后同步。任务列表保留在 Bulwark 中。", + "calendars_label": "日历", + "choose": "选择", + "choose_books_title": "要同步的通讯录", + "choose_calendars_title": "要同步的日历", + "collections_count": "同步 {selected} 个,共 {total} 个", + "continue": "继续", + "deletions_delete": "在服务器上删除", + "deletions_message": "此设备上一次删除了许多项目,因此未在服务器上删除。请同时在服务器上删除它们,或在此设备上恢复它们。", + "deletions_restore": "恢复", + "deletions_title": "也在服务器上删除?", + "denied_calendar": "没有日历访问权限,Bulwark 无法将您的日历放到此设备上。", + "denied_contacts": "没有联系人访问权限,Bulwark 无法将您的通讯录放到此设备上。", + "denied_title": "同步保持关闭", + "failed_title": "无法开启同步", + "grant_access": "授予访问权限", + "interval_15m": "每 15 分钟", + "interval_1h": "每小时", + "interval_30m": "每 30 分钟", + "interval_6h": "每 6 小时", + "interval_desc": "Android 自动同步的频率。在 Bulwark 中的编辑和通过推送通知的更改会更快同步。", + "interval_label": "同步间隔", + "interval_manual": "不进行定期同步", + "keep_syncing": "继续同步", + "list_error_auth": "服务器未接受登录。", + "list_error_network": "无法连接到服务器。", + "list_error_server": "服务器无法提供列表。", + "list_error_signed_out": "此账户未在此设备上登录。", + "master_off": "此设备的自动同步已关闭,因此只有在您点按“立即同步”时才会同步。", + "new_contacts_default": "默认通讯录", + "new_contacts_desc": "用于保存在此设备上其他应用中创建的联系人的通讯录。", + "new_contacts_label": "新联系人保存到", + "no_books": "此账户没有通讯录。", + "no_calendars": "此账户没有可同步的日历。", + "ok": "确定", + "open_app_settings": "打开应用设置", + "own_collections": "您自己的", + "rationale_calendar": "要在日历应用中显示您的日历,Bulwark 需要访问此设备上的日历。它只会更改自己账户的日历。", + "rationale_contacts": "要在联系人应用中显示您的通讯录,Bulwark 需要访问此设备上的联系人。它只会更改自己账户的联系人。", + "rationale_title_calendar": "允许访问日历", + "rationale_title_contacts": "允许访问联系人", + "read_only": "只读", + "reminder_owner_bulwark": "Bulwark", + "reminder_owner_device": "日历应用", + "reminder_question_message": "已同步活动的提醒可以来自此设备上的日历应用或 Bulwark。请选择其一,以免收到两次提醒。您之后可以更改。", + "reminder_question_title": "由谁来提醒您?", + "reminders_desc": "由谁提醒您已同步的活动。另一方保持静默,这样您就不会收到两次提醒。", + "reminders_label": "提醒", + "reminders_nobody": "Bulwark 的活动通知已关闭,因此已同步的活动不会有任何提醒。", + "remove": "移除", + "remove_from_device": "从此设备移除", + "remove_message": "该账户的联系人和日历将从此设备移除。在此处所做但尚未到达服务器的更改将丢失。", + "remove_title": "从此设备移除?", + "review_deletions": "查看删除项", + "shared_by": "由 {name} 共享", + "sign_in_again": "重新登录", + "status_auth": "服务器不再接受登录。请重新登录以继续同步。", + "status_cancelled": "上次同步提前停止。下次会继续。", + "status_failed": "上次同步失败:{message}", + "status_failed_plain": "上次同步失败。", + "status_io": "无法连接到服务器({when})。Android 会重试。", + "status_item_errors": "{count, plural, other {有 # 个项目无法同步。}}", + "status_off": "关闭", + "status_paused": "已在 Android 设置中暂停", + "status_permission_calendar": "日历访问权限已关闭,因此不会同步任何内容。", + "status_permission_contacts": "联系人访问权限已关闭,因此不会同步任何内容。", + "status_removed": "已在 Android 设置中移除。开启同步即可重新添加该账户。", + "status_safety_abort": "已停止:此设备上仍有项目,但服务器未返回任何内容。请检查服务器,然后重新同步。", + "status_synced": "上次同步:{when}", + "status_syncing": "正在同步…", + "status_too_many_deletions": "此设备上删除了许多项目。在您确认之前,它们不会在服务器上被删除。", + "status_unsupported_calendar": "服务器没有为此账户提供日历。", + "status_unsupported_contacts": "服务器没有为此账户提供通讯录。", + "status_waiting": "正在等待首次同步", + "status_waiting_next": "正在等待下次同步", + "suspended_desc": "已退出登录。该账户的联系人和日历会连同在此处所做的更改一起保留在此设备上,并在您登录后重新同步。", + "switch_label": "将 {account} 同步到此设备", + "sync_now": "立即同步", + "turn_off_anyway": "关闭", + "turn_off_failed": "无法从此设备移除已同步的数据。同步已暂停;请稍后重试。", + "turn_off_failed_title": "无法关闭同步", + "turn_off_pending": "{count, plural, other {在此设备上所做的 # 项更改尚未到达服务器。如果现在关闭同步,这些更改将丢失。}}", + "turn_off_title": "仍要关闭?", + "turn_off_unknown": "Bulwark 无法确认在此设备上所做的更改是否已到达服务器。如果现在关闭同步,这些更改将丢失。", + "turning_off": "正在上传更改,然后从此设备移除数据…", + "turning_on": "正在设置…" + }, "downloads": { "attachment_template": { "description_mobile": "保存或分享附件时使用的模板。" From 13d5d1733be9fb0aeeff564f0c0ce47d65af3747 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 08:50:59 +0200 Subject: [PATCH 076/334] fix: say "1 of 3 syncs" when one collection of several syncs #34 --- locales/rn/en.json | 2 +- src/components/settings/device-sync/DeviceSyncAccountRow.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/rn/en.json b/locales/rn/en.json index dff3fc6b..7a6011c5 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -644,7 +644,7 @@ "choose": "Choose", "choose_books_title": "Address books to sync", "choose_calendars_title": "Calendars to sync", - "collections_count": "{selected} of {total} sync", + "collections_count": "{selected, plural, one {# of {total} syncs} other {# of {total} sync}}", "continue": "Continue", "deletions_delete": "Delete on server", "deletions_message": "Many items were deleted on this device at once, so they were not deleted on the server. Delete them there as well, or bring them back on this device.", diff --git a/src/components/settings/device-sync/DeviceSyncAccountRow.tsx b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx index e83b9910..b7053ec9 100644 --- a/src/components/settings/device-sync/DeviceSyncAccountRow.tsx +++ b/src/components/settings/device-sync/DeviceSyncAccountRow.tsx @@ -288,7 +288,7 @@ export function DeviceSyncAccountRow({ account, authority }: Props) { : ''; const collectionsDescription = collections && selectedKeys - ? t('settings.device_sync.collections_count', '{selected} of {total} sync', { + ? t('settings.device_sync.collections_count', '{selected, plural, one {# of {total} syncs} other {# of {total} sync}}', { selected: selectedKeys.length, total: collections.length, }) From f24d919fdbaf1adc632702cfad04c010e0baadc7 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:15:53 +0200 Subject: [PATCH 077/334] docs: describe device sync after the review fixes and how it was verified #34 The device sync doc still described the feature as first built. The review fixes since then changed pairs, merges of items outside the synced collections, the order of uploads, group handling, photos, the provider-op scope checks, the settings actions and what the report says about changes put back on the phone. Each fix's doc notes are applied against the code as it is now, the known limitations gain what the device runs found (an identity as organizer, Stalwart refusing answers in read-only calendars, Fossify Calendar around DST changes, AOSP's one-line address edits), and a new Verification section lists what was checked on emulators and the scale run's numbers. --- docs/device-sync.md | 582 +++++++++++++++++++++++++++++++++----------- 1 file changed, 436 insertions(+), 146 deletions(-) diff --git a/docs/device-sync.md b/docs/device-sync.md index ef5ad0dc..b1ae634d 100644 --- a/docs/device-sync.md +++ b/docs/device-sync.md @@ -10,10 +10,11 @@ server. This document is for maintainers. It describes the design: the architecture, what every provider column holds, the mapping tables, the merge rules, the -native API, the failure matrix and the known limitations. Behaviour it relies -on was checked against Stalwart v0.16.23 (the server Bulwark targets) and -AOSP's ContactsProvider, CalendarProvider and the calendar/contacts apps; the -notable findings are quoted where they shape a rule. +native API, the failure matrix, the known limitations and what was checked on +devices. Behaviour it relies on was checked against Stalwart v0.16.23 (the +server Bulwark targets) and AOSP's ContactsProvider, CalendarProvider and the +calendar/contacts apps; the notable findings are quoted where they shape a +rule. ## Contents @@ -33,6 +34,7 @@ notable findings are quoted where they shape a rule. 14. [Testing](#testing) 15. [Decisions and limitations](#decisions-and-limitations) 16. [Out of scope](#out-of-scope) +17. [Verification](#verification) ## Architecture @@ -251,7 +253,7 @@ event of its own. |---|---| | `RawContacts.SOURCE_ID` | identity (above) | | `RawContacts.SYNC1` | the card's selected address books: `/` joined by `,` | -| `RawContacts.SYNC2` | the shadow: the last server card, wire format, with each `data:` photo URI replaced by `sha256:` of its bytes, plus a `~memberOf` member: the group cards whose memberships the rows held after our last write, so a removal on the device and an addition on the server can be told apart (stripped before any projection or patch) | +| `RawContacts.SYNC2` | the shadow: the last server card, wire format, with each `data:` photo URI replaced by `sha256:` of its bytes, plus a `~memberOf` member: the group cards whose memberships the rows held after our last write, so a removal on the device and an addition on the server can be told apart (stripped before any projection or patch), and `~noPhoto: true` when the card's photo has no row on the device because it could not be written | | `RawContacts.SYNC3` | the pending create: `{"uid":…,"target":"/"}`, written before the `/set` and kept until the identity is | | `RawContacts.SYNC4` | poison marker `{"fp":…,"type":…,"n":…,"until":…}` | | `RawContacts.RAW_CONTACT_IS_READ_ONLY` | 1 when none of the card's address books grants `mayWrite`. Only written, and only along with other raw contact writes, so an echo stays write-free: ContactsProvider refuses the column in every projection and selection ("Invalid column", API 32 and 35). Whether device edits are put back is decided from the server's current rights on the stored card | @@ -306,7 +308,9 @@ JSON, versioned. "collectionsState": "s12eq", "itemsState": "syufa", "selected": ["c/b"], + "partial": [], "stale": [], + "created": [], "reconcile": null, "groups": ["c/e12"], "taskOnly": [] @@ -331,21 +335,37 @@ JSON, versioned. refusal that re-planning did not fix) goes on `stale` in the same batch, is fetched again first in every run, and uploads nothing until it has been. The state still advances, so one bad object never blocks the pages after - it. + it. It also names the contacts whose membership a group card just written + changed, until their own chunk ran. +- `created` lists the objects our uploads created after `itemsState`, written + in the batch that stores their identity. The next run fetches them first, + with `stale`: `/changes` from the stored state omits an object that was + created and destroyed since (Stalwart does, as RFC 8620 allows), so a create + that another client destroyed before the next download would otherwise stay + on the device. - `reconcile` marks a full reconcile in progress: `{ "from": , "phase": "ids" | "objects", "position": n, "after": }`, - written with every chunk of objects. `after` is the last id processed in - the reconcile's sorted order: the next run lists the ids again and - continues after it (`phase` and `position` are informative only). `from: - null` asks for a reconcile that has not started (after - `cannotCalculateChanges`, or when local deletions were discarded). Its - deletions are applied only when the complete id list is known, and + first>, "phase": "ids" | "objects", "position": n, "after": , + "collections": [] }`, written with every chunk of objects. `after` is + the last id processed in the reconcile's sorted order: the next run lists + the ids again and continues after it (`phase` and `position` are + informative only). `collections` names the collections it lists, fixed when + it starts; a collection selected while it runs is loaded after it, like any + newly selected one. `from: null` asks for a reconcile that has not started + (after `cannotCalculateChanges`, or when local deletions were discarded). + Its deletions are applied only when the complete id list is known, and `itemsState` becomes `from` when it ends. -- `selected` is the selection the rows were written for; a collection is added - in the batch with its last loaded chunk, and removed with its last deleted - row. +- `selected` names the collections whose rows are all on the device: a + collection is added in the batch with its last loaded chunk, and removed in + a batch of its own before its first row is dropped. `partial` names + collections loaded or dropped only in part: added before the first chunk of + a load or the first row a contacts drop removes (a calendar is dropped with + its calendar row), removed with the last. Selected, such a collection is + loaded again; deselected, it is dropped again. - `groups` (contacts): the group cards present on the device, so a group an app - hard-deleted (Fossify does) is recognised by its absence. + hard-deleted (Fossify does) is recognised by its absence. It changes in the + batch that inserts or deletes the group's row (such a batch ends with a + state op, also on a retry), and a group a download finds unchanged is listed + too. - `taskOnly` lists calendars found to hold only tasks. - `deviceZone` (calendar) is the zone floating events were written in; `deviceZonePending` is set while a zone-change pass runs and cleared, with @@ -375,7 +395,8 @@ deadline reports `cancelled` with `moreRecordsToGet`. for our account ends here with `ok`, before JMAP is opened: ContactsProvider and CalendarProvider schedule an upload sync for every account 30 s after any app write, other accounts' included. A pending - reconcile or objects on `stale` make it a full run. + reconcile, objects on `stale` or objects in `created` make it a full + run. - `new JMAPClient().loadAccount(registryId)`: `false` or `AuthenticationError` → rebuild once (another client may have rotated the refresh token); still failing → `auth`, plus one notification that @@ -395,7 +416,8 @@ deadline reports `cancelled` with `moreRecordsToGet`. never skips changes of the other collections. Deselected collections are dropped in step 5. 3. **Download.** - - Objects on `stale` are fetched again first. + - Objects on `stale`, and the objects our last uploads created + (`created`), are fetched again first. - With a stored `itemsState`: `/changes` with `maxChanges: 256` and a `hasMoreChanges` loop; each page's changed ids are fetched with explicit `properties` in batches of `maxObjectsInGet`. @@ -412,15 +434,25 @@ deadline reports `cancelled` with `moreRecordsToGet`. - Contacts: a page's group cards are written before its contacts. When a group card's `members` changed, its old and new member contacts are fetched in the same page, before the page's state op: their membership - rows change although their own cards did not. + rows change although their own cards did not. They go on `stale` in the + batch that writes the group, so a stop before their chunk leaves them to + be fetched first by the next run. - An object without rows first looks for a local item it may already be: a new row whose pending create targets a collection the object is in and whose pending uid is the object's `uid` (our own create whose identity never got written) is adopted: it takes the identity and shadow and is - merged as a dirty item. + merged as a dirty item. A contact or group the user deleted meanwhile + takes only the identity and shadow, and its deletion then uploads by id. - Each object is [merged](#merge-rules) into its rows. An object that left - every selected collection loses its rows only when they are clean; dirty - units are uploaded first. + every selected collection loses its rows only when they are clean; a + dirty or deleted item is merged like any change first (a deleted one's + shadow follows the server, so its deletion is decided on the server's + version), so its upload builds on the server's version, and its rows go + once the upload is accepted. A full reconcile downloads such objects the + same way. The calendar planner places an event only in a synced + calendar, so an edited event that left all of them is not merged: it goes + on `stale` (reported as `plannerError`) and its changes wait on the + device; a deleted one's deletion is decided on its last shadow. - An object whose rows could not be written (a planner error, a provider refusal that re-planning did not fix) goes on `stale`, and the page's state still advances. @@ -430,11 +462,13 @@ deadline reports `cancelled` with `moreRecordsToGet`. [Uploads](#uploads). 5. **After the upload.** Deselected collections are dropped, and only their clean rows go: dirty items there were uploaded first (they are still valid - objects on the server), and an item whose upload failed keeps its rows, - and its collection stays in `selected`. Rows of objects that left every - selected collection go the same way. Calendar: then the device-zone pass - and the reminder-owner pass rewrite clean events (see [Timing](#timing) - and [Reminder owner](#reminder-owner)). + objects on the server), and an item whose upload failed keeps its rows; + its collection is no longer complete (not in `selected`, `partial` for + contacts), so the next run drops it again, or loads it again when it is + selected again. Rows of objects that left every selected collection go the + same way. Calendar: then the device-zone pass and the reminder-owner pass + rewrite clean events (see [Timing](#timing) and [Reminder + owner](#reminder-owner)). 6. **Report** counts, conflicts, per-item errors and duration; persist the status for the settings UI; call `finishRun` before the task's promise settles (JS timers stop once the task is over). @@ -477,13 +511,15 @@ Every reviewer checks against these. entry it dropped) is never read as a deletion. 2. **No silent device-edit loss.** A dirty row is cleared only after the server accepted the change, and only if it wasn't edited again during the upload: - the clearing batch asserts `RawContacts.VERSION` for contacts and the full - read projection (event row, attendees, reminders) for events, so check and - clear are one provider transaction. A row whose mapped projection equals its - baseline (only STARRED changed, say) is cleared without uploading. No dirty, - deleted or pending row is removed locally (deselection, move-away, disable, - logout, account reconciliation) before its upload succeeded or the user - confirmed losing it. + the clearing batch asserts `RawContacts.VERSION` for contacts and the read + projection for events (every event row, with the attendees and reminders + of the rows that are dirty: a clean row's DIRTY flag guards them, since + CalendarProvider sets it on every app write to an event, its attendees or + reminders), so check and clear are one provider transaction. A row whose + mapped projection equals its baseline (only STARRED changed, say) is + cleared without uploading. No dirty, deleted or pending row is removed + locally (deselection, move-away, disable, logout, account reconciliation) + before its upload succeeded or the user confirmed losing it. 3. **Crash-safe and idempotent.** Every step can be re-run. The JMAP state is written in the same `applyBatch` as the last chunk of rows it describes, as that batch's last op. Creates carry a client-minted `uid`, written to the @@ -562,7 +598,10 @@ not model (DEPARTMENT, labels, a website's type), `IS_PRIMARY`, and every nickname and organization but one; AOSP Contacts updates rows in place. So, per data kind: -1. Empty rows (an editor's blank nickname or note) are ignored. +1. Empty rows (an editor's blank nickname or note) are ignored. A Photo row + is never empty: ContactsProvider keeps a photo within its 96 px thumbnail + as the thumbnail alone, without `PHOTO_FILE_ID`, and the thumbnail does + not read back. 2. Rows whose `DATA_SYNC1` key is in the shadow keep that key. 3. Rows without a usable key are matched to the shadow's unmatched entries by the kind's primary value (email address case-insensitively, phone digits, @@ -579,7 +618,8 @@ per data kind: birthday). When every row of the kind is keyless, an editor rewrote them all, and a missing entry may just be one it could not show: it stays on the server. A missing name row is never a deletion; a missing photo row always - is (no editor re-inserts photo rows). + is (no editor re-inserts photo rows), unless the device never got the + photo (`~noPhoto`). A row matched by its key is compared column by column with its baseline; an emptied column is a real deletion. A row matched by value has no baseline: its @@ -645,31 +685,40 @@ patches only the paths the user changed and the server keeps its own. Whole-object rules: - Edit on the device + delete on the server → **the server delete wins**; the - rows are deleted and counted as a conflict. + rows are deleted and counted as a conflict. A move made on the device as a + delete plus an insert (a pair not uploaded yet) is such an edit: the + inserted row goes with the deleted one. - Delete on the device + edit on the server → **the device delete wins**; the object is destroyed. A deletion is an explicit act; restoring the object - would resurrect something the user removed. + would resurrect something the user removed. The download writes only the + new version into the item's shadow, so the deletion is judged by the + object's current collections: one the server meanwhile also filed in a + collection this device does not sync only leaves the synced ones. - Delete on both → purge, no conflict. - A server object whose `uid` differs from the shadow's is a different object: its rows are replaced, never patched. Every download write into an existing item is guarded: the group asserts the -DIRTY flag and VERSION (contacts) or projection (events) the plan was made from, -and ops that address one row by id expect exactly one row. Exception rows are +DIRTY flag and VERSION (contacts) or projection (events) the plan was made from +(for events, attendees and reminders only for the rows that are dirty), and +ops that address one row by id expect exactly one row. Exception rows are deleted by id only. A failed assert re-reads and re-plans the item (up to 3 -times); after that the item goes on `stale`. +times); after that the item goes on `stale`. A batch that failed as a whole +may have been committed up to a provider yield point: its downloads are then +planned again from a new read rather than sent again, so a committed one +becomes an echo. ## Uploads -The upload runs phase by phase across the JMAP accounts: new rows are claimed -(below), each account resolves its series splits and pairs, then every -account's creates go up, then every account's updates, then every account's -deletions, then (contacts) groups deleted by absence and group memberships. So -a move between accounts never destroys before it created. Changes go in `/set` -calls of at most `maxObjectsInSet` objects (creates: 50) and about 1 MB of -JSON (Stalwart splits bigger calls into several commits, which can apply -partially), one request in flight, and one `sendSchedulingMessages` value per -call (it is a request-level flag). +The upload runs phase by phase across the JMAP accounts: each account uploads +its pairs (below), new rows are claimed, each account resolves its series +splits, then every account's creates go up, then every account's updates, +then every account's deletions, then (contacts) groups deleted by absence and +group memberships. So a move between accounts never destroys before it +created. Changes go in `/set` calls of at most `maxObjectsInSet` objects +(creates: 50) and about 1 MB of JSON (Stalwart splits bigger calls into +several commits, which can apply partially), one request in flight, and one +`sendSchedulingMessages` value per call (it is a request-level flag). - **ifInState.** Every `/set` carries `ifInState` = the item state the merge was based on (then the `newState` of our previous `/set`). A `stateMismatch` @@ -687,21 +736,40 @@ call (it is a request-level flag). `accountNotSupportedByMethod`) holds back only that account: its items wait, the report lists the account, and the other accounts go on. - **Splits.** A `CONTENT_EXCEPTION_URI` split (two masters sharing a - `_SYNC_ID`, see [Recurrence](#recurrence)) is resolved first: the older - master's capped rule is planned while the split is still visible, the newer - row is claimed as a new event, then the rule is sent. The new event is - created with the other creates. + `_SYNC_ID`, see [Recurrence](#recurrence)) is resolved before the creates: + the older master's capped rule is planned while the split is still visible, + the newer row is claimed as a new event, then the rule is sent. The new + event is created with the other creates. - **Pairs.** Some apps turn an edit into a delete plus an insert: Etar moves an event to another calendar that way, and turns a recurring series into a single event (or edits "this and following" from its first instance) the same way. A deleted row and a new row of the same run whose projections match apart from the calendar (or the rule) are uploaded as a patch of the existing object (`calendarIds` swapped; `recurrenceRule: null` with its - overrides removed); the new row takes the identity and exceptions of the old - one, which is purged. Rows of different JMAP accounts never pair: that move - is a create in the target account, identity written, and later in the run a - destroy in the source, whose echo finds no rows left to change. Unpaired - rows are ordinary creates and deletions. + overrides removed); the new row takes the identity and the exception rows + of the old one, which is purged. Rows of different JMAP accounts never + pair: that move is a create in the target account, identity written, and + later in the run a destroy in the source, whose echo finds no rows left to + change. Unpaired rows are ordinary creates and deletions. Pairs go up + before any new row is claimed: the new row takes the old row's identity + instead of a uid of its own, and a paired deletion is a move, which the + deletion threshold does not count. When an app changes the rows between the + patch and the identity write, they are read and paired again; rows that no + longer pair do not get the identity: the object is fetched again first next + run, and the rows upload as a create and a deletion. The new row's baseline + is the old row's, with what the patch changed (the calendar; for a series + turned single the rule and its timing as a single event), so anything else + saved with the move or made before it, a reminder say, keeps the row dirty + and uploads right after the pair as an ordinary update; the pair's accepted + write then waits for that upload. Such a change keeps the device's value + even where the server changed the same field meanwhile: the download of the + deleted row refreshes only its shadow, so no merge sees the conflict. + Attendee rows of the new row are the app's re-insert (Etar writes every + attendee as required, without a status): only their addresses count, for + the pairing. A new row without reminder rows is taken to have copied none, + not to have deleted them. The old row's exception rows (Etar copies only + those it never synced) move to the new row with their pending edits; a + series turned single has none left. - **New rows** (see [Identity](#identity) for what counts as new): 1. Claim: write the pending create (`SYNC3` / `SYNC_DATA3`: the uid and the target collection; for events also `UID_2445` and @@ -713,10 +781,15 @@ call (it is a request-level flag). A server object with that uid does not count, because it may be this row's own earlier create. The target is fixed at the claim: contacts go to the address book chosen in the settings if it is selected and - writable, else to the personal account's default book (or its first by + writable, else to the primary account's default book (or its first by sort order) among the selected writable ones; with none, the item is skipped and reported as `noWritableAddressBook` in every run. Events go - to the calendar of their row. + to the calendar of their row. A claim whose collection is gone from the + server (its account was listed and no longer has it) is made again for + contacts and groups, since nothing of an earlier create can exist there; + an event's create into a calendar that is gone is not sent and is + reported as `collectionGone` until the user moves the event to another + calendar. 2. Look the uid up: contacts in the target address book (card uids are unique per book), events in the whole account (event uids are unique per account). A hit in the create's target collection is adopted: the row @@ -756,19 +829,25 @@ call (it is a request-level flag). uploaded, and DIRTY stays: the newer edit uploads next time, and so does a revert to the old value. For contacts these baseline writes address their data rows with `where _id = ?`, so a row deleted meanwhile does not fail the - group. + group. A group card accepted for a membership patch keeps a rename the + device made that the card does not show: only its shadow is written, and + the rename uploads next. - **SetErrors**: - - `notFound` on update → treated as a remote delete (the rows go); on a - destroy it counts as done; on a create the item is poisoned; + - `notFound` on update → treated as a remote delete (the rows go, and for + a pair the new row too); on a destroy it counts as done; on a create the + item is poisoned; - `forbidden` in a call that asked for scheduling messages → the item is - retried once without them and the report notes "invitations not sent" - (Stalwart refuses scheduling when it is disabled, when the account has no - calendar address, or without the scheduling permission). `forbidden` - otherwise: on a create the item is poisoned (a revert would delete the - user's new item); on an update or a deletion the object is read-only on - the server after all, and its rows are rewritten from the server's - current version, fetched for it (the shadow holds photos only as hashes), - or removed when the object is gone or outside the selection; + retried once without them, and the report notes "invitations not sent" + once that retry is accepted (Stalwart refuses scheduling when it is + disabled, when the account has no calendar address, or without the + scheduling permission). `forbidden` otherwise: on a create the item is + poisoned (a revert would delete the user's new item); on an update or a + deletion the object is read-only on the server after all, and the report + lists the item as `forbidden`: its rows are rewritten in place from the + server's current version, fetched for it, so device-only columns (a star, + a ringtone, joins, row ids) stay; a refused deletion brings the item back + as a new copy, and the rows are removed when the object is gone or + outside the selection; - `stateMismatch` → as above; - `invalidProperties` on `uid` → as above; - `invalidProperties`, `invalidPatch`, `tooLarge`, `overQuota` and the rest → @@ -788,14 +867,17 @@ call (it is a request-level flag). More than 50 local deletions of an authority in one run, and more than 20 % of that authority's synced objects, set `tooManyDeletions` unless the run carries `overrideTooManyDeletions`. Deletions inferred from absence (groups) count. -No deletion of that run is uploaded; everything else proceeds. With -`discardLocalDeletions` (the user chose "undo" in Android's too-many-deletions -notification), the affected JMAP accounts first get a `reconcile` marker in a -batch of its own, then the deleted rows are purged, so the next runs download -the objects again whatever their extras; a crash in between only asks the -question again. A teardown applies the same threshold: deletions above it stay -on the device and count as waiting changes, so the app asks before turning -sync off (see [Lifecycle](#lifecycle)). +No deletion of that run is uploaded; everything else proceeds. The user +resolves it in Android's too-many-deletions notification, or with "Review +deletions" in the app's sync settings, which asks whether to delete the items +on the server too ("Delete on server", a sync with `overrideTooManyDeletions`) +or to bring them back ("Bring them back", a sync with `discardLocalDeletions`). +With `discardLocalDeletions`, the affected JMAP accounts first get a +`reconcile` marker in a batch of its own, then the deleted rows are purged, so +the next runs download the objects again whatever their extras; a crash in +between only asks the question again. A teardown applies the same threshold: +deletions above it stay on the device and count as waiting changes, so the +app asks before turning sync off (see [Lifecycle](#lifecycle)). ## Contacts mapping @@ -833,11 +915,11 @@ as "Ada King" on the device sends both. | JSContact | Android kind | Columns | |---|---|---| | `nicknames/` | Nickname | `NAME`, `TYPE=DEFAULT` | -| `emails/` | Email | `ADDRESS`; `TYPE` from contexts: `private`→HOME, `work`→WORK, none→OTHER, `label`→CUSTOM+`LABEL`; `pref=1`→`IS_PRIMARY` | +| `emails/` | Email | `ADDRESS`; `TYPE` from contexts: `private`→HOME, `work`→WORK, none→OTHER, `label`→CUSTOM+`LABEL` (`mobile`→MOBILE, which uploads that label); `pref=1`→`IS_PRIMARY` | | `phones/` | Phone | `NUMBER`; `TYPE` from features + contexts (table below); `label`→CUSTOM+`LABEL`; `pref=1`→`IS_PRIMARY` | | `addresses/` | StructuredPostal | components → `STREET`, `POBOX`, `NEIGHBORHOOD`, `CITY`, `REGION`, `POSTCODE`, `COUNTRY` (table below), `full`→`FORMATTED_ADDRESS` (a card without `full` gets a one-line address joined from the components, so the provider leaves the row as written); contexts → TYPE HOME/WORK/OTHER, `label`→CUSTOM | | `organizations/` + `titles` pointing at it | Organization | `name`→`COMPANY`, `units` joined with `, `→`DEPARTMENT`, title with `kind: title`→`TITLE`, title with `kind: role`→`JOB_DESCRIPTION`; key `organizations:|titles:,`; titles without an organization get their own row | -| `links/` | Website | `URL` ← `uri`; contexts `private`→HOME, `work`→WORK, else OTHER; `label`→CUSTOM | +| `links/` | Website | `URL` ← `uri`; contexts `private`→HOME, `work`→WORK, else OTHER; `label` `homepage`/`blog`/`profile`/`ftp`→HOMEPAGE/BLOG/PROFILE/FTP (they upload that label), any other `label`→CUSTOM | | `anniversaries/` | Event | `START_DATE`: `YYYY-MM-DD`, `--MM-DD` without year (`partialDateToString`); kind `birth`→BIRTHDAY, `wedding`→ANNIVERSARY, `other`→OTHER, anything else → CUSTOM + its kind as `LABEL`; a custom type uploads its label, lowercased, as the kind | | `relatedTo/` | Relation | `NAME` ← the key (a `urn:uuid:` of a synced card shows that card's name), `TYPE` from the relation set (table below) | | first entry of `notes` | Note | `NOTE` ← `note` | @@ -858,11 +940,17 @@ Phone types (first match wins; reverse: the table read right to left, and | pager + work | WORK_PAGER | | pager | PAGER | | textphone | TTY_TDD | +| main-number + work | COMPANY_MAIN | | main-number | MAIN | | work (voice) | WORK | | private (voice) | HOME | | anything else | OTHER | +CALLBACK, CAR, ISDN, RADIO, TELEX and ASSISTANT upload as `voice` with the +label `callback`, `car`, `isdn`, `radio`, `telex` or `assistant`, and MMS as +`text` with the label `mms`; such a label next to that feature reads back as +the type (a custom label uploads without the feature and stays CUSTOM). + Postal components (reverse: an edited field replaces all components it was built from with one component of the first kind, keeps the rest): @@ -878,7 +966,16 @@ built from with one component of the first kind, keeps the rest): Legacy flat address fields (`street`, `locality`, …) are read when there are no components and never written. When an editor wrote only the formatted address -and the provider copied it into `STREET`, only `full` uploads. +(AOSP Contacts edits it as one line), the provider copies it into `STREET` and +empties the other parts. An unchanged line uploads nothing. A changed line +uploads as `full` with the parts it implies, read back along the old line: +each line or comma-separated segment of the old one must be some of the old +parts in a row, and a segment whose change is confined to one part (the parts +around it keep their words) rewrites that part as an edit of its field would. +When the layout changed or a change spans two parts, the address keeps the +whole line as its street (`name`) component. Either way `full` and the +components agree; an address without components would show empty in the +app's editor and in CardDAV clients. Relations: `friend`↔FRIEND, `spouse`↔SPOUSE, `child`↔CHILD, `parent`↔PARENT, `sibling`→RELATIVE, `kin`→RELATIVE, `co-worker`/`colleague`→CUSTOM with the @@ -912,9 +1009,15 @@ with the detached client. Upload: the display photo is read back, scaled to at most 512 px, JPEG, and sent as a `data:image/jpeg;base64,…` URI in `media//uri`, matching the app's picker. `DATA_SYNC2` holds the hash of the server photo last applied, the baseline holds `PHOTO_FILE_ID`; a new file -id means the user changed the photo. Photos travel in batches of their own -(Binder budget). A photo whose bytes could not be fetched keeps its row and -counts as unchanged. +id means the user changed the photo. A photo within the 96 px thumbnail has no +file id, so replacing it on the device with another photo that small is not +seen. Photos travel in batches of their own (Binder budget). A photo larger +than 700,000 base64 characters (about 512 KiB) is not written: a contact's +rows travel in one provider batch, one Binder transaction of at most 1 MB. +Such a photo, and one whose bytes could not be fetched, keeps an existing row +and counts as unchanged; without a row the shadow's `~noPhoto` records it, so +the missing row is not read as a deletion, and the photo stays on the server +until the device sets one of its own. Groups: group cards are written before the contacts of the same download, and membership rows name their group by row id (`GROUP_ROW_ID`, looked up by the @@ -924,15 +1027,30 @@ membership whose group has no row is not written. A contact's memberships are merged from three sets: what the rows held after our last write (`~memberOf` in the shadow), the rows now, and the group cards that list the card's uid now; both sides can only flip a membership the same way, so memberships never -conflict. Membership edits upload after the deletions, as patches of the -group cards' `members` (`members/: true` or `null`; the whole map for a -group without members). A contact stays dirty until the server's group cards -show its membership edits (the clean and accepted writes keep DIRTY); the -next run's clean write clears it. +conflict. Membership edits, also those of a contact created on the device +(after its create), upload after the deletions as patches of the group cards' +`members` (`members/: true` or `null`; the whole map for a group without +members). Edits for a group on `stale` wait until it has been fetched again. A +contact stays dirty until the server's group cards show its membership edits +(the clean and accepted writes keep DIRTY); once the run's membership patches +are accepted, a contact with nothing else to upload is cleaned in the same +run. A membership edit the server can't take, of a group in read-only address +books or of another JMAP account, does not wait: the contact's next clean or +accepted write puts it back to what the server has, like an edit of a +read-only contact, and when that was the contact's only change the upload +counts it as skipped and the report lists the contact as `groupNotWritable`. A group deleted on the device is found by `DELETED`, or by its absence from the rows while `SyncState.groups` still lists it (Fossify hard-deletes groups -through a sync-adapter URI); both count toward the deletion threshold. +through a sync-adapter URI); both count toward the deletion threshold. An +absent group whose card is no longer a group, or is in no address book that +syncs, is only dropped from the list: it is not one the device showed. + +A contact or group whose address books are all read-only takes no device +changes: an edit (a group's rename) is put back from the shadow, a deletion +is purged and fetched again, and the report lists either as `readOnly`. +Joining a writable group is no edit of the contact: it uploads through that +group's card. ## Calendar mapping @@ -941,7 +1059,7 @@ through a sync-adapter URI); both count toward the deletion threshold. | JMAP `Calendar` | Android `Calendars` | |---|---| | `id` | `_SYNC_ID` = `/` | -| `name` | `NAME`, `CALENDAR_DISPLAY_NAME`; a shared account's calendars show ` ()` (the JMAP account's name, unless it is one of the user's addresses), the personal account's get no suffix | +| `name` | `NAME`, `CALENDAR_DISPLAY_NAME`; a shared account's calendars show ` ()` (the JMAP account's name, unless it is one of the user's addresses), the primary account's get no suffix | | `color` | `CALENDAR_COLOR` (CSS color → ARGB; absent → the app's default palette colour) | | `myRights` | `CALENDAR_ACCESS_LEVEL`: `mayWriteAll` → OWNER (700); `mayWriteOwn` → CONTRIBUTOR (500); `mayRSVP` → RESPOND (300); else READ (200); subscribed iCal feed → READ | | the user's calendar address | `OWNER_ACCOUNT` (never null: Etar crashes on it) | @@ -959,10 +1077,18 @@ first uploads the calendar's dirty events. A read-only calendar (without `mayWriteAll` and `mayWriteOwn`, or a subscribed feed) takes no device changes: a dirty event is rewritten from its shadow, a deleted one is purged and fetched again, a new one is removed. The one -exception is the user's own RSVP on the master -(`participants//participationStatus`), which uploads when the calendar -grants `mayRSVP`. Edits of an event Stalwart holds only as an instance (no -series) are reverted too, in any calendar. +exception is the user's own answer, to the series +(`participants//participationStatus`) or to occurrences (their +overrides' participants, changed in nothing but the user's status), which +uploads when the calendar grants `mayRSVP`. Stalwart 0.16.23 refuses it +anyway (`forbidden`: its `/set` requires write rights in a shared calendar); +the rows are then rewritten from the server and the item is reported +(`forbidden`). Edits of an event Stalwart holds only as an instance (no +series) are reverted too, in any calendar. Every change put back this way is +listed in the run's report: `readOnly` (an edit, a deletion or a new event in +a read-only calendar, or a move into one), `rsvpRefused` (an answer where the +calendar takes none), `instanceOnly`, and `crossAccountMove` for an event an +app moved in place to another account's calendar. ### Timing @@ -1033,12 +1159,12 @@ Device-side changes map back as follows: | exception row inserted (Etar "this event": `ORIGINAL_SYNC_ID` + `ORIGINAL_INSTANCE_TIME`, `DIRTY=1`) or edited | `recurrenceOverrides/`; the key is `ORIGINAL_INSTANCE_TIME` in the master's zone (a date at midnight for all-day). A new override carries the master's title, locations, participants and sequence (Stalwart stores a bare override as a separate event otherwise); an existing one is patched one level deep (`recurrenceOverrides//`), never deeper: Stalwart turns a deeper pointer into a partial override (other attendees vanish for that instance) | | exception row with `STATUS_CANCELED` (Etar and Fossify "delete this event"), a deleted exception row, or an EXDATE added to the master | `recurrenceOverrides/: {excluded: true}` | | EXDATE removed | `recurrenceOverrides/: null` | -| master `RRULE` capped with `UNTIL`/`COUNT` + a new event without identity (Etar "this and following") | the master uploads only `recurrenceRule` (whole object) with the new `until`/`count` and removes the overrides after the new end (`recurrenceOverrides/: null`; for a `COUNT` the end is the start of the last instance, CalendarProvider's `LAST_DATE` − `DURATION`); the new series is a row of its own, claimed and created as a new event with a fresh uid | +| master `RRULE` capped with `UNTIL`/`COUNT` + a new event without identity (Etar "this and following") | the master uploads only `recurrenceRule` (whole object) with the new `until`/`count` and removes the overrides after the new end (`recurrenceOverrides/: null`; for a `COUNT` the end is the start of the last instance, CalendarProvider's `LAST_DATE` − `DURATION`); the new series is a row of its own, claimed and created as a new event with a fresh uid; exclusions Etar copied from the old series' EXDATE that fall before the new series' start are left out | | master start moved (Etar "all events") | `start`/`duration`/`timeZone`, and every override key moved by the same local offset (overrides whose `start` equalled their old key move too), sent as the whole `recurrenceOverrides` map | -| two masters with the same `_SYNC_ID` (a `CONTENT_EXCEPTION_URI` split clones `_SYNC_ID`, `SYNC_DATA*` and `UID_2445`, and caps the old master's rule without `DIRTY`) | both masters are always loaded together. The download merges into the older master as a dirty item, leaves the newer one alone and heals no baseline of either. The upload resolves the split in its first step: the older master's current RRULE is uploaded, and the newer row is claimed as a new event with a fresh uid; that claim points the source's exception rows' `ORIGINAL_SYNC_ID` back at the source and sets the source `DIRTY=1`, so its capped rule uploads | +| two masters with the same `_SYNC_ID` (a `CONTENT_EXCEPTION_URI` split clones `_SYNC_ID`, `SYNC_DATA*` and `UID_2445`, and caps the old master's rule without `DIRTY`) | both masters are always loaded together. The download merges into the older master as a dirty item, leaves the newer one alone and heals no baseline of either. The upload resolves the split before the creates: the older master's current RRULE is uploaded, and the newer row is claimed as a new event with a fresh uid; that claim points the source's exception rows' `ORIGINAL_SYNC_ID` back at the source and sets the source `DIRTY=1`, so its capped rule uploads | | a new master carrying another master's `UID_2445` (an app's "duplicate" copies it) | a clone without a source: claimed with a fresh uid and created as a new event, never adopting the other master's object (the engine loads every master that shares a new master's `UID_2445`) | | event moved to another calendar, or a series turned into a single event (Etar deletes and re-inserts both) | paired with the deleted row and uploaded as a patch of the same object (see [Uploads](#uploads)); never across JMAP accounts, where the move is a create in the target account and a destroy in the source | -| `CALENDAR_ID` changed in place to a calendar of another JMAP account | not uploaded; the next download of the object puts the row back | +| `CALENDAR_ID` changed in place to a calendar of another JMAP account | not uploaded: the row is put back into its calendar, rewritten from the shadow and reported as `crossAccountMove` (changes made with it upload, and their accepted write puts it back) | | an RSVP for one instance (Etar goes through `CONTENT_EXCEPTION_URI`) | the override's whole `participants` map with the user's new status. An exception row's status uploads only when the user organizes the event: Etar also puts `STATUS_CONFIRMED` on the instance, and Stalwart refuses an attendee's change to an event's status | On an exception row an app created, an empty title, description, location or @@ -1046,6 +1172,13 @@ colour, or no attendee or reminder rows, means "inherit from the series", never a deletion. An override never carries `privacy`: Stalwart drops it from overrides. +On an exception row device sync wrote, clearing the description or the +location clears it for that occurrence. An override without them shows the +series' ones, so while the series has a description or location the override +gets the empty text, or one location without a name (Stalwart writes no +LOCATION line for an empty map, which would read back as the series' +locations); otherwise the override's own value is removed. + The first override of an event with none sends the whole `recurrenceOverrides` map (a patch below a missing property fails). @@ -1056,7 +1189,7 @@ map (a patch below a missing property fails). | `title` | `TITLE` (exception rows always carry the master's title: Fossify Calendar treats an exception with an empty title as deleted) | | `description` | `DESCRIPTION`; `descriptionContentType: text/html` is written as plain text and uploaded only when edited, then as `text/plain` | | first entry of `locations` (server order) | `EVENT_LOCATION` ← `name`; edit patches `locations//name`, a new one adds `locations/`, clearing deletes the entry | -| `status` confirmed / tentative / cancelled | `STATUS` 1 / 0 / 2; absent → 1. Never NULL (the provider throws) | +| `status` confirmed / tentative / cancelled | `STATUS` 1 / 0 / 2; absent → 1. Never written NULL (the provider throws). A NULL STATUS (CalendarProvider has no default; Google Calendar leaves it NULL on the events and exceptions it inserts) sets no status: a create leaves `status` out, an override carries none, and it is never a change or a reason not to pair | | `freeBusyStatus` busy / free | `AVAILABILITY` 0 / 1; device TENTATIVE (2) uploads as `busy` | | `privacy` absent / public / private / secret | `ACCESS_LEVEL` DEFAULT / PUBLIC / PRIVATE / CONFIDENTIAL | | `color` | `EVENT_COLOR` (CSS name or `#rrggbb` → ARGB; upload `#rrggbb`); no Colors table in v1 | @@ -1065,7 +1198,7 @@ map (a patch below a missing property fails). | — | `HAS_ATTENDEE_DATA=1`; `SELF_ATTENDEE_STATUS` is never written (the provider derives it and throws on update) | | alerts with an offset before start (`relativeTo` start or absent, offset ≤ 0) | Reminders: `MINUTES` = −offset, `METHOD` = EMAIL for `action: email`, else ALERT | | alerts relative to the end, absolute alerts, offsets after the start | preserved, not shown | -| `useDefaultAlerts: true` | the calendar's `defaultAlertsWithTime`/`WithoutTime` become the reminders (as the app's own scheduler does); a device edit uploads `useDefaultAlerts: false` plus explicit `alerts` | +| `useDefaultAlerts: true` | the calendar's `defaultAlertsWithTime`/`WithoutTime` become the reminders (as the app's own scheduler does); a device edit uploads `useDefaultAlerts: false` plus explicit `alerts`; an edit of one occurrence's reminders does so in that occurrence's override only (Stalwart keeps an override's `useDefaultAlerts` as a JSPROP), so the series and its other occurrences stay on the defaults | | `uid` | `UID_2445` | | `keywords`, `categories`, `priority`, `virtualLocations`, `links`, `relatedTo`, `sequence`, `locale`, `replyTo`, locations after the first, participants without a `mailto:` address | preserved only | @@ -1170,7 +1303,7 @@ nested maps. Every provider call is scoped to one account of our type. | `setSyncEnabled(name, authority, on)` | syncable = 1 and `setSyncAutomatically` | | `setPeriodicSync(name, authority, seconds)` | replaces our periodic syncs (including the framework's default daily one); 0 removes them; the framework clamps to 15 min | | `requestSync(name, authority, optionsJson)` | `{manual, expedited, upload, overrideTooManyDeletions, discardLocalDeletions}` as SYNC_EXTRAS_*; constant extras so repeated requests coalesce; flags an active run for `fullSyncRequested` | -| `openAccountSettings(name)` | the account's sync screen (`android.settings.ACCOUNT_SYNC_SETTINGS` with the Account), else `SYNC_SETTINGS` filtered to our type | +| `openAccountSettings(name)` | the account's sync screen (`android.settings.ACCOUNT_SYNC_SETTINGS` with the Account as the `account` extra and in `:settings:show_fragment_args`: the Settings app of Android 12L and later passes only those arguments on to the screen, which closes at once without an account), else `SYNC_SETTINGS` filtered to our type | | `finishRun(runId, reportJson)` / `isRunCancelled(runId)` | the run handshake | | `setPushRoutes(json)` | `{ jmapAccountId: [{ accountName, authorities }] }` for the push router, in SharedPreferences | | `showSyncProblem(name, title, text, uri, channelName)` / `clearSyncProblem(name)` | the account's one sync-problem notification; a tap opens `uri` (`bulwarkmobile://` only) in the app; `channelName` names the channel in the app's language | @@ -1205,6 +1338,12 @@ Scoping, enforced in Kotlin for every op - attendees, reminders and extended properties get `event_id IN` the account's events among those the op touches (resolved first; the providers may compile selections strictly, so no subqueries). +- Selections and sort orders are checked before the account condition is + ANDed on as `(where) AND (scope)`: their parentheses must balance outside + quoted literals (`1) OR (1` would close the scope's own), and they may not + end the statement, carry a comment or use `SELECT`, `UNION`, `ATTACH`, + `DETACH` or `PRAGMA` (`scope` otherwise). The engine sends only constant + selections with `?` arguments; the check guards against bugs. - Ops by `id` verify the row belongs to the account first (`scope` otherwise) and expect exactly one row unless they set `expectCount`, so a row that vanished fails with `assert`. `id` and `where` together address @@ -1217,8 +1356,11 @@ Scoping, enforced in Kotlin for every op parent (a back-reference to an insert of the same batch, or an id); an insert or update naming a parent, exception master (`original_id`) or group (`data1` of a membership row) checks it: gone fails with `assert` (re-read - and re-plan), another account's with `scope`. A back-reference must point - to an earlier insert of the right table; settings inserts return no id. + and re-plan), another account's with `scope`. An update of a data row by id + that writes `data1` without restating the mimetype has the row's mimetype + looked up, so a membership's group is checked there too; such a write by + `where` is refused (`scope`). A back-reference must point to an earlier + insert of the right table; settings inserts return no id. - Inserts into the tables with account columns (raw contacts, groups, settings, calendars, colors) get `account_name` and `account_type` added, so calendar inserts carry them too. Otherwise @@ -1227,12 +1369,18 @@ Scoping, enforced in Kotlin for every op - Tables are an enumerated set per authority; anything else is refused (`scope`). - Blobs (`{ b64 }`) are only accepted for `data.data15` (photos). + `data.data14`, a photo row's file id, is never written (`scope`): the + provider sets it, and another account's file id would let `readPhoto` read + that account's photo. - A `syncState` op must be the last op of its batch and not `yieldAllowed`, so a provider yield never commits the state ahead of the rows (refused as `scope` otherwise). -- Batches: at most 499 ops up to and including each yield point +- Batches: for contacts, at most 499 ops up to and including each yield point (ContactsProvider counts an op before resetting at a yield point and throws - at 500), about 300 KB per call; `TransactionTooLargeException` resolves as + at 500), so the SyncState op goes in a batch of its own after a group of + 499 ops or more; about 500 KB per call as `estimateBatchBytes` weighs it (2 + bytes per character of the ops' JSON plus 300 bytes per op: the Parcel + carries UTF-16 strings); `TransactionTooLargeException` resolves as `tooLarge` so JS splits the batch. `OperationApplicationException` resolves as `assert` (except the provider's own "too many operations" and failed inserts, `provider`), `SecurityException` as `permission`. The whole batch @@ -1255,10 +1403,11 @@ What the providers do with values, which the planners account for: `device-sync:v1` with `createPersistStorage({ writeDelayMs: 0 })` (not the 1-second delayed storage) and not part of the settings export. Per account: `contactsSelection` / `calendarSelection` (explicit choices only; unlisted -collections follow the default: the personal account's on, shared accounts' -off, and the webmail's "Trusted Senders" address book off too: it is the -allow-list of senders whose images load, not an address book people expect -in their phone's contacts), `newContactsAddressBook`, `reminderOwner`, +collections follow the default: those of the capability's primary account +on, every other account's off (also one the server marks as personal), and +the webmail's "Trusted Senders" address book off too: it is the allow-list of +senders whose images load, not an address book people expect in their +phone's contacts), `newContactsAddressBook`, `reminderOwner`, `intervalSeconds` per authority, the last `RunStatus` per authority, `androidAccountName` (the Android account it uses), `knownStates` (the latest states the engine synced, per JMAP account and type, for the echo skip), @@ -1277,16 +1426,19 @@ native module present). Settings → Contacts and Settings → Calendar each get - an on/off switch (reflects `getSyncAutomatically`; "Paused in Android settings" when the account exists but the authority is off there; a hint when the device's master auto-sync is off); -- a chooser (sheet) for address books or calendars, with personal ones on and - shared ones (and Trusted Senders) off by default, read-only ones marked, and - a warning when another signed-in account already syncs the same shared - collection; +- a chooser (sheet) for address books or calendars, with the primary + account's on and shared ones (and Trusted Senders) off by default, + read-only ones marked, and a warning when another signed-in account already + syncs the same shared collection; - contacts: the address book for new contacts; - calendar: who reminds you (calendar app or Bulwark); - the interval: 15 min, 30 min, 1 h (default), 6 h, manual; - "Sync now" (`requestSync` with manual + expedited); -- the last sync time and its result, or the error with an action ("Sign in - again", "Grant access", "Review deletions"); +- the last sync time (its "… ago" refreshed every 30 s while the section is + open) and its result, or the error with an action ("Sign in again", "Grant + access", "Review deletions", which asks whether to delete the items on the + server too or bring them back; see [Deletion + threshold](#deletion-threshold)); - "Android account settings" (`openAccountSettings`). Turning sync on asks for `READ/WRITE_CONTACTS` or `READ/WRITE_CALENDAR` after a @@ -1306,16 +1458,22 @@ app's system settings when Android no longer asks. deleted and new items (bounded to 30 s; it is not a sync run, so the "disabled" preflight does not stop it), with the [deletion threshold](#deletion-threshold) applied, and never uploads rows written for - another registry account or server (SyncState `owner`). Then it deletes the - authority's rows and clears the SyncState by writing `''`. When items could - not be uploaded (offline, errors, deletions above the threshold), nothing is - deleted and the app asks: "N changes made on this device haven't reached - the server. Turn off anyway?" Turning off anyway still makes one short - upload attempt (5 s) before the rows go; "Keep syncing" turns automatic sync - back on. With nothing waiting the rows go without a question: the server - keeps everything. When both authorities are off the Android account is - removed. Turning calendar sync off also forgets who reminds of synced - events, so the question comes again the next time. + another registry account or server (SyncState `owner`). It downloads + nothing first: its uploads carry the stored state (`ifInState`), so a + server change makes it download before it decides; for an account whose + first sync never ended (no stored state) only new items go up, and the rest + counts as waiting. Then it deletes the authority's rows and clears the + SyncState by writing `''`. When items could not be uploaded (offline, + errors, deletions above the threshold), nothing is deleted and the app + asks: "N changes made on this device haven't reached the server. Turn off + anyway?" Turning off anyway still makes one short upload attempt (5 s) + before the rows go; "Keep syncing" turns automatic sync back on. With + nothing waiting the rows go without a question: the server keeps + everything. When both authorities are off the Android account is removed. + Turning calendar sync off also forgets who reminds of synced events, so the + question comes again the next time. Turning sync on, off and back on for + one app account runs one step at a time: a turn-on made while a turn-off + still tears down waits for it, then sets everything up again. - **Logout** (`auth-store.logout`, `logoutAll`, `removeAccount`): the same as disabling both, before the credentials go, with the same question when changes would be lost; "stay signed in" keeps the accounts syncing. Several @@ -1340,7 +1498,7 @@ app's system settings when Android no longer asks. on in Android Settings is adopted as on, one switched off there shows as "Paused in Android settings" (not "Off"), and "Sync now" is disabled while paused, since a manual run would only report `disabled`. `suspended` marks - an evicted account, `primaryJmapAccounts` the personal JMAP account per + an evicted account, `primaryJmapAccounts` the primary JMAP account per authority (push routes and the trigger filter). - An Android account that exists without a registry id (created, then the app died before writing it) is adopted by the next `ensureAccount`; one @@ -1404,13 +1562,14 @@ app's system settings when Android no longer asks. | Password revoked | `auth` (hard, no retry) + "Sign in again" | user signs in; the next trigger syncs | | Permission revoked | Android fails the sync (`databaseError`) | "Grant access" in settings | | `cannotCalculateChanges` | full reconcile of that JMAP account | automatic | -| Server `SetError` on an item | notFound on an update → local delete; forbidden with scheduling → retried without it ("invitations not sent"); forbidden on an update or deletion → rows rewritten from the server's current version; forbidden on a create, and the rest → poisoned with back-off, listed in the report | automatic / user fixes the item | +| Server `SetError` on an item | notFound on an update → local delete (a pair's new row too); forbidden with scheduling → retried without it ("invitations not sent" once that is accepted); forbidden on an update → rows rewritten in place from the server's current version, on a deletion → the item comes back from the server, both reported as `forbidden`; forbidden on a create, and the rest → poisoned with back-off, listed in the report | automatic / user fixes the item | +| A device change the server can't take (a read-only collection, an answer where none is allowed, an instance without its series, an in-place move to another account's calendar, a membership of a group the device can't change) | put back on the device without a request, listed in the report as `readOnly`, `rsvpRefused`, `instanceOnly`, `crossAccountMove` or `groupNotWritable` | — | | A whole `/set` refused for an account (`accountReadOnly`, `forbidden`, `accountNotFound`, `accountNotSupportedByMethod`) | that account's items wait, the report lists the account; the other accounts go on | automatic once the account takes writes | | A shared account refuses to list its address books or calendars (`forbidden`, `accountNotFound`, `accountNotSupportedByMethod`) | the account is left out of the run and only logged; its rows stay untouched | automatic once it lists them again | | A planner throws on an item | download: the object goes on `stale`; upload: the item is skipped (`plannerError`); the rest of the run goes on | fixed in code; listed in the report | | `stateMismatch` or `serverUnavailable` on a `/set` | re-download, retry up to 3 times, then `io` without progress | automatic (back-off) | | 429 | `io` with `delayUntil` = Retry-After (60 s without one) | automatic | -| Too many local deletions | `tooManyDeletions` + system notification | user picks delete or undo | +| Too many local deletions | `tooManyDeletions` + system notification | user picks delete or undo (in the notification, or "Review deletions" in the settings) | | Empty server on a full reconcile | `safetyAbort`, device untouched | user checks the server; turning sync off and on starts over | | Edit during an upload | clearing assert fails → DIRTY stays, shadow updated | next run uploads the newer edit | | JS crash or reload mid-run | task ends without report → soft error | automatic | @@ -1429,11 +1588,21 @@ app's system settings when Android no longer asks. in flight), `maxSizeRequest` (10 MB; the engine targets 1 MB), queries of at most 5000 ids per call, `/changes` pages of 256 (Stalwart caps at 5000), cards and events of at most 512 KiB. -- Provider: ≤ 499 ops between yield points (the engine packs at most 400 per - batch), ~300 KB per `applyBatch`, photos in their own batch; 50 raw contacts +- Provider: ≤ 499 ops between yield points in ContactsProvider (the engine + packs at most 400 per batch; CalendarProvider has no such limit), about + 500 KB per `applyBatch` as `estimateBatchBytes` weighs it, photos in their + own batch (photos over about 512 KiB stay on the server); 50 raw contacts or 50 events per chunk. +- A recurring meeting with many attendees on many occurrences is big: 25 + attendees on 52 overrides weigh about 2 MB of provider ops on its first + download (the shadow alone about 0.56 MB), more than one Binder transaction + (1 MB). Writes after an edit take about 0.65 MB. The engine writes an event + in one group, so such an event does not reach a device that downloads it + for the first time: it stays on `stale`. - JS thread: a `setTimeout(0)` yield between chunks, so the UI keeps rendering - when the app is open; JSON crosses the bridge as strings. + when the app is open (a teardown, which the UI starts, yields without + timers: React Native pauses them while the app is in the background and no + headless task runs); JSON crosses the bridge as strings. - Time: 9.5 min per run (2.5 min expedited), checkpoints between chunks, continuation through `fullSyncRequested`. Checkpoints stop 30 s before the payload's deadline and a create batch needs 60 s left (see [A sync @@ -1443,7 +1612,7 @@ app's system settings when Android no longer asks. group's members, reads every synced contact's shadow (`SYNC2`) once per run to index the cards by uid. -The 2,000 + 2,000 scale run is recorded in the parity notes when #34 closes. +The 2,000 + 2,000 scale run is in [Verification](#verification). ## Testing @@ -1454,18 +1623,19 @@ The 2,000 + 2,000 scale run is recorded in the parity notes when #34 closes. attendees; merge rules; the import-graph purity test. - **Engine** (vitest): `src/device-sync/__tests__/fakes/fake-provider.ts`, an in-memory ProviderPort with DIRTY/DELETED/VERSION, soft/hard deletes, - asserts, back-references, yield points and user-edit helpers (AOSP-style - in-place edits and Fossify-style delete+reinsert); and a fake JMAP server + asserts, back-references, yield points (and a batch committed up to a yield + point before it fails) and user-edit helpers (AOSP-style in-place edits and + Fossify-style delete+reinsert); and a fake JMAP server for ContactCard/CalendarEvent/AddressBook/Calendar with `/changes`, `cannotCalculateChanges`, `ifInState`, SetError injection, the async uid index and Stalwart's key behaviour. Crash injection at every checkpoint; poison items; the deletion guards; echo suppression; an edit during upload. - **Kotlin** (JUnit, `gradlew :app:testDebugUnitTest`): report mapping, op and URI translation, scoping. -- **Device** (API 34 and 32 emulators, local Stalwart only): the scenario matrix - in the #34 parity note, verified on three sides: the provider (`adb shell - content query …`), the server (JMAP calls) and the apps' UI. Debug builds - create accounts without UI: +- **Device** (API 34 and 32 emulators, local Stalwart only): the scenarios in + [Verification](#verification), checked on three sides: the provider (`adb + shell content query …`), the server (JMAP calls) and the apps' UI. Debug + builds create accounts without UI: ``` adb shell am broadcast -n com.anonymous.bulwarkmobile/.sync.DeviceSyncDebugReceiver \ @@ -1523,13 +1693,34 @@ Known limitations: include that instance in the old series again (better than Stalwart dropping the `UNTIL`). - A single occurrence that Stalwart holds only as an instance (no master) is - shown but read-only. -- In a calendar that grants RSVP but no writes, only the user's answer to the - whole series uploads; an answer for one instance is reverted. + shown but read-only: an edit is put back and reported as `instanceOnly`. +- Stalwart refuses every change to an event in a calendar shared without + write rights, even the user's own answer to an invitation (to the series or + to one occurrence). Where the calendar grants RSVP the sync sends the + answer; after the refusal (`forbidden`, also on the retry without + scheduling messages) it rewrites the rows from the server's version, so the + answer is put back on the device, and the report lists the item as + `forbidden`. Where the calendar grants no RSVP, the answer is put back + without a request and reported as `rsvpRefused`. +- When the organizer of an event is one of the user's own identities (for + example a shared mailbox the user may send as, which Stalwart lists among + the user's identities), the device shows the user as the organizer: no + answer buttons, and the user's own attendee entry is folded into the + organizer's row. Every identity address counts as "me" + (`calendarAddresses` in `src/device-sync/jmap/session.ts`, the address keys + in `src/device-sync/calendar/attendees.ts`). - Moving an event to a calendar of another JMAP account uploads as a create in the target account and a destroy in the source (a new uid; participants are notified accordingly). An app that moves it by changing `CALENDAR_ID` in - place gets the row put back by the next download of the event. + place gets the row put back at the next sync (reported as + `crossAccountMove`). +- Fossify Calendar shows the occurrences of a zoned weekly series one hour + off after a daylight-saving change (it expands the rule without the zone's + DST change); Google Calendar and Etar show them right. The rows are + correct. +- A recurring meeting too big for one provider transaction on its first + download (about 25 attendees on 52 overrides) does not reach the device; + see [Limits and performance](#limits-and-performance). - Descriptions over 64 KB are shown truncated and are not uploaded when edited. - A shared address book or calendar that two signed-in accounts can both see appears once per account when both sync it (each Android account holds its @@ -1537,7 +1728,22 @@ Known limitations: - Floating events follow the device zone; alerts relative to the end, absolute alerts and alerts after the start are kept on the server but not shown. - Photos travel as 512 px JPEG `data:` URIs; a larger original is replaced by - its scaled copy only when the user changes the photo on the device. + its scaled copy only when the user changes the photo on the device. A photo + over about 512 KiB is not shown on the device (it stays on the server), and + replacing a photo within the 96 px thumbnail by another one that small on + the device is not uploaded. +- AOSP Contacts edits an address as one formatted line. The sync reads the + parts back along the old line; when the edit changes the line's layout or + spans two parts, the server's address keeps the whole line as its street: + `full` and the components agree, but city, postcode and country are no + longer separate parts. +- The settings show the status of the last run only, and an upload sync the + providers scheduled after an app write replaces it even when it finds + nothing to do: the conflicts and item errors of the run before can + disappear from the section moments after it. +- Read-only address books and calendars and subscribed calendars were not + verified on a device (the test server had none); their handling is covered + by tests only. Open: @@ -1560,3 +1766,87 @@ Possible follow-ups: - **Importing device-local contacts** into Bulwark. - An account-scoped Colors table so calendar apps can offer event colours. - Re-basing old daily/hourly series past AOSP's 2000-period expansion cap. + +## Verification + +Checked on Android emulators against a local Stalwart 0.16.23, each time on +three sides (provider rows, server objects, the apps' screens): contacts on +API 32 with AOSP Contacts and Fossify Contacts, calendars on API 34 with +Google Calendar, Etar and Fossify Calendar. What these runs found in other +apps or in Stalwart is under [known limitations](#decisions-and-limitations). + +- **Contacts.** Every mapped field of a card (name parts, nickname, + organization with department and title, typed and labelled emails, phones + and addresses, a website, a birthday without year, an anniversary, a + relation, a note, a photo) shows in both apps. An edit in AOSP Contacts (in + place) and one in Fossify Contacts (delete and re-insert) upload only the + changed entries and leave the rest of the card alone; the next clean write + puts back on the device what Fossify dropped (a department, the primary + flag, the address parts). A contact created in AOSP Contacts goes up once; + deletions go both ways; a conflict on different fields keeps both changes, + one on the same field takes the server's and is counted. A photo within the + 96 px thumbnail keeps one row through server edits and stays on the server + after a device edit. +- **Calendar.** Timed events in two zones, a two-day all-day event, a + floating event, a weekly series with an excluded and a moved occurrence, + two alerts and a meeting show right in Google Calendar and Etar. An edit, a + new event and a deletion in Google Calendar go up, and the edit keeps the + event's unmapped properties (keywords, locale, priority, links). Etar's + "only this event", deleting one occurrence and "this and all future + events", and "only this occurrence" in Fossify Calendar and Google Calendar + become the right overrides, exclusions and series, with no instance + duplicated or lost. "Yes" and "Maybe" in Google Calendar change only the + user's `participationStatus`. Tasks and a tasks-only calendar stay off the + device; switching the reminder owner both ways rewrites only the reminders; + a calendar deselected and selected again leaves and comes back without + server writes; an event created in a shared account's writable calendar + lands in that account; a change of the device zone rewrites only floating + events. +- **No duplicates.** With the process killed, or the network cut, between a + create's `/set` and its identity write (30 contacts or 20 events at a + time), the next run adopts every row by uid, and server and device hold + each item once. +- **Deletion threshold.** 60 contacts deleted at once stay on the server + until the user chooses in Android's notification: "undo" brings them back + on the device, "delete" deletes them on the server. +- **Settings and lifecycle.** The switch, the chooser, "Sync now", "Paused in + Android settings", "Grant access" after the permissions were revoked, and + "Android account settings" (API 32 and 34) work as described. Turning sync + off with an edit waiting offline asks first, and "Keep syncing" keeps the + edit, which goes up once the network is back; turning sync off and on + removes and restores the rows without server writes or duplicates; an + account removed in Android Settings is not added back by the app; signing + out removes the rows and the Android account. +- **Covered by tests only:** contact groups and memberships, photos too + large for the device, moves between calendars and a series turned into a + single event (Etar's delete and insert), events and exceptions Google + Calendar creates without a status, a device create the server deletes + before the next download, changes put back because the server can't take + them, an address edited as one line in AOSP Contacts, and the "Review + deletions" question in the settings. + +Scale, with a debug build (JavaScript from Metro in dev mode) on an API 34 +emulator given 4 GB of memory: 2,000 cards and 2,000 events (100 of them +recurring, with overrides) on top of 152 cards and 35 events. Times are the +adapter's, from the start of the sync to the report; memory is the peak PSS +of the app process. + +| Run | Contacts | Calendar | Peak PSS | +|---|---|---|---| +| First sync, contacts alone | 2,000 inserts, 57.6 s | — | 464 MB | +| First sync, calendar alone | — | 2,000 inserts + 68 exception rows, 38.7 s | 450 MB | +| First sync, both at once (they share the JS thread) | 117.1 s | 117.8 s | 455 MB | +| Both at once, while scrolling the mail list | 150.3 s | 154.0 s | 433 MB | +| 1 card and 1 event changed on the server | 0.83 s | 1.03 s | 448 MB | +| No changes | 0.38 s | 0.70 s | 436 MB | +| Both collections deleted on the server | 2,000 deletes, 27-36 s | 2,000 deletes, 36-41 s | 452 MB | + +The app used about 412 MB before the runs (a debug build with its dev +tooling); syncing added 20-50 MB at the peak. No run crashed or caused an +ANR. During the first syncs, scrolling the mail list stayed as smooth as +without them (2.85 % janky frames against 3.05 %), and switching between the +app's tabs took as long as without a sync, except once, when the app's own +Contacts screen loaded the 2,000 new cards at the same time. The sync shares +the JS thread with the app's screens (it peaked at 96 %), so a first sync +takes longer while the app is in use (74 s instead of 57.6 s for the +contacts). A release build and a real phone were not measured. From 724c3b80ee348fdcfd057c414a541a6cc35e65dd Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:15:08 +0200 Subject: [PATCH 078/334] chore: update version number to 0.1.67 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index c24b7ee5..5730f92c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.66 +0.1.67 diff --git a/package-lock.json b/package-lock.json index 9395ca4d..ed373ed6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.66", + "version": "0.1.67", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.66", + "version": "0.1.67", "license": "AGPL-3.0-only", "dependencies": { "@react-native-async-storage/async-storage": "2.2.0", diff --git a/package.json b/package.json index 2bd83ca4..d32281f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.66", + "version": "0.1.67", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From 0110eebb7802d188292be5a87c60a1888a57ca26 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:54:03 +0200 Subject: [PATCH 079/334] fix: upload an edited event another client moved out of synced calendars #34 When another client moved an event into a calendar this device does not sync while the device had an unsent edit of it, the calendar planner refused to merge the server's version, since the event is in no synced calendar any more. The edit then waited on every run as a planner error, and the event never left the device. The server's version now merges into the rows where they are, so the edit uploads against it without undoing the other client's changes, and the rows go once the server has taken the edit. Checked with a planner test and a run through the engine and the fake server: the series rescheduled on the device keeps the occurrence the other client added, and no rows are left. --- .../__tests__/calendar/download.test.ts | 22 +++++++++++++++++++ .../__tests__/engine/real-planners.test.ts | 8 +++---- src/device-sync/calendar/planner.ts | 17 +++++++++----- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/device-sync/__tests__/calendar/download.test.ts b/src/device-sync/__tests__/calendar/download.test.ts index ab00eea5..b4884312 100644 --- a/src/device-sync/__tests__/calendar/download.test.ts +++ b/src/device-sync/__tests__/calendar/download.test.ts @@ -235,6 +235,28 @@ describe('calendar planner: downloads', () => { expect(() => calendarPlanner.planDownload({ ...single(), '@type': undefined, due: '2026-10-01T00:00:00' } as unknown as CalendarEventWire, null, h.ctx)).toThrow(/tasks/); }); + it('merges an edited event another client moved out of every synced calendar where its rows are', async () => { + const h = await new Harness().setup(); + await h.download(single()); + const id = Number(h.masterRow('e1')._id); + h.fake.user.updateEvent(id, { [Events.TITLE]: 'Team lunch' }); + // Filed in a calendar this device does not sync, with a new description. + const moved = single({ calendarIds: { archive: true }, description: 'Bring salad' }); + + const plan = calendarPlanner.planDownload(moved, await h.local('e1'), h.ctx); + await h.apply(plan.ops); + + expect(plan).toMatchObject({ effect: 'update', stillDirty: true, conflicts: 0 }); + expect(h.masterRow('e1')).toMatchObject({ [Events.CALENDAR_ID]: h.rowIds.get('b'), [Events.TITLE]: 'Team lunch', [Events.DESCRIPTION]: 'Bring salad', [Events.DIRTY]: 1 }); + const local = (await h.local('e1'))!; + expect(local.shadow).toEqual(moved); + // Only the device's edit goes up (no calendar move), and the accepted version takes the rows away. + const plan2 = calendarPlanner.planUpload(local, h.ctx); + expect(plan2.kind === 'upload' && plan2.actions).toEqual([{ kind: 'update', id: 'e1', patch: { title: 'Team lunch' } }]); + await h.apply(calendarPlanner.planAccepted(local, { ...moved, title: 'Team lunch' }, h.ctx).ops); + expect(await h.events()).toEqual([]); + }); + it('places an event in several calendars in the first selected one, and keeps it where it is', async () => { const h = await new Harness().setup(); await h.download(single({ calendarIds: { w: true, b: true } })); diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index cae21f0c..1f95bb6c 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -574,14 +574,14 @@ describe('device sync engine with the real planners', () => { 'recurrenceOverrides/2026-10-01T09:00:00': { title: 'Retro' }, }); - expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + expect(await h.run(CALENDAR_AUTHORITY)).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); const event = h.server.get('CalendarEvent', 'a', series)!; const titles = Object.values((event.recurrenceOverrides ?? {}) as Record).map((o) => o.title).sort(); expect(titles).toEqual(['Planning', 'Retro']); - // The move went up, or it still waits on the device: it is not lost. - const row = h.events().find((e) => Number(e._id) === Number(master._id)); - expect(event.start === '2026-09-28T10:00:00' || (Number(row?.dirty) === 1 && Number(row?.[Events.DTSTART]) === later)).toBe(true); + // The move went up against the server's version, and the rows went with the event. + expect(event).toMatchObject({ start: '2026-09-28T10:00:00', calendarIds: { [archive]: true } }); + expect(h.events()).toEqual([]); }); }); diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index 8348e6ef..5db04219 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -31,7 +31,7 @@ import { claimOps, claimUid, claimedUid, computeCreate, createTarget, deleteActi import { decodeCalendar, decodeEvents, isExceptionRow, isNewMaster } from './decode'; import { isExcluded, overridesOf } from './exceptions'; import { eventImage } from './image'; -import { insertEvent, isRemovedInstance, linkedExceptionCount, pickCalendar, writeEvent } from './merge'; +import { insertEvent, isRemovedInstance, linkedExceptionCount, pickCalendar, writeEvent, type CalendarTarget } from './merge'; import { pairKind, pairOps, pairPatch } from './pairs'; import { GroupBuilder, assertExceptionCount, assertRow, deleteRow, writesOf } from './rows'; import { eventZone } from './timing'; @@ -52,6 +52,12 @@ function refOf(local: LocalEventRow | null, event?: CalendarEventWire, jmapAccou return local ? `row:${local.eventId}` : 'new'; } +/** The calendar a row is in, when it is one of ours. */ +function rowCalendar(local: LocalEventRow, ctx: CalendarContext): CalendarTarget | null { + const cal = ctx.calendarIdOfRow(local.calendarRowId); + return cal ? { calendarRowId: local.calendarRowId, calendarId: cal.calendarId } : null; +} + // ─── Downloads ────────────────────────────────────────── function planDownload(event: CalendarEventWire, local: LocalEvent | null, ctx: CalendarContext): DownloadPlan { @@ -59,7 +65,9 @@ function planDownload(event: CalendarEventWire, local: LocalEvent | null, ctx: C refuseTask(local?.shadow); const ref = refOf(local, event, ctx.jmapAccountId); const none = (stillDirty: boolean): DownloadPlan => ({ ops: { ref, ops: [] }, conflicts: 0, stillDirty, effect: 'none', writes: 0 }); - const target = pickCalendar(event, local, ctx); + // An event another client moved out of every synced calendar still merges into its rows where they are + // (they wait for an upload; the accepted write then removes them). + const target = pickCalendar(event, local, ctx) ?? (local ? rowCalendar(local, ctx) : null); if (!target) throw new Error(`calendar planner: ${ref} is in no synced calendar (the engine removes such rows)`); const image = eventImage(event, target.calendarRowId, target.calendarId, ctx); // Stalwart drops a start it can't resolve (a DST gap or overlap): nothing to show. @@ -210,10 +218,7 @@ function purgeGroup(local: LocalEvent): OpGroup { function revertGroup(local: LocalEvent, ctx: CalendarContext): OpGroup | null { const shadow = local.shadow; if (!shadow) return null; - const target = pickCalendar(shadow, local, ctx) ?? (() => { - const cal = ctx.calendarIdOfRow(local.calendarRowId); - return cal ? { calendarRowId: local.calendarRowId, calendarId: cal.calendarId } : null; - })(); + const target = pickCalendar(shadow, local, ctx) ?? rowCalendar(local, ctx); if (!target) return null; const image = eventImage(shadow, target.calendarRowId, target.calendarId, ctx); if (!image) return null; From 4bcca705485789907bd8fe42e6dd412b67619a78 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:55:03 +0200 Subject: [PATCH 080/334] fix: write big meetings in several provider transactions #34 A recurring meeting with many attendees on many occurrences (25 on 52, 100 on 12) is about 2 MB of provider operations, and one Android provider transaction carries 1 MB. Every event was written as one group that the engine never splits, so such a meeting was refused every run and never reached a phone that synced it for the first time. An item too big for one group now comes as a chain of groups that the engine applies one by one, each in a transaction of its own. A new event is written master first, with its identity, then its occurrences in groups that fit, then its shadow; a write into an existing event writes the occurrences first and the series with its new shadow last. When a run stops between two groups, the next one finds the event by its identity and writes only what is missing, planned from the old shadow, so no row is written twice and no server change is lost. Checked with planner tests for both meeting sizes and every cut point, and runs through the engine with a provider that refuses transactions over 1 MB, a crash after every batch included. --- .../__tests__/calendar/big-events.test.ts | 158 +++++++++++++++++- src/device-sync/__tests__/calendar/harness.ts | 7 +- .../__tests__/engine/internals.test.ts | 51 ++++++ src/device-sync/calendar/merge.ts | 73 ++++++-- src/device-sync/calendar/planner.ts | 12 +- src/device-sync/calendar/rows.ts | 143 +++++++++++++++- src/device-sync/engine/batch.ts | 81 ++++++++- src/device-sync/engine/calendar-sync.ts | 6 +- src/device-sync/engine/item-sync.ts | 7 +- src/device-sync/planner.ts | 9 + 10 files changed, 505 insertions(+), 42 deletions(-) diff --git a/src/device-sync/__tests__/calendar/big-events.test.ts b/src/device-sync/__tests__/calendar/big-events.test.ts index d367ab0f..0b1942ba 100644 --- a/src/device-sync/__tests__/calendar/big-events.test.ts +++ b/src/device-sync/__tests__/calendar/big-events.test.ts @@ -4,17 +4,23 @@ import { describe, expect, it } from 'vitest'; import { Attendees, Events } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { estimateBatchBytes } from '../../engine/batch'; import type { OpGroup } from '../../planner'; +import { CALENDAR_AUTHORITY, type ProviderPort } from '../../types'; import type { CalendarEventWire } from '../../wire'; +import { createHarness, rowWrites, type Harness as EngineHarness } from '../engine/harness'; import { Harness, ME, serverApply } from './harness'; const TRANSACTION = 1_000_000; const estimate = (group: OpGroup) => 2 * JSON.stringify(group.ops).length + 300 * group.ops.length; +/** A plan's groups: the group and the ones applied after it. */ +const chain = (group: OpGroup): OpGroup[] => [group, ...(group.next ?? [])]; /** A daily meeting with `attendees` guests whose `overrides` occurrences each carry the whole participants map. */ -function meeting(attendees: number, overrides: number): CalendarEventWire { +function meeting(attendees: number, overrides: number, me = ME, calendarId = 'b'): CalendarEventWire { const participants: Record = { - me: { '@type': 'Participant', calendarAddress: `mailto:${ME}`, roles: { owner: true, attendee: true }, participationStatus: 'accepted' }, + me: { '@type': 'Participant', calendarAddress: `mailto:${me}`, roles: { owner: true, attendee: true }, participationStatus: 'accepted' }, }; for (let i = 0; i < attendees; i++) { participants[`p${i}`] = { '@type': 'Participant', calendarAddress: `mailto:person${i}@example.com`, name: `Person ${i}`, roles: { attendee: true }, participationStatus: 'needs-action', expectReply: true }; @@ -28,19 +34,54 @@ function meeting(attendees: number, overrides: number): CalendarEventWire { id: 'big', '@type': 'Event', uid: 'big@example.org', - calendarIds: { b: true }, + calendarIds: { [calendarId]: true }, title: 'Standup', description: 'Daily standup', start: '2026-10-05T09:00:00', timeZone: 'Europe/Berlin', duration: 'PT15M', recurrenceRule: { frequency: 'daily', count: 300 }, - organizerCalendarAddress: `mailto:${ME}`, + organizerCalendarAddress: `mailto:${me}`, participants, recurrenceOverrides, } as unknown as CalendarEventWire; } +/** The engine with the real planners, its provider refusing a batch bigger than one Binder transaction (as Android does). */ +function engineWithBinderLimit(): EngineHarness { + const h = createHarness(); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + const base = h.deps.provider; + h.deps.provider = (name, authority): ProviderPort => { + const port = base(name, authority); + return { + accountName: port.accountName, + authority: port.authority, + query: (q) => port.query(q), + readSyncState: () => port.readSyncState(), + readPhoto: (id, px) => port.readPhoto(id, px), + applyBatch: async (ops) => + estimateBatchBytes(ops) > TRANSACTION ? { ok: false, reason: 'tooLarge', message: 'TransactionTooLargeException' } : port.applyBatch(ops), + }; + }; + return h; +} + +/** The big meeting's rows on the engine's device: one master, one exception row per override, every attendee once. */ +function expectWholeMeeting(h: EngineHarness, id: string, attendees: number, overrides: number, context = ''): void { + const rows = h.events(); + const masters = rows.filter((e) => e[Events._SYNC_ID] === `a/${id}`); + expect(masters, context).toHaveLength(1); + const exceptions = rows.filter((e) => e[Events.ORIGINAL_SYNC_ID] === `a/${id}`); + expect(exceptions, context).toHaveLength(overrides); + expect(new Set(exceptions.map((x) => x[Events._SYNC_ID])).size, context).toBe(overrides); + expect(exceptions.every((x) => Number(x[Events.ORIGINAL_ID]) === Number(masters[0]._id)), context).toBe(true); + const attendeesOf = (eventId: unknown) => h.device.rows('attendees').filter((a) => Number(a.event_id) === Number(eventId)).length; + expect([masters[0], ...exceptions].every((row) => attendeesOf(row._id) === attendees + 1), context).toBe(true); + expect(Object.keys(JSON.parse(String(masters[0][Events.SYNC_DATA1])).recurrenceOverrides), context).toHaveLength(overrides); + expect(rows.filter((e) => e[Events.ORIGINAL_SYNC_ID] === null), context).toHaveLength(1); +} + async function synced(event: CalendarEventWire) { const h = await new Harness().setup(); await h.download(event); @@ -87,4 +128,113 @@ describe('calendar planner: big meetings', () => { expect(await h.tryApply(accepted.ops)).toMatchObject({ ok: false, reason: 'assert' }); expect(h.fake.rows('attendees', `${Attendees.EVENT_ID} = ? AND ${Attendees.ATTENDEE_EMAIL} = ?`, [x.eventId, 'person1@example.com'])[0][Attendees.ATTENDEE_STATUS]).toBe(1); }); + + it.each([ + [25, 52], + [100, 12], + ])('writes the first download of %i attendees on %i overrides in groups that each fit one transaction', async (attendees, overrides) => { + const h = await new Harness().setup(); + const event = meeting(attendees, overrides); + + const plan = calendarPlanner.planDownload(event, null, h.ctx); + + expect(plan.effect).toBe('insert'); + expect(chain(plan.ops).length).toBeGreaterThan(1); + for (const group of chain(plan.ops)) expect(estimate(group)).toBeLessThan(TRANSACTION); + await h.apply(plan.ops); + const local = (await h.local('big'))!; + expect(local.shadow).toEqual(event); + expect(local.exceptions.map((x) => x.recurrenceId).sort()).toEqual(Object.keys(event.recurrenceOverrides!).sort()); + expect(local.exceptions.every((x) => x.attendees.length === attendees + 1 && !x.dirty)).toBe(true); + // Its echo writes nothing. + expect(calendarPlanner.planDownload(event, local, h.ctx).effect).toBe('none'); + }); + + it('finishes a first download cut off between its groups, without writing a row twice', async () => { + const event = meeting(25, 52); + const groups = chain(calendarPlanner.planDownload(event, null, (await new Harness().setup()).ctx).ops).length; + for (let cut = 1; cut < groups; cut++) { + const h = await new Harness().setup(); + for (const group of chain(calendarPlanner.planDownload(event, null, h.ctx).ops).slice(0, cut)) await h.apply({ ref: group.ref, ops: group.ops }); + + // Planned again from what the first groups wrote. + const again = calendarPlanner.planDownload(event, await h.local('big'), h.ctx); + for (const group of chain(again.ops)) expect(estimate(group)).toBeLessThan(TRANSACTION); + await h.apply(again.ops); + + const local = (await h.local('big'))!; + expect(local.shadow, `cut after ${cut}`).toEqual(event); + expect(new Set(local.exceptions.map((x) => x.recurrenceId)).size, `cut after ${cut}`).toBe(52); + expect(local.exceptions, `cut after ${cut}`).toHaveLength(52); + expect(local.exceptions.every((x) => x.attendees.length === 26), `cut after ${cut}`).toBe(true); + expect(calendarPlanner.planDownload(event, local, h.ctx).effect, `cut after ${cut}`).toBe('none'); + } + // Every cut point writes a 2 MB meeting again. + }, 60_000); + + it('writes a change of every occurrence in groups, the series with its new shadow last', async () => { + const h = await new Harness().setup(); + const event = meeting(25, 52); + await h.download(event); + // Another client adds a guest to the meeting and to every occurrence. + const guest = { '@type': 'Participant', calendarAddress: 'mailto:guest@example.com', name: 'Guest', roles: { attendee: true }, participationStatus: 'needs-action' }; + const changed = JSON.parse(JSON.stringify(event)) as CalendarEventWire; + (changed.participants as Record).guest = guest; + for (const override of Object.values(changed.recurrenceOverrides!)) (override as { participants: Record }).participants.guest = guest; + + const plan = calendarPlanner.planDownload(changed, await h.local('big'), h.ctx); + + const groups = chain(plan.ops); + expect(groups.length).toBeGreaterThan(1); + for (const group of groups) expect(estimate(group)).toBeLessThan(TRANSACTION); + const writesShadow = (group: OpGroup) => group.ops.some((op) => op.op === 'update' && Events.SYNC_DATA1 in op.values); + expect(groups.map(writesShadow)).toEqual(groups.map((_, i) => i === groups.length - 1)); + // Cut off after the first group: planned again from what it wrote, the rest follows. + await h.apply({ ref: groups[0].ref, ops: groups[0].ops }); + await h.apply(calendarPlanner.planDownload(changed, await h.local('big'), h.ctx).ops); + const local = (await h.local('big'))!; + expect(local.shadow).toEqual(changed); + expect(local.exceptions.every((x) => x.attendees.length === 27)).toBe(true); + expect(calendarPlanner.planDownload(changed, local, h.ctx).effect).toBe('none'); + }); +}); + +describe('device sync engine: big meetings', () => { + it.each([ + [25, 52], + [100, 12], + ])('downloads a meeting of %i attendees on %i overrides in several transactions', async (attendees, overrides) => { + const h = engineWithBinderLimit(); + const { id: _drop, ...event } = meeting(attendees, overrides, 'alice@example.com', h.calendar) as unknown as Record; + const id = h.server.addEvent('a', event); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { downloaded: { created: 1 } } }); + expectWholeMeeting(h, id, attendees, overrides); + const before = h.batches.log.length; + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + expect(rowWrites(h.batches.log.slice(before))).toEqual([]); + }); + + it('finishes a big meeting cut off between its groups by a crash, without duplicating rows', async () => { + const setup = () => { + const h = engineWithBinderLimit(); + const { id: _drop, ...event } = meeting(25, 52, 'alice@example.com', h.calendar) as unknown as Record; + return { h, id: h.server.addEvent('a', event) }; + }; + const probe = setup(); + await probe.h.run(CALENDAR_AUTHORITY); + const batches = probe.h.batches.applied; + + for (let n = 1; n < batches; n++) { + const { h, id } = setup(); + h.batches.crashAfter = n; + await h.run(CALENDAR_AUTHORITY); + h.batches.crashAfter = null; + expect((await h.run(CALENDAR_AUTHORITY)).outcome, `crash after batch ${n}`).toBe('ok'); + expectWholeMeeting(h, id, 25, 52, `crash after batch ${n}`); + } + // Two runs of a 2 MB meeting per crash point. + }, 60_000); }); diff --git a/src/device-sync/__tests__/calendar/harness.ts b/src/device-sync/__tests__/calendar/harness.ts index e8f5e7af..1ec0873c 100644 --- a/src/device-sync/__tests__/calendar/harness.ts +++ b/src/device-sync/__tests__/calendar/harness.ts @@ -86,9 +86,12 @@ export class Harness { return this; } + /** Applies a group and the groups after it (`next`), each in a batch of its own, as the engine does. */ async apply(group: OpGroup): Promise { - const result = await this.port.applyBatch(group.ops); - if (!result.ok) throw new Error(`batch ${group.ref} failed: ${result.reason} ${result.message}`); + for (const g of [group, ...(group.next ?? [])]) { + const result = await this.port.applyBatch(g.ops); + if (!result.ok) throw new Error(`batch ${group.ref} failed: ${result.reason} ${result.message}`); + } } async tryApply(group: OpGroup) { diff --git a/src/device-sync/__tests__/engine/internals.test.ts b/src/device-sync/__tests__/engine/internals.test.ts index 974f9c9b..33323444 100644 --- a/src/device-sync/__tests__/engine/internals.test.ts +++ b/src/device-sync/__tests__/engine/internals.test.ts @@ -201,6 +201,57 @@ describe('provider batches', () => { await writer(p).write([{ group: { ref: 'g', ops: [{ op: 'assert', table: 'raw_contacts', id: 1, values: { dirty: 0 } }] } }]); expect(p.sent).toEqual([]); }); + + describe('an item too big for one group (a chain)', () => { + const named = (n: string) => insert('data', { n }); + const namesOf = (batches: ProviderOp[][]) => batches.map((b) => b.map((op) => (op.op === 'insert' ? op.values.n : op.op))); + const chained = (extra: Partial = {}): Work => ({ + group: { ref: 'big', ops: [named('1')], next: [{ ref: 'big', ops: [named('2')] }, { ref: 'big', ops: [named('3')] }] }, + ...extra, + }); + + it('applies its groups in order, each in a batch of its own, between the other works, and counts it after the last', async () => { + const p = port(ok); + const applied: string[] = []; + const store = new StateStore(emptySyncState(), { registryId: 'r', origin: 'o' }); + const big = chained({ + applied: (results) => void applied.push(`big:${results.length}`), + state: (next) => { + accountOf(next, 'a').stale = ['big']; + }, + }); + const other = (n: string): Work => ({ group: { ref: n, ops: [named(n)] }, applied: () => void applied.push(n) }); + + await new BatchWriter(p, { maxOps: 400, maxBytes: 300_000, maxReplans: 3 }, store).write( + [other('a'), big, other('b')], + store.tail(() => undefined), + ); + + expect(namesOf(p.sent)).toEqual([['a'], ['1'], ['2'], ['3', 'syncState'], ['b', 'syncState']]); + expect(applied).toEqual(['a', 'big:3', 'b']); + expect(store.committed.accounts.a.stale).toEqual(['big']); + }); + + it('plans the item again from what its first groups wrote when a later one fails, never sending that one again', async () => { + let failures = 1; + const p = port((ops) => (namesOf([ops])[0].includes('2') && failures-- > 0 ? { ok: false, reason: 'assert', message: 'changed' } : ok(ops))); + const rest: Work = { group: { ref: 'big', ops: [named('rest')] } }; + + await writer(p).write([chained({ replan: async () => rest })]); + + expect(namesOf(p.sent)).toEqual([['1'], ['2'], ['rest']]); + }); + + it('gives the item up when one of its groups is refused for good', async () => { + const p = port((ops) => (namesOf([ops])[0].includes('2') ? { ok: false, reason: 'tooLarge', message: 'TransactionTooLargeException' } : ok(ops))); + const failed = vi.fn(); + + await writer(p).write([chained({ failed, replan: async () => null })]); + + expect(namesOf(p.sent)).toEqual([['1'], ['2']]); + expect(failed).toHaveBeenCalledWith('tooLarge', 'TransactionTooLargeException'); + }); + }); }); describe('SyncState', () => { diff --git a/src/device-sync/calendar/merge.ts b/src/device-sync/calendar/merge.ts index eadd6b71..f03a9834 100644 --- a/src/device-sync/calendar/merge.ts +++ b/src/device-sync/calendar/merge.ts @@ -19,7 +19,7 @@ * edit of them) and the number of exception rows. */ import { Events } from '../android-columns'; -import type { CalendarContext, EventBaseline, LocalEvent, LocalEventRow, LocalException } from '../planner'; +import type { CalendarContext, EventBaseline, LocalEvent, LocalEventRow, LocalException, OpGroup } from '../planner'; import type { Row } from '../types'; import type { CalendarEventWire } from '../wire'; import { collectionKey, exceptionRef, objectRef } from '../common/ids'; @@ -31,6 +31,8 @@ import { assertExceptionCount, assertRow, deleteRow, + eventInsert, + eventWrite, insertRow, updateRow, type RowState, @@ -240,7 +242,8 @@ export type WriteMode = | 'overwrite'; export interface EventWriteResult { - group: GroupBuilder; + /** One group, or a chain for an event too big for one (see `eventWrite`). */ + ops: OpGroup; conflicts: number; stillDirty: boolean; } @@ -277,7 +280,6 @@ export function writeEvent( options: EventWriteOptions, ): EventWriteResult { const { ctx } = options; - const group = new GroupBuilder(options.ref); const overwrite = options.mode === 'overwrite'; const masterRef = objectRef(ctx.jmapAccountId, event.id); const uid = typeof event.uid === 'string' && event.uid ? event.uid : null; @@ -288,9 +290,14 @@ export function writeEvent( // Asserts first: the rows as read. Attendees and reminders only for dirty rows (see `assertRow`): a big // meeting's occurrences would otherwise assert every attendee and outgrow one provider transaction. - assertRow(group, local, false, local.dirty || local.deleted); - for (const x of local.exceptions) assertRow(group, x, true, x.dirty || x.deleted); - assertExceptionCount(group, local.eventId, linkedExceptionCount(local)); + const opsOf = (build: (group: GroupBuilder) => void) => { + const group = new GroupBuilder(options.ref); + build(group); + return group.build().ops; + }; + const masterAsserts = opsOf((g) => assertRow(g, local, false, local.dirty || local.deleted)); + const rowAsserts = new Map(local.exceptions.map((x) => [x.eventId, opsOf((g) => assertRow(g, x, true, x.dirty || x.deleted))])); + const countAssert = opsOf((g) => assertExceptionCount(g, local.eventId, linkedExceptionCount(local))); let conflicts = 0; let stillDirty = false; @@ -412,12 +419,17 @@ export function writeEvent( updates.push({ row, state: exceptionState(key, merged, left, row, !!r) }); } - for (const row of deletes) deleteRow(group, row.eventId); - updateRow(group, local, masterState); - for (const { row, state } of updates) updateRow(group, row, state); - for (const state of inserts) insertRow(group, state, { id: local.eventId }); + const ops = eventWrite(options.ref, { + master: masterAsserts, + rows: rowAsserts, + count: countAssert, + deletes: deletes.map((row) => ({ row: row.eventId, asserts: [], writes: opsOf((g) => deleteRow(g, row.eventId)) })), + masterWrite: opsOf((g) => updateRow(g, local, masterState)), + updates: updates.map(({ row, state }) => ({ row: row.eventId, asserts: [], writes: opsOf((g) => updateRow(g, row, state)) })), + inserts: inserts.map((state) => ({ asserts: [], writes: opsOf((g) => insertRow(g, state, { id: local.eventId })) })), + }); - return { group, conflicts, stillDirty: !overwrite && (stillDirty || local.deleted) }; + return { ops, conflicts, stillDirty: !overwrite && (stillDirty || local.deleted) }; } /** The row an exception image becomes, written clean. */ @@ -438,12 +450,8 @@ export function imageState(x: ExceptionImage, calendarRowId: number, masterRef: }; } -/** A new event's rows: the master and one row per exception, attendees and reminders included. */ -export function insertEvent(event: CalendarEventWire, image: EventImage, ctx: CalendarContext, ref: string): GroupBuilder { - const group = new GroupBuilder(ref); - const masterRef = objectRef(ctx.jmapAccountId, event.id); - const uid = typeof event.uid === 'string' && event.uid ? event.uid : null; - const master = insertRow(group, { +function masterInsertState(event: CalendarEventWire, image: EventImage, masterRef: string, uid: string | null): RowState { + return { isException: false, cells: image.master.cells, attendees: image.master.attendees, @@ -453,10 +461,39 @@ export function insertEvent(event: CalendarEventWire, image: EventImage, ctx: Ca uid, shadow: event, hasParticipants: image.master.hasParticipants, - }); + }; +} + +/** A new event's rows: the master and one row per exception, attendees and reminders included. */ +export function insertEvent(event: CalendarEventWire, image: EventImage, ctx: CalendarContext, ref: string): GroupBuilder { + const group = new GroupBuilder(ref); + const masterRef = objectRef(ctx.jmapAccountId, event.id); + const uid = typeof event.uid === 'string' && event.uid ? event.uid : null; + const master = insertRow(group, masterInsertState(event, image, masterRef, uid)); for (const key of [...image.exceptions.keys()].sort()) { const x = image.exceptions.get(key)!; insertRow(group, imageState(x, Number(image.master.cells[Events.CALENDAR_ID]), masterRef, uid), { ref: master }); } return group; } + +/** A new event's rows as one group, or as a chain for an event too big for one (see `eventInsert`). */ +export function insertEventOps(event: CalendarEventWire, image: EventImage, ctx: CalendarContext, ref: string): OpGroup { + const masterRef = objectRef(ctx.jmapAccountId, event.id); + const uid = typeof event.uid === 'string' && event.uid ? event.uid : null; + const calendarRowId = Number(image.master.cells[Events.CALENDAR_ID]); + const opsOf = (build: (group: GroupBuilder) => void) => { + const group = new GroupBuilder(ref); + build(group); + return group.build().ops; + }; + return eventInsert(ref, insertEvent(event, image, ctx, ref).build(), () => ({ + masterRef, + master: opsOf((g) => insertRow(g, { ...masterInsertState(event, image, masterRef, uid), shadow: undefined })), + exceptions: [...image.exceptions.keys()].sort().map((key) => ({ + asserts: [], + writes: opsOf((g) => insertRow(g, imageState(image.exceptions.get(key)!, calendarRowId, masterRef, uid))), + })), + shadow: event, + })); +} diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index 5db04219..4ee3b26b 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -31,7 +31,7 @@ import { claimOps, claimUid, claimedUid, computeCreate, createTarget, deleteActi import { decodeCalendar, decodeEvents, isExceptionRow, isNewMaster } from './decode'; import { isExcluded, overridesOf } from './exceptions'; import { eventImage } from './image'; -import { insertEvent, isRemovedInstance, linkedExceptionCount, pickCalendar, writeEvent, type CalendarTarget } from './merge'; +import { insertEvent, insertEventOps, isRemovedInstance, linkedExceptionCount, pickCalendar, writeEvent, type CalendarTarget } from './merge'; import { pairKind, pairOps, pairPatch } from './pairs'; import { GroupBuilder, assertExceptionCount, assertRow, deleteRow, writesOf } from './rows'; import { eventZone } from './timing'; @@ -74,7 +74,7 @@ function planDownload(event: CalendarEventWire, local: LocalEvent | null, ctx: C if (!image) return none(!!local && (local.dirty || local.deleted)); if (!local) { - const ops = insertEvent(event, image, ctx, ref).build(); + const ops = insertEventOps(event, image, ctx, ref); return { ops, conflicts: 0, stillDirty: false, effect: 'insert', writes: writesOf(ops) }; } // A split clone only looks like this object: it is a new event and uploads as one. The source row is the object's. @@ -118,7 +118,7 @@ function planDownload(event: CalendarEventWire, local: LocalEvent | null, ctx: C ...(local.shadow ? {} : { pending: null }), clearPoison: !itemDirty, }); - const ops = result.group.build(); + const ops = result.ops; const writes = writesOf(ops); return { ops: writes ? ops : { ref, ops: [] }, @@ -222,7 +222,7 @@ function revertGroup(local: LocalEvent, ctx: CalendarContext): OpGroup | null { if (!target) return null; const image = eventImage(shadow, target.calendarRowId, target.calendarId, ctx); if (!image) return null; - return writeEvent(shadow, image, target, local, { mode: 'overwrite', ref: refOf(local), ctx, base: shadow }).group.build(); + return writeEvent(shadow, image, target, local, { mode: 'overwrite', ref: refOf(local), ctx, base: shadow }).ops; } /** Clears DIRTY where nothing uploadable changed, taking the rows as they are as the new baselines. */ @@ -368,7 +368,7 @@ function planAccepted(local: LocalEvent, server: CalendarEventWire, ctx: Calenda base: local.shadow ?? server, pending: null, clearPoison: true, - }).group.build(); + }).ops; return { ops, keepDirtyOps }; } @@ -411,7 +411,7 @@ function planZoneChange(local: LocalEvent, previousZone: string, ctx: CalendarCo if (!target) return null; const image = eventImage(shadow, target.calendarRowId, target.calendarId, ctx); if (!image) return null; - const ops = writeEvent(shadow, image, target, local, { mode: 'overwrite', ref: refOf(local), ctx, base: shadow }).group.build(); + const ops = writeEvent(shadow, image, target, local, { mode: 'overwrite', ref: refOf(local), ctx, base: shadow }).ops; return writesOf(ops) ? ops : null; } diff --git a/src/device-sync/calendar/rows.ts b/src/device-sync/calendar/rows.ts index 8c877a6c..79111f57 100644 --- a/src/device-sync/calendar/rows.ts +++ b/src/device-sync/calendar/rows.ts @@ -14,6 +14,8 @@ import type { EventBaseline, LocalEventRow, OpGroup, PendingCreate, PoisonMarker import type { ProviderOp, Row, WriteRow } from '../types'; import type { CalendarEventWire } from '../wire'; import { deepEqual } from '../common/json'; +import { MAX_OPS_PER_YIELD, estimateBatchBytes } from '../engine/batch'; +import { DEFAULT_TUNING } from '../engine/deps'; import { ATTENDEE_CELLS, MASTER_CELLS, EXCEPTION_CELLS, REMINDER_CELLS, encodeBaseline, sameCell } from './columns'; import { rowReminder } from './reminders'; @@ -245,7 +247,144 @@ function writeReminders(group: GroupBuilder, current: LocalEventRow, wanted: Row for (const add of inserts) group.write({ op: 'insert', table: 'reminders', values: { ...add, [Reminders.EVENT_ID]: current.eventId } }); } -/** Counts the provider writes of a group (asserts are not writes). */ +/** Counts the provider writes of a group and the groups after it (asserts are not writes). */ export function writesOf(group: OpGroup): number { - return group.ops.filter((op) => op.op === 'insert' || op.op === 'update' || op.op === 'delete').length; + return [group, ...(group.next ?? [])].reduce( + (n, g) => n + g.ops.filter((op) => op.op === 'insert' || op.op === 'update' || op.op === 'delete').length, + 0, + ); +} + +// ─── Items too big for one group ──────────────────────── + +/** + * What one group may hold: the engine's batch budget as `estimateBatchBytes` + * weighs it, and one op less than a provider yield window (room for the + * engine's insert guard). A bigger event is written as a chain of groups + * (`OpGroup.next`); a master's shadow is one column and may take a group of + * its own beyond the budget (under one transaction for any event that syncs). + */ +const GROUP_BYTES = DEFAULT_TUNING.maxBatchBytes; +const GROUP_OPS = MAX_OPS_PER_YIELD - 1; + +const weight = (ops: readonly ProviderOp[]) => (ops.length ? estimateBatchBytes(ops) : 0); + +/** One group from asserts and write blocks whose `refs` index into their own block, asserts first. */ +function assemble(ref: string, asserts: readonly ProviderOp[], blocks: ReadonlyArray): OpGroup { + const ops: ProviderOp[] = [...asserts]; + for (const block of blocks) { + const offset = ops.length; + for (const op of block) { + if (op.op !== 'insert' || !op.refs) ops.push(op); + else ops.push({ ...op, refs: Object.fromEntries(Object.entries(op.refs).map(([column, i]) => [column, i + offset])) }); + } + } + return { ref, ops }; +} + +/** Writes of one row (`refs` index into them), and the asserts that guard that row (none for a new row). */ +export interface RowWrite { + asserts: ProviderOp[]; + writes: ProviderOp[]; +} + +/** + * Cuts row writes into groups of the budget, in order, a row never split: + * each group starts with `guard`, the first also with `first`. + */ +function cutRows(ref: string, rows: readonly RowWrite[], guard: readonly ProviderOp[], first: readonly ProviderOp[] = []): OpGroup[] { + const groups: OpGroup[] = []; + let asserts: ProviderOp[] = [...guard, ...first]; + let blocks: ProviderOp[][] = []; + let ops = asserts.length; + let bytes = weight(asserts); + for (const row of rows) { + const n = row.asserts.length + row.writes.length; + const b = weight(row.asserts) + weight(row.writes); + if (blocks.length && (ops + n > GROUP_OPS || bytes + b > GROUP_BYTES)) { + groups.push(assemble(ref, asserts, blocks)); + asserts = [...guard]; + blocks = []; + ops = asserts.length; + bytes = weight(asserts); + } + asserts.push(...row.asserts); + blocks.push(row.writes); + ops += n; + bytes += b; + } + if (blocks.length) groups.push(assemble(ref, asserts, blocks)); + return groups; +} + +function chained(groups: readonly OpGroup[]): OpGroup { + const [first, ...next] = groups; + return next.length ? { ...first, next } : first; +} + +/** + * The writes into an existing event as one group: asserts first (`master`, + * the exception rows' `rows` in order, `count`), then the writes (`deletes`, + * the master's `master`, `updates`, `inserts`). When that is more than a group + * may hold, a chain: the exception rows' writes in groups of the budget, each + * asserting the master row and the rows it writes, the first also the number + * of exception rows; then the master's write, with its new shadow, last, + * asserting the other rows. Cut off in between, the item is planned again + * from the old shadow, so no server change to a row not written yet is taken + * as done. + */ +export function eventWrite( + ref: string, + parts: { + master: ProviderOp[]; + rows: Map; + count: ProviderOp[]; + deletes: Array; + masterWrite: ProviderOp[]; + updates: Array; + inserts: RowWrite[]; + }, +): OpGroup { + const rowAsserts = [...parts.rows.values()].flat(); + const single = assemble(ref, [...parts.master, ...rowAsserts, ...parts.count], [ + ...parts.deletes.map((d) => d.writes), + parts.masterWrite, + ...parts.updates.map((u) => u.writes), + ...parts.inserts.map((i) => i.writes), + ]); + if (single.ops.length <= GROUP_OPS && estimateBatchBytes(single.ops) <= GROUP_BYTES) return single; + const writing = [...parts.deletes, ...parts.updates].filter((w) => w.writes.length); + if (!writing.length && !parts.inserts.length) return single; + const written = new Set(writing.map((w) => w.row)); + const rows: RowWrite[] = [ + ...writing.map((w) => ({ asserts: parts.rows.get(w.row) ?? [], writes: w.writes })), + ...parts.inserts, + ]; + // The master row's own assert guards every group; its attendees and reminders go with its write. + const groups = cutRows(ref, rows, parts.master.slice(0, 1), parts.count); + const rest = [...parts.rows].filter(([row]) => !written.has(row)).flatMap(([, asserts]) => asserts); + groups.push(assemble(ref, [...parts.master, ...rest], [parts.masterWrite])); + return chained(groups); +} + +/** + * A new event's rows as one group, or, when they are more than a group may + * hold, as a chain: `master` (the master's insert with its identity but + * without its shadow), its exception rows in groups of the budget, each + * asserting the master by `masterRef` and linked to it by ORIGINAL_SYNC_ID + * (the provider fills ORIGINAL_ID from it and CALENDAR_ID), then `shadow`. + * Cut off in between, the next download finds the master by its identity and + * writes what is missing; a master without a shadow merges like an adopted one. + */ +export function eventInsert( + ref: string, + single: OpGroup, + partsOf: () => { masterRef: string; master: ProviderOp[]; exceptions: RowWrite[]; shadow: CalendarEventWire }, +): OpGroup { + if (single.ops.length <= GROUP_OPS && estimateBatchBytes(single.ops) <= GROUP_BYTES) return single; + const parts = partsOf(); + const byIdentity = { table: 'events' as const, where: `${Events._SYNC_ID} = ?`, args: [parts.masterRef], expectCount: 1 }; + const groups = [assemble(ref, [], [parts.master]), ...cutRows(ref, parts.exceptions, [{ op: 'assert', ...byIdentity }])]; + groups.push({ ref, ops: [{ op: 'update', ...byIdentity, values: { [Events.SYNC_DATA1]: JSON.stringify(parts.shadow) } }] }); + return chained(groups); } diff --git a/src/device-sync/engine/batch.ts b/src/device-sync/engine/batch.ts index 334a4419..686d7aea 100644 --- a/src/device-sync/engine/batch.ts +++ b/src/device-sync/engine/batch.ts @@ -23,6 +23,12 @@ * A group whose rows the SyncState describes (a group row: `groups`) carries * that `state` change: every batch that applies such a group, a retry * included, ends with a state op holding it. + * + * An item too big for one transaction comes as a chain (`OpGroup.next`): its + * groups go in order, each in a batch of its own, between the batches of the + * works before and after it; its `state` and `applied` go with the last. A + * group of the chain that fails leaves the earlier ones written: after an + * assert the item is read and planned again from them, else it is given up. */ import type { OpGroup } from '../planner'; import type { BatchFailure, OpResult, ProviderOp, ProviderPort } from '../types'; @@ -67,6 +73,17 @@ export function estimateBatchBytes(ops: readonly ProviderOp[]): number { return 2 * JSON.stringify(ops).length + 300 * ops.length; } +/** A group and the groups of its item that follow it (`next`), each without `next`. */ +export function chainOf(group: OpGroup): OpGroup[] { + const { next, ...first } = group; + return [first, ...(next ?? [])]; +} + +/** Whether a group, or one of the groups after it, writes anything. */ +export function chainHasWrites(group: OpGroup): boolean { + return chainOf(group).some((g) => hasWrites(g.ops)); +} + function carriesBlob(ops: readonly ProviderOp[]): boolean { return ops.some( (op) => @@ -100,19 +117,30 @@ export function buildBatch(groups: readonly OpGroup[], tailOp?: ProviderOp): Pro return out; } -/** Prepends ops to a group (e.g. an engine-side assert), shifting the group's refs. */ +/** Prepends ops to a group (e.g. an engine-side assert), shifting the group's refs; the groups after it stay. */ export function prependOps(group: OpGroup, ops: ProviderOp[]): OpGroup { - return { ref: group.ref, ops: [...ops, ...placeGroup(group.ops, ops.length).map(stripYield)] }; + return { ...group, ops: [...ops, ...placeGroup(group.ops, ops.length).map(stripYield)] }; } -/** One group running `first` then `second` atomically. */ +/** One group running `first` then `second` atomically (then the groups after `second`). */ export function concatGroups(ref: string, first: OpGroup, second: OpGroup): OpGroup { return { ref, ops: [...first.ops.map(stripYield), ...placeGroup(second.ops, first.ops.length).map(stripYield)], + ...(second.next ? { next: second.next } : {}), }; } +/** Appends ops to the last group of a chain (e.g. clearing a poison marker once the item is written). */ +export function appendToLast(group: OpGroup, ops: ProviderOp[]): OpGroup { + if (!ops.length) return group; + if (!group.next?.length) return { ...group, ops: [...group.ops, ...ops] }; + const next = [...group.next]; + const last = next[next.length - 1]; + next[next.length - 1] = { ...last, ops: [...last.ops, ...ops] }; + return { ...group, next }; +} + function stripYield(op: ProviderOp): ProviderOp { const { yieldAllowed: _y, ...rest } = op as ProviderOp & { yieldAllowed?: boolean }; return rest as ProviderOp; @@ -134,7 +162,22 @@ export class BatchWriter { * every group of `works` was applied or given up. */ async write(works: readonly Work[], tail?: Tail): Promise { - const todo = works.filter((w) => hasWrites(w.group.ops)); + let run: Work[] = []; + for (const work of works) { + if (!chainHasWrites(work.group)) continue; + if (!work.group.next?.length) { + run.push(work); + continue; + } + await this.writeRun(run); + run = []; + await this.applyChain(work, 0); + } + await this.writeRun(run, tail); + } + + /** Works of one group each, packed into batches. */ + private async writeRun(todo: readonly Work[], tail?: Tail): Promise { const batches = this.pack(todo); if (batches.length === 0) { if (tail) await this.applyTail(tail); @@ -243,7 +286,37 @@ export class BatchWriter { if (tail) await this.applyTail(tail); } + /** + * An item's chain of groups, each in a batch of its own and so committed + * whole or not at all; the item's state and `applied` go with the last. + */ + private async applyChain(work: Work, attempt: number): Promise { + const groups = chainOf(work.group).filter((g) => hasWrites(g.ops)); + const results: OpResult[] = []; + for (let i = 0; i < groups.length; i++) { + const last = i === groups.length - 1; + const stated = last ? this.stage([work]) : { staged: [], tail: undefined }; + let result; + try { + result = await this.send(buildBatch([groups[i]], stated.tail?.op())); + } finally { + this.unstage(stated.staged); + } + if (!result.ok) { + // The groups before it are written: planned again from them, never sent again as they are. + await this.retryAlone(work, result.reason, result.message, attempt); + return; + } + results.push(...result.results.slice(0, groups[i].ops.length)); + if (last) { + await work.applied?.(results); + stated.tail?.applied(); + } + } + } + private async applyAlone(work: Work, attempt: number): Promise { + if (work.group.next?.length) return this.applyChain(work, attempt); if (!hasWrites(work.group.ops)) return; const stated = this.stage([work]); let result; diff --git a/src/device-sync/engine/calendar-sync.ts b/src/device-sync/engine/calendar-sync.ts index 12dcb233..6fecd598 100644 --- a/src/device-sync/engine/calendar-sync.ts +++ b/src/device-sync/engine/calendar-sync.ts @@ -10,13 +10,13 @@ import type { CalendarContext, CalendarEventWire, CalendarLike, CalendarPlanner, import { CALENDAR_AUTHORITY, type ReminderOwner, type Row } from '../types'; import { CALENDAR_EVENT_PROPERTIES, CALENDAR_PROPERTIES } from '../wire'; import { calendarAddresses, type CalendarAddresses, type CapabilityAccount } from '../jmap/session'; -import type { Work } from './batch'; +import { chainHasWrites, type Work } from './batch'; import type { RunEnv } from './context'; import type { SubscriptionCalendar } from './deps'; import { ItemSync, type Pending } from './item-sync'; import { accountOfRef, idInAccount, refOf, type Held, type Kind, type ServerObject } from './kinds'; import { poisonFingerprint } from './poison'; -import { flag, hasWrites, num, str } from './provider'; +import { flag, num, str } from './provider'; import { isCollectionSelected } from './selection'; import { accountOf, type SyncState } from './sync-state'; @@ -501,7 +501,7 @@ export class CalendarSync extends ItemSync { const held: Held = { kind: this.eventKind, local: moved }; try { const work = this.acceptWork(acct, { held, meta: this.eventKind.meta(moved), fingerprint: '', actions: [] }, server, false); - if (hasWrites(work.group.ops)) return [work]; + if (chainHasWrites(work.group)) return [work]; // The pair's ops already wrote what the server holds. this.env.report.stats.uploaded.updated++; return []; diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index c5856617..ce7947f5 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -16,7 +16,7 @@ import { type SetResponse, } from '../jmap/caller'; import { isMethodError } from '../jmap/errors'; -import { concatGroups, prependOps, type Work } from './batch'; +import { appendToLast, chainHasWrites, concatGroups, prependOps, type Work } from './batch'; import type { RunEnv } from './context'; import { RunAbort, StateMismatch } from './errors'; import { @@ -687,7 +687,7 @@ export abstract class ItemSync { ): Work | null { const ref = refOf(acct, object.id); const plan = kind.planDownload(object, local ?? null, acct); - if (plan.effect === 'none' || !hasWrites(plan.ops.ops)) { + if (plan.effect === 'none' || !chainHasWrites(plan.ops)) { if (local) this.onEcho(kind, acct, object); if (!local || kind.meta(local).dirty) return null; const heal = kind.planBaselineHeal(local); @@ -1405,7 +1405,8 @@ export abstract class ItemSync { const { kind, local } = p.held; const plan = kind.planAccepted(local, server, acct); const extra = p.meta.poison ? [poisonOp(kind, p.meta.rowId, null)] : []; - const withExtra = (group: OpGroup): OpGroup => (extra.length ? { ref: group.ref, ops: [...group.ops, ...extra] } : group); + // With the item's last group: a big one comes in several (OpGroup.next). + const withExtra = (group: OpGroup): OpGroup => appendToLast(group, extra); const counted = () => { if (created) this.env.report.stats.uploaded.created++; else this.env.report.stats.uploaded.updated++; diff --git a/src/device-sync/planner.ts b/src/device-sync/planner.ts index 97de9ef4..65f8a659 100644 --- a/src/device-sync/planner.ts +++ b/src/device-sync/planner.ts @@ -25,6 +25,15 @@ export interface OpGroup { /** For logs and reports: SOURCE_ID/_SYNC_ID or `row:<_id>`. */ ref: string; ops: ProviderOp[]; + /** + * Further groups of the same item, for one too big for a provider + * transaction (a big meeting): the engine applies `[this, ...next]` in + * order, each atomically in a batch of its own, and counts the item once + * the last one is in. When one fails, the item is read and planned again + * from what the earlier ones wrote, so a planner orders them for that: a + * new row's identity first, a master's shadow last. They have no `next`. + */ + next?: OpGroup[]; } /** Why an item is left alone, with the fingerprint that lifts the back-off when the item changes. */ From 9439a4ffc0ed5517ef2b8dccbfcea6dd9b289a0c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:56:12 +0200 Subject: [PATCH 081/334] fix: keep an occurrence's colour cleared, and report reminders that can't go #34 Clearing the colour of one occurrence of a coloured series removed the occurrence's own colour, so it showed the series' colour again after the sync. The occurrence now gets an empty colour, which Stalwart keeps and which shows as the calendar's colour, as on the phone. Removing every reminder of one occurrence sent an empty set of alerts, which Stalwart does not store: the occurrence then showed the series' reminders again, without a word. When the series uses the calendar's default reminders, the occurrence leaves the defaults and has none, as before. When the series has reminders of its own, the occurrence would show them whatever is sent, so the change is put back and the report says why (`remindersNotRepresentable`). Checked against calcard 0.3.14, with planner tests and runs through the engine and the fake server. --- .../__tests__/calendar/engine.test.ts | 47 ++++++++++++ .../__tests__/calendar/exceptions.test.ts | 75 +++++++++++++++++++ src/device-sync/__tests__/calendar/harness.ts | 5 ++ src/device-sync/calendar/planner.ts | 5 ++ src/device-sync/calendar/upload.ts | 46 ++++++++---- 5 files changed, 162 insertions(+), 16 deletions(-) diff --git a/src/device-sync/__tests__/calendar/engine.test.ts b/src/device-sync/__tests__/calendar/engine.test.ts index 10418fd6..8e30e46b 100644 --- a/src/device-sync/__tests__/calendar/engine.test.ts +++ b/src/device-sync/__tests__/calendar/engine.test.ts @@ -379,6 +379,53 @@ describe('calendar device edits through the engine', () => { expect(h.server.all('CalendarEvent', 'team')).toEqual([]); }); + describe('an occurrence\'s own colour and reminders removed on the device', () => { + async function standup(extra: Record, override: Record) { + const h = real(); + const id = h.server.addEvent('a', { + uid: 'standup-uid', + title: 'Standup', + start: '2026-09-28T09:00:00', + duration: 'PT15M', + timeZone: 'Europe/Berlin', + recurrenceRule: { '@type': 'RecurrenceRule', frequency: 'daily', count: 5 }, + recurrenceOverrides: { '2026-09-29T09:00:00': { title: 'Standup (late)', ...override } }, + calendarIds: { [h.calendar]: true }, + ...extra, + }); + await h.run(CALENDAR_AUTHORITY); + const exception = () => h.events().find((e) => e[Events.ORIGINAL_SYNC_ID] === `a/${id}`)!; + const minutes = () => h.device.rows('reminders').filter((r) => Number(r[Reminders.EVENT_ID]) === Number(exception()._id)).map((r) => Number(r[Reminders.MINUTES])); + return { h, id, exception, minutes }; + } + + it('keeps a colour cleared on one occurrence cleared', async () => { + const { h, id, exception } = await standup({ color: 'steelblue' }, { color: 'red' }); + + h.device.user.updateEvent(Number(exception()._id), { [Events.EVENT_COLOR]: null }); + expect(await h.run(CALENDAR_AUTHORITY)).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { updated: 1 } } }); + + expect(exception()).toMatchObject({ [Events.EVENT_COLOR]: null, [Events.DIRTY]: 0 }); + expect((h.server.get('CalendarEvent', 'a', id)!.recurrenceOverrides as Overrides)['2026-09-29T09:00:00']).toMatchObject({ color: '' }); + }); + + it('puts back the removal of every reminder of an occurrence whose series shows its own, and reports it', async () => { + const alert = { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT15M' }, action: 'display' }; + const { h, id, exception, minutes } = await standup({ alerts: { al1: alert } }, {}); + expect(minutes()).toEqual([15]); + const sets = () => h.server.requests.filter((r) => r.methods.includes('CalendarEvent/set')).length; + const before = sets(); + + h.device.user.setReminders(Number(exception()._id), []); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report.itemErrors).toEqual([expect.objectContaining({ ref: `a/${id}`, side: 'upload', type: 'remindersNotRepresentable' })]); + expect(sets()).toBe(before); + expect(minutes()).toEqual([15]); + expect(exception()[Events.DIRTY]).toBe(0); + }); + }); + it('keeps a description and location cleared on one occurrence cleared', async () => { const h = real(); const id = h.server.addEvent('a', { diff --git a/src/device-sync/__tests__/calendar/exceptions.test.ts b/src/device-sync/__tests__/calendar/exceptions.test.ts index 2163d4c1..da473d36 100644 --- a/src/device-sync/__tests__/calendar/exceptions.test.ts +++ b/src/device-sync/__tests__/calendar/exceptions.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from 'vitest'; import { Attendees, Events, Reminders } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; +import { cssColorToArgb } from '../../calendar/values'; import type { LocalEvent, UploadPlan } from '../../planner'; import type { CalendarEventWire } from '../../wire'; import { ACCT, Harness, ME, serverApply, single, utc, weekly } from './harness'; @@ -194,6 +195,80 @@ describe('calendar planner: recurrence edits made on the device', () => { expect(patch['recurrenceOverrides/2026-10-14T09:00:00']).toMatchObject({ useDefaultAlerts: false }); }); + it('clears an occurrence\'s own colour instead of letting the series\' come back', async () => { + const event = weekly(); + event.recurrenceOverrides!['2026-10-07T09:00:00'] = { ...event.recurrenceOverrides!['2026-10-07T09:00:00'], color: 'red' }; + const { h } = await synced(event); + const x = (await h.local('bl'))!.exceptions[0]; + expect(h.row(x.eventId)[Events.EVENT_COLOR]).toBe(cssColorToArgb('red')); + h.fake.user.updateEvent(x.eventId, { [Events.EVENT_COLOR]: null }); + const local = (await h.local('bl'))!; + + const plan = calendarPlanner.planUpload(local, h.ctx); + + // Without a colour of its own the occurrence would show the series' steelblue again: an empty one, which Stalwart keeps. + expect(patchOf(plan).patch).toEqual({ 'recurrenceOverrides/2026-10-07T09:00:00/color': '' }); + const server = await accept(h, event, local, plan); + expect(h.row(x.eventId)).toMatchObject({ [Events.EVENT_COLOR]: null, [Events.DIRTY]: 0 }); + expect(calendarPlanner.planDownload(server, (await h.local('bl'))!, h.ctx).effect).toBe('none'); + + // A series without a colour has none to show: the occurrence's own is removed. + const plain = weekly(); + delete plain.color; + plain.recurrenceOverrides!['2026-10-07T09:00:00'] = { ...plain.recurrenceOverrides!['2026-10-07T09:00:00'], color: 'red' }; + const p = await synced(plain); + const px = (await p.h.local('bl'))!.exceptions[0]; + p.h.fake.user.updateEvent(px.eventId, { [Events.EVENT_COLOR]: null }); + expect(patchOf(calendarPlanner.planUpload((await p.h.local('bl'))!, p.h.ctx)).patch).toEqual({ 'recurrenceOverrides/2026-10-07T09:00:00/color': null }); + }); + + it('removes every reminder of one occurrence of a series on the calendar\'s defaults', async () => { + const event = weekly(); + delete event.alerts; + event.useDefaultAlerts = true; + event.recurrenceOverrides!['2026-10-07T09:00:00'] = { + ...event.recurrenceOverrides!['2026-10-07T09:00:00'], + useDefaultAlerts: false, + alerts: { a60: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT1H', relativeTo: 'start' }, action: 'display' } }, + }; + const { h } = await synced(event); + const x = (await h.local('bl'))!.exceptions[0]; + h.fake.user.setReminders(x.eventId, []); + const local = (await h.local('bl'))!; + + const plan = calendarPlanner.planUpload(local, h.ctx); + const server = await accept(h, event, local, plan); + + // Stalwart drops the empty map, but the series has no alerts of its own to show instead. + expect(server.recurrenceOverrides!['2026-10-07T09:00:00']).toMatchObject({ useDefaultAlerts: false }); + expect(server.recurrenceOverrides!['2026-10-07T09:00:00']).not.toHaveProperty('alerts'); + expect(h.fake.rows('reminders', `${Reminders.EVENT_ID} = ?`, [x.eventId])).toEqual([]); + expect(h.row(x.eventId)[Events.DIRTY]).toBe(0); + expect(calendarPlanner.planDownload(server, (await h.local('bl'))!, h.ctx).effect).toBe('none'); + }); + + it('puts back and reports the removal of every reminder of one occurrence whose series would show its own again', async () => { + const event = weekly(); + event.alerts = { al1: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT15M', relativeTo: 'start' }, action: 'display' } }; + const { h } = await synced(event); + const minutes = (eventId: number) => h.fake.rows('reminders', `${Reminders.EVENT_ID} = ?`, [eventId]).map((r) => Number(r[Reminders.MINUTES])); + const x = (await h.local('bl'))!.exceptions[0]; + expect(minutes(x.eventId)).toEqual([15]); + h.fake.user.setReminders(x.eventId, []); + + // An override without alerts shows the series' (Stalwart writes no VALARM for an empty map): none can't be stored. + const plan = calendarPlanner.planUpload((await h.local('bl'))!, h.ctx); + expect(plan).toMatchObject({ kind: 'revert', reason: 'remindersNotRepresentable' }); + if (plan.kind === 'revert') await h.apply(plan.ops); + expect(minutes(x.eventId)).toEqual([15]); + expect(h.row(x.eventId)[Events.DIRTY]).toBe(0); + + // With another edit, that one goes up alone. + h.fake.user.setReminders(x.eventId, []); + h.fake.user.updateEvent(x.eventId, { [Events.TITLE]: 'Moved again' }); + expect(patchOf(calendarPlanner.planUpload((await h.local('bl'))!, h.ctx)).patch).toEqual({ 'recurrenceOverrides/2026-10-07T09:00:00/title': 'Moved again' }); + }); + it('patches an existing override one level deep, never deeper', async () => { const { h } = await synced(weekly()); const x = (await h.local('bl'))!.exceptions[0]; diff --git a/src/device-sync/__tests__/calendar/harness.ts b/src/device-sync/__tests__/calendar/harness.ts index 1ec0873c..b073e75b 100644 --- a/src/device-sync/__tests__/calendar/harness.ts +++ b/src/device-sync/__tests__/calendar/harness.ts @@ -169,6 +169,11 @@ export function serverApply(event: CalendarEventWire, patch: PatchObject): Calen for (const forbidden of ['@type', 'method', 'organizerCalendarAddress', 'privacy', 'prodId', 'recurrenceId', 'recurrenceIdTimeZone', 'sentBy', 'uid', 'recurrenceRule', 'recurrenceOverrides']) { delete (value as Record)[forbidden]; } + // An override is stored as its own VEVENT: an empty map writes nothing, so it reads back without it. + for (const map of ['participants', 'alerts', 'locations']) { + const inner = (value as Record)[map]; + if (inner && typeof inner === 'object' && !Object.keys(inner).length) delete (value as Record)[map]; + } } if (!Object.keys(overrides).length) delete next.recurrenceOverrides; } diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index 4ee3b26b..ac65d474 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -331,6 +331,11 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { const ops = revertGroup(local, ctx); if (ops) return { kind: 'revert', ops, reason: SKIP.crossAccountMove }; } + // Every reminder of an occurrence removed where the server can't store none: back to what it holds, reported. + if (computation.dropped.includes('reminders')) { + const ops = revertGroup(local, ctx); + if (ops) return { kind: 'revert', ops, reason: SKIP.remindersNotRepresentable }; + } return { kind: 'clean', ops: cleanGroup(local, computation) }; } const action: UploadAction = { kind: 'update', id: ref.id, patch: computation.patch }; diff --git a/src/device-sync/calendar/upload.ts b/src/device-sync/calendar/upload.ts index f39210cc..73d63a73 100644 --- a/src/device-sync/calendar/upload.ts +++ b/src/device-sync/calendar/upload.ts @@ -50,6 +50,7 @@ import { } from './exceptions'; import { eventImage, plainInstanceImage } from './image'; import { isRemovedInstance } from './merge'; +import { reminderRows } from './reminders'; import { isRuleRepresentable, partsToRule, ruleToRRule, rruleParts, untilUtc, type RuleZone } from './rrule'; import { NOTIFYING_UNITS, sendsSchedulingMessages } from './scheduling'; import { fixedOffsetTiming, rowTiming, type RowTiming } from './timing'; @@ -90,6 +91,8 @@ export const SKIP = { rsvpRefused: 'rsvpRefused', /** Moved in place to a calendar of another JMAP account: no patch can do that. */ crossAccountMove: 'crossAccountMove', + /** Every reminder removed from one occurrence of a series with alerts of its own: the occurrence would show those. */ + remindersNotRepresentable: 'remindersNotRepresentable', } as const; export class SkipUpload extends Error { @@ -373,6 +376,7 @@ export function computeUpdate(local: LocalEvent, ctx: CalendarContext): UploadCo if (!ruleRemoved && shadow.recurrenceRule) { for (const x of local.exceptions) { const units = exceptionChanges(x, local, shadow, calendarId, zone, overrides, removedExceptions, ctx, lossy); + if (units.dropped) dropped.push(...units.dropped); if (units.units.size) { exceptionUnits.set(x.eventId, units.units); overridesChanged = true; @@ -423,7 +427,7 @@ function exceptionChanges( removed: Set, ctx: CalendarContext, lossy: boolean, -): { units: Set; rsvp: boolean; others?: boolean } { +): { units: Set; rsvp: boolean; others?: boolean; dropped?: string[] } { const units = new Set(); const key = x.recurrenceId; if (!key) return { units, rsvp: false }; @@ -478,7 +482,7 @@ function exceptionChanges( values.freeBusyStatus = availabilityFromDevice(cells[Events.AVAILABILITY]); break; case 'color': - values.color = argbToCss(cells[Events.EVENT_COLOR]); + values.color = argbToCss(cells[Events.EVENT_COLOR]) ?? clearedInOverride('color', instance, shadow); break; case 'timing': { const moved = columnUnitDiffers('timing', cur, reference); @@ -519,22 +523,30 @@ function exceptionChanges( units.add('attendees'); } } + let dropped: string[] | undefined; if (ctx.reminderOwner === 'device' && (existing || cur.reminders!.length) && remindersDiffer(cur, reference)) { - values.alerts = reminderChanges(instance, ctx.calendar(calendarId), cur.reminders!, ctx.mintKey).alerts; - // An occurrence on the calendar's defaults ignores its own alerts: it leaves them, as a series does - // (Stalwart keeps the override's `useDefaultAlerts` as a JSPROP; the series stays on the defaults). - if (instance.useDefaultAlerts) values.useDefaultAlerts = false; - units.add('reminders'); + const alerts = reminderChanges(instance, ctx.calendar(calendarId), cur.reminders!, ctx.mintKey).alerts; + if (!Object.keys(alerts).length && reminderRows(shadow.alerts).length) { + // An override is stored as a VEVENT of its own and an empty map writes no VALARM, so an override without + // alerts shows the series' again: none at all can't be stored while the series has alerts of its own. + dropped = ['reminders']; + } else { + values.alerts = alerts; + // An occurrence on the calendar's defaults ignores its own alerts: it leaves them, as a series does + // (Stalwart keeps the override's `useDefaultAlerts` as a JSPROP; the series stays on the defaults). + if (instance.useDefaultAlerts) values.useDefaultAlerts = false; + units.add('reminders'); + } } if (existing) { if (units.size) work.props.set(key, { ...(work.props.get(key) ?? {}), ...values }); - return { units, rsvp, others }; + return { units, rsvp, others, dropped }; } // A new override: nothing but its pinned time is still an override (an app saving the instance unchanged). if (!units.size) units.add('new'); work.whole.set(key, newOverride(shadow, key, values)); - return { units, rsvp, others }; + return { units, rsvp, others, dropped }; } /** Units a new exception row inherits from its series when it leaves them empty. */ @@ -547,15 +559,17 @@ function isEmptyUnit(unit: string, cells: Row): boolean { } /** - * A description or location cleared on an existing override. An override - * without one of its own shows the series' again, so while the series has - * one the override gets the empty value Stalwart keeps: the empty text, or a - * location without a name (an empty map writes no LOCATION line and reads - * back as none of its own). Null, a clean removal, when nothing would come - * back. + * A description, location or colour cleared on an existing override. An + * override without one of its own shows the series' again, so while the + * series has one the override gets the empty value Stalwart keeps: the empty + * text or colour (an empty DESCRIPTION or COLOR; an empty colour is none, the + * calendar's, on the device), or a location without a name (an empty map + * writes no LOCATION line and reads back as none of its own). Null, a clean + * removal, when nothing would come back. */ -function clearedInOverride(unit: 'description' | 'location', instance: CalendarEventWire, master: CalendarEventWire): unknown { +function clearedInOverride(unit: 'description' | 'location' | 'color', instance: CalendarEventWire, master: CalendarEventWire): unknown { if (unit === 'description') return typeof master.description === 'string' && master.description ? '' : null; + if (unit === 'color') return typeof master.color === 'string' && master.color ? '' : null; const series = Object.keys(master.locations ?? {}); if (!series.length) return null; const key = Object.keys(instance.locations ?? {})[0] ?? series[0]; From 3ab131dbe85ca56a0d6568abd18f696bc70aef60 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:57:06 +0200 Subject: [PATCH 082/334] fix: keep the last sync's result when an upload sync finds nothing to do #34 Android starts an upload-only sync right after a manual sync, and 30 s after any app write. When nothing waited on the device it ended at once, but it still stored its status, so the settings showed a plain "synced" and lost the conflicts and item errors of the run just before it. Such a run did nothing and now leaves the stored status alone. --- .../__tests__/engine/contacts-flow.test.ts | 19 +++++++++++++++++- src/device-sync/engine/run.ts | 20 ++++++++++++++++--- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/device-sync/__tests__/engine/contacts-flow.test.ts b/src/device-sync/__tests__/engine/contacts-flow.test.ts index d786b20e..54c7dfa2 100644 --- a/src/device-sync/__tests__/engine/contacts-flow.test.ts +++ b/src/device-sync/__tests__/engine/contacts-flow.test.ts @@ -2,7 +2,7 @@ // server: first sync, incremental /changes, uploads, echoes, selection // changes and groups (docs/device-sync.md, "A sync run"). -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { Data, MimeType } from '../../android-columns'; import { runDeviceSync } from '../../engine/run'; import { @@ -295,6 +295,23 @@ describe('device sync engine: contacts', () => { expect(h.server.requests.length).toBe(requests); }); + it("keeps the last run's status (its conflicts and item errors) when an upload-only sync finds nothing to do", async () => { + const h = createHarness(); + addServerCards(h, ['Ada']); + await h.run(); + const recordStatus = vi.mocked(h.deps.recordStatus); + const recorded = recordStatus.mock.calls.length; + + // The upload sync Android starts right after a manual one: nothing waits on the device. + expect((await h.run(CONTACTS_AUTHORITY, { upload: true })).outcome).toBe('ok'); + + expect(recordStatus.mock.calls.length).toBe(recorded); + // One that has something to upload is a run like any other. + renameDeviceContact(h, h.contactNamed('Ada')!.id, 'Ada King'); + await h.run(CONTACTS_AUTHORITY, { upload: true }); + expect(recordStatus.mock.calls.length).toBe(recorded + 1); + }); + it('reports disabled, unsupported and rows of another owner without touching anything', async () => { const off = createHarness(); off.deps.isSyncEnabled = async () => false; diff --git a/src/device-sync/engine/run.ts b/src/device-sync/engine/run.ts index 883be9c5..4fb8c348 100644 --- a/src/device-sync/engine/run.ts +++ b/src/device-sync/engine/run.ts @@ -157,15 +157,19 @@ export async function runDeviceSync(payload: RunPayload, deps: EngineDeps): Prom const key = lockKey(payload.registryId, payload.authority); const release = await acquireLock(key, Math.max(0, payload.deadline - deps.now() - tuning.deadlineMarginMs)); let result: RunReport; + const run = { idle: false }; if (!release) { result = report.build('cancelled', deps.now(), { message: 'Another sync of this account is still running', moreRecordsToGet: true }); } else { try { - result = await runLocked(payload, deps, tuning, report, key); + result = await runLocked(payload, deps, tuning, report, key, run); } finally { release(); } } + // An upload sync with nothing to upload (Android starts one right after a manual sync) did nothing: + // the last run's status, with its conflicts and item errors, stays what the settings show. + if (run.idle) return result; try { await deps.recordStatus(payload.registryId, payload.authority, statusOf(result, report.itemErrorCount)); } catch (error) { @@ -174,7 +178,14 @@ export async function runDeviceSync(payload: RunPayload, deps: EngineDeps): Prom return result; } -async function runLocked(payload: RunPayload, deps: EngineDeps, tuning: Tuning, report: ReportBuilder, key: string): Promise { +async function runLocked( + payload: RunPayload, + deps: EngineDeps, + tuning: Tuning, + report: ReportBuilder, + key: string, + run: { idle: boolean }, +): Promise { const finish = (outcome: RunReport['outcome'], extra: Parameters[2] = {}) => report.build(outcome, deps.now(), extra); const extras = payload.extras ?? {}; @@ -189,7 +200,10 @@ async function runLocked(payload: RunPayload, deps: EngineDeps, tuning: Tuning, return finish('internal', { message: 'The rows on this device were written for another account' }); } // Providers schedule an upload sync for every account 30 s after any app write. - if (extras.upload && !(await hasLocalWork(reader, payload.authority, stored))) return finish('ok'); + if (extras.upload && !(await hasLocalWork(reader, payload.authority, stored))) { + run.idle = true; + return finish('ok'); + } const connection = await deps.jmap(payload.registryId); if (stored.owner && stored.owner.origin !== connection.origin) { From 117823f746e50dd63b3a4c08bd132a848d2d37f9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:58:01 +0200 Subject: [PATCH 083/334] test: make the device sync fakes as strict as Android and Stalwart #34 The fake providers and the fake server were more lenient than the real ones, so three bugs showed only on a device: an event inserted without a status, small contact photos, and meetings too big for one provider transaction. The fake CalendarProvider leaves STATUS NULL when an app writes none. The fake ContactsProvider keeps a photo within the 96 px thumbnail as the thumbnail alone, without a PHOTO_FILE_ID, and serves it to readPhoto from there. A batch fails with tooLarge when it weighs more than one Binder transaction, a photo counted by its bytes. The fake server drops empty maps inside recurrence overrides, as Stalwart does. Tests now read photos the way the engine does, and the helpers that made up for the old fakes are gone. --- .../__tests__/calendar/big-events.test.ts | 24 ++---- .../__tests__/contacts/download.test.ts | 4 +- src/device-sync/__tests__/contacts/harness.ts | 44 +---------- .../__tests__/contacts/lifecycle.test.ts | 7 +- .../__tests__/contacts/real-engine.test.ts | 3 - .../__tests__/fake-jmap-server.test.ts | 23 ++++++ .../__tests__/fake-provider.test.ts | 39 ++++++++- .../__tests__/fakes/fake-jmap-server.ts | 2 + .../__tests__/fakes/fake-provider.ts | 79 +++++++++++++++++-- 9 files changed, 148 insertions(+), 77 deletions(-) diff --git a/src/device-sync/__tests__/calendar/big-events.test.ts b/src/device-sync/__tests__/calendar/big-events.test.ts index 0b1942ba..6075134c 100644 --- a/src/device-sync/__tests__/calendar/big-events.test.ts +++ b/src/device-sync/__tests__/calendar/big-events.test.ts @@ -5,9 +5,8 @@ import { describe, expect, it } from 'vitest'; import { Attendees, Events } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; -import { estimateBatchBytes } from '../../engine/batch'; import type { OpGroup } from '../../planner'; -import { CALENDAR_AUTHORITY, type ProviderPort } from '../../types'; +import { CALENDAR_AUTHORITY } from '../../types'; import type { CalendarEventWire } from '../../wire'; import { createHarness, rowWrites, type Harness as EngineHarness } from '../engine/harness'; import { Harness, ME, serverApply } from './harness'; @@ -47,23 +46,10 @@ function meeting(attendees: number, overrides: number, me = ME, calendarId = 'b' } as unknown as CalendarEventWire; } -/** The engine with the real planners, its provider refusing a batch bigger than one Binder transaction (as Android does). */ -function engineWithBinderLimit(): EngineHarness { +/** The engine with the real planners (the fake provider refuses a batch over one Binder transaction, as Android does). */ +function engineWithRealPlanners(): EngineHarness { const h = createHarness(); h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; - const base = h.deps.provider; - h.deps.provider = (name, authority): ProviderPort => { - const port = base(name, authority); - return { - accountName: port.accountName, - authority: port.authority, - query: (q) => port.query(q), - readSyncState: () => port.readSyncState(), - readPhoto: (id, px) => port.readPhoto(id, px), - applyBatch: async (ops) => - estimateBatchBytes(ops) > TRANSACTION ? { ok: false, reason: 'tooLarge', message: 'TransactionTooLargeException' } : port.applyBatch(ops), - }; - }; return h; } @@ -204,7 +190,7 @@ describe('device sync engine: big meetings', () => { [25, 52], [100, 12], ])('downloads a meeting of %i attendees on %i overrides in several transactions', async (attendees, overrides) => { - const h = engineWithBinderLimit(); + const h = engineWithRealPlanners(); const { id: _drop, ...event } = meeting(attendees, overrides, 'alice@example.com', h.calendar) as unknown as Record; const id = h.server.addEvent('a', event); @@ -219,7 +205,7 @@ describe('device sync engine: big meetings', () => { it('finishes a big meeting cut off between its groups by a crash, without duplicating rows', async () => { const setup = () => { - const h = engineWithBinderLimit(); + const h = engineWithRealPlanners(); const { id: _drop, ...event } = meeting(25, 52, 'alice@example.com', h.calendar) as unknown as Record; return { h, id: h.server.addEvent('a', event) }; }; diff --git a/src/device-sync/__tests__/contacts/download.test.ts b/src/device-sync/__tests__/contacts/download.test.ts index 4fd6e639..40c02b7f 100644 --- a/src/device-sync/__tests__/contacts/download.test.ts +++ b/src/device-sync/__tests__/contacts/download.test.ts @@ -52,7 +52,9 @@ describe('contacts download: golden rows', () => { expect(cells(rows['relatedTo:urn:uuid:other-person'], TYPED)).toEqual({ data1: 'Other Person', data2: '6', data3: null }); expect(cells(rows['notes:n1'], ['data1'])).toEqual({ data1: 'first note' }); expect(rows['media:ph'].data_sync2).toBe(photoHash(JPEG)); - expect(h.device.photo(Number(rows['media:ph'].data14))).toBe(JPEG); + // A 1×1 photo is kept as the thumbnail alone (no PHOTO_FILE_ID), and read from it. + expect(rows['media:ph'].data14).toBeNull(); + expect((await h.port.readPhoto(rawId, 512))?.jpegBase64).toBe(JPEG); const raw = h.rawContact(rawId); expect(raw).toMatchObject({ diff --git a/src/device-sync/__tests__/contacts/harness.ts b/src/device-sync/__tests__/contacts/harness.ts index 8705ef99..544aa89a 100644 --- a/src/device-sync/__tests__/contacts/harness.ts +++ b/src/device-sync/__tests__/contacts/harness.ts @@ -4,7 +4,7 @@ * way the engine applies them (one op group per batch); uploads go through * the fake server's patch handling and come back through its normalisation. */ -import { Data, GroupMembership, Groups, RawContacts } from '../../android-columns'; +import { GroupMembership, Groups, RawContacts } from '../../android-columns'; import { makeKeyMinter, parseObjectRef, uuidFrom } from '../../common/ids'; import { contactsPlanner } from '../../contacts/planner'; import type { @@ -16,7 +16,7 @@ import type { UploadAction, UploadPlan, } from '../../planner'; -import { CONTACTS_AUTHORITY, type BatchResult, type OpResult, type ProviderOp, type ProviderPort, type Row } from '../../types'; +import { CONTACTS_AUTHORITY, type BatchResult, type ProviderPort, type Row } from '../../types'; import { FakeJmapServer } from '../fakes/fake-jmap-server'; import { FakeDeviceProviders } from '../fakes/fake-provider'; @@ -34,40 +34,6 @@ function rowsOf(res: { columns: string[]; rows: unknown[][] }): Row[] { return res.rows.map((cells) => Object.fromEntries(res.columns.map((c, i) => [c, cells[i]])) as Row); } -/** - * ContactsProvider keeps a photo no larger than its 96 px thumbnail as the - * thumbnail only: PHOTO_FILE_ID (DATA14) stays NULL, and the thumbnail - * (DATA15) does not read back. The fake stores every photo with a display - * photo; this makes the photo rows a batch wrote thumbnail-only, as a device - * does with the test fixtures' 1×1 JPEGs. - */ -export function keepThumbnailsOnly(device: FakeDeviceProviders, ops: ProviderOp[], results: OpResult[]): void { - ops.forEach((op, i) => { - if ((op.op !== 'insert' && op.op !== 'update') || op.table !== 'data') return; - const bytes = op.values[Data.DATA15]; - if (!bytes || typeof bytes !== 'object') return; - const id = op.op === 'insert' ? results[i]?.id : op.id; - const row = id === undefined ? undefined : device.table('data').get(id); - if (row) row[Data.DATA14] = null; - }); -} - -/** A provider port whose photo writes come out thumbnail-only (see `keepThumbnailsOnly`). */ -export function thumbnailOnlyPort(device: FakeDeviceProviders, port: ProviderPort): ProviderPort { - return { - accountName: port.accountName, - authority: port.authority, - query: (q) => port.query(q), - readSyncState: () => port.readSyncState(), - readPhoto: (id, px) => port.readPhoto(id, px), - applyBatch: async (ops) => { - const res = await port.applyBatch(ops); - if (res.ok) keepThumbnailsOnly(device, ops, res.results); - return res; - }, - }; -} - export class Harness { readonly device = new FakeDeviceProviders(); readonly port: ProviderPort = this.device.port(ACCOUNT, CONTACTS_AUTHORITY); @@ -82,8 +48,6 @@ export class Harness { readonly unselected = new Set(); /** When false the context has no `groupsOf` (memberships not synced). */ groupIndex = true; - /** When true the photos the planner writes come out thumbnail-only (see `keepThumbnailsOnly`). */ - thumbnailPhotos = false; readonly ctx: ContactsContext; constructor() { @@ -133,9 +97,7 @@ export class Harness { async apply(group: OpGroup): Promise { if (!group.ops.length) return { ok: true, results: [] }; - const res = await this.port.applyBatch(group.ops); - if (res.ok && this.thumbnailPhotos) keepThumbnailsOnly(this.device, group.ops, res.results); - return res; + return this.port.applyBatch(group.ops); } async applyOk(group: OpGroup): Promise { diff --git a/src/device-sync/__tests__/contacts/lifecycle.test.ts b/src/device-sync/__tests__/contacts/lifecycle.test.ts index 8d66eff4..e8583e32 100644 --- a/src/device-sync/__tests__/contacts/lifecycle.test.ts +++ b/src/device-sync/__tests__/contacts/lifecycle.test.ts @@ -80,8 +80,7 @@ describe('contacts lifecycle', () => { const h = new Harness(); h.blobs.set('B1', JPEG); const { id, rawId } = await h.seed({ name: { full: 'Blob Photo' }, media: { p: { kind: 'photo', blobId: 'B1', mediaType: 'image/jpeg' } } }); - const photo = h.rowsByKey(rawId)['media:p']; - expect(h.device.photo(Number(photo.data14))).toBe(JPEG); + expect((await h.port.readPhoto(rawId, 512))?.jpegBase64).toBe(JPEG); expect((await h.contact(rawId)).shadow?.media).toEqual({ p: { kind: 'photo', blobId: 'B1', mediaType: 'image/jpeg' } }); await expectSettled(h, id, rawId); }); @@ -164,7 +163,7 @@ describe('contacts lifecycle', () => { h.blobs.set('B1', JPEG); h.server.serverUpdate('ContactCard', JMAP, id, { 'emails/e1/address': 'c@example.org' }); await h.download(id); - expect(h.device.photo(Number(h.rowsByKey(rawId)['media:p'].data14))).toBe(JPEG); + expect((await h.port.readPhoto(rawId, 512))?.jpegBase64).toBe(JPEG); await expectSettled(h, id, rawId); }); @@ -188,7 +187,6 @@ describe('contacts lifecycle', () => { it('keeps one row through downloads and merges, and never reads it as deleted', async () => { const h = new Harness(); - h.thumbnailPhotos = true; const { id, rawId } = await h.seed(small()); expect(photos(h, rawId)).toMatchObject([{ data14: null, data_sync1: 'media:ph' }]); await expectSettled(h, id, rawId); @@ -220,7 +218,6 @@ describe('contacts lifecycle', () => { it('heals the photo rows the same photo was inserted as again, keeping one and the server photo', async () => { const h = new Harness(); - h.thumbnailPhotos = true; const { id, rawId } = await h.seed(small()); const [row] = photos(h, rawId); const again = { [Data.MIMETYPE]: MimeType.PHOTO, [Data.DATA15]: { b64: JPEG }, data_sync1: row.data_sync1, data_sync2: row.data_sync2, data_sync3: row.data_sync3 }; diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index d39e6bb4..3e1ee1c3 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -9,7 +9,6 @@ import { contactsPlanner } from '../../contacts/planner'; import { addServerCards, ANDROID_ACCOUNT, CONTACTS_AUTHORITY, createHarness, rowWrites, type Harness } from '../engine/harness'; import type { SetTarget } from '../fakes/fake-jmap-server'; import { JPEG } from './fixtures'; -import { thumbnailOnlyPort } from './harness'; function real(options: Parameters[0] = {}): Harness { const h = createHarness(options); @@ -119,8 +118,6 @@ describe('contacts through the engine', () => { it('keeps one row for a small photo the provider stores as its thumbnail, and never deletes it on the server', async () => { const h = real(); - const base = h.deps.provider; - h.deps.provider = (name, authority) => thumbnailOnlyPort(h.device, base(name, authority)); const media = { ph: { kind: 'photo', uri: `data:image/jpeg;base64,${JPEG}`, mediaType: 'image/jpeg' } }; const card = h.server.addCard('a', { uid: 'u-small', name: { full: 'Photo Smalltest' }, notes: { n1: { note: 'v1' } }, addressBookIds: { [h.book]: true }, media }); expect((await h.run()).outcome).toBe('ok'); diff --git a/src/device-sync/__tests__/fake-jmap-server.test.ts b/src/device-sync/__tests__/fake-jmap-server.test.ts index ceae4551..426830f4 100644 --- a/src/device-sync/__tests__/fake-jmap-server.test.ts +++ b/src/device-sync/__tests__/fake-jmap-server.test.ts @@ -429,6 +429,29 @@ describe('FakeJmapServer', () => { expect(server.get('CalendarEvent', 'a', id)?.updated).toBe('2026-09-27T10:02:00Z'); }); + it('drops empty maps inside overrides too, which an override VEVENT cannot hold', async () => { + const { server, port, cal } = setup(); + const id = server.addEvent('a', { + title: 'Standup', + start: '2026-09-28T09:00:00', + recurrenceRule: { frequency: 'daily' }, + alerts: { al1: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT15M' } } }, + recurrenceOverrides: { '2026-09-29T09:00:00': { title: 'Late', alerts: { a2: { '@type': 'Alert', trigger: { '@type': 'OffsetTrigger', offset: '-PT1H' } } } } }, + calendarIds: { [cal]: true }, + }); + + await call(port, 'CalendarEvent/set', { + accountId: 'a', + update: { [id]: { 'recurrenceOverrides/2026-09-29T09:00:00/alerts': {}, 'recurrenceOverrides/2026-09-29T09:00:00/locations': {} } }, + }); + + // Read back without them, so the occurrence shows the series' alerts again, as on Stalwart. + const override = (server.get('CalendarEvent', 'a', id)?.recurrenceOverrides as Record>)['2026-09-29T09:00:00']; + expect(override).toMatchObject({ title: 'Late' }); + expect(override).not.toHaveProperty('alerts'); + expect(override).not.toHaveProperty('locations'); + }); + it('leaves useDefaultAlerts out of properties: null and returns it when listed', async () => { const { server, port, cal } = setup(); const id = server.addEvent('a', { title: 'x', useDefaultAlerts: true, calendarIds: { [cal]: true } }); diff --git a/src/device-sync/__tests__/fake-provider.test.ts b/src/device-sync/__tests__/fake-provider.test.ts index 98803ef9..41afcc47 100644 --- a/src/device-sync/__tests__/fake-provider.test.ts +++ b/src/device-sync/__tests__/fake-provider.test.ts @@ -10,6 +10,7 @@ import { StructuredPostal, } from '../android-columns'; import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, type ProviderOp } from '../types'; +import { JPEG } from './contacts/fixtures'; import { FakeDeviceProviders, aospDurationValid, aospRruleValid } from './fakes/fake-provider'; import { compileWhere } from './fakes/fake-sql'; @@ -129,13 +130,13 @@ describe('FakeDeviceProviders: contacts', () => { }); it('refuses 500 contacts ops without a yield point and oversized batches', async () => { - const fake = new FakeDeviceProviders({ maxBatchBytes: 50_000 }); + const fake = new FakeDeviceProviders({ maxBatchBytes: 500_000 }); const port = fake.port(ME, CONTACTS_AUTHORITY); const ops: ProviderOp[] = Array.from({ length: 500 }, () => ({ op: 'insert', table: 'raw_contacts', values: {} }) as ProviderOp); expect(await port.applyBatch(ops)).toMatchObject({ ok: false, reason: 'provider' }); ops[250] = { ...ops[250], yieldAllowed: true }; expect((await port.applyBatch(ops)).ok).toBe(true); - const big = await port.applyBatch([{ op: 'insert', table: 'raw_contacts', values: { [RawContacts.SYNC2]: 'x'.repeat(60_000) } }]); + const big = await port.applyBatch([{ op: 'insert', table: 'raw_contacts', values: { [RawContacts.SYNC2]: 'x'.repeat(300_000) } }]); expect(big).toMatchObject({ ok: false, reason: 'tooLarge' }); }); @@ -166,6 +167,31 @@ describe('FakeDeviceProviders: contacts', () => { expect(rows.rows[0]).toEqual([String(photo!.fileId), null]); }); + it('keeps a photo within the 96 px thumbnail as the thumbnail alone, and serves that to readPhoto', async () => { + const fake = new FakeDeviceProviders(); + const { port, rawId } = await contactWithEmail(fake); + const res = await port.applyBatch([ + { op: 'insert', table: 'data', values: { [Data.RAW_CONTACT_ID]: rawId, [Data.MIMETYPE]: MimeType.PHOTO, [Data.DATA15]: { b64: JPEG } } }, + ]); + expect(res.ok).toBe(true); + const rows = await port.query({ table: 'data', columns: [Data.DATA14, Data.DATA15], where: `${Data.MIMETYPE} = ?`, args: [MimeType.PHOTO] }); + // No PHOTO_FILE_ID for a 1×1 JPEG, and the thumbnail does not read back. + expect(rows.rows[0]).toEqual([null, null]); + expect(await port.readPhoto(rawId, 512)).toEqual({ jpegBase64: JPEG, fileId: null }); + }); + + it('weighs a batch as its Binder transaction does: two bytes per character of its JSON, a photo by its bytes', async () => { + const fake = new FakeDeviceProviders(); + const { port, rawId } = await contactWithEmail(fake); + // 600,000 characters of text are about 1.2 MB in the Parcel; the same number of base64 characters are 450 KB of photo. + const text = await port.applyBatch([{ op: 'update', table: 'raw_contacts', id: rawId, values: { [RawContacts.SYNC2]: 'x'.repeat(600_000) } }]); + expect(text).toMatchObject({ ok: false, reason: 'tooLarge' }); + const photo = await port.applyBatch([ + { op: 'insert', table: 'data', values: { [Data.RAW_CONTACT_ID]: rawId, [Data.MIMETYPE]: MimeType.PHOTO, [Data.DATA15]: { b64: 'A'.repeat(600_000) } } }, + ]); + expect(photo.ok).toBe(true); + }); + it('models Fossify saves: rows re-inserted without DATA_SYNC', async () => { const fake = new FakeDeviceProviders(); const { rawId, dataId } = await contactWithEmail(fake); @@ -246,6 +272,15 @@ describe('FakeDeviceProviders: calendar', () => { expect(fake.row('events', exceptionId)).toBeDefined(); }); + it('leaves STATUS NULL on an event an app inserts without one (the column has no default)', async () => { + const fake = new FakeDeviceProviders(); + const { calendarId } = await calendar(fake); + const id = fake.user.insertEvent(calendarId, { [Events.DTSTART]: 0, [Events.DTEND]: 1000, [Events.EVENT_TIMEZONE]: 'UTC', [Events.TITLE]: 'x' }); + expect(fake.row('events', id)![Events.STATUS]).toBeNull(); + // Unlike AVAILABILITY and ACCESS_LEVEL (NOT NULL DEFAULT 0). + expect(fake.row('events', id)).toMatchObject({ [Events.AVAILABILITY]: 0, [Events.ACCESS_LEVEL]: 0 }); + }); + it('hard-deletes app-deleted events without _SYNC_ID and soft-deletes synced ones', async () => { const fake = new FakeDeviceProviders(); const { calendarId } = await calendar(fake); diff --git a/src/device-sync/__tests__/fakes/fake-jmap-server.ts b/src/device-sync/__tests__/fakes/fake-jmap-server.ts index 015a161d..892f4c7f 100644 --- a/src/device-sync/__tests__/fakes/fake-jmap-server.ts +++ b/src/device-sync/__tests__/fakes/fake-jmap-server.ts @@ -965,6 +965,8 @@ function normalizeEvent(input: Obj, now: string, mode: 'jmap' | 'server'): Obj { } privacy = Math.max(privacy, PRIVACY_RANK.indexOf(String(value.privacy))); for (const forbidden of FORBIDDEN_OVERRIDE_PROPERTIES) delete value[forbidden]; + // Stored as a VEVENT of its own: an empty map writes nothing there either. + dropEmptyMaps(value, EVENT_MAPS); if (Object.keys(value).length > 0) { normalizeEventTimes(value); addNestedTypes(value); diff --git a/src/device-sync/__tests__/fakes/fake-provider.ts b/src/device-sync/__tests__/fakes/fake-provider.ts index 100d5229..17c238f6 100644 --- a/src/device-sync/__tests__/fakes/fake-provider.ts +++ b/src/device-sync/__tests__/fakes/fake-provider.ts @@ -25,6 +25,12 @@ * owner, all-day times are zeroed in UTC and `PS` all-day durations become * `PD`, while `PT0S`-style all-day durations, NULL STATUS updates, * SELF_ATTENDEE_STATUS updates and RRULEs with unknown parts fail the batch. + * STATUS has no default: an event an app inserts without one holds NULL. + * - Photos, as PhotoProcessor keeps them: one within the 96 px thumbnail is + * the thumbnail alone (PHOTO_FILE_ID NULL), a bigger one (or bytes that are + * no JPEG or PNG) a display photo with a file id; DATA15 never reads back. + * - A batch over one Binder transaction (`estimateBatchBytes` over 1 MB, by + * default) fails with `tooLarge`. * - Scoping: rows of other accounts are invisible and untouchable. * - The native bridge's rules (android/.../sync/ProviderOps.kt and * ProviderScope.kt): an op with `id` expects exactly one row unless it says @@ -74,6 +80,7 @@ import { type WriteCell, type WriteRow, } from '../../types'; +import { estimateBatchBytes } from '../../engine/batch'; import { compileOrderBy, compileWhere, whereColumns } from './fake-sql'; export const FAKE_ACCOUNT_TYPE = 'com.anonymous.bulwarkmobile.account'; @@ -240,7 +247,7 @@ export interface FakeProviderOptions { accountType?: string; /** Derive/split names and addresses like ContactsProvider (default true). */ normalize?: boolean; - /** Byte budget of one applyBatch before it fails with `tooLarge` (default 1 MB). */ + /** Bytes of one applyBatch, as `estimateBatchBytes` weighs them, before it fails with `tooLarge` (default 1 MB). */ maxBatchBytes?: number; } @@ -517,7 +524,7 @@ export class FakeDeviceProviders { record(false); return { ok: false, reason, message }; } - if (JSON.stringify(ops).length > this.maxBatchBytes) { + if (parcelBytes(ops) > this.maxBatchBytes) { record(false); return { ok: false, reason: 'tooLarge', message: 'TransactionTooLargeException' }; } @@ -569,6 +576,26 @@ export class FakeDeviceProviders { } } +/** + * What a batch weighs in its Binder transaction: `estimateBatchBytes`, with a + * photo blob counted by its bytes (the native side hands ContentValues a + * decoded `byte[]`, not the base64 text). + */ +function parcelBytes(ops: readonly ProviderOp[]): number { + let blobs = 0; + const texts = ops.map((op) => { + if (op.op !== 'insert' && op.op !== 'update') return op; + const values = { ...op.values }; + for (const [column, value] of Object.entries(values)) { + if (value === null || typeof value !== 'object') continue; + blobs += Math.floor((value.b64.length * 3) / 4); + values[column] = null; + } + return { ...op, values }; + }); + return estimateBatchBytes(texts) + blobs; +} + function cloneTables(tables: Tables): Tables { const out = {} as Tables; for (const [name, rows] of Object.entries(tables) as Array<[ProviderTable, RowMap]>) { @@ -642,8 +669,11 @@ class FakePort implements ProviderPort { } for (const row of this.fake.table('data').values()) { if (row[Data.RAW_CONTACT_ID] === rawContactId && row[Data.MIMETYPE] === MimeType.PHOTO) { - const fileId = row[Data.DATA14] === null ? null : Number(row[Data.DATA14]); - const b64 = fileId === null ? undefined : this.fake.photo(fileId); + const fileId = row[Data.DATA14] === null || row[Data.DATA14] === undefined ? null : Number(row[Data.DATA14]); + // A photo kept as its thumbnail alone is read from the thumbnail. + const thumbnail = row[Data.DATA15]; + if (fileId === null) return typeof thumbnail === 'string' && thumbnail.startsWith('blob:') ? { jpegBase64: thumbnail.slice(5), fileId: null } : null; + const b64 = this.fake.photo(fileId); return b64 === undefined ? null : { jpegBase64: b64, fileId }; } } @@ -755,10 +785,19 @@ class BatchApplier { return out; } - /** A photo row's full-size bytes become a stored display photo plus a file id, like PhotoProcessor. */ + /** + * A photo row's full-size bytes, as PhotoProcessor keeps them: within the + * 96 px thumbnail as the thumbnail alone (PHOTO_FILE_ID NULL, the bytes kept + * for readPhoto), else as a stored display photo plus a file id. + */ private processPhoto(row: StoredRow): void { const v = row[Data.DATA15]; if (typeof v === 'string' && v.startsWith('blob:')) { + const size = imageSize(v.slice(5)); + if (size && Math.max(size.width, size.height) <= THUMBNAIL_PX) { + row[Data.DATA14] = null; + return; + } row[Data.DATA14] = this.fake.storePhoto(v.slice(5)); row[Data.DATA15] = 'thumbnail'; } @@ -996,6 +1035,34 @@ class BatchApplier { const defaults = (d: StoredRow): StoredRow => ({ ...d }); +/** ContactsProvider's thumbnail size: a photo no bigger is kept as the thumbnail alone. */ +const THUMBNAIL_PX = 96; + +/** The pixel size of JPEG (its SOFn segment) or PNG (its IHDR chunk) bytes; null for anything else. */ +function imageSize(b64: string): { width: number; height: number } | null { + let bytes: Uint8Array; + try { + bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)); + } catch { + return null; + } + const u16 = (i: number) => (bytes[i] << 8) | bytes[i + 1]; + const u32 = (i: number) => ((bytes[i] << 24) >>> 0) + (bytes[i + 1] << 16) + (bytes[i + 2] << 8) + bytes[i + 3]; + if (bytes.length >= 24 && bytes[0] === 0x89 && String.fromCharCode(...bytes.slice(12, 16)) === 'IHDR') { + return { width: u32(16), height: u32(20) }; + } + if (bytes[0] !== 0xff || bytes[1] !== 0xd8) return null; + for (let i = 2; i + 9 < bytes.length; ) { + if (bytes[i] !== 0xff) return null; + const marker = bytes[i + 1]; + if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) { + return { height: u16(i + 5), width: u16(i + 7) }; + } + i += 2 + u16(i + 2); + } + return null; +} + const RAW_CONTACT_DEFAULTS: StoredRow = { [RawContacts.SOURCE_ID]: null, [RawContacts.VERSION]: 1, @@ -1045,7 +1112,7 @@ const EVENT_DEFAULTS: StoredRow = { [Events.GUESTS_CAN_INVITE_OTHERS]: 1, [Events.GUESTS_CAN_SEE_GUESTS]: 1, [Events.ALL_DAY]: 0, - [Events.STATUS]: 0, + // No default for STATUS (`eventStatus INTEGER`): an app that writes none leaves NULL. }; /** From 66789251e3635fb7a2527c386a48f3713875e95e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:58:56 +0200 Subject: [PATCH 084/334] fix: keep a meeting moved in Etar the same event instead of cancelling and re-inviting #34 Etar moves an event by inserting a copy from its editor and deleting the old row. Its editor keeps the user's own attendee row out of the attendee list, so the copy of a meeting that lists the user has no row for the user, and the copy of one that does not gets the user as its organizer. The pairing compared the attendee addresses of both rows exactly, so a moved meeting never paired: it was deleted on the server and created again under a new uid, the other attendees' answers were lost, and everyone got a cancellation and a new invitation. The pairing now leaves the user's own addresses out of that comparison. Anyone else still has to be on both rows, so a copy that lost or gained another attendee is not taken for the same event. Checked with planner tests for both kinds of meeting and a run through the engine with a fake Etar move. --- .../__tests__/calendar/lifecycle.test.ts | 50 +++++++++++++++++++ .../__tests__/engine/real-planners.test.ts | 41 +++++++++++++++ .../__tests__/fakes/fake-provider.ts | 46 +++++++++++++++++ src/device-sync/calendar/pairs.ts | 18 +++++-- src/device-sync/calendar/planner.ts | 2 +- 5 files changed, 152 insertions(+), 5 deletions(-) diff --git a/src/device-sync/__tests__/calendar/lifecycle.test.ts b/src/device-sync/__tests__/calendar/lifecycle.test.ts index 52546c45..038d2d00 100644 --- a/src/device-sync/__tests__/calendar/lifecycle.test.ts +++ b/src/device-sync/__tests__/calendar/lifecycle.test.ts @@ -492,6 +492,56 @@ describe('calendar planner: pairs', () => { expect(calendarPlanner.planUpload({ ...after, dirty: true }, h.ctx).kind).toBe('clean'); }); + describe("Etar's move of a meeting (its copy leaves the user's own row out, or adds the user as organizer)", () => { + const emailsOf = (h: Harness, eventId: number) => + h.fake.rows('attendees', `${Attendees.EVENT_ID} = ?`, [eventId]).map((a) => String(a[Attendees.ATTENDEE_EMAIL])).sort(); + const person = (address: string, extra: Record = {}) => ({ '@type': 'Participant', calendarAddress: `mailto:${address}`, roles: { attendee: true }, ...extra }); + const moveToWork = { kind: 'update', id: 'e1', patch: { 'calendarIds/b': null, 'calendarIds/w': true } }; + + it('pairs a meeting that lists the user', async () => { + const participants = { + me: person(ME, { roles: { owner: true, attendee: true }, participationStatus: 'accepted' }), + bob: person('bob@example.net', { name: 'Bob', participationStatus: 'accepted' }), + } as CalendarEventWire['participants']; + const { h, id } = await synced(single({ participants, organizerCalendarAddress: `mailto:${ME}` })); + + const moved = h.fake.user.etarMove(id, h.rowIds.get('w')!); + + expect(emailsOf(h, moved)).toEqual(['bob@example.net']); + expect((await pairAndApply(h)).actions).toEqual([moveToWork]); + expect((await h.local('e1'))!).toMatchObject({ eventId: moved, dirty: false }); + }); + + it('pairs a meeting that does not list the user', async () => { + const participants = { + boss: person('boss@example.net', { roles: { owner: true, attendee: true }, participationStatus: 'accepted' }), + bob: person('bob@example.net', { participationStatus: 'needs-action' }), + } as CalendarEventWire['participants']; + const { h, id } = await synced(single({ participants, organizerCalendarAddress: 'mailto:boss@example.net' })); + + const moved = h.fake.user.etarMove(id, h.rowIds.get('w')!); + + expect(emailsOf(h, moved)).toEqual(['bob@example.net', 'boss@example.net', ME]); + expect((await pairAndApply(h)).actions).toEqual([moveToWork]); + }); + + it('never pairs a copy that lost or gained someone else', async () => { + const participants = { + me: person(ME, { roles: { owner: true, attendee: true }, participationStatus: 'accepted' }), + bob: person('bob@example.net'), + carol: person('carol@example.net'), + } as CalendarEventWire['participants']; + const attendee = (email: string) => ({ [Attendees.ATTENDEE_EMAIL]: email, [Attendees.ATTENDEE_RELATIONSHIP]: 1, [Attendees.ATTENDEE_TYPE]: 1, [Attendees.ATTENDEE_STATUS]: 0 }); + for (const copied of [['bob@example.net'], ['bob@example.net', 'carol@example.net', 'dave@example.net']]) { + const { h, id } = await synced(single({ participants, organizerCalendarAddress: `mailto:${ME}` })); + h.fake.user.deleteEvent(id); + h.fake.user.insertEvent(h.rowIds.get('w')!, lunchColumns, { attendees: copied.map(attendee) }); + const events = await h.events(); + expect(calendarPlanner.planPairs(events.filter((e) => e.deleted), events.filter((e) => !e.syncId), h.ctx), copied.join()).toEqual([]); + } + }); + }); + it('keeps the timing of a series turned single against the series, so a start moved before uploads', async () => { const { h, id } = await synced(single({ recurrenceRule: { frequency: 'daily' } })); // Moved an hour later in place, then turned into a single event (Etar: delete + insert). diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 1f95bb6c..1f466ac9 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -697,6 +697,47 @@ describe('device sync engine with the real planners', () => { expect(rowWrites(h.batches.log.slice(before))).toEqual([]); }); + it('keeps a meeting Etar moved the same event, without cancelling and inviting anyone again', async () => { + const h = real(); + const work = h.server.addCalendar('a', { name: 'Work' }); + const lunch = h.server.addEvent('a', { + uid: 'lunch-uid', + title: 'Lunch', + start: '2026-10-06T12:00:00', + duration: 'PT1H', + timeZone: 'Europe/Berlin', + organizerCalendarAddress: 'mailto:alice@example.com', + participants: { + me: { '@type': 'Participant', calendarAddress: 'mailto:alice@example.com', roles: { owner: true, attendee: true }, participationStatus: 'accepted' }, + bob: { '@type': 'Participant', calendarAddress: 'mailto:bob@example.com', name: 'Bob', roles: { attendee: true, optional: true }, participationStatus: 'accepted' }, + }, + calendarIds: { [h.calendar]: true }, + }); + await h.run(CALENDAR_AUTHORITY); + const old = h.events().find((e) => e._sync_id === `a/${lunch}`)!; + const workRow = Number(h.device.rows('calendars').find((c) => c._sync_id === `a/${work}`)!._id); + + // What Etar really inserts: Bob as a required attendee without an answer, and no row for the user. + const moved = h.device.user.etarMove(Number(old._id), workRow); + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, updated: 1, deleted: 0 } } }); + expect(h.server.all('CalendarEvent', 'a')).toHaveLength(1); + expect(h.server.get('CalendarEvent', 'a', lunch)).toMatchObject({ + uid: 'lunch-uid', + calendarIds: { [work]: true }, + participants: { bob: { roles: { attendee: true, optional: true }, participationStatus: 'accepted' } }, + }); + expect(h.server.scheduling).toEqual([]); + // The rows show the meeting as the server holds it again: the user's own row and Bob's answer. + const attendees = h.device.rows('attendees').filter((a) => Number(a.event_id) === moved); + expect(attendees.map((a) => [a[Attendees.ATTENDEE_EMAIL], a[Attendees.ATTENDEE_STATUS], a[Attendees.ATTENDEE_TYPE]]).sort()).toEqual([ + ['alice@example.com', 1, 1], + ['bob@example.com', 1, 2], + ]); + expect(h.events()).toEqual([expect.objectContaining({ _id: moved, _sync_id: `a/${lunch}`, dirty: 0 })]); + }); + it('uploads a series Etar turned into a single event as the removal of its rule', async () => { const h = real(); const series = h.server.addEvent('a', { diff --git a/src/device-sync/__tests__/fakes/fake-provider.ts b/src/device-sync/__tests__/fakes/fake-provider.ts index 17c238f6..8e1fa716 100644 --- a/src/device-sync/__tests__/fakes/fake-provider.ts +++ b/src/device-sync/__tests__/fakes/fake-provider.ts @@ -45,6 +45,9 @@ * Pure TypeScript, for vitest only. */ import { + AttendeeRelationship, + AttendeeStatus, + AttendeeType, Attendees, Calendars, Colors, @@ -1448,4 +1451,47 @@ export class FakeUser { for (const a of attendees) this.fake.table('attendees').set(this.fake.allocId(), { ...a, event_id: eventId }); this.setReminders(eventId, reminders); } + + /** + * Etar's move to another calendar (EditEventHelper.saveEvent → moveEventToCalendar): a copy of the + * event from its editor model is inserted there, then the event is deleted. The model leaves the + * owner's own attendee row out (EditEventFragment: ATTENDEE_EMAIL equal to the calendar's + * OWNER_ACCOUNT, ignoring case), so the copy gets every other attendee as a required attendee + * without a status, and, when the owner had no row, the owner as its organizer. The reminders are + * copied. (Exceptions Etar never synced, which it moves too on API 30+, are not modelled.) + */ + etarMove(eventId: number, calendarId: number, changes: Row = {}): number { + const old = this.event(eventId); + const owner = String(this.fake.table('calendars').get(Number(old[Events.CALENDAR_ID]))?.[Calendars.OWNER_ACCOUNT] ?? ''); + const isOwner = (a: StoredRow) => String(a[Attendees.ATTENDEE_EMAIL] ?? '').toLowerCase() === owner.toLowerCase(); + const values: Row = {}; + for (const c of ETAR_COPY) if (old[c] !== undefined) values[c] = old[c]; + const rows = [...this.fake.table('attendees').values()].filter((a) => a.event_id === eventId); + const others: Row[] = rows.filter((a) => !isOwner(a)).map((a) => ({ + [Attendees.ATTENDEE_NAME]: a[Attendees.ATTENDEE_NAME] ?? null, + [Attendees.ATTENDEE_EMAIL]: a[Attendees.ATTENDEE_EMAIL], + [Attendees.ATTENDEE_RELATIONSHIP]: AttendeeRelationship.ATTENDEE, + [Attendees.ATTENDEE_TYPE]: AttendeeType.REQUIRED, + [Attendees.ATTENDEE_STATUS]: AttendeeStatus.NONE, + })); + const organizer: Row[] = rows.some(isOwner) || !others.length ? [] : [{ + [Attendees.ATTENDEE_EMAIL]: owner, + [Attendees.ATTENDEE_RELATIONSHIP]: AttendeeRelationship.ORGANIZER, + [Attendees.ATTENDEE_TYPE]: AttendeeType.REQUIRED, + [Attendees.ATTENDEE_STATUS]: AttendeeStatus.ACCEPTED, + }]; + const reminders = [...this.fake.table('reminders').values()] + .filter((r) => r.event_id === eventId) + .map((r) => ({ [Reminders.MINUTES]: r[Reminders.MINUTES], [Reminders.METHOD]: r[Reminders.METHOD] })); + const id = this.insertEvent(calendarId, { ...values, ...changes }, { attendees: [...organizer, ...others], reminders }); + this.deleteEvent(eventId); + return id; + } } + +/** The event columns Etar's editor model writes into the copy of an event it moves (getContentValuesFromModel). */ +const ETAR_COPY = [ + Events.TITLE, Events.DESCRIPTION, Events.EVENT_LOCATION, Events.STATUS, Events.AVAILABILITY, Events.ACCESS_LEVEL, + Events.EVENT_COLOR, Events.DTSTART, Events.DTEND, Events.DURATION, Events.EVENT_TIMEZONE, Events.ALL_DAY, Events.RRULE, + Events.EXDATE, +]; diff --git a/src/device-sync/calendar/pairs.ts b/src/device-sync/calendar/pairs.ts index 1788bfcb..267066f6 100644 --- a/src/device-sync/calendar/pairs.ts +++ b/src/device-sync/calendar/pairs.ts @@ -15,6 +15,7 @@ import type { CalendarEventWire } from '../wire'; import { exceptionRef, parseObjectRef } from '../common/ids'; import { clone } from '../common/json'; import { applyPatch, type PatchObject } from '../common/patch'; +import { isSelfAddress, type SelfContext } from './attendees'; import { EXCEPTION_CELLS, MASTER_CELLS, encodeBaseline, makeBaseline } from './columns'; import { durationTextSeconds } from './duration'; import { isExcluded, overridesOf } from './exceptions'; @@ -49,8 +50,17 @@ function lengthMs(cells: Row): number | null { return seconds === null ? null : seconds * 1000; } -const attendeeSet = (row: LocalEventRow) => - row.attendees.map((a) => String(a.cells[Attendees.ATTENDEE_EMAIL] ?? '').toLowerCase()).sort().join(','); +/** + * The attendees' addresses, the user's own left out: Etar's copy of a meeting + * has no row for the user when the user was listed (its editor keeps the + * owner's row out of its attendee list), and one as the organizer when not. + */ +const attendeeSet = (row: LocalEventRow, self: SelfContext) => + row.attendees + .map((a) => String(a.cells[Attendees.ATTENDEE_EMAIL] ?? '').toLowerCase()) + .filter((email) => !isSelfAddress(email, self)) + .sort() + .join(','); export interface PairKind { /** The same event in another calendar. */ @@ -60,7 +70,7 @@ export interface PairKind { } /** How a deleted and a new row are one edit, or null. */ -export function pairKind(deleted: LocalEvent, fresh: LocalEvent): PairKind | null { +export function pairKind(deleted: LocalEvent, fresh: LocalEvent, self: SelfContext): PairKind | null { for (const c of PAIR_COLUMNS) { // A copy inserted without STATUS (Google Calendar) says nothing about it. if (c === Events.STATUS && (isUnsetStatus(deleted.cells[c]) || isUnsetStatus(fresh.cells[c]))) continue; @@ -69,7 +79,7 @@ export function pairKind(deleted: LocalEvent, fresh: LocalEvent): PairKind | nul const allDay = Number(fresh.cells[Events.ALL_DAY] ?? 0) === 1; if (!allDay && String(deleted.cells[Events.EVENT_TIMEZONE] ?? '') !== String(fresh.cells[Events.EVENT_TIMEZONE] ?? '')) return null; if (lengthMs(deleted.cells) !== lengthMs(fresh.cells)) return null; - if (attendeeSet(deleted) !== attendeeSet(fresh)) return null; + if (attendeeSet(deleted, self) !== attendeeSet(fresh, self)) return null; const move = deleted.calendarRowId !== fresh.calendarRowId; const deletedRule = rruleParts(deleted.cells[Events.RRULE]); const freshRule = rruleParts(fresh.cells[Events.RRULE]); diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index ac65d474..7a01bbfd 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -391,7 +391,7 @@ function planPairs( refuseTask(d.shadow); for (const f of fresh) { if (used.has(f.eventId) || f.syncId !== null || f.pending || f.deleted || isExceptionRow(f.cells) || !inAccount(f)) continue; - const kind = pairKind(d, f); + const kind = pairKind(d, f, ctx); if (!kind) continue; const pair = pairPatch(d, f, kind, ctx); if (!pair) continue; From 029eba4562a22090cd046bd63f6af312bba8a128 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:59:53 +0200 Subject: [PATCH 085/334] fix: keep the last sync's result when a sync after it finds nothing to do #34 The settings show the stored status of the last sync. A sync that finds nothing to do still replaced it, so a change the server refused and the phone put back, or a conflict, was shown for a second or two and then gone. The previous fix covered only the upload sync Android starts right after a manual one. When changes arrive during a sync, the adapter runs a full sync right after it, which finds nothing new on the server and replaced the status 1-2 s after the real one; a manual sync right after another does the same. Now what counts is what a run did, not how it started. A run that wrote nothing either way and reported no conflict, item error, note or failure keeps the details of the run before it and moves only the time on. After a failed run it shows the sync as working again, without the failure's message. Any other run replaces the status, so a problem stays only until the next run that does something. --- .../__tests__/engine/contacts-flow.test.ts | 13 ++-- src/device-sync/__tests__/engine/harness.ts | 10 ++- .../__tests__/engine/internals.test.ts | 47 +++++++++++++- .../__tests__/engine/real-planners.test.ts | 65 +++++++++++++++++++ src/device-sync/__tests__/engine/task.test.ts | 14 +++- src/device-sync/engine/deps.ts | 2 + src/device-sync/engine/report.ts | 34 ++++++++++ src/device-sync/engine/run.ts | 52 +++++++-------- src/device-sync/task.ts | 6 +- 9 files changed, 206 insertions(+), 37 deletions(-) diff --git a/src/device-sync/__tests__/engine/contacts-flow.test.ts b/src/device-sync/__tests__/engine/contacts-flow.test.ts index 54c7dfa2..35db0a97 100644 --- a/src/device-sync/__tests__/engine/contacts-flow.test.ts +++ b/src/device-sync/__tests__/engine/contacts-flow.test.ts @@ -2,7 +2,7 @@ // server: first sync, incremental /changes, uploads, echoes, selection // changes and groups (docs/device-sync.md, "A sync run"). -import { describe, expect, it, vi } from 'vitest'; +import { describe, expect, it } from 'vitest'; import { Data, MimeType } from '../../android-columns'; import { runDeviceSync } from '../../engine/run'; import { @@ -295,21 +295,22 @@ describe('device sync engine: contacts', () => { expect(h.server.requests.length).toBe(requests); }); - it("keeps the last run's status (its conflicts and item errors) when an upload-only sync finds nothing to do", async () => { + it("keeps the last run's status when an upload-only sync finds nothing to do, moving only its time on", async () => { const h = createHarness(); addServerCards(h, ['Ada']); await h.run(); - const recordStatus = vi.mocked(h.deps.recordStatus); - const recorded = recordStatus.mock.calls.length; + const status = h.lastStatus()!; + expect(status).toMatchObject({ outcome: 'ok', stats: { downloaded: { created: 1 } } }); // The upload sync Android starts right after a manual one: nothing waits on the device. + h.clock.now += 1_000; expect((await h.run(CONTACTS_AUTHORITY, { upload: true })).outcome).toBe('ok'); - expect(recordStatus.mock.calls.length).toBe(recorded); + expect(h.lastStatus()).toEqual({ ...status, at: h.clock.now }); // One that has something to upload is a run like any other. renameDeviceContact(h, h.contactNamed('Ada')!.id, 'Ada King'); await h.run(CONTACTS_AUTHORITY, { upload: true }); - expect(recordStatus.mock.calls.length).toBe(recorded + 1); + expect(h.lastStatus()).toMatchObject({ stats: { downloaded: { created: 0 }, uploaded: { updated: 1 } } }); }); it('reports disabled, unsupported and rows of another owner without touching anything', async () => { diff --git a/src/device-sync/__tests__/engine/harness.ts b/src/device-sync/__tests__/engine/harness.ts index dc13849e..c86135c2 100644 --- a/src/device-sync/__tests__/engine/harness.ts +++ b/src/device-sync/__tests__/engine/harness.ts @@ -19,6 +19,7 @@ import { type ProviderPort, type RunExtras, type RunReport, + type RunStatus, type Row, } from '../../types'; import { FakeDeviceProviders } from '../fakes/fake-provider'; @@ -67,6 +68,8 @@ export interface Harness { nameDataRow(rawContactId: number): Row | undefined; events(): Row[]; serverNames(accountId?: string): string[]; + /** The status the runs recorded last for the settings (what `deps.lastStatus` answers). */ + lastStatus(authority?: Authority): RunStatus | undefined; } export function createHarness(options: { tuning?: Partial; accounts?: 'personal' | 'withShared'; rebuild?: boolean } = {}): Harness { @@ -84,6 +87,7 @@ export function createHarness(options: { tuning?: Partial; accounts?: 'p const prefs: AccountSyncPrefs = { contactsSelection: {}, calendarSelection: {}, reminderOwner: 'device' }; const checkpoints: Harness['checkpoints'] = { count: 0, crashAt: null }; const batches: Harness['batches'] = { applied: 0, crashAfter: null, log: [] }; + const statuses = new Map(); const wrap = (port: ProviderPort): ProviderPort => ({ accountName: port.accountName, @@ -119,7 +123,10 @@ export function createHarness(options: { tuning?: Partial; accounts?: 'p subscriptionCalendars: async () => [], isSyncEnabled: async () => true, deviceZone: () => 'Europe/Berlin', - recordStatus: vi.fn(), + recordStatus: vi.fn((_registryId: string, authority: Authority, status: RunStatus) => { + statuses.set(authority, status); + }), + lastStatus: (_registryId, authority) => statuses.get(authority), recordKnownState: vi.fn(), notifyAuthProblem: vi.fn(), yieldThread: async () => undefined, @@ -168,6 +175,7 @@ export function createHarness(options: { tuning?: Partial; accounts?: 'p .filter((c) => c.kind !== 'group') .map((c) => String((c.name as { full?: string } | undefined)?.full ?? '')) .sort(), + lastStatus: (authority = CONTACTS_AUTHORITY) => statuses.get(authority), }; return harness; } diff --git a/src/device-sync/__tests__/engine/internals.test.ts b/src/device-sync/__tests__/engine/internals.test.ts index 33323444..32c20531 100644 --- a/src/device-sync/__tests__/engine/internals.test.ts +++ b/src/device-sync/__tests__/engine/internals.test.ts @@ -7,13 +7,14 @@ import { BatchWriter, buildBatch, estimateBatchBytes, MAX_OPS_PER_YIELD, prepend import { classifyFailure, RunAbort, StateMismatch, StopRun } from '../../engine/errors'; import { acquireLock } from '../../engine/mutex'; import { isBackedOff, nextMarker } from '../../engine/poison'; +import { emptyStats, isQuietRun, statusToRecord } from '../../engine/report'; import { collectionDefaultOn, isCollectionSelected } from '../../engine/selection'; import { accountOf, emptySyncState, parseSyncState, serializeSyncState, StateStore, type SyncState } from '../../engine/sync-state'; import { classifySetError, existingIdFromUidError, JmapCaller } from '../../jmap/caller'; import { openWithAuthRebuild } from '../../jmap/connection'; import { JMAPMethodError } from '../../jmap/errors'; import { accountsWithCapability, calendarAddresses } from '../../jmap/session'; -import { JMAP_CALENDARS, JMAP_CONTACTS, type BatchResult, type ProviderOp, type ProviderPort } from '../../types'; +import { JMAP_CALENDARS, JMAP_CONTACTS, type BatchResult, type ProviderOp, type ProviderPort, type RunStats, type RunStatus } from '../../types'; import { FakeJmapServer } from '../fakes/fake-jmap-server'; function port(apply: (ops: ProviderOp[]) => BatchResult | Promise): ProviderPort & { sent: ProviderOp[][] } { @@ -459,3 +460,47 @@ describe('outcomes', () => { expect(classifyFailure(new TypeError('x is undefined'), 0)).toMatchObject({ outcome: 'internal' }); }); }); + +describe('the status a run leaves for the settings', () => { + const status = (over: Partial = {}): RunStatus => ({ at: 2_000, outcome: 'ok', durationMs: 50, conflicts: 0, itemErrors: 0, stats: emptyStats(), ...over }); + const withStats = (edit: (stats: RunStats) => void) => { + const stats = emptyStats(); + edit(stats); + return status({ stats }); + }; + const reported = status({ at: 1_000, durationMs: 900, conflicts: 1, itemErrors: 2, message: 'invitations not sent', stats: { ...emptyStats(), skipped: 2 } }); + + it('keeps what the run before reported through a run that did nothing, moving the time on', () => { + expect(isQuietRun(status())).toBe(true); + expect(statusToRecord(status(), reported)).toEqual({ ...reported, at: 2_000 }); + expect(statusToRecord(status(), undefined)).toEqual(status()); + }); + + it('shows a failure followed by a run that did nothing as recovered, with what the failed run reported', () => { + const failed = { ...reported, outcome: 'io' as const, message: 'Could not reach the server' }; + const recorded = statusToRecord(status(), failed); + + expect(recorded).toEqual({ at: 2_000, outcome: 'ok', durationMs: 900, conflicts: 1, itemErrors: 2, stats: reported.stats }); + }); + + it('replaces it with a run that wrote, skipped or reported anything, or failed', () => { + const busy = [ + withStats((s) => (s.downloaded.created = 1)), + withStats((s) => (s.downloaded.deleted = 1)), + withStats((s) => (s.uploaded.updated = 1)), + withStats((s) => (s.skipped = 1)), + status({ conflicts: 1 }), + status({ itemErrors: 1 }), + status({ message: 'invitations not sent' }), + status({ outcome: 'io', message: 'Could not reach the server' }), + status({ outcome: 'cancelled' }), + status({ outcome: 'disabled', message: 'Sync is off for this account' }), + ]; + for (const run of busy) { + expect(isQuietRun(run)).toBe(false); + expect(statusToRecord(run, reported)).toBe(run); + } + // Items only looked at are no work. + expect(isQuietRun(withStats((s) => (s.entries = 3)))).toBe(true); + }); +}); diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 1f466ac9..0652f345 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -452,6 +452,71 @@ describe('device sync engine with the real planners', () => { }); }); + describe('the last run status the settings show', () => { + /** A run that puts back an edit the server refuses: reported once, nothing waits afterwards. */ + async function refusedEdit() { + const h = real(); + const [ada] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper']); + await h.run(); + renameDeviceContact(h, h.contactNamed('Ada Lovelace')!.id, 'Ada King'); + h.server.setErrorFor('ContactCard', 'a', ada, { type: 'forbidden' }); + h.clock.now += 60_000; + expect((await h.run()).itemErrors).toEqual([expect.objectContaining({ type: 'forbidden' })]); + const status = h.lastStatus()!; + expect(status).toMatchObject({ outcome: 'ok', itemErrors: 1 }); + return { h, status }; + } + + it('keeps what a run reported through an upload sync after it that finds nothing to do', async () => { + const { h, status } = await refusedEdit(); + h.clock.now += 2_000; + + await h.run(CONTACTS_AUTHORITY, { upload: true }); + + expect(h.lastStatus()).toEqual({ ...status, at: expect.any(Number) }); + expect(h.lastStatus()!.at).toBeGreaterThan(status.at); + }); + + it('keeps what a run reported through a follow-up sync that finds nothing new on the server', async () => { + const { h, status } = await refusedEdit(); + h.clock.now += 2_000; + + expect((await h.run()).itemErrors).toEqual([]); + + expect(h.lastStatus()).toEqual({ ...status, at: expect.any(Number) }); + expect(h.lastStatus()!.at).toBeGreaterThan(status.at); + }); + + it('replaces it with a run that did something', async () => { + const { h } = await refusedEdit(); + addServerCards(h, ['Linus Torvalds']); + + await h.run(); + + expect(h.lastStatus()).toMatchObject({ outcome: 'ok', itemErrors: 0, stats: { downloaded: { created: 1 } } }); + }); + + it('shows a sync that finds nothing to do after a failed one as recovered', async () => { + const h = real(); + addServerCards(h, ['Ada Lovelace']); + await h.run(); + renameDeviceContact(h, h.contactNamed('Ada Lovelace')!.id, 'Ada King'); + h.server.failNextRequest('network', { match: 'ContactCard/set' }); + expect((await h.run()).outcome).toBe('io'); + expect(h.lastStatus()).toMatchObject({ outcome: 'io' }); + // Uploaded by the next sync; the one after it finds nothing to do. + expect((await h.run()).outcome).toBe('ok'); + h.clock.now += 2_000; + h.server.failNextRequest('network', { match: 'ContactCard/changes' }); + expect((await h.run()).outcome).toBe('io'); + + expect((await h.run()).outcome).toBe('ok'); + + expect(h.lastStatus()).toMatchObject({ outcome: 'ok', itemErrors: 0, conflicts: 0 }); + expect(h.lastStatus()).not.toHaveProperty('message'); + }); + }); + describe('a create whose target the server deleted after the claim', () => { it('claims a new contact again for a book that exists', async () => { const h = real(); diff --git a/src/device-sync/__tests__/engine/task.test.ts b/src/device-sync/__tests__/engine/task.test.ts index 384fc21c..5bc8adaa 100644 --- a/src/device-sync/__tests__/engine/task.test.ts +++ b/src/device-sync/__tests__/engine/task.test.ts @@ -15,7 +15,8 @@ vi.mock('../../native', () => ({ })); import * as native from '../../native'; -import { runDeviceSyncTask, teardownAuthority } from '../../task'; +import { emptyStats } from '../../engine/report'; +import { createTaskDeps, runDeviceSyncTask, teardownAuthority } from '../../task'; import { useDeviceSyncStore } from '../../../stores/device-sync-store'; import type { RunPayload } from '../../types'; @@ -42,6 +43,17 @@ describe('device sync task', () => { }); }); + it('reads back the status it records, for a run that did nothing to keep', async () => { + const deps = createTaskDeps(); + const status = { at: 5, outcome: 'ok' as const, durationMs: 3, conflicts: 1, itemErrors: 2, stats: emptyStats() }; + + await deps.recordStatus('bob@mail.example.com', 'com.android.calendar', status); + + await expect(deps.lastStatus('bob@mail.example.com', 'com.android.calendar')).resolves.toEqual(status); + await expect(deps.lastStatus('bob@mail.example.com', 'com.android.contacts')).resolves.toBeUndefined(); + await expect(deps.lastStatus('nobody@mail.example.com', 'com.android.calendar')).resolves.toBeUndefined(); + }); + it('reports a failure instead of throwing, and survives a refused finishRun', async () => { vi.mocked(native.getSyncSettings).mockRejectedValueOnce(new Error('module gone')); vi.mocked(native.finishRun).mockRejectedValueOnce(new Error('bridge down')); diff --git a/src/device-sync/engine/deps.ts b/src/device-sync/engine/deps.ts index 8f5bb227..28c9b98d 100644 --- a/src/device-sync/engine/deps.ts +++ b/src/device-sync/engine/deps.ts @@ -39,6 +39,8 @@ export interface EngineDeps { deviceZone(): string; /** The last run's status for the settings UI. */ recordStatus(registryId: string, authority: Authority, status: RunStatus): void | Promise; + /** The status `recordStatus` stored last, if any (a run that did nothing keeps its details). */ + lastStatus(registryId: string, authority: Authority): RunStatus | undefined | Promise; /** The latest state the engine synced for a JMAP account and type, so the app skips the echo of our own writes. */ recordKnownState(registryId: string, jmapAccountId: string, type: string, state: string): void | Promise; /** One notification that deep-links to sign-in, after an auth failure the rebuild did not fix. */ diff --git a/src/device-sync/engine/report.ts b/src/device-sync/engine/report.ts index 6f810607..b324e652 100644 --- a/src/device-sync/engine/report.ts +++ b/src/device-sync/engine/report.ts @@ -93,3 +93,37 @@ export function statusOf(report: RunReport, itemErrorCount = report.itemErrors.l if (report.message) status.message = report.message; return status; } + +const wrote =(counts: RunStats['downloaded']) => counts.created + counts.updated + counts.deleted > 0; + +/** A run that wrote nothing either way and had nothing to report: no conflict, item error, note or failure. */ +export function isQuietRun(status: RunStatus): boolean { + const { stats } = status; + return ( + status.outcome === 'ok' && + !status.conflicts && + !status.itemErrors && + !status.message && + !stats.skipped && + !wrote(stats.downloaded) && + !wrote(stats.uploaded) + ); +} + +/** + * The status the settings keep after a run. A quiet run (`isQuietRun`: the + * upload sync Android starts after a manual one, the follow-up sync the + * adapter asks for when changes arrived during a run, a manual sync right + * after an automatic one) keeps what the run before it reported, its + * conflicts, item errors and counts, and moves only the time on: otherwise + * a put-back edit or a conflict is reported for a second and gone. Its + * outcome is its own, so a failure before it shows as recovered, without the + * failure's message. Any other run replaces the status, so a problem stays + * only until the next run that does something. + */ +export function statusToRecord(status: RunStatus, previous: RunStatus | undefined): RunStatus { + if (!previous || !isQuietRun(status)) return status; + const kept: RunStatus = { ...previous, at: status.at, outcome: status.outcome }; + if (previous.outcome !== 'ok') delete kept.message; + return kept; +} diff --git a/src/device-sync/engine/run.ts b/src/device-sync/engine/run.ts index 4fb8c348..238d0aaa 100644 --- a/src/device-sync/engine/run.ts +++ b/src/device-sync/engine/run.ts @@ -16,6 +16,7 @@ import { type RunExtras, type RunPayload, type RunReport, + type RunStatus, } from '../types'; import { BatchWriter } from './batch'; import { CalendarSync } from './calendar-sync'; @@ -26,7 +27,7 @@ import { DEFAULT_TUNING, type EngineDeps, type Tuning } from './deps'; import { classifyFailure, RunAbort } from './errors'; import { acquireLock, lockKey, stopRequested } from './mutex'; import { ProviderReader, str } from './provider'; -import { ReportBuilder, statusOf } from './report'; +import { isQuietRun, ReportBuilder, statusOf, statusToRecord } from './report'; import { emptySyncState, parseSyncState, StateStore, type SyncState } from './sync-state'; import type { ItemSync } from './item-sync'; @@ -156,36 +157,36 @@ export async function runDeviceSync(payload: RunPayload, deps: EngineDeps): Prom const report = new ReportBuilder(payload.runId, payload.authority, deps.now(), tuning.maxItemErrors); const key = lockKey(payload.registryId, payload.authority); const release = await acquireLock(key, Math.max(0, payload.deadline - deps.now() - tuning.deadlineMarginMs)); - let result: RunReport; - const run = { idle: false }; if (!release) { - result = report.build('cancelled', deps.now(), { message: 'Another sync of this account is still running', moreRecordsToGet: true }); - } else { - try { - result = await runLocked(payload, deps, tuning, report, key, run); - } finally { - release(); - } + const result = report.build('cancelled', deps.now(), { message: 'Another sync of this account is still running', moreRecordsToGet: true }); + await recordStatus(payload, deps, statusOf(result, report.itemErrorCount)); + return result; } - // An upload sync with nothing to upload (Android starts one right after a manual sync) did nothing: - // the last run's status, with its conflicts and item errors, stays what the settings show. - if (run.idle) return result; try { - await deps.recordStatus(payload.registryId, payload.authority, statusOf(result, report.itemErrorCount)); + const result = await runLocked(payload, deps, tuning, report, key); + // Still under the lock: no other run of the account and authority records between the read and the write. + await recordStatus(payload, deps, statusOf(result, report.itemErrorCount)); + return result; + } finally { + release(); + } +} + +/** + * Stores the status the settings show. A run that did nothing (the upload + * sync Android starts after a manual one, the follow-up of a run during which + * changes arrived) keeps the details of the run before it (`statusToRecord`). + */ +async function recordStatus(payload: RunPayload, deps: EngineDeps, status: RunStatus): Promise { + try { + const previous = isQuietRun(status) ? await deps.lastStatus(payload.registryId, payload.authority) : undefined; + await deps.recordStatus(payload.registryId, payload.authority, statusToRecord(status, previous)); } catch (error) { deps.log?.('recording the run status failed', error); } - return result; } -async function runLocked( - payload: RunPayload, - deps: EngineDeps, - tuning: Tuning, - report: ReportBuilder, - key: string, - run: { idle: boolean }, -): Promise { +async function runLocked(payload: RunPayload, deps: EngineDeps, tuning: Tuning, report: ReportBuilder, key: string): Promise { const finish = (outcome: RunReport['outcome'], extra: Parameters[2] = {}) => report.build(outcome, deps.now(), extra); const extras = payload.extras ?? {}; @@ -200,10 +201,7 @@ async function runLocked( return finish('internal', { message: 'The rows on this device were written for another account' }); } // Providers schedule an upload sync for every account 30 s after any app write. - if (extras.upload && !(await hasLocalWork(reader, payload.authority, stored))) { - run.idle = true; - return finish('ok'); - } + if (extras.upload && !(await hasLocalWork(reader, payload.authority, stored))) return finish('ok'); const connection = await deps.jmap(payload.registryId); if (stored.owner && stored.owner.origin !== connection.origin) { diff --git a/src/device-sync/task.ts b/src/device-sync/task.ts index 4500875b..cb0f1009 100644 --- a/src/device-sync/task.ts +++ b/src/device-sync/task.ts @@ -16,7 +16,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { detectDeviceLocale, isSupportedLocale, translate, type LocaleCode } from '../i18n'; import { randomBytes } from '../lib/random'; -import { accountSyncPrefs, useDeviceSyncStore, waitForDeviceSyncHydration } from '../stores/device-sync-store'; +import { accountDeviceSync, accountSyncPrefs, useDeviceSyncStore, waitForDeviceSyncHydration } from '../stores/device-sync-store'; import type { RandomSource } from './common/ids'; import { runDeviceSync, @@ -124,6 +124,10 @@ export function createTaskDeps(): EngineDeps { await waitForDeviceSyncHydration(); useDeviceSyncStore.getState().recordRunStatus(registryId, authority, status); }, + lastStatus: async (registryId, authority) => { + await waitForDeviceSyncHydration(); + return accountDeviceSync(registryId).lastRun[authority]; + }, recordKnownState: async (registryId, jmapAccountId, type, state) => { await waitForDeviceSyncHydration(); useDeviceSyncStore.getState().recordKnownState(registryId, jmapAccountId, type, state); From 11b897177d718f88aa6f91cebbb237091f15294f Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:32:35 +0200 Subject: [PATCH 086/334] fix: keep mail push on servers without contacts or calendars #34 The push subscription got the contact and calendar types whenever the account had device sync on, without asking whether its server has contacts or calendars. Sync can be on for such an account: the settings list every signed-in account, and a server can drop a capability later. A server that refuses unknown types then refused the update and the replacement, and push setup failed: a new account got no mail push, an existing one lost it when its subscription expired. The types are now added only for a capability the account's session advertises, and a write the server refuses while it carries device sync's types is retried with EmailDelivery alone, so mail push never depends on device sync. Checked with the push setup tests against a fake server that refuses unknown types: a new subscription is created mail-only, an existing one is kept and refreshed, and another account's subscription is left alone when its server lacks the capability. --- src/lib/__tests__/push-notifications.test.ts | 115 +++++++++++++++++-- src/lib/push-notifications.ts | 95 ++++++++++----- 2 files changed, 172 insertions(+), 38 deletions(-) diff --git a/src/lib/__tests__/push-notifications.test.ts b/src/lib/__tests__/push-notifications.test.ts index 519b47ce..11db452c 100644 --- a/src/lib/__tests__/push-notifications.test.ts +++ b/src/lib/__tests__/push-notifications.test.ts @@ -43,10 +43,21 @@ const { CREATED } = vi.hoisted(() => ({ })); // A client of another signed-in account (device sync patches its push types -// without the singleton). -const { DETACHED } = vi.hoisted(() => ({ - DETACHED: { loaded: true, types: ['EmailDelivery'] as string[], log: [] as unknown[] }, -})); +// without the singleton), on a server with contacts and calendars unless a +// test says otherwise. +const { DETACHED, SYNC_SESSION } = vi.hoisted(() => { + const SYNC_SESSION = { + capabilities: { + 'urn:ietf:params:jmap:core': {}, + 'urn:ietf:params:jmap:contacts': {}, + 'urn:ietf:params:jmap:calendars': {}, + } as Record, + }; + return { + SYNC_SESSION, + DETACHED: { loaded: true, types: ['EmailDelivery'] as string[], log: [] as unknown[], session: SYNC_SESSION }, + }; +}); vi.mock('../../api/jmap-client', () => ({ jmapClient: { @@ -56,6 +67,9 @@ vi.mock('../../api/jmap-client', () => ({ currentSession: { capabilities: { 'urn:ietf:params:jmap:core': {} } }, }, JMAPClient: class { + get currentSession() { + return DETACHED.session; + } async loadAccount(id: string) { DETACHED.log.push(['load', id]); return DETACHED.loaded; @@ -112,7 +126,7 @@ import { } from '../../api/push'; import { getSharedMailboxes } from '../../api/email'; import { JMAPMethodError } from '../../api/jmap-result'; -import type { EmailPushConfig } from '../../api/types'; +import type { EmailPushConfig, JMAPSession } from '../../api/types'; import { jmapClient } from '../../api/jmap-client'; import { NativeModules } from 'react-native'; import { generateAccountId } from '../account-utils'; @@ -394,26 +408,103 @@ describe('push types for device sync (#34)', () => { })); } + // A server that does not know a type refuses the whole write. + const refusedTypes = (types: string[] | undefined) => { + const unknown = (types ?? []).filter((type) => type !== 'EmailDelivery'); + if (unknown.length > 0) throw new JMAPMethodError('invalidProperties', `Unknown types: ${unknown.join(', ')}`); + }; + beforeEach(async () => { vi.clearAllMocks(); await AsyncStorage.clear(); await AsyncStorage.setItem(deviceClientIdKey(ACCOUNT_ID), OUR_DCID); - (jmapClient as { currentSession: unknown }).currentSession = { - capabilities: { 'urn:ietf:params:jmap:core': {} }, - }; + (jmapClient as { currentSession: unknown }).currentSession = SYNC_SESSION; + DETACHED.session = SYNC_SESSION; listMock.mockResolvedValue([]); updateMock.mockResolvedValue(true); installFetch({}); }); + afterEach(() => { + (jmapClient as { currentSession: unknown }).currentSession = { + capabilities: { 'urn:ietf:params:jmap:core': {} }, + }; + DETACHED.session = SYNC_SESSION; + createMock.mockImplementation(async () => CREATED); + updateMock.mockImplementation(async () => undefined); + }); + it('adds the contact and calendar types of what the account syncs to the device', async () => { - expect(await pushTypesFor(ACCOUNT_ID)).toEqual(['EmailDelivery']); + expect(await pushTypesFor(ACCOUNT_ID, SYNC_SESSION as unknown as JMAPSession)).toEqual(['EmailDelivery']); await syncing({ [CONTACTS]: true, [CALENDAR]: false }); - expect(await pushTypesFor(ACCOUNT_ID)).toEqual(['EmailDelivery', 'ContactCard', 'AddressBook']); - expect(await pushTypesFor('someone@else.example')).toEqual(['EmailDelivery']); + expect(await pushTypesFor(ACCOUNT_ID, SYNC_SESSION as unknown as JMAPSession)) + .toEqual(['EmailDelivery', 'ContactCard', 'AddressBook']); + expect(await pushTypesFor('someone@else.example', SYNC_SESSION as unknown as JMAPSession)).toEqual(['EmailDelivery']); // Removed in Android Settings: nothing to route any more. await syncing({ [CONTACTS]: true }, { removedInAndroidSettings: true }); - expect(await pushTypesFor(ACCOUNT_ID)).toEqual(['EmailDelivery']); + expect(await pushTypesFor(ACCOUNT_ID, SYNC_SESSION as unknown as JMAPSession)).toEqual(['EmailDelivery']); + }); + + it('adds only the types of what the server offers', async () => { + await syncing({ [CONTACTS]: true, [CALENDAR]: true }); + const calendarsOnly = { capabilities: { 'urn:ietf:params:jmap:core': {}, 'urn:ietf:params:jmap:calendars': {} } }; + expect(await pushTypesFor(ACCOUNT_ID, calendarsOnly as unknown as JMAPSession)) + .toEqual(['EmailDelivery', 'CalendarEvent', 'Calendar']); + expect(await pushTypesFor(ACCOUNT_ID, null)).toEqual(['EmailDelivery']); + + // Sync was turned on while another account's server was served; this one + // has neither: its subscription stays mail-only. + (jmapClient as { currentSession: unknown }).currentSession = { + capabilities: { 'urn:ietf:params:jmap:core': {} }, + }; + createMock.mockImplementation(async (params: { types: string[] }) => { + refusedTypes(params.types); + return CREATED; + }); + const result = await setupPushNotifications({ relayBaseUrl: RELAY }); + expect(result.subscriptionId).toBe('new-server-id'); + expect(createMock.mock.calls.map((c) => c[0].types)).toEqual([['EmailDelivery']]); + }); + + it("does not add types another account's server does not offer", async () => { + const OTHER = 'bob@other.example.net'; + await AsyncStorage.setItem(`push:subscriptionId:v2:${OTHER}`, 'bob-sub'); + await AsyncStorage.setItem(DEVICE_SYNC_KEY, JSON.stringify({ + state: { accounts: { [OTHER]: { enabled: { [CONTACTS]: true } } } }, + version: 0, + })); + DETACHED.session = { capabilities: { 'urn:ietf:params:jmap:core': {}, 'urn:ietf:params:jmap:calendars': {} } }; + DETACHED.log = []; + DETACHED.types = ['EmailDelivery']; + await refreshPushSubscriptionTypes(OTHER); + expect(DETACHED.log.map((entry) => (entry as unknown[])[0])).toEqual(['load', 'PushSubscription/get']); + }); + + it('keeps mail push on a server that refuses the device sync types', async () => { + await syncing({ [CONTACTS]: true }); + createMock.mockImplementation(async (params: { types: string[] }) => { + refusedTypes(params.types); + return CREATED; + }); + updateMock.mockImplementation(async (_id: string, patch: { types?: string[] }) => refusedTypes(patch.types)); + + // A new subscription: mail-only rather than none. + const created = await setupPushNotifications({ relayBaseUrl: RELAY }); + expect(created).toEqual({ subscriptionId: 'new-server-id', verified: true }); + expect(createMock.mock.calls.map((c) => c[0].types)) + .toEqual([['EmailDelivery', 'ContactCard', 'AddressBook'], ['EmailDelivery']]); + + // The working subscription is kept and its expiry pushed forward, never + // replaced by one the server refuses as well. + vi.clearAllMocks(); + await AsyncStorage.setItem(SUB_KEY, 'existing'); + listMock.mockResolvedValue([healthy(['EmailDelivery'])]); + const refreshed = await setupPushNotifications({ relayBaseUrl: RELAY }); + expect(refreshed.subscriptionId).toBe('existing'); + expect(updateMock.mock.calls.map((c) => c[1].types)).toEqual([['EmailDelivery', 'ContactCard', 'AddressBook'], undefined]); + expect(updateMock.mock.calls[1][1].expires).toBeDefined(); + expect(createMock).not.toHaveBeenCalled(); + expect(destroyMock).not.toHaveBeenCalled(); }); it('subscribes with them', async () => { diff --git a/src/lib/push-notifications.ts b/src/lib/push-notifications.ts index 4151665b..97c48971 100644 --- a/src/lib/push-notifications.ts +++ b/src/lib/push-notifications.ts @@ -12,10 +12,16 @@ import { loadedMailboxes } from './mailbox-source'; import { jmapClient, JMAPClient } from '../api/jmap-client'; import { JMAPMethodError, requireMethodResult } from '../api/jmap-result'; import { CAPABILITIES } from '../api/types'; -import type { EmailPushConfig, JMAPAccountInfo, Mailbox } from '../api/types'; +import type { EmailPushConfig, JMAPAccountInfo, JMAPSession, Mailbox } from '../api/types'; import { generateAccountId } from './account-utils'; import { t } from '../stores/locale-store'; -import { DEVICE_SYNC_STORAGE_KEY, deviceSyncPushTypes, persistedAccounts } from '../device-sync/app/prefs'; +import { + authorityOfType, + DEVICE_SYNC_STORAGE_KEY, + deviceSyncPushTypes, + persistedAccounts, +} from '../device-sync/app/prefs'; +import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, type Authority } from '../device-sync/types'; import { getUnifiedPushDistributors, isUnifiedPushSupported, @@ -207,19 +213,32 @@ export function isValidRelayUrl(value: string): boolean { // In-app sync uses the separate SSE channel and is unaffected. export const PUSH_TYPES = ['EmailDelivery'] as const; +// The capability a server advertises for the data types of an authority. +const DEVICE_SYNC_CAPABILITIES: Record = { + [CONTACTS_AUTHORITY]: CAPABILITIES.CONTACTS, + [CALENDAR_AUTHORITY]: CAPABILITIES.CALENDARS, +}; + /** * The push types of one account's subscription: `EmailDelivery`, plus the * contact and calendar types while the account syncs them to the device * (#34, docs/device-sync.md "Triggers"): the native push router turns those - * into device syncs, and a push carrying only them never notifies. + * into device syncs, and a push carrying only them never notifies. Only the + * types of a capability the account's session advertises: a server without + * contacts or calendars may refuse the whole subscription over them. * * Read from the device sync store's persisted JSON rather than the store, so * code that runs without it (the push task) never creates it. */ -export async function pushTypesFor(accountId: string): Promise { +export async function pushTypesFor(accountId: string, session: JMAPSession | null): Promise { let extra: string[] = []; try { - extra = deviceSyncPushTypes(persistedAccounts(await AsyncStorage.getItem(DEVICE_SYNC_STORAGE_KEY))[accountId]); + const capabilities = session?.capabilities ?? {}; + extra = deviceSyncPushTypes(persistedAccounts(await AsyncStorage.getItem(DEVICE_SYNC_STORAGE_KEY))[accountId]) + .filter((type) => { + const authority = authorityOfType(type); + return !!authority && DEVICE_SYNC_CAPABILITIES[authority] in capabilities; + }); } catch { extra = []; } @@ -404,6 +423,24 @@ async function writeWithEmailPush( } } +/** + * Run a subscription write with the account's push types and, when the server + * refuses it while they include device sync's, once more with the mail types + * alone: mail push never depends on device sync (#34). + */ +async function writeWithPushTypes( + types: readonly string[], + write: (types: readonly string[]) => Promise, +): Promise { + try { + return await write(types); + } catch (err) { + if (sameTypes(types, PUSH_TYPES)) throw err; + logPhase('jmap', 'push types refused, retrying with the mail types alone'); + return write(PUSH_TYPES); + } +} + async function readRefusedEmailPushAccounts(accountId: string): Promise { try { const parsed = JSON.parse((await AsyncStorage.getItem(emailPushRefusedKey(accountId))) ?? '[]'); @@ -947,7 +984,7 @@ async function setupPushNotificationsInner( : null; const subKey = subscriptionIdKey(accountId); const storedServerId = await AsyncStorage.getItem(subKey); - const types = await pushTypesFor(accountId); + const types = await pushTypesFor(accountId, jmapClient.currentSession); let jmapAccountId: string | null = null; try { jmapAccountId = jmapClient.accountId; @@ -1006,14 +1043,16 @@ async function setupPushNotificationsInner( let serverAssignedId: string; let serverExpires: string | null; try { - const created = await writeWithEmailPush(emailPush, (filter) => - createPushSubscription({ - deviceClientId, - url: buildRelayUrl(relayBaseUrl, `/api/push/jmap/${encodeURIComponent(deviceClientId)}`), - types: [...types], - expires: expiresFromNow(SUBSCRIPTION_EXPIRES_DAYS), - ...(filter ? { emailPush: filter } : {}), - }), + const created = await writeWithPushTypes(types, (wanted) => + writeWithEmailPush(emailPush, (filter) => + createPushSubscription({ + deviceClientId, + url: buildRelayUrl(relayBaseUrl, `/api/push/jmap/${encodeURIComponent(deviceClientId)}`), + types: [...wanted], + expires: expiresFromNow(SUBSCRIPTION_EXPIRES_DAYS), + ...(filter ? { emailPush: filter } : {}), + }), + ), ); serverAssignedId = created.result.id; serverExpires = created.result.expires; @@ -1063,8 +1102,9 @@ async function addPushAccountId(accountId: string): Promise { // from what this client wants (a subscription created by an older build still // listens to `Email`/`Mailbox`; device sync was turned on or off; a Junk // mailbox id can change under us). Returns false if the server rejects the -// update, which the caller treats as "replace"; otherwise the emailPush map -// the update installed (null when it left the filter alone). +// update, also with the mail types alone, which the caller treats as +// "replace"; otherwise the emailPush map the update installed (null when it +// left the filter alone). async function refreshSubscriptionExpires( sub: { id: string; @@ -1088,15 +1128,17 @@ async function refreshSubscriptionExpires( } } try { - const patch: { expires?: string; types?: string[] } = { - expires: expiresFromNow(SUBSCRIPTION_EXPIRES_DAYS), - }; - if (typesNeedUpdate) patch.types = [...types]; - const { emailPush } = await writeWithEmailPush( - emailPushNeedsUpdate ? desiredEmailPush : null, - (filter) => updatePushSubscription(sub.id, filter ? { ...patch, emailPush: filter } : patch), - ); - return { emailPush }; + return await writeWithPushTypes(types, async (wanted) => { + const patch: { expires?: string; types?: string[] } = { + expires: expiresFromNow(SUBSCRIPTION_EXPIRES_DAYS), + }; + if (!sameTypes(sub.types, wanted)) patch.types = [...wanted]; + const { emailPush } = await writeWithEmailPush( + emailPushNeedsUpdate ? desiredEmailPush : null, + (filter) => updatePushSubscription(sub.id, filter ? { ...patch, emailPush: filter } : patch), + ); + return { emailPush }; + }); } catch { return false; } @@ -1231,12 +1273,12 @@ export async function refreshPushSubscriptionTypes(accountId: string): Promise s.id === subscriptionId); if (!current || sameTypes(current.types, types)) return; await updatePushSubscription(subscriptionId, { types: [...types] }); @@ -1244,6 +1286,7 @@ export async function refreshPushSubscriptionTypes(accountId: string): Promise Date: Sun, 27 Sep 2026 09:33:32 +0200 Subject: [PATCH 087/334] fix: notify new mail again when a push names Email but not EmailDelivery #34 On main every push ran the mail task, whose legacy path notifies the newest unread inbox mail. Device sync narrowed that to pushes naming EmailDelivery, so contact and calendar changes never notify. But a subscription made by an older build listens to Email and Mailbox until the app is opened again, and a server without EmailDelivery reports new mail only as those changes. Their new mail no longer showed a notification while the app was closed. Email and Mailbox now count as possible mail again, in the native push router and in the push task alike. A push that names only contact or calendar types still never takes the mail path. Checked with the push router and push task tests: an Email or Mailbox change starts the task and the legacy path notifies the message, next to the syncs a mixed map asks for. --- .../bulwarkmobile/sync/PushRouting.kt | 14 ++++++-- .../bulwarkmobile/sync/PushRoutingTest.kt | 20 +++++++++++ .../__tests__/push-background-task.test.ts | 36 +++++++++++++++++++ src/lib/push-background-task.ts | 15 +++++--- 4 files changed, 78 insertions(+), 7 deletions(-) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt index 3bdcca1b..70c0b97d 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt @@ -13,7 +13,7 @@ import org.json.JSONObject * The relay forwards the JMAP StateChange's `changed` map verbatim (FCM data * key `changed`: JMAP account id → type → state; only its first account is * repeated as `accountId`), so every account key is looked at. The mail task - * runs only for mail: when some account's map has `EmailDelivery`, for an + * runs only for mail: when some account's map has a mail type, for an * EmailPush, or when `changed` is missing or unreadable (older relays), so a * contact change never ends in a mail notification. */ @@ -28,7 +28,15 @@ object PushRouting { ) private const val EMAIL_PUSH_KIND = "jmap-email-push" - private const val EMAIL_DELIVERY = "EmailDelivery" + + /** + * Types that may mean new mail: `EmailDelivery`, and `Email`/`Mailbox`, + * which new mail changes too. A subscription made by an older build + * listens to them until the launch-time resync replaces its types, and a + * server without `EmailDelivery` has only them. Keep in sync with + * MAIL_TYPES in src/lib/push-background-task.ts. + */ + private val MAIL_TYPES = setOf("EmailDelivery", "Email", "Mailbox") private val CONTACT_TYPES = setOf("ContactCard", "AddressBook") private val CALENDAR_TYPES = setOf("CalendarEvent", "Calendar") @@ -42,7 +50,7 @@ object PushRouting { mail = true continue } - if (EMAIL_DELIVERY in types) mail = true + if (types.any { it in MAIL_TYPES }) mail = true val contacts = types.any { it in CONTACT_TYPES } val calendar = types.any { it in CALENDAR_TYPES } for (route in routes[jmapAccountId].orEmpty()) { diff --git a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt index 1a13656d..8e608022 100644 --- a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt +++ b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt @@ -64,6 +64,26 @@ class PushRoutingTest { assertEquals(setOf("alice-work" to calendar), decision.syncs) } + @Test + fun `an Email or Mailbox change is possible mail, as a subscription from an older build sends it`() { + for (changed in listOf( + """{"c":{"Email":"s1","Mailbox":"s2"}}""", + """{"c":{"Email":"s1"}}""", + """{"c":{"Mailbox":"s1"}}""", + )) { + val decision = PushRouting.decide(stateChange(changed), routes) + assertTrue("changed=$changed", decision.startMailTask) + assertTrue("changed=$changed", decision.syncs.isEmpty()) + } + val mixed = PushRouting.decide(stateChange("""{"team":{"CalendarEvent":"e1"},"c":{"Email":"s1"}}"""), routes) + assertTrue(mixed.startMailTask) + assertEquals(setOf("alice-work" to calendar), mixed.syncs) + // Types that are neither mail nor device sync say nothing about mail. + val other = PushRouting.decide(stateChange("""{"c":{"Thread":"t1","ContactCard":"s1"}}"""), routes) + assertFalse(other.startMailTask) + assertEquals(setOf("alice@example.org" to contacts), other.syncs) + } + @Test fun `an email push always starts the mail task`() { val data = mapOf("kind" to "jmap-email-push", "accountId" to "c", "changed" to """{"c":{"EmailDelivery":"s"}}""") diff --git a/src/lib/__tests__/push-background-task.test.ts b/src/lib/__tests__/push-background-task.test.ts index c8984ad9..8f1f0dae 100644 --- a/src/lib/__tests__/push-background-task.test.ts +++ b/src/lib/__tests__/push-background-task.test.ts @@ -237,6 +237,42 @@ describe('pushes for device sync (#34)', () => { await pushBackgroundTask(stateChange({ 'jmap-primary': { EmailDelivery: 'm1', ContactCard: 's1' } })); expect(jmapClient.getStoredCredentials).toHaveBeenCalledWith(LOCAL); }); + + it('takes an Email or Mailbox change for possible mail, as a subscription from an older build sends it', () => { + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { Email: 's1', Mailbox: 's2' } })))).toBe(false); + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { Email: 's1' } })))).toBe(false); + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { Mailbox: 's1' } })))).toBe(false); + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { CalendarEvent: 'e1' }, b: { Email: 's1' } })))).toBe(false); + // Types that are neither mail nor device sync say nothing about mail. + expect(carriesNoMail(parseRelayPushData(stateChange({ a: { Thread: 't1', ContactCard: 's1' } })))).toBe(true); + }); + + it('notifies new mail from a subscription that listens to Email and Mailbox', async () => { + (secureFetch as ReturnType).mockImplementation(async (url: string, init?: { body?: string }) => { + if (url.endsWith('/.well-known/jmap')) { + return { + ok: true, + json: async () => ({ + apiUrl: 'https://mail.example.com/jmap/', + primaryAccounts: { 'urn:ietf:params:jmap:mail': 'jmap-primary' }, + accounts: { 'jmap-primary': {} }, + }), + }; + } + // The legacy path: the inbox, its newest unread ids, then the messages. + const [[name]] = JSON.parse(init?.body ?? '{}').methodCalls; + const body = name === 'Mailbox/get' + ? { list: [{ id: 'inbox', role: 'inbox' }] } + : name === 'Email/query' + ? { ids: ['m1'] } + : { list: [{ id: 'm1', threadId: 't1', keywords: {}, subject: 'Hi', from: [{ email: 'bob@example.com' }] }] }; + return { ok: true, json: async () => ({ methodResponses: [[name, body, '0']] }) }; + }); + + await pushBackgroundTask(stateChange({ 'jmap-primary': { Email: 's1', Mailbox: 's2' } })); + + expect(showNotification).toHaveBeenCalledWith(expect.objectContaining({ emailId: 'm1', accountId: LOCAL })); + }); }); describe('selectNotifiableEmails', () => { diff --git a/src/lib/push-background-task.ts b/src/lib/push-background-task.ts index 776b39c4..2c703f8e 100644 --- a/src/lib/push-background-task.ts +++ b/src/lib/push-background-task.ts @@ -378,18 +378,25 @@ async function detachedNewestUnreadInboxIds( return (qBody.ids as string[]) ?? []; } +// Types that may mean new mail: `EmailDelivery`, and `Email`/`Mailbox`, which +// new mail changes too. A subscription made by an older build listens to them +// until the launch-time resync replaces its types, and a server without +// `EmailDelivery` has only them. Keep in sync with MAIL_TYPES in PushRouting.kt. +const MAIL_TYPES = ['EmailDelivery', 'Email', 'Mailbox']; + /** * A StateChange push in which no account got new mail: only contact or * calendar changes, which the native push router already turned into device - * syncs (#34, docs/device-sync.md "Triggers"). Without `EmailDelivery` there - * is nothing to notify, and the legacy path (the newest unread mail) must - * not run for it. An empty or missing `changed` map keeps the old behaviour. + * syncs (#34, docs/device-sync.md "Triggers"). Without a mail type there is + * nothing to notify, and the legacy path (the newest unread mail) must not + * run for it. An empty or missing `changed` map keeps the old behaviour. */ export function carriesNoMail(payload: RelayPushData): boolean { if (payload.kind !== 'jmap-state-change' || !payload.changed) return false; const accounts = Object.values(payload.changed); if (accounts.length === 0) return false; - return !accounts.some((types) => !!types && typeof types === 'object' && 'EmailDelivery' in types); + return !accounts.some((types) => !!types && typeof types === 'object' + && MAIL_TYPES.some((type) => type in types)); } // Fired by BulwarkPushTaskService when a data FCM message arrives. Runs in a From eebb00238c5f207a39d89d6d291a5718e14d2f9c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 09:34:43 +0200 Subject: [PATCH 088/334] fix: look for new mail when a push names changes it can't read #34 The native push router and the push task decided separately whether a push may carry mail, and disagreed. For an account entry that is not an object, or a map that names no type, the router started the mail task as possible mail, but the task's guard read the same payload as "no mail" and returned, so new mail it announced was never looked for. A push without a kind was judged by its map in the router but taken for mail by the task. Both now apply the same rules: anything but a StateChange, a map that is missing, unreadable or names no type, an unreadable account entry, or a mail type means the mail task runs. Only a readable StateChange naming contact, calendar or other non-mail types skips it. Checked with one table of payloads run against the router's decision and the task's guard alike. --- .../bulwarkmobile/sync/PushRouting.kt | 13 +++-- .../bulwarkmobile/sync/PushRoutingTest.kt | 50 ++++++++++++++++ .../__tests__/push-background-task.test.ts | 57 +++++++++++++++++++ src/lib/__tests__/push-notifications.test.ts | 8 +-- src/lib/push-background-task.ts | 15 +++-- 5 files changed, 128 insertions(+), 15 deletions(-) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt index 70c0b97d..fd5d938a 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/sync/PushRouting.kt @@ -13,9 +13,11 @@ import org.json.JSONObject * The relay forwards the JMAP StateChange's `changed` map verbatim (FCM data * key `changed`: JMAP account id → type → state; only its first account is * repeated as `accountId`), so every account key is looked at. The mail task - * runs only for mail: when some account's map has a mail type, for an - * EmailPush, or when `changed` is missing or unreadable (older relays), so a - * contact change never ends in a mail notification. + * runs only for mail: when some account's map has a mail type, for anything + * but a StateChange (an EmailPush), when `changed` is missing, unreadable or + * names no type (older relays), or when an account's entry is unreadable, so + * a contact change never ends in a mail notification. The push task's guard + * (`carriesNoMail` in src/lib/push-background-task.ts) applies the same rules. */ object PushRouting { /** An Android account and the authorities it syncs, fed by one JMAP account. */ @@ -27,7 +29,7 @@ object PushRouting { val startMailTask: Boolean, ) - private const val EMAIL_PUSH_KIND = "jmap-email-push" + private const val STATE_CHANGE_KIND = "jmap-state-change" /** * Types that may mean new mail: `EmailDelivery`, and `Email`/`Mailbox`, @@ -42,7 +44,8 @@ object PushRouting { fun decide(data: Map, routes: Map>): Decision { val changed = data["changed"]?.let(::parseChanged) - var mail = data["kind"] == EMAIL_PUSH_KIND || changed == null + // Only a StateChange can say it carries no mail. + var mail = data["kind"] != STATE_CHANGE_KIND || changed == null val syncs = LinkedHashSet>() for ((jmapAccountId, types) in changed.orEmpty()) { if (types == null) { diff --git a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt index 8e608022..db2d5b53 100644 --- a/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt +++ b/android/app/src/test/java/com/anonymous/bulwarkmobile/sync/PushRoutingTest.kt @@ -108,6 +108,56 @@ class PushRoutingTest { assertEquals(setOf("alice@example.org" to contacts), decision.syncs) } + @Test + fun `reads a push the same way as the push task`() { + // kind (null: none), changed (null: none) → whether the mail task starts. The + // same table as MAIL_CASES in src/lib/__tests__/push-background-task.test.ts, + // whose carriesNoMail must agree. + val state = "jmap-state-change" + val emailPush = "jmap-email-push" + val cases: List> = listOf( + // No readable map: as before device sync. + Triple(state, null, true), + Triple(state, "", true), + Triple(state, "not json", true), + Triple(state, "[1]", true), + Triple(state, "{}", true), + // A map that names no type is read like a missing one. + Triple(state, """{"c":{}}""", true), + Triple(state, """{"c":{},"team":{}}""", true), + // An account entry that can't be read may be mail. + Triple(state, """{"c":null}""", true), + Triple(state, """{"c":"garbled","team":{"ContactCard":"s"}}""", true), + Triple(state, """{"c":[],"team":{"ContactCard":"s"}}""", true), + Triple(state, """{"c":5}""", true), + // Mail types anywhere in the map. + Triple(state, """{"c":{"EmailDelivery":"s"}}""", true), + Triple(state, """{"c":{"Email":"s"}}""", true), + Triple(state, """{"c":{"Mailbox":"s"}}""", true), + Triple(state, """{"c":{"ContactCard":"s1"},"team":{"EmailDelivery":"s2"}}""", true), + // Only a StateChange can say it carries no mail. + Triple(null, """{"c":{"ContactCard":"s"}}""", true), + Triple(emailPush, """{"c":{"EmailDelivery":"s"}}""", true), + Triple(emailPush, null, true), + // Contact and calendar changes, and types that say nothing about mail. + Triple(state, """{"c":{"ContactCard":"s1","AddressBook":"s2"}}""", false), + Triple(state, """{"c":{"CalendarEvent":"e1"},"team":{"Calendar":"c1"}}""", false), + Triple(state, """{"c":{"ContactCard":"s"},"team":{}}""", false), + Triple(state, """{"elsewhere":{"ContactCard":"s"}}""", false), + Triple(state, """{"c":{"Thread":"t1","ContactCard":"s1"}}""", false), + ) + for ((kind, changed, mail) in cases) { + val data = buildMap { + if (kind != null) put("kind", kind) + put("accountLabel", "alice") + put("accountId", "c") + put("emailIds", "[]") + if (changed != null) put("changed", changed) + } + assertEquals("kind=$kind changed=$changed", mail, PushRouting.decide(data, routes).startMailTask) + } + } + @Test fun `accounts without routes are ignored`() { val decision = PushRouting.decide(stateChange("""{"elsewhere":{"ContactCard":"s"}}"""), routes) diff --git a/src/lib/__tests__/push-background-task.test.ts b/src/lib/__tests__/push-background-task.test.ts index 8f1f0dae..b8298866 100644 --- a/src/lib/__tests__/push-background-task.test.ts +++ b/src/lib/__tests__/push-background-task.test.ts @@ -238,6 +238,63 @@ describe('pushes for device sync (#34)', () => { expect(jmapClient.getStoredCredentials).toHaveBeenCalledWith(LOCAL); }); + // kind (undefined: none), changed (undefined: none) → whether the push may + // carry mail. The same table as "reads a push the same way as the push task" + // in PushRoutingTest.kt, whose startMailTask must agree. + const STATE = 'jmap-state-change'; + const EMAIL_PUSH = 'jmap-email-push'; + const MAIL_CASES: Array<[string | undefined, string | undefined, boolean]> = [ + // No readable map: as before device sync. + [STATE, undefined, true], + [STATE, '', true], + [STATE, 'not json', true], + [STATE, '[1]', true], + [STATE, '{}', true], + // A map that names no type is read like a missing one. + [STATE, '{"c":{}}', true], + [STATE, '{"c":{},"team":{}}', true], + // An account entry that can't be read may be mail. + [STATE, '{"c":null}', true], + [STATE, '{"c":"garbled","team":{"ContactCard":"s"}}', true], + [STATE, '{"c":[],"team":{"ContactCard":"s"}}', true], + [STATE, '{"c":5}', true], + // Mail types anywhere in the map. + [STATE, '{"c":{"EmailDelivery":"s"}}', true], + [STATE, '{"c":{"Email":"s"}}', true], + [STATE, '{"c":{"Mailbox":"s"}}', true], + [STATE, '{"c":{"ContactCard":"s1"},"team":{"EmailDelivery":"s2"}}', true], + // Only a StateChange can say it carries no mail. + [undefined, '{"c":{"ContactCard":"s"}}', true], + [EMAIL_PUSH, '{"c":{"EmailDelivery":"s"}}', true], + [EMAIL_PUSH, undefined, true], + // Contact and calendar changes, and types that say nothing about mail. + [STATE, '{"c":{"ContactCard":"s1","AddressBook":"s2"}}', false], + [STATE, '{"c":{"CalendarEvent":"e1"},"team":{"Calendar":"c1"}}', false], + [STATE, '{"c":{"ContactCard":"s"},"team":{}}', false], + [STATE, '{"elsewhere":{"ContactCard":"s"}}', false], + [STATE, '{"c":{"Thread":"t1","ContactCard":"s1"}}', false], + ]; + + it('reads a push the same way as the native router', () => { + for (const [kind, changed, mail] of MAIL_CASES) { + const data: Record = { accountLabel: 'alice', accountId: 'c', emailIds: '[]' }; + if (kind !== undefined) data.kind = kind; + if (changed !== undefined) data.changed = changed; + expect(carriesNoMail(parseRelayPushData(data)), `kind=${kind} changed=${changed}`).toBe(!mail); + } + // Native code hands `changed` over as a string; a map that is not an + // object is unreadable in any form. + expect(carriesNoMail(parseRelayPushData({ kind: STATE, changed: [{ ContactCard: 's' }] }))).toBe(false); + }); + + it("looks for mail when it can't read an account's entry, as the native router does", async () => { + for (const changed of ['{"jmap-primary":null}', '{"jmap-primary":{}}']) { + vi.clearAllMocks(); + await pushBackgroundTask({ kind: STATE, accountLabel: 'alice', accountId: 'jmap-primary', emailIds: '[]', changed }); + expect(jmapClient.getStoredCredentials, changed).toHaveBeenCalledWith(LOCAL); + } + }); + it('takes an Email or Mailbox change for possible mail, as a subscription from an older build sends it', () => { expect(carriesNoMail(parseRelayPushData(stateChange({ a: { Email: 's1', Mailbox: 's2' } })))).toBe(false); expect(carriesNoMail(parseRelayPushData(stateChange({ a: { Email: 's1' } })))).toBe(false); diff --git a/src/lib/__tests__/push-notifications.test.ts b/src/lib/__tests__/push-notifications.test.ts index 11db452c..44f1c2b2 100644 --- a/src/lib/__tests__/push-notifications.test.ts +++ b/src/lib/__tests__/push-notifications.test.ts @@ -409,7 +409,7 @@ describe('push types for device sync (#34)', () => { } // A server that does not know a type refuses the whole write. - const refusedTypes = (types: string[] | undefined) => { + const refuseUnknownTypes = (types: string[] | undefined) => { const unknown = (types ?? []).filter((type) => type !== 'EmailDelivery'); if (unknown.length > 0) throw new JMAPMethodError('invalidProperties', `Unknown types: ${unknown.join(', ')}`); }; @@ -458,7 +458,7 @@ describe('push types for device sync (#34)', () => { capabilities: { 'urn:ietf:params:jmap:core': {} }, }; createMock.mockImplementation(async (params: { types: string[] }) => { - refusedTypes(params.types); + refuseUnknownTypes(params.types); return CREATED; }); const result = await setupPushNotifications({ relayBaseUrl: RELAY }); @@ -483,10 +483,10 @@ describe('push types for device sync (#34)', () => { it('keeps mail push on a server that refuses the device sync types', async () => { await syncing({ [CONTACTS]: true }); createMock.mockImplementation(async (params: { types: string[] }) => { - refusedTypes(params.types); + refuseUnknownTypes(params.types); return CREATED; }); - updateMock.mockImplementation(async (_id: string, patch: { types?: string[] }) => refusedTypes(patch.types)); + updateMock.mockImplementation(async (_id: string, patch: { types?: string[] }) => refuseUnknownTypes(patch.types)); // A new subscription: mail-only rather than none. const created = await setupPushNotifications({ relayBaseUrl: RELAY }); diff --git a/src/lib/push-background-task.ts b/src/lib/push-background-task.ts index 2c703f8e..1f513bc9 100644 --- a/src/lib/push-background-task.ts +++ b/src/lib/push-background-task.ts @@ -154,7 +154,7 @@ export function parseRelayPushData(data: unknown): RelayPushData { } catch { // ignore } - } else if (d.changed && typeof d.changed === 'object') { + } else if (d.changed && typeof d.changed === 'object' && !Array.isArray(d.changed)) { out.changed = d.changed as Record>; } // Older relays only sent `changed`; the account id is its first key. @@ -389,14 +389,17 @@ const MAIL_TYPES = ['EmailDelivery', 'Email', 'Mailbox']; * calendar changes, which the native push router already turned into device * syncs (#34, docs/device-sync.md "Triggers"). Without a mail type there is * nothing to notify, and the legacy path (the newest unread mail) must not - * run for it. An empty or missing `changed` map keeps the old behaviour. + * run for it. A missing map, or one that names no type, keeps the old + * behaviour, and an account entry it can't read may be mail. The same rules + * as PushRouting.decide, so the two never disagree about a push. */ export function carriesNoMail(payload: RelayPushData): boolean { if (payload.kind !== 'jmap-state-change' || !payload.changed) return false; - const accounts = Object.values(payload.changed); - if (accounts.length === 0) return false; - return !accounts.some((types) => !!types && typeof types === 'object' - && MAIL_TYPES.some((type) => type in types)); + const entries: unknown[] = Object.values(payload.changed); + if (entries.some((types) => !types || typeof types !== 'object' || Array.isArray(types))) return false; + const types = entries.flatMap((entry) => Object.keys(entry as Record)); + if (types.length === 0) return false; + return !types.some((type) => MAIL_TYPES.includes(type)); } // Fired by BulwarkPushTaskService when a data FCM message arrives. Runs in a From 217bb59d573af6961035fabc7113fa4f5041d6ba Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:01:23 +0200 Subject: [PATCH 089/334] fix: forget a contact created and deleted on the phone between two syncs #34 ContactsProvider soft-deletes every raw contact and group of a sync account, also one that never reached the server (DELETED=1, SOURCE_ID NULL). When a contact or group was created and deleted on the phone before a run claimed it, no upload phase took the row: without an identity or a pending create it belongs to no JMAP account, and the claim step skipped deleted rows. The row stayed for good, and the upload-only check and the teardown counted it as a waiting change: every upload sync the providers schedule became a full run, and turning sync off or signing out always asked about a change that would be lost. The claim step now hands such a row to the planner, whose plan for it is a purge, and `hasLocalWork` and `countPending` leave out deleted rows that have neither an identity nor a pending create. Nothing is uploaded for them. Calendar has no such gap: CalendarProvider hard-deletes an app-deleted event without `_SYNC_ID`, and a claimed event carries `~pending/`, so it is soft-deleted with its pending create and destroyed by uid as before. --- .../engine/deleted-before-claim.test.ts | 97 +++++++++++++++++++ src/device-sync/engine/item-sync.ts | 6 +- src/device-sync/engine/run.ts | 20 +++- src/device-sync/engine/teardown.ts | 10 +- 4 files changed, 121 insertions(+), 12 deletions(-) create mode 100644 src/device-sync/__tests__/engine/deleted-before-claim.test.ts diff --git a/src/device-sync/__tests__/engine/deleted-before-claim.test.ts b/src/device-sync/__tests__/engine/deleted-before-claim.test.ts new file mode 100644 index 00000000..c31d2995 --- /dev/null +++ b/src/device-sync/__tests__/engine/deleted-before-claim.test.ts @@ -0,0 +1,97 @@ +// Items created on the device and deleted before any run claimed them. +// ContactsProvider soft-deletes every raw contact and group of a sync account +// (DELETED=1, SOURCE_ID NULL), CalendarProvider hard-deletes an event without +// `_SYNC_ID` at once. Such an item never reached the server: it is purged, +// nothing uploads, and it never counts as a change waiting for an upload. + +import { describe, expect, it } from 'vitest'; +import { calendarPlanner } from '../../calendar/planner'; +import { contactsPlanner } from '../../contacts/planner'; +import { + addDeviceContact, + addServerCards, + ANDROID_ACCOUNT, + CALENDAR_AUTHORITY, + CONTACTS_AUTHORITY, + createHarness, + type Harness, +} from './harness'; + +function real(): Harness { + const h = createHarness(); + h.deps.planners = { contacts: contactsPlanner, calendar: calendarPlanner }; + return h; +} + +/** Synced once, then a contact and a group created on the device and deleted again. */ +async function createdAndDeleted(): Promise<{ h: Harness; contact: number; group: number }> { + const h = real(); + addServerCards(h, ['Ada Lovelace']); + expect((await h.run()).outcome).toBe('ok'); + const contact = addDeviceContact(h, 'Hedy Lamarr'); + h.device.user.deleteContact(contact); + const group = h.device.user.insertGroup(ANDROID_ACCOUNT, 'Friends'); + h.device.user.deleteGroup(group); + return { h, contact, group }; +} + +function offline(h: Harness): void { + h.deps.jmap = async () => { + const error = new Error('Network request failed'); + error.name = 'NetworkError'; + throw error; + }; +} + +describe('device sync engine: an item deleted on the device before it was claimed', () => { + it('purges a contact and a group, and uploads nothing', async () => { + const { h, contact, group } = await createdAndDeleted(); + + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, updated: 0, deleted: 0 } } }); + expect(h.device.row('raw_contacts', contact)).toBeUndefined(); + expect(h.device.rows('data', 'raw_contact_id = ?', [contact])).toEqual([]); + expect(h.device.row('groups', group)).toBeUndefined(); + expect(h.server.all('ContactCard', 'a').map((c) => c.name)).toEqual([{ full: 'Ada Lovelace' }]); + }); + + it('does not turn an upload-only sync into a full one', async () => { + const { h } = await createdAndDeleted(); + const requests = h.server.requests.length; + + expect((await h.run(CONTACTS_AUTHORITY, { upload: true })).outcome).toBe('ok'); + + expect(h.server.requests.length).toBe(requests); + }); + + it('is no change that turning sync off would lose, also while offline', async () => { + const { h } = await createdAndDeleted(); + offline(h); + + expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); + + expect(h.device.rows('raw_contacts')).toEqual([]); + expect(h.device.rows('groups')).toEqual([]); + }); + + it('leaves nothing behind for an event: CalendarProvider removes it at once', async () => { + const h = real(); + expect((await h.run(CALENDAR_AUTHORITY)).outcome).toBe('ok'); + const calendarRow = Number(h.device.rows('calendars')[0]._id); + const event = h.device.user.insertEvent(calendarRow, { + title: 'Dentist', + dtstart: Date.UTC(2026, 9, 1, 8), + dtend: Date.UTC(2026, 9, 1, 9), + eventTimezone: 'Europe/Berlin', + }); + h.device.user.deleteEvent(event); + expect(h.events()).toEqual([]); + const requests = h.server.requests.length; + + expect((await h.run(CALENDAR_AUTHORITY, { upload: true })).outcome).toBe('ok'); + expect(h.server.requests.length).toBe(requests); + offline(h); + expect(await h.teardown(CALENDAR_AUTHORITY)).toEqual({ pending: 0 }); + }); +}); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index ce7947f5..c747ddae 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -886,13 +886,15 @@ export abstract class ItemSync { /** * New items get their uid and target written before anything is sent. A * claim whose collection is gone from the server is made again where the - * kind allows it (contacts: nothing of it can exist in a deleted book). + * kind allows it (contacts: nothing of it can exist in a deleted book). A + * new item deleted before it was claimed never reached the server, and no + * upload phase takes it (it has no account): its plan is a purge. */ private async claimNewItems(): Promise { const works: Work[] = []; for (const held of await this.loadUploadItems()) { const meta = held.kind.meta(held.local); - if (!meta.isNew || meta.deleted || !this.claimable(held)) continue; + if (!meta.isNew || (meta.deleted && meta.pending) || !this.claimable(held)) continue; const reclaim = !!meta.pending && !!held.kind.unclaimed && this.collectionGone(meta.pending.target); if (meta.pending && !reclaim) continue; const ref = `row:${meta.rowId}`; diff --git a/src/device-sync/engine/run.ts b/src/device-sync/engine/run.ts index 238d0aaa..d478abcb 100644 --- a/src/device-sync/engine/run.ts +++ b/src/device-sync/engine/run.ts @@ -17,6 +17,7 @@ import { type RunPayload, type RunReport, type RunStatus, + type Row, } from '../types'; import { BatchWriter } from './batch'; import { CalendarSync } from './calendar-sync'; @@ -26,7 +27,7 @@ import type { RunEnv } from './context'; import { DEFAULT_TUNING, type EngineDeps, type Tuning } from './deps'; import { classifyFailure, RunAbort } from './errors'; import { acquireLock, lockKey, stopRequested } from './mutex'; -import { ProviderReader, str } from './provider'; +import { flag, ProviderReader, str } from './provider'; import { isQuietRun, ReportBuilder, statusOf, statusToRecord } from './report'; import { emptySyncState, parseSyncState, StateStore, type SyncState } from './sync-state'; import type { ItemSync } from './item-sync'; @@ -46,6 +47,15 @@ function itemTypeOf(authority: Authority): string { return authority === CONTACTS_AUTHORITY ? 'ContactCard' : 'CalendarEvent'; } +/** + * Whether a dirty, deleted or new raw contact or group has anything to + * upload. One created and deleted on the device before a run claimed it (no + * identity, no pending create) never reached the server: a run purges it. + */ +export function waitsForUpload(row: Row): boolean { + return !!str(row.sourceid) || !!str(row.sync3) || !flag(row.deleted); +} + /** Rows waiting for an upload (dirty, deleted, new), or state work a run must do whatever its extras. */ export async function hasLocalWork(reader: ProviderReader, authority: Authority, state: SyncState): Promise { for (const account of Object.values(state.accounts)) { @@ -55,11 +65,11 @@ export async function hasLocalWork(reader: ProviderReader, authority: Authority, const rows = await reader.rows('events', ['_id'], "dirty = 1 OR deleted = 1 OR _sync_id IS NULL OR _sync_id LIKE '~pending/%'"); return rows.length > 0; } - const contacts = await reader.rows('raw_contacts', ['_id'], 'dirty = 1 OR deleted = 1 OR sourceid IS NULL'); - if (contacts.length) return true; - const groups = await reader.rows('groups', ['_id', 'sourceid', 'dirty', 'deleted']); + const contacts = await reader.rows('raw_contacts', ['_id', 'sourceid', 'deleted', 'sync3'], 'dirty = 1 OR deleted = 1 OR sourceid IS NULL'); + if (contacts.some(waitsForUpload)) return true; + const groups = await reader.rows('groups', ['_id', 'sourceid', 'dirty', 'deleted', 'sync3']); const present = new Set(groups.map((g) => str(g.sourceid)).filter(Boolean)); - if (groups.some((g) => !str(g.sourceid) || Number(g.dirty) === 1 || Number(g.deleted) === 1)) return true; + if (groups.some((g) => (!str(g.sourceid) || Number(g.dirty) === 1 || Number(g.deleted) === 1) && waitsForUpload(g))) return true; // A group an app hard-deleted is only found by its absence. return Object.values(state.accounts).some((a) => (a.groups ?? []).some((ref) => !present.has(ref))); } diff --git a/src/device-sync/engine/teardown.ts b/src/device-sync/engine/teardown.ts index 8e0bbc20..abc80dcc 100644 --- a/src/device-sync/engine/teardown.ts +++ b/src/device-sync/engine/teardown.ts @@ -12,7 +12,7 @@ import type { EngineDeps } from './deps'; import { acquireLock, clearStopRequest, lockKey, requestStop } from './mutex'; import { ProviderReader, str } from './provider'; import { ReportBuilder } from './report'; -import { createEnv, tuningOf } from './run'; +import { createEnv, tuningOf, waitsForUpload } from './run'; import { parseSyncState } from './sync-state'; export interface TeardownOptions { @@ -36,12 +36,12 @@ export async function countPending(reader: ProviderReader, authority: Authority) const masters = new Set(rows.map((r) => str(r.original_id) ?? str(r.original_sync_id) ?? `row:${String(r._id)}`)); return masters.size; } - const contacts = await reader.rows('raw_contacts', ['_id'], 'dirty = 1 OR deleted = 1 OR sourceid IS NULL'); - const groups = await reader.rows('groups', ['_id', 'sourceid', 'dirty', 'deleted']); + const contacts = await reader.rows('raw_contacts', ['_id', 'sourceid', 'deleted', 'sync3'], 'dirty = 1 OR deleted = 1 OR sourceid IS NULL'); + const groups = await reader.rows('groups', ['_id', 'sourceid', 'dirty', 'deleted', 'sync3']); const present = new Set(groups.map((g) => str(g.sourceid)).filter(Boolean)); - const waitingGroups = groups.filter((g) => !str(g.sourceid) || Number(g.dirty) === 1 || Number(g.deleted) === 1).length; + const waitingGroups = groups.filter((g) => (!str(g.sourceid) || Number(g.dirty) === 1 || Number(g.deleted) === 1) && waitsForUpload(g)).length; const absent = Object.values(state.accounts).reduce((n, a) => n + (a.groups ?? []).filter((ref) => !present.has(ref)).length, 0); - return contacts.length + waitingGroups + absent; + return contacts.filter(waitsForUpload).length + waitingGroups + absent; } function deleteEverything(authority: Authority): ProviderOp[] { From 0664a31f8b2aa90d33eda5d10fb7074ac64ddf76 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:02:22 +0200 Subject: [PATCH 090/334] fix: keep reminding of synced events while the phone's calendar can't sync #34 With calendar sync on and the calendar app reminding, Bulwark skipped its own reminders for the synced calendars whenever sync was on in the app, even when the phone's calendar received nothing: its calendar sync paused in Android's account settings, Android's auto-sync off, or the calendar permission revoked. Events created on the server meanwhile never reached the phone, and nobody reminded of them. The reminder filter now keeps what Android last said about each account (the permission, `getMasterSyncAutomatically` and the calendar's `getSyncAutomatically`) and stays quiet only while the phone's calendar receives events. Auto-sync off counts as not receiving: Android then runs manual syncs only, and the periodic, pushed and app-triggered syncs are not manual. Android's side is read when the scheduler subscribes and again whenever the filter is asked for; a changed answer makes the scheduler reschedule. Until it was read, Bulwark reminds: a double reminder is better than a missed one. --- .../__tests__/app/reminders.test.ts | 52 +++++++++++++- src/device-sync/app/reminders.ts | 71 +++++++++++++++++-- 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/src/device-sync/__tests__/app/reminders.test.ts b/src/device-sync/__tests__/app/reminders.test.ts index 744081c4..ed1b5915 100644 --- a/src/device-sync/__tests__/app/reminders.test.ts +++ b/src/device-sync/__tests__/app/reminders.test.ts @@ -1,9 +1,21 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -const h = vi.hoisted(() => ({ available: true })); +// Android's side: the device's auto-sync, the account's calendar sync and the calendar permission. +const h = vi.hoisted(() => ({ available: true, master: true, automatic: true, permitted: true })); vi.mock('../../native', () => ({ isDeviceSyncAvailable: () => h.available, + getSyncSettings: vi.fn(async () => ({ + masterAutomatic: h.master, + authorities: { + 'com.android.contacts': { syncable: 1, automatic: false, periodicSeconds: 0, active: false, pending: false }, + 'com.android.calendar': { syncable: 1, automatic: h.automatic, periodicSeconds: 3600, active: false, pending: false }, + }, + })), +})); + +vi.mock('../../app/permissions', () => ({ + hasSyncPermissions: vi.fn(async () => h.permitted), })); import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY } from '../../types'; @@ -34,10 +46,16 @@ function calendarSync(extra: Record = {}): void { }); } +/** Waits until the calendar app reminds of Alice's synced events (Android's side is read when the filter is asked for). */ +async function remindedByCalendarApp(): Promise { + await vi.waitFor(() => expect(remindedOnDevice(ALICE, 'c')).not.toBeNull()); +} + beforeEach(async () => { await waitForDeviceSyncHydration(); - h.available = true; + Object.assign(h, { available: true, master: true, automatic: true, permitted: true }); calendarSync(); + await remindedByCalendarApp(); }); describe('remindedOnDevice', () => { @@ -82,6 +100,36 @@ describe('remindedOnDevice', () => { }); }); +describe("remindedOnDevice while the device calendar can't receive events", () => { + it('leaves the reminders to Bulwark while calendar sync is paused in Android settings', async () => { + h.automatic = false; + await vi.waitFor(() => expect(remindedOnDevice(ALICE, 'c')).toBeNull()); + }); + + it("leaves them to Bulwark while Android's auto-sync is off: Android then runs manual syncs only", async () => { + h.master = false; + await vi.waitFor(() => expect(remindedOnDevice(ALICE, 'c')).toBeNull()); + }); + + it('leaves them to Bulwark while the calendar permission is revoked', async () => { + h.permitted = false; + await vi.waitFor(() => expect(remindedOnDevice(ALICE, 'c')).toBeNull()); + }); + + it('hands them back to the calendar app once it syncs again, and tells the scheduler both times', async () => { + const listener = vi.fn(); + const off = onDeviceRemindersChange(listener); + h.automatic = false; + await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(1)); + expect(remindedOnDevice(ALICE, 'c')).toBeNull(); + + h.automatic = true; + await vi.waitFor(() => expect(listener).toHaveBeenCalledTimes(2)); + expect(remindedOnDevice(ALICE, 'c')).not.toBeNull(); + off(); + }); +}); + describe('onDeviceRemindersChange', () => { it('fires for the owner and the calendar selection, not for sync bookkeeping', () => { const listener = vi.fn(); diff --git a/src/device-sync/app/reminders.ts b/src/device-sync/app/reminders.ts index 023bac16..bb45c209 100644 --- a/src/device-sync/app/reminders.ts +++ b/src/device-sync/app/reminders.ts @@ -4,18 +4,28 @@ * app owns the reminders, the device fires them from its Reminders rows, so * Bulwark's own scheduler skips the events of those calendars. Tasks are not * synced and keep Bulwark's reminders. + * + * Only while the device calendar receives the server's events, as Android + * last said: not while the account's calendar sync is paused in Android's + * settings, the device's auto-sync is off (Android then runs manual syncs + * only, and the periodic, pushed and app-triggered ones are not manual), or + * the calendar permission is revoked. Bulwark reminds then, and until + * Android's side was read: a double reminder is better than a missed one. */ import type { CalendarEvent } from '../../api/types'; +import { getSyncSettings } from '../native'; import { isCollectionSelected, selectionFor, syncOnInApp, useDeviceSyncStore, + waitForDeviceSyncHydration, type AccountDeviceSync, } from '../../stores/device-sync-store'; import { collectionKey } from '../common/ids'; import { CALENDAR_AUTHORITY } from '../types'; import { deviceSyncAvailable } from './available'; +import { hasSyncPermissions } from './permissions'; /** Whether the device's calendar app fires the reminders of an account's synced events. */ function deviceOwnsReminders(entry: AccountDeviceSync | undefined): entry is AccountDeviceSync { @@ -23,11 +33,53 @@ function deviceOwnsReminders(entry: AccountDeviceSync | undefined): entry is Acc return !!entry && syncOnInApp(entry, CALENDAR_AUTHORITY) && (entry.reminderOwner ?? 'device') === 'device'; } +// ─── What Android says ───────────────────────────────── + +/** App accounts whose device calendar receives the server's events, as last read from Android. */ +let receiving = new Set(); +const androidListeners = new Set<() => void>(); +let reads = 0; + +async function calendarReceives(accountName: string | undefined): Promise { + if (!accountName) return false; + try { + const settings = await getSyncSettings(accountName); + return settings.masterAutomatic !== false && !!settings.authorities?.[CALENDAR_AUTHORITY]?.automatic; + } catch { + return false; + } +} + +/** + * Reads from Android whose device calendars receive the server's events; + * a change calls the `onDeviceRemindersChange` listeners. Only the latest + * read lands. + */ +async function readAndroid(): Promise { + const read = ++reads; + await waitForDeviceSyncHydration(); + const owners = Object.entries(useDeviceSyncStore.getState().accounts).filter(([, entry]) => deviceOwnsReminders(entry)); + const next = new Set(); + if (owners.length > 0 && (await hasSyncPermissions(CALENDAR_AUTHORITY))) { + for (const [registryId, entry] of owners) { + if (await calendarReceives(entry.androidAccountName)) next.add(registryId); + } + } + if (read !== reads) return; + if (next.size === receiving.size && [...next].every((id) => receiving.has(id))) return; + receiving = next; + for (const listener of [...androidListeners]) listener(); +} + +// ─── The filter ──────────────────────────────────────── + /** * A test for the events whose reminders the device's calendar app fires for * this app account, so Bulwark schedules none; null when there are none. * `primaryJmapAccountId`: the JMAP account the app's calendar store loads - * events from when they carry no `accountId` (the user's own). + * events from when they carry no `accountId` (the user's own). Android's side + * is read again each time, for the next time: a change calls the + * `onDeviceRemindersChange` listeners. */ export function remindedOnDevice( registryId: string | null | undefined, @@ -36,6 +88,8 @@ export function remindedOnDevice( if (!registryId || !deviceSyncAvailable()) return null; const entry = useDeviceSyncStore.getState().accounts[registryId]; if (!deviceOwnsReminders(entry)) return null; + void readAndroid(); + if (!receiving.has(registryId)) return null; const selection = selectionFor(entry, CALENDAR_AUTHORITY); const personal = entry.primaryJmapAccounts?.[CALENDAR_AUTHORITY] ?? primaryJmapAccountId ?? null; return (event) => { @@ -65,15 +119,24 @@ function reminderSignature(accounts: Record): string /** * Calls `listener` whenever the set of events the device reminds of may have - * changed (reminder owner, calendar sync on or off, the calendar selection). - * Returns the unsubscribe. + * changed (reminder owner, calendar sync on or off, the calendar selection, + * whether Android lets the device calendar sync). Android's side is read + * right away, so the first reminders are scheduled with it known. Returns + * the unsubscribe. */ export function onDeviceRemindersChange(listener: () => void): () => void { let last = reminderSignature(useDeviceSyncStore.getState().accounts); - return useDeviceSyncStore.subscribe((state) => { + const unsubscribe = useDeviceSyncStore.subscribe((state) => { const next = reminderSignature(state.accounts); if (next === last) return; last = next; listener(); }); + const android = () => listener(); + androidListeners.add(android); + if (deviceSyncAvailable()) void readAndroid(); + return () => { + unsubscribe(); + androidListeners.delete(android); + }; } From ef96acd05f68c135e19b34e58b08d13a5fde2f56 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:03:20 +0200 Subject: [PATCH 091/334] fix: finish turning sync on or off before signing an account out #34 Turning device sync on or off runs one step after the other per account, but signing out did not wait its turn. A sign-out that started while a turn-on was still setting up (or queued behind a slow turn-off) found no Android account yet and went ahead; the turn-on then created the Android account, stored it and requested a sync for the signed-out account, and the next check posted "Sign in to keep syncing". A turn-off still tearing down could likewise store its preferences after the sign-out had forgotten the account. The release before a sign-out is now a step in the queue of every account it releases, taken in a fixed order: it waits for the steps under way and releases what they set up. A turn-on or turn-off asked for while the sign-out runs comes after it, and does nothing once the account was released; if the user stays signed in it goes ahead. --- .../__tests__/app/lifecycle.test.ts | 59 +++++++++++++++++++ src/device-sync/app/lifecycle.ts | 58 +++++++++++++----- 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/src/device-sync/__tests__/app/lifecycle.test.ts b/src/device-sync/__tests__/app/lifecycle.test.ts index c61d4a85..b95b0bb1 100644 --- a/src/device-sync/__tests__/app/lifecycle.test.ts +++ b/src/device-sync/__tests__/app/lifecycle.test.ts @@ -375,6 +375,65 @@ describe('sign-out', () => { expect(confirm).toHaveBeenCalledWith(4); expect(h.state.accounts).toEqual([]); }); + + it('waits for a turn-on in progress, then releases what it set up', async () => { + let finishEnsure: () => void = () => undefined; + vi.mocked(native.ensureAndroidAccount).mockImplementationOnce(async (name: string, registryId: string) => { + h.state.log.push(`ensure ${name}`); + await new Promise((resolve) => { finishEnsure = resolve; }); + h.state.accounts.push({ name, registryId, sync: {} }); + return true; + }); + const turningOn = enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + await vi.waitFor(() => expect(h.state.log).toContain('ensure alice')); + + const signingOut = releaseDeviceSyncBeforeSignOut([ALICE], vi.fn(async () => true)); + finishEnsure(); + + expect(await signingOut).toBe(true); + await turningOn; + expect(teardown).toHaveBeenCalledWith(ALICE, 'alice', CONTACTS_AUTHORITY, { uploadFirst: true, timeoutMs: TEARDOWN_TIMEOUT_MS }); + expect(h.state.accounts).toEqual([]); + expect(useDeviceSyncStore.getState().accounts[ALICE]).toBeUndefined(); + }); + + it('lets a turn-on or turn-off asked for during the sign-out set nothing up for the account', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + let finishTeardown: () => void = () => undefined; + teardown.mockImplementationOnce(async () => { + await new Promise((resolve) => { finishTeardown = resolve; }); + return { pending: 0 }; + }); + const signingOut = releaseDeviceSyncBeforeSignOut([ALICE], vi.fn(async () => true)); + await vi.waitFor(() => expect(teardown).toHaveBeenCalled()); + + const turningOn = enableDeviceSync(ALICE, CALENDAR_AUTHORITY); + const turningOff = disableDeviceSync(ALICE, CONTACTS_AUTHORITY); + finishTeardown(); + + expect(await signingOut).toBe(true); + expect((await turningOn).kind).toBe('failed'); + expect(await turningOff).toEqual({ done: true, pending: 0 }); + expect(h.state.accounts).toEqual([]); + expect(h.state.log.filter((l) => l.startsWith('ensure') || l.startsWith('sync'))).toEqual(['ensure alice', `sync alice ${CONTACTS_AUTHORITY} {"manual":true}`]); + expect(useDeviceSyncStore.getState().accounts[ALICE]).toBeUndefined(); + }); + + it('lets a turn-on asked for during the sign-out go ahead once the user chose to stay signed in', async () => { + await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + teardown.mockImplementation(async (_r: string, _n: string, authority: string) => ({ pending: authority === CONTACTS_AUTHORITY ? 1 : 0 })); + let answer: (signOut: boolean) => void = () => undefined; + const confirm = vi.fn(() => new Promise((resolve) => { answer = resolve; })); + const signingOut = releaseDeviceSyncBeforeSignOut([ALICE], confirm); + await vi.waitFor(() => expect(confirm).toHaveBeenCalled()); + + const turningOn = enableDeviceSync(ALICE, CALENDAR_AUTHORITY); + answer(false); + + expect(await signingOut).toBe(false); + expect(await turningOn).toEqual({ kind: 'enabled', accountName: 'alice' }); + expect(h.find('alice')?.sync[CALENDAR_AUTHORITY].automatic).toBe(true); + }); }); describe('reconcile', () => { diff --git a/src/device-sync/app/lifecycle.ts b/src/device-sync/app/lifecycle.ts index e873595e..4daba392 100644 --- a/src/device-sync/app/lifecycle.ts +++ b/src/device-sync/app/lifecycle.ts @@ -180,6 +180,25 @@ function oneAtATime(registryId: string, step: () => Promise): Promise { return next; } +/** One step in the queue of every one of the accounts, taken in a fixed order so two such steps never wait for each other. */ +function oneAtATimeForAll(registryIds: readonly string[], step: () => Promise): Promise { + const [first, ...rest] = registryIds; + return first === undefined ? step() : oneAtATime(first, () => oneAtATimeForAll(rest, step)); +} + +// Sign-outs under way, per app account: true once they released it. +const signOuts = new Map>(); + +/** + * A turn-on or turn-off of one app account, in its queue. One asked for + * while the account signs out runs after the sign-out, and does nothing + * (`released`) when that released the account. + */ +function accountStep(registryId: string, step: () => Promise, released: T): Promise { + const signOut = signOuts.get(registryId); + return oneAtATime(registryId, async () => ((await signOut) ? released : step())); +} + // ─── Enable ──────────────────────────────────────────── export type EnableOutcome = @@ -193,7 +212,7 @@ export type EnableOutcome = * asked for the permissions (and, for calendars, who reminds the user). */ export function enableDeviceSync(registryId: string, authority: Authority): Promise { - return oneAtATime(registryId, () => enable(registryId, authority)); + return accountStep(registryId, () => enable(registryId, authority), { kind: 'failed', message: 'The account was signed out' }); } async function enable(registryId: string, authority: Authority): Promise { @@ -230,7 +249,7 @@ async function enable(registryId: string, authority: Authority): Promise { - return oneAtATime(registryId, () => resume(registryId, authority)); + return accountStep(registryId, () => resume(registryId, authority), undefined); } async function resume(registryId: string, authority: Authority): Promise { @@ -316,7 +335,8 @@ export function disableDeviceSync( authority: Authority, options: { force?: boolean } = {}, ): Promise { - return oneAtATime(registryId, () => disable(registryId, authority, options)); + // Released for signing out meanwhile: nothing of it is left on the device. + return accountStep(registryId, () => disable(registryId, authority, options), { done: true, pending: 0 }); } async function disable(registryId: string, authority: Authority, options: { force?: boolean }): Promise { @@ -363,21 +383,31 @@ async function disable(registryId: string, authority: Authority, options: { forc * rows and removes their Android accounts, while the credentials are still * there. When changes could not be uploaded, `confirm` asks whether to sign * out anyway. Resolves false when the user chose to stay signed in; their - * accounts then keep syncing. + * accounts then keep syncing. It is a step of the accounts' own turn-ons and + * turn-offs: it waits for those under way, and those asked for meanwhile find + * the accounts released and do nothing. */ -export async function releaseDeviceSyncBeforeSignOut( +export function releaseDeviceSyncBeforeSignOut( registryIds: readonly string[], confirm: (pending: number) => Promise = confirmSignOutLosingChanges, ): Promise { - if (!deviceSyncAvailable() || registryIds.length === 0) return true; - try { - return await release(registryIds, confirm); - } catch (err) { - // Never keep the user from signing out over a bug here; an Android - // account left behind is suspended by the next reconcile. - console.warn('[device-sync] release before sign-out failed', errorMessage(err)); - return true; - } + if (!deviceSyncAvailable() || registryIds.length === 0) return Promise.resolve(true); + const ids = [...new Set(registryIds)].sort(); + const signOut = oneAtATimeForAll(ids, async () => { + try { + return await release(registryIds, confirm); + } catch (err) { + // Never keep the user from signing out over a bug here; an Android + // account left behind is suspended by the next reconcile. + console.warn('[device-sync] release before sign-out failed', errorMessage(err)); + return true; + } + }); + for (const id of ids) signOuts.set(id, signOut); + void signOut.then(() => { + for (const id of ids) if (signOuts.get(id) === signOut) signOuts.delete(id); + }); + return signOut; } async function release( From 5e62cf71f4c8f96c8b6f84b50de5b981ee67b0b5 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:04:18 +0200 Subject: [PATCH 092/334] fix: add no extra account named after its id when Android refuses one #34 Turning device sync on retried with the app account's registry id as the Android account name whenever adding the account failed. That fallback is for one case only: the login name turned out to belong to another app account's Android account, which the native module reports with the `conflict` code. A passing AccountManager failure or a permission error instead created a second account named after the opaque id, which then showed in the Contacts and Calendar apps. Only a `conflict` rejection takes the fallback now; any other failure fails the turn-on as before. --- src/device-sync/__tests__/app/lifecycle.test.ts | 15 ++++++++++++++- src/device-sync/app/lifecycle.ts | 5 +++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/device-sync/__tests__/app/lifecycle.test.ts b/src/device-sync/__tests__/app/lifecycle.test.ts index b95b0bb1..18729359 100644 --- a/src/device-sync/__tests__/app/lifecycle.test.ts +++ b/src/device-sync/__tests__/app/lifecycle.test.ts @@ -43,7 +43,8 @@ vi.mock('../../native', () => ({ ensureAndroidAccount: vi.fn(async (name: string, registryId: string) => { h.state.log.push(`ensure ${name}`); const existing = h.find(name); - if (existing && existing.registryId !== registryId) throw new Error('conflict'); + // As the native module rejects (DeviceSyncAccounts.ensure → AccountConflictException). + if (existing && existing.registryId !== registryId) throw Object.assign(new Error(`${name} belongs to another app account`), { code: 'conflict' }); if (existing) return false; h.state.accounts.push({ name, registryId, sync: {} }); return true; @@ -212,6 +213,18 @@ describe('enable', () => { expect(outcome).toEqual({ kind: 'enabled', accountName: ALICE }); }); + it('adds no second account named after the registry id when adding the account fails for another reason', async () => { + for (const code of ['failed', 'permission']) { + vi.mocked(native.ensureAndroidAccount).mockClear(); + vi.mocked(native.ensureAndroidAccount).mockRejectedValueOnce(Object.assign(new Error(`AccountManager said no (${code})`), { code })); + const outcome = await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); + expect(outcome).toEqual({ kind: 'failed', message: `AccountManager said no (${code})` }); + expect(native.ensureAndroidAccount).toHaveBeenCalledTimes(1); + expect(h.state.accounts).toEqual([]); + expect(accountDeviceSync(ALICE).androidAccountName).toBeUndefined(); + } + }); + it('leaves no account behind when turning sync on fails', async () => { h.state.failSetSyncEnabled = true; const outcome = await enableDeviceSync(ALICE, CONTACTS_AUTHORITY); diff --git a/src/device-sync/app/lifecycle.ts b/src/device-sync/app/lifecycle.ts index 4daba392..6f058c71 100644 --- a/src/device-sync/app/lifecycle.ts +++ b/src/device-sync/app/lifecycle.ts @@ -122,8 +122,9 @@ async function ensureAccountFor(registryId: string): Promise { await ensureAndroidAccount(name, registryId); return name; } catch (err) { - // The name belongs to another app account after all (the list was stale). - if (name === registryId) throw err; + // The name belongs to another app account after all (the list was stale). Any + // other failure fails the turn-on: a second account would show in the apps. + if (name === registryId || (err as { code?: unknown } | null)?.code !== 'conflict') throw err; await ensureAndroidAccount(registryId, registryId); return registryId; } From 800c4e144203a54cb684082d084ff0385acd8ec5 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:05:16 +0200 Subject: [PATCH 093/334] fix: stop waiting for React Native once a sync gave up on it #34 A sync that had to start React Native left a listener on the React host until a context came up. When the start failed (a debug build without Metro) or never finished, every retried sync added another one, each holding its run, account and extras, for the life of the process, and they all fired at once when a context finally came up. The wait now ends once, and removes its listener, for whatever reason: a ready context, a start task that faulted, or 10 minutes (no sync or push job waits longer; JobScheduler stops them). A context that comes up later runs nothing of it. The adapter's own check of its run in the callback still covers a run it gave up sooner. --- .../com/anonymous/bulwarkmobile/HeadlessJs.kt | 86 ++++++++++++++----- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/HeadlessJs.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/HeadlessJs.kt index e8521c38..b9722584 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/HeadlessJs.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/HeadlessJs.kt @@ -1,6 +1,10 @@ package com.anonymous.bulwarkmobile import android.app.Application +import android.os.Handler +import android.os.Looper +import android.os.SystemClock +import android.util.Log import androidx.annotation.MainThread import com.facebook.react.ReactApplication import com.facebook.react.ReactInstanceEventListener @@ -19,6 +23,12 @@ import java.util.concurrent.atomic.AtomicBoolean * sync adapters. */ object HeadlessJs { + private const val TAG = "HeadlessJs" + + /** No caller waits longer for React Native: JobScheduler stops sync and push jobs after 10 minutes. */ + private const val MAX_WAIT_MS = 600_000L + private const val WATCH_MS = 1_000L + /** * Calls [onReady] once with a React context whose JS instance is up, * starting React Native if the process was woken for this work. @@ -30,6 +40,13 @@ object HeadlessJs { * first and the ready check after it; whichever sees the ready instance * first runs the task. * + * The wait ends once, and its listener goes with it: with a ready context, + * or without one when React fails to start (the start task faults) or + * after [MAX_WAIT_MS], when no caller can still be waiting. A context that + * comes up later runs nothing of it, so waits given up on don't pile up on + * the host with everything their [onReady] holds. (A caller that gives up + * sooner, like the sync adapter at its deadline, checks that in [onReady].) + * * Call it on the main thread; [onReady] runs there too. MainApplication * creates the ReactHost on first use without a lock (ExpoReactHostFactory), * so a background caller racing MainActivity could create a second one, @@ -41,45 +58,68 @@ object HeadlessJs { */ @MainThread fun withReadyReactContext(app: Application, onReady: (ReactContext) -> Unit): TaskInterface? { - val done = AtomicBoolean(false) - val runOnce = { context: ReactContext -> if (done.compareAndSet(false, true)) onReady(context) } val reactApp = app as ReactApplication if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) { val host = checkNotNull(reactApp.reactHost) { "ReactHost is not initialized in New Architecture" } - val listener = object : ReactInstanceEventListener { - override fun onReactContextInitialized(context: ReactContext) { - host.removeReactInstanceEventListener(this) - runOnce(context) - } - } - host.addReactInstanceEventListener(listener) + val wait = ContextWait(onReady) { host.removeReactInstanceEventListener(it) } + host.addReactInstanceEventListener(wait) val current = host.currentReactContext if (current != null && current.hasActiveReactInstance()) { - host.removeReactInstanceEventListener(listener) - runOnce(current) + wait.onReactContextInitialized(current) return null } - return host.start() + return host.start().also { wait.watch(it) } } @Suppress("DEPRECATION") val manager = reactApp.reactNativeHost.reactInstanceManager - val listener = object : ReactInstanceEventListener { - override fun onReactContextInitialized(context: ReactContext) { - manager.removeReactInstanceEventListener(this) - runOnce(context) - } - } - manager.addReactInstanceEventListener(listener) + val wait = ContextWait(onReady) { manager.removeReactInstanceEventListener(it) } + manager.addReactInstanceEventListener(wait) val current = manager.currentReactContext if (current != null && current.hasActiveReactInstance()) { - manager.removeReactInstanceEventListener(listener) - runOnce(current) - } else if (!manager.hasStartedCreatingInitialContext()) { - manager.createReactContextInBackground() + wait.onReactContextInitialized(current) + return null } + if (!manager.hasStartedCreatingInitialContext()) manager.createReactContextInBackground() + wait.watch(null) return null } + /** One caller's wait for a ready context: a listener on the host that ends once and then removes itself. */ + private class ContextWait( + private val onReady: (ReactContext) -> Unit, + private val removeListener: (ReactInstanceEventListener) -> Unit, + ) : ReactInstanceEventListener { + private val ended = AtomicBoolean(false) + + override fun onReactContextInitialized(context: ReactContext) { + if (end()) onReady(context) + } + + /** Ends the wait without a context once [boot] faulted or [MAX_WAIT_MS] passed; checked on the main thread. */ + fun watch(boot: TaskInterface?) { + val main = Handler(Looper.getMainLooper()) + val giveUpAt = SystemClock.elapsedRealtime() + MAX_WAIT_MS + main.postDelayed(object : Runnable { + override fun run() { + if (ended.get()) return + val faulted = boot?.isFaulted() == true + if (!faulted && SystemClock.elapsedRealtime() < giveUpAt) { + main.postDelayed(this, WATCH_MS) + } else if (end()) { + val why = if (faulted) "React Native did not start" else "no React context after $MAX_WAIT_MS ms" + Log.w(TAG, "$why; no longer waiting") + } + } + }, WATCH_MS) + } + + private fun end(): Boolean { + if (!ended.compareAndSet(false, true)) return false + removeListener(this) + return true + } + } + /** * Starts [config] on the UI thread, where HeadlessJsTaskContext wants it. * [onStarted] gets the task id in that same UI-thread turn, so before any From df3175ff72b43d6777cfc4b1dd5ffbaa0238ef16 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:06:14 +0200 Subject: [PATCH 094/334] refactor: decide in one place whether a card is in read-only books only #34 --- .../__tests__/contacts/real-engine.test.ts | 18 +++++++++++++++++- src/device-sync/engine/contacts-sync.ts | 14 ++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/device-sync/__tests__/contacts/real-engine.test.ts b/src/device-sync/__tests__/contacts/real-engine.test.ts index 3e1ee1c3..e4cba093 100644 --- a/src/device-sync/__tests__/contacts/real-engine.test.ts +++ b/src/device-sync/__tests__/contacts/real-engine.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it } from 'vitest'; import { Data, Groups, MimeType } from '../../android-columns'; import { calendarPlanner } from '../../calendar/planner'; import { contactsPlanner } from '../../contacts/planner'; -import { addServerCards, ANDROID_ACCOUNT, CONTACTS_AUTHORITY, createHarness, rowWrites, type Harness } from '../engine/harness'; +import { addServerCards, ANDROID_ACCOUNT, CONTACTS_AUTHORITY, createHarness, renameDeviceContact, rowWrites, type Harness } from '../engine/harness'; import type { SetTarget } from '../fakes/fake-jmap-server'; import { JPEG } from './fixtures'; @@ -101,6 +101,22 @@ describe('contacts through the engine', () => { expect(await h.teardown(CONTACTS_AUTHORITY)).toEqual({ pending: 0 }); }); + it('puts back an edit of a card that is only in a read-only book, without asking the server', async () => { + const h = real(); + const shared = h.server.addAddressBook('a', { name: 'Shared', myRights: { mayRead: true, mayWrite: false, mayShare: false, mayDelete: false } }); + h.prefs.contactsSelection[`a/${shared}`] = true; + const [ada] = addServerCards(h, ['Ada Lovelace'], 'a', shared); + expect((await h.run()).outcome).toBe('ok'); + + renameDeviceContact(h, h.contactNamed('Ada Lovelace')!.id, 'Ada King'); + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', stats: { skipped: 1, uploaded: { updated: 0 } } }); + expect(report.itemErrors).toEqual([expect.objectContaining({ side: 'upload', type: 'readOnly' })]); + expect(h.contactNamed('Ada Lovelace')).toMatchObject({ dirty: false }); + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ name: { full: 'Ada Lovelace' } }); + }); + it('puts a contact created on the device into the group it was added to', async () => { const h = real(); const group = h.server.addCard('a', { uid: 'g1', kind: 'group', name: { full: 'Friends' }, members: { other: true }, addressBookIds: { [h.book]: true } }); diff --git a/src/device-sync/engine/contacts-sync.ts b/src/device-sync/engine/contacts-sync.ts index 2e3469ba..9a0afa0e 100644 --- a/src/device-sync/engine/contacts-sync.ts +++ b/src/device-sync/engine/contacts-sync.ts @@ -265,8 +265,7 @@ export class ContactsSync extends ItemSync { for (const group of await this.loadGroupsWhere()) { if (!group.sourceId || group.deleted) continue; this.groupRows.set(group.sourceId, group.groupId); - const keys = this.cardKeys(accountOfRef(group.sourceId), group.shadow); - if (keys.length > 0 && keys.every((k) => this.book(k)?.myRights?.mayWrite === false)) this.readOnlyGroups.add(group.sourceId); + if (this.inReadOnlyBooksOnly(accountOfRef(group.sourceId), group.shadow)) this.readOnlyGroups.add(group.sourceId); for (const uid of onIds(group.shadow?.members as Record | undefined)) { let set = this.membership.get(uid); if (!set) this.membership.set(uid, (set = new Set())); @@ -293,6 +292,12 @@ export class ContactsSync extends ItemSync { return !!book && book.myRights?.mayWrite !== false; } + /** Whether the card is in address books, and only in ones that take no writes. */ + private inReadOnlyBooksOnly(acct: string | null, card: Pick | null | undefined): boolean { + const keys = this.cardKeys(acct, card); + return keys.length > 0 && keys.every((k) => this.book(k)?.myRights?.mayWrite === false); + } + protected collectionGone(key: string): boolean { const parsed = parseCollectionKey(key); const books = parsed ? this.books.get(parsed.accountId) : undefined; @@ -373,10 +378,7 @@ export class ContactsSync extends ItemSync { mintKey: (taken) => env.mintKey(taken), mintUid: () => env.mintUid(), selectedCollections: (card) => this.cardKeys(acct, card).filter((k) => this.syncedKeys.has(k)), - isReadOnly: (card) => { - const keys = this.cardKeys(acct, card); - return keys.length > 0 && keys.every((k) => this.book(k)?.myRights?.mayWrite === false); - }, + isReadOnly: (card) => this.inReadOnlyBooksOnly(acct, card), isSelected: (key) => this.syncedKeys.has(key), createTarget: () => this.createTarget(), photoBytes: (entry) => this.photoBytes(entry), From 156e79b348833367cecfcba009b2ed822f4843a2 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:07:14 +0200 Subject: [PATCH 095/334] fix: keep a card or event another app moved away when it is deleted on the phone #34 A contact or event deleted on the phone that another client meanwhile filed in an address book or calendar the phone doesn't sync was destroyed on the server by the phone's next sync, and the other client's move with it. 0faee56 made the sync decide such a deletion on the server's version, but the planners still destroyed it: the contacts planner saw the card in no synced book and destroyed it, and the calendar planner could not take the new version at all (it placed the event before looking at the deletion), so the old one decided. The deletion is now moot when the server's version holds none of the collections the rows were written for and none the phone syncs: only the rows go, nothing is uploaded, and the object stays where the other client put it. A deletion still goes up when the book or calendar was deselected on the phone before the upload, and when the object was moved into another collection the phone syncs, since the phone would otherwise miss an object it should show. The calendar planner now lets a deleted row's shadow follow the server before it places the event. --- .../__tests__/calendar/lifecycle.test.ts | 28 ++++++++++++ .../__tests__/contacts/upload-editors.test.ts | 30 +++++++++++++ .../__tests__/engine/real-planners.test.ts | 45 ++++++++++++++++++- src/device-sync/calendar/create.ts | 14 ++++++ src/device-sync/calendar/planner.ts | 30 +++++++------ src/device-sync/contacts/upload.ts | 14 ++++++ src/device-sync/engine/item-sync.ts | 2 +- 7 files changed, 148 insertions(+), 15 deletions(-) diff --git a/src/device-sync/__tests__/calendar/lifecycle.test.ts b/src/device-sync/__tests__/calendar/lifecycle.test.ts index 038d2d00..9c9e8a4b 100644 --- a/src/device-sync/__tests__/calendar/lifecycle.test.ts +++ b/src/device-sync/__tests__/calendar/lifecycle.test.ts @@ -336,6 +336,34 @@ describe('calendar planner: accepted uploads, merges and deletions', () => { expect(await h.events()).toEqual([]); }); + it('leaves an event the server moved out of every synced calendar before the deletion reached it, and only purges the rows', async () => { + const { h, id } = await synced(single()); + const isSelected = h.ctx.isSelected; + h.ctx = { ...h.ctx, isSelected: (key) => key !== `${ACCT}/x` && isSelected(key) }; + h.fake.user.deleteEvent(id); + // The shadow follows the server although no synced calendar holds the event now. + expect(await h.download(single({ calendarIds: { x: true } }))).toMatchObject({ stillDirty: true, effect: 'update' }); + expect(h.row(id)).toMatchObject({ [Events.DELETED]: 1 }); + + const plan = calendarPlanner.planUpload((await h.local('e1'))!, h.ctx); + expect(plan.kind).toBe('purge'); + if (plan.kind === 'purge') await h.apply(plan.ops); + expect(await h.events()).toEqual([]); + }); + + it('still destroys an event the server moved to another synced calendar, or whose calendar was deselected before the upload', async () => { + const destroy = [{ kind: 'destroy', id: 'e1', uid: 'single-1@example.org' }]; + const moved = await synced(single()); + moved.h.fake.user.deleteEvent(moved.id); + await moved.h.download(single({ calendarIds: { w: true } })); + expect(actionsOf(calendarPlanner.planUpload((await moved.h.local('e1'))!, moved.h.ctx))).toEqual(destroy); + + const deselected = await synced(single()); + deselected.h.fake.user.deleteEvent(deselected.id); + deselected.h.ctx = { ...deselected.h.ctx, isSelected: () => false }; + expect(actionsOf(calendarPlanner.planUpload((await deselected.h.local('e1'))!, deselected.h.ctx))).toEqual(destroy); + }); + it('destroys a deleted event whose create had an unknown outcome by its uid', async () => { const h = await new Harness().setup(); const id = h.fake.user.insertEvent(h.rowIds.get('b')!, { [Events.TITLE]: 'x', [Events.DTSTART]: 0, [Events.DTEND]: 1000, [Events.EVENT_TIMEZONE]: 'UTC', [Events.STATUS]: 1 }); diff --git a/src/device-sync/__tests__/contacts/upload-editors.test.ts b/src/device-sync/__tests__/contacts/upload-editors.test.ts index d4783bef..eb2dd661 100644 --- a/src/device-sync/__tests__/contacts/upload-editors.test.ts +++ b/src/device-sync/__tests__/contacts/upload-editors.test.ts @@ -326,6 +326,36 @@ describe('contacts upload: deleted contacts', () => { expect(h.device.row('raw_contacts', rawId)).toBeUndefined(); }); + it('leaves a card the server moved out of every synced book before the deletion reached it, and only purges the rows', async () => { + const h = new Harness(); + const archive = h.server.addAddressBook(JMAP, { name: 'Archive' }); + h.unselected.add(`${JMAP}/${archive}`); + const { id, rawId } = await h.seed(appleCard()); + h.device.user.deleteContact(rawId); + h.server.serverUpdate('ContactCard', JMAP, id, { addressBookIds: { [archive]: true } }); + await h.applyOk(h.planner.planDownload(h.card(id), await h.contact(rawId), h.ctx).ops); + expect(await uploadPlan(h, rawId)).toMatchObject({ kind: 'purge' }); + await h.upload(rawId); + expect(h.device.row('raw_contacts', rawId)).toBeUndefined(); + expect(h.server.get('ContactCard', JMAP, id)).toMatchObject({ addressBookIds: { [archive]: true } }); + }); + + it('still destroys a card the server moved to another synced book, or whose book was deselected before the upload', async () => { + const moved = new Harness(); + const work = moved.server.addAddressBook(JMAP, { name: 'Work' }); + const a = await moved.seed(appleCard()); + moved.device.user.deleteContact(a.rawId); + moved.server.serverUpdate('ContactCard', JMAP, a.id, { addressBookIds: { [work]: true } }); + await moved.applyOk(moved.planner.planDownload(moved.card(a.id), await moved.contact(a.rawId), moved.ctx).ops); + expect(await uploadPlan(moved, a.rawId)).toEqual({ kind: 'upload', actions: [{ kind: 'destroy', id: a.id, uid: appleCard().uid }] }); + + const deselected = new Harness(); + const b = await deselected.seed(appleCard()); + deselected.device.user.deleteContact(b.rawId); + deselected.unselected.add(`${JMAP}/${deselected.book}`); + expect(await uploadPlan(deselected, b.rawId)).toEqual({ kind: 'upload', actions: [{ kind: 'destroy', id: b.id, uid: appleCard().uid }] }); + }); + it('deletes its own create by id once the download found it, not by a uid lookup', async () => { const h = new Harness(); const rawId = h.device.user.insertContact(ACCOUNT, [{ [Data.MIMETYPE]: MimeType.NOTE, data1: 'y' }]); diff --git a/src/device-sync/__tests__/engine/real-planners.test.ts b/src/device-sync/__tests__/engine/real-planners.test.ts index 0652f345..68596146 100644 --- a/src/device-sync/__tests__/engine/real-planners.test.ts +++ b/src/device-sync/__tests__/engine/real-planners.test.ts @@ -358,7 +358,25 @@ describe('device sync engine with the real planners', () => { expect(planned).toEqual([[archive]]); }); - it('still uploads the deletion of an event whose calendar the planner cannot place', async () => { + it('leaves a contact deleted on the device that another client moved out of every synced book', async () => { + const h = real(); + const archive = h.server.addAddressBook('a', { name: 'Archive' }); + h.prefs.contactsSelection[`a/${archive}`] = false; + const [ada] = addServerCards(h, ['Ada Lovelace', 'Grace Hopper']); + await h.run(); + h.device.user.deleteContact(h.contactNamed('Ada Lovelace')!.id); + // Another client files Ada in Archive only before the deletion reached the server. + h.server.serverUpdate('ContactCard', 'a', ada, { addressBookIds: { [archive]: true } }); + + const report = await h.run(); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, updated: 0, deleted: 0 } } }); + expect(h.server.get('ContactCard', 'a', ada)).toMatchObject({ addressBookIds: { [archive]: true } }); + expect(h.contacts().map((c) => c.name)).toEqual(['Grace Hopper']); + }); + + /** Lunch synced, deleted on the device, then filed by another client in Archive, which this device does not sync. */ + async function lunchDeletedAndFiledAway() { const h = real(); const archive = h.server.addCalendar('a', { name: 'Archive' }); h.prefs.calendarSelection[`a/${archive}`] = false; @@ -366,6 +384,31 @@ describe('device sync engine with the real planners', () => { await h.run(CALENDAR_AUTHORITY); h.device.user.deleteEvent(Number(h.events()[0]._id)); h.server.serverUpdate('CalendarEvent', 'a', lunch, { calendarIds: { [archive]: true } }); + return { h, archive, lunch }; + } + + it('leaves an event deleted on the device that another client moved out of every synced calendar', async () => { + const { h, archive, lunch } = await lunchDeletedAndFiledAway(); + + const report = await h.run(CALENDAR_AUTHORITY); + + expect(report).toMatchObject({ outcome: 'ok', itemErrors: [], stats: { uploaded: { created: 0, updated: 0, deleted: 0 } } }); + expect(h.server.get('CalendarEvent', 'a', lunch)).toMatchObject({ calendarIds: { [archive]: true } }); + expect(h.events()).toEqual([]); + }); + + it('still uploads the deletion as it was planned when the planner cannot take the server version', async () => { + const { h, lunch } = await lunchDeletedAndFiledAway(); + h.deps.planners = { + ...h.deps.planners, + calendar: { + ...calendarPlanner, + planDownload: (event, local, ctx) => { + if (local?.deleted) throw new Error('cannot take it'); + return calendarPlanner.planDownload(event, local, ctx); + }, + }, + }; const report = await h.run(CALENDAR_AUTHORITY); diff --git a/src/device-sync/calendar/create.ts b/src/device-sync/calendar/create.ts index e1ed3fa3..a2dadf9e 100644 --- a/src/device-sync/calendar/create.ts +++ b/src/device-sync/calendar/create.ts @@ -261,6 +261,20 @@ export function createTarget(local: LocalEvent, ctx: CalendarContext): { key: st // ─── Deletions ────────────────────────────────────────── +/** + * Whether the server moved a deleted event out of the calendar its row is in + * (`own`), and into none this device syncs, before the device's deletion + * reached it: the deletion is moot then, and the event stays where it is now. + * A calendar deselected since is still the row's own, so that deletion goes + * up; so does one of an event moved into another synced calendar. + */ +export function movedAway(local: LocalEvent, own: { jmapAccountId: string; calendarId: string }, ctx: CalendarContext): boolean { + if (!local.shadow) return false; + const ownKey = collectionKey(own.jmapAccountId, own.calendarId); + const keys = Object.entries(local.shadow.calendarIds ?? {}).filter(([, on]) => on).map(([id]) => collectionKey(ctx.jmapAccountId, id)); + return !keys.some((key) => key === ownKey || ctx.isSelected(key)); +} + /** * A deleted event with identity: an object in several calendars loses only * the selected memberships; otherwise it is destroyed, with scheduling diff --git a/src/device-sync/calendar/planner.ts b/src/device-sync/calendar/planner.ts index 7a01bbfd..540f4190 100644 --- a/src/device-sync/calendar/planner.ts +++ b/src/device-sync/calendar/planner.ts @@ -27,7 +27,7 @@ import { isTaskLikeObject } from '../../lib/calendar-component-detection'; import { keepDirtyGroup, type UploadedUnits } from './accepted'; import { ATTENDEE_COLUMNS, CALENDAR_COLUMNS, EVENT_COLUMNS, EXCEPTION_CELLS, MASTER_CELLS, REMINDER_COLUMNS, isTruncatedBaseline, makeBaseline } from './columns'; import { planCalendars } from './calendars'; -import { claimOps, claimUid, claimedUid, computeCreate, createTarget, deleteActions } from './create'; +import { claimOps, claimUid, claimedUid, computeCreate, createTarget, deleteActions, movedAway } from './create'; import { decodeCalendar, decodeEvents, isExceptionRow, isNewMaster } from './decode'; import { isExcluded, overridesOf } from './exceptions'; import { eventImage } from './image'; @@ -65,6 +65,20 @@ function planDownload(event: CalendarEventWire, local: LocalEvent | null, ctx: C refuseTask(local?.shadow); const ref = refOf(local, event, ctx.jmapAccountId); const none = (stillDirty: boolean): DownloadPlan => ({ ops: { ref, ops: [] }, conflicts: 0, stillDirty, effect: 'none', writes: 0 }); + // A different object under a reused id: its rows are replaced, never patched. + const uidChanged = !!local?.shadow && typeof local.shadow.uid === 'string' && typeof event.uid === 'string' && local.shadow.uid !== event.uid; + + if (local?.deleted && local.split !== 'clone' && !uidChanged) { + // Deleted on the device: that wins over a server edit, so only the shadow follows, for the deletion. + // Also when no synced calendar holds the event any more: the deletion is then moot (see planUpload). + if (!local.shadow || deepEqual(local.shadow, event)) return none(true); + const group = new GroupBuilder(ref); + group.assert({ op: 'assert', table: 'events', id: local.eventId, values: { [Events.DELETED]: 1, [Events._SYNC_ID]: local.syncId }, expectCount: 1 }); + group.write({ op: 'update', table: 'events', id: local.eventId, values: { [Events.SYNC_DATA1]: JSON.stringify(event) }, expectCount: 1 }); + const ops = group.build(); + return { ops, conflicts: 0, stillDirty: true, effect: 'update', writes: writesOf(ops) }; + } + // An event another client moved out of every synced calendar still merges into its rows where they are // (they wait for an upload; the accepted write then removes them). const target = pickCalendar(event, local, ctx) ?? (local ? rowCalendar(local, ctx) : null); @@ -80,8 +94,6 @@ function planDownload(event: CalendarEventWire, local: LocalEvent | null, ctx: C // A split clone only looks like this object: it is a new event and uploads as one. The source row is the object's. if (local.split === 'clone') return none(true); - // A different object under a reused id: its rows are replaced, never patched. - const uidChanged = !!local.shadow && typeof local.shadow.uid === 'string' && typeof event.uid === 'string' && local.shadow.uid !== event.uid; if (uidChanged) { const group = new GroupBuilder(ref); for (const x of local.exceptions) deleteRow(group, x.eventId); @@ -98,16 +110,6 @@ function planDownload(event: CalendarEventWire, local: LocalEvent | null, ctx: C return { ops, conflicts: 0, stillDirty: false, effect: 'update', writes: writesOf(ops) }; } - if (local.deleted) { - // Deleted on the device: that wins over a server edit, so only the shadow follows, for the destroy. - if (!local.shadow || deepEqual(local.shadow, event)) return none(true); - const group = new GroupBuilder(ref); - group.assert({ op: 'assert', table: 'events', id: local.eventId, values: { [Events.DELETED]: 1, [Events._SYNC_ID]: local.syncId }, expectCount: 1 }); - group.write({ op: 'update', table: 'events', id: local.eventId, values: { [Events.SYNC_DATA1]: JSON.stringify(event) }, expectCount: 1 }); - const ops = group.build(); - return { ops, conflicts: 0, stillDirty: true, effect: 'update', writes: writesOf(ops) }; - } - const itemDirty = local.dirty || local.split === 'source' || local.exceptions.some((x) => x.dirty || x.deleted); const result = writeEvent(event, image, target, local, { mode: 'merge', @@ -266,6 +268,8 @@ function planUpload(local: LocalEvent, ctx: CalendarContext): Plan { return { kind: 'upload', actions: [{ kind: 'destroy', id: null, uid }] }; } if (!parseObjectRef(local.syncId)) return { kind: 'purge', ops: purgeGroup(local) }; + // Moved away on the server before the deletion reached it: nothing goes up, only the rows go. + if (movedAway(local, calendar, ctx)) return { kind: 'purge', ops: purgeGroup(local) }; if (ctx.isReadOnly(calendar.calendarId)) return { kind: 'revert', ops: purgeGroup(local), refetch: true, reason: SKIP.readOnly }; return { kind: 'upload', actions: deleteActions(local, ctx) }; } diff --git a/src/device-sync/contacts/upload.ts b/src/device-sync/contacts/upload.ts index 98845503..cceb0ed6 100644 --- a/src/device-sync/contacts/upload.ts +++ b/src/device-sync/contacts/upload.ts @@ -215,10 +215,24 @@ function readOnlyOnServer(local: LocalContact, ctx: Ctx): boolean { return !!local.shadow && ctx.isReadOnly(withoutMemberOf(local.shadow)); } +/** + * Whether the server moved the card out of every book its rows were written + * for (SYNC1), and into none this device syncs, before the device's deletion + * reached it: the deletion is moot then, and the card stays where it is now. + * A book deselected since still counts as the rows' own, so that deletion + * goes up; so does one of a card moved into another synced book. + */ +function movedAway(local: LocalContact, accountId: string, ctx: Ctx): boolean { + if (!local.shadow || !local.collections.length) return false; + const books = Object.entries(local.shadow.addressBookIds ?? {}).filter(([, on]) => on).map(([id]) => collectionKey(accountId, id)); + return !books.some((key) => local.collections.includes(key) || ctx.isSelected(key)); +} + function deletedPlan(local: LocalContact, ctx: Ctx): UploadPlan { const purge = (): OpGroup => group(local, [deleteWhereId('raw_contacts', local.rawContactId)]); const ref = parseObjectRef(local.sourceId); if (ref) { + if (movedAway(local, ref.accountId, ctx)) return { kind: 'purge', ops: purge() }; if (readOnlyOnServer(local, ctx)) return { kind: 'revert', ops: purge(), refetch: true, reason: 'readOnly' }; const books = Object.entries(local.shadow?.addressBookIds ?? {}).filter(([, on]) => on).map(([id]) => id); const selected = books.filter((id) => ctx.isSelected(collectionKey(ref.accountId, id))); diff --git a/src/device-sync/engine/item-sync.ts b/src/device-sync/engine/item-sync.ts index c747ddae..1f72ec28 100644 --- a/src/device-sync/engine/item-sync.ts +++ b/src/device-sync/engine/item-sync.ts @@ -659,7 +659,7 @@ export abstract class ItemSync { try { return this.downloadWork(kind, acct, object, found.local, written); } catch (error) { - // A planner that cannot place it (no synced calendar) leaves the deletion as it was planned. + // A planner that cannot take the server version leaves the deletion as it was planned. this.env.log(`deleted ${refOf(acct, object.id)} outside the selection keeps its shadow`, error); return null; } From ae679a7d3d67ff5a6d047bcf8a3cde8981293ee9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:24:06 +0200 Subject: [PATCH 096/334] docs: list contacts and calendar sync with the phone as done on Android #34 --- PARITY_CHECKLIST.md | 2 +- README.md | 3 ++- docs/audit-2026-09.md | 6 +++--- docs/parity/06-contacts.md | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/PARITY_CHECKLIST.md b/PARITY_CHECKLIST.md index c6542d1e..9166f36e 100644 --- a/PARITY_CHECKLIST.md +++ b/PARITY_CHECKLIST.md @@ -153,7 +153,7 @@ Open split them by tick; the P columns count the priority tags on those items | #47 Account security not accessible | [01](docs/parity/01-auth-accounts.md) capability map bug (P1) | | #46 email preview shows css | [03](docs/parity/03-email-viewer.md) HTML-only body detection (P1) | | #45 error when re-registering to relay | [08](docs/parity/08-settings-push-i18n-ui.md) push section | -| #34 Native contacts/calendar sync adapters | feature request, out of parity scope; noted in [06](docs/parity/06-contacts.md) | +| #34 Native contacts/calendar sync adapters | done on Android: Settings → Contacts / Calendar → "Sync to this device" syncs both ways with the device's Contacts and Calendar apps ([design](docs/device-sync.md)); iOS has no API for it | | #5 Sort ascending/descending | in progress in the working tree; ordering presets in [02](docs/parity/02-mail-list-folders.md) | | #3 TLS client auth | Android-only today; gaps in [01](docs/parity/01-auth-accounts.md) and [09](docs/parity/09-jmap-core-sync-security.md) | | #1 Shared settings between webmail and native | [08](docs/parity/08-settings-push-i18n-ui.md) — webmail sync is server-side/cookie-bound; a JMAP-blob design is sketched there | diff --git a/README.md b/README.md index 87861346..6ca38d01 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,12 @@ React Native (Expo SDK 54) client for [Bulwark Webmail](https://github.com/bulwa - Calendar (basic) - Contacts (basic) - Push notifications via the Bulwark relay - FCM by default, or [UnifiedPush](https://unifiedpush.org) (e.g. ntfy) for devices without Google Play services +- Android: contacts and calendars sync both ways with the phone's Contacts and Calendar apps (Settings → Contacts / Calendar → Sync to this device) - In-app sideload updates from GitHub Releases ## What's missing or rough -- iOS builds, but push notifications and client certificates are Android-only so far +- iOS builds, but push notifications, client certificates and syncing with the phone's contacts and calendars are Android-only so far - S/MIME, plugins, themes - UI stubs only (filters & rules, the vacation responder and file storage are real implementations) - Calendar editing is partial; contacts editing is basic - No Play Store distribution yet (sideload APK from Releases); iOS ships via TestFlight diff --git a/docs/audit-2026-09.md b/docs/audit-2026-09.md index 0cfcdc48..2c0ccc98 100644 --- a/docs/audit-2026-09.md +++ b/docs/audit-2026-09.md @@ -1020,7 +1020,8 @@ ones moved to the N/A lists, and the wrong text corrected. | #46 email preview shows CSS | Not reproduced: HTML-only mail with `${renderBlocks(lines)}`; +} diff --git a/src/lib/preview-viewer-html.ts b/src/lib/preview-viewer-html.ts new file mode 100644 index 00000000..b5f4460e --- /dev/null +++ b/src/lib/preview-viewer-html.ts @@ -0,0 +1,104 @@ +// The pages the file preview shows images and media in. Each is written next +// to the downloaded file and loaded from there, so the WebView reads the +// file by a relative URL and nothing else on the device. They post 'error' +// when the WebView can't decode the file, so the preview can offer another +// app instead of a blank page or a dead player. + +export interface ViewerColors { + /** The app's theme, so the WebView draws its own controls to match. */ + scheme: 'light' | 'dark'; + background: string; + text: string; + textMuted: string; + surface: string; +} + +const ESCAPES: Record = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; + +function escapeHtml(s: string): string { + return s.replace(/[&<>"']/g, (ch) => ESCAPES[ch]); +} + +/** A file name as a relative URL: every reserved character encoded. */ +export function relativeFileUrl(fileName: string): string { + return encodeURIComponent(fileName).replace(/[!'()*]/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}`); +} + +const REPORT = `function report(m){try{window.ReactNativeWebView.postMessage(m)}catch(e){}}`; + +function page(title: string, colors: ViewerColors, viewport: string, style: string, body: string): string { + return '' + + `` + + `${escapeHtml(title)}` + + `` + + `${body}`; +} + +/** + * An image fitted to the screen, with the WebView's own pinch zoom. Used on + * Android, where React Native's Image can't zoom, and for SVG and ICO, which + * it can't draw. SVG is shown through , so its scripts never run and it + * loads nothing from the network. + */ +export function imageViewerHtml(fileName: string, colors: ViewerColors): string { + // Raster icons scaled up stay crisp instead of blurring. + const fillClass = /\.(ico|bmp|gif|png)$/i.test(fileName) ? 'fill pixel' : 'fill'; + return page( + fileName, + colors, + 'width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=10,user-scalable=yes', + 'body{display:flex;align-items:center;justify-content:center;box-sizing:border-box;padding:16px}' + + 'img{display:block;max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}' + // A vector or a tiny icon has no useful natural size; fill the screen. + + 'img.fill{width:100%;height:100%}' + + 'img.pixel{image-rendering:pixelated}', + `${escapeHtml(fileName)}` + + ``, + ); +} + +/** + * The platform's own player for a video or a sound. A video fills the + * screen on black, with fullscreen and picture in picture where the WebView + * offers them; a sound gets a card with its name. Playback starts at once. + */ +export function mediaViewerHtml(kind: 'video' | 'audio', fileName: string, colors: ViewerColors): string { + const src = relativeFileUrl(fileName); + const script = ``; + if (kind === 'video') { + return page( + fileName, + { ...colors, scheme: 'dark', background: '#000' }, + 'width=device-width,initial-scale=1', + 'body{display:flex;align-items:center;justify-content:center}video{width:100%;height:100%;object-fit:contain;background:#000}', + `${script}`, + ); + } + const note = ''; + return page( + fileName, + colors, + 'width=device-width,initial-scale=1', + 'body{display:flex;align-items:center;justify-content:center;box-sizing:border-box;padding:24px}' + + `.card{width:100%;max-width:480px;display:flex;flex-direction:column;align-items:center;gap:20px}` + + `.art{width:160px;height:160px;border-radius:24px;background:${colors.surface};color:${colors.textMuted};` + + 'display:flex;align-items:center;justify-content:center}' + + '.name{font-size:17px;font-weight:600;text-align:center;word-break:break-word}' + + 'audio{width:100%}', + `
${note}
${escapeHtml(fileName)}
` + + `
${script}`, + ); +} From 11e74042e28da3311fdf0048d1e7f7b0a21ca9a0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:11:50 +0200 Subject: [PATCH 245/334] fix: hand files with a generic type to other apps by the type their name implies --- src/lib/__tests__/file-preview.test.ts | 23 +++++++- src/lib/email-export.ts | 9 +-- src/lib/file-preview.ts | 78 ++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/src/lib/__tests__/file-preview.test.ts b/src/lib/__tests__/file-preview.test.ts index bdfe70c7..870d2b95 100644 --- a/src/lib/__tests__/file-preview.test.ts +++ b/src/lib/__tests__/file-preview.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { filePreviewKind, inAppPreviewKind, exceedsPreviewLimit, FILE_PREVIEW_MAX_BYTES, - needsNativeImageDecoder, needsWebImageViewer, + needsNativeImageDecoder, needsWebImageViewer, handoffMimeType, } from '../file-preview'; const IOS = { platform: 'ios' } as const; @@ -145,3 +145,24 @@ describe('preview size limits', () => { expect(inAppPreviewKind({ name: 'film.mp4', type: 'video/mp4', size: 300 * 1024 * 1024 }, ANDROID)).toBe('video'); }); }); + +describe('handoffMimeType', () => { + it('keeps a real declared type', () => { + expect(handoffMimeType('a.pdf', 'application/pdf')).toBe('application/pdf'); + expect(handoffMimeType('scan', 'image/png')).toBe('image/png'); + expect(handoffMimeType('a.zip', 'application/x-zip-compressed')).toBe('application/zip'); + }); + + it('names the format when the declared type does not, so Android finds a viewer', () => { + expect(handoffMimeType('contract.docx', 'application/octet-stream')) + .toBe('application/vnd.openxmlformats-officedocument.wordprocessingml.document'); + expect(handoffMimeType('invoice.PDF', '')).toBe('application/pdf'); + expect(handoffMimeType('invoice.pdf', 'application/x-as400attachment')).toBe('application/pdf'); + expect(handoffMimeType('photo.jpg', 'application/x-unknown')).toBe('image/jpeg'); + }); + + it('falls back to the declared type or octet-stream for unknown names', () => { + expect(handoffMimeType('data.bin', '')).toBe('application/octet-stream'); + expect(handoffMimeType('data.xyz', 'application/x-foo')).toBe('application/x-foo'); + }); +}); diff --git a/src/lib/email-export.ts b/src/lib/email-export.ts index b189caf2..c9b20160 100644 --- a/src/lib/email-export.ts +++ b/src/lib/email-export.ts @@ -14,6 +14,7 @@ import { } from './download-filename'; import { getClientCertAlias, secureFetch } from './client-cert'; import { sniffImageMime } from './email-html'; +import { handoffMimeType } from './file-preview'; const RFC822 = 'message/rfc822'; const FLAG_GRANT_READ_URI_PERMISSION = 0x00000001; @@ -156,7 +157,7 @@ async function openWithViewer(file: File, mimeType: string): Promise { // intent. Deliberately no FLAG_ACTIVITY_NEW_TASK: expo-intent-launcher // uses startActivityForResult, and a new task cancels that result. data: file.contentUri, - type: mimeType, + type: handoffMimeType(file.name, mimeType), flags: FLAG_GRANT_READ_URI_PERMISSION, }); return true; @@ -259,7 +260,7 @@ export async function shareAttachment( } try { await Sharing.shareAsync(downloaded.uri, { - mimeType, + mimeType: handoffMimeType(filename, mimeType), dialogTitle: filename, }); } finally { @@ -296,7 +297,7 @@ async function offerSavedFile(file: File, filename: string, mimeType: string): P throw new Error(t('files.share_unavailable', 'Sharing is not available on this device')); } await Sharing.shareAsync(file.uri, { - mimeType, + mimeType: handoffMimeType(filename, mimeType), dialogTitle: filename, }); } @@ -422,7 +423,7 @@ export async function shareLocalFile( throw new Error(t('files.share_unavailable', 'Sharing is not available on this device')); } try { - await Sharing.shareAsync(file.uri, { mimeType, dialogTitle: dialogTitle ?? file.name }); + await Sharing.shareAsync(file.uri, { mimeType: handoffMimeType(file.name, mimeType), dialogTitle: dialogTitle ?? file.name }); } finally { if (!opts.keep) scheduleTempCleanup(file); } diff --git a/src/lib/file-preview.ts b/src/lib/file-preview.ts index 0f9463ac..d0c1922f 100644 --- a/src/lib/file-preview.ts +++ b/src/lib/file-preview.ts @@ -179,3 +179,81 @@ export function needsWebImageViewer(file: Pick const mime = baseMime(file.type); return /^image\/(svg\+xml|x-icon|vnd\.microsoft\.icon|ico)$/.test(mime) || /\.(svg|ico)$/i.test(file.name); } + +// Types for handing a file to another app when the sender declared none, or +// a catch-all: Android picks the apps for "Open with" by type, and finds +// none for application/octet-stream. +const EXT_TO_MIME: Record = { + pdf: 'application/pdf', + doc: 'application/msword', + docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + xls: 'application/vnd.ms-excel', + xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ppt: 'application/vnd.ms-powerpoint', + pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + odt: 'application/vnd.oasis.opendocument.text', + ods: 'application/vnd.oasis.opendocument.spreadsheet', + odp: 'application/vnd.oasis.opendocument.presentation', + rtf: 'application/rtf', + epub: 'application/epub+zip', + zip: 'application/zip', + '7z': 'application/x-7z-compressed', + rar: 'application/vnd.rar', + gz: 'application/gzip', + tar: 'application/x-tar', + apk: 'application/vnd.android.package-archive', + txt: 'text/plain', + csv: 'text/csv', + html: 'text/html', + htm: 'text/html', + md: 'text/markdown', + json: 'application/json', + xml: 'application/xml', + ics: 'text/calendar', + vcf: 'text/vcard', + eml: 'message/rfc822', + png: 'image/png', + jpg: 'image/jpeg', + jpeg: 'image/jpeg', + gif: 'image/gif', + webp: 'image/webp', + heic: 'image/heic', + heif: 'image/heif', + avif: 'image/avif', + bmp: 'image/bmp', + tif: 'image/tiff', + tiff: 'image/tiff', + svg: 'image/svg+xml', + ico: 'image/x-icon', + mp3: 'audio/mpeg', + m4a: 'audio/mp4', + aac: 'audio/aac', + wav: 'audio/wav', + ogg: 'audio/ogg', + opus: 'audio/ogg', + flac: 'audio/flac', + amr: 'audio/amr', + mp4: 'video/mp4', + m4v: 'video/mp4', + mov: 'video/quicktime', + webm: 'video/webm', + mkv: 'video/x-matroska', + avi: 'video/x-msvideo', + wmv: 'video/x-ms-wmv', + '3gp': 'video/3gpp', +}; + +/** + * The type to hand a file to another app with: the declared one, unless it + * is generic or a catch-all that names no format, in which case the one the + * file name implies. + */ +export function handoffMimeType(name: string | undefined, type: string | null | undefined): string { + const declared = baseMime(type); + const fromName = EXT_TO_MIME[extension(name || '')]; + if (!fromName) return type || 'application/octet-stream'; + if (GENERIC_MIME.has(declared)) return fromName; + // An "x-" application type the name contradicts, such as x-as400attachment. + if (/^application\/x-/.test(declared) && declared !== fromName) return fromName; + return type || fromName; +} From b50436b1ba7bd7bb5ad2a54554c51e93d242ba41 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:12:43 +0200 Subject: [PATCH 246/334] chore: update version number to 0.1.80 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index b9dbcf61..8c1c54a7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.79 +0.1.80 diff --git a/package-lock.json b/package-lock.json index d1b1ce75..ce6fc623 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.79", + "version": "0.1.80", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.79", + "version": "0.1.80", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 0d30cc24..0cbe5215 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.79", + "version": "0.1.80", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From e5492b89c822a264893f118f9414a93f4852d20d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:23:17 +0200 Subject: [PATCH 247/334] feat: show PDFs in the app on Android --- .../bulwarkmobile/BulwarkFcmPackage.kt | 1 + .../bulwarkmobile/BulwarkPdfModule.kt | 141 ++++++++++++++++++ locales/rn/ar.json | 4 +- locales/rn/ca.json | 4 +- locales/rn/cs.json | 4 +- locales/rn/da.json | 4 +- locales/rn/de.json | 4 +- locales/rn/en.json | 4 +- locales/rn/es.json | 4 +- locales/rn/fa.json | 4 +- locales/rn/fr.json | 4 +- locales/rn/he.json | 4 +- locales/rn/hu.json | 4 +- locales/rn/it.json | 4 +- locales/rn/ja.json | 4 +- locales/rn/ko.json | 4 +- locales/rn/lv.json | 4 +- locales/rn/mn.json | 4 +- locales/rn/nb.json | 4 +- locales/rn/nl.json | 4 +- locales/rn/pl.json | 4 +- locales/rn/pt.json | 4 +- locales/rn/ro.json | 4 +- locales/rn/ru.json | 4 +- locales/rn/sk.json | 4 +- locales/rn/tr.json | 4 +- locales/rn/uk.json | 4 +- locales/rn/zh-TW.json | 4 +- locales/rn/zh.json | 4 +- .../email/AttachmentPreviewModal.tsx | 78 ++++++++-- src/components/email/use-file-preview.ts | 39 ++++- src/lib/__tests__/file-preview.test.ts | 5 +- src/lib/file-preview.ts | 7 +- src/lib/pdf-pages.ts | 67 +++++++++ src/lib/preview-viewer-html.ts | 38 +++++ 35 files changed, 434 insertions(+), 50 deletions(-) create mode 100644 android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt create mode 100644 src/lib/pdf-pages.ts diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt index 683ff9aa..71cb797a 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt @@ -15,6 +15,7 @@ class BulwarkFcmPackage : ReactPackage { add(BulwarkClientCertModule(reactContext)) add(BulwarkDeviceSyncModule(reactContext)) add(BulwarkWidgetsModule(reactContext)) + add(BulwarkPdfModule(reactContext)) // The bridge registers React Native's own module under this name. if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) { add(BulwarkHeadlessJsTaskSupportModule(reactContext)) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt new file mode 100644 index 00000000..c2461c1c --- /dev/null +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt @@ -0,0 +1,141 @@ +package com.anonymous.bulwarkmobile + +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.pdf.PdfRenderer +import android.net.Uri +import android.os.ParcelFileDescriptor +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import java.io.File +import java.io.FileOutputStream +import java.util.concurrent.Executors + +/** + * Renders PDF pages to PNG files for the in-app file preview, with the + * platform's own PdfRenderer: Android has no PDF view, and its WebView + * doesn't show PDFs either. The preview draws the pages in a WebView of + * its own so they zoom, asking for each page as it scrolls into view. + * + * PdfRenderer is not thread-safe and allows one open page at a time, so + * every call runs on one background thread, and the open document is kept + * between calls for the file the preview shows. + */ +class BulwarkPdfModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { + + private val worker = Executors.newSingleThreadExecutor { r -> Thread(r, "BulwarkPdf").apply { isDaemon = true } } + private var openPath: String? = null + private var descriptor: ParcelFileDescriptor? = null + private var renderer: PdfRenderer? = null + + override fun getName(): String = "BulwarkPdf" + + override fun invalidate() { + worker.execute { closeDocument() } + worker.shutdown() + super.invalidate() + } + + /** Page count and each page's size in points: `{ pages: [{ width, height }] }`. */ + @ReactMethod + fun open(fileUri: String, promise: Promise) { + worker.execute { + try { + val doc = document(fileUri) + val pages = Arguments.createArray() + for (i in 0 until doc.pageCount) { + doc.openPage(i).use { page -> + pages.pushMap(Arguments.createMap().apply { + putInt("width", page.width) + putInt("height", page.height) + }) + } + } + promise.resolve(Arguments.createMap().apply { putArray("pages", pages) }) + } catch (e: SecurityException) { + // Password-protected; PdfRenderer can't open those. + closeDocument() + promise.reject("E_PDF_PROTECTED", e.message, e) + } catch (e: Throwable) { + closeDocument() + promise.reject("E_PDF_OPEN", e.message, e) + } + } + } + + /** Render one page `width` pixels wide into `outUri` (a PNG). Resolves with the file URI. */ + @ReactMethod + fun renderPage(fileUri: String, index: Int, width: Int, outUri: String, promise: Promise) { + worker.execute { + try { + val doc = document(fileUri) + if (index < 0 || index >= doc.pageCount) throw IndexOutOfBoundsException("page $index") + doc.openPage(index).use { page -> + val w = width.coerceIn(1, MAX_PAGE_PX) + val h = (w.toLong() * page.height / page.width.coerceAtLeast(1)).toInt().coerceIn(1, MAX_PAGE_PX * 2) + val bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) + try { + // Pages are transparent where nothing is drawn. + bitmap.eraseColor(Color.WHITE) + page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) + val out = fileOf(outUri) + out.parentFile?.mkdirs() + FileOutputStream(out).use { stream -> bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) } + } finally { + bitmap.recycle() + } + } + promise.resolve(outUri) + } catch (e: Throwable) { + promise.reject("E_PDF_RENDER", e.message, e) + } + } + } + + /** Close the document the preview showed. */ + @ReactMethod + fun close(fileUri: String, promise: Promise) { + worker.execute { + if (openPath == fileOf(fileUri).absolutePath) closeDocument() + promise.resolve(null) + } + } + + private fun document(fileUri: String): PdfRenderer { + val path = fileOf(fileUri).absolutePath + renderer?.let { if (openPath == path) return it } + closeDocument() + val fd = ParcelFileDescriptor.open(File(path), ParcelFileDescriptor.MODE_READ_ONLY) + try { + val doc = PdfRenderer(fd) + descriptor = fd + renderer = doc + openPath = path + return doc + } catch (e: Throwable) { + fd.close() + throw e + } + } + + private fun closeDocument() { + try { renderer?.close() } catch (_: Throwable) {} + try { descriptor?.close() } catch (_: Throwable) {} + renderer = null + descriptor = null + openPath = null + } + + private fun fileOf(uri: String): File { + val path = if (uri.startsWith("file:")) Uri.parse(uri).path else uri + return File(path ?: throw IllegalArgumentException("not a file: $uri")) + } + + companion object { + // A 4096 px page bitmap is 64 MB; keep well below. + private const val MAX_PAGE_PX = 2480 + } +} diff --git a/locales/rn/ar.json b/locales/rn/ar.json index 549d87d4..31da86be 100644 --- a/locales/rn/ar.json +++ b/locales/rn/ar.json @@ -408,7 +408,9 @@ "open_external": "فتح في تطبيق", "pdf_external": "تُفتح ملفات PDF في عارض PDF على هذا الجهاز.", "unsupported": "لا توجد معاينة لهذا النوع من الملفات.", - "media_unsupported": "لا يمكن تشغيل هذا الملف هنا." + "media_unsupported": "لا يمكن تشغيل هذا الملف هنا.", + "pdf_protected": "ملف PDF هذا محمي بكلمة مرور. افتحه في تطبيق آخر لإدخال كلمة المرور.", + "pdf_unreadable": "لا يمكن عرض ملف PDF هذا هنا." }, "received_via": "إلى {name}", "remove_color": "إزالة الوسم", diff --git a/locales/rn/ca.json b/locales/rn/ca.json index 3168ddc8..0572c33e 100644 --- a/locales/rn/ca.json +++ b/locales/rn/ca.json @@ -408,7 +408,9 @@ "open_external": "Obre en una aplicació", "pdf_external": "Els PDF s'obren amb el visualitzador de PDF d'aquest dispositiu.", "unsupported": "No hi ha previsualització per a aquest tipus de fitxer.", - "media_unsupported": "Aquest fitxer no es pot reproduir aquí." + "media_unsupported": "Aquest fitxer no es pot reproduir aquí.", + "pdf_protected": "Aquest PDF està protegit amb contrasenya. Obre'l en una altra aplicació per introduir-la.", + "pdf_unreadable": "Aquest PDF no es pot mostrar aquí." }, "received_via": "per a {name}", "remove_color": "Elimina l'etiqueta", diff --git a/locales/rn/cs.json b/locales/rn/cs.json index 06ae3816..4bb3f3d0 100644 --- a/locales/rn/cs.json +++ b/locales/rn/cs.json @@ -408,7 +408,9 @@ "open_external": "Otevřít v aplikaci", "pdf_external": "Soubory PDF se otevírají v prohlížeči PDF na tomto zařízení.", "unsupported": "Pro tento typ souboru není náhled k dispozici.", - "media_unsupported": "Tento soubor zde nelze přehrát." + "media_unsupported": "Tento soubor zde nelze přehrát.", + "pdf_protected": "Toto PDF je chráněné heslem. Otevřete ho v jiné aplikaci a zadejte heslo.", + "pdf_unreadable": "Toto PDF zde nelze zobrazit." }, "received_via": "pro {name}", "remove_color": "Odebrat štítek", diff --git a/locales/rn/da.json b/locales/rn/da.json index 2e4d1b37..078ebb72 100644 --- a/locales/rn/da.json +++ b/locales/rn/da.json @@ -408,7 +408,9 @@ "open_external": "Åbn i app", "pdf_external": "PDF'er åbnes i din PDF-fremviser på denne enhed.", "unsupported": "Ingen forhåndsvisning for denne filtype.", - "media_unsupported": "Denne fil kan ikke afspilles her." + "media_unsupported": "Denne fil kan ikke afspilles her.", + "pdf_protected": "Denne PDF er beskyttet med adgangskode. Åbn den i en anden app for at indtaste adgangskoden.", + "pdf_unreadable": "Denne PDF kan ikke vises her." }, "received_via": "til {name}", "remove_color": "Fjern tag", diff --git a/locales/rn/de.json b/locales/rn/de.json index 813cf886..84ca7657 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -414,7 +414,9 @@ "open_external": "In App öffnen", "pdf_external": "PDFs werden in Ihrem PDF-Viewer auf diesem Gerät geöffnet.", "unsupported": "Keine Vorschau für diesen Dateityp.", - "media_unsupported": "Diese Datei kann hier nicht abgespielt werden." + "media_unsupported": "Diese Datei kann hier nicht abgespielt werden.", + "pdf_protected": "Diese PDF ist passwortgeschützt. Öffne sie in einer anderen App, um das Passwort einzugeben.", + "pdf_unreadable": "Diese PDF kann hier nicht angezeigt werden." }, "received_via": "an {name}", "remove_color": "Label entfernen", diff --git a/locales/rn/en.json b/locales/rn/en.json index a02d31a2..45cb669a 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -425,7 +425,9 @@ "open_external": "Open in app", "pdf_external": "PDFs open in your PDF viewer on this device.", "unsupported": "No preview for this file type.", - "media_unsupported": "This file can't be played here." + "media_unsupported": "This file can't be played here.", + "pdf_protected": "This PDF is password-protected. Open it in another app to enter the password.", + "pdf_unreadable": "This PDF can't be shown here." }, "received_via": "to {name}", "remove_color": "Remove tag", diff --git a/locales/rn/es.json b/locales/rn/es.json index 86836702..14e0df37 100644 --- a/locales/rn/es.json +++ b/locales/rn/es.json @@ -408,7 +408,9 @@ "open_external": "Abrir en una app", "pdf_external": "Los PDF se abren en el visor de PDF de este dispositivo.", "unsupported": "No hay vista previa para este tipo de archivo.", - "media_unsupported": "Este archivo no se puede reproducir aquí." + "media_unsupported": "Este archivo no se puede reproducir aquí.", + "pdf_protected": "Este PDF está protegido con contraseña. Ábrelo en otra aplicación para introducirla.", + "pdf_unreadable": "Este PDF no se puede mostrar aquí." }, "received_via": "para {name}", "remove_color": "Eliminar etiqueta", diff --git a/locales/rn/fa.json b/locales/rn/fa.json index ded4c31c..393c9e25 100644 --- a/locales/rn/fa.json +++ b/locales/rn/fa.json @@ -408,7 +408,9 @@ "open_external": "باز کردن در برنامه", "pdf_external": "فایل‌های PDF در نمایشگر PDF این دستگاه باز می‌شوند.", "unsupported": "برای این نوع فایل پیش‌نمایشی وجود ندارد.", - "media_unsupported": "این فایل اینجا پخش نمی‌شود." + "media_unsupported": "این فایل اینجا پخش نمی‌شود.", + "pdf_protected": "این PDF با رمز عبور محافظت شده است. برای وارد کردن رمز، آن را در برنامهٔ دیگری باز کنید.", + "pdf_unreadable": "این PDF اینجا نمایش داده نمی‌شود." }, "received_via": "به {name}", "remove_color": "حذف برچسب", diff --git a/locales/rn/fr.json b/locales/rn/fr.json index ce9ed8e3..8675882f 100644 --- a/locales/rn/fr.json +++ b/locales/rn/fr.json @@ -408,7 +408,9 @@ "open_external": "Ouvrir dans une app", "pdf_external": "Les PDF s'ouvrent dans votre lecteur PDF sur cet appareil.", "unsupported": "Aucun aperçu pour ce type de fichier.", - "media_unsupported": "Ce fichier ne peut pas être lu ici." + "media_unsupported": "Ce fichier ne peut pas être lu ici.", + "pdf_protected": "Ce PDF est protégé par un mot de passe. Ouvrez-le dans une autre application pour le saisir.", + "pdf_unreadable": "Ce PDF ne peut pas être affiché ici." }, "received_via": "à {name}", "remove_color": "Retirer l'étiquette", diff --git a/locales/rn/he.json b/locales/rn/he.json index 0e37ee86..b3331776 100644 --- a/locales/rn/he.json +++ b/locales/rn/he.json @@ -408,7 +408,9 @@ "open_external": "פתח באפליקציה", "pdf_external": "קובצי PDF נפתחים במציג ה-PDF במכשיר זה.", "unsupported": "אין תצוגה מקדימה לסוג קובץ זה.", - "media_unsupported": "לא ניתן להפעיל את הקובץ הזה כאן." + "media_unsupported": "לא ניתן להפעיל את הקובץ הזה כאן.", + "pdf_protected": "קובץ ה-PDF הזה מוגן בסיסמה. פתחו אותו באפליקציה אחרת כדי להזין את הסיסמה.", + "pdf_unreadable": "לא ניתן להציג את קובץ ה-PDF הזה כאן." }, "received_via": "אל {name}", "remove_color": "הסר תג", diff --git a/locales/rn/hu.json b/locales/rn/hu.json index 0f7f75c6..1f9b1370 100644 --- a/locales/rn/hu.json +++ b/locales/rn/hu.json @@ -408,7 +408,9 @@ "open_external": "Megnyitás alkalmazásban", "pdf_external": "A PDF-fájlok az eszköz PDF-megjelenítőjében nyílnak meg.", "unsupported": "Ehhez a fájltípushoz nincs előnézet.", - "media_unsupported": "Ez a fájl itt nem játszható le." + "media_unsupported": "Ez a fájl itt nem játszható le.", + "pdf_protected": "Ez a PDF jelszóval védett. Nyisd meg egy másik alkalmazásban a jelszó megadásához.", + "pdf_unreadable": "Ez a PDF itt nem jeleníthető meg." }, "received_via": "címzett: {name}", "remove_color": "Címke eltávolítása", diff --git a/locales/rn/it.json b/locales/rn/it.json index 6479ef03..512a386b 100644 --- a/locales/rn/it.json +++ b/locales/rn/it.json @@ -408,7 +408,9 @@ "open_external": "Apri in un'app", "pdf_external": "I PDF si aprono nel lettore PDF di questo dispositivo.", "unsupported": "Nessuna anteprima per questo tipo di file.", - "media_unsupported": "Questo file non può essere riprodotto qui." + "media_unsupported": "Questo file non può essere riprodotto qui.", + "pdf_protected": "Questo PDF è protetto da password. Aprilo in un'altra app per inserirla.", + "pdf_unreadable": "Questo PDF non può essere visualizzato qui." }, "received_via": "a {name}", "remove_color": "Rimuovi etichetta", diff --git a/locales/rn/ja.json b/locales/rn/ja.json index 8828cba6..96a00d49 100644 --- a/locales/rn/ja.json +++ b/locales/rn/ja.json @@ -408,7 +408,9 @@ "open_external": "アプリで開く", "pdf_external": "PDF はこのデバイスの PDF ビューアーで開きます。", "unsupported": "このファイル形式はプレビューできません。", - "media_unsupported": "このファイルはここでは再生できません。" + "media_unsupported": "このファイルはここでは再生できません。", + "pdf_protected": "このPDFはパスワードで保護されています。パスワードを入力するには別のアプリで開いてください。", + "pdf_unreadable": "このPDFはここでは表示できません。" }, "received_via": "{name}宛", "remove_color": "ラベルを削除", diff --git a/locales/rn/ko.json b/locales/rn/ko.json index cff3ef27..d9afa773 100644 --- a/locales/rn/ko.json +++ b/locales/rn/ko.json @@ -408,7 +408,9 @@ "open_external": "앱에서 열기", "pdf_external": "PDF는 이 기기의 PDF 뷰어에서 열려요.", "unsupported": "이 파일 형식은 미리보기를 지원하지 않아요.", - "media_unsupported": "이 파일은 여기서 재생할 수 없습니다." + "media_unsupported": "이 파일은 여기서 재생할 수 없습니다.", + "pdf_protected": "이 PDF는 암호로 보호되어 있습니다. 암호를 입력하려면 다른 앱에서 여세요.", + "pdf_unreadable": "이 PDF는 여기서 표시할 수 없습니다." }, "received_via": "받는 사람: {name}", "remove_color": "태그 제거", diff --git a/locales/rn/lv.json b/locales/rn/lv.json index 09342da2..3d413883 100644 --- a/locales/rn/lv.json +++ b/locales/rn/lv.json @@ -408,7 +408,9 @@ "open_external": "Atvērt lietotnē", "pdf_external": "PDF faili tiek atvērti šīs ierīces PDF skatītājā.", "unsupported": "Šim faila tipam nav priekšskatījuma.", - "media_unsupported": "Šo failu šeit nevar atskaņot." + "media_unsupported": "Šo failu šeit nevar atskaņot.", + "pdf_protected": "Šis PDF ir aizsargāts ar paroli. Atveriet to citā lietotnē, lai ievadītu paroli.", + "pdf_unreadable": "Šo PDF šeit nevar parādīt." }, "received_via": "kam: {name}", "remove_color": "Noņemt tagu", diff --git a/locales/rn/mn.json b/locales/rn/mn.json index 6a3f0bfd..6d63704b 100644 --- a/locales/rn/mn.json +++ b/locales/rn/mn.json @@ -408,7 +408,9 @@ "open_external": "Апп-д нээх", "pdf_external": "PDF файлууд энэ төхөөрөмжийн PDF үзэгчид нээгдэнэ.", "unsupported": "Энэ төрлийн файлыг урьдчилан харах боломжгүй.", - "media_unsupported": "Энэ файлыг энд тоглуулах боломжгүй." + "media_unsupported": "Энэ файлыг энд тоглуулах боломжгүй.", + "pdf_protected": "Энэ PDF нууц үгээр хамгаалагдсан. Нууц үг оруулахын тулд өөр апп-аар нээнэ үү.", + "pdf_unreadable": "Энэ PDF-ийг энд харуулах боломжгүй." }, "received_via": "Хэнд: {name}", "remove_color": "Тагийг хасах", diff --git a/locales/rn/nb.json b/locales/rn/nb.json index 0f5cf1dd..a6612ee1 100644 --- a/locales/rn/nb.json +++ b/locales/rn/nb.json @@ -408,7 +408,9 @@ "open_external": "Åpne i app", "pdf_external": "PDF-filer åpnes i PDF-leseren på denne enheten.", "unsupported": "Ingen forhåndsvisning for denne filtypen.", - "media_unsupported": "Denne filen kan ikke spilles av her." + "media_unsupported": "Denne filen kan ikke spilles av her.", + "pdf_protected": "Denne PDF-en er passordbeskyttet. Åpne den i en annen app for å skrive inn passordet.", + "pdf_unreadable": "Denne PDF-en kan ikke vises her." }, "received_via": "til {name}", "remove_color": "Fjern etiketten", diff --git a/locales/rn/nl.json b/locales/rn/nl.json index c7f279a1..e5006356 100644 --- a/locales/rn/nl.json +++ b/locales/rn/nl.json @@ -408,7 +408,9 @@ "open_external": "Openen in app", "pdf_external": "PDF's worden geopend in de PDF-viewer op dit apparaat.", "unsupported": "Geen voorbeeld voor dit bestandstype.", - "media_unsupported": "Dit bestand kan hier niet worden afgespeeld." + "media_unsupported": "Dit bestand kan hier niet worden afgespeeld.", + "pdf_protected": "Deze pdf is beveiligd met een wachtwoord. Open hem in een andere app om het wachtwoord in te voeren.", + "pdf_unreadable": "Deze pdf kan hier niet worden weergegeven." }, "received_via": "aan {name}", "remove_color": "Label verwijderen", diff --git a/locales/rn/pl.json b/locales/rn/pl.json index 43613007..249f3294 100644 --- a/locales/rn/pl.json +++ b/locales/rn/pl.json @@ -408,7 +408,9 @@ "open_external": "Otwórz w aplikacji", "pdf_external": "Pliki PDF otwierają się w przeglądarce PDF na tym urządzeniu.", "unsupported": "Brak podglądu dla tego typu pliku.", - "media_unsupported": "Tego pliku nie można tutaj odtworzyć." + "media_unsupported": "Tego pliku nie można tutaj odtworzyć.", + "pdf_protected": "Ten plik PDF jest chroniony hasłem. Otwórz go w innej aplikacji, aby wpisać hasło.", + "pdf_unreadable": "Tego pliku PDF nie można tutaj wyświetlić." }, "received_via": "do {name}", "remove_color": "Usuń etykietę", diff --git a/locales/rn/pt.json b/locales/rn/pt.json index 06e4ff49..17be6f02 100644 --- a/locales/rn/pt.json +++ b/locales/rn/pt.json @@ -408,7 +408,9 @@ "open_external": "Abrir em outro app", "pdf_external": "PDFs são abertos no leitor de PDF deste dispositivo.", "unsupported": "Sem pré-visualização para este tipo de arquivo.", - "media_unsupported": "Este ficheiro não pode ser reproduzido aqui." + "media_unsupported": "Este ficheiro não pode ser reproduzido aqui.", + "pdf_protected": "Este PDF está protegido por palavra-passe. Abra-o noutra aplicação para a introduzir.", + "pdf_unreadable": "Este PDF não pode ser mostrado aqui." }, "received_via": "para {name}", "remove_color": "Remover etiqueta", diff --git a/locales/rn/ro.json b/locales/rn/ro.json index 782dc7ed..a86f5a0d 100644 --- a/locales/rn/ro.json +++ b/locales/rn/ro.json @@ -408,7 +408,9 @@ "open_external": "Deschideți într-o aplicație", "pdf_external": "Fișierele PDF se deschid în vizualizatorul PDF de pe acest dispozitiv.", "unsupported": "Nu există previzualizare pentru acest tip de fișier.", - "media_unsupported": "Acest fișier nu poate fi redat aici." + "media_unsupported": "Acest fișier nu poate fi redat aici.", + "pdf_protected": "Acest PDF este protejat cu parolă. Deschide-l în altă aplicație pentru a introduce parola.", + "pdf_unreadable": "Acest PDF nu poate fi afișat aici." }, "received_via": "către {name}", "remove_color": "Eliminați eticheta", diff --git a/locales/rn/ru.json b/locales/rn/ru.json index 483fe7f2..f12ec606 100644 --- a/locales/rn/ru.json +++ b/locales/rn/ru.json @@ -408,7 +408,9 @@ "open_external": "Открыть в приложении", "pdf_external": "PDF-файлы открываются в программе просмотра PDF на этом устройстве.", "unsupported": "Для этого типа файлов нет предпросмотра.", - "media_unsupported": "Этот файл нельзя воспроизвести здесь." + "media_unsupported": "Этот файл нельзя воспроизвести здесь.", + "pdf_protected": "Этот PDF защищён паролем. Откройте его в другом приложении, чтобы ввести пароль.", + "pdf_unreadable": "Этот PDF нельзя показать здесь." }, "received_via": "на {name}", "remove_color": "Убрать тег", diff --git a/locales/rn/sk.json b/locales/rn/sk.json index a561cf0d..26494ae8 100644 --- a/locales/rn/sk.json +++ b/locales/rn/sk.json @@ -408,7 +408,9 @@ "open_external": "Otvoriť v aplikácii", "pdf_external": "Súbory PDF sa otvárajú v prehliadači PDF na tomto zariadení.", "unsupported": "Pre tento typ súboru nie je k dispozícii náhľad.", - "media_unsupported": "Tento súbor tu nie je možné prehrať." + "media_unsupported": "Tento súbor tu nie je možné prehrať.", + "pdf_protected": "Toto PDF je chránené heslom. Otvorte ho v inej aplikácii a zadajte heslo.", + "pdf_unreadable": "Toto PDF tu nie je možné zobraziť." }, "received_via": "pre {name}", "remove_color": "Odstrániť štítok", diff --git a/locales/rn/tr.json b/locales/rn/tr.json index af1e539e..be06fdab 100644 --- a/locales/rn/tr.json +++ b/locales/rn/tr.json @@ -408,7 +408,9 @@ "open_external": "Uygulamada aç", "pdf_external": "PDF dosyaları bu cihazdaki PDF görüntüleyicide açılır.", "unsupported": "Bu dosya türü için önizleme yok.", - "media_unsupported": "Bu dosya burada oynatılamıyor." + "media_unsupported": "Bu dosya burada oynatılamıyor.", + "pdf_protected": "Bu PDF parola korumalı. Parolayı girmek için başka bir uygulamada açın.", + "pdf_unreadable": "Bu PDF burada gösterilemiyor." }, "received_via": "alıcı: {name}", "remove_color": "Etiketi kaldır", diff --git a/locales/rn/uk.json b/locales/rn/uk.json index 666e4504..1be0e304 100644 --- a/locales/rn/uk.json +++ b/locales/rn/uk.json @@ -408,7 +408,9 @@ "open_external": "Відкрити в застосунку", "pdf_external": "PDF-файли відкриваються в переглядачі PDF на цьому пристрої.", "unsupported": "Для цього типу файлів немає попереднього перегляду.", - "media_unsupported": "Цей файл не можна відтворити тут." + "media_unsupported": "Цей файл не можна відтворити тут.", + "pdf_protected": "Цей PDF захищено паролем. Відкрийте його в іншому застосунку, щоб ввести пароль.", + "pdf_unreadable": "Цей PDF не можна показати тут." }, "received_via": "на {name}", "remove_color": "Прибрати тег", diff --git a/locales/rn/zh-TW.json b/locales/rn/zh-TW.json index c577ec2a..14e41e90 100644 --- a/locales/rn/zh-TW.json +++ b/locales/rn/zh-TW.json @@ -408,7 +408,9 @@ "open_external": "在應用程式中開啟", "pdf_external": "PDF 會在此裝置的 PDF 檢視器中開啟。", "unsupported": "無法預覽此檔案類型。", - "media_unsupported": "無法在此播放這個檔案。" + "media_unsupported": "無法在此播放這個檔案。", + "pdf_protected": "此 PDF 受密碼保護。請在其他應用程式中開啟以輸入密碼。", + "pdf_unreadable": "無法在此顯示這個 PDF。" }, "received_via": "寄給 {name}", "remove_color": "移除標籤", diff --git a/locales/rn/zh.json b/locales/rn/zh.json index 0a27a7fc..d0ab9384 100644 --- a/locales/rn/zh.json +++ b/locales/rn/zh.json @@ -408,7 +408,9 @@ "open_external": "在应用中打开", "pdf_external": "PDF 将在此设备的 PDF 查看器中打开。", "unsupported": "无法预览此类文件。", - "media_unsupported": "无法在此播放该文件。" + "media_unsupported": "无法在此播放该文件。", + "pdf_protected": "此 PDF 受密码保护。请在其他应用中打开以输入密码。", + "pdf_unreadable": "无法在此显示该 PDF。" }, "received_via": "发给 {name}", "remove_color": "删除标签", diff --git a/src/components/email/AttachmentPreviewModal.tsx b/src/components/email/AttachmentPreviewModal.tsx index 0b94b90e..da81ec10 100644 --- a/src/components/email/AttachmentPreviewModal.tsx +++ b/src/components/email/AttachmentPreviewModal.tsx @@ -1,9 +1,10 @@ import React from 'react'; import { - View, Text, StyleSheet, Modal, Pressable, ScrollView, Image, ActivityIndicator, Platform, useWindowDimensions, + View, Text, StyleSheet, Modal, Pressable, ScrollView, Image, ActivityIndicator, Platform, PixelRatio, + useWindowDimensions, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { WebView } from 'react-native-webview'; +import { WebView, type WebViewMessageEvent } from 'react-native-webview'; import { X, ExternalLink, Share2, Download } from '../icons'; import type { Email } from '../../api/types'; import { spacing, radius, typography, componentSizes, type ThemePalette } from '../../theme/tokens'; @@ -11,6 +12,7 @@ import { useColors } from '../../theme/colors'; import { useLocaleStore } from '../../stores/locale-store'; import EmailBodyView from '../EmailBodyView'; import { needsNativeImageDecoder, needsWebImageViewer, type PreviewKind } from '../../lib/file-preview'; +import { pdfPageFileName, renderPdfPage } from '../../lib/pdf-pages'; import type { EmlPreview } from './use-body-override'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; @@ -20,7 +22,7 @@ export interface PreviewItem { mimeType: string; /** Local file:// URI of the downloaded copy (images, PDFs, media). */ fileUri?: string; - /** The viewer page next to the file (images, video, audio); see preview-viewer-html. */ + /** The viewer page next to the file (images, video, audio, Android's PDFs); see preview-viewer-html. */ viewerUri?: string; /** Decoded text for text-like parts. */ text?: string; @@ -47,14 +49,28 @@ interface Props { docked?: boolean; } +interface ViewerWebViewProps { + uri: string; + background: string; + onFailed: () => void; + /** Messages other than 'error'. */ + onMessage?: (data: string) => void; + webRef?: React.Ref; +} + /** - * A local page in a WebView that may read only its own folder: the image - * and media viewers. It posts 'error' when the file doesn't decode. + * A local page in a WebView that may read only its own folder: the image, + * media and PDF viewers. It posts 'error' when the file doesn't decode. */ -function ViewerWebView({ uri, background, onFailed }: { uri: string; background: string; onFailed: () => void }) { +function ViewerWebView({ uri, background, onFailed, onMessage, webRef }: ViewerWebViewProps) { const folder = uri.slice(0, uri.lastIndexOf('/') + 1); + const handleMessage = (e: WebViewMessageEvent) => { + if (e.nativeEvent.data === 'error') onFailed(); + else onMessage?.(e.nativeEvent.data); + }; return ( req.url === uri} - onMessage={(e) => { if (e.nativeEvent.data === 'error') onFailed(); }} + onMessage={handleMessage} onError={onFailed} style={[viewerStyles.fill, { backgroundColor: background }]} containerStyle={{ backgroundColor: background }} @@ -82,8 +98,39 @@ function ViewerWebView({ uri, background, onFailed }: { uri: string; background: ); } +// Pages are rendered half again as wide as the screen's pixels, so text stays +// sharp at the first zoom step; PdfRenderer's bitmap is capped natively. +const PDF_PAGE_OVERSAMPLE = 1.5; + /** - * In-app preview for images, PDFs (iOS renders them in the WebView), video + * Android's PDF view: the viewer page asks for each page as it comes near + * the screen, and the page is rendered next to it by the platform's + * PdfRenderer (pdf-pages.ts). + */ +function PdfPagesView({ fileUri, viewerUri, background, onFailed }: { + fileUri: string; + viewerUri: string; + background: string; + onFailed: () => void; +}) { + const ref = React.useRef(null); + const { width } = useWindowDimensions(); + const pagePx = PixelRatio.getPixelSizeForLayoutSize(Math.min(width, 1000)) * PDF_PAGE_OVERSAMPLE; + const folder = viewerUri.slice(0, viewerUri.lastIndexOf('/') + 1); + const onMessage = (data: string) => { + const m = /^page:(\d+)$/.exec(data); + if (!m) return; + const index = Number(m[1]); + const name = pdfPageFileName(index); + renderPdfPage(fileUri, index, pagePx, folder + name) + .then(() => ref.current?.injectJavaScript(`window.__pdfPage(${index}, ${JSON.stringify(name)});true;`)) + .catch(() => undefined); + }; + return ; +} + +/** + * In-app preview for images, PDFs (iOS's WebView shows them, Android renders the pages), video * and audio (the WebView's player), text, Markdown and HTML documents and * embedded messages. Files that don't decode, and types without a preview, * offer another app instead. Used by every place that opens a file. @@ -133,7 +180,7 @@ export function AttachmentPreviewModal({ || (!!failure?.native && !item.viewerUri) ); const mediaFailed = (item?.kind === 'video' || item?.kind === 'audio') && !!failure?.web; - const pdfInline = item?.kind === 'pdf' && Platform.OS === 'ios' && !!item.fileUri; + const pdfInline = item?.kind === 'pdf' && !!item.fileUri && (Platform.OS === 'ios' || !!item.viewerUri) && !failure?.web; // The fallback screen has its own Open button; the header adds one elsewhere. const showsFallback = !!item && ( item.kind === 'none' || (item.kind === 'pdf' && !pdfInline) || imageFailed || mediaFailed @@ -216,6 +263,17 @@ export function AttachmentPreviewModal({ /> ); case 'pdf': + if (pdfInline && item.fileUri && item.viewerUri) { + return ( + markFailed(item.fileUri, 'web')} + /> + ); + } if (pdfInline && item.fileUri) { return ( ); } - return renderFallback(t('email_viewer.preview.pdf_external', 'PDFs open in your PDF viewer on this device.')); + return renderFallback(t('email_viewer.preview.pdf_unreadable', "This PDF can't be shown here.")); case 'text': return ( diff --git a/src/components/email/use-file-preview.ts b/src/components/email/use-file-preview.ts index d18888d4..6cc83a8c 100644 --- a/src/components/email/use-file-preview.ts +++ b/src/components/email/use-file-preview.ts @@ -11,7 +11,8 @@ import { } from '../../lib/file-preview'; import type { PreviewKind } from '../../lib/file-preview'; import { markdownToHtml } from '../../lib/markdown-html'; -import { imageViewerHtml, mediaViewerHtml, type ViewerColors } from '../../lib/preview-viewer-html'; +import { imageViewerHtml, mediaViewerHtml, pdfViewerHtml, type ViewerColors } from '../../lib/preview-viewer-html'; +import { closePdfPages, openPdfPages, PdfOpenError } from '../../lib/pdf-pages'; import { useLocaleStore } from '../../stores/locale-store'; import { useColors, useResolvedTheme } from '../../theme/colors'; import type { PreviewItem } from './AttachmentPreviewModal'; @@ -74,16 +75,22 @@ function writeViewer(local: File, html: string): string { return page.uri; } +interface Notices { + tooLarge: string; + pdfProtected: string; + pdfUnreadable: string; +} + async function buildItem( kind: Exclude, source: PreviewSource, local: File, - tooLarge: string, + notices: Notices, colors: ViewerColors, ): Promise { const base = { name: source.name, mimeType: source.type }; // The source may not know its size; the copy does. - if (exceedsPreviewLimit(kind, local.size)) return { ...base, kind: 'none', notice: tooLarge }; + if (exceedsPreviewLimit(kind, local.size)) return { ...base, kind: 'none', notice: notices.tooLarge }; switch (kind) { case 'text': return { ...base, kind, text: await local.text() }; @@ -98,6 +105,17 @@ async function buildItem( case 'video': case 'audio': return { ...base, kind, fileUri: local.uri, viewerUri: writeViewer(local, mediaViewerHtml(kind, local.name, colors)) }; + case 'pdf': { + // iOS's WebView shows the PDF itself; Android renders its pages. + if (Platform.OS !== 'android') return { ...base, kind, fileUri: local.uri }; + try { + const pages = await openPdfPages(local.uri); + return { ...base, kind, fileUri: local.uri, viewerUri: writeViewer(local, pdfViewerHtml(local.name, pages, colors)) }; + } catch (e) { + const protectedPdf = e instanceof PdfOpenError && e.reason === 'protected'; + return { ...base, kind: 'none', notice: protectedPdf ? notices.pdfProtected : notices.pdfUnreadable }; + } + } default: return { ...base, kind, fileUri: local.uri }; } @@ -141,7 +159,9 @@ export function useFilePreview(opts: { seq.current += 1; const open = session.current; session.current = null; - if (open && !open.handedOff) discardPreviewFile(open.file); + if (!open) return; + closePdfPages(open.file.uri); + if (!open.handedOff) discardPreviewFile(open.file); }, []); React.useEffect(() => end, [end]); @@ -181,9 +201,14 @@ export function useFilePreview(opts: { return; } session.current = { file: local, handedOff: false }; - const next = await buildItem( - kind, src, local, t('files.preview_too_large', 'This file is too large to preview.'), colors.current, - ); + const next = await buildItem(kind, src, local, { + tooLarge: t('files.preview_too_large', 'This file is too large to preview.'), + pdfProtected: t( + 'email_viewer.preview.pdf_protected', + 'This PDF is password-protected. Open it in another app to enter the password.', + ), + pdfUnreadable: t('email_viewer.preview.pdf_unreadable', "This PDF can't be shown here."), + }, colors.current); if (current()) setItem(next); } catch (e) { if (!current()) return; diff --git a/src/lib/__tests__/file-preview.test.ts b/src/lib/__tests__/file-preview.test.ts index 870d2b95..f4c6823a 100644 --- a/src/lib/__tests__/file-preview.test.ts +++ b/src/lib/__tests__/file-preview.test.ts @@ -37,10 +37,11 @@ describe('filePreviewKind', () => { expect(filePreviewKind({ name: 'notes', type: 'text/markdown' }, ANDROID)).toBe('markdown'); }); - it('shows PDFs in-app only where the platform renders them', () => { + it('shows PDFs in-app on both platforms', () => { expect(filePreviewKind({ name: 'report.pdf', type: 'application/pdf' }, IOS)).toBe('pdf'); expect(filePreviewKind({ name: 'report.pdf', type: 'application/octet-stream' }, IOS)).toBe('pdf'); - expect(filePreviewKind({ name: 'report.pdf', type: 'application/pdf' }, ANDROID)).toBe('none'); + expect(filePreviewKind({ name: 'report.pdf', type: 'application/pdf' }, ANDROID)).toBe('pdf'); + expect(filePreviewKind({ name: 'REPORT.PDF', type: '' }, ANDROID)).toBe('pdf'); }); it('plays the video formats each platform plays', () => { diff --git a/src/lib/file-preview.ts b/src/lib/file-preview.ts index d0c1922f..8b707074 100644 --- a/src/lib/file-preview.ts +++ b/src/lib/file-preview.ts @@ -11,7 +11,7 @@ export interface FilePreviewOptions { /** * The platform's viewers decide what plays: video and audio play in the * system WebView (Chromium on Android, WebKit on iOS), whose formats - * differ, and only iOS renders PDFs in-app. + * differ. */ platform: 'ios' | 'android'; } @@ -139,9 +139,8 @@ export function filePreviewKind(file: Pick, op || (ios && (IOS_IMAGE_MIME.has(mime) || IOS_IMAGE_EXT.has(ext))); return shown ? 'image' : 'none'; } - if (mime === 'application/pdf' || (!media && ext === 'pdf')) { - return opts.platform === 'ios' ? 'pdf' : 'none'; - } + // iOS's WebView shows PDFs; Android renders their pages (pdf-pages.ts). + if (mime === 'application/pdf' || (!media && ext === 'pdf')) return 'pdf'; for (const kind of ['video', 'audio'] as const) { const formats = MEDIA[kind][opts.platform]; if (mime.startsWith(`${kind}/`)) return formats.mime.has(mime) || formats.ext.has(ext) ? kind : 'none'; diff --git a/src/lib/pdf-pages.ts b/src/lib/pdf-pages.ts new file mode 100644 index 00000000..16247471 --- /dev/null +++ b/src/lib/pdf-pages.ts @@ -0,0 +1,67 @@ +// Android's PDF pages for the file preview, rendered by BulwarkPdfModule +// (the platform's PdfRenderer) into PNGs next to the PDF. iOS needs none of +// this: its WebView shows PDFs itself. + +import { NativeModules, Platform } from 'react-native'; + +export interface PdfPageSize { + /** In points. */ + width: number; + height: number; +} + +interface NativePdf { + open(fileUri: string): Promise<{ pages: PdfPageSize[] }>; + renderPage(fileUri: string, index: number, width: number, outUri: string): Promise; + close(fileUri: string): Promise; +} + +function native(): NativePdf | null { + if (Platform.OS !== 'android') return null; + return (NativeModules.BulwarkPdf as NativePdf | undefined) ?? null; +} + +/** Whether this build renders PDFs in-app. */ +export function canRenderPdfPages(): boolean { + return native() !== null; +} + +export type PdfOpenFailure = 'protected' | 'unreadable'; + +export class PdfOpenError extends Error { + constructor(readonly reason: PdfOpenFailure, message: string) { + super(message); + } +} + +/** The pages of a PDF, or a {@link PdfOpenError} saying why it can't be shown. */ +export async function openPdfPages(fileUri: string): Promise { + const pdf = native(); + if (!pdf) throw new PdfOpenError('unreadable', 'PDF rendering is not available'); + try { + const { pages } = await pdf.open(fileUri); + if (!pages.length) throw new PdfOpenError('unreadable', 'The PDF has no pages'); + return pages; + } catch (e) { + if (e instanceof PdfOpenError) throw e; + const code = (e as { code?: string } | null)?.code; + throw new PdfOpenError(code === 'E_PDF_PROTECTED' ? 'protected' : 'unreadable', e instanceof Error ? e.message : String(e)); + } +} + +/** Render page `index` (0-based) `width` pixels wide into `outUri`. */ +export function renderPdfPage(fileUri: string, index: number, width: number, outUri: string): Promise { + const pdf = native(); + if (!pdf) return Promise.reject(new Error('PDF rendering is not available')); + return pdf.renderPage(fileUri, index, Math.round(width), outUri); +} + +/** Let go of the open document. */ +export function closePdfPages(fileUri: string): void { + void native()?.close(fileUri).catch(() => undefined); +} + +/** Name of the image a page is rendered into, next to the viewer page. */ +export function pdfPageFileName(index: number): string { + return `__page-${index + 1}.png`; +} diff --git a/src/lib/preview-viewer-html.ts b/src/lib/preview-viewer-html.ts index b5f4460e..c98b6134 100644 --- a/src/lib/preview-viewer-html.ts +++ b/src/lib/preview-viewer-html.ts @@ -102,3 +102,41 @@ export function mediaViewerHtml(kind: 'video' | 'audio', fileName: string, color + `${script}`, ); } + +/** + * Android's PDF viewer: one placeholder per page at the page's proportions, + * filled with an image the app renders when the page comes near the screen + * (it posts `page:` and the app answers by calling + * `window.__pdfPage(index, src)`). The WebView zooms the whole document, and a + * badge shows the page at the middle of the screen. + */ +export function pdfViewerHtml( + fileName: string, + pages: ReadonlyArray<{ width: number; height: number }>, + colors: ViewerColors, +): string { + const sizes = JSON.stringify(pages.map((p) => [Math.max(1, Math.round(p.width)), Math.max(1, Math.round(p.height))])); + return page( + fileName, + { ...colors, background: colors.surface }, + 'width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=6,user-scalable=yes', + 'body{height:auto;min-height:100%}' + + '#doc{padding:8px 8px 56px;display:flex;flex-direction:column;align-items:center;gap:8px}' + + '.page{width:100%;max-width:1000px;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.25);position:relative}' + + '.page img{position:absolute;inset:0;width:100%;height:100%;opacity:0;transition:opacity .15s}' + + '.page img.on{opacity:1}' + + `#badge{position:fixed;left:50%;bottom:16px;transform:translateX(-50%);padding:4px 12px;border-radius:999px;` + + `background:rgba(0,0,0,.65);color:#fff;font-size:13px;font-variant-numeric:tabular-nums;transition:opacity .3s}` + + '#badge.hide{opacity:0}', + '
' + + ``, + ); +} From 56d826c50876c5c19b6635f6d36b26c37e79eef9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:24:19 +0200 Subject: [PATCH 248/334] test: cover how the PDF preview reports protected and unreadable files --- src/lib/__tests__/pdf-pages.test.ts | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/lib/__tests__/pdf-pages.test.ts diff --git a/src/lib/__tests__/pdf-pages.test.ts b/src/lib/__tests__/pdf-pages.test.ts new file mode 100644 index 00000000..32a5261c --- /dev/null +++ b/src/lib/__tests__/pdf-pages.test.ts @@ -0,0 +1,42 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { NativeModules } from 'react-native'; +import { openPdfPages, PdfOpenError, renderPdfPage, canRenderPdfPages } from '../pdf-pages'; + +const modules = NativeModules as Record; + +function nativeError(code: string, message: string): Error { + return Object.assign(new Error(message), { code }); +} + +describe('pdf-pages', () => { + afterEach(() => { delete modules.BulwarkPdf; }); + + it('reports the page sizes the renderer returns', async () => { + modules.BulwarkPdf = { open: vi.fn(async () => ({ pages: [{ width: 595, height: 842 }] })) }; + await expect(openPdfPages('file:///x.pdf')).resolves.toEqual([{ width: 595, height: 842 }]); + }); + + it('tells a password-protected PDF from an unreadable one', async () => { + modules.BulwarkPdf = { open: vi.fn(async () => { throw nativeError('E_PDF_PROTECTED', 'password'); }) }; + await expect(openPdfPages('file:///x.pdf')).rejects.toMatchObject({ reason: 'protected' }); + + modules.BulwarkPdf = { open: vi.fn(async () => { throw nativeError('E_PDF_OPEN', 'not a PDF'); }) }; + await expect(openPdfPages('file:///x.pdf')).rejects.toMatchObject({ reason: 'unreadable' }); + + modules.BulwarkPdf = { open: vi.fn(async () => ({ pages: [] })) }; + await expect(openPdfPages('file:///x.pdf')).rejects.toBeInstanceOf(PdfOpenError); + }); + + it('fails cleanly in a build without the renderer', async () => { + expect(canRenderPdfPages()).toBe(false); + await expect(openPdfPages('file:///x.pdf')).rejects.toMatchObject({ reason: 'unreadable' }); + await expect(renderPdfPage('file:///x.pdf', 0, 100, 'file:///p.png')).rejects.toThrow(); + }); + + it('renders at a whole pixel width', async () => { + const renderPage = vi.fn(async () => 'file:///p.png'); + modules.BulwarkPdf = { renderPage }; + await renderPdfPage('file:///x.pdf', 2, 1620.6, 'file:///p.png'); + expect(renderPage).toHaveBeenCalledWith('file:///x.pdf', 2, 1621, 'file:///p.png'); + }); +}); From e0f18713aa970f15886deda226f1ef7e68035f47 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:27:04 +0200 Subject: [PATCH 249/334] chore: update version number to 0.1.81 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 8c1c54a7..962d1c10 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.80 +0.1.81 diff --git a/package-lock.json b/package-lock.json index ce6fc623..cebe5dab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.80", + "version": "0.1.81", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.80", + "version": "0.1.81", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 0cbe5215..e25dd504 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.80", + "version": "0.1.81", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From d22fb8d5d0d2c809b023eb16b93f87b1142d1d5d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:39:57 +0200 Subject: [PATCH 250/334] feat: open the files inside ZIP archives and attached messages --- locales/rn/ar.json | 6 +- locales/rn/ca.json | 6 +- locales/rn/cs.json | 6 +- locales/rn/da.json | 6 +- locales/rn/de.json | 6 +- locales/rn/en.json | 6 +- locales/rn/es.json | 6 +- locales/rn/fa.json | 6 +- locales/rn/fr.json | 6 +- locales/rn/he.json | 6 +- locales/rn/hu.json | 6 +- locales/rn/it.json | 6 +- locales/rn/ja.json | 6 +- locales/rn/ko.json | 6 +- locales/rn/lv.json | 6 +- locales/rn/mn.json | 6 +- locales/rn/nb.json | 6 +- locales/rn/nl.json | 6 +- locales/rn/pl.json | 6 +- locales/rn/pt.json | 6 +- locales/rn/ro.json | 6 +- locales/rn/ru.json | 6 +- locales/rn/sk.json | 6 +- locales/rn/tr.json | 6 +- locales/rn/uk.json | 6 +- locales/rn/zh-TW.json | 6 +- locales/rn/zh.json | 6 +- .../email/AttachmentPreviewModal.tsx | 85 +++++++++- src/components/email/PreviewPartList.tsx | 152 ++++++++++++++++++ src/components/email/use-body-override.ts | 6 +- src/components/email/use-file-preview.ts | 14 ++ src/lib/__tests__/file-preview.test.ts | 12 +- src/lib/__tests__/zip-preview.test.ts | 72 +++++++++ src/lib/file-preview.ts | 11 +- src/lib/zip-preview.ts | 90 +++++++++++ 35 files changed, 562 insertions(+), 42 deletions(-) create mode 100644 src/components/email/PreviewPartList.tsx create mode 100644 src/lib/__tests__/zip-preview.test.ts create mode 100644 src/lib/zip-preview.ts diff --git a/locales/rn/ar.json b/locales/rn/ar.json index 31da86be..4874b1c5 100644 --- a/locales/rn/ar.json +++ b/locales/rn/ar.json @@ -410,7 +410,11 @@ "unsupported": "لا توجد معاينة لهذا النوع من الملفات.", "media_unsupported": "لا يمكن تشغيل هذا الملف هنا.", "pdf_protected": "ملف PDF هذا محمي بكلمة مرور. افتحه في تطبيق آخر لإدخال كلمة المرور.", - "pdf_unreadable": "لا يمكن عرض ملف PDF هذا هنا." + "pdf_unreadable": "لا يمكن عرض ملف PDF هذا هنا.", + "archive_files": "{count, plural, zero {لا ملفات} one {ملف واحد} two {ملفان} few {# ملفات} many {# ملفًا} other {# ملف}}", + "archive_first_files": "أول {count} ملف", + "archive_protected": "هذا الأرشيف محمي بكلمة مرور. افتحه في تطبيق آخر لإدخال كلمة المرور.", + "archive_unreadable": "لا يمكن فتح هذا الأرشيف هنا." }, "received_via": "إلى {name}", "remove_color": "إزالة الوسم", diff --git a/locales/rn/ca.json b/locales/rn/ca.json index 0572c33e..c35057c6 100644 --- a/locales/rn/ca.json +++ b/locales/rn/ca.json @@ -410,7 +410,11 @@ "unsupported": "No hi ha previsualització per a aquest tipus de fitxer.", "media_unsupported": "Aquest fitxer no es pot reproduir aquí.", "pdf_protected": "Aquest PDF està protegit amb contrasenya. Obre'l en una altra aplicació per introduir-la.", - "pdf_unreadable": "Aquest PDF no es pot mostrar aquí." + "pdf_unreadable": "Aquest PDF no es pot mostrar aquí.", + "archive_files": "{count, plural, one {# fitxer} other {# fitxers}}", + "archive_first_files": "Els primers {count} fitxers", + "archive_protected": "Aquest arxiu està protegit amb contrasenya. Obre'l en una altra aplicació per introduir-la.", + "archive_unreadable": "Aquest arxiu no es pot obrir aquí." }, "received_via": "per a {name}", "remove_color": "Elimina l'etiqueta", diff --git a/locales/rn/cs.json b/locales/rn/cs.json index 4bb3f3d0..fb3f8006 100644 --- a/locales/rn/cs.json +++ b/locales/rn/cs.json @@ -410,7 +410,11 @@ "unsupported": "Pro tento typ souboru není náhled k dispozici.", "media_unsupported": "Tento soubor zde nelze přehrát.", "pdf_protected": "Toto PDF je chráněné heslem. Otevřete ho v jiné aplikaci a zadejte heslo.", - "pdf_unreadable": "Toto PDF zde nelze zobrazit." + "pdf_unreadable": "Toto PDF zde nelze zobrazit.", + "archive_files": "{count, plural, one {# soubor} few {# soubory} many {# souboru} other {# souborů}}", + "archive_first_files": "Prvních {count} souborů", + "archive_protected": "Tento archiv je chráněný heslem. Otevřete ho v jiné aplikaci a zadejte heslo.", + "archive_unreadable": "Tento archiv zde nelze otevřít." }, "received_via": "pro {name}", "remove_color": "Odebrat štítek", diff --git a/locales/rn/da.json b/locales/rn/da.json index 078ebb72..6d5c1a79 100644 --- a/locales/rn/da.json +++ b/locales/rn/da.json @@ -410,7 +410,11 @@ "unsupported": "Ingen forhåndsvisning for denne filtype.", "media_unsupported": "Denne fil kan ikke afspilles her.", "pdf_protected": "Denne PDF er beskyttet med adgangskode. Åbn den i en anden app for at indtaste adgangskoden.", - "pdf_unreadable": "Denne PDF kan ikke vises her." + "pdf_unreadable": "Denne PDF kan ikke vises her.", + "archive_files": "{count, plural, one {# fil} other {# filer}}", + "archive_first_files": "De første {count} filer", + "archive_protected": "Dette arkiv er beskyttet med adgangskode. Åbn det i en anden app for at indtaste adgangskoden.", + "archive_unreadable": "Dette arkiv kan ikke åbnes her." }, "received_via": "til {name}", "remove_color": "Fjern tag", diff --git a/locales/rn/de.json b/locales/rn/de.json index 84ca7657..df75b691 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -416,7 +416,11 @@ "unsupported": "Keine Vorschau für diesen Dateityp.", "media_unsupported": "Diese Datei kann hier nicht abgespielt werden.", "pdf_protected": "Diese PDF ist passwortgeschützt. Öffne sie in einer anderen App, um das Passwort einzugeben.", - "pdf_unreadable": "Diese PDF kann hier nicht angezeigt werden." + "pdf_unreadable": "Diese PDF kann hier nicht angezeigt werden.", + "archive_files": "{count, plural, one {# Datei} other {# Dateien}}", + "archive_first_files": "Die ersten {count} Dateien", + "archive_protected": "Dieses Archiv ist passwortgeschützt. Öffne es in einer anderen App, um das Passwort einzugeben.", + "archive_unreadable": "Dieses Archiv kann hier nicht geöffnet werden." }, "received_via": "an {name}", "remove_color": "Label entfernen", diff --git a/locales/rn/en.json b/locales/rn/en.json index 45cb669a..56c33f23 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -427,7 +427,11 @@ "unsupported": "No preview for this file type.", "media_unsupported": "This file can't be played here.", "pdf_protected": "This PDF is password-protected. Open it in another app to enter the password.", - "pdf_unreadable": "This PDF can't be shown here." + "pdf_unreadable": "This PDF can't be shown here.", + "archive_files": "{count, plural, one {# file} other {# files}}", + "archive_first_files": "The first {count} files", + "archive_protected": "This archive is password-protected. Open it in another app to enter the password.", + "archive_unreadable": "This archive can't be opened here." }, "received_via": "to {name}", "remove_color": "Remove tag", diff --git a/locales/rn/es.json b/locales/rn/es.json index 14e0df37..ba43ca8f 100644 --- a/locales/rn/es.json +++ b/locales/rn/es.json @@ -410,7 +410,11 @@ "unsupported": "No hay vista previa para este tipo de archivo.", "media_unsupported": "Este archivo no se puede reproducir aquí.", "pdf_protected": "Este PDF está protegido con contraseña. Ábrelo en otra aplicación para introducirla.", - "pdf_unreadable": "Este PDF no se puede mostrar aquí." + "pdf_unreadable": "Este PDF no se puede mostrar aquí.", + "archive_files": "{count, plural, one {# archivo} other {# archivos}}", + "archive_first_files": "Los primeros {count} archivos", + "archive_protected": "Este archivo comprimido está protegido con contraseña. Ábrelo en otra aplicación para introducirla.", + "archive_unreadable": "Este archivo comprimido no se puede abrir aquí." }, "received_via": "para {name}", "remove_color": "Eliminar etiqueta", diff --git a/locales/rn/fa.json b/locales/rn/fa.json index 393c9e25..e69110c6 100644 --- a/locales/rn/fa.json +++ b/locales/rn/fa.json @@ -410,7 +410,11 @@ "unsupported": "برای این نوع فایل پیش‌نمایشی وجود ندارد.", "media_unsupported": "این فایل اینجا پخش نمی‌شود.", "pdf_protected": "این PDF با رمز عبور محافظت شده است. برای وارد کردن رمز، آن را در برنامهٔ دیگری باز کنید.", - "pdf_unreadable": "این PDF اینجا نمایش داده نمی‌شود." + "pdf_unreadable": "این PDF اینجا نمایش داده نمی‌شود.", + "archive_files": "{count, plural, one {# فایل} other {# فایل}}", + "archive_first_files": "{count} فایل نخست", + "archive_protected": "این بایگانی با رمز عبور محافظت شده است. برای وارد کردن رمز، آن را در برنامهٔ دیگری باز کنید.", + "archive_unreadable": "این بایگانی اینجا باز نمی‌شود." }, "received_via": "به {name}", "remove_color": "حذف برچسب", diff --git a/locales/rn/fr.json b/locales/rn/fr.json index 8675882f..286dde78 100644 --- a/locales/rn/fr.json +++ b/locales/rn/fr.json @@ -410,7 +410,11 @@ "unsupported": "Aucun aperçu pour ce type de fichier.", "media_unsupported": "Ce fichier ne peut pas être lu ici.", "pdf_protected": "Ce PDF est protégé par un mot de passe. Ouvrez-le dans une autre application pour le saisir.", - "pdf_unreadable": "Ce PDF ne peut pas être affiché ici." + "pdf_unreadable": "Ce PDF ne peut pas être affiché ici.", + "archive_files": "{count, plural, one {# fichier} other {# fichiers}}", + "archive_first_files": "Les {count} premiers fichiers", + "archive_protected": "Cette archive est protégée par un mot de passe. Ouvrez-la dans une autre application pour le saisir.", + "archive_unreadable": "Cette archive ne peut pas être ouverte ici." }, "received_via": "à {name}", "remove_color": "Retirer l'étiquette", diff --git a/locales/rn/he.json b/locales/rn/he.json index b3331776..94985885 100644 --- a/locales/rn/he.json +++ b/locales/rn/he.json @@ -410,7 +410,11 @@ "unsupported": "אין תצוגה מקדימה לסוג קובץ זה.", "media_unsupported": "לא ניתן להפעיל את הקובץ הזה כאן.", "pdf_protected": "קובץ ה-PDF הזה מוגן בסיסמה. פתחו אותו באפליקציה אחרת כדי להזין את הסיסמה.", - "pdf_unreadable": "לא ניתן להציג את קובץ ה-PDF הזה כאן." + "pdf_unreadable": "לא ניתן להציג את קובץ ה-PDF הזה כאן.", + "archive_files": "{count, plural, one {קובץ אחד} two {שני קבצים} other {# קבצים}}", + "archive_first_files": "{count} הקבצים הראשונים", + "archive_protected": "הארכיון הזה מוגן בסיסמה. פתחו אותו באפליקציה אחרת כדי להזין את הסיסמה.", + "archive_unreadable": "לא ניתן לפתוח את הארכיון הזה כאן." }, "received_via": "אל {name}", "remove_color": "הסר תג", diff --git a/locales/rn/hu.json b/locales/rn/hu.json index 1f9b1370..51401294 100644 --- a/locales/rn/hu.json +++ b/locales/rn/hu.json @@ -410,7 +410,11 @@ "unsupported": "Ehhez a fájltípushoz nincs előnézet.", "media_unsupported": "Ez a fájl itt nem játszható le.", "pdf_protected": "Ez a PDF jelszóval védett. Nyisd meg egy másik alkalmazásban a jelszó megadásához.", - "pdf_unreadable": "Ez a PDF itt nem jeleníthető meg." + "pdf_unreadable": "Ez a PDF itt nem jeleníthető meg.", + "archive_files": "{count, plural, one {# fájl} other {# fájl}}", + "archive_first_files": "Az első {count} fájl", + "archive_protected": "Ez az archívum jelszóval védett. Nyisd meg egy másik alkalmazásban a jelszó megadásához.", + "archive_unreadable": "Ez az archívum itt nem nyitható meg." }, "received_via": "címzett: {name}", "remove_color": "Címke eltávolítása", diff --git a/locales/rn/it.json b/locales/rn/it.json index 512a386b..d960c64b 100644 --- a/locales/rn/it.json +++ b/locales/rn/it.json @@ -410,7 +410,11 @@ "unsupported": "Nessuna anteprima per questo tipo di file.", "media_unsupported": "Questo file non può essere riprodotto qui.", "pdf_protected": "Questo PDF è protetto da password. Aprilo in un'altra app per inserirla.", - "pdf_unreadable": "Questo PDF non può essere visualizzato qui." + "pdf_unreadable": "Questo PDF non può essere visualizzato qui.", + "archive_files": "{count, plural, one {# file} other {# file}}", + "archive_first_files": "I primi {count} file", + "archive_protected": "Questo archivio è protetto da password. Aprilo in un'altra app per inserirla.", + "archive_unreadable": "Questo archivio non può essere aperto qui." }, "received_via": "a {name}", "remove_color": "Rimuovi etichetta", diff --git a/locales/rn/ja.json b/locales/rn/ja.json index 96a00d49..1a48476e 100644 --- a/locales/rn/ja.json +++ b/locales/rn/ja.json @@ -410,7 +410,11 @@ "unsupported": "このファイル形式はプレビューできません。", "media_unsupported": "このファイルはここでは再生できません。", "pdf_protected": "このPDFはパスワードで保護されています。パスワードを入力するには別のアプリで開いてください。", - "pdf_unreadable": "このPDFはここでは表示できません。" + "pdf_unreadable": "このPDFはここでは表示できません。", + "archive_files": "{count, plural, other {# 個のファイル}}", + "archive_first_files": "最初の {count} 個のファイル", + "archive_protected": "このアーカイブはパスワードで保護されています。パスワードを入力するには別のアプリで開いてください。", + "archive_unreadable": "このアーカイブはここでは開けません。" }, "received_via": "{name}宛", "remove_color": "ラベルを削除", diff --git a/locales/rn/ko.json b/locales/rn/ko.json index d9afa773..b0ffa78e 100644 --- a/locales/rn/ko.json +++ b/locales/rn/ko.json @@ -410,7 +410,11 @@ "unsupported": "이 파일 형식은 미리보기를 지원하지 않아요.", "media_unsupported": "이 파일은 여기서 재생할 수 없습니다.", "pdf_protected": "이 PDF는 암호로 보호되어 있습니다. 암호를 입력하려면 다른 앱에서 여세요.", - "pdf_unreadable": "이 PDF는 여기서 표시할 수 없습니다." + "pdf_unreadable": "이 PDF는 여기서 표시할 수 없습니다.", + "archive_files": "{count, plural, other {파일 #개}}", + "archive_first_files": "처음 {count}개 파일", + "archive_protected": "이 압축 파일은 암호로 보호되어 있습니다. 암호를 입력하려면 다른 앱에서 여세요.", + "archive_unreadable": "이 압축 파일은 여기서 열 수 없습니다." }, "received_via": "받는 사람: {name}", "remove_color": "태그 제거", diff --git a/locales/rn/lv.json b/locales/rn/lv.json index 3d413883..27189a1b 100644 --- a/locales/rn/lv.json +++ b/locales/rn/lv.json @@ -410,7 +410,11 @@ "unsupported": "Šim faila tipam nav priekšskatījuma.", "media_unsupported": "Šo failu šeit nevar atskaņot.", "pdf_protected": "Šis PDF ir aizsargāts ar paroli. Atveriet to citā lietotnē, lai ievadītu paroli.", - "pdf_unreadable": "Šo PDF šeit nevar parādīt." + "pdf_unreadable": "Šo PDF šeit nevar parādīt.", + "archive_files": "{count, plural, zero {# failu} one {# fails} other {# faili}}", + "archive_first_files": "Pirmie {count} faili", + "archive_protected": "Šis arhīvs ir aizsargāts ar paroli. Atveriet to citā lietotnē, lai ievadītu paroli.", + "archive_unreadable": "Šo arhīvu šeit nevar atvērt." }, "received_via": "kam: {name}", "remove_color": "Noņemt tagu", diff --git a/locales/rn/mn.json b/locales/rn/mn.json index 6d63704b..c808bb5a 100644 --- a/locales/rn/mn.json +++ b/locales/rn/mn.json @@ -410,7 +410,11 @@ "unsupported": "Энэ төрлийн файлыг урьдчилан харах боломжгүй.", "media_unsupported": "Энэ файлыг энд тоглуулах боломжгүй.", "pdf_protected": "Энэ PDF нууц үгээр хамгаалагдсан. Нууц үг оруулахын тулд өөр апп-аар нээнэ үү.", - "pdf_unreadable": "Энэ PDF-ийг энд харуулах боломжгүй." + "pdf_unreadable": "Энэ PDF-ийг энд харуулах боломжгүй.", + "archive_files": "{count, plural, one {# файл} other {# файл}}", + "archive_first_files": "Эхний {count} файл", + "archive_protected": "Энэ архив нууц үгээр хамгаалагдсан. Нууц үг оруулахын тулд өөр апп-аар нээнэ үү.", + "archive_unreadable": "Энэ архивыг энд нээх боломжгүй." }, "received_via": "Хэнд: {name}", "remove_color": "Тагийг хасах", diff --git a/locales/rn/nb.json b/locales/rn/nb.json index a6612ee1..169a55b6 100644 --- a/locales/rn/nb.json +++ b/locales/rn/nb.json @@ -410,7 +410,11 @@ "unsupported": "Ingen forhåndsvisning for denne filtypen.", "media_unsupported": "Denne filen kan ikke spilles av her.", "pdf_protected": "Denne PDF-en er passordbeskyttet. Åpne den i en annen app for å skrive inn passordet.", - "pdf_unreadable": "Denne PDF-en kan ikke vises her." + "pdf_unreadable": "Denne PDF-en kan ikke vises her.", + "archive_files": "{count, plural, one {# fil} other {# filer}}", + "archive_first_files": "De første {count} filene", + "archive_protected": "Dette arkivet er passordbeskyttet. Åpne det i en annen app for å skrive inn passordet.", + "archive_unreadable": "Dette arkivet kan ikke åpnes her." }, "received_via": "til {name}", "remove_color": "Fjern etiketten", diff --git a/locales/rn/nl.json b/locales/rn/nl.json index e5006356..6b468c43 100644 --- a/locales/rn/nl.json +++ b/locales/rn/nl.json @@ -410,7 +410,11 @@ "unsupported": "Geen voorbeeld voor dit bestandstype.", "media_unsupported": "Dit bestand kan hier niet worden afgespeeld.", "pdf_protected": "Deze pdf is beveiligd met een wachtwoord. Open hem in een andere app om het wachtwoord in te voeren.", - "pdf_unreadable": "Deze pdf kan hier niet worden weergegeven." + "pdf_unreadable": "Deze pdf kan hier niet worden weergegeven.", + "archive_files": "{count, plural, one {# bestand} other {# bestanden}}", + "archive_first_files": "De eerste {count} bestanden", + "archive_protected": "Dit archief is beveiligd met een wachtwoord. Open het in een andere app om het wachtwoord in te voeren.", + "archive_unreadable": "Dit archief kan hier niet worden geopend." }, "received_via": "aan {name}", "remove_color": "Label verwijderen", diff --git a/locales/rn/pl.json b/locales/rn/pl.json index 249f3294..b57829c4 100644 --- a/locales/rn/pl.json +++ b/locales/rn/pl.json @@ -410,7 +410,11 @@ "unsupported": "Brak podglądu dla tego typu pliku.", "media_unsupported": "Tego pliku nie można tutaj odtworzyć.", "pdf_protected": "Ten plik PDF jest chroniony hasłem. Otwórz go w innej aplikacji, aby wpisać hasło.", - "pdf_unreadable": "Tego pliku PDF nie można tutaj wyświetlić." + "pdf_unreadable": "Tego pliku PDF nie można tutaj wyświetlić.", + "archive_files": "{count, plural, one {# plik} few {# pliki} many {# plików} other {# pliku}}", + "archive_first_files": "Pierwsze {count} plików", + "archive_protected": "To archiwum jest chronione hasłem. Otwórz je w innej aplikacji, aby wpisać hasło.", + "archive_unreadable": "Tego archiwum nie można tutaj otworzyć." }, "received_via": "do {name}", "remove_color": "Usuń etykietę", diff --git a/locales/rn/pt.json b/locales/rn/pt.json index 17be6f02..19acf556 100644 --- a/locales/rn/pt.json +++ b/locales/rn/pt.json @@ -410,7 +410,11 @@ "unsupported": "Sem pré-visualização para este tipo de arquivo.", "media_unsupported": "Este ficheiro não pode ser reproduzido aqui.", "pdf_protected": "Este PDF está protegido por palavra-passe. Abra-o noutra aplicação para a introduzir.", - "pdf_unreadable": "Este PDF não pode ser mostrado aqui." + "pdf_unreadable": "Este PDF não pode ser mostrado aqui.", + "archive_files": "{count, plural, one {# ficheiro} other {# ficheiros}}", + "archive_first_files": "Os primeiros {count} ficheiros", + "archive_protected": "Este arquivo está protegido por palavra-passe. Abra-o noutra aplicação para a introduzir.", + "archive_unreadable": "Este arquivo não pode ser aberto aqui." }, "received_via": "para {name}", "remove_color": "Remover etiqueta", diff --git a/locales/rn/ro.json b/locales/rn/ro.json index a86f5a0d..618e98d8 100644 --- a/locales/rn/ro.json +++ b/locales/rn/ro.json @@ -410,7 +410,11 @@ "unsupported": "Nu există previzualizare pentru acest tip de fișier.", "media_unsupported": "Acest fișier nu poate fi redat aici.", "pdf_protected": "Acest PDF este protejat cu parolă. Deschide-l în altă aplicație pentru a introduce parola.", - "pdf_unreadable": "Acest PDF nu poate fi afișat aici." + "pdf_unreadable": "Acest PDF nu poate fi afișat aici.", + "archive_files": "{count, plural, one {# fișier} few {# fișiere} other {# de fișiere}}", + "archive_first_files": "Primele {count} fișiere", + "archive_protected": "Această arhivă este protejată cu parolă. Deschide-o în altă aplicație pentru a introduce parola.", + "archive_unreadable": "Această arhivă nu poate fi deschisă aici." }, "received_via": "către {name}", "remove_color": "Eliminați eticheta", diff --git a/locales/rn/ru.json b/locales/rn/ru.json index f12ec606..1bbd16d5 100644 --- a/locales/rn/ru.json +++ b/locales/rn/ru.json @@ -410,7 +410,11 @@ "unsupported": "Для этого типа файлов нет предпросмотра.", "media_unsupported": "Этот файл нельзя воспроизвести здесь.", "pdf_protected": "Этот PDF защищён паролем. Откройте его в другом приложении, чтобы ввести пароль.", - "pdf_unreadable": "Этот PDF нельзя показать здесь." + "pdf_unreadable": "Этот PDF нельзя показать здесь.", + "archive_files": "{count, plural, one {# файл} few {# файла} many {# файлов} other {# файла}}", + "archive_first_files": "Первые {count} файлов", + "archive_protected": "Этот архив защищён паролем. Откройте его в другом приложении, чтобы ввести пароль.", + "archive_unreadable": "Этот архив нельзя открыть здесь." }, "received_via": "на {name}", "remove_color": "Убрать тег", diff --git a/locales/rn/sk.json b/locales/rn/sk.json index 26494ae8..60df6ac0 100644 --- a/locales/rn/sk.json +++ b/locales/rn/sk.json @@ -410,7 +410,11 @@ "unsupported": "Pre tento typ súboru nie je k dispozícii náhľad.", "media_unsupported": "Tento súbor tu nie je možné prehrať.", "pdf_protected": "Toto PDF je chránené heslom. Otvorte ho v inej aplikácii a zadajte heslo.", - "pdf_unreadable": "Toto PDF tu nie je možné zobraziť." + "pdf_unreadable": "Toto PDF tu nie je možné zobraziť.", + "archive_files": "{count, plural, one {# súbor} few {# súbory} many {# súboru} other {# súborov}}", + "archive_first_files": "Prvých {count} súborov", + "archive_protected": "Tento archív je chránený heslom. Otvorte ho v inej aplikácii a zadajte heslo.", + "archive_unreadable": "Tento archív tu nie je možné otvoriť." }, "received_via": "pre {name}", "remove_color": "Odstrániť štítok", diff --git a/locales/rn/tr.json b/locales/rn/tr.json index be06fdab..deae15a7 100644 --- a/locales/rn/tr.json +++ b/locales/rn/tr.json @@ -410,7 +410,11 @@ "unsupported": "Bu dosya türü için önizleme yok.", "media_unsupported": "Bu dosya burada oynatılamıyor.", "pdf_protected": "Bu PDF parola korumalı. Parolayı girmek için başka bir uygulamada açın.", - "pdf_unreadable": "Bu PDF burada gösterilemiyor." + "pdf_unreadable": "Bu PDF burada gösterilemiyor.", + "archive_files": "{count, plural, one {# dosya} other {# dosya}}", + "archive_first_files": "İlk {count} dosya", + "archive_protected": "Bu arşiv parola korumalı. Parolayı girmek için başka bir uygulamada açın.", + "archive_unreadable": "Bu arşiv burada açılamıyor." }, "received_via": "alıcı: {name}", "remove_color": "Etiketi kaldır", diff --git a/locales/rn/uk.json b/locales/rn/uk.json index 1be0e304..e3150d88 100644 --- a/locales/rn/uk.json +++ b/locales/rn/uk.json @@ -410,7 +410,11 @@ "unsupported": "Для цього типу файлів немає попереднього перегляду.", "media_unsupported": "Цей файл не можна відтворити тут.", "pdf_protected": "Цей PDF захищено паролем. Відкрийте його в іншому застосунку, щоб ввести пароль.", - "pdf_unreadable": "Цей PDF не можна показати тут." + "pdf_unreadable": "Цей PDF не можна показати тут.", + "archive_files": "{count, plural, one {# файл} few {# файли} many {# файлів} other {# файлу}}", + "archive_first_files": "Перші {count} файлів", + "archive_protected": "Цей архів захищено паролем. Відкрийте його в іншому застосунку, щоб ввести пароль.", + "archive_unreadable": "Цей архів не можна відкрити тут." }, "received_via": "на {name}", "remove_color": "Прибрати тег", diff --git a/locales/rn/zh-TW.json b/locales/rn/zh-TW.json index 14e41e90..dea23a13 100644 --- a/locales/rn/zh-TW.json +++ b/locales/rn/zh-TW.json @@ -410,7 +410,11 @@ "unsupported": "無法預覽此檔案類型。", "media_unsupported": "無法在此播放這個檔案。", "pdf_protected": "此 PDF 受密碼保護。請在其他應用程式中開啟以輸入密碼。", - "pdf_unreadable": "無法在此顯示這個 PDF。" + "pdf_unreadable": "無法在此顯示這個 PDF。", + "archive_files": "{count, plural, other {# 個檔案}}", + "archive_first_files": "前 {count} 個檔案", + "archive_protected": "此壓縮檔受密碼保護。請在其他應用程式中開啟以輸入密碼。", + "archive_unreadable": "無法在此開啟這個壓縮檔。" }, "received_via": "寄給 {name}", "remove_color": "移除標籤", diff --git a/locales/rn/zh.json b/locales/rn/zh.json index d0ab9384..71758718 100644 --- a/locales/rn/zh.json +++ b/locales/rn/zh.json @@ -410,7 +410,11 @@ "unsupported": "无法预览此类文件。", "media_unsupported": "无法在此播放该文件。", "pdf_protected": "此 PDF 受密码保护。请在其他应用中打开以输入密码。", - "pdf_unreadable": "无法在此显示该 PDF。" + "pdf_unreadable": "无法在此显示该 PDF。", + "archive_files": "{count, plural, other {# 个文件}}", + "archive_first_files": "前 {count} 个文件", + "archive_protected": "此压缩包受密码保护。请在其他应用中打开以输入密码。", + "archive_unreadable": "无法在此打开该压缩包。" }, "received_via": "发给 {name}", "remove_color": "删除标签", diff --git a/src/components/email/AttachmentPreviewModal.tsx b/src/components/email/AttachmentPreviewModal.tsx index da81ec10..8948d40c 100644 --- a/src/components/email/AttachmentPreviewModal.tsx +++ b/src/components/email/AttachmentPreviewModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, Modal, Pressable, ScrollView, Image, ActivityIndicator, Platform, PixelRatio, + View, Text, StyleSheet, Modal, Pressable, ScrollView, Image, ActivityIndicator, Platform, PixelRatio, Alert, useWindowDimensions, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -10,10 +10,17 @@ import type { Email } from '../../api/types'; import { spacing, radius, typography, componentSizes, type ThemePalette } from '../../theme/tokens'; import { useColors } from '../../theme/colors'; import { useLocaleStore } from '../../stores/locale-store'; +import { useSettingsStore } from '../../stores/settings-store'; import EmailBodyView from '../EmailBodyView'; -import { needsNativeImageDecoder, needsWebImageViewer, type PreviewKind } from '../../lib/file-preview'; +import { + handoffMimeType, needsNativeImageDecoder, needsWebImageViewer, type PreviewKind, +} from '../../lib/file-preview'; import { pdfPageFileName, renderPdfPage } from '../../lib/pdf-pages'; +import type { ZipArchive } from '../../lib/zip-preview'; +import { formatFullDateTime } from '../../lib/email-date'; import type { EmlPreview } from './use-body-override'; +import { useFilePreview } from './use-file-preview'; +import { ArchiveSummary, PreviewPartList, type PreviewPart } from './PreviewPartList'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; export interface PreviewItem { @@ -30,6 +37,8 @@ export interface PreviewItem { html?: string; /** Parsed embedded message for .eml previews. */ eml?: EmlPreview; + /** A ZIP's directory, for archive previews. */ + archive?: ZipArchive; /** Replaces "No preview for this file type." on the fallback screen (e.g. the file is too large). */ notice?: string; } @@ -131,9 +140,11 @@ function PdfPagesView({ fileUri, viewerUri, background, onFailed }: { /** * In-app preview for images, PDFs (iOS's WebView shows them, Android renders the pages), video - * and audio (the WebView's player), text, Markdown and HTML documents and - * embedded messages. Files that don't decode, and types without a preview, - * offer another app instead. Used by every place that opens a file. + * and audio (the WebView's player), text, Markdown and HTML documents, + * embedded messages and ZIP archives. The files inside an archive or a + * message open in a preview of their own on top. Files that don't decode, + * and types without a preview, offer another app instead. Used by every + * place that opens a file. */ export function AttachmentPreviewModal({ item, loading, title, onClose, onOpenExternal, onShare, onDownload, docked = false, @@ -141,7 +152,45 @@ export function AttachmentPreviewModal({ const c = useColors(); const styles = React.useMemo(() => makeStyles(c), [c]); const t = useLocaleStore((s) => s.t); + const locale = useLocaleStore((s) => s.locale); + const timeFormat = useSettingsStore((s) => s.timeFormat); const { width } = useWindowDimensions(); + // Files inside this one (archive entries, an .eml's attachments). + const nested = useFilePreview(); + const { open: openNested } = nested; + const openPart = React.useCallback(async (part: PreviewPart) => { + try { + const bytes = await part.read(); + await openNested({ + from: 'bytes', + bytes, + name: part.name, + type: part.type || handoffMimeType(part.name, ''), + size: bytes.byteLength, + }); + } catch (e) { + Alert.alert(t('email_viewer.attachment_failed', 'Could not open attachment'), e instanceof Error ? e.message : String(e)); + } + }, [openNested, t]); + const archive = item?.kind === 'archive' ? item.archive : undefined; + const archiveParts = React.useMemo(() => { + if (!archive) return []; + return archive.entries.map((entry) => ({ + key: entry.path, + name: entry.name, + folder: entry.folder, + size: entry.size, + read: () => archive.read(entry.path), + })); + }, [archive]); + const emlAttachments = item?.kind === 'eml' ? item.eml?.attachments : undefined; + const emlParts = React.useMemo(() => (emlAttachments ?? []).map((att, i) => ({ + key: `${i}-${att.name}`, + name: att.name, + type: att.type, + size: att.size, + read: async () => att.bytes, + })), [emlAttachments]); // Which viewers failed for the open file, keyed by its URI so the next one // starts clean: React Native's Image falls back to the WebView viewer, and // a WebView that can't decode the file falls back to another app. @@ -196,10 +245,11 @@ export function AttachmentPreviewModal({ ); - const renderDocument = (header?: React.ReactNode) => ( + const renderDocument = (header?: React.ReactNode, footer?: React.ReactNode) => ( {header} {docEmail && } + {footer} ); @@ -303,12 +353,30 @@ export function AttachmentPreviewModal({ {item.eml?.subject || t('email_viewer.no_subject', '(No Subject)')} {item.eml?.from ? {item.eml.from} : null} - {item.eml?.date ? {item.eml.date} : null} + {item.eml?.date ? {formatFullDateTime(item.eml.date, timeFormat, locale) || item.eml.date} : null} , + emlParts.length > 0 ? ( + } + /> + ) : null, + ); + case 'archive': + if (!item.archive) break; + return ( + } + /> ); default: - return renderFallback(item.notice ?? t('email_viewer.preview.unsupported', 'No preview for this file type.')); + break; } + return renderFallback(item.notice ?? t('email_viewer.preview.unsupported', 'No preview for this file type.')); }; const content = ( @@ -359,6 +427,7 @@ export function AttachmentPreviewModal({ {renderBody()} + {nested.modalProps.item || nested.modalProps.loading ? : null} ); if (docked) return item || loading ? content : null; diff --git a/src/components/email/PreviewPartList.tsx b/src/components/email/PreviewPartList.tsx new file mode 100644 index 00000000..72e2eea6 --- /dev/null +++ b/src/components/email/PreviewPartList.tsx @@ -0,0 +1,152 @@ +import React from 'react'; +import { ActivityIndicator, FlatList, Pressable, StyleSheet, Text, View } from 'react-native'; +import { + File as FileIcon, FileArchive, FileAudio, FileImage, FileSpreadsheet, FileText, FileVideo, +} from '../icons'; +import { formatSize } from '../../lib/attachment-display'; +import { filePreviewKind, handoffMimeType } from '../../lib/file-preview'; +import { spacing, radius, typography, type ThemePalette } from '../../theme/tokens'; +import { useColors } from '../../theme/colors'; +import { useLocaleStore } from '../../stores/locale-store'; + +/** A file inside a file: an archive entry or an attachment of an .eml. */ +export interface PreviewPart { + key: string; + name: string; + /** Folder inside an archive, '' at the top. */ + folder?: string; + type?: string; + size?: number; + read: () => Promise; +} + +function iconFor(part: PreviewPart) { + const type = part.type || handoffMimeType(part.name, ''); + if (/\.(xlsx?|ods|csv|tsv|numbers)$/i.test(part.name)) return FileSpreadsheet; + if (/\.(zip|7z|rar|gz|tgz|tar|bz2|xz)$/i.test(part.name)) return FileArchive; + // Platform-independent: the icon names the format, not what plays here. + switch (filePreviewKind({ name: part.name, type }, { platform: 'android' })) { + case 'image': return FileImage; + case 'video': return FileVideo; + case 'audio': return FileAudio; + case 'none': return /^(application\/(msword|vnd\.|rtf)|text\/)/.test(type) ? FileText : FileIcon; + default: return FileText; + } +} + +interface Props { + parts: PreviewPart[]; + /** Opens a part; the row shows a spinner until the promise settles. */ + onOpen: (part: PreviewPart) => Promise; + /** Above the rows (an .eml's body, an archive's summary). */ + header?: React.ReactElement; + /** Renders as a plain column inside a parent ScrollView instead of a list of its own. */ + inline?: boolean; +} + +/** + * The files inside an archive or an embedded message, each opening in the + * file preview like any attachment. + */ +export function PreviewPartList({ parts, onOpen, header, inline = false }: Props) { + const c = useColors(); + const styles = React.useMemo(() => makeStyles(c), [c]); + const [busy, setBusy] = React.useState(null); + + const open = (part: PreviewPart) => { + if (busy) return; + setBusy(part.key); + onOpen(part).finally(() => setBusy(null)); + }; + + const renderRow = (part: PreviewPart) => { + const Icon = iconFor(part); + return ( + [styles.row, pressed && styles.rowPressed]} + onPress={() => open(part)} + disabled={!!busy} + accessibilityRole="button" + accessibilityLabel={part.name} + > + + {busy === part.key ? : } + + + {part.name} + {part.folder ? {part.folder} : null} + + {part.size != null ? {formatSize(part.size) || '0 B'} : null} + + ); + }; + + if (inline) { + return ( + + {header} + {parts.map(renderRow)} + + ); + } + return ( + p.key} + renderItem={({ item }) => renderRow(item)} + ListHeaderComponent={header} + initialNumToRender={30} + /> + ); +} + +/** The summary line above an archive's entries. */ +export function ArchiveSummary({ count, truncated }: { count: number; truncated: boolean }) { + const c = useColors(); + const styles = React.useMemo(() => makeStyles(c), [c]); + const t = useLocaleStore((s) => s.t); + return ( + + {truncated + ? t('email_viewer.preview.archive_first_files', 'The first {count} files', { count }) + : t('email_viewer.preview.archive_files', '{count, plural, one {# file} other {# files}}', { count })} + + ); +} + +function makeStyles(c: ThemePalette) { + return StyleSheet.create({ + list: { flex: 1 }, + inline: { paddingBottom: spacing.lg }, + row: { + flexDirection: 'row', + alignItems: 'center', + gap: spacing.md, + paddingHorizontal: spacing.lg, + paddingVertical: spacing.sm, + minHeight: 52, + }, + rowPressed: { backgroundColor: c.surfaceHover }, + icon: { + width: 36, + height: 36, + borderRadius: radius.md, + backgroundColor: c.surface, + alignItems: 'center', + justifyContent: 'center', + }, + text: { flex: 1, minWidth: 0 }, + name: { ...typography.body, color: c.text }, + folder: { ...typography.caption, color: c.textMuted }, + size: { ...typography.caption, color: c.textMuted }, + summary: { + ...typography.caption, + color: c.textMuted, + paddingHorizontal: spacing.lg, + paddingTop: spacing.md, + paddingBottom: spacing.xs, + }, + }); +} diff --git a/src/components/email/use-body-override.ts b/src/components/email/use-body-override.ts index 47bde2ae..bd6387c1 100644 --- a/src/components/email/use-body-override.ts +++ b/src/components/email/use-body-override.ts @@ -60,16 +60,18 @@ export interface EmlPreview { date?: string; html: string | null; text: string | null; + /** The message's own attachments (not the images its body embeds). */ + attachments: ExtractedAttachment[]; } /** Parse a message once for the preview: its unwrapped body plus the header lines shown above it. */ export async function emlPreviewFromBytes(bytes: Uint8Array): Promise { const parsed = await parseMessage(bytes); - const { html, text } = unwrapParsed(parsed); + const { html, text, attachments } = unwrapParsed(parsed); const from = parsed.from && 'address' in parsed.from ? `${parsed.from.name ? `${parsed.from.name} ` : ''}<${parsed.from.address}>` : parsed.from?.name; - return { subject: parsed.subject, from, date: parsed.date, html, text }; + return { subject: parsed.subject, from, date: parsed.date, html, text, attachments }; } function unwrapParsed(parsed: ParsedEmail): UnwrappedMessage { diff --git a/src/components/email/use-file-preview.ts b/src/components/email/use-file-preview.ts index 6cc83a8c..0a9d9c2a 100644 --- a/src/components/email/use-file-preview.ts +++ b/src/components/email/use-file-preview.ts @@ -13,6 +13,7 @@ import type { PreviewKind } from '../../lib/file-preview'; import { markdownToHtml } from '../../lib/markdown-html'; import { imageViewerHtml, mediaViewerHtml, pdfViewerHtml, type ViewerColors } from '../../lib/preview-viewer-html'; import { closePdfPages, openPdfPages, PdfOpenError } from '../../lib/pdf-pages'; +import { openZip, ZipEncryptedError } from '../../lib/zip-preview'; import { useLocaleStore } from '../../stores/locale-store'; import { useColors, useResolvedTheme } from '../../theme/colors'; import type { PreviewItem } from './AttachmentPreviewModal'; @@ -79,6 +80,8 @@ interface Notices { tooLarge: string; pdfProtected: string; pdfUnreadable: string; + zipProtected: string; + zipUnreadable: string; } async function buildItem( @@ -100,6 +103,12 @@ async function buildItem( return { ...base, kind, html: await local.text() }; case 'eml': return { ...base, kind, eml: await emlPreviewFromBytes(await local.bytes()) }; + case 'archive': + try { + return { ...base, kind, archive: await openZip(await local.bytes()) }; + } catch (e) { + return { ...base, kind: 'none', notice: e instanceof ZipEncryptedError ? notices.zipProtected : notices.zipUnreadable }; + } case 'image': return { ...base, kind, fileUri: local.uri, viewerUri: writeViewer(local, imageViewerHtml(local.name, colors)) }; case 'video': @@ -208,6 +217,11 @@ export function useFilePreview(opts: { 'This PDF is password-protected. Open it in another app to enter the password.', ), pdfUnreadable: t('email_viewer.preview.pdf_unreadable', "This PDF can't be shown here."), + zipProtected: t( + 'email_viewer.preview.archive_protected', + 'This archive is password-protected. Open it in another app to enter the password.', + ), + zipUnreadable: t('email_viewer.preview.archive_unreadable', "This archive can't be opened here."), }, colors.current); if (current()) setItem(next); } catch (e) { diff --git a/src/lib/__tests__/file-preview.test.ts b/src/lib/__tests__/file-preview.test.ts index f4c6823a..4daa2e14 100644 --- a/src/lib/__tests__/file-preview.test.ts +++ b/src/lib/__tests__/file-preview.test.ts @@ -93,8 +93,16 @@ describe('filePreviewKind', () => { expect(filePreviewKind({ name: 'cover.mp3', type: 'image/jpeg' }, ANDROID)).toBe('image'); }); - it('leaves archives, office documents and binaries to other apps', () => { - expect(filePreviewKind({ name: 'archive.zip', type: 'application/zip' }, IOS)).toBe('none'); + it('lists the files in a ZIP, but not in the formats built on ZIP', () => { + expect(filePreviewKind({ name: 'archive.zip', type: 'application/zip' }, IOS)).toBe('archive'); + expect(filePreviewKind({ name: 'photos', type: 'application/x-zip-compressed' }, ANDROID)).toBe('archive'); + expect(filePreviewKind({ name: 'Archive.ZIP', type: 'application/octet-stream' }, ANDROID)).toBe('archive'); + expect(filePreviewKind({ name: 'app.apk', type: 'application/vnd.android.package-archive' }, ANDROID)).toBe('none'); + }); + + it('leaves other archives, office documents and binaries to other apps', () => { + expect(filePreviewKind({ name: 'backup.7z', type: 'application/x-7z-compressed' }, IOS)).toBe('none'); + expect(filePreviewKind({ name: 'logs.tar.gz', type: 'application/gzip' }, ANDROID)).toBe('none'); expect(filePreviewKind({ name: 'sheet.xlsx', type: 'application/octet-stream' }, IOS)).toBe('none'); expect(filePreviewKind({ name: 'contract.docx', type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' }, ANDROID)).toBe('none'); expect(filePreviewKind({ name: 'data.bin', type: undefined }, IOS)).toBe('none'); diff --git a/src/lib/__tests__/zip-preview.test.ts b/src/lib/__tests__/zip-preview.test.ts new file mode 100644 index 00000000..c560c792 --- /dev/null +++ b/src/lib/__tests__/zip-preview.test.ts @@ -0,0 +1,72 @@ +import { describe, it, expect } from 'vitest'; +import JSZip from 'jszip'; +import { openZip, isZipArchive, ZipEncryptedError, MAX_ZIP_ENTRIES } from '../zip-preview'; + +async function makeZip(files: Record): Promise { + const zip = new JSZip(); + for (const [path, text] of Object.entries(files)) zip.file(path, text); + return zip.generateAsync({ type: 'uint8array', compression: 'DEFLATE' }); +} + +// Set the "encrypted" bit in every local and central header, as a +// password-protected archive has it. +function markEncrypted(bytes: Uint8Array): Uint8Array { + const out = bytes.slice(); + const view = new DataView(out.buffer); + for (let i = 0; i + 4 <= out.length; i++) { + const sig = view.getUint32(i, true); + if (sig === 0x04034b50) view.setUint16(i + 6, view.getUint16(i + 6, true) | 1, true); + if (sig === 0x02014b50) view.setUint16(i + 8, view.getUint16(i + 8, true) | 1, true); + } + return out; +} + +describe('openZip', () => { + it('lists the files by folder and name, without folders or OS metadata', async () => { + const archive = await openZip(await makeZip({ + 'b.txt': 'b', + 'docs/z.md': '# z', + 'a.txt': 'a', + 'docs/inner/c.csv': '1,2', + '__MACOSX/docs/._z.md': 'junk', + '.DS_Store': 'junk', + 'docs/Thumbs.db': 'junk', + })); + expect(archive.entries.map((e) => e.path)).toEqual(['a.txt', 'b.txt', 'docs/z.md', 'docs/inner/c.csv']); + expect(archive.entries[2]).toMatchObject({ name: 'z.md', folder: 'docs', size: 3 }); + expect(archive.truncated).toBe(false); + }); + + it('reads an entry from the loaded archive', async () => { + const archive = await openZip(await makeZip({ 'docs/notes.txt': 'Grüße' })); + expect(new TextDecoder().decode(await archive.read('docs/notes.txt'))).toBe('Grüße'); + await expect(archive.read('missing.txt')).rejects.toThrow(/not in the archive/); + }); + + it('stops listing at the cap', async () => { + const files: Record = {}; + for (let i = 0; i < MAX_ZIP_ENTRIES + 5; i++) files[`f${i}.txt`] = ''; + const archive = await openZip(await makeZip(files)); + expect(archive.entries).toHaveLength(MAX_ZIP_ENTRIES); + expect(archive.truncated).toBe(true); + }); + + it('says when an archive is password-protected', async () => { + const locked = markEncrypted(await makeZip({ 'secret.txt': 'x' })); + await expect(openZip(locked)).rejects.toBeInstanceOf(ZipEncryptedError); + }); + + it('rejects data that is not a ZIP', async () => { + await expect(openZip(new TextEncoder().encode('not a zip'))).rejects.not.toBeInstanceOf(ZipEncryptedError); + }); +}); + +describe('isZipArchive', () => { + it('takes ZIPs by type or name, not the formats built on ZIP', () => { + expect(isZipArchive('a.zip', 'application/zip')).toBe(true); + expect(isZipArchive('bundle', 'application/x-zip-compressed')).toBe(true); + expect(isZipArchive('Archive.ZIP', 'application/octet-stream')).toBe(true); + expect(isZipArchive('contract.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')).toBe(false); + expect(isZipArchive('app.apk', 'application/vnd.android.package-archive')).toBe(false); + }); +}); diff --git a/src/lib/file-preview.ts b/src/lib/file-preview.ts index 8b707074..6e3fcb2e 100644 --- a/src/lib/file-preview.ts +++ b/src/lib/file-preview.ts @@ -5,7 +5,7 @@ // everything else goes to the OS "Open with" chooser. export type PreviewKind = - | 'image' | 'pdf' | 'text' | 'markdown' | 'html' | 'eml' | 'video' | 'audio' | 'none'; + | 'image' | 'pdf' | 'text' | 'markdown' | 'html' | 'eml' | 'video' | 'audio' | 'archive' | 'none'; export interface FilePreviewOptions { /** @@ -26,14 +26,16 @@ interface PreviewableFile { * Largest file each preview kind loads. Text is rendered in one selectable * ``, which gets slow to lay out past a few hundred KB; HTML and * Markdown go through the mail sanitiser in JS; parsing an .eml and decoding - * an image hold the whole file in memory. PDFs are paged by their viewer and - * media stream from the downloaded copy. Larger files open in another app. + * an image hold the whole file in memory, and so does listing a ZIP. PDFs + * are paged by their viewer and media stream from the downloaded copy. + * Larger files open in another app. */ export const FILE_PREVIEW_MAX_BYTES: Record, number> = { text: 512 * 1024, markdown: 1024 * 1024, html: 5 * 1024 * 1024, eml: 10 * 1024 * 1024, + archive: 50 * 1024 * 1024, image: 25 * 1024 * 1024, pdf: 100 * 1024 * 1024, audio: 500 * 1024 * 1024, @@ -94,6 +96,7 @@ const MEDIA: Record<'video' | 'audio', Record, op const media = /^(image|audio|video)\//.test(mime); if (mime === 'message/rfc822' || ext === 'eml') return 'eml'; + // ZIPs list their files; formats built on ZIP (docx, apk) are not ZIPs here. + if (ZIP_MIME.has(mime) || ((!mime || mime.startsWith('application/x-')) && ext === 'zip')) return 'archive'; if (HTML_MIME.has(mime) || HTML_EXT.has(ext)) return 'html'; if (MARKDOWN_MIME.has(mime) || MARKDOWN_EXT.has(ext)) return 'markdown'; diff --git a/src/lib/zip-preview.ts b/src/lib/zip-preview.ts new file mode 100644 index 00000000..848c0544 --- /dev/null +++ b/src/lib/zip-preview.ts @@ -0,0 +1,90 @@ +// ZIP archives in the file preview: the entries as a list, and one entry's +// bytes when it is opened. Nothing is extracted to disk under the archive's +// paths; an opened entry goes through the same preview as any attachment. + +import type JSZip from 'jszip'; + +export interface ZipEntry { + /** Full path inside the archive, as stored. */ + path: string; + /** Last path segment. */ + name: string; + /** Folder path, '' at the top. */ + folder: string; + /** Uncompressed size, when the archive records it. */ + size?: number; + modified?: Date; +} + +export interface ZipArchive { + entries: ZipEntry[]; + /** True when the listing stopped at {@link MAX_ZIP_ENTRIES}. */ + truncated: boolean; + /** One entry's bytes. */ + read(path: string): Promise; +} + +/** The archive is password-protected; JSZip can't decrypt it. */ +export class ZipEncryptedError extends Error {} + +// Metadata the archives of macOS and Windows carry and nobody wants to open. +const JUNK = /(^|\/)(__MACOSX\/|\.DS_Store$|Thumbs\.db$|desktop\.ini$)/i; + +/** Most entries listed; a larger archive lists the first ones. */ +export const MAX_ZIP_ENTRIES = 2000; + +// JSZip keeps the directory's sizes here without exposing them. +interface ZipObjectInternals { + _data?: { uncompressedSize?: number }; +} + +/** + * Read an archive's directory: its files (not folders), sorted by folder and + * then name, and a reader for their contents. + */ +export async function openZip(bytes: Uint8Array): Promise { + const { default: Zip } = await import('jszip'); + let zip: JSZip; + try { + zip = await Zip.loadAsync(bytes, { createFolders: false }); + } catch (e) { + if (/encrypt/i.test(e instanceof Error ? e.message : String(e))) throw new ZipEncryptedError('encrypted'); + throw e; + } + const entries: ZipEntry[] = []; + let truncated = false; + for (const file of Object.values(zip.files)) { + if (file.dir || JUNK.test(file.name)) continue; + if (entries.length >= MAX_ZIP_ENTRIES) { + truncated = true; + break; + } + const path = file.name; + const slash = path.lastIndexOf('/'); + const size = (file as unknown as ZipObjectInternals)._data?.uncompressedSize; + entries.push({ + path, + name: slash >= 0 ? path.slice(slash + 1) : path, + folder: slash >= 0 ? path.slice(0, slash) : '', + size: typeof size === 'number' && size >= 0 ? size : undefined, + modified: file.date instanceof Date && !Number.isNaN(file.date.getTime()) ? file.date : undefined, + }); + } + entries.sort((a, b) => a.folder.localeCompare(b.folder) || a.name.localeCompare(b.name)); + return { + entries, + truncated, + read: async (path) => { + const file = zip.file(path); + if (!file) throw new Error(`${path} is not in the archive`); + return file.async('uint8array'); + }, + }; +} + +/** Whether a file is a ZIP the preview lists (not one of the formats built on ZIP). */ +export function isZipArchive(name: string, type?: string | null): boolean { + const mime = (type || '').split(';')[0].trim().toLowerCase(); + if (mime === 'application/zip' || mime === 'application/x-zip-compressed' || mime === 'application/x-zip') return true; + return /\.zip$/i.test(name); +} From 1f253e9488e6349d7833d76a8da7fa108a35d409 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:40:59 +0200 Subject: [PATCH 251/334] fix: stop a PDF page rendered after its preview closed from leaving files behind --- .../main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt index c2461c1c..e92b0805 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkPdfModule.kt @@ -82,7 +82,9 @@ class BulwarkPdfModule(reactContext: ReactApplicationContext) : ReactContextBase bitmap.eraseColor(Color.WHITE) page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) val out = fileOf(outUri) - out.parentFile?.mkdirs() + // The preview's folder is gone once it closed; a page + // still queued then must not bring it back. + if (out.parentFile?.isDirectory != true) throw IllegalStateException("preview closed") FileOutputStream(out).use { stream -> bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream) } } finally { bitmap.recycle() From 9166b0fb3cffc2e19a562b37167b8e663b698c41 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:40:59 +0200 Subject: [PATCH 252/334] docs: note which files the preview shows --- docs/parity/03-email-viewer.md | 2 +- docs/parity/07-filters-vacation-files.md | 2 +- src/screens/FilesScreen.tsx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/parity/03-email-viewer.md b/docs/parity/03-email-viewer.md index 8e89c446..c232c144 100644 --- a/docs/parity/03-email-viewer.md +++ b/docs/parity/03-email-viewer.md @@ -175,7 +175,7 @@ RN has a solid single-message reader (WebView body with CSP, shrink-to-fit + pin - What RN does: single tap follows the global `mailAttachmentAction`; `jszip` is already a dependency (`package.json:36`) but unused for mail. - Fix hint: long-press sheet with "Open / Save / Share"; a "Download all" entry building the zip with `jszip` into `Paths.cache` then sharing. -- [x] **In-app attachment preview (images, PDF, text, audio/video) is absent; iOS "Preview" is just the share sheet — fixed in d2ed27f** — `P3` — `partial` +- [x] **In-app attachment preview (images, PDF, text, audio/video) is absent; iOS "Preview" is just the share sheet — fixed in d2ed27f; audio/video, HTML, Markdown, SVG, Android PDFs and ZIPs since 0.1.80-0.1.82** — `P3` — `partial` - What WEB does: `FilePreviewModal` for images/PDF (bytes handed to pdf.js, #871)/text/markdown/audio/video/eml, script-bearing types forced to download (`components/mail/mail-app.tsx:2887-2935`, `lib/file-preview.ts:29-92`). - What RN does: Android hands the file to an external viewer via `ACTION_VIEW`, iOS opens the share sheet (`src/lib/email-export.ts:64-79`, `115-141`); nothing is previewed inside the app. - Fix hint: for images/PDF/text open a modal `WebView` on the cached `file://` (or use `expo-file-system`'s `File.uri` with a QuickLook bridge on iOS); keep the external-viewer path as fallback. diff --git a/docs/parity/07-filters-vacation-files.md b/docs/parity/07-filters-vacation-files.md index 22de4820..f8d1a8d6 100644 --- a/docs/parity/07-filters-vacation-files.md +++ b/docs/parity/07-filters-vacation-files.md @@ -110,7 +110,7 @@ Filters: RN carries a byte-for-byte port of WEB's Sieve parser/generator/tests a - What RN does: `getDocumentAsync({ multiple: false })` (`src/screens/FilesScreen.tsx:357-360`); uploading the same name twice creates two nodes with the same name. - Fix hint: `multiple: true` + sequential upload loop; reuse WEB's `getUniqueName` against `visibleFiles`. -- [x] **No in-app preview (image/text/markdown/PDF/audio/video/eml)** — `P2` — `missing` — done in 409cdb1 (images, text and source files, `.eml`, and PDFs on iOS open in the reader's preview; PDFs on Android, audio, video, HTML and Markdown still go to another app) +- [x] **No in-app preview (image/text/markdown/PDF/audio/video/eml)** — `P2` — `missing` — done in 409cdb1 (images, text and source files, `.eml`, and PDFs on iOS open in the reader's preview); since 0.1.80/0.1.81 also audio, video, SVG/ICO, HTML, Markdown, PDFs on Android (native `PdfRenderer`) and ZIP listings, through the one preview the reader, list, Files and compose share (`src/components/email/use-file-preview.ts`) - What WEB does: `ImagePreviewModal` and `FilePreviewModal` (text, markdown, PDF incl. mobile pdf.js viewer, audio, video, EML) chosen via `getFilePreviewKind` (`lib/file-preview.ts:29-68`; `components/files/file-preview-modal.tsx`, `image-preview-modal.tsx`, `pdf-mobile-viewer.tsx`; `files-app.tsx:487-495`, `690-708`). - What RN does: tapping a file calls `shareAttachment` which downloads to cache and hands off to the OS share/viewer sheet (`src/screens/FilesScreen.tsx:248-258`; `src/lib/email-export.ts` "preview" variant). Works but there is no inline viewer and no "recent files" tracking. - Fix hint: reuse whatever attachment viewer the mail area has (image modal / WebView for text+PDF); N/A if the team decides the OS viewer is the mobile answer - then close this item. diff --git a/src/screens/FilesScreen.tsx b/src/screens/FilesScreen.tsx index 14bf2080..a05cc7f2 100644 --- a/src/screens/FilesScreen.tsx +++ b/src/screens/FilesScreen.tsx @@ -360,8 +360,8 @@ export default function FilesScreen() { } }, [t]); - // Tap on a file: the in-app preview for the types it shows (images, text - // and code, .eml, PDFs on iOS), the other app for the rest. + // Tap on a file: the in-app preview for the types it shows (lib/file-preview), + // the other app for the rest. const openFile = useCallback((row: FileRow) => { if (canPreviewInApp(row)) setPreviewTarget(row); else void openFileWith(row); From 323797b4eea2299d1fff38c069bae020575341c2 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:42:04 +0200 Subject: [PATCH 253/334] fix: release a PDF that finishes opening after its preview was closed --- src/components/email/use-file-preview.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/email/use-file-preview.ts b/src/components/email/use-file-preview.ts index 0a9d9c2a..29a40b8b 100644 --- a/src/components/email/use-file-preview.ts +++ b/src/components/email/use-file-preview.ts @@ -224,6 +224,8 @@ export function useFilePreview(opts: { zipUnreadable: t('email_viewer.preview.archive_unreadable', "This archive can't be opened here."), }, colors.current); if (current()) setItem(next); + // Closed while the PDF was opening: end() ran before the document was. + else closePdfPages(local.uri); } catch (e) { if (!current()) return; close(); From 86400b124a38a007af1b486dde1337ca8d63e0a3 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 16 Oct 2026 19:46:04 +0200 Subject: [PATCH 254/334] chore: update version number to 0.1.82 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 962d1c10..59e55c1f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.81 +0.1.82 diff --git a/package-lock.json b/package-lock.json index cebe5dab..3a7f58cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.81", + "version": "0.1.82", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.81", + "version": "0.1.82", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index e25dd504..94d88fd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.81", + "version": "0.1.82", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From 2b486cb1ef0104b9369b5c5170f37e7c93ea72ff Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:02:29 +0200 Subject: [PATCH 255/334] fix: show tags with no local definition in the message header, as the list does #79 --- src/components/email/MessageHeader.tsx | 8 +++----- src/lib/__tests__/thread-utils.test.ts | 10 ++++++++++ src/lib/thread-utils.ts | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/components/email/MessageHeader.tsx b/src/components/email/MessageHeader.tsx index 3d8bca3a..bd599b3c 100644 --- a/src/components/email/MessageHeader.tsx +++ b/src/components/email/MessageHeader.tsx @@ -9,7 +9,7 @@ import { useColors } from '../../theme/colors'; import SenderAvatar from '../SenderAvatar'; import { useSettingsStore } from '../../stores/settings-store'; import { useLocaleStore } from '../../stores/locale-store'; -import { useKeywordsStore, keywordToken } from '../../stores/keywords-store'; +import { useKeywordsStore } from '../../stores/keywords-store'; import { emailDisplayDate, formatHeaderDate, formatHeaderTime, formatFullDateTime } from '../../lib/email-date'; import { deriveHeaderInfo, deliveryDeltaMs, formatDelta, isAuthenticationSpoofed, findReceivingIdentity, @@ -17,6 +17,7 @@ import { } from '../../lib/email-headers'; import { formatSize } from '../../lib/attachment-display'; import { isSmimeEmail } from '../../lib/smime'; +import { resolveEmailTags } from '../../lib/thread-utils'; interface Props { email: Email; @@ -121,10 +122,7 @@ export function MessageHeader({ email, identities, headerInfo, onToggleStar, onA return null; }, [spoofed, identities, from?.email, email]); - const tags = React.useMemo( - () => keywordDefs.filter((kw) => !!email.keywords?.[keywordToken(kw.id)]), - [keywordDefs, email.keywords], - ); + const tags = React.useMemo(() => resolveEmailTags(email.keywords, keywordDefs), [keywordDefs, email.keywords]); const important = !!email.keywords?.$important; const answered = !!email.keywords?.$answered; const forwarded = !!email.keywords?.$forwarded; diff --git a/src/lib/__tests__/thread-utils.test.ts b/src/lib/__tests__/thread-utils.test.ts index 13ff3814..80099775 100644 --- a/src/lib/__tests__/thread-utils.test.ts +++ b/src/lib/__tests__/thread-utils.test.ts @@ -4,6 +4,7 @@ import { expandThreadSelection, getEmailTagIds, getThreadTagIds, + resolveEmailTags, tagIdFromKeyword, threadKeyOf, rowKeyOf, @@ -30,6 +31,15 @@ describe('tag ids', () => { ])).toEqual(['work', 'todo']); }); + it('keeps a tag with no local definition, grey and under its raw id (#79)', () => { + const defs = [{ id: 'work', label: 'Work', color: 'blue' as const }]; + expect(resolveEmailTags({ '$label:work': true, '$label:pgp-encrypted': true, $seen: true }, defs)).toEqual([ + { id: 'work', label: 'Work', color: 'blue' }, + { id: 'pgp-encrypted', label: 'pgp-encrypted', color: 'gray' }, + ]); + expect(resolveEmailTags(undefined, defs)).toEqual([]); + }); + it('extracts the tag id from a keyword', () => { expect(tagIdFromKeyword('$label:work')).toBe('work'); expect(tagIdFromKeyword('$color:work')).toBe('work'); diff --git a/src/lib/thread-utils.ts b/src/lib/thread-utils.ts index ad65ced3..84a4529c 100644 --- a/src/lib/thread-utils.ts +++ b/src/lib/thread-utils.ts @@ -1,4 +1,5 @@ import type { Email } from '../api/types'; +import type { KeywordDef } from '../stores/keywords-store'; // Port of the webmail's `lib/thread-utils.ts` (tag id extraction, thread // grouping helpers) trimmed to what the native list needs. @@ -23,6 +24,21 @@ export function getEmailTagIds(keywords: Record | undefined): s return [...tags]; } +/** + * The tags to draw for a message: every tag id on it, with its local + * definition where there is one. A tag no local definition explains (set by + * another client or by the server) is kept, grey and labelled with its raw + * id, so the header shows the same tags as the list row (#79). + */ +export function resolveEmailTags( + keywords: Record | undefined, + defs: readonly KeywordDef[], +): KeywordDef[] { + return getEmailTagIds(keywords).map( + (id) => defs.find((d) => d.id === id) ?? { id, label: id, color: 'gray' }, + ); +} + /** * Every tag anywhere in a thread, deduplicated. A collapsed thread row stands * in for all its messages, so it has to account for all their tags. From 083ec958a008cdb6e47d94608bf413338a6ba129 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:03:52 +0200 Subject: [PATCH 256/334] chore: sync the locale catalogs from the webmail --- locales/ar/common.json | 8151 +++++++++++++++++++----------------- locales/ca/common.json | 8151 +++++++++++++++++++----------------- locales/cs/common.json | 8151 +++++++++++++++++++----------------- locales/da/common.json | 8151 +++++++++++++++++++----------------- locales/de/common.json | 1045 ++++- locales/en/common.json | 1041 ++++- locales/es/common.json | 8151 +++++++++++++++++++----------------- locales/et/common.json | 4506 ++++++++++++++++++++ locales/fa/common.json | 8151 +++++++++++++++++++----------------- locales/fr/common.json | 8151 +++++++++++++++++++----------------- locales/he/common.json | 8153 ++++++++++++++++++++----------------- locales/hu/common.json | 8151 +++++++++++++++++++----------------- locales/it/common.json | 8151 +++++++++++++++++++----------------- locales/ja/common.json | 8151 +++++++++++++++++++----------------- locales/ko/common.json | 8151 +++++++++++++++++++----------------- locales/lv/common.json | 8151 +++++++++++++++++++----------------- locales/mn/common.json | 8151 +++++++++++++++++++----------------- locales/nb/common.json | 8151 +++++++++++++++++++----------------- locales/nl/common.json | 8151 +++++++++++++++++++----------------- locales/pl/common.json | 8151 +++++++++++++++++++----------------- locales/pt/common.json | 8151 +++++++++++++++++++----------------- locales/rn/ar.json | 6 +- locales/rn/ca.json | 6 +- locales/rn/cs.json | 6 +- locales/rn/da.json | 6 +- locales/rn/de.json | 6 +- locales/rn/en.json | 95 +- locales/rn/es.json | 6 +- locales/rn/fa.json | 6 +- locales/rn/fr.json | 6 +- locales/rn/he.json | 6 +- locales/rn/hu.json | 6 +- locales/rn/it.json | 6 +- locales/rn/ja.json | 6 +- locales/rn/ko.json | 6 +- locales/rn/lv.json | 6 +- locales/rn/mn.json | 6 +- locales/rn/nb.json | 6 +- locales/rn/nl.json | 6 +- locales/rn/pl.json | 6 +- locales/rn/pt.json | 6 +- locales/rn/ro.json | 6 +- locales/rn/ru.json | 6 +- locales/rn/sk.json | 6 +- locales/rn/tr.json | 6 +- locales/rn/uk.json | 6 +- locales/rn/zh-TW.json | 6 +- locales/rn/zh.json | 6 +- locales/ro/common.json | 8151 +++++++++++++++++++----------------- locales/ru/common.json | 8151 +++++++++++++++++++----------------- locales/sk/common.json | 8151 +++++++++++++++++++----------------- locales/tr/common.json | 8151 +++++++++++++++++++----------------- locales/uk/common.json | 8151 +++++++++++++++++++----------------- locales/zh-TW/common.json | 8151 +++++++++++++++++++----------------- locales/zh/common.json | 8151 +++++++++++++++++++----------------- 55 files changed, 119142 insertions(+), 91478 deletions(-) create mode 100644 locales/et/common.json diff --git a/locales/ar/common.json b/locales/ar/common.json index c5c0143b..d300aa76 100644 --- a/locales/ar/common.json +++ b/locales/ar/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "عميل بريد إلكتروني ويب بسيط يستخدم بروتوكول JMAP", - "login": { - "title": "البريد الإلكتروني", - "username_label": "البريد الإلكتروني", - "username_placeholder": "user@example.com", - "password_label": "كلمة المرور", - "password_placeholder": "أدخل كلمة المرور", - "jmap_endpoint_label": "خادم JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "يجب أن يسمح الخادم بطلبات CORS من هذا النطاق.", - "jmap_server_label": "الخادم", - "jmap_server_auto_picked": "تم اختيار الخادم بناءً على نطاق بريدك الإلكتروني.", - "sign_in": "تسجيل الدخول", - "signing_in": "جارٍ تسجيل الدخول...", - "loading": "جارٍ التحميل...", - "reconnecting": "انقطع الاتصال. جارٍ محاولة إعادة الاتصال…", - "error": { - "invalid_credentials": "البريد الإلكتروني أو كلمة المرور غير صحيحة. يرجى التحقق من بياناتك والمحاولة مرة أخرى.", - "connection_failed": "تعذّر الوصول إلى الخادم. تحقق من اتصالك بالإنترنت وحاول مرة أخرى.", - "cors_blocked": "الخادم متاح لكنه يحظر الطلبات من مصادر مختلفة (CORS). تحقق من إعدادات CORS في خادم JMAP واسمح بهذا النطاق.", - "server_error": "الخادم غير متاح مؤقتًا. يرجى المحاولة مرة أخرى لاحقًا.", - "generic": "حدث خطأ غير متوقع. إذا استمرت المشكلة، تواصل مع المسؤول.", - "totp_required": "مطلوب رمز مصادقة ثنائية. يرجى إدخال الرمز أدناه.", - "totp_invalid": "رمز المصادقة غير صحيح. تحقق من تطبيق المصادقة وحاول مرة أخرى.", - "oauth_discovery_failed": "تسجيل الدخول الموحد (SSO) مفعّل، لكن تعذّر الوصول إلى مزوّد الهوية. تحقق من إعدادات OAuth." - }, - "show_password": "إظهار كلمة المرور", - "hide_password": "إخفاء كلمة المرور", - "totp_toggle": "لديّ رمز مصادقة ثنائية", - "remember_me": "تذكرني", - "config_error": { - "title": "خطأ في الإعداد", - "fetch_failed": "تعذّر تحميل إعدادات التطبيق. يرجى المحاولة مرة أخرى لاحقًا.", - "server_not_configured": "لم يتم إعداد خادم البريد. يرجى التواصل مع المسؤول." - }, - "remove_from_history": "إزالة من السجل", - "totp_label": "رمز المصادقة", - "totp_placeholder": "000000", - "session_expired": "انتهت صلاحية الجلسة. يرجى تسجيل الدخول مرة أخرى.", - "dismiss": "تجاهل", - "or": "أو", - "sign_in_sso": "تسجيل الدخول عبر SSO", - "add_account_title": "إضافة حساب", - "add_account_subtitle": "تسجيل الدخول بحساب آخر", - "cancel": "إلغاء", - "website": "الموقع الإلكتروني", - "imprint": "بيانات الناشر", - "privacy_policy": "سياسة الخصوصية", - "try_demo": "جرّب النسخة التجريبية", - "demo_description": "استكشف باستخدام بيانات نموذجية - دون الحاجة إلى حساب", - "demo_launching": "جارٍ تشغيل النسخة التجريبية...", - "demo_login_button": "تشغيل النسخة التجريبية", - "demo_tagline": "جرّب عميل بريد إلكتروني متكامل الميزات. لا حاجة إلى حساب.", - "demo_no_signup": "لا حاجة للتسجيل - استكشف بحرية باستخدام بيانات نموذجية", - "oauth_completing": "جارٍ إتمام تسجيل الدخول...", - "oauth_error": { - "title": "فشلت المصادقة", - "invalid_state": "فشل التحقق الأمني. يرجى محاولة تسجيل الدخول مرة أخرى.", - "missing_params": "بيانات التفويض مفقودة. يرجى محاولة تسجيل الدخول مرة أخرى.", - "token_exchange_failed": "فشل إتمام المصادقة. يرجى المحاولة مرة أخرى.", - "access_denied": "تم رفض الوصول. يرجى التواصل مع المسؤول.", - "back_to_login": "العودة إلى تسجيل الدخول" - } - }, - "sidebar": { - "close": "إغلاق", - "compose": "إنشاء رسالة", - "compose_hint": "إنشاء رسالة (c)", - "search_placeholder": "بحث في البريد...", - "search_placeholder_hint": "بحث في البريد... (اضغط /)", - "storage": "التخزين", - "storage_used": "المستخدم", - "storage_free": "المتاح", - "storage_total": "الإجمالي", - "sign_out": "تسجيل الخروج", - "sign_out_of": "تسجيل الخروج من {account}", - "sign_out_all": "تسجيل الخروج من جميع الحسابات", - "add_account": "إضافة حساب", - "set_as_default": "تعيين كافتراضي", - "switch_account": "تبديل الحساب", - "contacts": "جهات الاتصال", - "calendar": "التقويم", - "settings": "الإعدادات", - "admin": "الإدارة", - "files": "الملفات", - "loading_mailboxes": "جارٍ تحميل صناديق البريد...", - "push_connected": "التحديثات الفورية نشطة", - "push_disconnected": "التحديثات الفورية غير نشطة", - "keyboard_shortcuts": "اختصارات لوحة المفاتيح", - "theme": { - "light": "الوضع الفاتح", - "dark": "الوضع الداكن", - "system": "وضع النظام" - }, - "language": { - "title": "اللغة" - }, - "mailboxes": { - "inbox": "البريد الوارد", - "sent": "المرسل", - "drafts": "المسودات", - "trash": "المهملات", - "archive": "الأرشيف", - "starred": "المميزة بنجمة", - "all_mail": "كل البريد", - "spam": "البريد المزعج", - "important": "مهم" - }, - "unified_inbox": "البريد الوارد الموحّد", - "unified_sent": "كل المرسل", - "unified_drafts": "كل المسودات", - "unified_trash": "كل المهملات", - "unified_archive": "كل الأرشيف", - "unified_junk": "كل البريد المزعج", - "all_accounts": "كل الحسابات", - "unified_mailbox": "صندوق البريد الموحّد", - "expand": "توسيع", - "collapse": "طي", - "expand_tooltip": "توسيع", - "collapse_tooltip": "طي", - "mobile": { - "search": "بحث", - "compose": "إنشاء رسالة", - "go_back": "العودة", - "toggle_menu": "تبديل القائمة" - }, - "clear_search": "مسح البحث", - "vacation_active": "الرد التلقائي أثناء الغياب مفعّل", - "demo_banner": "وضع تجريبي", - "demo_reset": "إعادة تعيين", - "demo_tour": "جولة", - "tags": "الوسوم", - "show_all_tags": "إظهار الكل ({count})", - "show_fewer_tags": "إظهار أقل", - "folders": "المجلدات", - "shared": "مشترك", - "mail": "البريد", - "nav_label": "التنقل", - "add_app": "التطبيقات", - "scheduled": "المجدولة", - "unified_all_unread": "كل غير المقروء", - "unified_all_starred": "كل المميز بنجمة", - "unified_all_mail": "كل البريد", - "remove_account": "إزالة الحساب", - "remove_account_confirm": "هل تريد إزالة {account} من هذا الجهاز؟ يمكنك إضافته مرة أخرى لاحقًا." - }, - "protocol_handlers": { - "title": "التطبيقات الافتراضية", - "description": "اختر ما إذا كانت روابط البريد والتقويم تُفتح في Bulwark. من الناحية التقنية، يسجّل Bulwark نفسه كمعالج بروتوكول لروابط mailto: وwebcal:.", - "unsupported": "هذا المتصفح أو الاتصال لا يدعم التسجيل اليدوي لمعالج البروتوكول. قد يمكنك مع ذلك استخدام تطبيق PWA المثبّت عبر إعدادات المتصفح أو النظام.", - "mailto_label": "روابط البريد الإلكتروني", - "mailto_description": "فتح روابط mailto: في Bulwark مع نافذة إنشاء رسالة معبأة مسبقًا.", - "protocol_open_mode_label": "عند فتح روابط البروتوكول", - "protocol_open_mode_description": "اختر ما إذا كان Bulwark يفتح روابط mailto: وwebcal: في علامة تبويب جديدة أو يعيد استخدام جلسة مفتوحة. يتطلب خيار الجلسة النشطة إذن الإشعارات حتى تتمكن من النقر على إشعار احتياطي لإحضار Bulwark إلى المقدمة إذا منع المتصفح التركيز.", - "protocol_open_mode_active_session": "الفتح في الجلسة النشطة إن أمكن", - "protocol_open_mode_new_tab": "فتح علامة تبويب جديدة دائمًا", - "focus_notification_title": "فتح Bulwark", - "focus_notification_body": "تم فتح الرابط في Bulwark. انقر لإحضار النافذة إلى المقدمة.", - "webcal_label": "روابط التقويم", - "webcal_description": "فتح روابط webcal: في Bulwark مع نافذة اشتراك تقويم معبأة مسبقًا.", - "register_mailto": "تسجيل تطبيق البريد", - "register_webcal": "تسجيل تطبيق التقويم", - "mailto_registered": "تم طلب تسجيل معالج البريد الإلكتروني", - "webcal_registered": "تم طلب تسجيل معالج التقويم", - "registration_failed": "فشل تسجيل معالج البروتوكول", - "opening_mailto": "جارٍ فتح نافذة الإنشاء...", - "opening_webcal": "جارٍ فتح التقويم...", - "browser_note": "قد يطلب منك المتصفح أو نظام التشغيل تأكيد ذلك، وقد يتطلب تثبيت Bulwark أولًا قبل إمكانية اختياره كتطبيق افتراضي.", - "select_account_title": "اختر الحساب", - "select_mailto_account": "اختر الحساب الذي سيفتح رابط البريد هذا.", - "select_webcal_account": "اختر الحساب الذي سيفتح رابط التقويم هذا.", - "select_account_note": "ينطبق هذا فقط على رابط البروتوكول هذا.", - "detail_to": "إلى", - "detail_subject": "الموضوع", - "detail_no_subject": "بلا موضوع", - "detail_calendar": "التقويم", - "detail_source": "المصدر", - "active_account": "نشط", - "switching_account": "جارٍ تبديل الحساب..." - }, - "sidebar_apps": { - "modal_title": "تطبيقات الشريط الجانبي", - "add_new": "إضافة تطبيق", - "edit_app": "تعديل التطبيق", - "name_label": "الاسم", - "name_placeholder": "تطبيقي", - "name_required": "الاسم مطلوب", - "url_label": "الرابط", - "url_required": "الرابط مطلوب", - "url_invalid": "أدخل رابط http أو https صالحًا", - "icon_label": "الأيقونة", - "icon_required": "الأيقونة مطلوبة", - "open_mode_label": "وضع الفتح", - "open_new_tab": "علامة تبويب جديدة", - "open_inline": "ضمن الصفحة", - "cancel": "إلغاء", - "add": "إضافة", - "update": "تحديث", - "delete": "حذف", - "delete_confirm_title": "حذف التطبيق", - "delete_confirm": "هل أنت متأكد أنك تريد حذف \"{name}\"؟", - "no_apps": "لم تتم إضافة أي تطبيقات بعد", - "no_apps_hint": "أضف تطبيقات وروابط مخصصة إلى الشريط الجانبي", - "managed_title": "مقدَّمة من المسؤول", - "managed_description": "تم إعداد هذه التطبيقات للجميع ولا يمكن تغييرها.", - "managed_badge": "مُدارة", - "search_icons": "بحث عن أيقونات...", - "show_popular": "الأكثر شيوعًا", - "show_all": "الكل", - "no_icons_found": "لم يتم العثور على أيقونات", - "inline_badge": "ضمن الصفحة", - "tab_badge": "علامة تبويب", - "show_on_mobile": "إظهار على الجوال" - }, - "email_list": { - "no_emails": "لم يتم العثور على رسائل", - "no_emails_description": "صندوق البريد هذا فارغ", - "no_search_results": "لم يتم العثور على نتائج", - "no_search_results_description": "حاول تعديل البحث أو عوامل التصفية", - "loading": "جارٍ تحميل الرسائل...", - "unread": "غير مقروءة", - "to_me": "إليّ", - "to_recipients": "إلى {count} من المستلمين", - "and_others": "و{count} آخرين", - "draft": "مسودة", - "starred": "مميزة بنجمة", - "pinned": "مثبتة", - "replied": "تم الرد عليها", - "forwarded": "تمت إعادة توجيهها", - "has_attachment": "تحتوي على مرفق", - "conversations_count": "{count} من {total} محادثة", - "conversations_count_plus": "{count}+ محادثة", - "conversations_count_simple": "{count} محادثة", - "no_conversations": "لا توجد محادثات", - "loading_more": "جارٍ تحميل المزيد من الرسائل...", - "no_more_emails": "لا مزيد من الرسائل للتحميل", - "batch_actions": { - "select": "تحديد الرسائل", - "select_all": "تحديد الكل", - "selected_messages": "{count, plural, one {تم تحديد رسالة واحدة} other {تم تحديد # رسالة}}", - "mark_read": "تمييز كمقروءة", - "mark_unread": "تمييز كغير مقروءة", - "delete": "حذف", - "delete_confirm_title": "حذف الرسائل", - "delete_confirm_message": "هل أنت متأكد أنك تريد حذف {count, plural, one {رسالة واحدة} other {# رسالة}}؟", - "clear_selection": "إلغاء التحديد" - }, - "permanent_delete": "حذف نهائي", - "permanent_delete_confirm_title": "حذف نهائي", - "permanent_delete_confirm_message": "سيتم حذف هذه الرسالة نهائيًا. لا يمكن التراجع عن هذا الإجراء.", - "permanent_delete_confirm_batch_message": "سيتم حذف {count, plural, one {رسالة واحدة} other {# رسالة}} نهائيًا. لا يمكن التراجع عن هذا الإجراء.", - "empty_folder": { - "button": "إفراغ المجلد", - "confirm_title": "إفراغ المجلد", - "confirm_message": "سيتم حذف جميع الرسائل في هذا المجلد نهائيًا. لا يمكن التراجع عن هذا الإجراء.", - "confirm_button": "إفراغ المجلد", - "junk_hint": "يمكنك إفراغ مجلد البريد المزعج لحذف جميع الرسائل نهائيًا.", - "trash_hint": "يمكنك إفراغ مجلد المهملات لحذف جميع الرسائل نهائيًا." - }, - "no_scheduled_emails": "لا توجد رسائل مجدولة", - "no_scheduled_emails_description": "ستظهر هنا الرسائل المجدولة للإرسال لاحقًا.", - "scheduled_count": "{count, plural, one {رسالة مجدولة واحدة} other {# رسالة مجدولة}}", - "scheduled_actions_hint": "يمكن إلغاء الرسائل المجدولة أو إعادة جدولتها أو تعديلها من إجراءاتها المجدولة.", - "cancel_scheduled_send": "إلغاء الإرسال", - "reschedule_send": "إعادة الجدولة", - "cancel_and_edit": "إلغاء والتعديل", - "cancel_and_compose_again": "إلغاء وإعادة الكتابة", - "reschedule_prompt": "أدخل تاريخًا/وقتًا جديدًا، مثل 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "طلب المُرسل إشعاره عند فتح هذه الرسالة.", - "send": "إرسال إيصال", - "ignore": "تجاهل", - "sent": "تم إرسال إيصال القراءة.", - "send_failed": "تعذّر إرسال إيصال القراءة", - "mdn_subject": "تمت القراءة: {subject}", - "mdn_body": "هذا إيصال إرجاع للرسالة التي أرسلتها إلى {recipient}.\n\nملاحظة: يفيد هذا الإيصال فقط بأن الرسالة عُرضت على جهاز المستلم. لا يوجد ضمان بأن المستلم قد قرأ أو فهم محتوى الرسالة." - }, - "no_email_selected": "لم يتم تحديد أي رسالة", - "no_email_description": "اختر رسالة من القائمة لعرضها هنا", - "no_conversation_selected": "لم يتم تحديد أي محادثة", - "no_conversation_description": "اختر محادثة من القائمة لقراءتها هنا", - "compose": "إنشاء رسالة", - "compose_hint": "إنشاء رسالة جديدة", - "no_subject": "(بلا موضوع)", - "no_body_content": "(لا يوجد محتوى نصي متاح)", - "no_preview_available": "لا تتوفر معاينة", - "show_quoted_text": "إظهار النص المقتبس", - "hide_quoted_text": "إخفاء النص المقتبس", - "loading_email": "جارٍ تحميل الرسالة...", - "loading": "جارٍ التحميل...", - "reply": "رد", - "reply_all": "الرد على الجميع", - "forward": "إعادة توجيه", - "delete": "حذف", - "archive": "أرشفة", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "تمييز بنجمة", - "unstar": "إزالة النجمة", - "mark_unread": "تمييز كغير مقروءة", - "mark_read": "تمييز كمقروءة", - "unread": "غير مقروءة", - "read": "مقروءة", - "spam_short": "مزعج", - "not_spam_short": "ليس مزعجًا", - "move": "نقل", - "print": "طباعة", - "view_source": "عرض المصدر", - "fullscreen": "ملء الشاشة", - "exit_fullscreen": "الخروج من ملء الشاشة", - "export_email": "تصدير كملف ‎.eml", - "forward_as_attachment": "إعادة التوجيه كمرفق", - "import_email": "استيراد ملف ‎.eml أو ‎.zip", - "keyboard_shortcuts": "اختصارات لوحة المفاتيح (؟)", - "email_source": "مصدر الرسالة", - "draft_banner": "هذه الرسالة مسودة", - "edit_draft": "تعديل", - "copy_source": "نسخ إلى الحافظة", - "source_copied": "تم نسخ المصدر إلى الحافظة", - "attachments": "المرفقات", - "important": "مهم", - "download": "تنزيل", - "download_all": "تنزيل الكل", - "from": "من", - "to": "إلى", - "cc": "نسخة", - "bcc": "نسخة مخفية", - "date": "التاريخ", - "subject": "الموضوع", - "show_details": "إظهار التفاصيل", - "hide_details": "إخفاء التفاصيل", - "external_content_warning": "تم حظر الصور والمحتوى الخارجي", - "load_external_content": "تحميل الصور", - "trust_sender": "الوثوق بهذا المرسل دائمًا", - "back_to_list": "العودة إلى القائمة", - "view_contact": "عرض جهة الاتصال", - "message_details": "تفاصيل الرسالة", - "more_reply_options": "خيارات رد إضافية", - "set_tag": "تعيين وسم", - "tag": "وسم", - "more_actions": "المزيد من الإجراءات", - "previous": "السابق", - "next": "التالي", - "move_to": "نقل إلى...", - "remove_tag": "إزالة الوسم", - "tag_filter_placeholder": "تصفية الوسوم", - "tag_no_matches": "لا توجد وسوم مطابقة", - "more_count": "+{count} أخرى", - "characters_count": "{count} حرفًا", - "quick_reply_placeholder": "اكتب ردًا سريعًا...", - "more_options": "المزيد من الخيارات", - "sending": "جارٍ الإرسال...", - "security_authentication": "الأمان والمصادقة", - "technical_details": "التفاصيل التقنية", - "message_id_label": "معرّف الرسالة:", - "reply_to_label": "الرد إلى:", - "delivery_time_label": "وقت التسليم:", - "conversation_part_label": "جزء من المحادثة:", - "previous_messages": "{count} رسالة سابقة", - "previous_messages_plural": "{count} رسائل سابقة", - "time": { - "day": "يوم", - "days": "أيام", - "hour": "ساعة", - "hours": "ساعات", - "minute": "دقيقة", - "minutes": "دقائق" - }, - "unknown_sender": "غير معروف", - "recipient_me": "أنا", - "recipient_and_others": "{name} و{count} آخرين", - "recipient_to_prefix": "إلى:", - "authentication": { - "title": "المصادقة", - "status": { - "verified": "موثّق", - "warning": "تحذير", - "none": "غير موثّق" - }, - "spf": { - "pass": "نجاح SPF", - "fail": "فشل SPF", - "none": "لا يوجد SPF" - }, - "dkim": { - "pass": "DKIM صالح", - "fail": "DKIM غير صالح", - "none": "لا يوجد DKIM" - }, - "dmarc": { - "pass": "نجاح DMARC", - "fail": "فشل DMARC", - "none": "لا يوجد DMARC" - }, - "spam_score": "درجة البريد المزعج", - "tooltip_spf": "إطار سياسة المرسل (SPF): يتحقق من أن خادم الإرسال مخوّل بإرسال البريد نيابة عن النطاق", - "tooltip_dkim": "بريد معرّف بمفاتيح النطاق (DKIM): يؤكد أن الرسالة لم تُعدَّل أثناء النقل باستخدام توقيع تشفيري", - "tooltip_dmarc": "مصادقة الرسائل والإبلاغ والتوافق المستندة إلى النطاق (DMARC): تضمن توافق SPF وDKIM مع نطاق المرسل وتحدد سياسة عند الفشل", - "policy": "السياسة", - "result": { - "pass": "نجاح", - "fail": "فشل", - "softfail": "فشل بسيط", - "neutral": "محايد", - "permerror": "خطأ دائم", - "temperror": "خطأ مؤقت", - "none": "لا شيء" - } - }, - "details": { - "recipients_routing": "المستلمون والتوجيه", - "authentication_security": "المصادقة والأمان", - "identifiers_threading": "المعرّفات والمحادثة", - "mailing_list": "القائمة البريدية", - "message_properties": "خصائص الرسالة", - "sent": "أُرسلت", - "received": "استُلمت", - "delivery_time": "وقت التسليم", - "in_reply_to": "ردًا على", - "references": "المراجع", - "thread_id": "معرّف المحادثة", - "size": "الحجم", - "mime_type": "نوع MIME", - "attachments_summary": "{count} ملفات · {size}", - "list_id": "معرّف القائمة", - "list_help": "مساعدة القائمة", - "list_post": "نشر القائمة", - "list_unsubscribe": "إلغاء الاشتراك", - "iprev": "DNS العكسي", - "spam_status": "حالة البريد المزعج", - "ai_verdict": "تقييم الذكاء الاصطناعي", - "account": "الحساب", - "no_subject": "(بلا موضوع)" - }, - "headers": { - "routing": "التوجيه", - "received": "استُلمت", - "message_id": "معرّف الرسالة", - "list_info": "معلومات القائمة" - }, - "tooltips": { - "reply": "رد (r)", - "reply_all": "الرد على الجميع (a)", - "forward": "إعادة توجيه (f)", - "archive": "أرشفة (e)", - "delete": "حذف (# أو Del)", - "star": "تمييز بنجمة (s)", - "unstar": "إزالة النجمة (s)", - "compose": "إنشاء رسالة (c)", - "previous": "الرسالة السابقة", - "next": "الرسالة التالية", - "edit_draft": "تعديل المسودة" - }, - "spam": { - "button_title": "الإبلاغ عن بريد مزعج", - "not_spam_title": "تمييز كرسالة سليمة", - "toast_success": "تم النقل إلى البريد المزعج", - "toast_batch": "تم نقل {count} رسالة إلى البريد المزعج", - "toast_undo": "تراجع", - "toast_not_spam_success": "تم النقل إلى البريد الوارد", - "toast_not_spam_batch": "تم نقل {count} رسالة إلى البريد الوارد", - "error": "فشل الإبلاغ عن البريد المزعج", - "error_not_spam": "فشلت استعادة الرسالة" - }, - "unsubscribe_banner": { - "label": "نشرة بريدية", - "button": "إلغاء الاشتراك", - "confirm_title": "إلغاء الاشتراك من هذا المرسل؟", - "confirm_button": "تأكيد", - "cancel": "إلغاء", - "success_http": "تم فتح صفحة إلغاء الاشتراك في علامة تبويب جديدة", - "success_mailto": "تم إرسال رسالة إلغاء الاشتراك", - "confirm_message_http": "سيتم فتح صفحة إلغاء الاشتراك في علامة تبويب جديدة.", - "confirm_message_mailto": "سيتم إرسال رسالة إلغاء اشتراك إلى المرسل.", - "error": "تعذّر إلغاء الاشتراك", - "dismiss": "تجاهل" - }, - "calendar_invitation": { - "loading": "جارٍ تحميل تفاصيل الحدث…", - "title": "دعوة تقويم", - "published_title": "حدث منشور", - "response_title": "رد على الحدث", - "update_title": "تحديث الحدث", - "counter_title": "اقتراح مضاد", - "refresh_title": "طلب تحديث", - "declined_counter_title": "تم رفض الاقتراح المضاد", - "cancelled_title": "تم إلغاء الحدث", - "organizer": "نظّمه {name}", - "organizer_label": "نظّمه", - "attendees": "{count, plural, one {# حاضر} other {# حاضرين}}", - "accept": "قبول", - "maybe": "ربما", - "decline": "رفض", - "add_to_calendar": "إضافة إلى التقويم", - "added": "تمت الإضافة إلى التقويم", - "rsvp_sent": "تم إرسال الرد", - "parse_error": "تعذّرت قراءة الدعوة", - "action_failed": "تعذّر إتمام إجراء التقويم هذا.", - "no_calendar": "التقويم غير متاح", - "published_info": "تمت مشاركة هذا الحدث للاطلاع.", - "response_info": "تحتوي هذه الرسالة على رد أحد الحاضرين.", - "response_info_organizer": "يحدّث رد الحاضر هذا حدثك.", - "update_info": "تحدّث هذه الرسالة حدثًا موجودًا.", - "counter_info": "تقترح هذه الرسالة تغييرات على حدث.", - "counter_info_organizer": "اقترح هذا الحاضر تغييرات على حدثك.", - "refresh_info": "تطلب هذه الرسالة أحدث تفاصيل الحدث.", - "refresh_info_organizer": "طلب أحد الحاضرين أحدث تفاصيل الحدث.", - "declined_counter_info": "رفض المنظّم اقتراحًا مضادًا.", - "authentication_failed_info": "فشلت فحوصات مصادقة البريد لهذه الدعوة. تعامل مع إجراءات التقويم بحذر.", - "authentication_missing_info": "لا تتضمن هذه الدعوة مصادقة بريد موثّقة. تأكد من التفاصيل مع المنظّم إذا بدا أي شيء غير معتاد.", - "sender_mismatch_info": "أُرسلت هذه الدعوة من {sender}، بينما المنظّم المدرج في بيانات التقويم هو {organizer}.", - "sender_mismatch_unverified_info": "أُرسلت هذه الدعوة من {sender}، بينما المنظّم المدرج في بيانات التقويم هو {organizer}، ولم يتم التحقق من الرسالة.", - "organizer_role": "أنت منظّم هذا الحدث", - "your_response": "ردّك: {status}", - "response_needed": "بحاجة إلى رد", - "response_accepted": "مقبول", - "response_tentative": "مبدئي", - "response_declined": "مرفوض", - "response_delegated": "مفوَّض", - "actor_sent_info": "أُرسلت بواسطة {name}.", - "actor_response_info": "ردّ {name} بـ{status}.", - "actor_counter_info": "اقترح {name} تغييرات على هذا الحدث.", - "actor_refresh_info": "طلب {name} أحدث تفاصيل الحدث.", - "actor_declined_counter_info": "رفض {name} الاقتراح المضاد.", - "actor_note": "ملاحظة: {comment}", - "actor_unknown": "شخص ما", - "proposed_changes": "التغييرات المقترحة", - "change_title": "العنوان", - "change_time": "الوقت", - "change_location": "الموقع", - "change_description": "الوصف", - "change_empty": "لا شيء", - "change_from_to": "{before} -> {after}", - "apply_proposal": "تطبيق التغييرات المقترحة", - "proposal_applied": "تم تطبيق التغييرات المقترحة.", - "review_proposal": "مراجعة الاقتراح", - "review_request": "طلب مراجعة", - "view_in_calendar": "عرض في التقويم", - "select_calendar": "اختر التقويم", - "already_in_calendar": "موجود بالفعل في تقويمك", - "request_info": "تمت دعوتك إلى هذا الحدث. أجب لإعلام المنظّم بتوفرك.", - "cancel_info": "ألغى المنظّم هذا الحدث.", - "event_updated": "تحديث رقم {sequence}", - "event_status_tentative": "مبدئي", - "event_status_cancelled": "ملغى", - "expand": "إظهار التفاصيل", - "collapse": "إخفاء التفاصيل" - }, - "send": "إرسال", - "more": "المزيد", - "scheduled_banner": "مجدولة للإرسال في {date}", - "scheduled_send_created": "تمت جدولة الرسالة للإرسال", - "cancel_scheduled_send": "إلغاء الإرسال", - "reschedule_send": "إعادة الجدولة", - "cancel_and_edit": "إلغاء والتعديل", - "cancel_and_compose_again": "إلغاء وإعادة الكتابة", - "reschedule_prompt": "أدخل تاريخًا/وقتًا جديدًا، مثل 2026-05-04T15:30", - "scheduled_actions_only": "عرض المجدولة يدعم الإجراءات المجدولة فقط", - "undo_send_scheduled": "تمت جدولة الرسالة للإرسال", - "undo_send": "تراجع عن الإرسال", - "contact_sidebar": { - "title": "جهة الاتصال", - "close": "إغلاق الشريط الجانبي", - "action_email": "بريد إلكتروني", - "action_email_title": "إرسال بريد إلكتروني", - "action_copy": "نسخ", - "action_copy_title": "نسخ البريد الإلكتروني", - "action_edit_title": "تعديل جهة الاتصال", - "section_emails": "عناوين البريد الإلكتروني", - "section_phones": "أرقام الهاتف", - "section_organizations": "المؤسسات", - "section_addresses": "العناوين", - "section_notes": "ملاحظات", - "not_in_contacts": "ليست ضمن جهات اتصالك", - "add_to_contacts": "إضافة إلى جهات الاتصال", - "copied": "تم النسخ!", - "copy_failed": "فشل النسخ" - }, - "send_now": "إرسال الآن" - }, - "email_composer": { - "read_receipt_on": "إيصال القراءة مطلوب (انقر للتعطيل)", - "read_receipt_off": "طلب إيصال قراءة", - "dsn_on": "تم طلب إشعار التسليم (انقر للتعطيل)", - "dsn_off": "طلب إشعار بالتسليم", - "format_plain_text": "التبديل إلى نص عادي", - "format_rich_text": "التبديل إلى نص منسق (HTML)", - "require_tls_on": "التسليم المشفّر مطلوب (انقر للتعطيل)", - "require_tls_off": "اشتراط التسليم المشفّر (TLS)", - "new_message": "رسالة جديدة", - "signature_edit_hint": "انقر نقرًا مزدوجًا لتحرير التوقيع", - "reply": "رد", - "reply_all": "الرد على الجميع", - "forward": "إعادة توجيه", - "reply_to": "رد", - "reply_all_to": "الرد على الجميع", - "forward_message": "إعادة توجيه", - "from": "من", - "to": "إلى", - "cc": "نسخة", - "bcc": "نسخة مخفية", - "subject": "الموضوع", - "body_placeholder": "اكتب رسالتك...", - "send": "إرسال", - "cancel": "إلغاء", - "attach": "إرفاق", - "attach_photos": "الصور ومقاطع الفيديو", - "attach_files": "ملفات", - "discard": "تجاهل", - "discard_draft_title": "تجاهل المسودة؟", - "discard_draft_confirm": "لديك تغييرات غير محفوظة. هل تريد تجاهل هذه المسودة؟", - "saving": "جارٍ الحفظ...", - "sending": "جارٍ الإرسال...", - "add_link": "إضافة رابط", - "link_url_prompt": "أدخل الرابط", - "draft_saved": "تم حفظ المسودة", - "save_failed": "فشل الحفظ", - "to_placeholder": "عناوين البريد الإلكتروني للمستلمين", - "cc_placeholder": "مستلمو النسخة", - "bcc_placeholder": "مستلمو النسخة المخفية", - "subject_placeholder": "الموضوع", - "cc_label": "نسخة:", - "bcc_label": "نسخة مخفية:", - "subject_label": "الموضوع:", - "file_size_kb": "ك.ب", - "prefix": { - "forward": "إعادة توجيه:", - "reply": "رد:" - }, - "no_subject": "(بلا موضوع)", - "unknown_sender": "غير معروف", - "quote": { - "reply_header": "في {date}، كتب {sender}:", - "forward_header": "---------- رسالة موجّهة ----------", - "from": "من: {sender}", - "date": "التاريخ: {date}", - "subject": "الموضوع: {subject}", - "to": "إلى: {recipients}" - }, - "remove_sub_address": "إزالة العنوان الفرعي", - "from_override": { - "toggle_off": "تجاوز", - "toggle_on": "إلغاء التجاوز", - "toggle_tooltip": "عدّل اسم وعنوان \"من\" بحرية. سيُرسل البريد عبر هويتك.", - "name_label": "اسم المرسل", - "name_placeholder": "الاسم", - "email_label": "عنوان بريد المرسل", - "email_placeholder": "alias@example.com", - "envelope_notice": "إذا لم يقبل خادمك هذا العنوان كمرسل للمغلف، فسيُستخدم {identity} بدلاً منه، ويمكن للمستلمين رؤيته في ترويسة Return-Path." - }, - "use_template": "قالب", - "save_as_template": "حفظ كقالب", - "validation": { - "recipient_required": "أضف مستلمًا للإرسال", - "body_required": "اكتب رسالة أو أرفق ملفًا", - "attachments_uploading": "المرفقات لا تزال قيد الرفع — سيتم الإرسال فور انتهائها", - "attachment_upload_failed": "لم يتم الإرسال — فشل رفع أحد المرفقات. أزله وحاول مرة أخرى." - }, - "upload_progress": "جارٍ الرفع {uploaded} / {total}", - "upload_cancel": "إلغاء الرفع", - "upload_failed": "فشل رفع {filename}", - "attachment_too_large": "\"{name}\" أكبر مما يسمح به الخادم ({max} لكل ملف)", - "attachments_total_too_large": "ستتجاوز المرفقات حد الخادم البالغ {max} لكل رسالة", - "drop_files": "أفلت الملفات لإرفاقها", - "show_less": "إظهار أقل", - "send_failed": "فشل إرسال الرسالة", - "send_timeout": "توقف الخادم عن الاستجابة. تحقق من مجلد المُرسَل قبل إعادة الإرسال — فقد تكون الرسالة قد أُرسلت بالفعل.", - "continue_draft": "متابعة المسودة", - "close_draft_title": "حفظ المسودة أم تجاهلها؟", - "close_draft_message": "لديك تغييرات غير محفوظة. هل تريد حفظها كمسودة أم تجاهلها؟", - "smime_sign_on": "توقيع S/MIME مفعّل", - "smime_sign_off": "تفعيل توقيع S/MIME", - "smime_encrypt_on": "تشفير S/MIME مفعّل", - "smime_encrypt_off": "تفعيل تشفير S/MIME", - "smime_encrypt_unavailable": "تشفير S/MIME غير متاح – شهادات المستلمين مفقودة", - "smime_unlock_title": "فتح مفتاح S/MIME", - "smime_unlock_message": "أدخل عبارة المرور لفتح مفتاح توقيع S/MIME الخاص بك.", - "smime_unlock_button": "فتح", - "smime_passphrase_placeholder": "عبارة المرور", - "forgot_attachment": { - "title": "هل نسيت مرفقًا؟", - "message": "رسالتك تذكر \"{keyword}\" لكن لا يوجد ملف مرفق. هل تريد الإرسال على أي حال؟", - "send_anyway": "الإرسال على أي حال", - "back": "العودة إلى التعديل" - }, - "empty_subject": { - "title": "إرسال بدون موضوع؟", - "message": "لا يحتوي هذا البريد على موضوع. هل تريد إرساله على أي حال؟", - "dont_ask_again": "عدم السؤال مرة أخرى", - "send_anyway": "الإرسال على أي حال", - "back": "العودة إلى التعديل" - }, - "schedule_send": "جدولة الإرسال", - "schedule_send_description": "اختر متى يجب على الخادم إرسال هذه الرسالة.", - "schedule_send_required": "اختر تاريخًا ووقتًا.", - "schedule_send_invalid": "أدخل تاريخًا ووقتًا صالحين.", - "schedule_send_future": "اختر تاريخًا ووقتًا في المستقبل.", - "schedule_send_too_late": "هذا الوقت متأخر عمّا يسمح به الخادم.", - "schedule_send_unsupported": "جدولة الإرسال غير مدعومة لهذا الحساب.", - "schedule_send_cleanup_warning": "تم إنشاء الإرسال المجدول، لكن فشل تنظيف المسودة.", - "send_delay_unsupported": "تأخير الإرسال غير مدعوم لهذا الحساب.", - "send_delay_unsupported_confirm": "هذا الحساب لا يدعم تأخير الإرسال. هل تريد الإرسال فورًا بدلاً من ذلك؟", - "recipient_edit_email": "تعديل عنوان البريد الإلكتروني", - "recipient_edit_name": "تعديل الاسم المعروض", - "recipient_email_placeholder": "عنوان البريد الإلكتروني", - "recipient_name_placeholder": "الاسم المعروض", - "autocomplete_search_server": "البحث في الخادم", - "autocomplete_searching": "جارٍ البحث...", - "toolbar": { - "bold": "غامق", - "italic": "مائل", - "underline": "تسطير", - "strikethrough": "يتوسطه خط", - "text_color": "لون النص", - "remove_color": "إزالة اللون", - "heading_1": "عنوان 1", - "heading_2": "عنوان 2", - "bullet_list": "قائمة نقطية", - "ordered_list": "قائمة مرقمة", - "quote": "اقتباس", - "code_block": "كتلة برمجية", - "align_left": "محاذاة لليسار", - "align_center": "توسيط", - "align_right": "محاذاة لليمين", - "text_direction": "اتجاه النص (RTL/LTR)", - "link": "رابط", - "table": "جدول", - "clear_formatting": "مسح التنسيق", - "undo": "تراجع", - "redo": "إعادة", - "add_row_above": "إضافة صف بالأعلى", - "add_row_below": "إضافة صف بالأسفل", - "add_column_before": "إضافة عمود قبل", - "add_column_after": "إضافة عمود بعد", - "delete_row": "حذف الصف", - "delete_column": "حذف العمود", - "toggle_header_row": "تبديل صف الرأس", - "delete_table": "حذف الجدول", - "pick_size": "اختيار الحجم", - "font_size": "حجم الخط", - "font_size_default": "افتراضي", - "background_color": "لون الخلفية", - "remove_background_color": "إزالة لون الخلفية" - }, - "send_filing_warning": "تم الإرسال - لكن التنظيف بعد الإرسال فشل، وقد تبقى مسودة قديمة." - }, - "confirm_dialog": { - "confirm": "تأكيد", - "cancel": "إلغاء" - }, - "common": { - "loading": "جارٍ التحميل...", - "error": "خطأ", - "success": "تم بنجاح", - "cancel": "إلغاء", - "save": "حفظ", - "delete": "حذف", - "edit": "تعديل", - "close": "إغلاق", - "search": "بحث", - "refresh": "تحديث", - "settings": "الإعدادات", - "help": "المساعدة", - "logout": "تسجيل الخروج", - "yes": "نعم", - "no": "لا", - "unknown": "غير معروف", - "app_title": "البريد الإلكتروني", - "reconnecting": "انقطع الاتصال. جارٍ محاولة إعادة الاتصال…", - "rate_limited_title": "مصادقة الخادم مقيّدة مؤقتًا بحد المعدل.", - "rate_limited_detail": "أوقف Bulwark الطلبات في الخلفية مؤقتًا لتجنب الحظر. ستتم إعادة المحاولة خلال {seconds} ثانية.", - "rate_limited_action_title": "تم إيقاف الطلب مؤقتًا لتجنب الحظر.", - "rate_limited_action_detail": "ينتظر Bulwark انتهاء فترة تهدئة الخادم قبل إرسال المزيد من الطلبات الموثّقة. حاول مرة أخرى خلال {seconds} ثانية.", - "pull_to_refresh": "اسحب للتحديث", - "release_to_refresh": "أفلت للتحديث", - "refreshing": "جارٍ التحديث…" - }, - "notifications": { - "email_sent": "تم إرسال الرسالة بنجاح", - "email_deleted": "تم حذف الرسالة", - "email_archived": "تمت أرشفة الرسالة", - "emails_deleted": "{count, plural, one {تم حذف رسالة واحدة} other {تم حذف # رسائل}}", - "emails_archived": "{count, plural, one {تمت أرشفة رسالة واحدة} other {تمت أرشفة # رسائل}}", - "error_archiving": "فشل أرشفة الرسالة", - "error_updating": "فشل تحديث الرسالة", - "email_starred": "تم تمييز الرسالة بنجمة", - "email_unstarred": "تمت إزالة النجمة عن الرسالة", - "email_marked_read": "تم تمييز الرسالة كمقروءة", - "email_marked_unread": "تم تمييز الرسالة كغير مقروءة", - "copied_to_clipboard": "تم النسخ إلى الحافظة", - "source_copied": "تم نسخ المصدر إلى الحافظة", - "error_sending": "فشل إرسال الرسالة", - "error_deleting": "فشل حذف الرسالة", - "error_loading": "فشل تحميل الرسائل", - "new_email": "رسالة جديدة", - "new_email_from": "من {sender}", - "click_to_view": "انقر للعرض", - "email_moved": "تم نقل الرسالة", - "emails_moved": "تم نقل {count} رسالة", - "moved_to_mailbox": "تم النقل إلى {mailbox}", - "move_failed": "فشل النقل", - "move_error": "تعذّر نقل الرسائل إلى المجلد المحدد", - "email_tagged": "تم وسم الرسالة", - "emails_tagged": "تم وسم {count} رسالة", - "tag_failed": "فشل الوسم", - "identity_created": "تم إنشاء الهوية بنجاح", - "identity_updated": "تم تحديث الهوية بنجاح", - "identity_deleted": "تم حذف الهوية", - "identity_set_primary": "تم تحديث الهوية الأساسية", - "identity_create_failed": "فشل إنشاء الهوية: {error}", - "identity_update_failed": "فشل تحديث الهوية: {error}", - "identity_delete_failed": "فشل حذف الهوية: {error}", - "identity_unauthorized": "غير مخوّل لك الإرسال من عنوان البريد الإلكتروني هذا", - "identity_not_found": "الهوية غير موجودة", - "vacation_saved": "تم حفظ إعدادات الرد التلقائي أثناء الغياب", - "vacation_save_failed": "فشل حفظ إعدادات الرد التلقائي أثناء الغياب", - "filters_saved": "تم حفظ المرشحات بنجاح", - "filters_save_failed": "فشل حفظ المرشحات", - "filters_deleted": "تم حذف قاعدة المرشح", - "templates_exported": "تم تصدير القوالب بنجاح", - "templates_imported": "{count, plural, one {تم استيراد قالب واحد} other {تم استيراد # قالب}}", - "templates_import_errors": "تعذّر استيراد بعض القوالب", - "templates_import_empty": "لم يتم العثور على قوالب في الملف", - "export_email_error": "فشل تصدير الرسالة", - "import_email_success": "تم استيراد الرسالة بنجاح", - "import_email_error": "فشل استيراد الرسالة" - }, - "date": { - "today": "اليوم", - "yesterday": "أمس", - "this_week": "هذا الأسبوع", - "last_week": "الأسبوع الماضي", - "this_month": "هذا الشهر", - "older": "أقدم", - "just_now": "الآن", - "minutes_ago": "قبل {count} دقيقة", - "minutes_ago_plural": "قبل {count} دقائق", - "hours_ago": "قبل {count} ساعة", - "hours_ago_plural": "قبل {count} ساعات", - "days_ago": "قبل {count} يوم", - "days_ago_plural": "قبل {count} أيام" - }, - "language": { - "title": "اللغة", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "اختيار اللغة", - "switch_to_english": "التبديل إلى الإنجليزية", - "switch_to_french": "التبديل إلى الفرنسية", - "switch_to_japanese": "التبديل إلى اليابانية", - "switch_to_spanish": "التبديل إلى الإسبانية", - "switch_to_italian": "التبديل إلى الإيطالية", - "switch_to_german": "التبديل إلى الألمانية", - "switch_to_dutch": "التبديل إلى الهولندية", - "switch_to_portuguese": "التبديل إلى البرتغالية", - "switch_to_russian": "التبديل إلى الروسية", - "switching": "جارٍ تغيير اللغة...", - "switch": "تبديل اللغة", - "current": "اللغة الحالية", - "polish": "Polski", - "switch_to_polish": "التبديل إلى البولندية", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "التبديل إلى الفارسية", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "التبديل إلى المنغولية" - }, - "settings": { - "title": "الإعدادات", - "back_to_mail": "العودة إلى البريد", - "save_success": "تم حفظ الإعدادات بنجاح", - "import_success": "تم استيراد الإعدادات بنجاح", - "import_error": "فشل استيراد الإعدادات", - "reset_confirm": "هل أنت متأكد أنك تريد إعادة تعيين جميع الإعدادات إلى الافتراضية؟", - "unsaved_changes": "لديك تغييرات غير محفوظة", - "discard_changes": "تجاهل التغييرات غير المحفوظة؟", - "discard": "تجاهل", - "keep_editing": "متابعة التعديل", - "search_placeholder": "بحث في الإعدادات", - "search_clear": "مسح البحث", - "search_no_results": "لا توجد إعدادات مطابقة", - "tabs": { - "appearance": "المظهر", - "language": "اللغة والمنطقة والوقت", - "email": "سلوك البريد", - "composer": "محرر الرسائل", - "privacy": "الخصوصية والأمان", - "account": "الحساب", - "identities": "الهويات", - "vacation": "الرد التلقائي أثناء الغياب", - "advanced": "متقدم", - "calendar": "التقويم", - "filters": "المرشحات", - "templates": "القوالب", - "folders": "المجلدات", - "keywords": "الوسوم", - "security": "الأمان", - "files": "الملفات", - "contacts": "جهات الاتصال", - "encryption": "التشفير", - "protocol_handlers": "التطبيقات الافتراضية", - "sidebar_apps": "تطبيقات الشريط الجانبي", - "notifications": "الإشعارات", - "layout": "التخطيط", - "reading": "القراءة", - "composing": "الكتابة", - "downloads": "التنزيلات", - "content_senders": "المحتوى والمرسلون", - "about_data": "حول والبيانات", - "debug": "التصحيح" - }, - "tab_groups": { - "general": "عام", - "account": "الحساب والهوية", - "organization": "تنظيم البريد", - "apps": "التطبيقات", - "system": "النظام", - "appearance": "المظهر", - "mail": "البريد", - "privacy": "الخصوصية والأمان", - "advanced": "متقدم" - }, - "appearance": { - "message_list_order": { - "label": "ترتيب قائمة الرسائل", - "description": "كيفية ترتيب الرسائل في القائمة. يقوم الخادم بالترتيب، لذا يشمل المجلد بأكمله وليس الرسائل المحمّلة فقط.", - "preset": { - "chronological": "زمني (الأحدث أولاً)", - "unread_first": "غير المقروءة أولاً", - "starred_first": "المميزة بنجمة أولاً", - "tagged_first": "الموسومة أولاً", - "custom": "مخصص" - }, - "tag_label": "الوسم", - "advanced_show": "متقدم: تحرير مستويات الترتيب", - "advanced_hide": "إخفاء الخيارات المتقدمة", - "criterion_label": "معيار المستوى {n}", - "direction_label": "اتجاه المستوى {n}", - "criteria": { - "unread": "غير مقروءة", - "starred": "مميزة بنجمة", - "tag": "الوسم", - "receivedAt": "تاريخ الاستلام", - "sentAt": "تاريخ الإرسال", - "from": "المرسل", - "subject": "الموضوع", - "size": "الحجم" - }, - "direction": { - "unread_first": "غير المقروءة أولاً", - "read_first": "المقروءة أولاً", - "starred_first": "المميزة بنجمة أولاً", - "unstarred_first": "غير المميزة أولاً", - "tagged_first": "الموسومة أولاً", - "untagged_first": "غير الموسومة أولاً", - "newest_first": "الأحدث أولاً", - "oldest_first": "الأقدم أولاً", - "a_to_z": "أ → ي", - "z_to_a": "ي → أ", - "largest_first": "الأكبر أولاً", - "smallest_first": "الأصغر أولاً" - }, - "add_level": "إضافة مستوى", - "remove_level": "إزالة المستوى", - "move_up": "نقل لأعلى", - "move_down": "نقل لأسفل", - "tiebreak_note": "حتى ثلاثة مستويات؛ تحسم المستويات اللاحقة حالات التعادل. يُطبَّق «الأحدث أولاً» دائمًا في النهاية.", - "scope": { - "label": "تطبيق على جميع المجلدات", - "description": "عند الإيقاف، يُطبَّق الترتيب على صندوق الوارد فقط؛ وتبقى المجلدات الأخرى مرتبة زمنيًا." - }, - "unsupported_note": "خادم البريد لديك لا يدعم الترتيب حسب حالة القراءة أو النجمة أو الوسوم. يتم تجاهل هذه المستويات وتبقى القائمة مرتبة زمنيًا." - }, - "title": "المظهر", - "description": "تخصيص شكل ومظهر البريد الإلكتروني على الويب", - "theme": { - "label": "السمة", - "description": "اختر نظام الألوان المفضل لديك", - "light": "فاتح", - "dark": "داكن", - "system": "النظام" - }, - "language": { - "label": "اللغة", - "description": "اختر لغتك المفضلة" - }, - "font_size": { - "label": "حجم الخط", - "description": "اضبط حجم النص لقراءة أفضل", - "small": "صغير", - "medium": "متوسط", - "large": "كبير" - }, - "list_density": { - "label": "الكثافة", - "description": "التحكم في التباعد والحشو عبر الواجهة", - "extra_compact": "مضغوط جدًا", - "compact": "مضغوط", - "regular": "عادي", - "comfortable": "مريح" - }, - "animations": { - "label": "تفعيل الحركات", - "description": "إظهار الانتقالات والتأثيرات السلسة" - }, - "toolbar_position": { - "label": "موضع شريط الأدوات", - "description": "أين تظهر أزرار إجراءات البريد (رد، أرشفة، حذف، إلخ)", - "top": "أعلى", - "below_subject": "أسفل الموضوع" - }, - "toolbar_labels": { - "label": "إظهار تسميات شريط الأدوات", - "description": "عرض تسميات نصية بجانب أيقونات شريط الأدوات. عطّلها لتوفير المساحة بعد أن تعتاد على الأيقونات." - }, - "hide_account_switcher": { - "label": "إخفاء مبدّل الحسابات في الشريط الجانبي", - "description": "إخفاء محدد الحساب أعلى الشريط الجانبي للمجلدات. يمكنك مع ذلك تبديل الحسابات من شريط التنقل السفلي." - }, - "show_rail_account_list": { - "label": "إظهار صور الحسابات على شريط التنقل", - "description": "عرض دوائر الحسابات الفردية أسفل شريط التنقل للتبديل السريع، مع زر تسجيل خروج أسفلها." - }, - "unified_mailbox": { - "label": "صندوق البريد الموحّد", - "description": "إظهار المجلدات المجمّعة (البريد الوارد، المرسل، إلخ) للحساب النشط ومجلداته المشتركة.", - "cross_account": { - "label": "عبر جميع الحسابات", - "description": "دمج صندوق البريد الموحّد عبر كل حساب متصل بدلًا من الاقتصار على الحساب النشط." - }, - "include_group": { - "label": "تضمين صناديق البريد الجماعية", - "description": "دمج صناديق البريد المشتركة/الجماعية أيضًا في العرض الموحّد." - } - }, - "all_mail": { - "folders_label": "المجلدات في القوائم الموحّدة", - "folders_description": "اختر أي مجلدات هذا الحساب تُدمج في قوائم كل البريد / غير المقروء / المميز بنجمة.", - "account_hint": "ينطبق على {account}.", - "no_folders": "لا توجد مجلدات متاحة." - }, - "colorful_sidebar_icons": { - "label": "أيقونات ملونة في الشريط الجانبي", - "description": "تلوين أيقونات المجلدات والوسوم حسب النوع (أزرق للبريد الوارد، أحمر للمزعج، أخضر للمرسل، إلخ). عطّل هذا لشريط جانبي أحادي اللون." - }, - "tint_list_rows": { - "label": "تلوين صفوف القائمة بلون الوسم", - "description": "تظليل كل صف رسالة بلون أول وسم فيها. عطّل هذا لإبقاء الصفوف عادية؛ ستظل نقاط الوسوم وشاراتها تظهر اللون." - }, - "show_folder_total_count": { - "label": "إظهار إجمالي عدد الرسائل", - "description": "إظهار إجمالي عدد الرسائل بجانب المجلدات والوسوم، إلى جانب عدد غير المقروء. عطّل هذا لإظهار عدد غير المقروء فقط." - }, - "favicon_unread_badge": { - "label": "عدد غير المقروء على أيقونة علامة التبويب", - "description": "إظهار عدد رسائل البريد الوارد غير المقروءة كشارة على أيقونة علامة تبويب المتصفح، بحيث يظهر البريد الجديد عند عدم التركيز على علامة التبويب. عطّل هذا لإبقاء الأيقونة بلا شارة." - }, - "pro_interface": { - "label": "الواجهة الاحترافية (تجريبي)", - "description": "تخطيط للمستخدمين المتقدمين لسطح المكتب فقط، مع تصفح رسائل بعلامات تبويب متعددة وسير عمل عبر الحسابات. الواجهة القياسية لا تتأثر؛ يمكنك العودة إليها في أي وقت.", - "open_label": "فتح الواجهة الاحترافية", - "back_to_standard": "العودة إلى القياسية" - }, - "cross_unread": { - "label": "غير مقروء", - "description": "إظهار عنصر \"غير مقروء\" في صندوق البريد الموحّد يسرد البريد غير المقروء عبر المجلدات المحددة." - }, - "cross_starred": { - "label": "مميز بنجمة", - "description": "إظهار عنصر \"مميز بنجمة\" في صندوق البريد الموحّد يسرد البريد المميز بنجمة عبر المجلدات المحددة." - }, - "cross_all": { - "label": "كل البريد", - "description": "إظهار عنصر \"كل البريد\" في صندوق البريد الموحّد يسرد كل البريد عبر المجلدات المحددة." - } - }, - "keywords": { - "title": "وسوم البريد", - "description": "عرّف وسومًا لتنظيم رسائلك بالألوان. تُخزَّن هذه ككلمات مفتاحية JMAP على الخادم.", - "add_keyword": "إضافة وسم", - "label_field": "الاسم المعروض", - "label_placeholder": "مثال: عمل، شخصي، عاجل", - "id_field": "معرّف الوسم", - "id_placeholder": "مثال: work, personal", - "color_field": "اللون", - "id_exists": "معرّف الوسم هذا موجود بالفعل", - "edit": "تعديل الوسم", - "delete": "حذف الوسم", - "save": "حفظ", - "add": "إضافة", - "cancel": "إلغاء", - "migrating": "جارٍ تحديث الوسم على الرسائل الحالية…", - "migration_error": "فشل تحديث الوسم على الرسائل الحالية", - "nesting": { - "label": "وسوم متداخلة", - "description": "ضع الوسوم داخل وسوم أخرى واعرضها كشجرة في الشريط الجانبي." - }, - "parent_field": "الوسم الأصل", - "no_parent": "بدون وسم أصل", - "too_long": "مسار الوسم طويل جدًا ({max} حرفًا على الأكثر)", - "has_children_locked": "توجد وسوم أخرى متداخلة تحت هذا الوسم، لذا فإن اسمه ووسمه الأصل مقفلان. انقلها أو احذفها أولًا.", - "has_children_delete": "احذف أولًا الوسوم المتداخلة تحت هذا الوسم", - "visibility_field": "الظهور في الشريط الجانبي", - "visibility": { - "show": "إظهار", - "unread": "إظهار عند وجود غير مقروء", - "hide": "إخفاء" - }, - "unrecognized": { - "title": "استعادة الوسوم من الخادم", - "description": "تُخزَّن أسماء الوسوم وألوانها في هذا المتصفح، لكن الوسوم نفسها موجودة على رسائلك ككلمات مفتاحية JMAP. افحص بريدك بحثًا عن كلمات مفتاحية لا يفسّرها أي وسم هنا، ثم أعِد إضافتها.", - "scan": "البحث عن الوسوم", - "rescan": "إعادة الفحص", - "scanning": "جارٍ الفحص… {scanned} من {total} رسالة", - "none": "كل وسم على رسائلك معرَّف هنا بالفعل.", - "messages": "{count, plural, one {رسالة واحدة} other {# رسالة}}", - "add_all": "إضافة الكل ({count})", - "partial": "تم فحص أحدث {scanned} رسالة فقط من أصل {total}. قد تكون الوسوم المستخدمة في البريد الأقدم فقط غير مدرجة.", - "error": "تعذّر فحص بريدك بحثًا عن الوسوم." - } - }, - "notifications": { - "test_sound": "اختبار صوت الإشعار", - "sounds": { - "default": "افتراضي (تنبيه)", - "cheerful": "مرح", - "involved": "متفاعل", - "swift": "إيماءة سريعة", - "relax": "استرخاء" - }, - "push": { - "title": "الإشعارات في الخلفية", - "description": "تلقّي إشعارات النظام للبريد الجديد عند إغلاق هذا الموقع. تُسلَّم عبر خادم ترحيل Bulwark؛ ولا يطّلع الترحيل على محتوى البريد أبدًا.", - "relay_label": "خادم ترحيل الإشعارات", - "relay_desc": "يستخدم افتراضيًا خادم ترحيل Bulwark المستضاف. غيّره فقط إذا كنت تستضيف بنفسك.", - "relay_locked": "تم تعيينه بواسطة المسؤول", - "relay_locked_desc": "تم تعيين رابط خادم الترحيل بواسطة المسؤول ولا يمكن تغييره.", - "status_active": "نشط على هذا الجهاز", - "status_inactive": "غير مفعّل على هذا الجهاز", - "status_unsupported": "هذا المتصفح لا يدعم Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "جارٍ العمل…", - "enable": "تفعيل", - "reenable": "إعادة التسجيل", - "disable": "تعطيل", - "confirm_disable_title": "تعطيل الإشعارات في الخلفية؟", - "confirm_disable_message": "سيتوقف هذا الجهاز عن تلقي التنبيهات عند إغلاق الموقع.", - "dismiss_aria": "تجاهل مطالبة الإشعارات", - "ios_hint": "على iOS، ثبّت الموقع على الشاشة الرئيسية أولًا - يقدّم Safari خدمة Web Push فقط لتطبيقات PWA المثبّتة.", - "devices_title": "الأجهزة المسجّلة", - "devices_desc": "كل الأجهزة المسجّلة لتلقّي الإشعارات في الخلفية لهذا الحساب. إلغاء أي جهاز يوقف تلقّيه فورًا.", - "devices_loading": "جارٍ تحميل الأجهزة المسجّلة…", - "devices_empty": "لا توجد أجهزة مسجّلة للإشعارات في الخلفية.", - "devices_error": "تعذّر تحميل الأجهزة المسجّلة.", - "devices_refresh": "تحديث", - "device_this": "هذا الجهاز", - "device_other": "جهاز آخر", - "device_status_active": "يسلّم الإشعارات", - "device_status_inactive": "لم يسلّم أي إشعار قط", - "device_status_unknown": "غير معروف لهذا المُرحّل", - "device_expires": "ينتهي في {date}", - "revoke": "إلغاء التسجيل", - "confirm_revoke_title": "إلغاء تسجيل هذا الجهاز؟", - "confirm_revoke_message": "سيتوقّف فورًا عن تلقّي الإشعارات في الخلفية. تفعيل الدفع مجددًا على ذلك الجهاز سيسجّله من جديد.", - "confirm_revoke_message_this": "سيتوقّف هذا الجهاز فورًا عن تلقّي الإشعارات في الخلفية." - }, - "sound_selection": { - "title": "صوت الإشعار", - "description": "اختر الصوت الذي يُشغَّل للإشعارات", - "choose": "الصوت", - "choose_desc": "اختر نغمة إشعار وانقر على أيقونة السماعة لمعاينتها" - }, - "email": { - "title": "إشعارات البريد", - "description": "إعداد الإشعارات للرسائل الواردة", - "enabled": "إشعارات البريد", - "enabled_desc": "إظهار الإشعارات عند وصول رسائل جديدة", - "sound": "صوت الإشعار", - "sound_desc": "تشغيل تنبيه صوتي عند وصول رسائل جديدة", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "إشعارات التقويم", - "description": "إعداد الإشعارات لأحداث التقويم", - "enabled": "إشعارات الأحداث", - "enabled_desc": "إظهار تنبيهات لأحداث التقويم القادمة", - "sound": "صوت الإشعار", - "sound_desc": "تشغيل تنبيه صوتي لتذكيرات التقويم", - "invitation_parsing": "تحليل دعوات البريد الإلكتروني", - "invitation_parsing_desc": "اكتشاف دعوات التقويم في مرفقات البريد وإظهار إجراءات التقويم" - } - }, - "language_region": { - "title": "اللغة والمنطقة والوقت", - "description": "اللغة، وتنسيق التاريخ، وتنسيق الوقت، وتفضيلات إقليمية أخرى", - "language": { - "label": "اللغة", - "description": "اختر لغتك المفضلة", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "تنسيق التاريخ", - "description": "كيفية عرض التواريخ في قائمة البريد", - "smart": "ذكي (يراعي اللغة المحلية)", - "relative": "نسبي (قبل ساعة، قبل يومين)", - "full": "التاريخ الكامل دائمًا", - "preview_today": "اليوم:", - "preview_this_week": "هذا الأسبوع:", - "preview_older": "أقدم:" - }, - "date_locale": { - "label": "منطقة تنسيق التاريخ", - "description": "كيفية ترتيب التاريخ الرقمي (يوم، شهر، سنة)", - "auto": "تلقائي (يطابق اللغة)", - "iso": "ISO 8601 (سنة-شهر-يوم)", - "dmy": "يوم/شهر/سنة", - "mdy": "شهر/يوم/سنة" - }, - "time_format": { - "label": "تنسيق الوقت", - "description": "اختر بين نظام 12 ساعة أو 24 ساعة", - "12h": "12 ساعة", - "24h": "24 ساعة" - }, - "first_day": { - "label": "أول يوم في الأسبوع", - "description": "ابدأ الأسبوع بالأحد أو الاثنين", - "sunday": "الأحد", - "monday": "الاثنين" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "مسح البحث عند تبديل المجلدات", - "description": "النقر على مجلد (البريد الوارد، المرسل…) يمسح البحث النشط ويعرض ذلك المجلد. عند الإيقاف، يبقى البحث ويُعاد تشغيله في المجلد الذي تنتقل إليه." - }, - "request_read_receipt": { - "label": "طلب إيصالات القراءة افتراضيًا", - "description": "تفعيل طلب إيصال القراءة مسبقًا عند إنشاء رسالة جديدة." - }, - "read_receipt_response": { - "label": "الرد على طلبات إيصال القراءة", - "description": "ما الإجراء عند طلب رسالة واردة إيصال قراءة.", - "ask": "السؤال في كل مرة", - "always": "الإرسال دائمًا", - "never": "عدم الإرسال أبدًا" - }, - "title": "سلوك البريد", - "description": "إعداد كيفية التعامل مع الرسائل", - "mark_read": { - "label": "التمييز كمقروءة", - "description": "متى تُمَيَّز الرسائل كمقروءة عند فتحها", - "instant": "فورًا", - "delay_3s": "بعد 3 ثوانٍ", - "delay_5s": "بعد 5 ثوانٍ", - "never": "أبدًا" - }, - "delete_action": { - "label": "إجراء الحذف", - "description": "ما الذي يحدث عند حذف رسالة", - "trash": "النقل إلى المهملات", - "trash_and_read": "النقل إلى المهملات والتمييز كمقروءة", - "permanent": "الحذف نهائيًا", - "warning": "سيتم حذف الرسائل نهائيًا ولا يمكن استرجاعها. هذا الإجراء لا رجعة فيه." - }, - "message_spacing": { - "label": "تباعد الرسالة", - "description": "الهوامش حول نص الرسالة في وضع القراءة", - "auto": "تلقائي (مستحسن)", - "always": "إضافة تباعد دائمًا", - "edge": "من الحافة إلى الحافة" - }, - "plain_text_font": { - "label": "خط النص العادي", - "description": "الخط المستخدم لرسائل النص العادي", - "mono": "أحادي المسافة", - "sans": "عادي (مثل التطبيق)" - }, - "archive_mode": { - "label": "الأرشفة في", - "description": "كيفية تنظيم الرسائل عند الأرشفة", - "single": "مجلد واحد", - "year": "مجلد لكل سنة", - "month": "مجلد لكل شهر", - "reorganize": "إعادة تنظيم الأرشيف الحالي", - "reorganize_success": "{count, plural, =0 {لا توجد رسائل لإعادة تنظيمها} =1 {تمت إعادة تنظيم رسالة واحدة} other {تمت إعادة تنظيم # رسالة}}", - "reorganize_error": "فشلت إعادة تنظيم الأرشيف" - }, - "permanently_delete_junk": { - "label": "حذف البريد المزعج نهائيًا", - "description": "حذف الرسائل من مجلد البريد المزعج نهائيًا بدلًا من نقلها إلى المهملات" - }, - "mail_layout": { - "label": "تخطيط البريد", - "description": "اختر بين جزء القراءة المقسّم الكلاسيكي، أو تدفق قراءة مركّز على طراز Gmail، أو جزء قراءة سفلي على طراز Zimbra.", - "split": "جزء مقسّم", - "split_description": "إبقاء قائمة الرسائل وجزء القراءة ظاهرين جنبًا إلى جنب.", - "focus": "قائمة مركّزة", - "focus_description": "إظهار سطر واحد لكل رسالة وفتح البريد بعرض كامل مع إبقاء الشريط الجانبي للمجلدات ظاهرًا.", - "horizontal": "جزء القراءة أسفل", - "horizontal_description": "إظهار قائمة الرسائل أعلى وفتح الرسالة المحددة في جزء قراءة أسفلها." - }, - "show_preview": { - "label": "إظهار نص المعاينة", - "description": "عرض معاينة الرسالة في القائمة", - "focus_description": "عرض نص معاينة ضمن السطر في قائمة الرسائل المركّزة" - }, - "disable_threading": { - "label": "تعطيل تجميع المحادثات", - "description": "إظهار الرسائل كرسائل منفردة بدلًا من تجميعها حسب المحادثة" - }, - "plain_text_mode": { - "label": "نص عادي فقط", - "description": "تعطيل محرر النصوص المنسقة وإرسال جميع الرسائل كنص عادي فقط، بما في ذلك الردود وإعادة التوجيه" - }, - "auto_select_reply_identity": { - "label": "الرد من العنوان المستلَم إليه", - "description": "عند الرد، الإرسال من العنوان الذي أُرسلت إليه الرسالة أصلًا. يطابق الهويات أولًا؛ وبالنسبة لعمليات التسليم الشاملة للنطاق، يعيد كتابة ترويسة \"من\" إلى العنوان البديل مع الإرسال عبر هويتك الأساسية." - }, - "reply_identity_match": { - "label": "مطابقة عنوان الاستلام", - "description": "أي عناوين الاستلام تُعدّ عناوينك. «العنوان المطابق فقط» يختار إحدى هوياتك المضبوطة فقط؛ أما «نفس النطاق» فيعامل أيضًا أي عنوان آخر على نطاقات هوياتك كاسم مستعار شامل ويستبدل به ترويسة المرسل. اختر العنوان المطابق إذا كانت تلك العناوين قوائم توزيع.", - "exact": "العنوان المطابق فقط", - "domain": "أي عنوان على نطاقاتي" - }, - "signature_position": { - "label": "موضع التوقيع", - "description": "أين يُدرج توقيعك في الردود وإعادة التوجيه. أعلى النص المقتبس يُقرأ بشكل طبيعي كخاتمة للرد؛ وأسفله يُبقي الرسالة الأصلية متصلة.", - "above_quote": "قبل النص المقتبس", - "below_quote": "بعد النص المقتبس" - }, - "signature_separator": { - "label": "فاصل التوقيع", - "description": "إضافة السطر الفاصل القياسي \"-- \" قبل التوقيع (RFC 3676). عطّله إذا كنت تفضّل الانتقال مباشرة من رسالتك إلى التوقيع." - }, - "sub_address_delimiter": { - "label": "فاصل العنوان الفرعي", - "description": "الحرف الذي يفصل اسم مستخدمك عن وسم العنوان الفرعي. طابق الفاصل الذي يستخدمه خادم بريدك (مثال: user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "مخصص…", - "custom_input_label": "حرف فاصل مخصص" - }, - "attachment_click_action": { - "label": "إجراء النقر على المرفق", - "description": "اختر ما إذا كان النقر على مرفق ملف يعاينه أو يحمّله فورًا", - "preview": "المعاينة عند الإمكان", - "download": "التنزيل فورًا" - }, - "attachment_position": { - "label": "موضع المرفق", - "description": "أين تُعرض المرفقات في ترويسة الرسالة", - "beside-sender": "بجانب المرسل", - "below-header": "أسفل الترويسة" - }, - "emails_per_page": { - "10": "10 رسائل", - "25": "25 رسالة", - "50": "50 رسالة", - "100": "100 رسالة", - "label": "عدد الرسائل في الصفحة", - "description": "عدد الرسائل التي تُحمَّل دفعة واحدة" - }, - "always_light_mode": { - "label": "إظهار الرسائل دائمًا في الوضع الفاتح", - "description": "عرض محتوى الرسائل في الوضع الفاتح حتى عندما يكون التطبيق في الوضع الداكن، لتجنب مشاكل تحويل الوضع الداكن" - }, - "external_content": { - "label": "المحتوى الخارجي", - "description": "كيفية التعامل مع الصور والمحتوى الخارجي", - "ask": "السؤال دائمًا", - "block": "الحظر دائمًا", - "allow": "السماح دائمًا" - }, - "trusted_senders": { - "label": "المرسلون الموثوقون", - "description": "إدارة المرسلين الذين تُحمَّل صورهم تلقائيًا", - "count_zero": "لا يوجد", - "count_one": "مرسل واحد", - "count_other": "{count} مرسلين", - "modal_title": "المرسلون الموثوقون", - "empty_title": "لا يوجد مرسلون موثوقون بعد", - "empty_description": "عند عرض رسالة بصور محظورة، انقر على \"الوثوق بهذا المرسل دائمًا\" لإضافته هنا.", - "add_manually": "إضافة مرسل يدويًا", - "add_button": "إضافة", - "add_placeholder": "أدخل عنوان البريد الإلكتروني", - "search_placeholder": "بحث عن مرسلين...", - "no_results": "لا يوجد مرسلون مطابقون لبحثك", - "remove": "إزالة", - "close": "إغلاق", - "invalid_email": "يرجى إدخال عنوان بريد إلكتروني صالح", - "already_added": "هذا المرسل موثوق به بالفعل", - "save_error": "فشل الحفظ - راجع سجل تصحيح جهات الاتصال للتفاصيل", - "use_address_book_label": "المزامنة مع دفتر العناوين", - "use_address_book_description": "تخزين المرسلين الموثوقين في دفتر عناوين مخصص \"المرسلون الموثوقون\" ليتم مزامنتهم عبر جميع أجهزتك" - }, - "hover_actions": { - "label": "إجراءات التمرير السريعة", - "description": "اختر الإجراءات السريعة التي تظهر عند التمرير فوق رسالة في القائمة", - "delete": "حذف", - "star": "تمييز بنجمة / إزالة", - "mark_read": "تمييز كمقروءة / غير مقروءة", - "archive": "أرشفة", - "tag": "وسم", - "spam": "تمييز كبريد مزعج", - "not_spam": "ليس مزعجًا", - "none_selected": "لم يتم تحديد أي إجراءات", - "mode_label": "وضع العرض", - "mode_inline": "ضمن الصف", - "mode_floating": "عائم", - "corner_label": "موضع العنصر العائم", - "corner_top-left": "أعلى اليسار", - "corner_top-right": "أعلى اليمين", - "corner_bottom-left": "أسفل اليسار", - "corner_bottom-right": "أسفل اليمين" - }, - "default_mail_program": { - "label": "برنامج البريد الافتراضي", - "description": "تسجيل {appName} كبرنامج البريد الافتراضي لديك لروابط mailto:", - "button": "تعيين كافتراضي", - "success": "تمت مطالبة المتصفح بالتعيين كافتراضي", - "error": "متصفحك لا يدعم هذه الميزة" - }, - "attachment_reminder": { - "label": "تذكير المرفقات", - "description": "التحذير قبل الإرسال عندما تذكر رسالتك مرفقات دون إرفاق ملف", - "keywords_label": "كلمات مفتاحية للتنبيه", - "keywords_description": "كلمات أو عبارات تُفعّل التذكير عند العثور عليها في رسالتك", - "add_placeholder": "إضافة كلمة مفتاحية...", - "add": "إضافة", - "remove": "إزالة" - }, - "empty_subject_warning": { - "label": "تحذير الموضوع الفارغ", - "description": "طلب تأكيد قبل إرسال رسالة بدون موضوع." - }, - "hide_inline_image_attachments": { - "label": "إخفاء الصور المضمّنة من المرفقات", - "description": "الصور المضمّنة في نص الرسالة لا تُدرج كمرفقات منفصلة" - }, - "attachment_image_previews": { - "label": "إظهار معاينات الصور في المرفقات", - "description": "عرض مرفقات الصور كبطاقات مصغّرة بدلًا من أيقونات الملفات العامة" - }, - "send_delay": { - "label": "التراجع عن الإرسال / تأخير الإرسال", - "description": "تأخير الإرسال العادي بفترة قصيرة من جانب الخادم.", - "off": "إيقاف", - "seconds": "{seconds} ثانية", - "unsupported": "الحساب الحالي لا يعلن عن دعم الإرسال المؤجل. لا يزال الإعداد محفوظًا للحسابات الأخرى." - }, - "return_to_list_after_action": { - "label": "العودة إلى القائمة بعد الحذف أو التمييز كغير مقروء", - "description": "بعد حذف الرسالة المفتوحة أو تمييزها كغير مقروءة، العودة إلى قائمة الرسائل بدلًا من فتح الرسالة التالية." - }, - "swipe_right_action": { - "label": "إجراء السحب لليمين (الجوال)", - "description": "على أجهزة اللمس، الإجراء الذي يُنفَّذ عند سحب رسالة إلى اليمين في القائمة." - }, - "swipe_left_action": { - "label": "إجراء السحب لليسار (الجوال)", - "description": "على أجهزة اللمس، الإجراء الذي يُنفَّذ عند سحب رسالة إلى اليسار في القائمة." - }, - "swipe_actions": { - "none": "لا شيء", - "archive": "أرشفة", - "delete": "حذف", - "mark_read": "تبديل حالة القراءة", - "star": "تبديل النجمة", - "spam": "تمييز كبريد مزعج" - }, - "rtl_editing": { - "label": "دعم الكتابة من اليمين إلى اليسار", - "description": "يضيف زر اتجاه إلى شريط أدوات المحرر حتى تتمكن من ضبط الفقرات من اليسار لليمين أو من اليمين لليسار" - } - }, - "composer": { - "title": "محرر الرسائل", - "description": "إعداد خيارات كتابة البريد الإلكتروني", - "autosave": { - "label": "فاصل الحفظ التلقائي", - "description": "عدد مرات حفظ المسودات تلقائيًا", - "30s": "كل 30 ثانية", - "1m": "كل دقيقة", - "2m": "كل دقيقتين", - "5m": "كل 5 دقائق" - }, - "send_confirmation": { - "label": "تأكيد الإرسال", - "description": "طلب تأكيد قبل إرسال الرسائل" - }, - "default_reply": { - "label": "وضع الرد الافتراضي", - "description": "الإجراء الافتراضي عند النقر على رد", - "reply": "رد", - "reply_all": "الرد على الجميع" - } - }, - "privacy": { - "title": "الخصوصية والأمان", - "description": "إدارة إعدادات الخصوصية والأمان", - "external_images": { - "label": "حظر الصور الخارجية", - "description": "منع التتبع عبر الصور الخارجية" - }, - "session_timeout": { - "label": "مهلة الجلسة", - "description": "تسجيل الخروج تلقائيًا بعد فترة من عدم النشاط", - "never": "أبدًا", - "30m": "30 دقيقة", - "1h": "ساعة واحدة", - "4h": "4 ساعات" - }, - "clear_cache": { - "label": "مسح ذاكرة التخزين المؤقت", - "description": "إزالة البيانات المخزنة مؤقتًا والملفات المؤقتة", - "button": "مسح ذاكرة التخزين المؤقت", - "confirm": "هل أنت متأكد أنك تريد مسح ذاكرة التخزين المؤقت؟", - "success": "تم مسح ذاكرة التخزين المؤقت بنجاح" - } - }, - "account": { - "title": "الحساب", - "description": "عرض معلومات حسابك", - "name_label": "الاسم المعروض", - "username_label": "اسم المستخدم", - "account_type_label": "نوع الحساب", - "auth_method_label": "المصادقة", - "auth_method_oauth": "تسجيل الدخول الموحّد (OAuth/OIDC)", - "auth_method_basic": "كلمة المرور", - "demo_account": "حساب تجريبي", - "email": { - "label": "عنوان البريد الإلكتروني", - "value": "{email}" - }, - "server": { - "label": "خادم JMAP", - "value": "{server}" - }, - "storage": { - "label": "استخدام التخزين", - "used": "{used} من {total} مستخدَم", - "percentage": "{percent}% مستخدَم" - }, - "last_sync": { - "label": "آخر مزامنة", - "value": "{time}" - }, - "accounts": { - "title": "الحسابات المسجَّلة", - "description": "اسحب لإعادة ترتيب ظهور الحسابات في قائمة الحسابات المنسدلة", - "active": "الحساب النشط حاليًا", - "default_badge": "الحساب الافتراضي", - "set_default": "تعيين كافتراضي", - "switch_to": "التبديل إلى هذا الحساب", - "move_up": "تحريك لأعلى", - "move_down": "تحريك لأسفل", - "drag_handle": "اسحب لإعادة الترتيب", - "add": "إضافة حساب" - }, - "shared_accounts": { - "title": "مشترك معي", - "description": "الحسابات الجماعية والمشتركة التي يمكنك إدارتها. اختر واحدًا لتعديل مرشحاته، والرد التلقائي أثناء الغياب، والتقويمات، وجهات الاتصال.", - "shared_label": "حساب مشترك" - } - }, - "scoped": { - "back": "العودة إلى حسابي", - "managing": "الإدارة: {name}" - }, - "security": { - "title": "أمان الحساب", - "description": "إدارة كلمة المرور والمصادقة الثنائية وإعدادات الأمان", - "detecting": "جارٍ اكتشاف قدرات الخادم...", - "not_available": "إدارة أمان الحساب غير متاحة لخادم البريد هذا. قد تكون الأذونات المطلوبة معطّلة. راجع
الوثائق للتفاصيل.", - "password": { - "title": "تغيير كلمة المرور", - "current": "كلمة المرور الحالية", - "new": "كلمة المرور الجديدة", - "confirm": "تأكيد كلمة المرور الجديدة", - "submit": "تغيير كلمة المرور", - "success": "تم تغيير كلمة المرور بنجاح", - "error_title": "فشل تغيير كلمة المرور", - "error_mismatch": "كلمتا المرور الجديدتان غير متطابقتين", - "error_min_length": "يجب أن تتكون كلمة المرور من 8 أحرف على الأقل", - "error_generic": "فشل تغيير كلمة المرور" - }, - "display_name": { - "label": "الاسم المعروض", - "description": "اسمك كما يظهر على الخادم", - "placeholder": "أدخل اسمك المعروض", - "save": "حفظ", - "success": "تم تحديث الاسم المعروض", - "error": "فشل تحديث الاسم المعروض" - }, - "totp": { - "section_title": "المصادقة الثنائية", - "label": "مصادقة TOTP", - "description": "أضف طبقة حماية إضافية بكلمة مرور لمرة واحدة قائمة على الوقت", - "active": "مفعّلة", - "inactive": "معطّلة", - "enabled": "تم تفعيل المصادقة الثنائية", - "disabled": "تم تعطيل المصادقة الثنائية", - "enable_error": "فشل تفعيل المصادقة الثنائية", - "disable_error": "فشل تعطيل المصادقة الثنائية", - "setup_instructions": "انسخ هذا الرابط إلى تطبيق المصادقة الخاص بك (Google Authenticator، Authy، إلخ):", - "verification_code": "رمز التحقق", - "confirm": "تأكيد", - "disable": "تعطيل", - "disable_confirm_prompt": "أدخل كلمة مرورك لتعطيل المصادقة الثنائية.", - "password_required": "كلمة المرور مطلوبة", - "code_required": "رمز التحقق مطلوب", - "code_invalid": "رمز التحقق غير صالح. تحقق من تطبيق المصادقة وحاول مرة أخرى." - }, - "app_passwords": { - "title": "كلمات مرور التطبيقات", - "description": "أنشئ كلمات مرور للتطبيقات التي لا تدعم المصادقة الثنائية", - "add": "إضافة", - "create": "إنشاء", - "cancel": "إلغاء", - "done": "تم", - "generate": "توليد", - "name_label": "اسم التطبيق", - "name_placeholder": "مثال: Thunderbird، iPhone Mail", - "expires_label": "تاريخ الانتهاء (اختياري)", - "allowed_ips_label": "عناوين IP المسموح بها (اختياري)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "مفصولة بفواصل أو مسافات. اتركها فارغة للسماح بأي عنوان IP.", - "password_label": "كلمة المرور (اتركها فارغة للتوليد التلقائي)", - "password_placeholder": "تُولَّد تلقائيًا إذا تُركت فارغة", - "copy_now_warning": "انسخ كلمة المرور هذه الآن - لن تُعرض مرة أخرى.", - "added": "تم إنشاء كلمة مرور التطبيق", - "removed": "تمت إزالة كلمة مرور التطبيق", - "add_error": "فشل إنشاء كلمة مرور التطبيق", - "remove_error": "فشلت إزالة كلمة مرور التطبيق", - "none": "لا توجد كلمات مرور تطبيقات مُعدَّة" - }, - "api_keys": { - "title": "مفاتيح API", - "description": "أنشئ مفاتيح API للنصوص البرمجية والتكاملات التي تتواصل مع الخادم مباشرة", - "name_label": "اسم المفتاح", - "name_placeholder": "مثال: نص النسخ الاحتياطي، عامل CI", - "copy_now_warning": "انسخ مفتاح API هذا الآن - لن يُعرض مرة أخرى.", - "added": "تم إنشاء مفتاح API", - "removed": "تمت إزالة مفتاح API", - "add_error": "فشل إنشاء مفتاح API", - "remove_error": "فشلت إزالة مفتاح API", - "none": "لا توجد مفاتيح API مُعدَّة" - }, - "encryption": { - "section_title": "التشفير أثناء التخزين", - "label": "تشفير البريد", - "description": "تشفير الرسائل المخزنة على الخادم لمزيد من الخصوصية", - "active": "تشفير {type} مفعّل", - "inactive": "معطّل", - "enabled": "تم تفعيل التشفير أثناء التخزين", - "disabled_success": "تم تعطيل التشفير أثناء التخزين", - "error": "فشل تحديث إعدادات التشفير", - "algorithm_label": "خوارزمية التشفير", - "encrypt_on_append": "تشفير الرسائل الجديدة عند رفعها", - "allow_spam_training": "السماح بتدريب مرشّح البريد العشوائي قبل تشفير الرسائل", - "enable_encryption": "تفعيل التشفير", - "enable_tooltip": "تفعيل التشفير باستخدام هذا المفتاح", - "configure_title": "تفعيل التشفير باستخدام هذا المفتاح", - "enable_button": "تفعيل", - "disable_tooltip": "تعطيل التشفير", - "enabled_success": "تم تفعيل التشفير أثناء التخزين" - }, - "email_client": { - "title": "إعداد عميل البريد الإلكتروني", - "description": "استخدم بيانات الاعتماد هذه لإعداد عميل البريد على سطح المكتب أو الجوال (Thunderbird، Apple Mail، Outlook، إلخ)", - "jmap_username_label": "اسم مستخدم JMAP", - "copy": "نسخ", - "copied": "تم النسخ", - "password_instructions": "استخدم اسم مستخدم JMAP أعلاه مع كلمة مرور تطبيق لتسجيل الدخول إلى عميل البريد الخاص بك. أنشئ كلمة مرور تطبيق في القسم أعلاه إذا لم تفعل بعد." - }, - "link_device": { - "title": "ربط تطبيق الجوال", - "description": "سجّل الدخول إلى تطبيق Bulwark Mail للجوال دون كتابة أي شيء. أنشئ رمز QR هنا وامسحه ضوئيًا من شاشة تسجيل الدخول في التطبيق.", - "generate": "إظهار رمز QR", - "regenerate": "إظهار رمز جديد", - "instructions": "افتح تطبيق Bulwark Mail، اضغط على \"مسح رمز QR\" في شاشة تسجيل الدخول، ووجّه الكاميرا هنا.", - "expires_in": "تنتهي صلاحية هذا الرمز خلال {seconds} ثانية. يمكن استخدامه مرة واحدة فقط.", - "expired": "انتهت صلاحية هذا الرمز.", - "generating": "جارٍ التوليد…", - "error": "تعذّر إنشاء رمز الاقتران. يرجى المحاولة مرة أخرى." - }, - "public_keys": { - "name_label": "اسم المفتاح العام", - "name_placeholder": "مثال: مفتاحي", - "key_label": "المفتاح العام (S/MIME أو PGP بترميز ASCII)", - "key_placeholder": "الصق مفتاحك العام هنا", - "none": "لا توجد مفاتيح عامة مُعدّة" - } - }, - "identities": { - "title": "هويات الإرسال", - "description": "إدارة عناوين البريد الإلكتروني التي يمكنك الإرسال منها", - "identities_count": { - "label": "هوياتك", - "description": "عناوين البريد الإلكتروني المُعدَّة للإرسال", - "count_zero": "لا توجد هويات", - "count_one": "هوية واحدة", - "count_other": "{count} هويات" - }, - "manage": "إدارة الهويات", - "sub_addressing": { - "label": "العنونة الفرعية", - "description": "استخدم وسومًا مثل user+tag@domain.com لتنظيم البريد الوارد", - "learn_more": "معرفة المزيد" - } - }, - "vacation": { - "title": "الرد التلقائي أثناء الغياب", - "description": "الرد تلقائيًا على الرسائل الواردة أثناء غيابك", - "loading": "جارٍ تحميل إعدادات الغياب...", - "not_supported": "خادم بريدك لا يدعم ردود الغياب.", - "fetch_error": "فشل تحميل إعدادات الغياب. يرجى المحاولة مرة أخرى.", - "status": { - "label": "الرد التلقائي أثناء الغياب", - "description": "إرسال رد تلقائي إلى من يرسل إليك بريدًا", - "active": "مفعّل", - "inactive": "معطّل" - }, - "date_range": { - "title": "النطاق الزمني", - "description": "يمكنك اختياريًا تحديد الرد التلقائي بفترة معينة", - "start": "تاريخ البدء", - "start_description": "اتركه فارغًا لعدم وجود حد للبدء", - "end": "تاريخ الانتهاء", - "end_description": "اتركه فارغًا لعدم وجود حد للانتهاء" - }, - "message": { - "title": "رسالة الرد التلقائي", - "description": "الرسالة التي ستُرسل كرد", - "subject_label": "الموضوع", - "subject_description": "سطر موضوع الرد التلقائي", - "subject_placeholder": "خارج المكتب", - "body_label": "نص الرسالة", - "body_description": "محتوى الرسالة النصي العادي", - "body_placeholder": "شكرًا لرسالتك. أنا حاليًا خارج المكتب وسأرد عند عودتي.", - "html_label": "رسالة منسّقة (HTML)", - "html_description": "أضِف نسخة منسّقة تتضمّن روابط وتنسيقًا. سيرى المستلمون الذين لا يستطيع برنامج بريدهم عرضها النصّ العادي أعلاه.", - "html_placeholder": "اكتب ردّ غياب منسّقًا…" - }, - "preview": { - "title": "معاينة", - "show": "إظهار المعاينة", - "hide": "إخفاء المعاينة" - }, - "save": "حفظ التغييرات", - "saving": "جارٍ الحفظ...", - "warnings": { - "end_before_start": "يجب أن يكون تاريخ الانتهاء بعد تاريخ البدء", - "start_in_past": "تاريخ البدء في الماضي", - "empty_body": "نص الرسالة فارغ - سيتلقى المستلمون ردًا فارغًا", - "subject_too_long": "الموضوع طويل جدًا بالنسبة للخادم (بحد أقصى {max} بايت).", - "body_too_long": "الرسالة طويلة جدًا بالنسبة للخادم (بحد أقصى {max} بايت بما في ذلك التنسيق)." - } - }, - "folders": { - "role_memos": "الملاحظات", - "title": "المجلدات", - "description": "إدارة مجلدات بريدك وتعيين الأدوار القياسية", - "folder_list": "مجلداتك", - "folder_list_description": "انقر على أيقونة مجلد لتخصيصه", - "standard_roles": "أدوار المجلدات القياسية", - "standard_roles_description": "عيّن أي المجلدات تُستخدم للأدوار القياسية لصندوق البريد مثل الوارد، المرسل، المهملات، إلخ.", - "role_inbox": "البريد الوارد", - "role_drafts": "المسودات", - "role_sent": "المرسل", - "role_trash": "المهملات", - "role_junk": "البريد المزعج", - "role_archive": "الأرشيف", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "بلا", - "create_folder": "إنشاء مجلد", - "create_subfolder": "إنشاء مجلد فرعي", - "reorder": "اسحب لإعادة الترتيب", - "reorder_error": "فشل في إعادة ترتيب المجلدات", - "move_error": "فشل في نقل المجلد", - "subfolder_of": "داخل {name}", - "subfolder_name": "اسم المجلد الفرعي", - "new_folder_name": "اسم المجلد", - "rename": "إعادة تسمية", - "change_icon": "تغيير الأيقونة", - "delete": "حذف", - "confirm_delete": "هل أنت متأكد أنك تريد حذف \"{name}\"؟ ستُنقل الرسائل في هذا المجلد إلى المهملات.", - "create": "إنشاء", - "cancel": "إلغاء", - "no_folders": "لا توجد مجلدات مخصصة", - "cannot_delete_role": "لا يمكن حذف مجلد له دور قياسي. أزل الدور أولًا.", - "folder_created": "تم إنشاء المجلد", - "folder_renamed": "تمت إعادة تسمية المجلد", - "folder_deleted": "تم حذف المجلد", - "role_updated": "تم تحديث دور المجلد", - "error_create": "فشل إنشاء المجلد", - "error_rename": "فشلت إعادة تسمية المجلد", - "error_delete": "فشل حذف المجلد", - "error_delete_has_children": "لا يمكن حذف المجلد: لا يزال يحتوي على مجلدات فرعية. احذفها أو انقلها أولًا.", - "error_delete_has_email": "لا يمكن حذف المجلد: لا يزال يحتوي على رسائل. انقلها أو احذفها أولًا.", - "error_role": "فشل تحديث دور المجلد" - }, - "advanced": { - "title": "متقدم", - "description": "خيارات متقدمة وإعدادات المطورين", - "debug_mode": { - "label": "وضع التصحيح", - "description": "تفعيل التسجيل المفصّل لاستكشاف الأخطاء وإصلاحها" - }, - "debug_categories": { - "description": "اختر الفئات التي تريد تسجيلها. عطّل الفئات التي لا تحتاجها لتقليل ضجيج وحدة التحكم.", - "jmap": "عميل JMAP", - "jmap_description": "عمليات صناديق البريد، وجلب الرسائل، وطلبات بروتوكول JMAP", - "calendar": "التقويم", - "calendar_description": "أحداث التقويم، والاستيراد، ورسائل الجدولة", - "tasks": "المهام", - "tasks_description": "إنشاء مهام التقويم وجلبها وتحديثها", - "auth": "المصادقة", - "auth_description": "تسجيل الدخول، وTOTP، وتبادل الرموز، وإدارة الجلسات", - "filters": "المرشحات", - "filters_description": "قواعد مرشح Sieve ونصوص الغياب", - "email": "عرض البريد", - "email_description": "عرض الرسائل، ومعالجة TNEF، والتمييز كمقروء", - "push": "الإشعارات الفورية", - "push_description": "إعداد الإشعارات الفورية وتسليمها", - "contacts": "جهات الاتصال ودفاتر العناوين", - "contacts_description": "مزامنة جهات الاتصال، وعمليات دفتر العناوين، والمرسلون الموثوقون" - }, - "settings_sync": { - "label": "مزامنة الإعدادات", - "description": "مزامنة إعداداتك عبر المتصفحات والأجهزة" - }, - "sender_favicons": { - "label": "أيقونات مواقع المرسلين", - "description": "إظهار أيقونات المواقع كصور شخصية للمرسلين من الشركات" - }, - "show_avatars_in_junk": { - "label": "إظهار الصور الشخصية في مجلد البريد المزعج", - "description": "إظهار الصور الشخصية وأيقونات المواقع للمرسلين في مجلد البريد المزعج. معطّل افتراضيًا لتجنب إضفاء شرعية بصرية على محاولات التصيد الاحتيالي." - }, - "keyboard_shortcuts": { - "label": "اختصارات لوحة المفاتيح", - "description": "عرض اختصارات لوحة المفاتيح المتاحة", - "button": "عرض الاختصارات" - }, - "refresh_cache": { - "label": "تحديث البيانات المخزنة مؤقتًا", - "description": "إعادة تحميل جهات الاتصال والتقويمات والمجلدات من الخادم. يحافظ على حساباتك وجلساتك — يصلح العرض القديم أو الخاطئ دون تسجيل الخروج.", - "button": "تحديث" - }, - "reset_settings": { - "label": "إعادة تعيين الإعدادات", - "description": "استعادة جميع الإعدادات إلى القيم الافتراضية", - "button": "إعادة التعيين للافتراضي" - }, - "export_settings": { - "label": "تصدير الإعدادات", - "description": "تنزيل إعداداتك بصيغة JSON", - "button": "تصدير" - }, - "import_settings": { - "label": "استيراد الإعدادات", - "description": "رفع الإعدادات من ملف JSON", - "button": "استيراد" - }, - "about": { - "title": "بريد Bulwark الإلكتروني" - } - }, - "sidebar_apps": { - "title": "تطبيقات الشريط الجانبي", - "description": "إدارة التطبيقات والروابط المخصصة في الشريط الجانبي", - "keep_loaded": "إبقاء التطبيقات محمّلة", - "keep_loaded_description": "إبقاء التطبيقات الداخلية تعمل في الخلفية عند التبديل بينها لتجنب إعادة التحميل", - "managed_title": "مقدَّمة من المسؤول", - "managed_description": "تم إعداد هذه التطبيقات للجميع ولا يمكن تغييرها.", - "manage_title": "التطبيقات المخصصة", - "manage_description": "إضافة أو تعديل أو إزالة التطبيقات المخصصة من الشريط الجانبي" - }, - "contacts": { - "title": "جهات الاتصال", - "description": "استيراد وتصدير جهات اتصالك", - "group_by_letter_label": "التجميع حسب الحرف الأول", - "sort_by_last_name_label": "الترتيب حسب اسم العائلة", - "sort_by_last_name_description": "ترتيب قائمة جهات الاتصال حسب اسم العائلة بحيث يظهر أفراد العائلة معًا", - "group_by_letter_description": "إظهار عناوين أقسام أبجدية في قائمة جهات الاتصال", - "import_label": "استيراد جهات الاتصال", - "import_description": "استيراد جهات الاتصال من ملف vCard (‎.vcf)", - "export_label": "تصدير جهات الاتصال", - "export_description": "تصدير جميع جهات الاتصال كملف vCard (‎.vcf)", - "manage_title": "دفاتر العناوين", - "manage_description": "إعادة تسمية دفاتر عناوينك", - "no_address_books": "لم يتم العثور على دفاتر عناوين", - "categories_title": "الفئات", - "categories_description": "إعادة تسمية فئات جهات الاتصال", - "no_categories": "لم يتم العثور على فئات" - }, - "downloads": { - "title": "التنزيلات", - "description": "تخصيص كيفية تسمية الرسائل والمرفقات التي تُنزَّل.", - "reset": "استعادة الافتراضي", - "preview": "معاينة:", - "email_template": { - "label": "اسم ملف البريد (‎.eml)", - "description": "القالب المستخدم عند تصدير رسالة أو سحبها إلى نظام الملفات. تُضاف امتداد ‎.eml تلقائيًا." - }, - "attachment_template": { - "label": "اسم ملف المرفق", - "description": "القالب المستخدم عند تنزيل مرفق أو سحبه للخارج. إذا حذفت '{filename}' و'{ext}'، يُحتفظ بالامتداد الأصلي." - }, - "bundle_template": { - "label": "اسم ملف ‎.zip لعدة رسائل", - "description": "القالب المستخدم عند سحب أو تنزيل عدة رسائل محددة كأرشيف ‎.zip واحد. يُضاف امتداد ‎.zip تلقائيًا." - }, - "spaces": { - "label": "المسافات", - "description": "استبدال المسافات في اسم الملف الناتج بحرف آخر.", - "keep": "الإبقاء على المسافات", - "underscore": "الاستبدال بـ _", - "dash": "الاستبدال بـ -" - }, - "lowercase": { - "label": "أحرف صغيرة", - "description": "فرض تحويل اسم الملف بالكامل إلى أحرف صغيرة." - }, - "strip_diacritics": { - "label": "إزالة علامات التشكيل", - "description": "تحويل الأحرف المنقّطة إلى ما يعادلها بترميز ASCII (ä ← a، é ← e). مفيد للأدوات التي تُشوّه أسماء الملفات بترميز Unicode." - }, - "collapse_separators": { - "label": "دمج الفواصل المتكررة", - "description": "دمج سلاسل المسافات أو الشرطات السفلية أو الشرطات في حرف واحد." - }, - "after_export": { - "label": "بعد التصدير", - "description": "يمكنك اختياريًا نقل الرسالة بعد تصديرها كملف ‎.eml.", - "keep": "الإبقاء في صندوق البريد", - "archive": "النقل إلى الأرشيف", - "trash": "النقل إلى المهملات" - } - }, - "filters": { - "title": "مرشحات البريد", - "description": "إنشاء قواعد لفرز الرسائل الواردة ووسمها وإدارتها تلقائيًا", - "add_rule": "إضافة قاعدة", - "no_rules": "لا توجد قواعد مرشح", - "no_rules_description": "أنشئ قواعد لتنظيم رسائلك الواردة تلقائيًا", - "vacation_active": "الرد التلقائي أثناء الغياب مفعّل", - "vacation_active_description": "الرد التلقائي مفعّل للرسائل الواردة", - "vacation_configure": "إعداد", - "edit_rule": "تعديل القاعدة", - "new_rule": "قاعدة جديدة", - "delete_rule": "حذف القاعدة", - "delete_confirm": "هل أنت متأكد أنك تريد حذف هذه القاعدة؟", - "enable": "تفعيل", - "disable": "تعطيل", - "raw_editor": "محرر Sieve الخام", - "raw_editor_warning": "قد يؤدي تعديل نص Sieve الخام إلى تعطيل التعديل المرئي للقواعد. التغييرات هنا تتجاوز أداة الإنشاء المرئي.", - "validate": "تحقق", - "validation_success": "النص صالح", - "validation_error": "النص يحتوي على أخطاء", - "save": "حفظ القواعد", - "saving": "جارٍ الحفظ...", - "saved": "تم حفظ المرشحات بنجاح", - "save_failed": "فشل حفظ المرشحات", - "loading": "جارٍ تحميل المرشحات...", - "not_supported": "خادم بريدك لا يدعم مرشحات البريد.", - "rule_name": "اسم القاعدة", - "rule_name_placeholder": "مثال: فرز النشرات البريدية", - "match_all": "مطابقة كل الشروط", - "match_any": "مطابقة أي شرط", - "conditions": "الشروط", - "add_condition": "إضافة شرط", - "actions": "الإجراءات", - "add_action": "إضافة إجراء", - "stop_processing": "إيقاف معالجة القواعد التالية", - "include_spam": "نقل الرسائل المصنفة كرسائل مزعجة أيضًا", - "attachment_type_placeholder": "مثال: pdf, doc, jpg", - "value_placeholder_multi": "القيمة (عدة قيم مفصولة بفواصل)", - "condition_fields": { - "attachment": "المرفق", - "from": "من", - "to": "إلى", - "cc": "نسخة", - "subject": "الموضوع", - "header": "ترويسة مخصصة", - "size": "الحجم", - "body": "النص" - }, - "comparators": { - "has_any": "موجود", - "has_type": "من النوع", - "contains": "يحتوي على", - "not_contains": "لا يحتوي على", - "is": "يساوي تمامًا", - "not_is": "لا يساوي", - "starts_with": "يبدأ بـ", - "ends_with": "ينتهي بـ", - "matches": "يطابق النمط", - "greater_than": "أكبر من", - "less_than": "أصغر من" - }, - "action_types": { - "move": "النقل إلى مجلد", - "copy": "النسخ إلى مجلد", - "forward": "إعادة التوجيه إلى", - "mark_read": "التمييز كمقروءة", - "star": "تمييز الرسالة بنجمة", - "add_label": "إضافة وسم", - "discard": "التجاهل (حذف صامت)", - "reject": "الرفض برسالة", - "keep": "الإبقاء في الوارد", - "stop": "إيقاف المعالجة" - }, - "move_to_folder": "اختر المجلد", - "copy_to_folder": "اختر المجلد", - "forward_to": "إعادة التوجيه إلى عنوان بريد إلكتروني", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "الاحتفاظ بنسخة", - "forward_limit": "حد إعادة التوجيه لكل رسالة على هذا الخادم: {count}. يتم تخطي عمليات إعادة التوجيه الإضافية.", - "reject_message": "رسالة الرفض", - "reject_placeholder": "تم رفض رسالتك", - "label_name": "اسم الوسم", - "label_placeholder": "اختر وسمًا", - "header_name": "اسم الترويسة", - "header_placeholder": "مثال: X-Mailing-List", - "size_bytes": "الحجم بالبايت", - "size_placeholder": "مثال: 1000000", - "system_managed": "قاعدة تديرها المنظومة", - "opaque_warning": "تم تعديل هذا النص خارج أداة الإنشاء المرئي. يتوفر فقط تعديل Sieve الخام.", - "open_sieve_editor": "فتح محرر Sieve الخام", - "fetch_error": "فشل تحميل المرشحات", - "expanded_view": "عرض موسّع", - "expanded_view_description": "إظهار قواعد المرشح مع كتل الشروط والإجراءات المفصّلة", - "if": "إذا", - "then": "عندئذٍ", - "match_all_conditions": "تطابق الكل", - "match_any_condition": "تطابق أي منها", - "and": "و", - "or": "أو", - "cancel": "إلغاء", - "confirm_delete": "حذف", - "rule_list": "قواعد المرشح", - "drag_to_reorder": "اسحب لإعادة الترتيب", - "match_type": "نوع المطابقة", - "reset_to_visual": "إعادة التعيين إلى أداة الإنشاء المرئي", - "reset_warning": "سيؤدي هذا إلى تجاهل النص الحالي والبدء من جديد.", - "confirm_reset": "إعادة التعيين", - "validation_empty_name": "اسم القاعدة مطلوب", - "validation_empty_conditions": "مطلوب شرط واحد على الأقل بقيمة", - "validation_empty_actions": "مطلوب إجراء واحد على الأقل", - "templates_section": "البدء من قالب", - "template_newsletters": "نقل النشرات البريدية إلى مجلد", - "template_receipts": "أرشفة الإيصالات تلقائيًا", - "template_important": "وسم الرسائل المهمة", - "template_notifications": "تصفية الإشعارات", - "sieve_editor": { - "title": "محرر نص Sieve", - "warning": "قد يؤدي تعديل نص Sieve الخام إلى تعطيل التعديل المرئي للقواعد. التغييرات هنا تتجاوز أداة الإنشاء المرئي.", - "script_content": "نص Sieve", - "valid": "النص صالح", - "invalid": "النص يحتوي على أخطاء", - "save_warning": "سيؤدي الحفظ إلى استبدال أي قواعد مرئية. لا يمكن التراجع عن هذا. انقر على حفظ مرة أخرى للتأكيد.", - "validating": "جارٍ التحقق...", - "validate": "تحقق", - "cancel": "إلغاء", - "save": "حفظ", - "confirm_save": "تأكيد الحفظ", - "validation_failed": "فشل طلب التحقق" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# شرط} other {# شروط}}", - "actions_count": "{count, plural, one {# إجراء} other {# إجراءات}}" - }, - "origin_external": "خارجي", - "managed_by_tooltip": "تُدار بواسطة {source}. عدّلها في ذلك التطبيق، أو استخدم محرر Sieve الخام." - }, - "templates": { - "title": "قوالب البريد", - "description": "إنشاء قوالب بريد قابلة لإعادة الاستخدام بمتغيرات نائبة", - "add": "قالب جديد", - "edit": "تعديل القالب", - "name": "اسم القالب", - "name_placeholder": "مثال: رسالة متابعة", - "category": "الفئة", - "category_placeholder": "مثال: عمل، شخصي", - "subject": "الموضوع", - "subject_placeholder": "سطر موضوع الرسالة", - "body": "النص", - "body_placeholder": "محتوى نص الرسالة...", - "recipients_placeholder": "email@example.com", - "identity": "الإرسال كـ", - "default_identity": "الهوية الافتراضية", - "favorite": "مفضّل", - "cancel": "إلغاء", - "create": "إنشاء", - "update": "تحديث", - "confirm_delete": "حذف", - "no_templates": "لا توجد قوالب بعد", - "manage": "إدارة القوالب", - "count": "{count, plural, one {# قالب} other {# قوالب}}", - "export_import": "التصدير والاستيراد", - "export_import_description": "احفظ نسخة احتياطية من قوالبك أو انقلها إلى جهاز آخر", - "export": "تصدير", - "import": "استيراد", - "validation": { - "empty": "اسم القالب مطلوب", - "too_long": "يجب ألا يتجاوز اسم القالب 200 حرف" - } - }, - "files": { - "display": { - "title": "العرض", - "description": "إعداد كيفية عرض الملفات والمجلدات" - }, - "folder_layout": { - "label": "التنقل بين المجلدات", - "description": "اختر كيفية عرض المجلدات: ضمن الملفات أو في شجرة بالشريط الجانبي", - "inline": "ضمن الصفحة", - "sidebar": "الشريط الجانبي" - }, - "default_view": { - "label": "العرض الافتراضي", - "description": "اختر بين تخطيط الشبكة والقائمة", - "list": "قائمة", - "grid": "شبكة" - }, - "default_sort": { - "label": "الترتيب الافتراضي", - "description": "اختر الترتيب الافتراضي للملفات", - "name": "الاسم", - "size": "الحجم", - "modified": "تاريخ التعديل" - }, - "sort_direction": { - "label": "اتجاه الترتيب", - "description": "اختر ترتيبًا تصاعديًا أو تنازليًا", - "ascending": "تصاعدي", - "descending": "تنازلي" - }, - "icons": { - "title": "الأيقونات", - "description": "إعداد مظهر أيقونات الملفات" - }, - "show_icons": { - "label": "إظهار أيقونات الملفات", - "description": "عرض أيقونات بجانب الملفات والمجلدات" - }, - "colored_icons": { - "label": "أيقونات ملونة", - "description": "استخدام أيقونات ملونة بدلًا من أحادية اللون" - }, - "show_thumbnails": { - "label": "إظهار الصور المصغّرة", - "description": "عرض معاينات الصور بدلًا من الأيقونات لملفات الصور" - }, - "behavior": { - "title": "السلوك", - "description": "إعداد سلوك متصفح الملفات" - }, - "show_hidden": { - "label": "إظهار الملفات المخفية", - "description": "عرض الملفات والمجلدات التي تبدأ بنقطة" - }, - "preview": { - "label": "معاينة" - } - } - }, - "errors": { - "page_error_title": "حدث خطأ ما", - "page_error_description": "واجهنا خطأ غير متوقع. يرجى المحاولة مرة أخرى أو العودة إلى الصفحة الرئيسية.", - "sidebar_error": "تعذّر تحميل صناديق البريد", - "email_list_error": "تعذّر تحميل الرسائل", - "viewer_error_title": "تعذّر عرض الرسالة", - "viewer_error_description": "حدثت مشكلة أثناء عرض هذه الرسالة. قد تحتوي على محتوى غير مدعوم.", - "composer_error": "تعذّر تحميل محرر الرسائل", - "settings_error_title": "الإعدادات غير متاحة", - "settings_error_description": "تعذّر تحميل الإعدادات. قد لا يتم حفظ تفضيلاتك.", - "try_again": "حاول مرة أخرى", - "reload": "إعادة التحميل", - "reload_emails": "إعادة تحميل الرسائل", - "reload_settings": "إعادة تحميل الإعدادات", - "retry": "إعادة المحاولة", - "go_home": "الانتقال إلى البريد الوارد" - }, - "context_menu": { - "reply": "رد", - "reply_all": "الرد على الجميع", - "forward": "إعادة توجيه", - "mark_read": "تمييز كمقروءة", - "mark_unread": "تمييز كغير مقروءة", - "star": "تمييز بنجمة", - "unstar": "إزالة النجمة", - "pin": "تثبيت", - "unpin": "إلغاء التثبيت", - "move_to": "نقل إلى...", - "archive": "أرشفة", - "delete": "حذف", - "mark_as_spam": "الإبلاغ عن بريد مزعج", - "not_spam": "ليس مزعجًا", - "tag": "وسم", - "items_selected": "{count} رسالة محددة", - "edit_draft": "تعديل المسودة", - "cancel_scheduled_send": "إلغاء الإرسال", - "reschedule_send": "إعادة الجدولة", - "cancel_and_edit": "إلغاء والتعديل", - "cancel_and_compose_again": "إلغاء وإعادة الكتابة" - }, - "mailbox_context_menu": { - "mark_folder_read": "تمييز المجلد كمقروء", - "mark_folder_tree_read": "تمييز المجلد والمجلدات الفرعية كمقروءة", - "mark_all_folders_read": "تمييز جميع المجلدات كمقروءة", - "new_subfolder": "مجلد فرعي جديد...", - "new_folder": "مجلد جديد...", - "rename": "إعادة تسمية...", - "share": "مشاركة...", - "import_email": "استيراد ملف ‎.eml أو ‎.zip...", - "empty_folder": "إفراغ المجلد", - "empty_folder_generic": "إفراغ المجلد", - "delete_folder": "حذف المجلد", - "refresh": "تحديث", - "mark_all_confirm_title": "تمييز جميع المجلدات كمقروءة", - "mark_all_confirm_message": "هل تريد تمييز كل رسالة غير مقروءة في حسابك الشخصي كمقروءة؟", - "delete_confirm_title": "حذف المجلد", - "delete_confirm_message": "هل تريد حذف المجلد \"{name}\" نهائيًا؟ لا يمكن التراجع عن هذا الإجراء.", - "prompt_new_subfolder": "أدخل اسمًا للمجلد الفرعي الجديد.", - "prompt_new_folder": "أدخل اسمًا للمجلد الجديد.", - "prompt_rename": "أدخل اسمًا جديدًا لهذا المجلد.", - "placeholder_folder_name": "اسم المجلد", - "create": "إنشاء", - "rename_confirm": "إعادة تسمية", - "toast_marked_read": "تم تمييز المجلد كمقروء", - "toast_marked_read_count": "تم تمييز {count, plural, one {رسالة واحدة} other {# رسالة}} كمقروءة", - "toast_already_read": "لا توجد رسائل غير مقروءة", - "toast_marked_all_read": "تم تمييز جميع المجلدات كمقروءة", - "toast_emptied": "تم إفراغ المجلد", - "toast_folder_created": "تم إنشاء المجلد", - "toast_folder_renamed": "تمت إعادة تسمية المجلد", - "toast_folder_deleted": "تم حذف المجلد", - "toast_error_mark_read": "فشل التمييز كمقروء", - "toast_error_empty": "فشل إفراغ المجلد", - "toast_error_create": "فشل إنشاء المجلد", - "toast_error_rename": "فشلت إعادة تسمية المجلد", - "toast_error_delete": "فشل حذف المجلد", - "toast_error_delete_has_children": "المجلد يحتوي على مجلدات فرعية. أزلها أولًا.", - "toast_error_delete_has_email": "المجلد ليس فارغًا. أفرغه أولًا.", - "delete_with_contents_title": "المجلد ليس فارغًا", - "delete_with_contents_message": "لا يزال المجلد \"{name}\" يحتوي على {count, plural, one {رسالة واحدة} other {# رسائل}}. هل تريد حذف المجلد وكل رسائله؟ لا يمكن التراجع عن هذا.", - "delete_with_contents_confirm": "حذف المجلد والرسائل" - }, - "shortcuts": { - "title": "اختصارات لوحة المفاتيح", - "tip": "اضغط ؟ في أي وقت لإظهار هذه المساعدة", - "sections": { - "navigation": "التنقل", - "actions": "إجراءات البريد", - "global": "عام", - "threads": "المحادثات", - "composer": "محرر الرسائل" - }, - "navigation": { - "next_email": "الرسالة التالية", - "previous_email": "الرسالة السابقة", - "open_email": "فتح الرسالة", - "close_email": "إغلاق / إلغاء التحديد" - }, - "actions": { - "reply": "رد", - "reply_all": "الرد على الجميع", - "forward": "إعادة توجيه", - "star": "تبديل النجمة", - "archive": "أرشفة", - "delete": "حذف", - "mark_unread": "تمييز كغير مقروءة", - "mark_read": "تمييز كمقروءة", - "toggle_spam": "الإبلاغ عن بريد مزعج / ليس مزعجًا" - }, - "global": { - "compose": "إنشاء رسالة جديدة", - "search": "التركيز على البحث", - "help": "إظهار الاختصارات", - "refresh": "تحديث الرسائل", - "select_all": "تحديد الكل", - "global_search": "البحث في كل شيء" - }, - "threads": { - "expand_collapse": "توسيع/طي المحادثة" - }, - "composer": { - "send": "إرسال الرسالة", - "schedule_send": "جدولة الإرسال", - "template_picker": "فتح منتقي القوالب" - } - }, - "threads": { - "messages_one": "{count} رسالة", - "messages_other": "{count} رسالة", - "messages_tooltip": "{count, plural, one {# رسالة في هذه المحادثة} other {# رسالة في هذه المحادثة}}", - "expand": "توسيع المحادثة", - "collapse": "طي المحادثة", - "loading": "جارٍ تحميل المحادثة...", - "mark_read": "تمييز المحادثة كمقروءة", - "mark_unread": "تمييز المحادثة كغير مقروءة", - "archive": "أرشفة المحادثة", - "delete": "حذف المحادثة", - "star": "تمييز المحادثة بنجمة", - "unstar": "إزالة النجمة عن المحادثة", - "toggle_thread": "تبديل عرض المحادثة" - }, - "identities": { - "modal_title": "إدارة هويات الإرسال", - "create_new": "إنشاء هوية جديدة", - "edit_identity": "تعديل الهوية", - "delete_confirm": "حذف هذه الهوية؟ لا يمكن التراجع عن هذا.", - "cannot_delete": "لا يمكن حذف هذه الهوية", - "primary_identity": "أساسية", - "set_as_primary": "تعيين كأساسية", - "no_identities": "لم يتم العثور على هويات", - "display": { - "reply_to": "الرد إلى:", - "bcc": "نسخة مخفية:", - "signature": "التوقيع:", - "preview": "معاينة:" - }, - "validation_errors": { - "invalid_emails": "عناوين بريد غير صالحة: {emails}", - "unknown_error": "خطأ غير معروف" - }, - "form": { - "name_label": "الاسم المعروض", - "name_placeholder": "مثال: بريد العمل، شخصي", - "name_required": "الاسم مطلوب", - "email_label": "عنوان البريد الإلكتروني", - "email_placeholder": "your.email@example.com", - "email_required": "البريد الإلكتروني مطلوب", - "email_invalid": "يرجى إدخال عنوان بريد إلكتروني صالح", - "email_immutable": "لا يمكن تغيير عنوان البريد الإلكتروني بعد الإنشاء", - "reply_to_label": "الرد إلى (اختياري)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "نسخة مخفية تلقائية (اختياري)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "توقيع نصي", - "html_signature_label": "توقيع HTML", - "signature_byte_counter": "{bytes} / {max} بايت", - "signature_byte_limit_reached": "تم بلوغ حد الخادم", - "save": "حفظ الهوية", - "cancel": "إلغاء", - "creating": "جارٍ الإنشاء...", - "updating": "جارٍ التحديث..." - }, - "sub_address": { - "button_tooltip": "استخدام عنوان فرعي", - "popover_title": "إضافة وسم عنوان فرعي", - "tag_input_placeholder": "أدخل وسمًا (مثال: shopping)", - "preview_label": "معاينة:", - "recent_tags": "الوسوم الأخيرة", - "suggested_tags": "مقترح", - "use_address": "استخدام هذا العنوان", - "invalid_tag": "يجب أن يتكون الوسم من أحرف وأرقام وشرطات فقط", - "tag_too_long": "يجب ألا يتجاوز الوسم 30 حرفًا", - "help_text": "الرسائل المرسلة إلى user{delimiter}tag@domain.com ستصل إلى بريدك الوارد", - "validation": { - "empty": "لا يمكن أن يكون الوسم فارغًا", - "too_long": "يجب ألا يتجاوز الوسم {max} حرفًا", - "invalid_chars": "يجب أن يحتوي الوسم على أحرف وأرقام وشرطات فقط" - } - }, - "badge": { - "sent_via": "عبر", - "sub_address_tag": "أُرسلت باستخدام عنوان فرعي: {tag}", - "identity_name": "أُرسلت باستخدام هوية: {name}", - "identity_short": "عبر {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "حذف", - "delete_confirm_title": "حذف الهوية" - }, - "templates": { - "picker_title": "اختر قالبًا", - "search_placeholder": "بحث في القوالب...", - "section_favorites": "المفضّلة", - "section_recent": "الأخيرة", - "section_uncategorized": "أخرى", - "no_templates": "لا توجد قوالب بعد", - "no_results": "لم يتم العثور على قوالب", - "fill_placeholders": "تعبئة القيم النائبة", - "enter_value": "أدخل قيمة...", - "preview": "معاينة", - "insert_with_values": "الإدراج بالقيم", - "insert_raw": "الإدراج كما هو", - "copy_suffix": "(نسخة)", - "placeholder": "متغيّر", - "placeholders": { - "recipient_name": "اسم المستلم", - "company": "اسم الشركة", - "date": "التاريخ الحالي", - "day_of_week": "يوم الأسبوع", - "sender_name": "اسمك" - } - }, - "contacts": { - "title": "جهات الاتصال", - "search_placeholder": "بحث في جهات الاتصال...", - "create_new": "جهة اتصال جديدة", - "no_category": "بلا فئة", - "rename_category": "إعادة تسمية الفئة", - "category_name_label": "اسم الفئة", - "category_renamed": "تمت إعادة تسمية الفئة", - "category_rename_failed": "فشلت إعادة تسمية الفئة", - "category_added": "تمت إضافة جهة الاتصال إلى {name}", - "category_added_plural": "تمت إضافة {count} جهات اتصال إلى {name}", - "empty_state": "لا توجد جهات اتصال بعد", - "empty_state_title": "لا توجد جهات اتصال بعد", - "empty_state_subtitle": "أنشئ أول جهة اتصال أو استوردها من ملف vCard", - "empty_search": "لا توجد جهات اتصال مطابقة لبحثك", - "empty_search_hint": "جرّب مصطلح بحث مختلف", - "empty_filtered": "لا توجد جهات اتصال مطابقة لمرشحاتك", - "empty_filtered_hint": "حاول تعديل المرشحات أو مسحها", - "clear_search": "مسح البحث", - "import_vcard": "استيراد vCard", - "delete_confirm_title": "حذف جهة الاتصال", - "delete_confirm": "هل أنت متأكد أنك تريد حذف جهة الاتصال هذه؟", - "local_mode": "تُخزَّن جهات الاتصال محليًا (الخادم لا يدعم جهات اتصال JMAP)", - "back_to_contacts": "العودة إلى جهات الاتصال", - "back_to_email": "العودة إلى البريد", - "open_categories": "فتح الفئات", - "tabs": { - "all": "الكل", - "groups": "المجموعات" - }, - "shared": { - "title": "مشترك" - }, - "address_books": { - "create": "دفتر عناوين جديد", - "created": "تم إنشاء دفتر العناوين", - "create_failed": "فشل إنشاء دفتر العناوين", - "title": "دفاتر عناويني", - "shared_prefix": "مشترك: {name}", - "moved": "تم نقل جهة الاتصال إلى {name}", - "moved_plural": "تم نقل {count} جهات اتصال إلى {name}", - "move_failed": "فشل نقل جهة الاتصال", - "address_book": "دفتر العناوين", - "rename": "إعادة تسمية دفتر العناوين", - "name_label": "اسم دفتر العناوين", - "renamed": "تمت إعادة تسمية دفتر العناوين", - "rename_failed": "فشلت إعادة تسمية دفتر العناوين", - "default": "افتراضي", - "set_default": "تعيين كافتراضي", - "default_updated": "تم تحديث دفتر العناوين الافتراضي", - "set_default_failed": "فشل تعيين دفتر العناوين الافتراضي", - "manage": "إدارة دفاتر العناوين", - "share": "مشاركة دفتر العناوين", - "new_contact_in_book": "جهة اتصال جديدة في دفتر العناوين هذا", - "delete": "حذف دفتر العناوين", - "confirm_delete": "حذف \"{name}\"؟ ستتم إزالة جميع جهات الاتصال في دفتر العناوين هذا.", - "deleted": "تم حذف دفتر العناوين", - "delete_failed": "فشل حذف دفتر العناوين" - }, - "detail": { - "emails": "عناوين البريد الإلكتروني", - "phones": "أرقام الهاتف", - "organizations": "المؤسسات", - "addresses": "العناوين", - "notes": "ملاحظات", - "titles": "الألقاب والأدوار", - "online_services": "الخدمات عبر الإنترنت", - "anniversaries": "المناسبات", - "personal_info": "معلومات شخصية", - "languages": "اللغات", - "categories": "الفئات", - "related_contacts": "جهات اتصال ذات صلة", - "crypto_keys": "مفاتيح التشفير", - "cert_issuer": "الجهة المُصدِرة", - "cert_expires": "تنتهي في", - "cert_expired": "منتهية الصلاحية", - "cert_fingerprint": "البصمة", - "cert_algorithm": "الخوارزمية", - "import_to_smime": "الاستيراد إلى S/MIME", - "cert_already_imported": "مستوردة بالفعل إلى S/MIME", - "cert_imported": "تم استيراد الشهادة إلى مخزن S/MIME", - "cert_import_failed": "فشل استيراد الشهادة", - "no_contact_selected": "اختر جهة اتصال لعرض التفاصيل", - "compose_email": "إنشاء رسالة", - "copy_email": "نسخ البريد الإلكتروني", - "copy_phone": "نسخ رقم الهاتف", - "copy_url": "نسخ الرابط", - "copied": "تم النسخ إلى الحافظة", - "copy_failed": "فشل النسخ إلى الحافظة", - "created": "تاريخ الإنشاء", - "updated": "آخر تحديث", - "timezone": "المنطقة الزمنية", - "anniversary_birth": "عيد الميلاد", - "anniversary_death": "الوفاة", - "anniversary_wedding": "الذكرى السنوية", - "anniversary_other": "أخرى", - "personal_expertise": "الخبرة", - "personal_hobby": "الهواية", - "personal_interest": "الاهتمام", - "personal_other": "أخرى", - "gender": "الجنس", - "gender_masculine": "ذكر", - "gender_feminine": "أنثى", - "gender_other": "آخر", - "gender_none": "لا ينطبق", - "gender_unknown": "غير معروف", - "calendar": "التقويم", - "calendar_uri": "رابط التقويم", - "scheduling_uri": "رابط الجدولة", - "freebusy_uri": "رابط التوفر/الانشغال", - "section_contact": "تفاصيل الاتصال", - "section_work": "العمل", - "section_personal": "شخصي", - "email_default_label": "بريد إلكتروني", - "phone_default_label": "هاتف", - "address_default_label": "عنوان", - "online_service_default_label": "عبر الإنترنت", - "organization_label": "المؤسسة", - "title_label": "اللقب", - "role_label": "الدور", - "language_label": "اللغة", - "related_default_label": "ذو صلة", - "more_actions": "المزيد من الإجراءات", - "age_years": "{count, plural, one {عمره سنة واحدة} other {عمره # سنة}}", - "years_since": "{count, plural, one {سنة واحدة} other {# سنوات}}" - }, - "activity": { - "recent_emails": "الرسائل الأخيرة", - "upcoming_events": "الأحداث القادمة", - "no_emails": "لا توجد رسائل حديثة", - "no_events": "لا توجد أحداث قادمة", - "no_subject": "(بلا موضوع)", - "no_title": "(بلا عنوان)", - "load_failed": "فشل التحميل", - "unknown_sender": "مرسل غير معروف", - "all_day": "طوال اليوم" - }, - "form": { - "create_title": "جهة اتصال جديدة", - "edit_title": "تعديل جهة الاتصال", - "section_address_book": "الدليل", - "select_address_book": "اختر دليلًا...", - "section_identity": "الاسم والهوية", - "contact_type": "نوع جهة الاتصال", - "type_person": "شخص", - "type_organization": "المؤسسة", - "section_work": "العمل والمؤسسة", - "prefix": "اللقب", - "prefix_placeholder": "د.، أ.، السيدة", - "given_name": "الاسم الأول", - "middle_name": "الاسم الأوسط", - "surname": "اسم العائلة", - "suffix": "اللاحقة", - "suffix_placeholder": "الابن، الأب، الثالث", - "nickname": "الاسم المستعار", - "nickname_placeholder": "الاسم المستعار", - "email": "البريد الإلكتروني", - "email_placeholder": "email@example.com", - "phone": "الهاتف", - "phone_placeholder": "+1 234 567 890", - "phone_type": "النوع", - "phone_voice": "صوت", - "phone_cell": "جوال", - "phone_fax": "فاكس", - "phone_pager": "جهاز نداء", - "phone_video": "فيديو", - "phone_text": "رسائل نصية", - "organization": "المؤسسة", - "organization_placeholder": "اسم الشركة", - "department": "القسم", - "department_placeholder": "القسم", - "job_title": "المسمى الوظيفي", - "job_title_placeholder": "مثال: مهندس برمجيات", - "role": "الدور", - "role_placeholder": "مثال: قائد فريق", - "addresses": "العناوين", - "add_address": "إضافة عنوان", - "street": "الشارع", - "city": "المدينة", - "region": "المحافظة / المنطقة", - "postcode": "الرمز البريدي", - "country": "البلد", - "online_services": "الخدمات عبر الإنترنت", - "add_online_service": "إضافة خدمة عبر الإنترنت", - "url_placeholder": "https://...", - "service_placeholder": "الخدمة", - "anniversaries": "المناسبات", - "add_anniversary": "إضافة تاريخ", - "anniversary_birth": "عيد الميلاد", - "anniversary_wedding": "الذكرى السنوية", - "anniversary_death": "الذكرى التأبينية", - "anniversary_other": "أخرى", - "personal_info": "معلومات شخصية", - "add_personal_info": "إضافة إدخال", - "personal_info_placeholder": "مثال: التصوير الفوتوغرافي", - "personal_expertise": "الخبرة", - "personal_hobby": "الهواية", - "personal_interest": "الاهتمام", - "personal_other": "أخرى", - "level": "المستوى", - "level_high": "مرتفع", - "level_medium": "متوسط", - "level_low": "منخفض", - "categories": "الفئات", - "categories_placeholder": "مثال: عائلة، أصدقاء، زملاء", - "categories_hint": "اكتب للبحث عن فئات أو إضافتها", - "category_add": "إضافة", - "note": "ملاحظات", - "note_placeholder": "أضف ملاحظة...", - "gender": "الجنس", - "gender_sex": "الجنس البيولوجي", - "gender_male": "ذكر", - "gender_female": "أنثى", - "gender_other": "آخر", - "gender_none": "لا ينطبق", - "gender_unknown": "غير معروف", - "gender_identity": "الهوية الجندرية", - "gender_identity_placeholder": "الهوية الجندرية...", - "calendar": "التقويم", - "calendar_uri": "رابط التقويم", - "scheduling_uri": "رابط الجدولة", - "freebusy_uri": "رابط التوفر/الانشغال", - "context_work": "العمل", - "context_private": "خاص", - "add_email": "إضافة بريد إلكتروني", - "add_phone": "إضافة هاتف", - "save": "حفظ", - "cancel": "إلغاء", - "creating": "جارٍ الإنشاء...", - "updating": "جارٍ التحديث...", - "name_required": "أدخل اسمًا أول أو اسم عائلة أو مؤسسة", - "email_invalid": "يرجى إدخال عنوان بريد إلكتروني صالح", - "email_error_inline": "تنسيق البريد الإلكتروني غير صالح", - "save_failed": "فشل حفظ جهة الاتصال", - "delete": "حذف", - "upload_photo": "رفع صورة", - "remove_photo": "إزالة الصورة", - "photo_hint": "JPG أو PNG، حتى 10 ميغابايت. سيتم تغيير حجمها.", - "photo_too_large": "الصورة كبيرة جدًا (الحد الأقصى 10 ميغابايت)", - "photo_invalid": "ملف صورة غير صالح", - "change_photo": "تغيير" - }, - "groups": { - "create": "مجموعة جديدة", - "edit": "تعديل المجموعة", - "empty": "لا توجد مجموعات بعد", - "delete_confirm_title": "حذف المجموعة", - "delete_confirm": "هل أنت متأكد أنك تريد حذف هذه المجموعة؟", - "name_label": "اسم المجموعة", - "name_placeholder": "مثال: فريق، عائلة", - "name_required": "اسم المجموعة مطلوب", - "save_failed": "فشل حفظ المجموعة", - "members_label": "الأعضاء", - "search_members": "بحث عن جهات اتصال لإضافتها...", - "no_members": "لا يوجد أعضاء في هذه المجموعة", - "member_count": "{count, plural, =0 {لا يوجد أعضاء} one {عضو واحد} other {# أعضاء}}", - "send_email": "إرسال بريد إلكتروني إلى المجموعة", - "send_email_to": "إلى", - "send_email_cc": "نسخة", - "send_email_bcc": "نسخة مخفية", - "no_member_emails": "لا يحتوي هذا الفريق على أعضاء لديهم عنوان بريد إلكتروني." - }, - "import": { - "title": "استيراد جهات الاتصال", - "drop_hint": "انقر لاختيار ملف vCard", - "file_types": "ملفات ‎.vcf أو ‎.vcard", - "no_contacts": "لم يتم العثور على جهات اتصال في الملف", - "parse_error": "فشل تحليل ملف vCard", - "found": "{count, plural, one {تم العثور على جهة اتصال واحدة} other {تم العثور على # جهة اتصال}}", - "duplicate": "مكررة", - "select_all": "تحديد الكل", - "deselect_all": "إلغاء تحديد الكل", - "selected": "{count, plural, one {تم تحديد واحدة} other {تم تحديد #}}", - "import_button": "استيراد", - "importing": "جارٍ الاستيراد...", - "success": "{count, plural, one {تم استيراد جهة اتصال واحدة} other {تم استيراد # جهة اتصال}}", - "failed": "فشل الاستيراد", - "close": "إغلاق", - "file_too_large": "الملف كبير جدًا (الحد الأقصى 5 ميغابايت)" - }, - "export": { - "title": "تصدير جهات الاتصال", - "success": "{count, plural, one {تم تصدير جهة اتصال واحدة} other {تم تصدير # جهة اتصال}}" - }, - "bulk": { - "selected": "{count, plural, one {تم تحديد واحدة} other {تم تحديد #}}", - "select_all": "تحديد الكل", - "delete": "حذف", - "delete_confirm_title": "حذف جهات الاتصال", - "delete_confirm": "حذف {count, plural, one {جهة اتصال واحدة} other {# جهة اتصال}}؟", - "deleted": "{count, plural, one {تم حذف جهة اتصال واحدة} other {تم حذف # جهة اتصال}}", - "add_to_group": "إضافة إلى مجموعة", - "choose_group": "اختر مجموعة", - "adding_contacts": "جارٍ إضافة {count, plural, one {جهة اتصال واحدة} other {# جهة اتصال}}", - "added_to_group": "تمت إضافة جهات الاتصال إلى المجموعة", - "export": "تصدير", - "clear": "إلغاء التحديد" - }, - "toast": { - "created": "تم إنشاء جهة الاتصال", - "updated": "تم تحديث جهة الاتصال", - "deleted": "تم حذف جهة الاتصال", - "error_create": "فشل إنشاء جهة الاتصال", - "error_update": "فشل تحديث جهة الاتصال", - "error_delete": "فشل حذف جهة الاتصال" - }, - "context_menu": { - "open": "فتح", - "edit": "تعديل", - "send_email": "إرسال بريد إلكتروني", - "add_to_group": "إضافة إلى مجموعة", - "export_vcard": "التصدير كـ vCard", - "delete": "حذف", - "call": "اتصال", - "duplicate": "تكرار", - "print": "طباعة" - }, - "filters": { - "toggle": "المرشحات", - "select": "تحديد", - "clear": "مسح", - "close": "إغلاق", - "title": "مرشحات متقدمة", - "organization": "الشركة", - "organization_placeholder": "مثال: شركة أكمي", - "job_title": "المسمى الوظيفي", - "job_title_placeholder": "مثال: مصمم", - "location": "الموقع", - "location_placeholder": "المدينة أو البلد", - "email_domain": "نطاق البريد الإلكتروني", - "email_domain_placeholder": "example.com", - "birthday_month": "عيد الميلاد في", - "any_month": "أي شهر", - "has_email": "لديه بريد إلكتروني", - "has_phone": "لديه هاتف", - "has_photo": "لديه صورة", - "sort_by": "الترتيب حسب", - "sort_first_name": "الاسم الأول", - "sort_last_name": "اسم العائلة" - } - }, - "calendar": { - "title": "التقويم", - "back_to_email": "العودة إلى البريد", - "back_to_month": "العودة إلى الشهر", - "my_calendars": "التقويمات", - "birthday_calendar": "أعياد الميلاد", - "mini_calendar_change": "انقر لتغيير الشهر", - "views": { - "month": "شهر", - "week": "أسبوع", - "day": "يوم", - "agenda": "جدول الأعمال", - "today": "اليوم", - "month_hint": "شهر (m)", - "week_hint": "أسبوع (w)", - "day_hint": "يوم (d)", - "agenda_hint": "جدول الأعمال (a)", - "tasks": "المهام", - "tasks_hint": "المهام (k)" - }, - "events": { - "create": "إنشاء حدث", - "edit": "تعديل الحدث", - "delete": "حذف الحدث", - "details": "تفاصيل الحدث", - "no_events": "لا توجد أحداث", - "all_day": "طوال اليوم", - "more": "+{count} أخرى", - "no_title": "(بلا عنوان)", - "resize": "تغيير حجم الحدث", - "duplicate": "تكرار", - "today_header": "اليوم", - "tomorrow_header": "غدًا", - "agenda_show_earlier": "عرض الأحداث السابقة", - "agenda_loading": "جارٍ التحميل…", - "agenda_range_start": "الأحداث من {date}", - "agenda_range_end": "الأحداث حتى {date}", - "export_ics": "التصدير كملف ‎.ics", - "copy_title": "نسخ العنوان", - "copy_link": "نسخ رابط الاجتماع", - "new_event": "حدث جديد", - "new_all_day_event": "حدث جديد طوال اليوم", - "new_task": "مهمة جديدة", - "go_to_today": "الانتقال إلى اليوم" - }, - "detail": { - "add_note": "أضف ملاحظة...", - "save_note": "حفظ", - "note_saved": "تمت إضافة الملاحظة", - "open_link": "فتح الرابط", - "meeting_link": "رابط الاجتماع", - "tentative": "مبدئي", - "cancelled": "ملغى", - "delete_confirm": "حذف هذا الحدث؟" - }, - "form": { - "title": "العنوان", - "description": "الوصف", - "location": "الموقع", - "meeting_link": "رابط الاجتماع", - "start_date": "تاريخ البدء", - "end_date": "تاريخ الانتهاء", - "start_time": "وقت البدء", - "end_time": "وقت الانتهاء", - "all_day_event": "حدث طوال اليوم", - "calendar_select": "التقويم", - "save": "حفظ", - "cancel": "إلغاء", - "delete_confirm": "هل أنت متأكد أنك تريد حذف هذا الحدث؟", - "color": "اللون" - }, - "participants": { - "availability": { - "title": "التوفر", - "free": "متاح", - "busy": "مشغول", - "tentative": "مبدئي", - "unknown": "ليس مستخدمًا على هذا الخادم", - "checking": "جارٍ التحقق…" - }, - "title": "المشاركون", - "add": "إضافة مشارك", - "organizer": "المنظّم", - "attendee": "حاضر", - "accepted": "مقبول", - "declined": "مرفوض", - "tentative": "مبدئي", - "needs_action": "بحاجة إلى رد", - "remove": "إزالة", - "edit": "تعديل", - "email_placeholder": "أضف عنوان بريد إلكتروني أو ابحث في جهات الاتصال", - "send_invitations": "إرسال الدعوات إلى المشاركين", - "status_summary": "{accepted} قبلوا، {pending} في الانتظار", - "invited_by": "دعوة من {name}", - "respond_below": "أجب باستخدام الأزرار أدناه", - "rsvp_label": "ردّك", - "cancel_notification": "سيتم إعلام المشاركين بالإلغاء", - "you_organizer": "أنت المنظّم", - "you_attendee": "أنت أحد الحاضرين", - "no_participants": "لا يوجد مشاركون", - "count": "{count, plural, one {# مشارك} other {# مشاركين}}" - }, - "recurrence": { - "title": "التكرار", - "none": "لا يتكرر", - "daily": "يوميًا", - "weekly": "أسبوعيًا", - "monthly": "شهريًا", - "yearly": "سنويًا", - "every_n_days": "كل {count} أيام", - "every_n_weeks": "كل {count} أسابيع", - "every_n_months": "كل {count} أشهر", - "until": "حتى", - "occurrences": "{count} مرة", - "custom": "مخصص…", - "edit_custom": "تعديل التكرار المخصص", - "every_n_years": "كل {count} سنوات", - "on_days": "في {days}", - "on_day_n": "في اليوم {day}", - "on_the_nth": "في {nth} {day}", - "in_month": "في {month}", - "nth_1": "الأول", - "nth_2": "الثاني", - "nth_3": "الثالث", - "nth_4": "الرابع", - "nth_last": "الأخير", - "editor_freq_day": "يوم", - "editor_freq_week": "أسبوع", - "editor_freq_month": "شهر", - "editor_freq_year": "سنة", - "editor_repeats_on": "يتكرر في", - "editor_every": "كل", - "editor_unit_days": "يوم/أيام", - "editor_unit_weeks": "أسبوع/أسابيع", - "editor_unit_months": "شهر/أشهر", - "editor_unit_years": "سنة/سنوات", - "editor_on_day": "في اليوم", - "editor_on_the": "في", - "editor_in": "في", - "editor_ends": "ينتهي", - "editor_never": "أبدًا", - "editor_ends_on": "في", - "editor_ends_after": "بعد", - "editor_occurrences": "مرة" - }, - "recurrence_scope": { - "edit_title": "تعديل حدث متكرر", - "delete_title": "حذف حدث متكرر", - "description": "هذا حدث متكرر. ما الأحداث التي تريد تعديلها؟", - "this_event": "هذا الحدث فقط", - "this_and_future": "هذا الحدث والأحداث التالية", - "all_events": "جميع الأحداث", - "cancel": "إلغاء", - "save": "حفظ", - "delete": "حذف", - "rsvp_title": "الرد على حدث متكرر", - "rsvp_description": "هذا حدث متكرر. على أي الأحداث ينطبق ردك؟", - "respond": "رد" - }, - "alerts": { - "title": "التذكيرات", - "none": "لا توجد تذكيرات", - "at_time": "عند وقت الحدث", - "minutes_before": "{count, plural, one {قبل # دقيقة} other {قبل # دقيقة}}", - "hours_before": "{count, plural, one {قبل # ساعة} other {قبل # ساعة}}", - "days_before": "{count, plural, one {قبل # يوم} other {قبل # يوم}}", - "weeks_before": "{count, plural, one {قبل # أسبوع} other {قبل # أسبوع}}", - "unit_minutes_before": "دقائق قبل", - "unit_hours_before": "ساعات قبل", - "unit_days_before": "أيام قبل", - "unit_weeks_before": "أسابيع قبل", - "add": "إضافة تذكير", - "remove": "إزالة التذكير", - "amount": "مقدار التذكير", - "unit": "وحدة التذكير" - }, - "settings": { - "title": "إعدادات التقويم", - "default_view": "العرض الافتراضي", - "week_starts_on": "يبدأ الأسبوع في", - "time_format": "تنسيق الوقت", - "default_calendar": "التقويم الافتراضي", - "default_reminder": "التذكير الافتراضي", - "time_format_12h": "12 ساعة", - "time_format_24h": "24 ساعة", - "notifications_enabled": "إشعارات الأحداث", - "notifications_enabled_desc": "إظهار تنبيهات لأحداث التقويم القادمة", - "notification_sound": "صوت الإشعار", - "notification_sound_desc": "تشغيل صوت لتنبيهات التقويم", - "invitation_parsing": "تحليل دعوات البريد الإلكتروني", - "invitation_parsing_desc": "اكتشاف دعوات التقويم في مرفقات البريد وإظهار إجراءات التقويم", - "show_time_in_month_view": "إظهار الوقت في عرض الشهر", - "show_time_in_month_view_desc": "عرض أوقات الأحداث في عرض التقويم الشهري", - "show_week_numbers": "إظهار أرقام الأسابيع", - "show_week_numbers_desc": "عرض أرقام الأسابيع في التقويم المصغّر", - "calendar_free_scroll": "تمرير حر", - "calendar_free_scroll_desc": "التمرير باستمرار عبر الأشهر والأسابيع والأيام بدلاً من فترة واحدة في كل مرة", - "enable_tasks": "تفعيل المهام", - "enable_tasks_desc": "إظهار عرض للمهام في التقويم لإدارة قوائم المهام", - "show_tasks_on_calendar": "إظهار المهام في التقويم", - "show_tasks_on_calendar_desc": "عرض شارات المهام في عروض التقويم اليومي والأسبوعي", - "hover_preview": "معاينة عند التمرير فوق الحدث", - "hover_preview_desc": "إظهار نافذة تفاصيل منبثقة عند التمرير فوق أحداث التقويم", - "hover_preview_instant": "فوري", - "hover_preview_delay_500ms": "تأخير 0.5 ثانية", - "hover_preview_delay_1s": "تأخير ثانية واحدة", - "hover_preview_delay_2s": "تأخير ثانيتين", - "hover_preview_off": "معطّل", - "organizer_identity": "تنظيم الدعوات باسم", - "organizer_identity_desc": "عنوان التقويم المستخدم كمنظّم عند دعوة الآخرين إلى الأحداث.", - "organizer_identity_failed": "فشل تغيير عنوان المنظّم", - "show_birthday_calendar": "تقويم أعياد ميلاد جهات الاتصال", - "show_birthday_calendar_desc": "إظهار تقويم افتراضي بأعياد ميلاد جهات اتصالك" - }, - "days": { - "monday": "الاثنين", - "tuesday": "الثلاثاء", - "wednesday": "الأربعاء", - "thursday": "الخميس", - "friday": "الجمعة", - "saturday": "السبت", - "sunday": "الأحد", - "mon": "اثنين", - "tue": "ثلاثاء", - "wed": "أربعاء", - "thu": "خميس", - "fri": "جمعة", - "sat": "سبت", - "sun": "أحد" - }, - "months": { - "jan": "يناير", - "feb": "فبراير", - "mar": "مارس", - "apr": "أبريل", - "may": "مايو", - "jun": "يونيو", - "jul": "يوليو", - "aug": "أغسطس", - "sep": "سبتمبر", - "oct": "أكتوبر", - "nov": "نوفمبر", - "dec": "ديسمبر", - "far": "فروردين", - "ord": "أرديبهشت", - "kho": "خرداد", - "tir": "تير", - "mor": "مرداد", - "sha": "شهریور", - "meh": "مهر", - "aba": "آبان", - "aza": "آذر", - "dey": "دی", - "bah": "بهمن", - "esf": "اسفند" - }, - "notifications": { - "event_created": "تم إنشاء الحدث", - "event_updated": "تم تحديث الحدث", - "event_deleted": "تم حذف الحدث", - "calendar_created": "تم إنشاء التقويم", - "calendar_deleted": "تم حذف التقويم", - "event_move_error": "فشل نقل الحدث", - "event_resize_error": "فشل تغيير حجم الحدث", - "alert_title": "حدث قادم", - "alert_now": "يبدأ الآن", - "alert_in_minutes": "خلال {count} دقيقة", - "invitation_sent": "تم إرسال الدعوات", - "invitations_denied_title": "تعذر إرسال الدعوات", - "invitations_denied": "رفض الخادم إرسال الدعوات: {reason}. هل تريد حفظ الحدث دون إرسالها؟", - "save_without_invitations": "حفظ دون دعوات", - "rsvp_updated": "تم تحديث الرد", - "rsvp_error": "فشل تحديث الرد", - "event_duplicated": "تم تكرار الحدث", - "event_error": "فشل حفظ الحدث", - "task_due": "موعد استحقاق المهمة", - "event_exported": "تم تصدير الحدث", - "title_copied": "تم نسخ العنوان", - "link_copied": "تم نسخ الرابط" - }, - "status": { - "loading_calendars": "جارٍ تحميل التقويمات...", - "loading_events": "جارٍ تحميل الأحداث..." - }, - "quick_create": { - "placeholder": "عنوان الحدث الجديد", - "aria_label": "إنشاء حدث سريع" - }, - "nav_prev": "السابق", - "nav_next": "التالي", - "nav_open_menu": "فتح القائمة", - "import": { - "title": "استيراد تقويم", - "tab_file": "ملف", - "tab_url": "رابط", - "select_file": "اختر ملف ‎.ics", - "drop_file": "أو أفلت الملف هنا", - "supported_formats": "يدعم ملفات iCalendar (‎.ics)", - "url_description": "أدخل رابط تغذية iCalendar (‎.ics) خارجية لاستيراد الأحداث.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "يدعم روابط iCalendar (‎.ics)", - "fetch": "جلب", - "invalid_url": "يرجى إدخال رابط صالح", - "url_fetch_failed": "فشل جلب التقويم من الرابط", - "parsing": "جارٍ تحليل ملف التقويم...", - "parsed_events": "تم العثور على {count} حدث", - "no_events": "لم يتم العثور على أحداث في الملف", - "select_all": "تحديد الكل", - "deselect_all": "إلغاء تحديد الكل", - "target_calendar": "الاستيراد إلى التقويم", - "import_button": "استيراد المحدد", - "importing": "جارٍ استيراد الأحداث...", - "success": "تم استيراد {count} حدث بنجاح", - "error": "فشل استيراد التقويم", - "file_too_large": "الملف يتجاوز الحد الأقصى 10 ميغابايت", - "invalid_format": "تنسيق ملف التقويم غير صالح" - }, - "webcal_action": { - "title": "فتح رابط التقويم", - "description": "كيف تريد استخدام \"{name}\"؟", - "import_title": "استيراد لمرة واحدة", - "import_description": "جلب الأحداث الآن ونسخها إلى أحد تقويماتك.", - "subscribe_title": "اشتراك", - "subscribe_description": "إبقاء هذا التقويم متزامنًا تلقائيًا كتقويم منفصل.", - "cancel": "إلغاء" - }, - "management": { - "title": "إدارة التقويمات", - "description": "أنشئ تقويماتك وأعد تسميتها وخصّصها. انقر بالزر الأيمن على تقويم في الشريط الجانبي لتغيير لونه بسرعة.", - "name": "الاسم", - "name_placeholder": "اسم التقويم", - "color": "اللون", - "change_color": "تغيير اللون", - "random_color": "لون عشوائي جديد", - "add_calendar": "إضافة تقويم", - "edit": "تعديل", - "delete": "حذف", - "save": "حفظ", - "create": "إنشاء", - "cancel": "إلغاء", - "default": "افتراضي", - "confirm_delete": "حذف \"{name}\"؟ ستتم إزالة جميع الأحداث في هذا التقويم.", - "confirm_clear": "مسح جميع الأحداث من \"{name}\"؟ لا يمكن التراجع عن هذا.", - "set_default": "تعيين كافتراضي", - "default_updated": "تم تحديث التقويم الافتراضي", - "error_default": "فشل تعيين التقويم الافتراضي", - "clear_events": "مسح الأحداث", - "events_cleared": "تم مسح {count} حدث", - "error_clear": "فشل مسح أحداث التقويم", - "calendar_created": "تم إنشاء التقويم", - "calendar_updated": "تم تحديث التقويم", - "calendar_deleted": "تم حذف التقويم", - "color_updated": "تم تحديث لون التقويم", - "error_create": "فشل إنشاء التقويم", - "error_update": "فشل تحديث التقويم", - "error_delete": "فشل حذف التقويم", - "caldav_url": "رابط CalDAV", - "copy_url": "نسخ رابط CalDAV", - "url_copied": "تم نسخ رابط CalDAV إلى الحافظة", - "share": "مشاركة التقويم", - "new_event_in_calendar": "حدث جديد في هذا التقويم", - "kind": "المحتوى", - "kind_events": "الأحداث", - "kind_tasks": "المهام", - "kind_both": "الأحداث والمهام", - "kind_hint": "يحدد التطبيقات التي ترى هذا التقويم عند مزامنته عبر CalDAV. لا يمكن تغييره لاحقًا." - }, - "subscription": { - "title": "اشتراك iCal", - "section_title": "اشتراكات iCal", - "description": "اشترك في تغذية iCalendar خارجية. ستتم مزامنة الأحداث تلقائيًا في تقويمها الخاص. يدعم روابط https:// وwebcal://.", - "url_label": "رابط التقويم", - "url_placeholder": "https://example.com/calendar.ics أو webcal://...", - "name_label": "اسم التقويم", - "name_placeholder": "مثال: العطلات الرسمية", - "color_label": "اللون", - "refresh_interval": "فترة التحديث", - "interval_15": "كل 15 دقيقة", - "interval_30": "كل 30 دقيقة", - "interval_60": "كل ساعة", - "interval_360": "كل 6 ساعات", - "interval_1440": "كل يوم", - "subscribe": "اشتراك", - "subscribing": "جارٍ الاشتراك...", - "save": "حفظ التغييرات", - "saving": "جارٍ الحفظ...", - "edit": "تعديل", - "edit_title": "تعديل الاشتراك", - "updated": "تم تحديث \"{name}\"", - "update_error": "فشل تحديث الاشتراك", - "invalid_url": "يرجى إدخال رابط صالح", - "success": "تم الاشتراك في \"{name}\"", - "error": "فشلت إضافة الاشتراك", - "refresh": "تحديث الآن", - "refresh_success": "تم تحديث الاشتراك", - "refresh_error": "فشل تحديث الاشتراك", - "unsubscribe": "إلغاء الاشتراك", - "confirm_delete": "إلغاء الاشتراك من \"{name}\"؟ ستتم إزالة التقويم وجميع أحداثه.", - "deleted": "تمت إزالة الاشتراك", - "delete_error": "فشلت إزالة الاشتراك", - "last_refreshed": "آخر تحديث: {time}" - }, - "tasks": { - "label": "المهام", - "no_tasks": "لا توجد مهام", - "no_title": "(بلا عنوان)", - "mark_complete": "تمييز كمكتملة", - "mark_incomplete": "تمييز كغير مكتملة", - "filter_all": "الكل", - "filter_pending": "معلّقة", - "filter_completed": "مكتملة", - "filter_overdue": "متأخرة", - "show_completed": "إظهار المكتملة", - "create": "مهمة جديدة", - "edit": "تعديل المهمة", - "title_placeholder": "عنوان المهمة", - "description_placeholder": "أضف وصفًا...", - "due_date": "تاريخ الاستحقاق", - "include_time": "تضمين الوقت", - "priority": "الأولوية", - "priority_none": "بلا", - "priority_high": "مرتفعة", - "priority_medium": "متوسطة", - "priority_low": "منخفضة", - "progress": "الحالة", - "progress_needs_action": "بحاجة إلى إجراء", - "progress_in_process": "قيد التنفيذ", - "progress_completed": "مكتملة", - "progress_cancelled": "ملغاة", - "calendar": "التقويم", - "alert": "تذكير", - "alert_none": "بلا", - "alert_at_time": "عند وقت الاستحقاق", - "alert_5min": "قبل 5 دقائق", - "alert_15min": "قبل 15 دقيقة", - "alert_30min": "قبل 30 دقيقة", - "alert_1hr": "قبل ساعة", - "alert_1day": "قبل يوم", - "delete": "حذف", - "cancel": "إلغاء", - "save": "حفظ", - "quick_add_placeholder": "أضف مهمة...", - "due_today": "اليوم", - "due_tomorrow": "غدًا", - "overdue": "متأخرة" - } - }, - "calendar_event_notifications": { - "someone": "شخص ما", - "invited": "دعاك {name} إلى \"{title}\"", - "updated": "حدّث {name} \"{title}\"", - "cancelled": "ألغى {name} \"{title}\"", - "untitled": "حدث بدون عنوان" - }, - "share_notifications": { - "someone": "شخص ما", - "shared": "شارك {name} معك {kind} \"{object}\"", - "changed": "غيّر {name} صلاحيتك على {kind} \"{object}\"", - "revoked": "أزال {name} صلاحيتك على {kind} \"{object}\"", - "object": { - "folder": "المجلد", - "calendar": "التقويم", - "address_book": "دفتر العناوين", - "files": "مجلد الملفات" - } - }, - "sharing": { - "title": "مشاركة \"{name}\"", - "description": "امنح الوصول لمستخدمين أو مجموعات أخرى على هذا الخادم. تُطبَّق التغييرات فورًا.", - "no_shares": "لم تتم المشاركة مع أي شخص بعد.", - "add_person": "إضافة شخص أو مجموعة", - "search_placeholder": "بحث بالاسم أو البريد الإلكتروني…", - "loading_principals": "جارٍ تحميل المستخدمين…", - "no_principals": "لم يتم العثور على مستخدمين أو مجموعات أخرى.", - "no_match": "لا توجد نتائج مطابقة.", - "remove": "إزالة الوصول", - "group": "مجموعة", - "share_added": "تم منح الوصول", - "share_updated": "تم تحديث الوصول", - "share_removed": "تمت إزالة الوصول", - "share_failed": "فشل تحديث المشاركة", - "preset": { - "freeBusy": "التوفر/الانشغال فقط", - "read": "قراءة فقط", - "readWrite": "قراءة وكتابة", - "manager": "مدير", - "custom": "مخصص" - } - }, - "advanced_search": { - "title": "بحث متقدم", - "from": "من", - "from_placeholder": "بريد المرسل أو اسمه", - "to": "إلى", - "to_placeholder": "بريد المستلم أو اسمه", - "subject": "الموضوع", - "subject_placeholder": "يحتوي الموضوع على...", - "body": "النص", - "body_placeholder": "يحتوي النص على...", - "folder": "المجلد", - "all_folders": "جميع المجلدات", - "has_attachment": "المرفقات", - "date_after": "بعد", - "date_before": "قبل", - "size_min": "أكبر من (كيلوبايت)", - "size_max": "أصغر من (كيلوبايت)", - "starred": "مميز بنجمة", - "unread": "غير مقروءة", - "read": "مقروءة", - "yes": "نعم", - "no": "لا", - "clear": "مسح", - "clear_all": "مسح الكل", - "filters_active": "{count} مرشح", - "filters_active_plural": "{count} مرشحات", - "toggle_filters": "المزيد", - "search_hint": "استخدم المرشحات المتقدمة لبحث دقيق", - "advanced_filters_tooltip": "مرشحات البحث المتقدم", - "results_found": "{count, plural, =0 {لم يتم العثور على نتائج} one {تم العثور على # نتيجة} other {تم العثور على # نتيجة}}", - "results_found_more": "تم العثور على {count}+ نتيجة", - "suggestions_recent": "عمليات البحث الأخيرة", - "suggestions_people": "الأشخاص", - "suggestions_from": "البحث في البريد من {address}", - "suggestions_to": "البحث في البريد إلى {address}", - "suggestions_remove_recent": "إزالة من عمليات البحث الأخيرة" - }, - "welcome": { - "title": "مرحبًا بك في صندوق بريدك", - "tip_compose": "اضغط c لإنشاء رسالة جديدة", - "tip_shortcuts": "اضغط ؟ لعرض جميع اختصارات لوحة المفاتيح", - "tip_sidebar": "ستجد التقويم وجهات الاتصال والإعدادات في قائمة الشريط الجانبي", - "tip_settings": "خصّص تجربتك من الإعدادات", - "got_it": "فهمت", - "settings": "الإعدادات", - "dismiss": "تجاهل", - "start_tour": "بدء الجولة" - }, - "demo_welcome": { - "title": "مرحبًا بك في بريد Bulwark", - "description": "استكشف عميل بريد ويب متكامل الميزات - مباشرة في متصفحك. تبقى جميع البيانات على جهازك، فلا تتردد في تجربة كل شيء.", - "feature_email": "قراءة الرسائل وكتابتها", - "feature_organize": "الوسوم والنجوم والمجلدات", - "feature_shortcuts": "اختصارات لوحة المفاتيح", - "feature_privacy": "نسخة تجريبية خاصة 100%", - "hint": "انقر على أي رسالة على اليسار للبدء، أو خذ الجولة أدناه." - }, - "files": { - "office_edit": "تحرير المستند", - "office_loading": "جارٍ فتح محرر المستندات…", - "office_error": "تعذر فتح محرر المستندات.", - "office_close": "إغلاق المحرر", - "open_in_new_tab": "فتح في علامة تبويب جديدة", - "title": "الملفات", - "search_placeholder": "بحث في الملفات...", - "empty_state_title": "لا توجد ملفات بعد", - "empty_state_description": "ارفع ملفات أو أنشئ مجلدات للبدء", - "upload": "رفع", - "upload_files": "رفع الملفات", - "new_folder": "مجلد جديد", - "new_folder_name": "اسم المجلد", - "rename": "إعادة تسمية", - "rename_title": "إعادة تسمية", - "new_name": "الاسم الجديد", - "name_forbidden_chars": "لا يسمح الخادم بهذه الأحرف في الأسماء: {chars}", - "name_reserved": "هذا الاسم محجوز على الخادم.", - "delete": "حذف", - "delete_confirm_title": "حذف المورد", - "delete_confirm_message": "هل أنت متأكد أنك تريد حذف \"{name}\"؟ لا يمكن التراجع عن هذا.", - "download": "تنزيل", - "name": "الاسم", - "size": "الحجم", - "modified": "تاريخ التعديل", - "type": "النوع", - "folder": "مجلد", - "file": "ملف", - "parent_directory": "المجلد الأصل", - "breadcrumb_root": "الرئيسية", - "other_accounts": "حسابات أخرى", - "no_accounts": "لا توجد حسابات متصلة.", - "drop_files_here": "أفلت الملفات أو المجلدات هنا للرفع", - "uploading": "جارٍ الرفع...", - "upload_success": "{count, plural, one {تم رفع ملف واحد} other {تم رفع # ملف}}", - "upload_error": "فشل رفع الملف", - "create_folder_success": "تم إنشاء المجلد", - "create_folder_error": "فشل إنشاء المجلد", - "delete_success": "تم الحذف بنجاح", - "delete_error": "فشل الحذف", - "rename_success": "تمت إعادة التسمية بنجاح", - "rename_error": "فشلت إعادة التسمية", - "download_error": "فشل التنزيل", - "not_available": "تخزين الملفات غير متاح على هذا الخادم", - "cancel": "إلغاء", - "create": "إنشاء", - "save": "حفظ", - "no_results": "لا توجد ملفات مطابقة لبحثك", - "batch_delete_confirm_message": "هل أنت متأكد أنك تريد حذف {count, plural, one {عنصر واحد} other {# عنصر}}؟ لا يمكن التراجع عن هذا.", - "batch_delete_success": "{count, plural, one {تم حذف عنصر واحد} other {تم حذف # عنصر}}", - "grid_view": "عرض شبكي", - "list_view": "عرض قائمة", - "details": "التفاصيل", - "path": "المسار", - "preview": "معاينة", - "preview_error": "فشل تحميل المعاينة", - "cut": "قص", - "copy": "نسخ", - "paste": "لصق", - "move_success": "{count, plural, one {تم نقل عنصر واحد} other {تم نقل # عنصر}}", - "move_error": "فشل النقل", - "paste_success": "تم اللصق بنجاح", - "paste_error": "فشل اللصق", - "new_text_file": "ملف نصي جديد", - "file_name": "اسم الملف", - "retry": "إعادة المحاولة", - "refresh": "تحديث", - "toggle_favorite": "تبديل المفضّلة", - "duplicate": "تكرار", - "duplicate_success": "تم التكرار بنجاح", - "duplicate_error": "فشل التكرار", - "share": "مشاركة", - "shared": "مشترك", - "shared_with_me": "مشترك معي", - "shared_by": "تمت المشاركة بواسطة {name}", - "create_file_success": "تم إنشاء الملف", - "create_file_error": "فشل إنشاء الملف", - "favorites": "المفضّلة", - "recent": "الأخيرة", - "properties": "الخصائص", - "open_folder": "فتح المجلد", - "upload_folder": "رفع مجلد", - "file_too_large": "\"{name}\" يتجاوز الحد الأقصى لحجم الملف ({max})", - "undo": "تراجع", - "undo_success": "تم التراجع عن الإجراء", - "undo_error": "فشل التراجع", - "toolbar": "إجراءات الملفات", - "open_folder_tree": "فتح شجرة المجلدات", - "file_list": "الملفات والمجلدات", - "context_menu": "الإجراءات", - "settings_title": "إعدادات الملفات", - "settings_display": "العرض", - "settings_default_view": "العرض الافتراضي", - "settings_default_view_desc": "اختر بين تخطيط الشبكة والقائمة", - "settings_default_sort": "الترتيب الافتراضي", - "settings_default_sort_desc": "اختر الترتيب الافتراضي للملفات", - "settings_sort_direction": "اتجاه الترتيب", - "settings_sort_direction_desc": "اختر ترتيبًا تصاعديًا أو تنازليًا", - "settings_ascending": "تصاعدي", - "settings_descending": "تنازلي", - "settings_icons": "الأيقونات", - "settings_show_icons": "إظهار أيقونات الملفات", - "settings_show_icons_desc": "عرض أيقونات بجانب الملفات والمجلدات", - "settings_colored_icons": "أيقونات ملونة", - "settings_colored_icons_desc": "استخدام أيقونات ملونة بدلًا من أحادية اللون", - "settings_show_thumbnails": "إظهار الصور المصغّرة", - "settings_show_thumbnails_desc": "عرض معاينات الصور بدلًا من الأيقونات لملفات الصور", - "settings_behavior": "السلوك", - "settings_show_hidden": "إظهار الملفات المخفية", - "settings_show_hidden_desc": "عرض الملفات والمجلدات التي تبدأ بنقطة", - "settings_folder_layout": "التنقل بين المجلدات", - "settings_folder_layout_desc": "اختر كيفية عرض المجلدات: ضمن الملفات أو في شجرة بالشريط الجانبي", - "settings_folder_layout_inline": "ضمن الصفحة", - "settings_folder_layout_sidebar": "الشريط الجانبي", - "disabled_title": "ميزة الملفات معطّلة بواسطة المسؤول", - "disabled_description": "قد تتسبب عمليات رفع الملفات الكبيرة عبر WebDAV في زعزعة استقرار Stalwart/RocksDB، بما في ذلك انهيارات نفاد الذاكرة واستخدام غير قابل للاسترجاع لمساحة القرص. قد لا تُحذف الملفات المحذوفة فورًا من مخزن الكائنات الثنائية. لا يُنصح بهذه الميزة لبيئات الإنتاج.", - "stability_warning": "قد تتسبب عمليات رفع الملفات الكبيرة في زعزعة استقرار الخادم. قد لا تُحذف الملفات المحذوفة فورًا من التخزين. استخدمها بحذر.", - "migration_title": "جارٍ تحديث ملفاتك…", - "migration_description": "جارٍ تنظيم المجلدات والملفات في هيكلها الصحيح. يحدث هذا مرة واحدة فقط." - }, - "smime": { - "your_certificates": "شهاداتك", - "your_certificates_desc": "استيراد وإدارة شهادات S/MIME الخاصة بك لتوقيع الرسائل وتشفيرها", - "recipient_certificates": "شهادات المستلمين", - "recipient_certificates_desc": "الشهادات العامة لتشفير الرسائل إلى المستلمين", - "identity_bindings": "ربط الهوية بالمفتاح", - "identity_bindings_desc": "ربط شهادات S/MIME بهويات بريدك الإلكتروني", - "defaults_title": "الإعدادات الافتراضية", - "defaults_desc": "إعداد سلوك التوقيع والتشفير الافتراضي", - "import_pkcs12": "استيراد PKCS#12 (‎.p12/‎.pfx)", - "import_public_cert": "استيراد شهادة", - "no_certificates": "لم تُستورد أي شهادات بعد", - "no_recipient_certs": "لا توجد شهادات مستلمين", - "expires": "تنتهي في", - "expired": "منتهية الصلاحية", - "bound_to": "مرتبطة بـ", - "no_key_bound": "بلا", - "lock": "قفل المفتاح", - "unlock": "فتح المفتاح", - "details": "عرض التفاصيل", - "delete": "حذف", - "encrypt_by_default": "التشفير افتراضيًا", - "encrypt_by_default_desc": "تشفير الرسائل تلقائيًا عندما تتوفر شهادات لجميع المستلمين", - "remember_unlocked": "تذكّر المفاتيح المفتوحة", - "remember_unlocked_desc": "إبقاء المفاتيح مفتوحة طوال مدة جلسة المتصفح هذه", - "sign_default_for": "التوقيع افتراضيًا لـ", - "enter_p12_passphrase": "أدخل عبارة مرور PKCS#12", - "p12_passphrase_desc": "أدخل عبارة المرور التي تحمي ملف الشهادة هذا", - "enter_storage_passphrase": "تعيين عبارة مرور التخزين", - "storage_passphrase_desc": "اختر عبارة مرور لحماية هذا المفتاح أثناء التخزين في متصفحك", - "next": "التالي", - "import": "استيراد", - "unlock_key": "فتح المفتاح", - "unlock_key_desc": "أدخل عبارة مرور التخزين لفتح هذا المفتاح للتوقيع أو فك التشفير", - "passphrase_placeholder": "أدخل عبارة المرور", - "confirm_passphrase_placeholder": "تأكيد عبارة المرور", - "passphrase_mismatch": "عبارتا المرور غير متطابقتين", - "cancel": "إلغاء", - "processing": "جارٍ المعالجة…", - "close": "إغلاق", - "certificate_details": "تفاصيل الشهادة", - "cert_subject": "الموضوع", - "cert_issuer": "الجهة المُصدِرة", - "cert_email": "البريد الإلكتروني", - "cert_serial": "الرقم التسلسلي", - "cert_validity": "الصلاحية", - "cert_fingerprint": "البصمة (SHA-256)", - "cert_algorithm": "الخوارزمية", - "cert_capabilities": "القدرات", - "cert_source": "المصدر", - "cert_expired": "انتهت صلاحية هذه الشهادة", - "cert_not_yet_valid": "هذه الشهادة غير سارية بعد", - "cap_sign": "التوقيع", - "cap_encrypt": "التشفير", - "cap_none": "بلا", - "show_passphrase": "إظهار عبارة المرور", - "hide_passphrase": "إخفاء عبارة المرور", - "sign_toggle": "توقيع", - "encrypt_toggle": "تشفير", - "missing_recipient_certs": "شهادات مفقودة لـ: {emails}", - "missing_sender_cert": "لا توجد شهادة مرتبطة بهذه الهوية", - "status_encrypted_ok": "تم تشفير هذه الرسالة", - "status_encrypted_no_key": "هذه الرسالة مشفّرة لكن لم يتم العثور على مفتاح مطابق", - "status_encrypted_failed": "فشل فك تشفير هذه الرسالة", - "status_signed_valid": "تم التحقق من التوقيع", - "status_signed_invalid": "فشل التحقق من التوقيع", - "status_signed_expired_cert": "موقّعة بشهادة منتهية الصلاحية", - "status_signed_self_signed": "التوقيع صالح، لكن الشهادة موقّعة ذاتيًا (غير موثوقة)", - "status_signed_mismatch": "التوقيع صالح، لكن الموقّع لا يطابق المرسل", - "status_unsupported": "تنسيق S/MIME غير مدعوم", - "auto_import_signer_certs": "استيراد شهادات الموقّعين تلقائيًا", - "auto_import_signer_certs_desc": "حفظ الشهادات تلقائيًا من الرسائل الموقّعة الموثّقة للاستخدام في التشفير مستقبلًا", - "export": "تصدير", - "enter_export_passphrase": "تعيين عبارة مرور التصدير", - "export_passphrase_desc": "اختر عبارة مرور لحماية ملف PKCS#12 المُصدَّر", - "export_storage_desc": "أدخل عبارة مرور التخزين لفك تشفير المفتاح من أجل التصدير", - "incorrect_passphrase": "عبارة المرور غير صحيحة" - }, - "tour": { - "step_counter": "الخطوة {current} من {total}", - "skip": "تخطي الجولة", - "back": "السابق", - "next": "التالي", - "finish": "إنهاء", - "take_a_tour": "خذ جولة في الواجهة", - "restart_title": "الجولة التعريفية", - "restart_desc": "إعادة عرض الجولة التعريفية للواجهة", - "restart_button": "إعادة تشغيل الجولة", - "show_on_new_devices_title": "الإظهار على الأجهزة الجديدة", - "show_on_new_devices_desc": "إعادة عرض شعار الترحيب والجولة عند أول تسجيل دخول على جهاز جديد، حتى لو أكملتها بالفعل في مكان آخر", - "sidebar_title": "صناديق بريدك", - "sidebar_desc": "هذا هو الشريط الجانبي لمجلداتك. انقر على أي صندوق بريد لعرض رسائله. يمكنك إنشاء مجلدات، وسحب الرسائل بينها، ورؤية عدد غير المقروء بلمحة.", - "compose_title": "إنشاء رسالة", - "compose_desc": "انقر هنا لكتابة رسالة جديدة. يمكنك إضافة مستلمين ومرفقات واستخدام التنسيق النصي المنسّق.", - "search_title": "البحث في بريدك", - "search_desc": "ابحث حسب المرسل أو الموضوع أو المحتوى. انقر على أيقونة المرشح لخيارات متقدمة مثل نطاق التاريخ والمرفقات والرسائل المميزة بنجمة.", - "email_list_title": "قائمة رسائلك", - "email_list_desc": "تظهر الرسائل هنا. انقر على واحدة لقراءتها على اليمين. استخدم مربع الاختيار لتحديد عدة رسائل، ثم انقلها أو احذفها أو ضع لها وسمًا دفعة واحدة.", - "email_viewer_title": "جزء القراءة", - "email_viewer_desc": "تُفتح الرسالة المحددة هنا. الرد أو إعادة التوجيه أو الأرشفة أو الحذف باستخدام أزرار شريط الأدوات. يمكنك أيضًا تمييز الرسائل بنجمة أو إضافة وسوم ملونة.", - "keywords_title": "الوسوم الملونة", - "keywords_desc": "نظّم بريدك بوسوم ملونة. اسحب رسالة إلى وسم لتصنيفها، أو انقر بالزر الأيمن على رسالة لتعيين وسوم لها.", - "calendar_title": "التقويم", - "calendar_desc": "انتقل إلى التقويم لإدارة أحداثك. أنشئ الأحداث واضبط التذكيرات واستعرض تخطيطات اليوم أو الأسبوع أو الشهر.", - "contacts_title": "جهات الاتصال", - "contacts_desc": "دفتر عناوينك موجود هنا. استورد جهات الاتصال، وأنشئ مجموعات، وانقر على أي جهة اتصال لرؤية تفاصيلها الكاملة.", - "settings_title": "الإعدادات", - "settings_desc": "خصّص كل شيء: السمة، والكثافة، والتوقيعات، والمرشحات، واختصارات لوحة المفاتيح، وإعدادات التقويم الافتراضية، والمزيد.", - "shortcuts_title": "اختصارات لوحة المفاتيح", - "shortcuts_desc": "يحب المستخدمون المتقدمون هذا. اضغط ؟ في أي وقت لرؤية جميع الاختصارات المتاحة. يمكنك التنقل والكتابة وإدارة الرسائل دون لمس الفأرة.", - "compose_open_title": "محرر الرسائل", - "compose_open_desc": "هذا هو محرر الرسائل. أضف مستلمين، اكتب رسالتك، أرفق ملفات، واستخدم التنسيق النصي المنسّق. يمكنك أيضًا حفظ المسودات واستخدام القوالب.", - "calendar_view_title": "تقويمك", - "calendar_view_desc": "هذا تقويمك مع أحداث نموذجية. يمكنك التبديل بين عروض اليوم والأسبوع والشهر وجدول الأعمال باستخدام شريط الأدوات.", - "create_event_title": "إنشاء حدث", - "create_event_desc": "انقر على هذا الزر لإنشاء حدث تقويم جديد. يمكنك تعيين عنوان وتاريخ ووقت وإضافة مشاركين.", - "event_modal_title": "تفاصيل الحدث", - "event_modal_desc": "هذا نموذج الحدث. املأ العنوان، واختر تاريخًا ووقتًا، وأضف موقعًا أو مشاركين. اضغط حفظ عند الانتهاء - أو أغلقه وتابع.", - "contacts_list_title": "جهات اتصالك", - "contacts_list_desc": "هذه جهات اتصالك. انقر على أي جهة اتصال لرؤية تفاصيلها الكاملة على اليمين. يمكنك أيضًا إنشاء جهات اتصال جديدة، أو استيراد بطاقات vCard، أو تنظيم جهات الاتصال في مجموعات.", - "settings_tabs_title": "قائمة الإعدادات", - "settings_tabs_desc": "هذه كل فئات الإعدادات. خصّص مظهرك، وأدر الهويات، وأعدّ مرشحات البريد، واضبط تقويمك، وأكثر من ذلك بكثير.", - "files_title": "تخزين الملفات", - "files_desc": "يتيح لك متصفح الملفات رفع الملفات وتنظيمها ومشاركتها - كمحرك سحابي شخصي مدمج في بريدك.", - "demo_banner_title": "أدوات التحكم التجريبية", - "demo_banner_desc": "أنت في الوضع التجريبي - يبقى كل شيء في متصفحك. اضغط \"إعادة تعيين التجربة\" في أي وقت للبدء من جديد ببيانات نموذجية نظيفة.", - "quota_title": "استخدام التخزين", - "quota_desc": "تتبّع حجم صندوق بريدك هنا. تمتلئ الدائرة كلما استخدمت مساحة أكبر." - }, - "unified_mailbox": { - "search_unavailable": "البحث غير متاح في العرض الموحّد" - }, - "quote_header": { - "reply_line": "في {date}، كتب {from}:", - "forwarded_separator": "---------- رسالة موجّهة ----------", - "from_label": "من", - "date_label": "التاريخ", - "subject_label": "الموضوع" - }, - "pwa_install": { - "title": "تثبيت {appName}", - "description": "ثبّت تطبيقنا للوصول السريع والدعم دون اتصال.", - "not_now": "ليس الآن", - "install": "تثبيت", - "dont_remind": "عدم التذكير مرة أخرى", - "dismiss_aria": "تجاهل مطالبة التثبيت" - }, - "deep_link": { - "message_not_found": "لم تعد هذه الرسالة متاحة.", - "thread_not_found": "لم تعد هذه المحادثة متاحة.", - "folder_not_found": "لم يعد هذا المجلد متاحًا.", - "event_not_found": "لم يعد هذا الحدث متاحًا.", - "file_not_found": "لم يعد هذا الملف متاحًا.", - "account_unavailable": "سجّل الدخول إلى ذلك الحساب لفتح هذا الرابط.", - "copy_message": "نسخ رابط الرسالة", - "copy_conversation": "نسخ رابط المحادثة", - "copy_event": "نسخ رابط الحدث", - "copied": "تم نسخ الرابط", - "copy_failed": "تعذّر نسخ الرابط" - }, - "plugins": { - "oauth_callback": { - "completing": "جارٍ إكمال التفويض…", - "completing_message": "يرجى الانتظار بينما يتم إنشاء الاتصال.", - "received_title": "تم استلام التفويض", - "received_message": "يمكنك العودة إلى علامة التبويب التي بدأت فيها الاتصال.", - "failed_title": "فشل التفويض", - "denied": "تم رفض التفويض.", - "failed_detail": "فشل التفويض: {error}", - "missing_params": "رمز التفويض أو الحالة مفقود.", - "delivery_failed": "تعذّر إرسال نتيجة التفويض إلى تبويب التطبيق. يرجى إغلاق هذه النافذة والمحاولة مرة أخرى." - } - }, - "global_search": { - "title": "البحث في كل شيء", - "placeholder": "ابحث في البريد وجهات الاتصال والتقويم والملفات...", - "scope_label": "البحث في", - "account_label": "الحساب", - "scope_all": "الكل", - "scope_mail": "البريد", - "scope_contacts": "جهات الاتصال", - "scope_calendar": "التقويم", - "scope_files": "الملفات", - "all_accounts": "جميع الحسابات", - "show_all": "عرض كل النتائج", - "no_results": "لا توجد نتائج لـ “{query}”", - "server_hint": "الكلمات الكاملة تبحث في الخادم؛ أجزاء الكلمات تطابق العناصر المحمّلة بالفعل", - "searching": "جارٍ البحث...", - "account_error": "تعذر البحث في {account}", - "recent_searches": "عمليات البحث الأخيرة", - "recurring": "متكرر", - "no_subject": "(no subject)", - "empty_hint": "ابحث في البريد وجهات الاتصال والأحداث والملفات في جميع الحسابات. جرّب from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "مسطح حسب التاريخ", - "grouped": "مجمّع", - "load_more": "تحميل المزيد", - "preview_empty": "اختر نتيجة لمعاينتها", - "open_full": "فتح", - "view_label": "العرض" - } -} +{ + "meta_description": "عميل بريد إلكتروني ويب بسيط يستخدم بروتوكول JMAP", + "login": { + "title": "البريد الإلكتروني", + "username_label": "البريد الإلكتروني", + "username_placeholder": "user@example.com", + "password_label": "كلمة المرور", + "password_placeholder": "أدخل كلمة المرور", + "jmap_endpoint_label": "خادم JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "يجب أن يسمح الخادم بطلبات CORS من هذا النطاق.", + "jmap_server_label": "الخادم", + "jmap_server_auto_picked": "تم اختيار الخادم بناءً على نطاق بريدك الإلكتروني.", + "sign_in": "تسجيل الدخول", + "signing_in": "جارٍ تسجيل الدخول...", + "loading": "جارٍ التحميل...", + "reconnecting": "انقطع الاتصال. جارٍ محاولة إعادة الاتصال…", + "error": { + "invalid_credentials": "البريد الإلكتروني أو كلمة المرور غير صحيحة. يرجى التحقق من بياناتك والمحاولة مرة أخرى.", + "invalid_token": "لم يقبل الخادم رمز الوصول هذا. تحقق من أنه كامل وأن صلاحيته لم تنتهِ ولم يُلغَ.", + "connection_failed": "تعذّر الوصول إلى الخادم. تحقق من اتصالك بالإنترنت وحاول مرة أخرى.", + "cors_blocked": "الخادم متاح لكنه يحظر الطلبات من مصادر مختلفة (CORS). تحقق من إعدادات CORS في خادم JMAP واسمح بهذا النطاق.", + "server_error": "الخادم غير متاح مؤقتًا. يرجى المحاولة مرة أخرى لاحقًا.", + "generic": "حدث خطأ غير متوقع. إذا استمرت المشكلة، تواصل مع المسؤول.", + "totp_required": "مطلوب رمز مصادقة ثنائية. يرجى إدخال الرمز أدناه.", + "totp_invalid": "رمز المصادقة غير صحيح. تحقق من تطبيق المصادقة وحاول مرة أخرى.", + "oauth_discovery_failed": "تسجيل الدخول الموحد (SSO) مفعّل، لكن تعذّر الوصول إلى مزوّد الهوية. تحقق من إعدادات OAuth.", + "token_exchange_failed": "تم قبول كلمة المرور والرمز، لكن خادم البريد رفض بدء جلسة لهذا التطبيق. اطلب من المسؤول التحقق من إعدادات عميل OAuth." + }, + "show_password": "إظهار كلمة المرور", + "hide_password": "إخفاء كلمة المرور", + "totp_toggle": "لديّ رمز مصادقة ثنائية", + "token_toggle": "تسجيل الدخول باستخدام رمز وصول", + "password_toggle": "تسجيل الدخول باستخدام كلمة مرور", + "token_label": "رمز الوصول", + "token_placeholder": "الصق رمز API الخاص بك", + "token_hint": "أنشئ رمز API في إعدادات مزوّد البريد الخاص بك والصقه هنا.", + "remember_me": "تذكرني", + "config_error": { + "title": "خطأ في الإعداد", + "fetch_failed": "تعذّر تحميل إعدادات التطبيق. يرجى المحاولة مرة أخرى لاحقًا.", + "server_not_configured": "لم يتم إعداد خادم البريد. يرجى التواصل مع المسؤول." + }, + "remove_from_history": "إزالة من السجل", + "totp_label": "رمز المصادقة", + "totp_placeholder": "000000", + "session_expired": "انتهت صلاحية الجلسة. يرجى تسجيل الدخول مرة أخرى.", + "dismiss": "تجاهل", + "or": "أو", + "sign_in_sso": "تسجيل الدخول عبر SSO", + "add_account_title": "إضافة حساب", + "add_account_subtitle": "تسجيل الدخول بحساب آخر", + "cancel": "إلغاء", + "website": "الموقع الإلكتروني", + "imprint": "بيانات الناشر", + "privacy_policy": "سياسة الخصوصية", + "try_demo": "جرّب النسخة التجريبية", + "demo_description": "استكشف باستخدام بيانات نموذجية - دون الحاجة إلى حساب", + "demo_launching": "جارٍ تشغيل النسخة التجريبية...", + "demo_login_button": "تشغيل النسخة التجريبية", + "demo_tagline": "جرّب عميل بريد إلكتروني متكامل الميزات. لا حاجة إلى حساب.", + "demo_no_signup": "لا حاجة للتسجيل - استكشف بحرية باستخدام بيانات نموذجية", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "جارٍ إتمام تسجيل الدخول...", + "oauth_error": { + "title": "فشلت المصادقة", + "invalid_state": "فشل التحقق الأمني. يرجى محاولة تسجيل الدخول مرة أخرى.", + "missing_params": "بيانات التفويض مفقودة. يرجى محاولة تسجيل الدخول مرة أخرى.", + "token_exchange_failed": "فشل إتمام المصادقة. يرجى المحاولة مرة أخرى.", + "access_denied": "تم رفض الوصول. يرجى التواصل مع المسؤول.", + "back_to_login": "العودة إلى تسجيل الدخول" + } + }, + "sidebar": { + "close": "إغلاق", + "compose": "إنشاء رسالة", + "compose_hint": "إنشاء رسالة (c)", + "search_placeholder": "بحث في البريد...", + "search_placeholder_hint": "بحث في البريد... (اضغط /)", + "storage": "التخزين", + "storage_used": "المستخدم", + "storage_free": "المتاح", + "storage_total": "الإجمالي", + "sign_out": "تسجيل الخروج", + "sign_out_of": "تسجيل الخروج من {account}", + "sign_out_all": "تسجيل الخروج من جميع الحسابات", + "add_account": "إضافة حساب", + "set_as_default": "تعيين كافتراضي", + "switch_account": "تبديل الحساب", + "contacts": "جهات الاتصال", + "calendar": "التقويم", + "settings": "الإعدادات", + "admin": "الإدارة", + "files": "الملفات", + "loading_mailboxes": "جارٍ تحميل صناديق البريد...", + "push_connected": "التحديثات الفورية نشطة", + "push_disconnected": "التحديثات الفورية غير نشطة", + "keyboard_shortcuts": "اختصارات لوحة المفاتيح", + "theme": { + "light": "الوضع الفاتح", + "dark": "الوضع الداكن", + "system": "وضع النظام" + }, + "language": { + "title": "اللغة" + }, + "mailboxes": { + "inbox": "البريد الوارد", + "sent": "المرسل", + "drafts": "المسودات", + "trash": "المهملات", + "archive": "الأرشيف", + "starred": "المميزة بنجمة", + "all_mail": "كل البريد", + "spam": "البريد المزعج", + "important": "مهم" + }, + "unified_inbox": "البريد الوارد الموحّد", + "unified_sent": "كل المرسل", + "unified_drafts": "كل المسودات", + "unified_trash": "كل المهملات", + "unified_archive": "كل الأرشيف", + "unified_junk": "كل البريد المزعج", + "all_accounts": "كل الحسابات", + "all_accounts_loading": "جارٍ تحميل الحسابات: {loaded} من {total}", + "unified_mailbox": "صندوق البريد الموحّد", + "expand": "توسيع", + "collapse": "طي", + "expand_tooltip": "توسيع", + "collapse_tooltip": "طي", + "mobile": { + "search": "بحث", + "compose": "إنشاء رسالة", + "go_back": "العودة", + "toggle_menu": "تبديل القائمة" + }, + "clear_search": "مسح البحث", + "vacation_active": "الرد التلقائي أثناء الغياب مفعّل", + "demo_banner": "وضع تجريبي", + "demo_reset": "إعادة تعيين", + "demo_tour": "جولة", + "tags": "الوسوم", + "show_all_tags": "إظهار الكل ({count})", + "show_fewer_tags": "إظهار أقل", + "folders": "المجلدات", + "your_folders": "مجلداتك", + "create_folder": "إنشاء مجلد", + "show_more": "المزيد", + "show_fewer": "أقل", + "shared": "مشترك", + "shared_account_hidden": "تم الإخفاء من الشريط الجانبي. أعد إظهاره من الإعدادات › التخطيط.", + "notifications_muted": "الإشعارات مكتومة", + "mail": "البريد", + "nav_label": "التنقل", + "add_app": "التطبيقات", + "scheduled": "المجدولة", + "unified_all_unread": "كل غير المقروء", + "unified_all_starred": "كل المميز بنجمة", + "unified_all_mail": "كل البريد", + "remove_account": "إزالة الحساب", + "remove_account_confirm": "هل تريد إزالة {account} من هذا الجهاز؟ يمكنك إضافته مرة أخرى لاحقًا.", + "labels": "التصنيفات", + "create_label": "إنشاء تصنيف جديد", + "mv_tasks": "المهام", + "mv_side_panel": "اللوحة الجانبية", + "mv_open_full": "فتح في عرض كامل", + "mv_previous_day": "اليوم السابق", + "mv_next_day": "اليوم التالي", + "mv_add_another_account": "إضافة حساب آخر", + "mv_manage_account": "إدارة حسابك", + "mv_storage_used": "تم استخدام {percent}% من {total}" + }, + "protocol_handlers": { + "title": "التطبيقات الافتراضية", + "description": "اختر ما إذا كانت روابط البريد والتقويم تُفتح في Bulwark. من الناحية التقنية، يسجّل Bulwark نفسه كمعالج بروتوكول لروابط mailto: وwebcal:.", + "unsupported": "هذا المتصفح أو الاتصال لا يدعم التسجيل اليدوي لمعالج البروتوكول. قد يمكنك مع ذلك استخدام تطبيق PWA المثبّت عبر إعدادات المتصفح أو النظام.", + "mailto_label": "روابط البريد الإلكتروني", + "mailto_description": "فتح روابط mailto: في Bulwark مع نافذة إنشاء رسالة معبأة مسبقًا.", + "protocol_open_mode_label": "عند فتح روابط البروتوكول", + "protocol_open_mode_description": "اختر ما إذا كان Bulwark يفتح روابط mailto: وwebcal: في علامة تبويب جديدة أو يعيد استخدام جلسة مفتوحة. يتطلب خيار الجلسة النشطة إذن الإشعارات حتى تتمكن من النقر على إشعار احتياطي لإحضار Bulwark إلى المقدمة إذا منع المتصفح التركيز.", + "protocol_open_mode_active_session": "الفتح في الجلسة النشطة إن أمكن", + "protocol_open_mode_new_tab": "فتح علامة تبويب جديدة دائمًا", + "focus_notification_title": "فتح Bulwark", + "focus_notification_body": "تم فتح الرابط في Bulwark. انقر لإحضار النافذة إلى المقدمة.", + "webcal_label": "روابط التقويم", + "webcal_description": "فتح روابط webcal: في Bulwark مع نافذة اشتراك تقويم معبأة مسبقًا.", + "register_mailto": "تسجيل تطبيق البريد", + "register_webcal": "تسجيل تطبيق التقويم", + "mailto_registered": "تم طلب تسجيل معالج البريد الإلكتروني", + "webcal_registered": "تم طلب تسجيل معالج التقويم", + "registration_failed": "فشل تسجيل معالج البروتوكول", + "opening_mailto": "جارٍ فتح نافذة الإنشاء...", + "opening_webcal": "جارٍ فتح التقويم...", + "browser_note": "قد يطلب منك المتصفح أو نظام التشغيل تأكيد ذلك، وقد يتطلب تثبيت Bulwark أولًا قبل إمكانية اختياره كتطبيق افتراضي.", + "select_account_title": "اختر الحساب", + "select_mailto_account": "اختر الحساب الذي سيفتح رابط البريد هذا.", + "select_webcal_account": "اختر الحساب الذي سيفتح رابط التقويم هذا.", + "select_account_note": "ينطبق هذا فقط على رابط البروتوكول هذا.", + "detail_to": "إلى", + "detail_subject": "الموضوع", + "detail_no_subject": "بلا موضوع", + "detail_calendar": "التقويم", + "detail_source": "المصدر", + "active_account": "نشط", + "switching_account": "جارٍ تبديل الحساب..." + }, + "sidebar_apps": { + "modal_title": "تطبيقات الشريط الجانبي", + "add_new": "إضافة تطبيق", + "edit_app": "تعديل التطبيق", + "name_label": "الاسم", + "name_placeholder": "تطبيقي", + "name_required": "الاسم مطلوب", + "url_label": "الرابط", + "url_required": "الرابط مطلوب", + "url_invalid": "أدخل رابط http أو https صالحًا", + "icon_label": "الأيقونة", + "icon_required": "الأيقونة مطلوبة", + "open_mode_label": "وضع الفتح", + "open_new_tab": "علامة تبويب جديدة", + "open_inline": "ضمن الصفحة", + "cancel": "إلغاء", + "add": "إضافة", + "update": "تحديث", + "delete": "حذف", + "delete_confirm_title": "حذف التطبيق", + "delete_confirm": "هل أنت متأكد أنك تريد حذف \"{name}\"؟", + "no_apps": "لم تتم إضافة أي تطبيقات بعد", + "no_apps_hint": "أضف تطبيقات وروابط مخصصة إلى الشريط الجانبي", + "managed_title": "مقدَّمة من المسؤول", + "managed_description": "تم إعداد هذه التطبيقات للجميع ولا يمكن تغييرها.", + "managed_badge": "مُدارة", + "search_icons": "بحث عن أيقونات...", + "show_popular": "الأكثر شيوعًا", + "show_all": "الكل", + "no_icons_found": "لم يتم العثور على أيقونات", + "inline_badge": "ضمن الصفحة", + "tab_badge": "علامة تبويب", + "show_on_mobile": "إظهار على الجوال" + }, + "email_list": { + "columns": { + "from": "من", + "to": "إلى", + "subject": "الموضوع", + "date": "التاريخ", + "size": "الحجم" + }, + "no_emails": "لم يتم العثور على رسائل", + "no_emails_description": "صندوق البريد هذا فارغ", + "no_search_results": "لم يتم العثور على نتائج", + "no_search_results_description": "حاول تعديل البحث أو عوامل التصفية", + "loading": "جارٍ تحميل الرسائل...", + "unread": "غير مقروءة", + "to_me": "إليّ", + "to_recipients": "إلى {count} من المستلمين", + "and_others": "و{count} آخرين", + "draft": "مسودة", + "starred": "مميزة بنجمة", + "pinned": "مثبتة", + "replied": "تم الرد عليها", + "forwarded": "تمت إعادة توجيهها", + "has_attachment": "تحتوي على مرفق", + "conversations_range": "{from}–{to, number} من {total, number}", + "conversations_count": "{count} من {total} محادثة", + "conversations_count_plus": "{count}+ محادثة", + "conversations_count_simple": "{count} محادثة", + "no_conversations": "لا توجد محادثات", + "loading_more": "جارٍ تحميل المزيد من الرسائل...", + "no_more_emails": "لا مزيد من الرسائل للتحميل", + "batch_actions": { + "select": "تحديد الرسائل", + "select_all": "تحديد الكل", + "selected_messages": "{count, plural, one {تم تحديد رسالة واحدة} other {تم تحديد # رسالة}}", + "mark_read": "تمييز كمقروءة", + "mark_unread": "تمييز كغير مقروءة", + "delete": "حذف", + "delete_confirm_title": "حذف الرسائل", + "delete_confirm_message": "هل أنت متأكد أنك تريد حذف {count, plural, one {رسالة واحدة} other {# رسالة}}؟", + "clear_selection": "إلغاء التحديد" + }, + "permanent_delete": "حذف نهائي", + "permanent_delete_confirm_title": "حذف نهائي", + "permanent_delete_confirm_message": "سيتم حذف هذه الرسالة نهائيًا. لا يمكن التراجع عن هذا الإجراء.", + "permanent_delete_confirm_batch_message": "سيتم حذف {count, plural, one {رسالة واحدة} other {# رسالة}} نهائيًا. لا يمكن التراجع عن هذا الإجراء.", + "empty_folder": { + "button": "إفراغ المجلد", + "confirm_title": "إفراغ المجلد", + "confirm_message": "سيتم حذف جميع الرسائل في هذا المجلد نهائيًا. لا يمكن التراجع عن هذا الإجراء.", + "confirm_message_trash": "سيتم نقل جميع الرسائل في هذا المجلد إلى المهملات.", + "confirm_button": "إفراغ المجلد", + "junk_hint": "يمكنك إفراغ مجلد البريد المزعج لحذف جميع الرسائل نهائيًا.", + "trash_hint": "يمكنك إفراغ مجلد المهملات لحذف جميع الرسائل نهائيًا." + }, + "no_scheduled_emails": "لا توجد رسائل مجدولة", + "no_scheduled_emails_description": "ستظهر هنا الرسائل المجدولة للإرسال لاحقًا.", + "scheduled_count": "{count, plural, one {رسالة مجدولة واحدة} other {# رسالة مجدولة}}", + "scheduled_actions_hint": "يمكن إلغاء الرسائل المجدولة أو إعادة جدولتها أو تعديلها من إجراءاتها المجدولة.", + "cancel_scheduled_send": "إلغاء الإرسال", + "reschedule_send": "إعادة الجدولة", + "cancel_and_edit": "إلغاء والتعديل", + "cancel_and_compose_again": "إلغاء وإعادة الكتابة", + "reschedule_prompt": "أدخل تاريخًا/وقتًا جديدًا، مثل 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "طلب المُرسل إشعاره عند فتح هذه الرسالة.", + "send": "إرسال إيصال", + "ignore": "تجاهل", + "sent": "تم إرسال إيصال القراءة.", + "send_failed": "تعذّر إرسال إيصال القراءة", + "mdn_subject": "تمت القراءة: {subject}", + "mdn_body": "هذا إيصال إرجاع للرسالة التي أرسلتها إلى {recipient}.\n\nملاحظة: يفيد هذا الإيصال فقط بأن الرسالة عُرضت على جهاز المستلم. لا يوجد ضمان بأن المستلم قد قرأ أو فهم محتوى الرسالة." + }, + "no_email_selected": "لم يتم تحديد أي رسالة", + "no_email_description": "اختر رسالة من القائمة لعرضها هنا", + "no_conversation_selected": "لم يتم تحديد أي محادثة", + "no_conversation_description": "اختر محادثة من القائمة لقراءتها هنا", + "compose": "إنشاء رسالة", + "compose_hint": "إنشاء رسالة جديدة", + "no_subject": "(بلا موضوع)", + "no_body_content": "(لا يوجد محتوى نصي متاح)", + "no_preview_available": "لا تتوفر معاينة", + "show_quoted_text": "إظهار النص المقتبس", + "hide_quoted_text": "إخفاء النص المقتبس", + "loading_email": "جارٍ تحميل الرسالة...", + "loading": "جارٍ التحميل...", + "reply": "رد", + "reply_all": "الرد على الجميع", + "forward": "إعادة توجيه", + "delete": "حذف", + "archive": "أرشفة", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "تمييز بنجمة", + "unstar": "إزالة النجمة", + "mark_unread": "تمييز كغير مقروءة", + "mark_read": "تمييز كمقروءة", + "unread": "غير مقروءة", + "read": "مقروءة", + "spam_short": "مزعج", + "not_spam_short": "ليس مزعجًا", + "move": "نقل", + "print": "طباعة", + "view_source": "عرض المصدر", + "fullscreen": "ملء الشاشة", + "exit_fullscreen": "الخروج من ملء الشاشة", + "view_in_dark_mode": "عرض في الوضع الداكن", + "view_in_light_mode": "عرض في الوضع الفاتح", + "export_email": "تصدير كملف ‎.eml", + "forward_as_attachment": "إعادة التوجيه كمرفق", + "import_email": "استيراد ملف ‎.eml أو ‎.zip", + "keyboard_shortcuts": "اختصارات لوحة المفاتيح (؟)", + "email_source": "مصدر الرسالة", + "draft_banner": "هذه الرسالة مسودة", + "edit_draft": "تعديل", + "copy_source": "نسخ إلى الحافظة", + "source_copied": "تم نسخ المصدر إلى الحافظة", + "attachments": "المرفقات", + "important": "مهم", + "download": "تنزيل", + "download_all": "تنزيل الكل", + "from": "من", + "to": "إلى", + "cc": "نسخة", + "bcc": "نسخة مخفية", + "date": "التاريخ", + "subject": "الموضوع", + "show_details": "إظهار التفاصيل", + "hide_details": "إخفاء التفاصيل", + "external_content_warning": "تم حظر الصور والمحتوى الخارجي", + "load_external_content": "تحميل الصور", + "trust_sender": "الوثوق بهذا المرسل دائمًا", + "sender_check": { + "unverified_label": "مرسل غير موثّق", + "failed_label": "فشل التحقق من المرسل", + "unverified": "تدّعي هذه الرسالة أنها من {domain}، لكن لا شيء يؤكد ذلك. فهي لا تحمل توقيعًا صالحًا من {domain} ولم تأتِ من خادم مخوّل من قِبل {domain}.", + "unverified_sent_from": "تدّعي هذه الرسالة أنها من {domain}، لكنها أُرسلت من {host} ولا تحمل توقيعًا صالحًا من {domain}.", + "failed": "تدّعي هذه الرسالة أنها من {domain}، لكنها لم تجتز فحوصات المرسل الخاصة بهذا النطاق. قد تكون مزوّرة.", + "failed_sent_from": "تدّعي هذه الرسالة أنها من {domain}، لكنها أُرسلت من {host} ولم تجتز فحوصات المرسل الخاصة بـ {domain}. قد تكون مزوّرة.", + "caution": "توخَّ الحذر مع الروابط والمرفقات وطلبات كلمات المرور أو بيانات الدفع." + }, + "back_to_list": "العودة إلى القائمة", + "view_contact": "عرض جهة الاتصال", + "message_details": "تفاصيل الرسالة", + "more_reply_options": "خيارات رد إضافية", + "set_tag": "تعيين وسم", + "tag": "وسم", + "more_actions": "المزيد من الإجراءات", + "previous": "السابق", + "next": "التالي", + "move_to": "نقل إلى...", + "remove_tag": "إزالة الوسم", + "tag_filter_placeholder": "تصفية الوسوم", + "tag_no_matches": "لا توجد وسوم مطابقة", + "more_count": "+{count} أخرى", + "characters_count": "{count} حرفًا", + "quick_reply_placeholder": "اكتب ردًا سريعًا...", + "more_options": "المزيد من الخيارات", + "sending": "جارٍ الإرسال...", + "security_authentication": "الأمان والمصادقة", + "technical_details": "التفاصيل التقنية", + "message_id_label": "معرّف الرسالة:", + "reply_to_label": "الرد إلى:", + "delivery_time_label": "وقت التسليم:", + "conversation_part_label": "جزء من المحادثة:", + "previous_messages": "{count} رسالة سابقة", + "previous_messages_plural": "{count} رسائل سابقة", + "time": { + "day": "يوم", + "days": "أيام", + "hour": "ساعة", + "hours": "ساعات", + "minute": "دقيقة", + "minutes": "دقائق" + }, + "unknown_sender": "غير معروف", + "recipient_me": "أنا", + "recipient_and_others": "{name} و{count} آخرين", + "recipient_to_prefix": "إلى:", + "authentication": { + "title": "المصادقة", + "status": { + "verified": "موثّق", + "warning": "تحذير", + "none": "غير موثّق" + }, + "spf": { + "pass": "نجاح SPF", + "fail": "فشل SPF", + "none": "لا يوجد SPF" + }, + "dkim": { + "pass": "DKIM صالح", + "fail": "DKIM غير صالح", + "none": "لا يوجد DKIM" + }, + "dmarc": { + "pass": "نجاح DMARC", + "fail": "فشل DMARC", + "none": "لا يوجد DMARC" + }, + "spam_score": "درجة البريد المزعج", + "tooltip_spf": "إطار سياسة المرسل (SPF): يتحقق من أن خادم الإرسال مخوّل بإرسال البريد نيابة عن النطاق", + "tooltip_dkim": "بريد معرّف بمفاتيح النطاق (DKIM): يؤكد أن الرسالة لم تُعدَّل أثناء النقل باستخدام توقيع تشفيري", + "tooltip_dmarc": "مصادقة الرسائل والإبلاغ والتوافق المستندة إلى النطاق (DMARC): تضمن توافق SPF وDKIM مع نطاق المرسل وتحدد سياسة عند الفشل", + "policy": "السياسة", + "result": { + "pass": "نجاح", + "fail": "فشل", + "softfail": "فشل بسيط", + "neutral": "محايد", + "permerror": "خطأ دائم", + "temperror": "خطأ مؤقت", + "none": "لا شيء" + } + }, + "details": { + "recipients_routing": "المستلمون والتوجيه", + "authentication_security": "المصادقة والأمان", + "identifiers_threading": "المعرّفات والمحادثة", + "mailing_list": "القائمة البريدية", + "message_properties": "خصائص الرسالة", + "sent": "أُرسلت", + "received": "استُلمت", + "delivery_time": "وقت التسليم", + "in_reply_to": "ردًا على", + "references": "المراجع", + "thread_id": "معرّف المحادثة", + "size": "الحجم", + "mime_type": "نوع MIME", + "attachments_summary": "{count} ملفات · {size}", + "list_id": "معرّف القائمة", + "list_help": "مساعدة القائمة", + "list_post": "نشر القائمة", + "list_unsubscribe": "إلغاء الاشتراك", + "iprev": "DNS العكسي", + "spam_status": "حالة البريد المزعج", + "ai_verdict": "تقييم الذكاء الاصطناعي", + "account": "الحساب", + "no_subject": "(بلا موضوع)" + }, + "headers": { + "routing": "التوجيه", + "received": "استُلمت", + "message_id": "معرّف الرسالة", + "list_info": "معلومات القائمة" + }, + "tooltips": { + "reply": "رد (r)", + "reply_all": "الرد على الجميع (a)", + "forward": "إعادة توجيه (f)", + "archive": "أرشفة (e)", + "delete": "حذف (# أو Del)", + "star": "تمييز بنجمة (s)", + "unstar": "إزالة النجمة (s)", + "compose": "إنشاء رسالة (c)", + "previous": "الرسالة السابقة", + "next": "الرسالة التالية", + "edit_draft": "تعديل المسودة" + }, + "spam": { + "button_title": "الإبلاغ عن بريد مزعج", + "not_spam_title": "تمييز كرسالة سليمة", + "toast_success": "تم النقل إلى البريد المزعج", + "toast_batch": "تم نقل {count} رسالة إلى البريد المزعج", + "toast_undo": "تراجع", + "toast_not_spam_success": "تم النقل إلى البريد الوارد", + "toast_not_spam_batch": "تم نقل {count} رسالة إلى البريد الوارد", + "error": "فشل الإبلاغ عن البريد المزعج", + "error_not_spam": "فشلت استعادة الرسالة" + }, + "unsubscribe_banner": { + "label": "نشرة بريدية", + "button": "إلغاء الاشتراك", + "confirm_title": "إلغاء الاشتراك من هذا المرسل؟", + "confirm_button": "تأكيد", + "cancel": "إلغاء", + "success_http": "تم فتح صفحة إلغاء الاشتراك في علامة تبويب جديدة", + "success_mailto": "تم إرسال رسالة إلغاء الاشتراك", + "confirm_message_http": "سيتم فتح صفحة إلغاء الاشتراك في علامة تبويب جديدة.", + "confirm_message_mailto": "سيتم إرسال رسالة إلغاء اشتراك إلى المرسل.", + "error": "تعذّر إلغاء الاشتراك", + "dismiss": "تجاهل" + }, + "calendar_invitation": { + "loading": "جارٍ تحميل تفاصيل الحدث…", + "title": "دعوة تقويم", + "published_title": "حدث منشور", + "response_title": "رد على الحدث", + "update_title": "تحديث الحدث", + "counter_title": "اقتراح مضاد", + "refresh_title": "طلب تحديث", + "declined_counter_title": "تم رفض الاقتراح المضاد", + "cancelled_title": "تم إلغاء الحدث", + "organizer": "نظّمه {name}", + "organizer_label": "نظّمه", + "attendees": "{count, plural, one {# حاضر} other {# حاضرين}}", + "accept": "قبول", + "maybe": "ربما", + "decline": "رفض", + "add_to_calendar": "إضافة إلى التقويم", + "added": "تمت الإضافة إلى التقويم", + "rsvp_sent": "تم إرسال الرد", + "parse_error": "تعذّرت قراءة الدعوة", + "action_failed": "تعذّر إتمام إجراء التقويم هذا.", + "no_calendar": "التقويم غير متاح", + "published_info": "تمت مشاركة هذا الحدث للاطلاع.", + "response_info": "تحتوي هذه الرسالة على رد أحد الحاضرين.", + "response_info_organizer": "يحدّث رد الحاضر هذا حدثك.", + "update_info": "تحدّث هذه الرسالة حدثًا موجودًا.", + "counter_info": "تقترح هذه الرسالة تغييرات على حدث.", + "counter_info_organizer": "اقترح هذا الحاضر تغييرات على حدثك.", + "refresh_info": "تطلب هذه الرسالة أحدث تفاصيل الحدث.", + "refresh_info_organizer": "طلب أحد الحاضرين أحدث تفاصيل الحدث.", + "declined_counter_info": "رفض المنظّم اقتراحًا مضادًا.", + "authentication_failed_info": "فشلت فحوصات مصادقة البريد لهذه الدعوة. تعامل مع إجراءات التقويم بحذر.", + "authentication_missing_info": "لا تتضمن هذه الدعوة مصادقة بريد موثّقة. تأكد من التفاصيل مع المنظّم إذا بدا أي شيء غير معتاد.", + "sender_mismatch_info": "أُرسلت هذه الدعوة من {sender}، بينما المنظّم المدرج في بيانات التقويم هو {organizer}.", + "sender_mismatch_unverified_info": "أُرسلت هذه الدعوة من {sender}، بينما المنظّم المدرج في بيانات التقويم هو {organizer}، ولم يتم التحقق من الرسالة.", + "organizer_role": "أنت منظّم هذا الحدث", + "your_response": "ردّك: {status}", + "response_needed": "بحاجة إلى رد", + "response_accepted": "مقبول", + "response_tentative": "مبدئي", + "response_declined": "مرفوض", + "response_delegated": "مفوَّض", + "actor_sent_info": "أُرسلت بواسطة {name}.", + "actor_response_info": "ردّ {name} بـ{status}.", + "actor_counter_info": "اقترح {name} تغييرات على هذا الحدث.", + "actor_refresh_info": "طلب {name} أحدث تفاصيل الحدث.", + "actor_declined_counter_info": "رفض {name} الاقتراح المضاد.", + "actor_note": "ملاحظة: {comment}", + "actor_unknown": "شخص ما", + "proposed_changes": "التغييرات المقترحة", + "change_title": "العنوان", + "change_time": "الوقت", + "change_location": "الموقع", + "change_description": "الوصف", + "change_empty": "لا شيء", + "change_from_to": "{before} -> {after}", + "apply_proposal": "تطبيق التغييرات المقترحة", + "proposal_applied": "تم تطبيق التغييرات المقترحة.", + "review_proposal": "مراجعة الاقتراح", + "review_request": "طلب مراجعة", + "view_in_calendar": "عرض في التقويم", + "select_calendar": "اختر التقويم", + "already_in_calendar": "موجود بالفعل في تقويمك", + "request_info": "تمت دعوتك إلى هذا الحدث. أجب لإعلام المنظّم بتوفرك.", + "cancel_info": "ألغى المنظّم هذا الحدث.", + "event_updated": "تحديث رقم {sequence}", + "event_status_tentative": "مبدئي", + "event_status_cancelled": "ملغى", + "expand": "إظهار التفاصيل", + "collapse": "إخفاء التفاصيل" + }, + "send": "إرسال", + "more": "المزيد", + "scheduled_banner": "مجدولة للإرسال في {date}", + "scheduled_send_created": "تمت جدولة الرسالة للإرسال", + "sending_in": "الإرسال خلال {seconds} ث", + "undo_send_failed": "تعذّر التراجع. ربما أُرسلت الرسالة بالفعل.", + "cancel_scheduled_send": "إلغاء الإرسال", + "reschedule_send": "إعادة الجدولة", + "cancel_and_edit": "إلغاء والتعديل", + "cancel_and_compose_again": "إلغاء وإعادة الكتابة", + "reschedule_prompt": "أدخل تاريخًا/وقتًا جديدًا، مثل 2026-05-04T15:30", + "scheduled_actions_only": "عرض المجدولة يدعم الإجراءات المجدولة فقط", + "undo_send_scheduled": "تمت جدولة الرسالة للإرسال", + "undo_send": "تراجع عن الإرسال", + "contact_sidebar": { + "title": "جهة الاتصال", + "close": "إغلاق الشريط الجانبي", + "action_email": "بريد إلكتروني", + "action_email_title": "إرسال بريد إلكتروني", + "action_copy": "نسخ", + "action_copy_title": "نسخ البريد الإلكتروني", + "action_edit_title": "تعديل جهة الاتصال", + "section_emails": "عناوين البريد الإلكتروني", + "section_phones": "أرقام الهاتف", + "section_organizations": "المؤسسات", + "section_addresses": "العناوين", + "section_notes": "ملاحظات", + "not_in_contacts": "ليست ضمن جهات اتصالك", + "add_to_contacts": "إضافة إلى جهات الاتصال", + "copied": "تم النسخ!", + "copy_failed": "فشل النسخ", + "view_contact": "عرض جهة الاتصال", + "view_details": "عرض التفاصيل" + }, + "send_now": "إرسال الآن", + "verification_code": { + "copy": "نسخ الرمز {code}", + "copied": "تم نسخ الرمز", + "copy_failed": "تعذّر نسخ الرمز" + }, + "mv_position": "{index} من {total}", + "plugin_panel_show": "إظهار لوحة الإضافة", + "plugin_panel_hide": "إخفاء لوحة الإضافة" + }, + "email_composer": { + "select_from_directory": "الاختيار من الدليل", + "minimize": "تصغير", + "maximize": "ملء الشاشة", + "restore": "استعادة", + "new_message": "رسالة جديدة", + "signature_edit_hint": "انقر نقرًا مزدوجًا لتحرير التوقيع", + "reply": "رد", + "reply_all": "الرد على الجميع", + "forward": "إعادة توجيه", + "reply_to": "رد", + "reply_all_to": "الرد على الجميع", + "forward_message": "إعادة توجيه", + "from": "من", + "to": "إلى", + "cc": "نسخة", + "bcc": "نسخة مخفية", + "subject": "الموضوع", + "body_placeholder": "اكتب رسالتك...", + "send": "إرسال", + "cancel": "إلغاء", + "attach": "إرفاق", + "attach_photos": "الصور ومقاطع الفيديو", + "attach_files": "ملفات", + "attach_from_files": "إرفاق من الملفات", + "files_picker": { + "attach": "{count, plural, =0 {إرفاق} one {إرفاق ملف واحد} other {إرفاق # ملفات}}", + "empty_folder": "هذا المجلد فارغ", + "load_failed": "تعذّر تحميل ملفاتك" + }, + "discard": "تجاهل", + "discard_draft_title": "تجاهل المسودة؟", + "discard_draft_confirm": "لديك تغييرات غير محفوظة. هل تريد تجاهل هذه المسودة؟", + "discard_draft_message": "ستُحذف المسودة.", + "saving": "جارٍ الحفظ...", + "sending": "جارٍ الإرسال...", + "add_link": "إضافة رابط", + "link_url_prompt": "أدخل الرابط", + "draft_saved": "تم حفظ المسودة", + "save_failed": "فشل الحفظ", + "to_placeholder": "عناوين البريد الإلكتروني للمستلمين", + "cc_placeholder": "مستلمو النسخة", + "bcc_placeholder": "مستلمو النسخة المخفية", + "subject_placeholder": "الموضوع", + "cc_label": "نسخة:", + "bcc_label": "نسخة مخفية:", + "subject_label": "الموضوع:", + "file_size_kb": "ك.ب", + "prefix": { + "forward": "إعادة توجيه:", + "reply": "رد:" + }, + "forward_batch_count": "{count, plural, one {رسالة واحدة} other {# رسائل}}", + "no_subject": "(بلا موضوع)", + "unknown_sender": "غير معروف", + "quote": { + "reply_header": "في {date}، كتب {sender}:", + "forward_header": "---------- رسالة موجّهة ----------", + "from": "من: {sender}", + "date": "التاريخ: {date}", + "subject": "الموضوع: {subject}", + "to": "إلى: {recipients}" + }, + "remove_sub_address": "إزالة العنوان الفرعي", + "from_override": { + "toggle_off": "تجاوز", + "toggle_on": "إلغاء التجاوز", + "toggle_tooltip": "عدّل اسم وعنوان \"من\" بحرية. سيُرسل البريد عبر هويتك.", + "name_label": "اسم المرسل", + "name_placeholder": "الاسم", + "email_label": "عنوان بريد المرسل", + "email_placeholder": "alias@example.com", + "envelope_notice": "إذا لم يقبل خادمك هذا العنوان كمرسل للمغلف، فسيُستخدم {identity} بدلاً منه، ويمكن للمستلمين رؤيته في ترويسة Return-Path." + }, + "save_as_template": "حفظ كقالب", + "attach_from_device": "رفع من هذا الجهاز", + "templates_menu": "القوالب", + "insert_template": "إدراج قالب", + "options": "الخيارات", + "option_plain_text": "نص عادي", + "option_plain_text_hint": "إرسال بدون تنسيق", + "option_read_receipt": "طلب إشعار القراءة", + "option_read_receipt_hint": "اطلب من المستلم تأكيد فتح الرسالة", + "option_dsn": "إشعار التسليم", + "option_dsn_hint": "احصل على تقرير عندما يسلّمها الخادم", + "option_require_tls": "اشتراط التسليم المشفّر", + "option_require_tls_hint": "التسليم عبر TLS فقط، وإلا تُرفض", + "option_priority": "الأهمية", + "option_priority_high": "عالية", + "option_priority_normal": "عادية", + "option_priority_low": "منخفضة", + "validation": { + "recipient_required": "أضف مستلمًا للإرسال", + "body_required": "اكتب رسالة أو أرفق ملفًا", + "invalid_recipients": "لم يُرسل - تحقق من هذه العناوين: {addresses}", + "attachments_uploading": "المرفقات لا تزال قيد الرفع — سيتم الإرسال فور انتهائها", + "attachment_upload_failed": "لم يتم الإرسال — فشل رفع أحد المرفقات. أزله وحاول مرة أخرى." + }, + "upload_progress": "جارٍ الرفع {uploaded} / {total}", + "upload_cancel": "إلغاء الرفع", + "upload_failed": "فشل رفع {filename}", + "attachment_too_large": "\"{name}\" أكبر مما يسمح به الخادم ({max} لكل ملف)", + "attachments_total_too_large": "ستتجاوز المرفقات حد الخادم البالغ {max} لكل رسالة", + "drop_files": "أفلت الملفات لإرفاقها", + "show_less": "إظهار أقل", + "send_failed": "فشل إرسال الرسالة", + "send_timeout": "توقف الخادم عن الاستجابة. تحقق من مجلد المُرسَل قبل إعادة الإرسال — فقد تكون الرسالة قد أُرسلت بالفعل.", + "send_offline": "يبدو أنك غير متصل. الرسالة ما زالت هنا، أرسلها مجددًا عند عودة الاتصال.", + "formatting": "التنسيق", + "send_recipients_rejected": "لم تُرسل الرسالة - رفض الخادم جميع المستلمين.", + "send_plugin_failed": "لم يُرسل - فشلت إضافة تتولى الإرسال أو استغرقت وقتًا طويلًا. تحقق من مجلد المرسل قبل الإرسال مرة أخرى.", + "send_some_recipients_rejected": "أُرسلت الرسالة، لكن ليس إلى هؤلاء المستلمين - رفضهم الخادم.", + "continue_draft": "متابعة المسودة", + "close_draft_title": "حفظ المسودة أم تجاهلها؟", + "close_draft_message": "لديك تغييرات غير محفوظة. هل تريد حفظها كمسودة أم تجاهلها؟", + "smime_sign_on": "توقيع S/MIME مفعّل", + "smime_sign_off": "تفعيل توقيع S/MIME", + "smime_encrypt_on": "تشفير S/MIME مفعّل", + "smime_encrypt_off": "تفعيل تشفير S/MIME", + "smime_encrypt_unavailable": "تشفير S/MIME غير متاح – شهادات المستلمين مفقودة", + "smime_unlock_title": "فتح مفتاح S/MIME", + "smime_unlock_message": "أدخل عبارة المرور لفتح مفتاح توقيع S/MIME الخاص بك.", + "smime_unlock_button": "فتح", + "smime_passphrase_placeholder": "عبارة المرور", + "forgot_attachment": { + "title": "هل نسيت مرفقًا؟", + "message": "رسالتك تذكر \"{keyword}\" لكن لا يوجد ملف مرفق. هل تريد الإرسال على أي حال؟", + "send_anyway": "الإرسال على أي حال", + "back": "العودة إلى التعديل" + }, + "empty_subject": { + "title": "إرسال بدون موضوع؟", + "message": "لا يحتوي هذا البريد على موضوع. هل تريد إرساله على أي حال؟", + "dont_ask_again": "عدم السؤال مرة أخرى", + "send_anyway": "الإرسال على أي حال", + "back": "العودة إلى التعديل" + }, + "schedule_send": "جدولة الإرسال", + "schedule_send_description": "اختر متى يجب على الخادم إرسال هذه الرسالة.", + "schedule_preset": { + "tomorrow_morning": "صباح الغد", + "tomorrow_afternoon": "ظهر الغد", + "monday_morning": "صباح الاثنين" + }, + "schedule_send_required": "اختر تاريخًا ووقتًا.", + "schedule_send_invalid": "أدخل تاريخًا ووقتًا صالحين.", + "schedule_send_future": "اختر تاريخًا ووقتًا في المستقبل.", + "schedule_send_too_late": "هذا الوقت متأخر عمّا يسمح به الخادم.", + "schedule_send_unsupported": "جدولة الإرسال غير مدعومة لهذا الحساب.", + "schedule_send_cleanup_warning": "تم إنشاء الإرسال المجدول، لكن فشل تنظيف المسودة.", + "send_delay_unsupported": "تأخير الإرسال غير مدعوم لهذا الحساب.", + "send_delay_unsupported_confirm": "هذا الحساب لا يدعم تأخير الإرسال. هل تريد الإرسال فورًا بدلاً من ذلك؟", + "recipient_edit_email": "تعديل عنوان البريد الإلكتروني", + "recipient_edit_name": "تعديل الاسم المعروض", + "recipient_email_placeholder": "عنوان البريد الإلكتروني", + "recipient_name_placeholder": "الاسم المعروض", + "invalid_recipient": "ليس عنوان بريد إلكتروني صالحًا", + "autocomplete_search_server": "البحث في الخادم", + "autocomplete_searching": "جارٍ البحث...", + "mention_recipients": "المستلمون", + "mention_card_not_recipient": "ليس في \"إلى\" أو \"نسخة\"، لذلك لن تصله هذه الرسالة.", + "mention_card_add": "إضافة إلى {field}", + "mention_card_remove": "إزالة الإشارة", + "toolbar": { + "bold": "غامق", + "italic": "مائل", + "underline": "تسطير", + "strikethrough": "يتوسطه خط", + "text_color": "لون النص", + "remove_color": "إزالة اللون", + "heading_1": "عنوان 1", + "heading_2": "عنوان 2", + "bullet_list": "قائمة نقطية", + "ordered_list": "قائمة مرقمة", + "quote": "اقتباس", + "code_block": "كتلة برمجية", + "align_left": "محاذاة لليسار", + "align_center": "توسيط", + "align_right": "محاذاة لليمين", + "text_direction": "اتجاه النص (RTL/LTR)", + "link": "رابط", + "table": "جدول", + "clear_formatting": "مسح التنسيق", + "undo": "تراجع", + "redo": "إعادة", + "add_row_above": "إضافة صف بالأعلى", + "add_row_below": "إضافة صف بالأسفل", + "add_column_before": "إضافة عمود قبل", + "add_column_after": "إضافة عمود بعد", + "delete_row": "حذف الصف", + "delete_column": "حذف العمود", + "toggle_header_row": "تبديل صف الرأس", + "delete_table": "حذف الجدول", + "pick_size": "اختيار الحجم", + "font_size": "حجم الخط", + "font_size_default": "افتراضي", + "background_color": "لون الخلفية", + "remove_background_color": "إزالة لون الخلفية" + }, + "send_filing_warning": "تم الإرسال - لكن التنظيف بعد الإرسال فشل، وقد تبقى مسودة قديمة.", + "pop_out": "فتح الرد في نافذة منفصلة", + "send_guard_failed": "لم يُرسَل - فشلت إضافة تفحص الرسائل قبل الإرسال أو لم تستجب." + }, + "confirm_dialog": { + "confirm": "تأكيد", + "cancel": "إلغاء" + }, + "common": { + "loading": "جارٍ التحميل...", + "error": "خطأ", + "success": "تم بنجاح", + "cancel": "إلغاء", + "save": "حفظ", + "delete": "حذف", + "edit": "تعديل", + "close": "إغلاق", + "search": "بحث", + "refresh": "تحديث", + "settings": "الإعدادات", + "help": "المساعدة", + "logout": "تسجيل الخروج", + "yes": "نعم", + "no": "لا", + "unknown": "غير معروف", + "app_title": "البريد الإلكتروني", + "reconnecting": "انقطع الاتصال. جارٍ محاولة إعادة الاتصال…", + "rate_limited_title": "مصادقة الخادم مقيّدة مؤقتًا بحد المعدل.", + "rate_limited_detail": "أوقف Bulwark الطلبات في الخلفية مؤقتًا لتجنب الحظر. ستتم إعادة المحاولة خلال {seconds} ثانية.", + "rate_limited_action_title": "تم إيقاف الطلب مؤقتًا لتجنب الحظر.", + "rate_limited_action_detail": "ينتظر Bulwark انتهاء فترة تهدئة الخادم قبل إرسال المزيد من الطلبات الموثّقة. حاول مرة أخرى خلال {seconds} ثانية.", + "pull_to_refresh": "اسحب للتحديث", + "release_to_refresh": "أفلت للتحديث", + "refreshing": "جارٍ التحديث…" + }, + "notifications": { + "email_sent": "تم إرسال الرسالة بنجاح", + "email_deleted": "تم حذف الرسالة", + "email_archived": "تمت أرشفة الرسالة", + "emails_deleted": "{count, plural, one {تم حذف رسالة واحدة} other {تم حذف # رسائل}}", + "emails_archived": "{count, plural, one {تمت أرشفة رسالة واحدة} other {تمت أرشفة # رسائل}}", + "error_archiving": "فشل أرشفة الرسالة", + "error_updating": "فشل تحديث الرسالة", + "email_starred": "تم تمييز الرسالة بنجمة", + "email_unstarred": "تمت إزالة النجمة عن الرسالة", + "email_marked_read": "تم تمييز الرسالة كمقروءة", + "email_marked_unread": "تم تمييز الرسالة كغير مقروءة", + "copied_to_clipboard": "تم النسخ إلى الحافظة", + "source_copied": "تم نسخ المصدر إلى الحافظة", + "error_sending": "فشل إرسال الرسالة", + "error_deleting": "فشل حذف الرسالة", + "error_loading": "فشل تحميل الرسائل", + "new_email": "رسالة جديدة", + "new_email_from": "من {sender}", + "click_to_view": "انقر للعرض", + "email_moved": "تم نقل الرسالة", + "emails_moved": "تم نقل {count} رسالة", + "moved_to_mailbox": "تم النقل إلى {mailbox}", + "move_failed": "فشل النقل", + "removed_from_folder": "تمت الإزالة من {mailbox}", + "remove_from_folder_failed": "تعذرت الإزالة من هذا المجلد", + "emails_copied": "تم نسخ {count} رسالة", + "copy_failed": "فشل النسخ", + "move_error": "تعذّر نقل الرسائل إلى المجلد المحدد", + "email_tagged": "تم وسم الرسالة", + "emails_tagged": "تم وسم {count} رسالة", + "tag_failed": "فشل الوسم", + "identity_created": "تم إنشاء الهوية بنجاح", + "identity_updated": "تم تحديث الهوية بنجاح", + "identity_deleted": "تم حذف الهوية", + "identity_set_primary": "تم تحديث الهوية الأساسية", + "identity_create_failed": "فشل إنشاء الهوية: {error}", + "identity_update_failed": "فشل تحديث الهوية: {error}", + "identity_delete_failed": "فشل حذف الهوية: {error}", + "identity_unauthorized": "غير مخوّل لك الإرسال من عنوان البريد الإلكتروني هذا", + "identity_not_found": "الهوية غير موجودة", + "vacation_saved": "تم حفظ إعدادات الغياب", + "vacation_save_failed": "فشل حفظ إعدادات الغياب", + "filters_saved": "تم حفظ المرشحات بنجاح", + "filters_save_failed": "فشل حفظ المرشحات", + "filters_deleted": "تم حذف قاعدة المرشح", + "templates_exported": "تم تصدير القوالب بنجاح", + "templates_imported": "{count, plural, one {تم استيراد قالب واحد} other {تم استيراد # قالب}}", + "templates_import_errors": "تعذّر استيراد بعض القوالب", + "templates_import_empty": "لم يتم العثور على قوالب في الملف", + "export_email_error": "فشل تصدير الرسالة", + "import_email_success": "تم استيراد الرسالة بنجاح", + "import_email_error": "فشل استيراد الرسالة", + "rule_created": "تم إنشاء القاعدة", + "rule_merged": "تمت الإضافة إلى القاعدة «{name}»", + "rule_already_covered": "تغطيها القاعدة «{name}» بالفعل", + "rule_apply_existing": "{count, plural, one {تطبيق على رسالة موجودة واحدة} other {تطبيق على # رسائل موجودة}}", + "rule_applied": "{count, plural, =0 {لم تحتج أي رسالة إلى تغيير} one {طُبّقت القاعدة على رسالة واحدة} other {طُبّقت القاعدة على # رسائل}}", + "rule_apply_failed": "تعذّر تطبيق القاعدة على الرسائل الموجودة", + "rule_edit": "تعديل القاعدة", + "rule_undo": "تراجع", + "rule_undone": "تم التراجع عن تغيير القاعدة", + "rule_undo_failed": "تعذّر التراجع عن تغيير القاعدة", + "rule_undo_conflict": "تغيّرت مرشحاتك منذ ذلك الحين. تراجع عن التغيير من إعدادات المرشحات.", + "rule_folder_failed": "تعذّر إنشاء المجلد", + "vacation_filters_save_failed": "تم حفظ إعدادات الغياب، لكن تعذّر حفظ إعادة التوجيه ومستلمي الرد" + }, + "date": { + "today": "اليوم", + "yesterday": "أمس", + "this_week": "هذا الأسبوع", + "last_week": "الأسبوع الماضي", + "this_month": "هذا الشهر", + "older": "أقدم", + "just_now": "الآن", + "minutes_ago": "قبل {count} دقيقة", + "minutes_ago_plural": "قبل {count} دقائق", + "hours_ago": "قبل {count} ساعة", + "hours_ago_plural": "قبل {count} ساعات", + "days_ago": "قبل {count} يوم", + "days_ago_plural": "قبل {count} أيام" + }, + "language": { + "title": "اللغة", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "اختيار اللغة", + "switch_to_english": "التبديل إلى الإنجليزية", + "switch_to_french": "التبديل إلى الفرنسية", + "switch_to_japanese": "التبديل إلى اليابانية", + "switch_to_spanish": "التبديل إلى الإسبانية", + "switch_to_italian": "التبديل إلى الإيطالية", + "switch_to_german": "التبديل إلى الألمانية", + "switch_to_dutch": "التبديل إلى الهولندية", + "switch_to_portuguese": "التبديل إلى البرتغالية", + "switch_to_russian": "التبديل إلى الروسية", + "switching": "جارٍ تغيير اللغة...", + "switch": "تبديل اللغة", + "current": "اللغة الحالية", + "polish": "Polski", + "switch_to_polish": "التبديل إلى البولندية", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "التبديل إلى الفارسية", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "التبديل إلى المنغولية" + }, + "settings": { + "title": "الإعدادات", + "back_to_mail": "العودة إلى البريد", + "save_success": "تم حفظ الإعدادات بنجاح", + "import_success": "تم استيراد الإعدادات بنجاح", + "import_error": "فشل استيراد الإعدادات", + "reset_confirm": "هل أنت متأكد أنك تريد إعادة تعيين جميع الإعدادات إلى الافتراضية؟", + "unsaved_changes": "لديك تغييرات غير محفوظة", + "discard_changes": "تجاهل التغييرات غير المحفوظة؟", + "discard": "تجاهل", + "keep_editing": "متابعة التعديل", + "search_placeholder": "بحث في الإعدادات", + "search_clear": "مسح البحث", + "search_no_results": "لا توجد إعدادات مطابقة", + "tabs": { + "appearance": "المظهر", + "language": "اللغة والمنطقة والوقت", + "email": "سلوك البريد", + "composer": "محرر الرسائل", + "privacy": "الخصوصية والأمان", + "account": "الحساب", + "identities": "الهويات", + "vacation": "الغياب", + "advanced": "متقدم", + "calendar": "التقويم", + "filters": "المرشحات", + "templates": "القوالب", + "folders": "المجلدات", + "keywords": "الوسوم", + "security": "الأمان", + "files": "الملفات", + "contacts": "جهات الاتصال", + "encryption": "التشفير", + "protocol_handlers": "التطبيقات الافتراضية", + "sidebar_apps": "تطبيقات الشريط الجانبي", + "notifications": "الإشعارات", + "layout": "التخطيط", + "themes": "السمات", + "reading": "القراءة", + "composing": "الكتابة", + "downloads": "التنزيلات", + "content_senders": "المحتوى والمرسلون", + "about_data": "حول والبيانات", + "debug": "التصحيح" + }, + "themes": { + "title": "السمات", + "description": "اختر من بين السمات التي نشرها المسؤول والإعدادات المسبقة المضمنة.", + "locked": "اختيار السمة مقفل من قِبل المسؤول.", + "forced": "السمة «{name}» مفروضة من قِبل المسؤول ولا يمكن تغييرها", + "admin_theme": "سمة المسؤول", + "activated": "تم تفعيل السمة", + "default_restored": "تمت استعادة السمة الافتراضية", + "default_name": "الافتراضية", + "default_badge": "افتراضية", + "admin_enforced": "مفروضة من المسؤول" + }, + "tab_groups": { + "general": "عام", + "account": "الحساب والهوية", + "organization": "تنظيم البريد", + "apps": "التطبيقات", + "system": "النظام", + "appearance": "المظهر", + "mail": "البريد", + "privacy": "الخصوصية والأمان", + "advanced": "متقدم" + }, + "appearance": { + "message_list_order": { + "label": "ترتيب قائمة الرسائل", + "description": "كيفية ترتيب الرسائل في القائمة. يقوم الخادم بالترتيب، لذا يشمل المجلد بأكمله وليس الرسائل المحمّلة فقط.", + "preset": { + "chronological": "زمني (الأحدث أولاً)", + "unread_first": "غير المقروءة أولاً", + "starred_first": "المميزة بنجمة أولاً", + "tagged_first": "الموسومة أولاً", + "custom": "مخصص" + }, + "tag_label": "الوسم", + "advanced_show": "متقدم: تحرير مستويات الترتيب", + "advanced_hide": "إخفاء الخيارات المتقدمة", + "criterion_label": "معيار المستوى {n}", + "direction_label": "اتجاه المستوى {n}", + "criteria": { + "unread": "غير مقروءة", + "starred": "مميزة بنجمة", + "tag": "الوسم", + "receivedAt": "تاريخ الاستلام", + "sentAt": "تاريخ الإرسال", + "from": "المرسل", + "subject": "الموضوع", + "size": "الحجم" + }, + "direction": { + "unread_first": "غير المقروءة أولاً", + "read_first": "المقروءة أولاً", + "starred_first": "المميزة بنجمة أولاً", + "unstarred_first": "غير المميزة أولاً", + "tagged_first": "الموسومة أولاً", + "untagged_first": "غير الموسومة أولاً", + "newest_first": "الأحدث أولاً", + "oldest_first": "الأقدم أولاً", + "a_to_z": "أ → ي", + "z_to_a": "ي → أ", + "largest_first": "الأكبر أولاً", + "smallest_first": "الأصغر أولاً" + }, + "add_level": "إضافة مستوى", + "remove_level": "إزالة المستوى", + "move_up": "نقل لأعلى", + "move_down": "نقل لأسفل", + "tiebreak_note": "حتى ثلاثة مستويات؛ تحسم المستويات اللاحقة حالات التعادل. يُطبَّق «الأحدث أولاً» دائمًا في النهاية.", + "scope": { + "label": "تطبيق على جميع المجلدات", + "description": "عند الإيقاف، يُطبَّق الترتيب على صندوق الوارد فقط؛ وتبقى المجلدات الأخرى مرتبة زمنيًا." + }, + "unsupported_note": "خادم البريد لديك لا يدعم الترتيب حسب حالة القراءة أو النجمة أو الوسوم. يتم تجاهل هذه المستويات وتبقى القائمة مرتبة زمنيًا." + }, + "title": "المظهر", + "description": "تخصيص شكل ومظهر البريد الإلكتروني على الويب", + "theme": { + "label": "السمة", + "description": "اختر نظام الألوان المفضل لديك", + "light": "فاتح", + "dark": "داكن", + "system": "النظام" + }, + "language": { + "label": "اللغة", + "description": "اختر لغتك المفضلة" + }, + "font_size": { + "label": "حجم الخط", + "description": "اضبط حجم النص لقراءة أفضل", + "small": "صغير", + "medium": "متوسط", + "large": "كبير" + }, + "list_density": { + "label": "الكثافة", + "description": "التحكم في التباعد والحشو عبر الواجهة", + "extra_compact": "مضغوط جدًا", + "compact": "مضغوط", + "regular": "عادي", + "comfortable": "مريح" + }, + "interface_layout": { + "label": "تخطيط الواجهة", + "description": "موضع البحث والتنقل وأشرطة الأدوات", + "default": "افتراضي", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "تفعيل الحركات", + "description": "إظهار الانتقالات والتأثيرات السلسة" + }, + "toolbar_position": { + "label": "موضع شريط الأدوات", + "description": "أين تظهر أزرار إجراءات البريد (رد، أرشفة، حذف، إلخ)", + "top": "أعلى", + "below_subject": "أسفل الموضوع" + }, + "toolbar_labels": { + "label": "إظهار تسميات شريط الأدوات", + "description": "عرض تسميات نصية بجانب أيقونات شريط الأدوات. عطّلها لتوفير المساحة بعد أن تعتاد على الأيقونات." + }, + "hide_account_switcher": { + "label": "إخفاء مبدّل الحسابات في الشريط الجانبي", + "description": "إخفاء محدد الحساب أعلى الشريط الجانبي للمجلدات. يمكنك مع ذلك تبديل الحسابات من شريط التنقل السفلي." + }, + "show_rail_account_list": { + "label": "إظهار صور الحسابات على شريط التنقل", + "description": "عرض دوائر الحسابات الفردية أسفل شريط التنقل للتبديل السريع، مع زر تسجيل خروج أسفلها." + }, + "unified_mailbox": { + "label": "صندوق البريد الموحّد", + "description": "إظهار المجلدات المجمّعة (البريد الوارد، المرسل، إلخ) للحساب النشط ومجلداته المشتركة.", + "cross_account": { + "label": "عبر جميع الحسابات", + "description": "دمج صندوق البريد الموحّد عبر كل حساب متصل بدلًا من الاقتصار على الحساب النشط." + }, + "include_group": { + "label": "تضمين صناديق البريد الجماعية", + "description": "دمج صناديق البريد المشتركة/الجماعية أيضًا في العرض الموحّد." + } + }, + "all_mail": { + "folders_label": "المجلدات في القوائم الموحّدة", + "folders_description": "اختر أي مجلدات هذا الحساب تُدمج في قوائم كل البريد / غير المقروء / المميز بنجمة.", + "account_hint": "ينطبق على {account}.", + "no_folders": "لا توجد مجلدات متاحة." + }, + "colorful_sidebar_icons": { + "label": "أيقونات ملونة في الشريط الجانبي", + "description": "تلوين أيقونات المجلدات والوسوم حسب النوع (أزرق للبريد الوارد، أحمر للمزعج، أخضر للمرسل، إلخ). عطّل هذا لشريط جانبي أحادي اللون." + }, + "tint_list_rows": { + "label": "تلوين صفوف القائمة بلون الوسم", + "description": "تظليل كل صف رسالة بلون أول وسم فيها. عطّل هذا لإبقاء الصفوف عادية؛ ستظل نقاط الوسوم وشاراتها تظهر اللون." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "إظهار إجمالي عدد الرسائل", + "description": "إظهار إجمالي عدد الرسائل بجانب المجلدات والوسوم، إلى جانب عدد غير المقروء. عطّل هذا لإظهار عدد غير المقروء فقط." + }, + "favicon_unread_badge": { + "label": "عدد غير المقروء على أيقونة علامة التبويب", + "description": "إظهار عدد رسائل البريد الوارد غير المقروءة كشارة على أيقونة علامة تبويب المتصفح، بحيث يظهر البريد الجديد عند عدم التركيز على علامة التبويب. عطّل هذا لإبقاء الأيقونة بلا شارة." + }, + "show_shared_mail_accounts": { + "label": "إظهار حسابات البريد المشتركة", + "description": "عرض مجلدات صناديق البريد المشتركة معك تحت \"مشترك\" في الشريط الجانبي. أوقفه لإخفائها كلها، أو أخفِ حسابات بعينها أدناه. تبقى تقاويمها وجهات اتصالها ظاهرة." + }, + "pro_interface": { + "label": "الواجهة الاحترافية (تجريبي)", + "description": "تخطيط للمستخدمين المتقدمين لسطح المكتب فقط، مع تصفح رسائل بعلامات تبويب متعددة وسير عمل عبر الحسابات. الواجهة القياسية لا تتأثر؛ يمكنك العودة إليها في أي وقت.", + "open_label": "فتح الواجهة الاحترافية", + "back_to_standard": "العودة إلى القياسية" + }, + "cross_unread": { + "label": "غير مقروء", + "description": "إظهار عنصر \"غير مقروء\" في صندوق البريد الموحّد يسرد البريد غير المقروء عبر المجلدات المحددة." + }, + "cross_starred": { + "label": "مميز بنجمة", + "description": "إظهار عنصر \"مميز بنجمة\" في صندوق البريد الموحّد يسرد البريد المميز بنجمة عبر المجلدات المحددة." + }, + "cross_all": { + "label": "كل البريد", + "description": "إظهار عنصر \"كل البريد\" في صندوق البريد الموحّد يسرد كل البريد عبر المجلدات المحددة." + } + }, + "keywords": { + "title": "وسوم البريد", + "description": "عرّف وسومًا لتنظيم رسائلك بالألوان. تُخزَّن هذه ككلمات مفتاحية JMAP على الخادم.", + "add_keyword": "إضافة وسم", + "label_field": "الاسم المعروض", + "label_placeholder": "مثال: عمل، شخصي، عاجل", + "id_field": "معرّف الوسم", + "id_placeholder": "مثال: work, personal", + "color_field": "اللون", + "id_exists": "معرّف الوسم هذا موجود بالفعل", + "edit": "تعديل الوسم", + "delete": "حذف الوسم", + "save": "حفظ", + "add": "إضافة", + "cancel": "إلغاء", + "migrating": "جارٍ تحديث الوسم على الرسائل الحالية…", + "migration_error": "فشل تحديث الوسم على الرسائل الحالية", + "filters_migration_error": "لا تزال بعض قواعد التصفية تستخدم الوسم القديم — حدّثها يدويًا", + "nesting": { + "label": "وسوم متداخلة", + "description": "ضع الوسوم داخل وسوم أخرى واعرضها كشجرة في الشريط الجانبي." + }, + "parent_field": "الوسم الأصل", + "no_parent": "بدون وسم أصل", + "too_long": "مسار الوسم طويل جدًا ({max} حرفًا على الأكثر)", + "has_children_locked": "توجد وسوم أخرى متداخلة تحت هذا الوسم، لذا فإن اسمه ووسمه الأصل مقفلان. انقلها أو احذفها أولًا.", + "has_children_delete": "احذف أولًا الوسوم المتداخلة تحت هذا الوسم", + "visibility_field": "الظهور في الشريط الجانبي", + "visibility": { + "show": "إظهار", + "unread": "إظهار عند وجود غير مقروء", + "hide": "إخفاء" + }, + "unrecognized": { + "title": "استعادة الوسوم من الخادم", + "description": "تُخزَّن أسماء الوسوم وألوانها في هذا المتصفح، لكن الوسوم نفسها موجودة على رسائلك ككلمات مفتاحية JMAP. افحص بريدك بحثًا عن كلمات مفتاحية لا يفسّرها أي وسم هنا، ثم أعِد إضافتها.", + "scan": "البحث عن الوسوم", + "rescan": "إعادة الفحص", + "scanning": "جارٍ الفحص… {scanned} من {total} رسالة", + "none": "كل وسم على رسائلك معرَّف هنا بالفعل.", + "messages": "{count, plural, one {رسالة واحدة} other {# رسالة}}", + "add_all": "إضافة الكل ({count})", + "partial": "تم فحص أحدث {scanned} رسالة فقط من أصل {total}. قد تكون الوسوم المستخدمة في البريد الأقدم فقط غير مدرجة.", + "error": "تعذّر فحص بريدك بحثًا عن الوسوم." + } + }, + "notifications": { + "test_sound": "اختبار صوت الإشعار", + "sounds": { + "default": "افتراضي (تنبيه)", + "cheerful": "مرح", + "involved": "متفاعل", + "swift": "إيماءة سريعة", + "relax": "استرخاء" + }, + "push": { + "title": "الإشعارات في الخلفية", + "description": "تلقّي إشعارات النظام للبريد الجديد عند إغلاق هذا الموقع. تُسلَّم عبر خادم ترحيل Bulwark؛ ولا يطّلع الترحيل على محتوى البريد أبدًا.", + "relay_label": "خادم ترحيل الإشعارات", + "relay_desc": "يستخدم افتراضيًا خادم ترحيل Bulwark المستضاف. غيّره فقط إذا كنت تستضيف بنفسك.", + "relay_locked": "تم تعيينه بواسطة المسؤول", + "relay_locked_desc": "تم تعيين رابط خادم الترحيل بواسطة المسؤول ولا يمكن تغييره.", + "status_active": "نشط على هذا الجهاز", + "status_inactive": "غير مفعّل على هذا الجهاز", + "status_unsupported": "هذا المتصفح لا يدعم Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "جارٍ العمل…", + "enable": "تفعيل", + "reenable": "إعادة التسجيل", + "disable": "تعطيل", + "confirm_disable_title": "تعطيل الإشعارات في الخلفية؟", + "confirm_disable_message": "سيتوقف هذا الجهاز عن تلقي التنبيهات عند إغلاق الموقع.", + "dismiss_aria": "تجاهل مطالبة الإشعارات", + "ios_hint": "على iOS، ثبّت الموقع على الشاشة الرئيسية أولًا - يقدّم Safari خدمة Web Push فقط لتطبيقات PWA المثبّتة.", + "devices_title": "الأجهزة المسجّلة", + "devices_desc": "كل الأجهزة المسجّلة لتلقّي الإشعارات في الخلفية لهذا الحساب. إلغاء أي جهاز يوقف تلقّيه فورًا.", + "devices_loading": "جارٍ تحميل الأجهزة المسجّلة…", + "devices_empty": "لا توجد أجهزة مسجّلة للإشعارات في الخلفية.", + "devices_error": "تعذّر تحميل الأجهزة المسجّلة.", + "devices_refresh": "تحديث", + "device_this": "هذا الجهاز", + "device_other": "جهاز آخر", + "device_status_active": "يسلّم الإشعارات", + "device_status_inactive": "لم يسلّم أي إشعار قط", + "device_status_unknown": "غير معروف لهذا المُرحّل", + "device_expires": "ينتهي في {date}", + "revoke": "إلغاء التسجيل", + "confirm_revoke_title": "إلغاء تسجيل هذا الجهاز؟", + "confirm_revoke_message": "سيتوقّف فورًا عن تلقّي الإشعارات في الخلفية. تفعيل الدفع مجددًا على ذلك الجهاز سيسجّله من جديد.", + "confirm_revoke_message_this": "سيتوقّف هذا الجهاز فورًا عن تلقّي الإشعارات في الخلفية." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "صوت الإشعار", + "description": "اختر الصوت الذي يُشغَّل للإشعارات", + "choose": "الصوت", + "choose_desc": "اختر نغمة إشعار وانقر على أيقونة السماعة لمعاينتها" + }, + "email": { + "title": "إشعارات البريد", + "description": "إعداد الإشعارات للرسائل الواردة", + "enabled": "إشعارات البريد", + "enabled_desc": "إظهار الإشعارات عند وصول رسائل جديدة", + "sound": "صوت الإشعار", + "sound_desc": "تشغيل تنبيه صوتي عند وصول رسائل جديدة", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "إشعارات التقويم", + "description": "إعداد الإشعارات لأحداث التقويم", + "enabled": "إشعارات الأحداث", + "enabled_desc": "إظهار تنبيهات لأحداث التقويم القادمة", + "sound": "صوت الإشعار", + "sound_desc": "تشغيل تنبيه صوتي لتذكيرات التقويم", + "invitation_parsing": "تحليل دعوات البريد الإلكتروني", + "invitation_parsing_desc": "اكتشاف دعوات التقويم في مرفقات البريد وإظهار إجراءات التقويم" + } + }, + "language_region": { + "title": "اللغة والمنطقة والوقت", + "description": "اللغة، وتنسيق التاريخ، وتنسيق الوقت، وتفضيلات إقليمية أخرى", + "language": { + "label": "اللغة", + "description": "اختر لغتك المفضلة", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "تنسيق التاريخ", + "description": "كيفية عرض التواريخ في قائمة البريد", + "smart": "ذكي", + "relative": "نسبي", + "full": "التاريخ الكامل دائمًا", + "preview_today": "اليوم:", + "preview_this_week": "هذا الأسبوع:", + "preview_older": "أقدم:" + }, + "date_locale": { + "label": "منطقة تنسيق التاريخ", + "description": "كيفية ترتيب التاريخ الرقمي (يوم، شهر، سنة)", + "auto": "تلقائي", + "iso": "ISO 8601", + "dmy": "يوم/شهر/سنة", + "mdy": "شهر/يوم/سنة" + }, + "time_format": { + "label": "تنسيق الوقت", + "description": "اختر بين نظام 12 ساعة أو 24 ساعة", + "12h": "12 ساعة", + "24h": "24 ساعة" + }, + "first_day": { + "label": "أول يوم في الأسبوع", + "description": "ابدأ الأسبوع بالأحد أو الاثنين", + "sunday": "الأحد", + "monday": "الاثنين" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "مسح البحث عند تبديل المجلدات", + "description": "النقر على مجلد (البريد الوارد، المرسل…) يمسح البحث النشط ويعرض ذلك المجلد. عند الإيقاف، يبقى البحث ويُعاد تشغيله في المجلد الذي تنتقل إليه." + }, + "request_read_receipt": { + "label": "طلب إيصالات القراءة افتراضيًا", + "description": "تفعيل طلب إيصال القراءة مسبقًا عند إنشاء رسالة جديدة." + }, + "read_receipt_response": { + "label": "الرد على طلبات إيصال القراءة", + "description": "ما الإجراء عند طلب رسالة واردة إيصال قراءة.", + "ask": "السؤال في كل مرة", + "always": "الإرسال دائمًا", + "never": "عدم الإرسال أبدًا" + }, + "title": "سلوك البريد", + "description": "إعداد كيفية التعامل مع الرسائل", + "mark_read": { + "label": "التمييز كمقروءة", + "description": "متى تُمَيَّز الرسائل كمقروءة عند فتحها", + "instant": "فورًا", + "delay_3s": "بعد 3 ثوانٍ", + "delay_5s": "بعد 5 ثوانٍ", + "never": "أبدًا" + }, + "delete_action": { + "label": "إجراء الحذف", + "description": "ما الذي يحدث عند حذف رسالة", + "trash": "النقل إلى المهملات", + "trash_and_read": "النقل إلى المهملات والتمييز كمقروءة", + "permanent": "الحذف نهائيًا", + "warning": "سيتم حذف الرسائل نهائيًا ولا يمكن استرجاعها. هذا الإجراء لا رجعة فيه." + }, + "message_spacing": { + "label": "تباعد الرسالة", + "description": "الهوامش حول نص الرسالة في وضع القراءة", + "auto": "تلقائي (مستحسن)", + "always": "إضافة تباعد دائمًا", + "edge": "من الحافة إلى الحافة" + }, + "plain_text_font": { + "label": "خط النص العادي", + "description": "الخط المستخدم لرسائل النص العادي", + "mono": "أحادي المسافة", + "sans": "عادي (مثل التطبيق)" + }, + "archive_mode": { + "label": "الأرشفة في", + "description": "كيفية تنظيم الرسائل عند الأرشفة", + "single": "مجلد واحد", + "year": "مجلد لكل سنة", + "month": "مجلد لكل شهر", + "reorganize": "إعادة تنظيم الأرشيف الحالي", + "reorganize_success": "{count, plural, =0 {لا توجد رسائل لإعادة تنظيمها} =1 {تمت إعادة تنظيم رسالة واحدة} other {تمت إعادة تنظيم # رسالة}}", + "reorganize_error": "فشلت إعادة تنظيم الأرشيف" + }, + "permanently_delete_junk": { + "label": "حذف البريد المزعج نهائيًا", + "description": "حذف الرسائل من مجلد البريد المزعج نهائيًا بدلًا من نقلها إلى المهملات" + }, + "confirm_bulk_delete": { + "label": "التأكيد قبل حذف عدة رسائل", + "description": "السؤال قبل نقل عدة رسائل محددة إلى سلة المهملات. الحذف النهائي يطلب التأكيد دائمًا." + }, + "mail_layout": { + "label": "تخطيط البريد", + "description": "اختر بين جزء القراءة المقسّم الكلاسيكي، أو تدفق قراءة مركّز على طراز Gmail، أو جزء قراءة سفلي على طراز Zimbra.", + "split": "جزء مقسّم", + "split_description": "إبقاء قائمة الرسائل وجزء القراءة ظاهرين جنبًا إلى جنب.", + "focus": "قائمة مركّزة", + "focus_description": "إظهار سطر واحد لكل رسالة وفتح البريد بعرض كامل مع إبقاء الشريط الجانبي للمجلدات ظاهرًا.", + "horizontal": "جزء القراءة أسفل", + "horizontal_description": "إظهار قائمة الرسائل أعلى وفتح الرسالة المحددة في جزء قراءة أسفلها." + }, + "list_columns": { + "label": "عرض القائمة كأعمدة", + "description": "عرض الصفوف ذات السطر الواحد كجدول بأعمدة المرسل والموضوع والتاريخ والحجم أسفل ترويسة." + }, + "show_preview": { + "label": "إظهار نص المعاينة", + "description": "عرض معاينة الرسالة في القائمة", + "focus_description": "عرض نص معاينة ضمن السطر في قائمة الرسائل المركّزة" + }, + "show_verification_codes": { + "label": "إظهار رموز التحقق", + "description": "عرض الرمز من رسائل تسجيل الدخول والتأكيد لنسخه بنقرة واحدة، في القائمة لمدة يوم وفي الرسالة" + }, + "disable_threading": { + "label": "تعطيل تجميع المحادثات", + "description": "إظهار الرسائل كرسائل منفردة بدلًا من تجميعها حسب المحادثة" + }, + "plain_text_mode": { + "label": "نص عادي فقط", + "description": "تعطيل محرر النصوص المنسقة وإرسال جميع الرسائل كنص عادي فقط، بما في ذلك الردود وإعادة التوجيه" + }, + "auto_select_reply_identity": { + "label": "الرد من العنوان المستلَم إليه", + "description": "عند الرد، الإرسال من العنوان الذي أُرسلت إليه الرسالة أصلًا. يطابق الهويات أولًا؛ وبالنسبة لعمليات التسليم الشاملة للنطاق، يعيد كتابة ترويسة \"من\" إلى العنوان البديل مع الإرسال عبر هويتك الأساسية." + }, + "reply_identity_match": { + "label": "مطابقة عنوان الاستلام", + "description": "أي عناوين الاستلام تُعدّ عناوينك. «العنوان المطابق فقط» يختار إحدى هوياتك المضبوطة فقط؛ أما «نفس النطاق» فيعامل أيضًا أي عنوان آخر على نطاقات هوياتك كاسم مستعار شامل ويستبدل به ترويسة المرسل. اختر العنوان المطابق إذا كانت تلك العناوين قوائم توزيع.", + "exact": "العنوان المطابق فقط", + "domain": "أي عنوان على نطاقاتي" + }, + "signature_position": { + "label": "موضع التوقيع", + "description": "أين يُدرج توقيعك في الردود وإعادة التوجيه. أعلى النص المقتبس يُقرأ بشكل طبيعي كخاتمة للرد؛ وأسفله يُبقي الرسالة الأصلية متصلة.", + "above_quote": "قبل النص المقتبس", + "below_quote": "بعد النص المقتبس" + }, + "signature_separator": { + "label": "فاصل التوقيع", + "description": "إضافة السطر الفاصل القياسي \"-- \" قبل التوقيع (RFC 3676). عطّله إذا كنت تفضّل الانتقال مباشرة من رسالتك إلى التوقيع." + }, + "sub_address_delimiter": { + "label": "فاصل العنوان الفرعي", + "description": "الحرف الذي يفصل اسم مستخدمك عن وسم العنوان الفرعي. طابق الفاصل الذي يستخدمه خادم بريدك (مثال: user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "مخصص…", + "custom_input_label": "حرف فاصل مخصص" + }, + "attachment_click_action": { + "label": "إجراء النقر على المرفق", + "description": "اختر ما إذا كان النقر على مرفق ملف يعاينه أو يحمّله فورًا", + "preview": "المعاينة عند الإمكان", + "download": "التنزيل فورًا" + }, + "attachment_position": { + "label": "موضع المرفق", + "description": "أين تُعرض المرفقات في ترويسة الرسالة", + "beside-sender": "بجانب المرسل", + "below-header": "أسفل الترويسة" + }, + "emails_per_page": { + "10": "10 رسائل", + "25": "25 رسالة", + "50": "50 رسالة", + "100": "100 رسالة", + "label": "عدد الرسائل في الصفحة", + "description": "عدد الرسائل التي تُحمَّل دفعة واحدة" + }, + "always_light_mode": { + "label": "إظهار الرسائل دائمًا في الوضع الفاتح", + "description": "عرض محتوى الرسائل في الوضع الفاتح حتى عندما يكون التطبيق في الوضع الداكن، لتجنب مشاكل تحويل الوضع الداكن" + }, + "external_content": { + "label": "المحتوى الخارجي", + "description": "كيفية التعامل مع الصور والمحتوى الخارجي", + "ask": "السؤال دائمًا", + "block": "الحظر دائمًا", + "allow": "السماح دائمًا" + }, + "trusted_senders": { + "label": "المرسلون الموثوقون", + "description": "إدارة المرسلين الذين تُحمَّل صورهم تلقائيًا", + "count_zero": "لا يوجد", + "count_one": "مرسل واحد", + "count_other": "{count} مرسلين", + "modal_title": "المرسلون الموثوقون", + "empty_title": "لا يوجد مرسلون موثوقون بعد", + "empty_description": "عند عرض رسالة بصور محظورة، انقر على \"الوثوق بهذا المرسل دائمًا\" لإضافته هنا.", + "add_manually": "إضافة مرسل يدويًا", + "add_button": "إضافة", + "add_placeholder": "أدخل عنوان البريد الإلكتروني", + "search_placeholder": "بحث عن مرسلين...", + "no_results": "لا يوجد مرسلون مطابقون لبحثك", + "remove": "إزالة", + "close": "إغلاق", + "invalid_email": "يرجى إدخال عنوان بريد إلكتروني صالح", + "already_added": "هذا المرسل موثوق به بالفعل", + "save_error": "فشل الحفظ - راجع سجل تصحيح جهات الاتصال للتفاصيل", + "use_address_book_label": "المزامنة مع دفتر العناوين", + "use_address_book_description": "تخزين المرسلين الموثوقين في دفتر عناوين مخصص \"المرسلون الموثوقون\" ليتم مزامنتهم عبر جميع أجهزتك" + }, + "hover_actions": { + "label": "إجراءات التمرير السريعة", + "description": "اختر الإجراءات السريعة التي تظهر عند التمرير فوق رسالة في القائمة", + "delete": "حذف", + "star": "تمييز بنجمة / إزالة", + "mark_read": "تمييز كمقروءة / غير مقروءة", + "archive": "أرشفة", + "tag": "وسم", + "spam": "تمييز كبريد مزعج", + "not_spam": "ليس مزعجًا", + "none_selected": "لم يتم تحديد أي إجراءات", + "mode_label": "وضع العرض", + "mode_inline": "ضمن الصف", + "mode_floating": "عائم", + "corner_label": "موضع العنصر العائم", + "corner_top-left": "أعلى اليسار", + "corner_top-right": "أعلى اليمين", + "corner_bottom-left": "أسفل اليسار", + "corner_bottom-right": "أسفل اليمين" + }, + "default_mail_program": { + "label": "برنامج البريد الافتراضي", + "description": "تسجيل {appName} كبرنامج البريد الافتراضي لديك لروابط mailto:", + "button": "تعيين كافتراضي", + "success": "تمت مطالبة المتصفح بالتعيين كافتراضي", + "error": "متصفحك لا يدعم هذه الميزة" + }, + "attachment_reminder": { + "label": "تذكير المرفقات", + "description": "التحذير قبل الإرسال عندما تذكر رسالتك مرفقات دون إرفاق ملف", + "keywords_label": "كلمات مفتاحية للتنبيه", + "keywords_description": "كلمات أو عبارات تُفعّل التذكير عند العثور عليها في رسالتك", + "add_placeholder": "إضافة كلمة مفتاحية...", + "add": "إضافة", + "remove": "إزالة" + }, + "empty_subject_warning": { + "label": "تحذير الموضوع الفارغ", + "description": "طلب تأكيد قبل إرسال رسالة بدون موضوع." + }, + "recipient_mentions": { + "label": "الإشارة إلى المستلمين باستخدام @", + "description": "تؤدي كتابة @ في الرسالة إلى اقتراح المستلمين وإدراج الاسم الأول للمستلم المختار. يختار Enter أو Tab الاسم المميز." + }, + "mention_adds_recipient": { + "label": "جهات الاتصال في الإشارات بـ @", + "description": "عند كتابة @ تُعرض أيضًا جهات اتصالك والأشخاص من الدليل. اختيار شخص ليس من المستلمين بعد يضيفه إلى الرسالة.", + "to": "إضافة إلى \"إلى\"", + "cc": "إضافة إلى \"نسخة\"", + "off": "عرض المستلمين فقط" + }, + "hide_inline_image_attachments": { + "label": "إخفاء الصور المضمّنة من المرفقات", + "description": "الصور المضمّنة في نص الرسالة لا تُدرج كمرفقات منفصلة" + }, + "attachment_image_previews": { + "label": "إظهار معاينات الصور في المرفقات", + "description": "عرض مرفقات الصور كبطاقات مصغّرة بدلًا من أيقونات الملفات العامة" + }, + "send_delay": { + "label": "التراجع عن الإرسال / تأخير الإرسال", + "description": "تأخير الإرسال العادي بفترة قصيرة من جانب الخادم.", + "off": "إيقاف", + "seconds": "{seconds} ثانية", + "unsupported": "الحساب الحالي لا يعلن عن دعم الإرسال المؤجل. لا يزال الإعداد محفوظًا للحسابات الأخرى." + }, + "return_to_list_after_action": { + "label": "العودة إلى القائمة بعد الحذف أو التمييز كغير مقروء", + "description": "بعد حذف الرسالة المفتوحة أو تمييزها كغير مقروءة، العودة إلى قائمة الرسائل بدلًا من فتح الرسالة التالية." + }, + "swipe_right_action": { + "label": "إجراء السحب لليمين (الجوال)", + "description": "على أجهزة اللمس، الإجراء الذي يُنفَّذ عند سحب رسالة إلى اليمين في القائمة." + }, + "swipe_left_action": { + "label": "إجراء السحب لليسار (الجوال)", + "description": "على أجهزة اللمس، الإجراء الذي يُنفَّذ عند سحب رسالة إلى اليسار في القائمة." + }, + "swipe_actions": { + "none": "لا شيء", + "archive": "أرشفة", + "delete": "حذف", + "mark_read": "تبديل حالة القراءة", + "star": "تبديل النجمة", + "spam": "تمييز كبريد مزعج" + }, + "rtl_editing": { + "label": "دعم الكتابة من اليمين إلى اليسار", + "description": "يضيف زر اتجاه إلى شريط أدوات المحرر حتى تتمكن من ضبط الفقرات من اليسار لليمين أو من اليمين لليسار" + } + }, + "composer": { + "title": "محرر الرسائل", + "description": "إعداد خيارات كتابة البريد الإلكتروني", + "autosave": { + "label": "فاصل الحفظ التلقائي", + "description": "عدد مرات حفظ المسودات تلقائيًا", + "30s": "كل 30 ثانية", + "1m": "كل دقيقة", + "2m": "كل دقيقتين", + "5m": "كل 5 دقائق" + }, + "send_confirmation": { + "label": "تأكيد الإرسال", + "description": "طلب تأكيد قبل إرسال الرسائل" + }, + "default_reply": { + "label": "وضع الرد الافتراضي", + "description": "الإجراء الافتراضي عند النقر على رد", + "reply": "رد", + "reply_all": "الرد على الجميع" + } + }, + "privacy": { + "title": "الخصوصية والأمان", + "description": "إدارة إعدادات الخصوصية والأمان", + "external_images": { + "label": "حظر الصور الخارجية", + "description": "منع التتبع عبر الصور الخارجية" + }, + "session_timeout": { + "label": "مهلة الجلسة", + "description": "تسجيل الخروج تلقائيًا بعد فترة من عدم النشاط", + "never": "أبدًا", + "30m": "30 دقيقة", + "1h": "ساعة واحدة", + "4h": "4 ساعات" + }, + "clear_cache": { + "label": "مسح ذاكرة التخزين المؤقت", + "description": "إزالة البيانات المخزنة مؤقتًا والملفات المؤقتة", + "button": "مسح ذاكرة التخزين المؤقت", + "confirm": "هل أنت متأكد أنك تريد مسح ذاكرة التخزين المؤقت؟", + "success": "تم مسح ذاكرة التخزين المؤقت بنجاح" + } + }, + "account": { + "title": "الحساب", + "description": "عرض معلومات حسابك", + "name_label": "الاسم المعروض", + "username_label": "اسم المستخدم", + "account_type_label": "نوع الحساب", + "auth_method_label": "المصادقة", + "auth_method_oauth": "تسجيل الدخول الموحّد (OAuth/OIDC)", + "auth_method_basic": "كلمة المرور", + "auth_method_token": "رمز الوصول", + "demo_account": "حساب تجريبي", + "email": { + "label": "عنوان البريد الإلكتروني", + "value": "{email}" + }, + "server": { + "label": "خادم JMAP", + "value": "{server}" + }, + "storage": { + "label": "استخدام التخزين", + "used": "{used} من {total} مستخدَم", + "percentage": "{percent}% مستخدَم" + }, + "last_sync": { + "label": "آخر مزامنة", + "value": "{time}" + }, + "accounts": { + "title": "الحسابات المسجَّلة", + "description": "اسحب لإعادة ترتيب ظهور الحسابات في قائمة الحسابات المنسدلة", + "active": "الحساب النشط حاليًا", + "default_badge": "الحساب الافتراضي", + "set_default": "تعيين كافتراضي", + "switch_to": "التبديل إلى هذا الحساب", + "move_up": "تحريك لأعلى", + "move_down": "تحريك لأسفل", + "drag_handle": "اسحب لإعادة الترتيب", + "add": "إضافة حساب" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "مشترك معي", + "description": "الحسابات الجماعية والمشتركة التي يمكنك إدارتها. اختر واحدًا لتعديل مرشحاته، والرد التلقائي أثناء الغياب، والتقويمات، وجهات الاتصال.", + "shared_label": "حساب مشترك" + } + }, + "scoped": { + "back": "العودة إلى حسابي", + "managing": "الإدارة: {name}" + }, + "security": { + "title": "أمان الحساب", + "description": "إدارة كلمة المرور والمصادقة الثنائية وإعدادات الأمان", + "detecting": "جارٍ اكتشاف قدرات الخادم...", + "not_available": "إدارة أمان الحساب غير متاحة لخادم البريد هذا. قد تكون الأذونات المطلوبة معطّلة. راجع الوثائق للتفاصيل.", + "password": { + "title": "تغيير كلمة المرور", + "current": "كلمة المرور الحالية", + "new": "كلمة المرور الجديدة", + "confirm": "تأكيد كلمة المرور الجديدة", + "submit": "تغيير كلمة المرور", + "success": "تم تغيير كلمة المرور بنجاح", + "error_title": "فشل تغيير كلمة المرور", + "error_mismatch": "كلمتا المرور الجديدتان غير متطابقتين", + "error_min_length": "يجب أن تتكون كلمة المرور من 8 أحرف على الأقل", + "error_generic": "فشل تغيير كلمة المرور" + }, + "display_name": { + "label": "الاسم المعروض", + "description": "اسمك كما يظهر على الخادم", + "placeholder": "أدخل اسمك المعروض", + "save": "حفظ", + "success": "تم تحديث الاسم المعروض", + "error": "فشل تحديث الاسم المعروض" + }, + "totp": { + "section_title": "المصادقة الثنائية", + "label": "مصادقة TOTP", + "description": "أضف طبقة حماية إضافية بكلمة مرور لمرة واحدة قائمة على الوقت", + "active": "مفعّلة", + "inactive": "معطّلة", + "enabled": "تم تفعيل المصادقة الثنائية", + "disabled": "تم تعطيل المصادقة الثنائية", + "enable_error": "فشل تفعيل المصادقة الثنائية", + "disable_error": "فشل تعطيل المصادقة الثنائية", + "setup_instructions": "انسخ هذا الرابط إلى تطبيق المصادقة الخاص بك (Google Authenticator، Authy، إلخ):", + "verification_code": "رمز التحقق", + "confirm": "تأكيد", + "disable": "تعطيل", + "disable_confirm_prompt": "أدخل كلمة مرورك لتعطيل المصادقة الثنائية.", + "password_required": "كلمة المرور مطلوبة", + "code_required": "رمز التحقق مطلوب", + "code_invalid": "رمز التحقق غير صالح. تحقق من تطبيق المصادقة وحاول مرة أخرى." + }, + "app_passwords": { + "title": "كلمات مرور التطبيقات", + "description": "أنشئ كلمات مرور للتطبيقات التي لا تدعم المصادقة الثنائية", + "add": "إضافة", + "create": "إنشاء", + "cancel": "إلغاء", + "done": "تم", + "generate": "توليد", + "name_label": "اسم التطبيق", + "name_placeholder": "مثال: Thunderbird، iPhone Mail", + "expires_label": "تاريخ الانتهاء (اختياري)", + "allowed_ips_label": "عناوين IP المسموح بها (اختياري)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "مفصولة بفواصل أو مسافات. اتركها فارغة للسماح بأي عنوان IP.", + "password_label": "كلمة المرور (اتركها فارغة للتوليد التلقائي)", + "password_placeholder": "تُولَّد تلقائيًا إذا تُركت فارغة", + "copy_now_warning": "انسخ كلمة المرور هذه الآن - لن تُعرض مرة أخرى.", + "added": "تم إنشاء كلمة مرور التطبيق", + "removed": "تمت إزالة كلمة مرور التطبيق", + "add_error": "فشل إنشاء كلمة مرور التطبيق", + "remove_error": "فشلت إزالة كلمة مرور التطبيق", + "none": "لا توجد كلمات مرور تطبيقات مُعدَّة" + }, + "api_keys": { + "title": "مفاتيح API", + "description": "أنشئ مفاتيح API للنصوص البرمجية والتكاملات التي تتواصل مع الخادم مباشرة", + "name_label": "اسم المفتاح", + "name_placeholder": "مثال: نص النسخ الاحتياطي، عامل CI", + "copy_now_warning": "انسخ مفتاح API هذا الآن - لن يُعرض مرة أخرى.", + "added": "تم إنشاء مفتاح API", + "removed": "تمت إزالة مفتاح API", + "add_error": "فشل إنشاء مفتاح API", + "remove_error": "فشلت إزالة مفتاح API", + "none": "لا توجد مفاتيح API مُعدَّة" + }, + "encryption": { + "section_title": "التشفير أثناء التخزين", + "label": "تشفير البريد", + "description": "تشفير الرسائل المخزنة على الخادم لمزيد من الخصوصية", + "active": "تشفير {type} مفعّل", + "inactive": "معطّل", + "enabled": "تم تفعيل التشفير أثناء التخزين", + "disabled_success": "تم تعطيل التشفير أثناء التخزين", + "error": "فشل تحديث إعدادات التشفير", + "algorithm_label": "خوارزمية التشفير", + "encrypt_on_append": "تشفير الرسائل الجديدة عند رفعها", + "allow_spam_training": "السماح بتدريب مرشّح البريد العشوائي قبل تشفير الرسائل", + "enable_encryption": "تفعيل التشفير", + "enable_tooltip": "تفعيل التشفير باستخدام هذا المفتاح", + "configure_title": "تفعيل التشفير باستخدام هذا المفتاح", + "enable_button": "تفعيل", + "disable_tooltip": "تعطيل التشفير", + "enabled_success": "تم تفعيل التشفير أثناء التخزين" + }, + "email_client": { + "title": "إعداد عميل البريد الإلكتروني", + "description": "استخدم بيانات الاعتماد هذه لإعداد عميل البريد على سطح المكتب أو الجوال (Thunderbird، Apple Mail، Outlook، إلخ)", + "jmap_username_label": "اسم مستخدم JMAP", + "copy": "نسخ", + "copied": "تم النسخ", + "password_instructions": "استخدم اسم مستخدم JMAP أعلاه مع كلمة مرور تطبيق لتسجيل الدخول إلى عميل البريد الخاص بك. أنشئ كلمة مرور تطبيق في القسم أعلاه إذا لم تفعل بعد." + }, + "link_device": { + "title": "ربط تطبيق الجوال", + "description": "سجّل الدخول إلى تطبيق Bulwark Mail على هاتفك دون كتابة كلمة المرور فيه. يحصل هاتفك على تسجيل دخول خاص به، لذا لن يؤدي تسجيل الخروج هنا إلى تسجيل خروجه.", + "generate": "إظهار رمز QR", + "regenerate": "إظهار رمز جديد", + "instructions": "افتح تطبيق Bulwark Mail واضغط على \"Scan a sign-in code\" في شاشة تسجيل الدخول (أو \"Add account\" → \"Scan a sign-in code\" إذا كنت مسجّل الدخول بالفعل)، ثم وجّه الكاميرا إلى هنا.", + "expires_in": "تنتهي صلاحية هذا الرمز خلال {seconds} ثانية. يمكن استخدامه مرة واحدة فقط.", + "expired": "انتهت صلاحية هذا الرمز.", + "error": "تعذّر إنشاء رمز الاقتران. يرجى المحاولة مرة أخرى.", + "password_prompt": "أدخل كلمة المرور لتأكيد هويتك. يحصل هاتفك على تسجيل دخول خاص به، ولا تُرسَل كلمة المرور إلى الهاتف.", + "show_password": "إظهار كلمة المرور", + "hide_password": "إخفاء كلمة المرور", + "continue": "متابعة", + "error_wrong_password": "كلمة المرور غير صحيحة. حاول مرة أخرى.", + "totp_prompt": "أدخل الرمز من تطبيق المصادقة.", + "error_password_or_code": "كلمة المرور أو الرمز غير صحيح.", + "error_not_signed_in": "انتهت صلاحية جلستك. سجّل الدخول مرة أخرى لربط جهاز.", + "error_impersonation": "لا يمكن ربط الأجهزة أثناء عرض صندوق بريد مستخدم آخر.", + "error_too_many_attempts": "محاولات كثيرة جدًا. انتظر قليلًا ثم حاول مرة أخرى.", + "error_session_secret": "ربط الأجهزة غير مُعدّ على هذا الخادم (SESSION_SECRET مفقود). تواصل مع المسؤول.", + "error_server_unreachable": "تعذّر الوصول إلى خادم البريد. حاول مرة أخرى.", + "error_pairing_unavailable": "لا يسمح خادم البريد لديك بربط الأجهزة بهذه الطريقة. تواصل مع المسؤول.", + "qr_alt": "رمز QR لتسجيل الدخول", + "link_label": "رابط تسجيل الدخول", + "copy_link": "نسخ رابط تسجيل الدخول", + "copied": "تم النسخ", + "copy_hint": "لا توجد كاميرا؟ انسخ الرابط والصقه في التطبيق عبر \"Paste sign-in link\".", + "success_title": "تم تسجيل الدخول على هاتفك", + "success_body": "يمكنك إغلاق هذه الصفحة. لربط جهاز آخر، اعرض رمزًا جديدًا.", + "link_another": "ربط جهاز آخر", + "use_sso": "استخدم تسجيل الدخول الموحّد بدلاً من ذلك", + "error_insecure_server": "يتصل تطبيق الهاتف بخوادم البريد عبر https فقط، وهذا الخادم يستخدم http." + }, + "public_keys": { + "name_label": "اسم المفتاح العام", + "name_placeholder": "مثال: مفتاحي", + "key_label": "المفتاح العام (S/MIME أو PGP بترميز ASCII)", + "key_placeholder": "الصق مفتاحك العام هنا", + "none": "لا توجد مفاتيح عامة مُعدّة" + } + }, + "identities": { + "title": "هويات الإرسال", + "description": "إدارة عناوين البريد الإلكتروني التي يمكنك الإرسال منها", + "identities_count": { + "label": "هوياتك", + "description": "عناوين البريد الإلكتروني المُعدَّة للإرسال", + "count_zero": "لا توجد هويات", + "count_one": "هوية واحدة", + "count_other": "{count} هويات" + }, + "manage": "إدارة الهويات", + "sub_addressing": { + "label": "العنونة الفرعية", + "description": "استخدم وسومًا مثل user+tag@domain.com لتنظيم البريد الوارد", + "learn_more": "معرفة المزيد" + } + }, + "vacation": { + "title": "الغياب", + "description": "التعامل تلقائيًا مع الرسائل الواردة أثناء غيابك", + "loading": "جارٍ تحميل إعدادات الغياب...", + "not_supported": "خادم بريدك لا يدعم ردود الغياب.", + "fetch_error": "فشل تحميل إعدادات الغياب. يرجى المحاولة مرة أخرى.", + "status": { + "label": "إرسال رد تلقائي", + "description": "إرسال رد تلقائي إلى من يرسل إليك بريدًا", + "active": "مفعّل", + "inactive": "معطّل" + }, + "date_range": { + "start": "تاريخ البدء", + "start_description": "اتركه فارغًا لعدم وجود حد للبدء", + "end": "تاريخ الانتهاء", + "end_description": "اتركه فارغًا لعدم وجود حد للانتهاء" + }, + "message": { + "subject_label": "الموضوع", + "subject_description": "سطر موضوع الرد التلقائي", + "subject_placeholder": "خارج المكتب", + "body_label": "نص الرسالة", + "body_description": "محتوى الرسالة النصي العادي", + "body_placeholder": "شكرًا لرسالتك. أنا حاليًا خارج المكتب وسأرد عند عودتي.", + "html_label": "رسالة منسّقة (HTML)", + "html_description": "أضِف نسخة منسّقة تتضمّن روابط وتنسيقًا. سيرى المستلمون الذين لا يستطيع برنامج بريدهم عرضها النصّ العادي أعلاه.", + "html_placeholder": "اكتب ردّ غياب منسّقًا…" + }, + "preview": { + "title": "معاينة", + "show": "إظهار المعاينة", + "hide": "إخفاء المعاينة" + }, + "save": "حفظ التغييرات", + "saving": "جارٍ الحفظ...", + "warnings": { + "end_before_start": "يجب أن يكون تاريخ الانتهاء بعد تاريخ البدء", + "invalid_date": "أدخل تاريخ بدء وانتهاء صالحين مع الوقت، أو اترك الحقل فارغًا", + "start_in_past": "تاريخ البدء في الماضي", + "empty_body": "نص الرسالة فارغ - سيتلقى المستلمون ردًا فارغًا", + "subject_too_long": "الموضوع طويل جدًا بالنسبة للخادم (بحد أقصى {max} بايت).", + "body_too_long": "الرسالة طويلة جدًا بالنسبة للخادم (بحد أقصى {max} بايت بما في ذلك التنسيق).", + "forward_address": "أدخل عنوانًا صالحًا لإعادة التوجيه إليه", + "not_running": "إعادة التوجيه أو مستلمو الرد محفوظون لكنهم غير مفعّلين الآن. احفظ لإعادة تفعيلهم." + }, + "forward": { + "title": "إعادة التوجيه", + "description": "تُعيد توجيه الرسائل الواردة إلى عنوان آخر، مع الرد التلقائي أو بدونه، خلال الفترة أعلاه (بدون تاريخ انتهاء، حتى يتم إيقافها). لا تتم إعادة توجيه الرسائل غير المرغوب فيها.", + "enabled_label": "إعادة توجيه الرسائل", + "to_label": "إعادة التوجيه إلى", + "to_placeholder": "email@example.com", + "keep_label": "الإبقاء في الوارد", + "keep_description": "وإلا فستتم إعادة توجيه الرسالة فقط: لن تبقى في صندوق البريد هذا، ولن تُطبَّق عليها أي قاعدة تصفية." + }, + "audience": { + "label": "الرد على", + "description": "المرسلون الداخليون: {domains}", + "all": "جميع المرسلين", + "internal": "المرسلون الداخليون فقط", + "external": "المرسلون الخارجيون فقط" + }, + "auto_reply": { + "title": "الرد التلقائي" + } + }, + "folders": { + "role_memos": "الملاحظات", + "title": "المجلدات", + "description": "إدارة مجلدات بريدك وتعيين الأدوار القياسية", + "folder_list": "مجلداتك", + "folder_list_description": "انقر على أيقونة مجلد لتخصيصه", + "standard_roles": "أدوار المجلدات القياسية", + "standard_roles_description": "عيّن أي المجلدات تُستخدم للأدوار القياسية لصندوق البريد مثل الوارد، المرسل، المهملات، إلخ.", + "role_inbox": "البريد الوارد", + "role_drafts": "المسودات", + "role_sent": "المرسل", + "role_trash": "المهملات", + "role_junk": "البريد المزعج", + "role_archive": "الأرشيف", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "بلا", + "create_folder": "إنشاء مجلد", + "create_subfolder": "إنشاء مجلد فرعي", + "reorder": "اسحب لإعادة الترتيب", + "reorder_error": "فشل في إعادة ترتيب المجلدات", + "move_error": "فشل في نقل المجلد", + "select_folder": "تحديد {name}", + "select_hint": "تحديد. Shift+نقر يحدد كل المجلدات حتى آخر مجلد نقرت عليه.", + "selected_count": "{count, plural, one {# مجلد} other {# مجلدات}} محددة", + "move_to": "نقل إلى...", + "move_to_top_level": "المستوى الأعلى", + "move": "نقل", + "clear_selection": "إلغاء التحديد", + "folders_moved": "{count, plural, one {تم نقل # مجلد} other {تم نقل # مجلدات}}", + "move_partial_error": "{count, plural, one {تعذّر نقل # مجلد} other {تعذّر نقل # مجلدات}}", + "subfolder_of": "داخل {name}", + "subfolder_name": "اسم المجلد الفرعي", + "new_folder_name": "اسم المجلد", + "rename": "إعادة تسمية", + "change_icon": "تغيير الأيقونة", + "count_mode": "العداد لـ {name}", + "count_mode_default": "العداد: افتراضي", + "count_mode_unread": "غير المقروءة فقط", + "count_mode_all": "غير المقروءة والإجمالي", + "count_mode_none": "بدون عداد", + "delete": "حذف", + "confirm_delete": "هل أنت متأكد أنك تريد حذف \"{name}\"؟ ستُنقل الرسائل في هذا المجلد إلى المهملات.", + "create": "إنشاء", + "cancel": "إلغاء", + "no_folders": "لا توجد مجلدات مخصصة", + "cannot_delete_role": "لا يمكن حذف مجلد له دور قياسي. أزل الدور أولًا.", + "folder_created": "تم إنشاء المجلد", + "folder_renamed": "تمت إعادة تسمية المجلد", + "folder_deleted": "تم حذف المجلد", + "role_updated": "تم تحديث دور المجلد", + "error_create": "فشل إنشاء المجلد", + "error_rename": "فشلت إعادة تسمية المجلد", + "error_delete": "فشل حذف المجلد", + "error_delete_has_children": "لا يمكن حذف المجلد: لا يزال يحتوي على مجلدات فرعية. احذفها أو انقلها أولًا.", + "error_delete_has_email": "لا يمكن حذف المجلد: لا يزال يحتوي على رسائل. انقلها أو احذفها أولًا.", + "error_role": "فشل تحديث دور المجلد" + }, + "advanced": { + "title": "متقدم", + "description": "خيارات متقدمة وإعدادات المطورين", + "debug_mode": { + "label": "وضع التصحيح", + "description": "تفعيل التسجيل المفصّل لاستكشاف الأخطاء وإصلاحها" + }, + "debug_categories": { + "description": "اختر الفئات التي تريد تسجيلها. عطّل الفئات التي لا تحتاجها لتقليل ضجيج وحدة التحكم.", + "jmap": "عميل JMAP", + "jmap_description": "عمليات صناديق البريد، وجلب الرسائل، وطلبات بروتوكول JMAP", + "calendar": "التقويم", + "calendar_description": "أحداث التقويم، والاستيراد، ورسائل الجدولة", + "tasks": "المهام", + "tasks_description": "إنشاء مهام التقويم وجلبها وتحديثها", + "auth": "المصادقة", + "auth_description": "تسجيل الدخول، وTOTP، وتبادل الرموز، وإدارة الجلسات", + "filters": "المرشحات", + "filters_description": "قواعد مرشح Sieve ونصوص الغياب", + "email": "عرض البريد", + "email_description": "عرض الرسائل، ومعالجة TNEF، والتمييز كمقروء", + "push": "الإشعارات الفورية", + "push_description": "إعداد الإشعارات الفورية وتسليمها", + "contacts": "جهات الاتصال ودفاتر العناوين", + "contacts_description": "مزامنة جهات الاتصال، وعمليات دفتر العناوين، والمرسلون الموثوقون" + }, + "settings_sync": { + "label": "مزامنة الإعدادات", + "description": "مزامنة إعداداتك عبر المتصفحات والأجهزة" + }, + "sender_favicons": { + "label": "أيقونات مواقع المرسلين", + "description": "إظهار أيقونات المواقع كصور شخصية للمرسلين من الشركات", + "refresh_label": "تحديث أيقونات المرسلين", + "refresh_description": "جلب أيقونات المواقع مرة أخرى، مثلاً عندما يغيّر المرسل أيقونته", + "refresh_button": "تحديث", + "refresh_done": "سيتم جلب أيقونات المرسلين مرة أخرى" + }, + "show_avatars_in_junk": { + "label": "إظهار الصور الشخصية في مجلد البريد المزعج", + "description": "إظهار الصور الشخصية وأيقونات المواقع للمرسلين في مجلد البريد المزعج. معطّل افتراضيًا لتجنب إضفاء شرعية بصرية على محاولات التصيد الاحتيالي." + }, + "keyboard_shortcuts": { + "label": "اختصارات لوحة المفاتيح", + "description": "عرض اختصارات لوحة المفاتيح المتاحة", + "button": "عرض الاختصارات" + }, + "refresh_cache": { + "label": "تحديث البيانات المخزنة مؤقتًا", + "description": "إعادة تحميل جهات الاتصال والتقويمات والمجلدات من الخادم. يحافظ على حساباتك وجلساتك — يصلح العرض القديم أو الخاطئ دون تسجيل الخروج.", + "button": "تحديث" + }, + "reset_settings": { + "label": "إعادة تعيين الإعدادات", + "description": "استعادة جميع الإعدادات إلى القيم الافتراضية", + "button": "إعادة التعيين للافتراضي" + }, + "export_settings": { + "label": "تصدير الإعدادات", + "description": "تنزيل إعداداتك بصيغة JSON", + "button": "تصدير" + }, + "import_settings": { + "label": "استيراد الإعدادات", + "description": "رفع الإعدادات من ملف JSON", + "button": "استيراد" + }, + "about": { + "title": "بريد Bulwark الإلكتروني", + "source_code": "الشيفرة المصدرية" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "انقر على البريد المزعج ورسائل التصيّد قبل أن تصل إلى صندوق الوارد. دع البريد الحقيقي يمر. إذا خسرت الدروع الثلاثة يسقط صندوق الوارد.", + "kinds": { + "spam": "مزعج", + "phishing": "تصيّد", + "legit": "بريد حقيقي" + }, + "click_to_block": "انقر للحظر", + "let_through": "دعه يمر", + "start": "ابدأ", + "play_again": "العب مجددًا", + "score": "النقاط", + "combo": "مكافأة السلسلة", + "shields_left": "تبقى {count} من {total} دروع", + "seconds": "{seconds} ث", + "best": "الأفضل: {score}", + "new_best": "رقم قياسي جديد", + "held": "صمد صندوق الوارد", + "overrun": "سقط صندوق الوارد", + "stats": { + "caught": "محظورة", + "delivered": "مُسلَّمة", + "mistakes": "أخطاء" + } + } + }, + "sidebar_apps": { + "title": "تطبيقات الشريط الجانبي", + "description": "إدارة التطبيقات والروابط المخصصة في الشريط الجانبي", + "keep_loaded": "إبقاء التطبيقات محمّلة", + "keep_loaded_description": "إبقاء التطبيقات الداخلية تعمل في الخلفية عند التبديل بينها لتجنب إعادة التحميل", + "managed_title": "مقدَّمة من المسؤول", + "managed_description": "تم إعداد هذه التطبيقات للجميع ولا يمكن تغييرها.", + "manage_title": "التطبيقات المخصصة", + "manage_description": "إضافة أو تعديل أو إزالة التطبيقات المخصصة من الشريط الجانبي" + }, + "contacts": { + "title": "جهات الاتصال", + "description": "استيراد وتصدير جهات اتصالك", + "group_by_letter_label": "التجميع حسب الحرف الأول", + "sort_by_last_name_label": "الترتيب حسب اسم العائلة", + "sort_by_last_name_description": "ترتيب قائمة جهات الاتصال حسب اسم العائلة بحيث يظهر أفراد العائلة معًا", + "group_by_letter_description": "إظهار عناوين أقسام أبجدية في قائمة جهات الاتصال", + "import_label": "استيراد جهات الاتصال", + "import_description": "استيراد جهات الاتصال من ملف vCard (‎.vcf)", + "export_label": "تصدير جهات الاتصال", + "export_description": "تصدير جميع جهات الاتصال كملف vCard (‎.vcf)", + "manage_title": "دفاتر العناوين", + "manage_description": "إعادة تسمية دفاتر عناوينك", + "no_address_books": "لم يتم العثور على دفاتر عناوين", + "categories_title": "الفئات", + "categories_description": "إعادة تسمية فئات جهات الاتصال", + "no_categories": "لم يتم العثور على فئات" + }, + "downloads": { + "title": "التنزيلات", + "description": "تخصيص كيفية تسمية الرسائل والمرفقات التي تُنزَّل.", + "reset": "استعادة الافتراضي", + "preview": "معاينة:", + "email_template": { + "label": "اسم ملف البريد (‎.eml)", + "description": "القالب المستخدم عند تصدير رسالة أو سحبها إلى نظام الملفات. تُضاف امتداد ‎.eml تلقائيًا." + }, + "attachment_template": { + "label": "اسم ملف المرفق", + "description": "القالب المستخدم عند تنزيل مرفق أو سحبه للخارج. إذا حذفت '{filename}' و'{ext}'، يُحتفظ بالامتداد الأصلي." + }, + "bundle_template": { + "label": "اسم ملف ‎.zip لعدة رسائل", + "description": "القالب المستخدم عند سحب أو تنزيل عدة رسائل محددة كأرشيف ‎.zip واحد. يُضاف امتداد ‎.zip تلقائيًا." + }, + "spaces": { + "label": "المسافات", + "description": "استبدال المسافات في اسم الملف الناتج بحرف آخر.", + "keep": "الإبقاء على المسافات", + "underscore": "الاستبدال بـ _", + "dash": "الاستبدال بـ -" + }, + "lowercase": { + "label": "أحرف صغيرة", + "description": "فرض تحويل اسم الملف بالكامل إلى أحرف صغيرة." + }, + "strip_diacritics": { + "label": "إزالة علامات التشكيل", + "description": "تحويل الأحرف المنقّطة إلى ما يعادلها بترميز ASCII (ä ← a، é ← e). مفيد للأدوات التي تُشوّه أسماء الملفات بترميز Unicode." + }, + "collapse_separators": { + "label": "دمج الفواصل المتكررة", + "description": "دمج سلاسل المسافات أو الشرطات السفلية أو الشرطات في حرف واحد." + }, + "after_export": { + "label": "بعد التصدير", + "description": "يمكنك اختياريًا نقل الرسالة بعد تصديرها كملف ‎.eml.", + "keep": "الإبقاء في صندوق البريد", + "archive": "النقل إلى الأرشيف", + "trash": "النقل إلى المهملات" + } + }, + "filters": { + "title": "مرشحات البريد", + "description": "إنشاء قواعد لفرز الرسائل الواردة ووسمها وإدارتها تلقائيًا", + "add_rule": "إضافة قاعدة", + "no_rules": "لا توجد قواعد مرشح", + "no_rules_description": "أنشئ قواعد لتنظيم رسائلك الواردة تلقائيًا", + "vacation_active": "الرد التلقائي أثناء الغياب مفعّل", + "vacation_active_description": "الرد التلقائي مفعّل للرسائل الواردة", + "vacation_configure": "إعداد", + "edit_rule": "تعديل القاعدة", + "new_rule": "قاعدة جديدة", + "delete_rule": "حذف القاعدة", + "delete_confirm": "هل أنت متأكد أنك تريد حذف هذه القاعدة؟", + "enable": "تفعيل", + "disable": "تعطيل", + "raw_editor": "محرر Sieve الخام", + "raw_editor_warning": "قد يؤدي تعديل نص Sieve الخام إلى تعطيل التعديل المرئي للقواعد. التغييرات هنا تتجاوز أداة الإنشاء المرئي.", + "validate": "تحقق", + "validation_success": "النص صالح", + "validation_error": "النص يحتوي على أخطاء", + "save": "حفظ القواعد", + "saving": "جارٍ الحفظ...", + "saved": "تم حفظ المرشحات بنجاح", + "save_failed": "فشل حفظ المرشحات", + "loading": "جارٍ تحميل المرشحات...", + "not_supported": "خادم بريدك لا يدعم مرشحات البريد.", + "rule_name": "اسم القاعدة", + "rule_name_placeholder": "مثال: فرز النشرات البريدية", + "match_all": "مطابقة كل الشروط", + "match_any": "مطابقة أي شرط", + "conditions": "الشروط", + "add_condition": "إضافة شرط", + "actions": "الإجراءات", + "add_action": "إضافة إجراء", + "stop_processing": "إيقاف معالجة القواعد التالية", + "include_spam": "نقل الرسائل المصنفة كرسائل مزعجة أيضًا", + "attachment_type_placeholder": "مثال: pdf, doc, jpg", + "value_placeholder_multi": "القيمة (عدة قيم مفصولة بفواصل)", + "condition_fields": { + "attachment": "المرفق", + "from": "من", + "to": "إلى", + "cc": "نسخة", + "subject": "الموضوع", + "header": "ترويسة مخصصة", + "size": "الحجم", + "body": "النص", + "all": "جميع الرسائل" + }, + "comparators": { + "has_any": "موجود", + "has_type": "من النوع", + "contains": "يحتوي على", + "not_contains": "لا يحتوي على", + "is": "يساوي تمامًا", + "not_is": "لا يساوي", + "starts_with": "يبدأ بـ", + "ends_with": "ينتهي بـ", + "matches": "يطابق النمط", + "greater_than": "أكبر من", + "less_than": "أصغر من", + "address_is": "هو العنوان", + "domain_is": "نطاقه" + }, + "action_types": { + "move": "النقل إلى مجلد", + "copy": "النسخ إلى مجلد", + "forward": "إعادة التوجيه إلى", + "mark_read": "التمييز كمقروءة", + "star": "تمييز الرسالة بنجمة", + "add_label": "إضافة وسم", + "discard": "التجاهل (حذف صامت)", + "reject": "الرفض برسالة", + "keep": "الإبقاء في الوارد", + "stop": "إيقاف المعالجة" + }, + "move_to_folder": "اختر المجلد", + "copy_to_folder": "اختر المجلد", + "forward_to": "إعادة التوجيه إلى عنوان بريد إلكتروني", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "الاحتفاظ بنسخة", + "forward_limit": "حد إعادة التوجيه لكل رسالة على هذا الخادم: {count}. يتم تخطي عمليات إعادة التوجيه الإضافية.", + "forward_unavailable": "لا يسمح هذا الخادم بإعادة التوجيه. إجراءات إعادة التوجيه في قواعد التصفية ليس لها أي تأثير.", + "reject_message": "رسالة الرفض", + "reject_placeholder": "تم رفض رسالتك", + "label_name": "اسم الوسم", + "label_placeholder": "اختر وسمًا", + "header_name": "اسم الترويسة", + "header_placeholder": "مثال: X-Mailing-List", + "size_bytes": "الحجم بالبايت", + "size_placeholder": "مثال: 1000000", + "system_managed": "قاعدة تديرها المنظومة", + "opaque_warning": "تم تعديل هذا النص خارج أداة الإنشاء المرئي. يتوفر فقط تعديل Sieve الخام.", + "open_sieve_editor": "فتح محرر Sieve الخام", + "fetch_error": "فشل تحميل المرشحات", + "expanded_view": "عرض موسّع", + "expanded_view_description": "إظهار قواعد المرشح مع كتل الشروط والإجراءات المفصّلة", + "if": "إذا", + "then": "عندئذٍ", + "match_all_conditions": "تطابق الكل", + "match_any_condition": "تطابق أي منها", + "and": "و", + "or": "أو", + "cancel": "إلغاء", + "confirm_delete": "حذف", + "rule_list": "قواعد المرشح", + "drag_to_reorder": "اسحب لإعادة الترتيب", + "match_type": "نوع المطابقة", + "reset_to_visual": "إعادة التعيين إلى أداة الإنشاء المرئي", + "reset_warning": "سيؤدي هذا إلى تجاهل النص الحالي والبدء من جديد.", + "confirm_reset": "إعادة التعيين", + "validation_empty_name": "اسم القاعدة مطلوب", + "validation_empty_conditions": "مطلوب شرط واحد على الأقل بقيمة", + "validation_empty_actions": "مطلوب إجراء واحد على الأقل", + "templates_section": "البدء من قالب", + "template_newsletters": "نقل النشرات البريدية إلى مجلد", + "template_receipts": "أرشفة الإيصالات تلقائيًا", + "template_important": "وسم الرسائل المهمة", + "template_notifications": "تصفية الإشعارات", + "sieve_editor": { + "title": "محرر نص Sieve", + "warning": "قد يؤدي تعديل نص Sieve الخام إلى تعطيل التعديل المرئي للقواعد. التغييرات هنا تتجاوز أداة الإنشاء المرئي.", + "script_content": "نص Sieve", + "valid": "النص صالح", + "invalid": "النص يحتوي على أخطاء", + "save_warning": "سيؤدي الحفظ إلى استبدال أي قواعد مرئية. لا يمكن التراجع عن هذا. انقر على حفظ مرة أخرى للتأكيد.", + "validating": "جارٍ التحقق...", + "validate": "تحقق", + "cancel": "إلغاء", + "save": "حفظ", + "confirm_save": "تأكيد الحفظ", + "validation_failed": "فشل طلب التحقق" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# شرط} other {# شروط}}", + "actions_count": "{count, plural, one {# إجراء} other {# إجراءات}}" + }, + "origin_external": "خارجي", + "managed_by_tooltip": "تُدار بواسطة {source}. عدّلها في ذلك التطبيق، أو استخدم محرر Sieve الخام.", + "quick_rule_names": { + "move_sender": "نقل من {sender} إلى {folder}", + "move_domain": "نقل من @{domain} إلى {folder}", + "move_list": "نقل القائمة {list} إلى {folder}", + "mark_read": "تمييز رسائل {sender} كمقروءة", + "tag": "وسم رسائل {sender} بـ {tag}", + "block": "حظر {sender}", + "custom": "رسائل من {sender}", + "custom_plain": "قاعدة جديدة", + "senders": "{count, plural, one {مرسل واحد} two {مرسلَين} few {# مرسلين} many {# مرسلًا} other {# مرسل}}" + }, + "suggestions": "اقتراحات", + "suggest_subject": "الموضوع «{subject}»", + "suggest_to": "إلى {address}", + "suggest_cc": "نسخة إلى {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "طبّقها أيضًا على الرسائل الموجودة في هذا المجلد", + "apply_existing_unsupported": "لا يمكن تطبيق القواعد على الرسائل الموجودة إلا إذا كانت تفحص المرسل أو المستلمين أو الموضوع أو الترويسات، وتنقل أو تنسخ أو تميّز كمقروءة أو بنجمة أو بوسم.", + "period_title": "النطاق الزمني", + "period_start": "تاريخ البدء", + "period_end": "تاريخ الانتهاء", + "validation_period_order": "يجب أن يكون تاريخ الانتهاء بعد تاريخ البدء", + "period_toggle": "نشطة فقط خلال فترة زمنية", + "period_hint": "تنطبق على الرسائل التي تصل خلال هذه الفترة. اترك حقلًا فارغًا لإبقاء ذلك الطرف مفتوحًا.", + "validation_period_empty": "أدخل تاريخ بدء أو انتهاء للفترة", + "validation_period_invalid": "أدخل تاريخًا صالحًا مع الوقت للفترة، أو اترك الحقل فارغًا", + "apply_existing_period": "القاعدة ذات الفترة الزمنية تعمل فقط على الرسائل التي تصل خلالها، لذا لا يمكن تطبيقها على الرسائل الموجودة.", + "period_status_scheduled": "مجدولة", + "period_status_active": "نشطة", + "period_status_expired": "منتهية" + }, + "templates": { + "title": "قوالب البريد", + "description": "إنشاء قوالب بريد قابلة لإعادة الاستخدام بمتغيرات نائبة", + "add": "قالب جديد", + "edit": "تعديل القالب", + "name": "اسم القالب", + "name_placeholder": "مثال: رسالة متابعة", + "category": "الفئة", + "category_placeholder": "مثال: عمل، شخصي", + "subject": "الموضوع", + "subject_placeholder": "سطر موضوع الرسالة", + "body": "النص", + "body_placeholder": "محتوى نص الرسالة...", + "recipients_placeholder": "email@example.com", + "identity": "الإرسال كـ", + "default_identity": "الهوية الافتراضية", + "favorite": "مفضّل", + "cancel": "إلغاء", + "create": "إنشاء", + "update": "تحديث", + "confirm_delete": "حذف", + "no_templates": "لا توجد قوالب بعد", + "manage": "إدارة القوالب", + "count": "{count, plural, one {# قالب} other {# قوالب}}", + "export_import": "التصدير والاستيراد", + "export_import_description": "احفظ نسخة احتياطية من قوالبك أو انقلها إلى جهاز آخر", + "export": "تصدير", + "import": "استيراد", + "validation": { + "empty": "اسم القالب مطلوب", + "too_long": "يجب ألا يتجاوز اسم القالب 200 حرف" + } + }, + "files": { + "display": { + "title": "العرض", + "description": "إعداد كيفية عرض الملفات والمجلدات" + }, + "default_view": { + "label": "العرض الافتراضي", + "description": "اختر بين تخطيط الشبكة والقائمة", + "list": "قائمة", + "grid": "شبكة" + }, + "default_sort": { + "label": "الترتيب الافتراضي", + "description": "اختر الترتيب الافتراضي للملفات", + "name": "الاسم", + "size": "الحجم", + "modified": "تاريخ التعديل" + }, + "sort_direction": { + "label": "اتجاه الترتيب", + "description": "اختر ترتيبًا تصاعديًا أو تنازليًا", + "ascending": "تصاعدي", + "descending": "تنازلي" + }, + "icons": { + "title": "الأيقونات", + "description": "إعداد مظهر أيقونات الملفات" + }, + "show_icons": { + "label": "إظهار أيقونات الملفات", + "description": "عرض أيقونات بجانب الملفات والمجلدات" + }, + "colored_icons": { + "label": "أيقونات ملونة", + "description": "استخدام أيقونات ملونة بدلًا من أحادية اللون" + }, + "show_thumbnails": { + "label": "إظهار الصور المصغّرة", + "description": "عرض معاينات الصور بدلًا من الأيقونات لملفات الصور" + }, + "behavior": { + "title": "السلوك", + "description": "إعداد سلوك متصفح الملفات" + }, + "show_hidden": { + "label": "إظهار الملفات المخفية", + "description": "عرض الملفات والمجلدات التي تبدأ بنقطة" + }, + "preview": { + "label": "معاينة" + } + } + }, + "errors": { + "page_error_title": "حدث خطأ ما", + "page_error_description": "واجهنا خطأ غير متوقع. يرجى المحاولة مرة أخرى أو العودة إلى الصفحة الرئيسية.", + "sidebar_error": "تعذّر تحميل صناديق البريد", + "email_list_error": "تعذّر تحميل الرسائل", + "viewer_error_title": "تعذّر عرض الرسالة", + "viewer_error_description": "حدثت مشكلة أثناء عرض هذه الرسالة. قد تحتوي على محتوى غير مدعوم.", + "composer_error": "تعذّر تحميل محرر الرسائل", + "settings_error_title": "الإعدادات غير متاحة", + "settings_error_description": "تعذّر تحميل الإعدادات. قد لا يتم حفظ تفضيلاتك.", + "try_again": "حاول مرة أخرى", + "reload": "إعادة التحميل", + "reload_emails": "إعادة تحميل الرسائل", + "reload_settings": "إعادة تحميل الإعدادات", + "retry": "إعادة المحاولة", + "go_home": "الانتقال إلى البريد الوارد" + }, + "context_menu": { + "reply": "رد", + "reply_all": "الرد على الجميع", + "forward": "إعادة توجيه", + "mark_read": "تمييز كمقروءة", + "mark_unread": "تمييز كغير مقروءة", + "star": "تمييز بنجمة", + "unstar": "إزالة النجمة", + "pin": "تثبيت", + "unpin": "إلغاء التثبيت", + "move_to": "نقل إلى...", + "copy_to_account": "نسخ إلى حساب...", + "remove_from_folder": "إزالة من هذا المجلد", + "archive": "أرشفة", + "delete": "حذف", + "mark_as_spam": "الإبلاغ عن بريد مزعج", + "not_spam": "ليس مزعجًا", + "tag": "وسم", + "items_selected": "{count} رسالة محددة", + "forward_as_attachments": "إعادة التوجيه كمرفقات", + "edit_draft": "تعديل المسودة", + "cancel_scheduled_send": "إلغاء الإرسال", + "reschedule_send": "إعادة الجدولة", + "cancel_and_edit": "إلغاء والتعديل", + "cancel_and_compose_again": "إلغاء وإعادة الكتابة", + "rules": { + "title": "القواعد", + "move_from": "انقل دائمًا الرسائل من {sender}", + "move_from_domain": "انقل دائمًا الرسائل من @{domain}", + "move_from_list": "انقل دائمًا رسائل هذه القائمة", + "mark_read_from": "ميّز دائمًا رسائل {sender} كمقروءة", + "tag_with": "أضف دائمًا الوسم", + "block": "{count, plural, one {حظر المرسل} other {حظر # مرسلين}}", + "no_junk": "لا يحتوي هذا الحساب على مجلد للبريد غير المرغوب فيه", + "create": "إنشاء قاعدة…", + "manage": "إدارة القواعد", + "senders_count": "{count, plural, one {مرسل واحد} two {مرسلَين} few {# مرسلين} many {# مرسلًا} other {# مرسل}}", + "cross_account": "حدد رسائل من حساب واحد", + "opaque_hint": "تم تعديل مرشحاتك يدويًا. افتح إعدادات المرشحات" , + "loading": "جارٍ تحميل القواعد…", + "load_failed": "تعذّر تحميل القواعد", + "none": "لا توجد قواعد بعد", + "new_mail_only": "للرسائل الجديدة فقط", + "running": "جارٍ تشغيل «{name}» على {folder}…" + }, + "find_emails_from": "البحث عن رسائل من {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "تمييز المجلد كمقروء", + "mark_folder_tree_read": "تمييز المجلد والمجلدات الفرعية كمقروءة", + "mark_all_folders_read": "تمييز جميع المجلدات كمقروءة", + "new_subfolder": "مجلد فرعي جديد...", + "new_folder": "مجلد جديد...", + "rename": "إعادة تسمية...", + "share": "مشاركة...", + "import_email": "استيراد ملف ‎.eml أو ‎.zip...", + "empty_folder": "إفراغ المجلد", + "empty_folder_generic": "إفراغ المجلد", + "delete_folder": "حذف المجلد", + "refresh": "تحديث", + "hide_shared_account": "إخفاء من الشريط الجانبي", + "mute_notifications": "كتم الإشعارات", + "unmute_notifications": "إلغاء كتم الإشعارات", + "mute_account_notifications": "كتم إشعارات هذا الحساب", + "unmute_account_notifications": "إلغاء كتم إشعارات هذا الحساب", + "reset_folder_notifications": "إعادة تعيين إشعارات المجلدات", + "mark_all_confirm_title": "تمييز جميع المجلدات كمقروءة", + "mark_all_confirm_message": "هل تريد تمييز كل رسالة غير مقروءة في حسابك الشخصي كمقروءة؟", + "delete_confirm_title": "حذف المجلد", + "delete_confirm_message": "هل تريد حذف المجلد \"{name}\" نهائيًا؟ لا يمكن التراجع عن هذا الإجراء.", + "prompt_new_subfolder": "أدخل اسمًا للمجلد الفرعي الجديد.", + "prompt_new_folder": "أدخل اسمًا للمجلد الجديد.", + "prompt_rename": "أدخل اسمًا جديدًا لهذا المجلد.", + "placeholder_folder_name": "اسم المجلد", + "create": "إنشاء", + "rename_confirm": "إعادة تسمية", + "toast_marked_read": "تم تمييز المجلد كمقروء", + "toast_marked_read_count": "تم تمييز {count, plural, one {رسالة واحدة} other {# رسالة}} كمقروءة", + "toast_already_read": "لا توجد رسائل غير مقروءة", + "toast_marked_all_read": "تم تمييز جميع المجلدات كمقروءة", + "toast_emptied": "تم إفراغ المجلد", + "toast_folder_created": "تم إنشاء المجلد", + "toast_folder_renamed": "تمت إعادة تسمية المجلد", + "toast_folder_deleted": "تم حذف المجلد", + "toast_error_mark_read": "فشل التمييز كمقروء", + "toast_error_empty": "فشل إفراغ المجلد", + "toast_error_create": "فشل إنشاء المجلد", + "toast_error_rename": "فشلت إعادة تسمية المجلد", + "toast_error_delete": "فشل حذف المجلد", + "toast_error_delete_has_children": "المجلد يحتوي على مجلدات فرعية. أزلها أولًا.", + "toast_error_delete_has_email": "المجلد ليس فارغًا. أفرغه أولًا.", + "delete_with_contents_title": "المجلد ليس فارغًا", + "delete_with_contents_message": "لا يزال المجلد \"{name}\" يحتوي على {count, plural, one {رسالة واحدة} other {# رسائل}}. هل تريد حذف المجلد وكل رسائله؟ لا يمكن التراجع عن هذا.", + "delete_with_contents_confirm": "حذف المجلد والرسائل" + }, + "shortcuts": { + "title": "اختصارات لوحة المفاتيح", + "tip": "اضغط ؟ في أي وقت لإظهار هذه المساعدة", + "sections": { + "navigation": "التنقل", + "actions": "إجراءات البريد", + "global": "عام", + "threads": "المحادثات", + "composer": "محرر الرسائل" + }, + "navigation": { + "next_email": "الرسالة التالية", + "previous_email": "الرسالة السابقة", + "open_email": "فتح الرسالة", + "close_email": "إغلاق / إلغاء التحديد", + "go_to_folder": "الانتقال إلى المجلد", + "next_unread": "الرسالة غير المقروءة التالية", + "no_more_unread": "لا توجد رسائل غير مقروءة أسفل القائمة" + }, + "actions": { + "reply": "رد", + "reply_all": "الرد على الجميع", + "forward": "إعادة توجيه", + "star": "تبديل النجمة", + "archive": "أرشفة", + "delete": "حذف", + "mark_unread": "تمييز كغير مقروءة", + "mark_read": "تمييز كمقروءة", + "toggle_spam": "الإبلاغ عن بريد مزعج / ليس مزعجًا" + }, + "global": { + "compose": "إنشاء رسالة جديدة", + "search": "التركيز على البحث", + "help": "إظهار الاختصارات", + "refresh": "تحديث الرسائل", + "select_all": "تحديد الكل", + "global_search": "البحث في كل شيء", + "extend_selection": "توسيع التحديد لأسفل / لأعلى" + }, + "threads": { + "expand_collapse": "توسيع/طي المحادثة", + "select_mountain_view": "تحديد المحادثة" + }, + "composer": { + "send": "إرسال الرسالة", + "schedule_send": "جدولة الإرسال", + "template_picker": "فتح منتقي القوالب" + } + }, + "threads": { + "messages_one": "{count} رسالة", + "messages_other": "{count} رسالة", + "messages_tooltip": "{count, plural, one {# رسالة في هذه المحادثة} other {# رسالة في هذه المحادثة}}", + "expand": "توسيع المحادثة", + "collapse": "طي المحادثة", + "loading": "جارٍ تحميل المحادثة...", + "mark_read": "تمييز المحادثة كمقروءة", + "mark_unread": "تمييز المحادثة كغير مقروءة", + "archive": "أرشفة المحادثة", + "delete": "حذف المحادثة", + "star": "تمييز المحادثة بنجمة", + "unstar": "إزالة النجمة عن المحادثة", + "toggle_thread": "تبديل عرض المحادثة" + }, + "identities": { + "modal_title": "إدارة هويات الإرسال", + "create_new": "إنشاء هوية جديدة", + "edit_identity": "تعديل الهوية", + "delete_confirm": "حذف هذه الهوية؟ لا يمكن التراجع عن هذا.", + "cannot_delete": "لا يمكن حذف هذه الهوية", + "primary_identity": "أساسية", + "set_as_primary": "تعيين كأساسية", + "no_identities": "لم يتم العثور على هويات", + "display": { + "reply_to": "الرد إلى:", + "bcc": "نسخة مخفية:", + "signature": "التوقيع:", + "preview": "معاينة:" + }, + "validation_errors": { + "invalid_emails": "عناوين بريد غير صالحة: {emails}", + "unknown_error": "خطأ غير معروف" + }, + "form": { + "name_label": "الاسم المعروض", + "name_placeholder": "مثال: بريد العمل، شخصي", + "name_required": "الاسم مطلوب", + "email_label": "عنوان البريد الإلكتروني", + "email_placeholder": "your.email@example.com", + "email_required": "البريد الإلكتروني مطلوب", + "email_invalid": "يرجى إدخال عنوان بريد إلكتروني صالح", + "email_immutable": "لا يمكن تغيير عنوان البريد الإلكتروني بعد الإنشاء", + "reply_to_label": "الرد إلى (اختياري)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "نسخة مخفية تلقائية (اختياري)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "توقيع نصي", + "html_signature_label": "توقيع HTML", + "signature_byte_counter": "{bytes} / {max} بايت", + "signature_byte_limit_reached": "تم بلوغ حد الخادم", + "save": "حفظ الهوية", + "cancel": "إلغاء", + "creating": "جارٍ الإنشاء...", + "updating": "جارٍ التحديث..." + }, + "sub_address": { + "button_tooltip": "استخدام عنوان فرعي", + "popover_title": "إضافة وسم عنوان فرعي", + "tag_input_placeholder": "أدخل وسمًا (مثال: shopping)", + "preview_label": "معاينة:", + "recent_tags": "الوسوم الأخيرة", + "suggested_tags": "مقترح", + "use_address": "استخدام هذا العنوان", + "invalid_tag": "يجب أن يتكون الوسم من أحرف وأرقام وشرطات فقط", + "tag_too_long": "يجب ألا يتجاوز الوسم 30 حرفًا", + "help_text": "الرسائل المرسلة إلى user{delimiter}tag@domain.com ستصل إلى بريدك الوارد", + "validation": { + "empty": "لا يمكن أن يكون الوسم فارغًا", + "too_long": "يجب ألا يتجاوز الوسم {max} حرفًا", + "invalid_chars": "يجب أن يحتوي الوسم على أحرف وأرقام وشرطات فقط" + } + }, + "badge": { + "sent_via": "عبر", + "sub_address_tag": "أُرسلت باستخدام عنوان فرعي: {tag}", + "identity_name": "أُرسلت باستخدام هوية: {name}", + "identity_short": "عبر {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "حذف", + "delete_confirm_title": "حذف الهوية" + }, + "templates": { + "picker_title": "اختر قالبًا", + "search_placeholder": "بحث في القوالب...", + "section_favorites": "المفضّلة", + "section_recent": "الأخيرة", + "section_uncategorized": "أخرى", + "no_templates": "لا توجد قوالب بعد", + "no_results": "لم يتم العثور على قوالب", + "fill_placeholders": "تعبئة القيم النائبة", + "enter_value": "أدخل قيمة...", + "preview": "معاينة", + "insert_with_values": "الإدراج بالقيم", + "insert_raw": "الإدراج كما هو", + "copy_suffix": "(نسخة)", + "placeholder": "متغيّر", + "placeholders": { + "recipient_name": "اسم المستلم", + "company": "اسم الشركة", + "date": "التاريخ الحالي", + "day_of_week": "يوم الأسبوع", + "sender_name": "اسمك" + } + }, + "contacts": { + "directory": { + "title": "الدليل", + "search_placeholder": "ابحث عن اسم أو عنوان بريد", + "refresh": "تحديث الدليل", + "retry": "إعادة المحاولة", + "loading": "جارٍ تحميل الدليل…", + "unavailable_title": "لا يوجد دليل", + "unavailable_hint": "لا يوفّر خادم البريد دليلًا للحسابات.", + "forbidden_title": "الدليل غير متاح", + "forbidden_hint": "لا يسمح خادم البريد لهذا الحساب بتصفّح الدليل. اسأل المسؤول.", + "error_title": "تعذّر تحميل الدليل", + "error_hint": "تحقق من الاتصال وحاول مرة أخرى.", + "empty_title": "الدليل فارغ", + "empty_hint": "لا يعرض خادم البريد أي حساب له عنوان بريد.", + "no_match": "لا أحد في الدليل يطابق بحثك", + "refresh_failed": "تعذّر تحديث الدليل. تُعرض آخر قائمة تم تحميلها.", + "select_page": "تحديد الجميع في هذه الصفحة", + "deselect_page": "إلغاء تحديد الجميع في هذه الصفحة", + "select_entry": "تحديد {name}", + "range": "{from}–{to} من {total}", + "page": "الصفحة {page} من {count}", + "previous_page": "الصفحة السابقة", + "next_page": "الصفحة التالية", + "picker_title": "الاختيار من الدليل", + "cancel": "إلغاء", + "selected_count": "تم تحديد {count}", + "add_to": "إضافة إلى «إلى»", + "add_cc": "إضافة إلى «نسخة»", + "add_bcc": "إضافة إلى «نسخة مخفية»", + "select_hint": "اختر شخصًا لعرض تفاصيله", + "write_email": "كتابة رسالة", + "add_to_contacts": "إضافة إلى جهات الاتصال", + "email": "البريد", + "read_only_note": "تأتي إدخالات الدليل من خادم البريد ولا يمكن تعديلها هنا.", + "type": { + "individual": "شخص", + "group": "مجموعة", + "resource": "مورد", + "location": "موقع", + "other": "أخرى" + } + }, + "title": "جهات الاتصال", + "search_placeholder": "بحث في جهات الاتصال...", + "create_new": "جهة اتصال جديدة", + "no_category": "بلا فئة", + "rename_category": "إعادة تسمية الفئة", + "category_name_label": "اسم الفئة", + "category_renamed": "تمت إعادة تسمية الفئة", + "category_rename_failed": "فشلت إعادة تسمية الفئة", + "category_added": "تمت إضافة جهة الاتصال إلى {name}", + "category_added_plural": "تمت إضافة {count} جهات اتصال إلى {name}", + "empty_state": "لا توجد جهات اتصال بعد", + "empty_state_title": "لا توجد جهات اتصال بعد", + "empty_state_subtitle": "أنشئ أول جهة اتصال أو استوردها من ملف vCard", + "empty_search": "لا توجد جهات اتصال مطابقة لبحثك", + "empty_search_hint": "جرّب مصطلح بحث مختلف", + "empty_filtered": "لا توجد جهات اتصال مطابقة لمرشحاتك", + "empty_filtered_hint": "حاول تعديل المرشحات أو مسحها", + "clear_search": "مسح البحث", + "import_vcard": "استيراد vCard", + "delete_confirm_title": "حذف جهة الاتصال", + "delete_confirm": "هل أنت متأكد أنك تريد حذف جهة الاتصال هذه؟", + "local_mode": "تُخزَّن جهات الاتصال محليًا (الخادم لا يدعم جهات اتصال JMAP)", + "back_to_contacts": "العودة إلى جهات الاتصال", + "back_to_email": "العودة إلى البريد", + "open_categories": "فتح الفئات", + "tabs": { + "all": "الكل", + "groups": "المجموعات" + }, + "shared": { + "title": "مشترك" + }, + "address_books": { + "create": "دفتر عناوين جديد", + "created": "تم إنشاء دفتر العناوين", + "create_failed": "فشل إنشاء دفتر العناوين", + "title": "دفاتر عناويني", + "shared_prefix": "مشترك: {name}", + "moved": "تم نقل جهة الاتصال إلى {name}", + "moved_plural": "تم نقل {count} جهات اتصال إلى {name}", + "move_failed": "فشل نقل جهة الاتصال", + "address_book": "دفتر العناوين", + "rename": "إعادة تسمية دفتر العناوين", + "name_label": "اسم دفتر العناوين", + "renamed": "تمت إعادة تسمية دفتر العناوين", + "rename_failed": "فشلت إعادة تسمية دفتر العناوين", + "default": "افتراضي", + "set_default": "تعيين كافتراضي", + "default_updated": "تم تحديث دفتر العناوين الافتراضي", + "set_default_failed": "فشل تعيين دفتر العناوين الافتراضي", + "manage": "إدارة دفاتر العناوين", + "share": "مشاركة دفتر العناوين", + "new_contact_in_book": "جهة اتصال جديدة في دفتر العناوين هذا", + "delete": "حذف دفتر العناوين", + "confirm_delete": "حذف \"{name}\"؟ ستتم إزالة جميع جهات الاتصال في دفتر العناوين هذا.", + "deleted": "تم حذف دفتر العناوين", + "delete_failed": "فشل حذف دفتر العناوين" + }, + "detail": { + "emails": "عناوين البريد الإلكتروني", + "phones": "أرقام الهاتف", + "organizations": "المؤسسات", + "addresses": "العناوين", + "notes": "ملاحظات", + "titles": "الألقاب والأدوار", + "online_services": "الخدمات عبر الإنترنت", + "anniversaries": "المناسبات", + "personal_info": "معلومات شخصية", + "languages": "اللغات", + "categories": "الفئات", + "related_contacts": "جهات اتصال ذات صلة", + "crypto_keys": "مفاتيح التشفير", + "cert_issuer": "الجهة المُصدِرة", + "cert_expires": "تنتهي في", + "cert_expired": "منتهية الصلاحية", + "cert_fingerprint": "البصمة", + "cert_algorithm": "الخوارزمية", + "import_to_smime": "الاستيراد إلى S/MIME", + "cert_already_imported": "مستوردة بالفعل إلى S/MIME", + "cert_imported": "تم استيراد الشهادة إلى مخزن S/MIME", + "cert_import_failed": "فشل استيراد الشهادة", + "no_contact_selected": "اختر جهة اتصال لعرض التفاصيل", + "compose_email": "إنشاء رسالة", + "copy_email": "نسخ البريد الإلكتروني", + "copy_phone": "نسخ رقم الهاتف", + "copy_url": "نسخ الرابط", + "copied": "تم النسخ إلى الحافظة", + "copy_failed": "فشل النسخ إلى الحافظة", + "created": "تاريخ الإنشاء", + "updated": "آخر تحديث", + "timezone": "المنطقة الزمنية", + "anniversary_birth": "عيد الميلاد", + "anniversary_death": "الوفاة", + "anniversary_wedding": "الذكرى السنوية", + "anniversary_other": "أخرى", + "personal_expertise": "الخبرة", + "personal_hobby": "الهواية", + "personal_interest": "الاهتمام", + "personal_other": "أخرى", + "gender": "الجنس", + "gender_masculine": "ذكر", + "gender_feminine": "أنثى", + "gender_other": "آخر", + "gender_none": "لا ينطبق", + "gender_unknown": "غير معروف", + "calendar": "التقويم", + "calendar_uri": "رابط التقويم", + "scheduling_uri": "رابط الجدولة", + "freebusy_uri": "رابط التوفر/الانشغال", + "section_contact": "تفاصيل الاتصال", + "section_work": "العمل", + "section_personal": "شخصي", + "email_default_label": "بريد إلكتروني", + "phone_default_label": "هاتف", + "address_default_label": "عنوان", + "online_service_default_label": "عبر الإنترنت", + "organization_label": "المؤسسة", + "title_label": "اللقب", + "role_label": "الدور", + "language_label": "اللغة", + "related_default_label": "ذو صلة", + "more_actions": "المزيد من الإجراءات", + "age_years": "{count, plural, one {عمره سنة واحدة} other {عمره # سنة}}", + "years_since": "{count, plural, one {سنة واحدة} other {# سنوات}}" + }, + "activity": { + "recent_emails": "الرسائل الأخيرة", + "upcoming_events": "الأحداث القادمة", + "no_emails": "لا توجد رسائل حديثة", + "no_events": "لا توجد أحداث قادمة", + "no_subject": "(بلا موضوع)", + "no_title": "(بلا عنوان)", + "load_failed": "فشل التحميل", + "unknown_sender": "مرسل غير معروف", + "all_day": "طوال اليوم" + }, + "form": { + "create_title": "جهة اتصال جديدة", + "edit_title": "تعديل جهة الاتصال", + "section_address_book": "الدليل", + "select_address_book": "اختر دليلًا...", + "section_identity": "الاسم والهوية", + "contact_type": "نوع جهة الاتصال", + "type_person": "شخص", + "type_organization": "المؤسسة", + "section_work": "العمل والمؤسسة", + "prefix": "اللقب", + "prefix_placeholder": "د.، أ.، السيدة", + "given_name": "الاسم الأول", + "middle_name": "الاسم الأوسط", + "surname": "اسم العائلة", + "suffix": "اللاحقة", + "suffix_placeholder": "الابن، الأب، الثالث", + "nickname": "الاسم المستعار", + "nickname_placeholder": "الاسم المستعار", + "email": "البريد الإلكتروني", + "email_placeholder": "email@example.com", + "phone": "الهاتف", + "phone_placeholder": "+1 234 567 890", + "phone_type": "النوع", + "phone_voice": "صوت", + "phone_cell": "جوال", + "phone_fax": "فاكس", + "phone_pager": "جهاز نداء", + "phone_video": "فيديو", + "phone_text": "رسائل نصية", + "organization": "المؤسسة", + "organization_placeholder": "اسم الشركة", + "department": "القسم", + "department_placeholder": "القسم", + "job_title": "المسمى الوظيفي", + "job_title_placeholder": "مثال: مهندس برمجيات", + "role": "الدور", + "role_placeholder": "مثال: قائد فريق", + "addresses": "العناوين", + "add_address": "إضافة عنوان", + "street": "الشارع", + "city": "المدينة", + "region": "المحافظة / المنطقة", + "postcode": "الرمز البريدي", + "country": "البلد", + "online_services": "الخدمات عبر الإنترنت", + "add_online_service": "إضافة خدمة عبر الإنترنت", + "url_placeholder": "https://...", + "service_placeholder": "الخدمة", + "anniversaries": "المناسبات", + "add_anniversary": "إضافة تاريخ", + "anniversary_birth": "عيد الميلاد", + "anniversary_wedding": "الذكرى السنوية", + "anniversary_death": "الذكرى التأبينية", + "anniversary_other": "أخرى", + "personal_info": "معلومات شخصية", + "add_personal_info": "إضافة إدخال", + "personal_info_placeholder": "مثال: التصوير الفوتوغرافي", + "personal_expertise": "الخبرة", + "personal_hobby": "الهواية", + "personal_interest": "الاهتمام", + "personal_other": "أخرى", + "level": "المستوى", + "level_high": "مرتفع", + "level_medium": "متوسط", + "level_low": "منخفض", + "categories": "الفئات", + "categories_placeholder": "مثال: عائلة، أصدقاء، زملاء", + "categories_hint": "اكتب للبحث عن فئات أو إضافتها", + "category_add": "إضافة", + "note": "ملاحظات", + "note_placeholder": "أضف ملاحظة...", + "gender": "الجنس", + "gender_sex": "الجنس البيولوجي", + "gender_male": "ذكر", + "gender_female": "أنثى", + "gender_other": "آخر", + "gender_none": "لا ينطبق", + "gender_unknown": "غير معروف", + "gender_identity": "الهوية الجندرية", + "gender_identity_placeholder": "الهوية الجندرية...", + "calendar": "التقويم", + "calendar_uri": "رابط التقويم", + "scheduling_uri": "رابط الجدولة", + "freebusy_uri": "رابط التوفر/الانشغال", + "context_work": "العمل", + "context_private": "خاص", + "add_email": "إضافة بريد إلكتروني", + "add_phone": "إضافة هاتف", + "save": "حفظ", + "cancel": "إلغاء", + "creating": "جارٍ الإنشاء...", + "updating": "جارٍ التحديث...", + "name_required": "أدخل اسمًا أول أو اسم عائلة أو مؤسسة", + "email_invalid": "يرجى إدخال عنوان بريد إلكتروني صالح", + "email_error_inline": "تنسيق البريد الإلكتروني غير صالح", + "save_failed": "فشل حفظ جهة الاتصال", + "delete": "حذف", + "upload_photo": "رفع صورة", + "remove_photo": "إزالة الصورة", + "photo_hint": "JPG أو PNG، حتى 10 ميغابايت. سيتم تغيير حجمها.", + "photo_too_large": "الصورة كبيرة جدًا (الحد الأقصى 10 ميغابايت)", + "photo_invalid": "ملف صورة غير صالح", + "change_photo": "تغيير" + }, + "groups": { + "create": "مجموعة جديدة", + "edit": "تعديل المجموعة", + "empty": "لا توجد مجموعات بعد", + "delete_confirm_title": "حذف المجموعة", + "delete_confirm": "هل أنت متأكد أنك تريد حذف هذه المجموعة؟", + "name_label": "اسم المجموعة", + "name_placeholder": "مثال: فريق، عائلة", + "name_required": "اسم المجموعة مطلوب", + "save_failed": "فشل حفظ المجموعة", + "members_label": "الأعضاء", + "search_members": "بحث عن جهات اتصال لإضافتها...", + "no_members": "لا يوجد أعضاء في هذه المجموعة", + "member_count": "{count, plural, =0 {لا يوجد أعضاء} one {عضو واحد} other {# أعضاء}}", + "send_email": "إرسال بريد إلكتروني إلى المجموعة", + "send_email_to": "إلى", + "send_email_cc": "نسخة", + "send_email_bcc": "نسخة مخفية", + "no_member_emails": "لا يحتوي هذا الفريق على أعضاء لديهم عنوان بريد إلكتروني." + }, + "import": { + "title": "استيراد جهات الاتصال", + "drop_hint": "انقر لاختيار ملف vCard", + "file_types": "ملفات ‎.vcf أو ‎.vcard", + "no_contacts": "لم يتم العثور على جهات اتصال في الملف", + "parse_error": "فشل تحليل ملف vCard", + "found": "{count, plural, one {تم العثور على جهة اتصال واحدة} other {تم العثور على # جهة اتصال}}", + "duplicate": "مكررة", + "select_all": "تحديد الكل", + "deselect_all": "إلغاء تحديد الكل", + "selected": "{count, plural, one {تم تحديد واحدة} other {تم تحديد #}}", + "import_button": "استيراد", + "importing": "جارٍ الاستيراد...", + "success": "{count, plural, one {تم استيراد جهة اتصال واحدة} other {تم استيراد # جهة اتصال}}", + "failed": "فشل الاستيراد", + "close": "إغلاق", + "file_too_large": "الملف كبير جدًا (الحد الأقصى 5 ميغابايت)" + }, + "export": { + "title": "تصدير جهات الاتصال", + "success": "{count, plural, one {تم تصدير جهة اتصال واحدة} other {تم تصدير # جهة اتصال}}" + }, + "bulk": { + "selected": "{count, plural, one {تم تحديد واحدة} other {تم تحديد #}}", + "select_all": "تحديد الكل", + "delete": "حذف", + "delete_confirm_title": "حذف جهات الاتصال", + "delete_confirm": "حذف {count, plural, one {جهة اتصال واحدة} other {# جهة اتصال}}؟", + "deleted": "{count, plural, one {تم حذف جهة اتصال واحدة} other {تم حذف # جهة اتصال}}", + "add_to_group": "إضافة إلى مجموعة", + "choose_group": "اختر مجموعة", + "adding_contacts": "جارٍ إضافة {count, plural, one {جهة اتصال واحدة} other {# جهة اتصال}}", + "added_to_group": "تمت إضافة جهات الاتصال إلى المجموعة", + "export": "تصدير", + "clear": "إلغاء التحديد" + }, + "toast": { + "created": "تم إنشاء جهة الاتصال", + "updated": "تم تحديث جهة الاتصال", + "deleted": "تم حذف جهة الاتصال", + "error_create": "فشل إنشاء جهة الاتصال", + "error_update": "فشل تحديث جهة الاتصال", + "error_delete": "فشل حذف جهة الاتصال" + }, + "context_menu": { + "open": "فتح", + "edit": "تعديل", + "send_email": "إرسال بريد إلكتروني", + "add_to_group": "إضافة إلى مجموعة", + "export_vcard": "التصدير كـ vCard", + "delete": "حذف", + "call": "اتصال", + "duplicate": "تكرار", + "print": "طباعة" + }, + "filters": { + "toggle": "المرشحات", + "select": "تحديد", + "clear": "مسح", + "close": "إغلاق", + "title": "مرشحات متقدمة", + "organization": "الشركة", + "organization_placeholder": "مثال: شركة أكمي", + "job_title": "المسمى الوظيفي", + "job_title_placeholder": "مثال: مصمم", + "location": "الموقع", + "location_placeholder": "المدينة أو البلد", + "email_domain": "نطاق البريد الإلكتروني", + "email_domain_placeholder": "example.com", + "birthday_month": "عيد الميلاد في", + "any_month": "أي شهر", + "has_email": "لديه بريد إلكتروني", + "has_phone": "لديه هاتف", + "has_photo": "لديه صورة", + "sort_by": "الترتيب حسب", + "sort_first_name": "الاسم الأول", + "sort_last_name": "اسم العائلة" + } + }, + "calendar": { + "title": "التقويم", + "back_to_email": "العودة إلى البريد", + "back_to_month": "العودة إلى الشهر", + "my_calendars": "التقويمات", + "birthday_calendar": "أعياد الميلاد", + "mini_calendar_change": "انقر لتغيير الشهر", + "views": { + "month": "شهر", + "week": "أسبوع", + "day": "يوم", + "agenda": "جدول الأعمال", + "today": "اليوم", + "month_hint": "شهر (m)", + "week_hint": "أسبوع (w)", + "day_hint": "يوم (d)", + "agenda_hint": "جدول الأعمال (a)", + "tasks": "المهام", + "tasks_hint": "المهام (k)" + }, + "events": { + "hidden_before": "{count, plural, zero {لا أحداث سابقة} one {حدث سابق واحد} two {حدثان سابقان} few {# أحداث سابقة} many {# حدثًا سابقًا} other {# حدث سابق}}", + "hidden_after": "{count, plural, zero {لا أحداث لاحقة} one {حدث لاحق واحد} two {حدثان لاحقان} few {# أحداث لاحقة} many {# حدثًا لاحقًا} other {# حدث لاحق}}", + "show_all_hours": "إظهار كل الساعات", + "show_display_hours": "إظهار {start} – {end} فقط", + "create": "إنشاء حدث", + "edit": "تعديل الحدث", + "delete": "حذف الحدث", + "details": "تفاصيل الحدث", + "no_events": "لا توجد أحداث", + "all_day": "طوال اليوم", + "more": "+{count} أخرى", + "show_more": "إظهار المزيد", + "show_less": "إظهار أقل", + "no_title": "(بلا عنوان)", + "resize": "تغيير حجم الحدث", + "duplicate": "تكرار", + "today_header": "اليوم", + "tomorrow_header": "غدًا", + "agenda_show_earlier": "عرض الأحداث السابقة", + "agenda_loading": "جارٍ التحميل…", + "agenda_range_start": "الأحداث من {date}", + "agenda_range_end": "الأحداث حتى {date}", + "export_ics": "التصدير كملف ‎.ics", + "copy_title": "نسخ العنوان", + "copy_link": "نسخ رابط الاجتماع", + "new_event": "حدث جديد", + "new_all_day_event": "حدث جديد طوال اليوم", + "new_task": "مهمة جديدة", + "go_to_today": "الانتقال إلى اليوم" + }, + "detail": { + "add_note": "أضف ملاحظة...", + "save_note": "حفظ", + "note_saved": "تمت إضافة الملاحظة", + "open_link": "فتح الرابط", + "copy_location": "نسخ الموقع", + "meeting_link": "رابط الاجتماع", + "tentative": "مبدئي", + "cancelled": "ملغى", + "delete_confirm": "حذف هذا الحدث؟" + }, + "form": { + "title": "العنوان", + "description": "الوصف", + "location": "الموقع", + "meeting_link": "رابط الاجتماع", + "start_date": "تاريخ البدء", + "end_date": "تاريخ الانتهاء", + "start_time": "وقت البدء", + "end_time": "وقت الانتهاء", + "all_day_event": "حدث طوال اليوم", + "calendar_select": "التقويم", + "save": "حفظ", + "cancel": "إلغاء", + "delete_confirm": "هل أنت متأكد أنك تريد حذف هذا الحدث؟", + "color": "اللون" + }, + "participants": { + "availability": { + "title": "التوفر", + "free": "متاح", + "busy": "مشغول", + "tentative": "مبدئي", + "unknown": "ليس مستخدمًا على هذا الخادم", + "checking": "جارٍ التحقق…" + }, + "title": "المشاركون", + "add": "إضافة مشارك", + "organizer": "المنظّم", + "attendee": "حاضر", + "accepted": "مقبول", + "declined": "مرفوض", + "tentative": "مبدئي", + "needs_action": "بحاجة إلى رد", + "remove": "إزالة", + "edit": "تعديل", + "email_placeholder": "أضف عنوان بريد إلكتروني أو ابحث في جهات الاتصال", + "send_invitations": "إرسال الدعوات إلى المشاركين", + "status_summary": "{accepted} قبلوا، {pending} في الانتظار", + "invited_by": "دعوة من {name}", + "respond_below": "أجب باستخدام الأزرار أدناه", + "rsvp_label": "ردّك", + "cancel_notification": "سيتم إعلام المشاركين بالإلغاء", + "you_organizer": "أنت المنظّم", + "you_attendee": "أنت أحد الحاضرين", + "no_participants": "لا يوجد مشاركون", + "count": "{count, plural, one {# مشارك} other {# مشاركين}}" + }, + "recurrence": { + "title": "التكرار", + "none": "لا يتكرر", + "daily": "يوميًا", + "weekly": "أسبوعيًا", + "monthly": "شهريًا", + "yearly": "سنويًا", + "every_n_days": "كل {count} أيام", + "every_n_weeks": "كل {count} أسابيع", + "every_n_months": "كل {count} أشهر", + "until": "حتى", + "occurrences": "{count} مرة", + "custom": "مخصص…", + "edit_custom": "تعديل التكرار المخصص", + "every_n_years": "كل {count} سنوات", + "on_days": "في {days}", + "on_day_n": "في اليوم {day}", + "on_the_nth": "في {nth} {day}", + "in_month": "في {month}", + "nth_1": "الأول", + "nth_2": "الثاني", + "nth_3": "الثالث", + "nth_4": "الرابع", + "nth_last": "الأخير", + "editor_freq_day": "يوم", + "editor_freq_week": "أسبوع", + "editor_freq_month": "شهر", + "editor_freq_year": "سنة", + "editor_repeats_on": "يتكرر في", + "editor_every": "كل", + "editor_unit_days": "يوم/أيام", + "editor_unit_weeks": "أسبوع/أسابيع", + "editor_unit_months": "شهر/أشهر", + "editor_unit_years": "سنة/سنوات", + "editor_on_day": "في اليوم", + "editor_on_the": "في", + "editor_in": "في", + "editor_ends": "ينتهي", + "editor_never": "أبدًا", + "editor_ends_on": "في", + "editor_ends_after": "بعد", + "editor_occurrences": "مرة" + }, + "recurrence_scope": { + "edit_title": "تعديل حدث متكرر", + "delete_title": "حذف حدث متكرر", + "description": "هذا حدث متكرر. ما الأحداث التي تريد تعديلها؟", + "this_event": "هذا الحدث فقط", + "this_and_future": "هذا الحدث والأحداث التالية", + "all_events": "جميع الأحداث", + "cancel": "إلغاء", + "save": "حفظ", + "delete": "حذف", + "rsvp_title": "الرد على حدث متكرر", + "rsvp_description": "هذا حدث متكرر. على أي الأحداث ينطبق ردك؟", + "respond": "رد" + }, + "alerts": { + "title": "التذكيرات", + "none": "لا توجد تذكيرات", + "at_time": "عند وقت الحدث", + "minutes_before": "{count, plural, one {قبل # دقيقة} other {قبل # دقيقة}}", + "hours_before": "{count, plural, one {قبل # ساعة} other {قبل # ساعة}}", + "days_before": "{count, plural, one {قبل # يوم} other {قبل # يوم}}", + "weeks_before": "{count, plural, one {قبل # أسبوع} other {قبل # أسبوع}}", + "unit_minutes_before": "دقائق قبل", + "unit_hours_before": "ساعات قبل", + "unit_days_before": "أيام قبل", + "unit_weeks_before": "أسابيع قبل", + "add": "إضافة تذكير", + "remove": "إزالة التذكير", + "amount": "مقدار التذكير", + "unit": "وحدة التذكير", + "action": "نوع التذكير", + "action_display": "إشعار", + "action_email": "البريد الإلكتروني", + "by_email": "{label} عبر البريد الإلكتروني", + "kept": "{count, plural, one {يتم الاحتفاظ بتذكير إضافي واحد لا يمكن تعديله هنا} two {يتم الاحتفاظ بتذكيرين إضافيين لا يمكن تعديلهما هنا} few {يتم الاحتفاظ بـ # تذكيرات إضافية لا يمكن تعديلها هنا} many {يتم الاحتفاظ بـ # تذكيرًا إضافيًا لا يمكن تعديله هنا} other {يتم الاحتفاظ بـ # تذكير إضافي لا يمكن تعديله هنا}}", + "other": "{count, plural, one {تذكير آخر واحد} two {تذكيران آخران} few {# تذكيرات أخرى} many {# تذكيرًا آخر} other {# تذكير آخر}}", + "calendar_defaults": "التذكيرات الافتراضية للتقويم", + "customize": "تخصيص", + "use_defaults": "استخدام التذكيرات الافتراضية للتقويم" + }, + "settings": { + "limit_hours": "تقييد الساعات المعروضة", + "limit_hours_desc": "اعرض هذه الساعات فقط في عرض اليوم والأسبوع. تبقى الأحداث خارجها متاحة عبر الأسهم أعلى كل يوم وأسفله.", + "visible_hours": "الساعات المعروضة", + "visible_hours_start": "بداية الساعات المعروضة", + "visible_hours_end": "نهاية الساعات المعروضة", + "hide_non_working_days": "إخفاء أيام العطلة", + "hide_non_working_days_desc": "استبعد من عرض الأسبوع الأيام التي لا تعمل فيها. تظل أحداثها ظاهرة في عرض الشهر واليوم وجدول الأعمال.", + "working_days": "أيام العمل", + "title": "إعدادات التقويم", + "default_view": "العرض الافتراضي", + "week_starts_on": "يبدأ الأسبوع في", + "time_format": "تنسيق الوقت", + "default_calendar": "التقويم الافتراضي", + "default_reminder": "التذكير الافتراضي", + "time_format_12h": "12 ساعة", + "time_format_24h": "24 ساعة", + "notifications_enabled": "إشعارات الأحداث", + "notifications_enabled_desc": "إظهار تنبيهات لأحداث التقويم القادمة", + "notification_sound": "صوت الإشعار", + "notification_sound_desc": "تشغيل صوت لتنبيهات التقويم", + "invitation_parsing": "تحليل دعوات البريد الإلكتروني", + "invitation_parsing_desc": "اكتشاف دعوات التقويم في مرفقات البريد وإظهار إجراءات التقويم", + "show_time_in_month_view": "إظهار الوقت في عرض الشهر", + "show_time_in_month_view_desc": "عرض أوقات الأحداث في عرض التقويم الشهري", + "show_week_numbers": "إظهار أرقام الأسابيع", + "show_week_numbers_desc": "عرض أرقام الأسابيع في التقويم المصغّر", + "calendar_free_scroll": "تمرير حر", + "calendar_free_scroll_desc": "التمرير باستمرار عبر الأشهر والأسابيع والأيام بدلاً من فترة واحدة في كل مرة", + "enable_tasks": "تفعيل المهام", + "enable_tasks_desc": "إظهار عرض للمهام في التقويم لإدارة قوائم المهام", + "show_tasks_on_calendar": "إظهار المهام في التقويم", + "show_tasks_on_calendar_desc": "عرض المهام في عروض التقويم الشهري والأسبوعي واليومي", + "hover_preview": "معاينة عند التمرير فوق الحدث", + "hover_preview_desc": "إظهار نافذة تفاصيل منبثقة عند التمرير فوق أحداث التقويم", + "hover_preview_instant": "فوري", + "hover_preview_delay_500ms": "تأخير 0.5 ثانية", + "hover_preview_delay_1s": "تأخير ثانية واحدة", + "hover_preview_delay_2s": "تأخير ثانيتين", + "hover_preview_off": "معطّل", + "organizer_identity": "تنظيم الدعوات باسم", + "organizer_identity_desc": "عنوان التقويم المستخدم كمنظّم عند دعوة الآخرين إلى الأحداث.", + "organizer_identity_failed": "فشل تغيير عنوان المنظّم", + "show_birthday_calendar": "تقويم أعياد ميلاد جهات الاتصال", + "show_birthday_calendar_desc": "إظهار تقويم افتراضي بأعياد ميلاد جهات اتصالك" + }, + "days": { + "monday": "الاثنين", + "tuesday": "الثلاثاء", + "wednesday": "الأربعاء", + "thursday": "الخميس", + "friday": "الجمعة", + "saturday": "السبت", + "sunday": "الأحد", + "mon": "اثنين", + "tue": "ثلاثاء", + "wed": "أربعاء", + "thu": "خميس", + "fri": "جمعة", + "sat": "سبت", + "sun": "أحد" + }, + "months": { + "jan": "يناير", + "feb": "فبراير", + "mar": "مارس", + "apr": "أبريل", + "may": "مايو", + "jun": "يونيو", + "jul": "يوليو", + "aug": "أغسطس", + "sep": "سبتمبر", + "oct": "أكتوبر", + "nov": "نوفمبر", + "dec": "ديسمبر", + "far": "فروردين", + "ord": "أرديبهشت", + "kho": "خرداد", + "tir": "تير", + "mor": "مرداد", + "sha": "شهریور", + "meh": "مهر", + "aba": "آبان", + "aza": "آذر", + "dey": "دی", + "bah": "بهمن", + "esf": "اسفند" + }, + "notifications": { + "event_created": "تم إنشاء الحدث", + "event_updated": "تم تحديث الحدث", + "event_deleted": "تم حذف الحدث", + "calendar_created": "تم إنشاء التقويم", + "calendar_deleted": "تم حذف التقويم", + "event_move_error": "فشل نقل الحدث", + "event_resize_error": "فشل تغيير حجم الحدث", + "alert_title": "حدث قادم", + "alert_now": "يبدأ الآن", + "alert_in_minutes": "خلال {count} دقيقة", + "invitation_sent": "تم إرسال الدعوات", + "invitations_denied_title": "تعذر إرسال الدعوات", + "invitations_denied": "رفض الخادم إرسال الدعوات: {reason}. هل تريد حفظ الحدث دون إرسالها؟", + "save_without_invitations": "حفظ دون دعوات", + "rsvp_updated": "تم تحديث الرد", + "rsvp_error": "فشل تحديث الرد", + "event_duplicated": "تم تكرار الحدث", + "event_error": "فشل حفظ الحدث", + "task_due": "موعد استحقاق المهمة", + "event_exported": "تم تصدير الحدث", + "title_copied": "تم نسخ العنوان", + "link_copied": "تم نسخ الرابط" + }, + "status": { + "loading_calendars": "جارٍ تحميل التقويمات...", + "loading_events": "جارٍ تحميل الأحداث..." + }, + "quick_create": { + "placeholder": "عنوان الحدث الجديد", + "aria_label": "إنشاء حدث سريع", + "add_title": "أضف عنوانًا", + "more_options": "خيارات أخرى" + }, + "nav_prev": "السابق", + "nav_next": "التالي", + "nav_open_menu": "فتح القائمة", + "import": { + "title": "استيراد تقويم", + "tab_file": "ملف", + "tab_url": "رابط", + "select_file": "اختر ملف ‎.ics", + "drop_file": "أو أفلت الملف هنا", + "supported_formats": "يدعم ملفات iCalendar (‎.ics)", + "url_description": "أدخل رابط تغذية iCalendar (‎.ics) خارجية لاستيراد الأحداث.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "يدعم روابط iCalendar (‎.ics)", + "fetch": "جلب", + "invalid_url": "يرجى إدخال رابط صالح", + "url_fetch_failed": "فشل جلب التقويم من الرابط", + "parsing": "جارٍ تحليل ملف التقويم...", + "parsed_events": "تم العثور على {count} حدث", + "no_events": "لم يتم العثور على أحداث في الملف", + "select_all": "تحديد الكل", + "deselect_all": "إلغاء تحديد الكل", + "target_calendar": "الاستيراد إلى التقويم", + "import_button": "استيراد المحدد", + "importing": "جارٍ استيراد الأحداث...", + "success": "تم استيراد {count} حدث بنجاح", + "error": "فشل استيراد التقويم", + "file_too_large": "الملف يتجاوز الحد الأقصى 10 ميغابايت", + "invalid_format": "تنسيق ملف التقويم غير صالح" + }, + "webcal_action": { + "title": "فتح رابط التقويم", + "description": "كيف تريد استخدام \"{name}\"؟", + "import_title": "استيراد لمرة واحدة", + "import_description": "جلب الأحداث الآن ونسخها إلى أحد تقويماتك.", + "subscribe_title": "اشتراك", + "subscribe_description": "إبقاء هذا التقويم متزامنًا تلقائيًا كتقويم منفصل.", + "cancel": "إلغاء" + }, + "management": { + "title": "إدارة التقويمات", + "description": "أنشئ تقويماتك وأعد تسميتها وخصّصها. انقر بالزر الأيمن على تقويم في الشريط الجانبي لتغيير لونه بسرعة.", + "name": "الاسم", + "name_placeholder": "اسم التقويم", + "color": "اللون", + "change_color": "تغيير اللون", + "random_color": "لون عشوائي جديد", + "add_calendar": "إضافة تقويم", + "edit": "تعديل", + "delete": "حذف", + "save": "حفظ", + "create": "إنشاء", + "cancel": "إلغاء", + "default": "افتراضي", + "confirm_delete": "حذف \"{name}\"؟ ستتم إزالة جميع الأحداث في هذا التقويم.", + "confirm_clear": "مسح جميع الأحداث من \"{name}\"؟ لا يمكن التراجع عن هذا.", + "set_default": "تعيين كافتراضي", + "default_updated": "تم تحديث التقويم الافتراضي", + "error_default": "فشل تعيين التقويم الافتراضي", + "clear_events": "مسح الأحداث", + "events_cleared": "تم مسح {count} حدث", + "error_clear": "فشل مسح أحداث التقويم", + "calendar_created": "تم إنشاء التقويم", + "calendar_updated": "تم تحديث التقويم", + "calendar_deleted": "تم حذف التقويم", + "color_updated": "تم تحديث لون التقويم", + "error_create": "فشل إنشاء التقويم", + "error_update": "فشل تحديث التقويم", + "error_delete": "فشل حذف التقويم", + "caldav_url": "رابط CalDAV", + "copy_url": "نسخ رابط CalDAV", + "url_copied": "تم نسخ رابط CalDAV إلى الحافظة", + "share": "مشاركة التقويم", + "new_event_in_calendar": "حدث جديد في هذا التقويم", + "shared_with": "مشترك مع {names}", + "shared_with_count": "{count, plural, zero {غير مشترك مع أحد} one {مشترك مع شخص واحد} two {مشترك مع شخصين} few {مشترك مع # أشخاص} many {مشترك مع # شخصًا} other {مشترك مع # شخص}}", + "move_up": "تحريك لأعلى", + "move_down": "تحريك لأسفل", + "kind": "المحتوى", + "kind_events": "الأحداث", + "kind_tasks": "المهام", + "kind_both": "الأحداث والمهام", + "kind_hint": "يحدد التطبيقات التي ترى هذا التقويم عند مزامنته عبر CalDAV. لا يمكن تغييره لاحقًا.", + "default_alerts": "التذكيرات الافتراضية", + "default_alerts_title": "التذكيرات الافتراضية لـ \"{name}\"", + "default_alerts_hint": "تحصل الأحداث الجديدة في هذا التقويم على هذه التذكيرات ما لم يتم تغييرها في الحدث. لا يؤدي تغييرها هنا إلى تغيير الأحداث الحالية. يرسل خادم البريد التذكيرات عبر البريد الإلكتروني.", + "default_alerts_timed": "الأحداث ذات الوقت المحدد", + "default_alerts_all_day": "أحداث طوال اليوم", + "default_alerts_updated": "تم تحديث التذكيرات الافتراضية" + }, + "subscription": { + "title": "اشتراك iCal", + "section_title": "اشتراكات iCal", + "description": "اشترك في تغذية iCalendar خارجية. ستتم مزامنة الأحداث تلقائيًا في تقويمها الخاص. يدعم روابط https:// وwebcal://.", + "url_label": "رابط التقويم", + "url_placeholder": "https://example.com/calendar.ics أو webcal://...", + "name_label": "اسم التقويم", + "name_placeholder": "مثال: العطلات الرسمية", + "color_label": "اللون", + "refresh_interval": "فترة التحديث", + "interval_15": "كل 15 دقيقة", + "interval_30": "كل 30 دقيقة", + "interval_60": "كل ساعة", + "interval_360": "كل 6 ساعات", + "interval_1440": "كل يوم", + "subscribe": "اشتراك", + "subscribing": "جارٍ الاشتراك...", + "save": "حفظ التغييرات", + "saving": "جارٍ الحفظ...", + "edit": "تعديل", + "edit_title": "تعديل الاشتراك", + "updated": "تم تحديث \"{name}\"", + "update_error": "فشل تحديث الاشتراك", + "invalid_url": "يرجى إدخال رابط صالح", + "success": "تم الاشتراك في \"{name}\"", + "error": "فشلت إضافة الاشتراك", + "refresh": "تحديث الآن", + "refresh_success": "تم تحديث الاشتراك", + "refresh_error": "فشل تحديث الاشتراك", + "unsubscribe": "إلغاء الاشتراك", + "confirm_delete": "إلغاء الاشتراك من \"{name}\"؟ ستتم إزالة التقويم وجميع أحداثه.", + "deleted": "تمت إزالة الاشتراك", + "delete_error": "فشلت إزالة الاشتراك", + "last_refreshed": "آخر تحديث: {time}" + }, + "tasks": { + "label": "المهام", + "no_tasks": "لا توجد مهام", + "no_title": "(بلا عنوان)", + "mark_complete": "تمييز كمكتملة", + "mark_incomplete": "تمييز كغير مكتملة", + "filter_all": "الكل", + "filter_pending": "معلّقة", + "filter_completed": "مكتملة", + "filter_overdue": "متأخرة", + "show_completed": "إظهار المكتملة", + "create": "مهمة جديدة", + "edit": "تعديل المهمة", + "title_placeholder": "عنوان المهمة", + "description_placeholder": "أضف وصفًا...", + "due_date": "تاريخ الاستحقاق", + "include_time": "تضمين الوقت", + "priority": "الأولوية", + "priority_none": "بلا", + "priority_high": "مرتفعة", + "priority_medium": "متوسطة", + "priority_low": "منخفضة", + "progress": "الحالة", + "progress_needs_action": "بحاجة إلى إجراء", + "progress_in_process": "قيد التنفيذ", + "progress_completed": "مكتملة", + "progress_cancelled": "ملغاة", + "calendar": "التقويم", + "alert": "تذكير", + "alert_none": "بلا", + "alert_at_time": "عند وقت الاستحقاق", + "alert_5min": "قبل 5 دقائق", + "alert_15min": "قبل 15 دقيقة", + "alert_30min": "قبل 30 دقيقة", + "alert_1hr": "قبل ساعة", + "alert_1day": "قبل يوم", + "delete": "حذف", + "cancel": "إلغاء", + "save": "حفظ", + "quick_add_placeholder": "أضف مهمة...", + "due_today": "اليوم", + "due_tomorrow": "غدًا", + "overdue": "متأخرة" + }, + "toolbar": { + "more": "إجراءات أخرى", + "select_view": "تغيير العرض، الحالي: {view}" + }, + "other_calendars": "تقويمات أخرى", + "calendar_options": "خيارات {name}" + }, + "calendar_event_notifications": { + "someone": "شخص ما", + "invited": "دعاك {name} إلى \"{title}\"", + "updated": "حدّث {name} \"{title}\"", + "cancelled": "ألغى {name} \"{title}\"", + "untitled": "حدث بدون عنوان" + }, + "share_notifications": { + "someone": "شخص ما", + "shared": "شارك {name} معك {kind} \"{object}\"", + "changed": "غيّر {name} صلاحيتك على {kind} \"{object}\"", + "revoked": "أزال {name} صلاحيتك على {kind} \"{object}\"", + "object": { + "folder": "المجلد", + "calendar": "التقويم", + "address_book": "دفتر العناوين", + "files": "مجلد الملفات" + } + }, + "sharing": { + "title": "مشاركة \"{name}\"", + "description": "امنح الوصول لمستخدمين أو مجموعات أخرى على هذا الخادم. تُطبَّق التغييرات فورًا.", + "no_shares": "لم تتم المشاركة مع أي شخص بعد.", + "add_person": "إضافة شخص أو مجموعة", + "search_placeholder": "بحث بالاسم أو البريد الإلكتروني…", + "loading_principals": "جارٍ تحميل المستخدمين…", + "no_principals": "لم يتم العثور على مستخدمين أو مجموعات أخرى.", + "no_match": "لا توجد نتائج مطابقة.", + "remove": "إزالة الوصول", + "group": "مجموعة", + "share_added": "تم منح الوصول", + "share_updated": "تم تحديث الوصول", + "share_removed": "تمت إزالة الوصول", + "share_failed": "فشل تحديث المشاركة", + "preset": { + "freeBusy": "التوفر/الانشغال فقط", + "read": "قراءة فقط", + "readWrite": "قراءة وكتابة", + "manager": "مدير", + "custom": "مخصص" + } + }, + "advanced_search": { + "title": "بحث متقدم", + "from": "من", + "from_placeholder": "بريد المرسل أو اسمه", + "to": "إلى", + "to_placeholder": "بريد المستلم أو اسمه", + "subject": "الموضوع", + "subject_placeholder": "يحتوي الموضوع على...", + "body": "النص", + "body_placeholder": "يحتوي النص على...", + "folder": "المجلد", + "all_folders": "جميع المجلدات", + "all_folders_except_spam_trash": "جميع المجلدات باستثناء البريد المزعج والمهملات", + "has_attachment": "المرفقات", + "date_after": "بعد", + "date_before": "قبل", + "size_min": "أكبر من (كيلوبايت)", + "size_max": "أصغر من (كيلوبايت)", + "starred": "مميز بنجمة", + "unread": "غير مقروءة", + "read": "مقروءة", + "yes": "نعم", + "no": "لا", + "clear": "مسح", + "clear_all": "مسح الكل", + "filters_active": "{count} مرشح", + "filters_active_plural": "{count} مرشحات", + "toggle_filters": "المزيد", + "search_hint": "استخدم المرشحات المتقدمة لبحث دقيق", + "advanced_filters_tooltip": "مرشحات البحث المتقدم", + "results_found": "{count, plural, =0 {لم يتم العثور على نتائج} one {تم العثور على # نتيجة} other {تم العثور على # نتيجة}}", + "results_found_more": "تم العثور على {count}+ نتيجة", + "suggestions_recent": "عمليات البحث الأخيرة", + "suggestions_people": "الأشخاص", + "suggestions_from": "البحث في البريد من {address}", + "suggestions_to": "البحث في البريد إلى {address}", + "suggestions_remove_recent": "إزالة من عمليات البحث الأخيرة", + "has_words": "يحتوي على الكلمات", + "size": "الحجم", + "date_within": "التاريخ", + "search_in": "البحث في", + "has_attachment_check": "يحتوي على مرفق", + "search": "بحث" + }, + "welcome": { + "title": "مرحبًا بك في صندوق بريدك", + "tip_compose": "اضغط c لإنشاء رسالة جديدة", + "tip_shortcuts": "اضغط ؟ لعرض جميع اختصارات لوحة المفاتيح", + "tip_sidebar": "ستجد التقويم وجهات الاتصال والإعدادات في قائمة الشريط الجانبي", + "tip_settings": "خصّص تجربتك من الإعدادات", + "tip_compose_touch": "اضغط زر القلم لكتابة رسالة جديدة", + "tip_sidebar_touch": "ستجد التقويم وجهات الاتصال والإعدادات في الشريط السفلي", + "got_it": "فهمت", + "settings": "الإعدادات", + "dismiss": "تجاهل", + "start_tour": "بدء الجولة" + }, + "demo_welcome": { + "title": "مرحبًا بك في بريد Bulwark", + "description": "استكشف عميل بريد ويب متكامل الميزات - مباشرة في متصفحك. تبقى جميع البيانات على جهازك، فلا تتردد في تجربة كل شيء.", + "feature_email": "قراءة الرسائل وكتابتها", + "feature_organize": "الوسوم والنجوم والمجلدات", + "feature_shortcuts": "اختصارات لوحة المفاتيح", + "feature_privacy": "نسخة تجريبية خاصة 100%", + "hint": "انقر على أي رسالة على اليسار للبدء، أو خذ الجولة أدناه." + }, + "files": { + "office_edit": "تحرير المستند", + "office_loading": "جارٍ فتح محرر المستندات…", + "office_error": "تعذر فتح محرر المستندات.", + "office_close": "إغلاق المحرر", + "open_in_new_tab": "فتح في علامة تبويب جديدة", + "title": "الملفات", + "search_placeholder": "بحث في الملفات...", + "empty_state_title": "لا توجد ملفات بعد", + "upload_files": "رفع الملفات", + "new_folder": "مجلد جديد", + "new_folder_name": "اسم المجلد", + "rename": "إعادة تسمية", + "rename_title": "إعادة تسمية", + "new_name": "الاسم الجديد", + "name_forbidden_chars": "لا يسمح الخادم بهذه الأحرف في الأسماء: {chars}", + "name_reserved": "هذا الاسم محجوز على الخادم.", + "delete": "حذف", + "delete_confirm_title": "حذف المورد", + "delete_confirm_message": "هل أنت متأكد أنك تريد حذف \"{name}\"؟ لا يمكن التراجع عن هذا.", + "download": "تنزيل", + "name": "الاسم", + "size": "الحجم", + "modified": "تاريخ التعديل", + "type": "النوع", + "folder": "مجلد", + "file": "ملف", + "no_accounts": "لا توجد حسابات متصلة.", + "uploading": "جارٍ الرفع...", + "upload_success": "{count, plural, one {تم رفع ملف واحد} other {تم رفع # ملف}}", + "upload_canceled": "تم إلغاء الرفع", + "upload_canceled_partial": "{count, plural, zero {لم يُرفع أي ملف} one {تم رفع ملف واحد} two {تم رفع ملفين} few {تم رفع # ملفات} many {تم رفع # ملفًا} other {تم رفع # ملف}} قبل الإلغاء", + "upload_error": "فشل رفع الملف", + "create_folder_success": "تم إنشاء المجلد", + "create_folder_error": "فشل إنشاء المجلد", + "delete_success": "تم الحذف بنجاح", + "delete_error": "فشل الحذف", + "rename_success": "تمت إعادة التسمية بنجاح", + "rename_error": "فشلت إعادة التسمية", + "download_error": "فشل التنزيل", + "not_available": "تخزين الملفات غير متاح على هذا الخادم", + "cancel": "إلغاء", + "create": "إنشاء", + "save": "حفظ", + "no_results": "لا توجد ملفات مطابقة لبحثك", + "batch_delete_confirm_message": "هل أنت متأكد أنك تريد حذف {count, plural, one {عنصر واحد} other {# عنصر}}؟ لا يمكن التراجع عن هذا.", + "batch_delete_success": "{count, plural, one {تم حذف عنصر واحد} other {تم حذف # عنصر}}", + "grid_view": "عرض شبكي", + "list_view": "عرض قائمة", + "details": "التفاصيل", + "path": "المسار", + "preview": "معاينة", + "preview_error": "فشل تحميل المعاينة", + "cut": "قص", + "copy": "نسخ", + "paste": "لصق", + "move_success": "{count, plural, one {تم نقل عنصر واحد} other {تم نقل # عنصر}}", + "move_error": "فشل النقل", + "paste_success": "تم اللصق بنجاح", + "paste_error": "فشل اللصق", + "new_text_file": "ملف نصي جديد", + "file_name": "اسم الملف", + "retry": "إعادة المحاولة", + "refresh": "تحديث", + "duplicate": "تكرار", + "duplicate_success": "تم التكرار بنجاح", + "duplicate_error": "فشل التكرار", + "share": "مشاركة", + "shared": "مشترك", + "shared_with_me": "مشترك معي", + "shared_by": "تمت المشاركة بواسطة {name}", + "create_file_success": "تم إنشاء الملف", + "create_file_error": "فشل إنشاء الملف", + "favorites": "المفضّلة", + "recent": "الأخيرة", + "show_in_folder": "إظهار في المجلد", + "remove_from_recent": "إزالة من الأخيرة", + "upload_folder": "رفع مجلد", + "file_too_large": "\"{name}\" يتجاوز الحد الأقصى لحجم الملف ({max})", + "undo": "تراجع", + "undo_success": "تم التراجع عن الإجراء", + "undo_error": "فشل التراجع", + "toolbar": "إجراءات الملفات", + "open_folder_tree": "فتح شجرة المجلدات", + "file_list": "الملفات والمجلدات", + "context_menu": "الإجراءات", + "settings_ascending": "تصاعدي", + "settings_descending": "تنازلي", + "disabled_title": "ميزة الملفات معطّلة بواسطة المسؤول", + "disabled_description": "قد تتسبب عمليات رفع الملفات الكبيرة عبر WebDAV في زعزعة استقرار Stalwart/RocksDB، بما في ذلك انهيارات نفاد الذاكرة واستخدام غير قابل للاسترجاع لمساحة القرص. قد لا تُحذف الملفات المحذوفة فورًا من مخزن الكائنات الثنائية. لا يُنصح بهذه الميزة لبيئات الإنتاج.", + "migration_title": "جارٍ تحديث ملفاتك…", + "migration_description": "جارٍ تنظيم المجلدات والملفات في هيكلها الصحيح. يحدث هذا مرة واحدة فقط.", + "new": "جديد", + "my_files": "ملفاتي", + "folders": "المجلدات", + "files_heading": "الملفات", + "open": "فتح", + "star": "إضافة إلى المفضّلة", + "unstar": "إزالة من المفضّلة", + "expand_folder": "توسيع {name}", + "collapse_folder": "طي {name}", + "sidebar_hint": "تظهر هنا المجلدات المفضّلة والملفات التي فتحتها.", + "breadcrumbs": "مسار المجلد", + "show_path": "إظهار المجلدات المخفية في المسار", + "selection_actions": "إجراءات العناصر المحددة", + "selected_count": "{count, plural, zero {لم يُحدد أي عنصر} one {تم تحديد عنصر واحد} two {تم تحديد عنصرين} few {تم تحديد # عناصر} many {تم تحديد # عنصرًا} other {تم تحديد # عنصر}}", + "clear_selection": "مسح التحديد", + "select_all": "تحديد الكل", + "select_item": "تحديد {name}", + "more_actions": "إجراءات أخرى لـ {name}", + "paste_here": "لصق في هذا المجلد", + "paste_count": "{count, plural, zero {لصق 0 عنصر} one {لصق عنصر واحد} two {لصق عنصرين} few {لصق # عناصر} many {لصق # عنصرًا} other {لصق # عنصر}}", + "sort_by": "ترتيب حسب", + "view_mode": "طريقة العرض", + "clear_search": "مسح البحث", + "no_results_for": "لا شيء في هذا المجلد يطابق \"{query}\"", + "download_count": "{count, plural, zero {تنزيل 0 ملف} one {تنزيل ملف واحد} two {تنزيل ملفين} few {تنزيل # ملفات} many {تنزيل # ملفًا} other {تنزيل # ملف}}", + "delete_count": "{count, plural, zero {حذف 0 عنصر} one {حذف عنصر واحد} two {حذف عنصرين} few {حذف # عناصر} many {حذف # عنصرًا} other {حذف # عنصر}}", + "close_details": "إغلاق التفاصيل", + "file_kind": "ملف {ext}", + "folder_summary": "{folders, plural, =0 {لا توجد مجلدات} zero {لا توجد مجلدات} one {مجلد واحد} two {مجلدان} few {# مجلدات} many {# مجلدًا} other {# مجلد}}، {files, plural, =0 {لا توجد ملفات} zero {لا توجد ملفات} one {ملف واحد} two {ملفان} few {# ملفات} many {# ملفًا} other {# ملف}}", + "details_hint": "حدد ملفًا أو مجلدًا لعرض تفاصيله.", + "sharing": "المشاركة", + "shared_with_count": "{count, plural, zero {غير مشترك مع أحد} one {مشترك مع شخص واحد} two {مشترك مع شخصين} few {مشترك مع # أشخاص} many {مشترك مع # شخصًا} other {مشترك مع # شخص}}", + "only_you": "أنت فقط", + "empty_folder_title": "هذا المجلد فارغ", + "empty_folder_description": "أفلت الملفات هنا، أو ارفعها من جهازك.", + "empty_folder_read_only": "لا يمكنك إضافة ملفات إلى هذا المجلد.", + "choose_account": "اختر حسابًا", + "drop_to_upload": "أفلت للرفع إلى {folder}", + "uploading_count": "جارٍ رفع {current} من {total}", + "dismiss": "تجاهل", + "zoom_in": "تكبير", + "zoom_out": "تصغير", + "zoom_reset": "ملاءمة الشاشة", + "rotate": "تدوير", + "close_preview": "إغلاق" + }, + "smime": { + "your_certificates": "شهاداتك", + "your_certificates_desc": "استيراد وإدارة شهادات S/MIME الخاصة بك لتوقيع الرسائل وتشفيرها", + "recipient_certificates": "شهادات المستلمين", + "recipient_certificates_desc": "الشهادات العامة لتشفير الرسائل إلى المستلمين", + "identity_bindings": "ربط الهوية بالمفتاح", + "identity_bindings_desc": "ربط شهادات S/MIME بهويات بريدك الإلكتروني", + "defaults_title": "الإعدادات الافتراضية", + "defaults_desc": "إعداد سلوك التوقيع والتشفير الافتراضي", + "import_pkcs12": "استيراد PKCS#12 (‎.p12/‎.pfx)", + "import_public_cert": "استيراد شهادة", + "no_certificates": "لم تُستورد أي شهادات بعد", + "no_recipient_certs": "لا توجد شهادات مستلمين", + "expires": "تنتهي في", + "expired": "منتهية الصلاحية", + "bound_to": "مرتبطة بـ", + "no_key_bound": "بلا", + "lock": "قفل المفتاح", + "unlock": "فتح المفتاح", + "details": "عرض التفاصيل", + "delete": "حذف", + "encrypt_by_default": "التشفير افتراضيًا", + "encrypt_by_default_desc": "تشفير الرسائل تلقائيًا عندما تتوفر شهادات لجميع المستلمين", + "remember_unlocked": "تذكّر المفاتيح المفتوحة", + "remember_unlocked_desc": "إبقاء المفاتيح مفتوحة طوال مدة جلسة المتصفح هذه", + "sign_default_for": "التوقيع افتراضيًا لـ", + "enter_p12_passphrase": "أدخل عبارة مرور PKCS#12", + "p12_passphrase_desc": "أدخل عبارة المرور التي تحمي ملف الشهادة هذا", + "enter_storage_passphrase": "تعيين عبارة مرور التخزين", + "storage_passphrase_desc": "اختر عبارة مرور لحماية هذا المفتاح أثناء التخزين في متصفحك", + "next": "التالي", + "import": "استيراد", + "unlock_key": "فتح المفتاح", + "unlock_key_desc": "أدخل عبارة مرور التخزين لفتح هذا المفتاح للتوقيع أو فك التشفير", + "passphrase_placeholder": "أدخل عبارة المرور", + "confirm_passphrase_placeholder": "تأكيد عبارة المرور", + "passphrase_mismatch": "عبارتا المرور غير متطابقتين", + "cancel": "إلغاء", + "processing": "جارٍ المعالجة…", + "close": "إغلاق", + "certificate_details": "تفاصيل الشهادة", + "cert_subject": "صاحب الشهادة", + "cert_issuer": "الجهة المُصدِرة", + "cert_email": "البريد الإلكتروني", + "cert_serial": "الرقم التسلسلي", + "cert_validity": "الصلاحية", + "cert_fingerprint": "البصمة (SHA-256)", + "cert_algorithm": "الخوارزمية", + "cert_capabilities": "القدرات", + "cert_source": "المصدر", + "cert_expired": "انتهت صلاحية هذه الشهادة", + "cert_not_yet_valid": "هذه الشهادة غير سارية بعد", + "cap_sign": "التوقيع", + "cap_encrypt": "التشفير", + "cap_none": "بلا", + "show_passphrase": "إظهار عبارة المرور", + "hide_passphrase": "إخفاء عبارة المرور", + "sign_toggle": "توقيع", + "encrypt_toggle": "تشفير", + "missing_recipient_certs": "شهادات مفقودة لـ: {emails}", + "missing_sender_cert": "لا توجد شهادة مرتبطة بهذه الهوية", + "status_encrypted_ok": "تم تشفير هذه الرسالة", + "status_encrypted_no_key": "هذه الرسالة مشفّرة لكن لم يتم العثور على مفتاح مطابق", + "status_encrypted_failed": "فشل فك تشفير هذه الرسالة", + "status_signed_valid": "تم التحقق من التوقيع", + "status_signed_invalid": "فشل التحقق من التوقيع", + "status_signed_expired_cert": "موقّعة بشهادة منتهية الصلاحية", + "status_signed_self_signed": "التوقيع صالح، لكن الشهادة موقّعة ذاتيًا (غير موثوقة)", + "status_signed_mismatch": "التوقيع صالح، لكن الموقّع لا يطابق المرسل", + "status_unsupported": "تنسيق S/MIME غير مدعوم", + "auto_import_signer_certs": "استيراد شهادات الموقّعين تلقائيًا", + "auto_import_signer_certs_desc": "حفظ الشهادات تلقائيًا من الرسائل الموقّعة الموثّقة للاستخدام في التشفير مستقبلًا", + "export": "تصدير", + "enter_export_passphrase": "تعيين عبارة مرور التصدير", + "export_passphrase_desc": "اختر عبارة مرور لحماية ملف PKCS#12 المُصدَّر", + "export_storage_desc": "أدخل عبارة مرور التخزين لفك تشفير المفتاح من أجل التصدير", + "incorrect_passphrase": "عبارة المرور غير صحيحة" + }, + "tour": { + "step_counter": "الخطوة {current} من {total}", + "skip": "تخطي الجولة", + "back": "السابق", + "next": "التالي", + "finish": "إنهاء", + "take_a_tour": "خذ جولة في الواجهة", + "restart_title": "الجولة التعريفية", + "restart_desc": "إعادة عرض الجولة التعريفية للواجهة", + "restart_button": "إعادة تشغيل الجولة", + "show_on_new_devices_title": "الإظهار على الأجهزة الجديدة", + "show_on_new_devices_desc": "إعادة عرض شعار الترحيب والجولة عند أول تسجيل دخول على جهاز جديد، حتى لو أكملتها بالفعل في مكان آخر", + "sidebar_title": "صناديق بريدك", + "sidebar_desc": "هذا هو الشريط الجانبي لمجلداتك. انقر على أي صندوق بريد لعرض رسائله. يمكنك إنشاء مجلدات، وسحب الرسائل بينها، ورؤية عدد غير المقروء بلمحة.", + "compose_title": "إنشاء رسالة", + "compose_desc": "انقر هنا لكتابة رسالة جديدة. يمكنك إضافة مستلمين ومرفقات واستخدام التنسيق النصي المنسّق.", + "search_title": "البحث في بريدك", + "search_desc": "ابحث حسب المرسل أو الموضوع أو المحتوى. انقر على أيقونة المرشح لخيارات متقدمة مثل نطاق التاريخ والمرفقات والرسائل المميزة بنجمة.", + "email_list_title": "قائمة رسائلك", + "email_list_desc": "تظهر الرسائل هنا. انقر على واحدة لقراءتها على اليمين. استخدم مربع الاختيار لتحديد عدة رسائل، ثم انقلها أو احذفها أو ضع لها وسمًا دفعة واحدة.", + "email_viewer_title": "جزء القراءة", + "email_viewer_desc": "تُفتح الرسالة المحددة هنا. الرد أو إعادة التوجيه أو الأرشفة أو الحذف باستخدام أزرار شريط الأدوات. يمكنك أيضًا تمييز الرسائل بنجمة أو إضافة وسوم ملونة.", + "keywords_title": "الوسوم الملونة", + "keywords_desc": "نظّم بريدك بوسوم ملونة. اسحب رسالة إلى وسم لتصنيفها، أو انقر بالزر الأيمن على رسالة لتعيين وسوم لها.", + "calendar_title": "التقويم", + "calendar_desc": "انتقل إلى التقويم لإدارة أحداثك. أنشئ الأحداث واضبط التذكيرات واستعرض تخطيطات اليوم أو الأسبوع أو الشهر.", + "contacts_title": "جهات الاتصال", + "contacts_desc": "دفتر عناوينك موجود هنا. استورد جهات الاتصال، وأنشئ مجموعات، وانقر على أي جهة اتصال لرؤية تفاصيلها الكاملة.", + "settings_title": "الإعدادات", + "settings_desc": "خصّص كل شيء: السمة، والكثافة، والتوقيعات، والمرشحات، واختصارات لوحة المفاتيح، وإعدادات التقويم الافتراضية، والمزيد.", + "shortcuts_title": "اختصارات لوحة المفاتيح", + "shortcuts_desc": "يحب المستخدمون المتقدمون هذا. اضغط ؟ في أي وقت لرؤية جميع الاختصارات المتاحة. يمكنك التنقل والكتابة وإدارة الرسائل دون لمس الفأرة.", + "compose_open_title": "محرر الرسائل", + "compose_open_desc": "هذا هو محرر الرسائل. أضف مستلمين، اكتب رسالتك، أرفق ملفات، واستخدم التنسيق النصي المنسّق. يمكنك أيضًا حفظ المسودات واستخدام القوالب.", + "calendar_view_title": "تقويمك", + "calendar_view_desc": "هذا تقويمك مع أحداث نموذجية. يمكنك التبديل بين عروض اليوم والأسبوع والشهر وجدول الأعمال باستخدام شريط الأدوات.", + "create_event_title": "إنشاء حدث", + "create_event_desc": "انقر على هذا الزر لإنشاء حدث تقويم جديد. يمكنك تعيين عنوان وتاريخ ووقت وإضافة مشاركين.", + "event_modal_title": "تفاصيل الحدث", + "event_modal_desc": "هذا نموذج الحدث. املأ العنوان، واختر تاريخًا ووقتًا، وأضف موقعًا أو مشاركين. اضغط حفظ عند الانتهاء - أو أغلقه وتابع.", + "contacts_list_title": "جهات اتصالك", + "contacts_list_desc": "هذه جهات اتصالك. انقر على أي جهة اتصال لرؤية تفاصيلها الكاملة على اليمين. يمكنك أيضًا إنشاء جهات اتصال جديدة، أو استيراد بطاقات vCard، أو تنظيم جهات الاتصال في مجموعات.", + "settings_tabs_title": "قائمة الإعدادات", + "settings_tabs_desc": "هذه كل فئات الإعدادات. خصّص مظهرك، وأدر الهويات، وأعدّ مرشحات البريد، واضبط تقويمك، وأكثر من ذلك بكثير.", + "files_title": "تخزين الملفات", + "files_desc": "يتيح لك متصفح الملفات رفع الملفات وتنظيمها ومشاركتها - كمحرك سحابي شخصي مدمج في بريدك.", + "demo_banner_title": "أدوات التحكم التجريبية", + "demo_banner_desc": "أنت في الوضع التجريبي - يبقى كل شيء في متصفحك. اضغط \"إعادة تعيين التجربة\" في أي وقت للبدء من جديد ببيانات نموذجية نظيفة.", + "quota_title": "استخدام التخزين", + "quota_desc": "تتبّع حجم صندوق بريدك هنا. تمتلئ الدائرة كلما استخدمت مساحة أكبر." + }, + "unified_mailbox": { + "search_unavailable": "البحث غير متاح في العرض الموحّد" + }, + "quote_header": { + "reply_line": "في {date}، كتب {from}:", + "forwarded_separator": "---------- رسالة موجّهة ----------", + "from_label": "من", + "date_label": "التاريخ", + "subject_label": "الموضوع" + }, + "pwa_install": { + "title": "تثبيت {appName}", + "description": "ثبّت تطبيقنا للوصول السريع والدعم دون اتصال.", + "not_now": "ليس الآن", + "install": "تثبيت", + "dont_remind": "عدم التذكير مرة أخرى", + "dismiss_aria": "تجاهل مطالبة التثبيت" + }, + "deep_link": { + "message_not_found": "لم تعد هذه الرسالة متاحة.", + "thread_not_found": "لم تعد هذه المحادثة متاحة.", + "folder_not_found": "لم يعد هذا المجلد متاحًا.", + "event_not_found": "لم يعد هذا الحدث متاحًا.", + "file_not_found": "لم يعد هذا الملف متاحًا.", + "account_unavailable": "سجّل الدخول إلى ذلك الحساب لفتح هذا الرابط.", + "copy_message": "نسخ رابط الرسالة", + "copy_conversation": "نسخ رابط المحادثة", + "copy_event": "نسخ رابط الحدث", + "copied": "تم نسخ الرابط", + "copy_failed": "تعذّر نسخ الرابط" + }, + "plugins": { + "oauth_callback": { + "completing": "جارٍ إكمال التفويض…", + "completing_message": "يرجى الانتظار بينما يتم إنشاء الاتصال.", + "received_title": "تم استلام التفويض", + "received_message": "يمكنك العودة إلى علامة التبويب التي بدأت فيها الاتصال.", + "failed_title": "فشل التفويض", + "denied": "تم رفض التفويض.", + "failed_detail": "فشل التفويض: {error}", + "missing_params": "رمز التفويض أو الحالة مفقود.", + "delivery_failed": "تعذّر إرسال نتيجة التفويض إلى تبويب التطبيق. يرجى إغلاق هذه النافذة والمحاولة مرة أخرى." + }, + "settings": { + "title": "الإضافات", + "description": "إضافات أضافها المسؤول. فعّلها أو أوقفها لحسابك.", + "description_uploads": "تضيف الإضافات ميزات إلى تطبيق البريد. فعّلها أو أوقفها لحسابك، أو ثبّت إضافاتك الخاصة.", + "description_demo": "الإضافات غير متاحة في الوضع التجريبي.", + "install_button": "تثبيت إضافة", + "install_hint": "اختر ملف ZIP لإضافة.", + "install_hint_approval": "اختر ملف ZIP لإضافة. يجب أن يوافق عليها المسؤول قبل أن تتمكن من تفعيلها.", + "install_uploads_off": "لا يسمح المسؤول بتثبيت الإضافات.", + "install_managed_id": "يوفّر المسؤول بالفعل إضافة بهذا الاسم، لذا لا يمكن استبدالها.", + "install_invalid": "هذا الملف ليس إضافة قابلة للتثبيت: {details}", + "toast_installed": "تم تثبيت الإضافة. فعّلها أدناه.", + "toast_installed_pending": "تم تثبيت الإضافة وإرسالها إلى المسؤول للموافقة عليها.", + "toast_enabled": "تم تفعيل {name}", + "toast_disabled": "تم إيقاف {name}", + "toast_removed": "تمت إزالة {name}", + "toast_forced": "يُبقي المسؤول {name} مفعّلة", + "toast_approval_requested": "{name} بانتظار موافقة المسؤول", + "empty_title": "لا توجد إضافات بعد", + "empty_admin": "لم يُضف المسؤول أي إضافات.", + "empty_uploads": "ثبّت إضافة من ملف ZIP للبدء.", + "empty_demo": "تحتاج الإضافات إلى حساب حقيقي، لذا لا يتضمن الوضع التجريبي أيًّا منها.", + "syncing": "جارٍ التحقق من الإضافات مع الخادم…", + "status_installed": "متوقفة", + "status_enabled": "جارٍ التشغيل", + "status_running": "مفعّلة", + "status_disabled": "متوقفة", + "status_error": "مشكلة", + "badge_forced": "مطلوبة", + "badge_managed": "من المسؤول", + "badge_yours": "ثبّتها أنت", + "badge_awaiting": "بانتظار الموافقة", + "toggle_label": "تفعيل {name} أو إيقافها", + "forced_note": "يشترط المسؤول هذه الإضافة، لذا لا يمكن إيقافها.", + "permissions": "ما يمكنها فعله", + "permissions_managed": "ما يمكنها فعله (بموافقة المسؤول)", + "settings": "الإعدادات", + "remove": "إزالة", + "remove_title": "إزالة {name}؟", + "remove_message": "ستُحذف الإضافة وكل ما خزّنته في هذا المتصفح. يمكنك تثبيتها مرة أخرى لاحقًا.", + "remove_confirm": "إزالة", + "error_user_plugins_off": "أوقف المسؤول الإضافات التي يثبّتها المستخدمون، لذا لا تعمل هذه الإضافة.", + "error_approval_unavailable": "ثُبّتت هذه الإضافة قبل أن تصبح الموافقة مطلوبة. أزلها وثبّتها مرة أخرى لإرسالها للموافقة.", + "error_approval_denied": "لم يوافق المسؤول على هذه الإضافة.", + "error_approval_pending": "بانتظار موافقة المسؤول على هذه الإضافة.", + "error_auto_disabled": "تم إيقافها لأنها فشلت مرارًا. شغّلها مرة أخرى للمحاولة من جديد.", + "accounts_heading": "الحسابات المرتبطة", + "account_on": "{label} على {host}", + "account_waiting": "{label} على {host} (لم يكتمل تسجيل الدخول)", + "disconnect": "فصل", + "disconnect_failed": "تعذّر فصل {label}" + }, + "consent": { + "title": "هل تسمح لـ {name} بذلك؟", + "intro": "تطلب الإضافة الأذونات أدناه. لن تحصل عليها إلا إذا سمحت بذلك.", + "high_risk_note": "العناصر المميّزة تمنح الإضافة وصولًا إلى محتوى بريدك أو تتيح لها إرسال البيانات إلى جهات أخرى. لا تسمح بها إلا للإضافات التي تثق بها.", + "revoke_hint": "يمكنك إيقاف الإضافة أو إزالتها من الإعدادات في أي وقت.", + "deny": "عدم السماح", + "allow": "سماح" + }, + "permissions": { + "none": "لا تحتاج إلى أذونات خاصة.", + "high_risk": "تتطلب الحذر", + "email_read": { + "title": "قراءة بريدك", + "body": "عرض المرسلين والمستلمين والمواضيع والنص الكامل لأي رسالة في حسابك، ومجلداتك." + }, + "email_write": { + "title": "تغيير بريدك وتنظيمه", + "body": "إضافة الوسوم والعلامات والكلمات المفتاحية الأخرى إلى رسائلك أو إزالتها، ونقلها بين مجلداتك وإلى الأرشيف، وحفظ المسودات." + }, + "email_send": { + "title": "تغيير البريد قبل إرساله", + "body": "قراءة وتغيير المستلمين والموضوع والنص والمرفقات في الرسائل التي تكتبها، ومنع إرسال رسالة." + }, + "crypto_full": { + "title": "وصول كامل إلى هذا التطبيق", + "body": "تعمل بالصلاحيات نفسها التي يملكها تطبيق البريد. يمكنها قراءة كل بريدك ومفاتيحك المخزّنة والتصرف نيابةً عنك. لا تسمح بذلك إلا للإضافات التي تثق بها تمامًا." + }, + "email_raw_send": { + "title": "إرسال رسائل أنشأتها بنفسها", + "body": "إرسال رسائل كاملة من حسابك، مثل الرسائل الموقّعة أو المشفّرة." + }, + "email_blob_read": { + "title": "قراءة المحتوى الخام للرسائل", + "body": "قراءة البيانات الأصلية لرسائلك ومرفقاتها، ولكل ملف ترفقه قبل إرساله." + }, + "email_blob_write": { + "title": "استبدال المرفقات قبل الإرسال", + "body": "رفع ملفات إلى خادم البريد واستبدال ملف أرفقته بملف آخر قبل إرسال الرسالة." + }, + "email_render_takeover": { + "title": "تغيير طريقة عرض الرسائل", + "body": "استبدال نص الرسائل التي تفتحها ونص قائمة الرسائل، مثلًا لعرض محتوى مفكوك التشفير." + }, + "calendar_read": { + "title": "قراءة تقويمك", + "body": "عرض تقويماتك وأحداثها." + }, + "calendar_write": { + "title": "تغيير تقويمك", + "body": "إنشاء الأحداث وتغييرها وحذفها." + }, + "contacts_read": { + "title": "قراءة جهات اتصالك", + "body": "البحث في دفاتر العناوين وقراءتها." + }, + "contacts_write": { + "title": "تغيير جهات اتصالك", + "body": "إنشاء جهات الاتصال ودفاتر العناوين وتغييرها وحذفها." + }, + "files_read": { + "title": "قراءة ملفاتك", + "body": "تصفّح الملفات المخزّنة في حسابك وفتحها." + }, + "files_write": { + "title": "تغيير ملفاتك", + "body": "رفع الملفات في حسابك وإعادة تسميتها ونقلها وحذفها." + }, + "identity_read": { + "title": "عرض عناوين المرسل", + "body": "عرض العناوين التي ترسل منها وتوقيعاتها." + }, + "identity_write": { + "title": "تغيير عناوين المرسل", + "body": "إنشاء عناوين المرسل والتوقيعات وتغييرها وحذفها." + }, + "filters_read": { + "title": "قراءة مرشحات البريد", + "body": "عرض القواعد التي تفرز بريدك الوارد." + }, + "filters_write": { + "title": "تغيير مرشحات البريد", + "body": "إضافة قواعد إلى المرشحات على الخادم. يمكن لهذه القواعد نقل البريد الوارد أو إعادة توجيهه أو حذفه." + }, + "tasks_read": { + "title": "قراءة مهامك", + "body": "عرض قوائم المهام والمهام." + }, + "tasks_write": { + "title": "تغيير مهامك", + "body": "إنشاء المهام وتغييرها وإكمالها وحذفها." + }, + "templates_read": { + "title": "قراءة قوالبك", + "body": "عرض قوالب الرسائل المحفوظة." + }, + "templates_write": { + "title": "تغيير قوالبك", + "body": "إنشاء قوالب الرسائل وتغييرها وحذفها." + }, + "smime_read": { + "title": "عرض حالة S/MIME", + "body": "معرفة ما إذا كانت لديك مفاتيح وشهادات S/MIME، دون الوصول إلى المفاتيح." + }, + "vacation_read": { + "title": "عرض الرد التلقائي أثناء الغياب", + "body": "معرفة ما إذا كان الرد التلقائي مفعّلًا وما يقوله." + }, + "vacation_write": { + "title": "تغيير الرد التلقائي أثناء الغياب", + "body": "تفعيل الرد التلقائي أو إيقافه وتغيير نصه وتواريخه." + }, + "settings_read": { + "title": "قراءة إعداداتك", + "body": "عرض تفضيلاتك وتعريفات الوسوم." + }, + "settings_write": { + "title": "تغيير إعداداتك", + "body": "إضافة الوسوم وإعادة ترتيبها وتغيير تفضيلاتك." + }, + "security_read": { + "title": "عرض أمان حسابك", + "body": "معرفة ما إذا كانت المصادقة الثنائية والتشفير مفعّلين، دون الوصول إلى البيانات السرية." + }, + "auth_observe": { + "title": "معرفة وقت تسجيل دخولك", + "body": "تلقّي إشعار عند تسجيل دخولك أو تبديل الحسابات أو تجديد جلستك." + }, + "auth_emit": { + "title": "تسجيل خروجك", + "body": "تسجيل خروجك من تطبيق البريد." + }, + "account_read": { + "title": "عرض حساباتك", + "body": "عرض الحسابات التي أضفتها، مع عناوينها وخوادمها." + }, + "http_post": { + "title": "استخدام خادم البريد نيابةً عنك", + "body": "استدعاء نقاط النهاية هذه على الخادم ببيانات دخولك:" + }, + "http_fetch": { + "title": "الاتصال بمواقع أخرى", + "body": "إرسال طلبات إلى هذه المواقع. قد يُرسل إليها أي شيء تستطيع الإضافة قراءته:" + }, + "admin_config": { + "title": "قراءة إعداداتها من المسؤول", + "body": "قراءة الإعدادات التي حفظها المسؤول لهذه الإضافة." + }, + "ui_observe": { + "title": "تتبّع موقعك في التطبيق", + "body": "معرفة الصفحة المفتوحة والتفاعل مع الروابط التي تنقر عليها." + }, + "ui_toolbar": { + "title": "إضافة أزرار إلى أشرطة الأدوات", + "body": "عرض أزرارها الخاصة في شريط أدوات الرسائل، وعلى الرسالة المفتوحة، وفي الشريط العلوي." + }, + "ui_app_top_banner": { + "title": "عرض شريط في الأعلى", + "body": "عرض إشعار على امتداد أعلى التطبيق." + }, + "ui_email_banner": { + "title": "عرض إشعارات على الرسائل", + "body": "عرض إشعاراتها وملخصاتها الخاصة فوق أجزاء الرسالة أو المحادثة المفتوحة وداخلها وبينها." + }, + "ui_email_footer": { + "title": "إضافة محتوى أسفل الرسائل", + "body": "عرض محتواها الخاص أسفل الرسالة المفتوحة." + }, + "ui_contact_cryptokeys": { + "title": "عرض مفاتيح تشفير جهات الاتصال", + "body": "عرض مفاتيح التشفير لجهة اتصال وإدارتها." + }, + "ui_email_details": { + "title": "إضافة لوحات إلى تفاصيل الرسالة", + "body": "عرض لوحتها الخاصة بجانب الرسالة المفتوحة أو أسفلها." + }, + "ui_download_file": { + "title": "حفظ الملفات على جهازك", + "body": "إتاحة ملفات أنشأتها للتنزيل." + }, + "ui_message_list_tabs": { + "title": "إضافة علامات تبويب إلى البريد الوارد", + "body": "فرز البريد الوارد في علامات تبويب مثل التحديثات أو العروض الترويجية." + }, + "ui_composer_toolbar": { + "title": "الإضافة إلى محرر الرسائل", + "body": "عرض أزرارها وعناصر القوائم والأقسام الخاصة بها أثناء كتابة رسالة." + }, + "ui_composer_sidebar": { + "title": "إضافة لوحة إلى محرر الرسائل", + "body": "عرض لوحتها الخاصة بجانب الرسالة التي تكتبها." + }, + "ui_sidebar_widget": { + "title": "الإضافة إلى الشريط الجانبي", + "body": "عرض مربعاتها وأقسامها الخاصة في الشريط الجانبي للبريد." + }, + "ui_settings_section": { + "title": "إضافة صفحة إعدادات", + "body": "عرض صفحتها الخاصة في الإعدادات." + }, + "ui_context_menu": { + "title": "الإضافة إلى قائمة الرسالة", + "body": "إضافة عناصر إلى القائمة التي تظهر عند النقر بالزر الأيمن على رسالة." + }, + "ui_navigation_rail": { + "title": "الإضافة إلى شريط التنقل", + "body": "عرض زرها الخاص في شريط التنقل." + }, + "ui_keyboard": { + "title": "إضافة اختصارات لوحة المفاتيح", + "body": "الاستجابة لاختصارات لوحة المفاتيح التي تحددها." + }, + "ui_calendar_action": { + "title": "الإضافة إلى واجهة التقويم", + "body": "عرض أزرارها وأقسامها الخاصة على الأحداث، وتسميات قصيرة ونقاط على أيام التقويم." + }, + "ui_admin_page": { + "title": "إضافة صفحة إدارة", + "body": "عرض صفحتها الخاصة في إعدادات الإدارة." + }, + "app_lifecycle": { + "title": "معرفة وقت بدء التطبيق وتوقفه", + "body": "تلقّي إشعار عند بدء التطبيق أو انقطاع اتصاله أو تسجيل خروجك." + }, + "compose_open": { + "title": "فتح محرر الرسائل لك", + "body": "فتح رسالة جديدة بعد تعبئة المستلمين أو النص أو المرفقات. يبقى قرار إرسالها لك." + }, + "compose_read": { + "title": "قراءة الرسالة التي تكتبها", + "body": "قراءة نص الرسالة أثناء كتابتها، عندما تستخدم الإضافة في محرر الرسائل. لا يشمل التوقيع ولا الرسالة المقتبسة." + }, + "compose_write": { + "title": "تعديل الرسالة التي تكتبها", + "body": "استبدال نص أو إضافته في الرسالة أثناء كتابتها، عندما تستخدم الإضافة في محرر الرسائل. يمكنك التراجع عن كل تغيير، وأنت من يقرر الإرسال." + }, + "ui_notifications": { + "title": "عرض الإشعارات", + "body": "عرض إشعارات على جهازك، أو داخل التطبيق إذا لم تسمح بها. يذكر كل إشعار اسم الإضافة." + }, + "ui_message_list": { + "title": "الإضافة إلى قائمة الرسائل", + "body": "عرض محتواها الخاص فوق قائمة الرسائل، وأزرار على الرسائل وفي شريط أدوات الرسائل المحددة." + }, + "ui_contacts": { + "title": "الإضافة إلى واجهة جهات الاتصال", + "body": "عرض أقسامها الخاصة في تفاصيل جهة الاتصال، وأزرار في قائمة جهات الاتصال." + }, + "ui_tasks": { + "title": "الإضافة إلى واجهة المهام", + "body": "عرض أقسامها الخاصة في تفاصيل المهمة، وأزرار في قائمة المهام." + }, + "ui_files": { + "title": "الإضافة إلى واجهة الملفات", + "body": "عرض أقسامها الخاصة في معاينة الملف، وعناصر في قائمة خيارات الملف." + }, + "ui_commands": { + "title": "إضافة أوامر", + "body": "إضافة عناصرها الخاصة إلى لوحة الأوامر (Ctrl+K)." + }, + "ui_app_page": { + "title": "إضافة صفحات", + "body": "إضافة صفحاتها الخاصة إلى التطبيق، لكل منها زر في شريط التنقل." + }, + "http_proxy": { + "title": "استخدام الحسابات التي تربطها", + "body": "إرسال طلبات عبر خادم Bulwark باستخدام حساب أو مفتاح أعددته أنت أو مسؤول النظام. لا ترى الإضافة كلمة المرور أبدًا، لكنها تستطيع فعل أي شيء يستطيعه ذلك الحساب على خادمه." + } + }, + "dialog": { + "from_plugin": "من الإضافة {name}", + "ok": "حسنًا", + "submit": "إرسال", + "confirm": "تأكيد", + "cancel": "إلغاء", + "close": "إغلاق", + "turn_off": "إيقاف {name}", + "plugin_menu": "خيارات {name}", + "password_note": "هذا النموذج من الإضافة {name} وليس من بريد الويب. لا تدخل كلمة مرور إلا إذا كنت تثق بهذه الإضافة.", + "toast_auto_disabled": "تم إيقاف {name} لأنها فشلت مرارًا." + }, + "connect": { + "title": "ربط {label}", + "intro_password": "يريد {name} استخدام حسابك على {label}. يحتفظ Bulwark بكلمة المرور على خادمه ولا يرسلها إلا إلى {host}. لا ترى الإضافة كلمة المرور أبدًا.", + "intro_password_any": "يريد {name} استخدام حسابك على {label}. يحتفظ Bulwark بكلمة المرور على خادمه ولا يرسلها إلا إلى الخادم الذي تدخله هنا. لا ترى الإضافة كلمة المرور أبدًا.", + "intro_signin": "يريد {name} تسجيل الدخول إلى حسابك على {label}. أدخل الخادم هنا، ثم سجّل الدخول في صفحة ذلك الخادم. يحتفظ Bulwark بالوصول الذي يحصل عليه على خادمه، ولا تراه الإضافة أبدًا.", + "server": "عنوان الخادم", + "server_hint": "cloud.example.com", + "username": "اسم المستخدم", + "secret": "كلمة المرور أو المفتاح", + "connect": "ربط", + "continue": "متابعة", + "cancel": "إلغاء", + "saving": "جارٍ الحفظ…", + "error_server": "أدخل عنوان الخادم، مثل https://cloud.example.com", + "error_save": "تعذّر الحفظ: {error}" + } + }, + "global_search": { + "title": "البحث في كل شيء", + "placeholder": "ابحث في البريد وجهات الاتصال والتقويم والملفات...", + "scope_label": "البحث في", + "account_label": "الحساب", + "scope_all": "الكل", + "scope_mail": "البريد", + "scope_contacts": "جهات الاتصال", + "scope_calendar": "التقويم", + "scope_files": "الملفات", + "all_accounts": "جميع الحسابات", + "show_all": "عرض كل النتائج", + "no_results": "لا توجد نتائج لـ “{query}”", + "server_hint": "الكلمات الكاملة تبحث في الخادم؛ أجزاء الكلمات تطابق العناصر المحمّلة بالفعل", + "searching": "جارٍ البحث...", + "account_error": "تعذر البحث في {account}", + "recent_searches": "عمليات البحث الأخيرة", + "recurring": "متكرر", + "no_subject": "(no subject)", + "empty_hint": "ابحث في البريد وجهات الاتصال والأحداث والملفات في جميع الحسابات. جرّب from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "مسطح حسب التاريخ", + "grouped": "مجمّع", + "load_more": "تحميل المزيد", + "preview_empty": "اختر نتيجة لمعاينتها", + "open_full": "فتح", + "view_label": "العرض" + }, + "top_bar": { + "more": "خيارات أخرى" + }, + "plugin_pages": { + "loading": "جارٍ تحميل الصفحة", + "not_available_title": "هذه الصفحة غير متاحة", + "not_available_description": "الإضافة التي أضافتها متوقفة أو لم تعد مثبتة.", + "back_to_mail": "العودة إلى البريد", + "settings_group": "الإضافات" + }, + "command_palette": { + "title": "لوحة الأوامر", + "open": "فتح لوحة الأوامر", + "placeholder": "اكتب أمرًا", + "no_results": "لا توجد أوامر مطابقة", + "go_to": "الانتقال إلى {name}", + "compose": "رسالة جديدة", + "search": "البحث في البريد", + "theme_dark": "التبديل إلى المظهر الداكن", + "theme_light": "التبديل إلى المظهر الفاتح", + "shortcuts": "عرض اختصارات لوحة المفاتيح", + "group_navigation": "الانتقال إلى", + "group_actions": "إجراءات", + "group_plugins": "الإضافات" + }, + "notification_center": { + "title": "الإشعارات", + "mark_all_read": "تعليم الكل كمقروء", + "clear": "مسح", + "empty": "لا توجد إشعارات", + "close": "إغلاق", + "unread": "غير مقروء", + "unread_count": "غير المقروءة: {count}", + "level_error": "خطأ", + "level_warning": "تحذير" + } +} diff --git a/locales/ca/common.json b/locales/ca/common.json index 53dd19d7..c20ba9e6 100644 --- a/locales/ca/common.json +++ b/locales/ca/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Client de correu web minimalista que utilitza el protocol JMAP", - "login": { - "title": "Webmail", - "username_label": "Correu electrònic", - "username_placeholder": "usuari@exemple.com", - "password_label": "Contrasenya", - "password_placeholder": "Introduïu la contrasenya", - "jmap_endpoint_label": "Servidor JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "El servidor ha de permetre sol·licituds CORS des d'aquest domini.", - "jmap_server_label": "Servidor", - "jmap_server_auto_picked": "Servidor seleccionat a partir del domini del vostre correu electrònic.", - "sign_in": "Inicia la sessió", - "signing_in": "Iniciant la sessió...", - "loading": "Carregant...", - "reconnecting": "S'ha perdut la connexió. S'està intentant reconnectar…", - "error": { - "invalid_credentials": "El correu electrònic o la contrasenya no són vàlids. Comproveu les credencials i torneu-ho a provar.", - "connection_failed": "No s'ha pogut contactar amb el servidor. Comproveu la connexió a Internet i torneu-ho a provar.", - "cors_blocked": "El servidor és accessible, però està bloquejant les sol·licituds d'origen creuat. Comproveu la configuració CORS del servidor JMAP i permeteu aquest domini.", - "server_error": "El servidor no està disponible temporalment. Torneu-ho a provar més tard.", - "generic": "S'ha produït un error inesperat. Si el problema persisteix, contacteu amb l'administrador.", - "totp_required": "Cal un codi d'autenticació de dos factors. Introduïu-lo a continuació.", - "totp_invalid": "El codi d'autenticació no és vàlid. Comproveu l'aplicació d'autenticació i torneu-ho a provar.", - "oauth_discovery_failed": "El SSO està activat, però no s'ha pogut contactar amb el proveïdor d'identitat. Comproveu la configuració d'OAuth." - }, - "show_password": "Mostra la contrasenya", - "hide_password": "Amaga la contrasenya", - "totp_toggle": "Tinc un codi 2FA", - "remember_me": "Recorda'm", - "config_error": { - "title": "Error de configuració", - "fetch_failed": "No s'ha pogut carregar la configuració de l'aplicació. Torneu-ho a provar més tard.", - "server_not_configured": "El servidor de correu no s'ha configurat. Contacteu amb l'administrador." - }, - "remove_from_history": "Elimina de l'historial", - "totp_label": "Codi d'autenticació", - "totp_placeholder": "000000", - "session_expired": "La sessió ha caducat. Torneu a iniciar la sessió.", - "dismiss": "Descarta", - "or": "o", - "sign_in_sso": "Inicia la sessió amb SSO", - "add_account_title": "Afegeix un compte", - "add_account_subtitle": "Inicieu la sessió amb un altre compte", - "cancel": "Cancel·la", - "website": "Lloc web", - "imprint": "Avís legal", - "privacy_policy": "Política de privadesa", - "try_demo": "Prova la demo", - "demo_description": "Exploreu amb dades d'exemple - no cal cap compte", - "demo_launching": "Iniciant la demo...", - "demo_login_button": "Inicia la demo", - "demo_tagline": "Proveu un client de correu complet. No cal cap compte.", - "demo_no_signup": "No cal registrar-se - exploreu lliurement amb dades d'exemple", - "oauth_completing": "Completant l'inici de sessió...", - "oauth_error": { - "title": "Ha fallat l'autenticació", - "invalid_state": "Ha fallat la validació de seguretat. Torneu a iniciar la sessió.", - "missing_params": "Falten dades d'autorització. Torneu a iniciar la sessió.", - "token_exchange_failed": "No s'ha pogut completar l'autenticació. Torneu-ho a provar.", - "access_denied": "S'ha denegat l'accés. Contacteu amb l'administrador.", - "back_to_login": "Torna a l'inici de sessió" - } - }, - "sidebar": { - "close": "Tanca", - "compose": "Redacta", - "compose_hint": "Redacta (c)", - "search_placeholder": "Cerca al correu...", - "search_placeholder_hint": "Cerca al correu... (premeu /)", - "storage": "Emmagatzematge", - "storage_used": "Utilitzat", - "storage_free": "Lliure", - "storage_total": "Total", - "sign_out": "Tanca la sessió", - "sign_out_of": "Tanca la sessió de {account}", - "sign_out_all": "Tanca la sessió de tots els comptes", - "add_account": "Afegeix un compte", - "set_as_default": "Estableix com a predeterminat", - "switch_account": "Canvia de compte", - "contacts": "Contactes", - "calendar": "Calendari", - "settings": "Configuració", - "admin": "Administració", - "files": "Fitxers", - "loading_mailboxes": "Carregant les bústies...", - "push_connected": "Actualitzacions en temps real actives", - "push_disconnected": "Actualitzacions en temps real inactives", - "keyboard_shortcuts": "Dreceres de teclat", - "theme": { - "light": "Mode clar", - "dark": "Mode fosc", - "system": "Tema del sistema" - }, - "language": { - "title": "Idioma" - }, - "mailboxes": { - "inbox": "Safata d'entrada", - "sent": "Enviats", - "drafts": "Esborranys", - "trash": "Paperera", - "archive": "Arxiu", - "starred": "Destacats", - "all_mail": "Tot el correu", - "spam": "Correu brossa", - "important": "Important" - }, - "unified_inbox": "Safata d'entrada unificada", - "unified_sent": "Tots els enviats", - "unified_drafts": "Tots els esborranys", - "unified_trash": "Tota la paperera", - "unified_archive": "Tot l'arxiu", - "unified_junk": "Tot el correu brossa", - "all_accounts": "Tots els comptes", - "unified_mailbox": "Bústia unificada", - "expand": "Desplega", - "collapse": "Replega", - "expand_tooltip": "Desplega", - "collapse_tooltip": "Replega", - "mobile": { - "search": "Cerca", - "compose": "Redacta", - "go_back": "Torna enrere", - "toggle_menu": "Commuta el menú" - }, - "clear_search": "Neteja la cerca", - "vacation_active": "La resposta automàtica està activa", - "demo_banner": "Mode de demostració", - "demo_reset": "Reinicia", - "demo_tour": "Visita guiada", - "tags": "Etiquetes", - "show_all_tags": "Mostra-ho tot ({count})", - "show_fewer_tags": "Mostra'n menys", - "folders": "Carpetes", - "shared": "Compartit", - "mail": "Correu", - "nav_label": "Navegació", - "add_app": "Aplicacions", - "scheduled": "Programats", - "unified_all_unread": "Tots els no llegits", - "unified_all_starred": "Tots els destacats", - "unified_all_mail": "Tot el correu", - "remove_account": "Elimina el compte", - "remove_account_confirm": "Voleu eliminar {account} d'aquest dispositiu? El podreu tornar a afegir més endavant." - }, - "protocol_handlers": { - "title": "Aplicacions predeterminades", - "description": "Trieu si els enllaços de correu i calendari s'obren al Bulwark. Tècnicament, el Bulwark es registra com a gestor de protocol per als enllaços mailto: i webcal:.", - "unsupported": "Aquest navegador o connexió no admet el registre manual de gestors de protocol. És possible que encara pugueu utilitzar la PWA instal·lada des de la configuració del navegador o del sistema operatiu.", - "mailto_label": "Enllaços de correu", - "mailto_description": "Obre els enllaços mailto: al Bulwark amb el redactor emplenat prèviament.", - "protocol_open_mode_label": "En obrir enllaços de protocol", - "protocol_open_mode_description": "Trieu si el Bulwark obre els enllaços mailto: i webcal: en una pestanya nova o reutilitza una sessió oberta. L'opció de sessió activa necessita permís de notificacions perquè pugueu prémer una notificació alternativa per portar el Bulwark al davant si el navegador bloqueja el focus.", - "protocol_open_mode_active_session": "Obre en la sessió activa si és possible", - "protocol_open_mode_new_tab": "Obre sempre una pestanya nova", - "focus_notification_title": "Obre el Bulwark", - "focus_notification_body": "L'enllaç s'ha obert al Bulwark. Feu clic per portar la finestra al davant.", - "webcal_label": "Enllaços de calendari", - "webcal_description": "Obre els enllaços webcal: al Bulwark amb el diàleg de subscripció al calendari emplenat prèviament.", - "register_mailto": "Registra com a aplicació de correu", - "register_webcal": "Registra com a aplicació de calendari", - "mailto_registered": "S'ha sol·licitat el registre del gestor de correu", - "webcal_registered": "S'ha sol·licitat el registre del gestor de calendari", - "registration_failed": "No s'ha pogut registrar el gestor de protocol", - "opening_mailto": "Obrint el redactor...", - "opening_webcal": "Obrint el calendari...", - "browser_note": "És possible que el navegador o el sistema operatiu us demani confirmar-ho, i que calgui tenir el Bulwark instal·lat per poder seleccionar-lo com a aplicació predeterminada.", - "select_account_title": "Trieu un compte", - "select_mailto_account": "Trieu quin compte ha d'obrir aquest enllaç de correu.", - "select_webcal_account": "Trieu quin compte ha d'obrir aquest enllaç de calendari.", - "select_account_note": "Això només s'aplica a aquest enllaç de protocol.", - "detail_to": "Per a", - "detail_subject": "Assumpte", - "detail_no_subject": "Sense assumpte", - "detail_calendar": "Calendari", - "detail_source": "Origen", - "active_account": "Actiu", - "switching_account": "Canviant de compte..." - }, - "sidebar_apps": { - "modal_title": "Aplicacions de la barra lateral", - "add_new": "Afegeix una aplicació", - "edit_app": "Edita l'aplicació", - "name_label": "Nom", - "name_placeholder": "La meva aplicació", - "name_required": "El nom és obligatori", - "url_label": "URL", - "url_required": "L'URL és obligatori", - "url_invalid": "Introduïu un URL http o https vàlid", - "icon_label": "Icona", - "icon_required": "La icona és obligatòria", - "open_mode_label": "Mode d'obertura", - "open_new_tab": "Pestanya nova", - "open_inline": "Incrustat", - "cancel": "Cancel·la", - "add": "Afegeix", - "update": "Actualitza", - "delete": "Suprimeix", - "delete_confirm_title": "Suprimeix l'aplicació", - "delete_confirm": "Segur que voleu suprimir «{name}»?", - "no_apps": "Encara no s'ha afegit cap aplicació", - "no_apps_hint": "Afegiu aplicacions i enllaços personalitzats a la barra lateral", - "managed_title": "Proporcionades per l'administració", - "managed_description": "Aquestes aplicacions estan configurades per a tothom i no es poden canviar.", - "managed_badge": "Gestionada", - "search_icons": "Cerca icones...", - "show_popular": "Populars", - "show_all": "Totes", - "no_icons_found": "No s'ha trobat cap icona", - "inline_badge": "Incrustat", - "tab_badge": "Pestanya", - "show_on_mobile": "Mostra al mòbil" - }, - "email_list": { - "no_emails": "No s'ha trobat cap missatge", - "no_emails_description": "Aquesta bústia és buida", - "no_search_results": "No s'ha trobat cap resultat", - "no_search_results_description": "Proveu d'ajustar la cerca o els filtres", - "loading": "Carregant els correus...", - "unread": "no llegit", - "to_me": "Per a mi", - "to_recipients": "Per a {count} destinataris", - "and_others": "i {count} més", - "draft": "Esborrany", - "starred": "Destacat", - "pinned": "Fixat", - "replied": "Respost", - "forwarded": "Reenviat", - "has_attachment": "Té adjunt", - "conversations_count": "{count} de {total} converses", - "conversations_count_plus": "{count}+ converses", - "conversations_count_simple": "{count} converses", - "no_conversations": "Cap conversa", - "loading_more": "Carregant més correus...", - "no_more_emails": "No hi ha més correus per carregar", - "batch_actions": { - "select": "Selecciona correus", - "select_all": "Selecciona-ho tot", - "selected_messages": "{count, plural, one {1 correu seleccionat} other {# correus seleccionats}}", - "mark_read": "Marca com a llegit", - "mark_unread": "Marca com a no llegit", - "delete": "Suprimeix", - "delete_confirm_title": "Suprimeix els correus", - "delete_confirm_message": "Segur que voleu suprimir {count, plural, one {1 correu} other {# correus}}?", - "clear_selection": "Neteja la selecció" - }, - "permanent_delete": "Suprimeix definitivament", - "permanent_delete_confirm_title": "Suprimeix definitivament", - "permanent_delete_confirm_message": "Aquest correu se suprimirà definitivament. Aquesta acció no es pot desfer.", - "permanent_delete_confirm_batch_message": "Aquests {count, plural, one {1 correu} other {# correus}} se suprimiran definitivament. Aquesta acció no es pot desfer.", - "empty_folder": { - "button": "Buida la carpeta", - "confirm_title": "Buida la carpeta", - "confirm_message": "Tots els correus d'aquesta carpeta se suprimiran definitivament. Aquesta acció no es pot desfer.", - "confirm_button": "Buida la carpeta", - "junk_hint": "Podeu buidar la carpeta de correu brossa per eliminar definitivament tots els missatges.", - "trash_hint": "Podeu buidar la paperera per eliminar definitivament tots els missatges." - }, - "no_scheduled_emails": "Cap correu programat", - "no_scheduled_emails_description": "Els missatges programats per a més endavant apareixeran aquí.", - "scheduled_count": "{count, plural, one {1 correu programat} other {# correus programats}}", - "scheduled_actions_hint": "Els missatges programats es poden cancel·lar, reprogramar o editar des de les seves accions programades.", - "cancel_scheduled_send": "Cancel·la l'enviament", - "reschedule_send": "Reprograma", - "cancel_and_edit": "Cancel·la i edita", - "cancel_and_compose_again": "Cancel·la i torna a redactar", - "reschedule_prompt": "Introduïu una data i hora noves, p. ex. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "El remitent ha demanat rebre una notificació quan obriu aquest missatge.", - "send": "Envia la confirmació", - "ignore": "Ignora", - "sent": "S'ha enviat la confirmació de lectura.", - "send_failed": "No s'ha pogut enviar la confirmació de lectura", - "mdn_subject": "Llegit: {subject}", - "mdn_body": "Aquesta és una confirmació de recepció del missatge que heu enviat a {recipient}.\n\nNota: aquesta confirmació només indica que el missatge s'ha mostrat a l'ordinador del destinatari. No hi ha cap garantia que el destinatari hagi llegit o entès el contingut del missatge." - }, - "no_email_selected": "Cap correu seleccionat", - "no_email_description": "Seleccioneu un correu de la llista per visualitzar-lo aquí", - "no_conversation_selected": "Cap conversa seleccionada", - "no_conversation_description": "Trieu una conversa de la llista per llegir-la aquí", - "compose": "Redacta", - "compose_hint": "Redacta un missatge nou", - "no_subject": "(Sense assumpte)", - "no_body_content": "(No hi ha contingut disponible)", - "no_preview_available": "No hi ha cap previsualització disponible", - "show_quoted_text": "Mostra el text citat", - "hide_quoted_text": "Amaga el text citat", - "loading_email": "Carregant el correu...", - "loading": "Carregant...", - "reply": "Respon", - "reply_all": "Respon a tots", - "forward": "Reenvia", - "delete": "Suprimeix", - "archive": "Arxiva", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Marca amb estrella", - "unstar": "Treu l'estrella", - "mark_unread": "Marca com a no llegit", - "mark_read": "Marca com a llegit", - "unread": "No llegit", - "read": "Llegit", - "spam_short": "Brossa", - "not_spam_short": "No és brossa", - "move": "Mou", - "print": "Imprimeix", - "view_source": "Mostra el codi font", - "fullscreen": "Pantalla completa", - "exit_fullscreen": "Surt de la pantalla completa", - "export_email": "Exporta com a .eml", - "forward_as_attachment": "Reenvia com a fitxer adjunt", - "import_email": "Importa .eml o .zip", - "keyboard_shortcuts": "Dreceres de teclat (?)", - "email_source": "Codi font del correu", - "draft_banner": "Aquest missatge és un esborrany", - "edit_draft": "Edita", - "copy_source": "Copia al porta-retalls", - "source_copied": "S'ha copiat el codi font al porta-retalls", - "attachments": "Fitxers adjunts", - "important": "Important", - "download": "Baixa", - "download_all": "Baixa-ho tot", - "from": "De", - "to": "Per a", - "cc": "CC", - "bcc": "CCO", - "date": "Data", - "subject": "Assumpte", - "show_details": "Mostra els detalls", - "hide_details": "Amaga els detalls", - "external_content_warning": "S'han bloquejat les imatges i el contingut extern", - "load_external_content": "Carrega les imatges", - "trust_sender": "Confia sempre en aquest remitent", - "back_to_list": "Torna a la llista", - "view_contact": "Mostra el contacte", - "message_details": "Detalls del missatge", - "more_reply_options": "Més opcions de resposta", - "set_tag": "Estableix l'etiqueta", - "tag": "Etiqueta", - "more_actions": "Més accions", - "previous": "Anterior", - "next": "Següent", - "move_to": "Mou a...", - "remove_tag": "Elimina l'etiqueta", - "tag_filter_placeholder": "Filtra les etiquetes", - "tag_no_matches": "Cap etiqueta coincident", - "more_count": "+{count} més", - "characters_count": "{count} caràcters", - "quick_reply_placeholder": "Escriviu una resposta ràpida...", - "more_options": "Més opcions", - "sending": "Enviant...", - "security_authentication": "Seguretat i autenticació", - "technical_details": "Detalls tècnics", - "message_id_label": "ID del missatge:", - "reply_to_label": "Respon a:", - "delivery_time_label": "Hora de lliurament:", - "conversation_part_label": "Part de la conversa:", - "previous_messages": "{count} missatge anterior", - "previous_messages_plural": "{count} missatges anteriors", - "time": { - "day": "dia", - "days": "dies", - "hour": "hora", - "hours": "hores", - "minute": "minut", - "minutes": "minuts" - }, - "unknown_sender": "Desconegut", - "recipient_me": "jo", - "recipient_and_others": "{name} i {count} més", - "recipient_to_prefix": "Per a:", - "authentication": { - "title": "Autenticació", - "status": { - "verified": "Verificat", - "warning": "Advertència", - "none": "No autenticat" - }, - "spf": { - "pass": "SPF correcte", - "fail": "SPF incorrecte", - "none": "Sense SPF" - }, - "dkim": { - "pass": "DKIM vàlid", - "fail": "DKIM no vàlid", - "none": "Sense DKIM" - }, - "dmarc": { - "pass": "DMARC correcte", - "fail": "DMARC incorrecte", - "none": "Sense DMARC" - }, - "spam_score": "Puntuació de brossa", - "tooltip_spf": "Sender Policy Framework: verifica que el servidor remitent estigui autoritzat a enviar correu en nom del domini", - "tooltip_dkim": "DomainKeys Identified Mail: confirma que el correu no s'ha alterat durant el trànsit mitjançant una signatura criptogràfica", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: assegura que l'SPF i el DKIM coincideixin amb el domini del remitent i estableix una política per als errors", - "policy": "Política", - "result": { - "pass": "Correcte", - "fail": "Incorrecte", - "softfail": "Error lleu", - "neutral": "Neutre", - "permerror": "Error permanent", - "temperror": "Error temporal", - "none": "Cap" - } - }, - "details": { - "recipients_routing": "Destinataris i encaminament", - "authentication_security": "Autenticació i seguretat", - "identifiers_threading": "Identificadors i fils", - "mailing_list": "Llista de correu", - "message_properties": "Propietats del missatge", - "sent": "Enviat", - "received": "Rebut", - "delivery_time": "Hora de lliurament", - "in_reply_to": "In-Reply-To", - "references": "References", - "thread_id": "ID del fil", - "size": "Mida", - "mime_type": "Tipus MIME", - "attachments_summary": "{count} fitxers · {size}", - "list_id": "ID de la llista", - "list_help": "Ajuda de la llista", - "list_post": "Publicació a la llista", - "list_unsubscribe": "Cancel·la la subscripció", - "iprev": "DNS invers", - "spam_status": "Estat de brossa", - "ai_verdict": "Veredicte de la IA", - "account": "Compte", - "no_subject": "(sense assumpte)" - }, - "headers": { - "routing": "Encaminament", - "received": "Received", - "message_id": "ID del missatge", - "list_info": "Informació de la llista" - }, - "tooltips": { - "reply": "Respon (r)", - "reply_all": "Respon a tots (a)", - "forward": "Reenvia (f)", - "archive": "Arxiva (e)", - "delete": "Suprimeix (# o Supr)", - "star": "Marca amb estrella (s)", - "unstar": "Treu l'estrella (s)", - "compose": "Redacta (c)", - "previous": "Correu anterior", - "next": "Correu següent", - "edit_draft": "Edita l'esborrany" - }, - "spam": { - "button_title": "Denuncia com a brossa", - "not_spam_title": "Marca com a legítim", - "toast_success": "S'ha mogut a la brossa", - "toast_batch": "{count} correus moguts a la brossa", - "toast_undo": "Desfés", - "toast_not_spam_success": "S'ha mogut a la safata d'entrada", - "toast_not_spam_batch": "{count} correus moguts a la safata d'entrada", - "error": "No s'ha pogut denunciar com a brossa", - "error_not_spam": "No s'ha pogut restaurar el correu" - }, - "unsubscribe_banner": { - "label": "Butlletí", - "button": "Cancel·la la subscripció", - "confirm_title": "Voleu cancel·lar la subscripció d'aquest remitent?", - "confirm_button": "Confirma", - "cancel": "Cancel·la", - "success_http": "S'ha obert la pàgina de cancel·lació en una pestanya nova", - "success_mailto": "S'ha enviat el correu de cancel·lació", - "confirm_message_http": "La pàgina de cancel·lació s'obrirà en una pestanya nova.", - "confirm_message_mailto": "S'enviarà un correu de cancel·lació al remitent.", - "error": "No s'ha pogut cancel·lar la subscripció", - "dismiss": "Descarta" - }, - "calendar_invitation": { - "loading": "Carregant els detalls de l'esdeveniment…", - "title": "Invitació de calendari", - "published_title": "Esdeveniment publicat", - "response_title": "Resposta a l'esdeveniment", - "update_title": "Actualització de l'esdeveniment", - "counter_title": "Contraproposta", - "refresh_title": "Sol·licitud d'actualització", - "declined_counter_title": "Contraproposta rebutjada", - "cancelled_title": "Esdeveniment cancel·lat", - "organizer": "Organitzat per {name}", - "organizer_label": "Organitzat per", - "attendees": "{count, plural, one {# assistent} other {# assistents}}", - "accept": "Accepta", - "maybe": "Potser", - "decline": "Rebutja", - "add_to_calendar": "Afegeix al calendari", - "added": "S'ha afegit al calendari", - "rsvp_sent": "Resposta enviada", - "parse_error": "No s'ha pogut llegir la invitació", - "action_failed": "No s'ha pogut completar aquesta acció del calendari.", - "no_calendar": "Calendari no disponible", - "published_info": "Aquest esdeveniment s'ha compartit com a referència.", - "response_info": "Aquest missatge conté la resposta d'un assistent.", - "response_info_organizer": "Aquesta resposta de l'assistent actualitza el vostre esdeveniment.", - "update_info": "Aquest missatge actualitza un esdeveniment existent.", - "counter_info": "Aquest missatge proposa canvis a un esdeveniment.", - "counter_info_organizer": "Aquest assistent ha proposat canvis al vostre esdeveniment.", - "refresh_info": "Aquest missatge sol·licita els detalls més recents de l'esdeveniment.", - "refresh_info_organizer": "Un assistent ha sol·licitat els detalls més recents de l'esdeveniment.", - "declined_counter_info": "L'organitzador ha rebutjat una contraproposta.", - "authentication_failed_info": "Les comprovacions d'autenticació del correu d'aquesta invitació han fallat. Tracteu les accions del calendari amb precaució.", - "authentication_missing_info": "Aquesta invitació no inclou autenticació de correu verificada. Confirmeu els detalls amb l'organitzador si alguna cosa sembla estranya.", - "sender_mismatch_info": "Aquesta invitació s'ha enviat des de {sender}, mentre que l'organitzador indicat a les dades del calendari és {organizer}.", - "sender_mismatch_unverified_info": "Aquesta invitació s'ha enviat des de {sender}, mentre que l'organitzador indicat a les dades del calendari és {organizer}, i no s'ha pogut verificar el missatge.", - "organizer_role": "Vós organitzeu aquest esdeveniment", - "your_response": "La vostra resposta: {status}", - "response_needed": "Cal resposta", - "response_accepted": "Acceptat", - "response_tentative": "Provisional", - "response_declined": "Rebutjat", - "response_delegated": "Delegat", - "actor_sent_info": "Enviat per {name}.", - "actor_response_info": "{name} ha respost {status}.", - "actor_counter_info": "{name} ha proposat canvis a aquest esdeveniment.", - "actor_refresh_info": "{name} ha demanat els detalls més recents de l'esdeveniment.", - "actor_declined_counter_info": "{name} ha rebutjat la contraproposta.", - "actor_note": "Nota: {comment}", - "actor_unknown": "Algú", - "proposed_changes": "Canvis proposats", - "change_title": "Títol", - "change_time": "Hora", - "change_location": "Ubicació", - "change_description": "Descripció", - "change_empty": "Cap", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Aplica els canvis proposats", - "proposal_applied": "S'han aplicat els canvis proposats.", - "review_proposal": "Revisa la proposta", - "review_request": "Revisa la sol·licitud", - "view_in_calendar": "Mostra al calendari", - "select_calendar": "Selecciona un calendari", - "already_in_calendar": "Ja és al vostre calendari", - "request_info": "Heu estat convidat a aquest esdeveniment. Responeu per informar l'organitzador de la vostra disponibilitat.", - "cancel_info": "L'organitzador ha cancel·lat aquest esdeveniment.", - "event_updated": "Actualització núm. {sequence}", - "event_status_tentative": "Provisional", - "event_status_cancelled": "Cancel·lat", - "expand": "Mostra els detalls", - "collapse": "Amaga els detalls" - }, - "send": "Envia", - "more": "més", - "scheduled_banner": "Programat per enviar-se el {date}", - "scheduled_send_created": "Correu programat per enviar-se", - "cancel_scheduled_send": "Cancel·la l'enviament", - "reschedule_send": "Reprograma", - "cancel_and_edit": "Cancel·la i edita", - "cancel_and_compose_again": "Cancel·la i torna a redactar", - "reschedule_prompt": "Introduïu una data i hora noves, p. ex. 2026-05-04T15:30", - "scheduled_actions_only": "La vista de programats només admet accions de programació", - "undo_send_scheduled": "Missatge programat per enviar-se", - "undo_send": "Desfés l'enviament", - "contact_sidebar": { - "title": "Contacte", - "close": "Tanca la barra lateral", - "action_email": "Correu electrònic", - "action_email_title": "Envia un correu", - "action_copy": "Copia", - "action_copy_title": "Copia el correu electrònic", - "action_edit_title": "Edita el contacte", - "section_emails": "Correus electrònics", - "section_phones": "Telèfons", - "section_organizations": "Organitzacions", - "section_addresses": "Adreces", - "section_notes": "Notes", - "not_in_contacts": "No és als vostres contactes", - "add_to_contacts": "Afegeix als contactes", - "copied": "Copiat!", - "copy_failed": "No s'ha pogut copiar" - }, - "send_now": "Envia ara" - }, - "email_composer": { - "read_receipt_on": "Confirmació de lectura sol·licitada (feu clic per desactivar-la)", - "read_receipt_off": "Sol·licita una confirmació de lectura", - "dsn_on": "Notificació de lliurament sol·licitada (fes clic per desactivar)", - "dsn_off": "Sol·licita una notificació de lliurament", - "format_plain_text": "Canvia a text sense format", - "format_rich_text": "Canvia a text enriquit (HTML)", - "require_tls_on": "Lliurament xifrat obligatori (fes clic per desactivar)", - "require_tls_off": "Exigeix lliurament xifrat (TLS)", - "new_message": "Missatge nou", - "signature_edit_hint": "Feu doble clic per editar la signatura", - "reply": "Respon", - "reply_all": "Respon a tots", - "forward": "Reenvia", - "reply_to": "Respon", - "reply_all_to": "Respon a tots", - "forward_message": "Reenvia", - "from": "De", - "to": "Per a", - "cc": "CC", - "bcc": "CCO", - "subject": "Assumpte", - "body_placeholder": "Escriviu el vostre missatge...", - "send": "Envia", - "cancel": "Cancel·la", - "attach": "Adjunta", - "attach_photos": "Fotos i vídeos", - "attach_files": "Fitxers", - "discard": "Descarta", - "discard_draft_title": "Voleu descartar l'esborrany?", - "discard_draft_confirm": "Teniu canvis sense desar. Voleu descartar aquest esborrany?", - "saving": "Desant...", - "sending": "Enviant...", - "add_link": "Afegeix un enllaç", - "link_url_prompt": "Introduïu l'URL", - "draft_saved": "S'ha desat l'esborrany", - "save_failed": "No s'ha pogut desar", - "to_placeholder": "Adreces electròniques dels destinataris", - "cc_placeholder": "Destinataris amb CC", - "bcc_placeholder": "Destinataris amb CCO", - "subject_placeholder": "Assumpte", - "cc_label": "CC:", - "bcc_label": "CCO:", - "subject_label": "Assumpte:", - "file_size_kb": "KB", - "prefix": { - "forward": "Reenv.:", - "reply": "Re:" - }, - "no_subject": "(Sense assumpte)", - "unknown_sender": "Desconegut", - "quote": { - "reply_header": "El {date}, {sender} va escriure:", - "forward_header": "---------- Missatge reenviat ----------", - "from": "De: {sender}", - "date": "Data: {date}", - "subject": "Assumpte: {subject}", - "to": "Per a: {recipients}" - }, - "remove_sub_address": "Elimina el subadreçament", - "from_override": { - "toggle_off": "Sobreescriu", - "toggle_on": "Cancel·la la sobreescriptura", - "toggle_tooltip": "Editeu lliurement el nom i l'adreça del remitent. El correu es continua enviant a través de la vostra identitat.", - "name_label": "Nom del remitent", - "name_placeholder": "Nom", - "email_label": "Adreça electrònica del remitent", - "email_placeholder": "alias@exemple.com", - "envelope_notice": "Si el vostre servidor no accepta aquesta adreça com a remitent del sobre, s'hi fa servir {identity}, i els destinataris la poden veure a la capçalera Return-Path." - }, - "use_template": "Plantilla", - "save_as_template": "Desa com a plantilla", - "validation": { - "recipient_required": "Afegiu un destinatari per enviar", - "body_required": "Escriviu un missatge o adjunteu un fitxer", - "attachments_uploading": "Els fitxers adjunts encara s'estan pujant — s'enviarà en acabar", - "attachment_upload_failed": "No s'ha enviat — no s'ha pogut pujar un fitxer adjunt. Elimineu-lo i torneu-ho a provar." - }, - "upload_progress": "Pujant {uploaded} / {total}", - "upload_cancel": "Cancel·la la pujada", - "upload_failed": "No s'ha pogut pujar {filename}", - "attachment_too_large": "«{name}» supera la mida que permet el servidor ({max} per fitxer)", - "attachments_total_too_large": "Els adjunts superarien el límit del servidor de {max} per missatge", - "drop_files": "Deixeu anar els fitxers per adjuntar-los", - "show_less": "Mostra'n menys", - "send_failed": "No s'ha pogut enviar el correu", - "send_timeout": "El servidor ha deixat de respondre. Comproveu la carpeta Enviats abans de tornar a enviar: el missatge pot haver sortit.", - "continue_draft": "Continua l'esborrany", - "close_draft_title": "Voleu desar o descartar l'esborrany?", - "close_draft_message": "Teniu canvis sense desar. Voleu desar-los com a esborrany o descartar-los?", - "smime_sign_on": "Signatura S/MIME activada", - "smime_sign_off": "Activa la signatura S/MIME", - "smime_encrypt_on": "Xifratge S/MIME activat", - "smime_encrypt_off": "Activa el xifratge S/MIME", - "smime_encrypt_unavailable": "Xifratge S/MIME no disponible – falten certificats del destinatari", - "smime_unlock_title": "Desbloqueja la clau S/MIME", - "smime_unlock_message": "Introduïu la contrasenya per desbloquejar la vostra clau de signatura S/MIME.", - "smime_unlock_button": "Desbloqueja", - "smime_passphrase_placeholder": "Contrasenya", - "forgot_attachment": { - "title": "Us heu oblidat d'un fitxer adjunt?", - "message": "El vostre missatge esmenta «{keyword}», però no hi ha cap fitxer adjunt. Voleu enviar-lo igualment?", - "send_anyway": "Envia igualment", - "back": "Torna a l'edició" - }, - "empty_subject": { - "title": "Voleu enviar-lo sense assumpte?", - "message": "Aquest missatge no té assumpte. Voleu enviar-lo igualment?", - "dont_ask_again": "No ho tornis a preguntar", - "send_anyway": "Envia igualment", - "back": "Torna a l'edició" - }, - "schedule_send": "Programa l'enviament", - "schedule_send_description": "Trieu quan ha d'alliberar el servidor aquest missatge.", - "schedule_send_required": "Trieu una data i hora.", - "schedule_send_invalid": "Introduïu una data i hora vàlides.", - "schedule_send_future": "Trieu una data i hora futures.", - "schedule_send_too_late": "Aquesta hora és més tardana del que permet el servidor.", - "schedule_send_unsupported": "Aquest compte no admet l'enviament programat.", - "schedule_send_cleanup_warning": "S'ha creat l'enviament programat, però no s'ha pogut netejar l'esborrany.", - "send_delay_unsupported": "Aquest compte no admet el retard d'enviament.", - "send_delay_unsupported_confirm": "Aquest compte no admet el retard d'enviament. Voleu enviar-lo immediatament?", - "recipient_edit_email": "Edita l'adreça electrònica", - "recipient_edit_name": "Edita el nom mostrat", - "recipient_email_placeholder": "Adreça electrònica", - "recipient_name_placeholder": "Nom mostrat", - "autocomplete_search_server": "Cerca al servidor", - "autocomplete_searching": "Cercant...", - "toolbar": { - "bold": "Negreta", - "italic": "Cursiva", - "underline": "Subratllat", - "strikethrough": "Ratllat", - "text_color": "Color del text", - "remove_color": "Elimina el color", - "heading_1": "Encapçalament 1", - "heading_2": "Encapçalament 2", - "bullet_list": "Llista de pics", - "ordered_list": "Llista numerada", - "quote": "Cita", - "code_block": "Bloc de codi", - "align_left": "Alinea a l'esquerra", - "align_center": "Centra", - "align_right": "Alinea a la dreta", - "text_direction": "Direcció del text (RTL/LTR)", - "link": "Enllaç", - "table": "Taula", - "clear_formatting": "Neteja el format", - "undo": "Desfés", - "redo": "Refés", - "add_row_above": "Afegeix una fila a sobre", - "add_row_below": "Afegeix una fila a sota", - "add_column_before": "Afegeix una columna abans", - "add_column_after": "Afegeix una columna després", - "delete_row": "Elimina la fila", - "delete_column": "Elimina la columna", - "toggle_header_row": "Commuta la fila de capçalera", - "delete_table": "Elimina la taula", - "pick_size": "Tria la mida", - "font_size": "Mida de la lletra", - "font_size_default": "Per defecte", - "background_color": "Color de fons", - "remove_background_color": "Elimina el color de fons" - }, - "send_filing_warning": "Enviat, però la neteja posterior a l'enviament ha fallat; és possible que quedi un esborrany obsolet." - }, - "confirm_dialog": { - "confirm": "Confirma", - "cancel": "Cancel·la" - }, - "common": { - "loading": "Carregant...", - "error": "Error", - "success": "Correcte", - "cancel": "Cancel·la", - "save": "Desa", - "delete": "Suprimeix", - "edit": "Edita", - "close": "Tanca", - "search": "Cerca", - "refresh": "Actualitza", - "settings": "Configuració", - "help": "Ajuda", - "logout": "Tanca la sessió", - "yes": "Sí", - "no": "No", - "unknown": "Desconegut", - "app_title": "Webmail", - "reconnecting": "S'ha perdut la connexió. S'està intentant reconnectar…", - "rate_limited_title": "L'autenticació al servidor està temporalment limitada.", - "rate_limited_detail": "El Bulwark ha aturat les sol·licituds en segon pla per evitar el bloqueig. Es tornarà a provar d'aquí a {seconds} s.", - "rate_limited_action_title": "Sol·licitud aturada per evitar el bloqueig.", - "rate_limited_action_detail": "El Bulwark espera que acabi el temps d'espera del servidor abans d'enviar més sol·licituds autenticades. Torneu-ho a provar d'aquí a {seconds} s.", - "pull_to_refresh": "Estira per actualitzar", - "release_to_refresh": "Deixa anar per actualitzar", - "refreshing": "S'està actualitzant…" - }, - "notifications": { - "email_sent": "Correu enviat correctament", - "email_deleted": "Correu suprimit", - "email_archived": "Correu arxivat", - "emails_deleted": "{count, plural, one {# correu suprimit} other {# correus suprimits}}", - "emails_archived": "{count, plural, one {# correu arxivat} other {# correus arxivats}}", - "error_archiving": "No s'ha pogut arxivar el correu", - "error_updating": "No s'ha pogut actualitzar el correu", - "email_starred": "Correu destacat", - "email_unstarred": "S'ha tret l'estrella del correu", - "email_marked_read": "Correu marcat com a llegit", - "email_marked_unread": "Correu marcat com a no llegit", - "copied_to_clipboard": "Copiat al porta-retalls", - "source_copied": "S'ha copiat el codi font al porta-retalls", - "error_sending": "No s'ha pogut enviar el correu", - "error_deleting": "No s'ha pogut suprimir el correu", - "error_loading": "No s'han pogut carregar els correus", - "new_email": "Correu nou", - "new_email_from": "De {sender}", - "click_to_view": "Feu clic per veure'l", - "email_moved": "Correu mogut", - "emails_moved": "{count} correus moguts", - "moved_to_mailbox": "Mogut a {mailbox}", - "move_failed": "No s'ha pogut moure", - "move_error": "No s'han pogut moure els correus a la carpeta seleccionada", - "email_tagged": "Correu etiquetat", - "emails_tagged": "{count} correus etiquetats", - "tag_failed": "No s'ha pogut etiquetar", - "identity_created": "Identitat creada correctament", - "identity_updated": "Identitat actualitzada correctament", - "identity_deleted": "Identitat suprimida", - "identity_set_primary": "Identitat principal actualitzada", - "identity_create_failed": "No s'ha pogut crear la identitat: {error}", - "identity_update_failed": "No s'ha pogut actualitzar la identitat: {error}", - "identity_delete_failed": "No s'ha pogut suprimir la identitat: {error}", - "identity_unauthorized": "No teniu autorització per enviar des d'aquesta adreça electrònica", - "identity_not_found": "No s'ha trobat la identitat", - "vacation_saved": "S'ha desat la configuració de la resposta automàtica", - "vacation_save_failed": "No s'ha pogut desar la configuració de la resposta automàtica", - "filters_saved": "Filtres desats correctament", - "filters_save_failed": "No s'han pogut desar els filtres", - "filters_deleted": "Regla de filtre suprimida", - "templates_exported": "Plantilles exportades correctament", - "templates_imported": "{count, plural, one {# plantilla importada} other {# plantilles importades}}", - "templates_import_errors": "Algunes plantilles no s'han pogut importar", - "templates_import_empty": "No s'ha trobat cap plantilla al fitxer", - "export_email_error": "No s'ha pogut exportar el correu", - "import_email_success": "Correu importat correctament", - "import_email_error": "No s'ha pogut importar el correu" - }, - "date": { - "today": "Avui", - "yesterday": "Ahir", - "this_week": "Aquesta setmana", - "last_week": "La setmana passada", - "this_month": "Aquest mes", - "older": "Més antics", - "just_now": "Ara mateix", - "minutes_ago": "Fa {count} minut", - "minutes_ago_plural": "Fa {count} minuts", - "hours_ago": "Fa {count} hora", - "hours_ago_plural": "Fa {count} hores", - "days_ago": "Fa {count} dia", - "days_ago_plural": "Fa {count} dies" - }, - "language": { - "title": "Idioma", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Selecciona l'idioma", - "switch_to_english": "Canvia a l'anglès", - "switch_to_french": "Canvia al francès", - "switch_to_japanese": "Canvia al japonès", - "switch_to_spanish": "Canvia a l'espanyol", - "switch_to_italian": "Canvia a l'italià", - "switch_to_german": "Canvia a l'alemany", - "switch_to_dutch": "Canvia al neerlandès", - "switch_to_portuguese": "Canvia al portuguès", - "switch_to_russian": "Canvia al rus", - "switching": "Canviant d'idioma...", - "switch": "Canvia d'idioma", - "current": "Idioma actual", - "polish": "Polski", - "switch_to_polish": "Canvia al polonès", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Canvia al persa", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Canvia al mongol" - }, - "settings": { - "title": "Configuració", - "back_to_mail": "Torna al correu", - "save_success": "Configuració desada correctament", - "import_success": "Configuració importada correctament", - "import_error": "No s'ha pogut importar la configuració", - "reset_confirm": "Segur que voleu restablir tota la configuració als valors per defecte?", - "unsaved_changes": "Teniu canvis sense desar", - "discard_changes": "Voleu descartar els canvis sense desar?", - "discard": "Descarta", - "keep_editing": "Continua editant", - "search_placeholder": "Cerca a la configuració", - "search_clear": "Neteja la cerca", - "search_no_results": "Cap configuració coincident", - "tabs": { - "appearance": "Aparença", - "language": "Idioma, regió i hora", - "email": "Comportament del correu", - "composer": "Redactor", - "privacy": "Privadesa i seguretat", - "account": "Compte", - "identities": "Identitats", - "vacation": "Resposta automàtica", - "advanced": "Avançat", - "calendar": "Calendari", - "filters": "Filtres", - "templates": "Plantilles", - "folders": "Carpetes", - "keywords": "Etiquetes", - "security": "Seguretat", - "files": "Fitxers", - "contacts": "Contactes", - "encryption": "Xifratge", - "protocol_handlers": "Aplicacions predeterminades", - "sidebar_apps": "Aplicacions de la barra lateral", - "notifications": "Notificacions", - "layout": "Disposició", - "reading": "Lectura", - "composing": "Redacció", - "downloads": "Baixades", - "content_senders": "Contingut i remitents", - "about_data": "Quant a i dades", - "debug": "Depuració" - }, - "tab_groups": { - "general": "General", - "account": "Compte i identitat", - "organization": "Organització del correu", - "apps": "Aplicacions", - "system": "Sistema", - "appearance": "Aparença", - "mail": "Correu", - "privacy": "Privadesa i seguretat", - "advanced": "Avançat" - }, - "appearance": { - "message_list_order": { - "label": "Ordre de la llista de missatges", - "description": "Com s'ordenen els missatges a la llista. L'ordenació la fa el servidor, de manera que abasta tota la carpeta i no només els missatges carregats.", - "preset": { - "chronological": "Cronològic (més recents primer)", - "unread_first": "No llegits primer", - "starred_first": "Destacats primer", - "tagged_first": "Etiquetats primer", - "custom": "Personalitzat" - }, - "tag_label": "Etiqueta", - "advanced_show": "Avançat: edita els nivells d'ordenació", - "advanced_hide": "Amaga les opcions avançades", - "criterion_label": "Criteri del nivell {n}", - "direction_label": "Direcció del nivell {n}", - "criteria": { - "unread": "No llegit", - "starred": "Destacat", - "tag": "Etiqueta", - "receivedAt": "Data de recepció", - "sentAt": "Data d'enviament", - "from": "Remitent", - "subject": "Assumpte", - "size": "Mida" - }, - "direction": { - "unread_first": "No llegits primer", - "read_first": "Llegits primer", - "starred_first": "Destacats primer", - "unstarred_first": "No destacats primer", - "tagged_first": "Etiquetats primer", - "untagged_first": "Sense etiqueta primer", - "newest_first": "Més recents primer", - "oldest_first": "Més antics primer", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Més grans primer", - "smallest_first": "Més petits primer" - }, - "add_level": "Afegeix un nivell", - "remove_level": "Elimina el nivell", - "move_up": "Mou amunt", - "move_down": "Mou avall", - "tiebreak_note": "Fins a tres nivells; els nivells posteriors desfan els empats. «Més recents primer» s'aplica sempre al final.", - "scope": { - "label": "Aplica a totes les carpetes", - "description": "Si està desactivat, l'ordre només s'aplica a la safata d'entrada; la resta de carpetes es mantenen cronològiques." - }, - "unsupported_note": "El teu servidor de correu no admet ordenar per estat de lectura, destacats o etiquetes. Aquests nivells s'ignoren i la llista es manté cronològica." - }, - "title": "Aparença", - "description": "Personalitzeu l'aspecte del vostre correu web", - "theme": { - "label": "Tema", - "description": "Trieu l'esquema de colors preferit", - "light": "Clar", - "dark": "Fosc", - "system": "Sistema" - }, - "language": { - "label": "Idioma", - "description": "Trieu l'idioma preferit" - }, - "font_size": { - "label": "Mida de la lletra", - "description": "Ajusteu la mida del text per millorar-ne la llegibilitat", - "small": "Petita", - "medium": "Mitjana", - "large": "Gran" - }, - "list_density": { - "label": "Densitat", - "description": "Controleu l'espaiat i el farciment de la interfície", - "extra_compact": "Extra compacta", - "compact": "Compacta", - "regular": "Normal", - "comfortable": "Còmoda" - }, - "animations": { - "label": "Activa les animacions", - "description": "Mostra transicions i efectes suaus" - }, - "toolbar_position": { - "label": "Posició de la barra d'eines", - "description": "On mostrar els botons d'acció del correu (Respon, Arxiva, Suprimeix, etc.)", - "top": "A dalt", - "below_subject": "Sota l'assumpte" - }, - "toolbar_labels": { - "label": "Mostra les etiquetes de la barra d'eines", - "description": "Mostra etiquetes de text al costat de les icones de la barra d'eines. Desactiveu-ho per estalviar espai un cop us hàgiu familiaritzat amb les icones." - }, - "hide_account_switcher": { - "label": "Amaga el selector de comptes de la barra lateral", - "description": "Amaga el selector de comptes a la part superior de la barra lateral de carpetes. Encara podreu canviar de compte des de la barra de navegació inferior." - }, - "show_rail_account_list": { - "label": "Mostra els avatars dels comptes a la barra de navegació", - "description": "Mostra cercles individuals per a cada compte a la part inferior de la barra de navegació per canviar-hi ràpidament, amb un botó de tancament de sessió a sota." - }, - "unified_mailbox": { - "label": "Bústia unificada", - "description": "Mostra carpetes combinades (Safata d'entrada, Enviats, etc.) del compte actiu i les seves carpetes compartides.", - "cross_account": { - "label": "En tots els comptes", - "description": "Combina la bústia unificada de tots els comptes connectats en lloc de limitar-la al compte actiu." - }, - "include_group": { - "label": "Inclou les safates d'entrada de grup", - "description": "Combina també les safates d'entrada compartides o de grup a la vista unificada." - } - }, - "all_mail": { - "folders_label": "Carpetes a les llistes unificades", - "folders_description": "Trieu quines carpetes d'aquest compte es combinen a les llistes Tot el correu / No llegits / Destacats.", - "account_hint": "S'aplica a {account}.", - "no_folders": "No hi ha cap carpeta disponible." - }, - "colorful_sidebar_icons": { - "label": "Icones de colors a la barra lateral", - "description": "Acoloreix les icones de carpetes i etiquetes segons el tipus (blau per a la safata d'entrada, vermell per a la brossa, verd per als enviats, etc.). Desactiveu-ho per a una barra lateral monocroma." - }, - "tint_list_rows": { - "label": "Acoloreix les files de la llista segons l'etiqueta", - "description": "Ombreja cada fila de missatge amb el color de la primera etiqueta. Desactiveu-ho per mantenir les files planes; els punts i les etiquetes encara mostraran el color." - }, - "show_folder_total_count": { - "label": "Mostra el nombre total de missatges", - "description": "Mostra el nombre total de missatges al costat de les carpetes i etiquetes, juntament amb el nombre de no llegits. Desactiveu-ho per mostrar només els no llegits." - }, - "favicon_unread_badge": { - "label": "Nombre de no llegits a la icona de la pestanya", - "description": "Mostra el nombre de no llegits de la safata d'entrada com a distintiu a la icona de la pestanya del navegador, perquè el correu nou sigui visible encara que la pestanya no tingui el focus. Desactiveu-ho per mantenir la icona sense distintiu." - }, - "pro_interface": { - "label": "Interfície Pro (experimental)", - "description": "Disposició avançada només per a escriptori amb navegació de missatges en diverses pestanyes i fluxos de treball entre comptes. La interfície estàndard no es veu afectada; podeu tornar-hi en qualsevol moment.", - "open_label": "Obre la interfície Pro", - "back_to_standard": "Torna a l'estàndard" - }, - "cross_unread": { - "label": "No llegits", - "description": "Mostra una entrada de No llegits a la bústia unificada amb el correu no llegit de les carpetes seleccionades." - }, - "cross_starred": { - "label": "Destacats", - "description": "Mostra una entrada de Destacats a la bústia unificada amb el correu destacat de les carpetes seleccionades." - }, - "cross_all": { - "label": "Tot el correu", - "description": "Mostra una entrada de Tot el correu a la bústia unificada amb tot el correu de les carpetes seleccionades." - } - }, - "keywords": { - "title": "Etiquetes de correu", - "description": "Definiu etiquetes per organitzar els correus amb colors. Es desen com a paraules clau JMAP al servidor.", - "add_keyword": "Afegeix una etiqueta", - "label_field": "Nom mostrat", - "label_placeholder": "p. ex. Feina, Personal, Urgent", - "id_field": "ID de l'etiqueta", - "id_placeholder": "p. ex. feina, personal", - "color_field": "Color", - "id_exists": "Aquest ID d'etiqueta ja existeix", - "edit": "Edita l'etiqueta", - "delete": "Suprimeix l'etiqueta", - "save": "Desa", - "add": "Afegeix", - "cancel": "Cancel·la", - "migrating": "Actualitzant l'etiqueta als correus existents…", - "migration_error": "No s'ha pogut actualitzar l'etiqueta als correus existents", - "nesting": { - "label": "Etiquetes imbricades", - "description": "Imbrica etiquetes sota altres etiquetes i mostra-les com un arbre a la barra lateral." - }, - "parent_field": "Etiqueta principal", - "no_parent": "Sense etiqueta principal", - "too_long": "Aquest camí d'etiqueta és massa llarg (com a màxim {max} caràcters)", - "has_children_locked": "Hi ha altres etiquetes imbricades sota aquesta, per això el seu nom i la seva etiqueta principal estan bloquejats. Mou-les o elimina-les primer.", - "has_children_delete": "Elimina primer les etiquetes imbricades sota aquesta", - "visibility_field": "Visibilitat a la barra lateral", - "visibility": { - "show": "Mostra", - "unread": "Mostra si hi ha no llegits", - "hide": "Amaga" - }, - "unrecognized": { - "title": "Recupera etiquetes del servidor", - "description": "Els noms i els colors de les etiquetes es desen en aquest navegador, però les etiquetes viuen als vostres correus com a paraules clau JMAP. Analitzeu el correu per trobar paraules clau que cap etiqueta d'aquí no expliqui i torneu-les a afegir.", - "scan": "Cerca etiquetes", - "rescan": "Torna a cercar", - "scanning": "S'està analitzant… {scanned} de {total} correus", - "none": "Totes les etiquetes dels vostres correus ja estan definides aquí.", - "messages": "{count, plural, one {# correu} other {# correus}}", - "add_all": "Afegeix-les totes ({count})", - "partial": "Només s'han analitzat els {scanned} correus més recents de {total}. Poden faltar etiquetes usades únicament en correus més antics.", - "error": "No s'ha pogut analitzar el correu per cercar etiquetes." - } - }, - "notifications": { - "test_sound": "Prova el so de notificació", - "sounds": { - "default": "Per defecte (bip)", - "cheerful": "Alegre", - "involved": "Elaborat", - "swift": "Gest ràpid", - "relax": "Relaxant" - }, - "push": { - "title": "Notificacions en segon pla", - "description": "Rebeu notificacions del sistema per al correu nou quan aquest lloc estigui tancat. S'entreguen mitjançant el repetidor push del Bulwark; el repetidor mai no veu el contingut del correu.", - "relay_label": "Repetidor push", - "relay_desc": "Per defecte utilitza el repetidor allotjat del Bulwark. Canvieu-ho només si allotgeu el vostre propi servidor.", - "relay_locked": "Establert per l'administrador", - "relay_locked_desc": "L'URL del repetidor push l'ha establert l'administrador i no es pot canviar.", - "status_active": "Actiu en aquest dispositiu", - "status_inactive": "No activat en aquest dispositiu", - "status_unsupported": "Aquest navegador no admet Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "Treballant…", - "enable": "Activa", - "reenable": "Torna a registrar", - "disable": "Desactiva", - "confirm_disable_title": "Voleu desactivar les notificacions en segon pla?", - "confirm_disable_message": "Aquest dispositiu deixarà de rebre avisos quan el lloc estigui tancat.", - "dismiss_aria": "Descarta l'avís de notificacions", - "ios_hint": "A l'iOS, primer instal·leu el lloc a la pantalla d'inici - el Safari només entrega Web Push a les PWA instal·lades.", - "devices_title": "Dispositius registrats", - "devices_desc": "Tots els dispositius registrats per rebre notificacions en segon pla d’aquest compte. Si en revoques un, deixa de rebre-les immediatament.", - "devices_loading": "S’estan carregant els dispositius registrats…", - "devices_empty": "No hi ha cap dispositiu registrat per a les notificacions en segon pla.", - "devices_error": "No s’han pogut carregar els dispositius registrats.", - "devices_refresh": "Actualitza", - "device_this": "Aquest dispositiu", - "device_other": "Un altre dispositiu", - "device_status_active": "Lliura notificacions", - "device_status_inactive": "Mai no ha lliurat cap notificació", - "device_status_unknown": "Desconegut per a aquest relé", - "device_expires": "Caduca el {date}", - "revoke": "Revoca", - "confirm_revoke_title": "Voleu revocar aquest dispositiu?", - "confirm_revoke_message": "Deixarà de rebre notificacions en segon pla immediatament. Si hi tornes a activar les notificacions push, es registrarà de nou.", - "confirm_revoke_message_this": "Aquest dispositiu deixarà de rebre notificacions en segon pla immediatament." - }, - "sound_selection": { - "title": "So de notificació", - "description": "Trieu quin so es reprodueix per a les notificacions", - "choose": "So", - "choose_desc": "Seleccioneu un to de notificació i feu clic a la icona de l'altaveu per escoltar-lo" - }, - "email": { - "title": "Notificacions de correu", - "description": "Configureu les notificacions per als correus entrants", - "enabled": "Notificacions de correu", - "enabled_desc": "Mostra notificacions quan arribin correus nous", - "sound": "So de notificació", - "sound_desc": "Reprodueix un avís sonor quan arribin correus nous", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Notificacions de calendari", - "description": "Configureu les notificacions per als esdeveniments del calendari", - "enabled": "Notificacions d'esdeveniments", - "enabled_desc": "Mostra avisos per als esdeveniments propers del calendari", - "sound": "So de notificació", - "sound_desc": "Reprodueix un avís sonor per als recordatoris del calendari", - "invitation_parsing": "Analitza les invitacions per correu", - "invitation_parsing_desc": "Detecta les invitacions de calendari als fitxers adjunts del correu i mostra les accions del calendari" - } - }, - "language_region": { - "title": "Idioma, regió i hora", - "description": "Idioma, format de data, format d'hora i altres preferències regionals", - "language": { - "label": "Idioma", - "description": "Trieu l'idioma preferit", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Format de data", - "description": "Com es mostren les dates a la llista de correus", - "smart": "Intel·ligent (segons l'idioma)", - "relative": "Relatiu (fa 1 h, fa 2 d)", - "full": "Sempre la data completa", - "preview_today": "Avui:", - "preview_this_week": "Aquesta setmana:", - "preview_older": "Més antics:" - }, - "date_locale": { - "label": "Regió del format de data", - "description": "Com s'ordenen les dates numèriques (dia, mes, any)", - "auto": "Automàtic (segons l'idioma)", - "iso": "ISO 8601 (AAAA-MM-DD)", - "dmy": "Dia/Mes/Any", - "mdy": "Mes/Dia/Any" - }, - "time_format": { - "label": "Format d'hora", - "description": "Trieu entre el format de 12 o 24 hores", - "12h": "12 hores", - "24h": "24 hores" - }, - "first_day": { - "label": "Primer dia de la setmana", - "description": "Comença la setmana en diumenge o dilluns", - "sunday": "Diumenge", - "monday": "Dilluns" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Esborra la cerca en canviar de carpeta", - "description": "En clicar una carpeta (Safata d'entrada, Enviats…) s'esborra la cerca activa i es mostra aquesta carpeta. Si està desactivat, la cerca es manté i es torna a executar a la carpeta triada." - }, - "request_read_receipt": { - "label": "Sol·licita confirmacions de lectura per defecte", - "description": "Activa per defecte la sol·licitud de confirmació de lectura en redactar un missatge nou." - }, - "read_receipt_response": { - "label": "Respon a les sol·licituds de confirmació de lectura", - "description": "Què fer quan un missatge entrant sol·licita una confirmació de lectura.", - "ask": "Pregunta cada vegada", - "always": "Envia sempre", - "never": "No enviïs mai" - }, - "title": "Comportament del correu", - "description": "Configureu com es gestionen els correus", - "mark_read": { - "label": "Marca com a llegit", - "description": "Quan marcar els correus com a llegits en obrir-los", - "instant": "Immediatament", - "delay_3s": "Després de 3 segons", - "delay_5s": "Després de 5 segons", - "never": "Mai" - }, - "delete_action": { - "label": "Acció de supressió", - "description": "Què passa quan suprimiu un correu", - "trash": "Mou a la paperera", - "trash_and_read": "Mou a la paperera i marca com a llegit", - "permanent": "Suprimeix definitivament", - "warning": "Els correus se suprimiran definitivament i no es podran recuperar. Aquesta acció és irreversible." - }, - "message_spacing": { - "label": "Espaiat del missatge", - "description": "Farciment al voltant del cos del missatge en el lector", - "auto": "Automàtic (recomanat)", - "always": "Afegeix sempre espaiat", - "edge": "De vora a vora" - }, - "plain_text_font": { - "label": "Tipus de lletra del text pla", - "description": "Tipus de lletra per als correus de text pla", - "mono": "Monoespaiada", - "sans": "Normal (igual que l'aplicació)" - }, - "archive_mode": { - "label": "Arxiva a", - "description": "Com organitzar els correus en arxivar-los", - "single": "Una única carpeta", - "year": "Una carpeta per any", - "month": "Una carpeta per mes", - "reorganize": "Reorganitza l'arxiu existent", - "reorganize_success": "{count, plural, =0 {Cap correu per reorganitzar} =1 {1 correu reorganitzat} other {# correus reorganitzats}}", - "reorganize_error": "No s'ha pogut reorganitzar l'arxiu" - }, - "permanently_delete_junk": { - "label": "Suprimeix definitivament la brossa", - "description": "Suprimeix definitivament els correus de la carpeta de brossa en lloc de moure'ls a la paperera" - }, - "mail_layout": { - "label": "Disposició del correu", - "description": "Trieu entre la vista dividida clàssica, un flux de lectura centrat a l'estil Gmail, o una subfinestra de lectura inferior a l'estil Zimbra.", - "split": "Vista dividida", - "split_description": "Manté la llista de missatges i la subfinestra de lectura visibles una al costat de l'altra.", - "focus": "Llista centrada", - "focus_description": "Mostra una línia per missatge i obre el correu a amplada completa mantenint visible la barra lateral de carpetes.", - "horizontal": "Subfinestra de lectura a baix", - "horizontal_description": "Mostra la llista de missatges a dalt i obre el missatge seleccionat en una subfinestra de lectura a sota." - }, - "show_preview": { - "label": "Mostra el text de previsualització", - "description": "Mostra la previsualització del correu a la llista", - "focus_description": "Mostra el text de previsualització incrustat dins la llista de missatges d'una línia centrada" - }, - "disable_threading": { - "label": "Desactiva l'agrupació en converses", - "description": "Mostra els correus com a missatges individuals en lloc d'agrupar-los per conversa" - }, - "plain_text_mode": { - "label": "Només text sense format", - "description": "Desactiva l'editor de text enriquit i envia tots els correus només com a text sense format, incloses les respostes i els reenviaments" - }, - "auto_select_reply_identity": { - "label": "Respon des de l'adreça de recepció", - "description": "En respondre, envia des de l'adreça a la qual es va enviar originalment el missatge. Primer intenta coincidir amb les identitats; per als lliuraments de captura general de domini, reescriu la capçalera «De» amb l'àlies mentre envia a través de la identitat principal." - }, - "reply_identity_match": { - "label": "Coincidència de l'adreça de recepció", - "description": "Quines adreces de recepció compten com a teves. «Només l'adreça exacta» tria únicament una de les teves identitats configurades; «mateix domini» també tracta qualsevol altra adreça dels teus dominis d'identitat com un àlies catch-all i hi reescriu la capçalera De. Tria l'adreça exacta si aquestes adreces són llistes de distribució.", - "exact": "Només l'adreça exacta", - "domain": "Qualsevol adreça dels meus dominis" - }, - "signature_position": { - "label": "Posició de la signatura", - "description": "On inserir la signatura a les respostes i reenviaments. Abans del text citat es llegeix de manera natural com a tancament de la resposta; després manté el missatge original contigu.", - "above_quote": "Abans del text citat", - "below_quote": "Després del text citat" - }, - "signature_separator": { - "label": "Delimitador de la signatura", - "description": "Prefixa la signatura amb la línia delimitadora estàndard «-- » (RFC 3676). Desactiveu-ho si preferiu passar directament del missatge a la signatura." - }, - "sub_address_delimiter": { - "label": "Delimitador de subadreça", - "description": "Caràcter que separa el nom d'usuari d'una etiqueta de subadreça. Feu-lo coincidir amb el delimitador que utilitza el servidor de correu (p. ex. user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "Personalitzat…", - "custom_input_label": "Caràcter delimitador personalitzat" - }, - "attachment_click_action": { - "label": "Acció en clicar un fitxer adjunt", - "description": "Trieu si en clicar un fitxer adjunt es previsualitza o es baixa immediatament", - "preview": "Previsualitza quan sigui possible", - "download": "Baixa immediatament" - }, - "attachment_position": { - "label": "Posició dels fitxers adjunts", - "description": "On mostrar els fitxers adjunts a la capçalera del correu", - "beside-sender": "Al costat del remitent", - "below-header": "Sota la capçalera" - }, - "emails_per_page": { - "10": "10 correus", - "25": "25 correus", - "50": "50 correus", - "100": "100 correus", - "label": "Correus per pàgina", - "description": "Nombre de correus que es carreguen alhora" - }, - "always_light_mode": { - "label": "Mostra sempre els correus en mode clar", - "description": "Renderitza el contingut del correu en mode clar encara que l'aplicació estigui en mode fosc, evitant problemes de conversió del mode fosc" - }, - "external_content": { - "label": "Contingut extern", - "description": "Com gestionar les imatges i el contingut extern", - "ask": "Pregunta sempre", - "block": "Bloqueja sempre", - "allow": "Permet sempre" - }, - "trusted_senders": { - "label": "Remitents de confiança", - "description": "Gestioneu els remitents les imatges dels quals es carreguen automàticament", - "count_zero": "Cap", - "count_one": "1 remitent", - "count_other": "{count} remitents", - "modal_title": "Remitents de confiança", - "empty_title": "Encara no hi ha remitents de confiança", - "empty_description": "En visualitzar un correu amb imatges bloquejades, feu clic a «Confia sempre en aquest remitent» per afegir-lo aquí.", - "add_manually": "Afegeix un remitent manualment", - "add_button": "Afegeix", - "add_placeholder": "Introduïu l'adreça electrònica", - "search_placeholder": "Cerca remitents...", - "no_results": "Cap remitent coincideix amb la cerca", - "remove": "Elimina", - "close": "Tanca", - "invalid_email": "Introduïu una adreça electrònica vàlida", - "already_added": "Aquest remitent ja és de confiança", - "save_error": "No s'ha pogut desar - consulteu el registre de depuració de Contactes per obtenir més detalls", - "use_address_book_label": "Sincronitza amb la llibreta d'adreces", - "use_address_book_description": "Desa els remitents de confiança en una llibreta d'adreces dedicada «Remitents de confiança» perquè es sincronitzin a tots els dispositius" - }, - "hover_actions": { - "label": "Accions ràpides en passar el cursor", - "description": "Trieu quines accions ràpides apareixen en passar el cursor per sobre d'un correu de la llista", - "delete": "Suprimeix", - "star": "Marca/Treu l'estrella", - "mark_read": "Marca com a llegit/no llegit", - "archive": "Arxiva", - "tag": "Etiqueta", - "spam": "Marca com a brossa", - "not_spam": "No és brossa", - "none_selected": "Cap acció seleccionada", - "mode_label": "Mode de visualització", - "mode_inline": "Incrustat", - "mode_floating": "Flotant", - "corner_label": "Posició flotant", - "corner_top-left": "Superior esquerra", - "corner_top-right": "Superior dreta", - "corner_bottom-left": "Inferior esquerra", - "corner_bottom-right": "Inferior dreta" - }, - "default_mail_program": { - "label": "Programa de correu predeterminat", - "description": "Registra {appName} com al programa de correu predeterminat per als enllaços mailto:", - "button": "Estableix com a predeterminat", - "success": "S'ha demanat al navegador que l'estableixi com a predeterminat", - "error": "El navegador no admet aquesta funció" - }, - "attachment_reminder": { - "label": "Recordatori de fitxers adjunts", - "description": "Avisa abans d'enviar quan el missatge esmenta fitxers adjunts però no n'hi ha cap", - "keywords_label": "Paraules clau que ho activen", - "keywords_description": "Paraules o frases que activen el recordatori quan es troben al missatge", - "add_placeholder": "Afegeix una paraula clau...", - "add": "Afegeix", - "remove": "Elimina" - }, - "empty_subject_warning": { - "label": "Avís d'assumpte buit", - "description": "Demana confirmació abans d'enviar un missatge sense assumpte." - }, - "hide_inline_image_attachments": { - "label": "Amaga les imatges incrustades dels fitxers adjunts", - "description": "Les imatges incrustades al cos del missatge no es llisten com a fitxers adjunts separats" - }, - "attachment_image_previews": { - "label": "Mostra previsualitzacions d'imatge als fitxers adjunts", - "description": "Renderitza els fitxers adjunts d'imatge com a targetes de miniatura en lloc d'icones de fitxer genèriques" - }, - "send_delay": { - "label": "Desfés l'enviament / retard d'enviament", - "description": "Retarda els enviaments normals amb un petit marge al servidor.", - "off": "Desactivat", - "seconds": "{seconds} segons", - "unsupported": "El compte actual no indica compatibilitat amb l'enviament retardat. La configuració es continua desant per a altres comptes." - }, - "return_to_list_after_action": { - "label": "Torna a la llista després de suprimir o marcar com a no llegit", - "description": "Després de suprimir o marcar com a no llegit el missatge obert, torna a la llista de missatges en lloc d'obrir el següent." - }, - "swipe_right_action": { - "label": "Acció en lliscar a la dreta (mòbil)", - "description": "En dispositius tàctils, l'acció que es fa quan llisqueu un missatge cap a la dreta a la llista." - }, - "swipe_left_action": { - "label": "Acció en lliscar a l'esquerra (mòbil)", - "description": "En dispositius tàctils, l'acció que es fa quan llisqueu un missatge cap a l'esquerra a la llista." - }, - "swipe_actions": { - "none": "Cap", - "archive": "Arxiva", - "delete": "Suprimeix", - "mark_read": "Commuta llegit", - "star": "Commuta l'estrella", - "spam": "Marca com a brossa" - }, - "rtl_editing": { - "label": "Compatibilitat amb edició de dreta a esquerra", - "description": "Afegeix un botó de direcció a la barra d'eines del redactor perquè pugueu establir els paràgrafs d'esquerra a dreta o de dreta a esquerra" - } - }, - "composer": { - "title": "Redactor", - "description": "Configureu les opcions de redacció de correu", - "autosave": { - "label": "Interval de desat automàtic", - "description": "Amb quina freqüència es desen automàticament els esborranys", - "30s": "Cada 30 segons", - "1m": "Cada minut", - "2m": "Cada 2 minuts", - "5m": "Cada 5 minuts" - }, - "send_confirmation": { - "label": "Confirmació d'enviament", - "description": "Demana confirmació abans d'enviar els correus" - }, - "default_reply": { - "label": "Mode de resposta predeterminat", - "description": "Acció predeterminada en clicar respon", - "reply": "Respon", - "reply_all": "Respon a tots" - } - }, - "privacy": { - "title": "Privadesa i seguretat", - "description": "Gestioneu la configuració de privadesa i seguretat", - "external_images": { - "label": "Bloqueja les imatges externes", - "description": "Evita el seguiment mitjançant imatges externes" - }, - "session_timeout": { - "label": "Temps d'espera de la sessió", - "description": "Tanca la sessió automàticament després d'inactivitat", - "never": "Mai", - "30m": "30 minuts", - "1h": "1 hora", - "4h": "4 hores" - }, - "clear_cache": { - "label": "Neteja la memòria cau", - "description": "Elimina les dades emmagatzemades i els fitxers temporals", - "button": "Neteja la memòria cau", - "confirm": "Segur que voleu netejar la memòria cau?", - "success": "Memòria cau netejada correctament" - } - }, - "account": { - "title": "Compte", - "description": "Vegeu la informació del vostre compte", - "name_label": "Nom mostrat", - "username_label": "Nom d'usuari", - "account_type_label": "Tipus de compte", - "auth_method_label": "Autenticació", - "auth_method_oauth": "Inici de sessió únic (OAuth/OIDC)", - "auth_method_basic": "Contrasenya", - "demo_account": "Compte de demostració", - "email": { - "label": "Adreça electrònica", - "value": "{email}" - }, - "server": { - "label": "Servidor JMAP", - "value": "{server}" - }, - "storage": { - "label": "Ús de l'emmagatzematge", - "used": "{used} de {total} utilitzats", - "percentage": "{percent}% utilitzat" - }, - "last_sync": { - "label": "Última sincronització", - "value": "{time}" - }, - "accounts": { - "title": "Comptes amb la sessió iniciada", - "description": "Arrossegueu per reordenar com apareixen els comptes al menú desplegable", - "active": "Compte actiu actualment", - "default_badge": "Compte predeterminat", - "set_default": "Estableix com a predeterminat", - "switch_to": "Canvia a aquest compte", - "move_up": "Mou amunt", - "move_down": "Mou avall", - "drag_handle": "Arrossegueu per reordenar", - "add": "Afegeix un compte" - }, - "shared_accounts": { - "title": "Compartit amb mi", - "description": "Comptes de grup i compartits que podeu gestionar. Seleccioneu-ne un per editar-ne els filtres, la resposta automàtica, els calendaris i els contactes.", - "shared_label": "Compte compartit" - } - }, - "scoped": { - "back": "Torna al meu compte", - "managing": "Gestionant: {name}" - }, - "security": { - "title": "Seguretat del compte", - "description": "Gestioneu la contrasenya, l'autenticació de dos factors i la configuració de seguretat", - "detecting": "Detectant les capacitats del servidor...", - "not_available": "La gestió de la seguretat del compte no està disponible per a aquest servidor de correu. És possible que els permisos necessaris estiguin desactivats. Consulteu la documentació per obtenir més detalls.", - "password": { - "title": "Canvia la contrasenya", - "current": "Contrasenya actual", - "new": "Contrasenya nova", - "confirm": "Confirma la contrasenya nova", - "submit": "Canvia la contrasenya", - "success": "Contrasenya canviada correctament", - "error_title": "No s'ha pogut canviar la contrasenya", - "error_mismatch": "Les contrasenyes noves no coincideixen", - "error_min_length": "La contrasenya ha de tenir com a mínim 8 caràcters", - "error_generic": "No s'ha pogut canviar la contrasenya" - }, - "display_name": { - "label": "Nom mostrat", - "description": "El vostre nom tal com apareix al servidor", - "placeholder": "Introduïu el nom mostrat", - "save": "Desa", - "success": "Nom mostrat actualitzat", - "error": "No s'ha pogut actualitzar el nom mostrat" - }, - "totp": { - "section_title": "Autenticació de dos factors", - "label": "Autenticació TOTP", - "description": "Afegiu una capa addicional de seguretat amb una contrasenya d'un sol ús basada en el temps", - "active": "Activada", - "inactive": "Desactivada", - "enabled": "Autenticació de dos factors activada", - "disabled": "Autenticació de dos factors desactivada", - "enable_error": "No s'ha pogut activar el 2FA", - "disable_error": "No s'ha pogut desactivar el 2FA", - "setup_instructions": "Copieu aquest URL a la vostra aplicació d'autenticació (Google Authenticator, Authy, etc.):", - "verification_code": "Codi de verificació", - "confirm": "Confirma", - "disable": "Desactiva", - "disable_confirm_prompt": "Introduïu la contrasenya per desactivar l'autenticació de dos factors.", - "password_required": "Cal la contrasenya", - "code_required": "Cal el codi de verificació", - "code_invalid": "El codi de verificació no és vàlid. Comproveu l'aplicació d'autenticació i torneu-ho a provar." - }, - "app_passwords": { - "title": "Contrasenyes d'aplicació", - "description": "Creeu contrasenyes per a aplicacions que no admeten l'autenticació de dos factors", - "add": "Afegeix", - "create": "Crea", - "cancel": "Cancel·la", - "done": "Fet", - "generate": "Genera", - "name_label": "Nom de l'aplicació", - "name_placeholder": "p. ex. Thunderbird, Correu de l'iPhone", - "expires_label": "Caduca (opcional)", - "allowed_ips_label": "IP permeses (opcional)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Separades per comes o espais. Deixeu-ho buit per permetre qualsevol IP.", - "password_label": "Contrasenya (deixeu-ho buit per generar-la automàticament)", - "password_placeholder": "Es genera automàticament si es deixa buit", - "copy_now_warning": "Copieu aquesta contrasenya ara - no es tornarà a mostrar.", - "added": "Contrasenya d'aplicació creada", - "removed": "Contrasenya d'aplicació eliminada", - "add_error": "No s'ha pogut crear la contrasenya d'aplicació", - "remove_error": "No s'ha pogut eliminar la contrasenya d'aplicació", - "none": "No hi ha cap contrasenya d'aplicació configurada" - }, - "api_keys": { - "title": "Claus API", - "description": "Creeu claus API per a scripts i integracions que es comuniquen directament amb el servidor", - "name_label": "Nom de la clau", - "name_placeholder": "p. ex. Script de còpia de seguretat, executor de CI", - "copy_now_warning": "Copieu aquesta clau API ara - no es tornarà a mostrar.", - "added": "Clau API creada", - "removed": "Clau API eliminada", - "add_error": "No s'ha pogut crear la clau API", - "remove_error": "No s'ha pogut eliminar la clau API", - "none": "No hi ha cap clau API configurada" - }, - "encryption": { - "section_title": "Xifratge en repòs", - "label": "Xifratge del correu", - "description": "Xifra els correus emmagatzemats al servidor per obtenir més privadesa", - "active": "Xifratge {type} activat", - "inactive": "Desactivat", - "enabled": "Xifratge en repòs activat", - "disabled_success": "Xifratge en repòs desactivat", - "error": "No s'ha pogut actualitzar la configuració de xifratge", - "algorithm_label": "Algorisme de xifratge", - "encrypt_on_append": "Xifra els correus nous en pujar-los", - "allow_spam_training": "Permet l'entrenament antispam abans de xifrar els correus", - "enable_encryption": "Activa el xifratge", - "enable_tooltip": "Activa el xifratge amb aquesta clau", - "configure_title": "Activa el xifratge amb aquesta clau", - "enable_button": "Activa", - "disable_tooltip": "Desactiva el xifratge", - "enabled_success": "Xifratge en repòs activat" - }, - "email_client": { - "title": "Configuració del client de correu", - "description": "Utilitzeu aquestes credencials per configurar el client de correu d'escriptori o mòbil (Thunderbird, Apple Mail, Outlook, etc.)", - "jmap_username_label": "Nom d'usuari JMAP", - "copy": "Copia", - "copied": "Copiat", - "password_instructions": "Utilitzeu el nom d'usuari JMAP anterior juntament amb una contrasenya d'aplicació per iniciar la sessió al client de correu. Creeu una contrasenya d'aplicació a la secció anterior si encara no ho heu fet." - }, - "link_device": { - "title": "Enllaça l'aplicació mòbil", - "description": "Inicieu la sessió a l'aplicació mòbil Bulwark Mail sense escriure res. Genereu un codi QR aquí i escanegeu-lo des de la pantalla d'inici de sessió de l'aplicació.", - "generate": "Mostra el codi QR", - "regenerate": "Mostra un codi nou", - "instructions": "Obriu l'aplicació Bulwark Mail, toqueu «Escaneja el codi QR» a la pantalla d'inici de sessió i apunteu la càmera aquí.", - "expires_in": "Aquest codi caduca d'aquí a {seconds} segons. Només es pot utilitzar una vegada.", - "expired": "Aquest codi ha caducat.", - "generating": "Generant…", - "error": "No s'ha pogut crear el codi d'aparellament. Torneu-ho a provar." - }, - "public_keys": { - "name_label": "Nom de la clau pública", - "name_placeholder": "p. ex. La meva clau", - "key_label": "Clau pública (S/MIME o PGP en ASCII armor)", - "key_placeholder": "Enganxeu aquí la vostra clau pública", - "none": "No hi ha cap clau pública configurada" - } - }, - "identities": { - "title": "Identitats d'enviament", - "description": "Gestioneu les adreces electròniques des de les quals podeu enviar", - "identities_count": { - "label": "Les vostres identitats", - "description": "Adreces electròniques configurades per a l'enviament", - "count_zero": "Cap identitat", - "count_one": "1 identitat", - "count_other": "{count} identitats" - }, - "manage": "Gestiona les identitats", - "sub_addressing": { - "label": "Subadreçament", - "description": "Utilitzeu etiquetes com user+tag@domain.com per organitzar el correu entrant", - "learn_more": "Més informació" - } - }, - "vacation": { - "title": "Resposta automàtica", - "description": "Respon automàticament als correus entrants mentre esteu fora", - "loading": "Carregant la configuració de la resposta automàtica...", - "not_supported": "El servidor de correu no admet respostes automàtiques.", - "fetch_error": "No s'ha pogut carregar la configuració de la resposta automàtica. Torneu-ho a provar.", - "status": { - "label": "Resposta automàtica", - "description": "Envia una resposta automàtica a les persones que us envien correu", - "active": "Activa", - "inactive": "Inactiva" - }, - "date_range": { - "title": "Interval de dates", - "description": "Opcionalment, limiteu la resposta automàtica a un període específic", - "start": "Data d'inici", - "start_description": "Deixeu-ho buit per no establir cap límit inicial", - "end": "Data de finalització", - "end_description": "Deixeu-ho buit per no establir cap límit final" - }, - "message": { - "title": "Missatge de resposta automàtica", - "description": "El missatge que s'enviarà com a resposta", - "subject_label": "Assumpte", - "subject_description": "Línia d'assumpte de la resposta automàtica", - "subject_placeholder": "Fora de l'oficina", - "body_label": "Cos del missatge", - "body_description": "Contingut del missatge en text sense format", - "body_placeholder": "Gràcies pel vostre correu. Actualment estic fora de l'oficina i respondré quan torni.", - "html_label": "Missatge amb format (HTML)", - "html_description": "Afegiu una versió amb format enriquit, amb enllaços i estils. Els destinataris el client de correu dels quals no la pugui mostrar rebran el text sense format anterior.", - "html_placeholder": "Escriviu una resposta amb format per a fora de l'oficina…" - }, - "preview": { - "title": "Previsualització", - "show": "Mostra la previsualització", - "hide": "Amaga la previsualització" - }, - "save": "Desa els canvis", - "saving": "Desant...", - "warnings": { - "end_before_start": "La data de finalització ha de ser posterior a la data d'inici", - "start_in_past": "La data d'inici és al passat", - "empty_body": "El cos del missatge està buit - els destinataris rebran una resposta en blanc", - "subject_too_long": "L'assumpte és massa llarg per al servidor (com a màxim {max} bytes).", - "body_too_long": "El missatge és massa llarg per al servidor (com a màxim {max} bytes, amb el format inclòs)." - } - }, - "folders": { - "role_memos": "Notes", - "title": "Carpetes", - "description": "Gestioneu les carpetes de correu i assigneu-hi rols estàndard", - "folder_list": "Les vostres carpetes", - "folder_list_description": "Feu clic a la icona d'una carpeta per personalitzar-la", - "standard_roles": "Rols estàndard de carpeta", - "standard_roles_description": "Assigneu quines carpetes s'utilitzen per als rols estàndard de la bústia, com la safata d'entrada, els enviats, la paperera, etc.", - "role_inbox": "Safata d'entrada", - "role_drafts": "Esborranys", - "role_sent": "Enviats", - "role_trash": "Paperera", - "role_junk": "Correu brossa", - "role_archive": "Arxiu", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Cap", - "create_folder": "Crea una carpeta", - "create_subfolder": "Crea una subcarpeta", - "reorder": "Arrossegueu per reordenar", - "reorder_error": "No s'han pogut reordenar les carpetes", - "move_error": "No s'ha pogut moure la carpeta", - "subfolder_of": "Dins de {name}", - "subfolder_name": "Nom de la subcarpeta", - "new_folder_name": "Nom de la carpeta", - "rename": "Canvia el nom", - "change_icon": "Canvia la icona", - "delete": "Suprimeix", - "confirm_delete": "Segur que voleu suprimir «{name}»? Els correus d'aquesta carpeta es mouran a la paperera.", - "create": "Crea", - "cancel": "Cancel·la", - "no_folders": "Cap carpeta personalitzada", - "cannot_delete_role": "No es pot suprimir una carpeta amb un rol estàndard. Elimineu primer el rol.", - "folder_created": "Carpeta creada", - "folder_renamed": "Nom de la carpeta canviat", - "folder_deleted": "Carpeta suprimida", - "role_updated": "Rol de la carpeta actualitzat", - "error_create": "No s'ha pogut crear la carpeta", - "error_rename": "No s'ha pogut canviar el nom de la carpeta", - "error_delete": "No s'ha pogut suprimir la carpeta", - "error_delete_has_children": "No es pot suprimir la carpeta: encara conté subcarpetes. Suprimiu-les o moveu-les primer.", - "error_delete_has_email": "No es pot suprimir la carpeta: encara conté correus. Moveu-los o suprimiu-los primer.", - "error_role": "No s'ha pogut actualitzar el rol de la carpeta" - }, - "advanced": { - "title": "Avançat", - "description": "Opcions avançades i configuració per a desenvolupadors", - "debug_mode": { - "label": "Mode de depuració", - "description": "Activa el registre detallat per a la resolució de problemes" - }, - "debug_categories": { - "description": "Seleccioneu quines categories registrar. Desactiveu les que no necessiteu per reduir el soroll a la consola.", - "jmap": "Client JMAP", - "jmap_description": "Operacions de bústia, obtenció de correus i sol·licituds del protocol JMAP", - "calendar": "Calendari", - "calendar_description": "Esdeveniments del calendari, importacions i missatges de programació", - "tasks": "Tasques", - "tasks_description": "Creació, obtenció i actualització de tasques del calendari", - "auth": "Autenticació", - "auth_description": "Inici de sessió, TOTP, intercanvi de testimonis i gestió de sessions", - "filters": "Filtres", - "filters_description": "Regles de filtre Sieve i scripts de resposta automàtica", - "email": "Visualització de correu", - "email_description": "Renderització de correu, processament TNEF i marcatge com a llegit", - "push": "Notificacions push", - "push_description": "Configuració i entrega de notificacions push", - "contacts": "Contactes i llibretes d'adreces", - "contacts_description": "Sincronització de contactes, operacions de llibreta d'adreces i remitents de confiança" - }, - "settings_sync": { - "label": "Sincronització de la configuració", - "description": "Sincronitzeu la configuració entre navegadors i dispositius" - }, - "sender_favicons": { - "label": "Icones dels remitents", - "description": "Mostra icones de llocs web com a imatges de perfil per als remitents d'empresa" - }, - "show_avatars_in_junk": { - "label": "Mostra els avatars a la carpeta de brossa", - "description": "Mostra imatges de perfil i icones dels remitents a la carpeta de brossa. Desactivat per defecte per evitar donar aparença de legitimitat a intents de pesca electrònica." - }, - "keyboard_shortcuts": { - "label": "Dreceres de teclat", - "description": "Consulteu les dreceres de teclat disponibles", - "button": "Mostra les dreceres" - }, - "refresh_cache": { - "label": "Actualitza les dades emmagatzemades", - "description": "Torna a carregar els contactes, els calendaris i les carpetes des del servidor. Manté els comptes i les sessions — soluciona una vista obsoleta o incorrecta sense tancar la sessió.", - "button": "Actualitza" - }, - "reset_settings": { - "label": "Restableix la configuració", - "description": "Restaura tota la configuració als valors per defecte", - "button": "Restableix als valors per defecte" - }, - "export_settings": { - "label": "Exporta la configuració", - "description": "Baixeu la configuració en format JSON", - "button": "Exporta" - }, - "import_settings": { - "label": "Importa la configuració", - "description": "Carregueu la configuració des d'un fitxer JSON", - "button": "Importa" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Aplicacions de la barra lateral", - "description": "Gestioneu aplicacions i enllaços personalitzats a la barra lateral", - "keep_loaded": "Mantén les aplicacions carregades", - "keep_loaded_description": "Manté les aplicacions incrustades en execució en segon pla en canviar entre elles per evitar recarregar-les", - "managed_title": "Proporcionades per l'administració", - "managed_description": "Aquestes aplicacions estan configurades per a tothom i no es poden canviar.", - "manage_title": "Aplicacions personalitzades", - "manage_description": "Afegiu, editeu o elimineu aplicacions personalitzades de la barra lateral" - }, - "contacts": { - "title": "Contactes", - "description": "Importeu i exporteu els contactes", - "group_by_letter_label": "Agrupa per lletra inicial", - "sort_by_last_name_label": "Ordena per cognom", - "sort_by_last_name_description": "Ordena la llista de contactes per cognom perquè els membres d'una família apareguin junts", - "group_by_letter_description": "Mostra capçaleres de secció alfabètiques a la llista de contactes", - "import_label": "Importa contactes", - "import_description": "Importeu contactes des d'un fitxer vCard (.vcf)", - "export_label": "Exporta contactes", - "export_description": "Exporteu tots els contactes com a fitxer vCard (.vcf)", - "manage_title": "Llibretes d'adreces", - "manage_description": "Canvieu el nom de les llibretes d'adreces", - "no_address_books": "No s'ha trobat cap llibreta d'adreces", - "categories_title": "Categories", - "categories_description": "Canvieu el nom de les categories de contactes", - "no_categories": "No s'ha trobat cap categoria" - }, - "downloads": { - "title": "Baixades", - "description": "Personalitzeu com es nomenen els correus i fitxers adjunts baixats.", - "reset": "Restaura el valor per defecte", - "preview": "Previsualització:", - "email_template": { - "label": "Nom del fitxer de correu (.eml)", - "description": "Plantilla utilitzada en exportar un correu o arrossegar-lo al sistema de fitxers. L'extensió .eml s'afegeix automàticament." - }, - "attachment_template": { - "label": "Nom del fitxer adjunt", - "description": "Plantilla utilitzada en baixar o arrossegar un fitxer adjunt. Si ometeu «{filename}» i «{ext}», es conserva l'extensió original." - }, - "bundle_template": { - "label": "Nom del fitxer .zip de diversos correus", - "description": "Plantilla utilitzada en arrossegar o baixar diversos correus seleccionats com a un únic fitxer .zip. L'extensió .zip s'afegeix automàticament." - }, - "spaces": { - "label": "Espais", - "description": "Substitueix els espais del nom del fitxer resultant per un altre caràcter.", - "keep": "Mantén els espais", - "underscore": "Substitueix per _", - "dash": "Substitueix per -" - }, - "lowercase": { - "label": "Minúscules", - "description": "Força que tot el nom del fitxer estigui en minúscules." - }, - "strip_diacritics": { - "label": "Elimina els diacrítics", - "description": "Converteix les lletres accentuades als seus equivalents ASCII (ä → a, é → e). Útil per a eines que malmeten els noms de fitxer Unicode." - }, - "collapse_separators": { - "label": "Redueix els separadors repetits", - "description": "Redueix seqüències d'espais, guions baixos o guions a un sol caràcter." - }, - "after_export": { - "label": "Després d'exportar", - "description": "Opcionalment, moveu el correu després d'exportar-lo com a .eml.", - "keep": "Mantén-lo a la bústia", - "archive": "Mou a l'arxiu", - "trash": "Mou a la paperera" - } - }, - "filters": { - "title": "Filtres de correu", - "description": "Creeu regles per ordenar, etiquetar i gestionar automàticament els correus entrants", - "add_rule": "Afegeix una regla", - "no_rules": "Cap regla de filtre", - "no_rules_description": "Creeu regles per organitzar automàticament els correus entrants", - "vacation_active": "La resposta automàtica està activa", - "vacation_active_description": "La resposta automàtica està activada per als missatges entrants", - "vacation_configure": "Configura", - "edit_rule": "Edita la regla", - "new_rule": "Regla nova", - "delete_rule": "Suprimeix la regla", - "delete_confirm": "Segur que voleu suprimir aquesta regla?", - "enable": "Activa", - "disable": "Desactiva", - "raw_editor": "Editor Sieve en brut", - "raw_editor_warning": "Editar l'script Sieve en brut pot trencar l'edició visual de regles. Els canvis fets aquí substitueixen el constructor visual.", - "validate": "Valida", - "validation_success": "L'script és vàlid", - "validation_error": "L'script té errors", - "save": "Desa les regles", - "saving": "Desant...", - "saved": "Filtres desats correctament", - "save_failed": "No s'han pogut desar els filtres", - "loading": "Carregant els filtres...", - "not_supported": "El servidor de correu no admet filtres de correu.", - "rule_name": "Nom de la regla", - "rule_name_placeholder": "p. ex., Ordena els butlletins", - "match_all": "Coincideix amb TOTES les condicions", - "match_any": "Coincideix amb QUALSEVOL condició", - "conditions": "Condicions", - "add_condition": "Afegeix una condició", - "actions": "Accions", - "add_action": "Afegeix una acció", - "stop_processing": "Atura el processament de les regles següents", - "include_spam": "Mou també els missatges marcats com a correu brossa", - "attachment_type_placeholder": "p. ex. pdf, doc, jpg", - "value_placeholder_multi": "Valor (múltiples separats per comes)", - "condition_fields": { - "attachment": "Fitxer adjunt", - "from": "De", - "to": "Per a", - "cc": "CC", - "subject": "Assumpte", - "header": "Capçalera personalitzada", - "size": "Mida", - "body": "Cos" - }, - "comparators": { - "has_any": "és present", - "has_type": "del tipus", - "contains": "conté", - "not_contains": "no conté", - "is": "és exactament", - "not_is": "no és", - "starts_with": "comença per", - "ends_with": "acaba amb", - "matches": "coincideix amb el patró", - "greater_than": "és més gran que", - "less_than": "és menys que" - }, - "action_types": { - "move": "Mou a una carpeta", - "copy": "Copia a una carpeta", - "forward": "Reenvia a", - "mark_read": "Marca com a llegit", - "star": "Marca el missatge amb estrella", - "add_label": "Afegeix una etiqueta", - "discard": "Descarta (suprimeix silenciosament)", - "reject": "Rebutja amb un missatge", - "keep": "Mantén a la safata d'entrada", - "stop": "Atura el processament" - }, - "move_to_folder": "Selecciona una carpeta", - "copy_to_folder": "Selecciona una carpeta", - "forward_to": "Reenvia a l'adreça electrònica", - "forward_placeholder": "correu@exemple.com", - "forward_keep_copy": "Conserva'n una còpia", - "forward_limit": "Límit de reenviaments per missatge en aquest servidor: {count}. Els reenviaments addicionals s'ometen.", - "reject_message": "Missatge de rebuig", - "reject_placeholder": "El vostre correu ha estat rebutjat", - "label_name": "Nom de l'etiqueta", - "label_placeholder": "Selecciona una etiqueta", - "header_name": "Nom de la capçalera", - "header_placeholder": "p. ex., X-Mailing-List", - "size_bytes": "Mida en bytes", - "size_placeholder": "p. ex., 1000000", - "system_managed": "Regla gestionada pel sistema", - "opaque_warning": "Aquest script s'ha editat fora del constructor visual. Només està disponible l'edició Sieve en brut.", - "open_sieve_editor": "Obre l'editor Sieve en brut", - "fetch_error": "No s'han pogut carregar els filtres", - "expanded_view": "Vista ampliada", - "expanded_view_description": "Mostra les regles de filtre amb blocs detallats de condicions i accions", - "if": "Si", - "then": "Aleshores", - "match_all_conditions": "totes coincideixen", - "match_any_condition": "alguna coincideix", - "and": "i", - "or": "o", - "cancel": "Cancel·la", - "confirm_delete": "Suprimeix", - "rule_list": "Regles de filtre", - "drag_to_reorder": "Arrossegueu per reordenar", - "match_type": "Tipus de coincidència", - "reset_to_visual": "Torna al constructor visual", - "reset_warning": "Això descartarà l'script actual i començarà de nou.", - "confirm_reset": "Reinicia", - "validation_empty_name": "El nom de la regla és obligatori", - "validation_empty_conditions": "Cal com a mínim una condició amb un valor", - "validation_empty_actions": "Cal com a mínim una acció", - "templates_section": "Comença des d'una plantilla", - "template_newsletters": "Mou els butlletins a una carpeta", - "template_receipts": "Arxiva automàticament els rebuts", - "template_important": "Marca els correus importants", - "template_notifications": "Filtra les notificacions", - "sieve_editor": { - "title": "Editor d'scripts Sieve", - "warning": "Editar l'script Sieve en brut pot trencar l'edició visual de regles. Els canvis fets aquí substitueixen el constructor visual.", - "script_content": "Script Sieve", - "valid": "L'script és vàlid", - "invalid": "L'script té errors", - "save_warning": "En desar se sobreescriuran totes les regles visuals. Això no es pot desfer. Torneu a fer clic a Desa per confirmar-ho.", - "validating": "Validant...", - "validate": "Valida", - "cancel": "Cancel·la", - "save": "Desa", - "confirm_save": "Confirma el desament", - "validation_failed": "Ha fallat la sol·licitud de validació" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# condició} other {# condicions}}", - "actions_count": "{count, plural, one {# acció} other {# accions}}" - }, - "origin_external": "Extern", - "managed_by_tooltip": "Gestionat per {source}. Editeu-ho en aquesta aplicació, o utilitzeu l'editor Sieve en brut." - }, - "templates": { - "title": "Plantilles de correu", - "description": "Creeu plantilles de correu reutilitzables amb variables d'espai reservat", - "add": "Plantilla nova", - "edit": "Edita la plantilla", - "name": "Nom de la plantilla", - "name_placeholder": "p. ex., Correu de seguiment", - "category": "Categoria", - "category_placeholder": "p. ex., Feina, Personal", - "subject": "Assumpte", - "subject_placeholder": "Línia d'assumpte del correu", - "body": "Cos", - "body_placeholder": "Contingut del cos del correu...", - "recipients_placeholder": "correu@exemple.com", - "identity": "Envia com a", - "default_identity": "Identitat predeterminada", - "favorite": "Preferit", - "cancel": "Cancel·la", - "create": "Crea", - "update": "Actualitza", - "confirm_delete": "Suprimeix", - "no_templates": "Encara no hi ha plantilles", - "manage": "Gestiona les plantilles", - "count": "{count, plural, one {# plantilla} other {# plantilles}}", - "export_import": "Exporta i importa", - "export_import_description": "Feu una còpia de seguretat de les plantilles o transferiu-les a un altre dispositiu", - "export": "Exporta", - "import": "Importa", - "validation": { - "empty": "El nom de la plantilla és obligatori", - "too_long": "El nom de la plantilla ha de tenir com a màxim 200 caràcters" - } - }, - "files": { - "display": { - "title": "Visualització", - "description": "Configureu com es mostren els fitxers i les carpetes" - }, - "folder_layout": { - "label": "Navegació de carpetes", - "description": "Trieu com es mostren les carpetes: incrustades amb els fitxers o en un arbre a la barra lateral", - "inline": "Incrustades", - "sidebar": "Barra lateral" - }, - "default_view": { - "label": "Vista predeterminada", - "description": "Trieu entre disposició de quadrícula i de llista", - "list": "Llista", - "grid": "Quadrícula" - }, - "default_sort": { - "label": "Ordenació predeterminada", - "description": "Trieu l'ordenació predeterminada dels fitxers", - "name": "Nom", - "size": "Mida", - "modified": "Modificat" - }, - "sort_direction": { - "label": "Direcció de l'ordenació", - "description": "Trieu ordre ascendent o descendent", - "ascending": "Ascendent", - "descending": "Descendent" - }, - "icons": { - "title": "Icones", - "description": "Configureu l'aparença de les icones de fitxer" - }, - "show_icons": { - "label": "Mostra les icones de fitxer", - "description": "Mostra icones al costat dels fitxers i les carpetes" - }, - "colored_icons": { - "label": "Icones de colors", - "description": "Utilitza icones de colors en lloc de monocromes" - }, - "show_thumbnails": { - "label": "Mostra les miniatures", - "description": "Mostra previsualitzacions d'imatge en lloc d'icones per als fitxers d'imatge" - }, - "behavior": { - "title": "Comportament", - "description": "Configureu el comportament del navegador de fitxers" - }, - "show_hidden": { - "label": "Mostra els fitxers ocults", - "description": "Mostra els fitxers i les carpetes que comencen per un punt" - }, - "preview": { - "label": "Previsualització" - } - } - }, - "errors": { - "page_error_title": "S'ha produït un error", - "page_error_description": "S'ha produït un error inesperat. Torneu-ho a provar o torneu a la pàgina d'inici.", - "sidebar_error": "No s'han pogut carregar les bústies", - "email_list_error": "No s'han pogut carregar els correus", - "viewer_error_title": "No s'ha pogut mostrar el correu", - "viewer_error_description": "S'ha produït un problema en renderitzar aquest correu. És possible que contingui contingut no admès.", - "composer_error": "No s'ha pogut carregar el redactor", - "settings_error_title": "Configuració no disponible", - "settings_error_description": "No s'ha pogut carregar la configuració. És possible que les preferències no es desin.", - "try_again": "Torna-ho a provar", - "reload": "Torna a carregar", - "reload_emails": "Torna a carregar els correus", - "reload_settings": "Torna a carregar la configuració", - "retry": "Reintenta", - "go_home": "Vés a la safata d'entrada" - }, - "context_menu": { - "reply": "Respon", - "reply_all": "Respon a tots", - "forward": "Reenvia", - "mark_read": "Marca com a llegit", - "mark_unread": "Marca com a no llegit", - "star": "Marca amb estrella", - "unstar": "Treu l'estrella", - "pin": "Fixa", - "unpin": "Desfixa", - "move_to": "Mou a...", - "archive": "Arxiva", - "delete": "Suprimeix", - "mark_as_spam": "Denuncia com a brossa", - "not_spam": "No és brossa", - "tag": "Etiqueta", - "items_selected": "{count} correus seleccionats", - "edit_draft": "Edita l'esborrany", - "cancel_scheduled_send": "Cancel·la l'enviament", - "reschedule_send": "Reprograma", - "cancel_and_edit": "Cancel·la i edita", - "cancel_and_compose_again": "Cancel·la i torna a redactar" - }, - "mailbox_context_menu": { - "mark_folder_read": "Marca la carpeta com a llegida", - "mark_folder_tree_read": "Marca la carpeta i les subcarpetes com a llegides", - "mark_all_folders_read": "Marca totes les carpetes com a llegides", - "new_subfolder": "Subcarpeta nova...", - "new_folder": "Carpeta nova...", - "rename": "Canvia el nom...", - "share": "Comparteix...", - "import_email": "Importa .eml o .zip...", - "empty_folder": "Buida la carpeta", - "empty_folder_generic": "Buida la carpeta", - "delete_folder": "Suprimeix la carpeta", - "refresh": "Actualitza", - "mark_all_confirm_title": "Marca totes les carpetes com a llegides", - "mark_all_confirm_message": "Voleu marcar tots els missatges no llegits del vostre compte personal com a llegits?", - "delete_confirm_title": "Suprimeix la carpeta", - "delete_confirm_message": "Voleu suprimir definitivament la carpeta «{name}»? Aquesta acció no es pot desfer.", - "prompt_new_subfolder": "Introduïu un nom per a la subcarpeta nova.", - "prompt_new_folder": "Introduïu un nom per a la carpeta nova.", - "prompt_rename": "Introduïu un nom nou per a aquesta carpeta.", - "placeholder_folder_name": "Nom de la carpeta", - "create": "Crea", - "rename_confirm": "Canvia el nom", - "toast_marked_read": "Carpeta marcada com a llegida", - "toast_marked_read_count": "{count, plural, one {S'ha marcat 1 missatge com a llegit} other {S'han marcat # missatges com a llegits}}", - "toast_already_read": "No hi ha missatges no llegits", - "toast_marked_all_read": "Totes les carpetes marcades com a llegides", - "toast_emptied": "Carpeta buidada", - "toast_folder_created": "Carpeta creada", - "toast_folder_renamed": "Nom de la carpeta canviat", - "toast_folder_deleted": "Carpeta suprimida", - "toast_error_mark_read": "No s'ha pogut marcar com a llegit", - "toast_error_empty": "No s'ha pogut buidar la carpeta", - "toast_error_create": "No s'ha pogut crear la carpeta", - "toast_error_rename": "No s'ha pogut canviar el nom de la carpeta", - "toast_error_delete": "No s'ha pogut suprimir la carpeta", - "toast_error_delete_has_children": "La carpeta té subcarpetes. Elimineu-les primer.", - "toast_error_delete_has_email": "La carpeta no és buida. Buideu-la primer.", - "delete_with_contents_title": "La carpeta no està buida", - "delete_with_contents_message": "La carpeta \"{name}\" encara conté {count, plural, one {1 missatge} other {# missatges}}. Vols suprimir la carpeta i tots els seus missatges? No es pot desfer.", - "delete_with_contents_confirm": "Suprimeix la carpeta i els missatges" - }, - "shortcuts": { - "title": "Dreceres de teclat", - "tip": "Premeu ? en qualsevol moment per mostrar aquesta ajuda", - "sections": { - "navigation": "Navegació", - "actions": "Accions de correu", - "global": "Global", - "threads": "Fils", - "composer": "Redactor" - }, - "navigation": { - "next_email": "Correu següent", - "previous_email": "Correu anterior", - "open_email": "Obre el correu", - "close_email": "Tanca / Desselecciona" - }, - "actions": { - "reply": "Respon", - "reply_all": "Respon a tots", - "forward": "Reenvia", - "star": "Commuta l'estrella", - "archive": "Arxiva", - "delete": "Suprimeix", - "mark_unread": "Marca com a no llegit", - "mark_read": "Marca com a llegit", - "toggle_spam": "Denuncia com a brossa / No és brossa" - }, - "global": { - "compose": "Redacta un correu nou", - "search": "Enfoca la cerca", - "help": "Mostra les dreceres", - "refresh": "Actualitza els correus", - "select_all": "Selecciona-ho tot", - "global_search": "Cerca-ho tot" - }, - "threads": { - "expand_collapse": "Desplega/replega el fil" - }, - "composer": { - "send": "Envia el correu", - "schedule_send": "Programa l'enviament", - "template_picker": "Obre el selector de plantilles" - } - }, - "threads": { - "messages_one": "{count} missatge", - "messages_other": "{count} missatges", - "messages_tooltip": "{count, plural, one {# missatge en aquesta conversa} other {# missatges en aquesta conversa}}", - "expand": "Desplega la conversa", - "collapse": "Replega la conversa", - "loading": "Carregant la conversa...", - "mark_read": "Marca la conversa com a llegida", - "mark_unread": "Marca la conversa com a no llegida", - "archive": "Arxiva la conversa", - "delete": "Suprimeix la conversa", - "star": "Marca la conversa amb estrella", - "unstar": "Treu l'estrella de la conversa", - "toggle_thread": "Commuta el fil" - }, - "identities": { - "modal_title": "Gestiona les identitats d'enviament", - "create_new": "Crea una identitat nova", - "edit_identity": "Edita la identitat", - "delete_confirm": "Voleu suprimir aquesta identitat? Això no es pot desfer.", - "cannot_delete": "Aquesta identitat no es pot suprimir", - "primary_identity": "Principal", - "set_as_primary": "Estableix com a principal", - "no_identities": "No s'ha trobat cap identitat", - "display": { - "reply_to": "Respon a:", - "bcc": "CCO:", - "signature": "Signatura:", - "preview": "Previsualització:" - }, - "validation_errors": { - "invalid_emails": "Correus electrònics no vàlids: {emails}", - "unknown_error": "Error desconegut" - }, - "form": { - "name_label": "Nom mostrat", - "name_placeholder": "p. ex., Correu de feina, Personal", - "name_required": "El nom és obligatori", - "email_label": "Adreça electrònica", - "email_placeholder": "el.vostre.correu@exemple.com", - "email_required": "El correu electrònic és obligatori", - "email_invalid": "Introduïu una adreça electrònica vàlida", - "email_immutable": "L'adreça electrònica no es pot canviar després de crear-la", - "reply_to_label": "Respon a (opcional)", - "reply_to_placeholder": "diferent@exemple.com", - "bcc_label": "CCO automàtica (opcional)", - "bcc_placeholder": "arxiu@exemple.com", - "text_signature_label": "Signatura de text", - "html_signature_label": "Signatura HTML", - "signature_byte_counter": "{bytes} / {max} bytes", - "signature_byte_limit_reached": "S'ha arribat al límit del servidor", - "save": "Desa la identitat", - "cancel": "Cancel·la", - "creating": "Creant...", - "updating": "Actualitzant..." - }, - "sub_address": { - "button_tooltip": "Utilitza subadreça", - "popover_title": "Afegeix una etiqueta de subadreça", - "tag_input_placeholder": "Introduïu una etiqueta (p. ex., compres)", - "preview_label": "Previsualització:", - "recent_tags": "Etiquetes recents", - "suggested_tags": "Suggerides", - "use_address": "Utilitza aquesta adreça", - "invalid_tag": "L'etiqueta només pot contenir caràcters alfanumèrics i guions", - "tag_too_long": "L'etiqueta ha de tenir com a màxim 30 caràcters", - "help_text": "Els correus enviats a user{delimiter}tag@domain.com arribaran a la vostra safata d'entrada", - "validation": { - "empty": "L'etiqueta no pot estar buida", - "too_long": "L'etiqueta ha de tenir com a màxim {max} caràcters", - "invalid_chars": "L'etiqueta només pot contenir lletres, números i guions" - } - }, - "badge": { - "sent_via": "via", - "sub_address_tag": "Enviat mitjançant la subadreça: {tag}", - "identity_name": "Enviat mitjançant la identitat: {name}", - "identity_short": "via {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Suprimeix", - "delete_confirm_title": "Suprimeix la identitat" - }, - "templates": { - "picker_title": "Trieu una plantilla", - "search_placeholder": "Cerca plantilles...", - "section_favorites": "Preferides", - "section_recent": "Recents", - "section_uncategorized": "Altres", - "no_templates": "Encara no hi ha plantilles", - "no_results": "No s'ha trobat cap plantilla", - "fill_placeholders": "Emplena els valors de les variables", - "enter_value": "Introduïu un valor...", - "preview": "Previsualització", - "insert_with_values": "Insereix amb els valors", - "insert_raw": "Insereix sense processar", - "copy_suffix": "(còpia)", - "placeholder": "Variable", - "placeholders": { - "recipient_name": "Nom del destinatari", - "company": "Nom de l'empresa", - "date": "Data actual", - "day_of_week": "Dia de la setmana", - "sender_name": "El vostre nom" - } - }, - "contacts": { - "title": "Contactes", - "search_placeholder": "Cerca contactes...", - "create_new": "Contacte nou", - "no_category": "Sense categoria", - "rename_category": "Canvia el nom de la categoria", - "category_name_label": "Nom de la categoria", - "category_renamed": "Nom de la categoria canviat", - "category_rename_failed": "No s'ha pogut canviar el nom de la categoria", - "category_added": "Contacte afegit a {name}", - "category_added_plural": "{count} contactes afegits a {name}", - "empty_state": "Encara no hi ha contactes", - "empty_state_title": "Encara no hi ha contactes", - "empty_state_subtitle": "Creeu el primer contacte o importeu-lo des d'un fitxer vCard", - "empty_search": "Cap contacte coincideix amb la cerca", - "empty_search_hint": "Proveu amb un altre terme de cerca", - "empty_filtered": "Cap contacte coincideix amb els filtres", - "empty_filtered_hint": "Proveu d'ajustar o netejar els filtres", - "clear_search": "Neteja la cerca", - "import_vcard": "Importa vCard", - "delete_confirm_title": "Suprimeix el contacte", - "delete_confirm": "Segur que voleu suprimir aquest contacte?", - "local_mode": "Els contactes es desen localment (el servidor no admet JMAP Contacts)", - "back_to_contacts": "Torna als contactes", - "back_to_email": "Torna al correu", - "open_categories": "Obre les categories", - "tabs": { - "all": "Tots", - "groups": "Grups" - }, - "shared": { - "title": "Compartit" - }, - "address_books": { - "create": "Llibreta d'adreces nova", - "created": "Llibreta d'adreces creada", - "create_failed": "No s'ha pogut crear la llibreta d'adreces", - "title": "Les meves llibretes d'adreces", - "shared_prefix": "Compartida: {name}", - "moved": "Contacte mogut a {name}", - "moved_plural": "{count} contactes moguts a {name}", - "move_failed": "No s'ha pogut moure el contacte", - "address_book": "Llibreta d'adreces", - "rename": "Canvia el nom de la llibreta d'adreces", - "name_label": "Nom de la llibreta d'adreces", - "renamed": "Nom de la llibreta d'adreces canviat", - "rename_failed": "No s'ha pogut canviar el nom de la llibreta d'adreces", - "default": "Predeterminada", - "set_default": "Estableix com a predeterminada", - "default_updated": "Llibreta d’adreces predeterminada actualitzada", - "set_default_failed": "No s’ha pogut establir la llibreta d’adreces predeterminada", - "manage": "Gestiona les llibretes d'adreces", - "share": "Comparteix la llibreta d'adreces", - "new_contact_in_book": "Contacte nou en aquesta llibreta d'adreces", - "delete": "Suprimeix la llibreta d'adreces", - "confirm_delete": "Voleu suprimir «{name}»? S'eliminaran tots els contactes d'aquesta llibreta d'adreces.", - "deleted": "Llibreta d'adreces suprimida", - "delete_failed": "No s'ha pogut suprimir la llibreta d'adreces" - }, - "detail": { - "emails": "Adreces electròniques", - "phones": "Números de telèfon", - "organizations": "Organitzacions", - "addresses": "Adreces", - "notes": "Notes", - "titles": "Càrrecs i funcions", - "online_services": "Serveis en línia", - "anniversaries": "Commemoracions", - "personal_info": "Informació personal", - "languages": "Idiomes", - "categories": "Categories", - "related_contacts": "Contactes relacionats", - "crypto_keys": "Claus criptogràfiques", - "cert_issuer": "Emissor", - "cert_expires": "Caduca", - "cert_expired": "Caducat", - "cert_fingerprint": "Empremta digital", - "cert_algorithm": "Algorisme", - "import_to_smime": "Importa a S/MIME", - "cert_already_imported": "Ja importat a S/MIME", - "cert_imported": "Certificat importat a l'emmagatzematge S/MIME", - "cert_import_failed": "No s'ha pogut importar el certificat", - "no_contact_selected": "Seleccioneu un contacte per veure'n els detalls", - "compose_email": "Redacta un correu", - "copy_email": "Copia el correu electrònic", - "copy_phone": "Copia el número de telèfon", - "copy_url": "Copia l'URL", - "copied": "Copiat al porta-retalls", - "copy_failed": "No s'ha pogut copiar al porta-retalls", - "created": "Creat", - "updated": "Última actualització", - "timezone": "Fus horari", - "anniversary_birth": "Aniversari", - "anniversary_death": "Traspàs", - "anniversary_wedding": "Commemoració", - "anniversary_other": "Altres", - "personal_expertise": "Especialitat", - "personal_hobby": "Afició", - "personal_interest": "Interès", - "personal_other": "Altres", - "gender": "Gènere", - "gender_masculine": "Home", - "gender_feminine": "Dona", - "gender_other": "Altres", - "gender_none": "No aplicable", - "gender_unknown": "Desconegut", - "calendar": "Calendari", - "calendar_uri": "URL del calendari", - "scheduling_uri": "URL de programació", - "freebusy_uri": "URL de disponibilitat", - "section_contact": "Dades de contacte", - "section_work": "Feina", - "section_personal": "Personal", - "email_default_label": "Correu electrònic", - "phone_default_label": "Telèfon", - "address_default_label": "Adreça", - "online_service_default_label": "En línia", - "organization_label": "Organització", - "title_label": "Càrrec", - "role_label": "Funció", - "language_label": "Idioma", - "related_default_label": "Relacionat", - "more_actions": "Més accions", - "age_years": "{count, plural, one {1 any} other {# anys}}", - "years_since": "{count, plural, one {1 any} other {# anys}}" - }, - "activity": { - "recent_emails": "Correus recents", - "upcoming_events": "Propers esdeveniments", - "no_emails": "Cap correu recent", - "no_events": "Cap esdeveniment proper", - "no_subject": "(Sense assumpte)", - "no_title": "(Sense títol)", - "load_failed": "No s'ha pogut carregar", - "unknown_sender": "Remitent desconegut", - "all_day": "Tot el dia" - }, - "form": { - "create_title": "Contacte nou", - "edit_title": "Edita el contacte", - "section_address_book": "Directori", - "select_address_book": "Seleccioneu un directori...", - "section_identity": "Nom i identitat", - "contact_type": "Tipus de contacte", - "type_person": "Persona", - "type_organization": "Organització", - "section_work": "Feina i organització", - "prefix": "Prefix", - "prefix_placeholder": "Dr., Sr., Sra.", - "given_name": "Nom", - "middle_name": "Segon nom", - "surname": "Cognoms", - "suffix": "Sufix", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Sobrenom", - "nickname_placeholder": "Sobrenom", - "email": "Correu electrònic", - "email_placeholder": "correu@exemple.com", - "phone": "Telèfon", - "phone_placeholder": "+1 234 567 890", - "phone_type": "Tipus", - "phone_voice": "Veu", - "phone_cell": "Mòbil", - "phone_fax": "Fax", - "phone_pager": "Buscapersones", - "phone_video": "Vídeo", - "phone_text": "SMS", - "organization": "Organització", - "organization_placeholder": "Nom de l'empresa", - "department": "Departament", - "department_placeholder": "Departament", - "job_title": "Càrrec", - "job_title_placeholder": "p. ex., Enginyer/a de programari", - "role": "Funció", - "role_placeholder": "p. ex., Cap d'equip", - "addresses": "Adreces", - "add_address": "Afegeix una adreça", - "street": "Carrer", - "city": "Ciutat", - "region": "Estat / Regió", - "postcode": "Codi postal", - "country": "País", - "online_services": "Serveis en línia", - "add_online_service": "Afegeix un servei en línia", - "url_placeholder": "https://...", - "service_placeholder": "Servei", - "anniversaries": "Commemoracions", - "add_anniversary": "Afegeix una data", - "anniversary_birth": "Aniversari", - "anniversary_wedding": "Commemoració", - "anniversary_death": "Traspàs", - "anniversary_other": "Altres", - "personal_info": "Informació personal", - "add_personal_info": "Afegeix una entrada", - "personal_info_placeholder": "p. ex., Fotografia", - "personal_expertise": "Especialitat", - "personal_hobby": "Afició", - "personal_interest": "Interès", - "personal_other": "Altres", - "level": "Nivell", - "level_high": "Alt", - "level_medium": "Mitjà", - "level_low": "Baix", - "categories": "Categories", - "categories_placeholder": "p. ex., Família, Amics, Companys", - "categories_hint": "Escriviu per cercar o afegir categories", - "category_add": "Afegeix", - "note": "Notes", - "note_placeholder": "Afegiu una nota...", - "gender": "Gènere", - "gender_sex": "Sexe", - "gender_male": "Home", - "gender_female": "Dona", - "gender_other": "Altres", - "gender_none": "No aplicable", - "gender_unknown": "Desconegut", - "gender_identity": "Identitat de gènere", - "gender_identity_placeholder": "Identitat de gènere...", - "calendar": "Calendari", - "calendar_uri": "URL del calendari", - "scheduling_uri": "URL de programació", - "freebusy_uri": "URL de disponibilitat", - "context_work": "Feina", - "context_private": "Privat", - "add_email": "Afegeix un correu electrònic", - "add_phone": "Afegeix un telèfon", - "save": "Desa", - "cancel": "Cancel·la", - "creating": "Creant...", - "updating": "Actualitzant...", - "name_required": "Introduïu un nom, un cognom o una organització", - "email_invalid": "Introduïu una adreça electrònica vàlida", - "email_error_inline": "Format de correu electrònic no vàlid", - "save_failed": "No s'ha pogut desar el contacte", - "delete": "Suprimeix", - "upload_photo": "Puja una foto", - "remove_photo": "Elimina la foto", - "photo_hint": "JPG o PNG, fins a 10 MB. Es redimensionarà.", - "photo_too_large": "La imatge és massa gran (màxim 10 MB)", - "photo_invalid": "Fitxer d'imatge no vàlid", - "change_photo": "Canvia" - }, - "groups": { - "create": "Grup nou", - "edit": "Edita el grup", - "empty": "Encara no hi ha grups", - "delete_confirm_title": "Suprimeix el grup", - "delete_confirm": "Segur que voleu suprimir aquest grup?", - "name_label": "Nom del grup", - "name_placeholder": "p. ex., Equip, Família", - "name_required": "El nom del grup és obligatori", - "save_failed": "No s'ha pogut desar el grup", - "members_label": "Membres", - "search_members": "Cerca contactes per afegir...", - "no_members": "Cap membre en aquest grup", - "member_count": "{count, plural, =0 {Cap membre} one {1 membre} other {# membres}}", - "send_email": "Envia un correu al grup", - "send_email_to": "Per a", - "send_email_cc": "CC", - "send_email_bcc": "CCO", - "no_member_emails": "Aquest grup no té cap membre amb adreça electrònica." - }, - "import": { - "title": "Importa contactes", - "drop_hint": "Feu clic per seleccionar un fitxer vCard", - "file_types": "Fitxers .vcf o .vcard", - "no_contacts": "No s'ha trobat cap contacte al fitxer", - "parse_error": "No s'ha pogut analitzar el fitxer vCard", - "found": "{count, plural, one {1 contacte trobat} other {# contactes trobats}}", - "duplicate": "Duplicat", - "select_all": "Selecciona-ho tot", - "deselect_all": "Desselecciona-ho tot", - "selected": "{count, plural, one {1 seleccionat} other {# seleccionats}}", - "import_button": "Importa", - "importing": "Important...", - "success": "{count, plural, one {1 contacte importat} other {# contactes importats}}", - "failed": "No s'ha pogut importar", - "close": "Tanca", - "file_too_large": "El fitxer és massa gran (màxim 5 MB)" - }, - "export": { - "title": "Exporta contactes", - "success": "{count, plural, one {1 contacte exportat} other {# contactes exportats}}" - }, - "bulk": { - "selected": "{count, plural, one {1 seleccionat} other {# seleccionats}}", - "select_all": "Selecciona-ho tot", - "delete": "Suprimeix", - "delete_confirm_title": "Suprimeix els contactes", - "delete_confirm": "Voleu suprimir {count, plural, one {1 contacte} other {# contactes}}?", - "deleted": "{count, plural, one {1 contacte suprimit} other {# contactes suprimits}}", - "add_to_group": "Afegeix al grup", - "choose_group": "Trieu un grup", - "adding_contacts": "Afegint {count, plural, one {1 contacte} other {# contactes}}", - "added_to_group": "Contactes afegits al grup", - "export": "Exporta", - "clear": "Neteja la selecció" - }, - "toast": { - "created": "Contacte creat", - "updated": "Contacte actualitzat", - "deleted": "Contacte suprimit", - "error_create": "No s'ha pogut crear el contacte", - "error_update": "No s'ha pogut actualitzar el contacte", - "error_delete": "No s'ha pogut suprimir el contacte" - }, - "context_menu": { - "open": "Obre", - "edit": "Edita", - "send_email": "Envia un correu", - "add_to_group": "Afegeix al grup", - "export_vcard": "Exporta com a vCard", - "delete": "Suprimeix", - "call": "Truca", - "duplicate": "Duplica", - "print": "Imprimeix" - }, - "filters": { - "toggle": "Filtres", - "select": "Selecciona", - "clear": "Neteja", - "close": "Tanca", - "title": "Filtres avançats", - "organization": "Empresa", - "organization_placeholder": "p. ex. Acme Corp", - "job_title": "Càrrec", - "job_title_placeholder": "p. ex. Dissenyador/a", - "location": "Ubicació", - "location_placeholder": "Ciutat o país", - "email_domain": "Domini del correu", - "email_domain_placeholder": "exemple.com", - "birthday_month": "Aniversari a", - "any_month": "Qualsevol mes", - "has_email": "Té correu electrònic", - "has_phone": "Té telèfon", - "has_photo": "Té foto", - "sort_by": "Ordena per", - "sort_first_name": "Nom", - "sort_last_name": "Cognom" - } - }, - "calendar": { - "title": "Calendari", - "back_to_email": "Torna al correu", - "back_to_month": "Torna al mes", - "my_calendars": "Calendaris", - "birthday_calendar": "Aniversaris", - "mini_calendar_change": "Feu clic per canviar de mes", - "views": { - "month": "Mes", - "week": "Setmana", - "day": "Dia", - "agenda": "Agenda", - "today": "Avui", - "month_hint": "Mes (m)", - "week_hint": "Setmana (w)", - "day_hint": "Dia (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Tasques", - "tasks_hint": "Tasques (k)" - }, - "events": { - "create": "Crea un esdeveniment", - "edit": "Edita l'esdeveniment", - "delete": "Suprimeix l'esdeveniment", - "details": "Detalls de l'esdeveniment", - "no_events": "Cap esdeveniment", - "all_day": "Tot el dia", - "more": "+{count} més", - "no_title": "(Sense títol)", - "resize": "Redimensiona l'esdeveniment", - "duplicate": "Duplica", - "today_header": "Avui", - "tomorrow_header": "Demà", - "agenda_show_earlier": "Mostra esdeveniments anteriors", - "agenda_loading": "Carregant…", - "agenda_range_start": "Esdeveniments des del {date}", - "agenda_range_end": "Esdeveniments fins al {date}", - "export_ics": "Exporta com a .ics", - "copy_title": "Copia el títol", - "copy_link": "Copia l'enllaç de la reunió", - "new_event": "Esdeveniment nou", - "new_all_day_event": "Esdeveniment de tot el dia nou", - "new_task": "Tasca nova", - "go_to_today": "Vés a avui" - }, - "detail": { - "add_note": "Afegiu una nota...", - "save_note": "Desa", - "note_saved": "Nota afegida", - "open_link": "Obre l'enllaç", - "meeting_link": "Enllaç de la reunió", - "tentative": "Provisional", - "cancelled": "Cancel·lat", - "delete_confirm": "Voleu suprimir aquest esdeveniment?" - }, - "form": { - "title": "Títol", - "description": "Descripció", - "location": "Ubicació", - "meeting_link": "Enllaç de la reunió", - "start_date": "Data d'inici", - "end_date": "Data de finalització", - "start_time": "Hora d'inici", - "end_time": "Hora de finalització", - "all_day_event": "Esdeveniment de tot el dia", - "calendar_select": "Calendari", - "save": "Desa", - "cancel": "Cancel·la", - "delete_confirm": "Segur que voleu suprimir aquest esdeveniment?", - "color": "Color" - }, - "participants": { - "availability": { - "title": "Disponibilitat", - "free": "Disponible", - "busy": "Ocupat", - "tentative": "Provisional", - "unknown": "No és un usuari d'aquest servidor", - "checking": "Comprovant…" - }, - "title": "Participants", - "add": "Afegeix un participant", - "organizer": "Organitzador", - "attendee": "Assistent", - "accepted": "Acceptat", - "declined": "Rebutjat", - "tentative": "Provisional", - "needs_action": "Cal resposta", - "remove": "Elimina", - "edit": "Edita", - "email_placeholder": "Afegiu una adreça electrònica o cerqueu contactes", - "send_invitations": "Envia invitacions als participants", - "status_summary": "{accepted} acceptats, {pending} pendents", - "invited_by": "Invitació de {name}", - "respond_below": "Responeu utilitzant els botons de sota", - "rsvp_label": "La vostra resposta", - "cancel_notification": "S'informarà els participants de la cancel·lació", - "you_organizer": "Sou l'organitzador", - "you_attendee": "Sou un assistent", - "no_participants": "Cap participant", - "count": "{count, plural, one {# participant} other {# participants}}" - }, - "recurrence": { - "title": "Repetició", - "none": "No es repeteix", - "daily": "Diàriament", - "weekly": "Setmanalment", - "monthly": "Mensualment", - "yearly": "Anualment", - "every_n_days": "Cada {count} dies", - "every_n_weeks": "Cada {count} setmanes", - "every_n_months": "Cada {count} mesos", - "until": "Fins a", - "occurrences": "{count} repeticions", - "custom": "Personalitzat…", - "edit_custom": "Edita la repetició personalitzada", - "every_n_years": "Cada {count} anys", - "on_days": "el {days}", - "on_day_n": "el dia {day}", - "on_the_nth": "el {nth} {day}", - "in_month": "al mes de {month}", - "nth_1": "primer", - "nth_2": "segon", - "nth_3": "tercer", - "nth_4": "quart", - "nth_last": "últim", - "editor_freq_day": "Dia", - "editor_freq_week": "Setmana", - "editor_freq_month": "Mes", - "editor_freq_year": "Any", - "editor_repeats_on": "Es repeteix el", - "editor_every": "Cada", - "editor_unit_days": "dia(dies)", - "editor_unit_weeks": "setmana(es)", - "editor_unit_months": "mes(os)", - "editor_unit_years": "any(s)", - "editor_on_day": "el dia", - "editor_on_the": "el", - "editor_in": "al", - "editor_ends": "Acaba", - "editor_never": "Mai", - "editor_ends_on": "El", - "editor_ends_after": "Després de", - "editor_occurrences": "repeticions" - }, - "recurrence_scope": { - "edit_title": "Edita l'esdeveniment periòdic", - "delete_title": "Suprimeix l'esdeveniment periòdic", - "description": "Aquest és un esdeveniment periòdic. Quins esdeveniments voleu modificar?", - "this_event": "Només aquest esdeveniment", - "this_and_future": "Aquest i els següents esdeveniments", - "all_events": "Tots els esdeveniments", - "cancel": "Cancel·la", - "save": "Desa", - "delete": "Suprimeix", - "rsvp_title": "Respon a l'esdeveniment periòdic", - "rsvp_description": "Aquest és un esdeveniment periòdic. A quins esdeveniments s'aplica la vostra resposta?", - "respond": "Respon" - }, - "alerts": { - "title": "Recordatoris", - "none": "Cap recordatori", - "at_time": "A l'hora de l'esdeveniment", - "minutes_before": "{count, plural, one {# minut abans} other {# minuts abans}}", - "hours_before": "{count, plural, one {# hora abans} other {# hores abans}}", - "days_before": "{count, plural, one {# dia abans} other {# dies abans}}", - "weeks_before": "{count, plural, one {# setmana abans} other {# setmanes abans}}", - "unit_minutes_before": "minuts abans", - "unit_hours_before": "hores abans", - "unit_days_before": "dies abans", - "unit_weeks_before": "setmanes abans", - "add": "Afegeix un recordatori", - "remove": "Elimina el recordatori", - "amount": "Quantitat del recordatori", - "unit": "Unitat del recordatori" - }, - "settings": { - "title": "Configuració del calendari", - "default_view": "Vista predeterminada", - "week_starts_on": "La setmana comença el", - "time_format": "Format d'hora", - "default_calendar": "Calendari predeterminat", - "default_reminder": "Recordatori predeterminat", - "time_format_12h": "12 hores", - "time_format_24h": "24 hores", - "notifications_enabled": "Notificacions d'esdeveniments", - "notifications_enabled_desc": "Mostra avisos per als esdeveniments propers del calendari", - "notification_sound": "So de notificació", - "notification_sound_desc": "Reprodueix un so per als avisos del calendari", - "invitation_parsing": "Analitza les invitacions per correu", - "invitation_parsing_desc": "Detecta les invitacions de calendari als fitxers adjunts del correu i mostra les accions del calendari", - "show_time_in_month_view": "Mostra l'hora a la vista mensual", - "show_time_in_month_view_desc": "Mostra l'hora dels esdeveniments a la vista mensual del calendari. En pantalles petites, això mostra entrades completes d'esdeveniment en lloc de punts.", - "show_week_numbers": "Mostra els números de setmana", - "show_week_numbers_desc": "Mostra els números de setmana al minicalendari", - "calendar_free_scroll": "Desplaçament lliure", - "calendar_free_scroll_desc": "Desplaça't de manera contínua per mesos, setmanes i dies en lloc d'un període cada vegada", - "enable_tasks": "Activa les tasques", - "enable_tasks_desc": "Mostra una vista de tasques al calendari per gestionar pendents", - "show_tasks_on_calendar": "Mostra les tasques al calendari", - "show_tasks_on_calendar_desc": "Mostra etiquetes de tasca a les vistes de dia i setmana del calendari", - "hover_preview": "Previsualització en passar el cursor", - "hover_preview_desc": "Mostra una finestra emergent de detalls en passar el cursor per sobre dels esdeveniments", - "hover_preview_instant": "Instantània", - "hover_preview_delay_500ms": "Retard de 0,5 segons", - "hover_preview_delay_1s": "Retard d'1 segon", - "hover_preview_delay_2s": "Retard de 2 segons", - "hover_preview_off": "Desactivada", - "organizer_identity": "Organitza les invitacions com a", - "organizer_identity_desc": "L'adreça de calendari usada com a organitzador quan convides gent als esdeveniments.", - "organizer_identity_failed": "No s'ha pogut canviar l'adreça de l'organitzador", - "show_birthday_calendar": "Calendari d'aniversaris dels contactes", - "show_birthday_calendar_desc": "Mostra un calendari virtual amb els aniversaris dels vostres contactes" - }, - "days": { - "monday": "Dilluns", - "tuesday": "Dimarts", - "wednesday": "Dimecres", - "thursday": "Dijous", - "friday": "Divendres", - "saturday": "Dissabte", - "sunday": "Diumenge", - "mon": "Dl", - "tue": "Dt", - "wed": "Dc", - "thu": "Dj", - "fri": "Dv", - "sat": "Ds", - "sun": "Dg" - }, - "months": { - "jan": "Gen", - "feb": "Feb", - "mar": "Mar", - "apr": "Abr", - "may": "Maig", - "jun": "Juny", - "jul": "Jul", - "aug": "Ag", - "sep": "Set", - "oct": "Oct", - "nov": "Nov", - "dec": "Des", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Esdeveniment creat", - "event_updated": "Esdeveniment actualitzat", - "event_deleted": "Esdeveniment suprimit", - "calendar_created": "Calendari creat", - "calendar_deleted": "Calendari suprimit", - "event_move_error": "No s'ha pogut moure l'esdeveniment", - "event_resize_error": "No s'ha pogut redimensionar l'esdeveniment", - "alert_title": "Esdeveniment proper", - "alert_now": "Comença ara", - "alert_in_minutes": "D'aquí a {count} min", - "invitation_sent": "Invitacions enviades", - "invitations_denied_title": "No es poden enviar les invitacions", - "invitations_denied": "El servidor ha refusat enviar les invitacions: {reason}. Voleu desar l'esdeveniment sense enviar-les?", - "save_without_invitations": "Desa sense invitacions", - "rsvp_updated": "Resposta actualitzada", - "rsvp_error": "No s'ha pogut actualitzar la resposta", - "event_duplicated": "Esdeveniment duplicat", - "event_error": "No s'ha pogut desar l'esdeveniment", - "task_due": "Tasca pendent", - "event_exported": "Esdeveniment exportat", - "title_copied": "Títol copiat", - "link_copied": "Enllaç copiat" - }, - "status": { - "loading_calendars": "Carregant els calendaris...", - "loading_events": "Carregant els esdeveniments..." - }, - "quick_create": { - "placeholder": "Títol del nou esdeveniment", - "aria_label": "Crea un esdeveniment ràpidament" - }, - "nav_prev": "Anterior", - "nav_next": "Següent", - "nav_open_menu": "Obre el menú", - "import": { - "title": "Importa un calendari", - "tab_file": "Fitxer", - "tab_url": "URL", - "select_file": "Selecciona un fitxer .ics", - "drop_file": "o deixeu anar el fitxer aquí", - "supported_formats": "Admet fitxers iCalendar (.ics)", - "url_description": "Introduïu l'URL d'un canal iCalendar (.ics) extern per importar-ne els esdeveniments.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Admet URL d’iCalendar (.ics)", - "fetch": "Obtén", - "invalid_url": "Introduïu un URL vàlid", - "url_fetch_failed": "No s'ha pogut obtenir el calendari des de l'URL", - "parsing": "Analitzant el fitxer del calendari...", - "parsed_events": "{count} esdeveniments trobats", - "no_events": "No s'ha trobat cap esdeveniment al fitxer", - "select_all": "Selecciona-ho tot", - "deselect_all": "Desselecciona-ho tot", - "target_calendar": "Importa al calendari", - "import_button": "Importa la selecció", - "importing": "Important els esdeveniments...", - "success": "{count} esdeveniments importats correctament", - "error": "No s'ha pogut importar el calendari", - "file_too_large": "El fitxer supera el límit de 10 MB", - "invalid_format": "Format de fitxer de calendari no vàlid" - }, - "webcal_action": { - "title": "Obre l'enllaç de calendari", - "description": "Com voleu utilitzar «{name}»?", - "import_title": "Importa una vegada", - "import_description": "Obté els esdeveniments ara i copia'ls a un dels vostres calendaris.", - "subscribe_title": "Subscriu-te", - "subscribe_description": "Mantén aquest calendari sincronitzat automàticament com a calendari separat.", - "cancel": "Cancel·la" - }, - "management": { - "title": "Gestió de calendaris", - "description": "Creeu, canvieu el nom i personalitzeu els calendaris. Feu clic dret a un calendari a la barra lateral per canviar-ne ràpidament el color.", - "name": "Nom", - "name_placeholder": "Nom del calendari", - "color": "Color", - "change_color": "Canvia el color", - "random_color": "Color aleatori nou", - "add_calendar": "Afegeix un calendari", - "edit": "Edita", - "delete": "Suprimeix", - "save": "Desa", - "create": "Crea", - "cancel": "Cancel·la", - "default": "Predeterminat", - "confirm_delete": "Voleu suprimir «{name}»? S'eliminaran tots els esdeveniments d'aquest calendari.", - "confirm_clear": "Voleu esborrar tots els esdeveniments de «{name}»? Això no es pot desfer.", - "set_default": "Estableix com a predeterminat", - "default_updated": "Calendari predeterminat actualitzat", - "error_default": "No s'ha pogut establir el calendari predeterminat", - "clear_events": "Esborra els esdeveniments", - "events_cleared": "{count} esdeveniments esborrats", - "error_clear": "No s'han pogut esborrar els esdeveniments del calendari", - "calendar_created": "Calendari creat", - "calendar_updated": "Calendari actualitzat", - "calendar_deleted": "Calendari suprimit", - "color_updated": "Color del calendari actualitzat", - "error_create": "No s'ha pogut crear el calendari", - "error_update": "No s'ha pogut actualitzar el calendari", - "error_delete": "No s'ha pogut suprimir el calendari", - "caldav_url": "URL de CalDAV", - "copy_url": "Copia l'URL de CalDAV", - "url_copied": "URL de CalDAV copiat al porta-retalls", - "share": "Comparteix el calendari", - "new_event_in_calendar": "Esdeveniment nou en aquest calendari", - "kind": "Contingut", - "kind_events": "Esdeveniments", - "kind_tasks": "Tasques", - "kind_both": "Esdeveniments i tasques", - "kind_hint": "Determina quines aplicacions veuen aquest calendari quan se sincronitza per CalDAV. No es pot canviar més endavant." - }, - "subscription": { - "title": "Subscripció iCal", - "section_title": "Subscripcions iCal", - "description": "Subscriviu-vos a un canal iCalendar extern. Els esdeveniments se sincronitzaran automàticament en el seu propi calendari. Admet URL https:// i webcal://.", - "url_label": "URL del calendari", - "url_placeholder": "https://example.com/calendar.ics or webcal://...", - "name_label": "Nom del calendari", - "name_placeholder": "p. ex. Festius", - "color_label": "Color", - "refresh_interval": "Interval d'actualització", - "interval_15": "Cada 15 minuts", - "interval_30": "Cada 30 minuts", - "interval_60": "Cada hora", - "interval_360": "Cada 6 hores", - "interval_1440": "Cada dia", - "subscribe": "Subscriu-te", - "subscribing": "Subscrivint...", - "save": "Desa els canvis", - "saving": "Desant...", - "edit": "Edita", - "edit_title": "Edita la subscripció", - "updated": "S'ha actualitzat «{name}»", - "update_error": "No s'ha pogut actualitzar la subscripció", - "invalid_url": "Introduïu un URL vàlid", - "success": "Subscrit a «{name}»", - "error": "No s'ha pogut afegir la subscripció", - "refresh": "Actualitza ara", - "refresh_success": "Subscripció actualitzada", - "refresh_error": "No s'ha pogut actualitzar la subscripció", - "unsubscribe": "Cancel·la la subscripció", - "confirm_delete": "Voleu cancel·lar la subscripció a «{name}»? S'eliminaran el calendari i tots els seus esdeveniments.", - "deleted": "Subscripció eliminada", - "delete_error": "No s'ha pogut eliminar la subscripció", - "last_refreshed": "Última actualització: {time}" - }, - "tasks": { - "label": "Tasques", - "no_tasks": "Cap tasca", - "no_title": "(Sense títol)", - "mark_complete": "Marca com a completada", - "mark_incomplete": "Marca com a incompleta", - "filter_all": "Totes", - "filter_pending": "Pendents", - "filter_completed": "Completades", - "filter_overdue": "Vençudes", - "show_completed": "Mostra les completades", - "create": "Tasca nova", - "edit": "Edita la tasca", - "title_placeholder": "Títol de la tasca", - "description_placeholder": "Afegiu una descripció...", - "due_date": "Data de venciment", - "include_time": "Inclou l'hora", - "priority": "Prioritat", - "priority_none": "Cap", - "priority_high": "Alta", - "priority_medium": "Mitjana", - "priority_low": "Baixa", - "progress": "Estat", - "progress_needs_action": "Cal actuar", - "progress_in_process": "En curs", - "progress_completed": "Completada", - "progress_cancelled": "Cancel·lada", - "calendar": "Calendari", - "alert": "Recordatori", - "alert_none": "Cap", - "alert_at_time": "A l'hora de venciment", - "alert_5min": "5 minuts abans", - "alert_15min": "15 minuts abans", - "alert_30min": "30 minuts abans", - "alert_1hr": "1 hora abans", - "alert_1day": "1 dia abans", - "delete": "Suprimeix", - "cancel": "Cancel·la", - "save": "Desa", - "quick_add_placeholder": "Afegiu una tasca...", - "due_today": "Avui", - "due_tomorrow": "Demà", - "overdue": "Vençuda" - } - }, - "calendar_event_notifications": { - "someone": "Algú", - "invited": "{name} t'ha convidat a \"{title}\"", - "updated": "{name} ha actualitzat \"{title}\"", - "cancelled": "{name} ha cancel·lat \"{title}\"", - "untitled": "Esdeveniment sense títol" - }, - "share_notifications": { - "someone": "Algú", - "shared": "{name} ha compartit amb tu {kind} \"{object}\"", - "changed": "{name} ha canviat el teu accés a {kind} \"{object}\"", - "revoked": "{name} ha retirat el teu accés a {kind} \"{object}\"", - "object": { - "folder": "la carpeta", - "calendar": "el calendari", - "address_book": "la llibreta d'adreces", - "files": "la carpeta de fitxers" - } - }, - "sharing": { - "title": "Comparteix «{name}»", - "description": "Concediu accés a altres usuaris o grups d'aquest servidor. Els canvis s'apliquen immediatament.", - "no_shares": "Encara no s'ha compartit amb ningú.", - "add_person": "Afegeix una persona o un grup", - "search_placeholder": "Cerca per nom o correu electrònic…", - "loading_principals": "Carregant els usuaris…", - "no_principals": "No s'ha trobat cap altre usuari o grup.", - "no_match": "Cap coincidència.", - "remove": "Elimina l'accés", - "group": "Grup", - "share_added": "Accés concedit", - "share_updated": "Accés actualitzat", - "share_removed": "Accés eliminat", - "share_failed": "No s'ha pogut actualitzar la compartició", - "preset": { - "freeBusy": "Només disponibilitat", - "read": "Només lectura", - "readWrite": "Lectura i escriptura", - "manager": "Gestor", - "custom": "Personalitzat" - } - }, - "advanced_search": { - "title": "Cerca avançada", - "from": "De", - "from_placeholder": "Nom o correu del remitent", - "to": "Per a", - "to_placeholder": "Nom o correu del destinatari", - "subject": "Assumpte", - "subject_placeholder": "L'assumpte conté...", - "body": "Cos", - "body_placeholder": "El cos conté...", - "folder": "Carpeta", - "all_folders": "Totes les carpetes", - "has_attachment": "Fitxers adjunts", - "date_after": "Després de", - "date_before": "Abans de", - "size_min": "Més gran que (KB)", - "size_max": "Més petit que (KB)", - "starred": "Destacat", - "unread": "No llegit", - "read": "Llegit", - "yes": "Sí", - "no": "No", - "clear": "Neteja", - "clear_all": "Neteja-ho tot", - "filters_active": "{count} filtre", - "filters_active_plural": "{count} filtres", - "toggle_filters": "Més", - "search_hint": "Utilitzeu els filtres avançats per a una cerca precisa", - "advanced_filters_tooltip": "Filtres de cerca avançada", - "results_found": "{count, plural, =0 {No s'ha trobat cap resultat} one {# resultat trobat} other {# resultats trobats}}", - "results_found_more": "{count}+ resultats trobats", - "suggestions_recent": "Cerques recents", - "suggestions_people": "Persones", - "suggestions_from": "Cerca correu de {address}", - "suggestions_to": "Cerca correu per a {address}", - "suggestions_remove_recent": "Elimina de les cerques recents" - }, - "welcome": { - "title": "Us donem la benvinguda a la bústia", - "tip_compose": "Premeu c per redactar un correu nou", - "tip_shortcuts": "Premeu ? per veure totes les dreceres de teclat", - "tip_sidebar": "Trobeu el Calendari, els Contactes i la Configuració al menú de la barra lateral", - "tip_settings": "Personalitzeu la vostra experiència a la Configuració", - "got_it": "Entesos", - "settings": "Configuració", - "dismiss": "Descarta", - "start_tour": "Comença la visita guiada" - }, - "demo_welcome": { - "title": "Us donem la benvinguda al Bulwark Mail", - "description": "Exploreu un client de correu web complet, directament al navegador. Totes les dades es queden al vostre dispositiu, així que proveu-ho tot sense cap problema.", - "feature_email": "Llegiu i redacteu correus", - "feature_organize": "Etiquetes, estrelles i carpetes", - "feature_shortcuts": "Dreceres de teclat", - "feature_privacy": "Demo 100% privada", - "hint": "Feu clic a qualsevol correu de l'esquerra per començar, o feu la visita guiada de sota." - }, - "files": { - "office_edit": "Edita el document", - "office_loading": "S'està obrint l'editor de documents…", - "office_error": "No s'ha pogut obrir l'editor de documents.", - "office_close": "Tanca l'editor", - "open_in_new_tab": "Obre en una pestanya nova", - "title": "Fitxers", - "search_placeholder": "Cerca fitxers...", - "empty_state_title": "Encara no hi ha fitxers", - "empty_state_description": "Pugeu fitxers o creeu carpetes per començar", - "upload": "Puja", - "upload_files": "Puja fitxers", - "new_folder": "Carpeta nova", - "new_folder_name": "Nom de la carpeta", - "rename": "Canvia el nom", - "rename_title": "Canvia el nom", - "new_name": "Nom nou", - "name_forbidden_chars": "El servidor no permet aquests caràcters als noms: {chars}", - "name_reserved": "Aquest nom està reservat al servidor.", - "delete": "Suprimeix", - "delete_confirm_title": "Suprimeix el recurs", - "delete_confirm_message": "Segur que voleu suprimir «{name}»? Això no es pot desfer.", - "download": "Baixa", - "name": "Nom", - "size": "Mida", - "modified": "Modificat", - "type": "Tipus", - "folder": "Carpeta", - "file": "Fitxer", - "parent_directory": "Directori superior", - "breadcrumb_root": "Inici", - "other_accounts": "Altres comptes", - "no_accounts": "Cap compte connectat.", - "drop_files_here": "Deixeu anar fitxers o carpetes aquí per pujar-los", - "uploading": "Pujant...", - "upload_success": "{count, plural, one {1 fitxer pujat} other {# fitxers pujats}}", - "upload_error": "No s'ha pogut pujar el fitxer", - "create_folder_success": "Carpeta creada", - "create_folder_error": "No s'ha pogut crear la carpeta", - "delete_success": "Suprimit correctament", - "delete_error": "No s'ha pogut suprimir", - "rename_success": "Nom canviat correctament", - "rename_error": "No s'ha pogut canviar el nom", - "download_error": "No s'ha pogut baixar", - "not_available": "L'emmagatzematge de fitxers no està disponible en aquest servidor", - "cancel": "Cancel·la", - "create": "Crea", - "save": "Desa", - "no_results": "Cap fitxer coincideix amb la cerca", - "batch_delete_confirm_message": "Segur que voleu suprimir {count, plural, one {1 element} other {# elements}}? Això no es pot desfer.", - "batch_delete_success": "{count, plural, one {1 element suprimit} other {# elements suprimits}}", - "grid_view": "Vista de quadrícula", - "list_view": "Vista de llista", - "details": "Detalls", - "path": "Camí", - "preview": "Previsualització", - "preview_error": "No s'ha pogut carregar la previsualització", - "cut": "Retalla", - "copy": "Copia", - "paste": "Enganxa", - "move_success": "{count, plural, one {1 element mogut} other {# elements moguts}}", - "move_error": "No s'ha pogut moure", - "paste_success": "Enganxat correctament", - "paste_error": "No s'ha pogut enganxar", - "new_text_file": "Fitxer de text nou", - "file_name": "Nom del fitxer", - "retry": "Reintenta", - "refresh": "Actualitza", - "toggle_favorite": "Commuta preferit", - "duplicate": "Duplica", - "duplicate_success": "Duplicat correctament", - "duplicate_error": "No s'ha pogut duplicar", - "share": "Comparteix", - "shared": "Compartit", - "shared_with_me": "Compartit amb mi", - "shared_by": "Compartit per {name}", - "create_file_success": "Fitxer creat", - "create_file_error": "No s'ha pogut crear el fitxer", - "favorites": "Preferits", - "recent": "Recents", - "properties": "Propietats", - "open_folder": "Obre la carpeta", - "upload_folder": "Puja una carpeta", - "file_too_large": "«{name}» supera la mida màxima de fitxer ({max})", - "undo": "Desfés", - "undo_success": "Acció desfeta", - "undo_error": "No s'ha pogut desfer", - "toolbar": "Accions de fitxer", - "open_folder_tree": "Obre l'arbre de carpetes", - "file_list": "Fitxers i carpetes", - "context_menu": "Accions", - "settings_title": "Configuració de fitxers", - "settings_display": "Visualització", - "settings_default_view": "Vista predeterminada", - "settings_default_view_desc": "Trieu entre disposició de quadrícula i de llista", - "settings_default_sort": "Ordenació predeterminada", - "settings_default_sort_desc": "Trieu l'ordenació predeterminada dels fitxers", - "settings_sort_direction": "Direcció de l'ordenació", - "settings_sort_direction_desc": "Trieu ordre ascendent o descendent", - "settings_ascending": "Ascendent", - "settings_descending": "Descendent", - "settings_icons": "Icones", - "settings_show_icons": "Mostra les icones de fitxer", - "settings_show_icons_desc": "Mostra icones al costat dels fitxers i les carpetes", - "settings_colored_icons": "Icones de colors", - "settings_colored_icons_desc": "Utilitza icones de colors en lloc de monocromes", - "settings_show_thumbnails": "Mostra les miniatures", - "settings_show_thumbnails_desc": "Mostra previsualitzacions d'imatge en lloc d'icones per als fitxers d'imatge", - "settings_behavior": "Comportament", - "settings_show_hidden": "Mostra els fitxers ocults", - "settings_show_hidden_desc": "Mostra els fitxers i les carpetes que comencen per un punt", - "settings_folder_layout": "Navegació de carpetes", - "settings_folder_layout_desc": "Trieu com es mostren les carpetes: incrustades amb els fitxers o en un arbre a la barra lateral", - "settings_folder_layout_inline": "Incrustades", - "settings_folder_layout_sidebar": "Barra lateral", - "disabled_title": "La funció de fitxers està desactivada per l'administrador", - "disabled_description": "Les pujades de fitxers grans via WebDAV poden causar inestabilitat a Stalwart/RocksDB, incloent-hi fallades per manca de memòria i ús de disc irrecuperable. És possible que els fitxers suprimits no s'eliminin immediatament de l'emmagatzematge de blobs. No es recomana aquesta funció per a entorns de producció.", - "stability_warning": "Les pujades de fitxers grans poden causar inestabilitat al servidor. És possible que els fitxers suprimits no s'eliminin immediatament de l'emmagatzematge. Utilitzeu-ho amb precaució.", - "migration_title": "Actualitzant els vostres fitxers…", - "migration_description": "S'estan organitzant les carpetes i els fitxers en la seva estructura adequada. Això només passa una vegada." - }, - "smime": { - "your_certificates": "Els vostres certificats", - "your_certificates_desc": "Importeu i gestioneu els certificats S/MIME per signar i xifrar correus", - "recipient_certificates": "Certificats de destinatari", - "recipient_certificates_desc": "Certificats públics per xifrar correus als destinataris", - "identity_bindings": "Vinculacions de clau a identitat", - "identity_bindings_desc": "Vinculeu certificats S/MIME a les vostres identitats de correu", - "defaults_title": "Valors per defecte", - "defaults_desc": "Configureu el comportament predeterminat de signatura i xifratge", - "import_pkcs12": "Importa PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importa un certificat", - "no_certificates": "Encara no s'ha importat cap certificat", - "no_recipient_certs": "Cap certificat de destinatari", - "expires": "Caduca", - "expired": "Caducat", - "bound_to": "Vinculat a", - "no_key_bound": "Cap", - "lock": "Bloqueja la clau", - "unlock": "Desbloqueja la clau", - "details": "Mostra els detalls", - "delete": "Suprimeix", - "encrypt_by_default": "Xifra per defecte", - "encrypt_by_default_desc": "Xifra automàticament els correus quan tots els destinataris tinguin certificat", - "remember_unlocked": "Recorda les claus desbloquejades", - "remember_unlocked_desc": "Manté les claus desbloquejades durant aquesta sessió del navegador", - "sign_default_for": "Signa per defecte per a", - "enter_p12_passphrase": "Introduïu la contrasenya del PKCS#12", - "p12_passphrase_desc": "Introduïu la contrasenya que protegeix aquest fitxer de certificat", - "enter_storage_passphrase": "Estableix la contrasenya d'emmagatzematge", - "storage_passphrase_desc": "Trieu una contrasenya per protegir aquesta clau en repòs al navegador", - "next": "Següent", - "import": "Importa", - "unlock_key": "Desbloqueja la clau", - "unlock_key_desc": "Introduïu la contrasenya d'emmagatzematge per desbloquejar aquesta clau per signar o desxifrar", - "passphrase_placeholder": "Introduïu la contrasenya", - "confirm_passphrase_placeholder": "Confirmeu la contrasenya", - "passphrase_mismatch": "Les contrasenyes no coincideixen", - "cancel": "Cancel·la", - "processing": "Processant…", - "close": "Tanca", - "certificate_details": "Detalls del certificat", - "cert_subject": "Subjecte", - "cert_issuer": "Emissor", - "cert_email": "Correu electrònic", - "cert_serial": "Número de sèrie", - "cert_validity": "Validesa", - "cert_fingerprint": "Empremta digital (SHA-256)", - "cert_algorithm": "Algorisme", - "cert_capabilities": "Capacitats", - "cert_source": "Origen", - "cert_expired": "Aquest certificat ha caducat", - "cert_not_yet_valid": "Aquest certificat encara no és vàlid", - "cap_sign": "Signatura", - "cap_encrypt": "Xifratge", - "cap_none": "Cap", - "show_passphrase": "Mostra la contrasenya", - "hide_passphrase": "Amaga la contrasenya", - "sign_toggle": "Signa", - "encrypt_toggle": "Xifra", - "missing_recipient_certs": "Falten certificats per a: {emails}", - "missing_sender_cert": "Cap certificat vinculat a aquesta identitat", - "status_encrypted_ok": "Aquest missatge s'ha xifrat", - "status_encrypted_no_key": "Aquest missatge està xifrat, però no s'ha trobat cap clau coincident", - "status_encrypted_failed": "No s'ha pogut desxifrar aquest missatge", - "status_signed_valid": "Signatura verificada", - "status_signed_invalid": "Ha fallat la verificació de la signatura", - "status_signed_expired_cert": "Signat amb un certificat caducat", - "status_signed_self_signed": "Signatura vàlida, però el certificat és autosignat (no fiable)", - "status_signed_mismatch": "Signatura vàlida, però el signant no coincideix amb el remitent", - "status_unsupported": "Format S/MIME no admès", - "auto_import_signer_certs": "Importa automàticament els certificats dels signants", - "auto_import_signer_certs_desc": "Desa automàticament els certificats dels correus signats verificats per a un xifratge futur", - "export": "Exporta", - "enter_export_passphrase": "Estableix la contrasenya d'exportació", - "export_passphrase_desc": "Trieu una contrasenya per protegir el fitxer PKCS#12 exportat", - "export_storage_desc": "Introduïu la contrasenya d'emmagatzematge per desxifrar la clau per exportar-la", - "incorrect_passphrase": "Contrasenya incorrecta" - }, - "tour": { - "step_counter": "Pas {current} de {total}", - "skip": "Omet la visita", - "back": "Enrere", - "next": "Següent", - "finish": "Finalitza", - "take_a_tour": "Feu una visita guiada de la interfície", - "restart_title": "Visita guiada introductòria", - "restart_desc": "Torneu a veure la visita guiada de la interfície", - "restart_button": "Reinicia la visita guiada", - "show_on_new_devices_title": "Mostra en dispositius nous", - "show_on_new_devices_desc": "Torna a mostrar el bàner de benvinguda i la visita guiada la primera vegada que inicieu la sessió en un dispositiu nou, encara que ja els hàgiu completat en un altre lloc", - "sidebar_title": "Les vostres bústies", - "sidebar_desc": "Aquesta és la barra lateral de carpetes. Feu clic a qualsevol bústia per veure'n els correus. Podeu crear carpetes, arrossegar-hi correus i veure d'un cop d'ull el nombre de no llegits.", - "compose_title": "Redacteu un correu", - "compose_desc": "Feu clic aquí per escriure un correu nou. Podeu afegir destinataris, fitxers adjunts i utilitzar format de text enriquit.", - "search_title": "Cerqueu al vostre correu", - "search_desc": "Cerqueu per remitent, assumpte o contingut. Feu clic a la icona de filtre per a opcions avançades com l'interval de dates, els fitxers adjunts i els missatges destacats.", - "email_list_title": "La vostra llista de correus", - "email_list_desc": "Els correus apareixen aquí. Feu-hi clic per llegir-los a la dreta. Utilitzeu la casella de selecció per seleccionar-ne diversos i moure'ls, suprimir-los o etiquetar-los en bloc.", - "email_viewer_title": "Subfinestra de lectura", - "email_viewer_desc": "El correu seleccionat s'obre aquí. Responeu, reenvieu, arxiveu o suprimiu amb els botons de la barra d'eines. També podeu marcar correus amb estrella o afegir-hi etiquetes de color.", - "keywords_title": "Etiquetes de color", - "keywords_desc": "Organitzeu el correu amb etiquetes de color. Arrossegueu un correu sobre una etiqueta per etiquetar-lo, o feu clic dret a un correu per assignar-hi etiquetes.", - "calendar_title": "Calendari", - "calendar_desc": "Canvieu al calendari per gestionar els vostres esdeveniments. Creeu esdeveniments, establiu recordatoris i visualitzeu-los per dia, setmana o mes.", - "contacts_title": "Contactes", - "contacts_desc": "Aquí hi teniu la llibreta d'adreces. Importeu contactes, creeu grups i feu clic a qualsevol contacte per veure'n tots els detalls.", - "settings_title": "Configuració", - "settings_desc": "Personalitzeu-ho tot: tema, densitat, signatures, filtres, dreceres de teclat, valors predeterminats del calendari i molt més.", - "shortcuts_title": "Dreceres de teclat", - "shortcuts_desc": "Als usuaris avançats els encanta. Premeu ? en qualsevol moment per veure totes les dreceres disponibles. Podeu navegar, redactar i gestionar correus sense tocar el ratolí.", - "compose_open_title": "El redactor", - "compose_open_desc": "Aquest és el redactor de correu. Afegiu destinataris, escriviu el missatge, adjunteu fitxers i utilitzeu format de text enriquit. També podeu desar esborranys i utilitzar plantilles.", - "calendar_view_title": "El vostre calendari", - "calendar_view_desc": "Aquí teniu el vostre calendari amb esdeveniments d'exemple. Podeu canviar entre les vistes de dia, setmana, mes i agenda amb la barra d'eines.", - "create_event_title": "Creeu un esdeveniment", - "create_event_desc": "Feu clic a aquest botó per crear un esdeveniment de calendari nou. Podeu establir un títol, una data, una hora i afegir-hi participants.", - "event_modal_title": "Detalls de l'esdeveniment", - "event_modal_desc": "Aquest és el formulari de l'esdeveniment. Ompliu el títol, trieu una data i hora, afegiu una ubicació o participants. Deseu-lo quan hàgiu acabat, o tanqueu-lo i continueu.", - "contacts_list_title": "Els vostres contactes", - "contacts_list_desc": "Aquí teniu els vostres contactes. Feu clic a qualsevol contacte per veure'n tots els detalls a la dreta. També podeu crear contactes nous, importar vCards o organitzar contactes en grups.", - "settings_tabs_title": "Menú de configuració", - "settings_tabs_desc": "Aquí teniu totes les categories de configuració. Personalitzeu l'aparença, gestioneu identitats, configureu filtres de correu, ajusteu el calendari i molt més.", - "files_title": "Emmagatzematge de fitxers", - "files_desc": "El navegador de fitxers us permet pujar, organitzar i compartir fitxers, com un disc al núvol personal integrat al correu.", - "demo_banner_title": "Controls de la demo", - "demo_banner_desc": "Esteu en mode de demostració: tot es queda al navegador. Premeu «Reinicia la demo» en qualsevol moment per començar de nou amb dades d'exemple netes.", - "quota_title": "Ús de l'emmagatzematge", - "quota_desc": "Feu un seguiment de la mida de la bústia aquí. El cercle s'omple a mesura que utilitzeu més espai." - }, - "unified_mailbox": { - "search_unavailable": "La cerca no està disponible a la vista unificada" - }, - "quote_header": { - "reply_line": "El {date}, {from} va escriure:", - "forwarded_separator": "---------- Missatge reenviat ----------", - "from_label": "De", - "date_label": "Data", - "subject_label": "Assumpte" - }, - "pwa_install": { - "title": "Instal·la {appName}", - "description": "Instal·leu la nostra aplicació per a un accés ràpid i compatibilitat sense connexió.", - "not_now": "Ara no", - "install": "Instal·la", - "dont_remind": "No m'ho tornis a recordar", - "dismiss_aria": "Descarta l'avís d'instal·lació" - }, - "deep_link": { - "message_not_found": "Aquest missatge ja no està disponible.", - "thread_not_found": "Aquesta conversa ja no està disponible.", - "folder_not_found": "Aquesta carpeta ja no està disponible.", - "event_not_found": "Aquest esdeveniment ja no està disponible.", - "file_not_found": "Aquest fitxer ja no està disponible.", - "account_unavailable": "Inicia la sessió en aquest compte per obrir l'enllaç.", - "copy_message": "Copia l'enllaç del missatge", - "copy_conversation": "Copia l'enllaç de la conversa", - "copy_event": "Copia l'enllaç de l'esdeveniment", - "copied": "Enllaç copiat", - "copy_failed": "No s'ha pogut copiar l'enllaç" - }, - "plugins": { - "oauth_callback": { - "completing": "Completant l'autorització…", - "completing_message": "Espereu mentre s'estableix la connexió.", - "received_title": "S'ha rebut l'autorització", - "received_message": "Podeu tornar a la pestanya on heu iniciat la connexió.", - "failed_title": "Ha fallat l'autorització", - "denied": "Sha denegat lautorització.", - "failed_detail": "Ha fallat lautorització: {error}", - "missing_params": "Falta el codi dautorització o lestat.", - "delivery_failed": "No sha pogut lliurar el resultat de lautorització a la pestanya de laplicació. Tanqueu aquesta finestra i torneu-ho a provar." - } - }, - "global_search": { - "title": "Cerca-ho tot", - "placeholder": "Cerca al correu, contactes, calendari, fitxers...", - "scope_label": "Cerca a", - "account_label": "Compte", - "scope_all": "Tot", - "scope_mail": "Correu", - "scope_contacts": "Contactes", - "scope_calendar": "Calendari", - "scope_files": "Fitxers", - "all_accounts": "Tots els comptes", - "show_all": "Mostra tots els resultats", - "no_results": "Cap resultat per a “{query}”", - "server_hint": "Les paraules completes cerquen al servidor; les parcials coincideixen amb els elements ja carregats", - "searching": "Cercant...", - "account_error": "No s'ha pogut cercar a {account}", - "recent_searches": "Cerques recents", - "recurring": "Periòdic", - "no_subject": "(no subject)", - "empty_hint": "Cerca correu, contactes, esdeveniments i fitxers a tots els comptes. Prova from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Pla per data", - "grouped": "Agrupat", - "load_more": "Carrega'n més", - "preview_empty": "Seleccioneu un resultat per previsualitzar-lo", - "open_full": "Obre", - "view_label": "Vista" - } -} +{ + "meta_description": "Client de correu web minimalista que utilitza el protocol JMAP", + "login": { + "title": "Webmail", + "username_label": "Correu electrònic", + "username_placeholder": "usuari@exemple.com", + "password_label": "Contrasenya", + "password_placeholder": "Introduïu la contrasenya", + "jmap_endpoint_label": "Servidor JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "El servidor ha de permetre sol·licituds CORS des d'aquest domini.", + "jmap_server_label": "Servidor", + "jmap_server_auto_picked": "Servidor seleccionat a partir del domini del vostre correu electrònic.", + "sign_in": "Inicia la sessió", + "signing_in": "Iniciant la sessió...", + "loading": "Carregant...", + "reconnecting": "S'ha perdut la connexió. S'està intentant reconnectar…", + "error": { + "invalid_credentials": "El correu electrònic o la contrasenya no són vàlids. Comproveu les credencials i torneu-ho a provar.", + "invalid_token": "El servidor no ha acceptat aquest token d'accés. Comproveu que sigui complet i que no hagi caducat ni s'hagi revocat.", + "connection_failed": "No s'ha pogut contactar amb el servidor. Comproveu la connexió a Internet i torneu-ho a provar.", + "cors_blocked": "El servidor és accessible, però està bloquejant les sol·licituds d'origen creuat. Comproveu la configuració CORS del servidor JMAP i permeteu aquest domini.", + "server_error": "El servidor no està disponible temporalment. Torneu-ho a provar més tard.", + "generic": "S'ha produït un error inesperat. Si el problema persisteix, contacteu amb l'administrador.", + "totp_required": "Cal un codi d'autenticació de dos factors. Introduïu-lo a continuació.", + "totp_invalid": "El codi d'autenticació no és vàlid. Comproveu l'aplicació d'autenticació i torneu-ho a provar.", + "oauth_discovery_failed": "El SSO està activat, però no s'ha pogut contactar amb el proveïdor d'identitat. Comproveu la configuració d'OAuth.", + "token_exchange_failed": "La contrasenya i el codi s'han acceptat, però el servidor de correu s'ha negat a iniciar una sessió per a aquesta aplicació. Demaneu a l'administrador que revisi la configuració del client OAuth." + }, + "show_password": "Mostra la contrasenya", + "hide_password": "Amaga la contrasenya", + "totp_toggle": "Tinc un codi 2FA", + "token_toggle": "Inicia la sessió amb un token d'accés", + "password_toggle": "Inicia la sessió amb una contrasenya", + "token_label": "Token d'accés", + "token_placeholder": "Enganxeu el vostre token d'API", + "token_hint": "Creeu un token d'API a la configuració del vostre proveïdor de correu i enganxeu-lo aquí.", + "remember_me": "Recorda'm", + "config_error": { + "title": "Error de configuració", + "fetch_failed": "No s'ha pogut carregar la configuració de l'aplicació. Torneu-ho a provar més tard.", + "server_not_configured": "El servidor de correu no s'ha configurat. Contacteu amb l'administrador." + }, + "remove_from_history": "Elimina de l'historial", + "totp_label": "Codi d'autenticació", + "totp_placeholder": "000000", + "session_expired": "La sessió ha caducat. Torneu a iniciar la sessió.", + "dismiss": "Descarta", + "or": "o", + "sign_in_sso": "Inicia la sessió amb SSO", + "add_account_title": "Afegeix un compte", + "add_account_subtitle": "Inicieu la sessió amb un altre compte", + "cancel": "Cancel·la", + "website": "Lloc web", + "imprint": "Avís legal", + "privacy_policy": "Política de privadesa", + "try_demo": "Prova la demo", + "demo_description": "Exploreu amb dades d'exemple - no cal cap compte", + "demo_launching": "Iniciant la demo...", + "demo_login_button": "Inicia la demo", + "demo_tagline": "Proveu un client de correu complet. No cal cap compte.", + "demo_no_signup": "No cal registrar-se - exploreu lliurement amb dades d'exemple", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Completant l'inici de sessió...", + "oauth_error": { + "title": "Ha fallat l'autenticació", + "invalid_state": "Ha fallat la validació de seguretat. Torneu a iniciar la sessió.", + "missing_params": "Falten dades d'autorització. Torneu a iniciar la sessió.", + "token_exchange_failed": "No s'ha pogut completar l'autenticació. Torneu-ho a provar.", + "access_denied": "S'ha denegat l'accés. Contacteu amb l'administrador.", + "back_to_login": "Torna a l'inici de sessió" + } + }, + "sidebar": { + "close": "Tanca", + "compose": "Redacta", + "compose_hint": "Redacta (c)", + "search_placeholder": "Cerca al correu...", + "search_placeholder_hint": "Cerca al correu... (premeu /)", + "storage": "Emmagatzematge", + "storage_used": "Utilitzat", + "storage_free": "Lliure", + "storage_total": "Total", + "sign_out": "Tanca la sessió", + "sign_out_of": "Tanca la sessió de {account}", + "sign_out_all": "Tanca la sessió de tots els comptes", + "add_account": "Afegeix un compte", + "set_as_default": "Estableix com a predeterminat", + "switch_account": "Canvia de compte", + "contacts": "Contactes", + "calendar": "Calendari", + "settings": "Configuració", + "admin": "Administració", + "files": "Fitxers", + "loading_mailboxes": "Carregant les bústies...", + "push_connected": "Actualitzacions en temps real actives", + "push_disconnected": "Actualitzacions en temps real inactives", + "keyboard_shortcuts": "Dreceres de teclat", + "theme": { + "light": "Mode clar", + "dark": "Mode fosc", + "system": "Tema del sistema" + }, + "language": { + "title": "Idioma" + }, + "mailboxes": { + "inbox": "Safata d'entrada", + "sent": "Enviats", + "drafts": "Esborranys", + "trash": "Paperera", + "archive": "Arxiu", + "starred": "Destacats", + "all_mail": "Tot el correu", + "spam": "Correu brossa", + "important": "Important" + }, + "unified_inbox": "Safata d'entrada unificada", + "unified_sent": "Tots els enviats", + "unified_drafts": "Tots els esborranys", + "unified_trash": "Tota la paperera", + "unified_archive": "Tot l'arxiu", + "unified_junk": "Tot el correu brossa", + "all_accounts": "Tots els comptes", + "all_accounts_loading": "Carregant comptes: {loaded} de {total}", + "unified_mailbox": "Bústia unificada", + "expand": "Desplega", + "collapse": "Replega", + "expand_tooltip": "Desplega", + "collapse_tooltip": "Replega", + "mobile": { + "search": "Cerca", + "compose": "Redacta", + "go_back": "Torna enrere", + "toggle_menu": "Commuta el menú" + }, + "clear_search": "Neteja la cerca", + "vacation_active": "La resposta automàtica està activa", + "demo_banner": "Mode de demostració", + "demo_reset": "Reinicia", + "demo_tour": "Visita guiada", + "tags": "Etiquetes", + "show_all_tags": "Mostra-ho tot ({count})", + "show_fewer_tags": "Mostra'n menys", + "folders": "Carpetes", + "your_folders": "Les teves carpetes", + "create_folder": "Crea una carpeta", + "show_more": "Més", + "show_fewer": "Menys", + "shared": "Compartit", + "shared_account_hidden": "Amagat de la barra lateral. Torna'l a mostrar a Configuració › Disposició.", + "notifications_muted": "Notificacions silenciades", + "mail": "Correu", + "nav_label": "Navegació", + "add_app": "Aplicacions", + "scheduled": "Programats", + "unified_all_unread": "Tots els no llegits", + "unified_all_starred": "Tots els destacats", + "unified_all_mail": "Tot el correu", + "remove_account": "Elimina el compte", + "remove_account_confirm": "Voleu eliminar {account} d'aquest dispositiu? El podreu tornar a afegir més endavant.", + "labels": "Etiquetes", + "create_label": "Crea una etiqueta nova", + "mv_tasks": "Tasques", + "mv_side_panel": "Tauler lateral", + "mv_open_full": "Obre a pantalla completa", + "mv_previous_day": "Dia anterior", + "mv_next_day": "Dia següent", + "mv_add_another_account": "Afegeix un altre compte", + "mv_manage_account": "Gestiona el teu compte", + "mv_storage_used": "{percent}% de {total} utilitzat" + }, + "protocol_handlers": { + "title": "Aplicacions predeterminades", + "description": "Trieu si els enllaços de correu i calendari s'obren al Bulwark. Tècnicament, el Bulwark es registra com a gestor de protocol per als enllaços mailto: i webcal:.", + "unsupported": "Aquest navegador o connexió no admet el registre manual de gestors de protocol. És possible que encara pugueu utilitzar la PWA instal·lada des de la configuració del navegador o del sistema operatiu.", + "mailto_label": "Enllaços de correu", + "mailto_description": "Obre els enllaços mailto: al Bulwark amb el redactor emplenat prèviament.", + "protocol_open_mode_label": "En obrir enllaços de protocol", + "protocol_open_mode_description": "Trieu si el Bulwark obre els enllaços mailto: i webcal: en una pestanya nova o reutilitza una sessió oberta. L'opció de sessió activa necessita permís de notificacions perquè pugueu prémer una notificació alternativa per portar el Bulwark al davant si el navegador bloqueja el focus.", + "protocol_open_mode_active_session": "Obre en la sessió activa si és possible", + "protocol_open_mode_new_tab": "Obre sempre una pestanya nova", + "focus_notification_title": "Obre el Bulwark", + "focus_notification_body": "L'enllaç s'ha obert al Bulwark. Feu clic per portar la finestra al davant.", + "webcal_label": "Enllaços de calendari", + "webcal_description": "Obre els enllaços webcal: al Bulwark amb el diàleg de subscripció al calendari emplenat prèviament.", + "register_mailto": "Registra com a aplicació de correu", + "register_webcal": "Registra com a aplicació de calendari", + "mailto_registered": "S'ha sol·licitat el registre del gestor de correu", + "webcal_registered": "S'ha sol·licitat el registre del gestor de calendari", + "registration_failed": "No s'ha pogut registrar el gestor de protocol", + "opening_mailto": "Obrint el redactor...", + "opening_webcal": "Obrint el calendari...", + "browser_note": "És possible que el navegador o el sistema operatiu us demani confirmar-ho, i que calgui tenir el Bulwark instal·lat per poder seleccionar-lo com a aplicació predeterminada.", + "select_account_title": "Trieu un compte", + "select_mailto_account": "Trieu quin compte ha d'obrir aquest enllaç de correu.", + "select_webcal_account": "Trieu quin compte ha d'obrir aquest enllaç de calendari.", + "select_account_note": "Això només s'aplica a aquest enllaç de protocol.", + "detail_to": "Per a", + "detail_subject": "Assumpte", + "detail_no_subject": "Sense assumpte", + "detail_calendar": "Calendari", + "detail_source": "Origen", + "active_account": "Actiu", + "switching_account": "Canviant de compte..." + }, + "sidebar_apps": { + "modal_title": "Aplicacions de la barra lateral", + "add_new": "Afegeix una aplicació", + "edit_app": "Edita l'aplicació", + "name_label": "Nom", + "name_placeholder": "La meva aplicació", + "name_required": "El nom és obligatori", + "url_label": "URL", + "url_required": "L'URL és obligatori", + "url_invalid": "Introduïu un URL http o https vàlid", + "icon_label": "Icona", + "icon_required": "La icona és obligatòria", + "open_mode_label": "Mode d'obertura", + "open_new_tab": "Pestanya nova", + "open_inline": "Incrustat", + "cancel": "Cancel·la", + "add": "Afegeix", + "update": "Actualitza", + "delete": "Suprimeix", + "delete_confirm_title": "Suprimeix l'aplicació", + "delete_confirm": "Segur que voleu suprimir «{name}»?", + "no_apps": "Encara no s'ha afegit cap aplicació", + "no_apps_hint": "Afegiu aplicacions i enllaços personalitzats a la barra lateral", + "managed_title": "Proporcionades per l'administració", + "managed_description": "Aquestes aplicacions estan configurades per a tothom i no es poden canviar.", + "managed_badge": "Gestionada", + "search_icons": "Cerca icones...", + "show_popular": "Populars", + "show_all": "Totes", + "no_icons_found": "No s'ha trobat cap icona", + "inline_badge": "Incrustat", + "tab_badge": "Pestanya", + "show_on_mobile": "Mostra al mòbil" + }, + "email_list": { + "columns": { + "from": "De", + "to": "Per a", + "subject": "Assumpte", + "date": "Data", + "size": "Mida" + }, + "no_emails": "No s'ha trobat cap missatge", + "no_emails_description": "Aquesta bústia és buida", + "no_search_results": "No s'ha trobat cap resultat", + "no_search_results_description": "Proveu d'ajustar la cerca o els filtres", + "loading": "Carregant els correus...", + "unread": "no llegit", + "to_me": "Per a mi", + "to_recipients": "Per a {count} destinataris", + "and_others": "i {count} més", + "draft": "Esborrany", + "starred": "Destacat", + "pinned": "Fixat", + "replied": "Respost", + "forwarded": "Reenviat", + "has_attachment": "Té adjunt", + "conversations_range": "{from}–{to, number} de {total, number}", + "conversations_count": "{count} de {total} converses", + "conversations_count_plus": "{count}+ converses", + "conversations_count_simple": "{count} converses", + "no_conversations": "Cap conversa", + "loading_more": "Carregant més correus...", + "no_more_emails": "No hi ha més correus per carregar", + "batch_actions": { + "select": "Selecciona correus", + "select_all": "Selecciona-ho tot", + "selected_messages": "{count, plural, one {1 correu seleccionat} other {# correus seleccionats}}", + "mark_read": "Marca com a llegit", + "mark_unread": "Marca com a no llegit", + "delete": "Suprimeix", + "delete_confirm_title": "Suprimeix els correus", + "delete_confirm_message": "Segur que voleu suprimir {count, plural, one {1 correu} other {# correus}}?", + "clear_selection": "Neteja la selecció" + }, + "permanent_delete": "Suprimeix definitivament", + "permanent_delete_confirm_title": "Suprimeix definitivament", + "permanent_delete_confirm_message": "Aquest correu se suprimirà definitivament. Aquesta acció no es pot desfer.", + "permanent_delete_confirm_batch_message": "Aquests {count, plural, one {1 correu} other {# correus}} se suprimiran definitivament. Aquesta acció no es pot desfer.", + "empty_folder": { + "button": "Buida la carpeta", + "confirm_title": "Buida la carpeta", + "confirm_message": "Tots els correus d'aquesta carpeta se suprimiran definitivament. Aquesta acció no es pot desfer.", + "confirm_message_trash": "Tots els correus d'aquesta carpeta es mouran a la paperera.", + "confirm_button": "Buida la carpeta", + "junk_hint": "Podeu buidar la carpeta de correu brossa per eliminar definitivament tots els missatges.", + "trash_hint": "Podeu buidar la paperera per eliminar definitivament tots els missatges." + }, + "no_scheduled_emails": "Cap correu programat", + "no_scheduled_emails_description": "Els missatges programats per a més endavant apareixeran aquí.", + "scheduled_count": "{count, plural, one {1 correu programat} other {# correus programats}}", + "scheduled_actions_hint": "Els missatges programats es poden cancel·lar, reprogramar o editar des de les seves accions programades.", + "cancel_scheduled_send": "Cancel·la l'enviament", + "reschedule_send": "Reprograma", + "cancel_and_edit": "Cancel·la i edita", + "cancel_and_compose_again": "Cancel·la i torna a redactar", + "reschedule_prompt": "Introduïu una data i hora noves, p. ex. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "El remitent ha demanat rebre una notificació quan obriu aquest missatge.", + "send": "Envia la confirmació", + "ignore": "Ignora", + "sent": "S'ha enviat la confirmació de lectura.", + "send_failed": "No s'ha pogut enviar la confirmació de lectura", + "mdn_subject": "Llegit: {subject}", + "mdn_body": "Aquesta és una confirmació de recepció del missatge que heu enviat a {recipient}.\n\nNota: aquesta confirmació només indica que el missatge s'ha mostrat a l'ordinador del destinatari. No hi ha cap garantia que el destinatari hagi llegit o entès el contingut del missatge." + }, + "no_email_selected": "Cap correu seleccionat", + "no_email_description": "Seleccioneu un correu de la llista per visualitzar-lo aquí", + "no_conversation_selected": "Cap conversa seleccionada", + "no_conversation_description": "Trieu una conversa de la llista per llegir-la aquí", + "compose": "Redacta", + "compose_hint": "Redacta un missatge nou", + "no_subject": "(Sense assumpte)", + "no_body_content": "(No hi ha contingut disponible)", + "no_preview_available": "No hi ha cap previsualització disponible", + "show_quoted_text": "Mostra el text citat", + "hide_quoted_text": "Amaga el text citat", + "loading_email": "Carregant el correu...", + "loading": "Carregant...", + "reply": "Respon", + "reply_all": "Respon a tots", + "forward": "Reenvia", + "delete": "Suprimeix", + "archive": "Arxiva", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Marca amb estrella", + "unstar": "Treu l'estrella", + "mark_unread": "Marca com a no llegit", + "mark_read": "Marca com a llegit", + "unread": "No llegit", + "read": "Llegit", + "spam_short": "Brossa", + "not_spam_short": "No és brossa", + "move": "Mou", + "print": "Imprimeix", + "view_source": "Mostra el codi font", + "fullscreen": "Pantalla completa", + "exit_fullscreen": "Surt de la pantalla completa", + "view_in_dark_mode": "Veure en mode fosc", + "view_in_light_mode": "Veure en mode clar", + "export_email": "Exporta com a .eml", + "forward_as_attachment": "Reenvia com a fitxer adjunt", + "import_email": "Importa .eml o .zip", + "keyboard_shortcuts": "Dreceres de teclat (?)", + "email_source": "Codi font del correu", + "draft_banner": "Aquest missatge és un esborrany", + "edit_draft": "Edita", + "copy_source": "Copia al porta-retalls", + "source_copied": "S'ha copiat el codi font al porta-retalls", + "attachments": "Fitxers adjunts", + "important": "Important", + "download": "Baixa", + "download_all": "Baixa-ho tot", + "from": "De", + "to": "Per a", + "cc": "CC", + "bcc": "CCO", + "date": "Data", + "subject": "Assumpte", + "show_details": "Mostra els detalls", + "hide_details": "Amaga els detalls", + "external_content_warning": "S'han bloquejat les imatges i el contingut extern", + "load_external_content": "Carrega les imatges", + "trust_sender": "Confia sempre en aquest remitent", + "sender_check": { + "unverified_label": "Remitent no verificat", + "failed_label": "La verificació del remitent ha fallat", + "unverified": "Aquest missatge diu que és de {domain}, però res no ho confirma. No té cap signatura vàlida de {domain} i no prové de cap servidor autoritzat per {domain}.", + "unverified_sent_from": "Aquest missatge diu que és de {domain}, però s'ha enviat des de {host} i no té cap signatura vàlida de {domain}.", + "failed": "Aquest missatge diu que és de {domain}, però no ha superat les comprovacions de remitent d'aquest domini. Podria estar falsificat.", + "failed_sent_from": "Aquest missatge diu que és de {domain}, però s'ha enviat des de {host} i no ha superat les comprovacions de remitent de {domain}. Podria estar falsificat.", + "caution": "Aneu amb compte amb els enllaços, els fitxers adjunts i les sol·licituds de contrasenyes o dades de pagament." + }, + "back_to_list": "Torna a la llista", + "view_contact": "Mostra el contacte", + "message_details": "Detalls del missatge", + "more_reply_options": "Més opcions de resposta", + "set_tag": "Estableix l'etiqueta", + "tag": "Etiqueta", + "more_actions": "Més accions", + "previous": "Anterior", + "next": "Següent", + "move_to": "Mou a...", + "remove_tag": "Elimina l'etiqueta", + "tag_filter_placeholder": "Filtra les etiquetes", + "tag_no_matches": "Cap etiqueta coincident", + "more_count": "+{count} més", + "characters_count": "{count} caràcters", + "quick_reply_placeholder": "Escriviu una resposta ràpida...", + "more_options": "Més opcions", + "sending": "Enviant...", + "security_authentication": "Seguretat i autenticació", + "technical_details": "Detalls tècnics", + "message_id_label": "ID del missatge:", + "reply_to_label": "Respon a:", + "delivery_time_label": "Hora de lliurament:", + "conversation_part_label": "Part de la conversa:", + "previous_messages": "{count} missatge anterior", + "previous_messages_plural": "{count} missatges anteriors", + "time": { + "day": "dia", + "days": "dies", + "hour": "hora", + "hours": "hores", + "minute": "minut", + "minutes": "minuts" + }, + "unknown_sender": "Desconegut", + "recipient_me": "jo", + "recipient_and_others": "{name} i {count} més", + "recipient_to_prefix": "Per a:", + "authentication": { + "title": "Autenticació", + "status": { + "verified": "Verificat", + "warning": "Advertència", + "none": "No autenticat" + }, + "spf": { + "pass": "SPF correcte", + "fail": "SPF incorrecte", + "none": "Sense SPF" + }, + "dkim": { + "pass": "DKIM vàlid", + "fail": "DKIM no vàlid", + "none": "Sense DKIM" + }, + "dmarc": { + "pass": "DMARC correcte", + "fail": "DMARC incorrecte", + "none": "Sense DMARC" + }, + "spam_score": "Puntuació de brossa", + "tooltip_spf": "Sender Policy Framework: verifica que el servidor remitent estigui autoritzat a enviar correu en nom del domini", + "tooltip_dkim": "DomainKeys Identified Mail: confirma que el correu no s'ha alterat durant el trànsit mitjançant una signatura criptogràfica", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: assegura que l'SPF i el DKIM coincideixin amb el domini del remitent i estableix una política per als errors", + "policy": "Política", + "result": { + "pass": "Correcte", + "fail": "Incorrecte", + "softfail": "Error lleu", + "neutral": "Neutre", + "permerror": "Error permanent", + "temperror": "Error temporal", + "none": "Cap" + } + }, + "details": { + "recipients_routing": "Destinataris i encaminament", + "authentication_security": "Autenticació i seguretat", + "identifiers_threading": "Identificadors i fils", + "mailing_list": "Llista de correu", + "message_properties": "Propietats del missatge", + "sent": "Enviat", + "received": "Rebut", + "delivery_time": "Hora de lliurament", + "in_reply_to": "In-Reply-To", + "references": "References", + "thread_id": "ID del fil", + "size": "Mida", + "mime_type": "Tipus MIME", + "attachments_summary": "{count} fitxers · {size}", + "list_id": "ID de la llista", + "list_help": "Ajuda de la llista", + "list_post": "Publicació a la llista", + "list_unsubscribe": "Cancel·la la subscripció", + "iprev": "DNS invers", + "spam_status": "Estat de brossa", + "ai_verdict": "Veredicte de la IA", + "account": "Compte", + "no_subject": "(sense assumpte)" + }, + "headers": { + "routing": "Encaminament", + "received": "Received", + "message_id": "ID del missatge", + "list_info": "Informació de la llista" + }, + "tooltips": { + "reply": "Respon (r)", + "reply_all": "Respon a tots (a)", + "forward": "Reenvia (f)", + "archive": "Arxiva (e)", + "delete": "Suprimeix (# o Supr)", + "star": "Marca amb estrella (s)", + "unstar": "Treu l'estrella (s)", + "compose": "Redacta (c)", + "previous": "Correu anterior", + "next": "Correu següent", + "edit_draft": "Edita l'esborrany" + }, + "spam": { + "button_title": "Denuncia com a brossa", + "not_spam_title": "Marca com a legítim", + "toast_success": "S'ha mogut a la brossa", + "toast_batch": "{count} correus moguts a la brossa", + "toast_undo": "Desfés", + "toast_not_spam_success": "S'ha mogut a la safata d'entrada", + "toast_not_spam_batch": "{count} correus moguts a la safata d'entrada", + "error": "No s'ha pogut denunciar com a brossa", + "error_not_spam": "No s'ha pogut restaurar el correu" + }, + "unsubscribe_banner": { + "label": "Butlletí", + "button": "Cancel·la la subscripció", + "confirm_title": "Voleu cancel·lar la subscripció d'aquest remitent?", + "confirm_button": "Confirma", + "cancel": "Cancel·la", + "success_http": "S'ha obert la pàgina de cancel·lació en una pestanya nova", + "success_mailto": "S'ha enviat el correu de cancel·lació", + "confirm_message_http": "La pàgina de cancel·lació s'obrirà en una pestanya nova.", + "confirm_message_mailto": "S'enviarà un correu de cancel·lació al remitent.", + "error": "No s'ha pogut cancel·lar la subscripció", + "dismiss": "Descarta" + }, + "calendar_invitation": { + "loading": "Carregant els detalls de l'esdeveniment…", + "title": "Invitació de calendari", + "published_title": "Esdeveniment publicat", + "response_title": "Resposta a l'esdeveniment", + "update_title": "Actualització de l'esdeveniment", + "counter_title": "Contraproposta", + "refresh_title": "Sol·licitud d'actualització", + "declined_counter_title": "Contraproposta rebutjada", + "cancelled_title": "Esdeveniment cancel·lat", + "organizer": "Organitzat per {name}", + "organizer_label": "Organitzat per", + "attendees": "{count, plural, one {# assistent} other {# assistents}}", + "accept": "Accepta", + "maybe": "Potser", + "decline": "Rebutja", + "add_to_calendar": "Afegeix al calendari", + "added": "S'ha afegit al calendari", + "rsvp_sent": "Resposta enviada", + "parse_error": "No s'ha pogut llegir la invitació", + "action_failed": "No s'ha pogut completar aquesta acció del calendari.", + "no_calendar": "Calendari no disponible", + "published_info": "Aquest esdeveniment s'ha compartit com a referència.", + "response_info": "Aquest missatge conté la resposta d'un assistent.", + "response_info_organizer": "Aquesta resposta de l'assistent actualitza el vostre esdeveniment.", + "update_info": "Aquest missatge actualitza un esdeveniment existent.", + "counter_info": "Aquest missatge proposa canvis a un esdeveniment.", + "counter_info_organizer": "Aquest assistent ha proposat canvis al vostre esdeveniment.", + "refresh_info": "Aquest missatge sol·licita els detalls més recents de l'esdeveniment.", + "refresh_info_organizer": "Un assistent ha sol·licitat els detalls més recents de l'esdeveniment.", + "declined_counter_info": "L'organitzador ha rebutjat una contraproposta.", + "authentication_failed_info": "Les comprovacions d'autenticació del correu d'aquesta invitació han fallat. Tracteu les accions del calendari amb precaució.", + "authentication_missing_info": "Aquesta invitació no inclou autenticació de correu verificada. Confirmeu els detalls amb l'organitzador si alguna cosa sembla estranya.", + "sender_mismatch_info": "Aquesta invitació s'ha enviat des de {sender}, mentre que l'organitzador indicat a les dades del calendari és {organizer}.", + "sender_mismatch_unverified_info": "Aquesta invitació s'ha enviat des de {sender}, mentre que l'organitzador indicat a les dades del calendari és {organizer}, i no s'ha pogut verificar el missatge.", + "organizer_role": "Vós organitzeu aquest esdeveniment", + "your_response": "La vostra resposta: {status}", + "response_needed": "Cal resposta", + "response_accepted": "Acceptat", + "response_tentative": "Provisional", + "response_declined": "Rebutjat", + "response_delegated": "Delegat", + "actor_sent_info": "Enviat per {name}.", + "actor_response_info": "{name} ha respost {status}.", + "actor_counter_info": "{name} ha proposat canvis a aquest esdeveniment.", + "actor_refresh_info": "{name} ha demanat els detalls més recents de l'esdeveniment.", + "actor_declined_counter_info": "{name} ha rebutjat la contraproposta.", + "actor_note": "Nota: {comment}", + "actor_unknown": "Algú", + "proposed_changes": "Canvis proposats", + "change_title": "Títol", + "change_time": "Hora", + "change_location": "Ubicació", + "change_description": "Descripció", + "change_empty": "Cap", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Aplica els canvis proposats", + "proposal_applied": "S'han aplicat els canvis proposats.", + "review_proposal": "Revisa la proposta", + "review_request": "Revisa la sol·licitud", + "view_in_calendar": "Mostra al calendari", + "select_calendar": "Selecciona un calendari", + "already_in_calendar": "Ja és al vostre calendari", + "request_info": "Heu estat convidat a aquest esdeveniment. Responeu per informar l'organitzador de la vostra disponibilitat.", + "cancel_info": "L'organitzador ha cancel·lat aquest esdeveniment.", + "event_updated": "Actualització núm. {sequence}", + "event_status_tentative": "Provisional", + "event_status_cancelled": "Cancel·lat", + "expand": "Mostra els detalls", + "collapse": "Amaga els detalls" + }, + "send": "Envia", + "more": "més", + "scheduled_banner": "Programat per enviar-se el {date}", + "scheduled_send_created": "Correu programat per enviar-se", + "sending_in": "S'enviarà d'aquí a {seconds} s", + "undo_send_failed": "No s'ha pogut desfer. Potser el missatge ja s'ha enviat.", + "cancel_scheduled_send": "Cancel·la l'enviament", + "reschedule_send": "Reprograma", + "cancel_and_edit": "Cancel·la i edita", + "cancel_and_compose_again": "Cancel·la i torna a redactar", + "reschedule_prompt": "Introduïu una data i hora noves, p. ex. 2026-05-04T15:30", + "scheduled_actions_only": "La vista de programats només admet accions de programació", + "undo_send_scheduled": "Missatge programat per enviar-se", + "undo_send": "Desfés l'enviament", + "contact_sidebar": { + "title": "Contacte", + "close": "Tanca la barra lateral", + "action_email": "Correu electrònic", + "action_email_title": "Envia un correu", + "action_copy": "Copia", + "action_copy_title": "Copia el correu electrònic", + "action_edit_title": "Edita el contacte", + "section_emails": "Correus electrònics", + "section_phones": "Telèfons", + "section_organizations": "Organitzacions", + "section_addresses": "Adreces", + "section_notes": "Notes", + "not_in_contacts": "No és als vostres contactes", + "add_to_contacts": "Afegeix als contactes", + "copied": "Copiat!", + "copy_failed": "No s'ha pogut copiar", + "view_contact": "Mostra el contacte", + "view_details": "Mostra els detalls" + }, + "send_now": "Envia ara", + "verification_code": { + "copy": "Copia el codi {code}", + "copied": "Codi copiat", + "copy_failed": "No s'ha pogut copiar el codi" + }, + "mv_position": "{index} de {total}", + "plugin_panel_show": "Mostra el tauler del connector", + "plugin_panel_hide": "Amaga el tauler del connector" + }, + "email_composer": { + "select_from_directory": "Selecciona del directori", + "minimize": "Minimitza", + "maximize": "Pantalla completa", + "restore": "Restaura", + "new_message": "Missatge nou", + "signature_edit_hint": "Feu doble clic per editar la signatura", + "reply": "Respon", + "reply_all": "Respon a tots", + "forward": "Reenvia", + "reply_to": "Respon", + "reply_all_to": "Respon a tots", + "forward_message": "Reenvia", + "from": "De", + "to": "Per a", + "cc": "CC", + "bcc": "CCO", + "subject": "Assumpte", + "body_placeholder": "Escriviu el vostre missatge...", + "send": "Envia", + "cancel": "Cancel·la", + "attach": "Adjunta", + "attach_photos": "Fotos i vídeos", + "attach_files": "Fitxers", + "attach_from_files": "Adjunta des de Fitxers", + "files_picker": { + "attach": "{count, plural, =0 {Adjunta} one {Adjunta 1 fitxer} other {Adjunta # fitxers}}", + "empty_folder": "Aquesta carpeta és buida", + "load_failed": "No s'han pogut carregar els teus fitxers" + }, + "discard": "Descarta", + "discard_draft_title": "Voleu descartar l'esborrany?", + "discard_draft_confirm": "Teniu canvis sense desar. Voleu descartar aquest esborrany?", + "discard_draft_message": "S'eliminarà l'esborrany.", + "saving": "Desant...", + "sending": "Enviant...", + "add_link": "Afegeix un enllaç", + "link_url_prompt": "Introduïu l'URL", + "draft_saved": "S'ha desat l'esborrany", + "save_failed": "No s'ha pogut desar", + "to_placeholder": "Adreces electròniques dels destinataris", + "cc_placeholder": "Destinataris amb CC", + "bcc_placeholder": "Destinataris amb CCO", + "subject_placeholder": "Assumpte", + "cc_label": "CC:", + "bcc_label": "CCO:", + "subject_label": "Assumpte:", + "file_size_kb": "KB", + "prefix": { + "forward": "Reenv.:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 correu} other {# correus}}", + "no_subject": "(Sense assumpte)", + "unknown_sender": "Desconegut", + "quote": { + "reply_header": "El {date}, {sender} va escriure:", + "forward_header": "---------- Missatge reenviat ----------", + "from": "De: {sender}", + "date": "Data: {date}", + "subject": "Assumpte: {subject}", + "to": "Per a: {recipients}" + }, + "remove_sub_address": "Elimina el subadreçament", + "from_override": { + "toggle_off": "Sobreescriu", + "toggle_on": "Cancel·la la sobreescriptura", + "toggle_tooltip": "Editeu lliurement el nom i l'adreça del remitent. El correu es continua enviant a través de la vostra identitat.", + "name_label": "Nom del remitent", + "name_placeholder": "Nom", + "email_label": "Adreça electrònica del remitent", + "email_placeholder": "alias@exemple.com", + "envelope_notice": "Si el vostre servidor no accepta aquesta adreça com a remitent del sobre, s'hi fa servir {identity}, i els destinataris la poden veure a la capçalera Return-Path." + }, + "save_as_template": "Desa com a plantilla", + "attach_from_device": "Puja des d'aquest dispositiu", + "templates_menu": "Plantilles", + "insert_template": "Insereix una plantilla", + "options": "Opcions", + "option_plain_text": "Text pla", + "option_plain_text_hint": "Envia sense format", + "option_read_receipt": "Demana confirmació de lectura", + "option_read_receipt_hint": "Demana al destinatari que confirmi que l'ha obert", + "option_dsn": "Notificació de lliurament", + "option_dsn_hint": "Rep un informe quan el servidor el lliuri", + "option_require_tls": "Exigeix lliurament xifrat", + "option_require_tls_hint": "Lliura només per TLS; si no, retorna'l", + "option_priority": "Prioritat", + "option_priority_high": "Alta", + "option_priority_normal": "Normal", + "option_priority_low": "Baixa", + "validation": { + "recipient_required": "Afegiu un destinatari per enviar", + "body_required": "Escriviu un missatge o adjunteu un fitxer", + "invalid_recipients": "No s'ha enviat - comprova aquestes adreces: {addresses}", + "attachments_uploading": "Els fitxers adjunts encara s'estan pujant — s'enviarà en acabar", + "attachment_upload_failed": "No s'ha enviat — no s'ha pogut pujar un fitxer adjunt. Elimineu-lo i torneu-ho a provar." + }, + "upload_progress": "Pujant {uploaded} / {total}", + "upload_cancel": "Cancel·la la pujada", + "upload_failed": "No s'ha pogut pujar {filename}", + "attachment_too_large": "«{name}» supera la mida que permet el servidor ({max} per fitxer)", + "attachments_total_too_large": "Els adjunts superarien el límit del servidor de {max} per missatge", + "drop_files": "Deixeu anar els fitxers per adjuntar-los", + "show_less": "Mostra'n menys", + "send_failed": "No s'ha pogut enviar el correu", + "send_timeout": "El servidor ha deixat de respondre. Comproveu la carpeta Enviats abans de tornar a enviar: el missatge pot haver sortit.", + "send_offline": "Sembla que no tens connexió. El missatge continua aquí; torna a enviar-lo quan tornis a estar en línia.", + "formatting": "Format", + "send_recipients_rejected": "No s'ha enviat: el servidor ha rebutjat tots els destinataris.", + "send_plugin_failed": "No s'ha enviat: un connector que gestiona l'enviament ha fallat o ha trigat massa. Comprova la carpeta Enviats abans de tornar-ho a enviar.", + "send_some_recipients_rejected": "S'ha enviat, però no a aquests destinataris: el servidor els ha rebutjat.", + "continue_draft": "Continua l'esborrany", + "close_draft_title": "Voleu desar o descartar l'esborrany?", + "close_draft_message": "Teniu canvis sense desar. Voleu desar-los com a esborrany o descartar-los?", + "smime_sign_on": "Signatura S/MIME activada", + "smime_sign_off": "Activa la signatura S/MIME", + "smime_encrypt_on": "Xifratge S/MIME activat", + "smime_encrypt_off": "Activa el xifratge S/MIME", + "smime_encrypt_unavailable": "Xifratge S/MIME no disponible – falten certificats del destinatari", + "smime_unlock_title": "Desbloqueja la clau S/MIME", + "smime_unlock_message": "Introduïu la contrasenya per desbloquejar la vostra clau de signatura S/MIME.", + "smime_unlock_button": "Desbloqueja", + "smime_passphrase_placeholder": "Contrasenya", + "forgot_attachment": { + "title": "Us heu oblidat d'un fitxer adjunt?", + "message": "El vostre missatge esmenta «{keyword}», però no hi ha cap fitxer adjunt. Voleu enviar-lo igualment?", + "send_anyway": "Envia igualment", + "back": "Torna a l'edició" + }, + "empty_subject": { + "title": "Voleu enviar-lo sense assumpte?", + "message": "Aquest missatge no té assumpte. Voleu enviar-lo igualment?", + "dont_ask_again": "No ho tornis a preguntar", + "send_anyway": "Envia igualment", + "back": "Torna a l'edició" + }, + "schedule_send": "Programa l'enviament", + "schedule_send_description": "Trieu quan ha d'alliberar el servidor aquest missatge.", + "schedule_preset": { + "tomorrow_morning": "Demà al matí", + "tomorrow_afternoon": "Demà a la tarda", + "monday_morning": "Dilluns al matí" + }, + "schedule_send_required": "Trieu una data i hora.", + "schedule_send_invalid": "Introduïu una data i hora vàlides.", + "schedule_send_future": "Trieu una data i hora futures.", + "schedule_send_too_late": "Aquesta hora és més tardana del que permet el servidor.", + "schedule_send_unsupported": "Aquest compte no admet l'enviament programat.", + "schedule_send_cleanup_warning": "S'ha creat l'enviament programat, però no s'ha pogut netejar l'esborrany.", + "send_delay_unsupported": "Aquest compte no admet el retard d'enviament.", + "send_delay_unsupported_confirm": "Aquest compte no admet el retard d'enviament. Voleu enviar-lo immediatament?", + "recipient_edit_email": "Edita l'adreça electrònica", + "recipient_edit_name": "Edita el nom mostrat", + "recipient_email_placeholder": "Adreça electrònica", + "recipient_name_placeholder": "Nom mostrat", + "invalid_recipient": "No és una adreça de correu vàlida", + "autocomplete_search_server": "Cerca al servidor", + "autocomplete_searching": "Cercant...", + "mention_recipients": "Destinataris", + "mention_card_not_recipient": "No és a Per a ni a CC, així que no rebrà aquest missatge.", + "mention_card_add": "Afegeix a {field}", + "mention_card_remove": "Elimina la menció", + "toolbar": { + "bold": "Negreta", + "italic": "Cursiva", + "underline": "Subratllat", + "strikethrough": "Ratllat", + "text_color": "Color del text", + "remove_color": "Elimina el color", + "heading_1": "Encapçalament 1", + "heading_2": "Encapçalament 2", + "bullet_list": "Llista de pics", + "ordered_list": "Llista numerada", + "quote": "Cita", + "code_block": "Bloc de codi", + "align_left": "Alinea a l'esquerra", + "align_center": "Centra", + "align_right": "Alinea a la dreta", + "text_direction": "Direcció del text (RTL/LTR)", + "link": "Enllaç", + "table": "Taula", + "clear_formatting": "Neteja el format", + "undo": "Desfés", + "redo": "Refés", + "add_row_above": "Afegeix una fila a sobre", + "add_row_below": "Afegeix una fila a sota", + "add_column_before": "Afegeix una columna abans", + "add_column_after": "Afegeix una columna després", + "delete_row": "Elimina la fila", + "delete_column": "Elimina la columna", + "toggle_header_row": "Commuta la fila de capçalera", + "delete_table": "Elimina la taula", + "pick_size": "Tria la mida", + "font_size": "Mida de la lletra", + "font_size_default": "Per defecte", + "background_color": "Color de fons", + "remove_background_color": "Elimina el color de fons" + }, + "send_filing_warning": "Enviat, però la neteja posterior a l'enviament ha fallat; és possible que quedi un esborrany obsolet.", + "pop_out": "Obre la resposta en una finestra", + "send_guard_failed": "No s'ha enviat: un connector que revisa els missatges abans d'enviar-los ha fallat o no ha respost." + }, + "confirm_dialog": { + "confirm": "Confirma", + "cancel": "Cancel·la" + }, + "common": { + "loading": "Carregant...", + "error": "Error", + "success": "Correcte", + "cancel": "Cancel·la", + "save": "Desa", + "delete": "Suprimeix", + "edit": "Edita", + "close": "Tanca", + "search": "Cerca", + "refresh": "Actualitza", + "settings": "Configuració", + "help": "Ajuda", + "logout": "Tanca la sessió", + "yes": "Sí", + "no": "No", + "unknown": "Desconegut", + "app_title": "Webmail", + "reconnecting": "S'ha perdut la connexió. S'està intentant reconnectar…", + "rate_limited_title": "L'autenticació al servidor està temporalment limitada.", + "rate_limited_detail": "El Bulwark ha aturat les sol·licituds en segon pla per evitar el bloqueig. Es tornarà a provar d'aquí a {seconds} s.", + "rate_limited_action_title": "Sol·licitud aturada per evitar el bloqueig.", + "rate_limited_action_detail": "El Bulwark espera que acabi el temps d'espera del servidor abans d'enviar més sol·licituds autenticades. Torneu-ho a provar d'aquí a {seconds} s.", + "pull_to_refresh": "Estira per actualitzar", + "release_to_refresh": "Deixa anar per actualitzar", + "refreshing": "S'està actualitzant…" + }, + "notifications": { + "email_sent": "Correu enviat correctament", + "email_deleted": "Correu suprimit", + "email_archived": "Correu arxivat", + "emails_deleted": "{count, plural, one {# correu suprimit} other {# correus suprimits}}", + "emails_archived": "{count, plural, one {# correu arxivat} other {# correus arxivats}}", + "error_archiving": "No s'ha pogut arxivar el correu", + "error_updating": "No s'ha pogut actualitzar el correu", + "email_starred": "Correu destacat", + "email_unstarred": "S'ha tret l'estrella del correu", + "email_marked_read": "Correu marcat com a llegit", + "email_marked_unread": "Correu marcat com a no llegit", + "copied_to_clipboard": "Copiat al porta-retalls", + "source_copied": "S'ha copiat el codi font al porta-retalls", + "error_sending": "No s'ha pogut enviar el correu", + "error_deleting": "No s'ha pogut suprimir el correu", + "error_loading": "No s'han pogut carregar els correus", + "new_email": "Correu nou", + "new_email_from": "De {sender}", + "click_to_view": "Feu clic per veure'l", + "email_moved": "Correu mogut", + "emails_moved": "{count} correus moguts", + "moved_to_mailbox": "Mogut a {mailbox}", + "move_failed": "No s'ha pogut moure", + "removed_from_folder": "S'ha tret de {mailbox}", + "remove_from_folder_failed": "No s'ha pogut treure d'aquesta carpeta", + "emails_copied": "{count} correus copiats", + "copy_failed": "No s'ha pogut copiar", + "move_error": "No s'han pogut moure els correus a la carpeta seleccionada", + "email_tagged": "Correu etiquetat", + "emails_tagged": "{count} correus etiquetats", + "tag_failed": "No s'ha pogut etiquetar", + "identity_created": "Identitat creada correctament", + "identity_updated": "Identitat actualitzada correctament", + "identity_deleted": "Identitat suprimida", + "identity_set_primary": "Identitat principal actualitzada", + "identity_create_failed": "No s'ha pogut crear la identitat: {error}", + "identity_update_failed": "No s'ha pogut actualitzar la identitat: {error}", + "identity_delete_failed": "No s'ha pogut suprimir la identitat: {error}", + "identity_unauthorized": "No teniu autorització per enviar des d'aquesta adreça electrònica", + "identity_not_found": "No s'ha trobat la identitat", + "vacation_saved": "S'ha desat la configuració d'absència", + "vacation_save_failed": "No s'ha pogut desar la configuració d'absència", + "filters_saved": "Filtres desats correctament", + "filters_save_failed": "No s'han pogut desar els filtres", + "filters_deleted": "Regla de filtre suprimida", + "templates_exported": "Plantilles exportades correctament", + "templates_imported": "{count, plural, one {# plantilla importada} other {# plantilles importades}}", + "templates_import_errors": "Algunes plantilles no s'han pogut importar", + "templates_import_empty": "No s'ha trobat cap plantilla al fitxer", + "export_email_error": "No s'ha pogut exportar el correu", + "import_email_success": "Correu importat correctament", + "import_email_error": "No s'ha pogut importar el correu", + "rule_created": "S'ha creat la regla", + "rule_merged": "S'ha afegit a la regla «{name}»", + "rule_already_covered": "Ja ho cobreix la regla «{name}»", + "rule_apply_existing": "{count, plural, one {Aplica a # missatge existent} other {Aplica a # missatges existents}}", + "rule_applied": "{count, plural, =0 {Cap missatge necessitava canvis} one {S'ha aplicat la regla a # missatge} other {S'ha aplicat la regla a # missatges}}", + "rule_apply_failed": "No s'ha pogut aplicar la regla als missatges existents", + "rule_edit": "Edita la regla", + "rule_undo": "Desfés", + "rule_undone": "S'ha desfet el canvi de la regla", + "rule_undo_failed": "No s'ha pogut desfer el canvi de la regla", + "rule_undo_conflict": "Els teus filtres han canviat des d'aleshores. Desfés el canvi a la configuració dels filtres.", + "rule_folder_failed": "No s'ha pogut crear la carpeta", + "vacation_filters_save_failed": "S'ha desat la configuració d'absència, però no s'han pogut desar el reenviament ni els destinataris de la resposta" + }, + "date": { + "today": "Avui", + "yesterday": "Ahir", + "this_week": "Aquesta setmana", + "last_week": "La setmana passada", + "this_month": "Aquest mes", + "older": "Més antics", + "just_now": "Ara mateix", + "minutes_ago": "Fa {count} minut", + "minutes_ago_plural": "Fa {count} minuts", + "hours_ago": "Fa {count} hora", + "hours_ago_plural": "Fa {count} hores", + "days_ago": "Fa {count} dia", + "days_ago_plural": "Fa {count} dies" + }, + "language": { + "title": "Idioma", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Selecciona l'idioma", + "switch_to_english": "Canvia a l'anglès", + "switch_to_french": "Canvia al francès", + "switch_to_japanese": "Canvia al japonès", + "switch_to_spanish": "Canvia a l'espanyol", + "switch_to_italian": "Canvia a l'italià", + "switch_to_german": "Canvia a l'alemany", + "switch_to_dutch": "Canvia al neerlandès", + "switch_to_portuguese": "Canvia al portuguès", + "switch_to_russian": "Canvia al rus", + "switching": "Canviant d'idioma...", + "switch": "Canvia d'idioma", + "current": "Idioma actual", + "polish": "Polski", + "switch_to_polish": "Canvia al polonès", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Canvia al persa", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Canvia al mongol" + }, + "settings": { + "title": "Configuració", + "back_to_mail": "Torna al correu", + "save_success": "Configuració desada correctament", + "import_success": "Configuració importada correctament", + "import_error": "No s'ha pogut importar la configuració", + "reset_confirm": "Segur que voleu restablir tota la configuració als valors per defecte?", + "unsaved_changes": "Teniu canvis sense desar", + "discard_changes": "Voleu descartar els canvis sense desar?", + "discard": "Descarta", + "keep_editing": "Continua editant", + "search_placeholder": "Cerca a la configuració", + "search_clear": "Neteja la cerca", + "search_no_results": "Cap configuració coincident", + "tabs": { + "appearance": "Aparença", + "language": "Idioma, regió i hora", + "email": "Comportament del correu", + "composer": "Redactor", + "privacy": "Privadesa i seguretat", + "account": "Compte", + "identities": "Identitats", + "vacation": "Absència", + "advanced": "Avançat", + "calendar": "Calendari", + "filters": "Filtres", + "templates": "Plantilles", + "folders": "Carpetes", + "keywords": "Etiquetes", + "security": "Seguretat", + "files": "Fitxers", + "contacts": "Contactes", + "encryption": "Xifratge", + "protocol_handlers": "Aplicacions predeterminades", + "sidebar_apps": "Aplicacions de la barra lateral", + "notifications": "Notificacions", + "layout": "Disposició", + "themes": "Temes", + "reading": "Lectura", + "composing": "Redacció", + "downloads": "Baixades", + "content_senders": "Contingut i remitents", + "about_data": "Quant a i dades", + "debug": "Depuració" + }, + "themes": { + "title": "Temes", + "description": "Trieu entre els temes desplegats pel vostre administrador i els predefinits integrats.", + "locked": "La selecció de tema està bloquejada per un administrador.", + "forced": "El tema «{name}» l'imposa l'administrador i no es pot canviar", + "admin_theme": "Tema de l'administrador", + "activated": "Tema activat", + "default_restored": "S'ha restaurat el tema predeterminat", + "default_name": "Predeterminat", + "default_badge": "Predeterminat", + "admin_enforced": "Imposat per l'administrador" + }, + "tab_groups": { + "general": "General", + "account": "Compte i identitat", + "organization": "Organització del correu", + "apps": "Aplicacions", + "system": "Sistema", + "appearance": "Aparença", + "mail": "Correu", + "privacy": "Privadesa i seguretat", + "advanced": "Avançat" + }, + "appearance": { + "message_list_order": { + "label": "Ordre de la llista de missatges", + "description": "Com s'ordenen els missatges a la llista. L'ordenació la fa el servidor, de manera que abasta tota la carpeta i no només els missatges carregats.", + "preset": { + "chronological": "Cronològic (més recents primer)", + "unread_first": "No llegits primer", + "starred_first": "Destacats primer", + "tagged_first": "Etiquetats primer", + "custom": "Personalitzat" + }, + "tag_label": "Etiqueta", + "advanced_show": "Avançat: edita els nivells d'ordenació", + "advanced_hide": "Amaga les opcions avançades", + "criterion_label": "Criteri del nivell {n}", + "direction_label": "Direcció del nivell {n}", + "criteria": { + "unread": "No llegit", + "starred": "Destacat", + "tag": "Etiqueta", + "receivedAt": "Data de recepció", + "sentAt": "Data d'enviament", + "from": "Remitent", + "subject": "Assumpte", + "size": "Mida" + }, + "direction": { + "unread_first": "No llegits primer", + "read_first": "Llegits primer", + "starred_first": "Destacats primer", + "unstarred_first": "No destacats primer", + "tagged_first": "Etiquetats primer", + "untagged_first": "Sense etiqueta primer", + "newest_first": "Més recents primer", + "oldest_first": "Més antics primer", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Més grans primer", + "smallest_first": "Més petits primer" + }, + "add_level": "Afegeix un nivell", + "remove_level": "Elimina el nivell", + "move_up": "Mou amunt", + "move_down": "Mou avall", + "tiebreak_note": "Fins a tres nivells; els nivells posteriors desfan els empats. «Més recents primer» s'aplica sempre al final.", + "scope": { + "label": "Aplica a totes les carpetes", + "description": "Si està desactivat, l'ordre només s'aplica a la safata d'entrada; la resta de carpetes es mantenen cronològiques." + }, + "unsupported_note": "El teu servidor de correu no admet ordenar per estat de lectura, destacats o etiquetes. Aquests nivells s'ignoren i la llista es manté cronològica." + }, + "title": "Aparença", + "description": "Personalitzeu l'aspecte del vostre correu web", + "theme": { + "label": "Tema", + "description": "Trieu l'esquema de colors preferit", + "light": "Clar", + "dark": "Fosc", + "system": "Sistema" + }, + "language": { + "label": "Idioma", + "description": "Trieu l'idioma preferit" + }, + "font_size": { + "label": "Mida de la lletra", + "description": "Ajusteu la mida del text per millorar-ne la llegibilitat", + "small": "Petita", + "medium": "Mitjana", + "large": "Gran" + }, + "list_density": { + "label": "Densitat", + "description": "Controleu l'espaiat i el farciment de la interfície", + "extra_compact": "Extra compacta", + "compact": "Compacta", + "regular": "Normal", + "comfortable": "Còmoda" + }, + "interface_layout": { + "label": "Disposició de la interfície", + "description": "On se situen la cerca, la navegació i les barres d'eines", + "default": "Predeterminat", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Activa les animacions", + "description": "Mostra transicions i efectes suaus" + }, + "toolbar_position": { + "label": "Posició de la barra d'eines", + "description": "On mostrar els botons d'acció del correu (Respon, Arxiva, Suprimeix, etc.)", + "top": "A dalt", + "below_subject": "Sota l'assumpte" + }, + "toolbar_labels": { + "label": "Mostra les etiquetes de la barra d'eines", + "description": "Mostra etiquetes de text al costat de les icones de la barra d'eines. Desactiveu-ho per estalviar espai un cop us hàgiu familiaritzat amb les icones." + }, + "hide_account_switcher": { + "label": "Amaga el selector de comptes de la barra lateral", + "description": "Amaga el selector de comptes a la part superior de la barra lateral de carpetes. Encara podreu canviar de compte des de la barra de navegació inferior." + }, + "show_rail_account_list": { + "label": "Mostra els avatars dels comptes a la barra de navegació", + "description": "Mostra cercles individuals per a cada compte a la part inferior de la barra de navegació per canviar-hi ràpidament, amb un botó de tancament de sessió a sota." + }, + "unified_mailbox": { + "label": "Bústia unificada", + "description": "Mostra carpetes combinades (Safata d'entrada, Enviats, etc.) del compte actiu i les seves carpetes compartides.", + "cross_account": { + "label": "En tots els comptes", + "description": "Combina la bústia unificada de tots els comptes connectats en lloc de limitar-la al compte actiu." + }, + "include_group": { + "label": "Inclou les safates d'entrada de grup", + "description": "Combina també les safates d'entrada compartides o de grup a la vista unificada." + } + }, + "all_mail": { + "folders_label": "Carpetes a les llistes unificades", + "folders_description": "Trieu quines carpetes d'aquest compte es combinen a les llistes Tot el correu / No llegits / Destacats.", + "account_hint": "S'aplica a {account}.", + "no_folders": "No hi ha cap carpeta disponible." + }, + "colorful_sidebar_icons": { + "label": "Icones de colors a la barra lateral", + "description": "Acoloreix les icones de carpetes i etiquetes segons el tipus (blau per a la safata d'entrada, vermell per a la brossa, verd per als enviats, etc.). Desactiveu-ho per a una barra lateral monocroma." + }, + "tint_list_rows": { + "label": "Acoloreix les files de la llista segons l'etiqueta", + "description": "Ombreja cada fila de missatge amb el color de la primera etiqueta. Desactiveu-ho per mantenir les files planes; els punts i les etiquetes encara mostraran el color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Mostra el nombre total de missatges", + "description": "Mostra el nombre total de missatges al costat de les carpetes i etiquetes, juntament amb el nombre de no llegits. Desactiveu-ho per mostrar només els no llegits." + }, + "favicon_unread_badge": { + "label": "Nombre de no llegits a la icona de la pestanya", + "description": "Mostra el nombre de no llegits de la safata d'entrada com a distintiu a la icona de la pestanya del navegador, perquè el correu nou sigui visible encara que la pestanya no tingui el focus. Desactiveu-ho per mantenir la icona sense distintiu." + }, + "show_shared_mail_accounts": { + "label": "Mostra els comptes de correu compartits", + "description": "Mostra les carpetes de les bústies compartides amb tu a Compartit, a la barra lateral. Desactiva-ho per amagar-les totes, o amaga comptes concrets a sota. Els calendaris i contactes es mantenen." + }, + "pro_interface": { + "label": "Interfície Pro (experimental)", + "description": "Disposició avançada només per a escriptori amb navegació de missatges en diverses pestanyes i fluxos de treball entre comptes. La interfície estàndard no es veu afectada; podeu tornar-hi en qualsevol moment.", + "open_label": "Obre la interfície Pro", + "back_to_standard": "Torna a l'estàndard" + }, + "cross_unread": { + "label": "No llegits", + "description": "Mostra una entrada de No llegits a la bústia unificada amb el correu no llegit de les carpetes seleccionades." + }, + "cross_starred": { + "label": "Destacats", + "description": "Mostra una entrada de Destacats a la bústia unificada amb el correu destacat de les carpetes seleccionades." + }, + "cross_all": { + "label": "Tot el correu", + "description": "Mostra una entrada de Tot el correu a la bústia unificada amb tot el correu de les carpetes seleccionades." + } + }, + "keywords": { + "title": "Etiquetes de correu", + "description": "Definiu etiquetes per organitzar els correus amb colors. Es desen com a paraules clau JMAP al servidor.", + "add_keyword": "Afegeix una etiqueta", + "label_field": "Nom mostrat", + "label_placeholder": "p. ex. Feina, Personal, Urgent", + "id_field": "ID de l'etiqueta", + "id_placeholder": "p. ex. feina, personal", + "color_field": "Color", + "id_exists": "Aquest ID d'etiqueta ja existeix", + "edit": "Edita l'etiqueta", + "delete": "Suprimeix l'etiqueta", + "save": "Desa", + "add": "Afegeix", + "cancel": "Cancel·la", + "migrating": "Actualitzant l'etiqueta als correus existents…", + "migration_error": "No s'ha pogut actualitzar l'etiqueta als correus existents", + "filters_migration_error": "Algunes regles de filtre encara fan servir l'etiqueta antiga: actualitza-les manualment", + "nesting": { + "label": "Etiquetes imbricades", + "description": "Imbrica etiquetes sota altres etiquetes i mostra-les com un arbre a la barra lateral." + }, + "parent_field": "Etiqueta principal", + "no_parent": "Sense etiqueta principal", + "too_long": "Aquest camí d'etiqueta és massa llarg (com a màxim {max} caràcters)", + "has_children_locked": "Hi ha altres etiquetes imbricades sota aquesta, per això el seu nom i la seva etiqueta principal estan bloquejats. Mou-les o elimina-les primer.", + "has_children_delete": "Elimina primer les etiquetes imbricades sota aquesta", + "visibility_field": "Visibilitat a la barra lateral", + "visibility": { + "show": "Mostra", + "unread": "Mostra si hi ha no llegits", + "hide": "Amaga" + }, + "unrecognized": { + "title": "Recupera etiquetes del servidor", + "description": "Els noms i els colors de les etiquetes es desen en aquest navegador, però les etiquetes viuen als vostres correus com a paraules clau JMAP. Analitzeu el correu per trobar paraules clau que cap etiqueta d'aquí no expliqui i torneu-les a afegir.", + "scan": "Cerca etiquetes", + "rescan": "Torna a cercar", + "scanning": "S'està analitzant… {scanned} de {total} correus", + "none": "Totes les etiquetes dels vostres correus ja estan definides aquí.", + "messages": "{count, plural, one {# correu} other {# correus}}", + "add_all": "Afegeix-les totes ({count})", + "partial": "Només s'han analitzat els {scanned} correus més recents de {total}. Poden faltar etiquetes usades únicament en correus més antics.", + "error": "No s'ha pogut analitzar el correu per cercar etiquetes." + } + }, + "notifications": { + "test_sound": "Prova el so de notificació", + "sounds": { + "default": "Per defecte (bip)", + "cheerful": "Alegre", + "involved": "Elaborat", + "swift": "Gest ràpid", + "relax": "Relaxant" + }, + "push": { + "title": "Notificacions en segon pla", + "description": "Rebeu notificacions del sistema per al correu nou quan aquest lloc estigui tancat. S'entreguen mitjançant el repetidor push del Bulwark; el repetidor mai no veu el contingut del correu.", + "relay_label": "Repetidor push", + "relay_desc": "Per defecte utilitza el repetidor allotjat del Bulwark. Canvieu-ho només si allotgeu el vostre propi servidor.", + "relay_locked": "Establert per l'administrador", + "relay_locked_desc": "L'URL del repetidor push l'ha establert l'administrador i no es pot canviar.", + "status_active": "Actiu en aquest dispositiu", + "status_inactive": "No activat en aquest dispositiu", + "status_unsupported": "Aquest navegador no admet Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "Treballant…", + "enable": "Activa", + "reenable": "Torna a registrar", + "disable": "Desactiva", + "confirm_disable_title": "Voleu desactivar les notificacions en segon pla?", + "confirm_disable_message": "Aquest dispositiu deixarà de rebre avisos quan el lloc estigui tancat.", + "dismiss_aria": "Descarta l'avís de notificacions", + "ios_hint": "A l'iOS, primer instal·leu el lloc a la pantalla d'inici - el Safari només entrega Web Push a les PWA instal·lades.", + "devices_title": "Dispositius registrats", + "devices_desc": "Tots els dispositius registrats per rebre notificacions en segon pla d’aquest compte. Si en revoques un, deixa de rebre-les immediatament.", + "devices_loading": "S’estan carregant els dispositius registrats…", + "devices_empty": "No hi ha cap dispositiu registrat per a les notificacions en segon pla.", + "devices_error": "No s’han pogut carregar els dispositius registrats.", + "devices_refresh": "Actualitza", + "device_this": "Aquest dispositiu", + "device_other": "Un altre dispositiu", + "device_status_active": "Lliura notificacions", + "device_status_inactive": "Mai no ha lliurat cap notificació", + "device_status_unknown": "Desconegut per a aquest relé", + "device_expires": "Caduca el {date}", + "revoke": "Revoca", + "confirm_revoke_title": "Voleu revocar aquest dispositiu?", + "confirm_revoke_message": "Deixarà de rebre notificacions en segon pla immediatament. Si hi tornes a activar les notificacions push, es registrarà de nou.", + "confirm_revoke_message_this": "Aquest dispositiu deixarà de rebre notificacions en segon pla immediatament." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "So de notificació", + "description": "Trieu quin so es reprodueix per a les notificacions", + "choose": "So", + "choose_desc": "Seleccioneu un to de notificació i feu clic a la icona de l'altaveu per escoltar-lo" + }, + "email": { + "title": "Notificacions de correu", + "description": "Configureu les notificacions per als correus entrants", + "enabled": "Notificacions de correu", + "enabled_desc": "Mostra notificacions quan arribin correus nous", + "sound": "So de notificació", + "sound_desc": "Reprodueix un avís sonor quan arribin correus nous", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Notificacions de calendari", + "description": "Configureu les notificacions per als esdeveniments del calendari", + "enabled": "Notificacions d'esdeveniments", + "enabled_desc": "Mostra avisos per als esdeveniments propers del calendari", + "sound": "So de notificació", + "sound_desc": "Reprodueix un avís sonor per als recordatoris del calendari", + "invitation_parsing": "Analitza les invitacions per correu", + "invitation_parsing_desc": "Detecta les invitacions de calendari als fitxers adjunts del correu i mostra les accions del calendari" + } + }, + "language_region": { + "title": "Idioma, regió i hora", + "description": "Idioma, format de data, format d'hora i altres preferències regionals", + "language": { + "label": "Idioma", + "description": "Trieu l'idioma preferit", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Format de data", + "description": "Com es mostren les dates a la llista de correus", + "smart": "Intel·ligent", + "relative": "Relatiu", + "full": "Sempre la data completa", + "preview_today": "Avui:", + "preview_this_week": "Aquesta setmana:", + "preview_older": "Més antics:" + }, + "date_locale": { + "label": "Regió del format de data", + "description": "Com s'ordenen les dates numèriques (dia, mes, any)", + "auto": "Automàtic", + "iso": "ISO 8601", + "dmy": "Dia/Mes/Any", + "mdy": "Mes/Dia/Any" + }, + "time_format": { + "label": "Format d'hora", + "description": "Trieu entre el format de 12 o 24 hores", + "12h": "12 hores", + "24h": "24 hores" + }, + "first_day": { + "label": "Primer dia de la setmana", + "description": "Comença la setmana en diumenge o dilluns", + "sunday": "Diumenge", + "monday": "Dilluns" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Esborra la cerca en canviar de carpeta", + "description": "En clicar una carpeta (Safata d'entrada, Enviats…) s'esborra la cerca activa i es mostra aquesta carpeta. Si està desactivat, la cerca es manté i es torna a executar a la carpeta triada." + }, + "request_read_receipt": { + "label": "Sol·licita confirmacions de lectura per defecte", + "description": "Activa per defecte la sol·licitud de confirmació de lectura en redactar un missatge nou." + }, + "read_receipt_response": { + "label": "Respon a les sol·licituds de confirmació de lectura", + "description": "Què fer quan un missatge entrant sol·licita una confirmació de lectura.", + "ask": "Pregunta cada vegada", + "always": "Envia sempre", + "never": "No enviïs mai" + }, + "title": "Comportament del correu", + "description": "Configureu com es gestionen els correus", + "mark_read": { + "label": "Marca com a llegit", + "description": "Quan marcar els correus com a llegits en obrir-los", + "instant": "Immediatament", + "delay_3s": "Després de 3 segons", + "delay_5s": "Després de 5 segons", + "never": "Mai" + }, + "delete_action": { + "label": "Acció de supressió", + "description": "Què passa quan suprimiu un correu", + "trash": "Mou a la paperera", + "trash_and_read": "Mou a la paperera i marca com a llegit", + "permanent": "Suprimeix definitivament", + "warning": "Els correus se suprimiran definitivament i no es podran recuperar. Aquesta acció és irreversible." + }, + "message_spacing": { + "label": "Espaiat del missatge", + "description": "Farciment al voltant del cos del missatge en el lector", + "auto": "Automàtic (recomanat)", + "always": "Afegeix sempre espaiat", + "edge": "De vora a vora" + }, + "plain_text_font": { + "label": "Tipus de lletra del text pla", + "description": "Tipus de lletra per als correus de text pla", + "mono": "Monoespaiada", + "sans": "Normal (igual que l'aplicació)" + }, + "archive_mode": { + "label": "Arxiva a", + "description": "Com organitzar els correus en arxivar-los", + "single": "Una única carpeta", + "year": "Una carpeta per any", + "month": "Una carpeta per mes", + "reorganize": "Reorganitza l'arxiu existent", + "reorganize_success": "{count, plural, =0 {Cap correu per reorganitzar} =1 {1 correu reorganitzat} other {# correus reorganitzats}}", + "reorganize_error": "No s'ha pogut reorganitzar l'arxiu" + }, + "permanently_delete_junk": { + "label": "Suprimeix definitivament la brossa", + "description": "Suprimeix definitivament els correus de la carpeta de brossa en lloc de moure'ls a la paperera" + }, + "confirm_bulk_delete": { + "label": "Confirma abans d'eliminar diversos missatges", + "description": "Pregunta abans de moure diversos missatges seleccionats a la paperera. L'eliminació definitiva sempre demana confirmació." + }, + "mail_layout": { + "label": "Disposició del correu", + "description": "Trieu entre la vista dividida clàssica, un flux de lectura centrat a l'estil Gmail, o una subfinestra de lectura inferior a l'estil Zimbra.", + "split": "Vista dividida", + "split_description": "Manté la llista de missatges i la subfinestra de lectura visibles una al costat de l'altra.", + "focus": "Llista centrada", + "focus_description": "Mostra una línia per missatge i obre el correu a amplada completa mantenint visible la barra lateral de carpetes.", + "horizontal": "Subfinestra de lectura a baix", + "horizontal_description": "Mostra la llista de missatges a dalt i obre el missatge seleccionat en una subfinestra de lectura a sota." + }, + "list_columns": { + "label": "Mostra la llista en columnes", + "description": "Mostra les files d'una línia com una taula amb les columnes De, Assumpte, Data i Mida sota una capçalera." + }, + "show_preview": { + "label": "Mostra el text de previsualització", + "description": "Mostra la previsualització del correu a la llista", + "focus_description": "Mostra el text de previsualització incrustat dins la llista de missatges d'una línia centrada" + }, + "show_verification_codes": { + "label": "Mostra els codis de verificació", + "description": "Ofereix el codi dels correus d'inici de sessió i de confirmació per copiar-lo amb un clic, a la llista durant un dia i al missatge" + }, + "disable_threading": { + "label": "Desactiva l'agrupació en converses", + "description": "Mostra els correus com a missatges individuals en lloc d'agrupar-los per conversa" + }, + "plain_text_mode": { + "label": "Només text sense format", + "description": "Desactiva l'editor de text enriquit i envia tots els correus només com a text sense format, incloses les respostes i els reenviaments" + }, + "auto_select_reply_identity": { + "label": "Respon des de l'adreça de recepció", + "description": "En respondre, envia des de l'adreça a la qual es va enviar originalment el missatge. Primer intenta coincidir amb les identitats; per als lliuraments de captura general de domini, reescriu la capçalera «De» amb l'àlies mentre envia a través de la identitat principal." + }, + "reply_identity_match": { + "label": "Coincidència de l'adreça de recepció", + "description": "Quines adreces de recepció compten com a teves. «Només l'adreça exacta» tria únicament una de les teves identitats configurades; «mateix domini» també tracta qualsevol altra adreça dels teus dominis d'identitat com un àlies catch-all i hi reescriu la capçalera De. Tria l'adreça exacta si aquestes adreces són llistes de distribució.", + "exact": "Només l'adreça exacta", + "domain": "Qualsevol adreça dels meus dominis" + }, + "signature_position": { + "label": "Posició de la signatura", + "description": "On inserir la signatura a les respostes i reenviaments. Abans del text citat es llegeix de manera natural com a tancament de la resposta; després manté el missatge original contigu.", + "above_quote": "Abans del text citat", + "below_quote": "Després del text citat" + }, + "signature_separator": { + "label": "Delimitador de la signatura", + "description": "Prefixa la signatura amb la línia delimitadora estàndard «-- » (RFC 3676). Desactiveu-ho si preferiu passar directament del missatge a la signatura." + }, + "sub_address_delimiter": { + "label": "Delimitador de subadreça", + "description": "Caràcter que separa el nom d'usuari d'una etiqueta de subadreça. Feu-lo coincidir amb el delimitador que utilitza el servidor de correu (p. ex. user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "Personalitzat…", + "custom_input_label": "Caràcter delimitador personalitzat" + }, + "attachment_click_action": { + "label": "Acció en clicar un fitxer adjunt", + "description": "Trieu si en clicar un fitxer adjunt es previsualitza o es baixa immediatament", + "preview": "Previsualitza quan sigui possible", + "download": "Baixa immediatament" + }, + "attachment_position": { + "label": "Posició dels fitxers adjunts", + "description": "On mostrar els fitxers adjunts a la capçalera del correu", + "beside-sender": "Al costat del remitent", + "below-header": "Sota la capçalera" + }, + "emails_per_page": { + "10": "10 correus", + "25": "25 correus", + "50": "50 correus", + "100": "100 correus", + "label": "Correus per pàgina", + "description": "Nombre de correus que es carreguen alhora" + }, + "always_light_mode": { + "label": "Mostra sempre els correus en mode clar", + "description": "Renderitza el contingut del correu en mode clar encara que l'aplicació estigui en mode fosc, evitant problemes de conversió del mode fosc" + }, + "external_content": { + "label": "Contingut extern", + "description": "Com gestionar les imatges i el contingut extern", + "ask": "Pregunta sempre", + "block": "Bloqueja sempre", + "allow": "Permet sempre" + }, + "trusted_senders": { + "label": "Remitents de confiança", + "description": "Gestioneu els remitents les imatges dels quals es carreguen automàticament", + "count_zero": "Cap", + "count_one": "1 remitent", + "count_other": "{count} remitents", + "modal_title": "Remitents de confiança", + "empty_title": "Encara no hi ha remitents de confiança", + "empty_description": "En visualitzar un correu amb imatges bloquejades, feu clic a «Confia sempre en aquest remitent» per afegir-lo aquí.", + "add_manually": "Afegeix un remitent manualment", + "add_button": "Afegeix", + "add_placeholder": "Introduïu l'adreça electrònica", + "search_placeholder": "Cerca remitents...", + "no_results": "Cap remitent coincideix amb la cerca", + "remove": "Elimina", + "close": "Tanca", + "invalid_email": "Introduïu una adreça electrònica vàlida", + "already_added": "Aquest remitent ja és de confiança", + "save_error": "No s'ha pogut desar - consulteu el registre de depuració de Contactes per obtenir més detalls", + "use_address_book_label": "Sincronitza amb la llibreta d'adreces", + "use_address_book_description": "Desa els remitents de confiança en una llibreta d'adreces dedicada «Remitents de confiança» perquè es sincronitzin a tots els dispositius" + }, + "hover_actions": { + "label": "Accions ràpides en passar el cursor", + "description": "Trieu quines accions ràpides apareixen en passar el cursor per sobre d'un correu de la llista", + "delete": "Suprimeix", + "star": "Marca/Treu l'estrella", + "mark_read": "Marca com a llegit/no llegit", + "archive": "Arxiva", + "tag": "Etiqueta", + "spam": "Marca com a brossa", + "not_spam": "No és brossa", + "none_selected": "Cap acció seleccionada", + "mode_label": "Mode de visualització", + "mode_inline": "Incrustat", + "mode_floating": "Flotant", + "corner_label": "Posició flotant", + "corner_top-left": "Superior esquerra", + "corner_top-right": "Superior dreta", + "corner_bottom-left": "Inferior esquerra", + "corner_bottom-right": "Inferior dreta" + }, + "default_mail_program": { + "label": "Programa de correu predeterminat", + "description": "Registra {appName} com al programa de correu predeterminat per als enllaços mailto:", + "button": "Estableix com a predeterminat", + "success": "S'ha demanat al navegador que l'estableixi com a predeterminat", + "error": "El navegador no admet aquesta funció" + }, + "attachment_reminder": { + "label": "Recordatori de fitxers adjunts", + "description": "Avisa abans d'enviar quan el missatge esmenta fitxers adjunts però no n'hi ha cap", + "keywords_label": "Paraules clau que ho activen", + "keywords_description": "Paraules o frases que activen el recordatori quan es troben al missatge", + "add_placeholder": "Afegeix una paraula clau...", + "add": "Afegeix", + "remove": "Elimina" + }, + "empty_subject_warning": { + "label": "Avís d'assumpte buit", + "description": "Demana confirmació abans d'enviar un missatge sense assumpte." + }, + "recipient_mentions": { + "label": "Esmentar destinataris amb @", + "description": "En escriure @ al missatge s'ofereixen els destinataris i s'insereix el nom de pila del triat. Retorn o Tab tria el nom ressaltat." + }, + "mention_adds_recipient": { + "label": "Contactes a les mencions amb @", + "description": "En escriure @ també s'ofereixen els teus contactes i persones del directori. Si tries algú que encara no és destinatari, s'afegeix al missatge.", + "to": "Afegeix a Per a", + "cc": "Afegeix a CC", + "off": "Només destinataris" + }, + "hide_inline_image_attachments": { + "label": "Amaga les imatges incrustades dels fitxers adjunts", + "description": "Les imatges incrustades al cos del missatge no es llisten com a fitxers adjunts separats" + }, + "attachment_image_previews": { + "label": "Mostra previsualitzacions d'imatge als fitxers adjunts", + "description": "Renderitza els fitxers adjunts d'imatge com a targetes de miniatura en lloc d'icones de fitxer genèriques" + }, + "send_delay": { + "label": "Desfés l'enviament / retard d'enviament", + "description": "Retarda els enviaments normals amb un petit marge al servidor.", + "off": "Desactivat", + "seconds": "{seconds} segons", + "unsupported": "El compte actual no indica compatibilitat amb l'enviament retardat. La configuració es continua desant per a altres comptes." + }, + "return_to_list_after_action": { + "label": "Torna a la llista després de suprimir o marcar com a no llegit", + "description": "Després de suprimir o marcar com a no llegit el missatge obert, torna a la llista de missatges en lloc d'obrir el següent." + }, + "swipe_right_action": { + "label": "Acció en lliscar a la dreta (mòbil)", + "description": "En dispositius tàctils, l'acció que es fa quan llisqueu un missatge cap a la dreta a la llista." + }, + "swipe_left_action": { + "label": "Acció en lliscar a l'esquerra (mòbil)", + "description": "En dispositius tàctils, l'acció que es fa quan llisqueu un missatge cap a l'esquerra a la llista." + }, + "swipe_actions": { + "none": "Cap", + "archive": "Arxiva", + "delete": "Suprimeix", + "mark_read": "Commuta llegit", + "star": "Commuta l'estrella", + "spam": "Marca com a brossa" + }, + "rtl_editing": { + "label": "Compatibilitat amb edició de dreta a esquerra", + "description": "Afegeix un botó de direcció a la barra d'eines del redactor perquè pugueu establir els paràgrafs d'esquerra a dreta o de dreta a esquerra" + } + }, + "composer": { + "title": "Redactor", + "description": "Configureu les opcions de redacció de correu", + "autosave": { + "label": "Interval de desat automàtic", + "description": "Amb quina freqüència es desen automàticament els esborranys", + "30s": "Cada 30 segons", + "1m": "Cada minut", + "2m": "Cada 2 minuts", + "5m": "Cada 5 minuts" + }, + "send_confirmation": { + "label": "Confirmació d'enviament", + "description": "Demana confirmació abans d'enviar els correus" + }, + "default_reply": { + "label": "Mode de resposta predeterminat", + "description": "Acció predeterminada en clicar respon", + "reply": "Respon", + "reply_all": "Respon a tots" + } + }, + "privacy": { + "title": "Privadesa i seguretat", + "description": "Gestioneu la configuració de privadesa i seguretat", + "external_images": { + "label": "Bloqueja les imatges externes", + "description": "Evita el seguiment mitjançant imatges externes" + }, + "session_timeout": { + "label": "Temps d'espera de la sessió", + "description": "Tanca la sessió automàticament després d'inactivitat", + "never": "Mai", + "30m": "30 minuts", + "1h": "1 hora", + "4h": "4 hores" + }, + "clear_cache": { + "label": "Neteja la memòria cau", + "description": "Elimina les dades emmagatzemades i els fitxers temporals", + "button": "Neteja la memòria cau", + "confirm": "Segur que voleu netejar la memòria cau?", + "success": "Memòria cau netejada correctament" + } + }, + "account": { + "title": "Compte", + "description": "Vegeu la informació del vostre compte", + "name_label": "Nom mostrat", + "username_label": "Nom d'usuari", + "account_type_label": "Tipus de compte", + "auth_method_label": "Autenticació", + "auth_method_oauth": "Inici de sessió únic (OAuth/OIDC)", + "auth_method_basic": "Contrasenya", + "auth_method_token": "Token d'accés", + "demo_account": "Compte de demostració", + "email": { + "label": "Adreça electrònica", + "value": "{email}" + }, + "server": { + "label": "Servidor JMAP", + "value": "{server}" + }, + "storage": { + "label": "Ús de l'emmagatzematge", + "used": "{used} de {total} utilitzats", + "percentage": "{percent}% utilitzat" + }, + "last_sync": { + "label": "Última sincronització", + "value": "{time}" + }, + "accounts": { + "title": "Comptes amb la sessió iniciada", + "description": "Arrossegueu per reordenar com apareixen els comptes al menú desplegable", + "active": "Compte actiu actualment", + "default_badge": "Compte predeterminat", + "set_default": "Estableix com a predeterminat", + "switch_to": "Canvia a aquest compte", + "move_up": "Mou amunt", + "move_down": "Mou avall", + "drag_handle": "Arrossegueu per reordenar", + "add": "Afegeix un compte" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Compartit amb mi", + "description": "Comptes de grup i compartits que podeu gestionar. Seleccioneu-ne un per editar-ne els filtres, la resposta automàtica, els calendaris i els contactes.", + "shared_label": "Compte compartit" + } + }, + "scoped": { + "back": "Torna al meu compte", + "managing": "Gestionant: {name}" + }, + "security": { + "title": "Seguretat del compte", + "description": "Gestioneu la contrasenya, l'autenticació de dos factors i la configuració de seguretat", + "detecting": "Detectant les capacitats del servidor...", + "not_available": "La gestió de la seguretat del compte no està disponible per a aquest servidor de correu. És possible que els permisos necessaris estiguin desactivats. Consulteu la documentació per obtenir més detalls.", + "password": { + "title": "Canvia la contrasenya", + "current": "Contrasenya actual", + "new": "Contrasenya nova", + "confirm": "Confirma la contrasenya nova", + "submit": "Canvia la contrasenya", + "success": "Contrasenya canviada correctament", + "error_title": "No s'ha pogut canviar la contrasenya", + "error_mismatch": "Les contrasenyes noves no coincideixen", + "error_min_length": "La contrasenya ha de tenir com a mínim 8 caràcters", + "error_generic": "No s'ha pogut canviar la contrasenya" + }, + "display_name": { + "label": "Nom mostrat", + "description": "El vostre nom tal com apareix al servidor", + "placeholder": "Introduïu el nom mostrat", + "save": "Desa", + "success": "Nom mostrat actualitzat", + "error": "No s'ha pogut actualitzar el nom mostrat" + }, + "totp": { + "section_title": "Autenticació de dos factors", + "label": "Autenticació TOTP", + "description": "Afegiu una capa addicional de seguretat amb una contrasenya d'un sol ús basada en el temps", + "active": "Activada", + "inactive": "Desactivada", + "enabled": "Autenticació de dos factors activada", + "disabled": "Autenticació de dos factors desactivada", + "enable_error": "No s'ha pogut activar el 2FA", + "disable_error": "No s'ha pogut desactivar el 2FA", + "setup_instructions": "Copieu aquest URL a la vostra aplicació d'autenticació (Google Authenticator, Authy, etc.):", + "verification_code": "Codi de verificació", + "confirm": "Confirma", + "disable": "Desactiva", + "disable_confirm_prompt": "Introduïu la contrasenya per desactivar l'autenticació de dos factors.", + "password_required": "Cal la contrasenya", + "code_required": "Cal el codi de verificació", + "code_invalid": "El codi de verificació no és vàlid. Comproveu l'aplicació d'autenticació i torneu-ho a provar." + }, + "app_passwords": { + "title": "Contrasenyes d'aplicació", + "description": "Creeu contrasenyes per a aplicacions que no admeten l'autenticació de dos factors", + "add": "Afegeix", + "create": "Crea", + "cancel": "Cancel·la", + "done": "Fet", + "generate": "Genera", + "name_label": "Nom de l'aplicació", + "name_placeholder": "p. ex. Thunderbird, Correu de l'iPhone", + "expires_label": "Caduca (opcional)", + "allowed_ips_label": "IP permeses (opcional)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Separades per comes o espais. Deixeu-ho buit per permetre qualsevol IP.", + "password_label": "Contrasenya (deixeu-ho buit per generar-la automàticament)", + "password_placeholder": "Es genera automàticament si es deixa buit", + "copy_now_warning": "Copieu aquesta contrasenya ara - no es tornarà a mostrar.", + "added": "Contrasenya d'aplicació creada", + "removed": "Contrasenya d'aplicació eliminada", + "add_error": "No s'ha pogut crear la contrasenya d'aplicació", + "remove_error": "No s'ha pogut eliminar la contrasenya d'aplicació", + "none": "No hi ha cap contrasenya d'aplicació configurada" + }, + "api_keys": { + "title": "Claus API", + "description": "Creeu claus API per a scripts i integracions que es comuniquen directament amb el servidor", + "name_label": "Nom de la clau", + "name_placeholder": "p. ex. Script de còpia de seguretat, executor de CI", + "copy_now_warning": "Copieu aquesta clau API ara - no es tornarà a mostrar.", + "added": "Clau API creada", + "removed": "Clau API eliminada", + "add_error": "No s'ha pogut crear la clau API", + "remove_error": "No s'ha pogut eliminar la clau API", + "none": "No hi ha cap clau API configurada" + }, + "encryption": { + "section_title": "Xifratge en repòs", + "label": "Xifratge del correu", + "description": "Xifra els correus emmagatzemats al servidor per obtenir més privadesa", + "active": "Xifratge {type} activat", + "inactive": "Desactivat", + "enabled": "Xifratge en repòs activat", + "disabled_success": "Xifratge en repòs desactivat", + "error": "No s'ha pogut actualitzar la configuració de xifratge", + "algorithm_label": "Algorisme de xifratge", + "encrypt_on_append": "Xifra els correus nous en pujar-los", + "allow_spam_training": "Permet l'entrenament antispam abans de xifrar els correus", + "enable_encryption": "Activa el xifratge", + "enable_tooltip": "Activa el xifratge amb aquesta clau", + "configure_title": "Activa el xifratge amb aquesta clau", + "enable_button": "Activa", + "disable_tooltip": "Desactiva el xifratge", + "enabled_success": "Xifratge en repòs activat" + }, + "email_client": { + "title": "Configuració del client de correu", + "description": "Utilitzeu aquestes credencials per configurar el client de correu d'escriptori o mòbil (Thunderbird, Apple Mail, Outlook, etc.)", + "jmap_username_label": "Nom d'usuari JMAP", + "copy": "Copia", + "copied": "Copiat", + "password_instructions": "Utilitzeu el nom d'usuari JMAP anterior juntament amb una contrasenya d'aplicació per iniciar la sessió al client de correu. Creeu una contrasenya d'aplicació a la secció anterior si encara no ho heu fet." + }, + "link_device": { + "title": "Enllaça l'aplicació mòbil", + "description": "Inicieu la sessió a l'aplicació Bulwark Mail del telèfon sense escriure-hi la contrasenya. El telèfon obté el seu propi inici de sessió, de manera que tancar la sessió aquí no la tanca al telèfon.", + "generate": "Mostra el codi QR", + "regenerate": "Mostra un codi nou", + "instructions": "Obriu l'aplicació Bulwark Mail i toqueu «Scan a sign-in code» a la pantalla d'inici de sessió (o «Add account» → «Scan a sign-in code» si ja heu iniciat la sessió) i, després, apunteu la càmera aquí.", + "expires_in": "Aquest codi caduca d'aquí a {seconds} segons. Només es pot utilitzar una vegada.", + "expired": "Aquest codi ha caducat.", + "error": "No s'ha pogut crear el codi d'aparellament. Torneu-ho a provar.", + "password_prompt": "Introduïu la contrasenya per confirmar que sou vós. El telèfon obté el seu propi inici de sessió; la contrasenya no s'envia al telèfon.", + "show_password": "Mostra la contrasenya", + "hide_password": "Amaga la contrasenya", + "continue": "Continua", + "error_wrong_password": "Contrasenya incorrecta. Torneu-ho a provar.", + "totp_prompt": "Introduïu el codi de l'aplicació d'autenticació.", + "error_password_or_code": "La contrasenya o el codi són incorrectes.", + "error_not_signed_in": "La sessió ha caducat. Torneu a iniciar la sessió per enllaçar un dispositiu.", + "error_impersonation": "No es poden enllaçar dispositius mentre consulteu la bústia d'un altre usuari.", + "error_too_many_attempts": "Massa intents. Espereu un moment i torneu-ho a provar.", + "error_session_secret": "L'enllaç de dispositius no està configurat en aquest servidor (falta SESSION_SECRET). Poseu-vos en contacte amb l'administrador.", + "error_server_unreachable": "No s'ha pogut connectar amb el servidor de correu. Torneu-ho a provar.", + "error_pairing_unavailable": "El vostre servidor de correu no permet enllaçar dispositius d'aquesta manera. Poseu-vos en contacte amb l'administrador.", + "qr_alt": "Codi QR d'inici de sessió", + "link_label": "Enllaç d'inici de sessió", + "copy_link": "Copia l'enllaç d'inici de sessió", + "copied": "Copiat", + "copy_hint": "No teniu càmera? Copieu l'enllaç i enganxeu-lo a l'aplicació amb «Paste sign-in link».", + "success_title": "Sessió iniciada al telèfon", + "success_body": "Podeu tancar aquesta pàgina. Per enllaçar un altre dispositiu, mostreu un codi nou.", + "link_another": "Enllaça un altre dispositiu", + "use_sso": "Utilitza l'inici de sessió únic", + "error_insecure_server": "L'aplicació mòbil només es connecta a servidors de correu per https, i aquest fa servir http." + }, + "public_keys": { + "name_label": "Nom de la clau pública", + "name_placeholder": "p. ex. La meva clau", + "key_label": "Clau pública (S/MIME o PGP en ASCII armor)", + "key_placeholder": "Enganxeu aquí la vostra clau pública", + "none": "No hi ha cap clau pública configurada" + } + }, + "identities": { + "title": "Identitats d'enviament", + "description": "Gestioneu les adreces electròniques des de les quals podeu enviar", + "identities_count": { + "label": "Les vostres identitats", + "description": "Adreces electròniques configurades per a l'enviament", + "count_zero": "Cap identitat", + "count_one": "1 identitat", + "count_other": "{count} identitats" + }, + "manage": "Gestiona les identitats", + "sub_addressing": { + "label": "Subadreçament", + "description": "Utilitzeu etiquetes com user+tag@domain.com per organitzar el correu entrant", + "learn_more": "Més informació" + } + }, + "vacation": { + "title": "Absència", + "description": "Gestiona automàticament els correus entrants mentre esteu fora", + "loading": "Carregant la configuració de la resposta automàtica...", + "not_supported": "El servidor de correu no admet respostes automàtiques.", + "fetch_error": "No s'ha pogut carregar la configuració de la resposta automàtica. Torneu-ho a provar.", + "status": { + "label": "Respon automàticament", + "description": "Envia una resposta automàtica a les persones que us envien correu", + "active": "Activa", + "inactive": "Inactiva" + }, + "date_range": { + "start": "Data d'inici", + "start_description": "Deixeu-ho buit per no establir cap límit inicial", + "end": "Data de finalització", + "end_description": "Deixeu-ho buit per no establir cap límit final" + }, + "message": { + "subject_label": "Assumpte", + "subject_description": "Línia d'assumpte de la resposta automàtica", + "subject_placeholder": "Fora de l'oficina", + "body_label": "Cos del missatge", + "body_description": "Contingut del missatge en text sense format", + "body_placeholder": "Gràcies pel vostre correu. Actualment estic fora de l'oficina i respondré quan torni.", + "html_label": "Missatge amb format (HTML)", + "html_description": "Afegiu una versió amb format enriquit, amb enllaços i estils. Els destinataris el client de correu dels quals no la pugui mostrar rebran el text sense format anterior.", + "html_placeholder": "Escriviu una resposta amb format per a fora de l'oficina…" + }, + "preview": { + "title": "Previsualització", + "show": "Mostra la previsualització", + "hide": "Amaga la previsualització" + }, + "save": "Desa els canvis", + "saving": "Desant...", + "warnings": { + "end_before_start": "La data de finalització ha de ser posterior a la data d'inici", + "invalid_date": "Introdueix una data d'inici i de finalització vàlides amb l'hora, o deixa el camp buit", + "start_in_past": "La data d'inici és al passat", + "empty_body": "El cos del missatge està buit - els destinataris rebran una resposta en blanc", + "subject_too_long": "L'assumpte és massa llarg per al servidor (com a màxim {max} bytes).", + "body_too_long": "El missatge és massa llarg per al servidor (com a màxim {max} bytes, amb el format inclòs).", + "forward_address": "Introdueix una adreça vàlida per al reenviament", + "not_running": "El reenviament o els destinataris de la resposta estan desats però ara no estan actius. Desa per tornar-los a activar." + }, + "forward": { + "title": "Reenviament", + "description": "Reenvia els missatges entrants a una altra adreça, amb resposta automàtica o sense, dins del període indicat a dalt (sense data de finalització, fins que es desactivi). El correu brossa no es reenvia.", + "enabled_label": "Reenvia els missatges", + "to_label": "Reenvia a", + "to_placeholder": "correu@exemple.com", + "keep_label": "Mantén a la safata d'entrada", + "keep_description": "Si no, el missatge només es reenvia: no es conserva en aquesta bústia i no s'hi aplica cap regla de filtre." + }, + "audience": { + "label": "Respon a", + "description": "Remitents interns: {domains}", + "all": "Tots els remitents", + "internal": "Només remitents interns", + "external": "Només remitents externs" + }, + "auto_reply": { + "title": "Resposta automàtica" + } + }, + "folders": { + "role_memos": "Notes", + "title": "Carpetes", + "description": "Gestioneu les carpetes de correu i assigneu-hi rols estàndard", + "folder_list": "Les vostres carpetes", + "folder_list_description": "Feu clic a la icona d'una carpeta per personalitzar-la", + "standard_roles": "Rols estàndard de carpeta", + "standard_roles_description": "Assigneu quines carpetes s'utilitzen per als rols estàndard de la bústia, com la safata d'entrada, els enviats, la paperera, etc.", + "role_inbox": "Safata d'entrada", + "role_drafts": "Esborranys", + "role_sent": "Enviats", + "role_trash": "Paperera", + "role_junk": "Correu brossa", + "role_archive": "Arxiu", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Cap", + "create_folder": "Crea una carpeta", + "create_subfolder": "Crea una subcarpeta", + "reorder": "Arrossegueu per reordenar", + "reorder_error": "No s'han pogut reordenar les carpetes", + "move_error": "No s'ha pogut moure la carpeta", + "select_folder": "Selecciona {name}", + "select_hint": "Selecciona. Maj+clic selecciona totes les carpetes fins a l'última on has fet clic.", + "selected_count": "{count, plural, one {# carpeta seleccionada} other {# carpetes seleccionades}}", + "move_to": "Mou a...", + "move_to_top_level": "Nivell superior", + "move": "Mou", + "clear_selection": "Esborra la selecció", + "folders_moved": "{count, plural, one {# carpeta moguda} other {# carpetes mogudes}}", + "move_partial_error": "{count, plural, one {No s'ha pogut moure # carpeta} other {No s'han pogut moure # carpetes}}", + "subfolder_of": "Dins de {name}", + "subfolder_name": "Nom de la subcarpeta", + "new_folder_name": "Nom de la carpeta", + "rename": "Canvia el nom", + "change_icon": "Canvia la icona", + "count_mode": "Comptador per a {name}", + "count_mode_default": "Comptador: per defecte", + "count_mode_unread": "Només no llegits", + "count_mode_all": "No llegits i total", + "count_mode_none": "Sense comptador", + "delete": "Suprimeix", + "confirm_delete": "Segur que voleu suprimir «{name}»? Els correus d'aquesta carpeta es mouran a la paperera.", + "create": "Crea", + "cancel": "Cancel·la", + "no_folders": "Cap carpeta personalitzada", + "cannot_delete_role": "No es pot suprimir una carpeta amb un rol estàndard. Elimineu primer el rol.", + "folder_created": "Carpeta creada", + "folder_renamed": "Nom de la carpeta canviat", + "folder_deleted": "Carpeta suprimida", + "role_updated": "Rol de la carpeta actualitzat", + "error_create": "No s'ha pogut crear la carpeta", + "error_rename": "No s'ha pogut canviar el nom de la carpeta", + "error_delete": "No s'ha pogut suprimir la carpeta", + "error_delete_has_children": "No es pot suprimir la carpeta: encara conté subcarpetes. Suprimiu-les o moveu-les primer.", + "error_delete_has_email": "No es pot suprimir la carpeta: encara conté correus. Moveu-los o suprimiu-los primer.", + "error_role": "No s'ha pogut actualitzar el rol de la carpeta" + }, + "advanced": { + "title": "Avançat", + "description": "Opcions avançades i configuració per a desenvolupadors", + "debug_mode": { + "label": "Mode de depuració", + "description": "Activa el registre detallat per a la resolució de problemes" + }, + "debug_categories": { + "description": "Seleccioneu quines categories registrar. Desactiveu les que no necessiteu per reduir el soroll a la consola.", + "jmap": "Client JMAP", + "jmap_description": "Operacions de bústia, obtenció de correus i sol·licituds del protocol JMAP", + "calendar": "Calendari", + "calendar_description": "Esdeveniments del calendari, importacions i missatges de programació", + "tasks": "Tasques", + "tasks_description": "Creació, obtenció i actualització de tasques del calendari", + "auth": "Autenticació", + "auth_description": "Inici de sessió, TOTP, intercanvi de testimonis i gestió de sessions", + "filters": "Filtres", + "filters_description": "Regles de filtre Sieve i scripts de resposta automàtica", + "email": "Visualització de correu", + "email_description": "Renderització de correu, processament TNEF i marcatge com a llegit", + "push": "Notificacions push", + "push_description": "Configuració i entrega de notificacions push", + "contacts": "Contactes i llibretes d'adreces", + "contacts_description": "Sincronització de contactes, operacions de llibreta d'adreces i remitents de confiança" + }, + "settings_sync": { + "label": "Sincronització de la configuració", + "description": "Sincronitzeu la configuració entre navegadors i dispositius" + }, + "sender_favicons": { + "label": "Icones dels remitents", + "description": "Mostra icones de llocs web com a imatges de perfil per als remitents d'empresa", + "refresh_label": "Actualitza les icones dels remitents", + "refresh_description": "Torna a obtenir les icones dels llocs web, per exemple quan un remitent ha canviat la seva", + "refresh_button": "Actualitza", + "refresh_done": "Es tornaran a obtenir les icones dels remitents" + }, + "show_avatars_in_junk": { + "label": "Mostra els avatars a la carpeta de brossa", + "description": "Mostra imatges de perfil i icones dels remitents a la carpeta de brossa. Desactivat per defecte per evitar donar aparença de legitimitat a intents de pesca electrònica." + }, + "keyboard_shortcuts": { + "label": "Dreceres de teclat", + "description": "Consulteu les dreceres de teclat disponibles", + "button": "Mostra les dreceres" + }, + "refresh_cache": { + "label": "Actualitza les dades emmagatzemades", + "description": "Torna a carregar els contactes, els calendaris i les carpetes des del servidor. Manté els comptes i les sessions — soluciona una vista obsoleta o incorrecta sense tancar la sessió.", + "button": "Actualitza" + }, + "reset_settings": { + "label": "Restableix la configuració", + "description": "Restaura tota la configuració als valors per defecte", + "button": "Restableix als valors per defecte" + }, + "export_settings": { + "label": "Exporta la configuració", + "description": "Baixeu la configuració en format JSON", + "button": "Exporta" + }, + "import_settings": { + "label": "Importa la configuració", + "description": "Carregueu la configuració des d'un fitxer JSON", + "button": "Importa" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Codi font" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Fes clic a la brossa i al phishing abans que arribin a la teva safata d'entrada. Deixa passar el correu real. Si perds els tres escuts, la safata cau.", + "kinds": { + "spam": "Brossa", + "phishing": "Phishing", + "legit": "Correu real" + }, + "click_to_block": "Fes clic per bloquejar", + "let_through": "Deixa'l passar", + "start": "Comença", + "play_again": "Torna a jugar", + "score": "Punts", + "combo": "Bonificació de ratxa", + "shields_left": "Queden {count} de {total} escuts", + "seconds": "{seconds} s", + "best": "Rècord: {score}", + "new_best": "Nou rècord", + "held": "La safata ha resistit", + "overrun": "La safata ha caigut", + "stats": { + "caught": "Bloquejats", + "delivered": "Lliurats", + "mistakes": "Errors" + } + } + }, + "sidebar_apps": { + "title": "Aplicacions de la barra lateral", + "description": "Gestioneu aplicacions i enllaços personalitzats a la barra lateral", + "keep_loaded": "Mantén les aplicacions carregades", + "keep_loaded_description": "Manté les aplicacions incrustades en execució en segon pla en canviar entre elles per evitar recarregar-les", + "managed_title": "Proporcionades per l'administració", + "managed_description": "Aquestes aplicacions estan configurades per a tothom i no es poden canviar.", + "manage_title": "Aplicacions personalitzades", + "manage_description": "Afegiu, editeu o elimineu aplicacions personalitzades de la barra lateral" + }, + "contacts": { + "title": "Contactes", + "description": "Importeu i exporteu els contactes", + "group_by_letter_label": "Agrupa per lletra inicial", + "sort_by_last_name_label": "Ordena per cognom", + "sort_by_last_name_description": "Ordena la llista de contactes per cognom perquè els membres d'una família apareguin junts", + "group_by_letter_description": "Mostra capçaleres de secció alfabètiques a la llista de contactes", + "import_label": "Importa contactes", + "import_description": "Importeu contactes des d'un fitxer vCard (.vcf)", + "export_label": "Exporta contactes", + "export_description": "Exporteu tots els contactes com a fitxer vCard (.vcf)", + "manage_title": "Llibretes d'adreces", + "manage_description": "Canvieu el nom de les llibretes d'adreces", + "no_address_books": "No s'ha trobat cap llibreta d'adreces", + "categories_title": "Categories", + "categories_description": "Canvieu el nom de les categories de contactes", + "no_categories": "No s'ha trobat cap categoria" + }, + "downloads": { + "title": "Baixades", + "description": "Personalitzeu com es nomenen els correus i fitxers adjunts baixats.", + "reset": "Restaura el valor per defecte", + "preview": "Previsualització:", + "email_template": { + "label": "Nom del fitxer de correu (.eml)", + "description": "Plantilla utilitzada en exportar un correu o arrossegar-lo al sistema de fitxers. L'extensió .eml s'afegeix automàticament." + }, + "attachment_template": { + "label": "Nom del fitxer adjunt", + "description": "Plantilla utilitzada en baixar o arrossegar un fitxer adjunt. Si ometeu «{filename}» i «{ext}», es conserva l'extensió original." + }, + "bundle_template": { + "label": "Nom del fitxer .zip de diversos correus", + "description": "Plantilla utilitzada en arrossegar o baixar diversos correus seleccionats com a un únic fitxer .zip. L'extensió .zip s'afegeix automàticament." + }, + "spaces": { + "label": "Espais", + "description": "Substitueix els espais del nom del fitxer resultant per un altre caràcter.", + "keep": "Mantén els espais", + "underscore": "Substitueix per _", + "dash": "Substitueix per -" + }, + "lowercase": { + "label": "Minúscules", + "description": "Força que tot el nom del fitxer estigui en minúscules." + }, + "strip_diacritics": { + "label": "Elimina els diacrítics", + "description": "Converteix les lletres accentuades als seus equivalents ASCII (ä → a, é → e). Útil per a eines que malmeten els noms de fitxer Unicode." + }, + "collapse_separators": { + "label": "Redueix els separadors repetits", + "description": "Redueix seqüències d'espais, guions baixos o guions a un sol caràcter." + }, + "after_export": { + "label": "Després d'exportar", + "description": "Opcionalment, moveu el correu després d'exportar-lo com a .eml.", + "keep": "Mantén-lo a la bústia", + "archive": "Mou a l'arxiu", + "trash": "Mou a la paperera" + } + }, + "filters": { + "title": "Filtres de correu", + "description": "Creeu regles per ordenar, etiquetar i gestionar automàticament els correus entrants", + "add_rule": "Afegeix una regla", + "no_rules": "Cap regla de filtre", + "no_rules_description": "Creeu regles per organitzar automàticament els correus entrants", + "vacation_active": "La resposta automàtica està activa", + "vacation_active_description": "La resposta automàtica està activada per als missatges entrants", + "vacation_configure": "Configura", + "edit_rule": "Edita la regla", + "new_rule": "Regla nova", + "delete_rule": "Suprimeix la regla", + "delete_confirm": "Segur que voleu suprimir aquesta regla?", + "enable": "Activa", + "disable": "Desactiva", + "raw_editor": "Editor Sieve en brut", + "raw_editor_warning": "Editar l'script Sieve en brut pot trencar l'edició visual de regles. Els canvis fets aquí substitueixen el constructor visual.", + "validate": "Valida", + "validation_success": "L'script és vàlid", + "validation_error": "L'script té errors", + "save": "Desa les regles", + "saving": "Desant...", + "saved": "Filtres desats correctament", + "save_failed": "No s'han pogut desar els filtres", + "loading": "Carregant els filtres...", + "not_supported": "El servidor de correu no admet filtres de correu.", + "rule_name": "Nom de la regla", + "rule_name_placeholder": "p. ex., Ordena els butlletins", + "match_all": "Coincideix amb TOTES les condicions", + "match_any": "Coincideix amb QUALSEVOL condició", + "conditions": "Condicions", + "add_condition": "Afegeix una condició", + "actions": "Accions", + "add_action": "Afegeix una acció", + "stop_processing": "Atura el processament de les regles següents", + "include_spam": "Mou també els missatges marcats com a correu brossa", + "attachment_type_placeholder": "p. ex. pdf, doc, jpg", + "value_placeholder_multi": "Valor (múltiples separats per comes)", + "condition_fields": { + "attachment": "Fitxer adjunt", + "from": "De", + "to": "Per a", + "cc": "CC", + "subject": "Assumpte", + "header": "Capçalera personalitzada", + "size": "Mida", + "body": "Cos", + "all": "Tots els missatges" + }, + "comparators": { + "has_any": "és present", + "has_type": "del tipus", + "contains": "conté", + "not_contains": "no conté", + "is": "és exactament", + "not_is": "no és", + "starts_with": "comença per", + "ends_with": "acaba amb", + "matches": "coincideix amb el patró", + "greater_than": "és més gran que", + "less_than": "és menys que", + "address_is": "és l'adreça", + "domain_is": "té el domini" + }, + "action_types": { + "move": "Mou a una carpeta", + "copy": "Copia a una carpeta", + "forward": "Reenvia a", + "mark_read": "Marca com a llegit", + "star": "Marca el missatge amb estrella", + "add_label": "Afegeix una etiqueta", + "discard": "Descarta (suprimeix silenciosament)", + "reject": "Rebutja amb un missatge", + "keep": "Mantén a la safata d'entrada", + "stop": "Atura el processament" + }, + "move_to_folder": "Selecciona una carpeta", + "copy_to_folder": "Selecciona una carpeta", + "forward_to": "Reenvia a l'adreça electrònica", + "forward_placeholder": "correu@exemple.com", + "forward_keep_copy": "Conserva'n una còpia", + "forward_limit": "Límit de reenviaments per missatge en aquest servidor: {count}. Els reenviaments addicionals s'ometen.", + "forward_unavailable": "Aquest servidor no permet reenviar. Les accions de reenviament de les regles de filtre no tenen cap efecte.", + "reject_message": "Missatge de rebuig", + "reject_placeholder": "El vostre correu ha estat rebutjat", + "label_name": "Nom de l'etiqueta", + "label_placeholder": "Selecciona una etiqueta", + "header_name": "Nom de la capçalera", + "header_placeholder": "p. ex., X-Mailing-List", + "size_bytes": "Mida en bytes", + "size_placeholder": "p. ex., 1000000", + "system_managed": "Regla gestionada pel sistema", + "opaque_warning": "Aquest script s'ha editat fora del constructor visual. Només està disponible l'edició Sieve en brut.", + "open_sieve_editor": "Obre l'editor Sieve en brut", + "fetch_error": "No s'han pogut carregar els filtres", + "expanded_view": "Vista ampliada", + "expanded_view_description": "Mostra les regles de filtre amb blocs detallats de condicions i accions", + "if": "Si", + "then": "Aleshores", + "match_all_conditions": "totes coincideixen", + "match_any_condition": "alguna coincideix", + "and": "i", + "or": "o", + "cancel": "Cancel·la", + "confirm_delete": "Suprimeix", + "rule_list": "Regles de filtre", + "drag_to_reorder": "Arrossegueu per reordenar", + "match_type": "Tipus de coincidència", + "reset_to_visual": "Torna al constructor visual", + "reset_warning": "Això descartarà l'script actual i començarà de nou.", + "confirm_reset": "Reinicia", + "validation_empty_name": "El nom de la regla és obligatori", + "validation_empty_conditions": "Cal com a mínim una condició amb un valor", + "validation_empty_actions": "Cal com a mínim una acció", + "templates_section": "Comença des d'una plantilla", + "template_newsletters": "Mou els butlletins a una carpeta", + "template_receipts": "Arxiva automàticament els rebuts", + "template_important": "Marca els correus importants", + "template_notifications": "Filtra les notificacions", + "sieve_editor": { + "title": "Editor d'scripts Sieve", + "warning": "Editar l'script Sieve en brut pot trencar l'edició visual de regles. Els canvis fets aquí substitueixen el constructor visual.", + "script_content": "Script Sieve", + "valid": "L'script és vàlid", + "invalid": "L'script té errors", + "save_warning": "En desar se sobreescriuran totes les regles visuals. Això no es pot desfer. Torneu a fer clic a Desa per confirmar-ho.", + "validating": "Validant...", + "validate": "Valida", + "cancel": "Cancel·la", + "save": "Desa", + "confirm_save": "Confirma el desament", + "validation_failed": "Ha fallat la sol·licitud de validació" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# condició} other {# condicions}}", + "actions_count": "{count, plural, one {# acció} other {# accions}}" + }, + "origin_external": "Extern", + "managed_by_tooltip": "Gestionat per {source}. Editeu-ho en aquesta aplicació, o utilitzeu l'editor Sieve en brut.", + "quick_rule_names": { + "move_sender": "Mou de {sender} a {folder}", + "move_domain": "Mou de @{domain} a {folder}", + "move_list": "Mou la llista {list} a {folder}", + "mark_read": "Marca com a llegits els de {sender}", + "tag": "Etiqueta com a {tag} els de {sender}", + "block": "Bloca {sender}", + "custom": "Missatges de {sender}", + "custom_plain": "Regla nova", + "senders": "{count, plural, one {# remitent} other {# remitents}}" + }, + "suggestions": "Suggeriments", + "suggest_subject": "Assumpte «{subject}»", + "suggest_to": "Per a {address}", + "suggest_cc": "A/c {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Aplica-ho també als missatges que ja hi ha en aquesta carpeta", + "apply_existing_unsupported": "Només les regles que comproven el remitent, els destinataris, l'assumpte o les capçaleres, i que mouen, copien, marquen com a llegit, destaquen o etiqueten, es poden aplicar a missatges existents.", + "period_title": "Interval de dates", + "period_start": "Data d'inici", + "period_end": "Data de finalització", + "validation_period_order": "La data de finalització ha de ser posterior a la data d'inici", + "period_toggle": "Activa només durant un període", + "period_hint": "S'aplica als missatges que arriben durant aquest període. Deixa un camp buit per no limitar aquell extrem.", + "validation_period_empty": "Introdueix una data d'inici o de finalització per al període", + "validation_period_invalid": "Introdueix una data vàlida amb l'hora per al període, o deixa el camp buit", + "apply_existing_period": "Una regla amb període només actua sobre els missatges que arriben dins d'aquest període, de manera que no es pot aplicar als missatges existents.", + "period_status_scheduled": "programada", + "period_status_active": "activa", + "period_status_expired": "caducada" + }, + "templates": { + "title": "Plantilles de correu", + "description": "Creeu plantilles de correu reutilitzables amb variables d'espai reservat", + "add": "Plantilla nova", + "edit": "Edita la plantilla", + "name": "Nom de la plantilla", + "name_placeholder": "p. ex., Correu de seguiment", + "category": "Categoria", + "category_placeholder": "p. ex., Feina, Personal", + "subject": "Assumpte", + "subject_placeholder": "Línia d'assumpte del correu", + "body": "Cos", + "body_placeholder": "Contingut del cos del correu...", + "recipients_placeholder": "correu@exemple.com", + "identity": "Envia com a", + "default_identity": "Identitat predeterminada", + "favorite": "Preferit", + "cancel": "Cancel·la", + "create": "Crea", + "update": "Actualitza", + "confirm_delete": "Suprimeix", + "no_templates": "Encara no hi ha plantilles", + "manage": "Gestiona les plantilles", + "count": "{count, plural, one {# plantilla} other {# plantilles}}", + "export_import": "Exporta i importa", + "export_import_description": "Feu una còpia de seguretat de les plantilles o transferiu-les a un altre dispositiu", + "export": "Exporta", + "import": "Importa", + "validation": { + "empty": "El nom de la plantilla és obligatori", + "too_long": "El nom de la plantilla ha de tenir com a màxim 200 caràcters" + } + }, + "files": { + "display": { + "title": "Visualització", + "description": "Configureu com es mostren els fitxers i les carpetes" + }, + "default_view": { + "label": "Vista predeterminada", + "description": "Trieu entre disposició de quadrícula i de llista", + "list": "Llista", + "grid": "Quadrícula" + }, + "default_sort": { + "label": "Ordenació predeterminada", + "description": "Trieu l'ordenació predeterminada dels fitxers", + "name": "Nom", + "size": "Mida", + "modified": "Modificat" + }, + "sort_direction": { + "label": "Direcció de l'ordenació", + "description": "Trieu ordre ascendent o descendent", + "ascending": "Ascendent", + "descending": "Descendent" + }, + "icons": { + "title": "Icones", + "description": "Configureu l'aparença de les icones de fitxer" + }, + "show_icons": { + "label": "Mostra les icones de fitxer", + "description": "Mostra icones al costat dels fitxers i les carpetes" + }, + "colored_icons": { + "label": "Icones de colors", + "description": "Utilitza icones de colors en lloc de monocromes" + }, + "show_thumbnails": { + "label": "Mostra les miniatures", + "description": "Mostra previsualitzacions d'imatge en lloc d'icones per als fitxers d'imatge" + }, + "behavior": { + "title": "Comportament", + "description": "Configureu el comportament del navegador de fitxers" + }, + "show_hidden": { + "label": "Mostra els fitxers ocults", + "description": "Mostra els fitxers i les carpetes que comencen per un punt" + }, + "preview": { + "label": "Previsualització" + } + } + }, + "errors": { + "page_error_title": "S'ha produït un error", + "page_error_description": "S'ha produït un error inesperat. Torneu-ho a provar o torneu a la pàgina d'inici.", + "sidebar_error": "No s'han pogut carregar les bústies", + "email_list_error": "No s'han pogut carregar els correus", + "viewer_error_title": "No s'ha pogut mostrar el correu", + "viewer_error_description": "S'ha produït un problema en renderitzar aquest correu. És possible que contingui contingut no admès.", + "composer_error": "No s'ha pogut carregar el redactor", + "settings_error_title": "Configuració no disponible", + "settings_error_description": "No s'ha pogut carregar la configuració. És possible que les preferències no es desin.", + "try_again": "Torna-ho a provar", + "reload": "Torna a carregar", + "reload_emails": "Torna a carregar els correus", + "reload_settings": "Torna a carregar la configuració", + "retry": "Reintenta", + "go_home": "Vés a la safata d'entrada" + }, + "context_menu": { + "reply": "Respon", + "reply_all": "Respon a tots", + "forward": "Reenvia", + "mark_read": "Marca com a llegit", + "mark_unread": "Marca com a no llegit", + "star": "Marca amb estrella", + "unstar": "Treu l'estrella", + "pin": "Fixa", + "unpin": "Desfixa", + "move_to": "Mou a...", + "copy_to_account": "Copia a un compte...", + "remove_from_folder": "Treu d'aquesta carpeta", + "archive": "Arxiva", + "delete": "Suprimeix", + "mark_as_spam": "Denuncia com a brossa", + "not_spam": "No és brossa", + "tag": "Etiqueta", + "items_selected": "{count} correus seleccionats", + "forward_as_attachments": "Reenvia com a fitxers adjunts", + "edit_draft": "Edita l'esborrany", + "cancel_scheduled_send": "Cancel·la l'enviament", + "reschedule_send": "Reprograma", + "cancel_and_edit": "Cancel·la i edita", + "cancel_and_compose_again": "Cancel·la i torna a redactar", + "rules": { + "title": "Regles", + "move_from": "Mou sempre els missatges de {sender}", + "move_from_domain": "Mou sempre els missatges de @{domain}", + "move_from_list": "Mou sempre els missatges d'aquesta llista", + "mark_read_from": "Marca sempre com a llegits els missatges de {sender}", + "tag_with": "Etiqueta sempre amb", + "block": "{count, plural, one {Bloca el remitent} other {Bloca # remitents}}", + "no_junk": "Aquest compte no té carpeta de correu brossa", + "create": "Crea una regla…", + "manage": "Gestiona les regles", + "senders_count": "{count, plural, one {# remitent} other {# remitents}}", + "cross_account": "Selecciona missatges d'un sol compte", + "opaque_hint": "Els teus filtres s'han editat a mà. Obre la configuració dels filtres" , + "loading": "S'estan carregant les regles…", + "load_failed": "No s'han pogut carregar les regles", + "none": "Encara no hi ha regles", + "new_mail_only": "Només per a missatges nous", + "running": "S'està executant «{name}» a {folder}…" + }, + "find_emails_from": "Cerca correus de {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Marca la carpeta com a llegida", + "mark_folder_tree_read": "Marca la carpeta i les subcarpetes com a llegides", + "mark_all_folders_read": "Marca totes les carpetes com a llegides", + "new_subfolder": "Subcarpeta nova...", + "new_folder": "Carpeta nova...", + "rename": "Canvia el nom...", + "share": "Comparteix...", + "import_email": "Importa .eml o .zip...", + "empty_folder": "Buida la carpeta", + "empty_folder_generic": "Buida la carpeta", + "delete_folder": "Suprimeix la carpeta", + "refresh": "Actualitza", + "hide_shared_account": "Amaga de la barra lateral", + "mute_notifications": "Silencia les notificacions", + "unmute_notifications": "Activa les notificacions", + "mute_account_notifications": "Silencia les notificacions d'aquest compte", + "unmute_account_notifications": "Activa les notificacions d'aquest compte", + "reset_folder_notifications": "Restableix les notificacions de les carpetes", + "mark_all_confirm_title": "Marca totes les carpetes com a llegides", + "mark_all_confirm_message": "Voleu marcar tots els missatges no llegits del vostre compte personal com a llegits?", + "delete_confirm_title": "Suprimeix la carpeta", + "delete_confirm_message": "Voleu suprimir definitivament la carpeta «{name}»? Aquesta acció no es pot desfer.", + "prompt_new_subfolder": "Introduïu un nom per a la subcarpeta nova.", + "prompt_new_folder": "Introduïu un nom per a la carpeta nova.", + "prompt_rename": "Introduïu un nom nou per a aquesta carpeta.", + "placeholder_folder_name": "Nom de la carpeta", + "create": "Crea", + "rename_confirm": "Canvia el nom", + "toast_marked_read": "Carpeta marcada com a llegida", + "toast_marked_read_count": "{count, plural, one {S'ha marcat 1 missatge com a llegit} other {S'han marcat # missatges com a llegits}}", + "toast_already_read": "No hi ha missatges no llegits", + "toast_marked_all_read": "Totes les carpetes marcades com a llegides", + "toast_emptied": "Carpeta buidada", + "toast_folder_created": "Carpeta creada", + "toast_folder_renamed": "Nom de la carpeta canviat", + "toast_folder_deleted": "Carpeta suprimida", + "toast_error_mark_read": "No s'ha pogut marcar com a llegit", + "toast_error_empty": "No s'ha pogut buidar la carpeta", + "toast_error_create": "No s'ha pogut crear la carpeta", + "toast_error_rename": "No s'ha pogut canviar el nom de la carpeta", + "toast_error_delete": "No s'ha pogut suprimir la carpeta", + "toast_error_delete_has_children": "La carpeta té subcarpetes. Elimineu-les primer.", + "toast_error_delete_has_email": "La carpeta no és buida. Buideu-la primer.", + "delete_with_contents_title": "La carpeta no està buida", + "delete_with_contents_message": "La carpeta \"{name}\" encara conté {count, plural, one {1 missatge} other {# missatges}}. Vols suprimir la carpeta i tots els seus missatges? No es pot desfer.", + "delete_with_contents_confirm": "Suprimeix la carpeta i els missatges" + }, + "shortcuts": { + "title": "Dreceres de teclat", + "tip": "Premeu ? en qualsevol moment per mostrar aquesta ajuda", + "sections": { + "navigation": "Navegació", + "actions": "Accions de correu", + "global": "Global", + "threads": "Fils", + "composer": "Redactor" + }, + "navigation": { + "next_email": "Correu següent", + "previous_email": "Correu anterior", + "open_email": "Obre el correu", + "close_email": "Tanca / Desselecciona", + "go_to_folder": "Vés a la carpeta", + "next_unread": "Següent correu no llegit", + "no_more_unread": "No hi ha més correus no llegits més avall a la llista" + }, + "actions": { + "reply": "Respon", + "reply_all": "Respon a tots", + "forward": "Reenvia", + "star": "Commuta l'estrella", + "archive": "Arxiva", + "delete": "Suprimeix", + "mark_unread": "Marca com a no llegit", + "mark_read": "Marca com a llegit", + "toggle_spam": "Denuncia com a brossa / No és brossa" + }, + "global": { + "compose": "Redacta un correu nou", + "search": "Enfoca la cerca", + "help": "Mostra les dreceres", + "refresh": "Actualitza els correus", + "select_all": "Selecciona-ho tot", + "global_search": "Cerca-ho tot", + "extend_selection": "Amplia la selecció cap avall / amunt" + }, + "threads": { + "expand_collapse": "Desplega/replega el fil", + "select_mountain_view": "Selecciona la conversa" + }, + "composer": { + "send": "Envia el correu", + "schedule_send": "Programa l'enviament", + "template_picker": "Obre el selector de plantilles" + } + }, + "threads": { + "messages_one": "{count} missatge", + "messages_other": "{count} missatges", + "messages_tooltip": "{count, plural, one {# missatge en aquesta conversa} other {# missatges en aquesta conversa}}", + "expand": "Desplega la conversa", + "collapse": "Replega la conversa", + "loading": "Carregant la conversa...", + "mark_read": "Marca la conversa com a llegida", + "mark_unread": "Marca la conversa com a no llegida", + "archive": "Arxiva la conversa", + "delete": "Suprimeix la conversa", + "star": "Marca la conversa amb estrella", + "unstar": "Treu l'estrella de la conversa", + "toggle_thread": "Commuta el fil" + }, + "identities": { + "modal_title": "Gestiona les identitats d'enviament", + "create_new": "Crea una identitat nova", + "edit_identity": "Edita la identitat", + "delete_confirm": "Voleu suprimir aquesta identitat? Això no es pot desfer.", + "cannot_delete": "Aquesta identitat no es pot suprimir", + "primary_identity": "Principal", + "set_as_primary": "Estableix com a principal", + "no_identities": "No s'ha trobat cap identitat", + "display": { + "reply_to": "Respon a:", + "bcc": "CCO:", + "signature": "Signatura:", + "preview": "Previsualització:" + }, + "validation_errors": { + "invalid_emails": "Correus electrònics no vàlids: {emails}", + "unknown_error": "Error desconegut" + }, + "form": { + "name_label": "Nom mostrat", + "name_placeholder": "p. ex., Correu de feina, Personal", + "name_required": "El nom és obligatori", + "email_label": "Adreça electrònica", + "email_placeholder": "el.vostre.correu@exemple.com", + "email_required": "El correu electrònic és obligatori", + "email_invalid": "Introduïu una adreça electrònica vàlida", + "email_immutable": "L'adreça electrònica no es pot canviar després de crear-la", + "reply_to_label": "Respon a (opcional)", + "reply_to_placeholder": "diferent@exemple.com", + "bcc_label": "CCO automàtica (opcional)", + "bcc_placeholder": "arxiu@exemple.com", + "text_signature_label": "Signatura de text", + "html_signature_label": "Signatura HTML", + "signature_byte_counter": "{bytes} / {max} bytes", + "signature_byte_limit_reached": "S'ha arribat al límit del servidor", + "save": "Desa la identitat", + "cancel": "Cancel·la", + "creating": "Creant...", + "updating": "Actualitzant..." + }, + "sub_address": { + "button_tooltip": "Utilitza subadreça", + "popover_title": "Afegeix una etiqueta de subadreça", + "tag_input_placeholder": "Introduïu una etiqueta (p. ex., compres)", + "preview_label": "Previsualització:", + "recent_tags": "Etiquetes recents", + "suggested_tags": "Suggerides", + "use_address": "Utilitza aquesta adreça", + "invalid_tag": "L'etiqueta només pot contenir caràcters alfanumèrics i guions", + "tag_too_long": "L'etiqueta ha de tenir com a màxim 30 caràcters", + "help_text": "Els correus enviats a user{delimiter}tag@domain.com arribaran a la vostra safata d'entrada", + "validation": { + "empty": "L'etiqueta no pot estar buida", + "too_long": "L'etiqueta ha de tenir com a màxim {max} caràcters", + "invalid_chars": "L'etiqueta només pot contenir lletres, números i guions" + } + }, + "badge": { + "sent_via": "via", + "sub_address_tag": "Enviat mitjançant la subadreça: {tag}", + "identity_name": "Enviat mitjançant la identitat: {name}", + "identity_short": "via {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Suprimeix", + "delete_confirm_title": "Suprimeix la identitat" + }, + "templates": { + "picker_title": "Trieu una plantilla", + "search_placeholder": "Cerca plantilles...", + "section_favorites": "Preferides", + "section_recent": "Recents", + "section_uncategorized": "Altres", + "no_templates": "Encara no hi ha plantilles", + "no_results": "No s'ha trobat cap plantilla", + "fill_placeholders": "Emplena els valors de les variables", + "enter_value": "Introduïu un valor...", + "preview": "Previsualització", + "insert_with_values": "Insereix amb els valors", + "insert_raw": "Insereix sense processar", + "copy_suffix": "(còpia)", + "placeholder": "Variable", + "placeholders": { + "recipient_name": "Nom del destinatari", + "company": "Nom de l'empresa", + "date": "Data actual", + "day_of_week": "Dia de la setmana", + "sender_name": "El vostre nom" + } + }, + "contacts": { + "directory": { + "title": "Directori", + "search_placeholder": "Cerca un nom o una adreça electrònica", + "refresh": "Actualitza el directori", + "retry": "Torna-ho a provar", + "loading": "S'està carregant el directori…", + "unavailable_title": "Sense directori", + "unavailable_hint": "El teu servidor de correu no ofereix un directori de comptes.", + "forbidden_title": "Directori no disponible", + "forbidden_hint": "El teu servidor de correu no permet que aquest compte consulti el directori. Pregunta a l'administrador.", + "error_title": "No s'ha pogut carregar el directori", + "error_hint": "Comprova la connexió i torna-ho a provar.", + "empty_title": "El directori és buit", + "empty_hint": "El teu servidor de correu no llista cap compte amb adreça electrònica.", + "no_match": "Ningú del directori coincideix amb la cerca", + "refresh_failed": "No s'ha pogut actualitzar el directori. Es mostra l'última llista carregada.", + "select_page": "Selecciona tothom d'aquesta pàgina", + "deselect_page": "Desselecciona tothom d'aquesta pàgina", + "select_entry": "Selecciona {name}", + "range": "{from}–{to} de {total}", + "page": "Pàgina {page} de {count}", + "previous_page": "Pàgina anterior", + "next_page": "Pàgina següent", + "picker_title": "Selecciona del directori", + "cancel": "Cancel·la", + "selected_count": "{count} seleccionats", + "add_to": "Afegeix a Per a", + "add_cc": "Afegeix a A/c", + "add_bcc": "Afegeix a C/o", + "select_hint": "Selecciona algú per veure'n els detalls", + "write_email": "Escriu un correu", + "add_to_contacts": "Afegeix als contactes", + "email": "Correu", + "read_only_note": "Les entrades del directori vénen del teu servidor de correu i no es poden editar aquí.", + "type": { + "individual": "Persona", + "group": "Grup", + "resource": "Recurs", + "location": "Ubicació", + "other": "Altre" + } + }, + "title": "Contactes", + "search_placeholder": "Cerca contactes...", + "create_new": "Contacte nou", + "no_category": "Sense categoria", + "rename_category": "Canvia el nom de la categoria", + "category_name_label": "Nom de la categoria", + "category_renamed": "Nom de la categoria canviat", + "category_rename_failed": "No s'ha pogut canviar el nom de la categoria", + "category_added": "Contacte afegit a {name}", + "category_added_plural": "{count} contactes afegits a {name}", + "empty_state": "Encara no hi ha contactes", + "empty_state_title": "Encara no hi ha contactes", + "empty_state_subtitle": "Creeu el primer contacte o importeu-lo des d'un fitxer vCard", + "empty_search": "Cap contacte coincideix amb la cerca", + "empty_search_hint": "Proveu amb un altre terme de cerca", + "empty_filtered": "Cap contacte coincideix amb els filtres", + "empty_filtered_hint": "Proveu d'ajustar o netejar els filtres", + "clear_search": "Neteja la cerca", + "import_vcard": "Importa vCard", + "delete_confirm_title": "Suprimeix el contacte", + "delete_confirm": "Segur que voleu suprimir aquest contacte?", + "local_mode": "Els contactes es desen localment (el servidor no admet JMAP Contacts)", + "back_to_contacts": "Torna als contactes", + "back_to_email": "Torna al correu", + "open_categories": "Obre les categories", + "tabs": { + "all": "Tots", + "groups": "Grups" + }, + "shared": { + "title": "Compartit" + }, + "address_books": { + "create": "Llibreta d'adreces nova", + "created": "Llibreta d'adreces creada", + "create_failed": "No s'ha pogut crear la llibreta d'adreces", + "title": "Les meves llibretes d'adreces", + "shared_prefix": "Compartida: {name}", + "moved": "Contacte mogut a {name}", + "moved_plural": "{count} contactes moguts a {name}", + "move_failed": "No s'ha pogut moure el contacte", + "address_book": "Llibreta d'adreces", + "rename": "Canvia el nom de la llibreta d'adreces", + "name_label": "Nom de la llibreta d'adreces", + "renamed": "Nom de la llibreta d'adreces canviat", + "rename_failed": "No s'ha pogut canviar el nom de la llibreta d'adreces", + "default": "Predeterminada", + "set_default": "Estableix com a predeterminada", + "default_updated": "Llibreta d’adreces predeterminada actualitzada", + "set_default_failed": "No s’ha pogut establir la llibreta d’adreces predeterminada", + "manage": "Gestiona les llibretes d'adreces", + "share": "Comparteix la llibreta d'adreces", + "new_contact_in_book": "Contacte nou en aquesta llibreta d'adreces", + "delete": "Suprimeix la llibreta d'adreces", + "confirm_delete": "Voleu suprimir «{name}»? S'eliminaran tots els contactes d'aquesta llibreta d'adreces.", + "deleted": "Llibreta d'adreces suprimida", + "delete_failed": "No s'ha pogut suprimir la llibreta d'adreces" + }, + "detail": { + "emails": "Adreces electròniques", + "phones": "Números de telèfon", + "organizations": "Organitzacions", + "addresses": "Adreces", + "notes": "Notes", + "titles": "Càrrecs i funcions", + "online_services": "Serveis en línia", + "anniversaries": "Commemoracions", + "personal_info": "Informació personal", + "languages": "Idiomes", + "categories": "Categories", + "related_contacts": "Contactes relacionats", + "crypto_keys": "Claus criptogràfiques", + "cert_issuer": "Emissor", + "cert_expires": "Caduca", + "cert_expired": "Caducat", + "cert_fingerprint": "Empremta digital", + "cert_algorithm": "Algorisme", + "import_to_smime": "Importa a S/MIME", + "cert_already_imported": "Ja importat a S/MIME", + "cert_imported": "Certificat importat a l'emmagatzematge S/MIME", + "cert_import_failed": "No s'ha pogut importar el certificat", + "no_contact_selected": "Seleccioneu un contacte per veure'n els detalls", + "compose_email": "Redacta un correu", + "copy_email": "Copia el correu electrònic", + "copy_phone": "Copia el número de telèfon", + "copy_url": "Copia l'URL", + "copied": "Copiat al porta-retalls", + "copy_failed": "No s'ha pogut copiar al porta-retalls", + "created": "Creat", + "updated": "Última actualització", + "timezone": "Fus horari", + "anniversary_birth": "Aniversari", + "anniversary_death": "Traspàs", + "anniversary_wedding": "Commemoració", + "anniversary_other": "Altres", + "personal_expertise": "Especialitat", + "personal_hobby": "Afició", + "personal_interest": "Interès", + "personal_other": "Altres", + "gender": "Gènere", + "gender_masculine": "Home", + "gender_feminine": "Dona", + "gender_other": "Altres", + "gender_none": "No aplicable", + "gender_unknown": "Desconegut", + "calendar": "Calendari", + "calendar_uri": "URL del calendari", + "scheduling_uri": "URL de programació", + "freebusy_uri": "URL de disponibilitat", + "section_contact": "Dades de contacte", + "section_work": "Feina", + "section_personal": "Personal", + "email_default_label": "Correu electrònic", + "phone_default_label": "Telèfon", + "address_default_label": "Adreça", + "online_service_default_label": "En línia", + "organization_label": "Organització", + "title_label": "Càrrec", + "role_label": "Funció", + "language_label": "Idioma", + "related_default_label": "Relacionat", + "more_actions": "Més accions", + "age_years": "{count, plural, one {1 any} other {# anys}}", + "years_since": "{count, plural, one {1 any} other {# anys}}" + }, + "activity": { + "recent_emails": "Correus recents", + "upcoming_events": "Propers esdeveniments", + "no_emails": "Cap correu recent", + "no_events": "Cap esdeveniment proper", + "no_subject": "(Sense assumpte)", + "no_title": "(Sense títol)", + "load_failed": "No s'ha pogut carregar", + "unknown_sender": "Remitent desconegut", + "all_day": "Tot el dia" + }, + "form": { + "create_title": "Contacte nou", + "edit_title": "Edita el contacte", + "section_address_book": "Directori", + "select_address_book": "Seleccioneu un directori...", + "section_identity": "Nom i identitat", + "contact_type": "Tipus de contacte", + "type_person": "Persona", + "type_organization": "Organització", + "section_work": "Feina i organització", + "prefix": "Prefix", + "prefix_placeholder": "Dr., Sr., Sra.", + "given_name": "Nom", + "middle_name": "Segon nom", + "surname": "Cognoms", + "suffix": "Sufix", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Sobrenom", + "nickname_placeholder": "Sobrenom", + "email": "Correu electrònic", + "email_placeholder": "correu@exemple.com", + "phone": "Telèfon", + "phone_placeholder": "+1 234 567 890", + "phone_type": "Tipus", + "phone_voice": "Veu", + "phone_cell": "Mòbil", + "phone_fax": "Fax", + "phone_pager": "Buscapersones", + "phone_video": "Vídeo", + "phone_text": "SMS", + "organization": "Organització", + "organization_placeholder": "Nom de l'empresa", + "department": "Departament", + "department_placeholder": "Departament", + "job_title": "Càrrec", + "job_title_placeholder": "p. ex., Enginyer/a de programari", + "role": "Funció", + "role_placeholder": "p. ex., Cap d'equip", + "addresses": "Adreces", + "add_address": "Afegeix una adreça", + "street": "Carrer", + "city": "Ciutat", + "region": "Estat / Regió", + "postcode": "Codi postal", + "country": "País", + "online_services": "Serveis en línia", + "add_online_service": "Afegeix un servei en línia", + "url_placeholder": "https://...", + "service_placeholder": "Servei", + "anniversaries": "Commemoracions", + "add_anniversary": "Afegeix una data", + "anniversary_birth": "Aniversari", + "anniversary_wedding": "Commemoració", + "anniversary_death": "Traspàs", + "anniversary_other": "Altres", + "personal_info": "Informació personal", + "add_personal_info": "Afegeix una entrada", + "personal_info_placeholder": "p. ex., Fotografia", + "personal_expertise": "Especialitat", + "personal_hobby": "Afició", + "personal_interest": "Interès", + "personal_other": "Altres", + "level": "Nivell", + "level_high": "Alt", + "level_medium": "Mitjà", + "level_low": "Baix", + "categories": "Categories", + "categories_placeholder": "p. ex., Família, Amics, Companys", + "categories_hint": "Escriviu per cercar o afegir categories", + "category_add": "Afegeix", + "note": "Notes", + "note_placeholder": "Afegiu una nota...", + "gender": "Gènere", + "gender_sex": "Sexe", + "gender_male": "Home", + "gender_female": "Dona", + "gender_other": "Altres", + "gender_none": "No aplicable", + "gender_unknown": "Desconegut", + "gender_identity": "Identitat de gènere", + "gender_identity_placeholder": "Identitat de gènere...", + "calendar": "Calendari", + "calendar_uri": "URL del calendari", + "scheduling_uri": "URL de programació", + "freebusy_uri": "URL de disponibilitat", + "context_work": "Feina", + "context_private": "Privat", + "add_email": "Afegeix un correu electrònic", + "add_phone": "Afegeix un telèfon", + "save": "Desa", + "cancel": "Cancel·la", + "creating": "Creant...", + "updating": "Actualitzant...", + "name_required": "Introduïu un nom, un cognom o una organització", + "email_invalid": "Introduïu una adreça electrònica vàlida", + "email_error_inline": "Format de correu electrònic no vàlid", + "save_failed": "No s'ha pogut desar el contacte", + "delete": "Suprimeix", + "upload_photo": "Puja una foto", + "remove_photo": "Elimina la foto", + "photo_hint": "JPG o PNG, fins a 10 MB. Es redimensionarà.", + "photo_too_large": "La imatge és massa gran (màxim 10 MB)", + "photo_invalid": "Fitxer d'imatge no vàlid", + "change_photo": "Canvia" + }, + "groups": { + "create": "Grup nou", + "edit": "Edita el grup", + "empty": "Encara no hi ha grups", + "delete_confirm_title": "Suprimeix el grup", + "delete_confirm": "Segur que voleu suprimir aquest grup?", + "name_label": "Nom del grup", + "name_placeholder": "p. ex., Equip, Família", + "name_required": "El nom del grup és obligatori", + "save_failed": "No s'ha pogut desar el grup", + "members_label": "Membres", + "search_members": "Cerca contactes per afegir...", + "no_members": "Cap membre en aquest grup", + "member_count": "{count, plural, =0 {Cap membre} one {1 membre} other {# membres}}", + "send_email": "Envia un correu al grup", + "send_email_to": "Per a", + "send_email_cc": "CC", + "send_email_bcc": "CCO", + "no_member_emails": "Aquest grup no té cap membre amb adreça electrònica." + }, + "import": { + "title": "Importa contactes", + "drop_hint": "Feu clic per seleccionar un fitxer vCard", + "file_types": "Fitxers .vcf o .vcard", + "no_contacts": "No s'ha trobat cap contacte al fitxer", + "parse_error": "No s'ha pogut analitzar el fitxer vCard", + "found": "{count, plural, one {1 contacte trobat} other {# contactes trobats}}", + "duplicate": "Duplicat", + "select_all": "Selecciona-ho tot", + "deselect_all": "Desselecciona-ho tot", + "selected": "{count, plural, one {1 seleccionat} other {# seleccionats}}", + "import_button": "Importa", + "importing": "Important...", + "success": "{count, plural, one {1 contacte importat} other {# contactes importats}}", + "failed": "No s'ha pogut importar", + "close": "Tanca", + "file_too_large": "El fitxer és massa gran (màxim 5 MB)" + }, + "export": { + "title": "Exporta contactes", + "success": "{count, plural, one {1 contacte exportat} other {# contactes exportats}}" + }, + "bulk": { + "selected": "{count, plural, one {1 seleccionat} other {# seleccionats}}", + "select_all": "Selecciona-ho tot", + "delete": "Suprimeix", + "delete_confirm_title": "Suprimeix els contactes", + "delete_confirm": "Voleu suprimir {count, plural, one {1 contacte} other {# contactes}}?", + "deleted": "{count, plural, one {1 contacte suprimit} other {# contactes suprimits}}", + "add_to_group": "Afegeix al grup", + "choose_group": "Trieu un grup", + "adding_contacts": "Afegint {count, plural, one {1 contacte} other {# contactes}}", + "added_to_group": "Contactes afegits al grup", + "export": "Exporta", + "clear": "Neteja la selecció" + }, + "toast": { + "created": "Contacte creat", + "updated": "Contacte actualitzat", + "deleted": "Contacte suprimit", + "error_create": "No s'ha pogut crear el contacte", + "error_update": "No s'ha pogut actualitzar el contacte", + "error_delete": "No s'ha pogut suprimir el contacte" + }, + "context_menu": { + "open": "Obre", + "edit": "Edita", + "send_email": "Envia un correu", + "add_to_group": "Afegeix al grup", + "export_vcard": "Exporta com a vCard", + "delete": "Suprimeix", + "call": "Truca", + "duplicate": "Duplica", + "print": "Imprimeix" + }, + "filters": { + "toggle": "Filtres", + "select": "Selecciona", + "clear": "Neteja", + "close": "Tanca", + "title": "Filtres avançats", + "organization": "Empresa", + "organization_placeholder": "p. ex. Acme Corp", + "job_title": "Càrrec", + "job_title_placeholder": "p. ex. Dissenyador/a", + "location": "Ubicació", + "location_placeholder": "Ciutat o país", + "email_domain": "Domini del correu", + "email_domain_placeholder": "exemple.com", + "birthday_month": "Aniversari a", + "any_month": "Qualsevol mes", + "has_email": "Té correu electrònic", + "has_phone": "Té telèfon", + "has_photo": "Té foto", + "sort_by": "Ordena per", + "sort_first_name": "Nom", + "sort_last_name": "Cognom" + } + }, + "calendar": { + "title": "Calendari", + "back_to_email": "Torna al correu", + "back_to_month": "Torna al mes", + "my_calendars": "Calendaris", + "birthday_calendar": "Aniversaris", + "mini_calendar_change": "Feu clic per canviar de mes", + "views": { + "month": "Mes", + "week": "Setmana", + "day": "Dia", + "agenda": "Agenda", + "today": "Avui", + "month_hint": "Mes (m)", + "week_hint": "Setmana (w)", + "day_hint": "Dia (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Tasques", + "tasks_hint": "Tasques (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# esdeveniment anterior} other {# esdeveniments anteriors}}", + "hidden_after": "{count, plural, one {# esdeveniment posterior} other {# esdeveniments posteriors}}", + "show_all_hours": "Mostra totes les hores", + "show_display_hours": "Mostra només {start} – {end}", + "create": "Crea un esdeveniment", + "edit": "Edita l'esdeveniment", + "delete": "Suprimeix l'esdeveniment", + "details": "Detalls de l'esdeveniment", + "no_events": "Cap esdeveniment", + "all_day": "Tot el dia", + "more": "+{count} més", + "show_more": "Mostra'n més", + "show_less": "Mostra'n menys", + "no_title": "(Sense títol)", + "resize": "Redimensiona l'esdeveniment", + "duplicate": "Duplica", + "today_header": "Avui", + "tomorrow_header": "Demà", + "agenda_show_earlier": "Mostra esdeveniments anteriors", + "agenda_loading": "Carregant…", + "agenda_range_start": "Esdeveniments des del {date}", + "agenda_range_end": "Esdeveniments fins al {date}", + "export_ics": "Exporta com a .ics", + "copy_title": "Copia el títol", + "copy_link": "Copia l'enllaç de la reunió", + "new_event": "Esdeveniment nou", + "new_all_day_event": "Esdeveniment de tot el dia nou", + "new_task": "Tasca nova", + "go_to_today": "Vés a avui" + }, + "detail": { + "add_note": "Afegiu una nota...", + "save_note": "Desa", + "note_saved": "Nota afegida", + "open_link": "Obre l'enllaç", + "copy_location": "Copia la ubicació", + "meeting_link": "Enllaç de la reunió", + "tentative": "Provisional", + "cancelled": "Cancel·lat", + "delete_confirm": "Voleu suprimir aquest esdeveniment?" + }, + "form": { + "title": "Títol", + "description": "Descripció", + "location": "Ubicació", + "meeting_link": "Enllaç de la reunió", + "start_date": "Data d'inici", + "end_date": "Data de finalització", + "start_time": "Hora d'inici", + "end_time": "Hora de finalització", + "all_day_event": "Esdeveniment de tot el dia", + "calendar_select": "Calendari", + "save": "Desa", + "cancel": "Cancel·la", + "delete_confirm": "Segur que voleu suprimir aquest esdeveniment?", + "color": "Color" + }, + "participants": { + "availability": { + "title": "Disponibilitat", + "free": "Disponible", + "busy": "Ocupat", + "tentative": "Provisional", + "unknown": "No és un usuari d'aquest servidor", + "checking": "Comprovant…" + }, + "title": "Participants", + "add": "Afegeix un participant", + "organizer": "Organitzador", + "attendee": "Assistent", + "accepted": "Acceptat", + "declined": "Rebutjat", + "tentative": "Provisional", + "needs_action": "Cal resposta", + "remove": "Elimina", + "edit": "Edita", + "email_placeholder": "Afegiu una adreça electrònica o cerqueu contactes", + "send_invitations": "Envia invitacions als participants", + "status_summary": "{accepted} acceptats, {pending} pendents", + "invited_by": "Invitació de {name}", + "respond_below": "Responeu utilitzant els botons de sota", + "rsvp_label": "La vostra resposta", + "cancel_notification": "S'informarà els participants de la cancel·lació", + "you_organizer": "Sou l'organitzador", + "you_attendee": "Sou un assistent", + "no_participants": "Cap participant", + "count": "{count, plural, one {# participant} other {# participants}}" + }, + "recurrence": { + "title": "Repetició", + "none": "No es repeteix", + "daily": "Diàriament", + "weekly": "Setmanalment", + "monthly": "Mensualment", + "yearly": "Anualment", + "every_n_days": "Cada {count} dies", + "every_n_weeks": "Cada {count} setmanes", + "every_n_months": "Cada {count} mesos", + "until": "Fins a", + "occurrences": "{count} repeticions", + "custom": "Personalitzat…", + "edit_custom": "Edita la repetició personalitzada", + "every_n_years": "Cada {count} anys", + "on_days": "el {days}", + "on_day_n": "el dia {day}", + "on_the_nth": "el {nth} {day}", + "in_month": "al mes de {month}", + "nth_1": "primer", + "nth_2": "segon", + "nth_3": "tercer", + "nth_4": "quart", + "nth_last": "últim", + "editor_freq_day": "Dia", + "editor_freq_week": "Setmana", + "editor_freq_month": "Mes", + "editor_freq_year": "Any", + "editor_repeats_on": "Es repeteix el", + "editor_every": "Cada", + "editor_unit_days": "dia(dies)", + "editor_unit_weeks": "setmana(es)", + "editor_unit_months": "mes(os)", + "editor_unit_years": "any(s)", + "editor_on_day": "el dia", + "editor_on_the": "el", + "editor_in": "al", + "editor_ends": "Acaba", + "editor_never": "Mai", + "editor_ends_on": "El", + "editor_ends_after": "Després de", + "editor_occurrences": "repeticions" + }, + "recurrence_scope": { + "edit_title": "Edita l'esdeveniment periòdic", + "delete_title": "Suprimeix l'esdeveniment periòdic", + "description": "Aquest és un esdeveniment periòdic. Quins esdeveniments voleu modificar?", + "this_event": "Només aquest esdeveniment", + "this_and_future": "Aquest i els següents esdeveniments", + "all_events": "Tots els esdeveniments", + "cancel": "Cancel·la", + "save": "Desa", + "delete": "Suprimeix", + "rsvp_title": "Respon a l'esdeveniment periòdic", + "rsvp_description": "Aquest és un esdeveniment periòdic. A quins esdeveniments s'aplica la vostra resposta?", + "respond": "Respon" + }, + "alerts": { + "title": "Recordatoris", + "none": "Cap recordatori", + "at_time": "A l'hora de l'esdeveniment", + "minutes_before": "{count, plural, one {# minut abans} other {# minuts abans}}", + "hours_before": "{count, plural, one {# hora abans} other {# hores abans}}", + "days_before": "{count, plural, one {# dia abans} other {# dies abans}}", + "weeks_before": "{count, plural, one {# setmana abans} other {# setmanes abans}}", + "unit_minutes_before": "minuts abans", + "unit_hours_before": "hores abans", + "unit_days_before": "dies abans", + "unit_weeks_before": "setmanes abans", + "add": "Afegeix un recordatori", + "remove": "Elimina el recordatori", + "amount": "Quantitat del recordatori", + "unit": "Unitat del recordatori", + "action": "Tipus de recordatori", + "action_display": "Notificació", + "action_email": "Correu electrònic", + "by_email": "{label} per correu electrònic", + "kept": "{count, plural, one {Es conserva # recordatori més que no es pot editar aquí} other {Es conserven # recordatoris més que no es poden editar aquí}}", + "other": "{count, plural, one {# recordatori més} other {# recordatoris més}}", + "calendar_defaults": "Recordatoris predeterminats del calendari", + "customize": "Personalitza", + "use_defaults": "Utilitza els recordatoris predeterminats del calendari" + }, + "settings": { + "limit_hours": "Limita les hores visibles", + "limit_hours_desc": "Mostra només aquestes hores a les vistes de dia i setmana. Els esdeveniments fora d'aquestes hores continuen accessibles amb les fletxes de dalt i de baix de cada dia.", + "visible_hours": "Hores visibles", + "visible_hours_start": "Inici de les hores visibles", + "visible_hours_end": "Final de les hores visibles", + "hide_non_working_days": "Amaga els dies no laborables", + "hide_non_working_days_desc": "Treu de la vista setmanal els dies que no treballes. Els seus esdeveniments continuen apareixent a les vistes de mes, dia i agenda.", + "working_days": "Dies laborables", + "title": "Configuració del calendari", + "default_view": "Vista predeterminada", + "week_starts_on": "La setmana comença el", + "time_format": "Format d'hora", + "default_calendar": "Calendari predeterminat", + "default_reminder": "Recordatori predeterminat", + "time_format_12h": "12 hores", + "time_format_24h": "24 hores", + "notifications_enabled": "Notificacions d'esdeveniments", + "notifications_enabled_desc": "Mostra avisos per als esdeveniments propers del calendari", + "notification_sound": "So de notificació", + "notification_sound_desc": "Reprodueix un so per als avisos del calendari", + "invitation_parsing": "Analitza les invitacions per correu", + "invitation_parsing_desc": "Detecta les invitacions de calendari als fitxers adjunts del correu i mostra les accions del calendari", + "show_time_in_month_view": "Mostra l'hora a la vista mensual", + "show_time_in_month_view_desc": "Mostra l'hora dels esdeveniments a la vista mensual del calendari. En pantalles petites, això mostra entrades completes d'esdeveniment en lloc de punts.", + "show_week_numbers": "Mostra els números de setmana", + "show_week_numbers_desc": "Mostra els números de setmana al minicalendari", + "calendar_free_scroll": "Desplaçament lliure", + "calendar_free_scroll_desc": "Desplaça't de manera contínua per mesos, setmanes i dies en lloc d'un període cada vegada", + "enable_tasks": "Activa les tasques", + "enable_tasks_desc": "Mostra una vista de tasques al calendari per gestionar pendents", + "show_tasks_on_calendar": "Mostra les tasques al calendari", + "show_tasks_on_calendar_desc": "Mostra les tasques a les vistes de mes, setmana i dia del calendari", + "hover_preview": "Previsualització en passar el cursor", + "hover_preview_desc": "Mostra una finestra emergent de detalls en passar el cursor per sobre dels esdeveniments", + "hover_preview_instant": "Instantània", + "hover_preview_delay_500ms": "Retard de 0,5 segons", + "hover_preview_delay_1s": "Retard d'1 segon", + "hover_preview_delay_2s": "Retard de 2 segons", + "hover_preview_off": "Desactivada", + "organizer_identity": "Organitza les invitacions com a", + "organizer_identity_desc": "L'adreça de calendari usada com a organitzador quan convides gent als esdeveniments.", + "organizer_identity_failed": "No s'ha pogut canviar l'adreça de l'organitzador", + "show_birthday_calendar": "Calendari d'aniversaris dels contactes", + "show_birthday_calendar_desc": "Mostra un calendari virtual amb els aniversaris dels vostres contactes" + }, + "days": { + "monday": "Dilluns", + "tuesday": "Dimarts", + "wednesday": "Dimecres", + "thursday": "Dijous", + "friday": "Divendres", + "saturday": "Dissabte", + "sunday": "Diumenge", + "mon": "Dl", + "tue": "Dt", + "wed": "Dc", + "thu": "Dj", + "fri": "Dv", + "sat": "Ds", + "sun": "Dg" + }, + "months": { + "jan": "Gen", + "feb": "Feb", + "mar": "Mar", + "apr": "Abr", + "may": "Maig", + "jun": "Juny", + "jul": "Jul", + "aug": "Ag", + "sep": "Set", + "oct": "Oct", + "nov": "Nov", + "dec": "Des", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Esdeveniment creat", + "event_updated": "Esdeveniment actualitzat", + "event_deleted": "Esdeveniment suprimit", + "calendar_created": "Calendari creat", + "calendar_deleted": "Calendari suprimit", + "event_move_error": "No s'ha pogut moure l'esdeveniment", + "event_resize_error": "No s'ha pogut redimensionar l'esdeveniment", + "alert_title": "Esdeveniment proper", + "alert_now": "Comença ara", + "alert_in_minutes": "D'aquí a {count} min", + "invitation_sent": "Invitacions enviades", + "invitations_denied_title": "No es poden enviar les invitacions", + "invitations_denied": "El servidor ha refusat enviar les invitacions: {reason}. Voleu desar l'esdeveniment sense enviar-les?", + "save_without_invitations": "Desa sense invitacions", + "rsvp_updated": "Resposta actualitzada", + "rsvp_error": "No s'ha pogut actualitzar la resposta", + "event_duplicated": "Esdeveniment duplicat", + "event_error": "No s'ha pogut desar l'esdeveniment", + "task_due": "Tasca pendent", + "event_exported": "Esdeveniment exportat", + "title_copied": "Títol copiat", + "link_copied": "Enllaç copiat" + }, + "status": { + "loading_calendars": "Carregant els calendaris...", + "loading_events": "Carregant els esdeveniments..." + }, + "quick_create": { + "placeholder": "Títol del nou esdeveniment", + "aria_label": "Crea un esdeveniment ràpidament", + "add_title": "Afegeix un títol", + "more_options": "Més opcions" + }, + "nav_prev": "Anterior", + "nav_next": "Següent", + "nav_open_menu": "Obre el menú", + "import": { + "title": "Importa un calendari", + "tab_file": "Fitxer", + "tab_url": "URL", + "select_file": "Selecciona un fitxer .ics", + "drop_file": "o deixeu anar el fitxer aquí", + "supported_formats": "Admet fitxers iCalendar (.ics)", + "url_description": "Introduïu l'URL d'un canal iCalendar (.ics) extern per importar-ne els esdeveniments.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Admet URL d’iCalendar (.ics)", + "fetch": "Obtén", + "invalid_url": "Introduïu un URL vàlid", + "url_fetch_failed": "No s'ha pogut obtenir el calendari des de l'URL", + "parsing": "Analitzant el fitxer del calendari...", + "parsed_events": "{count} esdeveniments trobats", + "no_events": "No s'ha trobat cap esdeveniment al fitxer", + "select_all": "Selecciona-ho tot", + "deselect_all": "Desselecciona-ho tot", + "target_calendar": "Importa al calendari", + "import_button": "Importa la selecció", + "importing": "Important els esdeveniments...", + "success": "{count} esdeveniments importats correctament", + "error": "No s'ha pogut importar el calendari", + "file_too_large": "El fitxer supera el límit de 10 MB", + "invalid_format": "Format de fitxer de calendari no vàlid" + }, + "webcal_action": { + "title": "Obre l'enllaç de calendari", + "description": "Com voleu utilitzar «{name}»?", + "import_title": "Importa una vegada", + "import_description": "Obté els esdeveniments ara i copia'ls a un dels vostres calendaris.", + "subscribe_title": "Subscriu-te", + "subscribe_description": "Mantén aquest calendari sincronitzat automàticament com a calendari separat.", + "cancel": "Cancel·la" + }, + "management": { + "title": "Gestió de calendaris", + "description": "Creeu, canvieu el nom i personalitzeu els calendaris. Feu clic dret a un calendari a la barra lateral per canviar-ne ràpidament el color.", + "name": "Nom", + "name_placeholder": "Nom del calendari", + "color": "Color", + "change_color": "Canvia el color", + "random_color": "Color aleatori nou", + "add_calendar": "Afegeix un calendari", + "edit": "Edita", + "delete": "Suprimeix", + "save": "Desa", + "create": "Crea", + "cancel": "Cancel·la", + "default": "Predeterminat", + "confirm_delete": "Voleu suprimir «{name}»? S'eliminaran tots els esdeveniments d'aquest calendari.", + "confirm_clear": "Voleu esborrar tots els esdeveniments de «{name}»? Això no es pot desfer.", + "set_default": "Estableix com a predeterminat", + "default_updated": "Calendari predeterminat actualitzat", + "error_default": "No s'ha pogut establir el calendari predeterminat", + "clear_events": "Esborra els esdeveniments", + "events_cleared": "{count} esdeveniments esborrats", + "error_clear": "No s'han pogut esborrar els esdeveniments del calendari", + "calendar_created": "Calendari creat", + "calendar_updated": "Calendari actualitzat", + "calendar_deleted": "Calendari suprimit", + "color_updated": "Color del calendari actualitzat", + "error_create": "No s'ha pogut crear el calendari", + "error_update": "No s'ha pogut actualitzar el calendari", + "error_delete": "No s'ha pogut suprimir el calendari", + "caldav_url": "URL de CalDAV", + "copy_url": "Copia l'URL de CalDAV", + "url_copied": "URL de CalDAV copiat al porta-retalls", + "share": "Comparteix el calendari", + "new_event_in_calendar": "Esdeveniment nou en aquest calendari", + "shared_with": "Compartit amb {names}", + "shared_with_count": "{count, plural, one {Compartit amb 1 persona} other {Compartit amb # persones}}", + "move_up": "Mou amunt", + "move_down": "Mou avall", + "kind": "Contingut", + "kind_events": "Esdeveniments", + "kind_tasks": "Tasques", + "kind_both": "Esdeveniments i tasques", + "kind_hint": "Determina quines aplicacions veuen aquest calendari quan se sincronitza per CalDAV. No es pot canviar més endavant.", + "default_alerts": "Recordatoris predeterminats", + "default_alerts_title": "Recordatoris predeterminats de «{name}»", + "default_alerts_hint": "Els esdeveniments nous d'aquest calendari reben aquests recordatoris, tret que es canviïn a l'esdeveniment. Canviar-los aquí no modifica els esdeveniments existents. Els recordatoris per correu els envia el servidor de correu.", + "default_alerts_timed": "Esdeveniments amb hora", + "default_alerts_all_day": "Esdeveniments de tot el dia", + "default_alerts_updated": "Recordatoris predeterminats actualitzats" + }, + "subscription": { + "title": "Subscripció iCal", + "section_title": "Subscripcions iCal", + "description": "Subscriviu-vos a un canal iCalendar extern. Els esdeveniments se sincronitzaran automàticament en el seu propi calendari. Admet URL https:// i webcal://.", + "url_label": "URL del calendari", + "url_placeholder": "https://example.com/calendar.ics or webcal://...", + "name_label": "Nom del calendari", + "name_placeholder": "p. ex. Festius", + "color_label": "Color", + "refresh_interval": "Interval d'actualització", + "interval_15": "Cada 15 minuts", + "interval_30": "Cada 30 minuts", + "interval_60": "Cada hora", + "interval_360": "Cada 6 hores", + "interval_1440": "Cada dia", + "subscribe": "Subscriu-te", + "subscribing": "Subscrivint...", + "save": "Desa els canvis", + "saving": "Desant...", + "edit": "Edita", + "edit_title": "Edita la subscripció", + "updated": "S'ha actualitzat «{name}»", + "update_error": "No s'ha pogut actualitzar la subscripció", + "invalid_url": "Introduïu un URL vàlid", + "success": "Subscrit a «{name}»", + "error": "No s'ha pogut afegir la subscripció", + "refresh": "Actualitza ara", + "refresh_success": "Subscripció actualitzada", + "refresh_error": "No s'ha pogut actualitzar la subscripció", + "unsubscribe": "Cancel·la la subscripció", + "confirm_delete": "Voleu cancel·lar la subscripció a «{name}»? S'eliminaran el calendari i tots els seus esdeveniments.", + "deleted": "Subscripció eliminada", + "delete_error": "No s'ha pogut eliminar la subscripció", + "last_refreshed": "Última actualització: {time}" + }, + "tasks": { + "label": "Tasques", + "no_tasks": "Cap tasca", + "no_title": "(Sense títol)", + "mark_complete": "Marca com a completada", + "mark_incomplete": "Marca com a incompleta", + "filter_all": "Totes", + "filter_pending": "Pendents", + "filter_completed": "Completades", + "filter_overdue": "Vençudes", + "show_completed": "Mostra les completades", + "create": "Tasca nova", + "edit": "Edita la tasca", + "title_placeholder": "Títol de la tasca", + "description_placeholder": "Afegiu una descripció...", + "due_date": "Data de venciment", + "include_time": "Inclou l'hora", + "priority": "Prioritat", + "priority_none": "Cap", + "priority_high": "Alta", + "priority_medium": "Mitjana", + "priority_low": "Baixa", + "progress": "Estat", + "progress_needs_action": "Cal actuar", + "progress_in_process": "En curs", + "progress_completed": "Completada", + "progress_cancelled": "Cancel·lada", + "calendar": "Calendari", + "alert": "Recordatori", + "alert_none": "Cap", + "alert_at_time": "A l'hora de venciment", + "alert_5min": "5 minuts abans", + "alert_15min": "15 minuts abans", + "alert_30min": "30 minuts abans", + "alert_1hr": "1 hora abans", + "alert_1day": "1 dia abans", + "delete": "Suprimeix", + "cancel": "Cancel·la", + "save": "Desa", + "quick_add_placeholder": "Afegiu una tasca...", + "due_today": "Avui", + "due_tomorrow": "Demà", + "overdue": "Vençuda" + }, + "toolbar": { + "more": "Més accions", + "select_view": "Canvia la vista, actualment {view}" + }, + "other_calendars": "Altres calendaris", + "calendar_options": "Opcions de {name}" + }, + "calendar_event_notifications": { + "someone": "Algú", + "invited": "{name} t'ha convidat a \"{title}\"", + "updated": "{name} ha actualitzat \"{title}\"", + "cancelled": "{name} ha cancel·lat \"{title}\"", + "untitled": "Esdeveniment sense títol" + }, + "share_notifications": { + "someone": "Algú", + "shared": "{name} ha compartit amb tu {kind} \"{object}\"", + "changed": "{name} ha canviat el teu accés a {kind} \"{object}\"", + "revoked": "{name} ha retirat el teu accés a {kind} \"{object}\"", + "object": { + "folder": "la carpeta", + "calendar": "el calendari", + "address_book": "la llibreta d'adreces", + "files": "la carpeta de fitxers" + } + }, + "sharing": { + "title": "Comparteix «{name}»", + "description": "Concediu accés a altres usuaris o grups d'aquest servidor. Els canvis s'apliquen immediatament.", + "no_shares": "Encara no s'ha compartit amb ningú.", + "add_person": "Afegeix una persona o un grup", + "search_placeholder": "Cerca per nom o correu electrònic…", + "loading_principals": "Carregant els usuaris…", + "no_principals": "No s'ha trobat cap altre usuari o grup.", + "no_match": "Cap coincidència.", + "remove": "Elimina l'accés", + "group": "Grup", + "share_added": "Accés concedit", + "share_updated": "Accés actualitzat", + "share_removed": "Accés eliminat", + "share_failed": "No s'ha pogut actualitzar la compartició", + "preset": { + "freeBusy": "Només disponibilitat", + "read": "Només lectura", + "readWrite": "Lectura i escriptura", + "manager": "Gestor", + "custom": "Personalitzat" + } + }, + "advanced_search": { + "title": "Cerca avançada", + "from": "De", + "from_placeholder": "Nom o correu del remitent", + "to": "Per a", + "to_placeholder": "Nom o correu del destinatari", + "subject": "Assumpte", + "subject_placeholder": "L'assumpte conté...", + "body": "Cos", + "body_placeholder": "El cos conté...", + "folder": "Carpeta", + "all_folders": "Totes les carpetes", + "all_folders_except_spam_trash": "Totes les carpetes excepte Correu brossa i Paperera", + "has_attachment": "Fitxers adjunts", + "date_after": "Després de", + "date_before": "Abans de", + "size_min": "Més gran que (KB)", + "size_max": "Més petit que (KB)", + "starred": "Destacat", + "unread": "No llegit", + "read": "Llegit", + "yes": "Sí", + "no": "No", + "clear": "Neteja", + "clear_all": "Neteja-ho tot", + "filters_active": "{count} filtre", + "filters_active_plural": "{count} filtres", + "toggle_filters": "Més", + "search_hint": "Utilitzeu els filtres avançats per a una cerca precisa", + "advanced_filters_tooltip": "Filtres de cerca avançada", + "results_found": "{count, plural, =0 {No s'ha trobat cap resultat} one {# resultat trobat} other {# resultats trobats}}", + "results_found_more": "{count}+ resultats trobats", + "suggestions_recent": "Cerques recents", + "suggestions_people": "Persones", + "suggestions_from": "Cerca correu de {address}", + "suggestions_to": "Cerca correu per a {address}", + "suggestions_remove_recent": "Elimina de les cerques recents", + "has_words": "Conté les paraules", + "size": "Mida", + "date_within": "Data", + "search_in": "Cerca a", + "has_attachment_check": "Té fitxers adjunts", + "search": "Cerca" + }, + "welcome": { + "title": "Us donem la benvinguda a la bústia", + "tip_compose": "Premeu c per redactar un correu nou", + "tip_shortcuts": "Premeu ? per veure totes les dreceres de teclat", + "tip_sidebar": "Trobeu el Calendari, els Contactes i la Configuració al menú de la barra lateral", + "tip_settings": "Personalitzeu la vostra experiència a la Configuració", + "tip_compose_touch": "Toca el botó del llapis per escriure un correu nou", + "tip_sidebar_touch": "Trobaràs el Calendari, els Contactes i la Configuració a la barra inferior", + "got_it": "Entesos", + "settings": "Configuració", + "dismiss": "Descarta", + "start_tour": "Comença la visita guiada" + }, + "demo_welcome": { + "title": "Us donem la benvinguda al Bulwark Mail", + "description": "Exploreu un client de correu web complet, directament al navegador. Totes les dades es queden al vostre dispositiu, així que proveu-ho tot sense cap problema.", + "feature_email": "Llegiu i redacteu correus", + "feature_organize": "Etiquetes, estrelles i carpetes", + "feature_shortcuts": "Dreceres de teclat", + "feature_privacy": "Demo 100% privada", + "hint": "Feu clic a qualsevol correu de l'esquerra per començar, o feu la visita guiada de sota." + }, + "files": { + "office_edit": "Edita el document", + "office_loading": "S'està obrint l'editor de documents…", + "office_error": "No s'ha pogut obrir l'editor de documents.", + "office_close": "Tanca l'editor", + "open_in_new_tab": "Obre en una pestanya nova", + "title": "Fitxers", + "search_placeholder": "Cerca fitxers...", + "empty_state_title": "Encara no hi ha fitxers", + "upload_files": "Puja fitxers", + "new_folder": "Carpeta nova", + "new_folder_name": "Nom de la carpeta", + "rename": "Canvia el nom", + "rename_title": "Canvia el nom", + "new_name": "Nom nou", + "name_forbidden_chars": "El servidor no permet aquests caràcters als noms: {chars}", + "name_reserved": "Aquest nom està reservat al servidor.", + "delete": "Suprimeix", + "delete_confirm_title": "Suprimeix el recurs", + "delete_confirm_message": "Segur que voleu suprimir «{name}»? Això no es pot desfer.", + "download": "Baixa", + "name": "Nom", + "size": "Mida", + "modified": "Modificat", + "type": "Tipus", + "folder": "Carpeta", + "file": "Fitxer", + "no_accounts": "Cap compte connectat.", + "uploading": "Pujant...", + "upload_success": "{count, plural, one {1 fitxer pujat} other {# fitxers pujats}}", + "upload_canceled": "Pujada cancel·lada", + "upload_canceled_partial": "{count, plural, one {S'ha pujat 1 fitxer} other {S'han pujat # fitxers}} abans de cancel·lar", + "upload_error": "No s'ha pogut pujar el fitxer", + "create_folder_success": "Carpeta creada", + "create_folder_error": "No s'ha pogut crear la carpeta", + "delete_success": "Suprimit correctament", + "delete_error": "No s'ha pogut suprimir", + "rename_success": "Nom canviat correctament", + "rename_error": "No s'ha pogut canviar el nom", + "download_error": "No s'ha pogut baixar", + "not_available": "L'emmagatzematge de fitxers no està disponible en aquest servidor", + "cancel": "Cancel·la", + "create": "Crea", + "save": "Desa", + "no_results": "Cap fitxer coincideix amb la cerca", + "batch_delete_confirm_message": "Segur que voleu suprimir {count, plural, one {1 element} other {# elements}}? Això no es pot desfer.", + "batch_delete_success": "{count, plural, one {1 element suprimit} other {# elements suprimits}}", + "grid_view": "Vista de quadrícula", + "list_view": "Vista de llista", + "details": "Detalls", + "path": "Camí", + "preview": "Previsualització", + "preview_error": "No s'ha pogut carregar la previsualització", + "cut": "Retalla", + "copy": "Copia", + "paste": "Enganxa", + "move_success": "{count, plural, one {1 element mogut} other {# elements moguts}}", + "move_error": "No s'ha pogut moure", + "paste_success": "Enganxat correctament", + "paste_error": "No s'ha pogut enganxar", + "new_text_file": "Fitxer de text nou", + "file_name": "Nom del fitxer", + "retry": "Reintenta", + "refresh": "Actualitza", + "duplicate": "Duplica", + "duplicate_success": "Duplicat correctament", + "duplicate_error": "No s'ha pogut duplicar", + "share": "Comparteix", + "shared": "Compartit", + "shared_with_me": "Compartit amb mi", + "shared_by": "Compartit per {name}", + "create_file_success": "Fitxer creat", + "create_file_error": "No s'ha pogut crear el fitxer", + "favorites": "Preferits", + "recent": "Recents", + "show_in_folder": "Mostra a la carpeta", + "remove_from_recent": "Treu de recents", + "upload_folder": "Puja una carpeta", + "file_too_large": "«{name}» supera la mida màxima de fitxer ({max})", + "undo": "Desfés", + "undo_success": "Acció desfeta", + "undo_error": "No s'ha pogut desfer", + "toolbar": "Accions de fitxer", + "open_folder_tree": "Obre l'arbre de carpetes", + "file_list": "Fitxers i carpetes", + "context_menu": "Accions", + "settings_ascending": "Ascendent", + "settings_descending": "Descendent", + "disabled_title": "La funció de fitxers està desactivada per l'administrador", + "disabled_description": "Les pujades de fitxers grans via WebDAV poden causar inestabilitat a Stalwart/RocksDB, incloent-hi fallades per manca de memòria i ús de disc irrecuperable. És possible que els fitxers suprimits no s'eliminin immediatament de l'emmagatzematge de blobs. No es recomana aquesta funció per a entorns de producció.", + "migration_title": "Actualitzant els vostres fitxers…", + "migration_description": "S'estan organitzant les carpetes i els fitxers en la seva estructura adequada. Això només passa una vegada.", + "new": "Nou", + "my_files": "Els meus fitxers", + "folders": "Carpetes", + "files_heading": "Fitxers", + "open": "Obre", + "star": "Afegeix a preferits", + "unstar": "Treu de preferits", + "expand_folder": "Desplega {name}", + "collapse_folder": "Plega {name}", + "sidebar_hint": "Les carpetes preferides i els fitxers oberts apareixen aquí.", + "breadcrumbs": "Camí de la carpeta", + "show_path": "Mostra les carpetes ocultes del camí", + "selection_actions": "Accions per als elements seleccionats", + "selected_count": "{count, plural, one {1 seleccionat} other {# seleccionats}}", + "clear_selection": "Esborra la selecció", + "select_all": "Selecciona-ho tot", + "select_item": "Selecciona {name}", + "more_actions": "Més accions per a {name}", + "paste_here": "Enganxa en aquesta carpeta", + "paste_count": "{count, plural, one {Enganxa 1 element} other {Enganxa # elements}}", + "sort_by": "Ordena per", + "view_mode": "Visualització", + "clear_search": "Esborra la cerca", + "no_results_for": "No hi ha res en aquesta carpeta que coincideixi amb «{query}»", + "download_count": "{count, plural, one {Baixa 1 fitxer} other {Baixa # fitxers}}", + "delete_count": "{count, plural, one {Suprimeix 1 element} other {Suprimeix # elements}}", + "close_details": "Tanca els detalls", + "file_kind": "Fitxer {ext}", + "folder_summary": "{folders, plural, =0 {Cap carpeta} one {1 carpeta} other {# carpetes}}, {files, plural, =0 {cap fitxer} one {1 fitxer} other {# fitxers}}", + "details_hint": "Seleccioneu un fitxer o una carpeta per veure'n els detalls.", + "sharing": "Compartició", + "shared_with_count": "{count, plural, one {Compartit amb 1 persona} other {Compartit amb # persones}}", + "only_you": "Només vós", + "empty_folder_title": "Aquesta carpeta és buida", + "empty_folder_description": "Deixeu anar fitxers aquí o pugeu-los des del dispositiu.", + "empty_folder_read_only": "No podeu afegir fitxers a aquesta carpeta.", + "choose_account": "Trieu un compte", + "drop_to_upload": "Deixeu anar per pujar a {folder}", + "uploading_count": "S'està pujant {current} de {total}", + "dismiss": "Descarta", + "zoom_in": "Amplia", + "zoom_out": "Redueix", + "zoom_reset": "Ajusta a la pantalla", + "rotate": "Gira", + "close_preview": "Tanca" + }, + "smime": { + "your_certificates": "Els vostres certificats", + "your_certificates_desc": "Importeu i gestioneu els certificats S/MIME per signar i xifrar correus", + "recipient_certificates": "Certificats de destinatari", + "recipient_certificates_desc": "Certificats públics per xifrar correus als destinataris", + "identity_bindings": "Vinculacions de clau a identitat", + "identity_bindings_desc": "Vinculeu certificats S/MIME a les vostres identitats de correu", + "defaults_title": "Valors per defecte", + "defaults_desc": "Configureu el comportament predeterminat de signatura i xifratge", + "import_pkcs12": "Importa PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importa un certificat", + "no_certificates": "Encara no s'ha importat cap certificat", + "no_recipient_certs": "Cap certificat de destinatari", + "expires": "Caduca", + "expired": "Caducat", + "bound_to": "Vinculat a", + "no_key_bound": "Cap", + "lock": "Bloqueja la clau", + "unlock": "Desbloqueja la clau", + "details": "Mostra els detalls", + "delete": "Suprimeix", + "encrypt_by_default": "Xifra per defecte", + "encrypt_by_default_desc": "Xifra automàticament els correus quan tots els destinataris tinguin certificat", + "remember_unlocked": "Recorda les claus desbloquejades", + "remember_unlocked_desc": "Manté les claus desbloquejades durant aquesta sessió del navegador", + "sign_default_for": "Signa per defecte per a", + "enter_p12_passphrase": "Introduïu la contrasenya del PKCS#12", + "p12_passphrase_desc": "Introduïu la contrasenya que protegeix aquest fitxer de certificat", + "enter_storage_passphrase": "Estableix la contrasenya d'emmagatzematge", + "storage_passphrase_desc": "Trieu una contrasenya per protegir aquesta clau en repòs al navegador", + "next": "Següent", + "import": "Importa", + "unlock_key": "Desbloqueja la clau", + "unlock_key_desc": "Introduïu la contrasenya d'emmagatzematge per desbloquejar aquesta clau per signar o desxifrar", + "passphrase_placeholder": "Introduïu la contrasenya", + "confirm_passphrase_placeholder": "Confirmeu la contrasenya", + "passphrase_mismatch": "Les contrasenyes no coincideixen", + "cancel": "Cancel·la", + "processing": "Processant…", + "close": "Tanca", + "certificate_details": "Detalls del certificat", + "cert_subject": "Subjecte", + "cert_issuer": "Emissor", + "cert_email": "Correu electrònic", + "cert_serial": "Número de sèrie", + "cert_validity": "Validesa", + "cert_fingerprint": "Empremta digital (SHA-256)", + "cert_algorithm": "Algorisme", + "cert_capabilities": "Capacitats", + "cert_source": "Origen", + "cert_expired": "Aquest certificat ha caducat", + "cert_not_yet_valid": "Aquest certificat encara no és vàlid", + "cap_sign": "Signatura", + "cap_encrypt": "Xifratge", + "cap_none": "Cap", + "show_passphrase": "Mostra la contrasenya", + "hide_passphrase": "Amaga la contrasenya", + "sign_toggle": "Signa", + "encrypt_toggle": "Xifra", + "missing_recipient_certs": "Falten certificats per a: {emails}", + "missing_sender_cert": "Cap certificat vinculat a aquesta identitat", + "status_encrypted_ok": "Aquest missatge s'ha xifrat", + "status_encrypted_no_key": "Aquest missatge està xifrat, però no s'ha trobat cap clau coincident", + "status_encrypted_failed": "No s'ha pogut desxifrar aquest missatge", + "status_signed_valid": "Signatura verificada", + "status_signed_invalid": "Ha fallat la verificació de la signatura", + "status_signed_expired_cert": "Signat amb un certificat caducat", + "status_signed_self_signed": "Signatura vàlida, però el certificat és autosignat (no fiable)", + "status_signed_mismatch": "Signatura vàlida, però el signant no coincideix amb el remitent", + "status_unsupported": "Format S/MIME no admès", + "auto_import_signer_certs": "Importa automàticament els certificats dels signants", + "auto_import_signer_certs_desc": "Desa automàticament els certificats dels correus signats verificats per a un xifratge futur", + "export": "Exporta", + "enter_export_passphrase": "Estableix la contrasenya d'exportació", + "export_passphrase_desc": "Trieu una contrasenya per protegir el fitxer PKCS#12 exportat", + "export_storage_desc": "Introduïu la contrasenya d'emmagatzematge per desxifrar la clau per exportar-la", + "incorrect_passphrase": "Contrasenya incorrecta" + }, + "tour": { + "step_counter": "Pas {current} de {total}", + "skip": "Omet la visita", + "back": "Enrere", + "next": "Següent", + "finish": "Finalitza", + "take_a_tour": "Feu una visita guiada de la interfície", + "restart_title": "Visita guiada introductòria", + "restart_desc": "Torneu a veure la visita guiada de la interfície", + "restart_button": "Reinicia la visita guiada", + "show_on_new_devices_title": "Mostra en dispositius nous", + "show_on_new_devices_desc": "Torna a mostrar el bàner de benvinguda i la visita guiada la primera vegada que inicieu la sessió en un dispositiu nou, encara que ja els hàgiu completat en un altre lloc", + "sidebar_title": "Les vostres bústies", + "sidebar_desc": "Aquesta és la barra lateral de carpetes. Feu clic a qualsevol bústia per veure'n els correus. Podeu crear carpetes, arrossegar-hi correus i veure d'un cop d'ull el nombre de no llegits.", + "compose_title": "Redacteu un correu", + "compose_desc": "Feu clic aquí per escriure un correu nou. Podeu afegir destinataris, fitxers adjunts i utilitzar format de text enriquit.", + "search_title": "Cerqueu al vostre correu", + "search_desc": "Cerqueu per remitent, assumpte o contingut. Feu clic a la icona de filtre per a opcions avançades com l'interval de dates, els fitxers adjunts i els missatges destacats.", + "email_list_title": "La vostra llista de correus", + "email_list_desc": "Els correus apareixen aquí. Feu-hi clic per llegir-los a la dreta. Utilitzeu la casella de selecció per seleccionar-ne diversos i moure'ls, suprimir-los o etiquetar-los en bloc.", + "email_viewer_title": "Subfinestra de lectura", + "email_viewer_desc": "El correu seleccionat s'obre aquí. Responeu, reenvieu, arxiveu o suprimiu amb els botons de la barra d'eines. També podeu marcar correus amb estrella o afegir-hi etiquetes de color.", + "keywords_title": "Etiquetes de color", + "keywords_desc": "Organitzeu el correu amb etiquetes de color. Arrossegueu un correu sobre una etiqueta per etiquetar-lo, o feu clic dret a un correu per assignar-hi etiquetes.", + "calendar_title": "Calendari", + "calendar_desc": "Canvieu al calendari per gestionar els vostres esdeveniments. Creeu esdeveniments, establiu recordatoris i visualitzeu-los per dia, setmana o mes.", + "contacts_title": "Contactes", + "contacts_desc": "Aquí hi teniu la llibreta d'adreces. Importeu contactes, creeu grups i feu clic a qualsevol contacte per veure'n tots els detalls.", + "settings_title": "Configuració", + "settings_desc": "Personalitzeu-ho tot: tema, densitat, signatures, filtres, dreceres de teclat, valors predeterminats del calendari i molt més.", + "shortcuts_title": "Dreceres de teclat", + "shortcuts_desc": "Als usuaris avançats els encanta. Premeu ? en qualsevol moment per veure totes les dreceres disponibles. Podeu navegar, redactar i gestionar correus sense tocar el ratolí.", + "compose_open_title": "El redactor", + "compose_open_desc": "Aquest és el redactor de correu. Afegiu destinataris, escriviu el missatge, adjunteu fitxers i utilitzeu format de text enriquit. També podeu desar esborranys i utilitzar plantilles.", + "calendar_view_title": "El vostre calendari", + "calendar_view_desc": "Aquí teniu el vostre calendari amb esdeveniments d'exemple. Podeu canviar entre les vistes de dia, setmana, mes i agenda amb la barra d'eines.", + "create_event_title": "Creeu un esdeveniment", + "create_event_desc": "Feu clic a aquest botó per crear un esdeveniment de calendari nou. Podeu establir un títol, una data, una hora i afegir-hi participants.", + "event_modal_title": "Detalls de l'esdeveniment", + "event_modal_desc": "Aquest és el formulari de l'esdeveniment. Ompliu el títol, trieu una data i hora, afegiu una ubicació o participants. Deseu-lo quan hàgiu acabat, o tanqueu-lo i continueu.", + "contacts_list_title": "Els vostres contactes", + "contacts_list_desc": "Aquí teniu els vostres contactes. Feu clic a qualsevol contacte per veure'n tots els detalls a la dreta. També podeu crear contactes nous, importar vCards o organitzar contactes en grups.", + "settings_tabs_title": "Menú de configuració", + "settings_tabs_desc": "Aquí teniu totes les categories de configuració. Personalitzeu l'aparença, gestioneu identitats, configureu filtres de correu, ajusteu el calendari i molt més.", + "files_title": "Emmagatzematge de fitxers", + "files_desc": "El navegador de fitxers us permet pujar, organitzar i compartir fitxers, com un disc al núvol personal integrat al correu.", + "demo_banner_title": "Controls de la demo", + "demo_banner_desc": "Esteu en mode de demostració: tot es queda al navegador. Premeu «Reinicia la demo» en qualsevol moment per començar de nou amb dades d'exemple netes.", + "quota_title": "Ús de l'emmagatzematge", + "quota_desc": "Feu un seguiment de la mida de la bústia aquí. El cercle s'omple a mesura que utilitzeu més espai." + }, + "unified_mailbox": { + "search_unavailable": "La cerca no està disponible a la vista unificada" + }, + "quote_header": { + "reply_line": "El {date}, {from} va escriure:", + "forwarded_separator": "---------- Missatge reenviat ----------", + "from_label": "De", + "date_label": "Data", + "subject_label": "Assumpte" + }, + "pwa_install": { + "title": "Instal·la {appName}", + "description": "Instal·leu la nostra aplicació per a un accés ràpid i compatibilitat sense connexió.", + "not_now": "Ara no", + "install": "Instal·la", + "dont_remind": "No m'ho tornis a recordar", + "dismiss_aria": "Descarta l'avís d'instal·lació" + }, + "deep_link": { + "message_not_found": "Aquest missatge ja no està disponible.", + "thread_not_found": "Aquesta conversa ja no està disponible.", + "folder_not_found": "Aquesta carpeta ja no està disponible.", + "event_not_found": "Aquest esdeveniment ja no està disponible.", + "file_not_found": "Aquest fitxer ja no està disponible.", + "account_unavailable": "Inicia la sessió en aquest compte per obrir l'enllaç.", + "copy_message": "Copia l'enllaç del missatge", + "copy_conversation": "Copia l'enllaç de la conversa", + "copy_event": "Copia l'enllaç de l'esdeveniment", + "copied": "Enllaç copiat", + "copy_failed": "No s'ha pogut copiar l'enllaç" + }, + "plugins": { + "oauth_callback": { + "completing": "Completant l'autorització…", + "completing_message": "Espereu mentre s'estableix la connexió.", + "received_title": "S'ha rebut l'autorització", + "received_message": "Podeu tornar a la pestanya on heu iniciat la connexió.", + "failed_title": "Ha fallat l'autorització", + "denied": "Sha denegat lautorització.", + "failed_detail": "Ha fallat lautorització: {error}", + "missing_params": "Falta el codi dautorització o lestat.", + "delivery_failed": "No sha pogut lliurar el resultat de lautorització a la pestanya de laplicació. Tanqueu aquesta finestra i torneu-ho a provar." + }, + "settings": { + "title": "Connectors", + "description": "Connectors que ha afegit l'administrador. Activeu-los o desactiveu-los per al vostre compte.", + "description_uploads": "Els connectors afegeixen funcions al webmail. Activeu-los o desactiveu-los per al vostre compte, o instal·leu-ne de propis.", + "description_demo": "Els connectors no estan disponibles a la demo.", + "install_button": "Instal·la un connector", + "install_hint": "Trieu un fitxer ZIP de connector.", + "install_hint_approval": "Trieu un fitxer ZIP de connector. Un administrador l'ha d'aprovar abans que el pugueu activar.", + "install_uploads_off": "L'administrador no permet instal·lar connectors.", + "install_managed_id": "L'administrador ja proporciona un connector amb aquest nom, així que no es pot substituir.", + "install_invalid": "Aquest fitxer no és un connector que es pugui instal·lar: {details}", + "toast_installed": "S'ha instal·lat el connector. Activeu-lo a continuació.", + "toast_installed_pending": "S'ha instal·lat el connector i s'ha enviat a l'administrador perquè l'aprovi.", + "toast_enabled": "{name} està activat", + "toast_disabled": "{name} està desactivat", + "toast_removed": "S'ha eliminat {name}", + "toast_forced": "L'administrador manté {name} activat", + "toast_approval_requested": "{name} està pendent de l'aprovació de l'administrador", + "empty_title": "Encara no hi ha connectors", + "empty_admin": "L'administrador no ha afegit cap connector.", + "empty_uploads": "Per començar, instal·leu un connector des d'un fitxer ZIP.", + "empty_demo": "Els connectors necessiten un compte real, així que la demo no en té cap.", + "syncing": "S'estan comprovant els connectors amb el servidor…", + "status_installed": "Desactivat", + "status_enabled": "S'està iniciant", + "status_running": "Activat", + "status_disabled": "Desactivat", + "status_error": "Problema", + "badge_forced": "Obligatori", + "badge_managed": "De l'administrador", + "badge_yours": "Instal·lat per vós", + "badge_awaiting": "Pendent d'aprovació", + "toggle_label": "Activa o desactiva {name}", + "forced_note": "L'administrador requereix aquest connector, així que no es pot desactivar.", + "permissions": "Què pot fer", + "permissions_managed": "Què pot fer (aprovat per l'administrador)", + "settings": "Configuració", + "remove": "Elimina", + "remove_title": "Voleu eliminar {name}?", + "remove_message": "S'eliminaran el connector i tot el que ha desat en aquest navegador. El podeu tornar a instal·lar més endavant.", + "remove_confirm": "Elimina", + "error_user_plugins_off": "L'administrador ha desactivat els connectors instal·lats pels usuaris, així que aquest connector no s'executa.", + "error_approval_unavailable": "Aquest connector es va instal·lar abans que calgués l'aprovació. Elimineu-lo i torneu-lo a instal·lar per enviar-lo a aprovar.", + "error_approval_denied": "L'administrador no ha aprovat aquest connector.", + "error_approval_pending": "S'espera que l'administrador aprovi aquest connector.", + "error_auto_disabled": "S'ha desactivat perquè fallava repetidament. Torneu-lo a activar per tornar-ho a provar.", + "accounts_heading": "Comptes connectats", + "account_on": "{label} a {host}", + "account_waiting": "{label} a {host} (inici de sessió sense acabar)", + "disconnect": "Desconnecta", + "disconnect_failed": "No s'ha pogut desconnectar {label}" + }, + "consent": { + "title": "Voleu permetre que {name} faci això?", + "intro": "El connector demana els permisos següents. Només els obté si ho permeteu.", + "high_risk_note": "Els elements marcats donen al connector accés al contingut del vostre correu o li permeten enviar dades a altres llocs. Permeteu-los només als connectors de confiança.", + "revoke_hint": "Podeu desactivar o eliminar el connector a Configuració en qualsevol moment.", + "deny": "No ho permetis", + "allow": "Permet" + }, + "permissions": { + "none": "No necessita cap permís especial.", + "high_risk": "Cal anar amb compte", + "email_read": { + "title": "Llegir el vostre correu", + "body": "Veure remitents, destinataris, assumptes i el text complet de qualsevol missatge del vostre compte, i les vostres carpetes." + }, + "email_write": { + "title": "Canviar i organitzar el vostre correu", + "body": "Afegir o treure etiquetes, marques i altres paraules clau dels vostres missatges, moure'ls entre les vostres carpetes i a l'arxiu, i desar esborranys." + }, + "email_send": { + "title": "Canviar el correu abans d'enviar-lo", + "body": "Llegir i canviar els destinataris, l'assumpte, el text i els adjunts dels missatges que escriviu, i impedir que s'enviï un missatge." + }, + "crypto_full": { + "title": "Accés complet a aquesta aplicació", + "body": "S'executa amb els mateixos drets que el mateix webmail. Pot llegir tot el vostre correu i les claus desades i actuar en nom vostre. Permeteu-ho només als connectors en què confieu plenament." + }, + "email_raw_send": { + "title": "Enviar missatges que ha creat ell mateix", + "body": "Enviar missatges complets, per exemple signats o xifrats, des del vostre compte." + }, + "email_blob_read": { + "title": "Llegir el contingut original dels missatges", + "body": "Llegir les dades originals dels vostres missatges i adjunts, i de cada fitxer que adjunteu abans d'enviar-lo." + }, + "email_blob_write": { + "title": "Substituir adjunts abans d'enviar", + "body": "Pujar fitxers al vostre servidor de correu i substituir un fitxer adjunt per un altre abans que s'enviï el missatge." + }, + "email_render_takeover": { + "title": "Canviar com es mostren els missatges", + "body": "Substituir el text dels missatges que obriu i de la llista de missatges, per exemple per mostrar contingut desxifrat." + }, + "calendar_read": { + "title": "Llegir el vostre calendari", + "body": "Veure els vostres calendaris i els seus esdeveniments." + }, + "calendar_write": { + "title": "Canviar el vostre calendari", + "body": "Crear, canviar i suprimir esdeveniments." + }, + "contacts_read": { + "title": "Llegir els vostres contactes", + "body": "Cercar i llegir les vostres llibretes d'adreces." + }, + "contacts_write": { + "title": "Canviar els vostres contactes", + "body": "Crear, canviar i suprimir contactes i llibretes d'adreces." + }, + "files_read": { + "title": "Llegir els vostres fitxers", + "body": "Navegar pels fitxers desats al vostre compte i obrir-los." + }, + "files_write": { + "title": "Canviar els vostres fitxers", + "body": "Pujar, canviar el nom, moure i suprimir fitxers del vostre compte." + }, + "identity_read": { + "title": "Veure les vostres identitats d'enviament", + "body": "Veure les adreces des de les quals envieu i les seves signatures." + }, + "identity_write": { + "title": "Canviar les vostres identitats d'enviament", + "body": "Crear, canviar i suprimir identitats d'enviament i signatures." + }, + "filters_read": { + "title": "Llegir els vostres filtres de correu", + "body": "Veure les regles que classifiquen el correu entrant." + }, + "filters_write": { + "title": "Canviar els vostres filtres de correu", + "body": "Afegir regles als filtres del servidor. Aquestes regles poden moure, reenviar o suprimir el correu entrant." + }, + "tasks_read": { + "title": "Llegir les vostres tasques", + "body": "Veure les vostres llistes de tasques i les tasques." + }, + "tasks_write": { + "title": "Canviar les vostres tasques", + "body": "Crear, canviar, completar i suprimir tasques." + }, + "templates_read": { + "title": "Llegir les vostres plantilles", + "body": "Veure les plantilles de missatge desades." + }, + "templates_write": { + "title": "Canviar les vostres plantilles", + "body": "Crear, canviar i suprimir plantilles de missatge." + }, + "smime_read": { + "title": "Veure l'estat de S/MIME", + "body": "Veure si teniu claus i certificats S/MIME, sense accés a les claus." + }, + "vacation_read": { + "title": "Veure la resposta automàtica", + "body": "Veure si la resposta automàtica està activada i què diu." + }, + "vacation_write": { + "title": "Canviar la resposta automàtica", + "body": "Activar o desactivar la resposta automàtica i canviar-ne el text i les dates." + }, + "settings_read": { + "title": "Llegir la vostra configuració", + "body": "Veure les vostres preferències i les definicions d'etiquetes." + }, + "settings_write": { + "title": "Canviar la vostra configuració", + "body": "Afegir i reordenar etiquetes i canviar les vostres preferències." + }, + "security_read": { + "title": "Veure la seguretat del compte", + "body": "Veure si l'autenticació de dos factors i el xifratge estan activats, sense accés als secrets." + }, + "auth_observe": { + "title": "Saber quan inicieu la sessió", + "body": "Rebre un avís quan inicieu la sessió, canvieu de compte o es renova la sessió." + }, + "auth_emit": { + "title": "Tancar la vostra sessió", + "body": "Tancar la vostra sessió al webmail." + }, + "account_read": { + "title": "Veure els vostres comptes", + "body": "Veure els comptes que heu afegit, amb les seves adreces i servidors." + }, + "http_post": { + "title": "Fer servir el servidor del webmail en nom vostre", + "body": "Cridar aquests punts d'accés del servidor amb el vostre inici de sessió:" + }, + "http_fetch": { + "title": "Connectar-se a altres llocs web", + "body": "Enviar peticions a aquests llocs. Qualsevol cosa que el connector pugui llegir s'hi pot enviar:" + }, + "admin_config": { + "title": "Llegir la configuració que li ha posat l'administrador", + "body": "Llegir la configuració que l'administrador ha desat per a aquest connector." + }, + "ui_observe": { + "title": "Seguir on sou dins l'aplicació", + "body": "Saber quina pàgina està oberta i reaccionar als enllaços en què feu clic." + }, + "ui_toolbar": { + "title": "Afegir botons a les barres d'eines", + "body": "Mostrar botons propis a la barra d'eines dels missatges, en un missatge obert i a la barra superior." + }, + "ui_app_top_banner": { + "title": "Mostrar un bàner a dalt", + "body": "Mostrar un avís a la part superior de l'aplicació." + }, + "ui_email_banner": { + "title": "Mostrar avisos als missatges", + "body": "Mostrar avisos i resums propis a sobre, dins i entre les parts d'un missatge o una conversa oberts." + }, + "ui_email_footer": { + "title": "Afegir contingut sota els missatges", + "body": "Mostrar contingut propi a sota d'un missatge obert." + }, + "ui_contact_cryptokeys": { + "title": "Mostrar les claus de xifratge dels contactes", + "body": "Mostrar i gestionar les claus de xifratge d'un contacte." + }, + "ui_email_details": { + "title": "Afegir panells als detalls del missatge", + "body": "Mostrar un panell propi al costat o a sota d'un missatge obert." + }, + "ui_download_file": { + "title": "Desar fitxers al dispositiu", + "body": "Oferir per baixar fitxers que ha creat." + }, + "ui_message_list_tabs": { + "title": "Afegir pestanyes a la safata d'entrada", + "body": "Classificar la safata d'entrada en pestanyes com ara Actualitzacions o Promocions." + }, + "ui_composer_toolbar": { + "title": "Afegir elements al redactor", + "body": "Mostrar botons, elements de menú i seccions propis mentre escriviu un missatge." + }, + "ui_composer_sidebar": { + "title": "Afegir un panell al redactor", + "body": "Mostrar un panell propi al costat del missatge que escriviu." + }, + "ui_sidebar_widget": { + "title": "Afegir elements a la barra lateral", + "body": "Mostrar quadres i seccions propis a la barra lateral del correu." + }, + "ui_settings_section": { + "title": "Afegir una pàgina de configuració", + "body": "Mostrar una pàgina pròpia a Configuració." + }, + "ui_context_menu": { + "title": "Afegir elements al menú del missatge", + "body": "Afegir elements al menú que s'obre quan feu clic amb el botó dret en un missatge." + }, + "ui_navigation_rail": { + "title": "Afegir elements a la barra de navegació", + "body": "Mostrar un botó propi a la barra de navegació." + }, + "ui_keyboard": { + "title": "Afegir dreceres de teclat", + "body": "Reaccionar a les dreceres de teclat que defineix." + }, + "ui_calendar_action": { + "title": "Afegir elements a la vista del calendari", + "body": "Mostrar botons i seccions propis als esdeveniments, i etiquetes curtes i punts als dies del calendari." + }, + "ui_admin_page": { + "title": "Afegir una pàgina d'administració", + "body": "Mostrar una pàgina pròpia a la configuració d'administració." + }, + "app_lifecycle": { + "title": "Saber quan l'aplicació s'inicia i s'atura", + "body": "Rebre un avís quan l'aplicació s'inicia, es queda sense connexió o tanqueu la sessió." + }, + "compose_open": { + "title": "Obrir el redactor per a vós", + "body": "Obrir un missatge nou amb els destinataris, el text o els adjunts ja emplenats. Sou vós qui decidiu si s'envia." + }, + "compose_read": { + "title": "Llegir el missatge que escriviu", + "body": "Llegir el text d'un missatge mentre l'escriviu, quan feu servir el connector a l'editor. No la signatura ni el missatge citat." + }, + "compose_write": { + "title": "Canviar el missatge que escriviu", + "body": "Substituir o afegir text en un missatge mentre l'escriviu, quan feu servir el connector a l'editor. Podeu desfer cada canvi i continueu decidint si l'envieu." + }, + "ui_notifications": { + "title": "Mostrar notificacions", + "body": "Mostrar notificacions al dispositiu, o dins l'aplicació si no les heu permeses. Cadascuna indica el nom del connector." + }, + "ui_message_list": { + "title": "Afegir elements a la llista de missatges", + "body": "Mostrar contingut propi a sobre de la llista de missatges, i botons als missatges i a la barra dels missatges seleccionats." + }, + "ui_contacts": { + "title": "Afegir elements a la vista de contactes", + "body": "Mostrar seccions pròpies als detalls d'un contacte i botons a la llista de contactes." + }, + "ui_tasks": { + "title": "Afegir elements a la vista de tasques", + "body": "Mostrar seccions pròpies als detalls d'una tasca i botons a la llista de tasques." + }, + "ui_files": { + "title": "Afegir elements a la vista de fitxers", + "body": "Mostrar seccions pròpies a la previsualització de fitxers i entrades al menú d'un fitxer." + }, + "ui_commands": { + "title": "Afegir ordres", + "body": "Afegir entrades pròpies a la paleta d'ordres (Ctrl+K)." + }, + "ui_app_page": { + "title": "Afegir pàgines", + "body": "Afegir pàgines pròpies a l'aplicació, cadascuna amb un botó a la barra de navegació." + }, + "http_proxy": { + "title": "Fer servir els comptes que connecteu", + "body": "Enviar peticions a través del servidor de Bulwark amb un compte o una clau que heu configurat vós o el vostre administrador. El connector no veu mai la contrasenya, però pot fer tot el que aquest compte pot fer al seu propi servidor." + } + }, + "dialog": { + "from_plugin": "Del connector {name}", + "ok": "D'acord", + "submit": "Envia", + "confirm": "Confirma", + "cancel": "Cancel·la", + "close": "Tanca", + "turn_off": "Desactiva {name}", + "plugin_menu": "Opcions de {name}", + "password_note": "Aquest formulari ve del connector {name}, no del correu web. Introduïu una contrasenya només si confieu en aquest connector.", + "toast_auto_disabled": "{name} s'ha desactivat perquè fallava repetidament." + }, + "connect": { + "title": "Connecta {label}", + "intro_password": "{name} vol utilitzar el vostre compte de {label}. Bulwark desa la contrasenya al seu servidor i només l'envia a {host}. El connector no la veu mai.", + "intro_password_any": "{name} vol utilitzar el vostre compte de {label}. Bulwark desa la contrasenya al seu servidor i només l'envia al servidor que introduïu aquí. El connector no la veu mai.", + "intro_signin": "{name} vol iniciar la sessió al vostre compte de {label}. Introduïu aquí el servidor i després inicieu la sessió a la seva pròpia pàgina. Bulwark desa al seu servidor l'accés que obté; el connector no el veu mai.", + "server": "Adreça del servidor", + "server_hint": "cloud.example.com", + "username": "Nom d'usuari", + "secret": "Contrasenya o clau", + "connect": "Connecta", + "continue": "Continua", + "cancel": "Cancel·la", + "saving": "S'està desant…", + "error_server": "Introduïu l'adreça del servidor, per exemple https://cloud.example.com", + "error_save": "No s'ha pogut desar: {error}" + } + }, + "global_search": { + "title": "Cerca-ho tot", + "placeholder": "Cerca al correu, contactes, calendari, fitxers...", + "scope_label": "Cerca a", + "account_label": "Compte", + "scope_all": "Tot", + "scope_mail": "Correu", + "scope_contacts": "Contactes", + "scope_calendar": "Calendari", + "scope_files": "Fitxers", + "all_accounts": "Tots els comptes", + "show_all": "Mostra tots els resultats", + "no_results": "Cap resultat per a “{query}”", + "server_hint": "Les paraules completes cerquen al servidor; les parcials coincideixen amb els elements ja carregats", + "searching": "Cercant...", + "account_error": "No s'ha pogut cercar a {account}", + "recent_searches": "Cerques recents", + "recurring": "Periòdic", + "no_subject": "(no subject)", + "empty_hint": "Cerca correu, contactes, esdeveniments i fitxers a tots els comptes. Prova from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Pla per data", + "grouped": "Agrupat", + "load_more": "Carrega'n més", + "preview_empty": "Seleccioneu un resultat per previsualitzar-lo", + "open_full": "Obre", + "view_label": "Vista" + }, + "top_bar": { + "more": "Més opcions" + }, + "plugin_pages": { + "loading": "S'està carregant la pàgina", + "not_available_title": "Aquesta pàgina no està disponible", + "not_available_description": "El connector que l'ha afegida està desactivat o ja no està instal·lat.", + "back_to_mail": "Torna al correu", + "settings_group": "Connectors" + }, + "command_palette": { + "title": "Paleta d'ordres", + "open": "Obre la paleta d'ordres", + "placeholder": "Escriviu una ordre", + "no_results": "Cap ordre coincident", + "go_to": "Ves a {name}", + "compose": "Missatge nou", + "search": "Cerca al correu", + "theme_dark": "Canvia al tema fosc", + "theme_light": "Canvia al tema clar", + "shortcuts": "Mostra les dreceres de teclat", + "group_navigation": "Ves a", + "group_actions": "Accions", + "group_plugins": "Connectors" + }, + "notification_center": { + "title": "Notificacions", + "mark_all_read": "Marca-ho tot com a llegit", + "clear": "Esborra", + "empty": "No hi ha notificacions", + "close": "Tanca", + "unread": "No llegida", + "unread_count": "{count} sense llegir", + "level_error": "Error", + "level_warning": "Avís" + } +} diff --git a/locales/cs/common.json b/locales/cs/common.json index 72b5a579..4dfa844f 100644 --- a/locales/cs/common.json +++ b/locales/cs/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimalistický webmailový klient využívající protokol JMAP", - "login": { - "title": "Webmail", - "username_label": "E-mail", - "username_placeholder": "uzivatel@example.com", - "password_label": "Heslo", - "password_placeholder": "Zadejte heslo", - "jmap_endpoint_label": "JMAP server", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Server musí povolovat požadavky CORS z této domény.", - "jmap_server_label": "Server", - "jmap_server_auto_picked": "Server vybrán podle domény vašeho e-mailu.", - "sign_in": "Přihlásit se", - "signing_in": "Přihlašování...", - "loading": "Načítání...", - "reconnecting": "Spojení ztraceno. Pokus o opětovné připojení…", - "error": { - "invalid_credentials": "Neplatná e-mailová adresa nebo heslo. Zkontrolujte přihlašovací údaje a zkuste to znovu.", - "connection_failed": "Nelze se připojit k serveru. Zkontrolujte připojení k internetu a zkuste to znovu.", - "cors_blocked": "Server je dostupný, ale blokuje požadavky mezi doménami. Zkontrolujte nastavení CORS serveru JMAP a povolte tuto doménu.", - "server_error": "Server je dočasně nedostupný. Zkuste to prosím později.", - "generic": "Došlo k neočekávané chybě. Pokud problém přetrvává, kontaktujte správce.", - "totp_required": "Je vyžadován kód dvoufázového ověření. Zadejte kód níže.", - "totp_invalid": "Neplatný ověřovací kód. Zkontrolujte svou ověřovací aplikaci a zkuste to znovu.", - "oauth_discovery_failed": "SSO je povoleno, ale nelze se připojit k poskytovateli identity. Zkontrolujte konfiguraci OAuth." - }, - "show_password": "Zobrazit heslo", - "hide_password": "Skrýt heslo", - "totp_toggle": "Mám 2FA kód", - "remember_me": "Zapamatovat si mě", - "config_error": { - "title": "Chyba konfigurace", - "fetch_failed": "Konfiguraci aplikace nelze načíst. Zkuste to prosím později.", - "server_not_configured": "Poštovní server nebyl nakonfigurován. Kontaktujte správce." - }, - "remove_from_history": "Odstranit z historie", - "totp_label": "Ověřovací kód", - "totp_placeholder": "000000", - "session_expired": "Platnost relace vypršela. Přihlaste se prosím znovu.", - "dismiss": "Zavřít", - "or": "nebo", - "sign_in_sso": "Přihlásit se přes SSO", - "add_account_title": "Přidat účet", - "add_account_subtitle": "Přihlásit se k jinému účtu", - "cancel": "Zrušit", - "website": "Webové stránky", - "imprint": "Tiráž", - "privacy_policy": "Zásady ochrany osobních údajů", - "try_demo": "Vyzkoušet demoverzi", - "demo_description": "Prozkoumejte aplikaci s ukázkovými daty - bez nutnosti zakládat účet", - "demo_launching": "Spouštění demoverze...", - "demo_login_button": "Spustit demoverzi", - "demo_tagline": "Vyzkoušejte plně funkční e-mailový klient. Účet není vyžadován.", - "demo_no_signup": "Bez registrace - volně prozkoumejte s ukázkovými daty", - "oauth_completing": "Dokončování přihlášení...", - "oauth_error": { - "title": "Ověření selhalo", - "invalid_state": "Bezpečnostní ověření selhalo. Zkuste se přihlásit znovu.", - "missing_params": "Chybí autorizační data. Zkuste se přihlásit znovu.", - "token_exchange_failed": "Ověření se nepodařilo dokončit. Zkuste to znovu.", - "access_denied": "Přístup odepřen. Kontaktujte správce.", - "back_to_login": "Zpět na přihlášení" - } - }, - "sidebar": { - "close": "Zavřít", - "compose": "Napsat", - "compose_hint": "Napsat (c)", - "search_placeholder": "Hledat ve zprávách...", - "search_placeholder_hint": "Hledat ve zprávách... (stiskněte /)", - "storage": "Úložiště", - "storage_used": "Využito", - "storage_free": "Volno", - "storage_total": "Celkem", - "sign_out": "Odhlásit se", - "sign_out_of": "Odhlásit se z {account}", - "sign_out_all": "Odhlásit se ze všech účtů", - "add_account": "Přidat účet", - "set_as_default": "Nastavit jako výchozí", - "switch_account": "Přepnout účet", - "contacts": "Kontakty", - "calendar": "Kalendář", - "settings": "Nastavení", - "admin": "Administrace", - "files": "Soubory", - "loading_mailboxes": "Načítání schránek...", - "push_connected": "Aktualizace v reálném čase aktivní", - "push_disconnected": "Aktualizace v reálném čase neaktivní", - "keyboard_shortcuts": "Klávesové zkratky", - "theme": { - "light": "Světlý režim", - "dark": "Tmavý režim", - "system": "Systémový motiv" - }, - "language": { - "title": "Jazyk" - }, - "mailboxes": { - "inbox": "Doručené", - "sent": "Odeslané", - "drafts": "Koncepty", - "trash": "Koš", - "archive": "Archiv", - "starred": "S hvězdičkou", - "all_mail": "Všechny zprávy", - "spam": "Spam", - "important": "Důležité" - }, - "unified_inbox": "Sjednocená doručená pošta", - "unified_sent": "Všechny odeslané", - "unified_drafts": "Všechny koncepty", - "unified_trash": "Všechny koše", - "unified_archive": "Všechny archivy", - "unified_junk": "Všechen spam", - "all_accounts": "Všechny účty", - "unified_mailbox": "Sjednocená schránka", - "expand": "Rozbalit", - "collapse": "Sbalit", - "expand_tooltip": "Rozbalit", - "collapse_tooltip": "Sbalit", - "mobile": { - "search": "Hledat", - "compose": "Napsat", - "go_back": "Zpět", - "toggle_menu": "Přepnout nabídku" - }, - "clear_search": "Vymazat hledání", - "vacation_active": "Automatická odpověď je aktivní", - "demo_banner": "Demo režim", - "demo_reset": "Resetovat", - "demo_tour": "Průvodce", - "tags": "Štítky", - "show_all_tags": "Zobrazit vše ({count})", - "show_fewer_tags": "Zobrazit méně", - "folders": "Složky", - "shared": "Sdílené", - "mail": "Pošta", - "nav_label": "Navigace", - "add_app": "Aplikace", - "scheduled": "Naplánováno", - "unified_all_unread": "Vše nepřečtené", - "unified_all_starred": "Vše s hvězdičkou", - "unified_all_mail": "Veškerá pošta", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Výchozí aplikace", - "description": "Zvolte, zda se mají e-mailové a kalendářové odkazy otevírat v Bulwarku. Technicky se Bulwark registruje jako obslužná aplikace protokolu pro odkazy mailto: a webcal:.", - "unsupported": "Tento prohlížeč nebo toto připojení nepodporuje ruční registraci obslužné aplikace protokolu. Nainstalovanou PWA můžete případně použít přes nastavení prohlížeče nebo systému.", - "mailto_label": "E-mailové odkazy", - "mailto_description": "Otevře odkazy mailto: v Bulwarku s předvyplněným editorem zprávy.", - "protocol_open_mode_label": "Při otevírání odkazů protokolů", - "protocol_open_mode_description": "Zvolte, zda má Bulwark otevírat odkazy mailto: a webcal: v nové kartě, nebo znovu použít otevřenou relaci. Volba aktivní relace vyžaduje oprávnění k oznámením, abyste mohli kliknout na záložní oznámení a přenést Bulwark do popředí, pokud prohlížeč blokuje fokus.", - "protocol_open_mode_active_session": "Otevřít v aktivní relaci, pokud je to možné", - "protocol_open_mode_new_tab": "Vždy otevřít novou kartu", - "focus_notification_title": "Otevřít Bulwark", - "focus_notification_body": "Odkaz byl otevřen v Bulwarku. Kliknutím přenesete okno do popředí.", - "webcal_label": "Kalendářové odkazy", - "webcal_description": "Otevře odkazy webcal: v Bulwarku s předvyplněným dialogem pro odběr kalendáře.", - "register_mailto": "Registrovat e-mailovou aplikaci", - "register_webcal": "Registrovat kalendářovou aplikaci", - "mailto_registered": "Registrace obsluhy e-mailových odkazů byla vyžádána", - "webcal_registered": "Registrace obsluhy kalendářových odkazů byla vyžádána", - "registration_failed": "Registrace obslužné aplikace protokolu selhala", - "opening_mailto": "Otevírá se editor...", - "opening_webcal": "Otevírá se kalendář...", - "browser_note": "Prohlížeč nebo operační systém vás může požádat o potvrzení a může vyžadovat, aby byl Bulwark nainstalovaný, než jej půjde vybrat jako výchozí aplikaci.", - "select_account_title": "Vybrat účet", - "select_mailto_account": "Vyberte účet, ve kterém se má tento e-mailový odkaz otevřít.", - "select_webcal_account": "Vyberte účet, ve kterém se má tento kalendářový odkaz otevřít.", - "select_account_note": "Tato volba platí jen pro tento odkaz protokolu.", - "detail_to": "Komu", - "detail_subject": "Předmět", - "detail_no_subject": "Bez předmětu", - "detail_calendar": "Kalendář", - "detail_source": "Zdroj", - "active_account": "Aktivní", - "switching_account": "Přepínání účtu..." - }, - "sidebar_apps": { - "modal_title": "Aplikace postranního panelu", - "add_new": "Přidat aplikaci", - "edit_app": "Upravit aplikaci", - "name_label": "Název", - "name_placeholder": "Moje aplikace", - "name_required": "Název je vyžadován", - "url_label": "URL", - "url_required": "URL je vyžadováno", - "url_invalid": "Zadejte platnou adresu http nebo https", - "icon_label": "Ikona", - "icon_required": "Ikona je vyžadována", - "open_mode_label": "Režim otevírání", - "open_new_tab": "Nová karta", - "open_inline": "Vložené", - "cancel": "Zrušit", - "add": "Přidat", - "update": "Aktualizovat", - "delete": "Odstranit", - "delete_confirm_title": "Odstranit aplikaci", - "delete_confirm": "Opravdu chcete odstranit \"{name}\"?", - "no_apps": "Zatím nebyly přidány žádné aplikace", - "no_apps_hint": "Přidejte vlastní aplikace a odkazy do postranního panelu", - "managed_title": "Poskytnuto správcem", - "managed_description": "Tyto aplikace jsou nastaveny pro všechny a nelze je změnit.", - "managed_badge": "Spravováno", - "search_icons": "Hledat ikony...", - "show_popular": "Oblíbené", - "show_all": "Všechny", - "no_icons_found": "Ikony nenalezeny", - "inline_badge": "Vložené", - "tab_badge": "Karta", - "show_on_mobile": "Zobrazit na mobilu" - }, - "email_list": { - "no_emails": "Nenalezeny žádné zprávy", - "no_emails_description": "Tato schránka je prázdná", - "no_search_results": "Nenalezeny žádné výsledky", - "no_search_results_description": "Zkuste změnit hledání nebo filtry", - "loading": "Načítání zpráv...", - "unread": "nepřečtené", - "to_me": "Pro mě", - "to_recipients": "Pro {count} příjemců", - "and_others": "a {count} dalších", - "draft": "Koncept", - "starred": "S hvězdičkou", - "pinned": "Připnuto", - "replied": "Zodpovězeno", - "forwarded": "Přeposláno", - "has_attachment": "Obsahuje přílohu", - "conversations_count": "{count} z {total} konverzací", - "conversations_count_plus": "Více než {count} konverzací", - "conversations_count_simple": "{count} konverzací", - "no_conversations": "Žádné konverzace", - "loading_more": "Načítání dalších zpráv...", - "no_more_emails": "Žádné další zprávy k načtení", - "batch_actions": { - "select": "Vybrat zprávy", - "select_all": "Vybrat vše", - "selected_messages": "{count, plural, one {Vybraný 1 e-mail} other {Vybrané # e-maily}}", - "mark_read": "Označit jako přečtené", - "mark_unread": "Označit jako nepřečtené", - "delete": "Odstranit", - "delete_confirm_title": "Odstranit zprávy", - "delete_confirm_message": "Opravdu chcete odstranit {count, plural, one {1 zprávu} few {# zprávy} other {# zpráv}}?", - "clear_selection": "Zrušit výběr" - }, - "permanent_delete": "Trvale odstranit", - "permanent_delete_confirm_title": "Trvalé odstranění", - "permanent_delete_confirm_message": "Tato zpráva bude trvale odstraněna. Tuto akci nelze vrátit zpět.", - "permanent_delete_confirm_batch_message": "{count, plural, one {1 zpráva bude trvale odstraněna} few {# zprávy budou trvale odstraněny} other {# zpráv bude trvale odstraněno}}. Tuto akci nelze vrátit zpět.", - "empty_folder": { - "button": "Vyprázdnit složku", - "confirm_title": "Vyprázdnit složku", - "confirm_message": "Všechny zprávy v této složce budou trvale odstraněny. Tuto akci nelze vrátit zpět.", - "confirm_button": "Vyprázdnit složku", - "junk_hint": "Můžete vyprázdnit složku Spam a trvale odstranit všechny zprávy.", - "trash_hint": "Můžete vyprázdnit Koš a trvale odstranit všechny zprávy." - }, - "no_scheduled_emails": "Žádné naplánované e-maily", - "no_scheduled_emails_description": "Zprávy naplánované na později se zobrazí zde.", - "scheduled_count": "{count, plural, one {1 naplánovaný e-mail} few {# naplánované e-maily} other {# naplánovaných e-mailů}}", - "scheduled_actions_hint": "Naplánované zprávy můžete zrušit, přeplánovat nebo upravit pomocí akcí plánování.", - "cancel_scheduled_send": "Zrušit odeslání", - "reschedule_send": "Přeplánovat", - "cancel_and_edit": "Zrušit a upravit", - "cancel_and_compose_again": "Zrušit a napsat znovu", - "reschedule_prompt": "Zadejte nové datum/čas, např. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Odesílatel žádá o potvrzení o přečtení:", - "send": "Odeslat potvrzení", - "ignore": "Ignorovat", - "sent": "Potvrzení o přečtení odesláno.", - "send_failed": "Potvrzení o přečtení se nepodařilo odeslat", - "mdn_subject": "Přečteno: {subject}", - "mdn_body": "Toto je potvrzení o přečtení zprávy, kterou jste odeslali na adresu {recipient}.\n\nPoznámka: Toto potvrzení pouze potvrzuje, že zpráva byla zobrazena na počítači příjemce. Nezaručuje, že příjemce obsah přečetl nebo mu porozuměl." - }, - "no_email_selected": "Není vybrána žádná zpráva", - "no_email_description": "Vyberte zprávu ze seznamu pro její zobrazení", - "no_conversation_selected": "Není vybrána žádná konverzace", - "no_conversation_description": "Vyberte konverzaci ze seznamu pro její zobrazení", - "compose": "Napsat", - "compose_hint": "Napsat novou zprávu", - "no_subject": "(Bez předmětu)", - "no_body_content": "(Žádný obsah těla)", - "no_preview_available": "Náhled není k dispozici", - "show_quoted_text": "Zobrazit citovaný text", - "hide_quoted_text": "Skrýt citovaný text", - "loading_email": "Načítání zprávy...", - "loading": "Načítání...", - "reply": "Odpovědět", - "reply_all": "Odpovědět všem", - "forward": "Přeposlat", - "delete": "Odstranit", - "archive": "Archivovat", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Označit hvězdičkou", - "unstar": "Odebrat hvězdičku", - "mark_unread": "Označit jako nepřečtené", - "mark_read": "Označit jako přečtené", - "unread": "Nepřečtené", - "read": "Přečtené", - "spam_short": "Spam", - "not_spam_short": "Není spam", - "move": "Přesunout", - "print": "Tisk", - "view_source": "Zobrazit zdrojový kód", - "fullscreen": "Celá obrazovka", - "exit_fullscreen": "Ukončit režim celé obrazovky", - "export_email": "Exportovat jako .eml", - "forward_as_attachment": "Přeposlat jako přílohu", - "import_email": "Importovat .eml nebo .zip", - "keyboard_shortcuts": "Klávesové zkratky (?)", - "email_source": "Zdrojový kód zprávy", - "draft_banner": "Tato zpráva je koncept", - "edit_draft": "Upravit", - "copy_source": "Kopírovat do schránky", - "source_copied": "Zdrojový kód byl zkopírován do schránky", - "attachments": "Přílohy", - "important": "Důležité", - "download": "Stáhnout", - "download_all": "Stáhnout vše", - "from": "Od", - "to": "Komu", - "cc": "Kopie", - "bcc": "Skrytá kopie", - "date": "Datum", - "subject": "Předmět", - "show_details": "Zobrazit podrobnosti", - "hide_details": "Skrýt podrobnosti", - "external_content_warning": "Obrázky a externí obsah byly zablokovány", - "load_external_content": "Načíst obrázky", - "trust_sender": "Vždy důvěřovat tomuto odesílateli", - "back_to_list": "Zpět na seznam", - "view_contact": "Zobrazit kontakt", - "message_details": "Podrobnosti zprávy", - "more_reply_options": "Další možnosti odpovědi", - "set_tag": "Nastavit štítek", - "tag": "Štítek", - "more_actions": "Další akce", - "previous": "Předchozí", - "next": "Další", - "move_to": "Přesunout do...", - "remove_tag": "Odebrat štítek", - "tag_filter_placeholder": "Filtrovat štítky", - "tag_no_matches": "Žádné odpovídající štítky", - "more_count": "+{count} dalších", - "characters_count": "{count} znaků", - "quick_reply_placeholder": "Napsat rychlou odpověď...", - "more_options": "Další možnosti", - "sending": "Odesílání...", - "security_authentication": "Zabezpečení a ověřování", - "technical_details": "Technické podrobnosti", - "message_id_label": "Message-ID:", - "reply_to_label": "Reply-To:", - "delivery_time_label": "Čas doručení:", - "conversation_part_label": "Část konverzace:", - "previous_messages": "{count} předchozí zpráva", - "previous_messages_plural": "{count} předchozích zpráv", - "time": { - "day": "den", - "days": "dní", - "hour": "hodina", - "hours": "hodin", - "minute": "minuta", - "minutes": "minut" - }, - "unknown_sender": "Neznámý", - "recipient_me": "já", - "recipient_and_others": "{name} a {count} dalších", - "recipient_to_prefix": "Komu:", - "authentication": { - "title": "Ověřování", - "status": { - "verified": "Ověřeno", - "warning": "Upozornění", - "none": "Neověřeno" - }, - "spf": { - "pass": "SPF úspěšné", - "fail": "SPF selhalo", - "none": "Bez SPF" - }, - "dkim": { - "pass": "DKIM platný", - "fail": "DKIM neplatný", - "none": "Bez DKIM" - }, - "dmarc": { - "pass": "DMARC úspěšné", - "fail": "DMARC selhalo", - "none": "Bez DMARC" - }, - "spam_score": "Skóre spamu", - "tooltip_spf": "Sender Policy Framework: ověřuje, zda je odesílající server oprávněn odesílat e-maily jménem domény", - "tooltip_dkim": "DomainKeys Identified Mail: kryptografickým podpisem potvrzuje, že e-mail nebyl při přenosu změněn", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zajišťuje shodu SPF a DKIM s doménou odesílatele a stanoví zásadu pro selhání", - "policy": "Zásada", - "result": { - "pass": "Úspěšné", - "fail": "Selhalo", - "softfail": "Mírné selhání", - "neutral": "Neutrální", - "permerror": "Trvalá chyba", - "temperror": "Dočasná chyba", - "none": "Žádné" - } - }, - "headers": { - "routing": "Směrování", - "received": "Přijato", - "message_id": "ID zprávy", - "list_info": "Informace o konferenci" - }, - "tooltips": { - "reply": "Odpovědět (r)", - "reply_all": "Odpovědět všem (a)", - "forward": "Přeposlat (f)", - "archive": "Archivovat (e)", - "delete": "Odstranit (# nebo Del)", - "star": "Označit hvězdičkou (s)", - "unstar": "Odebrat hvězdičku (s)", - "compose": "Napsat (c)", - "previous": "Předchozí zpráva", - "next": "Další zpráva", - "edit_draft": "Upravit koncept" - }, - "spam": { - "button_title": "Nahlásit spam", - "not_spam_title": "Označit jako bezpečné", - "toast_success": "Přesunuto do Spamu", - "toast_batch": "{count} zpráv přesunuto do Spamu", - "toast_undo": "Zpět", - "toast_not_spam_success": "Přesunuto do Doručených", - "toast_not_spam_batch": "{count} zpráv přesunuto do Doručených", - "error": "Nahlášení spamu selhalo", - "error_not_spam": "Obnovení zprávy selhalo" - }, - "unsubscribe_banner": { - "label": "Newsletter", - "button": "Odhlásit odběr", - "confirm_title": "Odhlásit odběr od tohoto odesílatele?", - "confirm_button": "Potvrdit", - "cancel": "Zrušit", - "success_http": "Stránka pro odhlášení byla otevřena na nové kartě", - "success_mailto": "Odhlašovací e-mail byl odeslán", - "confirm_message_http": "Odhlašovací stránka se otevře na nové kartě.", - "confirm_message_mailto": "Odesílateli bude zaslán odhlašovací e-mail.", - "error": "Odběr nelze odhlásit", - "dismiss": "Zavřít" - }, - "calendar_invitation": { - "loading": "Načítání podrobností události…", - "title": "Pozvánka kalendáře", - "published_title": "Publikovaná událost", - "response_title": "Odpověď na událost", - "update_title": "Aktualizace události", - "counter_title": "Návrh na změnu", - "refresh_title": "Požadavek na obnovení", - "declined_counter_title": "Návrh na změnu zamítnut", - "cancelled_title": "Událost zrušena", - "organizer": "Organizátor: {name}", - "organizer_label": "Organizátor:", - "attendees": "{count, plural, one {1 účastník} few {# účastníci} other {# účastníků}}", - "accept": "Přijmout", - "maybe": "Možná", - "decline": "Odmítnout", - "add_to_calendar": "Přidat do kalendáře", - "added": "Přidáno do kalendáře", - "rsvp_sent": "Odpověď odeslána", - "parse_error": "Nelze přečíst pozvánku", - "action_failed": "Akce kalendáře selhala.", - "no_calendar": "Kalendář není dostupný", - "published_info": "Tato událost byla sdílena pro vaši informaci.", - "response_info": "Tato zpráva obsahuje odpověď účastníka.", - "response_info_organizer": "Tato odpověď účastníka aktualizuje vaši událost.", - "update_info": "Tato zpráva aktualizuje existující událost.", - "counter_info": "Tato zpráva navrhuje změny události.", - "counter_info_organizer": "Tento účastník navrhl změny vaší události.", - "refresh_info": "Tato zpráva vyžaduje nejnovější podrobnosti o události.", - "refresh_info_organizer": "Účastník požádal o nejnovější podrobnosti o události.", - "declined_counter_info": "Organizátor odmítl návrh na změnu.", - "authentication_failed_info": "Ověřování zpráv u této pozvánky selhalo. S akcemi kalendáře zacházejte opatrně.", - "authentication_missing_info": "Tato pozvánka neobsahuje ověření zprávy. Pokud se vám cokoli zdá neobvyklé, potvrďte si podrobnosti s organizátorem.", - "sender_mismatch_info": "Tato pozvánka byla odeslána od {sender}, zatímco organizátor v datech kalendáře je {organizer}.", - "sender_mismatch_unverified_info": "Tato pozvánka byla odeslána od {sender}, zatímco organizátor v datech kalendáře je {organizer}, a zprávu se nepodařilo ověřit.", - "organizer_role": "Jste organizátorem této události", - "your_response": "Vaše odpověď: {status}", - "response_needed": "Vyžaduje odpověď", - "response_accepted": "Přijato", - "response_tentative": "Nezávazně", - "response_declined": "Odmítnuto", - "response_delegated": "Delegováno", - "actor_sent_info": "Odeslal(a) {name}.", - "actor_response_info": "{name} odpověděl(a) {status}.", - "actor_counter_info": "{name} navrhl(a) změny pro tuto událost.", - "actor_refresh_info": "{name} požádal(a) o nejnovější podrobnosti o události.", - "actor_declined_counter_info": "{name} odmítl(a) návrh na změnu.", - "actor_note": "Poznámka: {comment}", - "actor_unknown": "Někdo", - "proposed_changes": "Navrhované změny", - "change_title": "Název", - "change_time": "Čas", - "change_location": "Místo", - "change_description": "Popis", - "change_empty": "Žádné", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Použít navrhované změny", - "proposal_applied": "Navrhované změny byly použity.", - "review_proposal": "Zkontrolovat návrh", - "review_request": "Zkontrolovat požadavek", - "view_in_calendar": "Zobrazit v kalendáři", - "select_calendar": "Vybrat kalendář", - "already_in_calendar": "Již je ve vašem kalendáři", - "request_info": "Byli jste pozváni na tuto událost. Odpovězte, abyste informovali organizátora o své dostupnosti.", - "cancel_info": "Organizátor tuto událost zrušil.", - "event_updated": "Aktualizace #{sequence}", - "event_status_tentative": "Nezávazně", - "event_status_cancelled": "Zrušeno", - "expand": "Zobrazit podrobnosti", - "collapse": "Skrýt podrobnosti" - }, - "send": "Odeslat", - "more": "více", - "details": { - "recipients_routing": "Příjemci a směrování", - "authentication_security": "Ověřování a zabezpečení", - "identifiers_threading": "Identifikátory a vlákna", - "mailing_list": "Mailing list", - "message_properties": "Vlastnosti zprávy", - "sent": "Odesláno", - "received": "Přijato", - "delivery_time": "Doba doručení", - "in_reply_to": "V odpovědi na", - "references": "Odkazy", - "thread_id": "ID vlákna", - "size": "Velikost", - "mime_type": "Typ MIME", - "attachments_summary": "{count} souborů · {size}", - "list_id": "ID seznamu", - "list_help": "Nápověda seznamu", - "list_post": "Příspěvek do seznamu", - "list_unsubscribe": "Odhlásit se", - "iprev": "Reverzní DNS", - "spam_status": "Stav spamu", - "ai_verdict": "Verdikt AI", - "account": "Účet", - "no_subject": "(bez předmětu)" - }, - "scheduled_banner": "Naplánováno k odeslání {date}", - "scheduled_send_created": "E-mail byl naplánován k odeslání", - "cancel_scheduled_send": "Zrušit odeslání", - "reschedule_send": "Přeplánovat", - "cancel_and_edit": "Zrušit a upravit", - "cancel_and_compose_again": "Zrušit a napsat znovu", - "reschedule_prompt": "Zadejte nové datum/čas, např. 2026-05-04T15:30", - "scheduled_actions_only": "Zobrazení Naplánováno podporuje pouze akce plánování", - "undo_send_scheduled": "Zpráva je naplánována k odeslání", - "undo_send": "Vrátit odeslání", - "contact_sidebar": { - "title": "Kontakt", - "close": "Zavřít postranní panel", - "action_email": "E-mail", - "action_email_title": "Odeslat e-mail", - "action_copy": "Kopírovat", - "action_copy_title": "Kopírovat e-mail", - "action_edit_title": "Upravit kontakt", - "section_emails": "E-maily", - "section_phones": "Telefony", - "section_organizations": "Organizace", - "section_addresses": "Adresy", - "section_notes": "Poznámky", - "not_in_contacts": "Není ve vašich kontaktech", - "add_to_contacts": "Přidat do kontaktů", - "copied": "Zkopírováno!", - "copy_failed": "Kopírování se nezdařilo" - }, - "send_now": "Odeslat nyní" - }, - "email_composer": { - "read_receipt_on": "Vyžádáno potvrzení o přečtení (kliknutím vypnete)", - "read_receipt_off": "Vyžádat potvrzení o přečtení", - "dsn_on": "Oznámení o doručení vyžádáno (kliknutím vypnete)", - "dsn_off": "Vyžádat oznámení o doručení", - "format_plain_text": "Přepnout na prostý text", - "format_rich_text": "Přepnout na formátovaný text (HTML)", - "require_tls_on": "Šifrované doručení vyžadováno (kliknutím vypnete)", - "require_tls_off": "Vyžadovat šifrované doručení (TLS)", - "new_message": "Nová zpráva", - "signature_edit_hint": "Dvojklikem upravíte podpis", - "reply": "Odpovědět", - "reply_all": "Odpovědět všem", - "forward": "Přeposlat", - "reply_to": "Odpovědět", - "reply_all_to": "Odpovědět všem", - "forward_message": "Přeposlat", - "from": "Od", - "to": "Komu", - "cc": "Kopie", - "bcc": "Skrytá kopie", - "subject": "Předmět", - "body_placeholder": "Napište zprávu...", - "send": "Odeslat", - "cancel": "Zrušit", - "attach": "Připojit", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Zahodit", - "discard_draft_title": "Zahodit koncept?", - "discard_draft_confirm": "Máte neuložené změny. Chcete tento koncept zahodit?", - "saving": "Ukládání...", - "draft_saved": "Koncept byl uložen", - "save_failed": "Uložení selhalo", - "to_placeholder": "E-mailové adresy příjemců", - "cc_placeholder": "Příjemci kopie", - "bcc_placeholder": "Příjemci skryté kopie", - "subject_placeholder": "Předmět", - "cc_label": "Kopie:", - "bcc_label": "Skrytá kopie:", - "subject_label": "Předmět:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(Bez předmětu)", - "unknown_sender": "Neznámý", - "quote": { - "reply_header": "Dne {date}, {sender} napsal(a):", - "forward_header": "---------- Přeposlaná zpráva ----------", - "from": "Od: {sender}", - "date": "Datum: {date}", - "subject": "Předmět: {subject}", - "to": "Komu: {recipients}" - }, - "remove_sub_address": "Odebrat subadresu", - "from_override": { - "toggle_off": "Přepsat", - "toggle_on": "Zrušit přepsání", - "toggle_tooltip": "Volně upravujte jméno a adresu odesílatele. Pošta se stále odesílá přes vaši identitu.", - "name_label": "Jméno odesílatele", - "name_placeholder": "Jméno", - "email_label": "E-mailová adresa odesílatele", - "email_placeholder": "alias@example.com", - "envelope_notice": "Pokud váš server tuto adresu jako odesílatele obálky nepřijme, použije se místo ní {identity} a příjemci ji uvidí v záhlaví Return-Path." - }, - "use_template": "Šablona", - "save_as_template": "Uložit jako šablonu", - "validation": { - "recipient_required": "Chcete-li zprávu odeslat, přidejte příjemce", - "body_required": "Napište zprávu nebo připojte soubor", - "attachments_uploading": "Přílohy se stále nahrávají – odeslání proběhne po dokončení", - "attachment_upload_failed": "Neodesláno - přílohu se nepodařilo nahrát. Odeberte ji a zkuste to znovu." - }, - "upload_progress": "Nahrávání {uploaded} / {total}", - "upload_cancel": "Zrušit nahrávání", - "upload_failed": "Soubor {filename} se nepodařilo nahrát", - "attachment_too_large": "„{name}“ je větší, než server povoluje ({max} na soubor)", - "attachments_total_too_large": "Přílohy by překročily limit serveru {max} na zprávu", - "drop_files": "Přetáhněte soubory sem a připojte je", - "show_less": "Zobrazit méně", - "send_failed": "Odeslání zprávy selhalo", - "send_timeout": "Server přestal odpovídat. Než odešlete znovu, zkontrolujte složku Odeslané – zpráva už mohla odejít.", - "continue_draft": "Pokračovat v úpravě konceptu", - "close_draft_title": "Uložit nebo zahodit koncept?", - "close_draft_message": "Máte neuložené změny. Chcete je uložit jako koncept, nebo zahodit?", - "smime_sign_on": "Podepisování S/MIME zapnuto", - "smime_sign_off": "Zapnout podepisování S/MIME", - "smime_encrypt_on": "Šifrování S/MIME zapnuto", - "smime_encrypt_off": "Zapnout šifrování S/MIME", - "smime_encrypt_unavailable": "Šifrování S/MIME není dostupné – chybí certifikáty příjemců", - "smime_unlock_title": "Odemknout klíč S/MIME", - "smime_unlock_message": "Zadejte heslo pro odemčení klíče k podpisu S/MIME.", - "smime_unlock_button": "Odemknout", - "smime_passphrase_placeholder": "Heslo", - "forgot_attachment": { - "title": "Nezapomněli jste na přílohu?", - "message": "Vaše zpráva obsahuje slovo \"{keyword}\", ale není k ní připojen žádný soubor. Přesto odeslat?", - "send_anyway": "Přesto odeslat", - "back": "Zpět k úpravám" - }, - "empty_subject": { - "title": "Odeslat bez předmětu?", - "message": "Tato zpráva nemá předmět. Přesto odeslat?", - "dont_ask_again": "Znovu se neptat", - "send_anyway": "Přesto odeslat", - "back": "Zpět k úpravám" - }, - "add_link": "Přidat odkaz", - "link_url_prompt": "Zadejte URL", - "sending": "Odesílání...", - "schedule_send": "Naplánovat odeslání", - "schedule_send_description": "Vyberte, kdy má server tuto zprávu odeslat.", - "schedule_send_required": "Vyberte datum a čas.", - "schedule_send_invalid": "Zadejte platné datum a čas.", - "schedule_send_future": "Vyberte budoucí datum a čas.", - "schedule_send_too_late": "Tento čas je později, než server dovoluje.", - "schedule_send_unsupported": "Naplánované odeslání není pro tento účet podporováno.", - "schedule_send_cleanup_warning": "Naplánované odeslání bylo vytvořeno, ale vyčištění konceptu selhalo.", - "send_delay_unsupported": "Prodleva odeslání není pro tento účet podporována.", - "send_delay_unsupported_confirm": "Tento účet nepodporuje prodlevu odeslání. Odeslat ihned?", - "recipient_edit_email": "Upravit e-mailovou adresu", - "recipient_edit_name": "Upravit zobrazované jméno", - "recipient_email_placeholder": "E-mailová adresa", - "recipient_name_placeholder": "Zobrazované jméno", - "autocomplete_search_server": "Hledat na serveru", - "autocomplete_searching": "Hledání...", - "toolbar": { - "bold": "Tučné", - "italic": "Kurzíva", - "underline": "Podtržené", - "strikethrough": "Přeškrtnuté", - "text_color": "Barva textu", - "remove_color": "Odebrat barvu", - "heading_1": "Nadpis 1", - "heading_2": "Nadpis 2", - "bullet_list": "Odrážkový seznam", - "ordered_list": "Číslovaný seznam", - "quote": "Citace", - "code_block": "Blok kódu", - "align_left": "Zarovnat vlevo", - "align_center": "Na střed", - "align_right": "Zarovnat vpravo", - "text_direction": "Směr textu (RTL/LTR)", - "link": "Odkaz", - "table": "Tabulka", - "clear_formatting": "Vymazat formátování", - "undo": "Zpět", - "redo": "Znovu", - "add_row_above": "Přidat řádek nad", - "add_row_below": "Přidat řádek pod", - "add_column_before": "Přidat sloupec před", - "add_column_after": "Přidat sloupec za", - "delete_row": "Odstranit řádek", - "delete_column": "Odstranit sloupec", - "toggle_header_row": "Přepnout řádek záhlaví", - "delete_table": "Odstranit tabulku", - "pick_size": "Vybrat velikost", - "font_size": "Velikost písma", - "font_size_default": "Výchozí", - "background_color": "Barva pozadí", - "remove_background_color": "Odebrat barvu pozadí" - }, - "send_filing_warning": "Odesláno - ale následný úklid selhal, může zůstat zastaralý koncept." - }, - "confirm_dialog": { - "confirm": "Potvrdit", - "cancel": "Zrušit" - }, - "common": { - "loading": "Načítání...", - "error": "Chyba", - "success": "Úspěch", - "cancel": "Zrušit", - "save": "Uložit", - "delete": "Odstranit", - "edit": "Upravit", - "close": "Zavřít", - "search": "Hledat", - "refresh": "Obnovit", - "settings": "Nastavení", - "help": "Nápověda", - "logout": "Odhlásit se", - "yes": "Ano", - "no": "Ne", - "unknown": "Neznámý", - "app_title": "Webmail", - "reconnecting": "Spojení ztraceno. Pokus o opětovné připojení…", - "rate_limited_title": "Ověřování serveru je dočasně omezeno.", - "rate_limited_detail": "Požadavky na pozadí byly pozastaveny, aby se zabránilo zablokování. Další pokus za {seconds} s.", - "rate_limited_action_title": "Požadavek pozastaven, aby se zabránilo zablokování.", - "rate_limited_action_detail": "Aplikace čeká na ukončení doby blokování serveru před odesláním dalších ověřovaných požadavků. Zkuste to znovu za {seconds} s.", - "pull_to_refresh": "Přetažením obnovíte", - "release_to_refresh": "Uvolněním obnovíte", - "refreshing": "Obnovování…" - }, - "notifications": { - "email_sent": "Zpráva byla úspěšně odeslána", - "email_deleted": "Zpráva byla odstraněna", - "email_archived": "Zpráva byla archivována", - "emails_deleted": "{count, plural, one {# e-mail smazán} few {# e-maily smazány} other {# e-mailů smazáno}}", - "emails_archived": "{count, plural, one {# e-mail archivován} few {# e-maily archivovány} other {# e-mailů archivováno}}", - "error_archiving": "E-mail se nepodařilo archivovat", - "error_updating": "E-mail se nepodařilo aktualizovat", - "email_starred": "Zpráva byla označena hvězdičkou", - "email_unstarred": "Hvězdička byla odebrána", - "email_marked_read": "Zpráva byla označena jako přečtená", - "email_marked_unread": "Zpráva byla označena jako nepřečtená", - "copied_to_clipboard": "Zkopírováno do schránky", - "source_copied": "Zdrojový kód zkopírován do schránky", - "error_sending": "Odeslání zprávy selhalo", - "error_deleting": "Odstranění zprávy selhalo", - "error_loading": "Načtení zprávy selhalo", - "new_email": "Nová zpráva", - "new_email_from": "Od {sender}", - "click_to_view": "Kliknutím zobrazíte", - "email_moved": "Zpráva byla přesunuta", - "emails_moved": "{count} zpráv bylo přesunuto", - "moved_to_mailbox": "Přesunuto do složky {mailbox}", - "move_failed": "Přesun selhal", - "move_error": "Zprávu nelze přesunout do vybrané složky", - "email_tagged": "Zpráva byla označena štítkem", - "emails_tagged": "{count} zpráv bylo označeno štítkem", - "tag_failed": "Označení štítkem selhalo", - "identity_created": "Identita úspěšně vytvořena", - "identity_updated": "Identita úspěšně aktualizována", - "identity_deleted": "Identita odstraněna", - "identity_set_primary": "Hlavní identita byla aktualizována", - "identity_create_failed": "Vytvoření identity selhalo: {error}", - "identity_update_failed": "Aktualizace identity selhala: {error}", - "identity_delete_failed": "Odstranění identity selhalo: {error}", - "identity_unauthorized": "Nemáte oprávnění odesílat z této e-mailové adresy", - "identity_not_found": "Identita nenalezena", - "vacation_saved": "Nastavení automatické odpovědi bylo uloženo", - "vacation_save_failed": "Uložení nastavení automatické odpovědi selhalo", - "filters_saved": "Filtry úspěšně uloženy", - "filters_save_failed": "Uložení filtrů selhalo", - "filters_deleted": "Pravidlo filtru bylo odstraněno", - "templates_exported": "Šablony úspěšně exportovány", - "templates_imported": "{count, plural, one {Importována 1 šablona} few {Importovány # šablony} other {Importováno # šablon}}", - "templates_import_errors": "Některé šablony se nepodařilo importovat", - "templates_import_empty": "V souboru nebyly nalezeny žádné šablony", - "export_email_error": "Export zprávy selhal", - "import_email_success": "Zpráva byla úspěšně importována", - "import_email_error": "Import zprávy selhal" - }, - "date": { - "today": "Dnes", - "yesterday": "Včera", - "this_week": "Tento týden", - "last_week": "Minulý týden", - "this_month": "Tento měsíc", - "older": "Starší", - "just_now": "Právě teď", - "minutes_ago": "před {count} minutou", - "minutes_ago_plural": "před {count} minutami", - "hours_ago": "před {count} hodinou", - "hours_ago_plural": "před {count} hodinami", - "days_ago": "před {count} dnem", - "days_ago_plural": "před {count} dny" - }, - "language": { - "title": "Jazyk", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "polish": "Polski", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Vybrat jazyk", - "switch_to_english": "Přepnout na angličtinu", - "switch_to_french": "Přepnout na francouzštinu", - "switch_to_japanese": "Přepnout na japonštinu", - "switch_to_spanish": "Přepnout na španělštinu", - "switch_to_italian": "Přepnout na italštinu", - "switch_to_german": "Přepnout na němčinu", - "switch_to_dutch": "Přepnout na nizozemštinu", - "switch_to_polish": "Přepnout na polštinu", - "switch_to_portuguese": "Přepnout na portugalštinu", - "switch_to_russian": "Přepnout na ruštinu", - "switching": "Změna jazyka...", - "switch": "Změnit jazyk", - "current": "Aktuální jazyk", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Přepnout na perštinu", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Přepnout na mongolštinu" - }, - "settings": { - "title": "Nastavení", - "back_to_mail": "Zpět na poštu", - "save_success": "Nastavení bylo uloženo", - "import_success": "Nastavení bylo importováno", - "import_error": "Import nastavení selhal", - "reset_confirm": "Opravdu chcete obnovit výchozí nastavení?", - "unsaved_changes": "Máte neuložené změny", - "discard_changes": "Zahodit neuložené změny?", - "discard": "Zahodit", - "keep_editing": "Pokračovat v úpravách", - "tabs": { - "appearance": "Vzhled", - "language": "Jazyk, region a čas", - "email": "Chování e-mailu", - "composer": "Psaní zpráv", - "privacy": "Soukromí a bezpečnost", - "account": "Účet", - "identities": "Identity", - "vacation": "Automatická odpověď", - "advanced": "Pokročilé", - "calendar": "Kalendář", - "filters": "Filtry", - "templates": "Šablony", - "folders": "Složky", - "keywords": "Štítky", - "security": "Zabezpečení", - "files": "Soubory", - "contacts": "Kontakty", - "encryption": "Šifrování", - "protocol_handlers": "Výchozí aplikace", - "sidebar_apps": "Aplikace postranního panelu", - "notifications": "Oznámení", - "layout": "Vzhled", - "reading": "Čtení", - "composing": "Psaní", - "downloads": "Stažené", - "content_senders": "Obsah a odesílatelé", - "about_data": "Info a data", - "debug": "Ladění" - }, - "tab_groups": { - "general": "Obecné", - "account": "Účet a identita", - "organization": "Organizace pošty", - "apps": "Aplikace", - "system": "Systém", - "appearance": "Vzhled", - "mail": "Pošta", - "privacy": "Soukromí a zabezpečení", - "advanced": "Pokročilé" - }, - "appearance": { - "message_list_order": { - "label": "Pořadí seznamu zpráv", - "description": "Jak jsou zprávy v seznamu seřazeny. Řazení provádí server, takže platí pro celou složku, ne jen pro načtené zprávy.", - "preset": { - "chronological": "Chronologicky (nejnovější první)", - "unread_first": "Nepřečtené první", - "starred_first": "S hvězdičkou první", - "tagged_first": "Se štítkem první", - "custom": "Vlastní" - }, - "tag_label": "Štítek", - "advanced_show": "Pokročilé: upravit úrovně řazení", - "advanced_hide": "Skrýt pokročilé", - "criterion_label": "Kritérium úrovně {n}", - "direction_label": "Směr úrovně {n}", - "criteria": { - "unread": "Nepřečtené", - "starred": "S hvězdičkou", - "tag": "Štítek", - "receivedAt": "Datum přijetí", - "sentAt": "Datum odeslání", - "from": "Odesílatel", - "subject": "Předmět", - "size": "Velikost" - }, - "direction": { - "unread_first": "Nepřečtené první", - "read_first": "Přečtené první", - "starred_first": "S hvězdičkou první", - "unstarred_first": "Bez hvězdičky první", - "tagged_first": "Se štítkem první", - "untagged_first": "Bez štítku první", - "newest_first": "Nejnovější první", - "oldest_first": "Nejstarší první", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Největší první", - "smallest_first": "Nejmenší první" - }, - "add_level": "Přidat úroveň", - "remove_level": "Odebrat úroveň", - "move_up": "Posunout nahoru", - "move_down": "Posunout dolů", - "tiebreak_note": "Až tři úrovně; další úrovně rozhodují při shodě. „Nejnovější první“ se vždy uplatní jako poslední.", - "scope": { - "label": "Použít na všechny složky", - "description": "Pokud je vypnuto, pořadí platí jen pro Doručenou poštu; ostatní složky zůstávají chronologické." - }, - "unsupported_note": "Váš poštovní server nepodporuje řazení podle stavu přečtení, hvězdiček ani štítků. Tyto úrovně se ignorují a seznam zůstává chronologický." - }, - "title": "Vzhled", - "description": "Přizpůsobte si vzhled a chování vašeho webového e-mailu", - "theme": { - "label": "Motiv", - "description": "Vyberte preferované barevné schéma", - "light": "Světlý", - "dark": "Tmavý", - "system": "Systémový" - }, - "language": { - "label": "Jazyk", - "description": "Vyberte preferovaný jazyk" - }, - "font_size": { - "label": "Velikost písma", - "description": "Upravte velikost textu pro lepší čitelnost", - "small": "Malé", - "medium": "Střední", - "large": "Velké" - }, - "list_density": { - "label": "Hustota zobrazení", - "description": "Ovládání rozestupů a výplní v celém rozhraní", - "extra_compact": "Velmi kompaktní", - "compact": "Kompaktní", - "regular": "Běžná", - "comfortable": "Pohodlná" - }, - "animations": { - "label": "Povolit animace", - "description": "Zobrazovat plynulé přechody a efekty" - }, - "toolbar_position": { - "label": "Pozice panelu nástrojů", - "description": "Kde se mají zobrazovat tlačítka pro akce se zprávou (Odpovědět, Archivovat, Odstranit atd.)", - "top": "Nahoře", - "below_subject": "Pod předmětem" - }, - "toolbar_labels": { - "label": "Zobrazovat popisky panelu nástrojů", - "description": "Zobrazovat textové štítky vedle ikon na panelu nástrojů. Vypněte, abyste ušetřili místo, jakmile se s ikonami seznámíte." - }, - "hide_account_switcher": { - "label": "Skrýt přepínač účtů v postranním panelu", - "description": "Skryje výběr účtu v horní části postranního panelu. Stále můžete přepínat účty pomocí spodního navigačního panelu." - }, - "show_rail_account_list": { - "label": "Zobrazovat avatary účtů v navigačním panelu", - "description": "Zobrazovat samostatné ikony účtů ve spodní části navigačního panelu pro rychlé přepínání, s tlačítkem odhlášení níže." - }, - "unified_mailbox": { - "label": "Sjednocená schránka", - "description": "Zobrazovat sloučené složky (Doručené, Odeslané atd.) ze všech připojených účtů", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Zahrnout skupinové schránky", - "description": "Zahrnout do sjednoceného zobrazení také sdílené/skupinové schránky." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Barevné ikony postranního panelu", - "description": "Obarví ikony složek a štítků podle typu (modré Doručené, červený Spam, zelené Odeslané atd.). Vypněte pro jednobarevný postranní panel." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Zobrazit celkový počet zpráv", - "description": "Zobrazí celkový počet zpráv vedle složek a štítků spolu s počtem nepřečtených. Vypněte pro zobrazení pouze nepřečtených." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro rozhraní (experimentální)", - "description": "Rozložení pro pokročilé uživatele pouze pro stolní počítače s prohlížením zpráv na více kartách a pracovními postupy napříč účty. Standardní rozhraní zůstává nedotčeno; kdykoli se můžete vrátit.", - "open_label": "Otevřít Pro rozhraní", - "back_to_standard": "Zpět na standard" - }, - "cross_unread": { - "label": "Všechny účty: Nepřečtené", - "description": "Zobrazí v sekci Všechny účty položku s nepřečtenou poštou ze všech účtů, napříč všemi složkami kromě spamu, odeslané, archivu, koše a konceptů." - }, - "cross_starred": { - "label": "Všechny účty: S hvězdičkou", - "description": "Zobrazí v sekci Všechny účty položku s poštou s hvězdičkou ze všech účtů, napříč všemi složkami kromě spamu, odeslané, archivu, koše a konceptů." - }, - "cross_all": { - "label": "Všechny účty: Veškerá pošta", - "description": "Zobrazí v sekci Všechny účty položku s veškerou poštou ze všech účtů, napříč všemi složkami kromě spamu, odeslané, archivu, koše a konceptů." - } - }, - "keywords": { - "title": "E-mailové štítky", - "description": "Definujte štítky pro organizaci e-mailů pomocí barev. Ukládají se jako klíčová slova JMAP na serveru.", - "add_keyword": "Přidat štítek", - "label_field": "Zobrazovaný název", - "label_placeholder": "např. Práce, Osobní, Naliehavé", - "id_field": "ID štítku", - "id_placeholder": "např. prace, osobni", - "color_field": "Barva", - "id_exists": "Toto ID štítku již existuje", - "edit": "Upravit štítek", - "delete": "Odstranit štítek", - "save": "Uložit", - "add": "Přidat", - "cancel": "Zrušit", - "migrating": "Aktualizace štítku v existujících e-mailech…", - "migration_error": "Nepodařilo se aktualizovat štítek v existujících e-mailech", - "nesting": { - "label": "Vnořené štítky", - "description": "Vnořujte štítky pod jiné štítky a zobrazujte je v postranním panelu jako strom." - }, - "parent_field": "Nadřazený štítek", - "no_parent": "Bez nadřazeného štítku", - "too_long": "Tato cesta štítku je příliš dlouhá (nejvýše {max} znaků)", - "has_children_locked": "Pod tímto štítkem jsou vnořeny další štítky, proto jsou jeho název a nadřazený štítek uzamčeny. Nejprve je přesuňte nebo odeberte.", - "has_children_delete": "Nejprve odeberte štítky vnořené pod tímto", - "visibility_field": "Viditelnost v postranním panelu", - "visibility": { - "show": "Zobrazit", - "unread": "Zobrazit při nepřečtených", - "hide": "Skrýt" - }, - "unrecognized": { - "title": "Obnovit štítky ze serveru", - "description": "Názvy a barvy štítků jsou uloženy v tomto prohlížeči, ale samotné štítky jsou na vašich zprávách jako klíčová slova JMAP. Prohledejte poštu na klíčová slova, která zde žádný štítek nepokrývá, a přidejte je zpět.", - "scan": "Vyhledat štítky", - "rescan": "Hledat znovu", - "scanning": "Prohledávání… {scanned} z {total} zpráv", - "none": "Všechny štítky na vašich zprávách jsou zde už definované.", - "messages": "{count, plural, one {# zpráva} few {# zprávy} other {# zpráv}}", - "add_all": "Přidat všech {count}", - "partial": "Prohledáno bylo jen {scanned} nejnovějších zpráv z {total}. Štítky použité pouze ve starší poště mohou chybět.", - "error": "Poštu se nepodařilo prohledat na štítky." - } - }, - "notifications": { - "test_sound": "Otestovat zvuk oznámení", - "sounds": { - "default": "Výchozí (Pípnutí)", - "cheerful": "Veselý", - "involved": "Dynamický", - "swift": "Rychlý", - "relax": "Relaxační" - }, - "sound_selection": { - "title": "Zvuk oznámení", - "description": "Vyberte zvuk, který se přehraje při oznámení", - "choose": "Zvuk", - "choose_desc": "Vyberte tón oznámení a kliknutím na ikonu reproduktoru si jej poslechněte" - }, - "email": { - "title": "E-mailová oznámení", - "description": "Nakonfigurujte oznámení pro příchozí e-maily", - "enabled": "E-mailová oznámení", - "enabled_desc": "Zobrazovat oznámení při příchodu nových e-mailů", - "sound": "Zvuk oznámení", - "sound_desc": "Přehrát zvukové upozornění při příchodu nových e-mailů", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Oznámení kalendáře", - "description": "Nakonfigurujte oznámení pro události kalendáře", - "enabled": "Oznámení o událostech", - "enabled_desc": "Zobrazovat upozornění na nadcházející události v kalendáři", - "sound": "Zvuk oznámení", - "sound_desc": "Přehrát zvukové upozornění pro připomenutí kalendáře", - "invitation_parsing": "Rozpoznávat e-mailové pozvánky", - "invitation_parsing_desc": "Rozpoznávat pozvánky kalendáře v přílohách e-mailů a zobrazovat akce kalendáře" - }, - "push": { - "confirm_disable_message": "Toto zařízení přestane přijímat upozornění, když je web zavřený.", - "confirm_disable_title": "Zakázat oznámení na pozadí?", - "description": "Přijímat systémová oznámení o nové poště, když je tento web zavřený. Doručováno přes push relay Bulwark; relay nikdy nevidí obsah pošty.", - "disable": "Zakázat", - "enable": "Povolit", - "dismiss_aria": "Zavřít výzvu k oznámením", - "ios_hint": "Na iOS nejprve nainstalujte web na domovskou obrazovku – Safari doručuje Web Push pouze nainstalovaným PWA.", - "reenable": "Znovu zaregistrovat", - "relay_desc": "Výchozí je hostovaný relay Bulwark. Změňte pouze pokud používáte vlastní hosting.", - "relay_label": "Push relay", - "relay_locked": "Nastaveno administrátorem", - "relay_locked_desc": "URL push relay byla nastavena administrátorem a nelze ji změnit.", - "status_active": "Aktivní na tomto zařízení", - "status_busy": "Pracuji…", - "status_inactive": "Není povoleno na tomto zařízení", - "status_unsupported": "Tento prohlížeč nepodporuje Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Oznámení na pozadí", - "devices_title": "Registrovaná zařízení", - "devices_desc": "Všechna zařízení registrovaná pro příjem oznamení na pozadí u tohoto účtu. Odvolání okamžitě zastaví jejich příjem.", - "devices_loading": "Načítání registrovaných zařízení…", - "devices_empty": "Pro oznámení na pozadí není registrováno žádné zařízení.", - "devices_error": "Registrovaná zařízení se nepodařilo načíst.", - "devices_refresh": "Obnovit", - "device_this": "Toto zařízení", - "device_other": "Jiné zařízení", - "device_status_active": "Doručuje oznámení", - "device_status_inactive": "Nikdy nedoručilo oznámení", - "device_status_unknown": "Tomuto relay není známo", - "device_expires": "Vyprší {date}", - "revoke": "Odvolat", - "confirm_revoke_title": "Odvolat toto zařízení?", - "confirm_revoke_message": "Okamžitě přestane přijímat oznámení na pozadí. Opětovné zapnutí push na tom zařízení jej zaregistruje znovu.", - "confirm_revoke_message_this": "Toto zařízení okamžitě přestane přijímat oznámení na pozadí." - } - }, - "language_region": { - "title": "Jazyk, region a čas", - "description": "Jazyk, formát data, formát času a další regionální předvolby", - "language": { - "label": "Jazyk", - "description": "Vyberte preferovaný jazyk", - "english": "Angličtina", - "french": "Francouzština" - }, - "date_format": { - "label": "Formát data", - "description": "Jak se zobrazují data v seznamu e-mailů", - "smart": "Chytrý (regionální)", - "relative": "Relativní (před 1 h, před 2 d)", - "full": "Vždy úplné datum", - "preview_today": "Dnes:", - "preview_this_week": "Tento týden:", - "preview_older": "Starší:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Formát času", - "description": "Vyberte 12hodinový nebo 24hodinový formát času", - "12h": "12hodinový", - "24h": "24hodinový" - }, - "first_day": { - "label": "První den v týdnu", - "description": "Začátek týdne v neděli nebo v pondělí", - "sunday": "Neděle", - "monday": "Pondělí" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Vymazat hledání při přepnutí složky", - "description": "Kliknutí na složku (Doručená pošta, Odeslaná…) vymaže aktivní hledání a zobrazí složku. Když je vypnuto, hledání zůstává a spustí se znovu ve zvolené složce." - }, - "request_read_receipt": { - "label": "Standardně vyžadovat potvrzení o přečtení", - "description": "Při psaní nové zprávy předem zapnout žádost o potvrzení o přečtení." - }, - "read_receipt_response": { - "label": "Reagovat na žádosti o potvrzení o přečtení", - "description": "Co dělat, když příchozí zpráva žádá o potvrzení o přečtení.", - "ask": "Vždy se zeptat", - "always": "Vždy odeslat", - "never": "Nikdy neodesílat" - }, - "title": "Chování e-mailu", - "description": "Nakonfigurujte způsob zpracování e-mailů", - "mark_read": { - "label": "Označit jako přečtené", - "description": "Kdy označit e-mail jako přečtený po jeho otevření", - "instant": "Okamžitě", - "delay_3s": "Po 3 sekundách", - "delay_5s": "Po 5 sekundách", - "never": "Nikdy" - }, - "delete_action": { - "label": "Akce odstranění", - "description": "Co se má stát po odstranění e-mailu", - "trash": "Přesunout do Koše", - "trash_and_read": "Přesunout do Koše a označit jako přečtené", - "permanent": "Trvale odstranit", - "warning": "E-maily budou trvale odstraněny a nebude možné je obnovit. Tato akce je nevratná." - }, - "message_spacing": { - "label": "Odsazení zprávy", - "description": "Odsazení kolem těla zprávy při čtení", - "auto": "Automaticky (doporučeno)", - "always": "Vždy přidat odsazení", - "edge": "Od okraje k okraji" - }, - "plain_text_font": { - "label": "Písmo prostého textu", - "description": "Písmo použité pro e-maily v prostém textu", - "mono": "Neproporcionální", - "sans": "Normální (stejné jako aplikace)" - }, - "archive_mode": { - "label": "Archivovat do", - "description": "Jak organizovat e-maily při archivaci", - "single": "Jedna složka", - "year": "Složka pro každý rok", - "month": "Složka pro každý měsíc", - "reorganize": "Reorganizovat existující archiv", - "reorganize_success": "{count, plural, =0 {Žádné zprávy k reorganizaci} one {Reorganizována 1 zpráva} few {Reorganizovány # zprávy} other {Reorganizováno # zpráv}}", - "reorganize_error": "Nepodařilo se reorganizovat archiv" - }, - "permanently_delete_junk": { - "label": "Trvale odstraňovat spam", - "description": "Trvale odstraňovat zprávy ze složky Spam/Nevyžádaná pošta místo přesunutí do Koše" - }, - "mail_layout": { - "label": "Rozložení pošty", - "description": "Vyberte si mezi klasickým rozděleným zobrazením a soustředěným seznamem podobným Gmailu.", - "split": "Rozdělené zobrazení", - "split_description": "Seznam zpráv a panel pro čtení zůstávají viditelné vedle sebe.", - "focus": "Soustředěný seznam", - "focus_description": "Zobrazit jeden řádek na zprávu a otevřít poštu na plnou šířku s viditelným panelem složek.", - "horizontal": "Čtecí panel dole", - "horizontal_description": "Zobrazit seznam zpráv nahoře a otevřít vybranou zprávu ve čtecím panelu pod ním." - }, - "show_preview": { - "label": "Zobrazit náhledový text", - "description": "Zobrazovat náhledy e-mailů v seznamu", - "focus_description": "Zobrazovat vložený náhledový text v jednořádkovém soustředěném seznamu" - }, - "disable_threading": { - "label": "Zakázat seskupování konverzací", - "description": "Zobrazovat e-maily jako jednotlivé zprávy namísto seskupování do vláken" - }, - "plain_text_mode": { - "label": "Pouze prostý text", - "description": "Zakázat editor formátovaného textu a odesílat všechny e-maily pouze jako prostý text, včetně odpovědí a přeposílání" - }, - "auto_select_reply_identity": { - "label": "Automaticky vybírat adresu pro odpověď", - "description": "Při odpovídání automaticky přepnout adresu odesílatele na identitu, která původně obdržela zprávu" - }, - "reply_identity_match": { - "label": "Porovnávání přijímací adresy", - "description": "Které přijímací adresy se počítají jako vaše. „Pouze přesná adresa“ vybere jen jednu z vašich nastavených identit; „stejná doména“ navíc považuje jakoukoli jinou adresu na doménách vašich identit za catch-all alias a přepíše na ni hlavičku Od. Pokud jsou tyto adresy distribuční seznamy, zvolte přesnou adresu.", - "exact": "Pouze přesná adresa", - "domain": "Jakákoli adresa na mých doménách" - }, - "signature_position": { - "label": "Pozice podpisu", - "description": "Kam vložit podpis v odpovědích a přeposláních. Nad citovaným textem působí přirozeně jako zakončení odpovědi; pod ním zachovává původní zprávu vcelku.", - "above_quote": "Před citovaným textem", - "below_quote": "Za citovaným textem" - }, - "signature_separator": { - "label": "Oddělovač podpisu", - "description": "Před podpis přidat standardní oddělovací řádek \"-- \" (RFC 3676). Vypněte, pokud chcete plynule přejít z textu zprávy do podpisu." - }, - "sub_address_delimiter": { - "label": "Oddělovač sub-adresy", - "description": "Znak oddělující uživatelské jméno od sub-adresy. Zvolte oddělovač používaný vaším poštovním serverem (např. uzivatel{delimiter}stitek@domena.cz).", - "option": "{delimiter} (uzivatel{delimiter}stitek@domena.cz)", - "custom": "Vlastní…", - "custom_input_label": "Vlastní znak oddělovače" - }, - "attachment_click_action": { - "label": "Akce po kliknutí na přílohu", - "description": "Vyberte, zda má kliknutí na přílohu zobrazit náhled, nebo ji ihned stáhnout", - "preview": "Náhled, pokud je to možné", - "download": "Okamžitě stáhnout" - }, - "attachment_position": { - "label": "Pozice příloh", - "description": "Kde zobrazovat přílohy v hlavičce e-mailu", - "beside-sender": "Vedle odesílatele", - "below-header": "Pod hlavičkou" - }, - "emails_per_page": { - "10": "10 zpráv", - "25": "25 zpráv", - "50": "50 zpráv", - "100": "100 zpráv", - "label": "Zpráv na stránku", - "description": "Počet e-mailů načtených najednou" - }, - "always_light_mode": { - "label": "Vždy zobrazovat zprávy ve světlém režimu", - "description": "Vykreslovat obsah e-mailu ve světlém režimu, i když je aplikace v tmavém režimu, aby se předešlo problémům s konverzí" - }, - "external_content": { - "label": "Externí obsah", - "description": "Jak zacházet s obrázky a externím obsahem", - "ask": "Vždy se ptát", - "block": "Vždy blokovat", - "allow": "Vždy povolit" - }, - "trusted_senders": { - "label": "Důvěryhodní odesílatelé", - "description": "Spravujte odesílatele, jejichž obrázky se načítají automaticky", - "count_zero": "Žádní", - "count_one": "1 odesílatel", - "count_other": "{count} odesílatelů", - "modal_title": "Důvěryhodní odesílatelé", - "empty_title": "Žádní důvěryhodní odesílatelé", - "empty_description": "Při prohlížení e-mailů se zablokovanými obrázky klikněte na „Vždy důvěřovat tomuto odesílateli“, čímž jej přidáte sem.", - "add_manually": "Přidat odesílatele ručně", - "add_button": "Přidat", - "add_placeholder": "Zadejte e-mailovou adresu", - "search_placeholder": "Hledat odesílatele...", - "no_results": "Hledání neodpovídá žádný odesílatel", - "remove": "Odstranit", - "close": "Zavřít", - "invalid_email": "Zadejte platnou e-mailovou adresu", - "already_added": "Tento odesílatel je již důvěryhodný", - "save_error": "Uložení selhalo - zkontrolujte ladicí protokol Kontaktů", - "use_address_book_label": "Synchronizovat s adresářem", - "use_address_book_description": "Ukládat důvěryhodné odesílatele ve vyhrazeném adresáři „Důvěryhodní odesílatelé“, aby se synchronizovali napříč zařízeními" - }, - "hover_actions": { - "label": "Rychlé akce po přejetí myší", - "description": "Vyberte rychlé akce viditelné po přejetí přes e-mail v seznamu", - "delete": "Odstranit", - "star": "Označit hvězdičkou / odebrat hvězdičku", - "mark_read": "Označit jako přečtené / nepřečtené", - "archive": "Archivovat", - "tag": "Štítek", - "spam": "Označit jako spam", - "not_spam": "Není spam", - "none_selected": "Nebyly vybrány žádné akce", - "mode_label": "Režim zobrazení", - "mode_inline": "Vložené", - "mode_floating": "Plovoucí", - "corner_label": "Plovoucí pozice", - "corner_top-left": "Levý horní roh", - "corner_top-right": "Pravý horní roh", - "corner_bottom-left": "Levý dolní roh", - "corner_bottom-right": "Pravý dolní roh" - }, - "default_mail_program": { - "label": "Výchozí poštovní program", - "description": "Zaregistrovat aplikaci {appName} jako výchozí poštovní program pro odkazy mailto:", - "button": "Nastavit jako výchozí", - "success": "Prohlížeč požádal o nastavení jako výchozího programu", - "error": "Váš prohlížeč tuto funkci nepodporuje" - }, - "attachment_reminder": { - "label": "Připomenutí přílohy", - "description": "Upozornit před odesláním, když zpráva zmiňuje přílohy, ale žádné nejsou připojeny", - "keywords_label": "Klíčová slova spouštějící připomenutí", - "keywords_description": "Slova nebo fráze, které po nalezení ve zprávě spustí připomenutí", - "add_placeholder": "Přidat klíčové slovo...", - "add": "Přidat", - "remove": "Odstranit" - }, - "empty_subject_warning": { - "label": "Upozornění na prázdný předmět", - "description": "Před odesláním zprávy bez předmětu požádat o potvrzení." - }, - "hide_inline_image_attachments": { - "label": "Skrýt vložené obrázky ze seznamu příloh", - "description": "Obrázky vložené přímo do textu zprávy se nezobrazí jako samostatné přílohy" - }, - "attachment_image_previews": { - "label": "Zobrazit náhledy obrázků v přílohách", - "description": "Zobrazovat obrázkové přílohy jako miniatury místo obecných ikon souborů" - }, - "send_delay": { - "label": "Vrátit odeslání / prodleva odeslání", - "description": "Pozdržet běžné odeslání krátkým oknem na serveru.", - "off": "Vypnuto", - "seconds": "{seconds} sekund", - "unsupported": "Aktuální účet neoznamuje podporu odloženého odeslání. Nastavení zůstane uloženo pro jiné účty." - }, - "return_to_list_after_action": { - "label": "Návrat do seznamu po smazání nebo označení jako nepřečtené", - "description": "Po smazání nebo označení otevřené zprávy jako nepřečtené se vrátit do seznamu zpráv místo otevření následující zprávy." - }, - "swipe_right_action": { - "label": "Akce při přejetí doprava (mobil)", - "description": "Na dotykových zařízeních akce, která se provede při přejetí zprávy v seznamu doprava." - }, - "swipe_left_action": { - "label": "Akce při přejetí doleva (mobil)", - "description": "Na dotykových zařízeních akce, která se provede při přejetí zprávy v seznamu doleva." - }, - "swipe_actions": { - "none": "Žádná", - "archive": "Archivovat", - "delete": "Odstranit", - "mark_read": "Přepnout přečteno", - "star": "Přepnout hvězdičku", - "spam": "Označit jako spam" - }, - "rtl_editing": { - "label": "Podpora psaní zprava doleva", - "description": "Přidá do panelu nástrojů editoru tlačítko směru textu, kterým lze odstavce nastavit zleva doprava nebo zprava doleva" - } - }, - "composer": { - "title": "Psaní zpráv", - "description": "Nakonfigurujte nastavení pro psaní e-mailů", - "autosave": { - "label": "Interval automatického ukládání", - "description": "Jak často se mají koncepty automaticky ukládat", - "30s": "Každých 30 sekund", - "1m": "Každou minutu", - "2m": "Každé 2 minuty", - "5m": "Každých 5 minut" - }, - "send_confirmation": { - "label": "Potvrzení odeslání", - "description": "Před odesláním zprávy požádat o potvrzení" - }, - "default_reply": { - "label": "Výchozí režim odpovědi", - "description": "Výchozí akce po kliknutí na Odpovědět", - "reply": "Odpovědět", - "reply_all": "Odpovědět všem" - } - }, - "privacy": { - "title": "Soukromí a bezpečnost", - "description": "Správa nastavení soukromí a bezpečnosti", - "external_images": { - "label": "Blokovat externí obrázky", - "description": "Zabránit sledování pomocí externích obrázků" - }, - "session_timeout": { - "label": "Časový limit relace", - "description": "Automaticky odhlásit po určité době nečinnosti", - "never": "Nikdy", - "30m": "30 minut", - "1h": "1 hodina", - "4h": "4 hodiny" - }, - "clear_cache": { - "label": "Vymazat mezipaměť", - "description": "Odstranit data z mezipaměti a dočasné soubory", - "button": "Vymazat mezipaměť", - "confirm": "Opravdu chcete vymazat mezipaměť?", - "success": "Mezipaměť byla vymazána" - } - }, - "account": { - "title": "Účet", - "description": "Zobrazit informace o vašem účtu", - "name_label": "Zobrazované jméno", - "username_label": "Uživatelské jméno", - "account_type_label": "Typ účtu", - "auth_method_label": "Ověřování", - "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", - "auth_method_basic": "Heslo", - "demo_account": "Demo účet", - "email": { - "label": "E-mailová adresa", - "value": "{email}" - }, - "server": { - "label": "JMAP server", - "value": "{server}" - }, - "storage": { - "label": "Využití úložiště", - "used": "Využito {used} z {total}", - "percentage": "Využito {percent} %" - }, - "last_sync": { - "label": "Poslední synchronizace", - "value": "{time}" - }, - "accounts": { - "title": "Přihlášené účty", - "description": "Přetažením změňte pořadí účtů v rozbalovacím seznamu", - "active": "Aktuálně aktivní účet", - "default_badge": "Výchozí účet", - "set_default": "Nastavit jako výchozí", - "switch_to": "Přepnout na tento účet", - "move_up": "Posunout nahoru", - "move_down": "Posunout dolů", - "drag_handle": "Přetažením změňte pořadí", - "add": "Přidat účet" - }, - "shared_accounts": { - "title": "Sdíleno se mnou", - "description": "Skupinové a sdílené účty, které můžete spravovat. Vyberte jeden pro úpravu jeho filtrů, odpovědi v nepřítomnosti, kalendářů a kontaktů.", - "shared_label": "Sdílený účet" - } - }, - "security": { - "title": "Zabezpečení účtu", - "description": "Správa hesla, dvoufázového ověření a nastavení zabezpečení", - "detecting": "Zjišťování možností serveru...", - "not_available": "Správa zabezpečení účtu není pro tento poštovní server k dispozici. Požadovaná oprávnění mohou být vypnuta. Podrobnosti naleznete v dokumentaci.", - "password": { - "title": "Změnit heslo", - "current": "Aktuální heslo", - "new": "Nové heslo", - "confirm": "Potvrdit nové heslo", - "submit": "Změnit heslo", - "success": "Heslo bylo změněno", - "error_title": "Změna hesla selhala", - "error_mismatch": "Nová hesla se neshodují", - "error_min_length": "Heslo musí obsahovat alespoň 8 znaků", - "error_generic": "Heslo se nepodařilo změnit" - }, - "display_name": { - "label": "Zobrazované jméno", - "description": "Vaše jméno zobrazené na serveru", - "placeholder": "Zadejte své zobrazované jméno", - "save": "Uložit", - "success": "Zobrazované jméno bylo aktualizováno", - "error": "Nepodařilo se aktualizovat zobrazované jméno" - }, - "totp": { - "section_title": "Dvoufázové ověření", - "label": "Ověřování TOTP", - "description": "Přidejte další vrstvu zabezpečení pomocí jednorázového hesla na bázi času", - "active": "Zapnuto", - "inactive": "Vypnuto", - "enabled": "Dvoufázové ověření zapnuto", - "disabled": "Dvoufázové ověření vypnuto", - "enable_error": "Nepodařilo se zapnout 2FA", - "disable_error": "Nepodařilo se vypnout 2FA", - "setup_instructions": "Zkopírujte tuto URL adresu do své ověřovací aplikace (Google Authenticator, Authy atd.):", - "verification_code": "Ověřovací kód", - "confirm": "Potvrdit", - "disable": "Vypnout", - "disable_confirm_prompt": "Pro vypnutí dvoufázového ověření zadejte heslo.", - "password_required": "Zadejte heslo", - "code_required": "Zadejte ověřovací kód", - "code_invalid": "Neplatný kód. Zkontrolujte ověřovací aplikaci a zkuste to znovu." - }, - "app_passwords": { - "title": "Hesla aplikací", - "description": "Vytvořte hesla pro aplikace, které nepodporují dvoufázové ověření", - "add": "Přidat", - "create": "Vytvořit", - "cancel": "Zrušit", - "done": "Hotovo", - "generate": "Vygenerovat", - "name_label": "Název aplikace", - "name_placeholder": "např. Thunderbird, iPhone Mail", - "expires_label": "Platí do (volitelné)", - "allowed_ips_label": "Povolené IP (volitelné)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Oddělte čárkou či mezerou. Pro povolení všech IP nechte prázdné.", - "password_label": "Heslo (pro vygenerování nechte prázdné)", - "password_placeholder": "Bude vygenerováno automaticky", - "copy_now_warning": "Heslo si hned zkopírujte – už se znovu nezobrazí.", - "added": "Heslo aplikace vytvořeno", - "removed": "Heslo aplikace smazáno", - "add_error": "Chyba při vytváření hesla aplikace", - "remove_error": "Chyba při mazání hesla aplikace", - "none": "Žádná hesla aplikací" - }, - "api_keys": { - "title": "API klíče", - "description": "Vytvořte si API klíče pro skripty a integrace, které komunikují přímo se serverem.", - "name_label": "Název klíče", - "name_placeholder": "např. Zálohovací skript, CI runner", - "copy_now_warning": "API klíč si hned zkopírujte – už se znovu nezobrazí.", - "added": "API klíč vytvořen", - "removed": "API klíč smazán", - "add_error": "Chyba při vytváření API klíče", - "remove_error": "Chyba při mazání API klíče", - "none": "Žádné API klíče" - }, - "encryption": { - "section_title": "Šifrování dat v klidu", - "label": "Šifrování e-mailů", - "description": "Šifrovat zprávy uložené na serveru pro větší soukromí", - "active": "Šifrování {type} zapnuto", - "inactive": "Vypnuto", - "enabled": "Šifrování dat v klidu zapnuto", - "disabled_success": "Šifrování dat v klidu vypnuto", - "error": "Nepodařilo se aktualizovat nastavení šifrování", - "algorithm_label": "Šifrovací algoritmus", - "encrypt_on_append": "Šifrovat nové e-maily při nahrání", - "allow_spam_training": "Povolit trénování spamového filtru před zašifrováním e-mailů", - "enable_encryption": "Zapnout šifrování", - "enable_tooltip": "Zapnout šifrování tímto klíčem", - "configure_title": "Zapnout šifrování tímto klíčem", - "enable_button": "Zapnout", - "disable_tooltip": "Vypnout šifrování", - "enabled_success": "Šifrování dat v klidu zapnuto" - }, - "email_client": { - "title": "Konfigurace poštovního klienta", - "description": "Použijte tyto přihlašovací údaje pro konfiguraci e-mailového klienta na počítači nebo mobilním zařízení (Thunderbird, Apple Mail, Outlook atd.)", - "jmap_username_label": "Uživatelské jméno JMAP", - "copy": "Kopírovat", - "copied": "Zkopírováno", - "password_instructions": "Použijte výše uvedené uživatelské jméno JMAP společně s heslem aplikace pro přihlášení v poštovním klientovi. Pokud jste tak ještě neučinili, vytvořte si heslo aplikace v sekci výše." - }, - "link_device": { - "title": "Propojit mobilní aplikaci", - "description": "Přihlaste se do mobilní aplikace Bulwark Mail bez psaní. Vygenerujte zde QR kód a naskenujte jej na přihlašovací obrazovce aplikace.", - "generate": "Zobrazit QR kód", - "regenerate": "Zobrazit nový kód", - "instructions": "Otevřete aplikaci Bulwark Mail, na přihlašovací obrazovce klepněte na \"Naskenovat QR kód\" a namiřte fotoaparát sem.", - "expires_in": "Tento kód vyprší za {seconds} sekund. Lze jej použít pouze jednou.", - "expired": "Platnost tohoto kódu vypršela.", - "generating": "Generování…", - "error": "Párovací kód se nepodařilo vytvořit. Zkuste to prosím znovu." - }, - "public_keys": { - "name_label": "Název veřejného klíče", - "name_placeholder": "např. Můj klíč", - "key_label": "Veřejný klíč (S/MIME nebo PGP ve formátu ASCII)", - "key_placeholder": "Sem vložte svůj veřejný klíč", - "none": "Nejsou nastaveny žádné veřejné klíče" - } - }, - "identities": { - "title": "Identity odesílatele", - "description": "Správa e-mailových adres, ze kterých můžete odesílat zprávy", - "identities_count": { - "label": "Vaše identity", - "description": "E-mailové adresy nakonfigurované pro odesílání", - "count_zero": "Žádné identity", - "count_one": "1 identita", - "count_other": "{count} identit" - }, - "manage": "Spravovat identity", - "sub_addressing": { - "label": "Subadresování", - "description": "Používejte štítky jako uzivatel+stitek@domena.cz pro uspořádání příchozí pošty", - "learn_more": "Zjistit více" - } - }, - "vacation": { - "title": "Automatická odpověď", - "description": "Během vaší nepřítomnosti bude automaticky odpovídáno na příchozí e-maily", - "loading": "Načítání nastavení automatické odpovědi...", - "not_supported": "Váš poštovní server nepodporuje automatické odpovědi v nepřítomnosti.", - "fetch_error": "Nepodařilo se načíst nastavení automatické odpovědi. Zkuste to znovu.", - "status": { - "label": "Automatická odpověď", - "description": "Odesílat automatickou odpověď lidem, kteří vám napíší", - "active": "Aktivní", - "inactive": "Neaktivní" - }, - "date_range": { - "title": "Časové období", - "description": "Volitelně omezte automatickou odpověď na určité období", - "start": "Datum zahájení", - "start_description": "Nechte prázdné pro spuštění ihned", - "end": "Datum ukončení", - "end_description": "Nechte prázdné pro běh bez ukončení" - }, - "message": { - "title": "Zpráva automatické odpovědi", - "description": "Zpráva, která bude odeslána jako odpověď", - "subject_label": "Předmět", - "subject_description": "Předmět automatické odpovědi", - "subject_placeholder": "Mimo kancelář", - "body_label": "Tělo zprávy", - "body_description": "Obsah zprávy v prostém textu", - "body_placeholder": "Děkuji za vaši zprávu. Momentálně jsem mimo kancelář a odpovím po svém návratu.", - "html_label": "Formátovaná zpráva (HTML)", - "html_description": "Přidejte formátovanou verzi s odkazy a stylem. Příjemci, jejichž poštovní klient ji nedokáže zobrazit, uvidí prostý text výše.", - "html_placeholder": "Napište formátovanou odpověď o nepřítomnosti…" - }, - "preview": { - "title": "Náhled", - "show": "Zobrazit náhled", - "hide": "Skrýt náhled" - }, - "save": "Uložit změny", - "saving": "Ukládání...", - "warnings": { - "end_before_start": "Datum ukončení musí být po datu zahájení", - "start_in_past": "Datum zahájení je v minulosti", - "empty_body": "Tělo zprávy je prázdné - příjemci obdrží prázdnou odpověď", - "subject_too_long": "Předmět je pro server příliš dlouhý (nejvýše {max} bajtů).", - "body_too_long": "Zpráva je pro server příliš dlouhá (nejvýše {max} bajtů včetně formátování)." - } - }, - "folders": { - "role_memos": "Poznámky", - "title": "Složky", - "description": "Správa složek pošty a přiřazení standardních rolí", - "folder_list": "Vaše složky", - "folder_list_description": "Kliknutím na ikonu složky ji upravíte", - "standard_roles": "Standardní role složek", - "standard_roles_description": "Přiřaďte složky používané pro standardní role poštovní schránky, jako jsou Doručené, Odeslané, Koš atd.", - "role_inbox": "Doručené", - "role_drafts": "Koncepty", - "role_sent": "Odeslané", - "role_trash": "Koš", - "role_junk": "Spam / Nevyžádaná pošta", - "role_archive": "Archiv", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Žádná", - "create_folder": "Vytvořit složku", - "create_subfolder": "Vytvořit podsložku", - "reorder": "Přetažením změníte pořadí", - "reorder_error": "Nepodařilo se změnit pořadí složek", - "move_error": "Nepodařilo se přesunout složku", - "subfolder_of": "Ve složce {name}", - "subfolder_name": "Název podsložky", - "new_folder_name": "Název složky", - "rename": "Přejmenovat", - "change_icon": "Změnit ikonu", - "delete": "Odstranit", - "confirm_delete": "Opravdu chcete odstranit složku „{name}“? Zprávy v této složce budou přesunuty do Koše.", - "create": "Vytvořit", - "cancel": "Zrušit", - "no_folders": "Žádné vlastní složky", - "cannot_delete_role": "Složku se standardní rolí nelze odstranit. Nejprve tuto roli odstraňte.", - "folder_created": "Složka byla vytvořena", - "folder_renamed": "Složka byla přejmenována", - "folder_deleted": "Složka byla odstraněna", - "role_updated": "Role složky byla aktualizována", - "error_create": "Složku se nepodařilo vytvořit", - "error_rename": "Složku se nepodařilo přejmenovat", - "error_delete": "Složku se nepodařilo odstranit", - "error_delete_has_children": "Složku nelze odstranit: stále obsahuje podsložky. Nejprve je odstraňte nebo přesuňte.", - "error_delete_has_email": "Složku nelze odstranit: stále obsahuje zprávy. Nejprve je přesuňte nebo odstraňte.", - "error_role": "Roli složky se nepodařilo aktualizovat" - }, - "advanced": { - "title": "Pokročilé", - "description": "Pokročilé možnosti a nastavení pro vývojáře", - "debug_mode": { - "label": "Režim ladění", - "description": "Povolit podrobné protokolování pro řešení problémů" - }, - "debug_categories": { - "description": "Vyberte kategorie pro protokolování. Vypněte kategorie, které nepotřebujete, pro snížení množství záznamů v konzoli.", - "jmap": "Klient JMAP", - "jmap_description": "Operace se schránkami, načítání zpráv a požadavky protokolu JMAP", - "calendar": "Kalendář", - "calendar_description": "Události kalendáře, importy a plánování zpráv", - "tasks": "Úkoly", - "tasks_description": "Vytváření úkolů kalendáře, načítání a aktualizace", - "auth": "Ověřování", - "auth_description": "Přihlašování, TOTP, výměna tokenů a správa relací", - "filters": "Filtry", - "filters_description": "Pravidla filtrů Sieve a skripty pro automatickou odpověď", - "email": "Zobrazení e-mailu", - "email_description": "Vykreslování e-mailů, zpracování TNEF a označování jako přečtené", - "push": "Push oznámení", - "push_description": "Konfigurace a doručování push oznámení", - "contacts": "Kontakty a adresáře", - "contacts_description": "Synchronizace kontaktů, operace s adresářem a důvěryhodní odesílatelé" - }, - "settings_sync": { - "label": "Synchronizace nastavení", - "description": "Synchronizovat nastavení mezi prohlížeči a zařízeními" - }, - "sender_favicons": { - "label": "Favikony odesílatelů", - "description": "Zobrazovat ikony webových stránek jako profilové obrázky firemních odesílatelů" - }, - "show_avatars_in_junk": { - "label": "Zobrazovat avatary ve spamu", - "description": "Zobrazovat profilové fotky a ikony odesílatelů ve spamu. Ve výchozím stavu vypnuto, aby phishingové e-maily nepůsobily důvěryhodně." - }, - "keyboard_shortcuts": { - "label": "Klávesové zkratky", - "description": "Zobrazit dostupné klávesové zkratky", - "button": "Zobrazit zkratky" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Resetovat nastavení", - "description": "Obnovit všechna nastavení na výchozí hodnoty", - "button": "Obnovit výchozí" - }, - "export_settings": { - "label": "Exportovat nastavení", - "description": "Stáhnout svá nastavení jako soubor JSON", - "button": "Exportovat" - }, - "import_settings": { - "label": "Importovat nastavení", - "description": "Nahrát nastavení ze souboru JSON", - "button": "Importovat" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Aplikace postranního panelu", - "description": "Spravujte vlastní aplikace a odkazy v postranním panelu", - "keep_loaded": "Ponechat aplikace načtené", - "keep_loaded_description": "Udržovat vložené aplikace spuštěné na pozadí během přepínání, aby se předešlo jejich opětovnému načítání", - "managed_title": "Poskytnuto správcem", - "managed_description": "Tyto aplikace jsou nastaveny pro všechny a nelze je změnit.", - "manage_title": "Vlastní aplikace", - "manage_description": "Přidávat, upravovat nebo odebírat vlastní aplikace z postranního panelu" - }, - "contacts": { - "title": "Kontakty", - "description": "Import a export kontaktů", - "group_by_letter_label": "Seskupit podle prvního písmena", - "sort_by_last_name_label": "Řadit podle příjmení", - "sort_by_last_name_description": "Seřadit seznam kontaktů podle příjmení, aby členové rodiny byli pohromadě", - "group_by_letter_description": "Zobrazit záhlaví sekcí podle abecedy v seznamu kontaktů", - "import_label": "Importovat kontakty", - "import_description": "Importovat kontakty ze souboru vCard (.vcf)", - "export_label": "Exportovat kontakty", - "export_description": "Exportovat všechny kontakty jako soubor vCard (.vcf)", - "manage_title": "Adresáře", - "manage_description": "Přejmenovat své adresáře", - "no_address_books": "Nenalezeny žádné adresáře", - "categories_title": "Kategorie", - "categories_description": "Přejmenovat kategorie kontaktů", - "no_categories": "Nenalezeny žádné kategorie" - }, - "downloads": { - "title": "Stažené", - "description": "Přizpůsobte si, jak se pojmenovávají stažené e-maily a přílohy.", - "reset": "Obnovit výchozí", - "preview": "Náhled:", - "email_template": { - "label": "Název souboru e-mailu (.eml)", - "description": "Šablona použitá při exportu e-mailu nebo jeho přetažení do souborového systému. Přípona .eml se přidává automaticky." - }, - "attachment_template": { - "label": "Název souboru přílohy", - "description": "Šablona použitá při stahování nebo přetahování přílohy. Pokud vynecháte '{filename}' a '{ext}', původní přípona se zachová." - }, - "bundle_template": { - "label": "Název .zip souboru s více e-maily", - "description": "Šablona použitá při přetažení nebo stažení několika vybraných e-mailů jako jediného .zip archivu. Přípona .zip se přidává automaticky." - }, - "spaces": { - "label": "Mezery", - "description": "Nahradit mezery ve výsledném názvu souboru jiným znakem.", - "keep": "Zachovat mezery", - "underscore": "Nahradit za _", - "dash": "Nahradit za -" - }, - "lowercase": { - "label": "Malá písmena", - "description": "Vynutit celý název souboru malými písmeny." - }, - "strip_diacritics": { - "label": "Odstranit diakritiku", - "description": "Převést písmena s diakritikou na ASCII ekvivalenty (á → a, é → e). Užitečné pro nástroje, které špatně zpracovávají Unicode názvy." - }, - "collapse_separators": { - "label": "Sloučit opakované oddělovače", - "description": "Sloučit posloupnosti mezer, podtržítek nebo pomlček do jednoho znaku." - }, - "after_export": { - "label": "Po exportu", - "description": "Po exportu jako .eml lze e-mail volitelně přesunout.", - "keep": "Ponechat ve schránce", - "archive": "Přesunout do archivu", - "trash": "Přesunout do koše" - } - }, - "filters": { - "title": "Filtry e-mailů", - "description": "Vytvářejte pravidla pro automatické třídění, štítkování a správu příchozích e-mailů", - "add_rule": "Přidat pravidlo", - "no_rules": "Žádná pravidla filtrů", - "no_rules_description": "Vytvářejte pravidla pro automatickou organizaci příchozích e-mailů", - "vacation_active": "Automatická odpověď je aktivní", - "vacation_active_description": "Automatická odpověď je zapnuta pro příchozí zprávy", - "vacation_configure": "Konfigurovat", - "edit_rule": "Upravit pravidlo", - "new_rule": "Nové pravidlo", - "delete_rule": "Odstranit pravidlo", - "delete_confirm": "Opravdu chcete toto pravidlo odstranit?", - "enable": "Povolit", - "disable": "Zakázat", - "raw_editor": "Textový editor Sieve", - "raw_editor_warning": "Úprava samotného skriptu Sieve může rozbít vizuální úpravu pravidel. Zde provedené změny nahradí vizuálního průvodce.", - "validate": "Ověřit", - "validation_success": "Skript je platný", - "validation_error": "Skript obsahuje chyby", - "save": "Uložit pravidla", - "saving": "Ukládání...", - "saved": "Filtry byly uloženy", - "save_failed": "Uložení filtrů selhalo", - "loading": "Načítání filtrů...", - "not_supported": "Váš poštovní server nepodporuje e-mailové filtry.", - "rule_name": "Název pravidla", - "rule_name_placeholder": "např. Třídit newslettery", - "match_all": "Splňuje VŠECHNY podmínky", - "match_any": "Splňuje JAKOUKOLI podmínku", - "conditions": "Podmínky", - "add_condition": "Přidat podmínku", - "actions": "Akce", - "add_action": "Přidat akci", - "stop_processing": "Zastavit zpracování dalších pravidel", - "include_spam": "Přesouvat i zprávy označené jako spam", - "attachment_type_placeholder": "např. pdf, doc, jpg", - "value_placeholder_multi": "Hodnota (více oddělených čárkami)", - "condition_fields": { - "attachment": "Příloha", - "from": "Od", - "to": "Komu", - "cc": "Kopie", - "subject": "Předmět", - "header": "Vlastní hlavička", - "size": "Velikost", - "body": "Tělo zprávy" - }, - "comparators": { - "has_any": "je přítomna", - "has_type": "typu", - "contains": "obsahuje", - "not_contains": "neobsahuje", - "is": "je přesně", - "not_is": "není", - "starts_with": "začíná na", - "ends_with": "končí na", - "matches": "odpovídá vzoru", - "greater_than": "je větší než", - "less_than": "je menší než" - }, - "action_types": { - "move": "Přesunout do složky", - "copy": "Kopírovat do složky", - "forward": "Přeposlat na", - "mark_read": "Označit jako přečtené", - "star": "Označit zprávu hvězdičkou", - "add_label": "Přidat štítek", - "discard": "Zahodit (tiše odstranit)", - "reject": "Odmítnout se zprávou", - "keep": "Ponechat v Doručených", - "stop": "Zastavit zpracování" - }, - "move_to_folder": "Vyberte složku", - "copy_to_folder": "Vyberte složku", - "forward_to": "Přeposlat na e-mailovou adresu", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Ponechat kopii", - "forward_limit": "Limit přeposlání na zprávu na tomto serveru: {count}. Další přeposlání se přeskočí.", - "reject_message": "Zpráva o odmítnutí", - "reject_placeholder": "Váš e-mail byl odmítnut", - "label_name": "Název štítku", - "label_placeholder": "Vyberte štítek", - "header_name": "Název hlavičky", - "header_placeholder": "např. X-Mailing-List", - "size_bytes": "Velikost v bajtech", - "size_placeholder": "např. 1000000", - "system_managed": "Pravidlo spravováno systémem", - "opaque_warning": "Tento skript byl upraven mimo vizuálního průvodce. Je k dispozici pouze přímá úprava Sieve.", - "open_sieve_editor": "Otevřít textový editor Sieve", - "fetch_error": "Nepodařilo se načíst filtry", - "expanded_view": "Rozšířené zobrazení", - "expanded_view_description": "Zobrazovat pravidla filtrů s podrobnými bloky podmínek a akcí", - "if": "Pokud", - "then": "Pak", - "match_all_conditions": "všechny splněny", - "match_any_condition": "jakékoli splněny", - "and": "a", - "or": "nebo", - "cancel": "Zrušit", - "confirm_delete": "Odstranit", - "rule_list": "Pravidla filtrů", - "drag_to_reorder": "Přetáhněte pro změnu pořadí", - "match_type": "Typ shody", - "reset_to_visual": "Resetovat na vizuálního průvodce", - "reset_warning": "Tímto zahodíte aktuální skript a začnete znovu.", - "confirm_reset": "Resetovat", - "validation_empty_name": "Název pravidla je vyžadován", - "validation_empty_conditions": "Je vyžadována alespoň jedna podmínka s hodnotou", - "validation_empty_actions": "Je vyžadována alespoň jedna akce", - "templates_section": "Začít ze šablony", - "template_newsletters": "Přesunout newslettery do složky", - "template_receipts": "Automaticky archivovat účtenky", - "template_important": "Označit důležité e-maily", - "template_notifications": "Filtrovat oznámení", - "sieve_editor": { - "title": "Editor skriptů Sieve", - "warning": "Úprava samotného skriptu Sieve může rozbít vizuální úpravu pravidel. Zde provedené změny nahradí vizuálního průvodce.", - "script_content": "Skript Sieve", - "valid": "Skript je platný", - "invalid": "Skript obsahuje chyby", - "save_warning": "Uložení přepíše všechna vizuální pravidla. Tuto akci nelze vrátit zpět. Potvrďte opětovným kliknutím na Uložit.", - "validating": "Ověřování...", - "validate": "Ověřit", - "cancel": "Zrušit", - "save": "Uložit", - "confirm_save": "Potvrdit uložení", - "validation_failed": "Požadavek na ověření selhal" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {1 podmínka} few {# podmínky} other {# podmínek}}", - "actions_count": "{count, plural, one {1 akce} few {# akce} other {# akcí}}" - }, - "origin_external": "Externí", - "managed_by_tooltip": "Spravováno aplikací {source}. Úpravy provádějte v příslušné aplikaci nebo použijte editor zdrojového kódu Sieve." - }, - "templates": { - "title": "E-mailové šablony", - "description": "Vytvářejte opakovaně použitelné e-mailové šablony s proměnnými zástupnými symboly", - "add": "Nová šablona", - "edit": "Upravit šablonu", - "name": "Název šablony", - "name_placeholder": "např. Kontrolní zpráva", - "category": "Kategorie", - "category_placeholder": "např. Práce, Osobní", - "subject": "Předmět", - "subject_placeholder": "Předmět e-mailu", - "body": "Tělo zprávy", - "body_placeholder": "Text e-mailu...", - "recipients_placeholder": "email@example.com", - "identity": "Odeslat jako", - "default_identity": "Výchozí identita", - "favorite": "Oblíbené", - "cancel": "Zrušit", - "create": "Vytvořit", - "update": "Aktualizovat", - "confirm_delete": "Odstranit", - "no_templates": "Žádné šablony", - "manage": "Spravovat šablony", - "count": "{count, plural, one {1 šablona} few {# šablony} other {# šablon}}", - "export_import": "Export a import", - "export_import_description": "Vytvořte zálohu šablon nebo je přeneste do jiného zařízení", - "export": "Exportovat", - "import": "Importovat", - "validation": { - "empty": "Název šablony je vyžadován", - "too_long": "Název šablony může mít maximálně 200 znaků" - } - }, - "files": { - "display": { - "title": "Zobrazení", - "description": "Nakonfigurujte způsob zobrazení souborů a složek" - }, - "folder_layout": { - "label": "Navigace složek", - "description": "Vyberte způsob zobrazení složek: společně se soubory nebo ve stromové struktuře postranního panelu", - "inline": "Vložená", - "sidebar": "Postranní panel" - }, - "default_view": { - "label": "Výchozí zobrazení", - "description": "Vyberte rozložení do mřížky nebo seznamu", - "list": "Seznam", - "grid": "Mřížka" - }, - "default_sort": { - "label": "Výchozí řazení", - "description": "Vyberte výchozí řazení souborů", - "name": "Název", - "size": "Velikost", - "modified": "Změněno" - }, - "sort_direction": { - "label": "Směr řazení", - "description": "Vyberte vzestupné nebo sestupné pořadí", - "ascending": "Vzestupně", - "descending": "Sestupně" - }, - "icons": { - "title": "Ikony", - "description": "Nakonfigurujte vzhled ikon souborů" - }, - "show_icons": { - "label": "Zobrazovat ikony souborů", - "description": "Zobrazovat ikony vedle souborů a složek" - }, - "colored_icons": { - "label": "Barevné ikony", - "description": "Používat barevné ikony místo monochromatických" - }, - "show_thumbnails": { - "label": "Zobrazovat miniatury", - "description": "Zobrazovat náhledy obrázků místo ikon pro obrazové soubory" - }, - "behavior": { - "title": "Chování", - "description": "Nakonfigurujte chování prohlížeče souborů" - }, - "show_hidden": { - "label": "Zobrazovat skryté soubory", - "description": "Zobrazovat soubory a složky začínající tečkou" - }, - "preview": { - "label": "Náhled" - } - }, - "search_placeholder": "Hledat nastavení", - "search_clear": "Vymazat hledání", - "search_no_results": "Žádná odpovídající nastavení", - "scoped": { - "back": "Zpět na můj účet", - "managing": "Správa: {name}" - } - }, - "errors": { - "page_error_title": "Něco se pokazilo", - "page_error_description": "Došlo k neočekávané chybě. Zkuste to znovu nebo se vraťte na domovskou stránku.", - "sidebar_error": "Nepodařilo se načíst poštovní schránky", - "email_list_error": "Nepodařilo se načíst zprávy", - "viewer_error_title": "Zprávu nelze zobrazit", - "viewer_error_description": "Během vykreslování této zprávy došlo k problému. Může obsahovat nepodporovaný obsah.", - "composer_error": "Editor zpráv se nepodařilo načíst", - "settings_error_title": "Nastavení jsou nedostupná", - "settings_error_description": "Nepodařilo se načíst nastavení. Vaše předvolby možná nebudou uloženy.", - "try_again": "Zkusit znovu", - "reload": "Obnovit", - "reload_emails": "Obnovit zprávy", - "reload_settings": "Obnovit nastavení", - "retry": "Opakovat", - "go_home": "Přejít do doručené pošty" - }, - "context_menu": { - "reply": "Odpovědět", - "reply_all": "Odpovědět všem", - "forward": "Přeposlat", - "mark_read": "Označit jako přečtené", - "mark_unread": "Označit jako nepřečtené", - "star": "Označit hvězdičkou", - "unstar": "Odebrat hvězdičku", - "pin": "Připnout", - "unpin": "Odepnout", - "move_to": "Přesunout do...", - "archive": "Archivovat", - "delete": "Odstranit", - "mark_as_spam": "Nahlásit spam", - "not_spam": "Není spam", - "tag": "Štítek", - "items_selected": "{count} vybraných zpráv", - "edit_draft": "Upravit koncept", - "cancel_scheduled_send": "Zrušit odeslání", - "reschedule_send": "Přeplánovat", - "cancel_and_edit": "Zrušit a upravit", - "cancel_and_compose_again": "Zrušit a napsat znovu" - }, - "mailbox_context_menu": { - "mark_folder_read": "Označit složku jako přečtenou", - "mark_folder_tree_read": "Označit složku a podsložky jako přečtené", - "mark_all_folders_read": "Označit všechny složky jako přečtené", - "new_subfolder": "Nová podsložka...", - "new_folder": "Nová složka...", - "rename": "Přejmenovat...", - "share": "Sdílet...", - "import_email": "Importovat .eml nebo .zip...", - "empty_folder": "Vyprázdnit složku", - "empty_folder_generic": "Vyprázdnit složku", - "delete_folder": "Smazat složku", - "refresh": "Obnovit", - "mark_all_confirm_title": "Označit všechny složky jako přečtené", - "mark_all_confirm_message": "Označit všechny nepřečtené zprávy ve vašem osobním účtu jako přečtené?", - "delete_confirm_title": "Smazat složku", - "delete_confirm_message": "Trvale smazat složku \"{name}\"? Tuto akci nelze vrátit.", - "prompt_new_subfolder": "Zadejte název nové podsložky.", - "prompt_new_folder": "Zadejte název nové složky.", - "prompt_rename": "Zadejte nový název této složky.", - "placeholder_folder_name": "Název složky", - "create": "Vytvořit", - "rename_confirm": "Přejmenovat", - "toast_marked_read": "Složka označena jako přečtená", - "toast_marked_read_count": "{count, plural, one {1 zpráva označena jako přečtená} few {# zprávy označeny jako přečtené} other {# zpráv označeno jako přečtených}}", - "toast_already_read": "Žádné nepřečtené zprávy", - "toast_marked_all_read": "Všechny složky označeny jako přečtené", - "toast_emptied": "Složka vyprázdněna", - "toast_folder_created": "Složka vytvořena", - "toast_folder_renamed": "Složka přejmenována", - "toast_folder_deleted": "Složka smazána", - "toast_error_mark_read": "Nepodařilo se označit jako přečtené", - "toast_error_empty": "Nepodařilo se vyprázdnit složku", - "toast_error_create": "Nepodařilo se vytvořit složku", - "toast_error_rename": "Nepodařilo se přejmenovat složku", - "toast_error_delete": "Nepodařilo se smazat složku", - "toast_error_delete_has_children": "Složka obsahuje podsložky. Nejprve je odstraňte.", - "toast_error_delete_has_email": "Složka není prázdná. Nejprve ji vyprázdněte.", - "delete_with_contents_title": "Složka není prázdná", - "delete_with_contents_message": "Složka „{name}“ stále obsahuje {count, plural, one {1 zprávu} few {# zprávy} other {# zpráv}}. Smazat složku i všechny její zprávy? Tuto akci nelze vrátit.", - "delete_with_contents_confirm": "Smazat složku i zprávy" - }, - "shortcuts": { - "title": "Klávesové zkratky", - "tip": "Kdykoli stiskněte ? pro zobrazení této nápovědy", - "sections": { - "navigation": "Navigace", - "actions": "Akce zprávy", - "global": "Globální", - "threads": "Vlákna", - "composer": "Editor zpráv" - }, - "navigation": { - "next_email": "Další zpráva", - "previous_email": "Předchozí zpráva", - "open_email": "Otevřít zprávu", - "close_email": "Zavřít / Zrušit výběr" - }, - "actions": { - "reply": "Odpovědět", - "reply_all": "Odpovědět všem", - "forward": "Přeposlat", - "star": "Přepnout hvězdičku", - "archive": "Archivovat", - "delete": "Odstranit", - "mark_unread": "Označit jako nepřečtené", - "mark_read": "Označit jako přečtené", - "toggle_spam": "Nahlásit spam / Není spam" - }, - "global": { - "compose": "Napsat novou zprávu", - "search": "Přejít do hledání", - "help": "Zobrazit zkratky", - "refresh": "Obnovit zprávy", - "select_all": "Vybrat vše", - "global_search": "Hledat ve všem" - }, - "threads": { - "expand_collapse": "Rozbalit/sbalit vlákno" - }, - "composer": { - "send": "Odeslat e-mail", - "schedule_send": "Naplánovat odeslání", - "template_picker": "Otevřít výběr šablon" - } - }, - "threads": { - "messages_one": "{count} zpráva", - "messages_other": "{count} zpráv", - "messages_tooltip": "{count, plural, one {1 zpráva v této konverzaci} few {# zprávy v této konverzaci} other {# zpráv v této konverzaci}}", - "expand": "Rozbalit konverzaci", - "collapse": "Sbalit konverzaci", - "loading": "Načítání konverzace...", - "mark_read": "Označit konverzaci jako přečtenou", - "mark_unread": "Označit konverzaci jako nepřečtenou", - "archive": "Archivovat konverzaci", - "delete": "Odstranit konverzaci", - "star": "Označit konverzaci hvězdičkou", - "unstar": "Odebrat hvězdičku z konverzace", - "toggle_thread": "Přepnout zobrazení vlákna" - }, - "identities": { - "modal_title": "Správa identit odesílatele", - "create_new": "Vytvořit novou identitu", - "edit_identity": "Upravit identitu", - "delete_confirm": "Odstranit tuto identitu? Tuto akci nelze vrátit zpět.", - "cannot_delete": "Tuto identitu nelze odstranit", - "primary_identity": "Hlavní", - "set_as_primary": "Nastavit jako hlavní", - "no_identities": "Identity nenalezeny", - "display": { - "reply_to": "Odpovědět na:", - "bcc": "Skrytá kopie:", - "signature": "Podpis:", - "preview": "Náhled:" - }, - "validation_errors": { - "invalid_emails": "Neplatné e-mailové adresy: {emails}", - "unknown_error": "Neznámá chyba" - }, - "form": { - "name_label": "Zobrazované jméno", - "name_placeholder": "např. Pracovní e-mail, Soukromý", - "name_required": "Jméno je vyžadováno", - "email_label": "E-mailová adresa", - "email_placeholder": "vas.email@example.com", - "email_required": "E-mailová adresa je vyžadována", - "email_invalid": "Zadejte platnou e-mailovou adresu", - "email_immutable": "E-mailovou adresu nelze po vytvoření změnit", - "reply_to_label": "Odpovědět na (volitelně)", - "reply_to_placeholder": "jiny@example.com", - "bcc_label": "Automatická skrytá kopie (volitelně)", - "bcc_placeholder": "archiv@example.com", - "text_signature_label": "Textový podpis", - "html_signature_label": "HTML podpis", - "signature_byte_counter": "{bytes} / {max} bajtů", - "signature_byte_limit_reached": "Dosažen limit serveru", - "save": "Uložit identitu", - "cancel": "Zrušit", - "creating": "Vytváření...", - "updating": "Aktualizování..." - }, - "sub_address": { - "button_tooltip": "Použít subadresu", - "popover_title": "Přidat štítek subadresy", - "tag_input_placeholder": "Zadejte štítek (např. nakupy)", - "preview_label": "Náhled:", - "recent_tags": "Nedávné štítky", - "suggested_tags": "Doporučené", - "use_address": "Použít tuto adresu", - "invalid_tag": "Štítek může obsahovat pouze písmena, číslice a pomlčky", - "tag_too_long": "Štítek může mít maximálně 30 znaků", - "help_text": "Zprávy odeslané na adresu uzivatel{delimiter}stitek@domena.cz budou doručeny do vaší doručené pošty", - "validation": { - "empty": "Štítek nesmí být prázdný", - "too_long": "Štítek může mít maximálně {max} znaků", - "invalid_chars": "Štítek může obsahovat pouze písmena, číslice a pomlčky" - } - }, - "badge": { - "sent_via": "přes", - "sub_address_tag": "Odesláno pomocí subadresy: {tag}", - "identity_name": "Odesláno pomocí identity: {name}", - "identity_short": "přes {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Odstranit", - "delete_confirm_title": "Odstranit identitu" - }, - "templates": { - "picker_title": "Vyberte šablonu", - "search_placeholder": "Hledat šablony...", - "section_favorites": "Oblíbené", - "section_recent": "Nedávné", - "section_uncategorized": "Ostatní", - "no_templates": "Zatím žádné šablony", - "no_results": "Nenalezeny žádné šablony", - "fill_placeholders": "Vyplnit hodnoty proměnných", - "enter_value": "Zadejte hodnotu...", - "preview": "Náhled", - "insert_with_values": "Vložit s hodnotami", - "insert_raw": "Vložit beze změn", - "copy_suffix": "(kopie)", - "placeholder": "Proměnná", - "placeholders": { - "recipient_name": "Jméno příjemce", - "company": "Název společnosti", - "date": "Aktuální datum", - "day_of_week": "Den v týdnu", - "sender_name": "Vaše jméno" - } - }, - "contacts": { - "title": "Kontakty", - "search_placeholder": "Hledat kontakty...", - "create_new": "Nový kontakt", - "no_category": "Bez kategorie", - "rename_category": "Přejmenovat kategorii", - "category_name_label": "Název kategorie", - "category_renamed": "Kategorie byla přejmenována", - "category_rename_failed": "Přejmenování kategorie selhalo", - "category_added": "Kontakt přidán do {name}", - "category_added_plural": "{count} kontaktů bylo přidáno do {name}", - "empty_state": "Žádné kontakty", - "empty_state_title": "Žádné kontakty", - "empty_state_subtitle": "Vytvořte první kontakt nebo importujte ze souboru vCard", - "empty_search": "Hledání neodpovídají žádné kontakty", - "empty_search_hint": "Zkuste použít jiný hledaný výraz", - "empty_filtered": "Žádné kontakty neodpovídají filtrům", - "empty_filtered_hint": "Zkuste filtry upravit nebo zrušit", - "clear_search": "Vymazat hledání", - "import_vcard": "Importovat vCard", - "delete_confirm_title": "Odstranit kontakt", - "delete_confirm": "Opravdu chcete odstranit tento kontakt?", - "local_mode": "Kontakty jsou uloženy lokálně (server nepodporuje JMAP Contacts)", - "back_to_contacts": "Zpět na kontakty", - "back_to_email": "Zpět na e-mail", - "tabs": { - "all": "Všechny", - "groups": "Skupiny" - }, - "shared": { - "title": "Sdílené" - }, - "address_books": { - "create": "Nový adresář", - "created": "Adresář vytvořen", - "create_failed": "Nepodařilo se vytvořit adresář", - "title": "Moje adresáře", - "shared_prefix": "Sdílené: {name}", - "moved": "Kontakt přesunut do {name}", - "moved_plural": "{count} kontaktů bylo přesunuto do {name}", - "move_failed": "Přesun kontaktu selhal", - "address_book": "Adresář", - "rename": "Přejmenovat adresář", - "name_label": "Název adresáře", - "renamed": "Adresář byl přejmenován", - "rename_failed": "Přejmenování adresáře selhalo", - "default": "Výchozí", - "set_default": "Nastavit jako výchozí", - "default_updated": "Výchozí adresář aktualizován", - "set_default_failed": "Nepodařilo se nastavit výchozí adresář", - "manage": "Spravovat adresáře", - "share": "Sdílet adresář", - "new_contact_in_book": "Nový kontakt v tomto adresáři", - "delete": "Smazat adresář", - "confirm_delete": "Smazat „{name}\"? Všechny kontakty v tomto adresáři budou odstraněny.", - "deleted": "Adresář smazán", - "delete_failed": "Adresář se nepodařilo smazat" - }, - "detail": { - "emails": "E-mailové adresy", - "phones": "Telefonní čísla", - "organizations": "Organizace", - "addresses": "Adresy", - "notes": "Poznámky", - "titles": "Pozice a role", - "online_services": "Online služby", - "anniversaries": "Výročí", - "personal_info": "Osobní informace", - "languages": "Jazyky", - "categories": "Kategorie", - "related_contacts": "Související kontakty", - "crypto_keys": "Kryptografické klíče", - "cert_issuer": "Vystavitel", - "cert_expires": "Vyprší", - "cert_expired": "Vypršela platnost", - "cert_fingerprint": "Otisk", - "cert_algorithm": "Algoritmus", - "import_to_smime": "Importovat do S/MIME", - "cert_already_imported": "Již importováno do S/MIME", - "cert_imported": "Certifikát byl importován do úložiště S/MIME", - "cert_import_failed": "Import certifikátu selhal", - "no_contact_selected": "Vyberte kontakt pro zobrazení podrobností", - "compose_email": "Napsat e-mail", - "copy_email": "Kopírovat e-mail", - "copy_phone": "Kopírovat telefon", - "copy_url": "Kopírovat URL", - "copied": "Zkopírováno do schránky", - "copy_failed": "Kopírování do schránky selhalo", - "created": "Vytvořeno", - "updated": "Poslední aktualizace", - "timezone": "Časové pásmo", - "anniversary_birth": "Narozeniny", - "anniversary_death": "Úmrtí", - "anniversary_wedding": "Výročí svatby", - "anniversary_other": "Ostatní", - "personal_expertise": "Odbornost", - "personal_hobby": "Koníčky", - "personal_interest": "Zájmy", - "personal_other": "Ostatní", - "gender": "Pohlaví", - "gender_masculine": "Muž", - "gender_feminine": "Žena", - "gender_other": "Jiné", - "gender_none": "Neuvádí se", - "gender_unknown": "Neznámé", - "calendar": "Kalendář", - "calendar_uri": "URL kalendáře", - "scheduling_uri": "URL pro plánování", - "freebusy_uri": "URL dostupnosti", - "section_contact": "Kontaktní údaje", - "section_work": "Práce", - "section_personal": "Osobní", - "email_default_label": "E-mail", - "phone_default_label": "Telefon", - "address_default_label": "Adresa", - "online_service_default_label": "Online", - "organization_label": "Organizace", - "title_label": "Pozice", - "role_label": "Role", - "language_label": "Jazyk", - "related_default_label": "Vztahy", - "more_actions": "Další akce", - "age_years": "{count, plural, one {1 rok} few {# roky} other {# let}}", - "years_since": "{count, plural, one {1 rok} few {# roky} other {# let}}" - }, - "activity": { - "recent_emails": "Nedávné e-maily", - "upcoming_events": "Nadcházející události", - "no_emails": "Žádné nedávné e-maily", - "no_events": "Žádné nadcházející události", - "no_subject": "(Bez předmětu)", - "no_title": "(Bez názvu)", - "load_failed": "Nepodařilo se načíst", - "unknown_sender": "Neznámý odesílatel", - "all_day": "Celý den" - }, - "form": { - "create_title": "Nový kontakt", - "edit_title": "Upravit kontakt", - "section_address_book": "Adresář", - "select_address_book": "Vyberte adresář...", - "section_identity": "Jméno a identita", - "contact_type": "Typ kontaktu", - "type_person": "Osoba", - "type_organization": "Organizace", - "section_work": "Práce a organizace", - "prefix": "Titul", - "prefix_placeholder": "Dr., Pan, Paní", - "given_name": "Jméno", - "middle_name": "Prostřední jméno", - "surname": "Příjmení", - "suffix": "Přípona", - "suffix_placeholder": "Jr., Sr., Ph.D.", - "nickname": "Přezdívka", - "nickname_placeholder": "Přezdívka", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Telefon", - "phone_placeholder": "+420 123 456 789", - "phone_type": "Typ", - "phone_voice": "Hlasový", - "phone_cell": "Mobilní", - "phone_fax": "Fax", - "phone_pager": "Pager", - "phone_video": "Video", - "phone_text": "SMS", - "organization": "Organizace", - "organization_placeholder": "Název společnosti", - "department": "Oddělení", - "department_placeholder": "Oddělení", - "job_title": "Pozice", - "job_title_placeholder": "např. Softwarový inženýr", - "role": "Role", - "role_placeholder": "např. Vedoucí týmu", - "addresses": "Adresy", - "add_address": "Přidat adresu", - "street": "Ulice", - "city": "Město", - "region": "Stát / Kraj", - "postcode": "PSČ", - "country": "Země", - "online_services": "Online služby", - "add_online_service": "Přidat online službu", - "url_placeholder": "https://...", - "service_placeholder": "Služba", - "anniversaries": "Výročí", - "add_anniversary": "Přidat datum", - "anniversary_birth": "Narozeniny", - "anniversary_wedding": "Výročí svatby", - "anniversary_death": "Úmrtí", - "anniversary_other": "Ostatní", - "personal_info": "Osobní informace", - "add_personal_info": "Přidat záznam", - "personal_info_placeholder": "např. Fotografie", - "personal_expertise": "Odbornost", - "personal_hobby": "Koníčky", - "personal_interest": "Zájmy", - "personal_other": "Ostatní", - "level": "Úroveň", - "level_high": "Vysoká", - "level_medium": "Střední", - "level_low": "Nízká", - "categories": "Kategorie", - "categories_placeholder": "např. Rodina, Přátelé, Kolegové", - "categories_hint": "Pište pro vyhledání nebo přidání kategorií", - "category_add": "Přidat", - "note": "Poznámky", - "note_placeholder": "Přidat poznámku...", - "gender": "Pohlaví", - "gender_sex": "Biologické pohlaví", - "gender_male": "Muž", - "gender_female": "Žena", - "gender_other": "Jiné", - "gender_none": "Neuvádí se", - "gender_unknown": "Neznámé", - "gender_identity": "Genderová identita", - "gender_identity_placeholder": "Genderová identita...", - "calendar": "Kalendář", - "calendar_uri": "URL kalendáře", - "scheduling_uri": "URL pro plánování", - "freebusy_uri": "URL volný/obsazený", - "context_work": "Pracovní", - "context_private": "Soukromé", - "add_email": "Přidat e-mail", - "add_phone": "Přidat telefon", - "save": "Uložit", - "cancel": "Zrušit", - "creating": "Vytváření...", - "updating": "Aktualizování...", - "name_required": "Zadejte jméno, příjmení nebo organizaci", - "email_invalid": "Zadejte platnou e-mailovou adresu", - "email_error_inline": "Neplatný formát e-mailové adresy", - "save_failed": "Uložení kontaktu selhalo", - "delete": "Odstranit", - "upload_photo": "Nahrát fotku", - "remove_photo": "Odebrat fotku", - "photo_hint": "JPG nebo PNG, max. 10 MB. Velikost se upraví.", - "photo_too_large": "Obrázek je moc velký (max. 10 MB)", - "photo_invalid": "Neplatný formát obrázku", - "change_photo": "Změnit" - }, - "groups": { - "create": "Nová skupina", - "edit": "Upravit skupinu", - "empty": "Žádné skupiny", - "delete_confirm_title": "Odstranit skupinu", - "delete_confirm": "Opravdu chcete odstranit tuto skupinu?", - "name_label": "Název skupiny", - "name_placeholder": "např. Tým, Rodina", - "name_required": "Název skupiny je vyžadován", - "save_failed": "Uložení skupiny selhalo", - "members_label": "Členové", - "search_members": "Hledat kontakty k přidání...", - "no_members": "Tato skupina nemá žádné členy", - "member_count": "{count, plural, =0 {Žádní členové} one {1 člen} few {# členové} other {# členů}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Importovat kontakty", - "drop_hint": "Klikněte pro výběr souboru vCard", - "file_types": "soubory .vcf nebo .vcard", - "no_contacts": "V souboru nebyly nalezeny žádné kontakty", - "parse_error": "Zpracování souboru vCard selhalo", - "found": "{count, plural, one {Nalezen 1 kontakt} few {Nalezeny # kontakty} other {Nalezeno # kontaktů}}", - "duplicate": "Duplikát", - "select_all": "Vybrat vše", - "deselect_all": "Zrušit výběr všeho", - "selected": "{count, plural, one {1 vybrán} few {# vybrány} other {# vybráno}}", - "import_button": "Importovat", - "importing": "Importování...", - "success": "{count, plural, one {Importován 1 kontakt} few {Importovány # kontakty} other {Importováno # kontaktů}}", - "failed": "Import selhal", - "close": "Zavřít", - "file_too_large": "Soubor je příliš velký (max. 5 MB)" - }, - "export": { - "title": "Exportovat kontakty", - "success": "{count, plural, one {Exportován 1 kontakt} few {Exportovány # kontakty} other {Exportováno # kontaktů}}" - }, - "bulk": { - "selected": "{count, plural, one {1 vybrán} few {# vybrány} other {# vybráno}}", - "select_all": "Vybrat vše", - "delete": "Odstranit", - "delete_confirm_title": "Odstranit kontakty", - "delete_confirm": "Odstranit {count, plural, one {1 kontakt} few {# kontakty} other {# kontaktů}}?", - "deleted": "{count, plural, one {Odstraněn 1 kontakt} few {Odstraněny # kontakty} other {Odstraněno # kontaktů}}", - "add_to_group": "Přidat do skupiny", - "choose_group": "Vyberte skupinu", - "adding_contacts": "Přidávání {count, plural, one {1 kontaktu} few {# kontaktů} other {# kontaktů}}", - "added_to_group": "Kontakty byly přidány do skupiny", - "export": "Exportovat", - "clear": "Zrušit výběr" - }, - "toast": { - "created": "Kontakt byl vytvořen", - "updated": "Kontakt byl aktualizován", - "deleted": "Kontakt byl odstraněn", - "error_create": "Vytvoření kontaktu selhalo", - "error_update": "Aktualizace kontaktu selhala", - "error_delete": "Odstranění kontaktu selhalo" - }, - "context_menu": { - "open": "Otevřít", - "edit": "Upravit", - "send_email": "Poslat e-mail", - "add_to_group": "Přidat do skupiny", - "export_vcard": "Exportovat jako vCard", - "delete": "Smazat", - "call": "Zavolat", - "duplicate": "Duplikovat", - "print": "Vytisknout" - }, - "filters": { - "toggle": "Filtry", - "select": "Vybrat", - "clear": "Vymazat", - "close": "Zavřít", - "title": "Pokročilé filtry", - "organization": "Firma", - "organization_placeholder": "např. Acme Corp", - "job_title": "Pozice", - "job_title_placeholder": "např. Designér", - "location": "Lokalita", - "location_placeholder": "Město nebo země", - "email_domain": "E-mailová doména", - "email_domain_placeholder": "example.com", - "birthday_month": "Narozeniny v", - "any_month": "Kterýkoli měsíc", - "has_email": "Má e-mail", - "has_phone": "Má telefon", - "has_photo": "Má fotku", - "sort_by": "Řadit podle", - "sort_first_name": "Jméno", - "sort_last_name": "Příjmení" - }, - "open_categories": "Otevřít kategorie" - }, - "calendar": { - "title": "Kalendář", - "back_to_email": "Zpět na poštu", - "back_to_month": "Zpět na měsíc", - "my_calendars": "Kalendáře", - "birthday_calendar": "Narozeniny", - "mini_calendar_change": "Kliknutím změníte měsíc", - "views": { - "month": "Měsíc", - "week": "Týden", - "day": "Den", - "agenda": "Agenda", - "today": "Dnes", - "month_hint": "Měsíc (m)", - "week_hint": "Týden (w)", - "day_hint": "Den (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Úkoly", - "tasks_hint": "Úkoly (k)" - }, - "events": { - "create": "Vytvořit událost", - "edit": "Upravit událost", - "delete": "Odstranit událost", - "details": "Podrobnosti události", - "no_events": "Žádné události", - "all_day": "Celý den", - "more": "+{count} dalších", - "no_title": "(Bez názvu)", - "resize": "Změnit velikost události", - "duplicate": "Duplikovat", - "today_header": "Dnes", - "tomorrow_header": "Zítra", - "agenda_show_earlier": "Zobrazit dřívější události", - "agenda_loading": "Načítání…", - "agenda_range_start": "Události od {date}", - "agenda_range_end": "Události do {date}", - "export_ics": "Exportovat jako .ics", - "copy_title": "Kopírovat název", - "copy_link": "Kopírovat odkaz na schůzku", - "go_to_today": "Přejít na dnešek", - "new_all_day_event": "Nová celodenní událost", - "new_event": "Nová událost", - "new_task": "Nový úkol" - }, - "detail": { - "add_note": "Přidat poznámku...", - "save_note": "Uložit", - "note_saved": "Poznámka přidána", - "open_link": "Otevřít odkaz", - "meeting_link": "Odkaz na schůzku", - "tentative": "Nezávazně", - "cancelled": "Zrušeno", - "delete_confirm": "Odstranit tuto událost?" - }, - "form": { - "title": "Název", - "description": "Popis", - "location": "Místo", - "meeting_link": "Odkaz na schůzku", - "start_date": "Datum zahájení", - "end_date": "Datum ukončení", - "start_time": "Čas zahájení", - "end_time": "Čas ukončení", - "all_day_event": "Celodenní událost", - "calendar_select": "Kalendář", - "save": "Uložit", - "cancel": "Zrušit", - "delete_confirm": "Opravdu chcete odstranit tuto událost?", - "color": "Barva" - }, - "participants": { - "availability": { - "title": "Dostupnost", - "free": "Volný", - "busy": "Zaneprázdněn", - "tentative": "Předběžně", - "unknown": "Není uživatelem tohoto serveru", - "checking": "Ověřuje se…" - }, - "title": "Účastníci", - "add": "Přidat účastníka", - "organizer": "Organizátor", - "attendee": "Účastník", - "accepted": "Přijato", - "declined": "Odmítnuto", - "tentative": "Nezávazně", - "needs_action": "Vyžaduje akci", - "remove": "Odstranit", - "edit": "Upravit", - "email_placeholder": "Přidejte e-mailovou adresu nebo vyhledejte kontakty", - "send_invitations": "Odeslat pozvánky účastníkům", - "status_summary": "{accepted} přijato, {pending} čeká", - "invited_by": "Pozvánka od {name}", - "respond_below": "Odpovězte pomocí tlačítek níže", - "rsvp_label": "Vaše odpověď", - "cancel_notification": "Účastníci budou upozorněni na zrušení", - "you_organizer": "Jste organizátorem", - "you_attendee": "Jste účastníkem", - "no_participants": "Žádní účastníci", - "count": "{count, plural, one {1 účastník} few {# účastníci} other {# účastníků}}" - }, - "recurrence": { - "title": "Opakování", - "none": "Neopakuje se", - "daily": "Denně", - "weekly": "Týdně", - "monthly": "Měsíčně", - "yearly": "Ročně", - "every_n_days": "Každých {count} dní", - "every_n_weeks": "Každých {count} týdnů", - "every_n_months": "Každých {count} měsíců", - "until": "Do", - "occurrences": "{count} opakování", - "custom": "Vlastní…", - "edit_custom": "Upravit vlastní opakování", - "every_n_years": "Každých {count} let", - "on_days": "v {days}", - "on_day_n": "v den {day}", - "on_the_nth": "{nth} {day}", - "in_month": "v měsíci {month}", - "nth_1": "první", - "nth_2": "druhý", - "nth_3": "třetí", - "nth_4": "čtvrtý", - "nth_last": "poslední", - "editor_freq_day": "Den", - "editor_freq_week": "Týden", - "editor_freq_month": "Měsíc", - "editor_freq_year": "Rok", - "editor_repeats_on": "Opakuje se", - "editor_every": "Každých", - "editor_unit_days": "dní", - "editor_unit_weeks": "týdnů", - "editor_unit_months": "měsíců", - "editor_unit_years": "let", - "editor_on_day": "v den", - "editor_on_the": "v", - "editor_in": "v měsíci", - "editor_ends": "Končí", - "editor_never": "Nikdy", - "editor_ends_on": "Dne", - "editor_ends_after": "Po", - "editor_occurrences": "opakováních" - }, - "recurrence_scope": { - "edit_title": "Upravit opakující se událost", - "delete_title": "Odstranit opakující se událost", - "description": "Toto je opakující se událost. Které události chcete upravit?", - "this_event": "Pouze tuto událost", - "this_and_future": "Tuto a všechny následující události", - "all_events": "Všechny události", - "cancel": "Zrušit", - "save": "Uložit", - "delete": "Odstranit", - "rsvp_title": "Odpovědět na opakující se událost", - "rsvp_description": "Toto je opakující se událost. Na které události se vaše odpověď vztahuje?", - "respond": "Odpovědět" - }, - "alerts": { - "title": "Připomenutí", - "none": "Žádné připomenutí", - "at_time": "V čase události", - "minutes_before": "{count, plural, one {1 minutu před} few {# minuty před} other {# minut před}}", - "hours_before": "{count, plural, one {1 hodinu před} few {# hodiny před} other {# hodin před}}", - "days_before": "{count, plural, one {1 den před} few {# dny před} other {# dnů před}}", - "weeks_before": "{count, plural, one {1 týden před} few {# týdny před} other {# týdnů před}}", - "unit_minutes_before": "minut před", - "unit_hours_before": "hodin před", - "unit_days_before": "dní před", - "unit_weeks_before": "týdnů před", - "add": "Přidat připomenutí", - "remove": "Odebrat připomenutí", - "amount": "Množství připomenutí", - "unit": "Jednotka připomenutí" - }, - "settings": { - "title": "Nastavení kalendáře", - "default_view": "Výchozí zobrazení", - "week_starts_on": "Týden začíná v", - "time_format": "Formát času", - "default_calendar": "Výchozí kalendář", - "default_reminder": "Výchozí připomenutí", - "time_format_12h": "12hodinový", - "time_format_24h": "24hodinový", - "notifications_enabled": "Oznámení o událostech", - "notifications_enabled_desc": "Zobrazovat upozornění na nadcházející události v kalendáři", - "notification_sound": "Zvuk oznámení", - "notification_sound_desc": "Přehrát zvukové upozornění pro kalendář", - "invitation_parsing": "Analyzovat e-mailové pozvánky", - "invitation_parsing_desc": "Rozpoznávat pozvánky kalendáře v přílohách e-mailů a zobrazovat akce kalendáře", - "show_time_in_month_view": "Zobrazit čas v měsíčním zobrazení", - "show_time_in_month_view_desc": "Zobrazovat časy událostí v měsíčním zobrazení kalendáře", - "show_week_numbers": "Zobrazit čísla týdnů", - "show_week_numbers_desc": "Zobrazovat čísla týdnů v minikalendáři", - "calendar_free_scroll": "Volné posouvání", - "calendar_free_scroll_desc": "Plynule procházet měsíce, týdny a dny místo jednoho období najednou", - "enable_tasks": "Povolit úkoly", - "enable_tasks_desc": "Zobrazovat zobrazení úkolů v kalendáři pro správu úkolů", - "show_tasks_on_calendar": "Zobrazit úkoly v kalendáři", - "show_tasks_on_calendar_desc": "Zobrazovat značky úkolů v denním a týdenním zobrazení kalendáře", - "hover_preview": "Náhled události po přejetí", - "hover_preview_desc": "Zobrazovat vyskakovací okno s podrobnostmi při najetí myší na události v kalendáři", - "hover_preview_instant": "Okamžitě", - "hover_preview_delay_500ms": "Zpoždění 0,5 sekundy", - "hover_preview_delay_1s": "Zpoždění 1 sekunda", - "hover_preview_delay_2s": "Zpoždění 2 sekundy", - "hover_preview_off": "Vypnuto", - "organizer_identity": "Organizovat pozvánky jako", - "organizer_identity_desc": "Adresa kalendáře použitá jako organizátor, když zvete lidi na události.", - "organizer_identity_failed": "Nepodařilo se změnit adresu organizátora", - "show_birthday_calendar": "Kalendář narozenin", - "show_birthday_calendar_desc": "Zobrazovat virtuální kalendář s narozeninami z vašich kontaktů" - }, - "days": { - "monday": "Pondělí", - "tuesday": "Úterý", - "wednesday": "Středa", - "thursday": "Čtvrtek", - "friday": "Pátek", - "saturday": "Sobota", - "sunday": "Neděle", - "mon": "Po", - "tue": "Út", - "wed": "St", - "thu": "Čt", - "fri": "Pá", - "sat": "So", - "sun": "Ne" - }, - "notifications": { - "event_created": "Událost byla vytvořena", - "event_updated": "Událost byla aktualizována", - "event_deleted": "Událost byla odstraněna", - "calendar_created": "Kalendář byl vytvořen", - "calendar_deleted": "Kalendář byl odstraněn", - "event_move_error": "Přesun události selhal", - "event_resize_error": "Změna velikosti události selhala", - "alert_title": "Nadcházející událost", - "alert_now": "Začíná nyní", - "alert_in_minutes": "Za {count} min", - "invitation_sent": "Pozvánky byly odeslány", - "invitations_denied_title": "Pozvánky nelze odeslat", - "invitations_denied": "Server odmítl odeslat pozvánky: {reason}. Uložit událost bez jejich odeslání?", - "save_without_invitations": "Uložit bez pozvánek", - "rsvp_updated": "Odpověď byla aktualizována", - "rsvp_error": "Aktualizace odpovědi selhala", - "event_duplicated": "Událost byla duplikována", - "event_error": "Uložení události selhalo", - "task_due": "Termín úkolu", - "event_exported": "Událost exportována", - "title_copied": "Název zkopírován", - "link_copied": "Odkaz zkopírován" - }, - "status": { - "loading_calendars": "Načítání kalendářů...", - "loading_events": "Načítání událostí..." - }, - "quick_create": { - "placeholder": "Název nové události", - "aria_label": "Rychlé vytvoření události" - }, - "nav_prev": "Předchozí", - "nav_next": "Další", - "import": { - "title": "Importovat kalendář", - "tab_file": "Soubor", - "tab_url": "URL", - "select_file": "Vyberte soubor .ics", - "drop_file": "nebo sem přetáhněte soubor", - "supported_formats": "Podporuje soubory iCalendar (.ics)", - "url_description": "Zadejte adresu URL externího zdroje iCalendar (.ics) pro import událostí.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Podporuje adresy URL iCalendar (.ics)", - "fetch": "Načíst", - "invalid_url": "Zadejte platnou adresu URL", - "url_fetch_failed": "Načtení kalendáře z adresy URL selhalo", - "parsing": "Analyzování souboru kalendáře...", - "parsed_events": "Nalezeno {count} událostí", - "no_events": "V souboru nebyly nalezeny žádné události", - "select_all": "Vybrat vše", - "deselect_all": "Zrušit výběr všeho", - "target_calendar": "Importovat do kalendáře", - "import_button": "Importovat vybrané", - "importing": "Importování událostí...", - "success": "Úspěšně importováno {count} událostí", - "error": "Import kalendáře selhal", - "file_too_large": "Soubor překračuje limit 10 MB", - "invalid_format": "Neplatný formát souboru kalendáře" - }, - "webcal_action": { - "title": "Otevřít odkaz kalendáře", - "description": "Jak chcete použít \"{name}\"?", - "import_title": "Jednorázově importovat", - "import_description": "Načíst události nyní a zkopírovat je do jednoho z vašich kalendářů.", - "subscribe_title": "Odebírat", - "subscribe_description": "Automaticky synchronizovat tento kalendář jako samostatný kalendář.", - "cancel": "Zrušit" - }, - "management": { - "title": "Správa kalendáře", - "description": "Vytvářejte, přejmenovávejte a přizpůsobujte si své kalendáře. Klikněte pravým tlačítkem na kalendář v postranním panelu pro rychlou změnu jeho barvy.", - "name": "Název", - "name_placeholder": "Název kalendáře", - "color": "Barva", - "change_color": "Změnit barvu", - "random_color": "Nová náhodná barva", - "add_calendar": "Přidat kalendář", - "edit": "Upravit", - "delete": "Odstranit", - "save": "Uložit", - "create": "Vytvořit", - "cancel": "Zrušit", - "default": "Výchozí", - "confirm_delete": "Odstranit \"{name}\"? Všechny události v tomto kalendáři budou odstraněny.", - "confirm_clear": "Vymazat všechny události z \"{name}\"? Tuto akci nelze vrátit zpět.", - "set_default": "Nastavit jako výchozí", - "default_updated": "Výchozí kalendář aktualizován", - "error_default": "Nepodařilo se nastavit výchozí kalendář", - "clear_events": "Vymazat události", - "events_cleared": "Vymazáno {count} událostí", - "error_clear": "Vymazání událostí kalendáře selhalo", - "calendar_created": "Kalendář byl vytvořen", - "calendar_updated": "Kalendář byl aktualizován", - "calendar_deleted": "Kalendář byl odstraněn", - "color_updated": "Barva kalendáře byla aktualizována", - "error_create": "Vytvoření kalendáře selhalo", - "error_update": "Aktualizace kalendáře selhala", - "error_delete": "Odstranění kalendáře selhalo", - "caldav_url": "URL CalDAV", - "copy_url": "Kopírovat URL CalDAV", - "url_copied": "URL CalDAV zkopírováno do schránky", - "share": "Sdílet kalendář", - "new_event_in_calendar": "Nová událost v tomto kalendáři", - "kind": "Obsah", - "kind_events": "Události", - "kind_tasks": "Úkoly", - "kind_both": "Události a úkoly", - "kind_hint": "Určuje, které aplikace tento kalendář uvidí při synchronizaci přes CalDAV. Později to nelze změnit." - }, - "subscription": { - "title": "Odběr iCal", - "section_title": "Odběry iCal", - "description": "Odebírejte externí zdroj iCalendar. Události budou automaticky synchronizovány do samostatného kalendáře. Podporuje adresy URL https:// a webcal://.", - "url_label": "URL kalendáře", - "url_placeholder": "https://example.com/calendar.ics nebo webcal://...", - "name_label": "Název kalendáře", - "name_placeholder": "např. Státní svátky", - "color_label": "Barva", - "refresh_interval": "Interval obnovy", - "interval_15": "Každých 15 minut", - "interval_30": "Každých 30 minut", - "interval_60": "Každou hodinu", - "interval_360": "Každých 6 hodin", - "interval_1440": "Denně", - "subscribe": "Odebírat", - "subscribing": "Přihlašování k odběru...", - "save": "Uložit změny", - "saving": "Ukládání...", - "edit": "Upravit", - "edit_title": "Upravit odběr", - "updated": "Aktualizováno \"{name}\"", - "update_error": "Aktualizace odběru selhala", - "invalid_url": "Zadejte platnou adresu URL", - "success": "Odebíráte \"{name}\"", - "error": "Přidání odběru selhalo", - "refresh": "Obnovit nyní", - "refresh_success": "Odběr byl obnoven", - "refresh_error": "Obnovení odběru selhalo", - "unsubscribe": "Zrušit odběr", - "confirm_delete": "Zrušit odběr \"{name}\"? Kalendář a všechny jeho události budou odstraněny.", - "deleted": "Odběr byl odstraněn", - "delete_error": "Odstranění odběru selhalo", - "last_refreshed": "Poslední aktualizace: {time}" - }, - "tasks": { - "label": "Úkoly", - "no_tasks": "Žádné úkoly", - "no_title": "(Bez názvu)", - "mark_complete": "Označit jako dokončené", - "mark_incomplete": "Označit jako nedokončené", - "filter_all": "Všechny", - "filter_pending": "Čekající", - "filter_completed": "Dokončené", - "filter_overdue": "Po termínu", - "show_completed": "Zobrazit dokončené", - "create": "Nový úkol", - "edit": "Upravit úkol", - "title_placeholder": "Název úkolu", - "description_placeholder": "Přidat popis...", - "due_date": "Termín", - "include_time": "Zahrnout čas", - "priority": "Priorita", - "priority_none": "Žádná", - "priority_high": "Vysoká", - "priority_medium": "Střední", - "priority_low": "Nízká", - "progress": "Stav", - "progress_needs_action": "Vyžaduje akci", - "progress_in_process": "Probíhá", - "progress_completed": "Dokončeno", - "progress_cancelled": "Zrušeno", - "calendar": "Kalendář", - "alert": "Připomenutí", - "alert_none": "Žádné", - "alert_at_time": "V čase termínu", - "alert_5min": "5 minut před", - "alert_15min": "15 minut před", - "alert_30min": "30 minut před", - "alert_1hr": "1 hodinu před", - "alert_1day": "1 den před", - "delete": "Odstranit", - "cancel": "Zrušit", - "save": "Uložit", - "quick_add_placeholder": "Přidat úkol...", - "due_today": "Dnes", - "due_tomorrow": "Zítra", - "overdue": "Po termínu" - }, - "months": { - "jan": "led", - "feb": "úno", - "mar": "bře", - "apr": "dub", - "may": "kvě", - "jun": "čvn", - "jul": "čvc", - "aug": "srp", - "sep": "zář", - "oct": "říj", - "nov": "lis", - "dec": "pro", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Otevřít nabídku" - }, - "advanced_search": { - "title": "Pokročilé hledání", - "from": "Od", - "from_placeholder": "E-mailová adresa nebo jméno odesílatele", - "to": "Komu", - "to_placeholder": "E-mailová adresa nebo jméno příjemce", - "subject": "Předmět", - "subject_placeholder": "Předmět obsahuje...", - "body": "Tělo zprávy", - "body_placeholder": "Tělo zprávy obsahuje...", - "folder": "Složka", - "all_folders": "Všechny složky", - "has_attachment": "Obsahuje přílohu", - "date_after": "Po", - "date_before": "Před", - "size_min": "Větší než (KB)", - "size_max": "Menší než (KB)", - "starred": "S hvězdičkou", - "unread": "Nepřečtené", - "read": "Přečtené", - "yes": "Ano", - "no": "Ne", - "clear": "Vymazat", - "clear_all": "Vymazat vše", - "filters_active": "{count} filtr", - "filters_active_plural": "{count} filtry", - "toggle_filters": "Více", - "search_hint": "Použijte pokročilé filtry pro přesné vyhledávání", - "advanced_filters_tooltip": "Pokročilé filtry hledání", - "results_found": "{count, plural, =0 {Nenalezeny žádné výsledky} one {Nalezen 1 výsledek} few {Nalezeny # výsledky} other {Nalezeno # výsledků}}", - "results_found_more": "Nalezeno více než {count} výsledků", - "suggestions_recent": "Nedávná hledání", - "suggestions_people": "Lidé", - "suggestions_from": "Hledat e-maily od {address}", - "suggestions_to": "Hledat e-maily pro {address}", - "suggestions_remove_recent": "Odebrat z nedávných hledání" - }, - "welcome": { - "title": "Vítejte ve své poštovní schránce", - "tip_compose": "Stiskněte c pro napsání nové zprávy", - "tip_shortcuts": "Stiskněte ? pro zobrazení všech klávesových zkratek", - "tip_sidebar": "Kalendář, Kontakty a Nastavení najdete v postranním panelu", - "tip_settings": "Přizpůsobte si aplikaci v Nastavení", - "got_it": "Rozumím", - "settings": "Nastavení", - "dismiss": "Zavřít", - "start_tour": "Spustit průvodce" - }, - "demo_welcome": { - "title": "Vítejte v Bulwark Mail", - "description": "Prozkoumejte plně funkčního webového e-mailového klienta - přímo v prohlížeči. Všechna data zůstávají na vašem zařízení, takže můžete bez obav vše otestovat.", - "feature_email": "Čtěte a pište e-maily", - "feature_organize": "Štítky, hvězdičky a složky", - "feature_shortcuts": "Klávesové zkratky", - "feature_privacy": "100% soukromá demoverze", - "hint": "Klikněte na libovolnou zprávu vlevo pro začátek, nebo využijte průvodce níže." - }, - "files": { - "office_edit": "Upravit dokument", - "office_loading": "Otevírání editoru dokumentů…", - "office_error": "Editor dokumentů se nepodařilo otevřít.", - "office_close": "Zavřít editor", - "open_in_new_tab": "Otevřít na nové kartě", - "title": "Soubory", - "search_placeholder": "Hledat soubory...", - "empty_state_title": "Žádné soubory", - "empty_state_description": "Nahrajte soubory nebo vytvořte složky pro začátek", - "upload": "Nahrát", - "upload_files": "Nahrát soubory", - "new_folder": "Nová složka", - "new_folder_name": "Název složky", - "rename": "Přejmenovat", - "rename_title": "Přejmenovat", - "new_name": "Nový název", - "name_forbidden_chars": "Server nepovoluje v názvech tyto znaky: {chars}", - "name_reserved": "Tento název je na serveru vyhrazen.", - "delete": "Odstranit", - "delete_confirm_title": "Odstranit položku", - "delete_confirm_message": "Opravdu chcete odstranit \"{name}\"? Tuto akci nelze vrátit zpět.", - "download": "Stáhnout", - "name": "Název", - "size": "Velikost", - "modified": "Změněno", - "type": "Typ", - "folder": "Složka", - "file": "Soubor", - "parent_directory": "Nadřazený adresář", - "breadcrumb_root": "Domů", - "drop_files_here": "Přetáhněte sem soubory nebo složky pro jejich nahrání", - "uploading": "Nahrávání...", - "upload_success": "{count, plural, one {Nahrán 1 soubor} few {Nahrány # soubory} other {Nahráno # souborů}}", - "upload_error": "Nahrání souboru selhalo", - "create_folder_success": "Složka byla vytvořena", - "create_folder_error": "Vytvoření složky selhalo", - "delete_success": "Úspěšně odstraněno", - "delete_error": "Odstranění selhalo", - "rename_success": "Úspěšně přejmenováno", - "rename_error": "Přejmenování selhalo", - "download_error": "Stahování selhalo", - "not_available": "Úložiště souborů není na tomto serveru k dispozici", - "cancel": "Zrušit", - "create": "Vytvořit", - "save": "Uložit", - "no_results": "Hledání neodpovídají žádné soubory", - "batch_delete_confirm_message": "Opravdu chcete odstranit {count, plural, one {1 položku} few {# položky} other {# položek}}? Tuto akci nelze vrátit zpět.", - "batch_delete_success": "{count, plural, one {Odstraněna 1 položka} few {Odstraněny # položky} other {Odstraněno # položek}}", - "grid_view": "Zobrazení mřížky", - "list_view": "Zobrazení seznamu", - "details": "Podrobnosti", - "path": "Cesta", - "preview": "Náhled", - "preview_error": "Náhled se nepodařilo načíst", - "cut": "Vyjmout", - "copy": "Kopírovat", - "paste": "Vložit", - "move_success": "{count, plural, one {Přesunuta 1 položka} few {Přesunuty # položky} other {Přesunuto # položek}}", - "move_error": "Přesun selhal", - "paste_success": "Úspěšně vloženo", - "paste_error": "Vložení selhalo", - "new_text_file": "Nový textový soubor", - "file_name": "Název souboru", - "retry": "Opakovat", - "refresh": "Obnovit", - "toggle_favorite": "Přepnout oblíbené", - "duplicate": "Duplikovat", - "duplicate_success": "Úspěšně duplikováno", - "duplicate_error": "Duplikování selhalo", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Soubor byl vytvořen", - "create_file_error": "Vytvoření souboru selhalo", - "favorites": "Oblíbené", - "recent": "Nedávné", - "properties": "Vlastnosti", - "open_folder": "Otevřít složku", - "upload_folder": "Nahrát složku", - "file_too_large": "\"{name}\" překračuje maximální velikost souboru ({max})", - "undo": "Zpět", - "undo_success": "Akce vrácena zpět", - "undo_error": "Akci nelze vrátit zpět", - "toolbar": "Akce se soubory", - "file_list": "Soubory a složky", - "context_menu": "Akce", - "settings_title": "Nastavení souborů", - "settings_display": "Zobrazení", - "settings_default_view": "Výchozí zobrazení", - "settings_default_view_desc": "Vyberte rozložení mřížky nebo seznamu", - "settings_default_sort": "Výchozí řazení", - "settings_default_sort_desc": "Vyberte výchozí řazení souborů", - "settings_sort_direction": "Směr řazení", - "settings_sort_direction_desc": "Vyberte vzestupné nebo sestupné pořadí", - "settings_ascending": "Vzestupně", - "settings_descending": "Sestupně", - "settings_icons": "Ikony", - "settings_show_icons": "Zobrazovat ikony souborů", - "settings_show_icons_desc": "Zobrazovat ikony vedle souborů a složek", - "settings_colored_icons": "Barevné ikony", - "settings_colored_icons_desc": "Používat barevné ikony místo monochromatických", - "settings_show_thumbnails": "Zobrazovat miniatury", - "settings_show_thumbnails_desc": "Zobrazovat náhledy obrázků místo ikon pro obrazové soubory", - "settings_behavior": "Chování", - "settings_show_hidden": "Zobrazovat skryté soubory", - "settings_show_hidden_desc": "Zobrazovat soubory a složky začínající tečkou", - "settings_folder_layout": "Navigace po složkách", - "settings_folder_layout_desc": "Vyberte způsob zobrazení složek: společně se soubory nebo ve stromové struktuře v postranním panelu", - "settings_folder_layout_inline": "Vložené", - "settings_folder_layout_sidebar": "Postranní panel", - "disabled_title": "Funkce Soubory je zakázána správcem", - "disabled_description": "Nahrávání velkých souborů přes WebDAV může způsobit nestabilitu Stalwart/RocksDB, včetně pádů z důvodu nedostatku paměti a nevratného zaplnění disku. Odstraněné soubory nemusí být okamžitě odstraněny z úložiště. Tato funkce se nedoporučuje v produkčním prostředí.", - "stability_warning": "Nahrávání velkých souborů může způsobit nestabilitu serveru. Odstraněné soubory nemusí být okamžitě odstraněny z úložiště. Používejte s opatrností.", - "no_accounts": "Žádné připojené účty.", - "open_folder_tree": "Otevřít strom složek", - "other_accounts": "Ostatní účty", - "migration_title": "Aktualizace vašich souborů…", - "migration_description": "Uspořádání složek a souborů do správné struktury. Toto proběhne pouze jednou." - }, - "smime": { - "your_certificates": "Vaše certifikáty", - "your_certificates_desc": "Importujte a spravujte své certifikáty S/MIME pro podepisování a šifrování e-mailů", - "recipient_certificates": "Certifikáty příjemců", - "recipient_certificates_desc": "Veřejné certifikáty pro šifrování e-mailů odesílaných příjemcům", - "identity_bindings": "Vazby klíčů na identity", - "identity_bindings_desc": "Svažte certifikáty S/MIME s e-mailovými identitami", - "defaults_title": "Výchozí", - "defaults_desc": "Nakonfigurujte výchozí chování pro podepisování a šifrování", - "import_pkcs12": "Importovat PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importovat certifikát", - "no_certificates": "Zatím nebyly importovány žádné certifikáty", - "no_recipient_certs": "Žádné certifikáty příjemců", - "expires": "Vyprší", - "expired": "Vypršela platnost", - "bound_to": "Vázáno k", - "no_key_bound": "Žádné", - "lock": "Uzamknout klíč", - "unlock": "Odemknout klíč", - "details": "Zobrazit podrobnosti", - "delete": "Odstranit", - "encrypt_by_default": "Výchozí šifrování", - "encrypt_by_default_desc": "Automaticky šifrovat e-maily, pokud mají všichni příjemci certifikáty", - "remember_unlocked": "Pamatovat si odemčené klíče", - "remember_unlocked_desc": "Ponechat klíče odemčené po dobu trvání této relace prohlížeče", - "sign_default_for": "Výchozí podepisování pro", - "enter_p12_passphrase": "Zadejte heslo PKCS#12", - "p12_passphrase_desc": "Zadejte heslo chránící tento soubor certifikátu", - "enter_storage_passphrase": "Nastavte heslo úložiště", - "storage_passphrase_desc": "Vyberte heslo pro ochranu tohoto klíče při uložení v prohlížeči", - "next": "Další", - "import": "Importovat", - "unlock_key": "Odemknout klíč", - "unlock_key_desc": "Zadejte heslo úložiště pro odemčení tohoto klíče pro podepisování nebo dešifrování", - "passphrase_placeholder": "Zadejte heslo", - "confirm_passphrase_placeholder": "Potvrďte heslo", - "passphrase_mismatch": "Hesla se neshodují", - "cancel": "Zrušit", - "processing": "Zpracování…", - "close": "Zavřít", - "certificate_details": "Podrobnosti o certifikátu", - "cert_subject": "Subjekt", - "cert_issuer": "Vystavitel", - "cert_email": "E-mail", - "cert_serial": "Sériové číslo", - "cert_validity": "Platnost", - "cert_fingerprint": "Otisk (SHA-256)", - "cert_algorithm": "Algoritmus", - "cert_capabilities": "Možnosti", - "cert_source": "Zdroj", - "cert_expired": "Tento certifikát vypršel", - "cert_not_yet_valid": "Tento certifikát ještě není platný", - "cap_sign": "Podepisování", - "cap_encrypt": "Šifrování", - "cap_none": "Žádné", - "show_passphrase": "Zobrazit heslo", - "hide_passphrase": "Skrýt heslo", - "sign_toggle": "Podepsat", - "encrypt_toggle": "Šifrovat", - "missing_recipient_certs": "Chybí certifikáty pro: {emails}", - "missing_sender_cert": "Chybí certifikát vázaný k této identitě", - "status_encrypted_ok": "Tato zpráva byla zašifrována", - "status_encrypted_no_key": "Tato zpráva je zašifrována, ale nebyl nalezen odpovídající klíč", - "status_encrypted_failed": "Nepodařilo se dešifrovat tuto zprávu", - "status_signed_valid": "Podpis byl ověřen", - "status_signed_invalid": "Ověření podpisu selhalo", - "status_signed_expired_cert": "Podepsáno certifikátem, jehož platnost vypršela", - "status_signed_self_signed": "Podpis je platný, ale certifikát je vydán sám sobě (nedůvěryhodný)", - "status_signed_mismatch": "Podpis je platný, ale podepisující se neshoduje s odesílatelem", - "status_unsupported": "Nepodporovaný formát S/MIME", - "auto_import_signer_certs": "Automaticky importovat certifikáty podepisujících", - "auto_import_signer_certs_desc": "Automaticky ukládat certifikáty z ověřených podepsaných e-mailů pro budoucí šifrování", - "export": "Exportovat", - "enter_export_passphrase": "Nastavte heslo pro export", - "export_passphrase_desc": "Vyberte heslo pro ochranu exportovaného souboru PKCS#12", - "export_storage_desc": "Zadejte heslo úložiště pro dešifrování klíče k exportu", - "incorrect_passphrase": "Nesprávné heslo" - }, - "tour": { - "step_counter": "Krok {current} z {total}", - "skip": "Přeskočit průvodce", - "back": "Zpět", - "next": "Další", - "finish": "Dokončit", - "take_a_tour": "Prozkoumejte rozhraní v krátkém průvodci", - "restart_title": "Úvodní průvodce", - "restart_desc": "Přehrát průvodce rozhraním krok za krokem", - "restart_button": "Spustit průvodce znovu", - "show_on_new_devices_title": "Zobrazit na nových zařízeních", - "show_on_new_devices_desc": "Přehrát uvítací banner a průvodce při prvním přihlášení na novém zařízení, i když jste je již dokončili jinde", - "sidebar_title": "Vaše poštovní schránky", - "sidebar_desc": "Toto je postranní panel se složkami. Kliknutím na libovolnou schránku zobrazíte její zprávy. Můžete vytvářet složky, přetahovat zprávy mezi nimi a okamžitě vidět počet nepřečtených e-mailů.", - "compose_title": "Napsat zprávu", - "compose_desc": "Kliknutím sem napíšete novou zprávu. Můžete přidávat příjemce, přílohy a využívat formátovaný text.", - "search_title": "Prohledávat poštu", - "search_desc": "Vyhledávejte podle odesílatele, předmětu nebo obsahu. Kliknutím na ikonu filtru využijete pokročilé možnosti, jako jsou časová období, přílohy a zprávy označené hvězdičkou.", - "email_list_title": "Seznam zpráv", - "email_list_desc": "Zde se zobrazují zprávy. Kliknutím na jednu z nich si ji přečtete napravo. Pomocí zaškrtávacích políček můžete vybrat více zpráv a hromadně je přesunout, odstranit nebo označit štítkem.", - "email_viewer_title": "Panel čtení", - "email_viewer_desc": "Zde se otevírá vybraná zpráva. Pomocí tlačítek na panelu nástrojů můžete odpovědět, přeposlat ji, archivovat nebo odstranit. Můžete také označovat zprávy hvězdičkou nebo přidávat barevné štítky.", - "keywords_title": "Barevné štítky", - "keywords_desc": "Organizujte poštu pomocí barevných štítků. Přetažením zprávy na štítek ji označíte, nebo klikněte na zprávu pravým tlačítkem a štítky přiřaďte.", - "calendar_title": "Kalendář", - "calendar_desc": "Přepněte se do kalendáře pro správu událostí. Můžete vytvářet události, nastavovat připomenutí a přepínat mezi zobrazením dne, týdne nebo měsíce.", - "contacts_title": "Kontakty", - "contacts_desc": "Zde se nachází váš adresář. Importujte kontakty, vytvářejte skupiny a kliknutím na libovolný kontakt zobrazíte jeho úplné údaje.", - "settings_title": "Nastavení", - "settings_desc": "Přizpůsobte si vše: motiv, hustotu zobrazení, podpisy, filtry, klávesové zkratky, výchozí nastavení kalendáře a mnoho dalšího.", - "shortcuts_title": "Klávesové zkratky", - "shortcuts_desc": "Oblíbená funkce pokročilých uživatelů. Kdykoli stiskněte ? pro zobrazení všech dostupných zkratek. Můžete procházet aplikací, psát a spravovat zprávy bez použití myši.", - "compose_open_title": "Editor zpráv", - "compose_open_desc": "Toto je editor zpráv. Přidávejte příjemce, pište obsah, připojujte soubory a formátujte text. Můžete také ukládat koncepty a používat šablony.", - "calendar_view_title": "Váš kalendář", - "calendar_view_desc": "Zde vidíte kalendář s ukázkovými událostmi. Pomocí panelu nástrojů můžete přepínat zobrazení dne, týdne, měsíce a agendy.", - "create_event_title": "Vytvořit událost", - "create_event_desc": "Kliknutím na toto tlačítko vytvoříte novou událost v kalendáři. Můžete nastavit název, datum, čas a přidat účastníky.", - "event_modal_title": "Podrobnosti události", - "event_modal_desc": "Toto je formulář události. Vyplňte název, vyberte datum a čas, přidejte místo konání nebo účastníky. Až budete hotovi, klikněte na uložit - nebo okno zavřete a pokračujte dále.", - "contacts_list_title": "Vaše kontakty", - "contacts_list_desc": "Zde jsou vaše kontakty. Kliknutím na libovolný kontakt zobrazíte jeho úplné údaje na pravé straně. Můžete také vytvářet nové kontakty, importovat soubory vCard a organizovat kontakty do skupin.", - "settings_tabs_title": "Menu nastavení", - "settings_tabs_desc": "Zde najdete všechny kategorie nastavení. Upravte vzhled, spravujte identity, nastavte e-mailové filtry, předvolby kalendáře a mnoho dalšího.", - "files_title": "Úložiště souborů", - "files_desc": "Prohlížeč souborů umožňuje nahrávat, organizovat a sdílet soubory - jako váš osobní cloudový disk zabudovaný přímo v e-mailu.", - "demo_banner_title": "Ovládání demoverze", - "demo_banner_desc": "Nacházíte se v demo režimu - vše zůstává ve vašem prohlížeči. Kdykoli klikněte na „Resetovat“, abyste začali znovu s čistými ukázkovými daty.", - "quota_title": "Využití úložiště", - "quota_desc": "Zde můžete sledovat velikost své poštovní schránky. Kruh se plní podle využití kapacity." - }, - "unified_mailbox": { - "search_unavailable": "Vyhledávání není ve sjednoceném zobrazení k dispozici" - }, - "calendar_event_notifications": { - "someone": "Někdo", - "invited": "{name} vás pozval(a) na „{title}“", - "updated": "{name} aktualizoval(a) „{title}“", - "cancelled": "{name} zrušil(a) „{title}“", - "untitled": "Událost bez názvu" - }, - "share_notifications": { - "someone": "Někdo", - "shared": "{name} s vámi sdílí {kind} „{object}“", - "changed": "{name} změnil(a) váš přístup k {kind} „{object}“", - "revoked": "{name} odebral(a) váš přístup k {kind} „{object}“", - "object": { - "folder": "složku", - "calendar": "kalendář", - "address_book": "adresář", - "files": "složku souborů" - } - }, - "sharing": { - "title": "Sdílet „{name}\"", - "description": "Udělte přístup dalším uživatelům nebo skupinám na tomto serveru. Změny se projeví okamžitě.", - "no_shares": "Zatím nikomu nesdíleno.", - "add_person": "Přidat osobu nebo skupinu", - "search_placeholder": "Hledat podle jména nebo e-mailu…", - "loading_principals": "Načítání uživatelů…", - "no_principals": "Nenalezeni žádní další uživatelé ani skupiny.", - "no_match": "Žádné výsledky.", - "remove": "Odebrat přístup", - "group": "Skupina", - "share_added": "Přístup udělen", - "share_updated": "Přístup aktualizován", - "share_removed": "Přístup odebrán", - "share_failed": "Aktualizace sdílení selhala", - "preset": { - "freeBusy": "Pouze volno/zaneprázdněno", - "read": "Pouze čtení", - "readWrite": "Čtení a zápis", - "manager": "Správce", - "custom": "Vlastní" - } - }, - "quote_header": { - "reply_line": "Dne {date} napsal(a) {from}:", - "forwarded_separator": "---------- Přeposlaná zpráva ----------", - "from_label": "Od", - "date_label": "Datum", - "subject_label": "Předmět" - }, - "pwa_install": { - "title": "Nainstalovat {appName}", - "description": "Nainstalujte si naši aplikaci pro rychlý přístup a offline podporu.", - "not_now": "Teď ne", - "install": "Nainstalovat", - "dont_remind": "Už mi to nepřipomínat", - "dismiss_aria": "Zavřít výzvu k instalaci" - }, - "deep_link": { - "message_not_found": "Tato zpráva už není dostupná.", - "thread_not_found": "Tato konverzace už není dostupná.", - "folder_not_found": "Tato složka už není dostupná.", - "event_not_found": "Tato událost už není dostupná.", - "file_not_found": "Tento soubor už není dostupný.", - "account_unavailable": "Přihlaste se k tomuto účtu, abyste odkaz otevřeli.", - "copy_message": "Kopírovat odkaz na zprávu", - "copy_conversation": "Kopírovat odkaz na konverzaci", - "copy_event": "Kopírovat odkaz na událost", - "copied": "Odkaz zkopírován", - "copy_failed": "Odkaz se nepodařilo zkopírovat" - }, - "plugins": { - "oauth_callback": { - "completing": "Dokončování autorizace…", - "completing_message": "Počkejte prosím, než se naváže spojení.", - "received_title": "Autorizace přijata", - "received_message": "Můžete se vrátit na kartu, kde jste zahájili připojení.", - "failed_title": "Autorizace se nezdařila", - "denied": "Autorizace byla zamítnuta.", - "failed_detail": "Autorizace se nezdařila: {error}", - "missing_params": "Chybí autorizační kód nebo stav.", - "delivery_failed": "Výsledek autorizace se nepodařilo předat kartě aplikace. Zavřete toto okno a zkuste to znovu." - } - }, - "global_search": { - "title": "Hledat ve všem", - "placeholder": "Hledat v poště, kontaktech, kalendáři, souborech...", - "scope_label": "Hledat v", - "account_label": "Účet", - "scope_all": "Vše", - "scope_mail": "Pošta", - "scope_contacts": "Kontakty", - "scope_calendar": "Kalendář", - "scope_files": "Soubory", - "all_accounts": "Všechny účty", - "show_all": "Zobrazit všechny výsledky", - "no_results": "Žádné výsledky pro „{query}“", - "server_hint": "Celá slova prohledávají server; části slov odpovídají již načteným položkám", - "searching": "Hledání...", - "account_error": "Nepodařilo se prohledat {account}", - "recent_searches": "Nedávná hledání", - "recurring": "Opakovaná", - "no_subject": "(no subject)", - "empty_hint": "Hledejte poštu, kontakty, události a soubory ve všech účtech. Zkuste from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Ploše podle data", - "grouped": "Seskupené", - "load_more": "Načíst další", - "preview_empty": "Vyberte výsledek pro náhled", - "open_full": "Otevřít", - "view_label": "Zobrazení" - } -} +{ + "meta_description": "Minimalistický webmailový klient využívající protokol JMAP", + "login": { + "title": "Webmail", + "username_label": "E-mail", + "username_placeholder": "uzivatel@example.com", + "password_label": "Heslo", + "password_placeholder": "Zadejte heslo", + "jmap_endpoint_label": "JMAP server", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Server musí povolovat požadavky CORS z této domény.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Server vybrán podle domény vašeho e-mailu.", + "sign_in": "Přihlásit se", + "signing_in": "Přihlašování...", + "loading": "Načítání...", + "reconnecting": "Spojení ztraceno. Pokus o opětovné připojení…", + "error": { + "invalid_credentials": "Neplatná e-mailová adresa nebo heslo. Zkontrolujte přihlašovací údaje a zkuste to znovu.", + "invalid_token": "Server tento přístupový token nepřijal. Zkontrolujte, zda je úplný a zda nevypršel nebo nebyl odvolán.", + "connection_failed": "Nelze se připojit k serveru. Zkontrolujte připojení k internetu a zkuste to znovu.", + "cors_blocked": "Server je dostupný, ale blokuje požadavky mezi doménami. Zkontrolujte nastavení CORS serveru JMAP a povolte tuto doménu.", + "server_error": "Server je dočasně nedostupný. Zkuste to prosím později.", + "generic": "Došlo k neočekávané chybě. Pokud problém přetrvává, kontaktujte správce.", + "totp_required": "Je vyžadován kód dvoufázového ověření. Zadejte kód níže.", + "totp_invalid": "Neplatný ověřovací kód. Zkontrolujte svou ověřovací aplikaci a zkuste to znovu.", + "oauth_discovery_failed": "SSO je povoleno, ale nelze se připojit k poskytovateli identity. Zkontrolujte konfiguraci OAuth.", + "token_exchange_failed": "Heslo a kód byly přijaty, ale poštovní server odmítl pro tuto aplikaci zahájit relaci. Požádejte správce o kontrolu nastavení klienta OAuth." + }, + "show_password": "Zobrazit heslo", + "hide_password": "Skrýt heslo", + "totp_toggle": "Mám 2FA kód", + "token_toggle": "Přihlásit se přístupovým tokenem", + "password_toggle": "Přihlásit se heslem", + "token_label": "Přístupový token", + "token_placeholder": "Vložte svůj API token", + "token_hint": "Vytvořte API token v nastavení svého poskytovatele e-mailu a vložte ho sem.", + "remember_me": "Zapamatovat si mě", + "config_error": { + "title": "Chyba konfigurace", + "fetch_failed": "Konfiguraci aplikace nelze načíst. Zkuste to prosím později.", + "server_not_configured": "Poštovní server nebyl nakonfigurován. Kontaktujte správce." + }, + "remove_from_history": "Odstranit z historie", + "totp_label": "Ověřovací kód", + "totp_placeholder": "000000", + "session_expired": "Platnost relace vypršela. Přihlaste se prosím znovu.", + "dismiss": "Zavřít", + "or": "nebo", + "sign_in_sso": "Přihlásit se přes SSO", + "add_account_title": "Přidat účet", + "add_account_subtitle": "Přihlásit se k jinému účtu", + "cancel": "Zrušit", + "website": "Webové stránky", + "imprint": "Tiráž", + "privacy_policy": "Zásady ochrany osobních údajů", + "try_demo": "Vyzkoušet demoverzi", + "demo_description": "Prozkoumejte aplikaci s ukázkovými daty - bez nutnosti zakládat účet", + "demo_launching": "Spouštění demoverze...", + "demo_login_button": "Spustit demoverzi", + "demo_tagline": "Vyzkoušejte plně funkční e-mailový klient. Účet není vyžadován.", + "demo_no_signup": "Bez registrace - volně prozkoumejte s ukázkovými daty", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Dokončování přihlášení...", + "oauth_error": { + "title": "Ověření selhalo", + "invalid_state": "Bezpečnostní ověření selhalo. Zkuste se přihlásit znovu.", + "missing_params": "Chybí autorizační data. Zkuste se přihlásit znovu.", + "token_exchange_failed": "Ověření se nepodařilo dokončit. Zkuste to znovu.", + "access_denied": "Přístup odepřen. Kontaktujte správce.", + "back_to_login": "Zpět na přihlášení" + } + }, + "sidebar": { + "close": "Zavřít", + "compose": "Napsat", + "compose_hint": "Napsat (c)", + "search_placeholder": "Hledat ve zprávách...", + "search_placeholder_hint": "Hledat ve zprávách... (stiskněte /)", + "storage": "Úložiště", + "storage_used": "Využito", + "storage_free": "Volno", + "storage_total": "Celkem", + "sign_out": "Odhlásit se", + "sign_out_of": "Odhlásit se z {account}", + "sign_out_all": "Odhlásit se ze všech účtů", + "add_account": "Přidat účet", + "set_as_default": "Nastavit jako výchozí", + "switch_account": "Přepnout účet", + "contacts": "Kontakty", + "calendar": "Kalendář", + "settings": "Nastavení", + "admin": "Administrace", + "files": "Soubory", + "loading_mailboxes": "Načítání schránek...", + "push_connected": "Aktualizace v reálném čase aktivní", + "push_disconnected": "Aktualizace v reálném čase neaktivní", + "keyboard_shortcuts": "Klávesové zkratky", + "theme": { + "light": "Světlý režim", + "dark": "Tmavý režim", + "system": "Systémový motiv" + }, + "language": { + "title": "Jazyk" + }, + "mailboxes": { + "inbox": "Doručené", + "sent": "Odeslané", + "drafts": "Koncepty", + "trash": "Koš", + "archive": "Archiv", + "starred": "S hvězdičkou", + "all_mail": "Všechny zprávy", + "spam": "Spam", + "important": "Důležité" + }, + "unified_inbox": "Sjednocená doručená pošta", + "unified_sent": "Všechny odeslané", + "unified_drafts": "Všechny koncepty", + "unified_trash": "Všechny koše", + "unified_archive": "Všechny archivy", + "unified_junk": "Všechen spam", + "all_accounts": "Všechny účty", + "all_accounts_loading": "Načítání účtů: {loaded} z {total}", + "unified_mailbox": "Sjednocená schránka", + "expand": "Rozbalit", + "collapse": "Sbalit", + "expand_tooltip": "Rozbalit", + "collapse_tooltip": "Sbalit", + "mobile": { + "search": "Hledat", + "compose": "Napsat", + "go_back": "Zpět", + "toggle_menu": "Přepnout nabídku" + }, + "clear_search": "Vymazat hledání", + "vacation_active": "Automatická odpověď je aktivní", + "demo_banner": "Demo režim", + "demo_reset": "Resetovat", + "demo_tour": "Průvodce", + "tags": "Štítky", + "show_all_tags": "Zobrazit vše ({count})", + "show_fewer_tags": "Zobrazit méně", + "folders": "Složky", + "your_folders": "Vaše složky", + "create_folder": "Vytvořit složku", + "show_more": "Více", + "show_fewer": "Méně", + "shared": "Sdílené", + "shared_account_hidden": "Skryto z postranního panelu. Znovu zobrazíte v Nastavení › Vzhled.", + "notifications_muted": "Oznámení ztlumena", + "mail": "Pošta", + "nav_label": "Navigace", + "add_app": "Aplikace", + "scheduled": "Naplánováno", + "unified_all_unread": "Vše nepřečtené", + "unified_all_starred": "Vše s hvězdičkou", + "unified_all_mail": "Veškerá pošta", + "remove_account": "Odebrat účet", + "remove_account_confirm": "Odebrat účet {account} z tohoto zařízení? Později ho můžete znovu přidat.", + "labels": "Štítky", + "create_label": "Vytvořit nový štítek", + "mv_tasks": "Úkoly", + "mv_side_panel": "Boční panel", + "mv_open_full": "Otevřít v plném zobrazení", + "mv_previous_day": "Předchozí den", + "mv_next_day": "Následující den", + "mv_add_another_account": "Přidat další účet", + "mv_manage_account": "Spravovat účet", + "mv_storage_used": "Využito {percent} % z {total}" + }, + "protocol_handlers": { + "title": "Výchozí aplikace", + "description": "Zvolte, zda se mají e-mailové a kalendářové odkazy otevírat v Bulwarku. Technicky se Bulwark registruje jako obslužná aplikace protokolu pro odkazy mailto: a webcal:.", + "unsupported": "Tento prohlížeč nebo toto připojení nepodporuje ruční registraci obslužné aplikace protokolu. Nainstalovanou PWA můžete případně použít přes nastavení prohlížeče nebo systému.", + "mailto_label": "E-mailové odkazy", + "mailto_description": "Otevře odkazy mailto: v Bulwarku s předvyplněným editorem zprávy.", + "protocol_open_mode_label": "Při otevírání odkazů protokolů", + "protocol_open_mode_description": "Zvolte, zda má Bulwark otevírat odkazy mailto: a webcal: v nové kartě, nebo znovu použít otevřenou relaci. Volba aktivní relace vyžaduje oprávnění k oznámením, abyste mohli kliknout na záložní oznámení a přenést Bulwark do popředí, pokud prohlížeč blokuje fokus.", + "protocol_open_mode_active_session": "Otevřít v aktivní relaci, pokud je to možné", + "protocol_open_mode_new_tab": "Vždy otevřít novou kartu", + "focus_notification_title": "Otevřít Bulwark", + "focus_notification_body": "Odkaz byl otevřen v Bulwarku. Kliknutím přenesete okno do popředí.", + "webcal_label": "Kalendářové odkazy", + "webcal_description": "Otevře odkazy webcal: v Bulwarku s předvyplněným dialogem pro odběr kalendáře.", + "register_mailto": "Registrovat e-mailovou aplikaci", + "register_webcal": "Registrovat kalendářovou aplikaci", + "mailto_registered": "Registrace obsluhy e-mailových odkazů byla vyžádána", + "webcal_registered": "Registrace obsluhy kalendářových odkazů byla vyžádána", + "registration_failed": "Registrace obslužné aplikace protokolu selhala", + "opening_mailto": "Otevírá se editor...", + "opening_webcal": "Otevírá se kalendář...", + "browser_note": "Prohlížeč nebo operační systém vás může požádat o potvrzení a může vyžadovat, aby byl Bulwark nainstalovaný, než jej půjde vybrat jako výchozí aplikaci.", + "select_account_title": "Vybrat účet", + "select_mailto_account": "Vyberte účet, ve kterém se má tento e-mailový odkaz otevřít.", + "select_webcal_account": "Vyberte účet, ve kterém se má tento kalendářový odkaz otevřít.", + "select_account_note": "Tato volba platí jen pro tento odkaz protokolu.", + "detail_to": "Komu", + "detail_subject": "Předmět", + "detail_no_subject": "Bez předmětu", + "detail_calendar": "Kalendář", + "detail_source": "Zdroj", + "active_account": "Aktivní", + "switching_account": "Přepínání účtu..." + }, + "sidebar_apps": { + "modal_title": "Aplikace postranního panelu", + "add_new": "Přidat aplikaci", + "edit_app": "Upravit aplikaci", + "name_label": "Název", + "name_placeholder": "Moje aplikace", + "name_required": "Název je vyžadován", + "url_label": "URL", + "url_required": "URL je vyžadováno", + "url_invalid": "Zadejte platnou adresu http nebo https", + "icon_label": "Ikona", + "icon_required": "Ikona je vyžadována", + "open_mode_label": "Režim otevírání", + "open_new_tab": "Nová karta", + "open_inline": "Vložené", + "cancel": "Zrušit", + "add": "Přidat", + "update": "Aktualizovat", + "delete": "Odstranit", + "delete_confirm_title": "Odstranit aplikaci", + "delete_confirm": "Opravdu chcete odstranit \"{name}\"?", + "no_apps": "Zatím nebyly přidány žádné aplikace", + "no_apps_hint": "Přidejte vlastní aplikace a odkazy do postranního panelu", + "managed_title": "Poskytnuto správcem", + "managed_description": "Tyto aplikace jsou nastaveny pro všechny a nelze je změnit.", + "managed_badge": "Spravováno", + "search_icons": "Hledat ikony...", + "show_popular": "Oblíbené", + "show_all": "Všechny", + "no_icons_found": "Ikony nenalezeny", + "inline_badge": "Vložené", + "tab_badge": "Karta", + "show_on_mobile": "Zobrazit na mobilu" + }, + "email_list": { + "columns": { + "from": "Od", + "to": "Komu", + "subject": "Předmět", + "date": "Datum", + "size": "Velikost" + }, + "no_emails": "Nenalezeny žádné zprávy", + "no_emails_description": "Tato schránka je prázdná", + "no_search_results": "Nenalezeny žádné výsledky", + "no_search_results_description": "Zkuste změnit hledání nebo filtry", + "loading": "Načítání zpráv...", + "unread": "nepřečtené", + "to_me": "Pro mě", + "to_recipients": "Pro {count} příjemců", + "and_others": "a {count} dalších", + "draft": "Koncept", + "starred": "S hvězdičkou", + "pinned": "Připnuto", + "replied": "Zodpovězeno", + "forwarded": "Přeposláno", + "has_attachment": "Obsahuje přílohu", + "conversations_range": "{from}–{to, number} z {total, number}", + "conversations_count": "{count} z {total} konverzací", + "conversations_count_plus": "Více než {count} konverzací", + "conversations_count_simple": "{count} konverzací", + "no_conversations": "Žádné konverzace", + "loading_more": "Načítání dalších zpráv...", + "no_more_emails": "Žádné další zprávy k načtení", + "batch_actions": { + "select": "Vybrat zprávy", + "select_all": "Vybrat vše", + "selected_messages": "{count, plural, one {Vybraný 1 e-mail} other {Vybrané # e-maily}}", + "mark_read": "Označit jako přečtené", + "mark_unread": "Označit jako nepřečtené", + "delete": "Odstranit", + "delete_confirm_title": "Odstranit zprávy", + "delete_confirm_message": "Opravdu chcete odstranit {count, plural, one {1 zprávu} few {# zprávy} other {# zpráv}}?", + "clear_selection": "Zrušit výběr" + }, + "permanent_delete": "Trvale odstranit", + "permanent_delete_confirm_title": "Trvalé odstranění", + "permanent_delete_confirm_message": "Tato zpráva bude trvale odstraněna. Tuto akci nelze vrátit zpět.", + "permanent_delete_confirm_batch_message": "{count, plural, one {1 zpráva bude trvale odstraněna} few {# zprávy budou trvale odstraněny} other {# zpráv bude trvale odstraněno}}. Tuto akci nelze vrátit zpět.", + "empty_folder": { + "button": "Vyprázdnit složku", + "confirm_title": "Vyprázdnit složku", + "confirm_message": "Všechny zprávy v této složce budou trvale odstraněny. Tuto akci nelze vrátit zpět.", + "confirm_message_trash": "Všechny e-maily v této složce budou přesunuty do koše.", + "confirm_button": "Vyprázdnit složku", + "junk_hint": "Můžete vyprázdnit složku Spam a trvale odstranit všechny zprávy.", + "trash_hint": "Můžete vyprázdnit Koš a trvale odstranit všechny zprávy." + }, + "no_scheduled_emails": "Žádné naplánované e-maily", + "no_scheduled_emails_description": "Zprávy naplánované na později se zobrazí zde.", + "scheduled_count": "{count, plural, one {1 naplánovaný e-mail} few {# naplánované e-maily} other {# naplánovaných e-mailů}}", + "scheduled_actions_hint": "Naplánované zprávy můžete zrušit, přeplánovat nebo upravit pomocí akcí plánování.", + "cancel_scheduled_send": "Zrušit odeslání", + "reschedule_send": "Přeplánovat", + "cancel_and_edit": "Zrušit a upravit", + "cancel_and_compose_again": "Zrušit a napsat znovu", + "reschedule_prompt": "Zadejte nové datum/čas, např. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Odesílatel žádá o potvrzení o přečtení:", + "send": "Odeslat potvrzení", + "ignore": "Ignorovat", + "sent": "Potvrzení o přečtení odesláno.", + "send_failed": "Potvrzení o přečtení se nepodařilo odeslat", + "mdn_subject": "Přečteno: {subject}", + "mdn_body": "Toto je potvrzení o přečtení zprávy, kterou jste odeslali na adresu {recipient}.\n\nPoznámka: Toto potvrzení pouze potvrzuje, že zpráva byla zobrazena na počítači příjemce. Nezaručuje, že příjemce obsah přečetl nebo mu porozuměl." + }, + "no_email_selected": "Není vybrána žádná zpráva", + "no_email_description": "Vyberte zprávu ze seznamu pro její zobrazení", + "no_conversation_selected": "Není vybrána žádná konverzace", + "no_conversation_description": "Vyberte konverzaci ze seznamu pro její zobrazení", + "compose": "Napsat", + "compose_hint": "Napsat novou zprávu", + "no_subject": "(Bez předmětu)", + "no_body_content": "(Žádný obsah těla)", + "no_preview_available": "Náhled není k dispozici", + "show_quoted_text": "Zobrazit citovaný text", + "hide_quoted_text": "Skrýt citovaný text", + "loading_email": "Načítání zprávy...", + "loading": "Načítání...", + "reply": "Odpovědět", + "reply_all": "Odpovědět všem", + "forward": "Přeposlat", + "delete": "Odstranit", + "archive": "Archivovat", + "archive_mailbox_not_found": "Složka Archiv nebyla nalezena – e-mail nelze archivovat", + "star": "Označit hvězdičkou", + "unstar": "Odebrat hvězdičku", + "mark_unread": "Označit jako nepřečtené", + "mark_read": "Označit jako přečtené", + "unread": "Nepřečtené", + "read": "Přečtené", + "spam_short": "Spam", + "not_spam_short": "Není spam", + "move": "Přesunout", + "print": "Tisk", + "view_source": "Zobrazit zdrojový kód", + "fullscreen": "Celá obrazovka", + "exit_fullscreen": "Ukončit režim celé obrazovky", + "view_in_dark_mode": "Zobrazit v tmavém režimu", + "view_in_light_mode": "Zobrazit v světlém režimu", + "export_email": "Exportovat jako .eml", + "forward_as_attachment": "Přeposlat jako přílohu", + "import_email": "Importovat .eml nebo .zip", + "keyboard_shortcuts": "Klávesové zkratky (?)", + "email_source": "Zdrojový kód zprávy", + "draft_banner": "Tato zpráva je koncept", + "edit_draft": "Upravit", + "copy_source": "Kopírovat do schránky", + "source_copied": "Zdrojový kód byl zkopírován do schránky", + "attachments": "Přílohy", + "important": "Důležité", + "download": "Stáhnout", + "download_all": "Stáhnout vše", + "from": "Od", + "to": "Komu", + "cc": "Kopie", + "bcc": "Skrytá kopie", + "date": "Datum", + "subject": "Předmět", + "show_details": "Zobrazit podrobnosti", + "hide_details": "Skrýt podrobnosti", + "external_content_warning": "Obrázky a externí obsah byly zablokovány", + "load_external_content": "Načíst obrázky", + "trust_sender": "Vždy důvěřovat tomuto odesílateli", + "sender_check": { + "unverified_label": "Neověřený odesílatel", + "failed_label": "Kontrola odesílatele selhala", + "unverified": "Tato zpráva tvrdí, že pochází z domény {domain}, ale nic to nepotvrzuje. Nemá platný podpis domény {domain} a nepřišla ze serveru, který doména {domain} povoluje.", + "unverified_sent_from": "Tato zpráva tvrdí, že pochází z domény {domain}, ale byla odeslána z domény {host} a nemá platný podpis domény {domain}.", + "failed": "Tato zpráva tvrdí, že pochází z domény {domain}, ale neprošla kontrolami odesílatele této domény. Může být podvržená.", + "failed_sent_from": "Tato zpráva tvrdí, že pochází z domény {domain}, ale byla odeslána z domény {host} a neprošla kontrolami odesílatele domény {domain}. Může být podvržená.", + "caution": "Buďte opatrní u odkazů, příloh a žádostí o hesla nebo platební údaje." + }, + "back_to_list": "Zpět na seznam", + "view_contact": "Zobrazit kontakt", + "message_details": "Podrobnosti zprávy", + "more_reply_options": "Další možnosti odpovědi", + "set_tag": "Nastavit štítek", + "tag": "Štítek", + "more_actions": "Další akce", + "previous": "Předchozí", + "next": "Další", + "move_to": "Přesunout do...", + "remove_tag": "Odebrat štítek", + "tag_filter_placeholder": "Filtrovat štítky", + "tag_no_matches": "Žádné odpovídající štítky", + "more_count": "+{count} dalších", + "characters_count": "{count} znaků", + "quick_reply_placeholder": "Napsat rychlou odpověď...", + "more_options": "Další možnosti", + "sending": "Odesílání...", + "security_authentication": "Zabezpečení a ověřování", + "technical_details": "Technické podrobnosti", + "message_id_label": "Message-ID:", + "reply_to_label": "Reply-To:", + "delivery_time_label": "Čas doručení:", + "conversation_part_label": "Část konverzace:", + "previous_messages": "{count} předchozí zpráva", + "previous_messages_plural": "{count} předchozích zpráv", + "time": { + "day": "den", + "days": "dní", + "hour": "hodina", + "hours": "hodin", + "minute": "minuta", + "minutes": "minut" + }, + "unknown_sender": "Neznámý", + "recipient_me": "já", + "recipient_and_others": "{name} a {count} dalších", + "recipient_to_prefix": "Komu:", + "authentication": { + "title": "Ověřování", + "status": { + "verified": "Ověřeno", + "warning": "Upozornění", + "none": "Neověřeno" + }, + "spf": { + "pass": "SPF úspěšné", + "fail": "SPF selhalo", + "none": "Bez SPF" + }, + "dkim": { + "pass": "DKIM platný", + "fail": "DKIM neplatný", + "none": "Bez DKIM" + }, + "dmarc": { + "pass": "DMARC úspěšné", + "fail": "DMARC selhalo", + "none": "Bez DMARC" + }, + "spam_score": "Skóre spamu", + "tooltip_spf": "Sender Policy Framework: ověřuje, zda je odesílající server oprávněn odesílat e-maily jménem domény", + "tooltip_dkim": "DomainKeys Identified Mail: kryptografickým podpisem potvrzuje, že e-mail nebyl při přenosu změněn", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zajišťuje shodu SPF a DKIM s doménou odesílatele a stanoví zásadu pro selhání", + "policy": "Zásada", + "result": { + "pass": "Úspěšné", + "fail": "Selhalo", + "softfail": "Mírné selhání", + "neutral": "Neutrální", + "permerror": "Trvalá chyba", + "temperror": "Dočasná chyba", + "none": "Žádné" + } + }, + "headers": { + "routing": "Směrování", + "received": "Přijato", + "message_id": "ID zprávy", + "list_info": "Informace o konferenci" + }, + "tooltips": { + "reply": "Odpovědět (r)", + "reply_all": "Odpovědět všem (a)", + "forward": "Přeposlat (f)", + "archive": "Archivovat (e)", + "delete": "Odstranit (# nebo Del)", + "star": "Označit hvězdičkou (s)", + "unstar": "Odebrat hvězdičku (s)", + "compose": "Napsat (c)", + "previous": "Předchozí zpráva", + "next": "Další zpráva", + "edit_draft": "Upravit koncept" + }, + "spam": { + "button_title": "Nahlásit spam", + "not_spam_title": "Označit jako bezpečné", + "toast_success": "Přesunuto do Spamu", + "toast_batch": "{count} zpráv přesunuto do Spamu", + "toast_undo": "Zpět", + "toast_not_spam_success": "Přesunuto do Doručených", + "toast_not_spam_batch": "{count} zpráv přesunuto do Doručených", + "error": "Nahlášení spamu selhalo", + "error_not_spam": "Obnovení zprávy selhalo" + }, + "unsubscribe_banner": { + "label": "Newsletter", + "button": "Odhlásit odběr", + "confirm_title": "Odhlásit odběr od tohoto odesílatele?", + "confirm_button": "Potvrdit", + "cancel": "Zrušit", + "success_http": "Stránka pro odhlášení byla otevřena na nové kartě", + "success_mailto": "Odhlašovací e-mail byl odeslán", + "confirm_message_http": "Odhlašovací stránka se otevře na nové kartě.", + "confirm_message_mailto": "Odesílateli bude zaslán odhlašovací e-mail.", + "error": "Odběr nelze odhlásit", + "dismiss": "Zavřít" + }, + "calendar_invitation": { + "loading": "Načítání podrobností události…", + "title": "Pozvánka kalendáře", + "published_title": "Publikovaná událost", + "response_title": "Odpověď na událost", + "update_title": "Aktualizace události", + "counter_title": "Návrh na změnu", + "refresh_title": "Požadavek na obnovení", + "declined_counter_title": "Návrh na změnu zamítnut", + "cancelled_title": "Událost zrušena", + "organizer": "Organizátor: {name}", + "organizer_label": "Organizátor:", + "attendees": "{count, plural, one {1 účastník} few {# účastníci} other {# účastníků}}", + "accept": "Přijmout", + "maybe": "Možná", + "decline": "Odmítnout", + "add_to_calendar": "Přidat do kalendáře", + "added": "Přidáno do kalendáře", + "rsvp_sent": "Odpověď odeslána", + "parse_error": "Nelze přečíst pozvánku", + "action_failed": "Akce kalendáře selhala.", + "no_calendar": "Kalendář není dostupný", + "published_info": "Tato událost byla sdílena pro vaši informaci.", + "response_info": "Tato zpráva obsahuje odpověď účastníka.", + "response_info_organizer": "Tato odpověď účastníka aktualizuje vaši událost.", + "update_info": "Tato zpráva aktualizuje existující událost.", + "counter_info": "Tato zpráva navrhuje změny události.", + "counter_info_organizer": "Tento účastník navrhl změny vaší události.", + "refresh_info": "Tato zpráva vyžaduje nejnovější podrobnosti o události.", + "refresh_info_organizer": "Účastník požádal o nejnovější podrobnosti o události.", + "declined_counter_info": "Organizátor odmítl návrh na změnu.", + "authentication_failed_info": "Ověřování zpráv u této pozvánky selhalo. S akcemi kalendáře zacházejte opatrně.", + "authentication_missing_info": "Tato pozvánka neobsahuje ověření zprávy. Pokud se vám cokoli zdá neobvyklé, potvrďte si podrobnosti s organizátorem.", + "sender_mismatch_info": "Tato pozvánka byla odeslána od {sender}, zatímco organizátor v datech kalendáře je {organizer}.", + "sender_mismatch_unverified_info": "Tato pozvánka byla odeslána od {sender}, zatímco organizátor v datech kalendáře je {organizer}, a zprávu se nepodařilo ověřit.", + "organizer_role": "Jste organizátorem této události", + "your_response": "Vaše odpověď: {status}", + "response_needed": "Vyžaduje odpověď", + "response_accepted": "Přijato", + "response_tentative": "Nezávazně", + "response_declined": "Odmítnuto", + "response_delegated": "Delegováno", + "actor_sent_info": "Odeslal(a) {name}.", + "actor_response_info": "{name} odpověděl(a) {status}.", + "actor_counter_info": "{name} navrhl(a) změny pro tuto událost.", + "actor_refresh_info": "{name} požádal(a) o nejnovější podrobnosti o události.", + "actor_declined_counter_info": "{name} odmítl(a) návrh na změnu.", + "actor_note": "Poznámka: {comment}", + "actor_unknown": "Někdo", + "proposed_changes": "Navrhované změny", + "change_title": "Název", + "change_time": "Čas", + "change_location": "Místo", + "change_description": "Popis", + "change_empty": "Žádné", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Použít navrhované změny", + "proposal_applied": "Navrhované změny byly použity.", + "review_proposal": "Zkontrolovat návrh", + "review_request": "Zkontrolovat požadavek", + "view_in_calendar": "Zobrazit v kalendáři", + "select_calendar": "Vybrat kalendář", + "already_in_calendar": "Již je ve vašem kalendáři", + "request_info": "Byli jste pozváni na tuto událost. Odpovězte, abyste informovali organizátora o své dostupnosti.", + "cancel_info": "Organizátor tuto událost zrušil.", + "event_updated": "Aktualizace #{sequence}", + "event_status_tentative": "Nezávazně", + "event_status_cancelled": "Zrušeno", + "expand": "Zobrazit podrobnosti", + "collapse": "Skrýt podrobnosti" + }, + "send": "Odeslat", + "more": "více", + "details": { + "recipients_routing": "Příjemci a směrování", + "authentication_security": "Ověřování a zabezpečení", + "identifiers_threading": "Identifikátory a vlákna", + "mailing_list": "Diskusní skupina", + "message_properties": "Vlastnosti zprávy", + "sent": "Odesláno", + "received": "Přijato", + "delivery_time": "Doba doručení", + "in_reply_to": "V odpovědi na", + "references": "Odkazy", + "thread_id": "ID vlákna", + "size": "Velikost", + "mime_type": "Typ MIME", + "attachments_summary": "{count} souborů · {size}", + "list_id": "ID seznamu", + "list_help": "Nápověda seznamu", + "list_post": "Příspěvek do seznamu", + "list_unsubscribe": "Odhlásit se", + "iprev": "Reverzní DNS", + "spam_status": "Stav spamu", + "ai_verdict": "Verdikt AI", + "account": "Účet", + "no_subject": "(bez předmětu)" + }, + "scheduled_banner": "Naplánováno k odeslání {date}", + "scheduled_send_created": "E-mail byl naplánován k odeslání", + "sending_in": "Odeslání za {seconds} s", + "undo_send_failed": "Odeslání nelze vrátit. Zpráva už možná byla odeslána.", + "cancel_scheduled_send": "Zrušit odeslání", + "reschedule_send": "Přeplánovat", + "cancel_and_edit": "Zrušit a upravit", + "cancel_and_compose_again": "Zrušit a napsat znovu", + "reschedule_prompt": "Zadejte nové datum/čas, např. 2026-05-04T15:30", + "scheduled_actions_only": "Zobrazení Naplánováno podporuje pouze akce plánování", + "undo_send_scheduled": "Zpráva je naplánována k odeslání", + "undo_send": "Vrátit odeslání", + "contact_sidebar": { + "title": "Kontakt", + "close": "Zavřít postranní panel", + "action_email": "E-mail", + "action_email_title": "Odeslat e-mail", + "action_copy": "Kopírovat", + "action_copy_title": "Kopírovat e-mail", + "action_edit_title": "Upravit kontakt", + "section_emails": "E-maily", + "section_phones": "Telefony", + "section_organizations": "Organizace", + "section_addresses": "Adresy", + "section_notes": "Poznámky", + "not_in_contacts": "Není ve vašich kontaktech", + "add_to_contacts": "Přidat do kontaktů", + "copied": "Zkopírováno!", + "copy_failed": "Kopírování se nezdařilo", + "view_contact": "Zobrazit kontakt", + "view_details": "Zobrazit podrobnosti" + }, + "send_now": "Odeslat nyní", + "verification_code": { + "copy": "Kopírovat kód {code}", + "copied": "Kód zkopírován", + "copy_failed": "Kód se nepodařilo zkopírovat" + }, + "mv_position": "{index} z {total}", + "plugin_panel_show": "Zobrazit panel pluginu", + "plugin_panel_hide": "Skrýt panel pluginu" + }, + "email_composer": { + "select_from_directory": "Vybrat z adresáře", + "minimize": "Minimalizovat", + "maximize": "Celá obrazovka", + "restore": "Obnovit", + "new_message": "Nová zpráva", + "signature_edit_hint": "Dvojklikem upravíte podpis", + "reply": "Odpovědět", + "reply_all": "Odpovědět všem", + "forward": "Přeposlat", + "reply_to": "Odpovědět", + "reply_all_to": "Odpovědět všem", + "forward_message": "Přeposlat", + "from": "Od", + "to": "Komu", + "cc": "Kopie", + "bcc": "Skrytá kopie", + "subject": "Předmět", + "body_placeholder": "Napište zprávu...", + "send": "Odeslat", + "cancel": "Zrušit", + "attach": "Připojit", + "attach_photos": "Fotky a videa", + "attach_files": "Soubory", + "attach_from_files": "Připojit ze Souborů", + "files_picker": { + "attach": "{count, plural, =0 {Připojit} one {Připojit 1 soubor} few {Připojit # soubory} other {Připojit # souborů}}", + "empty_folder": "Tato složka je prázdná", + "load_failed": "Vaše soubory se nepodařilo načíst" + }, + "discard": "Zahodit", + "discard_draft_title": "Zahodit koncept?", + "discard_draft_confirm": "Máte neuložené změny. Chcete tento koncept zahodit?", + "discard_draft_message": "Koncept bude smazán.", + "saving": "Ukládání...", + "draft_saved": "Koncept byl uložen", + "save_failed": "Uložení selhalo", + "to_placeholder": "E-mailové adresy příjemců", + "cc_placeholder": "Příjemci kopie", + "bcc_placeholder": "Příjemci skryté kopie", + "subject_placeholder": "Předmět", + "cc_label": "Kopie:", + "bcc_label": "Skrytá kopie:", + "subject_label": "Předmět:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 zpráva} few {# zprávy} other {# zpráv}}", + "no_subject": "(Bez předmětu)", + "unknown_sender": "Neznámý", + "quote": { + "reply_header": "Dne {date}, {sender} napsal(a):", + "forward_header": "---------- Přeposlaná zpráva ----------", + "from": "Od: {sender}", + "date": "Datum: {date}", + "subject": "Předmět: {subject}", + "to": "Komu: {recipients}" + }, + "remove_sub_address": "Odebrat subadresu", + "from_override": { + "toggle_off": "Přepsat", + "toggle_on": "Zrušit přepsání", + "toggle_tooltip": "Volně upravujte jméno a adresu odesílatele. Pošta se stále odesílá přes vaši identitu.", + "name_label": "Jméno odesílatele", + "name_placeholder": "Jméno", + "email_label": "E-mailová adresa odesílatele", + "email_placeholder": "alias@example.com", + "envelope_notice": "Pokud váš server tuto adresu jako odesílatele obálky nepřijme, použije se místo ní {identity} a příjemci ji uvidí v záhlaví Return-Path." + }, + "save_as_template": "Uložit jako šablonu", + "attach_from_device": "Nahrát z tohoto zařízení", + "templates_menu": "Šablony", + "insert_template": "Vložit šablonu", + "options": "Možnosti", + "option_plain_text": "Prostý text", + "option_plain_text_hint": "Odeslat bez formátování", + "option_read_receipt": "Vyžádat potvrzení o přečtení", + "option_read_receipt_hint": "Požádat příjemce o potvrzení otevření", + "option_dsn": "Oznámení o doručení", + "option_dsn_hint": "Dostat zprávu, až ji server doručí", + "option_require_tls": "Vyžadovat šifrované doručení", + "option_require_tls_hint": "Doručit jen přes TLS, jinak vrátit", + "option_priority": "Priorita", + "option_priority_high": "Vysoká", + "option_priority_normal": "Normální", + "option_priority_low": "Nízká", + "validation": { + "recipient_required": "Chcete-li zprávu odeslat, přidejte příjemce", + "body_required": "Napište zprávu nebo připojte soubor", + "invalid_recipients": "Neodesláno - zkontrolujte tyto adresy: {addresses}", + "attachments_uploading": "Přílohy se stále nahrávají – odeslání proběhne po dokončení", + "attachment_upload_failed": "Neodesláno - přílohu se nepodařilo nahrát. Odeberte ji a zkuste to znovu." + }, + "upload_progress": "Nahrávání {uploaded} / {total}", + "upload_cancel": "Zrušit nahrávání", + "upload_failed": "Soubor {filename} se nepodařilo nahrát", + "attachment_too_large": "„{name}“ je větší, než server povoluje ({max} na soubor)", + "attachments_total_too_large": "Přílohy by překročily limit serveru {max} na zprávu", + "drop_files": "Přetáhněte soubory sem a připojte je", + "show_less": "Zobrazit méně", + "send_failed": "Odeslání zprávy selhalo", + "send_timeout": "Server přestal odpovídat. Než odešlete znovu, zkontrolujte složku Odeslané – zpráva už mohla odejít.", + "send_offline": "Zdá se, že jste offline. Zpráva tu zůstává, odešlete ji znovu, až budete online.", + "formatting": "Formátování", + "send_recipients_rejected": "Neodesláno – server odmítl všechny příjemce.", + "send_plugin_failed": "Neodesláno – plugin, který zajišťuje odeslání, selhal nebo trval příliš dlouho. Před opětovným odesláním zkontrolujte složku Odeslané.", + "send_some_recipients_rejected": "Odesláno, ale ne těmto příjemcům – server je odmítl.", + "continue_draft": "Pokračovat v úpravě konceptu", + "close_draft_title": "Uložit nebo zahodit koncept?", + "close_draft_message": "Máte neuložené změny. Chcete je uložit jako koncept, nebo zahodit?", + "smime_sign_on": "Podepisování S/MIME zapnuto", + "smime_sign_off": "Zapnout podepisování S/MIME", + "smime_encrypt_on": "Šifrování S/MIME zapnuto", + "smime_encrypt_off": "Zapnout šifrování S/MIME", + "smime_encrypt_unavailable": "Šifrování S/MIME není dostupné – chybí certifikáty příjemců", + "smime_unlock_title": "Odemknout klíč S/MIME", + "smime_unlock_message": "Zadejte heslo pro odemčení klíče k podpisu S/MIME.", + "smime_unlock_button": "Odemknout", + "smime_passphrase_placeholder": "Heslo", + "forgot_attachment": { + "title": "Nezapomněli jste na přílohu?", + "message": "Vaše zpráva obsahuje slovo \"{keyword}\", ale není k ní připojen žádný soubor. Přesto odeslat?", + "send_anyway": "Přesto odeslat", + "back": "Zpět k úpravám" + }, + "empty_subject": { + "title": "Odeslat bez předmětu?", + "message": "Tato zpráva nemá předmět. Přesto odeslat?", + "dont_ask_again": "Znovu se neptat", + "send_anyway": "Přesto odeslat", + "back": "Zpět k úpravám" + }, + "add_link": "Přidat odkaz", + "link_url_prompt": "Zadejte URL", + "sending": "Odesílání...", + "schedule_send": "Naplánovat odeslání", + "schedule_send_description": "Vyberte, kdy má server tuto zprávu odeslat.", + "schedule_preset": { + "tomorrow_morning": "Zítra ráno", + "tomorrow_afternoon": "Zítra odpoledne", + "monday_morning": "V pondělí ráno" + }, + "schedule_send_required": "Vyberte datum a čas.", + "schedule_send_invalid": "Zadejte platné datum a čas.", + "schedule_send_future": "Vyberte budoucí datum a čas.", + "schedule_send_too_late": "Tento čas je později, než server dovoluje.", + "schedule_send_unsupported": "Naplánované odeslání není pro tento účet podporováno.", + "schedule_send_cleanup_warning": "Naplánované odeslání bylo vytvořeno, ale vyčištění konceptu selhalo.", + "send_delay_unsupported": "Prodleva odeslání není pro tento účet podporována.", + "send_delay_unsupported_confirm": "Tento účet nepodporuje prodlevu odeslání. Odeslat ihned?", + "recipient_edit_email": "Upravit e-mailovou adresu", + "recipient_edit_name": "Upravit zobrazované jméno", + "recipient_email_placeholder": "E-mailová adresa", + "recipient_name_placeholder": "Zobrazované jméno", + "invalid_recipient": "Neplatná e-mailová adresa", + "autocomplete_search_server": "Hledat na serveru", + "autocomplete_searching": "Hledání...", + "mention_recipients": "Příjemci", + "mention_card_not_recipient": "Není v Komu ani v Kopii, takže tuto zprávu nedostane.", + "mention_card_add": "Přidat do {field}", + "mention_card_remove": "Odebrat zmínku", + "toolbar": { + "bold": "Tučné", + "italic": "Kurzíva", + "underline": "Podtržené", + "strikethrough": "Přeškrtnuté", + "text_color": "Barva textu", + "remove_color": "Odebrat barvu", + "heading_1": "Nadpis 1", + "heading_2": "Nadpis 2", + "bullet_list": "Odrážkový seznam", + "ordered_list": "Číslovaný seznam", + "quote": "Citace", + "code_block": "Blok kódu", + "align_left": "Zarovnat vlevo", + "align_center": "Na střed", + "align_right": "Zarovnat vpravo", + "text_direction": "Směr textu (RTL/LTR)", + "link": "Odkaz", + "table": "Tabulka", + "clear_formatting": "Vymazat formátování", + "undo": "Zpět", + "redo": "Znovu", + "add_row_above": "Přidat řádek nad", + "add_row_below": "Přidat řádek pod", + "add_column_before": "Přidat sloupec před", + "add_column_after": "Přidat sloupec za", + "delete_row": "Odstranit řádek", + "delete_column": "Odstranit sloupec", + "toggle_header_row": "Přepnout řádek záhlaví", + "delete_table": "Odstranit tabulku", + "pick_size": "Vybrat velikost", + "font_size": "Velikost písma", + "font_size_default": "Výchozí", + "background_color": "Barva pozadí", + "remove_background_color": "Odebrat barvu pozadí" + }, + "send_filing_warning": "Odesláno - ale následný úklid selhal, může zůstat zastaralý koncept.", + "pop_out": "Otevřít odpověď v okně", + "send_guard_failed": "Neodesláno - plugin, který kontroluje zprávy před odesláním, selhal nebo neodpověděl." + }, + "confirm_dialog": { + "confirm": "Potvrdit", + "cancel": "Zrušit" + }, + "common": { + "loading": "Načítání...", + "error": "Chyba", + "success": "Úspěch", + "cancel": "Zrušit", + "save": "Uložit", + "delete": "Odstranit", + "edit": "Upravit", + "close": "Zavřít", + "search": "Hledat", + "refresh": "Obnovit", + "settings": "Nastavení", + "help": "Nápověda", + "logout": "Odhlásit se", + "yes": "Ano", + "no": "Ne", + "unknown": "Neznámý", + "app_title": "Webmail", + "reconnecting": "Spojení ztraceno. Pokus o opětovné připojení…", + "rate_limited_title": "Ověřování serveru je dočasně omezeno.", + "rate_limited_detail": "Požadavky na pozadí byly pozastaveny, aby se zabránilo zablokování. Další pokus za {seconds} s.", + "rate_limited_action_title": "Požadavek pozastaven, aby se zabránilo zablokování.", + "rate_limited_action_detail": "Aplikace čeká na ukončení doby blokování serveru před odesláním dalších ověřovaných požadavků. Zkuste to znovu za {seconds} s.", + "pull_to_refresh": "Přetažením obnovíte", + "release_to_refresh": "Uvolněním obnovíte", + "refreshing": "Obnovování…" + }, + "notifications": { + "email_sent": "Zpráva byla úspěšně odeslána", + "email_deleted": "Zpráva byla odstraněna", + "email_archived": "Zpráva byla archivována", + "emails_deleted": "{count, plural, one {# e-mail smazán} few {# e-maily smazány} other {# e-mailů smazáno}}", + "emails_archived": "{count, plural, one {# e-mail archivován} few {# e-maily archivovány} other {# e-mailů archivováno}}", + "error_archiving": "E-mail se nepodařilo archivovat", + "error_updating": "E-mail se nepodařilo aktualizovat", + "email_starred": "Zpráva byla označena hvězdičkou", + "email_unstarred": "Hvězdička byla odebrána", + "email_marked_read": "Zpráva byla označena jako přečtená", + "email_marked_unread": "Zpráva byla označena jako nepřečtená", + "copied_to_clipboard": "Zkopírováno do schránky", + "source_copied": "Zdrojový kód zkopírován do schránky", + "error_sending": "Odeslání zprávy selhalo", + "error_deleting": "Odstranění zprávy selhalo", + "error_loading": "Načtení zprávy selhalo", + "new_email": "Nová zpráva", + "new_email_from": "Od {sender}", + "click_to_view": "Kliknutím zobrazíte", + "email_moved": "Zpráva byla přesunuta", + "emails_moved": "{count} zpráv bylo přesunuto", + "moved_to_mailbox": "Přesunuto do složky {mailbox}", + "move_failed": "Přesun selhal", + "removed_from_folder": "Odebráno ze složky {mailbox}", + "remove_from_folder_failed": "Odebrání z této složky se nezdařilo", + "emails_copied": "{count} zpráv bylo zkopírováno", + "copy_failed": "Kopírování selhalo", + "move_error": "Zprávu nelze přesunout do vybrané složky", + "email_tagged": "Zpráva byla označena štítkem", + "emails_tagged": "{count} zpráv bylo označeno štítkem", + "tag_failed": "Označení štítkem selhalo", + "identity_created": "Identita úspěšně vytvořena", + "identity_updated": "Identita úspěšně aktualizována", + "identity_deleted": "Identita odstraněna", + "identity_set_primary": "Hlavní identita byla aktualizována", + "identity_create_failed": "Vytvoření identity selhalo: {error}", + "identity_update_failed": "Aktualizace identity selhala: {error}", + "identity_delete_failed": "Odstranění identity selhalo: {error}", + "identity_unauthorized": "Nemáte oprávnění odesílat z této e-mailové adresy", + "identity_not_found": "Identita nenalezena", + "vacation_saved": "Nastavení nepřítomnosti bylo uloženo", + "vacation_save_failed": "Uložení nastavení nepřítomnosti selhalo", + "filters_saved": "Filtry úspěšně uloženy", + "filters_save_failed": "Uložení filtrů selhalo", + "filters_deleted": "Pravidlo filtru bylo odstraněno", + "templates_exported": "Šablony úspěšně exportovány", + "templates_imported": "{count, plural, one {Importována 1 šablona} few {Importovány # šablony} other {Importováno # šablon}}", + "templates_import_errors": "Některé šablony se nepodařilo importovat", + "templates_import_empty": "V souboru nebyly nalezeny žádné šablony", + "export_email_error": "Export zprávy selhal", + "import_email_success": "Zpráva byla úspěšně importována", + "import_email_error": "Import zprávy selhal", + "rule_created": "Pravidlo vytvořeno", + "rule_merged": "Přidáno do pravidla „{name}“", + "rule_already_covered": "Už to pokrývá pravidlo „{name}“", + "rule_apply_existing": "{count, plural, one {Použít na # existující zprávu} few {Použít na # existující zprávy} many {Použít na # existující zprávy} other {Použít na # existujících zpráv}}", + "rule_applied": "{count, plural, =0 {Žádnou zprávu nebylo třeba měnit} one {Pravidlo použito na # zprávu} few {Pravidlo použito na # zprávy} many {Pravidlo použito na # zprávy} other {Pravidlo použito na # zpráv}}", + "rule_apply_failed": "Pravidlo se nepodařilo použít na existující zprávy", + "rule_edit": "Upravit pravidlo", + "rule_undo": "Zpět", + "rule_undone": "Změna pravidla vrácena", + "rule_undo_failed": "Změnu pravidla se nepodařilo vrátit", + "rule_undo_conflict": "Vaše filtry se mezitím změnily. Vraťte změnu v nastavení filtrů.", + "rule_folder_failed": "Složku se nepodařilo vytvořit", + "vacation_filters_save_failed": "Nastavení nepřítomnosti bylo uloženo, ale přeposílání a příjemce odpovědi se uložit nepodařilo" + }, + "date": { + "today": "Dnes", + "yesterday": "Včera", + "this_week": "Tento týden", + "last_week": "Minulý týden", + "this_month": "Tento měsíc", + "older": "Starší", + "just_now": "Právě teď", + "minutes_ago": "před {count} minutou", + "minutes_ago_plural": "před {count} minutami", + "hours_ago": "před {count} hodinou", + "hours_ago_plural": "před {count} hodinami", + "days_ago": "před {count} dnem", + "days_ago_plural": "před {count} dny" + }, + "language": { + "title": "Jazyk", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "polish": "Polski", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Vybrat jazyk", + "switch_to_english": "Přepnout na angličtinu", + "switch_to_french": "Přepnout na francouzštinu", + "switch_to_japanese": "Přepnout na japonštinu", + "switch_to_spanish": "Přepnout na španělštinu", + "switch_to_italian": "Přepnout na italštinu", + "switch_to_german": "Přepnout na němčinu", + "switch_to_dutch": "Přepnout na nizozemštinu", + "switch_to_polish": "Přepnout na polštinu", + "switch_to_portuguese": "Přepnout na portugalštinu", + "switch_to_russian": "Přepnout na ruštinu", + "switching": "Změna jazyka...", + "switch": "Změnit jazyk", + "current": "Aktuální jazyk", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Přepnout na perštinu", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Přepnout na mongolštinu" + }, + "settings": { + "title": "Nastavení", + "back_to_mail": "Zpět na poštu", + "save_success": "Nastavení bylo uloženo", + "import_success": "Nastavení bylo importováno", + "import_error": "Import nastavení selhal", + "reset_confirm": "Opravdu chcete obnovit výchozí nastavení?", + "unsaved_changes": "Máte neuložené změny", + "discard_changes": "Zahodit neuložené změny?", + "discard": "Zahodit", + "keep_editing": "Pokračovat v úpravách", + "tabs": { + "appearance": "Vzhled", + "language": "Jazyk, region a čas", + "email": "Chování e-mailu", + "composer": "Psaní zpráv", + "privacy": "Soukromí a bezpečnost", + "account": "Účet", + "identities": "Identity", + "vacation": "Nepřítomnost", + "advanced": "Pokročilé", + "calendar": "Kalendář", + "filters": "Filtry", + "templates": "Šablony", + "folders": "Složky", + "keywords": "Štítky", + "security": "Zabezpečení", + "files": "Soubory", + "contacts": "Kontakty", + "encryption": "Šifrování", + "protocol_handlers": "Výchozí aplikace", + "sidebar_apps": "Aplikace postranního panelu", + "notifications": "Oznámení", + "layout": "Vzhled", + "themes": "Motivy", + "reading": "Čtení", + "composing": "Psaní", + "downloads": "Stažené", + "content_senders": "Obsah a odesílatelé", + "about_data": "Info a data", + "debug": "Ladění" + }, + "themes": { + "title": "Motivy", + "description": "Vyberte si z motivů nasazených vaším správcem a z vestavěných předvoleb.", + "locked": "Výběr motivu je uzamčen správcem.", + "forced": "Motiv „{name}“ je vynucen správcem a nelze jej změnit", + "admin_theme": "Motiv správce", + "activated": "Motiv aktivován", + "default_restored": "Výchozí motiv obnoven", + "default_name": "Výchozí", + "default_badge": "Výchozí", + "admin_enforced": "Vynuceno správcem" + }, + "tab_groups": { + "general": "Obecné", + "account": "Účet a identita", + "organization": "Organizace pošty", + "apps": "Aplikace", + "system": "Systém", + "appearance": "Vzhled", + "mail": "Pošta", + "privacy": "Soukromí a zabezpečení", + "advanced": "Pokročilé" + }, + "appearance": { + "message_list_order": { + "label": "Pořadí seznamu zpráv", + "description": "Jak jsou zprávy v seznamu seřazeny. Řazení provádí server, takže platí pro celou složku, ne jen pro načtené zprávy.", + "preset": { + "chronological": "Chronologicky (nejnovější první)", + "unread_first": "Nepřečtené první", + "starred_first": "S hvězdičkou první", + "tagged_first": "Se štítkem první", + "custom": "Vlastní" + }, + "tag_label": "Štítek", + "advanced_show": "Pokročilé: upravit úrovně řazení", + "advanced_hide": "Skrýt pokročilé", + "criterion_label": "Kritérium úrovně {n}", + "direction_label": "Směr úrovně {n}", + "criteria": { + "unread": "Nepřečtené", + "starred": "S hvězdičkou", + "tag": "Štítek", + "receivedAt": "Datum přijetí", + "sentAt": "Datum odeslání", + "from": "Odesílatel", + "subject": "Předmět", + "size": "Velikost" + }, + "direction": { + "unread_first": "Nepřečtené první", + "read_first": "Přečtené první", + "starred_first": "S hvězdičkou první", + "unstarred_first": "Bez hvězdičky první", + "tagged_first": "Se štítkem první", + "untagged_first": "Bez štítku první", + "newest_first": "Nejnovější první", + "oldest_first": "Nejstarší první", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Největší první", + "smallest_first": "Nejmenší první" + }, + "add_level": "Přidat úroveň", + "remove_level": "Odebrat úroveň", + "move_up": "Posunout nahoru", + "move_down": "Posunout dolů", + "tiebreak_note": "Až tři úrovně; další úrovně rozhodují při shodě. „Nejnovější první“ se vždy uplatní jako poslední.", + "scope": { + "label": "Použít na všechny složky", + "description": "Pokud je vypnuto, pořadí platí jen pro Doručenou poštu; ostatní složky zůstávají chronologické." + }, + "unsupported_note": "Váš poštovní server nepodporuje řazení podle stavu přečtení, hvězdiček ani štítků. Tyto úrovně se ignorují a seznam zůstává chronologický." + }, + "title": "Vzhled", + "description": "Přizpůsobte si vzhled a chování vašeho webového e-mailu", + "theme": { + "label": "Motiv", + "description": "Vyberte preferované barevné schéma", + "light": "Světlý", + "dark": "Tmavý", + "system": "Systémový" + }, + "language": { + "label": "Jazyk", + "description": "Vyberte preferovaný jazyk" + }, + "font_size": { + "label": "Velikost písma", + "description": "Upravte velikost textu pro lepší čitelnost", + "small": "Malé", + "medium": "Střední", + "large": "Velké" + }, + "list_density": { + "label": "Hustota zobrazení", + "description": "Ovládání rozestupů a výplní v celém rozhraní", + "extra_compact": "Velmi kompaktní", + "compact": "Kompaktní", + "regular": "Běžná", + "comfortable": "Pohodlná" + }, + "interface_layout": { + "label": "Rozvržení rozhraní", + "description": "Kde se nachází hledání, navigace a panely nástrojů", + "default": "Výchozí", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Povolit animace", + "description": "Zobrazovat plynulé přechody a efekty" + }, + "toolbar_position": { + "label": "Pozice panelu nástrojů", + "description": "Kde se mají zobrazovat tlačítka pro akce se zprávou (Odpovědět, Archivovat, Odstranit atd.)", + "top": "Nahoře", + "below_subject": "Pod předmětem" + }, + "toolbar_labels": { + "label": "Zobrazovat popisky panelu nástrojů", + "description": "Zobrazovat textové štítky vedle ikon na panelu nástrojů. Vypněte, abyste ušetřili místo, jakmile se s ikonami seznámíte." + }, + "hide_account_switcher": { + "label": "Skrýt přepínač účtů v postranním panelu", + "description": "Skryje výběr účtu v horní části postranního panelu. Stále můžete přepínat účty pomocí spodního navigačního panelu." + }, + "show_rail_account_list": { + "label": "Zobrazovat avatary účtů v navigačním panelu", + "description": "Zobrazovat samostatné ikony účtů ve spodní části navigačního panelu pro rychlé přepínání, s tlačítkem odhlášení níže." + }, + "unified_mailbox": { + "label": "Sjednocená schránka", + "description": "Zobrazovat sloučené složky (Doručené, Odeslané atd.) ze všech připojených účtů", + "cross_account": { + "label": "Napříč všemi účty", + "description": "Sloučit sjednocenou schránku napříč všemi připojenými účty místo omezení na aktivní účet." + }, + "include_group": { + "label": "Zahrnout skupinové schránky", + "description": "Zahrnout do sjednoceného zobrazení také sdílené/skupinové schránky." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Platí pro účet {account}.", + "no_folders": "Nejsou k dispozici žádné složky." + }, + "colorful_sidebar_icons": { + "label": "Barevné ikony postranního panelu", + "description": "Obarví ikony složek a štítků podle typu (modré Doručené, červený Spam, zelené Odeslané atd.). Vypněte pro jednobarevný postranní panel." + }, + "tint_list_rows": { + "label": "Podbarvit řádky barvou štítku", + "description": "Podbarvit každý řádek zprávy barvou jejího prvního štítku. Po vypnutí zůstanou řádky bílé; tečky a chipy štítků barvu dál zobrazují." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Podbarvit řádky barvou účtu", + "description": "Ve sjednoceném zobrazení podbarvit každý řádek barvou účtu místo zobrazení tečky účtu. Má přednost před podbarvením štítkem; štítky si barvu na chipech ponechají." + }, + "show_folder_total_count": { + "label": "Zobrazit celkový počet zpráv", + "description": "Zobrazí celkový počet zpráv vedle složek a štítků spolu s počtem nepřečtených. Vypněte pro zobrazení pouze nepřečtených." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Zobrazit sdílené poštovní účty", + "description": "Zobrazit složky schránek sdílených s vámi v postranním panelu pod Sdílené. Vypnutím skryjete všechny, nebo níže skryjte jednotlivé účty. Jejich kalendáře a kontakty zůstanou." + }, + "pro_interface": { + "label": "Pro rozhraní (experimentální)", + "description": "Rozložení pro pokročilé uživatele pouze pro stolní počítače s prohlížením zpráv na více kartách a pracovními postupy napříč účty. Standardní rozhraní zůstává nedotčeno; kdykoli se můžete vrátit.", + "open_label": "Otevřít Pro rozhraní", + "back_to_standard": "Zpět na standard" + }, + "cross_unread": { + "label": "Všechny účty: Nepřečtené", + "description": "Zobrazí v sekci Všechny účty položku s nepřečtenou poštou ze všech účtů, napříč všemi složkami kromě spamu, odeslané, archivu, koše a konceptů." + }, + "cross_starred": { + "label": "Všechny účty: S hvězdičkou", + "description": "Zobrazí v sekci Všechny účty položku s poštou s hvězdičkou ze všech účtů, napříč všemi složkami kromě spamu, odeslané, archivu, koše a konceptů." + }, + "cross_all": { + "label": "Všechny účty: Veškerá pošta", + "description": "Zobrazí v sekci Všechny účty položku s veškerou poštou ze všech účtů, napříč všemi složkami kromě spamu, odeslané, archivu, koše a konceptů." + } + }, + "keywords": { + "title": "E-mailové štítky", + "description": "Definujte štítky pro organizaci e-mailů pomocí barev. Ukládají se jako klíčová slova JMAP na serveru.", + "add_keyword": "Přidat štítek", + "label_field": "Zobrazovaný název", + "label_placeholder": "např. Práce, Osobní, Naliehavé", + "id_field": "ID štítku", + "id_placeholder": "např. prace, osobni", + "color_field": "Barva", + "id_exists": "Toto ID štítku již existuje", + "edit": "Upravit štítek", + "delete": "Odstranit štítek", + "save": "Uložit", + "add": "Přidat", + "cancel": "Zrušit", + "migrating": "Aktualizace štítku v existujících e-mailech…", + "migration_error": "Nepodařilo se aktualizovat štítek v existujících e-mailech", + "filters_migration_error": "Některá pravidla filtrů stále používají starý štítek – upravte je ručně", + "nesting": { + "label": "Vnořené štítky", + "description": "Vnořujte štítky pod jiné štítky a zobrazujte je v postranním panelu jako strom." + }, + "parent_field": "Nadřazený štítek", + "no_parent": "Bez nadřazeného štítku", + "too_long": "Tato cesta štítku je příliš dlouhá (nejvýše {max} znaků)", + "has_children_locked": "Pod tímto štítkem jsou vnořeny další štítky, proto jsou jeho název a nadřazený štítek uzamčeny. Nejprve je přesuňte nebo odeberte.", + "has_children_delete": "Nejprve odeberte štítky vnořené pod tímto", + "visibility_field": "Viditelnost v postranním panelu", + "visibility": { + "show": "Zobrazit", + "unread": "Zobrazit při nepřečtených", + "hide": "Skrýt" + }, + "unrecognized": { + "title": "Obnovit štítky ze serveru", + "description": "Názvy a barvy štítků jsou uloženy v tomto prohlížeči, ale samotné štítky jsou na vašich zprávách jako klíčová slova JMAP. Prohledejte poštu na klíčová slova, která zde žádný štítek nepokrývá, a přidejte je zpět.", + "scan": "Vyhledat štítky", + "rescan": "Hledat znovu", + "scanning": "Prohledávání… {scanned} z {total} zpráv", + "none": "Všechny štítky na vašich zprávách jsou zde už definované.", + "messages": "{count, plural, one {# zpráva} few {# zprávy} other {# zpráv}}", + "add_all": "Přidat všech {count}", + "partial": "Prohledáno bylo jen {scanned} nejnovějších zpráv z {total}. Štítky použité pouze ve starší poště mohou chybět.", + "error": "Poštu se nepodařilo prohledat na štítky." + } + }, + "notifications": { + "test_sound": "Otestovat zvuk oznámení", + "sounds": { + "default": "Výchozí (Pípnutí)", + "cheerful": "Veselý", + "involved": "Dynamický", + "swift": "Rychlý", + "relax": "Relaxační" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Zvuk oznámení", + "description": "Vyberte zvuk, který se přehraje při oznámení", + "choose": "Zvuk", + "choose_desc": "Vyberte tón oznámení a kliknutím na ikonu reproduktoru si jej poslechněte" + }, + "email": { + "title": "E-mailová oznámení", + "description": "Nakonfigurujte oznámení pro příchozí e-maily", + "enabled": "E-mailová oznámení", + "enabled_desc": "Zobrazovat oznámení při příchodu nových e-mailů", + "sound": "Zvuk oznámení", + "sound_desc": "Přehrát zvukové upozornění při příchodu nových e-mailů", + "inbox_only": "Jen Doručené", + "inbox_only_desc": "Upozorňovat jen na poštu, která přijde do Doručených; poštu, kterou filtr uloží do jiné složky, přeskočit" + }, + "calendar": { + "title": "Oznámení kalendáře", + "description": "Nakonfigurujte oznámení pro události kalendáře", + "enabled": "Oznámení o událostech", + "enabled_desc": "Zobrazovat upozornění na nadcházející události v kalendáři", + "sound": "Zvuk oznámení", + "sound_desc": "Přehrát zvukové upozornění pro připomenutí kalendáře", + "invitation_parsing": "Rozpoznávat e-mailové pozvánky", + "invitation_parsing_desc": "Rozpoznávat pozvánky kalendáře v přílohách e-mailů a zobrazovat akce kalendáře" + }, + "push": { + "confirm_disable_message": "Toto zařízení přestane přijímat upozornění, když je web zavřený.", + "confirm_disable_title": "Zakázat oznámení na pozadí?", + "description": "Přijímat systémová oznámení o nové poště, když je tento web zavřený. Doručováno přes push relay Bulwark; relay nikdy nevidí obsah pošty.", + "disable": "Zakázat", + "enable": "Povolit", + "dismiss_aria": "Zavřít výzvu k oznámením", + "ios_hint": "Na iOS nejprve nainstalujte web na domovskou obrazovku – Safari doručuje Web Push pouze nainstalovaným PWA.", + "reenable": "Znovu zaregistrovat", + "relay_desc": "Výchozí je hostovaný relay Bulwark. Změňte pouze pokud používáte vlastní hosting.", + "relay_label": "Push relay", + "relay_locked": "Nastaveno administrátorem", + "relay_locked_desc": "URL push relay byla nastavena administrátorem a nelze ji změnit.", + "status_active": "Aktivní na tomto zařízení", + "status_busy": "Pracuji…", + "status_inactive": "Není povoleno na tomto zařízení", + "status_unsupported": "Tento prohlížeč nepodporuje Web Push", + "status_lite": "Web Push není v Bulwark Lite k dispozici; vyžaduje server Bulwark.", + "title": "Oznámení na pozadí", + "devices_title": "Registrovaná zařízení", + "devices_desc": "Všechna zařízení registrovaná pro příjem oznamení na pozadí u tohoto účtu. Odvolání okamžitě zastaví jejich příjem.", + "devices_loading": "Načítání registrovaných zařízení…", + "devices_empty": "Pro oznámení na pozadí není registrováno žádné zařízení.", + "devices_error": "Registrovaná zařízení se nepodařilo načíst.", + "devices_refresh": "Obnovit", + "device_this": "Toto zařízení", + "device_other": "Jiné zařízení", + "device_status_active": "Doručuje oznámení", + "device_status_inactive": "Nikdy nedoručilo oznámení", + "device_status_unknown": "Tomuto relay není známo", + "device_expires": "Vyprší {date}", + "revoke": "Odvolat", + "confirm_revoke_title": "Odvolat toto zařízení?", + "confirm_revoke_message": "Okamžitě přestane přijímat oznámení na pozadí. Opětovné zapnutí push na tom zařízení jej zaregistruje znovu.", + "confirm_revoke_message_this": "Toto zařízení okamžitě přestane přijímat oznámení na pozadí." + } + }, + "language_region": { + "title": "Jazyk, region a čas", + "description": "Jazyk, formát data, formát času a další regionální předvolby", + "language": { + "label": "Jazyk", + "description": "Vyberte preferovaný jazyk", + "english": "Angličtina", + "french": "Francouzština" + }, + "date_format": { + "label": "Formát data", + "description": "Jak se zobrazují data v seznamu e-mailů", + "smart": "Chytrý", + "relative": "Relativní", + "full": "Vždy úplné datum", + "preview_today": "Dnes:", + "preview_this_week": "Tento týden:", + "preview_older": "Starší:" + }, + "date_locale": { + "label": "Region formátu data", + "description": "Pořadí částí číselného data (den, měsíc, rok)", + "auto": "Automaticky", + "iso": "ISO 8601", + "dmy": "Den/měsíc/rok", + "mdy": "Měsíc/den/rok" + }, + "time_format": { + "label": "Formát času", + "description": "Vyberte 12hodinový nebo 24hodinový formát času", + "12h": "12hodinový", + "24h": "24hodinový" + }, + "first_day": { + "label": "První den v týdnu", + "description": "Začátek týdne v neděli nebo v pondělí", + "sunday": "Neděle", + "monday": "Pondělí" + }, + "time_zone": { + "label": "Časové pásmo", + "description": "Zobrazovat časy e-mailů a kalendáře v tomto časovém pásmu místo pásma hlášeného prohlížečem", + "auto": "Automaticky ({zone})", + "preview_now": "Nyní:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Vymazat hledání při přepnutí složky", + "description": "Kliknutí na složku (Doručená pošta, Odeslaná…) vymaže aktivní hledání a zobrazí složku. Když je vypnuto, hledání zůstává a spustí se znovu ve zvolené složce." + }, + "request_read_receipt": { + "label": "Standardně vyžadovat potvrzení o přečtení", + "description": "Při psaní nové zprávy předem zapnout žádost o potvrzení o přečtení." + }, + "read_receipt_response": { + "label": "Reagovat na žádosti o potvrzení o přečtení", + "description": "Co dělat, když příchozí zpráva žádá o potvrzení o přečtení.", + "ask": "Vždy se zeptat", + "always": "Vždy odeslat", + "never": "Nikdy neodesílat" + }, + "title": "Chování e-mailu", + "description": "Nakonfigurujte způsob zpracování e-mailů", + "mark_read": { + "label": "Označit jako přečtené", + "description": "Kdy označit e-mail jako přečtený po jeho otevření", + "instant": "Okamžitě", + "delay_3s": "Po 3 sekundách", + "delay_5s": "Po 5 sekundách", + "never": "Nikdy" + }, + "delete_action": { + "label": "Akce odstranění", + "description": "Co se má stát po odstranění e-mailu", + "trash": "Přesunout do Koše", + "trash_and_read": "Přesunout do Koše a označit jako přečtené", + "permanent": "Trvale odstranit", + "warning": "E-maily budou trvale odstraněny a nebude možné je obnovit. Tato akce je nevratná." + }, + "message_spacing": { + "label": "Odsazení zprávy", + "description": "Odsazení kolem těla zprávy při čtení", + "auto": "Automaticky (doporučeno)", + "always": "Vždy přidat odsazení", + "edge": "Od okraje k okraji" + }, + "plain_text_font": { + "label": "Písmo prostého textu", + "description": "Písmo použité pro e-maily v prostém textu", + "mono": "Neproporcionální", + "sans": "Normální (stejné jako aplikace)" + }, + "archive_mode": { + "label": "Archivovat do", + "description": "Jak organizovat e-maily při archivaci", + "single": "Jedna složka", + "year": "Složka pro každý rok", + "month": "Složka pro každý měsíc", + "reorganize": "Reorganizovat existující archiv", + "reorganize_success": "{count, plural, =0 {Žádné zprávy k reorganizaci} one {Reorganizována 1 zpráva} few {Reorganizovány # zprávy} other {Reorganizováno # zpráv}}", + "reorganize_error": "Nepodařilo se reorganizovat archiv" + }, + "permanently_delete_junk": { + "label": "Trvale odstraňovat spam", + "description": "Trvale odstraňovat zprávy ze složky Spam/Nevyžádaná pošta místo přesunutí do Koše" + }, + "confirm_bulk_delete": { + "label": "Potvrdit před smazáním více zpráv", + "description": "Zeptat se před přesunem více vybraných zpráv do koše. Trvalé smazání se ptá vždy." + }, + "mail_layout": { + "label": "Rozložení pošty", + "description": "Vyberte si mezi klasickým rozděleným zobrazením a soustředěným seznamem podobným Gmailu.", + "split": "Rozdělené zobrazení", + "split_description": "Seznam zpráv a panel pro čtení zůstávají viditelné vedle sebe.", + "focus": "Soustředěný seznam", + "focus_description": "Zobrazit jeden řádek na zprávu a otevřít poštu na plnou šířku s viditelným panelem složek.", + "horizontal": "Čtecí panel dole", + "horizontal_description": "Zobrazit seznam zpráv nahoře a otevřít vybranou zprávu ve čtecím panelu pod ním." + }, + "list_columns": { + "label": "Zobrazit seznam ve sloupcích", + "description": "Zobrazí jednořádkové řádky jako tabulku se sloupci Od, Předmět, Datum a Velikost pod záhlavím." + }, + "show_preview": { + "label": "Zobrazit náhledový text", + "description": "Zobrazovat náhledy e-mailů v seznamu", + "focus_description": "Zobrazovat vložený náhledový text v jednořádkovém soustředěném seznamu" + }, + "show_verification_codes": { + "label": "Zobrazovat ověřovací kódy", + "description": "Nabízet kód z přihlašovacích a potvrzovacích e-mailů ke zkopírování jedním kliknutím, v seznamu po dobu jednoho dne a ve zprávě" + }, + "disable_threading": { + "label": "Zakázat seskupování konverzací", + "description": "Zobrazovat e-maily jako jednotlivé zprávy namísto seskupování do vláken" + }, + "plain_text_mode": { + "label": "Pouze prostý text", + "description": "Zakázat editor formátovaného textu a odesílat všechny e-maily pouze jako prostý text, včetně odpovědí a přeposílání" + }, + "auto_select_reply_identity": { + "label": "Automaticky vybírat adresu pro odpověď", + "description": "Při odpovídání automaticky přepnout adresu odesílatele na identitu, která původně obdržela zprávu" + }, + "reply_identity_match": { + "label": "Porovnávání přijímací adresy", + "description": "Které přijímací adresy se počítají jako vaše. „Pouze přesná adresa“ vybere jen jednu z vašich nastavených identit; „stejná doména“ navíc považuje jakoukoli jinou adresu na doménách vašich identit za catch-all alias a přepíše na ni hlavičku Od. Pokud jsou tyto adresy distribuční seznamy, zvolte přesnou adresu.", + "exact": "Pouze přesná adresa", + "domain": "Jakákoli adresa na mých doménách" + }, + "signature_position": { + "label": "Pozice podpisu", + "description": "Kam vložit podpis v odpovědích a přeposláních. Nad citovaným textem působí přirozeně jako zakončení odpovědi; pod ním zachovává původní zprávu vcelku.", + "above_quote": "Před citovaným textem", + "below_quote": "Za citovaným textem" + }, + "signature_separator": { + "label": "Oddělovač podpisu", + "description": "Před podpis přidat standardní oddělovací řádek \"-- \" (RFC 3676). Vypněte, pokud chcete plynule přejít z textu zprávy do podpisu." + }, + "sub_address_delimiter": { + "label": "Oddělovač sub-adresy", + "description": "Znak oddělující uživatelské jméno od sub-adresy. Zvolte oddělovač používaný vaším poštovním serverem (např. uzivatel{delimiter}stitek@domena.cz).", + "option": "{delimiter} (uzivatel{delimiter}stitek@domena.cz)", + "custom": "Vlastní…", + "custom_input_label": "Vlastní znak oddělovače" + }, + "attachment_click_action": { + "label": "Akce po kliknutí na přílohu", + "description": "Vyberte, zda má kliknutí na přílohu zobrazit náhled, nebo ji ihned stáhnout", + "preview": "Náhled, pokud je to možné", + "download": "Okamžitě stáhnout" + }, + "attachment_position": { + "label": "Pozice příloh", + "description": "Kde zobrazovat přílohy v hlavičce e-mailu", + "beside-sender": "Vedle odesílatele", + "below-header": "Pod hlavičkou" + }, + "emails_per_page": { + "10": "10 zpráv", + "25": "25 zpráv", + "50": "50 zpráv", + "100": "100 zpráv", + "label": "Zpráv na stránku", + "description": "Počet e-mailů načtených najednou" + }, + "always_light_mode": { + "label": "Vždy zobrazovat zprávy ve světlém režimu", + "description": "Vykreslovat obsah e-mailu ve světlém režimu, i když je aplikace v tmavém režimu, aby se předešlo problémům s konverzí" + }, + "external_content": { + "label": "Externí obsah", + "description": "Jak zacházet s obrázky a externím obsahem", + "ask": "Vždy se ptát", + "block": "Vždy blokovat", + "allow": "Vždy povolit" + }, + "trusted_senders": { + "label": "Důvěryhodní odesílatelé", + "description": "Spravujte odesílatele, jejichž obrázky se načítají automaticky", + "count_zero": "Žádní", + "count_one": "1 odesílatel", + "count_other": "{count} odesílatelů", + "modal_title": "Důvěryhodní odesílatelé", + "empty_title": "Žádní důvěryhodní odesílatelé", + "empty_description": "Při prohlížení e-mailů se zablokovanými obrázky klikněte na „Vždy důvěřovat tomuto odesílateli“, čímž jej přidáte sem.", + "add_manually": "Přidat odesílatele ručně", + "add_button": "Přidat", + "add_placeholder": "Zadejte e-mailovou adresu", + "search_placeholder": "Hledat odesílatele...", + "no_results": "Hledání neodpovídá žádný odesílatel", + "remove": "Odstranit", + "close": "Zavřít", + "invalid_email": "Zadejte platnou e-mailovou adresu", + "already_added": "Tento odesílatel je již důvěryhodný", + "save_error": "Uložení selhalo - zkontrolujte ladicí protokol Kontaktů", + "use_address_book_label": "Synchronizovat s adresářem", + "use_address_book_description": "Ukládat důvěryhodné odesílatele ve vyhrazeném adresáři „Důvěryhodní odesílatelé“, aby se synchronizovali napříč zařízeními" + }, + "hover_actions": { + "label": "Rychlé akce po přejetí myší", + "description": "Vyberte rychlé akce viditelné po přejetí přes e-mail v seznamu", + "delete": "Odstranit", + "star": "Označit hvězdičkou / odebrat hvězdičku", + "mark_read": "Označit jako přečtené / nepřečtené", + "archive": "Archivovat", + "tag": "Štítek", + "spam": "Označit jako spam", + "not_spam": "Není spam", + "none_selected": "Nebyly vybrány žádné akce", + "mode_label": "Režim zobrazení", + "mode_inline": "Vložené", + "mode_floating": "Plovoucí", + "corner_label": "Plovoucí pozice", + "corner_top-left": "Levý horní roh", + "corner_top-right": "Pravý horní roh", + "corner_bottom-left": "Levý dolní roh", + "corner_bottom-right": "Pravý dolní roh" + }, + "default_mail_program": { + "label": "Výchozí poštovní program", + "description": "Zaregistrovat aplikaci {appName} jako výchozí poštovní program pro odkazy mailto:", + "button": "Nastavit jako výchozí", + "success": "Prohlížeč požádal o nastavení jako výchozího programu", + "error": "Váš prohlížeč tuto funkci nepodporuje" + }, + "attachment_reminder": { + "label": "Připomenutí přílohy", + "description": "Upozornit před odesláním, když zpráva zmiňuje přílohy, ale žádné nejsou připojeny", + "keywords_label": "Klíčová slova spouštějící připomenutí", + "keywords_description": "Slova nebo fráze, které po nalezení ve zprávě spustí připomenutí", + "add_placeholder": "Přidat klíčové slovo...", + "add": "Přidat", + "remove": "Odstranit" + }, + "empty_subject_warning": { + "label": "Upozornění na prázdný předmět", + "description": "Před odesláním zprávy bez předmětu požádat o potvrzení." + }, + "recipient_mentions": { + "label": "Zmiňovat příjemce pomocí @", + "description": "Napsáním @ ve zprávě se nabídnou její příjemci a vloží se křestní jméno vybraného. Enter nebo Tab vybere zvýrazněné jméno." + }, + "mention_adds_recipient": { + "label": "Kontakty při zmínkách přes @", + "description": "Po napsání @ se nabídnou i vaše kontakty a lidé z adresáře. Kdo ještě není příjemcem, bude po výběru do zprávy přidán.", + "to": "Přidat do Komu", + "cc": "Přidat do Kopie", + "off": "Nabízet jen příjemce" + }, + "hide_inline_image_attachments": { + "label": "Skrýt vložené obrázky ze seznamu příloh", + "description": "Obrázky vložené přímo do textu zprávy se nezobrazí jako samostatné přílohy" + }, + "attachment_image_previews": { + "label": "Zobrazit náhledy obrázků v přílohách", + "description": "Zobrazovat obrázkové přílohy jako miniatury místo obecných ikon souborů" + }, + "send_delay": { + "label": "Vrátit odeslání / prodleva odeslání", + "description": "Pozdržet běžné odeslání krátkým oknem na serveru.", + "off": "Vypnuto", + "seconds": "{seconds} sekund", + "unsupported": "Aktuální účet neoznamuje podporu odloženého odeslání. Nastavení zůstane uloženo pro jiné účty." + }, + "return_to_list_after_action": { + "label": "Návrat do seznamu po smazání nebo označení jako nepřečtené", + "description": "Po smazání nebo označení otevřené zprávy jako nepřečtené se vrátit do seznamu zpráv místo otevření následující zprávy." + }, + "swipe_right_action": { + "label": "Akce při přejetí doprava (mobil)", + "description": "Na dotykových zařízeních akce, která se provede při přejetí zprávy v seznamu doprava." + }, + "swipe_left_action": { + "label": "Akce při přejetí doleva (mobil)", + "description": "Na dotykových zařízeních akce, která se provede při přejetí zprávy v seznamu doleva." + }, + "swipe_actions": { + "none": "Žádná", + "archive": "Archivovat", + "delete": "Odstranit", + "mark_read": "Přepnout přečteno", + "star": "Přepnout hvězdičku", + "spam": "Označit jako spam" + }, + "rtl_editing": { + "label": "Podpora psaní zprava doleva", + "description": "Přidá do panelu nástrojů editoru tlačítko směru textu, kterým lze odstavce nastavit zleva doprava nebo zprava doleva" + } + }, + "composer": { + "title": "Psaní zpráv", + "description": "Nakonfigurujte nastavení pro psaní e-mailů", + "autosave": { + "label": "Interval automatického ukládání", + "description": "Jak často se mají koncepty automaticky ukládat", + "30s": "Každých 30 sekund", + "1m": "Každou minutu", + "2m": "Každé 2 minuty", + "5m": "Každých 5 minut" + }, + "send_confirmation": { + "label": "Potvrzení odeslání", + "description": "Před odesláním zprávy požádat o potvrzení" + }, + "default_reply": { + "label": "Výchozí režim odpovědi", + "description": "Výchozí akce po kliknutí na Odpovědět", + "reply": "Odpovědět", + "reply_all": "Odpovědět všem" + } + }, + "privacy": { + "title": "Soukromí a bezpečnost", + "description": "Správa nastavení soukromí a bezpečnosti", + "external_images": { + "label": "Blokovat externí obrázky", + "description": "Zabránit sledování pomocí externích obrázků" + }, + "session_timeout": { + "label": "Časový limit relace", + "description": "Automaticky odhlásit po určité době nečinnosti", + "never": "Nikdy", + "30m": "30 minut", + "1h": "1 hodina", + "4h": "4 hodiny" + }, + "clear_cache": { + "label": "Vymazat mezipaměť", + "description": "Odstranit data z mezipaměti a dočasné soubory", + "button": "Vymazat mezipaměť", + "confirm": "Opravdu chcete vymazat mezipaměť?", + "success": "Mezipaměť byla vymazána" + } + }, + "account": { + "title": "Účet", + "description": "Zobrazit informace o vašem účtu", + "name_label": "Zobrazované jméno", + "username_label": "Uživatelské jméno", + "account_type_label": "Typ účtu", + "auth_method_label": "Ověřování", + "auth_method_oauth": "Jednotné přihlášení (OAuth/OIDC)", + "auth_method_basic": "Heslo", + "auth_method_token": "Přístupový token", + "demo_account": "Demo účet", + "email": { + "label": "E-mailová adresa", + "value": "{email}" + }, + "server": { + "label": "JMAP server", + "value": "{server}" + }, + "storage": { + "label": "Využití úložiště", + "used": "Využito {used} z {total}", + "percentage": "Využito {percent} %" + }, + "last_sync": { + "label": "Poslední synchronizace", + "value": "{time}" + }, + "accounts": { + "title": "Přihlášené účty", + "description": "Přetažením změňte pořadí účtů v rozbalovacím seznamu", + "active": "Aktuálně aktivní účet", + "default_badge": "Výchozí účet", + "set_default": "Nastavit jako výchozí", + "switch_to": "Přepnout na tento účet", + "move_up": "Posunout nahoru", + "move_down": "Posunout dolů", + "drag_handle": "Přetažením změňte pořadí", + "add": "Přidat účet" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Sdíleno se mnou", + "description": "Skupinové a sdílené účty, které můžete spravovat. Vyberte jeden pro úpravu jeho filtrů, odpovědi v nepřítomnosti, kalendářů a kontaktů.", + "shared_label": "Sdílený účet" + } + }, + "security": { + "title": "Zabezpečení účtu", + "description": "Správa hesla, dvoufázového ověření a nastavení zabezpečení", + "detecting": "Zjišťování možností serveru...", + "not_available": "Správa zabezpečení účtu není pro tento poštovní server k dispozici. Požadovaná oprávnění mohou být vypnuta. Podrobnosti naleznete v dokumentaci.", + "password": { + "title": "Změnit heslo", + "current": "Aktuální heslo", + "new": "Nové heslo", + "confirm": "Potvrdit nové heslo", + "submit": "Změnit heslo", + "success": "Heslo bylo změněno", + "error_title": "Změna hesla selhala", + "error_mismatch": "Nová hesla se neshodují", + "error_min_length": "Heslo musí obsahovat alespoň 8 znaků", + "error_generic": "Heslo se nepodařilo změnit" + }, + "display_name": { + "label": "Zobrazované jméno", + "description": "Vaše jméno zobrazené na serveru", + "placeholder": "Zadejte své zobrazované jméno", + "save": "Uložit", + "success": "Zobrazované jméno bylo aktualizováno", + "error": "Nepodařilo se aktualizovat zobrazované jméno" + }, + "totp": { + "section_title": "Dvoufázové ověření", + "label": "Ověřování TOTP", + "description": "Přidejte další vrstvu zabezpečení pomocí jednorázového hesla na bázi času", + "active": "Zapnuto", + "inactive": "Vypnuto", + "enabled": "Dvoufázové ověření zapnuto", + "disabled": "Dvoufázové ověření vypnuto", + "enable_error": "Nepodařilo se zapnout 2FA", + "disable_error": "Nepodařilo se vypnout 2FA", + "setup_instructions": "Zkopírujte tuto URL adresu do své ověřovací aplikace (Google Authenticator, Authy atd.):", + "verification_code": "Ověřovací kód", + "confirm": "Potvrdit", + "disable": "Vypnout", + "disable_confirm_prompt": "Pro vypnutí dvoufázového ověření zadejte heslo.", + "password_required": "Zadejte heslo", + "code_required": "Zadejte ověřovací kód", + "code_invalid": "Neplatný kód. Zkontrolujte ověřovací aplikaci a zkuste to znovu." + }, + "app_passwords": { + "title": "Hesla aplikací", + "description": "Vytvořte hesla pro aplikace, které nepodporují dvoufázové ověření", + "add": "Přidat", + "create": "Vytvořit", + "cancel": "Zrušit", + "done": "Hotovo", + "generate": "Vygenerovat", + "name_label": "Název aplikace", + "name_placeholder": "např. Thunderbird, iPhone Mail", + "expires_label": "Platí do (volitelné)", + "allowed_ips_label": "Povolené IP (volitelné)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Oddělte čárkou či mezerou. Pro povolení všech IP nechte prázdné.", + "password_label": "Heslo (pro vygenerování nechte prázdné)", + "password_placeholder": "Bude vygenerováno automaticky", + "copy_now_warning": "Heslo si hned zkopírujte – už se znovu nezobrazí.", + "added": "Heslo aplikace vytvořeno", + "removed": "Heslo aplikace smazáno", + "add_error": "Chyba při vytváření hesla aplikace", + "remove_error": "Chyba při mazání hesla aplikace", + "none": "Žádná hesla aplikací" + }, + "api_keys": { + "title": "API klíče", + "description": "Vytvořte si API klíče pro skripty a integrace, které komunikují přímo se serverem.", + "name_label": "Název klíče", + "name_placeholder": "např. Zálohovací skript, CI runner", + "copy_now_warning": "API klíč si hned zkopírujte – už se znovu nezobrazí.", + "added": "API klíč vytvořen", + "removed": "API klíč smazán", + "add_error": "Chyba při vytváření API klíče", + "remove_error": "Chyba při mazání API klíče", + "none": "Žádné API klíče" + }, + "encryption": { + "section_title": "Šifrování dat v klidu", + "label": "Šifrování e-mailů", + "description": "Šifrovat zprávy uložené na serveru pro větší soukromí", + "active": "Šifrování {type} zapnuto", + "inactive": "Vypnuto", + "enabled": "Šifrování dat v klidu zapnuto", + "disabled_success": "Šifrování dat v klidu vypnuto", + "error": "Nepodařilo se aktualizovat nastavení šifrování", + "algorithm_label": "Šifrovací algoritmus", + "encrypt_on_append": "Šifrovat nové e-maily při nahrání", + "allow_spam_training": "Povolit trénování spamového filtru před zašifrováním e-mailů", + "enable_encryption": "Zapnout šifrování", + "enable_tooltip": "Zapnout šifrování tímto klíčem", + "configure_title": "Zapnout šifrování tímto klíčem", + "enable_button": "Zapnout", + "disable_tooltip": "Vypnout šifrování", + "enabled_success": "Šifrování dat v klidu zapnuto" + }, + "email_client": { + "title": "Konfigurace poštovního klienta", + "description": "Použijte tyto přihlašovací údaje pro konfiguraci e-mailového klienta na počítači nebo mobilním zařízení (Thunderbird, Apple Mail, Outlook atd.)", + "jmap_username_label": "Uživatelské jméno JMAP", + "copy": "Kopírovat", + "copied": "Zkopírováno", + "password_instructions": "Použijte výše uvedené uživatelské jméno JMAP společně s heslem aplikace pro přihlášení v poštovním klientovi. Pokud jste tak ještě neučinili, vytvořte si heslo aplikace v sekci výše." + }, + "link_device": { + "title": "Propojit mobilní aplikaci", + "description": "Přihlaste se do aplikace Bulwark Mail v telefonu, aniž byste tam museli zadávat heslo. Telefon dostane vlastní přihlášení, takže odhlášení zde ho neodhlásí.", + "generate": "Zobrazit QR kód", + "regenerate": "Zobrazit nový kód", + "instructions": "Otevřete aplikaci Bulwark Mail a na přihlašovací obrazovce klepněte na \"Scan a sign-in code\" (nebo na \"Add account\" → \"Scan a sign-in code\", pokud jste už přihlášeni) a pak namiřte fotoaparát sem.", + "expires_in": "Tento kód vyprší za {seconds} sekund. Lze jej použít pouze jednou.", + "expired": "Platnost tohoto kódu vypršela.", + "error": "Párovací kód se nepodařilo vytvořit. Zkuste to prosím znovu.", + "password_prompt": "Zadejte heslo a potvrďte, že jste to vy. Telefon dostane vlastní přihlášení; vaše heslo se do telefonu neposílá.", + "show_password": "Zobrazit heslo", + "hide_password": "Skrýt heslo", + "continue": "Pokračovat", + "error_wrong_password": "Nesprávné heslo. Zkuste to znovu.", + "totp_prompt": "Zadejte kód z ověřovací aplikace.", + "error_password_or_code": "Heslo nebo kód je nesprávný.", + "error_not_signed_in": "Vaše relace vypršela. Pro propojení zařízení se znovu přihlaste.", + "error_impersonation": "Zařízení nelze propojit, když si prohlížíte schránku jiného uživatele.", + "error_too_many_attempts": "Příliš mnoho pokusů. Chvíli počkejte a zkuste to znovu.", + "error_session_secret": "Propojení zařízení není na tomto serveru nastaveno (chybí SESSION_SECRET). Obraťte se na správce.", + "error_server_unreachable": "Nepodařilo se spojit s vaším poštovním serverem. Zkuste to znovu.", + "error_pairing_unavailable": "Váš poštovní server neumožňuje propojit zařízení tímto způsobem. Obraťte se na správce.", + "qr_alt": "QR kód pro přihlášení", + "link_label": "Přihlašovací odkaz", + "copy_link": "Kopírovat přihlašovací odkaz", + "copied": "Zkopírováno", + "copy_hint": "Nemáte fotoaparát? Zkopírujte odkaz a vložte ho do aplikace pomocí \"Paste sign-in link\".", + "success_title": "Přihlášeno v telefonu", + "success_body": "Tuto stránku můžete zavřít. Chcete-li propojit další zařízení, zobrazte nový kód.", + "link_another": "Propojit další zařízení", + "use_sso": "Použít místo toho jednotné přihlášení", + "error_insecure_server": "Mobilní aplikace se k poštovním serverům připojuje jen přes https a tento server používá http." + }, + "public_keys": { + "name_label": "Název veřejného klíče", + "name_placeholder": "např. Můj klíč", + "key_label": "Veřejný klíč (S/MIME nebo PGP ve formátu ASCII)", + "key_placeholder": "Sem vložte svůj veřejný klíč", + "none": "Nejsou nastaveny žádné veřejné klíče" + } + }, + "identities": { + "title": "Identity odesílatele", + "description": "Správa e-mailových adres, ze kterých můžete odesílat zprávy", + "identities_count": { + "label": "Vaše identity", + "description": "E-mailové adresy nakonfigurované pro odesílání", + "count_zero": "Žádné identity", + "count_one": "1 identita", + "count_other": "{count} identit" + }, + "manage": "Spravovat identity", + "sub_addressing": { + "label": "Subadresování", + "description": "Používejte štítky jako uzivatel+stitek@domena.cz pro uspořádání příchozí pošty", + "learn_more": "Zjistit více" + } + }, + "vacation": { + "title": "Nepřítomnost", + "description": "Během vaší nepřítomnosti se na příchozí e-maily bude reagovat automaticky", + "loading": "Načítání nastavení automatické odpovědi...", + "not_supported": "Váš poštovní server nepodporuje automatické odpovědi v nepřítomnosti.", + "fetch_error": "Nepodařilo se načíst nastavení automatické odpovědi. Zkuste to znovu.", + "status": { + "label": "Odpovídat automaticky", + "description": "Odesílat automatickou odpověď lidem, kteří vám napíší", + "active": "Aktivní", + "inactive": "Neaktivní" + }, + "date_range": { + "start": "Datum zahájení", + "start_description": "Nechte prázdné pro spuštění ihned", + "end": "Datum ukončení", + "end_description": "Nechte prázdné pro běh bez ukončení" + }, + "message": { + "subject_label": "Předmět", + "subject_description": "Předmět automatické odpovědi", + "subject_placeholder": "Mimo kancelář", + "body_label": "Tělo zprávy", + "body_description": "Obsah zprávy v prostém textu", + "body_placeholder": "Děkuji za vaši zprávu. Momentálně jsem mimo kancelář a odpovím po svém návratu.", + "html_label": "Formátovaná zpráva (HTML)", + "html_description": "Přidejte formátovanou verzi s odkazy a stylem. Příjemci, jejichž poštovní klient ji nedokáže zobrazit, uvidí prostý text výše.", + "html_placeholder": "Napište formátovanou odpověď o nepřítomnosti…" + }, + "preview": { + "title": "Náhled", + "show": "Zobrazit náhled", + "hide": "Skrýt náhled" + }, + "save": "Uložit změny", + "saving": "Ukládání...", + "warnings": { + "end_before_start": "Datum ukončení musí být po datu zahájení", + "invalid_date": "Zadejte platné datum zahájení a ukončení včetně času, nebo pole nechte prázdné", + "start_in_past": "Datum zahájení je v minulosti", + "empty_body": "Tělo zprávy je prázdné - příjemci obdrží prázdnou odpověď", + "subject_too_long": "Předmět je pro server příliš dlouhý (nejvýše {max} bajtů).", + "body_too_long": "Zpráva je pro server příliš dlouhá (nejvýše {max} bajtů včetně formátování).", + "forward_address": "Zadejte platnou adresu pro přeposílání", + "not_running": "Přeposílání nebo příjemci odpovědi jsou uloženi, ale právě nejsou aktivní. Uložením je znovu zapnete." + }, + "forward": { + "title": "Přeposílání", + "description": "Přeposílá příchozí zprávy na jinou adresu, s automatickou odpovědí i bez ní, v období uvedeném výše (bez data ukončení, dokud ho nevypnete). Spam se nepřeposílá.", + "enabled_label": "Přeposílat zprávy", + "to_label": "Přeposlat na", + "to_placeholder": "email@example.com", + "keep_label": "Ponechat v Doručených", + "keep_description": "Jinak se zpráva pouze přepošle: nezůstane v této schránce a nepoužije se na ni žádné pravidlo filtru." + }, + "audience": { + "label": "Odpovídat", + "description": "Interní odesílatelé: {domains}", + "all": "Všem odesílatelům", + "internal": "Jen interním odesílatelům", + "external": "Jen externím odesílatelům" + }, + "auto_reply": { + "title": "Automatická odpověď" + } + }, + "folders": { + "role_memos": "Poznámky", + "title": "Složky", + "description": "Správa složek pošty a přiřazení standardních rolí", + "folder_list": "Vaše složky", + "folder_list_description": "Kliknutím na ikonu složky ji upravíte", + "standard_roles": "Standardní role složek", + "standard_roles_description": "Přiřaďte složky používané pro standardní role poštovní schránky, jako jsou Doručené, Odeslané, Koš atd.", + "role_inbox": "Doručené", + "role_drafts": "Koncepty", + "role_sent": "Odeslané", + "role_trash": "Koš", + "role_junk": "Spam / Nevyžádaná pošta", + "role_archive": "Archiv", + "role_scheduled": "Naplánováno", + "role_snoozed": "Odloženo", + "role_none": "Žádná", + "create_folder": "Vytvořit složku", + "create_subfolder": "Vytvořit podsložku", + "reorder": "Přetažením změníte pořadí", + "reorder_error": "Nepodařilo se změnit pořadí složek", + "move_error": "Nepodařilo se přesunout složku", + "select_folder": "Vybrat {name}", + "select_hint": "Vybrat. Shift+kliknutí vybere všechny složky až po naposledy kliknutou.", + "selected_count": "{count, plural, one {Vybrána # složka} few {Vybrány # složky} other {Vybráno # složek}}", + "move_to": "Přesunout do...", + "move_to_top_level": "Nejvyšší úroveň", + "move": "Přesunout", + "clear_selection": "Zrušit výběr", + "folders_moved": "{count, plural, one {Přesunuta # složka} few {Přesunuty # složky} other {Přesunuto # složek}}", + "move_partial_error": "{count, plural, one {# složku} few {# složky} other {# složek}} se nepodařilo přesunout", + "subfolder_of": "Ve složce {name}", + "subfolder_name": "Název podsložky", + "new_folder_name": "Název složky", + "rename": "Přejmenovat", + "change_icon": "Změnit ikonu", + "count_mode": "Počítadlo pro {name}", + "count_mode_default": "Počítadlo: výchozí", + "count_mode_unread": "Jen nepřečtené", + "count_mode_all": "Nepřečtené a celkem", + "count_mode_none": "Bez počítadla", + "delete": "Odstranit", + "confirm_delete": "Opravdu chcete odstranit složku „{name}“? Zprávy v této složce budou přesunuty do Koše.", + "create": "Vytvořit", + "cancel": "Zrušit", + "no_folders": "Žádné vlastní složky", + "cannot_delete_role": "Složku se standardní rolí nelze odstranit. Nejprve tuto roli odstraňte.", + "folder_created": "Složka byla vytvořena", + "folder_renamed": "Složka byla přejmenována", + "folder_deleted": "Složka byla odstraněna", + "role_updated": "Role složky byla aktualizována", + "error_create": "Složku se nepodařilo vytvořit", + "error_rename": "Složku se nepodařilo přejmenovat", + "error_delete": "Složku se nepodařilo odstranit", + "error_delete_has_children": "Složku nelze odstranit: stále obsahuje podsložky. Nejprve je odstraňte nebo přesuňte.", + "error_delete_has_email": "Složku nelze odstranit: stále obsahuje zprávy. Nejprve je přesuňte nebo odstraňte.", + "error_role": "Roli složky se nepodařilo aktualizovat" + }, + "advanced": { + "title": "Pokročilé", + "description": "Pokročilé možnosti a nastavení pro vývojáře", + "debug_mode": { + "label": "Režim ladění", + "description": "Povolit podrobné protokolování pro řešení problémů" + }, + "debug_categories": { + "description": "Vyberte kategorie pro protokolování. Vypněte kategorie, které nepotřebujete, pro snížení množství záznamů v konzoli.", + "jmap": "Klient JMAP", + "jmap_description": "Operace se schránkami, načítání zpráv a požadavky protokolu JMAP", + "calendar": "Kalendář", + "calendar_description": "Události kalendáře, importy a plánování zpráv", + "tasks": "Úkoly", + "tasks_description": "Vytváření úkolů kalendáře, načítání a aktualizace", + "auth": "Ověřování", + "auth_description": "Přihlašování, TOTP, výměna tokenů a správa relací", + "filters": "Filtry", + "filters_description": "Pravidla filtrů Sieve a skripty pro automatickou odpověď", + "email": "Zobrazení e-mailu", + "email_description": "Vykreslování e-mailů, zpracování TNEF a označování jako přečtené", + "push": "Push oznámení", + "push_description": "Konfigurace a doručování push oznámení", + "contacts": "Kontakty a adresáře", + "contacts_description": "Synchronizace kontaktů, operace s adresářem a důvěryhodní odesílatelé" + }, + "settings_sync": { + "label": "Synchronizace nastavení", + "description": "Synchronizovat nastavení mezi prohlížeči a zařízeními" + }, + "sender_favicons": { + "label": "Favikony odesílatelů", + "description": "Zobrazovat ikony webových stránek jako profilové obrázky firemních odesílatelů", + "refresh_label": "Obnovit ikony odesílatelů", + "refresh_description": "Znovu načíst ikony webů, například když odesílatel změnil svou ikonu", + "refresh_button": "Obnovit", + "refresh_done": "Ikony odesílatelů se načtou znovu" + }, + "show_avatars_in_junk": { + "label": "Zobrazovat avatary ve spamu", + "description": "Zobrazovat profilové fotky a ikony odesílatelů ve spamu. Ve výchozím stavu vypnuto, aby phishingové e-maily nepůsobily důvěryhodně." + }, + "keyboard_shortcuts": { + "label": "Klávesové zkratky", + "description": "Zobrazit dostupné klávesové zkratky", + "button": "Zobrazit zkratky" + }, + "refresh_cache": { + "label": "Obnovit data z mezipaměti", + "description": "Znovu načíst kontakty, kalendáře a složky ze serveru. Účty a přihlášení zůstanou zachovány – opraví zastaralé nebo chybné zobrazení bez odhlášení.", + "button": "Obnovit" + }, + "reset_settings": { + "label": "Resetovat nastavení", + "description": "Obnovit všechna nastavení na výchozí hodnoty", + "button": "Obnovit výchozí" + }, + "export_settings": { + "label": "Exportovat nastavení", + "description": "Stáhnout svá nastavení jako soubor JSON", + "button": "Exportovat" + }, + "import_settings": { + "label": "Importovat nastavení", + "description": "Nahrát nastavení ze souboru JSON", + "button": "Importovat" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Zdrojový kód" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Klikněte na spam a phishing dřív, než dorazí do vaší doručené pošty. Skutečnou poštu nechte projít. Když ztratíte všechny tři štíty, schránka padne.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Skutečná pošta" + }, + "click_to_block": "Klikněte pro zablokování", + "let_through": "Nechte projít", + "start": "Začít", + "play_again": "Hrát znovu", + "score": "Skóre", + "combo": "Bonus za sérii", + "shields_left": "Zbývá {count} z {total} štítů", + "seconds": "{seconds} s", + "best": "Rekord: {score}", + "new_best": "Nový rekord", + "held": "Schránka ubráněna", + "overrun": "Schránka padla", + "stats": { + "caught": "Zablokováno", + "delivered": "Doručeno", + "mistakes": "Chyby" + } + } + }, + "sidebar_apps": { + "title": "Aplikace postranního panelu", + "description": "Spravujte vlastní aplikace a odkazy v postranním panelu", + "keep_loaded": "Ponechat aplikace načtené", + "keep_loaded_description": "Udržovat vložené aplikace spuštěné na pozadí během přepínání, aby se předešlo jejich opětovnému načítání", + "managed_title": "Poskytnuto správcem", + "managed_description": "Tyto aplikace jsou nastaveny pro všechny a nelze je změnit.", + "manage_title": "Vlastní aplikace", + "manage_description": "Přidávat, upravovat nebo odebírat vlastní aplikace z postranního panelu" + }, + "contacts": { + "title": "Kontakty", + "description": "Import a export kontaktů", + "group_by_letter_label": "Seskupit podle prvního písmena", + "sort_by_last_name_label": "Řadit podle příjmení", + "sort_by_last_name_description": "Seřadit seznam kontaktů podle příjmení, aby členové rodiny byli pohromadě", + "group_by_letter_description": "Zobrazit záhlaví sekcí podle abecedy v seznamu kontaktů", + "import_label": "Importovat kontakty", + "import_description": "Importovat kontakty ze souboru vCard (.vcf)", + "export_label": "Exportovat kontakty", + "export_description": "Exportovat všechny kontakty jako soubor vCard (.vcf)", + "manage_title": "Adresáře", + "manage_description": "Přejmenovat své adresáře", + "no_address_books": "Nenalezeny žádné adresáře", + "categories_title": "Kategorie", + "categories_description": "Přejmenovat kategorie kontaktů", + "no_categories": "Nenalezeny žádné kategorie" + }, + "downloads": { + "title": "Stažené", + "description": "Přizpůsobte si, jak se pojmenovávají stažené e-maily a přílohy.", + "reset": "Obnovit výchozí", + "preview": "Náhled:", + "email_template": { + "label": "Název souboru e-mailu (.eml)", + "description": "Šablona použitá při exportu e-mailu nebo jeho přetažení do souborového systému. Přípona .eml se přidává automaticky." + }, + "attachment_template": { + "label": "Název souboru přílohy", + "description": "Šablona použitá při stahování nebo přetahování přílohy. Pokud vynecháte '{filename}' a '{ext}', původní přípona se zachová." + }, + "bundle_template": { + "label": "Název .zip souboru s více e-maily", + "description": "Šablona použitá při přetažení nebo stažení několika vybraných e-mailů jako jediného .zip archivu. Přípona .zip se přidává automaticky." + }, + "spaces": { + "label": "Mezery", + "description": "Nahradit mezery ve výsledném názvu souboru jiným znakem.", + "keep": "Zachovat mezery", + "underscore": "Nahradit za _", + "dash": "Nahradit za -" + }, + "lowercase": { + "label": "Malá písmena", + "description": "Vynutit celý název souboru malými písmeny." + }, + "strip_diacritics": { + "label": "Odstranit diakritiku", + "description": "Převést písmena s diakritikou na ASCII ekvivalenty (á → a, é → e). Užitečné pro nástroje, které špatně zpracovávají Unicode názvy." + }, + "collapse_separators": { + "label": "Sloučit opakované oddělovače", + "description": "Sloučit posloupnosti mezer, podtržítek nebo pomlček do jednoho znaku." + }, + "after_export": { + "label": "Po exportu", + "description": "Po exportu jako .eml lze e-mail volitelně přesunout.", + "keep": "Ponechat ve schránce", + "archive": "Přesunout do archivu", + "trash": "Přesunout do koše" + } + }, + "filters": { + "title": "Filtry e-mailů", + "description": "Vytvářejte pravidla pro automatické třídění, štítkování a správu příchozích e-mailů", + "add_rule": "Přidat pravidlo", + "no_rules": "Žádná pravidla filtrů", + "no_rules_description": "Vytvářejte pravidla pro automatickou organizaci příchozích e-mailů", + "vacation_active": "Automatická odpověď je aktivní", + "vacation_active_description": "Automatická odpověď je zapnuta pro příchozí zprávy", + "vacation_configure": "Konfigurovat", + "edit_rule": "Upravit pravidlo", + "new_rule": "Nové pravidlo", + "delete_rule": "Odstranit pravidlo", + "delete_confirm": "Opravdu chcete toto pravidlo odstranit?", + "enable": "Povolit", + "disable": "Zakázat", + "raw_editor": "Textový editor Sieve", + "raw_editor_warning": "Úprava samotného skriptu Sieve může rozbít vizuální úpravu pravidel. Zde provedené změny nahradí vizuálního průvodce.", + "validate": "Ověřit", + "validation_success": "Skript je platný", + "validation_error": "Skript obsahuje chyby", + "save": "Uložit pravidla", + "saving": "Ukládání...", + "saved": "Filtry byly uloženy", + "save_failed": "Uložení filtrů selhalo", + "loading": "Načítání filtrů...", + "not_supported": "Váš poštovní server nepodporuje e-mailové filtry.", + "rule_name": "Název pravidla", + "rule_name_placeholder": "např. Třídit newslettery", + "match_all": "Splňuje VŠECHNY podmínky", + "match_any": "Splňuje JAKOUKOLI podmínku", + "conditions": "Podmínky", + "add_condition": "Přidat podmínku", + "actions": "Akce", + "add_action": "Přidat akci", + "stop_processing": "Zastavit zpracování dalších pravidel", + "include_spam": "Přesouvat i zprávy označené jako spam", + "attachment_type_placeholder": "např. pdf, doc, jpg", + "value_placeholder_multi": "Hodnota (více oddělených čárkami)", + "condition_fields": { + "attachment": "Příloha", + "from": "Od", + "to": "Komu", + "cc": "Kopie", + "subject": "Předmět", + "header": "Vlastní hlavička", + "size": "Velikost", + "body": "Tělo zprávy", + "all": "Všechny zprávy" + }, + "comparators": { + "has_any": "je přítomna", + "has_type": "typu", + "contains": "obsahuje", + "not_contains": "neobsahuje", + "is": "je přesně", + "not_is": "není", + "starts_with": "začíná na", + "ends_with": "končí na", + "matches": "odpovídá vzoru", + "greater_than": "je větší než", + "less_than": "je menší než", + "address_is": "je adresa", + "domain_is": "má doménu" + }, + "action_types": { + "move": "Přesunout do složky", + "copy": "Kopírovat do složky", + "forward": "Přeposlat na", + "mark_read": "Označit jako přečtené", + "star": "Označit zprávu hvězdičkou", + "add_label": "Přidat štítek", + "discard": "Zahodit (tiše odstranit)", + "reject": "Odmítnout se zprávou", + "keep": "Ponechat v Doručených", + "stop": "Zastavit zpracování" + }, + "move_to_folder": "Vyberte složku", + "copy_to_folder": "Vyberte složku", + "forward_to": "Přeposlat na e-mailovou adresu", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Ponechat kopii", + "forward_limit": "Limit přeposlání na zprávu na tomto serveru: {count}. Další přeposlání se přeskočí.", + "forward_unavailable": "Tento server nepovoluje přeposílání. Akce přeposlání v pravidlech filtrů nemají žádný účinek.", + "reject_message": "Zpráva o odmítnutí", + "reject_placeholder": "Váš e-mail byl odmítnut", + "label_name": "Název štítku", + "label_placeholder": "Vyberte štítek", + "header_name": "Název hlavičky", + "header_placeholder": "např. X-Mailing-List", + "size_bytes": "Velikost v bajtech", + "size_placeholder": "např. 1000000", + "system_managed": "Pravidlo spravováno systémem", + "opaque_warning": "Tento skript byl upraven mimo vizuálního průvodce. Je k dispozici pouze přímá úprava Sieve.", + "open_sieve_editor": "Otevřít textový editor Sieve", + "fetch_error": "Nepodařilo se načíst filtry", + "expanded_view": "Rozšířené zobrazení", + "expanded_view_description": "Zobrazovat pravidla filtrů s podrobnými bloky podmínek a akcí", + "if": "Pokud", + "then": "Pak", + "match_all_conditions": "všechny splněny", + "match_any_condition": "jakékoli splněny", + "and": "a", + "or": "nebo", + "cancel": "Zrušit", + "confirm_delete": "Odstranit", + "rule_list": "Pravidla filtrů", + "drag_to_reorder": "Přetáhněte pro změnu pořadí", + "match_type": "Typ shody", + "reset_to_visual": "Resetovat na vizuálního průvodce", + "reset_warning": "Tímto zahodíte aktuální skript a začnete znovu.", + "confirm_reset": "Resetovat", + "validation_empty_name": "Název pravidla je vyžadován", + "validation_empty_conditions": "Je vyžadována alespoň jedna podmínka s hodnotou", + "validation_empty_actions": "Je vyžadována alespoň jedna akce", + "templates_section": "Začít ze šablony", + "template_newsletters": "Přesunout newslettery do složky", + "template_receipts": "Automaticky archivovat účtenky", + "template_important": "Označit důležité e-maily", + "template_notifications": "Filtrovat oznámení", + "sieve_editor": { + "title": "Editor skriptů Sieve", + "warning": "Úprava samotného skriptu Sieve může rozbít vizuální úpravu pravidel. Zde provedené změny nahradí vizuálního průvodce.", + "script_content": "Skript Sieve", + "valid": "Skript je platný", + "invalid": "Skript obsahuje chyby", + "save_warning": "Uložení přepíše všechna vizuální pravidla. Tuto akci nelze vrátit zpět. Potvrďte opětovným kliknutím na Uložit.", + "validating": "Ověřování...", + "validate": "Ověřit", + "cancel": "Zrušit", + "save": "Uložit", + "confirm_save": "Potvrdit uložení", + "validation_failed": "Požadavek na ověření selhal" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {1 podmínka} few {# podmínky} other {# podmínek}}", + "actions_count": "{count, plural, one {1 akce} few {# akce} other {# akcí}}" + }, + "origin_external": "Externí", + "managed_by_tooltip": "Spravováno aplikací {source}. Úpravy provádějte v příslušné aplikaci nebo použijte editor zdrojového kódu Sieve.", + "quick_rule_names": { + "move_sender": "Přesunout od {sender} do {folder}", + "move_domain": "Přesunout od @{domain} do {folder}", + "move_list": "Přesunout seznam {list} do {folder}", + "mark_read": "Označit od {sender} jako přečtené", + "tag": "Označit od {sender} štítkem {tag}", + "block": "Blokovat {sender}", + "custom": "Zprávy od {sender}", + "custom_plain": "Nové pravidlo", + "senders": "{count, plural, one {# odesílatele} few {# odesílatelů} many {# odesílatele} other {# odesílatelů}}" + }, + "suggestions": "Návrhy", + "suggest_subject": "Předmět „{subject}“", + "suggest_to": "Komu {address}", + "suggest_cc": "Kopie {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Použít i na zprávy, které už v této složce jsou", + "apply_existing_unsupported": "Na existující zprávy lze použít jen pravidla, která kontrolují odesílatele, příjemce, předmět nebo hlavičky a která přesouvají, kopírují, označují jako přečtené, hvězdičkou nebo štítkem.", + "period_title": "Časové období", + "period_start": "Datum zahájení", + "period_end": "Datum ukončení", + "validation_period_order": "Datum ukončení musí být po datu zahájení", + "period_toggle": "Aktivní jen v určitém období", + "period_hint": "Platí pro zprávy, které přijdou v tomto období. Prázdné pole ponechá daný konec otevřený.", + "validation_period_empty": "Zadejte datum zahájení nebo ukončení období", + "validation_period_invalid": "Zadejte pro období platné datum včetně času, nebo pole nechte prázdné", + "apply_existing_period": "Pravidlo s obdobím působí jen na zprávy, které v něm přijdou, a proto ho nelze použít na existující zprávy.", + "period_status_scheduled": "naplánováno", + "period_status_active": "aktivní", + "period_status_expired": "vypršelo" + }, + "templates": { + "title": "E-mailové šablony", + "description": "Vytvářejte opakovaně použitelné e-mailové šablony s proměnnými zástupnými symboly", + "add": "Nová šablona", + "edit": "Upravit šablonu", + "name": "Název šablony", + "name_placeholder": "např. Kontrolní zpráva", + "category": "Kategorie", + "category_placeholder": "např. Práce, Osobní", + "subject": "Předmět", + "subject_placeholder": "Předmět e-mailu", + "body": "Tělo zprávy", + "body_placeholder": "Text e-mailu...", + "recipients_placeholder": "email@example.com", + "identity": "Odeslat jako", + "default_identity": "Výchozí identita", + "favorite": "Oblíbené", + "cancel": "Zrušit", + "create": "Vytvořit", + "update": "Aktualizovat", + "confirm_delete": "Odstranit", + "no_templates": "Žádné šablony", + "manage": "Spravovat šablony", + "count": "{count, plural, one {1 šablona} few {# šablony} other {# šablon}}", + "export_import": "Export a import", + "export_import_description": "Vytvořte zálohu šablon nebo je přeneste do jiného zařízení", + "export": "Exportovat", + "import": "Importovat", + "validation": { + "empty": "Název šablony je vyžadován", + "too_long": "Název šablony může mít maximálně 200 znaků" + } + }, + "files": { + "display": { + "title": "Zobrazení", + "description": "Nakonfigurujte způsob zobrazení souborů a složek" + }, + "default_view": { + "label": "Výchozí zobrazení", + "description": "Vyberte rozložení do mřížky nebo seznamu", + "list": "Seznam", + "grid": "Mřížka" + }, + "default_sort": { + "label": "Výchozí řazení", + "description": "Vyberte výchozí řazení souborů", + "name": "Název", + "size": "Velikost", + "modified": "Změněno" + }, + "sort_direction": { + "label": "Směr řazení", + "description": "Vyberte vzestupné nebo sestupné pořadí", + "ascending": "Vzestupně", + "descending": "Sestupně" + }, + "icons": { + "title": "Ikony", + "description": "Nakonfigurujte vzhled ikon souborů" + }, + "show_icons": { + "label": "Zobrazovat ikony souborů", + "description": "Zobrazovat ikony vedle souborů a složek" + }, + "colored_icons": { + "label": "Barevné ikony", + "description": "Používat barevné ikony místo monochromatických" + }, + "show_thumbnails": { + "label": "Zobrazovat miniatury", + "description": "Zobrazovat náhledy obrázků místo ikon pro obrazové soubory" + }, + "behavior": { + "title": "Chování", + "description": "Nakonfigurujte chování prohlížeče souborů" + }, + "show_hidden": { + "label": "Zobrazovat skryté soubory", + "description": "Zobrazovat soubory a složky začínající tečkou" + }, + "preview": { + "label": "Náhled" + } + }, + "search_placeholder": "Hledat nastavení", + "search_clear": "Vymazat hledání", + "search_no_results": "Žádná odpovídající nastavení", + "scoped": { + "back": "Zpět na můj účet", + "managing": "Správa: {name}" + } + }, + "errors": { + "page_error_title": "Něco se pokazilo", + "page_error_description": "Došlo k neočekávané chybě. Zkuste to znovu nebo se vraťte na domovskou stránku.", + "sidebar_error": "Nepodařilo se načíst poštovní schránky", + "email_list_error": "Nepodařilo se načíst zprávy", + "viewer_error_title": "Zprávu nelze zobrazit", + "viewer_error_description": "Během vykreslování této zprávy došlo k problému. Může obsahovat nepodporovaný obsah.", + "composer_error": "Editor zpráv se nepodařilo načíst", + "settings_error_title": "Nastavení jsou nedostupná", + "settings_error_description": "Nepodařilo se načíst nastavení. Vaše předvolby možná nebudou uloženy.", + "try_again": "Zkusit znovu", + "reload": "Obnovit", + "reload_emails": "Obnovit zprávy", + "reload_settings": "Obnovit nastavení", + "retry": "Opakovat", + "go_home": "Přejít do doručené pošty" + }, + "context_menu": { + "reply": "Odpovědět", + "reply_all": "Odpovědět všem", + "forward": "Přeposlat", + "mark_read": "Označit jako přečtené", + "mark_unread": "Označit jako nepřečtené", + "star": "Označit hvězdičkou", + "unstar": "Odebrat hvězdičku", + "pin": "Připnout", + "unpin": "Odepnout", + "move_to": "Přesunout do...", + "copy_to_account": "Kopírovat do účtu...", + "remove_from_folder": "Odebrat z této složky", + "archive": "Archivovat", + "delete": "Odstranit", + "mark_as_spam": "Nahlásit spam", + "not_spam": "Není spam", + "tag": "Štítek", + "items_selected": "{count} vybraných zpráv", + "forward_as_attachments": "Přeposlat jako přílohy", + "edit_draft": "Upravit koncept", + "cancel_scheduled_send": "Zrušit odeslání", + "reschedule_send": "Přeplánovat", + "cancel_and_edit": "Zrušit a upravit", + "cancel_and_compose_again": "Zrušit a napsat znovu", + "rules": { + "title": "Pravidla", + "move_from": "Vždy přesouvat zprávy od {sender}", + "move_from_domain": "Vždy přesouvat zprávy od @{domain}", + "move_from_list": "Vždy přesouvat zprávy z tohoto seznamu", + "mark_read_from": "Vždy označit zprávy od {sender} jako přečtené", + "tag_with": "Vždy označit štítkem", + "block": "{count, plural, one {Blokovat odesílatele} few {Blokovat # odesílatele} many {Blokovat # odesílatele} other {Blokovat # odesílatelů}}", + "no_junk": "Tento účet nemá složku nevyžádané pošty", + "create": "Vytvořit pravidlo…", + "manage": "Spravovat pravidla", + "senders_count": "{count, plural, one {# odesílatele} few {# odesílatelů} many {# odesílatele} other {# odesílatelů}}", + "cross_account": "Vyberte zprávy z jednoho účtu", + "opaque_hint": "Vaše filtry byly upraveny ručně. Otevřete nastavení filtrů" , + "loading": "Načítání pravidel…", + "load_failed": "Pravidla se nepodařilo načíst", + "none": "Zatím žádná pravidla", + "new_mail_only": "Pouze pro nové zprávy", + "running": "Spouštění „{name}“ ve složce {folder}…" + }, + "find_emails_from": "Najít e-maily od {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Označit složku jako přečtenou", + "mark_folder_tree_read": "Označit složku a podsložky jako přečtené", + "mark_all_folders_read": "Označit všechny složky jako přečtené", + "new_subfolder": "Nová podsložka...", + "new_folder": "Nová složka...", + "rename": "Přejmenovat...", + "share": "Sdílet...", + "import_email": "Importovat .eml nebo .zip...", + "empty_folder": "Vyprázdnit složku", + "empty_folder_generic": "Vyprázdnit složku", + "delete_folder": "Smazat složku", + "refresh": "Obnovit", + "hide_shared_account": "Skrýt z postranního panelu", + "mute_notifications": "Ztlumit oznámení", + "unmute_notifications": "Zapnout oznámení", + "mute_account_notifications": "Ztlumit oznámení tohoto účtu", + "unmute_account_notifications": "Zapnout oznámení tohoto účtu", + "reset_folder_notifications": "Obnovit oznámení složek", + "mark_all_confirm_title": "Označit všechny složky jako přečtené", + "mark_all_confirm_message": "Označit všechny nepřečtené zprávy ve vašem osobním účtu jako přečtené?", + "delete_confirm_title": "Smazat složku", + "delete_confirm_message": "Trvale smazat složku \"{name}\"? Tuto akci nelze vrátit.", + "prompt_new_subfolder": "Zadejte název nové podsložky.", + "prompt_new_folder": "Zadejte název nové složky.", + "prompt_rename": "Zadejte nový název této složky.", + "placeholder_folder_name": "Název složky", + "create": "Vytvořit", + "rename_confirm": "Přejmenovat", + "toast_marked_read": "Složka označena jako přečtená", + "toast_marked_read_count": "{count, plural, one {1 zpráva označena jako přečtená} few {# zprávy označeny jako přečtené} other {# zpráv označeno jako přečtených}}", + "toast_already_read": "Žádné nepřečtené zprávy", + "toast_marked_all_read": "Všechny složky označeny jako přečtené", + "toast_emptied": "Složka vyprázdněna", + "toast_folder_created": "Složka vytvořena", + "toast_folder_renamed": "Složka přejmenována", + "toast_folder_deleted": "Složka smazána", + "toast_error_mark_read": "Nepodařilo se označit jako přečtené", + "toast_error_empty": "Nepodařilo se vyprázdnit složku", + "toast_error_create": "Nepodařilo se vytvořit složku", + "toast_error_rename": "Nepodařilo se přejmenovat složku", + "toast_error_delete": "Nepodařilo se smazat složku", + "toast_error_delete_has_children": "Složka obsahuje podsložky. Nejprve je odstraňte.", + "toast_error_delete_has_email": "Složka není prázdná. Nejprve ji vyprázdněte.", + "delete_with_contents_title": "Složka není prázdná", + "delete_with_contents_message": "Složka „{name}“ stále obsahuje {count, plural, one {1 zprávu} few {# zprávy} other {# zpráv}}. Smazat složku i všechny její zprávy? Tuto akci nelze vrátit.", + "delete_with_contents_confirm": "Smazat složku i zprávy" + }, + "shortcuts": { + "title": "Klávesové zkratky", + "tip": "Kdykoli stiskněte ? pro zobrazení této nápovědy", + "sections": { + "navigation": "Navigace", + "actions": "Akce zprávy", + "global": "Globální", + "threads": "Vlákna", + "composer": "Editor zpráv" + }, + "navigation": { + "next_email": "Další zpráva", + "previous_email": "Předchozí zpráva", + "open_email": "Otevřít zprávu", + "close_email": "Zavřít / Zrušit výběr", + "go_to_folder": "Přejít do složky", + "next_unread": "Další nepřečtený e-mail", + "no_more_unread": "Níže v seznamu už nejsou žádné nepřečtené e-maily" + }, + "actions": { + "reply": "Odpovědět", + "reply_all": "Odpovědět všem", + "forward": "Přeposlat", + "star": "Přepnout hvězdičku", + "archive": "Archivovat", + "delete": "Odstranit", + "mark_unread": "Označit jako nepřečtené", + "mark_read": "Označit jako přečtené", + "toggle_spam": "Nahlásit spam / Není spam" + }, + "global": { + "compose": "Napsat novou zprávu", + "search": "Přejít do hledání", + "help": "Zobrazit zkratky", + "refresh": "Obnovit zprávy", + "select_all": "Vybrat vše", + "global_search": "Hledat ve všem", + "extend_selection": "Rozšířit výběr dolů / nahoru" + }, + "threads": { + "expand_collapse": "Rozbalit/sbalit vlákno", + "select_mountain_view": "Vybrat konverzaci" + }, + "composer": { + "send": "Odeslat e-mail", + "schedule_send": "Naplánovat odeslání", + "template_picker": "Otevřít výběr šablon" + } + }, + "threads": { + "messages_one": "{count} zpráva", + "messages_other": "{count} zpráv", + "messages_tooltip": "{count, plural, one {1 zpráva v této konverzaci} few {# zprávy v této konverzaci} other {# zpráv v této konverzaci}}", + "expand": "Rozbalit konverzaci", + "collapse": "Sbalit konverzaci", + "loading": "Načítání konverzace...", + "mark_read": "Označit konverzaci jako přečtenou", + "mark_unread": "Označit konverzaci jako nepřečtenou", + "archive": "Archivovat konverzaci", + "delete": "Odstranit konverzaci", + "star": "Označit konverzaci hvězdičkou", + "unstar": "Odebrat hvězdičku z konverzace", + "toggle_thread": "Přepnout zobrazení vlákna" + }, + "identities": { + "modal_title": "Správa identit odesílatele", + "create_new": "Vytvořit novou identitu", + "edit_identity": "Upravit identitu", + "delete_confirm": "Odstranit tuto identitu? Tuto akci nelze vrátit zpět.", + "cannot_delete": "Tuto identitu nelze odstranit", + "primary_identity": "Hlavní", + "set_as_primary": "Nastavit jako hlavní", + "no_identities": "Identity nenalezeny", + "display": { + "reply_to": "Odpovědět na:", + "bcc": "Skrytá kopie:", + "signature": "Podpis:", + "preview": "Náhled:" + }, + "validation_errors": { + "invalid_emails": "Neplatné e-mailové adresy: {emails}", + "unknown_error": "Neznámá chyba" + }, + "form": { + "name_label": "Zobrazované jméno", + "name_placeholder": "např. Pracovní e-mail, Soukromý", + "name_required": "Jméno je vyžadováno", + "email_label": "E-mailová adresa", + "email_placeholder": "vas.email@example.com", + "email_required": "E-mailová adresa je vyžadována", + "email_invalid": "Zadejte platnou e-mailovou adresu", + "email_immutable": "E-mailovou adresu nelze po vytvoření změnit", + "reply_to_label": "Odpovědět na (volitelně)", + "reply_to_placeholder": "jiny@example.com", + "bcc_label": "Automatická skrytá kopie (volitelně)", + "bcc_placeholder": "archiv@example.com", + "text_signature_label": "Textový podpis", + "html_signature_label": "HTML podpis", + "signature_byte_counter": "{bytes} / {max} bajtů", + "signature_byte_limit_reached": "Dosažen limit serveru", + "save": "Uložit identitu", + "cancel": "Zrušit", + "creating": "Vytváření...", + "updating": "Aktualizování..." + }, + "sub_address": { + "button_tooltip": "Použít subadresu", + "popover_title": "Přidat štítek subadresy", + "tag_input_placeholder": "Zadejte štítek (např. nakupy)", + "preview_label": "Náhled:", + "recent_tags": "Nedávné štítky", + "suggested_tags": "Doporučené", + "use_address": "Použít tuto adresu", + "invalid_tag": "Štítek může obsahovat pouze písmena, číslice a pomlčky", + "tag_too_long": "Štítek může mít maximálně 30 znaků", + "help_text": "Zprávy odeslané na adresu uzivatel{delimiter}stitek@domena.cz budou doručeny do vaší doručené pošty", + "validation": { + "empty": "Štítek nesmí být prázdný", + "too_long": "Štítek může mít maximálně {max} znaků", + "invalid_chars": "Štítek může obsahovat pouze písmena, číslice a pomlčky" + } + }, + "badge": { + "sent_via": "přes", + "sub_address_tag": "Odesláno pomocí subadresy: {tag}", + "identity_name": "Odesláno pomocí identity: {name}", + "identity_short": "přes {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Odstranit", + "delete_confirm_title": "Odstranit identitu" + }, + "templates": { + "picker_title": "Vyberte šablonu", + "search_placeholder": "Hledat šablony...", + "section_favorites": "Oblíbené", + "section_recent": "Nedávné", + "section_uncategorized": "Ostatní", + "no_templates": "Zatím žádné šablony", + "no_results": "Nenalezeny žádné šablony", + "fill_placeholders": "Vyplnit hodnoty proměnných", + "enter_value": "Zadejte hodnotu...", + "preview": "Náhled", + "insert_with_values": "Vložit s hodnotami", + "insert_raw": "Vložit beze změn", + "copy_suffix": "(kopie)", + "placeholder": "Proměnná", + "placeholders": { + "recipient_name": "Jméno příjemce", + "company": "Název společnosti", + "date": "Aktuální datum", + "day_of_week": "Den v týdnu", + "sender_name": "Vaše jméno" + } + }, + "contacts": { + "directory": { + "title": "Adresář organizace", + "search_placeholder": "Hledat jméno nebo e-mailovou adresu", + "refresh": "Obnovit adresář", + "retry": "Zkusit znovu", + "loading": "Načítání adresáře…", + "unavailable_title": "Žádný adresář", + "unavailable_hint": "Váš poštovní server nenabízí adresář účtů.", + "forbidden_title": "Adresář není dostupný", + "forbidden_hint": "Váš poštovní server tomuto účtu nepovoluje procházet adresář. Obraťte se na správce.", + "error_title": "Adresář se nepodařilo načíst", + "error_hint": "Zkontrolujte připojení a zkuste to znovu.", + "empty_title": "Adresář je prázdný", + "empty_hint": "Váš poštovní server neuvádí žádné účty s e-mailovou adresou.", + "no_match": "Hledání neodpovídá nikdo v adresáři", + "refresh_failed": "Adresář se nepodařilo obnovit. Zobrazuje se naposledy načtený seznam.", + "select_page": "Vybrat všechny na této stránce", + "deselect_page": "Zrušit výběr na této stránce", + "select_entry": "Vybrat {name}", + "range": "{from}–{to} z {total}", + "page": "Stránka {page} z {count}", + "previous_page": "Předchozí stránka", + "next_page": "Další stránka", + "picker_title": "Vybrat z adresáře", + "cancel": "Zrušit", + "selected_count": "Vybráno: {count}", + "add_to": "Přidat do Komu", + "add_cc": "Přidat do Kopie", + "add_bcc": "Přidat do Skrytá kopie", + "select_hint": "Vyberte osobu a zobrazte podrobnosti", + "write_email": "Napsat e-mail", + "add_to_contacts": "Přidat do kontaktů", + "email": "E-mail", + "read_only_note": "Položky adresáře pocházejí z vašeho poštovního serveru a nelze je zde upravit.", + "type": { + "individual": "Osoba", + "group": "Skupina", + "resource": "Prostředek", + "location": "Místo", + "other": "Jiné" + } + }, + "title": "Kontakty", + "search_placeholder": "Hledat kontakty...", + "create_new": "Nový kontakt", + "no_category": "Bez kategorie", + "rename_category": "Přejmenovat kategorii", + "category_name_label": "Název kategorie", + "category_renamed": "Kategorie byla přejmenována", + "category_rename_failed": "Přejmenování kategorie selhalo", + "category_added": "Kontakt přidán do {name}", + "category_added_plural": "{count} kontaktů bylo přidáno do {name}", + "empty_state": "Žádné kontakty", + "empty_state_title": "Žádné kontakty", + "empty_state_subtitle": "Vytvořte první kontakt nebo importujte ze souboru vCard", + "empty_search": "Hledání neodpovídají žádné kontakty", + "empty_search_hint": "Zkuste použít jiný hledaný výraz", + "empty_filtered": "Žádné kontakty neodpovídají filtrům", + "empty_filtered_hint": "Zkuste filtry upravit nebo zrušit", + "clear_search": "Vymazat hledání", + "import_vcard": "Importovat vCard", + "delete_confirm_title": "Odstranit kontakt", + "delete_confirm": "Opravdu chcete odstranit tento kontakt?", + "local_mode": "Kontakty jsou uloženy lokálně (server nepodporuje JMAP Contacts)", + "back_to_contacts": "Zpět na kontakty", + "back_to_email": "Zpět na e-mail", + "tabs": { + "all": "Všechny", + "groups": "Skupiny" + }, + "shared": { + "title": "Sdílené" + }, + "address_books": { + "create": "Nový adresář", + "created": "Adresář vytvořen", + "create_failed": "Nepodařilo se vytvořit adresář", + "title": "Moje adresáře", + "shared_prefix": "Sdílené: {name}", + "moved": "Kontakt přesunut do {name}", + "moved_plural": "{count} kontaktů bylo přesunuto do {name}", + "move_failed": "Přesun kontaktu selhal", + "address_book": "Adresář", + "rename": "Přejmenovat adresář", + "name_label": "Název adresáře", + "renamed": "Adresář byl přejmenován", + "rename_failed": "Přejmenování adresáře selhalo", + "default": "Výchozí", + "set_default": "Nastavit jako výchozí", + "default_updated": "Výchozí adresář aktualizován", + "set_default_failed": "Nepodařilo se nastavit výchozí adresář", + "manage": "Spravovat adresáře", + "share": "Sdílet adresář", + "new_contact_in_book": "Nový kontakt v tomto adresáři", + "delete": "Smazat adresář", + "confirm_delete": "Smazat „{name}\"? Všechny kontakty v tomto adresáři budou odstraněny.", + "deleted": "Adresář smazán", + "delete_failed": "Adresář se nepodařilo smazat" + }, + "detail": { + "emails": "E-mailové adresy", + "phones": "Telefonní čísla", + "organizations": "Organizace", + "addresses": "Adresy", + "notes": "Poznámky", + "titles": "Pozice a role", + "online_services": "Online služby", + "anniversaries": "Výročí", + "personal_info": "Osobní informace", + "languages": "Jazyky", + "categories": "Kategorie", + "related_contacts": "Související kontakty", + "crypto_keys": "Kryptografické klíče", + "cert_issuer": "Vystavitel", + "cert_expires": "Vyprší", + "cert_expired": "Vypršela platnost", + "cert_fingerprint": "Otisk", + "cert_algorithm": "Algoritmus", + "import_to_smime": "Importovat do S/MIME", + "cert_already_imported": "Již importováno do S/MIME", + "cert_imported": "Certifikát byl importován do úložiště S/MIME", + "cert_import_failed": "Import certifikátu selhal", + "no_contact_selected": "Vyberte kontakt pro zobrazení podrobností", + "compose_email": "Napsat e-mail", + "copy_email": "Kopírovat e-mail", + "copy_phone": "Kopírovat telefon", + "copy_url": "Kopírovat URL", + "copied": "Zkopírováno do schránky", + "copy_failed": "Kopírování do schránky selhalo", + "created": "Vytvořeno", + "updated": "Poslední aktualizace", + "timezone": "Časové pásmo", + "anniversary_birth": "Narozeniny", + "anniversary_death": "Úmrtí", + "anniversary_wedding": "Výročí svatby", + "anniversary_other": "Ostatní", + "personal_expertise": "Odbornost", + "personal_hobby": "Koníčky", + "personal_interest": "Zájmy", + "personal_other": "Ostatní", + "gender": "Pohlaví", + "gender_masculine": "Muž", + "gender_feminine": "Žena", + "gender_other": "Jiné", + "gender_none": "Neuvádí se", + "gender_unknown": "Neznámé", + "calendar": "Kalendář", + "calendar_uri": "URL kalendáře", + "scheduling_uri": "URL pro plánování", + "freebusy_uri": "URL dostupnosti", + "section_contact": "Kontaktní údaje", + "section_work": "Práce", + "section_personal": "Osobní", + "email_default_label": "E-mail", + "phone_default_label": "Telefon", + "address_default_label": "Adresa", + "online_service_default_label": "Online", + "organization_label": "Organizace", + "title_label": "Pozice", + "role_label": "Role", + "language_label": "Jazyk", + "related_default_label": "Vztahy", + "more_actions": "Další akce", + "age_years": "{count, plural, one {1 rok} few {# roky} other {# let}}", + "years_since": "{count, plural, one {1 rok} few {# roky} other {# let}}" + }, + "activity": { + "recent_emails": "Nedávné e-maily", + "upcoming_events": "Nadcházející události", + "no_emails": "Žádné nedávné e-maily", + "no_events": "Žádné nadcházející události", + "no_subject": "(Bez předmětu)", + "no_title": "(Bez názvu)", + "load_failed": "Nepodařilo se načíst", + "unknown_sender": "Neznámý odesílatel", + "all_day": "Celý den" + }, + "form": { + "create_title": "Nový kontakt", + "edit_title": "Upravit kontakt", + "section_address_book": "Adresář", + "select_address_book": "Vyberte adresář...", + "section_identity": "Jméno a identita", + "contact_type": "Typ kontaktu", + "type_person": "Osoba", + "type_organization": "Organizace", + "section_work": "Práce a organizace", + "prefix": "Titul", + "prefix_placeholder": "Dr., Pan, Paní", + "given_name": "Jméno", + "middle_name": "Prostřední jméno", + "surname": "Příjmení", + "suffix": "Přípona", + "suffix_placeholder": "Jr., Sr., Ph.D.", + "nickname": "Přezdívka", + "nickname_placeholder": "Přezdívka", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Telefon", + "phone_placeholder": "+420 123 456 789", + "phone_type": "Typ", + "phone_voice": "Hlasový", + "phone_cell": "Mobilní", + "phone_fax": "Fax", + "phone_pager": "Pager", + "phone_video": "Video", + "phone_text": "SMS", + "organization": "Organizace", + "organization_placeholder": "Název společnosti", + "department": "Oddělení", + "department_placeholder": "Oddělení", + "job_title": "Pozice", + "job_title_placeholder": "např. Softwarový inženýr", + "role": "Role", + "role_placeholder": "např. Vedoucí týmu", + "addresses": "Adresy", + "add_address": "Přidat adresu", + "street": "Ulice", + "city": "Město", + "region": "Stát / Kraj", + "postcode": "PSČ", + "country": "Země", + "online_services": "Online služby", + "add_online_service": "Přidat online službu", + "url_placeholder": "https://...", + "service_placeholder": "Služba", + "anniversaries": "Výročí", + "add_anniversary": "Přidat datum", + "anniversary_birth": "Narozeniny", + "anniversary_wedding": "Výročí svatby", + "anniversary_death": "Úmrtí", + "anniversary_other": "Ostatní", + "personal_info": "Osobní informace", + "add_personal_info": "Přidat záznam", + "personal_info_placeholder": "např. Fotografie", + "personal_expertise": "Odbornost", + "personal_hobby": "Koníčky", + "personal_interest": "Zájmy", + "personal_other": "Ostatní", + "level": "Úroveň", + "level_high": "Vysoká", + "level_medium": "Střední", + "level_low": "Nízká", + "categories": "Kategorie", + "categories_placeholder": "např. Rodina, Přátelé, Kolegové", + "categories_hint": "Pište pro vyhledání nebo přidání kategorií", + "category_add": "Přidat", + "note": "Poznámky", + "note_placeholder": "Přidat poznámku...", + "gender": "Pohlaví", + "gender_sex": "Biologické pohlaví", + "gender_male": "Muž", + "gender_female": "Žena", + "gender_other": "Jiné", + "gender_none": "Neuvádí se", + "gender_unknown": "Neznámé", + "gender_identity": "Genderová identita", + "gender_identity_placeholder": "Genderová identita...", + "calendar": "Kalendář", + "calendar_uri": "URL kalendáře", + "scheduling_uri": "URL pro plánování", + "freebusy_uri": "URL volný/obsazený", + "context_work": "Pracovní", + "context_private": "Soukromé", + "add_email": "Přidat e-mail", + "add_phone": "Přidat telefon", + "save": "Uložit", + "cancel": "Zrušit", + "creating": "Vytváření...", + "updating": "Aktualizování...", + "name_required": "Zadejte jméno, příjmení nebo organizaci", + "email_invalid": "Zadejte platnou e-mailovou adresu", + "email_error_inline": "Neplatný formát e-mailové adresy", + "save_failed": "Uložení kontaktu selhalo", + "delete": "Odstranit", + "upload_photo": "Nahrát fotku", + "remove_photo": "Odebrat fotku", + "photo_hint": "JPG nebo PNG, max. 10 MB. Velikost se upraví.", + "photo_too_large": "Obrázek je moc velký (max. 10 MB)", + "photo_invalid": "Neplatný formát obrázku", + "change_photo": "Změnit" + }, + "groups": { + "create": "Nová skupina", + "edit": "Upravit skupinu", + "empty": "Žádné skupiny", + "delete_confirm_title": "Odstranit skupinu", + "delete_confirm": "Opravdu chcete odstranit tuto skupinu?", + "name_label": "Název skupiny", + "name_placeholder": "např. Tým, Rodina", + "name_required": "Název skupiny je vyžadován", + "save_failed": "Uložení skupiny selhalo", + "members_label": "Členové", + "search_members": "Hledat kontakty k přidání...", + "no_members": "Tato skupina nemá žádné členy", + "member_count": "{count, plural, =0 {Žádní členové} one {1 člen} few {# členové} other {# členů}}", + "send_email": "Poslat e-mail skupině", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "Tato skupina nemá žádné členy s e-mailovou adresou." + }, + "import": { + "title": "Importovat kontakty", + "drop_hint": "Klikněte pro výběr souboru vCard", + "file_types": "soubory .vcf nebo .vcard", + "no_contacts": "V souboru nebyly nalezeny žádné kontakty", + "parse_error": "Zpracování souboru vCard selhalo", + "found": "{count, plural, one {Nalezen 1 kontakt} few {Nalezeny # kontakty} other {Nalezeno # kontaktů}}", + "duplicate": "Duplikát", + "select_all": "Vybrat vše", + "deselect_all": "Zrušit výběr všeho", + "selected": "{count, plural, one {1 vybrán} few {# vybrány} other {# vybráno}}", + "import_button": "Importovat", + "importing": "Importování...", + "success": "{count, plural, one {Importován 1 kontakt} few {Importovány # kontakty} other {Importováno # kontaktů}}", + "failed": "Import selhal", + "close": "Zavřít", + "file_too_large": "Soubor je příliš velký (max. 5 MB)" + }, + "export": { + "title": "Exportovat kontakty", + "success": "{count, plural, one {Exportován 1 kontakt} few {Exportovány # kontakty} other {Exportováno # kontaktů}}" + }, + "bulk": { + "selected": "{count, plural, one {1 vybrán} few {# vybrány} other {# vybráno}}", + "select_all": "Vybrat vše", + "delete": "Odstranit", + "delete_confirm_title": "Odstranit kontakty", + "delete_confirm": "Odstranit {count, plural, one {1 kontakt} few {# kontakty} other {# kontaktů}}?", + "deleted": "{count, plural, one {Odstraněn 1 kontakt} few {Odstraněny # kontakty} other {Odstraněno # kontaktů}}", + "add_to_group": "Přidat do skupiny", + "choose_group": "Vyberte skupinu", + "adding_contacts": "Přidávání {count, plural, one {1 kontaktu} few {# kontaktů} other {# kontaktů}}", + "added_to_group": "Kontakty byly přidány do skupiny", + "export": "Exportovat", + "clear": "Zrušit výběr" + }, + "toast": { + "created": "Kontakt byl vytvořen", + "updated": "Kontakt byl aktualizován", + "deleted": "Kontakt byl odstraněn", + "error_create": "Vytvoření kontaktu selhalo", + "error_update": "Aktualizace kontaktu selhala", + "error_delete": "Odstranění kontaktu selhalo" + }, + "context_menu": { + "open": "Otevřít", + "edit": "Upravit", + "send_email": "Poslat e-mail", + "add_to_group": "Přidat do skupiny", + "export_vcard": "Exportovat jako vCard", + "delete": "Smazat", + "call": "Zavolat", + "duplicate": "Duplikovat", + "print": "Vytisknout" + }, + "filters": { + "toggle": "Filtry", + "select": "Vybrat", + "clear": "Vymazat", + "close": "Zavřít", + "title": "Pokročilé filtry", + "organization": "Firma", + "organization_placeholder": "např. Acme Corp", + "job_title": "Pozice", + "job_title_placeholder": "např. Designér", + "location": "Lokalita", + "location_placeholder": "Město nebo země", + "email_domain": "E-mailová doména", + "email_domain_placeholder": "example.com", + "birthday_month": "Narozeniny v", + "any_month": "Kterýkoli měsíc", + "has_email": "Má e-mail", + "has_phone": "Má telefon", + "has_photo": "Má fotku", + "sort_by": "Řadit podle", + "sort_first_name": "Jméno", + "sort_last_name": "Příjmení" + }, + "open_categories": "Otevřít kategorie" + }, + "calendar": { + "title": "Kalendář", + "back_to_email": "Zpět na poštu", + "back_to_month": "Zpět na měsíc", + "my_calendars": "Kalendáře", + "birthday_calendar": "Narozeniny", + "mini_calendar_change": "Kliknutím změníte měsíc", + "views": { + "month": "Měsíc", + "week": "Týden", + "day": "Den", + "agenda": "Agenda", + "today": "Dnes", + "month_hint": "Měsíc (m)", + "week_hint": "Týden (w)", + "day_hint": "Den (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Úkoly", + "tasks_hint": "Úkoly (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# dřívější událost} few {# dřívější události} many {# dřívější události} other {# dřívějších událostí}}", + "hidden_after": "{count, plural, one {# pozdější událost} few {# pozdější události} many {# pozdější události} other {# pozdějších událostí}}", + "show_all_hours": "Zobrazit všechny hodiny", + "show_display_hours": "Zobrazit jen {start} – {end}", + "create": "Vytvořit událost", + "edit": "Upravit událost", + "delete": "Odstranit událost", + "details": "Podrobnosti události", + "no_events": "Žádné události", + "all_day": "Celý den", + "more": "+{count} dalších", + "show_more": "Zobrazit více", + "show_less": "Zobrazit méně", + "no_title": "(Bez názvu)", + "resize": "Změnit velikost události", + "duplicate": "Duplikovat", + "today_header": "Dnes", + "tomorrow_header": "Zítra", + "agenda_show_earlier": "Zobrazit dřívější události", + "agenda_loading": "Načítání…", + "agenda_range_start": "Události od {date}", + "agenda_range_end": "Události do {date}", + "export_ics": "Exportovat jako .ics", + "copy_title": "Kopírovat název", + "copy_link": "Kopírovat odkaz na schůzku", + "go_to_today": "Přejít na dnešek", + "new_all_day_event": "Nová celodenní událost", + "new_event": "Nová událost", + "new_task": "Nový úkol" + }, + "detail": { + "add_note": "Přidat poznámku...", + "save_note": "Uložit", + "note_saved": "Poznámka přidána", + "open_link": "Otevřít odkaz", + "copy_location": "Kopírovat místo", + "meeting_link": "Odkaz na schůzku", + "tentative": "Nezávazně", + "cancelled": "Zrušeno", + "delete_confirm": "Odstranit tuto událost?" + }, + "form": { + "title": "Název", + "description": "Popis", + "location": "Místo", + "meeting_link": "Odkaz na schůzku", + "start_date": "Datum zahájení", + "end_date": "Datum ukončení", + "start_time": "Čas zahájení", + "end_time": "Čas ukončení", + "all_day_event": "Celodenní událost", + "calendar_select": "Kalendář", + "save": "Uložit", + "cancel": "Zrušit", + "delete_confirm": "Opravdu chcete odstranit tuto událost?", + "color": "Barva" + }, + "participants": { + "availability": { + "title": "Dostupnost", + "free": "Volný", + "busy": "Zaneprázdněn", + "tentative": "Předběžně", + "unknown": "Není uživatelem tohoto serveru", + "checking": "Ověřuje se…" + }, + "title": "Účastníci", + "add": "Přidat účastníka", + "organizer": "Organizátor", + "attendee": "Účastník", + "accepted": "Přijato", + "declined": "Odmítnuto", + "tentative": "Nezávazně", + "needs_action": "Vyžaduje akci", + "remove": "Odstranit", + "edit": "Upravit", + "email_placeholder": "Přidejte e-mailovou adresu nebo vyhledejte kontakty", + "send_invitations": "Odeslat pozvánky účastníkům", + "status_summary": "{accepted} přijato, {pending} čeká", + "invited_by": "Pozvánka od {name}", + "respond_below": "Odpovězte pomocí tlačítek níže", + "rsvp_label": "Vaše odpověď", + "cancel_notification": "Účastníci budou upozorněni na zrušení", + "you_organizer": "Jste organizátorem", + "you_attendee": "Jste účastníkem", + "no_participants": "Žádní účastníci", + "count": "{count, plural, one {1 účastník} few {# účastníci} other {# účastníků}}" + }, + "recurrence": { + "title": "Opakování", + "none": "Neopakuje se", + "daily": "Denně", + "weekly": "Týdně", + "monthly": "Měsíčně", + "yearly": "Ročně", + "every_n_days": "Každých {count} dní", + "every_n_weeks": "Každých {count} týdnů", + "every_n_months": "Každých {count} měsíců", + "until": "Do", + "occurrences": "{count} opakování", + "custom": "Vlastní…", + "edit_custom": "Upravit vlastní opakování", + "every_n_years": "Každých {count} let", + "on_days": "v {days}", + "on_day_n": "v den {day}", + "on_the_nth": "{nth} {day}", + "in_month": "v měsíci {month}", + "nth_1": "první", + "nth_2": "druhý", + "nth_3": "třetí", + "nth_4": "čtvrtý", + "nth_last": "poslední", + "editor_freq_day": "Den", + "editor_freq_week": "Týden", + "editor_freq_month": "Měsíc", + "editor_freq_year": "Rok", + "editor_repeats_on": "Opakuje se", + "editor_every": "Každých", + "editor_unit_days": "dní", + "editor_unit_weeks": "týdnů", + "editor_unit_months": "měsíců", + "editor_unit_years": "let", + "editor_on_day": "v den", + "editor_on_the": "v", + "editor_in": "v měsíci", + "editor_ends": "Končí", + "editor_never": "Nikdy", + "editor_ends_on": "Dne", + "editor_ends_after": "Po", + "editor_occurrences": "opakováních" + }, + "recurrence_scope": { + "edit_title": "Upravit opakující se událost", + "delete_title": "Odstranit opakující se událost", + "description": "Toto je opakující se událost. Které události chcete upravit?", + "this_event": "Pouze tuto událost", + "this_and_future": "Tuto a všechny následující události", + "all_events": "Všechny události", + "cancel": "Zrušit", + "save": "Uložit", + "delete": "Odstranit", + "rsvp_title": "Odpovědět na opakující se událost", + "rsvp_description": "Toto je opakující se událost. Na které události se vaše odpověď vztahuje?", + "respond": "Odpovědět" + }, + "alerts": { + "title": "Připomenutí", + "none": "Žádné připomenutí", + "at_time": "V čase události", + "minutes_before": "{count, plural, one {1 minutu před} few {# minuty před} other {# minut před}}", + "hours_before": "{count, plural, one {1 hodinu před} few {# hodiny před} other {# hodin před}}", + "days_before": "{count, plural, one {1 den před} few {# dny před} other {# dnů před}}", + "weeks_before": "{count, plural, one {1 týden před} few {# týdny před} other {# týdnů před}}", + "unit_minutes_before": "minut před", + "unit_hours_before": "hodin před", + "unit_days_before": "dní před", + "unit_weeks_before": "týdnů před", + "add": "Přidat připomenutí", + "remove": "Odebrat připomenutí", + "amount": "Množství připomenutí", + "unit": "Jednotka připomenutí", + "action": "Typ připomenutí", + "action_display": "Oznámení", + "action_email": "E-mail", + "by_email": "{label} e-mailem", + "kept": "{count, plural, one {Zachová se # další připomenutí, které zde nelze upravit} few {Zachovají se # další připomenutí, která zde nelze upravit} other {Zachová se # dalších připomenutí, která zde nelze upravit}}", + "other": "{count, plural, one {# další připomenutí} few {# další připomenutí} other {# dalších připomenutí}}", + "calendar_defaults": "Výchozí připomenutí kalendáře", + "customize": "Přizpůsobit", + "use_defaults": "Použít výchozí připomenutí kalendáře" + }, + "settings": { + "limit_hours": "Omezit zobrazené hodiny", + "limit_hours_desc": "V denním a týdenním zobrazení ukazovat jen tyto hodiny. Události mimo ně jsou dostupné přes šipky nahoře a dole u každého dne.", + "visible_hours": "Zobrazené hodiny", + "visible_hours_start": "Začátek zobrazených hodin", + "visible_hours_end": "Konec zobrazených hodin", + "hide_non_working_days": "Skrýt nepracovní dny", + "hide_non_working_days_desc": "Vynechat z týdenního zobrazení dny, kdy nepracujete. Jejich události se dál zobrazují v měsíčním, denním a agendovém zobrazení.", + "working_days": "Pracovní dny", + "title": "Nastavení kalendáře", + "default_view": "Výchozí zobrazení", + "week_starts_on": "Týden začíná v", + "time_format": "Formát času", + "default_calendar": "Výchozí kalendář", + "default_reminder": "Výchozí připomenutí", + "time_format_12h": "12hodinový", + "time_format_24h": "24hodinový", + "notifications_enabled": "Oznámení o událostech", + "notifications_enabled_desc": "Zobrazovat upozornění na nadcházející události v kalendáři", + "notification_sound": "Zvuk oznámení", + "notification_sound_desc": "Přehrát zvukové upozornění pro kalendář", + "invitation_parsing": "Analyzovat e-mailové pozvánky", + "invitation_parsing_desc": "Rozpoznávat pozvánky kalendáře v přílohách e-mailů a zobrazovat akce kalendáře", + "show_time_in_month_view": "Zobrazit čas v měsíčním zobrazení", + "show_time_in_month_view_desc": "Zobrazovat časy událostí v měsíčním zobrazení kalendáře", + "show_week_numbers": "Zobrazit čísla týdnů", + "show_week_numbers_desc": "Zobrazovat čísla týdnů v minikalendáři", + "calendar_free_scroll": "Volné posouvání", + "calendar_free_scroll_desc": "Plynule procházet měsíce, týdny a dny místo jednoho období najednou", + "enable_tasks": "Povolit úkoly", + "enable_tasks_desc": "Zobrazovat zobrazení úkolů v kalendáři pro správu úkolů", + "show_tasks_on_calendar": "Zobrazit úkoly v kalendáři", + "show_tasks_on_calendar_desc": "Zobrazovat úkoly v měsíčním, týdenním a denním zobrazení kalendáře", + "hover_preview": "Náhled události po přejetí", + "hover_preview_desc": "Zobrazovat vyskakovací okno s podrobnostmi při najetí myší na události v kalendáři", + "hover_preview_instant": "Okamžitě", + "hover_preview_delay_500ms": "Zpoždění 0,5 sekundy", + "hover_preview_delay_1s": "Zpoždění 1 sekunda", + "hover_preview_delay_2s": "Zpoždění 2 sekundy", + "hover_preview_off": "Vypnuto", + "organizer_identity": "Organizovat pozvánky jako", + "organizer_identity_desc": "Adresa kalendáře použitá jako organizátor, když zvete lidi na události.", + "organizer_identity_failed": "Nepodařilo se změnit adresu organizátora", + "show_birthday_calendar": "Kalendář narozenin", + "show_birthday_calendar_desc": "Zobrazovat virtuální kalendář s narozeninami z vašich kontaktů" + }, + "days": { + "monday": "Pondělí", + "tuesday": "Úterý", + "wednesday": "Středa", + "thursday": "Čtvrtek", + "friday": "Pátek", + "saturday": "Sobota", + "sunday": "Neděle", + "mon": "Po", + "tue": "Út", + "wed": "St", + "thu": "Čt", + "fri": "Pá", + "sat": "So", + "sun": "Ne" + }, + "notifications": { + "event_created": "Událost byla vytvořena", + "event_updated": "Událost byla aktualizována", + "event_deleted": "Událost byla odstraněna", + "calendar_created": "Kalendář byl vytvořen", + "calendar_deleted": "Kalendář byl odstraněn", + "event_move_error": "Přesun události selhal", + "event_resize_error": "Změna velikosti události selhala", + "alert_title": "Nadcházející událost", + "alert_now": "Začíná nyní", + "alert_in_minutes": "Za {count} min", + "invitation_sent": "Pozvánky byly odeslány", + "invitations_denied_title": "Pozvánky nelze odeslat", + "invitations_denied": "Server odmítl odeslat pozvánky: {reason}. Uložit událost bez jejich odeslání?", + "save_without_invitations": "Uložit bez pozvánek", + "rsvp_updated": "Odpověď byla aktualizována", + "rsvp_error": "Aktualizace odpovědi selhala", + "event_duplicated": "Událost byla duplikována", + "event_error": "Uložení události selhalo", + "task_due": "Termín úkolu", + "event_exported": "Událost exportována", + "title_copied": "Název zkopírován", + "link_copied": "Odkaz zkopírován" + }, + "status": { + "loading_calendars": "Načítání kalendářů...", + "loading_events": "Načítání událostí..." + }, + "quick_create": { + "placeholder": "Název nové události", + "aria_label": "Rychlé vytvoření události", + "add_title": "Přidat název", + "more_options": "Další možnosti" + }, + "nav_prev": "Předchozí", + "nav_next": "Další", + "import": { + "title": "Importovat kalendář", + "tab_file": "Soubor", + "tab_url": "URL", + "select_file": "Vyberte soubor .ics", + "drop_file": "nebo sem přetáhněte soubor", + "supported_formats": "Podporuje soubory iCalendar (.ics)", + "url_description": "Zadejte adresu URL externího zdroje iCalendar (.ics) pro import událostí.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Podporuje adresy URL iCalendar (.ics)", + "fetch": "Načíst", + "invalid_url": "Zadejte platnou adresu URL", + "url_fetch_failed": "Načtení kalendáře z adresy URL selhalo", + "parsing": "Analyzování souboru kalendáře...", + "parsed_events": "Nalezeno {count} událostí", + "no_events": "V souboru nebyly nalezeny žádné události", + "select_all": "Vybrat vše", + "deselect_all": "Zrušit výběr všeho", + "target_calendar": "Importovat do kalendáře", + "import_button": "Importovat vybrané", + "importing": "Importování událostí...", + "success": "Úspěšně importováno {count} událostí", + "error": "Import kalendáře selhal", + "file_too_large": "Soubor překračuje limit 10 MB", + "invalid_format": "Neplatný formát souboru kalendáře" + }, + "webcal_action": { + "title": "Otevřít odkaz kalendáře", + "description": "Jak chcete použít \"{name}\"?", + "import_title": "Jednorázově importovat", + "import_description": "Načíst události nyní a zkopírovat je do jednoho z vašich kalendářů.", + "subscribe_title": "Odebírat", + "subscribe_description": "Automaticky synchronizovat tento kalendář jako samostatný kalendář.", + "cancel": "Zrušit" + }, + "management": { + "title": "Správa kalendáře", + "description": "Vytvářejte, přejmenovávejte a přizpůsobujte si své kalendáře. Klikněte pravým tlačítkem na kalendář v postranním panelu pro rychlou změnu jeho barvy.", + "name": "Název", + "name_placeholder": "Název kalendáře", + "color": "Barva", + "change_color": "Změnit barvu", + "random_color": "Nová náhodná barva", + "add_calendar": "Přidat kalendář", + "edit": "Upravit", + "delete": "Odstranit", + "save": "Uložit", + "create": "Vytvořit", + "cancel": "Zrušit", + "default": "Výchozí", + "confirm_delete": "Odstranit \"{name}\"? Všechny události v tomto kalendáři budou odstraněny.", + "confirm_clear": "Vymazat všechny události z \"{name}\"? Tuto akci nelze vrátit zpět.", + "set_default": "Nastavit jako výchozí", + "default_updated": "Výchozí kalendář aktualizován", + "error_default": "Nepodařilo se nastavit výchozí kalendář", + "clear_events": "Vymazat události", + "events_cleared": "Vymazáno {count} událostí", + "error_clear": "Vymazání událostí kalendáře selhalo", + "calendar_created": "Kalendář byl vytvořen", + "calendar_updated": "Kalendář byl aktualizován", + "calendar_deleted": "Kalendář byl odstraněn", + "color_updated": "Barva kalendáře byla aktualizována", + "error_create": "Vytvoření kalendáře selhalo", + "error_update": "Aktualizace kalendáře selhala", + "error_delete": "Odstranění kalendáře selhalo", + "caldav_url": "URL CalDAV", + "copy_url": "Kopírovat URL CalDAV", + "url_copied": "URL CalDAV zkopírováno do schránky", + "share": "Sdílet kalendář", + "new_event_in_calendar": "Nová událost v tomto kalendáři", + "shared_with": "Sdíleno s: {names}", + "shared_with_count": "{count, plural, one {Sdíleno s 1 osobou} few {Sdíleno s # osobami} many {Sdíleno s # osoby} other {Sdíleno s # osobami}}", + "move_up": "Posunout nahoru", + "move_down": "Posunout dolů", + "kind": "Obsah", + "kind_events": "Události", + "kind_tasks": "Úkoly", + "kind_both": "Události a úkoly", + "kind_hint": "Určuje, které aplikace tento kalendář uvidí při synchronizaci přes CalDAV. Později to nelze změnit.", + "default_alerts": "Výchozí připomenutí", + "default_alerts_title": "Výchozí připomenutí pro \"{name}\"", + "default_alerts_hint": "Nové události v tomto kalendáři dostanou tato připomenutí, pokud je v události nezměníte. Změna zde neovlivní existující události. E-mailová připomenutí odesílá poštovní server.", + "default_alerts_timed": "Události s časem", + "default_alerts_all_day": "Celodenní události", + "default_alerts_updated": "Výchozí připomenutí aktualizována" + }, + "subscription": { + "title": "Odběr iCal", + "section_title": "Odběry iCal", + "description": "Odebírejte externí zdroj iCalendar. Události budou automaticky synchronizovány do samostatného kalendáře. Podporuje adresy URL https:// a webcal://.", + "url_label": "URL kalendáře", + "url_placeholder": "https://example.com/calendar.ics nebo webcal://...", + "name_label": "Název kalendáře", + "name_placeholder": "např. Státní svátky", + "color_label": "Barva", + "refresh_interval": "Interval obnovy", + "interval_15": "Každých 15 minut", + "interval_30": "Každých 30 minut", + "interval_60": "Každou hodinu", + "interval_360": "Každých 6 hodin", + "interval_1440": "Denně", + "subscribe": "Odebírat", + "subscribing": "Přihlašování k odběru...", + "save": "Uložit změny", + "saving": "Ukládání...", + "edit": "Upravit", + "edit_title": "Upravit odběr", + "updated": "Aktualizováno \"{name}\"", + "update_error": "Aktualizace odběru selhala", + "invalid_url": "Zadejte platnou adresu URL", + "success": "Odebíráte \"{name}\"", + "error": "Přidání odběru selhalo", + "refresh": "Obnovit nyní", + "refresh_success": "Odběr byl obnoven", + "refresh_error": "Obnovení odběru selhalo", + "unsubscribe": "Zrušit odběr", + "confirm_delete": "Zrušit odběr \"{name}\"? Kalendář a všechny jeho události budou odstraněny.", + "deleted": "Odběr byl odstraněn", + "delete_error": "Odstranění odběru selhalo", + "last_refreshed": "Poslední aktualizace: {time}" + }, + "tasks": { + "label": "Úkoly", + "no_tasks": "Žádné úkoly", + "no_title": "(Bez názvu)", + "mark_complete": "Označit jako dokončené", + "mark_incomplete": "Označit jako nedokončené", + "filter_all": "Všechny", + "filter_pending": "Čekající", + "filter_completed": "Dokončené", + "filter_overdue": "Po termínu", + "show_completed": "Zobrazit dokončené", + "create": "Nový úkol", + "edit": "Upravit úkol", + "title_placeholder": "Název úkolu", + "description_placeholder": "Přidat popis...", + "due_date": "Termín", + "include_time": "Zahrnout čas", + "priority": "Priorita", + "priority_none": "Žádná", + "priority_high": "Vysoká", + "priority_medium": "Střední", + "priority_low": "Nízká", + "progress": "Stav", + "progress_needs_action": "Vyžaduje akci", + "progress_in_process": "Probíhá", + "progress_completed": "Dokončeno", + "progress_cancelled": "Zrušeno", + "calendar": "Kalendář", + "alert": "Připomenutí", + "alert_none": "Žádné", + "alert_at_time": "V čase termínu", + "alert_5min": "5 minut před", + "alert_15min": "15 minut před", + "alert_30min": "30 minut před", + "alert_1hr": "1 hodinu před", + "alert_1day": "1 den před", + "delete": "Odstranit", + "cancel": "Zrušit", + "save": "Uložit", + "quick_add_placeholder": "Přidat úkol...", + "due_today": "Dnes", + "due_tomorrow": "Zítra", + "overdue": "Po termínu" + }, + "months": { + "jan": "led", + "feb": "úno", + "mar": "bře", + "apr": "dub", + "may": "kvě", + "jun": "čvn", + "jul": "čvc", + "aug": "srp", + "sep": "zář", + "oct": "říj", + "nov": "lis", + "dec": "pro", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Otevřít nabídku", + "toolbar": { + "more": "Další akce", + "select_view": "Změnit zobrazení, nyní {view}" + }, + "other_calendars": "Další kalendáře", + "calendar_options": "Možnosti pro {name}" + }, + "advanced_search": { + "title": "Pokročilé hledání", + "from": "Od", + "from_placeholder": "E-mailová adresa nebo jméno odesílatele", + "to": "Komu", + "to_placeholder": "E-mailová adresa nebo jméno příjemce", + "subject": "Předmět", + "subject_placeholder": "Předmět obsahuje...", + "body": "Tělo zprávy", + "body_placeholder": "Tělo zprávy obsahuje...", + "folder": "Složka", + "all_folders": "Všechny složky", + "all_folders_except_spam_trash": "Všechny složky kromě spamu a koše", + "has_attachment": "Obsahuje přílohu", + "date_after": "Po", + "date_before": "Před", + "size_min": "Větší než (KB)", + "size_max": "Menší než (KB)", + "starred": "S hvězdičkou", + "unread": "Nepřečtené", + "read": "Přečtené", + "yes": "Ano", + "no": "Ne", + "clear": "Vymazat", + "clear_all": "Vymazat vše", + "filters_active": "{count} filtr", + "filters_active_plural": "{count} filtry", + "toggle_filters": "Více", + "search_hint": "Použijte pokročilé filtry pro přesné vyhledávání", + "advanced_filters_tooltip": "Pokročilé filtry hledání", + "results_found": "{count, plural, =0 {Nenalezeny žádné výsledky} one {Nalezen 1 výsledek} few {Nalezeny # výsledky} other {Nalezeno # výsledků}}", + "results_found_more": "Nalezeno více než {count} výsledků", + "suggestions_recent": "Nedávná hledání", + "suggestions_people": "Lidé", + "suggestions_from": "Hledat e-maily od {address}", + "suggestions_to": "Hledat e-maily pro {address}", + "suggestions_remove_recent": "Odebrat z nedávných hledání", + "has_words": "Obsahuje slova", + "size": "Velikost", + "date_within": "Datum", + "search_in": "Hledat v", + "has_attachment_check": "Má přílohu", + "search": "Hledat" + }, + "welcome": { + "title": "Vítejte ve své poštovní schránce", + "tip_compose": "Stiskněte c pro napsání nové zprávy", + "tip_shortcuts": "Stiskněte ? pro zobrazení všech klávesových zkratek", + "tip_sidebar": "Kalendář, Kontakty a Nastavení najdete v postranním panelu", + "tip_settings": "Přizpůsobte si aplikaci v Nastavení", + "tip_compose_touch": "Klepnutím na tlačítko s tužkou napíšete nový e-mail", + "tip_sidebar_touch": "Kalendář, Kontakty a Nastavení najdete na liště dole", + "got_it": "Rozumím", + "settings": "Nastavení", + "dismiss": "Zavřít", + "start_tour": "Spustit průvodce" + }, + "demo_welcome": { + "title": "Vítejte v Bulwark Mail", + "description": "Prozkoumejte plně funkčního webového e-mailového klienta - přímo v prohlížeči. Všechna data zůstávají na vašem zařízení, takže můžete bez obav vše otestovat.", + "feature_email": "Čtěte a pište e-maily", + "feature_organize": "Štítky, hvězdičky a složky", + "feature_shortcuts": "Klávesové zkratky", + "feature_privacy": "100% soukromá demoverze", + "hint": "Klikněte na libovolnou zprávu vlevo pro začátek, nebo využijte průvodce níže." + }, + "files": { + "office_edit": "Upravit dokument", + "office_loading": "Otevírání editoru dokumentů…", + "office_error": "Editor dokumentů se nepodařilo otevřít.", + "office_close": "Zavřít editor", + "open_in_new_tab": "Otevřít na nové kartě", + "title": "Soubory", + "search_placeholder": "Hledat soubory...", + "empty_state_title": "Žádné soubory", + "upload_files": "Nahrát soubory", + "new_folder": "Nová složka", + "new_folder_name": "Název složky", + "rename": "Přejmenovat", + "rename_title": "Přejmenovat", + "new_name": "Nový název", + "name_forbidden_chars": "Server nepovoluje v názvech tyto znaky: {chars}", + "name_reserved": "Tento název je na serveru vyhrazen.", + "delete": "Odstranit", + "delete_confirm_title": "Odstranit položku", + "delete_confirm_message": "Opravdu chcete odstranit \"{name}\"? Tuto akci nelze vrátit zpět.", + "download": "Stáhnout", + "name": "Název", + "size": "Velikost", + "modified": "Změněno", + "type": "Typ", + "folder": "Složka", + "file": "Soubor", + "uploading": "Nahrávání...", + "upload_success": "{count, plural, one {Nahrán 1 soubor} few {Nahrány # soubory} other {Nahráno # souborů}}", + "upload_canceled": "Nahrávání zrušeno", + "upload_canceled_partial": "Před zrušením {count, plural, one {byl nahrán 1 soubor} few {byly nahrány # soubory} many {bylo nahráno # souboru} other {bylo nahráno # souborů}}", + "upload_error": "Nahrání souboru selhalo", + "create_folder_success": "Složka byla vytvořena", + "create_folder_error": "Vytvoření složky selhalo", + "delete_success": "Úspěšně odstraněno", + "delete_error": "Odstranění selhalo", + "rename_success": "Úspěšně přejmenováno", + "rename_error": "Přejmenování selhalo", + "download_error": "Stahování selhalo", + "not_available": "Úložiště souborů není na tomto serveru k dispozici", + "cancel": "Zrušit", + "create": "Vytvořit", + "save": "Uložit", + "no_results": "Hledání neodpovídají žádné soubory", + "batch_delete_confirm_message": "Opravdu chcete odstranit {count, plural, one {1 položku} few {# položky} other {# položek}}? Tuto akci nelze vrátit zpět.", + "batch_delete_success": "{count, plural, one {Odstraněna 1 položka} few {Odstraněny # položky} other {Odstraněno # položek}}", + "grid_view": "Zobrazení mřížky", + "list_view": "Zobrazení seznamu", + "details": "Podrobnosti", + "path": "Cesta", + "preview": "Náhled", + "preview_error": "Náhled se nepodařilo načíst", + "cut": "Vyjmout", + "copy": "Kopírovat", + "paste": "Vložit", + "move_success": "{count, plural, one {Přesunuta 1 položka} few {Přesunuty # položky} other {Přesunuto # položek}}", + "move_error": "Přesun selhal", + "paste_success": "Úspěšně vloženo", + "paste_error": "Vložení selhalo", + "new_text_file": "Nový textový soubor", + "file_name": "Název souboru", + "retry": "Opakovat", + "refresh": "Obnovit", + "duplicate": "Duplikovat", + "duplicate_success": "Úspěšně duplikováno", + "duplicate_error": "Duplikování selhalo", + "share": "Sdílet", + "shared": "Sdíleno", + "shared_with_me": "Sdíleno se mnou", + "shared_by": "Sdílí {name}", + "create_file_success": "Soubor byl vytvořen", + "create_file_error": "Vytvoření souboru selhalo", + "favorites": "Oblíbené", + "recent": "Nedávné", + "show_in_folder": "Zobrazit ve složce", + "remove_from_recent": "Odebrat z nedávných", + "upload_folder": "Nahrát složku", + "file_too_large": "\"{name}\" překračuje maximální velikost souboru ({max})", + "undo": "Zpět", + "undo_success": "Akce vrácena zpět", + "undo_error": "Akci nelze vrátit zpět", + "toolbar": "Akce se soubory", + "file_list": "Soubory a složky", + "context_menu": "Akce", + "settings_ascending": "Vzestupně", + "settings_descending": "Sestupně", + "disabled_title": "Funkce Soubory je zakázána správcem", + "disabled_description": "Nahrávání velkých souborů přes WebDAV může způsobit nestabilitu Stalwart/RocksDB, včetně pádů z důvodu nedostatku paměti a nevratného zaplnění disku. Odstraněné soubory nemusí být okamžitě odstraněny z úložiště. Tato funkce se nedoporučuje v produkčním prostředí.", + "no_accounts": "Žádné připojené účty.", + "open_folder_tree": "Otevřít strom složek", + "migration_title": "Aktualizace vašich souborů…", + "migration_description": "Uspořádání složek a souborů do správné struktury. Toto proběhne pouze jednou.", + "new": "Nový", + "my_files": "Moje soubory", + "folders": "Složky", + "files_heading": "Soubory", + "open": "Otevřít", + "star": "Přidat do oblíbených", + "unstar": "Odebrat z oblíbených", + "expand_folder": "Rozbalit {name}", + "collapse_folder": "Sbalit {name}", + "sidebar_hint": "Zde najdete oblíbené složky a otevřené soubory.", + "breadcrumbs": "Cesta ke složce", + "show_path": "Zobrazit skryté složky v cestě", + "selection_actions": "Akce pro vybrané položky", + "selected_count": "{count, plural, one {Vybrána 1 položka} few {Vybrány # položky} many {Vybráno # položky} other {Vybráno # položek}}", + "clear_selection": "Zrušit výběr", + "select_all": "Vybrat vše", + "select_item": "Vybrat {name}", + "more_actions": "Další akce pro {name}", + "paste_here": "Vložit do této složky", + "paste_count": "{count, plural, one {Vložit 1 položku} few {Vložit # položky} many {Vložit # položky} other {Vložit # položek}}", + "sort_by": "Seřadit podle", + "view_mode": "Zobrazení", + "clear_search": "Vymazat hledání", + "no_results_for": "V této složce nic neodpovídá „{query}“", + "download_count": "{count, plural, one {Stáhnout 1 soubor} few {Stáhnout # soubory} many {Stáhnout # souboru} other {Stáhnout # souborů}}", + "delete_count": "{count, plural, one {Odstranit 1 položku} few {Odstranit # položky} many {Odstranit # položky} other {Odstranit # položek}}", + "close_details": "Zavřít podrobnosti", + "file_kind": "Soubor {ext}", + "folder_summary": "{folders, plural, =0 {Žádné složky} one {1 složka} few {# složky} many {# složky} other {# složek}}, {files, plural, =0 {žádné soubory} one {1 soubor} few {# soubory} many {# souboru} other {# souborů}}", + "details_hint": "Vyberte soubor nebo složku a zobrazí se podrobnosti.", + "sharing": "Sdílení", + "shared_with_count": "{count, plural, one {Sdíleno s 1 osobou} few {Sdíleno s # osobami} many {Sdíleno s # osoby} other {Sdíleno s # osobami}}", + "only_you": "Pouze vy", + "empty_folder_title": "Tato složka je prázdná", + "empty_folder_description": "Přetáhněte sem soubory nebo je nahrajte ze svého zařízení.", + "empty_folder_read_only": "Do této složky nemůžete přidávat soubory.", + "choose_account": "Vyberte účet", + "drop_to_upload": "Pusťte pro nahrání do {folder}", + "uploading_count": "Nahrávání {current} z {total}", + "dismiss": "Zavřít", + "zoom_in": "Přiblížit", + "zoom_out": "Oddálit", + "zoom_reset": "Přizpůsobit obrazovce", + "rotate": "Otočit", + "close_preview": "Zavřít" + }, + "smime": { + "your_certificates": "Vaše certifikáty", + "your_certificates_desc": "Importujte a spravujte své certifikáty S/MIME pro podepisování a šifrování e-mailů", + "recipient_certificates": "Certifikáty příjemců", + "recipient_certificates_desc": "Veřejné certifikáty pro šifrování e-mailů odesílaných příjemcům", + "identity_bindings": "Vazby klíčů na identity", + "identity_bindings_desc": "Svažte certifikáty S/MIME s e-mailovými identitami", + "defaults_title": "Výchozí", + "defaults_desc": "Nakonfigurujte výchozí chování pro podepisování a šifrování", + "import_pkcs12": "Importovat PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importovat certifikát", + "no_certificates": "Zatím nebyly importovány žádné certifikáty", + "no_recipient_certs": "Žádné certifikáty příjemců", + "expires": "Vyprší", + "expired": "Vypršela platnost", + "bound_to": "Vázáno k", + "no_key_bound": "Žádné", + "lock": "Uzamknout klíč", + "unlock": "Odemknout klíč", + "details": "Zobrazit podrobnosti", + "delete": "Odstranit", + "encrypt_by_default": "Výchozí šifrování", + "encrypt_by_default_desc": "Automaticky šifrovat e-maily, pokud mají všichni příjemci certifikáty", + "remember_unlocked": "Pamatovat si odemčené klíče", + "remember_unlocked_desc": "Ponechat klíče odemčené po dobu trvání této relace prohlížeče", + "sign_default_for": "Výchozí podepisování pro", + "enter_p12_passphrase": "Zadejte heslo PKCS#12", + "p12_passphrase_desc": "Zadejte heslo chránící tento soubor certifikátu", + "enter_storage_passphrase": "Nastavte heslo úložiště", + "storage_passphrase_desc": "Vyberte heslo pro ochranu tohoto klíče při uložení v prohlížeči", + "next": "Další", + "import": "Importovat", + "unlock_key": "Odemknout klíč", + "unlock_key_desc": "Zadejte heslo úložiště pro odemčení tohoto klíče pro podepisování nebo dešifrování", + "passphrase_placeholder": "Zadejte heslo", + "confirm_passphrase_placeholder": "Potvrďte heslo", + "passphrase_mismatch": "Hesla se neshodují", + "cancel": "Zrušit", + "processing": "Zpracování…", + "close": "Zavřít", + "certificate_details": "Podrobnosti o certifikátu", + "cert_subject": "Subjekt", + "cert_issuer": "Vystavitel", + "cert_email": "E-mail", + "cert_serial": "Sériové číslo", + "cert_validity": "Platnost", + "cert_fingerprint": "Otisk (SHA-256)", + "cert_algorithm": "Algoritmus", + "cert_capabilities": "Možnosti", + "cert_source": "Zdroj", + "cert_expired": "Tento certifikát vypršel", + "cert_not_yet_valid": "Tento certifikát ještě není platný", + "cap_sign": "Podepisování", + "cap_encrypt": "Šifrování", + "cap_none": "Žádné", + "show_passphrase": "Zobrazit heslo", + "hide_passphrase": "Skrýt heslo", + "sign_toggle": "Podepsat", + "encrypt_toggle": "Šifrovat", + "missing_recipient_certs": "Chybí certifikáty pro: {emails}", + "missing_sender_cert": "Chybí certifikát vázaný k této identitě", + "status_encrypted_ok": "Tato zpráva byla zašifrována", + "status_encrypted_no_key": "Tato zpráva je zašifrována, ale nebyl nalezen odpovídající klíč", + "status_encrypted_failed": "Nepodařilo se dešifrovat tuto zprávu", + "status_signed_valid": "Podpis byl ověřen", + "status_signed_invalid": "Ověření podpisu selhalo", + "status_signed_expired_cert": "Podepsáno certifikátem, jehož platnost vypršela", + "status_signed_self_signed": "Podpis je platný, ale certifikát je vydán sám sobě (nedůvěryhodný)", + "status_signed_mismatch": "Podpis je platný, ale podepisující se neshoduje s odesílatelem", + "status_unsupported": "Nepodporovaný formát S/MIME", + "auto_import_signer_certs": "Automaticky importovat certifikáty podepisujících", + "auto_import_signer_certs_desc": "Automaticky ukládat certifikáty z ověřených podepsaných e-mailů pro budoucí šifrování", + "export": "Exportovat", + "enter_export_passphrase": "Nastavte heslo pro export", + "export_passphrase_desc": "Vyberte heslo pro ochranu exportovaného souboru PKCS#12", + "export_storage_desc": "Zadejte heslo úložiště pro dešifrování klíče k exportu", + "incorrect_passphrase": "Nesprávné heslo" + }, + "tour": { + "step_counter": "Krok {current} z {total}", + "skip": "Přeskočit průvodce", + "back": "Zpět", + "next": "Další", + "finish": "Dokončit", + "take_a_tour": "Prozkoumejte rozhraní v krátkém průvodci", + "restart_title": "Úvodní průvodce", + "restart_desc": "Přehrát průvodce rozhraním krok za krokem", + "restart_button": "Spustit průvodce znovu", + "show_on_new_devices_title": "Zobrazit na nových zařízeních", + "show_on_new_devices_desc": "Přehrát uvítací banner a průvodce při prvním přihlášení na novém zařízení, i když jste je již dokončili jinde", + "sidebar_title": "Vaše poštovní schránky", + "sidebar_desc": "Toto je postranní panel se složkami. Kliknutím na libovolnou schránku zobrazíte její zprávy. Můžete vytvářet složky, přetahovat zprávy mezi nimi a okamžitě vidět počet nepřečtených e-mailů.", + "compose_title": "Napsat zprávu", + "compose_desc": "Kliknutím sem napíšete novou zprávu. Můžete přidávat příjemce, přílohy a využívat formátovaný text.", + "search_title": "Prohledávat poštu", + "search_desc": "Vyhledávejte podle odesílatele, předmětu nebo obsahu. Kliknutím na ikonu filtru využijete pokročilé možnosti, jako jsou časová období, přílohy a zprávy označené hvězdičkou.", + "email_list_title": "Seznam zpráv", + "email_list_desc": "Zde se zobrazují zprávy. Kliknutím na jednu z nich si ji přečtete napravo. Pomocí zaškrtávacích políček můžete vybrat více zpráv a hromadně je přesunout, odstranit nebo označit štítkem.", + "email_viewer_title": "Panel čtení", + "email_viewer_desc": "Zde se otevírá vybraná zpráva. Pomocí tlačítek na panelu nástrojů můžete odpovědět, přeposlat ji, archivovat nebo odstranit. Můžete také označovat zprávy hvězdičkou nebo přidávat barevné štítky.", + "keywords_title": "Barevné štítky", + "keywords_desc": "Organizujte poštu pomocí barevných štítků. Přetažením zprávy na štítek ji označíte, nebo klikněte na zprávu pravým tlačítkem a štítky přiřaďte.", + "calendar_title": "Kalendář", + "calendar_desc": "Přepněte se do kalendáře pro správu událostí. Můžete vytvářet události, nastavovat připomenutí a přepínat mezi zobrazením dne, týdne nebo měsíce.", + "contacts_title": "Kontakty", + "contacts_desc": "Zde se nachází váš adresář. Importujte kontakty, vytvářejte skupiny a kliknutím na libovolný kontakt zobrazíte jeho úplné údaje.", + "settings_title": "Nastavení", + "settings_desc": "Přizpůsobte si vše: motiv, hustotu zobrazení, podpisy, filtry, klávesové zkratky, výchozí nastavení kalendáře a mnoho dalšího.", + "shortcuts_title": "Klávesové zkratky", + "shortcuts_desc": "Oblíbená funkce pokročilých uživatelů. Kdykoli stiskněte ? pro zobrazení všech dostupných zkratek. Můžete procházet aplikací, psát a spravovat zprávy bez použití myši.", + "compose_open_title": "Editor zpráv", + "compose_open_desc": "Toto je editor zpráv. Přidávejte příjemce, pište obsah, připojujte soubory a formátujte text. Můžete také ukládat koncepty a používat šablony.", + "calendar_view_title": "Váš kalendář", + "calendar_view_desc": "Zde vidíte kalendář s ukázkovými událostmi. Pomocí panelu nástrojů můžete přepínat zobrazení dne, týdne, měsíce a agendy.", + "create_event_title": "Vytvořit událost", + "create_event_desc": "Kliknutím na toto tlačítko vytvoříte novou událost v kalendáři. Můžete nastavit název, datum, čas a přidat účastníky.", + "event_modal_title": "Podrobnosti události", + "event_modal_desc": "Toto je formulář události. Vyplňte název, vyberte datum a čas, přidejte místo konání nebo účastníky. Až budete hotovi, klikněte na uložit - nebo okno zavřete a pokračujte dále.", + "contacts_list_title": "Vaše kontakty", + "contacts_list_desc": "Zde jsou vaše kontakty. Kliknutím na libovolný kontakt zobrazíte jeho úplné údaje na pravé straně. Můžete také vytvářet nové kontakty, importovat soubory vCard a organizovat kontakty do skupin.", + "settings_tabs_title": "Menu nastavení", + "settings_tabs_desc": "Zde najdete všechny kategorie nastavení. Upravte vzhled, spravujte identity, nastavte e-mailové filtry, předvolby kalendáře a mnoho dalšího.", + "files_title": "Úložiště souborů", + "files_desc": "Prohlížeč souborů umožňuje nahrávat, organizovat a sdílet soubory - jako váš osobní cloudový disk zabudovaný přímo v e-mailu.", + "demo_banner_title": "Ovládání demoverze", + "demo_banner_desc": "Nacházíte se v demo režimu - vše zůstává ve vašem prohlížeči. Kdykoli klikněte na „Resetovat“, abyste začali znovu s čistými ukázkovými daty.", + "quota_title": "Využití úložiště", + "quota_desc": "Zde můžete sledovat velikost své poštovní schránky. Kruh se plní podle využití kapacity." + }, + "unified_mailbox": { + "search_unavailable": "Vyhledávání není ve sjednoceném zobrazení k dispozici" + }, + "calendar_event_notifications": { + "someone": "Někdo", + "invited": "{name} vás pozval(a) na „{title}“", + "updated": "{name} aktualizoval(a) „{title}“", + "cancelled": "{name} zrušil(a) „{title}“", + "untitled": "Událost bez názvu" + }, + "share_notifications": { + "someone": "Někdo", + "shared": "{name} s vámi sdílí {kind} „{object}“", + "changed": "{name} změnil(a) váš přístup k {kind} „{object}“", + "revoked": "{name} odebral(a) váš přístup k {kind} „{object}“", + "object": { + "folder": "složku", + "calendar": "kalendář", + "address_book": "adresář", + "files": "složku souborů" + } + }, + "sharing": { + "title": "Sdílet „{name}\"", + "description": "Udělte přístup dalším uživatelům nebo skupinám na tomto serveru. Změny se projeví okamžitě.", + "no_shares": "Zatím nikomu nesdíleno.", + "add_person": "Přidat osobu nebo skupinu", + "search_placeholder": "Hledat podle jména nebo e-mailu…", + "loading_principals": "Načítání uživatelů…", + "no_principals": "Nenalezeni žádní další uživatelé ani skupiny.", + "no_match": "Žádné výsledky.", + "remove": "Odebrat přístup", + "group": "Skupina", + "share_added": "Přístup udělen", + "share_updated": "Přístup aktualizován", + "share_removed": "Přístup odebrán", + "share_failed": "Aktualizace sdílení selhala", + "preset": { + "freeBusy": "Pouze volno/zaneprázdněno", + "read": "Pouze čtení", + "readWrite": "Čtení a zápis", + "manager": "Správce", + "custom": "Vlastní" + } + }, + "quote_header": { + "reply_line": "Dne {date} napsal(a) {from}:", + "forwarded_separator": "---------- Přeposlaná zpráva ----------", + "from_label": "Od", + "date_label": "Datum", + "subject_label": "Předmět" + }, + "pwa_install": { + "title": "Nainstalovat {appName}", + "description": "Nainstalujte si naši aplikaci pro rychlý přístup a offline podporu.", + "not_now": "Teď ne", + "install": "Nainstalovat", + "dont_remind": "Už mi to nepřipomínat", + "dismiss_aria": "Zavřít výzvu k instalaci" + }, + "deep_link": { + "message_not_found": "Tato zpráva už není dostupná.", + "thread_not_found": "Tato konverzace už není dostupná.", + "folder_not_found": "Tato složka už není dostupná.", + "event_not_found": "Tato událost už není dostupná.", + "file_not_found": "Tento soubor už není dostupný.", + "account_unavailable": "Přihlaste se k tomuto účtu, abyste odkaz otevřeli.", + "copy_message": "Kopírovat odkaz na zprávu", + "copy_conversation": "Kopírovat odkaz na konverzaci", + "copy_event": "Kopírovat odkaz na událost", + "copied": "Odkaz zkopírován", + "copy_failed": "Odkaz se nepodařilo zkopírovat" + }, + "plugins": { + "oauth_callback": { + "completing": "Dokončování autorizace…", + "completing_message": "Počkejte prosím, než se naváže spojení.", + "received_title": "Autorizace přijata", + "received_message": "Můžete se vrátit na kartu, kde jste zahájili připojení.", + "failed_title": "Autorizace se nezdařila", + "denied": "Autorizace byla zamítnuta.", + "failed_detail": "Autorizace se nezdařila: {error}", + "missing_params": "Chybí autorizační kód nebo stav.", + "delivery_failed": "Výsledek autorizace se nepodařilo předat kartě aplikace. Zavřete toto okno a zkuste to znovu." + }, + "settings": { + "title": "Pluginy", + "description": "Pluginy přidané vaším správcem. Pro svůj účet je můžete zapnout nebo vypnout.", + "description_uploads": "Pluginy přidávají do webmailu další funkce. Pro svůj účet je můžete zapnout nebo vypnout, případně nainstalovat vlastní.", + "description_demo": "Pluginy nejsou v ukázkové verzi k dispozici.", + "install_button": "Nainstalovat plugin", + "install_hint": "Vyberte soubor ZIP s pluginem.", + "install_hint_approval": "Vyberte soubor ZIP s pluginem. Než ho budete moci zapnout, musí ho schválit správce.", + "install_uploads_off": "Váš správce nepovoluje instalaci pluginů.", + "install_managed_id": "Váš správce už poskytuje plugin s tímto názvem, proto ho nelze nahradit.", + "install_invalid": "Tento soubor není plugin, který lze nainstalovat: {details}", + "toast_installed": "Plugin nainstalován. Zapněte ho níže.", + "toast_installed_pending": "Plugin nainstalován a odeslán správci ke schválení.", + "toast_enabled": "Plugin {name} je zapnutý", + "toast_disabled": "Plugin {name} je vypnutý", + "toast_removed": "Plugin {name} byl odstraněn", + "toast_forced": "Váš správce ponechává plugin {name} zapnutý", + "toast_approval_requested": "Plugin {name} čeká na schválení správcem", + "empty_title": "Zatím žádné pluginy", + "empty_admin": "Váš správce zatím nepřidal žádné pluginy.", + "empty_uploads": "Začněte instalací pluginu ze souboru ZIP.", + "empty_demo": "Pluginy vyžadují skutečný účet, proto je ukázková verze nemá.", + "syncing": "Kontrola pluginů na serveru…", + "status_installed": "Vypnuto", + "status_enabled": "Spouští se", + "status_running": "Zapnuto", + "status_disabled": "Vypnuto", + "status_error": "Problém", + "badge_forced": "Povinný", + "badge_managed": "Od správce", + "badge_yours": "Nainstalováno vámi", + "badge_awaiting": "Čeká na schválení", + "toggle_label": "Zapnout nebo vypnout plugin {name}", + "forced_note": "Váš správce tento plugin vyžaduje, proto ho nelze vypnout.", + "permissions": "Co může dělat", + "permissions_managed": "Co může dělat (schváleno správcem)", + "settings": "Nastavení", + "remove": "Odstranit", + "remove_title": "Odstranit plugin {name}?", + "remove_message": "Plugin i vše, co uložil v tomto prohlížeči, bude smazáno. Později ho můžete nainstalovat znovu.", + "remove_confirm": "Odstranit", + "error_user_plugins_off": "Váš správce vypnul pluginy instalované uživateli, proto tento plugin neběží.", + "error_approval_unavailable": "Tento plugin byl nainstalován dříve, než bylo vyžadováno schválení. Odstraňte ho a nainstalujte znovu, aby se odeslal ke schválení.", + "error_approval_denied": "Váš správce tento plugin neschválil.", + "error_approval_pending": "Čeká se, až správce tento plugin schválí.", + "error_auto_disabled": "Vypnuto, protože opakovaně selhávalo. Zapněte ho znovu a zkuste to znovu.", + "accounts_heading": "Připojené účty", + "account_on": "{label} na {host}", + "account_waiting": "{label} na {host} (přihlášení nedokončeno)", + "disconnect": "Odpojit", + "disconnect_failed": "{label} se nepodařilo odpojit" + }, + "consent": { + "title": "Povolit pluginu {name} tyto akce?", + "intro": "Plugin žádá o níže uvedená oprávnění. Dostane je, jen pokud je povolíte.", + "high_risk_note": "Označené položky dávají pluginu přístup k obsahu vaší pošty nebo mu umožňují posílat data jinam. Povolte je jen pluginům, kterým důvěřujete.", + "revoke_hint": "Plugin můžete kdykoli vypnout nebo odstranit v Nastavení.", + "deny": "Nepovolit", + "allow": "Povolit" + }, + "permissions": { + "none": "Nepotřebuje žádná zvláštní oprávnění.", + "high_risk": "Vyžaduje opatrnost", + "email_read": { + "title": "Číst vaši poštu", + "body": "Vidí odesílatele, příjemce, předměty a celý text kterékoli zprávy ve vašem účtu i vaše složky." + }, + "email_write": { + "title": "Měnit a třídit vaši poštu", + "body": "Přidává nebo odebírá štítky, příznaky a další klíčová slova u vašich zpráv, přesouvá je mezi vašimi složkami a do archivu a ukládá koncepty." + }, + "email_send": { + "title": "Měnit poštu před odesláním", + "body": "Čte a mění příjemce, předmět, text a přílohy zpráv, které píšete, a může zastavit odeslání zprávy." + }, + "crypto_full": { + "title": "Plný přístup k této aplikaci", + "body": "Běží se stejnými právy jako samotný webmail. Může číst veškerou vaši poštu a uložené klíče a jednat vaším jménem. Povolte to jen pluginům, kterým plně důvěřujete." + }, + "email_raw_send": { + "title": "Odesílat zprávy, které sám vytvořil", + "body": "Odesílá z vašeho účtu kompletní zprávy, například podepsané nebo šifrované." + }, + "email_blob_read": { + "title": "Číst nezpracovaný obsah zpráv", + "body": "Čte původní data vašich zpráv a příloh i každého souboru, který připojíte před odesláním." + }, + "email_blob_write": { + "title": "Nahrazovat přílohy před odesláním", + "body": "Nahrává soubory na váš poštovní server a před odesláním zprávy nahradí připojený soubor jiným." + }, + "email_render_takeover": { + "title": "Měnit zobrazení zpráv", + "body": "Nahrazuje text zpráv, které otevřete, i seznamu zpráv, například aby zobrazil dešifrovaný obsah." + }, + "calendar_read": { + "title": "Číst váš kalendář", + "body": "Vidí vaše kalendáře a jejich události." + }, + "calendar_write": { + "title": "Měnit váš kalendář", + "body": "Vytváří, mění a maže události." + }, + "contacts_read": { + "title": "Číst vaše kontakty", + "body": "Prohledává a čte vaše adresáře." + }, + "contacts_write": { + "title": "Měnit vaše kontakty", + "body": "Vytváří, mění a maže kontakty a adresáře." + }, + "files_read": { + "title": "Číst vaše soubory", + "body": "Prochází a otevírá soubory uložené ve vašem účtu." + }, + "files_write": { + "title": "Měnit vaše soubory", + "body": "Nahrává, přejmenovává, přesouvá a maže soubory ve vašem účtu." + }, + "identity_read": { + "title": "Vidět vaše identity odesílatele", + "body": "Vidí adresy, ze kterých odesíláte, a jejich podpisy." + }, + "identity_write": { + "title": "Měnit vaše identity odesílatele", + "body": "Vytváří, mění a maže identity odesílatele a podpisy." + }, + "filters_read": { + "title": "Číst vaše filtry pošty", + "body": "Vidí pravidla, která třídí vaši příchozí poštu." + }, + "filters_write": { + "title": "Měnit vaše filtry pošty", + "body": "Přidává pravidla do filtrů na serveru. Taková pravidla mohou příchozí poštu přesouvat, přeposílat nebo mazat." + }, + "tasks_read": { + "title": "Číst vaše úkoly", + "body": "Vidí vaše seznamy úkolů a úkoly." + }, + "tasks_write": { + "title": "Měnit vaše úkoly", + "body": "Vytváří, mění, dokončuje a maže úkoly." + }, + "templates_read": { + "title": "Číst vaše šablony", + "body": "Vidí vaše uložené šablony zpráv." + }, + "templates_write": { + "title": "Měnit vaše šablony", + "body": "Vytváří, mění a maže šablony zpráv." + }, + "smime_read": { + "title": "Vidět stav S/MIME", + "body": "Vidí, zda máte klíče a certifikáty S/MIME, bez přístupu ke klíčům." + }, + "vacation_read": { + "title": "Vidět automatickou odpověď v nepřítomnosti", + "body": "Vidí, zda je automatická odpověď zapnutá a co obsahuje." + }, + "vacation_write": { + "title": "Měnit automatickou odpověď v nepřítomnosti", + "body": "Zapíná a vypíná automatickou odpověď a mění její text a data." + }, + "settings_read": { + "title": "Číst vaše nastavení", + "body": "Vidí vaše předvolby a definice štítků." + }, + "settings_write": { + "title": "Měnit vaše nastavení", + "body": "Přidává štítky, mění jejich pořadí a mění vaše předvolby." + }, + "security_read": { + "title": "Vidět zabezpečení účtu", + "body": "Vidí, zda je zapnuté dvoufázové ověření a šifrování, bez přístupu k tajným údajům." + }, + "auth_observe": { + "title": "Vědět, kdy se přihlásíte", + "body": "Dostává zprávu, když se přihlásíte, přepnete účet nebo se obnoví vaše relace." + }, + "auth_emit": { + "title": "Odhlásit vás", + "body": "Může vás odhlásit z webmailu." + }, + "account_read": { + "title": "Vidět vaše účty", + "body": "Vidí účty, které jste přidali, včetně jejich adres a serverů." + }, + "http_post": { + "title": "Používat server webmailu vaším jménem", + "body": "Volá tyto koncové body serveru s vaším přihlášením:" + }, + "http_fetch": { + "title": "Připojovat se k jiným webům", + "body": "Odesílá požadavky na tyto weby. Cokoli, co plugin může číst, tam může být odesláno:" + }, + "admin_config": { + "title": "Číst nastavení od správce", + "body": "Čte nastavení, která pro tento plugin uložil váš správce." + }, + "ui_observe": { + "title": "Sledovat, kde se v aplikaci nacházíte", + "body": "Ví, která stránka je otevřená, a reaguje na odkazy, na které kliknete." + }, + "ui_toolbar": { + "title": "Přidávat tlačítka na panely nástrojů", + "body": "Zobrazuje vlastní tlačítka na panelu nástrojů zpráv, u otevřené zprávy a v horní liště." + }, + "ui_app_top_banner": { + "title": "Zobrazovat banner nahoře", + "body": "Zobrazuje oznámení přes horní část aplikace." + }, + "ui_email_banner": { + "title": "Zobrazovat oznámení u zpráv", + "body": "Zobrazuje vlastní oznámení a shrnutí nad částmi otevřené zprávy nebo konverzace, uvnitř nich a mezi nimi." + }, + "ui_email_footer": { + "title": "Přidávat obsah pod zprávy", + "body": "Zobrazuje vlastní obsah pod otevřenou zprávou." + }, + "ui_contact_cryptokeys": { + "title": "Zobrazovat šifrovací klíče kontaktů", + "body": "Zobrazuje a spravuje šifrovací klíče u kontaktu." + }, + "ui_email_details": { + "title": "Přidávat panely do podrobností zprávy", + "body": "Zobrazuje vlastní panel vedle otevřené zprávy nebo pod ní." + }, + "ui_download_file": { + "title": "Ukládat soubory do zařízení", + "body": "Nabízí ke stažení soubory, které vytvořil." + }, + "ui_message_list_tabs": { + "title": "Přidávat karty do Doručených", + "body": "Rozděluje Doručené do karet, například Aktualizace nebo Akce." + }, + "ui_composer_toolbar": { + "title": "Přidávat obsah do editoru zprávy", + "body": "Zobrazuje vlastní tlačítka, položky nabídky a sekce, když píšete zprávu." + }, + "ui_composer_sidebar": { + "title": "Přidávat panel do editoru zprávy", + "body": "Zobrazuje vlastní panel vedle zprávy, kterou píšete." + }, + "ui_sidebar_widget": { + "title": "Přidávat obsah do postranního panelu", + "body": "Zobrazuje vlastní pole a sekce v postranním panelu pošty." + }, + "ui_settings_section": { + "title": "Přidávat stránku nastavení", + "body": "Zobrazuje vlastní stránku v Nastavení." + }, + "ui_context_menu": { + "title": "Přidávat položky do nabídky zprávy", + "body": "Přidává položky do nabídky, která se otevře po kliknutí pravým tlačítkem na zprávu." + }, + "ui_navigation_rail": { + "title": "Přidávat tlačítko do navigačního panelu", + "body": "Zobrazuje vlastní tlačítko v navigačním panelu." + }, + "ui_keyboard": { + "title": "Přidávat klávesové zkratky", + "body": "Reaguje na klávesové zkratky, které sám definuje." + }, + "ui_calendar_action": { + "title": "Přidávat obsah do sekce Kalendář", + "body": "Zobrazuje vlastní tlačítka a sekce u událostí a krátké popisky a tečky u dnů v kalendáři." + }, + "ui_admin_page": { + "title": "Přidávat stránku administrace", + "body": "Zobrazuje vlastní stránku v nastavení administrace." + }, + "app_lifecycle": { + "title": "Vědět, kdy se aplikace spouští a ukončuje", + "body": "Dostává zprávu, když se aplikace spustí, přejde do režimu offline nebo když se odhlásíte." + }, + "compose_open": { + "title": "Otevírat editor zprávy", + "body": "Otevře novou zprávu s vyplněnými příjemci, textem nebo přílohami. O odeslání stále rozhodujete vy." + }, + "compose_read": { + "title": "Číst zprávu, kterou píšete", + "body": "Číst text zprávy během psaní, když plugin použijete v editoru. Ne podpis ani citovanou zprávu." + }, + "compose_write": { + "title": "Měnit zprávu, kterou píšete", + "body": "Nahrazovat nebo přidávat text ve zprávě během psaní, když plugin použijete v editoru. Každou změnu můžete vrátit a o odeslání stále rozhodujete vy." + }, + "ui_notifications": { + "title": "Zobrazovat oznámení", + "body": "Zobrazuje oznámení ve vašem zařízení, nebo v aplikaci, pokud jste je nepovolili. Každé uvádí název pluginu." + }, + "ui_message_list": { + "title": "Přidávat obsah do seznamu zpráv", + "body": "Zobrazuje vlastní obsah nad seznamem zpráv a tlačítka u zpráv a na panelu nástrojů pro vybrané zprávy." + }, + "ui_contacts": { + "title": "Přidávat obsah do sekce Kontakty", + "body": "Zobrazuje vlastní sekce v podrobnostech kontaktu a tlačítka v seznamu kontaktů." + }, + "ui_tasks": { + "title": "Přidávat obsah do sekce Úkoly", + "body": "Zobrazuje vlastní sekce v podrobnostech úkolu a tlačítka v seznamu úkolů." + }, + "ui_files": { + "title": "Přidávat obsah do sekce Soubory", + "body": "Zobrazuje vlastní sekce v náhledu souboru a položky v nabídce souboru." + }, + "ui_commands": { + "title": "Přidávat příkazy", + "body": "Přidává vlastní položky do palety příkazů (Ctrl+K)." + }, + "ui_app_page": { + "title": "Přidávat stránky", + "body": "Přidává do aplikace vlastní stránky, každou s tlačítkem v navigačním panelu." + }, + "http_proxy": { + "title": "Používat účty, které připojíte", + "body": "Odesílá požadavky přes server Bulwarku pomocí účtu nebo klíče, který jste nastavili vy nebo váš správce. Plugin nikdy nevidí heslo, ale může na příslušném serveru dělat cokoli, co může tento účet." + } + }, + "dialog": { + "from_plugin": "Od pluginu {name}", + "ok": "OK", + "submit": "Odeslat", + "confirm": "Potvrdit", + "cancel": "Zrušit", + "close": "Zavřít", + "turn_off": "Vypnout {name}", + "plugin_menu": "Možnosti pro {name}", + "password_note": "Tento formulář pochází z pluginu {name}, ne z webmailu. Heslo zadejte jen tehdy, když tomuto pluginu důvěřujete.", + "toast_auto_disabled": "{name} byl vypnut, protože opakovaně selhával." + }, + "connect": { + "title": "Připojit {label}", + "intro_password": "{name} chce používat váš účet {label}. Bulwark uchovává heslo na svém serveru a posílá ho jen na {host}. Plugin ho nikdy nevidí.", + "intro_password_any": "{name} chce používat váš účet {label}. Bulwark uchovává heslo na svém serveru a posílá ho jen na server, který zde zadáte. Plugin ho nikdy nevidí.", + "intro_signin": "{name} se chce přihlásit k vašemu účtu {label}. Zadejte zde server a pak se přihlaste na jeho vlastní stránce. Bulwark uchovává získaný přístup na svém serveru; plugin ho nikdy nevidí.", + "server": "Adresa serveru", + "server_hint": "cloud.example.com", + "username": "Uživatelské jméno", + "secret": "Heslo nebo klíč", + "connect": "Připojit", + "continue": "Pokračovat", + "cancel": "Zrušit", + "saving": "Ukládání…", + "error_server": "Zadejte adresu serveru, například https://cloud.example.com", + "error_save": "Uložení se nezdařilo: {error}" + } + }, + "global_search": { + "title": "Hledat ve všem", + "placeholder": "Hledat v poště, kontaktech, kalendáři, souborech...", + "scope_label": "Hledat v", + "account_label": "Účet", + "scope_all": "Vše", + "scope_mail": "Pošta", + "scope_contacts": "Kontakty", + "scope_calendar": "Kalendář", + "scope_files": "Soubory", + "all_accounts": "Všechny účty", + "show_all": "Zobrazit všechny výsledky", + "no_results": "Žádné výsledky pro „{query}“", + "server_hint": "Celá slova prohledávají server; části slov odpovídají již načteným položkám", + "searching": "Hledání...", + "account_error": "Nepodařilo se prohledat {account}", + "recent_searches": "Nedávná hledání", + "recurring": "Opakovaná", + "no_subject": "(bez předmětu)", + "empty_hint": "Hledejte poštu, kontakty, události a soubory ve všech účtech. Zkuste from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Ploše podle data", + "grouped": "Seskupené", + "load_more": "Načíst další", + "preview_empty": "Vyberte výsledek pro náhled", + "open_full": "Otevřít", + "view_label": "Zobrazení" + }, + "top_bar": { + "more": "Další možnosti" + }, + "plugin_pages": { + "loading": "Načítání stránky", + "not_available_title": "Tato stránka není k dispozici", + "not_available_description": "Plugin, který ji přidal, je vypnutý nebo už není nainstalovaný.", + "back_to_mail": "Zpět na poštu", + "settings_group": "Pluginy" + }, + "command_palette": { + "title": "Paleta příkazů", + "open": "Otevřít paletu příkazů", + "placeholder": "Zadejte příkaz", + "no_results": "Žádné odpovídající příkazy", + "go_to": "Přejít na {name}", + "compose": "Nová zpráva", + "search": "Hledat v poště", + "theme_dark": "Přepnout na tmavý motiv", + "theme_light": "Přepnout na světlý motiv", + "shortcuts": "Zobrazit klávesové zkratky", + "group_navigation": "Přejít na", + "group_actions": "Akce", + "group_plugins": "Pluginy" + }, + "notification_center": { + "title": "Oznámení", + "mark_all_read": "Označit vše jako přečtené", + "clear": "Vymazat", + "empty": "Žádná oznámení", + "close": "Zavřít", + "unread": "Nepřečtené", + "unread_count": "Nepřečtené: {count}", + "level_error": "Chyba", + "level_warning": "Upozornění" + } +} diff --git a/locales/da/common.json b/locales/da/common.json index 27e98c28..395e8d5c 100644 --- a/locales/da/common.json +++ b/locales/da/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimalistisk webmail-klient, der bruger JMAP-protokollen", - "login": { - "title": "Webmail", - "username_label": "Email", - "username_placeholder": "bruger@eksempel.dk", - "password_label": "Adgangskode", - "password_placeholder": "Indtast din adgangskode", - "jmap_endpoint_label": "JMAP-server", - "jmap_endpoint_placeholder": "https://mail.eksempel.dk", - "jmap_endpoint_cors_hint": "Serveren skal tillade CORS-anmodninger fra dette domæne.", - "jmap_server_label": "Server", - "jmap_server_auto_picked": "Server valgt ud fra din e-maildomæne.", - "sign_in": "Log ind", - "signing_in": "Logger ind...", - "loading": "Indlæser...", - "reconnecting": "Forbindelse mistet. Forsøger at genoprette forbindelse…", - "error": { - "invalid_credentials": "Ugyldig e-mail eller adgangskode. Kontroller dine oplysninger og prøv igen.", - "connection_failed": "Kan ikke nå serveren. Kontrollér din internetforbindelse og prøv igen.", - "cors_blocked": "Serveren kan nås, men blokerer cross-origin-anmodninger. Kontrollér JMAP-serverens CORS-indstillinger og tillad dette domæne.", - "server_error": "Serveren er midlertidigt utilgængelig. Prøv igen senere.", - "generic": "Der opstod en uventet fejl. Kontakt din administrator, hvis problemet fortsætter.", - "totp_required": "En to-faktor-godkendelseskode er påkrævet. Indtast din kode nedenfor.", - "totp_invalid": "Ugyldig godkendelseskode. Kontrollér din autentificeringsapp og prøv igen.", - "oauth_discovery_failed": "SSO er aktiveret, men identitetsudbyderen kunne ikke nås. Kontrollér din OAuth-konfiguration." - }, - "show_password": "Vis adgangskode", - "hide_password": "Skjul adgangskode", - "totp_toggle": "Jeg har en 2FA-kode", - "remember_me": "Husk mig", - "config_error": { - "title": "Konfigurationsfejl", - "fetch_failed": "Kan ikke indlæse applikationskonfiguration. Prøv igen senere.", - "server_not_configured": "Mailserveren er ikke konfigureret. Kontakt din administrator." - }, - "remove_from_history": "Fjern fra historik", - "totp_label": "Godkendelseskode", - "totp_placeholder": "000000", - "session_expired": "Din session er udløbet. Log venligst ind igen.", - "dismiss": "Afvis", - "or": "eller", - "sign_in_sso": "Log ind med SSO", - "add_account_title": "Tilføj konto", - "add_account_subtitle": "Log ind med en anden konto", - "cancel": "Annuller", - "website": "Hjemmeside", - "imprint": "Impressum", - "privacy_policy": "Privatlivspolitik", - "try_demo": "Prøv demo", - "demo_description": "Udforsk med eksempeldata - ingen konto nødvendig", - "demo_launching": "Åbner demo...", - "demo_login_button": "Start demo", - "demo_tagline": "Oplev en fuldt udstyret e-mailklient. Ingen konto påkrævet.", - "demo_no_signup": "Ingen tilmelding nødvendig - udforsk frit med eksempeldata", - "oauth_completing": "Fuldfører login...", - "oauth_error": { - "title": "Godkendelse mislykkedes", - "invalid_state": "Sikkerhedsvalidering mislykkedes. Prøv at logge ind igen.", - "missing_params": "Manglende godkendelsesdata. Prøv at logge ind igen.", - "token_exchange_failed": "Kunne ikke fuldføre godkendelse. Prøv igen.", - "access_denied": "Adgang blev nægtet. Kontakt din administrator.", - "back_to_login": "Tilbage til login" - } - }, - "sidebar": { - "close": "Luk", - "compose": "Ny besked", - "compose_hint": "Ny besked (c)", - "search_placeholder": "Søg i mail...", - "search_placeholder_hint": "Søg i mail... (tryk /)", - "storage": "Lagerplads", - "storage_used": "Brugt", - "storage_free": "Ledig", - "storage_total": "I alt", - "sign_out": "Log ud", - "sign_out_of": "Log ud af {account}", - "sign_out_all": "Log ud af alle konti", - "add_account": "Tilføj konto", - "set_as_default": "Angiv som standard", - "switch_account": "Skift konto", - "contacts": "Kontakter", - "calendar": "Kalender", - "settings": "Indstillinger", - "admin": "Admin", - "files": "Filer", - "loading_mailboxes": "Indlæser postkasser...", - "push_connected": "Realtidsopdateringer aktive", - "push_disconnected": "Realtidsopdateringer inaktive", - "keyboard_shortcuts": "Tastaturgenveje", - "theme": { - "light": "Lys tilstand", - "dark": "Mørk tilstand", - "system": "Systemtema" - }, - "language": { - "title": "Sprog" - }, - "mailboxes": { - "inbox": "Indbakke", - "sent": "Sendt", - "drafts": "Kladder", - "trash": "Papirkurv", - "archive": "Arkiv", - "starred": "Stjernemarkerede", - "all_mail": "Al mail", - "spam": "Spam", - "important": "Vigtig" - }, - "unified_inbox": "Samlet indbakke", - "unified_sent": "Alle sendte", - "unified_drafts": "Alle kladder", - "unified_trash": "Alle papirkurve", - "unified_archive": "Alle arkiver", - "unified_junk": "Alt spam", - "all_accounts": "Alle konti", - "unified_mailbox": "Samlet postkasse", - "expand": "Udvid", - "collapse": "Skjul", - "expand_tooltip": "Udvid", - "collapse_tooltip": "Skjul", - "mobile": { - "search": "Søg", - "compose": "Ny besked", - "go_back": "Gå tilbage", - "toggle_menu": "Slå menu til/fra" - }, - "clear_search": "Ryd søgning", - "vacation_active": "Fraværsbeskeder er aktive", - "demo_banner": "Demo-tilstand", - "demo_reset": "Nulstil", - "demo_tour": "Rundvisning", - "tags": "Tags", - "show_all_tags": "Vis alle ({count})", - "show_fewer_tags": "Vis færre", - "folders": "Mapper", - "shared": "Delt", - "mail": "Mail", - "nav_label": "Navigation", - "add_app": "Apps", - "scheduled": "Planlagt", - "unified_all_unread": "Alle ulæste", - "unified_all_starred": "Alle med stjerne", - "unified_all_mail": "Al post", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Standardapps", - "description": "Vælg om e-mail- og kalenderlinks åbnes i Bulwark. Teknisk set registrerer Bulwark sig som protokolhåndtering for mailto:- og webcal:-links.", - "unsupported": "Denne browser eller forbindelse understøtter ikke manuel registrering af protokolhåndtering. Du kan muligvis stadig bruge den installerede PWA via browser- eller OS-indstillinger.", - "mailto_label": "E-mail-links", - "mailto_description": "Åbn mailto:-links i Bulwark med en forudfyldt komponist.", - "protocol_open_mode_label": "Ved åbning af protokollinks", - "protocol_open_mode_description": "Vælg om Bulwark åbner mailto:- og webcal:-links i en ny fane eller genbruger en åben session. Indstillingen \\\"aktiv session\\\" kræver notifikationstilladelse, så du kan klikke på en nødnotifikation for at bringe Bulwark frem, hvis browseren blokerer fokus.", - "protocol_open_mode_active_session": "Åbn i aktiv session hvis muligt", - "protocol_open_mode_new_tab": "Åbn altid ny fane", - "focus_notification_title": "Åbn Bulwark", - "focus_notification_body": "Linket blev åbnet i Bulwark. Klik for at bringe vinduet frem.", - "webcal_label": "Kalenderlinks", - "webcal_description": "Åbn webcal:-links i Bulwark med en forudfyldt kalenderabonnementsdialog.", - "register_mailto": "Registrer e-mail-app", - "register_webcal": "Registrer kalender-app", - "mailto_registered": "Registrering af e-mail-håndtering anmodet", - "webcal_registered": "Registrering af kalenderhåndtering anmodet", - "registration_failed": "Registrering af protokolhåndtering mislykkedes", - "opening_mailto": "Åbner komponist...", - "opening_webcal": "Åbner kalender...", - "browser_note": "Din browser eller dit operativsystem beder dig muligvis bekræfte dette og kræver muligvis, at Bulwark er installeret, før det kan vælges som standardapp.", - "select_account_title": "Vælg konto", - "select_mailto_account": "Vælg hvilken konto der skal åbne dette e-mail-link.", - "select_webcal_account": "Vælg hvilken konto der skal åbne dette kalenderlink.", - "select_account_note": "Dette gælder kun for dette protokollink.", - "detail_to": "Til", - "detail_subject": "Emne", - "detail_no_subject": "Intet emne", - "detail_calendar": "Kalender", - "detail_source": "Kilde", - "active_account": "Aktiv", - "switching_account": "Skifter konto..." - }, - "sidebar_apps": { - "modal_title": "Sidepanel-apps", - "add_new": "Tilføj app", - "edit_app": "Rediger app", - "name_label": "Navn", - "name_placeholder": "Min app", - "name_required": "Navn er påkrævet", - "url_label": "URL", - "url_required": "URL er påkrævet", - "url_invalid": "Indtast en gyldig http- eller https-URL", - "icon_label": "Ikon", - "icon_required": "Ikon er påkrævet", - "open_mode_label": "Åbningstilstand", - "open_new_tab": "Ny fane", - "open_inline": "Indlejret", - "cancel": "Annuller", - "add": "Tilføj", - "update": "Opdater", - "delete": "Slet", - "delete_confirm_title": "Slet app", - "delete_confirm": "Er du sikker på, at du vil slette \\\"{name}\\\"?", - "no_apps": "Ingen apps tilføjet endnu", - "no_apps_hint": "Tilføj brugerdefinerede apps og links til dit sidepanel", - "managed_title": "Leveret af din administrator", - "managed_description": "Disse apps er sat op for alle og kan ikke ændres.", - "managed_badge": "Administreret", - "search_icons": "Søg efter ikoner...", - "show_popular": "Populære", - "show_all": "Alle", - "no_icons_found": "Ingen ikoner fundet", - "inline_badge": "Indlejret", - "tab_badge": "Fane", - "show_on_mobile": "Vis på mobil" - }, - "email_list": { - "no_emails": "Ingen beskeder fundet", - "no_emails_description": "Denne postkasse er tom", - "no_search_results": "Ingen resultater fundet", - "no_search_results_description": "Prøv at justere din søgning eller filtre", - "loading": "Indlæser e-mails...", - "unread": "ulæst", - "to_me": "Til mig", - "to_recipients": "Til {count} modtagere", - "and_others": "og {count} andre", - "draft": "Kladde", - "starred": "Stjernemarkerede", - "pinned": "Fastgjort", - "replied": "Besvaret", - "forwarded": "Videresendt", - "has_attachment": "Har vedhæftning", - "conversations_count": "{count} af {total} samtaler", - "conversations_count_plus": "{count}+ samtaler", - "conversations_count_simple": "{count} samtaler", - "no_conversations": "Ingen samtaler", - "loading_more": "Indlæser flere e-mails...", - "no_more_emails": "Ikke flere e-mails at indlæse", - "batch_actions": { - "select": "Vælg e-mails", - "select_all": "Vælg alle", - "selected_messages": "{count, plural, one {1 e-mail} other {# e-mails}} valgt", - "mark_read": "Markér som læst", - "mark_unread": "Markér som ulæst", - "delete": "Slet", - "delete_confirm_title": "Slet e-mails", - "delete_confirm_message": "Er du sikker på, at du vil slette {count, plural, one {1 e-mail} other {# e-mails}}?", - "clear_selection": "Ryd markering" - }, - "permanent_delete": "Slet permanent", - "permanent_delete_confirm_title": "Slet permanent", - "permanent_delete_confirm_message": "Denne e-mail vil blive slettet permanent. Denne handling kan ikke fortrydes.", - "permanent_delete_confirm_batch_message": "Disse {count, plural, one {1 e-mail} other {# e-mails}} vil blive slettet permanent. Denne handling kan ikke fortrydes.", - "empty_folder": { - "button": "Tøm mappe", - "confirm_title": "Tøm mappe", - "confirm_message": "Alle e-mails i denne mappe vil blive slettet permanent. Denne handling kan ikke fortrydes.", - "confirm_button": "Tøm mappe", - "junk_hint": "Du kan tømme spam-mappen for permanent at fjerne alle beskeder.", - "trash_hint": "Du kan tømme papirkurven for permanent at fjerne alle beskeder." - }, - "no_scheduled_emails": "Ingen planlagte e-mails", - "no_scheduled_emails_description": "Beskeder planlagt til senere vises her.", - "scheduled_count": "{count, plural, one {1 planlagt e-mail} other {# planlagte e-mails}}", - "scheduled_actions_hint": "Planlagte beskeder kan annulleres, planlægges om eller redigeres fra deres planlagte handlinger.", - "cancel_scheduled_send": "Annuller afsendelse", - "reschedule_send": "Planlæg om", - "cancel_and_edit": "Annuller og redigér", - "cancel_and_compose_again": "Annuller og skriv igen", - "reschedule_prompt": "Indtast en ny dato/tid, f.eks. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Afsenderen anmoder om en læsekvittering:", - "send": "Send kvittering", - "ignore": "Ignorér", - "sent": "Læsekvittering sendt.", - "send_failed": "Læsekvittering kunne ikke sendes", - "mdn_subject": "Læst: {subject}", - "mdn_body": "Dette er en læsekvittering for den besked, du sendte til {recipient}.\n\nBemærk: Denne kvittering bekræfter kun, at beskeden blev vist på modtagerens computer. Der er ingen garanti for, at modtageren har læst eller forstået indholdet." - }, - "no_email_selected": "Ingen e-mail valgt", - "no_email_description": "Vælg en e-mail fra listen for at se den her", - "no_conversation_selected": "Ingen samtale valgt", - "no_conversation_description": "Vælg en samtale fra listen for at læse den her", - "compose": "Ny besked", - "compose_hint": "Skriv ny besked", - "no_subject": "(Intet emne)", - "no_body_content": "(Intet indhold tilgængeligt)", - "no_preview_available": "Forhåndsvisning ikke tilgængelig", - "show_quoted_text": "Vis citeret tekst", - "hide_quoted_text": "Skjul citeret tekst", - "loading_email": "Indlæser e-mail...", - "loading": "Indlæser...", - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "delete": "Slet", - "archive": "Arkivér", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Stjernemarkér", - "unstar": "Fjern stjernemarkering", - "mark_unread": "Markér som ulæst", - "mark_read": "Markér som læst", - "unread": "Ulæst", - "read": "Læst", - "spam_short": "Spam", - "not_spam_short": "Ikke spam", - "move": "Flyt", - "print": "Udskriv", - "view_source": "Vis kilde", - "fullscreen": "Fuld skærm", - "exit_fullscreen": "Afslut fuld skærm", - "export_email": "Eksportér som .eml", - "forward_as_attachment": "Videresend som vedhæftet fil", - "import_email": "Importér .eml eller .zip", - "keyboard_shortcuts": "Tastaturgenveje (?)", - "email_source": "E-mail-kilde", - "draft_banner": "Denne besked er en kladde", - "edit_draft": "Redigér", - "copy_source": "Kopier til udklipsholder", - "source_copied": "Kilde kopieret til udklipsholder", - "attachments": "Vedhæftninger", - "important": "Vigtig", - "download": "Download", - "download_all": "Download alle", - "from": "Fra", - "to": "Til", - "cc": "CC", - "bcc": "BCC", - "date": "Dato", - "subject": "Emne", - "show_details": "Vis detaljer", - "hide_details": "Skjul detaljer", - "external_content_warning": "Billeder og eksternt indhold er blevet blokeret", - "load_external_content": "Indlæs billeder", - "trust_sender": "Stol altid på denne afsender", - "back_to_list": "Tilbage til liste", - "view_contact": "Vis kontakt", - "message_details": "Beskeddetaljer", - "more_reply_options": "Flere svar-muligheder", - "set_tag": "Sæt tag", - "tag": "Tag", - "more_actions": "Flere handlinger", - "previous": "Forrige", - "next": "Næste", - "move_to": "Flyt til...", - "remove_tag": "Fjern tag", - "tag_filter_placeholder": "Filtrer tags", - "tag_no_matches": "Ingen matchende tags", - "more_count": "+{count} mere", - "characters_count": "{count} tegn", - "quick_reply_placeholder": "Skriv et hurtigt svar...", - "more_options": "Flere muligheder", - "sending": "Sender...", - "security_authentication": "Sikkerhed & godkendelse", - "technical_details": "Tekniske detaljer", - "message_id_label": "Message-ID:", - "reply_to_label": "Svar-til:", - "delivery_time_label": "Leveringstid:", - "conversation_part_label": "Del af samtale:", - "previous_messages": "{count} tidligere besked", - "previous_messages_plural": "{count} tidligere beskeder", - "time": { - "day": "dag", - "days": "dage", - "hour": "time", - "hours": "timer", - "minute": "minut", - "minutes": "minutter" - }, - "unknown_sender": "Ukendt", - "recipient_me": "mig", - "recipient_and_others": "{name} og {count} andre", - "recipient_to_prefix": "Til:", - "authentication": { - "title": "Godkendelse", - "status": { - "verified": "Bekræftet", - "warning": "Advarsel", - "none": "Ikke godkendt" - }, - "spf": { - "pass": "SPF bestået", - "fail": "SPF fejlet", - "none": "Ingen SPF" - }, - "dkim": { - "pass": "DKIM gyldig", - "fail": "DKIM ugyldig", - "none": "Ingen DKIM" - }, - "dmarc": { - "pass": "DMARC bestået", - "fail": "DMARC fejlet", - "none": "Ingen DMARC" - }, - "spam_score": "Spam-score", - "tooltip_spf": "Sender Policy Framework: Bekræfter at afsendelsesserveren har tilladelse til at sende e-mails på vegne af domænet", - "tooltip_dkim": "DomainKeys Identified Mail: Bekræfter at e-mailen ikke er ændret under transport ved hjælp af en kryptografisk signatur", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: Sikrer at SPF og DKIM er i overensstemmelse med afsenderens domæne og fastsætter en politik for fejl", - "policy": "Politik", - "result": { - "pass": "Bestået", - "fail": "Fejlet", - "softfail": "Soft fejl", - "neutral": "Neutral", - "permerror": "Permanent fejl", - "temperror": "Midlertidig fejl", - "none": "Ingen" - } - }, - "details": { - "recipients_routing": "Modtagere & routing", - "authentication_security": "Godkendelse & sikkerhed", - "identifiers_threading": "Identifikatorer & trådning", - "mailing_list": "Mailingliste", - "message_properties": "Beskedegenskaber", - "sent": "Sendt", - "received": "Modtaget", - "delivery_time": "Leveringstid", - "in_reply_to": "Svar-til", - "references": "Referencer", - "thread_id": "Tråd-ID", - "size": "Størrelse", - "mime_type": "MIME-type", - "attachments_summary": "{count} filer · {size}", - "list_id": "Liste-ID", - "list_help": "Listehjælp", - "list_post": "Listeopslag", - "list_unsubscribe": "Afmeld", - "iprev": "Omvendt DNS", - "spam_status": "Spam-status", - "ai_verdict": "AI-vurdering", - "account": "Konto", - "no_subject": "(intet emne)" - }, - "headers": { - "routing": "Routing", - "received": "Modtaget", - "message_id": "Besked-ID", - "list_info": "Listeinformation" - }, - "tooltips": { - "reply": "Svar (r)", - "reply_all": "Svar alle (a)", - "forward": "Videresend (f)", - "archive": "Arkivér (e)", - "delete": "Slet (# eller Del)", - "star": "Stjernemarkér (s)", - "unstar": "Fjern stjerne (s)", - "compose": "Ny besked (c)", - "previous": "Forrige e-mail", - "next": "Næste e-mail", - "edit_draft": "Redigér kladde" - }, - "spam": { - "button_title": "Rapportér spam", - "not_spam_title": "Markér som legitim", - "toast_success": "Flyttet til spam", - "toast_batch": "{count} e-mails flyttet til spam", - "toast_undo": "Fortryd", - "toast_not_spam_success": "Flyttet til indbakke", - "toast_not_spam_batch": "{count} e-mails flyttet til indbakke", - "error": "Kunne ikke rapportere spam", - "error_not_spam": "Kunne ikke gendanne e-mail" - }, - "unsubscribe_banner": { - "label": "Nyhedsbrev", - "button": "Afmeld", - "confirm_title": "Afmeld denne afsender?", - "confirm_button": "Bekræft", - "cancel": "Annuller", - "success_http": "Afmeldingsside åbnet i ny fane", - "success_mailto": "Afmeldings-e-mail sendt", - "confirm_message_http": "Afmeldingssiden åbnes i en ny fane.", - "confirm_message_mailto": "Der sendes en afmeldings-e-mail til afsenderen.", - "error": "Kan ikke afmelde", - "dismiss": "Afvis" - }, - "calendar_invitation": { - "loading": "Indlæser begivenhedsdetaljer…", - "title": "Kalenderinvitation", - "published_title": "Offentliggjort begivenhed", - "response_title": "Begivenhedssvar", - "update_title": "Begivenhedsopdatering", - "counter_title": "Modforslag", - "refresh_title": "Opdateringsanmodning", - "declined_counter_title": "Modforslag afvist", - "cancelled_title": "Begivenhed aflyst", - "organizer": "Organiseret af {name}", - "organizer_label": "Organiseret af", - "attendees": "{count, plural, one {# deltager} other {# deltagere}}", - "accept": "Accepter", - "maybe": "Måske", - "decline": "Afvis", - "add_to_calendar": "Tilføj til kalender", - "added": "Tilføjet til kalender", - "rsvp_sent": "Svar sendt", - "parse_error": "Kunne ikke læse invitationen", - "action_failed": "Kunne ikke udføre denne kalenderhandling.", - "no_calendar": "Kalender ikke tilgængelig", - "published_info": "Denne begivenhed blev delt til reference.", - "response_info": "Denne besked indeholder et deltagersvar.", - "response_info_organizer": "Dette deltagersvar opdaterer din begivenhed.", - "update_info": "Denne besked opdaterer en eksisterende begivenhed.", - "counter_info": "Denne besked foreslår ændringer til en begivenhed.", - "counter_info_organizer": "Denne deltager foreslog ændringer til din begivenhed.", - "refresh_info": "Denne besked anmoder om de seneste begivenhedsdetaljer.", - "refresh_info_organizer": "En deltager anmodede om de seneste begivenhedsdetaljer.", - "declined_counter_info": "Organisatoren afviste et modforslag.", - "authentication_failed_info": "Mailgodkendelseskontrol for denne invitation mislykkedes. Vær forsigtig med kalenderhandlinger.", - "authentication_missing_info": "Denne invitation inkluderer ikke bekræftet mailgodkendelse. Bekræft detaljerne med organisatoren, hvis noget ser usædvanligt ud.", - "sender_mismatch_info": "Denne invitation blev sendt fra {sender}, mens organisatoren i kalenderdataene er {organizer}.", - "sender_mismatch_unverified_info": "Denne invitation blev sendt fra {sender}, mens organisatoren i kalenderdataene er {organizer}, og beskeden kunne ikke bekræftes.", - "organizer_role": "Du organiserer denne begivenhed", - "your_response": "Dit svar: {status}", - "response_needed": "Kræver svar", - "response_accepted": "Accepteret", - "response_tentative": "Foreløbig", - "response_declined": "Afslået", - "response_delegated": "Delegeret", - "actor_sent_info": "Sendt af {name}.", - "actor_response_info": "{name} svarede {status}.", - "actor_counter_info": "{name} foreslog ændringer til denne begivenhed.", - "actor_refresh_info": "{name} bad om de seneste begivenhedsdetaljer.", - "actor_declined_counter_info": "{name} afviste modforslaget.", - "actor_note": "Note: {comment}", - "actor_unknown": "Nogen", - "proposed_changes": "Foreslåede ændringer", - "change_title": "Titel", - "change_time": "Tid", - "change_location": "Sted", - "change_description": "Beskrivelse", - "change_empty": "Ingen", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Anvend foreslåede ændringer", - "proposal_applied": "Foreslåede ændringer anvendt.", - "review_proposal": "Gennemgå forslag", - "review_request": "Gennemgå anmodning", - "view_in_calendar": "Vis i kalender", - "select_calendar": "Vælg kalender", - "already_in_calendar": "Allerede i din kalender", - "request_info": "Du er blevet inviteret til denne begivenhed. Svar for at lade arrangøren kende din tilgængelighed.", - "cancel_info": "Arrangøren har aflyst denne begivenhed.", - "event_updated": "Opdatering #{sequence}", - "event_status_tentative": "Foreløbig", - "event_status_cancelled": "Aflyst", - "expand": "Vis detaljer", - "collapse": "Skjul detaljer" - }, - "send": "Send", - "more": "mere", - "scheduled_banner": "Planlagt til afsendelse {date}", - "scheduled_send_created": "E-mail planlagt til afsendelse", - "cancel_scheduled_send": "Annuller afsendelse", - "reschedule_send": "Planlæg om", - "cancel_and_edit": "Annuller og redigér", - "cancel_and_compose_again": "Annuller og skriv igen", - "reschedule_prompt": "Indtast en ny dato/tid, f.eks. 2026-05-04T15:30", - "scheduled_actions_only": "Planlagt-visningen understøtter kun planlagte handlinger", - "undo_send_scheduled": "Besked planlagt til afsendelse", - "undo_send": "Fortryd afsendelse", - "contact_sidebar": { - "title": "Kontakt", - "close": "Luk sidepanel", - "action_email": "E-mail", - "action_email_title": "Send e-mail", - "action_copy": "Kopiér", - "action_copy_title": "Kopiér e-mail", - "action_edit_title": "Rediger kontakt", - "section_emails": "E-mails", - "section_phones": "Telefoner", - "section_organizations": "Organisationer", - "section_addresses": "Adresser", - "section_notes": "Noter", - "not_in_contacts": "Ikke i dine kontakter", - "add_to_contacts": "Tilføj til kontakter", - "copied": "Kopieret!", - "copy_failed": "Kunne ikke kopiere" - }, - "send_now": "Send nu" - }, - "email_composer": { - "read_receipt_on": "Læsekvittering anmodet (klik for at deaktivere)", - "read_receipt_off": "Anmod om læsekvittering", - "dsn_on": "Leveringsbesked anmodet (klik for at slå fra)", - "dsn_off": "Anmod om leveringsbesked", - "format_plain_text": "Skift til ren tekst", - "format_rich_text": "Skift til formateret tekst (HTML)", - "require_tls_on": "Krypteret levering påkrævet (klik for at slå fra)", - "require_tls_off": "Kræv krypteret levering (TLS)", - "new_message": "Ny besked", - "signature_edit_hint": "Dobbeltklik for at redigere signaturen", - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "reply_to": "Svar", - "reply_all_to": "Svar alle", - "forward_message": "Videresend", - "from": "Fra", - "to": "Til", - "cc": "CC", - "bcc": "BCC", - "subject": "Emne", - "body_placeholder": "Skriv din besked...", - "send": "Send", - "cancel": "Annuller", - "attach": "Vedhæft", - "attach_photos": "Fotos & videoer", - "attach_files": "Filer", - "discard": "Kassér", - "discard_draft_title": "Kassér kladde?", - "discard_draft_confirm": "Du har ugemte ændringer. Vil du kassere denne kladde?", - "saving": "Gemmer...", - "sending": "Sender...", - "add_link": "Tilføj link", - "draft_saved": "Kladde gemt", - "save_failed": "Kunne ikke gemme", - "to_placeholder": "Modtageres e-mailadresser", - "cc_placeholder": "Cc-modtagere", - "bcc_placeholder": "Bcc-modtagere", - "subject_placeholder": "Emne", - "cc_label": "Cc:", - "bcc_label": "Bcc:", - "subject_label": "Emne:", - "file_size_kb": "KB", - "prefix": { - "forward": "Vs:", - "reply": "Sv:" - }, - "no_subject": "(Intet emne)", - "unknown_sender": "Ukendt", - "quote": { - "reply_header": "Den {date} skrev {sender}:", - "forward_header": "---------- Videresendt besked ----------", - "from": "Fra: {sender}", - "date": "Dato: {date}", - "subject": "Emne: {subject}", - "to": "Til: {recipients}" - }, - "remove_sub_address": "Fjern underadresse", - "from_override": { - "toggle_off": "Tilsidesæt", - "toggle_on": "Annuller tilsidesættelse", - "toggle_tooltip": "Redigér Fra-navn og -adresse frit. Mail sendes stadig gennem din identitet.", - "name_label": "Fra-navn", - "name_placeholder": "Navn", - "email_label": "Fra-e-mailadresse", - "email_placeholder": "alias@eksempel.dk", - "envelope_notice": "Hvis din server ikke accepterer denne adresse som konvolutafsender, bruges {identity} i stedet, og modtagerne kan se den i Return-Path-headeren." - }, - "use_template": "Skabelon", - "save_as_template": "Gem som skabelon", - "validation": { - "recipient_required": "Tilføj en modtager for at sende", - "body_required": "Skriv en besked eller vedhæft en fil", - "attachments_uploading": "Vedhæftninger uploades stadig — sendes, når de er færdige", - "attachment_upload_failed": "Ikke sendt - en vedhæftet fil kunne ikke uploades. Fjern den, og prøv igen." - }, - "upload_progress": "Uploader {uploaded} / {total}", - "upload_cancel": "Annuller upload", - "upload_failed": "Kunne ikke uploade {filename}", - "attachment_too_large": "\"{name}\" er større, end serveren tillader ({max} pr. fil)", - "attachments_total_too_large": "Vedhæftningerne ville overskride serverens grænse på {max} pr. besked", - "drop_files": "Træk filer hertil for at vedhæfte", - "show_less": "Vis mindre", - "send_failed": "Kunne ikke sende e-mail", - "send_timeout": "Serveren holdt op med at svare. Tjek mappen Sendt, før du sender igen — beskeden er måske allerede afsendt.", - "continue_draft": "Fortsæt kladde", - "close_draft_title": "Gem eller kassér kladde?", - "close_draft_message": "Du har ugemte ændringer. Vil du gemme dette som en kladde eller kassere det?", - "smime_sign_on": "S/MIME-signering aktiveret", - "smime_sign_off": "Aktivér S/MIME-signering", - "smime_encrypt_on": "S/MIME-kryptering aktiveret", - "smime_encrypt_off": "Aktivér S/MIME-kryptering", - "smime_encrypt_unavailable": "S/MIME-kryptering utilgængelig – mangler modtager-certifikater", - "smime_unlock_title": "Lås S/MIME-nøgle op", - "smime_unlock_message": "Indtast adgangssætningen for at låse din S/MIME-signeringsnøgle op.", - "smime_unlock_button": "Lås op", - "smime_passphrase_placeholder": "Adgangssætning", - "forgot_attachment": { - "title": "Har du glemt en vedhæftning?", - "message": "Din besked nævner \"{keyword}\" men ingen fil er vedhæftet. Send alligevel?", - "send_anyway": "Send alligevel", - "back": "Tilbage til redigering" - }, - "empty_subject": { - "title": "Send uden emne?", - "message": "Denne besked har intet emne. Send alligevel?", - "dont_ask_again": "Spørg ikke igen", - "send_anyway": "Send alligevel", - "back": "Tilbage til redigering" - }, - "link_url_prompt": "Indtast URL'en", - "schedule_send": "Planlæg afsendelse", - "schedule_send_description": "Vælg hvornår serveren skal frigive denne besked.", - "schedule_send_required": "Vælg en dato og et tidspunkt.", - "schedule_send_invalid": "Indtast en gyldig dato og tid.", - "schedule_send_future": "Vælg en fremtidig dato og tid.", - "schedule_send_too_late": "Dette tidspunkt er senere end serveren tillader.", - "schedule_send_unsupported": "Planlagt afsendelse understøttes ikke for denne konto.", - "schedule_send_cleanup_warning": "Planlagt afsendelse blev oprettet, men oprydning af kladde mislykkedes.", - "send_delay_unsupported": "Sendeforsinkelse understøttes ikke for denne konto.", - "send_delay_unsupported_confirm": "Denne konto understøtter ikke sendeforsinkelse. Send straks i stedet?", - "recipient_edit_email": "Rediger e-mailadresse", - "recipient_edit_name": "Rediger visningsnavn", - "recipient_email_placeholder": "E-mailadresse", - "recipient_name_placeholder": "Visningsnavn", - "autocomplete_search_server": "Søg på serveren", - "autocomplete_searching": "Søger...", - "toolbar": { - "bold": "Fed", - "italic": "Kursiv", - "underline": "Understreget", - "strikethrough": "Gennemstreget", - "text_color": "Tekstfarve", - "remove_color": "Fjern farve", - "heading_1": "Overskrift 1", - "heading_2": "Overskrift 2", - "bullet_list": "Punktopstilling", - "ordered_list": "Nummereret liste", - "quote": "Citat", - "code_block": "Kodeblok", - "align_left": "Venstrejusteret", - "align_center": "Centreret", - "align_right": "Højrejusteret", - "text_direction": "Tekstretning (RTL/LTR)", - "link": "Link", - "table": "Tabel", - "clear_formatting": "Ryd formatering", - "undo": "Fortryd", - "redo": "Gentag", - "add_row_above": "Tilføj række over", - "add_row_below": "Tilføj række under", - "add_column_before": "Tilføj kolonne før", - "add_column_after": "Tilføj kolonne efter", - "delete_row": "Slet række", - "delete_column": "Slet kolonne", - "toggle_header_row": "Slå overskriftsrække til/fra", - "delete_table": "Slet tabel", - "pick_size": "Vælg størrelse", - "font_size": "Skriftstørrelse", - "font_size_default": "Standard", - "background_color": "Baggrundsfarve", - "remove_background_color": "Fjern baggrundsfarve" - }, - "send_filing_warning": "Sendt - men oprydningen bagefter mislykkedes, en forældet kladde kan blive stående." - }, - "confirm_dialog": { - "confirm": "Bekræft", - "cancel": "Annuller" - }, - "common": { - "loading": "Indlæser...", - "error": "Fejl", - "success": "Succes", - "cancel": "Annuller", - "save": "Gem", - "delete": "Slet", - "edit": "Redigér", - "close": "Luk", - "search": "Søg", - "refresh": "Opdatér", - "settings": "Indstillinger", - "help": "Hjælp", - "logout": "Log ud", - "yes": "Ja", - "no": "Nej", - "unknown": "Ukendt", - "app_title": "Webmail", - "reconnecting": "Forbindelse mistet. Forsøger at genoprette forbindelse…", - "rate_limited_title": "Server-godkendelse er midlertidigt hastighedsbegrænset.", - "rate_limited_detail": "Bulwark har sat baggrundsanmodninger på pause for at undgå udelukkelse. Prøver igen om {seconds}s.", - "rate_limited_action_title": "Anmodning sat på pause for at undgå udelukkelse.", - "rate_limited_action_detail": "Bulwark venter på, at serverens nedkølingsperiode slutter, før der sendes flere godkendte anmodninger. Prøv igen om {seconds}s.", - "pull_to_refresh": "Træk for at opdatere", - "release_to_refresh": "Slip for at opdatere", - "refreshing": "Opdaterer…" - }, - "notifications": { - "email_sent": "E-mail sendt succesfuldt", - "email_deleted": "E-mail slettet", - "email_archived": "E-mail arkiveret", - "emails_deleted": "{count, plural, one {# e-mail slettet} other {# e-mails slettet}}", - "emails_archived": "{count, plural, one {# e-mail arkiveret} other {# e-mails arkiveret}}", - "error_archiving": "Kunne ikke arkivere e-mail", - "error_updating": "Kunne ikke opdatere e-mail", - "email_starred": "E-mail stjernemarkeret", - "email_unstarred": "Stjernemarkering fjernet", - "email_marked_read": "E-mail markeret som læst", - "email_marked_unread": "E-mail markeret som ulæst", - "copied_to_clipboard": "Kopieret til udklipsholder", - "source_copied": "Kilde kopieret til udklipsholder", - "error_sending": "Kunne ikke sende e-mail", - "error_deleting": "Kunne ikke slette e-mail", - "error_loading": "Kunne ikke indlæse e-mails", - "new_email": "Ny e-mail", - "new_email_from": "Fra {sender}", - "click_to_view": "Klik for at se", - "email_moved": "E-mail flyttet", - "emails_moved": "{count} e-mails flyttet", - "moved_to_mailbox": "Flyttet til {mailbox}", - "move_failed": "Flytning mislykkedes", - "move_error": "Kunne ikke flytte e-mails til den valgte mappe", - "email_tagged": "E-mail tagget", - "emails_tagged": "{count} e-mails tagget", - "tag_failed": "Tagning mislykkedes", - "identity_created": "Identitet oprettet succesfuldt", - "identity_updated": "Identitet opdateret succesfuldt", - "identity_deleted": "Identitet slettet", - "identity_set_primary": "Primær identitet opdateret", - "identity_create_failed": "Kunne ikke oprette identitet: {error}", - "identity_update_failed": "Kunne ikke opdatere identitet: {error}", - "identity_delete_failed": "Kunne ikke slette identitet: {error}", - "identity_unauthorized": "Du er ikke autoriseret til at sende fra denne e-mailadresse", - "identity_not_found": "Identitet ikke fundet", - "vacation_saved": "Fraværsbesked-indstillinger gemt", - "vacation_save_failed": "Kunne ikke gemme fraværsbesked-indstillinger", - "filters_saved": "Filtre gemt succesfuldt", - "filters_save_failed": "Kunne ikke gemme filtre", - "filters_deleted": "Filterregel slettet", - "templates_exported": "Skabeloner eksporteret succesfuldt", - "templates_imported": "{count, plural, one {# skabelon} other {# skabeloner}} importeret", - "templates_import_errors": "Nogle skabeloner kunne ikke importeres", - "templates_import_empty": "Ingen skabeloner fundet i filen", - "export_email_error": "Kunne ikke eksportere e-mail", - "import_email_success": "E-mail importeret succesfuldt", - "import_email_error": "Kunne ikke importere e-mail" - }, - "date": { - "today": "I dag", - "yesterday": "I går", - "this_week": "Denne uge", - "last_week": "Sidste uge", - "this_month": "Denne måned", - "older": "Ældre", - "just_now": "Lige nu", - "minutes_ago": "{count} minut siden", - "minutes_ago_plural": "{count} minutter siden", - "hours_ago": "{count} time siden", - "hours_ago_plural": "{count} timer siden", - "days_ago": "{count} dag siden", - "days_ago_plural": "{count} dage siden" - }, - "language": { - "title": "Sprog", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Vælg sprog", - "switch_to_english": "Skift til engelsk", - "switch_to_french": "Skift til fransk", - "switch_to_japanese": "Skift til japansk", - "switch_to_spanish": "Skift til spansk", - "switch_to_italian": "Skift til italiensk", - "switch_to_german": "Skift til tysk", - "switch_to_dutch": "Skift til nederlandsk", - "switch_to_portuguese": "Skift til portugisisk", - "switch_to_russian": "Skift til russisk", - "switching": "Skifter sprog...", - "switch": "Skift sprog", - "current": "Nuværende sprog", - "polish": "Polski", - "switch_to_polish": "Skift til polsk", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Skift til farsi", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Skift til mongolsk" - }, - "settings": { - "title": "Indstillinger", - "back_to_mail": "Tilbage til mail", - "save_success": "Indstillinger gemt succesfuldt", - "import_success": "Indstillinger importeret succesfuldt", - "import_error": "Kunne ikke importere indstillinger", - "reset_confirm": "Er du sikker på, at du vil nulstille alle indstillinger til standard?", - "unsaved_changes": "Du har ugemte ændringer", - "discard_changes": "Kassér ugemte ændringer?", - "discard": "Kassér", - "keep_editing": "Fortsæt redigering", - "search_placeholder": "Søg i indstillinger", - "search_clear": "Ryd søgning", - "search_no_results": "Ingen match i indstillinger", - "tabs": { - "appearance": "Udseende", - "language": "Sprog, region & tid", - "email": "E-mail-adfærd", - "composer": "Komponist", - "privacy": "Privatliv & sikkerhed", - "account": "Konto", - "identities": "Identiteter", - "vacation": "Fraværsbeskeder", - "advanced": "Avanceret", - "calendar": "Kalender", - "filters": "Filtre", - "templates": "Skabeloner", - "folders": "Mapper", - "keywords": "Tags", - "security": "Sikkerhed", - "files": "Filer", - "contacts": "Kontakter", - "encryption": "Kryptering", - "protocol_handlers": "Standardapps", - "sidebar_apps": "Sidepanel-apps", - "notifications": "Notifikationer", - "layout": "Layout", - "reading": "Læsning", - "composing": "Skrivning", - "downloads": "Downloads", - "content_senders": "Indhold & afsendere", - "about_data": "Om & data", - "debug": "Debug" - }, - "tab_groups": { - "general": "Generelt", - "account": "Konto & identitet", - "organization": "Mailorganisering", - "apps": "Apps", - "system": "System", - "appearance": "Udseende", - "mail": "Mail", - "privacy": "Privatliv & sikkerhed", - "advanced": "Avanceret" - }, - "appearance": { - "message_list_order": { - "label": "Rækkefølge i beskedlisten", - "description": "Hvordan beskeder sorteres i listen. Serveren sorterer, så det gælder hele mappen og ikke kun de indlæste beskeder.", - "preset": { - "chronological": "Kronologisk (nyeste først)", - "unread_first": "Ulæste først", - "starred_first": "Stjernemarkerede først", - "tagged_first": "Med mærkat først", - "custom": "Tilpasset" - }, - "tag_label": "Mærkat", - "advanced_show": "Avanceret: rediger sorteringsniveauer", - "advanced_hide": "Skjul avanceret", - "criterion_label": "Kriterium for niveau {n}", - "direction_label": "Retning for niveau {n}", - "criteria": { - "unread": "Ulæst", - "starred": "Stjernemarkeret", - "tag": "Mærkat", - "receivedAt": "Modtagelsesdato", - "sentAt": "Afsendelsesdato", - "from": "Afsender", - "subject": "Emne", - "size": "Størrelse" - }, - "direction": { - "unread_first": "Ulæste først", - "read_first": "Læste først", - "starred_first": "Stjernemarkerede først", - "unstarred_first": "Uden stjerne først", - "tagged_first": "Med mærkat først", - "untagged_first": "Uden mærkat først", - "newest_first": "Nyeste først", - "oldest_first": "Ældste først", - "a_to_z": "A → Å", - "z_to_a": "Å → A", - "largest_first": "Største først", - "smallest_first": "Mindste først" - }, - "add_level": "Tilføj niveau", - "remove_level": "Fjern niveau", - "move_up": "Flyt op", - "move_down": "Flyt ned", - "tiebreak_note": "Op til tre niveauer; senere niveauer afgør ved lighed. \"Nyeste først\" anvendes altid til sidst.", - "scope": { - "label": "Anvend på alle mapper", - "description": "Når den er slået fra, gælder rækkefølgen kun for Indbakken; andre mapper forbliver kronologiske." - }, - "unsupported_note": "Din mailserver understøtter ikke sortering efter læsestatus, stjerner eller mærkater. Disse niveauer ignoreres, og listen forbliver kronologisk." - }, - "title": "Udseende", - "description": "Tilpas udseendet af din webmail", - "theme": { - "label": "Tema", - "description": "Vælg dit foretrukne farveskema", - "light": "Lys", - "dark": "Mørk", - "system": "System" - }, - "language": { - "label": "Sprog", - "description": "Vælg dit foretrukne sprog" - }, - "font_size": { - "label": "Skriftstørrelse", - "description": "Juster tekststørrelse for bedre læsbarhed", - "small": "Lille", - "medium": "Mellem", - "large": "Stor" - }, - "list_density": { - "label": "Tæthed", - "description": "Kontrollér afstand og polstring i brugergrænsefladen", - "extra_compact": "Ekstra kompakt", - "compact": "Kompakt", - "regular": "Almindelig", - "comfortable": "Komfortabel" - }, - "animations": { - "label": "Aktivér animationer", - "description": "Vis glatte overgange og effekter" - }, - "toolbar_position": { - "label": "Værktøjslinjens position", - "description": "Hvor e-mail-handlingsknapper (Svar, Arkivér, Slet osv.) skal vises", - "top": "Top", - "below_subject": "Under emne" - }, - "toolbar_labels": { - "label": "Vis værktøjslinjeetiketter", - "description": "Vis tekstetiketter ved siden af værktøjslinjeikoner. Deaktivér for at spare plads, når du kender ikonerne." - }, - "hide_account_switcher": { - "label": "Skjul kontoskifter i sidepanel", - "description": "Skjul kontovælgeren øverst i mappesidepanelet. Du kan stadig skifte konto fra navigationslinjen nederst." - }, - "show_rail_account_list": { - "label": "Vis kontoavatarer på navigationslinje", - "description": "Vis individuelle kontocirkler nederst på navigationslinjen for hurtig skift, med en log-ud-knap nedenunder." - }, - "unified_mailbox": { - "label": "Samlet postkasse", - "description": "Vis samlede mapper (Indbakke, Sendt osv.) på tværs af alle tilknyttede konti", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Inkluder gruppepostkasser", - "description": "Inkluder også delte/gruppepostkasser i den samlede visning." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Farverige sidepane-ikoner", - "description": "Farvelæg mappe- og tag-ikoner efter type (blå indbakke, rød spam, grøn sendt osv.). Deaktivér for et monokromt sidepanel." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Vis samlet antal beskeder", - "description": "Viser det samlede antal beskeder ud for mapper og tags sammen med antallet af ulæste. Slå fra for kun at vise ulæste." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro-grænseflade (eksperimentel)", - "description": "Power user-layout kun til skrivebordet med beskedvisning på flere faner og arbejdsforløb på tværs af konti. Standardgrænsefladen påvirkes ikke; du kan skifte tilbage når som helst.", - "open_label": "Åbn Pro-grænsefladen", - "back_to_standard": "Tilbage til standard" - }, - "cross_unread": { - "label": "Alle konti: Ulæste", - "description": "Viser en post i sektionen Alle konti med ulæst post fra alle konti, på tværs af alle mapper undtagen spam, sendt, arkiv, papirkurv og kladder." - }, - "cross_starred": { - "label": "Alle konti: Med stjerne", - "description": "Viser en post i sektionen Alle konti med post med stjerne fra alle konti, på tværs af alle mapper undtagen spam, sendt, arkiv, papirkurv og kladder." - }, - "cross_all": { - "label": "Alle konti: Al post", - "description": "Viser en post i sektionen Alle konti med al post fra alle konti, på tværs af alle mapper undtagen spam, sendt, arkiv, papirkurv og kladder." - } - }, - "keywords": { - "title": "E-mail-tags", - "description": "Definér tags til at organisere dine e-mails med farver. Disse gemmes som JMAP-nøgleord på serveren.", - "add_keyword": "Tilføj tag", - "label_field": "Visningsnavn", - "label_placeholder": "f.eks. Arbejde, Privat, Vigtigt", - "id_field": "Tag-ID", - "id_placeholder": "f.eks. arbejde, privat", - "color_field": "Farve", - "id_exists": "Dette tag-ID findes allerede", - "edit": "Redigér tag", - "delete": "Slet tag", - "save": "Gem", - "add": "Tilføj", - "cancel": "Annuller", - "migrating": "Opdaterer tag på eksisterende e-mails…", - "migration_error": "Kunne ikke opdatere tag på eksisterende e-mails", - "nesting": { - "label": "Indlejrede tags", - "description": "Indlejr tags under andre tags og vis dem som et træ i sidepanelet." - }, - "parent_field": "Overordnet tag", - "no_parent": "Intet overordnet tag", - "too_long": "Denne tagsti er for lang (højst {max} tegn)", - "has_children_locked": "Andre tags er indlejret under dette, så dets navn og overordnede tag er låst. Flyt eller fjern dem først.", - "has_children_delete": "Fjern først de tags, der er indlejret under dette", - "visibility_field": "Synlighed i sidepanel", - "visibility": { - "show": "Vis", - "unread": "Vis ved ulæste", - "hide": "Skjul" - }, - "unrecognized": { - "title": "Gendan tags fra serveren", - "description": "Tagnavne og farver gemmes i denne browser, men selve taggene ligger på dine e-mails som JMAP-nøgleord. Scan din post for nøgleord, som intet tag her forklarer, og tilføj dem igen.", - "scan": "Søg efter tags", - "rescan": "Scan igen", - "scanning": "Scanner… {scanned} af {total} e-mails", - "none": "Alle tags på dine e-mails er allerede defineret her.", - "messages": "{count, plural, one {# e-mail} other {# e-mails}}", - "add_all": "Tilføj alle {count}", - "partial": "Kun de {scanned} nyeste af {total} e-mails blev scannet. Tags, der kun bruges på ældre post, mangler måske.", - "error": "Kunne ikke scanne din post for tags." - } - }, - "notifications": { - "test_sound": "Test notifikationslyd", - "sounds": { - "default": "Standard (Bip)", - "cheerful": "Glad", - "involved": "Engageret", - "swift": "Hurtig gestus", - "relax": "Afslapning" - }, - "push": { - "title": "Baggrundsnotifikationer", - "description": "Modtag systemnotifikationer for ny mail, når dette site er lukket. Leveres via Bulwark push-relæet; relæet ser aldrig mail-indhold.", - "relay_label": "Push-relæ", - "relay_desc": "Som standard det hosted Bulwark-relæ. Ændr kun, hvis du selv-host'er.", - "relay_locked": "Indstillet af administrator", - "relay_locked_desc": "Push-relæ-URL'en er indstillet af din administrator og kan ikke ændres.", - "status_active": "Aktiv på denne enhed", - "status_inactive": "Ikke aktiveret på denne enhed", - "status_unsupported": "Denne browser understøtter ikke Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "Arbejder…", - "enable": "Aktivér", - "reenable": "Genregistrér", - "disable": "Deaktivér", - "confirm_disable_title": "Deaktivér baggrundsnotifikationer?", - "confirm_disable_message": "Denne enhed stopper med at modtage alarmer, når sitet er lukket.", - "dismiss_aria": "Afvis notifikationsprompt", - "ios_hint": "På iOS skal du installere sitet på din startskærm først - Safari leverer kun Web Push til installerede PWA'er.", - "devices_title": "Registrerede enheder", - "devices_desc": "Alle enheder, der er registreret til at modtage baggrundsnotifikationer for denne konto. Tilbagekalder du en, stopper den med at modtage med det samme.", - "devices_loading": "Indlæser registrerede enheder…", - "devices_empty": "Ingen enheder er registreret til baggrundsnotifikationer.", - "devices_error": "Kunne ikke indlæse de registrerede enheder.", - "devices_refresh": "Opdater", - "device_this": "Denne enhed", - "device_other": "Anden enhed", - "device_status_active": "Leverer notifikationer", - "device_status_inactive": "Har aldrig leveret en notifikation", - "device_status_unknown": "Ukendt for dette relæ", - "device_expires": "Udløber {date}", - "revoke": "Tilbagekald", - "confirm_revoke_title": "Tilbagekald denne enhed?", - "confirm_revoke_message": "Den stopper med at modtage baggrundsnotifikationer med det samme. Slår du push til igen på enheden, registreres den på ny.", - "confirm_revoke_message_this": "Denne enhed stopper med at modtage baggrundsnotifikationer med det samme." - }, - "sound_selection": { - "title": "Notifikationslyd", - "description": "Vælg hvilken lyd der skal afspilles for notifikationer", - "choose": "Lyd", - "choose_desc": "Vælg en notifikationstone og klik på højttalerikonet for at forhåndsvise den" - }, - "email": { - "title": "E-mail-notifikationer", - "description": "Konfigurér notifikationer for indkommende e-mails", - "enabled": "E-mail-notifikationer", - "enabled_desc": "Vis notifikationer, når nye e-mails ankommer", - "sound": "Notifikationslyd", - "sound_desc": "Afspil en lydadvarsel, når nye e-mails ankommer", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Kalendernotifikationer", - "description": "Konfigurér notifikationer for kalenderbegivenheder", - "enabled": "Begivenhedsnotifikationer", - "enabled_desc": "Vis alarmer for kommende kalenderbegivenheder", - "sound": "Notifikationslyd", - "sound_desc": "Afspil en lyd for kalenderpåmindelser", - "invitation_parsing": "Pars e-mail-invitationer", - "invitation_parsing_desc": "Registrér kalenderinvitationer i e-mail-vedhæftninger og vis kalenderhandlinger" - } - }, - "language_region": { - "title": "Sprog, region & tid", - "description": "Sprog, datoformat, tidsformat og andre regionale indstillinger", - "language": { - "label": "Sprog", - "description": "Vælg dit foretrukne sprog", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Datoformat", - "description": "Sådan vises datoer på e-mail-listen", - "smart": "Smart (regionalt)", - "relative": "Relativ (for 1 t siden, for 2 d siden)", - "full": "Altid fuld dato", - "preview_today": "I dag:", - "preview_this_week": "Denne uge:", - "preview_older": "Ældre:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Tidsformat", - "description": "Vælg mellem 12-timers eller 24-timers ur", - "12h": "12-timers", - "24h": "24-timers" - }, - "first_day": { - "label": "Første ugedag", - "description": "Start ugen på søndag eller mandag", - "sunday": "Søndag", - "monday": "Mandag" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Ryd søgningen ved skift af mappe", - "description": "Klik på en mappe (Indbakke, Sendt …) rydder den aktive søgning og viser mappen. Når det er slået fra, bevares søgningen og køres igen i den valgte mappe." - }, - "request_read_receipt": { - "label": "Anmod om læsekvittering som standard", - "description": "Aktivér anmodningen om læsekvittering på forhånd, når du skriver en ny besked." - }, - "read_receipt_response": { - "label": "Svar på anmodninger om læsekvittering", - "description": "Hvad der skal ske, når en indgående besked beder om en læsekvittering.", - "ask": "Spørg hver gang", - "always": "Send altid", - "never": "Send aldrig" - }, - "title": "E-mail-adfærd", - "description": "Konfigurér hvordan e-mails håndteres", - "mark_read": { - "label": "Markér som læst", - "description": "Hvornår e-mails skal markeres som læst, når de åbnes", - "instant": "Øjeblikkeligt", - "delay_3s": "Efter 3 sekunder", - "delay_5s": "Efter 5 sekunder", - "never": "Aldrig" - }, - "delete_action": { - "label": "Slet-handling", - "description": "Hvad der sker, når du sletter en e-mail", - "trash": "Flyt til papirkurv", - "trash_and_read": "Flyt til papirkurv og marker som læst", - "permanent": "Slet permanent", - "warning": "E-mails vil blive slettet permanent og kan ikke gendannes. Denne handling er irreversibel." - }, - "message_spacing": { - "label": "Beskedafstand", - "description": "Luft omkring beskedens indhold i læseren", - "auto": "Automatisk (anbefalet)", - "always": "Tilføj altid afstand", - "edge": "Kant til kant" - }, - "plain_text_font": { - "label": "Skrifttype til ren tekst", - "description": "Skrifttype til e-mails i ren tekst", - "mono": "Monospace", - "sans": "Normal (samme som appen)" - }, - "archive_mode": { - "label": "Arkivér i", - "description": "Hvordan e-mails organiseres ved arkivering", - "single": "En enkelt mappe", - "year": "En mappe pr. år", - "month": "En mappe pr. måned", - "reorganize": "Omorganisér eksisterende arkiv", - "reorganize_success": "{count, plural, =0 {Ingen e-mails at omorganisere} =1 {1 e-mail omorganiseret} other {# e-mails omorganiseret}}", - "reorganize_error": "Kunne ikke omorganisere arkiv" - }, - "permanently_delete_junk": { - "label": "Slet spam permanent", - "description": "Slet e-mails fra spam-mappen permanent i stedet for at flytte dem til papirkurven" - }, - "mail_layout": { - "label": "Mail-layout", - "description": "Vælg mellem den klassiske opdelte læserude, en Gmail-lignende fokuseret læsestrøm eller en Zimbra-lignende læserude i bunden.", - "split": "Opdelt rude", - "split_description": "Hold beskedlisten og læseruden synlige side om side.", - "focus": "Fokuseret liste", - "focus_description": "Vis én linje pr. besked og åbn mail i fuld bredde, mens mappesidepanelet forbliver synligt.", - "horizontal": "Læserude i bunden", - "horizontal_description": "Vis beskedlisten øverst og åbn den valgte besked i en læserude nedenunder." - }, - "show_preview": { - "label": "Vis forhåndsvisningstekst", - "description": "Vis e-mail-forhåndsvisning i listen", - "focus_description": "Vis indlejret forhåndsvisningstekst inde i den fokuserede én-linjers beskedliste" - }, - "disable_threading": { - "label": "Deaktivér samtale-gruppering", - "description": "Vis e-mails som individuelle beskeder i stedet for grupperet efter samtale" - }, - "plain_text_mode": { - "label": "Kun almindelig tekst", - "description": "Deaktivér den rige teksteditor og send alle e-mails som ren tekst, inklusive svar og videresendelser" - }, - "auto_select_reply_identity": { - "label": "Svar fra modtaget adresse", - "description": "Når du svarer, send fra den adresse som beskeden oprindeligt blev sendt til. Matcher først identiteter; for domæne catch-all-leveringer omskriver Fra-headeren til aliaset, mens der sendes gennem din primære identitet." - }, - "reply_identity_match": { - "label": "Matchning af modtageradresse", - "description": "Hvilke modtageradresser der tæller som dine. „Kun præcis adresse“ vælger udelukkende en af dine opsatte identiteter; „samme domæne“ behandler desuden enhver anden adresse på dine identitetsdomæner som et catch-all-alias og omskriver Fra-headeren til den. Vælg præcis adresse, hvis disse adresser er distributionslister.", - "exact": "Kun præcis adresse", - "domain": "Enhver adresse på mine domæner" - }, - "signature_position": { - "label": "Signaturplacering", - "description": "Hvor din signatur indsættes i svar og videresendelser. Før citeret tekst læses naturligt som en afslutning på svaret; under bevarer den oprindelige besked sammenhængende.", - "above_quote": "Før citeret tekst", - "below_quote": "Efter citeret tekst" - }, - "signature_separator": { - "label": "Signatur-afgrænser", - "description": "Præfiksér signaturen med standard \"-- \"-afgrænserlinjen (RFC 3676). Slå fra, hvis du hellere vil gå direkte fra din besked til signaturen." - }, - "sub_address_delimiter": { - "label": "Underadresse-afgrænser", - "description": "Tegn der adskiller dit brugernavn fra et underadresse-tag. Match afgrænseren som din mailserver bruger (f.eks. bruger{delimiter}tag@domæne.dk).", - "option": "{delimiter} (bruger{delimiter}tag@domæne.dk)", - "custom": "Brugerdefineret…", - "custom_input_label": "Brugerdefineret afgrænsertegn" - }, - "attachment_click_action": { - "label": "Klik-handling for vedhæftninger", - "description": "Vælg om et klik på en vedhæftet fil forhåndsviser den eller downloader den med det samme", - "preview": "Forhåndsvis når muligt", - "download": "Download med det samme" - }, - "attachment_position": { - "label": "Placering af vedhæftninger", - "description": "Hvor vedhæftninger skal vises i e-mail-headeren", - "beside-sender": "Ved siden af afsender", - "below-header": "Under header" - }, - "emails_per_page": { - "10": "10 e-mails", - "25": "25 e-mails", - "50": "50 e-mails", - "100": "100 e-mails", - "label": "E-mails pr. side", - "description": "Antal e-mails der indlæses ad gangen" - }, - "always_light_mode": { - "label": "Vis altid e-mails i lys tilstand", - "description": "Vis e-mail-indhold i lys tilstand, selv når appen er i mørk tilstand, for at undgå problemer med mørk tilstand-konvertering" - }, - "external_content": { - "label": "Eksternt indhold", - "description": "Hvordan billeder og eksternt indhold håndteres", - "ask": "Spørg altid", - "block": "Bloker altid", - "allow": "Tillad altid" - }, - "trusted_senders": { - "label": "Betroede afsendere", - "description": "Administrér afsendere hvis billeder indlæses automatisk", - "count_zero": "Ingen", - "count_one": "1 afsender", - "count_other": "{count} afsendere", - "modal_title": "Betroede afsendere", - "empty_title": "Ingen betroede afsendere endnu", - "empty_description": "Når du ser en e-mail med blokerede billeder, klik på \"Stol altid på denne afsender\" for at tilføje dem her.", - "add_manually": "Tilføj afsender manuelt", - "add_button": "Tilføj", - "add_placeholder": "Indtast e-mailadresse", - "search_placeholder": "Søg efter afsendere...", - "no_results": "Ingen afsendere matcher din søgning", - "remove": "Fjern", - "close": "Luk", - "invalid_email": "Indtast en gyldig e-mailadresse", - "already_added": "Denne afsender er allerede betroet", - "save_error": "Kunne ikke gemme - tjek Kontakter-debugloggen for detaljer", - "use_address_book_label": "Synkronisér med adressebog", - "use_address_book_description": "Gem betroede afsendere i en dedikeret \"Betroede afsendere\"-adressebog, så de synkroniseres på tværs af alle dine enheder" - }, - "hover_actions": { - "label": "Hurtige hover-handlinger", - "description": "Vælg hvilke hurtige handlinger der vises, når du holder musen over en e-mail i listen", - "delete": "Slet", - "star": "Stjernemarkér / Fjern stjerne", - "mark_read": "Markér læst/ulæst", - "archive": "Arkivér", - "tag": "Tag", - "spam": "Markér som spam", - "not_spam": "Ikke spam", - "none_selected": "Ingen handlinger valgt", - "mode_label": "Visningstilstand", - "mode_inline": "Indlejret", - "mode_floating": "Flydende", - "corner_label": "Flydende position", - "corner_top-left": "Øverst til venstre", - "corner_top-right": "Øverst til højre", - "corner_bottom-left": "Nederst til venstre", - "corner_bottom-right": "Nederst til højre" - }, - "default_mail_program": { - "label": "Standard mailprogram", - "description": "Registrér {appName} som dit standard mailprogram for mailto:-links", - "button": "Angiv som standard", - "success": "Browseren blev bedt om at angive som standard", - "error": "Din browser understøtter ikke denne funktion" - }, - "attachment_reminder": { - "label": "Påmindelse om vedhæftning", - "description": "Advar før afsendelse, når din besked nævner vedhæftninger, men ingen er vedhæftet", - "keywords_label": "Udløsende nøgleord", - "keywords_description": "Ord eller sætninger der udløser påmindelsen, når de findes i din besked", - "add_placeholder": "Tilføj nøgleord...", - "add": "Tilføj", - "remove": "Fjern" - }, - "empty_subject_warning": { - "label": "Advarsel om tomt emne", - "description": "Bed om bekræftelse, før en besked uden emne sendes." - }, - "hide_inline_image_attachments": { - "label": "Skjul indlejrede billeder fra vedhæftninger", - "description": "Billeder indlejret i beskedteksten vises ikke som separate vedhæftninger" - }, - "attachment_image_previews": { - "label": "Vis billedforhåndsvisninger i vedhæftninger", - "description": "Vis billedvedhæftninger som miniaturekort i stedet for generiske filikoner" - }, - "send_delay": { - "label": "Fortryd afsendelse / sendeforsinkelse", - "description": "Forsink normale afsendelser med et kort serverstyret tidsrum.", - "off": "Fra", - "seconds": "{seconds} sekunder", - "unsupported": "Den aktuelle konto annoncerer ikke understøttelse af forsinket afsendelse. Indstillingen gemmes stadig for andre konti." - }, - "return_to_list_after_action": { - "label": "Vend tilbage til listen efter sletning eller markering som ulæst", - "description": "Efter sletning eller markering af den åbne besked som ulæst vendes der tilbage til beskedlisten i stedet for at åbne den næste besked." - }, - "swipe_right_action": { - "label": "Handling ved strygning til højre (mobil)", - "description": "På touch-enheder den handling, der udføres, når du stryger en besked til højre i listen." - }, - "swipe_left_action": { - "label": "Handling ved strygning til venstre (mobil)", - "description": "På touch-enheder den handling, der udføres, når du stryger en besked til venstre i listen." - }, - "swipe_actions": { - "none": "Ingen", - "archive": "Arkivér", - "delete": "Slet", - "mark_read": "Skift læst/ulæst", - "star": "Skift stjerne", - "spam": "Markér som spam" - }, - "rtl_editing": { - "label": "Understøttelse af højre-mod-venstre-redigering", - "description": "Tilføjer en retningsknap til værktøjslinjen i editoren, så afsnit kan sættes venstre-mod-højre eller højre-mod-venstre" - } - }, - "composer": { - "title": "Komponist", - "description": "Konfigurér indstillinger for e-mail-komposition", - "autosave": { - "label": "Auto-gem interval", - "description": "Hvor ofte kladder gemmes automatisk", - "30s": "Hvert 30. sekund", - "1m": "Hvert minut", - "2m": "Hvert 2. minut", - "5m": "Hvert 5. minut" - }, - "send_confirmation": { - "label": "Send-bekræftelse", - "description": "Spørg om bekræftelse før afsendelse af e-mails" - }, - "default_reply": { - "label": "Standard svar-tilstand", - "description": "Standardhandling ved klik på svar", - "reply": "Svar", - "reply_all": "Svar alle" - } - }, - "privacy": { - "title": "Privatliv & sikkerhed", - "description": "Administrér dine privatlivs- og sikkerhedsindstillinger", - "external_images": { - "label": "Bloker eksterne billeder", - "description": "Forhindr sporing via eksterne billeder" - }, - "session_timeout": { - "label": "Sessionstimeout", - "description": "Log automatisk ud efter inaktivitet", - "never": "Aldrig", - "30m": "30 minutter", - "1h": "1 time", - "4h": "4 timer" - }, - "clear_cache": { - "label": "Ryd cache", - "description": "Fjern cached data og midlertidige filer", - "button": "Ryd cache", - "confirm": "Er du sikker på, at du vil rydde cachen?", - "success": "Cache ryddet succesfuldt" - } - }, - "account": { - "title": "Konto", - "description": "Se dine kontooplysninger", - "name_label": "Visningsnavn", - "username_label": "Brugernavn", - "account_type_label": "Kontotype", - "auth_method_label": "Godkendelse", - "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", - "auth_method_basic": "Adgangskode", - "demo_account": "Demo-konto", - "email": { - "label": "E-mailadresse", - "value": "{email}" - }, - "server": { - "label": "JMAP-server", - "value": "{server}" - }, - "storage": { - "label": "Lagerforbrug", - "used": "{used} af {total} brugt", - "percentage": "{percent}% brugt" - }, - "last_sync": { - "label": "Sidste synkronisering", - "value": "{time}" - }, - "accounts": { - "title": "Indloggede konti", - "description": "Træk for at ændre rækkefølgen af konti i kontomenuen", - "active": "Aktuelt aktiv konto", - "default_badge": "Standardkonto", - "set_default": "Indstil som standard", - "switch_to": "Skift til denne konto", - "move_up": "Flyt op", - "move_down": "Flyt ned", - "drag_handle": "Træk for at omarrangere", - "add": "Tilføj konto" - }, - "shared_accounts": { - "title": "Delt med mig", - "description": "Gruppe- og delte konti, du kan administrere. Vælg en for at redigere dens filtre, autosvar ved fravær, kalendere og kontakter.", - "shared_label": "Delt konto" - } - }, - "security": { - "title": "Kontosikkerhed", - "description": "Administrér din adgangskode, to-faktor-godkendelse og sikkerhedsindstillinger", - "detecting": "Registrerer serverfunktioner...", - "not_available": "Kontosikkerhedsstyring er ikke tilgængelig for denne mailserver. Nødvendige tilladelser er muligvis deaktiveret. Se dokumentationen for detaljer.", - "password": { - "title": "Skift adgangskode", - "current": "Nuværende adgangskode", - "new": "Ny adgangskode", - "confirm": "Bekræft ny adgangskode", - "submit": "Skift adgangskode", - "success": "Adgangskode ændret succesfuldt", - "error_title": "Adgangskodeændring mislykkedes", - "error_mismatch": "De nye adgangskoder er ikke ens", - "error_min_length": "Adgangskoden skal være mindst 8 tegn", - "error_generic": "Kunne ikke ændre adgangskode" - }, - "display_name": { - "label": "Visningsnavn", - "description": "Dit navn som det vises på serveren", - "placeholder": "Indtast dit visningsnavn", - "save": "Gem", - "success": "Visningsnavn opdateret", - "error": "Kunne ikke opdatere visningsnavn" - }, - "totp": { - "section_title": "To-faktor-godkendelse", - "label": "TOTP-godkendelse", - "description": "Tilføj et ekstra lag af sikkerhed med en tidsbaseret engangskode", - "active": "Aktiveret", - "inactive": "Deaktiveret", - "enabled": "To-faktor-godkendelse aktiveret", - "disabled": "To-faktor-godkendelse deaktiveret", - "enable_error": "Kunne ikke aktivere 2FA", - "disable_error": "Kunne ikke deaktivere 2FA", - "setup_instructions": "Kopier denne URL ind i din autentificeringsapp (Google Authenticator, Authy osv.):", - "verification_code": "Bekræftelseskode", - "confirm": "Bekræft", - "disable": "Deaktivér", - "disable_confirm_prompt": "Indtast din adgangskode for at deaktivere to-faktor-godkendelse.", - "password_required": "Adgangskode er påkrævet", - "code_required": "Bekræftelseskode er påkrævet", - "code_invalid": "Ugyldig bekræftelseskode. Kontrollér din autentificeringsapp og prøv igen." - }, - "app_passwords": { - "title": "App-adgangskoder", - "description": "Opret adgangskoder til apps, der ikke understøtter to-faktor-godkendelse", - "add": "Tilføj", - "create": "Opret", - "cancel": "Annuller", - "done": "Færdig", - "generate": "Generér", - "name_label": "App-navn", - "name_placeholder": "f.eks. Thunderbird, iPhone Mail", - "expires_label": "Udløber (valgfrit)", - "allowed_ips_label": "Tilladte IP'er (valgfrit)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Komma- eller mellemrumssepareret. Lad være tom for at tillade alle IP'er.", - "password_label": "Adgangskode (lad være tom for auto-generering)", - "password_placeholder": "Auto-genereret hvis tom", - "copy_now_warning": "Kopier denne adgangskode nu - den vil ikke blive vist igen.", - "added": "App-adgangskode oprettet", - "removed": "App-adgangskode fjernet", - "add_error": "Kunne ikke oprette app-adgangskode", - "remove_error": "Kunne ikke fjerne app-adgangskode", - "none": "Ingen app-adgangskoder konfigureret" - }, - "api_keys": { - "title": "API-nøgler", - "description": "Opret API-nøgler til scripts og integrationer, der taler direkte med serveren", - "name_label": "Nøglenavn", - "name_placeholder": "f.eks. Backup-script, CI-runner", - "copy_now_warning": "Kopier denne API-nøgle nu - den vil ikke blive vist igen.", - "added": "API-nøgle oprettet", - "removed": "API-nøgle fjernet", - "add_error": "Kunne ikke oprette API-nøgle", - "remove_error": "Kunne ikke fjerne API-nøgle", - "none": "Ingen API-nøgler konfigureret" - }, - "encryption": { - "section_title": "Kryptering af lagrede data", - "label": "E-mail-kryptering", - "description": "Krypter lagrede e-mails på serveren for ekstra privatliv", - "active": "{type}-kryptering aktiveret", - "inactive": "Deaktiveret", - "enabled": "Kryptering af lagrede data aktiveret", - "disabled_success": "Kryptering af lagrede data deaktiveret", - "error": "Kunne ikke opdatere krypteringsindstillinger", - "algorithm_label": "Krypteringsalgoritme", - "encrypt_on_append": "Krypter nye e-mails ved upload", - "allow_spam_training": "Tillad spamtræning, før e-mails krypteres", - "enable_encryption": "Aktivér kryptering", - "enable_tooltip": "Aktivér kryptering med denne nøgle", - "configure_title": "Aktivér kryptering med denne nøgle", - "enable_button": "Aktivér", - "disable_tooltip": "Deaktivér kryptering", - "enabled_success": "Kryptering af lagrede data aktiveret" - }, - "email_client": { - "title": "E-mail-klientopsætning", - "description": "Brug disse legitimationsoplysninger til at konfigurere din desktop- eller mobil-e-mailklient (Thunderbird, Apple Mail, Outlook osv.)", - "jmap_username_label": "JMAP-brugernavn", - "copy": "Kopier", - "copied": "Kopieret", - "password_instructions": "Brug dit JMAP-brugernavn ovenfor sammen med en app-adgangskode for at logge ind i din e-mailklient. Opret en app-adgangskode i sektionen ovenfor, hvis du ikke allerede har gjort det." - }, - "link_device": { - "title": "Tilknyt mobilapp", - "description": "Log ind i Bulwark Mail-mobilappen uden at skrive noget. Generér en QR-kode her, og scan den fra appens loginskærm.", - "generate": "Vis QR-kode", - "regenerate": "Vis en ny kode", - "instructions": "Åbn Bulwark Mail-appen, tryk på \"Scan QR-kode\" på loginskærmen, og ret dit kamera herhen.", - "expires_in": "Denne kode udløber om {seconds} sekunder. Den kan kun bruges én gang.", - "expired": "Denne kode er udløbet.", - "generating": "Genererer…", - "error": "Kunne ikke oprette en parringskode. Prøv igen." - }, - "public_keys": { - "name_label": "Navn på offentlig nøgle", - "name_placeholder": "f.eks. Min nøgle", - "key_label": "Offentlig nøgle (S/MIME eller PGP i ASCII-format)", - "key_placeholder": "Indsæt din offentlige nøgle her", - "none": "Ingen offentlige nøgler konfigureret" - } - }, - "identities": { - "title": "Afsenderidentiteter", - "description": "Administrér e-mailadresser du kan sende fra", - "identities_count": { - "label": "Dine identiteter", - "description": "E-mailadresser konfigureret til afsendelse", - "count_zero": "Ingen identiteter", - "count_one": "1 identitet", - "count_other": "{count} identiteter" - }, - "manage": "Administrér identiteter", - "sub_addressing": { - "label": "Underadressering", - "description": "Brug tags som bruger+tag@domæne.dk til at organisere indkommende mail", - "learn_more": "Lær mere" - } - }, - "vacation": { - "title": "Fraværsbeskeder", - "description": "Svar automatisk på indkommende e-mails, mens du er væk", - "loading": "Indlæser ferieindstillinger...", - "not_supported": "Din mailserver understøtter ikke fraværsbeskeder.", - "fetch_error": "Kunne ikke indlæse ferieindstillinger. Prøv igen.", - "status": { - "label": "Fraværsbeskeder", - "description": "Send et automatisk svar til personer, der skriver til dig", - "active": "Aktiv", - "inactive": "Inaktiv" - }, - "date_range": { - "title": "Datoperiode", - "description": "Begræns eventuelt auto-svaret til en bestemt periode", - "start": "Startdato", - "start_description": "Lad være tom for ingen startbegrænsning", - "end": "Slutdato", - "end_description": "Lad være tom for ingen slutbegrænsning" - }, - "message": { - "title": "Auto-svar-besked", - "description": "Beskeden der sendes som svar", - "subject_label": "Emne", - "subject_description": "Emnelinje for auto-svaret", - "subject_placeholder": "Fraværende", - "body_label": "Beskedtekst", - "body_description": "Ren tekst-beskedindhold", - "body_placeholder": "Tak for din e-mail. Jeg er i øjeblikket fraværende og vil svare, når jeg vender tilbage.", - "html_label": "Formateret besked (HTML)", - "html_description": "Tilføj en formateret version med links og styling. Modtagere, hvis mailprogram ikke kan vise den, får den almindelige tekst ovenfor.", - "html_placeholder": "Skriv et formateret fraværssvar…" - }, - "preview": { - "title": "Forhåndsvisning", - "show": "Vis forhåndsvisning", - "hide": "Skjul forhåndsvisning" - }, - "save": "Gem ændringer", - "saving": "Gemmer...", - "warnings": { - "end_before_start": "Slutdato skal være efter startdato", - "start_in_past": "Startdato er tidligere end i dag", - "empty_body": "Beskedteksten er tom - modtagere vil modtage et tomt svar", - "subject_too_long": "Emnet er for langt til serveren (højst {max} byte).", - "body_too_long": "Beskeden er for lang til serveren (højst {max} byte inkl. formatering)." - } - }, - "folders": { - "role_memos": "Noter", - "title": "Mapper", - "description": "Administrér dine e-mail-mapper og tildel standardroller", - "folder_list": "Dine mapper", - "folder_list_description": "Klik på et mappeikon for at tilpasse det", - "standard_roles": "Standard mapproller", - "standard_roles_description": "Tildel hvilke mapper der bruges til standard postkasseroller som Indbakke, Sendt, Papirkurv osv.", - "role_inbox": "Indbakke", - "role_drafts": "Kladder", - "role_sent": "Sendt", - "role_trash": "Papirkurv", - "role_junk": "Spam", - "role_archive": "Arkiv", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Ingen", - "create_folder": "Opret mappe", - "create_subfolder": "Opret undermappe", - "reorder": "Træk for at ændre rækkefølgen", - "reorder_error": "Kunne ikke ændre rækkefølgen af mapper", - "move_error": "Kunne ikke flytte mappen", - "subfolder_of": "Inde i {name}", - "subfolder_name": "Undermappenavn", - "new_folder_name": "Mappenavn", - "rename": "Omdøb", - "change_icon": "Skift ikon", - "delete": "Slet", - "confirm_delete": "Er du sikker på, at du vil slette \"{name}\"? E-mails i denne mappe flyttes til papirkurven.", - "create": "Opret", - "cancel": "Annuller", - "no_folders": "Ingen brugerdefinerede mapper", - "cannot_delete_role": "Kan ikke slette en mappe med en standardrolle. Fjern rollen først.", - "folder_created": "Mappe oprettet", - "folder_renamed": "Mappe omdøbt", - "folder_deleted": "Mappe slettet", - "role_updated": "Mappens rolle opdateret", - "error_create": "Kunne ikke oprette mappe", - "error_rename": "Kunne ikke omdøbe mappe", - "error_delete": "Kunne ikke slette mappe", - "error_delete_has_children": "Kan ikke slette mappe: den indeholder stadig undermapper. Slet eller flyt dem først.", - "error_delete_has_email": "Kan ikke slette mappe: den indeholder stadig e-mails. Flyt eller slet dem først.", - "error_role": "Kunne ikke opdatere mappens rolle" - }, - "advanced": { - "title": "Avanceret", - "description": "Avancerede indstillinger og udviklerindstillinger", - "debug_mode": { - "label": "Debug-tilstand", - "description": "Aktivér detaljeret logning til fejlfinding" - }, - "debug_categories": { - "description": "Vælg hvilke kategorier der skal logges. Deaktivér kategorier du ikke har brug for at reducere konsolstøj.", - "jmap": "JMAP-klient", - "jmap_description": "Mappeoperationer, e-mail-hentning og JMAP-protokolforespørgsler", - "calendar": "Kalender", - "calendar_description": "Kalenderbegivenheder, import og planlægningsbeskeder", - "tasks": "Opgaver", - "tasks_description": "Kalenderopgaveoprettelse, -hentning og -opdateringer", - "auth": "Godkendelse", - "auth_description": "Login, TOTP, token-udveksling og sessionsstyring", - "filters": "Filtre", - "filters_description": "Sieve-filterregler og feriescripts", - "email": "E-mail-visning", - "email_description": "E-mail-visning, TNEF-behandling og markér-som-læst", - "push": "Push-notifikationer", - "push_description": "Push-notifikationsopsætning og -levering", - "contacts": "Kontakter & adressebøger", - "contacts_description": "Kontaktsynkronisering, adressebogsoperationer og betroede afsendere" - }, - "settings_sync": { - "label": "Indstillingssynkronisering", - "description": "Synkronisér dine indstillinger på tværs af browsere og enheder" - }, - "sender_favicons": { - "label": "Afsender-favicons", - "description": "Vis hjemmesideikoner som profilbilleder for forretningsafsendere" - }, - "show_avatars_in_junk": { - "label": "Vis avatarer i spam-mappe", - "description": "Vis profilbilleder og favicons for afsendere i spam-mappen. Deaktiveret som standard for at undgå at give visuel legitimitet til phishing-forsøg." - }, - "keyboard_shortcuts": { - "label": "Tastaturgenveje", - "description": "Se tilgængelige tastaturgenveje", - "button": "Vis genveje" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Nulstil indstillinger", - "description": "Gendan alle indstillinger til standardværdier", - "button": "Nulstil til standard" - }, - "export_settings": { - "label": "Eksportér indstillinger", - "description": "Download dine indstillinger som JSON", - "button": "Eksportér" - }, - "import_settings": { - "label": "Importér indstillinger", - "description": "Upload indstillinger fra JSON-fil", - "button": "Importér" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Sidepanel-apps", - "description": "Administrér brugerdefinerede apps og links i dit sidepanel", - "keep_loaded": "Hold apps indlæst", - "keep_loaded_description": "Hold indlejrede apps kørende i baggrunden, når du skifter mellem dem, for at undgå genindlæsning", - "managed_title": "Leveret af din administrator", - "managed_description": "Disse apps er sat op for alle og kan ikke ændres.", - "manage_title": "Brugerdefinerede apps", - "manage_description": "Tilføj, redigér eller fjern brugerdefinerede apps fra dit sidepanel" - }, - "contacts": { - "title": "Kontakter", - "description": "Importér og eksportér dine kontakter", - "group_by_letter_label": "Gruppér efter første bogstav", - "sort_by_last_name_label": "Sortér efter efternavn", - "sort_by_last_name_description": "Sortér kontaktlisten efter efternavn, så familiemedlemmer står samlet", - "group_by_letter_description": "Vis alfabetiske sektionsoverskrifter i kontaktlisten", - "import_label": "Importér kontakter", - "import_description": "Importér kontakter fra en vCard-fil (.vcf)", - "export_label": "Eksportér kontakter", - "export_description": "Eksportér alle kontakter som en vCard-fil (.vcf)", - "manage_title": "Adressebøger", - "manage_description": "Omdøb dine adressebøger", - "no_address_books": "Ingen adressebøger fundet", - "categories_title": "Kategorier", - "categories_description": "Omdøb kontaktkategorier", - "no_categories": "Ingen kategorier fundet" - }, - "downloads": { - "title": "Downloads", - "description": "Tilpas hvordan downloadede e-mails og vedhæftede filer navngives.", - "reset": "Gendan standard", - "preview": "Forhåndsvisning:", - "email_template": { - "label": "E-mail-filnavn (.eml)", - "description": "Skabelon som bruges når du eksporterer en e-mail eller trækker den ud i filsystemet. Endelsen .eml tilføjes automatisk." - }, - "attachment_template": { - "label": "Filnavn på vedhæftet fil", - "description": "Skabelon som bruges når du downloader eller trækker en vedhæftet fil ud. Hvis du udelader '{filename}' og '{ext}', bevares den oprindelige endelse." - }, - "bundle_template": { - "label": "Filnavn for .zip med flere e-mails", - "description": "Skabelon som bruges når du trækker ud eller downloader flere valgte e-mails som ét .zip-arkiv. Endelsen .zip tilføjes automatisk." - }, - "spaces": { - "label": "Mellemrum", - "description": "Erstat mellemrum i det endelige filnavn med et andet tegn.", - "keep": "Behold mellemrum", - "underscore": "Erstat med _", - "dash": "Erstat med -" - }, - "lowercase": { - "label": "Små bogstaver", - "description": "Tving hele filnavnet til små bogstaver." - }, - "strip_diacritics": { - "label": "Fjern diakritiske tegn", - "description": "Konvertér bogstaver med accenter til deres ASCII-pendant (ä → a, é → e). Nyttigt for værktøjer, der ikke håndterer Unicode-navne korrekt." - }, - "collapse_separators": { - "label": "Slå gentagne skilletegn sammen", - "description": "Slå serier af mellemrum, understreger eller bindestreger sammen til ét tegn." - }, - "after_export": { - "label": "Efter eksport", - "description": "Flyt eventuelt e-mailen efter den er eksporteret som .eml.", - "keep": "Behold i postkassen", - "archive": "Flyt til arkiv", - "trash": "Flyt til papirkurv" - } - }, - "filters": { - "title": "E-mail-filtre", - "description": "Opret regler til automatisk at sortere, mærke og administrere indkommende e-mails", - "add_rule": "Tilføj regel", - "no_rules": "Ingen filterregler", - "no_rules_description": "Opret regler for automatisk at organisere dine indkommende e-mails", - "vacation_active": "Fraværsbeskeder er aktive", - "vacation_active_description": "Auto-svar er aktiveret for indkommende beskeder", - "vacation_configure": "Konfigurér", - "edit_rule": "Redigér regel", - "new_rule": "Ny regel", - "delete_rule": "Slet regel", - "delete_confirm": "Er du sikker på, at du vil slette denne regel?", - "enable": "Aktivér", - "disable": "Deaktivér", - "raw_editor": "Raw Sieve-editor", - "raw_editor_warning": "Redigering af det rå Sieve-script kan ødelægge visuel regelledigering. Ændringer foretaget her tilsidesætter den visuelle builder.", - "validate": "Valider", - "validation_success": "Scriptet er gyldigt", - "validation_error": "Scriptet har fejl", - "save": "Gem regler", - "saving": "Gemmer...", - "saved": "Filtre gemt succesfuldt", - "save_failed": "Kunne ikke gemme filtre", - "loading": "Indlæser filtre...", - "not_supported": "Din mailserver understøtter ikke e-mail-filtre.", - "rule_name": "Regelnavn", - "rule_name_placeholder": "f.eks. Sorter nyhedsbreve", - "match_all": "Match ALLE betingelser", - "match_any": "Match en af betingelserne", - "conditions": "Betingelser", - "add_condition": "Tilføj betingelse", - "actions": "Handlinger", - "add_action": "Tilføj handling", - "stop_processing": "Stop behandling af efterfølgende regler", - "include_spam": "Flyt også beskeder, der er markeret som spam", - "attachment_type_placeholder": "f.eks. pdf, doc, jpg", - "value_placeholder_multi": "Værdi (flere adskilt med komma)", - "condition_fields": { - "attachment": "Vedhæftet fil", - "from": "Fra", - "to": "Til", - "cc": "Cc", - "subject": "Emne", - "header": "Brugerdefineret header", - "size": "Størrelse", - "body": "Brødtekst" - }, - "comparators": { - "has_any": "er til stede", - "has_type": "af typen", - "contains": "indeholder", - "not_contains": "indeholder ikke", - "is": "er præcis", - "not_is": "er ikke", - "starts_with": "begynder med", - "ends_with": "slutter med", - "matches": "matcher mønster", - "greater_than": "er større end", - "less_than": "er mindre end" - }, - "action_types": { - "move": "Flyt til mappe", - "copy": "Kopier til mappe", - "forward": "Videresend til", - "mark_read": "Markér som læst", - "star": "Stjernemarkér besked", - "add_label": "Tilføj tag", - "discard": "Kassér (slet stille)", - "reject": "Afvis med besked", - "keep": "Behold i indbakke", - "stop": "Stop behandling" - }, - "move_to_folder": "Vælg mappe", - "copy_to_folder": "Vælg mappe", - "forward_to": "Videresend til e-mailadresse", - "forward_placeholder": "email@eksempel.dk", - "forward_keep_copy": "Behold en kopi", - "forward_limit": "Grænse for videresendelser pr. besked på denne server: {count}. Yderligere videresendelser springes over.", - "reject_message": "Afvisningsbesked", - "reject_placeholder": "Din e-mail er blevet afvist", - "label_name": "Tag-navn", - "label_placeholder": "Vælg tag", - "header_name": "Headernavn", - "header_placeholder": "f.eks. X-Mailing-List", - "size_bytes": "Størrelse i bytes", - "size_placeholder": "f.eks. 1000000", - "system_managed": "Systemadministreret regel", - "opaque_warning": "Dette script blev redigeret uden for den visuelle builder. Kun raw Sieve-redigering er tilgængelig.", - "open_sieve_editor": "Åbn raw Sieve-editor", - "fetch_error": "Kunne ikke indlæse filtre", - "expanded_view": "Udvidet visning", - "expanded_view_description": "Vis filterregler med detaljerede betingelses- og handlingsblokke", - "if": "Hvis", - "then": "Så", - "match_all_conditions": "alle matcher", - "match_any_condition": "enhver matcher", - "and": "og", - "or": "eller", - "cancel": "Annuller", - "confirm_delete": "Slet", - "rule_list": "Filterregler", - "drag_to_reorder": "Træk for at omorganisere", - "match_type": "Match-type", - "reset_to_visual": "Nulstil til visuel builder", - "reset_warning": "Dette vil kassere det nuværende script og starte forfra.", - "confirm_reset": "Nulstil", - "validation_empty_name": "Regelnavn er påkrævet", - "validation_empty_conditions": "Mindst én betingelse med en værdi er påkrævet", - "validation_empty_actions": "Mindst én handling er påkrævet", - "templates_section": "Start fra skabelon", - "template_newsletters": "Flyt nyhedsbreve til mappe", - "template_receipts": "Auto-arkiver kvitteringer", - "template_important": "Markér vigtige e-mails", - "template_notifications": "Filtrér notifikationer", - "sieve_editor": { - "title": "Sieve-script-editor", - "warning": "Redigering af det rå Sieve-script kan ødelægge visuel regelledigering. Ændringer foretaget her tilsidesætter den visuelle builder.", - "script_content": "Sieve-script", - "valid": "Scriptet er gyldigt", - "invalid": "Scriptet har fejl", - "save_warning": "Lagring vil overskrive eventuelle visuelle regler. Dette kan ikke fortrydes. Klik Gem igen for at bekræfte.", - "validating": "Validerer...", - "validate": "Valider", - "cancel": "Annuller", - "save": "Gem", - "confirm_save": "Bekræft gem", - "validation_failed": "Valideringsanmodning mislykkedes" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# betingelse} other {# betingelser}}", - "actions_count": "{count, plural, one {# handling} other {# handlinger}}" - }, - "origin_external": "Ekstern", - "managed_by_tooltip": "Administreres af {source}. Redigér det i den pågældende app, eller brug raw Sieve-editoren." - }, - "templates": { - "title": "E-mail-skabeloner", - "description": "Opret genanvendelige e-mail-skabeloner med pladsholdervariabler", - "add": "Ny skabelon", - "edit": "Redigér skabelon", - "name": "Skabelonnavn", - "name_placeholder": "f.eks. Opfølgning", - "category": "Kategori", - "category_placeholder": "f.eks. Arbejde, Privat", - "subject": "Emne", - "subject_placeholder": "E-mail-emnelinje", - "body": "Brødtekst", - "body_placeholder": "E-mail-tekst...", - "recipients_placeholder": "email@eksempel.dk", - "identity": "Send som", - "default_identity": "Standardidentitet", - "favorite": "Favorit", - "cancel": "Annuller", - "create": "Opret", - "update": "Opdater", - "confirm_delete": "Slet", - "no_templates": "Ingen skabeloner endnu", - "manage": "Administrér skabeloner", - "count": "{count, plural, one {# skabelon} other {# skabeloner}}", - "export_import": "Eksportér & importér", - "export_import_description": "Sikkerhedskopér dine skabeloner eller overfør dem til en anden enhed", - "export": "Eksportér", - "import": "Importér", - "validation": { - "empty": "Skabelonnavn er påkrævet", - "too_long": "Skabelonnavn skal være 200 tegn eller mindre" - } - }, - "files": { - "display": { - "title": "Visning", - "description": "Konfigurér hvordan filer og mapper vises" - }, - "folder_layout": { - "label": "Mappenavigation", - "description": "Vælg hvordan mapper vises: integreret med filer eller i et sidepanel-træ", - "inline": "Integreret", - "sidebar": "Sidepanel" - }, - "default_view": { - "label": "Standardvisning", - "description": "Vælg mellem gitter- og listelayout", - "list": "Liste", - "grid": "Gitter" - }, - "default_sort": { - "label": "Standardsortering", - "description": "Vælg standardsortering for filer", - "name": "Navn", - "size": "Størrelse", - "modified": "Ændret" - }, - "sort_direction": { - "label": "Sorteringsretning", - "description": "Vælg stigende eller faldende rækkefølge", - "ascending": "Stigende", - "descending": "Faldende" - }, - "icons": { - "title": "Ikoner", - "description": "Konfigurér filikonernes udseende" - }, - "show_icons": { - "label": "Vis filikoner", - "description": "Vis ikoner ved siden af filer og mapper" - }, - "colored_icons": { - "label": "Farvede ikoner", - "description": "Brug farverige ikoner i stedet for monokrome" - }, - "show_thumbnails": { - "label": "Vis miniaturer", - "description": "Vis billedforhåndsvisninger i stedet for ikoner for billedfiler" - }, - "behavior": { - "title": "Adfærd", - "description": "Konfigurér filbrowserens adfærd" - }, - "show_hidden": { - "label": "Vis skjulte filer", - "description": "Vis filer og mapper der begynder med et punktum" - }, - "preview": { - "label": "Forhåndsvisning" - } - }, - "scoped": { - "back": "Tilbage til min konto", - "managing": "Administrerer: {name}" - } - }, - "errors": { - "page_error_title": "Noget gik galt", - "page_error_description": "Vi stødte på en uventet fejl. Prøv igen eller gå tilbage til startsiden.", - "sidebar_error": "Kunne ikke indlæse postkasser", - "email_list_error": "Kunne ikke indlæse e-mails", - "viewer_error_title": "Kan ikke vise e-mail", - "viewer_error_description": "Der var et problem med at vise denne e-mail. Den kan indeholde indhold, der ikke understøttes.", - "composer_error": "Kunne ikke indlæse komponist", - "settings_error_title": "Indstillinger utilgængelige", - "settings_error_description": "Kunne ikke indlæse indstillinger. Dine præferencer bliver muligvis ikke gemt.", - "try_again": "Prøv igen", - "reload": "Genindlæs", - "reload_emails": "Genindlæs e-mails", - "reload_settings": "Genindlæs indstillinger", - "retry": "Prøv igen", - "go_home": "Gå til indbakke" - }, - "context_menu": { - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "mark_read": "Markér som læst", - "mark_unread": "Markér som ulæst", - "star": "Stjernemarkér", - "unstar": "Fjern stjerne", - "pin": "Fastgør", - "unpin": "Frigør", - "move_to": "Flyt til...", - "archive": "Arkivér", - "delete": "Slet", - "mark_as_spam": "Rapportér spam", - "not_spam": "Ikke spam", - "tag": "Tag", - "items_selected": "{count} e-mails valgt", - "edit_draft": "Redigér kladde", - "cancel_scheduled_send": "Annuller afsendelse", - "reschedule_send": "Planlæg om", - "cancel_and_edit": "Annuller og redigér", - "cancel_and_compose_again": "Annuller og skriv igen" - }, - "mailbox_context_menu": { - "mark_folder_read": "Markér mappe som læst", - "mark_folder_tree_read": "Markér mappe & undermapper som læst", - "mark_all_folders_read": "Markér alle mapper som læst", - "new_subfolder": "Ny undermappe...", - "new_folder": "Ny mappe...", - "rename": "Omdøb...", - "share": "Del...", - "import_email": "Importér .eml eller .zip...", - "empty_folder": "Tøm mappe", - "empty_folder_generic": "Tøm mappe", - "delete_folder": "Slet mappe", - "refresh": "Opdatér", - "mark_all_confirm_title": "Markér alle mapper som læst", - "mark_all_confirm_message": "Markér alle ulæste beskeder på din personlige konto som læst?", - "delete_confirm_title": "Slet mappe", - "delete_confirm_message": "Slet mappen \"{name}\" permanent? Denne handling kan ikke fortrydes.", - "prompt_new_subfolder": "Indtast et navn til den nye undermappe.", - "prompt_new_folder": "Indtast et navn til den nye mappe.", - "prompt_rename": "Indtast et nyt navn til denne mappe.", - "placeholder_folder_name": "Mappenavn", - "create": "Opret", - "rename_confirm": "Omdøb", - "toast_marked_read": "Mappe markeret som læst", - "toast_marked_read_count": "Markerede {count, plural, one {1 besked} other {# beskeder}} som læst", - "toast_already_read": "Ingen ulæste beskeder", - "toast_marked_all_read": "Alle mapper markeret som læst", - "toast_emptied": "Mappe tømt", - "toast_folder_created": "Mappe oprettet", - "toast_folder_renamed": "Mappe omdøbt", - "toast_folder_deleted": "Mappe slettet", - "toast_error_mark_read": "Kunne ikke markere som læst", - "toast_error_empty": "Kunne ikke tømme mappe", - "toast_error_create": "Kunne ikke oprette mappe", - "toast_error_rename": "Kunne ikke omdøbe mappe", - "toast_error_delete": "Kunne ikke slette mappe", - "toast_error_delete_has_children": "Mappen har undermapper. Fjern dem først.", - "toast_error_delete_has_email": "Mappen er ikke tom. Tøm den først.", - "delete_with_contents_title": "Mappen er ikke tom", - "delete_with_contents_message": "Mappen \"{name}\" indeholder stadig {count, plural, one {1 besked} other {# beskeder}}. Slet mappen og alle dens beskeder? Dette kan ikke fortrydes.", - "delete_with_contents_confirm": "Slet mappe og beskeder" - }, - "shortcuts": { - "title": "Tastaturgenveje", - "tip": "Tryk ? når som helst for at se denne hjælp", - "sections": { - "navigation": "Navigation", - "actions": "E-mail-handlinger", - "global": "Globalt", - "threads": "Tråde", - "composer": "Komponist" - }, - "navigation": { - "next_email": "Næste e-mail", - "previous_email": "Forrige e-mail", - "open_email": "Åbn e-mail", - "close_email": "Luk / Fravaælg" - }, - "actions": { - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "star": "Slå stjerne til/fra", - "archive": "Arkivér", - "delete": "Slet", - "mark_unread": "Markér som ulæst", - "mark_read": "Markér som læst", - "toggle_spam": "Rapportér spam / Ikke spam" - }, - "global": { - "compose": "Ny e-mail", - "search": "Fokusér søgning", - "help": "Vis genveje", - "refresh": "Opdatér e-mails", - "select_all": "Vælg alle", - "global_search": "Søg i alt" - }, - "threads": { - "expand_collapse": "Udvid/skjul tråd" - }, - "composer": { - "send": "Send e-mail", - "schedule_send": "Planlæg afsendelse", - "template_picker": "Åbn skabelonvælger" - } - }, - "threads": { - "messages_one": "{count} besked", - "messages_other": "{count} beskeder", - "messages_tooltip": "{count, plural, one {# besked i denne samtale} other {# beskeder i denne samtale}}", - "expand": "Udvid samtale", - "collapse": "Skjul samtale", - "loading": "Indlæser samtale...", - "mark_read": "Markér samtale som læst", - "mark_unread": "Markér samtale som ulæst", - "archive": "Arkivér samtale", - "delete": "Slet samtale", - "star": "Stjernemarkér samtale", - "unstar": "Fjern stjernemarkering", - "toggle_thread": "Slå tråd til/fra" - }, - "identities": { - "modal_title": "Administrér afsenderidentiteter", - "create_new": "Opret ny identitet", - "edit_identity": "Redigér identitet", - "delete_confirm": "Slet denne identitet? Dette kan ikke fortrydes.", - "cannot_delete": "Denne identitet kan ikke slettes", - "primary_identity": "Primær", - "set_as_primary": "Angiv som primær", - "no_identities": "Ingen identiteter fundet", - "display": { - "reply_to": "Svar-til:", - "bcc": "BCC:", - "signature": "Signatur:", - "preview": "Forhåndsvisning:" - }, - "validation_errors": { - "invalid_emails": "Ugyldige e-mails: {emails}", - "unknown_error": "Ukendt fejl" - }, - "form": { - "name_label": "Visningsnavn", - "name_placeholder": "f.eks. Arbejdsmail, Privat", - "name_required": "Navn er påkrævet", - "email_label": "E-mailadresse", - "email_placeholder": "din.email@eksempel.dk", - "email_required": "E-mail er påkrævet", - "email_invalid": "Indtast en gyldig e-mailadresse", - "email_immutable": "E-mailadresse kan ikke ændres efter oprettelse", - "reply_to_label": "Svar-til (valgfrit)", - "reply_to_placeholder": "anden@email.dk", - "bcc_label": "Auto BCC (valgfrit)", - "bcc_placeholder": "arkiv@email.dk", - "text_signature_label": "Tekstsignatur", - "html_signature_label": "HTML-signatur", - "signature_byte_counter": "{bytes} / {max} bytes", - "signature_byte_limit_reached": "Servergrænse nået", - "save": "Gem identitet", - "cancel": "Annuller", - "creating": "Opretter...", - "updating": "Opdaterer..." - }, - "sub_address": { - "button_tooltip": "Brug underadresse", - "popover_title": "Tilføj underadresse-tag", - "tag_input_placeholder": "Indtast tag (f.eks. shopping)", - "preview_label": "Forhåndsvisning:", - "recent_tags": "Seneste tags", - "suggested_tags": "Foreslået", - "use_address": "Brug denne adresse", - "invalid_tag": "Tag må kun indeholde bogstaver, tal og bindestreger", - "tag_too_long": "Tag skal være 30 tegn eller mindre", - "help_text": "E-mails sendt til bruger{delimiter}tag@domæne.dk ankommer i din indbakke", - "validation": { - "empty": "Tag kan ikke være tomt", - "too_long": "Tag skal være {max} tegn eller mindre", - "invalid_chars": "Tag må kun indeholde bogstaver, tal og bindestreger" - } - }, - "badge": { - "sent_via": "via", - "sub_address_tag": "Sendt med underadresse: {tag}", - "identity_name": "Sendt med identitet: {name}", - "identity_short": "via {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Slet", - "delete_confirm_title": "Slet identitet" - }, - "templates": { - "picker_title": "Vælg en skabelon", - "search_placeholder": "Søg efter skabeloner...", - "section_favorites": "Favoritter", - "section_recent": "Seneste", - "section_uncategorized": "Andet", - "no_templates": "Ingen skabeloner endnu", - "no_results": "Ingen skabeloner fundet", - "fill_placeholders": "Udfyld pladsholderværdier", - "enter_value": "Indtast en værdi...", - "preview": "Forhåndsvisning", - "insert_with_values": "Indsæt med værdier", - "insert_raw": "Indsæt rå", - "copy_suffix": "(kopi)", - "placeholder": "Variabel", - "placeholders": { - "recipient_name": "Modtagers navn", - "company": "Virksomhedsnavn", - "date": "Dagens dato", - "day_of_week": "Ugedag", - "sender_name": "Dit navn" - } - }, - "contacts": { - "title": "Kontakter", - "search_placeholder": "Søg efter kontakter...", - "create_new": "Ny kontakt", - "no_category": "Ingen kategori", - "rename_category": "Omdøb kategori", - "category_name_label": "Kategorinavn", - "category_renamed": "Kategori omdøbt", - "category_rename_failed": "Kunne ikke omdøbe kategori", - "category_added": "Kontakt tilføjet til {name}", - "category_added_plural": "{count} kontakter tilføjet til {name}", - "empty_state": "Ingen kontakter endnu", - "empty_state_title": "Ingen kontakter endnu", - "empty_state_subtitle": "Opret din første kontakt eller importér fra en vCard-fil", - "empty_search": "Ingen kontakter matcher din søgning", - "empty_search_hint": "Prøv en anden søgeterm", - "empty_filtered": "Ingen kontakter matcher dine filtre", - "empty_filtered_hint": "Prøv at justere eller rydde filtre", - "clear_search": "Ryd søgning", - "import_vcard": "Importér vCard", - "delete_confirm_title": "Slet kontakt", - "delete_confirm": "Er du sikker på, at du vil slette denne kontakt?", - "local_mode": "Kontakter gemmes lokalt (serveren understøtter ikke JMAP-kontakter)", - "back_to_contacts": "Tilbage til kontakter", - "back_to_email": "Tilbage til e-mail", - "tabs": { - "all": "Alle", - "groups": "Grupper" - }, - "shared": { - "title": "Delt" - }, - "address_books": { - "create": "Ny adressebog", - "created": "Adressebog oprettet", - "create_failed": "Kunne ikke oprette adressebog", - "title": "Mine adressebøger", - "shared_prefix": "Delt: {name}", - "moved": "Kontakt flyttet til {name}", - "moved_plural": "{count} kontakter flyttet til {name}", - "move_failed": "Kunne ikke flytte kontakt", - "address_book": "Adressebog", - "rename": "Omdøb adressebog", - "name_label": "Adressebogsnavn", - "renamed": "Adressebog omdøbt", - "rename_failed": "Kunne ikke omdøbe adressebog", - "default": "Standard", - "set_default": "Angiv som standard", - "default_updated": "Standardadressebog opdateret", - "set_default_failed": "Kunne ikke angive standardadressebog", - "manage": "Administrér adressebøger", - "share": "Del adressebog", - "new_contact_in_book": "Ny kontakt i denne adressebog", - "delete": "Slet adressebog", - "confirm_delete": "Slet \"{name}\"? Alle kontakter i denne adressebog fjernes.", - "deleted": "Adressebog slettet", - "delete_failed": "Kunne ikke slette adressebog" - }, - "detail": { - "emails": "E-mailadresser", - "phones": "Telefonnumre", - "organizations": "Organisationer", - "addresses": "Adresser", - "notes": "Noter", - "titles": "Titler & roller", - "online_services": "Online tjenester", - "anniversaries": "Mærkedage", - "personal_info": "Personlige oplysninger", - "languages": "Sprog", - "categories": "Kategorier", - "related_contacts": "Relaterede kontakter", - "crypto_keys": "Krypto-nøgler", - "cert_issuer": "Udsteder", - "cert_expires": "Udløber", - "cert_expired": "Udløbet", - "cert_fingerprint": "Fingeraftryk", - "cert_algorithm": "Algoritme", - "import_to_smime": "Importér til S/MIME", - "cert_already_imported": "Allerede importeret til S/MIME", - "cert_imported": "Certifikat importeret til S/MIME-lager", - "cert_import_failed": "Kunne ikke importere certifikat", - "no_contact_selected": "Vælg en kontakt for at se detaljer", - "compose_email": "Skriv e-mail", - "copy_email": "Kopier e-mail", - "copy_phone": "Kopier telefonnummer", - "copy_url": "Kopier URL", - "copied": "Kopieret til udklipsholder", - "copy_failed": "Kunne ikke kopiere til udklipsholder", - "created": "Oprettet", - "updated": "Sidst opdateret", - "timezone": "Tidszone", - "anniversary_birth": "Fødselsdag", - "anniversary_death": "Dødsdag", - "anniversary_wedding": "Bryllupsdag", - "anniversary_other": "Andet", - "personal_expertise": "Ekspertise", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Andet", - "gender": "Køn", - "gender_masculine": "Mand", - "gender_feminine": "Kvinde", - "gender_other": "Andet", - "gender_none": "Ikke relevant", - "gender_unknown": "Ukendt", - "calendar": "Kalender", - "calendar_uri": "Kalender-URL", - "scheduling_uri": "Planlægnings-URL", - "freebusy_uri": "Ledig/Opptagen-URL", - "section_contact": "Kontaktoplysninger", - "section_work": "Arbejde", - "section_personal": "Privat", - "email_default_label": "E-mail", - "phone_default_label": "Telefon", - "address_default_label": "Adresse", - "online_service_default_label": "Online", - "organization_label": "Organisation", - "title_label": "Titel", - "role_label": "Rolle", - "language_label": "Sprog", - "related_default_label": "Relateret", - "more_actions": "Flere handlinger", - "age_years": "{count, plural, one {1 år gammel} other {# år gammel}}", - "years_since": "{count, plural, one {1 år} other {# år}}" - }, - "activity": { - "recent_emails": "Seneste e-mails", - "upcoming_events": "Kommende begivenheder", - "no_emails": "Ingen seneste e-mails", - "no_events": "Ingen kommende begivenheder", - "no_subject": "(Intet emne)", - "no_title": "(Ingen titel)", - "load_failed": "Kunne ikke indlæse", - "unknown_sender": "Ukendt afsender", - "all_day": "Hele dagen" - }, - "form": { - "create_title": "Ny kontakt", - "edit_title": "Redigér kontakt", - "section_address_book": "Adressebog", - "select_address_book": "Vælg en adressebog...", - "section_identity": "Navn & identitet", - "contact_type": "Kontakttype", - "type_person": "Person", - "type_organization": "Organisation", - "section_work": "Arbejde & organisation", - "prefix": "Præfiks", - "prefix_placeholder": "Dr., hr., fru", - "given_name": "Fornavn", - "middle_name": "Mellemnavn", - "surname": "Efternavn", - "suffix": "Suffiks", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Kaldenavn", - "nickname_placeholder": "Kaldenavn", - "email": "E-mail", - "email_placeholder": "email@eksempel.dk", - "phone": "Telefon", - "phone_placeholder": "+45 12 34 56 78", - "phone_type": "Type", - "phone_voice": "Telefon", - "phone_cell": "Mobil", - "phone_fax": "Fax", - "phone_pager": "Personsøger", - "phone_video": "Video", - "phone_text": "Tekst", - "organization": "Organisation", - "organization_placeholder": "Virksomhedsnavn", - "department": "Afdeling", - "department_placeholder": "Afdeling", - "job_title": "Jobtitel", - "job_title_placeholder": "f.eks. Softwareudvikler", - "role": "Rolle", - "role_placeholder": "f.eks. Teamleder", - "addresses": "Adresser", - "add_address": "Tilføj adresse", - "street": "Gade", - "city": "By", - "region": "Region", - "postcode": "Postnummer", - "country": "Land", - "online_services": "Online tjenester", - "add_online_service": "Tilføj online tjeneste", - "url_placeholder": "https://...", - "service_placeholder": "Tjeneste", - "anniversaries": "Mærkedage", - "add_anniversary": "Tilføj dato", - "anniversary_birth": "Fødselsdag", - "anniversary_wedding": "Bryllupsdag", - "anniversary_death": "Mindesmærke", - "anniversary_other": "Andet", - "personal_info": "Personlige oplysninger", - "add_personal_info": "Tilføj indførsel", - "personal_info_placeholder": "f.eks. Fotografi", - "personal_expertise": "Ekspertise", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Andet", - "level": "Niveau", - "level_high": "Høj", - "level_medium": "Mellem", - "level_low": "Lav", - "categories": "Kategorier", - "categories_placeholder": "f.eks. Familie, Venner, Kolleger", - "categories_hint": "Skriv for at søge eller tilføje kategorier", - "category_add": "Tilføj", - "note": "Noter", - "note_placeholder": "Tilføj en note...", - "gender": "Køn", - "gender_sex": "Biologisk køn", - "gender_male": "Mand", - "gender_female": "Kvinde", - "gender_other": "Andet", - "gender_none": "Ikke relevant", - "gender_unknown": "Ukendt", - "gender_identity": "Kønsidentitet", - "gender_identity_placeholder": "Kønsidentitet...", - "calendar": "Kalender", - "calendar_uri": "Kalender-URL", - "scheduling_uri": "Planlægnings-URL", - "freebusy_uri": "Ledig/Opptagen-URL", - "context_work": "Arbejde", - "context_private": "Privat", - "add_email": "Tilføj e-mail", - "add_phone": "Tilføj telefon", - "save": "Gem", - "cancel": "Annuller", - "creating": "Opretter...", - "updating": "Opdaterer...", - "name_required": "Angiv et fornavn, efternavn eller en organisation", - "email_invalid": "Indtast en gyldig e-mailadresse", - "email_error_inline": "Ugyldigt e-mailformat", - "save_failed": "Kunne ikke gemme kontakt", - "delete": "Slet", - "upload_photo": "Upload billede", - "remove_photo": "Fjern billede", - "photo_hint": "JPG eller PNG, op til 10 MB. Vil blive ændret i størrelse.", - "photo_too_large": "Billedet er for stort (max 10 MB)", - "photo_invalid": "Ugyldig billedfil", - "change_photo": "Skift" - }, - "groups": { - "create": "Ny gruppe", - "edit": "Redigér gruppe", - "empty": "Ingen grupper endnu", - "delete_confirm_title": "Slet gruppe", - "delete_confirm": "Er du sikker på, at du vil slette denne gruppe?", - "name_label": "Gruppenavn", - "name_placeholder": "f.eks. Team, Familie", - "name_required": "Gruppenavn er påkrævet", - "save_failed": "Kunne ikke gemme gruppe", - "members_label": "Medlemmer", - "search_members": "Søg efter kontakter at tilføje...", - "no_members": "Ingen medlemmer i denne gruppe", - "member_count": "{count, plural, =0 {Ingen medlemmer} one {1 medlem} other {# medlemmer}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Importér kontakter", - "drop_hint": "Klik for at vælge en vCard-fil", - "file_types": ".vcf eller .vcard filer", - "no_contacts": "Ingen kontakter fundet i filen", - "parse_error": "Kunne ikke parse vCard-fil", - "found": "{count, plural, one {1 kontakt fundet} other {# kontakter fundet}}", - "duplicate": "Dublet", - "select_all": "Vælg alle", - "deselect_all": "Fravælg alle", - "selected": "{count, plural, one {1 valgt} other {# valgte}}", - "import_button": "Importér", - "importing": "Importerer...", - "success": "{count, plural, one {1 kontakt importeret} other {# kontakter importeret}}", - "failed": "Import mislykkedes", - "close": "Luk", - "file_too_large": "Filen er for stor (max 5 MB)" - }, - "export": { - "title": "Eksportér kontakter", - "success": "{count, plural, one {1 kontakt eksporteret} other {# kontakter eksporteret}}" - }, - "bulk": { - "selected": "{count, plural, one {1 valgt} other {# valgte}}", - "select_all": "Vælg alle", - "delete": "Slet", - "delete_confirm_title": "Slet kontakter", - "delete_confirm": "Slet {count, plural, one {1 kontakt} other {# kontakter}}?", - "deleted": "{count, plural, one {1 kontakt slettet} other {# kontakter slettet}}", - "add_to_group": "Tilføj til gruppe", - "choose_group": "Vælg en gruppe", - "adding_contacts": "Tilføjer {count, plural, one {1 kontakt} other {# kontakter}}", - "added_to_group": "Kontakter tilføjet til gruppe", - "export": "Eksportér", - "clear": "Ryd markering" - }, - "toast": { - "created": "Kontakt oprettet", - "updated": "Kontakt opdateret", - "deleted": "Kontakt slettet", - "error_create": "Kunne ikke oprette kontakt", - "error_update": "Kunne ikke opdatere kontakt", - "error_delete": "Kunne ikke slette kontakt" - }, - "context_menu": { - "open": "Åbn", - "edit": "Redigér", - "send_email": "Send e-mail", - "add_to_group": "Tilføj til gruppe", - "export_vcard": "Eksportér som vCard", - "delete": "Slet", - "call": "Ring", - "duplicate": "Duplikér", - "print": "Udskriv" - }, - "filters": { - "toggle": "Filtre", - "select": "Vælg", - "clear": "Ryd", - "close": "Luk", - "title": "Avancerede filtre", - "organization": "Virksomhed", - "organization_placeholder": "f.eks. Acme Corp", - "job_title": "Jobtitel", - "job_title_placeholder": "f.eks. Designer", - "location": "Sted", - "location_placeholder": "By eller land", - "email_domain": "E-maildomæne", - "email_domain_placeholder": "eksempel.dk", - "birthday_month": "Fødselsdag i", - "any_month": "Enhver måned", - "has_email": "Har e-mail", - "has_phone": "Har telefon", - "has_photo": "Har billede", - "sort_by": "Sortér efter", - "sort_first_name": "Fornavn", - "sort_last_name": "Efternavn" - }, - "open_categories": "Åbn kategorier" - }, - "calendar": { - "title": "Kalender", - "back_to_email": "Tilbage til e-mail", - "back_to_month": "Tilbage til måned", - "my_calendars": "Kalendere", - "birthday_calendar": "Fødselsdage", - "mini_calendar_change": "Klik for at skifte måned", - "views": { - "month": "Måned", - "week": "Uge", - "day": "Dag", - "agenda": "Agenda", - "today": "I dag", - "month_hint": "Måned (m)", - "week_hint": "Uge (w)", - "day_hint": "Dag (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Opgaver", - "tasks_hint": "Opgaver (k)" - }, - "events": { - "create": "Opret begivenhed", - "edit": "Redigér begivenhed", - "delete": "Slet begivenhed", - "details": "Begivenhedsdetaljer", - "no_events": "Ingen begivenheder", - "all_day": "Hele dagen", - "more": "+{count} mere", - "no_title": "(Ingen titel)", - "resize": "Tilpas størrelse på begivenhed", - "duplicate": "Duplikér", - "today_header": "I dag", - "tomorrow_header": "I morgen", - "agenda_show_earlier": "Vis tidligere begivenheder", - "agenda_loading": "Indlæser…", - "agenda_range_start": "Begivenheder fra {date}", - "agenda_range_end": "Begivenheder til {date}", - "export_ics": "Eksportér som .ics", - "copy_title": "Kopier titel", - "copy_link": "Kopier mødelink", - "new_event": "Ny begivenhed", - "new_all_day_event": "Ny heldagsbegivenhed", - "new_task": "Ny opgave", - "go_to_today": "Gå til i dag" - }, - "detail": { - "add_note": "Tilføj en note...", - "save_note": "Gem", - "note_saved": "Note tilføjet", - "open_link": "Åbn link", - "meeting_link": "Mødelink", - "tentative": "Foreløbig", - "cancelled": "Aflyst", - "delete_confirm": "Slet denne begivenhed?" - }, - "form": { - "title": "Titel", - "description": "Beskrivelse", - "location": "Sted", - "meeting_link": "Mødelink", - "start_date": "Startdato", - "end_date": "Slutdato", - "start_time": "Starttidspunkt", - "end_time": "Sluttidspunkt", - "all_day_event": "Heldagsbegivenhed", - "calendar_select": "Kalender", - "save": "Gem", - "cancel": "Annuller", - "delete_confirm": "Er du sikker på, at du vil slette denne begivenhed?", - "color": "Farve" - }, - "participants": { - "availability": { - "title": "Tilgængelighed", - "free": "Ledig", - "busy": "Optaget", - "tentative": "Foreløbig", - "unknown": "Ikke en bruger på denne server", - "checking": "Kontrollerer…" - }, - "title": "Deltagere", - "add": "Tilføj deltager", - "organizer": "Arrangør", - "attendee": "Deltager", - "accepted": "Accepteret", - "declined": "Afslået", - "tentative": "Foreløbig", - "needs_action": "Kræver handling", - "remove": "Fjern", - "edit": "Redigér", - "email_placeholder": "Tilføj e-mailadresse eller søg efter kontakter", - "send_invitations": "Send invitationer til deltagere", - "status_summary": "{accepted} accepteret, {pending} afventer", - "invited_by": "Invitation fra {name}", - "respond_below": "Svar ved hjælp af knapperne nedenfor", - "rsvp_label": "Dit svar", - "cancel_notification": "Deltagere vil blive underrettet om aflysningen", - "you_organizer": "Du er arrangøren", - "you_attendee": "Du er deltager", - "no_participants": "Ingen deltagere", - "count": "{count, plural, one {# deltager} other {# deltagere}}" - }, - "recurrence": { - "title": "Gentagelse", - "none": "Gentages ikke", - "daily": "Dagligt", - "weekly": "Ugentligt", - "monthly": "Månedligt", - "yearly": "Årligt", - "every_n_days": "Hver {count} dag", - "every_n_weeks": "Hver {count} uge", - "every_n_months": "Hver {count} måned", - "until": "Indtil", - "occurrences": "{count} forekomster", - "custom": "Tilpasset…", - "edit_custom": "Rediger tilpasset gentagelse", - "every_n_years": "Hvert {count}. år", - "on_days": "på {days}", - "on_day_n": "på dag {day}", - "on_the_nth": "den {nth} {day}", - "in_month": "i {month}", - "nth_1": "første", - "nth_2": "anden", - "nth_3": "tredje", - "nth_4": "fjerde", - "nth_last": "sidste", - "editor_freq_day": "Dag", - "editor_freq_week": "Uge", - "editor_freq_month": "Måned", - "editor_freq_year": "År", - "editor_repeats_on": "Gentages", - "editor_every": "Hver", - "editor_unit_days": "dag(e)", - "editor_unit_weeks": "uge(r)", - "editor_unit_months": "måned(er)", - "editor_unit_years": "år", - "editor_on_day": "på dag", - "editor_on_the": "den", - "editor_in": "i", - "editor_ends": "Slutter", - "editor_never": "Aldrig", - "editor_ends_on": "Den", - "editor_ends_after": "Efter", - "editor_occurrences": "gentagelser" - }, - "recurrence_scope": { - "edit_title": "Redigér tilbagevendende begivenhed", - "delete_title": "Slet tilbagevendende begivenhed", - "description": "Dette er en tilbagevendende begivenhed. Hvilke begivenheder vil du ændre?", - "this_event": "Kun denne begivenhed", - "this_and_future": "Denne og fremtidige begivenheder", - "all_events": "Alle begivenheder", - "cancel": "Annuller", - "save": "Gem", - "delete": "Slet", - "rsvp_title": "Svar på tilbagevendende begivenhed", - "rsvp_description": "Dette er en tilbagevendende begivenhed. Hvilke begivenheder gælder dit svar for?", - "respond": "Svar" - }, - "alerts": { - "title": "Påmindelse", - "none": "Ingen påmindelse", - "at_time": "På begivenhedstidspunktet", - "minutes_before": "{count, plural, one {# minut før} other {# minutter før}}", - "hours_before": "{count, plural, one {# time før} other {# timer før}}", - "days_before": "{count, plural, one {# dag før} other {# dage før}}", - "weeks_before": "{count, plural, one {# uge før} other {# uger før}}", - "unit_minutes_before": "minutter før", - "unit_hours_before": "timer før", - "unit_days_before": "dage før", - "unit_weeks_before": "uger før", - "add": "Tilføj påmindelse", - "remove": "Fjern påmindelse", - "amount": "Påmindelsesmængde", - "unit": "Påmindelsesenhed" - }, - "settings": { - "title": "Kalenderindstillinger", - "default_view": "Standardvisning", - "week_starts_on": "Ugen starter på", - "time_format": "Tidsformat", - "default_calendar": "Standardkalender", - "default_reminder": "Standardpåmindelse", - "time_format_12h": "12-timers", - "time_format_24h": "24-timers", - "notifications_enabled": "Begivenhedsnotifikationer", - "notifications_enabled_desc": "Vis alarmer for kommende kalenderbegivenheder", - "notification_sound": "Notifikationslyd", - "notification_sound_desc": "Afspil en lyd for kalenderalarmer", - "invitation_parsing": "Pars e-mail-invitationer", - "invitation_parsing_desc": "Registrér kalenderinvitationer i e-mail-vedhæftninger og vis kalenderhandlinger", - "show_time_in_month_view": "Vis tid i månedsvisning", - "show_time_in_month_view_desc": "Vis begivenhedstider i månedskalendervisningen", - "show_week_numbers": "Vis ugenumre", - "show_week_numbers_desc": "Vis ugenumre i minikalenderen", - "calendar_free_scroll": "Fri rulning", - "calendar_free_scroll_desc": "Rul fortløbende gennem måneder, uger og dage i stedet for én periode ad gangen", - "enable_tasks": "Aktivér opgaver", - "enable_tasks_desc": "Vis en opgavevisning i kalenderen til styring af gøremål", - "show_tasks_on_calendar": "Vis opgaver på kalender", - "show_tasks_on_calendar_desc": "Vis opgave-chips i dags- og ugekalendervisningerne", - "hover_preview": "Hover-forhåndsvisning", - "hover_preview_desc": "Vis en detalje-popover når du holder musen over kalenderbegivenheder", - "hover_preview_instant": "Øjeblikkeligt", - "hover_preview_delay_500ms": "0,5 sekunds forsinkelse", - "hover_preview_delay_1s": "1 sekunds forsinkelse", - "hover_preview_delay_2s": "2 sekunders forsinkelse", - "hover_preview_off": "Deaktiveret", - "organizer_identity": "Organisér invitationer som", - "organizer_identity_desc": "Kalenderadressen, der bruges som arrangør, når du inviterer folk til begivenheder.", - "organizer_identity_failed": "Kunne ikke ændre arrangøradressen", - "show_birthday_calendar": "Kontaktfødselsdagskalender", - "show_birthday_calendar_desc": "Vis en virtuel kalender med fødselsdage fra dine kontakter" - }, - "days": { - "monday": "Mandag", - "tuesday": "Tirsdag", - "wednesday": "Onsdag", - "thursday": "Torsdag", - "friday": "Fredag", - "saturday": "Lørdag", - "sunday": "Søndag", - "mon": "Man", - "tue": "Tir", - "wed": "Ons", - "thu": "Tor", - "fri": "Fre", - "sat": "Lør", - "sun": "Søn" - }, - "months": { - "jan": "Jan", - "feb": "Feb", - "mar": "Mar", - "apr": "Apr", - "may": "Maj", - "jun": "Jun", - "jul": "Jul", - "aug": "Aug", - "sep": "Sep", - "oct": "Okt", - "nov": "Nov", - "dec": "Dec", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Begivenhed oprettet", - "event_updated": "Begivenhed opdateret", - "event_deleted": "Begivenhed slettet", - "calendar_created": "Kalender oprettet", - "calendar_deleted": "Kalender slettet", - "event_move_error": "Kunne ikke flytte begivenhed", - "event_resize_error": "Kunne ikke ændre størrelse på begivenhed", - "alert_title": "Kommende begivenhed", - "alert_now": "Starter nu", - "alert_in_minutes": "Om {count} min", - "invitation_sent": "Invitationer sendt", - "invitations_denied_title": "Invitationerne kan ikke sendes", - "invitations_denied": "Serveren afviste at sende invitationerne: {reason}. Gem begivenheden uden at sende dem?", - "save_without_invitations": "Gem uden invitationer", - "rsvp_updated": "Svar opdateret", - "rsvp_error": "Kunne ikke opdatere svar", - "event_duplicated": "Begivenhed duplikeret", - "event_error": "Kunne ikke gemme begivenhed", - "task_due": "Opgave forfalder", - "event_exported": "Begivenhed eksporteret", - "title_copied": "Titel kopieret", - "link_copied": "Link kopieret" - }, - "status": { - "loading_calendars": "Indlæser kalendere...", - "loading_events": "Indlæser begivenheder..." - }, - "quick_create": { - "placeholder": "Ny begivenhedstitel", - "aria_label": "Hurtig oprettelse af begivenhed" - }, - "nav_prev": "Forrige", - "nav_next": "Næste", - "import": { - "title": "Importér kalender", - "tab_file": "Fil", - "tab_url": "URL", - "select_file": "Vælg .ics-fil", - "drop_file": "eller slip fil her", - "supported_formats": "Understøtter iCalendar (.ics) filer", - "url_description": "Indtast URL'en til et eksternt iCalendar (.ics)-feed for at importere begivenheder.", - "url_placeholder": "https://eksempel.dk/kalender.ics", - "url_hint": "Understøtter iCalendar (.ics) URL'er", - "fetch": "Hent", - "invalid_url": "Indtast en gyldig URL", - "url_fetch_failed": "Kunne ikke hente kalender fra URL", - "parsing": "Parser kalenderfil...", - "parsed_events": "{count} begivenheder fundet", - "no_events": "Ingen begivenheder fundet i filen", - "select_all": "Vælg alle", - "deselect_all": "Fravælg alle", - "target_calendar": "Importér til kalender", - "import_button": "Importér valgte", - "importing": "Importerer begivenheder...", - "success": "{count} begivenheder importeret", - "error": "Kunne ikke importere kalender", - "file_too_large": "Filen overstiger 10 MB grænsen", - "invalid_format": "Ugyldigt kalenderfilformat" - }, - "webcal_action": { - "title": "Åbn kalenderlink", - "description": "Hvordan vil du bruge \"{name}\"?", - "import_title": "Importér én gang", - "import_description": "Hent begivenhederne nu og kopier dem til en af dine kalendere.", - "subscribe_title": "Abonnér", - "subscribe_description": "Hold denne kalender synkroniseret automatisk som en separat kalender.", - "cancel": "Annuller" - }, - "management": { - "title": "Kalenderadministration", - "description": "Opret, omdøb og tilpas dine kalendere. Højreklik på en kalender i sidepanelet for hurtigt at ændre dens farve.", - "name": "Navn", - "name_placeholder": "Kalendernavn", - "color": "Farve", - "change_color": "Skift farve", - "random_color": "Ny tilfældig farve", - "add_calendar": "Tilføj kalender", - "edit": "Redigér", - "delete": "Slet", - "save": "Gem", - "create": "Opret", - "cancel": "Annuller", - "default": "Standard", - "confirm_delete": "Slet \"{name}\"? Alle begivenheder i denne kalender fjernes.", - "confirm_clear": "Ryd alle begivenheder fra \"{name}\"? Dette kan ikke fortrydes.", - "set_default": "Angiv som standard", - "default_updated": "Standardkalender opdateret", - "error_default": "Kunne ikke angive standardkalender", - "clear_events": "Ryd begivenheder", - "events_cleared": "{count} begivenheder ryddet", - "error_clear": "Kunne ikke rydde kalenderbegivenheder", - "calendar_created": "Kalender oprettet", - "calendar_updated": "Kalender opdateret", - "calendar_deleted": "Kalender slettet", - "color_updated": "Kalenderfarve opdateret", - "error_create": "Kunne ikke oprette kalender", - "error_update": "Kunne ikke opdatere kalender", - "error_delete": "Kunne ikke slette kalender", - "caldav_url": "CalDAV-URL", - "copy_url": "Kopier CalDAV-URL", - "url_copied": "CalDAV-URL kopieret til udklipsholder", - "share": "Del kalender", - "new_event_in_calendar": "Ny begivenhed i denne kalender", - "kind": "Indhold", - "kind_events": "Begivenheder", - "kind_tasks": "Opgaver", - "kind_both": "Begivenheder og opgaver", - "kind_hint": "Bestemmer, hvilke apps der kan se denne kalender, når den synkroniseres via CalDAV. Det kan ikke ændres senere." - }, - "subscription": { - "title": "iCal-abonnement", - "section_title": "iCal-abonnementer", - "description": "Abonnér på et eksternt iCalendar-feed. Begivenheder synkroniseres automatisk til deres egen kalender. Understøtter https:// og webcal:// URL'er.", - "url_label": "Kalender-URL", - "url_placeholder": "https://eksempel.dk/kalender.ics eller webcal://...", - "name_label": "Kalendernavn", - "name_placeholder": "f.eks. Offentlige helligdage", - "color_label": "Farve", - "refresh_interval": "Opdateringsinterval", - "interval_15": "Hvert 15. minut", - "interval_30": "Hvert 30. minut", - "interval_60": "Hver time", - "interval_360": "Hver 6. time", - "interval_1440": "Hver dag", - "subscribe": "Abonnér", - "subscribing": "Abonnerer...", - "save": "Gem ændringer", - "saving": "Gemmer...", - "edit": "Redigér", - "edit_title": "Redigér abonnement", - "updated": "Opdaterede \"{name}\"", - "update_error": "Kunne ikke opdatere abonnement", - "invalid_url": "Indtast en gyldig URL", - "success": "Abonneret på \"{name}\"", - "error": "Kunne ikke tilføje abonnement", - "refresh": "Opdatér nu", - "refresh_success": "Abonnement opdateret", - "refresh_error": "Kunne ikke opdatere abonnement", - "unsubscribe": "Afmeld", - "confirm_delete": "Afmeld \"{name}\"? Kalenderen og alle dens begivenheder fjernes.", - "deleted": "Abonnement fjernet", - "delete_error": "Kunne ikke fjerne abonnement", - "last_refreshed": "Sidst opdateret: {time}" - }, - "tasks": { - "label": "Opgaver", - "no_tasks": "Ingen opgaver", - "no_title": "(Ingen titel)", - "mark_complete": "Markér som fuldført", - "mark_incomplete": "Markér som ikke fuldført", - "filter_all": "Alle", - "filter_pending": "Afventer", - "filter_completed": "Fuldførte", - "filter_overdue": "Forfaldne", - "show_completed": "Vis fuldførte", - "create": "Ny opgave", - "edit": "Redigér opgave", - "title_placeholder": "Opgavetitel", - "description_placeholder": "Tilføj en beskrivelse...", - "due_date": "Forfaldsdato", - "include_time": "Inkludér tid", - "priority": "Prioritet", - "priority_none": "Ingen", - "priority_high": "Høj", - "priority_medium": "Mellem", - "priority_low": "Lav", - "progress": "Status", - "progress_needs_action": "Kræver handling", - "progress_in_process": "I gang", - "progress_completed": "Fuldført", - "progress_cancelled": "Annulleret", - "calendar": "Kalender", - "alert": "Påmindelse", - "alert_none": "Ingen", - "alert_at_time": "På forfaldsdato", - "alert_5min": "5 minutter før", - "alert_15min": "15 minutter før", - "alert_30min": "30 minutter før", - "alert_1hr": "1 time før", - "alert_1day": "1 dag før", - "delete": "Slet", - "cancel": "Annuller", - "save": "Gem", - "quick_add_placeholder": "Tilføj en opgave...", - "due_today": "I dag", - "due_tomorrow": "I morgen", - "overdue": "Forfalden" - }, - "nav_open_menu": "Åbn menu" - }, - "calendar_event_notifications": { - "someone": "Nogen", - "invited": "{name} har inviteret dig til \"{title}\"", - "updated": "{name} har opdateret \"{title}\"", - "cancelled": "{name} har aflyst \"{title}\"", - "untitled": "Unavngivet begivenhed" - }, - "share_notifications": { - "someone": "Nogen", - "shared": "{name} har delt {kind} \"{object}\" med dig", - "changed": "{name} har ændret din adgang til {kind} \"{object}\"", - "revoked": "{name} har fjernet din adgang til {kind} \"{object}\"", - "object": { - "folder": "mappen", - "calendar": "kalenderen", - "address_book": "adressebogen", - "files": "filmappen" - } - }, - "sharing": { - "title": "Del \"{name}\"", - "description": "Giv adgang til andre brugere eller grupper på denne server. Ændringer træder i kraft øjeblikkeligt.", - "no_shares": "Ikke delt med nogen endnu.", - "add_person": "Tilføj person eller gruppe", - "search_placeholder": "Søg efter navn eller e-mail…", - "loading_principals": "Indlæser brugere…", - "no_principals": "Ingen andre brugere eller grupper fundet.", - "no_match": "Ingen match.", - "remove": "Fjern adgang", - "group": "Gruppe", - "share_added": "Adgang givet", - "share_updated": "Adgang opdateret", - "share_removed": "Adgang fjernet", - "share_failed": "Kunne ikke opdatere deling", - "preset": { - "freeBusy": "Kun ledig/optaget", - "read": "Kun læsning", - "readWrite": "Læs & skriv", - "manager": "Administrator", - "custom": "Brugerdefineret" - } - }, - "advanced_search": { - "title": "Avanceret søgning", - "from": "Fra", - "from_placeholder": "Afsenders e-mail eller navn", - "to": "Til", - "to_placeholder": "Modtagers e-mail eller navn", - "subject": "Emne", - "subject_placeholder": "Emne indeholder...", - "body": "Brødtekst", - "body_placeholder": "Brødtekst indeholder...", - "folder": "Mappe", - "all_folders": "Alle mapper", - "has_attachment": "Vedhæftninger", - "date_after": "Efter", - "date_before": "Før", - "size_min": "Større end (KB)", - "size_max": "Mindre end (KB)", - "starred": "Stjernemarkerede", - "unread": "Ulæst", - "read": "Læst", - "yes": "Ja", - "no": "Nej", - "clear": "Ryd", - "clear_all": "Ryd alle", - "filters_active": "{count} filter", - "filters_active_plural": "{count} filtre", - "toggle_filters": "Mere", - "search_hint": "Brug avancerede filtre til præcis søgning", - "advanced_filters_tooltip": "Avancerede søgefiltre", - "results_found": "{count, plural, =0 {Ingen resultater fundet} one {# resultat fundet} other {# resultater fundet}}", - "results_found_more": "{count}+ resultater fundet", - "suggestions_recent": "Seneste søgninger", - "suggestions_people": "Personer", - "suggestions_from": "Søg efter mails fra {address}", - "suggestions_to": "Søg efter mails til {address}", - "suggestions_remove_recent": "Fjern fra seneste søgninger" - }, - "welcome": { - "title": "Velkommen til din postkasse", - "tip_compose": "Tryk c for at skrive en ny e-mail", - "tip_shortcuts": "Tryk ? for at se alle tastaturgenveje", - "tip_sidebar": "Find Kalender, Kontakter og Indstillinger i sidepanelets menu", - "tip_settings": "Tilpas din oplevelse i Indstillinger", - "got_it": "Forstået", - "settings": "Indstillinger", - "dismiss": "Afvis", - "start_tour": "Start rundvisning" - }, - "demo_welcome": { - "title": "Velkommen til Bulwark Mail", - "description": "Udforsk en fuldt udstyret webmailklient - direkte i din browser. Alle data forbliver på din enhed, så du kan teste alt frit.", - "feature_email": "Læs & skriv e-mail", - "feature_organize": "Tags, stjerner & mapper", - "feature_shortcuts": "Tastaturgenveje", - "feature_privacy": "100% privat demo", - "hint": "Klik på en e-mail til venstre for at komme i gang, eller tag rundvisningen nedenfor." - }, - "files": { - "office_edit": "Rediger dokument", - "office_loading": "Åbner dokumenteditor…", - "office_error": "Dokumenteditoren kunne ikke åbnes.", - "office_close": "Luk editor", - "open_in_new_tab": "Åbn i ny fane", - "title": "Filer", - "search_placeholder": "Søg efter filer...", - "empty_state_title": "Ingen filer endnu", - "empty_state_description": "Upload filer eller opret mapper for at komme i gang", - "upload": "Upload", - "upload_files": "Upload filer", - "new_folder": "Ny mappe", - "new_folder_name": "Mappenavn", - "rename": "Omdøb", - "rename_title": "Omdøb", - "new_name": "Nyt navn", - "name_forbidden_chars": "Serveren tillader ikke disse tegn i navne: {chars}", - "name_reserved": "Dette navn er reserveret på serveren.", - "delete": "Slet", - "delete_confirm_title": "Slet ressource", - "delete_confirm_message": "Er du sikker på, at du vil slette \"{name}\"? Dette kan ikke fortrydes.", - "download": "Download", - "name": "Navn", - "size": "Størrelse", - "modified": "Ændret", - "type": "Type", - "folder": "Mappe", - "file": "Fil", - "parent_directory": "Overordnet mappe", - "breadcrumb_root": "Hjem", - "drop_files_here": "Træk filer eller mapper hertil for at uploade", - "uploading": "Uploader...", - "upload_success": "{count, plural, one {1 fil uploadet} other {# filer uploadet}}", - "upload_error": "Kunne ikke uploade fil", - "create_folder_success": "Mappe oprettet", - "create_folder_error": "Kunne ikke oprette mappe", - "delete_success": "Slettet", - "delete_error": "Kunne ikke slette", - "rename_success": "Omdøbt", - "rename_error": "Kunne ikke omdøbe", - "download_error": "Kunne ikke downloade", - "not_available": "Fillager er ikke tilgængeligt på denne server", - "cancel": "Annuller", - "create": "Opret", - "save": "Gem", - "no_results": "Ingen filer matcher din søgning", - "batch_delete_confirm_message": "Er du sikker på, at du vil slette {count, plural, one {1 element} other {# elementer}}? Dette kan ikke fortrydes.", - "batch_delete_success": "{count, plural, one {1 element slettet} other {# elementer slettet}}", - "grid_view": "Gittervisning", - "list_view": "Listevisning", - "details": "Detaljer", - "path": "Sti", - "preview": "Forhåndsvisning", - "preview_error": "Kunne ikke indlæse forhåndsvisning", - "cut": "Klip", - "copy": "Kopier", - "paste": "Sæt ind", - "move_success": "{count, plural, one {1 element flyttet} other {# elementer flyttet}}", - "move_error": "Kunne ikke flytte", - "paste_success": "Indsat", - "paste_error": "Kunne ikke indsætte", - "new_text_file": "Ny tekstfil", - "file_name": "Filnavn", - "retry": "Prøv igen", - "refresh": "Opdatér", - "toggle_favorite": "Slå favorit til/fra", - "duplicate": "Duplikér", - "duplicate_success": "Duplikeret", - "duplicate_error": "Kunne ikke duplikere", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Fil oprettet", - "create_file_error": "Kunne ikke oprette fil", - "favorites": "Favoritter", - "recent": "Seneste", - "properties": "Egenskaber", - "open_folder": "Åbn mappe", - "upload_folder": "Upload mappe", - "file_too_large": "\"{name}\" overstiger den maksimale filstørrelse ({max})", - "undo": "Fortryd", - "undo_success": "Handling fortrydt", - "undo_error": "Kunne ikke fortryde", - "toolbar": "Filhandlinger", - "file_list": "Filer og mapper", - "context_menu": "Handlinger", - "settings_title": "Filindstillinger", - "settings_display": "Visning", - "settings_default_view": "Standardvisning", - "settings_default_view_desc": "Vælg mellem gitter- og listelayout", - "settings_default_sort": "Standardsortering", - "settings_default_sort_desc": "Vælg standardsortering for filer", - "settings_sort_direction": "Sorteringsretning", - "settings_sort_direction_desc": "Vælg stigende eller faldende rækkefølge", - "settings_ascending": "Stigende", - "settings_descending": "Faldende", - "settings_icons": "Ikoner", - "settings_show_icons": "Vis filikoner", - "settings_show_icons_desc": "Vis ikoner ved siden af filer og mapper", - "settings_colored_icons": "Farvede ikoner", - "settings_colored_icons_desc": "Brug farverige ikoner i stedet for monokrome", - "settings_show_thumbnails": "Vis miniaturer", - "settings_show_thumbnails_desc": "Vis billedforhåndsvisninger i stedet for ikoner for billedfiler", - "settings_behavior": "Adfærd", - "settings_show_hidden": "Vis skjulte filer", - "settings_show_hidden_desc": "Vis filer og mapper der begynder med et punktum", - "settings_folder_layout": "Mappenavigation", - "settings_folder_layout_desc": "Vælg hvordan mapper vises: integreret med filer eller i et sidepanel-træ", - "settings_folder_layout_inline": "Integreret", - "settings_folder_layout_sidebar": "Sidepanel", - "disabled_title": "Filer-funktionen er deaktiveret af din administrator", - "disabled_description": "Store filuploads via WebDAV kan forårsage Stalwart/RocksDB-ustabilitet, herunder hukommelsessvigt og uopretteligt diskforbrug. Slettede filer fjernes muligvis ikke straks fra blob-lageret. Denne funktion anbefales ikke til produktionsmiljøer.", - "stability_warning": "Store filuploads kan forårsage serverustabilitet. Slettede filer fjernes muligvis ikke straks fra lageret. Brug med forsigtighed.", - "no_accounts": "Ingen tilknyttede konti.", - "open_folder_tree": "Åbn mappetræ", - "other_accounts": "Andre konti", - "migration_title": "Opdaterer dine filer…", - "migration_description": "Organiserer mapper og filer i deres rette struktur. Dette sker kun én gang." - }, - "smime": { - "your_certificates": "Dine certifikater", - "your_certificates_desc": "Importér og administrér dine S/MIME-certifikater til signering og kryptering af e-mails", - "recipient_certificates": "Modtagercertifikater", - "recipient_certificates_desc": "Offentlige certifikater til kryptering af e-mails til modtagere", - "identity_bindings": "Identitetsnøglebindinger", - "identity_bindings_desc": "Bind S/MIME-certifikater til dine e-mail-identiteter", - "defaults_title": "Standarder", - "defaults_desc": "Konfigurér standard signerings- og krypteringsadfærd", - "import_pkcs12": "Importér PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importér certifikat", - "no_certificates": "Ingen certifikater importeret endnu", - "no_recipient_certs": "Ingen modtagercertifikater", - "expires": "Udløber", - "expired": "Udløbet", - "bound_to": "Bundet til", - "no_key_bound": "Ingen", - "lock": "Lås nøgle", - "unlock": "Lås nøgle op", - "details": "Vis detaljer", - "delete": "Slet", - "encrypt_by_default": "Kryptér som standard", - "encrypt_by_default_desc": "Kryptér automatisk e-mails når alle modtagere har certifikater", - "remember_unlocked": "Husk oplåste nøgler", - "remember_unlocked_desc": "Hold nøgler oplåst i denne browsersessions varighed", - "sign_default_for": "Signér som standard for", - "enter_p12_passphrase": "Indtast PKCS#12-adgangssætning", - "p12_passphrase_desc": "Indtast adgangssætningen der beskytter denne certifikatfil", - "enter_storage_passphrase": "Angiv lager-adgangssætning", - "storage_passphrase_desc": "Vælg en adgangssætning for at beskytte denne nøgle på din enhed", - "next": "Næste", - "import": "Importér", - "unlock_key": "Lås nøgle op", - "unlock_key_desc": "Indtast lager-adgangssætningen for at låse denne nøgle op til signering eller dekryptering", - "passphrase_placeholder": "Indtast adgangssætning", - "confirm_passphrase_placeholder": "Bekræft adgangssætning", - "passphrase_mismatch": "Adgangssætningerne er ikke ens", - "cancel": "Annuller", - "processing": "Behandler…", - "close": "Luk", - "certificate_details": "Certifikatdetaljer", - "cert_subject": "Emne", - "cert_issuer": "Udsteder", - "cert_email": "E-mail", - "cert_serial": "Serienummer", - "cert_validity": "Gyldighed", - "cert_fingerprint": "Fingeraftryk (SHA-256)", - "cert_algorithm": "Algoritme", - "cert_capabilities": "Funktioner", - "cert_source": "Kilde", - "cert_expired": "Dette certifikat er udløbet", - "cert_not_yet_valid": "Dette certifikat er endnu ikke gyldigt", - "cap_sign": "Signering", - "cap_encrypt": "Kryptering", - "cap_none": "Ingen", - "show_passphrase": "Vis adgangssætning", - "hide_passphrase": "Skjul adgangssætning", - "sign_toggle": "Signér", - "encrypt_toggle": "Kryptér", - "missing_recipient_certs": "Manglende certifikater for: {emails}", - "missing_sender_cert": "Intet certifikat bundet til denne identitet", - "status_encrypted_ok": "Denne besked blev krypteret", - "status_encrypted_no_key": "Denne besked er krypteret, men ingen matchende nøgle blev fundet", - "status_encrypted_failed": "Kunne ikke dekryptere denne besked", - "status_signed_valid": "Signatur bekræftet", - "status_signed_invalid": "Signaturbekræftelse mislykkedes", - "status_signed_expired_cert": "Signeret med et udløbet certifikat", - "status_signed_self_signed": "Signatur gyldig, men certifikatet er selvsigneret (ikke betroet)", - "status_signed_mismatch": "Signatur gyldig, men underskriver matcher ikke afsender", - "status_unsupported": "Ikke-understøttet S/MIME-format", - "auto_import_signer_certs": "Auto-importér underskriver-certifikater", - "auto_import_signer_certs_desc": "Gem automatisk certifikater fra bekræftede signerede e-mails til fremtidig kryptering", - "export": "Eksportér", - "enter_export_passphrase": "Angiv eksport-adgangssætning", - "export_passphrase_desc": "Vælg en adgangssætning for at beskytte den eksporterede PKCS#12-fil", - "export_storage_desc": "Indtast lager-adgangssætningen for at dekryptere nøglen til eksport", - "incorrect_passphrase": "Forkert adgangssætning" - }, - "tour": { - "step_counter": "Trin {current} af {total}", - "skip": "Spring rundvisning over", - "back": "Tilbage", - "next": "Næste", - "finish": "Afslut", - "take_a_tour": "Tag en rundvisning af brugergrænsefladen", - "restart_title": "Introduktionsrundvisning", - "restart_desc": "Afspil den guidede gennemgang af brugergrænsefladen", - "restart_button": "Start rundvisning igen", - "show_on_new_devices_title": "Vis på nye enheder", - "show_on_new_devices_desc": "Afspil velkomstbanneret og rundvisningen første gang du logger ind på en ny enhed, selv hvis du allerede har gennemført dem andetsteds", - "sidebar_title": "Dine postkasser", - "sidebar_desc": "Dette er dit mappesidepanel. Klik på en postkasse for at se dens e-mails. Du kan oprette mapper, trække e-mails mellem dem og se ulæste antal på et øjeblik.", - "compose_title": "Skriv en e-mail", - "compose_desc": "Klik her for at skrive en ny e-mail. Du kan tilføje modtagere, vedhæftninger og bruge rig tekstformatering.", - "search_title": "Søg i din mail", - "search_desc": "Søg efter afsender, emne eller indhold. Klik på filterikonet for avancerede muligheder som datoperiode, vedhæftninger og stjernemarkerede beskeder.", - "email_list_title": "Din e-mail-liste", - "email_list_desc": "E-mails vises her. Klik på en for at læse den til højre. Brug afkrydsningsfeltet til at vælge flere, og masseflyt, -slet eller -tag dem.", - "email_viewer_title": "Læserude", - "email_viewer_desc": "Den valgte e-mail åbnes her. Svar, videresend, arkivér eller slet med værktøjslinjeknapperne. Du kan også stjernemarkere e-mails eller tilføje farvetags.", - "keywords_title": "Farvetags", - "keywords_desc": "Organisér din e-mail med farvekodede tags. Træk en e-mail til et tag for at mærke den, eller højreklik på en e-mail for at tildele tags.", - "calendar_title": "Kalender", - "calendar_desc": "Skift til kalenderen for at administrere dine begivenheder. Opret begivenheder, sæt påmindelser, og se dag-, uge- eller månedslayout.", - "contacts_title": "Kontakter", - "contacts_desc": "Din adressebog bor her. Importér kontakter, opret grupper, og klik på en kontakt for at se deres fulde detaljer.", - "settings_title": "Indstillinger", - "settings_desc": "Tilpas alt: tema, tæthed, signaturer, filtre, tastaturgenveje, kalenderstandarder og meget mere.", - "shortcuts_title": "Tastaturgenveje", - "shortcuts_desc": "Strømbrugere elsker dette. Tryk ? når som helst for at se alle tilgængelige genveje. Du kan navigere, skrive og administrere e-mails uden at røre musen.", - "compose_open_title": "Komponisten", - "compose_open_desc": "Dette er e-mail-komponisten. Tilføj modtagere, skriv din besked, vedhæft filer, og brug rig tekstformatering. Du kan også gemme kladder og bruge skabeloner.", - "calendar_view_title": "Din kalender", - "calendar_view_desc": "Her er din kalender med eksempelbegivenheder. Du kan skifte mellem dag-, uge-, måneds- og agendavisning ved hjælp af værktøjslinjen.", - "create_event_title": "Opret en begivenhed", - "create_event_desc": "Klik på denne knap for at oprette en ny kalenderbegivenhed. Du kan angive en titel, dato, tid og tilføje deltagere.", - "event_modal_title": "Begivenhedsdetaljer", - "event_modal_desc": "Her er begivenhedsformularen. Udfyld titlen, vælg dato og tidspunkt, tilføj et sted eller deltagere. Tryk gem, når du er færdig - eller luk den og gå videre.", - "contacts_list_title": "Dine kontakter", - "contacts_list_desc": "Her er dine kontakter. Klik på en kontakt for at se fulde detaljer til højre. Du kan også oprette nye kontakter, importere vCards eller organisere kontakter i grupper.", - "settings_tabs_title": "Indstillingsmenu", - "settings_tabs_desc": "Her er alle indstillingskategorier. Tilpas dit udseende, administrér identiteter, opsæt e-mail-filtre, konfigurér din kalender og meget mere.", - "files_title": "Fillager", - "files_desc": "Din filbrowser lader dig uploade, organisere og dele filer - som en personlig cloud-drev indbygget i din mail.", - "demo_banner_title": "Demo-kontroller", - "demo_banner_desc": "Du er i demo-tilstand - alt forbliver i din browser. Tryk 'Nulstil demo' når som helst for at starte forfra med rent eksempeldata.", - "quota_title": "Lagerforbrug", - "quota_desc": "Spor din postkassestørrelse her. Cirklen fyldes op, efterhånden som du bruger mere plads." - }, - "unified_mailbox": { - "search_unavailable": "Søgning er ikke tilgængelig i den samlede visning" - }, - "quote_header": { - "reply_line": "Den {date} skrev {from}:", - "forwarded_separator": "---------- Videresendt besked ----------", - "from_label": "Fra", - "date_label": "Dato", - "subject_label": "Emne" - }, - "pwa_install": { - "title": "Installer {appName}", - "description": "Installer vores app for hurtig adgang og offline-understøttelse.", - "not_now": "Ikke nu", - "install": "Installer", - "dont_remind": "Påmind mig ikke igen", - "dismiss_aria": "Afvis installationsprompt" - }, - "deep_link": { - "message_not_found": "Denne besked er ikke længere tilgængelig.", - "thread_not_found": "Denne samtale er ikke længere tilgængelig.", - "folder_not_found": "Denne mappe er ikke længere tilgængelig.", - "event_not_found": "Denne begivenhed er ikke længere tilgængelig.", - "file_not_found": "Denne fil er ikke længere tilgængelig.", - "account_unavailable": "Log ind på den konto for at åbne dette link.", - "copy_message": "Kopier link til besked", - "copy_conversation": "Kopier link til samtale", - "copy_event": "Kopier link til begivenhed", - "copied": "Link kopieret", - "copy_failed": "Kunne ikke kopiere linket" - }, - "plugins": { - "oauth_callback": { - "completing": "Fuldfører godkendelse…", - "completing_message": "Vent venligst, mens forbindelsen oprettes.", - "received_title": "Godkendelse modtaget", - "received_message": "Du kan vende tilbage til den fane, hvor du startede forbindelsen.", - "failed_title": "Godkendelse mislykkedes", - "denied": "Godkendelse blev nægtet.", - "failed_detail": "Godkendelse mislykkedes: {error}", - "missing_params": "Autorisationskode eller tilstand mangler.", - "delivery_failed": "Resultatet af godkendelsen kunne ikke leveres til app-fanen. Luk dette vindue og prøv igen." - } - }, - "global_search": { - "title": "Søg i alt", - "placeholder": "Søg i mail, kontakter, kalender, filer...", - "scope_label": "Søg i", - "account_label": "Konto", - "scope_all": "Alt", - "scope_mail": "Mail", - "scope_contacts": "Kontakter", - "scope_calendar": "Kalender", - "scope_files": "Filer", - "all_accounts": "Alle konti", - "show_all": "Vis alle resultater", - "no_results": "Ingen resultater for “{query}”", - "server_hint": "Hele ord søger på serveren; dele af ord matcher allerede indlæste elementer", - "searching": "Søger...", - "account_error": "Kunne ikke søge i {account}", - "recent_searches": "Seneste søgninger", - "recurring": "Tilbagevendende", - "no_subject": "(no subject)", - "empty_hint": "Søg i mail, kontakter, begivenheder og filer på alle konti. Prøv from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Fladt efter dato", - "grouped": "Grupperet", - "load_more": "Indlæs flere", - "preview_empty": "Vælg et resultat for at se det", - "open_full": "Åbn", - "view_label": "Visning" - } -} +{ + "meta_description": "Minimalistisk webmail-klient, der bruger JMAP-protokollen", + "login": { + "title": "Webmail", + "username_label": "Email", + "username_placeholder": "bruger@eksempel.dk", + "password_label": "Adgangskode", + "password_placeholder": "Indtast din adgangskode", + "jmap_endpoint_label": "JMAP-server", + "jmap_endpoint_placeholder": "https://mail.eksempel.dk", + "jmap_endpoint_cors_hint": "Serveren skal tillade CORS-anmodninger fra dette domæne.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Server valgt ud fra din e-maildomæne.", + "sign_in": "Log ind", + "signing_in": "Logger ind...", + "loading": "Indlæser...", + "reconnecting": "Forbindelse mistet. Forsøger at genoprette forbindelse…", + "error": { + "invalid_credentials": "Ugyldig e-mail eller adgangskode. Kontroller dine oplysninger og prøv igen.", + "invalid_token": "Serveren accepterede ikke dette adgangstoken. Kontroller, at det er komplet og ikke er udløbet eller tilbagekaldt.", + "connection_failed": "Kan ikke nå serveren. Kontrollér din internetforbindelse og prøv igen.", + "cors_blocked": "Serveren kan nås, men blokerer cross-origin-anmodninger. Kontrollér JMAP-serverens CORS-indstillinger og tillad dette domæne.", + "server_error": "Serveren er midlertidigt utilgængelig. Prøv igen senere.", + "generic": "Der opstod en uventet fejl. Kontakt din administrator, hvis problemet fortsætter.", + "totp_required": "En to-faktor-godkendelseskode er påkrævet. Indtast din kode nedenfor.", + "totp_invalid": "Ugyldig godkendelseskode. Kontrollér din autentificeringsapp og prøv igen.", + "oauth_discovery_failed": "SSO er aktiveret, men identitetsudbyderen kunne ikke nås. Kontrollér din OAuth-konfiguration.", + "token_exchange_failed": "Din adgangskode og kode blev accepteret, men mailserveren nægtede at starte en session for denne app. Bed din administrator om at kontrollere OAuth-klientindstillingerne." + }, + "show_password": "Vis adgangskode", + "hide_password": "Skjul adgangskode", + "totp_toggle": "Jeg har en 2FA-kode", + "token_toggle": "Log ind med et adgangstoken", + "password_toggle": "Log ind med adgangskode", + "token_label": "Adgangstoken", + "token_placeholder": "Indsæt dit API-token", + "token_hint": "Opret et API-token i indstillingerne hos din mailudbyder, og indsæt det her.", + "remember_me": "Husk mig", + "config_error": { + "title": "Konfigurationsfejl", + "fetch_failed": "Kan ikke indlæse applikationskonfiguration. Prøv igen senere.", + "server_not_configured": "Mailserveren er ikke konfigureret. Kontakt din administrator." + }, + "remove_from_history": "Fjern fra historik", + "totp_label": "Godkendelseskode", + "totp_placeholder": "000000", + "session_expired": "Din session er udløbet. Log venligst ind igen.", + "dismiss": "Afvis", + "or": "eller", + "sign_in_sso": "Log ind med SSO", + "add_account_title": "Tilføj konto", + "add_account_subtitle": "Log ind med en anden konto", + "cancel": "Annuller", + "website": "Hjemmeside", + "imprint": "Impressum", + "privacy_policy": "Privatlivspolitik", + "try_demo": "Prøv demo", + "demo_description": "Udforsk med eksempeldata - ingen konto nødvendig", + "demo_launching": "Åbner demo...", + "demo_login_button": "Start demo", + "demo_tagline": "Oplev en fuldt udstyret e-mailklient. Ingen konto påkrævet.", + "demo_no_signup": "Ingen tilmelding nødvendig - udforsk frit med eksempeldata", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Fuldfører login...", + "oauth_error": { + "title": "Godkendelse mislykkedes", + "invalid_state": "Sikkerhedsvalidering mislykkedes. Prøv at logge ind igen.", + "missing_params": "Manglende godkendelsesdata. Prøv at logge ind igen.", + "token_exchange_failed": "Kunne ikke fuldføre godkendelse. Prøv igen.", + "access_denied": "Adgang blev nægtet. Kontakt din administrator.", + "back_to_login": "Tilbage til login" + } + }, + "sidebar": { + "close": "Luk", + "compose": "Ny besked", + "compose_hint": "Ny besked (c)", + "search_placeholder": "Søg i mail...", + "search_placeholder_hint": "Søg i mail... (tryk /)", + "storage": "Lagerplads", + "storage_used": "Brugt", + "storage_free": "Ledig", + "storage_total": "I alt", + "sign_out": "Log ud", + "sign_out_of": "Log ud af {account}", + "sign_out_all": "Log ud af alle konti", + "add_account": "Tilføj konto", + "set_as_default": "Angiv som standard", + "switch_account": "Skift konto", + "contacts": "Kontakter", + "calendar": "Kalender", + "settings": "Indstillinger", + "admin": "Admin", + "files": "Filer", + "loading_mailboxes": "Indlæser postkasser...", + "push_connected": "Realtidsopdateringer aktive", + "push_disconnected": "Realtidsopdateringer inaktive", + "keyboard_shortcuts": "Tastaturgenveje", + "theme": { + "light": "Lys tilstand", + "dark": "Mørk tilstand", + "system": "Systemtema" + }, + "language": { + "title": "Sprog" + }, + "mailboxes": { + "inbox": "Indbakke", + "sent": "Sendt", + "drafts": "Kladder", + "trash": "Papirkurv", + "archive": "Arkiv", + "starred": "Stjernemarkerede", + "all_mail": "Al mail", + "spam": "Spam", + "important": "Vigtig" + }, + "unified_inbox": "Samlet indbakke", + "unified_sent": "Alle sendte", + "unified_drafts": "Alle kladder", + "unified_trash": "Alle papirkurve", + "unified_archive": "Alle arkiver", + "unified_junk": "Alt spam", + "all_accounts": "Alle konti", + "all_accounts_loading": "Indlæser konti: {loaded} af {total}", + "unified_mailbox": "Samlet postkasse", + "expand": "Udvid", + "collapse": "Skjul", + "expand_tooltip": "Udvid", + "collapse_tooltip": "Skjul", + "mobile": { + "search": "Søg", + "compose": "Ny besked", + "go_back": "Gå tilbage", + "toggle_menu": "Slå menu til/fra" + }, + "clear_search": "Ryd søgning", + "vacation_active": "Fraværsbeskeder er aktive", + "demo_banner": "Demo-tilstand", + "demo_reset": "Nulstil", + "demo_tour": "Rundvisning", + "tags": "Tags", + "show_all_tags": "Vis alle ({count})", + "show_fewer_tags": "Vis færre", + "folders": "Mapper", + "your_folders": "Dine mapper", + "create_folder": "Opret mappe", + "show_more": "Mere", + "show_fewer": "Mindre", + "shared": "Delt", + "shared_account_hidden": "Skjult i sidepanelet. Vis den igen under Indstillinger › Layout.", + "notifications_muted": "Notifikationer slået fra", + "mail": "Mail", + "nav_label": "Navigation", + "add_app": "Apps", + "scheduled": "Planlagt", + "unified_all_unread": "Alle ulæste", + "unified_all_starred": "Alle med stjerne", + "unified_all_mail": "Al post", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Etiketter", + "create_label": "Opret ny etiket", + "mv_tasks": "Opgaver", + "mv_side_panel": "Sidepanel", + "mv_open_full": "Åbn i fuld visning", + "mv_previous_day": "Forrige dag", + "mv_next_day": "Næste dag", + "mv_add_another_account": "Tilføj en anden konto", + "mv_manage_account": "Administrer din konto", + "mv_storage_used": "{percent}% af {total} brugt" + }, + "protocol_handlers": { + "title": "Standardapps", + "description": "Vælg om e-mail- og kalenderlinks åbnes i Bulwark. Teknisk set registrerer Bulwark sig som protokolhåndtering for mailto:- og webcal:-links.", + "unsupported": "Denne browser eller forbindelse understøtter ikke manuel registrering af protokolhåndtering. Du kan muligvis stadig bruge den installerede PWA via browser- eller OS-indstillinger.", + "mailto_label": "E-mail-links", + "mailto_description": "Åbn mailto:-links i Bulwark med et forudfyldt skrivevindue.", + "protocol_open_mode_label": "Ved åbning af protokollinks", + "protocol_open_mode_description": "Vælg om Bulwark åbner mailto:- og webcal:-links i en ny fane eller genbruger en åben session. Indstillingen \\\"aktiv session\\\" kræver notifikationstilladelse, så du kan klikke på en nødnotifikation for at bringe Bulwark frem, hvis browseren blokerer fokus.", + "protocol_open_mode_active_session": "Åbn i aktiv session hvis muligt", + "protocol_open_mode_new_tab": "Åbn altid ny fane", + "focus_notification_title": "Åbn Bulwark", + "focus_notification_body": "Linket blev åbnet i Bulwark. Klik for at bringe vinduet frem.", + "webcal_label": "Kalenderlinks", + "webcal_description": "Åbn webcal:-links i Bulwark med en forudfyldt kalenderabonnementsdialog.", + "register_mailto": "Registrer e-mail-app", + "register_webcal": "Registrer kalender-app", + "mailto_registered": "Registrering af e-mail-håndtering anmodet", + "webcal_registered": "Registrering af kalenderhåndtering anmodet", + "registration_failed": "Registrering af protokolhåndtering mislykkedes", + "opening_mailto": "Åbner skrivevindue...", + "opening_webcal": "Åbner kalender...", + "browser_note": "Din browser eller dit operativsystem beder dig muligvis bekræfte dette og kræver muligvis, at Bulwark er installeret, før det kan vælges som standardapp.", + "select_account_title": "Vælg konto", + "select_mailto_account": "Vælg hvilken konto der skal åbne dette e-mail-link.", + "select_webcal_account": "Vælg hvilken konto der skal åbne dette kalenderlink.", + "select_account_note": "Dette gælder kun for dette protokollink.", + "detail_to": "Til", + "detail_subject": "Emne", + "detail_no_subject": "Intet emne", + "detail_calendar": "Kalender", + "detail_source": "Kilde", + "active_account": "Aktiv", + "switching_account": "Skifter konto..." + }, + "sidebar_apps": { + "modal_title": "Sidepanel-apps", + "add_new": "Tilføj app", + "edit_app": "Rediger app", + "name_label": "Navn", + "name_placeholder": "Min app", + "name_required": "Navn er påkrævet", + "url_label": "URL", + "url_required": "URL er påkrævet", + "url_invalid": "Indtast en gyldig http- eller https-URL", + "icon_label": "Ikon", + "icon_required": "Ikon er påkrævet", + "open_mode_label": "Åbningstilstand", + "open_new_tab": "Ny fane", + "open_inline": "Indlejret", + "cancel": "Annuller", + "add": "Tilføj", + "update": "Opdater", + "delete": "Slet", + "delete_confirm_title": "Slet app", + "delete_confirm": "Er du sikker på, at du vil slette \\\"{name}\\\"?", + "no_apps": "Ingen apps tilføjet endnu", + "no_apps_hint": "Tilføj brugerdefinerede apps og links til dit sidepanel", + "managed_title": "Leveret af din administrator", + "managed_description": "Disse apps er sat op for alle og kan ikke ændres.", + "managed_badge": "Administreret", + "search_icons": "Søg efter ikoner...", + "show_popular": "Populære", + "show_all": "Alle", + "no_icons_found": "Ingen ikoner fundet", + "inline_badge": "Indlejret", + "tab_badge": "Fane", + "show_on_mobile": "Vis på mobil" + }, + "email_list": { + "columns": { + "from": "Fra", + "to": "Til", + "subject": "Emne", + "date": "Dato", + "size": "Størrelse" + }, + "no_emails": "Ingen beskeder fundet", + "no_emails_description": "Denne postkasse er tom", + "no_search_results": "Ingen resultater fundet", + "no_search_results_description": "Prøv at justere din søgning eller filtre", + "loading": "Indlæser e-mails...", + "unread": "ulæst", + "to_me": "Til mig", + "to_recipients": "Til {count} modtagere", + "and_others": "og {count} andre", + "draft": "Kladde", + "starred": "Stjernemarkerede", + "pinned": "Fastgjort", + "replied": "Besvaret", + "forwarded": "Videresendt", + "has_attachment": "Har vedhæftning", + "conversations_range": "{from}–{to, number} af {total, number}", + "conversations_count": "{count} af {total} samtaler", + "conversations_count_plus": "{count}+ samtaler", + "conversations_count_simple": "{count} samtaler", + "no_conversations": "Ingen samtaler", + "loading_more": "Indlæser flere e-mails...", + "no_more_emails": "Ikke flere e-mails at indlæse", + "batch_actions": { + "select": "Vælg e-mails", + "select_all": "Vælg alle", + "selected_messages": "{count, plural, one {1 e-mail} other {# e-mails}} valgt", + "mark_read": "Markér som læst", + "mark_unread": "Markér som ulæst", + "delete": "Slet", + "delete_confirm_title": "Slet e-mails", + "delete_confirm_message": "Er du sikker på, at du vil slette {count, plural, one {1 e-mail} other {# e-mails}}?", + "clear_selection": "Ryd markering" + }, + "permanent_delete": "Slet permanent", + "permanent_delete_confirm_title": "Slet permanent", + "permanent_delete_confirm_message": "Denne e-mail vil blive slettet permanent. Denne handling kan ikke fortrydes.", + "permanent_delete_confirm_batch_message": "Disse {count, plural, one {1 e-mail} other {# e-mails}} vil blive slettet permanent. Denne handling kan ikke fortrydes.", + "empty_folder": { + "button": "Tøm mappe", + "confirm_title": "Tøm mappe", + "confirm_message": "Alle e-mails i denne mappe vil blive slettet permanent. Denne handling kan ikke fortrydes.", + "confirm_message_trash": "Alle e-mails i denne mappe flyttes til papirkurven.", + "confirm_button": "Tøm mappe", + "junk_hint": "Du kan tømme spam-mappen for permanent at fjerne alle beskeder.", + "trash_hint": "Du kan tømme papirkurven for permanent at fjerne alle beskeder." + }, + "no_scheduled_emails": "Ingen planlagte e-mails", + "no_scheduled_emails_description": "Beskeder planlagt til senere vises her.", + "scheduled_count": "{count, plural, one {1 planlagt e-mail} other {# planlagte e-mails}}", + "scheduled_actions_hint": "Planlagte beskeder kan annulleres, planlægges om eller redigeres fra deres planlagte handlinger.", + "cancel_scheduled_send": "Annuller afsendelse", + "reschedule_send": "Planlæg om", + "cancel_and_edit": "Annuller og redigér", + "cancel_and_compose_again": "Annuller og skriv igen", + "reschedule_prompt": "Indtast en ny dato/tid, f.eks. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Afsenderen anmoder om en læsekvittering:", + "send": "Send kvittering", + "ignore": "Ignorér", + "sent": "Læsekvittering sendt.", + "send_failed": "Læsekvittering kunne ikke sendes", + "mdn_subject": "Læst: {subject}", + "mdn_body": "Dette er en læsekvittering for den besked, du sendte til {recipient}.\n\nBemærk: Denne kvittering bekræfter kun, at beskeden blev vist på modtagerens computer. Der er ingen garanti for, at modtageren har læst eller forstået indholdet." + }, + "no_email_selected": "Ingen e-mail valgt", + "no_email_description": "Vælg en e-mail fra listen for at se den her", + "no_conversation_selected": "Ingen samtale valgt", + "no_conversation_description": "Vælg en samtale fra listen for at læse den her", + "compose": "Ny besked", + "compose_hint": "Skriv ny besked", + "no_subject": "(Intet emne)", + "no_body_content": "(Intet indhold tilgængeligt)", + "no_preview_available": "Forhåndsvisning ikke tilgængelig", + "show_quoted_text": "Vis citeret tekst", + "hide_quoted_text": "Skjul citeret tekst", + "loading_email": "Indlæser e-mail...", + "loading": "Indlæser...", + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "delete": "Slet", + "archive": "Arkivér", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Stjernemarkér", + "unstar": "Fjern stjernemarkering", + "mark_unread": "Markér som ulæst", + "mark_read": "Markér som læst", + "unread": "Ulæst", + "read": "Læst", + "spam_short": "Spam", + "not_spam_short": "Ikke spam", + "move": "Flyt", + "print": "Udskriv", + "view_source": "Vis kilde", + "fullscreen": "Fuld skærm", + "exit_fullscreen": "Afslut fuld skærm", + "view_in_dark_mode": "Vis i mørk tilstand", + "view_in_light_mode": "Vis i lys tilstand", + "export_email": "Eksportér som .eml", + "forward_as_attachment": "Videresend som vedhæftet fil", + "import_email": "Importér .eml eller .zip", + "keyboard_shortcuts": "Tastaturgenveje (?)", + "email_source": "E-mail-kilde", + "draft_banner": "Denne besked er en kladde", + "edit_draft": "Redigér", + "copy_source": "Kopier til udklipsholder", + "source_copied": "Kilde kopieret til udklipsholder", + "attachments": "Vedhæftninger", + "important": "Vigtig", + "download": "Download", + "download_all": "Download alle", + "from": "Fra", + "to": "Til", + "cc": "CC", + "bcc": "BCC", + "date": "Dato", + "subject": "Emne", + "show_details": "Vis detaljer", + "hide_details": "Skjul detaljer", + "external_content_warning": "Billeder og eksternt indhold er blevet blokeret", + "load_external_content": "Indlæs billeder", + "trust_sender": "Stol altid på denne afsender", + "sender_check": { + "unverified_label": "Ubekræftet afsender", + "failed_label": "Afsenderkontrol mislykkedes", + "unverified": "Denne besked udgiver sig for at være fra {domain}, men intet bekræfter det. Den har ingen gyldig signatur fra {domain} og kom ikke fra en server, som {domain} har godkendt.", + "unverified_sent_from": "Denne besked udgiver sig for at være fra {domain}, men den blev sendt fra {host} og har ingen gyldig signatur fra {domain}.", + "failed": "Denne besked udgiver sig for at være fra {domain}, men den bestod ikke domænets afsenderkontrol. Den kan være forfalsket.", + "failed_sent_from": "Denne besked udgiver sig for at være fra {domain}, men den blev sendt fra {host} og bestod ikke afsenderkontrollen for {domain}. Den kan være forfalsket.", + "caution": "Vær forsigtig med links, vedhæftede filer og anmodninger om adgangskoder eller betalingsoplysninger." + }, + "back_to_list": "Tilbage til liste", + "view_contact": "Vis kontakt", + "message_details": "Beskeddetaljer", + "more_reply_options": "Flere svar-muligheder", + "set_tag": "Sæt tag", + "tag": "Tag", + "more_actions": "Flere handlinger", + "previous": "Forrige", + "next": "Næste", + "move_to": "Flyt til...", + "remove_tag": "Fjern tag", + "tag_filter_placeholder": "Filtrer tags", + "tag_no_matches": "Ingen matchende tags", + "more_count": "+{count} mere", + "characters_count": "{count} tegn", + "quick_reply_placeholder": "Skriv et hurtigt svar...", + "more_options": "Flere muligheder", + "sending": "Sender...", + "security_authentication": "Sikkerhed & godkendelse", + "technical_details": "Tekniske detaljer", + "message_id_label": "Message-ID:", + "reply_to_label": "Svar-til:", + "delivery_time_label": "Leveringstid:", + "conversation_part_label": "Del af samtale:", + "previous_messages": "{count} tidligere besked", + "previous_messages_plural": "{count} tidligere beskeder", + "time": { + "day": "dag", + "days": "dage", + "hour": "time", + "hours": "timer", + "minute": "minut", + "minutes": "minutter" + }, + "unknown_sender": "Ukendt", + "recipient_me": "mig", + "recipient_and_others": "{name} og {count} andre", + "recipient_to_prefix": "Til:", + "authentication": { + "title": "Godkendelse", + "status": { + "verified": "Bekræftet", + "warning": "Advarsel", + "none": "Ikke godkendt" + }, + "spf": { + "pass": "SPF bestået", + "fail": "SPF fejlet", + "none": "Ingen SPF" + }, + "dkim": { + "pass": "DKIM gyldig", + "fail": "DKIM ugyldig", + "none": "Ingen DKIM" + }, + "dmarc": { + "pass": "DMARC bestået", + "fail": "DMARC fejlet", + "none": "Ingen DMARC" + }, + "spam_score": "Spam-score", + "tooltip_spf": "Sender Policy Framework: Bekræfter at afsendelsesserveren har tilladelse til at sende e-mails på vegne af domænet", + "tooltip_dkim": "DomainKeys Identified Mail: Bekræfter at e-mailen ikke er ændret under transport ved hjælp af en kryptografisk signatur", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: Sikrer at SPF og DKIM er i overensstemmelse med afsenderens domæne og fastsætter en politik for fejl", + "policy": "Politik", + "result": { + "pass": "Bestået", + "fail": "Fejlet", + "softfail": "Soft fejl", + "neutral": "Neutral", + "permerror": "Permanent fejl", + "temperror": "Midlertidig fejl", + "none": "Ingen" + } + }, + "details": { + "recipients_routing": "Modtagere & routing", + "authentication_security": "Godkendelse & sikkerhed", + "identifiers_threading": "Identifikatorer & trådning", + "mailing_list": "Mailingliste", + "message_properties": "Beskedegenskaber", + "sent": "Sendt", + "received": "Modtaget", + "delivery_time": "Leveringstid", + "in_reply_to": "Svar-til", + "references": "Referencer", + "thread_id": "Tråd-ID", + "size": "Størrelse", + "mime_type": "MIME-type", + "attachments_summary": "{count} filer · {size}", + "list_id": "Liste-ID", + "list_help": "Listehjælp", + "list_post": "Listeopslag", + "list_unsubscribe": "Afmeld", + "iprev": "Omvendt DNS", + "spam_status": "Spam-status", + "ai_verdict": "AI-vurdering", + "account": "Konto", + "no_subject": "(intet emne)" + }, + "headers": { + "routing": "Routing", + "received": "Modtaget", + "message_id": "Besked-ID", + "list_info": "Listeinformation" + }, + "tooltips": { + "reply": "Svar (r)", + "reply_all": "Svar alle (a)", + "forward": "Videresend (f)", + "archive": "Arkivér (e)", + "delete": "Slet (# eller Del)", + "star": "Stjernemarkér (s)", + "unstar": "Fjern stjerne (s)", + "compose": "Ny besked (c)", + "previous": "Forrige e-mail", + "next": "Næste e-mail", + "edit_draft": "Redigér kladde" + }, + "spam": { + "button_title": "Rapportér spam", + "not_spam_title": "Markér som legitim", + "toast_success": "Flyttet til spam", + "toast_batch": "{count} e-mails flyttet til spam", + "toast_undo": "Fortryd", + "toast_not_spam_success": "Flyttet til indbakke", + "toast_not_spam_batch": "{count} e-mails flyttet til indbakke", + "error": "Kunne ikke rapportere spam", + "error_not_spam": "Kunne ikke gendanne e-mail" + }, + "unsubscribe_banner": { + "label": "Nyhedsbrev", + "button": "Afmeld", + "confirm_title": "Afmeld denne afsender?", + "confirm_button": "Bekræft", + "cancel": "Annuller", + "success_http": "Afmeldingsside åbnet i ny fane", + "success_mailto": "Afmeldings-e-mail sendt", + "confirm_message_http": "Afmeldingssiden åbnes i en ny fane.", + "confirm_message_mailto": "Der sendes en afmeldings-e-mail til afsenderen.", + "error": "Kan ikke afmelde", + "dismiss": "Afvis" + }, + "calendar_invitation": { + "loading": "Indlæser begivenhedsdetaljer…", + "title": "Kalenderinvitation", + "published_title": "Offentliggjort begivenhed", + "response_title": "Begivenhedssvar", + "update_title": "Begivenhedsopdatering", + "counter_title": "Modforslag", + "refresh_title": "Opdateringsanmodning", + "declined_counter_title": "Modforslag afvist", + "cancelled_title": "Begivenhed aflyst", + "organizer": "Organiseret af {name}", + "organizer_label": "Organiseret af", + "attendees": "{count, plural, one {# deltager} other {# deltagere}}", + "accept": "Accepter", + "maybe": "Måske", + "decline": "Afvis", + "add_to_calendar": "Tilføj til kalender", + "added": "Tilføjet til kalender", + "rsvp_sent": "Svar sendt", + "parse_error": "Kunne ikke læse invitationen", + "action_failed": "Kunne ikke udføre denne kalenderhandling.", + "no_calendar": "Kalender ikke tilgængelig", + "published_info": "Denne begivenhed blev delt til reference.", + "response_info": "Denne besked indeholder et deltagersvar.", + "response_info_organizer": "Dette deltagersvar opdaterer din begivenhed.", + "update_info": "Denne besked opdaterer en eksisterende begivenhed.", + "counter_info": "Denne besked foreslår ændringer til en begivenhed.", + "counter_info_organizer": "Denne deltager foreslog ændringer til din begivenhed.", + "refresh_info": "Denne besked anmoder om de seneste begivenhedsdetaljer.", + "refresh_info_organizer": "En deltager anmodede om de seneste begivenhedsdetaljer.", + "declined_counter_info": "Organisatoren afviste et modforslag.", + "authentication_failed_info": "Mailgodkendelseskontrol for denne invitation mislykkedes. Vær forsigtig med kalenderhandlinger.", + "authentication_missing_info": "Denne invitation inkluderer ikke bekræftet mailgodkendelse. Bekræft detaljerne med organisatoren, hvis noget ser usædvanligt ud.", + "sender_mismatch_info": "Denne invitation blev sendt fra {sender}, mens organisatoren i kalenderdataene er {organizer}.", + "sender_mismatch_unverified_info": "Denne invitation blev sendt fra {sender}, mens organisatoren i kalenderdataene er {organizer}, og beskeden kunne ikke bekræftes.", + "organizer_role": "Du organiserer denne begivenhed", + "your_response": "Dit svar: {status}", + "response_needed": "Kræver svar", + "response_accepted": "Accepteret", + "response_tentative": "Foreløbig", + "response_declined": "Afslået", + "response_delegated": "Delegeret", + "actor_sent_info": "Sendt af {name}.", + "actor_response_info": "{name} svarede {status}.", + "actor_counter_info": "{name} foreslog ændringer til denne begivenhed.", + "actor_refresh_info": "{name} bad om de seneste begivenhedsdetaljer.", + "actor_declined_counter_info": "{name} afviste modforslaget.", + "actor_note": "Note: {comment}", + "actor_unknown": "Nogen", + "proposed_changes": "Foreslåede ændringer", + "change_title": "Titel", + "change_time": "Tid", + "change_location": "Sted", + "change_description": "Beskrivelse", + "change_empty": "Ingen", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Anvend foreslåede ændringer", + "proposal_applied": "Foreslåede ændringer anvendt.", + "review_proposal": "Gennemgå forslag", + "review_request": "Gennemgå anmodning", + "view_in_calendar": "Vis i kalender", + "select_calendar": "Vælg kalender", + "already_in_calendar": "Allerede i din kalender", + "request_info": "Du er blevet inviteret til denne begivenhed. Svar for at lade arrangøren kende din tilgængelighed.", + "cancel_info": "Arrangøren har aflyst denne begivenhed.", + "event_updated": "Opdatering #{sequence}", + "event_status_tentative": "Foreløbig", + "event_status_cancelled": "Aflyst", + "expand": "Vis detaljer", + "collapse": "Skjul detaljer" + }, + "send": "Send", + "more": "mere", + "scheduled_banner": "Planlagt til afsendelse {date}", + "scheduled_send_created": "E-mail planlagt til afsendelse", + "sending_in": "Sendes om {seconds} s", + "undo_send_failed": "Kunne ikke fortrydes. Beskeden er måske allerede sendt.", + "cancel_scheduled_send": "Annuller afsendelse", + "reschedule_send": "Planlæg om", + "cancel_and_edit": "Annuller og redigér", + "cancel_and_compose_again": "Annuller og skriv igen", + "reschedule_prompt": "Indtast en ny dato/tid, f.eks. 2026-05-04T15:30", + "scheduled_actions_only": "Planlagt-visningen understøtter kun planlagte handlinger", + "undo_send_scheduled": "Besked planlagt til afsendelse", + "undo_send": "Fortryd afsendelse", + "contact_sidebar": { + "title": "Kontakt", + "close": "Luk sidepanel", + "action_email": "E-mail", + "action_email_title": "Send e-mail", + "action_copy": "Kopiér", + "action_copy_title": "Kopiér e-mail", + "action_edit_title": "Rediger kontakt", + "section_emails": "E-mails", + "section_phones": "Telefoner", + "section_organizations": "Organisationer", + "section_addresses": "Adresser", + "section_notes": "Noter", + "not_in_contacts": "Ikke i dine kontakter", + "add_to_contacts": "Tilføj til kontakter", + "copied": "Kopieret!", + "copy_failed": "Kunne ikke kopiere", + "view_contact": "Vis kontakt", + "view_details": "Vis detaljer" + }, + "send_now": "Send nu", + "verification_code": { + "copy": "Kopiér kode {code}", + "copied": "Kode kopieret", + "copy_failed": "Koden kunne ikke kopieres" + }, + "mv_position": "{index} af {total}", + "plugin_panel_show": "Vis pluginpanel", + "plugin_panel_hide": "Skjul pluginpanel" + }, + "email_composer": { + "select_from_directory": "Vælg fra adressefortegnelse", + "minimize": "Minimer", + "maximize": "Fuld skærm", + "restore": "Gendan", + "new_message": "Ny besked", + "signature_edit_hint": "Dobbeltklik for at redigere signaturen", + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "reply_to": "Svar", + "reply_all_to": "Svar alle", + "forward_message": "Videresend", + "from": "Fra", + "to": "Til", + "cc": "CC", + "bcc": "BCC", + "subject": "Emne", + "body_placeholder": "Skriv din besked...", + "send": "Send", + "cancel": "Annuller", + "attach": "Vedhæft", + "attach_photos": "Fotos & videoer", + "attach_files": "Filer", + "attach_from_files": "Vedhæft fra Filer", + "files_picker": { + "attach": "{count, plural, =0 {Vedhæft} one {Vedhæft 1 fil} other {Vedhæft # filer}}", + "empty_folder": "Denne mappe er tom", + "load_failed": "Dine filer kunne ikke indlæses" + }, + "discard": "Kassér", + "discard_draft_title": "Kassér kladde?", + "discard_draft_confirm": "Du har ugemte ændringer. Vil du kassere denne kladde?", + "discard_draft_message": "Kladden bliver slettet.", + "saving": "Gemmer...", + "sending": "Sender...", + "add_link": "Tilføj link", + "draft_saved": "Kladde gemt", + "save_failed": "Kunne ikke gemme", + "to_placeholder": "Modtageres e-mailadresser", + "cc_placeholder": "Cc-modtagere", + "bcc_placeholder": "Bcc-modtagere", + "subject_placeholder": "Emne", + "cc_label": "Cc:", + "bcc_label": "Bcc:", + "subject_label": "Emne:", + "file_size_kb": "KB", + "prefix": { + "forward": "Vs:", + "reply": "Sv:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} other {# e-mails}}", + "no_subject": "(Intet emne)", + "unknown_sender": "Ukendt", + "quote": { + "reply_header": "Den {date} skrev {sender}:", + "forward_header": "---------- Videresendt besked ----------", + "from": "Fra: {sender}", + "date": "Dato: {date}", + "subject": "Emne: {subject}", + "to": "Til: {recipients}" + }, + "remove_sub_address": "Fjern underadresse", + "from_override": { + "toggle_off": "Tilsidesæt", + "toggle_on": "Annuller tilsidesættelse", + "toggle_tooltip": "Redigér Fra-navn og -adresse frit. Mail sendes stadig gennem din identitet.", + "name_label": "Fra-navn", + "name_placeholder": "Navn", + "email_label": "Fra-e-mailadresse", + "email_placeholder": "alias@eksempel.dk", + "envelope_notice": "Hvis din server ikke accepterer denne adresse som konvolutafsender, bruges {identity} i stedet, og modtagerne kan se den i Return-Path-headeren." + }, + "save_as_template": "Gem som skabelon", + "attach_from_device": "Upload fra denne enhed", + "templates_menu": "Skabeloner", + "insert_template": "Indsæt skabelon", + "options": "Indstillinger", + "option_plain_text": "Ren tekst", + "option_plain_text_hint": "Send uden formatering", + "option_read_receipt": "Anmod om læsekvittering", + "option_read_receipt_hint": "Bed modtageren bekræfte, at den er åbnet", + "option_dsn": "Leveringsbesked", + "option_dsn_hint": "Få en rapport, når serveren leverer den", + "option_require_tls": "Kræv krypteret levering", + "option_require_tls_hint": "Lever kun over TLS, ellers afvis", + "option_priority": "Prioritet", + "option_priority_high": "Høj", + "option_priority_normal": "Normal", + "option_priority_low": "Lav", + "validation": { + "recipient_required": "Tilføj en modtager for at sende", + "body_required": "Skriv en besked eller vedhæft en fil", + "invalid_recipients": "Ikke sendt - kontroller disse adresser: {addresses}", + "attachments_uploading": "Vedhæftninger uploades stadig — sendes, når de er færdige", + "attachment_upload_failed": "Ikke sendt - en vedhæftet fil kunne ikke uploades. Fjern den, og prøv igen." + }, + "upload_progress": "Uploader {uploaded} / {total}", + "upload_cancel": "Annuller upload", + "upload_failed": "Kunne ikke uploade {filename}", + "attachment_too_large": "\"{name}\" er større, end serveren tillader ({max} pr. fil)", + "attachments_total_too_large": "Vedhæftningerne ville overskride serverens grænse på {max} pr. besked", + "drop_files": "Træk filer hertil for at vedhæfte", + "show_less": "Vis mindre", + "send_failed": "Kunne ikke sende e-mail", + "send_timeout": "Serveren holdt op med at svare. Tjek mappen Sendt, før du sender igen — beskeden er måske allerede afsendt.", + "send_offline": "Du ser ud til at være offline. Beskeden er her stadig, send den igen, når du er online.", + "formatting": "Formatering", + "send_recipients_rejected": "Ikke sendt - serveren afviste alle modtagere.", + "send_plugin_failed": "Ikke sendt - et plugin, der står for afsendelsen, fejlede eller tog for lang tid. Tjek mappen Sendt, før du sender igen.", + "send_some_recipients_rejected": "Sendt, men ikke til disse modtagere - serveren afviste dem.", + "continue_draft": "Fortsæt kladde", + "close_draft_title": "Gem eller kassér kladde?", + "close_draft_message": "Du har ugemte ændringer. Vil du gemme dette som en kladde eller kassere det?", + "smime_sign_on": "S/MIME-signering aktiveret", + "smime_sign_off": "Aktivér S/MIME-signering", + "smime_encrypt_on": "S/MIME-kryptering aktiveret", + "smime_encrypt_off": "Aktivér S/MIME-kryptering", + "smime_encrypt_unavailable": "S/MIME-kryptering utilgængelig – mangler modtager-certifikater", + "smime_unlock_title": "Lås S/MIME-nøgle op", + "smime_unlock_message": "Indtast adgangssætningen for at låse din S/MIME-signeringsnøgle op.", + "smime_unlock_button": "Lås op", + "smime_passphrase_placeholder": "Adgangssætning", + "forgot_attachment": { + "title": "Har du glemt en vedhæftning?", + "message": "Din besked nævner \"{keyword}\" men ingen fil er vedhæftet. Send alligevel?", + "send_anyway": "Send alligevel", + "back": "Tilbage til redigering" + }, + "empty_subject": { + "title": "Send uden emne?", + "message": "Denne besked har intet emne. Send alligevel?", + "dont_ask_again": "Spørg ikke igen", + "send_anyway": "Send alligevel", + "back": "Tilbage til redigering" + }, + "link_url_prompt": "Indtast URL'en", + "schedule_send": "Planlæg afsendelse", + "schedule_send_description": "Vælg hvornår serveren skal frigive denne besked.", + "schedule_preset": { + "tomorrow_morning": "I morgen formiddag", + "tomorrow_afternoon": "I morgen eftermiddag", + "monday_morning": "Mandag formiddag" + }, + "schedule_send_required": "Vælg en dato og et tidspunkt.", + "schedule_send_invalid": "Indtast en gyldig dato og tid.", + "schedule_send_future": "Vælg en fremtidig dato og tid.", + "schedule_send_too_late": "Dette tidspunkt er senere end serveren tillader.", + "schedule_send_unsupported": "Planlagt afsendelse understøttes ikke for denne konto.", + "schedule_send_cleanup_warning": "Planlagt afsendelse blev oprettet, men oprydning af kladde mislykkedes.", + "send_delay_unsupported": "Sendeforsinkelse understøttes ikke for denne konto.", + "send_delay_unsupported_confirm": "Denne konto understøtter ikke sendeforsinkelse. Send straks i stedet?", + "recipient_edit_email": "Rediger e-mailadresse", + "recipient_edit_name": "Rediger visningsnavn", + "recipient_email_placeholder": "E-mailadresse", + "recipient_name_placeholder": "Visningsnavn", + "invalid_recipient": "Ikke en gyldig e-mailadresse", + "autocomplete_search_server": "Søg på serveren", + "autocomplete_searching": "Søger...", + "mention_recipients": "Modtagere", + "mention_card_not_recipient": "Ikke i Til eller CC, så vedkommende får ikke beskeden.", + "mention_card_add": "Tilføj til {field}", + "mention_card_remove": "Fjern omtale", + "toolbar": { + "bold": "Fed", + "italic": "Kursiv", + "underline": "Understreget", + "strikethrough": "Gennemstreget", + "text_color": "Tekstfarve", + "remove_color": "Fjern farve", + "heading_1": "Overskrift 1", + "heading_2": "Overskrift 2", + "bullet_list": "Punktopstilling", + "ordered_list": "Nummereret liste", + "quote": "Citat", + "code_block": "Kodeblok", + "align_left": "Venstrejusteret", + "align_center": "Centreret", + "align_right": "Højrejusteret", + "text_direction": "Tekstretning (RTL/LTR)", + "link": "Link", + "table": "Tabel", + "clear_formatting": "Ryd formatering", + "undo": "Fortryd", + "redo": "Gentag", + "add_row_above": "Tilføj række over", + "add_row_below": "Tilføj række under", + "add_column_before": "Tilføj kolonne før", + "add_column_after": "Tilføj kolonne efter", + "delete_row": "Slet række", + "delete_column": "Slet kolonne", + "toggle_header_row": "Slå overskriftsrække til/fra", + "delete_table": "Slet tabel", + "pick_size": "Vælg størrelse", + "font_size": "Skriftstørrelse", + "font_size_default": "Standard", + "background_color": "Baggrundsfarve", + "remove_background_color": "Fjern baggrundsfarve" + }, + "send_filing_warning": "Sendt - men oprydningen bagefter mislykkedes, en forældet kladde kan blive stående.", + "pop_out": "Åbn svaret i et vindue", + "send_guard_failed": "Ikke sendt - et plugin, der kontrollerer beskeder før afsendelse, fejlede eller svarede ikke." + }, + "confirm_dialog": { + "confirm": "Bekræft", + "cancel": "Annuller" + }, + "common": { + "loading": "Indlæser...", + "error": "Fejl", + "success": "Succes", + "cancel": "Annuller", + "save": "Gem", + "delete": "Slet", + "edit": "Redigér", + "close": "Luk", + "search": "Søg", + "refresh": "Opdatér", + "settings": "Indstillinger", + "help": "Hjælp", + "logout": "Log ud", + "yes": "Ja", + "no": "Nej", + "unknown": "Ukendt", + "app_title": "Webmail", + "reconnecting": "Forbindelse mistet. Forsøger at genoprette forbindelse…", + "rate_limited_title": "Server-godkendelse er midlertidigt hastighedsbegrænset.", + "rate_limited_detail": "Bulwark har sat baggrundsanmodninger på pause for at undgå udelukkelse. Prøver igen om {seconds}s.", + "rate_limited_action_title": "Anmodning sat på pause for at undgå udelukkelse.", + "rate_limited_action_detail": "Bulwark venter på, at serverens nedkølingsperiode slutter, før der sendes flere godkendte anmodninger. Prøv igen om {seconds}s.", + "pull_to_refresh": "Træk for at opdatere", + "release_to_refresh": "Slip for at opdatere", + "refreshing": "Opdaterer…" + }, + "notifications": { + "email_sent": "E-mail sendt succesfuldt", + "email_deleted": "E-mail slettet", + "email_archived": "E-mail arkiveret", + "emails_deleted": "{count, plural, one {# e-mail slettet} other {# e-mails slettet}}", + "emails_archived": "{count, plural, one {# e-mail arkiveret} other {# e-mails arkiveret}}", + "error_archiving": "Kunne ikke arkivere e-mail", + "error_updating": "Kunne ikke opdatere e-mail", + "email_starred": "E-mail stjernemarkeret", + "email_unstarred": "Stjernemarkering fjernet", + "email_marked_read": "E-mail markeret som læst", + "email_marked_unread": "E-mail markeret som ulæst", + "copied_to_clipboard": "Kopieret til udklipsholder", + "source_copied": "Kilde kopieret til udklipsholder", + "error_sending": "Kunne ikke sende e-mail", + "error_deleting": "Kunne ikke slette e-mail", + "error_loading": "Kunne ikke indlæse e-mails", + "new_email": "Ny e-mail", + "new_email_from": "Fra {sender}", + "click_to_view": "Klik for at se", + "email_moved": "E-mail flyttet", + "emails_moved": "{count} e-mails flyttet", + "moved_to_mailbox": "Flyttet til {mailbox}", + "move_failed": "Flytning mislykkedes", + "removed_from_folder": "Fjernet fra {mailbox}", + "remove_from_folder_failed": "Kunne ikke fjerne fra denne mappe", + "emails_copied": "{count} e-mails kopieret", + "copy_failed": "Kopiering mislykkedes", + "move_error": "Kunne ikke flytte e-mails til den valgte mappe", + "email_tagged": "E-mail tagget", + "emails_tagged": "{count} e-mails tagget", + "tag_failed": "Tagning mislykkedes", + "identity_created": "Identitet oprettet succesfuldt", + "identity_updated": "Identitet opdateret succesfuldt", + "identity_deleted": "Identitet slettet", + "identity_set_primary": "Primær identitet opdateret", + "identity_create_failed": "Kunne ikke oprette identitet: {error}", + "identity_update_failed": "Kunne ikke opdatere identitet: {error}", + "identity_delete_failed": "Kunne ikke slette identitet: {error}", + "identity_unauthorized": "Du er ikke autoriseret til at sende fra denne e-mailadresse", + "identity_not_found": "Identitet ikke fundet", + "vacation_saved": "Fraværsindstillinger gemt", + "vacation_save_failed": "Kunne ikke gemme fraværsindstillinger", + "filters_saved": "Filtre gemt succesfuldt", + "filters_save_failed": "Kunne ikke gemme filtre", + "filters_deleted": "Filterregel slettet", + "templates_exported": "Skabeloner eksporteret succesfuldt", + "templates_imported": "{count, plural, one {# skabelon} other {# skabeloner}} importeret", + "templates_import_errors": "Nogle skabeloner kunne ikke importeres", + "templates_import_empty": "Ingen skabeloner fundet i filen", + "export_email_error": "Kunne ikke eksportere e-mail", + "import_email_success": "E-mail importeret succesfuldt", + "import_email_error": "Kunne ikke importere e-mail", + "rule_created": "Regel oprettet", + "rule_merged": "Føjet til reglen “{name}”", + "rule_already_covered": "Er allerede dækket af reglen “{name}”", + "rule_apply_existing": "{count, plural, one {Anvend på # eksisterende besked} other {Anvend på # eksisterende beskeder}}", + "rule_applied": "{count, plural, =0 {Ingen beskeder skulle ændres} one {Regel anvendt på # besked} other {Regel anvendt på # beskeder}}", + "rule_apply_failed": "Reglen kunne ikke anvendes på eksisterende beskeder", + "rule_edit": "Rediger regel", + "rule_undo": "Fortryd", + "rule_undone": "Regelændring fortrudt", + "rule_undo_failed": "Regelændringen kunne ikke fortrydes", + "rule_undo_conflict": "Dine filtre er ændret siden. Fortryd ændringen i filterindstillingerne.", + "rule_folder_failed": "Mappen kunne ikke oprettes", + "vacation_filters_save_failed": "Fraværsindstillinger gemt, men videresendelse og svarmodtagere kunne ikke gemmes" + }, + "date": { + "today": "I dag", + "yesterday": "I går", + "this_week": "Denne uge", + "last_week": "Sidste uge", + "this_month": "Denne måned", + "older": "Ældre", + "just_now": "Lige nu", + "minutes_ago": "{count} minut siden", + "minutes_ago_plural": "{count} minutter siden", + "hours_ago": "{count} time siden", + "hours_ago_plural": "{count} timer siden", + "days_ago": "{count} dag siden", + "days_ago_plural": "{count} dage siden" + }, + "language": { + "title": "Sprog", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Vælg sprog", + "switch_to_english": "Skift til engelsk", + "switch_to_french": "Skift til fransk", + "switch_to_japanese": "Skift til japansk", + "switch_to_spanish": "Skift til spansk", + "switch_to_italian": "Skift til italiensk", + "switch_to_german": "Skift til tysk", + "switch_to_dutch": "Skift til nederlandsk", + "switch_to_portuguese": "Skift til portugisisk", + "switch_to_russian": "Skift til russisk", + "switching": "Skifter sprog...", + "switch": "Skift sprog", + "current": "Nuværende sprog", + "polish": "Polski", + "switch_to_polish": "Skift til polsk", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Skift til farsi", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Skift til mongolsk" + }, + "settings": { + "title": "Indstillinger", + "back_to_mail": "Tilbage til mail", + "save_success": "Indstillinger gemt succesfuldt", + "import_success": "Indstillinger importeret succesfuldt", + "import_error": "Kunne ikke importere indstillinger", + "reset_confirm": "Er du sikker på, at du vil nulstille alle indstillinger til standard?", + "unsaved_changes": "Du har ugemte ændringer", + "discard_changes": "Kassér ugemte ændringer?", + "discard": "Kassér", + "keep_editing": "Fortsæt redigering", + "search_placeholder": "Søg i indstillinger", + "search_clear": "Ryd søgning", + "search_no_results": "Ingen match i indstillinger", + "tabs": { + "appearance": "Udseende", + "language": "Sprog, region & tid", + "email": "E-mail-adfærd", + "composer": "Skrivevindue", + "privacy": "Privatliv & sikkerhed", + "account": "Konto", + "identities": "Identiteter", + "vacation": "Fravær", + "advanced": "Avanceret", + "calendar": "Kalender", + "filters": "Filtre", + "templates": "Skabeloner", + "folders": "Mapper", + "keywords": "Tags", + "security": "Sikkerhed", + "files": "Filer", + "contacts": "Kontakter", + "encryption": "Kryptering", + "protocol_handlers": "Standardapps", + "sidebar_apps": "Sidepanel-apps", + "notifications": "Notifikationer", + "layout": "Layout", + "themes": "Temaer", + "reading": "Læsning", + "composing": "Skrivning", + "downloads": "Downloads", + "content_senders": "Indhold & afsendere", + "about_data": "Om & data", + "debug": "Debug" + }, + "themes": { + "title": "Temaer", + "description": "Vælg mellem temaer, som din administrator har udrullet, og indbyggede forudindstillinger.", + "locked": "Temavalget er låst af en administrator.", + "forced": "Temaet »{name}« er gennemtvunget af administratoren og kan ikke ændres", + "admin_theme": "Administratortema", + "activated": "Tema aktiveret", + "default_restored": "Standardtema gendannet", + "default_name": "Standard", + "default_badge": "Standard", + "admin_enforced": "Gennemtvunget af administrator" + }, + "tab_groups": { + "general": "Generelt", + "account": "Konto & identitet", + "organization": "Mailorganisering", + "apps": "Apps", + "system": "System", + "appearance": "Udseende", + "mail": "Mail", + "privacy": "Privatliv & sikkerhed", + "advanced": "Avanceret" + }, + "appearance": { + "message_list_order": { + "label": "Rækkefølge i beskedlisten", + "description": "Hvordan beskeder sorteres i listen. Serveren sorterer, så det gælder hele mappen og ikke kun de indlæste beskeder.", + "preset": { + "chronological": "Kronologisk (nyeste først)", + "unread_first": "Ulæste først", + "starred_first": "Stjernemarkerede først", + "tagged_first": "Med mærkat først", + "custom": "Tilpasset" + }, + "tag_label": "Mærkat", + "advanced_show": "Avanceret: rediger sorteringsniveauer", + "advanced_hide": "Skjul avanceret", + "criterion_label": "Kriterium for niveau {n}", + "direction_label": "Retning for niveau {n}", + "criteria": { + "unread": "Ulæst", + "starred": "Stjernemarkeret", + "tag": "Mærkat", + "receivedAt": "Modtagelsesdato", + "sentAt": "Afsendelsesdato", + "from": "Afsender", + "subject": "Emne", + "size": "Størrelse" + }, + "direction": { + "unread_first": "Ulæste først", + "read_first": "Læste først", + "starred_first": "Stjernemarkerede først", + "unstarred_first": "Uden stjerne først", + "tagged_first": "Med mærkat først", + "untagged_first": "Uden mærkat først", + "newest_first": "Nyeste først", + "oldest_first": "Ældste først", + "a_to_z": "A → Å", + "z_to_a": "Å → A", + "largest_first": "Største først", + "smallest_first": "Mindste først" + }, + "add_level": "Tilføj niveau", + "remove_level": "Fjern niveau", + "move_up": "Flyt op", + "move_down": "Flyt ned", + "tiebreak_note": "Op til tre niveauer; senere niveauer afgør ved lighed. \"Nyeste først\" anvendes altid til sidst.", + "scope": { + "label": "Anvend på alle mapper", + "description": "Når den er slået fra, gælder rækkefølgen kun for Indbakken; andre mapper forbliver kronologiske." + }, + "unsupported_note": "Din mailserver understøtter ikke sortering efter læsestatus, stjerner eller mærkater. Disse niveauer ignoreres, og listen forbliver kronologisk." + }, + "title": "Udseende", + "description": "Tilpas udseendet af din webmail", + "theme": { + "label": "Tema", + "description": "Vælg dit foretrukne farveskema", + "light": "Lys", + "dark": "Mørk", + "system": "System" + }, + "language": { + "label": "Sprog", + "description": "Vælg dit foretrukne sprog" + }, + "font_size": { + "label": "Skriftstørrelse", + "description": "Juster tekststørrelse for bedre læsbarhed", + "small": "Lille", + "medium": "Mellem", + "large": "Stor" + }, + "list_density": { + "label": "Tæthed", + "description": "Kontrollér afstand og polstring i brugergrænsefladen", + "extra_compact": "Ekstra kompakt", + "compact": "Kompakt", + "regular": "Almindelig", + "comfortable": "Komfortabel" + }, + "interface_layout": { + "label": "Grænsefladelayout", + "description": "Hvor søgning, navigation og værktøjslinjer er placeret", + "default": "Standard", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Aktivér animationer", + "description": "Vis glatte overgange og effekter" + }, + "toolbar_position": { + "label": "Værktøjslinjens position", + "description": "Hvor e-mail-handlingsknapper (Svar, Arkivér, Slet osv.) skal vises", + "top": "Top", + "below_subject": "Under emne" + }, + "toolbar_labels": { + "label": "Vis værktøjslinjeetiketter", + "description": "Vis tekstetiketter ved siden af værktøjslinjeikoner. Deaktivér for at spare plads, når du kender ikonerne." + }, + "hide_account_switcher": { + "label": "Skjul kontoskifter i sidepanel", + "description": "Skjul kontovælgeren øverst i mappesidepanelet. Du kan stadig skifte konto fra navigationslinjen nederst." + }, + "show_rail_account_list": { + "label": "Vis kontoavatarer på navigationslinje", + "description": "Vis individuelle kontocirkler nederst på navigationslinjen for hurtig skift, med en log-ud-knap nedenunder." + }, + "unified_mailbox": { + "label": "Samlet postkasse", + "description": "Vis samlede mapper (Indbakke, Sendt osv.) på tværs af alle tilknyttede konti", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Inkluder gruppepostkasser", + "description": "Inkluder også delte/gruppepostkasser i den samlede visning." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Farverige sidepane-ikoner", + "description": "Farvelæg mappe- og tag-ikoner efter type (blå indbakke, rød spam, grøn sendt osv.). Deaktivér for et monokromt sidepanel." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Vis samlet antal beskeder", + "description": "Viser det samlede antal beskeder ud for mapper og tags sammen med antallet af ulæste. Slå fra for kun at vise ulæste." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Vis delte mailkonti", + "description": "Vis mapperne fra postkasser, der er delt med dig, under Delt i sidepanelet. Slå fra for at skjule dem alle, eller skjul enkelte konti nedenfor. Deres kalendere og kontakter bliver." + }, + "pro_interface": { + "label": "Pro-grænseflade (eksperimentel)", + "description": "Power user-layout kun til skrivebordet med beskedvisning på flere faner og arbejdsforløb på tværs af konti. Standardgrænsefladen påvirkes ikke; du kan skifte tilbage når som helst.", + "open_label": "Åbn Pro-grænsefladen", + "back_to_standard": "Tilbage til standard" + }, + "cross_unread": { + "label": "Alle konti: Ulæste", + "description": "Viser en post i sektionen Alle konti med ulæst post fra alle konti, på tværs af alle mapper undtagen spam, sendt, arkiv, papirkurv og kladder." + }, + "cross_starred": { + "label": "Alle konti: Med stjerne", + "description": "Viser en post i sektionen Alle konti med post med stjerne fra alle konti, på tværs af alle mapper undtagen spam, sendt, arkiv, papirkurv og kladder." + }, + "cross_all": { + "label": "Alle konti: Al post", + "description": "Viser en post i sektionen Alle konti med al post fra alle konti, på tværs af alle mapper undtagen spam, sendt, arkiv, papirkurv og kladder." + } + }, + "keywords": { + "title": "E-mail-tags", + "description": "Definér tags til at organisere dine e-mails med farver. Disse gemmes som JMAP-nøgleord på serveren.", + "add_keyword": "Tilføj tag", + "label_field": "Visningsnavn", + "label_placeholder": "f.eks. Arbejde, Privat, Vigtigt", + "id_field": "Tag-ID", + "id_placeholder": "f.eks. arbejde, privat", + "color_field": "Farve", + "id_exists": "Dette tag-ID findes allerede", + "edit": "Redigér tag", + "delete": "Slet tag", + "save": "Gem", + "add": "Tilføj", + "cancel": "Annuller", + "migrating": "Opdaterer tag på eksisterende e-mails…", + "migration_error": "Kunne ikke opdatere tag på eksisterende e-mails", + "filters_migration_error": "Nogle filterregler bruger stadig det gamle tag – opdatér dem manuelt", + "nesting": { + "label": "Indlejrede tags", + "description": "Indlejr tags under andre tags og vis dem som et træ i sidepanelet." + }, + "parent_field": "Overordnet tag", + "no_parent": "Intet overordnet tag", + "too_long": "Denne tagsti er for lang (højst {max} tegn)", + "has_children_locked": "Andre tags er indlejret under dette, så dets navn og overordnede tag er låst. Flyt eller fjern dem først.", + "has_children_delete": "Fjern først de tags, der er indlejret under dette", + "visibility_field": "Synlighed i sidepanel", + "visibility": { + "show": "Vis", + "unread": "Vis ved ulæste", + "hide": "Skjul" + }, + "unrecognized": { + "title": "Gendan tags fra serveren", + "description": "Tagnavne og farver gemmes i denne browser, men selve taggene ligger på dine e-mails som JMAP-nøgleord. Scan din post for nøgleord, som intet tag her forklarer, og tilføj dem igen.", + "scan": "Søg efter tags", + "rescan": "Scan igen", + "scanning": "Scanner… {scanned} af {total} e-mails", + "none": "Alle tags på dine e-mails er allerede defineret her.", + "messages": "{count, plural, one {# e-mail} other {# e-mails}}", + "add_all": "Tilføj alle {count}", + "partial": "Kun de {scanned} nyeste af {total} e-mails blev scannet. Tags, der kun bruges på ældre post, mangler måske.", + "error": "Kunne ikke scanne din post for tags." + } + }, + "notifications": { + "test_sound": "Test notifikationslyd", + "sounds": { + "default": "Standard (Bip)", + "cheerful": "Glad", + "involved": "Engageret", + "swift": "Hurtig gestus", + "relax": "Afslapning" + }, + "push": { + "title": "Baggrundsnotifikationer", + "description": "Modtag systemnotifikationer for ny mail, når dette site er lukket. Leveres via Bulwark push-relæet; relæet ser aldrig mail-indhold.", + "relay_label": "Push-relæ", + "relay_desc": "Som standard det hosted Bulwark-relæ. Ændr kun, hvis du selv-host'er.", + "relay_locked": "Indstillet af administrator", + "relay_locked_desc": "Push-relæ-URL'en er indstillet af din administrator og kan ikke ændres.", + "status_active": "Aktiv på denne enhed", + "status_inactive": "Ikke aktiveret på denne enhed", + "status_unsupported": "Denne browser understøtter ikke Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "Arbejder…", + "enable": "Aktivér", + "reenable": "Genregistrér", + "disable": "Deaktivér", + "confirm_disable_title": "Deaktivér baggrundsnotifikationer?", + "confirm_disable_message": "Denne enhed stopper med at modtage alarmer, når sitet er lukket.", + "dismiss_aria": "Afvis notifikationsprompt", + "ios_hint": "På iOS skal du installere sitet på din startskærm først - Safari leverer kun Web Push til installerede PWA'er.", + "devices_title": "Registrerede enheder", + "devices_desc": "Alle enheder, der er registreret til at modtage baggrundsnotifikationer for denne konto. Tilbagekalder du en, stopper den med at modtage med det samme.", + "devices_loading": "Indlæser registrerede enheder…", + "devices_empty": "Ingen enheder er registreret til baggrundsnotifikationer.", + "devices_error": "Kunne ikke indlæse de registrerede enheder.", + "devices_refresh": "Opdater", + "device_this": "Denne enhed", + "device_other": "Anden enhed", + "device_status_active": "Leverer notifikationer", + "device_status_inactive": "Har aldrig leveret en notifikation", + "device_status_unknown": "Ukendt for dette relæ", + "device_expires": "Udløber {date}", + "revoke": "Tilbagekald", + "confirm_revoke_title": "Tilbagekald denne enhed?", + "confirm_revoke_message": "Den stopper med at modtage baggrundsnotifikationer med det samme. Slår du push til igen på enheden, registreres den på ny.", + "confirm_revoke_message_this": "Denne enhed stopper med at modtage baggrundsnotifikationer med det samme." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Notifikationslyd", + "description": "Vælg hvilken lyd der skal afspilles for notifikationer", + "choose": "Lyd", + "choose_desc": "Vælg en notifikationstone og klik på højttalerikonet for at forhåndsvise den" + }, + "email": { + "title": "E-mail-notifikationer", + "description": "Konfigurér notifikationer for indkommende e-mails", + "enabled": "E-mail-notifikationer", + "enabled_desc": "Vis notifikationer, når nye e-mails ankommer", + "sound": "Notifikationslyd", + "sound_desc": "Afspil en lydadvarsel, når nye e-mails ankommer", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Kalendernotifikationer", + "description": "Konfigurér notifikationer for kalenderbegivenheder", + "enabled": "Begivenhedsnotifikationer", + "enabled_desc": "Vis alarmer for kommende kalenderbegivenheder", + "sound": "Notifikationslyd", + "sound_desc": "Afspil en lyd for kalenderpåmindelser", + "invitation_parsing": "Pars e-mail-invitationer", + "invitation_parsing_desc": "Registrér kalenderinvitationer i e-mail-vedhæftninger og vis kalenderhandlinger" + } + }, + "language_region": { + "title": "Sprog, region & tid", + "description": "Sprog, datoformat, tidsformat og andre regionale indstillinger", + "language": { + "label": "Sprog", + "description": "Vælg dit foretrukne sprog", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Datoformat", + "description": "Sådan vises datoer på e-mail-listen", + "smart": "Smart", + "relative": "Relativ", + "full": "Altid fuld dato", + "preview_today": "I dag:", + "preview_this_week": "Denne uge:", + "preview_older": "Ældre:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Tidsformat", + "description": "Vælg mellem 12-timers eller 24-timers ur", + "12h": "12-timers", + "24h": "24-timers" + }, + "first_day": { + "label": "Første ugedag", + "description": "Start ugen på søndag eller mandag", + "sunday": "Søndag", + "monday": "Mandag" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Ryd søgningen ved skift af mappe", + "description": "Klik på en mappe (Indbakke, Sendt …) rydder den aktive søgning og viser mappen. Når det er slået fra, bevares søgningen og køres igen i den valgte mappe." + }, + "request_read_receipt": { + "label": "Anmod om læsekvittering som standard", + "description": "Aktivér anmodningen om læsekvittering på forhånd, når du skriver en ny besked." + }, + "read_receipt_response": { + "label": "Svar på anmodninger om læsekvittering", + "description": "Hvad der skal ske, når en indgående besked beder om en læsekvittering.", + "ask": "Spørg hver gang", + "always": "Send altid", + "never": "Send aldrig" + }, + "title": "E-mail-adfærd", + "description": "Konfigurér hvordan e-mails håndteres", + "mark_read": { + "label": "Markér som læst", + "description": "Hvornår e-mails skal markeres som læst, når de åbnes", + "instant": "Øjeblikkeligt", + "delay_3s": "Efter 3 sekunder", + "delay_5s": "Efter 5 sekunder", + "never": "Aldrig" + }, + "delete_action": { + "label": "Slet-handling", + "description": "Hvad der sker, når du sletter en e-mail", + "trash": "Flyt til papirkurv", + "trash_and_read": "Flyt til papirkurv og marker som læst", + "permanent": "Slet permanent", + "warning": "E-mails vil blive slettet permanent og kan ikke gendannes. Denne handling er irreversibel." + }, + "message_spacing": { + "label": "Beskedafstand", + "description": "Luft omkring beskedens indhold i læseren", + "auto": "Automatisk (anbefalet)", + "always": "Tilføj altid afstand", + "edge": "Kant til kant" + }, + "plain_text_font": { + "label": "Skrifttype til ren tekst", + "description": "Skrifttype til e-mails i ren tekst", + "mono": "Monospace", + "sans": "Normal (samme som appen)" + }, + "archive_mode": { + "label": "Arkivér i", + "description": "Hvordan e-mails organiseres ved arkivering", + "single": "En enkelt mappe", + "year": "En mappe pr. år", + "month": "En mappe pr. måned", + "reorganize": "Omorganisér eksisterende arkiv", + "reorganize_success": "{count, plural, =0 {Ingen e-mails at omorganisere} =1 {1 e-mail omorganiseret} other {# e-mails omorganiseret}}", + "reorganize_error": "Kunne ikke omorganisere arkiv" + }, + "permanently_delete_junk": { + "label": "Slet spam permanent", + "description": "Slet e-mails fra spam-mappen permanent i stedet for at flytte dem til papirkurven" + }, + "confirm_bulk_delete": { + "label": "Bekræft før sletning af flere beskeder", + "description": "Spørg før flere valgte beskeder flyttes til papirkurven. Permanent sletning spørger altid." + }, + "mail_layout": { + "label": "Mail-layout", + "description": "Vælg mellem den klassiske opdelte læserude, en Gmail-lignende fokuseret læsestrøm eller en Zimbra-lignende læserude i bunden.", + "split": "Opdelt rude", + "split_description": "Hold beskedlisten og læseruden synlige side om side.", + "focus": "Fokuseret liste", + "focus_description": "Vis én linje pr. besked og åbn mail i fuld bredde, mens mappesidepanelet forbliver synligt.", + "horizontal": "Læserude i bunden", + "horizontal_description": "Vis beskedlisten øverst og åbn den valgte besked i en læserude nedenunder." + }, + "list_columns": { + "label": "Vis listen som kolonner", + "description": "Vis enkeltlinjerækker som en tabel med kolonnerne Fra, Emne, Dato og Størrelse under en overskrift." + }, + "show_preview": { + "label": "Vis forhåndsvisningstekst", + "description": "Vis e-mail-forhåndsvisning i listen", + "focus_description": "Vis indlejret forhåndsvisningstekst inde i den fokuserede én-linjers beskedliste" + }, + "show_verification_codes": { + "label": "Vis bekræftelseskoder", + "description": "Tilbyd koden fra login- og bekræftelsesmails til kopiering med ét klik, i listen i en dag og i beskeden" + }, + "disable_threading": { + "label": "Deaktivér samtale-gruppering", + "description": "Vis e-mails som individuelle beskeder i stedet for grupperet efter samtale" + }, + "plain_text_mode": { + "label": "Kun almindelig tekst", + "description": "Deaktivér den rige teksteditor og send alle e-mails som ren tekst, inklusive svar og videresendelser" + }, + "auto_select_reply_identity": { + "label": "Svar fra modtaget adresse", + "description": "Når du svarer, send fra den adresse som beskeden oprindeligt blev sendt til. Matcher først identiteter; for domæne catch-all-leveringer omskriver Fra-headeren til aliaset, mens der sendes gennem din primære identitet." + }, + "reply_identity_match": { + "label": "Matchning af modtageradresse", + "description": "Hvilke modtageradresser der tæller som dine. „Kun præcis adresse“ vælger udelukkende en af dine opsatte identiteter; „samme domæne“ behandler desuden enhver anden adresse på dine identitetsdomæner som et catch-all-alias og omskriver Fra-headeren til den. Vælg præcis adresse, hvis disse adresser er distributionslister.", + "exact": "Kun præcis adresse", + "domain": "Enhver adresse på mine domæner" + }, + "signature_position": { + "label": "Signaturplacering", + "description": "Hvor din signatur indsættes i svar og videresendelser. Før citeret tekst læses naturligt som en afslutning på svaret; under bevarer den oprindelige besked sammenhængende.", + "above_quote": "Før citeret tekst", + "below_quote": "Efter citeret tekst" + }, + "signature_separator": { + "label": "Signatur-afgrænser", + "description": "Præfiksér signaturen med standard \"-- \"-afgrænserlinjen (RFC 3676). Slå fra, hvis du hellere vil gå direkte fra din besked til signaturen." + }, + "sub_address_delimiter": { + "label": "Underadresse-afgrænser", + "description": "Tegn der adskiller dit brugernavn fra et underadresse-tag. Match afgrænseren som din mailserver bruger (f.eks. bruger{delimiter}tag@domæne.dk).", + "option": "{delimiter} (bruger{delimiter}tag@domæne.dk)", + "custom": "Brugerdefineret…", + "custom_input_label": "Brugerdefineret afgrænsertegn" + }, + "attachment_click_action": { + "label": "Klik-handling for vedhæftninger", + "description": "Vælg om et klik på en vedhæftet fil forhåndsviser den eller downloader den med det samme", + "preview": "Forhåndsvis når muligt", + "download": "Download med det samme" + }, + "attachment_position": { + "label": "Placering af vedhæftninger", + "description": "Hvor vedhæftninger skal vises i e-mail-headeren", + "beside-sender": "Ved siden af afsender", + "below-header": "Under header" + }, + "emails_per_page": { + "10": "10 e-mails", + "25": "25 e-mails", + "50": "50 e-mails", + "100": "100 e-mails", + "label": "E-mails pr. side", + "description": "Antal e-mails der indlæses ad gangen" + }, + "always_light_mode": { + "label": "Vis altid e-mails i lys tilstand", + "description": "Vis e-mail-indhold i lys tilstand, selv når appen er i mørk tilstand, for at undgå problemer med mørk tilstand-konvertering" + }, + "external_content": { + "label": "Eksternt indhold", + "description": "Hvordan billeder og eksternt indhold håndteres", + "ask": "Spørg altid", + "block": "Bloker altid", + "allow": "Tillad altid" + }, + "trusted_senders": { + "label": "Betroede afsendere", + "description": "Administrér afsendere hvis billeder indlæses automatisk", + "count_zero": "Ingen", + "count_one": "1 afsender", + "count_other": "{count} afsendere", + "modal_title": "Betroede afsendere", + "empty_title": "Ingen betroede afsendere endnu", + "empty_description": "Når du ser en e-mail med blokerede billeder, klik på \"Stol altid på denne afsender\" for at tilføje dem her.", + "add_manually": "Tilføj afsender manuelt", + "add_button": "Tilføj", + "add_placeholder": "Indtast e-mailadresse", + "search_placeholder": "Søg efter afsendere...", + "no_results": "Ingen afsendere matcher din søgning", + "remove": "Fjern", + "close": "Luk", + "invalid_email": "Indtast en gyldig e-mailadresse", + "already_added": "Denne afsender er allerede betroet", + "save_error": "Kunne ikke gemme - tjek Kontakter-debugloggen for detaljer", + "use_address_book_label": "Synkronisér med adressebog", + "use_address_book_description": "Gem betroede afsendere i en dedikeret \"Betroede afsendere\"-adressebog, så de synkroniseres på tværs af alle dine enheder" + }, + "hover_actions": { + "label": "Hurtige hover-handlinger", + "description": "Vælg hvilke hurtige handlinger der vises, når du holder musen over en e-mail i listen", + "delete": "Slet", + "star": "Stjernemarkér / Fjern stjerne", + "mark_read": "Markér læst/ulæst", + "archive": "Arkivér", + "tag": "Tag", + "spam": "Markér som spam", + "not_spam": "Ikke spam", + "none_selected": "Ingen handlinger valgt", + "mode_label": "Visningstilstand", + "mode_inline": "Indlejret", + "mode_floating": "Flydende", + "corner_label": "Flydende position", + "corner_top-left": "Øverst til venstre", + "corner_top-right": "Øverst til højre", + "corner_bottom-left": "Nederst til venstre", + "corner_bottom-right": "Nederst til højre" + }, + "default_mail_program": { + "label": "Standard mailprogram", + "description": "Registrér {appName} som dit standard mailprogram for mailto:-links", + "button": "Angiv som standard", + "success": "Browseren blev bedt om at angive som standard", + "error": "Din browser understøtter ikke denne funktion" + }, + "attachment_reminder": { + "label": "Påmindelse om vedhæftning", + "description": "Advar før afsendelse, når din besked nævner vedhæftninger, men ingen er vedhæftet", + "keywords_label": "Udløsende nøgleord", + "keywords_description": "Ord eller sætninger der udløser påmindelsen, når de findes i din besked", + "add_placeholder": "Tilføj nøgleord...", + "add": "Tilføj", + "remove": "Fjern" + }, + "empty_subject_warning": { + "label": "Advarsel om tomt emne", + "description": "Bed om bekræftelse, før en besked uden emne sendes." + }, + "recipient_mentions": { + "label": "Nævn modtagere med @", + "description": "Når du skriver @ i beskeden, foreslås modtagerne, og den valgtes fornavn indsættes. Enter eller Tab vælger det fremhævede navn." + }, + "mention_adds_recipient": { + "label": "Kontakter i @-omtaler", + "description": "Når du skriver @, foreslås også dine kontakter og personer fra adressebogen. Vælger du en, der endnu ikke er modtager, tilføjes vedkommende til beskeden.", + "to": "Tilføj til Til", + "cc": "Tilføj til CC", + "off": "Foreslå kun modtagere" + }, + "hide_inline_image_attachments": { + "label": "Skjul indlejrede billeder fra vedhæftninger", + "description": "Billeder indlejret i beskedteksten vises ikke som separate vedhæftninger" + }, + "attachment_image_previews": { + "label": "Vis billedforhåndsvisninger i vedhæftninger", + "description": "Vis billedvedhæftninger som miniaturekort i stedet for generiske filikoner" + }, + "send_delay": { + "label": "Fortryd afsendelse / sendeforsinkelse", + "description": "Forsink normale afsendelser med et kort serverstyret tidsrum.", + "off": "Fra", + "seconds": "{seconds} sekunder", + "unsupported": "Den aktuelle konto annoncerer ikke understøttelse af forsinket afsendelse. Indstillingen gemmes stadig for andre konti." + }, + "return_to_list_after_action": { + "label": "Vend tilbage til listen efter sletning eller markering som ulæst", + "description": "Efter sletning eller markering af den åbne besked som ulæst vendes der tilbage til beskedlisten i stedet for at åbne den næste besked." + }, + "swipe_right_action": { + "label": "Handling ved strygning til højre (mobil)", + "description": "På touch-enheder den handling, der udføres, når du stryger en besked til højre i listen." + }, + "swipe_left_action": { + "label": "Handling ved strygning til venstre (mobil)", + "description": "På touch-enheder den handling, der udføres, når du stryger en besked til venstre i listen." + }, + "swipe_actions": { + "none": "Ingen", + "archive": "Arkivér", + "delete": "Slet", + "mark_read": "Skift læst/ulæst", + "star": "Skift stjerne", + "spam": "Markér som spam" + }, + "rtl_editing": { + "label": "Understøttelse af højre-mod-venstre-redigering", + "description": "Tilføjer en retningsknap til værktøjslinjen i editoren, så afsnit kan sættes venstre-mod-højre eller højre-mod-venstre" + } + }, + "composer": { + "title": "Skrivevindue", + "description": "Konfigurér indstillinger for e-mail-komposition", + "autosave": { + "label": "Auto-gem interval", + "description": "Hvor ofte kladder gemmes automatisk", + "30s": "Hvert 30. sekund", + "1m": "Hvert minut", + "2m": "Hvert 2. minut", + "5m": "Hvert 5. minut" + }, + "send_confirmation": { + "label": "Send-bekræftelse", + "description": "Spørg om bekræftelse før afsendelse af e-mails" + }, + "default_reply": { + "label": "Standard svar-tilstand", + "description": "Standardhandling ved klik på svar", + "reply": "Svar", + "reply_all": "Svar alle" + } + }, + "privacy": { + "title": "Privatliv & sikkerhed", + "description": "Administrér dine privatlivs- og sikkerhedsindstillinger", + "external_images": { + "label": "Bloker eksterne billeder", + "description": "Forhindr sporing via eksterne billeder" + }, + "session_timeout": { + "label": "Sessionstimeout", + "description": "Log automatisk ud efter inaktivitet", + "never": "Aldrig", + "30m": "30 minutter", + "1h": "1 time", + "4h": "4 timer" + }, + "clear_cache": { + "label": "Ryd cache", + "description": "Fjern cached data og midlertidige filer", + "button": "Ryd cache", + "confirm": "Er du sikker på, at du vil rydde cachen?", + "success": "Cache ryddet succesfuldt" + } + }, + "account": { + "title": "Konto", + "description": "Se dine kontooplysninger", + "name_label": "Visningsnavn", + "username_label": "Brugernavn", + "account_type_label": "Kontotype", + "auth_method_label": "Godkendelse", + "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", + "auth_method_basic": "Adgangskode", + "auth_method_token": "Adgangstoken", + "demo_account": "Demo-konto", + "email": { + "label": "E-mailadresse", + "value": "{email}" + }, + "server": { + "label": "JMAP-server", + "value": "{server}" + }, + "storage": { + "label": "Lagerforbrug", + "used": "{used} af {total} brugt", + "percentage": "{percent}% brugt" + }, + "last_sync": { + "label": "Sidste synkronisering", + "value": "{time}" + }, + "accounts": { + "title": "Indloggede konti", + "description": "Træk for at ændre rækkefølgen af konti i kontomenuen", + "active": "Aktuelt aktiv konto", + "default_badge": "Standardkonto", + "set_default": "Indstil som standard", + "switch_to": "Skift til denne konto", + "move_up": "Flyt op", + "move_down": "Flyt ned", + "drag_handle": "Træk for at omarrangere", + "add": "Tilføj konto" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Delt med mig", + "description": "Gruppe- og delte konti, du kan administrere. Vælg en for at redigere dens filtre, autosvar ved fravær, kalendere og kontakter.", + "shared_label": "Delt konto" + } + }, + "security": { + "title": "Kontosikkerhed", + "description": "Administrér din adgangskode, to-faktor-godkendelse og sikkerhedsindstillinger", + "detecting": "Registrerer serverfunktioner...", + "not_available": "Kontosikkerhedsstyring er ikke tilgængelig for denne mailserver. Nødvendige tilladelser er muligvis deaktiveret. Se dokumentationen for detaljer.", + "password": { + "title": "Skift adgangskode", + "current": "Nuværende adgangskode", + "new": "Ny adgangskode", + "confirm": "Bekræft ny adgangskode", + "submit": "Skift adgangskode", + "success": "Adgangskode ændret succesfuldt", + "error_title": "Adgangskodeændring mislykkedes", + "error_mismatch": "De nye adgangskoder er ikke ens", + "error_min_length": "Adgangskoden skal være mindst 8 tegn", + "error_generic": "Kunne ikke ændre adgangskode" + }, + "display_name": { + "label": "Visningsnavn", + "description": "Dit navn som det vises på serveren", + "placeholder": "Indtast dit visningsnavn", + "save": "Gem", + "success": "Visningsnavn opdateret", + "error": "Kunne ikke opdatere visningsnavn" + }, + "totp": { + "section_title": "To-faktor-godkendelse", + "label": "TOTP-godkendelse", + "description": "Tilføj et ekstra lag af sikkerhed med en tidsbaseret engangskode", + "active": "Aktiveret", + "inactive": "Deaktiveret", + "enabled": "To-faktor-godkendelse aktiveret", + "disabled": "To-faktor-godkendelse deaktiveret", + "enable_error": "Kunne ikke aktivere 2FA", + "disable_error": "Kunne ikke deaktivere 2FA", + "setup_instructions": "Kopier denne URL ind i din autentificeringsapp (Google Authenticator, Authy osv.):", + "verification_code": "Bekræftelseskode", + "confirm": "Bekræft", + "disable": "Deaktivér", + "disable_confirm_prompt": "Indtast din adgangskode for at deaktivere to-faktor-godkendelse.", + "password_required": "Adgangskode er påkrævet", + "code_required": "Bekræftelseskode er påkrævet", + "code_invalid": "Ugyldig bekræftelseskode. Kontrollér din autentificeringsapp og prøv igen." + }, + "app_passwords": { + "title": "App-adgangskoder", + "description": "Opret adgangskoder til apps, der ikke understøtter to-faktor-godkendelse", + "add": "Tilføj", + "create": "Opret", + "cancel": "Annuller", + "done": "Færdig", + "generate": "Generér", + "name_label": "App-navn", + "name_placeholder": "f.eks. Thunderbird, iPhone Mail", + "expires_label": "Udløber (valgfrit)", + "allowed_ips_label": "Tilladte IP'er (valgfrit)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Komma- eller mellemrumssepareret. Lad være tom for at tillade alle IP'er.", + "password_label": "Adgangskode (lad være tom for auto-generering)", + "password_placeholder": "Auto-genereret hvis tom", + "copy_now_warning": "Kopier denne adgangskode nu - den vil ikke blive vist igen.", + "added": "App-adgangskode oprettet", + "removed": "App-adgangskode fjernet", + "add_error": "Kunne ikke oprette app-adgangskode", + "remove_error": "Kunne ikke fjerne app-adgangskode", + "none": "Ingen app-adgangskoder konfigureret" + }, + "api_keys": { + "title": "API-nøgler", + "description": "Opret API-nøgler til scripts og integrationer, der taler direkte med serveren", + "name_label": "Nøglenavn", + "name_placeholder": "f.eks. Backup-script, CI-runner", + "copy_now_warning": "Kopier denne API-nøgle nu - den vil ikke blive vist igen.", + "added": "API-nøgle oprettet", + "removed": "API-nøgle fjernet", + "add_error": "Kunne ikke oprette API-nøgle", + "remove_error": "Kunne ikke fjerne API-nøgle", + "none": "Ingen API-nøgler konfigureret" + }, + "encryption": { + "section_title": "Kryptering af lagrede data", + "label": "E-mail-kryptering", + "description": "Krypter lagrede e-mails på serveren for ekstra privatliv", + "active": "{type}-kryptering aktiveret", + "inactive": "Deaktiveret", + "enabled": "Kryptering af lagrede data aktiveret", + "disabled_success": "Kryptering af lagrede data deaktiveret", + "error": "Kunne ikke opdatere krypteringsindstillinger", + "algorithm_label": "Krypteringsalgoritme", + "encrypt_on_append": "Krypter nye e-mails ved upload", + "allow_spam_training": "Tillad spamtræning, før e-mails krypteres", + "enable_encryption": "Aktivér kryptering", + "enable_tooltip": "Aktivér kryptering med denne nøgle", + "configure_title": "Aktivér kryptering med denne nøgle", + "enable_button": "Aktivér", + "disable_tooltip": "Deaktivér kryptering", + "enabled_success": "Kryptering af lagrede data aktiveret" + }, + "email_client": { + "title": "E-mail-klientopsætning", + "description": "Brug disse legitimationsoplysninger til at konfigurere din desktop- eller mobil-e-mailklient (Thunderbird, Apple Mail, Outlook osv.)", + "jmap_username_label": "JMAP-brugernavn", + "copy": "Kopier", + "copied": "Kopieret", + "password_instructions": "Brug dit JMAP-brugernavn ovenfor sammen med en app-adgangskode for at logge ind i din e-mailklient. Opret en app-adgangskode i sektionen ovenfor, hvis du ikke allerede har gjort det." + }, + "link_device": { + "title": "Tilknyt mobilapp", + "description": "Log ind i Bulwark Mail-appen på din telefon uden at skrive din adgangskode der. Din telefon får sit eget login, så den forbliver logget ind, når du logger ud her.", + "generate": "Vis QR-kode", + "regenerate": "Vis en ny kode", + "instructions": "Åbn Bulwark Mail-appen, og tryk på \"Scan a sign-in code\" på loginskærmen (eller \"Add account\" → \"Scan a sign-in code\", hvis du allerede er logget ind), og ret derefter kameraet herhen.", + "expires_in": "Denne kode udløber om {seconds} sekunder. Den kan kun bruges én gang.", + "expired": "Denne kode er udløbet.", + "error": "Kunne ikke oprette en parringskode. Prøv igen.", + "password_prompt": "Indtast din adgangskode for at bekræfte, at det er dig. Din telefon får sit eget login; din adgangskode sendes ikke til telefonen.", + "show_password": "Vis adgangskode", + "hide_password": "Skjul adgangskode", + "continue": "Fortsæt", + "error_wrong_password": "Forkert adgangskode. Prøv igen.", + "totp_prompt": "Indtast koden fra din godkendelsesapp.", + "error_password_or_code": "Adgangskoden eller koden er forkert.", + "error_not_signed_in": "Din session er udløbet. Log ind igen for at tilknytte en enhed.", + "error_impersonation": "Enheder kan ikke tilknyttes, mens du ser en anden brugers postkasse.", + "error_too_many_attempts": "For mange forsøg. Vent et øjeblik, og prøv igen.", + "error_session_secret": "Tilknytning af enheder er ikke sat op på denne server (SESSION_SECRET mangler). Kontakt din administrator.", + "error_server_unreachable": "Kunne ikke få forbindelse til din mailserver. Prøv igen.", + "error_pairing_unavailable": "Din mailserver tillader ikke tilknytning af enheder på denne måde. Kontakt din administrator.", + "qr_alt": "QR-kode til login", + "link_label": "Loginlink", + "copy_link": "Kopiér loginlink", + "copied": "Kopieret", + "copy_hint": "Intet kamera? Kopiér linket, og indsæt det i appen med \"Paste sign-in link\".", + "success_title": "Logget ind på din telefon", + "success_body": "Du kan lukke denne side. Vis en ny kode for at tilknytte en anden enhed.", + "link_another": "Tilknyt en anden enhed", + "use_sso": "Brug single sign-on i stedet", + "error_insecure_server": "Mobilappen forbinder kun til mailservere via https, og denne bruger http." + }, + "public_keys": { + "name_label": "Navn på offentlig nøgle", + "name_placeholder": "f.eks. Min nøgle", + "key_label": "Offentlig nøgle (S/MIME eller PGP i ASCII-format)", + "key_placeholder": "Indsæt din offentlige nøgle her", + "none": "Ingen offentlige nøgler konfigureret" + } + }, + "identities": { + "title": "Afsenderidentiteter", + "description": "Administrér e-mailadresser du kan sende fra", + "identities_count": { + "label": "Dine identiteter", + "description": "E-mailadresser konfigureret til afsendelse", + "count_zero": "Ingen identiteter", + "count_one": "1 identitet", + "count_other": "{count} identiteter" + }, + "manage": "Administrér identiteter", + "sub_addressing": { + "label": "Underadressering", + "description": "Brug tags som bruger+tag@domæne.dk til at organisere indkommende mail", + "learn_more": "Lær mere" + } + }, + "vacation": { + "title": "Fravær", + "description": "Håndter indkommende e-mails automatisk, mens du er væk", + "loading": "Indlæser ferieindstillinger...", + "not_supported": "Din mailserver understøtter ikke fraværsbeskeder.", + "fetch_error": "Kunne ikke indlæse ferieindstillinger. Prøv igen.", + "status": { + "label": "Svar automatisk", + "description": "Send et automatisk svar til personer, der skriver til dig", + "active": "Aktiv", + "inactive": "Inaktiv" + }, + "date_range": { + "start": "Startdato", + "start_description": "Lad være tom for ingen startbegrænsning", + "end": "Slutdato", + "end_description": "Lad være tom for ingen slutbegrænsning" + }, + "message": { + "subject_label": "Emne", + "subject_description": "Emnelinje for auto-svaret", + "subject_placeholder": "Fraværende", + "body_label": "Beskedtekst", + "body_description": "Ren tekst-beskedindhold", + "body_placeholder": "Tak for din e-mail. Jeg er i øjeblikket fraværende og vil svare, når jeg vender tilbage.", + "html_label": "Formateret besked (HTML)", + "html_description": "Tilføj en formateret version med links og styling. Modtagere, hvis mailprogram ikke kan vise den, får den almindelige tekst ovenfor.", + "html_placeholder": "Skriv et formateret fraværssvar…" + }, + "preview": { + "title": "Forhåndsvisning", + "show": "Vis forhåndsvisning", + "hide": "Skjul forhåndsvisning" + }, + "save": "Gem ændringer", + "saving": "Gemmer...", + "warnings": { + "end_before_start": "Slutdato skal være efter startdato", + "invalid_date": "Angiv en gyldig start- og slutdato med klokkeslæt, eller lad feltet stå tomt", + "start_in_past": "Startdato er tidligere end i dag", + "empty_body": "Beskedteksten er tom - modtagere vil modtage et tomt svar", + "subject_too_long": "Emnet er for langt til serveren (højst {max} byte).", + "body_too_long": "Beskeden er for lang til serveren (højst {max} byte inkl. formatering).", + "forward_address": "Angiv en gyldig adresse at videresende til", + "not_running": "Videresendelse eller svarmodtagere er gemt, men er ikke aktive lige nu. Gem for at slå dem til igen." + }, + "forward": { + "title": "Videresendelse", + "description": "Videresender indgående beskeder til en anden adresse, med eller uden fraværsbesked, i perioden ovenfor (uden slutdato, indtil den slås fra). Spam videresendes ikke.", + "enabled_label": "Videresend beskeder", + "to_label": "Videresend til", + "to_placeholder": "email@eksempel.dk", + "keep_label": "Behold i indbakke", + "keep_description": "Ellers bliver beskeden kun videresendt: Den bliver ikke i denne postkasse, og ingen filterregel køres på den." + }, + "audience": { + "label": "Svar til", + "description": "Interne afsendere: {domains}", + "all": "Alle afsendere", + "internal": "Kun interne afsendere", + "external": "Kun eksterne afsendere" + }, + "auto_reply": { + "title": "Automatisk svar" + } + }, + "folders": { + "role_memos": "Noter", + "title": "Mapper", + "description": "Administrér dine e-mail-mapper og tildel standardroller", + "folder_list": "Dine mapper", + "folder_list_description": "Klik på et mappeikon for at tilpasse det", + "standard_roles": "Standard mapproller", + "standard_roles_description": "Tildel hvilke mapper der bruges til standard postkasseroller som Indbakke, Sendt, Papirkurv osv.", + "role_inbox": "Indbakke", + "role_drafts": "Kladder", + "role_sent": "Sendt", + "role_trash": "Papirkurv", + "role_junk": "Spam", + "role_archive": "Arkiv", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Ingen", + "create_folder": "Opret mappe", + "create_subfolder": "Opret undermappe", + "reorder": "Træk for at ændre rækkefølgen", + "reorder_error": "Kunne ikke ændre rækkefølgen af mapper", + "move_error": "Kunne ikke flytte mappen", + "select_folder": "Vælg {name}", + "select_hint": "Vælg. Shift+klik vælger alle mapper op til den sidste, du klikkede på.", + "selected_count": "{count, plural, one {# mappe} other {# mapper}} valgt", + "move_to": "Flyt til...", + "move_to_top_level": "Øverste niveau", + "move": "Flyt", + "clear_selection": "Ryd markering", + "folders_moved": "{count, plural, one {# mappe flyttet} other {# mapper flyttet}}", + "move_partial_error": "{count, plural, one {# mappe} other {# mapper}} kunne ikke flyttes", + "subfolder_of": "Inde i {name}", + "subfolder_name": "Undermappenavn", + "new_folder_name": "Mappenavn", + "rename": "Omdøb", + "change_icon": "Skift ikon", + "count_mode": "Tæller for {name}", + "count_mode_default": "Tæller: standard", + "count_mode_unread": "Kun ulæste", + "count_mode_all": "Ulæste og i alt", + "count_mode_none": "Ingen tæller", + "delete": "Slet", + "confirm_delete": "Er du sikker på, at du vil slette \"{name}\"? E-mails i denne mappe flyttes til papirkurven.", + "create": "Opret", + "cancel": "Annuller", + "no_folders": "Ingen brugerdefinerede mapper", + "cannot_delete_role": "Kan ikke slette en mappe med en standardrolle. Fjern rollen først.", + "folder_created": "Mappe oprettet", + "folder_renamed": "Mappe omdøbt", + "folder_deleted": "Mappe slettet", + "role_updated": "Mappens rolle opdateret", + "error_create": "Kunne ikke oprette mappe", + "error_rename": "Kunne ikke omdøbe mappe", + "error_delete": "Kunne ikke slette mappe", + "error_delete_has_children": "Kan ikke slette mappe: den indeholder stadig undermapper. Slet eller flyt dem først.", + "error_delete_has_email": "Kan ikke slette mappe: den indeholder stadig e-mails. Flyt eller slet dem først.", + "error_role": "Kunne ikke opdatere mappens rolle" + }, + "advanced": { + "title": "Avanceret", + "description": "Avancerede indstillinger og udviklerindstillinger", + "debug_mode": { + "label": "Debug-tilstand", + "description": "Aktivér detaljeret logning til fejlfinding" + }, + "debug_categories": { + "description": "Vælg hvilke kategorier der skal logges. Deaktivér kategorier du ikke har brug for at reducere konsolstøj.", + "jmap": "JMAP-klient", + "jmap_description": "Mappeoperationer, e-mail-hentning og JMAP-protokolforespørgsler", + "calendar": "Kalender", + "calendar_description": "Kalenderbegivenheder, import og planlægningsbeskeder", + "tasks": "Opgaver", + "tasks_description": "Kalenderopgaveoprettelse, -hentning og -opdateringer", + "auth": "Godkendelse", + "auth_description": "Login, TOTP, token-udveksling og sessionsstyring", + "filters": "Filtre", + "filters_description": "Sieve-filterregler og feriescripts", + "email": "E-mail-visning", + "email_description": "E-mail-visning, TNEF-behandling og markér-som-læst", + "push": "Push-notifikationer", + "push_description": "Push-notifikationsopsætning og -levering", + "contacts": "Kontakter & adressebøger", + "contacts_description": "Kontaktsynkronisering, adressebogsoperationer og betroede afsendere" + }, + "settings_sync": { + "label": "Indstillingssynkronisering", + "description": "Synkronisér dine indstillinger på tværs af browsere og enheder" + }, + "sender_favicons": { + "label": "Afsender-favicons", + "description": "Vis hjemmesideikoner som profilbilleder for forretningsafsendere", + "refresh_label": "Opdater afsenderikoner", + "refresh_description": "Hent webstedsikonerne igen, f.eks. når en afsender har skiftet sit ikon", + "refresh_button": "Opdater", + "refresh_done": "Afsenderikonerne hentes igen" + }, + "show_avatars_in_junk": { + "label": "Vis avatarer i spam-mappe", + "description": "Vis profilbilleder og favicons for afsendere i spam-mappen. Deaktiveret som standard for at undgå at give visuel legitimitet til phishing-forsøg." + }, + "keyboard_shortcuts": { + "label": "Tastaturgenveje", + "description": "Se tilgængelige tastaturgenveje", + "button": "Vis genveje" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Nulstil indstillinger", + "description": "Gendan alle indstillinger til standardværdier", + "button": "Nulstil til standard" + }, + "export_settings": { + "label": "Eksportér indstillinger", + "description": "Download dine indstillinger som JSON", + "button": "Eksportér" + }, + "import_settings": { + "label": "Importér indstillinger", + "description": "Upload indstillinger fra JSON-fil", + "button": "Importér" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Kildekode" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Klik på spam og phishing, før de når din indbakke. Lad ægte mails komme igennem. Mister du alle tre skjolde, falder indbakken.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Ægte mail" + }, + "click_to_block": "Klik for at blokere", + "let_through": "Lad den komme igennem", + "start": "Start", + "play_again": "Spil igen", + "score": "Point", + "combo": "Stribebonus", + "shields_left": "{count} af {total} skjolde tilbage", + "seconds": "{seconds} s", + "best": "Rekord: {score}", + "new_best": "Ny rekord", + "held": "Indbakken holdt", + "overrun": "Indbakken blev overrendt", + "stats": { + "caught": "Blokeret", + "delivered": "Leveret", + "mistakes": "Fejl" + } + } + }, + "sidebar_apps": { + "title": "Sidepanel-apps", + "description": "Administrér brugerdefinerede apps og links i dit sidepanel", + "keep_loaded": "Hold apps indlæst", + "keep_loaded_description": "Hold indlejrede apps kørende i baggrunden, når du skifter mellem dem, for at undgå genindlæsning", + "managed_title": "Leveret af din administrator", + "managed_description": "Disse apps er sat op for alle og kan ikke ændres.", + "manage_title": "Brugerdefinerede apps", + "manage_description": "Tilføj, redigér eller fjern brugerdefinerede apps fra dit sidepanel" + }, + "contacts": { + "title": "Kontakter", + "description": "Importér og eksportér dine kontakter", + "group_by_letter_label": "Gruppér efter første bogstav", + "sort_by_last_name_label": "Sortér efter efternavn", + "sort_by_last_name_description": "Sortér kontaktlisten efter efternavn, så familiemedlemmer står samlet", + "group_by_letter_description": "Vis alfabetiske sektionsoverskrifter i kontaktlisten", + "import_label": "Importér kontakter", + "import_description": "Importér kontakter fra en vCard-fil (.vcf)", + "export_label": "Eksportér kontakter", + "export_description": "Eksportér alle kontakter som en vCard-fil (.vcf)", + "manage_title": "Adressebøger", + "manage_description": "Omdøb dine adressebøger", + "no_address_books": "Ingen adressebøger fundet", + "categories_title": "Kategorier", + "categories_description": "Omdøb kontaktkategorier", + "no_categories": "Ingen kategorier fundet" + }, + "downloads": { + "title": "Downloads", + "description": "Tilpas hvordan downloadede e-mails og vedhæftede filer navngives.", + "reset": "Gendan standard", + "preview": "Forhåndsvisning:", + "email_template": { + "label": "E-mail-filnavn (.eml)", + "description": "Skabelon som bruges når du eksporterer en e-mail eller trækker den ud i filsystemet. Endelsen .eml tilføjes automatisk." + }, + "attachment_template": { + "label": "Filnavn på vedhæftet fil", + "description": "Skabelon som bruges når du downloader eller trækker en vedhæftet fil ud. Hvis du udelader '{filename}' og '{ext}', bevares den oprindelige endelse." + }, + "bundle_template": { + "label": "Filnavn for .zip med flere e-mails", + "description": "Skabelon som bruges når du trækker ud eller downloader flere valgte e-mails som ét .zip-arkiv. Endelsen .zip tilføjes automatisk." + }, + "spaces": { + "label": "Mellemrum", + "description": "Erstat mellemrum i det endelige filnavn med et andet tegn.", + "keep": "Behold mellemrum", + "underscore": "Erstat med _", + "dash": "Erstat med -" + }, + "lowercase": { + "label": "Små bogstaver", + "description": "Tving hele filnavnet til små bogstaver." + }, + "strip_diacritics": { + "label": "Fjern diakritiske tegn", + "description": "Konvertér bogstaver med accenter til deres ASCII-pendant (ä → a, é → e). Nyttigt for værktøjer, der ikke håndterer Unicode-navne korrekt." + }, + "collapse_separators": { + "label": "Slå gentagne skilletegn sammen", + "description": "Slå serier af mellemrum, understreger eller bindestreger sammen til ét tegn." + }, + "after_export": { + "label": "Efter eksport", + "description": "Flyt eventuelt e-mailen efter den er eksporteret som .eml.", + "keep": "Behold i postkassen", + "archive": "Flyt til arkiv", + "trash": "Flyt til papirkurv" + } + }, + "filters": { + "title": "E-mail-filtre", + "description": "Opret regler til automatisk at sortere, mærke og administrere indkommende e-mails", + "add_rule": "Tilføj regel", + "no_rules": "Ingen filterregler", + "no_rules_description": "Opret regler for automatisk at organisere dine indkommende e-mails", + "vacation_active": "Fraværsbeskeder er aktive", + "vacation_active_description": "Auto-svar er aktiveret for indkommende beskeder", + "vacation_configure": "Konfigurér", + "edit_rule": "Redigér regel", + "new_rule": "Ny regel", + "delete_rule": "Slet regel", + "delete_confirm": "Er du sikker på, at du vil slette denne regel?", + "enable": "Aktivér", + "disable": "Deaktivér", + "raw_editor": "Raw Sieve-editor", + "raw_editor_warning": "Redigering af det rå Sieve-script kan ødelægge visuel regelledigering. Ændringer foretaget her tilsidesætter den visuelle builder.", + "validate": "Valider", + "validation_success": "Scriptet er gyldigt", + "validation_error": "Scriptet har fejl", + "save": "Gem regler", + "saving": "Gemmer...", + "saved": "Filtre gemt succesfuldt", + "save_failed": "Kunne ikke gemme filtre", + "loading": "Indlæser filtre...", + "not_supported": "Din mailserver understøtter ikke e-mail-filtre.", + "rule_name": "Regelnavn", + "rule_name_placeholder": "f.eks. Sorter nyhedsbreve", + "match_all": "Match ALLE betingelser", + "match_any": "Match en af betingelserne", + "conditions": "Betingelser", + "add_condition": "Tilføj betingelse", + "actions": "Handlinger", + "add_action": "Tilføj handling", + "stop_processing": "Stop behandling af efterfølgende regler", + "include_spam": "Flyt også beskeder, der er markeret som spam", + "attachment_type_placeholder": "f.eks. pdf, doc, jpg", + "value_placeholder_multi": "Værdi (flere adskilt med komma)", + "condition_fields": { + "attachment": "Vedhæftet fil", + "from": "Fra", + "to": "Til", + "cc": "Cc", + "subject": "Emne", + "header": "Brugerdefineret header", + "size": "Størrelse", + "body": "Brødtekst", + "all": "Alle beskeder" + }, + "comparators": { + "has_any": "er til stede", + "has_type": "af typen", + "contains": "indeholder", + "not_contains": "indeholder ikke", + "is": "er præcis", + "not_is": "er ikke", + "starts_with": "begynder med", + "ends_with": "slutter med", + "matches": "matcher mønster", + "greater_than": "er større end", + "less_than": "er mindre end", + "address_is": "er adressen", + "domain_is": "har domænet" + }, + "action_types": { + "move": "Flyt til mappe", + "copy": "Kopier til mappe", + "forward": "Videresend til", + "mark_read": "Markér som læst", + "star": "Stjernemarkér besked", + "add_label": "Tilføj tag", + "discard": "Kassér (slet stille)", + "reject": "Afvis med besked", + "keep": "Behold i indbakke", + "stop": "Stop behandling" + }, + "move_to_folder": "Vælg mappe", + "copy_to_folder": "Vælg mappe", + "forward_to": "Videresend til e-mailadresse", + "forward_placeholder": "email@eksempel.dk", + "forward_keep_copy": "Behold en kopi", + "forward_limit": "Grænse for videresendelser pr. besked på denne server: {count}. Yderligere videresendelser springes over.", + "forward_unavailable": "Denne server tillader ikke videresendelse. Videresendelseshandlinger i filterregler har ingen effekt.", + "reject_message": "Afvisningsbesked", + "reject_placeholder": "Din e-mail er blevet afvist", + "label_name": "Tag-navn", + "label_placeholder": "Vælg tag", + "header_name": "Headernavn", + "header_placeholder": "f.eks. X-Mailing-List", + "size_bytes": "Størrelse i bytes", + "size_placeholder": "f.eks. 1000000", + "system_managed": "Systemadministreret regel", + "opaque_warning": "Dette script blev redigeret uden for den visuelle builder. Kun raw Sieve-redigering er tilgængelig.", + "open_sieve_editor": "Åbn raw Sieve-editor", + "fetch_error": "Kunne ikke indlæse filtre", + "expanded_view": "Udvidet visning", + "expanded_view_description": "Vis filterregler med detaljerede betingelses- og handlingsblokke", + "if": "Hvis", + "then": "Så", + "match_all_conditions": "alle matcher", + "match_any_condition": "enhver matcher", + "and": "og", + "or": "eller", + "cancel": "Annuller", + "confirm_delete": "Slet", + "rule_list": "Filterregler", + "drag_to_reorder": "Træk for at omorganisere", + "match_type": "Match-type", + "reset_to_visual": "Nulstil til visuel builder", + "reset_warning": "Dette vil kassere det nuværende script og starte forfra.", + "confirm_reset": "Nulstil", + "validation_empty_name": "Regelnavn er påkrævet", + "validation_empty_conditions": "Mindst én betingelse med en værdi er påkrævet", + "validation_empty_actions": "Mindst én handling er påkrævet", + "templates_section": "Start fra skabelon", + "template_newsletters": "Flyt nyhedsbreve til mappe", + "template_receipts": "Auto-arkiver kvitteringer", + "template_important": "Markér vigtige e-mails", + "template_notifications": "Filtrér notifikationer", + "sieve_editor": { + "title": "Sieve-script-editor", + "warning": "Redigering af det rå Sieve-script kan ødelægge visuel regelledigering. Ændringer foretaget her tilsidesætter den visuelle builder.", + "script_content": "Sieve-script", + "valid": "Scriptet er gyldigt", + "invalid": "Scriptet har fejl", + "save_warning": "Lagring vil overskrive eventuelle visuelle regler. Dette kan ikke fortrydes. Klik Gem igen for at bekræfte.", + "validating": "Validerer...", + "validate": "Valider", + "cancel": "Annuller", + "save": "Gem", + "confirm_save": "Bekræft gem", + "validation_failed": "Valideringsanmodning mislykkedes" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# betingelse} other {# betingelser}}", + "actions_count": "{count, plural, one {# handling} other {# handlinger}}" + }, + "origin_external": "Ekstern", + "managed_by_tooltip": "Administreres af {source}. Redigér det i den pågældende app, eller brug raw Sieve-editoren.", + "quick_rule_names": { + "move_sender": "Flyt fra {sender} til {folder}", + "move_domain": "Flyt fra @{domain} til {folder}", + "move_list": "Flyt listen {list} til {folder}", + "mark_read": "Markér fra {sender} som læst", + "tag": "Tag fra {sender} som {tag}", + "block": "Bloker {sender}", + "custom": "Beskeder fra {sender}", + "custom_plain": "Ny regel", + "senders": "{count, plural, one {# afsender} other {# afsendere}}" + }, + "suggestions": "Forslag", + "suggest_subject": "Emne “{subject}”", + "suggest_to": "Til {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Anvend også på beskeder, der allerede ligger i denne mappe", + "apply_existing_unsupported": "Kun regler, der tjekker afsender, modtagere, emne eller headere, og som flytter, kopierer, markerer som læst, stjernemarkerer eller tagger, kan anvendes på eksisterende beskeder.", + "period_title": "Datoperiode", + "period_start": "Startdato", + "period_end": "Slutdato", + "validation_period_order": "Slutdato skal være efter startdato", + "period_toggle": "Kun aktiv i en periode", + "period_hint": "Gælder for beskeder, der ankommer i denne periode. Lad et felt stå tomt for at holde den ende åben.", + "validation_period_empty": "Angiv en start- eller slutdato for perioden", + "validation_period_invalid": "Angiv en gyldig dato med klokkeslæt for perioden, eller lad feltet stå tomt", + "apply_existing_period": "En regel med en periode virker kun på beskeder, der ankommer i perioden, og kan derfor ikke køres på eksisterende beskeder.", + "period_status_scheduled": "planlagt", + "period_status_active": "aktiv", + "period_status_expired": "udløbet" + }, + "templates": { + "title": "E-mail-skabeloner", + "description": "Opret genanvendelige e-mail-skabeloner med pladsholdervariabler", + "add": "Ny skabelon", + "edit": "Redigér skabelon", + "name": "Skabelonnavn", + "name_placeholder": "f.eks. Opfølgning", + "category": "Kategori", + "category_placeholder": "f.eks. Arbejde, Privat", + "subject": "Emne", + "subject_placeholder": "E-mail-emnelinje", + "body": "Brødtekst", + "body_placeholder": "E-mail-tekst...", + "recipients_placeholder": "email@eksempel.dk", + "identity": "Send som", + "default_identity": "Standardidentitet", + "favorite": "Favorit", + "cancel": "Annuller", + "create": "Opret", + "update": "Opdater", + "confirm_delete": "Slet", + "no_templates": "Ingen skabeloner endnu", + "manage": "Administrér skabeloner", + "count": "{count, plural, one {# skabelon} other {# skabeloner}}", + "export_import": "Eksportér & importér", + "export_import_description": "Sikkerhedskopér dine skabeloner eller overfør dem til en anden enhed", + "export": "Eksportér", + "import": "Importér", + "validation": { + "empty": "Skabelonnavn er påkrævet", + "too_long": "Skabelonnavn skal være 200 tegn eller mindre" + } + }, + "files": { + "display": { + "title": "Visning", + "description": "Konfigurér hvordan filer og mapper vises" + }, + "default_view": { + "label": "Standardvisning", + "description": "Vælg mellem gitter- og listelayout", + "list": "Liste", + "grid": "Gitter" + }, + "default_sort": { + "label": "Standardsortering", + "description": "Vælg standardsortering for filer", + "name": "Navn", + "size": "Størrelse", + "modified": "Ændret" + }, + "sort_direction": { + "label": "Sorteringsretning", + "description": "Vælg stigende eller faldende rækkefølge", + "ascending": "Stigende", + "descending": "Faldende" + }, + "icons": { + "title": "Ikoner", + "description": "Konfigurér filikonernes udseende" + }, + "show_icons": { + "label": "Vis filikoner", + "description": "Vis ikoner ved siden af filer og mapper" + }, + "colored_icons": { + "label": "Farvede ikoner", + "description": "Brug farverige ikoner i stedet for monokrome" + }, + "show_thumbnails": { + "label": "Vis miniaturer", + "description": "Vis billedforhåndsvisninger i stedet for ikoner for billedfiler" + }, + "behavior": { + "title": "Adfærd", + "description": "Konfigurér filbrowserens adfærd" + }, + "show_hidden": { + "label": "Vis skjulte filer", + "description": "Vis filer og mapper der begynder med et punktum" + }, + "preview": { + "label": "Forhåndsvisning" + } + }, + "scoped": { + "back": "Tilbage til min konto", + "managing": "Administrerer: {name}" + } + }, + "errors": { + "page_error_title": "Noget gik galt", + "page_error_description": "Vi stødte på en uventet fejl. Prøv igen eller gå tilbage til startsiden.", + "sidebar_error": "Kunne ikke indlæse postkasser", + "email_list_error": "Kunne ikke indlæse e-mails", + "viewer_error_title": "Kan ikke vise e-mail", + "viewer_error_description": "Der var et problem med at vise denne e-mail. Den kan indeholde indhold, der ikke understøttes.", + "composer_error": "Kunne ikke indlæse skrivevinduet", + "settings_error_title": "Indstillinger utilgængelige", + "settings_error_description": "Kunne ikke indlæse indstillinger. Dine præferencer bliver muligvis ikke gemt.", + "try_again": "Prøv igen", + "reload": "Genindlæs", + "reload_emails": "Genindlæs e-mails", + "reload_settings": "Genindlæs indstillinger", + "retry": "Prøv igen", + "go_home": "Gå til indbakke" + }, + "context_menu": { + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "mark_read": "Markér som læst", + "mark_unread": "Markér som ulæst", + "star": "Stjernemarkér", + "unstar": "Fjern stjerne", + "pin": "Fastgør", + "unpin": "Frigør", + "move_to": "Flyt til...", + "copy_to_account": "Kopiér til konto...", + "remove_from_folder": "Fjern fra denne mappe", + "archive": "Arkivér", + "delete": "Slet", + "mark_as_spam": "Rapportér spam", + "not_spam": "Ikke spam", + "tag": "Tag", + "items_selected": "{count} e-mails valgt", + "forward_as_attachments": "Videresend som vedhæftede filer", + "edit_draft": "Redigér kladde", + "cancel_scheduled_send": "Annuller afsendelse", + "reschedule_send": "Planlæg om", + "cancel_and_edit": "Annuller og redigér", + "cancel_and_compose_again": "Annuller og skriv igen", + "rules": { + "title": "Regler", + "move_from": "Flyt altid beskeder fra {sender}", + "move_from_domain": "Flyt altid beskeder fra @{domain}", + "move_from_list": "Flyt altid beskeder fra denne liste", + "mark_read_from": "Markér altid beskeder fra {sender} som læst", + "tag_with": "Tilføj altid tagget", + "block": "{count, plural, one {Bloker afsender} other {Bloker # afsendere}}", + "no_junk": "Denne konto har ingen mappe til uønsket mail", + "create": "Opret regel…", + "manage": "Administrer regler", + "senders_count": "{count, plural, one {# afsender} other {# afsendere}}", + "cross_account": "Vælg beskeder fra én konto", + "opaque_hint": "Dine filtre er redigeret manuelt. Åbn filterindstillingerne" , + "loading": "Indlæser regler…", + "load_failed": "Reglerne kunne ikke indlæses", + "none": "Ingen regler endnu", + "new_mail_only": "Kun for nye beskeder", + "running": "Kører „{name}“ på {folder}…" + }, + "find_emails_from": "Find e-mails fra {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Markér mappe som læst", + "mark_folder_tree_read": "Markér mappe & undermapper som læst", + "mark_all_folders_read": "Markér alle mapper som læst", + "new_subfolder": "Ny undermappe...", + "new_folder": "Ny mappe...", + "rename": "Omdøb...", + "share": "Del...", + "import_email": "Importér .eml eller .zip...", + "empty_folder": "Tøm mappe", + "empty_folder_generic": "Tøm mappe", + "delete_folder": "Slet mappe", + "refresh": "Opdatér", + "hide_shared_account": "Skjul i sidepanelet", + "mute_notifications": "Slå notifikationer fra", + "unmute_notifications": "Slå notifikationer til", + "mute_account_notifications": "Slå notifikationer fra for denne konto", + "unmute_account_notifications": "Slå notifikationer til for denne konto", + "reset_folder_notifications": "Nulstil mappernes notifikationer", + "mark_all_confirm_title": "Markér alle mapper som læst", + "mark_all_confirm_message": "Markér alle ulæste beskeder på din personlige konto som læst?", + "delete_confirm_title": "Slet mappe", + "delete_confirm_message": "Slet mappen \"{name}\" permanent? Denne handling kan ikke fortrydes.", + "prompt_new_subfolder": "Indtast et navn til den nye undermappe.", + "prompt_new_folder": "Indtast et navn til den nye mappe.", + "prompt_rename": "Indtast et nyt navn til denne mappe.", + "placeholder_folder_name": "Mappenavn", + "create": "Opret", + "rename_confirm": "Omdøb", + "toast_marked_read": "Mappe markeret som læst", + "toast_marked_read_count": "Markerede {count, plural, one {1 besked} other {# beskeder}} som læst", + "toast_already_read": "Ingen ulæste beskeder", + "toast_marked_all_read": "Alle mapper markeret som læst", + "toast_emptied": "Mappe tømt", + "toast_folder_created": "Mappe oprettet", + "toast_folder_renamed": "Mappe omdøbt", + "toast_folder_deleted": "Mappe slettet", + "toast_error_mark_read": "Kunne ikke markere som læst", + "toast_error_empty": "Kunne ikke tømme mappe", + "toast_error_create": "Kunne ikke oprette mappe", + "toast_error_rename": "Kunne ikke omdøbe mappe", + "toast_error_delete": "Kunne ikke slette mappe", + "toast_error_delete_has_children": "Mappen har undermapper. Fjern dem først.", + "toast_error_delete_has_email": "Mappen er ikke tom. Tøm den først.", + "delete_with_contents_title": "Mappen er ikke tom", + "delete_with_contents_message": "Mappen \"{name}\" indeholder stadig {count, plural, one {1 besked} other {# beskeder}}. Slet mappen og alle dens beskeder? Dette kan ikke fortrydes.", + "delete_with_contents_confirm": "Slet mappe og beskeder" + }, + "shortcuts": { + "title": "Tastaturgenveje", + "tip": "Tryk ? når som helst for at se denne hjælp", + "sections": { + "navigation": "Navigation", + "actions": "E-mail-handlinger", + "global": "Globalt", + "threads": "Tråde", + "composer": "Skrivevindue" + }, + "navigation": { + "next_email": "Næste e-mail", + "previous_email": "Forrige e-mail", + "open_email": "Åbn e-mail", + "close_email": "Luk / Fravaælg", + "go_to_folder": "Gå til mappe", + "next_unread": "Næste ulæste e-mail", + "no_more_unread": "Der er ikke flere ulæste e-mails længere nede på listen" + }, + "actions": { + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "star": "Slå stjerne til/fra", + "archive": "Arkivér", + "delete": "Slet", + "mark_unread": "Markér som ulæst", + "mark_read": "Markér som læst", + "toggle_spam": "Rapportér spam / Ikke spam" + }, + "global": { + "compose": "Ny e-mail", + "search": "Fokusér søgning", + "help": "Vis genveje", + "refresh": "Opdatér e-mails", + "select_all": "Vælg alle", + "global_search": "Søg i alt", + "extend_selection": "Udvid markering ned / op" + }, + "threads": { + "expand_collapse": "Udvid/skjul tråd", + "select_mountain_view": "Vælg samtalen" + }, + "composer": { + "send": "Send e-mail", + "schedule_send": "Planlæg afsendelse", + "template_picker": "Åbn skabelonvælger" + } + }, + "threads": { + "messages_one": "{count} besked", + "messages_other": "{count} beskeder", + "messages_tooltip": "{count, plural, one {# besked i denne samtale} other {# beskeder i denne samtale}}", + "expand": "Udvid samtale", + "collapse": "Skjul samtale", + "loading": "Indlæser samtale...", + "mark_read": "Markér samtale som læst", + "mark_unread": "Markér samtale som ulæst", + "archive": "Arkivér samtale", + "delete": "Slet samtale", + "star": "Stjernemarkér samtale", + "unstar": "Fjern stjernemarkering", + "toggle_thread": "Slå tråd til/fra" + }, + "identities": { + "modal_title": "Administrér afsenderidentiteter", + "create_new": "Opret ny identitet", + "edit_identity": "Redigér identitet", + "delete_confirm": "Slet denne identitet? Dette kan ikke fortrydes.", + "cannot_delete": "Denne identitet kan ikke slettes", + "primary_identity": "Primær", + "set_as_primary": "Angiv som primær", + "no_identities": "Ingen identiteter fundet", + "display": { + "reply_to": "Svar-til:", + "bcc": "BCC:", + "signature": "Signatur:", + "preview": "Forhåndsvisning:" + }, + "validation_errors": { + "invalid_emails": "Ugyldige e-mails: {emails}", + "unknown_error": "Ukendt fejl" + }, + "form": { + "name_label": "Visningsnavn", + "name_placeholder": "f.eks. Arbejdsmail, Privat", + "name_required": "Navn er påkrævet", + "email_label": "E-mailadresse", + "email_placeholder": "din.email@eksempel.dk", + "email_required": "E-mail er påkrævet", + "email_invalid": "Indtast en gyldig e-mailadresse", + "email_immutable": "E-mailadresse kan ikke ændres efter oprettelse", + "reply_to_label": "Svar-til (valgfrit)", + "reply_to_placeholder": "anden@email.dk", + "bcc_label": "Auto BCC (valgfrit)", + "bcc_placeholder": "arkiv@email.dk", + "text_signature_label": "Tekstsignatur", + "html_signature_label": "HTML-signatur", + "signature_byte_counter": "{bytes} / {max} bytes", + "signature_byte_limit_reached": "Servergrænse nået", + "save": "Gem identitet", + "cancel": "Annuller", + "creating": "Opretter...", + "updating": "Opdaterer..." + }, + "sub_address": { + "button_tooltip": "Brug underadresse", + "popover_title": "Tilføj underadresse-tag", + "tag_input_placeholder": "Indtast tag (f.eks. shopping)", + "preview_label": "Forhåndsvisning:", + "recent_tags": "Seneste tags", + "suggested_tags": "Foreslået", + "use_address": "Brug denne adresse", + "invalid_tag": "Tag må kun indeholde bogstaver, tal og bindestreger", + "tag_too_long": "Tag skal være 30 tegn eller mindre", + "help_text": "E-mails sendt til bruger{delimiter}tag@domæne.dk ankommer i din indbakke", + "validation": { + "empty": "Tag kan ikke være tomt", + "too_long": "Tag skal være {max} tegn eller mindre", + "invalid_chars": "Tag må kun indeholde bogstaver, tal og bindestreger" + } + }, + "badge": { + "sent_via": "via", + "sub_address_tag": "Sendt med underadresse: {tag}", + "identity_name": "Sendt med identitet: {name}", + "identity_short": "via {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Slet", + "delete_confirm_title": "Slet identitet" + }, + "templates": { + "picker_title": "Vælg en skabelon", + "search_placeholder": "Søg efter skabeloner...", + "section_favorites": "Favoritter", + "section_recent": "Seneste", + "section_uncategorized": "Andet", + "no_templates": "Ingen skabeloner endnu", + "no_results": "Ingen skabeloner fundet", + "fill_placeholders": "Udfyld pladsholderværdier", + "enter_value": "Indtast en værdi...", + "preview": "Forhåndsvisning", + "insert_with_values": "Indsæt med værdier", + "insert_raw": "Indsæt rå", + "copy_suffix": "(kopi)", + "placeholder": "Variabel", + "placeholders": { + "recipient_name": "Modtagers navn", + "company": "Virksomhedsnavn", + "date": "Dagens dato", + "day_of_week": "Ugedag", + "sender_name": "Dit navn" + } + }, + "contacts": { + "directory": { + "title": "Adressefortegnelse", + "search_placeholder": "Søg efter navn eller e-mailadresse", + "refresh": "Opdater adressefortegnelse", + "retry": "Prøv igen", + "loading": "Indlæser adressefortegnelse…", + "unavailable_title": "Ingen adressefortegnelse", + "unavailable_hint": "Din mailserver tilbyder ikke en fortegnelse over konti.", + "forbidden_title": "Adressefortegnelse ikke tilgængelig", + "forbidden_hint": "Din mailserver tillader ikke denne konto at gennemse fortegnelsen. Spørg din administrator.", + "error_title": "Adressefortegnelsen kunne ikke indlæses", + "error_hint": "Tjek din forbindelse, og prøv igen.", + "empty_title": "Adressefortegnelsen er tom", + "empty_hint": "Din mailserver viser ingen konti med en e-mailadresse.", + "no_match": "Ingen i fortegnelsen matcher din søgning", + "refresh_failed": "Adressefortegnelsen kunne ikke opdateres. Viser den senest indlæste liste.", + "select_page": "Vælg alle på denne side", + "deselect_page": "Fravælg alle på denne side", + "select_entry": "Vælg {name}", + "range": "{from}–{to} af {total}", + "page": "Side {page} af {count}", + "previous_page": "Forrige side", + "next_page": "Næste side", + "picker_title": "Vælg fra adressefortegnelse", + "cancel": "Annuller", + "selected_count": "{count} valgt", + "add_to": "Tilføj til Til", + "add_cc": "Tilføj til Cc", + "add_bcc": "Tilføj til Bcc", + "select_hint": "Vælg en person for at se detaljer", + "write_email": "Skriv e-mail", + "add_to_contacts": "Tilføj til kontakter", + "email": "E-mail", + "read_only_note": "Posterne kommer fra din mailserver og kan ikke redigeres her.", + "type": { + "individual": "Person", + "group": "Gruppe", + "resource": "Ressource", + "location": "Sted", + "other": "Andet" + } + }, + "title": "Kontakter", + "search_placeholder": "Søg efter kontakter...", + "create_new": "Ny kontakt", + "no_category": "Ingen kategori", + "rename_category": "Omdøb kategori", + "category_name_label": "Kategorinavn", + "category_renamed": "Kategori omdøbt", + "category_rename_failed": "Kunne ikke omdøbe kategori", + "category_added": "Kontakt tilføjet til {name}", + "category_added_plural": "{count} kontakter tilføjet til {name}", + "empty_state": "Ingen kontakter endnu", + "empty_state_title": "Ingen kontakter endnu", + "empty_state_subtitle": "Opret din første kontakt eller importér fra en vCard-fil", + "empty_search": "Ingen kontakter matcher din søgning", + "empty_search_hint": "Prøv en anden søgeterm", + "empty_filtered": "Ingen kontakter matcher dine filtre", + "empty_filtered_hint": "Prøv at justere eller rydde filtre", + "clear_search": "Ryd søgning", + "import_vcard": "Importér vCard", + "delete_confirm_title": "Slet kontakt", + "delete_confirm": "Er du sikker på, at du vil slette denne kontakt?", + "local_mode": "Kontakter gemmes lokalt (serveren understøtter ikke JMAP-kontakter)", + "back_to_contacts": "Tilbage til kontakter", + "back_to_email": "Tilbage til e-mail", + "tabs": { + "all": "Alle", + "groups": "Grupper" + }, + "shared": { + "title": "Delt" + }, + "address_books": { + "create": "Ny adressebog", + "created": "Adressebog oprettet", + "create_failed": "Kunne ikke oprette adressebog", + "title": "Mine adressebøger", + "shared_prefix": "Delt: {name}", + "moved": "Kontakt flyttet til {name}", + "moved_plural": "{count} kontakter flyttet til {name}", + "move_failed": "Kunne ikke flytte kontakt", + "address_book": "Adressebog", + "rename": "Omdøb adressebog", + "name_label": "Adressebogsnavn", + "renamed": "Adressebog omdøbt", + "rename_failed": "Kunne ikke omdøbe adressebog", + "default": "Standard", + "set_default": "Angiv som standard", + "default_updated": "Standardadressebog opdateret", + "set_default_failed": "Kunne ikke angive standardadressebog", + "manage": "Administrér adressebøger", + "share": "Del adressebog", + "new_contact_in_book": "Ny kontakt i denne adressebog", + "delete": "Slet adressebog", + "confirm_delete": "Slet \"{name}\"? Alle kontakter i denne adressebog fjernes.", + "deleted": "Adressebog slettet", + "delete_failed": "Kunne ikke slette adressebog" + }, + "detail": { + "emails": "E-mailadresser", + "phones": "Telefonnumre", + "organizations": "Organisationer", + "addresses": "Adresser", + "notes": "Noter", + "titles": "Titler & roller", + "online_services": "Online tjenester", + "anniversaries": "Mærkedage", + "personal_info": "Personlige oplysninger", + "languages": "Sprog", + "categories": "Kategorier", + "related_contacts": "Relaterede kontakter", + "crypto_keys": "Krypto-nøgler", + "cert_issuer": "Udsteder", + "cert_expires": "Udløber", + "cert_expired": "Udløbet", + "cert_fingerprint": "Fingeraftryk", + "cert_algorithm": "Algoritme", + "import_to_smime": "Importér til S/MIME", + "cert_already_imported": "Allerede importeret til S/MIME", + "cert_imported": "Certifikat importeret til S/MIME-lager", + "cert_import_failed": "Kunne ikke importere certifikat", + "no_contact_selected": "Vælg en kontakt for at se detaljer", + "compose_email": "Skriv e-mail", + "copy_email": "Kopier e-mail", + "copy_phone": "Kopier telefonnummer", + "copy_url": "Kopier URL", + "copied": "Kopieret til udklipsholder", + "copy_failed": "Kunne ikke kopiere til udklipsholder", + "created": "Oprettet", + "updated": "Sidst opdateret", + "timezone": "Tidszone", + "anniversary_birth": "Fødselsdag", + "anniversary_death": "Dødsdag", + "anniversary_wedding": "Bryllupsdag", + "anniversary_other": "Andet", + "personal_expertise": "Ekspertise", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Andet", + "gender": "Køn", + "gender_masculine": "Mand", + "gender_feminine": "Kvinde", + "gender_other": "Andet", + "gender_none": "Ikke relevant", + "gender_unknown": "Ukendt", + "calendar": "Kalender", + "calendar_uri": "Kalender-URL", + "scheduling_uri": "Planlægnings-URL", + "freebusy_uri": "Ledig/Opptagen-URL", + "section_contact": "Kontaktoplysninger", + "section_work": "Arbejde", + "section_personal": "Privat", + "email_default_label": "E-mail", + "phone_default_label": "Telefon", + "address_default_label": "Adresse", + "online_service_default_label": "Online", + "organization_label": "Organisation", + "title_label": "Titel", + "role_label": "Rolle", + "language_label": "Sprog", + "related_default_label": "Relateret", + "more_actions": "Flere handlinger", + "age_years": "{count, plural, one {1 år gammel} other {# år gammel}}", + "years_since": "{count, plural, one {1 år} other {# år}}" + }, + "activity": { + "recent_emails": "Seneste e-mails", + "upcoming_events": "Kommende begivenheder", + "no_emails": "Ingen seneste e-mails", + "no_events": "Ingen kommende begivenheder", + "no_subject": "(Intet emne)", + "no_title": "(Ingen titel)", + "load_failed": "Kunne ikke indlæse", + "unknown_sender": "Ukendt afsender", + "all_day": "Hele dagen" + }, + "form": { + "create_title": "Ny kontakt", + "edit_title": "Redigér kontakt", + "section_address_book": "Adressebog", + "select_address_book": "Vælg en adressebog...", + "section_identity": "Navn & identitet", + "contact_type": "Kontakttype", + "type_person": "Person", + "type_organization": "Organisation", + "section_work": "Arbejde & organisation", + "prefix": "Præfiks", + "prefix_placeholder": "Dr., hr., fru", + "given_name": "Fornavn", + "middle_name": "Mellemnavn", + "surname": "Efternavn", + "suffix": "Suffiks", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Kaldenavn", + "nickname_placeholder": "Kaldenavn", + "email": "E-mail", + "email_placeholder": "email@eksempel.dk", + "phone": "Telefon", + "phone_placeholder": "+45 12 34 56 78", + "phone_type": "Type", + "phone_voice": "Telefon", + "phone_cell": "Mobil", + "phone_fax": "Fax", + "phone_pager": "Personsøger", + "phone_video": "Video", + "phone_text": "Tekst", + "organization": "Organisation", + "organization_placeholder": "Virksomhedsnavn", + "department": "Afdeling", + "department_placeholder": "Afdeling", + "job_title": "Jobtitel", + "job_title_placeholder": "f.eks. Softwareudvikler", + "role": "Rolle", + "role_placeholder": "f.eks. Teamleder", + "addresses": "Adresser", + "add_address": "Tilføj adresse", + "street": "Gade", + "city": "By", + "region": "Region", + "postcode": "Postnummer", + "country": "Land", + "online_services": "Online tjenester", + "add_online_service": "Tilføj online tjeneste", + "url_placeholder": "https://...", + "service_placeholder": "Tjeneste", + "anniversaries": "Mærkedage", + "add_anniversary": "Tilføj dato", + "anniversary_birth": "Fødselsdag", + "anniversary_wedding": "Bryllupsdag", + "anniversary_death": "Mindesmærke", + "anniversary_other": "Andet", + "personal_info": "Personlige oplysninger", + "add_personal_info": "Tilføj indførsel", + "personal_info_placeholder": "f.eks. Fotografi", + "personal_expertise": "Ekspertise", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Andet", + "level": "Niveau", + "level_high": "Høj", + "level_medium": "Mellem", + "level_low": "Lav", + "categories": "Kategorier", + "categories_placeholder": "f.eks. Familie, Venner, Kolleger", + "categories_hint": "Skriv for at søge eller tilføje kategorier", + "category_add": "Tilføj", + "note": "Noter", + "note_placeholder": "Tilføj en note...", + "gender": "Køn", + "gender_sex": "Biologisk køn", + "gender_male": "Mand", + "gender_female": "Kvinde", + "gender_other": "Andet", + "gender_none": "Ikke relevant", + "gender_unknown": "Ukendt", + "gender_identity": "Kønsidentitet", + "gender_identity_placeholder": "Kønsidentitet...", + "calendar": "Kalender", + "calendar_uri": "Kalender-URL", + "scheduling_uri": "Planlægnings-URL", + "freebusy_uri": "Ledig/Opptagen-URL", + "context_work": "Arbejde", + "context_private": "Privat", + "add_email": "Tilføj e-mail", + "add_phone": "Tilføj telefon", + "save": "Gem", + "cancel": "Annuller", + "creating": "Opretter...", + "updating": "Opdaterer...", + "name_required": "Angiv et fornavn, efternavn eller en organisation", + "email_invalid": "Indtast en gyldig e-mailadresse", + "email_error_inline": "Ugyldigt e-mailformat", + "save_failed": "Kunne ikke gemme kontakt", + "delete": "Slet", + "upload_photo": "Upload billede", + "remove_photo": "Fjern billede", + "photo_hint": "JPG eller PNG, op til 10 MB. Vil blive ændret i størrelse.", + "photo_too_large": "Billedet er for stort (max 10 MB)", + "photo_invalid": "Ugyldig billedfil", + "change_photo": "Skift" + }, + "groups": { + "create": "Ny gruppe", + "edit": "Redigér gruppe", + "empty": "Ingen grupper endnu", + "delete_confirm_title": "Slet gruppe", + "delete_confirm": "Er du sikker på, at du vil slette denne gruppe?", + "name_label": "Gruppenavn", + "name_placeholder": "f.eks. Team, Familie", + "name_required": "Gruppenavn er påkrævet", + "save_failed": "Kunne ikke gemme gruppe", + "members_label": "Medlemmer", + "search_members": "Søg efter kontakter at tilføje...", + "no_members": "Ingen medlemmer i denne gruppe", + "member_count": "{count, plural, =0 {Ingen medlemmer} one {1 medlem} other {# medlemmer}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Importér kontakter", + "drop_hint": "Klik for at vælge en vCard-fil", + "file_types": ".vcf eller .vcard filer", + "no_contacts": "Ingen kontakter fundet i filen", + "parse_error": "Kunne ikke parse vCard-fil", + "found": "{count, plural, one {1 kontakt fundet} other {# kontakter fundet}}", + "duplicate": "Dublet", + "select_all": "Vælg alle", + "deselect_all": "Fravælg alle", + "selected": "{count, plural, one {1 valgt} other {# valgte}}", + "import_button": "Importér", + "importing": "Importerer...", + "success": "{count, plural, one {1 kontakt importeret} other {# kontakter importeret}}", + "failed": "Import mislykkedes", + "close": "Luk", + "file_too_large": "Filen er for stor (max 5 MB)" + }, + "export": { + "title": "Eksportér kontakter", + "success": "{count, plural, one {1 kontakt eksporteret} other {# kontakter eksporteret}}" + }, + "bulk": { + "selected": "{count, plural, one {1 valgt} other {# valgte}}", + "select_all": "Vælg alle", + "delete": "Slet", + "delete_confirm_title": "Slet kontakter", + "delete_confirm": "Slet {count, plural, one {1 kontakt} other {# kontakter}}?", + "deleted": "{count, plural, one {1 kontakt slettet} other {# kontakter slettet}}", + "add_to_group": "Tilføj til gruppe", + "choose_group": "Vælg en gruppe", + "adding_contacts": "Tilføjer {count, plural, one {1 kontakt} other {# kontakter}}", + "added_to_group": "Kontakter tilføjet til gruppe", + "export": "Eksportér", + "clear": "Ryd markering" + }, + "toast": { + "created": "Kontakt oprettet", + "updated": "Kontakt opdateret", + "deleted": "Kontakt slettet", + "error_create": "Kunne ikke oprette kontakt", + "error_update": "Kunne ikke opdatere kontakt", + "error_delete": "Kunne ikke slette kontakt" + }, + "context_menu": { + "open": "Åbn", + "edit": "Redigér", + "send_email": "Send e-mail", + "add_to_group": "Tilføj til gruppe", + "export_vcard": "Eksportér som vCard", + "delete": "Slet", + "call": "Ring", + "duplicate": "Duplikér", + "print": "Udskriv" + }, + "filters": { + "toggle": "Filtre", + "select": "Vælg", + "clear": "Ryd", + "close": "Luk", + "title": "Avancerede filtre", + "organization": "Virksomhed", + "organization_placeholder": "f.eks. Acme Corp", + "job_title": "Jobtitel", + "job_title_placeholder": "f.eks. Designer", + "location": "Sted", + "location_placeholder": "By eller land", + "email_domain": "E-maildomæne", + "email_domain_placeholder": "eksempel.dk", + "birthday_month": "Fødselsdag i", + "any_month": "Enhver måned", + "has_email": "Har e-mail", + "has_phone": "Har telefon", + "has_photo": "Har billede", + "sort_by": "Sortér efter", + "sort_first_name": "Fornavn", + "sort_last_name": "Efternavn" + }, + "open_categories": "Åbn kategorier" + }, + "calendar": { + "title": "Kalender", + "back_to_email": "Tilbage til e-mail", + "back_to_month": "Tilbage til måned", + "my_calendars": "Kalendere", + "birthday_calendar": "Fødselsdage", + "mini_calendar_change": "Klik for at skifte måned", + "views": { + "month": "Måned", + "week": "Uge", + "day": "Dag", + "agenda": "Agenda", + "today": "I dag", + "month_hint": "Måned (m)", + "week_hint": "Uge (w)", + "day_hint": "Dag (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Opgaver", + "tasks_hint": "Opgaver (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# tidligere begivenhed} other {# tidligere begivenheder}}", + "hidden_after": "{count, plural, one {# senere begivenhed} other {# senere begivenheder}}", + "show_all_hours": "Vis alle timer", + "show_display_hours": "Vis kun {start} – {end}", + "create": "Opret begivenhed", + "edit": "Redigér begivenhed", + "delete": "Slet begivenhed", + "details": "Begivenhedsdetaljer", + "no_events": "Ingen begivenheder", + "all_day": "Hele dagen", + "more": "+{count} mere", + "show_more": "Vis mere", + "show_less": "Vis mindre", + "no_title": "(Ingen titel)", + "resize": "Tilpas størrelse på begivenhed", + "duplicate": "Duplikér", + "today_header": "I dag", + "tomorrow_header": "I morgen", + "agenda_show_earlier": "Vis tidligere begivenheder", + "agenda_loading": "Indlæser…", + "agenda_range_start": "Begivenheder fra {date}", + "agenda_range_end": "Begivenheder til {date}", + "export_ics": "Eksportér som .ics", + "copy_title": "Kopier titel", + "copy_link": "Kopier mødelink", + "new_event": "Ny begivenhed", + "new_all_day_event": "Ny heldagsbegivenhed", + "new_task": "Ny opgave", + "go_to_today": "Gå til i dag" + }, + "detail": { + "add_note": "Tilføj en note...", + "save_note": "Gem", + "note_saved": "Note tilføjet", + "open_link": "Åbn link", + "copy_location": "Kopiér sted", + "meeting_link": "Mødelink", + "tentative": "Foreløbig", + "cancelled": "Aflyst", + "delete_confirm": "Slet denne begivenhed?" + }, + "form": { + "title": "Titel", + "description": "Beskrivelse", + "location": "Sted", + "meeting_link": "Mødelink", + "start_date": "Startdato", + "end_date": "Slutdato", + "start_time": "Starttidspunkt", + "end_time": "Sluttidspunkt", + "all_day_event": "Heldagsbegivenhed", + "calendar_select": "Kalender", + "save": "Gem", + "cancel": "Annuller", + "delete_confirm": "Er du sikker på, at du vil slette denne begivenhed?", + "color": "Farve" + }, + "participants": { + "availability": { + "title": "Tilgængelighed", + "free": "Ledig", + "busy": "Optaget", + "tentative": "Foreløbig", + "unknown": "Ikke en bruger på denne server", + "checking": "Kontrollerer…" + }, + "title": "Deltagere", + "add": "Tilføj deltager", + "organizer": "Arrangør", + "attendee": "Deltager", + "accepted": "Accepteret", + "declined": "Afslået", + "tentative": "Foreløbig", + "needs_action": "Kræver handling", + "remove": "Fjern", + "edit": "Redigér", + "email_placeholder": "Tilføj e-mailadresse eller søg efter kontakter", + "send_invitations": "Send invitationer til deltagere", + "status_summary": "{accepted} accepteret, {pending} afventer", + "invited_by": "Invitation fra {name}", + "respond_below": "Svar ved hjælp af knapperne nedenfor", + "rsvp_label": "Dit svar", + "cancel_notification": "Deltagere vil blive underrettet om aflysningen", + "you_organizer": "Du er arrangøren", + "you_attendee": "Du er deltager", + "no_participants": "Ingen deltagere", + "count": "{count, plural, one {# deltager} other {# deltagere}}" + }, + "recurrence": { + "title": "Gentagelse", + "none": "Gentages ikke", + "daily": "Dagligt", + "weekly": "Ugentligt", + "monthly": "Månedligt", + "yearly": "Årligt", + "every_n_days": "Hver {count} dag", + "every_n_weeks": "Hver {count} uge", + "every_n_months": "Hver {count} måned", + "until": "Indtil", + "occurrences": "{count} forekomster", + "custom": "Tilpasset…", + "edit_custom": "Rediger tilpasset gentagelse", + "every_n_years": "Hvert {count}. år", + "on_days": "på {days}", + "on_day_n": "på dag {day}", + "on_the_nth": "den {nth} {day}", + "in_month": "i {month}", + "nth_1": "første", + "nth_2": "anden", + "nth_3": "tredje", + "nth_4": "fjerde", + "nth_last": "sidste", + "editor_freq_day": "Dag", + "editor_freq_week": "Uge", + "editor_freq_month": "Måned", + "editor_freq_year": "År", + "editor_repeats_on": "Gentages", + "editor_every": "Hver", + "editor_unit_days": "dag(e)", + "editor_unit_weeks": "uge(r)", + "editor_unit_months": "måned(er)", + "editor_unit_years": "år", + "editor_on_day": "på dag", + "editor_on_the": "den", + "editor_in": "i", + "editor_ends": "Slutter", + "editor_never": "Aldrig", + "editor_ends_on": "Den", + "editor_ends_after": "Efter", + "editor_occurrences": "gentagelser" + }, + "recurrence_scope": { + "edit_title": "Redigér tilbagevendende begivenhed", + "delete_title": "Slet tilbagevendende begivenhed", + "description": "Dette er en tilbagevendende begivenhed. Hvilke begivenheder vil du ændre?", + "this_event": "Kun denne begivenhed", + "this_and_future": "Denne og fremtidige begivenheder", + "all_events": "Alle begivenheder", + "cancel": "Annuller", + "save": "Gem", + "delete": "Slet", + "rsvp_title": "Svar på tilbagevendende begivenhed", + "rsvp_description": "Dette er en tilbagevendende begivenhed. Hvilke begivenheder gælder dit svar for?", + "respond": "Svar" + }, + "alerts": { + "title": "Påmindelse", + "none": "Ingen påmindelse", + "at_time": "På begivenhedstidspunktet", + "minutes_before": "{count, plural, one {# minut før} other {# minutter før}}", + "hours_before": "{count, plural, one {# time før} other {# timer før}}", + "days_before": "{count, plural, one {# dag før} other {# dage før}}", + "weeks_before": "{count, plural, one {# uge før} other {# uger før}}", + "unit_minutes_before": "minutter før", + "unit_hours_before": "timer før", + "unit_days_before": "dage før", + "unit_weeks_before": "uger før", + "add": "Tilføj påmindelse", + "remove": "Fjern påmindelse", + "amount": "Påmindelsesmængde", + "unit": "Påmindelsesenhed", + "action": "Påmindelsestype", + "action_display": "Notifikation", + "action_email": "E-mail", + "by_email": "{label} via e-mail", + "kept": "{count, plural, one {# påmindelse mere, der ikke kan redigeres her, bevares} other {# påmindelser mere, der ikke kan redigeres her, bevares}}", + "other": "{count, plural, one {# anden påmindelse} other {# andre påmindelser}}", + "calendar_defaults": "Kalenderens standardpåmindelser", + "customize": "Tilpas", + "use_defaults": "Brug kalenderens standardpåmindelser" + }, + "settings": { + "limit_hours": "Begræns synlige timer", + "limit_hours_desc": "Vis kun disse timer i dags- og ugevisningen. Begivenheder uden for dem kan stadig nås via pilene øverst og nederst på hver dag.", + "visible_hours": "Synlige timer", + "visible_hours_start": "Start på synlige timer", + "visible_hours_end": "Slut på synlige timer", + "hide_non_working_days": "Skjul fridage", + "hide_non_working_days_desc": "Udelad de dage, du ikke arbejder, fra ugevisningen. Deres begivenheder vises stadig i måneds-, dags- og agendavisningen.", + "working_days": "Arbejdsdage", + "title": "Kalenderindstillinger", + "default_view": "Standardvisning", + "week_starts_on": "Ugen starter på", + "time_format": "Tidsformat", + "default_calendar": "Standardkalender", + "default_reminder": "Standardpåmindelse", + "time_format_12h": "12-timers", + "time_format_24h": "24-timers", + "notifications_enabled": "Begivenhedsnotifikationer", + "notifications_enabled_desc": "Vis alarmer for kommende kalenderbegivenheder", + "notification_sound": "Notifikationslyd", + "notification_sound_desc": "Afspil en lyd for kalenderalarmer", + "invitation_parsing": "Pars e-mail-invitationer", + "invitation_parsing_desc": "Registrér kalenderinvitationer i e-mail-vedhæftninger og vis kalenderhandlinger", + "show_time_in_month_view": "Vis tid i månedsvisning", + "show_time_in_month_view_desc": "Vis begivenhedstider i månedskalendervisningen", + "show_week_numbers": "Vis ugenumre", + "show_week_numbers_desc": "Vis ugenumre i minikalenderen", + "calendar_free_scroll": "Fri rulning", + "calendar_free_scroll_desc": "Rul fortløbende gennem måneder, uger og dage i stedet for én periode ad gangen", + "enable_tasks": "Aktivér opgaver", + "enable_tasks_desc": "Vis en opgavevisning i kalenderen til styring af gøremål", + "show_tasks_on_calendar": "Vis opgaver på kalender", + "show_tasks_on_calendar_desc": "Vis opgaver i måneds-, uge- og dagsvisningen", + "hover_preview": "Hover-forhåndsvisning", + "hover_preview_desc": "Vis en detalje-popover når du holder musen over kalenderbegivenheder", + "hover_preview_instant": "Øjeblikkeligt", + "hover_preview_delay_500ms": "0,5 sekunds forsinkelse", + "hover_preview_delay_1s": "1 sekunds forsinkelse", + "hover_preview_delay_2s": "2 sekunders forsinkelse", + "hover_preview_off": "Deaktiveret", + "organizer_identity": "Organisér invitationer som", + "organizer_identity_desc": "Kalenderadressen, der bruges som arrangør, når du inviterer folk til begivenheder.", + "organizer_identity_failed": "Kunne ikke ændre arrangøradressen", + "show_birthday_calendar": "Kontaktfødselsdagskalender", + "show_birthday_calendar_desc": "Vis en virtuel kalender med fødselsdage fra dine kontakter" + }, + "days": { + "monday": "Mandag", + "tuesday": "Tirsdag", + "wednesday": "Onsdag", + "thursday": "Torsdag", + "friday": "Fredag", + "saturday": "Lørdag", + "sunday": "Søndag", + "mon": "Man", + "tue": "Tir", + "wed": "Ons", + "thu": "Tor", + "fri": "Fre", + "sat": "Lør", + "sun": "Søn" + }, + "months": { + "jan": "Jan", + "feb": "Feb", + "mar": "Mar", + "apr": "Apr", + "may": "Maj", + "jun": "Jun", + "jul": "Jul", + "aug": "Aug", + "sep": "Sep", + "oct": "Okt", + "nov": "Nov", + "dec": "Dec", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Begivenhed oprettet", + "event_updated": "Begivenhed opdateret", + "event_deleted": "Begivenhed slettet", + "calendar_created": "Kalender oprettet", + "calendar_deleted": "Kalender slettet", + "event_move_error": "Kunne ikke flytte begivenhed", + "event_resize_error": "Kunne ikke ændre størrelse på begivenhed", + "alert_title": "Kommende begivenhed", + "alert_now": "Starter nu", + "alert_in_minutes": "Om {count} min", + "invitation_sent": "Invitationer sendt", + "invitations_denied_title": "Invitationerne kan ikke sendes", + "invitations_denied": "Serveren afviste at sende invitationerne: {reason}. Gem begivenheden uden at sende dem?", + "save_without_invitations": "Gem uden invitationer", + "rsvp_updated": "Svar opdateret", + "rsvp_error": "Kunne ikke opdatere svar", + "event_duplicated": "Begivenhed duplikeret", + "event_error": "Kunne ikke gemme begivenhed", + "task_due": "Opgave forfalder", + "event_exported": "Begivenhed eksporteret", + "title_copied": "Titel kopieret", + "link_copied": "Link kopieret" + }, + "status": { + "loading_calendars": "Indlæser kalendere...", + "loading_events": "Indlæser begivenheder..." + }, + "quick_create": { + "placeholder": "Ny begivenhedstitel", + "aria_label": "Hurtig oprettelse af begivenhed", + "add_title": "Tilføj titel", + "more_options": "Flere indstillinger" + }, + "nav_prev": "Forrige", + "nav_next": "Næste", + "import": { + "title": "Importér kalender", + "tab_file": "Fil", + "tab_url": "URL", + "select_file": "Vælg .ics-fil", + "drop_file": "eller slip fil her", + "supported_formats": "Understøtter iCalendar (.ics) filer", + "url_description": "Indtast URL'en til et eksternt iCalendar (.ics)-feed for at importere begivenheder.", + "url_placeholder": "https://eksempel.dk/kalender.ics", + "url_hint": "Understøtter iCalendar (.ics) URL'er", + "fetch": "Hent", + "invalid_url": "Indtast en gyldig URL", + "url_fetch_failed": "Kunne ikke hente kalender fra URL", + "parsing": "Parser kalenderfil...", + "parsed_events": "{count} begivenheder fundet", + "no_events": "Ingen begivenheder fundet i filen", + "select_all": "Vælg alle", + "deselect_all": "Fravælg alle", + "target_calendar": "Importér til kalender", + "import_button": "Importér valgte", + "importing": "Importerer begivenheder...", + "success": "{count} begivenheder importeret", + "error": "Kunne ikke importere kalender", + "file_too_large": "Filen overstiger 10 MB grænsen", + "invalid_format": "Ugyldigt kalenderfilformat" + }, + "webcal_action": { + "title": "Åbn kalenderlink", + "description": "Hvordan vil du bruge \"{name}\"?", + "import_title": "Importér én gang", + "import_description": "Hent begivenhederne nu og kopier dem til en af dine kalendere.", + "subscribe_title": "Abonnér", + "subscribe_description": "Hold denne kalender synkroniseret automatisk som en separat kalender.", + "cancel": "Annuller" + }, + "management": { + "title": "Kalenderadministration", + "description": "Opret, omdøb og tilpas dine kalendere. Højreklik på en kalender i sidepanelet for hurtigt at ændre dens farve.", + "name": "Navn", + "name_placeholder": "Kalendernavn", + "color": "Farve", + "change_color": "Skift farve", + "random_color": "Ny tilfældig farve", + "add_calendar": "Tilføj kalender", + "edit": "Redigér", + "delete": "Slet", + "save": "Gem", + "create": "Opret", + "cancel": "Annuller", + "default": "Standard", + "confirm_delete": "Slet \"{name}\"? Alle begivenheder i denne kalender fjernes.", + "confirm_clear": "Ryd alle begivenheder fra \"{name}\"? Dette kan ikke fortrydes.", + "set_default": "Angiv som standard", + "default_updated": "Standardkalender opdateret", + "error_default": "Kunne ikke angive standardkalender", + "clear_events": "Ryd begivenheder", + "events_cleared": "{count} begivenheder ryddet", + "error_clear": "Kunne ikke rydde kalenderbegivenheder", + "calendar_created": "Kalender oprettet", + "calendar_updated": "Kalender opdateret", + "calendar_deleted": "Kalender slettet", + "color_updated": "Kalenderfarve opdateret", + "error_create": "Kunne ikke oprette kalender", + "error_update": "Kunne ikke opdatere kalender", + "error_delete": "Kunne ikke slette kalender", + "caldav_url": "CalDAV-URL", + "copy_url": "Kopier CalDAV-URL", + "url_copied": "CalDAV-URL kopieret til udklipsholder", + "share": "Del kalender", + "new_event_in_calendar": "Ny begivenhed i denne kalender", + "shared_with": "Delt med {names}", + "shared_with_count": "{count, plural, one {Delt med 1 person} other {Delt med # personer}}", + "move_up": "Flyt op", + "move_down": "Flyt ned", + "kind": "Indhold", + "kind_events": "Begivenheder", + "kind_tasks": "Opgaver", + "kind_both": "Begivenheder og opgaver", + "kind_hint": "Bestemmer, hvilke apps der kan se denne kalender, når den synkroniseres via CalDAV. Det kan ikke ændres senere.", + "default_alerts": "Standardpåmindelser", + "default_alerts_title": "Standardpåmindelser for \"{name}\"", + "default_alerts_hint": "Nye begivenheder i denne kalender får disse påmindelser, medmindre de ændres i begivenheden. Ændringer her påvirker ikke eksisterende begivenheder. Påmindelser via e-mail sendes af mailserveren.", + "default_alerts_timed": "Begivenheder med klokkeslæt", + "default_alerts_all_day": "Heldagsbegivenheder", + "default_alerts_updated": "Standardpåmindelser opdateret" + }, + "subscription": { + "title": "iCal-abonnement", + "section_title": "iCal-abonnementer", + "description": "Abonnér på et eksternt iCalendar-feed. Begivenheder synkroniseres automatisk til deres egen kalender. Understøtter https:// og webcal:// URL'er.", + "url_label": "Kalender-URL", + "url_placeholder": "https://eksempel.dk/kalender.ics eller webcal://...", + "name_label": "Kalendernavn", + "name_placeholder": "f.eks. Offentlige helligdage", + "color_label": "Farve", + "refresh_interval": "Opdateringsinterval", + "interval_15": "Hvert 15. minut", + "interval_30": "Hvert 30. minut", + "interval_60": "Hver time", + "interval_360": "Hver 6. time", + "interval_1440": "Hver dag", + "subscribe": "Abonnér", + "subscribing": "Abonnerer...", + "save": "Gem ændringer", + "saving": "Gemmer...", + "edit": "Redigér", + "edit_title": "Redigér abonnement", + "updated": "Opdaterede \"{name}\"", + "update_error": "Kunne ikke opdatere abonnement", + "invalid_url": "Indtast en gyldig URL", + "success": "Abonneret på \"{name}\"", + "error": "Kunne ikke tilføje abonnement", + "refresh": "Opdatér nu", + "refresh_success": "Abonnement opdateret", + "refresh_error": "Kunne ikke opdatere abonnement", + "unsubscribe": "Afmeld", + "confirm_delete": "Afmeld \"{name}\"? Kalenderen og alle dens begivenheder fjernes.", + "deleted": "Abonnement fjernet", + "delete_error": "Kunne ikke fjerne abonnement", + "last_refreshed": "Sidst opdateret: {time}" + }, + "tasks": { + "label": "Opgaver", + "no_tasks": "Ingen opgaver", + "no_title": "(Ingen titel)", + "mark_complete": "Markér som fuldført", + "mark_incomplete": "Markér som ikke fuldført", + "filter_all": "Alle", + "filter_pending": "Afventer", + "filter_completed": "Fuldførte", + "filter_overdue": "Forfaldne", + "show_completed": "Vis fuldførte", + "create": "Ny opgave", + "edit": "Redigér opgave", + "title_placeholder": "Opgavetitel", + "description_placeholder": "Tilføj en beskrivelse...", + "due_date": "Forfaldsdato", + "include_time": "Inkludér tid", + "priority": "Prioritet", + "priority_none": "Ingen", + "priority_high": "Høj", + "priority_medium": "Mellem", + "priority_low": "Lav", + "progress": "Status", + "progress_needs_action": "Kræver handling", + "progress_in_process": "I gang", + "progress_completed": "Fuldført", + "progress_cancelled": "Annulleret", + "calendar": "Kalender", + "alert": "Påmindelse", + "alert_none": "Ingen", + "alert_at_time": "På forfaldsdato", + "alert_5min": "5 minutter før", + "alert_15min": "15 minutter før", + "alert_30min": "30 minutter før", + "alert_1hr": "1 time før", + "alert_1day": "1 dag før", + "delete": "Slet", + "cancel": "Annuller", + "save": "Gem", + "quick_add_placeholder": "Tilføj en opgave...", + "due_today": "I dag", + "due_tomorrow": "I morgen", + "overdue": "Forfalden" + }, + "nav_open_menu": "Åbn menu", + "toolbar": { + "more": "Flere handlinger", + "select_view": "Skift visning, aktuelt {view}" + }, + "other_calendars": "Andre kalendere", + "calendar_options": "Indstillinger for {name}" + }, + "calendar_event_notifications": { + "someone": "Nogen", + "invited": "{name} har inviteret dig til \"{title}\"", + "updated": "{name} har opdateret \"{title}\"", + "cancelled": "{name} har aflyst \"{title}\"", + "untitled": "Unavngivet begivenhed" + }, + "share_notifications": { + "someone": "Nogen", + "shared": "{name} har delt {kind} \"{object}\" med dig", + "changed": "{name} har ændret din adgang til {kind} \"{object}\"", + "revoked": "{name} har fjernet din adgang til {kind} \"{object}\"", + "object": { + "folder": "mappen", + "calendar": "kalenderen", + "address_book": "adressebogen", + "files": "filmappen" + } + }, + "sharing": { + "title": "Del \"{name}\"", + "description": "Giv adgang til andre brugere eller grupper på denne server. Ændringer træder i kraft øjeblikkeligt.", + "no_shares": "Ikke delt med nogen endnu.", + "add_person": "Tilføj person eller gruppe", + "search_placeholder": "Søg efter navn eller e-mail…", + "loading_principals": "Indlæser brugere…", + "no_principals": "Ingen andre brugere eller grupper fundet.", + "no_match": "Ingen match.", + "remove": "Fjern adgang", + "group": "Gruppe", + "share_added": "Adgang givet", + "share_updated": "Adgang opdateret", + "share_removed": "Adgang fjernet", + "share_failed": "Kunne ikke opdatere deling", + "preset": { + "freeBusy": "Kun ledig/optaget", + "read": "Kun læsning", + "readWrite": "Læs & skriv", + "manager": "Administrator", + "custom": "Brugerdefineret" + } + }, + "advanced_search": { + "title": "Avanceret søgning", + "from": "Fra", + "from_placeholder": "Afsenders e-mail eller navn", + "to": "Til", + "to_placeholder": "Modtagers e-mail eller navn", + "subject": "Emne", + "subject_placeholder": "Emne indeholder...", + "body": "Brødtekst", + "body_placeholder": "Brødtekst indeholder...", + "folder": "Mappe", + "all_folders": "Alle mapper", + "all_folders_except_spam_trash": "Alle mapper undtagen spam og papirkurv", + "has_attachment": "Vedhæftninger", + "date_after": "Efter", + "date_before": "Før", + "size_min": "Større end (KB)", + "size_max": "Mindre end (KB)", + "starred": "Stjernemarkerede", + "unread": "Ulæst", + "read": "Læst", + "yes": "Ja", + "no": "Nej", + "clear": "Ryd", + "clear_all": "Ryd alle", + "filters_active": "{count} filter", + "filters_active_plural": "{count} filtre", + "toggle_filters": "Mere", + "search_hint": "Brug avancerede filtre til præcis søgning", + "advanced_filters_tooltip": "Avancerede søgefiltre", + "results_found": "{count, plural, =0 {Ingen resultater fundet} one {# resultat fundet} other {# resultater fundet}}", + "results_found_more": "{count}+ resultater fundet", + "suggestions_recent": "Seneste søgninger", + "suggestions_people": "Personer", + "suggestions_from": "Søg efter mails fra {address}", + "suggestions_to": "Søg efter mails til {address}", + "suggestions_remove_recent": "Fjern fra seneste søgninger", + "has_words": "Indeholder ordene", + "size": "Størrelse", + "date_within": "Dato", + "search_in": "Søg i", + "has_attachment_check": "Har vedhæftet fil", + "search": "Søg" + }, + "welcome": { + "title": "Velkommen til din postkasse", + "tip_compose": "Tryk c for at skrive en ny e-mail", + "tip_shortcuts": "Tryk ? for at se alle tastaturgenveje", + "tip_sidebar": "Find Kalender, Kontakter og Indstillinger i sidepanelets menu", + "tip_settings": "Tilpas din oplevelse i Indstillinger", + "tip_compose_touch": "Tryk på blyantknappen for at skrive en ny e-mail", + "tip_sidebar_touch": "Kalender, Kontakter og Indstillinger finder du i bjælken nederst", + "got_it": "Forstået", + "settings": "Indstillinger", + "dismiss": "Afvis", + "start_tour": "Start rundvisning" + }, + "demo_welcome": { + "title": "Velkommen til Bulwark Mail", + "description": "Udforsk en fuldt udstyret webmailklient - direkte i din browser. Alle data forbliver på din enhed, så du kan teste alt frit.", + "feature_email": "Læs & skriv e-mail", + "feature_organize": "Tags, stjerner & mapper", + "feature_shortcuts": "Tastaturgenveje", + "feature_privacy": "100% privat demo", + "hint": "Klik på en e-mail til venstre for at komme i gang, eller tag rundvisningen nedenfor." + }, + "files": { + "office_edit": "Rediger dokument", + "office_loading": "Åbner dokumenteditor…", + "office_error": "Dokumenteditoren kunne ikke åbnes.", + "office_close": "Luk editor", + "open_in_new_tab": "Åbn i ny fane", + "title": "Filer", + "search_placeholder": "Søg efter filer...", + "empty_state_title": "Ingen filer endnu", + "upload_files": "Upload filer", + "new_folder": "Ny mappe", + "new_folder_name": "Mappenavn", + "rename": "Omdøb", + "rename_title": "Omdøb", + "new_name": "Nyt navn", + "name_forbidden_chars": "Serveren tillader ikke disse tegn i navne: {chars}", + "name_reserved": "Dette navn er reserveret på serveren.", + "delete": "Slet", + "delete_confirm_title": "Slet ressource", + "delete_confirm_message": "Er du sikker på, at du vil slette \"{name}\"? Dette kan ikke fortrydes.", + "download": "Download", + "name": "Navn", + "size": "Størrelse", + "modified": "Ændret", + "type": "Type", + "folder": "Mappe", + "file": "Fil", + "uploading": "Uploader...", + "upload_success": "{count, plural, one {1 fil uploadet} other {# filer uploadet}}", + "upload_canceled": "Upload annulleret", + "upload_canceled_partial": "{count, plural, one {1 fil blev} other {# filer blev}} uploadet, før du annullerede", + "upload_error": "Kunne ikke uploade fil", + "create_folder_success": "Mappe oprettet", + "create_folder_error": "Kunne ikke oprette mappe", + "delete_success": "Slettet", + "delete_error": "Kunne ikke slette", + "rename_success": "Omdøbt", + "rename_error": "Kunne ikke omdøbe", + "download_error": "Kunne ikke downloade", + "not_available": "Fillager er ikke tilgængeligt på denne server", + "cancel": "Annuller", + "create": "Opret", + "save": "Gem", + "no_results": "Ingen filer matcher din søgning", + "batch_delete_confirm_message": "Er du sikker på, at du vil slette {count, plural, one {1 element} other {# elementer}}? Dette kan ikke fortrydes.", + "batch_delete_success": "{count, plural, one {1 element slettet} other {# elementer slettet}}", + "grid_view": "Gittervisning", + "list_view": "Listevisning", + "details": "Detaljer", + "path": "Sti", + "preview": "Forhåndsvisning", + "preview_error": "Kunne ikke indlæse forhåndsvisning", + "cut": "Klip", + "copy": "Kopier", + "paste": "Sæt ind", + "move_success": "{count, plural, one {1 element flyttet} other {# elementer flyttet}}", + "move_error": "Kunne ikke flytte", + "paste_success": "Indsat", + "paste_error": "Kunne ikke indsætte", + "new_text_file": "Ny tekstfil", + "file_name": "Filnavn", + "retry": "Prøv igen", + "refresh": "Opdatér", + "duplicate": "Duplikér", + "duplicate_success": "Duplikeret", + "duplicate_error": "Kunne ikke duplikere", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Fil oprettet", + "create_file_error": "Kunne ikke oprette fil", + "favorites": "Favoritter", + "recent": "Seneste", + "show_in_folder": "Vis i mappe", + "remove_from_recent": "Fjern fra seneste", + "upload_folder": "Upload mappe", + "file_too_large": "\"{name}\" overstiger den maksimale filstørrelse ({max})", + "undo": "Fortryd", + "undo_success": "Handling fortrydt", + "undo_error": "Kunne ikke fortryde", + "toolbar": "Filhandlinger", + "file_list": "Filer og mapper", + "context_menu": "Handlinger", + "settings_ascending": "Stigende", + "settings_descending": "Faldende", + "disabled_title": "Filer-funktionen er deaktiveret af din administrator", + "disabled_description": "Store filuploads via WebDAV kan forårsage Stalwart/RocksDB-ustabilitet, herunder hukommelsessvigt og uopretteligt diskforbrug. Slettede filer fjernes muligvis ikke straks fra blob-lageret. Denne funktion anbefales ikke til produktionsmiljøer.", + "no_accounts": "Ingen tilknyttede konti.", + "open_folder_tree": "Åbn mappetræ", + "migration_title": "Opdaterer dine filer…", + "migration_description": "Organiserer mapper og filer i deres rette struktur. Dette sker kun én gang.", + "new": "Ny", + "my_files": "Mine filer", + "folders": "Mapper", + "files_heading": "Filer", + "open": "Åbn", + "star": "Tilføj til favoritter", + "unstar": "Fjern fra favoritter", + "expand_folder": "Udvid {name}", + "collapse_folder": "Skjul {name}", + "sidebar_hint": "Favoritmapper og åbnede filer vises her.", + "breadcrumbs": "Mappesti", + "show_path": "Vis skjulte mapper i stien", + "selection_actions": "Handlinger for valgte elementer", + "selected_count": "{count, plural, one {1 valgt} other {# valgt}}", + "clear_selection": "Ryd markering", + "select_all": "Vælg alle", + "select_item": "Vælg {name}", + "more_actions": "Flere handlinger for {name}", + "paste_here": "Sæt ind i denne mappe", + "paste_count": "{count, plural, one {Sæt 1 element ind} other {Sæt # elementer ind}}", + "sort_by": "Sortér efter", + "view_mode": "Visning", + "clear_search": "Ryd søgning", + "no_results_for": "Intet i denne mappe matcher \"{query}\"", + "download_count": "{count, plural, one {Download 1 fil} other {Download # filer}}", + "delete_count": "{count, plural, one {Slet 1 element} other {Slet # elementer}}", + "close_details": "Luk detaljer", + "file_kind": "{ext}-fil", + "folder_summary": "{folders, plural, =0 {Ingen mapper} one {1 mappe} other {# mapper}}, {files, plural, =0 {ingen filer} one {1 fil} other {# filer}}", + "details_hint": "Vælg en fil eller mappe for at se detaljer.", + "sharing": "Deling", + "shared_with_count": "{count, plural, one {Delt med 1 person} other {Delt med # personer}}", + "only_you": "Kun dig", + "empty_folder_title": "Denne mappe er tom", + "empty_folder_description": "Slip filer her, eller upload dem fra din enhed.", + "empty_folder_read_only": "Du kan ikke tilføje filer til denne mappe.", + "choose_account": "Vælg en konto", + "drop_to_upload": "Slip for at uploade til {folder}", + "uploading_count": "Uploader {current} af {total}", + "dismiss": "Afvis", + "zoom_in": "Zoom ind", + "zoom_out": "Zoom ud", + "zoom_reset": "Tilpas til skærm", + "rotate": "Rotér", + "close_preview": "Luk" + }, + "smime": { + "your_certificates": "Dine certifikater", + "your_certificates_desc": "Importér og administrér dine S/MIME-certifikater til signering og kryptering af e-mails", + "recipient_certificates": "Modtagercertifikater", + "recipient_certificates_desc": "Offentlige certifikater til kryptering af e-mails til modtagere", + "identity_bindings": "Identitetsnøglebindinger", + "identity_bindings_desc": "Bind S/MIME-certifikater til dine e-mail-identiteter", + "defaults_title": "Standarder", + "defaults_desc": "Konfigurér standard signerings- og krypteringsadfærd", + "import_pkcs12": "Importér PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importér certifikat", + "no_certificates": "Ingen certifikater importeret endnu", + "no_recipient_certs": "Ingen modtagercertifikater", + "expires": "Udløber", + "expired": "Udløbet", + "bound_to": "Bundet til", + "no_key_bound": "Ingen", + "lock": "Lås nøgle", + "unlock": "Lås nøgle op", + "details": "Vis detaljer", + "delete": "Slet", + "encrypt_by_default": "Kryptér som standard", + "encrypt_by_default_desc": "Kryptér automatisk e-mails når alle modtagere har certifikater", + "remember_unlocked": "Husk oplåste nøgler", + "remember_unlocked_desc": "Hold nøgler oplåst i denne browsersessions varighed", + "sign_default_for": "Signér som standard for", + "enter_p12_passphrase": "Indtast PKCS#12-adgangssætning", + "p12_passphrase_desc": "Indtast adgangssætningen der beskytter denne certifikatfil", + "enter_storage_passphrase": "Angiv lager-adgangssætning", + "storage_passphrase_desc": "Vælg en adgangssætning for at beskytte denne nøgle på din enhed", + "next": "Næste", + "import": "Importér", + "unlock_key": "Lås nøgle op", + "unlock_key_desc": "Indtast lager-adgangssætningen for at låse denne nøgle op til signering eller dekryptering", + "passphrase_placeholder": "Indtast adgangssætning", + "confirm_passphrase_placeholder": "Bekræft adgangssætning", + "passphrase_mismatch": "Adgangssætningerne er ikke ens", + "cancel": "Annuller", + "processing": "Behandler…", + "close": "Luk", + "certificate_details": "Certifikatdetaljer", + "cert_subject": "Indehaver", + "cert_issuer": "Udsteder", + "cert_email": "E-mail", + "cert_serial": "Serienummer", + "cert_validity": "Gyldighed", + "cert_fingerprint": "Fingeraftryk (SHA-256)", + "cert_algorithm": "Algoritme", + "cert_capabilities": "Funktioner", + "cert_source": "Kilde", + "cert_expired": "Dette certifikat er udløbet", + "cert_not_yet_valid": "Dette certifikat er endnu ikke gyldigt", + "cap_sign": "Signering", + "cap_encrypt": "Kryptering", + "cap_none": "Ingen", + "show_passphrase": "Vis adgangssætning", + "hide_passphrase": "Skjul adgangssætning", + "sign_toggle": "Signér", + "encrypt_toggle": "Kryptér", + "missing_recipient_certs": "Manglende certifikater for: {emails}", + "missing_sender_cert": "Intet certifikat bundet til denne identitet", + "status_encrypted_ok": "Denne besked blev krypteret", + "status_encrypted_no_key": "Denne besked er krypteret, men ingen matchende nøgle blev fundet", + "status_encrypted_failed": "Kunne ikke dekryptere denne besked", + "status_signed_valid": "Signatur bekræftet", + "status_signed_invalid": "Signaturbekræftelse mislykkedes", + "status_signed_expired_cert": "Signeret med et udløbet certifikat", + "status_signed_self_signed": "Signatur gyldig, men certifikatet er selvsigneret (ikke betroet)", + "status_signed_mismatch": "Signatur gyldig, men underskriver matcher ikke afsender", + "status_unsupported": "Ikke-understøttet S/MIME-format", + "auto_import_signer_certs": "Auto-importér underskriver-certifikater", + "auto_import_signer_certs_desc": "Gem automatisk certifikater fra bekræftede signerede e-mails til fremtidig kryptering", + "export": "Eksportér", + "enter_export_passphrase": "Angiv eksport-adgangssætning", + "export_passphrase_desc": "Vælg en adgangssætning for at beskytte den eksporterede PKCS#12-fil", + "export_storage_desc": "Indtast lager-adgangssætningen for at dekryptere nøglen til eksport", + "incorrect_passphrase": "Forkert adgangssætning" + }, + "tour": { + "step_counter": "Trin {current} af {total}", + "skip": "Spring rundvisning over", + "back": "Tilbage", + "next": "Næste", + "finish": "Afslut", + "take_a_tour": "Tag en rundvisning af brugergrænsefladen", + "restart_title": "Introduktionsrundvisning", + "restart_desc": "Afspil den guidede gennemgang af brugergrænsefladen", + "restart_button": "Start rundvisning igen", + "show_on_new_devices_title": "Vis på nye enheder", + "show_on_new_devices_desc": "Afspil velkomstbanneret og rundvisningen første gang du logger ind på en ny enhed, selv hvis du allerede har gennemført dem andetsteds", + "sidebar_title": "Dine postkasser", + "sidebar_desc": "Dette er dit mappesidepanel. Klik på en postkasse for at se dens e-mails. Du kan oprette mapper, trække e-mails mellem dem og se ulæste antal på et øjeblik.", + "compose_title": "Skriv en e-mail", + "compose_desc": "Klik her for at skrive en ny e-mail. Du kan tilføje modtagere, vedhæftninger og bruge rig tekstformatering.", + "search_title": "Søg i din mail", + "search_desc": "Søg efter afsender, emne eller indhold. Klik på filterikonet for avancerede muligheder som datoperiode, vedhæftninger og stjernemarkerede beskeder.", + "email_list_title": "Din e-mail-liste", + "email_list_desc": "E-mails vises her. Klik på en for at læse den til højre. Brug afkrydsningsfeltet til at vælge flere, og masseflyt, -slet eller -tag dem.", + "email_viewer_title": "Læserude", + "email_viewer_desc": "Den valgte e-mail åbnes her. Svar, videresend, arkivér eller slet med værktøjslinjeknapperne. Du kan også stjernemarkere e-mails eller tilføje farvetags.", + "keywords_title": "Farvetags", + "keywords_desc": "Organisér din e-mail med farvekodede tags. Træk en e-mail til et tag for at mærke den, eller højreklik på en e-mail for at tildele tags.", + "calendar_title": "Kalender", + "calendar_desc": "Skift til kalenderen for at administrere dine begivenheder. Opret begivenheder, sæt påmindelser, og se dag-, uge- eller månedslayout.", + "contacts_title": "Kontakter", + "contacts_desc": "Din adressebog bor her. Importér kontakter, opret grupper, og klik på en kontakt for at se deres fulde detaljer.", + "settings_title": "Indstillinger", + "settings_desc": "Tilpas alt: tema, tæthed, signaturer, filtre, tastaturgenveje, kalenderstandarder og meget mere.", + "shortcuts_title": "Tastaturgenveje", + "shortcuts_desc": "Strømbrugere elsker dette. Tryk ? når som helst for at se alle tilgængelige genveje. Du kan navigere, skrive og administrere e-mails uden at røre musen.", + "compose_open_title": "Skrivevinduet", + "compose_open_desc": "Dette er skrivevinduet. Tilføj modtagere, skriv din besked, vedhæft filer, og brug rig tekstformatering. Du kan også gemme kladder og bruge skabeloner.", + "calendar_view_title": "Din kalender", + "calendar_view_desc": "Her er din kalender med eksempelbegivenheder. Du kan skifte mellem dag-, uge-, måneds- og agendavisning ved hjælp af værktøjslinjen.", + "create_event_title": "Opret en begivenhed", + "create_event_desc": "Klik på denne knap for at oprette en ny kalenderbegivenhed. Du kan angive en titel, dato, tid og tilføje deltagere.", + "event_modal_title": "Begivenhedsdetaljer", + "event_modal_desc": "Her er begivenhedsformularen. Udfyld titlen, vælg dato og tidspunkt, tilføj et sted eller deltagere. Tryk gem, når du er færdig - eller luk den og gå videre.", + "contacts_list_title": "Dine kontakter", + "contacts_list_desc": "Her er dine kontakter. Klik på en kontakt for at se fulde detaljer til højre. Du kan også oprette nye kontakter, importere vCards eller organisere kontakter i grupper.", + "settings_tabs_title": "Indstillingsmenu", + "settings_tabs_desc": "Her er alle indstillingskategorier. Tilpas dit udseende, administrér identiteter, opsæt e-mail-filtre, konfigurér din kalender og meget mere.", + "files_title": "Fillager", + "files_desc": "Din filbrowser lader dig uploade, organisere og dele filer - som en personlig cloud-drev indbygget i din mail.", + "demo_banner_title": "Demo-kontroller", + "demo_banner_desc": "Du er i demo-tilstand - alt forbliver i din browser. Tryk 'Nulstil demo' når som helst for at starte forfra med rent eksempeldata.", + "quota_title": "Lagerforbrug", + "quota_desc": "Spor din postkassestørrelse her. Cirklen fyldes op, efterhånden som du bruger mere plads." + }, + "unified_mailbox": { + "search_unavailable": "Søgning er ikke tilgængelig i den samlede visning" + }, + "quote_header": { + "reply_line": "Den {date} skrev {from}:", + "forwarded_separator": "---------- Videresendt besked ----------", + "from_label": "Fra", + "date_label": "Dato", + "subject_label": "Emne" + }, + "pwa_install": { + "title": "Installer {appName}", + "description": "Installer vores app for hurtig adgang og offline-understøttelse.", + "not_now": "Ikke nu", + "install": "Installer", + "dont_remind": "Påmind mig ikke igen", + "dismiss_aria": "Afvis installationsprompt" + }, + "deep_link": { + "message_not_found": "Denne besked er ikke længere tilgængelig.", + "thread_not_found": "Denne samtale er ikke længere tilgængelig.", + "folder_not_found": "Denne mappe er ikke længere tilgængelig.", + "event_not_found": "Denne begivenhed er ikke længere tilgængelig.", + "file_not_found": "Denne fil er ikke længere tilgængelig.", + "account_unavailable": "Log ind på den konto for at åbne dette link.", + "copy_message": "Kopier link til besked", + "copy_conversation": "Kopier link til samtale", + "copy_event": "Kopier link til begivenhed", + "copied": "Link kopieret", + "copy_failed": "Kunne ikke kopiere linket" + }, + "plugins": { + "oauth_callback": { + "completing": "Fuldfører godkendelse…", + "completing_message": "Vent venligst, mens forbindelsen oprettes.", + "received_title": "Godkendelse modtaget", + "received_message": "Du kan vende tilbage til den fane, hvor du startede forbindelsen.", + "failed_title": "Godkendelse mislykkedes", + "denied": "Godkendelse blev nægtet.", + "failed_detail": "Godkendelse mislykkedes: {error}", + "missing_params": "Autorisationskode eller tilstand mangler.", + "delivery_failed": "Resultatet af godkendelsen kunne ikke leveres til app-fanen. Luk dette vindue og prøv igen." + }, + "settings": { + "title": "Plugins", + "description": "Plugins, som din administrator har tilføjet. Slå dem til eller fra for din konto.", + "description_uploads": "Plugins tilføjer funktioner til webmailen. Slå dem til eller fra for din konto, eller installer dine egne.", + "description_demo": "Plugins er ikke tilgængelige i demoen.", + "install_button": "Installer plugin", + "install_hint": "Vælg en ZIP-fil med et plugin.", + "install_hint_approval": "Vælg en ZIP-fil med et plugin. En administrator skal godkende det, før du kan slå det til.", + "install_uploads_off": "Din administrator tillader ikke, at du installerer plugins.", + "install_managed_id": "Din administrator leverer allerede et plugin med dette navn, så det kan ikke erstattes.", + "install_invalid": "Filen er ikke et plugin, der kan installeres: {details}", + "toast_installed": "Plugin installeret. Slå det til herunder.", + "toast_installed_pending": "Plugin installeret og sendt til din administrator til godkendelse.", + "toast_enabled": "{name} er slået til", + "toast_disabled": "{name} er slået fra", + "toast_removed": "{name} er fjernet", + "toast_forced": "Din administrator holder {name} slået til", + "toast_approval_requested": "{name} venter på godkendelse fra din administrator", + "empty_title": "Ingen plugins endnu", + "empty_admin": "Din administrator har ikke tilføjet nogen plugins.", + "empty_uploads": "Installer et plugin fra en ZIP-fil for at komme i gang.", + "empty_demo": "Plugins kræver en rigtig konto, så demoen har ingen.", + "syncing": "Tjekker plugins med serveren…", + "status_installed": "Fra", + "status_enabled": "Starter", + "status_running": "Til", + "status_disabled": "Fra", + "status_error": "Problem", + "badge_forced": "Påkrævet", + "badge_managed": "Fra din administrator", + "badge_yours": "Installeret af dig", + "badge_awaiting": "Venter på godkendelse", + "toggle_label": "Slå {name} til eller fra", + "forced_note": "Din administrator kræver dette plugin, så det kan ikke slås fra.", + "permissions": "Hvad det kan", + "permissions_managed": "Hvad det kan (godkendt af din administrator)", + "settings": "Indstillinger", + "remove": "Fjern", + "remove_title": "Fjern {name}?", + "remove_message": "Pluginnet og alt, hvad det har gemt i denne browser, slettes. Du kan installere det igen senere.", + "remove_confirm": "Fjern", + "error_user_plugins_off": "Din administrator har slået plugins installeret af brugere fra, så dette plugin kører ikke.", + "error_approval_unavailable": "Dette plugin blev installeret, før godkendelse var påkrævet. Fjern det, og installer det igen for at sende det til godkendelse.", + "error_approval_denied": "Din administrator har ikke godkendt dette plugin.", + "error_approval_pending": "Venter på, at din administrator godkender dette plugin.", + "error_auto_disabled": "Slået fra, fordi det blev ved med at fejle. Slå det til igen for at prøve igen.", + "accounts_heading": "Forbundne konti", + "account_on": "{label} på {host}", + "account_waiting": "{label} på {host} (login ikke afsluttet)", + "disconnect": "Afbryd forbindelse", + "disconnect_failed": "Kunne ikke afbryde forbindelsen til {label}" + }, + "consent": { + "title": "Vil du tillade {name} at gøre dette?", + "intro": "Pluginnet beder om tilladelserne herunder. Det får dem kun, hvis du tillader det.", + "high_risk_note": "Markerede punkter giver pluginnet adgang til indholdet af dine e-mails eller lader det sende data andre steder hen. Tillad dem kun for plugins, du stoler på.", + "revoke_hint": "Du kan altid slå pluginnet fra eller fjerne det under Indstillinger.", + "deny": "Tillad ikke", + "allow": "Tillad" + }, + "permissions": { + "none": "Det kræver ingen særlige tilladelser.", + "high_risk": "Kræver omtanke", + "email_read": { + "title": "Læse dine e-mails", + "body": "Se afsendere, modtagere, emner og den fulde tekst i alle beskeder på din konto samt dine mapper." + }, + "email_write": { + "title": "Ændre og organisere dine e-mails", + "body": "Tilføje eller fjerne tags, markeringer og andre nøgleord på dine beskeder, flytte dem mellem dine mapper og til arkivet og gemme kladder." + }, + "email_send": { + "title": "Ændre e-mails, før de sendes", + "body": "Læse og ændre modtagere, emne, tekst og vedhæftninger i de beskeder, du skriver, og forhindre, at en besked bliver sendt." + }, + "crypto_full": { + "title": "Fuld adgang til denne app", + "body": "Kører med de samme rettigheder som selve webmailen. Det kan læse alle dine e-mails og gemte nøgler og handle på dine vegne. Tillad det kun for plugins, du stoler fuldt ud på." + }, + "email_raw_send": { + "title": "Sende beskeder, det selv har lavet", + "body": "Sende færdige beskeder, f.eks. signerede eller krypterede, fra din konto." + }, + "email_blob_read": { + "title": "Læse beskeders rå indhold", + "body": "Læse de originale data i dine beskeder og vedhæftninger og i alle filer, du vedhæfter, før de sendes." + }, + "email_blob_write": { + "title": "Erstatte vedhæftninger før afsendelse", + "body": "Uploade filer til din mailserver og erstatte en vedhæftet fil med en anden, før beskeden sendes." + }, + "email_render_takeover": { + "title": "Ændre, hvordan beskeder vises", + "body": "Erstatte teksten i de beskeder, du åbner, og i beskedlisten, f.eks. for at vise dekrypteret indhold." + }, + "calendar_read": { + "title": "Læse din kalender", + "body": "Se dine kalendere og deres begivenheder." + }, + "calendar_write": { + "title": "Ændre din kalender", + "body": "Oprette, ændre og slette begivenheder." + }, + "contacts_read": { + "title": "Læse dine kontakter", + "body": "Søge i og læse dine adressebøger." + }, + "contacts_write": { + "title": "Ændre dine kontakter", + "body": "Oprette, ændre og slette kontakter og adressebøger." + }, + "files_read": { + "title": "Læse dine filer", + "body": "Gennemse og åbne de filer, der er gemt på din konto." + }, + "files_write": { + "title": "Ændre dine filer", + "body": "Uploade, omdøbe, flytte og slette filer på din konto." + }, + "identity_read": { + "title": "Se dine afsenderidentiteter", + "body": "Se de adresser, du sender fra, og deres signaturer." + }, + "identity_write": { + "title": "Ændre dine afsenderidentiteter", + "body": "Oprette, ændre og slette afsenderidentiteter og signaturer." + }, + "filters_read": { + "title": "Læse dine mailfiltre", + "body": "Se de regler, der sorterer din indgående mail." + }, + "filters_write": { + "title": "Ændre dine mailfiltre", + "body": "Tilføje regler til dine filtre på serveren. Sådanne regler kan flytte, videresende eller slette indgående mail." + }, + "tasks_read": { + "title": "Læse dine opgaver", + "body": "Se dine opgavelister og opgaver." + }, + "tasks_write": { + "title": "Ændre dine opgaver", + "body": "Oprette, ændre, fuldføre og slette opgaver." + }, + "templates_read": { + "title": "Læse dine skabeloner", + "body": "Se dine gemte beskedskabeloner." + }, + "templates_write": { + "title": "Ændre dine skabeloner", + "body": "Oprette, ændre og slette beskedskabeloner." + }, + "smime_read": { + "title": "Se din S/MIME-status", + "body": "Se, om du har S/MIME-nøgler og -certifikater, uden adgang til nøglerne." + }, + "vacation_read": { + "title": "Se dit fraværssvar", + "body": "Se, om dit automatiske svar er slået til, og hvad der står i det." + }, + "vacation_write": { + "title": "Ændre dit fraværssvar", + "body": "Slå dit automatiske svar til eller fra og ændre teksten og datoerne." + }, + "settings_read": { + "title": "Læse dine indstillinger", + "body": "Se dine præferencer og tagdefinitioner." + }, + "settings_write": { + "title": "Ændre dine indstillinger", + "body": "Tilføje og omarrangere tags og ændre dine præferencer." + }, + "security_read": { + "title": "Se din kontos sikkerhed", + "body": "Se, om to-faktor-godkendelse og kryptering er slået til, uden adgang til hemmeligheder." + }, + "auth_observe": { + "title": "Vide, når du logger ind", + "body": "Få besked, når du logger ind, skifter konto, eller din session fornyes." + }, + "auth_emit": { + "title": "Logge dig ud", + "body": "Logge dig ud af webmailen." + }, + "account_read": { + "title": "Se dine konti", + "body": "Se de konti, du har tilføjet, med deres adresser og servere." + }, + "http_post": { + "title": "Bruge webmail-serveren for dig", + "body": "Kalde disse server-endpoints med dit login:" + }, + "http_fetch": { + "title": "Forbinde til andre websteder", + "body": "Sende forespørgsler til disse websteder. Alt, hvad pluginnet kan læse, kan blive sendt dertil:" + }, + "admin_config": { + "title": "Læse sine indstillinger fra din administrator", + "body": "Læse de indstillinger, din administrator har gemt for dette plugin." + }, + "ui_observe": { + "title": "Følge med i, hvor du er i appen", + "body": "Vide, hvilken side der er åben, og reagere på links, du klikker på." + }, + "ui_toolbar": { + "title": "Tilføje knapper til værktøjslinjer", + "body": "Vise sine egne knapper på beskedernes værktøjslinje, på en åben besked og på den øverste bjælke." + }, + "ui_app_top_banner": { + "title": "Vise et banner øverst", + "body": "Vise en meddelelse hen over toppen af appen." + }, + "ui_email_banner": { + "title": "Vise meddelelser på beskeder", + "body": "Vise sine egne meddelelser og resuméer over, i og mellem delene af en åben besked eller samtale." + }, + "ui_email_footer": { + "title": "Tilføje indhold under beskeder", + "body": "Vise sit eget indhold under en åben besked." + }, + "ui_contact_cryptokeys": { + "title": "Vise kontakters krypteringsnøgler", + "body": "Vise og administrere krypteringsnøglerne på en kontakt." + }, + "ui_email_details": { + "title": "Tilføje paneler til beskeddetaljer", + "body": "Vise sit eget panel ved siden af eller under en åben besked." + }, + "ui_download_file": { + "title": "Gemme filer på din enhed", + "body": "Tilbyde filer, det har lavet, til download." + }, + "ui_message_list_tabs": { + "title": "Tilføje faner til din indbakke", + "body": "Sortere din indbakke i faner som Opdateringer eller Kampagner." + }, + "ui_composer_toolbar": { + "title": "Tilføje til skrivevinduet", + "body": "Vise sine egne knapper, menupunkter og sektioner, mens du skriver en besked." + }, + "ui_composer_sidebar": { + "title": "Tilføje et panel til skrivevinduet", + "body": "Vise sit eget panel ved siden af den besked, du skriver." + }, + "ui_sidebar_widget": { + "title": "Tilføje til sidepanelet", + "body": "Vise sine egne bokse og sektioner i mailens sidepanel." + }, + "ui_settings_section": { + "title": "Tilføje en indstillingsside", + "body": "Vise sin egen side under Indstillinger." + }, + "ui_context_menu": { + "title": "Tilføje til beskedmenuen", + "body": "Tilføje punkter til den menu, der åbner, når du højreklikker på en besked." + }, + "ui_navigation_rail": { + "title": "Tilføje til navigationslinjen", + "body": "Vise sin egen knap på navigationslinjen." + }, + "ui_keyboard": { + "title": "Tilføje tastaturgenveje", + "body": "Reagere på tastaturgenveje, som det selv definerer." + }, + "ui_calendar_action": { + "title": "Tilføje til kalendervisningen", + "body": "Vise sine egne knapper og sektioner på begivenheder og korte etiketter og prikker på kalenderdage." + }, + "ui_admin_page": { + "title": "Tilføje en administratorside", + "body": "Vise sin egen side i administratorindstillingerne." + }, + "app_lifecycle": { + "title": "Vide, når appen starter og stopper", + "body": "Få besked, når appen starter, går offline, eller du logger ud." + }, + "compose_open": { + "title": "Åbne skrivevinduet for dig", + "body": "Åbne en ny besked med modtagere, tekst eller vedhæftninger udfyldt. Du bestemmer stadig, om den sendes." + }, + "compose_read": { + "title": "Læse den besked, du skriver", + "body": "Læse teksten i en besked, mens du skriver den, når du bruger pluginet i editoren. Ikke signaturen eller den citerede besked." + }, + "compose_write": { + "title": "Ændre den besked, du skriver", + "body": "Erstatte eller tilføje tekst i en besked, mens du skriver den, når du bruger pluginet i editoren. Du kan fortryde hver ændring, og du bestemmer stadig, om den sendes." + }, + "ui_notifications": { + "title": "Vise notifikationer", + "body": "Vise notifikationer på din enhed eller i appen, hvis du ikke har tilladt dem. Hver af dem nævner pluginets navn." + }, + "ui_message_list": { + "title": "Tilføje til beskedlisten", + "body": "Vise sit eget indhold over beskedlisten og knapper på beskeder og på værktøjslinjen for valgte beskeder." + }, + "ui_contacts": { + "title": "Tilføje til kontaktvisningen", + "body": "Vise sine egne sektioner i en kontakts detaljer og knapper i kontaktlisten." + }, + "ui_tasks": { + "title": "Tilføje til opgavevisningen", + "body": "Vise sine egne sektioner i en opgaves detaljer og knapper i opgavelisten." + }, + "ui_files": { + "title": "Tilføje til filvisningen", + "body": "Vise sine egne sektioner i forhåndsvisningen af filer og punkter i en fils menu." + }, + "ui_commands": { + "title": "Tilføje kommandoer", + "body": "Tilføje sine egne punkter til kommandopaletten (Ctrl+K)." + }, + "ui_app_page": { + "title": "Tilføje sider", + "body": "Tilføje sine egne sider til appen, hver med en knap på navigationslinjen." + }, + "http_proxy": { + "title": "Bruge konti, du forbinder", + "body": "Sende forespørgsler via Bulwarks server med en konto eller nøgle, som du eller din administrator har sat op. Pluginnet ser aldrig adgangskoden, men det kan gøre alt, hvad kontoen kan gøre på sin egen server." + } + }, + "dialog": { + "from_plugin": "Fra pluginet {name}", + "ok": "OK", + "submit": "Send", + "confirm": "Bekræft", + "cancel": "Annuller", + "close": "Luk", + "turn_off": "Slå {name} fra", + "plugin_menu": "Indstillinger for {name}", + "password_note": "Denne formular kommer fra pluginet {name}, ikke fra webmailen. Indtast kun en adgangskode, hvis du stoler på dette plugin.", + "toast_auto_disabled": "{name} blev slået fra, fordi det blev ved med at fejle." + }, + "connect": { + "title": "Forbind {label}", + "intro_password": "{name} vil bruge din konto hos {label}. Bulwark gemmer adgangskoden på sin server og sender den kun til {host}. Pluginnet ser den aldrig.", + "intro_password_any": "{name} vil bruge din konto hos {label}. Bulwark gemmer adgangskoden på sin server og sender den kun til den server, du angiver her. Pluginnet ser den aldrig.", + "intro_signin": "{name} vil logge ind på din konto hos {label}. Angiv serveren her, og log derefter ind på serverens egen side. Bulwark gemmer den adgang, det får, på sin server; pluginnet ser den aldrig.", + "server": "Serveradresse", + "server_hint": "cloud.example.com", + "username": "Brugernavn", + "secret": "Adgangskode eller nøgle", + "connect": "Forbind", + "continue": "Fortsæt", + "cancel": "Annuller", + "saving": "Gemmer…", + "error_server": "Angiv serverens adresse, for eksempel https://cloud.example.com", + "error_save": "Kunne ikke gemme: {error}" + } + }, + "global_search": { + "title": "Søg i alt", + "placeholder": "Søg i mail, kontakter, kalender, filer...", + "scope_label": "Søg i", + "account_label": "Konto", + "scope_all": "Alt", + "scope_mail": "Mail", + "scope_contacts": "Kontakter", + "scope_calendar": "Kalender", + "scope_files": "Filer", + "all_accounts": "Alle konti", + "show_all": "Vis alle resultater", + "no_results": "Ingen resultater for “{query}”", + "server_hint": "Hele ord søger på serveren; dele af ord matcher allerede indlæste elementer", + "searching": "Søger...", + "account_error": "Kunne ikke søge i {account}", + "recent_searches": "Seneste søgninger", + "recurring": "Tilbagevendende", + "no_subject": "(no subject)", + "empty_hint": "Søg i mail, kontakter, begivenheder og filer på alle konti. Prøv from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Fladt efter dato", + "grouped": "Grupperet", + "load_more": "Indlæs flere", + "preview_empty": "Vælg et resultat for at se det", + "open_full": "Åbn", + "view_label": "Visning" + }, + "top_bar": { + "more": "Flere muligheder" + }, + "plugin_pages": { + "loading": "Indlæser side", + "not_available_title": "Denne side er ikke tilgængelig", + "not_available_description": "Det plugin, der tilføjede den, er slået fra eller ikke længere installeret.", + "back_to_mail": "Tilbage til mail", + "settings_group": "Plugins" + }, + "command_palette": { + "title": "Kommandopalet", + "open": "Åbn kommandopaletten", + "placeholder": "Skriv en kommando", + "no_results": "Ingen matchende kommandoer", + "go_to": "Gå til {name}", + "compose": "Ny besked", + "search": "Søg i mail", + "theme_dark": "Skift til mørkt tema", + "theme_light": "Skift til lyst tema", + "shortcuts": "Vis tastaturgenveje", + "group_navigation": "Gå til", + "group_actions": "Handlinger", + "group_plugins": "Plugins" + }, + "notification_center": { + "title": "Notifikationer", + "mark_all_read": "Markér alle som læst", + "clear": "Ryd", + "empty": "Ingen notifikationer", + "close": "Luk", + "unread": "Ulæst", + "unread_count": "{count} ulæste", + "level_error": "Fejl", + "level_warning": "Advarsel" + } +} diff --git a/locales/de/common.json b/locales/de/common.json index 8ca3ee0f..7341f54c 100644 --- a/locales/de/common.json +++ b/locales/de/common.json @@ -17,17 +17,24 @@ "reconnecting": "Verbindung verloren. Verbindung wird wiederhergestellt…", "error": { "invalid_credentials": "Ungültige E-Mail-Adresse oder Passwort", + "invalid_token": "Der Server hat dieses Zugriffstoken nicht akzeptiert. Prüfen Sie, ob es vollständig ist und nicht abgelaufen oder widerrufen wurde.", "connection_failed": "Verbindung zum Server fehlgeschlagen", "cors_blocked": "Der Server ist erreichbar, blockiert aber Cross-Origin-Anfragen. Überprüfen Sie die CORS-Einstellungen Ihres JMAP-Servers und erlauben Sie diese Domain.", "server_error": "Der Server ist vorübergehend nicht erreichbar. Bitte versuchen Sie es später erneut.", "generic": "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.", "totp_required": "Ein Zwei-Faktor-Authentifizierungscode ist erforderlich. Bitte geben Sie Ihren Code unten ein.", "totp_invalid": "Ungültiger Authentifizierungscode. Überprüfen Sie Ihre Authenticator-App.", - "oauth_discovery_failed": "SSO ist aktiviert, aber der Identitätsanbieter ist nicht erreichbar. Überprüfen Sie Ihre OAuth-Konfiguration." + "oauth_discovery_failed": "SSO ist aktiviert, aber der Identitätsanbieter ist nicht erreichbar. Überprüfen Sie Ihre OAuth-Konfiguration.", + "token_exchange_failed": "Passwort und Code wurden akzeptiert, aber der Mailserver hat für diese App keine Sitzung gestartet. Bitten Sie Ihre Administration, die OAuth-Client-Einstellungen zu prüfen." }, "show_password": "Passwort anzeigen", "hide_password": "Passwort verbergen", "totp_toggle": "Ich habe einen 2FA-Code", + "token_toggle": "Mit Zugriffstoken anmelden", + "password_toggle": "Mit Passwort anmelden", + "token_label": "Zugriffstoken", + "token_placeholder": "API-Token einfügen", + "token_hint": "Erstellen Sie in den Einstellungen Ihres E-Mail-Anbieters ein API-Token und fügen Sie es hier ein.", "remember_me": "Angemeldet bleiben", "config_error": { "title": "Konfigurationsfehler", @@ -53,6 +60,12 @@ "demo_login_button": "Demo starten", "demo_tagline": "Erleben Sie einen voll ausgestatteten E-Mail-Client. Kein Konto erforderlich.", "demo_no_signup": "Keine Registrierung nötig - erkunden Sie frei mit Beispieldaten", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, "oauth_completing": "Anmeldung wird abgeschlossen...", "oauth_error": { "title": "Authentifizierung fehlgeschlagen", @@ -114,6 +127,7 @@ "unified_archive": "Alle Archive", "unified_junk": "Alle Spam", "all_accounts": "Alle Konten", + "all_accounts_loading": "Konten werden geladen: {loaded} von {total}", "unified_mailbox": "Gemeinsames Postfach", "expand": "Erweitern", "collapse": "Einklappen", @@ -134,16 +148,32 @@ "show_all_tags": "Alle anzeigen ({count})", "show_fewer_tags": "Weniger anzeigen", "folders": "Ordner", + "your_folders": "Eigene Ordner", + "create_folder": "Ordner erstellen", + "show_more": "Mehr", + "show_fewer": "Weniger", "mail": "E-Mail", "nav_label": "Navigation", "add_app": "Apps", "shared": "Geteilt", + "shared_account_hidden": "In der Seitenleiste ausgeblendet. Unter Einstellungen › Layout wieder einblenden.", + "notifications_muted": "Benachrichtigungen stummgeschaltet", "scheduled": "Geplant", "unified_all_unread": "Alle ungelesenen", "unified_all_starred": "Alle markierten", "unified_all_mail": "Alle Mails", "remove_account": "Konto entfernen", - "remove_account_confirm": "{account} von diesem Gerät entfernen? Sie können das Konto später wieder hinzufügen." + "remove_account_confirm": "{account} von diesem Gerät entfernen? Sie können das Konto später wieder hinzufügen.", + "labels": "Labels", + "create_label": "Neues Label erstellen", + "mv_tasks": "Aufgaben", + "mv_side_panel": "Seitenleiste", + "mv_open_full": "In voller Ansicht öffnen", + "mv_previous_day": "Vorheriger Tag", + "mv_next_day": "Nächster Tag", + "mv_add_another_account": "Weiteres Konto hinzufügen", + "mv_manage_account": "Konto verwalten", + "mv_storage_used": "{percent} % von {total} belegt" }, "protocol_handlers": { "title": "Standard-Apps", @@ -214,6 +244,13 @@ "show_on_mobile": "Auf Mobilgerät anzeigen" }, "email_list": { + "columns": { + "from": "Von", + "to": "An", + "subject": "Betreff", + "date": "Datum", + "size": "Größe" + }, "no_emails": "Keine Nachrichten gefunden", "no_emails_description": "Dieses Postfach ist leer", "no_search_results": "Keine Ergebnisse gefunden", @@ -229,6 +266,7 @@ "replied": "Beantwortet", "forwarded": "Weitergeleitet", "has_attachment": "Mit Anhang", + "conversations_range": "{from}–{to, number} von {total, number}", "conversations_count": "{count} von {total} Unterhaltungen", "conversations_count_plus": "{count}+ Unterhaltungen", "conversations_count_simple": "{count} Unterhaltungen", @@ -254,6 +292,7 @@ "button": "Ordner leeren", "confirm_title": "Ordner leeren", "confirm_message": "Alle E-Mails in diesem Ordner werden dauerhaft gelöscht. Diese Aktion kann nicht rückgängig gemacht werden.", + "confirm_message_trash": "Alle E-Mails in diesem Ordner werden in den Papierkorb verschoben.", "confirm_button": "Ordner leeren", "junk_hint": "Sie können den Spam-Ordner leeren, um alle Nachrichten dauerhaft zu entfernen.", "trash_hint": "Sie können den Papierkorb leeren, um alle Nachrichten dauerhaft zu entfernen." @@ -296,7 +335,7 @@ "forward": "Weiterleiten", "delete": "Löschen", "archive": "Archivieren", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "archive_mailbox_not_found": "Archivordner nicht gefunden, die E-Mail kann nicht archiviert werden", "star": "Stern hinzufügen", "unstar": "Stern entfernen", "mark_unread": "Als ungelesen markieren", @@ -310,6 +349,8 @@ "view_source": "Quelltext anzeigen", "fullscreen": "Vollbild", "exit_fullscreen": "Vollbild beenden", + "view_in_dark_mode": "Im dunklen Modus anzeigen", + "view_in_light_mode": "Im hellen Modus anzeigen", "export_email": "Als .eml exportieren", "forward_as_attachment": "Als Anhang weiterleiten", "import_email": ".eml oder .zip importieren", @@ -334,6 +375,15 @@ "external_content_warning": "Bilder und externe Inhalte wurden blockiert", "load_external_content": "Bilder laden", "trust_sender": "Diesem Absender immer vertrauen", + "sender_check": { + "unverified_label": "Absender nicht bestätigt", + "failed_label": "Absenderprüfung fehlgeschlagen", + "unverified": "Diese Nachricht gibt vor, von {domain} zu kommen, aber nichts bestätigt das. Sie trägt keine gültige Signatur von {domain} und kam nicht von einem Server, den {domain} zulässt.", + "unverified_sent_from": "Diese Nachricht gibt vor, von {domain} zu kommen, wurde aber von {host} verschickt und trägt keine gültige Signatur von {domain}.", + "failed": "Diese Nachricht gibt vor, von {domain} zu kommen, hat aber die Absenderprüfung dieser Domain nicht bestanden. Sie ist möglicherweise gefälscht.", + "failed_sent_from": "Diese Nachricht gibt vor, von {domain} zu kommen, wurde aber von {host} verschickt und hat die Absenderprüfung von {domain} nicht bestanden. Sie ist möglicherweise gefälscht.", + "caution": "Seien Sie vorsichtig mit Links, Anhängen und Fragen nach Passwörtern oder Zahlungsdaten." + }, "back_to_list": "Zurück zur Liste", "view_contact": "Kontakt anzeigen", "message_details": "Nachrichtendetails", @@ -552,6 +602,8 @@ }, "scheduled_banner": "Geplant zum Senden um {date}", "scheduled_send_created": "E-Mail wurde zum Senden geplant", + "sending_in": "Wird in {seconds} s gesendet", + "undo_send_failed": "Rückgängig machen nicht möglich. Die Nachricht ist vielleicht schon gesendet.", "cancel_scheduled_send": "Senden abbrechen", "reschedule_send": "Neu planen", "cancel_and_edit": "Abbrechen und bearbeiten", @@ -576,19 +628,25 @@ "not_in_contacts": "Nicht in Ihren Kontakten", "add_to_contacts": "Zu Kontakten hinzufügen", "copied": "Kopiert!", - "copy_failed": "Kopieren fehlgeschlagen" + "copy_failed": "Kopieren fehlgeschlagen", + "view_contact": "Kontakt ansehen", + "view_details": "Details ansehen" }, - "send_now": "Jetzt senden" + "send_now": "Jetzt senden", + "verification_code": { + "copy": "Code {code} kopieren", + "copied": "Code kopiert", + "copy_failed": "Code konnte nicht kopiert werden" + }, + "mv_position": "{index} von {total}", + "plugin_panel_show": "Plugin-Bereich einblenden", + "plugin_panel_hide": "Plugin-Bereich ausblenden" }, "email_composer": { - "read_receipt_on": "Lesebestätigung angefordert (klicken zum Deaktivieren)", - "read_receipt_off": "Lesebestätigung anfordern", - "dsn_on": "Zustellbenachrichtigung angefordert (zum Deaktivieren klicken)", - "dsn_off": "Zustellbenachrichtigung anfordern", - "format_plain_text": "Zu Nur-Text wechseln", - "format_rich_text": "Zu formatiertem Text (HTML) wechseln", - "require_tls_on": "Verschlüsselte Zustellung erforderlich (zum Deaktivieren klicken)", - "require_tls_off": "Verschlüsselte Zustellung verlangen (TLS)", + "select_from_directory": "Aus Verzeichnis auswählen", + "minimize": "Minimieren", + "maximize": "Vollbild", + "restore": "Wiederherstellen", "new_message": "Neue Nachricht", "signature_edit_hint": "Doppelklick, um die Signatur zu bearbeiten", "reply": "Antworten", @@ -608,9 +666,16 @@ "attach": "Anhängen", "attach_photos": "Fotos & Videos", "attach_files": "Dateien", + "attach_from_files": "Aus Dateien anhängen", + "files_picker": { + "attach": "{count, plural, =0 {Anhängen} one {1 Datei anhängen} other {# Dateien anhängen}}", + "empty_folder": "Dieser Ordner ist leer", + "load_failed": "Ihre Dateien konnten nicht geladen werden" + }, "discard": "Verwerfen", "discard_draft_title": "Entwurf verwerfen?", "discard_draft_confirm": "Sie haben ungespeicherte Änderungen. Möchten Sie diesen Entwurf verwerfen?", + "discard_draft_message": "Der Entwurf wird gelöscht.", "saving": "Wird gespeichert...", "draft_saved": "Entwurf gespeichert", "save_failed": "Speichern fehlgeschlagen", @@ -626,6 +691,7 @@ "forward": "WG:", "reply": "AW:" }, + "forward_batch_count": "{count, plural, one {1 E-Mail} other {# E-Mails}}", "no_subject": "(Kein Betreff)", "unknown_sender": "Unbekannt", "quote": { @@ -647,11 +713,27 @@ "email_placeholder": "alias@example.com", "envelope_notice": "Akzeptiert Ihr Server diese Adresse nicht als Umschlag-Absender, wird dort {identity} verwendet, und Empfänger können sie in der Kopfzeile Return-Path sehen." }, - "use_template": "Vorlage", "save_as_template": "Als Vorlage speichern", + "attach_from_device": "Von diesem Gerät hochladen", + "templates_menu": "Vorlagen", + "insert_template": "Vorlage einfügen", + "options": "Optionen", + "option_plain_text": "Nur Text", + "option_plain_text_hint": "Ohne Formatierung senden", + "option_read_receipt": "Lesebestätigung anfordern", + "option_read_receipt_hint": "Empfänger bitten, das Öffnen zu bestätigen", + "option_dsn": "Zustellbenachrichtigung", + "option_dsn_hint": "Bericht erhalten, sobald der Server zustellt", + "option_require_tls": "Verschlüsselte Zustellung erzwingen", + "option_require_tls_hint": "Nur über TLS zustellen, sonst zurückweisen", + "option_priority": "Wichtigkeit", + "option_priority_high": "Hoch", + "option_priority_normal": "Normal", + "option_priority_low": "Niedrig", "validation": { "recipient_required": "Empfänger hinzufügen zum Senden", "body_required": "Nachricht schreiben oder Datei anhängen", + "invalid_recipients": "Nicht gesendet - prüfen Sie diese Adressen: {addresses}", "attachments_uploading": "Anhänge werden noch hochgeladen – wird gesendet, sobald sie fertig sind", "attachment_upload_failed": "Nicht gesendet - ein Anhang konnte nicht hochgeladen werden. Entfernen Sie ihn und versuchen Sie es erneut." }, @@ -671,6 +753,11 @@ "smime_unlock_button": "Entsperren", "smime_passphrase_placeholder": "S/MIME-Passphrase", "send_timeout": "Der Server antwortet nicht mehr. Prüfen Sie den Ordner „Gesendet“, bevor Sie erneut senden – die Nachricht wurde möglicherweise bereits verschickt.", + "send_offline": "Sie scheinen offline zu sein. Die Nachricht ist noch da, senden Sie sie erneut, sobald Sie wieder online sind.", + "formatting": "Formatierung", + "send_recipients_rejected": "Nicht gesendet – der Server hat alle Empfänger abgelehnt.", + "send_plugin_failed": "Nicht gesendet – ein Plugin, das den Versand übernimmt, ist fehlgeschlagen oder hat zu lange gebraucht. Prüfen Sie den Ordner „Gesendet“, bevor Sie erneut senden.", + "send_some_recipients_rejected": "Gesendet, aber nicht an diese Empfänger – der Server hat sie abgelehnt.", "continue_draft": "Entwurf fortsetzen", "close_draft_title": "Entwurf speichern oder verwerfen?", "close_draft_message": "Sie haben ungespeicherte Änderungen. Möchten Sie diese als Entwurf speichern oder verwerfen?", @@ -694,6 +781,11 @@ "sending": "Wird gesendet...", "schedule_send": "Senden planen", "schedule_send_description": "Wählen Sie, wann der Server diese Nachricht freigeben soll.", + "schedule_preset": { + "tomorrow_morning": "Morgen früh", + "tomorrow_afternoon": "Morgen Nachmittag", + "monday_morning": "Montag früh" + }, "schedule_send_required": "Wählen Sie Datum und Uhrzeit.", "schedule_send_invalid": "Geben Sie ein gültiges Datum und eine gültige Uhrzeit ein.", "schedule_send_future": "Wählen Sie ein Datum und eine Uhrzeit in der Zukunft.", @@ -706,8 +798,13 @@ "recipient_edit_name": "Anzeigenamen bearbeiten", "recipient_email_placeholder": "E-Mail-Adresse", "recipient_name_placeholder": "Anzeigename", + "invalid_recipient": "Keine gültige E-Mail-Adresse", "autocomplete_search_server": "Auf dem Server suchen", "autocomplete_searching": "Suche läuft...", + "mention_recipients": "Empfänger", + "mention_card_not_recipient": "Nicht in An oder CC, bekommt diese Nachricht also nicht.", + "mention_card_add": "Zu {field} hinzufügen", + "mention_card_remove": "Erwähnung entfernen", "toolbar": { "bold": "Fett", "italic": "Kursiv", @@ -744,7 +841,9 @@ "background_color": "Hintergrundfarbe", "remove_background_color": "Hintergrundfarbe entfernen" }, - "send_filing_warning": "Gesendet - aber das Aufräumen danach schlug fehl, evtl. bleibt ein alter Entwurf sichtbar." + "send_filing_warning": "Gesendet - aber das Aufräumen danach schlug fehl, evtl. bleibt ein alter Entwurf sichtbar.", + "pop_out": "Antwort in eigenem Fenster öffnen", + "send_guard_failed": "Nicht gesendet - ein Plugin, das Nachrichten vor dem Senden prüft, ist fehlgeschlagen oder hat nicht geantwortet." }, "confirm_dialog": { "confirm": "Bestätigen", @@ -801,6 +900,10 @@ "emails_moved": "{count} E-Mails verschoben", "moved_to_mailbox": "Nach {mailbox} verschoben", "move_failed": "Verschieben fehlgeschlagen", + "removed_from_folder": "Aus {mailbox} entfernt", + "remove_from_folder_failed": "Konnte nicht aus diesem Ordner entfernt werden", + "emails_copied": "{count} E-Mails kopiert", + "copy_failed": "Kopieren fehlgeschlagen", "move_error": "E-Mails konnten nicht in den ausgewählten Ordner verschoben werden", "email_tagged": "E-Mail markiert", "emails_tagged": "{count} E-Mails markiert", @@ -814,8 +917,8 @@ "identity_delete_failed": "Identität löschen fehlgeschlagen: {error}", "identity_unauthorized": "Sie sind nicht autorisiert, von dieser E-Mail-Adresse zu senden", "identity_not_found": "Identität nicht gefunden", - "vacation_saved": "Abwesenheitsnotiz-Einstellungen gespeichert", - "vacation_save_failed": "Fehler beim Speichern der Abwesenheitsnotiz-Einstellungen", + "vacation_saved": "Abwesenheits-Einstellungen gespeichert", + "vacation_save_failed": "Fehler beim Speichern der Abwesenheits-Einstellungen", "filters_saved": "Filter erfolgreich gespeichert", "filters_save_failed": "Fehler beim Speichern der Filter", "filters_deleted": "Filterregel gelöscht", @@ -825,7 +928,20 @@ "templates_import_empty": "Keine Vorlagen in der Datei gefunden", "export_email_error": "Fehler beim Exportieren der E-Mail", "import_email_success": "E-Mail erfolgreich importiert", - "import_email_error": "Fehler beim Importieren der E-Mail" + "import_email_error": "Fehler beim Importieren der E-Mail", + "rule_created": "Regel erstellt", + "rule_merged": "Zur Regel „{name}“ hinzugefügt", + "rule_already_covered": "Bereits durch die Regel „{name}“ abgedeckt", + "rule_apply_existing": "{count, plural, one {Auf # vorhandene Nachricht anwenden} other {Auf # vorhandene Nachrichten anwenden}}", + "rule_applied": "{count, plural, =0 {Keine Nachricht musste geändert werden} one {Regel auf # Nachricht angewendet} other {Regel auf # Nachrichten angewendet}}", + "rule_apply_failed": "Die Regel konnte nicht auf vorhandene Nachrichten angewendet werden", + "rule_edit": "Regel bearbeiten", + "rule_undo": "Rückgängig", + "rule_undone": "Regeländerung rückgängig gemacht", + "rule_undo_failed": "Die Regeländerung konnte nicht rückgängig gemacht werden", + "rule_undo_conflict": "Ihre Filter wurden inzwischen geändert. Machen Sie die Änderung in den Filter-Einstellungen rückgängig.", + "rule_folder_failed": "Der Ordner konnte nicht erstellt werden", + "vacation_filters_save_failed": "Abwesenheits-Einstellungen gespeichert, Weiterleitung und Antwortempfänger konnten aber nicht gespeichert werden" }, "date": { "today": "Heute", @@ -910,7 +1026,7 @@ "privacy": "Datenschutz & Sicherheit", "account": "Konto", "identities": "Identitäten", - "vacation": "Abwesenheitsnotiz", + "vacation": "Abwesenheit", "advanced": "Erweitert", "calendar": "Kalender", "filters": "Filter", @@ -925,6 +1041,7 @@ "sidebar_apps": "Sidebar-Apps", "notifications": "Benachrichtigungen", "layout": "Layout", + "themes": "Designs", "reading": "Lesen", "composing": "Verfassen", "downloads": "Downloads", @@ -932,6 +1049,18 @@ "about_data": "Über & Daten", "debug": "Debug" }, + "themes": { + "title": "Designs", + "description": "Wählen Sie aus den von Ihrem Administrator bereitgestellten Designs und den integrierten Vorlagen.", + "locked": "Die Designauswahl wurde von einem Administrator gesperrt.", + "forced": "Das Design „{name}“ wird vom Administrator vorgegeben und kann nicht geändert werden", + "admin_theme": "Administrator-Design", + "activated": "Design aktiviert", + "default_restored": "Standarddesign wiederhergestellt", + "default_name": "Standard", + "default_badge": "Standard", + "admin_enforced": "Vom Administrator erzwungen" + }, "tab_groups": { "general": "Allgemein", "account": "Konto & Identität", @@ -1022,6 +1151,12 @@ "regular": "Normal", "comfortable": "Komfortabel" }, + "interface_layout": { + "label": "Oberflächen-Layout", + "description": "Wo Suche, Navigation und Symbolleisten liegen", + "default": "Standard", + "mountain_view": "Mountain View" + }, "animations": { "label": "Animationen aktivieren", "description": "Weiche Übergänge und Effekte anzeigen" @@ -1070,6 +1205,14 @@ "label": "Nachrichtenzeilen mit Tag-Farbe einfärben", "description": "Die einzelnen Nachrichtenzeilen mit der Farbe ihres ersten Tags einfärben. Deaktivieren Sie diese Option, um die Zeilen neutral zu halten; Tag-Punkte und -Markierungen zeigen die Farbe weiterhin an." }, + "show_account_dot": { + "label": "Konto-Punkt anzeigen", + "description": "Zeigt in der gemeinsamen Ansicht einen Punkt in der Kontofarbe neben dem Absender, damit Sie sehen, aus welchem Konto eine Nachricht kommt. Wirkt nicht, solange Zeilen nach Kontofarbe eingefärbt werden." + }, + "tint_list_rows_by_account": { + "label": "Nachrichtenzeilen mit Kontofarbe einfärben", + "description": "In der gemeinsamen Ansicht jede Nachrichtenzeile in der Farbe ihres Kontos einfärben, statt den Konto-Punkt anzuzeigen. Hat Vorrang vor der Tag-Farbe; Tags behalten ihre Farbe auf ihren Markierungen." + }, "show_folder_total_count": { "label": "Gesamtzahl der Nachrichten anzeigen", "description": "Zeigt neben Ordnern und Tags die Gesamtzahl der Nachrichten zusätzlich zur Anzahl ungelesener Nachrichten an. Deaktivieren, um nur ungelesene Nachrichten anzuzeigen." @@ -1078,6 +1221,10 @@ "label": "Anzahl ungelesener Nachrichten am Tab-Symbol", "description": "Die Anzahl ungelesener Nachrichten im Posteingang als Abzeichen am Browser-Tab-Symbol anzeigen, damit neue E-Mails auch bei nicht fokussiertem Tab sichtbar sind. Deaktivieren Sie diese Option, um das Symbol ohne Abzeichen anzuzeigen." }, + "show_shared_mail_accounts": { + "label": "Geteilte E-Mail-Konten anzeigen", + "description": "Die Ordner von Postfächern, die mit Ihnen geteilt wurden, in der Seitenleiste unter „Geteilt“ anzeigen. Ausschalten, um alle auszublenden, oder einzelne Konten unten ausblenden. Kalender und Kontakte bleiben sichtbar." + }, "pro_interface": { "label": "Pro-Oberfläche (experimentell)", "description": "Desktop-Power-User-Layout mit Multi-Tab-Nachrichtenansicht und kontoübergreifenden Workflows. Die Standardoberfläche bleibt unverändert; Sie können jederzeit zurückwechseln.", @@ -1114,6 +1261,7 @@ "cancel": "Abbrechen", "migrating": "Label auf vorhandenen E-Mails aktualisieren…", "migration_error": "Label auf vorhandenen E-Mails konnte nicht aktualisiert werden", + "filters_migration_error": "Einige Filterregeln verwenden noch das alte Tag – aktualisieren Sie sie manuell", "nesting": { "label": "Verschachtelte Labels", "description": "Labels unter anderen Labels verschachteln und als Baum in der Seitenleiste anzeigen." @@ -1151,6 +1299,18 @@ "swift": "Schnelle Geste", "relax": "Entspannt" }, + "push_all": { + "title": "Alle Konten", + "description": "Push-Benachrichtigungen für mehrere Konten auf einmal ein- oder ausschalten. Ein einziges Browser-Abonnement gilt für alle.", + "select_all": "Alle verbundenen Konten auswählen", + "state_on": "Benachrichtigungen an", + "state_off": "Benachrichtigungen aus", + "enable_selected": "Für ausgewählte aktivieren", + "disable_selected": "Für ausgewählte deaktivieren", + "result_enabled": "Benachrichtigungen für {count} Konten aktiviert.", + "result_partial": "Für {enabled} Konten aktiviert; {failed} konnten nicht aktiviert werden.", + "result_disabled": "Benachrichtigungen für {count} Konten deaktiviert." + }, "sound_selection": { "title": "Benachrichtigungston", "description": "Wählen Sie den Ton für Benachrichtigungen", @@ -1164,8 +1324,8 @@ "enabled_desc": "Benachrichtigungen anzeigen, wenn neue E-Mails eintreffen", "sound": "Benachrichtigungston", "sound_desc": "Einen Ton abspielen, wenn neue E-Mails eintreffen", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + "inbox_only": "Nur Posteingang", + "inbox_only_desc": "Nur bei E-Mails benachrichtigen, die im Posteingang landen; E-Mails, die ein Filter in einen anderen Ordner verschiebt, auslassen" }, "calendar": { "title": "Kalender-Benachrichtigungen", @@ -1226,8 +1386,8 @@ "date_format": { "label": "Datumsformat", "description": "Wie Daten in der E-Mail-Liste angezeigt werden", - "smart": "Intelligent (gebietsschemaabhängig)", - "relative": "Relativ (vor 1 Std., vor 2 Tagen)", + "smart": "Intelligent", + "relative": "Relativ", "full": "Immer vollständiges Datum", "preview_today": "Heute:", "preview_this_week": "Diese Woche:", @@ -1236,8 +1396,8 @@ "date_locale": { "label": "Region für Datumsformat", "description": "Legt fest, in welcher Reihenfolge numerische Datumsangaben angezeigt werden (Tag, Monat, Jahr).", - "auto": "Automatisch (passend zur Sprache)", - "iso": "ISO 8601 (YYYY-MM-DD)", + "auto": "Automatisch", + "iso": "ISO 8601", "dmy": "Tag/Monat/Jahr", "mdy": "Monat/Tag/Jahr" }, @@ -1321,6 +1481,10 @@ "label": "Spam dauerhaft löschen", "description": "E-Mails aus dem Spam-Ordner dauerhaft löschen, anstatt sie in den Papierkorb zu verschieben" }, + "confirm_bulk_delete": { + "label": "Vor dem Löschen mehrerer Nachrichten nachfragen", + "description": "Nachfragen, bevor mehrere ausgewählte Nachrichten in den Papierkorb verschoben werden. Endgültiges Löschen fragt immer nach." + }, "mail_layout": { "label": "Mail-Layout", "description": "Wählen Sie zwischen dem klassischen geteilten Lesebereich und einem fokussierten Lesefluss im Gmail-Stil.", @@ -1331,11 +1495,19 @@ "horizontal": "Lesebereich unten", "horizontal_description": "Nachrichtenliste oben anzeigen und ausgewählte Nachricht in einem darunter liegenden Lesebereich öffnen." }, + "list_columns": { + "label": "Liste als Spalten anzeigen", + "description": "Zeigt einzeilige Zeilen als Tabelle mit den Spalten Von, Betreff, Datum und Größe unter einer Kopfzeile." + }, "show_preview": { "label": "Vorschautext anzeigen", "description": "E-Mail-Vorschau in der Liste anzeigen", "focus_description": "Inline-Vorschautext in der fokussierten einzeiligen Nachrichtenliste anzeigen" }, + "show_verification_codes": { + "label": "Bestätigungscodes anzeigen", + "description": "Code aus Anmelde- und Bestätigungs-E-Mails zum Kopieren mit einem Klick anbieten, in der Liste einen Tag lang und in der Nachricht" + }, "disable_threading": { "label": "Konversationsgruppierung deaktivieren", "description": "E-Mails als einzelne Nachrichten statt nach Konversation gruppiert anzeigen" @@ -1464,6 +1636,17 @@ "label": "Warnung bei leerem Betreff", "description": "Vor dem Senden einer Nachricht ohne Betreff nachfragen." }, + "recipient_mentions": { + "label": "Empfänger mit @ erwähnen", + "description": "@ in der Nachricht bietet ihre Empfänger an und fügt den Vornamen des gewählten ein. Enter oder Tab übernimmt den markierten Namen." + }, + "mention_adds_recipient": { + "label": "Kontakte bei @-Erwähnungen", + "description": "Beim Tippen von @ werden auch deine Kontakte und Personen aus dem Verzeichnis angeboten. Wer noch kein Empfänger ist, wird beim Auswählen zur Nachricht hinzugefügt.", + "to": "Zu An hinzufügen", + "cc": "Zu CC hinzufügen", + "off": "Nur Empfänger anbieten" + }, "hide_inline_image_attachments": { "label": "Eingebettete Bilder in Anhangsliste ausblenden", "description": "Im Nachrichtentext eingebettete Bilder werden nicht als separate Anhänge aufgeführt" @@ -1558,6 +1741,7 @@ "auth_method_label": "Authentifizierung", "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", "auth_method_basic": "Passwort", + "auth_method_token": "Zugriffstoken", "demo_account": "Demokonto", "email": { "label": "E-Mail-Adresse", @@ -1588,6 +1772,40 @@ "drag_handle": "Zum Sortieren ziehen", "add": "Konto hinzufügen" }, + "backup": { + "title": "Sicherung der Kontoliste", + "description": "Speichert die Liste der Konten in diesem Browser auf dem Server, verschlüsselt mit einem Passwort, das nur Sie kennen, und stellt sie auf einem anderen Gerät wieder her. Postfachpasswörter werden nicht gespeichert: Sie melden sich bei jedem wiederhergestellten Konto selbst an.", + "description_passwords": "Speichert die Liste der Konten in diesem Browser auf dem Server, verschlüsselt mit einem Passwort, das nur Sie kennen, und stellt sie auf einem anderen Gerät wieder her. Sie können die Postfachpasswörter einschließen, dann melden sich die wiederhergestellten Konten selbst an.", + "status_none": "Für {account} ist keine Liste gespeichert.", + "status_saved": "Für {account} ist eine Liste gespeichert.", + "save": "Liste speichern", + "restore": "Liste wiederherstellen", + "delete": "Gespeicherte Liste löschen", + "cancel": "Abbrechen", + "delete_confirm": "Die gespeicherte Kontoliste vom Server löschen?", + "password": "Sicherungspasswort", + "password_hint": "Mindestens 10 Zeichen. Es kann nicht wiederhergestellt werden: Wenn Sie es vergessen, speichern Sie die Liste erneut mit einem neuen.", + "restore_hint": "Das Passwort, das beim Speichern der Liste gewählt wurde.", + "confirm": "Passwort bestätigen", + "include_passwords": "Postfachpasswörter einschließen", + "include_passwords_hint": "Konten, die hier mit einem Passwort angemeldet sind, melden sich nach einer Wiederherstellung selbst an. Wer das Sicherungspasswort kennt, kommt in jedes Postfach der Liste.", + "saved": "Liste gespeichert ({count, plural, one {# Konto} other {# Konten}}).", + "restored": "Hinzugefügte Konten: {added}. Bereits vorhandene, aktualisierte Konten: {updated}. Öffnen Sie jedes neue Konto in der Kontoliste, um sich anzumelden.", + "restored_signed_in": "Hinzugefügte Konten: {added}. Bereits vorhandene, aktualisierte Konten: {updated}.", + "skipped": "{count, plural, one {# Konto wurde} other {# Konten wurden}} nicht hinzugefügt: Die Höchstzahl an Konten ist erreicht.", + "saved_passwords": "Passwörter für {count, plural, one {# Konto} other {# Konten}} eingeschlossen.", + "signed_in": "Bei {count, plural, one {# Konto} other {# Konten}} mit dem gespeicherten Passwort angemeldet.", + "sign_in_failed": "{count, plural, one {# gespeichertes Passwort wurde} other {# gespeicherte Passwörter wurden}} abgelehnt: Melden Sie sich bei diesen Konten über die Kontoliste an.", + "deleted": "Gespeicherte Liste gelöscht.", + "errors": { + "mismatch": "Die Passwörter stimmen nicht überein.", + "password_length": "Verwenden Sie mindestens 10 Zeichen.", + "unlock_failed": "Falsches Passwort, oder die Liste wurde für ein anderes Konto gespeichert.", + "owner_signin_required": "Melden Sie sich erneut bei diesem Konto an und versuchen Sie es dann noch einmal.", + "https_required": "Dafür ist eine sichere (HTTPS-)Verbindung nötig.", + "storage_failed": "Der Server konnte die Liste nicht lesen oder speichern. Versuchen Sie es später erneut." + } + }, "shared_accounts": { "title": "Mit mir geteilt", "description": "Gruppen- und freigegebene Konten, die Sie verwalten können. Wählen Sie eines aus, um dessen Filter, Abwesenheitsnotiz, Kalender und Kontakte zu bearbeiten.", @@ -1702,14 +1920,36 @@ }, "link_device": { "title": "Mobile App verknüpfen", - "description": "Melden Sie sich in der Bulwark Mail App an, ohne etwas einzutippen. Erzeugen Sie hier einen QR-Code und scannen Sie ihn auf dem Anmeldebildschirm der App.", + "description": "Melden Sie sich in der Bulwark Mail App auf Ihrem Smartphone an, ohne dort Ihr Passwort einzugeben. Ihr Smartphone erhält eine eigene Anmeldung – wenn Sie sich hier abmelden, bleibt es angemeldet.", "generate": "QR-Code anzeigen", "regenerate": "Neuen Code anzeigen", - "instructions": "Öffnen Sie die Bulwark Mail App, tippen Sie auf dem Anmeldebildschirm auf \"QR-Code scannen\" und richten Sie die Kamera hierauf.", + "instructions": "Öffnen Sie die Bulwark Mail App und tippen Sie auf dem Anmeldebildschirm auf \"Scan a sign-in code\" (oder auf \"Add account\" → \"Scan a sign-in code\", wenn Sie bereits angemeldet sind). Richten Sie dann die Kamera hierauf.", "expires_in": "Dieser Code läuft in {seconds} Sekunden ab. Er kann nur einmal verwendet werden.", "expired": "Dieser Code ist abgelaufen.", - "generating": "Wird erstellt…", - "error": "Kopplungscode konnte nicht erstellt werden. Bitte versuchen Sie es erneut." + "error": "Kopplungscode konnte nicht erstellt werden. Bitte versuchen Sie es erneut.", + "password_prompt": "Geben Sie zur Bestätigung Ihr Passwort ein. Ihr Smartphone erhält eine eigene Anmeldung; Ihr Passwort wird nicht an das Smartphone übertragen.", + "show_password": "Passwort anzeigen", + "hide_password": "Passwort verbergen", + "continue": "Weiter", + "error_wrong_password": "Falsches Passwort. Bitte versuchen Sie es erneut.", + "totp_prompt": "Geben Sie den Code aus Ihrer Authenticator-App ein.", + "error_password_or_code": "Das Passwort oder der Code ist falsch.", + "error_not_signed_in": "Ihre Sitzung ist abgelaufen. Melden Sie sich erneut an, um ein Gerät zu verknüpfen.", + "error_impersonation": "Geräte können nicht verknüpft werden, während Sie das Postfach eines anderen Benutzers ansehen.", + "error_too_many_attempts": "Zu viele Versuche. Warten Sie einen Moment und versuchen Sie es erneut.", + "error_session_secret": "Das Verknüpfen von Geräten ist auf diesem Server nicht eingerichtet (SESSION_SECRET fehlt). Wenden Sie sich an Ihren Administrator.", + "error_server_unreachable": "Ihr Mailserver ist nicht erreichbar. Bitte versuchen Sie es erneut.", + "error_pairing_unavailable": "Ihr Mailserver erlaubt das Verknüpfen von Geräten auf diese Weise nicht. Wenden Sie sich an Ihren Administrator.", + "qr_alt": "QR-Code zur Anmeldung", + "link_label": "Anmeldelink", + "copy_link": "Anmeldelink kopieren", + "copied": "Kopiert", + "copy_hint": "Keine Kamera? Kopieren Sie den Link und fügen Sie ihn in der App über \"Paste sign-in link\" ein.", + "success_title": "Auf Ihrem Smartphone angemeldet", + "success_body": "Sie können diese Seite schließen. Um ein weiteres Gerät zu verknüpfen, lassen Sie einen neuen Code anzeigen.", + "link_another": "Weiteres Gerät verknüpfen", + "use_sso": "Stattdessen Single Sign-On verwenden", + "error_insecure_server": "Die App verbindet sich nur über https mit Mailservern, und dieser verwendet http." }, "public_keys": { "name_label": "Name des öffentlichen Schlüssels", @@ -1737,28 +1977,24 @@ } }, "vacation": { - "title": "Abwesenheitsnotiz", - "description": "Automatisch auf eingehende E-Mails antworten, während Sie abwesend sind", + "title": "Abwesenheit", + "description": "Automatisch auf eingehende E-Mails reagieren, während Sie abwesend sind", "loading": "Abwesenheitseinstellungen werden geladen...", "not_supported": "Ihr Mailserver unterstützt keine Abwesenheitsantworten.", "fetch_error": "Fehler beim Laden der Abwesenheitseinstellungen. Bitte versuchen Sie es erneut.", "status": { - "label": "Abwesenheitsnotiz", + "label": "Automatisch antworten", "description": "Senden Sie eine automatische Antwort an Personen, die Ihnen eine E-Mail senden", "active": "Aktiv", "inactive": "Inaktiv" }, "date_range": { - "title": "Zeitraum", - "description": "Optional die automatische Antwort auf einen bestimmten Zeitraum beschränken", "start": "Startdatum", "start_description": "Leer lassen für kein Startlimit", "end": "Enddatum", "end_description": "Leer lassen für kein Endlimit" }, "message": { - "title": "Automatische Antwortnachricht", - "description": "Die Nachricht, die als Antwort gesendet wird", "subject_label": "Betreff", "subject_description": "Betreffzeile der automatischen Antwort", "subject_placeholder": "Abwesenheitsnotiz", @@ -1778,10 +2014,32 @@ "saving": "Speichern...", "warnings": { "end_before_start": "Das Enddatum muss nach dem Startdatum liegen", + "invalid_date": "Bitte ein gültiges Start- und Enddatum mit Uhrzeit angeben oder das Feld leer lassen", "start_in_past": "Das Startdatum liegt in der Vergangenheit", "empty_body": "Der Nachrichtentext ist leer - Empfänger erhalten eine leere Antwort", "subject_too_long": "Der Betreff ist für den Server zu lang (höchstens {max} Byte).", - "body_too_long": "Die Nachricht ist für den Server zu lang (höchstens {max} Byte, inklusive Formatierung)." + "body_too_long": "Die Nachricht ist für den Server zu lang (höchstens {max} Byte, inklusive Formatierung).", + "forward_address": "Bitte eine gültige Adresse für die Weiterleitung angeben", + "not_running": "Weiterleitung oder Antwortempfänger sind gespeichert, aber gerade nicht aktiv. Speichern aktiviert sie wieder." + }, + "forward": { + "title": "Weiterleitung", + "description": "Leitet eingehende Nachrichten an eine andere Adresse weiter, mit oder ohne automatische Antwort, im Zeitraum oben (ohne Enddatum, bis sie ausgeschaltet wird). Spam wird nicht weitergeleitet.", + "enabled_label": "Nachrichten weiterleiten", + "to_label": "Weiterleiten an", + "to_placeholder": "email@beispiel.de", + "keep_label": "Im Posteingang behalten", + "keep_description": "Sonst wird die Nachricht nur weitergeleitet: Sie bleibt nicht in diesem Postfach, und keine Filterregel läuft für sie." + }, + "audience": { + "label": "Antworten an", + "description": "Interne Absender: {domains}", + "all": "Alle Absender", + "internal": "Nur interne Absender", + "external": "Nur externe Absender" + }, + "auto_reply": { + "title": "Automatische Antwort" } }, "folders": { @@ -1806,11 +2064,25 @@ "reorder": "Zum Neuanordnen ziehen", "reorder_error": "Ordner konnten nicht neu angeordnet werden", "move_error": "Ordner konnte nicht verschoben werden", + "select_folder": "{name} auswählen", + "select_hint": "Auswählen. Umschalt+Klick wählt alle Ordner bis zum zuletzt angeklickten aus.", + "selected_count": "{count, plural, one {# Ordner} other {# Ordner}} ausgewählt", + "move_to": "Verschieben nach...", + "move_to_top_level": "Oberste Ebene", + "move": "Verschieben", + "clear_selection": "Auswahl aufheben", + "folders_moved": "{count, plural, one {# Ordner verschoben} other {# Ordner verschoben}}", + "move_partial_error": "{count, plural, one {# Ordner konnte} other {# Ordner konnten}} nicht verschoben werden", "subfolder_of": "In {name}", "subfolder_name": "Name des Unterordners", "new_folder_name": "Ordnername", "rename": "Umbenennen", "change_icon": "Symbol ändern", + "count_mode": "Zähler für {name}", + "count_mode_default": "Zähler: Standard", + "count_mode_unread": "Nur ungelesene", + "count_mode_all": "Ungelesene und gesamt", + "count_mode_none": "Kein Zähler", "delete": "Löschen", "confirm_delete": "Möchten Sie \"{name}\" wirklich löschen? E-Mails in diesem Ordner werden in den Papierkorb verschoben.", "create": "Erstellen", @@ -1860,7 +2132,11 @@ }, "sender_favicons": { "label": "Absender-Favicons", - "description": "Website-Symbole als Profilbilder für geschäftliche Absender anzeigen" + "description": "Website-Symbole als Profilbilder für geschäftliche Absender anzeigen", + "refresh_label": "Absender-Symbole aktualisieren", + "refresh_description": "Website-Symbole neu laden, zum Beispiel wenn ein Absender sein Symbol geändert hat", + "refresh_button": "Aktualisieren", + "refresh_done": "Absender-Symbole werden neu geladen" }, "show_avatars_in_junk": { "label": "Avatare im Spam-Ordner anzeigen", @@ -1892,7 +2168,34 @@ "button": "Importieren" }, "about": { - "title": "Bulwark Webmail" + "title": "Bulwark Webmail", + "source_code": "Quellcode" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Klicken Sie Spam und Phishing an, bevor sie Ihren Posteingang erreichen. Echte E-Mails lassen Sie durch. Sind alle drei Schilde verloren, fällt der Posteingang.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Echte E-Mail" + }, + "click_to_block": "Klicken zum Blockieren", + "let_through": "Durchlassen", + "start": "Starten", + "play_again": "Nochmal spielen", + "score": "Punkte", + "combo": "Serienbonus", + "shields_left": "{count} von {total} Schilden übrig", + "seconds": "{seconds} s", + "best": "Bestwert: {score}", + "new_best": "Neuer Bestwert", + "held": "Posteingang gehalten", + "overrun": "Posteingang überrannt", + "stats": { + "caught": "Blockiert", + "delivered": "Zugestellt", + "mistakes": "Fehler" + } } }, "sidebar_apps": { @@ -2013,7 +2316,8 @@ "subject": "Betreff", "header": "Benutzerdefinierter Header", "size": "Größe", - "body": "Nachrichtentext" + "body": "Nachrichtentext", + "all": "Alle Nachrichten" }, "comparators": { "has_any": "vorhanden", @@ -2026,7 +2330,9 @@ "ends_with": "endet mit", "matches": "entspricht dem Muster", "greater_than": "ist größer als", - "less_than": "ist kleiner als" + "less_than": "ist kleiner als", + "address_is": "ist die Adresse", + "domain_is": "hat die Domain" }, "action_types": { "move": "In Ordner verschieben", @@ -2046,6 +2352,7 @@ "forward_placeholder": "email@beispiel.de", "forward_keep_copy": "Kopie behalten", "forward_limit": "Weiterleitungen pro Nachricht auf diesem Server: höchstens {count}. Weitere werden übersprungen.", + "forward_unavailable": "Dieser Server erlaubt keine Weiterleitungen. Weiterleitungsaktionen in Filterregeln haben keine Wirkung.", "reject_message": "Ablehnungsnachricht", "reject_placeholder": "Ihre E-Mail wurde abgelehnt", "label_name": "Label-Name", @@ -2101,7 +2408,38 @@ "actions_count": "{count, plural, one {# Aktion} other {# Aktionen}}" }, "origin_external": "Extern", - "managed_by_tooltip": "Verwaltet von {source}. Bearbeiten Sie sie in dieser App oder verwenden Sie den rohen Sieve-Editor." + "managed_by_tooltip": "Verwaltet von {source}. Bearbeiten Sie sie in dieser App oder verwenden Sie den rohen Sieve-Editor.", + "quick_rule_names": { + "move_sender": "Von {sender} nach {folder} verschieben", + "move_domain": "Von @{domain} nach {folder} verschieben", + "move_list": "Liste {list} nach {folder} verschieben", + "mark_read": "Von {sender} als gelesen markieren", + "tag": "Von {sender} mit Label {tag} versehen", + "block": "{sender} blockieren", + "custom": "Nachrichten von {sender}", + "custom_plain": "Neue Regel", + "senders": "{count, plural, one {# Absender} other {# Absendern}}" + }, + "suggestions": "Vorschläge", + "suggest_subject": "Betreff „{subject}“", + "suggest_to": "An {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Auch auf vorhandene Nachrichten in diesem Ordner anwenden", + "apply_existing_unsupported": "Nur Regeln, die Absender, Empfänger, Betreff oder Kopfzeilen prüfen und verschieben, kopieren, als gelesen markieren, mit Stern markieren oder mit Label versehen, lassen sich auf vorhandene Nachrichten anwenden.", + "period_title": "Zeitraum", + "period_start": "Startdatum", + "period_end": "Enddatum", + "validation_period_order": "Das Enddatum muss nach dem Startdatum liegen", + "period_toggle": "Nur in einem Zeitraum aktiv", + "period_hint": "Gilt für Nachrichten, die in diesem Zeitraum eintreffen. Ein leeres Feld lässt den Zeitraum an dieser Seite offen.", + "validation_period_empty": "Bitte ein Start- oder Enddatum für den Zeitraum angeben", + "validation_period_invalid": "Bitte für den Zeitraum ein gültiges Datum mit Uhrzeit angeben oder das Feld leer lassen", + "apply_existing_period": "Eine Regel mit Zeitraum wirkt nur auf Nachrichten, die in diesem Zeitraum eintreffen, und lässt sich daher nicht auf vorhandene Nachrichten anwenden.", + "period_status_scheduled": "geplant", + "period_status_active": "aktiv", + "period_status_expired": "abgelaufen" }, "templates": { "title": "E-Mail-Vorlagen", @@ -2141,12 +2479,6 @@ "title": "Anzeige", "description": "Konfigurieren Sie die Anzeige von Dateien und Ordnern" }, - "folder_layout": { - "label": "Ordnernavigation", - "description": "Wählen Sie, wie Ordner angezeigt werden: eingebettet bei den Dateien oder als Baumansicht in der Seitenleiste", - "inline": "Eingebettet", - "sidebar": "Seitenleiste" - }, "default_view": { "label": "Standardansicht", "description": "Wählen Sie zwischen Raster- und Listenansicht", @@ -2230,17 +2562,41 @@ "pin": "Anheften", "unpin": "Lösen", "move_to": "Verschieben nach...", + "copy_to_account": "In Konto kopieren...", + "remove_from_folder": "Aus diesem Ordner entfernen", "archive": "Archivieren", "delete": "Löschen", "mark_as_spam": "Spam melden", "not_spam": "Kein Spam", "tag": "Label", "items_selected": "{count} E-Mails ausgewählt", + "forward_as_attachments": "Als Anhänge weiterleiten", "edit_draft": "Entwurf bearbeiten", "cancel_scheduled_send": "Senden abbrechen", "reschedule_send": "Neu planen", "cancel_and_edit": "Abbrechen und bearbeiten", - "cancel_and_compose_again": "Abbrechen und neu verfassen" + "cancel_and_compose_again": "Abbrechen und neu verfassen", + "rules": { + "title": "Regeln", + "move_from": "Nachrichten von {sender} immer verschieben", + "move_from_domain": "Nachrichten von @{domain} immer verschieben", + "move_from_list": "Nachrichten dieser Liste immer verschieben", + "mark_read_from": "Nachrichten von {sender} immer als gelesen markieren", + "tag_with": "Immer mit Label versehen", + "block": "{count, plural, one {Absender blockieren} other {# Absender blockieren}}", + "no_junk": "Dieses Konto hat keinen Spam-Ordner", + "create": "Regel erstellen…", + "manage": "Regeln verwalten", + "senders_count": "{count, plural, one {# Absender} other {# Absendern}}", + "cross_account": "Nachrichten aus nur einem Konto auswählen", + "opaque_hint": "Ihre Filter wurden von Hand bearbeitet. Öffnen Sie die Filter-Einstellungen", + "loading": "Regeln werden geladen…", + "load_failed": "Die Regeln konnten nicht geladen werden", + "none": "Noch keine Regeln", + "new_mail_only": "Nur für neue Nachrichten", + "running": "„{name}“ wird auf {folder} angewendet…" + }, + "find_emails_from": "E-Mails von {name} finden" }, "mailbox_context_menu": { "mark_folder_read": "Ordner als gelesen markieren", @@ -2255,6 +2611,12 @@ "empty_folder_generic": "Ordner leeren", "delete_folder": "Ordner löschen", "refresh": "Aktualisieren", + "hide_shared_account": "In Seitenleiste ausblenden", + "mute_notifications": "Benachrichtigungen stummschalten", + "unmute_notifications": "Benachrichtigungen einschalten", + "mute_account_notifications": "Benachrichtigungen für dieses Konto stummschalten", + "unmute_account_notifications": "Benachrichtigungen für dieses Konto einschalten", + "reset_folder_notifications": "Ordner-Benachrichtigungen zurücksetzen", "mark_all_confirm_title": "Alle Ordner als gelesen markieren", "mark_all_confirm_message": "Jede ungelesene Nachricht in deinem persönlichen Konto als gelesen markieren?", "delete_confirm_title": "Ordner löschen", @@ -2298,7 +2660,10 @@ "next_email": "Nächste E-Mail", "previous_email": "Vorherige E-Mail", "open_email": "E-Mail öffnen", - "close_email": "Schließen / Auswahl aufheben" + "close_email": "Schließen / Auswahl aufheben", + "go_to_folder": "Zum Ordner springen", + "next_unread": "Nächste ungelesene E-Mail", + "no_more_unread": "Weiter unten in der Liste gibt es keine ungelesenen E-Mails" }, "actions": { "reply": "Antworten", @@ -2317,10 +2682,12 @@ "help": "Tastaturkürzel anzeigen", "refresh": "E-Mails aktualisieren", "select_all": "Alle auswählen", - "global_search": "Alles durchsuchen" + "global_search": "Alles durchsuchen", + "extend_selection": "Auswahl nach unten / oben erweitern" }, "threads": { - "expand_collapse": "Unterhaltung erweitern/einklappen" + "expand_collapse": "Unterhaltung erweitern/einklappen", + "select_mountain_view": "Konversation auswählen" }, "composer": { "send": "E-Mail senden", @@ -2435,6 +2802,48 @@ } }, "contacts": { + "directory": { + "title": "Verzeichnis", + "search_placeholder": "Name oder E-Mail-Adresse suchen", + "refresh": "Verzeichnis aktualisieren", + "retry": "Erneut versuchen", + "loading": "Verzeichnis wird geladen…", + "unavailable_title": "Kein Verzeichnis", + "unavailable_hint": "Dein Mailserver bietet kein Verzeichnis der Konten an.", + "forbidden_title": "Verzeichnis nicht verfügbar", + "forbidden_hint": "Dein Mailserver erlaubt diesem Konto nicht, das Verzeichnis zu durchsuchen. Frag deine Administration.", + "error_title": "Verzeichnis konnte nicht geladen werden", + "error_hint": "Prüfe deine Verbindung und versuche es erneut.", + "empty_title": "Das Verzeichnis ist leer", + "empty_hint": "Dein Mailserver listet keine Konten mit E-Mail-Adresse.", + "no_match": "Niemand im Verzeichnis passt zu deiner Suche", + "refresh_failed": "Verzeichnis konnte nicht aktualisiert werden. Angezeigt wird die zuletzt geladene Liste.", + "select_page": "Alle auf dieser Seite auswählen", + "deselect_page": "Auswahl auf dieser Seite aufheben", + "select_entry": "{name} auswählen", + "range": "{from}–{to} von {total}", + "page": "Seite {page} von {count}", + "previous_page": "Vorherige Seite", + "next_page": "Nächste Seite", + "picker_title": "Aus Verzeichnis auswählen", + "cancel": "Abbrechen", + "selected_count": "{count} ausgewählt", + "add_to": "Zu An", + "add_cc": "Zu Cc", + "add_bcc": "Zu Bcc", + "select_hint": "Wähle jemanden aus, um die Details zu sehen", + "write_email": "E-Mail schreiben", + "add_to_contacts": "Zu Kontakten hinzufügen", + "email": "E-Mail", + "read_only_note": "Verzeichniseinträge kommen von deinem Mailserver und können hier nicht bearbeitet werden.", + "type": { + "individual": "Person", + "group": "Gruppe", + "resource": "Ressource", + "location": "Ort", + "other": "Sonstiges" + } + }, "title": "Kontakte", "search_placeholder": "Kontakte suchen...", "create_new": "Neuer Kontakt", @@ -2796,6 +3205,10 @@ "tasks_hint": "Aufgaben (k)" }, "events": { + "hidden_before": "{count, plural, one {# früherer Termin} other {# frühere Termine}}", + "hidden_after": "{count, plural, one {# späterer Termin} other {# spätere Termine}}", + "show_all_hours": "Alle Stunden anzeigen", + "show_display_hours": "Nur {start} – {end} anzeigen", "create": "Termin erstellen", "edit": "Termin bearbeiten", "delete": "Termin löschen", @@ -2803,6 +3216,8 @@ "no_events": "Keine Termine", "all_day": "Ganztägig", "more": "+{count} weitere", + "show_more": "Mehr anzeigen", + "show_less": "Weniger anzeigen", "no_title": "(Kein Titel)", "resize": "Termingröße ändern", "duplicate": "Duplizieren", @@ -2825,6 +3240,7 @@ "save_note": "Speichern", "note_saved": "Notiz hinzugefügt", "open_link": "Link öffnen", + "copy_location": "Ort kopieren", "meeting_link": "Besprechungslink", "tentative": "Vorläufig", "cancelled": "Abgesagt", @@ -2949,9 +3365,26 @@ "add": "Erinnerung hinzufügen", "remove": "Erinnerung entfernen", "amount": "Erinnerungsmenge", - "unit": "Erinnerungseinheit" + "unit": "Erinnerungseinheit", + "action": "Erinnerungsart", + "action_display": "Benachrichtigung", + "action_email": "E-Mail", + "by_email": "{label} per E-Mail", + "kept": "{count, plural, one {# weitere Erinnerung, die hier nicht bearbeitet werden kann, bleibt erhalten} other {# weitere Erinnerungen, die hier nicht bearbeitet werden können, bleiben erhalten}}", + "other": "{count, plural, one {# weitere Erinnerung} other {# weitere Erinnerungen}}", + "calendar_defaults": "Standarderinnerungen des Kalenders", + "customize": "Anpassen", + "use_defaults": "Standarderinnerungen des Kalenders verwenden" }, "settings": { + "limit_hours": "Sichtbare Stunden begrenzen", + "limit_hours_desc": "In der Tages- und Wochenansicht nur diese Stunden anzeigen. Termine außerhalb bleiben über die Pfeile oben und unten an jedem Tag erreichbar.", + "visible_hours": "Sichtbare Stunden", + "visible_hours_start": "Beginn der sichtbaren Stunden", + "visible_hours_end": "Ende der sichtbaren Stunden", + "hide_non_working_days": "Arbeitsfreie Tage ausblenden", + "hide_non_working_days_desc": "Tage, an denen du nicht arbeitest, in der Wochenansicht weglassen. Ihre Termine erscheinen weiterhin in der Monats-, Tages- und Agendaansicht.", + "working_days": "Arbeitstage", "title": "Kalendereinstellungen", "default_view": "Standardansicht", "week_starts_on": "Woche beginnt am", @@ -2975,7 +3408,7 @@ "enable_tasks": "Aufgaben aktivieren", "enable_tasks_desc": "Eine Aufgabenansicht im Kalender zum Verwalten von To-dos anzeigen", "show_tasks_on_calendar": "Aufgaben im Kalender anzeigen", - "show_tasks_on_calendar_desc": "Aufgabenmarkierungen in der Tages- und Wochenansicht anzeigen", + "show_tasks_on_calendar_desc": "Aufgaben in der Monats-, Wochen- und Tagesansicht anzeigen", "hover_preview": "Ereignis-Hover-Vorschau", "hover_preview_desc": "Ein Detail-Popover anzeigen, wenn über Kalender-Ereignisse gehovert wird", "hover_preview_instant": "Sofort", @@ -3035,7 +3468,9 @@ }, "quick_create": { "placeholder": "Titel des neuen Termins", - "aria_label": "Schnellerstellung eines Termins" + "aria_label": "Schnellerstellung eines Termins", + "add_title": "Titel hinzufügen", + "more_options": "Weitere Optionen" }, "nav_prev": "Zurück", "nav_next": "Weiter", @@ -3109,11 +3544,21 @@ "error_clear": "Kalenderereignisse konnten nicht gelöscht werden", "share": "Kalender freigeben", "new_event_in_calendar": "Neuer Termin in diesem Kalender", + "shared_with": "Geteilt mit {names}", + "shared_with_count": "{count, plural, one {Mit 1 Person geteilt} other {Mit # Personen geteilt}}", + "move_up": "Nach oben", + "move_down": "Nach unten", "kind": "Inhalt", "kind_events": "Termine", "kind_tasks": "Aufgaben", "kind_both": "Termine und Aufgaben", - "kind_hint": "Legt fest, welche Apps diesen Kalender sehen, wenn er über CalDAV synchronisiert wird. Kann später nicht mehr geändert werden." + "kind_hint": "Legt fest, welche Apps diesen Kalender sehen, wenn er über CalDAV synchronisiert wird. Kann später nicht mehr geändert werden.", + "default_alerts": "Standarderinnerungen", + "default_alerts_title": "Standarderinnerungen für „{name}“", + "default_alerts_hint": "Neue Termine in diesem Kalender erhalten diese Erinnerungen, sofern sie im Termin nicht geändert werden. Änderungen hier wirken sich nicht auf bestehende Termine aus. E-Mail-Erinnerungen werden vom Mailserver verschickt.", + "default_alerts_timed": "Termine mit Uhrzeit", + "default_alerts_all_day": "Ganztägige Termine", + "default_alerts_updated": "Standarderinnerungen aktualisiert" }, "subscription": { "title": "iCal-Abonnement", @@ -3220,7 +3665,13 @@ "bah": "Bahman", "esf": "Esfand" }, - "nav_open_menu": "Menü öffnen" + "nav_open_menu": "Menü öffnen", + "toolbar": { + "more": "Weitere Aktionen", + "select_view": "Ansicht wechseln, aktuell {view}" + }, + "other_calendars": "Weitere Kalender", + "calendar_options": "Optionen für {name}" }, "advanced_search": { "title": "Erweiterte Suche", @@ -3234,6 +3685,7 @@ "body_placeholder": "Inhalt enthält...", "folder": "Ordner", "all_folders": "Alle Ordner", + "all_folders_except_spam_trash": "Alle Ordner außer Spam und Papierkorb", "has_attachment": "Anhänge", "date_after": "Nach dem", "date_before": "Vor dem", @@ -3257,7 +3709,13 @@ "suggestions_people": "Personen", "suggestions_from": "E-Mails von {address} suchen", "suggestions_to": "E-Mails an {address} suchen", - "suggestions_remove_recent": "Aus den letzten Suchen entfernen" + "suggestions_remove_recent": "Aus den letzten Suchen entfernen", + "has_words": "Enthält die Wörter", + "size": "Größe", + "date_within": "Datum", + "search_in": "Suchen in", + "has_attachment_check": "Mit Anhang", + "search": "Suchen" }, "welcome": { "title": "Willkommen in Ihrem Postfach", @@ -3265,6 +3723,8 @@ "tip_shortcuts": "Drücken Sie ?, um alle Tastaturkürzel anzuzeigen", "tip_sidebar": "Finden Sie Kalender, Kontakte und Einstellungen im Seitenmenü", "tip_settings": "Passen Sie Ihre Erfahrung in den Einstellungen an", + "tip_compose_touch": "Tippen Sie auf den Stift, um eine neue E-Mail zu schreiben", + "tip_sidebar_touch": "Kalender, Kontakte und Einstellungen finden Sie in der Leiste unten", "got_it": "Verstanden", "settings": "Einstellungen", "dismiss": "Schließen", @@ -3288,8 +3748,6 @@ "title": "Dateien", "search_placeholder": "Dateien suchen...", "empty_state_title": "Noch keine Dateien", - "empty_state_description": "Laden Sie Dateien hoch oder erstellen Sie Ordner, um loszulegen", - "upload": "Hochladen", "upload_files": "Dateien hochladen", "new_folder": "Neuer Ordner", "new_folder_name": "Ordnername", @@ -3308,11 +3766,10 @@ "type": "Typ", "folder": "Ordner", "file": "Datei", - "parent_directory": "Übergeordnetes Verzeichnis", - "breadcrumb_root": "Startseite", - "drop_files_here": "Dateien oder Ordner hier ablegen zum Hochladen", "uploading": "Wird hochgeladen...", "upload_success": "{count, plural, one {1 Datei hochgeladen} other {# Dateien hochgeladen}}", + "upload_canceled": "Hochladen abgebrochen", + "upload_canceled_partial": "{count, plural, one {1 Datei wurde} other {# Dateien wurden}} vor dem Abbrechen hochgeladen", "upload_error": "Datei konnte nicht hochgeladen werden", "create_folder_success": "Ordner erstellt", "create_folder_error": "Ordner konnte nicht erstellt werden", @@ -3345,7 +3802,6 @@ "file_name": "Dateiname", "retry": "Wiederholen", "refresh": "Aktualisieren", - "toggle_favorite": "Favorit umschalten", "duplicate": "Duplizieren", "duplicate_success": "Erfolgreich dupliziert", "duplicate_error": "Duplizieren fehlgeschlagen", @@ -3357,8 +3813,8 @@ "create_file_error": "Datei konnte nicht erstellt werden", "favorites": "Favoriten", "recent": "Zuletzt verwendet", - "properties": "Eigenschaften", - "open_folder": "Ordner öffnen", + "show_in_folder": "Im Ordner anzeigen", + "remove_from_recent": "Aus „Zuletzt verwendet“ entfernen", "upload_folder": "Ordner hochladen", "file_too_large": "\"{name}\" überschreitet die maximale Dateigröße ({max})", "undo": "Rückgängig", @@ -3367,38 +3823,59 @@ "toolbar": "Dateiaktionen", "file_list": "Dateien und Ordner", "context_menu": "Aktionen", - "settings_title": "Dateieinstellungen", - "settings_display": "Anzeige", - "settings_default_view": "Standardansicht", - "settings_default_view_desc": "Wählen Sie zwischen Raster- und Listenansicht", - "settings_default_sort": "Standardsortierung", - "settings_default_sort_desc": "Wählen Sie die Standardsortierung für Dateien", - "settings_sort_direction": "Sortierrichtung", - "settings_sort_direction_desc": "Wählen Sie auf- oder absteigende Reihenfolge", "settings_ascending": "Aufsteigend", "settings_descending": "Absteigend", - "settings_icons": "Symbole", - "settings_show_icons": "Dateisymbole anzeigen", - "settings_show_icons_desc": "Symbole neben Dateien und Ordnern anzeigen", - "settings_colored_icons": "Farbige Symbole", - "settings_colored_icons_desc": "Farbige statt einfarbige Symbole verwenden", - "settings_show_thumbnails": "Vorschaubilder anzeigen", - "settings_show_thumbnails_desc": "Bildvorschauen statt Symbole für Bilddateien anzeigen", - "settings_behavior": "Verhalten", - "settings_show_hidden": "Versteckte Dateien anzeigen", - "settings_show_hidden_desc": "Dateien und Ordner anzeigen, die mit einem Punkt beginnen", - "settings_folder_layout": "Ordnernavigation", - "settings_folder_layout_desc": "Wählen Sie, wie Ordner angezeigt werden: eingebettet bei den Dateien oder als Baumansicht in der Seitenleiste", - "settings_folder_layout_inline": "Eingebettet", - "settings_folder_layout_sidebar": "Seitenleiste", "disabled_title": "Die Dateifunktion wurde von Ihrem Administrator deaktiviert", "disabled_description": "Große Datei-Uploads über WebDAV können Stalwart/RocksDB-Instabilität verursachen, einschließlich Out-of-Memory-Abstürzen und nicht wiederherstellbarer Festplattennutzung. Gelöschte Dateien werden möglicherweise nicht sofort aus dem Blob-Speicher entfernt. Diese Funktion wird für Produktionsumgebungen nicht empfohlen.", - "stability_warning": "Große Datei-Uploads können zu Serverinstabilität führen. Gelöschte Dateien werden möglicherweise nicht sofort aus dem Speicher entfernt. Mit Vorsicht verwenden.", "no_accounts": "Keine verbundenen Konten.", "open_folder_tree": "Ordnerbaum öffnen", - "other_accounts": "Andere Konten", "migration_title": "Ihre Dateien werden aktualisiert…", - "migration_description": "Ordner und Dateien werden in die richtige Struktur gebracht. Dies geschieht nur einmal." + "migration_description": "Ordner und Dateien werden in die richtige Struktur gebracht. Dies geschieht nur einmal.", + "new": "Neu", + "my_files": "Meine Dateien", + "folders": "Ordner", + "files_heading": "Dateien", + "open": "Öffnen", + "star": "Zu Favoriten hinzufügen", + "unstar": "Aus Favoriten entfernen", + "expand_folder": "{name} aufklappen", + "collapse_folder": "{name} zuklappen", + "sidebar_hint": "Favorisierte Ordner und geöffnete Dateien erscheinen hier.", + "breadcrumbs": "Ordnerpfad", + "show_path": "Ausgeblendete Ordner im Pfad anzeigen", + "selection_actions": "Aktionen für ausgewählte Elemente", + "selected_count": "{count, plural, one {1 ausgewählt} other {# ausgewählt}}", + "clear_selection": "Auswahl aufheben", + "select_all": "Alle auswählen", + "select_item": "{name} auswählen", + "more_actions": "Weitere Aktionen für {name}", + "paste_here": "In diesen Ordner einfügen", + "paste_count": "{count, plural, one {1 Element einfügen} other {# Elemente einfügen}}", + "sort_by": "Sortieren nach", + "view_mode": "Ansicht", + "clear_search": "Suche löschen", + "no_results_for": "Nichts in diesem Ordner entspricht „{query}“", + "download_count": "{count, plural, one {1 Datei herunterladen} other {# Dateien herunterladen}}", + "delete_count": "{count, plural, one {1 Element löschen} other {# Elemente löschen}}", + "close_details": "Details schließen", + "file_kind": "{ext}-Datei", + "folder_summary": "{folders, plural, =0 {Keine Ordner} one {1 Ordner} other {# Ordner}}, {files, plural, =0 {keine Dateien} one {1 Datei} other {# Dateien}}", + "details_hint": "Wählen Sie eine Datei oder einen Ordner aus, um die Details zu sehen.", + "sharing": "Freigabe", + "shared_with_count": "{count, plural, one {Mit 1 Person geteilt} other {Mit # Personen geteilt}}", + "only_you": "Nur Sie", + "empty_folder_title": "Dieser Ordner ist leer", + "empty_folder_description": "Legen Sie Dateien hier ab oder laden Sie sie von Ihrem Gerät hoch.", + "empty_folder_read_only": "Sie können diesem Ordner keine Dateien hinzufügen.", + "choose_account": "Konto auswählen", + "drop_to_upload": "Ablegen, um in {folder} hochzuladen", + "uploading_count": "{current} von {total} wird hochgeladen", + "dismiss": "Ausblenden", + "zoom_in": "Vergrößern", + "zoom_out": "Verkleinern", + "zoom_reset": "An Bildschirm anpassen", + "rotate": "Drehen", + "close_preview": "Schließen" }, "smime": { "your_certificates": "Ihre Zertifikate", @@ -3441,7 +3918,7 @@ "processing": "Wird verarbeitet…", "close": "Schließen", "certificate_details": "Zertifikatsdetails", - "cert_subject": "Betreff", + "cert_subject": "Inhaber", "cert_issuer": "Aussteller", "cert_email": "E-Mail", "cert_serial": "Seriennummer", @@ -3613,6 +4090,354 @@ "failed_detail": "Autorisierung fehlgeschlagen: {error}", "missing_params": "Autorisierungscode oder State fehlt.", "delivery_failed": "Das Autorisierungsergebnis konnte nicht an den App-Tab übermittelt werden. Bitte schließen Sie dieses Fenster und versuchen Sie es erneut." + }, + "settings": { + "title": "Plugins", + "description": "Plugins, die Ihr Administrator hinzugefügt hat. Schalten Sie sie für Ihr Konto ein oder aus.", + "description_uploads": "Plugins erweitern das Webmail um Funktionen. Schalten Sie sie für Ihr Konto ein oder aus, oder installieren Sie eigene.", + "description_demo": "In der Demo sind keine Plugins verfügbar.", + "install_button": "Plugin installieren", + "install_hint": "Wählen Sie eine Plugin-ZIP-Datei.", + "install_hint_approval": "Wählen Sie eine Plugin-ZIP-Datei. Ein Administrator muss das Plugin freigeben, bevor Sie es einschalten können.", + "install_uploads_off": "Ihr Administrator erlaubt keine Installation von Plugins.", + "install_managed_id": "Ihr Administrator stellt bereits ein Plugin mit diesem Namen bereit. Es kann daher nicht ersetzt werden.", + "install_invalid": "Diese Datei ist kein installierbares Plugin: {details}", + "toast_installed": "Plugin installiert. Schalten Sie es unten ein.", + "toast_installed_pending": "Plugin installiert und Ihrem Administrator zur Freigabe vorgelegt.", + "toast_enabled": "{name} ist eingeschaltet", + "toast_disabled": "{name} ist ausgeschaltet", + "toast_removed": "{name} wurde entfernt", + "toast_forced": "Ihr Administrator lässt {name} eingeschaltet", + "toast_approval_requested": "{name} wartet auf die Freigabe durch Ihren Administrator", + "empty_title": "Noch keine Plugins", + "empty_admin": "Ihr Administrator hat keine Plugins hinzugefügt.", + "empty_uploads": "Installieren Sie zum Start ein Plugin aus einer ZIP-Datei.", + "empty_demo": "Plugins brauchen ein echtes Konto, daher gibt es in der Demo keine.", + "syncing": "Plugins werden mit dem Server abgeglichen…", + "status_installed": "Aus", + "status_enabled": "Wird gestartet", + "status_running": "An", + "status_disabled": "Aus", + "status_error": "Problem", + "badge_forced": "Erforderlich", + "badge_managed": "Von Ihrem Administrator", + "badge_yours": "Von Ihnen installiert", + "badge_awaiting": "Wartet auf Freigabe", + "toggle_label": "{name} ein- oder ausschalten", + "forced_note": "Ihr Administrator schreibt dieses Plugin vor. Es kann daher nicht ausgeschaltet werden.", + "permissions": "Was es darf", + "permissions_managed": "Was es darf (von Ihrem Administrator freigegeben)", + "settings": "Einstellungen", + "remove": "Entfernen", + "remove_title": "{name} entfernen?", + "remove_message": "Das Plugin und alles, was es in diesem Browser gespeichert hat, wird gelöscht. Sie können es später erneut installieren.", + "remove_confirm": "Entfernen", + "error_user_plugins_off": "Ihr Administrator hat von Benutzern installierte Plugins ausgeschaltet. Dieses Plugin läuft daher nicht.", + "error_approval_unavailable": "Dieses Plugin wurde installiert, bevor eine Freigabe nötig war. Entfernen Sie es und installieren Sie es erneut, um es zur Freigabe vorzulegen.", + "error_approval_denied": "Ihr Administrator hat dieses Plugin nicht freigegeben.", + "error_approval_pending": "Wartet darauf, dass Ihr Administrator dieses Plugin freigibt.", + "error_auto_disabled": "Ausgeschaltet, weil es wiederholt fehlgeschlagen ist. Schalten Sie es wieder ein, um es erneut zu versuchen.", + "accounts_heading": "Verbundene Konten", + "account_on": "{label} auf {host}", + "account_waiting": "{label} auf {host} (Anmeldung nicht abgeschlossen)", + "disconnect": "Trennen", + "disconnect_failed": "{label} konnte nicht getrennt werden" + }, + "consent": { + "title": "{name} Folgendes erlauben?", + "intro": "Das Plugin bittet um die folgenden Berechtigungen. Es erhält sie nur, wenn Sie zustimmen.", + "high_risk_note": "Markierte Punkte geben dem Plugin Zugriff auf den Inhalt Ihrer E-Mails oder erlauben ihm, Daten anderswohin zu senden. Erlauben Sie sie nur Plugins, denen Sie vertrauen.", + "revoke_hint": "Sie können das Plugin jederzeit in den Einstellungen ausschalten oder entfernen.", + "deny": "Nicht erlauben", + "allow": "Erlauben" + }, + "permissions": { + "none": "Es braucht keine besonderen Berechtigungen.", + "high_risk": "Mit Vorsicht", + "email_read": { + "title": "Ihre E-Mails lesen", + "body": "Absender, Empfänger, Betreff und den vollständigen Text jeder Nachricht in Ihrem Konto sowie Ihre Ordner sehen." + }, + "email_write": { + "title": "Ihre E-Mails ändern und ordnen", + "body": "Labels, Markierungen und andere Schlüsselwörter an Ihren Nachrichten hinzufügen oder entfernen, Nachrichten zwischen Ihren Ordnern und ins Archiv verschieben und Entwürfe speichern." + }, + "email_send": { + "title": "E-Mails vor dem Senden ändern", + "body": "Empfänger, Betreff, Text und Anhänge der Nachrichten lesen und ändern, die Sie schreiben, und das Senden einer Nachricht verhindern." + }, + "crypto_full": { + "title": "Vollzugriff auf diese App", + "body": "Läuft mit denselben Rechten wie das Webmail selbst. Es kann alle Ihre E-Mails und gespeicherten Schlüssel lesen und in Ihrem Namen handeln. Erlauben Sie das nur Plugins, denen Sie voll vertrauen." + }, + "email_raw_send": { + "title": "Selbst erstellte Nachrichten senden", + "body": "Vollständige Nachrichten, zum Beispiel signierte oder verschlüsselte, von Ihrem Konto senden." + }, + "email_blob_read": { + "title": "Rohinhalt von Nachrichten lesen", + "body": "Die Originaldaten Ihrer Nachrichten und Anhänge lesen, auch jeder Datei, die Sie vor dem Senden anhängen." + }, + "email_blob_write": { + "title": "Anhänge vor dem Senden ersetzen", + "body": "Dateien auf Ihren Mailserver hochladen und eine angehängte Datei vor dem Senden durch eine andere ersetzen." + }, + "email_render_takeover": { + "title": "Darstellung von Nachrichten ändern", + "body": "Den Text geöffneter Nachrichten und der Nachrichtenliste ersetzen, zum Beispiel um entschlüsselte Inhalte anzuzeigen." + }, + "calendar_read": { + "title": "Ihren Kalender lesen", + "body": "Ihre Kalender und deren Termine sehen." + }, + "calendar_write": { + "title": "Ihren Kalender ändern", + "body": "Termine erstellen, ändern und löschen." + }, + "contacts_read": { + "title": "Ihre Kontakte lesen", + "body": "Ihre Adressbücher durchsuchen und lesen." + }, + "contacts_write": { + "title": "Ihre Kontakte ändern", + "body": "Kontakte und Adressbücher erstellen, ändern und löschen." + }, + "files_read": { + "title": "Ihre Dateien lesen", + "body": "Die in Ihrem Konto gespeicherten Dateien durchsuchen und öffnen." + }, + "files_write": { + "title": "Ihre Dateien ändern", + "body": "Dateien in Ihrem Konto hochladen, umbenennen, verschieben und löschen." + }, + "identity_read": { + "title": "Ihre Absenderadressen sehen", + "body": "Die Adressen, von denen Sie senden, und deren Signaturen sehen." + }, + "identity_write": { + "title": "Ihre Absenderadressen ändern", + "body": "Absenderadressen und Signaturen erstellen, ändern und löschen." + }, + "filters_read": { + "title": "Ihre E-Mail-Filter lesen", + "body": "Die Regeln sehen, die Ihre eingehenden E-Mails sortieren." + }, + "filters_write": { + "title": "Ihre E-Mail-Filter ändern", + "body": "Regeln zu Ihren serverseitigen Filtern hinzufügen. Solche Regeln können eingehende E-Mails verschieben, weiterleiten oder löschen." + }, + "tasks_read": { + "title": "Ihre Aufgaben lesen", + "body": "Ihre Aufgabenlisten und Aufgaben sehen." + }, + "tasks_write": { + "title": "Ihre Aufgaben ändern", + "body": "Aufgaben erstellen, ändern, erledigen und löschen." + }, + "templates_read": { + "title": "Ihre Vorlagen lesen", + "body": "Ihre gespeicherten Nachrichtenvorlagen sehen." + }, + "templates_write": { + "title": "Ihre Vorlagen ändern", + "body": "Nachrichtenvorlagen erstellen, ändern und löschen." + }, + "smime_read": { + "title": "Ihren S/MIME-Status sehen", + "body": "Sehen, ob Sie S/MIME-Schlüssel und -Zertifikate haben, ohne Zugriff auf die Schlüssel." + }, + "vacation_read": { + "title": "Ihre Abwesenheitsnotiz sehen", + "body": "Sehen, ob Ihre automatische Antwort eingeschaltet ist und was sie enthält." + }, + "vacation_write": { + "title": "Ihre Abwesenheitsnotiz ändern", + "body": "Ihre automatische Antwort ein- oder ausschalten und ihren Text und Zeitraum ändern." + }, + "settings_read": { + "title": "Ihre Einstellungen lesen", + "body": "Ihre Einstellungen und Label-Definitionen sehen." + }, + "settings_write": { + "title": "Ihre Einstellungen ändern", + "body": "Labels hinzufügen und neu anordnen und Ihre Einstellungen ändern." + }, + "security_read": { + "title": "Ihre Kontosicherheit sehen", + "body": "Sehen, ob Zwei-Faktor-Anmeldung und Verschlüsselung eingeschaltet sind, ohne Zugriff auf Geheimnisse." + }, + "auth_observe": { + "title": "Erfahren, wann Sie sich anmelden", + "body": "Benachrichtigt werden, wenn Sie sich anmelden, das Konto wechseln oder Ihre Sitzung erneuert wird." + }, + "auth_emit": { + "title": "Sie abmelden", + "body": "Sie vom Webmail abmelden." + }, + "account_read": { + "title": "Ihre Konten sehen", + "body": "Die von Ihnen hinzugefügten Konten mit ihren Adressen und Servern sehen." + }, + "http_post": { + "title": "Den Webmail-Server für Sie nutzen", + "body": "Diese Server-Endpunkte mit Ihrer Anmeldung aufrufen:" + }, + "http_fetch": { + "title": "Mit anderen Websites verbinden", + "body": "Anfragen an diese Websites senden. Alles, was das Plugin lesen kann, kann dorthin gesendet werden:" + }, + "admin_config": { + "title": "Seine Einstellungen von Ihrem Administrator lesen", + "body": "Die Einstellungen lesen, die Ihr Administrator für dieses Plugin gespeichert hat." + }, + "ui_observe": { + "title": "Verfolgen, wo Sie sich in der App befinden", + "body": "Wissen, welche Seite geöffnet ist, und auf angeklickte Links reagieren." + }, + "ui_toolbar": { + "title": "Schaltflächen zu Symbolleisten hinzufügen", + "body": "Eigene Schaltflächen in der Nachrichten-Symbolleiste, bei einer geöffneten Nachricht und in der oberen Leiste anzeigen." + }, + "ui_app_top_banner": { + "title": "Oben ein Banner anzeigen", + "body": "Einen Hinweis über die gesamte Breite oben in der App anzeigen." + }, + "ui_email_banner": { + "title": "Hinweise bei Nachrichten anzeigen", + "body": "Eigene Hinweise und Zusammenfassungen über, in und zwischen den Teilen einer geöffneten Nachricht oder Unterhaltung anzeigen." + }, + "ui_email_footer": { + "title": "Inhalte unter Nachrichten hinzufügen", + "body": "Eigene Inhalte unter einer geöffneten Nachricht anzeigen." + }, + "ui_contact_cryptokeys": { + "title": "Verschlüsselungsschlüssel von Kontakten anzeigen", + "body": "Die Verschlüsselungsschlüssel eines Kontakts anzeigen und verwalten." + }, + "ui_email_details": { + "title": "Bereiche zu Nachrichtendetails hinzufügen", + "body": "Einen eigenen Bereich neben oder unter einer geöffneten Nachricht anzeigen." + }, + "ui_download_file": { + "title": "Dateien auf Ihrem Gerät speichern", + "body": "Selbst erstellte Dateien zum Herunterladen anbieten." + }, + "ui_message_list_tabs": { + "title": "Tabs zu Ihrem Posteingang hinzufügen", + "body": "Ihren Posteingang in Tabs wie Updates oder Werbung aufteilen." + }, + "ui_composer_toolbar": { + "title": "Zum Editor hinzufügen", + "body": "Eigene Schaltflächen, Menüeinträge und Abschnitte anzeigen, während Sie eine Nachricht schreiben." + }, + "ui_composer_sidebar": { + "title": "Einen Bereich zum Editor hinzufügen", + "body": "Einen eigenen Bereich neben der Nachricht anzeigen, die Sie schreiben." + }, + "ui_sidebar_widget": { + "title": "Zur Seitenleiste hinzufügen", + "body": "Eigene Felder und Abschnitte in der E-Mail-Seitenleiste anzeigen." + }, + "ui_settings_section": { + "title": "Eine Einstellungsseite hinzufügen", + "body": "Eine eigene Seite in den Einstellungen anzeigen." + }, + "ui_context_menu": { + "title": "Zum Nachrichtenmenü hinzufügen", + "body": "Einträge zu dem Menü hinzufügen, das sich per Rechtsklick auf eine Nachricht öffnet." + }, + "ui_navigation_rail": { + "title": "Zur Navigationsleiste hinzufügen", + "body": "Eine eigene Schaltfläche in der Navigationsleiste anzeigen." + }, + "ui_keyboard": { + "title": "Tastaturkürzel hinzufügen", + "body": "Auf selbst festgelegte Tastaturkürzel reagieren." + }, + "ui_calendar_action": { + "title": "Kalenderansicht erweitern", + "body": "Eigene Schaltflächen und Abschnitte bei Terminen sowie kurze Beschriftungen und Punkte an Kalendertagen anzeigen." + }, + "ui_admin_page": { + "title": "Eine Admin-Seite hinzufügen", + "body": "Eine eigene Seite in den Admin-Einstellungen anzeigen." + }, + "app_lifecycle": { + "title": "Erfahren, wann die App startet und beendet wird", + "body": "Benachrichtigt werden, wenn die App startet, offline geht oder Sie sich abmelden." + }, + "compose_open": { + "title": "Den Editor für Sie öffnen", + "body": "Eine neue Nachricht mit bereits ausgefüllten Empfängern, Text oder Anhängen öffnen. Ob sie gesendet wird, entscheiden weiterhin Sie." + }, + "compose_read": { + "title": "Die Nachricht lesen, die Sie schreiben", + "body": "Den Text einer Nachricht lesen, während Sie sie schreiben, wenn Sie das Plugin im Editor benutzen. Nicht die Signatur und nicht die zitierte Nachricht." + }, + "compose_write": { + "title": "Die Nachricht ändern, die Sie schreiben", + "body": "Text in einer Nachricht ersetzen oder ergänzen, während Sie sie schreiben, wenn Sie das Plugin im Editor benutzen. Jede Änderung lässt sich rückgängig machen, und Sie entscheiden weiterhin, ob Sie senden." + }, + "ui_notifications": { + "title": "Benachrichtigungen anzeigen", + "body": "Benachrichtigungen auf Ihrem Gerät anzeigen oder in der App, wenn Sie sie nicht erlaubt haben. Jede nennt den Namen des Plugins." + }, + "ui_message_list": { + "title": "Zur Nachrichtenliste hinzufügen", + "body": "Eigene Inhalte über der Nachrichtenliste sowie Schaltflächen bei Nachrichten und in der Leiste für ausgewählte Nachrichten anzeigen." + }, + "ui_contacts": { + "title": "Kontaktansicht erweitern", + "body": "Eigene Abschnitte in den Details eines Kontakts und Schaltflächen in der Kontaktliste anzeigen." + }, + "ui_tasks": { + "title": "Aufgabenansicht erweitern", + "body": "Eigene Abschnitte in den Details einer Aufgabe und Schaltflächen in der Aufgabenliste anzeigen." + }, + "ui_files": { + "title": "Dateiansicht erweitern", + "body": "Eigene Abschnitte in der Dateivorschau und Einträge im Menü einer Datei anzeigen." + }, + "ui_commands": { + "title": "Befehle hinzufügen", + "body": "Eigene Einträge zur Befehlspalette (Ctrl+K) hinzufügen." + }, + "ui_app_page": { + "title": "Seiten hinzufügen", + "body": "Eigene Seiten zur App hinzufügen, jede mit einer Schaltfläche in der Navigationsleiste." + }, + "http_proxy": { + "title": "Von Ihnen verbundene Konten nutzen", + "body": "Anfragen über den Server von Bulwark senden, mit einem Konto oder Schlüssel, das Sie oder Ihr Administrator eingerichtet haben. Das Plugin sieht das Passwort nie, kann auf dem zugehörigen Server aber alles tun, was dieses Konto darf." + } + }, + "dialog": { + "from_plugin": "Vom Plugin {name}", + "ok": "OK", + "submit": "Senden", + "confirm": "Bestätigen", + "cancel": "Abbrechen", + "close": "Schließen", + "turn_off": "{name} ausschalten", + "plugin_menu": "Optionen für {name}", + "password_note": "Dieses Formular stammt vom Plugin {name}, nicht vom Webmail. Geben Sie ein Passwort nur ein, wenn Sie diesem Plugin vertrauen.", + "toast_auto_disabled": "{name} wurde ausgeschaltet, weil es wiederholt fehlgeschlagen ist." + }, + "connect": { + "title": "{label} verbinden", + "intro_password": "{name} möchte Ihr Konto bei {label} verwenden. Bulwark speichert das Passwort auf seinem Server und sendet es nur an {host}. Das Plugin sieht es nie.", + "intro_password_any": "{name} möchte Ihr Konto bei {label} verwenden. Bulwark speichert das Passwort auf seinem Server und sendet es nur an den Server, den Sie hier eingeben. Das Plugin sieht es nie.", + "intro_signin": "{name} möchte sich bei Ihrem Konto bei {label} anmelden. Geben Sie hier den Server ein und melden Sie sich dann auf dessen eigener Seite an. Bulwark speichert den erhaltenen Zugang auf seinem Server; das Plugin sieht ihn nie.", + "server": "Serveradresse", + "server_hint": "cloud.example.com", + "username": "Benutzername", + "secret": "Passwort oder Schlüssel", + "connect": "Verbinden", + "continue": "Weiter", + "cancel": "Abbrechen", + "saving": "Wird gespeichert…", + "error_server": "Geben Sie die Adresse des Servers ein, zum Beispiel https://cloud.example.com", + "error_save": "Speichern fehlgeschlagen: {error}" } }, "global_search": { @@ -3641,5 +4466,41 @@ "preview_empty": "Ergebnis auswählen, um es anzuzeigen", "open_full": "Öffnen", "view_label": "Ansicht" + }, + "top_bar": { + "more": "Weitere Optionen" + }, + "plugin_pages": { + "loading": "Seite wird geladen", + "not_available_title": "Diese Seite ist nicht verfügbar", + "not_available_description": "Das Plugin, das sie hinzugefügt hat, ist ausgeschaltet oder nicht mehr installiert.", + "back_to_mail": "Zurück zu E-Mail", + "settings_group": "Plugins" + }, + "command_palette": { + "title": "Befehlspalette", + "open": "Befehlspalette öffnen", + "placeholder": "Befehl eingeben", + "no_results": "Keine passenden Befehle", + "go_to": "Zu {name} wechseln", + "compose": "Neue Nachricht", + "search": "E-Mails durchsuchen", + "theme_dark": "Dunkles Design verwenden", + "theme_light": "Helles Design verwenden", + "shortcuts": "Tastaturkürzel anzeigen", + "group_navigation": "Wechseln zu", + "group_actions": "Aktionen", + "group_plugins": "Plugins" + }, + "notification_center": { + "title": "Benachrichtigungen", + "mark_all_read": "Alle als gelesen markieren", + "clear": "Leeren", + "empty": "Keine Benachrichtigungen", + "close": "Schließen", + "unread": "Ungelesen", + "unread_count": "{count} ungelesen", + "level_error": "Fehler", + "level_warning": "Warnung" } } diff --git a/locales/en/common.json b/locales/en/common.json index 54b80837..f7f38617 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -17,17 +17,24 @@ "reconnecting": "Connection lost. Attempting to reconnect…", "error": { "invalid_credentials": "Invalid email or password. Please check your credentials and try again.", + "invalid_token": "The server did not accept this access token. Check that it is complete and has not expired or been revoked.", "connection_failed": "Unable to reach the server. Check your internet connection and try again.", "cors_blocked": "The server is reachable but is blocking cross-origin requests. Check your JMAP server's CORS settings and allow this domain.", "server_error": "The server is temporarily unavailable. Please try again later.", "generic": "An unexpected error occurred. If this persists, contact your administrator.", "totp_required": "A two-factor authentication code is required. Please enter your code below.", "totp_invalid": "Invalid authentication code. Please check your authenticator app and try again.", - "oauth_discovery_failed": "SSO is enabled but the identity provider could not be reached. Check your OAuth configuration." + "oauth_discovery_failed": "SSO is enabled but the identity provider could not be reached. Check your OAuth configuration.", + "token_exchange_failed": "Your password and code were accepted, but the mail server refused to start a session for this app. Ask your administrator to check its OAuth client settings." }, "show_password": "Show password", "hide_password": "Hide password", "totp_toggle": "I have a 2FA code", + "token_toggle": "Sign in with an access token", + "password_toggle": "Sign in with a password", + "token_label": "Access token", + "token_placeholder": "Paste your API token", + "token_hint": "Create an API token in your mail provider's settings and paste it here.", "remember_me": "Remember me", "config_error": { "title": "Configuration Error", @@ -53,6 +60,12 @@ "demo_login_button": "Launch Demo", "demo_tagline": "Experience a full-featured email client. No account required.", "demo_no_signup": "No signup needed - explore freely with sample data", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, "oauth_completing": "Completing sign in...", "oauth_error": { "title": "Authentication Failed", @@ -114,6 +127,7 @@ "unified_archive": "All Archive", "unified_junk": "All Junk", "all_accounts": "All Accounts", + "all_accounts_loading": "Loading accounts: {loaded} of {total}", "unified_mailbox": "Unified Mailbox", "expand": "Expand", "collapse": "Collapse", @@ -134,7 +148,13 @@ "show_all_tags": "Show all ({count})", "show_fewer_tags": "Show less", "folders": "Folders", + "your_folders": "Your folders", + "create_folder": "Create folder", + "show_more": "More", + "show_fewer": "Less", "shared": "Shared", + "shared_account_hidden": "Hidden from the sidebar. Turn it back on in Settings › Layout.", + "notifications_muted": "Notifications muted", "mail": "Mail", "nav_label": "Navigation", "add_app": "Apps", @@ -143,7 +163,17 @@ "unified_all_starred": "All starred", "unified_all_mail": "All mail", "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Labels", + "create_label": "Create new label", + "mv_tasks": "Tasks", + "mv_side_panel": "Side panel", + "mv_open_full": "Open in full view", + "mv_previous_day": "Previous day", + "mv_next_day": "Next day", + "mv_add_another_account": "Add another account", + "mv_manage_account": "Manage your account", + "mv_storage_used": "{percent}% of {total} used" }, "protocol_handlers": { "title": "Default apps", @@ -214,6 +244,13 @@ "show_on_mobile": "Show on Mobile" }, "email_list": { + "columns": { + "from": "From", + "to": "To", + "subject": "Subject", + "date": "Date", + "size": "Size" + }, "no_emails": "No messages found", "no_emails_description": "This mailbox is empty", "no_search_results": "No results found", @@ -229,6 +266,7 @@ "replied": "Replied", "forwarded": "Forwarded", "has_attachment": "Has attachment", + "conversations_range": "{from}–{to, number} of {total, number}", "conversations_count": "{count} of {total} conversations", "conversations_count_plus": "{count}+ conversations", "conversations_count_simple": "{count} conversations", @@ -254,6 +292,7 @@ "button": "Empty folder", "confirm_title": "Empty folder", "confirm_message": "All emails in this folder will be permanently deleted. This action cannot be undone.", + "confirm_message_trash": "All emails in this folder will be moved to the Trash.", "confirm_button": "Empty folder", "junk_hint": "You can empty the Junk folder to permanently remove all messages.", "trash_hint": "You can empty the Trash folder to permanently remove all messages." @@ -310,6 +349,8 @@ "view_source": "View source", "fullscreen": "Fullscreen", "exit_fullscreen": "Exit fullscreen", + "view_in_dark_mode": "View in dark mode", + "view_in_light_mode": "View in light mode", "export_email": "Export as .eml", "forward_as_attachment": "Forward as attachment", "import_email": "Import .eml or .zip", @@ -334,6 +375,15 @@ "external_content_warning": "Images and external content have been blocked", "load_external_content": "Load images", "trust_sender": "Always trust this sender", + "sender_check": { + "unverified_label": "Unverified sender", + "failed_label": "Sender check failed", + "unverified": "This message claims to be from {domain}, but nothing confirms that. It has no valid signature from {domain} and didn't come from a server {domain} authorizes.", + "unverified_sent_from": "This message claims to be from {domain}, but it was sent from {host} and has no valid signature from {domain}.", + "failed": "This message claims to be from {domain}, but it failed that domain's sender checks. It may be forged.", + "failed_sent_from": "This message claims to be from {domain}, but it was sent from {host} and failed {domain}'s sender checks. It may be forged.", + "caution": "Be careful with links, attachments and requests for passwords or payment details." + }, "back_to_list": "Back to list", "view_contact": "View contact", "message_details": "Message Details", @@ -552,6 +602,8 @@ "more": "more", "scheduled_banner": "Scheduled to send at {date}", "scheduled_send_created": "Email scheduled for sending", + "sending_in": "Sending in {seconds, plural, one {# second} other {# seconds}}", + "undo_send_failed": "Could not undo. The message may already have been sent.", "cancel_scheduled_send": "Cancel send", "reschedule_send": "Reschedule", "cancel_and_edit": "Cancel and edit", @@ -576,19 +628,25 @@ "not_in_contacts": "Not in your contacts", "add_to_contacts": "Add to contacts", "copied": "Copied!", - "copy_failed": "Failed to copy" + "copy_failed": "Failed to copy", + "view_contact": "View contact", + "view_details": "View details" }, - "send_now": "Send now" + "send_now": "Send now", + "verification_code": { + "copy": "Copy code {code}", + "copied": "Code copied", + "copy_failed": "Could not copy the code" + }, + "mv_position": "{index} of {total}", + "plugin_panel_show": "Show plugin panel", + "plugin_panel_hide": "Hide plugin panel" }, "email_composer": { - "read_receipt_on": "Read receipt requested (click to disable)", - "read_receipt_off": "Request a read receipt", - "dsn_on": "Delivery notification requested (click to disable)", - "dsn_off": "Request a delivery notification", - "format_plain_text": "Switch to plain text", - "format_rich_text": "Switch to rich text (HTML)", - "require_tls_on": "Encrypted delivery required (click to disable)", - "require_tls_off": "Require encrypted delivery (TLS)", + "select_from_directory": "Select from directory", + "minimize": "Minimize", + "maximize": "Full screen", + "restore": "Restore", "new_message": "New Message", "signature_edit_hint": "Double-click to edit the signature", "reply": "Reply", @@ -603,14 +661,22 @@ "bcc": "BCC", "subject": "Subject", "body_placeholder": "Write your message...", + "formatting": "Formatting", "send": "Send", "cancel": "Cancel", "attach": "Attach", "attach_photos": "Photos & Videos", "attach_files": "Files", + "attach_from_files": "Attach from Files", + "files_picker": { + "attach": "{count, plural, =0 {Attach} one {Attach 1 file} other {Attach # files}}", + "empty_folder": "This folder is empty", + "load_failed": "Your files could not be loaded" + }, "discard": "Discard", "discard_draft_title": "Discard draft?", "discard_draft_confirm": "You have unsaved changes. Do you want to discard this draft?", + "discard_draft_message": "The draft will be deleted.", "saving": "Saving...", "sending": "Sending...", "add_link": "Add link", @@ -629,6 +695,7 @@ "forward": "Fwd:", "reply": "Re:" }, + "forward_batch_count": "{count, plural, one {1 email} other {# emails}}", "no_subject": "(No Subject)", "unknown_sender": "Unknown", "quote": { @@ -650,11 +717,27 @@ "email_placeholder": "alias@example.com", "envelope_notice": "If your server doesn't accept this address as the envelope sender, {identity} is used there instead, and recipients can see it in the Return-Path header." }, - "use_template": "Template", "save_as_template": "Save as Template", + "attach_from_device": "Upload from this device", + "templates_menu": "Templates", + "insert_template": "Insert template", + "options": "Options", + "option_plain_text": "Plain text", + "option_plain_text_hint": "Send without formatting", + "option_read_receipt": "Request read receipt", + "option_read_receipt_hint": "Ask the recipient to confirm they opened it", + "option_dsn": "Delivery notification", + "option_dsn_hint": "Get a report when the server delivers it", + "option_require_tls": "Require encrypted delivery", + "option_require_tls_hint": "Only deliver over TLS, otherwise bounce", + "option_priority": "Priority", + "option_priority_high": "High", + "option_priority_normal": "Normal", + "option_priority_low": "Low", "validation": { "recipient_required": "Add a recipient to send", "body_required": "Write a message or attach a file", + "invalid_recipients": "Not sent - check these addresses: {addresses}", "attachments_uploading": "Attachments are still uploading — sending as soon as they finish", "attachment_upload_failed": "Not sent — an attachment failed to upload. Remove it and try again." }, @@ -666,7 +749,11 @@ "drop_files": "Drop files to attach", "show_less": "Show less", "send_failed": "Failed to send email", + "send_offline": "You seem to be offline. The message is still here, send it again once you are back online.", "send_timeout": "The server stopped responding. Check your Sent folder before sending again — the message may have gone out.", + "send_recipients_rejected": "Not sent - the server rejected every recipient.", + "send_plugin_failed": "Not sent - a plugin that handles sending failed or took too long. Check your Sent folder before sending again.", + "send_some_recipients_rejected": "Sent, but not to these recipients - the server rejected them.", "continue_draft": "Continue draft", "close_draft_title": "Save or discard draft?", "close_draft_message": "You have unsaved changes. Would you like to save this as a draft or discard it?", @@ -694,6 +781,11 @@ }, "schedule_send": "Schedule send", "schedule_send_description": "Choose when the server should release this message.", + "schedule_preset": { + "tomorrow_morning": "Tomorrow morning", + "tomorrow_afternoon": "Tomorrow afternoon", + "monday_morning": "Monday morning" + }, "schedule_send_required": "Choose a date and time.", "schedule_send_invalid": "Enter a valid date and time.", "schedule_send_future": "Choose a future date and time.", @@ -706,8 +798,13 @@ "recipient_edit_name": "Edit display name", "recipient_email_placeholder": "Email address", "recipient_name_placeholder": "Display name", + "invalid_recipient": "Not a valid email address", "autocomplete_search_server": "Search the server", "autocomplete_searching": "Searching...", + "mention_recipients": "Recipients", + "mention_card_not_recipient": "Not in To or Cc, so they won't get this message.", + "mention_card_add": "Add to {field}", + "mention_card_remove": "Remove mention", "toolbar": { "bold": "Bold", "italic": "Italic", @@ -744,7 +841,9 @@ "background_color": "Background color", "remove_background_color": "Remove background color" }, - "send_filing_warning": "Sent - but the post-send cleanup failed, a stale draft may remain." + "send_filing_warning": "Sent - but the post-send cleanup failed, a stale draft may remain.", + "pop_out": "Pop out reply", + "send_guard_failed": "Not sent - a plugin that checks messages before sending failed or did not answer." }, "confirm_dialog": { "confirm": "Confirm", @@ -801,6 +900,10 @@ "emails_moved": "{count} emails moved", "moved_to_mailbox": "Moved to {mailbox}", "move_failed": "Move failed", + "removed_from_folder": "Removed from {mailbox}", + "remove_from_folder_failed": "Could not remove from this folder", + "emails_copied": "{count} emails copied", + "copy_failed": "Copy failed", "move_error": "Could not move emails to the selected folder", "email_tagged": "Email tagged", "emails_tagged": "{count} emails tagged", @@ -814,8 +917,8 @@ "identity_delete_failed": "Failed to delete identity: {error}", "identity_unauthorized": "You are not authorized to send from this email address", "identity_not_found": "Identity not found", - "vacation_saved": "Vacation responder settings saved", - "vacation_save_failed": "Failed to save vacation responder settings", + "vacation_saved": "Out of office settings saved", + "vacation_save_failed": "Failed to save out of office settings", "filters_saved": "Filters saved successfully", "filters_save_failed": "Failed to save filters", "filters_deleted": "Filter rule deleted", @@ -825,7 +928,20 @@ "templates_import_empty": "No templates found in the file", "export_email_error": "Failed to export email", "import_email_success": "Email imported successfully", - "import_email_error": "Failed to import email" + "import_email_error": "Failed to import email", + "rule_created": "Rule created", + "rule_merged": "Added to rule “{name}”", + "rule_already_covered": "Already covered by rule “{name}”", + "rule_apply_existing": "{count, plural, one {Apply to # existing message} other {Apply to # existing messages}}", + "rule_applied": "{count, plural, =0 {No messages needed changes} one {Rule applied to # message} other {Rule applied to # messages}}", + "rule_apply_failed": "Could not apply the rule to existing messages", + "rule_edit": "Edit rule", + "rule_undo": "Undo", + "rule_undone": "Rule change undone", + "rule_undo_failed": "Could not undo the rule change", + "rule_undo_conflict": "Your filters have changed since. Undo the change in Filters settings.", + "rule_folder_failed": "Could not create the folder", + "vacation_filters_save_failed": "Out of office settings saved, but the forwarding and reply recipients could not be saved" }, "date": { "today": "Today", @@ -913,7 +1029,7 @@ "privacy": "Privacy & Security", "account": "Account", "identities": "Identities", - "vacation": "Vacation Responder", + "vacation": "Out of Office", "advanced": "Advanced", "calendar": "Calendar", "filters": "Filters", @@ -928,6 +1044,7 @@ "sidebar_apps": "Sidebar Apps", "notifications": "Notifications", "layout": "Layout", + "themes": "Themes", "reading": "Reading", "composing": "Composing", "downloads": "Downloads", @@ -935,6 +1052,18 @@ "about_data": "About & Data", "debug": "Debug" }, + "themes": { + "title": "Themes", + "description": "Choose from themes deployed by your administrator and built-in presets.", + "locked": "Theme selection is locked by an administrator.", + "forced": "Theme \"{name}\" is forced by admin and cannot be changed", + "admin_theme": "Admin theme", + "activated": "Theme activated", + "default_restored": "Default theme restored", + "default_name": "Default", + "default_badge": "Default", + "admin_enforced": "Admin enforced" + }, "tab_groups": { "general": "General", "account": "Account & Identity", @@ -1025,6 +1154,12 @@ "regular": "Regular", "comfortable": "Comfortable" }, + "interface_layout": { + "label": "Interface layout", + "description": "Where search, navigation and toolbars sit", + "default": "Default", + "mountain_view": "Mountain View" + }, "animations": { "label": "Enable Animations", "description": "Show smooth transitions and effects" @@ -1073,6 +1208,14 @@ "label": "Tint List Rows by Tag Color", "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, "show_folder_total_count": { "label": "Show Total Message Count", "description": "Show the total message count next to folders and tags, alongside the unread count. Disable to show only unread counts." @@ -1081,6 +1224,10 @@ "label": "Unread Count on Tab and App Icon", "description": "Show the inbox unread count as a badge on the browser tab icon, and on the installed app icon where supported, so new mail is visible when the tab is not focused. Disable to keep both icons unbadged." }, + "show_shared_mail_accounts": { + "label": "Show shared mail accounts", + "description": "List the folders of mailboxes shared with you under Shared in the sidebar. Turn off to hide them all, or hide single accounts below. Their calendars and contacts stay." + }, "pro_interface": { "label": "Pro Interface (Experimental)", "description": "Desktop-only power-user layout with multi-tab message browsing and cross-account workflows. The standard interface is unaffected; you can switch back at any time.", @@ -1117,6 +1264,7 @@ "cancel": "Cancel", "migrating": "Updating tag on existing emails…", "migration_error": "Failed to update tag on existing emails", + "filters_migration_error": "Some filter rules still use the old tag — update them manually", "nesting": { "label": "Nested Tags", "description": "Nest tags underneath other tags and show them as a tree in the sidebar." @@ -1190,6 +1338,18 @@ "confirm_revoke_message": "It will stop receiving background notifications immediately. Enabling push again on that device registers it anew.", "confirm_revoke_message_this": "This device will stop receiving background notifications immediately." }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, "sound_selection": { "title": "Notification Sound", "description": "Choose which sound to play for notifications", @@ -1229,8 +1389,8 @@ "date_format": { "label": "Date Format", "description": "How dates are shown in the email list", - "smart": "Smart (locale-aware)", - "relative": "Relative (1h ago, 2d ago)", + "smart": "Smart", + "relative": "Relative", "full": "Always full date", "preview_today": "Today:", "preview_this_week": "This week:", @@ -1239,8 +1399,8 @@ "date_locale": { "label": "Date format region", "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", + "auto": "Automatic", + "iso": "ISO 8601", "dmy": "Day/Month/Year", "mdy": "Month/Day/Year" }, @@ -1324,6 +1484,10 @@ "label": "Permanently Delete Junk", "description": "Permanently delete emails from the Junk/Spam folder instead of moving them to Trash" }, + "confirm_bulk_delete": { + "label": "Confirm before deleting several messages", + "description": "Ask before moving several selected messages to Trash. Permanent deletion always asks." + }, "mail_layout": { "label": "Mail Layout", "description": "Choose between the classic split reading pane, a Gmail-style focused reading flow, or a Zimbra-style bottom reading pane.", @@ -1334,11 +1498,19 @@ "horizontal": "Reading pane at bottom", "horizontal_description": "Show the message list on top and open the selected message in a reading pane below it." }, + "list_columns": { + "label": "Show the list as columns", + "description": "Draw one-line rows as a table with From, Subject, Date and Size columns under a header." + }, "show_preview": { "label": "Show Preview Text", "description": "Display email preview in the list", "focus_description": "Display inline preview text inside the focused one-line message list" }, + "show_verification_codes": { + "label": "Show Verification Codes", + "description": "Offer the code from sign-in and confirmation emails for one-click copying, in the list for a day and in the message" + }, "disable_threading": { "label": "Disable Conversation Grouping", "description": "Show emails as individual messages instead of grouped by conversation" @@ -1467,6 +1639,17 @@ "label": "Empty subject warning", "description": "Ask for confirmation before sending a message with no subject." }, + "recipient_mentions": { + "label": "Mention recipients with @", + "description": "Typing @ in the message offers its recipients and inserts the chosen one's first name. Enter or Tab picks the highlighted name." + }, + "mention_adds_recipient": { + "label": "Contacts in @ mentions", + "description": "Typing @ also offers your contacts and people from the directory. Picking someone who isn't a recipient yet adds them to the message.", + "to": "Add to To", + "cc": "Add to Cc", + "off": "Only offer recipients" + }, "hide_inline_image_attachments": { "label": "Hide inline images from attachments", "description": "Images embedded in the message body are not listed as separate attachments" @@ -1561,6 +1744,7 @@ "auth_method_label": "Authentication", "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", "auth_method_basic": "Password", + "auth_method_token": "Access token", "demo_account": "Demo Account", "email": { "label": "Email Address", @@ -1591,6 +1775,40 @@ "drag_handle": "Drag to reorder", "add": "Add account" }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, "shared_accounts": { "title": "Shared with me", "description": "Group and shared accounts you can manage. Select one to edit its filters, vacation responder, calendars, and contacts.", @@ -1716,14 +1934,36 @@ }, "link_device": { "title": "Link Mobile App", - "description": "Sign in to the Bulwark Mail mobile app without typing anything. Generate a QR code here and scan it from the app's login screen.", + "description": "Sign in to the Bulwark Mail app on your phone without typing your password there. Your phone gets its own sign-in, so signing out here doesn't sign it out.", "generate": "Show QR code", "regenerate": "Show a new code", - "instructions": "Open the Bulwark Mail app, tap \"Scan QR code\" on the login screen, and point your camera here.", + "instructions": "Open the Bulwark Mail app and tap \"Scan a sign-in code\" on the sign-in screen (or \"Add account\" → \"Scan a sign-in code\" if you're already signed in), then point the camera here.", "expires_in": "This code expires in {seconds} seconds. It can only be used once.", "expired": "This code has expired.", - "generating": "Generating…", - "error": "Couldn't create a pairing code. Please try again." + "error": "Couldn't create a pairing code. Please try again.", + "password_prompt": "Enter your password to confirm it's you. Your phone gets its own sign-in; your password is not sent to the phone.", + "show_password": "Show password", + "hide_password": "Hide password", + "continue": "Continue", + "error_wrong_password": "Wrong password. Try again.", + "totp_prompt": "Enter the code from your authenticator app.", + "error_password_or_code": "The password or code is incorrect.", + "error_not_signed_in": "Your session has expired. Sign in again to link a device.", + "error_impersonation": "Devices can't be linked while you're viewing another user's mailbox.", + "error_too_many_attempts": "Too many attempts. Wait a moment and try again.", + "error_session_secret": "Linking a device isn't set up on this server (SESSION_SECRET is missing). Ask your administrator.", + "error_server_unreachable": "Couldn't reach your mail server. Try again.", + "error_pairing_unavailable": "Your mail server doesn't allow linking a device this way. Ask your administrator.", + "qr_alt": "Sign-in QR code", + "link_label": "Sign-in link", + "copy_link": "Copy sign-in link", + "copied": "Copied", + "copy_hint": "No camera? Copy the link and paste it in the app with \"Paste sign-in link\".", + "success_title": "Signed in on your phone", + "success_body": "You can close this page. To link another device, show a new code.", + "link_another": "Link another device", + "use_sso": "Use single sign-on instead", + "error_insecure_server": "The mobile app only connects to mail servers over https, and this one uses http." } }, "identities": { @@ -1744,28 +1984,24 @@ } }, "vacation": { - "title": "Vacation Responder", - "description": "Automatically reply to incoming emails while you're away", + "title": "Out of Office", + "description": "Automatically handle incoming emails while you're away", "loading": "Loading vacation settings...", "not_supported": "Your mail server does not support vacation responses.", "fetch_error": "Failed to load vacation settings. Please try again.", "status": { - "label": "Vacation Responder", + "label": "Reply automatically", "description": "Send an automatic reply to people who email you", "active": "Active", "inactive": "Inactive" }, "date_range": { - "title": "Date Range", - "description": "Optionally limit the auto-reply to a specific period", "start": "Start Date", "start_description": "Leave empty for no start limit", "end": "End Date", "end_description": "Leave empty for no end limit" }, "message": { - "title": "Auto-Reply Message", - "description": "The message that will be sent as a reply", "subject_label": "Subject", "subject_description": "Subject line of the auto-reply", "subject_placeholder": "Out of Office", @@ -1785,10 +2021,32 @@ "saving": "Saving...", "warnings": { "end_before_start": "End date must be after start date", + "invalid_date": "Enter a valid start and end date with a time, or leave the field empty", "start_in_past": "Start date is in the past", "empty_body": "Message body is empty - recipients will receive a blank reply", "subject_too_long": "The subject is too long for the server (at most {max} bytes).", - "body_too_long": "The message is too long for the server (at most {max} bytes, formatting included)." + "body_too_long": "The message is too long for the server (at most {max} bytes, formatting included).", + "forward_address": "Enter a valid address to forward to", + "not_running": "Forwarding or the reply recipients are saved but not active right now. Save to turn them back on." + }, + "forward": { + "title": "Forwarding", + "description": "Passes incoming messages on to another address, with or without the auto-reply, within the period above (without an end date, until switched off). Spam is not forwarded.", + "enabled_label": "Forward messages", + "to_label": "Forward to", + "to_placeholder": "email@example.com", + "keep_label": "Keep in inbox", + "keep_description": "Otherwise the message is only forwarded: it is not kept in this mailbox, and no filter rule runs on it." + }, + "audience": { + "label": "Reply to", + "description": "Internal senders: {domains}", + "all": "All senders", + "internal": "Internal senders only", + "external": "External senders only" + }, + "auto_reply": { + "title": "Automatic Reply" } }, "folders": { @@ -1813,11 +2071,25 @@ "reorder": "Drag up or down to reorder, left or right to change the parent folder", "reorder_error": "Failed to reorder folders", "move_error": "Failed to move folder", + "select_folder": "Select {name}", + "select_hint": "Select. Shift+click selects every folder up to the last one you clicked.", + "selected_count": "{count, plural, one {# folder} other {# folders}} selected", + "move_to": "Move to...", + "move_to_top_level": "Top level", + "move": "Move", + "clear_selection": "Clear selection", + "folders_moved": "{count, plural, one {# folder moved} other {# folders moved}}", + "move_partial_error": "{count, plural, one {# folder} other {# folders}} could not be moved", "subfolder_of": "Inside {name}", "subfolder_name": "Subfolder name", "new_folder_name": "Folder name", "rename": "Rename", "change_icon": "Change icon", + "count_mode": "Counter for {name}", + "count_mode_default": "Counter: default", + "count_mode_unread": "Unread only", + "count_mode_all": "Unread and total", + "count_mode_none": "No counter", "delete": "Delete", "confirm_delete": "Are you sure you want to delete \"{name}\"? Emails in this folder will be moved to Trash.", "create": "Create", @@ -1867,7 +2139,11 @@ }, "sender_favicons": { "label": "Sender Favicons", - "description": "Show website icons as profile pictures for business senders" + "description": "Show website icons as profile pictures for business senders", + "refresh_label": "Refresh Sender Icons", + "refresh_description": "Fetch the website icons again, for example when a sender changed theirs", + "refresh_button": "Refresh", + "refresh_done": "Sender icons will be fetched again" }, "show_avatars_in_junk": { "label": "Show Avatars in Junk Folder", @@ -1899,7 +2175,34 @@ "button": "Import" }, "about": { - "title": "Bulwark Webmail" + "title": "Bulwark Webmail", + "source_code": "Source code" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Click spam and phishing before they reach your inbox. Let real mail through. Lose all three shields and the inbox falls.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Real mail" + }, + "click_to_block": "Click to block", + "let_through": "Let it through", + "start": "Start", + "play_again": "Play again", + "score": "Score", + "combo": "Streak bonus", + "shields_left": "{count} of {total} shields left", + "seconds": "{seconds}s", + "best": "Best: {score}", + "new_best": "New best", + "held": "Inbox held", + "overrun": "Inbox overrun", + "stats": { + "caught": "Blocked", + "delivered": "Delivered", + "mistakes": "Mistakes" + } } }, "sidebar_apps": { @@ -2020,7 +2323,8 @@ "subject": "Subject", "header": "Custom Header", "size": "Size", - "body": "Body" + "body": "Body", + "all": "All messages" }, "comparators": { "has_any": "is present", @@ -2033,7 +2337,9 @@ "ends_with": "ends with", "matches": "matches pattern", "greater_than": "is greater than", - "less_than": "is less than" + "less_than": "is less than", + "address_is": "is the address", + "domain_is": "has the domain" }, "action_types": { "move": "Move to folder", @@ -2053,6 +2359,7 @@ "forward_placeholder": "email@example.com", "forward_keep_copy": "Keep a copy", "forward_limit": "Forward limit per message on this server: {count}. Extra forwards are skipped.", + "forward_unavailable": "This server does not allow forwarding. Forward actions in filter rules have no effect.", "reject_message": "Rejection message", "reject_placeholder": "Your email has been rejected", "label_name": "Tag name", @@ -2108,7 +2415,38 @@ "actions_count": "{count, plural, one {# action} other {# actions}}" }, "origin_external": "External", - "managed_by_tooltip": "Managed by {source}. Edit it in that app, or use the raw Sieve editor." + "managed_by_tooltip": "Managed by {source}. Edit it in that app, or use the raw Sieve editor.", + "quick_rule_names": { + "move_sender": "Move from {sender} to {folder}", + "move_domain": "Move from @{domain} to {folder}", + "move_list": "Move list {list} to {folder}", + "mark_read": "Mark as read from {sender}", + "tag": "Tag as {tag} from {sender}", + "block": "Block {sender}", + "custom": "Mail from {sender}", + "custom_plain": "New rule", + "senders": "{count, plural, one {# sender} other {# senders}}" + }, + "suggestions": "Suggestions", + "suggest_subject": "Subject “{subject}”", + "suggest_to": "To {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Also apply to existing messages in this folder", + "apply_existing_unsupported": "Only rules that check the sender, recipients, subject or headers, and that move, copy, mark as read, star or tag, can run on existing messages.", + "period_title": "Date Range", + "period_start": "Start Date", + "period_end": "End Date", + "validation_period_order": "End date must be after start date", + "period_toggle": "Only active during a period", + "period_hint": "Applies to messages that arrive during this period. Leave a field empty to keep that end open.", + "validation_period_empty": "Enter a start or end date for the period", + "validation_period_invalid": "Enter a valid date with a time for the period, or leave the field empty", + "apply_existing_period": "A rule with a period acts only on messages that arrive within it, so it cannot run on existing messages.", + "period_status_scheduled": "scheduled", + "period_status_active": "active", + "period_status_expired": "expired" }, "templates": { "title": "Email Templates", @@ -2148,12 +2486,6 @@ "title": "Display", "description": "Configure how files and folders are displayed" }, - "folder_layout": { - "label": "Folder Navigation", - "description": "Choose how folders are displayed: inline with files or in a sidebar tree", - "inline": "Inline", - "sidebar": "Sidebar" - }, "default_view": { "label": "Default View", "description": "Choose between grid and list layout", @@ -2230,17 +2562,41 @@ "pin": "Pin", "unpin": "Unpin", "move_to": "Move to...", + "copy_to_account": "Copy to account...", + "remove_from_folder": "Remove from this folder", "archive": "Archive", "delete": "Delete", "mark_as_spam": "Report spam", "not_spam": "Not spam", "tag": "Tag", "items_selected": "{count} emails selected", + "forward_as_attachments": "Forward as attachments", "edit_draft": "Edit Draft", "cancel_scheduled_send": "Cancel send", "reschedule_send": "Reschedule", "cancel_and_edit": "Cancel and edit", - "cancel_and_compose_again": "Cancel and compose again" + "cancel_and_compose_again": "Cancel and compose again", + "rules": { + "title": "Rules", + "move_from": "Always move messages from {sender}", + "move_from_domain": "Always move messages from @{domain}", + "move_from_list": "Always move messages from this list", + "mark_read_from": "Always mark as read from {sender}", + "tag_with": "Always tag with", + "block": "{count, plural, one {Block sender} other {Block # senders}}", + "no_junk": "This account has no Junk folder", + "create": "Create rule…", + "manage": "Manage rules", + "senders_count": "{count, plural, one {# sender} other {# senders}}", + "cross_account": "Select messages from one account", + "opaque_hint": "Your filters were edited by hand. Open Filters settings", + "loading": "Loading rules…", + "load_failed": "Could not load the rules", + "none": "No rules yet", + "new_mail_only": "Only for new messages", + "running": "Running \"{name}\" on {folder}…" + }, + "find_emails_from": "Find emails from {name}" }, "mailbox_context_menu": { "mark_folder_read": "Mark folder as read", @@ -2255,6 +2611,12 @@ "empty_folder_generic": "Empty folder", "delete_folder": "Delete folder", "refresh": "Refresh", + "hide_shared_account": "Hide from sidebar", + "mute_notifications": "Mute notifications", + "unmute_notifications": "Unmute notifications", + "mute_account_notifications": "Mute notifications for this account", + "unmute_account_notifications": "Unmute notifications for this account", + "reset_folder_notifications": "Reset folder notification settings", "mark_all_confirm_title": "Mark all folders as read", "mark_all_confirm_message": "Mark every unread message in your personal account as read?", "delete_confirm_title": "Delete folder", @@ -2298,7 +2660,10 @@ "next_email": "Next email", "previous_email": "Previous email", "open_email": "Open email", - "close_email": "Close / Deselect" + "close_email": "Close / Deselect", + "go_to_folder": "Go to folder", + "next_unread": "Next unread email", + "no_more_unread": "No unread email further down the list" }, "actions": { "reply": "Reply", @@ -2317,10 +2682,12 @@ "help": "Show shortcuts", "refresh": "Refresh emails", "select_all": "Select all", - "global_search": "Search everything" + "global_search": "Search everything", + "extend_selection": "Extend selection down / up" }, "threads": { - "expand_collapse": "Expand/collapse thread" + "expand_collapse": "Expand/collapse thread", + "select_mountain_view": "Select the conversation" }, "composer": { "send": "Send email", @@ -2435,6 +2802,48 @@ } }, "contacts": { + "directory": { + "title": "Directory", + "search_placeholder": "Search name or email address", + "refresh": "Refresh directory", + "retry": "Try again", + "loading": "Loading directory…", + "unavailable_title": "No directory", + "unavailable_hint": "Your mail server doesn't offer a directory of accounts.", + "forbidden_title": "Directory not available", + "forbidden_hint": "Your mail server doesn't allow this account to browse the directory. Ask your administrator.", + "error_title": "Couldn't load the directory", + "error_hint": "Check your connection and try again.", + "empty_title": "The directory is empty", + "empty_hint": "Your mail server lists no accounts with an email address.", + "no_match": "No one in the directory matches your search", + "refresh_failed": "Couldn't refresh the directory. Showing the last loaded list.", + "select_page": "Select everyone on this page", + "deselect_page": "Deselect everyone on this page", + "select_entry": "Select {name}", + "range": "{from}–{to} of {total}", + "page": "Page {page} of {count}", + "previous_page": "Previous page", + "next_page": "Next page", + "picker_title": "Select from directory", + "cancel": "Cancel", + "selected_count": "{count} selected", + "add_to": "Add to To", + "add_cc": "Add to Cc", + "add_bcc": "Add to Bcc", + "select_hint": "Select someone to see their details", + "write_email": "Write email", + "add_to_contacts": "Add to contacts", + "email": "Email", + "read_only_note": "Directory entries come from your mail server and can't be edited here.", + "type": { + "individual": "Person", + "group": "Group", + "resource": "Resource", + "location": "Location", + "other": "Other" + } + }, "title": "Contacts", "search_placeholder": "Search contacts...", "create_new": "New Contact", @@ -2796,6 +3205,10 @@ "tasks_hint": "Tasks (k)" }, "events": { + "hidden_before": "{count, plural, one {# earlier event} other {# earlier events}}", + "hidden_after": "{count, plural, one {# later event} other {# later events}}", + "show_all_hours": "Show all hours", + "show_display_hours": "Show only {start} – {end}", "create": "Create event", "edit": "Edit event", "delete": "Delete event", @@ -2803,6 +3216,8 @@ "no_events": "No events", "all_day": "All day", "more": "+{count} more", + "show_more": "Show more", + "show_less": "Show less", "no_title": "(No title)", "resize": "Resize event", "duplicate": "Duplicate", @@ -2825,6 +3240,7 @@ "save_note": "Save", "note_saved": "Note added", "open_link": "Open link", + "copy_location": "Copy location", "meeting_link": "Meeting link", "tentative": "Tentative", "cancelled": "Cancelled", @@ -2949,9 +3365,26 @@ "add": "Add reminder", "remove": "Remove reminder", "amount": "Reminder amount", - "unit": "Reminder unit" + "unit": "Reminder unit", + "action": "Reminder type", + "action_display": "Notification", + "action_email": "Email", + "by_email": "{label} by email", + "kept": "{count, plural, one {# more reminder that can't be edited here is kept} other {# more reminders that can't be edited here are kept}}", + "other": "{count, plural, one {# other reminder} other {# other reminders}}", + "calendar_defaults": "Calendar defaults", + "customize": "Customize", + "use_defaults": "Use calendar defaults" }, "settings": { + "limit_hours": "Limit visible hours", + "limit_hours_desc": "Show only these hours in the day and week views. Events outside them stay reachable from the arrows at the top and bottom of each day.", + "visible_hours": "Visible hours", + "visible_hours_start": "Start of visible hours", + "visible_hours_end": "End of visible hours", + "hide_non_working_days": "Hide non-working days", + "hide_non_working_days_desc": "Leave the days you don't work out of the week view. Their events still show in the month, day and agenda views.", + "working_days": "Working days", "title": "Calendar settings", "default_view": "Default view", "week_starts_on": "Week starts on", @@ -2975,7 +3408,7 @@ "enable_tasks": "Enable tasks", "enable_tasks_desc": "Show a tasks view in the calendar for managing to-dos", "show_tasks_on_calendar": "Show tasks on calendar", - "show_tasks_on_calendar_desc": "Display task chips on the day and week calendar views", + "show_tasks_on_calendar_desc": "Display tasks on the month, week and day calendar views", "hover_preview": "Event hover preview", "hover_preview_desc": "Show a detail popover when hovering over calendar events", "hover_preview_instant": "Instant", @@ -3061,7 +3494,9 @@ }, "quick_create": { "placeholder": "New event title", - "aria_label": "Quick create event" + "aria_label": "Quick create event", + "add_title": "Add title", + "more_options": "More options" }, "nav_prev": "Previous", "nav_next": "Next", @@ -3136,11 +3571,21 @@ "url_copied": "CalDAV URL copied to clipboard", "share": "Share calendar", "new_event_in_calendar": "New event in this calendar", + "shared_with": "Shared with {names}", + "shared_with_count": "{count, plural, one {Shared with 1 person} other {Shared with # people}}", + "move_up": "Move up", + "move_down": "Move down", "kind": "Contents", "kind_events": "Events", "kind_tasks": "Tasks", "kind_both": "Events and tasks", - "kind_hint": "Decides which apps see this calendar when it is synced over CalDAV. It cannot be changed later." + "kind_hint": "Decides which apps see this calendar when it is synced over CalDAV. It cannot be changed later.", + "default_alerts": "Default reminders", + "default_alerts_title": "Default reminders for \"{name}\"", + "default_alerts_hint": "New events in this calendar get these reminders unless you change them in the event. Changing them here doesn't change existing events. Email reminders are sent by the mail server.", + "default_alerts_timed": "Events with a time", + "default_alerts_all_day": "All-day events", + "default_alerts_updated": "Default reminders updated" }, "subscription": { "title": "iCal Subscription", @@ -3220,7 +3665,13 @@ "due_today": "Today", "due_tomorrow": "Tomorrow", "overdue": "Overdue" - } + }, + "toolbar": { + "more": "More actions", + "select_view": "Change view, currently {view}" + }, + "other_calendars": "Other calendars", + "calendar_options": "Options for {name}" }, "calendar_event_notifications": { "someone": "Someone", @@ -3276,6 +3727,7 @@ "body_placeholder": "Body contains...", "folder": "Folder", "all_folders": "All folders", + "all_folders_except_spam_trash": "All folders except Spam and Trash", "has_attachment": "Attachments", "date_after": "After", "date_before": "Before", @@ -3299,7 +3751,13 @@ "suggestions_people": "People", "suggestions_from": "Search mail from {address}", "suggestions_to": "Search mail to {address}", - "suggestions_remove_recent": "Remove from recent searches" + "suggestions_remove_recent": "Remove from recent searches", + "has_words": "Has the words", + "size": "Size", + "date_within": "Date", + "search_in": "Search in", + "has_attachment_check": "Has attachment", + "search": "Search" }, "welcome": { "title": "Welcome to your mailbox", @@ -3307,6 +3765,8 @@ "tip_shortcuts": "Press ? to see all keyboard shortcuts", "tip_sidebar": "Find Calendar, Contacts and Settings in the sidebar menu", "tip_settings": "Customize your experience in Settings", + "tip_compose_touch": "Tap the pencil button to write a new email", + "tip_sidebar_touch": "Find Calendar, Contacts and Settings in the bar at the bottom", "got_it": "Got it", "settings": "Settings", "dismiss": "Dismiss", @@ -3328,10 +3788,8 @@ "office_close": "Close editor", "open_in_new_tab": "Open in new tab", "title": "Files", - "search_placeholder": "Search files...", + "search_placeholder": "Search this folder", "empty_state_title": "No files yet", - "empty_state_description": "Upload files or create folders to get started", - "upload": "Upload", "upload_files": "Upload Files", "new_folder": "New Folder", "new_folder_name": "Folder name", @@ -3350,13 +3808,11 @@ "type": "Type", "folder": "Folder", "file": "File", - "parent_directory": "Parent directory", - "breadcrumb_root": "Home", - "other_accounts": "Other accounts", "no_accounts": "No connected accounts.", - "drop_files_here": "Drop files or folders here to upload", "uploading": "Uploading...", "upload_success": "{count, plural, one {1 file uploaded} other {# files uploaded}}", + "upload_canceled": "Upload canceled", + "upload_canceled_partial": "{count, plural, one {1 file was} other {# files were}} uploaded before you canceled", "upload_error": "Failed to upload file", "create_folder_success": "Folder created", "create_folder_error": "Failed to create folder", @@ -3389,7 +3845,6 @@ "file_name": "File name", "retry": "Retry", "refresh": "Refresh", - "toggle_favorite": "Toggle favorite", "duplicate": "Duplicate", "duplicate_success": "Duplicated successfully", "duplicate_error": "Failed to duplicate", @@ -3399,10 +3854,10 @@ "shared_by": "Shared by {name}", "create_file_success": "File created", "create_file_error": "Failed to create file", - "favorites": "Favorites", + "favorites": "Starred", "recent": "Recent", - "properties": "Properties", - "open_folder": "Open folder", + "show_in_folder": "Show in folder", + "remove_from_recent": "Remove from recent", "upload_folder": "Upload Folder", "file_too_large": "\"{name}\" exceeds the maximum file size ({max})", "undo": "Undo", @@ -3412,35 +3867,57 @@ "open_folder_tree": "Open folder tree", "file_list": "Files and folders", "context_menu": "Actions", - "settings_title": "File Settings", - "settings_display": "Display", - "settings_default_view": "Default View", - "settings_default_view_desc": "Choose between grid and list layout", - "settings_default_sort": "Default Sort", - "settings_default_sort_desc": "Choose the default sorting for files", - "settings_sort_direction": "Sort Direction", - "settings_sort_direction_desc": "Choose ascending or descending order", "settings_ascending": "Ascending", "settings_descending": "Descending", - "settings_icons": "Icons", - "settings_show_icons": "Show File Icons", - "settings_show_icons_desc": "Display icons next to files and folders", - "settings_colored_icons": "Colored Icons", - "settings_colored_icons_desc": "Use colorful icons instead of monochrome", - "settings_show_thumbnails": "Show Thumbnails", - "settings_show_thumbnails_desc": "Display image previews instead of icons for image files", - "settings_behavior": "Behavior", - "settings_show_hidden": "Show Hidden Files", - "settings_show_hidden_desc": "Display files and folders that start with a dot", - "settings_folder_layout": "Folder Navigation", - "settings_folder_layout_desc": "Choose how folders are displayed: inline with files or in a sidebar tree", - "settings_folder_layout_inline": "Inline", - "settings_folder_layout_sidebar": "Sidebar", "disabled_title": "Files feature is disabled by your administrator", "disabled_description": "Large file uploads via WebDAV can cause Stalwart/RocksDB instability, including out-of-memory crashes and unrecoverable disk usage. Deleted files may not be immediately purged from blob storage. This feature is not recommended for production environments.", - "stability_warning": "Large file uploads can cause server instability. Deleted files may not be immediately purged from storage. Use with caution.", "migration_title": "Updating your files…", - "migration_description": "Organising folders and files into their proper structure. This happens only once." + "migration_description": "Organising folders and files into their proper structure. This happens only once.", + "new": "New", + "my_files": "My files", + "folders": "Folders", + "files_heading": "Files", + "open": "Open", + "star": "Star", + "unstar": "Remove star", + "expand_folder": "Expand {name}", + "collapse_folder": "Collapse {name}", + "sidebar_hint": "Star folders and open files to find them here.", + "breadcrumbs": "Folder path", + "show_path": "Show hidden folders in the path", + "selection_actions": "Actions for selected items", + "selected_count": "{count, plural, one {1 selected} other {# selected}}", + "clear_selection": "Clear selection", + "select_all": "Select all", + "select_item": "Select {name}", + "more_actions": "More actions for {name}", + "paste_here": "Paste into this folder", + "paste_count": "{count, plural, one {Paste 1 item} other {Paste # items}}", + "sort_by": "Sort by", + "view_mode": "Layout", + "clear_search": "Clear search", + "no_results_for": "Nothing in this folder matches \"{query}\"", + "download_count": "{count, plural, one {Download 1 file} other {Download # files}}", + "delete_count": "{count, plural, one {Delete 1 item} other {Delete # items}}", + "close_details": "Close details", + "file_kind": "{ext} file", + "folder_summary": "{folders, plural, =0 {No folders} one {1 folder} other {# folders}}, {files, plural, =0 {no files} one {1 file} other {# files}}", + "details_hint": "Select a file or folder to see its details.", + "sharing": "Sharing", + "shared_with_count": "{count, plural, one {Shared with 1 person} other {Shared with # people}}", + "only_you": "Only you", + "empty_folder_title": "This folder is empty", + "empty_folder_description": "Drop files here, or upload them from your device.", + "empty_folder_read_only": "You can't add files to this folder.", + "choose_account": "Choose an account", + "drop_to_upload": "Drop to upload to {folder}", + "uploading_count": "Uploading {current} of {total}", + "dismiss": "Dismiss", + "zoom_in": "Zoom in", + "zoom_out": "Zoom out", + "zoom_reset": "Fit to screen", + "rotate": "Rotate", + "close_preview": "Close" }, "smime": { "your_certificates": "Your Certificates", @@ -3613,6 +4090,354 @@ "failed_detail": "Authorization failed: {error}", "missing_params": "Missing authorization code or state.", "delivery_failed": "The authorization result could not be delivered to the app tab. Please close this window and try again." + }, + "settings": { + "title": "Plugins", + "description": "Plugins your administrator added. Turn them on or off for your account.", + "description_uploads": "Plugins add features to the webmail. Turn them on or off for your account, or install your own.", + "description_demo": "Plugins are not available in the demo.", + "install_button": "Install plugin", + "install_hint": "Choose a plugin ZIP file.", + "install_hint_approval": "Choose a plugin ZIP file. An administrator has to approve it before you can turn it on.", + "install_uploads_off": "Your administrator does not allow installing plugins.", + "install_managed_id": "Your administrator already provides a plugin with this name, so it can't be replaced.", + "install_invalid": "This file is not a plugin that can be installed: {details}", + "toast_installed": "Plugin installed. Turn it on below.", + "toast_installed_pending": "Plugin installed and sent to your administrator for approval.", + "toast_enabled": "{name} is on", + "toast_disabled": "{name} is off", + "toast_removed": "{name} was removed", + "toast_forced": "Your administrator keeps {name} turned on", + "toast_approval_requested": "{name} is waiting for approval by your administrator", + "empty_title": "No plugins yet", + "empty_admin": "Your administrator has not added any plugins.", + "empty_uploads": "Install a plugin from a ZIP file to get started.", + "empty_demo": "Plugins need a real account, so the demo has none.", + "syncing": "Checking plugins with the server…", + "status_installed": "Off", + "status_enabled": "Starting", + "status_running": "On", + "status_disabled": "Off", + "status_error": "Problem", + "badge_forced": "Required", + "badge_managed": "From your administrator", + "badge_yours": "Installed by you", + "badge_awaiting": "Awaiting approval", + "toggle_label": "Turn {name} on or off", + "forced_note": "Your administrator requires this plugin, so it can't be turned off.", + "permissions": "What it can do", + "permissions_managed": "What it can do (approved by your administrator)", + "settings": "Settings", + "remove": "Remove", + "remove_title": "Remove {name}?", + "remove_message": "The plugin and everything it stored in this browser are deleted. You can install it again later.", + "remove_confirm": "Remove", + "error_user_plugins_off": "Your administrator turned off plugins installed by users, so this plugin does not run.", + "error_approval_unavailable": "This plugin was installed before approvals were required. Remove it and install it again to send it for approval.", + "error_approval_denied": "Your administrator did not approve this plugin.", + "error_approval_pending": "Waiting for your administrator to approve this plugin.", + "error_auto_disabled": "Turned off because it kept failing. Turn it on again to retry.", + "accounts_heading": "Connected accounts", + "account_on": "{label} on {host}", + "account_waiting": "{label} on {host} (sign-in not finished)", + "disconnect": "Disconnect", + "disconnect_failed": "Could not disconnect {label}" + }, + "consent": { + "title": "Allow {name} to do this?", + "intro": "The plugin asks for the permissions below. It only gets them if you allow it.", + "high_risk_note": "Marked items give the plugin access to the content of your mail or let it send data elsewhere. Only allow them for plugins you trust.", + "revoke_hint": "You can turn the plugin off or remove it in Settings at any time.", + "deny": "Don't allow", + "allow": "Allow" + }, + "permissions": { + "none": "It needs no special permissions.", + "high_risk": "Needs care", + "email_read": { + "title": "Read your email", + "body": "See senders, recipients, subjects and the full text of any message in your account, and your folders." + }, + "email_write": { + "title": "Change and organise your email", + "body": "Add or remove tags, flags and other keywords on your messages, move them between your folders and to the archive, and save drafts." + }, + "email_send": { + "title": "Change mail before it is sent", + "body": "Read and change the recipients, subject, text and attachments of messages you write, and stop a message from being sent." + }, + "crypto_full": { + "title": "Full access to this app", + "body": "Runs with the same rights as the webmail itself. It can read all your mail and stored keys and act for you. Only allow this for plugins you fully trust." + }, + "email_raw_send": { + "title": "Send messages it built itself", + "body": "Send complete messages, for example signed or encrypted ones, from your account." + }, + "email_blob_read": { + "title": "Read the raw content of messages", + "body": "Read attachments and the full source of messages you act on, and every file you attach before it is sent." + }, + "email_blob_write": { + "title": "Replace attachments before sending", + "body": "Upload files to your mail server and replace a file you attached with another one before the message is sent." + }, + "email_render_takeover": { + "title": "Change how messages are shown", + "body": "Replace the text of messages you open and of the message list, for example to show decrypted content." + }, + "calendar_read": { + "title": "Read your calendar", + "body": "See your calendars and their events." + }, + "calendar_write": { + "title": "Change your calendar", + "body": "Create, change and delete events." + }, + "contacts_read": { + "title": "Read your contacts", + "body": "Search and read your address books." + }, + "contacts_write": { + "title": "Change your contacts", + "body": "Create, change and delete contacts and address books." + }, + "files_read": { + "title": "Read your files", + "body": "Browse and open the files stored in your account." + }, + "files_write": { + "title": "Change your files", + "body": "Upload, rename, move and delete files in your account." + }, + "identity_read": { + "title": "See your sender addresses", + "body": "See the addresses you send from and their signatures." + }, + "identity_write": { + "title": "Change your sender addresses", + "body": "Create, change and delete sender addresses and signatures." + }, + "filters_read": { + "title": "Read your mail filters", + "body": "See the rules that sort your incoming mail." + }, + "filters_write": { + "title": "Change your mail filters", + "body": "Add rules to your server-side filters. Such rules can move, forward or delete incoming mail." + }, + "tasks_read": { + "title": "Read your tasks", + "body": "See your task lists and tasks." + }, + "tasks_write": { + "title": "Change your tasks", + "body": "Create, change, complete and delete tasks." + }, + "templates_read": { + "title": "Read your templates", + "body": "See your saved message templates." + }, + "templates_write": { + "title": "Change your templates", + "body": "Create, change and delete message templates." + }, + "smime_read": { + "title": "See your S/MIME status", + "body": "See whether you have S/MIME keys and certificates, without access to the keys." + }, + "vacation_read": { + "title": "See your out-of-office reply", + "body": "See whether your automatic reply is on and what it says." + }, + "vacation_write": { + "title": "Change your out-of-office reply", + "body": "Turn your automatic reply on or off and change its text and dates." + }, + "settings_read": { + "title": "Read your settings", + "body": "See your preferences and tag definitions." + }, + "settings_write": { + "title": "Change your settings", + "body": "Add and reorder tags and change your preferences." + }, + "security_read": { + "title": "See your account security", + "body": "See whether two-factor sign-in and encryption are on, without access to secrets." + }, + "auth_observe": { + "title": "Know when you sign in", + "body": "Be told when you sign in, switch accounts or your session is renewed." + }, + "auth_emit": { + "title": "Sign you out", + "body": "Sign you out of the webmail." + }, + "account_read": { + "title": "See your accounts", + "body": "See the accounts you added, with their addresses and servers." + }, + "http_post": { + "title": "Use the webmail server for you", + "body": "Call these server endpoints with your login:" + }, + "http_fetch": { + "title": "Connect to other websites", + "body": "Send requests to these sites. Anything the plugin can read may be sent there:" + }, + "admin_config": { + "title": "Read its settings from your administrator", + "body": "Read the settings your administrator saved for this plugin." + }, + "ui_observe": { + "title": "Follow where you are in the app", + "body": "Know which page is open and react to links you click." + }, + "ui_toolbar": { + "title": "Add buttons to toolbars", + "body": "Show its own buttons in the message toolbar, on an open message and in the top bar." + }, + "ui_app_top_banner": { + "title": "Show a banner at the top", + "body": "Show a notice across the top of the app." + }, + "ui_email_banner": { + "title": "Show notices on messages", + "body": "Show its own notices and summaries above, inside and between the parts of an open message or conversation." + }, + "ui_email_footer": { + "title": "Add content below messages", + "body": "Show its own content under an open message." + }, + "ui_contact_cryptokeys": { + "title": "Show encryption keys of contacts", + "body": "Show and manage the encryption keys on a contact." + }, + "ui_email_details": { + "title": "Add panels to message details", + "body": "Show its own panel next to or below an open message." + }, + "ui_download_file": { + "title": "Save files to your device", + "body": "Offer files it created for download." + }, + "ui_message_list_tabs": { + "title": "Add tabs to your inbox", + "body": "Sort your inbox into tabs such as Updates or Promotions." + }, + "ui_composer_toolbar": { + "title": "Add to the composer", + "body": "Show its own buttons, menu entries and sections while you write a message." + }, + "ui_composer_sidebar": { + "title": "Add a panel to the composer", + "body": "Show its own panel next to the message you write." + }, + "ui_sidebar_widget": { + "title": "Add to the sidebar", + "body": "Show its own boxes and sections in the mail sidebar." + }, + "ui_settings_section": { + "title": "Add a settings page", + "body": "Show its own page in Settings." + }, + "ui_context_menu": { + "title": "Add to the message menu", + "body": "Add entries to the menu that opens when you right-click a message." + }, + "ui_navigation_rail": { + "title": "Add to the navigation bar", + "body": "Show its own button in the navigation bar." + }, + "ui_keyboard": { + "title": "Add keyboard shortcuts", + "body": "React to keyboard shortcuts it defines." + }, + "ui_calendar_action": { + "title": "Add to the calendar", + "body": "Show its own buttons and sections on events, and short labels and dots on calendar days." + }, + "ui_admin_page": { + "title": "Add an admin page", + "body": "Show its own page in the admin settings." + }, + "app_lifecycle": { + "title": "Know when the app starts and stops", + "body": "Be told when the app starts, goes offline or you sign out." + }, + "ui_message_list": { + "title": "Add to the message list", + "body": "Show its own content above the message list, and buttons on messages and in the selection toolbar." + }, + "ui_contacts": { + "title": "Add to contacts", + "body": "Show its own sections in a contact's details and buttons in the contact list." + }, + "ui_tasks": { + "title": "Add to tasks", + "body": "Show its own sections in a task's details and buttons in the task list." + }, + "ui_files": { + "title": "Add to files", + "body": "Show its own sections in the file preview and entries in a file's menu." + }, + "ui_commands": { + "title": "Add commands", + "body": "Add its own entries to the command palette (Ctrl+K)." + }, + "ui_app_page": { + "title": "Add pages", + "body": "Add its own pages to the app, each with a button in the navigation bar." + }, + "http_proxy": { + "title": "Use accounts you connect", + "body": "Send requests through Bulwark's server with an account or key that you or your administrator set up. The plugin never sees the password, but it can do anything that account can do on its own server." + }, + "compose_open": { + "title": "Open the composer for you", + "body": "Open a new message with recipients, text or attachments filled in. You still decide whether to send it." + }, + "compose_read": { + "title": "Read the message you are writing", + "body": "Read the text of a message while you write it, when you use the plugin in the composer. Not the signature or the quoted message." + }, + "compose_write": { + "title": "Change the message you are writing", + "body": "Replace or add text in a message while you write it, when you use the plugin in the composer. You can undo each change, and you still decide whether to send." + }, + "ui_notifications": { + "title": "Show notifications", + "body": "Show notifications on your device, or in the app if you haven't allowed them. Each one names the plugin." + } + }, + "dialog": { + "from_plugin": "From the plugin {name}", + "ok": "OK", + "submit": "Submit", + "confirm": "Confirm", + "cancel": "Cancel", + "close": "Close", + "turn_off": "Turn off {name}", + "plugin_menu": "Options for {name}", + "password_note": "This form comes from the plugin {name}, not from the webmail. Only enter a password if you trust this plugin.", + "toast_auto_disabled": "{name} was turned off because it kept failing." + }, + "connect": { + "title": "Connect {label}", + "intro_password": "{name} wants to use your {label} account. Bulwark keeps the password on its server and only sends it to {host}. The plugin never sees it.", + "intro_password_any": "{name} wants to use your {label} account. Bulwark keeps the password on its server and only sends it to the server you enter here. The plugin never sees it.", + "intro_signin": "{name} wants to sign in to your {label} account. Enter the server here, then sign in on its own page. Bulwark keeps the access it gets on its server; the plugin never sees it.", + "server": "Server address", + "server_hint": "cloud.example.com", + "username": "User name", + "secret": "Password or key", + "connect": "Connect", + "continue": "Continue", + "cancel": "Cancel", + "saving": "Saving…", + "error_server": "Enter the server's address, for example https://cloud.example.com", + "error_save": "Could not save: {error}" } }, "global_search": { @@ -3641,5 +4466,41 @@ "preview_empty": "Select a result to preview it", "open_full": "Open", "view_label": "View" + }, + "top_bar": { + "more": "More options" + }, + "plugin_pages": { + "loading": "Loading page", + "not_available_title": "This page is not available", + "not_available_description": "The plugin that added it is turned off or no longer installed.", + "back_to_mail": "Back to Mail", + "settings_group": "Plugins" + }, + "command_palette": { + "title": "Command palette", + "open": "Open the command palette", + "placeholder": "Type a command", + "no_results": "No matching commands", + "go_to": "Go to {name}", + "compose": "New message", + "search": "Search mail", + "theme_dark": "Switch to dark theme", + "theme_light": "Switch to light theme", + "shortcuts": "Show keyboard shortcuts", + "group_navigation": "Go to", + "group_actions": "Actions", + "group_plugins": "Plugins" + }, + "notification_center": { + "title": "Notifications", + "mark_all_read": "Mark all read", + "clear": "Clear", + "empty": "No notifications", + "close": "Close", + "unread": "Unread", + "unread_count": "{count} unread", + "level_error": "Error", + "level_warning": "Warning" } } diff --git a/locales/es/common.json b/locales/es/common.json index 3972660a..e609f900 100644 --- a/locales/es/common.json +++ b/locales/es/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Cliente de correo web minimalista que usa el protocolo JMAP", - "login": { - "title": "Correo Web", - "username_label": "Correo electrónico", - "username_placeholder": "usuario@ejemplo.com", - "password_label": "Contraseña", - "password_placeholder": "Ingrese su contraseña", - "jmap_endpoint_label": "Servidor JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "El servidor debe permitir solicitudes CORS desde este dominio.", - "jmap_server_label": "Servidor", - "jmap_server_auto_picked": "Servidor seleccionado a partir del dominio de tu correo.", - "sign_in": "Iniciar sesión", - "signing_in": "Iniciando sesión...", - "loading": "Cargando...", - "reconnecting": "Conexión perdida. Intentando reconectar…", - "error": { - "invalid_credentials": "Correo electrónico o contraseña inválidos", - "connection_failed": "No se pudo conectar con el servidor", - "cors_blocked": "El servidor es accesible pero está bloqueando las solicitudes de origen cruzado. Verifique la configuración CORS de su servidor JMAP y permita este dominio.", - "server_error": "El servidor no está disponible temporalmente. Inténtalo más tarde.", - "generic": "Ocurrió un error. Por favor, inténtelo de nuevo.", - "totp_required": "Se requiere un código de autenticación de dos factores. Introduce tu código a continuación.", - "totp_invalid": "Código de autenticación inválido. Verifica tu aplicación de autenticación.", - "oauth_discovery_failed": "SSO está habilitado pero no se pudo contactar al proveedor de identidad. Verifica tu configuración OAuth." - }, - "show_password": "Mostrar contraseña", - "hide_password": "Ocultar contraseña", - "totp_toggle": "Tengo un código 2FA", - "remember_me": "Recordarme", - "config_error": { - "title": "Error de Configuración", - "fetch_failed": "No se pudo cargar la configuración de la aplicación. Por favor, inténtelo más tarde.", - "server_not_configured": "El servidor de correo no ha sido configurado. Por favor, contacte a su administrador." - }, - "remove_from_history": "Eliminar del historial", - "totp_label": "Código de autenticación", - "totp_placeholder": "000000", - "session_expired": "Tu sesión ha expirado. Inicia sesión de nuevo.", - "dismiss": "Cerrar", - "or": "o", - "sign_in_sso": "Iniciar sesión con SSO", - "add_account_title": "Agregar cuenta", - "add_account_subtitle": "Iniciar sesión con otra cuenta", - "cancel": "Cancelar", - "website": "Sitio web", - "imprint": "Aviso legal", - "privacy_policy": "Política de privacidad", - "try_demo": "Probar demo", - "demo_description": "Explora con datos de ejemplo - sin cuenta necesaria", - "demo_launching": "Iniciando demo...", - "demo_login_button": "Iniciar demo", - "demo_tagline": "Experimenta un cliente de correo completo. Sin necesidad de cuenta.", - "demo_no_signup": "Sin registro - explora libremente con datos de ejemplo", - "oauth_completing": "Completando inicio de sesión...", - "oauth_error": { - "title": "Error de autenticación", - "invalid_state": "La validación de seguridad falló. Intenta iniciar sesión de nuevo.", - "missing_params": "Faltan datos de autorización. Intenta iniciar sesión de nuevo.", - "token_exchange_failed": "No se pudo completar la autenticación. Inténtalo de nuevo.", - "access_denied": "Acceso denegado. Contacta a tu administrador.", - "back_to_login": "Volver al inicio de sesión" - } - }, - "sidebar": { - "close": "Cerrar", - "compose": "Redactar", - "compose_hint": "Redactar (c)", - "search_placeholder": "Buscar correo...", - "search_placeholder_hint": "Buscar correo... (pulsa /)", - "storage": "Almacenamiento", - "storage_used": "Usado", - "storage_free": "Libre", - "storage_total": "Total", - "sign_out": "Cerrar sesión", - "sign_out_of": "Cerrar sesión de {account}", - "sign_out_all": "Cerrar sesión de todas las cuentas", - "add_account": "Agregar cuenta", - "set_as_default": "Establecer como predeterminada", - "switch_account": "Cambiar cuenta", - "contacts": "Contactos", - "calendar": "Calendario", - "settings": "Configuración", - "admin": "Admin", - "files": "Archivos", - "loading_mailboxes": "Cargando buzones...", - "push_connected": "Actualizaciones en tiempo real activas", - "push_disconnected": "Actualizaciones en tiempo real inactivas", - "keyboard_shortcuts": "Atajos de teclado", - "theme": { - "light": "Modo claro", - "dark": "Modo oscuro", - "system": "Tema del sistema" - }, - "language": { - "title": "Idioma" - }, - "mailboxes": { - "inbox": "Bandeja de entrada", - "sent": "Enviados", - "drafts": "Borradores", - "trash": "Papelera", - "archive": "Archivo", - "starred": "Destacados", - "all_mail": "Todo el correo", - "spam": "Spam", - "important": "Importante" - }, - "unified_inbox": "Bandeja unificada", - "unified_sent": "Todos los enviados", - "unified_drafts": "Todos los borradores", - "unified_trash": "Todas las papeleras", - "unified_archive": "Todos los archivos", - "unified_junk": "Todo el spam", - "all_accounts": "Todas las cuentas", - "unified_mailbox": "Buzón unificado", - "expand": "Expandir", - "collapse": "Contraer", - "expand_tooltip": "Expandir", - "collapse_tooltip": "Contraer", - "mobile": { - "search": "Buscar", - "compose": "Redactar", - "go_back": "Volver", - "toggle_menu": "Alternar menú" - }, - "clear_search": "Limpiar búsqueda", - "vacation_active": "Respuesta automática activa", - "demo_banner": "Modo demo", - "demo_reset": "Restablecer", - "demo_tour": "Tour", - "tags": "Etiquetas", - "show_all_tags": "Mostrar todo ({count})", - "show_fewer_tags": "Mostrar menos", - "folders": "Carpetas", - "mail": "Correo", - "nav_label": "Navegación", - "add_app": "Apps", - "shared": "Compartido", - "scheduled": "Programados", - "unified_all_unread": "Todo no leído", - "unified_all_starred": "Todo destacado", - "unified_all_mail": "Todo el correo", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Aplicaciones predeterminadas", - "description": "Elige si los enlaces de correo y calendario se abren en Bulwark. Técnicamente, Bulwark se registra como controlador de protocolo para enlaces mailto: y webcal:.", - "unsupported": "Este navegador o esta conexión no admite el registro manual de controladores de protocolo. Es posible que aún puedas usar la PWA instalada desde la configuración del navegador o del sistema.", - "mailto_label": "Enlaces de correo", - "mailto_description": "Abre enlaces mailto: en Bulwark con el redactor rellenado previamente.", - "protocol_open_mode_label": "Al abrir enlaces de protocolo", - "protocol_open_mode_description": "Elige si Bulwark abre los enlaces mailto: y webcal: en una nueva pestaña o reutiliza una sesión abierta. La opción de sesión activa necesita permiso de notificaciones para que puedas hacer clic en una notificación de respaldo y traer Bulwark al frente si el navegador bloquea el foco.", - "protocol_open_mode_active_session": "Abrir en la sesión activa si es posible", - "protocol_open_mode_new_tab": "Abrir siempre una nueva pestaña", - "focus_notification_title": "Abrir Bulwark", - "focus_notification_body": "El enlace se abrió en Bulwark. Haz clic para traer la ventana al frente.", - "webcal_label": "Enlaces de calendario", - "webcal_description": "Abre enlaces webcal: en Bulwark con un diálogo de suscripción al calendario rellenado previamente.", - "register_mailto": "Registrar aplicación de correo", - "register_webcal": "Registrar aplicación de calendario", - "mailto_registered": "Registro del controlador de correo solicitado", - "webcal_registered": "Registro del controlador de calendario solicitado", - "registration_failed": "No se pudo registrar el controlador de protocolo", - "opening_mailto": "Abriendo redactor...", - "opening_webcal": "Abriendo calendario...", - "browser_note": "Tu navegador o sistema operativo puede pedirte confirmación y puede requerir que Bulwark esté instalado antes de poder seleccionarlo como aplicación predeterminada.", - "select_account_title": "Elegir cuenta", - "select_mailto_account": "Elige qué cuenta debe abrir este enlace de correo.", - "select_webcal_account": "Elige qué cuenta debe abrir este enlace de calendario.", - "select_account_note": "Esta selección solo se aplica a este enlace de protocolo.", - "detail_to": "Para", - "detail_subject": "Asunto", - "detail_no_subject": "Sin asunto", - "detail_calendar": "Calendario", - "detail_source": "Fuente", - "active_account": "Activa", - "switching_account": "Cambiando de cuenta..." - }, - "sidebar_apps": { - "modal_title": "Aplicaciones de la barra lateral", - "add_new": "Añadir aplicación", - "edit_app": "Editar aplicación", - "name_label": "Nombre", - "name_placeholder": "Mi aplicación", - "name_required": "El nombre es obligatorio", - "url_label": "URL", - "url_required": "La URL es obligatoria", - "url_invalid": "Introduce una URL http o https válida", - "icon_label": "Icono", - "icon_required": "El icono es obligatorio", - "open_mode_label": "Modo de apertura", - "open_new_tab": "Nueva pestaña", - "open_inline": "Integrado", - "cancel": "Cancelar", - "add": "Añadir", - "update": "Actualizar", - "delete": "Eliminar", - "delete_confirm_title": "Eliminar aplicación", - "delete_confirm": "¿Estás seguro de que quieres eliminar \"{name}\"?", - "no_apps": "No se han añadido aplicaciones", - "no_apps_hint": "Añade aplicaciones y enlaces personalizados a tu barra lateral", - "managed_title": "Proporcionadas por tu administrador", - "managed_description": "Estas aplicaciones están configuradas para todos y no se pueden cambiar.", - "managed_badge": "Gestionada", - "search_icons": "Buscar iconos...", - "show_popular": "Populares", - "show_all": "Todos", - "no_icons_found": "No se encontraron iconos", - "inline_badge": "Integrado", - "tab_badge": "Pestaña", - "show_on_mobile": "Mostrar en móvil" - }, - "email_list": { - "no_emails": "No se encontraron mensajes", - "no_emails_description": "Este buzón está vacío", - "no_search_results": "No se encontraron resultados", - "no_search_results_description": "Intente ajustar su búsqueda o filtros", - "loading": "Cargando correos...", - "unread": "no leído", - "to_me": "Para mí", - "to_recipients": "Para {count} destinatarios", - "and_others": "y {count} más", - "draft": "Borrador", - "starred": "Destacado", - "pinned": "Fijado", - "replied": "Respondido", - "forwarded": "Reenviado", - "has_attachment": "Tiene adjunto", - "conversations_count": "{count} de {total} conversaciones", - "conversations_count_plus": "{count}+ conversaciones", - "conversations_count_simple": "{count} conversaciones", - "no_conversations": "Sin conversaciones", - "loading_more": "Cargando más correos...", - "no_more_emails": "No hay más correos para cargar", - "batch_actions": { - "select": "Seleccionar correos", - "select_all": "Seleccionar todo", - "selected_messages": "{count, plural, one {1 correo electrónico seleccionado} other {# correos electrónicos seleccionados}}", - "mark_read": "Marcar como leído", - "mark_unread": "Marcar como no leído", - "delete": "Eliminar", - "delete_confirm_title": "Eliminar correos", - "delete_confirm_message": "¿Estás seguro de que quieres eliminar {count, plural, one {1 correo} other {# correos}}?", - "clear_selection": "Limpiar selección" - }, - "permanent_delete": "Eliminar permanentemente", - "permanent_delete_confirm_title": "Eliminar permanentemente", - "permanent_delete_confirm_message": "Este correo se eliminará permanentemente. Esta acción no se puede deshacer.", - "permanent_delete_confirm_batch_message": "Estos {count, plural, one {1 correo} other {# correos}} se eliminarán permanentemente. Esta acción no se puede deshacer.", - "empty_folder": { - "button": "Vaciar carpeta", - "confirm_title": "Vaciar carpeta", - "confirm_message": "Todos los correos de esta carpeta se eliminarán permanentemente. Esta acción no se puede deshacer.", - "confirm_button": "Vaciar carpeta", - "junk_hint": "Puede vaciar la carpeta de Spam para eliminar permanentemente todos los mensajes.", - "trash_hint": "Puede vaciar la Papelera para eliminar permanentemente todos los mensajes." - }, - "no_scheduled_emails": "No hay correos programados", - "no_scheduled_emails_description": "Los mensajes programados para más tarde aparecerán aquí.", - "scheduled_count": "{count, plural, one {1 correo programado} other {# correos programados}}", - "scheduled_actions_hint": "Los mensajes programados se pueden cancelar, reprogramar o editar desde sus acciones.", - "cancel_scheduled_send": "Cancelar envío", - "reschedule_send": "Reprogramar", - "cancel_and_edit": "Cancelar y editar", - "cancel_and_compose_again": "Cancelar y redactar de nuevo", - "reschedule_prompt": "Introduce una nueva fecha/hora, p. ej. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "El remitente solicita una confirmación de lectura:", - "send": "Enviar confirmación", - "ignore": "Ignorar", - "sent": "Confirmación de lectura enviada.", - "send_failed": "No se pudo enviar la confirmación de lectura", - "mdn_subject": "Leído: {subject}", - "mdn_body": "Este es un acuse de recibo del mensaje que enviaste a {recipient}.\n\nNota: Este acuse solo confirma que el mensaje se mostró en el ordenador del destinatario. No garantiza que el destinatario haya leído o entendido el contenido." - }, - "no_email_selected": "Ningún correo seleccionado", - "no_email_description": "Seleccione un correo de la lista para verlo aquí", - "no_conversation_selected": "Ninguna conversación seleccionada", - "no_conversation_description": "Elija una conversación de la lista para leerla aquí", - "compose": "Redactar", - "compose_hint": "Redactar nuevo mensaje", - "no_subject": "(Sin Asunto)", - "no_body_content": "(Sin contenido disponible)", - "no_preview_available": "Vista previa no disponible", - "show_quoted_text": "Mostrar texto citado", - "hide_quoted_text": "Ocultar texto citado", - "loading_email": "Cargando correo...", - "loading": "Cargando...", - "reply": "Responder", - "reply_all": "Responder a todos", - "forward": "Reenviar", - "delete": "Eliminar", - "archive": "Archivar", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Destacar", - "unstar": "Quitar destacado", - "mark_unread": "Marcar como no leído", - "mark_read": "Marcar como leído", - "unread": "No leído", - "read": "Leído", - "spam_short": "Spam", - "not_spam_short": "No spam", - "move": "Mover", - "print": "Imprimir", - "view_source": "Ver código fuente", - "fullscreen": "Pantalla completa", - "exit_fullscreen": "Salir de pantalla completa", - "export_email": "Exportar como .eml", - "forward_as_attachment": "Reenviar como archivo adjunto", - "import_email": "Importar .eml o .zip", - "keyboard_shortcuts": "Atajos de teclado (?)", - "email_source": "Código Fuente del Correo", - "draft_banner": "Este mensaje es un borrador", - "edit_draft": "Editar", - "copy_source": "Copiar al portapapeles", - "source_copied": "Código fuente copiado al portapapeles", - "attachments": "Archivos adjuntos", - "important": "Importante", - "download": "Descargar", - "download_all": "Descargar todo", - "from": "De", - "to": "Para", - "cc": "CC", - "bcc": "CCO", - "date": "Fecha", - "subject": "Asunto", - "show_details": "Mostrar detalles", - "hide_details": "Ocultar detalles", - "external_content_warning": "Las imágenes y el contenido externo han sido bloqueados", - "load_external_content": "Cargar imágenes", - "trust_sender": "Confiar siempre en este remitente", - "back_to_list": "Volver a la lista", - "view_contact": "Ver contacto", - "message_details": "Detalles del Mensaje", - "more_reply_options": "Más opciones de respuesta", - "set_tag": "Establecer etiqueta", - "tag": "Etiqueta", - "more_actions": "Más acciones", - "move_to": "Mover a...", - "remove_tag": "Eliminar etiqueta", - "tag_filter_placeholder": "Filtrar etiquetas", - "tag_no_matches": "No hay etiquetas coincidentes", - "more_count": "+{count} más", - "characters_count": "{count} caracteres", - "quick_reply_placeholder": "Escriba una respuesta rápida...", - "more_options": "Más opciones", - "sending": "Enviando...", - "security_authentication": "Seguridad y Autenticación", - "technical_details": "Detalles Técnicos", - "message_id_label": "ID del Mensaje:", - "reply_to_label": "Responder a:", - "delivery_time_label": "Hora de entrega:", - "conversation_part_label": "Parte de conversación:", - "previous_messages": "{count} mensaje anterior", - "previous_messages_plural": "{count} mensajes anteriores", - "time": { - "day": "día", - "days": "días", - "hour": "hora", - "hours": "horas", - "minute": "minuto", - "minutes": "minutos" - }, - "unknown_sender": "Desconocido", - "recipient_me": "yo", - "recipient_and_others": "{name} y {count} más", - "recipient_to_prefix": "Para:", - "authentication": { - "title": "Autenticación", - "status": { - "verified": "Verificado", - "warning": "Advertencia", - "none": "No autenticado" - }, - "spf": { - "pass": "SPF Aprobado", - "fail": "SPF Fallido", - "none": "Sin SPF" - }, - "dkim": { - "pass": "DKIM Válido", - "fail": "DKIM Inválido", - "none": "Sin DKIM" - }, - "dmarc": { - "pass": "DMARC Aprobado", - "fail": "DMARC Fallido", - "none": "Sin DMARC" - }, - "spam_score": "Puntuación de Spam", - "tooltip_spf": "Sender Policy Framework: verifica que el servidor de envío esté autorizado a enviar correo en nombre del dominio", - "tooltip_dkim": "DomainKeys Identified Mail: confirma mediante una firma criptográfica que el correo no se alteró durante el transporte", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: garantiza que SPF y DKIM se alineen con el dominio del remitente y establece una política para los fallos", - "policy": "Política", - "result": { - "pass": "Aprobado", - "fail": "Fallido", - "softfail": "Fallo leve", - "neutral": "Neutro", - "permerror": "Error permanente", - "temperror": "Error temporal", - "none": "Ninguno" - } - }, - "headers": { - "routing": "Enrutamiento", - "received": "Recibido", - "message_id": "ID del Mensaje", - "list_info": "Información de Lista" - }, - "tooltips": { - "reply": "Responder", - "reply_all": "Responder a todos (a)", - "forward": "Reenviar (f)", - "archive": "Archivar", - "delete": "Eliminar", - "star": "Destacar (s)", - "unstar": "Quitar estrella (s)", - "compose": "Redactar (c)", - "previous": "Correo anterior", - "next": "Correo siguiente", - "edit_draft": "Editar borrador" - }, - "spam": { - "button_title": "Reportar spam", - "not_spam_title": "Marcar como legítimo", - "toast_success": "Movido a Correo no deseado", - "toast_batch": "{count} correos movidos a Correo no deseado", - "toast_undo": "Deshacer", - "toast_not_spam_success": "Movido a Bandeja de entrada", - "toast_not_spam_batch": "{count} correos movidos a Bandeja de entrada", - "error": "No se pudo reportar spam", - "error_not_spam": "No se pudo restaurar el correo" - }, - "unsubscribe_banner": { - "label": "Boletín", - "button": "Cancelar suscripción", - "confirm_title": "¿Cancelar suscripción de este remitente?", - "confirm_button": "Confirmar", - "cancel": "Cancelar", - "success_http": "Página de cancelación de suscripción abierta en nueva pestaña", - "success_mailto": "Correo de cancelación enviado", - "confirm_message_http": "La página de cancelación se abrirá en una pestaña nueva.", - "confirm_message_mailto": "Se enviará un correo de cancelación al remitente.", - "error": "No se pudo cancelar la suscripción", - "dismiss": "Descartar" - }, - "calendar_invitation": { - "loading": "Cargando detalles del evento…", - "title": "Invitación de calendario", - "published_title": "Evento publicado", - "response_title": "Respuesta al evento", - "update_title": "Actualización del evento", - "counter_title": "Contrapropuesta", - "refresh_title": "Solicitud de actualización", - "declined_counter_title": "Contrapropuesta rechazada", - "cancelled_title": "Evento cancelado", - "organizer": "Organizado por {name}", - "organizer_label": "Organizado por", - "attendees": "{count, plural, one {# asistente} other {# asistentes}}", - "accept": "Aceptar", - "maybe": "Quizás", - "decline": "Rechazar", - "add_to_calendar": "Añadir al calendario", - "added": "Añadido al calendario", - "rsvp_sent": "Respuesta enviada", - "parse_error": "No se pudo leer la invitación", - "action_failed": "No se pudo completar esa acción del calendario.", - "no_calendar": "Calendario no disponible", - "published_info": "Este evento se compartió como referencia.", - "response_info": "Este mensaje contiene la respuesta de un asistente.", - "response_info_organizer": "Esta respuesta del asistente actualiza su evento.", - "update_info": "Este mensaje actualiza un evento existente.", - "counter_info": "Este mensaje propone cambios a un evento.", - "counter_info_organizer": "Este asistente propuso cambios a su evento.", - "refresh_info": "Este mensaje solicita los últimos detalles del evento.", - "refresh_info_organizer": "Un asistente solicitó los últimos detalles del evento.", - "declined_counter_info": "El organizador rechazó una contrapropuesta.", - "authentication_failed_info": "Las comprobaciones de autenticación de esta invitación fallaron. Trata las acciones de calendario con precaución.", - "authentication_missing_info": "Esta invitación no incluye autenticación de correo verificada. Confirma los detalles con el organizador si algo parece inusual.", - "sender_mismatch_info": "Esta invitación se envió desde {sender}, mientras que el organizador indicado en los datos del calendario es {organizer}.", - "sender_mismatch_unverified_info": "Esta invitación se envió desde {sender}, mientras que el organizador indicado en los datos del calendario es {organizer}, y el mensaje no pudo verificarse.", - "organizer_role": "Organizas este evento", - "your_response": "Tu respuesta: {status}", - "response_needed": "Pendiente de respuesta", - "response_accepted": "Aceptada", - "response_tentative": "Tentativa", - "response_declined": "Rechazada", - "response_delegated": "Delegada", - "actor_sent_info": "Enviado por {name}.", - "actor_response_info": "{name} respondió {status}.", - "actor_counter_info": "{name} propuso cambios para este evento.", - "actor_refresh_info": "{name} pidió los detalles más recientes del evento.", - "actor_declined_counter_info": "{name} rechazó la contrapropuesta.", - "actor_note": "Nota: {comment}", - "actor_unknown": "Alguien", - "proposed_changes": "Cambios propuestos", - "change_title": "Título", - "change_time": "Hora", - "change_location": "Ubicación", - "change_description": "Descripción", - "change_empty": "Ninguno", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Aplicar cambios propuestos", - "proposal_applied": "Se aplicaron los cambios propuestos.", - "review_proposal": "Revisar propuesta", - "review_request": "Revisar solicitud", - "view_in_calendar": "Ver en el calendario", - "select_calendar": "Seleccionar calendario", - "already_in_calendar": "Ya está en tu calendario", - "request_info": "Has sido invitado a este evento. Responde para informar al organizador de tu disponibilidad.", - "cancel_info": "El organizador ha cancelado este evento.", - "event_updated": "Actualización #{sequence}", - "event_status_tentative": "Provisional", - "event_status_cancelled": "Cancelado", - "collapse": "Ocultar detalles", - "expand": "Mostrar detalles" - }, - "previous": "Anterior", - "next": "Siguiente", - "send": "Enviar", - "more": "más", - "details": { - "recipients_routing": "Destinatarios y enrutamiento", - "authentication_security": "Autenticación y seguridad", - "identifiers_threading": "Identificadores y conversación", - "mailing_list": "Lista de correo", - "message_properties": "Propiedades del mensaje", - "sent": "Enviado", - "received": "Recibido", - "delivery_time": "Tiempo de entrega", - "in_reply_to": "En respuesta a", - "references": "Referencias", - "thread_id": "ID de conversación", - "size": "Tamaño", - "mime_type": "Tipo MIME", - "attachments_summary": "{count} archivos · {size}", - "list_id": "ID de lista", - "list_help": "Ayuda de lista", - "list_post": "Publicación en lista", - "list_unsubscribe": "Darse de baja", - "iprev": "DNS inverso", - "spam_status": "Estado de spam", - "ai_verdict": "Veredicto de IA", - "account": "Cuenta", - "no_subject": "(sin asunto)" - }, - "scheduled_banner": "Programado para enviarse el {date}", - "scheduled_send_created": "Correo programado para envío", - "cancel_scheduled_send": "Cancelar envío", - "reschedule_send": "Reprogramar", - "cancel_and_edit": "Cancelar y editar", - "cancel_and_compose_again": "Cancelar y redactar de nuevo", - "reschedule_prompt": "Introduce una nueva fecha/hora, p. ej. 2026-05-04T15:30", - "scheduled_actions_only": "La vista Programados solo admite acciones de programación", - "undo_send_scheduled": "Mensaje programado para envío", - "undo_send": "Deshacer envío", - "contact_sidebar": { - "title": "Contacto", - "close": "Cerrar panel", - "action_email": "Correo", - "action_email_title": "Enviar correo", - "action_copy": "Copiar", - "action_copy_title": "Copiar correo electrónico", - "action_edit_title": "Editar contacto", - "section_emails": "Correos electrónicos", - "section_phones": "Teléfonos", - "section_organizations": "Organizaciones", - "section_addresses": "Direcciones", - "section_notes": "Notas", - "not_in_contacts": "No está en sus contactos", - "add_to_contacts": "Añadir a contactos", - "copied": "¡Copiado!", - "copy_failed": "Error al copiar" - }, - "send_now": "Enviar ahora" - }, - "email_composer": { - "read_receipt_on": "Confirmación de lectura solicitada (haz clic para desactivar)", - "read_receipt_off": "Solicitar confirmación de lectura", - "dsn_on": "Notificación de entrega solicitada (clic para desactivar)", - "dsn_off": "Solicitar notificación de entrega", - "format_plain_text": "Cambiar a texto sin formato", - "format_rich_text": "Cambiar a texto enriquecido (HTML)", - "require_tls_on": "Entrega cifrada obligatoria (clic para desactivar)", - "require_tls_off": "Exigir entrega cifrada (TLS)", - "new_message": "Nuevo Mensaje", - "signature_edit_hint": "Haz doble clic para editar la firma", - "reply": "Responder", - "reply_all": "Responder a Todos", - "forward": "Reenviar", - "reply_to": "Responder", - "reply_all_to": "Responder a Todos", - "forward_message": "Reenviar", - "from": "De", - "to": "Para", - "cc": "CC", - "bcc": "CCO", - "subject": "Asunto", - "body_placeholder": "Escriba su mensaje...", - "send": "Enviar", - "cancel": "Cancelar", - "attach": "Adjuntar", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Descartar", - "discard_draft_title": "¿Descartar borrador?", - "discard_draft_confirm": "Tiene cambios sin guardar. ¿Desea descartar este borrador?", - "saving": "Guardando...", - "draft_saved": "Borrador guardado", - "save_failed": "Error al guardar", - "to_placeholder": "Direcciones de correo de destinatarios", - "cc_placeholder": "Destinatarios CC", - "bcc_placeholder": "Destinatarios CCO", - "subject_placeholder": "Asunto", - "cc_label": "CC:", - "bcc_label": "CCO:", - "subject_label": "Asunto:", - "file_size_kb": "KB", - "prefix": { - "forward": "Rvf:", - "reply": "Re:" - }, - "no_subject": "(Sin Asunto)", - "unknown_sender": "Desconocido", - "quote": { - "reply_header": "El {date}, {sender} escribió:", - "forward_header": "---------- Mensaje reenviado ----------", - "from": "De: {sender}", - "date": "Fecha: {date}", - "subject": "Asunto: {subject}", - "to": "Para: {recipients}" - }, - "remove_sub_address": "Eliminar sub-dirección", - "from_override": { - "toggle_off": "Anular", - "toggle_on": "Cancelar anulación", - "toggle_tooltip": "Edita libremente el nombre y la dirección del remitente. El correo aún se envía a través de tu identidad.", - "name_label": "Nombre del remitente", - "name_placeholder": "Nombre", - "email_label": "Dirección de correo del remitente", - "email_placeholder": "alias@example.com", - "envelope_notice": "Si tu servidor no acepta esta dirección como remitente del sobre, se usa {identity} en su lugar, y los destinatarios pueden verla en el encabezado Return-Path." - }, - "use_template": "Plantilla", - "save_as_template": "Guardar como plantilla", - "validation": { - "recipient_required": "Agregue un destinatario para enviar", - "body_required": "Escriba un mensaje o adjunte un archivo", - "attachments_uploading": "Los archivos adjuntos aún se están subiendo; se enviará en cuanto terminen", - "attachment_upload_failed": "No enviado: no se pudo subir un adjunto. Elimínalo e inténtalo de nuevo." - }, - "upload_progress": "Subiendo {uploaded} / {total}", - "upload_cancel": "Cancelar subida", - "upload_failed": "Error al subir {filename}", - "attachment_too_large": "\"{name}\" supera el tamaño que permite el servidor ({max} por archivo)", - "attachments_total_too_large": "Los adjuntos superarían el límite del servidor de {max} por mensaje", - "send_failed": "Error al enviar el correo", - "smime_sign_on": "Firma S/MIME activada", - "smime_sign_off": "Firma S/MIME desactivada", - "smime_encrypt_on": "Cifrado S/MIME activado", - "smime_encrypt_off": "Cifrado S/MIME desactivado", - "smime_encrypt_unavailable": "Cifrado S/MIME no disponible – faltan certificados de destinatario", - "smime_unlock_title": "Desbloquear clave S/MIME", - "smime_unlock_message": "Introduce la contraseña para desbloquear tu clave S/MIME.", - "smime_unlock_button": "Desbloquear", - "smime_passphrase_placeholder": "Contraseña S/MIME", - "send_timeout": "El servidor dejó de responder. Compruebe la carpeta Enviados antes de volver a enviar: el mensaje puede haber salido.", - "continue_draft": "Continuar borrador", - "close_draft_title": "¿Guardar o descartar borrador?", - "close_draft_message": "Tiene cambios sin guardar. ¿Desea guardar esto como borrador o descartarlo?", - "drop_files": "Suelta archivos para adjuntar", - "show_less": "Mostrar menos", - "forgot_attachment": { - "title": "¿Olvidaste un archivo adjunto?", - "message": "Tu mensaje menciona \"{keyword}\" pero no hay ningún archivo adjunto. ¿Enviar de todos modos?", - "send_anyway": "Enviar de todos modos", - "back": "Volver a editar" - }, - "empty_subject": { - "title": "¿Enviar sin asunto?", - "message": "Este mensaje no tiene asunto. ¿Enviarlo de todos modos?", - "dont_ask_again": "No volver a preguntar", - "send_anyway": "Enviar de todos modos", - "back": "Volver a editar" - }, - "add_link": "Añadir enlace", - "link_url_prompt": "Introduce la URL", - "sending": "Enviando...", - "schedule_send": "Programar envío", - "schedule_send_description": "Elige cuándo debe liberar el servidor este mensaje.", - "schedule_send_required": "Elige una fecha y hora.", - "schedule_send_invalid": "Introduce una fecha y hora válidas.", - "schedule_send_future": "Elige una fecha y hora futuras.", - "schedule_send_too_late": "Esta hora es posterior a lo que permite el servidor.", - "schedule_send_unsupported": "El envío programado no es compatible con esta cuenta.", - "schedule_send_cleanup_warning": "Se creó el envío programado, pero falló la limpieza del borrador.", - "send_delay_unsupported": "La demora de envío no es compatible con esta cuenta.", - "send_delay_unsupported_confirm": "Esta cuenta no admite demora de envío. ¿Enviar inmediatamente?", - "recipient_edit_email": "Editar dirección de correo", - "recipient_edit_name": "Editar nombre para mostrar", - "recipient_email_placeholder": "Dirección de correo", - "recipient_name_placeholder": "Nombre para mostrar", - "autocomplete_search_server": "Buscar en el servidor", - "autocomplete_searching": "Buscando...", - "toolbar": { - "bold": "Negrita", - "italic": "Cursiva", - "underline": "Subrayado", - "strikethrough": "Tachado", - "text_color": "Color del texto", - "remove_color": "Quitar color", - "heading_1": "Encabezado 1", - "heading_2": "Encabezado 2", - "bullet_list": "Lista con viñetas", - "ordered_list": "Lista numerada", - "quote": "Cita", - "code_block": "Bloque de código", - "align_left": "Alinear a la izquierda", - "align_center": "Centrar", - "align_right": "Alinear a la derecha", - "text_direction": "Dirección del texto (RTL/LTR)", - "link": "Enlace", - "table": "Tabla", - "clear_formatting": "Borrar formato", - "undo": "Deshacer", - "redo": "Rehacer", - "add_row_above": "Añadir fila encima", - "add_row_below": "Añadir fila debajo", - "add_column_before": "Añadir columna antes", - "add_column_after": "Añadir columna después", - "delete_row": "Eliminar fila", - "delete_column": "Eliminar columna", - "toggle_header_row": "Alternar fila de encabezado", - "delete_table": "Eliminar tabla", - "pick_size": "Elegir tamaño", - "font_size": "Tamaño de fuente", - "font_size_default": "Predeterminado", - "background_color": "Color de fondo", - "remove_background_color": "Quitar color de fondo" - }, - "send_filing_warning": "Enviado - pero la limpieza posterior falló, puede quedar un borrador obsoleto." - }, - "confirm_dialog": { - "confirm": "Confirmar", - "cancel": "Cancelar" - }, - "common": { - "loading": "Cargando...", - "error": "Error", - "success": "Éxito", - "cancel": "Cancelar", - "save": "Guardar", - "delete": "Eliminar", - "edit": "Editar", - "close": "Cerrar", - "search": "Buscar", - "refresh": "Actualizar", - "settings": "Configuración", - "help": "Ayuda", - "logout": "Cerrar sesión", - "yes": "Sí", - "no": "No", - "unknown": "Desconocido", - "app_title": "Correo Web", - "reconnecting": "Conexión perdida. Intentando reconectar…", - "rate_limited_title": "La autenticación del servidor está limitada temporalmente.", - "rate_limited_detail": "Bulwark ha pausado las solicitudes en segundo plano para evitar un bloqueo. Reintentando en {seconds}s.", - "rate_limited_action_title": "Solicitud pausada para evitar el bloqueo.", - "rate_limited_action_detail": "Bulwark está esperando a que termine el enfriamiento del servidor antes de enviar más solicitudes autenticadas. Inténtalo de nuevo en {seconds}s.", - "pull_to_refresh": "Desliza para actualizar", - "release_to_refresh": "Suelta para actualizar", - "refreshing": "Actualizando…" - }, - "notifications": { - "email_sent": "Correo enviado exitosamente", - "email_deleted": "Correo eliminado", - "email_archived": "Correo archivado", - "emails_deleted": "{count, plural, one {# correo eliminado} other {# correos eliminados}}", - "emails_archived": "{count, plural, one {# correo archivado} other {# correos archivados}}", - "error_archiving": "No se pudo archivar el correo", - "error_updating": "No se pudo actualizar el correo", - "email_starred": "Correo destacado", - "email_unstarred": "Correo sin destacar", - "email_marked_read": "Correo marcado como leído", - "email_marked_unread": "Correo marcado como no leído", - "copied_to_clipboard": "Copiado al portapapeles", - "source_copied": "Código fuente copiado al portapapeles", - "error_sending": "Error al enviar el correo", - "error_deleting": "Error al eliminar el correo", - "error_loading": "Error al cargar los correos", - "new_email": "Nuevo correo", - "new_email_from": "De {sender}", - "click_to_view": "Haga clic para ver", - "email_moved": "Correo movido", - "emails_moved": "{count} correos movidos", - "moved_to_mailbox": "Movido a {mailbox}", - "move_failed": "Error al mover", - "move_error": "No se pudieron mover los correos a la carpeta seleccionada", - "email_tagged": "Correo etiquetado", - "emails_tagged": "{count} correos etiquetados", - "tag_failed": "Error al etiquetar", - "identity_created": "Identidad creada exitosamente", - "identity_updated": "Identidad actualizada exitosamente", - "identity_deleted": "Identidad eliminada", - "identity_set_primary": "Identidad principal actualizada", - "identity_create_failed": "Error al crear identidad: {error}", - "identity_update_failed": "Error al actualizar identidad: {error}", - "identity_delete_failed": "Error al eliminar identidad: {error}", - "identity_unauthorized": "No está autorizado para enviar desde esta dirección de correo", - "identity_not_found": "Identidad no encontrada", - "vacation_saved": "Configuración de respuesta automática guardada", - "vacation_save_failed": "Error al guardar la configuración de respuesta automática", - "filters_saved": "Filtros guardados correctamente", - "filters_save_failed": "Error al guardar los filtros", - "filters_deleted": "Regla de filtrado eliminada", - "templates_exported": "Plantillas exportadas correctamente", - "templates_imported": "{count, plural, one {# plantilla importada} other {# plantillas importadas}}", - "templates_import_errors": "Algunas plantillas no se pudieron importar", - "templates_import_empty": "No se encontraron plantillas en el archivo", - "export_email_error": "Error al exportar el correo electrónico", - "import_email_success": "Correo electrónico importado correctamente", - "import_email_error": "Error al importar el correo electrónico" - }, - "date": { - "today": "Hoy", - "yesterday": "Ayer", - "this_week": "Esta semana", - "last_week": "Semana pasada", - "this_month": "Este mes", - "older": "Más antiguo", - "just_now": "Justo ahora", - "minutes_ago": "hace {count} minuto", - "minutes_ago_plural": "hace {count} minutos", - "hours_ago": "hace {count} hora", - "hours_ago_plural": "hace {count} horas", - "days_ago": "hace {count} día", - "days_ago_plural": "hace {count} días" - }, - "language": { - "title": "Idioma", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Seleccionar idioma", - "switch_to_english": "Cambiar a inglés", - "switch_to_french": "Cambiar a francés", - "switch_to_japanese": "Cambiar a japonés", - "switch_to_spanish": "Cambiar a español", - "switch_to_italian": "Cambiar a italiano", - "switch_to_german": "Cambiar a alemán", - "switch_to_dutch": "Cambiar a neerlandés", - "switch_to_portuguese": "Cambiar a portugués", - "switch_to_russian": "Cambiar a ruso", - "switching": "Cambiando idioma...", - "switch": "Cambiar idioma", - "current": "Idioma actual", - "polish": "Polski", - "switch_to_polish": "Cambiar a polaco", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Cambiar a farsi", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Cambiar a mongol" - }, - "settings": { - "title": "Configuración", - "back_to_mail": "Volver al Correo", - "save_success": "Configuración guardada exitosamente", - "import_success": "Configuración importada exitosamente", - "import_error": "Error al importar la configuración", - "reset_confirm": "¿Está seguro de que desea restablecer toda la configuración a los valores predeterminados?", - "unsaved_changes": "Tienes cambios sin guardar", - "discard_changes": "¿Descartar los cambios?", - "discard": "Descartar", - "keep_editing": "Seguir editando", - "tabs": { - "appearance": "Apariencia", - "language": "Idioma, Región y Hora", - "email": "Comportamiento del Correo", - "composer": "Editor", - "privacy": "Privacidad y Seguridad", - "account": "Cuenta", - "identities": "Identidades", - "vacation": "Respuesta automática", - "advanced": "Avanzado", - "calendar": "Calendario", - "filters": "Filtros", - "templates": "Plantillas", - "folders": "Carpetas", - "keywords": "Etiquetas", - "security": "Seguridad", - "encryption": "Cifrado", - "files": "Archivos", - "contacts": "Contactos", - "protocol_handlers": "Aplicaciones predeterminadas", - "sidebar_apps": "Apps de barra lateral", - "notifications": "Notificaciones", - "layout": "Diseño", - "reading": "Lectura", - "composing": "Redacción", - "downloads": "Descargas", - "content_senders": "Contenido y remitentes", - "about_data": "Acerca de y datos", - "debug": "Depuración" - }, - "tab_groups": { - "general": "General", - "account": "Cuenta e identidad", - "organization": "Organización del correo", - "apps": "Aplicaciones", - "system": "Sistema", - "appearance": "Apariencia", - "mail": "Correo", - "privacy": "Privacidad y seguridad", - "advanced": "Avanzado" - }, - "appearance": { - "message_list_order": { - "label": "Orden de la lista de mensajes", - "description": "Cómo se ordenan los mensajes en la lista. El servidor realiza la ordenación, por lo que abarca toda la carpeta, no solo los mensajes cargados.", - "preset": { - "chronological": "Cronológico (más recientes primero)", - "unread_first": "No leídos primero", - "starred_first": "Destacados primero", - "tagged_first": "Etiquetados primero", - "custom": "Personalizado" - }, - "tag_label": "Etiqueta", - "advanced_show": "Avanzado: editar niveles de orden", - "advanced_hide": "Ocultar avanzado", - "criterion_label": "Criterio del nivel {n}", - "direction_label": "Dirección del nivel {n}", - "criteria": { - "unread": "No leído", - "starred": "Destacado", - "tag": "Etiqueta", - "receivedAt": "Fecha de recepción", - "sentAt": "Fecha de envío", - "from": "Remitente", - "subject": "Asunto", - "size": "Tamaño" - }, - "direction": { - "unread_first": "No leídos primero", - "read_first": "Leídos primero", - "starred_first": "Destacados primero", - "unstarred_first": "No destacados primero", - "tagged_first": "Etiquetados primero", - "untagged_first": "Sin etiqueta primero", - "newest_first": "Más recientes primero", - "oldest_first": "Más antiguos primero", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Más grandes primero", - "smallest_first": "Más pequeños primero" - }, - "add_level": "Añadir nivel", - "remove_level": "Eliminar nivel", - "move_up": "Subir", - "move_down": "Bajar", - "tiebreak_note": "Hasta tres niveles; los niveles posteriores deshacen empates. «Más recientes primero» se aplica siempre al final.", - "scope": { - "label": "Aplicar a todas las carpetas", - "description": "Si está desactivado, el orden solo se aplica a la bandeja de entrada; las demás carpetas siguen siendo cronológicas." - }, - "unsupported_note": "Tu servidor de correo no admite ordenar por estado de lectura, destacados o etiquetas. Esos niveles se ignoran y la lista sigue siendo cronológica." - }, - "title": "Apariencia", - "description": "Personalice la apariencia de su correo web", - "theme": { - "label": "Tema", - "description": "Elija su esquema de color preferido", - "light": "Claro", - "dark": "Oscuro", - "system": "Sistema" - }, - "language": { - "label": "Idioma", - "description": "Elija su idioma preferido" - }, - "font_size": { - "label": "Tamaño de Fuente", - "description": "Ajuste el tamaño del texto para mejor legibilidad", - "small": "Pequeño", - "medium": "Mediano", - "large": "Grande" - }, - "list_density": { - "label": "Densidad", - "description": "Controle el espaciado en toda la interfaz", - "extra_compact": "Extra compacto", - "compact": "Compacto", - "regular": "Normal", - "comfortable": "Cómodo" - }, - "animations": { - "label": "Habilitar Animaciones", - "description": "Mostrar transiciones y efectos suaves" - }, - "toolbar_position": { - "label": "Posición de la barra de herramientas", - "description": "Dónde mostrar los botones de acción del correo (Responder, Archivar, Eliminar, etc.)", - "top": "Arriba", - "below_subject": "Debajo del asunto" - }, - "toolbar_labels": { - "label": "Mostrar etiquetas de la barra de herramientas", - "description": "Mostrar etiquetas de texto junto a los iconos de la barra de herramientas. Desactivar para ahorrar espacio una vez que esté familiarizado con los iconos." - }, - "hide_account_switcher": { - "label": "Ocultar selector de cuentas de la barra lateral", - "description": "Ocultar el selector de cuentas en la parte superior de la barra lateral de carpetas. Aún puede cambiar de cuenta desde la barra de navegación inferior." - }, - "show_rail_account_list": { - "label": "Mostrar avatares de cuentas en la barra de navegación", - "description": "Mostrar círculos de cuentas individuales en la parte inferior de la barra de navegación para un cambio rápido, con un botón de cerrar sesión debajo." - }, - "unified_mailbox": { - "label": "Buzón unificado", - "description": "Mostrar carpetas combinadas (Entrada, Enviados, etc.) de todas las cuentas conectadas", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Incluir buzones de grupo", - "description": "Incluir también los buzones compartidos o de grupo en la vista unificada." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Iconos de barra lateral a color", - "description": "Colorea los iconos de carpetas y etiquetas según su tipo (azul para Bandeja de entrada, rojo para Spam, verde para Enviados, etc.). Desactívalo para una barra lateral monocroma." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Mostrar el número total de mensajes", - "description": "Muestra el número total de mensajes junto a las carpetas y etiquetas, además del número de mensajes no leídos. Desactívalo para mostrar solo los no leídos." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Interfaz Pro (experimental)", - "description": "Diseño de escritorio para usuarios avanzados con exploración de mensajes en varias pestañas y flujos de trabajo entre cuentas. La interfaz estándar no se ve afectada; puedes volver en cualquier momento.", - "open_label": "Abrir interfaz Pro", - "back_to_standard": "Volver al estándar" - }, - "cross_unread": { - "label": "Todas las cuentas: No leído", - "description": "Muestra una entrada en la sección Todas las cuentas con el correo no leído de todas las cuentas, abarcando todas las carpetas excepto spam, enviados, archivo, papelera y borradores." - }, - "cross_starred": { - "label": "Todas las cuentas: Destacado", - "description": "Muestra una entrada en la sección Todas las cuentas con el correo destacado de todas las cuentas, abarcando todas las carpetas excepto spam, enviados, archivo, papelera y borradores." - }, - "cross_all": { - "label": "Todas las cuentas: Todo el correo", - "description": "Muestra una entrada en la sección Todas las cuentas con todo el correo de todas las cuentas, abarcando todas las carpetas excepto spam, enviados, archivo, papelera y borradores." - } - }, - "keywords": { - "title": "Etiquetas de correo", - "description": "Define etiquetas para organizar tus correos con colores. Se almacenan como palabras clave JMAP en el servidor.", - "add_keyword": "Añadir etiqueta", - "label_field": "Nombre para mostrar", - "label_placeholder": "ej. Trabajo, Personal, Urgente", - "id_field": "ID de etiqueta", - "id_placeholder": "ej. trabajo, personal", - "color_field": "Color", - "id_exists": "Este ID de etiqueta ya existe", - "edit": "Editar etiqueta", - "delete": "Eliminar etiqueta", - "save": "Guardar", - "add": "Añadir", - "cancel": "Cancelar", - "migrating": "Actualizando etiqueta en correos existentes…", - "migration_error": "Error al actualizar la etiqueta en correos existentes", - "nesting": { - "label": "Etiquetas anidadas", - "description": "Anida etiquetas debajo de otras etiquetas y muéstralas como un árbol en la barra lateral." - }, - "parent_field": "Etiqueta principal", - "no_parent": "Sin etiqueta principal", - "too_long": "Esta ruta de etiqueta es demasiado larga (máximo {max} caracteres)", - "has_children_locked": "Hay otras etiquetas anidadas bajo esta, por lo que su nombre y su etiqueta principal están bloqueados. Muévelas o elimínalas primero.", - "has_children_delete": "Elimina primero las etiquetas anidadas bajo esta", - "visibility_field": "Visibilidad en la barra lateral", - "visibility": { - "show": "Mostrar", - "unread": "Mostrar si hay no leídos", - "hide": "Ocultar" - }, - "unrecognized": { - "title": "Recuperar etiquetas del servidor", - "description": "Los nombres y colores de las etiquetas se guardan en este navegador, pero las etiquetas en sí están en tus correos como palabras clave JMAP. Analiza tu correo en busca de palabras clave que ninguna etiqueta de aquí explique y vuelve a añadirlas.", - "scan": "Buscar etiquetas", - "rescan": "Analizar de nuevo", - "scanning": "Analizando… {scanned} de {total} correos", - "none": "Todas las etiquetas de tus correos ya están definidas aquí.", - "messages": "{count, plural, one {# correo} other {# correos}}", - "add_all": "Añadir las {count}", - "partial": "Solo se analizaron los {scanned} correos más recientes de {total}. Puede que falten etiquetas usadas únicamente en correos más antiguos.", - "error": "No se pudo analizar tu correo en busca de etiquetas." - } - }, - "notifications": { - "test_sound": "Probar sonido de notificación", - "sounds": { - "default": "Predeterminado (Pitido)", - "cheerful": "Alegre", - "involved": "Elaborado", - "swift": "Gesto rápido", - "relax": "Relajado" - }, - "sound_selection": { - "title": "Sonido de notificación", - "description": "Elige qué sonido reproducir para las notificaciones", - "choose": "Sonido", - "choose_desc": "Selecciona un tono de notificación y haz clic en el icono del altavoz para previsualizarlo" - }, - "email": { - "title": "Notificaciones de correo", - "description": "Configurar notificaciones para correos entrantes", - "enabled": "Notificaciones de correo", - "enabled_desc": "Mostrar notificaciones cuando lleguen nuevos correos", - "sound": "Sonido de notificación", - "sound_desc": "Reproducir un sonido cuando lleguen nuevos correos", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Notificaciones de calendario", - "description": "Configurar notificaciones para eventos del calendario", - "enabled": "Notificaciones de eventos", - "enabled_desc": "Mostrar alertas para próximos eventos del calendario", - "sound": "Sonido de notificación", - "sound_desc": "Reproducir un sonido para recordatorios del calendario", - "invitation_parsing": "Analizar invitaciones por correo", - "invitation_parsing_desc": "Detectar invitaciones de calendario en archivos adjuntos y mostrar acciones de calendario" - }, - "push": { - "confirm_disable_message": "Este dispositivo dejará de recibir alertas cuando el sitio esté cerrado.", - "confirm_disable_title": "¿Desactivar las notificaciones en segundo plano?", - "description": "Recibe notificaciones del sistema para correo nuevo cuando este sitio está cerrado. Se entrega a través del relay push de Bulwark; el relay nunca ve el contenido del correo.", - "disable": "Desactivar", - "enable": "Activar", - "dismiss_aria": "Cerrar aviso de notificaciones", - "ios_hint": "En iOS, instala primero el sitio en la pantalla de inicio: Safari solo entrega Web Push a PWAs instaladas.", - "reenable": "Volver a registrar", - "relay_desc": "Usa el relay alojado de Bulwark de forma predeterminada. Cámbialo solo si te alojas tú mismo.", - "relay_label": "Relay push", - "relay_locked": "Establecido por el administrador", - "relay_locked_desc": "La URL del relay push ha sido establecida por tu administrador y no se puede cambiar.", - "status_active": "Activo en este dispositivo", - "status_busy": "Trabajando…", - "status_inactive": "No habilitado en este dispositivo", - "status_unsupported": "Este navegador no admite Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Notificaciones en segundo plano", - "devices_title": "Dispositivos registrados", - "devices_desc": "Todos los dispositivos registrados para recibir notificaciones en segundo plano de esta cuenta. Al revocar uno, deja de recibirlas de inmediato.", - "devices_loading": "Cargando dispositivos registrados…", - "devices_empty": "No hay dispositivos registrados para notificaciones en segundo plano.", - "devices_error": "No se pudieron cargar los dispositivos registrados.", - "devices_refresh": "Actualizar", - "device_this": "Este dispositivo", - "device_other": "Otro dispositivo", - "device_status_active": "Entregando notificaciones", - "device_status_inactive": "Nunca ha entregado una notificación", - "device_status_unknown": "Desconocido para este relé", - "device_expires": "Caduca el {date}", - "revoke": "Revocar", - "confirm_revoke_title": "¿Revocar este dispositivo?", - "confirm_revoke_message": "Dejará de recibir notificaciones en segundo plano de inmediato. Si vuelves a activar las notificaciones push en ese dispositivo, se registrará de nuevo.", - "confirm_revoke_message_this": "Este dispositivo dejará de recibir notificaciones en segundo plano de inmediato." - } - }, - "language_region": { - "title": "Idioma, Región y Hora", - "description": "Idioma, formato de fecha, formato de hora y otras preferencias regionales", - "language": { - "label": "Idioma", - "description": "Elija su idioma preferido", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Formato de Fecha", - "description": "Cómo se muestran las fechas en la lista de correos", - "smart": "Inteligente (según región)", - "relative": "Relativo (hace 1 h, hace 2 d)", - "full": "Fecha completa siempre", - "preview_today": "Hoy:", - "preview_this_week": "Esta semana:", - "preview_older": "Más antiguo:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Formato de Hora", - "description": "Elija entre reloj de 12 o 24 horas", - "12h": "12 horas", - "24h": "24 horas" - }, - "first_day": { - "label": "Primer Día de la Semana", - "description": "Iniciar la semana en domingo o lunes", - "sunday": "Domingo", - "monday": "Lunes" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Borrar la búsqueda al cambiar de carpeta", - "description": "Al hacer clic en una carpeta (Bandeja de entrada, Enviados…) se borra la búsqueda activa y se muestra esa carpeta. Si está desactivado, la búsqueda se mantiene y se vuelve a ejecutar en la carpeta a la que cambies." - }, - "request_read_receipt": { - "label": "Solicitar confirmaciones de lectura de forma predeterminada", - "description": "Activar previamente la solicitud de confirmación de lectura al redactar un mensaje nuevo." - }, - "read_receipt_response": { - "label": "Responder a las solicitudes de confirmación de lectura", - "description": "Qué hacer cuando un mensaje entrante solicita una confirmación de lectura.", - "ask": "Preguntar cada vez", - "always": "Enviar siempre", - "never": "No enviar nunca" - }, - "title": "Comportamiento del Correo", - "description": "Configure cómo se manejan los correos", - "mark_read": { - "label": "Marcar como Leído", - "description": "Cuándo marcar correos como leídos al abrirlos", - "instant": "Instantáneamente", - "delay_3s": "Después de 3 segundos", - "delay_5s": "Después de 5 segundos", - "never": "Nunca" - }, - "delete_action": { - "label": "Acción al Eliminar", - "description": "Qué sucede cuando elimina un correo", - "trash": "Mover a Papelera", - "trash_and_read": "Mover a Papelera y marcar como leído", - "permanent": "Eliminar Permanentemente", - "warning": "Los correos se eliminarán permanentemente y no se podrán recuperar. Esta acción es irreversible." - }, - "message_spacing": { - "label": "Espaciado del mensaje", - "description": "Margen alrededor del cuerpo del mensaje en el lector", - "auto": "Automático (recomendado)", - "always": "Añadir espaciado siempre", - "edge": "De borde a borde" - }, - "plain_text_font": { - "label": "Fuente de texto sin formato", - "description": "Fuente utilizada para los correos de texto sin formato", - "mono": "Monoespaciada", - "sans": "Normal (igual que la aplicación)" - }, - "archive_mode": { - "label": "Archivar en", - "description": "Cómo organizar los correos al archivar", - "single": "Una sola carpeta", - "year": "Una carpeta por año", - "month": "Una carpeta por mes", - "reorganize": "Reorganizar archivo existente", - "reorganize_success": "{count, plural, =0 {No hay correos para reorganizar} =1 {1 correo reorganizado} other {# correos reorganizados}}", - "reorganize_error": "Error al reorganizar el archivo" - }, - "permanently_delete_junk": { - "label": "Eliminar spam permanentemente", - "description": "Eliminar permanentemente los correos de la carpeta Spam en lugar de moverlos a la Papelera" - }, - "mail_layout": { - "label": "Diseño del correo", - "description": "Elija entre el panel de lectura dividido clásico y un flujo de lectura enfocado estilo Gmail.", - "split": "Panel dividido", - "split_description": "Mantener la lista de mensajes y el panel de lectura visibles uno al lado del otro.", - "focus": "Lista enfocada", - "focus_description": "Mostrar una línea por mensaje y abrir el correo a ancho completo manteniendo visible la barra lateral de carpetas.", - "horizontal": "Panel de lectura abajo", - "horizontal_description": "Muestra la lista de mensajes arriba y abre el mensaje seleccionado en un panel de lectura debajo." - }, - "disable_threading": { - "label": "Desactivar agrupación de conversaciones", - "description": "Mostrar correos como mensajes individuales en lugar de agruparlos por conversación" - }, - "plain_text_mode": { - "label": "Solo texto sin formato", - "description": "Desactivar el editor de texto enriquecido y enviar todos los correos solo como texto sin formato, incluyendo respuestas y reenvíos" - }, - "auto_select_reply_identity": { - "label": "Seleccionar dirección de respuesta automáticamente", - "description": "Al responder, cambia automáticamente la dirección del remitente a la identidad que recibió el mensaje original" - }, - "reply_identity_match": { - "label": "Coincidencia de la dirección de recepción", - "description": "Qué direcciones de recepción cuentan como tuyas. «Solo la dirección exacta» elige únicamente una de tus identidades configuradas; «mismo dominio» también trata cualquier otra dirección de tus dominios de identidad como un alias catch-all y reescribe la cabecera De con ella. Elige la dirección exacta si esas direcciones son listas de distribución.", - "exact": "Solo la dirección exacta", - "domain": "Cualquier dirección de mis dominios" - }, - "signature_position": { - "label": "Posición de la firma", - "description": "Dónde insertar tu firma en respuestas y reenvíos. Encima del texto citado se lee de forma natural como cierre de la respuesta; debajo mantiene el mensaje original contiguo.", - "above_quote": "Antes del texto citado", - "below_quote": "Después del texto citado" - }, - "signature_separator": { - "label": "Delimitador de firma", - "description": "Anteponer a la firma la línea delimitadora estándar \"-- \" (RFC 3676). Desactiva si prefieres pasar directamente del mensaje a la firma." - }, - "sub_address_delimiter": { - "label": "Delimitador de sub-dirección", - "description": "Carácter que separa tu nombre de usuario de la etiqueta de sub-dirección. Usa el delimitador que utilice tu servidor de correo (por ejemplo, usuario{delimiter}etiqueta@dominio.com).", - "option": "{delimiter} (usuario{delimiter}etiqueta@dominio.com)", - "custom": "Personalizado…", - "custom_input_label": "Carácter delimitador personalizado" - }, - "show_preview": { - "label": "Mostrar Vista Previa", - "description": "Mostrar vista previa del correo en la lista", - "focus_description": "Mostrar texto de vista previa en línea dentro de la lista de mensajes enfocada de una línea" - }, - "attachment_click_action": { - "label": "Acción al hacer clic en adjuntos", - "description": "Elige si al hacer clic en un archivo adjunto se abre una vista previa o se descarga de inmediato", - "preview": "Mostrar vista previa cuando sea posible", - "download": "Descargar inmediatamente" - }, - "attachment_position": { - "label": "Posición del adjunto", - "description": "Dónde mostrar los adjuntos en el encabezado del correo", - "beside-sender": "Junto al remitente", - "below-header": "Debajo del encabezado" - }, - "emails_per_page": { - "10": "10 correos", - "25": "25 correos", - "50": "50 correos", - "100": "100 correos", - "label": "Correos por Página", - "description": "Número de correos a cargar a la vez" - }, - "always_light_mode": { - "label": "Mostrar correos siempre en modo claro", - "description": "Mostrar el contenido del correo en modo claro incluso cuando la aplicación está en modo oscuro, evitando problemas de conversión" - }, - "external_content": { - "label": "Contenido Externo", - "description": "Cómo manejar imágenes y contenido externo", - "ask": "Preguntar siempre", - "block": "Bloquear siempre", - "allow": "Permitir siempre" - }, - "trusted_senders": { - "label": "Remitentes de Confianza", - "description": "Administre remitentes cuyas imágenes se cargan automáticamente", - "count_zero": "Ninguno", - "count_one": "1 remitente", - "count_other": "{count} remitentes", - "modal_title": "Remitentes de Confianza", - "empty_title": "Aún no hay remitentes de confianza", - "empty_description": "Al ver un correo con imágenes bloqueadas, haga clic en \"Confiar siempre en este remitente\" para agregarlo aquí.", - "add_manually": "Agregar remitente manualmente", - "add_button": "Agregar", - "add_placeholder": "Ingrese dirección de correo", - "search_placeholder": "Buscar remitentes...", - "no_results": "Ningún remitente coincide con su búsqueda", - "remove": "Eliminar", - "close": "Cerrar", - "invalid_email": "Por favor ingrese una dirección de correo válida", - "already_added": "Este remitente ya es de confianza", - "save_error": "No se pudo guardar - revisa el registro de depuración de Contactos para más detalles", - "use_address_book_label": "Sincronizar con la libreta de direcciones", - "use_address_book_description": "Guarda remitentes de confianza en una libreta \"Remitentes de confianza\" para sincronizarlos en todos tus dispositivos" - }, - "hover_actions": { - "label": "Acciones rápidas al pasar el ratón", - "description": "Elige qué acciones rápidas aparecen al pasar el ratón sobre un correo en la lista", - "delete": "Eliminar", - "star": "Marcar / Desmarcar estrella", - "mark_read": "Marcar como leído / no leído", - "archive": "Archivar", - "tag": "Etiqueta", - "spam": "Marcar como spam", - "not_spam": "No es spam", - "none_selected": "No hay acciones seleccionadas", - "mode_label": "Modo de visualización", - "mode_inline": "En línea", - "mode_floating": "Flotante", - "corner_label": "Posición flotante", - "corner_top-left": "Arriba izquierda", - "corner_top-right": "Arriba derecha", - "corner_bottom-left": "Abajo izquierda", - "corner_bottom-right": "Abajo derecha" - }, - "default_mail_program": { - "label": "Programa de correo predeterminado", - "description": "Registrar {appName} como su programa de correo predeterminado para enlaces mailto:", - "button": "Establecer como predeterminado", - "success": "El navegador solicitó establecer como predeterminado", - "error": "Su navegador no admite esta función" - }, - "attachment_reminder": { - "label": "Recordatorio de adjuntos", - "description": "Advertir antes de enviar cuando su mensaje menciona adjuntos pero no hay ninguno adjuntado", - "keywords_label": "Palabras clave de activación", - "keywords_description": "Palabras o frases que activan el recordatorio cuando se encuentran en su mensaje", - "add_placeholder": "Añadir palabra clave...", - "add": "Add", - "remove": "Eliminar" - }, - "empty_subject_warning": { - "label": "Aviso de asunto vacío", - "description": "Pedir confirmación antes de enviar un mensaje sin asunto." - }, - "hide_inline_image_attachments": { - "label": "Ocultar imágenes incrustadas de los adjuntos", - "description": "Las imágenes incrustadas en el cuerpo del mensaje no se listan como adjuntos separados" - }, - "attachment_image_previews": { - "label": "Mostrar vistas previas de imágenes en adjuntos", - "description": "Mostrar las imágenes adjuntas como tarjetas de miniatura en lugar de iconos de archivo genéricos" - }, - "send_delay": { - "label": "Deshacer envío / demora de envío", - "description": "Retrasa envíos normales con una breve ventana del servidor.", - "off": "Desactivado", - "seconds": "{seconds} segundos", - "unsupported": "La cuenta actual no anuncia compatibilidad con envío demorado. La opción seguirá guardada para otras cuentas." - }, - "return_to_list_after_action": { - "label": "Volver a la lista tras eliminar o marcar como no leído", - "description": "Después de eliminar o marcar como no leído el mensaje abierto, volver a la lista de mensajes en lugar de abrir el mensaje siguiente." - }, - "swipe_right_action": { - "label": "Acción al deslizar a la derecha (móvil)", - "description": "En dispositivos táctiles, la acción que se realiza al deslizar un mensaje hacia la derecha en la lista." - }, - "swipe_left_action": { - "label": "Acción al deslizar a la izquierda (móvil)", - "description": "En dispositivos táctiles, la acción que se realiza al deslizar un mensaje hacia la izquierda en la lista." - }, - "swipe_actions": { - "none": "Ninguna", - "archive": "Archivar", - "delete": "Eliminar", - "mark_read": "Alternar leído", - "star": "Alternar destacado", - "spam": "Marcar como spam" - }, - "rtl_editing": { - "label": "Compatibilidad con edición de derecha a izquierda", - "description": "Añade un botón de dirección a la barra de herramientas del editor para configurar los párrafos de izquierda a derecha o de derecha a izquierda" - } - }, - "composer": { - "title": "Editor", - "description": "Configure los ajustes de redacción de correos", - "autosave": { - "label": "Intervalo de Guardado Automático", - "description": "Con qué frecuencia guardar borradores automáticamente", - "30s": "Cada 30 segundos", - "1m": "Cada minuto", - "2m": "Cada 2 minutos", - "5m": "Cada 5 minutos" - }, - "send_confirmation": { - "label": "Confirmación de Envío", - "description": "Solicitar confirmación antes de enviar correos" - }, - "default_reply": { - "label": "Modo de Respuesta Predeterminado", - "description": "Acción predeterminada al hacer clic en responder", - "reply": "Responder", - "reply_all": "Responder a Todos" - } - }, - "privacy": { - "title": "Privacidad y Seguridad", - "description": "Administre su configuración de privacidad y seguridad", - "external_images": { - "label": "Bloquear Imágenes Externas", - "description": "Prevenir el seguimiento a través de imágenes externas" - }, - "session_timeout": { - "label": "Tiempo de Espera de Sesión", - "description": "Cerrar sesión automáticamente después de inactividad", - "never": "Nunca", - "30m": "30 minutos", - "1h": "1 hora", - "4h": "4 horas" - }, - "clear_cache": { - "label": "Limpiar Caché", - "description": "Eliminar datos en caché y archivos temporales", - "button": "Limpiar Caché", - "confirm": "¿Está seguro de que desea limpiar la caché?", - "success": "Caché limpiada exitosamente" - } - }, - "account": { - "title": "Cuenta", - "description": "Vea la información de su cuenta", - "name_label": "Nombre para mostrar", - "username_label": "Nombre de usuario", - "account_type_label": "Tipo de cuenta", - "auth_method_label": "Autenticación", - "auth_method_oauth": "Inicio de sesión único (OAuth/OIDC)", - "auth_method_basic": "Contraseña", - "demo_account": "Cuenta de demostración", - "email": { - "label": "Dirección de Correo", - "value": "{email}" - }, - "server": { - "label": "Servidor JMAP", - "value": "{server}" - }, - "storage": { - "label": "Uso de Almacenamiento", - "used": "{used} de {total} usado", - "percentage": "{percent}% usado" - }, - "last_sync": { - "label": "Última Sincronización", - "value": "{time}" - }, - "accounts": { - "title": "Cuentas conectadas", - "description": "Arrastra para reordenar cómo aparecen las cuentas en el menú desplegable", - "active": "Cuenta actualmente activa", - "default_badge": "Cuenta predeterminada", - "set_default": "Establecer como predeterminada", - "switch_to": "Cambiar a esta cuenta", - "move_up": "Mover arriba", - "move_down": "Mover abajo", - "drag_handle": "Arrastra para reordenar", - "add": "Añadir cuenta" - }, - "shared_accounts": { - "title": "Compartido conmigo", - "description": "Cuentas de grupo y compartidas que puedes gestionar. Selecciona una para editar sus filtros, respuesta de vacaciones, calendarios y contactos.", - "shared_label": "Cuenta compartida" - } - }, - "security": { - "title": "Seguridad de la cuenta", - "description": "Administre su contraseña, autenticación de dos factores y configuración de seguridad", - "detecting": "Detectando capacidades del servidor...", - "not_available": "La gestión de seguridad de la cuenta no está disponible para este servidor de correo. Es posible que los permisos requeridos estén deshabilitados. Consulte la documentación para obtener más detalles.", - "password": { - "title": "Cambiar contraseña", - "current": "Contraseña actual", - "new": "Nueva contraseña", - "confirm": "Confirmar nueva contraseña", - "submit": "Cambiar contraseña", - "success": "Contraseña cambiada exitosamente", - "error_title": "Error al cambiar la contraseña", - "error_mismatch": "Las nuevas contraseñas no coinciden", - "error_min_length": "La contraseña debe tener al menos 8 caracteres", - "error_generic": "No se pudo cambiar la contraseña" - }, - "display_name": { - "label": "Nombre para mostrar", - "description": "Su nombre tal como aparece en el servidor", - "placeholder": "Ingrese su nombre para mostrar", - "save": "Guardar", - "success": "Nombre para mostrar actualizado", - "error": "No se pudo actualizar el nombre para mostrar" - }, - "totp": { - "section_title": "Autenticación de dos factores", - "label": "Autenticación TOTP", - "description": "Añada una capa adicional de seguridad con una contraseña de un solo uso basada en tiempo", - "active": "Habilitado", - "inactive": "Deshabilitado", - "enabled": "Autenticación de dos factores habilitada", - "disabled": "Autenticación de dos factores deshabilitada", - "enable_error": "No se pudo habilitar 2FA", - "disable_error": "No se pudo deshabilitar 2FA", - "setup_instructions": "Copie esta URL en su aplicación de autenticación (Google Authenticator, Authy, etc.):", - "verification_code": "Código de verificación", - "confirm": "Confirmar", - "disable": "Desactivar", - "disable_confirm_prompt": "Introduce tu contraseña para desactivar la autenticación de dos factores.", - "password_required": "Se requiere la contraseña", - "code_required": "Se requiere el código de verificación", - "code_invalid": "Código de verificación no válido. Revisa tu aplicación de autenticación e inténtalo de nuevo." - }, - "app_passwords": { - "title": "Contraseñas de aplicación", - "description": "Cree contraseñas para aplicaciones que no admiten autenticación de dos factores", - "add": "Agregar", - "create": "Crear", - "cancel": "Cancelar", - "generate": "Generar", - "name_label": "Nombre de la aplicación", - "name_placeholder": "p. ej. Thunderbird, iPhone Mail", - "password_label": "Contraseña (dejar vacío para auto-generar)", - "password_placeholder": "Auto-generada si está vacío", - "added": "Contraseña de aplicación creada", - "removed": "Contraseña de aplicación eliminada", - "add_error": "No se pudo crear la contraseña de aplicación", - "remove_error": "No se pudo eliminar la contraseña de aplicación", - "none": "No hay contraseñas de aplicación configuradas", - "done": "Hecho", - "expires_label": "Caduca (opcional)", - "copy_now_warning": "Copia esta contraseña ahora - no se volverá a mostrar.", - "allowed_ips_label": "IPs permitidas (opcional)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Separadas por coma o espacio. Dejar vacío para permitir cualquier IP." - }, - "api_keys": { - "title": "Claves API", - "description": "Crea claves API para scripts e integraciones que se comunican directamente con el servidor", - "name_label": "Nombre de la clave", - "name_placeholder": "p. ej. Script de copia de seguridad, CI runner", - "copy_now_warning": "Copia esta clave API ahora - no se mostrará de nuevo.", - "added": "Clave API creada", - "removed": "Clave API eliminada", - "add_error": "No se pudo crear la clave API", - "remove_error": "No se pudo eliminar la clave API", - "none": "No hay claves API configuradas" - }, - "encryption": { - "section_title": "Cifrado en reposo", - "label": "Cifrado de correo electrónico", - "description": "Cifre los correos almacenados en el servidor para mayor privacidad", - "active": "Cifrado {type} habilitado", - "inactive": "Deshabilitado", - "enabled": "Cifrado en reposo habilitado", - "disabled_success": "Cifrado en reposo deshabilitado", - "error": "No se pudieron actualizar las configuraciones de cifrado", - "algorithm_label": "Algoritmo de cifrado", - "encrypt_on_append": "Cifrar los correos nuevos al subirlos", - "allow_spam_training": "Permitir el entrenamiento antispam antes de cifrar los correos", - "enable_encryption": "Habilitar el cifrado", - "enable_tooltip": "Habilitar el cifrado con esta clave", - "configure_title": "Habilitar el cifrado con esta clave", - "enable_button": "Habilitar", - "disable_tooltip": "Deshabilitar el cifrado", - "enabled_success": "Cifrado en reposo habilitado" - }, - "email_client": { - "title": "Configuración del cliente de correo", - "description": "Use estas credenciales para configurar su cliente de correo de escritorio o móvil (Thunderbird, Apple Mail, Outlook, etc.)", - "jmap_username_label": "Nombre de usuario JMAP", - "copy": "Copiar", - "copied": "Copiado", - "password_instructions": "Use su nombre de usuario JMAP indicado arriba junto con una contraseña de aplicación para iniciar sesión en su cliente de correo. Cree una contraseña de aplicación en la sección anterior si aún no lo ha hecho." - }, - "link_device": { - "title": "Vincular aplicación móvil", - "description": "Inicie sesión en la aplicación móvil de Bulwark Mail sin escribir nada. Genere un código QR aquí y escanéelo desde la pantalla de inicio de sesión de la aplicación.", - "generate": "Mostrar código QR", - "regenerate": "Mostrar un código nuevo", - "instructions": "Abra la aplicación Bulwark Mail, toque \"Escanear código QR\" en la pantalla de inicio de sesión y apunte su cámara aquí.", - "expires_in": "Este código caduca en {seconds} segundos. Solo se puede usar una vez.", - "expired": "Este código ha caducado.", - "generating": "Generando…", - "error": "No se pudo crear un código de vinculación. Inténtelo de nuevo." - }, - "public_keys": { - "name_label": "Nombre de la clave pública", - "name_placeholder": "ej. Mi clave", - "key_label": "Clave pública (S/MIME o PGP en formato ASCII)", - "key_placeholder": "Pega aquí tu clave pública", - "none": "No hay claves públicas configuradas" - } - }, - "identities": { - "title": "Identidades de Envío", - "description": "Administre las direcciones de correo desde las que puede enviar", - "identities_count": { - "label": "Sus Identidades", - "description": "Direcciones de correo configuradas para enviar", - "count_zero": "Sin identidades", - "count_one": "1 identidad", - "count_other": "{count} identidades" - }, - "manage": "Administrar Identidades", - "sub_addressing": { - "label": "Sub-Direccionamiento", - "description": "Use etiquetas como usuario+etiqueta@dominio.com para organizar el correo entrante", - "learn_more": "Más Información" - } - }, - "vacation": { - "title": "Respuesta automática", - "description": "Responder automáticamente a los correos entrantes mientras está ausente", - "loading": "Cargando configuración de respuesta automática...", - "not_supported": "Su servidor de correo no admite respuestas automáticas de ausencia.", - "fetch_error": "Error al cargar la configuración de respuesta automática. Inténtelo de nuevo.", - "status": { - "label": "Respuesta automática", - "description": "Enviar una respuesta automática a quienes le envíen un correo", - "active": "Activo", - "inactive": "Inactivo" - }, - "date_range": { - "title": "Período", - "description": "Opcionalmente limitar la respuesta automática a un período específico", - "start": "Fecha de inicio", - "start_description": "Dejar vacío para sin límite de inicio", - "end": "Fecha de fin", - "end_description": "Dejar vacío para sin límite de fin" - }, - "message": { - "title": "Mensaje de respuesta automática", - "description": "El mensaje que se enviará como respuesta", - "subject_label": "Asunto", - "subject_description": "Línea de asunto de la respuesta automática", - "subject_placeholder": "Fuera de la oficina", - "body_label": "Cuerpo del mensaje", - "body_description": "Contenido del mensaje en texto plano", - "body_placeholder": "Gracias por su correo. Actualmente estoy fuera de la oficina y responderé cuando regrese.", - "html_label": "Mensaje con formato (HTML)", - "html_description": "Añade una versión con formato, enlaces y estilo. Los destinatarios cuyo cliente de correo no pueda mostrarla verán el texto sin formato de arriba.", - "html_placeholder": "Escribe una respuesta de ausencia con formato…" - }, - "preview": { - "title": "Vista previa", - "show": "Mostrar vista previa", - "hide": "Ocultar vista previa" - }, - "save": "Guardar cambios", - "saving": "Guardando...", - "warnings": { - "end_before_start": "La fecha de fin debe ser posterior a la fecha de inicio", - "start_in_past": "La fecha de inicio está en el pasado", - "empty_body": "El cuerpo del mensaje está vacío - los destinatarios recibirán una respuesta en blanco", - "subject_too_long": "El asunto es demasiado largo para el servidor (máximo {max} bytes).", - "body_too_long": "El mensaje es demasiado largo para el servidor (máximo {max} bytes, formato incluido)." - } - }, - "folders": { - "role_memos": "Notas", - "title": "Carpetas", - "description": "Administre sus carpetas de correo y asigne roles estándar", - "folder_list": "Sus carpetas", - "folder_list_description": "Haga clic en el icono de una carpeta para personalizarla", - "standard_roles": "Roles estándar de carpetas", - "standard_roles_description": "Asigne qué carpetas se utilizan para roles estándar como Bandeja de entrada, Enviados, Papelera, etc.", - "role_inbox": "Bandeja de entrada", - "role_drafts": "Borradores", - "role_sent": "Enviados", - "role_trash": "Papelera", - "role_junk": "Spam / No deseado", - "role_archive": "Archivo", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Ninguno", - "create_folder": "Crear carpeta", - "create_subfolder": "Crear subcarpeta", - "reorder": "Arrastra para reordenar", - "reorder_error": "No se pudieron reordenar las carpetas", - "move_error": "No se pudo mover la carpeta", - "subfolder_of": "Dentro de {name}", - "subfolder_name": "Nombre de la subcarpeta", - "new_folder_name": "Nombre de la carpeta", - "rename": "Renombrar", - "change_icon": "Cambiar icono", - "delete": "Eliminar", - "confirm_delete": "¿Está seguro de que desea eliminar \"{name}\"? Los correos de esta carpeta se moverán a la Papelera.", - "create": "Crear", - "cancel": "Cancelar", - "no_folders": "Sin carpetas personalizadas", - "cannot_delete_role": "No se puede eliminar una carpeta con un rol estándar. Quite el rol primero.", - "folder_created": "Carpeta creada", - "folder_renamed": "Carpeta renombrada", - "folder_deleted": "Carpeta eliminada", - "role_updated": "Rol de carpeta actualizado", - "error_create": "Error al crear la carpeta", - "error_rename": "Error al renombrar la carpeta", - "error_delete": "Error al eliminar la carpeta", - "error_delete_has_children": "No se puede eliminar la carpeta: aún contiene subcarpetas. Elimínelas o muévalas primero.", - "error_delete_has_email": "No se puede eliminar la carpeta: aún contiene correos. Muévalos o elimínelos primero.", - "error_role": "Error al actualizar el rol de la carpeta" - }, - "advanced": { - "title": "Avanzado", - "description": "Opciones avanzadas y configuración de desarrollador", - "debug_mode": { - "label": "Modo de Depuración", - "description": "Habilitar registro detallado para solución de problemas" - }, - "debug_categories": { - "description": "Selecciona qué categorías registrar. Desactiva las categorías que no necesites para reducir el ruido en la consola.", - "jmap": "Cliente JMAP", - "jmap_description": "Operaciones de buzón, obtención de correo y solicitudes del protocolo JMAP", - "calendar": "Calendario", - "calendar_description": "Eventos del calendario, importaciones y mensajes de programación", - "tasks": "Tareas", - "tasks_description": "Creación, obtención y actualizaciones de tareas del calendario", - "auth": "Autenticación", - "auth_description": "Inicio de sesión, TOTP, intercambio de tokens y gestión de sesiones", - "filters": "Filtros", - "filters_description": "Reglas de filtro Sieve y scripts de vacaciones", - "email": "Visualización de correo", - "email_description": "Renderizado de correos, procesamiento TNEF y marcado como leído", - "push": "Notificaciones push", - "push_description": "Configuración y entrega de notificaciones push", - "contacts": "Contactos y libretas de direcciones", - "contacts_description": "Sincronización de contactos, operaciones de libreta y remitentes de confianza" - }, - "settings_sync": { - "label": "Sincronización de ajustes", - "description": "Sincronice sus ajustes entre navegadores y dispositivos" - }, - "sender_favicons": { - "label": "Favicons de remitente", - "description": "Mostrar iconos de sitios web como fotos de perfil para remitentes empresariales" - }, - "show_avatars_in_junk": { - "label": "Mostrar avatares en la carpeta de spam", - "description": "Mostrar imágenes de perfil y favicons de remitentes en la carpeta de spam. Desactivado por defecto para no dar apariencia legítima a los intentos de phishing." - }, - "keyboard_shortcuts": { - "label": "Atajos de Teclado", - "description": "Ver atajos de teclado disponibles", - "button": "Ver Atajos" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Restablecer Configuración", - "description": "Restaurar toda la configuración a los valores predeterminados", - "button": "Restablecer a Predeterminados" - }, - "export_settings": { - "label": "Exportar Configuración", - "description": "Descargar su configuración como JSON", - "button": "Exportar" - }, - "import_settings": { - "label": "Importar Configuración", - "description": "Cargar configuración desde archivo JSON", - "button": "Importar" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Apps de barra lateral", - "description": "Gestionar aplicaciones y enlaces personalizados en la barra lateral", - "keep_loaded": "Mantener apps cargadas", - "keep_loaded_description": "Mantener las apps en línea ejecutándose en segundo plano al cambiar entre ellas para evitar recargas", - "managed_title": "Proporcionadas por tu administrador", - "managed_description": "Estas aplicaciones están configuradas para todos y no se pueden cambiar.", - "manage_title": "Apps personalizadas", - "manage_description": "Agregar, editar o eliminar apps personalizadas de la barra lateral" - }, - "contacts": { - "title": "Contactos", - "description": "Importar y exportar sus contactos", - "import_label": "Importar contactos", - "import_description": "Importar contactos desde un archivo vCard (.vcf)", - "export_label": "Exportar contactos", - "export_description": "Exportar todos los contactos como archivo vCard (.vcf)", - "manage_title": "Libretas de direcciones", - "manage_description": "Renombrar tus libretas de direcciones", - "no_address_books": "No se encontraron libretas de direcciones", - "categories_title": "Categorías", - "categories_description": "Renombrar categorías de contactos", - "no_categories": "No se encontraron categorías", - "group_by_letter_description": "Mostrar encabezados alfabéticos en la lista de contactos", - "group_by_letter_label": "Agrupar por primera letra", - "sort_by_last_name_label": "Ordenar por apellido", - "sort_by_last_name_description": "Ordenar la lista de contactos por apellido para que los miembros de una familia aparezcan juntos" - }, - "downloads": { - "title": "Descargas", - "description": "Personaliza cómo se nombran los correos y archivos adjuntos descargados.", - "reset": "Restaurar predeterminado", - "preview": "Vista previa:", - "email_template": { - "label": "Nombre del archivo de correo (.eml)", - "description": "Plantilla usada al exportar un correo o al arrastrarlo al sistema de archivos. La extensión .eml se añade automáticamente." - }, - "attachment_template": { - "label": "Nombre del archivo adjunto", - "description": "Plantilla usada al descargar o arrastrar un adjunto. Si omites '{filename}' y '{ext}', se conserva la extensión original." - }, - "bundle_template": { - "label": "Nombre de archivo .zip multi-correo", - "description": "Plantilla utilizada al arrastrar o descargar varios correos seleccionados como un único archivo .zip. La extensión .zip se añade automáticamente." - }, - "spaces": { - "label": "Espacios", - "description": "Reemplaza los espacios del nombre de archivo resultante por otro carácter.", - "keep": "Conservar espacios", - "underscore": "Reemplazar por _", - "dash": "Reemplazar por -" - }, - "lowercase": { - "label": "Minúsculas", - "description": "Forzar todo el nombre del archivo a minúsculas." - }, - "strip_diacritics": { - "label": "Eliminar diacríticos", - "description": "Convierte las letras acentuadas en sus equivalentes ASCII (ä → a, é → e). Útil para herramientas que estropean nombres Unicode." - }, - "collapse_separators": { - "label": "Combinar separadores repetidos", - "description": "Combina secuencias de espacios, guiones bajos o guiones en un único carácter." - }, - "after_export": { - "label": "Después de exportar", - "description": "Opcionalmente, mover el correo después de exportarlo como .eml.", - "keep": "Mantener en el buzón", - "archive": "Mover al archivo", - "trash": "Mover a la papelera" - } - }, - "filters": { - "title": "Filtros de correo", - "description": "Cree reglas para ordenar, etiquetar y gestionar automáticamente los correos entrantes", - "add_rule": "Agregar regla", - "no_rules": "Sin reglas de filtrado", - "vacation_active": "El respondedor de vacaciones está activo", - "vacation_active_description": "La respuesta automática está habilitada para los mensajes entrantes", - "vacation_configure": "Configurar", - "no_rules_description": "Cree reglas para organizar automáticamente sus correos entrantes", - "edit_rule": "Editar regla", - "new_rule": "Nueva regla", - "delete_rule": "Eliminar regla", - "delete_confirm": "¿Está seguro de que desea eliminar esta regla?", - "enable": "Activar", - "disable": "Desactivar", - "raw_editor": "Editor Sieve sin formato", - "raw_editor_warning": "Editar el script Sieve sin formato puede romper la edición visual de reglas. Los cambios realizados aquí anulan el constructor visual.", - "validate": "Validar", - "validation_success": "El script es válido", - "validation_error": "El script tiene errores", - "save": "Guardar reglas", - "saving": "Guardando...", - "saved": "Filtros guardados correctamente", - "save_failed": "Error al guardar los filtros", - "loading": "Cargando filtros...", - "not_supported": "Su servidor de correo no admite filtros de correo electrónico.", - "rule_name": "Nombre de la regla", - "rule_name_placeholder": "ej. Ordenar boletines", - "match_all": "Coincidir con TODAS las condiciones", - "match_any": "Coincidir con CUALQUIER condición", - "conditions": "Condiciones", - "add_condition": "Agregar condición", - "actions": "Acciones", - "add_action": "Agregar acción", - "stop_processing": "Detener el procesamiento de reglas posteriores", - "include_spam": "Mover también los mensajes marcados como spam", - "attachment_type_placeholder": "p. ej. pdf, doc, jpg", - "value_placeholder_multi": "Valor (varios separados por comas)", - "condition_fields": { - "attachment": "Adjunto", - "from": "De", - "to": "Para", - "cc": "Cc", - "subject": "Asunto", - "header": "Encabezado personalizado", - "size": "Tamaño", - "body": "Cuerpo" - }, - "comparators": { - "has_any": "está presente", - "has_type": "del tipo", - "contains": "contiene", - "not_contains": "no contiene", - "is": "es exactamente", - "not_is": "no es", - "starts_with": "comienza con", - "ends_with": "termina con", - "matches": "coincide con el patrón", - "greater_than": "es mayor que", - "less_than": "es menor que" - }, - "action_types": { - "move": "Mover a carpeta", - "copy": "Copiar a carpeta", - "forward": "Reenviar a", - "mark_read": "Marcar como leído", - "star": "Destacar mensaje", - "add_label": "Agregar etiqueta", - "discard": "Descartar (eliminar silenciosamente)", - "reject": "Rechazar con mensaje", - "keep": "Mantener en la bandeja de entrada", - "stop": "Detener procesamiento" - }, - "move_to_folder": "Seleccionar carpeta", - "copy_to_folder": "Seleccionar carpeta", - "forward_to": "Reenviar a dirección de correo", - "forward_placeholder": "correo@ejemplo.com", - "forward_keep_copy": "Conservar una copia", - "forward_limit": "Límite de reenvíos por mensaje en este servidor: {count}. Los reenvíos adicionales se omiten.", - "reject_message": "Mensaje de rechazo", - "reject_placeholder": "Su correo ha sido rechazado", - "label_name": "Nombre de la etiqueta", - "label_placeholder": "Seleccionar etiqueta", - "header_name": "Nombre del encabezado", - "header_placeholder": "ej. X-Mailing-List", - "size_bytes": "Tamaño en bytes", - "size_placeholder": "ej. 1000000", - "system_managed": "Regla gestionada por el sistema", - "opaque_warning": "Este script fue editado fuera del constructor visual. Solo está disponible la edición Sieve sin formato.", - "open_sieve_editor": "Abrir editor Sieve", - "fetch_error": "Error al cargar los filtros", - "expanded_view": "Vista expandida", - "expanded_view_description": "Mostrar reglas de filtro con bloques detallados de condiciones y acciones", - "if": "Si", - "then": "Entonces", - "match_all_conditions": "todas coinciden", - "match_any_condition": "alguna coincide", - "and": "y", - "or": "o", - "cancel": "Cancelar", - "confirm_delete": "Eliminar", - "rule_list": "Reglas de filtrado", - "drag_to_reorder": "Arrastrar para reordenar", - "match_type": "Tipo de coincidencia", - "reset_to_visual": "Restablecer al constructor visual", - "reset_warning": "Esto descartará el script actual y comenzará de nuevo.", - "confirm_reset": "Restablecer", - "validation_empty_name": "El nombre de la regla es obligatorio", - "validation_empty_conditions": "Se requiere al menos una condición con un valor", - "validation_empty_actions": "Se requiere al menos una acción", - "templates_section": "Usar una plantilla", - "template_newsletters": "Mover newsletters a carpeta", - "template_receipts": "Archivar recibos automáticamente", - "template_important": "Marcar correos importantes", - "template_notifications": "Filtrar notificaciones", - "sieve_editor": { - "title": "Editor de script Sieve", - "warning": "Editar el script Sieve puede romper la edición visual de reglas. Los cambios realizados aquí anulan el constructor visual.", - "script_content": "Script Sieve", - "valid": "El script es válido", - "invalid": "El script tiene errores", - "save_warning": "Guardar sobrescribirá todas las reglas visuales. Esta acción no se puede deshacer. Haga clic en Guardar de nuevo para confirmar.", - "validating": "Validando...", - "validate": "Validar", - "cancel": "Cancelar", - "save": "Guardar", - "confirm_save": "Confirmar guardado", - "validation_failed": "Error en la solicitud de validación" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# condición} other {# condiciones}}", - "actions_count": "{count, plural, one {# acción} other {# acciones}}" - }, - "origin_external": "Externo", - "managed_by_tooltip": "Gestionado por {source}. Edítalo en esa aplicación o usa el editor Sieve sin procesar." - }, - "templates": { - "title": "Plantillas de correo", - "description": "Crea plantillas reutilizables con variables de marcador de posición", - "add": "Nueva plantilla", - "edit": "Editar plantilla", - "name": "Nombre de la plantilla", - "name_placeholder": "ej.: Correo de seguimiento", - "category": "Categoría", - "category_placeholder": "ej.: Trabajo, Personal", - "subject": "Asunto", - "subject_placeholder": "Línea de asunto del correo", - "body": "Cuerpo", - "body_placeholder": "Contenido del correo...", - "recipients_placeholder": "email@example.com", - "identity": "Enviar como", - "default_identity": "Identidad predeterminada", - "favorite": "Favorito", - "cancel": "Cancelar", - "create": "Crear", - "update": "Actualizar", - "confirm_delete": "Eliminar", - "no_templates": "Sin plantillas", - "manage": "Gestionar plantillas", - "count": "{count, plural, one {# plantilla} other {# plantillas}}", - "export_import": "Exportar e importar", - "export_import_description": "Haz una copia de seguridad de tus plantillas o transfiérelas a otro dispositivo", - "export": "Exportar", - "import": "Importar", - "validation": { - "empty": "El nombre de la plantilla es obligatorio", - "too_long": "El nombre de la plantilla no debe superar los 200 caracteres" - } - }, - "files": { - "display": { - "title": "Visualización", - "description": "Configura cómo se muestran los archivos y carpetas" - }, - "folder_layout": { - "label": "Navegación de carpetas", - "description": "Elija cómo se muestran las carpetas: integradas con los archivos o en un árbol en la barra lateral", - "inline": "Integrado", - "sidebar": "Barra lateral" - }, - "default_view": { - "label": "Vista predeterminada", - "description": "Elige entre diseño de cuadrícula y lista", - "list": "Lista", - "grid": "Cuadrícula" - }, - "default_sort": { - "label": "Orden predeterminado", - "description": "Elige el orden predeterminado para los archivos", - "name": "Nombre", - "size": "Tamaño", - "modified": "Modificado" - }, - "sort_direction": { - "label": "Dirección de orden", - "description": "Elige orden ascendente o descendente", - "ascending": "Ascendente", - "descending": "Descendente" - }, - "icons": { - "title": "Iconos", - "description": "Configura la apariencia de los iconos de archivos" - }, - "show_icons": { - "label": "Mostrar iconos de archivos", - "description": "Mostrar iconos junto a archivos y carpetas" - }, - "colored_icons": { - "label": "Iconos de colores", - "description": "Usar iconos de colores en lugar de monocromáticos" - }, - "show_thumbnails": { - "label": "Mostrar miniaturas", - "description": "Mostrar vistas previas de imágenes en lugar de iconos" - }, - "behavior": { - "title": "Comportamiento", - "description": "Configura el comportamiento del explorador de archivos" - }, - "show_hidden": { - "label": "Mostrar archivos ocultos", - "description": "Mostrar archivos y carpetas que comienzan con un punto" - }, - "preview": { - "label": "Vista previa" - } - }, - "search_placeholder": "Buscar ajustes", - "search_clear": "Borrar búsqueda", - "search_no_results": "No hay ajustes coincidentes", - "scoped": { - "back": "Volver a mi cuenta", - "managing": "Gestionando: {name}" - } - }, - "errors": { - "page_error_title": "Algo salió mal", - "page_error_description": "Encontramos un error inesperado. Por favor, inténtelo de nuevo o regrese a la página principal.", - "sidebar_error": "No se pudieron cargar los buzones", - "email_list_error": "No se pudieron cargar los correos", - "viewer_error_title": "No se puede mostrar el correo", - "viewer_error_description": "Hubo un problema al renderizar este correo. Puede contener contenido no compatible.", - "composer_error": "No se pudo cargar el editor", - "settings_error_title": "Configuración no disponible", - "settings_error_description": "No se pudo cargar la configuración. Sus preferencias pueden no guardarse.", - "try_again": "Intentar de nuevo", - "reload": "Recargar", - "reload_emails": "Recargar correos", - "reload_settings": "Recargar configuración", - "retry": "Reintentar", - "go_home": "Ir a bandeja de entrada" - }, - "context_menu": { - "reply": "Responder", - "reply_all": "Responder a Todos", - "forward": "Reenviar", - "mark_read": "Marcar como Leído", - "mark_unread": "Marcar como No Leído", - "star": "Destacar", - "unstar": "Quitar Destacado", - "pin": "Anclar", - "unpin": "Desanclar", - "move_to": "Mover a...", - "archive": "Archivar", - "delete": "Eliminar", - "mark_as_spam": "Reportar spam", - "not_spam": "No es spam", - "tag": "Etiqueta", - "items_selected": "{count} correos seleccionados", - "edit_draft": "Editar borrador", - "cancel_scheduled_send": "Cancelar envío", - "reschedule_send": "Reprogramar", - "cancel_and_edit": "Cancelar y editar", - "cancel_and_compose_again": "Cancelar y redactar de nuevo" - }, - "mailbox_context_menu": { - "mark_folder_read": "Marcar carpeta como leída", - "mark_folder_tree_read": "Marcar carpeta y subcarpetas como leídas", - "mark_all_folders_read": "Marcar todas las carpetas como leídas", - "new_subfolder": "Nueva subcarpeta...", - "new_folder": "Nueva carpeta...", - "rename": "Renombrar...", - "share": "Compartir...", - "import_email": "Importar .eml o .zip...", - "empty_folder": "Vaciar carpeta", - "empty_folder_generic": "Vaciar carpeta", - "delete_folder": "Eliminar carpeta", - "refresh": "Actualizar", - "mark_all_confirm_title": "Marcar todas las carpetas como leídas", - "mark_all_confirm_message": "¿Marcar todos los mensajes no leídos de su cuenta personal como leídos?", - "delete_confirm_title": "Eliminar carpeta", - "delete_confirm_message": "¿Eliminar permanentemente la carpeta \"{name}\"? Esta acción no se puede deshacer.", - "prompt_new_subfolder": "Ingrese un nombre para la nueva subcarpeta.", - "prompt_new_folder": "Ingrese un nombre para la nueva carpeta.", - "prompt_rename": "Ingrese un nuevo nombre para esta carpeta.", - "toast_marked_read": "Carpeta marcada como leída", - "toast_marked_read_count": "Se marcaron {count, plural, one {1 mensaje} other {# mensajes}} como leídos", - "toast_already_read": "No hay mensajes no leídos", - "toast_marked_all_read": "Todas las carpetas marcadas como leídas", - "toast_emptied": "Carpeta vaciada", - "toast_folder_created": "Carpeta creada", - "toast_folder_renamed": "Carpeta renombrada", - "toast_folder_deleted": "Carpeta eliminada", - "toast_error_mark_read": "Error al marcar como leído", - "toast_error_empty": "Error al vaciar la carpeta", - "toast_error_create": "Error al crear la carpeta", - "toast_error_rename": "Error al renombrar la carpeta", - "toast_error_delete": "Error al eliminar la carpeta", - "toast_error_delete_has_children": "La carpeta tiene subcarpetas. Elimínelas primero.", - "toast_error_delete_has_email": "La carpeta no está vacía. Vacíela primero.", - "delete_with_contents_title": "La carpeta no está vacía", - "delete_with_contents_message": "La carpeta \"{name}\" aún contiene {count, plural, one {1 mensaje} other {# mensajes}}. ¿Eliminar la carpeta y todos sus mensajes? Esta acción no se puede deshacer.", - "delete_with_contents_confirm": "Eliminar carpeta y mensajes", - "placeholder_folder_name": "Nombre de carpeta", - "create": "Crear", - "rename_confirm": "Renombrar" - }, - "shortcuts": { - "title": "Atajos de Teclado", - "tip": "Presione ? en cualquier momento para mostrar esta ayuda", - "sections": { - "navigation": "Navegación", - "actions": "Acciones de Correo", - "global": "Global", - "threads": "Conversaciones", - "composer": "Redacción" - }, - "navigation": { - "next_email": "Siguiente correo", - "previous_email": "Correo anterior", - "open_email": "Abrir correo", - "close_email": "Cerrar / Deseleccionar" - }, - "actions": { - "reply": "Responder", - "reply_all": "Responder a todos", - "forward": "Reenviar", - "star": "Alternar destacado", - "archive": "Archivar", - "delete": "Eliminar", - "mark_unread": "Marcar como no leído", - "mark_read": "Marcar como leído", - "toggle_spam": "Reportar spam / No es spam" - }, - "global": { - "compose": "Redactar nuevo correo", - "search": "Enfocar búsqueda", - "help": "Mostrar atajos", - "refresh": "Actualizar correos", - "select_all": "Seleccionar todo", - "global_search": "Buscar en todo" - }, - "threads": { - "expand_collapse": "Expandir/contraer conversación" - }, - "composer": { - "send": "Enviar correo", - "schedule_send": "Programar envío", - "template_picker": "Abrir selector de plantillas" - } - }, - "threads": { - "messages_one": "{count} mensaje", - "messages_other": "{count} mensajes", - "messages_tooltip": "{count, plural, one {# mensaje en esta conversación} other {# mensajes en esta conversación}}", - "expand": "Expandir conversación", - "collapse": "Contraer conversación", - "loading": "Cargando conversación...", - "mark_read": "Marcar conversación como leída", - "mark_unread": "Marcar conversación como no leída", - "archive": "Archivar conversación", - "delete": "Eliminar conversación", - "star": "Destacar conversación", - "unstar": "Quitar destacado de conversación", - "toggle_thread": "Expandir/contraer hilo" - }, - "identities": { - "modal_title": "Administrar Identidades de Envío", - "create_new": "Crear Nueva Identidad", - "edit_identity": "Editar Identidad", - "delete_confirm": "¿Eliminar esta identidad? Esto no se puede deshacer.", - "cannot_delete": "Esta identidad no se puede eliminar", - "primary_identity": "Principal", - "set_as_primary": "Establecer como principal", - "no_identities": "No se encontraron identidades", - "display": { - "reply_to": "Responder a:", - "bcc": "CCO:", - "signature": "Firma:", - "preview": "Vista previa:" - }, - "validation_errors": { - "invalid_emails": "Correos inválidos: {emails}", - "unknown_error": "Error desconocido" - }, - "form": { - "name_label": "Nombre para Mostrar", - "name_placeholder": "ej., Correo de Trabajo, Personal", - "name_required": "El nombre es obligatorio", - "email_label": "Dirección de Correo", - "email_placeholder": "su.correo@ejemplo.com", - "email_required": "El correo es obligatorio", - "email_invalid": "Por favor ingrese una dirección de correo válida", - "email_immutable": "La dirección de correo no se puede cambiar después de la creación", - "reply_to_label": "Responder a (opcional)", - "reply_to_placeholder": "diferente@correo.com", - "bcc_label": "CCO Automático (opcional)", - "bcc_placeholder": "archivo@correo.com", - "text_signature_label": "Firma de Texto", - "html_signature_label": "Firma HTML", - "signature_byte_counter": "{bytes} / {max} bytes", - "signature_byte_limit_reached": "Límite del servidor alcanzado", - "save": "Guardar Identidad", - "cancel": "Cancelar", - "creating": "Creando...", - "updating": "Actualizando..." - }, - "sub_address": { - "button_tooltip": "Usar sub-dirección", - "popover_title": "Agregar Etiqueta de Sub-Dirección", - "tag_input_placeholder": "Ingrese etiqueta (ej., compras)", - "preview_label": "Vista previa:", - "recent_tags": "Etiquetas Recientes", - "suggested_tags": "Sugeridas", - "use_address": "Usar Esta Dirección", - "invalid_tag": "La etiqueta debe ser solo alfanumérica y guiones", - "tag_too_long": "La etiqueta debe tener 30 caracteres o menos", - "help_text": "Los correos enviados a usuario{delimiter}etiqueta@dominio.com llegarán a su bandeja de entrada", - "validation": { - "empty": "La etiqueta no puede estar vacía", - "too_long": "La etiqueta debe tener {max} caracteres o menos", - "invalid_chars": "La etiqueta debe contener solo letras, números y guiones" - } - }, - "badge": { - "sent_via": "vía", - "sub_address_tag": "Enviado usando sub-dirección: {tag}", - "identity_name": "Enviado usando identidad: {name}", - "identity_short": "vía {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Eliminar", - "delete_confirm_title": "Eliminar identidad" - }, - "templates": { - "picker_title": "Elegir una plantilla", - "search_placeholder": "Buscar plantillas...", - "section_favorites": "Favoritos", - "section_recent": "Recientes", - "section_uncategorized": "Otros", - "no_templates": "Sin plantillas", - "no_results": "No se encontraron plantillas", - "fill_placeholders": "Completar valores de marcadores", - "enter_value": "Introduce un valor...", - "preview": "Vista previa", - "insert_with_values": "Insertar con valores", - "insert_raw": "Insertar sin completar", - "copy_suffix": "(copia)", - "placeholder": "Variable", - "placeholders": { - "recipient_name": "Nombre del destinatario", - "company": "Nombre de la empresa", - "date": "Fecha actual", - "day_of_week": "Día de la semana", - "sender_name": "Tu nombre" - } - }, - "contacts": { - "title": "Contactos", - "search_placeholder": "Buscar contactos...", - "create_new": "Nuevo contacto", - "no_category": "Sin categoría", - "rename_category": "Renombrar categoría", - "category_name_label": "Nombre de la categoría", - "category_renamed": "Categoría renombrada", - "category_rename_failed": "Error al renombrar la categoría", - "category_added": "Contacto añadido a {name}", - "category_added_plural": "{count} contactos añadidos a {name}", - "empty_state": "No hay contactos", - "empty_state_title": "Sin contactos", - "empty_state_subtitle": "Crea tu primer contacto o importa desde un archivo vCard", - "empty_search": "Ningún contacto coincide con tu búsqueda", - "empty_search_hint": "Prueba con otro término de búsqueda", - "empty_filtered": "Ningún contacto coincide con tus filtros", - "empty_filtered_hint": "Ajusta o limpia los filtros", - "clear_search": "Borrar búsqueda", - "import_vcard": "Importar vCard", - "delete_confirm_title": "Eliminar contacto", - "delete_confirm": "¿Estás seguro de que quieres eliminar este contacto?", - "local_mode": "Los contactos se almacenan localmente (el servidor no soporta JMAP Contacts)", - "back_to_contacts": "Volver a contactos", - "back_to_email": "Volver al correo", - "tabs": { - "all": "Todos", - "groups": "Grupos" - }, - "shared": { - "title": "Compartidos" - }, - "address_books": { - "create": "Nueva libreta de direcciones", - "created": "Libreta de direcciones creada", - "create_failed": "No se pudo crear la libreta de direcciones", - "title": "Mis Libretas de Direcciones", - "shared_prefix": "Compartido: {name}", - "moved": "Contacto movido a {name}", - "moved_plural": "{count} contactos movidos a {name}", - "move_failed": "Error al mover el contacto", - "address_book": "Libreta de direcciones", - "rename": "Renombrar libreta de direcciones", - "name_label": "Nombre de la libreta de direcciones", - "renamed": "Libreta de direcciones renombrada", - "rename_failed": "Error al renombrar la libreta de direcciones", - "default": "Predeterminada", - "set_default": "Establecer como predeterminada", - "default_updated": "Libreta de direcciones predeterminada actualizada", - "set_default_failed": "Error al establecer la libreta de direcciones predeterminada", - "manage": "Administrar libretas de direcciones", - "share": "Compartir libreta de direcciones", - "new_contact_in_book": "Nuevo contacto en esta libreta", - "delete": "Eliminar libreta de direcciones", - "confirm_delete": "¿Eliminar «{name}»? Todos los contactos de esta libreta se eliminarán.", - "deleted": "Libreta de direcciones eliminada", - "delete_failed": "No se pudo eliminar la libreta de direcciones" - }, - "detail": { - "emails": "Direcciones de correo", - "phones": "Números de teléfono", - "organizations": "Organizaciones", - "addresses": "Direcciones", - "notes": "Notas", - "titles": "Títulos y roles", - "online_services": "Servicios en línea", - "anniversaries": "Aniversarios", - "personal_info": "Información personal", - "languages": "Idiomas", - "categories": "Categorías", - "related_contacts": "Contactos relacionados", - "crypto_keys": "Claves criptográficas", - "no_contact_selected": "Selecciona un contacto para ver los detalles", - "compose_email": "Escribir correo", - "copy_email": "Copiar correo", - "copy_phone": "Copiar teléfono", - "copy_url": "Copiar URL", - "copied": "Copiado al portapapeles", - "copy_failed": "Error al copiar al portapapeles", - "created": "Creado", - "updated": "Última actualización", - "timezone": "Zona horaria", - "anniversary_birth": "Cumpleaños", - "anniversary_death": "Fallecimiento", - "anniversary_wedding": "Aniversario", - "anniversary_other": "Otro", - "personal_expertise": "Especialidad", - "personal_hobby": "Pasatiempo", - "personal_interest": "Interés", - "personal_other": "Otro", - "gender": "Género", - "gender_masculine": "Masculino", - "gender_feminine": "Femenino", - "gender_other": "Otro", - "gender_none": "No aplicable", - "gender_unknown": "Desconocido", - "calendar": "Calendario", - "calendar_uri": "URL del calendario", - "scheduling_uri": "URL de programación", - "freebusy_uri": "URL de disponibilidad", - "cert_issuer": "Emisor", - "cert_expires": "Caduca", - "cert_expired": "Caducado", - "cert_fingerprint": "Huella digital", - "cert_algorithm": "Algoritmo", - "import_to_smime": "Importar a S/MIME", - "cert_already_imported": "Certificado ya importado", - "cert_imported": "Certificado importado", - "cert_import_failed": "Error al importar el certificado", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Más acciones", - "age_years": "{count, plural, one {1 año} other {# años}}", - "years_since": "{count, plural, one {hace 1 año} other {hace # años}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Nuevo contacto", - "edit_title": "Editar contacto", - "section_address_book": "Directorio", - "select_address_book": "Seleccionar un directorio...", - "section_identity": "Nombre e identidad", - "contact_type": "Tipo de contacto", - "type_person": "Persona", - "type_organization": "Organización", - "section_work": "Trabajo y organización", - "prefix": "Prefijo", - "prefix_placeholder": "Dr., Sr., Sra.", - "given_name": "Nombre", - "middle_name": "Segundo nombre", - "surname": "Apellido", - "suffix": "Sufijo", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Apodo", - "nickname_placeholder": "Apodo", - "email": "Correo", - "email_placeholder": "email@example.com", - "phone": "Teléfono", - "phone_placeholder": "+34 612 345 678", - "phone_type": "Tipo", - "phone_voice": "Voz", - "phone_cell": "Móvil", - "phone_fax": "Fax", - "phone_pager": "Buscapersonas", - "phone_video": "Video", - "phone_text": "Texto", - "organization": "Organización", - "organization_placeholder": "Nombre de la empresa", - "department": "Departamento", - "department_placeholder": "Departamento", - "job_title": "Cargo", - "job_title_placeholder": "p. ej., Ingeniero de software", - "role": "Rol", - "role_placeholder": "p. ej., Líder de equipo", - "addresses": "Direcciones", - "add_address": "Agregar dirección", - "street": "Calle", - "city": "Ciudad", - "region": "Estado / Región", - "postcode": "Código postal", - "country": "País", - "online_services": "Servicios en línea", - "add_online_service": "Agregar servicio en línea", - "url_placeholder": "https://...", - "service_placeholder": "Servicio", - "anniversaries": "Aniversarios", - "add_anniversary": "Agregar fecha", - "anniversary_birth": "Cumpleaños", - "anniversary_wedding": "Aniversario", - "anniversary_death": "Conmemoración", - "anniversary_other": "Otro", - "personal_info": "Información personal", - "add_personal_info": "Agregar entrada", - "personal_info_placeholder": "p. ej., Fotografía", - "personal_expertise": "Especialidad", - "personal_hobby": "Pasatiempo", - "personal_interest": "Interés", - "personal_other": "Otro", - "level": "Nivel", - "level_high": "Alto", - "level_medium": "Medio", - "level_low": "Bajo", - "categories": "Categorías", - "categories_placeholder": "p. ej., Familia, Amigos, Colegas", - "categories_hint": "Escriba para buscar o añadir categorías", - "category_add": "Añadir", - "note": "Notas", - "note_placeholder": "Agregar una nota...", - "gender": "Género", - "gender_sex": "Sexo", - "gender_male": "Masculino", - "gender_female": "Femenino", - "gender_other": "Otro", - "gender_none": "No aplicable", - "gender_unknown": "Desconocido", - "gender_identity": "Identidad de género", - "gender_identity_placeholder": "Identidad de género...", - "calendar": "Calendario", - "calendar_uri": "URL del calendario", - "scheduling_uri": "URL de programación", - "freebusy_uri": "URL de disponibilidad", - "context_work": "Trabajo", - "context_private": "Personal", - "add_email": "Agregar correo", - "add_phone": "Agregar teléfono", - "save": "Guardar", - "cancel": "Cancelar", - "creating": "Creando...", - "updating": "Actualizando...", - "name_required": "Introduce un nombre, un apellido o una organización", - "email_invalid": "Introduce una dirección de correo válida", - "email_error_inline": "Formato de correo inválido", - "save_failed": "Error al guardar el contacto", - "delete": "Eliminar", - "upload_photo": "Subir foto", - "remove_photo": "Quitar foto", - "photo_hint": "JPG o PNG, hasta 10 MB. Se redimensionará.", - "photo_too_large": "La imagen es demasiado grande (máx. 10 MB)", - "photo_invalid": "Archivo de imagen no válido", - "change_photo": "Cambiar" - }, - "groups": { - "create": "Nuevo grupo", - "edit": "Editar grupo", - "empty": "No hay grupos", - "delete_confirm_title": "Eliminar grupo", - "delete_confirm": "¿Estás seguro de que quieres eliminar este grupo?", - "name_label": "Nombre del grupo", - "name_placeholder": "ej. Equipo, Familia", - "name_required": "El nombre del grupo es obligatorio", - "save_failed": "Error al guardar el grupo", - "members_label": "Miembros", - "search_members": "Buscar contactos para agregar...", - "no_members": "No hay miembros en este grupo", - "member_count": "{count, plural, =0 {Sin miembros} one {1 miembro} other {# miembros}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Importar contactos", - "drop_hint": "Haz clic para seleccionar un archivo vCard", - "file_types": "Archivos .vcf o .vcard", - "no_contacts": "No se encontraron contactos en el archivo", - "parse_error": "Error al analizar el archivo vCard", - "found": "{count, plural, one {1 contacto encontrado} other {# contactos encontrados}}", - "duplicate": "Duplicado", - "select_all": "Seleccionar todo", - "deselect_all": "Deseleccionar todo", - "selected": "{count, plural, one {1 seleccionado} other {# seleccionados}}", - "import_button": "Importar", - "importing": "Importando...", - "success": "{count, plural, one {1 contacto importado} other {# contactos importados}}", - "failed": "Error en la importación", - "close": "Cerrar", - "file_too_large": "El archivo es demasiado grande (máx. 5 MB)" - }, - "export": { - "title": "Exportar contactos", - "success": "{count, plural, one {1 contacto exportado} other {# contactos exportados}}" - }, - "bulk": { - "selected": "{count, plural, one {1 seleccionado} other {# seleccionados}}", - "select_all": "Seleccionar todo", - "delete": "Eliminar", - "delete_confirm_title": "Eliminar contactos", - "delete_confirm": "¿Eliminar {count, plural, one {1 contacto} other {# contactos}}?", - "deleted": "{count, plural, one {1 contacto eliminado} other {# contactos eliminados}}", - "add_to_group": "Agregar al grupo", - "choose_group": "Elegir un grupo", - "adding_contacts": "Agregando {count, plural, one {1 contacto} other {# contactos}}", - "added_to_group": "Contactos agregados al grupo", - "export": "Exportar", - "clear": "Limpiar selección" - }, - "toast": { - "created": "Contacto creado", - "updated": "Contacto actualizado", - "deleted": "Contacto eliminado", - "error_create": "Error al crear el contacto", - "error_update": "Error al actualizar el contacto", - "error_delete": "Error al eliminar el contacto" - }, - "context_menu": { - "open": "Abrir", - "edit": "Editar", - "send_email": "Enviar correo", - "add_to_group": "Añadir al grupo", - "export_vcard": "Exportar como vCard", - "delete": "Eliminar", - "call": "Llamar", - "duplicate": "Duplicar", - "print": "Imprimir" - }, - "filters": { - "toggle": "Filtros", - "select": "Seleccionar", - "clear": "Borrar", - "close": "Cerrar", - "title": "Filtros avanzados", - "organization": "Empresa", - "organization_placeholder": "p. ej. Acme", - "job_title": "Puesto", - "job_title_placeholder": "p. ej. Diseñador", - "location": "Ubicación", - "location_placeholder": "Ciudad o país", - "email_domain": "Dominio de correo", - "email_domain_placeholder": "ejemplo.com", - "birthday_month": "Cumpleaños en", - "any_month": "Cualquier mes", - "has_email": "Con correo", - "has_phone": "Con teléfono", - "has_photo": "Con foto", - "sort_by": "Ordenar por", - "sort_first_name": "Nombre", - "sort_last_name": "Apellido" - }, - "open_categories": "Abrir categorías" - }, - "calendar": { - "title": "Calendario", - "back_to_email": "Volver al correo", - "back_to_month": "Volver al mes", - "my_calendars": "Mis calendarios", - "birthday_calendar": "Cumpleaños", - "mini_calendar_change": "Clic para cambiar de mes", - "views": { - "month": "Mes", - "week": "Semana", - "day": "Día", - "agenda": "Agenda", - "today": "Hoy", - "month_hint": "Mes (m)", - "week_hint": "Semana (w)", - "day_hint": "Día (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Tareas", - "tasks_hint": "Tareas (k)" - }, - "events": { - "create": "Crear evento", - "edit": "Editar evento", - "delete": "Eliminar evento", - "details": "Detalles del evento", - "no_events": "Sin eventos", - "all_day": "Todo el día", - "more": "+{count} más", - "no_title": "(Sin título)", - "resize": "Redimensionar evento", - "duplicate": "Duplicar", - "today_header": "Hoy", - "tomorrow_header": "Mañana", - "agenda_show_earlier": "Mostrar eventos anteriores", - "agenda_loading": "Cargando…", - "agenda_range_start": "Eventos desde el {date}", - "agenda_range_end": "Eventos hasta el {date}", - "export_ics": "Exportar como .ics", - "copy_title": "Copiar título", - "copy_link": "Copiar enlace de reunión", - "go_to_today": "Ir a hoy", - "new_all_day_event": "Nuevo evento de todo el día", - "new_event": "Nuevo evento", - "new_task": "Nueva tarea" - }, - "detail": { - "add_note": "Añadir una nota...", - "save_note": "Guardar", - "note_saved": "Nota añadida", - "open_link": "Abrir enlace", - "meeting_link": "Enlace de reunión", - "tentative": "Provisional", - "cancelled": "Cancelado", - "delete_confirm": "¿Eliminar este evento?" - }, - "form": { - "title": "Título", - "description": "Descripción", - "location": "Ubicación", - "meeting_link": "Enlace de reunión", - "start_date": "Fecha de inicio", - "end_date": "Fecha de fin", - "start_time": "Hora de inicio", - "end_time": "Hora de fin", - "all_day_event": "Evento de todo el día", - "calendar_select": "Calendario", - "save": "Guardar", - "cancel": "Cancelar", - "delete_confirm": "¿Está seguro de que desea eliminar este evento?", - "color": "Color" - }, - "participants": { - "availability": { - "title": "Disponibilidad", - "free": "Disponible", - "busy": "Ocupado", - "tentative": "Provisional", - "unknown": "No es un usuario de este servidor", - "checking": "Comprobando…" - }, - "title": "Participantes", - "add": "Agregar participante", - "organizer": "Organizador", - "attendee": "Participante", - "accepted": "Aceptado", - "declined": "Rechazado", - "tentative": "Provisional", - "needs_action": "Pendiente de respuesta", - "remove": "Eliminar", - "edit": "Editar", - "email_placeholder": "Añadir dirección de correo o buscar contactos", - "send_invitations": "Enviar invitaciones a los participantes", - "status_summary": "{accepted} aceptado(s), {pending} pendiente(s)", - "invited_by": "Invitación de {name}", - "respond_below": "Responde con los botones de abajo", - "rsvp_label": "Tu respuesta", - "cancel_notification": "Se notificará a los participantes de la cancelación", - "you_organizer": "Eres el organizador", - "you_attendee": "Eres un participante", - "no_participants": "Sin participantes", - "count": "{count, plural, one {# participante} other {# participantes}}" - }, - "recurrence": { - "title": "Recurrencia", - "none": "No se repite", - "daily": "Diario", - "weekly": "Semanal", - "monthly": "Mensual", - "yearly": "Anual", - "every_n_days": "Cada {count} días", - "every_n_weeks": "Cada {count} semanas", - "every_n_months": "Cada {count} meses", - "until": "Hasta", - "occurrences": "{count} repeticiones", - "custom": "Personalizado…", - "edit_custom": "Editar repetición personalizada", - "every_n_years": "Cada {count} años", - "on_days": "los {days}", - "on_day_n": "el día {day}", - "on_the_nth": "el {nth} {day}", - "in_month": "en {month}", - "nth_1": "primer", - "nth_2": "segundo", - "nth_3": "tercer", - "nth_4": "cuarto", - "nth_last": "último", - "editor_freq_day": "Día", - "editor_freq_week": "Semana", - "editor_freq_month": "Mes", - "editor_freq_year": "Año", - "editor_repeats_on": "Se repite", - "editor_every": "Cada", - "editor_unit_days": "día(s)", - "editor_unit_weeks": "semana(s)", - "editor_unit_months": "mes(es)", - "editor_unit_years": "año(s)", - "editor_on_day": "el día", - "editor_on_the": "el", - "editor_in": "en", - "editor_ends": "Termina", - "editor_never": "Nunca", - "editor_ends_on": "El", - "editor_ends_after": "Después de", - "editor_occurrences": "repeticiones" - }, - "recurrence_scope": { - "edit_title": "Editar evento recurrente", - "delete_title": "Eliminar evento recurrente", - "description": "Este es un evento recurrente. ¿Qué eventos desea modificar?", - "this_event": "Solo este evento", - "this_and_future": "Este evento y los siguientes", - "all_events": "Todos los eventos", - "cancel": "Cancelar", - "save": "Guardar", - "delete": "Eliminar", - "rsvp_title": "Responder a evento recurrente", - "rsvp_description": "Este es un evento recurrente. ¿A qué eventos se aplica su respuesta?", - "respond": "Responder" - }, - "alerts": { - "title": "Recordatorio", - "none": "Sin recordatorio", - "at_time": "En el momento del evento", - "minutes_before": "{count, plural, one {# minuto antes} other {# minutos antes}}", - "hours_before": "{count, plural, one {# hora antes} other {# horas antes}}", - "days_before": "{count, plural, one {# día antes} other {# días antes}}", - "weeks_before": "{count, plural, one {# semana antes} other {# semanas antes}}", - "unit_minutes_before": "minutos antes", - "unit_hours_before": "horas antes", - "unit_days_before": "días antes", - "unit_weeks_before": "semanas antes", - "add": "Añadir recordatorio", - "remove": "Eliminar recordatorio", - "amount": "Cantidad del recordatorio", - "unit": "Unidad del recordatorio" - }, - "settings": { - "title": "Configuración del calendario", - "default_view": "Vista predeterminada", - "week_starts_on": "La semana comienza el", - "time_format": "Formato de hora", - "default_calendar": "Calendario predeterminado", - "default_reminder": "Recordatorio predeterminado", - "time_format_12h": "12 horas", - "time_format_24h": "24 horas", - "notifications_enabled": "Notificaciones de eventos", - "notifications_enabled_desc": "Mostrar alertas para eventos próximos", - "notification_sound": "Sonido de notificación", - "notification_sound_desc": "Reproducir un sonido para las alertas del calendario", - "invitation_parsing": "Analizar invitaciones por correo", - "invitation_parsing_desc": "Detectar invitaciones de calendario en archivos adjuntos del correo y mostrar acciones del calendario", - "show_time_in_month_view": "Mostrar hora en vista mensual", - "show_time_in_month_view_desc": "Mostrar las horas de los eventos en la vista mensual del calendario", - "show_week_numbers": "Mostrar números de semana", - "show_week_numbers_desc": "Mostrar los números de semana en el minicalendario", - "calendar_free_scroll": "Desplazamiento libre", - "calendar_free_scroll_desc": "Desplazarse de forma continua por meses, semanas y días en lugar de un periodo cada vez", - "enable_tasks": "Activar tareas", - "enable_tasks_desc": "Mostrar una vista de tareas en el calendario para gestionar tareas pendientes", - "show_tasks_on_calendar": "Mostrar tareas en el calendario", - "show_tasks_on_calendar_desc": "Mostrar etiquetas de tareas en las vistas diarias y semanales del calendario", - "hover_preview": "Vista previa al pasar el ratón", - "hover_preview_desc": "Mostrar un popover de detalles al pasar el ratón sobre eventos del calendario", - "hover_preview_instant": "Instantáneo", - "hover_preview_delay_500ms": "Retraso de 0,5 segundos", - "hover_preview_delay_1s": "Retraso de 1 segundo", - "hover_preview_delay_2s": "Retraso de 2 segundos", - "hover_preview_off": "Desactivado", - "organizer_identity": "Organizar invitaciones como", - "organizer_identity_desc": "La dirección de calendario usada como organizador al invitar a personas a eventos.", - "organizer_identity_failed": "No se pudo cambiar la dirección del organizador", - "show_birthday_calendar": "Calendario de cumpleaños", - "show_birthday_calendar_desc": "Mostrar un calendario virtual con los cumpleaños de tus contactos" - }, - "days": { - "monday": "Lunes", - "tuesday": "Martes", - "wednesday": "Miércoles", - "thursday": "Jueves", - "friday": "Viernes", - "saturday": "Sábado", - "sunday": "Domingo", - "mon": "Lun", - "tue": "Mar", - "wed": "Mié", - "thu": "Jue", - "fri": "Vie", - "sat": "Sáb", - "sun": "Dom" - }, - "notifications": { - "event_created": "Evento creado", - "event_updated": "Evento actualizado", - "event_deleted": "Evento eliminado", - "calendar_created": "Calendario creado", - "calendar_deleted": "Calendario eliminado", - "event_move_error": "Error al mover el evento", - "event_resize_error": "Error al redimensionar el evento", - "alert_title": "Evento próximo", - "alert_now": "Comienza ahora", - "alert_in_minutes": "En {count} min", - "invitation_sent": "Invitaciones enviadas", - "invitations_denied_title": "No se pueden enviar las invitaciones", - "invitations_denied": "El servidor rechazó enviar las invitaciones: {reason}. ¿Guardar el evento sin enviarlas?", - "save_without_invitations": "Guardar sin invitaciones", - "rsvp_updated": "Respuesta actualizada", - "rsvp_error": "Error al actualizar la respuesta", - "event_duplicated": "Evento duplicado", - "event_error": "Error al guardar el evento", - "task_due": "Tarea vencida", - "event_exported": "Evento exportado", - "title_copied": "Título copiado", - "link_copied": "Enlace copiado" - }, - "status": { - "loading_calendars": "Cargando calendarios...", - "loading_events": "Cargando eventos..." - }, - "quick_create": { - "placeholder": "Título del nuevo evento", - "aria_label": "Creación rápida de evento" - }, - "nav_prev": "Anterior", - "nav_next": "Siguiente", - "import": { - "title": "Importar calendario", - "tab_file": "Archivo", - "tab_url": "URL", - "select_file": "Seleccionar archivo .ics", - "drop_file": "o arrastra el archivo aquí", - "supported_formats": "Archivos iCalendar (.ics) compatibles", - "url_description": "Introduce la URL de un feed iCalendar (.ics) externo para importar eventos.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Compatible con URLs iCalendar (.ics)", - "fetch": "Obtener", - "invalid_url": "Introduce una URL válida", - "url_fetch_failed": "No se pudo obtener el calendario desde la URL", - "parsing": "Analizando archivo de calendario...", - "parsed_events": "{count} eventos encontrados", - "no_events": "No se encontraron eventos en el archivo", - "select_all": "Seleccionar todo", - "deselect_all": "Deseleccionar todo", - "target_calendar": "Importar al calendario", - "import_button": "Importar selección", - "importing": "Importando eventos...", - "success": "{count} eventos importados correctamente", - "error": "Error al importar el calendario", - "file_too_large": "El archivo supera el límite de 10 MB", - "invalid_format": "Formato de archivo de calendario no válido" - }, - "webcal_action": { - "title": "Abrir enlace de calendario", - "description": "¿Cómo quieres usar \"{name}\"?", - "import_title": "Importar una vez", - "import_description": "Obtén los eventos ahora y cópialos en uno de tus calendarios.", - "subscribe_title": "Suscribirse", - "subscribe_description": "Mantén este calendario sincronizado automáticamente como un calendario separado.", - "cancel": "Cancelar" - }, - "management": { - "title": "Gestión de calendarios", - "description": "Crea, renombra y personaliza tus calendarios. Haz clic derecho en un calendario en la barra lateral para cambiar su color rápidamente.", - "name": "Nombre", - "name_placeholder": "Nombre del calendario", - "color": "Color", - "change_color": "Cambiar color", - "random_color": "Nuevo color aleatorio", - "add_calendar": "Añadir calendario", - "edit": "Editar", - "delete": "Eliminar", - "save": "Guardar", - "create": "Crear", - "cancel": "Cancelar", - "default": "Predeterminado", - "confirm_delete": "¿Eliminar \"{name}\"? Se eliminarán todos los eventos de este calendario.", - "calendar_created": "Calendario creado", - "calendar_updated": "Calendario actualizado", - "calendar_deleted": "Calendario eliminado", - "color_updated": "Color del calendario actualizado", - "error_create": "Error al crear el calendario", - "error_update": "Error al actualizar el calendario", - "error_delete": "Error al eliminar el calendario", - "caldav_url": "URL de CalDAV", - "copy_url": "Copiar URL de CalDAV", - "url_copied": "URL de CalDAV copiada al portapapeles", - "confirm_clear": "¿Borrar todos los eventos de \"{name}\"? Esta acción no se puede deshacer.", - "set_default": "Establecer como predeterminado", - "default_updated": "Calendario predeterminado actualizado", - "error_default": "Error al establecer el calendario predeterminado", - "clear_events": "Borrar eventos", - "events_cleared": "{count} eventos borrados", - "error_clear": "No se pudieron borrar los eventos del calendario", - "share": "Compartir calendario", - "new_event_in_calendar": "Nuevo evento en este calendario", - "kind": "Contenido", - "kind_events": "Eventos", - "kind_tasks": "Tareas", - "kind_both": "Eventos y tareas", - "kind_hint": "Determina qué aplicaciones ven este calendario cuando se sincroniza por CalDAV. No se puede cambiar después." - }, - "subscription": { - "title": "Suscripción iCal", - "section_title": "Suscripciones iCal", - "description": "Suscríbete a un feed externo de iCalendar. Los eventos se sincronizarán automáticamente en su propio calendario. Compatible con URLs https:// y webcal://.", - "url_label": "URL del calendario", - "url_placeholder": "https://example.com/calendar.ics o webcal://...", - "name_label": "Nombre del calendario", - "name_placeholder": "p. ej., Días festivos", - "color_label": "Color", - "refresh_interval": "Intervalo de actualización", - "interval_15": "Cada 15 minutos", - "interval_30": "Cada 30 minutos", - "interval_60": "Cada hora", - "interval_360": "Cada 6 horas", - "interval_1440": "Cada día", - "subscribe": "Suscribirse", - "subscribing": "Suscribiendo...", - "save": "Guardar cambios", - "saving": "Guardando...", - "edit": "Editar", - "edit_title": "Editar suscripción", - "updated": "\"{name}\" actualizado", - "update_error": "No se pudo actualizar la suscripción", - "invalid_url": "Por favor, introduce una URL válida", - "success": "Suscrito a \"{name}\"", - "error": "No se pudo añadir la suscripción", - "refresh": "Actualizar ahora", - "refresh_success": "Suscripción actualizada", - "refresh_error": "No se pudo actualizar la suscripción", - "unsubscribe": "Cancelar suscripción", - "confirm_delete": "¿Cancelar la suscripción a \"{name}\"? El calendario y todos sus eventos serán eliminados.", - "deleted": "Suscripción eliminada", - "delete_error": "No se pudo eliminar la suscripción", - "last_refreshed": "Última actualización: {time}" - }, - "tasks": { - "label": "Tareas", - "no_tasks": "Sin tareas", - "no_title": "(Sin título)", - "mark_complete": "Marcar como completada", - "mark_incomplete": "Marcar como incompleta", - "filter_all": "Todas", - "filter_pending": "Pendientes", - "filter_completed": "Completadas", - "filter_overdue": "Vencidas", - "show_completed": "Mostrar completadas", - "create": "Nueva tarea", - "edit": "Editar tarea", - "title_placeholder": "Título de la tarea", - "description_placeholder": "Agregar una descripción...", - "due_date": "Fecha de vencimiento", - "include_time": "Incluir hora", - "priority": "Prioridad", - "priority_none": "Ninguna", - "priority_high": "Alta", - "priority_medium": "Media", - "priority_low": "Baja", - "progress": "Estado", - "progress_needs_action": "Requiere acción", - "progress_in_process": "En proceso", - "progress_completed": "Completada", - "progress_cancelled": "Cancelada", - "calendar": "Calendario", - "alert": "Recordatorio", - "alert_none": "Ninguno", - "alert_at_time": "En la fecha y hora de vencimiento", - "alert_5min": "5 minutos antes", - "alert_15min": "15 minutos antes", - "alert_30min": "30 minutos antes", - "alert_1hr": "1 hora antes", - "alert_1day": "1 día antes", - "delete": "Eliminar", - "cancel": "Cancelar", - "save": "Guardar", - "quick_add_placeholder": "Agregar una tarea...", - "due_today": "Vence hoy", - "due_tomorrow": "Vence mañana", - "overdue": "Vencida" - }, - "months": { - "jan": "Ene", - "feb": "Feb", - "mar": "Mar", - "apr": "Abr", - "may": "May", - "jun": "Jun", - "jul": "Jul", - "aug": "Ago", - "sep": "Sep", - "oct": "Oct", - "nov": "Nov", - "dec": "Dic", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Abrir menú" - }, - "advanced_search": { - "title": "Búsqueda avanzada", - "from": "De", - "from_placeholder": "Email o nombre del remitente", - "to": "Para", - "to_placeholder": "Email o nombre del destinatario", - "subject": "Asunto", - "subject_placeholder": "El asunto contiene...", - "body": "Cuerpo", - "body_placeholder": "El cuerpo contiene...", - "folder": "Carpeta", - "all_folders": "Todas las carpetas", - "has_attachment": "Adjuntos", - "date_after": "Después de", - "date_before": "Antes de", - "size_min": "Mayor que (KB)", - "size_max": "Menor que (KB)", - "starred": "Destacados", - "unread": "No leídos", - "read": "Leídos", - "yes": "Sí", - "no": "No", - "clear": "Limpiar", - "clear_all": "Limpiar todo", - "filters_active": "{count} filtro", - "filters_active_plural": "{count} filtros", - "toggle_filters": "Filtros", - "search_hint": "Usa los filtros avanzados para una búsqueda precisa", - "advanced_filters_tooltip": "Filtros de búsqueda avanzados", - "results_found": "{count, plural, =0 {No se encontraron resultados} one {# resultado encontrado} other {# resultados encontrados}}", - "results_found_more": "{count}+ resultados encontrados", - "suggestions_recent": "Búsquedas recientes", - "suggestions_people": "Personas", - "suggestions_from": "Buscar correos de {address}", - "suggestions_to": "Buscar correos para {address}", - "suggestions_remove_recent": "Quitar de las búsquedas recientes" - }, - "welcome": { - "title": "Bienvenido a tu buzón", - "tip_compose": "Pulsa c para redactar un nuevo correo", - "tip_shortcuts": "Pulsa ? para ver todos los atajos de teclado", - "tip_sidebar": "Encuentra Calendario, Contactos y Ajustes en el menú lateral", - "tip_settings": "Personaliza tu experiencia en Ajustes", - "got_it": "Entendido", - "settings": "Ajustes", - "dismiss": "Cerrar", - "start_tour": "Iniciar tour" - }, - "demo_welcome": { - "title": "Bienvenido a Bulwark Mail", - "description": "Explora un cliente de correo web completo - directamente en tu navegador. Todos los datos quedan en tu dispositivo, así que prueba todo.", - "feature_email": "Leer y redactar correos", - "feature_organize": "Etiquetas, estrellas y carpetas", - "feature_shortcuts": "Atajos de teclado", - "feature_privacy": "Demo 100 % privada", - "hint": "Haz clic en un correo a la izquierda para empezar, o inicia el tour." - }, - "files": { - "office_edit": "Editar documento", - "office_loading": "Abriendo el editor de documentos…", - "office_error": "No se pudo abrir el editor de documentos.", - "office_close": "Cerrar editor", - "open_in_new_tab": "Abrir en una pestaña nueva", - "title": "Archivos", - "search_placeholder": "Buscar archivos...", - "empty_state_title": "Aún no hay archivos", - "empty_state_description": "Suba archivos o cree carpetas para comenzar", - "upload": "Subir", - "upload_files": "Subir archivos", - "new_folder": "Nueva carpeta", - "new_folder_name": "Nombre de la carpeta", - "rename": "Renombrar", - "rename_title": "Renombrar", - "new_name": "Nuevo nombre", - "name_forbidden_chars": "El servidor no permite estos caracteres en los nombres: {chars}", - "name_reserved": "Este nombre está reservado en el servidor.", - "delete": "Eliminar", - "delete_confirm_title": "Eliminar recurso", - "delete_confirm_message": "¿Está seguro de que desea eliminar \"{name}\"? Esta acción no se puede deshacer.", - "download": "Descargar", - "name": "Nombre", - "size": "Tamaño", - "modified": "Modificado", - "type": "Tipo", - "folder": "Carpeta", - "file": "Archivo", - "parent_directory": "Directorio superior", - "breadcrumb_root": "Inicio", - "drop_files_here": "Suelte archivos o carpetas aquí para subirlos", - "uploading": "Subiendo...", - "upload_success": "{count, plural, one {1 archivo subido} other {# archivos subidos}}", - "upload_error": "Error al subir el archivo", - "create_folder_success": "Carpeta creada", - "create_folder_error": "Error al crear la carpeta", - "delete_success": "Eliminado correctamente", - "delete_error": "Error al eliminar", - "rename_success": "Renombrado correctamente", - "rename_error": "Error al renombrar", - "download_error": "Error al descargar", - "not_available": "El almacenamiento de archivos no está disponible en este servidor", - "cancel": "Cancelar", - "create": "Crear", - "save": "Guardar", - "no_results": "Ningún archivo coincide con su búsqueda", - "batch_delete_confirm_message": "¿Está seguro de que desea eliminar {count, plural, one {1 elemento} other {# elementos}}? Esta acción no se puede deshacer.", - "batch_delete_success": "{count, plural, one {1 elemento eliminado} other {# elementos eliminados}}", - "grid_view": "Vista de cuadrícula", - "list_view": "Vista de lista", - "details": "Detalles", - "path": "Ruta", - "preview": "Vista previa", - "preview_error": "Error al cargar la vista previa", - "cut": "Cortar", - "copy": "Copiar", - "paste": "Pegar", - "move_success": "{count, plural, one {1 elemento movido} other {# elementos movidos}}", - "move_error": "Error al mover", - "paste_success": "Pegado correctamente", - "paste_error": "Error al pegar", - "new_text_file": "Nuevo archivo de texto", - "file_name": "Nombre del archivo", - "retry": "Reintentar", - "refresh": "Actualizar", - "toggle_favorite": "Alternar favorito", - "duplicate": "Duplicar", - "duplicate_success": "Duplicado correctamente", - "duplicate_error": "Error al duplicar", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Archivo creado", - "create_file_error": "Error al crear el archivo", - "favorites": "Favoritos", - "recent": "Recientes", - "properties": "Propiedades", - "open_folder": "Abrir carpeta", - "upload_folder": "Subir carpeta", - "file_too_large": "\"{name}\" excede el tamaño máximo de archivo ({max})", - "undo": "Deshacer", - "undo_success": "Acción deshecha", - "undo_error": "Error al deshacer", - "toolbar": "Acciones de archivo", - "file_list": "Archivos y carpetas", - "context_menu": "Acciones", - "settings_title": "Configuración de archivos", - "settings_display": "Visualización", - "settings_default_view": "Vista predeterminada", - "settings_default_view_desc": "Elige entre diseño de cuadrícula y lista", - "settings_default_sort": "Orden predeterminado", - "settings_default_sort_desc": "Elige el orden predeterminado para los archivos", - "settings_sort_direction": "Dirección de orden", - "settings_sort_direction_desc": "Elige orden ascendente o descendente", - "settings_ascending": "Ascendente", - "settings_descending": "Descendente", - "settings_icons": "Iconos", - "settings_show_icons": "Mostrar iconos de archivos", - "settings_show_icons_desc": "Mostrar iconos junto a archivos y carpetas", - "settings_colored_icons": "Iconos de colores", - "settings_colored_icons_desc": "Usar iconos de colores en lugar de monocromáticos", - "settings_show_thumbnails": "Mostrar miniaturas", - "settings_show_thumbnails_desc": "Mostrar vistas previas de imágenes en lugar de iconos", - "settings_behavior": "Comportamiento", - "settings_show_hidden": "Mostrar archivos ocultos", - "settings_show_hidden_desc": "Mostrar archivos y carpetas que comienzan con un punto", - "settings_folder_layout": "Navegación de carpetas", - "settings_folder_layout_desc": "Elija cómo se muestran las carpetas: integradas con los archivos o en un árbol en la barra lateral", - "settings_folder_layout_inline": "Integrado", - "settings_folder_layout_sidebar": "Barra lateral", - "disabled_title": "La función de archivos ha sido desactivada por su administrador", - "disabled_description": "Las cargas de archivos grandes a través de WebDAV pueden causar inestabilidad en Stalwart/RocksDB, incluyendo errores de memoria y uso irrecuperable del disco. Los archivos eliminados pueden no eliminarse inmediatamente del almacenamiento. Esta función no se recomienda para entornos de producción.", - "stability_warning": "Las cargas de archivos grandes pueden causar inestabilidad del servidor. Los archivos eliminados pueden no eliminarse inmediatamente del almacenamiento. Usar con precaución.", - "no_accounts": "No hay cuentas conectadas.", - "open_folder_tree": "Abrir árbol de carpetas", - "other_accounts": "Otras cuentas", - "migration_title": "Actualizando tus archivos…", - "migration_description": "Organizando carpetas y archivos en su estructura adecuada. Esto solo ocurre una vez." - }, - "smime": { - "your_certificates": "Tus certificados", - "your_certificates_desc": "Importa y administra tus certificados S/MIME para firmar y cifrar correos", - "recipient_certificates": "Certificados de destinatarios", - "recipient_certificates_desc": "Certificados públicos para cifrar correos a destinatarios", - "identity_bindings": "Vinculaciones de identidad y clave", - "identity_bindings_desc": "Vincula certificados S/MIME a tus identidades de correo", - "defaults_title": "Valores predeterminados", - "defaults_desc": "Configura el comportamiento predeterminado de firma y cifrado", - "import_pkcs12": "Importar PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importar certificado", - "no_certificates": "Aún no se han importado certificados", - "no_recipient_certs": "No hay certificados de destinatarios", - "expires": "Caduca", - "expired": "Caducado", - "bound_to": "Vinculado a", - "no_key_bound": "Ninguno", - "lock": "Bloquear clave", - "unlock": "Desbloquear clave", - "details": "Ver detalles", - "delete": "Eliminar", - "encrypt_by_default": "Cifrar por defecto", - "encrypt_by_default_desc": "Cifrar correos automáticamente cuando todos los destinatarios tengan certificados", - "remember_unlocked": "Recordar claves desbloqueadas", - "remember_unlocked_desc": "Mantener las claves desbloqueadas durante esta sesión del navegador", - "sign_default_for": "Firmar por defecto para", - "enter_p12_passphrase": "Introducir la contraseña de PKCS#12", - "p12_passphrase_desc": "Introduce la contraseña que protege este archivo de certificado", - "enter_storage_passphrase": "Definir contraseña de almacenamiento", - "storage_passphrase_desc": "Elige una contraseña para proteger esta clave en tu navegador", - "next": "Siguiente", - "import": "Importar", - "unlock_key": "Desbloquear clave", - "unlock_key_desc": "Introduce la contraseña de almacenamiento para desbloquear esta clave para firmar o descifrar", - "passphrase_placeholder": "Introduce la contraseña", - "confirm_passphrase_placeholder": "Confirma la contraseña", - "passphrase_mismatch": "Las contraseñas no coinciden", - "cancel": "Cancelar", - "processing": "Procesando…", - "close": "Cerrar", - "certificate_details": "Detalles del certificado", - "cert_subject": "Asunto", - "cert_issuer": "Emisor", - "cert_email": "Correo electrónico", - "cert_serial": "Número de serie", - "cert_validity": "Validez", - "cert_fingerprint": "Huella digital (SHA-256)", - "cert_algorithm": "Algoritmo", - "cert_capabilities": "Capacidades", - "cert_source": "Origen", - "cert_expired": "Este certificado ha caducado", - "cert_not_yet_valid": "Este certificado aún no es válido", - "cap_sign": "Firma", - "cap_encrypt": "Cifrado", - "cap_none": "Ninguno", - "show_passphrase": "Mostrar contraseña", - "hide_passphrase": "Ocultar contraseña", - "sign_toggle": "Firmar", - "encrypt_toggle": "Cifrar", - "missing_recipient_certs": "Faltan certificados para: {emails}", - "missing_sender_cert": "No hay un certificado vinculado a esta identidad", - "status_encrypted_ok": "Este mensaje fue cifrado", - "status_encrypted_no_key": "Este mensaje está cifrado, pero no se encontró una clave coincidente", - "status_encrypted_failed": "No se pudo descifrar este mensaje", - "status_signed_valid": "Firma verificada", - "status_signed_invalid": "La verificación de la firma falló", - "status_signed_expired_cert": "Firmado con un certificado caducado", - "status_signed_self_signed": "Firma válida, pero el certificado es autofirmado (no confiable)", - "status_signed_mismatch": "La firma es válida, pero el firmante no coincide con el remitente", - "status_unsupported": "Formato S/MIME no compatible", - "auto_import_signer_certs": "Importar automáticamente certificados de firmantes", - "auto_import_signer_certs_desc": "Guardar automáticamente certificados de correos firmados verificados para futuros cifrados", - "export": "Exportar", - "enter_export_passphrase": "Definir contraseña de exportación", - "export_passphrase_desc": "Elige una contraseña para proteger el archivo PKCS#12 exportado", - "export_storage_desc": "Introduce la contraseña de almacenamiento para descifrar la clave antes de exportarla", - "incorrect_passphrase": "Contraseña incorrecta" - }, - "tour": { - "step_counter": "Paso {current} de {total}", - "skip": "Saltar tour", - "back": "Atrás", - "next": "Siguiente", - "finish": "Finalizar", - "take_a_tour": "Haz un recorrido por la interfaz", - "restart_title": "Tour introductorio", - "restart_desc": "Repetir el recorrido guiado por la interfaz", - "restart_button": "Reiniciar tour", - "show_on_new_devices_title": "Mostrar en dispositivos nuevos", - "show_on_new_devices_desc": "Vuelve a mostrar el banner de bienvenida y el tour la primera vez que inicies sesión en un dispositivo nuevo, incluso si ya los completaste en otro lugar", - "sidebar_title": "Tus buzones", - "sidebar_desc": "Esta es tu barra lateral de carpetas. Haz clic en cualquier buzón para ver sus correos. Puedes crear carpetas, arrastrar correos entre ellas y ver los contadores de no leídos.", - "compose_title": "Redactar un correo", - "compose_desc": "Haz clic aquí para escribir un nuevo correo. Puedes añadir destinatarios, archivos adjuntos y formato de texto enriquecido.", - "search_title": "Buscar en tu correo", - "search_desc": "Busca por remitente, asunto o contenido. Haz clic en el icono de filtro para opciones avanzadas como rango de fechas, adjuntos y mensajes destacados.", - "email_list_title": "Tu lista de correos", - "email_list_desc": "Los correos aparecen aquí. Haz clic en uno para leerlo a la derecha. Usa la casilla para seleccionar varios y moverlos, eliminarlos o etiquetarlos.", - "email_viewer_title": "Panel de lectura", - "email_viewer_desc": "El correo seleccionado se abre aquí. Responde, reenvía, archiva o elimina con los botones de la barra. También puedes destacar correos o añadir etiquetas de color.", - "keywords_title": "Etiquetas de color", - "keywords_desc": "Organiza tu correo con etiquetas de colores. Arrastra un correo sobre una etiqueta o haz clic derecho para asignarlas.", - "calendar_title": "Calendario", - "calendar_desc": "Cambia al calendario para gestionar tus eventos. Crea eventos, configura recordatorios y elige diferentes vistas.", - "contacts_title": "Contactos", - "contacts_desc": "Tu libreta de direcciones está aquí. Importa contactos, crea grupos y consulta los detalles.", - "settings_title": "Ajustes", - "settings_desc": "Personaliza todo: tema, densidad, firmas, filtros, atajos de teclado, valores predeterminados del calendario y más.", - "shortcuts_title": "Atajos de teclado", - "shortcuts_desc": "Para usuarios avanzados. Pulsa ? en cualquier momento para ver todos los atajos disponibles.", - "compose_open_title": "El compositor", - "compose_open_desc": "Este es el compositor de correo. Añade destinatarios, escribe tu mensaje, adjunta archivos y usa formato de texto enriquecido. También puedes guardar borradores y usar plantillas.", - "calendar_view_title": "Tu calendario", - "calendar_view_desc": "Aquí está tu calendario con eventos de ejemplo. Cambia entre vistas de día, semana, mes y agenda.", - "create_event_title": "Crear un evento", - "create_event_desc": "Haz clic en este botón para crear un nuevo evento de calendario. Puedes establecer un título, fecha, hora y añadir participantes.", - "event_modal_title": "Detalles del evento", - "event_modal_desc": "Aquí está el formulario del evento. Rellena el título, elige una fecha y hora, añade una ubicación o participantes. Pulsa guardar cuando hayas terminado - o ciérralo y continúa.", - "contacts_list_title": "Tus contactos", - "contacts_list_desc": "Aquí están tus contactos. Haz clic en cualquier contacto para ver sus detalles. Puedes crear contactos nuevos o importar vCards.", - "files_title": "Almacenamiento de archivos", - "settings_tabs_title": "Menú de ajustes", - "settings_tabs_desc": "Aquí están todas las categorías de ajustes. Personaliza la apariencia, gestiona identidades, configura filtros de correo, ajusta tu calendario y mucho más.", - "files_desc": "Tu explorador de archivos para subir, organizar y compartir archivos.", - "demo_banner_title": "Controles de demo", - "demo_banner_desc": "Estás en modo demo - todo permanece en tu navegador. Haz clic en 'Restablecer demo' en cualquier momento.", - "quota_title": "Uso de almacenamiento", - "quota_desc": "Controla el tamaño de tu buzón aquí. El círculo se llena a medida que usas más espacio." - }, - "unified_mailbox": { - "search_unavailable": "La búsqueda no está disponible en la vista unificada" - }, - "calendar_event_notifications": { - "someone": "Alguien", - "invited": "{name} te ha invitado a \"{title}\"", - "updated": "{name} ha actualizado \"{title}\"", - "cancelled": "{name} ha cancelado \"{title}\"", - "untitled": "Evento sin título" - }, - "share_notifications": { - "someone": "Alguien", - "shared": "{name} ha compartido contigo {kind} \"{object}\"", - "changed": "{name} ha cambiado tu acceso a {kind} \"{object}\"", - "revoked": "{name} ha retirado tu acceso a {kind} \"{object}\"", - "object": { - "folder": "la carpeta", - "calendar": "el calendario", - "address_book": "la libreta de direcciones", - "files": "la carpeta de archivos" - } - }, - "sharing": { - "title": "Compartir «{name}»", - "description": "Concede acceso a otros usuarios o grupos de este servidor. Los cambios surten efecto inmediatamente.", - "no_shares": "Aún no se ha compartido con nadie.", - "add_person": "Añadir persona o grupo", - "search_placeholder": "Buscar por nombre o correo…", - "loading_principals": "Cargando usuarios…", - "no_principals": "No se han encontrado otros usuarios ni grupos.", - "no_match": "Sin resultados.", - "remove": "Quitar acceso", - "group": "Grupo", - "share_added": "Acceso concedido", - "share_updated": "Acceso actualizado", - "share_removed": "Acceso retirado", - "share_failed": "No se pudo actualizar el uso compartido", - "preset": { - "freeBusy": "Solo disponibilidad", - "read": "Solo lectura", - "readWrite": "Lectura y escritura", - "manager": "Administrador", - "custom": "Personalizado" - } - }, - "quote_header": { - "reply_line": "El {date}, {from} escribió:", - "forwarded_separator": "---------- Mensaje reenviado ----------", - "from_label": "De", - "date_label": "Fecha", - "subject_label": "Asunto" - }, - "pwa_install": { - "title": "Instalar {appName}", - "description": "Instala nuestra app para un acceso rápido y soporte sin conexión.", - "not_now": "Ahora no", - "install": "Instalar", - "dont_remind": "No volver a recordármelo", - "dismiss_aria": "Cerrar aviso de instalación" - }, - "deep_link": { - "message_not_found": "Este mensaje ya no está disponible.", - "thread_not_found": "Esta conversación ya no está disponible.", - "folder_not_found": "Esta carpeta ya no está disponible.", - "event_not_found": "Este evento ya no está disponible.", - "file_not_found": "Este archivo ya no está disponible.", - "account_unavailable": "Inicia sesión en esa cuenta para abrir este enlace.", - "copy_message": "Copiar enlace del mensaje", - "copy_conversation": "Copiar enlace de la conversación", - "copy_event": "Copiar enlace del evento", - "copied": "Enlace copiado", - "copy_failed": "No se pudo copiar el enlace" - }, - "plugins": { - "oauth_callback": { - "completing": "Completando la autorización…", - "completing_message": "Por favor, espera mientras se establece la conexión.", - "received_title": "Autorización recibida", - "received_message": "Puedes volver a la pestaña donde iniciaste la conexión.", - "failed_title": "Error de autorización", - "denied": "Se denegó la autorización.", - "failed_detail": "Error de autorización: {error}", - "missing_params": "Falta el código de autorización o el estado.", - "delivery_failed": "No se pudo entregar el resultado de la autorización a la pestaña de la aplicación. Cierre esta ventana e inténtelo de nuevo." - } - }, - "global_search": { - "title": "Buscar en todo", - "placeholder": "Buscar correo, contactos, calendario, archivos...", - "scope_label": "Buscar en", - "account_label": "Cuenta", - "scope_all": "Todo", - "scope_mail": "Correo", - "scope_contacts": "Contactos", - "scope_calendar": "Calendario", - "scope_files": "Archivos", - "all_accounts": "Todas las cuentas", - "show_all": "Mostrar todos los resultados", - "no_results": "Sin resultados para “{query}”", - "server_hint": "Las palabras completas buscan en el servidor; las parciales coinciden con lo ya cargado", - "searching": "Buscando...", - "account_error": "No se pudo buscar en {account}", - "recent_searches": "Búsquedas recientes", - "recurring": "Periódico", - "no_subject": "(no subject)", - "empty_hint": "Busca correo, contactos, eventos y archivos en todas las cuentas. Prueba from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Plano por fecha", - "grouped": "Agrupado", - "load_more": "Cargar más", - "preview_empty": "Selecciona un resultado para previsualizarlo", - "open_full": "Abrir", - "view_label": "Vista" - } -} +{ + "meta_description": "Cliente de correo web minimalista que usa el protocolo JMAP", + "login": { + "title": "Correo Web", + "username_label": "Correo electrónico", + "username_placeholder": "usuario@ejemplo.com", + "password_label": "Contraseña", + "password_placeholder": "Ingrese su contraseña", + "jmap_endpoint_label": "Servidor JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "El servidor debe permitir solicitudes CORS desde este dominio.", + "jmap_server_label": "Servidor", + "jmap_server_auto_picked": "Servidor seleccionado a partir del dominio de tu correo.", + "sign_in": "Iniciar sesión", + "signing_in": "Iniciando sesión...", + "loading": "Cargando...", + "reconnecting": "Conexión perdida. Intentando reconectar…", + "error": { + "invalid_credentials": "Correo electrónico o contraseña inválidos", + "invalid_token": "El servidor no aceptó este token de acceso. Comprueba que esté completo y que no haya caducado ni se haya revocado.", + "connection_failed": "No se pudo conectar con el servidor", + "cors_blocked": "El servidor es accesible pero está bloqueando las solicitudes de origen cruzado. Verifique la configuración CORS de su servidor JMAP y permita este dominio.", + "server_error": "El servidor no está disponible temporalmente. Inténtalo más tarde.", + "generic": "Ocurrió un error. Por favor, inténtelo de nuevo.", + "totp_required": "Se requiere un código de autenticación de dos factores. Introduce tu código a continuación.", + "totp_invalid": "Código de autenticación inválido. Verifica tu aplicación de autenticación.", + "oauth_discovery_failed": "SSO está habilitado pero no se pudo contactar al proveedor de identidad. Verifica tu configuración OAuth.", + "token_exchange_failed": "Tu contraseña y tu código fueron aceptados, pero el servidor de correo se negó a iniciar una sesión para esta aplicación. Pide a tu administrador que revise la configuración de su cliente OAuth." + }, + "show_password": "Mostrar contraseña", + "hide_password": "Ocultar contraseña", + "totp_toggle": "Tengo un código 2FA", + "token_toggle": "Iniciar sesión con un token de acceso", + "password_toggle": "Iniciar sesión con una contraseña", + "token_label": "Token de acceso", + "token_placeholder": "Pega tu token de API", + "token_hint": "Crea un token de API en la configuración de tu proveedor de correo y pégalo aquí.", + "remember_me": "Recordarme", + "config_error": { + "title": "Error de Configuración", + "fetch_failed": "No se pudo cargar la configuración de la aplicación. Por favor, inténtelo más tarde.", + "server_not_configured": "El servidor de correo no ha sido configurado. Por favor, contacte a su administrador." + }, + "remove_from_history": "Eliminar del historial", + "totp_label": "Código de autenticación", + "totp_placeholder": "000000", + "session_expired": "Tu sesión ha expirado. Inicia sesión de nuevo.", + "dismiss": "Cerrar", + "or": "o", + "sign_in_sso": "Iniciar sesión con SSO", + "add_account_title": "Agregar cuenta", + "add_account_subtitle": "Iniciar sesión con otra cuenta", + "cancel": "Cancelar", + "website": "Sitio web", + "imprint": "Aviso legal", + "privacy_policy": "Política de privacidad", + "try_demo": "Probar demo", + "demo_description": "Explora con datos de ejemplo - sin cuenta necesaria", + "demo_launching": "Iniciando demo...", + "demo_login_button": "Iniciar demo", + "demo_tagline": "Experimenta un cliente de correo completo. Sin necesidad de cuenta.", + "demo_no_signup": "Sin registro - explora libremente con datos de ejemplo", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Completando inicio de sesión...", + "oauth_error": { + "title": "Error de autenticación", + "invalid_state": "La validación de seguridad falló. Intenta iniciar sesión de nuevo.", + "missing_params": "Faltan datos de autorización. Intenta iniciar sesión de nuevo.", + "token_exchange_failed": "No se pudo completar la autenticación. Inténtalo de nuevo.", + "access_denied": "Acceso denegado. Contacta a tu administrador.", + "back_to_login": "Volver al inicio de sesión" + } + }, + "sidebar": { + "close": "Cerrar", + "compose": "Redactar", + "compose_hint": "Redactar (c)", + "search_placeholder": "Buscar correo...", + "search_placeholder_hint": "Buscar correo... (pulsa /)", + "storage": "Almacenamiento", + "storage_used": "Usado", + "storage_free": "Libre", + "storage_total": "Total", + "sign_out": "Cerrar sesión", + "sign_out_of": "Cerrar sesión de {account}", + "sign_out_all": "Cerrar sesión de todas las cuentas", + "add_account": "Agregar cuenta", + "set_as_default": "Establecer como predeterminada", + "switch_account": "Cambiar cuenta", + "contacts": "Contactos", + "calendar": "Calendario", + "settings": "Configuración", + "admin": "Admin", + "files": "Archivos", + "loading_mailboxes": "Cargando buzones...", + "push_connected": "Actualizaciones en tiempo real activas", + "push_disconnected": "Actualizaciones en tiempo real inactivas", + "keyboard_shortcuts": "Atajos de teclado", + "theme": { + "light": "Modo claro", + "dark": "Modo oscuro", + "system": "Tema del sistema" + }, + "language": { + "title": "Idioma" + }, + "mailboxes": { + "inbox": "Bandeja de entrada", + "sent": "Enviados", + "drafts": "Borradores", + "trash": "Papelera", + "archive": "Archivo", + "starred": "Destacados", + "all_mail": "Todo el correo", + "spam": "Spam", + "important": "Importante" + }, + "unified_inbox": "Bandeja unificada", + "unified_sent": "Todos los enviados", + "unified_drafts": "Todos los borradores", + "unified_trash": "Todas las papeleras", + "unified_archive": "Todos los archivos", + "unified_junk": "Todo el spam", + "all_accounts": "Todas las cuentas", + "all_accounts_loading": "Cargando cuentas: {loaded} de {total}", + "unified_mailbox": "Buzón unificado", + "expand": "Expandir", + "collapse": "Contraer", + "expand_tooltip": "Expandir", + "collapse_tooltip": "Contraer", + "mobile": { + "search": "Buscar", + "compose": "Redactar", + "go_back": "Volver", + "toggle_menu": "Alternar menú" + }, + "clear_search": "Limpiar búsqueda", + "vacation_active": "Respuesta automática activa", + "demo_banner": "Modo demo", + "demo_reset": "Restablecer", + "demo_tour": "Tour", + "tags": "Etiquetas", + "show_all_tags": "Mostrar todo ({count})", + "show_fewer_tags": "Mostrar menos", + "folders": "Carpetas", + "your_folders": "Tus carpetas", + "create_folder": "Crear carpeta", + "show_more": "Más", + "show_fewer": "Menos", + "mail": "Correo", + "nav_label": "Navegación", + "add_app": "Apps", + "shared": "Compartido", + "shared_account_hidden": "Oculta en la barra lateral. Vuelve a mostrarla en Configuración › Diseño.", + "notifications_muted": "Notificaciones silenciadas", + "scheduled": "Programados", + "unified_all_unread": "Todo no leído", + "unified_all_starred": "Todo destacado", + "unified_all_mail": "Todo el correo", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Etiquetas", + "create_label": "Crear etiqueta", + "mv_tasks": "Tareas", + "mv_side_panel": "Panel lateral", + "mv_open_full": "Abrir en vista completa", + "mv_previous_day": "Día anterior", + "mv_next_day": "Día siguiente", + "mv_add_another_account": "Añadir otra cuenta", + "mv_manage_account": "Gestionar tu cuenta", + "mv_storage_used": "{percent}% de {total} usado" + }, + "protocol_handlers": { + "title": "Aplicaciones predeterminadas", + "description": "Elige si los enlaces de correo y calendario se abren en Bulwark. Técnicamente, Bulwark se registra como controlador de protocolo para enlaces mailto: y webcal:.", + "unsupported": "Este navegador o esta conexión no admite el registro manual de controladores de protocolo. Es posible que aún puedas usar la PWA instalada desde la configuración del navegador o del sistema.", + "mailto_label": "Enlaces de correo", + "mailto_description": "Abre enlaces mailto: en Bulwark con el redactor rellenado previamente.", + "protocol_open_mode_label": "Al abrir enlaces de protocolo", + "protocol_open_mode_description": "Elige si Bulwark abre los enlaces mailto: y webcal: en una nueva pestaña o reutiliza una sesión abierta. La opción de sesión activa necesita permiso de notificaciones para que puedas hacer clic en una notificación de respaldo y traer Bulwark al frente si el navegador bloquea el foco.", + "protocol_open_mode_active_session": "Abrir en la sesión activa si es posible", + "protocol_open_mode_new_tab": "Abrir siempre una nueva pestaña", + "focus_notification_title": "Abrir Bulwark", + "focus_notification_body": "El enlace se abrió en Bulwark. Haz clic para traer la ventana al frente.", + "webcal_label": "Enlaces de calendario", + "webcal_description": "Abre enlaces webcal: en Bulwark con un diálogo de suscripción al calendario rellenado previamente.", + "register_mailto": "Registrar aplicación de correo", + "register_webcal": "Registrar aplicación de calendario", + "mailto_registered": "Registro del controlador de correo solicitado", + "webcal_registered": "Registro del controlador de calendario solicitado", + "registration_failed": "No se pudo registrar el controlador de protocolo", + "opening_mailto": "Abriendo redactor...", + "opening_webcal": "Abriendo calendario...", + "browser_note": "Tu navegador o sistema operativo puede pedirte confirmación y puede requerir que Bulwark esté instalado antes de poder seleccionarlo como aplicación predeterminada.", + "select_account_title": "Elegir cuenta", + "select_mailto_account": "Elige qué cuenta debe abrir este enlace de correo.", + "select_webcal_account": "Elige qué cuenta debe abrir este enlace de calendario.", + "select_account_note": "Esta selección solo se aplica a este enlace de protocolo.", + "detail_to": "Para", + "detail_subject": "Asunto", + "detail_no_subject": "Sin asunto", + "detail_calendar": "Calendario", + "detail_source": "Fuente", + "active_account": "Activa", + "switching_account": "Cambiando de cuenta..." + }, + "sidebar_apps": { + "modal_title": "Aplicaciones de la barra lateral", + "add_new": "Añadir aplicación", + "edit_app": "Editar aplicación", + "name_label": "Nombre", + "name_placeholder": "Mi aplicación", + "name_required": "El nombre es obligatorio", + "url_label": "URL", + "url_required": "La URL es obligatoria", + "url_invalid": "Introduce una URL http o https válida", + "icon_label": "Icono", + "icon_required": "El icono es obligatorio", + "open_mode_label": "Modo de apertura", + "open_new_tab": "Nueva pestaña", + "open_inline": "Integrado", + "cancel": "Cancelar", + "add": "Añadir", + "update": "Actualizar", + "delete": "Eliminar", + "delete_confirm_title": "Eliminar aplicación", + "delete_confirm": "¿Estás seguro de que quieres eliminar \"{name}\"?", + "no_apps": "No se han añadido aplicaciones", + "no_apps_hint": "Añade aplicaciones y enlaces personalizados a tu barra lateral", + "managed_title": "Proporcionadas por tu administrador", + "managed_description": "Estas aplicaciones están configuradas para todos y no se pueden cambiar.", + "managed_badge": "Gestionada", + "search_icons": "Buscar iconos...", + "show_popular": "Populares", + "show_all": "Todos", + "no_icons_found": "No se encontraron iconos", + "inline_badge": "Integrado", + "tab_badge": "Pestaña", + "show_on_mobile": "Mostrar en móvil" + }, + "email_list": { + "columns": { + "from": "De", + "to": "Para", + "subject": "Asunto", + "date": "Fecha", + "size": "Tamaño" + }, + "no_emails": "No se encontraron mensajes", + "no_emails_description": "Este buzón está vacío", + "no_search_results": "No se encontraron resultados", + "no_search_results_description": "Intente ajustar su búsqueda o filtros", + "loading": "Cargando correos...", + "unread": "no leído", + "to_me": "Para mí", + "to_recipients": "Para {count} destinatarios", + "and_others": "y {count} más", + "draft": "Borrador", + "starred": "Destacado", + "pinned": "Fijado", + "replied": "Respondido", + "forwarded": "Reenviado", + "has_attachment": "Tiene adjunto", + "conversations_range": "{from}–{to, number} de {total, number}", + "conversations_count": "{count} de {total} conversaciones", + "conversations_count_plus": "{count}+ conversaciones", + "conversations_count_simple": "{count} conversaciones", + "no_conversations": "Sin conversaciones", + "loading_more": "Cargando más correos...", + "no_more_emails": "No hay más correos para cargar", + "batch_actions": { + "select": "Seleccionar correos", + "select_all": "Seleccionar todo", + "selected_messages": "{count, plural, one {1 correo electrónico seleccionado} other {# correos electrónicos seleccionados}}", + "mark_read": "Marcar como leído", + "mark_unread": "Marcar como no leído", + "delete": "Eliminar", + "delete_confirm_title": "Eliminar correos", + "delete_confirm_message": "¿Estás seguro de que quieres eliminar {count, plural, one {1 correo} other {# correos}}?", + "clear_selection": "Limpiar selección" + }, + "permanent_delete": "Eliminar permanentemente", + "permanent_delete_confirm_title": "Eliminar permanentemente", + "permanent_delete_confirm_message": "Este correo se eliminará permanentemente. Esta acción no se puede deshacer.", + "permanent_delete_confirm_batch_message": "Estos {count, plural, one {1 correo} other {# correos}} se eliminarán permanentemente. Esta acción no se puede deshacer.", + "empty_folder": { + "button": "Vaciar carpeta", + "confirm_title": "Vaciar carpeta", + "confirm_message": "Todos los correos de esta carpeta se eliminarán permanentemente. Esta acción no se puede deshacer.", + "confirm_message_trash": "Todos los correos de esta carpeta se moverán a la papelera.", + "confirm_button": "Vaciar carpeta", + "junk_hint": "Puede vaciar la carpeta de Spam para eliminar permanentemente todos los mensajes.", + "trash_hint": "Puede vaciar la Papelera para eliminar permanentemente todos los mensajes." + }, + "no_scheduled_emails": "No hay correos programados", + "no_scheduled_emails_description": "Los mensajes programados para más tarde aparecerán aquí.", + "scheduled_count": "{count, plural, one {1 correo programado} other {# correos programados}}", + "scheduled_actions_hint": "Los mensajes programados se pueden cancelar, reprogramar o editar desde sus acciones.", + "cancel_scheduled_send": "Cancelar envío", + "reschedule_send": "Reprogramar", + "cancel_and_edit": "Cancelar y editar", + "cancel_and_compose_again": "Cancelar y redactar de nuevo", + "reschedule_prompt": "Introduce una nueva fecha/hora, p. ej. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "El remitente solicita una confirmación de lectura:", + "send": "Enviar confirmación", + "ignore": "Ignorar", + "sent": "Confirmación de lectura enviada.", + "send_failed": "No se pudo enviar la confirmación de lectura", + "mdn_subject": "Leído: {subject}", + "mdn_body": "Este es un acuse de recibo del mensaje que enviaste a {recipient}.\n\nNota: Este acuse solo confirma que el mensaje se mostró en el ordenador del destinatario. No garantiza que el destinatario haya leído o entendido el contenido." + }, + "no_email_selected": "Ningún correo seleccionado", + "no_email_description": "Seleccione un correo de la lista para verlo aquí", + "no_conversation_selected": "Ninguna conversación seleccionada", + "no_conversation_description": "Elija una conversación de la lista para leerla aquí", + "compose": "Redactar", + "compose_hint": "Redactar nuevo mensaje", + "no_subject": "(Sin Asunto)", + "no_body_content": "(Sin contenido disponible)", + "no_preview_available": "Vista previa no disponible", + "show_quoted_text": "Mostrar texto citado", + "hide_quoted_text": "Ocultar texto citado", + "loading_email": "Cargando correo...", + "loading": "Cargando...", + "reply": "Responder", + "reply_all": "Responder a todos", + "forward": "Reenviar", + "delete": "Eliminar", + "archive": "Archivar", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Destacar", + "unstar": "Quitar destacado", + "mark_unread": "Marcar como no leído", + "mark_read": "Marcar como leído", + "unread": "No leído", + "read": "Leído", + "spam_short": "Spam", + "not_spam_short": "No spam", + "move": "Mover", + "print": "Imprimir", + "view_source": "Ver código fuente", + "fullscreen": "Pantalla completa", + "exit_fullscreen": "Salir de pantalla completa", + "view_in_dark_mode": "Ver en modo oscuro", + "view_in_light_mode": "Ver en modo claro", + "export_email": "Exportar como .eml", + "forward_as_attachment": "Reenviar como archivo adjunto", + "import_email": "Importar .eml o .zip", + "keyboard_shortcuts": "Atajos de teclado (?)", + "email_source": "Código Fuente del Correo", + "draft_banner": "Este mensaje es un borrador", + "edit_draft": "Editar", + "copy_source": "Copiar al portapapeles", + "source_copied": "Código fuente copiado al portapapeles", + "attachments": "Archivos adjuntos", + "important": "Importante", + "download": "Descargar", + "download_all": "Descargar todo", + "from": "De", + "to": "Para", + "cc": "CC", + "bcc": "CCO", + "date": "Fecha", + "subject": "Asunto", + "show_details": "Mostrar detalles", + "hide_details": "Ocultar detalles", + "external_content_warning": "Las imágenes y el contenido externo han sido bloqueados", + "load_external_content": "Cargar imágenes", + "trust_sender": "Confiar siempre en este remitente", + "sender_check": { + "unverified_label": "Remitente no verificado", + "failed_label": "Falló la verificación del remitente", + "unverified": "Este mensaje dice proceder de {domain}, pero nada lo confirma. No tiene una firma válida de {domain} ni procede de un servidor autorizado por {domain}.", + "unverified_sent_from": "Este mensaje dice proceder de {domain}, pero se envió desde {host} y no tiene una firma válida de {domain}.", + "failed": "Este mensaje dice proceder de {domain}, pero no superó las comprobaciones de remitente de ese dominio. Podría estar falsificado.", + "failed_sent_from": "Este mensaje dice proceder de {domain}, pero se envió desde {host} y no superó las comprobaciones de remitente de {domain}. Podría estar falsificado.", + "caution": "Tenga cuidado con los enlaces, los archivos adjuntos y las solicitudes de contraseñas o datos de pago." + }, + "back_to_list": "Volver a la lista", + "view_contact": "Ver contacto", + "message_details": "Detalles del Mensaje", + "more_reply_options": "Más opciones de respuesta", + "set_tag": "Establecer etiqueta", + "tag": "Etiqueta", + "more_actions": "Más acciones", + "move_to": "Mover a...", + "remove_tag": "Eliminar etiqueta", + "tag_filter_placeholder": "Filtrar etiquetas", + "tag_no_matches": "No hay etiquetas coincidentes", + "more_count": "+{count} más", + "characters_count": "{count} caracteres", + "quick_reply_placeholder": "Escriba una respuesta rápida...", + "more_options": "Más opciones", + "sending": "Enviando...", + "security_authentication": "Seguridad y Autenticación", + "technical_details": "Detalles Técnicos", + "message_id_label": "ID del Mensaje:", + "reply_to_label": "Responder a:", + "delivery_time_label": "Hora de entrega:", + "conversation_part_label": "Parte de conversación:", + "previous_messages": "{count} mensaje anterior", + "previous_messages_plural": "{count} mensajes anteriores", + "time": { + "day": "día", + "days": "días", + "hour": "hora", + "hours": "horas", + "minute": "minuto", + "minutes": "minutos" + }, + "unknown_sender": "Desconocido", + "recipient_me": "yo", + "recipient_and_others": "{name} y {count} más", + "recipient_to_prefix": "Para:", + "authentication": { + "title": "Autenticación", + "status": { + "verified": "Verificado", + "warning": "Advertencia", + "none": "No autenticado" + }, + "spf": { + "pass": "SPF Aprobado", + "fail": "SPF Fallido", + "none": "Sin SPF" + }, + "dkim": { + "pass": "DKIM Válido", + "fail": "DKIM Inválido", + "none": "Sin DKIM" + }, + "dmarc": { + "pass": "DMARC Aprobado", + "fail": "DMARC Fallido", + "none": "Sin DMARC" + }, + "spam_score": "Puntuación de Spam", + "tooltip_spf": "Sender Policy Framework: verifica que el servidor de envío esté autorizado a enviar correo en nombre del dominio", + "tooltip_dkim": "DomainKeys Identified Mail: confirma mediante una firma criptográfica que el correo no se alteró durante el transporte", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: garantiza que SPF y DKIM se alineen con el dominio del remitente y establece una política para los fallos", + "policy": "Política", + "result": { + "pass": "Aprobado", + "fail": "Fallido", + "softfail": "Fallo leve", + "neutral": "Neutro", + "permerror": "Error permanente", + "temperror": "Error temporal", + "none": "Ninguno" + } + }, + "headers": { + "routing": "Enrutamiento", + "received": "Recibido", + "message_id": "ID del Mensaje", + "list_info": "Información de Lista" + }, + "tooltips": { + "reply": "Responder", + "reply_all": "Responder a todos (a)", + "forward": "Reenviar (f)", + "archive": "Archivar", + "delete": "Eliminar", + "star": "Destacar (s)", + "unstar": "Quitar estrella (s)", + "compose": "Redactar (c)", + "previous": "Correo anterior", + "next": "Correo siguiente", + "edit_draft": "Editar borrador" + }, + "spam": { + "button_title": "Reportar spam", + "not_spam_title": "Marcar como legítimo", + "toast_success": "Movido a Correo no deseado", + "toast_batch": "{count} correos movidos a Correo no deseado", + "toast_undo": "Deshacer", + "toast_not_spam_success": "Movido a Bandeja de entrada", + "toast_not_spam_batch": "{count} correos movidos a Bandeja de entrada", + "error": "No se pudo reportar spam", + "error_not_spam": "No se pudo restaurar el correo" + }, + "unsubscribe_banner": { + "label": "Boletín", + "button": "Cancelar suscripción", + "confirm_title": "¿Cancelar suscripción de este remitente?", + "confirm_button": "Confirmar", + "cancel": "Cancelar", + "success_http": "Página de cancelación de suscripción abierta en nueva pestaña", + "success_mailto": "Correo de cancelación enviado", + "confirm_message_http": "La página de cancelación se abrirá en una pestaña nueva.", + "confirm_message_mailto": "Se enviará un correo de cancelación al remitente.", + "error": "No se pudo cancelar la suscripción", + "dismiss": "Descartar" + }, + "calendar_invitation": { + "loading": "Cargando detalles del evento…", + "title": "Invitación de calendario", + "published_title": "Evento publicado", + "response_title": "Respuesta al evento", + "update_title": "Actualización del evento", + "counter_title": "Contrapropuesta", + "refresh_title": "Solicitud de actualización", + "declined_counter_title": "Contrapropuesta rechazada", + "cancelled_title": "Evento cancelado", + "organizer": "Organizado por {name}", + "organizer_label": "Organizado por", + "attendees": "{count, plural, one {# asistente} other {# asistentes}}", + "accept": "Aceptar", + "maybe": "Quizás", + "decline": "Rechazar", + "add_to_calendar": "Añadir al calendario", + "added": "Añadido al calendario", + "rsvp_sent": "Respuesta enviada", + "parse_error": "No se pudo leer la invitación", + "action_failed": "No se pudo completar esa acción del calendario.", + "no_calendar": "Calendario no disponible", + "published_info": "Este evento se compartió como referencia.", + "response_info": "Este mensaje contiene la respuesta de un asistente.", + "response_info_organizer": "Esta respuesta del asistente actualiza su evento.", + "update_info": "Este mensaje actualiza un evento existente.", + "counter_info": "Este mensaje propone cambios a un evento.", + "counter_info_organizer": "Este asistente propuso cambios a su evento.", + "refresh_info": "Este mensaje solicita los últimos detalles del evento.", + "refresh_info_organizer": "Un asistente solicitó los últimos detalles del evento.", + "declined_counter_info": "El organizador rechazó una contrapropuesta.", + "authentication_failed_info": "Las comprobaciones de autenticación de esta invitación fallaron. Trata las acciones de calendario con precaución.", + "authentication_missing_info": "Esta invitación no incluye autenticación de correo verificada. Confirma los detalles con el organizador si algo parece inusual.", + "sender_mismatch_info": "Esta invitación se envió desde {sender}, mientras que el organizador indicado en los datos del calendario es {organizer}.", + "sender_mismatch_unverified_info": "Esta invitación se envió desde {sender}, mientras que el organizador indicado en los datos del calendario es {organizer}, y el mensaje no pudo verificarse.", + "organizer_role": "Organizas este evento", + "your_response": "Tu respuesta: {status}", + "response_needed": "Pendiente de respuesta", + "response_accepted": "Aceptada", + "response_tentative": "Tentativa", + "response_declined": "Rechazada", + "response_delegated": "Delegada", + "actor_sent_info": "Enviado por {name}.", + "actor_response_info": "{name} respondió {status}.", + "actor_counter_info": "{name} propuso cambios para este evento.", + "actor_refresh_info": "{name} pidió los detalles más recientes del evento.", + "actor_declined_counter_info": "{name} rechazó la contrapropuesta.", + "actor_note": "Nota: {comment}", + "actor_unknown": "Alguien", + "proposed_changes": "Cambios propuestos", + "change_title": "Título", + "change_time": "Hora", + "change_location": "Ubicación", + "change_description": "Descripción", + "change_empty": "Ninguno", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Aplicar cambios propuestos", + "proposal_applied": "Se aplicaron los cambios propuestos.", + "review_proposal": "Revisar propuesta", + "review_request": "Revisar solicitud", + "view_in_calendar": "Ver en el calendario", + "select_calendar": "Seleccionar calendario", + "already_in_calendar": "Ya está en tu calendario", + "request_info": "Has sido invitado a este evento. Responde para informar al organizador de tu disponibilidad.", + "cancel_info": "El organizador ha cancelado este evento.", + "event_updated": "Actualización #{sequence}", + "event_status_tentative": "Provisional", + "event_status_cancelled": "Cancelado", + "collapse": "Ocultar detalles", + "expand": "Mostrar detalles" + }, + "previous": "Anterior", + "next": "Siguiente", + "send": "Enviar", + "more": "más", + "details": { + "recipients_routing": "Destinatarios y enrutamiento", + "authentication_security": "Autenticación y seguridad", + "identifiers_threading": "Identificadores y conversación", + "mailing_list": "Lista de correo", + "message_properties": "Propiedades del mensaje", + "sent": "Enviado", + "received": "Recibido", + "delivery_time": "Tiempo de entrega", + "in_reply_to": "En respuesta a", + "references": "Referencias", + "thread_id": "ID de conversación", + "size": "Tamaño", + "mime_type": "Tipo MIME", + "attachments_summary": "{count} archivos · {size}", + "list_id": "ID de lista", + "list_help": "Ayuda de lista", + "list_post": "Publicación en lista", + "list_unsubscribe": "Darse de baja", + "iprev": "DNS inverso", + "spam_status": "Estado de spam", + "ai_verdict": "Veredicto de IA", + "account": "Cuenta", + "no_subject": "(sin asunto)" + }, + "scheduled_banner": "Programado para enviarse el {date}", + "scheduled_send_created": "Correo programado para envío", + "sending_in": "Enviando en {seconds} s", + "undo_send_failed": "No se pudo deshacer. Puede que el mensaje ya se haya enviado.", + "cancel_scheduled_send": "Cancelar envío", + "reschedule_send": "Reprogramar", + "cancel_and_edit": "Cancelar y editar", + "cancel_and_compose_again": "Cancelar y redactar de nuevo", + "reschedule_prompt": "Introduce una nueva fecha/hora, p. ej. 2026-05-04T15:30", + "scheduled_actions_only": "La vista Programados solo admite acciones de programación", + "undo_send_scheduled": "Mensaje programado para envío", + "undo_send": "Deshacer envío", + "contact_sidebar": { + "title": "Contacto", + "close": "Cerrar panel", + "action_email": "Correo", + "action_email_title": "Enviar correo", + "action_copy": "Copiar", + "action_copy_title": "Copiar correo electrónico", + "action_edit_title": "Editar contacto", + "section_emails": "Correos electrónicos", + "section_phones": "Teléfonos", + "section_organizations": "Organizaciones", + "section_addresses": "Direcciones", + "section_notes": "Notas", + "not_in_contacts": "No está en sus contactos", + "add_to_contacts": "Añadir a contactos", + "copied": "¡Copiado!", + "copy_failed": "Error al copiar", + "view_contact": "Ver contacto", + "view_details": "Ver detalles" + }, + "send_now": "Enviar ahora", + "verification_code": { + "copy": "Copiar código {code}", + "copied": "Código copiado", + "copy_failed": "No se pudo copiar el código" + }, + "mv_position": "{index} de {total}", + "plugin_panel_show": "Mostrar el panel del complemento", + "plugin_panel_hide": "Ocultar el panel del complemento" + }, + "email_composer": { + "select_from_directory": "Seleccionar del directorio", + "minimize": "Minimizar", + "maximize": "Pantalla completa", + "restore": "Restaurar", + "new_message": "Nuevo Mensaje", + "signature_edit_hint": "Haz doble clic para editar la firma", + "reply": "Responder", + "reply_all": "Responder a Todos", + "forward": "Reenviar", + "reply_to": "Responder", + "reply_all_to": "Responder a Todos", + "forward_message": "Reenviar", + "from": "De", + "to": "Para", + "cc": "CC", + "bcc": "CCO", + "subject": "Asunto", + "body_placeholder": "Escriba su mensaje...", + "send": "Enviar", + "cancel": "Cancelar", + "attach": "Adjuntar", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Adjuntar desde Archivos", + "files_picker": { + "attach": "{count, plural, =0 {Adjuntar} one {Adjuntar 1 archivo} other {Adjuntar # archivos}}", + "empty_folder": "Esta carpeta está vacía", + "load_failed": "No se pudieron cargar tus archivos" + }, + "discard": "Descartar", + "discard_draft_title": "¿Descartar borrador?", + "discard_draft_confirm": "Tiene cambios sin guardar. ¿Desea descartar este borrador?", + "discard_draft_message": "El borrador se eliminará.", + "saving": "Guardando...", + "draft_saved": "Borrador guardado", + "save_failed": "Error al guardar", + "to_placeholder": "Direcciones de correo de destinatarios", + "cc_placeholder": "Destinatarios CC", + "bcc_placeholder": "Destinatarios CCO", + "subject_placeholder": "Asunto", + "cc_label": "CC:", + "bcc_label": "CCO:", + "subject_label": "Asunto:", + "file_size_kb": "KB", + "prefix": { + "forward": "Rvf:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 correo} other {# correos}}", + "no_subject": "(Sin Asunto)", + "unknown_sender": "Desconocido", + "quote": { + "reply_header": "El {date}, {sender} escribió:", + "forward_header": "---------- Mensaje reenviado ----------", + "from": "De: {sender}", + "date": "Fecha: {date}", + "subject": "Asunto: {subject}", + "to": "Para: {recipients}" + }, + "remove_sub_address": "Eliminar sub-dirección", + "from_override": { + "toggle_off": "Anular", + "toggle_on": "Cancelar anulación", + "toggle_tooltip": "Edita libremente el nombre y la dirección del remitente. El correo aún se envía a través de tu identidad.", + "name_label": "Nombre del remitente", + "name_placeholder": "Nombre", + "email_label": "Dirección de correo del remitente", + "email_placeholder": "alias@example.com", + "envelope_notice": "Si tu servidor no acepta esta dirección como remitente del sobre, se usa {identity} en su lugar, y los destinatarios pueden verla en el encabezado Return-Path." + }, + "save_as_template": "Guardar como plantilla", + "attach_from_device": "Subir desde este dispositivo", + "templates_menu": "Plantillas", + "insert_template": "Insertar plantilla", + "options": "Opciones", + "option_plain_text": "Texto sin formato", + "option_plain_text_hint": "Enviar sin formato", + "option_read_receipt": "Solicitar confirmación de lectura", + "option_read_receipt_hint": "Pedir al destinatario que confirme que lo abrió", + "option_dsn": "Notificación de entrega", + "option_dsn_hint": "Recibir un informe cuando el servidor lo entregue", + "option_require_tls": "Exigir entrega cifrada", + "option_require_tls_hint": "Entregar solo por TLS; si no, devolver", + "option_priority": "Prioridad", + "option_priority_high": "Alta", + "option_priority_normal": "Normal", + "option_priority_low": "Baja", + "validation": { + "recipient_required": "Agregue un destinatario para enviar", + "body_required": "Escriba un mensaje o adjunte un archivo", + "invalid_recipients": "No enviado - revisa estas direcciones: {addresses}", + "attachments_uploading": "Los archivos adjuntos aún se están subiendo; se enviará en cuanto terminen", + "attachment_upload_failed": "No enviado: no se pudo subir un adjunto. Elimínalo e inténtalo de nuevo." + }, + "upload_progress": "Subiendo {uploaded} / {total}", + "upload_cancel": "Cancelar subida", + "upload_failed": "Error al subir {filename}", + "attachment_too_large": "\"{name}\" supera el tamaño que permite el servidor ({max} por archivo)", + "attachments_total_too_large": "Los adjuntos superarían el límite del servidor de {max} por mensaje", + "send_failed": "Error al enviar el correo", + "smime_sign_on": "Firma S/MIME activada", + "smime_sign_off": "Firma S/MIME desactivada", + "smime_encrypt_on": "Cifrado S/MIME activado", + "smime_encrypt_off": "Cifrado S/MIME desactivado", + "smime_encrypt_unavailable": "Cifrado S/MIME no disponible – faltan certificados de destinatario", + "smime_unlock_title": "Desbloquear clave S/MIME", + "smime_unlock_message": "Introduce la contraseña para desbloquear tu clave S/MIME.", + "smime_unlock_button": "Desbloquear", + "smime_passphrase_placeholder": "Contraseña S/MIME", + "send_timeout": "El servidor dejó de responder. Compruebe la carpeta Enviados antes de volver a enviar: el mensaje puede haber salido.", + "send_offline": "Parece que no tienes conexión. El mensaje sigue aquí; vuelve a enviarlo cuando tengas conexión.", + "formatting": "Formato", + "send_recipients_rejected": "No enviado: el servidor rechazó a todos los destinatarios.", + "send_plugin_failed": "No enviado: un complemento que gestiona el envío falló o tardó demasiado. Revisa la carpeta Enviados antes de volver a enviarlo.", + "send_some_recipients_rejected": "Enviado, pero no a estos destinatarios: el servidor los rechazó.", + "continue_draft": "Continuar borrador", + "close_draft_title": "¿Guardar o descartar borrador?", + "close_draft_message": "Tiene cambios sin guardar. ¿Desea guardar esto como borrador o descartarlo?", + "drop_files": "Suelta archivos para adjuntar", + "show_less": "Mostrar menos", + "forgot_attachment": { + "title": "¿Olvidaste un archivo adjunto?", + "message": "Tu mensaje menciona \"{keyword}\" pero no hay ningún archivo adjunto. ¿Enviar de todos modos?", + "send_anyway": "Enviar de todos modos", + "back": "Volver a editar" + }, + "empty_subject": { + "title": "¿Enviar sin asunto?", + "message": "Este mensaje no tiene asunto. ¿Enviarlo de todos modos?", + "dont_ask_again": "No volver a preguntar", + "send_anyway": "Enviar de todos modos", + "back": "Volver a editar" + }, + "add_link": "Añadir enlace", + "link_url_prompt": "Introduce la URL", + "sending": "Enviando...", + "schedule_send": "Programar envío", + "schedule_send_description": "Elige cuándo debe liberar el servidor este mensaje.", + "schedule_preset": { + "tomorrow_morning": "Mañana por la mañana", + "tomorrow_afternoon": "Mañana por la tarde", + "monday_morning": "El lunes por la mañana" + }, + "schedule_send_required": "Elige una fecha y hora.", + "schedule_send_invalid": "Introduce una fecha y hora válidas.", + "schedule_send_future": "Elige una fecha y hora futuras.", + "schedule_send_too_late": "Esta hora es posterior a lo que permite el servidor.", + "schedule_send_unsupported": "El envío programado no es compatible con esta cuenta.", + "schedule_send_cleanup_warning": "Se creó el envío programado, pero falló la limpieza del borrador.", + "send_delay_unsupported": "La demora de envío no es compatible con esta cuenta.", + "send_delay_unsupported_confirm": "Esta cuenta no admite demora de envío. ¿Enviar inmediatamente?", + "recipient_edit_email": "Editar dirección de correo", + "recipient_edit_name": "Editar nombre para mostrar", + "recipient_email_placeholder": "Dirección de correo", + "recipient_name_placeholder": "Nombre para mostrar", + "invalid_recipient": "No es una dirección de correo válida", + "autocomplete_search_server": "Buscar en el servidor", + "autocomplete_searching": "Buscando...", + "mention_recipients": "Destinatarios", + "mention_card_not_recipient": "No está en Para ni en CC, así que no recibirá este mensaje.", + "mention_card_add": "Añadir a {field}", + "mention_card_remove": "Quitar mención", + "toolbar": { + "bold": "Negrita", + "italic": "Cursiva", + "underline": "Subrayado", + "strikethrough": "Tachado", + "text_color": "Color del texto", + "remove_color": "Quitar color", + "heading_1": "Encabezado 1", + "heading_2": "Encabezado 2", + "bullet_list": "Lista con viñetas", + "ordered_list": "Lista numerada", + "quote": "Cita", + "code_block": "Bloque de código", + "align_left": "Alinear a la izquierda", + "align_center": "Centrar", + "align_right": "Alinear a la derecha", + "text_direction": "Dirección del texto (RTL/LTR)", + "link": "Enlace", + "table": "Tabla", + "clear_formatting": "Borrar formato", + "undo": "Deshacer", + "redo": "Rehacer", + "add_row_above": "Añadir fila encima", + "add_row_below": "Añadir fila debajo", + "add_column_before": "Añadir columna antes", + "add_column_after": "Añadir columna después", + "delete_row": "Eliminar fila", + "delete_column": "Eliminar columna", + "toggle_header_row": "Alternar fila de encabezado", + "delete_table": "Eliminar tabla", + "pick_size": "Elegir tamaño", + "font_size": "Tamaño de fuente", + "font_size_default": "Predeterminado", + "background_color": "Color de fondo", + "remove_background_color": "Quitar color de fondo" + }, + "send_filing_warning": "Enviado - pero la limpieza posterior falló, puede quedar un borrador obsoleto.", + "pop_out": "Abrir la respuesta en una ventana", + "send_guard_failed": "No enviado: un complemento que revisa los mensajes antes de enviarlos falló o no respondió." + }, + "confirm_dialog": { + "confirm": "Confirmar", + "cancel": "Cancelar" + }, + "common": { + "loading": "Cargando...", + "error": "Error", + "success": "Éxito", + "cancel": "Cancelar", + "save": "Guardar", + "delete": "Eliminar", + "edit": "Editar", + "close": "Cerrar", + "search": "Buscar", + "refresh": "Actualizar", + "settings": "Configuración", + "help": "Ayuda", + "logout": "Cerrar sesión", + "yes": "Sí", + "no": "No", + "unknown": "Desconocido", + "app_title": "Correo Web", + "reconnecting": "Conexión perdida. Intentando reconectar…", + "rate_limited_title": "La autenticación del servidor está limitada temporalmente.", + "rate_limited_detail": "Bulwark ha pausado las solicitudes en segundo plano para evitar un bloqueo. Reintentando en {seconds}s.", + "rate_limited_action_title": "Solicitud pausada para evitar el bloqueo.", + "rate_limited_action_detail": "Bulwark está esperando a que termine el enfriamiento del servidor antes de enviar más solicitudes autenticadas. Inténtalo de nuevo en {seconds}s.", + "pull_to_refresh": "Desliza para actualizar", + "release_to_refresh": "Suelta para actualizar", + "refreshing": "Actualizando…" + }, + "notifications": { + "email_sent": "Correo enviado exitosamente", + "email_deleted": "Correo eliminado", + "email_archived": "Correo archivado", + "emails_deleted": "{count, plural, one {# correo eliminado} other {# correos eliminados}}", + "emails_archived": "{count, plural, one {# correo archivado} other {# correos archivados}}", + "error_archiving": "No se pudo archivar el correo", + "error_updating": "No se pudo actualizar el correo", + "email_starred": "Correo destacado", + "email_unstarred": "Correo sin destacar", + "email_marked_read": "Correo marcado como leído", + "email_marked_unread": "Correo marcado como no leído", + "copied_to_clipboard": "Copiado al portapapeles", + "source_copied": "Código fuente copiado al portapapeles", + "error_sending": "Error al enviar el correo", + "error_deleting": "Error al eliminar el correo", + "error_loading": "Error al cargar los correos", + "new_email": "Nuevo correo", + "new_email_from": "De {sender}", + "click_to_view": "Haga clic para ver", + "email_moved": "Correo movido", + "emails_moved": "{count} correos movidos", + "moved_to_mailbox": "Movido a {mailbox}", + "move_failed": "Error al mover", + "removed_from_folder": "Quitado de {mailbox}", + "remove_from_folder_failed": "No se pudo quitar de esta carpeta", + "emails_copied": "{count} correos copiados", + "copy_failed": "Error al copiar", + "move_error": "No se pudieron mover los correos a la carpeta seleccionada", + "email_tagged": "Correo etiquetado", + "emails_tagged": "{count} correos etiquetados", + "tag_failed": "Error al etiquetar", + "identity_created": "Identidad creada exitosamente", + "identity_updated": "Identidad actualizada exitosamente", + "identity_deleted": "Identidad eliminada", + "identity_set_primary": "Identidad principal actualizada", + "identity_create_failed": "Error al crear identidad: {error}", + "identity_update_failed": "Error al actualizar identidad: {error}", + "identity_delete_failed": "Error al eliminar identidad: {error}", + "identity_unauthorized": "No está autorizado para enviar desde esta dirección de correo", + "identity_not_found": "Identidad no encontrada", + "vacation_saved": "Configuración de ausencia guardada", + "vacation_save_failed": "Error al guardar la configuración de ausencia", + "filters_saved": "Filtros guardados correctamente", + "filters_save_failed": "Error al guardar los filtros", + "filters_deleted": "Regla de filtrado eliminada", + "templates_exported": "Plantillas exportadas correctamente", + "templates_imported": "{count, plural, one {# plantilla importada} other {# plantillas importadas}}", + "templates_import_errors": "Algunas plantillas no se pudieron importar", + "templates_import_empty": "No se encontraron plantillas en el archivo", + "export_email_error": "Error al exportar el correo electrónico", + "import_email_success": "Correo electrónico importado correctamente", + "import_email_error": "Error al importar el correo electrónico", + "rule_created": "Regla creada", + "rule_merged": "Añadido a la regla «{name}»", + "rule_already_covered": "Ya lo cubre la regla «{name}»", + "rule_apply_existing": "{count, plural, one {Aplicar a # mensaje existente} other {Aplicar a # mensajes existentes}}", + "rule_applied": "{count, plural, =0 {Ningún mensaje necesitaba cambios} one {Regla aplicada a # mensaje} other {Regla aplicada a # mensajes}}", + "rule_apply_failed": "No se pudo aplicar la regla a los mensajes existentes", + "rule_edit": "Editar regla", + "rule_undo": "Deshacer", + "rule_undone": "Cambio de regla deshecho", + "rule_undo_failed": "No se pudo deshacer el cambio de la regla", + "rule_undo_conflict": "Tus filtros han cambiado desde entonces. Deshaz el cambio en la configuración de filtros.", + "rule_folder_failed": "No se pudo crear la carpeta", + "vacation_filters_save_failed": "Configuración de ausencia guardada, pero no se pudieron guardar el reenvío ni los destinatarios de la respuesta" + }, + "date": { + "today": "Hoy", + "yesterday": "Ayer", + "this_week": "Esta semana", + "last_week": "Semana pasada", + "this_month": "Este mes", + "older": "Más antiguo", + "just_now": "Justo ahora", + "minutes_ago": "hace {count} minuto", + "minutes_ago_plural": "hace {count} minutos", + "hours_ago": "hace {count} hora", + "hours_ago_plural": "hace {count} horas", + "days_ago": "hace {count} día", + "days_ago_plural": "hace {count} días" + }, + "language": { + "title": "Idioma", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Seleccionar idioma", + "switch_to_english": "Cambiar a inglés", + "switch_to_french": "Cambiar a francés", + "switch_to_japanese": "Cambiar a japonés", + "switch_to_spanish": "Cambiar a español", + "switch_to_italian": "Cambiar a italiano", + "switch_to_german": "Cambiar a alemán", + "switch_to_dutch": "Cambiar a neerlandés", + "switch_to_portuguese": "Cambiar a portugués", + "switch_to_russian": "Cambiar a ruso", + "switching": "Cambiando idioma...", + "switch": "Cambiar idioma", + "current": "Idioma actual", + "polish": "Polski", + "switch_to_polish": "Cambiar a polaco", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Cambiar a farsi", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Cambiar a mongol" + }, + "settings": { + "title": "Configuración", + "back_to_mail": "Volver al Correo", + "save_success": "Configuración guardada exitosamente", + "import_success": "Configuración importada exitosamente", + "import_error": "Error al importar la configuración", + "reset_confirm": "¿Está seguro de que desea restablecer toda la configuración a los valores predeterminados?", + "unsaved_changes": "Tienes cambios sin guardar", + "discard_changes": "¿Descartar los cambios?", + "discard": "Descartar", + "keep_editing": "Seguir editando", + "tabs": { + "appearance": "Apariencia", + "language": "Idioma, Región y Hora", + "email": "Comportamiento del Correo", + "composer": "Editor", + "privacy": "Privacidad y Seguridad", + "account": "Cuenta", + "identities": "Identidades", + "vacation": "Ausencia", + "advanced": "Avanzado", + "calendar": "Calendario", + "filters": "Filtros", + "templates": "Plantillas", + "folders": "Carpetas", + "keywords": "Etiquetas", + "security": "Seguridad", + "encryption": "Cifrado", + "files": "Archivos", + "contacts": "Contactos", + "protocol_handlers": "Aplicaciones predeterminadas", + "sidebar_apps": "Apps de barra lateral", + "notifications": "Notificaciones", + "layout": "Diseño", + "themes": "Temas", + "reading": "Lectura", + "composing": "Redacción", + "downloads": "Descargas", + "content_senders": "Contenido y remitentes", + "about_data": "Acerca de y datos", + "debug": "Depuración" + }, + "themes": { + "title": "Temas", + "description": "Elija entre los temas implementados por su administrador y los ajustes preestablecidos integrados.", + "locked": "La selección de tema está bloqueada por un administrador.", + "forced": "El tema «{name}» lo impone el administrador y no se puede cambiar", + "admin_theme": "Tema del administrador", + "activated": "Tema activado", + "default_restored": "Tema predeterminado restaurado", + "default_name": "Predeterminado", + "default_badge": "Predeterminado", + "admin_enforced": "Impuesto por el administrador" + }, + "tab_groups": { + "general": "General", + "account": "Cuenta e identidad", + "organization": "Organización del correo", + "apps": "Aplicaciones", + "system": "Sistema", + "appearance": "Apariencia", + "mail": "Correo", + "privacy": "Privacidad y seguridad", + "advanced": "Avanzado" + }, + "appearance": { + "message_list_order": { + "label": "Orden de la lista de mensajes", + "description": "Cómo se ordenan los mensajes en la lista. El servidor realiza la ordenación, por lo que abarca toda la carpeta, no solo los mensajes cargados.", + "preset": { + "chronological": "Cronológico (más recientes primero)", + "unread_first": "No leídos primero", + "starred_first": "Destacados primero", + "tagged_first": "Etiquetados primero", + "custom": "Personalizado" + }, + "tag_label": "Etiqueta", + "advanced_show": "Avanzado: editar niveles de orden", + "advanced_hide": "Ocultar avanzado", + "criterion_label": "Criterio del nivel {n}", + "direction_label": "Dirección del nivel {n}", + "criteria": { + "unread": "No leído", + "starred": "Destacado", + "tag": "Etiqueta", + "receivedAt": "Fecha de recepción", + "sentAt": "Fecha de envío", + "from": "Remitente", + "subject": "Asunto", + "size": "Tamaño" + }, + "direction": { + "unread_first": "No leídos primero", + "read_first": "Leídos primero", + "starred_first": "Destacados primero", + "unstarred_first": "No destacados primero", + "tagged_first": "Etiquetados primero", + "untagged_first": "Sin etiqueta primero", + "newest_first": "Más recientes primero", + "oldest_first": "Más antiguos primero", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Más grandes primero", + "smallest_first": "Más pequeños primero" + }, + "add_level": "Añadir nivel", + "remove_level": "Eliminar nivel", + "move_up": "Subir", + "move_down": "Bajar", + "tiebreak_note": "Hasta tres niveles; los niveles posteriores deshacen empates. «Más recientes primero» se aplica siempre al final.", + "scope": { + "label": "Aplicar a todas las carpetas", + "description": "Si está desactivado, el orden solo se aplica a la bandeja de entrada; las demás carpetas siguen siendo cronológicas." + }, + "unsupported_note": "Tu servidor de correo no admite ordenar por estado de lectura, destacados o etiquetas. Esos niveles se ignoran y la lista sigue siendo cronológica." + }, + "title": "Apariencia", + "description": "Personalice la apariencia de su correo web", + "theme": { + "label": "Tema", + "description": "Elija su esquema de color preferido", + "light": "Claro", + "dark": "Oscuro", + "system": "Sistema" + }, + "language": { + "label": "Idioma", + "description": "Elija su idioma preferido" + }, + "font_size": { + "label": "Tamaño de Fuente", + "description": "Ajuste el tamaño del texto para mejor legibilidad", + "small": "Pequeño", + "medium": "Mediano", + "large": "Grande" + }, + "list_density": { + "label": "Densidad", + "description": "Controle el espaciado en toda la interfaz", + "extra_compact": "Extra compacto", + "compact": "Compacto", + "regular": "Normal", + "comfortable": "Cómodo" + }, + "interface_layout": { + "label": "Diseño de la interfaz", + "description": "Dónde se sitúan la búsqueda, la navegación y las barras de herramientas", + "default": "Predeterminado", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Habilitar Animaciones", + "description": "Mostrar transiciones y efectos suaves" + }, + "toolbar_position": { + "label": "Posición de la barra de herramientas", + "description": "Dónde mostrar los botones de acción del correo (Responder, Archivar, Eliminar, etc.)", + "top": "Arriba", + "below_subject": "Debajo del asunto" + }, + "toolbar_labels": { + "label": "Mostrar etiquetas de la barra de herramientas", + "description": "Mostrar etiquetas de texto junto a los iconos de la barra de herramientas. Desactivar para ahorrar espacio una vez que esté familiarizado con los iconos." + }, + "hide_account_switcher": { + "label": "Ocultar selector de cuentas de la barra lateral", + "description": "Ocultar el selector de cuentas en la parte superior de la barra lateral de carpetas. Aún puede cambiar de cuenta desde la barra de navegación inferior." + }, + "show_rail_account_list": { + "label": "Mostrar avatares de cuentas en la barra de navegación", + "description": "Mostrar círculos de cuentas individuales en la parte inferior de la barra de navegación para un cambio rápido, con un botón de cerrar sesión debajo." + }, + "unified_mailbox": { + "label": "Buzón unificado", + "description": "Mostrar carpetas combinadas (Entrada, Enviados, etc.) de todas las cuentas conectadas", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Incluir buzones de grupo", + "description": "Incluir también los buzones compartidos o de grupo en la vista unificada." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Iconos de barra lateral a color", + "description": "Colorea los iconos de carpetas y etiquetas según su tipo (azul para Bandeja de entrada, rojo para Spam, verde para Enviados, etc.). Desactívalo para una barra lateral monocroma." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Mostrar el número total de mensajes", + "description": "Muestra el número total de mensajes junto a las carpetas y etiquetas, además del número de mensajes no leídos. Desactívalo para mostrar solo los no leídos." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Mostrar cuentas de correo compartidas", + "description": "Muestra las carpetas de los buzones compartidos contigo en Compartido, en la barra lateral. Desactívalo para ocultarlas todas, u oculta cuentas concretas abajo. Sus calendarios y contactos se mantienen." + }, + "pro_interface": { + "label": "Interfaz Pro (experimental)", + "description": "Diseño de escritorio para usuarios avanzados con exploración de mensajes en varias pestañas y flujos de trabajo entre cuentas. La interfaz estándar no se ve afectada; puedes volver en cualquier momento.", + "open_label": "Abrir interfaz Pro", + "back_to_standard": "Volver al estándar" + }, + "cross_unread": { + "label": "Todas las cuentas: No leído", + "description": "Muestra una entrada en la sección Todas las cuentas con el correo no leído de todas las cuentas, abarcando todas las carpetas excepto spam, enviados, archivo, papelera y borradores." + }, + "cross_starred": { + "label": "Todas las cuentas: Destacado", + "description": "Muestra una entrada en la sección Todas las cuentas con el correo destacado de todas las cuentas, abarcando todas las carpetas excepto spam, enviados, archivo, papelera y borradores." + }, + "cross_all": { + "label": "Todas las cuentas: Todo el correo", + "description": "Muestra una entrada en la sección Todas las cuentas con todo el correo de todas las cuentas, abarcando todas las carpetas excepto spam, enviados, archivo, papelera y borradores." + } + }, + "keywords": { + "title": "Etiquetas de correo", + "description": "Define etiquetas para organizar tus correos con colores. Se almacenan como palabras clave JMAP en el servidor.", + "add_keyword": "Añadir etiqueta", + "label_field": "Nombre para mostrar", + "label_placeholder": "ej. Trabajo, Personal, Urgente", + "id_field": "ID de etiqueta", + "id_placeholder": "ej. trabajo, personal", + "color_field": "Color", + "id_exists": "Este ID de etiqueta ya existe", + "edit": "Editar etiqueta", + "delete": "Eliminar etiqueta", + "save": "Guardar", + "add": "Añadir", + "cancel": "Cancelar", + "migrating": "Actualizando etiqueta en correos existentes…", + "migration_error": "Error al actualizar la etiqueta en correos existentes", + "filters_migration_error": "Algunas reglas de filtro siguen usando la etiqueta anterior: actualízalas manualmente", + "nesting": { + "label": "Etiquetas anidadas", + "description": "Anida etiquetas debajo de otras etiquetas y muéstralas como un árbol en la barra lateral." + }, + "parent_field": "Etiqueta principal", + "no_parent": "Sin etiqueta principal", + "too_long": "Esta ruta de etiqueta es demasiado larga (máximo {max} caracteres)", + "has_children_locked": "Hay otras etiquetas anidadas bajo esta, por lo que su nombre y su etiqueta principal están bloqueados. Muévelas o elimínalas primero.", + "has_children_delete": "Elimina primero las etiquetas anidadas bajo esta", + "visibility_field": "Visibilidad en la barra lateral", + "visibility": { + "show": "Mostrar", + "unread": "Mostrar si hay no leídos", + "hide": "Ocultar" + }, + "unrecognized": { + "title": "Recuperar etiquetas del servidor", + "description": "Los nombres y colores de las etiquetas se guardan en este navegador, pero las etiquetas en sí están en tus correos como palabras clave JMAP. Analiza tu correo en busca de palabras clave que ninguna etiqueta de aquí explique y vuelve a añadirlas.", + "scan": "Buscar etiquetas", + "rescan": "Analizar de nuevo", + "scanning": "Analizando… {scanned} de {total} correos", + "none": "Todas las etiquetas de tus correos ya están definidas aquí.", + "messages": "{count, plural, one {# correo} other {# correos}}", + "add_all": "Añadir las {count}", + "partial": "Solo se analizaron los {scanned} correos más recientes de {total}. Puede que falten etiquetas usadas únicamente en correos más antiguos.", + "error": "No se pudo analizar tu correo en busca de etiquetas." + } + }, + "notifications": { + "test_sound": "Probar sonido de notificación", + "sounds": { + "default": "Predeterminado (Pitido)", + "cheerful": "Alegre", + "involved": "Elaborado", + "swift": "Gesto rápido", + "relax": "Relajado" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Sonido de notificación", + "description": "Elige qué sonido reproducir para las notificaciones", + "choose": "Sonido", + "choose_desc": "Selecciona un tono de notificación y haz clic en el icono del altavoz para previsualizarlo" + }, + "email": { + "title": "Notificaciones de correo", + "description": "Configurar notificaciones para correos entrantes", + "enabled": "Notificaciones de correo", + "enabled_desc": "Mostrar notificaciones cuando lleguen nuevos correos", + "sound": "Sonido de notificación", + "sound_desc": "Reproducir un sonido cuando lleguen nuevos correos", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Notificaciones de calendario", + "description": "Configurar notificaciones para eventos del calendario", + "enabled": "Notificaciones de eventos", + "enabled_desc": "Mostrar alertas para próximos eventos del calendario", + "sound": "Sonido de notificación", + "sound_desc": "Reproducir un sonido para recordatorios del calendario", + "invitation_parsing": "Analizar invitaciones por correo", + "invitation_parsing_desc": "Detectar invitaciones de calendario en archivos adjuntos y mostrar acciones de calendario" + }, + "push": { + "confirm_disable_message": "Este dispositivo dejará de recibir alertas cuando el sitio esté cerrado.", + "confirm_disable_title": "¿Desactivar las notificaciones en segundo plano?", + "description": "Recibe notificaciones del sistema para correo nuevo cuando este sitio está cerrado. Se entrega a través del relay push de Bulwark; el relay nunca ve el contenido del correo.", + "disable": "Desactivar", + "enable": "Activar", + "dismiss_aria": "Cerrar aviso de notificaciones", + "ios_hint": "En iOS, instala primero el sitio en la pantalla de inicio: Safari solo entrega Web Push a PWAs instaladas.", + "reenable": "Volver a registrar", + "relay_desc": "Usa el relay alojado de Bulwark de forma predeterminada. Cámbialo solo si te alojas tú mismo.", + "relay_label": "Relay push", + "relay_locked": "Establecido por el administrador", + "relay_locked_desc": "La URL del relay push ha sido establecida por tu administrador y no se puede cambiar.", + "status_active": "Activo en este dispositivo", + "status_busy": "Trabajando…", + "status_inactive": "No habilitado en este dispositivo", + "status_unsupported": "Este navegador no admite Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Notificaciones en segundo plano", + "devices_title": "Dispositivos registrados", + "devices_desc": "Todos los dispositivos registrados para recibir notificaciones en segundo plano de esta cuenta. Al revocar uno, deja de recibirlas de inmediato.", + "devices_loading": "Cargando dispositivos registrados…", + "devices_empty": "No hay dispositivos registrados para notificaciones en segundo plano.", + "devices_error": "No se pudieron cargar los dispositivos registrados.", + "devices_refresh": "Actualizar", + "device_this": "Este dispositivo", + "device_other": "Otro dispositivo", + "device_status_active": "Entregando notificaciones", + "device_status_inactive": "Nunca ha entregado una notificación", + "device_status_unknown": "Desconocido para este relé", + "device_expires": "Caduca el {date}", + "revoke": "Revocar", + "confirm_revoke_title": "¿Revocar este dispositivo?", + "confirm_revoke_message": "Dejará de recibir notificaciones en segundo plano de inmediato. Si vuelves a activar las notificaciones push en ese dispositivo, se registrará de nuevo.", + "confirm_revoke_message_this": "Este dispositivo dejará de recibir notificaciones en segundo plano de inmediato." + } + }, + "language_region": { + "title": "Idioma, Región y Hora", + "description": "Idioma, formato de fecha, formato de hora y otras preferencias regionales", + "language": { + "label": "Idioma", + "description": "Elija su idioma preferido", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Formato de Fecha", + "description": "Cómo se muestran las fechas en la lista de correos", + "smart": "Inteligente", + "relative": "Relativo", + "full": "Fecha completa siempre", + "preview_today": "Hoy:", + "preview_this_week": "Esta semana:", + "preview_older": "Más antiguo:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Formato de Hora", + "description": "Elija entre reloj de 12 o 24 horas", + "12h": "12 horas", + "24h": "24 horas" + }, + "first_day": { + "label": "Primer Día de la Semana", + "description": "Iniciar la semana en domingo o lunes", + "sunday": "Domingo", + "monday": "Lunes" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Borrar la búsqueda al cambiar de carpeta", + "description": "Al hacer clic en una carpeta (Bandeja de entrada, Enviados…) se borra la búsqueda activa y se muestra esa carpeta. Si está desactivado, la búsqueda se mantiene y se vuelve a ejecutar en la carpeta a la que cambies." + }, + "request_read_receipt": { + "label": "Solicitar confirmaciones de lectura de forma predeterminada", + "description": "Activar previamente la solicitud de confirmación de lectura al redactar un mensaje nuevo." + }, + "read_receipt_response": { + "label": "Responder a las solicitudes de confirmación de lectura", + "description": "Qué hacer cuando un mensaje entrante solicita una confirmación de lectura.", + "ask": "Preguntar cada vez", + "always": "Enviar siempre", + "never": "No enviar nunca" + }, + "title": "Comportamiento del Correo", + "description": "Configure cómo se manejan los correos", + "mark_read": { + "label": "Marcar como Leído", + "description": "Cuándo marcar correos como leídos al abrirlos", + "instant": "Instantáneamente", + "delay_3s": "Después de 3 segundos", + "delay_5s": "Después de 5 segundos", + "never": "Nunca" + }, + "delete_action": { + "label": "Acción al Eliminar", + "description": "Qué sucede cuando elimina un correo", + "trash": "Mover a Papelera", + "trash_and_read": "Mover a Papelera y marcar como leído", + "permanent": "Eliminar Permanentemente", + "warning": "Los correos se eliminarán permanentemente y no se podrán recuperar. Esta acción es irreversible." + }, + "message_spacing": { + "label": "Espaciado del mensaje", + "description": "Margen alrededor del cuerpo del mensaje en el lector", + "auto": "Automático (recomendado)", + "always": "Añadir espaciado siempre", + "edge": "De borde a borde" + }, + "plain_text_font": { + "label": "Fuente de texto sin formato", + "description": "Fuente utilizada para los correos de texto sin formato", + "mono": "Monoespaciada", + "sans": "Normal (igual que la aplicación)" + }, + "archive_mode": { + "label": "Archivar en", + "description": "Cómo organizar los correos al archivar", + "single": "Una sola carpeta", + "year": "Una carpeta por año", + "month": "Una carpeta por mes", + "reorganize": "Reorganizar archivo existente", + "reorganize_success": "{count, plural, =0 {No hay correos para reorganizar} =1 {1 correo reorganizado} other {# correos reorganizados}}", + "reorganize_error": "Error al reorganizar el archivo" + }, + "permanently_delete_junk": { + "label": "Eliminar spam permanentemente", + "description": "Eliminar permanentemente los correos de la carpeta Spam en lugar de moverlos a la Papelera" + }, + "confirm_bulk_delete": { + "label": "Confirmar antes de eliminar varios mensajes", + "description": "Preguntar antes de mover varios mensajes seleccionados a la papelera. La eliminación definitiva siempre pregunta." + }, + "mail_layout": { + "label": "Diseño del correo", + "description": "Elija entre el panel de lectura dividido clásico y un flujo de lectura enfocado estilo Gmail.", + "split": "Panel dividido", + "split_description": "Mantener la lista de mensajes y el panel de lectura visibles uno al lado del otro.", + "focus": "Lista enfocada", + "focus_description": "Mostrar una línea por mensaje y abrir el correo a ancho completo manteniendo visible la barra lateral de carpetas.", + "horizontal": "Panel de lectura abajo", + "horizontal_description": "Muestra la lista de mensajes arriba y abre el mensaje seleccionado en un panel de lectura debajo." + }, + "list_columns": { + "label": "Mostrar la lista en columnas", + "description": "Muestra las filas de una línea como una tabla con las columnas De, Asunto, Fecha y Tamaño bajo un encabezado." + }, + "disable_threading": { + "label": "Desactivar agrupación de conversaciones", + "description": "Mostrar correos como mensajes individuales en lugar de agruparlos por conversación" + }, + "plain_text_mode": { + "label": "Solo texto sin formato", + "description": "Desactivar el editor de texto enriquecido y enviar todos los correos solo como texto sin formato, incluyendo respuestas y reenvíos" + }, + "auto_select_reply_identity": { + "label": "Seleccionar dirección de respuesta automáticamente", + "description": "Al responder, cambia automáticamente la dirección del remitente a la identidad que recibió el mensaje original" + }, + "reply_identity_match": { + "label": "Coincidencia de la dirección de recepción", + "description": "Qué direcciones de recepción cuentan como tuyas. «Solo la dirección exacta» elige únicamente una de tus identidades configuradas; «mismo dominio» también trata cualquier otra dirección de tus dominios de identidad como un alias catch-all y reescribe la cabecera De con ella. Elige la dirección exacta si esas direcciones son listas de distribución.", + "exact": "Solo la dirección exacta", + "domain": "Cualquier dirección de mis dominios" + }, + "signature_position": { + "label": "Posición de la firma", + "description": "Dónde insertar tu firma en respuestas y reenvíos. Encima del texto citado se lee de forma natural como cierre de la respuesta; debajo mantiene el mensaje original contiguo.", + "above_quote": "Antes del texto citado", + "below_quote": "Después del texto citado" + }, + "signature_separator": { + "label": "Delimitador de firma", + "description": "Anteponer a la firma la línea delimitadora estándar \"-- \" (RFC 3676). Desactiva si prefieres pasar directamente del mensaje a la firma." + }, + "sub_address_delimiter": { + "label": "Delimitador de sub-dirección", + "description": "Carácter que separa tu nombre de usuario de la etiqueta de sub-dirección. Usa el delimitador que utilice tu servidor de correo (por ejemplo, usuario{delimiter}etiqueta@dominio.com).", + "option": "{delimiter} (usuario{delimiter}etiqueta@dominio.com)", + "custom": "Personalizado…", + "custom_input_label": "Carácter delimitador personalizado" + }, + "show_preview": { + "label": "Mostrar Vista Previa", + "description": "Mostrar vista previa del correo en la lista", + "focus_description": "Mostrar texto de vista previa en línea dentro de la lista de mensajes enfocada de una línea" + }, + "show_verification_codes": { + "label": "Mostrar códigos de verificación", + "description": "Ofrecer el código de los correos de inicio de sesión y confirmación para copiarlo con un clic, en la lista durante un día y en el mensaje" + }, + "attachment_click_action": { + "label": "Acción al hacer clic en adjuntos", + "description": "Elige si al hacer clic en un archivo adjunto se abre una vista previa o se descarga de inmediato", + "preview": "Mostrar vista previa cuando sea posible", + "download": "Descargar inmediatamente" + }, + "attachment_position": { + "label": "Posición del adjunto", + "description": "Dónde mostrar los adjuntos en el encabezado del correo", + "beside-sender": "Junto al remitente", + "below-header": "Debajo del encabezado" + }, + "emails_per_page": { + "10": "10 correos", + "25": "25 correos", + "50": "50 correos", + "100": "100 correos", + "label": "Correos por Página", + "description": "Número de correos a cargar a la vez" + }, + "always_light_mode": { + "label": "Mostrar correos siempre en modo claro", + "description": "Mostrar el contenido del correo en modo claro incluso cuando la aplicación está en modo oscuro, evitando problemas de conversión" + }, + "external_content": { + "label": "Contenido Externo", + "description": "Cómo manejar imágenes y contenido externo", + "ask": "Preguntar siempre", + "block": "Bloquear siempre", + "allow": "Permitir siempre" + }, + "trusted_senders": { + "label": "Remitentes de Confianza", + "description": "Administre remitentes cuyas imágenes se cargan automáticamente", + "count_zero": "Ninguno", + "count_one": "1 remitente", + "count_other": "{count} remitentes", + "modal_title": "Remitentes de Confianza", + "empty_title": "Aún no hay remitentes de confianza", + "empty_description": "Al ver un correo con imágenes bloqueadas, haga clic en \"Confiar siempre en este remitente\" para agregarlo aquí.", + "add_manually": "Agregar remitente manualmente", + "add_button": "Agregar", + "add_placeholder": "Ingrese dirección de correo", + "search_placeholder": "Buscar remitentes...", + "no_results": "Ningún remitente coincide con su búsqueda", + "remove": "Eliminar", + "close": "Cerrar", + "invalid_email": "Por favor ingrese una dirección de correo válida", + "already_added": "Este remitente ya es de confianza", + "save_error": "No se pudo guardar - revisa el registro de depuración de Contactos para más detalles", + "use_address_book_label": "Sincronizar con la libreta de direcciones", + "use_address_book_description": "Guarda remitentes de confianza en una libreta \"Remitentes de confianza\" para sincronizarlos en todos tus dispositivos" + }, + "hover_actions": { + "label": "Acciones rápidas al pasar el ratón", + "description": "Elige qué acciones rápidas aparecen al pasar el ratón sobre un correo en la lista", + "delete": "Eliminar", + "star": "Marcar / Desmarcar estrella", + "mark_read": "Marcar como leído / no leído", + "archive": "Archivar", + "tag": "Etiqueta", + "spam": "Marcar como spam", + "not_spam": "No es spam", + "none_selected": "No hay acciones seleccionadas", + "mode_label": "Modo de visualización", + "mode_inline": "En línea", + "mode_floating": "Flotante", + "corner_label": "Posición flotante", + "corner_top-left": "Arriba izquierda", + "corner_top-right": "Arriba derecha", + "corner_bottom-left": "Abajo izquierda", + "corner_bottom-right": "Abajo derecha" + }, + "default_mail_program": { + "label": "Programa de correo predeterminado", + "description": "Registrar {appName} como su programa de correo predeterminado para enlaces mailto:", + "button": "Establecer como predeterminado", + "success": "El navegador solicitó establecer como predeterminado", + "error": "Su navegador no admite esta función" + }, + "attachment_reminder": { + "label": "Recordatorio de adjuntos", + "description": "Advertir antes de enviar cuando su mensaje menciona adjuntos pero no hay ninguno adjuntado", + "keywords_label": "Palabras clave de activación", + "keywords_description": "Palabras o frases que activan el recordatorio cuando se encuentran en su mensaje", + "add_placeholder": "Añadir palabra clave...", + "add": "Add", + "remove": "Eliminar" + }, + "empty_subject_warning": { + "label": "Aviso de asunto vacío", + "description": "Pedir confirmación antes de enviar un mensaje sin asunto." + }, + "recipient_mentions": { + "label": "Mencionar destinatarios con @", + "description": "Al escribir @ en el mensaje se ofrecen sus destinatarios y se inserta el nombre de pila del elegido. Intro o Tab elige el nombre resaltado." + }, + "mention_adds_recipient": { + "label": "Contactos en las menciones con @", + "description": "Al escribir @ también se ofrecen tus contactos y personas del directorio. Si eliges a alguien que aún no es destinatario, se añade al mensaje.", + "to": "Añadir a Para", + "cc": "Añadir a CC", + "off": "Solo destinatarios" + }, + "hide_inline_image_attachments": { + "label": "Ocultar imágenes incrustadas de los adjuntos", + "description": "Las imágenes incrustadas en el cuerpo del mensaje no se listan como adjuntos separados" + }, + "attachment_image_previews": { + "label": "Mostrar vistas previas de imágenes en adjuntos", + "description": "Mostrar las imágenes adjuntas como tarjetas de miniatura en lugar de iconos de archivo genéricos" + }, + "send_delay": { + "label": "Deshacer envío / demora de envío", + "description": "Retrasa envíos normales con una breve ventana del servidor.", + "off": "Desactivado", + "seconds": "{seconds} segundos", + "unsupported": "La cuenta actual no anuncia compatibilidad con envío demorado. La opción seguirá guardada para otras cuentas." + }, + "return_to_list_after_action": { + "label": "Volver a la lista tras eliminar o marcar como no leído", + "description": "Después de eliminar o marcar como no leído el mensaje abierto, volver a la lista de mensajes en lugar de abrir el mensaje siguiente." + }, + "swipe_right_action": { + "label": "Acción al deslizar a la derecha (móvil)", + "description": "En dispositivos táctiles, la acción que se realiza al deslizar un mensaje hacia la derecha en la lista." + }, + "swipe_left_action": { + "label": "Acción al deslizar a la izquierda (móvil)", + "description": "En dispositivos táctiles, la acción que se realiza al deslizar un mensaje hacia la izquierda en la lista." + }, + "swipe_actions": { + "none": "Ninguna", + "archive": "Archivar", + "delete": "Eliminar", + "mark_read": "Alternar leído", + "star": "Alternar destacado", + "spam": "Marcar como spam" + }, + "rtl_editing": { + "label": "Compatibilidad con edición de derecha a izquierda", + "description": "Añade un botón de dirección a la barra de herramientas del editor para configurar los párrafos de izquierda a derecha o de derecha a izquierda" + } + }, + "composer": { + "title": "Editor", + "description": "Configure los ajustes de redacción de correos", + "autosave": { + "label": "Intervalo de Guardado Automático", + "description": "Con qué frecuencia guardar borradores automáticamente", + "30s": "Cada 30 segundos", + "1m": "Cada minuto", + "2m": "Cada 2 minutos", + "5m": "Cada 5 minutos" + }, + "send_confirmation": { + "label": "Confirmación de Envío", + "description": "Solicitar confirmación antes de enviar correos" + }, + "default_reply": { + "label": "Modo de Respuesta Predeterminado", + "description": "Acción predeterminada al hacer clic en responder", + "reply": "Responder", + "reply_all": "Responder a Todos" + } + }, + "privacy": { + "title": "Privacidad y Seguridad", + "description": "Administre su configuración de privacidad y seguridad", + "external_images": { + "label": "Bloquear Imágenes Externas", + "description": "Prevenir el seguimiento a través de imágenes externas" + }, + "session_timeout": { + "label": "Tiempo de Espera de Sesión", + "description": "Cerrar sesión automáticamente después de inactividad", + "never": "Nunca", + "30m": "30 minutos", + "1h": "1 hora", + "4h": "4 horas" + }, + "clear_cache": { + "label": "Limpiar Caché", + "description": "Eliminar datos en caché y archivos temporales", + "button": "Limpiar Caché", + "confirm": "¿Está seguro de que desea limpiar la caché?", + "success": "Caché limpiada exitosamente" + } + }, + "account": { + "title": "Cuenta", + "description": "Vea la información de su cuenta", + "name_label": "Nombre para mostrar", + "username_label": "Nombre de usuario", + "account_type_label": "Tipo de cuenta", + "auth_method_label": "Autenticación", + "auth_method_oauth": "Inicio de sesión único (OAuth/OIDC)", + "auth_method_basic": "Contraseña", + "auth_method_token": "Token de acceso", + "demo_account": "Cuenta de demostración", + "email": { + "label": "Dirección de Correo", + "value": "{email}" + }, + "server": { + "label": "Servidor JMAP", + "value": "{server}" + }, + "storage": { + "label": "Uso de Almacenamiento", + "used": "{used} de {total} usado", + "percentage": "{percent}% usado" + }, + "last_sync": { + "label": "Última Sincronización", + "value": "{time}" + }, + "accounts": { + "title": "Cuentas conectadas", + "description": "Arrastra para reordenar cómo aparecen las cuentas en el menú desplegable", + "active": "Cuenta actualmente activa", + "default_badge": "Cuenta predeterminada", + "set_default": "Establecer como predeterminada", + "switch_to": "Cambiar a esta cuenta", + "move_up": "Mover arriba", + "move_down": "Mover abajo", + "drag_handle": "Arrastra para reordenar", + "add": "Añadir cuenta" + }, + "backup": { + "title": "Copia de la lista de cuentas", + "description": "Guarda en el servidor la lista de cuentas de este navegador, cifrada con una contraseña que solo tú conoces, y recupérala en otro dispositivo. Las contraseñas de los buzones no se guardan: inicias sesión tú en cada cuenta restaurada.", + "description_passwords": "Guarda en el servidor la lista de cuentas de este navegador, cifrada con una contraseña que solo tú conoces, y recupérala en otro dispositivo. Puedes incluir las contraseñas de los buzones, así las cuentas restauradas inician sesión solas.", + "status_none": "No hay ninguna lista guardada para {account}.", + "status_saved": "Hay una lista guardada para {account}.", + "save": "Guardar lista", + "restore": "Restaurar lista", + "delete": "Eliminar lista guardada", + "cancel": "Cancelar", + "delete_confirm": "¿Eliminar del servidor la lista de cuentas guardada?", + "password": "Contraseña de la copia", + "password_hint": "Al menos 10 caracteres. No se puede recuperar: si la olvidas, vuelve a guardar la lista con una nueva.", + "restore_hint": "La contraseña elegida al guardar la lista.", + "confirm": "Confirmar contraseña", + "include_passwords": "Incluir las contraseñas de los buzones", + "include_passwords_hint": "Las cuentas iniciadas aquí con contraseña iniciarán sesión solas tras restaurar. Quien conozca la contraseña de la copia entra en todos los buzones de la lista.", + "saved": "Lista guardada ({count, plural, one {# cuenta} other {# cuentas}}).", + "restored": "Cuentas añadidas: {added}. Cuentas ya presentes y actualizadas: {updated}. Abre cada cuenta nueva desde la lista de cuentas para iniciar sesión.", + "restored_signed_in": "Cuentas añadidas: {added}. Cuentas ya presentes y actualizadas: {updated}.", + "skipped": "{count, plural, one {# cuenta no se ha añadido} other {# cuentas no se han añadido}}: se ha alcanzado el límite de cuentas.", + "saved_passwords": "Contraseñas incluidas para {count, plural, one {# cuenta} other {# cuentas}}.", + "signed_in": "Sesión iniciada en {count, plural, one {# cuenta} other {# cuentas}} con la contraseña guardada.", + "sign_in_failed": "{count, plural, one {# contraseña guardada fue rechazada} other {# contraseñas guardadas fueron rechazadas}}: inicia sesión en esas cuentas desde la lista de cuentas.", + "deleted": "Lista guardada eliminada.", + "errors": { + "mismatch": "Las contraseñas no coinciden.", + "password_length": "Usa al menos 10 caracteres.", + "unlock_failed": "Contraseña incorrecta, o la lista se guardó para otra cuenta.", + "owner_signin_required": "Vuelve a iniciar sesión en esta cuenta y reinténtalo.", + "https_required": "Se necesita una conexión segura (HTTPS).", + "storage_failed": "El servidor no ha podido leer ni guardar la lista. Inténtalo más tarde." + } + }, + "shared_accounts": { + "title": "Compartido conmigo", + "description": "Cuentas de grupo y compartidas que puedes gestionar. Selecciona una para editar sus filtros, respuesta de vacaciones, calendarios y contactos.", + "shared_label": "Cuenta compartida" + } + }, + "security": { + "title": "Seguridad de la cuenta", + "description": "Administre su contraseña, autenticación de dos factores y configuración de seguridad", + "detecting": "Detectando capacidades del servidor...", + "not_available": "La gestión de seguridad de la cuenta no está disponible para este servidor de correo. Es posible que los permisos requeridos estén deshabilitados. Consulte la documentación para obtener más detalles.", + "password": { + "title": "Cambiar contraseña", + "current": "Contraseña actual", + "new": "Nueva contraseña", + "confirm": "Confirmar nueva contraseña", + "submit": "Cambiar contraseña", + "success": "Contraseña cambiada exitosamente", + "error_title": "Error al cambiar la contraseña", + "error_mismatch": "Las nuevas contraseñas no coinciden", + "error_min_length": "La contraseña debe tener al menos 8 caracteres", + "error_generic": "No se pudo cambiar la contraseña" + }, + "display_name": { + "label": "Nombre para mostrar", + "description": "Su nombre tal como aparece en el servidor", + "placeholder": "Ingrese su nombre para mostrar", + "save": "Guardar", + "success": "Nombre para mostrar actualizado", + "error": "No se pudo actualizar el nombre para mostrar" + }, + "totp": { + "section_title": "Autenticación de dos factores", + "label": "Autenticación TOTP", + "description": "Añada una capa adicional de seguridad con una contraseña de un solo uso basada en tiempo", + "active": "Habilitado", + "inactive": "Deshabilitado", + "enabled": "Autenticación de dos factores habilitada", + "disabled": "Autenticación de dos factores deshabilitada", + "enable_error": "No se pudo habilitar 2FA", + "disable_error": "No se pudo deshabilitar 2FA", + "setup_instructions": "Copie esta URL en su aplicación de autenticación (Google Authenticator, Authy, etc.):", + "verification_code": "Código de verificación", + "confirm": "Confirmar", + "disable": "Desactivar", + "disable_confirm_prompt": "Introduce tu contraseña para desactivar la autenticación de dos factores.", + "password_required": "Se requiere la contraseña", + "code_required": "Se requiere el código de verificación", + "code_invalid": "Código de verificación no válido. Revisa tu aplicación de autenticación e inténtalo de nuevo." + }, + "app_passwords": { + "title": "Contraseñas de aplicación", + "description": "Cree contraseñas para aplicaciones que no admiten autenticación de dos factores", + "add": "Agregar", + "create": "Crear", + "cancel": "Cancelar", + "generate": "Generar", + "name_label": "Nombre de la aplicación", + "name_placeholder": "p. ej. Thunderbird, iPhone Mail", + "password_label": "Contraseña (dejar vacío para auto-generar)", + "password_placeholder": "Auto-generada si está vacío", + "added": "Contraseña de aplicación creada", + "removed": "Contraseña de aplicación eliminada", + "add_error": "No se pudo crear la contraseña de aplicación", + "remove_error": "No se pudo eliminar la contraseña de aplicación", + "none": "No hay contraseñas de aplicación configuradas", + "done": "Hecho", + "expires_label": "Caduca (opcional)", + "copy_now_warning": "Copia esta contraseña ahora - no se volverá a mostrar.", + "allowed_ips_label": "IPs permitidas (opcional)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Separadas por coma o espacio. Dejar vacío para permitir cualquier IP." + }, + "api_keys": { + "title": "Claves API", + "description": "Crea claves API para scripts e integraciones que se comunican directamente con el servidor", + "name_label": "Nombre de la clave", + "name_placeholder": "p. ej. Script de copia de seguridad, CI runner", + "copy_now_warning": "Copia esta clave API ahora - no se mostrará de nuevo.", + "added": "Clave API creada", + "removed": "Clave API eliminada", + "add_error": "No se pudo crear la clave API", + "remove_error": "No se pudo eliminar la clave API", + "none": "No hay claves API configuradas" + }, + "encryption": { + "section_title": "Cifrado en reposo", + "label": "Cifrado de correo electrónico", + "description": "Cifre los correos almacenados en el servidor para mayor privacidad", + "active": "Cifrado {type} habilitado", + "inactive": "Deshabilitado", + "enabled": "Cifrado en reposo habilitado", + "disabled_success": "Cifrado en reposo deshabilitado", + "error": "No se pudieron actualizar las configuraciones de cifrado", + "algorithm_label": "Algoritmo de cifrado", + "encrypt_on_append": "Cifrar los correos nuevos al subirlos", + "allow_spam_training": "Permitir el entrenamiento antispam antes de cifrar los correos", + "enable_encryption": "Habilitar el cifrado", + "enable_tooltip": "Habilitar el cifrado con esta clave", + "configure_title": "Habilitar el cifrado con esta clave", + "enable_button": "Habilitar", + "disable_tooltip": "Deshabilitar el cifrado", + "enabled_success": "Cifrado en reposo habilitado" + }, + "email_client": { + "title": "Configuración del cliente de correo", + "description": "Use estas credenciales para configurar su cliente de correo de escritorio o móvil (Thunderbird, Apple Mail, Outlook, etc.)", + "jmap_username_label": "Nombre de usuario JMAP", + "copy": "Copiar", + "copied": "Copiado", + "password_instructions": "Use su nombre de usuario JMAP indicado arriba junto con una contraseña de aplicación para iniciar sesión en su cliente de correo. Cree una contraseña de aplicación en la sección anterior si aún no lo ha hecho." + }, + "link_device": { + "title": "Vincular aplicación móvil", + "description": "Inicie sesión en la aplicación Bulwark Mail de su teléfono sin escribir allí su contraseña. Su teléfono obtiene su propio inicio de sesión, así que cerrar sesión aquí no la cierra en él.", + "generate": "Mostrar código QR", + "regenerate": "Mostrar un código nuevo", + "instructions": "Abra la aplicación Bulwark Mail y toque \"Scan a sign-in code\" en la pantalla de inicio de sesión (o \"Add account\" → \"Scan a sign-in code\" si ya ha iniciado sesión) y, a continuación, apunte la cámara aquí.", + "expires_in": "Este código caduca en {seconds} segundos. Solo se puede usar una vez.", + "expired": "Este código ha caducado.", + "error": "No se pudo crear un código de vinculación. Inténtelo de nuevo.", + "password_prompt": "Introduzca su contraseña para confirmar que es usted. Su teléfono obtiene su propio inicio de sesión; su contraseña no se envía al teléfono.", + "show_password": "Mostrar contraseña", + "hide_password": "Ocultar contraseña", + "continue": "Continuar", + "error_wrong_password": "Contraseña incorrecta. Inténtelo de nuevo.", + "totp_prompt": "Introduzca el código de su aplicación de autenticación.", + "error_password_or_code": "La contraseña o el código son incorrectos.", + "error_not_signed_in": "Su sesión ha caducado. Vuelva a iniciar sesión para vincular un dispositivo.", + "error_impersonation": "No se pueden vincular dispositivos mientras consulta el buzón de otro usuario.", + "error_too_many_attempts": "Demasiados intentos. Espere un momento e inténtelo de nuevo.", + "error_session_secret": "La vinculación de dispositivos no está configurada en este servidor (falta SESSION_SECRET). Consulte a su administrador.", + "error_server_unreachable": "No se pudo conectar con su servidor de correo. Inténtelo de nuevo.", + "error_pairing_unavailable": "Su servidor de correo no permite vincular dispositivos de esta forma. Consulte a su administrador.", + "qr_alt": "Código QR de inicio de sesión", + "link_label": "Enlace de inicio de sesión", + "copy_link": "Copiar enlace de inicio de sesión", + "copied": "Copiado", + "copy_hint": "¿No tiene cámara? Copie el enlace y péguelo en la aplicación con \"Paste sign-in link\".", + "success_title": "Sesión iniciada en su teléfono", + "success_body": "Puede cerrar esta página. Para vincular otro dispositivo, muestre un código nuevo.", + "link_another": "Vincular otro dispositivo", + "use_sso": "Usar el inicio de sesión único", + "error_insecure_server": "La aplicación móvil solo se conecta a servidores de correo por https, y este usa http." + }, + "public_keys": { + "name_label": "Nombre de la clave pública", + "name_placeholder": "ej. Mi clave", + "key_label": "Clave pública (S/MIME o PGP en formato ASCII)", + "key_placeholder": "Pega aquí tu clave pública", + "none": "No hay claves públicas configuradas" + } + }, + "identities": { + "title": "Identidades de Envío", + "description": "Administre las direcciones de correo desde las que puede enviar", + "identities_count": { + "label": "Sus Identidades", + "description": "Direcciones de correo configuradas para enviar", + "count_zero": "Sin identidades", + "count_one": "1 identidad", + "count_other": "{count} identidades" + }, + "manage": "Administrar Identidades", + "sub_addressing": { + "label": "Sub-Direccionamiento", + "description": "Use etiquetas como usuario+etiqueta@dominio.com para organizar el correo entrante", + "learn_more": "Más Información" + } + }, + "vacation": { + "title": "Ausencia", + "description": "Gestionar automáticamente los correos entrantes mientras está ausente", + "loading": "Cargando configuración de respuesta automática...", + "not_supported": "Su servidor de correo no admite respuestas automáticas de ausencia.", + "fetch_error": "Error al cargar la configuración de respuesta automática. Inténtelo de nuevo.", + "status": { + "label": "Responder automáticamente", + "description": "Enviar una respuesta automática a quienes le envíen un correo", + "active": "Activo", + "inactive": "Inactivo" + }, + "date_range": { + "start": "Fecha de inicio", + "start_description": "Dejar vacío para sin límite de inicio", + "end": "Fecha de fin", + "end_description": "Dejar vacío para sin límite de fin" + }, + "message": { + "subject_label": "Asunto", + "subject_description": "Línea de asunto de la respuesta automática", + "subject_placeholder": "Fuera de la oficina", + "body_label": "Cuerpo del mensaje", + "body_description": "Contenido del mensaje en texto plano", + "body_placeholder": "Gracias por su correo. Actualmente estoy fuera de la oficina y responderé cuando regrese.", + "html_label": "Mensaje con formato (HTML)", + "html_description": "Añade una versión con formato, enlaces y estilo. Los destinatarios cuyo cliente de correo no pueda mostrarla verán el texto sin formato de arriba.", + "html_placeholder": "Escribe una respuesta de ausencia con formato…" + }, + "preview": { + "title": "Vista previa", + "show": "Mostrar vista previa", + "hide": "Ocultar vista previa" + }, + "save": "Guardar cambios", + "saving": "Guardando...", + "warnings": { + "end_before_start": "La fecha de fin debe ser posterior a la fecha de inicio", + "invalid_date": "Introduce una fecha de inicio y de fin válidas con hora o deja el campo vacío", + "start_in_past": "La fecha de inicio está en el pasado", + "empty_body": "El cuerpo del mensaje está vacío - los destinatarios recibirán una respuesta en blanco", + "subject_too_long": "El asunto es demasiado largo para el servidor (máximo {max} bytes).", + "body_too_long": "El mensaje es demasiado largo para el servidor (máximo {max} bytes, formato incluido).", + "forward_address": "Introduce una dirección válida para el reenvío", + "not_running": "El reenvío o los destinatarios de la respuesta están guardados, pero ahora no están activos. Guarda para volver a activarlos." + }, + "forward": { + "title": "Reenvío", + "description": "Reenvía los mensajes entrantes a otra dirección, con o sin respuesta automática, dentro del período indicado arriba (sin fecha de fin, hasta que se desactive). El correo no deseado no se reenvía.", + "enabled_label": "Reenviar mensajes", + "to_label": "Reenviar a", + "to_placeholder": "correo@ejemplo.com", + "keep_label": "Mantener en la bandeja de entrada", + "keep_description": "Si no, el mensaje solo se reenvía: no se conserva en este buzón y no se le aplica ninguna regla de filtro." + }, + "audience": { + "label": "Responder a", + "description": "Remitentes internos: {domains}", + "all": "Todos los remitentes", + "internal": "Solo remitentes internos", + "external": "Solo remitentes externos" + }, + "auto_reply": { + "title": "Respuesta automática" + } + }, + "folders": { + "role_memos": "Notas", + "title": "Carpetas", + "description": "Administre sus carpetas de correo y asigne roles estándar", + "folder_list": "Sus carpetas", + "folder_list_description": "Haga clic en el icono de una carpeta para personalizarla", + "standard_roles": "Roles estándar de carpetas", + "standard_roles_description": "Asigne qué carpetas se utilizan para roles estándar como Bandeja de entrada, Enviados, Papelera, etc.", + "role_inbox": "Bandeja de entrada", + "role_drafts": "Borradores", + "role_sent": "Enviados", + "role_trash": "Papelera", + "role_junk": "Spam / No deseado", + "role_archive": "Archivo", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Ninguno", + "create_folder": "Crear carpeta", + "create_subfolder": "Crear subcarpeta", + "reorder": "Arrastra para reordenar", + "reorder_error": "No se pudieron reordenar las carpetas", + "move_error": "No se pudo mover la carpeta", + "select_folder": "Seleccionar {name}", + "select_hint": "Seleccionar. Mayús+clic selecciona todas las carpetas hasta la última en la que hiciste clic.", + "selected_count": "{count, plural, one {# carpeta seleccionada} other {# carpetas seleccionadas}}", + "move_to": "Mover a...", + "move_to_top_level": "Nivel superior", + "move": "Mover", + "clear_selection": "Borrar selección", + "folders_moved": "{count, plural, one {# carpeta movida} other {# carpetas movidas}}", + "move_partial_error": "{count, plural, one {No se pudo mover # carpeta} other {No se pudieron mover # carpetas}}", + "subfolder_of": "Dentro de {name}", + "subfolder_name": "Nombre de la subcarpeta", + "new_folder_name": "Nombre de la carpeta", + "rename": "Renombrar", + "change_icon": "Cambiar icono", + "count_mode": "Contador de {name}", + "count_mode_default": "Contador: predeterminado", + "count_mode_unread": "Solo no leídos", + "count_mode_all": "No leídos y total", + "count_mode_none": "Sin contador", + "delete": "Eliminar", + "confirm_delete": "¿Está seguro de que desea eliminar \"{name}\"? Los correos de esta carpeta se moverán a la Papelera.", + "create": "Crear", + "cancel": "Cancelar", + "no_folders": "Sin carpetas personalizadas", + "cannot_delete_role": "No se puede eliminar una carpeta con un rol estándar. Quite el rol primero.", + "folder_created": "Carpeta creada", + "folder_renamed": "Carpeta renombrada", + "folder_deleted": "Carpeta eliminada", + "role_updated": "Rol de carpeta actualizado", + "error_create": "Error al crear la carpeta", + "error_rename": "Error al renombrar la carpeta", + "error_delete": "Error al eliminar la carpeta", + "error_delete_has_children": "No se puede eliminar la carpeta: aún contiene subcarpetas. Elimínelas o muévalas primero.", + "error_delete_has_email": "No se puede eliminar la carpeta: aún contiene correos. Muévalos o elimínelos primero.", + "error_role": "Error al actualizar el rol de la carpeta" + }, + "advanced": { + "title": "Avanzado", + "description": "Opciones avanzadas y configuración de desarrollador", + "debug_mode": { + "label": "Modo de Depuración", + "description": "Habilitar registro detallado para solución de problemas" + }, + "debug_categories": { + "description": "Selecciona qué categorías registrar. Desactiva las categorías que no necesites para reducir el ruido en la consola.", + "jmap": "Cliente JMAP", + "jmap_description": "Operaciones de buzón, obtención de correo y solicitudes del protocolo JMAP", + "calendar": "Calendario", + "calendar_description": "Eventos del calendario, importaciones y mensajes de programación", + "tasks": "Tareas", + "tasks_description": "Creación, obtención y actualizaciones de tareas del calendario", + "auth": "Autenticación", + "auth_description": "Inicio de sesión, TOTP, intercambio de tokens y gestión de sesiones", + "filters": "Filtros", + "filters_description": "Reglas de filtro Sieve y scripts de vacaciones", + "email": "Visualización de correo", + "email_description": "Renderizado de correos, procesamiento TNEF y marcado como leído", + "push": "Notificaciones push", + "push_description": "Configuración y entrega de notificaciones push", + "contacts": "Contactos y libretas de direcciones", + "contacts_description": "Sincronización de contactos, operaciones de libreta y remitentes de confianza" + }, + "settings_sync": { + "label": "Sincronización de ajustes", + "description": "Sincronice sus ajustes entre navegadores y dispositivos" + }, + "sender_favicons": { + "label": "Favicons de remitente", + "description": "Mostrar iconos de sitios web como fotos de perfil para remitentes empresariales", + "refresh_label": "Actualizar iconos de remitentes", + "refresh_description": "Volver a obtener los iconos de los sitios web, por ejemplo cuando un remitente ha cambiado el suyo", + "refresh_button": "Actualizar", + "refresh_done": "Los iconos de los remitentes se volverán a obtener" + }, + "show_avatars_in_junk": { + "label": "Mostrar avatares en la carpeta de spam", + "description": "Mostrar imágenes de perfil y favicons de remitentes en la carpeta de spam. Desactivado por defecto para no dar apariencia legítima a los intentos de phishing." + }, + "keyboard_shortcuts": { + "label": "Atajos de Teclado", + "description": "Ver atajos de teclado disponibles", + "button": "Ver Atajos" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Restablecer Configuración", + "description": "Restaurar toda la configuración a los valores predeterminados", + "button": "Restablecer a Predeterminados" + }, + "export_settings": { + "label": "Exportar Configuración", + "description": "Descargar su configuración como JSON", + "button": "Exportar" + }, + "import_settings": { + "label": "Importar Configuración", + "description": "Cargar configuración desde archivo JSON", + "button": "Importar" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Código fuente" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Haz clic en el spam y el phishing antes de que lleguen a tu bandeja de entrada. Deja pasar el correo real. Si pierdes los tres escudos, la bandeja cae.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Correo real" + }, + "click_to_block": "Haz clic para bloquear", + "let_through": "Déjalo pasar", + "start": "Empezar", + "play_again": "Jugar de nuevo", + "score": "Puntos", + "combo": "Bonus de racha", + "shields_left": "Quedan {count} de {total} escudos", + "seconds": "{seconds} s", + "best": "Récord: {score}", + "new_best": "Nuevo récord", + "held": "Bandeja defendida", + "overrun": "Bandeja invadida", + "stats": { + "caught": "Bloqueados", + "delivered": "Entregados", + "mistakes": "Errores" + } + } + }, + "sidebar_apps": { + "title": "Apps de barra lateral", + "description": "Gestionar aplicaciones y enlaces personalizados en la barra lateral", + "keep_loaded": "Mantener apps cargadas", + "keep_loaded_description": "Mantener las apps en línea ejecutándose en segundo plano al cambiar entre ellas para evitar recargas", + "managed_title": "Proporcionadas por tu administrador", + "managed_description": "Estas aplicaciones están configuradas para todos y no se pueden cambiar.", + "manage_title": "Apps personalizadas", + "manage_description": "Agregar, editar o eliminar apps personalizadas de la barra lateral" + }, + "contacts": { + "title": "Contactos", + "description": "Importar y exportar sus contactos", + "import_label": "Importar contactos", + "import_description": "Importar contactos desde un archivo vCard (.vcf)", + "export_label": "Exportar contactos", + "export_description": "Exportar todos los contactos como archivo vCard (.vcf)", + "manage_title": "Libretas de direcciones", + "manage_description": "Renombrar tus libretas de direcciones", + "no_address_books": "No se encontraron libretas de direcciones", + "categories_title": "Categorías", + "categories_description": "Renombrar categorías de contactos", + "no_categories": "No se encontraron categorías", + "group_by_letter_description": "Mostrar encabezados alfabéticos en la lista de contactos", + "group_by_letter_label": "Agrupar por primera letra", + "sort_by_last_name_label": "Ordenar por apellido", + "sort_by_last_name_description": "Ordenar la lista de contactos por apellido para que los miembros de una familia aparezcan juntos" + }, + "downloads": { + "title": "Descargas", + "description": "Personaliza cómo se nombran los correos y archivos adjuntos descargados.", + "reset": "Restaurar predeterminado", + "preview": "Vista previa:", + "email_template": { + "label": "Nombre del archivo de correo (.eml)", + "description": "Plantilla usada al exportar un correo o al arrastrarlo al sistema de archivos. La extensión .eml se añade automáticamente." + }, + "attachment_template": { + "label": "Nombre del archivo adjunto", + "description": "Plantilla usada al descargar o arrastrar un adjunto. Si omites '{filename}' y '{ext}', se conserva la extensión original." + }, + "bundle_template": { + "label": "Nombre de archivo .zip multi-correo", + "description": "Plantilla utilizada al arrastrar o descargar varios correos seleccionados como un único archivo .zip. La extensión .zip se añade automáticamente." + }, + "spaces": { + "label": "Espacios", + "description": "Reemplaza los espacios del nombre de archivo resultante por otro carácter.", + "keep": "Conservar espacios", + "underscore": "Reemplazar por _", + "dash": "Reemplazar por -" + }, + "lowercase": { + "label": "Minúsculas", + "description": "Forzar todo el nombre del archivo a minúsculas." + }, + "strip_diacritics": { + "label": "Eliminar diacríticos", + "description": "Convierte las letras acentuadas en sus equivalentes ASCII (ä → a, é → e). Útil para herramientas que estropean nombres Unicode." + }, + "collapse_separators": { + "label": "Combinar separadores repetidos", + "description": "Combina secuencias de espacios, guiones bajos o guiones en un único carácter." + }, + "after_export": { + "label": "Después de exportar", + "description": "Opcionalmente, mover el correo después de exportarlo como .eml.", + "keep": "Mantener en el buzón", + "archive": "Mover al archivo", + "trash": "Mover a la papelera" + } + }, + "filters": { + "title": "Filtros de correo", + "description": "Cree reglas para ordenar, etiquetar y gestionar automáticamente los correos entrantes", + "add_rule": "Agregar regla", + "no_rules": "Sin reglas de filtrado", + "vacation_active": "El respondedor de vacaciones está activo", + "vacation_active_description": "La respuesta automática está habilitada para los mensajes entrantes", + "vacation_configure": "Configurar", + "no_rules_description": "Cree reglas para organizar automáticamente sus correos entrantes", + "edit_rule": "Editar regla", + "new_rule": "Nueva regla", + "delete_rule": "Eliminar regla", + "delete_confirm": "¿Está seguro de que desea eliminar esta regla?", + "enable": "Activar", + "disable": "Desactivar", + "raw_editor": "Editor Sieve sin formato", + "raw_editor_warning": "Editar el script Sieve sin formato puede romper la edición visual de reglas. Los cambios realizados aquí anulan el constructor visual.", + "validate": "Validar", + "validation_success": "El script es válido", + "validation_error": "El script tiene errores", + "save": "Guardar reglas", + "saving": "Guardando...", + "saved": "Filtros guardados correctamente", + "save_failed": "Error al guardar los filtros", + "loading": "Cargando filtros...", + "not_supported": "Su servidor de correo no admite filtros de correo electrónico.", + "rule_name": "Nombre de la regla", + "rule_name_placeholder": "ej. Ordenar boletines", + "match_all": "Coincidir con TODAS las condiciones", + "match_any": "Coincidir con CUALQUIER condición", + "conditions": "Condiciones", + "add_condition": "Agregar condición", + "actions": "Acciones", + "add_action": "Agregar acción", + "stop_processing": "Detener el procesamiento de reglas posteriores", + "include_spam": "Mover también los mensajes marcados como spam", + "attachment_type_placeholder": "p. ej. pdf, doc, jpg", + "value_placeholder_multi": "Valor (varios separados por comas)", + "condition_fields": { + "attachment": "Adjunto", + "from": "De", + "to": "Para", + "cc": "Cc", + "subject": "Asunto", + "header": "Encabezado personalizado", + "size": "Tamaño", + "body": "Cuerpo", + "all": "Todos los mensajes" + }, + "comparators": { + "has_any": "está presente", + "has_type": "del tipo", + "contains": "contiene", + "not_contains": "no contiene", + "is": "es exactamente", + "not_is": "no es", + "starts_with": "comienza con", + "ends_with": "termina con", + "matches": "coincide con el patrón", + "greater_than": "es mayor que", + "less_than": "es menor que", + "address_is": "es la dirección", + "domain_is": "tiene el dominio" + }, + "action_types": { + "move": "Mover a carpeta", + "copy": "Copiar a carpeta", + "forward": "Reenviar a", + "mark_read": "Marcar como leído", + "star": "Destacar mensaje", + "add_label": "Agregar etiqueta", + "discard": "Descartar (eliminar silenciosamente)", + "reject": "Rechazar con mensaje", + "keep": "Mantener en la bandeja de entrada", + "stop": "Detener procesamiento" + }, + "move_to_folder": "Seleccionar carpeta", + "copy_to_folder": "Seleccionar carpeta", + "forward_to": "Reenviar a dirección de correo", + "forward_placeholder": "correo@ejemplo.com", + "forward_keep_copy": "Conservar una copia", + "forward_limit": "Límite de reenvíos por mensaje en este servidor: {count}. Los reenvíos adicionales se omiten.", + "forward_unavailable": "Este servidor no permite reenviar. Las acciones de reenvío de las reglas de filtro no tienen efecto.", + "reject_message": "Mensaje de rechazo", + "reject_placeholder": "Su correo ha sido rechazado", + "label_name": "Nombre de la etiqueta", + "label_placeholder": "Seleccionar etiqueta", + "header_name": "Nombre del encabezado", + "header_placeholder": "ej. X-Mailing-List", + "size_bytes": "Tamaño en bytes", + "size_placeholder": "ej. 1000000", + "system_managed": "Regla gestionada por el sistema", + "opaque_warning": "Este script fue editado fuera del constructor visual. Solo está disponible la edición Sieve sin formato.", + "open_sieve_editor": "Abrir editor Sieve", + "fetch_error": "Error al cargar los filtros", + "expanded_view": "Vista expandida", + "expanded_view_description": "Mostrar reglas de filtro con bloques detallados de condiciones y acciones", + "if": "Si", + "then": "Entonces", + "match_all_conditions": "todas coinciden", + "match_any_condition": "alguna coincide", + "and": "y", + "or": "o", + "cancel": "Cancelar", + "confirm_delete": "Eliminar", + "rule_list": "Reglas de filtrado", + "drag_to_reorder": "Arrastrar para reordenar", + "match_type": "Tipo de coincidencia", + "reset_to_visual": "Restablecer al constructor visual", + "reset_warning": "Esto descartará el script actual y comenzará de nuevo.", + "confirm_reset": "Restablecer", + "validation_empty_name": "El nombre de la regla es obligatorio", + "validation_empty_conditions": "Se requiere al menos una condición con un valor", + "validation_empty_actions": "Se requiere al menos una acción", + "templates_section": "Usar una plantilla", + "template_newsletters": "Mover newsletters a carpeta", + "template_receipts": "Archivar recibos automáticamente", + "template_important": "Marcar correos importantes", + "template_notifications": "Filtrar notificaciones", + "sieve_editor": { + "title": "Editor de script Sieve", + "warning": "Editar el script Sieve puede romper la edición visual de reglas. Los cambios realizados aquí anulan el constructor visual.", + "script_content": "Script Sieve", + "valid": "El script es válido", + "invalid": "El script tiene errores", + "save_warning": "Guardar sobrescribirá todas las reglas visuales. Esta acción no se puede deshacer. Haga clic en Guardar de nuevo para confirmar.", + "validating": "Validando...", + "validate": "Validar", + "cancel": "Cancelar", + "save": "Guardar", + "confirm_save": "Confirmar guardado", + "validation_failed": "Error en la solicitud de validación" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# condición} other {# condiciones}}", + "actions_count": "{count, plural, one {# acción} other {# acciones}}" + }, + "origin_external": "Externo", + "managed_by_tooltip": "Gestionado por {source}. Edítalo en esa aplicación o usa el editor Sieve sin procesar.", + "quick_rule_names": { + "move_sender": "Mover de {sender} a {folder}", + "move_domain": "Mover de @{domain} a {folder}", + "move_list": "Mover la lista {list} a {folder}", + "mark_read": "Marcar como leídos los de {sender}", + "tag": "Etiquetar como {tag} los de {sender}", + "block": "Bloquear {sender}", + "custom": "Mensajes de {sender}", + "custom_plain": "Nueva regla", + "senders": "{count, plural, one {# remitente} other {# remitentes}}" + }, + "suggestions": "Sugerencias", + "suggest_subject": "Asunto «{subject}»", + "suggest_to": "Para {address}", + "suggest_cc": "CC {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Aplicar también a los mensajes que ya hay en esta carpeta", + "apply_existing_unsupported": "Solo las reglas que comprueban el remitente, los destinatarios, el asunto o los encabezados, y que mueven, copian, marcan como leído, destacan o etiquetan, se pueden aplicar a mensajes existentes.", + "period_title": "Período", + "period_start": "Fecha de inicio", + "period_end": "Fecha de fin", + "validation_period_order": "La fecha de fin debe ser posterior a la fecha de inicio", + "period_toggle": "Activa solo durante un período", + "period_hint": "Se aplica a los mensajes que llegan durante este período. Deja un campo vacío para no limitar ese extremo.", + "validation_period_empty": "Introduce una fecha de inicio o de fin para el período", + "validation_period_invalid": "Introduce una fecha válida con hora para el período o deja el campo vacío", + "apply_existing_period": "Una regla con período solo actúa sobre los mensajes que llegan dentro de él, por lo que no se puede aplicar a los mensajes existentes.", + "period_status_scheduled": "programada", + "period_status_active": "activa", + "period_status_expired": "caducada" + }, + "templates": { + "title": "Plantillas de correo", + "description": "Crea plantillas reutilizables con variables de marcador de posición", + "add": "Nueva plantilla", + "edit": "Editar plantilla", + "name": "Nombre de la plantilla", + "name_placeholder": "ej.: Correo de seguimiento", + "category": "Categoría", + "category_placeholder": "ej.: Trabajo, Personal", + "subject": "Asunto", + "subject_placeholder": "Línea de asunto del correo", + "body": "Cuerpo", + "body_placeholder": "Contenido del correo...", + "recipients_placeholder": "email@example.com", + "identity": "Enviar como", + "default_identity": "Identidad predeterminada", + "favorite": "Favorito", + "cancel": "Cancelar", + "create": "Crear", + "update": "Actualizar", + "confirm_delete": "Eliminar", + "no_templates": "Sin plantillas", + "manage": "Gestionar plantillas", + "count": "{count, plural, one {# plantilla} other {# plantillas}}", + "export_import": "Exportar e importar", + "export_import_description": "Haz una copia de seguridad de tus plantillas o transfiérelas a otro dispositivo", + "export": "Exportar", + "import": "Importar", + "validation": { + "empty": "El nombre de la plantilla es obligatorio", + "too_long": "El nombre de la plantilla no debe superar los 200 caracteres" + } + }, + "files": { + "display": { + "title": "Visualización", + "description": "Configura cómo se muestran los archivos y carpetas" + }, + "default_view": { + "label": "Vista predeterminada", + "description": "Elige entre diseño de cuadrícula y lista", + "list": "Lista", + "grid": "Cuadrícula" + }, + "default_sort": { + "label": "Orden predeterminado", + "description": "Elige el orden predeterminado para los archivos", + "name": "Nombre", + "size": "Tamaño", + "modified": "Modificado" + }, + "sort_direction": { + "label": "Dirección de orden", + "description": "Elige orden ascendente o descendente", + "ascending": "Ascendente", + "descending": "Descendente" + }, + "icons": { + "title": "Iconos", + "description": "Configura la apariencia de los iconos de archivos" + }, + "show_icons": { + "label": "Mostrar iconos de archivos", + "description": "Mostrar iconos junto a archivos y carpetas" + }, + "colored_icons": { + "label": "Iconos de colores", + "description": "Usar iconos de colores en lugar de monocromáticos" + }, + "show_thumbnails": { + "label": "Mostrar miniaturas", + "description": "Mostrar vistas previas de imágenes en lugar de iconos" + }, + "behavior": { + "title": "Comportamiento", + "description": "Configura el comportamiento del explorador de archivos" + }, + "show_hidden": { + "label": "Mostrar archivos ocultos", + "description": "Mostrar archivos y carpetas que comienzan con un punto" + }, + "preview": { + "label": "Vista previa" + } + }, + "search_placeholder": "Buscar ajustes", + "search_clear": "Borrar búsqueda", + "search_no_results": "No hay ajustes coincidentes", + "scoped": { + "back": "Volver a mi cuenta", + "managing": "Gestionando: {name}" + } + }, + "errors": { + "page_error_title": "Algo salió mal", + "page_error_description": "Encontramos un error inesperado. Por favor, inténtelo de nuevo o regrese a la página principal.", + "sidebar_error": "No se pudieron cargar los buzones", + "email_list_error": "No se pudieron cargar los correos", + "viewer_error_title": "No se puede mostrar el correo", + "viewer_error_description": "Hubo un problema al renderizar este correo. Puede contener contenido no compatible.", + "composer_error": "No se pudo cargar el editor", + "settings_error_title": "Configuración no disponible", + "settings_error_description": "No se pudo cargar la configuración. Sus preferencias pueden no guardarse.", + "try_again": "Intentar de nuevo", + "reload": "Recargar", + "reload_emails": "Recargar correos", + "reload_settings": "Recargar configuración", + "retry": "Reintentar", + "go_home": "Ir a bandeja de entrada" + }, + "context_menu": { + "reply": "Responder", + "reply_all": "Responder a Todos", + "forward": "Reenviar", + "mark_read": "Marcar como Leído", + "mark_unread": "Marcar como No Leído", + "star": "Destacar", + "unstar": "Quitar Destacado", + "pin": "Anclar", + "unpin": "Desanclar", + "move_to": "Mover a...", + "copy_to_account": "Copiar a cuenta...", + "remove_from_folder": "Quitar de esta carpeta", + "archive": "Archivar", + "delete": "Eliminar", + "mark_as_spam": "Reportar spam", + "not_spam": "No es spam", + "tag": "Etiqueta", + "items_selected": "{count} correos seleccionados", + "forward_as_attachments": "Reenviar como archivos adjuntos", + "edit_draft": "Editar borrador", + "cancel_scheduled_send": "Cancelar envío", + "reschedule_send": "Reprogramar", + "cancel_and_edit": "Cancelar y editar", + "cancel_and_compose_again": "Cancelar y redactar de nuevo", + "rules": { + "title": "Reglas", + "move_from": "Mover siempre los mensajes de {sender}", + "move_from_domain": "Mover siempre los mensajes de @{domain}", + "move_from_list": "Mover siempre los mensajes de esta lista", + "mark_read_from": "Marcar siempre como leídos los mensajes de {sender}", + "tag_with": "Etiquetar siempre con", + "block": "{count, plural, one {Bloquear remitente} other {Bloquear # remitentes}}", + "no_junk": "Esta cuenta no tiene carpeta de correo no deseado", + "create": "Crear regla…", + "manage": "Administrar reglas", + "senders_count": "{count, plural, one {# remitente} other {# remitentes}}", + "cross_account": "Selecciona mensajes de una sola cuenta", + "opaque_hint": "Tus filtros se editaron a mano. Abre la configuración de filtros" , + "loading": "Cargando reglas…", + "load_failed": "No se pudieron cargar las reglas", + "none": "Aún no hay reglas", + "new_mail_only": "Solo para mensajes nuevos", + "running": "Ejecutando «{name}» en {folder}…" + }, + "find_emails_from": "Buscar correos de {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Marcar carpeta como leída", + "mark_folder_tree_read": "Marcar carpeta y subcarpetas como leídas", + "mark_all_folders_read": "Marcar todas las carpetas como leídas", + "new_subfolder": "Nueva subcarpeta...", + "new_folder": "Nueva carpeta...", + "rename": "Renombrar...", + "share": "Compartir...", + "import_email": "Importar .eml o .zip...", + "empty_folder": "Vaciar carpeta", + "empty_folder_generic": "Vaciar carpeta", + "delete_folder": "Eliminar carpeta", + "refresh": "Actualizar", + "hide_shared_account": "Ocultar de la barra lateral", + "mute_notifications": "Silenciar notificaciones", + "unmute_notifications": "Activar notificaciones", + "mute_account_notifications": "Silenciar notificaciones de esta cuenta", + "unmute_account_notifications": "Activar notificaciones de esta cuenta", + "reset_folder_notifications": "Restablecer notificaciones de carpetas", + "mark_all_confirm_title": "Marcar todas las carpetas como leídas", + "mark_all_confirm_message": "¿Marcar todos los mensajes no leídos de su cuenta personal como leídos?", + "delete_confirm_title": "Eliminar carpeta", + "delete_confirm_message": "¿Eliminar permanentemente la carpeta \"{name}\"? Esta acción no se puede deshacer.", + "prompt_new_subfolder": "Ingrese un nombre para la nueva subcarpeta.", + "prompt_new_folder": "Ingrese un nombre para la nueva carpeta.", + "prompt_rename": "Ingrese un nuevo nombre para esta carpeta.", + "toast_marked_read": "Carpeta marcada como leída", + "toast_marked_read_count": "Se marcaron {count, plural, one {1 mensaje} other {# mensajes}} como leídos", + "toast_already_read": "No hay mensajes no leídos", + "toast_marked_all_read": "Todas las carpetas marcadas como leídas", + "toast_emptied": "Carpeta vaciada", + "toast_folder_created": "Carpeta creada", + "toast_folder_renamed": "Carpeta renombrada", + "toast_folder_deleted": "Carpeta eliminada", + "toast_error_mark_read": "Error al marcar como leído", + "toast_error_empty": "Error al vaciar la carpeta", + "toast_error_create": "Error al crear la carpeta", + "toast_error_rename": "Error al renombrar la carpeta", + "toast_error_delete": "Error al eliminar la carpeta", + "toast_error_delete_has_children": "La carpeta tiene subcarpetas. Elimínelas primero.", + "toast_error_delete_has_email": "La carpeta no está vacía. Vacíela primero.", + "delete_with_contents_title": "La carpeta no está vacía", + "delete_with_contents_message": "La carpeta \"{name}\" aún contiene {count, plural, one {1 mensaje} other {# mensajes}}. ¿Eliminar la carpeta y todos sus mensajes? Esta acción no se puede deshacer.", + "delete_with_contents_confirm": "Eliminar carpeta y mensajes", + "placeholder_folder_name": "Nombre de carpeta", + "create": "Crear", + "rename_confirm": "Renombrar" + }, + "shortcuts": { + "title": "Atajos de Teclado", + "tip": "Presione ? en cualquier momento para mostrar esta ayuda", + "sections": { + "navigation": "Navegación", + "actions": "Acciones de Correo", + "global": "Global", + "threads": "Conversaciones", + "composer": "Redacción" + }, + "navigation": { + "next_email": "Siguiente correo", + "previous_email": "Correo anterior", + "open_email": "Abrir correo", + "close_email": "Cerrar / Deseleccionar", + "go_to_folder": "Ir a la carpeta", + "next_unread": "Siguiente correo no leído", + "no_more_unread": "No hay más correos no leídos más abajo en la lista" + }, + "actions": { + "reply": "Responder", + "reply_all": "Responder a todos", + "forward": "Reenviar", + "star": "Alternar destacado", + "archive": "Archivar", + "delete": "Eliminar", + "mark_unread": "Marcar como no leído", + "mark_read": "Marcar como leído", + "toggle_spam": "Reportar spam / No es spam" + }, + "global": { + "compose": "Redactar nuevo correo", + "search": "Enfocar búsqueda", + "help": "Mostrar atajos", + "refresh": "Actualizar correos", + "select_all": "Seleccionar todo", + "global_search": "Buscar en todo", + "extend_selection": "Ampliar la selección hacia abajo / arriba" + }, + "threads": { + "expand_collapse": "Expandir/contraer conversación", + "select_mountain_view": "Seleccionar la conversación" + }, + "composer": { + "send": "Enviar correo", + "schedule_send": "Programar envío", + "template_picker": "Abrir selector de plantillas" + } + }, + "threads": { + "messages_one": "{count} mensaje", + "messages_other": "{count} mensajes", + "messages_tooltip": "{count, plural, one {# mensaje en esta conversación} other {# mensajes en esta conversación}}", + "expand": "Expandir conversación", + "collapse": "Contraer conversación", + "loading": "Cargando conversación...", + "mark_read": "Marcar conversación como leída", + "mark_unread": "Marcar conversación como no leída", + "archive": "Archivar conversación", + "delete": "Eliminar conversación", + "star": "Destacar conversación", + "unstar": "Quitar destacado de conversación", + "toggle_thread": "Expandir/contraer hilo" + }, + "identities": { + "modal_title": "Administrar Identidades de Envío", + "create_new": "Crear Nueva Identidad", + "edit_identity": "Editar Identidad", + "delete_confirm": "¿Eliminar esta identidad? Esto no se puede deshacer.", + "cannot_delete": "Esta identidad no se puede eliminar", + "primary_identity": "Principal", + "set_as_primary": "Establecer como principal", + "no_identities": "No se encontraron identidades", + "display": { + "reply_to": "Responder a:", + "bcc": "CCO:", + "signature": "Firma:", + "preview": "Vista previa:" + }, + "validation_errors": { + "invalid_emails": "Correos inválidos: {emails}", + "unknown_error": "Error desconocido" + }, + "form": { + "name_label": "Nombre para Mostrar", + "name_placeholder": "ej., Correo de Trabajo, Personal", + "name_required": "El nombre es obligatorio", + "email_label": "Dirección de Correo", + "email_placeholder": "su.correo@ejemplo.com", + "email_required": "El correo es obligatorio", + "email_invalid": "Por favor ingrese una dirección de correo válida", + "email_immutable": "La dirección de correo no se puede cambiar después de la creación", + "reply_to_label": "Responder a (opcional)", + "reply_to_placeholder": "diferente@correo.com", + "bcc_label": "CCO Automático (opcional)", + "bcc_placeholder": "archivo@correo.com", + "text_signature_label": "Firma de Texto", + "html_signature_label": "Firma HTML", + "signature_byte_counter": "{bytes} / {max} bytes", + "signature_byte_limit_reached": "Límite del servidor alcanzado", + "save": "Guardar Identidad", + "cancel": "Cancelar", + "creating": "Creando...", + "updating": "Actualizando..." + }, + "sub_address": { + "button_tooltip": "Usar sub-dirección", + "popover_title": "Agregar Etiqueta de Sub-Dirección", + "tag_input_placeholder": "Ingrese etiqueta (ej., compras)", + "preview_label": "Vista previa:", + "recent_tags": "Etiquetas Recientes", + "suggested_tags": "Sugeridas", + "use_address": "Usar Esta Dirección", + "invalid_tag": "La etiqueta debe ser solo alfanumérica y guiones", + "tag_too_long": "La etiqueta debe tener 30 caracteres o menos", + "help_text": "Los correos enviados a usuario{delimiter}etiqueta@dominio.com llegarán a su bandeja de entrada", + "validation": { + "empty": "La etiqueta no puede estar vacía", + "too_long": "La etiqueta debe tener {max} caracteres o menos", + "invalid_chars": "La etiqueta debe contener solo letras, números y guiones" + } + }, + "badge": { + "sent_via": "vía", + "sub_address_tag": "Enviado usando sub-dirección: {tag}", + "identity_name": "Enviado usando identidad: {name}", + "identity_short": "vía {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Eliminar", + "delete_confirm_title": "Eliminar identidad" + }, + "templates": { + "picker_title": "Elegir una plantilla", + "search_placeholder": "Buscar plantillas...", + "section_favorites": "Favoritos", + "section_recent": "Recientes", + "section_uncategorized": "Otros", + "no_templates": "Sin plantillas", + "no_results": "No se encontraron plantillas", + "fill_placeholders": "Completar valores de marcadores", + "enter_value": "Introduce un valor...", + "preview": "Vista previa", + "insert_with_values": "Insertar con valores", + "insert_raw": "Insertar sin completar", + "copy_suffix": "(copia)", + "placeholder": "Variable", + "placeholders": { + "recipient_name": "Nombre del destinatario", + "company": "Nombre de la empresa", + "date": "Fecha actual", + "day_of_week": "Día de la semana", + "sender_name": "Tu nombre" + } + }, + "contacts": { + "directory": { + "title": "Directorio", + "search_placeholder": "Buscar nombre o dirección de correo", + "refresh": "Actualizar directorio", + "retry": "Reintentar", + "loading": "Cargando directorio…", + "unavailable_title": "Sin directorio", + "unavailable_hint": "Tu servidor de correo no ofrece un directorio de cuentas.", + "forbidden_title": "Directorio no disponible", + "forbidden_hint": "Tu servidor de correo no permite a esta cuenta consultar el directorio. Pregunta a tu administrador.", + "error_title": "No se pudo cargar el directorio", + "error_hint": "Comprueba tu conexión e inténtalo de nuevo.", + "empty_title": "El directorio está vacío", + "empty_hint": "Tu servidor de correo no lista ninguna cuenta con dirección de correo.", + "no_match": "Nadie en el directorio coincide con tu búsqueda", + "refresh_failed": "No se pudo actualizar el directorio. Se muestra la última lista cargada.", + "select_page": "Seleccionar a todos en esta página", + "deselect_page": "Deseleccionar a todos en esta página", + "select_entry": "Seleccionar a {name}", + "range": "{from}–{to} de {total}", + "page": "Página {page} de {count}", + "previous_page": "Página anterior", + "next_page": "Página siguiente", + "picker_title": "Seleccionar del directorio", + "cancel": "Cancelar", + "selected_count": "{count} seleccionados", + "add_to": "Añadir a Para", + "add_cc": "Añadir a CC", + "add_bcc": "Añadir a CCO", + "select_hint": "Selecciona a alguien para ver sus datos", + "write_email": "Escribir correo", + "add_to_contacts": "Añadir a contactos", + "email": "Correo", + "read_only_note": "Las entradas del directorio vienen de tu servidor de correo y no se pueden editar aquí.", + "type": { + "individual": "Persona", + "group": "Grupo", + "resource": "Recurso", + "location": "Ubicación", + "other": "Otro" + } + }, + "title": "Contactos", + "search_placeholder": "Buscar contactos...", + "create_new": "Nuevo contacto", + "no_category": "Sin categoría", + "rename_category": "Renombrar categoría", + "category_name_label": "Nombre de la categoría", + "category_renamed": "Categoría renombrada", + "category_rename_failed": "Error al renombrar la categoría", + "category_added": "Contacto añadido a {name}", + "category_added_plural": "{count} contactos añadidos a {name}", + "empty_state": "No hay contactos", + "empty_state_title": "Sin contactos", + "empty_state_subtitle": "Crea tu primer contacto o importa desde un archivo vCard", + "empty_search": "Ningún contacto coincide con tu búsqueda", + "empty_search_hint": "Prueba con otro término de búsqueda", + "empty_filtered": "Ningún contacto coincide con tus filtros", + "empty_filtered_hint": "Ajusta o limpia los filtros", + "clear_search": "Borrar búsqueda", + "import_vcard": "Importar vCard", + "delete_confirm_title": "Eliminar contacto", + "delete_confirm": "¿Estás seguro de que quieres eliminar este contacto?", + "local_mode": "Los contactos se almacenan localmente (el servidor no soporta JMAP Contacts)", + "back_to_contacts": "Volver a contactos", + "back_to_email": "Volver al correo", + "tabs": { + "all": "Todos", + "groups": "Grupos" + }, + "shared": { + "title": "Compartidos" + }, + "address_books": { + "create": "Nueva libreta de direcciones", + "created": "Libreta de direcciones creada", + "create_failed": "No se pudo crear la libreta de direcciones", + "title": "Mis Libretas de Direcciones", + "shared_prefix": "Compartido: {name}", + "moved": "Contacto movido a {name}", + "moved_plural": "{count} contactos movidos a {name}", + "move_failed": "Error al mover el contacto", + "address_book": "Libreta de direcciones", + "rename": "Renombrar libreta de direcciones", + "name_label": "Nombre de la libreta de direcciones", + "renamed": "Libreta de direcciones renombrada", + "rename_failed": "Error al renombrar la libreta de direcciones", + "default": "Predeterminada", + "set_default": "Establecer como predeterminada", + "default_updated": "Libreta de direcciones predeterminada actualizada", + "set_default_failed": "Error al establecer la libreta de direcciones predeterminada", + "manage": "Administrar libretas de direcciones", + "share": "Compartir libreta de direcciones", + "new_contact_in_book": "Nuevo contacto en esta libreta", + "delete": "Eliminar libreta de direcciones", + "confirm_delete": "¿Eliminar «{name}»? Todos los contactos de esta libreta se eliminarán.", + "deleted": "Libreta de direcciones eliminada", + "delete_failed": "No se pudo eliminar la libreta de direcciones" + }, + "detail": { + "emails": "Direcciones de correo", + "phones": "Números de teléfono", + "organizations": "Organizaciones", + "addresses": "Direcciones", + "notes": "Notas", + "titles": "Títulos y roles", + "online_services": "Servicios en línea", + "anniversaries": "Aniversarios", + "personal_info": "Información personal", + "languages": "Idiomas", + "categories": "Categorías", + "related_contacts": "Contactos relacionados", + "crypto_keys": "Claves criptográficas", + "no_contact_selected": "Selecciona un contacto para ver los detalles", + "compose_email": "Escribir correo", + "copy_email": "Copiar correo", + "copy_phone": "Copiar teléfono", + "copy_url": "Copiar URL", + "copied": "Copiado al portapapeles", + "copy_failed": "Error al copiar al portapapeles", + "created": "Creado", + "updated": "Última actualización", + "timezone": "Zona horaria", + "anniversary_birth": "Cumpleaños", + "anniversary_death": "Fallecimiento", + "anniversary_wedding": "Aniversario", + "anniversary_other": "Otro", + "personal_expertise": "Especialidad", + "personal_hobby": "Pasatiempo", + "personal_interest": "Interés", + "personal_other": "Otro", + "gender": "Género", + "gender_masculine": "Masculino", + "gender_feminine": "Femenino", + "gender_other": "Otro", + "gender_none": "No aplicable", + "gender_unknown": "Desconocido", + "calendar": "Calendario", + "calendar_uri": "URL del calendario", + "scheduling_uri": "URL de programación", + "freebusy_uri": "URL de disponibilidad", + "cert_issuer": "Emisor", + "cert_expires": "Caduca", + "cert_expired": "Caducado", + "cert_fingerprint": "Huella digital", + "cert_algorithm": "Algoritmo", + "import_to_smime": "Importar a S/MIME", + "cert_already_imported": "Certificado ya importado", + "cert_imported": "Certificado importado", + "cert_import_failed": "Error al importar el certificado", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Más acciones", + "age_years": "{count, plural, one {1 año} other {# años}}", + "years_since": "{count, plural, one {hace 1 año} other {hace # años}}" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "Nuevo contacto", + "edit_title": "Editar contacto", + "section_address_book": "Directorio", + "select_address_book": "Seleccionar un directorio...", + "section_identity": "Nombre e identidad", + "contact_type": "Tipo de contacto", + "type_person": "Persona", + "type_organization": "Organización", + "section_work": "Trabajo y organización", + "prefix": "Prefijo", + "prefix_placeholder": "Dr., Sr., Sra.", + "given_name": "Nombre", + "middle_name": "Segundo nombre", + "surname": "Apellido", + "suffix": "Sufijo", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Apodo", + "nickname_placeholder": "Apodo", + "email": "Correo", + "email_placeholder": "email@example.com", + "phone": "Teléfono", + "phone_placeholder": "+34 612 345 678", + "phone_type": "Tipo", + "phone_voice": "Voz", + "phone_cell": "Móvil", + "phone_fax": "Fax", + "phone_pager": "Buscapersonas", + "phone_video": "Video", + "phone_text": "Texto", + "organization": "Organización", + "organization_placeholder": "Nombre de la empresa", + "department": "Departamento", + "department_placeholder": "Departamento", + "job_title": "Cargo", + "job_title_placeholder": "p. ej., Ingeniero de software", + "role": "Rol", + "role_placeholder": "p. ej., Líder de equipo", + "addresses": "Direcciones", + "add_address": "Agregar dirección", + "street": "Calle", + "city": "Ciudad", + "region": "Estado / Región", + "postcode": "Código postal", + "country": "País", + "online_services": "Servicios en línea", + "add_online_service": "Agregar servicio en línea", + "url_placeholder": "https://...", + "service_placeholder": "Servicio", + "anniversaries": "Aniversarios", + "add_anniversary": "Agregar fecha", + "anniversary_birth": "Cumpleaños", + "anniversary_wedding": "Aniversario", + "anniversary_death": "Conmemoración", + "anniversary_other": "Otro", + "personal_info": "Información personal", + "add_personal_info": "Agregar entrada", + "personal_info_placeholder": "p. ej., Fotografía", + "personal_expertise": "Especialidad", + "personal_hobby": "Pasatiempo", + "personal_interest": "Interés", + "personal_other": "Otro", + "level": "Nivel", + "level_high": "Alto", + "level_medium": "Medio", + "level_low": "Bajo", + "categories": "Categorías", + "categories_placeholder": "p. ej., Familia, Amigos, Colegas", + "categories_hint": "Escriba para buscar o añadir categorías", + "category_add": "Añadir", + "note": "Notas", + "note_placeholder": "Agregar una nota...", + "gender": "Género", + "gender_sex": "Sexo", + "gender_male": "Masculino", + "gender_female": "Femenino", + "gender_other": "Otro", + "gender_none": "No aplicable", + "gender_unknown": "Desconocido", + "gender_identity": "Identidad de género", + "gender_identity_placeholder": "Identidad de género...", + "calendar": "Calendario", + "calendar_uri": "URL del calendario", + "scheduling_uri": "URL de programación", + "freebusy_uri": "URL de disponibilidad", + "context_work": "Trabajo", + "context_private": "Personal", + "add_email": "Agregar correo", + "add_phone": "Agregar teléfono", + "save": "Guardar", + "cancel": "Cancelar", + "creating": "Creando...", + "updating": "Actualizando...", + "name_required": "Introduce un nombre, un apellido o una organización", + "email_invalid": "Introduce una dirección de correo válida", + "email_error_inline": "Formato de correo inválido", + "save_failed": "Error al guardar el contacto", + "delete": "Eliminar", + "upload_photo": "Subir foto", + "remove_photo": "Quitar foto", + "photo_hint": "JPG o PNG, hasta 10 MB. Se redimensionará.", + "photo_too_large": "La imagen es demasiado grande (máx. 10 MB)", + "photo_invalid": "Archivo de imagen no válido", + "change_photo": "Cambiar" + }, + "groups": { + "create": "Nuevo grupo", + "edit": "Editar grupo", + "empty": "No hay grupos", + "delete_confirm_title": "Eliminar grupo", + "delete_confirm": "¿Estás seguro de que quieres eliminar este grupo?", + "name_label": "Nombre del grupo", + "name_placeholder": "ej. Equipo, Familia", + "name_required": "El nombre del grupo es obligatorio", + "save_failed": "Error al guardar el grupo", + "members_label": "Miembros", + "search_members": "Buscar contactos para agregar...", + "no_members": "No hay miembros en este grupo", + "member_count": "{count, plural, =0 {Sin miembros} one {1 miembro} other {# miembros}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Importar contactos", + "drop_hint": "Haz clic para seleccionar un archivo vCard", + "file_types": "Archivos .vcf o .vcard", + "no_contacts": "No se encontraron contactos en el archivo", + "parse_error": "Error al analizar el archivo vCard", + "found": "{count, plural, one {1 contacto encontrado} other {# contactos encontrados}}", + "duplicate": "Duplicado", + "select_all": "Seleccionar todo", + "deselect_all": "Deseleccionar todo", + "selected": "{count, plural, one {1 seleccionado} other {# seleccionados}}", + "import_button": "Importar", + "importing": "Importando...", + "success": "{count, plural, one {1 contacto importado} other {# contactos importados}}", + "failed": "Error en la importación", + "close": "Cerrar", + "file_too_large": "El archivo es demasiado grande (máx. 5 MB)" + }, + "export": { + "title": "Exportar contactos", + "success": "{count, plural, one {1 contacto exportado} other {# contactos exportados}}" + }, + "bulk": { + "selected": "{count, plural, one {1 seleccionado} other {# seleccionados}}", + "select_all": "Seleccionar todo", + "delete": "Eliminar", + "delete_confirm_title": "Eliminar contactos", + "delete_confirm": "¿Eliminar {count, plural, one {1 contacto} other {# contactos}}?", + "deleted": "{count, plural, one {1 contacto eliminado} other {# contactos eliminados}}", + "add_to_group": "Agregar al grupo", + "choose_group": "Elegir un grupo", + "adding_contacts": "Agregando {count, plural, one {1 contacto} other {# contactos}}", + "added_to_group": "Contactos agregados al grupo", + "export": "Exportar", + "clear": "Limpiar selección" + }, + "toast": { + "created": "Contacto creado", + "updated": "Contacto actualizado", + "deleted": "Contacto eliminado", + "error_create": "Error al crear el contacto", + "error_update": "Error al actualizar el contacto", + "error_delete": "Error al eliminar el contacto" + }, + "context_menu": { + "open": "Abrir", + "edit": "Editar", + "send_email": "Enviar correo", + "add_to_group": "Añadir al grupo", + "export_vcard": "Exportar como vCard", + "delete": "Eliminar", + "call": "Llamar", + "duplicate": "Duplicar", + "print": "Imprimir" + }, + "filters": { + "toggle": "Filtros", + "select": "Seleccionar", + "clear": "Borrar", + "close": "Cerrar", + "title": "Filtros avanzados", + "organization": "Empresa", + "organization_placeholder": "p. ej. Acme", + "job_title": "Puesto", + "job_title_placeholder": "p. ej. Diseñador", + "location": "Ubicación", + "location_placeholder": "Ciudad o país", + "email_domain": "Dominio de correo", + "email_domain_placeholder": "ejemplo.com", + "birthday_month": "Cumpleaños en", + "any_month": "Cualquier mes", + "has_email": "Con correo", + "has_phone": "Con teléfono", + "has_photo": "Con foto", + "sort_by": "Ordenar por", + "sort_first_name": "Nombre", + "sort_last_name": "Apellido" + }, + "open_categories": "Abrir categorías" + }, + "calendar": { + "title": "Calendario", + "back_to_email": "Volver al correo", + "back_to_month": "Volver al mes", + "my_calendars": "Mis calendarios", + "birthday_calendar": "Cumpleaños", + "mini_calendar_change": "Clic para cambiar de mes", + "views": { + "month": "Mes", + "week": "Semana", + "day": "Día", + "agenda": "Agenda", + "today": "Hoy", + "month_hint": "Mes (m)", + "week_hint": "Semana (w)", + "day_hint": "Día (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Tareas", + "tasks_hint": "Tareas (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# evento anterior} other {# eventos anteriores}}", + "hidden_after": "{count, plural, one {# evento posterior} other {# eventos posteriores}}", + "show_all_hours": "Mostrar todas las horas", + "show_display_hours": "Mostrar solo {start} – {end}", + "create": "Crear evento", + "edit": "Editar evento", + "delete": "Eliminar evento", + "details": "Detalles del evento", + "no_events": "Sin eventos", + "all_day": "Todo el día", + "more": "+{count} más", + "show_more": "Mostrar más", + "show_less": "Mostrar menos", + "no_title": "(Sin título)", + "resize": "Redimensionar evento", + "duplicate": "Duplicar", + "today_header": "Hoy", + "tomorrow_header": "Mañana", + "agenda_show_earlier": "Mostrar eventos anteriores", + "agenda_loading": "Cargando…", + "agenda_range_start": "Eventos desde el {date}", + "agenda_range_end": "Eventos hasta el {date}", + "export_ics": "Exportar como .ics", + "copy_title": "Copiar título", + "copy_link": "Copiar enlace de reunión", + "go_to_today": "Ir a hoy", + "new_all_day_event": "Nuevo evento de todo el día", + "new_event": "Nuevo evento", + "new_task": "Nueva tarea" + }, + "detail": { + "add_note": "Añadir una nota...", + "save_note": "Guardar", + "note_saved": "Nota añadida", + "open_link": "Abrir enlace", + "copy_location": "Copiar ubicación", + "meeting_link": "Enlace de reunión", + "tentative": "Provisional", + "cancelled": "Cancelado", + "delete_confirm": "¿Eliminar este evento?" + }, + "form": { + "title": "Título", + "description": "Descripción", + "location": "Ubicación", + "meeting_link": "Enlace de reunión", + "start_date": "Fecha de inicio", + "end_date": "Fecha de fin", + "start_time": "Hora de inicio", + "end_time": "Hora de fin", + "all_day_event": "Evento de todo el día", + "calendar_select": "Calendario", + "save": "Guardar", + "cancel": "Cancelar", + "delete_confirm": "¿Está seguro de que desea eliminar este evento?", + "color": "Color" + }, + "participants": { + "availability": { + "title": "Disponibilidad", + "free": "Disponible", + "busy": "Ocupado", + "tentative": "Provisional", + "unknown": "No es un usuario de este servidor", + "checking": "Comprobando…" + }, + "title": "Participantes", + "add": "Agregar participante", + "organizer": "Organizador", + "attendee": "Participante", + "accepted": "Aceptado", + "declined": "Rechazado", + "tentative": "Provisional", + "needs_action": "Pendiente de respuesta", + "remove": "Eliminar", + "edit": "Editar", + "email_placeholder": "Añadir dirección de correo o buscar contactos", + "send_invitations": "Enviar invitaciones a los participantes", + "status_summary": "{accepted} aceptado(s), {pending} pendiente(s)", + "invited_by": "Invitación de {name}", + "respond_below": "Responde con los botones de abajo", + "rsvp_label": "Tu respuesta", + "cancel_notification": "Se notificará a los participantes de la cancelación", + "you_organizer": "Eres el organizador", + "you_attendee": "Eres un participante", + "no_participants": "Sin participantes", + "count": "{count, plural, one {# participante} other {# participantes}}" + }, + "recurrence": { + "title": "Recurrencia", + "none": "No se repite", + "daily": "Diario", + "weekly": "Semanal", + "monthly": "Mensual", + "yearly": "Anual", + "every_n_days": "Cada {count} días", + "every_n_weeks": "Cada {count} semanas", + "every_n_months": "Cada {count} meses", + "until": "Hasta", + "occurrences": "{count} repeticiones", + "custom": "Personalizado…", + "edit_custom": "Editar repetición personalizada", + "every_n_years": "Cada {count} años", + "on_days": "los {days}", + "on_day_n": "el día {day}", + "on_the_nth": "el {nth} {day}", + "in_month": "en {month}", + "nth_1": "primer", + "nth_2": "segundo", + "nth_3": "tercer", + "nth_4": "cuarto", + "nth_last": "último", + "editor_freq_day": "Día", + "editor_freq_week": "Semana", + "editor_freq_month": "Mes", + "editor_freq_year": "Año", + "editor_repeats_on": "Se repite", + "editor_every": "Cada", + "editor_unit_days": "día(s)", + "editor_unit_weeks": "semana(s)", + "editor_unit_months": "mes(es)", + "editor_unit_years": "año(s)", + "editor_on_day": "el día", + "editor_on_the": "el", + "editor_in": "en", + "editor_ends": "Termina", + "editor_never": "Nunca", + "editor_ends_on": "El", + "editor_ends_after": "Después de", + "editor_occurrences": "repeticiones" + }, + "recurrence_scope": { + "edit_title": "Editar evento recurrente", + "delete_title": "Eliminar evento recurrente", + "description": "Este es un evento recurrente. ¿Qué eventos desea modificar?", + "this_event": "Solo este evento", + "this_and_future": "Este evento y los siguientes", + "all_events": "Todos los eventos", + "cancel": "Cancelar", + "save": "Guardar", + "delete": "Eliminar", + "rsvp_title": "Responder a evento recurrente", + "rsvp_description": "Este es un evento recurrente. ¿A qué eventos se aplica su respuesta?", + "respond": "Responder" + }, + "alerts": { + "title": "Recordatorio", + "none": "Sin recordatorio", + "at_time": "En el momento del evento", + "minutes_before": "{count, plural, one {# minuto antes} other {# minutos antes}}", + "hours_before": "{count, plural, one {# hora antes} other {# horas antes}}", + "days_before": "{count, plural, one {# día antes} other {# días antes}}", + "weeks_before": "{count, plural, one {# semana antes} other {# semanas antes}}", + "unit_minutes_before": "minutos antes", + "unit_hours_before": "horas antes", + "unit_days_before": "días antes", + "unit_weeks_before": "semanas antes", + "add": "Añadir recordatorio", + "remove": "Eliminar recordatorio", + "amount": "Cantidad del recordatorio", + "unit": "Unidad del recordatorio", + "action": "Tipo de recordatorio", + "action_display": "Notificación", + "action_email": "Correo electrónico", + "by_email": "{label} por correo electrónico", + "kept": "{count, plural, one {Se conserva # recordatorio más que no se puede editar aquí} other {Se conservan # recordatorios más que no se pueden editar aquí}}", + "other": "{count, plural, one {# recordatorio más} other {# recordatorios más}}", + "calendar_defaults": "Recordatorios predeterminados del calendario", + "customize": "Personalizar", + "use_defaults": "Usar los recordatorios predeterminados del calendario" + }, + "settings": { + "limit_hours": "Limitar las horas visibles", + "limit_hours_desc": "Mostrar solo estas horas en las vistas de día y semana. Los eventos fuera de ellas siguen accesibles con las flechas de arriba y abajo de cada día.", + "visible_hours": "Horas visibles", + "visible_hours_start": "Inicio de las horas visibles", + "visible_hours_end": "Fin de las horas visibles", + "hide_non_working_days": "Ocultar días no laborables", + "hide_non_working_days_desc": "Quitar de la vista semanal los días que no trabajas. Sus eventos siguen apareciendo en las vistas de mes, día y agenda.", + "working_days": "Días laborables", + "title": "Configuración del calendario", + "default_view": "Vista predeterminada", + "week_starts_on": "La semana comienza el", + "time_format": "Formato de hora", + "default_calendar": "Calendario predeterminado", + "default_reminder": "Recordatorio predeterminado", + "time_format_12h": "12 horas", + "time_format_24h": "24 horas", + "notifications_enabled": "Notificaciones de eventos", + "notifications_enabled_desc": "Mostrar alertas para eventos próximos", + "notification_sound": "Sonido de notificación", + "notification_sound_desc": "Reproducir un sonido para las alertas del calendario", + "invitation_parsing": "Analizar invitaciones por correo", + "invitation_parsing_desc": "Detectar invitaciones de calendario en archivos adjuntos del correo y mostrar acciones del calendario", + "show_time_in_month_view": "Mostrar hora en vista mensual", + "show_time_in_month_view_desc": "Mostrar las horas de los eventos en la vista mensual del calendario", + "show_week_numbers": "Mostrar números de semana", + "show_week_numbers_desc": "Mostrar los números de semana en el minicalendario", + "calendar_free_scroll": "Desplazamiento libre", + "calendar_free_scroll_desc": "Desplazarse de forma continua por meses, semanas y días en lugar de un periodo cada vez", + "enable_tasks": "Activar tareas", + "enable_tasks_desc": "Mostrar una vista de tareas en el calendario para gestionar tareas pendientes", + "show_tasks_on_calendar": "Mostrar tareas en el calendario", + "show_tasks_on_calendar_desc": "Mostrar las tareas en las vistas mensual, semanal y diaria del calendario", + "hover_preview": "Vista previa al pasar el ratón", + "hover_preview_desc": "Mostrar un popover de detalles al pasar el ratón sobre eventos del calendario", + "hover_preview_instant": "Instantáneo", + "hover_preview_delay_500ms": "Retraso de 0,5 segundos", + "hover_preview_delay_1s": "Retraso de 1 segundo", + "hover_preview_delay_2s": "Retraso de 2 segundos", + "hover_preview_off": "Desactivado", + "organizer_identity": "Organizar invitaciones como", + "organizer_identity_desc": "La dirección de calendario usada como organizador al invitar a personas a eventos.", + "organizer_identity_failed": "No se pudo cambiar la dirección del organizador", + "show_birthday_calendar": "Calendario de cumpleaños", + "show_birthday_calendar_desc": "Mostrar un calendario virtual con los cumpleaños de tus contactos" + }, + "days": { + "monday": "Lunes", + "tuesday": "Martes", + "wednesday": "Miércoles", + "thursday": "Jueves", + "friday": "Viernes", + "saturday": "Sábado", + "sunday": "Domingo", + "mon": "Lun", + "tue": "Mar", + "wed": "Mié", + "thu": "Jue", + "fri": "Vie", + "sat": "Sáb", + "sun": "Dom" + }, + "notifications": { + "event_created": "Evento creado", + "event_updated": "Evento actualizado", + "event_deleted": "Evento eliminado", + "calendar_created": "Calendario creado", + "calendar_deleted": "Calendario eliminado", + "event_move_error": "Error al mover el evento", + "event_resize_error": "Error al redimensionar el evento", + "alert_title": "Evento próximo", + "alert_now": "Comienza ahora", + "alert_in_minutes": "En {count} min", + "invitation_sent": "Invitaciones enviadas", + "invitations_denied_title": "No se pueden enviar las invitaciones", + "invitations_denied": "El servidor rechazó enviar las invitaciones: {reason}. ¿Guardar el evento sin enviarlas?", + "save_without_invitations": "Guardar sin invitaciones", + "rsvp_updated": "Respuesta actualizada", + "rsvp_error": "Error al actualizar la respuesta", + "event_duplicated": "Evento duplicado", + "event_error": "Error al guardar el evento", + "task_due": "Tarea vencida", + "event_exported": "Evento exportado", + "title_copied": "Título copiado", + "link_copied": "Enlace copiado" + }, + "status": { + "loading_calendars": "Cargando calendarios...", + "loading_events": "Cargando eventos..." + }, + "quick_create": { + "placeholder": "Título del nuevo evento", + "aria_label": "Creación rápida de evento", + "add_title": "Añadir título", + "more_options": "Más opciones" + }, + "nav_prev": "Anterior", + "nav_next": "Siguiente", + "import": { + "title": "Importar calendario", + "tab_file": "Archivo", + "tab_url": "URL", + "select_file": "Seleccionar archivo .ics", + "drop_file": "o arrastra el archivo aquí", + "supported_formats": "Archivos iCalendar (.ics) compatibles", + "url_description": "Introduce la URL de un feed iCalendar (.ics) externo para importar eventos.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Compatible con URLs iCalendar (.ics)", + "fetch": "Obtener", + "invalid_url": "Introduce una URL válida", + "url_fetch_failed": "No se pudo obtener el calendario desde la URL", + "parsing": "Analizando archivo de calendario...", + "parsed_events": "{count} eventos encontrados", + "no_events": "No se encontraron eventos en el archivo", + "select_all": "Seleccionar todo", + "deselect_all": "Deseleccionar todo", + "target_calendar": "Importar al calendario", + "import_button": "Importar selección", + "importing": "Importando eventos...", + "success": "{count} eventos importados correctamente", + "error": "Error al importar el calendario", + "file_too_large": "El archivo supera el límite de 10 MB", + "invalid_format": "Formato de archivo de calendario no válido" + }, + "webcal_action": { + "title": "Abrir enlace de calendario", + "description": "¿Cómo quieres usar \"{name}\"?", + "import_title": "Importar una vez", + "import_description": "Obtén los eventos ahora y cópialos en uno de tus calendarios.", + "subscribe_title": "Suscribirse", + "subscribe_description": "Mantén este calendario sincronizado automáticamente como un calendario separado.", + "cancel": "Cancelar" + }, + "management": { + "title": "Gestión de calendarios", + "description": "Crea, renombra y personaliza tus calendarios. Haz clic derecho en un calendario en la barra lateral para cambiar su color rápidamente.", + "name": "Nombre", + "name_placeholder": "Nombre del calendario", + "color": "Color", + "change_color": "Cambiar color", + "random_color": "Nuevo color aleatorio", + "add_calendar": "Añadir calendario", + "edit": "Editar", + "delete": "Eliminar", + "save": "Guardar", + "create": "Crear", + "cancel": "Cancelar", + "default": "Predeterminado", + "confirm_delete": "¿Eliminar \"{name}\"? Se eliminarán todos los eventos de este calendario.", + "calendar_created": "Calendario creado", + "calendar_updated": "Calendario actualizado", + "calendar_deleted": "Calendario eliminado", + "color_updated": "Color del calendario actualizado", + "error_create": "Error al crear el calendario", + "error_update": "Error al actualizar el calendario", + "error_delete": "Error al eliminar el calendario", + "caldav_url": "URL de CalDAV", + "copy_url": "Copiar URL de CalDAV", + "url_copied": "URL de CalDAV copiada al portapapeles", + "confirm_clear": "¿Borrar todos los eventos de \"{name}\"? Esta acción no se puede deshacer.", + "set_default": "Establecer como predeterminado", + "default_updated": "Calendario predeterminado actualizado", + "error_default": "Error al establecer el calendario predeterminado", + "clear_events": "Borrar eventos", + "events_cleared": "{count} eventos borrados", + "error_clear": "No se pudieron borrar los eventos del calendario", + "share": "Compartir calendario", + "new_event_in_calendar": "Nuevo evento en este calendario", + "shared_with": "Compartido con {names}", + "shared_with_count": "{count, plural, one {Compartido con 1 persona} other {Compartido con # personas}}", + "move_up": "Mover arriba", + "move_down": "Mover abajo", + "kind": "Contenido", + "kind_events": "Eventos", + "kind_tasks": "Tareas", + "kind_both": "Eventos y tareas", + "kind_hint": "Determina qué aplicaciones ven este calendario cuando se sincroniza por CalDAV. No se puede cambiar después.", + "default_alerts": "Recordatorios predeterminados", + "default_alerts_title": "Recordatorios predeterminados de \"{name}\"", + "default_alerts_hint": "Los eventos nuevos de este calendario reciben estos recordatorios, salvo que se cambien en el evento. Cambiarlos aquí no modifica los eventos existentes. Los recordatorios por correo los envía el servidor de correo.", + "default_alerts_timed": "Eventos con hora", + "default_alerts_all_day": "Eventos de todo el día", + "default_alerts_updated": "Recordatorios predeterminados actualizados" + }, + "subscription": { + "title": "Suscripción iCal", + "section_title": "Suscripciones iCal", + "description": "Suscríbete a un feed externo de iCalendar. Los eventos se sincronizarán automáticamente en su propio calendario. Compatible con URLs https:// y webcal://.", + "url_label": "URL del calendario", + "url_placeholder": "https://example.com/calendar.ics o webcal://...", + "name_label": "Nombre del calendario", + "name_placeholder": "p. ej., Días festivos", + "color_label": "Color", + "refresh_interval": "Intervalo de actualización", + "interval_15": "Cada 15 minutos", + "interval_30": "Cada 30 minutos", + "interval_60": "Cada hora", + "interval_360": "Cada 6 horas", + "interval_1440": "Cada día", + "subscribe": "Suscribirse", + "subscribing": "Suscribiendo...", + "save": "Guardar cambios", + "saving": "Guardando...", + "edit": "Editar", + "edit_title": "Editar suscripción", + "updated": "\"{name}\" actualizado", + "update_error": "No se pudo actualizar la suscripción", + "invalid_url": "Por favor, introduce una URL válida", + "success": "Suscrito a \"{name}\"", + "error": "No se pudo añadir la suscripción", + "refresh": "Actualizar ahora", + "refresh_success": "Suscripción actualizada", + "refresh_error": "No se pudo actualizar la suscripción", + "unsubscribe": "Cancelar suscripción", + "confirm_delete": "¿Cancelar la suscripción a \"{name}\"? El calendario y todos sus eventos serán eliminados.", + "deleted": "Suscripción eliminada", + "delete_error": "No se pudo eliminar la suscripción", + "last_refreshed": "Última actualización: {time}" + }, + "tasks": { + "label": "Tareas", + "no_tasks": "Sin tareas", + "no_title": "(Sin título)", + "mark_complete": "Marcar como completada", + "mark_incomplete": "Marcar como incompleta", + "filter_all": "Todas", + "filter_pending": "Pendientes", + "filter_completed": "Completadas", + "filter_overdue": "Vencidas", + "show_completed": "Mostrar completadas", + "create": "Nueva tarea", + "edit": "Editar tarea", + "title_placeholder": "Título de la tarea", + "description_placeholder": "Agregar una descripción...", + "due_date": "Fecha de vencimiento", + "include_time": "Incluir hora", + "priority": "Prioridad", + "priority_none": "Ninguna", + "priority_high": "Alta", + "priority_medium": "Media", + "priority_low": "Baja", + "progress": "Estado", + "progress_needs_action": "Requiere acción", + "progress_in_process": "En proceso", + "progress_completed": "Completada", + "progress_cancelled": "Cancelada", + "calendar": "Calendario", + "alert": "Recordatorio", + "alert_none": "Ninguno", + "alert_at_time": "En la fecha y hora de vencimiento", + "alert_5min": "5 minutos antes", + "alert_15min": "15 minutos antes", + "alert_30min": "30 minutos antes", + "alert_1hr": "1 hora antes", + "alert_1day": "1 día antes", + "delete": "Eliminar", + "cancel": "Cancelar", + "save": "Guardar", + "quick_add_placeholder": "Agregar una tarea...", + "due_today": "Vence hoy", + "due_tomorrow": "Vence mañana", + "overdue": "Vencida" + }, + "months": { + "jan": "Ene", + "feb": "Feb", + "mar": "Mar", + "apr": "Abr", + "may": "May", + "jun": "Jun", + "jul": "Jul", + "aug": "Ago", + "sep": "Sep", + "oct": "Oct", + "nov": "Nov", + "dec": "Dic", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Abrir menú", + "toolbar": { + "more": "Más acciones", + "select_view": "Cambiar vista, actualmente {view}" + }, + "other_calendars": "Otros calendarios", + "calendar_options": "Opciones de {name}" + }, + "advanced_search": { + "title": "Búsqueda avanzada", + "from": "De", + "from_placeholder": "Email o nombre del remitente", + "to": "Para", + "to_placeholder": "Email o nombre del destinatario", + "subject": "Asunto", + "subject_placeholder": "El asunto contiene...", + "body": "Cuerpo", + "body_placeholder": "El cuerpo contiene...", + "folder": "Carpeta", + "all_folders": "Todas las carpetas", + "all_folders_except_spam_trash": "Todas las carpetas excepto Spam y Papelera", + "has_attachment": "Adjuntos", + "date_after": "Después de", + "date_before": "Antes de", + "size_min": "Mayor que (KB)", + "size_max": "Menor que (KB)", + "starred": "Destacados", + "unread": "No leídos", + "read": "Leídos", + "yes": "Sí", + "no": "No", + "clear": "Limpiar", + "clear_all": "Limpiar todo", + "filters_active": "{count} filtro", + "filters_active_plural": "{count} filtros", + "toggle_filters": "Filtros", + "search_hint": "Usa los filtros avanzados para una búsqueda precisa", + "advanced_filters_tooltip": "Filtros de búsqueda avanzados", + "results_found": "{count, plural, =0 {No se encontraron resultados} one {# resultado encontrado} other {# resultados encontrados}}", + "results_found_more": "{count}+ resultados encontrados", + "suggestions_recent": "Búsquedas recientes", + "suggestions_people": "Personas", + "suggestions_from": "Buscar correos de {address}", + "suggestions_to": "Buscar correos para {address}", + "suggestions_remove_recent": "Quitar de las búsquedas recientes", + "has_words": "Contiene las palabras", + "size": "Tamaño", + "date_within": "Fecha", + "search_in": "Buscar en", + "has_attachment_check": "Tiene archivos adjuntos", + "search": "Buscar" + }, + "welcome": { + "title": "Bienvenido a tu buzón", + "tip_compose": "Pulsa c para redactar un nuevo correo", + "tip_shortcuts": "Pulsa ? para ver todos los atajos de teclado", + "tip_sidebar": "Encuentra Calendario, Contactos y Ajustes en el menú lateral", + "tip_settings": "Personaliza tu experiencia en Ajustes", + "tip_compose_touch": "Toca el botón del lápiz para escribir un correo nuevo", + "tip_sidebar_touch": "Encontrarás Calendario, Contactos y Ajustes en la barra inferior", + "got_it": "Entendido", + "settings": "Ajustes", + "dismiss": "Cerrar", + "start_tour": "Iniciar tour" + }, + "demo_welcome": { + "title": "Bienvenido a Bulwark Mail", + "description": "Explora un cliente de correo web completo - directamente en tu navegador. Todos los datos quedan en tu dispositivo, así que prueba todo.", + "feature_email": "Leer y redactar correos", + "feature_organize": "Etiquetas, estrellas y carpetas", + "feature_shortcuts": "Atajos de teclado", + "feature_privacy": "Demo 100 % privada", + "hint": "Haz clic en un correo a la izquierda para empezar, o inicia el tour." + }, + "files": { + "office_edit": "Editar documento", + "office_loading": "Abriendo el editor de documentos…", + "office_error": "No se pudo abrir el editor de documentos.", + "office_close": "Cerrar editor", + "open_in_new_tab": "Abrir en una pestaña nueva", + "title": "Archivos", + "search_placeholder": "Buscar archivos...", + "empty_state_title": "Aún no hay archivos", + "upload_files": "Subir archivos", + "new_folder": "Nueva carpeta", + "new_folder_name": "Nombre de la carpeta", + "rename": "Renombrar", + "rename_title": "Renombrar", + "new_name": "Nuevo nombre", + "name_forbidden_chars": "El servidor no permite estos caracteres en los nombres: {chars}", + "name_reserved": "Este nombre está reservado en el servidor.", + "delete": "Eliminar", + "delete_confirm_title": "Eliminar recurso", + "delete_confirm_message": "¿Está seguro de que desea eliminar \"{name}\"? Esta acción no se puede deshacer.", + "download": "Descargar", + "name": "Nombre", + "size": "Tamaño", + "modified": "Modificado", + "type": "Tipo", + "folder": "Carpeta", + "file": "Archivo", + "uploading": "Subiendo...", + "upload_success": "{count, plural, one {1 archivo subido} other {# archivos subidos}}", + "upload_canceled": "Subida cancelada", + "upload_canceled_partial": "{count, plural, one {Se subió 1 archivo} other {Se subieron # archivos}} antes de cancelar", + "upload_error": "Error al subir el archivo", + "create_folder_success": "Carpeta creada", + "create_folder_error": "Error al crear la carpeta", + "delete_success": "Eliminado correctamente", + "delete_error": "Error al eliminar", + "rename_success": "Renombrado correctamente", + "rename_error": "Error al renombrar", + "download_error": "Error al descargar", + "not_available": "El almacenamiento de archivos no está disponible en este servidor", + "cancel": "Cancelar", + "create": "Crear", + "save": "Guardar", + "no_results": "Ningún archivo coincide con su búsqueda", + "batch_delete_confirm_message": "¿Está seguro de que desea eliminar {count, plural, one {1 elemento} other {# elementos}}? Esta acción no se puede deshacer.", + "batch_delete_success": "{count, plural, one {1 elemento eliminado} other {# elementos eliminados}}", + "grid_view": "Vista de cuadrícula", + "list_view": "Vista de lista", + "details": "Detalles", + "path": "Ruta", + "preview": "Vista previa", + "preview_error": "Error al cargar la vista previa", + "cut": "Cortar", + "copy": "Copiar", + "paste": "Pegar", + "move_success": "{count, plural, one {1 elemento movido} other {# elementos movidos}}", + "move_error": "Error al mover", + "paste_success": "Pegado correctamente", + "paste_error": "Error al pegar", + "new_text_file": "Nuevo archivo de texto", + "file_name": "Nombre del archivo", + "retry": "Reintentar", + "refresh": "Actualizar", + "duplicate": "Duplicar", + "duplicate_success": "Duplicado correctamente", + "duplicate_error": "Error al duplicar", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Archivo creado", + "create_file_error": "Error al crear el archivo", + "favorites": "Favoritos", + "recent": "Recientes", + "show_in_folder": "Mostrar en la carpeta", + "remove_from_recent": "Quitar de recientes", + "upload_folder": "Subir carpeta", + "file_too_large": "\"{name}\" excede el tamaño máximo de archivo ({max})", + "undo": "Deshacer", + "undo_success": "Acción deshecha", + "undo_error": "Error al deshacer", + "toolbar": "Acciones de archivo", + "file_list": "Archivos y carpetas", + "context_menu": "Acciones", + "settings_ascending": "Ascendente", + "settings_descending": "Descendente", + "disabled_title": "La función de archivos ha sido desactivada por su administrador", + "disabled_description": "Las cargas de archivos grandes a través de WebDAV pueden causar inestabilidad en Stalwart/RocksDB, incluyendo errores de memoria y uso irrecuperable del disco. Los archivos eliminados pueden no eliminarse inmediatamente del almacenamiento. Esta función no se recomienda para entornos de producción.", + "no_accounts": "No hay cuentas conectadas.", + "open_folder_tree": "Abrir árbol de carpetas", + "migration_title": "Actualizando tus archivos…", + "migration_description": "Organizando carpetas y archivos en su estructura adecuada. Esto solo ocurre una vez.", + "new": "Nuevo", + "my_files": "Mis archivos", + "folders": "Carpetas", + "files_heading": "Archivos", + "open": "Abrir", + "star": "Añadir a favoritos", + "unstar": "Quitar de favoritos", + "expand_folder": "Expandir {name}", + "collapse_folder": "Contraer {name}", + "sidebar_hint": "Las carpetas favoritas y los archivos abiertos aparecen aquí.", + "breadcrumbs": "Ruta de la carpeta", + "show_path": "Mostrar carpetas ocultas en la ruta", + "selection_actions": "Acciones para los elementos seleccionados", + "selected_count": "{count, plural, one {1 seleccionado} other {# seleccionados}}", + "clear_selection": "Borrar selección", + "select_all": "Seleccionar todo", + "select_item": "Seleccionar {name}", + "more_actions": "Más acciones para {name}", + "paste_here": "Pegar en esta carpeta", + "paste_count": "{count, plural, one {Pegar 1 elemento} other {Pegar # elementos}}", + "sort_by": "Ordenar por", + "view_mode": "Vista", + "clear_search": "Borrar búsqueda", + "no_results_for": "Nada en esta carpeta coincide con \"{query}\"", + "download_count": "{count, plural, one {Descargar 1 archivo} other {Descargar # archivos}}", + "delete_count": "{count, plural, one {Eliminar 1 elemento} other {Eliminar # elementos}}", + "close_details": "Cerrar detalles", + "file_kind": "Archivo {ext}", + "folder_summary": "{folders, plural, =0 {Ninguna carpeta} one {1 carpeta} other {# carpetas}}, {files, plural, =0 {ningún archivo} one {1 archivo} other {# archivos}}", + "details_hint": "Seleccione un archivo o una carpeta para ver sus detalles.", + "sharing": "Uso compartido", + "shared_with_count": "{count, plural, one {Compartido con 1 persona} other {Compartido con # personas}}", + "only_you": "Solo usted", + "empty_folder_title": "Esta carpeta está vacía", + "empty_folder_description": "Suelte archivos aquí o súbalos desde su dispositivo.", + "empty_folder_read_only": "No puede añadir archivos a esta carpeta.", + "choose_account": "Elija una cuenta", + "drop_to_upload": "Suelte para subir a {folder}", + "uploading_count": "Subiendo {current} de {total}", + "dismiss": "Descartar", + "zoom_in": "Acercar", + "zoom_out": "Alejar", + "zoom_reset": "Ajustar a la pantalla", + "rotate": "Girar", + "close_preview": "Cerrar" + }, + "smime": { + "your_certificates": "Tus certificados", + "your_certificates_desc": "Importa y administra tus certificados S/MIME para firmar y cifrar correos", + "recipient_certificates": "Certificados de destinatarios", + "recipient_certificates_desc": "Certificados públicos para cifrar correos a destinatarios", + "identity_bindings": "Vinculaciones de identidad y clave", + "identity_bindings_desc": "Vincula certificados S/MIME a tus identidades de correo", + "defaults_title": "Valores predeterminados", + "defaults_desc": "Configura el comportamiento predeterminado de firma y cifrado", + "import_pkcs12": "Importar PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importar certificado", + "no_certificates": "Aún no se han importado certificados", + "no_recipient_certs": "No hay certificados de destinatarios", + "expires": "Caduca", + "expired": "Caducado", + "bound_to": "Vinculado a", + "no_key_bound": "Ninguno", + "lock": "Bloquear clave", + "unlock": "Desbloquear clave", + "details": "Ver detalles", + "delete": "Eliminar", + "encrypt_by_default": "Cifrar por defecto", + "encrypt_by_default_desc": "Cifrar correos automáticamente cuando todos los destinatarios tengan certificados", + "remember_unlocked": "Recordar claves desbloqueadas", + "remember_unlocked_desc": "Mantener las claves desbloqueadas durante esta sesión del navegador", + "sign_default_for": "Firmar por defecto para", + "enter_p12_passphrase": "Introducir la contraseña de PKCS#12", + "p12_passphrase_desc": "Introduce la contraseña que protege este archivo de certificado", + "enter_storage_passphrase": "Definir contraseña de almacenamiento", + "storage_passphrase_desc": "Elige una contraseña para proteger esta clave en tu navegador", + "next": "Siguiente", + "import": "Importar", + "unlock_key": "Desbloquear clave", + "unlock_key_desc": "Introduce la contraseña de almacenamiento para desbloquear esta clave para firmar o descifrar", + "passphrase_placeholder": "Introduce la contraseña", + "confirm_passphrase_placeholder": "Confirma la contraseña", + "passphrase_mismatch": "Las contraseñas no coinciden", + "cancel": "Cancelar", + "processing": "Procesando…", + "close": "Cerrar", + "certificate_details": "Detalles del certificado", + "cert_subject": "Sujeto", + "cert_issuer": "Emisor", + "cert_email": "Correo electrónico", + "cert_serial": "Número de serie", + "cert_validity": "Validez", + "cert_fingerprint": "Huella digital (SHA-256)", + "cert_algorithm": "Algoritmo", + "cert_capabilities": "Capacidades", + "cert_source": "Origen", + "cert_expired": "Este certificado ha caducado", + "cert_not_yet_valid": "Este certificado aún no es válido", + "cap_sign": "Firma", + "cap_encrypt": "Cifrado", + "cap_none": "Ninguno", + "show_passphrase": "Mostrar contraseña", + "hide_passphrase": "Ocultar contraseña", + "sign_toggle": "Firmar", + "encrypt_toggle": "Cifrar", + "missing_recipient_certs": "Faltan certificados para: {emails}", + "missing_sender_cert": "No hay un certificado vinculado a esta identidad", + "status_encrypted_ok": "Este mensaje fue cifrado", + "status_encrypted_no_key": "Este mensaje está cifrado, pero no se encontró una clave coincidente", + "status_encrypted_failed": "No se pudo descifrar este mensaje", + "status_signed_valid": "Firma verificada", + "status_signed_invalid": "La verificación de la firma falló", + "status_signed_expired_cert": "Firmado con un certificado caducado", + "status_signed_self_signed": "Firma válida, pero el certificado es autofirmado (no confiable)", + "status_signed_mismatch": "La firma es válida, pero el firmante no coincide con el remitente", + "status_unsupported": "Formato S/MIME no compatible", + "auto_import_signer_certs": "Importar automáticamente certificados de firmantes", + "auto_import_signer_certs_desc": "Guardar automáticamente certificados de correos firmados verificados para futuros cifrados", + "export": "Exportar", + "enter_export_passphrase": "Definir contraseña de exportación", + "export_passphrase_desc": "Elige una contraseña para proteger el archivo PKCS#12 exportado", + "export_storage_desc": "Introduce la contraseña de almacenamiento para descifrar la clave antes de exportarla", + "incorrect_passphrase": "Contraseña incorrecta" + }, + "tour": { + "step_counter": "Paso {current} de {total}", + "skip": "Saltar tour", + "back": "Atrás", + "next": "Siguiente", + "finish": "Finalizar", + "take_a_tour": "Haz un recorrido por la interfaz", + "restart_title": "Tour introductorio", + "restart_desc": "Repetir el recorrido guiado por la interfaz", + "restart_button": "Reiniciar tour", + "show_on_new_devices_title": "Mostrar en dispositivos nuevos", + "show_on_new_devices_desc": "Vuelve a mostrar el banner de bienvenida y el tour la primera vez que inicies sesión en un dispositivo nuevo, incluso si ya los completaste en otro lugar", + "sidebar_title": "Tus buzones", + "sidebar_desc": "Esta es tu barra lateral de carpetas. Haz clic en cualquier buzón para ver sus correos. Puedes crear carpetas, arrastrar correos entre ellas y ver los contadores de no leídos.", + "compose_title": "Redactar un correo", + "compose_desc": "Haz clic aquí para escribir un nuevo correo. Puedes añadir destinatarios, archivos adjuntos y formato de texto enriquecido.", + "search_title": "Buscar en tu correo", + "search_desc": "Busca por remitente, asunto o contenido. Haz clic en el icono de filtro para opciones avanzadas como rango de fechas, adjuntos y mensajes destacados.", + "email_list_title": "Tu lista de correos", + "email_list_desc": "Los correos aparecen aquí. Haz clic en uno para leerlo a la derecha. Usa la casilla para seleccionar varios y moverlos, eliminarlos o etiquetarlos.", + "email_viewer_title": "Panel de lectura", + "email_viewer_desc": "El correo seleccionado se abre aquí. Responde, reenvía, archiva o elimina con los botones de la barra. También puedes destacar correos o añadir etiquetas de color.", + "keywords_title": "Etiquetas de color", + "keywords_desc": "Organiza tu correo con etiquetas de colores. Arrastra un correo sobre una etiqueta o haz clic derecho para asignarlas.", + "calendar_title": "Calendario", + "calendar_desc": "Cambia al calendario para gestionar tus eventos. Crea eventos, configura recordatorios y elige diferentes vistas.", + "contacts_title": "Contactos", + "contacts_desc": "Tu libreta de direcciones está aquí. Importa contactos, crea grupos y consulta los detalles.", + "settings_title": "Ajustes", + "settings_desc": "Personaliza todo: tema, densidad, firmas, filtros, atajos de teclado, valores predeterminados del calendario y más.", + "shortcuts_title": "Atajos de teclado", + "shortcuts_desc": "Para usuarios avanzados. Pulsa ? en cualquier momento para ver todos los atajos disponibles.", + "compose_open_title": "El editor", + "compose_open_desc": "Este es el editor de correo. Añade destinatarios, escribe tu mensaje, adjunta archivos y usa formato de texto enriquecido. También puedes guardar borradores y usar plantillas.", + "calendar_view_title": "Tu calendario", + "calendar_view_desc": "Aquí está tu calendario con eventos de ejemplo. Cambia entre vistas de día, semana, mes y agenda.", + "create_event_title": "Crear un evento", + "create_event_desc": "Haz clic en este botón para crear un nuevo evento de calendario. Puedes establecer un título, fecha, hora y añadir participantes.", + "event_modal_title": "Detalles del evento", + "event_modal_desc": "Aquí está el formulario del evento. Rellena el título, elige una fecha y hora, añade una ubicación o participantes. Pulsa guardar cuando hayas terminado - o ciérralo y continúa.", + "contacts_list_title": "Tus contactos", + "contacts_list_desc": "Aquí están tus contactos. Haz clic en cualquier contacto para ver sus detalles. Puedes crear contactos nuevos o importar vCards.", + "files_title": "Almacenamiento de archivos", + "settings_tabs_title": "Menú de ajustes", + "settings_tabs_desc": "Aquí están todas las categorías de ajustes. Personaliza la apariencia, gestiona identidades, configura filtros de correo, ajusta tu calendario y mucho más.", + "files_desc": "Tu explorador de archivos para subir, organizar y compartir archivos.", + "demo_banner_title": "Controles de demo", + "demo_banner_desc": "Estás en modo demo - todo permanece en tu navegador. Haz clic en 'Restablecer demo' en cualquier momento.", + "quota_title": "Uso de almacenamiento", + "quota_desc": "Controla el tamaño de tu buzón aquí. El círculo se llena a medida que usas más espacio." + }, + "unified_mailbox": { + "search_unavailable": "La búsqueda no está disponible en la vista unificada" + }, + "calendar_event_notifications": { + "someone": "Alguien", + "invited": "{name} te ha invitado a \"{title}\"", + "updated": "{name} ha actualizado \"{title}\"", + "cancelled": "{name} ha cancelado \"{title}\"", + "untitled": "Evento sin título" + }, + "share_notifications": { + "someone": "Alguien", + "shared": "{name} ha compartido contigo {kind} \"{object}\"", + "changed": "{name} ha cambiado tu acceso a {kind} \"{object}\"", + "revoked": "{name} ha retirado tu acceso a {kind} \"{object}\"", + "object": { + "folder": "la carpeta", + "calendar": "el calendario", + "address_book": "la libreta de direcciones", + "files": "la carpeta de archivos" + } + }, + "sharing": { + "title": "Compartir «{name}»", + "description": "Concede acceso a otros usuarios o grupos de este servidor. Los cambios surten efecto inmediatamente.", + "no_shares": "Aún no se ha compartido con nadie.", + "add_person": "Añadir persona o grupo", + "search_placeholder": "Buscar por nombre o correo…", + "loading_principals": "Cargando usuarios…", + "no_principals": "No se han encontrado otros usuarios ni grupos.", + "no_match": "Sin resultados.", + "remove": "Quitar acceso", + "group": "Grupo", + "share_added": "Acceso concedido", + "share_updated": "Acceso actualizado", + "share_removed": "Acceso retirado", + "share_failed": "No se pudo actualizar el uso compartido", + "preset": { + "freeBusy": "Solo disponibilidad", + "read": "Solo lectura", + "readWrite": "Lectura y escritura", + "manager": "Administrador", + "custom": "Personalizado" + } + }, + "quote_header": { + "reply_line": "El {date}, {from} escribió:", + "forwarded_separator": "---------- Mensaje reenviado ----------", + "from_label": "De", + "date_label": "Fecha", + "subject_label": "Asunto" + }, + "pwa_install": { + "title": "Instalar {appName}", + "description": "Instala nuestra app para un acceso rápido y soporte sin conexión.", + "not_now": "Ahora no", + "install": "Instalar", + "dont_remind": "No volver a recordármelo", + "dismiss_aria": "Cerrar aviso de instalación" + }, + "deep_link": { + "message_not_found": "Este mensaje ya no está disponible.", + "thread_not_found": "Esta conversación ya no está disponible.", + "folder_not_found": "Esta carpeta ya no está disponible.", + "event_not_found": "Este evento ya no está disponible.", + "file_not_found": "Este archivo ya no está disponible.", + "account_unavailable": "Inicia sesión en esa cuenta para abrir este enlace.", + "copy_message": "Copiar enlace del mensaje", + "copy_conversation": "Copiar enlace de la conversación", + "copy_event": "Copiar enlace del evento", + "copied": "Enlace copiado", + "copy_failed": "No se pudo copiar el enlace" + }, + "plugins": { + "oauth_callback": { + "completing": "Completando la autorización…", + "completing_message": "Por favor, espera mientras se establece la conexión.", + "received_title": "Autorización recibida", + "received_message": "Puedes volver a la pestaña donde iniciaste la conexión.", + "failed_title": "Error de autorización", + "denied": "Se denegó la autorización.", + "failed_detail": "Error de autorización: {error}", + "missing_params": "Falta el código de autorización o el estado.", + "delivery_failed": "No se pudo entregar el resultado de la autorización a la pestaña de la aplicación. Cierre esta ventana e inténtelo de nuevo." + }, + "settings": { + "title": "Complementos", + "description": "Complementos que añadió tu administrador. Actívalos o desactívalos para tu cuenta.", + "description_uploads": "Los complementos añaden funciones al webmail. Actívalos o desactívalos para tu cuenta, o instala los tuyos.", + "description_demo": "Los complementos no están disponibles en la demo.", + "install_button": "Instalar complemento", + "install_hint": "Elige el archivo ZIP de un complemento.", + "install_hint_approval": "Elige el archivo ZIP de un complemento. Un administrador tiene que aprobarlo antes de que puedas activarlo.", + "install_uploads_off": "Tu administrador no permite instalar complementos.", + "install_managed_id": "Tu administrador ya ofrece un complemento con este nombre, así que no se puede reemplazar.", + "install_invalid": "Este archivo no es un complemento que se pueda instalar: {details}", + "toast_installed": "Complemento instalado. Actívalo abajo.", + "toast_installed_pending": "Complemento instalado y enviado a tu administrador para su aprobación.", + "toast_enabled": "{name} está activado", + "toast_disabled": "{name} está desactivado", + "toast_removed": "Se eliminó {name}", + "toast_forced": "Tu administrador mantiene {name} activado", + "toast_approval_requested": "{name} está pendiente de la aprobación de tu administrador", + "empty_title": "Aún no hay complementos", + "empty_admin": "Tu administrador no ha añadido ningún complemento.", + "empty_uploads": "Instala un complemento desde un archivo ZIP para empezar.", + "empty_demo": "Los complementos necesitan una cuenta real, así que la demo no tiene ninguno.", + "syncing": "Comprobando los complementos con el servidor…", + "status_installed": "Desactivado", + "status_enabled": "Iniciando", + "status_running": "Activado", + "status_disabled": "Desactivado", + "status_error": "Problema", + "badge_forced": "Obligatorio", + "badge_managed": "De tu administrador", + "badge_yours": "Instalado por ti", + "badge_awaiting": "Pendiente de aprobación", + "toggle_label": "Activar o desactivar {name}", + "forced_note": "Tu administrador exige este complemento, así que no se puede desactivar.", + "permissions": "Qué puede hacer", + "permissions_managed": "Qué puede hacer (aprobado por tu administrador)", + "settings": "Configuración", + "remove": "Eliminar", + "remove_title": "¿Eliminar {name}?", + "remove_message": "Se eliminarán el complemento y todo lo que guardó en este navegador. Puedes volver a instalarlo más tarde.", + "remove_confirm": "Eliminar", + "error_user_plugins_off": "Tu administrador desactivó los complementos instalados por usuarios, así que este complemento no se ejecuta.", + "error_approval_unavailable": "Este complemento se instaló antes de que se exigiera aprobación. Elimínalo e instálalo de nuevo para enviarlo a aprobación.", + "error_approval_denied": "Tu administrador no aprobó este complemento.", + "error_approval_pending": "Esperando a que tu administrador apruebe este complemento.", + "error_auto_disabled": "Desactivado porque fallaba una y otra vez. Actívalo de nuevo para volver a intentarlo.", + "accounts_heading": "Cuentas conectadas", + "account_on": "{label} en {host}", + "account_waiting": "{label} en {host} (inicio de sesión sin terminar)", + "disconnect": "Desconectar", + "disconnect_failed": "No se pudo desconectar {label}" + }, + "consent": { + "title": "¿Permitir que {name} haga esto?", + "intro": "El complemento pide los permisos de abajo. Solo los obtiene si se los concedes.", + "high_risk_note": "Los elementos marcados dan al complemento acceso al contenido de tu correo o le permiten enviar datos a otros sitios. Permítelos solo en complementos de confianza.", + "revoke_hint": "Puedes desactivar o eliminar el complemento en Configuración en cualquier momento.", + "deny": "No permitir", + "allow": "Permitir" + }, + "permissions": { + "none": "No necesita permisos especiales.", + "high_risk": "Con cuidado", + "email_read": { + "title": "Leer tu correo", + "body": "Ver remitentes, destinatarios, asuntos y el texto completo de cualquier mensaje de tu cuenta, y tus carpetas." + }, + "email_write": { + "title": "Cambiar y organizar tu correo", + "body": "Añadir o quitar etiquetas, marcas y otras palabras clave de tus mensajes, moverlos entre tus carpetas y al archivo, y guardar borradores." + }, + "email_send": { + "title": "Cambiar el correo antes de enviarlo", + "body": "Leer y cambiar los destinatarios, el asunto, el texto y los adjuntos de los mensajes que escribes, e impedir que se envíe un mensaje." + }, + "crypto_full": { + "title": "Acceso total a esta aplicación", + "body": "Se ejecuta con los mismos derechos que el propio webmail. Puede leer todo tu correo y tus claves guardadas y actuar en tu nombre. Permítelo solo en complementos en los que confíes plenamente." + }, + "email_raw_send": { + "title": "Enviar mensajes que creó él mismo", + "body": "Enviar mensajes completos, por ejemplo firmados o cifrados, desde tu cuenta." + }, + "email_blob_read": { + "title": "Leer el contenido sin procesar de los mensajes", + "body": "Leer los datos originales de tus mensajes y adjuntos, y de cada archivo que adjuntas antes de enviarlo." + }, + "email_blob_write": { + "title": "Reemplazar adjuntos antes de enviar", + "body": "Subir archivos a tu servidor de correo y reemplazar un archivo adjunto por otro antes de enviar el mensaje." + }, + "email_render_takeover": { + "title": "Cambiar cómo se muestran los mensajes", + "body": "Reemplazar el texto de los mensajes que abres y de la lista de mensajes, por ejemplo para mostrar contenido descifrado." + }, + "calendar_read": { + "title": "Leer tu calendario", + "body": "Ver tus calendarios y sus eventos." + }, + "calendar_write": { + "title": "Cambiar tu calendario", + "body": "Crear, cambiar y eliminar eventos." + }, + "contacts_read": { + "title": "Leer tus contactos", + "body": "Buscar y leer en tus libretas de direcciones." + }, + "contacts_write": { + "title": "Cambiar tus contactos", + "body": "Crear, cambiar y eliminar contactos y libretas de direcciones." + }, + "files_read": { + "title": "Leer tus archivos", + "body": "Explorar y abrir los archivos guardados en tu cuenta." + }, + "files_write": { + "title": "Cambiar tus archivos", + "body": "Subir, renombrar, mover y eliminar archivos de tu cuenta." + }, + "identity_read": { + "title": "Ver tus direcciones de remitente", + "body": "Ver las direcciones desde las que envías y sus firmas." + }, + "identity_write": { + "title": "Cambiar tus direcciones de remitente", + "body": "Crear, cambiar y eliminar direcciones de remitente y firmas." + }, + "filters_read": { + "title": "Leer tus filtros de correo", + "body": "Ver las reglas que ordenan tu correo entrante." + }, + "filters_write": { + "title": "Cambiar tus filtros de correo", + "body": "Añadir reglas a tus filtros del servidor. Esas reglas pueden mover, reenviar o eliminar el correo entrante." + }, + "tasks_read": { + "title": "Leer tus tareas", + "body": "Ver tus listas de tareas y tus tareas." + }, + "tasks_write": { + "title": "Cambiar tus tareas", + "body": "Crear, cambiar, completar y eliminar tareas." + }, + "templates_read": { + "title": "Leer tus plantillas", + "body": "Ver tus plantillas de mensaje guardadas." + }, + "templates_write": { + "title": "Cambiar tus plantillas", + "body": "Crear, cambiar y eliminar plantillas de mensaje." + }, + "smime_read": { + "title": "Ver tu estado de S/MIME", + "body": "Ver si tienes claves y certificados S/MIME, sin acceso a las claves." + }, + "vacation_read": { + "title": "Ver tu respuesta de ausencia", + "body": "Ver si tu respuesta automática está activada y qué dice." + }, + "vacation_write": { + "title": "Cambiar tu respuesta de ausencia", + "body": "Activar o desactivar tu respuesta automática y cambiar su texto y sus fechas." + }, + "settings_read": { + "title": "Leer tu configuración", + "body": "Ver tus preferencias y las definiciones de tus etiquetas." + }, + "settings_write": { + "title": "Cambiar tu configuración", + "body": "Añadir y reordenar etiquetas y cambiar tus preferencias." + }, + "security_read": { + "title": "Ver la seguridad de tu cuenta", + "body": "Ver si el inicio de sesión con dos factores y el cifrado están activados, sin acceso a secretos." + }, + "auth_observe": { + "title": "Saber cuándo inicias sesión", + "body": "Recibir un aviso cuando inicias sesión, cambias de cuenta o se renueva tu sesión." + }, + "auth_emit": { + "title": "Cerrar tu sesión", + "body": "Cerrar tu sesión en el webmail." + }, + "account_read": { + "title": "Ver tus cuentas", + "body": "Ver las cuentas que añadiste, con sus direcciones y servidores." + }, + "http_post": { + "title": "Usar el servidor del webmail por ti", + "body": "Llamar a estos endpoints del servidor con tu inicio de sesión:" + }, + "http_fetch": { + "title": "Conectarse a otros sitios web", + "body": "Enviar solicitudes a estos sitios. Todo lo que el complemento pueda leer se puede enviar allí:" + }, + "admin_config": { + "title": "Leer su configuración de tu administrador", + "body": "Leer la configuración que tu administrador guardó para este complemento." + }, + "ui_observe": { + "title": "Seguir dónde estás en la aplicación", + "body": "Saber qué página está abierta y reaccionar a los enlaces en los que haces clic." + }, + "ui_toolbar": { + "title": "Añadir botones a las barras de herramientas", + "body": "Mostrar sus propios botones en la barra de herramientas de mensajes, en un mensaje abierto y en la barra superior." + }, + "ui_app_top_banner": { + "title": "Mostrar un banner arriba", + "body": "Mostrar un aviso a lo ancho de la parte superior de la aplicación." + }, + "ui_email_banner": { + "title": "Mostrar avisos en los mensajes", + "body": "Mostrar sus propios avisos y resúmenes encima, dentro y entre las partes de un mensaje o una conversación abiertos." + }, + "ui_email_footer": { + "title": "Añadir contenido debajo de los mensajes", + "body": "Mostrar su propio contenido debajo de un mensaje abierto." + }, + "ui_contact_cryptokeys": { + "title": "Mostrar las claves de cifrado de los contactos", + "body": "Mostrar y gestionar las claves de cifrado de un contacto." + }, + "ui_email_details": { + "title": "Añadir paneles a los detalles del mensaje", + "body": "Mostrar su propio panel junto a un mensaje abierto o debajo de él." + }, + "ui_download_file": { + "title": "Guardar archivos en tu dispositivo", + "body": "Ofrecer para descargar archivos que creó." + }, + "ui_message_list_tabs": { + "title": "Añadir pestañas a tu bandeja de entrada", + "body": "Ordenar tu bandeja de entrada en pestañas como Novedades o Promociones." + }, + "ui_composer_toolbar": { + "title": "Añadir al editor", + "body": "Mostrar sus propios botones, entradas de menú y secciones mientras escribes un mensaje." + }, + "ui_composer_sidebar": { + "title": "Añadir un panel al editor", + "body": "Mostrar su propio panel junto al mensaje que escribes." + }, + "ui_sidebar_widget": { + "title": "Añadir a la barra lateral", + "body": "Mostrar sus propios recuadros y secciones en la barra lateral del correo." + }, + "ui_settings_section": { + "title": "Añadir una página de configuración", + "body": "Mostrar su propia página en Configuración." + }, + "ui_context_menu": { + "title": "Añadir al menú del mensaje", + "body": "Añadir entradas al menú que se abre al hacer clic derecho en un mensaje." + }, + "ui_navigation_rail": { + "title": "Añadir a la barra de navegación", + "body": "Mostrar su propio botón en la barra de navegación." + }, + "ui_keyboard": { + "title": "Añadir atajos de teclado", + "body": "Reaccionar a los atajos de teclado que define." + }, + "ui_calendar_action": { + "title": "Añadir a la vista de calendario", + "body": "Mostrar sus propios botones y secciones en los eventos, y etiquetas cortas y puntos en los días del calendario." + }, + "ui_admin_page": { + "title": "Añadir una página de administración", + "body": "Mostrar su propia página en la configuración de administración." + }, + "app_lifecycle": { + "title": "Saber cuándo se inicia y se cierra la aplicación", + "body": "Recibir un aviso cuando la aplicación se inicia, se queda sin conexión o cierras sesión." + }, + "compose_open": { + "title": "Abrir el editor por ti", + "body": "Abrir un mensaje nuevo con los destinatarios, el texto o los adjuntos ya rellenados. Tú sigues decidiendo si se envía." + }, + "compose_read": { + "title": "Leer el mensaje que escribes", + "body": "Leer el texto de un mensaje mientras lo escribes, cuando usas el complemento en el editor. No la firma ni el mensaje citado." + }, + "compose_write": { + "title": "Cambiar el mensaje que escribes", + "body": "Sustituir o añadir texto en un mensaje mientras lo escribes, cuando usas el complemento en el editor. Puedes deshacer cada cambio y sigues decidiendo si lo envías." + }, + "ui_notifications": { + "title": "Mostrar notificaciones", + "body": "Mostrar notificaciones en tu dispositivo, o dentro de la aplicación si no las has permitido. Cada una indica el nombre del complemento." + }, + "ui_message_list": { + "title": "Añadir a la lista de mensajes", + "body": "Mostrar su propio contenido encima de la lista de mensajes, y botones en los mensajes y en la barra de los mensajes seleccionados." + }, + "ui_contacts": { + "title": "Añadir a la vista de contactos", + "body": "Mostrar sus propias secciones en los detalles de un contacto y botones en la lista de contactos." + }, + "ui_tasks": { + "title": "Añadir a la vista de tareas", + "body": "Mostrar sus propias secciones en los detalles de una tarea y botones en la lista de tareas." + }, + "ui_files": { + "title": "Añadir a la vista de archivos", + "body": "Mostrar sus propias secciones en la vista previa de archivos y entradas en el menú de un archivo." + }, + "ui_commands": { + "title": "Añadir comandos", + "body": "Añadir sus propias entradas a la paleta de comandos (Ctrl+K)." + }, + "ui_app_page": { + "title": "Añadir páginas", + "body": "Añadir sus propias páginas a la aplicación, cada una con un botón en la barra de navegación." + }, + "http_proxy": { + "title": "Usar las cuentas que conectes", + "body": "Enviar solicitudes a través del servidor de Bulwark con una cuenta o clave configurada por ti o por tu administrador. El complemento nunca ve la contraseña, pero puede hacer todo lo que esa cuenta puede hacer en su propio servidor." + } + }, + "dialog": { + "from_plugin": "Del complemento {name}", + "ok": "Aceptar", + "submit": "Enviar", + "confirm": "Confirmar", + "cancel": "Cancelar", + "close": "Cerrar", + "turn_off": "Desactivar {name}", + "plugin_menu": "Opciones de {name}", + "password_note": "Este formulario viene del complemento {name}, no del correo web. Introduce una contraseña solo si confías en este complemento.", + "toast_auto_disabled": "{name} se desactivó porque fallaba una y otra vez." + }, + "connect": { + "title": "Conectar {label}", + "intro_password": "{name} quiere usar tu cuenta de {label}. Bulwark guarda la contraseña en su servidor y solo la envía a {host}. El complemento nunca la ve.", + "intro_password_any": "{name} quiere usar tu cuenta de {label}. Bulwark guarda la contraseña en su servidor y solo la envía al servidor que indiques aquí. El complemento nunca la ve.", + "intro_signin": "{name} quiere iniciar sesión en tu cuenta de {label}. Indica aquí el servidor y luego inicia sesión en su propia página. Bulwark guarda en su servidor el acceso que obtiene; el complemento nunca lo ve.", + "server": "Dirección del servidor", + "server_hint": "cloud.example.com", + "username": "Nombre de usuario", + "secret": "Contraseña o clave", + "connect": "Conectar", + "continue": "Continuar", + "cancel": "Cancelar", + "saving": "Guardando…", + "error_server": "Indica la dirección del servidor, por ejemplo https://cloud.example.com", + "error_save": "No se pudo guardar: {error}" + } + }, + "global_search": { + "title": "Buscar en todo", + "placeholder": "Buscar correo, contactos, calendario, archivos...", + "scope_label": "Buscar en", + "account_label": "Cuenta", + "scope_all": "Todo", + "scope_mail": "Correo", + "scope_contacts": "Contactos", + "scope_calendar": "Calendario", + "scope_files": "Archivos", + "all_accounts": "Todas las cuentas", + "show_all": "Mostrar todos los resultados", + "no_results": "Sin resultados para “{query}”", + "server_hint": "Las palabras completas buscan en el servidor; las parciales coinciden con lo ya cargado", + "searching": "Buscando...", + "account_error": "No se pudo buscar en {account}", + "recent_searches": "Búsquedas recientes", + "recurring": "Periódico", + "no_subject": "(no subject)", + "empty_hint": "Busca correo, contactos, eventos y archivos en todas las cuentas. Prueba from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Plano por fecha", + "grouped": "Agrupado", + "load_more": "Cargar más", + "preview_empty": "Selecciona un resultado para previsualizarlo", + "open_full": "Abrir", + "view_label": "Vista" + }, + "top_bar": { + "more": "Más opciones" + }, + "plugin_pages": { + "loading": "Cargando la página", + "not_available_title": "Esta página no está disponible", + "not_available_description": "El complemento que la añadió está desactivado o ya no está instalado.", + "back_to_mail": "Volver al correo", + "settings_group": "Complementos" + }, + "command_palette": { + "title": "Paleta de comandos", + "open": "Abrir la paleta de comandos", + "placeholder": "Escribe un comando", + "no_results": "No hay comandos que coincidan", + "go_to": "Ir a {name}", + "compose": "Mensaje nuevo", + "search": "Buscar en el correo", + "theme_dark": "Cambiar al tema oscuro", + "theme_light": "Cambiar al tema claro", + "shortcuts": "Mostrar atajos de teclado", + "group_navigation": "Ir a", + "group_actions": "Acciones", + "group_plugins": "Complementos" + }, + "notification_center": { + "title": "Notificaciones", + "mark_all_read": "Marcar todo como leído", + "clear": "Borrar", + "empty": "No hay notificaciones", + "close": "Cerrar", + "unread": "Sin leer", + "unread_count": "{count} sin leer", + "level_error": "Error", + "level_warning": "Advertencia" + } +} diff --git a/locales/et/common.json b/locales/et/common.json new file mode 100644 index 00000000..527d286f --- /dev/null +++ b/locales/et/common.json @@ -0,0 +1,4506 @@ +{ + "meta_description": "Minimalistlik JMAP-protokolli kasutav veebipostiklient", + "login": { + "title": "Veebipost", + "username_label": "E-post", + "username_placeholder": "sinu@email.ee", + "password_label": "Parool", + "password_placeholder": "Sisesta oma parool", + "jmap_endpoint_label": "JMAP server", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Server peab lubama CORS-päringuid sellelt domeenilt.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Server valiti automaatselt sinu e-posti domeeni järgi.", + "sign_in": "Logi sisse", + "signing_in": "Sisselogimine...", + "loading": "Laadimine...", + "reconnecting": "Ühendus katkes. Proovin uuesti ühenduda…", + "error": { + "invalid_credentials": "Vale e-posti aadress või parool. Palun kontrolli andmeid ja proovi uuesti.", + "invalid_token": "Server ei võtnud seda juurdepääsuluba vastu. Kontrolli, et see on täielik ning pole aegunud ega tühistatud.", + "connection_failed": "Serveriga ei saanud ühendust. Kontrolli internetiühendust ja proovi uuesti.", + "cors_blocked": "Server on saadaval, kuid blokeerib ristpäritolu (cross-origin) päringuid. Kontrolli oma JMAP serveri CORS-seadeid ja luba see domeen.", + "server_error": "Server on ajutiselt kättesaamatu. Palun proovi hiljem uuesti.", + "generic": "Ilmnes ootamatu viga. Kui probleem püsib, võta ühendust administraatoriga.", + "totp_required": "Vajalik on kaheastmelise autentimise kood. Palun sisesta kood allpool.", + "totp_invalid": "Vale autentimiskood. Palun kontrolli autentimisrakendust ja proovi uuesti.", + "oauth_discovery_failed": "SSO on lubatud, kuid identiteedipakkujaga ei saadud ühendust. Kontrolli OAuth seadistust.", + "token_exchange_failed": "Sinu parool ja kood võeti vastu, aga meiliserver keeldus sellele rakendusele seansi algatamisest. Palu administraatoril kontrollida selle OAuth-kliendi seadeid." + }, + "show_password": "Näita parooli", + "hide_password": "Peida parool", + "totp_toggle": "Mul on kaheastmelise autentimise kood", + "token_toggle": "Logi sisse juurdepääsuloaga", + "password_toggle": "Logi sisse parooliga", + "token_label": "Juurdepääsuluba", + "token_placeholder": "Kleebi oma API-luba", + "token_hint": "Loo oma meiliteenuse seadetes API-luba ja kleebi see siia.", + "remember_me": "Jäta mind meelde", + "config_error": { + "title": "Seadistuse viga", + "fetch_failed": "Rakenduse seadistust ei õnnestunud laadida. Palun proovi hiljem uuesti.", + "server_not_configured": "Postiserverit ei ole seadistatud. Palun võta ühendust administraatoriga." + }, + "remove_from_history": "Eemalda ajaloost", + "totp_label": "Autentimiskood", + "totp_placeholder": "000000", + "session_expired": "Sinu sessioon aegus. Palun logi uuesti sisse.", + "dismiss": "Sulge", + "or": "või", + "sign_in_sso": "Logi sisse SSO-ga", + "add_account_title": "Lisa konto", + "add_account_subtitle": "Logi sisse teise kontoga", + "cancel": "Tühista", + "website": "Veebileht", + "imprint": "Väljaandja andmed", + "privacy_policy": "Privaatsuspoliitika", + "try_demo": "Proovi demot", + "demo_description": "Tutvu näidisandmetega - kontot ei ole vaja", + "demo_launching": "Demo käivitamine...", + "demo_login_button": "Käivita demo", + "demo_tagline": "Proovi täisfunktsionaalset e-posti klienti. Kontot pole vaja.", + "demo_no_signup": "Registreerimist pole vaja - tutvu vabalt näidisandmetega", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Sisselogimise lõpetamine...", + "oauth_error": { + "title": "Autentimine ebaõnnestus", + "invalid_state": "Turvakontroll ebaõnnestus. Palun proovi uuesti sisse logida.", + "missing_params": "Autoriseerimisandmed puuduvad. Palun proovi uuesti sisse logida.", + "token_exchange_failed": "Autentimist ei õnnestunud lõpetada. Palun proovi uuesti.", + "access_denied": "Juurdepääs keelati. Palun võta ühendust administraatoriga.", + "back_to_login": "Tagasi" + } + }, + "sidebar": { + "close": "Sulge", + "compose": "Koosta", + "compose_hint": "Koosta (c)", + "search_placeholder": "Otsi kirju...", + "search_placeholder_hint": "Otsi kirju... (vajuta /)", + "storage": "Talletusruum", + "storage_used": "Kasutatud", + "storage_free": "Vaba", + "storage_total": "Kokku", + "sign_out": "Logi välja", + "sign_out_of": "Logi välja kontolt {account}", + "sign_out_all": "Logi välja kõigist kontodest", + "add_account": "Lisa konto", + "set_as_default": "Määra vaikimisi", + "switch_account": "Vaheta kontot", + "contacts": "Kontaktid", + "calendar": "Kalender", + "settings": "Seaded", + "admin": "Haldus", + "files": "Failid", + "loading_mailboxes": "Postkastide laadimine...", + "push_connected": "Reaalajas uuendused aktiivsed", + "push_disconnected": "Reaalajas uuendused mitteaktiivsed", + "keyboard_shortcuts": "Kiirklahvid", + "theme": { + "light": "Hele režiim", + "dark": "Tume režiim", + "system": "Süsteemi teema" + }, + "language": { + "title": "Keel" + }, + "mailboxes": { + "inbox": "Saabunud", + "sent": "Saadetud", + "drafts": "Mustandid", + "trash": "Prügikast", + "archive": "Arhiiv", + "starred": "Tähega tähistatud", + "all_mail": "Kõik kirjad", + "spam": "Rämpspost", + "important": "Oluline" + }, + "unified_inbox": "Ühine saabunud", + "unified_sent": "Kõik saadetud", + "unified_drafts": "Kõik mustandid", + "unified_trash": "Kõik prügikastid", + "unified_archive": "Kõik arhiivid", + "unified_junk": "Kõik rämpspost", + "all_accounts": "Kõik kontod", + "all_accounts_loading": "Kontode laadimine: {loaded} / {total}", + "unified_mailbox": "Ühine postkast", + "expand": "Laienda", + "collapse": "Ahenda", + "expand_tooltip": "Laienda", + "collapse_tooltip": "Ahenda", + "mobile": { + "search": "Otsi", + "compose": "Koosta", + "go_back": "Mine tagasi", + "toggle_menu": "Ava/sulge menüü" + }, + "clear_search": "Tühjenda otsing", + "vacation_active": "Puhkuse vastaja on aktiivne", + "demo_banner": "Demorežiim", + "demo_reset": "Lähtesta", + "demo_tour": "Tutvustus", + "tags": "Sildid", + "show_all_tags": "Näita kõiki ({count})", + "show_fewer_tags": "Näita vähem", + "folders": "Kaustad", + "your_folders": "Sinu kaustad", + "create_folder": "Loo kaust", + "show_more": "Rohkem", + "show_fewer": "Vähem", + "shared": "Jagatud", + "shared_account_hidden": "Külgribal peidetud. Lülita see tagasi sisse menüüs Seaded › Paigutus.", + "notifications_muted": "Märguanded vaigistatud", + "mail": "Kirjad", + "nav_label": "Navigeerimine", + "add_app": "Rakendused", + "scheduled": "Ajastatud", + "unified_all_unread": "Kõik lugemata", + "unified_all_starred": "Kõik tähega tähistatud", + "unified_all_mail": "Kõik kirjad", + "remove_account": "Eemalda konto", + "remove_account_confirm": "Eemalda {account} sellest seadmest? Saad selle hiljem uuesti lisada.", + "labels": "Sildid", + "create_label": "Loo uus silt", + "mv_tasks": "Ülesanded", + "mv_side_panel": "Külgpaneel", + "mv_open_full": "Ava täisvaates", + "mv_previous_day": "Eelmine päev", + "mv_next_day": "Järgmine päev", + "mv_add_another_account": "Lisa veel üks konto", + "mv_manage_account": "Halda oma kontot", + "mv_storage_used": "{percent}% mahust {total} kasutatud" + }, + "protocol_handlers": { + "title": "Vaikerakendused", + "description": "Vali, kas e-posti ja kalendri lingid avanevad Bulwarkis. Tehniliselt registreerib Bulwark end mailto: ja webcal: linkide protokollikäsitlejana.", + "unsupported": "See brauser või ühendus ei toeta protokollikäsitleja käsitsi registreerimist. Paigaldatud PWA-d saad siiski kasutada brauseri või operatsioonisüsteemi seadete kaudu.", + "mailto_label": "E-posti lingid", + "mailto_description": "Ava mailto: lingid Bulwarkis koos eeltäidetud kirja koostamise aknaga.", + "protocol_open_mode_label": "Protokollilinkide avamisel", + "protocol_open_mode_description": "Vali, kas Bulwark avab mailto: ja webcal: lingid uuel vahekaardil või kasutab olemasolevat sessiooni. Aktiivse sessiooni valik vajab teavituste õigust, et saaksid vajadusel klõpsata varuteavitusel, mis toob Bulwarki esiplaanile, kui brauser blokeerib fookuse liikumise.", + "protocol_open_mode_active_session": "Ava võimalusel aktiivses sessioonis", + "protocol_open_mode_new_tab": "Ava alati uuel vahekaardil", + "focus_notification_title": "Ava Bulwark", + "focus_notification_body": "Link avati Bulwarkis. Klõpsa, et tuua aken esiplaanile.", + "webcal_label": "Kalendri lingid", + "webcal_description": "Ava webcal: lingid Bulwarkis koos eeltäidetud kalendri tellimise dialoogiga.", + "register_mailto": "Registreeri e-posti rakendus", + "register_webcal": "Registreeri kalendrirakendus", + "mailto_registered": "E-posti käsitleja registreerimist taotleti", + "webcal_registered": "Kalendri käsitleja registreerimist taotleti", + "registration_failed": "Protokollikäsitleja registreerimine ebaõnnestus", + "opening_mailto": "Kirja koostamise akna avamine...", + "opening_webcal": "Kalendri avamine...", + "browser_note": "Sinu brauser või operatsioonisüsteem võib paluda seda kinnitada ning enne vaikerakenduseks valimist võib nõuda Bulwarki paigaldamist.", + "select_account_title": "Vali konto", + "select_mailto_account": "Vali, milline konto peaks selle e-posti lingi avama.", + "select_webcal_account": "Vali, milline konto peaks selle kalendrilingi avama.", + "select_account_note": "See kehtib ainult selle protokollilingi kohta.", + "detail_to": "Kellele", + "detail_subject": "Teema", + "detail_no_subject": "Teema puudub", + "detail_calendar": "Kalender", + "detail_source": "Allikas", + "active_account": "Aktiivne", + "switching_account": "Konto vahetamine..." + }, + "sidebar_apps": { + "modal_title": "Külgriba rakendused", + "add_new": "Lisa rakendus", + "edit_app": "Muuda rakendust", + "name_label": "Nimi", + "name_placeholder": "Minu rakendus", + "name_required": "Nimi on kohustuslik", + "url_label": "URL", + "url_required": "URL on kohustuslik", + "url_invalid": "Sisesta kehtiv http või https URL", + "icon_label": "Ikoon", + "icon_required": "Ikoon on kohustuslik", + "open_mode_label": "Avamisrežiim", + "open_new_tab": "Uus vahekaart", + "open_inline": "Samas aknas", + "cancel": "Tühista", + "add": "Lisa", + "update": "Uuenda", + "delete": "Kustuta", + "delete_confirm_title": "Kustuta rakendus", + "delete_confirm": "Kas oled kindel, et soovid kustutada \"{name}\"?", + "no_apps": "Rakendusi pole veel lisatud", + "no_apps_hint": "Lisa oma külgribale kohandatud rakendusi ja linke", + "managed_title": "Pakutud sinu administraatori poolt", + "managed_description": "Need rakendused on seadistatud kõigile ja neid ei saa muuta.", + "managed_badge": "Hallatud", + "search_icons": "Otsi ikoone...", + "show_popular": "Populaarsed", + "show_all": "Kõik", + "no_icons_found": "Ikoone ei leitud", + "inline_badge": "Samas aknas", + "tab_badge": "Vahekaart", + "show_on_mobile": "Näita mobiilis" + }, + "email_list": { + "columns": { + "from": "Saatja", + "to": "Saaja", + "subject": "Teema", + "date": "Kuupäev", + "size": "Suurus" + }, + "no_emails": "Sõnumeid ei leitud", + "no_emails_description": "See postkast on tühi", + "no_search_results": "Tulemusi ei leitud", + "no_search_results_description": "Proovi otsingut või filtreid muuta", + "loading": "Kirjade laadimine...", + "unread": "lugemata", + "to_me": "Minule", + "to_recipients": "{count} saajale", + "and_others": "ja veel {count}", + "draft": "Mustand", + "starred": "Tähega tähistatud", + "pinned": "Kinnitatud", + "replied": "Vastatud", + "forwarded": "Edastatud", + "has_attachment": "Sisaldab manust", + "conversations_range": "{from}–{to, number} / {total, number}", + "conversations_count": "{count} vestlust {total}-st", + "conversations_count_plus": "{count}+ vestlust", + "conversations_count_simple": "{count} vestlust", + "no_conversations": "Vestlusi pole", + "loading_more": "Laen rohkem kirju...", + "no_more_emails": "Rohkem kirju pole laadida", + "batch_actions": { + "select": "Vali kirjad", + "select_all": "Vali kõik", + "selected_messages": "{count, plural, one {1 kiri} other {# kirja}} valitud", + "mark_read": "Märgi loetuks", + "mark_unread": "Märgi lugemata", + "delete": "Kustuta", + "delete_confirm_title": "Kirjade kustutamine", + "delete_confirm_message": "Kas oled kindel, et soovid kustutada {count, plural, one {1 kirja} other {# kirja}}?", + "clear_selection": "Tühista valik" + }, + "permanent_delete": "Kustuta jäädavalt", + "permanent_delete_confirm_title": "Jäädav kustutamine", + "permanent_delete_confirm_message": "See kiri kustutatakse jäädavalt. Seda toimingut ei saa tagasi võtta.", + "permanent_delete_confirm_batch_message": "{count, plural, one {See 1 kiri kustutatakse jäädavalt.} other {Need # kirja kustutatakse jäädavalt.}} Seda toimingut ei saa tagasi võtta.", + "empty_folder": { + "button": "Tühjenda kaust", + "confirm_title": "Kausta tühjendamine", + "confirm_message": "Kõik selle kausta kirjad kustutatakse jäädavalt. Seda toimingut ei saa tagasi võtta.", + "confirm_message_trash": "Kõik selle kausta kirjad teisaldatakse prügikasti.", + "confirm_button": "Tühjenda kaust", + "junk_hint": "Rämpsposti kausta tühjendades saad kõik kirjad jäädavalt eemaldada.", + "trash_hint": "Prügikasti tühjendades saad kõik kirjad jäädavalt eemaldada." + }, + "no_scheduled_emails": "Ajastatud kirju pole", + "no_scheduled_emails_description": "Hiljem saatmiseks ajastatud kirjad kuvatakse siin.", + "scheduled_count": "{count, plural, one {1 ajastatud kiri} other {# ajastatud kirja}}", + "scheduled_actions_hint": "Ajastatud kirju saab ajastatud toimingute alt tühistada, ümber ajastada või muuta.", + "cancel_scheduled_send": "Tühista saatmine", + "reschedule_send": "Ajasta uuesti", + "cancel_and_edit": "Tühista ja muuda", + "cancel_and_compose_again": "Tühista ja koosta uuesti", + "reschedule_prompt": "Sisesta uus kuupäev/kellaaeg, nt 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Saatja soovib teadet, kui avad selle kirja.", + "send": "Saada kinnitus", + "ignore": "Eira", + "sent": "Lugemiskinnitus saadetud.", + "send_failed": "Lugemiskinnitust ei õnnestunud saata", + "mdn_subject": "Loetud: {subject}", + "mdn_body": "See on kinnitus kirjale, mille saatsid saajale {recipient}.\n\nMärkus: see kinnitus tõendab vaid, et kiri kuvati saaja arvutis. Ei ole tagatud, et saaja on kirja sisu lugenud või mõistnud." + }, + "no_email_selected": "Kirja pole valitud", + "no_email_description": "Kirja vaatamiseks vali see loetelust", + "no_conversation_selected": "Vestlust pole valitud", + "no_conversation_description": "Vestluse lugemiseks vali see loetelust", + "compose": "Koosta", + "compose_hint": "Koosta uus kiri", + "no_subject": "(Teema puudub)", + "no_body_content": "(Sisu pole saadaval)", + "no_preview_available": "Eelvaadet pole saadaval", + "show_quoted_text": "Näita tsiteeritud teksti", + "hide_quoted_text": "Peida tsiteeritud tekst", + "loading_email": "Kirja laadimine...", + "loading": "Laadimine...", + "reply": "Vasta", + "reply_all": "Vasta kõigile", + "forward": "Saada edasi", + "delete": "Kustuta", + "archive": "Arhiveeri", + "archive_mailbox_not_found": "Arhiivikausta ei leitud - kirja ei saa arhiveerida", + "star": "Tähista tähega", + "unstar": "Eemalda täht", + "mark_unread": "Märgi lugemata", + "mark_read": "Märgi loetuks", + "unread": "Lugemata", + "read": "Loetud", + "spam_short": "Rämpspost", + "not_spam_short": "Pole rämpspost", + "move": "Teisalda", + "print": "Prindi", + "view_source": "Vaata lähtekoodi", + "fullscreen": "Täisekraan", + "exit_fullscreen": "Välju täisekraanist", + "view_in_dark_mode": "Vaata tumedas režiimis", + "view_in_light_mode": "Vaata heledas režiimis", + "export_email": "Ekspordi .eml failina", + "forward_as_attachment": "Saada edasi manusena", + "import_email": "Impordi .eml või .zip", + "keyboard_shortcuts": "Kiirklahvid (?)", + "email_source": "Kirja lähtekood", + "draft_banner": "See kiri on mustand", + "edit_draft": "Muuda", + "copy_source": "Kopeeri lõikelauale", + "source_copied": "Lähtekood kopeeritud lõikelauale", + "attachments": "Manused", + "important": "Oluline", + "download": "Laadi alla", + "download_all": "Laadi kõik alla", + "from": "Saatja", + "to": "Saaja", + "cc": "Koopia", + "bcc": "Pimekoopia", + "date": "Kuupäev", + "subject": "Teema", + "show_details": "Näita üksikasju", + "hide_details": "Peida üksikasjad", + "external_content_warning": "Pildid ja väline sisu on blokeeritud", + "load_external_content": "Laadi pildid", + "trust_sender": "Usalda seda saatjat alati", + "sender_check": { + "unverified_label": "Kontrollimata saatja", + "failed_label": "Saatja kontroll ebaõnnestus", + "unverified": "See kiri väidab, et on saadetud domeenilt {domain}, kuid seda ei kinnita miski. Sellel puudub domeeni {domain} kehtiv allkiri ja see ei tulnud serverist, mille {domain} on volitanud.", + "unverified_sent_from": "See kiri väidab, et on saadetud domeenilt {domain}, kuid see saadeti hostilt {host} ja sellel puudub domeeni {domain} kehtiv allkiri.", + "failed": "See kiri väidab, et on saadetud domeenilt {domain}, kuid see ei läbinud selle domeeni saatjakontrolle. See võib olla võltsitud.", + "failed_sent_from": "See kiri väidab, et on saadetud domeenilt {domain}, kuid see saadeti hostilt {host} ega läbinud domeeni {domain} saatjakontrolle. See võib olla võltsitud.", + "caution": "Ole ettevaatlik linkide, manuste ning paroolide või makseandmete küsimistega." + }, + "back_to_list": "Tagasi loetellu", + "view_contact": "Vaata kontakti", + "message_details": "Kirja üksikasjad", + "more_reply_options": "Rohkem vastamisvõimalusi", + "set_tag": "Määra silt", + "tag": "Silt", + "more_actions": "Rohkem toiminguid", + "previous": "Eelmine", + "next": "Järgmine", + "move_to": "Teisalda kausta...", + "remove_tag": "Eemalda silt", + "tag_filter_placeholder": "Filtreeri silte", + "tag_no_matches": "Sobivaid silte ei leitud", + "more_count": "+{count} veel", + "characters_count": "{count} tähemärki", + "quick_reply_placeholder": "Kirjuta kiirvastus...", + "more_options": "Rohkem valikuid", + "sending": "Saatmine...", + "security_authentication": "Turvalisus ja autentimine", + "technical_details": "Tehnilised üksikasjad", + "message_id_label": "Message-ID:", + "reply_to_label": "Reply-To:", + "delivery_time_label": "Kohaletoimetamise aeg:", + "conversation_part_label": "Osa vestlusest:", + "previous_messages": "{count} eelmine kiri", + "previous_messages_plural": "{count} eelmist kirja", + "time": { + "day": "päev", + "days": "päeva", + "hour": "tund", + "hours": "tundi", + "minute": "minut", + "minutes": "minutit" + }, + "unknown_sender": "Tundmatu", + "recipient_me": "mina", + "recipient_and_others": "{name} ja veel {count}", + "recipient_to_prefix": "Saaja:", + "authentication": { + "title": "Autentimine", + "status": { + "verified": "Kinnitatud", + "warning": "Hoiatus", + "none": "Autentimata" + }, + "spf": { + "pass": "SPF läbitud", + "fail": "SPF ebaõnnestus", + "none": "SPF puudub" + }, + "dkim": { + "pass": "DKIM kehtiv", + "fail": "DKIM kehtetu", + "none": "DKIM puudub" + }, + "dmarc": { + "pass": "DMARC läbitud", + "fail": "DMARC ebaõnnestus", + "none": "DMARC puudub" + }, + "spam_score": "Rämpsposti skoor", + "tooltip_spf": "Sender Policy Framework: kontrollib, kas saatev server on volitatud selle domeeni nimel kirju saatma", + "tooltip_dkim": "DomainKeys Identified Mail: kinnitab krüptograafilise allkirja abil, et kirja ei muudetud edastamise ajal", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: tagab, et SPF ja DKIM vastavad saatja domeenile, ning määrab reeglid ebaõnnestumiste jaoks", + "policy": "Reegel", + "result": { + "pass": "Läbitud", + "fail": "Ebaõnnestus", + "softfail": "Nõrk ebaõnnestumine", + "neutral": "Neutraalne", + "permerror": "Püsiv viga", + "temperror": "Ajutine viga", + "none": "Puudub" + } + }, + "details": { + "recipients_routing": "Saajad ja marsruutimine", + "authentication_security": "Autentimine ja turvalisus", + "identifiers_threading": "Identifikaatorid ja vestlusahel", + "mailing_list": "Meililist", + "message_properties": "Kirja omadused", + "sent": "Saadetud", + "received": "Vastu võetud", + "delivery_time": "Kohaletoimetamise aeg", + "in_reply_to": "In-Reply-To", + "references": "References", + "thread_id": "Vestluse ID", + "size": "Suurus", + "mime_type": "MIME tüüp", + "attachments_summary": "{count} faili · {size}", + "list_id": "Listi ID", + "list_help": "Listi abi", + "list_post": "Listi postitus", + "list_unsubscribe": "Tellimuse tühistamine", + "iprev": "Pöörd-DNS", + "spam_status": "Rämpsposti olek", + "ai_verdict": "AI hinnang", + "account": "Konto", + "no_subject": "(teema puudub)" + }, + "headers": { + "routing": "Marsruutimine", + "received": "Vastu võetud", + "message_id": "Kirja ID", + "list_info": "Listi teave" + }, + "tooltips": { + "reply": "Vasta (r)", + "reply_all": "Vasta kõigile (a)", + "forward": "Saada edasi (f)", + "archive": "Arhiveeri (e)", + "delete": "Kustuta (# või Del)", + "star": "Tähista tähega (s)", + "unstar": "Eemalda täht (s)", + "compose": "Koosta (c)", + "previous": "Eelmine kiri", + "next": "Järgmine kiri", + "edit_draft": "Muuda mustandit" + }, + "spam": { + "button_title": "Märgi rämpspostiks", + "not_spam_title": "Märgi tavaliseks kirjaks", + "toast_success": "Teisaldatud rämpsposti", + "toast_batch": "{count} kirja teisaldatud rämpsposti", + "toast_undo": "Võta tagasi", + "toast_not_spam_success": "Teisaldatud saabunud kirjadesse", + "toast_not_spam_batch": "{count} kirja teisaldatud saabunud kirjadesse", + "error": "Rämpspostiks märkimine ebaõnnestus", + "error_not_spam": "Kirja taastamine ebaõnnestus" + }, + "unsubscribe_banner": { + "label": "Uudiskiri", + "button": "Tühista tellimus", + "confirm_title": "Kas tühistada selle saatja tellimus?", + "confirm_button": "Kinnita", + "cancel": "Tühista", + "success_http": "Tellimuse tühistamise leht avati uues vahekaardis", + "success_mailto": "Tellimuse tühistamise kiri saadetud", + "confirm_message_http": "Tellimuse tühistamise leht avaneb uues vahekaardis.", + "confirm_message_mailto": "Saatjale saadetakse tellimuse tühistamise kiri.", + "error": "Tellimust ei õnnestunud tühistada", + "dismiss": "Sulge" + }, + "calendar_invitation": { + "loading": "Sündmuse üksikasjade laadimine…", + "title": "Kalendrikutse", + "published_title": "Avaldatud sündmus", + "response_title": "Vastus sündmusele", + "update_title": "Sündmuse uuendus", + "counter_title": "Vastuettepanek", + "refresh_title": "Värskenduse taotlus", + "declined_counter_title": "Vastuettepanek tagasi lükatud", + "cancelled_title": "Sündmus tühistatud", + "organizer": "Korraldaja: {name}", + "organizer_label": "Korraldaja", + "attendees": "{count, plural, one {# osaleja} other {# osalejat}}", + "accept": "Nõustu", + "maybe": "Võib-olla", + "decline": "Keeldu", + "add_to_calendar": "Lisa kalendrisse", + "added": "Lisatud kalendrisse", + "rsvp_sent": "Vastus saadetud", + "parse_error": "Kutset ei õnnestunud lugeda", + "action_failed": "Seda kalenditoimingut ei õnnestunud lõpetada.", + "no_calendar": "Kalender pole saadaval", + "published_info": "See sündmus jagati info edastamiseks.", + "response_info": "See kiri sisaldab osaleja vastust.", + "response_info_organizer": "See osaleja vastus uuendab sinu sündmust.", + "update_info": "See kiri uuendab olemasolevat sündmust.", + "counter_info": "See kiri teeb ettepaneku sündmust muuta.", + "counter_info_organizer": "See osaleja tegi ettepaneku sinu sündmust muuta.", + "refresh_info": "See kiri küsib sündmuse uusimaid andmeid.", + "refresh_info_organizer": "Osaleja küsis sündmuse uusimaid andmeid.", + "declined_counter_info": "Korraldaja lükkas vastuettepaneku tagasi.", + "authentication_failed_info": "Selle kutse e-posti autentimiskontrollid ebaõnnestusid. Suhtu kalenditoimingutesse ettevaatlikult.", + "authentication_missing_info": "Sellel kutsel puudub kinnitatud e-posti autentimine. Kui midagi tundub kahtlane, kontrolli üksikasju korraldajaga.", + "sender_mismatch_info": "See kutse saadeti aadressilt {sender}, kuid kalendriandmetes märgitud korraldaja on {organizer}.", + "sender_mismatch_unverified_info": "See kutse saadeti aadressilt {sender}, kuid kalendriandmetes märgitud korraldaja on {organizer}, ning kirja ei õnnestunud kinnitada.", + "organizer_role": "Sina oled selle sündmuse korraldaja", + "your_response": "Sinu vastus: {status}", + "response_needed": "Vajab vastust", + "response_accepted": "Nõustutud", + "response_tentative": "Esialgne", + "response_declined": "Tagasi lükatud", + "response_delegated": "Delegeeritud", + "actor_sent_info": "Saatis {name}.", + "actor_response_info": "{name} vastas: {status}.", + "actor_counter_info": "{name} tegi ettepaneku seda sündmust muuta.", + "actor_refresh_info": "{name} küsis sündmuse uusimaid andmeid.", + "actor_declined_counter_info": "{name} lükkas vastuettepaneku tagasi.", + "actor_note": "Märkus: {comment}", + "actor_unknown": "Keegi", + "proposed_changes": "Pakutud muudatused", + "change_title": "Pealkiri", + "change_time": "Aeg", + "change_location": "Asukoht", + "change_description": "Kirjeldus", + "change_empty": "Puudub", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Rakenda pakutud muudatused", + "proposal_applied": "Pakutud muudatused rakendatud.", + "review_proposal": "Vaata ettepanekut üle", + "review_request": "Vaata taotlust üle", + "view_in_calendar": "Vaata kalendris", + "select_calendar": "Vali kalender", + "already_in_calendar": "Juba sinu kalendris", + "request_info": "Sind on sellele sündmusele kutsutud. Vasta, et anda korraldajale teada oma saadavusest.", + "cancel_info": "Korraldaja on selle sündmuse tühistanud.", + "event_updated": "Uuendus #{sequence}", + "event_status_tentative": "Esialgne", + "event_status_cancelled": "Tühistatud", + "expand": "Näita üksikasju", + "collapse": "Peida üksikasjad" + }, + "send": "Saada", + "more": "veel", + "scheduled_banner": "Ajastatud saatmiseks kell {date}", + "scheduled_send_created": "Kiri ajastatud saatmiseks", + "sending_in": "Saatmine {seconds, plural, one {# sekundi} other {# sekundi}} pärast", + "undo_send_failed": "Tagasivõtmine ei õnnestunud. Kiri võib juba olla saadetud.", + "cancel_scheduled_send": "Tühista saatmine", + "reschedule_send": "Ajasta uuesti", + "cancel_and_edit": "Tühista ja muuda", + "cancel_and_compose_again": "Tühista ja koosta uuesti", + "reschedule_prompt": "Sisesta uus kuupäev/kellaaeg, nt 2026-05-04T15:30", + "scheduled_actions_only": "Ajastatud vaade toetab ainult ajastatud toiminguid", + "undo_send_scheduled": "Kiri ajastatud saatmiseks", + "undo_send": "Võta saatmine tagasi", + "contact_sidebar": { + "title": "Kontakt", + "close": "Sulge külgriba", + "action_email": "E-post", + "action_email_title": "Saada e-kiri", + "action_copy": "Kopeeri", + "action_copy_title": "Kopeeri e-posti aadress", + "action_edit_title": "Muuda kontakti", + "section_emails": "E-posti aadressid", + "section_phones": "Telefonid", + "section_organizations": "Organisatsioonid", + "section_addresses": "Aadressid", + "section_notes": "Märkused", + "not_in_contacts": "Ei ole sinu kontaktides", + "add_to_contacts": "Lisa kontaktidesse", + "copied": "Kopeeritud!", + "copy_failed": "Kopeerimine ebaõnnestus", + "view_contact": "Kuva kontakt", + "view_details": "Kuva üksikasjad" + }, + "send_now": "Saada kohe", + "verification_code": { + "copy": "Kopeeri kood {code}", + "copied": "Kood kopeeritud", + "copy_failed": "Koodi kopeerimine ebaõnnestus" + }, + "mv_position": "{index} / {total}", + "plugin_panel_show": "Näita pistikprogrammi paneeli", + "plugin_panel_hide": "Peida pistikprogrammi paneel" + }, + "email_composer": { + "select_from_directory": "Vali kataloogist", + "minimize": "Minimeeri", + "maximize": "Täisekraan", + "restore": "Taasta", + "new_message": "Uus kiri", + "signature_edit_hint": "Allkirja muutmiseks tee topeltklõps", + "reply": "Vasta", + "reply_all": "Vasta kõigile", + "forward": "Saada edasi", + "reply_to": "Vasta", + "reply_all_to": "Vasta kõigile", + "forward_message": "Saada edasi", + "from": "Saatja", + "to": "Saajale", + "cc": "CC", + "bcc": "BCC", + "subject": "Teema", + "body_placeholder": "Kirjuta oma sõnum...", + "formatting": "Vormindus", + "send": "Saada", + "cancel": "Tühista", + "attach": "Lisa manus", + "attach_photos": "Fotod ja videod", + "attach_files": "Failid", + "attach_from_files": "Lisa failidest", + "files_picker": { + "attach": "{count, plural, =0 {Lisa manus} one {Lisa 1 fail} other {Lisa # faili}}", + "empty_folder": "See kaust on tühi", + "load_failed": "Sinu faile ei õnnestunud laadida" + }, + "discard": "Kustuta", + "discard_draft_title": "Kustutada mustand?", + "discard_draft_confirm": "Sul on salvestamata muudatusi. Kas soovid selle mustandi kustutada?", + "discard_draft_message": "Mustand kustutatakse.", + "saving": "Salvestamine...", + "sending": "Saatmine...", + "add_link": "Lisa link", + "link_url_prompt": "Sisesta URL", + "draft_saved": "Mustand salvestatud", + "save_failed": "Salvestamine ebaõnnestus", + "to_placeholder": "Saaja e-posti aadressid", + "cc_placeholder": "CC saajad", + "bcc_placeholder": "BCC saajad", + "subject_placeholder": "Teema", + "cc_label": "CC:", + "bcc_label": "BCC:", + "subject_label": "Teema:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Vs:" + }, + "forward_batch_count": "{count, plural, one {1 kiri} other {# kirja}}", + "no_subject": "(Teemata)", + "unknown_sender": "Tundmatu", + "quote": { + "reply_header": "{date} kirjutas {sender}:", + "forward_header": "---------- Edastatud sõnum ----------", + "from": "Saatja: {sender}", + "date": "Kuupäev: {date}", + "subject": "Teema: {subject}", + "to": "Saajad: {recipients}" + }, + "remove_sub_address": "Eemalda alamaadress", + "from_override": { + "toggle_off": "Muuda saatjat", + "toggle_on": "Tühista saatja muutmine", + "toggle_tooltip": "Muuda saatja nime ja aadressi vabalt. Kiri saadetakse endiselt sinu identiteedi kaudu - muutub ainult nähtav saatja päis.", + "name_label": "Saatja nimi", + "name_placeholder": "Nimi", + "email_label": "Saatja e-posti aadress", + "email_placeholder": "alias@example.com", + "envelope_notice": "Kui sinu server ei võta seda aadressi ümbriku saatjaks vastu, kasutatakse selle asemel {identity} ja adressaadid näevad seda päisest Return-Path." + }, + "save_as_template": "Salvesta mallina", + "attach_from_device": "Laadi üles sellest seadmest", + "templates_menu": "Mallid", + "insert_template": "Lisa mall", + "options": "Valikud", + "option_plain_text": "Lihttekst", + "option_plain_text_hint": "Saada ilma vormindusteta", + "option_read_receipt": "Küsi lugemiskinnitust", + "option_read_receipt_hint": "Palu adressaadil kinnitada, et ta kirja avas", + "option_dsn": "Kohaletoimetamise teade", + "option_dsn_hint": "Saa aruanne, kui server kirja kohale toimetab", + "option_require_tls": "Nõua krüpteeritud kohaletoimetamist", + "option_require_tls_hint": "Toimeta kohale ainult TLS-iga, muidu lükka tagasi", + "option_priority": "Prioriteet", + "option_priority_high": "Kõrge", + "option_priority_normal": "Tavaline", + "option_priority_low": "Madal", + "validation": { + "recipient_required": "Saatmiseks lisa saaja", + "body_required": "Kirjuta sõnum või lisa fail", + "invalid_recipients": "Ei saadetud - kontrolli neid aadresse: {addresses}", + "attachments_uploading": "Manuseid laaditakse veel üles — saadetakse kohe, kui need valmis on", + "attachment_upload_failed": "Ei saadetud — manuse üleslaadimine ebaõnnestus. Eemalda see ja proovi uuesti." + }, + "upload_progress": "Laadimine üles: {uploaded} / {total}", + "upload_cancel": "Tühista üleslaadimine", + "upload_failed": "Faili {filename} üleslaadimine ebaõnnestus", + "attachment_too_large": "„{name}” on suurem, kui server lubab ({max} faili kohta)", + "attachments_total_too_large": "Manused ületaksid serveri piiri {max} kirja kohta", + "drop_files": "Manustamiseks lohista failid siia", + "show_less": "Näita vähem", + "send_failed": "Kirja saatmine ebaõnnestus", + "send_offline": "Paistab, et oled võrgust väljas. Kiri on alles, saada see uuesti, kui oled taas ühenduses.", + "send_timeout": "Server lakkas vastamast. Enne uuesti saatmist kontrolli oma saadetud kirjade kausta — sõnum võis siiski välja minna.", + "send_recipients_rejected": "Ei saadetud - server lükkas kõik adressaadid tagasi.", + "send_plugin_failed": "Ei saadetud - saatmist käsitlev pistikprogramm ebaõnnestus või võttis liiga kaua aega. Kontrolli enne uuesti saatmist kausta Saadetud.", + "send_some_recipients_rejected": "Saadetud, kuid mitte neile adressaatidele - server lükkas nad tagasi.", + "continue_draft": "Jätka mustandiga", + "close_draft_title": "Salvestada või kustutada mustand?", + "close_draft_message": "Sul on salvestamata muudatusi. Kas soovid selle mustandina salvestada või kustutada?", + "smime_sign_on": "S/MIME allkirjastamine lubatud", + "smime_sign_off": "Luba S/MIME allkirjastamine", + "smime_encrypt_on": "S/MIME krüpteerimine lubatud", + "smime_encrypt_off": "Luba S/MIME krüpteerimine", + "smime_encrypt_unavailable": "S/MIME krüpteerimine pole saadaval – puuduvad saaja sertifikaadid", + "smime_unlock_title": "Ava S/MIME võti", + "smime_unlock_message": "Sisesta parool, et avada oma S/MIME allkirjastamise võti.", + "smime_unlock_button": "Ava", + "smime_passphrase_placeholder": "Parool", + "forgot_attachment": { + "title": "Kas unustasid manuse lisada?", + "message": "Sinu sõnumis mainitakse \"{keyword}\", kuid ühtegi faili pole lisatud. Kas saata ikkagi?", + "send_anyway": "Saada ikkagi", + "back": "Tagasi muutmise juurde" + }, + "empty_subject": { + "title": "Saata ilma teemata?", + "message": "Sellel sõnumil pole teemat. Kas saata see ikkagi?", + "dont_ask_again": "Ära küsi uuesti", + "send_anyway": "Saada ikkagi", + "back": "Tagasi muutmise juurde" + }, + "schedule_send": "Ajasta saatmine", + "schedule_send_description": "Vali, millal server peaks selle sõnumi välja saatma.", + "schedule_preset": { + "tomorrow_morning": "Homme hommikul", + "tomorrow_afternoon": "Homme pärastlõunal", + "monday_morning": "Esmaspäeva hommikul" + }, + "schedule_send_required": "Vali kuupäev ja kellaaeg.", + "schedule_send_invalid": "Sisesta kehtiv kuupäev ja kellaaeg.", + "schedule_send_future": "Vali tulevikus olev kuupäev ja kellaaeg.", + "schedule_send_too_late": "See aeg on hilisem kui server lubab.", + "schedule_send_unsupported": "Ajastatud saatmine ei ole selle konto jaoks toetatud.", + "schedule_send_cleanup_warning": "Ajastatud saatmine loodi, kuid mustandi koristamine ebaõnnestus.", + "send_delay_unsupported": "Saatmise viivitus ei ole selle konto jaoks toetatud.", + "send_delay_unsupported_confirm": "See konto ei toeta saatmise viivitust. Kas saata kohe?", + "recipient_edit_email": "Muuda e-posti aadressi", + "recipient_edit_name": "Muuda kuvatavat nime", + "recipient_email_placeholder": "E-posti aadress", + "recipient_name_placeholder": "Kuvatav nimi", + "invalid_recipient": "Pole kehtiv e-posti aadress", + "autocomplete_search_server": "Otsi serverist", + "autocomplete_searching": "Otsimine...", + "mention_recipients": "Adressaadid", + "mention_card_not_recipient": "Pole väljal Saajale ega CC, seega seda kirja ei saa.", + "mention_card_add": "Lisa väljale {field}", + "mention_card_remove": "Eemalda mainimine", + "toolbar": { + "bold": "Rasvane", + "italic": "Kaldkiri", + "underline": "Allajoonitud", + "strikethrough": "Läbijoonitud", + "text_color": "Teksti värv", + "remove_color": "Eemalda värv", + "heading_1": "Pealkiri 1", + "heading_2": "Pealkiri 2", + "bullet_list": "Täpploend", + "ordered_list": "Numberloend", + "quote": "Tsitaat", + "code_block": "Koodiplokk", + "align_left": "Joonda vasakule", + "align_center": "Joonda keskele", + "align_right": "Joonda paremale", + "text_direction": "Teksti suund (RTL/LTR)", + "link": "Link", + "table": "Tabel", + "clear_formatting": "Eemalda vormindus", + "undo": "Võta tagasi", + "redo": "Tee uuesti", + "add_row_above": "Lisa rida ülalt", + "add_row_below": "Lisa rida alt", + "add_column_before": "Lisa veerg enne", + "add_column_after": "Lisa veerg pärast", + "delete_row": "Kustuta rida", + "delete_column": "Kustuta veerg", + "toggle_header_row": "Päiserida sisse/välja", + "delete_table": "Kustuta tabel", + "pick_size": "Vali suurus", + "font_size": "Fondi suurus", + "font_size_default": "Vaikimisi", + "background_color": "Taustavärv", + "remove_background_color": "Eemalda taustavärv" + }, + "send_filing_warning": "Saadetud - kuid saatmisjärgne koristus ebaõnnestus, mustand võib alles jääda.", + "pop_out": "Ava vastus eraldi aknas", + "send_guard_failed": "Ei saadetud - kirju enne saatmist kontrolliv pistikprogramm ebaõnnestus või ei vastanud." + }, + "confirm_dialog": { + "confirm": "Kinnita", + "cancel": "Tühista" + }, + "common": { + "loading": "Laadimine...", + "error": "Viga", + "success": "Õnnestus", + "cancel": "Tühista", + "save": "Salvesta", + "delete": "Kustuta", + "edit": "Muuda", + "close": "Sulge", + "search": "Otsi", + "refresh": "Värskenda", + "settings": "Seaded", + "help": "Abi", + "logout": "Logi välja", + "yes": "Jah", + "no": "Ei", + "unknown": "Tundmatu", + "app_title": "Webmail", + "reconnecting": "Ühendus katkes. Proovime uuesti ühenduda…", + "rate_limited_title": "Serveri autentimine on ajutiselt piiratud.", + "rate_limited_detail": "Bulwark on taustapäringud lukustumise vältimiseks peatanud. Proovime uuesti {seconds}s pärast.", + "rate_limited_action_title": "Päring peatati lukustumise vältimiseks.", + "rate_limited_action_detail": "Bulwark ootab serveri jahtumisaja lõppu, enne kui saadab rohkem autenditud päringuid. Proovi uuesti {seconds}s pärast.", + "pull_to_refresh": "Värskendamiseks tõmba alla", + "release_to_refresh": "Värskendamiseks vabasta", + "refreshing": "Värskendamine…" + }, + "notifications": { + "email_sent": "E-kiri saadeti edukalt", + "email_deleted": "E-kiri kustutati", + "email_archived": "E-kiri arhiveeriti", + "emails_deleted": "{count, plural, one {# kiri kustutatud} other {# kirja kustutatud}}", + "emails_archived": "{count, plural, one {# kiri arhiveeritud} other {# kirja arhiveeritud}}", + "error_archiving": "Kirja arhiveerimine ebaõnnestus", + "error_updating": "Kirja uuendamine ebaõnnestus", + "email_starred": "E-kiri tähistati", + "email_unstarred": "E-kirja tähistus eemaldati", + "email_marked_read": "E-kiri märgiti loetuks", + "email_marked_unread": "E-kiri märgiti lugemata", + "copied_to_clipboard": "Kopeeritud lõikelauale", + "source_copied": "Lähtekood kopeeriti lõikelauale", + "error_sending": "E-kirja saatmine ebaõnnestus", + "error_deleting": "E-kirja kustutamine ebaõnnestus", + "error_loading": "E-kirjade laadimine ebaõnnestus", + "new_email": "Uus e-kiri", + "new_email_from": "Saatja: {sender}", + "click_to_view": "Vaatamiseks klõpsa", + "email_moved": "E-kiri liigutati", + "emails_moved": "{count} e-kirja liigutati", + "moved_to_mailbox": "Liigutatud kausta {mailbox}", + "move_failed": "Liigutamine ebaõnnestus", + "removed_from_folder": "Eemaldatud kaustast {mailbox}", + "remove_from_folder_failed": "Sellest kaustast ei õnnestunud eemaldada", + "emails_copied": "{count} kirja kopeeritud", + "copy_failed": "Kopeerimine ebaõnnestus", + "move_error": "E-kirju ei õnnestunud valitud kausta liigutada", + "email_tagged": "E-kiri märgistati", + "emails_tagged": "{count} e-kirja märgistati", + "tag_failed": "Märgistamine ebaõnnestus", + "identity_created": "Identiteet loodi edukalt", + "identity_updated": "Identiteet uuendati edukalt", + "identity_deleted": "Identiteet kustutati", + "identity_set_primary": "Peamine identiteet uuendati", + "identity_create_failed": "Identiteedi loomine ebaõnnestus: {error}", + "identity_update_failed": "Identiteedi uuendamine ebaõnnestus: {error}", + "identity_delete_failed": "Identiteedi kustutamine ebaõnnestus: {error}", + "identity_unauthorized": "Sul ei ole õigust sellelt e-posti aadressilt saata", + "identity_not_found": "Identiteeti ei leitud", + "vacation_saved": "Puhkusevastaja seaded salvestati", + "vacation_save_failed": "Puhkusevastaja seadete salvestamine ebaõnnestus", + "filters_saved": "Filtrid salvestati edukalt", + "filters_save_failed": "Filtrite salvestamine ebaõnnestus", + "filters_deleted": "Filtrireegel kustutati", + "templates_exported": "Mallid eksporditi edukalt", + "templates_imported": "{count, plural, one {# mall} other {# malli}} imporditi", + "templates_import_errors": "Mõnda malli ei õnnestunud importida", + "templates_import_empty": "Failist ei leitud ühtegi malli", + "export_email_error": "E-kirja eksportimine ebaõnnestus", + "import_email_success": "E-kiri imporditi edukalt", + "import_email_error": "E-kirja importimine ebaõnnestus", + "rule_created": "Reegel loodud", + "rule_merged": "Lisatud reeglisse „{name}”", + "rule_already_covered": "Juba kaetud reegliga „{name}”", + "rule_apply_existing": "{count, plural, one {Rakenda # olemasolevale kirjale} other {Rakenda # olemasolevale kirjale}}", + "rule_applied": "{count, plural, =0 {Ühtegi kirja polnud vaja muuta} one {Reegel rakendati # kirjale} other {Reegel rakendati # kirjale}}", + "rule_apply_failed": "Reeglit ei õnnestunud olemasolevatele kirjadele rakendada", + "rule_edit": "Muuda reeglit", + "rule_undo": "Võta tagasi", + "rule_undone": "Reegli muudatus võeti tagasi", + "rule_undo_failed": "Reegli muudatust ei õnnestunud tagasi võtta", + "rule_undo_conflict": "Sinu filtreid on vahepeal muudetud. Võta muudatus tagasi filtriseadetes.", + "rule_folder_failed": "Kausta loomine ebaõnnestus", + "vacation_filters_save_failed": "Äraolekuteate seaded salvestati, kuid edastus- ja vastuse adressaate ei õnnestunud salvestada" + }, + "date": { + "today": "Täna", + "yesterday": "Eile", + "this_week": "Sel nädalal", + "last_week": "Eelmisel nädalal", + "this_month": "Sel kuul", + "older": "Vanemad", + "just_now": "Just praegu", + "minutes_ago": "{count} minut tagasi", + "minutes_ago_plural": "{count} minutit tagasi", + "hours_ago": "{count} tund tagasi", + "hours_ago_plural": "{count} tundi tagasi", + "days_ago": "{count} päev tagasi", + "days_ago_plural": "{count} päeva tagasi" + }, + "language": { + "title": "Keel", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Vali keel", + "switch_to_english": "Vaheta keeleks English", + "switch_to_french": "Vaheta keeleks Français", + "switch_to_japanese": "Vaheta keeleks 日本語", + "switch_to_spanish": "Vaheta keeleks Español", + "switch_to_italian": "Vaheta keeleks Italiano", + "switch_to_german": "Vaheta keeleks Deutsch", + "switch_to_dutch": "Vaheta keeleks Nederlands", + "switch_to_portuguese": "Vaheta keeleks Português", + "switch_to_russian": "Vaheta keeleks Русский", + "switching": "Keele vahetamine...", + "switch": "Vaheta keelt", + "current": "Praegune keel", + "polish": "Polski", + "switch_to_polish": "Vaheta keeleks Polski", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Vaheta keeleks فارسی", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Vaheta keeleks Монгол" + }, + "settings": { + "title": "Seaded", + "back_to_mail": "Tagasi postkasti", + "save_success": "Seaded salvestatud", + "import_success": "Seaded imporditud", + "import_error": "Seadete importimine ebaõnnestus", + "reset_confirm": "Kas soovid kindlasti lähtestada kõik seaded vaikeväärtustele?", + "unsaved_changes": "Sul on salvestamata muudatusi", + "discard_changes": "Kas loobuda salvestamata muudatustest?", + "discard": "Loobu", + "keep_editing": "Jätka muutmist", + "search_placeholder": "Otsi seadetest", + "search_clear": "Tühjenda otsing", + "search_no_results": "Vastavaid seadeid ei leitud", + "tabs": { + "appearance": "Välimus", + "language": "Keel, piirkond ja kellaaeg", + "email": "E-kirjade käitumine", + "composer": "Kirja koostamine", + "privacy": "Privaatsus ja turvalisus", + "account": "Konto", + "identities": "Identiteedid", + "vacation": "Puhkusevastaja", + "advanced": "Täpsemalt", + "calendar": "Kalender", + "filters": "Filtrid", + "templates": "Mallid", + "folders": "Kaustad", + "keywords": "Sildid", + "security": "Turvalisus", + "files": "Failid", + "contacts": "Kontaktid", + "encryption": "Krüpteerimine", + "protocol_handlers": "Vaikerakendused", + "sidebar_apps": "Külgriba rakendused", + "notifications": "Teavitused", + "layout": "Paigutus", + "themes": "Teemad", + "reading": "Lugemine", + "composing": "Kirjutamine", + "downloads": "Allalaadimised", + "content_senders": "Sisu ja saatjad", + "about_data": "Teave ja andmed", + "debug": "Silumine" + }, + "themes": { + "title": "Teemad", + "description": "Vali administraatori paigaldatud teemade ja sisseehitatud eelseadistuste seast.", + "locked": "Teema valiku on administraator lukustanud.", + "forced": "Teema „{name}” on administraatori määratud ja seda ei saa muuta", + "admin_theme": "Administraatori teema", + "activated": "Teema aktiveeritud", + "default_restored": "Vaiketeema taastatud", + "default_name": "Vaikimisi", + "default_badge": "Vaikimisi", + "admin_enforced": "Administraatori määratud" + }, + "tab_groups": { + "general": "Üldine", + "account": "Konto ja identiteet", + "organization": "Kirjade korraldamine", + "apps": "Rakendused", + "system": "Süsteem", + "appearance": "Välimus", + "mail": "Post", + "privacy": "Privaatsus ja turvalisus", + "advanced": "Täpsemalt" + }, + "appearance": { + "message_list_order": { + "label": "Kirjade loendi järjestus", + "description": "Kuidas kirju loendis järjestatakse. Järjestamise teeb server, seega hõlmab see kogu kausta, mitte ainult laaditud kirju.", + "preset": { + "chronological": "Kronoloogiline (uuemad enne)", + "unread_first": "Lugemata enne", + "starred_first": "Tärniga enne", + "tagged_first": "Siltidega enne", + "custom": "Kohandatud" + }, + "tag_label": "Silt", + "advanced_show": "Täpsemalt: muuda järjestustasemeid", + "advanced_hide": "Peida täpsem vaade", + "criterion_label": "{n}. taseme kriteerium", + "direction_label": "{n}. taseme suund", + "criteria": { + "unread": "Lugemata", + "starred": "Tärniga", + "tag": "Silt", + "receivedAt": "Saabumiskuupäev", + "sentAt": "Saatmiskuupäev", + "from": "Saatja", + "subject": "Teema", + "size": "Suurus" + }, + "direction": { + "unread_first": "Lugemata enne", + "read_first": "Loetud enne", + "starred_first": "Tärniga enne", + "unstarred_first": "Tärnita enne", + "tagged_first": "Siltidega enne", + "untagged_first": "Siltideta enne", + "newest_first": "Uuemad enne", + "oldest_first": "Vanemad enne", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Suuremad enne", + "smallest_first": "Väiksemad enne" + }, + "add_level": "Lisa tase", + "remove_level": "Eemalda tase", + "move_up": "Liiguta üles", + "move_down": "Liiguta alla", + "tiebreak_note": "Kuni kolm taset; hilisemad tasemed lahendavad viigid. Uuemad enne rakendatakse alati viimasena.", + "scope": { + "label": "Rakenda kõigile kaustadele", + "description": "Kui väljas, kehtib järjestus ainult Saabunud kirjade kaustale; teised kaustad jäävad kronoloogiliseks." + }, + "unsupported_note": "Sinu meiliserver ei toeta järjestamist loetud oleku, tärnide ega siltide järgi. Need tasemed jäetakse arvestamata ja loend jääb kronoloogiliseks." + }, + "title": "Välimus", + "description": "Kohanda oma veebiposti välimust ja tunnetust", + "theme": { + "label": "Teema", + "description": "Vali eelistatud värviskeem", + "light": "Hele", + "dark": "Tume", + "system": "Süsteem" + }, + "language": { + "label": "Keel", + "description": "Vali eelistatud keel" + }, + "font_size": { + "label": "Kirja suurus", + "description": "Kohanda teksti suurust parema loetavuse jaoks", + "small": "Väike", + "medium": "Keskmine", + "large": "Suur" + }, + "list_density": { + "label": "Tihedus", + "description": "Reguleeri vahesid ja polsterdust kogu kasutajaliideses", + "extra_compact": "Eriti kompaktne", + "compact": "Kompaktne", + "regular": "Tavaline", + "comfortable": "Mugav" + }, + "interface_layout": { + "label": "Liidese paigutus", + "description": "Kus asuvad otsing, navigeerimine ja tööriistaribad", + "default": "Vaikimisi", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Luba animatsioonid", + "description": "Kuva sujuvaid üleminekuid ja efekte" + }, + "toolbar_position": { + "label": "Tööriistariba asukoht", + "description": "Kuhu kuvada kirjatoimingute nupud (Vasta, Arhiveeri, Kustuta jne)", + "top": "Üleval", + "below_subject": "Teema all" + }, + "toolbar_labels": { + "label": "Näita tööriistariba nimetusi", + "description": "Kuva tekstisildid tööriistariba ikoonide kõrval. Lülita välja ruumi säästmiseks, kui ikoonid on juba tuttavad." + }, + "hide_account_switcher": { + "label": "Peida külgriba kontovahetaja", + "description": "Peida kontovaliku menüü kaustade külgriba ülaosast. Kontosid saab endiselt vahetada alumiselt navigeerimisribalt." + }, + "show_rail_account_list": { + "label": "Näita kontode avatare navigeerimisribal", + "description": "Kuva navigeerimisriba allosas eraldi kontoringid kiireks vahetamiseks ning nende all väljalogimise nupp." + }, + "unified_mailbox": { + "label": "Ühendatud postkast", + "description": "Kuva aktiivse konto ja selle jagatud kaustade ühendatud kaustad (Saabunud, Saadetud jne).", + "cross_account": { + "label": "Kõigi kontode ulatuses", + "description": "Ühenda ühendatud postkast kõigi lisatud kontode ulatuses, mitte ainult aktiivse konto piires." + }, + "include_group": { + "label": "Kaasa rühma postkastid", + "description": "Kaasa ühendatud vaatesse ka jagatud/rühma postkastid." + } + }, + "all_mail": { + "folders_label": "Kaustad ühendatud loendites", + "folders_description": "Vali, millised selle konto kaustad kaasatakse loenditesse Kõik kirjad / Lugemata / Tärniga.", + "account_hint": "Kehtib konto {account} kohta.", + "no_folders": "Kaustad puuduvad." + }, + "colorful_sidebar_icons": { + "label": "Värvilised külgriba ikoonid", + "description": "Värvi kausta- ja sildiikoonid tüübi järgi (sinine Saabunud, punane Rämpspost, roheline Saadetud jne). Lülita välja ühevärvilise külgriba jaoks." + }, + "tint_list_rows": { + "label": "Värvi loendirida sildi värviga", + "description": "Tooni iga kirja rida selle esimese sildi värviga. Lülita välja, et read jääksid tavalised; sildipunktid ja -märgised näitavad värvi ikkagi." + }, + "show_account_dot": { + "label": "Näita konto täppi", + "description": "Ühendatud vaates näidatakse saatja kõrval konto värvi täppi, et oleks näha, millisest kontost kiri pärineb. Ei mõju, kui read on konto värviga toonitud." + }, + "tint_list_rows_by_account": { + "label": "Toonita nimekirja read konto värviga", + "description": "Ühendatud vaates varjuta iga rida konto värviga konto punkti näitamise asemel. Sellel on eelis sildi värvi ees; sildid säilitavad oma värvi oma märgistel." + }, + "show_folder_total_count": { + "label": "Näita kirjade koguarvu", + "description": "Näita kaustade ja siltide kõrval kirjade koguarvu koos lugemata arvuga. Lülita välja, et näidata ainult lugemata arvu." + }, + "favicon_unread_badge": { + "label": "Lugemata arv vahekaardi ja rakenduse ikoonil", + "description": "Näita saabunud kirjade lugemata arvu märgisena brauseri vahekaardi ikoonil ja toetuse korral ka paigaldatud rakenduse ikoonil, et uued kirjad oleksid näha ka siis, kui vahekaart pole fookuses. Lülita välja, et mõlemad ikoonid jääksid märgiseta." + }, + "show_shared_mail_accounts": { + "label": "Näita jagatud meilikontosid", + "description": "Näita sinuga jagatud postkastide kaustu külgribal jaotises Jagatud. Lülita välja, et peita kõik, või peida allpool üksikud kontod. Nende kalendrid ja kontaktid jäävad alles." + }, + "pro_interface": { + "label": "Pro liides (eksperimentaalne)", + "description": "Ainult lauaarvutile mõeldud kogenud kasutaja paigutus mitme vahekaardiga kirjade sirvimiseks ja kontodeüleseks töövooks. Standardliidest see ei mõjuta ning saad igal ajal tagasi lülituda.", + "open_label": "Ava Pro liides", + "back_to_standard": "Tagasi standardliidesesse" + }, + "cross_unread": { + "label": "Lugemata", + "description": "Kuva ühendatud postkastis kirje Lugemata, mis loetleb valitud kaustade lugemata kirjad." + }, + "cross_starred": { + "label": "Tärniga", + "description": "Kuva ühendatud postkastis kirje Tärniga, mis loetleb valitud kaustade märgistatud/tärniga kirjad." + }, + "cross_all": { + "label": "Kõik kirjad", + "description": "Kuva ühendatud postkastis kirje Kõik kirjad, mis loetleb kõik valitud kaustade kirjad." + } + }, + "keywords": { + "title": "E-kirjade sildid", + "description": "Määra sildid, et oma e-kirju korrastada. Need salvestatakse serveris JMAP võtmesõnadena.", + "add_keyword": "Lisa silt", + "label_field": "Kuvatav nimi", + "label_placeholder": "nt Töö, Isiklik, Kiireloomuline", + "id_field": "Sildi ID", + "id_placeholder": "nt work, personal", + "color_field": "Värv", + "id_exists": "See sildi ID on juba kasutusel", + "edit": "Muuda silti", + "delete": "Kustuta silt", + "save": "Salvesta", + "add": "Lisa", + "cancel": "Tühista", + "migrating": "Sildi uuendamine olemasolevatel kirjadel…", + "migration_error": "Sildi uuendamine olemasolevatel kirjadel ebaõnnestus", + "filters_migration_error": "Mõned filtrireeglid kasutavad endiselt vana silti: uuenda need käsitsi", + "nesting": { + "label": "Pesastatud sildid", + "description": "Pesasta silte teiste siltide alla ja kuva neid külgribal puuna." + }, + "parent_field": "Ülemsilt", + "no_parent": "Ülemsilt puudub", + "too_long": "See sildi tee on liiga pikk (kuni {max} tähemärki)", + "has_children_locked": "Selle sildi all on pesastatud teisi silte, seega on selle nimi ja ülemsilt lukustatud. Teisalda või eemalda need kõigepealt.", + "has_children_delete": "Eemalda kõigepealt selle sildi alla pesastatud sildid", + "visibility_field": "Nähtavus külgribal", + "visibility": { + "show": "Näita", + "unread": "Näita, kui lugemata", + "hide": "Peida" + }, + "unrecognized": { + "title": "Taasta sildid serverist", + "description": "Siltide nimed ja värvid salvestatakse selles brauseris, kuid sildid ise elavad su kirjadel JMAP võtmesõnadena. Skanni oma posti võtmesõnade suhtes, mida ükski siinne silt ei seleta, ja lisa need tagasi.", + "scan": "Skanni silte", + "rescan": "Skanni uuesti", + "scanning": "Skannimine… {scanned} / {total} kirja", + "none": "Kõik su kirjade sildid on siin juba määratud.", + "messages": "{count, plural, one {# kiri} other {# kirja}}", + "add_all": "Lisa kõik {count}", + "partial": "Skannitud on ainult {scanned} kõige uuemat {total} kirjast. Ainult vanematel kirjadel kasutatud sildid võivad puududa.", + "error": "Kirjade siltide skannimine ebaõnnestus." + } + }, + "notifications": { + "test_sound": "Testi teavitusheli", + "sounds": { + "default": "Vaikimisi (piiks)", + "cheerful": "Rõõmus", + "involved": "Elav", + "swift": "Kiire liigutus", + "relax": "Rahulik" + }, + "push": { + "title": "Taustateavitused", + "description": "Saa süsteemseid teavitusi uutest kirjadest ka siis, kui see sait on suletud. Edastatakse Bulwarki push-releele; relee ei näe kirjade sisu kunagi.", + "relay_label": "Push-relee", + "relay_desc": "Server, mis edastab su teavitusi. Vali administraatori pakutavate releede hulgast.", + "relay_locked": "Määratud administraatori poolt", + "relay_locked_desc": "Push-relee on määranud administraator ja seda ei saa muuta.", + "status_active": "Aktiivne selles seadmes", + "status_inactive": "Selles seadmes pole lubatud", + "status_unsupported": "See brauser ei toeta Web Pushi", + "status_lite": "Veebi-tõukemärguanded pole Bulwark Lite'is saadaval; need vajavad Bulwarki serverit.", + "status_busy": "Töötleb…", + "enable": "Luba", + "reenable": "Registreeri uuesti", + "disable": "Keela", + "confirm_disable_title": "Kas keelata taustateavitused?", + "confirm_disable_message": "See seade ei saa enam teavitusi, kui sait on suletud.", + "dismiss_aria": "Sulge teavituse viip", + "ios_hint": "iOS-is paigalda sait esmalt avakuvale - Safari edastab Web Pushi ainult paigaldatud PWA-dele.", + "devices_title": "Registreeritud seadmed", + "devices_desc": "Kõik seadmed, mis on registreeritud selle konto taustateavitusi saama. Tühistamine peatab seadme teavitused koheselt.", + "devices_loading": "Registreeritud seadmete laadimine…", + "devices_empty": "Ühtegi seadet pole taustateavituste jaoks registreeritud.", + "devices_error": "Registreeritud seadmete laadimine ebaõnnestus.", + "devices_refresh": "Värskenda", + "device_this": "See seade", + "device_other": "Muu seade", + "device_status_active": "Edastab teavitusi", + "device_status_inactive": "Pole kunagi teavitust edastanud", + "device_status_unknown": "Sellele releele pole tuntud", + "device_expires": "Aegub {date}", + "revoke": "Tühista", + "confirm_revoke_title": "Kas tühistada see seade?", + "confirm_revoke_message": "See lakkab taustateavitusi koheselt saamast. Pushi uuesti lubamine sellel seadmel registreerib selle uuesti.", + "confirm_revoke_message_this": "See seade lakkab taustateavitusi koheselt saamast." + }, + "push_all": { + "title": "Kõik kontod", + "description": "Lülita tõukemärguanded korraga sisse või välja mitmele kontole. Üks brauseri tellimus teenindab neid kõiki.", + "select_all": "Vali iga ühendatud konto", + "state_on": "märguanded sees", + "state_off": "märguanded väljas", + "enable_selected": "Luba valitutele", + "disable_selected": "Keela valitutele", + "result_enabled": "Märguanded lubatud {count} kontole.", + "result_partial": "Lubatud {enabled} kontole; {failed} kontol ei õnnestunud lubada.", + "result_disabled": "Märguanded keelatud {count} kontole." + }, + "sound_selection": { + "title": "Teavitusheli", + "description": "Vali, milline heli teavituste puhul mängitakse", + "choose": "Heli", + "choose_desc": "Vali teavitusheli ja kliki eelvaate kuulamiseks kõlariikoonil" + }, + "email": { + "title": "E-kirja teavitused", + "description": "Seadista saabuvate kirjade teavitused", + "enabled": "E-kirja teavitused", + "enabled_desc": "Kuva teavitus uue kirja saabumisel", + "sound": "Teavitusheli", + "sound_desc": "Mängi helimärguanne uue kirja saabumisel", + "inbox_only": "Ainult saabunud kirjad", + "inbox_only_desc": "Teavita ainult kirjadest, mis jõuavad saabunud kirjade kausta; jäta vahele kirjad, mille filter liigitab mujale" + }, + "calendar": { + "title": "Kalendri teavitused", + "description": "Seadista kalendrisündmuste teavitused", + "enabled": "Sündmuste teavitused", + "enabled_desc": "Kuva märguanded eelseisvate kalendrisündmuste kohta", + "sound": "Teavitusheli", + "sound_desc": "Mängi helimärguanne kalendri meeldetuletuste puhul", + "invitation_parsing": "Tuvasta e-kirjade kutsed", + "invitation_parsing_desc": "Tuvasta kalendrikutsed e-kirjade manustest ja kuva kalenditoimingud" + } + }, + "language_region": { + "title": "Keel, piirkond ja kellaaeg", + "description": "Keel, kuupäevavorming, kellaajavorming ja muud piirkondlikud eelistused", + "language": { + "label": "Keel", + "description": "Vali eelistatud keel", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Kuupäevavorming", + "description": "Kuidas kuupäevi kirjade loendis kuvatakse", + "smart": "Nutikas (piirkonnateadlik)", + "relative": "Suhteline (1 t tagasi, 2 p tagasi)", + "full": "Alati täiskuupäev", + "preview_today": "Täna:", + "preview_this_week": "Sel nädalal:", + "preview_older": "Vanem:" + }, + "date_locale": { + "label": "Kuupäevavormingu piirkond", + "description": "Kuidas numbrilisi kuupäevi järjestatakse (päev, kuu, aasta)", + "auto": "Automaatne (vastavalt keelele)", + "iso": "ISO 8601 (YYYY-MM-DD)", + "dmy": "Päev/Kuu/Aasta", + "mdy": "Kuu/Päev/Aasta" + }, + "time_format": { + "label": "Kellaajavorming", + "description": "Vali 12- või 24-tunnise kella vahel", + "12h": "12-tunnine", + "24h": "24-tunnine" + }, + "first_day": { + "label": "Nädala esimene päev", + "description": "Alusta nädalat pühapäeval või esmaspäeval", + "sunday": "Pühapäev", + "monday": "Esmaspäev" + }, + "time_zone": { + "label": "Ajavöönd", + "description": "Kuva kirjade ja kalendri ajad selles ajavööndis, mitte brauseri teatatud ajavööndis", + "auto": "Automaatne ({zone})", + "preview_now": "Praegu:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Tühjenda otsing kausta vahetamisel", + "description": "Kaustale (Saabunud, Saadetud, …) klõpsamine tühjendab aktiivse otsingu ja avab selle kausta. Kui see on välja lülitatud, jääb otsing kehtima ja käivitub uuesti kaustas, millele lülitud." + }, + "request_read_receipt": { + "label": "Küsi vaikimisi lugemiskinnitust", + "description": "Luba uue kirja koostamisel vaikimisi lugemiskinnituse küsimine." + }, + "read_receipt_response": { + "label": "Vastamine lugemiskinnituse päringutele", + "description": "Mida teha, kui saabuv kiri küsib lugemiskinnitust.", + "ask": "Küsi iga kord", + "always": "Saada alati", + "never": "Ära kunagi saada" + }, + "title": "E-kirjade käitumine", + "description": "Seadista, kuidas e-kirju käsitletakse", + "mark_read": { + "label": "Märgi loetuks", + "description": "Millal märkida avatud e-kirjad loetuks", + "instant": "Koheselt", + "delay_3s": "3 sekundi pärast", + "delay_5s": "5 sekundi pärast", + "never": "Mitte kunagi" + }, + "delete_action": { + "label": "Kustutamistoiming", + "description": "Mis juhtub e-kirja kustutamisel", + "trash": "Liiguta prügikasti", + "trash_and_read": "Liiguta prügikasti ja märgi loetuks", + "permanent": "Kustuta jäädavalt", + "warning": "E-kirjad kustutatakse jäädavalt ja neid ei saa taastada. Seda toimingut ei saa tagasi võtta." + }, + "message_spacing": { + "label": "Kirja vahed", + "description": "Kirja sisu ümbritsev ruum lugemisvaates", + "auto": "Automaatne (soovitatud)", + "always": "Lisa alati ruumi", + "edge": "Servast servani" + }, + "plain_text_font": { + "label": "Lihtteksti kirjatüüp", + "description": "Lihttekstina saadetud kirjade kirjatüüp", + "mono": "Fikseeritud laiusega", + "sans": "Tavaline (sama, mis rakenduses)" + }, + "archive_mode": { + "label": "Arhiveeri kausta", + "description": "Kuidas korraldada e-kirju arhiveerimisel", + "single": "Ühte kausta", + "year": "Kaust aasta kohta", + "month": "Kaust kuu kohta", + "reorganize": "Korrasta olemasolev arhiiv", + "reorganize_success": "{count, plural, =0 {Ühtegi kirja ei olnud vaja korrastada} =1 {1 kiri korrastatud} other {# kirja korrastatud}}", + "reorganize_error": "Arhiivi korrastamine ebaõnnestus" + }, + "permanently_delete_junk": { + "label": "Kustuta rämpspost jäädavalt", + "description": "Kustuta e-kirjad rämpsposti kaustast jäädavalt, selle asemel et liigutada need prügikasti" + }, + "confirm_bulk_delete": { + "label": "Küsi kinnitust enne mitme kirja kustutamist", + "description": "Küsi enne mitme valitud kirja prügikasti teisaldamist. Lõplik kustutamine küsib alati." + }, + "mail_layout": { + "label": "Kirjade paigutus", + "description": "Vali klassikalise jagatud lugemispaani, Gmaili-laadse fokuseeritud lugemisvoo või Zimbra-laadse alumise lugemispaani vahel.", + "split": "Jagatud paan", + "split_description": "Kuva kirjade loend ja lugemispaan kõrvuti.", + "focus": "Fokuseeritud loend", + "focus_description": "Kuva iga kirja kohta üks rida ja ava kiri täislaiuses, hoides kaustade külgriba nähtaval.", + "horizontal": "Lugemispaan all", + "horizontal_description": "Kuva kirjade loend üleval ja ava valitud kiri selle all olevas lugemispaanis." + }, + "list_columns": { + "label": "Näita loendit veergudena", + "description": "Näitab üherealisi ridu tabelina, mille päise all on veerud Saatja, Teema, Kuupäev ja Suurus." + }, + "show_preview": { + "label": "Kuva eelvaate tekst", + "description": "Kuva e-kirja eelvaade loendis", + "focus_description": "Kuva eelvaate tekst fokuseeritud ühe rea kirjaloendi sees" + }, + "show_verification_codes": { + "label": "Näita kinnituskoode", + "description": "Paku sisselogimis- ja kinnituskirjade koodi ühe klõpsuga kopeerimiseks, nimekirjas ühe päeva jooksul ja kirjas endas" + }, + "disable_threading": { + "label": "Keela vestluste rühmitamine", + "description": "Kuva e-kirjad üksikute kirjadena vestluste kaupa rühmitamise asemel" + }, + "plain_text_mode": { + "label": "Ainult lihttekst", + "description": "Keela vormindatud teksti redaktor ja saada kõik kirjad ainult lihttekstina, sealhulgas vastused ja edastused" + }, + "auto_select_reply_identity": { + "label": "Vasta kirja saanud aadressilt", + "description": "Vastamisel saada kiri aadressilt, millele algne kiri saadeti. Sinu seadistatud identiteedid vastavad alati; kas ka teised sinu domeenide aadressid loetakse sobivaks (catch-all), määrab allpool olev „Vastuvõetud aadressi sobitamine“." + }, + "reply_identity_match": { + "label": "Vastuvõetud aadressi sobitamine", + "description": "Millised vastuvõetud aadressid loetakse sinu omaks. „Ainult täpne aadress“ valib ainult ühe sinu seadistatud identiteetidest. „Iga aadress minu domeenidel“ käsitleb ka iga muud sinu identiteedi domeenil olevat aadressi catch-all aliasena ja kirjutab From-päise selle järgi ümber. Vali täpne aadress, kui need aadressid on pigem meililistid, mitte aliased.", + "exact": "Ainult täpne aadress", + "domain": "Iga aadress minu domeenidel" + }, + "signature_position": { + "label": "Allkirja asukoht", + "description": "Kuhu lisada allkiri vastustes ja edastustes. Tsiteeritud teksti kohal mõjub loomuliku vastuse lõpetusena; all hoiab algse kirja katkematuna.", + "above_quote": "Enne tsiteeritud teksti", + "below_quote": "Pärast tsiteeritud teksti" + }, + "signature_separator": { + "label": "Allkirja eraldaja", + "description": "Lisa allkirja ette standardne „-- “ eraldusrida (RFC 3676). Lülita välja, kui eelistad, et kiri sulandub otse allkirjaga." + }, + "sub_address_delimiter": { + "label": "Alamaadressi eraldaja", + "description": "Märk, mis eraldab kasutajanime alamaadressi sildist. Kasuta sama eraldajat, mida kasutab sinu meiliserver (nt user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "Kohandatud…", + "custom_input_label": "Kohandatud eraldusmärk" + }, + "attachment_click_action": { + "label": "Manusel klõpsamise toiming", + "description": "Vali, kas manusel klõpsamine kuvab eelvaate või laadib selle kohe alla", + "preview": "Kuva eelvaade, kui võimalik", + "download": "Laadi kohe alla" + }, + "attachment_position": { + "label": "Manuse asukoht", + "description": "Kuhu kuvada manused kirja päises", + "beside-sender": "Saatja kõrval", + "below-header": "Päise all" + }, + "emails_per_page": { + "10": "10 kirja", + "25": "25 kirja", + "50": "50 kirja", + "100": "100 kirja", + "label": "Kirju lehe kohta", + "description": "Korraga laaditavate kirjade arv" + }, + "always_light_mode": { + "label": "Kuva kirjad alati heledas režiimis", + "description": "Kuva kirja sisu heledas režiimis ka siis, kui rakendus on tumedas režiimis, vältimaks tumeda režiimi teisendusprobleeme" + }, + "external_content": { + "label": "Väline sisu", + "description": "Kuidas käsitleda pilte ja välist sisu", + "ask": "Küsi alati", + "block": "Blokeeri alati", + "allow": "Luba alati" + }, + "trusted_senders": { + "label": "Usaldusväärsed saatjad", + "description": "Halda saatjaid, kelle pildid laaditakse automaatselt", + "count_zero": "Puuduvad", + "count_one": "1 saatja", + "count_other": "{count} saatjat", + "modal_title": "Usaldusväärsed saatjad", + "empty_title": "Usaldusväärseid saatjaid pole veel lisatud", + "empty_description": "Blokeeritud piltidega kirja vaadates klõpsa „Usalda seda saatjat alati“, et lisada see siia.", + "add_manually": "Lisa saatja käsitsi", + "add_button": "Lisa", + "add_placeholder": "Sisesta e-posti aadress", + "search_placeholder": "Otsi saatjaid...", + "no_results": "Otsingule vastavaid saatjaid ei leitud", + "remove": "Eemalda", + "close": "Sulge", + "invalid_email": "Palun sisesta kehtiv e-posti aadress", + "already_added": "See saatja on juba usaldusväärne", + "save_error": "Salvestamine ebaõnnestus - üksikasjade jaoks kontrolli kontaktide silumislogi", + "use_address_book_label": "Sünkrooni aadressiraamatuga", + "use_address_book_description": "Salvesta usaldusväärsed saatjad eraldi „Usaldusväärsed saatjad“ aadressiraamatusse, et need sünkroniseeruksid kõigis su seadmetes" + }, + "hover_actions": { + "label": "Kiirtoimingud hiirega üle liikudes", + "description": "Vali, millised kiirtoimingud kuvatakse, kui viid hiire kirja kohale loendis", + "delete": "Kustuta", + "star": "Lisa/eemalda täht", + "mark_read": "Märgi loetuks/lugemata", + "archive": "Arhiveeri", + "tag": "Silt", + "spam": "Märgi rämpspostiks", + "not_spam": "Pole rämpspost", + "none_selected": "Ühtegi toimingut pole valitud", + "mode_label": "Kuvamisrežiim", + "mode_inline": "Reasisene", + "mode_floating": "Hõljuv", + "corner_label": "Hõljuva asukoht", + "corner_top-left": "Üleval vasakul", + "corner_top-right": "Üleval paremal", + "corner_bottom-left": "All vasakul", + "corner_bottom-right": "All paremal" + }, + "default_mail_program": { + "label": "Vaikimisi meiliprogramm", + "description": "Registreeri {appName} mailto: linkide vaikimisi meiliprogrammiks", + "button": "Määra vaikimisi", + "success": "Brauserilt küsiti kinnitust vaikimisi määramiseks", + "error": "Sinu brauser ei toeta seda funktsiooni" + }, + "attachment_reminder": { + "label": "Manuse meeldetuletus", + "description": "Hoiata enne saatmist, kui kiri mainib manuseid, kuid ühtegi pole lisatud", + "keywords_label": "Käivitavad märksõnad", + "keywords_description": "Sõnad või fraasid, mis käivitavad meeldetuletuse, kui neid kirjas leidub", + "add_placeholder": "Lisa märksõna...", + "add": "Lisa", + "remove": "Eemalda" + }, + "empty_subject_warning": { + "label": "Tühja teemarea hoiatus", + "description": "Küsi kinnitust enne teemarealta kirja saatmist." + }, + "recipient_mentions": { + "label": "Maini adressaate @-ga", + "description": "Kirjas @ trükkimine pakub selle adressaate ja lisab valitu eesnime. Enter või Tab valib esiletõstetud nime." + }, + "mention_adds_recipient": { + "label": "Kontaktid @-mainimistes", + "description": "@ kirjutamisel pakutakse ka sinu kontakte ja kataloogis olevaid inimesi. Kui valid kellegi, kes pole veel adressaat, lisatakse ta kirjale.", + "to": "Lisa väljale Saajale", + "cc": "Lisa väljale CC", + "off": "Paku ainult adressaate" + }, + "hide_inline_image_attachments": { + "label": "Peida reasisesed pildid manuste hulgast", + "description": "Kirja sisusse põimitud pilte ei kuvata eraldi manustena" + }, + "attachment_image_previews": { + "label": "Kuva manustes piltide eelvaated", + "description": "Kuva pildimanused pisipildikaartidena üldiste failiikoonide asemel" + }, + "send_delay": { + "label": "Saatmise tühistamine / saatmise viivitus", + "description": "Viivita tavalisi saatmisi lühikese serveripoolse ajaakna võrra.", + "off": "Väljas", + "seconds": "{seconds} sekundit", + "unsupported": "Praegune konto ei toeta viivitatud saatmist. Seade salvestatakse siiski teiste kontode jaoks." + }, + "return_to_list_after_action": { + "label": "Naase loendisse pärast kustutamist või lugemata märkimist", + "description": "Pärast avatud kirja kustutamist või lugemata märkimist naase kirjade loendisse, selle asemel et avada järgmine kiri." + }, + "swipe_right_action": { + "label": "Paremale pühkimise toiming (mobiil)", + "description": "Puuteseadmetel toiming, mis käivitub, kui pühid kirja loendis paremale." + }, + "swipe_left_action": { + "label": "Vasakule pühkimise toiming (mobiil)", + "description": "Puuteseadmetel toiming, mis käivitub, kui pühid kirja loendis vasakule." + }, + "swipe_actions": { + "none": "Puudub", + "archive": "Arhiveeri", + "delete": "Kustuta", + "mark_read": "Lülita loetud olekut", + "star": "Lülita täht", + "spam": "Märgi rämpspostiks" + }, + "rtl_editing": { + "label": "Paremalt vasakule kirjutamise tugi", + "description": "Lisab koostamise tööriistaribale suunanupu, millega saad määrata lõigud vasakult paremale või paremalt vasakule" + } + }, + "composer": { + "title": "Koostamine", + "description": "Seadista kirja koostamise sätteid", + "autosave": { + "label": "Automaatsalvestuse intervall", + "description": "Kui sageli mustandeid automaatselt salvestatakse", + "30s": "Iga 30 sekundi järel", + "1m": "Iga minuti järel", + "2m": "Iga 2 minuti järel", + "5m": "Iga 5 minuti järel" + }, + "send_confirmation": { + "label": "Saatmise kinnitus", + "description": "Küsi kinnitust enne kirjade saatmist" + }, + "default_reply": { + "label": "Vaikimisi vastamisrežiim", + "description": "Vaikimisi toiming vastamisel klõpsates", + "reply": "Vasta", + "reply_all": "Vasta kõigile" + } + }, + "privacy": { + "title": "Privaatsus ja turvalisus", + "description": "Halda oma privaatsuse ja turvalisuse sätteid", + "external_images": { + "label": "Blokeeri välised pildid", + "description": "Väldi jälgimist väliste piltide kaudu" + }, + "session_timeout": { + "label": "Seansi aegumine", + "description": "Logi automaatselt välja pärast tegevusetust", + "never": "Mitte kunagi", + "30m": "30 minutit", + "1h": "1 tund", + "4h": "4 tundi" + }, + "clear_cache": { + "label": "Tühjenda vahemälu", + "description": "Eemalda vahemällu salvestatud andmed ja ajutised failid", + "button": "Tühjenda vahemälu", + "confirm": "Kas oled kindel, et soovid vahemälu tühjendada?", + "success": "Vahemälu tühjendati edukalt" + } + }, + "account": { + "title": "Konto", + "description": "Vaata oma konto teavet", + "name_label": "Kuvatav nimi", + "username_label": "Kasutajanimi", + "account_type_label": "Konto tüüp", + "auth_method_label": "Autentimine", + "auth_method_oauth": "Ühekordne sisselogimine (OAuth/OIDC)", + "auth_method_basic": "Parool", + "auth_method_token": "Juurdepääsuluba", + "demo_account": "Demokonto", + "email": { + "label": "E-posti aadress", + "value": "{email}" + }, + "server": { + "label": "JMAP server", + "value": "{server}" + }, + "storage": { + "label": "Mahu kasutus", + "used": "{used} / {total} kasutatud", + "percentage": "{percent}% kasutatud" + }, + "last_sync": { + "label": "Viimane sünkroonimine", + "value": "{time}" + }, + "accounts": { + "title": "Sisselogitud kontod", + "description": "Lohista, et muuta kontode järjekorda konto rippmenüüs", + "active": "Praegu aktiivne konto", + "default_badge": "Vaikimisi konto", + "set_default": "Määra vaikimisi kontoks", + "switch_to": "Lülitu sellele kontole", + "move_up": "Liiguta üles", + "move_down": "Liiguta alla", + "drag_handle": "Lohista järjekorra muutmiseks", + "add": "Lisa konto" + }, + "backup": { + "title": "Kontode nimekirja varukoopia", + "description": "Hoia selle brauseri kontode nimekirja serveris, krüpteeritult parooliga, mida tead ainult sina, ja too see tagasi teises seadmes. Postkastide paroole ei salvestata: igasse taastatud kontosse logid ise sisse.", + "description_passwords": "Hoia selle brauseri kontode nimekirja serveris, krüpteeritult parooliga, mida tead ainult sina, ja too see tagasi teises seadmes. Soovi korral saad lisada ka postkastide paroolid, nii et taastatud kontod logivad ise sisse.", + "status_none": "Kontole {account} pole nimekirja salvestatud.", + "status_saved": "Kontole {account} on nimekiri salvestatud.", + "save": "Salvesta nimekiri", + "restore": "Taasta nimekiri", + "delete": "Kustuta salvestatud nimekiri", + "cancel": "Tühista", + "delete_confirm": "Kas kustutada salvestatud kontode nimekiri serverist?", + "password": "Varukoopia parool", + "password_hint": "Vähemalt 10 märki. Seda ei saa taastada: kui unustad, salvesta nimekiri uuesti uue parooliga.", + "restore_hint": "Parool, mille valisid nimekirja salvestamisel.", + "confirm": "Kinnita parool", + "include_passwords": "Lisa postkastide paroolid", + "include_passwords_hint": "Siin parooliga sisse logitud kontod logivad pärast taastamist ise sisse. Igaüks, kes teab varukoopia parooli, pääseb ligi kõigile nimekirja postkastidele.", + "saved": "Nimekiri salvestatud ({count, plural, one {# konto} other {# kontot}}).", + "restored": "Lisatud kontosid: {added}. Juba olemasolevaid ja uuendatud kontosid: {updated}. Sisselogimiseks ava iga uus konto kontode nimekirjast.", + "restored_signed_in": "Lisatud kontosid: {added}. Juba olemasolevaid ja uuendatud kontosid: {updated}.", + "skipped": "{count, plural, one {# kontot} other {# kontot}} ei lisatud: kontode piir on täis.", + "saved_passwords": "Paroolid lisatud {count, plural, one {# kontole} other {# kontole}}.", + "signed_in": "Salvestatud parooliga logiti sisse {count, plural, one {# kontosse} other {# kontosse}}.", + "sign_in_failed": "{count, plural, one {# salvestatud parool lükati tagasi} other {# salvestatud parooli lükati tagasi}}: logi neisse kontodesse kontode nimekirjast sisse.", + "deleted": "Salvestatud nimekiri kustutatud.", + "errors": { + "mismatch": "Paroolid ei kattu.", + "password_length": "Kasuta vähemalt 10 märki.", + "unlock_failed": "Vale parool või nimekiri salvestati teisele kontole.", + "owner_signin_required": "Logi sellesse kontosse uuesti sisse ja proovi siis uuesti.", + "https_required": "See vajab turvalist (HTTPS) ühendust.", + "storage_failed": "Server ei saanud nimekirja lugeda ega salvestada. Proovi hiljem uuesti." + } + }, + "shared_accounts": { + "title": "Minuga jagatud", + "description": "Grupi- ja jagatud kontod, mida saad hallata. Vali üks, et muuta selle filtreid, puhkusevastust, kalendreid ja kontakte.", + "shared_label": "Jagatud konto" + } + }, + "scoped": { + "back": "Tagasi minu konto juurde", + "managing": "Haldan: {name}" + }, + "security": { + "title": "Konto turvalisus", + "description": "Halda oma parooli, kaheastmelist autentimist ja turvasätteid", + "detecting": "Serveri võimekuste tuvastamine...", + "not_available": "Konto turvahaldus ei ole selle meiliserveri jaoks saadaval. Vajalikud õigused võivad olla keelatud. Üksikasjade jaoks vaata dokumentatsiooni.", + "password": { + "title": "Muuda parooli", + "current": "Praegune parool", + "new": "Uus parool", + "confirm": "Kinnita uus parool", + "submit": "Muuda parooli", + "success": "Parool muudeti edukalt", + "error_title": "Parooli muutmine ebaõnnestus", + "error_mismatch": "Uued paroolid ei kattu", + "error_min_length": "Parool peab olema vähemalt 8 tähemärki pikk", + "error_generic": "Parooli muutmine ebaõnnestus" + }, + "display_name": { + "label": "Kuvatav nimi", + "description": "Sinu nimi, nagu see serveris kuvatakse", + "placeholder": "Sisesta oma kuvatav nimi", + "save": "Salvesta", + "success": "Kuvatav nimi uuendatud", + "error": "Kuvatava nime uuendamine ebaõnnestus" + }, + "totp": { + "section_title": "Kaheastmeline autentimine", + "label": "TOTP autentimine", + "description": "Lisa täiendav turvakiht ajapõhise ühekordse parooliga", + "active": "Lubatud", + "inactive": "Keelatud", + "enabled": "Kaheastmeline autentimine lubatud", + "disabled": "Kaheastmeline autentimine keelatud", + "enable_error": "2FA lubamine ebaõnnestus", + "disable_error": "2FA keelamine ebaõnnestus", + "setup_instructions": "Kopeeri see URL oma autentimisrakendusse (Google Authenticator, Authy jne):", + "verification_code": "Kinnituskood", + "confirm": "Kinnita", + "disable": "Keela", + "disable_confirm_prompt": "Sisesta oma parool, et keelata kaheastmeline autentimine.", + "password_required": "Parool on kohustuslik", + "code_required": "Kinnituskood on kohustuslik", + "code_invalid": "Vale kinnituskood. Kontrolli autentimisrakendust ja proovi uuesti." + }, + "app_passwords": { + "title": "Rakenduste paroolid", + "description": "Loo paroolid rakendustele, mis ei toeta kaheastmelist autentimist", + "add": "Lisa", + "create": "Loo", + "cancel": "Tühista", + "done": "Valmis", + "generate": "Genereeri", + "name_label": "Rakenduse nimi", + "name_placeholder": "nt Thunderbird, iPhone Mail", + "expires_label": "Aegub (valikuline)", + "allowed_ips_label": "Lubatud IP-d (valikuline)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Eraldatud koma või tühikuga. Jäta tühjaks, et lubada kõik IP-d.", + "password_label": "Parool (jäta tühjaks automaatseks genereerimiseks)", + "password_placeholder": "Genereeritakse automaatselt, kui tühi", + "copy_now_warning": "Kopeeri see parool kohe - seda ei kuvata enam uuesti.", + "added": "Rakenduse parool loodud", + "removed": "Rakenduse parool eemaldatud", + "add_error": "Rakenduse parooli loomine ebaõnnestus", + "remove_error": "Rakenduse parooli eemaldamine ebaõnnestus", + "none": "Ühtegi rakenduse parooli pole seadistatud" + }, + "api_keys": { + "title": "API võtmed", + "description": "Loo API võtmeid skriptidele ja integratsioonidele, mis suhtlevad serveriga otse", + "name_label": "Võtme nimi", + "name_placeholder": "nt Varunduse skript, CI runner", + "copy_now_warning": "Kopeeri see API võti kohe - seda ei kuvata enam uuesti.", + "added": "API võti loodud", + "removed": "API võti eemaldatud", + "add_error": "API võtme loomine ebaõnnestus", + "remove_error": "API võtme eemaldamine ebaõnnestus", + "none": "Ühtegi API võtit pole seadistatud" + }, + "encryption": { + "section_title": "Salvestatud andmete krüpteerimine", + "label": "E-kirjade krüpteerimine", + "description": "Krüpteeri serveris salvestatud kirjad täiendava privaatsuse tagamiseks", + "active": "{type} krüpteerimine lubatud", + "inactive": "Keelatud", + "enabled": "Salvestatud andmete krüpteerimine lubatud", + "disabled_success": "Salvestatud andmete krüpteerimine keelatud", + "error": "Krüpteerimissätete uuendamine ebaõnnestus", + "algorithm_label": "Krüpteerimisalgoritm", + "encrypt_on_append": "Krüpteeri uued kirjad üleslaadimisel", + "allow_spam_training": "Luba rämpsposti tuvastuse treenimine enne kirjade krüpteerimist", + "enable_encryption": "Luba krüpteerimine", + "enable_tooltip": "Luba krüpteerimine selle võtmega", + "configure_title": "Luba krüpteerimine selle võtmega", + "enable_button": "Luba", + "disable_tooltip": "Keela krüpteerimine", + "enabled_success": "Salvestatud andmete krüpteerimine lubatud" + }, + "public_keys": { + "name_label": "Avaliku võtme nimi", + "name_placeholder": "nt Minu võti", + "key_label": "Avalik võti (S/MIME või PGP ASCII-armored)", + "key_placeholder": "Kleebi oma avalik võti siia", + "none": "Ühtegi avalikku võtit pole seadistatud" + }, + "email_client": { + "title": "E-posti kliendi seadistus", + "description": "Kasuta neid andmeid oma lauaarvuti või mobiili e-posti kliendi seadistamiseks (Thunderbird, Apple Mail, Outlook jne)", + "jmap_username_label": "JMAP kasutajanimi", + "copy": "Kopeeri", + "copied": "Kopeeritud", + "password_instructions": "Kasuta oma e-posti kliendisse sisselogimiseks ülaltoodud JMAP kasutajanime koos rakenduse parooliga. Kui sul veel pole rakenduse parooli, loo see ülaltoodud jaotises." + }, + "link_device": { + "title": "Ühenda mobiilirakendus", + "description": "Logi Bulwark Mail mobiilirakendusse sisse ilma midagi sisestamata. Genereeri siin QR-kood ja skaneeri see rakenduse sisselogimisekraanilt.", + "generate": "Kuva QR-kood", + "regenerate": "Kuva uus kood", + "instructions": "Ava Bulwark Mail rakendus, puuduta sisselogimisekraanil „Skaneeri QR-kood“ ja suuna kaamera siia.", + "expires_in": "See kood aegub {seconds} sekundi pärast. Seda saab kasutada ainult üks kord.", + "expired": "See kood on aegunud.", + "error": "Sidumiskoodi loomine ebaõnnestus. Palun proovi uuesti.", + "password_prompt": "Sisesta kinnituseks oma parool. Sinu telefon saab oma sisselogimise; parooli telefonile ei saadeta.", + "show_password": "Näita parooli", + "hide_password": "Peida parool", + "continue": "Jätka", + "error_wrong_password": "Vale parool. Proovi uuesti.", + "totp_prompt": "Sisesta kood oma autentimisrakendusest.", + "error_password_or_code": "Parool või kood on vale.", + "error_not_signed_in": "Sinu seanss on aegunud. Seadme sidumiseks logi uuesti sisse.", + "error_impersonation": "Seadmeid ei saa siduda, kui vaatad teise kasutaja postkasti.", + "error_too_many_attempts": "Liiga palju katseid. Oota hetk ja proovi uuesti.", + "error_session_secret": "Seadme sidumine pole sellel serveril seadistatud (puudub SESSION_SECRET). Küsi administraatorilt.", + "error_server_unreachable": "Meiliserveriga ei saanud ühendust. Proovi uuesti.", + "error_pairing_unavailable": "Sinu meiliserver ei luba seadet sel viisil siduda. Küsi administraatorilt.", + "qr_alt": "Sisselogimise QR-kood", + "link_label": "Sisselogimise link", + "copy_link": "Kopeeri sisselogimise link", + "copied": "Kopeeritud", + "copy_hint": "Kaamerat pole? Kopeeri link ja kleebi see rakenduses valikuga „Kleebi sisselogimise link”.", + "success_title": "Telefonis sisse logitud", + "success_body": "Võid selle lehe sulgeda. Teise seadme sidumiseks näita uut koodi.", + "link_another": "Seo teine seade", + "use_sso": "Kasuta hoopis ühekordset sisselogimist", + "error_insecure_server": "Mobiilirakendus ühendub meiliserveritega ainult üle https-i ja see server kasutab http-d." + } + }, + "identities": { + "title": "Saatja identiteedid", + "description": "Halda e-posti aadresse, millelt saad kirju saata", + "identities_count": { + "label": "Sinu identiteedid", + "description": "Saatmiseks seadistatud e-posti aadressid", + "count_zero": "Identiteete pole", + "count_one": "1 identiteet", + "count_other": "{count} identiteeti" + }, + "manage": "Halda identiteete", + "sub_addressing": { + "label": "Alamaadressimine", + "description": "Kasuta silte, näiteks user+tag@domain.com, sisenevate kirjade korraldamiseks", + "learn_more": "Loe lähemalt" + } + }, + "vacation": { + "title": "Äraolekuteade", + "description": "Vasta automaatselt sisenevatele kirjadele, kui oled eemal", + "loading": "Äraolekuteate seadete laadimine...", + "not_supported": "Sinu meiliserver ei toeta äraolekuteateid.", + "fetch_error": "Äraolekuteate seadete laadimine ebaõnnestus. Palun proovi uuesti.", + "status": { + "label": "Äraolekuteade", + "description": "Saada automaatvastus inimestele, kes sulle kirjutavad", + "active": "Aktiivne", + "inactive": "Mitteaktiivne" + }, + "date_range": { + "start": "Alguskuupäev", + "start_description": "Jäta tühjaks, kui alguspiirangut pole vaja", + "end": "Lõppkuupäev", + "end_description": "Jäta tühjaks, kui lõpupiirangut pole vaja" + }, + "message": { + "subject_label": "Teema", + "subject_description": "Automaatvastuse teemarida", + "subject_placeholder": "Olen eemal", + "body_label": "Sõnumi sisu", + "body_description": "Lihtteksti kujul sõnumi sisu", + "body_placeholder": "Täname kirja eest. Olen hetkel eemal ja vastan pärast tagasi jõudmist.", + "html_label": "Vormindatud sõnum (HTML)", + "html_description": "Lisa rikkalikult vormindatud versioon linkide ja kujundusega. Saajad, kelle meiliklient seda kuvada ei suuda, näevad ülaltoodud lihtteksti.", + "html_placeholder": "Kirjuta vormindatud äraolekuvastus…" + }, + "preview": { + "title": "Eelvaade", + "show": "Näita eelvaadet", + "hide": "Peida eelvaade" + }, + "save": "Salvesta muudatused", + "saving": "Salvestamine...", + "warnings": { + "end_before_start": "Lõppkuupäev peab olema hilisem kui alguskuupäev", + "invalid_date": "Sisesta kehtiv algus- ja lõppkuupäev koos kellaajaga või jäta väli tühjaks", + "start_in_past": "Alguskuupäev on minevikus", + "empty_body": "Sõnumi sisu on tühi – saajad saavad tühja vastuse", + "subject_too_long": "Teema on serveri jaoks liiga pikk (kuni {max} baiti).", + "body_too_long": "Sõnum on serveri jaoks liiga pikk (kuni {max} baiti, vormindus kaasa arvatud).", + "forward_address": "Sisesta kehtiv aadress, kuhu edastada", + "not_running": "Edastus või vastuse adressaadid on salvestatud, kuid pole praegu aktiivsed. Salvesta, et need uuesti sisse lülitada." + }, + "forward": { + "title": "Edastamine", + "description": "Edastab saabuvad kirjad teisele aadressile, automaatvastusega või ilma, ülaltoodud ajavahemiku jooksul (ilma lõppkuupäevata kuni väljalülitamiseni). Rämpsposti ei edastata.", + "enabled_label": "Edasta kirjad", + "to_label": "Edasta aadressile", + "to_placeholder": "email@example.com", + "keep_label": "Hoia saabunud kirjade kaustas", + "keep_description": "Muidu kiri ainult edastatakse: seda ei hoita selles postkastis ja ühtegi filtrireeglit sellele ei rakendata." + }, + "audience": { + "label": "Vasta", + "description": "Sisesaatjad: {domains}", + "all": "Kõigile saatjatele", + "internal": "Ainult sisesaatjatele", + "external": "Ainult välissaatjatele" + }, + "auto_reply": { + "title": "Automaatvastus" + } + }, + "folders": { + "role_memos": "Märkmed", + "title": "Kaustad", + "description": "Halda oma e-posti kaustu ja määra neile standardrollid", + "folder_list": "Sinu kaustad", + "folder_list_description": "Kaustaikooni kohandamiseks klõpsa sellel", + "standard_roles": "Standardsed kaustarollid", + "standard_roles_description": "Määra, milliseid kaustu kasutatakse postkasti standardrollideks, nagu Sisendkast, Saadetud, Prügikast jne.", + "role_inbox": "Sisendkast", + "role_drafts": "Mustandid", + "role_sent": "Saadetud", + "role_trash": "Prügikast", + "role_junk": "Rämpspost", + "role_archive": "Arhiiv", + "role_scheduled": "Ajastatud", + "role_snoozed": "Edasi lükatud", + "role_none": "Puudub", + "create_folder": "Loo kaust", + "create_subfolder": "Loo alamkaust", + "reorder": "Lohista üles-alla ümberjärjestamiseks, vasakule-paremale ülemkausta muutmiseks", + "reorder_error": "Kaustade ümberjärjestamine ebaõnnestus", + "move_error": "Kausta liigutamine ebaõnnestus", + "select_folder": "Vali {name}", + "select_hint": "Vali. Shift+klõps valib kõik kaustad kuni viimati klõpsatuni.", + "selected_count": "{count, plural, one {# kaust} other {# kausta}} valitud", + "move_to": "Liiguta...", + "move_to_top_level": "Ülemine tase", + "move": "Liiguta", + "clear_selection": "Tühista valik", + "folders_moved": "{count, plural, one {# kaust liigutatud} other {# kausta liigutatud}}", + "move_partial_error": "{count, plural, one {# kausta} other {# kausta}} ei õnnestunud liigutada", + "subfolder_of": "Kausta {name} sees", + "subfolder_name": "Alamkausta nimi", + "new_folder_name": "Kausta nimi", + "rename": "Nimeta ümber", + "change_icon": "Muuda ikooni", + "count_mode": "Kausta {name} loendur", + "count_mode_default": "Loendur: vaikimisi", + "count_mode_unread": "Ainult lugemata", + "count_mode_all": "Lugemata ja kokku", + "count_mode_none": "Loendurit pole", + "delete": "Kustuta", + "confirm_delete": "Kas oled kindel, et soovid kustutada kausta \"{name}\"? Selles kaustas olevad kirjad liigutatakse prügikasti.", + "create": "Loo", + "cancel": "Tühista", + "no_folders": "Kohandatud kaustu pole", + "cannot_delete_role": "Standardrolliga kausta ei saa kustutada. Eemalda enne roll.", + "folder_created": "Kaust loodud", + "folder_renamed": "Kaust nimetatud ümber", + "folder_deleted": "Kaust kustutatud", + "role_updated": "Kausta roll uuendatud", + "error_create": "Kausta loomine ebaõnnestus", + "error_rename": "Kausta ümbernimetamine ebaõnnestus", + "error_delete": "Kausta kustutamine ebaõnnestus", + "error_delete_has_children": "Kausta ei saa kustutada: selles on veel alamkaustu. Kustuta või liiguta need enne.", + "error_delete_has_email": "Kausta ei saa kustutada: selles on veel kirju. Liiguta või kustuta need enne.", + "error_role": "Kausta rolli uuendamine ebaõnnestus" + }, + "advanced": { + "title": "Täpsemalt", + "description": "Täpsemad valikud ja arendaja seaded", + "debug_mode": { + "label": "Silumisrežiim", + "description": "Luba tõrkeotsinguks üksikasjalik logimine" + }, + "debug_categories": { + "description": "Vali, milliseid kategooriaid logida. Lülita välja need, mida ei vaja, et vähendada konsooli müra.", + "jmap": "JMAP klient", + "jmap_description": "Postkasti toimingud, kirjade laadimine ja JMAP protokolli päringud", + "calendar": "Kalender", + "calendar_description": "Kalendrisündmused, import ja ajastussõnumid", + "tasks": "Ülesanded", + "tasks_description": "Kalendriülesannete loomine, laadimine ja uuendamine", + "auth": "Autentimine", + "auth_description": "Sisselogimine, TOTP, tokenite vahetus ja seansihaldus", + "filters": "Filtrid", + "filters_description": "Sieve filtrireeglid ja äraolekuskriptid", + "email": "Kirjade vaatamine", + "email_description": "Kirjade renderdamine, TNEF-i töötlus ja loetuks märkimine", + "push": "Push-teavitused", + "push_description": "Push-teavituste seadistus ja edastamine", + "contacts": "Kontaktid ja aadressiraamatud", + "contacts_description": "Kontaktide sünkroonimine, aadressiraamatu toimingud ja usaldusväärsed saatjad" + }, + "settings_sync": { + "label": "Seadete sünkroonimine", + "description": "Sünkroni oma seaded brauserite ja seadmete vahel" + }, + "sender_favicons": { + "label": "Saatja favicon'id", + "description": "Näita äriliste saatjate profiilipiltidena veebisaitide ikoone", + "refresh_label": "Värskenda saatjate ikoone", + "refresh_description": "Laadi veebisaitide ikoonid uuesti, näiteks kui saatja on oma ikooni muutnud", + "refresh_button": "Värskenda", + "refresh_done": "Saatjate ikoonid laaditakse uuesti" + }, + "show_avatars_in_junk": { + "label": "Näita avatare rämpsposti kaustas", + "description": "Näita rämpsposti kaustas saatjate profiilipilte ja favicon'e. Vaikimisi välja lülitatud, et mitte anda õngitsuskatsetele visuaalset usaldusväärsust." + }, + "keyboard_shortcuts": { + "label": "Klaviatuuri otseteed", + "description": "Vaata saadaolevaid klaviatuuri otseteid", + "button": "Vaata otseteid" + }, + "refresh_cache": { + "label": "Värskenda vahemällu salvestatud andmeid", + "description": "Laadi kontaktid, kalendrid ja kaustad serverist uuesti. Kontod ja seansid jäävad alles — parandab aegunud või vale kuva ilma välja logimata.", + "button": "Värskenda" + }, + "reset_settings": { + "label": "Lähtesta seaded", + "description": "Taasta kõik seaded vaikeväärtustele", + "button": "Lähtesta vaikeväärtustele" + }, + "export_settings": { + "label": "Ekspordi seaded", + "description": "Laadi oma seaded alla JSON-ina", + "button": "Ekspordi" + }, + "import_settings": { + "label": "Impordi seaded", + "description": "Lae seaded üles JSON-failist", + "button": "Impordi" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Lähtekood" + }, + "spam_siege": { + "title": "Rämpsposti piiramine", + "intro": "Klõpsa rämpsposti ja õngitsuse peal enne, kui need sinu postkasti jõuavad. Lase päris kirjad läbi. Kui kaotad kõik kolm kilpi, langeb postkast.", + "kinds": { + "spam": "Rämpspost", + "phishing": "Õngitsus", + "legit": "Päris kiri" + }, + "click_to_block": "Klõpsa blokeerimiseks", + "let_through": "Lase läbi", + "start": "Alusta", + "play_again": "Mängi uuesti", + "score": "Skoor", + "combo": "Seeriabonus", + "shields_left": "{count} / {total} kilpi alles", + "seconds": "{seconds}s", + "best": "Parim: {score}", + "new_best": "Uus rekord", + "held": "Postkast peetud", + "overrun": "Postkast vallutatud", + "stats": { + "caught": "Blokeeritud", + "delivered": "Kohale jõudnud", + "mistakes": "Vead" + } + } + }, + "sidebar_apps": { + "title": "Külgriba rakendused", + "description": "Halda kohandatud rakendusi ja linke oma külgribal", + "keep_loaded": "Hoia rakendused laaditud", + "keep_loaded_description": "Hoia lisatud rakendused vahetamisel taustal töös, et vältida uuesti laadimist", + "managed_title": "Pakub sinu administraator", + "managed_description": "Need rakendused on seadistatud kõigile ja neid ei saa muuta.", + "manage_title": "Kohandatud rakendused", + "manage_description": "Lisa, muuda või eemalda oma külgriba kohandatud rakendusi" + }, + "contacts": { + "title": "Kontaktid", + "description": "Impordi ja ekspordi oma kontaktid", + "group_by_letter_label": "Rühmita esitähe järgi", + "group_by_letter_description": "Näita kontaktide loendis tähestikulisi jaotisepealkirju", + "sort_by_last_name_label": "Sordi perekonnanime järgi", + "sort_by_last_name_description": "Järjesta kontaktide loend perekonnanime järgi, et pereliikmed oleksid koos", + "import_label": "Impordi kontaktid", + "import_description": "Impordi kontaktid vCard (.vcf) failist", + "export_label": "Ekspordi kontaktid", + "export_description": "Ekspordi kõik kontaktid vCard (.vcf) failina", + "manage_title": "Aadressiraamatud", + "manage_description": "Nimeta oma aadressiraamatud ümber", + "no_address_books": "Aadressiraamatuid ei leitud", + "categories_title": "Kategooriad", + "categories_description": "Nimeta kontaktikategooriad ümber", + "no_categories": "Kategooriaid ei leitud" + }, + "downloads": { + "title": "Allalaadimised", + "description": "Kohanda, kuidas allalaaditud kirjad ja manused nimetatakse.", + "reset": "Taasta vaikeväärtus", + "preview": "Eelvaade:", + "email_template": { + "label": "Kirja (.eml) failinimi", + "description": "Mall, mida kasutatakse kirja eksportimisel või failisüsteemi lohistamisel. Laiend .eml lisatakse automaatselt." + }, + "attachment_template": { + "label": "Manuse failinimi", + "description": "Mall, mida kasutatakse manuse allalaadimisel või lohistamisel. Kui jätad välja '{filename}' ja '{ext}', säilib algne laiend." + }, + "bundle_template": { + "label": "Mitme kirja .zip-faili nimi", + "description": "Mall, mida kasutatakse mitme valitud kirja lohistamisel või allalaadimisel ühe .zip-arhiivina. Laiend .zip lisatakse automaatselt." + }, + "spaces": { + "label": "Tühikud", + "description": "Asenda tulemuseks oleva failinime tühikud muu märgiga.", + "keep": "Säilita tühikud", + "underscore": "Asenda märgiga _", + "dash": "Asenda märgiga -" + }, + "lowercase": { + "label": "Väiketähed", + "description": "Muuda kogu failinimi väiketähtedeks." + }, + "strip_diacritics": { + "label": "Eemalda diakriitikud", + "description": "Teisenda diakriitikutega tähed nende ASCII vastavusteks (ä → a, é → e). Kasulik tööriistadele, mis rikuvad Unicode failinimesid." + }, + "collapse_separators": { + "label": "Ahenda korduvad eraldajad", + "description": "Ahenda järjestikused tühikud, alakriipsud või sidekriipsud üheks märgiks." + }, + "after_export": { + "label": "Pärast eksportimist", + "description": "Soovi korral liiguta kiri pärast .eml-ina eksportimist.", + "keep": "Jäta postkasti", + "archive": "Liiguta arhiivi", + "trash": "Liiguta prügikasti" + } + }, + "filters": { + "title": "E-posti filtrid", + "description": "Loo reegleid sisenevate kirjade automaatseks sortimiseks, sildistamiseks ja haldamiseks", + "add_rule": "Lisa reegel", + "no_rules": "Filtrireegleid pole", + "no_rules_description": "Loo reegleid sisenevate kirjade automaatseks korraldamiseks", + "vacation_active": "Äraolekuteade on aktiivne", + "vacation_active_description": "Automaatvastus on sisenevatele sõnumitele lubatud", + "vacation_configure": "Seadista", + "edit_rule": "Muuda reeglit", + "new_rule": "Uus reegel", + "delete_rule": "Kustuta reegel", + "delete_confirm": "Kas oled kindel, et soovid selle reegli kustutada?", + "enable": "Luba", + "disable": "Keela", + "raw_editor": "Sieve lähtekoodi redaktor", + "raw_editor_warning": "Sieve lähtekoodi muutmine võib visuaalse reeglite redigeerimise katki teha. Siin tehtud muudatused tühistavad visuaalse koostaja.", + "validate": "Valideeri", + "validation_success": "Skript on kehtiv", + "validation_error": "Skriptis on vigu", + "save": "Salvesta reeglid", + "saving": "Salvestamine...", + "saved": "Filtrid salvestatud edukalt", + "save_failed": "Filtrite salvestamine ebaõnnestus", + "loading": "Filtrite laadimine...", + "not_supported": "Sinu meiliserver ei toeta e-posti filtreid.", + "rule_name": "Reegli nimi", + "rule_name_placeholder": "nt uudiskirjade sortimine", + "match_all": "Vasta KÕIGILE tingimustele", + "match_any": "Vasta MISTAHES tingimusele", + "conditions": "Tingimused", + "add_condition": "Lisa tingimus", + "actions": "Tegevused", + "add_action": "Lisa tegevus", + "stop_processing": "Peata järgmiste reeglite töötlemine", + "include_spam": "Liiguta ka rämpspostiks märgitud kirjad", + "attachment_type_placeholder": "nt pdf, doc, jpg", + "value_placeholder_multi": "Väärtus (mitu, komadega eraldatud)", + "condition_fields": { + "attachment": "Manus", + "from": "Saatja", + "to": "Saaja", + "cc": "Koopia", + "subject": "Teema", + "header": "Kohandatud päis", + "size": "Suurus", + "body": "Sisu", + "all": "Kõik kirjad" + }, + "comparators": { + "has_any": "on olemas", + "has_type": "tüübiga", + "contains": "sisaldab", + "not_contains": "ei sisalda", + "is": "on täpselt", + "not_is": "ei ole", + "starts_with": "algab", + "ends_with": "lõpeb", + "matches": "vastab mustrile", + "greater_than": "on suurem kui", + "less_than": "on väiksem kui", + "address_is": "on aadress", + "domain_is": "on domeen" + }, + "action_types": { + "move": "Liiguta kausta", + "copy": "Kopeeri kausta", + "forward": "Suuna edasi", + "mark_read": "Märgi loetuks", + "star": "Märgi tärniga", + "add_label": "Lisa silt", + "discard": "Hülga (kustuta vaikselt)", + "reject": "Lükka tagasi sõnumiga", + "keep": "Jäta sisendkasti", + "stop": "Peata töötlemine" + }, + "move_to_folder": "Vali kaust", + "copy_to_folder": "Vali kaust", + "forward_to": "Suuna edasi e-posti aadressile", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Hoia koopia", + "forward_limit": "Edastuspiir ühe kirja kohta sellel serveril: {count}. Lisaedastused jäetakse vahele.", + "forward_unavailable": "See server ei luba edastamist. Filtrireeglite edastustoimingutel pole mõju.", + "reject_message": "Tagasilükkamise sõnum", + "reject_placeholder": "Sinu kiri on tagasi lükatud", + "label_name": "Sildi nimi", + "label_placeholder": "Vali silt", + "header_name": "Päise nimi", + "header_placeholder": "nt X-Mailing-List", + "size_bytes": "Suurus baitides", + "size_placeholder": "nt 1000000", + "system_managed": "Süsteemi hallatav reegel", + "opaque_warning": "See skript on muudetud väljaspool visuaalset koostajat. Saadaval on ainult Sieve lähtekoodi redigeerimine.", + "open_sieve_editor": "Ava Sieve lähtekoodi redaktor", + "fetch_error": "Filtrite laadimine ebaõnnestus", + "expanded_view": "Laiendatud vaade", + "expanded_view_description": "Näita filtrireegleid koos üksikasjalike tingimuste ja tegevuste plokkidega", + "if": "Kui", + "then": "Siis", + "match_all_conditions": "kõik vastavad", + "match_any_condition": "mistahes vastab", + "and": "ja", + "or": "või", + "cancel": "Tühista", + "confirm_delete": "Kustuta", + "rule_list": "Filtrireeglid", + "drag_to_reorder": "Lohista ümberjärjestamiseks", + "match_type": "Vastavuse tüüp", + "reset_to_visual": "Lähtesta visuaalsele koostajale", + "reset_warning": "See hülgab praeguse skripti ja alustab uuesti.", + "confirm_reset": "Lähtesta", + "validation_empty_name": "Reegli nimi on kohustuslik", + "validation_empty_conditions": "Vajalik on vähemalt üks väärtusega tingimus", + "validation_empty_actions": "Vajalik on vähemalt üks tegevus", + "templates_section": "Alusta mallist", + "template_newsletters": "Liiguta uudiskirjad kausta", + "template_receipts": "Arhiveeri kviitungid automaatselt", + "template_important": "Märgi olulised kirjad", + "template_notifications": "Filtreeri teavitused", + "sieve_editor": { + "title": "Sieve skripti redaktor", + "warning": "Sieve lähtekoodi muutmine võib visuaalse reeglite redigeerimise katki teha. Siin tehtud muudatused tühistavad visuaalse koostaja.", + "script_content": "Sieve skript", + "valid": "Skript on kehtiv", + "invalid": "Skriptis on vigu", + "save_warning": "Salvestamine kirjutab kõik visuaalsed reeglid üle. Seda ei saa tagasi võtta. Kinnitamiseks klõpsa uuesti nuppu Salvesta.", + "validating": "Valideerimine...", + "validate": "Valideeri", + "cancel": "Tühista", + "save": "Salvesta", + "confirm_save": "Kinnita salvestamine", + "validation_failed": "Valideerimispäring ebaõnnestus" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# tingimus} other {# tingimust}}", + "actions_count": "{count, plural, one {# tegevus} other {# tegevust}}" + }, + "origin_external": "Väline", + "managed_by_tooltip": "Hallatud {source} poolt. Muuda seda vastavas rakenduses või kasuta Sieve lähtekoodi redaktorit.", + "quick_rule_names": { + "move_sender": "Liiguta saatjalt {sender} kausta {folder}", + "move_domain": "Liiguta domeenilt @{domain} kausta {folder}", + "move_list": "Liiguta list {list} kausta {folder}", + "mark_read": "Märgi loetuks saatjalt {sender}", + "tag": "Sildista kui {tag} saatjalt {sender}", + "block": "Blokeeri {sender}", + "custom": "Kirjad saatjalt {sender}", + "custom_plain": "Uus reegel", + "senders": "{count, plural, one {# saatja} other {# saatjat}}" + }, + "suggestions": "Soovitused", + "suggest_subject": "Teema „{subject}”", + "suggest_to": "Kellele: {address}", + "suggest_cc": "Koopia: {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Rakenda ka selle kausta olemasolevatele kirjadele", + "apply_existing_unsupported": "Olemasolevatele kirjadele saab rakendada ainult reegleid, mis kontrollivad saatjat, adressaate, teemat või päiseid ning mis liigutavad, kopeerivad, märgivad loetuks, tähistavad või sildistavad.", + "period_title": "Ajavahemik", + "period_start": "Alguskuupäev", + "period_end": "Lõppkuupäev", + "validation_period_order": "Lõppkuupäev peab olema pärast alguskuupäeva", + "period_toggle": "Aktiivne ainult ajavahemikus", + "period_hint": "Kehtib kirjadele, mis saabuvad sel ajavahemikul. Jäta väli tühjaks, et see ots jääks lahtiseks.", + "validation_period_empty": "Sisesta ajavahemiku algus- või lõppkuupäev", + "validation_period_invalid": "Sisesta ajavahemiku jaoks kehtiv kuupäev koos kellaajaga või jäta väli tühjaks", + "apply_existing_period": "Ajavahemikuga reegel toimib ainult kirjadele, mis saabuvad selle jooksul, seega ei saa seda olemasolevatele kirjadele rakendada.", + "period_status_scheduled": "ajastatud", + "period_status_active": "aktiivne", + "period_status_expired": "aegunud" + }, + "templates": { + "title": "E-kirjade mallid", + "description": "Loo korduvkasutatavaid e-kirja malle kohatäitja muutujatega", + "add": "Uus mall", + "edit": "Muuda malli", + "name": "Malli nimi", + "name_placeholder": "nt jätkukiri", + "category": "Kategooria", + "category_placeholder": "nt Töö, Isiklik", + "subject": "Teema", + "subject_placeholder": "Kirja teemarida", + "body": "Sisu", + "body_placeholder": "Kirja sisu...", + "recipients_placeholder": "email@example.com", + "identity": "Saada kui", + "default_identity": "Vaikimisi identiteet", + "favorite": "Lemmik", + "cancel": "Tühista", + "create": "Loo", + "update": "Uuenda", + "confirm_delete": "Kustuta", + "no_templates": "Malle pole veel", + "manage": "Halda malle", + "count": "{count, plural, one {# mall} other {# malli}}", + "export_import": "Eksport ja import", + "export_import_description": "Varunda oma mallid või kanna need üle teise seadmesse", + "export": "Ekspordi", + "import": "Impordi", + "validation": { + "empty": "Malli nimi on kohustuslik", + "too_long": "Malli nimi tohib olla kuni 200 tähemärki" + } + }, + "files": { + "display": { + "title": "Kuvamine", + "description": "Seadista, kuidas faile ja kaustu kuvatakse" + }, + "default_view": { + "label": "Vaikevaade", + "description": "Vali ruudustiku- ja loendipaigutuse vahel", + "list": "Loend", + "grid": "Ruudustik" + }, + "default_sort": { + "label": "Vaikimisi sortimine", + "description": "Vali failide vaikimisi sortimisviis", + "name": "Nimi", + "size": "Suurus", + "modified": "Muudetud" + }, + "sort_direction": { + "label": "Sortimissuund", + "description": "Vali kasvav või kahanev järjestus", + "ascending": "Kasvav", + "descending": "Kahanev" + }, + "icons": { + "title": "Ikoonid", + "description": "Seadista failiikoonide välimust" + }, + "show_icons": { + "label": "Näita failiikoone", + "description": "Kuva ikoonid failide ja kaustade kõrval" + }, + "colored_icons": { + "label": "Värvilised ikoonid", + "description": "Kasuta ühevärviliste asemel värvilisi ikoone" + }, + "show_thumbnails": { + "label": "Näita pisipilte", + "description": "Kuva pildifailide puhul ikoonide asemel eelvaated" + }, + "behavior": { + "title": "Käitumine", + "description": "Seadista failihalduri käitumist" + }, + "show_hidden": { + "label": "Näita peidetud faile", + "description": "Kuva failid ja kaustad, mis algavad punktiga" + }, + "preview": { + "label": "Eelvaade" + } + } + }, + "errors": { + "page_error_title": "Midagi läks valesti", + "page_error_description": "Ilmnes ootamatu viga. Proovi uuesti või mine tagasi avalehele.", + "sidebar_error": "Postkaste ei õnnestunud laadida", + "email_list_error": "E-kirju ei õnnestunud laadida", + "viewer_error_title": "E-kirja ei saa kuvada", + "viewer_error_description": "Selle e-kirja kuvamisel tekkis probleem. See võib sisaldada toetamata sisu.", + "composer_error": "Kirjutamisakent ei õnnestunud laadida", + "settings_error_title": "Seaded pole saadaval", + "settings_error_description": "Seadeid ei õnnestunud laadida. Sinu eelistusi ei pruugita salvestada.", + "try_again": "Proovi uuesti", + "reload": "Laadi uuesti", + "reload_emails": "Laadi e-kirjad uuesti", + "reload_settings": "Laadi seaded uuesti", + "retry": "Proovi uuesti", + "go_home": "Mine postkasti" + }, + "context_menu": { + "reply": "Vasta", + "reply_all": "Vasta kõigile", + "forward": "Edasta", + "mark_read": "Märgi loetuks", + "mark_unread": "Märgi lugemata", + "star": "Tähista", + "unstar": "Eemalda tähistus", + "pin": "Kinnita", + "unpin": "Eemalda kinnitus", + "move_to": "Liiguta kausta...", + "copy_to_account": "Kopeeri kontole...", + "remove_from_folder": "Eemalda sellest kaustast", + "archive": "Arhiveeri", + "delete": "Kustuta", + "mark_as_spam": "Märgi rämpspostiks", + "not_spam": "Pole rämpspost", + "tag": "Märgista", + "items_selected": "{count} e-kirja valitud", + "forward_as_attachments": "Edasta manustena", + "edit_draft": "Muuda mustandit", + "cancel_scheduled_send": "Tühista saatmine", + "reschedule_send": "Muuda ajastust", + "cancel_and_edit": "Tühista ja muuda", + "cancel_and_compose_again": "Tühista ja kirjuta uuesti", + "rules": { + "title": "Reeglid", + "move_from": "Liiguta alati saatja {sender} kirjad", + "move_from_domain": "Liiguta alati domeeni @{domain} kirjad", + "move_from_list": "Liiguta alati selle listi kirjad", + "mark_read_from": "Märgi alati loetuks saatjalt {sender}", + "tag_with": "Sildista alati sildiga", + "block": "{count, plural, one {Blokeeri saatja} other {Blokeeri # saatjat}}", + "no_junk": "Sellel kontol pole rämpsposti kausta", + "create": "Loo reegel…", + "manage": "Halda reegleid", + "senders_count": "{count, plural, one {# saatja} other {# saatjat}}", + "cross_account": "Vali kirjad ühelt kontolt", + "opaque_hint": "Sinu filtreid on käsitsi muudetud. Ava filtriseaded", + "loading": "Reeglite laadimine…", + "load_failed": "Reegleid ei õnnestunud laadida", + "none": "Reegleid pole veel", + "new_mail_only": "Ainult uutele kirjadele", + "running": "Käivitan „{name}” kaustas {folder}…" + }, + "find_emails_from": "Leia kirjad saatjalt {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Märgi kaust loetuks", + "mark_folder_tree_read": "Märgi kaust ja alamkaustad loetuks", + "mark_all_folders_read": "Märgi kõik kaustad loetuks", + "new_subfolder": "Uus alamkaust...", + "new_folder": "Uus kaust...", + "rename": "Nimeta ümber...", + "share": "Jaga...", + "import_email": "Impordi .eml või .zip...", + "empty_folder": "Tühjenda kaust", + "empty_folder_generic": "Tühjenda kaust", + "delete_folder": "Kustuta kaust", + "refresh": "Värskenda", + "hide_shared_account": "Peida külgribalt", + "mute_notifications": "Vaigista märguanded", + "unmute_notifications": "Lõpeta märguannete vaigistus", + "mute_account_notifications": "Vaigista selle konto märguanded", + "unmute_account_notifications": "Lõpeta selle konto märguannete vaigistus", + "reset_folder_notifications": "Lähtesta kausta märguandeseaded", + "mark_all_confirm_title": "Märgi kõik kaustad loetuks", + "mark_all_confirm_message": "Kas märkida kõik lugemata sõnumid sinu isiklikus kontos loetuks?", + "delete_confirm_title": "Kustuta kaust", + "delete_confirm_message": "Kas kustutada kaust \"{name}\" jäädavalt? Seda toimingut ei saa tagasi võtta.", + "prompt_new_subfolder": "Sisesta uue alamkausta nimi.", + "prompt_new_folder": "Sisesta uue kausta nimi.", + "prompt_rename": "Sisesta selle kausta uus nimi.", + "placeholder_folder_name": "Kausta nimi", + "create": "Loo", + "rename_confirm": "Nimeta ümber", + "toast_marked_read": "Kaust märgiti loetuks", + "toast_marked_read_count": "{count, plural, one {1 sõnum} other {# sõnumit}} märgiti loetuks", + "toast_already_read": "Lugemata sõnumeid pole", + "toast_marked_all_read": "Kõik kaustad märgiti loetuks", + "toast_emptied": "Kaust tühjendati", + "toast_folder_created": "Kaust loodi", + "toast_folder_renamed": "Kaust nimetati ümber", + "toast_folder_deleted": "Kaust kustutati", + "toast_error_mark_read": "Loetuks märkimine ebaõnnestus", + "toast_error_empty": "Kausta tühjendamine ebaõnnestus", + "toast_error_create": "Kausta loomine ebaõnnestus", + "toast_error_rename": "Kausta ümbernimetamine ebaõnnestus", + "toast_error_delete": "Kausta kustutamine ebaõnnestus", + "toast_error_delete_has_children": "Kaustal on alamkaustad. Eemalda need enne.", + "toast_error_delete_has_email": "Kaust ei ole tühi. Tühjenda see enne.", + "delete_with_contents_title": "Kaust ei ole tühi", + "delete_with_contents_message": "Kaust \"{name}\" sisaldab veel {count, plural, one {1 sõnum} other {# sõnumit}}. Kas kustutada kaust ja kõik selle sõnumid? Seda ei saa tagasi võtta.", + "delete_with_contents_confirm": "Kustuta kaust ja sõnumid" + }, + "shortcuts": { + "title": "Kiirklahvid", + "tip": "Selle abi nägemiseks vajuta igal ajal ?", + "sections": { + "navigation": "Navigeerimine", + "actions": "E-kirja toimingud", + "global": "Üldine", + "threads": "Vestlused", + "composer": "Kirjutamine" + }, + "navigation": { + "next_email": "Järgmine e-kiri", + "previous_email": "Eelmine e-kiri", + "open_email": "Ava e-kiri", + "close_email": "Sulge / tühista valik", + "go_to_folder": "Mine kausta", + "next_unread": "Järgmine lugemata e-kiri", + "no_more_unread": "Allpool loendis pole rohkem lugemata e-kirju" + }, + "actions": { + "reply": "Vasta", + "reply_all": "Vasta kõigile", + "forward": "Edasta", + "star": "Lülita tähistus", + "archive": "Arhiveeri", + "delete": "Kustuta", + "mark_unread": "Märgi lugemata", + "mark_read": "Märgi loetuks", + "toggle_spam": "Märgi rämpspostiks / Pole rämpspost" + }, + "global": { + "compose": "Kirjuta uus e-kiri", + "search": "Fookus otsingusse", + "help": "Näita kiirklahve", + "refresh": "Värskenda e-kirjad", + "select_all": "Vali kõik", + "global_search": "Otsi kõike", + "extend_selection": "Laienda valikut alla / üles" + }, + "threads": { + "expand_collapse": "Ava/sulge vestlus", + "select_mountain_view": "Vali vestlus" + }, + "composer": { + "send": "Saada e-kiri", + "schedule_send": "Ajasta saatmine", + "template_picker": "Ava mallivalija" + } + }, + "threads": { + "messages_one": "{count} sõnum", + "messages_other": "{count} sõnumit", + "messages_tooltip": "{count, plural, one {# sõnum selles vestluses} other {# sõnumit selles vestluses}}", + "expand": "Ava vestlus", + "collapse": "Sulge vestlus", + "loading": "Vestluse laadimine...", + "mark_read": "Märgi vestlus loetuks", + "mark_unread": "Märgi vestlus lugemata", + "archive": "Arhiveeri vestlus", + "delete": "Kustuta vestlus", + "star": "Tähista vestlus", + "unstar": "Eemalda vestluse tähistus", + "toggle_thread": "Lülita vestlus" + }, + "identities": { + "modal_title": "Halda saatja identiteete", + "create_new": "Loo uus identiteet", + "edit_identity": "Muuda identiteeti", + "delete_confirm": "Kustutada see identiteet? Seda ei saa tagasi võtta.", + "cannot_delete": "Seda identiteeti ei saa kustutada", + "primary_identity": "Peamine", + "set_as_primary": "Määra peamiseks", + "no_identities": "Identiteete ei leitud", + "display": { + "reply_to": "Vastamisaadress:", + "bcc": "BCC:", + "signature": "Allkiri:", + "preview": "Eelvaade:" + }, + "validation_errors": { + "invalid_emails": "Vigased e-posti aadressid: {emails}", + "unknown_error": "Tundmatu viga" + }, + "form": { + "name_label": "Kuvatav nimi", + "name_placeholder": "nt Töö e-post, Isiklik", + "name_required": "Nimi on kohustuslik", + "email_label": "E-posti aadress", + "email_placeholder": "your.email@example.com", + "email_required": "E-posti aadress on kohustuslik", + "email_invalid": "Palun sisesta kehtiv e-posti aadress", + "email_immutable": "E-posti aadressi ei saa pärast loomist muuta", + "reply_to_label": "Vastamisaadress (valikuline)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "Automaatne BCC (valikuline)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Tekstiallkiri", + "html_signature_label": "HTML-allkiri", + "signature_byte_counter": "{bytes} / {max} baiti", + "signature_byte_limit_reached": "Serveri piirmäär on täis", + "save": "Salvesta identiteet", + "cancel": "Tühista", + "creating": "Loomine...", + "updating": "Uuendamine..." + }, + "sub_address": { + "button_tooltip": "Kasuta alamaadressi", + "popover_title": "Lisa alamaadressi silt", + "tag_input_placeholder": "Sisesta silt (nt ostlemine)", + "preview_label": "Eelvaade:", + "recent_tags": "Hiljutised sildid", + "suggested_tags": "Soovitatud", + "use_address": "Kasuta seda aadressi", + "invalid_tag": "Silt tohib sisaldada ainult tähti, numbreid ja sidekriipse", + "tag_too_long": "Silt tohib olla kuni 30 tähemärki", + "help_text": "Aadressile user{delimiter}tag@domain.com saadetud kirjad jõuavad sinu postkasti", + "validation": { + "empty": "Silt ei tohi olla tühi", + "too_long": "Silt tohib olla kuni {max} tähemärki", + "invalid_chars": "Silt tohib sisaldada ainult tähti, numbreid ja sidekriipse" + } + }, + "badge": { + "sent_via": "kaudu", + "sub_address_tag": "Saadetud alamaadressiga: {tag}", + "identity_name": "Saadetud identiteediga: {name}", + "identity_short": "{name} kaudu", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Kustuta", + "delete_confirm_title": "Kustuta identiteet" + }, + "templates": { + "picker_title": "Vali mall", + "search_placeholder": "Otsi malle...", + "section_favorites": "Lemmikud", + "section_recent": "Hiljutised", + "section_uncategorized": "Muud", + "no_templates": "Malle veel pole", + "no_results": "Malle ei leitud", + "fill_placeholders": "Täida muutujate väärtused", + "enter_value": "Sisesta väärtus...", + "preview": "Eelvaade", + "insert_with_values": "Lisa väärtustega", + "insert_raw": "Lisa täitmata kujul", + "copy_suffix": "(koopia)", + "placeholder": "Muutuja", + "placeholders": { + "recipient_name": "Saaja nimi", + "company": "Ettevõtte nimi", + "date": "Tänane kuupäev", + "day_of_week": "Nädalapäev", + "sender_name": "Sinu nimi" + } + }, + "contacts": { + "directory": { + "title": "Kataloog", + "search_placeholder": "Otsi nime või e-posti aadressi", + "refresh": "Värskenda kataloogi", + "retry": "Proovi uuesti", + "loading": "Kataloogi laaditakse…", + "unavailable_title": "Kataloog puudub", + "unavailable_hint": "Sinu meiliserver ei paku kontode kataloogi.", + "forbidden_title": "Kataloog pole saadaval", + "forbidden_hint": "Sinu meiliserver ei luba selle kontoga kataloogi sirvida. Küsi oma administraatorilt.", + "error_title": "Kataloogi laadimine ebaõnnestus", + "error_hint": "Kontrolli ühendust ja proovi uuesti.", + "empty_title": "Kataloog on tühi", + "empty_hint": "Sinu meiliserver ei näita ühtegi e-posti aadressiga kontot.", + "no_match": "Kataloogis pole kedagi, kes sinu otsingule vastaks", + "refresh_failed": "Kataloogi värskendamine ebaõnnestus. Näidatakse viimati laaditud nimekirja.", + "select_page": "Vali kõik sellel lehel", + "deselect_page": "Tühista valik sellel lehel", + "select_entry": "Vali {name}", + "range": "{from}–{to} / {total}", + "page": "Leht {page} / {count}", + "previous_page": "Eelmine leht", + "next_page": "Järgmine leht", + "picker_title": "Vali kataloogist", + "cancel": "Tühista", + "selected_count": "{count} valitud", + "add_to": "Lisa saajaks", + "add_cc": "Lisa CC-sse", + "add_bcc": "Lisa BCC-sse", + "select_hint": "Vali keegi, et näha tema andmeid", + "write_email": "Kirjuta kiri", + "add_to_contacts": "Lisa kontaktidesse", + "email": "E-post", + "read_only_note": "Kataloogi kirjed tulevad sinu meiliserverist ja neid ei saa siin muuta.", + "type": { + "individual": "Isik", + "group": "Grupp", + "resource": "Ressurss", + "location": "Asukoht", + "other": "Muu" + } + }, + "title": "Kontaktid", + "search_placeholder": "Otsi kontakte...", + "create_new": "Uus kontakt", + "no_category": "Kategooria puudub", + "rename_category": "Nimeta kategooria ümber", + "category_name_label": "Kategooria nimi", + "category_renamed": "Kategooria nimetati ümber", + "category_rename_failed": "Kategooria ümbernimetamine ebaõnnestus", + "category_added": "Kontakt lisati kategooriasse {name}", + "category_added_plural": "{count} kontakti lisati kategooriasse {name}", + "empty_state": "Kontakte veel pole", + "empty_state_title": "Kontakte veel pole", + "empty_state_subtitle": "Loo oma esimene kontakt või impordi vCard-failist", + "empty_search": "Otsingule ei vastanud ükski kontakt", + "empty_search_hint": "Proovi teistsugust otsisõna", + "empty_filtered": "Filtritele ei vastanud ükski kontakt", + "empty_filtered_hint": "Proovi filtreid muuta või need tühjendada", + "clear_search": "Tühjenda otsing", + "import_vcard": "Impordi vCard", + "delete_confirm_title": "Kustuta kontakt", + "delete_confirm": "Kas oled kindel, et soovid selle kontakti kustutada?", + "local_mode": "Kontaktid salvestatakse kohalikult (server ei toeta JMAP Contacts funktsiooni)", + "back_to_contacts": "Tagasi kontaktide juurde", + "back_to_email": "Tagasi e-posti juurde", + "open_categories": "Ava kategooriad", + "tabs": { + "all": "Kõik", + "groups": "Grupid" + }, + "shared": { + "title": "Jagatud" + }, + "address_books": { + "create": "Uus aadressiraamat", + "created": "Aadressiraamat loodud", + "create_failed": "Aadressiraamatu loomine ebaõnnestus", + "title": "Minu aadressiraamatud", + "shared_prefix": "Jagatud: {name}", + "moved": "Kontakt liigutati aadressiraamatusse {name}", + "moved_plural": "{count} kontakti liigutati aadressiraamatusse {name}", + "move_failed": "Kontakti liigutamine ebaõnnestus", + "address_book": "Aadressiraamat", + "rename": "Nimeta aadressiraamat ümber", + "name_label": "Aadressiraamatu nimi", + "renamed": "Aadressiraamat nimetati ümber", + "rename_failed": "Aadressiraamatu ümbernimetamine ebaõnnestus", + "default": "Vaikimisi", + "set_default": "Määra vaikimisi valikuks", + "default_updated": "Vaikimisi aadressiraamat uuendatud", + "set_default_failed": "Vaikimisi aadressiraamatu määramine ebaõnnestus", + "manage": "Halda aadressiraamatuid", + "share": "Jaga aadressiraamatut", + "new_contact_in_book": "Uus kontakt selles aadressiraamatus", + "delete": "Kustuta aadressiraamat", + "confirm_delete": "Kustutada \"{name}\"? Kõik selle aadressiraamatu kontaktid eemaldatakse.", + "deleted": "Aadressiraamat kustutatud", + "delete_failed": "Aadressiraamatu kustutamine ebaõnnestus" + }, + "detail": { + "emails": "E-posti aadressid", + "phones": "Telefoninumbrid", + "organizations": "Organisatsioonid", + "addresses": "Aadressid", + "notes": "Märkmed", + "titles": "Ametinimetused ja rollid", + "online_services": "Veebiteenused", + "anniversaries": "Tähtpäevad", + "personal_info": "Isiklik info", + "languages": "Keeled", + "categories": "Kategooriad", + "related_contacts": "Seotud kontaktid", + "crypto_keys": "Krüptovõtmed", + "cert_issuer": "Väljastaja", + "cert_expires": "Aegub", + "cert_expired": "Aegunud", + "cert_fingerprint": "Sõrmejälg", + "cert_algorithm": "Algoritm", + "import_to_smime": "Impordi S/MIME-sse", + "cert_already_imported": "Juba imporditud S/MIME-sse", + "cert_imported": "Sertifikaat imporditud S/MIME hoidlasse", + "cert_import_failed": "Sertifikaadi importimine ebaõnnestus", + "no_contact_selected": "Vali kontakt, et vaadata üksikasju", + "compose_email": "Koosta kiri", + "copy_email": "Kopeeri e-posti aadress", + "copy_phone": "Kopeeri telefoninumber", + "copy_url": "Kopeeri URL", + "copied": "Kopeeritud lõikelauale", + "copy_failed": "Lõikelauale kopeerimine ebaõnnestus", + "created": "Loodud", + "updated": "Viimati uuendatud", + "timezone": "Ajavöönd", + "anniversary_birth": "Sünnipäev", + "anniversary_death": "Surmapäev", + "anniversary_wedding": "Aastapäev", + "anniversary_other": "Muu", + "personal_expertise": "Kompetents", + "personal_hobby": "Hobi", + "personal_interest": "Huvi", + "personal_other": "Muu", + "gender": "Sugu", + "gender_masculine": "Mees", + "gender_feminine": "Naine", + "gender_other": "Muu", + "gender_none": "Ei kohaldu", + "gender_unknown": "Teadmata", + "calendar": "Kalender", + "calendar_uri": "Kalendri URL", + "scheduling_uri": "Planeerimise URL", + "freebusy_uri": "Vaba/hõivatud URL", + "section_contact": "Kontaktandmed", + "section_work": "Töö", + "section_personal": "Isiklik", + "email_default_label": "E-post", + "phone_default_label": "Telefon", + "address_default_label": "Aadress", + "online_service_default_label": "Veebis", + "organization_label": "Organisatsioon", + "title_label": "Ametinimetus", + "role_label": "Roll", + "language_label": "Keel", + "related_default_label": "Seotud", + "more_actions": "Rohkem toiminguid", + "age_years": "{count, plural, one {1-aastane} other {#-aastane}}", + "years_since": "{count, plural, one {1 aasta} other {# aastat}}" + }, + "activity": { + "recent_emails": "Hiljutised kirjad", + "upcoming_events": "Eelseisvad sündmused", + "no_emails": "Hiljutisi kirju pole", + "no_events": "Eelseisvaid sündmusi pole", + "no_subject": "(Teema puudub)", + "no_title": "(Pealkiri puudub)", + "load_failed": "Laadimine ebaõnnestus", + "unknown_sender": "Tundmatu saatja", + "all_day": "Kogu päev" + }, + "form": { + "create_title": "Uus kontakt", + "edit_title": "Muuda kontakti", + "section_address_book": "Kataloog", + "select_address_book": "Vali kataloog...", + "section_identity": "Nimi ja identiteet", + "contact_type": "Kontakti tüüp", + "type_person": "Isik", + "type_organization": "Organisatsioon", + "section_work": "Töö ja organisatsioon", + "prefix": "Tiitel", + "prefix_placeholder": "dr., hr., pr.", + "given_name": "Eesnimi", + "middle_name": "Keskmine nimi", + "surname": "Perekonnanimi", + "suffix": "Liide", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Hüüdnimi", + "nickname_placeholder": "Hüüdnimi", + "email": "E-post", + "email_placeholder": "email@example.com", + "phone": "Telefon", + "phone_placeholder": "+1 234 567 890", + "phone_type": "Tüüp", + "phone_voice": "Kõne", + "phone_cell": "Mobiil", + "phone_fax": "Faks", + "phone_pager": "Pager", + "phone_video": "Video", + "phone_text": "Tekstisõnum", + "organization": "Organisatsioon", + "organization_placeholder": "Ettevõtte nimi", + "department": "Osakond", + "department_placeholder": "Osakond", + "job_title": "Ametinimetus", + "job_title_placeholder": "nt tarkvarainsener", + "role": "Roll", + "role_placeholder": "nt meeskonnajuht", + "addresses": "Aadressid", + "add_address": "Lisa aadress", + "street": "Tänav", + "city": "Linn", + "region": "Maakond / piirkond", + "postcode": "Sihtnumber", + "country": "Riik", + "online_services": "Veebiteenused", + "add_online_service": "Lisa veebiteenus", + "url_placeholder": "https://...", + "service_placeholder": "Teenus", + "anniversaries": "Tähtpäevad", + "add_anniversary": "Lisa kuupäev", + "anniversary_birth": "Sünnipäev", + "anniversary_wedding": "Aastapäev", + "anniversary_death": "Mälestuspäev", + "anniversary_other": "Muu", + "personal_info": "Isiklik info", + "add_personal_info": "Lisa kirje", + "personal_info_placeholder": "nt fotograafia", + "personal_expertise": "Kompetents", + "personal_hobby": "Hobi", + "personal_interest": "Huvi", + "personal_other": "Muu", + "level": "Tase", + "level_high": "Kõrge", + "level_medium": "Keskmine", + "level_low": "Madal", + "categories": "Kategooriad", + "categories_placeholder": "nt pere, sõbrad, kolleegid", + "categories_hint": "Sisesta, et otsida või lisada kategooriaid", + "category_add": "Lisa", + "note": "Märkmed", + "note_placeholder": "Lisa märkus...", + "gender": "Sugu", + "gender_sex": "Sugu", + "gender_male": "Mees", + "gender_female": "Naine", + "gender_other": "Muu", + "gender_none": "Ei kohaldu", + "gender_unknown": "Teadmata", + "gender_identity": "Sooidentiteet", + "gender_identity_placeholder": "Sooidentiteet...", + "calendar": "Kalender", + "calendar_uri": "Kalendri URL", + "scheduling_uri": "Planeerimise URL", + "freebusy_uri": "Vaba/hõivatud URL", + "context_work": "Töö", + "context_private": "Isiklik", + "add_email": "Lisa e-post", + "add_phone": "Lisa telefon", + "save": "Salvesta", + "cancel": "Tühista", + "creating": "Loomine...", + "updating": "Uuendamine...", + "name_required": "Sisesta eesnimi, perekonnanimi või organisatsioon", + "email_invalid": "Sisesta kehtiv e-posti aadress", + "email_error_inline": "Vigane e-posti aadressi vorming", + "save_failed": "Kontakti salvestamine ebaõnnestus", + "delete": "Kustuta", + "upload_photo": "Laadi foto üles", + "remove_photo": "Eemalda foto", + "photo_hint": "JPG või PNG, kuni 10 MB. Suurust muudetakse vajadusel.", + "photo_too_large": "Pilt on liiga suur (maksimaalselt 10 MB)", + "photo_invalid": "Vigane pildifail", + "change_photo": "Muuda" + }, + "groups": { + "create": "Uus grupp", + "edit": "Muuda gruppi", + "empty": "Gruppe veel pole", + "delete_confirm_title": "Kustuta grupp", + "delete_confirm": "Kas oled kindel, et soovid selle grupi kustutada?", + "name_label": "Grupi nimi", + "name_placeholder": "nt meeskond, pere", + "name_required": "Grupi nimi on kohustuslik", + "save_failed": "Grupi salvestamine ebaõnnestus", + "members_label": "Liikmed", + "search_members": "Otsi lisatavaid kontakte...", + "no_members": "Selles grupis pole liikmeid", + "member_count": "{count, plural, =0 {Liikmeid pole} one {1 liige} other {# liiget}}", + "send_email": "Saada kiri grupile", + "send_email_to": "Kellele", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "Selles grupis pole ühtegi e-posti aadressiga liiget." + }, + "import": { + "title": "Impordi kontaktid", + "drop_hint": "Kliki, et valida vCard-fail", + "file_types": ".vcf või .vcard failid", + "no_contacts": "Failist ei leitud ühtegi kontakti", + "parse_error": "vCard-faili töötlemine ebaõnnestus", + "found": "{count, plural, one {Leiti 1 kontakt} other {Leiti # kontakti}}", + "duplicate": "Duplikaat", + "select_all": "Vali kõik", + "deselect_all": "Tühista kõigi valik", + "selected": "{count, plural, one {1 valitud} other {# valitud}}", + "import_button": "Impordi", + "importing": "Importimine...", + "success": "{count, plural, one {Imporditi 1 kontakt} other {Imporditi # kontakti}}", + "failed": "Import ebaõnnestus", + "close": "Sulge", + "file_too_large": "Fail on liiga suur (maksimaalselt 5 MB)" + }, + "export": { + "title": "Ekspordi kontaktid", + "success": "{count, plural, one {Eksporditi 1 kontakt} other {Eksporditi # kontakti}}" + }, + "bulk": { + "selected": "{count, plural, one {1 valitud} other {# valitud}}", + "select_all": "Vali kõik", + "delete": "Kustuta", + "delete_confirm_title": "Kustuta kontaktid", + "delete_confirm": "Kustutada {count, plural, one {1 kontakt} other {# kontakti}}?", + "deleted": "{count, plural, one {Kustutati 1 kontakt} other {Kustutati # kontakti}}", + "add_to_group": "Lisa gruppi", + "choose_group": "Vali grupp", + "adding_contacts": "Lisamine: {count, plural, one {1 kontakt} other {# kontakti}}", + "added_to_group": "Kontaktid lisati gruppi", + "export": "Eksport", + "clear": "Tühista valik" + }, + "toast": { + "created": "Kontakt loodud", + "updated": "Kontakt uuendatud", + "deleted": "Kontakt kustutatud", + "error_create": "Kontakti loomine ebaõnnestus", + "error_update": "Kontakti uuendamine ebaõnnestus", + "error_delete": "Kontakti kustutamine ebaõnnestus" + }, + "context_menu": { + "open": "Ava", + "edit": "Muuda", + "send_email": "Saada kiri", + "add_to_group": "Lisa gruppi", + "export_vcard": "Ekspordi vCardina", + "delete": "Kustuta", + "call": "Helista", + "duplicate": "Dubleeri", + "print": "Prindi" + }, + "filters": { + "toggle": "Filtrid", + "select": "Vali", + "clear": "Tühjenda", + "close": "Sulge", + "title": "Täpsem filtreerimine", + "organization": "Ettevõte", + "organization_placeholder": "nt Acme Corp", + "job_title": "Ametinimetus", + "job_title_placeholder": "nt disainer", + "location": "Asukoht", + "location_placeholder": "Linn või riik", + "email_domain": "E-posti domeen", + "email_domain_placeholder": "example.com", + "birthday_month": "Sünnipäev kuul", + "any_month": "Iga kuu", + "has_email": "E-posti aadressiga", + "has_phone": "Telefoninumbriga", + "has_photo": "Fotoga", + "sort_by": "Sordi", + "sort_first_name": "Eesnimi", + "sort_last_name": "Perekonnanimi" + } + }, + "calendar": { + "title": "Kalender", + "back_to_email": "Tagasi e-posti juurde", + "back_to_month": "Tagasi kuu juurde", + "my_calendars": "Kalendrid", + "birthday_calendar": "Sünnipäevad", + "mini_calendar_change": "Kuu vahetamiseks klõpsa", + "views": { + "month": "Kuu", + "week": "Nädal", + "day": "Päev", + "agenda": "Päevakava", + "today": "Täna", + "month_hint": "Kuu (m)", + "week_hint": "Nädal (w)", + "day_hint": "Päev (d)", + "agenda_hint": "Päevakava (a)", + "tasks": "Ülesanded", + "tasks_hint": "Ülesanded (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# varasem sündmus} other {# varasemat sündmust}}", + "hidden_after": "{count, plural, one {# hilisem sündmus} other {# hilisemat sündmust}}", + "show_all_hours": "Näita kõiki tunde", + "show_display_hours": "Näita ainult {start} – {end}", + "create": "Loo sündmus", + "edit": "Muuda sündmust", + "delete": "Kustuta sündmus", + "details": "Sündmuse üksikasjad", + "no_events": "Sündmusi pole", + "all_day": "Kogu päev", + "more": "+{count} veel", + "show_more": "Näita rohkem", + "show_less": "Näita vähem", + "no_title": "(Pealkirjata)", + "resize": "Muuda sündmuse suurust", + "duplicate": "Dubleeri", + "today_header": "Täna", + "tomorrow_header": "Homme", + "agenda_show_earlier": "Näita varasemaid sündmusi", + "agenda_loading": "Laadimine…", + "agenda_range_start": "Kuvatakse sündmusi alates {date}", + "agenda_range_end": "Kuvatakse sündmusi kuni {date}", + "export_ics": "Ekspordi .ics-failina", + "copy_title": "Kopeeri pealkiri", + "copy_link": "Kopeeri koosoleku link", + "new_event": "Uus sündmus", + "new_all_day_event": "Uus kogu päeva sündmus", + "new_task": "Uus ülesanne", + "go_to_today": "Mine tänasesse päeva" + }, + "detail": { + "add_note": "Lisa märkus...", + "save_note": "Salvesta", + "note_saved": "Märkus lisatud", + "open_link": "Ava link", + "copy_location": "Kopeeri asukoht", + "meeting_link": "Koosoleku link", + "tentative": "Esialgne", + "cancelled": "Tühistatud", + "delete_confirm": "Kustutada see sündmus?" + }, + "form": { + "title": "Pealkiri", + "description": "Kirjeldus", + "location": "Asukoht", + "meeting_link": "Koosoleku link", + "start_date": "Alguskuupäev", + "end_date": "Lõpukuupäev", + "start_time": "Algusaeg", + "end_time": "Lõppaeg", + "all_day_event": "Kogu päeva sündmus", + "calendar_select": "Kalender", + "save": "Salvesta", + "cancel": "Tühista", + "delete_confirm": "Kas oled kindel, et soovid selle sündmuse kustutada?", + "color": "Värv" + }, + "participants": { + "availability": { + "title": "Saadavus", + "free": "Saadaval", + "busy": "Hõivatud", + "tentative": "Esialgne", + "unknown": "Ei ole selle serveri kasutaja", + "checking": "Kontrollimine…" + }, + "title": "Osalejad", + "add": "Lisa osaleja", + "organizer": "Korraldaja", + "attendee": "Osaleja", + "accepted": "Nõustunud", + "declined": "Keeldunud", + "tentative": "Esialgne", + "needs_action": "Vajab vastust", + "remove": "Eemalda", + "edit": "Muuda", + "email_placeholder": "Lisa e-posti aadress või otsi kontaktidest", + "send_invitations": "Saada osalejatele kutsed", + "status_summary": "{accepted} nõustunud, {pending} ootel", + "invited_by": "Kutse saatjalt {name}", + "respond_below": "Vasta allolevate nuppudega", + "rsvp_label": "Sinu vastus", + "cancel_notification": "Osalejaid teavitatakse tühistamisest", + "you_organizer": "Sina oled korraldaja", + "you_attendee": "Sina oled osaleja", + "no_participants": "Osalejaid pole", + "count": "{count, plural, one {# osaleja} other {# osalejat}}" + }, + "recurrence": { + "title": "Kordumine", + "none": "Ei kordu", + "daily": "Iga päev", + "weekly": "Iga nädal", + "monthly": "Iga kuu", + "yearly": "Iga aasta", + "every_n_days": "Iga {count} päeva järel", + "every_n_weeks": "Iga {count} nädala järel", + "every_n_months": "Iga {count} kuu järel", + "until": "Kuni", + "occurrences": "{count} kordust", + "custom": "Kohandatud…", + "edit_custom": "Muuda kohandatud kordumist", + "every_n_years": "Iga {count} aasta järel", + "on_days": "päevadel {days}", + "on_day_n": "kuupäeval {day}", + "on_the_nth": "{nth} {day}", + "in_month": "kuus {month}", + "nth_1": "esimene", + "nth_2": "teine", + "nth_3": "kolmas", + "nth_4": "neljas", + "nth_last": "viimane", + "editor_freq_day": "Päev", + "editor_freq_week": "Nädal", + "editor_freq_month": "Kuu", + "editor_freq_year": "Aasta", + "editor_repeats_on": "Kordub", + "editor_every": "Iga", + "editor_unit_days": "päev(a)", + "editor_unit_weeks": "nädal(at)", + "editor_unit_months": "kuu(d)", + "editor_unit_years": "aasta(t)", + "editor_on_day": "kuupäeval", + "editor_on_the": "kuu", + "editor_in": "kuus", + "editor_ends": "Lõpeb", + "editor_never": "Mitte kunagi", + "editor_ends_on": "Kuupäeval", + "editor_ends_after": "Pärast", + "editor_occurrences": "kordust" + }, + "recurrence_scope": { + "edit_title": "Korduva sündmuse muutmine", + "delete_title": "Korduva sündmuse kustutamine", + "description": "See on korduv sündmus. Milliseid sündmusi soovid muuta?", + "this_event": "Ainult see sündmus", + "this_and_future": "See ja järgnevad sündmused", + "all_events": "Kõik sündmused", + "cancel": "Tühista", + "save": "Salvesta", + "delete": "Kustuta", + "rsvp_title": "Vasta korduvale sündmusele", + "rsvp_description": "See on korduv sündmus. Millistele sündmustele sinu vastus kehtib?", + "respond": "Vasta" + }, + "alerts": { + "title": "Meeldetuletused", + "none": "Meeldetuletusi pole", + "at_time": "Sündmuse toimumise ajal", + "minutes_before": "{count, plural, one {# minut enne} other {# minutit enne}}", + "hours_before": "{count, plural, one {# tund enne} other {# tundi enne}}", + "days_before": "{count, plural, one {# päev enne} other {# päeva enne}}", + "weeks_before": "{count, plural, one {# nädal enne} other {# nädalat enne}}", + "unit_minutes_before": "minutit enne", + "unit_hours_before": "tundi enne", + "unit_days_before": "päeva enne", + "unit_weeks_before": "nädalat enne", + "add": "Lisa meeldetuletus", + "remove": "Eemalda meeldetuletus", + "amount": "Meeldetuletuse kogus", + "unit": "Meeldetuletuse ühik", + "action": "Meeldetuletuse tüüp", + "action_display": "Märguanne", + "action_email": "E-kiri", + "by_email": "{label} e-kirjaga", + "kept": "{count, plural, one {Säilitatakse veel # meeldetuletus, mida siin muuta ei saa} other {Säilitatakse veel # meeldetuletust, mida siin muuta ei saa}}", + "other": "{count, plural, one {# muu meeldetuletus} other {# muud meeldetuletust}}", + "calendar_defaults": "Kalendri vaikeseaded", + "customize": "Kohanda", + "use_defaults": "Kasuta kalendri vaikeseadeid" + }, + "settings": { + "limit_hours": "Piira nähtavaid tunde", + "limit_hours_desc": "Näita päeva- ja nädalavaates ainult neid tunde. Väljaspool neid olevad sündmused on kättesaadavad iga päeva üla- ja allservas olevate nooltega.", + "visible_hours": "Nähtavad tunnid", + "visible_hours_start": "Nähtavate tundide algus", + "visible_hours_end": "Nähtavate tundide lõpp", + "hide_non_working_days": "Peida puhkepäevad", + "hide_non_working_days_desc": "Jäta päevad, mil ei tööta, nädalavaatest välja. Nende sündmused on endiselt näha kuu-, päeva- ja päevakavavaates.", + "working_days": "Tööpäevad", + "title": "Kalendri seaded", + "default_view": "Vaikevaade", + "week_starts_on": "Nädal algab", + "time_format": "Ajavorming", + "default_calendar": "Vaikekalender", + "default_reminder": "Vaikimisi meeldetuletus", + "time_format_12h": "12-tunnine", + "time_format_24h": "24-tunnine", + "notifications_enabled": "Sündmuste teavitused", + "notifications_enabled_desc": "Kuva teavitusi eelseisvate kalendrisündmuste kohta", + "notification_sound": "Teavituse heli", + "notification_sound_desc": "Esita kalendriteavituste puhul heli", + "invitation_parsing": "Tuvasta e-kirjade kutsed", + "invitation_parsing_desc": "Tuvasta kalendrikutsed e-kirjade manustes ja kuva kalenditoimingud", + "show_time_in_month_view": "Kuva kellaaeg kuuvaates", + "show_time_in_month_view_desc": "Kuva sündmuste kellaajad kuu kalendrivaates. Väikestel ekraanidel kuvatakse punktide asemel täielikud sündmuste kirjed.", + "show_week_numbers": "Kuva nädalanumbrid", + "show_week_numbers_desc": "Kuva nädalanumbrid minikalendris", + "calendar_free_scroll": "Vaba kerimine", + "calendar_free_scroll_desc": "Keri kuude, nädalate ja päevade vahel pidevalt, mitte ühe perioodi kaupa", + "enable_tasks": "Luba ülesanded", + "enable_tasks_desc": "Kuva kalendris ülesannete vaade tegemiste haldamiseks", + "show_tasks_on_calendar": "Kuva ülesanded kalendris", + "show_tasks_on_calendar_desc": "Kuva ülesannete sildid päeva- ja nädalavaates", + "hover_preview": "Sündmuse hõljutusega eelvaade", + "hover_preview_desc": "Kuva kalendrisündmuse kohal hõljutades üksikasjade hüpikaken", + "hover_preview_instant": "Koheselt", + "hover_preview_delay_500ms": "0,5-sekundiline viivitus", + "hover_preview_delay_1s": "1-sekundiline viivitus", + "hover_preview_delay_2s": "2-sekundiline viivitus", + "hover_preview_off": "Keelatud", + "organizer_identity": "Korralda kutseid kui", + "organizer_identity_desc": "Kalendriaadress, mida kasutatakse korraldajana, kui kutsud inimesi sündmustele.", + "organizer_identity_failed": "Korraldaja aadressi muutmine ebaõnnestus", + "show_birthday_calendar": "Kontaktide sünnipäevakalender", + "show_birthday_calendar_desc": "Kuva virtuaalne kalender sinu kontaktide sünnipäevadega" + }, + "days": { + "monday": "Esmaspäev", + "tuesday": "Teisipäev", + "wednesday": "Kolmapäev", + "thursday": "Neljapäev", + "friday": "Reede", + "saturday": "Laupäev", + "sunday": "Pühapäev", + "mon": "E", + "tue": "T", + "wed": "K", + "thu": "N", + "fri": "R", + "sat": "L", + "sun": "P" + }, + "months": { + "jan": "Jaan", + "feb": "Veebr", + "mar": "Märts", + "apr": "Apr", + "may": "Mai", + "jun": "Juuni", + "jul": "Juuli", + "aug": "Aug", + "sep": "Sept", + "oct": "Okt", + "nov": "Nov", + "dec": "Dets", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Sündmus loodud", + "event_updated": "Sündmus uuendatud", + "event_deleted": "Sündmus kustutatud", + "calendar_created": "Kalender loodud", + "calendar_deleted": "Kalender kustutatud", + "event_move_error": "Sündmuse liigutamine ebaõnnestus", + "event_resize_error": "Sündmuse suuruse muutmine ebaõnnestus", + "alert_title": "Eelseisev sündmus", + "alert_now": "Algab kohe", + "alert_in_minutes": "{count} min pärast", + "invitation_sent": "Kutsed saadetud", + "invitations_denied_title": "Kutseid ei saa saata", + "invitations_denied": "Server keeldus kutseid saatmast: {reason}. Kas salvestada sündmus kutseid saatmata?", + "save_without_invitations": "Salvesta kutseteta", + "rsvp_updated": "Vastus uuendatud", + "rsvp_error": "Vastuse uuendamine ebaõnnestus", + "event_duplicated": "Sündmus dubleeritud", + "event_error": "Sündmuse salvestamine ebaõnnestus", + "task_due": "Ülesande tähtaeg", + "event_exported": "Sündmus eksporditud", + "title_copied": "Pealkiri kopeeritud", + "link_copied": "Link kopeeritud" + }, + "status": { + "loading_calendars": "Kalendrite laadimine...", + "loading_events": "Sündmuste laadimine..." + }, + "quick_create": { + "placeholder": "Uue sündmuse pealkiri", + "aria_label": "Kiire sündmuse loomine", + "add_title": "Lisa pealkiri", + "more_options": "Rohkem valikuid" + }, + "nav_prev": "Eelmine", + "nav_next": "Järgmine", + "nav_open_menu": "Ava menüü", + "import": { + "title": "Kalendri importimine", + "tab_file": "Fail", + "tab_url": "URL", + "select_file": "Vali .ics fail", + "drop_file": "või lohista fail siia", + "supported_formats": "Toetab iCalendari (.ics) faile", + "url_description": "Sündmuste importimiseks sisesta välise iCalendari (.ics) voo URL.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Toetab CalDAV ja iCalendari (.ics) URL-e", + "fetch": "Laadi", + "invalid_url": "Palun sisesta kehtiv URL", + "url_fetch_failed": "Kalendri laadimine URL-ilt ebaõnnestus", + "parsing": "Kalendrifaili töötlemine...", + "parsed_events": "Leiti {count} sündmust", + "no_events": "Failist ei leitud sündmusi", + "select_all": "Vali kõik", + "deselect_all": "Tühista kõigi valik", + "target_calendar": "Impordi kalendrisse", + "import_button": "Impordi valitud", + "importing": "Sündmuste importimine...", + "success": "{count} sündmust imporditi edukalt", + "error": "Kalendri importimine ebaõnnestus", + "file_too_large": "Fail ületab 10 MB piirangu", + "invalid_format": "Vale kalendrifaili vorming" + }, + "webcal_action": { + "title": "Ava kalendrilink", + "description": "Kuidas soovid kalendrit \"{name}\" kasutada?", + "import_title": "Impordi ühekordselt", + "import_description": "Laadi sündmused kohe alla ja kopeeri need ühte oma kalendritest.", + "subscribe_title": "Telli", + "subscribe_description": "Hoia see kalender automaatselt sünkroonituna eraldi kalendrina.", + "cancel": "Tühista" + }, + "management": { + "title": "Kalendrite haldamine", + "description": "Loo, nimeta ümber ja kohanda oma kalendreid. Kalendri värvi kiireks muutmiseks tee küljeribal kalendril paremklõps.", + "name": "Nimi", + "name_placeholder": "Kalendri nimi", + "color": "Värv", + "change_color": "Muuda värvi", + "random_color": "Uus juhuslik värv", + "add_calendar": "Lisa kalender", + "edit": "Muuda", + "delete": "Kustuta", + "save": "Salvesta", + "create": "Loo", + "cancel": "Tühista", + "default": "Vaikimisi", + "confirm_delete": "Kustutada \"{name}\"? Kõik selle kalendri sündmused eemaldatakse.", + "confirm_clear": "Tühjendada kõik sündmused kalendrist \"{name}\"? Seda ei saa tagasi võtta.", + "set_default": "Määra vaikimisi kalendriks", + "default_updated": "Vaikekalender uuendatud", + "error_default": "Vaikekalendri määramine ebaõnnestus", + "clear_events": "Tühjenda sündmused", + "events_cleared": "{count} sündmust tühjendatud", + "error_clear": "Kalendrisündmuste tühjendamine ebaõnnestus", + "calendar_created": "Kalender loodud", + "calendar_updated": "Kalender uuendatud", + "calendar_deleted": "Kalender kustutatud", + "color_updated": "Kalendri värv uuendatud", + "error_create": "Kalendri loomine ebaõnnestus", + "error_update": "Kalendri uuendamine ebaõnnestus", + "error_delete": "Kalendri kustutamine ebaõnnestus", + "caldav_url": "CalDAV URL", + "copy_url": "Kopeeri CalDAV URL", + "url_copied": "CalDAV URL kopeeriti lõikelauale", + "share": "Jaga kalendrit", + "new_event_in_calendar": "Uus sündmus selles kalendris", + "shared_with": "Jagatud: {names}", + "shared_with_count": "{count, plural, one {Jagatud 1 inimesega} other {Jagatud # inimesega}}", + "move_up": "Liiguta üles", + "move_down": "Liiguta alla", + "kind": "Sisu", + "kind_events": "Sündmused", + "kind_tasks": "Ülesanded", + "kind_both": "Sündmused ja ülesanded", + "kind_hint": "Määrab, millised rakendused seda kalendrit CalDAV kaudu sünkroonides näevad. Seda ei saa hiljem muuta.", + "default_alerts": "Vaikemeeldetuletused", + "default_alerts_title": "Kalendri „{name}” vaikemeeldetuletused", + "default_alerts_hint": "Selle kalendri uued sündmused saavad need meeldetuletused, kui sa neid sündmuses ei muuda. Siin muutmine ei muuda olemasolevaid sündmusi. E-posti meeldetuletused saadab meiliserver.", + "default_alerts_timed": "Kellaajaga sündmused", + "default_alerts_all_day": "Terve päeva sündmused", + "default_alerts_updated": "Vaikemeeldetuletused uuendatud" + }, + "subscription": { + "title": "iCal tellimus", + "section_title": "iCal tellimused", + "description": "Telli väline iCalendari voog. Sündmused sünkroonitakse automaatselt omaette kalendrisse. Toetab https:// ja webcal:// URL-e.", + "url_label": "Kalendri URL", + "url_placeholder": "https://example.com/calendar.ics või webcal://...", + "name_label": "Kalendri nimi", + "name_placeholder": "nt riigipühad", + "color_label": "Värv", + "refresh_interval": "Värskendamise intervall", + "interval_15": "Iga 15 minuti järel", + "interval_30": "Iga 30 minuti järel", + "interval_60": "Iga tunni järel", + "interval_360": "Iga 6 tunni järel", + "interval_1440": "Iga päev", + "subscribe": "Telli", + "subscribing": "Tellimine...", + "save": "Salvesta muudatused", + "saving": "Salvestamine...", + "edit": "Muuda", + "edit_title": "Tellimuse muutmine", + "updated": "\"{name}\" uuendatud", + "update_error": "Tellimuse uuendamine ebaõnnestus", + "invalid_url": "Palun sisesta kehtiv URL", + "success": "Tellitud: \"{name}\"", + "error": "Tellimuse lisamine ebaõnnestus", + "refresh": "Värskenda kohe", + "refresh_success": "Tellimus värskendatud", + "refresh_error": "Tellimuse värskendamine ebaõnnestus", + "unsubscribe": "Lõpeta tellimus", + "confirm_delete": "Lõpetada tellimus \"{name}\"? Kalender ja kõik selle sündmused eemaldatakse.", + "deleted": "Tellimus eemaldatud", + "delete_error": "Tellimuse eemaldamine ebaõnnestus", + "last_refreshed": "Viimati uuendatud: {time}" + }, + "tasks": { + "label": "Ülesanded", + "no_tasks": "Ülesandeid pole", + "no_title": "(Pealkirjata)", + "mark_complete": "Märgi täidetuks", + "mark_incomplete": "Märgi täitmata", + "filter_all": "Kõik", + "filter_pending": "Ootel", + "filter_completed": "Täidetud", + "filter_overdue": "Tähtaeg möödas", + "show_completed": "Kuva täidetud", + "create": "Uus ülesanne", + "edit": "Muuda ülesannet", + "title_placeholder": "Ülesande pealkiri", + "description_placeholder": "Lisa kirjeldus...", + "due_date": "Tähtaeg", + "include_time": "Kaasa kellaaeg", + "priority": "Prioriteet", + "priority_none": "Puudub", + "priority_high": "Kõrge", + "priority_medium": "Keskmine", + "priority_low": "Madal", + "progress": "Olek", + "progress_needs_action": "Vajab vastust", + "progress_in_process": "Pooleli", + "progress_completed": "Täidetud", + "progress_cancelled": "Tühistatud", + "calendar": "Kalender", + "alert": "Meeldetuletus", + "alert_none": "Puudub", + "alert_at_time": "Tähtaja saabudes", + "alert_5min": "5 minutit enne", + "alert_15min": "15 minutit enne", + "alert_30min": "30 minutit enne", + "alert_1hr": "1 tund enne", + "alert_1day": "1 päev enne", + "delete": "Kustuta", + "cancel": "Tühista", + "save": "Salvesta", + "quick_add_placeholder": "Lisa ülesanne...", + "due_today": "Täna", + "due_tomorrow": "Homme", + "overdue": "Tähtaeg möödas" + }, + "toolbar": { + "more": "Rohkem toiminguid", + "select_view": "Muuda vaadet, praegu {view}" + }, + "other_calendars": "Muud kalendrid", + "calendar_options": "Kalendri {name} valikud" + }, + "calendar_event_notifications": { + "someone": "Keegi", + "invited": "{name} kutsus sind sündmusele \"{title}\"", + "updated": "{name} uuendas sündmust \"{title}\"", + "cancelled": "{name} tühistas sündmuse \"{title}\"", + "untitled": "Nimetu sündmus" + }, + "share_notifications": { + "someone": "Keegi", + "shared": "{name} jagas sinuga {kind} \"{object}\"", + "changed": "{name} muutis sinu juurdepääsu {kind}-le \"{object}\"", + "revoked": "{name} eemaldas sinu juurdepääsu {kind}-le \"{object}\"", + "object": { + "folder": "kaust", + "calendar": "kalender", + "address_book": "aadressiraamat", + "files": "failikaust" + } + }, + "sharing": { + "title": "Jaga \"{name}\"", + "description": "Anna juurdepääs selle serveri teistele kasutajatele või gruppidele. Muudatused rakenduvad koheselt.", + "no_shares": "Pole veel kellegagi jagatud.", + "add_person": "Lisa isik või grupp", + "search_placeholder": "Otsi nime või e-posti järgi…", + "loading_principals": "Kasutajate laadimine…", + "no_principals": "Teisi kasutajaid ega gruppe ei leitud.", + "no_match": "Vasteid ei leitud.", + "remove": "Eemalda juurdepääs", + "group": "Grupp", + "share_added": "Juurdepääs antud", + "share_updated": "Juurdepääs uuendatud", + "share_removed": "Juurdepääs eemaldatud", + "share_failed": "Jagamise uuendamine ebaõnnestus", + "preset": { + "freeBusy": "Ainult vaba/hõivatud", + "read": "Ainult lugemine", + "readWrite": "Lugemine ja kirjutamine", + "manager": "Haldur", + "custom": "Kohandatud" + } + }, + "advanced_search": { + "title": "Täpsem otsing", + "from": "Saatja", + "from_placeholder": "Saatja e-post või nimi", + "to": "Saaja", + "to_placeholder": "Saaja e-post või nimi", + "subject": "Teema", + "subject_placeholder": "Teema sisaldab...", + "body": "Sisu", + "body_placeholder": "Sisu sisaldab...", + "folder": "Kaust", + "all_folders": "Kõik kaustad", + "all_folders_except_spam_trash": "Kõik kaustad peale rämpsposti ja prügikasti", + "has_attachment": "Manused", + "date_after": "Pärast", + "date_before": "Enne", + "size_min": "Suurem kui (KB)", + "size_max": "Väiksem kui (KB)", + "starred": "Tähistatud", + "unread": "Lugemata", + "read": "Loetud", + "yes": "Jah", + "no": "Ei", + "clear": "Tühjenda", + "clear_all": "Tühjenda kõik", + "filters_active": "{count} filter", + "filters_active_plural": "{count} filtrit", + "toggle_filters": "Rohkem", + "search_hint": "Kasuta täpseks otsinguks täiendavaid filtreid", + "advanced_filters_tooltip": "Täpsema otsingu filtrid", + "results_found": "{count, plural, =0 {Tulemusi ei leitud} one {# tulemus leitud} other {# tulemust leitud}}", + "results_found_more": "{count}+ tulemust leitud", + "suggestions_recent": "Hiljutised otsingud", + "suggestions_people": "Inimesed", + "suggestions_from": "Otsi e-kirju saatjalt {address}", + "suggestions_to": "Otsi e-kirju saajale {address}", + "suggestions_remove_recent": "Eemalda hiljutistest otsingutest", + "has_words": "Sisaldab sõnu", + "size": "Suurus", + "date_within": "Kuupäev", + "search_in": "Otsi kaustast", + "has_attachment_check": "Sisaldab manust", + "search": "Otsi" + }, + "welcome": { + "title": "Tere tulemast sinu postkasti", + "tip_compose": "Vajuta c uue e-kirja kirjutamiseks", + "tip_shortcuts": "Vajuta ? kõigi kiirklahvide nägemiseks", + "tip_sidebar": "Kalender, Kontaktid ja Seaded leiad külgmenüüst", + "tip_settings": "Kohanda oma kogemust seadetes", + "tip_compose_touch": "Uue kirja kirjutamiseks puuduta pliiatsinuppu", + "tip_sidebar_touch": "Kalender, kontaktid ja seaded asuvad alumisel ribal", + "got_it": "Selge", + "settings": "Seaded", + "dismiss": "Sulge", + "start_tour": "Alusta tutvustust" + }, + "demo_welcome": { + "title": "Tere tulemast Bulwark Maili juurde", + "description": "Tutvu täisfunktsionaalse veebipostiga - otse oma brauseris. Kõik andmed jäävad sinu seadmesse, seega võid vabalt kõike proovida.", + "feature_email": "Loe ja kirjuta e-kirju", + "feature_organize": "Märgid, tähed ja kaustad", + "feature_shortcuts": "Kiirklahvid", + "feature_privacy": "100% privaatne demo", + "hint": "Klõpsa alustamiseks vasakul mõnel e-kirjal või tee allpool tutvustusring." + }, + "files": { + "office_edit": "Muuda dokumenti", + "office_loading": "Dokumendiredaktori avamine…", + "office_error": "Dokumendiredaktorit ei õnnestunud avada.", + "office_close": "Sulge redaktor", + "open_in_new_tab": "Ava uues vahelehes", + "title": "Failid", + "search_placeholder": "Otsi faile...", + "empty_state_title": "Faile pole veel", + "upload_files": "Laadi failid üles", + "new_folder": "Uus kaust", + "new_folder_name": "Kausta nimi", + "rename": "Nimeta ümber", + "rename_title": "Nimeta ümber", + "new_name": "Uus nimi", + "name_forbidden_chars": "Server ei luba nimedes neid märke: {chars}", + "name_reserved": "See nimi on serveris reserveeritud.", + "delete": "Kustuta", + "delete_confirm_title": "Kustuta ressurss", + "delete_confirm_message": "Kas oled kindel, et soovid kustutada \"{name}\"? Seda ei saa tagasi võtta.", + "download": "Laadi alla", + "name": "Nimi", + "size": "Suurus", + "modified": "Muudetud", + "type": "Tüüp", + "folder": "Kaust", + "file": "Fail", + "no_accounts": "Ühendatud kontosid pole.", + "uploading": "Üleslaadimine...", + "upload_success": "{count, plural, one {1 fail üles laaditud} other {# faili üles laaditud}}", + "upload_canceled": "Üleslaadimine tühistatud", + "upload_canceled_partial": "{count, plural, one {1 fail} other {# faili}} laaditi üles enne tühistamist", + "upload_error": "Faili üleslaadimine ebaõnnestus", + "create_folder_success": "Kaust loodud", + "create_folder_error": "Kausta loomine ebaõnnestus", + "delete_success": "Edukalt kustutatud", + "delete_error": "Kustutamine ebaõnnestus", + "rename_success": "Edukalt ümber nimetatud", + "rename_error": "Ümbernimetamine ebaõnnestus", + "download_error": "Allalaadimine ebaõnnestus", + "not_available": "Failihoidla ei ole selles serveris saadaval", + "cancel": "Tühista", + "create": "Loo", + "save": "Salvesta", + "no_results": "Otsingule vastavaid faile ei leitud", + "batch_delete_confirm_message": "Kas oled kindel, et soovid kustutada {count, plural, one {1 üksuse} other {# üksust}}? Seda ei saa tagasi võtta.", + "batch_delete_success": "{count, plural, one {1 üksus kustutatud} other {# üksust kustutatud}}", + "grid_view": "Ruudustikvaade", + "list_view": "Loendivaade", + "details": "Üksikasjad", + "path": "Asukoht", + "preview": "Eelvaade", + "preview_error": "Eelvaate laadimine ebaõnnestus", + "cut": "Lõika", + "copy": "Kopeeri", + "paste": "Kleebi", + "move_success": "{count, plural, one {1 üksus liigutatud} other {# üksust liigutatud}}", + "move_error": "Liigutamine ebaõnnestus", + "paste_success": "Edukalt kleebitud", + "paste_error": "Kleepimine ebaõnnestus", + "new_text_file": "Uus tekstifail", + "file_name": "Faili nimi", + "retry": "Proovi uuesti", + "refresh": "Värskenda", + "duplicate": "Dubleeri", + "duplicate_success": "Edukalt dubleeritud", + "duplicate_error": "Dubleerimine ebaõnnestus", + "share": "Jaga", + "shared": "Jagatud", + "shared_with_me": "Minuga jagatud", + "shared_by": "Jagas {name}", + "create_file_success": "Fail loodud", + "create_file_error": "Faili loomine ebaõnnestus", + "favorites": "Lemmikud", + "recent": "Hiljutised", + "show_in_folder": "Näita kaustas", + "remove_from_recent": "Eemalda hiljutistest", + "upload_folder": "Laadi kaust üles", + "file_too_large": "\"{name}\" ületab maksimaalset failisuurust ({max})", + "undo": "Võta tagasi", + "undo_success": "Tegevus tagasi võetud", + "undo_error": "Tagasivõtmine ebaõnnestus", + "toolbar": "Failitoimingud", + "open_folder_tree": "Ava kaustapuu", + "file_list": "Failid ja kaustad", + "context_menu": "Toimingud", + "settings_ascending": "Kasvav", + "settings_descending": "Kahanev", + "disabled_title": "Administraator on failide funktsiooni välja lülitanud", + "disabled_description": "Suurte failide üleslaadimine WebDAV kaudu võib põhjustada Stalwart/RocksDB ebastabiilsust, sealhulgas mälupuudusest tingitud krahhe ja taastamatut kettaruumi kasutust. Kustutatud failid ei pruugi objektihoidlast kohe kaduda. Seda funktsiooni ei soovitata kasutada tootmiskeskkonnas.", + "migration_title": "Failide uuendamine…", + "migration_description": "Kaustade ja failide korrastamine õigesse struktuuri. See toimub ainult üks kord.", + "new": "Uus", + "my_files": "Minu failid", + "folders": "Kaustad", + "files_heading": "Failid", + "open": "Ava", + "star": "Tähista", + "unstar": "Eemalda tärn", + "expand_folder": "Laienda {name}", + "collapse_folder": "Ahenda {name}", + "sidebar_hint": "Tähista kaustu ja ava faile, et neid siit leida.", + "breadcrumbs": "Kausta tee", + "show_path": "Näita teel peidetud kaustu", + "selection_actions": "Valitud üksuste toimingud", + "selected_count": "{count, plural, one {1 valitud} other {# valitud}}", + "clear_selection": "Tühista valik", + "select_all": "Vali kõik", + "select_item": "Vali {name}", + "more_actions": "Rohkem toiminguid: {name}", + "paste_here": "Kleebi sellesse kausta", + "paste_count": "{count, plural, one {Kleebi 1 üksus} other {Kleebi # üksust}}", + "sort_by": "Sorteeri", + "view_mode": "Paigutus", + "clear_search": "Tühjenda otsing", + "no_results_for": "Selles kaustas pole midagi, mis vastaks päringule „{query}”", + "download_count": "{count, plural, one {Laadi alla 1 fail} other {Laadi alla # faili}}", + "delete_count": "{count, plural, one {Kustuta 1 üksus} other {Kustuta # üksust}}", + "close_details": "Sulge üksikasjad", + "file_kind": "{ext}-fail", + "folder_summary": "{folders, plural, =0 {Kaustu pole} one {1 kaust} other {# kausta}}, {files, plural, =0 {faile pole} one {1 fail} other {# faili}}", + "details_hint": "Üksikasjade nägemiseks vali fail või kaust.", + "sharing": "Jagamine", + "shared_with_count": "{count, plural, one {Jagatud 1 inimesega} other {Jagatud # inimesega}}", + "only_you": "Ainult sina", + "empty_folder_title": "See kaust on tühi", + "empty_folder_description": "Lohista failid siia või laadi need oma seadmest üles.", + "empty_folder_read_only": "Sellesse kausta ei saa faile lisada.", + "choose_account": "Vali konto", + "drop_to_upload": "Lase lahti, et laadida kausta {folder}", + "uploading_count": "Üleslaadimine {current} / {total}", + "dismiss": "Sulge", + "zoom_in": "Suumi sisse", + "zoom_out": "Suumi välja", + "zoom_reset": "Sobita ekraanile", + "rotate": "Pööra", + "close_preview": "Sulge" + }, + "smime": { + "your_certificates": "Sinu sertifikaadid", + "your_certificates_desc": "Impordi ja halda oma S/MIME sertifikaate e-kirjade allkirjastamiseks ja krüpteerimiseks", + "recipient_certificates": "Saajate sertifikaadid", + "recipient_certificates_desc": "Avalikud sertifikaadid saajatele saadetavate e-kirjade krüpteerimiseks", + "identity_bindings": "Identiteedi võtmeseosed", + "identity_bindings_desc": "Seo S/MIME sertifikaadid oma e-posti identiteetidega", + "defaults_title": "Vaikeseaded", + "defaults_desc": "Seadista allkirjastamise ja krüpteerimise vaikekäitumine", + "import_pkcs12": "Impordi PKCS#12 (.p12/.pfx)", + "import_public_cert": "Impordi sertifikaat", + "no_certificates": "Sertifikaate pole veel imporditud", + "no_recipient_certs": "Saajate sertifikaate pole", + "expires": "Aegub", + "expired": "Aegunud", + "bound_to": "Seotud", + "no_key_bound": "Puudub", + "lock": "Lukusta võti", + "unlock": "Ava võti", + "details": "Vaata üksikasju", + "delete": "Kustuta", + "encrypt_by_default": "Krüpteeri vaikimisi", + "encrypt_by_default_desc": "Krüpteeri e-kirjad automaatselt, kui kõigil saajatel on sertifikaat", + "remember_unlocked": "Jäta avatud võtmed meelde", + "remember_unlocked_desc": "Hoia võtmed avatuna kogu selle brauserisessiooni jooksul", + "sign_default_for": "Allkirjasta vaikimisi", + "enter_p12_passphrase": "Sisesta PKCS#12 paroolfraas", + "p12_passphrase_desc": "Sisesta paroolfraas, mis kaitseb seda sertifikaadifaili", + "enter_storage_passphrase": "Määra hoidla paroolfraas", + "storage_passphrase_desc": "Vali paroolfraas, mis kaitseb seda võtit brauseris puhkeolekus", + "next": "Järgmine", + "import": "Impordi", + "unlock_key": "Ava võti", + "unlock_key_desc": "Sisesta hoidla paroolfraas, et avada see võti allkirjastamiseks või dekrüpteerimiseks", + "passphrase_placeholder": "Sisesta paroolfraas", + "confirm_passphrase_placeholder": "Kinnita paroolfraas", + "passphrase_mismatch": "Paroolfraasid ei kattu", + "cancel": "Tühista", + "processing": "Töötlemine…", + "close": "Sulge", + "certificate_details": "Sertifikaadi üksikasjad", + "cert_subject": "Subjekt", + "cert_issuer": "Väljastaja", + "cert_email": "E-post", + "cert_serial": "Seerianumber", + "cert_validity": "Kehtivus", + "cert_fingerprint": "Sõrmejälg (SHA-256)", + "cert_algorithm": "Algoritm", + "cert_capabilities": "Võimalused", + "cert_source": "Allikas", + "cert_expired": "See sertifikaat on aegunud", + "cert_not_yet_valid": "See sertifikaat ei kehti veel", + "cap_sign": "Allkirjastamine", + "cap_encrypt": "Krüpteerimine", + "cap_none": "Puudub", + "show_passphrase": "Näita paroolfraasi", + "hide_passphrase": "Peida paroolfraas", + "sign_toggle": "Allkirjasta", + "encrypt_toggle": "Krüpteeri", + "missing_recipient_certs": "Sertifikaadid puuduvad: {emails}", + "missing_sender_cert": "Selle identiteediga pole sertifikaati seotud", + "status_encrypted_ok": "See sõnum oli krüpteeritud", + "status_encrypted_no_key": "See sõnum on krüpteeritud, kuid sobivat võtit ei leitud", + "status_encrypted_failed": "Selle sõnumi dekrüpteerimine ebaõnnestus", + "status_signed_valid": "Allkiri on kontrollitud", + "status_signed_invalid": "Allkirja kontrollimine ebaõnnestus", + "status_signed_expired_cert": "Allkirjastatud aegunud sertifikaadiga", + "status_signed_self_signed": "Allkiri on kehtiv, kuid sertifikaat on ise-allkirjastatud (ei ole usaldusväärne)", + "status_signed_mismatch": "Allkiri on kehtiv, kuid allkirjastaja ei ühti saatjaga", + "status_unsupported": "Toetamata S/MIME vorming", + "auto_import_signer_certs": "Impordi allkirjastajate sertifikaadid automaatselt", + "auto_import_signer_certs_desc": "Salvesta kontrollitud allkirjastatud e-kirjade sertifikaadid automaatselt hilisemaks krüpteerimiseks", + "export": "Ekspordi", + "enter_export_passphrase": "Määra ekspordi paroolfraas", + "export_passphrase_desc": "Vali paroolfraas eksporditud PKCS#12 faili kaitsmiseks", + "export_storage_desc": "Sisesta hoidla paroolfraas, et dekrüpteerida võti eksportimiseks", + "incorrect_passphrase": "Vale paroolfraas" + }, + "tour": { + "step_counter": "Samm {current}/{total}", + "skip": "Jäta tutvustus vahele", + "back": "Tagasi", + "next": "Järgmine", + "finish": "Lõpeta", + "take_a_tour": "Tutvu liidesega", + "restart_title": "Sissejuhatav tutvustus", + "restart_desc": "Vaata liidese juhendatud tutvustust uuesti", + "restart_button": "Alusta tutvustust uuesti", + "show_on_new_devices_title": "Näita uutes seadmetes", + "show_on_new_devices_desc": "Näita tervitusteadet ja tutvustust uuesti, kui logid esimest korda sisse uues seadmes, isegi kui oled need mujal juba läbinud", + "sidebar_title": "Sinu postkastid", + "sidebar_desc": "See on sinu kaustade külgriba. Kirjade vaatamiseks klõpsa mistahes postkastil. Saad luua kaustu, lohistada kirju nende vahel ja näha korraga lugemata kirjade arvu.", + "compose_title": "Koosta e-kiri", + "compose_desc": "Klõpsa siia, et kirjutada uus e-kiri. Saad lisada saajaid, manuseid ja kasutada rikastatud tekstivormingut.", + "search_title": "Otsi oma kirjade seast", + "search_desc": "Otsi saatja, teema või sisu järgi. Täpsemate valikute (kuupäevavahemik, manused, tärniga tähistatud kirjad) jaoks klõpsa filtriikoonil.", + "email_list_title": "Sinu kirjade loend", + "email_list_desc": "Kirjad kuvatakse siin. Lugemiseks klõpsa kirjal, see avaneb paremal. Märkeruutude abil saad valida mitu kirja ja need korraga liigutada, kustutada või sildistada.", + "email_viewer_title": "Lugemispaan", + "email_viewer_desc": "Valitud kiri avaneb siin. Tööriistariba nuppudega saad vastata, edastada, arhiveerida või kustutada. Samuti saad kirju tärniga märkida või lisada värvisilte.", + "keywords_title": "Värvisildid", + "keywords_desc": "Korrasta oma e-kirju värvikoodiga siltide abil. Sildistamiseks lohista kiri sildile või tee kirjal paremklõps ja määra silt.", + "calendar_title": "Kalender", + "calendar_desc": "Lülitu kalendrile, et hallata oma sündmusi. Loo sündmusi, sea meeldetuletusi ning vaata päeva-, nädala- või kuuvaadet.", + "contacts_title": "Kontaktid", + "contacts_desc": "Siin asub sinu aadressiraamat. Impordi kontakte, loo gruppe ja klõpsa kontaktil, et näha kõiki üksikasju.", + "settings_title": "Seaded", + "settings_desc": "Kohanda kõike: teema, tihedus, allkirjad, filtrid, kiirklahvid, kalendri vaikeseaded ja palju muud.", + "shortcuts_title": "Kiirklahvid", + "shortcuts_desc": "Kogenud kasutajate lemmik. Vajuta igal ajal ?, et näha kõiki saadaolevaid kiirklahve. Saad liikuda, kirjutada ja hallata kirju ilma hiirt puudutamata.", + "compose_open_title": "Kirjakoostaja", + "compose_open_desc": "See on kirjakoostaja. Lisa saajaid, kirjuta oma sõnum, lisa manuseid ja kasuta rikastatud tekstivormingut. Samuti saad salvestada mustandeid ja kasutada malle.", + "calendar_view_title": "Sinu kalender", + "calendar_view_desc": "Siin on sinu kalender koos näidissündmustega. Tööriistariba abil saad vahetada päeva-, nädala-, kuu- ja päevakorravaadete vahel.", + "create_event_title": "Loo sündmus", + "create_event_desc": "Klõpsa sellel nupul uue kalendrisündmuse loomiseks. Saad määrata pealkirja, kuupäeva, kellaaja ja lisada osalejaid.", + "event_modal_title": "Sündmuse üksikasjad", + "event_modal_desc": "Siin on sündmuse vorm. Täida pealkiri, vali kuupäev ja kellaaeg, lisa asukoht või osalejad. Kui oled valmis, vajuta salvesta - või sulge vorm ja liigu edasi.", + "contacts_list_title": "Sinu kontaktid", + "contacts_list_desc": "Siin on sinu kontaktid. Klõpsa kontaktil, et näha selle täielikke andmeid paremal. Samuti saad luua uusi kontakte, importida vCarde või korraldada kontakte gruppidesse.", + "settings_tabs_title": "Seadete menüü", + "settings_tabs_desc": "Siin on kõik seadete kategooriad. Kohanda välimust, halda identiteete, seadista e-posti filtreid, kalendrit ja palju muud.", + "files_title": "Failihoidla", + "files_desc": "Failihalduriga saad faile üles laadida, korrastada ja jagada - nagu isiklik pilvekettas, mis on sinu e-postiga sisse ehitatud.", + "demo_banner_title": "Demorežiimi juhtnupud", + "demo_banner_desc": "Oled demorežiimis - kõik jääb sinu brauserisse. Vajuta igal ajal 'Lähtesta demo', et alustada puhta näidisandmestikuga otsast peale.", + "quota_title": "Mahu kasutus", + "quota_desc": "Jälgi siin oma postkasti suurust. Ring täitub, mida rohkem ruumi kasutad." + }, + "unified_mailbox": { + "search_unavailable": "Otsing pole ühendatud vaates saadaval" + }, + "quote_header": { + "reply_line": "{date}, {from} kirjutas:", + "forwarded_separator": "---------- Edastatud sõnum ----------", + "from_label": "Saatja", + "date_label": "Kuupäev", + "subject_label": "Teema" + }, + "pwa_install": { + "title": "Paigalda {appName}", + "description": "Paigalda meie rakendus kiireks ligipääsuks ja võrguühenduseta kasutamiseks.", + "not_now": "Mitte praegu", + "install": "Paigalda", + "dont_remind": "Ära tuleta enam meelde", + "dismiss_aria": "Sulge paigaldusteade" + }, + "deep_link": { + "message_not_found": "See sõnum ei ole enam saadaval.", + "thread_not_found": "See vestlus ei ole enam saadaval.", + "folder_not_found": "See kaust ei ole enam saadaval.", + "event_not_found": "See sündmus ei ole enam saadaval.", + "file_not_found": "See fail ei ole enam saadaval.", + "account_unavailable": "Selle lingi avamiseks logi sisse vastavasse kontosse.", + "copy_message": "Kopeeri link sõnumile", + "copy_conversation": "Kopeeri link vestlusele", + "copy_event": "Kopeeri link sündmusele", + "copied": "Link kopeeritud", + "copy_failed": "Linki ei õnnestunud kopeerida" + }, + "plugins": { + "oauth_callback": { + "completing": "Autoriseerimise lõpetamine…", + "completing_message": "Palun oota, kuni ühendus luuakse.", + "received_title": "Autoriseerimine õnnestus", + "received_message": "Võid naasta vahelehele, kust ühenduse alustasid.", + "failed_title": "Autoriseerimine ebaõnnestus", + "denied": "Autoriseerimisest keelduti.", + "failed_detail": "Autoriseerimine ebaõnnestus: {error}", + "missing_params": "Autoriseerimiskood või olek puudub.", + "delivery_failed": "Autoriseerimise tulemust ei õnnestunud rakenduse vahelehele edastada. Palun sulge see aken ja proovi uuesti." + }, + "settings": { + "title": "Pistikprogrammid", + "description": "Administraatori lisatud pistikprogrammid. Lülita need oma konto jaoks sisse või välja.", + "description_uploads": "Pistikprogrammid lisavad veebimeilile funktsioone. Lülita need oma konto jaoks sisse või välja või paigalda oma.", + "description_demo": "Pistikprogrammid pole demos saadaval.", + "install_button": "Paigalda pistikprogramm", + "install_hint": "Vali pistikprogrammi ZIP-fail.", + "install_hint_approval": "Vali pistikprogrammi ZIP-fail. Administraator peab selle heaks kiitma, enne kui saad selle sisse lülitada.", + "install_uploads_off": "Sinu administraator ei luba pistikprogramme paigaldada.", + "install_managed_id": "Sinu administraator pakub juba selle nimega pistikprogrammi, seega ei saa seda asendada.", + "install_invalid": "See fail ei ole paigaldatav pistikprogramm: {details}", + "toast_installed": "Pistikprogramm paigaldatud. Lülita see allpool sisse.", + "toast_installed_pending": "Pistikprogramm paigaldatud ja saadetud administraatorile heakskiitmiseks.", + "toast_enabled": "{name} on sees", + "toast_disabled": "{name} on väljas", + "toast_removed": "{name} eemaldati", + "toast_forced": "Sinu administraator hoiab {name} sisse lülitatuna", + "toast_approval_requested": "{name} ootab administraatori heakskiitu", + "empty_title": "Pistikprogramme pole veel", + "empty_admin": "Sinu administraator pole ühtegi pistikprogrammi lisanud.", + "empty_uploads": "Alustamiseks paigalda pistikprogramm ZIP-failist.", + "empty_demo": "Pistikprogrammid vajavad päris kontot, seega demos neid pole.", + "syncing": "Kontrollin pistikprogramme serveriga…", + "status_installed": "Väljas", + "status_enabled": "Käivitub", + "status_running": "Sees", + "status_disabled": "Väljas", + "status_error": "Probleem", + "badge_forced": "Kohustuslik", + "badge_managed": "Sinu administraatorilt", + "badge_yours": "Sinu paigaldatud", + "badge_awaiting": "Ootab heakskiitu", + "toggle_label": "Lülita {name} sisse või välja", + "forced_note": "Sinu administraator nõuab seda pistikprogrammi, seega ei saa seda välja lülitada.", + "permissions": "Mida see teha saab", + "permissions_managed": "Mida see teha saab (administraatori heaks kiidetud)", + "settings": "Seaded", + "remove": "Eemalda", + "remove_title": "Kas eemaldada {name}?", + "remove_message": "Pistikprogramm ja kõik, mida see selles brauseris salvestas, kustutatakse. Võid selle hiljem uuesti paigaldada.", + "remove_confirm": "Eemalda", + "error_user_plugins_off": "Sinu administraator lülitas kasutajate paigaldatud pistikprogrammid välja, seega see pistikprogramm ei tööta.", + "error_approval_unavailable": "See pistikprogramm paigaldati enne heakskiidu nõuet. Eemalda see ja paigalda uuesti, et saata see heakskiitmiseks.", + "error_approval_denied": "Sinu administraator ei kiitnud seda pistikprogrammi heaks.", + "error_approval_pending": "Ootan, et administraator selle pistikprogrammi heaks kiidaks.", + "error_auto_disabled": "Lülitati välja, kuna see jäi ebaõnnestuma. Lülita uuesti sisse, et proovida.", + "accounts_heading": "Ühendatud kontod", + "account_on": "{label} serveris {host}", + "account_waiting": "{label} serveris {host} (sisselogimine pole lõpetatud)", + "disconnect": "Katkesta ühendus", + "disconnect_failed": "Ühenduse katkestamine ebaõnnestus: {label}" + }, + "consent": { + "title": "Kas lubada pistikprogrammil {name} seda teha?", + "intro": "Pistikprogramm küsib allolevaid õigusi. See saab need ainult siis, kui lubad.", + "high_risk_note": "Märgitud üksused annavad pistikprogrammile juurdepääsu sinu kirjade sisule või võimaldavad tal andmeid mujale saata. Luba need ainult pistikprogrammidele, mida usaldad.", + "revoke_hint": "Võid pistikprogrammi igal ajal seadetes välja lülitada või eemaldada.", + "deny": "Ära luba", + "allow": "Luba" + }, + "permissions": { + "none": "See ei vaja erilisi õigusi.", + "high_risk": "Vajab ettevaatust", + "email_read": { + "title": "Loe sinu kirju", + "body": "Näha saatjaid, adressaate, teemasid ja iga sinu konto kirja täisteksti ning sinu kaustu." + }, + "email_write": { + "title": "Muuda ja korrasta sinu kirju", + "body": "Lisada või eemaldada silte, lippe ja muid märksõnu sinu kirjadelt, liigutada neid sinu kaustade vahel ja arhiivi ning salvestada mustandeid." + }, + "email_send": { + "title": "Muuda kirja enne saatmist", + "body": "Lugeda ja muuta sinu kirjutatavate kirjade adressaate, teemat, teksti ja manuseid ning takistada kirja saatmist." + }, + "crypto_full": { + "title": "Täielik juurdepääs sellele rakendusele", + "body": "Töötab samade õigustega kui veebimeil ise. See saab lugeda kogu sinu posti ja salvestatud võtmeid ning tegutseda sinu nimel. Luba see ainult pistikprogrammidele, mida täielikult usaldad." + }, + "email_raw_send": { + "title": "Saada ise koostatud kirju", + "body": "Saata sinu kontolt täielikke kirju, näiteks allkirjastatud või krüpteeritud." + }, + "email_blob_read": { + "title": "Loe kirjade toorsisu", + "body": "Lugeda manuseid ja kirjade täielikku lähteteksti, millega tegutsed, ning iga faili, mille lisad, enne selle saatmist." + }, + "email_blob_write": { + "title": "Asenda manuseid enne saatmist", + "body": "Laadida faile sinu meiliserverisse ja asendada sinu lisatud fail teisega enne kirja saatmist." + }, + "email_render_takeover": { + "title": "Muuda kirjade kuvamist", + "body": "Asendada sinu avatud kirjade ja kirjade nimekirja tekst, näiteks dekrüpteeritud sisu näitamiseks." + }, + "calendar_read": { + "title": "Loe sinu kalendrit", + "body": "Näha sinu kalendreid ja nende sündmusi." + }, + "calendar_write": { + "title": "Muuda sinu kalendrit", + "body": "Luua, muuta ja kustutada sündmusi." + }, + "contacts_read": { + "title": "Loe sinu kontakte", + "body": "Otsida ja lugeda sinu aadressiraamatuid." + }, + "contacts_write": { + "title": "Muuda sinu kontakte", + "body": "Luua, muuta ja kustutada kontakte ja aadressiraamatuid." + }, + "files_read": { + "title": "Loe sinu faile", + "body": "Sirvida ja avada sinu kontol hoitavaid faile." + }, + "files_write": { + "title": "Muuda sinu faile", + "body": "Laadida üles, ümber nimetada, liigutada ja kustutada faile sinu kontol." + }, + "identity_read": { + "title": "Vaata sinu saatjaaadresse", + "body": "Näha aadresse, millelt saadad, ja nende allkirju." + }, + "identity_write": { + "title": "Muuda sinu saatjaaadresse", + "body": "Luua, muuta ja kustutada saatjaaadresse ja allkirju." + }, + "filters_read": { + "title": "Loe sinu meilifiltreid", + "body": "Näha reegleid, mis sinu saabuvat posti sorteerivad." + }, + "filters_write": { + "title": "Muuda sinu meilifiltreid", + "body": "Lisada reegleid sinu serveripoolsetele filtritele. Sellised reeglid võivad saabuvat posti liigutada, edastada või kustutada." + }, + "tasks_read": { + "title": "Loe sinu ülesandeid", + "body": "Näha sinu ülesandenimekirju ja ülesandeid." + }, + "tasks_write": { + "title": "Muuda sinu ülesandeid", + "body": "Luua, muuta, lõpetada ja kustutada ülesandeid." + }, + "templates_read": { + "title": "Loe sinu malle", + "body": "Näha sinu salvestatud kirjamalle." + }, + "templates_write": { + "title": "Muuda sinu malle", + "body": "Luua, muuta ja kustutada kirjamalle." + }, + "smime_read": { + "title": "Vaata sinu S/MIME olekut", + "body": "Näha, kas sul on S/MIME võtmeid ja sertifikaate, ilma võtmetele juurdepääsuta." + }, + "vacation_read": { + "title": "Vaata sinu äraolekuteadet", + "body": "Näha, kas sinu automaatvastus on sees ja mida see ütleb." + }, + "vacation_write": { + "title": "Muuda sinu äraolekuteadet", + "body": "Lülitada sinu automaatvastus sisse või välja ning muuta selle teksti ja kuupäevi." + }, + "settings_read": { + "title": "Loe sinu seadeid", + "body": "Näha sinu eelistusi ja siltide definitsioone." + }, + "settings_write": { + "title": "Muuda sinu seadeid", + "body": "Lisada ja ümber järjestada silte ning muuta sinu eelistusi." + }, + "security_read": { + "title": "Vaata sinu konto turvalisust", + "body": "Näha, kas kaheastmeline sisselogimine ja krüpteerimine on sees, ilma saladustele juurdepääsuta." + }, + "auth_observe": { + "title": "Tea, millal sisse logid", + "body": "Saada teada, kui logid sisse, vahetad kontot või sinu seanssi uuendatakse." + }, + "auth_emit": { + "title": "Logi sind välja", + "body": "Logida sind veebimeilist välja." + }, + "account_read": { + "title": "Vaata sinu kontosid", + "body": "Näha sinu lisatud kontosid koos nende aadresside ja serveritega." + }, + "http_post": { + "title": "Kasuta veebimeili serverit sinu eest", + "body": "Kutsuda neid serveri lõpp-punkte sinu sisselogimisega:" + }, + "http_fetch": { + "title": "Ühendu teiste veebisaitidega", + "body": "Saata päringuid neile saitidele. Kõik, mida pistikprogramm lugeda saab, võidakse sinna saata:" + }, + "admin_config": { + "title": "Loe oma seadeid administraatorilt", + "body": "Lugeda seadeid, mille administraator selle pistikprogrammi jaoks salvestas." + }, + "ui_observe": { + "title": "Jälgi, kus rakenduses oled", + "body": "Teada, milline leht on avatud, ja reageerida linkidele, mida klõpsad." + }, + "ui_toolbar": { + "title": "Lisa tööriistaribadele nuppe", + "body": "Näidata oma nuppe kirja tööriistaribal, avatud kirjal ja ülaribal." + }, + "ui_app_top_banner": { + "title": "Näita ülaservas bännerit", + "body": "Näidata rakenduse ülaservas üle kogu laiuse teadet." + }, + "ui_email_banner": { + "title": "Näita kirjadel teateid", + "body": "Näidata oma teateid ja kokkuvõtteid avatud kirja või vestluse osade kohal, sees ja vahel." + }, + "ui_email_footer": { + "title": "Lisa sisu kirjade alla", + "body": "Näidata oma sisu avatud kirja all." + }, + "ui_contact_cryptokeys": { + "title": "Näita kontaktide krüptovõtmeid", + "body": "Näidata ja hallata kontakti krüptovõtmeid." + }, + "ui_email_details": { + "title": "Lisa kirja üksikasjadele paneele", + "body": "Näidata oma paneeli avatud kirja kõrval või all." + }, + "ui_download_file": { + "title": "Salvesta faile sinu seadmesse", + "body": "Pakkuda allalaadimiseks enda loodud faile." + }, + "ui_message_list_tabs": { + "title": "Lisa saabunud kirjade kausta kaardid", + "body": "Sorteerida sinu saabunud kirjad kaartidesse, nagu Uuendused või Pakkumised." + }, + "ui_composer_toolbar": { + "title": "Lisa kirjakoostajasse", + "body": "Näidata oma nuppe, menüükirjeid ja jaotisi kirja kirjutamise ajal." + }, + "ui_composer_sidebar": { + "title": "Lisa kirjakoostajasse paneel", + "body": "Näidata oma paneeli sinu kirjutatava kirja kõrval." + }, + "ui_sidebar_widget": { + "title": "Lisa külgribale", + "body": "Näidata oma kaste ja jaotisi meili külgribal." + }, + "ui_settings_section": { + "title": "Lisa seadete leht", + "body": "Näidata oma lehte seadetes." + }, + "ui_context_menu": { + "title": "Lisa kirjamenüüsse", + "body": "Lisada kirjeid menüüsse, mis avaneb kirjal hiireparema klõpsamisel." + }, + "ui_navigation_rail": { + "title": "Lisa navigeerimisribale", + "body": "Näidata oma nuppu navigeerimisribal." + }, + "ui_keyboard": { + "title": "Lisa kiirklahve", + "body": "Reageerida enda määratud kiirklahvidele." + }, + "ui_calendar_action": { + "title": "Lisa kalendrisse", + "body": "Näidata oma nuppe ja jaotisi sündmustel ning lühikesi silte ja punkte kalendripäevadel." + }, + "ui_admin_page": { + "title": "Lisa administraatori leht", + "body": "Näidata oma lehte administraatori seadetes." + }, + "app_lifecycle": { + "title": "Tea, millal rakendus käivitub ja peatub", + "body": "Saada teada, kui rakendus käivitub, läheb võrgust välja või sa logid välja." + }, + "ui_message_list": { + "title": "Lisa kirjade nimekirja", + "body": "Näidata oma sisu kirjade nimekirja kohal ning nuppe kirjadel ja valiku tööriistaribal." + }, + "ui_contacts": { + "title": "Lisa kontaktidesse", + "body": "Näidata oma jaotisi kontakti üksikasjades ja nuppe kontaktide nimekirjas." + }, + "ui_tasks": { + "title": "Lisa ülesannetesse", + "body": "Näidata oma jaotisi ülesande üksikasjades ja nuppe ülesannete nimekirjas." + }, + "ui_files": { + "title": "Lisa failidesse", + "body": "Näidata oma jaotisi faili eelvaates ja kirjeid faili menüüs." + }, + "ui_commands": { + "title": "Lisa käske", + "body": "Lisada oma kirjeid käsupaletti (Ctrl+K)." + }, + "ui_app_page": { + "title": "Lisa lehti", + "body": "Lisada rakendusse oma lehti, igaühel nupp navigeerimisribal." + }, + "http_proxy": { + "title": "Kasuta sinu ühendatud kontosid", + "body": "Saata päringuid läbi Bulwarki serveri kontoga või võtmega, mille sina või sinu administraator seadistas. Pistikprogramm ei näe kunagi parooli, kuid saab teha kõike, mida see konto oma serveris teha saab." + }, + "compose_open": { + "title": "Ava kirjakoostaja sinu eest", + "body": "Avada uus kiri täidetud adressaatide, teksti või manustega. Sina otsustad ikka, kas saata." + }, + "compose_read": { + "title": "Loe kirja, mida kirjutad", + "body": "Lugeda kirja teksti selle kirjutamise ajal, kui kasutad pistikprogrammi kirjakoostajas. Mitte allkirja ega tsiteeritud kirja." + }, + "compose_write": { + "title": "Muuda kirja, mida kirjutad", + "body": "Asendada või lisada teksti kirjas selle kirjutamise ajal, kui kasutad pistikprogrammi kirjakoostajas. Iga muudatuse saad tagasi võtta ja sina otsustad ikka, kas saata." + }, + "ui_notifications": { + "title": "Näita märguandeid", + "body": "Näidata märguandeid sinu seadmes või rakenduses, kui pole neid lubanud. Iga märguanne nimetab pistikprogrammi." + } + }, + "dialog": { + "from_plugin": "Pistikprogrammilt {name}", + "ok": "OK", + "submit": "Saada", + "confirm": "Kinnita", + "cancel": "Tühista", + "close": "Sulge", + "turn_off": "Lülita {name} välja", + "plugin_menu": "Valikud: {name}", + "password_note": "See vorm pärineb pistikprogrammilt {name}, mitte veebimeililt. Sisesta parool ainult siis, kui usaldad seda pistikprogrammi.", + "toast_auto_disabled": "{name} lülitati välja, kuna see jäi ebaõnnestuma." + }, + "connect": { + "title": "Ühenda {label}", + "intro_password": "{name} soovib kasutada sinu kontot {label}. Bulwark hoiab parooli oma serveris ja saadab selle ainult hostile {host}. Pistikprogramm ei näe seda kunagi.", + "intro_password_any": "{name} soovib kasutada sinu kontot {label}. Bulwark hoiab parooli oma serveris ja saadab selle ainult serverisse, mille siia sisestad. Pistikprogramm ei näe seda kunagi.", + "intro_signin": "{name} soovib sinu kontole {label} sisse logida. Sisesta siia server ja logi siis sisse selle enda lehel. Bulwark hoiab saadud juurdepääsu oma serveris; pistikprogramm ei näe seda kunagi.", + "server": "Serveri aadress", + "server_hint": "cloud.example.com", + "username": "Kasutajanimi", + "secret": "Parool või võti", + "connect": "Ühenda", + "continue": "Jätka", + "cancel": "Tühista", + "saving": "Salvestamine…", + "error_server": "Sisesta serveri aadress, näiteks https://cloud.example.com", + "error_save": "Salvestamine ebaõnnestus: {error}" + } + }, + "global_search": { + "title": "Otsi kõike", + "placeholder": "Otsi e-kirju, kontakte, kalendrit, faile...", + "scope_label": "Otsi kohast", + "account_label": "Konto", + "scope_all": "Kõik", + "scope_mail": "E-post", + "scope_contacts": "Kontaktid", + "scope_calendar": "Kalender", + "scope_files": "Failid", + "all_accounts": "Kõik kontod", + "show_all": "Näita kõiki tulemusi", + "no_results": "Päringule „{query}“ tulemusi ei leitud", + "server_hint": "Täissõnad otsivad serverist; osalised sõnad vastavad juba laaditud kirjetele", + "searching": "Otsimine...", + "account_error": "Kontost {account} otsimine ebaõnnestus", + "recent_searches": "Hiljutised otsingud", + "recurring": "Korduv", + "no_subject": "(teema puudub)", + "empty_hint": "Otsi e-kirju, kontakte, sündmusi ja faile kõigist kontodest. Proovi from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Kuupäeva järgi", + "grouped": "Rühmitatud", + "load_more": "Laadi rohkem", + "preview_empty": "Vali tulemus eelvaate nägemiseks", + "open_full": "Ava", + "view_label": "Vaade" + }, + "top_bar": { + "more": "Rohkem valikuid" + }, + "plugin_pages": { + "loading": "Lehe laadimine", + "not_available_title": "See leht pole saadaval", + "not_available_description": "Pistikprogramm, mis selle lisas, on välja lülitatud või enam paigaldamata.", + "back_to_mail": "Tagasi meili juurde", + "settings_group": "Pistikprogrammid" + }, + "command_palette": { + "title": "Käsupalett", + "open": "Ava käsupalett", + "placeholder": "Trüki käsk", + "no_results": "Sobivaid käske pole", + "go_to": "Mine: {name}", + "compose": "Uus kiri", + "search": "Otsi kirju", + "theme_dark": "Lülitu tumedale teemale", + "theme_light": "Lülitu heledale teemale", + "shortcuts": "Näita kiirklahve", + "group_navigation": "Mine", + "group_actions": "Toimingud", + "group_plugins": "Pistikprogrammid" + }, + "notification_center": { + "title": "Märguanded", + "mark_all_read": "Märgi kõik loetuks", + "clear": "Tühjenda", + "empty": "Märguandeid pole", + "close": "Sulge", + "unread": "Lugemata", + "unread_count": "{count} lugemata", + "level_error": "Viga", + "level_warning": "Hoiatus" + } +} diff --git a/locales/fa/common.json b/locales/fa/common.json index aa63e3f3..189db08c 100644 --- a/locales/fa/common.json +++ b/locales/fa/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "سرویس ایمیل تحت وب مینیمال با پروتکل JMAP", - "login": { - "title": "ایمیل تحت وب", - "username_label": "ایمیل", - "username_placeholder": "user@example.com", - "password_label": "رمز عبور", - "password_placeholder": "رمز عبور خود را وارد کنید", - "jmap_endpoint_label": "سرور JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "سرور باید درخواست‌های CORS از این دامنه را مجاز کند.", - "jmap_server_label": "سرور", - "jmap_server_auto_picked": "سرور از دامنه ایمیل شما انتخاب شد.", - "sign_in": "ورود", - "signing_in": "در حال ورود...", - "loading": "در حال بارگذاری...", - "reconnecting": "ارتباط قطع شد. در حال تلاش برای اتصال مجدد…", - "error": { - "invalid_credentials": "ایمیل یا رمز عبور نامعتبر است. اطلاعات خود را بررسی و دوباره تلاش کنید.", - "connection_failed": "امکان اتصال به سرور وجود ندارد. اتصال اینترنت خود را بررسی کنید.", - "cors_blocked": "سرور در دسترس است اما درخواست‌های cross-origin را مسدود می‌کند.", - "server_error": "سرور موقتاً در دسترس نیست. لطفاً بعداً تلاش کنید.", - "generic": "خطای غیرمنتظره‌ای رخ داد. اگر ادامه داشت با مدیر سیستم تماس بگیرید.", - "totp_required": "کد احراز هویت دو مرحله‌ای لازم است.", - "totp_invalid": "کد احراز هویت نامعتبر است. برنامه authenticator خود را بررسی کنید.", - "oauth_discovery_failed": "ورود یکپارچه فعال است اما ارائه‌دهنده هویت در دسترس نیست." - }, - "show_password": "نمایش رمز عبور", - "hide_password": "مخفی کردن رمز عبور", - "totp_toggle": "کد احراز هویت دو مرحله‌ای دارم", - "remember_me": "مرا به خاطر بسپار", - "config_error": { - "title": "خطای پیکربندی", - "fetch_failed": "بارگذاری پیکربندی برنامه ممکن نیست.", - "server_not_configured": "سرور ایمیل پیکربندی نشده است. با مدیر سیستم تماس بگیرید." - }, - "remove_from_history": "حذف از تاریخچه", - "totp_label": "کد احراز هویت", - "totp_placeholder": "000000", - "session_expired": "نشست شما منقضی شده است. لطفاً دوباره وارد شوید.", - "dismiss": "رد کردن", - "or": "یا", - "sign_in_sso": "ورود با SSO", - "add_account_title": "افزودن حساب", - "add_account_subtitle": "ورود با حساب دیگر", - "cancel": "انصراف", - "website": "وبسایت", - "imprint": "اطلاعات حقوقی", - "privacy_policy": "حریم خصوصی", - "try_demo": "نسخه آزمایشی", - "demo_description": "کاوش با داده‌های نمونه - بدون نیاز به حساب", - "demo_launching": "در حال راه‌اندازی نسخه آزمایشی...", - "demo_login_button": "راه‌اندازی نسخه آزمایشی", - "demo_tagline": "تجربه یک سرویس ایمیل کامل. بدون نیاز به حساب کاربری.", - "demo_no_signup": "نیازی به ثبت‌نام نیست - آزادانه با داده‌های نمونه کاوش کنید", - "oauth_completing": "در حال تکمیل ورود...", - "oauth_error": { - "title": "احراز هویت ناموفق", - "invalid_state": "اعتبارسنجی امنیتی ناموفق بود. لطفاً دوباره وارد شوید.", - "missing_params": "اطلاعات مجوزدهی ناقص است. لطفاً دوباره وارد شوید.", - "token_exchange_failed": "تکمیل احراز هویت ناموفق بود.", - "access_denied": "دسترسی رد شد. با مدیر سیستم تماس بگیرید.", - "back_to_login": "بازگشت به صفحه ورود" - } - }, - "sidebar": { - "close": "بستن", - "compose": "ایمیل جدید", - "compose_hint": "ایمیل جدید (c)", - "search_placeholder": "جستجوی ایمیل...", - "search_placeholder_hint": "جستجوی ایمیل... (کلید /)", - "storage": "فضای ذخیره‌سازی", - "storage_used": "استفاده شده", - "storage_free": "آزاد", - "storage_total": "کل", - "sign_out": "خروج", - "sign_out_of": "خروج از {account}", - "sign_out_all": "خروج از همه حساب‌ها", - "add_account": "افزودن حساب", - "set_as_default": "تنظیم به عنوان پیش‌فرض", - "switch_account": "تعویض حساب", - "contacts": "مخاطبین", - "calendar": "تقویم", - "settings": "تنظیمات", - "admin": "مدیریت", - "files": "فایل‌ها", - "loading_mailboxes": "در حال بارگذاری صندوق‌های پستی...", - "push_connected": "به‌روزرسانی‌های بلادرنگ فعال", - "push_disconnected": "به‌روزرسانی‌های بلادرنگ غیرفعال", - "keyboard_shortcuts": "میانبرهای صفحه کلید", - "theme": { - "light": "حالت روشن", - "dark": "حالت تاریک", - "system": "تم سیستم" - }, - "language": { - "title": "زبان" - }, - "mailboxes": { - "inbox": "صندوق ورودی", - "sent": "ارسال شده", - "drafts": "پیش‌نویس‌ها", - "trash": "زباله‌دان", - "archive": "بایگانی", - "starred": "ستاره‌دار", - "all_mail": "همه ایمیل‌ها", - "spam": "هرزنامه", - "important": "مهم" - }, - "unified_inbox": "صندوق ورودی یکپارچه", - "unified_sent": "همه ارسال‌ها", - "unified_drafts": "همه پیش‌نویس‌ها", - "unified_trash": "همه زباله‌دان‌ها", - "unified_archive": "همه بایگانی‌ها", - "unified_junk": "همه هرزنامه‌ها", - "all_accounts": "همه حساب‌ها", - "unified_mailbox": "صندوق پستی یکپارچه", - "expand": "باز کردن", - "collapse": "جمع کردن", - "expand_tooltip": "باز کردن", - "collapse_tooltip": "جمع کردن", - "mobile": { - "search": "جستجو", - "compose": "ایمیل جدید", - "go_back": "بازگشت", - "toggle_menu": "تغییر وضعیت منو" - }, - "clear_search": "پاک کردن جستجو", - "vacation_active": "پاسخ‌گوی خودکار فعال است", - "demo_banner": "حالت آزمایشی", - "demo_reset": "بازنشانی", - "demo_tour": "تور", - "tags": "برچسب‌ها", - "show_all_tags": "نمایش همه ({count})", - "show_fewer_tags": "نمایش کمتر", - "folders": "پوشه‌ها", - "shared": "اشتراکی", - "mail": "ایمیل", - "nav_label": "ناوبری", - "add_app": "برنامه‌ها", - "scheduled": "زمان‌بندی شده", - "unified_all_mail": "همه ایمیل‌ها", - "unified_all_starred": "همه ستاره‌دارها", - "unified_all_unread": "همه خوانده‌نشده‌ها", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "برنامه‌های پیش‌فرض", - "description": "انتخاب کنید لینک‌های ایمیل و تقویم در Bulwark باز شوند.", - "unsupported": "این مرورگر از ثبت دستی پروتکل پشتیبانی نمی‌کند.", - "mailto_label": "لینک‌های ایمیل", - "mailto_description": "باز کردن لینک‌های mailto: در Bulwark", - "protocol_open_mode_label": "هنگام باز کردن لینک‌های پروتکل", - "protocol_open_mode_description": "انتخاب کنید لینک‌ها در تب جدید باز شوند یا نشست فعال.", - "protocol_open_mode_active_session": "باز کردن در نشست فعال", - "protocol_open_mode_new_tab": "همیشه در تب جدید", - "focus_notification_title": "باز کردن Bulwark", - "focus_notification_body": "لینک در Bulwark باز شد.", - "webcal_label": "لینک‌های تقویم", - "webcal_description": "باز کردن لینک‌های webcal: در Bulwark", - "register_mailto": "ثبت برنامه ایمیل", - "register_webcal": "ثبت برنامه تقویم", - "mailto_registered": "ثبت مدیریت ایمیل درخواست شد", - "webcal_registered": "ثبت مدیریت تقویم درخواست شد", - "registration_failed": "ثبت پروتکل ناموفق بود", - "opening_mailto": "در حال باز کردن ویرایشگر...", - "opening_webcal": "در حال باز کردن تقویم...", - "browser_note": "مرورگر یا سیستم عامل ممکن است تأیید شما را بخواهد.", - "select_account_title": "انتخاب حساب", - "select_mailto_account": "انتخاب کنید کدام حساب این لینک ایمیل را باز کند.", - "select_webcal_account": "انتخاب کنید کدام حساب این لینک تقویم را باز کند.", - "select_account_note": "فقط برای این لینک پروتکل اعمال می‌شود.", - "detail_to": "به", - "detail_subject": "موضوع", - "detail_no_subject": "بدون موضوع", - "detail_calendar": "تقویم", - "detail_source": "منبع", - "active_account": "فعال", - "switching_account": "در حال تعویض حساب..." - }, - "sidebar_apps": { - "modal_title": "برنامه‌های نوار کناری", - "add_new": "افزودن برنامه", - "edit_app": "ویرایش برنامه", - "name_label": "نام", - "name_placeholder": "برنامه من", - "name_required": "نام الزامی است", - "url_label": "آدرس", - "url_required": "آدرس الزامی است", - "url_invalid": "آدرس http یا https معتبر وارد کنید", - "icon_label": "آیکون", - "icon_required": "آیکون الزامی است", - "open_mode_label": "حالت باز کردن", - "open_new_tab": "تب جدید", - "open_inline": "داخلی", - "cancel": "انصراف", - "add": "افزودن", - "update": "به‌روزرسانی", - "delete": "حذف", - "delete_confirm_title": "حذف برنامه", - "delete_confirm": "آیا از حذف \"{name}\" اطمینان دارید؟", - "no_apps": "هنوز برنامه‌ای اضافه نشده", - "no_apps_hint": "برنامه‌ها و لینک‌های دلخواه را به نوار کناری اضافه کنید", - "managed_title": "ارائه‌شده توسط مدیر شما", - "managed_description": "این برنامه‌ها برای همه تنظیم شده‌اند و قابل تغییر نیستند.", - "managed_badge": "مدیریت‌شده", - "search_icons": "جستجوی آیکون‌ها...", - "show_popular": "محبوب", - "show_all": "همه", - "no_icons_found": "آیکونی یافت نشد", - "inline_badge": "داخلی", - "tab_badge": "تب", - "show_on_mobile": "نمایش در موبایل" - }, - "email_list": { - "no_emails": "پیامی یافت نشد", - "no_emails_description": "این صندوق پستی خالی است", - "no_search_results": "نتیجه‌ای یافت نشد", - "no_search_results_description": "جستجو یا فیلترهای خود را تنظیم کنید", - "loading": "در حال بارگذاری ایمیل‌ها...", - "unread": "خوانده نشده", - "to_me": "به من", - "to_recipients": "به {count} گیرنده", - "and_others": "و {count} نفر دیگر", - "draft": "پیش‌نویس", - "starred": "ستاره‌دار", - "pinned": "سنجاق‌شده", - "replied": "پاسخ داده شده", - "forwarded": "بازارسال شده", - "has_attachment": "دارای پیوست", - "conversations_count": "{count} از {total} مکالمه", - "conversations_count_plus": "+{count} مکالمه", - "conversations_count_simple": "{count} مکالمه", - "no_conversations": "مکالمه‌ای نیست", - "loading_more": "در حال بارگذاری بیشتر...", - "no_more_emails": "ایمیل بیشتری برای بارگذاری نیست", - "batch_actions": { - "select": "انتخاب ایمیل‌ها", - "select_all": "انتخاب همه", - "selected_messages": "{count, plural, one {1 ایمیل} other {# ایمیل}} انتخاب شده", - "mark_read": "علامت‌گذاری به عنوان خوانده شده", - "mark_unread": "علامت‌گذاری به عنوان خوانده نشده", - "delete": "حذف", - "delete_confirm_title": "حذف ایمیل‌ها", - "delete_confirm_message": "آیا از حذف {count, plural, one {1 ایمیل} other {# ایمیل}} اطمینان دارید؟", - "clear_selection": "پاک کردن انتخاب" - }, - "permanent_delete": "حذف دائمی", - "permanent_delete_confirm_title": "حذف دائمی", - "permanent_delete_confirm_message": "این ایمیل برای همیشه حذف خواهد شد. این عملیات قابل بازگشت نیست.", - "permanent_delete_confirm_batch_message": "این {count, plural, one {1 ایمیل} other {# ایمیل}} برای همیشه حذف خواهد شد.", - "empty_folder": { - "button": "خالی کردن پوشه", - "confirm_title": "خالی کردن پوشه", - "confirm_message": "همه ایمیل‌های این پوشه برای همیشه حذف خواهند شد.", - "confirm_button": "خالی کردن پوشه", - "junk_hint": "می‌توانید پوشه هرزنامه را خالی کنید.", - "trash_hint": "می‌توانید پوشه زباله‌دان را خالی کنید." - }, - "no_scheduled_emails": "ایمیل زمان‌بندی شده‌ای نیست", - "no_scheduled_emails_description": "پیام‌های زمان‌بندی شده برای بعد اینجا نمایش داده می‌شوند.", - "scheduled_count": "{count, plural, one {1 ایمیل زمان‌بندی شده} other {# ایمیل زمان‌بندی شده}}", - "scheduled_actions_hint": "پیام‌های زمان‌بندی شده را می‌توان لغو یا ویرایش کرد.", - "cancel_scheduled_send": "لغو ارسال", - "reschedule_send": "باززمان‌بندی", - "cancel_and_edit": "لغو و ویرایش", - "cancel_and_compose_again": "لغو و نوشتن مجدد", - "reschedule_prompt": "تاریخ/زمان جدید را وارد کنید" - }, - "email_viewer": { - "read_receipt": { - "prompt": "فرستنده درخواست تأیید خواندن کرده است:", - "send": "ارسال تأیید", - "ignore": "نادیده گرفتن", - "sent": "تأیید خواندن ارسال شد.", - "send_failed": "تأیید خواندن ارسال نشد", - "mdn_subject": "خوانده شد: {subject}", - "mdn_body": "این یک رسید برای پیام ارسالی به {recipient} است." - }, - "no_email_selected": "ایمیلی انتخاب نشده", - "no_email_description": "یک ایمیل از لیست انتخاب کنید", - "no_conversation_selected": "مکالمه‌ای انتخاب نشده", - "no_conversation_description": "یک مکالمه از لیست انتخاب کنید", - "compose": "ایمیل جدید", - "compose_hint": "نوشتن پیام جدید", - "no_subject": "(بدون موضوع)", - "no_body_content": "(محتوایی موجود نیست)", - "no_preview_available": "پیش‌نمایشی در دسترس نیست", - "show_quoted_text": "نمایش متن نقل‌قول‌شده", - "hide_quoted_text": "پنهان کردن متن نقل‌قول‌شده", - "loading_email": "در حال بارگذاری ایمیل...", - "loading": "در حال بارگذاری...", - "reply": "پاسخ", - "reply_all": "پاسخ به همه", - "forward": "ارسال مجدد", - "delete": "حذف", - "archive": "بایگانی", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "ستاره‌دار کردن", - "unstar": "حذف ستاره", - "mark_unread": "علامت‌گذاری خوانده نشده", - "mark_read": "علامت‌گذاری خوانده شده", - "unread": "خوانده نشده", - "read": "خوانده شده", - "spam_short": "هرزنامه", - "not_spam_short": "هرزنامه نیست", - "move": "انتقال", - "print": "چاپ", - "view_source": "مشاهده منبع", - "fullscreen": "تمام‌صفحه", - "exit_fullscreen": "خروج از تمام‌صفحه", - "export_email": "خروجی .eml", - "forward_as_attachment": "ارسال به‌عنوان پیوست", - "import_email": "وارد کردن .eml یا .zip", - "keyboard_shortcuts": "میانبرهای صفحه کلید (?)", - "email_source": "منبع ایمیل", - "draft_banner": "این پیام یک پیش‌نویس است", - "edit_draft": "ویرایش", - "copy_source": "کپی در کلیپ‌بورد", - "source_copied": "منبع کپی شد", - "attachments": "پیوست‌ها", - "important": "مهم", - "download": "دانلود", - "download_all": "دانلود همه", - "from": "از", - "to": "به", - "cc": "رونوشت", - "bcc": "رونوشت مخفی", - "date": "تاریخ", - "subject": "موضوع", - "show_details": "نمایش جزئیات", - "hide_details": "مخفی کردن جزئیات", - "external_content_warning": "تصاویر و محتوای خارجی مسدود شده‌اند", - "load_external_content": "بارگذاری تصاویر", - "trust_sender": "همیشه به این فرستنده اعتماد کن", - "back_to_list": "بازگشت به لیست", - "view_contact": "مشاهده مخاطب", - "message_details": "جزئیات پیام", - "more_reply_options": "گزینه‌های بیشتر پاسخ", - "set_tag": "تنظیم برچسب", - "tag": "برچسب", - "more_actions": "عملیات بیشتر", - "previous": "قبلی", - "next": "بعدی", - "move_to": "انتقال به...", - "remove_tag": "حذف برچسب", - "tag_filter_placeholder": "فیلتر برچسب‌ها", - "tag_no_matches": "برچسب مطابقی یافت نشد", - "more_count": "+{count} بیشتر", - "characters_count": "{count} کاراکتر", - "quick_reply_placeholder": "پاسخ سریع بنویسید...", - "more_options": "گزینه‌های بیشتر", - "sending": "در حال ارسال...", - "security_authentication": "امنیت و احراز هویت", - "technical_details": "جزئیات فنی", - "message_id_label": "شناسه پیام:", - "reply_to_label": "پاسخ به:", - "delivery_time_label": "زمان تحویل:", - "conversation_part_label": "بخشی از مکالمه:", - "previous_messages": "{count} پیام قبلی", - "previous_messages_plural": "{count} پیام قبلی", - "time": { - "day": "روز", - "days": "روز", - "hour": "ساعت", - "hours": "ساعت", - "minute": "دقیقه", - "minutes": "دقیقه" - }, - "unknown_sender": "ناشناس", - "recipient_me": "من", - "recipient_and_others": "{name} و {count} نفر دیگر", - "recipient_to_prefix": "به:", - "authentication": { - "title": "احراز هویت", - "status": { - "verified": "تأیید شده", - "warning": "هشدار", - "none": "احراز هویت نشده" - }, - "spf": { - "pass": "SPF تأیید شد", - "fail": "SPF ناموفق", - "none": "SPF ندارد" - }, - "dkim": { - "pass": "DKIM معتبر", - "fail": "DKIM نامعتبر", - "none": "DKIM ندارد" - }, - "dmarc": { - "pass": "DMARC تأیید شد", - "fail": "DMARC ناموفق", - "none": "DMARC ندارد" - }, - "spam_score": "امتیاز هرزنامه", - "tooltip_spf": "SPF: تأیید مجاز بودن سرور ارسال‌کننده", - "tooltip_dkim": "DKIM: تأیید عدم تغییر ایمیل در انتقال", - "tooltip_dmarc": "DMARC: هماهنگی SPF و DKIM با دامنه فرستنده", - "policy": "سیاست", - "result": { - "pass": "موفق", - "fail": "ناموفق", - "softfail": "ناموفق نسبی", - "neutral": "خنثی", - "permerror": "خطای دائمی", - "temperror": "خطای موقت", - "none": "ندارد" - } - }, - "details": { - "recipients_routing": "گیرندگان و مسیریابی", - "authentication_security": "احراز هویت و امنیت", - "identifiers_threading": "شناسه‌ها و رشته‌بندی", - "mailing_list": "لیست پستی", - "message_properties": "ویژگی‌های پیام", - "sent": "ارسال شده", - "received": "دریافت شده", - "delivery_time": "زمان تحویل", - "in_reply_to": "در پاسخ به", - "references": "منابع", - "thread_id": "شناسه رشته", - "size": "حجم", - "mime_type": "نوع MIME", - "attachments_summary": "{count} فایل · {size}", - "list_id": "شناسه لیست", - "list_help": "راهنمای لیست", - "list_post": "ارسال به لیست", - "list_unsubscribe": "لغو اشتراک", - "iprev": "DNS معکوس", - "spam_status": "وضعیت هرزنامه", - "ai_verdict": "نظر هوش مصنوعی", - "account": "حساب", - "no_subject": "(بدون موضوع)" - }, - "headers": { - "routing": "مسیریابی", - "received": "دریافت شده", - "message_id": "شناسه پیام", - "list_info": "اطلاعات لیست" - }, - "tooltips": { - "reply": "پاسخ (r)", - "reply_all": "پاسخ به همه (a)", - "forward": "ارسال مجدد (f)", - "archive": "بایگانی (e)", - "delete": "حذف (# یا Del)", - "star": "ستاره‌دار (s)", - "unstar": "حذف ستاره (s)", - "compose": "ایمیل جدید (c)", - "previous": "ایمیل قبلی", - "next": "ایمیل بعدی", - "edit_draft": "ویرایش پیش‌نویس" - }, - "spam": { - "button_title": "گزارش هرزنامه", - "not_spam_title": "علامت‌گذاری به عنوان قانونی", - "toast_success": "به هرزنامه منتقل شد", - "toast_batch": "{count} ایمیل به هرزنامه منتقل شد", - "toast_undo": "بازگردانی", - "toast_not_spam_success": "به صندوق ورودی منتقل شد", - "toast_not_spam_batch": "{count} ایمیل به صندوق ورودی منتقل شد", - "error": "گزارش هرزنامه ناموفق بود", - "error_not_spam": "بازگردانی ایمیل ناموفق بود" - }, - "unsubscribe_banner": { - "label": "خبرنامه", - "button": "لغو اشتراک", - "confirm_title": "لغو اشتراک از این فرستنده؟", - "confirm_button": "تأیید", - "cancel": "انصراف", - "success_http": "صفحه لغو اشتراک در تب جدید باز شد", - "success_mailto": "ایمیل لغو اشتراک ارسال شد", - "confirm_message_http": "صفحه لغو اشتراک در برگه جدیدی باز خواهد شد.", - "confirm_message_mailto": "ایمیل لغو اشتراک برای فرستنده ارسال خواهد شد.", - "error": "لغو اشتراک ممکن نیست", - "dismiss": "رد کردن" - }, - "calendar_invitation": { - "loading": "در حال بارگذاری جزئیات رویداد…", - "title": "دعوت‌نامه تقویم", - "published_title": "رویداد منتشر شده", - "response_title": "پاسخ رویداد", - "update_title": "به‌روزرسانی رویداد", - "counter_title": "پیشنهاد متقابل", - "refresh_title": "درخواست به‌روزرسانی", - "declined_counter_title": "پیشنهاد متقابل رد شد", - "cancelled_title": "رویداد لغو شد", - "organizer": "برگزارکننده: {name}", - "organizer_label": "برگزارکننده", - "attendees": "{count, plural, one {# شرکت‌کننده} other {# شرکت‌کننده}}", - "accept": "پذیرفتن", - "maybe": "شاید", - "decline": "رد کردن", - "add_to_calendar": "افزودن به تقویم", - "added": "به تقویم اضافه شد", - "rsvp_sent": "پاسخ ارسال شد", - "parse_error": "خواندن دعوت‌نامه ممکن نیست", - "action_failed": "عملیات تقویم انجام نشد.", - "no_calendar": "تقویم در دسترس نیست", - "published_info": "این رویداد برای اطلاع‌رسانی به اشتراک گذاشته شده.", - "response_info": "این پیام شامل پاسخ یک شرکت‌کننده است.", - "response_info_organizer": "پاسخ شرکت‌کننده رویداد شما را به‌روزرسانی می‌کند.", - "update_info": "این پیام یک رویداد موجود را به‌روزرسانی می‌کند.", - "counter_info": "این پیام تغییراتی در رویداد پیشنهاد می‌دهد.", - "counter_info_organizer": "شرکت‌کننده تغییراتی در رویداد شما پیشنهاد داده.", - "refresh_info": "این پیام آخرین جزئیات رویداد را درخواست می‌کند.", - "refresh_info_organizer": "شرکت‌کننده آخرین جزئیات رویداد را درخواست کرده.", - "declined_counter_info": "برگزارکننده پیشنهاد متقابل را رد کرد.", - "authentication_failed_info": "بررسی احراز هویت ایمیل ناموفق بود. با احتیاط عمل کنید.", - "authentication_missing_info": "این دعوت‌نامه فاقد احراز هویت ایمیل است.", - "sender_mismatch_info": "این دعوت‌نامه از {sender} ارسال شده ولی برگزارکننده {organizer} است.", - "sender_mismatch_unverified_info": "از {sender} ارسال شده ولی برگزارکننده {organizer} است و پیام تأیید نشد.", - "organizer_role": "شما برگزارکننده این رویداد هستید", - "your_response": "پاسخ شما: {status}", - "response_needed": "نیاز به پاسخ", - "response_accepted": "پذیرفته شد", - "response_tentative": "احتمالی", - "response_declined": "رد شد", - "response_delegated": "واگذار شد", - "actor_sent_info": "ارسال شده توسط {name}.", - "actor_response_info": "{name} پاسخ داد: {status}.", - "actor_counter_info": "{name} تغییراتی پیشنهاد داد.", - "actor_refresh_info": "{name} آخرین جزئیات را درخواست کرد.", - "actor_declined_counter_info": "{name} پیشنهاد متقابل را رد کرد.", - "actor_note": "یادداشت: {comment}", - "actor_unknown": "شخصی", - "proposed_changes": "تغییرات پیشنهادی", - "change_title": "عنوان", - "change_time": "زمان", - "change_location": "مکان", - "change_description": "توضیحات", - "change_empty": "هیچ", - "change_from_to": "{before} -> {after}", - "apply_proposal": "اعمال تغییرات پیشنهادی", - "proposal_applied": "تغییرات پیشنهادی اعمال شد.", - "review_proposal": "بررسی پیشنهاد", - "review_request": "بررسی درخواست", - "view_in_calendar": "مشاهده در تقویم", - "select_calendar": "انتخاب تقویم", - "already_in_calendar": "هم‌اکنون در تقویم شماست", - "request_info": "به این رویداد دعوت شده‌اید. پاسخ دهید.", - "cancel_info": "برگزارکننده این رویداد را لغو کرده است.", - "event_updated": "به‌روزرسانی #{sequence}", - "event_status_tentative": "احتمالی", - "event_status_cancelled": "لغو شده", - "expand": "نمایش جزئیات", - "collapse": "مخفی کردن جزئیات" - }, - "send": "ارسال", - "more": "بیشتر", - "scheduled_banner": "زمان‌بندی شده برای ارسال در {date}", - "scheduled_send_created": "ایمیل برای ارسال زمان‌بندی شد", - "cancel_scheduled_send": "لغو ارسال", - "reschedule_send": "باززمان‌بندی", - "cancel_and_edit": "لغو و ویرایش", - "cancel_and_compose_again": "لغو و نوشتن مجدد", - "reschedule_prompt": "تاریخ/زمان جدید را وارد کنید", - "scheduled_actions_only": "نمای زمان‌بندی فقط عملیات زمان‌بندی را پشتیبانی می‌کند", - "undo_send_scheduled": "پیام برای ارسال زمان‌بندی شد", - "undo_send": "بازگردانی ارسال", - "contact_sidebar": { - "title": "مخاطب", - "close": "بستن نوار کناری", - "action_email": "ایمیل", - "action_email_title": "ارسال ایمیل", - "action_copy": "کپی", - "action_copy_title": "کپی ایمیل", - "action_edit_title": "ویرایش مخاطب", - "section_emails": "ایمیل‌ها", - "section_phones": "تلفن‌ها", - "section_organizations": "سازمان‌ها", - "section_addresses": "آدرس‌ها", - "section_notes": "یادداشت‌ها", - "not_in_contacts": "در مخاطبین شما نیست", - "add_to_contacts": "افزودن به مخاطبین", - "copied": "کپی شد!", - "copy_failed": "کپی ناموفق بود" - }, - "send_now": "ارسال فوری" - }, - "email_composer": { - "read_receipt_on": "درخواست تأیید خواندن فعال (کلیک برای غیرفعال کردن)", - "read_receipt_off": "درخواست تأیید خواندن", - "dsn_on": "اعلان تحویل درخواست شد (برای غیرفعال کردن کلیک کنید)", - "dsn_off": "درخواست اعلان تحویل", - "format_plain_text": "تغییر به متن ساده", - "format_rich_text": "تغییر به متن غنی (HTML)", - "require_tls_on": "تحویل رمزگذاری‌شده الزامی است (برای غیرفعال کردن کلیک کنید)", - "require_tls_off": "الزام تحویل رمزگذاری‌شده (TLS)", - "new_message": "پیام جدید", - "signature_edit_hint": "برای ویرایش امضا دوبار کلیک کنید", - "reply": "پاسخ", - "reply_all": "پاسخ به همه", - "forward": "ارسال مجدد", - "reply_to": "پاسخ", - "reply_all_to": "پاسخ به همه", - "forward_message": "ارسال مجدد", - "from": "از", - "to": "به", - "cc": "رونوشت", - "bcc": "رونوشت مخفی", - "subject": "موضوع", - "body_placeholder": "پیام خود را بنویسید...", - "send": "ارسال", - "cancel": "انصراف", - "attach": "پیوست", - "attach_photos": "عکس و ویدیو", - "attach_files": "فایل‌ها", - "discard": "دور انداختن", - "discard_draft_title": "دور انداختن پیش‌نویس؟", - "discard_draft_confirm": "تغییرات ذخیره نشده دارید. پیش‌نویس دور انداخته شود؟", - "saving": "در حال ذخیره...", - "sending": "در حال ارسال...", - "add_link": "افزودن لینک", - "link_url_prompt": "آدرس را وارد کنید", - "draft_saved": "پیش‌نویس ذخیره شد", - "save_failed": "ذخیره ناموفق بود", - "to_placeholder": "آدرس ایمیل گیرندگان", - "cc_placeholder": "گیرندگان رونوشت", - "bcc_placeholder": "گیرندگان رونوشت مخفی", - "subject_placeholder": "موضوع", - "cc_label": "رونوشت:", - "bcc_label": "رونوشت مخفی:", - "subject_label": "موضوع:", - "file_size_kb": "کیلوبایت", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(بدون موضوع)", - "unknown_sender": "ناشناس", - "quote": { - "reply_header": "در تاریخ {date}، {sender} نوشت:", - "forward_header": "---------- پیام ارجاعی ----------", - "from": "از: {sender}", - "date": "تاریخ: {date}", - "subject": "موضوع: {subject}", - "to": "به: {recipients}" - }, - "remove_sub_address": "حذف زیرآدرس", - "from_override": { - "toggle_off": "بازنویسی", - "toggle_on": "لغو بازنویسی", - "toggle_tooltip": "نام و آدرس فرستنده را آزادانه ویرایش کنید.", - "name_label": "نام فرستنده", - "name_placeholder": "نام", - "email_label": "آدرس ایمیل فرستنده", - "email_placeholder": "alias@example.com", - "envelope_notice": "اگر سرور شما این آدرس را به‌عنوان فرستندهٔ پاکت نپذیرد، به‌جای آن {identity} استفاده می‌شود و گیرندگان می‌توانند آن را در سرآیند Return-Path ببینند." - }, - "use_template": "قالب", - "save_as_template": "ذخیره به عنوان قالب", - "validation": { - "recipient_required": "یک گیرنده اضافه کنید", - "body_required": "پیامی بنویسید یا فایلی پیوست کنید", - "attachments_uploading": "پیوست‌ها هنوز در حال بارگذاری هستند — پس از اتمام ارسال می‌شود", - "attachment_upload_failed": "ارسال نشد - بارگذاری یک پیوست ناموفق بود. آن را حذف کنید و دوباره تلاش کنید." - }, - "upload_progress": "در حال بارگذاری {uploaded} / {total}", - "upload_cancel": "لغو بارگذاری", - "upload_failed": "بارگذاری {filename} ناموفق بود", - "attachment_too_large": "«{name}» بزرگ‌تر از حدی است که سرور اجازه می‌دهد ({max} برای هر فایل)", - "attachments_total_too_large": "پیوست‌ها از سقف {max} سرور برای هر پیام بیشتر می‌شوند", - "drop_files": "فایل‌ها را اینجا رها کنید", - "show_less": "نمایش کمتر", - "send_failed": "ارسال ایمیل ناموفق بود", - "send_timeout": "سرور دیگر پاسخ نمی‌دهد. پیش از ارسال دوباره، پوشهٔ ارسال‌شده‌ها را بررسی کنید — ممکن است پیام ارسال شده باشد.", - "continue_draft": "ادامه پیش‌نویس", - "close_draft_title": "ذخیره یا دور انداختن پیش‌نویس؟", - "close_draft_message": "تغییرات ذخیره نشده دارید. ذخیره یا دور انداختن؟", - "smime_sign_on": "امضای S/MIME فعال", - "smime_sign_off": "فعال کردن امضای S/MIME", - "smime_encrypt_on": "رمزنگاری S/MIME فعال", - "smime_encrypt_off": "فعال کردن رمزنگاری S/MIME", - "smime_encrypt_unavailable": "رمزنگاری S/MIME در دسترس نیست", - "smime_unlock_title": "باز کردن قفل کلید S/MIME", - "smime_unlock_message": "عبارت عبور کلید S/MIME را وارد کنید.", - "smime_unlock_button": "باز کردن", - "smime_passphrase_placeholder": "عبارت عبور", - "forgot_attachment": { - "title": "آیا پیوست را فراموش کرده‌اید؟", - "message": "پیام شما به \"{keyword}\" اشاره دارد اما فایلی پیوست نشده.", - "send_anyway": "ارسال به هر حال", - "back": "بازگشت به ویرایش" - }, - "empty_subject": { - "title": "بدون موضوع ارسال شود؟", - "message": "این پیام موضوعی ندارد. با این حال ارسال شود؟", - "dont_ask_again": "دیگر نپرس", - "send_anyway": "ارسال به هر حال", - "back": "بازگشت به ویرایش" - }, - "schedule_send": "زمان‌بندی ارسال", - "schedule_send_description": "زمان انتشار پیام توسط سرور را انتخاب کنید.", - "schedule_send_required": "تاریخ و زمان را انتخاب کنید.", - "schedule_send_invalid": "تاریخ و زمان معتبر وارد کنید.", - "schedule_send_future": "تاریخ و زمان در آینده انتخاب کنید.", - "schedule_send_too_late": "این زمان دیرتر از حد مجاز سرور است.", - "schedule_send_unsupported": "ارسال زمان‌بندی شده پشتیبانی نمی‌شود.", - "schedule_send_cleanup_warning": "ارسال زمان‌بندی ایجاد شد اما پاکسازی پیش‌نویس ناموفق بود.", - "send_delay_unsupported": "تأخیر ارسال پشتیبانی نمی‌شود.", - "send_delay_unsupported_confirm": "این حساب تأخیر ارسال ندارد. بلافاصله ارسال شود؟", - "recipient_edit_email": "ویرایش آدرس ایمیل", - "recipient_edit_name": "ویرایش نام نمایشی", - "recipient_email_placeholder": "آدرس ایمیل", - "recipient_name_placeholder": "نام نمایشی", - "autocomplete_search_server": "جستجو در سرور", - "autocomplete_searching": "در حال جستجو...", - "toolbar": { - "bold": "پررنگ", - "italic": "کج", - "underline": "زیرخط‌دار", - "strikethrough": "خط‌خورده", - "text_color": "رنگ متن", - "remove_color": "حذف رنگ", - "heading_1": "سرفصل ۱", - "heading_2": "سرفصل ۲", - "bullet_list": "فهرست نشانه‌دار", - "ordered_list": "فهرست شماره‌دار", - "quote": "نقل‌قول", - "code_block": "بلوک کد", - "align_left": "تراز چپ", - "align_center": "وسط‌چین", - "align_right": "تراز راست", - "text_direction": "جهت متن (RTL/LTR)", - "link": "پیوند", - "table": "جدول", - "clear_formatting": "پاک کردن قالب‌بندی", - "undo": "واگرد", - "redo": "انجام دوباره", - "add_row_above": "افزودن ردیف در بالا", - "add_row_below": "افزودن ردیف در پایین", - "add_column_before": "افزودن ستون قبل", - "add_column_after": "افزودن ستون بعد", - "delete_row": "حذف ردیف", - "delete_column": "حذف ستون", - "toggle_header_row": "تغییر وضعیت ردیف سرصفحه", - "delete_table": "حذف جدول", - "pick_size": "انتخاب اندازه", - "font_size": "اندازه فونت", - "font_size_default": "پیش‌فرض", - "background_color": "رنگ پس‌زمینه", - "remove_background_color": "حذف رنگ پس‌زمینه" - }, - "send_filing_warning": "ارسال شد - اما پاک‌سازی پس از ارسال ناموفق بود، ممکن است پیش‌نویس قدیمی باقی بماند." - }, - "confirm_dialog": { - "confirm": "تأیید", - "cancel": "انصراف" - }, - "common": { - "loading": "در حال بارگذاری...", - "error": "خطا", - "success": "موفقیت", - "cancel": "انصراف", - "save": "ذخیره", - "delete": "حذف", - "edit": "ویرایش", - "close": "بستن", - "search": "جستجو", - "refresh": "بازنشانی", - "settings": "تنظیمات", - "help": "راهنما", - "logout": "خروج", - "yes": "بله", - "no": "خیر", - "unknown": "ناشناس", - "app_title": "ایمیل تحت وب", - "reconnecting": "ارتباط قطع شد. در حال تلاش برای اتصال مجدد…", - "rate_limited_title": "احراز هویت سرور موقتاً محدود شده است.", - "rate_limited_detail": "Bulwark درخواست‌های پس‌زمینه را متوقف کرده است. تلاش مجدد در {seconds} ثانیه.", - "rate_limited_action_title": "درخواست برای جلوگیری از قفل متوقف شد.", - "rate_limited_action_detail": "Bulwark منتظر پایان زمان انتظار سرور است. در {seconds} ثانیه دوباره تلاش کنید.", - "pull_to_refresh": "برای تازه‌سازی بکشید", - "release_to_refresh": "برای تازه‌سازی رها کنید", - "refreshing": "در حال تازه‌سازی…" - }, - "notifications": { - "email_sent": "ایمیل با موفقیت ارسال شد", - "email_deleted": "ایمیل حذف شد", - "email_archived": "ایمیل بایگانی شد", - "emails_deleted": "{count, plural, other {# ایمیل حذف شد}}", - "emails_archived": "{count, plural, other {# ایمیل بایگانی شد}}", - "error_archiving": "بایگانی ایمیل ناموفق بود", - "error_updating": "به‌روزرسانی ایمیل ناموفق بود", - "email_starred": "ایمیل ستاره‌دار شد", - "email_unstarred": "ستاره ایمیل برداشته شد", - "email_marked_read": "ایمیل خوانده شده علامت‌گذاری شد", - "email_marked_unread": "ایمیل خوانده نشده علامت‌گذاری شد", - "copied_to_clipboard": "در کلیپ‌بورد کپی شد", - "source_copied": "منبع در کلیپ‌بورد کپی شد", - "error_sending": "ارسال ایمیل ناموفق بود", - "error_deleting": "حذف ایمیل ناموفق بود", - "error_loading": "بارگذاری ایمیل‌ها ناموفق بود", - "new_email": "ایمیل جدید", - "new_email_from": "از {sender}", - "click_to_view": "برای مشاهده کلیک کنید", - "email_moved": "ایمیل منتقل شد", - "emails_moved": "{count} ایمیل منتقل شد", - "moved_to_mailbox": "منتقل شد به {mailbox}", - "move_failed": "انتقال ناموفق بود", - "move_error": "انتقال ایمیل‌ها به پوشه انتخاب شده ممکن نیست", - "email_tagged": "ایمیل برچسب‌گذاری شد", - "emails_tagged": "{count} ایمیل برچسب‌گذاری شد", - "tag_failed": "برچسب‌گذاری ناموفق بود", - "identity_created": "هویت با موفقیت ایجاد شد", - "identity_updated": "هویت با موفقیت به‌روزرسانی شد", - "identity_deleted": "هویت حذف شد", - "identity_set_primary": "هویت اصلی به‌روزرسانی شد", - "identity_create_failed": "ایجاد هویت ناموفق بود: {error}", - "identity_update_failed": "به‌روزرسانی هویت ناموفق بود: {error}", - "identity_delete_failed": "حذف هویت ناموفق بود: {error}", - "identity_unauthorized": "شما مجاز به ارسال از این آدرس ایمیل نیستید", - "identity_not_found": "هویت یافت نشد", - "vacation_saved": "تنظیمات پاسخ‌گوی خودکار ذخیره شد", - "vacation_save_failed": "ذخیره تنظیمات پاسخ‌گوی خودکار ناموفق بود", - "filters_saved": "فیلترها با موفقیت ذخیره شدند", - "filters_save_failed": "ذخیره فیلترها ناموفق بود", - "filters_deleted": "قانون فیلتر حذف شد", - "templates_exported": "قالب‌ها با موفقیت خروجی گرفته شدند", - "templates_imported": "{count, plural, one {# قالب} other {# قالب}} وارد شد", - "templates_import_errors": "برخی قالب‌ها وارد نشدند", - "templates_import_empty": "قالبی در فایل یافت نشد", - "export_email_error": "خروجی ایمیل ناموفق بود", - "import_email_success": "ایمیل با موفقیت وارد شد", - "import_email_error": "وارد کردن ایمیل ناموفق بود" - }, - "date": { - "today": "امروز", - "yesterday": "دیروز", - "this_week": "این هفته", - "last_week": "هفته گذشته", - "this_month": "این ماه", - "older": "قدیمی‌تر", - "just_now": "همین الان", - "minutes_ago": "{count} دقیقه پیش", - "minutes_ago_plural": "{count} دقیقه پیش", - "hours_ago": "{count} ساعت پیش", - "hours_ago_plural": "{count} ساعت پیش", - "days_ago": "{count} روز پیش", - "days_ago_plural": "{count} روز پیش" - }, - "language": { - "title": "زبان", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "انتخاب زبان", - "switch_to_english": "تغییر به انگلیسی", - "switch_to_french": "تغییر به فرانسوی", - "switch_to_japanese": "تغییر به ژاپنی", - "switch_to_spanish": "تغییر به اسپانیایی", - "switch_to_italian": "تغییر به ایتالیایی", - "switch_to_german": "تغییر به آلمانی", - "switch_to_dutch": "تغییر به هلندی", - "switch_to_portuguese": "تغییر به پرتغالی", - "switch_to_russian": "تغییر به روسی", - "switching": "در حال تغییر زبان...", - "switch": "تغییر زبان", - "current": "زبان فعلی", - "polish": "Polski", - "switch_to_polish": "تغییر به لهستانی", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "تغییر به فارسی", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "تغییر به مغولی" - }, - "settings": { - "title": "تنظیمات", - "back_to_mail": "بازگشت به ایمیل", - "save_success": "تنظیمات با موفقیت ذخیره شد", - "import_success": "تنظیمات با موفقیت وارد شد", - "import_error": "وارد کردن تنظیمات ناموفق بود", - "reset_confirm": "آیا از بازنشانی همه تنظیمات اطمینان دارید؟", - "unsaved_changes": "تغییرات ذخیره نشده دارید", - "discard_changes": "دور انداختن تغییرات ذخیره نشده؟", - "discard": "دور انداختن", - "keep_editing": "ادامه ویرایش", - "search_placeholder": "جستجوی تنظیمات", - "search_clear": "پاک کردن جستجو", - "search_no_results": "تنظبمات منطبقی یافت نشد", - "tabs": { - "appearance": "ظاهر", - "language": "زبان، منطقه و زمان", - "email": "رفتار ایمیل", - "composer": "ویرایشگر", - "privacy": "حریم خصوصی و امنیت", - "account": "حساب", - "identities": "هویت‌ها", - "vacation": "پاسخ‌گوی خودکار", - "advanced": "پیشرفته", - "calendar": "تقویم", - "filters": "فیلترها", - "templates": "قالب‌ها", - "folders": "پوشه‌ها", - "keywords": "برچسب‌ها", - "security": "امنیت", - "files": "فایل‌ها", - "contacts": "مخاطبین", - "encryption": "رمزنگاری", - "protocol_handlers": "برنامه‌های پیش‌فرض", - "sidebar_apps": "برنامه‌های نوار کناری", - "notifications": "اعلان‌ها", - "layout": "چیدمان", - "reading": "خواندن", - "composing": "نوشتن", - "downloads": "دانلودها", - "content_senders": "محتوا و فرستندگان", - "about_data": "درباره و داده‌ها", - "debug": "اشکال‌زدایی" - }, - "tab_groups": { - "general": "عمومی", - "account": "حساب و هویت", - "organization": "سازمان‌دهی ایمیل", - "apps": "برنامه‌ها", - "system": "سیستم", - "appearance": "ظاهر", - "mail": "ایمیل", - "privacy": "حریم خصوصی و امنیت", - "advanced": "پیشرفته" - }, - "appearance": { - "message_list_order": { - "label": "ترتیب فهرست پیام‌ها", - "description": "نحوهٔ مرتب‌سازی پیام‌ها در فهرست. مرتب‌سازی را سرور انجام می‌دهد، بنابراین کل پوشه را در بر می‌گیرد، نه فقط پیام‌های بارگذاری‌شده را.", - "preset": { - "chronological": "زمانی (جدیدترین ابتدا)", - "unread_first": "خوانده‌نشده‌ها ابتدا", - "starred_first": "ستاره‌دارها ابتدا", - "tagged_first": "برچسب‌دارها ابتدا", - "custom": "سفارشی" - }, - "tag_label": "برچسب", - "advanced_show": "پیشرفته: ویرایش سطوح مرتب‌سازی", - "advanced_hide": "پنهان کردن پیشرفته", - "criterion_label": "معیار سطح {n}", - "direction_label": "جهت سطح {n}", - "criteria": { - "unread": "خوانده‌نشده", - "starred": "ستاره‌دار", - "tag": "برچسب", - "receivedAt": "تاریخ دریافت", - "sentAt": "تاریخ ارسال", - "from": "فرستنده", - "subject": "موضوع", - "size": "اندازه" - }, - "direction": { - "unread_first": "خوانده‌نشده‌ها ابتدا", - "read_first": "خوانده‌شده‌ها ابتدا", - "starred_first": "ستاره‌دارها ابتدا", - "unstarred_first": "بدون ستاره ابتدا", - "tagged_first": "برچسب‌دارها ابتدا", - "untagged_first": "بدون برچسب ابتدا", - "newest_first": "جدیدترین ابتدا", - "oldest_first": "قدیمی‌ترین ابتدا", - "a_to_z": "الف → ی", - "z_to_a": "ی → الف", - "largest_first": "بزرگ‌ترین ابتدا", - "smallest_first": "کوچک‌ترین ابتدا" - }, - "add_level": "افزودن سطح", - "remove_level": "حذف سطح", - "move_up": "انتقال به بالا", - "move_down": "انتقال به پایین", - "tiebreak_note": "حداکثر سه سطح؛ سطوح بعدی در صورت تساوی تصمیم می‌گیرند. «جدیدترین ابتدا» همیشه در آخر اعمال می‌شود.", - "scope": { - "label": "اعمال بر همهٔ پوشه‌ها", - "description": "در حالت خاموش، ترتیب فقط بر صندوق ورودی اعمال می‌شود؛ پوشه‌های دیگر زمانی می‌مانند." - }, - "unsupported_note": "سرور ایمیل شما مرتب‌سازی بر اساس وضعیت خواندن، ستاره یا برچسب را پشتیبانی نمی‌کند. این سطوح نادیده گرفته می‌شوند و فهرست زمانی می‌ماند." - }, - "title": "ظاهر", - "description": "ظاهر و حس ایمیل تحت وب خود را سفارشی کنید", - "theme": { - "label": "تم", - "description": "طرح رنگی مورد نظر خود را انتخاب کنید", - "light": "روشن", - "dark": "تاریک", - "system": "سیستم" - }, - "language": { - "label": "زبان", - "description": "زبان مورد نظر خود را انتخاب کنید" - }, - "font_size": { - "label": "اندازه قلم", - "description": "اندازه متن را برای خوانایی بهتر تنظیم کنید", - "small": "کوچک", - "medium": "متوسط", - "large": "بزرگ" - }, - "list_density": { - "label": "تراکم", - "description": "فاصله‌گذاری و padding رابط کاربری را کنترل کنید", - "extra_compact": "خیلی فشرده", - "compact": "فشرده", - "regular": "معمولی", - "comfortable": "راحت" - }, - "animations": { - "label": "فعال کردن انیمیشن‌ها", - "description": "نمایش transitions و افکت‌های نرم" - }, - "toolbar_position": { - "label": "موقعیت نوار ابزار", - "description": "محل نمایش دکمه‌های عملیات ایمیل", - "top": "بالا", - "below_subject": "زیر موضوع" - }, - "toolbar_labels": { - "label": "نمایش برچسب‌های نوار ابزار", - "description": "نمایش متن کنار آیکون‌های نوار ابزار" - }, - "hide_account_switcher": { - "label": "مخفی کردن تعویض حساب نوار کناری", - "description": "انتخاب‌گر حساب در بالای نوار کناری پوشه‌ها را مخفی کنید" - }, - "show_rail_account_list": { - "label": "نمایش آواتار حساب‌ها در نوار ناوبری", - "description": "دایره‌های حساب جداگانه در پایین نوار ناوبری برای تعویض سریع" - }, - "unified_mailbox": { - "label": "صندوق پستی یکپارچه", - "description": "نمایش پوشه‌های ترکیبی در همه حساب‌های متصل", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "شامل صندوق ورودی گروه‌ها", - "description": "صندوق ورودی گروه‌های اشتراکی را هم ادغام کن" - } - }, - "all_mail": { - "folders_label": "پوشه‌ها در همه ایمیل‌ها", - "folders_description": "انتخاب کنید کدام پوشه‌ها ادغام شوند", - "no_folders": "پوشه‌ای موجود نیست", - "account_hint": "برای {account} اعمال می‌شود." - }, - "colorful_sidebar_icons": { - "label": "آیکون‌های رنگی نوار کناری", - "description": "رنگ‌آمیزی آیکون‌های پوشه و برچسب بر اساس نوع" - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "نمایش تعداد کل پیام‌ها", - "description": "تعداد کل پیام‌ها را در کنار پوشه‌ها و برچسب‌ها، همراه با تعداد خوانده‌نشده‌ها نمایش می‌دهد. برای نمایش فقط تعداد خوانده‌نشده‌ها غیرفعال کنید." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "رابط حرفه‌ای (آزمایشی)", - "description": "چیدمان قدرت-کاربری فقط دسکتاپ", - "open_label": "باز کردن رابط حرفه‌ای", - "back_to_standard": "بازگشت به استاندارد" - }, - "cross_all": { - "label": "همه حساب‌ها: همه ایمیل‌ها", - "description": "نمایش یک مورد در بخش همه حساب‌ها که همه ایمیل‌های همه حساب‌ها را فهرست می‌کند، شامل همه پوشه‌ها به جز هرزنامه، ارسال‌شده، بایگانی، زباله‌دان و پیش‌نویس‌ها." - }, - "cross_starred": { - "label": "همه حساب‌ها: ستاره‌دار", - "description": "نمایش یک مورد در بخش همه حساب‌ها که ایمیل‌های ستاره‌دار همه حساب‌ها را فهرست می‌کند، شامل همه پوشه‌ها به جز هرزنامه، ارسال‌شده، بایگانی، زباله‌دان و پیش‌نویس‌ها." - }, - "cross_unread": { - "label": "همه حساب‌ها: خوانده‌نشده", - "description": "نمایش یک مورد در بخش همه حساب‌ها که ایمیل‌های خوانده‌نشده همه حساب‌ها را فهرست می‌کند، شامل همه پوشه‌ها به جز هرزنامه، ارسال‌شده، بایگانی، زباله‌دان و پیش‌نویس‌ها." - } - }, - "keywords": { - "title": "برچسب‌های ایمیل", - "description": "برچسب‌ها را برای سازمان‌دهی ایمیل‌ها تعریف کنید", - "add_keyword": "افزودن برچسب", - "label_field": "نام نمایشی", - "label_placeholder": "مثال: کاری، شخصی، فوری", - "id_field": "شناسه برچسب", - "id_placeholder": "مثال: work, personal", - "color_field": "رنگ", - "id_exists": "این شناسه قبلاً وجود دارد", - "edit": "ویرایش برچسب", - "delete": "حذف برچسب", - "save": "ذخیره", - "add": "افزودن", - "cancel": "انصراف", - "migrating": "در حال به‌روزرسانی برچسب روی ایمیل‌های موجود…", - "migration_error": "به‌روزرسانی برچسب ناموفق بود", - "nesting": { - "label": "برچسب‌های تودرتو", - "description": "برچسب‌ها را زیر برچسب‌های دیگر قرار دهید و آن‌ها را به‌صورت درختی در نوار کناری نمایش دهید." - }, - "parent_field": "برچسب والد", - "no_parent": "بدون برچسب والد", - "too_long": "این مسیر برچسب خیلی طولانی است (حداکثر {max} نویسه)", - "has_children_locked": "برچسب‌های دیگری زیر این برچسب قرار دارند، بنابراین نام و برچسب والد آن قفل است. ابتدا آن‌ها را جابه‌جا یا حذف کنید.", - "has_children_delete": "ابتدا برچسب‌های زیرمجموعهٔ این برچسب را حذف کنید", - "visibility_field": "نمایش در نوار کناری", - "visibility": { - "show": "نمایش", - "unread": "نمایش در صورت وجود خوانده‌نشده", - "hide": "پنهان کردن" - }, - "unrecognized": { - "title": "بازیابی برچسب‌ها از سرور", - "description": "نام و رنگ برچسب‌ها در این مرورگر ذخیره می‌شود، اما خود برچسب‌ها به‌صورت کلیدواژه‌های JMAP روی ایمیل‌های شما قرار دارند. ایمیل‌ها را برای یافتن کلیدواژه‌هایی که هیچ برچسبی اینجا ندارند بررسی کنید و آن‌ها را دوباره اضافه کنید.", - "scan": "جست‌وجوی برچسب‌ها", - "rescan": "بررسی دوباره", - "scanning": "در حال بررسی… {scanned} از {total} ایمیل", - "none": "همهٔ برچسب‌های ایمیل‌های شما از قبل اینجا تعریف شده‌اند.", - "messages": "{count, plural, one {# ایمیل} other {# ایمیل}}", - "add_all": "افزودن همه ({count})", - "partial": "فقط {scanned} ایمیل جدیدتر از مجموع {total} بررسی شد. برچسب‌هایی که تنها در ایمیل‌های قدیمی‌تر به کار رفته‌اند ممکن است نمایش داده نشوند.", - "error": "بررسی ایمیل‌ها برای یافتن برچسب‌ها ناموفق بود." - } - }, - "notifications": { - "test_sound": "تست صدای اعلان", - "sounds": { - "default": "پیش‌فرض (بیپ)", - "cheerful": "شاد", - "involved": "درگیر", - "swift": "سریع", - "relax": "آرامش" - }, - "push": { - "title": "اعلان‌های پس‌زمینه", - "description": "دریافت اعلان‌های سیستم برای ایمیل جدید", - "relay_label": "رله push", - "relay_desc": "پیش‌فرض رله میزبانی شده Bulwark", - "relay_locked": "تنظیم شده توسط مدیر", - "relay_locked_desc": "قابل تغییر نیست", - "status_active": "فعال روی این دستگاه", - "status_inactive": "فعال نشده", - "status_unsupported": "مرورگر پشتیبانی نمی‌کند", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "در حال کار…", - "enable": "فعال کردن", - "reenable": "ثبت مجدد", - "disable": "غیرفعال کردن", - "confirm_disable_title": "غیرفعال کردن اعلان‌های پس‌زمینه؟", - "confirm_disable_message": "این دستگاه دیگر هشدار دریافت نمی‌کند", - "dismiss_aria": "رد کردن پیشنهاد اعلان‌ها", - "ios_hint": "در iOS ابتدا سایت را به صفحه اصلی اضافه کنید", - "devices_title": "دستگاه‌های ثبت‌شده", - "devices_desc": "همهٔ دستگاه‌هایی که برای دریافت اعلان‌های پس‌زمینهٔ این حساب ثبت شده‌اند. با لغو یک دستگاه، دریافت بی‌درنگ متوقف می‌شود.", - "devices_loading": "در حال بارگذاری دستگاه‌های ثبت‌شده…", - "devices_empty": "هیچ دستگاهی برای اعلان‌های پس‌زمینه ثبت نشده است.", - "devices_error": "بارگذاری دستگاه‌های ثبت‌شده ممکن نشد.", - "devices_refresh": "بازخوانی", - "device_this": "این دستگاه", - "device_other": "دستگاه دیگر", - "device_status_active": "در حال تحویل اعلان‌ها", - "device_status_inactive": "هرگز اعلانی تحویل نداده است", - "device_status_unknown": "برای این بازپخش ناشناس", - "device_expires": "انقضا در {date}", - "revoke": "لغو", - "confirm_revoke_title": "این دستگاه لغو شود؟", - "confirm_revoke_message": "بی‌درنگ دریافت اعلان‌های پس‌زمینه را متوقف می‌کند. با روشن‌کردن دوبارهٔ پوش روی آن دستگاه، دوباره ثبت می‌شود.", - "confirm_revoke_message_this": "این دستگاه بی‌درنگ دریافت اعلان‌های پس‌زمینه را متوقف می‌کند." - }, - "sound_selection": { - "title": "صدای اعلان", - "description": "صدای پخش شده برای اعلان‌ها", - "choose": "صدا", - "choose_desc": "یک آهنگ انتخاب کنید" - }, - "email": { - "title": "اعلان‌های ایمیل", - "description": "پیکربندی اعلان‌های ایمیل", - "enabled": "اعلان‌های ایمیل", - "enabled_desc": "نمایش اعلان هنگام ایمیل جدید", - "sound": "صدای اعلان", - "sound_desc": "پخش هشدار صوتی", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "اعلان‌های تقویم", - "description": "پیکربندی اعلان‌های تقویم", - "enabled": "اعلان‌های رویداد", - "enabled_desc": "نمایش هشدار برای رویدادها", - "sound": "صدای اعلان", - "sound_desc": "پخش صدا برای یادآورها", - "invitation_parsing": "پردازش دعوت‌نامه‌های ایمیل", - "invitation_parsing_desc": "تشخیص دعوت‌نامه در پیوست ایمیل" - } - }, - "language_region": { - "title": "زبان، منطقه و زمان", - "description": "تنظیمات زبان، فرمت تاریخ و زمان", - "language": { - "label": "زبان", - "description": "زبان مورد نظر را انتخاب کنید", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "فرمت تاریخ", - "description": "نحوه نمایش تاریخ‌ها", - "smart": "هوشمند", - "relative": "نسبی", - "full": "تاریخ کامل", - "preview_today": "امروز:", - "preview_this_week": "این هفته:", - "preview_older": "قدیمی‌تر:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "فرمت زمان", - "description": "۱۲ یا ۲۴ ساعته", - "12h": "۱۲ ساعته", - "24h": "۲۴ ساعته" - }, - "first_day": { - "label": "اولین روز هفته", - "description": "یکشنبه یا دوشنبه", - "sunday": "یکشنبه", - "monday": "دوشنبه" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "پاک کردن جستجو هنگام تغییر پوشه", - "description": "کلیک روی یک پوشه (صندوق ورودی، ارسال‌شده…) جستجوی فعال را پاک می‌کند و آن پوشه را نشان می‌دهد. وقتی خاموش باشد، جستجو باقی می‌ماند و در پوشهٔ جدید دوباره اجرا می‌شود." - }, - "request_read_receipt": { - "label": "درخواست تأیید خواندن", - "description": "پیش‌فعال کردن تأیید خواندن" - }, - "read_receipt_response": { - "label": "پاسخ به تأیید خواندن", - "description": "نحوه پاسخ به درخواست تأیید", - "ask": "هر بار بپرس", - "always": "همیشه ارسال کن", - "never": "هرگز ارسال نکن" - }, - "title": "رفتار ایمیل", - "description": "پیکربندی نحوه مدیریت ایمیل‌ها", - "mark_read": { - "label": "علامت‌گذاری خوانده شده", - "description": "زمان علامت‌گذاری", - "instant": "بلافاصله", - "delay_3s": "بعد از ۳ ثانیه", - "delay_5s": "بعد از ۵ ثانیه", - "never": "هرگز" - }, - "delete_action": { - "label": "عملیات حذف", - "description": "نتیجه حذف ایمیل", - "trash": "انتقال به زباله‌دان", - "trash_and_read": "زباله‌دان و خوانده شده", - "permanent": "حذف دائمی", - "warning": "غیرقابل بازگشت" - }, - "message_spacing": { - "label": "فاصله‌گذاری پیام", - "description": "حاشیه اطراف متن پیام در نمای خواندن", - "auto": "خودکار (پیشنهادی)", - "always": "همیشه فاصله اضافه شود", - "edge": "لبه به لبه" - }, - "plain_text_font": { - "label": "قلم متن ساده", - "description": "قلم مورد استفاده برای ایمیل‌های متن ساده", - "mono": "تک‌عرض", - "sans": "معمولی (مانند برنامه)" - }, - "archive_mode": { - "label": "بایگانی در", - "description": "سازمان‌دهی بایگانی", - "single": "یک پوشه", - "year": "پوشه در سال", - "month": "پوشه در ماه", - "reorganize": "بازسازمان‌دهی", - "reorganize_success": "{count, plural, =0 {بدون ایمیل} =1 {۱ ایمیل} other {# ایمیل}} بازسازمان‌دهی شد", - "reorganize_error": "خطای بازسازمان‌دهی" - }, - "permanently_delete_junk": { - "label": "حذف دائمی هرزنامه", - "description": "حذف مستقیم هرزنامه" - }, - "mail_layout": { - "label": "چیدمان ایمیل", - "description": "انتخاب چیدمان", - "split": "پنل تقسیم شده", - "split_description": "لیست و پنل کنار هم", - "focus": "لیست متمرکز", - "focus_description": "نمای متمرکز", - "horizontal": "پنل پایین", - "horizontal_description": "لیست بالا، پنل پایین" - }, - "show_preview": { - "label": "نمایش پیش‌نمایش", - "description": "پیش‌نمایش در لیست", - "focus_description": "پیش‌نمایش در نمای متمرکز" - }, - "disable_threading": { - "label": "غیرفعال کردن گروه‌بندی", - "description": "پیام‌های جداگانه" - }, - "plain_text_mode": { - "label": "فقط متن ساده", - "description": "غیرفعال کردن rich text" - }, - "auto_select_reply_identity": { - "label": "پاسخ از آدرس دریافتی", - "description": "ارسال با آدرس دریافت شده" - }, - "reply_identity_match": { - "label": "تطبیق نشانی دریافت", - "description": "کدام نشانی‌های دریافت به‌عنوان نشانی شما در نظر گرفته شوند. «فقط نشانی دقیق» تنها یکی از هویت‌های پیکربندی‌شدهٔ شما را انتخاب می‌کند؛ «همان دامنه» هر نشانی دیگری در دامنه‌های هویت شما را نیز نام مستعار catch-all در نظر می‌گیرد و سرآیند فرستنده را با آن بازنویسی می‌کند. اگر این نشانی‌ها فهرست توزیع هستند، نشانی دقیق را انتخاب کنید.", - "exact": "فقط نشانی دقیق", - "domain": "هر نشانی در دامنه‌های من" - }, - "signature_position": { - "label": "موقعیت امضا", - "description": "محل درج امضا", - "above_quote": "قبل از نقل قول", - "below_quote": "بعد از نقل قول" - }, - "signature_separator": { - "label": "جداکننده امضا", - "description": "پیشوند \"-- \"" - }, - "sub_address_delimiter": { - "label": "جداکننده زیرآدرس", - "description": "کاراکتر جداکننده", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "سفارشی…", - "custom_input_label": "کاراکتر سفارشی" - }, - "attachment_click_action": { - "label": "کلیک روی پیوست", - "description": "پیش‌نمایش یا دانلود", - "preview": "پیش‌نمایش", - "download": "دانلود" - }, - "attachment_position": { - "label": "موقعیت پیوست", - "description": "محل پیوست‌ها", - "beside-sender": "کنار فرستنده", - "below-header": "زیر هدر" - }, - "emails_per_page": { - "10": "۱۰ ایمیل", - "25": "۲۵ ایمیل", - "50": "۵۰ ایمیل", - "100": "۱۰۰ ایمیل", - "label": "ایمیل در صفحه", - "description": "تعداد در هر بار" - }, - "always_light_mode": { - "label": "حالت روشن برای ایمیل‌ها", - "description": "رندر روشن حتی در تم تاریک" - }, - "external_content": { - "label": "محتوای خارجی", - "description": "مدیریت تصاویر خارجی", - "ask": "همیشه بپرس", - "block": "همیشه مسدود کن", - "allow": "همیشه مجاز کن" - }, - "trusted_senders": { - "label": "فرستندگان مورد اعتماد", - "description": "مدیریت فرستندگان", - "count_zero": "هیچ", - "count_one": "۱ فرستنده", - "count_other": "{count} فرستنده", - "modal_title": "فرستندگان مورد اعتماد", - "empty_title": "هنوز فرستنده‌ای نیست", - "empty_description": "از طریق نمایش ایمیل اضافه کنید", - "add_manually": "افزودن دستی", - "add_button": "افزودن", - "add_placeholder": "آدرس ایمیل", - "search_placeholder": "جستجو...", - "no_results": "مطابقی نیست", - "remove": "حذف", - "close": "بستن", - "invalid_email": "ایمیل معتبر وارد کنید", - "already_added": "قبلاً اضافه شده", - "save_error": "خطای ذخیره", - "use_address_book_label": "همگام‌سازی با دفترچه", - "use_address_book_description": "ذخیره در دفترچه آدرس" - }, - "hover_actions": { - "label": "عملیات هاور", - "description": "عملیات سریع در لیست", - "delete": "حذف", - "star": "ستاره", - "mark_read": "خوانده شده", - "archive": "بایگانی", - "tag": "برچسب", - "spam": "هرزنامه", - "none_selected": "انتخاب نشده", - "mode_label": "حالت نمایش", - "mode_inline": "داخلی", - "mode_floating": "شناور", - "corner_label": "موقعیت", - "corner_top-left": "بالا چپ", - "corner_top-right": "بالا راست", - "corner_bottom-left": "پایین چپ", - "corner_bottom-right": "پایین راست", - "not_spam": "هرزنامه نیست" - }, - "default_mail_program": { - "label": "برنامه ایمیل پیش‌فرض", - "description": "ثبت برای mailto:", - "button": "تنظیم به عنوان پیش‌فرض", - "success": "ثبت شد", - "error": "پشتیبانی نمی‌شود" - }, - "attachment_reminder": { - "label": "یادآور پیوست", - "description": "هشدار فراموشی پیوست", - "keywords_label": "کلمات کلیدی", - "keywords_description": "کلمات محرک", - "add_placeholder": "افزودن...", - "add": "افزودن", - "remove": "حذف" - }, - "empty_subject_warning": { - "label": "هشدار موضوع خالی", - "description": "پیش از ارسال پیام بدون موضوع تأیید بگیرید." - }, - "hide_inline_image_attachments": { - "label": "مخفی کردن تصاویر داخلی", - "description": "عدم نمایش به عنوان پیوست" - }, - "attachment_image_previews": { - "label": "پیش‌نمایش تصویر پیوست", - "description": "نمایش thumbnail" - }, - "send_delay": { - "label": "تأخیر ارسال", - "description": "پنجره تأخیر", - "off": "خاموش", - "seconds": "{seconds} ثانیه", - "unsupported": "پشتیبانی نمی‌شود" - }, - "return_to_list_after_action": { - "label": "بازگشت به فهرست پس از حذف یا علامت‌گذاری به‌عنوان خوانده‌نشده", - "description": "پس از حذف یا علامت‌گذاری پیام باز به‌عنوان خوانده‌نشده، به‌جای باز کردن پیام بعدی به فهرست پیام‌ها بازگردید." - }, - "swipe_right_action": { - "label": "عملکرد کشیدن به راست (موبایل)", - "description": "در دستگاه‌های لمسی، عملی که هنگام کشیدن یک پیام به راست در فهرست انجام می‌شود." - }, - "swipe_left_action": { - "label": "عملکرد کشیدن به چپ (موبایل)", - "description": "در دستگاه‌های لمسی، عملی که هنگام کشیدن یک پیام به چپ در فهرست انجام می‌شود." - }, - "swipe_actions": { - "none": "هیچ", - "archive": "بایگانی", - "delete": "حذف", - "mark_read": "تغییر وضعیت خوانده‌شده", - "star": "تغییر وضعیت ستاره", - "spam": "علامت‌گذاری به عنوان هرزنامه" - }, - "rtl_editing": { - "label": "پشتیبانی از ویرایش راست‌به‌چپ", - "description": "دکمه جهت متن را به نوار ابزار ویرایشگر اضافه می‌کند تا بتوانید پاراگراف‌ها را چپ‌به‌راست یا راست‌به‌چپ تنظیم کنید" - } - }, - "composer": { - "title": "ویرایشگر", - "description": "تنظیمات نوشتن ایمیل", - "autosave": { - "label": "ذخیره خودکار", - "description": "فاصله ذخیره پیش‌نویس", - "30s": "هر ۳۰ ثانیه", - "1m": "هر دقیقه", - "2m": "هر ۲ دقیقه", - "5m": "هر ۵ دقیقه" - }, - "send_confirmation": { - "label": "تأیید ارسال", - "description": "تأیید قبل از ارسال" - }, - "default_reply": { - "label": "حالت پیش‌فرض پاسخ", - "description": "عملیات کلیک پاسخ", - "reply": "پاسخ", - "reply_all": "پاسخ به همه" - } - }, - "privacy": { - "title": "حریم خصوصی و امنیت", - "description": "مدیریت تنظیمات", - "external_images": { - "label": "مسدود کردن تصاویر خارجی", - "description": "جلوگیری از ردیابی" - }, - "session_timeout": { - "label": "مهلت نشست", - "description": "خروج خودکار", - "never": "هرگز", - "30m": "۳۰ دقیقه", - "1h": "۱ ساعت", - "4h": "۴ ساعت" - }, - "clear_cache": { - "label": "پاک کردن کش", - "description": "حذف داده‌های موقت", - "button": "پاک کردن کش", - "confirm": "اطمینان دارید؟", - "success": "کش پاک شد" - } - }, - "account": { - "title": "حساب", - "description": "اطلاعات حساب", - "name_label": "نام نمایشی", - "username_label": "نام کاربری", - "account_type_label": "نوع حساب", - "auth_method_label": "احراز هویت", - "auth_method_oauth": "SSO", - "auth_method_basic": "رمز عبور", - "demo_account": "حساب آزمایشی", - "email": { - "label": "آدرس ایمیل", - "value": "{email}" - }, - "server": { - "label": "سرور JMAP", - "value": "{server}" - }, - "storage": { - "label": "مصرف فضا", - "used": "{used} از {total}", - "percentage": "{percent}٪" - }, - "last_sync": { - "label": "آخرین همگام‌سازی", - "value": "{time}" - }, - "accounts": { - "title": "حساب‌های وارد شده", - "description": "ترتیب‌دهی حساب‌ها", - "active": "حساب فعال", - "default_badge": "پیش‌فرض", - "set_default": "تنظیم پیش‌فرض", - "switch_to": "تعویض", - "move_up": "بالا", - "move_down": "پایین", - "drag_handle": "کشیدن", - "add": "افزودن" - }, - "shared_accounts": { - "title": "اشتراکی با من", - "description": "حساب‌های گروهی", - "shared_label": "حساب اشتراکی" - } - }, - "scoped": { - "back": "بازگشت به حساب من", - "managing": "مدیریت: {name}" - }, - "security": { - "title": "امنیت حساب", - "description": "مدیریت امنیت", - "detecting": "شناسایی قابلیت‌ها...", - "not_available": "در دسترس نیست", - "password": { - "title": "تغییر رمز عبور", - "current": "رمز فعلی", - "new": "رمز جدید", - "confirm": "تأیید رمز جدید", - "submit": "تغییر رمز", - "success": "رمز تغییر کرد", - "error_title": "خطا", - "error_mismatch": "مطابقت ندارد", - "error_min_length": "حداقل ۸ کاراکتر", - "error_generic": "تغییر ناموفق" - }, - "display_name": { - "label": "نام نمایشی", - "description": "نام روی سرور", - "placeholder": "نام خود را وارد کنید", - "save": "ذخیره", - "success": "به‌روزرسانی شد", - "error": "خطا" - }, - "totp": { - "section_title": "احراز هویت دو مرحله‌ای", - "label": "TOTP", - "description": "لایه امنیتی اضافی", - "active": "فعال", - "inactive": "غیرفعال", - "enabled": "2FA فعال شد", - "disabled": "2FA غیرفعال شد", - "enable_error": "خطای فعال‌سازی", - "disable_error": "خطای غیرفعال‌سازی", - "setup_instructions": "در برنامه authenticator کپی کنید:", - "verification_code": "کد تأیید", - "confirm": "تأیید", - "disable": "غیرفعال کردن", - "disable_confirm_prompt": "رمز عبور را وارد کنید", - "password_required": "رمز الزامی است", - "code_required": "کد الزامی است", - "code_invalid": "کد نامعتبر" - }, - "app_passwords": { - "title": "رمزهای برنامه", - "description": "برای برنامه‌های بدون 2FA", - "add": "افزودن", - "create": "ایجاد", - "cancel": "انصراف", - "done": "انجام شد", - "generate": "تولید", - "name_label": "نام برنامه", - "name_placeholder": "مثال: Thunderbird", - "expires_label": "انقضا", - "allowed_ips_label": "IPهای مجاز", - "allowed_ips_placeholder": "10.0.0.5", - "allowed_ips_hint": "با کاما جدا کنید", - "password_label": "رمز عبور", - "password_placeholder": "خودکار تولید می‌شود", - "copy_now_warning": "همین حالا کپی کنید", - "added": "ایجاد شد", - "removed": "حذف شد", - "add_error": "خطای ایجاد", - "remove_error": "خطای حذف", - "none": "پیکربندی نشده" - }, - "api_keys": { - "title": "کلیدهای API", - "description": "برای اسکریپت‌ها", - "name_label": "نام کلید", - "name_placeholder": "مثال: اسکریپت پشتیبان", - "copy_now_warning": "همین حالا کپی کنید", - "added": "ایجاد شد", - "removed": "حذف شد", - "add_error": "خطای ایجاد", - "remove_error": "خطای حذف", - "none": "پیکربندی نشده" - }, - "encryption": { - "section_title": "رمزنگاری", - "label": "رمزنگاری ایمیل", - "description": "رمزنگاری ذخیره", - "active": "{type} فعال", - "inactive": "غیرفعال", - "enabled": "فعال شد", - "disabled_success": "غیرفعال شد", - "error": "خطا", - "algorithm_label": "الگوریتم رمزنگاری", - "encrypt_on_append": "رمزنگاری ایمیل‌های جدید هنگام بارگذاری", - "allow_spam_training": "اجازه به آموزش هرزنامه پیش از رمزنگاری ایمیل‌ها", - "enable_encryption": "فعال‌سازی رمزنگاری", - "enable_tooltip": "فعال‌سازی رمزنگاری با این کلید", - "configure_title": "فعال‌سازی رمزنگاری با این کلید", - "enable_button": "فعال‌سازی", - "disable_tooltip": "غیرفعال‌سازی رمزنگاری", - "enabled_success": "فعال شد" - }, - "email_client": { - "title": "تنظیم برنامه ایمیل", - "description": "اطلاعات برای برنامه دسکتاپ", - "jmap_username_label": "نام کاربری JMAP", - "copy": "کپی", - "copied": "کپی شد", - "password_instructions": "با رمز برنامه استفاده کنید" - }, - "link_device": { - "title": "اتصال موبایل", - "description": "ورود با QR code", - "generate": "نمایش QR", - "regenerate": "کد جدید", - "instructions": "با برنامه اسکن کنید", - "expires_in": "منقضی در {seconds} ثانیه", - "expired": "منقضی شده", - "generating": "در حال تولید…", - "error": "خطا" - }, - "public_keys": { - "name_label": "نام کلید عمومی", - "name_placeholder": "مثال: کلید من", - "key_label": "کلید عمومی (S/MIME یا PGP با قالب ASCII)", - "key_placeholder": "کلید عمومی خود را اینجا بچسبانید", - "none": "هیچ کلید عمومی‌ای تنظیم نشده است" - } - }, - "identities": { - "title": "هویت‌های ارسال", - "description": "مدیریت آدرس‌های ایمیلی که می‌توانید از آنها ارسال کنید", - "identities_count": { - "label": "هویت‌های شما", - "description": "آدرس‌های ایمیل پیکربندی شده برای ارسال", - "count_zero": "بدون هویت", - "count_one": "۱ هویت", - "count_other": "{count} هویت" - }, - "manage": "مدیریت هویت‌ها", - "sub_addressing": { - "label": "زیرآدرس‌دهی", - "description": "استفاده از برچسب‌هایی مثل user+tag@domain.com برای سازمان‌دهی ایمیل‌های ورودی", - "learn_more": "بیشتر بدانید" - } - }, - "vacation": { - "title": "پاسخ‌گوی خودکار", - "description": "پاسخ خودکار به ایمیل‌های ورودی در زمان غیبت", - "loading": "در حال بارگذاری تنظیمات پاسخ‌گوی خودکار...", - "not_supported": "سرور ایمیل شما از پاسخ خودکار پشتیبانی نمی‌کند.", - "fetch_error": "بارگذاری تنظیمات پاسخ‌گوی خودکار ناموفق بود. لطفاً دوباره تلاش کنید.", - "status": { - "label": "پاسخ‌گوی خودکار", - "description": "ارسال پاسخ خودکار به افرادی که به شما ایمیل می‌زنند", - "active": "فعال", - "inactive": "غیرفعال" - }, - "date_range": { - "title": "بازه زمانی", - "description": "محدود کردن پاسخ خودکار به یک بازه زمانی خاص", - "start": "تاریخ شروع", - "start_description": "برای بدون محدودیت شروع خالی بگذارید", - "end": "تاریخ پایان", - "end_description": "برای بدون محدودیت پایان خالی بگذارید" - }, - "message": { - "title": "پیام پاسخ خودکار", - "description": "پیامی که به عنوان پاسخ ارسال خواهد شد", - "subject_label": "موضوع", - "subject_description": "خط موضوع پاسخ خودکار", - "subject_placeholder": "خارج از دفتر", - "body_label": "بدنه پیام", - "body_description": "محتوای پیام متنی ساده", - "body_placeholder": "از ایمیل شما متشکرم. من در حال حاضر خارج از دفتر هستم و پس از بازگشت پاسخ خواهم داد.", - "html_label": "پیام قالب‌بندی‌شده (HTML)", - "html_description": "یک نسخهٔ قالب‌بندی‌شده با پیوند و استایل اضافه کنید. گیرندگانی که برنامهٔ ایمیلشان نمی‌تواند آن را نمایش دهد، متن سادهٔ بالا را می‌بینند.", - "html_placeholder": "یک پاسخ خودکار قالب‌بندی‌شده بنویسید…" - }, - "preview": { - "title": "پیش‌نمایش", - "show": "نمایش پیش‌نمایش", - "hide": "مخفی کردن پیش‌نمایش" - }, - "save": "ذخیره تغییرات", - "saving": "در حال ذخیره...", - "warnings": { - "end_before_start": "تاریخ پایان باید بعد از تاریخ شروع باشد", - "start_in_past": "تاریخ شروع در گذشته است", - "empty_body": "بدنه پیام خالی است - گیرندگان پاسخ خالی دریافت می‌کنند", - "subject_too_long": "موضوع برای سرور بیش از حد طولانی است (حداکثر {max} بایت).", - "body_too_long": "پیام برای سرور بیش از حد طولانی است (حداکثر {max} بایت، همراه با قالب‌بندی)." - } - }, - "folders": { - "role_memos": "یادداشت‌ها", - "title": "پوشه‌ها", - "description": "مدیریت پوشه‌های ایمیل و تخصیص نقش‌های استاندارد", - "folder_list": "پوشه‌های شما", - "folder_list_description": "روی آیکون پوشه کلیک کنید تا سفارشی شود", - "standard_roles": "نقش‌های استاندارد پوشه", - "standard_roles_description": "تخصیص پوشه‌ها به نقش‌های استاندارد مانند صندوق ورودی، ارسال شده، زباله‌دان و غیره", - "role_inbox": "صندوق ورودی", - "role_drafts": "پیش‌نویس‌ها", - "role_sent": "ارسال شده", - "role_trash": "زباله‌دان", - "role_junk": "هرزنامه", - "role_archive": "بایگانی", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "هیچکدام", - "create_folder": "ایجاد پوشه", - "create_subfolder": "ایجاد زیرپوشه", - "reorder": "برای تغییر ترتیب بکشید", - "reorder_error": "تغییر ترتیب پوشه‌ها ناموفق بود", - "move_error": "انتقال پوشه ناموفق بود", - "subfolder_of": "داخل {name}", - "subfolder_name": "نام زیرپوشه", - "new_folder_name": "نام پوشه", - "rename": "تغییر نام", - "change_icon": "تغییر آیکون", - "delete": "حذف", - "confirm_delete": "آیا از حذف \"{name}\" اطمینان دارید؟ ایمیل‌های این پوشه به زباله‌دان منتقل می‌شوند.", - "create": "ایجاد", - "cancel": "انصراف", - "no_folders": "پوشه سفارشی نیست", - "cannot_delete_role": "نمی‌توان پوشه با نقش استاندارد را حذف کرد. ابتدا نقش را حذف کنید.", - "folder_created": "پوشه ایجاد شد", - "folder_renamed": "نام پوشه تغییر کرد", - "folder_deleted": "پوشه حذف شد", - "role_updated": "نقش پوشه به‌روزرسانی شد", - "error_create": "ایجاد پوشه ناموفق بود", - "error_rename": "تغییر نام پوشه ناموفق بود", - "error_delete": "حذف پوشه ناموفق بود", - "error_delete_has_children": "نمی‌توان پوشه را حذف کرد: همچنان زیرپوشه دارد. ابتدا آنها را حذف یا منتقل کنید.", - "error_delete_has_email": "نمی‌توان پوشه را حذف کرد: همچنان حاوی ایمیل است. ابتدا آنها را منتقل یا حذف کنید.", - "error_role": "به‌روزرسانی نقش پوشه ناموفق بود" - }, - "advanced": { - "title": "پیشرفته", - "description": "گزینه‌های پیشرفته و تنظیمات توسعه‌دهنده", - "debug_mode": { - "label": "حالت اشکال‌زدایی", - "description": "فعال کردن ثبت جزئیات برای عیب‌یابی" - }, - "debug_categories": { - "description": "انتخاب کنید کدام دسته‌بندی‌ها ثبت شوند. دسته‌بندی‌های غیرضروری را برای کاهش نویز کنسول غیرفعال کنید.", - "jmap": "کلاینت JMAP", - "jmap_description": "عملیات صندوق پستی، دریافت ایمیل و درخواست‌های پروتکل JMAP", - "calendar": "تقویم", - "calendar_description": "رویدادهای تقویم، وارد کردن و پیام‌های زمان‌بندی", - "tasks": "وظایف", - "tasks_description": "ایجاد، دریافت و به‌روزرسانی وظایف تقویم", - "auth": "احراز هویت", - "auth_description": "ورود، TOTP، تبادل توکن و مدیریت نشست", - "filters": "فیلترها", - "filters_description": "قوانین فیلتر Sieve و اسکریپت‌های پاسخ خودکار", - "email": "مشاهده ایمیل", - "email_description": "رندر ایمیل، پردازش TNEF و علامت‌گذاری خوانده شده", - "push": "اعلان‌های Push", - "push_description": "تنظیم و تحویل اعلان‌های push", - "contacts": "مخاطبین و دفترچه آدرس", - "contacts_description": "همگام‌سازی مخاطبین، عملیات دفترچه آدرس و فرستندگان مورد اعتماد" - }, - "settings_sync": { - "label": "همگام‌سازی تنظیمات", - "description": "همگام‌سازی تنظیمات بین مرورگرها و دستگاه‌ها" - }, - "sender_favicons": { - "label": "Favicon فرستنده", - "description": "نمایش آیکون وبسایت به عنوان تصویر پروفایل فرستندگان تجاری" - }, - "show_avatars_in_junk": { - "label": "نمایش آواتار در پوشه هرزنامه", - "description": "نمایش تصاویر پروفایل و favicon برای فرستندگان در پوشه هرزنامه." - }, - "keyboard_shortcuts": { - "label": "میانبرهای صفحه کلید", - "description": "مشاهده میانبرهای صفحه کلید موجود", - "button": "مشاهده میانبرها" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "بازنشانی تنظیمات", - "description": "بازگردانی همه تنظیمات به مقادیر پیش‌فرض", - "button": "بازنشانی به پیش‌فرض" - }, - "export_settings": { - "label": "خروجی تنظیمات", - "description": "دانلود تنظیمات به صورت JSON", - "button": "خروجی" - }, - "import_settings": { - "label": "وارد کردن تنظیمات", - "description": "بارگذاری تنظیمات از فایل JSON", - "button": "وارد کردن" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "برنامه‌های نوار کناری", - "description": "مدیریت برنامه‌ها و لینک‌های سفارشی در نوار کناری", - "keep_loaded": "نگه داشتن برنامه‌ها در حالت بارگذاری", - "keep_loaded_description": "برنامه‌های inline هنگام تعویض بین آنها در پس‌زمینه فعال بمانند تا از بارگذاری مجدد جلوگیری شود", - "managed_title": "ارائه‌شده توسط مدیر شما", - "managed_description": "این برنامه‌ها برای همه تنظیم شده‌اند و قابل تغییر نیستند.", - "manage_title": "برنامه‌های سفارشی", - "manage_description": "افزودن، ویرایش یا حذف برنامه‌های سفارشی از نوار کناری" - }, - "contacts": { - "title": "مخاطبین", - "description": "وارد کردن و خروجی مخاطبین", - "group_by_letter_label": "گروه‌بندی بر اساس حرف اول", - "sort_by_last_name_label": "مرتب‌سازی بر اساس نام خانوادگی", - "sort_by_last_name_description": "لیست مخاطبین را بر اساس نام خانوادگی مرتب کنید تا اعضای خانواده کنار هم نمایش داده شوند", - "group_by_letter_description": "نمایش هدرهای الفبایی در لیست مخاطبین", - "import_label": "وارد کردن مخاطبین", - "import_description": "وارد کردن مخاطبین از فایل vCard (.vcf)", - "export_label": "خروجی مخاطبین", - "export_description": "خروجی همه مخاطبین به صورت فایل vCard (.vcf)", - "manage_title": "دفترچه‌های آدرس", - "manage_description": "تغییر نام دفترچه‌های آدرس", - "no_address_books": "دفترچه آدرسی یافت نشد", - "categories_title": "دسته‌بندی‌ها", - "categories_description": "تغییر نام دسته‌بندی‌های مخاطبین", - "no_categories": "دسته‌بندی یافت نشد" - }, - "downloads": { - "title": "دانلودها", - "description": "سفارشی‌سازی نام فایل‌های ایمیل و پیوست دانلودی.", - "reset": "بازگردانی پیش‌فرض", - "preview": "پیش‌نمایش:", - "email_template": { - "label": "نام فایل ایمیل (.eml)", - "description": "قالب استفاده شده هنگام خروجی ایمیل یا کشیدن آن به فایل سیستم." - }, - "attachment_template": { - "label": "نام فایل پیوست", - "description": "قالب استفاده شده هنگام دانلود یا کشیدن پیوست." - }, - "bundle_template": { - "label": "نام فایل .zip چند ایمیل", - "description": "قالب استفاده شده هنگام خروجی چند ایمیل انتخاب شده به صورت فایل zip." - }, - "spaces": { - "label": "فاصله‌ها", - "description": "جایگزینی فاصله‌ها در نام فایل با کاراکتر دیگر.", - "keep": "حفظ فاصله‌ها", - "underscore": "جایگزینی با _", - "dash": "جایگزینی با -" - }, - "lowercase": { - "label": "حروف کوچک", - "description": "تبدیل کل نام فایل به حروف کوچک." - }, - "strip_diacritics": { - "label": "حذف اعراب‌گذاری", - "description": "تبدیل حروف اعراب‌دار به معادل ASCII (ä → a، é → e)." - }, - "collapse_separators": { - "label": "ادغام جداکننده‌های تکراری", - "description": "ادغام فاصله‌ها، زیرخط‌ها یا خط تیره‌های تکراری به یک کاراکتر." - }, - "after_export": { - "label": "بعد از خروجی", - "description": "انتقال اختیاری ایمیل بعد از خروجی به صورت .eml.", - "keep": "نگه داشتن در صندوق", - "archive": "انتقال به بایگانی", - "trash": "انتقال به زباله‌دان" - } - }, - "filters": { - "title": "فیلترهای ایمیل", - "description": "ایجاد قوانین برای مرتب‌سازی خودکار، برچسب‌گذاری و مدیریت ایمیل‌های ورودی", - "add_rule": "افزودن قانون", - "no_rules": "قانون فیلتری نیست", - "no_rules_description": "قوانینی برای سازمان‌دهی خودکار ایمیل‌های ورودی ایجاد کنید", - "vacation_active": "پاسخ‌گوی خودکار فعال است", - "vacation_active_description": "پاسخ خودکار برای پیام‌های ورودی فعال است", - "vacation_configure": "پیکربندی", - "edit_rule": "ویرایش قانون", - "new_rule": "قانون جدید", - "delete_rule": "حذف قانون", - "delete_confirm": "آیا از حذف این قانون اطمینان دارید؟", - "enable": "فعال کردن", - "disable": "غیرفعال کردن", - "raw_editor": "ویرایشگر خام Sieve", - "raw_editor_warning": "ویرایش اسکریپت خام Sieve ممکن است ویرایش بصری قوانین را مختل کند. تغییرات اینجا ویرایشگر بصری را بازنویسی می‌کند.", - "validate": "اعتبارسنجی", - "validation_success": "اسکریپت معتبر است", - "validation_error": "اسکریپت خطا دارد", - "save": "ذخیره قوانین", - "saving": "در حال ذخیره...", - "saved": "فیلترها با موفقیت ذخیره شدند", - "save_failed": "ذخیره فیلترها ناموفق بود", - "loading": "در حال بارگذاری فیلترها...", - "not_supported": "سرور ایمیل شما از فیلترهای ایمیل پشتیبانی نمی‌کند.", - "rule_name": "نام قانون", - "rule_name_placeholder": "مثال: مرتب‌سازی خبرنامه‌ها", - "match_all": "مطابقت با همه شرایط", - "match_any": "مطابقت با هر شرط", - "conditions": "شرایط", - "add_condition": "افزودن شرط", - "actions": "عملیات‌ها", - "add_action": "افزودن عملیات", - "stop_processing": "توقف پردازش قوانین بعدی", - "include_spam": "پیام‌های علامت‌خورده به‌عنوان هرزنامه هم منتقل شوند", - "attachment_type_placeholder": "مثال: pdf, doc, jpg", - "value_placeholder_multi": "مقدار (چندتا با کاما جدا شوند)", - "condition_fields": { - "attachment": "پیوست", - "from": "از", - "to": "به", - "cc": "رونوشت", - "subject": "موضوع", - "header": "هدر سفارشی", - "size": "حجم", - "body": "بدنه" - }, - "comparators": { - "has_any": "وجود دارد", - "has_type": "از نوع", - "contains": "شامل", - "not_contains": "شامل نیست", - "is": "دقیقاً برابر است با", - "not_is": "نیست", - "starts_with": "شروع می‌شود با", - "ends_with": "پایان می‌یابد با", - "matches": "مطابقت با الگو", - "greater_than": "بزرگتر از", - "less_than": "کوچکتر از" - }, - "action_types": { - "move": "انتقال به پوشه", - "copy": "کپی به پوشه", - "forward": "ارسال به", - "mark_read": "علامت‌گذاری خوانده شده", - "star": "ستاره‌دار کردن", - "add_label": "افزودن برچسب", - "discard": "دور انداختن (حذف بی‌صدا)", - "reject": "رد با پیام", - "keep": "نگه داشتن در صندوق ورودی", - "stop": "توقف پردازش" - }, - "move_to_folder": "انتخاب پوشه", - "copy_to_folder": "انتخاب پوشه", - "forward_to": "ارسال به آدرس ایمیل", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "نگه‌داشتن یک نسخه", - "forward_limit": "سقف بازارسال برای هر پیام در این سرور: {count}. بازارسال‌های اضافه نادیده گرفته می‌شوند.", - "reject_message": "پیام رد", - "reject_placeholder": "ایمیل شما رد شد", - "label_name": "نام برچسب", - "label_placeholder": "انتخاب برچسب", - "header_name": "نام هدر", - "header_placeholder": "مثال: X-Mailing-List", - "size_bytes": "حجم به بایت", - "size_placeholder": "مثال: 1000000", - "system_managed": "قانون مدیریت شده توسط سیستم", - "opaque_warning": "این اسکریپت خارج از ویرایشگر بصری ویرایش شده. فقط ویرایش خام Sieve در دسترس است.", - "open_sieve_editor": "باز کردن ویرایشگر خام Sieve", - "fetch_error": "بارگذاری فیلترها ناموفق بود", - "expanded_view": "نمای گسترده", - "expanded_view_description": "نمایش قوانین فیلتر با بلوک‌های شرط و عملیات جزئی", - "if": "اگر", - "then": "آنگاه", - "match_all_conditions": "همه مطابقت دارند", - "match_any_condition": "هر کدام مطابقت دارد", - "and": "و", - "or": "یا", - "cancel": "انصراف", - "confirm_delete": "حذف", - "rule_list": "قوانین فیلتر", - "drag_to_reorder": "کشیدن برای ترتیب‌دهی", - "match_type": "نوع مطابقت", - "reset_to_visual": "بازنشانی به ویرایشگر بصری", - "reset_warning": "این اسکریپت فعلی را دور می‌اندازد و از نو شروع می‌کند.", - "confirm_reset": "بازنشانی", - "validation_empty_name": "نام قانون الزامی است", - "validation_empty_conditions": "حداقل یک شرط با مقدار الزامی است", - "validation_empty_actions": "حداقل یک عملیات الزامی است", - "templates_section": "شروع از قالب", - "template_newsletters": "انتقال خبرنامه‌ها به پوشه", - "template_receipts": "بایگانی خودکار رسیدها", - "template_important": "علامت‌گذاری ایمیل‌های مهم", - "template_notifications": "فیلتر اعلان‌ها", - "sieve_editor": { - "title": "ویرایشگر اسکریپت Sieve", - "warning": "ویرایش اسکریپت خام Sieve ممکن است ویرایش بصری را مختل کند. تغییرات اینجا ویرایشگر بصری را بازنویسی می‌کند.", - "script_content": "اسکریپت Sieve", - "valid": "اسکریپت معتبر است", - "invalid": "اسکریپت خطا دارد", - "save_warning": "ذخیره قوانین بصری را بازنویسی می‌کند. این عملیات قابل بازگشت نیست. برای تأیید دوباره ذخیره را کلیک کنید.", - "validating": "در حال اعتبارسنجی...", - "validate": "اعتبارسنجی", - "cancel": "انصراف", - "save": "ذخیره", - "confirm_save": "تأیید ذخیره", - "validation_failed": "درخواست اعتبارسنجی ناموفق بود" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# شرط} other {# شرط}}", - "actions_count": "{count, plural, one {# عملیات} other {# عملیات}}" - }, - "origin_external": "خارجی", - "managed_by_tooltip": "مدیریت شده توسط {source}. در آن برنامه ویرایش کنید یا از ویرایشگر خام Sieve استفاده کنید." - }, - "templates": { - "title": "قالب‌های ایمیل", - "description": "ایجاد قالب‌های ایمیل قابل استفاده مجدد با متغیرهای جایگزین", - "add": "قالب جدید", - "edit": "ویرایش قالب", - "name": "نام قالب", - "name_placeholder": "مثال: ایمیل پیگیری", - "category": "دسته‌بندی", - "category_placeholder": "مثال: کاری، شخصی", - "subject": "موضوع", - "subject_placeholder": "خط موضوع ایمیل", - "body": "بدنه", - "body_placeholder": "محتوای بدنه ایمیل...", - "recipients_placeholder": "email@example.com", - "identity": "ارسال به عنوان", - "default_identity": "هویت پیش‌فرض", - "favorite": "مورد علاقه", - "cancel": "انصراف", - "create": "ایجاد", - "update": "به‌روزرسانی", - "confirm_delete": "حذف", - "no_templates": "هنوز قالبی نیست", - "manage": "مدیریت قالب‌ها", - "count": "{count, plural, one {# قالب} other {# قالب}}", - "export_import": "خروجی و وارد کردن", - "export_import_description": "پشتیبان‌گیری از قالب‌ها یا انتقال به دستگاه دیگر", - "export": "خروجی", - "import": "وارد کردن", - "validation": { - "empty": "نام قالب الزامی است", - "too_long": "نام قالب باید ۲۰۰ کاراکتر یا کمتر باشد" - } - }, - "files": { - "display": { - "title": "نمایش", - "description": "پیکربندی نحوه نمایش فایل‌ها و پوشه‌ها" - }, - "folder_layout": { - "label": "ناوبری پوشه", - "description": "انتخاب نحوه نمایش پوشه‌ها: داخلی با فایل‌ها یا درخت کناری", - "inline": "داخلی", - "sidebar": "نوار کناری" - }, - "default_view": { - "label": "نمای پیش‌فرض", - "description": "انتخاب بین چیدمان grid یا لیست", - "list": "لیست", - "grid": "شبکه‌ای" - }, - "default_sort": { - "label": "مرتب‌سازی پیش‌فرض", - "description": "انتخاب مرتب‌سازی پیش‌فرض فایل‌ها", - "name": "نام", - "size": "حجم", - "modified": "تاریخ تغییر" - }, - "sort_direction": { - "label": "جهت مرتب‌سازی", - "description": "انتخاب ترتیب صعودی یا نزولی", - "ascending": "صعودی", - "descending": "نزولی" - }, - "icons": { - "title": "آیکون‌ها", - "description": "پیکربندی ظاهر آیکون فایل" - }, - "show_icons": { - "label": "نمایش آیکون فایل", - "description": "نمایش آیکون کنار فایل‌ها و پوشه‌ها" - }, - "colored_icons": { - "label": "آیکون‌های رنگی", - "description": "استفاده از آیکون‌های رنگی به جای تک‌رنگ" - }, - "show_thumbnails": { - "label": "نمایش thumbnail", - "description": "نمایش پیش‌نمایش تصویر به جای آیکون برای فایل‌های تصویری" - }, - "behavior": { - "title": "رفتار", - "description": "پیکربندی رفتار مرورگر فایل" - }, - "show_hidden": { - "label": "نمایش فایل‌های مخفی", - "description": "نمایش فایل‌ها و پوشه‌هایی که با نقطه شروع می‌شوند" - }, - "preview": { - "label": "پیش‌نمایش" - } - } - }, - "errors": { - "page_error_title": "مشکلی پیش آمد", - "page_error_description": "با خطای غیرمنتظره‌ای مواجه شدیم. لطفاً دوباره تلاش کنید.", - "sidebar_error": "بارگذاری صندوق‌های پستی ممکن نیست", - "email_list_error": "بارگذاری ایمیل‌ها ممکن نیست", - "viewer_error_title": "نمایش ایمیل ممکن نیست", - "viewer_error_description": "مشکلی در رندر این ایمیل وجود داشت.", - "composer_error": "بارگذاری ویرایشگر ممکن نیست", - "settings_error_title": "تنظیمات در دسترس نیست", - "settings_error_description": "بارگذاری تنظیمات ممکن نیست.", - "try_again": "دوباره تلاش کنید", - "reload": "بارگذاری مجدد", - "reload_emails": "بارگذاری مجدد ایمیل‌ها", - "reload_settings": "بارگذاری مجدد تنظیمات", - "retry": "تلاش مجدد", - "go_home": "رفتن به صندوق ورودی" - }, - "context_menu": { - "reply": "پاسخ", - "reply_all": "پاسخ به همه", - "forward": "ارسال مجدد", - "mark_read": "علامت‌گذاری خوانده شده", - "mark_unread": "علامت‌گذاری خوانده نشده", - "star": "ستاره‌دار", - "unstar": "حذف ستاره", - "pin": "سنجاق کردن", - "unpin": "برداشتن سنجاق", - "move_to": "انتقال به...", - "archive": "بایگانی", - "delete": "حذف", - "mark_as_spam": "گزارش هرزنامه", - "not_spam": "هرزنامه نیست", - "tag": "برچسب", - "items_selected": "{count} ایمیل انتخاب شده", - "edit_draft": "ویرایش پیش‌نویس", - "cancel_scheduled_send": "لغو ارسال", - "reschedule_send": "باززمان‌بندی", - "cancel_and_edit": "لغو و ویرایش", - "cancel_and_compose_again": "لغو و نوشتن مجدد" - }, - "mailbox_context_menu": { - "mark_folder_read": "علامت‌گذاری پوشه خوانده شده", - "mark_folder_tree_read": "علامت‌گذاری پوشه و زیرپوشه‌ها", - "mark_all_folders_read": "علامت‌گذاری همه پوشه‌ها", - "new_subfolder": "زیرپوشه جدید...", - "new_folder": "پوشه جدید...", - "rename": "تغییر نام...", - "share": "اشتراک‌گذاری...", - "import_email": "وارد کردن .eml/.zip...", - "empty_folder": "خالی کردن پوشه", - "empty_folder_generic": "خالی کردن پوشه", - "delete_folder": "حذف پوشه", - "refresh": "بازنشانی", - "mark_all_confirm_title": "علامت‌گذاری همه", - "mark_all_confirm_message": "همه پیام‌های خوانده نشده علامت‌گذاری شوند؟", - "delete_confirm_title": "حذف پوشه", - "delete_confirm_message": "پوشه \"{name}\" حذف شود؟", - "prompt_new_subfolder": "نام زیرپوشه جدید", - "prompt_new_folder": "نام پوشه جدید", - "prompt_rename": "نام جدید پوشه", - "placeholder_folder_name": "نام پوشه", - "create": "ایجاد", - "rename_confirm": "تغییر نام", - "toast_marked_read": "علامت‌گذاری شد", - "toast_marked_read_count": "{count, plural, one {۱ پیام} other {# پیام}} علامت‌گذاری شد", - "toast_already_read": "پیام خوانده نشده نیست", - "toast_marked_all_read": "همه علامت‌گذاری شدند", - "toast_emptied": "پوشه خالی شد", - "toast_folder_created": "پوشه ایجاد شد", - "toast_folder_renamed": "نام تغییر کرد", - "toast_folder_deleted": "پوشه حذف شد", - "toast_error_mark_read": "خطای علامت‌گذاری", - "toast_error_empty": "خطای خالی کردن", - "toast_error_create": "خطای ایجاد", - "toast_error_rename": "خطای تغییر نام", - "toast_error_delete": "خطای حذف", - "toast_error_delete_has_children": "زیرپوشه دارد", - "toast_error_delete_has_email": "خالی نیست", - "delete_with_contents_title": "پوشه خالی نیست", - "delete_with_contents_message": "پوشه «{name}» هنوز شامل {count, plural, one {۱ پیام} other {# پیام}} است. پوشه و همه پیام‌های آن حذف شود؟ این عمل قابل بازگشت نیست.", - "delete_with_contents_confirm": "حذف پوشه و پیام‌ها" - }, - "shortcuts": { - "title": "میانبرهای صفحه کلید", - "tip": "هر زمان کلید ? را فشار دهید", - "sections": { - "navigation": "ناوبری", - "actions": "عملیات ایمیل", - "global": "سراسری", - "threads": "رشته‌ها", - "composer": "ویرایشگر" - }, - "navigation": { - "next_email": "ایمیل بعدی", - "previous_email": "ایمیل قبلی", - "open_email": "باز کردن ایمیل", - "close_email": "بستن / لغو انتخاب" - }, - "actions": { - "reply": "پاسخ", - "reply_all": "پاسخ به همه", - "forward": "ارسال مجدد", - "star": "تغییر ستاره", - "archive": "بایگانی", - "delete": "حذف", - "mark_unread": "علامت‌گذاری خوانده نشده", - "mark_read": "علامت‌گذاری خوانده شده", - "toggle_spam": "هرزنامه / غیر هرزنامه" - }, - "global": { - "compose": "ایمیل جدید", - "search": "تمرکز جستجو", - "help": "نمایش میانبرها", - "refresh": "بازنشانی ایمیل‌ها", - "select_all": "انتخاب همه", - "global_search": "جستجو در همه جا" - }, - "threads": { - "expand_collapse": "باز/بسته کردن رشته" - }, - "composer": { - "send": "ارسال ایمیل", - "schedule_send": "زمان‌بندی ارسال", - "template_picker": "انتخاب‌گر قالب" - } - }, - "threads": { - "messages_one": "{count} پیام", - "messages_other": "{count} پیام", - "messages_tooltip": "{count, plural, one {# پیام در این مکالمه} other {# پیام در این مکالمه}}", - "expand": "باز کردن مکالمه", - "collapse": "جمع کردن مکالمه", - "loading": "در حال بارگذاری مکالمه...", - "mark_read": "علامت‌گذاری مکالمه به عنوان خوانده شده", - "mark_unread": "علامت‌گذاری مکالمه به عنوان خوانده نشده", - "archive": "بایگانی مکالمه", - "delete": "حذف مکالمه", - "star": "ستاره‌دار کردن مکالمه", - "unstar": "حذف ستاره مکالمه", - "toggle_thread": "تغییر وضعیت رشته" - }, - "identities": { - "modal_title": "مدیریت هویت‌های ارسال", - "create_new": "ایجاد هویت جدید", - "edit_identity": "ویرایش هویت", - "delete_confirm": "حذف این هویت؟ قابل بازگشت نیست.", - "cannot_delete": "این هویت قابل حذف نیست", - "primary_identity": "اصلی", - "set_as_primary": "تنظیم به عنوان اصلی", - "no_identities": "هویتی یافت نشد", - "display": { - "reply_to": "پاسخ به:", - "bcc": "رونوشت مخفی:", - "signature": "امضا:", - "preview": "پیش‌نمایش:" - }, - "validation_errors": { - "invalid_emails": "ایمیل‌های نامعتبر: {emails}", - "unknown_error": "خطای ناشناس" - }, - "form": { - "name_label": "نام نمایشی", - "name_placeholder": "مثال: ایمیل کاری، شخصی", - "name_required": "نام الزامی است", - "email_label": "آدرس ایمیل", - "email_placeholder": "your.email@example.com", - "email_required": "ایمیل الزامی است", - "email_invalid": "ایمیل معتبر وارد کنید", - "email_immutable": "پس از ایجاد قابل تغییر نیست", - "reply_to_label": "پاسخ به (اختیاری)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "رونوشت مخفی خودکار (اختیاری)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "امضای متنی", - "html_signature_label": "امضای HTML", - "signature_byte_counter": "{bytes} / {max} بایت", - "signature_byte_limit_reached": "حد سرور", - "save": "ذخیره هویت", - "cancel": "انصراف", - "creating": "در حال ایجاد...", - "updating": "در حال به‌روزرسانی..." - }, - "sub_address": { - "button_tooltip": "استفاده از زیرآدرس", - "popover_title": "افزودن برچسب زیرآدرس", - "tag_input_placeholder": "برچسب را وارد کنید", - "preview_label": "پیش‌نمایش:", - "recent_tags": "برچسب‌های اخیر", - "suggested_tags": "پیشنهادی", - "use_address": "استفاده از این آدرس", - "invalid_tag": "فقط حروف و خط تیره", - "tag_too_long": "حداکثر ۳۰ کاراکتر", - "help_text": "ایمیل به user{delimiter}tag@domain.com به صندوق ورودی می‌رسد", - "validation": { - "empty": "برچسب نمی‌تواند خالی باشد", - "too_long": "حداکثر {max} کاراکتر", - "invalid_chars": "فقط حروف، اعداد و خط تیره" - } - }, - "badge": { - "sent_via": "از طریق", - "sub_address_tag": "ارسال با زیرآدرس: {tag}", - "identity_name": "ارسال با هویت: {name}", - "identity_short": "از طریق {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "حذف", - "delete_confirm_title": "حذف هویت" - }, - "templates": { - "picker_title": "انتخاب قالب", - "search_placeholder": "جستجوی قالب‌ها...", - "section_favorites": "مورد علاقه‌ها", - "section_recent": "اخیر", - "section_uncategorized": "سایر", - "no_templates": "هنوز قالبی نیست", - "no_results": "قالبی یافت نشد", - "fill_placeholders": "پر کردن مقادیر جایگزین", - "enter_value": "یک مقدار وارد کنید...", - "preview": "پیش‌نمایش", - "insert_with_values": "درج با مقادیر", - "insert_raw": "درج خام", - "copy_suffix": "(کپی)", - "placeholder": "متغیر", - "placeholders": { - "recipient_name": "نام گیرنده", - "company": "نام شرکت", - "date": "تاریخ فعلی", - "day_of_week": "روز هفته", - "sender_name": "نام شما" - } - }, - "contacts": { - "title": "مخاطبین", - "search_placeholder": "جستجوی مخاطبین...", - "create_new": "مخاطب جدید", - "no_category": "بدون دسته‌بندی", - "rename_category": "تغییر نام دسته‌بندی", - "category_name_label": "نام دسته‌بندی", - "category_renamed": "نام دسته‌بندی تغییر کرد", - "category_rename_failed": "تغییر نام دسته‌بندی ناموفق بود", - "category_added": "مخاطب به {name} اضافه شد", - "category_added_plural": "{count} مخاطب به {name} اضافه شد", - "empty_state": "هنوز مخاطبی نیست", - "empty_state_title": "هنوز مخاطبی نیست", - "empty_state_subtitle": "اولین مخاطب خود را ایجاد کنید یا از فایل vCard وارد کنید", - "empty_search": "مخاطبی با این جستجو مطابقت ندارد", - "empty_search_hint": "عبارت جستجوی دیگری را امتحان کنید", - "empty_filtered": "مخاطبی با این فیلترها مطابقت ندارد", - "empty_filtered_hint": "فیلترها را تنظیم یا پاک کنید", - "clear_search": "پاک کردن جستجو", - "import_vcard": "وارد کردن vCard", - "delete_confirm_title": "حذف مخاطب", - "delete_confirm": "آیا از حذف این مخاطب اطمینان دارید؟", - "local_mode": "مخاطبین به صورت محلی ذخیره می‌شوند (سرور از JMAP Contacts پشتیبانی نمی‌کند)", - "back_to_contacts": "بازگشت به مخاطبین", - "back_to_email": "بازگشت به ایمیل", - "open_categories": "باز کردن دسته‌بندی‌ها", - "tabs": { - "all": "همه", - "groups": "گروه‌ها" - }, - "shared": { - "title": "اشتراکی" - }, - "address_books": { - "create": "دفترچه آدرس جدید", - "created": "دفترچه آدرس ایجاد شد", - "create_failed": "ایجاد دفترچه آدرس ناموفق بود", - "title": "دفترچه‌های آدرس من", - "shared_prefix": "اشتراکی: {name}", - "moved": "مخاطب به {name} منتقل شد", - "moved_plural": "{count} مخاطب به {name} منتقل شد", - "move_failed": "انتقال مخاطب ناموفق بود", - "address_book": "دفترچه آدرس", - "rename": "تغییر نام دفترچه آدرس", - "name_label": "نام دفترچه آدرس", - "renamed": "نام دفترچه آدرس تغییر کرد", - "rename_failed": "تغییر نام دفترچه آدرس ناموفق بود", - "default": "پیش‌فرض", - "set_default": "تنظیم به عنوان پیش‌فرض", - "default_updated": "دفترچه آدرس پیش‌فرض به‌روزرسانی شد", - "set_default_failed": "تنظیم دفترچه آدرس پیش‌فرض ناموفق بود", - "manage": "مدیریت دفترچه‌های آدرس", - "share": "اشتراک‌گذاری دفترچه آدرس", - "new_contact_in_book": "مخاطب جدید در این دفترچه آدرس", - "delete": "حذف دفترچه آدرس", - "confirm_delete": "حذف \"{name}\"؟ همه مخاطبین این دفترچه آدرس حذف خواهند شد.", - "deleted": "دفترچه آدرس حذف شد", - "delete_failed": "حذف دفترچه آدرس ناموفق بود" - }, - "detail": { - "emails": "آدرس‌های ایمیل", - "phones": "شماره تلفن‌ها", - "organizations": "سازمان‌ها", - "addresses": "آدرس‌ها", - "notes": "یادداشت‌ها", - "titles": "عناوین و نقش‌ها", - "online_services": "سرویس‌های آنلاین", - "anniversaries": "سالگردها", - "personal_info": "اطلاعات شخصی", - "languages": "زبان‌ها", - "categories": "دسته‌بندی‌ها", - "related_contacts": "مخاطبین مرتبط", - "crypto_keys": "کلیدهای رمزنگاری", - "cert_issuer": "صادرکننده", - "cert_expires": "انقضا", - "cert_expired": "منقضی شده", - "cert_fingerprint": "اثر انگشت", - "cert_algorithm": "الگوریتم", - "import_to_smime": "وارد کردن به S/MIME", - "cert_already_imported": "قبلاً به S/MIME وارد شده", - "cert_imported": "گواهی به مخزن S/MIME وارد شد", - "cert_import_failed": "وارد کردن گواهی ناموفق بود", - "no_contact_selected": "یک مخاطب برای مشاهده جزئیات انتخاب کنید", - "compose_email": "نوشتن ایمیل", - "copy_email": "کپی ایمیل", - "copy_phone": "کپی شماره تلفن", - "copy_url": "کپی آدرس", - "copied": "در کلیپ‌بورد کپی شد", - "copy_failed": "کپی در کلیپ‌بورد ناموفق بود", - "created": "ایجاد شده", - "updated": "آخرین به‌روزرسانی", - "timezone": "منطقه زمانی", - "anniversary_birth": "تولد", - "anniversary_death": "درگذشت", - "anniversary_wedding": "سالگرد", - "anniversary_other": "سایر", - "personal_expertise": "تخصص", - "personal_hobby": "سرگرمی", - "personal_interest": "علاقه", - "personal_other": "سایر", - "gender": "جنسیت", - "gender_masculine": "مرد", - "gender_feminine": "زن", - "gender_other": "سایر", - "gender_none": "نامرتبط", - "gender_unknown": "ناشناس", - "calendar": "تقویم", - "calendar_uri": "آدرس تقویم", - "scheduling_uri": "آدرس زمان‌بندی", - "freebusy_uri": "آدرس Free/Busy", - "section_contact": "جزئیات مخاطب", - "section_work": "کاری", - "section_personal": "شخصی", - "email_default_label": "ایمیل", - "phone_default_label": "تلفن", - "address_default_label": "آدرس", - "online_service_default_label": "آنلاین", - "organization_label": "سازمان", - "title_label": "عنوان", - "role_label": "نقش", - "language_label": "زبان", - "related_default_label": "مرتبط", - "more_actions": "عملیات بیشتر", - "age_years": "{count, plural, one {۱ ساله} other {# ساله}}", - "years_since": "{count, plural, one {۱ سال} other {# سال}}" - }, - "activity": { - "recent_emails": "ایمیل‌های اخیر", - "upcoming_events": "رویدادهای پیش رو", - "no_emails": "ایمیل اخیری نیست", - "no_events": "رویداد پیش رویی نیست", - "no_subject": "(بدون موضوع)", - "no_title": "(بدون عنوان)", - "load_failed": "بارگذاری ناموفق بود", - "unknown_sender": "فرستنده ناشناس", - "all_day": "تمام روز" - }, - "form": { - "create_title": "مخاطب جدید", - "edit_title": "ویرایش مخاطب", - "section_address_book": "دفترچه", - "select_address_book": "انتخاب دفترچه...", - "section_identity": "نام و هویت", - "contact_type": "نوع مخاطب", - "type_person": "شخص", - "type_organization": "سازمان", - "section_work": "کار و سازمان", - "prefix": "پیشوند", - "prefix_placeholder": "دکتر، مهندس", - "given_name": "نام", - "middle_name": "نام میانی", - "surname": "نام خانوادگی", - "suffix": "پسوند", - "suffix_placeholder": "جونیور، سینیور", - "nickname": "نام مستعار", - "nickname_placeholder": "نام مستعار", - "email": "ایمیل", - "email_placeholder": "email@example.com", - "phone": "تلفن", - "phone_placeholder": "+98 912 345 6789", - "phone_type": "نوع", - "phone_voice": "تلفن", - "phone_cell": "موبایل", - "phone_fax": "فکس", - "phone_pager": "پیجر", - "phone_video": "ویدیو", - "phone_text": "پیامک", - "organization": "سازمان", - "organization_placeholder": "نام شرکت", - "department": "دپارتمان", - "department_placeholder": "دپارتمان", - "job_title": "عنوان شغلی", - "job_title_placeholder": "مثال: مهندس نرم‌افزار", - "role": "نقش", - "role_placeholder": "مثال: سرپرست تیم", - "addresses": "آدرس‌ها", - "add_address": "افزودن آدرس", - "street": "خیابان", - "city": "شهر", - "region": "استان / منطقه", - "postcode": "کد پستی", - "country": "کشور", - "online_services": "سرویس‌های آنلاین", - "add_online_service": "افزودن سرویس آنلاین", - "url_placeholder": "https://...", - "service_placeholder": "سرویس", - "anniversaries": "سالگردها", - "add_anniversary": "افزودن تاریخ", - "anniversary_birth": "تولد", - "anniversary_wedding": "سالگرد", - "anniversary_death": "یادبود", - "anniversary_other": "سایر", - "personal_info": "اطلاعات شخصی", - "add_personal_info": "افزودن مورد", - "personal_info_placeholder": "مثال: عکاسی", - "personal_expertise": "تخصص", - "personal_hobby": "سرگرمی", - "personal_interest": "علاقه", - "personal_other": "سایر", - "level": "سطح", - "level_high": "بالا", - "level_medium": "متوسط", - "level_low": "پایین", - "categories": "دسته‌بندی‌ها", - "categories_placeholder": "مثال: خانواده، دوستان، همکاران", - "categories_hint": "برای جستجو یا افزودن دسته‌بندی تایپ کنید", - "category_add": "افزودن", - "note": "یادداشت‌ها", - "note_placeholder": "افزودن یادداشت...", - "gender": "جنسیت", - "gender_sex": "جنسیت", - "gender_male": "مرد", - "gender_female": "زن", - "gender_other": "سایر", - "gender_none": "نامرتبط", - "gender_unknown": "ناشناس", - "gender_identity": "هویت جنسیتی", - "gender_identity_placeholder": "هویت جنسیتی...", - "calendar": "تقویم", - "calendar_uri": "آدرس تقویم", - "scheduling_uri": "آدرس زمان‌بندی", - "freebusy_uri": "آدرس Free/Busy", - "context_work": "کاری", - "context_private": "خصوصی", - "add_email": "افزودن ایمیل", - "add_phone": "افزودن تلفن", - "save": "ذخیره", - "cancel": "انصراف", - "creating": "در حال ایجاد...", - "updating": "در حال به‌روزرسانی...", - "name_required": "نام، نام خانوادگی یا سازمان را وارد کنید", - "email_invalid": "لطفاً یک آدرس ایمیل معتبر وارد کنید", - "email_error_inline": "فرمت ایمیل نامعتبر است", - "save_failed": "ذخیره مخاطب ناموفق بود", - "delete": "حذف", - "upload_photo": "بارگذاری عکس", - "remove_photo": "حذف عکس", - "photo_hint": "JPG یا PNG، حداکثر ۱۰ مگابایت. تغییر اندازه داده می‌شود.", - "photo_too_large": "تصویر بیش از حد بزرگ است (حداکثر ۱۰ مگابایت)", - "photo_invalid": "فایل تصویر نامعتبر است", - "change_photo": "تغییر" - }, - "groups": { - "create": "گروه جدید", - "edit": "ویرایش گروه", - "empty": "هنوز گروهی نیست", - "delete_confirm_title": "حذف گروه", - "delete_confirm": "آیا از حذف این گروه اطمینان دارید؟", - "name_label": "نام گروه", - "name_placeholder": "مثال: تیم، خانواده", - "name_required": "نام گروه الزامی است", - "save_failed": "ذخیره گروه ناموفق بود", - "members_label": "اعضا", - "search_members": "جستجوی مخاطبین برای افزودن...", - "no_members": "عضوی در این گروه نیست", - "member_count": "{count, plural, =0 {بدون عضو} one {۱ عضو} other {# عضو}}", - "send_email": "ارسال ایمیل به گروه", - "send_email_to": "به", - "send_email_cc": "رونوشت", - "send_email_bcc": "رونوشت مخفی", - "no_member_emails": "این گروه عضوی با آدرس ایمیل ندارد." - }, - "import": { - "title": "وارد کردن مخاطبین", - "drop_hint": "برای انتخاب فایل vCard کلیک کنید", - "file_types": "فایل‌های vcf. یا vcard.", - "no_contacts": "مخاطبی در فایل یافت نشد", - "parse_error": "پردازش فایل vCard ناموفق بود", - "found": "{count, plural, one {۱ مخاطب یافت شد} other {# مخاطب یافت شد}}", - "duplicate": "تکراری", - "select_all": "انتخاب همه", - "deselect_all": "لغو انتخاب همه", - "selected": "{count, plural, one {۱ انتخاب شده} other {# انتخاب شده}}", - "import_button": "وارد کردن", - "importing": "در حال وارد کردن...", - "success": "{count, plural, one {۱ مخاطب وارد شد} other {# مخاطب وارد شد}}", - "failed": "وارد کردن ناموفق بود", - "close": "بستن", - "file_too_large": "حجم فایل بیش از حد است (حداکثر ۵ مگابایت)" - }, - "export": { - "title": "خروجی مخاطبین", - "success": "{count, plural, one {۱ مخاطب خروجی گرفته شد} other {# مخاطب خروجی گرفته شد}}" - }, - "bulk": { - "selected": "{count, plural, one {۱ انتخاب شده} other {# انتخاب شده}}", - "select_all": "انتخاب همه", - "delete": "حذف", - "delete_confirm_title": "حذف مخاطبین", - "delete_confirm": "حذف {count, plural, one {۱ مخاطب} other {# مخاطب}}؟", - "deleted": "{count, plural, one {۱ مخاطب حذف شد} other {# مخاطب حذف شد}}", - "add_to_group": "افزودن به گروه", - "choose_group": "انتخاب گروه", - "adding_contacts": "افزودن {count, plural, one {۱ مخاطب} other {# مخاطب}}", - "added_to_group": "مخاطبین به گروه اضافه شدند", - "export": "خروجی", - "clear": "پاک کردن انتخاب" - }, - "toast": { - "created": "مخاطب ایجاد شد", - "updated": "مخاطب به‌روزرسانی شد", - "deleted": "مخاطب حذف شد", - "error_create": "ایجاد مخاطب ناموفق بود", - "error_update": "به‌روزرسانی مخاطب ناموفق بود", - "error_delete": "حذف مخاطب ناموفق بود" - }, - "context_menu": { - "open": "باز کردن", - "edit": "ویرایش", - "send_email": "ارسال ایمیل", - "add_to_group": "افزودن به گروه", - "export_vcard": "خروجی vCard", - "delete": "حذف", - "call": "تماس", - "duplicate": "کپی", - "print": "چاپ" - }, - "filters": { - "toggle": "فیلترها", - "select": "انتخاب", - "clear": "پاک کردن", - "close": "بستن", - "title": "فیلترهای پیشرفته", - "organization": "شرکت", - "organization_placeholder": "مثال: شرکت نمونه", - "job_title": "عنوان شغلی", - "job_title_placeholder": "مثال: طراح", - "location": "مکان", - "location_placeholder": "شهر یا کشور", - "email_domain": "دامنه ایمیل", - "email_domain_placeholder": "example.com", - "birthday_month": "تولد در", - "any_month": "هر ماه", - "has_email": "دارای ایمیل", - "has_phone": "دارای تلفن", - "has_photo": "دارای عکس", - "sort_by": "مرتب‌سازی بر اساس", - "sort_first_name": "نام", - "sort_last_name": "نام خانوادگی" - } - }, - "calendar": { - "title": "تقویم", - "back_to_email": "بازگشت به ایمیل", - "back_to_month": "بازگشت به ماه", - "my_calendars": "تقویم‌ها", - "birthday_calendar": "تولدها", - "mini_calendar_change": "برای تغییر ماه کلیک کنید", - "views": { - "month": "ماه", - "week": "هفته", - "day": "روز", - "agenda": "برنامه", - "today": "امروز", - "month_hint": "ماه (m)", - "week_hint": "هفته (w)", - "day_hint": "روز (d)", - "agenda_hint": "برنامه (a)", - "tasks": "وظایف", - "tasks_hint": "وظایف (k)" - }, - "events": { - "create": "ایجاد رویداد", - "edit": "ویرایش رویداد", - "delete": "حذف رویداد", - "details": "جزئیات رویداد", - "no_events": "رویدادی نیست", - "all_day": "تمام روز", - "more": "+{count} بیشتر", - "no_title": "(بدون عنوان)", - "resize": "تغییر اندازه رویداد", - "duplicate": "کپی", - "today_header": "امروز", - "tomorrow_header": "فردا", - "agenda_show_earlier": "نمایش رویدادهای قبلی", - "agenda_loading": "در حال بارگذاری…", - "agenda_range_start": "رویدادها از {date}", - "agenda_range_end": "رویدادها تا {date}", - "export_ics": "خروجی .ics", - "copy_title": "کپی عنوان", - "copy_link": "کپی لینک جلسه", - "new_event": "رویداد جدید", - "new_all_day_event": "رویداد تمام روز جدید", - "new_task": "وظیفه جدید", - "go_to_today": "رفتن به امروز" - }, - "detail": { - "add_note": "افزودن یادداشت...", - "save_note": "ذخیره", - "note_saved": "یادداشت اضافه شد", - "open_link": "باز کردن لینک", - "meeting_link": "لینک جلسه", - "tentative": "احتمالی", - "cancelled": "لغو شده", - "delete_confirm": "این رویداد حذف شود؟" - }, - "form": { - "title": "عنوان", - "description": "توضیحات", - "location": "مکان", - "meeting_link": "لینک جلسه", - "start_date": "تاریخ شروع", - "end_date": "تاریخ پایان", - "start_time": "زمان شروع", - "end_time": "زمان پایان", - "all_day_event": "رویداد تمام روز", - "calendar_select": "تقویم", - "save": "ذخیره", - "cancel": "انصراف", - "delete_confirm": "آیا از حذف این رویداد اطمینان دارید؟", - "color": "رنگ" - }, - "participants": { - "availability": { - "title": "در دسترس بودن", - "free": "در دسترس", - "busy": "مشغول", - "tentative": "احتمالی", - "unknown": "کاربر این سرور نیست", - "checking": "در حال بررسی…" - }, - "title": "شرکت‌کنندگان", - "add": "افزودن شرکت‌کننده", - "organizer": "برگزارکننده", - "attendee": "شرکت‌کننده", - "accepted": "پذیرفته شد", - "declined": "رد شد", - "tentative": "احتمالی", - "needs_action": "نیاز به اقدام", - "remove": "حذف", - "edit": "ویرایش", - "email_placeholder": "آدرس ایمیل را وارد کنید یا مخاطبین را جستجو کنید", - "send_invitations": "ارسال دعوت‌نامه به شرکت‌کنندگان", - "status_summary": "{accepted} پذیرفته، {pending} در انتظار", - "invited_by": "دعوت از طرف {name}", - "respond_below": "با دکمه‌های زیر پاسخ دهید", - "rsvp_label": "پاسخ شما", - "cancel_notification": "شرکت‌کنندگان از لغو مطلع خواهند شد", - "you_organizer": "شما برگزارکننده هستید", - "you_attendee": "شما شرکت‌کننده هستید", - "no_participants": "شرکت‌کننده‌ای نیست", - "count": "{count, plural, one {# شرکت‌کننده} other {# شرکت‌کننده}}" - }, - "recurrence": { - "title": "تکرار", - "none": "تکرار نمی‌شود", - "daily": "روزانه", - "weekly": "هفتگی", - "monthly": "ماهانه", - "yearly": "سالیانه", - "every_n_days": "هر {count} روز", - "every_n_weeks": "هر {count} هفته", - "every_n_months": "هر {count} ماه", - "until": "تا", - "occurrences": "{count} بار تکرار", - "custom": "سفارشی…", - "edit_custom": "ویرایش تکرار سفارشی", - "every_n_years": "هر {count} سال", - "on_days": "در {days}", - "on_day_n": "در روز {day}", - "on_the_nth": "در {nth} {day}", - "in_month": "در {month}", - "nth_1": "اول", - "nth_2": "دوم", - "nth_3": "سوم", - "nth_4": "چهارم", - "nth_last": "آخر", - "editor_freq_day": "روز", - "editor_freq_week": "هفته", - "editor_freq_month": "ماه", - "editor_freq_year": "سال", - "editor_repeats_on": "تکرار در", - "editor_every": "هر", - "editor_unit_days": "روز", - "editor_unit_weeks": "هفته", - "editor_unit_months": "ماه", - "editor_unit_years": "سال", - "editor_on_day": "در روز", - "editor_on_the": "در", - "editor_in": "در", - "editor_ends": "پایان", - "editor_never": "هرگز", - "editor_ends_on": "در تاریخ", - "editor_ends_after": "بعد از", - "editor_occurrences": "بار" - }, - "recurrence_scope": { - "edit_title": "ویرایش رویداد تکراری", - "delete_title": "حذف رویداد تکراری", - "description": "این یک رویداد تکراری است. کدام رویدادها را می‌خواهید تغییر دهید؟", - "this_event": "فقط این رویداد", - "this_and_future": "این رویداد و رویدادهای بعدی", - "all_events": "همه رویدادها", - "cancel": "انصراف", - "save": "ذخیره", - "delete": "حذف", - "rsvp_title": "پاسخ به رویداد تکراری", - "rsvp_description": "این یک رویداد تکراری است. پاسخ شما برای کدام رویدادها اعمال شود؟", - "respond": "پاسخ" - }, - "alerts": { - "title": "یادآورها", - "none": "بدون یادآور", - "at_time": "در زمان رویداد", - "minutes_before": "{count, plural, one {# دقیقه قبل} other {# دقیقه قبل}}", - "hours_before": "{count, plural, one {# ساعت قبل} other {# ساعت قبل}}", - "days_before": "{count, plural, one {# روز قبل} other {# روز قبل}}", - "weeks_before": "{count, plural, one {# هفته قبل} other {# هفته قبل}}", - "unit_minutes_before": "دقیقه قبل", - "unit_hours_before": "ساعت قبل", - "unit_days_before": "روز قبل", - "unit_weeks_before": "هفته قبل", - "add": "افزودن یادآور", - "remove": "حذف یادآور", - "amount": "مقدار یادآور", - "unit": "واحد یادآور" - }, - "settings": { - "title": "تنظیمات تقویم", - "default_view": "نمای پیش‌فرض", - "week_starts_on": "شروع هفته", - "time_format": "فرمت زمان", - "default_calendar": "تقویم پیش‌فرض", - "default_reminder": "یادآور پیش‌فرض", - "time_format_12h": "۱۲ ساعته", - "time_format_24h": "۲۴ ساعته", - "notifications_enabled": "اعلان‌های رویداد", - "notifications_enabled_desc": "نمایش هشدار برای رویدادهای تقویم پیش رو", - "notification_sound": "صدای اعلان", - "notification_sound_desc": "پخش صدا برای هشدارهای تقویم", - "invitation_parsing": "پردازش دعوت‌نامه‌های ایمیل", - "invitation_parsing_desc": "تشخیص دعوت‌نامه‌های تقویم در پیوست ایمیل", - "show_time_in_month_view": "نمایش زمان در نمای ماه", - "show_time_in_month_view_desc": "نمایش زمان رویدادها در نمای ماه تقویم", - "show_week_numbers": "نمایش شماره هفته", - "show_week_numbers_desc": "نمایش شماره هفته در تقویم کوچک", - "calendar_free_scroll": "پیمایش آزاد", - "calendar_free_scroll_desc": "پیمایش پیوسته در ماه‌ها، هفته‌ها و روزها به جای یک بازه در هر بار", - "enable_tasks": "فعال کردن وظایف", - "enable_tasks_desc": "نمایش نمای وظایف در تقویم برای مدیریت کارها", - "show_tasks_on_calendar": "نمایش وظایف روی تقویم", - "show_tasks_on_calendar_desc": "نمایش برچسب وظایف در نماهای روز و هفته", - "hover_preview": "پیش‌نمایش هاور رویداد", - "hover_preview_desc": "نمایش popover جزئیات هنگام هاور روی رویدادهای تقویم", - "hover_preview_instant": "بلافاصله", - "hover_preview_delay_500ms": "تأخیر ۰.۵ ثانیه", - "hover_preview_delay_1s": "تأخیر ۱ ثانیه", - "hover_preview_delay_2s": "تأخیر ۲ ثانیه", - "hover_preview_off": "غیرفعال", - "organizer_identity": "سازمان‌دهی دعوت‌ها به‌عنوان", - "organizer_identity_desc": "آدرس تقویمی که هنگام دعوت افراد به رویدادها به‌عنوان برگزارکننده استفاده می‌شود.", - "organizer_identity_failed": "تغییر آدرس برگزارکننده ناموفق بود", - "show_birthday_calendar": "تقویم تولد مخاطبین", - "show_birthday_calendar_desc": "نمایش تقویم مجازی با تولدهای مخاطبین" - }, - "days": { - "monday": "دوشنبه", - "tuesday": "سه‌شنبه", - "wednesday": "چهارشنبه", - "thursday": "پنج‌شنبه", - "friday": "جمعه", - "saturday": "شنبه", - "sunday": "یکشنبه", - "mon": "د", - "tue": "س", - "wed": "چ", - "thu": "پ", - "fri": "ج", - "sat": "ش", - "sun": "ی" - }, - "months": { - "jan": "ژانویه", - "feb": "فوریه", - "mar": "مارس", - "apr": "آوریل", - "may": "مه", - "jun": "ژوئن", - "jul": "ژوئیه", - "aug": "اوت", - "sep": "سپتامبر", - "oct": "اکتبر", - "nov": "نوامبر", - "dec": "دسامبر", - "far": "فروردین", - "ord": "اردیبهشت", - "kho": "خرداد", - "tir": "تیر", - "mor": "مرداد", - "sha": "شهریور", - "meh": "مهر", - "aba": "آبان", - "aza": "آذر", - "dey": "دی", - "bah": "بهمن", - "esf": "اسفند" - }, - "notifications": { - "event_created": "رویداد ایجاد شد", - "event_updated": "رویداد به‌روزرسانی شد", - "event_deleted": "رویداد حذف شد", - "calendar_created": "تقویم ایجاد شد", - "calendar_deleted": "تقویم حذف شد", - "event_move_error": "انتقال رویداد ناموفق بود", - "event_resize_error": "تغییر اندازه رویداد ناموفق بود", - "alert_title": "رویداد پیش رو", - "alert_now": "همین الان شروع می‌شود", - "alert_in_minutes": "{count} دقیقه دیگر", - "invitation_sent": "دعوت‌نامه‌ها ارسال شد", - "invitations_denied_title": "ارسال دعوت‌نامه‌ها ممکن نیست", - "invitations_denied": "سرور از ارسال دعوت‌نامه‌ها خودداری کرد: {reason}. رویداد بدون ارسال آن‌ها ذخیره شود؟", - "save_without_invitations": "ذخیره بدون دعوت‌نامه", - "rsvp_updated": "پاسخ به‌روزرسانی شد", - "rsvp_error": "به‌روزرسانی پاسخ ناموفق بود", - "event_duplicated": "رویداد کپی شد", - "event_error": "ذخیره رویداد ناموفق بود", - "task_due": "موعد وظیفه", - "event_exported": "رویداد خروجی گرفته شد", - "title_copied": "عنوان کپی شد", - "link_copied": "لینک کپی شد" - }, - "status": { - "loading_calendars": "در حال بارگذاری تقویم‌ها...", - "loading_events": "در حال بارگذاری رویدادها..." - }, - "quick_create": { - "placeholder": "عنوان رویداد جدید", - "aria_label": "ایجاد سریع رویداد" - }, - "nav_prev": "قبلی", - "nav_next": "بعدی", - "nav_open_menu": "باز کردن منو", - "import": { - "title": "وارد کردن تقویم", - "tab_file": "فایل", - "tab_url": "آدرس", - "select_file": "انتخاب فایل .ics", - "drop_file": "یا فایل را اینجا رها کنید", - "supported_formats": "پشتیبانی از فایل‌های iCalendar (.ics)", - "url_description": "آدرس فید خارجی iCalendar (.ics) را برای وارد کردن رویدادها وارد کنید.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "پشتیبانی از آدرس‌های iCalendar (.ics)", - "fetch": "دریافت", - "invalid_url": "لطفاً یک آدرس معتبر وارد کنید", - "url_fetch_failed": "دریافت تقویم از آدرس ناموفق بود", - "parsing": "در حال پردازش فایل تقویم...", - "parsed_events": "{count} رویداد یافت شد", - "no_events": "رویدادی در فایل یافت نشد", - "select_all": "انتخاب همه", - "deselect_all": "لغو انتخاب همه", - "target_calendar": "وارد کردن به تقویم", - "import_button": "وارد کردن انتخاب‌شده‌ها", - "importing": "در حال وارد کردن رویدادها...", - "success": "{count} رویداد با موفقیت وارد شد", - "error": "وارد کردن تقویم ناموفق بود", - "file_too_large": "حجم فایل بیش از ۱۰ مگابایت است", - "invalid_format": "فرمت فایل تقویم نامعتبر است" - }, - "webcal_action": { - "title": "باز کردن لینک تقویم", - "description": "چگونه می‌خواهید از \"{name}\" استفاده کنید؟", - "import_title": "وارد کردن یکباره", - "import_description": "رویدادها را همین حالا دریافت و در یکی از تقویم‌هایتان کپی کنید.", - "subscribe_title": "اشتراک", - "subscribe_description": "این تقویم را به صورت خودکار به عنوان تقویم جداگانه همگام نگه دارید.", - "cancel": "انصراف" - }, - "management": { - "title": "مدیریت تقویم", - "description": "ایجاد، تغییر نام و سفارشی‌سازی تقویم‌ها.", - "name": "نام", - "name_placeholder": "نام تقویم", - "color": "رنگ", - "change_color": "تغییر رنگ", - "random_color": "رنگ تصادفی جدید", - "add_calendar": "افزودن تقویم", - "edit": "ویرایش", - "delete": "حذف", - "save": "ذخیره", - "create": "ایجاد", - "cancel": "انصراف", - "default": "پیش‌فرض", - "confirm_delete": "حذف \"{name}\"؟ همه رویدادهای این تقویم حذف خواهند شد.", - "confirm_clear": "پاک کردن همه رویدادهای \"{name}\"؟ این عملیات قابل بازگشت نیست.", - "set_default": "تنظیم به عنوان پیش‌فرض", - "default_updated": "تقویم پیش‌فرض به‌روزرسانی شد", - "error_default": "تنظیم تقویم پیش‌فرض ناموفق بود", - "clear_events": "پاک کردن رویدادها", - "events_cleared": "{count} رویداد پاک شد", - "error_clear": "پاک کردن رویدادهای تقویم ناموفق بود", - "calendar_created": "تقویم ایجاد شد", - "calendar_updated": "تقویم به‌روزرسانی شد", - "calendar_deleted": "تقویم حذف شد", - "color_updated": "رنگ تقویم به‌روزرسانی شد", - "error_create": "ایجاد تقویم ناموفق بود", - "error_update": "به‌روزرسانی تقویم ناموفق بود", - "error_delete": "حذف تقویم ناموفق بود", - "caldav_url": "آدرس CalDAV", - "copy_url": "کپی آدرس CalDAV", - "url_copied": "آدرس CalDAV در کلیپ‌بورد کپی شد", - "share": "اشتراک‌گذاری تقویم", - "new_event_in_calendar": "رویداد جدید در این تقویم", - "kind": "محتوا", - "kind_events": "رویدادها", - "kind_tasks": "وظایف", - "kind_both": "رویدادها و وظایف", - "kind_hint": "تعیین می‌کند کدام برنامه‌ها این تقویم را هنگام همگام‌سازی از طریق CalDAV می‌بینند. بعداً قابل تغییر نیست." - }, - "subscription": { - "title": "اشتراک iCal", - "section_title": "اشتراک‌های iCal", - "description": "اشتراک در فید خارجی iCalendar. رویدادها به صورت خودکار در تقویم خودشان همگام می‌شوند.", - "url_label": "آدرس تقویم", - "url_placeholder": "https://example.com/calendar.ics یا webcal://...", - "name_label": "نام تقویم", - "name_placeholder": "مثال: تعطیلات رسمی", - "color_label": "رنگ", - "refresh_interval": "فاصله بازنشانی", - "interval_15": "هر ۱۵ دقیقه", - "interval_30": "هر ۳۰ دقیقه", - "interval_60": "هر ساعت", - "interval_360": "هر ۶ ساعت", - "interval_1440": "هر روز", - "subscribe": "اشتراک", - "subscribing": "در حال اشتراک...", - "save": "ذخیره تغییرات", - "saving": "در حال ذخیره...", - "edit": "ویرایش", - "edit_title": "ویرایش اشتراک", - "updated": "\"{name}\" به‌روزرسانی شد", - "update_error": "به‌روزرسانی اشتراک ناموفق بود", - "invalid_url": "لطفاً یک آدرس معتبر وارد کنید", - "success": "اشتراک در \"{name}\" موفق بود", - "error": "افزودن اشتراک ناموفق بود", - "refresh": "بازنشانی هم‌اکنون", - "refresh_success": "اشتراک بازنشانی شد", - "refresh_error": "بازنشانی اشتراک ناموفق بود", - "unsubscribe": "لغو اشتراک", - "confirm_delete": "لغو اشتراک از \"{name}\"؟ تقویم و همه رویدادهایش حذف خواهند شد.", - "deleted": "اشتراک حذف شد", - "delete_error": "حذف اشتراک ناموفق بود", - "last_refreshed": "آخرین به‌روزرسانی: {time}" - }, - "tasks": { - "label": "وظایف", - "no_tasks": "وظیفه‌ای نیست", - "no_title": "(بدون عنوان)", - "mark_complete": "علامت‌گذاری تکمیل شده", - "mark_incomplete": "علامت‌گذاری تکمیل نشده", - "filter_all": "همه", - "filter_pending": "در انتظار", - "filter_completed": "تکمیل شده", - "filter_overdue": "عقب‌افتاده", - "show_completed": "نمایش تکمیل شده‌ها", - "create": "وظیفه جدید", - "edit": "ویرایش وظیفه", - "title_placeholder": "عنوان وظیفه", - "description_placeholder": "افزودن توضیحات...", - "due_date": "موعد", - "include_time": "شامل زمان", - "priority": "اولویت", - "priority_none": "هیچکدام", - "priority_high": "بالا", - "priority_medium": "متوسط", - "priority_low": "پایین", - "progress": "وضعیت", - "progress_needs_action": "نیاز به اقدام", - "progress_in_process": "در حال انجام", - "progress_completed": "تکمیل شده", - "progress_cancelled": "لغو شده", - "calendar": "تقویم", - "alert": "یادآور", - "alert_none": "هیچکدام", - "alert_at_time": "در زمان موعد", - "alert_5min": "۵ دقیقه قبل", - "alert_15min": "۱۵ دقیقه قبل", - "alert_30min": "۳۰ دقیقه قبل", - "alert_1hr": "۱ ساعت قبل", - "alert_1day": "۱ روز قبل", - "delete": "حذف", - "cancel": "انصراف", - "save": "ذخیره", - "quick_add_placeholder": "افزودن وظیفه...", - "due_today": "امروز", - "due_tomorrow": "فردا", - "overdue": "عقب‌افتاده" - } - }, - "calendar_event_notifications": { - "someone": "کسی", - "invited": "{name} شما را به «{title}» دعوت کرد", - "updated": "{name} «{title}» را به‌روزرسانی کرد", - "cancelled": "{name} «{title}» را لغو کرد", - "untitled": "رویداد بدون عنوان" - }, - "share_notifications": { - "someone": "کسی", - "shared": "{name} {kind} «{object}» را با شما به اشتراک گذاشت", - "changed": "{name} دسترسی شما به {kind} «{object}» را تغییر داد", - "revoked": "{name} دسترسی شما به {kind} «{object}» را حذف کرد", - "object": { - "folder": "پوشه", - "calendar": "تقویم", - "address_book": "دفترچه آدرس", - "files": "پوشه فایل" - } - }, - "sharing": { - "title": "اشتراک‌گذاری \"{name}\"", - "description": "دسترسی به سایر کاربران یا گروه‌های این سرور را فراهم کنید.", - "no_shares": "هنوز با کسی به اشتراک گذاشته نشده.", - "add_person": "افزودن شخص یا گروه", - "search_placeholder": "جستجو با نام یا ایمیل…", - "loading_principals": "در حال بارگذاری کاربران…", - "no_principals": "کاربر یا گروه دیگری یافت نشد.", - "no_match": "موردی مطابقت ندارد.", - "remove": "حذف دسترسی", - "group": "گروه", - "share_added": "دسترسی اعطا شد", - "share_updated": "دسترسی به‌روزرسانی شد", - "share_removed": "دسترسی حذف شد", - "share_failed": "به‌روزرسانی اشتراک‌گذاری ناموفق بود", - "preset": { - "freeBusy": "فقط آزاد/مشغول", - "read": "فقط خواندنی", - "readWrite": "خواندن و نوشتن", - "manager": "مدیر", - "custom": "سفارشی" - } - }, - "advanced_search": { - "title": "جستجوی پیشرفته", - "from": "از", - "from_placeholder": "ایمیل یا نام فرستنده", - "to": "به", - "to_placeholder": "ایمیل یا نام گیرنده", - "subject": "موضوع", - "subject_placeholder": "موضوع شامل...", - "body": "بدنه", - "body_placeholder": "بدنه شامل...", - "folder": "پوشه", - "all_folders": "همه پوشه‌ها", - "has_attachment": "پیوست", - "date_after": "بعد از", - "date_before": "قبل از", - "size_min": "بزرگ‌تر از (کیلوبایت)", - "size_max": "کوچک‌تر از (کیلوبایت)", - "starred": "ستاره‌دار", - "unread": "خوانده نشده", - "read": "خوانده شده", - "yes": "بله", - "no": "خیر", - "clear": "پاک کردن", - "clear_all": "پاک کردن همه", - "filters_active": "{count} فیلتر", - "filters_active_plural": "{count} فیلتر", - "toggle_filters": "بیشتر", - "search_hint": "از فیلترهای پیشرفته برای جستجوی دقیق استفاده کنید", - "advanced_filters_tooltip": "فیلترهای جستجوی پیشرفته", - "results_found": "{count, plural, =0 {نتیجه‌ای یافت نشد} one {# نتیجه یافت شد} other {# نتیجه یافت شد}}", - "results_found_more": "+{count} نتیجه یافت شد", - "suggestions_recent": "جستجوهای اخیر", - "suggestions_people": "افراد", - "suggestions_from": "جستجوی ایمیل‌های از {address}", - "suggestions_to": "جستجوی ایمیل‌های به {address}", - "suggestions_remove_recent": "حذف از جستجوهای اخیر" - }, - "welcome": { - "title": "به صندوق پستی خود خوش آمدید", - "tip_compose": "کلید c را برای ایمیل جدید فشار دهید", - "tip_shortcuts": "کلید ? را برای مشاهده میانبرها فشار دهید", - "tip_sidebar": "تقویم، مخاطبین و تنظیمات در نوار کناری", - "tip_settings": "تجربه خود را در تنظیمات سفارشی کنید", - "got_it": "متوجه شدم", - "settings": "تنظیمات", - "dismiss": "رد کردن", - "start_tour": "شروع تور" - }, - "demo_welcome": { - "title": "به Bulwark Mail خوش آمدید", - "description": "یک سرویس ایمیل کامل را در مرورگر خود کاوش کنید.", - "feature_email": "خواندن و نوشتن ایمیل", - "feature_organize": "برچسب‌ها، ستاره‌ها و پوشه‌ها", - "feature_shortcuts": "میانبرهای صفحه کلید", - "feature_privacy": "نسخه آزمایشی ۱۰۰٪ خصوصی", - "hint": "روی هر ایمیل در سمت چپ کلیک کنید یا تور را ببینید." - }, - "files": { - "office_edit": "ویرایش سند", - "office_loading": "در حال باز کردن ویرایشگر سند…", - "office_error": "ویرایشگر سند باز نشد.", - "office_close": "بستن ویرایشگر", - "open_in_new_tab": "باز کردن در تب جدید", - "title": "فایل‌ها", - "search_placeholder": "جستجوی فایل‌ها...", - "empty_state_title": "هنوز فایلی نیست", - "empty_state_description": "فایل آپلود کنید یا پوشه ایجاد کنید", - "upload": "بارگذاری", - "upload_files": "بارگذاری فایل‌ها", - "new_folder": "پوشه جدید", - "new_folder_name": "نام پوشه", - "rename": "تغییر نام", - "rename_title": "تغییر نام", - "new_name": "نام جدید", - "name_forbidden_chars": "سرور این نویسه‌ها را در نام‌ها مجاز نمی‌داند: {chars}", - "name_reserved": "این نام در سرور رزرو شده است.", - "delete": "حذف", - "delete_confirm_title": "حذف منبع", - "delete_confirm_message": "آیا از حذف \"{name}\" اطمینان دارید؟ این عملیات قابل بازگشت نیست.", - "download": "دانلود", - "name": "نام", - "size": "حجم", - "modified": "تاریخ تغییر", - "type": "نوع", - "folder": "پوشه", - "file": "فایل", - "parent_directory": "پوشه والد", - "breadcrumb_root": "خانه", - "other_accounts": "سایر حساب‌ها", - "no_accounts": "حساب متصلی نیست.", - "drop_files_here": "فایل‌ها یا پوشه‌ها را برای بارگذاری اینجا رها کنید", - "uploading": "در حال بارگذاری...", - "upload_success": "{count, plural, one {۱ فایل بارگذاری شد} other {# فایل بارگذاری شد}}", - "upload_error": "بارگذاری فایل ناموفق بود", - "create_folder_success": "پوشه ایجاد شد", - "create_folder_error": "ایجاد پوشه ناموفق بود", - "delete_success": "با موفقیت حذف شد", - "delete_error": "حذف ناموفق بود", - "rename_success": "تغییر نام با موفقیت انجام شد", - "rename_error": "تغییر نام ناموفق بود", - "download_error": "دانلود ناموفق بود", - "not_available": "ذخیره‌سازی فایل روی این سرور در دسترس نیست", - "cancel": "انصراف", - "create": "ایجاد", - "save": "ذخیره", - "no_results": "فایلی با این جستجو مطابقت ندارد", - "batch_delete_confirm_message": "آیا از حذف {count, plural, one {۱ مورد} other {# مورد}} اطمینان دارید؟", - "batch_delete_success": "{count, plural, one {۱ مورد حذف شد} other {# مورد حذف شد}}", - "grid_view": "نمای شبکه‌ای", - "list_view": "نمای لیست", - "details": "جزئیات", - "path": "مسیر", - "preview": "پیش‌نمایش", - "preview_error": "بارگذاری پیش‌نمایش ناموفق بود", - "cut": "برش", - "copy": "کپی", - "paste": "چسباندن", - "move_success": "{count, plural, one {۱ مورد منتقل شد} other {# مورد منتقل شد}}", - "move_error": "انتقال ناموفق بود", - "paste_success": "با موفقیت چسبانده شد", - "paste_error": "چسباندن ناموفق بود", - "new_text_file": "فایل متنی جدید", - "file_name": "نام فایل", - "retry": "تلاش مجدد", - "refresh": "بازنشانی", - "toggle_favorite": "تغییر وضعیت علاقه‌مندی", - "duplicate": "کپی", - "duplicate_success": "با موفقیت کپی شد", - "duplicate_error": "کپی ناموفق بود", - "share": "اشتراک‌گذاری", - "shared": "اشتراکی", - "shared_with_me": "با من به اشتراک گذاشته شده", - "shared_by": "به اشتراک گذاشته شده توسط {name}", - "create_file_success": "فایل ایجاد شد", - "create_file_error": "ایجاد فایل ناموفق بود", - "favorites": "مورد علاقه‌ها", - "recent": "اخیر", - "properties": "ویژگی‌ها", - "open_folder": "باز کردن پوشه", - "upload_folder": "بارگذاری پوشه", - "file_too_large": "\"{name}\" بیش از حداکثر حجم مجاز است ({max})", - "undo": "بازگردانی", - "undo_success": "عملیات بازگردانی شد", - "undo_error": "بازگردانی ناموفق بود", - "toolbar": "عملیات فایل", - "open_folder_tree": "باز کردن درخت پوشه", - "file_list": "فایل‌ها و پوشه‌ها", - "context_menu": "عملیات‌ها", - "settings_title": "تنظیمات فایل", - "settings_display": "نمایش", - "settings_default_view": "نمای پیش‌فرض", - "settings_default_view_desc": "انتخاب بین چیدمان grid یا لیست", - "settings_default_sort": "مرتب‌سازی پیش‌فرض", - "settings_default_sort_desc": "انتخاب مرتب‌سازی پیش‌فرض فایل‌ها", - "settings_sort_direction": "جهت مرتب‌سازی", - "settings_sort_direction_desc": "انتخاب ترتیب صعودی یا نزولی", - "settings_ascending": "صعودی", - "settings_descending": "نزولی", - "settings_icons": "آیکون‌ها", - "settings_show_icons": "نمایش آیکون فایل", - "settings_show_icons_desc": "نمایش آیکون کنار فایل‌ها و پوشه‌ها", - "settings_colored_icons": "آیکون‌های رنگی", - "settings_colored_icons_desc": "استفاده از آیکون‌های رنگی به جای تک‌رنگ", - "settings_show_thumbnails": "نمایش thumbnail", - "settings_show_thumbnails_desc": "نمایش پیش‌نمایش تصویر به جای آیکون", - "settings_behavior": "رفتار", - "settings_show_hidden": "نمایش فایل‌های مخفی", - "settings_show_hidden_desc": "نمایش فایل‌ها و پوشه‌هایی که با نقطه شروع می‌شوند", - "settings_folder_layout": "ناوبری پوشه", - "settings_folder_layout_desc": "انتخاب نحوه نمایش پوشه‌ها", - "settings_folder_layout_inline": "داخلی", - "settings_folder_layout_sidebar": "نوار کناری", - "disabled_title": "ویژگی فایل‌ها توسط مدیر غیرفعال شده", - "disabled_description": "بارگذاری فایل‌های حجیم از طریق WebDAV می‌تواند باعث ناپایداری سرور شود.", - "stability_warning": "بارگذاری فایل‌های حجیم می‌تواند باعث ناپایداری سرور شود. با احتیاط استفاده کنید.", - "migration_title": "در حال به‌روزرسانی فایل‌های شما…", - "migration_description": "سازمان‌دهی پوشه‌ها و فایل‌ها در ساختار مناسب. فقط یک بار انجام می‌شود." - }, - "smime": { - "your_certificates": "گواهی‌های شما", - "your_certificates_desc": "مدیریت گواهی‌های S/MIME", - "recipient_certificates": "گواهی‌های گیرندگان", - "recipient_certificates_desc": "گواهی‌های عمومی برای رمزنگاری", - "identity_bindings": "اتصال کلید به هویت", - "identity_bindings_desc": "اتصال گواهی به هویت‌های ایمیل", - "defaults_title": "پیش‌فرض‌ها", - "defaults_desc": "تنظیم رفتار پیش‌فرض امضا و رمزنگاری", - "import_pkcs12": "وارد کردن PKCS#12", - "import_public_cert": "وارد کردن گواهی", - "no_certificates": "گواهی وارد نشده", - "no_recipient_certs": "گواهی گیرنده نیست", - "expires": "انقضا", - "expired": "منقضی شده", - "bound_to": "متصل به", - "no_key_bound": "هیچ", - "lock": "قفل کردن", - "unlock": "باز کردن", - "details": "جزئیات", - "delete": "حذف", - "encrypt_by_default": "رمزنگاری پیش‌فرض", - "encrypt_by_default_desc": "رمزنگاری خودکار", - "remember_unlocked": "به خاطر سپردن کلید", - "remember_unlocked_desc": "در طول نشست مرورگر", - "sign_default_for": "امضای پیش‌فرض برای", - "enter_p12_passphrase": "عبارت عبور PKCS#12", - "p12_passphrase_desc": "عبارت محافظت از گواهی", - "enter_storage_passphrase": "عبارت عبور ذخیره‌سازی", - "storage_passphrase_desc": "محافظت از کلید در مرورگر", - "next": "بعدی", - "import": "وارد کردن", - "unlock_key": "باز کردن کلید", - "unlock_key_desc": "عبارت عبور برای باز کردن", - "passphrase_placeholder": "عبارت عبور", - "confirm_passphrase_placeholder": "تأیید عبارت عبور", - "passphrase_mismatch": "مطابقت ندارد", - "cancel": "انصراف", - "processing": "در حال پردازش…", - "close": "بستن", - "certificate_details": "جزئیات گواهی", - "cert_subject": "موضوع", - "cert_issuer": "صادرکننده", - "cert_email": "ایمیل", - "cert_serial": "شماره سریال", - "cert_validity": "اعتبار", - "cert_fingerprint": "اثر انگشت", - "cert_algorithm": "الگوریتم", - "cert_capabilities": "قابلیت‌ها", - "cert_source": "منبع", - "cert_expired": "منقضی شده", - "cert_not_yet_valid": "هنوز معتبر نیست", - "cap_sign": "امضا", - "cap_encrypt": "رمزنگاری", - "cap_none": "هیچ", - "show_passphrase": "نمایش", - "hide_passphrase": "مخفی", - "sign_toggle": "امضا", - "encrypt_toggle": "رمزنگاری", - "missing_recipient_certs": "گواهی گیرنده موجود نیست: {emails}", - "missing_sender_cert": "گواهی به این هویت متصل نیست", - "status_encrypted_ok": "پیام رمزنگاری شده", - "status_encrypted_no_key": "کلید مطابق یافت نشد", - "status_encrypted_failed": "رمزگشایی ناموفق", - "status_signed_valid": "امضا تأیید شد", - "status_signed_invalid": "تأیید امضا ناموفق", - "status_signed_expired_cert": "گواهی منقضی شده", - "status_signed_self_signed": "گواهی self-signed", - "status_signed_mismatch": "امضا با فرستنده مطابقت ندارد", - "status_unsupported": "فرمت پشتیبانی نشده", - "auto_import_signer_certs": "وارد کردن خودکار گواهی امضا", - "auto_import_signer_certs_desc": "ذخیره خودکار گواهی‌ها", - "export": "خروجی", - "enter_export_passphrase": "عبارت عبور خروجی", - "export_passphrase_desc": "محافظت از فایل PKCS#12", - "export_storage_desc": "عبارت ذخیره‌سازی را وارد کنید", - "incorrect_passphrase": "عبارت عبور نادرست" - }, - "tour": { - "step_counter": "مرحله {current} از {total}", - "skip": "رد کردن تور", - "back": "بازگشت", - "next": "بعدی", - "finish": "پایان", - "take_a_tour": "یک تور از رابط کاربری ببینید", - "restart_title": "تور مقدماتی", - "restart_desc": "بازپخش راهنمای تعاملی رابط کاربری", - "restart_button": "شروع مجدد تور", - "show_on_new_devices_title": "نمایش در دستگاه‌های جدید", - "show_on_new_devices_desc": "بازپخش بنر خوش‌آمدگویی و تور در اولین ورود در دستگاه جدید", - "sidebar_title": "صندوق‌های پستی شما", - "sidebar_desc": "این نوار کناری پوشه‌های شماست. روی هر صندوق کلیک کنید تا ایمیل‌هایش را ببینید.", - "compose_title": "نوشتن ایمیل", - "compose_desc": "برای نوشتن ایمیل جدید اینجا کلیک کنید. می‌توانید گیرنده، پیوست و قالب‌بندی اضافه کنید.", - "search_title": "جستجوی ایمیل‌ها", - "search_desc": "جستجو بر اساس فرستنده، موضوع یا محتوا. روی آیکون فیلتر برای گزینه‌های پیشرفته کلیک کنید.", - "email_list_title": "لیست ایمیل‌های شما", - "email_list_desc": "ایمیل‌ها اینجا نمایش داده می‌شوند. روی یکی کلیک کنید تا در سمت راست بخوانید.", - "email_viewer_title": "پنل مطالعه", - "email_viewer_desc": "ایمیل انتخاب شده اینجا باز می‌شود. پاسخ، ارسال مجدد، بایگانی یا حذف با دکمه‌های نوار ابزار.", - "keywords_title": "برچسب‌های رنگی", - "keywords_desc": "ایمیل‌های خود را با برچسب‌های رنگی سازمان‌دهی کنید.", - "calendar_title": "تقویم", - "calendar_desc": "برای مدیریت رویدادها به تقویم بروید. رویداد ایجاد کنید و یادآور تنظیم کنید.", - "contacts_title": "مخاطبین", - "contacts_desc": "دفترچه آدرس شما اینجاست. مخاطبین وارد کنید، گروه بسازید.", - "settings_title": "تنظیمات", - "settings_desc": "همه چیز را سفارشی کنید: تم، تراکم، امضاها، فیلترها، میانبرها و بیشتر.", - "shortcuts_title": "میانبرهای صفحه کلید", - "shortcuts_desc": "کاربران حرفه‌ای عاشق این هستند. هر زمان ? را فشار دهید تا همه میانبرها را ببینید.", - "compose_open_title": "ویرایشگر", - "compose_open_desc": "این ویرایشگر ایمیل است. گیرندگان را اضافه کنید، پیام بنویسید، فایل پیوست کنید.", - "calendar_view_title": "تقویم شما", - "calendar_view_desc": "این تقویم شما با رویدادهای نمونه است. بین نماهای روز، هفته و ماه جابجا شوید.", - "create_event_title": "ایجاد رویداد", - "create_event_desc": "برای ایجاد رویداد تقویم جدید این دکمه را کلیک کنید.", - "event_modal_title": "جزئیات رویداد", - "event_modal_desc": "فرم رویداد اینجاست. عنوان، تاریخ و زمان را پر کنید و ذخیره کنید.", - "contacts_list_title": "مخاطبین شما", - "contacts_list_desc": "مخاطبین شما اینجا هستند. روی هر کدام کلیک کنید تا جزئیات کامل را ببینید.", - "settings_tabs_title": "منوی تنظیمات", - "settings_tabs_desc": "همه دسته‌بندی‌های تنظیمات اینجا هستند.", - "files_title": "ذخیره‌سازی فایل", - "files_desc": "مرورگر فایل شما امکان بارگذاری، سازمان‌دهی و اشتراک‌گذاری فایل‌ها را می‌دهد.", - "demo_banner_title": "کنترل‌های نسخه آزمایشی", - "demo_banner_desc": "شما در حالت آزمایشی هستید. هر زمان \"بازنشانی نسخه آزمایشی\" را بزنید.", - "quota_title": "مصرف فضا", - "quota_desc": "مصرف صندوق پستی خود را اینجا پیگیری کنید." - }, - "unified_mailbox": { - "search_unavailable": "جستجو در نمای یکپارچه در دسترس نیست" - }, - "quote_header": { - "reply_line": "در تاریخ {date}، {from} نوشت:", - "forwarded_separator": "---------- پیام ارجاعی ----------", - "from_label": "از", - "date_label": "تاریخ", - "subject_label": "موضوع" - }, - "pwa_install": { - "title": "نصب {appName}", - "description": "برای دسترسی سریع برنامه را نصب کنید.", - "not_now": "الان نه", - "install": "نصب", - "dont_remind": "دیگر یادآوری نکن", - "dismiss_aria": "رد کردن پیشنهاد نصب" - }, - "deep_link": { - "message_not_found": "این پیام دیگر در دسترس نیست.", - "thread_not_found": "این گفتگو دیگر در دسترس نیست.", - "folder_not_found": "این پوشه دیگر در دسترس نیست.", - "event_not_found": "این رویداد دیگر در دسترس نیست.", - "file_not_found": "این فایل دیگر در دسترس نیست.", - "account_unavailable": "برای باز کردن این پیوند، به آن حساب وارد شوید.", - "copy_message": "کپی پیوند پیام", - "copy_conversation": "کپی پیوند گفتگو", - "copy_event": "کپی پیوند رویداد", - "copied": "پیوند کپی شد", - "copy_failed": "کپی پیوند ممکن نشد" - }, - "plugins": { - "oauth_callback": { - "completing": "در حال تکمیل احراز هویت…", - "completing_message": "لطفاً تا برقراری اتصال منتظر بمانید.", - "received_title": "احراز هویت دریافت شد", - "received_message": "می‌توانید به برگه‌ای که اتصال را در آن آغاز کردید بازگردید.", - "failed_title": "احراز هویت ناموفق بود", - "denied": "احراز هویت رد شد.", - "failed_detail": "احراز هویت ناموفق بود: {error}", - "missing_params": "کد احراز هویت یا حالت وجود ندارد.", - "delivery_failed": "نتیجه احراز هویت به برگه برنامه ارسال نشد. لطفاً این پنجره را ببندید و دوباره تلاش کنید." - } - }, - "global_search": { - "title": "جستجو در همه جا", - "placeholder": "جستجو در ایمیل، مخاطبین، تقویم، فایل‌ها...", - "scope_label": "جستجو در", - "account_label": "حساب", - "scope_all": "همه", - "scope_mail": "ایمیل", - "scope_contacts": "مخاطبین", - "scope_calendar": "تقویم", - "scope_files": "فایل‌ها", - "all_accounts": "همه حساب‌ها", - "show_all": "نمایش همه نتایج", - "no_results": "نتیجه‌ای برای “{query}” یافت نشد", - "server_hint": "کلمات کامل در سرور جستجو می‌شوند؛ بخشی از کلمات با موارد بارگذاری‌شده مطابقت دارد", - "searching": "در حال جستجو...", - "account_error": "جستجو در {account} ممکن نشد", - "recent_searches": "جستجوهای اخیر", - "recurring": "تکرارشونده", - "no_subject": "(no subject)", - "empty_hint": "در ایمیل، مخاطبین، رویدادها و فایل‌های همه حساب‌ها جستجو کنید. امتحان کنید: from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "تخت بر اساس تاریخ", - "grouped": "گروه‌بندی‌شده", - "load_more": "بارگذاری بیشتر", - "preview_empty": "برای پیش‌نمایش یک نتیجه انتخاب کنید", - "open_full": "باز کردن", - "view_label": "نما" - } -} +{ + "meta_description": "سرویس ایمیل تحت وب مینیمال با پروتکل JMAP", + "login": { + "title": "ایمیل تحت وب", + "username_label": "ایمیل", + "username_placeholder": "user@example.com", + "password_label": "رمز عبور", + "password_placeholder": "رمز عبور خود را وارد کنید", + "jmap_endpoint_label": "سرور JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "سرور باید درخواست‌های CORS از این دامنه را مجاز کند.", + "jmap_server_label": "سرور", + "jmap_server_auto_picked": "سرور از دامنه ایمیل شما انتخاب شد.", + "sign_in": "ورود", + "signing_in": "در حال ورود...", + "loading": "در حال بارگذاری...", + "reconnecting": "ارتباط قطع شد. در حال تلاش برای اتصال مجدد…", + "error": { + "invalid_credentials": "ایمیل یا رمز عبور نامعتبر است. اطلاعات خود را بررسی و دوباره تلاش کنید.", + "invalid_token": "سرور این توکن دسترسی را نپذیرفت. بررسی کنید که کامل باشد و منقضی یا لغو نشده باشد.", + "connection_failed": "امکان اتصال به سرور وجود ندارد. اتصال اینترنت خود را بررسی کنید.", + "cors_blocked": "سرور در دسترس است اما درخواست‌های cross-origin را مسدود می‌کند.", + "server_error": "سرور موقتاً در دسترس نیست. لطفاً بعداً تلاش کنید.", + "generic": "خطای غیرمنتظره‌ای رخ داد. اگر ادامه داشت با مدیر سیستم تماس بگیرید.", + "totp_required": "کد احراز هویت دو مرحله‌ای لازم است.", + "totp_invalid": "کد احراز هویت نامعتبر است. برنامه authenticator خود را بررسی کنید.", + "oauth_discovery_failed": "ورود یکپارچه فعال است اما ارائه‌دهنده هویت در دسترس نیست.", + "token_exchange_failed": "رمز عبور و کد پذیرفته شد، اما سرور ایمیل از شروع نشست برای این برنامه خودداری کرد. از مدیر سیستم بخواهید تنظیمات کلاینت OAuth را بررسی کند." + }, + "show_password": "نمایش رمز عبور", + "hide_password": "مخفی کردن رمز عبور", + "totp_toggle": "کد احراز هویت دو مرحله‌ای دارم", + "token_toggle": "ورود با توکن دسترسی", + "password_toggle": "ورود با رمز عبور", + "token_label": "توکن دسترسی", + "token_placeholder": "توکن API خود را وارد کنید", + "token_hint": "در تنظیمات سرویس ایمیل خود یک توکن API بسازید و آن را اینجا وارد کنید.", + "remember_me": "مرا به خاطر بسپار", + "config_error": { + "title": "خطای پیکربندی", + "fetch_failed": "بارگذاری پیکربندی برنامه ممکن نیست.", + "server_not_configured": "سرور ایمیل پیکربندی نشده است. با مدیر سیستم تماس بگیرید." + }, + "remove_from_history": "حذف از تاریخچه", + "totp_label": "کد احراز هویت", + "totp_placeholder": "000000", + "session_expired": "نشست شما منقضی شده است. لطفاً دوباره وارد شوید.", + "dismiss": "رد کردن", + "or": "یا", + "sign_in_sso": "ورود با SSO", + "add_account_title": "افزودن حساب", + "add_account_subtitle": "ورود با حساب دیگر", + "cancel": "انصراف", + "website": "وبسایت", + "imprint": "اطلاعات حقوقی", + "privacy_policy": "حریم خصوصی", + "try_demo": "نسخه آزمایشی", + "demo_description": "کاوش با داده‌های نمونه - بدون نیاز به حساب", + "demo_launching": "در حال راه‌اندازی نسخه آزمایشی...", + "demo_login_button": "راه‌اندازی نسخه آزمایشی", + "demo_tagline": "تجربه یک سرویس ایمیل کامل. بدون نیاز به حساب کاربری.", + "demo_no_signup": "نیازی به ثبت‌نام نیست - آزادانه با داده‌های نمونه کاوش کنید", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "در حال تکمیل ورود...", + "oauth_error": { + "title": "احراز هویت ناموفق", + "invalid_state": "اعتبارسنجی امنیتی ناموفق بود. لطفاً دوباره وارد شوید.", + "missing_params": "اطلاعات مجوزدهی ناقص است. لطفاً دوباره وارد شوید.", + "token_exchange_failed": "تکمیل احراز هویت ناموفق بود.", + "access_denied": "دسترسی رد شد. با مدیر سیستم تماس بگیرید.", + "back_to_login": "بازگشت به صفحه ورود" + } + }, + "sidebar": { + "close": "بستن", + "compose": "ایمیل جدید", + "compose_hint": "ایمیل جدید (c)", + "search_placeholder": "جستجوی ایمیل...", + "search_placeholder_hint": "جستجوی ایمیل... (کلید /)", + "storage": "فضای ذخیره‌سازی", + "storage_used": "استفاده شده", + "storage_free": "آزاد", + "storage_total": "کل", + "sign_out": "خروج", + "sign_out_of": "خروج از {account}", + "sign_out_all": "خروج از همه حساب‌ها", + "add_account": "افزودن حساب", + "set_as_default": "تنظیم به عنوان پیش‌فرض", + "switch_account": "تعویض حساب", + "contacts": "مخاطبین", + "calendar": "تقویم", + "settings": "تنظیمات", + "admin": "مدیریت", + "files": "فایل‌ها", + "loading_mailboxes": "در حال بارگذاری صندوق‌های پستی...", + "push_connected": "به‌روزرسانی‌های بلادرنگ فعال", + "push_disconnected": "به‌روزرسانی‌های بلادرنگ غیرفعال", + "keyboard_shortcuts": "میانبرهای صفحه کلید", + "theme": { + "light": "حالت روشن", + "dark": "حالت تاریک", + "system": "تم سیستم" + }, + "language": { + "title": "زبان" + }, + "mailboxes": { + "inbox": "صندوق ورودی", + "sent": "ارسال شده", + "drafts": "پیش‌نویس‌ها", + "trash": "زباله‌دان", + "archive": "بایگانی", + "starred": "ستاره‌دار", + "all_mail": "همه ایمیل‌ها", + "spam": "هرزنامه", + "important": "مهم" + }, + "unified_inbox": "صندوق ورودی یکپارچه", + "unified_sent": "همه ارسال‌ها", + "unified_drafts": "همه پیش‌نویس‌ها", + "unified_trash": "همه زباله‌دان‌ها", + "unified_archive": "همه بایگانی‌ها", + "unified_junk": "همه هرزنامه‌ها", + "all_accounts": "همه حساب‌ها", + "all_accounts_loading": "در حال بارگیری حساب‌ها: {loaded} از {total}", + "unified_mailbox": "صندوق پستی یکپارچه", + "expand": "باز کردن", + "collapse": "جمع کردن", + "expand_tooltip": "باز کردن", + "collapse_tooltip": "جمع کردن", + "mobile": { + "search": "جستجو", + "compose": "ایمیل جدید", + "go_back": "بازگشت", + "toggle_menu": "تغییر وضعیت منو" + }, + "clear_search": "پاک کردن جستجو", + "vacation_active": "پاسخ‌گوی خودکار فعال است", + "demo_banner": "حالت آزمایشی", + "demo_reset": "بازنشانی", + "demo_tour": "تور", + "tags": "برچسب‌ها", + "show_all_tags": "نمایش همه ({count})", + "show_fewer_tags": "نمایش کمتر", + "folders": "پوشه‌ها", + "your_folders": "پوشه‌های شما", + "create_folder": "ایجاد پوشه", + "show_more": "بیشتر", + "show_fewer": "کمتر", + "shared": "اشتراکی", + "shared_account_hidden": "از نوار کناری پنهان شد. از تنظیمات › چیدمان دوباره نمایش دهید.", + "notifications_muted": "اعلان‌ها بی‌صدا", + "mail": "ایمیل", + "nav_label": "ناوبری", + "add_app": "برنامه‌ها", + "scheduled": "زمان‌بندی شده", + "unified_all_mail": "همه ایمیل‌ها", + "unified_all_starred": "همه ستاره‌دارها", + "unified_all_unread": "همه خوانده‌نشده‌ها", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "برچسب‌ها", + "create_label": "ایجاد برچسب جدید", + "mv_tasks": "وظایف", + "mv_side_panel": "پنل کناری", + "mv_open_full": "باز کردن در نمای کامل", + "mv_previous_day": "روز قبل", + "mv_next_day": "روز بعد", + "mv_add_another_account": "افزودن حساب دیگر", + "mv_manage_account": "مدیریت حساب", + "mv_storage_used": "{percent}٪ از {total} استفاده شده" + }, + "protocol_handlers": { + "title": "برنامه‌های پیش‌فرض", + "description": "انتخاب کنید لینک‌های ایمیل و تقویم در Bulwark باز شوند.", + "unsupported": "این مرورگر از ثبت دستی پروتکل پشتیبانی نمی‌کند.", + "mailto_label": "لینک‌های ایمیل", + "mailto_description": "باز کردن لینک‌های mailto: در Bulwark", + "protocol_open_mode_label": "هنگام باز کردن لینک‌های پروتکل", + "protocol_open_mode_description": "انتخاب کنید لینک‌ها در تب جدید باز شوند یا نشست فعال.", + "protocol_open_mode_active_session": "باز کردن در نشست فعال", + "protocol_open_mode_new_tab": "همیشه در تب جدید", + "focus_notification_title": "باز کردن Bulwark", + "focus_notification_body": "لینک در Bulwark باز شد.", + "webcal_label": "لینک‌های تقویم", + "webcal_description": "باز کردن لینک‌های webcal: در Bulwark", + "register_mailto": "ثبت برنامه ایمیل", + "register_webcal": "ثبت برنامه تقویم", + "mailto_registered": "ثبت مدیریت ایمیل درخواست شد", + "webcal_registered": "ثبت مدیریت تقویم درخواست شد", + "registration_failed": "ثبت پروتکل ناموفق بود", + "opening_mailto": "در حال باز کردن ویرایشگر...", + "opening_webcal": "در حال باز کردن تقویم...", + "browser_note": "مرورگر یا سیستم عامل ممکن است تأیید شما را بخواهد.", + "select_account_title": "انتخاب حساب", + "select_mailto_account": "انتخاب کنید کدام حساب این لینک ایمیل را باز کند.", + "select_webcal_account": "انتخاب کنید کدام حساب این لینک تقویم را باز کند.", + "select_account_note": "فقط برای این لینک پروتکل اعمال می‌شود.", + "detail_to": "به", + "detail_subject": "موضوع", + "detail_no_subject": "بدون موضوع", + "detail_calendar": "تقویم", + "detail_source": "منبع", + "active_account": "فعال", + "switching_account": "در حال تعویض حساب..." + }, + "sidebar_apps": { + "modal_title": "برنامه‌های نوار کناری", + "add_new": "افزودن برنامه", + "edit_app": "ویرایش برنامه", + "name_label": "نام", + "name_placeholder": "برنامه من", + "name_required": "نام الزامی است", + "url_label": "آدرس", + "url_required": "آدرس الزامی است", + "url_invalid": "آدرس http یا https معتبر وارد کنید", + "icon_label": "آیکون", + "icon_required": "آیکون الزامی است", + "open_mode_label": "حالت باز کردن", + "open_new_tab": "تب جدید", + "open_inline": "داخلی", + "cancel": "انصراف", + "add": "افزودن", + "update": "به‌روزرسانی", + "delete": "حذف", + "delete_confirm_title": "حذف برنامه", + "delete_confirm": "آیا از حذف \"{name}\" اطمینان دارید؟", + "no_apps": "هنوز برنامه‌ای اضافه نشده", + "no_apps_hint": "برنامه‌ها و لینک‌های دلخواه را به نوار کناری اضافه کنید", + "managed_title": "ارائه‌شده توسط مدیر شما", + "managed_description": "این برنامه‌ها برای همه تنظیم شده‌اند و قابل تغییر نیستند.", + "managed_badge": "مدیریت‌شده", + "search_icons": "جستجوی آیکون‌ها...", + "show_popular": "محبوب", + "show_all": "همه", + "no_icons_found": "آیکونی یافت نشد", + "inline_badge": "داخلی", + "tab_badge": "تب", + "show_on_mobile": "نمایش در موبایل" + }, + "email_list": { + "columns": { + "from": "از", + "to": "به", + "subject": "موضوع", + "date": "تاریخ", + "size": "اندازه" + }, + "no_emails": "پیامی یافت نشد", + "no_emails_description": "این صندوق پستی خالی است", + "no_search_results": "نتیجه‌ای یافت نشد", + "no_search_results_description": "جستجو یا فیلترهای خود را تنظیم کنید", + "loading": "در حال بارگذاری ایمیل‌ها...", + "unread": "خوانده نشده", + "to_me": "به من", + "to_recipients": "به {count} گیرنده", + "and_others": "و {count} نفر دیگر", + "draft": "پیش‌نویس", + "starred": "ستاره‌دار", + "pinned": "سنجاق‌شده", + "replied": "پاسخ داده شده", + "forwarded": "بازارسال شده", + "has_attachment": "دارای پیوست", + "conversations_range": "{from}–{to, number} از {total, number}", + "conversations_count": "{count} از {total} مکالمه", + "conversations_count_plus": "+{count} مکالمه", + "conversations_count_simple": "{count} مکالمه", + "no_conversations": "مکالمه‌ای نیست", + "loading_more": "در حال بارگذاری بیشتر...", + "no_more_emails": "ایمیل بیشتری برای بارگذاری نیست", + "batch_actions": { + "select": "انتخاب ایمیل‌ها", + "select_all": "انتخاب همه", + "selected_messages": "{count, plural, one {1 ایمیل} other {# ایمیل}} انتخاب شده", + "mark_read": "علامت‌گذاری به عنوان خوانده شده", + "mark_unread": "علامت‌گذاری به عنوان خوانده نشده", + "delete": "حذف", + "delete_confirm_title": "حذف ایمیل‌ها", + "delete_confirm_message": "آیا از حذف {count, plural, one {1 ایمیل} other {# ایمیل}} اطمینان دارید؟", + "clear_selection": "پاک کردن انتخاب" + }, + "permanent_delete": "حذف دائمی", + "permanent_delete_confirm_title": "حذف دائمی", + "permanent_delete_confirm_message": "این ایمیل برای همیشه حذف خواهد شد. این عملیات قابل بازگشت نیست.", + "permanent_delete_confirm_batch_message": "این {count, plural, one {1 ایمیل} other {# ایمیل}} برای همیشه حذف خواهد شد.", + "empty_folder": { + "button": "خالی کردن پوشه", + "confirm_title": "خالی کردن پوشه", + "confirm_message": "همه ایمیل‌های این پوشه برای همیشه حذف خواهند شد.", + "confirm_message_trash": "همه ایمیل‌های این پوشه به زباله‌دان منتقل می‌شوند.", + "confirm_button": "خالی کردن پوشه", + "junk_hint": "می‌توانید پوشه هرزنامه را خالی کنید.", + "trash_hint": "می‌توانید پوشه زباله‌دان را خالی کنید." + }, + "no_scheduled_emails": "ایمیل زمان‌بندی شده‌ای نیست", + "no_scheduled_emails_description": "پیام‌های زمان‌بندی شده برای بعد اینجا نمایش داده می‌شوند.", + "scheduled_count": "{count, plural, one {1 ایمیل زمان‌بندی شده} other {# ایمیل زمان‌بندی شده}}", + "scheduled_actions_hint": "پیام‌های زمان‌بندی شده را می‌توان لغو یا ویرایش کرد.", + "cancel_scheduled_send": "لغو ارسال", + "reschedule_send": "باززمان‌بندی", + "cancel_and_edit": "لغو و ویرایش", + "cancel_and_compose_again": "لغو و نوشتن مجدد", + "reschedule_prompt": "تاریخ/زمان جدید را وارد کنید" + }, + "email_viewer": { + "read_receipt": { + "prompt": "فرستنده درخواست تأیید خواندن کرده است:", + "send": "ارسال تأیید", + "ignore": "نادیده گرفتن", + "sent": "تأیید خواندن ارسال شد.", + "send_failed": "تأیید خواندن ارسال نشد", + "mdn_subject": "خوانده شد: {subject}", + "mdn_body": "این یک رسید برای پیام ارسالی به {recipient} است." + }, + "no_email_selected": "ایمیلی انتخاب نشده", + "no_email_description": "یک ایمیل از لیست انتخاب کنید", + "no_conversation_selected": "مکالمه‌ای انتخاب نشده", + "no_conversation_description": "یک مکالمه از لیست انتخاب کنید", + "compose": "ایمیل جدید", + "compose_hint": "نوشتن پیام جدید", + "no_subject": "(بدون موضوع)", + "no_body_content": "(محتوایی موجود نیست)", + "no_preview_available": "پیش‌نمایشی در دسترس نیست", + "show_quoted_text": "نمایش متن نقل‌قول‌شده", + "hide_quoted_text": "پنهان کردن متن نقل‌قول‌شده", + "loading_email": "در حال بارگذاری ایمیل...", + "loading": "در حال بارگذاری...", + "reply": "پاسخ", + "reply_all": "پاسخ به همه", + "forward": "ارسال مجدد", + "delete": "حذف", + "archive": "بایگانی", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "ستاره‌دار کردن", + "unstar": "حذف ستاره", + "mark_unread": "علامت‌گذاری خوانده نشده", + "mark_read": "علامت‌گذاری خوانده شده", + "unread": "خوانده نشده", + "read": "خوانده شده", + "spam_short": "هرزنامه", + "not_spam_short": "هرزنامه نیست", + "move": "انتقال", + "print": "چاپ", + "view_source": "مشاهده منبع", + "fullscreen": "تمام‌صفحه", + "exit_fullscreen": "خروج از تمام‌صفحه", + "view_in_dark_mode": "نمایش در حالت تیره", + "view_in_light_mode": "نمایش در حالت روشن", + "export_email": "خروجی .eml", + "forward_as_attachment": "ارسال به‌عنوان پیوست", + "import_email": "وارد کردن .eml یا .zip", + "keyboard_shortcuts": "میانبرهای صفحه کلید (?)", + "email_source": "منبع ایمیل", + "draft_banner": "این پیام یک پیش‌نویس است", + "edit_draft": "ویرایش", + "copy_source": "کپی در کلیپ‌بورد", + "source_copied": "منبع کپی شد", + "attachments": "پیوست‌ها", + "important": "مهم", + "download": "دانلود", + "download_all": "دانلود همه", + "from": "از", + "to": "به", + "cc": "رونوشت", + "bcc": "رونوشت مخفی", + "date": "تاریخ", + "subject": "موضوع", + "show_details": "نمایش جزئیات", + "hide_details": "مخفی کردن جزئیات", + "external_content_warning": "تصاویر و محتوای خارجی مسدود شده‌اند", + "load_external_content": "بارگذاری تصاویر", + "trust_sender": "همیشه به این فرستنده اعتماد کن", + "sender_check": { + "unverified_label": "فرستنده تأییدنشده", + "failed_label": "بررسی فرستنده ناموفق بود", + "unverified": "این پیام ادعا می‌کند از {domain} است، اما هیچ چیزی این را تأیید نمی‌کند. امضای معتبری از {domain} ندارد و از سروری که {domain} مجاز دانسته ارسال نشده است.", + "unverified_sent_from": "این پیام ادعا می‌کند از {domain} است، اما از {host} ارسال شده و امضای معتبری از {domain} ندارد.", + "failed": "این پیام ادعا می‌کند از {domain} است، اما در بررسی‌های فرستنده برای این دامنه رد شده است. ممکن است جعلی باشد.", + "failed_sent_from": "این پیام ادعا می‌کند از {domain} است، اما از {host} ارسال شده و در بررسی‌های فرستنده برای {domain} رد شده است. ممکن است جعلی باشد.", + "caution": "مراقب پیوندها، پیوست‌ها و درخواست‌های رمز عبور یا اطلاعات پرداخت باشید." + }, + "back_to_list": "بازگشت به لیست", + "view_contact": "مشاهده مخاطب", + "message_details": "جزئیات پیام", + "more_reply_options": "گزینه‌های بیشتر پاسخ", + "set_tag": "تنظیم برچسب", + "tag": "برچسب", + "more_actions": "عملیات بیشتر", + "previous": "قبلی", + "next": "بعدی", + "move_to": "انتقال به...", + "remove_tag": "حذف برچسب", + "tag_filter_placeholder": "فیلتر برچسب‌ها", + "tag_no_matches": "برچسب مطابقی یافت نشد", + "more_count": "+{count} بیشتر", + "characters_count": "{count} کاراکتر", + "quick_reply_placeholder": "پاسخ سریع بنویسید...", + "more_options": "گزینه‌های بیشتر", + "sending": "در حال ارسال...", + "security_authentication": "امنیت و احراز هویت", + "technical_details": "جزئیات فنی", + "message_id_label": "شناسه پیام:", + "reply_to_label": "پاسخ به:", + "delivery_time_label": "زمان تحویل:", + "conversation_part_label": "بخشی از مکالمه:", + "previous_messages": "{count} پیام قبلی", + "previous_messages_plural": "{count} پیام قبلی", + "time": { + "day": "روز", + "days": "روز", + "hour": "ساعت", + "hours": "ساعت", + "minute": "دقیقه", + "minutes": "دقیقه" + }, + "unknown_sender": "ناشناس", + "recipient_me": "من", + "recipient_and_others": "{name} و {count} نفر دیگر", + "recipient_to_prefix": "به:", + "authentication": { + "title": "احراز هویت", + "status": { + "verified": "تأیید شده", + "warning": "هشدار", + "none": "احراز هویت نشده" + }, + "spf": { + "pass": "SPF تأیید شد", + "fail": "SPF ناموفق", + "none": "SPF ندارد" + }, + "dkim": { + "pass": "DKIM معتبر", + "fail": "DKIM نامعتبر", + "none": "DKIM ندارد" + }, + "dmarc": { + "pass": "DMARC تأیید شد", + "fail": "DMARC ناموفق", + "none": "DMARC ندارد" + }, + "spam_score": "امتیاز هرزنامه", + "tooltip_spf": "SPF: تأیید مجاز بودن سرور ارسال‌کننده", + "tooltip_dkim": "DKIM: تأیید عدم تغییر ایمیل در انتقال", + "tooltip_dmarc": "DMARC: هماهنگی SPF و DKIM با دامنه فرستنده", + "policy": "سیاست", + "result": { + "pass": "موفق", + "fail": "ناموفق", + "softfail": "ناموفق نسبی", + "neutral": "خنثی", + "permerror": "خطای دائمی", + "temperror": "خطای موقت", + "none": "ندارد" + } + }, + "details": { + "recipients_routing": "گیرندگان و مسیریابی", + "authentication_security": "احراز هویت و امنیت", + "identifiers_threading": "شناسه‌ها و رشته‌بندی", + "mailing_list": "لیست پستی", + "message_properties": "ویژگی‌های پیام", + "sent": "ارسال شده", + "received": "دریافت شده", + "delivery_time": "زمان تحویل", + "in_reply_to": "در پاسخ به", + "references": "منابع", + "thread_id": "شناسه رشته", + "size": "حجم", + "mime_type": "نوع MIME", + "attachments_summary": "{count} فایل · {size}", + "list_id": "شناسه لیست", + "list_help": "راهنمای لیست", + "list_post": "ارسال به لیست", + "list_unsubscribe": "لغو اشتراک", + "iprev": "DNS معکوس", + "spam_status": "وضعیت هرزنامه", + "ai_verdict": "نظر هوش مصنوعی", + "account": "حساب", + "no_subject": "(بدون موضوع)" + }, + "headers": { + "routing": "مسیریابی", + "received": "دریافت شده", + "message_id": "شناسه پیام", + "list_info": "اطلاعات لیست" + }, + "tooltips": { + "reply": "پاسخ (r)", + "reply_all": "پاسخ به همه (a)", + "forward": "ارسال مجدد (f)", + "archive": "بایگانی (e)", + "delete": "حذف (# یا Del)", + "star": "ستاره‌دار (s)", + "unstar": "حذف ستاره (s)", + "compose": "ایمیل جدید (c)", + "previous": "ایمیل قبلی", + "next": "ایمیل بعدی", + "edit_draft": "ویرایش پیش‌نویس" + }, + "spam": { + "button_title": "گزارش هرزنامه", + "not_spam_title": "علامت‌گذاری به عنوان قانونی", + "toast_success": "به هرزنامه منتقل شد", + "toast_batch": "{count} ایمیل به هرزنامه منتقل شد", + "toast_undo": "بازگردانی", + "toast_not_spam_success": "به صندوق ورودی منتقل شد", + "toast_not_spam_batch": "{count} ایمیل به صندوق ورودی منتقل شد", + "error": "گزارش هرزنامه ناموفق بود", + "error_not_spam": "بازگردانی ایمیل ناموفق بود" + }, + "unsubscribe_banner": { + "label": "خبرنامه", + "button": "لغو اشتراک", + "confirm_title": "لغو اشتراک از این فرستنده؟", + "confirm_button": "تأیید", + "cancel": "انصراف", + "success_http": "صفحه لغو اشتراک در تب جدید باز شد", + "success_mailto": "ایمیل لغو اشتراک ارسال شد", + "confirm_message_http": "صفحه لغو اشتراک در برگه جدیدی باز خواهد شد.", + "confirm_message_mailto": "ایمیل لغو اشتراک برای فرستنده ارسال خواهد شد.", + "error": "لغو اشتراک ممکن نیست", + "dismiss": "رد کردن" + }, + "calendar_invitation": { + "loading": "در حال بارگذاری جزئیات رویداد…", + "title": "دعوت‌نامه تقویم", + "published_title": "رویداد منتشر شده", + "response_title": "پاسخ رویداد", + "update_title": "به‌روزرسانی رویداد", + "counter_title": "پیشنهاد متقابل", + "refresh_title": "درخواست به‌روزرسانی", + "declined_counter_title": "پیشنهاد متقابل رد شد", + "cancelled_title": "رویداد لغو شد", + "organizer": "برگزارکننده: {name}", + "organizer_label": "برگزارکننده", + "attendees": "{count, plural, one {# شرکت‌کننده} other {# شرکت‌کننده}}", + "accept": "پذیرفتن", + "maybe": "شاید", + "decline": "رد کردن", + "add_to_calendar": "افزودن به تقویم", + "added": "به تقویم اضافه شد", + "rsvp_sent": "پاسخ ارسال شد", + "parse_error": "خواندن دعوت‌نامه ممکن نیست", + "action_failed": "عملیات تقویم انجام نشد.", + "no_calendar": "تقویم در دسترس نیست", + "published_info": "این رویداد برای اطلاع‌رسانی به اشتراک گذاشته شده.", + "response_info": "این پیام شامل پاسخ یک شرکت‌کننده است.", + "response_info_organizer": "پاسخ شرکت‌کننده رویداد شما را به‌روزرسانی می‌کند.", + "update_info": "این پیام یک رویداد موجود را به‌روزرسانی می‌کند.", + "counter_info": "این پیام تغییراتی در رویداد پیشنهاد می‌دهد.", + "counter_info_organizer": "شرکت‌کننده تغییراتی در رویداد شما پیشنهاد داده.", + "refresh_info": "این پیام آخرین جزئیات رویداد را درخواست می‌کند.", + "refresh_info_organizer": "شرکت‌کننده آخرین جزئیات رویداد را درخواست کرده.", + "declined_counter_info": "برگزارکننده پیشنهاد متقابل را رد کرد.", + "authentication_failed_info": "بررسی احراز هویت ایمیل ناموفق بود. با احتیاط عمل کنید.", + "authentication_missing_info": "این دعوت‌نامه فاقد احراز هویت ایمیل است.", + "sender_mismatch_info": "این دعوت‌نامه از {sender} ارسال شده ولی برگزارکننده {organizer} است.", + "sender_mismatch_unverified_info": "از {sender} ارسال شده ولی برگزارکننده {organizer} است و پیام تأیید نشد.", + "organizer_role": "شما برگزارکننده این رویداد هستید", + "your_response": "پاسخ شما: {status}", + "response_needed": "نیاز به پاسخ", + "response_accepted": "پذیرفته شد", + "response_tentative": "احتمالی", + "response_declined": "رد شد", + "response_delegated": "واگذار شد", + "actor_sent_info": "ارسال شده توسط {name}.", + "actor_response_info": "{name} پاسخ داد: {status}.", + "actor_counter_info": "{name} تغییراتی پیشنهاد داد.", + "actor_refresh_info": "{name} آخرین جزئیات را درخواست کرد.", + "actor_declined_counter_info": "{name} پیشنهاد متقابل را رد کرد.", + "actor_note": "یادداشت: {comment}", + "actor_unknown": "شخصی", + "proposed_changes": "تغییرات پیشنهادی", + "change_title": "عنوان", + "change_time": "زمان", + "change_location": "مکان", + "change_description": "توضیحات", + "change_empty": "هیچ", + "change_from_to": "{before} -> {after}", + "apply_proposal": "اعمال تغییرات پیشنهادی", + "proposal_applied": "تغییرات پیشنهادی اعمال شد.", + "review_proposal": "بررسی پیشنهاد", + "review_request": "بررسی درخواست", + "view_in_calendar": "مشاهده در تقویم", + "select_calendar": "انتخاب تقویم", + "already_in_calendar": "هم‌اکنون در تقویم شماست", + "request_info": "به این رویداد دعوت شده‌اید. پاسخ دهید.", + "cancel_info": "برگزارکننده این رویداد را لغو کرده است.", + "event_updated": "به‌روزرسانی #{sequence}", + "event_status_tentative": "احتمالی", + "event_status_cancelled": "لغو شده", + "expand": "نمایش جزئیات", + "collapse": "مخفی کردن جزئیات" + }, + "send": "ارسال", + "more": "بیشتر", + "scheduled_banner": "زمان‌بندی شده برای ارسال در {date}", + "scheduled_send_created": "ایمیل برای ارسال زمان‌بندی شد", + "sending_in": "ارسال تا {seconds} ثانیه دیگر", + "undo_send_failed": "لغو ممکن نشد. شاید پیام ارسال شده باشد.", + "cancel_scheduled_send": "لغو ارسال", + "reschedule_send": "باززمان‌بندی", + "cancel_and_edit": "لغو و ویرایش", + "cancel_and_compose_again": "لغو و نوشتن مجدد", + "reschedule_prompt": "تاریخ/زمان جدید را وارد کنید", + "scheduled_actions_only": "نمای زمان‌بندی فقط عملیات زمان‌بندی را پشتیبانی می‌کند", + "undo_send_scheduled": "پیام برای ارسال زمان‌بندی شد", + "undo_send": "بازگردانی ارسال", + "contact_sidebar": { + "title": "مخاطب", + "close": "بستن نوار کناری", + "action_email": "ایمیل", + "action_email_title": "ارسال ایمیل", + "action_copy": "کپی", + "action_copy_title": "کپی ایمیل", + "action_edit_title": "ویرایش مخاطب", + "section_emails": "ایمیل‌ها", + "section_phones": "تلفن‌ها", + "section_organizations": "سازمان‌ها", + "section_addresses": "آدرس‌ها", + "section_notes": "یادداشت‌ها", + "not_in_contacts": "در مخاطبین شما نیست", + "add_to_contacts": "افزودن به مخاطبین", + "copied": "کپی شد!", + "copy_failed": "کپی ناموفق بود", + "view_contact": "مشاهده مخاطب", + "view_details": "مشاهده جزئیات" + }, + "send_now": "ارسال فوری", + "verification_code": { + "copy": "کپی کد {code}", + "copied": "کد کپی شد", + "copy_failed": "کپی کد ممکن نشد" + }, + "mv_position": "{index} از {total}", + "plugin_panel_show": "نمایش پنل افزونه", + "plugin_panel_hide": "پنهان کردن پنل افزونه" + }, + "email_composer": { + "select_from_directory": "انتخاب از فهرست", + "minimize": "کوچک کردن", + "maximize": "تمام‌صفحه", + "restore": "بازگردانی", + "new_message": "پیام جدید", + "signature_edit_hint": "برای ویرایش امضا دوبار کلیک کنید", + "reply": "پاسخ", + "reply_all": "پاسخ به همه", + "forward": "ارسال مجدد", + "reply_to": "پاسخ", + "reply_all_to": "پاسخ به همه", + "forward_message": "ارسال مجدد", + "from": "از", + "to": "به", + "cc": "رونوشت", + "bcc": "رونوشت مخفی", + "subject": "موضوع", + "body_placeholder": "پیام خود را بنویسید...", + "send": "ارسال", + "cancel": "انصراف", + "attach": "پیوست", + "attach_photos": "عکس و ویدیو", + "attach_files": "فایل‌ها", + "attach_from_files": "پیوست از فایل‌ها", + "files_picker": { + "attach": "{count, plural, =0 {پیوست} one {پیوست ۱ فایل} other {پیوست # فایل}}", + "empty_folder": "این پوشه خالی است", + "load_failed": "فایل‌های شما بارگیری نشد" + }, + "discard": "دور انداختن", + "discard_draft_title": "دور انداختن پیش‌نویس؟", + "discard_draft_confirm": "تغییرات ذخیره نشده دارید. پیش‌نویس دور انداخته شود؟", + "discard_draft_message": "پیش‌نویس حذف می‌شود.", + "saving": "در حال ذخیره...", + "sending": "در حال ارسال...", + "add_link": "افزودن لینک", + "link_url_prompt": "آدرس را وارد کنید", + "draft_saved": "پیش‌نویس ذخیره شد", + "save_failed": "ذخیره ناموفق بود", + "to_placeholder": "آدرس ایمیل گیرندگان", + "cc_placeholder": "گیرندگان رونوشت", + "bcc_placeholder": "گیرندگان رونوشت مخفی", + "subject_placeholder": "موضوع", + "cc_label": "رونوشت:", + "bcc_label": "رونوشت مخفی:", + "subject_label": "موضوع:", + "file_size_kb": "کیلوبایت", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count} ایمیل", + "no_subject": "(بدون موضوع)", + "unknown_sender": "ناشناس", + "quote": { + "reply_header": "در تاریخ {date}، {sender} نوشت:", + "forward_header": "---------- پیام ارجاعی ----------", + "from": "از: {sender}", + "date": "تاریخ: {date}", + "subject": "موضوع: {subject}", + "to": "به: {recipients}" + }, + "remove_sub_address": "حذف زیرآدرس", + "from_override": { + "toggle_off": "بازنویسی", + "toggle_on": "لغو بازنویسی", + "toggle_tooltip": "نام و آدرس فرستنده را آزادانه ویرایش کنید.", + "name_label": "نام فرستنده", + "name_placeholder": "نام", + "email_label": "آدرس ایمیل فرستنده", + "email_placeholder": "alias@example.com", + "envelope_notice": "اگر سرور شما این آدرس را به‌عنوان فرستندهٔ پاکت نپذیرد، به‌جای آن {identity} استفاده می‌شود و گیرندگان می‌توانند آن را در سرآیند Return-Path ببینند." + }, + "save_as_template": "ذخیره به عنوان قالب", + "attach_from_device": "بارگذاری از این دستگاه", + "templates_menu": "قالب‌ها", + "insert_template": "درج قالب", + "options": "گزینه‌ها", + "option_plain_text": "متن ساده", + "option_plain_text_hint": "ارسال بدون قالب‌بندی", + "option_read_receipt": "درخواست رسید خواندن", + "option_read_receipt_hint": "از گیرنده بخواهید باز کردن را تأیید کند", + "option_dsn": "اعلان تحویل", + "option_dsn_hint": "دریافت گزارش وقتی سرور آن را تحویل داد", + "option_require_tls": "الزام تحویل رمزگذاری‌شده", + "option_require_tls_hint": "فقط از طریق TLS تحویل شود، وگرنه برگشت بخورد", + "option_priority": "اولویت", + "option_priority_high": "بالا", + "option_priority_normal": "عادی", + "option_priority_low": "پایین", + "validation": { + "recipient_required": "یک گیرنده اضافه کنید", + "body_required": "پیامی بنویسید یا فایلی پیوست کنید", + "invalid_recipients": "ارسال نشد - این نشانی‌ها را بررسی کنید: {addresses}", + "attachments_uploading": "پیوست‌ها هنوز در حال بارگذاری هستند — پس از اتمام ارسال می‌شود", + "attachment_upload_failed": "ارسال نشد - بارگذاری یک پیوست ناموفق بود. آن را حذف کنید و دوباره تلاش کنید." + }, + "upload_progress": "در حال بارگذاری {uploaded} / {total}", + "upload_cancel": "لغو بارگذاری", + "upload_failed": "بارگذاری {filename} ناموفق بود", + "attachment_too_large": "«{name}» بزرگ‌تر از حدی است که سرور اجازه می‌دهد ({max} برای هر فایل)", + "attachments_total_too_large": "پیوست‌ها از سقف {max} سرور برای هر پیام بیشتر می‌شوند", + "drop_files": "فایل‌ها را اینجا رها کنید", + "show_less": "نمایش کمتر", + "send_failed": "ارسال ایمیل ناموفق بود", + "send_timeout": "سرور دیگر پاسخ نمی‌دهد. پیش از ارسال دوباره، پوشهٔ ارسال‌شده‌ها را بررسی کنید — ممکن است پیام ارسال شده باشد.", + "send_offline": "به نظر می‌رسد آفلاین هستید. پیام هنوز اینجاست، وقتی آنلاین شدید دوباره ارسالش کنید.", + "formatting": "قالب‌بندی", + "send_recipients_rejected": "ارسال نشد - سرور همه گیرندگان را رد کرد.", + "send_plugin_failed": "ارسال نشد - افزونه‌ای که ارسال را انجام می‌دهد با خطا مواجه شد یا بیش از حد طول کشید. پیش از ارسال دوباره، پوشه ارسال‌شده را بررسی کنید.", + "send_some_recipients_rejected": "ارسال شد، اما نه برای این گیرندگان - سرور آنها را رد کرد.", + "continue_draft": "ادامه پیش‌نویس", + "close_draft_title": "ذخیره یا دور انداختن پیش‌نویس؟", + "close_draft_message": "تغییرات ذخیره نشده دارید. ذخیره یا دور انداختن؟", + "smime_sign_on": "امضای S/MIME فعال", + "smime_sign_off": "فعال کردن امضای S/MIME", + "smime_encrypt_on": "رمزنگاری S/MIME فعال", + "smime_encrypt_off": "فعال کردن رمزنگاری S/MIME", + "smime_encrypt_unavailable": "رمزنگاری S/MIME در دسترس نیست", + "smime_unlock_title": "باز کردن قفل کلید S/MIME", + "smime_unlock_message": "عبارت عبور کلید S/MIME را وارد کنید.", + "smime_unlock_button": "باز کردن", + "smime_passphrase_placeholder": "عبارت عبور", + "forgot_attachment": { + "title": "آیا پیوست را فراموش کرده‌اید؟", + "message": "پیام شما به \"{keyword}\" اشاره دارد اما فایلی پیوست نشده.", + "send_anyway": "ارسال به هر حال", + "back": "بازگشت به ویرایش" + }, + "empty_subject": { + "title": "بدون موضوع ارسال شود؟", + "message": "این پیام موضوعی ندارد. با این حال ارسال شود؟", + "dont_ask_again": "دیگر نپرس", + "send_anyway": "ارسال به هر حال", + "back": "بازگشت به ویرایش" + }, + "schedule_send": "زمان‌بندی ارسال", + "schedule_send_description": "زمان انتشار پیام توسط سرور را انتخاب کنید.", + "schedule_preset": { + "tomorrow_morning": "فردا صبح", + "tomorrow_afternoon": "فردا بعدازظهر", + "monday_morning": "دوشنبه صبح" + }, + "schedule_send_required": "تاریخ و زمان را انتخاب کنید.", + "schedule_send_invalid": "تاریخ و زمان معتبر وارد کنید.", + "schedule_send_future": "تاریخ و زمان در آینده انتخاب کنید.", + "schedule_send_too_late": "این زمان دیرتر از حد مجاز سرور است.", + "schedule_send_unsupported": "ارسال زمان‌بندی شده پشتیبانی نمی‌شود.", + "schedule_send_cleanup_warning": "ارسال زمان‌بندی ایجاد شد اما پاکسازی پیش‌نویس ناموفق بود.", + "send_delay_unsupported": "تأخیر ارسال پشتیبانی نمی‌شود.", + "send_delay_unsupported_confirm": "این حساب تأخیر ارسال ندارد. بلافاصله ارسال شود؟", + "recipient_edit_email": "ویرایش آدرس ایمیل", + "recipient_edit_name": "ویرایش نام نمایشی", + "recipient_email_placeholder": "آدرس ایمیل", + "recipient_name_placeholder": "نام نمایشی", + "invalid_recipient": "نشانی ایمیل معتبر نیست", + "autocomplete_search_server": "جستجو در سرور", + "autocomplete_searching": "در حال جستجو...", + "mention_recipients": "گیرندگان", + "mention_card_not_recipient": "در «به» یا «رونوشت» نیست، پس این پیام را دریافت نمی‌کند.", + "mention_card_add": "افزودن به {field}", + "mention_card_remove": "حذف اشاره", + "toolbar": { + "bold": "پررنگ", + "italic": "کج", + "underline": "زیرخط‌دار", + "strikethrough": "خط‌خورده", + "text_color": "رنگ متن", + "remove_color": "حذف رنگ", + "heading_1": "سرفصل ۱", + "heading_2": "سرفصل ۲", + "bullet_list": "فهرست نشانه‌دار", + "ordered_list": "فهرست شماره‌دار", + "quote": "نقل‌قول", + "code_block": "بلوک کد", + "align_left": "تراز چپ", + "align_center": "وسط‌چین", + "align_right": "تراز راست", + "text_direction": "جهت متن (RTL/LTR)", + "link": "پیوند", + "table": "جدول", + "clear_formatting": "پاک کردن قالب‌بندی", + "undo": "واگرد", + "redo": "انجام دوباره", + "add_row_above": "افزودن ردیف در بالا", + "add_row_below": "افزودن ردیف در پایین", + "add_column_before": "افزودن ستون قبل", + "add_column_after": "افزودن ستون بعد", + "delete_row": "حذف ردیف", + "delete_column": "حذف ستون", + "toggle_header_row": "تغییر وضعیت ردیف سرصفحه", + "delete_table": "حذف جدول", + "pick_size": "انتخاب اندازه", + "font_size": "اندازه فونت", + "font_size_default": "پیش‌فرض", + "background_color": "رنگ پس‌زمینه", + "remove_background_color": "حذف رنگ پس‌زمینه" + }, + "send_filing_warning": "ارسال شد - اما پاک‌سازی پس از ارسال ناموفق بود، ممکن است پیش‌نویس قدیمی باقی بماند.", + "pop_out": "باز کردن پاسخ در پنجره جدا", + "send_guard_failed": "ارسال نشد - افزونه‌ای که پیام‌ها را پیش از ارسال بررسی می‌کند ناموفق بود یا پاسخ نداد." + }, + "confirm_dialog": { + "confirm": "تأیید", + "cancel": "انصراف" + }, + "common": { + "loading": "در حال بارگذاری...", + "error": "خطا", + "success": "موفقیت", + "cancel": "انصراف", + "save": "ذخیره", + "delete": "حذف", + "edit": "ویرایش", + "close": "بستن", + "search": "جستجو", + "refresh": "بازنشانی", + "settings": "تنظیمات", + "help": "راهنما", + "logout": "خروج", + "yes": "بله", + "no": "خیر", + "unknown": "ناشناس", + "app_title": "ایمیل تحت وب", + "reconnecting": "ارتباط قطع شد. در حال تلاش برای اتصال مجدد…", + "rate_limited_title": "احراز هویت سرور موقتاً محدود شده است.", + "rate_limited_detail": "Bulwark درخواست‌های پس‌زمینه را متوقف کرده است. تلاش مجدد در {seconds} ثانیه.", + "rate_limited_action_title": "درخواست برای جلوگیری از قفل متوقف شد.", + "rate_limited_action_detail": "Bulwark منتظر پایان زمان انتظار سرور است. در {seconds} ثانیه دوباره تلاش کنید.", + "pull_to_refresh": "برای تازه‌سازی بکشید", + "release_to_refresh": "برای تازه‌سازی رها کنید", + "refreshing": "در حال تازه‌سازی…" + }, + "notifications": { + "email_sent": "ایمیل با موفقیت ارسال شد", + "email_deleted": "ایمیل حذف شد", + "email_archived": "ایمیل بایگانی شد", + "emails_deleted": "{count, plural, other {# ایمیل حذف شد}}", + "emails_archived": "{count, plural, other {# ایمیل بایگانی شد}}", + "error_archiving": "بایگانی ایمیل ناموفق بود", + "error_updating": "به‌روزرسانی ایمیل ناموفق بود", + "email_starred": "ایمیل ستاره‌دار شد", + "email_unstarred": "ستاره ایمیل برداشته شد", + "email_marked_read": "ایمیل خوانده شده علامت‌گذاری شد", + "email_marked_unread": "ایمیل خوانده نشده علامت‌گذاری شد", + "copied_to_clipboard": "در کلیپ‌بورد کپی شد", + "source_copied": "منبع در کلیپ‌بورد کپی شد", + "error_sending": "ارسال ایمیل ناموفق بود", + "error_deleting": "حذف ایمیل ناموفق بود", + "error_loading": "بارگذاری ایمیل‌ها ناموفق بود", + "new_email": "ایمیل جدید", + "new_email_from": "از {sender}", + "click_to_view": "برای مشاهده کلیک کنید", + "email_moved": "ایمیل منتقل شد", + "emails_moved": "{count} ایمیل منتقل شد", + "moved_to_mailbox": "منتقل شد به {mailbox}", + "move_failed": "انتقال ناموفق بود", + "removed_from_folder": "از {mailbox} حذف شد", + "remove_from_folder_failed": "حذف از این پوشه انجام نشد", + "emails_copied": "{count} ایمیل کپی شد", + "copy_failed": "کپی ناموفق بود", + "move_error": "انتقال ایمیل‌ها به پوشه انتخاب شده ممکن نیست", + "email_tagged": "ایمیل برچسب‌گذاری شد", + "emails_tagged": "{count} ایمیل برچسب‌گذاری شد", + "tag_failed": "برچسب‌گذاری ناموفق بود", + "identity_created": "هویت با موفقیت ایجاد شد", + "identity_updated": "هویت با موفقیت به‌روزرسانی شد", + "identity_deleted": "هویت حذف شد", + "identity_set_primary": "هویت اصلی به‌روزرسانی شد", + "identity_create_failed": "ایجاد هویت ناموفق بود: {error}", + "identity_update_failed": "به‌روزرسانی هویت ناموفق بود: {error}", + "identity_delete_failed": "حذف هویت ناموفق بود: {error}", + "identity_unauthorized": "شما مجاز به ارسال از این آدرس ایمیل نیستید", + "identity_not_found": "هویت یافت نشد", + "vacation_saved": "تنظیمات غیبت ذخیره شد", + "vacation_save_failed": "ذخیره تنظیمات غیبت ناموفق بود", + "filters_saved": "فیلترها با موفقیت ذخیره شدند", + "filters_save_failed": "ذخیره فیلترها ناموفق بود", + "filters_deleted": "قانون فیلتر حذف شد", + "templates_exported": "قالب‌ها با موفقیت خروجی گرفته شدند", + "templates_imported": "{count, plural, one {# قالب} other {# قالب}} وارد شد", + "templates_import_errors": "برخی قالب‌ها وارد نشدند", + "templates_import_empty": "قالبی در فایل یافت نشد", + "export_email_error": "خروجی ایمیل ناموفق بود", + "import_email_success": "ایمیل با موفقیت وارد شد", + "import_email_error": "وارد کردن ایمیل ناموفق بود", + "rule_created": "قانون ایجاد شد", + "rule_merged": "به قانون «{name}» افزوده شد", + "rule_already_covered": "قانون «{name}» از قبل این را پوشش می‌دهد", + "rule_apply_existing": "{count, plural, one {اعمال روی # پیام موجود} other {اعمال روی # پیام موجود}}", + "rule_applied": "{count, plural, =0 {هیچ پیامی نیاز به تغییر نداشت} one {قانون روی # پیام اعمال شد} other {قانون روی # پیام اعمال شد}}", + "rule_apply_failed": "اعمال قانون روی پیام‌های موجود ممکن نشد", + "rule_edit": "ویرایش قانون", + "rule_undo": "بازگردانی", + "rule_undone": "تغییر قانون بازگردانده شد", + "rule_undo_failed": "بازگردانی تغییر قانون ممکن نشد", + "rule_undo_conflict": "فیلترهای شما از آن زمان تغییر کرده‌اند. تغییر را در تنظیمات فیلترها بازگردانید.", + "rule_folder_failed": "ایجاد پوشه ممکن نشد", + "vacation_filters_save_failed": "تنظیمات غیبت ذخیره شد، اما ذخیره بازارسال و گیرندگان پاسخ ناموفق بود" + }, + "date": { + "today": "امروز", + "yesterday": "دیروز", + "this_week": "این هفته", + "last_week": "هفته گذشته", + "this_month": "این ماه", + "older": "قدیمی‌تر", + "just_now": "همین الان", + "minutes_ago": "{count} دقیقه پیش", + "minutes_ago_plural": "{count} دقیقه پیش", + "hours_ago": "{count} ساعت پیش", + "hours_ago_plural": "{count} ساعت پیش", + "days_ago": "{count} روز پیش", + "days_ago_plural": "{count} روز پیش" + }, + "language": { + "title": "زبان", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "انتخاب زبان", + "switch_to_english": "تغییر به انگلیسی", + "switch_to_french": "تغییر به فرانسوی", + "switch_to_japanese": "تغییر به ژاپنی", + "switch_to_spanish": "تغییر به اسپانیایی", + "switch_to_italian": "تغییر به ایتالیایی", + "switch_to_german": "تغییر به آلمانی", + "switch_to_dutch": "تغییر به هلندی", + "switch_to_portuguese": "تغییر به پرتغالی", + "switch_to_russian": "تغییر به روسی", + "switching": "در حال تغییر زبان...", + "switch": "تغییر زبان", + "current": "زبان فعلی", + "polish": "Polski", + "switch_to_polish": "تغییر به لهستانی", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "تغییر به فارسی", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "تغییر به مغولی" + }, + "settings": { + "title": "تنظیمات", + "back_to_mail": "بازگشت به ایمیل", + "save_success": "تنظیمات با موفقیت ذخیره شد", + "import_success": "تنظیمات با موفقیت وارد شد", + "import_error": "وارد کردن تنظیمات ناموفق بود", + "reset_confirm": "آیا از بازنشانی همه تنظیمات اطمینان دارید؟", + "unsaved_changes": "تغییرات ذخیره نشده دارید", + "discard_changes": "دور انداختن تغییرات ذخیره نشده؟", + "discard": "دور انداختن", + "keep_editing": "ادامه ویرایش", + "search_placeholder": "جستجوی تنظیمات", + "search_clear": "پاک کردن جستجو", + "search_no_results": "تنظبمات منطبقی یافت نشد", + "tabs": { + "appearance": "ظاهر", + "language": "زبان، منطقه و زمان", + "email": "رفتار ایمیل", + "composer": "ویرایشگر", + "privacy": "حریم خصوصی و امنیت", + "account": "حساب", + "identities": "هویت‌ها", + "vacation": "غیبت", + "advanced": "پیشرفته", + "calendar": "تقویم", + "filters": "فیلترها", + "templates": "قالب‌ها", + "folders": "پوشه‌ها", + "keywords": "برچسب‌ها", + "security": "امنیت", + "files": "فایل‌ها", + "contacts": "مخاطبین", + "encryption": "رمزنگاری", + "protocol_handlers": "برنامه‌های پیش‌فرض", + "sidebar_apps": "برنامه‌های نوار کناری", + "notifications": "اعلان‌ها", + "layout": "چیدمان", + "themes": "پوسته‌ها", + "reading": "خواندن", + "composing": "نوشتن", + "downloads": "دانلودها", + "content_senders": "محتوا و فرستندگان", + "about_data": "درباره و داده‌ها", + "debug": "اشکال‌زدایی" + }, + "themes": { + "title": "پوسته‌ها", + "description": "از میان پوسته‌هایی که مدیر شما منتشر کرده و پیش‌تنظیم‌های داخلی انتخاب کنید.", + "locked": "انتخاب پوسته توسط مدیر قفل شده است.", + "forced": "پوسته «{name}» توسط مدیر اجباری شده و قابل تغییر نیست", + "admin_theme": "پوسته مدیر", + "activated": "پوسته فعال شد", + "default_restored": "پوسته پیش‌فرض بازیابی شد", + "default_name": "پیش‌فرض", + "default_badge": "پیش‌فرض", + "admin_enforced": "اجباری از سوی مدیر" + }, + "tab_groups": { + "general": "عمومی", + "account": "حساب و هویت", + "organization": "سازمان‌دهی ایمیل", + "apps": "برنامه‌ها", + "system": "سیستم", + "appearance": "ظاهر", + "mail": "ایمیل", + "privacy": "حریم خصوصی و امنیت", + "advanced": "پیشرفته" + }, + "appearance": { + "message_list_order": { + "label": "ترتیب فهرست پیام‌ها", + "description": "نحوهٔ مرتب‌سازی پیام‌ها در فهرست. مرتب‌سازی را سرور انجام می‌دهد، بنابراین کل پوشه را در بر می‌گیرد، نه فقط پیام‌های بارگذاری‌شده را.", + "preset": { + "chronological": "زمانی (جدیدترین ابتدا)", + "unread_first": "خوانده‌نشده‌ها ابتدا", + "starred_first": "ستاره‌دارها ابتدا", + "tagged_first": "برچسب‌دارها ابتدا", + "custom": "سفارشی" + }, + "tag_label": "برچسب", + "advanced_show": "پیشرفته: ویرایش سطوح مرتب‌سازی", + "advanced_hide": "پنهان کردن پیشرفته", + "criterion_label": "معیار سطح {n}", + "direction_label": "جهت سطح {n}", + "criteria": { + "unread": "خوانده‌نشده", + "starred": "ستاره‌دار", + "tag": "برچسب", + "receivedAt": "تاریخ دریافت", + "sentAt": "تاریخ ارسال", + "from": "فرستنده", + "subject": "موضوع", + "size": "اندازه" + }, + "direction": { + "unread_first": "خوانده‌نشده‌ها ابتدا", + "read_first": "خوانده‌شده‌ها ابتدا", + "starred_first": "ستاره‌دارها ابتدا", + "unstarred_first": "بدون ستاره ابتدا", + "tagged_first": "برچسب‌دارها ابتدا", + "untagged_first": "بدون برچسب ابتدا", + "newest_first": "جدیدترین ابتدا", + "oldest_first": "قدیمی‌ترین ابتدا", + "a_to_z": "الف → ی", + "z_to_a": "ی → الف", + "largest_first": "بزرگ‌ترین ابتدا", + "smallest_first": "کوچک‌ترین ابتدا" + }, + "add_level": "افزودن سطح", + "remove_level": "حذف سطح", + "move_up": "انتقال به بالا", + "move_down": "انتقال به پایین", + "tiebreak_note": "حداکثر سه سطح؛ سطوح بعدی در صورت تساوی تصمیم می‌گیرند. «جدیدترین ابتدا» همیشه در آخر اعمال می‌شود.", + "scope": { + "label": "اعمال بر همهٔ پوشه‌ها", + "description": "در حالت خاموش، ترتیب فقط بر صندوق ورودی اعمال می‌شود؛ پوشه‌های دیگر زمانی می‌مانند." + }, + "unsupported_note": "سرور ایمیل شما مرتب‌سازی بر اساس وضعیت خواندن، ستاره یا برچسب را پشتیبانی نمی‌کند. این سطوح نادیده گرفته می‌شوند و فهرست زمانی می‌ماند." + }, + "title": "ظاهر", + "description": "ظاهر و حس ایمیل تحت وب خود را سفارشی کنید", + "theme": { + "label": "تم", + "description": "طرح رنگی مورد نظر خود را انتخاب کنید", + "light": "روشن", + "dark": "تاریک", + "system": "سیستم" + }, + "language": { + "label": "زبان", + "description": "زبان مورد نظر خود را انتخاب کنید" + }, + "font_size": { + "label": "اندازه قلم", + "description": "اندازه متن را برای خوانایی بهتر تنظیم کنید", + "small": "کوچک", + "medium": "متوسط", + "large": "بزرگ" + }, + "list_density": { + "label": "تراکم", + "description": "فاصله‌گذاری و padding رابط کاربری را کنترل کنید", + "extra_compact": "خیلی فشرده", + "compact": "فشرده", + "regular": "معمولی", + "comfortable": "راحت" + }, + "interface_layout": { + "label": "چیدمان رابط", + "description": "جای جست‌وجو، ناوبری و نوارهای ابزار", + "default": "پیش‌فرض", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "فعال کردن انیمیشن‌ها", + "description": "نمایش transitions و افکت‌های نرم" + }, + "toolbar_position": { + "label": "موقعیت نوار ابزار", + "description": "محل نمایش دکمه‌های عملیات ایمیل", + "top": "بالا", + "below_subject": "زیر موضوع" + }, + "toolbar_labels": { + "label": "نمایش برچسب‌های نوار ابزار", + "description": "نمایش متن کنار آیکون‌های نوار ابزار" + }, + "hide_account_switcher": { + "label": "مخفی کردن تعویض حساب نوار کناری", + "description": "انتخاب‌گر حساب در بالای نوار کناری پوشه‌ها را مخفی کنید" + }, + "show_rail_account_list": { + "label": "نمایش آواتار حساب‌ها در نوار ناوبری", + "description": "دایره‌های حساب جداگانه در پایین نوار ناوبری برای تعویض سریع" + }, + "unified_mailbox": { + "label": "صندوق پستی یکپارچه", + "description": "نمایش پوشه‌های ترکیبی در همه حساب‌های متصل", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "شامل صندوق ورودی گروه‌ها", + "description": "صندوق ورودی گروه‌های اشتراکی را هم ادغام کن" + } + }, + "all_mail": { + "folders_label": "پوشه‌ها در همه ایمیل‌ها", + "folders_description": "انتخاب کنید کدام پوشه‌ها ادغام شوند", + "no_folders": "پوشه‌ای موجود نیست", + "account_hint": "برای {account} اعمال می‌شود." + }, + "colorful_sidebar_icons": { + "label": "آیکون‌های رنگی نوار کناری", + "description": "رنگ‌آمیزی آیکون‌های پوشه و برچسب بر اساس نوع" + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "نمایش تعداد کل پیام‌ها", + "description": "تعداد کل پیام‌ها را در کنار پوشه‌ها و برچسب‌ها، همراه با تعداد خوانده‌نشده‌ها نمایش می‌دهد. برای نمایش فقط تعداد خوانده‌نشده‌ها غیرفعال کنید." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "نمایش حساب‌های ایمیل اشتراکی", + "description": "پوشه‌های صندوق‌هایی که با شما به اشتراک گذاشته شده‌اند در نوار کناری زیر «اشتراکی» نمایش داده شوند. برای پنهان کردن همه خاموش کنید، یا حساب‌های جداگانه را در پایین پنهان کنید. تقویم‌ها و مخاطبان آن‌ها باقی می‌مانند." + }, + "pro_interface": { + "label": "رابط حرفه‌ای (آزمایشی)", + "description": "چیدمان قدرت-کاربری فقط دسکتاپ", + "open_label": "باز کردن رابط حرفه‌ای", + "back_to_standard": "بازگشت به استاندارد" + }, + "cross_all": { + "label": "همه حساب‌ها: همه ایمیل‌ها", + "description": "نمایش یک مورد در بخش همه حساب‌ها که همه ایمیل‌های همه حساب‌ها را فهرست می‌کند، شامل همه پوشه‌ها به جز هرزنامه، ارسال‌شده، بایگانی، زباله‌دان و پیش‌نویس‌ها." + }, + "cross_starred": { + "label": "همه حساب‌ها: ستاره‌دار", + "description": "نمایش یک مورد در بخش همه حساب‌ها که ایمیل‌های ستاره‌دار همه حساب‌ها را فهرست می‌کند، شامل همه پوشه‌ها به جز هرزنامه، ارسال‌شده، بایگانی، زباله‌دان و پیش‌نویس‌ها." + }, + "cross_unread": { + "label": "همه حساب‌ها: خوانده‌نشده", + "description": "نمایش یک مورد در بخش همه حساب‌ها که ایمیل‌های خوانده‌نشده همه حساب‌ها را فهرست می‌کند، شامل همه پوشه‌ها به جز هرزنامه، ارسال‌شده، بایگانی، زباله‌دان و پیش‌نویس‌ها." + } + }, + "keywords": { + "title": "برچسب‌های ایمیل", + "description": "برچسب‌ها را برای سازمان‌دهی ایمیل‌ها تعریف کنید", + "add_keyword": "افزودن برچسب", + "label_field": "نام نمایشی", + "label_placeholder": "مثال: کاری، شخصی، فوری", + "id_field": "شناسه برچسب", + "id_placeholder": "مثال: work, personal", + "color_field": "رنگ", + "id_exists": "این شناسه قبلاً وجود دارد", + "edit": "ویرایش برچسب", + "delete": "حذف برچسب", + "save": "ذخیره", + "add": "افزودن", + "cancel": "انصراف", + "migrating": "در حال به‌روزرسانی برچسب روی ایمیل‌های موجود…", + "migration_error": "به‌روزرسانی برچسب ناموفق بود", + "filters_migration_error": "برخی قوانین فیلتر هنوز از برچسب قدیمی استفاده می‌کنند — آن‌ها را دستی به‌روزرسانی کنید", + "nesting": { + "label": "برچسب‌های تودرتو", + "description": "برچسب‌ها را زیر برچسب‌های دیگر قرار دهید و آن‌ها را به‌صورت درختی در نوار کناری نمایش دهید." + }, + "parent_field": "برچسب والد", + "no_parent": "بدون برچسب والد", + "too_long": "این مسیر برچسب خیلی طولانی است (حداکثر {max} نویسه)", + "has_children_locked": "برچسب‌های دیگری زیر این برچسب قرار دارند، بنابراین نام و برچسب والد آن قفل است. ابتدا آن‌ها را جابه‌جا یا حذف کنید.", + "has_children_delete": "ابتدا برچسب‌های زیرمجموعهٔ این برچسب را حذف کنید", + "visibility_field": "نمایش در نوار کناری", + "visibility": { + "show": "نمایش", + "unread": "نمایش در صورت وجود خوانده‌نشده", + "hide": "پنهان کردن" + }, + "unrecognized": { + "title": "بازیابی برچسب‌ها از سرور", + "description": "نام و رنگ برچسب‌ها در این مرورگر ذخیره می‌شود، اما خود برچسب‌ها به‌صورت کلیدواژه‌های JMAP روی ایمیل‌های شما قرار دارند. ایمیل‌ها را برای یافتن کلیدواژه‌هایی که هیچ برچسبی اینجا ندارند بررسی کنید و آن‌ها را دوباره اضافه کنید.", + "scan": "جست‌وجوی برچسب‌ها", + "rescan": "بررسی دوباره", + "scanning": "در حال بررسی… {scanned} از {total} ایمیل", + "none": "همهٔ برچسب‌های ایمیل‌های شما از قبل اینجا تعریف شده‌اند.", + "messages": "{count, plural, one {# ایمیل} other {# ایمیل}}", + "add_all": "افزودن همه ({count})", + "partial": "فقط {scanned} ایمیل جدیدتر از مجموع {total} بررسی شد. برچسب‌هایی که تنها در ایمیل‌های قدیمی‌تر به کار رفته‌اند ممکن است نمایش داده نشوند.", + "error": "بررسی ایمیل‌ها برای یافتن برچسب‌ها ناموفق بود." + } + }, + "notifications": { + "test_sound": "تست صدای اعلان", + "sounds": { + "default": "پیش‌فرض (بیپ)", + "cheerful": "شاد", + "involved": "درگیر", + "swift": "سریع", + "relax": "آرامش" + }, + "push": { + "title": "اعلان‌های پس‌زمینه", + "description": "دریافت اعلان‌های سیستم برای ایمیل جدید", + "relay_label": "رله push", + "relay_desc": "پیش‌فرض رله میزبانی شده Bulwark", + "relay_locked": "تنظیم شده توسط مدیر", + "relay_locked_desc": "قابل تغییر نیست", + "status_active": "فعال روی این دستگاه", + "status_inactive": "فعال نشده", + "status_unsupported": "مرورگر پشتیبانی نمی‌کند", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "در حال کار…", + "enable": "فعال کردن", + "reenable": "ثبت مجدد", + "disable": "غیرفعال کردن", + "confirm_disable_title": "غیرفعال کردن اعلان‌های پس‌زمینه؟", + "confirm_disable_message": "این دستگاه دیگر هشدار دریافت نمی‌کند", + "dismiss_aria": "رد کردن پیشنهاد اعلان‌ها", + "ios_hint": "در iOS ابتدا سایت را به صفحه اصلی اضافه کنید", + "devices_title": "دستگاه‌های ثبت‌شده", + "devices_desc": "همهٔ دستگاه‌هایی که برای دریافت اعلان‌های پس‌زمینهٔ این حساب ثبت شده‌اند. با لغو یک دستگاه، دریافت بی‌درنگ متوقف می‌شود.", + "devices_loading": "در حال بارگذاری دستگاه‌های ثبت‌شده…", + "devices_empty": "هیچ دستگاهی برای اعلان‌های پس‌زمینه ثبت نشده است.", + "devices_error": "بارگذاری دستگاه‌های ثبت‌شده ممکن نشد.", + "devices_refresh": "بازخوانی", + "device_this": "این دستگاه", + "device_other": "دستگاه دیگر", + "device_status_active": "در حال تحویل اعلان‌ها", + "device_status_inactive": "هرگز اعلانی تحویل نداده است", + "device_status_unknown": "برای این بازپخش ناشناس", + "device_expires": "انقضا در {date}", + "revoke": "لغو", + "confirm_revoke_title": "این دستگاه لغو شود؟", + "confirm_revoke_message": "بی‌درنگ دریافت اعلان‌های پس‌زمینه را متوقف می‌کند. با روشن‌کردن دوبارهٔ پوش روی آن دستگاه، دوباره ثبت می‌شود.", + "confirm_revoke_message_this": "این دستگاه بی‌درنگ دریافت اعلان‌های پس‌زمینه را متوقف می‌کند." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "صدای اعلان", + "description": "صدای پخش شده برای اعلان‌ها", + "choose": "صدا", + "choose_desc": "یک آهنگ انتخاب کنید" + }, + "email": { + "title": "اعلان‌های ایمیل", + "description": "پیکربندی اعلان‌های ایمیل", + "enabled": "اعلان‌های ایمیل", + "enabled_desc": "نمایش اعلان هنگام ایمیل جدید", + "sound": "صدای اعلان", + "sound_desc": "پخش هشدار صوتی", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "اعلان‌های تقویم", + "description": "پیکربندی اعلان‌های تقویم", + "enabled": "اعلان‌های رویداد", + "enabled_desc": "نمایش هشدار برای رویدادها", + "sound": "صدای اعلان", + "sound_desc": "پخش صدا برای یادآورها", + "invitation_parsing": "پردازش دعوت‌نامه‌های ایمیل", + "invitation_parsing_desc": "تشخیص دعوت‌نامه در پیوست ایمیل" + } + }, + "language_region": { + "title": "زبان، منطقه و زمان", + "description": "تنظیمات زبان، فرمت تاریخ و زمان", + "language": { + "label": "زبان", + "description": "زبان مورد نظر را انتخاب کنید", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "فرمت تاریخ", + "description": "نحوه نمایش تاریخ‌ها", + "smart": "هوشمند", + "relative": "نسبی", + "full": "تاریخ کامل", + "preview_today": "امروز:", + "preview_this_week": "این هفته:", + "preview_older": "قدیمی‌تر:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "فرمت زمان", + "description": "۱۲ یا ۲۴ ساعته", + "12h": "۱۲ ساعته", + "24h": "۲۴ ساعته" + }, + "first_day": { + "label": "اولین روز هفته", + "description": "یکشنبه یا دوشنبه", + "sunday": "یکشنبه", + "monday": "دوشنبه" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "پاک کردن جستجو هنگام تغییر پوشه", + "description": "کلیک روی یک پوشه (صندوق ورودی، ارسال‌شده…) جستجوی فعال را پاک می‌کند و آن پوشه را نشان می‌دهد. وقتی خاموش باشد، جستجو باقی می‌ماند و در پوشهٔ جدید دوباره اجرا می‌شود." + }, + "request_read_receipt": { + "label": "درخواست تأیید خواندن", + "description": "پیش‌فعال کردن تأیید خواندن" + }, + "read_receipt_response": { + "label": "پاسخ به تأیید خواندن", + "description": "نحوه پاسخ به درخواست تأیید", + "ask": "هر بار بپرس", + "always": "همیشه ارسال کن", + "never": "هرگز ارسال نکن" + }, + "title": "رفتار ایمیل", + "description": "پیکربندی نحوه مدیریت ایمیل‌ها", + "mark_read": { + "label": "علامت‌گذاری خوانده شده", + "description": "زمان علامت‌گذاری", + "instant": "بلافاصله", + "delay_3s": "بعد از ۳ ثانیه", + "delay_5s": "بعد از ۵ ثانیه", + "never": "هرگز" + }, + "delete_action": { + "label": "عملیات حذف", + "description": "نتیجه حذف ایمیل", + "trash": "انتقال به زباله‌دان", + "trash_and_read": "زباله‌دان و خوانده شده", + "permanent": "حذف دائمی", + "warning": "غیرقابل بازگشت" + }, + "message_spacing": { + "label": "فاصله‌گذاری پیام", + "description": "حاشیه اطراف متن پیام در نمای خواندن", + "auto": "خودکار (پیشنهادی)", + "always": "همیشه فاصله اضافه شود", + "edge": "لبه به لبه" + }, + "plain_text_font": { + "label": "قلم متن ساده", + "description": "قلم مورد استفاده برای ایمیل‌های متن ساده", + "mono": "تک‌عرض", + "sans": "معمولی (مانند برنامه)" + }, + "archive_mode": { + "label": "بایگانی در", + "description": "سازمان‌دهی بایگانی", + "single": "یک پوشه", + "year": "پوشه در سال", + "month": "پوشه در ماه", + "reorganize": "بازسازمان‌دهی", + "reorganize_success": "{count, plural, =0 {بدون ایمیل} =1 {۱ ایمیل} other {# ایمیل}} بازسازمان‌دهی شد", + "reorganize_error": "خطای بازسازمان‌دهی" + }, + "permanently_delete_junk": { + "label": "حذف دائمی هرزنامه", + "description": "حذف مستقیم هرزنامه" + }, + "confirm_bulk_delete": { + "label": "تأیید پیش از حذف چند پیام", + "description": "پیش از انتقال چند پیام انتخاب‌شده به زباله‌دان بپرس. حذف دائمی همیشه می‌پرسد." + }, + "mail_layout": { + "label": "چیدمان ایمیل", + "description": "انتخاب چیدمان", + "split": "پنل تقسیم شده", + "split_description": "لیست و پنل کنار هم", + "focus": "لیست متمرکز", + "focus_description": "نمای متمرکز", + "horizontal": "پنل پایین", + "horizontal_description": "لیست بالا، پنل پایین" + }, + "list_columns": { + "label": "نمایش فهرست به‌صورت ستونی", + "description": "ردیف‌های تک‌خطی را به‌صورت جدولی با ستون‌های فرستنده، موضوع، تاریخ و اندازه زیر یک سرستون نشان می‌دهد." + }, + "show_preview": { + "label": "نمایش پیش‌نمایش", + "description": "پیش‌نمایش در لیست", + "focus_description": "پیش‌نمایش در نمای متمرکز" + }, + "show_verification_codes": { + "label": "نمایش کدهای تأیید", + "description": "پیشنهاد کد ایمیل‌های ورود و تأیید برای کپی با یک کلیک، در فهرست به مدت یک روز و در پیام" + }, + "disable_threading": { + "label": "غیرفعال کردن گروه‌بندی", + "description": "پیام‌های جداگانه" + }, + "plain_text_mode": { + "label": "فقط متن ساده", + "description": "غیرفعال کردن rich text" + }, + "auto_select_reply_identity": { + "label": "پاسخ از آدرس دریافتی", + "description": "ارسال با آدرس دریافت شده" + }, + "reply_identity_match": { + "label": "تطبیق نشانی دریافت", + "description": "کدام نشانی‌های دریافت به‌عنوان نشانی شما در نظر گرفته شوند. «فقط نشانی دقیق» تنها یکی از هویت‌های پیکربندی‌شدهٔ شما را انتخاب می‌کند؛ «همان دامنه» هر نشانی دیگری در دامنه‌های هویت شما را نیز نام مستعار catch-all در نظر می‌گیرد و سرآیند فرستنده را با آن بازنویسی می‌کند. اگر این نشانی‌ها فهرست توزیع هستند، نشانی دقیق را انتخاب کنید.", + "exact": "فقط نشانی دقیق", + "domain": "هر نشانی در دامنه‌های من" + }, + "signature_position": { + "label": "موقعیت امضا", + "description": "محل درج امضا", + "above_quote": "قبل از نقل قول", + "below_quote": "بعد از نقل قول" + }, + "signature_separator": { + "label": "جداکننده امضا", + "description": "پیشوند \"-- \"" + }, + "sub_address_delimiter": { + "label": "جداکننده زیرآدرس", + "description": "کاراکتری که نام کاربری را از برچسب زیرآدرس جدا می‌کند. با جداکننده‌ای که سرور ایمیل شما استفاده می‌کند مطابقت دهید (مثلاً user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "سفارشی…", + "custom_input_label": "کاراکتر سفارشی" + }, + "attachment_click_action": { + "label": "کلیک روی پیوست", + "description": "پیش‌نمایش یا دانلود", + "preview": "پیش‌نمایش", + "download": "دانلود" + }, + "attachment_position": { + "label": "موقعیت پیوست", + "description": "محل پیوست‌ها", + "beside-sender": "کنار فرستنده", + "below-header": "زیر هدر" + }, + "emails_per_page": { + "10": "۱۰ ایمیل", + "25": "۲۵ ایمیل", + "50": "۵۰ ایمیل", + "100": "۱۰۰ ایمیل", + "label": "ایمیل در صفحه", + "description": "تعداد در هر بار" + }, + "always_light_mode": { + "label": "حالت روشن برای ایمیل‌ها", + "description": "رندر روشن حتی در تم تاریک" + }, + "external_content": { + "label": "محتوای خارجی", + "description": "مدیریت تصاویر خارجی", + "ask": "همیشه بپرس", + "block": "همیشه مسدود کن", + "allow": "همیشه مجاز کن" + }, + "trusted_senders": { + "label": "فرستندگان مورد اعتماد", + "description": "مدیریت فرستندگان", + "count_zero": "هیچ", + "count_one": "۱ فرستنده", + "count_other": "{count} فرستنده", + "modal_title": "فرستندگان مورد اعتماد", + "empty_title": "هنوز فرستنده‌ای نیست", + "empty_description": "از طریق نمایش ایمیل اضافه کنید", + "add_manually": "افزودن دستی", + "add_button": "افزودن", + "add_placeholder": "آدرس ایمیل", + "search_placeholder": "جستجو...", + "no_results": "مطابقی نیست", + "remove": "حذف", + "close": "بستن", + "invalid_email": "ایمیل معتبر وارد کنید", + "already_added": "قبلاً اضافه شده", + "save_error": "خطای ذخیره", + "use_address_book_label": "همگام‌سازی با دفترچه", + "use_address_book_description": "ذخیره در دفترچه آدرس" + }, + "hover_actions": { + "label": "عملیات هاور", + "description": "عملیات سریع در لیست", + "delete": "حذف", + "star": "ستاره", + "mark_read": "خوانده شده", + "archive": "بایگانی", + "tag": "برچسب", + "spam": "هرزنامه", + "none_selected": "انتخاب نشده", + "mode_label": "حالت نمایش", + "mode_inline": "داخلی", + "mode_floating": "شناور", + "corner_label": "موقعیت", + "corner_top-left": "بالا چپ", + "corner_top-right": "بالا راست", + "corner_bottom-left": "پایین چپ", + "corner_bottom-right": "پایین راست", + "not_spam": "هرزنامه نیست" + }, + "default_mail_program": { + "label": "برنامه ایمیل پیش‌فرض", + "description": "{appName} را به‌عنوان برنامهٔ پیش‌فرض ایمیل برای پیوندهای mailto: ثبت کنید", + "button": "تنظیم به عنوان پیش‌فرض", + "success": "ثبت شد", + "error": "پشتیبانی نمی‌شود" + }, + "attachment_reminder": { + "label": "یادآور پیوست", + "description": "هشدار فراموشی پیوست", + "keywords_label": "کلمات کلیدی", + "keywords_description": "کلمات محرک", + "add_placeholder": "افزودن...", + "add": "افزودن", + "remove": "حذف" + }, + "empty_subject_warning": { + "label": "هشدار موضوع خالی", + "description": "پیش از ارسال پیام بدون موضوع تأیید بگیرید." + }, + "recipient_mentions": { + "label": "اشاره به گیرندگان با @", + "description": "تایپ @ در متن پیام، گیرندگان را پیشنهاد می‌دهد و نام کوچک فرد انتخاب‌شده را درج می‌کند. Enter یا Tab نام برجسته‌شده را انتخاب می‌کند." + }, + "mention_adds_recipient": { + "label": "مخاطبان در اشاره با @", + "description": "با تایپ @ مخاطبان شما و افراد فهرست سازمان هم پیشنهاد می‌شوند. انتخاب کسی که هنوز گیرنده نیست، او را به پیام اضافه می‌کند.", + "to": "افزودن به «به»", + "cc": "افزودن به «رونوشت»", + "off": "فقط گیرندگان" + }, + "hide_inline_image_attachments": { + "label": "مخفی کردن تصاویر داخلی", + "description": "عدم نمایش به عنوان پیوست" + }, + "attachment_image_previews": { + "label": "پیش‌نمایش تصویر پیوست", + "description": "نمایش thumbnail" + }, + "send_delay": { + "label": "تأخیر ارسال", + "description": "پنجره تأخیر", + "off": "خاموش", + "seconds": "{seconds} ثانیه", + "unsupported": "پشتیبانی نمی‌شود" + }, + "return_to_list_after_action": { + "label": "بازگشت به فهرست پس از حذف یا علامت‌گذاری به‌عنوان خوانده‌نشده", + "description": "پس از حذف یا علامت‌گذاری پیام باز به‌عنوان خوانده‌نشده، به‌جای باز کردن پیام بعدی به فهرست پیام‌ها بازگردید." + }, + "swipe_right_action": { + "label": "عملکرد کشیدن به راست (موبایل)", + "description": "در دستگاه‌های لمسی، عملی که هنگام کشیدن یک پیام به راست در فهرست انجام می‌شود." + }, + "swipe_left_action": { + "label": "عملکرد کشیدن به چپ (موبایل)", + "description": "در دستگاه‌های لمسی، عملی که هنگام کشیدن یک پیام به چپ در فهرست انجام می‌شود." + }, + "swipe_actions": { + "none": "هیچ", + "archive": "بایگانی", + "delete": "حذف", + "mark_read": "تغییر وضعیت خوانده‌شده", + "star": "تغییر وضعیت ستاره", + "spam": "علامت‌گذاری به عنوان هرزنامه" + }, + "rtl_editing": { + "label": "پشتیبانی از ویرایش راست‌به‌چپ", + "description": "دکمه جهت متن را به نوار ابزار ویرایشگر اضافه می‌کند تا بتوانید پاراگراف‌ها را چپ‌به‌راست یا راست‌به‌چپ تنظیم کنید" + } + }, + "composer": { + "title": "ویرایشگر", + "description": "تنظیمات نوشتن ایمیل", + "autosave": { + "label": "ذخیره خودکار", + "description": "فاصله ذخیره پیش‌نویس", + "30s": "هر ۳۰ ثانیه", + "1m": "هر دقیقه", + "2m": "هر ۲ دقیقه", + "5m": "هر ۵ دقیقه" + }, + "send_confirmation": { + "label": "تأیید ارسال", + "description": "تأیید قبل از ارسال" + }, + "default_reply": { + "label": "حالت پیش‌فرض پاسخ", + "description": "عملیات کلیک پاسخ", + "reply": "پاسخ", + "reply_all": "پاسخ به همه" + } + }, + "privacy": { + "title": "حریم خصوصی و امنیت", + "description": "مدیریت تنظیمات", + "external_images": { + "label": "مسدود کردن تصاویر خارجی", + "description": "جلوگیری از ردیابی" + }, + "session_timeout": { + "label": "مهلت نشست", + "description": "خروج خودکار", + "never": "هرگز", + "30m": "۳۰ دقیقه", + "1h": "۱ ساعت", + "4h": "۴ ساعت" + }, + "clear_cache": { + "label": "پاک کردن کش", + "description": "حذف داده‌های موقت", + "button": "پاک کردن کش", + "confirm": "اطمینان دارید؟", + "success": "کش پاک شد" + } + }, + "account": { + "title": "حساب", + "description": "اطلاعات حساب", + "name_label": "نام نمایشی", + "username_label": "نام کاربری", + "account_type_label": "نوع حساب", + "auth_method_label": "احراز هویت", + "auth_method_oauth": "SSO", + "auth_method_basic": "رمز عبور", + "auth_method_token": "توکن دسترسی", + "demo_account": "حساب آزمایشی", + "email": { + "label": "آدرس ایمیل", + "value": "{email}" + }, + "server": { + "label": "سرور JMAP", + "value": "{server}" + }, + "storage": { + "label": "مصرف فضا", + "used": "{used} از {total}", + "percentage": "{percent}٪" + }, + "last_sync": { + "label": "آخرین همگام‌سازی", + "value": "{time}" + }, + "accounts": { + "title": "حساب‌های وارد شده", + "description": "ترتیب‌دهی حساب‌ها", + "active": "حساب فعال", + "default_badge": "پیش‌فرض", + "set_default": "تنظیم پیش‌فرض", + "switch_to": "تعویض", + "move_up": "بالا", + "move_down": "پایین", + "drag_handle": "کشیدن", + "add": "افزودن" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "اشتراکی با من", + "description": "حساب‌های گروهی", + "shared_label": "حساب اشتراکی" + } + }, + "scoped": { + "back": "بازگشت به حساب من", + "managing": "مدیریت: {name}" + }, + "security": { + "title": "امنیت حساب", + "description": "مدیریت امنیت", + "detecting": "شناسایی قابلیت‌ها...", + "not_available": "در دسترس نیست", + "password": { + "title": "تغییر رمز عبور", + "current": "رمز فعلی", + "new": "رمز جدید", + "confirm": "تأیید رمز جدید", + "submit": "تغییر رمز", + "success": "رمز تغییر کرد", + "error_title": "خطا", + "error_mismatch": "مطابقت ندارد", + "error_min_length": "حداقل ۸ کاراکتر", + "error_generic": "تغییر ناموفق" + }, + "display_name": { + "label": "نام نمایشی", + "description": "نام روی سرور", + "placeholder": "نام خود را وارد کنید", + "save": "ذخیره", + "success": "به‌روزرسانی شد", + "error": "خطا" + }, + "totp": { + "section_title": "احراز هویت دو مرحله‌ای", + "label": "TOTP", + "description": "لایه امنیتی اضافی", + "active": "فعال", + "inactive": "غیرفعال", + "enabled": "2FA فعال شد", + "disabled": "2FA غیرفعال شد", + "enable_error": "خطای فعال‌سازی", + "disable_error": "خطای غیرفعال‌سازی", + "setup_instructions": "در برنامه authenticator کپی کنید:", + "verification_code": "کد تأیید", + "confirm": "تأیید", + "disable": "غیرفعال کردن", + "disable_confirm_prompt": "رمز عبور را وارد کنید", + "password_required": "رمز الزامی است", + "code_required": "کد الزامی است", + "code_invalid": "کد نامعتبر" + }, + "app_passwords": { + "title": "رمزهای برنامه", + "description": "برای برنامه‌های بدون 2FA", + "add": "افزودن", + "create": "ایجاد", + "cancel": "انصراف", + "done": "انجام شد", + "generate": "تولید", + "name_label": "نام برنامه", + "name_placeholder": "مثال: Thunderbird", + "expires_label": "انقضا", + "allowed_ips_label": "IPهای مجاز", + "allowed_ips_placeholder": "10.0.0.5", + "allowed_ips_hint": "با کاما جدا کنید", + "password_label": "رمز عبور", + "password_placeholder": "خودکار تولید می‌شود", + "copy_now_warning": "همین حالا کپی کنید", + "added": "ایجاد شد", + "removed": "حذف شد", + "add_error": "خطای ایجاد", + "remove_error": "خطای حذف", + "none": "پیکربندی نشده" + }, + "api_keys": { + "title": "کلیدهای API", + "description": "برای اسکریپت‌ها", + "name_label": "نام کلید", + "name_placeholder": "مثال: اسکریپت پشتیبان", + "copy_now_warning": "همین حالا کپی کنید", + "added": "ایجاد شد", + "removed": "حذف شد", + "add_error": "خطای ایجاد", + "remove_error": "خطای حذف", + "none": "پیکربندی نشده" + }, + "encryption": { + "section_title": "رمزنگاری", + "label": "رمزنگاری ایمیل", + "description": "رمزنگاری ذخیره", + "active": "{type} فعال", + "inactive": "غیرفعال", + "enabled": "فعال شد", + "disabled_success": "غیرفعال شد", + "error": "خطا", + "algorithm_label": "الگوریتم رمزنگاری", + "encrypt_on_append": "رمزنگاری ایمیل‌های جدید هنگام بارگذاری", + "allow_spam_training": "اجازه به آموزش هرزنامه پیش از رمزنگاری ایمیل‌ها", + "enable_encryption": "فعال‌سازی رمزنگاری", + "enable_tooltip": "فعال‌سازی رمزنگاری با این کلید", + "configure_title": "فعال‌سازی رمزنگاری با این کلید", + "enable_button": "فعال‌سازی", + "disable_tooltip": "غیرفعال‌سازی رمزنگاری", + "enabled_success": "فعال شد" + }, + "email_client": { + "title": "تنظیم برنامه ایمیل", + "description": "اطلاعات برای برنامه دسکتاپ", + "jmap_username_label": "نام کاربری JMAP", + "copy": "کپی", + "copied": "کپی شد", + "password_instructions": "با رمز برنامه استفاده کنید" + }, + "link_device": { + "title": "اتصال موبایل", + "description": "بدون وارد کردن رمز عبور در گوشی، به برنامه Bulwark Mail وارد شوید. گوشی شما ورود جداگانه‌ای دریافت می‌کند، بنابراین خروج از اینجا باعث خروج آن نمی‌شود.", + "generate": "نمایش QR", + "regenerate": "کد جدید", + "instructions": "برنامه Bulwark Mail را باز کنید و در صفحه ورود روی \"Scan a sign-in code\" بزنید (یا اگر از قبل وارد شده‌اید، \"Add account\" → \"Scan a sign-in code\")، سپس دوربین را به این سمت بگیرید.", + "expires_in": "منقضی در {seconds} ثانیه", + "expired": "منقضی شده", + "error": "خطا", + "password_prompt": "برای تأیید هویت، رمز عبور خود را وارد کنید. گوشی شما ورود جداگانه‌ای دریافت می‌کند؛ رمز عبور شما به گوشی ارسال نمی‌شود.", + "show_password": "نمایش رمز عبور", + "hide_password": "پنهان کردن رمز عبور", + "continue": "ادامه", + "error_wrong_password": "رمز عبور نادرست است. دوباره تلاش کنید.", + "totp_prompt": "کد برنامه احراز هویت خود را وارد کنید.", + "error_password_or_code": "رمز عبور یا کد نادرست است.", + "error_not_signed_in": "نشست شما منقضی شده است. برای اتصال دستگاه دوباره وارد شوید.", + "error_impersonation": "هنگام مشاهده صندوق پستی کاربر دیگر نمی‌توان دستگاه متصل کرد.", + "error_too_many_attempts": "تلاش‌های بیش از حد. کمی صبر کنید و دوباره تلاش کنید.", + "error_session_secret": "اتصال دستگاه روی این سرور راه‌اندازی نشده است (SESSION_SECRET وجود ندارد). با مدیر سیستم تماس بگیرید.", + "error_server_unreachable": "دسترسی به سرور ایمیل شما ممکن نشد. دوباره تلاش کنید.", + "error_pairing_unavailable": "سرور ایمیل شما اجازه اتصال دستگاه به این روش را نمی‌دهد. با مدیر سیستم تماس بگیرید.", + "qr_alt": "کد QR ورود", + "link_label": "پیوند ورود", + "copy_link": "کپی پیوند ورود", + "copied": "کپی شد", + "copy_hint": "دوربین ندارید؟ پیوند را کپی کنید و در برنامه با \"Paste sign-in link\" جای‌گذاری کنید.", + "success_title": "ورود در گوشی شما انجام شد", + "success_body": "می‌توانید این صفحه را ببندید. برای اتصال دستگاه دیگر، کد جدیدی نمایش دهید.", + "link_another": "اتصال دستگاه دیگر", + "use_sso": "به‌جای آن از ورود یکپارچه استفاده کنید", + "error_insecure_server": "برنامه موبایل فقط از طریق https به سرورهای ایمیل وصل می‌شود و این سرور از http استفاده می‌کند." + }, + "public_keys": { + "name_label": "نام کلید عمومی", + "name_placeholder": "مثال: کلید من", + "key_label": "کلید عمومی (S/MIME یا PGP با قالب ASCII)", + "key_placeholder": "کلید عمومی خود را اینجا بچسبانید", + "none": "هیچ کلید عمومی‌ای تنظیم نشده است" + } + }, + "identities": { + "title": "هویت‌های ارسال", + "description": "مدیریت آدرس‌های ایمیلی که می‌توانید از آنها ارسال کنید", + "identities_count": { + "label": "هویت‌های شما", + "description": "آدرس‌های ایمیل پیکربندی شده برای ارسال", + "count_zero": "بدون هویت", + "count_one": "۱ هویت", + "count_other": "{count} هویت" + }, + "manage": "مدیریت هویت‌ها", + "sub_addressing": { + "label": "زیرآدرس‌دهی", + "description": "استفاده از برچسب‌هایی مثل user+tag@domain.com برای سازمان‌دهی ایمیل‌های ورودی", + "learn_more": "بیشتر بدانید" + } + }, + "vacation": { + "title": "غیبت", + "description": "رسیدگی خودکار به ایمیل‌های ورودی در زمان غیبت", + "loading": "در حال بارگذاری تنظیمات پاسخ‌گوی خودکار...", + "not_supported": "سرور ایمیل شما از پاسخ خودکار پشتیبانی نمی‌کند.", + "fetch_error": "بارگذاری تنظیمات پاسخ‌گوی خودکار ناموفق بود. لطفاً دوباره تلاش کنید.", + "status": { + "label": "ارسال پاسخ خودکار", + "description": "ارسال پاسخ خودکار به افرادی که به شما ایمیل می‌زنند", + "active": "فعال", + "inactive": "غیرفعال" + }, + "date_range": { + "start": "تاریخ شروع", + "start_description": "برای بدون محدودیت شروع خالی بگذارید", + "end": "تاریخ پایان", + "end_description": "برای بدون محدودیت پایان خالی بگذارید" + }, + "message": { + "subject_label": "موضوع", + "subject_description": "خط موضوع پاسخ خودکار", + "subject_placeholder": "خارج از دفتر", + "body_label": "بدنه پیام", + "body_description": "محتوای پیام متنی ساده", + "body_placeholder": "از ایمیل شما متشکرم. من در حال حاضر خارج از دفتر هستم و پس از بازگشت پاسخ خواهم داد.", + "html_label": "پیام قالب‌بندی‌شده (HTML)", + "html_description": "یک نسخهٔ قالب‌بندی‌شده با پیوند و استایل اضافه کنید. گیرندگانی که برنامهٔ ایمیلشان نمی‌تواند آن را نمایش دهد، متن سادهٔ بالا را می‌بینند.", + "html_placeholder": "یک پاسخ خودکار قالب‌بندی‌شده بنویسید…" + }, + "preview": { + "title": "پیش‌نمایش", + "show": "نمایش پیش‌نمایش", + "hide": "مخفی کردن پیش‌نمایش" + }, + "save": "ذخیره تغییرات", + "saving": "در حال ذخیره...", + "warnings": { + "end_before_start": "تاریخ پایان باید بعد از تاریخ شروع باشد", + "invalid_date": "تاریخ شروع و پایان معتبر را همراه با ساعت وارد کنید یا فیلد را خالی بگذارید", + "start_in_past": "تاریخ شروع در گذشته است", + "empty_body": "بدنه پیام خالی است - گیرندگان پاسخ خالی دریافت می‌کنند", + "subject_too_long": "موضوع برای سرور بیش از حد طولانی است (حداکثر {max} بایت).", + "body_too_long": "پیام برای سرور بیش از حد طولانی است (حداکثر {max} بایت، همراه با قالب‌بندی).", + "forward_address": "یک نشانی معتبر برای بازارسال وارد کنید", + "not_running": "بازارسال یا گیرندگان پاسخ ذخیره شده‌اند اما اکنون فعال نیستند. برای فعال‌سازی دوباره ذخیره کنید." + }, + "forward": { + "title": "بازارسال", + "description": "پیام‌های ورودی را با یا بدون پاسخ خودکار، در بازه زمانی بالا به نشانی دیگری بازارسال می‌کند (بدون تاریخ پایان، تا زمانی که خاموش شود). هرزنامه بازارسال نمی‌شود.", + "enabled_label": "بازارسال پیام‌ها", + "to_label": "ارسال به", + "to_placeholder": "email@example.com", + "keep_label": "نگه داشتن در صندوق ورودی", + "keep_description": "در غیر این صورت پیام فقط بازارسال می‌شود: در این صندوق پستی نمی‌ماند و هیچ قانون فیلتری روی آن اجرا نمی‌شود." + }, + "audience": { + "label": "پاسخ به", + "description": "فرستندگان داخلی: {domains}", + "all": "همه فرستندگان", + "internal": "فقط فرستندگان داخلی", + "external": "فقط فرستندگان خارجی" + }, + "auto_reply": { + "title": "پاسخ خودکار" + } + }, + "folders": { + "role_memos": "یادداشت‌ها", + "title": "پوشه‌ها", + "description": "مدیریت پوشه‌های ایمیل و تخصیص نقش‌های استاندارد", + "folder_list": "پوشه‌های شما", + "folder_list_description": "روی آیکون پوشه کلیک کنید تا سفارشی شود", + "standard_roles": "نقش‌های استاندارد پوشه", + "standard_roles_description": "تخصیص پوشه‌ها به نقش‌های استاندارد مانند صندوق ورودی، ارسال شده، زباله‌دان و غیره", + "role_inbox": "صندوق ورودی", + "role_drafts": "پیش‌نویس‌ها", + "role_sent": "ارسال شده", + "role_trash": "زباله‌دان", + "role_junk": "هرزنامه", + "role_archive": "بایگانی", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "هیچکدام", + "create_folder": "ایجاد پوشه", + "create_subfolder": "ایجاد زیرپوشه", + "reorder": "برای تغییر ترتیب بکشید", + "reorder_error": "تغییر ترتیب پوشه‌ها ناموفق بود", + "move_error": "انتقال پوشه ناموفق بود", + "select_folder": "انتخاب {name}", + "select_hint": "انتخاب. Shift+کلیک همه پوشه‌ها را تا آخرین پوشه‌ای که کلیک کردید انتخاب می‌کند.", + "selected_count": "{count, plural, one {# پوشه} other {# پوشه}} انتخاب شد", + "move_to": "انتقال به...", + "move_to_top_level": "سطح بالا", + "move": "انتقال", + "clear_selection": "پاک کردن انتخاب", + "folders_moved": "{count, plural, one {# پوشه منتقل شد} other {# پوشه منتقل شد}}", + "move_partial_error": "{count, plural, one {# پوشه} other {# پوشه}} منتقل نشد", + "subfolder_of": "داخل {name}", + "subfolder_name": "نام زیرپوشه", + "new_folder_name": "نام پوشه", + "rename": "تغییر نام", + "change_icon": "تغییر آیکون", + "count_mode": "شمارنده برای {name}", + "count_mode_default": "شمارنده: پیش‌فرض", + "count_mode_unread": "فقط خوانده‌نشده", + "count_mode_all": "خوانده‌نشده و کل", + "count_mode_none": "بدون شمارنده", + "delete": "حذف", + "confirm_delete": "آیا از حذف \"{name}\" اطمینان دارید؟ ایمیل‌های این پوشه به زباله‌دان منتقل می‌شوند.", + "create": "ایجاد", + "cancel": "انصراف", + "no_folders": "پوشه سفارشی نیست", + "cannot_delete_role": "نمی‌توان پوشه با نقش استاندارد را حذف کرد. ابتدا نقش را حذف کنید.", + "folder_created": "پوشه ایجاد شد", + "folder_renamed": "نام پوشه تغییر کرد", + "folder_deleted": "پوشه حذف شد", + "role_updated": "نقش پوشه به‌روزرسانی شد", + "error_create": "ایجاد پوشه ناموفق بود", + "error_rename": "تغییر نام پوشه ناموفق بود", + "error_delete": "حذف پوشه ناموفق بود", + "error_delete_has_children": "نمی‌توان پوشه را حذف کرد: همچنان زیرپوشه دارد. ابتدا آنها را حذف یا منتقل کنید.", + "error_delete_has_email": "نمی‌توان پوشه را حذف کرد: همچنان حاوی ایمیل است. ابتدا آنها را منتقل یا حذف کنید.", + "error_role": "به‌روزرسانی نقش پوشه ناموفق بود" + }, + "advanced": { + "title": "پیشرفته", + "description": "گزینه‌های پیشرفته و تنظیمات توسعه‌دهنده", + "debug_mode": { + "label": "حالت اشکال‌زدایی", + "description": "فعال کردن ثبت جزئیات برای عیب‌یابی" + }, + "debug_categories": { + "description": "انتخاب کنید کدام دسته‌بندی‌ها ثبت شوند. دسته‌بندی‌های غیرضروری را برای کاهش نویز کنسول غیرفعال کنید.", + "jmap": "کلاینت JMAP", + "jmap_description": "عملیات صندوق پستی، دریافت ایمیل و درخواست‌های پروتکل JMAP", + "calendar": "تقویم", + "calendar_description": "رویدادهای تقویم، وارد کردن و پیام‌های زمان‌بندی", + "tasks": "وظایف", + "tasks_description": "ایجاد، دریافت و به‌روزرسانی وظایف تقویم", + "auth": "احراز هویت", + "auth_description": "ورود، TOTP، تبادل توکن و مدیریت نشست", + "filters": "فیلترها", + "filters_description": "قوانین فیلتر Sieve و اسکریپت‌های پاسخ خودکار", + "email": "مشاهده ایمیل", + "email_description": "رندر ایمیل، پردازش TNEF و علامت‌گذاری خوانده شده", + "push": "اعلان‌های Push", + "push_description": "تنظیم و تحویل اعلان‌های push", + "contacts": "مخاطبین و دفترچه آدرس", + "contacts_description": "همگام‌سازی مخاطبین، عملیات دفترچه آدرس و فرستندگان مورد اعتماد" + }, + "settings_sync": { + "label": "همگام‌سازی تنظیمات", + "description": "همگام‌سازی تنظیمات بین مرورگرها و دستگاه‌ها" + }, + "sender_favicons": { + "label": "Favicon فرستنده", + "description": "نمایش آیکون وبسایت به عنوان تصویر پروفایل فرستندگان تجاری", + "refresh_label": "بازخوانی آیکون فرستندگان", + "refresh_description": "آیکون وب‌سایت‌ها را دوباره دریافت کن، مثلاً وقتی فرستنده آیکون خود را تغییر داده است", + "refresh_button": "بازخوانی", + "refresh_done": "آیکون فرستندگان دوباره دریافت می‌شود" + }, + "show_avatars_in_junk": { + "label": "نمایش آواتار در پوشه هرزنامه", + "description": "نمایش تصاویر پروفایل و favicon برای فرستندگان در پوشه هرزنامه." + }, + "keyboard_shortcuts": { + "label": "میانبرهای صفحه کلید", + "description": "مشاهده میانبرهای صفحه کلید موجود", + "button": "مشاهده میانبرها" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "بازنشانی تنظیمات", + "description": "بازگردانی همه تنظیمات به مقادیر پیش‌فرض", + "button": "بازنشانی به پیش‌فرض" + }, + "export_settings": { + "label": "خروجی تنظیمات", + "description": "دانلود تنظیمات به صورت JSON", + "button": "خروجی" + }, + "import_settings": { + "label": "وارد کردن تنظیمات", + "description": "بارگذاری تنظیمات از فایل JSON", + "button": "وارد کردن" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "کد منبع" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "پیش از آنکه هرزنامه و فیشینگ به صندوق ورودی برسند، روی آن‌ها کلیک کنید. ایمیل‌های واقعی را عبور دهید. اگر هر سه سپر را از دست بدهید، صندوق ورودی سقوط می‌کند.", + "kinds": { + "spam": "هرزنامه", + "phishing": "فیشینگ", + "legit": "ایمیل واقعی" + }, + "click_to_block": "برای مسدود کردن کلیک کنید", + "let_through": "بگذارید عبور کند", + "start": "شروع", + "play_again": "بازی دوباره", + "score": "امتیاز", + "combo": "پاداش زنجیره", + "shields_left": "{count} از {total} سپر باقی مانده", + "seconds": "{seconds} ث", + "best": "بهترین: {score}", + "new_best": "رکورد جدید", + "held": "صندوق ورودی مقاومت کرد", + "overrun": "صندوق ورودی سقوط کرد", + "stats": { + "caught": "مسدودشده", + "delivered": "تحویل‌شده", + "mistakes": "اشتباه‌ها" + } + } + }, + "sidebar_apps": { + "title": "برنامه‌های نوار کناری", + "description": "مدیریت برنامه‌ها و لینک‌های سفارشی در نوار کناری", + "keep_loaded": "نگه داشتن برنامه‌ها در حالت بارگذاری", + "keep_loaded_description": "برنامه‌های inline هنگام تعویض بین آنها در پس‌زمینه فعال بمانند تا از بارگذاری مجدد جلوگیری شود", + "managed_title": "ارائه‌شده توسط مدیر شما", + "managed_description": "این برنامه‌ها برای همه تنظیم شده‌اند و قابل تغییر نیستند.", + "manage_title": "برنامه‌های سفارشی", + "manage_description": "افزودن، ویرایش یا حذف برنامه‌های سفارشی از نوار کناری" + }, + "contacts": { + "title": "مخاطبین", + "description": "وارد کردن و خروجی مخاطبین", + "group_by_letter_label": "گروه‌بندی بر اساس حرف اول", + "sort_by_last_name_label": "مرتب‌سازی بر اساس نام خانوادگی", + "sort_by_last_name_description": "لیست مخاطبین را بر اساس نام خانوادگی مرتب کنید تا اعضای خانواده کنار هم نمایش داده شوند", + "group_by_letter_description": "نمایش هدرهای الفبایی در لیست مخاطبین", + "import_label": "وارد کردن مخاطبین", + "import_description": "وارد کردن مخاطبین از فایل vCard (.vcf)", + "export_label": "خروجی مخاطبین", + "export_description": "خروجی همه مخاطبین به صورت فایل vCard (.vcf)", + "manage_title": "دفترچه‌های آدرس", + "manage_description": "تغییر نام دفترچه‌های آدرس", + "no_address_books": "دفترچه آدرسی یافت نشد", + "categories_title": "دسته‌بندی‌ها", + "categories_description": "تغییر نام دسته‌بندی‌های مخاطبین", + "no_categories": "دسته‌بندی یافت نشد" + }, + "downloads": { + "title": "دانلودها", + "description": "سفارشی‌سازی نام فایل‌های ایمیل و پیوست دانلودی.", + "reset": "بازگردانی پیش‌فرض", + "preview": "پیش‌نمایش:", + "email_template": { + "label": "نام فایل ایمیل (.eml)", + "description": "قالب استفاده شده هنگام خروجی ایمیل یا کشیدن آن به فایل سیستم." + }, + "attachment_template": { + "label": "نام فایل پیوست", + "description": "قالبی که هنگام دانلود یا بیرون کشیدن پیوست استفاده می‌شود. اگر '{filename}' و '{ext}' را حذف کنید، پسوند اصلی فایل حفظ می‌شود." + }, + "bundle_template": { + "label": "نام فایل .zip چند ایمیل", + "description": "قالب استفاده شده هنگام خروجی چند ایمیل انتخاب شده به صورت فایل zip." + }, + "spaces": { + "label": "فاصله‌ها", + "description": "جایگزینی فاصله‌ها در نام فایل با کاراکتر دیگر.", + "keep": "حفظ فاصله‌ها", + "underscore": "جایگزینی با _", + "dash": "جایگزینی با -" + }, + "lowercase": { + "label": "حروف کوچک", + "description": "تبدیل کل نام فایل به حروف کوچک." + }, + "strip_diacritics": { + "label": "حذف اعراب‌گذاری", + "description": "تبدیل حروف اعراب‌دار به معادل ASCII (ä → a، é → e)." + }, + "collapse_separators": { + "label": "ادغام جداکننده‌های تکراری", + "description": "ادغام فاصله‌ها، زیرخط‌ها یا خط تیره‌های تکراری به یک کاراکتر." + }, + "after_export": { + "label": "بعد از خروجی", + "description": "انتقال اختیاری ایمیل بعد از خروجی به صورت .eml.", + "keep": "نگه داشتن در صندوق", + "archive": "انتقال به بایگانی", + "trash": "انتقال به زباله‌دان" + } + }, + "filters": { + "title": "فیلترهای ایمیل", + "description": "ایجاد قوانین برای مرتب‌سازی خودکار، برچسب‌گذاری و مدیریت ایمیل‌های ورودی", + "add_rule": "افزودن قانون", + "no_rules": "قانون فیلتری نیست", + "no_rules_description": "قوانینی برای سازمان‌دهی خودکار ایمیل‌های ورودی ایجاد کنید", + "vacation_active": "پاسخ‌گوی خودکار فعال است", + "vacation_active_description": "پاسخ خودکار برای پیام‌های ورودی فعال است", + "vacation_configure": "پیکربندی", + "edit_rule": "ویرایش قانون", + "new_rule": "قانون جدید", + "delete_rule": "حذف قانون", + "delete_confirm": "آیا از حذف این قانون اطمینان دارید؟", + "enable": "فعال کردن", + "disable": "غیرفعال کردن", + "raw_editor": "ویرایشگر خام Sieve", + "raw_editor_warning": "ویرایش اسکریپت خام Sieve ممکن است ویرایش بصری قوانین را مختل کند. تغییرات اینجا ویرایشگر بصری را بازنویسی می‌کند.", + "validate": "اعتبارسنجی", + "validation_success": "اسکریپت معتبر است", + "validation_error": "اسکریپت خطا دارد", + "save": "ذخیره قوانین", + "saving": "در حال ذخیره...", + "saved": "فیلترها با موفقیت ذخیره شدند", + "save_failed": "ذخیره فیلترها ناموفق بود", + "loading": "در حال بارگذاری فیلترها...", + "not_supported": "سرور ایمیل شما از فیلترهای ایمیل پشتیبانی نمی‌کند.", + "rule_name": "نام قانون", + "rule_name_placeholder": "مثال: مرتب‌سازی خبرنامه‌ها", + "match_all": "مطابقت با همه شرایط", + "match_any": "مطابقت با هر شرط", + "conditions": "شرایط", + "add_condition": "افزودن شرط", + "actions": "عملیات‌ها", + "add_action": "افزودن عملیات", + "stop_processing": "توقف پردازش قوانین بعدی", + "include_spam": "پیام‌های علامت‌خورده به‌عنوان هرزنامه هم منتقل شوند", + "attachment_type_placeholder": "مثال: pdf, doc, jpg", + "value_placeholder_multi": "مقدار (چندتا با کاما جدا شوند)", + "condition_fields": { + "attachment": "پیوست", + "from": "از", + "to": "به", + "cc": "رونوشت", + "subject": "موضوع", + "header": "هدر سفارشی", + "size": "حجم", + "body": "بدنه", + "all": "همه پیام‌ها" + }, + "comparators": { + "has_any": "وجود دارد", + "has_type": "از نوع", + "contains": "شامل", + "not_contains": "شامل نیست", + "is": "دقیقاً برابر است با", + "not_is": "نیست", + "starts_with": "شروع می‌شود با", + "ends_with": "پایان می‌یابد با", + "matches": "مطابقت با الگو", + "greater_than": "بزرگتر از", + "less_than": "کوچکتر از", + "address_is": "نشانی‌اش این است", + "domain_is": "دامنه‌اش این است" + }, + "action_types": { + "move": "انتقال به پوشه", + "copy": "کپی به پوشه", + "forward": "ارسال به", + "mark_read": "علامت‌گذاری خوانده شده", + "star": "ستاره‌دار کردن", + "add_label": "افزودن برچسب", + "discard": "دور انداختن (حذف بی‌صدا)", + "reject": "رد با پیام", + "keep": "نگه داشتن در صندوق ورودی", + "stop": "توقف پردازش" + }, + "move_to_folder": "انتخاب پوشه", + "copy_to_folder": "انتخاب پوشه", + "forward_to": "ارسال به آدرس ایمیل", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "نگه‌داشتن یک نسخه", + "forward_limit": "سقف بازارسال برای هر پیام در این سرور: {count}. بازارسال‌های اضافه نادیده گرفته می‌شوند.", + "forward_unavailable": "این سرور اجازهٔ بازارسال نمی‌دهد. اقدام‌های بازارسال در قانون‌های فیلتر اثری ندارند.", + "reject_message": "پیام رد", + "reject_placeholder": "ایمیل شما رد شد", + "label_name": "نام برچسب", + "label_placeholder": "انتخاب برچسب", + "header_name": "نام هدر", + "header_placeholder": "مثال: X-Mailing-List", + "size_bytes": "حجم به بایت", + "size_placeholder": "مثال: 1000000", + "system_managed": "قانون مدیریت شده توسط سیستم", + "opaque_warning": "این اسکریپت خارج از ویرایشگر بصری ویرایش شده. فقط ویرایش خام Sieve در دسترس است.", + "open_sieve_editor": "باز کردن ویرایشگر خام Sieve", + "fetch_error": "بارگذاری فیلترها ناموفق بود", + "expanded_view": "نمای گسترده", + "expanded_view_description": "نمایش قوانین فیلتر با بلوک‌های شرط و عملیات جزئی", + "if": "اگر", + "then": "آنگاه", + "match_all_conditions": "همه مطابقت دارند", + "match_any_condition": "هر کدام مطابقت دارد", + "and": "و", + "or": "یا", + "cancel": "انصراف", + "confirm_delete": "حذف", + "rule_list": "قوانین فیلتر", + "drag_to_reorder": "کشیدن برای ترتیب‌دهی", + "match_type": "نوع مطابقت", + "reset_to_visual": "بازنشانی به ویرایشگر بصری", + "reset_warning": "این اسکریپت فعلی را دور می‌اندازد و از نو شروع می‌کند.", + "confirm_reset": "بازنشانی", + "validation_empty_name": "نام قانون الزامی است", + "validation_empty_conditions": "حداقل یک شرط با مقدار الزامی است", + "validation_empty_actions": "حداقل یک عملیات الزامی است", + "templates_section": "شروع از قالب", + "template_newsletters": "انتقال خبرنامه‌ها به پوشه", + "template_receipts": "بایگانی خودکار رسیدها", + "template_important": "علامت‌گذاری ایمیل‌های مهم", + "template_notifications": "فیلتر اعلان‌ها", + "sieve_editor": { + "title": "ویرایشگر اسکریپت Sieve", + "warning": "ویرایش اسکریپت خام Sieve ممکن است ویرایش بصری را مختل کند. تغییرات اینجا ویرایشگر بصری را بازنویسی می‌کند.", + "script_content": "اسکریپت Sieve", + "valid": "اسکریپت معتبر است", + "invalid": "اسکریپت خطا دارد", + "save_warning": "ذخیره قوانین بصری را بازنویسی می‌کند. این عملیات قابل بازگشت نیست. برای تأیید دوباره ذخیره را کلیک کنید.", + "validating": "در حال اعتبارسنجی...", + "validate": "اعتبارسنجی", + "cancel": "انصراف", + "save": "ذخیره", + "confirm_save": "تأیید ذخیره", + "validation_failed": "درخواست اعتبارسنجی ناموفق بود" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# شرط} other {# شرط}}", + "actions_count": "{count, plural, one {# عملیات} other {# عملیات}}" + }, + "origin_external": "خارجی", + "managed_by_tooltip": "مدیریت شده توسط {source}. در آن برنامه ویرایش کنید یا از ویرایشگر خام Sieve استفاده کنید.", + "quick_rule_names": { + "move_sender": "انتقال از {sender} به {folder}", + "move_domain": "انتقال از @{domain} به {folder}", + "move_list": "انتقال فهرست {list} به {folder}", + "mark_read": "علامت خوانده‌شده برای {sender}", + "tag": "برچسب {tag} برای {sender}", + "block": "مسدود کردن {sender}", + "custom": "پیام‌های {sender}", + "custom_plain": "قانون جدید", + "senders": "{count, plural, one {# فرستنده} other {# فرستنده}}" + }, + "suggestions": "پیشنهادها", + "suggest_subject": "موضوع «{subject}»", + "suggest_to": "به {address}", + "suggest_cc": "رونوشت {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "روی پیام‌هایی که از قبل در این پوشه هستند هم اعمال شود", + "apply_existing_unsupported": "فقط قانون‌هایی که فرستنده، گیرندگان، موضوع یا سرایندها را بررسی می‌کنند و پیام را منتقل، کپی، خوانده‌شده، ستاره‌دار یا برچسب‌دار می‌کنند، روی پیام‌های موجود اعمال می‌شوند.", + "period_title": "بازه زمانی", + "period_start": "تاریخ شروع", + "period_end": "تاریخ پایان", + "validation_period_order": "تاریخ پایان باید بعد از تاریخ شروع باشد", + "period_toggle": "فقط در یک بازه زمانی فعال باشد", + "period_hint": "برای پیام‌هایی اعمال می‌شود که در این بازه می‌رسند. برای باز گذاشتن هر طرف، فیلد آن را خالی بگذارید.", + "validation_period_empty": "تاریخ شروع یا پایان بازه را وارد کنید", + "validation_period_invalid": "برای بازه یک تاریخ معتبر همراه با ساعت وارد کنید یا فیلد را خالی بگذارید", + "apply_existing_period": "قانونی که بازه زمانی دارد فقط روی پیام‌هایی عمل می‌کند که در آن بازه می‌رسند، بنابراین روی پیام‌های موجود اجرا نمی‌شود.", + "period_status_scheduled": "زمان‌بندی‌شده", + "period_status_active": "فعال", + "period_status_expired": "منقضی‌شده" + }, + "templates": { + "title": "قالب‌های ایمیل", + "description": "ایجاد قالب‌های ایمیل قابل استفاده مجدد با متغیرهای جایگزین", + "add": "قالب جدید", + "edit": "ویرایش قالب", + "name": "نام قالب", + "name_placeholder": "مثال: ایمیل پیگیری", + "category": "دسته‌بندی", + "category_placeholder": "مثال: کاری، شخصی", + "subject": "موضوع", + "subject_placeholder": "خط موضوع ایمیل", + "body": "بدنه", + "body_placeholder": "محتوای بدنه ایمیل...", + "recipients_placeholder": "email@example.com", + "identity": "ارسال به عنوان", + "default_identity": "هویت پیش‌فرض", + "favorite": "مورد علاقه", + "cancel": "انصراف", + "create": "ایجاد", + "update": "به‌روزرسانی", + "confirm_delete": "حذف", + "no_templates": "هنوز قالبی نیست", + "manage": "مدیریت قالب‌ها", + "count": "{count, plural, one {# قالب} other {# قالب}}", + "export_import": "خروجی و وارد کردن", + "export_import_description": "پشتیبان‌گیری از قالب‌ها یا انتقال به دستگاه دیگر", + "export": "خروجی", + "import": "وارد کردن", + "validation": { + "empty": "نام قالب الزامی است", + "too_long": "نام قالب باید ۲۰۰ کاراکتر یا کمتر باشد" + } + }, + "files": { + "display": { + "title": "نمایش", + "description": "پیکربندی نحوه نمایش فایل‌ها و پوشه‌ها" + }, + "default_view": { + "label": "نمای پیش‌فرض", + "description": "انتخاب بین چیدمان grid یا لیست", + "list": "لیست", + "grid": "شبکه‌ای" + }, + "default_sort": { + "label": "مرتب‌سازی پیش‌فرض", + "description": "انتخاب مرتب‌سازی پیش‌فرض فایل‌ها", + "name": "نام", + "size": "حجم", + "modified": "تاریخ تغییر" + }, + "sort_direction": { + "label": "جهت مرتب‌سازی", + "description": "انتخاب ترتیب صعودی یا نزولی", + "ascending": "صعودی", + "descending": "نزولی" + }, + "icons": { + "title": "آیکون‌ها", + "description": "پیکربندی ظاهر آیکون فایل" + }, + "show_icons": { + "label": "نمایش آیکون فایل", + "description": "نمایش آیکون کنار فایل‌ها و پوشه‌ها" + }, + "colored_icons": { + "label": "آیکون‌های رنگی", + "description": "استفاده از آیکون‌های رنگی به جای تک‌رنگ" + }, + "show_thumbnails": { + "label": "نمایش thumbnail", + "description": "نمایش پیش‌نمایش تصویر به جای آیکون برای فایل‌های تصویری" + }, + "behavior": { + "title": "رفتار", + "description": "پیکربندی رفتار مرورگر فایل" + }, + "show_hidden": { + "label": "نمایش فایل‌های مخفی", + "description": "نمایش فایل‌ها و پوشه‌هایی که با نقطه شروع می‌شوند" + }, + "preview": { + "label": "پیش‌نمایش" + } + } + }, + "errors": { + "page_error_title": "مشکلی پیش آمد", + "page_error_description": "با خطای غیرمنتظره‌ای مواجه شدیم. لطفاً دوباره تلاش کنید.", + "sidebar_error": "بارگذاری صندوق‌های پستی ممکن نیست", + "email_list_error": "بارگذاری ایمیل‌ها ممکن نیست", + "viewer_error_title": "نمایش ایمیل ممکن نیست", + "viewer_error_description": "مشکلی در رندر این ایمیل وجود داشت.", + "composer_error": "بارگذاری ویرایشگر ممکن نیست", + "settings_error_title": "تنظیمات در دسترس نیست", + "settings_error_description": "بارگذاری تنظیمات ممکن نیست.", + "try_again": "دوباره تلاش کنید", + "reload": "بارگذاری مجدد", + "reload_emails": "بارگذاری مجدد ایمیل‌ها", + "reload_settings": "بارگذاری مجدد تنظیمات", + "retry": "تلاش مجدد", + "go_home": "رفتن به صندوق ورودی" + }, + "context_menu": { + "reply": "پاسخ", + "reply_all": "پاسخ به همه", + "forward": "ارسال مجدد", + "mark_read": "علامت‌گذاری خوانده شده", + "mark_unread": "علامت‌گذاری خوانده نشده", + "star": "ستاره‌دار", + "unstar": "حذف ستاره", + "pin": "سنجاق کردن", + "unpin": "برداشتن سنجاق", + "move_to": "انتقال به...", + "copy_to_account": "کپی به حساب...", + "remove_from_folder": "حذف از این پوشه", + "archive": "بایگانی", + "delete": "حذف", + "mark_as_spam": "گزارش هرزنامه", + "not_spam": "هرزنامه نیست", + "tag": "برچسب", + "items_selected": "{count} ایمیل انتخاب شده", + "forward_as_attachments": "ارسال به‌عنوان پیوست‌ها", + "edit_draft": "ویرایش پیش‌نویس", + "cancel_scheduled_send": "لغو ارسال", + "reschedule_send": "باززمان‌بندی", + "cancel_and_edit": "لغو و ویرایش", + "cancel_and_compose_again": "لغو و نوشتن مجدد", + "rules": { + "title": "قانون‌ها", + "move_from": "همیشه پیام‌های {sender} را منتقل کن", + "move_from_domain": "همیشه پیام‌های @{domain} را منتقل کن", + "move_from_list": "همیشه پیام‌های این فهرست را منتقل کن", + "mark_read_from": "همیشه پیام‌های {sender} را خوانده‌شده علامت بزن", + "tag_with": "همیشه این برچسب را بزن", + "block": "{count, plural, one {مسدود کردن فرستنده} other {مسدود کردن # فرستنده}}", + "no_junk": "این حساب پوشهٔ هرزنامه ندارد", + "create": "ایجاد قانون…", + "manage": "مدیریت قانون‌ها", + "senders_count": "{count, plural, one {# فرستنده} other {# فرستنده}}", + "cross_account": "پیام‌هایی از یک حساب انتخاب کنید", + "opaque_hint": "فیلترهای شما دستی ویرایش شده‌اند. تنظیمات فیلترها را باز کنید" , + "loading": "در حال بارگذاری قوانین…", + "load_failed": "بارگذاری قوانین ممکن نشد", + "none": "هنوز قانونی وجود ندارد", + "new_mail_only": "فقط برای پیام‌های جدید", + "running": "در حال اجرای «{name}» روی {folder}…" + }, + "find_emails_from": "یافتن ایمیل‌های {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "علامت‌گذاری پوشه خوانده شده", + "mark_folder_tree_read": "علامت‌گذاری پوشه و زیرپوشه‌ها", + "mark_all_folders_read": "علامت‌گذاری همه پوشه‌ها", + "new_subfolder": "زیرپوشه جدید...", + "new_folder": "پوشه جدید...", + "rename": "تغییر نام...", + "share": "اشتراک‌گذاری...", + "import_email": "وارد کردن .eml/.zip...", + "empty_folder": "خالی کردن پوشه", + "empty_folder_generic": "خالی کردن پوشه", + "delete_folder": "حذف پوشه", + "refresh": "بازنشانی", + "hide_shared_account": "پنهان کردن از نوار کناری", + "mute_notifications": "بی‌صدا کردن اعلان‌ها", + "unmute_notifications": "فعال کردن اعلان‌ها", + "mute_account_notifications": "بی‌صدا کردن اعلان‌های این حساب", + "unmute_account_notifications": "فعال کردن اعلان‌های این حساب", + "reset_folder_notifications": "بازنشانی اعلان‌های پوشه‌ها", + "mark_all_confirm_title": "علامت‌گذاری همه", + "mark_all_confirm_message": "همه پیام‌های خوانده نشده علامت‌گذاری شوند؟", + "delete_confirm_title": "حذف پوشه", + "delete_confirm_message": "پوشه \"{name}\" حذف شود؟", + "prompt_new_subfolder": "نام زیرپوشه جدید", + "prompt_new_folder": "نام پوشه جدید", + "prompt_rename": "نام جدید پوشه", + "placeholder_folder_name": "نام پوشه", + "create": "ایجاد", + "rename_confirm": "تغییر نام", + "toast_marked_read": "علامت‌گذاری شد", + "toast_marked_read_count": "{count, plural, one {۱ پیام} other {# پیام}} علامت‌گذاری شد", + "toast_already_read": "پیام خوانده نشده نیست", + "toast_marked_all_read": "همه علامت‌گذاری شدند", + "toast_emptied": "پوشه خالی شد", + "toast_folder_created": "پوشه ایجاد شد", + "toast_folder_renamed": "نام تغییر کرد", + "toast_folder_deleted": "پوشه حذف شد", + "toast_error_mark_read": "خطای علامت‌گذاری", + "toast_error_empty": "خطای خالی کردن", + "toast_error_create": "خطای ایجاد", + "toast_error_rename": "خطای تغییر نام", + "toast_error_delete": "خطای حذف", + "toast_error_delete_has_children": "زیرپوشه دارد", + "toast_error_delete_has_email": "خالی نیست", + "delete_with_contents_title": "پوشه خالی نیست", + "delete_with_contents_message": "پوشه «{name}» هنوز شامل {count, plural, one {۱ پیام} other {# پیام}} است. پوشه و همه پیام‌های آن حذف شود؟ این عمل قابل بازگشت نیست.", + "delete_with_contents_confirm": "حذف پوشه و پیام‌ها" + }, + "shortcuts": { + "title": "میانبرهای صفحه کلید", + "tip": "هر زمان کلید ? را فشار دهید", + "sections": { + "navigation": "ناوبری", + "actions": "عملیات ایمیل", + "global": "سراسری", + "threads": "رشته‌ها", + "composer": "ویرایشگر" + }, + "navigation": { + "next_email": "ایمیل بعدی", + "previous_email": "ایمیل قبلی", + "open_email": "باز کردن ایمیل", + "close_email": "بستن / لغو انتخاب", + "go_to_folder": "رفتن به پوشه", + "next_unread": "ایمیل خوانده‌نشده بعدی", + "no_more_unread": "ایمیل خوانده‌نشده‌ای پایین‌تر در فهرست نیست" + }, + "actions": { + "reply": "پاسخ", + "reply_all": "پاسخ به همه", + "forward": "ارسال مجدد", + "star": "تغییر ستاره", + "archive": "بایگانی", + "delete": "حذف", + "mark_unread": "علامت‌گذاری خوانده نشده", + "mark_read": "علامت‌گذاری خوانده شده", + "toggle_spam": "هرزنامه / غیر هرزنامه" + }, + "global": { + "compose": "ایمیل جدید", + "search": "تمرکز جستجو", + "help": "نمایش میانبرها", + "refresh": "بازنشانی ایمیل‌ها", + "select_all": "انتخاب همه", + "global_search": "جستجو در همه جا", + "extend_selection": "گسترش انتخاب به پایین / بالا" + }, + "threads": { + "expand_collapse": "باز/بسته کردن رشته", + "select_mountain_view": "انتخاب گفت‌وگو" + }, + "composer": { + "send": "ارسال ایمیل", + "schedule_send": "زمان‌بندی ارسال", + "template_picker": "انتخاب‌گر قالب" + } + }, + "threads": { + "messages_one": "{count} پیام", + "messages_other": "{count} پیام", + "messages_tooltip": "{count, plural, one {# پیام در این مکالمه} other {# پیام در این مکالمه}}", + "expand": "باز کردن مکالمه", + "collapse": "جمع کردن مکالمه", + "loading": "در حال بارگذاری مکالمه...", + "mark_read": "علامت‌گذاری مکالمه به عنوان خوانده شده", + "mark_unread": "علامت‌گذاری مکالمه به عنوان خوانده نشده", + "archive": "بایگانی مکالمه", + "delete": "حذف مکالمه", + "star": "ستاره‌دار کردن مکالمه", + "unstar": "حذف ستاره مکالمه", + "toggle_thread": "تغییر وضعیت رشته" + }, + "identities": { + "modal_title": "مدیریت هویت‌های ارسال", + "create_new": "ایجاد هویت جدید", + "edit_identity": "ویرایش هویت", + "delete_confirm": "حذف این هویت؟ قابل بازگشت نیست.", + "cannot_delete": "این هویت قابل حذف نیست", + "primary_identity": "اصلی", + "set_as_primary": "تنظیم به عنوان اصلی", + "no_identities": "هویتی یافت نشد", + "display": { + "reply_to": "پاسخ به:", + "bcc": "رونوشت مخفی:", + "signature": "امضا:", + "preview": "پیش‌نمایش:" + }, + "validation_errors": { + "invalid_emails": "ایمیل‌های نامعتبر: {emails}", + "unknown_error": "خطای ناشناس" + }, + "form": { + "name_label": "نام نمایشی", + "name_placeholder": "مثال: ایمیل کاری، شخصی", + "name_required": "نام الزامی است", + "email_label": "آدرس ایمیل", + "email_placeholder": "your.email@example.com", + "email_required": "ایمیل الزامی است", + "email_invalid": "ایمیل معتبر وارد کنید", + "email_immutable": "پس از ایجاد قابل تغییر نیست", + "reply_to_label": "پاسخ به (اختیاری)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "رونوشت مخفی خودکار (اختیاری)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "امضای متنی", + "html_signature_label": "امضای HTML", + "signature_byte_counter": "{bytes} / {max} بایت", + "signature_byte_limit_reached": "حد سرور", + "save": "ذخیره هویت", + "cancel": "انصراف", + "creating": "در حال ایجاد...", + "updating": "در حال به‌روزرسانی..." + }, + "sub_address": { + "button_tooltip": "استفاده از زیرآدرس", + "popover_title": "افزودن برچسب زیرآدرس", + "tag_input_placeholder": "برچسب را وارد کنید", + "preview_label": "پیش‌نمایش:", + "recent_tags": "برچسب‌های اخیر", + "suggested_tags": "پیشنهادی", + "use_address": "استفاده از این آدرس", + "invalid_tag": "فقط حروف و خط تیره", + "tag_too_long": "حداکثر ۳۰ کاراکتر", + "help_text": "ایمیل به user{delimiter}tag@domain.com به صندوق ورودی می‌رسد", + "validation": { + "empty": "برچسب نمی‌تواند خالی باشد", + "too_long": "حداکثر {max} کاراکتر", + "invalid_chars": "فقط حروف، اعداد و خط تیره" + } + }, + "badge": { + "sent_via": "از طریق", + "sub_address_tag": "ارسال با زیرآدرس: {tag}", + "identity_name": "ارسال با هویت: {name}", + "identity_short": "از طریق {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "حذف", + "delete_confirm_title": "حذف هویت" + }, + "templates": { + "picker_title": "انتخاب قالب", + "search_placeholder": "جستجوی قالب‌ها...", + "section_favorites": "مورد علاقه‌ها", + "section_recent": "اخیر", + "section_uncategorized": "سایر", + "no_templates": "هنوز قالبی نیست", + "no_results": "قالبی یافت نشد", + "fill_placeholders": "پر کردن مقادیر جایگزین", + "enter_value": "یک مقدار وارد کنید...", + "preview": "پیش‌نمایش", + "insert_with_values": "درج با مقادیر", + "insert_raw": "درج خام", + "copy_suffix": "(کپی)", + "placeholder": "متغیر", + "placeholders": { + "recipient_name": "نام گیرنده", + "company": "نام شرکت", + "date": "تاریخ فعلی", + "day_of_week": "روز هفته", + "sender_name": "نام شما" + } + }, + "contacts": { + "directory": { + "title": "فهرست سازمانی", + "search_placeholder": "جستجوی نام یا نشانی ایمیل", + "refresh": "تازه‌سازی فهرست", + "retry": "تلاش دوباره", + "loading": "در حال بارگذاری فهرست…", + "unavailable_title": "فهرستی وجود ندارد", + "unavailable_hint": "سرور ایمیل شما فهرستی از حساب‌ها ارائه نمی‌دهد.", + "forbidden_title": "فهرست در دسترس نیست", + "forbidden_hint": "سرور ایمیل اجازه مرور فهرست را به این حساب نمی‌دهد. از مدیر سیستم بپرسید.", + "error_title": "بارگذاری فهرست ممکن نشد", + "error_hint": "اتصال خود را بررسی کنید و دوباره تلاش کنید.", + "empty_title": "فهرست خالی است", + "empty_hint": "سرور ایمیل هیچ حسابی با نشانی ایمیل نشان نمی‌دهد.", + "no_match": "هیچ‌کس در فهرست با جستجوی شما مطابقت ندارد", + "refresh_failed": "تازه‌سازی فهرست ممکن نشد. آخرین فهرست بارگذاری‌شده نمایش داده می‌شود.", + "select_page": "انتخاب همه در این صفحه", + "deselect_page": "لغو انتخاب همه در این صفحه", + "select_entry": "انتخاب {name}", + "range": "{from}–{to} از {total}", + "page": "صفحه {page} از {count}", + "previous_page": "صفحه قبل", + "next_page": "صفحه بعد", + "picker_title": "انتخاب از فهرست", + "cancel": "لغو", + "selected_count": "{count} انتخاب شد", + "add_to": "افزودن به «به»", + "add_cc": "افزودن به «رونوشت»", + "add_bcc": "افزودن به «رونوشت پنهان»", + "select_hint": "برای دیدن جزئیات، یک نفر را انتخاب کنید", + "write_email": "نوشتن ایمیل", + "add_to_contacts": "افزودن به مخاطبان", + "email": "ایمیل", + "read_only_note": "ورودی‌های فهرست از سرور ایمیل شما می‌آیند و اینجا قابل ویرایش نیستند.", + "type": { + "individual": "شخص", + "group": "گروه", + "resource": "منبع", + "location": "مکان", + "other": "سایر" + } + }, + "title": "مخاطبین", + "search_placeholder": "جستجوی مخاطبین...", + "create_new": "مخاطب جدید", + "no_category": "بدون دسته‌بندی", + "rename_category": "تغییر نام دسته‌بندی", + "category_name_label": "نام دسته‌بندی", + "category_renamed": "نام دسته‌بندی تغییر کرد", + "category_rename_failed": "تغییر نام دسته‌بندی ناموفق بود", + "category_added": "مخاطب به {name} اضافه شد", + "category_added_plural": "{count} مخاطب به {name} اضافه شد", + "empty_state": "هنوز مخاطبی نیست", + "empty_state_title": "هنوز مخاطبی نیست", + "empty_state_subtitle": "اولین مخاطب خود را ایجاد کنید یا از فایل vCard وارد کنید", + "empty_search": "مخاطبی با این جستجو مطابقت ندارد", + "empty_search_hint": "عبارت جستجوی دیگری را امتحان کنید", + "empty_filtered": "مخاطبی با این فیلترها مطابقت ندارد", + "empty_filtered_hint": "فیلترها را تنظیم یا پاک کنید", + "clear_search": "پاک کردن جستجو", + "import_vcard": "وارد کردن vCard", + "delete_confirm_title": "حذف مخاطب", + "delete_confirm": "آیا از حذف این مخاطب اطمینان دارید؟", + "local_mode": "مخاطبین به صورت محلی ذخیره می‌شوند (سرور از JMAP Contacts پشتیبانی نمی‌کند)", + "back_to_contacts": "بازگشت به مخاطبین", + "back_to_email": "بازگشت به ایمیل", + "open_categories": "باز کردن دسته‌بندی‌ها", + "tabs": { + "all": "همه", + "groups": "گروه‌ها" + }, + "shared": { + "title": "اشتراکی" + }, + "address_books": { + "create": "دفترچه آدرس جدید", + "created": "دفترچه آدرس ایجاد شد", + "create_failed": "ایجاد دفترچه آدرس ناموفق بود", + "title": "دفترچه‌های آدرس من", + "shared_prefix": "اشتراکی: {name}", + "moved": "مخاطب به {name} منتقل شد", + "moved_plural": "{count} مخاطب به {name} منتقل شد", + "move_failed": "انتقال مخاطب ناموفق بود", + "address_book": "دفترچه آدرس", + "rename": "تغییر نام دفترچه آدرس", + "name_label": "نام دفترچه آدرس", + "renamed": "نام دفترچه آدرس تغییر کرد", + "rename_failed": "تغییر نام دفترچه آدرس ناموفق بود", + "default": "پیش‌فرض", + "set_default": "تنظیم به عنوان پیش‌فرض", + "default_updated": "دفترچه آدرس پیش‌فرض به‌روزرسانی شد", + "set_default_failed": "تنظیم دفترچه آدرس پیش‌فرض ناموفق بود", + "manage": "مدیریت دفترچه‌های آدرس", + "share": "اشتراک‌گذاری دفترچه آدرس", + "new_contact_in_book": "مخاطب جدید در این دفترچه آدرس", + "delete": "حذف دفترچه آدرس", + "confirm_delete": "حذف \"{name}\"؟ همه مخاطبین این دفترچه آدرس حذف خواهند شد.", + "deleted": "دفترچه آدرس حذف شد", + "delete_failed": "حذف دفترچه آدرس ناموفق بود" + }, + "detail": { + "emails": "آدرس‌های ایمیل", + "phones": "شماره تلفن‌ها", + "organizations": "سازمان‌ها", + "addresses": "آدرس‌ها", + "notes": "یادداشت‌ها", + "titles": "عناوین و نقش‌ها", + "online_services": "سرویس‌های آنلاین", + "anniversaries": "سالگردها", + "personal_info": "اطلاعات شخصی", + "languages": "زبان‌ها", + "categories": "دسته‌بندی‌ها", + "related_contacts": "مخاطبین مرتبط", + "crypto_keys": "کلیدهای رمزنگاری", + "cert_issuer": "صادرکننده", + "cert_expires": "انقضا", + "cert_expired": "منقضی شده", + "cert_fingerprint": "اثر انگشت", + "cert_algorithm": "الگوریتم", + "import_to_smime": "وارد کردن به S/MIME", + "cert_already_imported": "قبلاً به S/MIME وارد شده", + "cert_imported": "گواهی به مخزن S/MIME وارد شد", + "cert_import_failed": "وارد کردن گواهی ناموفق بود", + "no_contact_selected": "یک مخاطب برای مشاهده جزئیات انتخاب کنید", + "compose_email": "نوشتن ایمیل", + "copy_email": "کپی ایمیل", + "copy_phone": "کپی شماره تلفن", + "copy_url": "کپی آدرس", + "copied": "در کلیپ‌بورد کپی شد", + "copy_failed": "کپی در کلیپ‌بورد ناموفق بود", + "created": "ایجاد شده", + "updated": "آخرین به‌روزرسانی", + "timezone": "منطقه زمانی", + "anniversary_birth": "تولد", + "anniversary_death": "درگذشت", + "anniversary_wedding": "سالگرد", + "anniversary_other": "سایر", + "personal_expertise": "تخصص", + "personal_hobby": "سرگرمی", + "personal_interest": "علاقه", + "personal_other": "سایر", + "gender": "جنسیت", + "gender_masculine": "مرد", + "gender_feminine": "زن", + "gender_other": "سایر", + "gender_none": "نامرتبط", + "gender_unknown": "ناشناس", + "calendar": "تقویم", + "calendar_uri": "آدرس تقویم", + "scheduling_uri": "آدرس زمان‌بندی", + "freebusy_uri": "آدرس Free/Busy", + "section_contact": "جزئیات مخاطب", + "section_work": "کاری", + "section_personal": "شخصی", + "email_default_label": "ایمیل", + "phone_default_label": "تلفن", + "address_default_label": "آدرس", + "online_service_default_label": "آنلاین", + "organization_label": "سازمان", + "title_label": "عنوان", + "role_label": "نقش", + "language_label": "زبان", + "related_default_label": "مرتبط", + "more_actions": "عملیات بیشتر", + "age_years": "{count, plural, one {۱ ساله} other {# ساله}}", + "years_since": "{count, plural, one {۱ سال} other {# سال}}" + }, + "activity": { + "recent_emails": "ایمیل‌های اخیر", + "upcoming_events": "رویدادهای پیش رو", + "no_emails": "ایمیل اخیری نیست", + "no_events": "رویداد پیش رویی نیست", + "no_subject": "(بدون موضوع)", + "no_title": "(بدون عنوان)", + "load_failed": "بارگذاری ناموفق بود", + "unknown_sender": "فرستنده ناشناس", + "all_day": "تمام روز" + }, + "form": { + "create_title": "مخاطب جدید", + "edit_title": "ویرایش مخاطب", + "section_address_book": "دفترچه", + "select_address_book": "انتخاب دفترچه...", + "section_identity": "نام و هویت", + "contact_type": "نوع مخاطب", + "type_person": "شخص", + "type_organization": "سازمان", + "section_work": "کار و سازمان", + "prefix": "پیشوند", + "prefix_placeholder": "دکتر، مهندس", + "given_name": "نام", + "middle_name": "نام میانی", + "surname": "نام خانوادگی", + "suffix": "پسوند", + "suffix_placeholder": "جونیور، سینیور", + "nickname": "نام مستعار", + "nickname_placeholder": "نام مستعار", + "email": "ایمیل", + "email_placeholder": "email@example.com", + "phone": "تلفن", + "phone_placeholder": "+98 912 345 6789", + "phone_type": "نوع", + "phone_voice": "تلفن", + "phone_cell": "موبایل", + "phone_fax": "فکس", + "phone_pager": "پیجر", + "phone_video": "ویدیو", + "phone_text": "پیامک", + "organization": "سازمان", + "organization_placeholder": "نام شرکت", + "department": "دپارتمان", + "department_placeholder": "دپارتمان", + "job_title": "عنوان شغلی", + "job_title_placeholder": "مثال: مهندس نرم‌افزار", + "role": "نقش", + "role_placeholder": "مثال: سرپرست تیم", + "addresses": "آدرس‌ها", + "add_address": "افزودن آدرس", + "street": "خیابان", + "city": "شهر", + "region": "استان / منطقه", + "postcode": "کد پستی", + "country": "کشور", + "online_services": "سرویس‌های آنلاین", + "add_online_service": "افزودن سرویس آنلاین", + "url_placeholder": "https://...", + "service_placeholder": "سرویس", + "anniversaries": "سالگردها", + "add_anniversary": "افزودن تاریخ", + "anniversary_birth": "تولد", + "anniversary_wedding": "سالگرد", + "anniversary_death": "یادبود", + "anniversary_other": "سایر", + "personal_info": "اطلاعات شخصی", + "add_personal_info": "افزودن مورد", + "personal_info_placeholder": "مثال: عکاسی", + "personal_expertise": "تخصص", + "personal_hobby": "سرگرمی", + "personal_interest": "علاقه", + "personal_other": "سایر", + "level": "سطح", + "level_high": "بالا", + "level_medium": "متوسط", + "level_low": "پایین", + "categories": "دسته‌بندی‌ها", + "categories_placeholder": "مثال: خانواده، دوستان، همکاران", + "categories_hint": "برای جستجو یا افزودن دسته‌بندی تایپ کنید", + "category_add": "افزودن", + "note": "یادداشت‌ها", + "note_placeholder": "افزودن یادداشت...", + "gender": "جنسیت", + "gender_sex": "جنسیت", + "gender_male": "مرد", + "gender_female": "زن", + "gender_other": "سایر", + "gender_none": "نامرتبط", + "gender_unknown": "ناشناس", + "gender_identity": "هویت جنسیتی", + "gender_identity_placeholder": "هویت جنسیتی...", + "calendar": "تقویم", + "calendar_uri": "آدرس تقویم", + "scheduling_uri": "آدرس زمان‌بندی", + "freebusy_uri": "آدرس Free/Busy", + "context_work": "کاری", + "context_private": "خصوصی", + "add_email": "افزودن ایمیل", + "add_phone": "افزودن تلفن", + "save": "ذخیره", + "cancel": "انصراف", + "creating": "در حال ایجاد...", + "updating": "در حال به‌روزرسانی...", + "name_required": "نام، نام خانوادگی یا سازمان را وارد کنید", + "email_invalid": "لطفاً یک آدرس ایمیل معتبر وارد کنید", + "email_error_inline": "فرمت ایمیل نامعتبر است", + "save_failed": "ذخیره مخاطب ناموفق بود", + "delete": "حذف", + "upload_photo": "بارگذاری عکس", + "remove_photo": "حذف عکس", + "photo_hint": "JPG یا PNG، حداکثر ۱۰ مگابایت. تغییر اندازه داده می‌شود.", + "photo_too_large": "تصویر بیش از حد بزرگ است (حداکثر ۱۰ مگابایت)", + "photo_invalid": "فایل تصویر نامعتبر است", + "change_photo": "تغییر" + }, + "groups": { + "create": "گروه جدید", + "edit": "ویرایش گروه", + "empty": "هنوز گروهی نیست", + "delete_confirm_title": "حذف گروه", + "delete_confirm": "آیا از حذف این گروه اطمینان دارید؟", + "name_label": "نام گروه", + "name_placeholder": "مثال: تیم، خانواده", + "name_required": "نام گروه الزامی است", + "save_failed": "ذخیره گروه ناموفق بود", + "members_label": "اعضا", + "search_members": "جستجوی مخاطبین برای افزودن...", + "no_members": "عضوی در این گروه نیست", + "member_count": "{count, plural, =0 {بدون عضو} one {۱ عضو} other {# عضو}}", + "send_email": "ارسال ایمیل به گروه", + "send_email_to": "به", + "send_email_cc": "رونوشت", + "send_email_bcc": "رونوشت مخفی", + "no_member_emails": "این گروه عضوی با آدرس ایمیل ندارد." + }, + "import": { + "title": "وارد کردن مخاطبین", + "drop_hint": "برای انتخاب فایل vCard کلیک کنید", + "file_types": "فایل‌های vcf. یا vcard.", + "no_contacts": "مخاطبی در فایل یافت نشد", + "parse_error": "پردازش فایل vCard ناموفق بود", + "found": "{count, plural, one {۱ مخاطب یافت شد} other {# مخاطب یافت شد}}", + "duplicate": "تکراری", + "select_all": "انتخاب همه", + "deselect_all": "لغو انتخاب همه", + "selected": "{count, plural, one {۱ انتخاب شده} other {# انتخاب شده}}", + "import_button": "وارد کردن", + "importing": "در حال وارد کردن...", + "success": "{count, plural, one {۱ مخاطب وارد شد} other {# مخاطب وارد شد}}", + "failed": "وارد کردن ناموفق بود", + "close": "بستن", + "file_too_large": "حجم فایل بیش از حد است (حداکثر ۵ مگابایت)" + }, + "export": { + "title": "خروجی مخاطبین", + "success": "{count, plural, one {۱ مخاطب خروجی گرفته شد} other {# مخاطب خروجی گرفته شد}}" + }, + "bulk": { + "selected": "{count, plural, one {۱ انتخاب شده} other {# انتخاب شده}}", + "select_all": "انتخاب همه", + "delete": "حذف", + "delete_confirm_title": "حذف مخاطبین", + "delete_confirm": "حذف {count, plural, one {۱ مخاطب} other {# مخاطب}}؟", + "deleted": "{count, plural, one {۱ مخاطب حذف شد} other {# مخاطب حذف شد}}", + "add_to_group": "افزودن به گروه", + "choose_group": "انتخاب گروه", + "adding_contacts": "افزودن {count, plural, one {۱ مخاطب} other {# مخاطب}}", + "added_to_group": "مخاطبین به گروه اضافه شدند", + "export": "خروجی", + "clear": "پاک کردن انتخاب" + }, + "toast": { + "created": "مخاطب ایجاد شد", + "updated": "مخاطب به‌روزرسانی شد", + "deleted": "مخاطب حذف شد", + "error_create": "ایجاد مخاطب ناموفق بود", + "error_update": "به‌روزرسانی مخاطب ناموفق بود", + "error_delete": "حذف مخاطب ناموفق بود" + }, + "context_menu": { + "open": "باز کردن", + "edit": "ویرایش", + "send_email": "ارسال ایمیل", + "add_to_group": "افزودن به گروه", + "export_vcard": "خروجی vCard", + "delete": "حذف", + "call": "تماس", + "duplicate": "کپی", + "print": "چاپ" + }, + "filters": { + "toggle": "فیلترها", + "select": "انتخاب", + "clear": "پاک کردن", + "close": "بستن", + "title": "فیلترهای پیشرفته", + "organization": "شرکت", + "organization_placeholder": "مثال: شرکت نمونه", + "job_title": "عنوان شغلی", + "job_title_placeholder": "مثال: طراح", + "location": "مکان", + "location_placeholder": "شهر یا کشور", + "email_domain": "دامنه ایمیل", + "email_domain_placeholder": "example.com", + "birthday_month": "تولد در", + "any_month": "هر ماه", + "has_email": "دارای ایمیل", + "has_phone": "دارای تلفن", + "has_photo": "دارای عکس", + "sort_by": "مرتب‌سازی بر اساس", + "sort_first_name": "نام", + "sort_last_name": "نام خانوادگی" + } + }, + "calendar": { + "title": "تقویم", + "back_to_email": "بازگشت به ایمیل", + "back_to_month": "بازگشت به ماه", + "my_calendars": "تقویم‌ها", + "birthday_calendar": "تولدها", + "mini_calendar_change": "برای تغییر ماه کلیک کنید", + "views": { + "month": "ماه", + "week": "هفته", + "day": "روز", + "agenda": "برنامه", + "today": "امروز", + "month_hint": "ماه (m)", + "week_hint": "هفته (w)", + "day_hint": "روز (d)", + "agenda_hint": "برنامه (a)", + "tasks": "وظایف", + "tasks_hint": "وظایف (k)" + }, + "events": { + "hidden_before": "{count, plural, other {# رویداد قبلی}}", + "hidden_after": "{count, plural, other {# رویداد بعدی}}", + "show_all_hours": "نمایش همه ساعت‌ها", + "show_display_hours": "فقط نمایش {start} – {end}", + "create": "ایجاد رویداد", + "edit": "ویرایش رویداد", + "delete": "حذف رویداد", + "details": "جزئیات رویداد", + "no_events": "رویدادی نیست", + "all_day": "تمام روز", + "more": "+{count} بیشتر", + "show_more": "نمایش بیشتر", + "show_less": "نمایش کمتر", + "no_title": "(بدون عنوان)", + "resize": "تغییر اندازه رویداد", + "duplicate": "کپی", + "today_header": "امروز", + "tomorrow_header": "فردا", + "agenda_show_earlier": "نمایش رویدادهای قبلی", + "agenda_loading": "در حال بارگذاری…", + "agenda_range_start": "رویدادها از {date}", + "agenda_range_end": "رویدادها تا {date}", + "export_ics": "خروجی .ics", + "copy_title": "کپی عنوان", + "copy_link": "کپی لینک جلسه", + "new_event": "رویداد جدید", + "new_all_day_event": "رویداد تمام روز جدید", + "new_task": "وظیفه جدید", + "go_to_today": "رفتن به امروز" + }, + "detail": { + "add_note": "افزودن یادداشت...", + "save_note": "ذخیره", + "note_saved": "یادداشت اضافه شد", + "open_link": "باز کردن لینک", + "copy_location": "کپی مکان", + "meeting_link": "لینک جلسه", + "tentative": "احتمالی", + "cancelled": "لغو شده", + "delete_confirm": "این رویداد حذف شود؟" + }, + "form": { + "title": "عنوان", + "description": "توضیحات", + "location": "مکان", + "meeting_link": "لینک جلسه", + "start_date": "تاریخ شروع", + "end_date": "تاریخ پایان", + "start_time": "زمان شروع", + "end_time": "زمان پایان", + "all_day_event": "رویداد تمام روز", + "calendar_select": "تقویم", + "save": "ذخیره", + "cancel": "انصراف", + "delete_confirm": "آیا از حذف این رویداد اطمینان دارید؟", + "color": "رنگ" + }, + "participants": { + "availability": { + "title": "در دسترس بودن", + "free": "در دسترس", + "busy": "مشغول", + "tentative": "احتمالی", + "unknown": "کاربر این سرور نیست", + "checking": "در حال بررسی…" + }, + "title": "شرکت‌کنندگان", + "add": "افزودن شرکت‌کننده", + "organizer": "برگزارکننده", + "attendee": "شرکت‌کننده", + "accepted": "پذیرفته شد", + "declined": "رد شد", + "tentative": "احتمالی", + "needs_action": "نیاز به اقدام", + "remove": "حذف", + "edit": "ویرایش", + "email_placeholder": "آدرس ایمیل را وارد کنید یا مخاطبین را جستجو کنید", + "send_invitations": "ارسال دعوت‌نامه به شرکت‌کنندگان", + "status_summary": "{accepted} پذیرفته، {pending} در انتظار", + "invited_by": "دعوت از طرف {name}", + "respond_below": "با دکمه‌های زیر پاسخ دهید", + "rsvp_label": "پاسخ شما", + "cancel_notification": "شرکت‌کنندگان از لغو مطلع خواهند شد", + "you_organizer": "شما برگزارکننده هستید", + "you_attendee": "شما شرکت‌کننده هستید", + "no_participants": "شرکت‌کننده‌ای نیست", + "count": "{count, plural, one {# شرکت‌کننده} other {# شرکت‌کننده}}" + }, + "recurrence": { + "title": "تکرار", + "none": "تکرار نمی‌شود", + "daily": "روزانه", + "weekly": "هفتگی", + "monthly": "ماهانه", + "yearly": "سالیانه", + "every_n_days": "هر {count} روز", + "every_n_weeks": "هر {count} هفته", + "every_n_months": "هر {count} ماه", + "until": "تا", + "occurrences": "{count} بار تکرار", + "custom": "سفارشی…", + "edit_custom": "ویرایش تکرار سفارشی", + "every_n_years": "هر {count} سال", + "on_days": "در {days}", + "on_day_n": "در روز {day}", + "on_the_nth": "در {nth} {day}", + "in_month": "در {month}", + "nth_1": "اول", + "nth_2": "دوم", + "nth_3": "سوم", + "nth_4": "چهارم", + "nth_last": "آخر", + "editor_freq_day": "روز", + "editor_freq_week": "هفته", + "editor_freq_month": "ماه", + "editor_freq_year": "سال", + "editor_repeats_on": "تکرار در", + "editor_every": "هر", + "editor_unit_days": "روز", + "editor_unit_weeks": "هفته", + "editor_unit_months": "ماه", + "editor_unit_years": "سال", + "editor_on_day": "در روز", + "editor_on_the": "در", + "editor_in": "در", + "editor_ends": "پایان", + "editor_never": "هرگز", + "editor_ends_on": "در تاریخ", + "editor_ends_after": "بعد از", + "editor_occurrences": "بار" + }, + "recurrence_scope": { + "edit_title": "ویرایش رویداد تکراری", + "delete_title": "حذف رویداد تکراری", + "description": "این یک رویداد تکراری است. کدام رویدادها را می‌خواهید تغییر دهید؟", + "this_event": "فقط این رویداد", + "this_and_future": "این رویداد و رویدادهای بعدی", + "all_events": "همه رویدادها", + "cancel": "انصراف", + "save": "ذخیره", + "delete": "حذف", + "rsvp_title": "پاسخ به رویداد تکراری", + "rsvp_description": "این یک رویداد تکراری است. پاسخ شما برای کدام رویدادها اعمال شود؟", + "respond": "پاسخ" + }, + "alerts": { + "title": "یادآورها", + "none": "بدون یادآور", + "at_time": "در زمان رویداد", + "minutes_before": "{count, plural, one {# دقیقه قبل} other {# دقیقه قبل}}", + "hours_before": "{count, plural, one {# ساعت قبل} other {# ساعت قبل}}", + "days_before": "{count, plural, one {# روز قبل} other {# روز قبل}}", + "weeks_before": "{count, plural, one {# هفته قبل} other {# هفته قبل}}", + "unit_minutes_before": "دقیقه قبل", + "unit_hours_before": "ساعت قبل", + "unit_days_before": "روز قبل", + "unit_weeks_before": "هفته قبل", + "add": "افزودن یادآور", + "remove": "حذف یادآور", + "amount": "مقدار یادآور", + "unit": "واحد یادآور", + "action": "نوع یادآور", + "action_display": "اعلان", + "action_email": "ایمیل", + "by_email": "{label} از طریق ایمیل", + "kept": "{count, plural, one {# یادآور دیگر که اینجا قابل ویرایش نیست حفظ می‌شود} other {# یادآور دیگر که اینجا قابل ویرایش نیستند حفظ می‌شوند}}", + "other": "{count, plural, one {# یادآور دیگر} other {# یادآور دیگر}}", + "calendar_defaults": "یادآورهای پیش‌فرض تقویم", + "customize": "سفارشی‌سازی", + "use_defaults": "استفاده از یادآورهای پیش‌فرض تقویم" + }, + "settings": { + "limit_hours": "محدود کردن ساعت‌های نمایش", + "limit_hours_desc": "در نمای روز و هفته فقط این ساعت‌ها نمایش داده می‌شوند. رویدادهای خارج از آن‌ها از طریق پیکان‌های بالا و پایین هر روز در دسترس هستند.", + "visible_hours": "ساعت‌های نمایش", + "visible_hours_start": "شروع ساعت‌های نمایش", + "visible_hours_end": "پایان ساعت‌های نمایش", + "hide_non_working_days": "پنهان کردن روزهای تعطیل", + "hide_non_working_days_desc": "روزهایی که کار نمی‌کنید از نمای هفته حذف می‌شوند. رویدادهای آن‌ها همچنان در نمای ماه، روز و دستور کار نمایش داده می‌شوند.", + "working_days": "روزهای کاری", + "title": "تنظیمات تقویم", + "default_view": "نمای پیش‌فرض", + "week_starts_on": "شروع هفته", + "time_format": "فرمت زمان", + "default_calendar": "تقویم پیش‌فرض", + "default_reminder": "یادآور پیش‌فرض", + "time_format_12h": "۱۲ ساعته", + "time_format_24h": "۲۴ ساعته", + "notifications_enabled": "اعلان‌های رویداد", + "notifications_enabled_desc": "نمایش هشدار برای رویدادهای تقویم پیش رو", + "notification_sound": "صدای اعلان", + "notification_sound_desc": "پخش صدا برای هشدارهای تقویم", + "invitation_parsing": "پردازش دعوت‌نامه‌های ایمیل", + "invitation_parsing_desc": "تشخیص دعوت‌نامه‌های تقویم در پیوست ایمیل", + "show_time_in_month_view": "نمایش زمان در نمای ماه", + "show_time_in_month_view_desc": "نمایش زمان رویدادها در نمای ماه تقویم", + "show_week_numbers": "نمایش شماره هفته", + "show_week_numbers_desc": "نمایش شماره هفته در تقویم کوچک", + "calendar_free_scroll": "پیمایش آزاد", + "calendar_free_scroll_desc": "پیمایش پیوسته در ماه‌ها، هفته‌ها و روزها به جای یک بازه در هر بار", + "enable_tasks": "فعال کردن وظایف", + "enable_tasks_desc": "نمایش نمای وظایف در تقویم برای مدیریت کارها", + "show_tasks_on_calendar": "نمایش وظایف روی تقویم", + "show_tasks_on_calendar_desc": "نمایش وظایف در نماهای ماه، هفته و روز", + "hover_preview": "پیش‌نمایش هاور رویداد", + "hover_preview_desc": "نمایش popover جزئیات هنگام هاور روی رویدادهای تقویم", + "hover_preview_instant": "بلافاصله", + "hover_preview_delay_500ms": "تأخیر ۰.۵ ثانیه", + "hover_preview_delay_1s": "تأخیر ۱ ثانیه", + "hover_preview_delay_2s": "تأخیر ۲ ثانیه", + "hover_preview_off": "غیرفعال", + "organizer_identity": "سازمان‌دهی دعوت‌ها به‌عنوان", + "organizer_identity_desc": "آدرس تقویمی که هنگام دعوت افراد به رویدادها به‌عنوان برگزارکننده استفاده می‌شود.", + "organizer_identity_failed": "تغییر آدرس برگزارکننده ناموفق بود", + "show_birthday_calendar": "تقویم تولد مخاطبین", + "show_birthday_calendar_desc": "نمایش تقویم مجازی با تولدهای مخاطبین" + }, + "days": { + "monday": "دوشنبه", + "tuesday": "سه‌شنبه", + "wednesday": "چهارشنبه", + "thursday": "پنج‌شنبه", + "friday": "جمعه", + "saturday": "شنبه", + "sunday": "یکشنبه", + "mon": "د", + "tue": "س", + "wed": "چ", + "thu": "پ", + "fri": "ج", + "sat": "ش", + "sun": "ی" + }, + "months": { + "jan": "ژانویه", + "feb": "فوریه", + "mar": "مارس", + "apr": "آوریل", + "may": "مه", + "jun": "ژوئن", + "jul": "ژوئیه", + "aug": "اوت", + "sep": "سپتامبر", + "oct": "اکتبر", + "nov": "نوامبر", + "dec": "دسامبر", + "far": "فروردین", + "ord": "اردیبهشت", + "kho": "خرداد", + "tir": "تیر", + "mor": "مرداد", + "sha": "شهریور", + "meh": "مهر", + "aba": "آبان", + "aza": "آذر", + "dey": "دی", + "bah": "بهمن", + "esf": "اسفند" + }, + "notifications": { + "event_created": "رویداد ایجاد شد", + "event_updated": "رویداد به‌روزرسانی شد", + "event_deleted": "رویداد حذف شد", + "calendar_created": "تقویم ایجاد شد", + "calendar_deleted": "تقویم حذف شد", + "event_move_error": "انتقال رویداد ناموفق بود", + "event_resize_error": "تغییر اندازه رویداد ناموفق بود", + "alert_title": "رویداد پیش رو", + "alert_now": "همین الان شروع می‌شود", + "alert_in_minutes": "{count} دقیقه دیگر", + "invitation_sent": "دعوت‌نامه‌ها ارسال شد", + "invitations_denied_title": "ارسال دعوت‌نامه‌ها ممکن نیست", + "invitations_denied": "سرور از ارسال دعوت‌نامه‌ها خودداری کرد: {reason}. رویداد بدون ارسال آن‌ها ذخیره شود؟", + "save_without_invitations": "ذخیره بدون دعوت‌نامه", + "rsvp_updated": "پاسخ به‌روزرسانی شد", + "rsvp_error": "به‌روزرسانی پاسخ ناموفق بود", + "event_duplicated": "رویداد کپی شد", + "event_error": "ذخیره رویداد ناموفق بود", + "task_due": "موعد وظیفه", + "event_exported": "رویداد خروجی گرفته شد", + "title_copied": "عنوان کپی شد", + "link_copied": "لینک کپی شد" + }, + "status": { + "loading_calendars": "در حال بارگذاری تقویم‌ها...", + "loading_events": "در حال بارگذاری رویدادها..." + }, + "quick_create": { + "placeholder": "عنوان رویداد جدید", + "aria_label": "ایجاد سریع رویداد", + "add_title": "افزودن عنوان", + "more_options": "گزینه‌های بیشتر" + }, + "nav_prev": "قبلی", + "nav_next": "بعدی", + "nav_open_menu": "باز کردن منو", + "import": { + "title": "وارد کردن تقویم", + "tab_file": "فایل", + "tab_url": "آدرس", + "select_file": "انتخاب فایل .ics", + "drop_file": "یا فایل را اینجا رها کنید", + "supported_formats": "پشتیبانی از فایل‌های iCalendar (.ics)", + "url_description": "آدرس فید خارجی iCalendar (.ics) را برای وارد کردن رویدادها وارد کنید.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "پشتیبانی از آدرس‌های iCalendar (.ics)", + "fetch": "دریافت", + "invalid_url": "لطفاً یک آدرس معتبر وارد کنید", + "url_fetch_failed": "دریافت تقویم از آدرس ناموفق بود", + "parsing": "در حال پردازش فایل تقویم...", + "parsed_events": "{count} رویداد یافت شد", + "no_events": "رویدادی در فایل یافت نشد", + "select_all": "انتخاب همه", + "deselect_all": "لغو انتخاب همه", + "target_calendar": "وارد کردن به تقویم", + "import_button": "وارد کردن انتخاب‌شده‌ها", + "importing": "در حال وارد کردن رویدادها...", + "success": "{count} رویداد با موفقیت وارد شد", + "error": "وارد کردن تقویم ناموفق بود", + "file_too_large": "حجم فایل بیش از ۱۰ مگابایت است", + "invalid_format": "فرمت فایل تقویم نامعتبر است" + }, + "webcal_action": { + "title": "باز کردن لینک تقویم", + "description": "چگونه می‌خواهید از \"{name}\" استفاده کنید؟", + "import_title": "وارد کردن یکباره", + "import_description": "رویدادها را همین حالا دریافت و در یکی از تقویم‌هایتان کپی کنید.", + "subscribe_title": "اشتراک", + "subscribe_description": "این تقویم را به صورت خودکار به عنوان تقویم جداگانه همگام نگه دارید.", + "cancel": "انصراف" + }, + "management": { + "title": "مدیریت تقویم", + "description": "ایجاد، تغییر نام و سفارشی‌سازی تقویم‌ها.", + "name": "نام", + "name_placeholder": "نام تقویم", + "color": "رنگ", + "change_color": "تغییر رنگ", + "random_color": "رنگ تصادفی جدید", + "add_calendar": "افزودن تقویم", + "edit": "ویرایش", + "delete": "حذف", + "save": "ذخیره", + "create": "ایجاد", + "cancel": "انصراف", + "default": "پیش‌فرض", + "confirm_delete": "حذف \"{name}\"؟ همه رویدادهای این تقویم حذف خواهند شد.", + "confirm_clear": "پاک کردن همه رویدادهای \"{name}\"؟ این عملیات قابل بازگشت نیست.", + "set_default": "تنظیم به عنوان پیش‌فرض", + "default_updated": "تقویم پیش‌فرض به‌روزرسانی شد", + "error_default": "تنظیم تقویم پیش‌فرض ناموفق بود", + "clear_events": "پاک کردن رویدادها", + "events_cleared": "{count} رویداد پاک شد", + "error_clear": "پاک کردن رویدادهای تقویم ناموفق بود", + "calendar_created": "تقویم ایجاد شد", + "calendar_updated": "تقویم به‌روزرسانی شد", + "calendar_deleted": "تقویم حذف شد", + "color_updated": "رنگ تقویم به‌روزرسانی شد", + "error_create": "ایجاد تقویم ناموفق بود", + "error_update": "به‌روزرسانی تقویم ناموفق بود", + "error_delete": "حذف تقویم ناموفق بود", + "caldav_url": "آدرس CalDAV", + "copy_url": "کپی آدرس CalDAV", + "url_copied": "آدرس CalDAV در کلیپ‌بورد کپی شد", + "share": "اشتراک‌گذاری تقویم", + "new_event_in_calendar": "رویداد جدید در این تقویم", + "shared_with": "به اشتراک گذاشته شده با {names}", + "shared_with_count": "{count, plural, one {با ۱ نفر به اشتراک گذاشته شده} other {با # نفر به اشتراک گذاشته شده}}", + "move_up": "انتقال به بالا", + "move_down": "انتقال به پایین", + "kind": "محتوا", + "kind_events": "رویدادها", + "kind_tasks": "وظایف", + "kind_both": "رویدادها و وظایف", + "kind_hint": "تعیین می‌کند کدام برنامه‌ها این تقویم را هنگام همگام‌سازی از طریق CalDAV می‌بینند. بعداً قابل تغییر نیست.", + "default_alerts": "یادآورهای پیش‌فرض", + "default_alerts_title": "یادآورهای پیش‌فرض برای \"{name}\"", + "default_alerts_hint": "رویدادهای جدید این تقویم این یادآورها را دریافت می‌کنند، مگر اینکه در خود رویداد تغییر داده شوند. تغییر آن‌ها در اینجا رویدادهای موجود را تغییر نمی‌دهد. یادآورهای ایمیلی توسط سرور ایمیل ارسال می‌شوند.", + "default_alerts_timed": "رویدادهای دارای زمان", + "default_alerts_all_day": "رویدادهای تمام‌روز", + "default_alerts_updated": "یادآورهای پیش‌فرض به‌روزرسانی شد" + }, + "subscription": { + "title": "اشتراک iCal", + "section_title": "اشتراک‌های iCal", + "description": "اشتراک در فید خارجی iCalendar. رویدادها به صورت خودکار در تقویم خودشان همگام می‌شوند.", + "url_label": "آدرس تقویم", + "url_placeholder": "https://example.com/calendar.ics یا webcal://...", + "name_label": "نام تقویم", + "name_placeholder": "مثال: تعطیلات رسمی", + "color_label": "رنگ", + "refresh_interval": "فاصله بازنشانی", + "interval_15": "هر ۱۵ دقیقه", + "interval_30": "هر ۳۰ دقیقه", + "interval_60": "هر ساعت", + "interval_360": "هر ۶ ساعت", + "interval_1440": "هر روز", + "subscribe": "اشتراک", + "subscribing": "در حال اشتراک...", + "save": "ذخیره تغییرات", + "saving": "در حال ذخیره...", + "edit": "ویرایش", + "edit_title": "ویرایش اشتراک", + "updated": "\"{name}\" به‌روزرسانی شد", + "update_error": "به‌روزرسانی اشتراک ناموفق بود", + "invalid_url": "لطفاً یک آدرس معتبر وارد کنید", + "success": "اشتراک در \"{name}\" موفق بود", + "error": "افزودن اشتراک ناموفق بود", + "refresh": "بازنشانی هم‌اکنون", + "refresh_success": "اشتراک بازنشانی شد", + "refresh_error": "بازنشانی اشتراک ناموفق بود", + "unsubscribe": "لغو اشتراک", + "confirm_delete": "لغو اشتراک از \"{name}\"؟ تقویم و همه رویدادهایش حذف خواهند شد.", + "deleted": "اشتراک حذف شد", + "delete_error": "حذف اشتراک ناموفق بود", + "last_refreshed": "آخرین به‌روزرسانی: {time}" + }, + "tasks": { + "label": "وظایف", + "no_tasks": "وظیفه‌ای نیست", + "no_title": "(بدون عنوان)", + "mark_complete": "علامت‌گذاری تکمیل شده", + "mark_incomplete": "علامت‌گذاری تکمیل نشده", + "filter_all": "همه", + "filter_pending": "در انتظار", + "filter_completed": "تکمیل شده", + "filter_overdue": "عقب‌افتاده", + "show_completed": "نمایش تکمیل شده‌ها", + "create": "وظیفه جدید", + "edit": "ویرایش وظیفه", + "title_placeholder": "عنوان وظیفه", + "description_placeholder": "افزودن توضیحات...", + "due_date": "موعد", + "include_time": "شامل زمان", + "priority": "اولویت", + "priority_none": "هیچکدام", + "priority_high": "بالا", + "priority_medium": "متوسط", + "priority_low": "پایین", + "progress": "وضعیت", + "progress_needs_action": "نیاز به اقدام", + "progress_in_process": "در حال انجام", + "progress_completed": "تکمیل شده", + "progress_cancelled": "لغو شده", + "calendar": "تقویم", + "alert": "یادآور", + "alert_none": "هیچکدام", + "alert_at_time": "در زمان موعد", + "alert_5min": "۵ دقیقه قبل", + "alert_15min": "۱۵ دقیقه قبل", + "alert_30min": "۳۰ دقیقه قبل", + "alert_1hr": "۱ ساعت قبل", + "alert_1day": "۱ روز قبل", + "delete": "حذف", + "cancel": "انصراف", + "save": "ذخیره", + "quick_add_placeholder": "افزودن وظیفه...", + "due_today": "امروز", + "due_tomorrow": "فردا", + "overdue": "عقب‌افتاده" + }, + "toolbar": { + "more": "اقدامات بیشتر", + "select_view": "تغییر نما، نمای فعلی: {view}" + }, + "other_calendars": "تقویم‌های دیگر", + "calendar_options": "گزینه‌های {name}" + }, + "calendar_event_notifications": { + "someone": "کسی", + "invited": "{name} شما را به «{title}» دعوت کرد", + "updated": "{name} «{title}» را به‌روزرسانی کرد", + "cancelled": "{name} «{title}» را لغو کرد", + "untitled": "رویداد بدون عنوان" + }, + "share_notifications": { + "someone": "کسی", + "shared": "{name} {kind} «{object}» را با شما به اشتراک گذاشت", + "changed": "{name} دسترسی شما به {kind} «{object}» را تغییر داد", + "revoked": "{name} دسترسی شما به {kind} «{object}» را حذف کرد", + "object": { + "folder": "پوشه", + "calendar": "تقویم", + "address_book": "دفترچه آدرس", + "files": "پوشه فایل" + } + }, + "sharing": { + "title": "اشتراک‌گذاری \"{name}\"", + "description": "دسترسی به سایر کاربران یا گروه‌های این سرور را فراهم کنید.", + "no_shares": "هنوز با کسی به اشتراک گذاشته نشده.", + "add_person": "افزودن شخص یا گروه", + "search_placeholder": "جستجو با نام یا ایمیل…", + "loading_principals": "در حال بارگذاری کاربران…", + "no_principals": "کاربر یا گروه دیگری یافت نشد.", + "no_match": "موردی مطابقت ندارد.", + "remove": "حذف دسترسی", + "group": "گروه", + "share_added": "دسترسی اعطا شد", + "share_updated": "دسترسی به‌روزرسانی شد", + "share_removed": "دسترسی حذف شد", + "share_failed": "به‌روزرسانی اشتراک‌گذاری ناموفق بود", + "preset": { + "freeBusy": "فقط آزاد/مشغول", + "read": "فقط خواندنی", + "readWrite": "خواندن و نوشتن", + "manager": "مدیر", + "custom": "سفارشی" + } + }, + "advanced_search": { + "title": "جستجوی پیشرفته", + "from": "از", + "from_placeholder": "ایمیل یا نام فرستنده", + "to": "به", + "to_placeholder": "ایمیل یا نام گیرنده", + "subject": "موضوع", + "subject_placeholder": "موضوع شامل...", + "body": "بدنه", + "body_placeholder": "بدنه شامل...", + "folder": "پوشه", + "all_folders": "همه پوشه‌ها", + "all_folders_except_spam_trash": "همه پوشه‌ها به‌جز هرزنامه و زباله‌دان", + "has_attachment": "پیوست", + "date_after": "بعد از", + "date_before": "قبل از", + "size_min": "بزرگ‌تر از (کیلوبایت)", + "size_max": "کوچک‌تر از (کیلوبایت)", + "starred": "ستاره‌دار", + "unread": "خوانده نشده", + "read": "خوانده شده", + "yes": "بله", + "no": "خیر", + "clear": "پاک کردن", + "clear_all": "پاک کردن همه", + "filters_active": "{count} فیلتر", + "filters_active_plural": "{count} فیلتر", + "toggle_filters": "بیشتر", + "search_hint": "از فیلترهای پیشرفته برای جستجوی دقیق استفاده کنید", + "advanced_filters_tooltip": "فیلترهای جستجوی پیشرفته", + "results_found": "{count, plural, =0 {نتیجه‌ای یافت نشد} one {# نتیجه یافت شد} other {# نتیجه یافت شد}}", + "results_found_more": "+{count} نتیجه یافت شد", + "suggestions_recent": "جستجوهای اخیر", + "suggestions_people": "افراد", + "suggestions_from": "جستجوی ایمیل‌های از {address}", + "suggestions_to": "جستجوی ایمیل‌های به {address}", + "suggestions_remove_recent": "حذف از جستجوهای اخیر", + "has_words": "شامل کلمات", + "size": "اندازه", + "date_within": "تاریخ", + "search_in": "جستجو در", + "has_attachment_check": "دارای پیوست", + "search": "جستجو" + }, + "welcome": { + "title": "به صندوق پستی خود خوش آمدید", + "tip_compose": "کلید c را برای ایمیل جدید فشار دهید", + "tip_shortcuts": "کلید ? را برای مشاهده میانبرها فشار دهید", + "tip_sidebar": "تقویم، مخاطبین و تنظیمات در نوار کناری", + "tip_settings": "تجربه خود را در تنظیمات سفارشی کنید", + "tip_compose_touch": "برای نوشتن ایمیل جدید روی دکمه مداد بزنید", + "tip_sidebar_touch": "تقویم، مخاطبین و تنظیمات در نوار پایین هستند", + "got_it": "متوجه شدم", + "settings": "تنظیمات", + "dismiss": "رد کردن", + "start_tour": "شروع تور" + }, + "demo_welcome": { + "title": "به Bulwark Mail خوش آمدید", + "description": "یک سرویس ایمیل کامل را در مرورگر خود کاوش کنید.", + "feature_email": "خواندن و نوشتن ایمیل", + "feature_organize": "برچسب‌ها، ستاره‌ها و پوشه‌ها", + "feature_shortcuts": "میانبرهای صفحه کلید", + "feature_privacy": "نسخه آزمایشی ۱۰۰٪ خصوصی", + "hint": "روی هر ایمیل در سمت چپ کلیک کنید یا تور را ببینید." + }, + "files": { + "office_edit": "ویرایش سند", + "office_loading": "در حال باز کردن ویرایشگر سند…", + "office_error": "ویرایشگر سند باز نشد.", + "office_close": "بستن ویرایشگر", + "open_in_new_tab": "باز کردن در تب جدید", + "title": "فایل‌ها", + "search_placeholder": "جستجوی فایل‌ها...", + "empty_state_title": "هنوز فایلی نیست", + "upload_files": "بارگذاری فایل‌ها", + "new_folder": "پوشه جدید", + "new_folder_name": "نام پوشه", + "rename": "تغییر نام", + "rename_title": "تغییر نام", + "new_name": "نام جدید", + "name_forbidden_chars": "سرور این نویسه‌ها را در نام‌ها مجاز نمی‌داند: {chars}", + "name_reserved": "این نام در سرور رزرو شده است.", + "delete": "حذف", + "delete_confirm_title": "حذف منبع", + "delete_confirm_message": "آیا از حذف \"{name}\" اطمینان دارید؟ این عملیات قابل بازگشت نیست.", + "download": "دانلود", + "name": "نام", + "size": "حجم", + "modified": "تاریخ تغییر", + "type": "نوع", + "folder": "پوشه", + "file": "فایل", + "no_accounts": "حساب متصلی نیست.", + "uploading": "در حال بارگذاری...", + "upload_success": "{count, plural, one {۱ فایل بارگذاری شد} other {# فایل بارگذاری شد}}", + "upload_canceled": "بارگذاری لغو شد", + "upload_canceled_partial": "{count, plural, one {۱ فایل} other {# فایل}} پیش از لغو بارگذاری شد", + "upload_error": "بارگذاری فایل ناموفق بود", + "create_folder_success": "پوشه ایجاد شد", + "create_folder_error": "ایجاد پوشه ناموفق بود", + "delete_success": "با موفقیت حذف شد", + "delete_error": "حذف ناموفق بود", + "rename_success": "تغییر نام با موفقیت انجام شد", + "rename_error": "تغییر نام ناموفق بود", + "download_error": "دانلود ناموفق بود", + "not_available": "ذخیره‌سازی فایل روی این سرور در دسترس نیست", + "cancel": "انصراف", + "create": "ایجاد", + "save": "ذخیره", + "no_results": "فایلی با این جستجو مطابقت ندارد", + "batch_delete_confirm_message": "آیا از حذف {count, plural, one {۱ مورد} other {# مورد}} اطمینان دارید؟", + "batch_delete_success": "{count, plural, one {۱ مورد حذف شد} other {# مورد حذف شد}}", + "grid_view": "نمای شبکه‌ای", + "list_view": "نمای لیست", + "details": "جزئیات", + "path": "مسیر", + "preview": "پیش‌نمایش", + "preview_error": "بارگذاری پیش‌نمایش ناموفق بود", + "cut": "برش", + "copy": "کپی", + "paste": "چسباندن", + "move_success": "{count, plural, one {۱ مورد منتقل شد} other {# مورد منتقل شد}}", + "move_error": "انتقال ناموفق بود", + "paste_success": "با موفقیت چسبانده شد", + "paste_error": "چسباندن ناموفق بود", + "new_text_file": "فایل متنی جدید", + "file_name": "نام فایل", + "retry": "تلاش مجدد", + "refresh": "بازنشانی", + "duplicate": "کپی", + "duplicate_success": "با موفقیت کپی شد", + "duplicate_error": "کپی ناموفق بود", + "share": "اشتراک‌گذاری", + "shared": "اشتراکی", + "shared_with_me": "با من به اشتراک گذاشته شده", + "shared_by": "به اشتراک گذاشته شده توسط {name}", + "create_file_success": "فایل ایجاد شد", + "create_file_error": "ایجاد فایل ناموفق بود", + "favorites": "مورد علاقه‌ها", + "recent": "اخیر", + "show_in_folder": "نمایش در پوشه", + "remove_from_recent": "حذف از اخیر", + "upload_folder": "بارگذاری پوشه", + "file_too_large": "\"{name}\" بیش از حداکثر حجم مجاز است ({max})", + "undo": "بازگردانی", + "undo_success": "عملیات بازگردانی شد", + "undo_error": "بازگردانی ناموفق بود", + "toolbar": "عملیات فایل", + "open_folder_tree": "باز کردن درخت پوشه", + "file_list": "فایل‌ها و پوشه‌ها", + "context_menu": "عملیات‌ها", + "settings_ascending": "صعودی", + "settings_descending": "نزولی", + "disabled_title": "ویژگی فایل‌ها توسط مدیر غیرفعال شده", + "disabled_description": "بارگذاری فایل‌های حجیم از طریق WebDAV می‌تواند باعث ناپایداری سرور شود.", + "migration_title": "در حال به‌روزرسانی فایل‌های شما…", + "migration_description": "سازمان‌دهی پوشه‌ها و فایل‌ها در ساختار مناسب. فقط یک بار انجام می‌شود.", + "new": "جدید", + "my_files": "فایل‌های من", + "folders": "پوشه‌ها", + "files_heading": "فایل‌ها", + "open": "باز کردن", + "star": "افزودن به مورد علاقه‌ها", + "unstar": "حذف از مورد علاقه‌ها", + "expand_folder": "باز کردن {name}", + "collapse_folder": "بستن {name}", + "sidebar_hint": "پوشه‌های مورد علاقه و فایل‌هایی که باز کرده‌اید اینجا نمایش داده می‌شوند.", + "breadcrumbs": "مسیر پوشه", + "show_path": "نمایش پوشه‌های پنهان در مسیر", + "selection_actions": "عملیات برای موارد انتخاب‌شده", + "selected_count": "{count, plural, one {۱ مورد انتخاب شد} other {# مورد انتخاب شد}}", + "clear_selection": "لغو انتخاب", + "select_all": "انتخاب همه", + "select_item": "انتخاب {name}", + "more_actions": "عملیات بیشتر برای {name}", + "paste_here": "چسباندن در این پوشه", + "paste_count": "{count, plural, one {چسباندن ۱ مورد} other {چسباندن # مورد}}", + "sort_by": "مرتب‌سازی بر اساس", + "view_mode": "نما", + "clear_search": "پاک کردن جستجو", + "no_results_for": "هیچ موردی در این پوشه با \"{query}\" مطابقت ندارد", + "download_count": "{count, plural, one {دانلود ۱ فایل} other {دانلود # فایل}}", + "delete_count": "{count, plural, one {حذف ۱ مورد} other {حذف # مورد}}", + "close_details": "بستن جزئیات", + "file_kind": "فایل {ext}", + "folder_summary": "{folders, plural, =0 {بدون پوشه} one {۱ پوشه} other {# پوشه}}، {files, plural, =0 {بدون فایل} one {۱ فایل} other {# فایل}}", + "details_hint": "یک فایل یا پوشه را انتخاب کنید تا جزئیات آن را ببینید.", + "sharing": "اشتراک‌گذاری", + "shared_with_count": "{count, plural, one {با ۱ نفر به اشتراک گذاشته شده} other {با # نفر به اشتراک گذاشته شده}}", + "only_you": "فقط شما", + "empty_folder_title": "این پوشه خالی است", + "empty_folder_description": "فایل‌ها را اینجا رها کنید یا از دستگاه خود بارگذاری کنید.", + "empty_folder_read_only": "نمی‌توانید به این پوشه فایل اضافه کنید.", + "choose_account": "انتخاب حساب", + "drop_to_upload": "برای بارگذاری در {folder} رها کنید", + "uploading_count": "در حال بارگذاری {current} از {total}", + "dismiss": "بستن", + "zoom_in": "بزرگ‌نمایی", + "zoom_out": "کوچک‌نمایی", + "zoom_reset": "متناسب با صفحه", + "rotate": "چرخش", + "close_preview": "بستن" + }, + "smime": { + "your_certificates": "گواهی‌های شما", + "your_certificates_desc": "مدیریت گواهی‌های S/MIME", + "recipient_certificates": "گواهی‌های گیرندگان", + "recipient_certificates_desc": "گواهی‌های عمومی برای رمزنگاری", + "identity_bindings": "اتصال کلید به هویت", + "identity_bindings_desc": "اتصال گواهی به هویت‌های ایمیل", + "defaults_title": "پیش‌فرض‌ها", + "defaults_desc": "تنظیم رفتار پیش‌فرض امضا و رمزنگاری", + "import_pkcs12": "وارد کردن PKCS#12", + "import_public_cert": "وارد کردن گواهی", + "no_certificates": "گواهی وارد نشده", + "no_recipient_certs": "گواهی گیرنده نیست", + "expires": "انقضا", + "expired": "منقضی شده", + "bound_to": "متصل به", + "no_key_bound": "هیچ", + "lock": "قفل کردن", + "unlock": "باز کردن", + "details": "جزئیات", + "delete": "حذف", + "encrypt_by_default": "رمزنگاری پیش‌فرض", + "encrypt_by_default_desc": "رمزنگاری خودکار", + "remember_unlocked": "به خاطر سپردن کلید", + "remember_unlocked_desc": "در طول نشست مرورگر", + "sign_default_for": "امضای پیش‌فرض برای", + "enter_p12_passphrase": "عبارت عبور PKCS#12", + "p12_passphrase_desc": "عبارت محافظت از گواهی", + "enter_storage_passphrase": "عبارت عبور ذخیره‌سازی", + "storage_passphrase_desc": "محافظت از کلید در مرورگر", + "next": "بعدی", + "import": "وارد کردن", + "unlock_key": "باز کردن کلید", + "unlock_key_desc": "عبارت عبور برای باز کردن", + "passphrase_placeholder": "عبارت عبور", + "confirm_passphrase_placeholder": "تأیید عبارت عبور", + "passphrase_mismatch": "مطابقت ندارد", + "cancel": "انصراف", + "processing": "در حال پردازش…", + "close": "بستن", + "certificate_details": "جزئیات گواهی", + "cert_subject": "دارنده", + "cert_issuer": "صادرکننده", + "cert_email": "ایمیل", + "cert_serial": "شماره سریال", + "cert_validity": "اعتبار", + "cert_fingerprint": "اثر انگشت", + "cert_algorithm": "الگوریتم", + "cert_capabilities": "قابلیت‌ها", + "cert_source": "منبع", + "cert_expired": "منقضی شده", + "cert_not_yet_valid": "هنوز معتبر نیست", + "cap_sign": "امضا", + "cap_encrypt": "رمزنگاری", + "cap_none": "هیچ", + "show_passphrase": "نمایش", + "hide_passphrase": "مخفی", + "sign_toggle": "امضا", + "encrypt_toggle": "رمزنگاری", + "missing_recipient_certs": "گواهی گیرنده موجود نیست: {emails}", + "missing_sender_cert": "گواهی به این هویت متصل نیست", + "status_encrypted_ok": "پیام رمزنگاری شده", + "status_encrypted_no_key": "کلید مطابق یافت نشد", + "status_encrypted_failed": "رمزگشایی ناموفق", + "status_signed_valid": "امضا تأیید شد", + "status_signed_invalid": "تأیید امضا ناموفق", + "status_signed_expired_cert": "گواهی منقضی شده", + "status_signed_self_signed": "گواهی self-signed", + "status_signed_mismatch": "امضا با فرستنده مطابقت ندارد", + "status_unsupported": "فرمت پشتیبانی نشده", + "auto_import_signer_certs": "وارد کردن خودکار گواهی امضا", + "auto_import_signer_certs_desc": "ذخیره خودکار گواهی‌ها", + "export": "خروجی", + "enter_export_passphrase": "عبارت عبور خروجی", + "export_passphrase_desc": "محافظت از فایل PKCS#12", + "export_storage_desc": "عبارت ذخیره‌سازی را وارد کنید", + "incorrect_passphrase": "عبارت عبور نادرست" + }, + "tour": { + "step_counter": "مرحله {current} از {total}", + "skip": "رد کردن تور", + "back": "بازگشت", + "next": "بعدی", + "finish": "پایان", + "take_a_tour": "یک تور از رابط کاربری ببینید", + "restart_title": "تور مقدماتی", + "restart_desc": "بازپخش راهنمای تعاملی رابط کاربری", + "restart_button": "شروع مجدد تور", + "show_on_new_devices_title": "نمایش در دستگاه‌های جدید", + "show_on_new_devices_desc": "بازپخش بنر خوش‌آمدگویی و تور در اولین ورود در دستگاه جدید", + "sidebar_title": "صندوق‌های پستی شما", + "sidebar_desc": "این نوار کناری پوشه‌های شماست. روی هر صندوق کلیک کنید تا ایمیل‌هایش را ببینید.", + "compose_title": "نوشتن ایمیل", + "compose_desc": "برای نوشتن ایمیل جدید اینجا کلیک کنید. می‌توانید گیرنده، پیوست و قالب‌بندی اضافه کنید.", + "search_title": "جستجوی ایمیل‌ها", + "search_desc": "جستجو بر اساس فرستنده، موضوع یا محتوا. روی آیکون فیلتر برای گزینه‌های پیشرفته کلیک کنید.", + "email_list_title": "لیست ایمیل‌های شما", + "email_list_desc": "ایمیل‌ها اینجا نمایش داده می‌شوند. روی یکی کلیک کنید تا در سمت راست بخوانید.", + "email_viewer_title": "پنل مطالعه", + "email_viewer_desc": "ایمیل انتخاب شده اینجا باز می‌شود. پاسخ، ارسال مجدد، بایگانی یا حذف با دکمه‌های نوار ابزار.", + "keywords_title": "برچسب‌های رنگی", + "keywords_desc": "ایمیل‌های خود را با برچسب‌های رنگی سازمان‌دهی کنید.", + "calendar_title": "تقویم", + "calendar_desc": "برای مدیریت رویدادها به تقویم بروید. رویداد ایجاد کنید و یادآور تنظیم کنید.", + "contacts_title": "مخاطبین", + "contacts_desc": "دفترچه آدرس شما اینجاست. مخاطبین وارد کنید، گروه بسازید.", + "settings_title": "تنظیمات", + "settings_desc": "همه چیز را سفارشی کنید: تم، تراکم، امضاها، فیلترها، میانبرها و بیشتر.", + "shortcuts_title": "میانبرهای صفحه کلید", + "shortcuts_desc": "کاربران حرفه‌ای عاشق این هستند. هر زمان ? را فشار دهید تا همه میانبرها را ببینید.", + "compose_open_title": "ویرایشگر", + "compose_open_desc": "این ویرایشگر ایمیل است. گیرندگان را اضافه کنید، پیام بنویسید، فایل پیوست کنید.", + "calendar_view_title": "تقویم شما", + "calendar_view_desc": "این تقویم شما با رویدادهای نمونه است. بین نماهای روز، هفته و ماه جابجا شوید.", + "create_event_title": "ایجاد رویداد", + "create_event_desc": "برای ایجاد رویداد تقویم جدید این دکمه را کلیک کنید.", + "event_modal_title": "جزئیات رویداد", + "event_modal_desc": "فرم رویداد اینجاست. عنوان، تاریخ و زمان را پر کنید و ذخیره کنید.", + "contacts_list_title": "مخاطبین شما", + "contacts_list_desc": "مخاطبین شما اینجا هستند. روی هر کدام کلیک کنید تا جزئیات کامل را ببینید.", + "settings_tabs_title": "منوی تنظیمات", + "settings_tabs_desc": "همه دسته‌بندی‌های تنظیمات اینجا هستند.", + "files_title": "ذخیره‌سازی فایل", + "files_desc": "مرورگر فایل شما امکان بارگذاری، سازمان‌دهی و اشتراک‌گذاری فایل‌ها را می‌دهد.", + "demo_banner_title": "کنترل‌های نسخه آزمایشی", + "demo_banner_desc": "شما در حالت آزمایشی هستید. هر زمان \"بازنشانی نسخه آزمایشی\" را بزنید.", + "quota_title": "مصرف فضا", + "quota_desc": "مصرف صندوق پستی خود را اینجا پیگیری کنید." + }, + "unified_mailbox": { + "search_unavailable": "جستجو در نمای یکپارچه در دسترس نیست" + }, + "quote_header": { + "reply_line": "در تاریخ {date}، {from} نوشت:", + "forwarded_separator": "---------- پیام ارجاعی ----------", + "from_label": "از", + "date_label": "تاریخ", + "subject_label": "موضوع" + }, + "pwa_install": { + "title": "نصب {appName}", + "description": "برای دسترسی سریع برنامه را نصب کنید.", + "not_now": "الان نه", + "install": "نصب", + "dont_remind": "دیگر یادآوری نکن", + "dismiss_aria": "رد کردن پیشنهاد نصب" + }, + "deep_link": { + "message_not_found": "این پیام دیگر در دسترس نیست.", + "thread_not_found": "این گفتگو دیگر در دسترس نیست.", + "folder_not_found": "این پوشه دیگر در دسترس نیست.", + "event_not_found": "این رویداد دیگر در دسترس نیست.", + "file_not_found": "این فایل دیگر در دسترس نیست.", + "account_unavailable": "برای باز کردن این پیوند، به آن حساب وارد شوید.", + "copy_message": "کپی پیوند پیام", + "copy_conversation": "کپی پیوند گفتگو", + "copy_event": "کپی پیوند رویداد", + "copied": "پیوند کپی شد", + "copy_failed": "کپی پیوند ممکن نشد" + }, + "plugins": { + "oauth_callback": { + "completing": "در حال تکمیل احراز هویت…", + "completing_message": "لطفاً تا برقراری اتصال منتظر بمانید.", + "received_title": "احراز هویت دریافت شد", + "received_message": "می‌توانید به برگه‌ای که اتصال را در آن آغاز کردید بازگردید.", + "failed_title": "احراز هویت ناموفق بود", + "denied": "احراز هویت رد شد.", + "failed_detail": "احراز هویت ناموفق بود: {error}", + "missing_params": "کد احراز هویت یا حالت وجود ندارد.", + "delivery_failed": "نتیجه احراز هویت به برگه برنامه ارسال نشد. لطفاً این پنجره را ببندید و دوباره تلاش کنید." + }, + "settings": { + "title": "افزونه‌ها", + "description": "افزونه‌هایی که مدیر سیستم اضافه کرده است. آن‌ها را برای حساب خود روشن یا خاموش کنید.", + "description_uploads": "افزونه‌ها قابلیت‌هایی به ایمیل تحت وب اضافه می‌کنند. آن‌ها را برای حساب خود روشن یا خاموش کنید، یا افزونه خودتان را نصب کنید.", + "description_demo": "افزونه‌ها در نسخه آزمایشی در دسترس نیستند.", + "install_button": "نصب افزونه", + "install_hint": "یک فایل ZIP افزونه انتخاب کنید.", + "install_hint_approval": "یک فایل ZIP افزونه انتخاب کنید. پیش از آنکه بتوانید آن را روشن کنید، مدیر سیستم باید آن را تأیید کند.", + "install_uploads_off": "مدیر سیستم اجازه نصب افزونه را نمی‌دهد.", + "install_managed_id": "مدیر سیستم از قبل افزونه‌ای با این نام ارائه می‌دهد، بنابراین نمی‌توان آن را جایگزین کرد.", + "install_invalid": "این فایل افزونه‌ای قابل نصب نیست: {details}", + "toast_installed": "افزونه نصب شد. آن را در پایین روشن کنید.", + "toast_installed_pending": "افزونه نصب شد و برای تأیید به مدیر سیستم ارسال شد.", + "toast_enabled": "{name} روشن است", + "toast_disabled": "{name} خاموش است", + "toast_removed": "{name} حذف شد", + "toast_forced": "مدیر سیستم {name} را روشن نگه می‌دارد", + "toast_approval_requested": "{name} در انتظار تأیید مدیر سیستم است", + "empty_title": "هنوز افزونه‌ای وجود ندارد", + "empty_admin": "مدیر سیستم هیچ افزونه‌ای اضافه نکرده است.", + "empty_uploads": "برای شروع، یک افزونه را از فایل ZIP نصب کنید.", + "empty_demo": "افزونه‌ها به یک حساب واقعی نیاز دارند، بنابراین نسخه آزمایشی هیچ افزونه‌ای ندارد.", + "syncing": "در حال بررسی افزونه‌ها با سرور…", + "status_installed": "خاموش", + "status_enabled": "در حال راه‌اندازی", + "status_running": "روشن", + "status_disabled": "خاموش", + "status_error": "مشکل", + "badge_forced": "الزامی", + "badge_managed": "از طرف مدیر سیستم", + "badge_yours": "نصب‌شده توسط شما", + "badge_awaiting": "در انتظار تأیید", + "toggle_label": "روشن یا خاموش کردن {name}", + "forced_note": "مدیر سیستم این افزونه را الزامی کرده است، بنابراین نمی‌توان آن را خاموش کرد.", + "permissions": "چه کارهایی می‌تواند انجام دهد", + "permissions_managed": "چه کارهایی می‌تواند انجام دهد (تأییدشده توسط مدیر سیستم)", + "settings": "تنظیمات", + "remove": "حذف", + "remove_title": "{name} حذف شود؟", + "remove_message": "افزونه و هر چیزی که در این مرورگر ذخیره کرده است پاک می‌شود. بعداً می‌توانید دوباره آن را نصب کنید.", + "remove_confirm": "حذف", + "error_user_plugins_off": "مدیر سیستم افزونه‌هایی را که کاربران نصب می‌کنند خاموش کرده است، بنابراین این افزونه اجرا نمی‌شود.", + "error_approval_unavailable": "این افزونه پیش از الزامی شدن تأیید نصب شده است. آن را حذف و دوباره نصب کنید تا برای تأیید ارسال شود.", + "error_approval_denied": "مدیر سیستم این افزونه را تأیید نکرد.", + "error_approval_pending": "در انتظار تأیید این افزونه توسط مدیر سیستم.", + "error_auto_disabled": "به‌دلیل خطاهای پی‌درپی خاموش شد. برای تلاش دوباره آن را روشن کنید.", + "accounts_heading": "حساب‌های متصل", + "account_on": "{label} روی {host}", + "account_waiting": "{label} روی {host} (ورود کامل نشده)", + "disconnect": "قطع اتصال", + "disconnect_failed": "قطع اتصال {label} ممکن نشد" + }, + "consent": { + "title": "به {name} اجازه این کار داده شود؟", + "intro": "این افزونه مجوزهای زیر را درخواست می‌کند. فقط در صورتی آن‌ها را دریافت می‌کند که شما اجازه دهید.", + "high_risk_note": "موارد علامت‌دار به افزونه اجازه دسترسی به محتوای ایمیل‌های شما یا ارسال داده به جای دیگر را می‌دهند. فقط برای افزونه‌هایی که به آن‌ها اعتماد دارید اجازه دهید.", + "revoke_hint": "هر زمان می‌توانید افزونه را در تنظیمات خاموش یا حذف کنید.", + "deny": "اجازه نده", + "allow": "اجازه بده" + }, + "permissions": { + "none": "به هیچ مجوز خاصی نیاز ندارد.", + "high_risk": "نیازمند احتیاط", + "email_read": { + "title": "خواندن ایمیل‌های شما", + "body": "دیدن فرستندگان، گیرندگان، موضوع‌ها و متن کامل هر پیامی در حساب شما، و پوشه‌های شما." + }, + "email_write": { + "title": "تغییر و سازمان‌دهی ایمیل‌های شما", + "body": "افزودن یا حذف برچسب‌ها، پرچم‌ها و کلیدواژه‌های دیگر روی پیام‌های شما، انتقال آن‌ها بین پوشه‌های شما و به بایگانی، و ذخیره پیش‌نویس‌ها." + }, + "email_send": { + "title": "تغییر ایمیل پیش از ارسال", + "body": "خواندن و تغییر گیرندگان، موضوع، متن و پیوست‌های پیام‌هایی که می‌نویسید، و جلوگیری از ارسال یک پیام." + }, + "crypto_full": { + "title": "دسترسی کامل به این برنامه", + "body": "با همان دسترسی‌های خود ایمیل تحت وب اجرا می‌شود. می‌تواند همه ایمیل‌ها و کلیدهای ذخیره‌شده شما را بخواند و از طرف شما عمل کند. فقط برای افزونه‌هایی که کاملاً به آن‌ها اعتماد دارید اجازه دهید." + }, + "email_raw_send": { + "title": "ارسال پیام‌هایی که خودش ساخته است", + "body": "ارسال پیام‌های کامل، مثلاً امضاشده یا رمزنگاری‌شده، از حساب شما." + }, + "email_blob_read": { + "title": "خواندن محتوای خام پیام‌ها", + "body": "خواندن داده‌های اصلی پیام‌ها و پیوست‌های شما، و هر فایلی که پیش از ارسال پیوست می‌کنید." + }, + "email_blob_write": { + "title": "جایگزینی پیوست‌ها پیش از ارسال", + "body": "بارگذاری فایل در سرور ایمیل شما و جایگزینی فایلی که پیوست کرده‌اید با فایلی دیگر، پیش از ارسال پیام." + }, + "email_render_takeover": { + "title": "تغییر نحوه نمایش پیام‌ها", + "body": "جایگزینی متن پیام‌هایی که باز می‌کنید و فهرست پیام‌ها، مثلاً برای نمایش محتوای رمزگشایی‌شده." + }, + "calendar_read": { + "title": "خواندن تقویم شما", + "body": "دیدن تقویم‌ها و رویدادهای شما." + }, + "calendar_write": { + "title": "تغییر تقویم شما", + "body": "ایجاد، تغییر و حذف رویدادها." + }, + "contacts_read": { + "title": "خواندن مخاطبان شما", + "body": "جست‌وجو و خواندن دفترچه‌های آدرس شما." + }, + "contacts_write": { + "title": "تغییر مخاطبان شما", + "body": "ایجاد، تغییر و حذف مخاطبان و دفترچه‌های آدرس." + }, + "files_read": { + "title": "خواندن فایل‌های شما", + "body": "مرور و باز کردن فایل‌های ذخیره‌شده در حساب شما." + }, + "files_write": { + "title": "تغییر فایل‌های شما", + "body": "بارگذاری، تغییر نام، جابه‌جایی و حذف فایل‌ها در حساب شما." + }, + "identity_read": { + "title": "دیدن آدرس‌های فرستنده شما", + "body": "دیدن آدرس‌هایی که از آن‌ها ایمیل می‌فرستید و امضاهای آن‌ها." + }, + "identity_write": { + "title": "تغییر آدرس‌های فرستنده شما", + "body": "ایجاد، تغییر و حذف آدرس‌های فرستنده و امضاها." + }, + "filters_read": { + "title": "خواندن فیلترهای ایمیل شما", + "body": "دیدن قوانینی که ایمیل‌های ورودی شما را مرتب می‌کنند." + }, + "filters_write": { + "title": "تغییر فیلترهای ایمیل شما", + "body": "افزودن قانون به فیلترهای سمت سرور. این قوانین می‌توانند ایمیل‌های ورودی را جابه‌جا، هدایت یا حذف کنند." + }, + "tasks_read": { + "title": "خواندن کارهای شما", + "body": "دیدن فهرست‌های کار و کارهای شما." + }, + "tasks_write": { + "title": "تغییر کارهای شما", + "body": "ایجاد، تغییر، تکمیل و حذف کارها." + }, + "templates_read": { + "title": "خواندن قالب‌های شما", + "body": "دیدن قالب‌های پیام ذخیره‌شده شما." + }, + "templates_write": { + "title": "تغییر قالب‌های شما", + "body": "ایجاد، تغییر و حذف قالب‌های پیام." + }, + "smime_read": { + "title": "دیدن وضعیت S/MIME شما", + "body": "دیدن اینکه آیا کلیدها و گواهی‌های S/MIME دارید، بدون دسترسی به کلیدها." + }, + "vacation_read": { + "title": "دیدن پاسخ خودکار شما", + "body": "دیدن اینکه آیا پاسخ خودکار شما روشن است و چه متنی دارد." + }, + "vacation_write": { + "title": "تغییر پاسخ خودکار شما", + "body": "روشن یا خاموش کردن پاسخ خودکار و تغییر متن و تاریخ‌های آن." + }, + "settings_read": { + "title": "خواندن تنظیمات شما", + "body": "دیدن ترجیحات و تعریف برچسب‌های شما." + }, + "settings_write": { + "title": "تغییر تنظیمات شما", + "body": "افزودن و مرتب کردن برچسب‌ها و تغییر ترجیحات شما." + }, + "security_read": { + "title": "دیدن امنیت حساب شما", + "body": "دیدن اینکه آیا احراز هویت دو مرحله‌ای و رمزنگاری روشن هستند، بدون دسترسی به اطلاعات محرمانه." + }, + "auth_observe": { + "title": "باخبر شدن از ورود شما", + "body": "باخبر شدن وقتی وارد می‌شوید، حساب را عوض می‌کنید یا نشست شما تمدید می‌شود." + }, + "auth_emit": { + "title": "خارج کردن شما از حساب", + "body": "خارج کردن شما از ایمیل تحت وب." + }, + "account_read": { + "title": "دیدن حساب‌های شما", + "body": "دیدن حساب‌هایی که اضافه کرده‌اید، همراه با آدرس‌ها و سرورهای آن‌ها." + }, + "http_post": { + "title": "استفاده از سرور ایمیل تحت وب از طرف شما", + "body": "فراخوانی این نقاط پایانی سرور با اطلاعات ورود شما:" + }, + "http_fetch": { + "title": "اتصال به وب‌سایت‌های دیگر", + "body": "ارسال درخواست به این سایت‌ها. هر چیزی که افزونه بتواند بخواند ممکن است به آنجا ارسال شود:" + }, + "admin_config": { + "title": "خواندن تنظیمات خود از مدیر سیستم", + "body": "خواندن تنظیماتی که مدیر سیستم برای این افزونه ذخیره کرده است." + }, + "ui_observe": { + "title": "دنبال کردن جای شما در برنامه", + "body": "دانستن اینکه کدام صفحه باز است و واکنش به پیوندهایی که روی آن‌ها کلیک می‌کنید." + }, + "ui_toolbar": { + "title": "افزودن دکمه به نوارهای ابزار", + "body": "نمایش دکمه‌های خودش در نوار ابزار پیام‌ها، روی پیام باز و در نوار بالا." + }, + "ui_app_top_banner": { + "title": "نمایش بنر در بالا", + "body": "نمایش یک اطلاعیه در سراسر بالای برنامه." + }, + "ui_email_banner": { + "title": "نمایش اطلاعیه روی پیام‌ها", + "body": "نمایش اطلاعیه‌ها و خلاصه‌های خودش بالا، داخل و بین بخش‌های پیام یا مکالمه باز." + }, + "ui_email_footer": { + "title": "افزودن محتوا زیر پیام‌ها", + "body": "نمایش محتوای خودش زیر پیام باز." + }, + "ui_contact_cryptokeys": { + "title": "نمایش کلیدهای رمزنگاری مخاطبان", + "body": "نمایش و مدیریت کلیدهای رمزنگاری یک مخاطب." + }, + "ui_email_details": { + "title": "افزودن پنل به جزئیات پیام", + "body": "نمایش پنل خودش کنار یا زیر پیام باز." + }, + "ui_download_file": { + "title": "ذخیره فایل در دستگاه شما", + "body": "ارائه فایل‌هایی که خودش ساخته است برای دانلود." + }, + "ui_message_list_tabs": { + "title": "افزودن برگه به صندوق ورودی", + "body": "مرتب کردن صندوق ورودی شما در برگه‌هایی مانند به‌روزرسانی‌ها یا تبلیغات." + }, + "ui_composer_toolbar": { + "title": "افزودن به ویرایشگر", + "body": "نمایش دکمه‌ها، گزینه‌های منو و بخش‌های خودش هنگام نوشتن پیام." + }, + "ui_composer_sidebar": { + "title": "افزودن پنل به ویرایشگر", + "body": "نمایش پنل خودش کنار پیامی که می‌نویسید." + }, + "ui_sidebar_widget": { + "title": "افزودن به نوار کناری", + "body": "نمایش کادرها و بخش‌های خودش در نوار کناری ایمیل." + }, + "ui_settings_section": { + "title": "افزودن صفحه تنظیمات", + "body": "نمایش صفحه خودش در تنظیمات." + }, + "ui_context_menu": { + "title": "افزودن به منوی پیام", + "body": "افزودن گزینه به منویی که با کلیک راست روی پیام باز می‌شود." + }, + "ui_navigation_rail": { + "title": "افزودن به نوار ناوبری", + "body": "نمایش دکمه خودش در نوار ناوبری." + }, + "ui_keyboard": { + "title": "افزودن میانبرهای صفحه کلید", + "body": "واکنش به میانبرهای صفحه کلیدی که خودش تعریف می‌کند." + }, + "ui_calendar_action": { + "title": "افزودن به نمای تقویم", + "body": "نمایش دکمه‌ها و بخش‌های خودش روی رویدادها، و برچسب‌های کوتاه و نقطه‌ها روی روزهای تقویم." + }, + "ui_admin_page": { + "title": "افزودن صفحه مدیریت", + "body": "نمایش صفحه خودش در تنظیمات مدیریت." + }, + "app_lifecycle": { + "title": "باخبر شدن از شروع و توقف برنامه", + "body": "باخبر شدن وقتی برنامه شروع می‌شود، آفلاین می‌شود یا از حساب خارج می‌شوید." + }, + "compose_open": { + "title": "باز کردن ویرایشگر برای شما", + "body": "باز کردن پیامی تازه با گیرندگان، متن یا پیوست‌های ازپیش‌پرشده. تصمیم ارسال همچنان با شماست." + }, + "compose_read": { + "title": "خواندن پیامی که می‌نویسید", + "body": "خواندن متن پیام هنگام نوشتن آن، وقتی افزونه را در ویرایشگر به کار می‌برید. امضا و پیام نقل‌شده را نمی‌خواند." + }, + "compose_write": { + "title": "تغییر پیامی که می‌نویسید", + "body": "جایگزینی یا افزودن متن در پیام هنگام نوشتن آن، وقتی افزونه را در ویرایشگر به کار می‌برید. هر تغییر را می‌توانید برگردانید و تصمیم ارسال همچنان با شماست." + }, + "ui_notifications": { + "title": "نمایش اعلان‌ها", + "body": "نمایش اعلان روی دستگاه شما، یا داخل برنامه اگر اجازه اعلان نداده‌اید. هر اعلان نام افزونه را نشان می‌دهد." + }, + "ui_message_list": { + "title": "افزودن به فهرست پیام‌ها", + "body": "نمایش محتوای خودش بالای فهرست پیام‌ها، و دکمه‌ها روی پیام‌ها و در نوار ابزار پیام‌های انتخاب‌شده." + }, + "ui_contacts": { + "title": "افزودن به نمای مخاطبین", + "body": "نمایش بخش‌های خودش در جزئیات مخاطب و دکمه‌ها در فهرست مخاطبین." + }, + "ui_tasks": { + "title": "افزودن به نمای وظایف", + "body": "نمایش بخش‌های خودش در جزئیات وظیفه و دکمه‌ها در فهرست وظایف." + }, + "ui_files": { + "title": "افزودن به نمای فایل‌ها", + "body": "نمایش بخش‌های خودش در پیش‌نمایش فایل و گزینه‌ها در منوی فایل." + }, + "ui_commands": { + "title": "افزودن فرمان", + "body": "افزودن گزینه‌های خودش به پالت فرمان (Ctrl+K)." + }, + "ui_app_page": { + "title": "افزودن صفحه", + "body": "افزودن صفحه‌های خودش به برنامه، هر کدام با یک دکمه در نوار ناوبری." + }, + "http_proxy": { + "title": "استفاده از حساب‌هایی که متصل می‌کنید", + "body": "ارسال درخواست از طریق سرور Bulwark با حساب یا کلیدی که شما یا مدیرتان راه‌اندازی کرده‌اید. افزونه هرگز گذرواژه را نمی‌بیند، اما می‌تواند هر کاری را که آن حساب روی سرور خودش می‌تواند انجام دهد، انجام دهد." + } + }, + "dialog": { + "from_plugin": "از افزونه {name}", + "ok": "تأیید", + "submit": "ارسال", + "confirm": "تأیید", + "cancel": "لغو", + "close": "بستن", + "turn_off": "خاموش کردن {name}", + "plugin_menu": "گزینه‌های {name}", + "password_note": "این فرم از افزونه {name} است، نه از وب‌میل. فقط اگر به این افزونه اعتماد دارید گذرواژه وارد کنید.", + "toast_auto_disabled": "{name} به‌دلیل خطاهای پی‌درپی خاموش شد." + }, + "connect": { + "title": "اتصال {label}", + "intro_password": "{name} می‌خواهد از حساب {label} شما استفاده کند. Bulwark گذرواژه را روی سرور خودش نگه می‌دارد و فقط آن را به {host} می‌فرستد. افزونه هرگز آن را نمی‌بیند.", + "intro_password_any": "{name} می‌خواهد از حساب {label} شما استفاده کند. Bulwark گذرواژه را روی سرور خودش نگه می‌دارد و فقط آن را به سروری که اینجا وارد می‌کنید می‌فرستد. افزونه هرگز آن را نمی‌بیند.", + "intro_signin": "{name} می‌خواهد به حساب {label} شما وارد شود. سرور را اینجا وارد کنید، سپس در صفحهٔ خود آن سرور وارد شوید. Bulwark دسترسی‌ای را که می‌گیرد روی سرور خودش نگه می‌دارد؛ افزونه هرگز آن را نمی‌بیند.", + "server": "آدرس سرور", + "server_hint": "cloud.example.com", + "username": "نام کاربری", + "secret": "گذرواژه یا کلید", + "connect": "اتصال", + "continue": "ادامه", + "cancel": "انصراف", + "saving": "در حال ذخیره…", + "error_server": "آدرس سرور را وارد کنید، برای نمونه https://cloud.example.com", + "error_save": "ذخیره نشد: {error}" + } + }, + "global_search": { + "title": "جستجو در همه جا", + "placeholder": "جستجو در ایمیل، مخاطبین، تقویم، فایل‌ها...", + "scope_label": "جستجو در", + "account_label": "حساب", + "scope_all": "همه", + "scope_mail": "ایمیل", + "scope_contacts": "مخاطبین", + "scope_calendar": "تقویم", + "scope_files": "فایل‌ها", + "all_accounts": "همه حساب‌ها", + "show_all": "نمایش همه نتایج", + "no_results": "نتیجه‌ای برای “{query}” یافت نشد", + "server_hint": "کلمات کامل در سرور جستجو می‌شوند؛ بخشی از کلمات با موارد بارگذاری‌شده مطابقت دارد", + "searching": "در حال جستجو...", + "account_error": "جستجو در {account} ممکن نشد", + "recent_searches": "جستجوهای اخیر", + "recurring": "تکرارشونده", + "no_subject": "(no subject)", + "empty_hint": "در ایمیل، مخاطبین، رویدادها و فایل‌های همه حساب‌ها جستجو کنید. امتحان کنید: from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "تخت بر اساس تاریخ", + "grouped": "گروه‌بندی‌شده", + "load_more": "بارگذاری بیشتر", + "preview_empty": "برای پیش‌نمایش یک نتیجه انتخاب کنید", + "open_full": "باز کردن", + "view_label": "نما" + }, + "top_bar": { + "more": "گزینه‌های بیشتر" + }, + "plugin_pages": { + "loading": "در حال بارگذاری صفحه", + "not_available_title": "این صفحه در دسترس نیست", + "not_available_description": "افزونه‌ای که آن را اضافه کرده خاموش است یا دیگر نصب نیست.", + "back_to_mail": "بازگشت به ایمیل", + "settings_group": "افزونه‌ها" + }, + "command_palette": { + "title": "پالت فرمان", + "open": "باز کردن پالت فرمان", + "placeholder": "یک فرمان بنویسید", + "no_results": "فرمان منطبقی پیدا نشد", + "go_to": "رفتن به {name}", + "compose": "پیام جدید", + "search": "جستجو در ایمیل", + "theme_dark": "رفتن به پوسته تیره", + "theme_light": "رفتن به پوسته روشن", + "shortcuts": "نمایش میانبرهای صفحه کلید", + "group_navigation": "رفتن به", + "group_actions": "کارها", + "group_plugins": "افزونه‌ها" + }, + "notification_center": { + "title": "اعلان‌ها", + "mark_all_read": "علامت‌گذاری همه به‌عنوان خوانده‌شده", + "clear": "پاک کردن", + "empty": "اعلانی نیست", + "close": "بستن", + "unread": "خوانده‌نشده", + "unread_count": "{count} خوانده‌نشده", + "level_error": "خطا", + "level_warning": "هشدار" + } +} diff --git a/locales/fr/common.json b/locales/fr/common.json index 03a643c4..b672cd3a 100644 --- a/locales/fr/common.json +++ b/locales/fr/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Client de messagerie web minimaliste utilisant le protocole JMAP", - "login": { - "title": "Webmail", - "username_label": "Email", - "username_placeholder": "utilisateur@exemple.com", - "password_label": "Mot de passe", - "password_placeholder": "Entrez votre mot de passe", - "jmap_endpoint_label": "Serveur JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Le serveur doit autoriser les requêtes CORS depuis ce domaine.", - "jmap_server_label": "Serveur", - "jmap_server_auto_picked": "Serveur sélectionné à partir de votre domaine de messagerie.", - "sign_in": "Se connecter", - "signing_in": "Connexion en cours...", - "loading": "Chargement...", - "reconnecting": "Connexion perdue. Tentative de reconnexion…", - "error": { - "invalid_credentials": "Email ou mot de passe invalide", - "connection_failed": "Échec de la connexion au serveur", - "cors_blocked": "Le serveur est joignable mais bloque les requêtes cross-origin. Vérifiez la configuration CORS de votre serveur JMAP et autorisez ce domaine.", - "server_error": "Le serveur est temporairement indisponible. Veuillez réessayer plus tard.", - "generic": "Une erreur s'est produite. Veuillez réessayer.", - "totp_required": "Un code d'authentification à deux facteurs est requis. Veuillez saisir votre code ci-dessous.", - "totp_invalid": "Code d'authentification invalide. Vérifiez votre application d'authentification.", - "oauth_discovery_failed": "Le SSO est activé mais le fournisseur d'identité est injoignable. Vérifiez votre configuration OAuth." - }, - "show_password": "Afficher le mot de passe", - "hide_password": "Masquer le mot de passe", - "totp_toggle": "J'ai un code 2FA", - "remember_me": "Se souvenir de moi", - "config_error": { - "title": "Erreur de configuration", - "fetch_failed": "Impossible de charger la configuration de l'application. Veuillez réessayer plus tard.", - "server_not_configured": "Le serveur de messagerie n'a pas été configuré. Veuillez contacter votre administrateur." - }, - "remove_from_history": "Supprimer de l'historique", - "totp_label": "Code d'authentification", - "totp_placeholder": "000000", - "session_expired": "Votre session a expiré. Veuillez vous reconnecter.", - "dismiss": "Fermer", - "or": "ou", - "sign_in_sso": "Se connecter avec SSO", - "add_account_title": "Ajouter un compte", - "add_account_subtitle": "Se connecter avec un autre compte", - "cancel": "Annuler", - "website": "Site web", - "imprint": "Mentions légales", - "privacy_policy": "Politique de confidentialité", - "try_demo": "Essayer la démo", - "demo_description": "Explorez avec des données d'exemple - aucun compte nécessaire", - "demo_launching": "Lancement de la démo...", - "demo_login_button": "Lancer la démo", - "demo_tagline": "Découvrez un client de messagerie complet. Aucun compte requis.", - "demo_no_signup": "Aucune inscription - explorez librement avec des données d'exemple", - "oauth_completing": "Connexion en cours...", - "oauth_error": { - "title": "Échec de l'authentification", - "invalid_state": "La validation de sécurité a échoué. Veuillez réessayer.", - "missing_params": "Données d'autorisation manquantes. Veuillez réessayer.", - "token_exchange_failed": "Impossible de finaliser l'authentification. Veuillez réessayer.", - "access_denied": "L'accès a été refusé. Veuillez contacter votre administrateur.", - "back_to_login": "Retour à la connexion" - } - }, - "sidebar": { - "close": "Fermer", - "compose": "Composer", - "compose_hint": "Composer (c)", - "search_placeholder": "Rechercher un email...", - "search_placeholder_hint": "Rechercher... (appuyez sur /)", - "storage": "Stockage", - "storage_used": "Utilisé", - "storage_free": "Libre", - "storage_total": "Total", - "sign_out": "Se déconnecter", - "sign_out_of": "Se déconnecter de {account}", - "sign_out_all": "Se déconnecter de tous les comptes", - "add_account": "Ajouter un compte", - "set_as_default": "Définir par défaut", - "switch_account": "Changer de compte", - "contacts": "Contacts", - "calendar": "Calendrier", - "settings": "Paramètres", - "admin": "Admin", - "files": "Fichiers", - "loading_mailboxes": "Chargement des boîtes mail...", - "push_connected": "Mises à jour en temps réel actives", - "push_disconnected": "Mises à jour en temps réel inactives", - "keyboard_shortcuts": "Raccourcis clavier", - "theme": { - "light": "Mode clair", - "dark": "Mode sombre", - "system": "Thème système" - }, - "language": { - "title": "Langue" - }, - "mailboxes": { - "inbox": "Boîte de réception", - "sent": "Envoyés", - "drafts": "Brouillons", - "trash": "Corbeille", - "archive": "Archives", - "starred": "Favoris", - "all_mail": "Tous les messages", - "spam": "Spam", - "important": "Important" - }, - "unified_inbox": "Boîte de réception unifiée", - "unified_sent": "Tous les envoyés", - "unified_drafts": "Tous les brouillons", - "unified_trash": "Toutes les corbeilles", - "unified_archive": "Toutes les archives", - "unified_junk": "Tous les indésirables", - "all_accounts": "Tous les comptes", - "unified_mailbox": "Boîte aux lettres unifiée", - "expand": "Développer", - "collapse": "Réduire", - "expand_tooltip": "Développer", - "collapse_tooltip": "Réduire", - "mobile": { - "search": "Rechercher", - "compose": "Composer", - "go_back": "Retour", - "toggle_menu": "Afficher/masquer le menu" - }, - "clear_search": "Effacer la recherche", - "vacation_active": "Répondeur d'absence activé", - "demo_banner": "Mode démo", - "demo_reset": "Réinitialiser", - "demo_tour": "Visite", - "tags": "Étiquettes", - "show_all_tags": "Tout afficher ({count})", - "show_fewer_tags": "Afficher moins", - "folders": "Dossiers", - "mail": "Messagerie", - "nav_label": "Navigation", - "add_app": "Apps", - "shared": "Partagé", - "scheduled": "Planifiés", - "unified_all_unread": "Tous les non lus", - "unified_all_starred": "Tous les favoris", - "unified_all_mail": "Tout le courrier", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Applications par défaut", - "description": "Choisissez si les liens d'e-mail et de calendrier s'ouvrent dans Bulwark. Techniquement, Bulwark s'enregistre comme gestionnaire de protocole pour les liens mailto: et webcal:.", - "unsupported": "Ce navigateur ou cette connexion ne prend pas en charge l'enregistrement manuel des gestionnaires de protocole. Vous pourrez peut-être quand même utiliser la PWA installée via les paramètres du navigateur ou du système.", - "mailto_label": "Liens e-mail", - "mailto_description": "Ouvre les liens mailto: dans Bulwark avec un éditeur prérempli.", - "protocol_open_mode_label": "À l’ouverture des liens de protocole", - "protocol_open_mode_description": "Choisissez si Bulwark ouvre les liens mailto: et webcal: dans un nouvel onglet ou réutilise une session ouverte. L’option de session active nécessite l’autorisation des notifications afin que vous puissiez cliquer sur une notification de secours pour ramener Bulwark au premier plan si le navigateur bloque le focus.", - "protocol_open_mode_active_session": "Ouvrir dans la session active si possible", - "protocol_open_mode_new_tab": "Toujours ouvrir un nouvel onglet", - "focus_notification_title": "Ouvrir Bulwark", - "focus_notification_body": "Le lien a été ouvert dans Bulwark. Cliquez pour ramener la fenêtre au premier plan.", - "webcal_label": "Liens de calendrier", - "webcal_description": "Ouvre les liens webcal: dans Bulwark avec une boîte de dialogue d'abonnement au calendrier préremplie.", - "register_mailto": "Enregistrer l'application e-mail", - "register_webcal": "Enregistrer l'application de calendrier", - "mailto_registered": "Enregistrement du gestionnaire d'e-mail demandé", - "webcal_registered": "Enregistrement du gestionnaire de calendrier demandé", - "registration_failed": "Échec de l'enregistrement du gestionnaire de protocole", - "opening_mailto": "Ouverture de l'éditeur...", - "opening_webcal": "Ouverture du calendrier...", - "browser_note": "Votre navigateur ou système d'exploitation peut vous demander de confirmer et peut exiger que Bulwark soit installé avant de pouvoir le sélectionner comme application par défaut.", - "select_account_title": "Choisir un compte", - "select_mailto_account": "Choisissez le compte qui doit ouvrir ce lien e-mail.", - "select_webcal_account": "Choisissez le compte qui doit ouvrir ce lien de calendrier.", - "select_account_note": "Cette sélection s'applique uniquement à ce lien de protocole.", - "detail_to": "À", - "detail_subject": "Objet", - "detail_no_subject": "Sans objet", - "detail_calendar": "Calendrier", - "detail_source": "Source", - "active_account": "Actif", - "switching_account": "Changement de compte..." - }, - "sidebar_apps": { - "modal_title": "Applications de la barre latérale", - "add_new": "Ajouter une application", - "edit_app": "Modifier l'application", - "name_label": "Nom", - "name_placeholder": "Mon application", - "name_required": "Le nom est requis", - "url_label": "URL", - "url_required": "L'URL est requise", - "url_invalid": "Entrez une URL http ou https valide", - "icon_label": "Icône", - "icon_required": "L'icône est requise", - "open_mode_label": "Mode d'ouverture", - "open_new_tab": "Nouvel onglet", - "open_inline": "Intégré", - "cancel": "Annuler", - "add": "Ajouter", - "update": "Mettre à jour", - "delete": "Supprimer", - "delete_confirm_title": "Supprimer l'application", - "delete_confirm": "Êtes-vous sûr de vouloir supprimer \"{name}\" ?", - "no_apps": "Aucune application ajoutée", - "no_apps_hint": "Ajoutez des applications et liens personnalisés à votre barre latérale", - "managed_title": "Fournies par votre administrateur", - "managed_description": "Ces applications sont configurées pour tout le monde et ne peuvent pas être modifiées.", - "managed_badge": "Gérée", - "search_icons": "Rechercher des icônes...", - "show_popular": "Populaires", - "show_all": "Toutes", - "no_icons_found": "Aucune icône trouvée", - "inline_badge": "Intégré", - "tab_badge": "Onglet", - "show_on_mobile": "Afficher sur mobile" - }, - "email_list": { - "no_emails": "Aucun message trouvé", - "no_emails_description": "Cette boîte est vide", - "no_search_results": "Aucun résultat trouvé", - "no_search_results_description": "Essayez de modifier votre recherche ou vos filtres", - "loading": "Chargement des emails...", - "unread": "non lu", - "to_me": "À moi", - "to_recipients": "À {count} destinataires", - "and_others": "et {count} autres", - "draft": "Brouillon", - "starred": "Favori", - "pinned": "Épinglé", - "replied": "Répondu", - "forwarded": "Transféré", - "has_attachment": "Avec pièce jointe", - "conversations_count": "{count} sur {total} conversations", - "conversations_count_plus": "{count}+ conversations", - "conversations_count_simple": "{count} conversations", - "no_conversations": "Aucune conversation", - "loading_more": "Chargement d'autres emails...", - "no_more_emails": "Plus d'emails à charger", - "batch_actions": { - "select": "Sélectionner les e-mails", - "select_all": "Tout sélectionner", - "selected_messages": "{count, plural, one {1 e-mail sélectionné} other {# e-mails sélectionnés}}", - "mark_read": "Marquer comme lu", - "mark_unread": "Marquer comme non lu", - "delete": "Supprimer", - "delete_confirm_title": "Supprimer les e-mails", - "delete_confirm_message": "Voulez-vous vraiment supprimer {count, plural, one {1 e-mail} other {# e-mails}} ?", - "clear_selection": "Effacer la sélection" - }, - "permanent_delete": "Supprimer définitivement", - "permanent_delete_confirm_title": "Supprimer définitivement", - "permanent_delete_confirm_message": "Cet e-mail sera définitivement supprimé. Cette action est irréversible.", - "permanent_delete_confirm_batch_message": "Ces {count, plural, one {1 e-mail} other {# e-mails}} seront définitivement supprimés. Cette action est irréversible.", - "empty_folder": { - "button": "Vider le dossier", - "confirm_title": "Vider le dossier", - "confirm_message": "Tous les e-mails de ce dossier seront définitivement supprimés. Cette action est irréversible.", - "confirm_button": "Vider le dossier", - "junk_hint": "Vous pouvez vider le dossier Indésirables pour supprimer définitivement tous les messages.", - "trash_hint": "Vous pouvez vider la corbeille pour supprimer définitivement tous les messages." - }, - "no_scheduled_emails": "Aucun e-mail planifié", - "no_scheduled_emails_description": "Les messages planifiés pour plus tard apparaîtront ici.", - "scheduled_count": "{count, plural, one {1 e-mail planifié} other {# e-mails planifiés}}", - "scheduled_actions_hint": "Les messages planifiés peuvent être annulés, replanifiés ou modifiés depuis leurs actions.", - "cancel_scheduled_send": "Annuler l’envoi", - "reschedule_send": "Replanifier", - "cancel_and_edit": "Annuler et modifier", - "cancel_and_compose_again": "Annuler et rédiger à nouveau", - "reschedule_prompt": "Saisissez une nouvelle date/heure, p. ex. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "L''expéditeur demande un accusé de lecture :", - "send": "Envoyer l''accusé", - "ignore": "Ignorer", - "sent": "Accusé de lecture envoyé.", - "send_failed": "Impossible d''envoyer l''accusé de lecture", - "mdn_subject": "Lu : {subject}", - "mdn_body": "Ceci est un accusé de réception du message que vous avez envoyé à {recipient}.\n\nRemarque : cet accusé confirme uniquement que le message a été affiché sur l''ordinateur du destinataire. Il ne garantit pas que le destinataire a lu ou compris le contenu." - }, - "no_email_selected": "Aucun email sélectionné", - "no_email_description": "Sélectionnez un email dans la liste pour le voir ici", - "no_conversation_selected": "Aucune conversation sélectionnée", - "no_conversation_description": "Choisissez une conversation dans la liste pour la lire ici", - "compose": "Rédiger", - "compose_hint": "Rédiger un nouveau message", - "no_subject": "(Sans objet)", - "no_body_content": "(Aucun contenu disponible)", - "no_preview_available": "Aucun aperçu disponible", - "show_quoted_text": "Afficher le texte cité", - "hide_quoted_text": "Masquer le texte cité", - "loading_email": "Chargement de l'email...", - "loading": "Chargement...", - "reply": "Répondre", - "reply_all": "Répondre à tous", - "forward": "Transférer", - "delete": "Supprimer", - "archive": "Archiver", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Marquer comme favori", - "unstar": "Retirer des favoris", - "mark_unread": "Marquer comme non lu", - "mark_read": "Marquer comme lu", - "unread": "Non lu", - "read": "Lu", - "spam_short": "Spam", - "not_spam_short": "Non spam", - "move": "Déplacer", - "print": "Imprimer", - "view_source": "Voir la source", - "fullscreen": "Plein écran", - "exit_fullscreen": "Quitter le plein écran", - "export_email": "Exporter en .eml", - "forward_as_attachment": "Transférer en pièce jointe", - "import_email": "Importer .eml ou .zip", - "keyboard_shortcuts": "Raccourcis clavier (?)", - "email_source": "Source de l'email", - "draft_banner": "Ce message est un brouillon", - "edit_draft": "Modifier", - "copy_source": "Copier dans le presse-papiers", - "source_copied": "Source copiée dans le presse-papiers", - "attachments": "Pièces jointes", - "important": "Important", - "download": "Télécharger", - "download_all": "Tout télécharger", - "from": "De", - "to": "À", - "cc": "CC", - "bcc": "CCI", - "date": "Date", - "subject": "Objet", - "show_details": "Afficher les détails", - "hide_details": "Masquer les détails", - "external_content_warning": "Les images et le contenu externe ont été bloqués", - "load_external_content": "Charger les images", - "trust_sender": "Toujours faire confiance à cet expéditeur", - "back_to_list": "Retour à la liste", - "view_contact": "Voir le contact", - "message_details": "Détails du message", - "more_reply_options": "Plus d'options de réponse", - "set_tag": "Définir l'étiquette", - "tag": "Étiquette", - "more_actions": "Plus d'actions", - "move_to": "Déplacer vers...", - "remove_tag": "Retirer l'étiquette", - "tag_filter_placeholder": "Filtrer les étiquettes", - "tag_no_matches": "Aucune étiquette correspondante", - "more_count": "+{count} de plus", - "characters_count": "{count} caractères", - "quick_reply_placeholder": "Écrivez une réponse rapide...", - "more_options": "Plus d'options", - "sending": "Envoi en cours...", - "security_authentication": "Sécurité et authentification", - "technical_details": "Détails techniques", - "message_id_label": "ID du message :", - "reply_to_label": "Répondre à :", - "delivery_time_label": "Temps de livraison :", - "conversation_part_label": "Partie de la conversation :", - "previous_messages": "{count} message précédent", - "previous_messages_plural": "{count} messages précédents", - "time": { - "day": "jour", - "days": "jours", - "hour": "heure", - "hours": "heures", - "minute": "minute", - "minutes": "minutes" - }, - "unknown_sender": "Inconnu", - "recipient_me": "moi", - "recipient_and_others": "{name} et {count} autres", - "recipient_to_prefix": "À :", - "authentication": { - "title": "Authentification", - "status": { - "verified": "Vérifié", - "warning": "Attention", - "none": "Non authentifié" - }, - "spf": { - "pass": "SPF Validé", - "fail": "SPF Échoué", - "none": "Pas de SPF" - }, - "dkim": { - "pass": "DKIM Valide", - "fail": "DKIM Invalide", - "none": "Pas de DKIM" - }, - "dmarc": { - "pass": "DMARC Validé", - "fail": "DMARC Échoué", - "none": "Pas de DMARC" - }, - "spam_score": "Score de spam", - "tooltip_spf": "Sender Policy Framework : vérifie que le serveur d'envoi est autorisé à envoyer des e-mails au nom du domaine", - "tooltip_dkim": "DomainKeys Identified Mail : confirme que l'e-mail n'a pas été modifié en transit grâce à une signature cryptographique", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance : garantit l'alignement de SPF et DKIM avec le domaine de l'expéditeur et définit une politique en cas d'échec", - "policy": "Politique", - "result": { - "pass": "Validé", - "fail": "Échoué", - "softfail": "Échec partiel", - "neutral": "Neutre", - "permerror": "Erreur permanente", - "temperror": "Erreur temporaire", - "none": "Aucun" - } - }, - "headers": { - "routing": "Routage", - "received": "Reçu", - "message_id": "ID du message", - "list_info": "Information de liste" - }, - "tooltips": { - "reply": "Répondre", - "reply_all": "Répondre à tous (a)", - "forward": "Transférer (f)", - "archive": "Archiver", - "delete": "Supprimer", - "star": "Suivre (s)", - "unstar": "Ne plus suivre (s)", - "compose": "Rédiger (c)", - "previous": "E-mail précédent", - "next": "E-mail suivant", - "edit_draft": "Modifier le brouillon" - }, - "spam": { - "button_title": "Signaler comme spam", - "not_spam_title": "Marquer comme légitime", - "toast_success": "Déplacé vers Indésirables", - "toast_batch": "{count} e-mails déplacés vers Indésirables", - "toast_undo": "Annuler", - "toast_not_spam_success": "Déplacé vers Boîte de réception", - "toast_not_spam_batch": "{count} e-mails déplacés vers Boîte de réception", - "error": "Échec du signalement spam", - "error_not_spam": "Échec de la restauration" - }, - "unsubscribe_banner": { - "label": "Newsletter", - "button": "Se désabonner", - "confirm_title": "Se désabonner de cet expéditeur ?", - "confirm_button": "Confirmer", - "cancel": "Annuler", - "success_http": "Page de désabonnement ouverte dans un nouvel onglet", - "success_mailto": "E-mail de désabonnement envoyé", - "confirm_message_http": "La page de désabonnement s'ouvrira dans un nouvel onglet.", - "confirm_message_mailto": "Un e-mail de désabonnement sera envoyé à l'expéditeur.", - "error": "Impossible de se désabonner", - "dismiss": "Ignorer" - }, - "calendar_invitation": { - "loading": "Chargement des détails…", - "title": "Invitation calendrier", - "published_title": "Événement publié", - "response_title": "Réponse à l'événement", - "update_title": "Mise à jour de l'événement", - "counter_title": "Contre-proposition", - "refresh_title": "Demande d'actualisation", - "declined_counter_title": "Contre-proposition refusée", - "cancelled_title": "Événement annulé", - "organizer": "Organisé par {name}", - "organizer_label": "Organisé par", - "attendees": "{count, plural, one {# participant} other {# participants}}", - "accept": "Accepter", - "maybe": "Peut-être", - "decline": "Refuser", - "add_to_calendar": "Ajouter au calendrier", - "added": "Ajouté au calendrier", - "rsvp_sent": "Réponse envoyée", - "parse_error": "Impossible de lire l'invitation", - "action_failed": "Impossible d'effectuer cette action de calendrier.", - "no_calendar": "Calendrier non disponible", - "published_info": "Cet événement a été partagé à titre informatif.", - "response_info": "Ce message contient la réponse d'un participant.", - "response_info_organizer": "Cette réponse met à jour votre événement.", - "update_info": "Ce message met à jour un événement existant.", - "counter_info": "Ce message propose des modifications à un événement.", - "counter_info_organizer": "Ce participant a proposé des modifications à votre événement.", - "refresh_info": "Ce message demande les derniers détails de l'événement.", - "refresh_info_organizer": "Un participant a demandé les derniers détails de l'événement.", - "declined_counter_info": "L'organisateur a refusé une contre-proposition.", - "authentication_failed_info": "Les contrôles d'authentification de cette invitation ont échoué. Traitez les actions de calendrier avec prudence.", - "authentication_missing_info": "Cette invitation ne comporte pas d'authentification de messagerie vérifiée. Vérifiez les détails avec l'organisateur si quelque chose semble inhabituel.", - "sender_mismatch_info": "Cette invitation a été envoyée depuis {sender}, alors que l'organisateur indiqué dans les données du calendrier est {organizer}.", - "sender_mismatch_unverified_info": "Cette invitation a été envoyée depuis {sender}, alors que l'organisateur indiqué dans les données du calendrier est {organizer}, et le message n'a pas pu être vérifié.", - "organizer_role": "Vous organisez cet événement", - "your_response": "Votre réponse : {status}", - "response_needed": "Réponse requise", - "response_accepted": "Acceptée", - "response_tentative": "Provisoire", - "response_declined": "Refusée", - "response_delegated": "Déléguée", - "actor_sent_info": "Envoyé par {name}.", - "actor_response_info": "{name} a répondu {status}.", - "actor_counter_info": "{name} a proposé des modifications à cet événement.", - "actor_refresh_info": "{name} a demandé les derniers détails de l'événement.", - "actor_declined_counter_info": "{name} a refusé la contre-proposition.", - "actor_note": "Note : {comment}", - "actor_unknown": "Quelqu'un", - "proposed_changes": "Modifications proposées", - "change_title": "Titre", - "change_time": "Horaire", - "change_location": "Lieu", - "change_description": "Description", - "change_empty": "Aucune", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Appliquer les modifications proposées", - "proposal_applied": "Les modifications proposées ont été appliquées.", - "review_proposal": "Examiner la proposition", - "review_request": "Examiner la demande", - "view_in_calendar": "Voir dans le calendrier", - "select_calendar": "Choisir un calendrier", - "already_in_calendar": "Déjà dans votre calendrier", - "request_info": "Vous êtes invité à cet événement. Répondez pour informer l'organisateur de votre disponibilité.", - "cancel_info": "L'organisateur a annulé cet événement.", - "event_updated": "Mise à jour #{sequence}", - "event_status_tentative": "Provisoire", - "event_status_cancelled": "Annulé", - "collapse": "Masquer les détails", - "expand": "Afficher les détails" - }, - "previous": "Précédent", - "next": "Suivant", - "send": "Envoyer", - "more": "plus", - "details": { - "recipients_routing": "Destinataires & routage", - "authentication_security": "Authentification & sécurité", - "identifiers_threading": "Identifiants & fil de discussion", - "mailing_list": "Liste de diffusion", - "message_properties": "Propriétés du message", - "sent": "Envoyé", - "received": "Reçu", - "delivery_time": "Délai de livraison", - "in_reply_to": "En-réponse-à", - "references": "Références", - "thread_id": "ID de fil", - "size": "Taille", - "mime_type": "Type MIME", - "attachments_summary": "{count} fichiers · {size}", - "list_id": "ID de liste", - "list_help": "Aide de liste", - "list_post": "Publication de liste", - "list_unsubscribe": "Se désinscrire", - "iprev": "DNS inversé", - "spam_status": "Statut spam", - "ai_verdict": "Verdict IA", - "account": "Compte", - "no_subject": "(sans objet)" - }, - "scheduled_banner": "Envoi planifié à {date}", - "scheduled_send_created": "E-mail planifié pour envoi", - "cancel_scheduled_send": "Annuler l’envoi", - "reschedule_send": "Replanifier", - "cancel_and_edit": "Annuler et modifier", - "cancel_and_compose_again": "Annuler et rédiger à nouveau", - "reschedule_prompt": "Saisissez une nouvelle date/heure, p. ex. 2026-05-04T15:30", - "scheduled_actions_only": "La vue Planifiés prend uniquement en charge les actions de planification", - "undo_send_scheduled": "Message planifié pour envoi", - "undo_send": "Annuler l’envoi", - "contact_sidebar": { - "title": "Contact", - "close": "Fermer le panneau", - "action_email": "E-mail", - "action_email_title": "Envoyer un e-mail", - "action_copy": "Copier", - "action_copy_title": "Copier l'adresse e-mail", - "action_edit_title": "Modifier le contact", - "section_emails": "E-mails", - "section_phones": "Téléphones", - "section_organizations": "Organisations", - "section_addresses": "Adresses", - "section_notes": "Notes", - "not_in_contacts": "Pas dans vos contacts", - "add_to_contacts": "Ajouter aux contacts", - "copied": "Copié !", - "copy_failed": "Échec de la copie" - }, - "send_now": "Envoyer maintenant" - }, - "email_composer": { - "read_receipt_on": "Accusé de lecture demandé (cliquez pour désactiver)", - "read_receipt_off": "Demander un accusé de lecture", - "dsn_on": "Notification de remise demandée (cliquer pour désactiver)", - "dsn_off": "Demander une notification de remise", - "format_plain_text": "Passer en texte brut", - "format_rich_text": "Passer en texte enrichi (HTML)", - "require_tls_on": "Remise chiffrée exigée (cliquer pour désactiver)", - "require_tls_off": "Exiger une remise chiffrée (TLS)", - "new_message": "Nouveau message", - "signature_edit_hint": "Double-cliquez pour modifier la signature", - "reply": "Répondre", - "reply_all": "Répondre à tous", - "forward": "Transférer", - "reply_to": "Répondre", - "reply_all_to": "Répondre à tous", - "forward_message": "Transférer", - "from": "De", - "to": "À", - "cc": "CC", - "bcc": "CCI", - "subject": "Objet", - "body_placeholder": "Écrivez votre message...", - "send": "Envoyer", - "cancel": "Annuler", - "attach": "Joindre", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Supprimer", - "discard_draft_title": "Supprimer le brouillon ?", - "discard_draft_confirm": "Vous avez des modifications non enregistrées. Voulez-vous supprimer ce brouillon ?", - "saving": "Enregistrement...", - "draft_saved": "Brouillon enregistré", - "save_failed": "Échec de l'enregistrement", - "to_placeholder": "Adresses email des destinataires", - "cc_placeholder": "Destinataires en copie", - "bcc_placeholder": "Destinataires en copie cachée", - "subject_placeholder": "Objet", - "cc_label": "Cc :", - "bcc_label": "Cci :", - "subject_label": "Objet :", - "file_size_kb": "Ko", - "prefix": { - "forward": "Tr:", - "reply": "Re:" - }, - "no_subject": "(Sans objet)", - "unknown_sender": "Inconnu", - "quote": { - "reply_header": "Le {date}, {sender} a écrit :", - "forward_header": "---------- Message transféré ----------", - "from": "De : {sender}", - "date": "Date : {date}", - "subject": "Objet : {subject}", - "to": "À : {recipients}" - }, - "remove_sub_address": "Retirer le sous-adressage", - "from_override": { - "toggle_off": "Remplacer", - "toggle_on": "Annuler le remplacement", - "toggle_tooltip": "Modifiez librement le nom et l'adresse d'expéditeur. Le courrier est toujours envoyé via votre identité.", - "name_label": "Nom de l'expéditeur", - "name_placeholder": "Nom", - "email_label": "Adresse e-mail de l'expéditeur", - "email_placeholder": "alias@example.com", - "envelope_notice": "Si votre serveur n'accepte pas cette adresse comme expéditeur d'enveloppe, {identity} est utilisée à la place, et les destinataires peuvent la voir dans l'en-tête Return-Path." - }, - "use_template": "Modèle", - "save_as_template": "Enregistrer comme modèle", - "validation": { - "recipient_required": "Ajoutez un destinataire pour envoyer", - "body_required": "Rédigez un message ou joignez un fichier", - "attachments_uploading": "Les pièces jointes sont en cours d'envoi — le message partira une fois terminé", - "attachment_upload_failed": "Non envoyé - le téléversement d'une pièce jointe a échoué. Supprimez-la et réessayez." - }, - "upload_progress": "Envoi {uploaded} / {total}", - "upload_cancel": "Annuler l'envoi", - "upload_failed": "Échec du téléversement de {filename}", - "attachment_too_large": "« {name} » dépasse la taille autorisée par le serveur ({max} par fichier)", - "attachments_total_too_large": "Les pièces jointes dépasseraient la limite du serveur de {max} par message", - "send_failed": "Échec de l'envoi de l'e-mail", - "smime_sign_on": "Signature S/MIME activée", - "smime_sign_off": "Signature S/MIME désactivée", - "smime_encrypt_on": "Chiffrement S/MIME activé", - "smime_encrypt_off": "Chiffrement S/MIME désactivé", - "smime_encrypt_unavailable": "Chiffrement S/MIME indisponible – certificats des destinataires manquants", - "smime_unlock_title": "Déverrouiller la clé S/MIME", - "smime_unlock_message": "Entrez la phrase secrète pour déverrouiller votre clé S/MIME.", - "smime_unlock_button": "Déverrouiller", - "smime_passphrase_placeholder": "Phrase secrète S/MIME", - "send_timeout": "Le serveur ne répond plus. Vérifiez le dossier Envoyés avant de renvoyer : le message est peut-être déjà parti.", - "continue_draft": "Continuer le brouillon", - "close_draft_title": "Enregistrer ou supprimer le brouillon ?", - "close_draft_message": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer comme brouillon ou supprimer ?", - "drop_files": "Déposez les fichiers à joindre", - "show_less": "Afficher moins", - "forgot_attachment": { - "title": "Avez-vous oublié une pièce jointe ?", - "message": "Votre message mentionne \"{keyword}\" mais aucun fichier n'est joint. Envoyer quand même ?", - "send_anyway": "Envoyer quand même", - "back": "Retour à l'édition" - }, - "empty_subject": { - "title": "Envoyer sans objet ?", - "message": "Ce message n'a pas d'objet. L'envoyer quand même ?", - "dont_ask_again": "Ne plus me demander", - "send_anyway": "Envoyer quand même", - "back": "Retour à l'édition" - }, - "add_link": "Ajouter un lien", - "link_url_prompt": "Saisissez l'URL", - "sending": "Envoi...", - "schedule_send": "Planifier l’envoi", - "schedule_send_description": "Choisissez quand le serveur doit libérer ce message.", - "schedule_send_required": "Choisissez une date et une heure.", - "schedule_send_invalid": "Saisissez une date et une heure valides.", - "schedule_send_future": "Choisissez une date et une heure futures.", - "schedule_send_too_late": "Cette heure est au-delà de ce que le serveur autorise.", - "schedule_send_unsupported": "L’envoi planifié n’est pas pris en charge pour ce compte.", - "schedule_send_cleanup_warning": "L’envoi planifié a été créé, mais le nettoyage du brouillon a échoué.", - "send_delay_unsupported": "Le délai d’envoi n’est pas pris en charge pour ce compte.", - "send_delay_unsupported_confirm": "Ce compte ne prend pas en charge le délai d’envoi. Envoyer immédiatement ?", - "recipient_edit_email": "Modifier l'adresse e-mail", - "recipient_edit_name": "Modifier le nom d'affichage", - "recipient_email_placeholder": "Adresse e-mail", - "recipient_name_placeholder": "Nom d'affichage", - "autocomplete_search_server": "Rechercher sur le serveur", - "autocomplete_searching": "Recherche en cours...", - "toolbar": { - "bold": "Gras", - "italic": "Italique", - "underline": "Souligné", - "strikethrough": "Barré", - "text_color": "Couleur du texte", - "remove_color": "Supprimer la couleur", - "heading_1": "Titre 1", - "heading_2": "Titre 2", - "bullet_list": "Liste à puces", - "ordered_list": "Liste numérotée", - "quote": "Citation", - "code_block": "Bloc de code", - "align_left": "Aligner à gauche", - "align_center": "Centrer", - "align_right": "Aligner à droite", - "text_direction": "Sens du texte (RTL/LTR)", - "link": "Lien", - "table": "Tableau", - "clear_formatting": "Effacer la mise en forme", - "undo": "Annuler", - "redo": "Rétablir", - "add_row_above": "Insérer une ligne au-dessus", - "add_row_below": "Insérer une ligne en dessous", - "add_column_before": "Insérer une colonne avant", - "add_column_after": "Insérer une colonne après", - "delete_row": "Supprimer la ligne", - "delete_column": "Supprimer la colonne", - "toggle_header_row": "Basculer la ligne d'en-tête", - "delete_table": "Supprimer le tableau", - "pick_size": "Choisir la taille", - "font_size": "Taille de police", - "font_size_default": "Par défaut", - "background_color": "Couleur d'arrière-plan", - "remove_background_color": "Supprimer la couleur d'arrière-plan" - }, - "send_filing_warning": "Envoyé - mais le nettoyage après envoi a échoué, un ancien brouillon peut subsister." - }, - "confirm_dialog": { - "confirm": "Confirmer", - "cancel": "Annuler" - }, - "common": { - "loading": "Chargement...", - "error": "Erreur", - "success": "Succès", - "cancel": "Annuler", - "save": "Enregistrer", - "delete": "Supprimer", - "edit": "Modifier", - "close": "Fermer", - "search": "Rechercher", - "refresh": "Actualiser", - "settings": "Paramètres", - "help": "Aide", - "logout": "Déconnexion", - "yes": "Oui", - "no": "Non", - "unknown": "Inconnu", - "app_title": "Webmail", - "reconnecting": "Connexion perdue. Tentative de reconnexion…", - "rate_limited_title": "L'authentification du serveur est temporairement limitee.", - "rate_limited_detail": "Bulwark a suspendu les requetes en arriere-plan pour eviter un blocage. Nouvelle tentative dans {seconds}s.", - "rate_limited_action_title": "Requete suspendue pour eviter le blocage.", - "rate_limited_action_detail": "Bulwark attend la fin du delai impose par le serveur avant d'envoyer d'autres requetes authentifiees. Reessayez dans {seconds}s.", - "pull_to_refresh": "Tirez pour actualiser", - "release_to_refresh": "Relâchez pour actualiser", - "refreshing": "Actualisation…" - }, - "notifications": { - "email_sent": "Email envoyé avec succès", - "email_deleted": "Email supprimé", - "email_archived": "Email archivé", - "emails_deleted": "{count, plural, one {# e-mail supprimé} other {# e-mails supprimés}}", - "emails_archived": "{count, plural, one {# e-mail archivé} other {# e-mails archivés}}", - "error_archiving": "Impossible d'archiver l'e-mail", - "error_updating": "Impossible de mettre à jour l'e-mail", - "email_starred": "Email ajouté aux favoris", - "email_unstarred": "Email retiré des favoris", - "email_marked_read": "Email marqué comme lu", - "email_marked_unread": "Email marqué comme non lu", - "copied_to_clipboard": "Copié dans le presse-papiers", - "source_copied": "Source copiée dans le presse-papiers", - "error_sending": "Échec de l'envoi de l'email", - "error_deleting": "Échec de la suppression de l'email", - "error_loading": "Échec du chargement des emails", - "new_email": "Nouvel email", - "new_email_from": "De {sender}", - "click_to_view": "Cliquer pour voir", - "email_moved": "Email déplacé", - "emails_moved": "{count} emails déplacés", - "moved_to_mailbox": "Déplacé vers {mailbox}", - "move_failed": "Échec du déplacement", - "move_error": "Impossible de déplacer les emails vers le dossier sélectionné", - "email_tagged": "E-mail étiqueté", - "emails_tagged": "{count} e-mails étiquetés", - "tag_failed": "Échec de l'étiquetage", - "identity_created": "Identité créée avec succès", - "identity_updated": "Identité mise à jour avec succès", - "identity_deleted": "Identité supprimée", - "identity_set_primary": "Identité principale mise à jour", - "identity_create_failed": "Échec de la création de l'identité: {error}", - "identity_update_failed": "Échec de la mise à jour de l'identité: {error}", - "identity_delete_failed": "Échec de la suppression de l'identité: {error}", - "identity_unauthorized": "Vous n'êtes pas autorisé à envoyer depuis cette adresse email", - "identity_not_found": "Identité introuvable", - "vacation_saved": "Paramètres du répondeur d'absence enregistrés", - "vacation_save_failed": "Échec de l'enregistrement des paramètres du répondeur d'absence", - "filters_saved": "Filtres enregistrés avec succès", - "filters_save_failed": "Échec de l'enregistrement des filtres", - "filters_deleted": "Règle de filtrage supprimée", - "templates_exported": "Modèles exportés avec succès", - "templates_imported": "{count, plural, one {# modèle importé} other {# modèles importés}}", - "templates_import_errors": "Certains modèles n'ont pas pu être importés", - "templates_import_empty": "Aucun modèle trouvé dans le fichier", - "export_email_error": "Échec de l'exportation de l'e-mail", - "import_email_success": "E-mail importé avec succès", - "import_email_error": "Échec de l'importation de l'e-mail" - }, - "date": { - "today": "Aujourd'hui", - "yesterday": "Hier", - "this_week": "Cette semaine", - "last_week": "La semaine dernière", - "this_month": "Ce mois-ci", - "older": "Plus ancien", - "just_now": "À l'instant", - "minutes_ago": "Il y a {count} minute", - "minutes_ago_plural": "Il y a {count} minutes", - "hours_ago": "Il y a {count} heure", - "hours_ago_plural": "Il y a {count} heures", - "days_ago": "Il y a {count} jour", - "days_ago_plural": "Il y a {count} jours" - }, - "language": { - "title": "Langue", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Sélectionner la langue", - "switch_to_english": "Passer à l'anglais", - "switch_to_french": "Passer au français", - "switch_to_japanese": "Passer au japonais", - "switch_to_spanish": "Passer à l'espagnol", - "switch_to_italian": "Passer à l'italien", - "switch_to_german": "Passer à l'allemand", - "switch_to_dutch": "Passer au néerlandais", - "switch_to_portuguese": "Passer au portugais", - "switch_to_russian": "Passer au russe", - "switching": "Changement de langue...", - "switch": "Changer de langue", - "current": "Langue actuelle", - "polish": "Polski", - "switch_to_polish": "Passer au polonais", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Passer au persan", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Passer au mongol" - }, - "settings": { - "title": "Paramètres", - "back_to_mail": "Retour aux emails", - "save_success": "Paramètres enregistrés avec succès", - "import_success": "Paramètres importés avec succès", - "import_error": "Échec de l'importation des paramètres", - "reset_confirm": "Êtes-vous sûr de vouloir réinitialiser tous les paramètres ?", - "unsaved_changes": "Vous avez des modifications non enregistrées", - "discard_changes": "Abandonner les modifications ?", - "discard": "Abandonner", - "keep_editing": "Continuer l'édition", - "tabs": { - "appearance": "Apparence", - "language": "Langue, région et heure", - "email": "Comportement email", - "composer": "Compositeur", - "privacy": "Confidentialité et sécurité", - "account": "Compte", - "identities": "Identités", - "vacation": "Répondeur d'absence", - "advanced": "Avancé", - "calendar": "Calendrier", - "filters": "Filtres", - "templates": "Modèles", - "folders": "Dossiers", - "keywords": "Étiquettes", - "security": "Sécurité", - "encryption": "Chiffrement", - "files": "Fichiers", - "contacts": "Contacts", - "protocol_handlers": "Applications par défaut", - "sidebar_apps": "Apps de la barre latérale", - "notifications": "Notifications", - "layout": "Mise en page", - "reading": "Lecture", - "composing": "Rédaction", - "downloads": "Téléchargements", - "content_senders": "Contenu et expéditeurs", - "about_data": "À propos et données", - "debug": "Débogage" - }, - "tab_groups": { - "general": "Général", - "account": "Compte & Identité", - "organization": "Organisation des e-mails", - "apps": "Applications", - "system": "Système", - "appearance": "Apparence", - "mail": "Courrier", - "privacy": "Confidentialité et sécurité", - "advanced": "Avancé" - }, - "appearance": { - "message_list_order": { - "label": "Ordre de la liste des messages", - "description": "Comment les messages sont ordonnés dans la liste. Le tri est effectué par le serveur et couvre donc tout le dossier, pas seulement les messages chargés.", - "preset": { - "chronological": "Chronologique (plus récents d'abord)", - "unread_first": "Non lus d'abord", - "starred_first": "Suivis d'abord", - "tagged_first": "Étiquetés d'abord", - "custom": "Personnalisé" - }, - "tag_label": "Étiquette", - "advanced_show": "Avancé : modifier les niveaux de tri", - "advanced_hide": "Masquer les options avancées", - "criterion_label": "Critère du niveau {n}", - "direction_label": "Sens du niveau {n}", - "criteria": { - "unread": "Non lu", - "starred": "Suivi", - "tag": "Étiquette", - "receivedAt": "Date de réception", - "sentAt": "Date d'envoi", - "from": "Expéditeur", - "subject": "Objet", - "size": "Taille" - }, - "direction": { - "unread_first": "Non lus d'abord", - "read_first": "Lus d'abord", - "starred_first": "Suivis d'abord", - "unstarred_first": "Non suivis d'abord", - "tagged_first": "Étiquetés d'abord", - "untagged_first": "Non étiquetés d'abord", - "newest_first": "Plus récents d'abord", - "oldest_first": "Plus anciens d'abord", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Plus volumineux d'abord", - "smallest_first": "Plus petits d'abord" - }, - "add_level": "Ajouter un niveau", - "remove_level": "Supprimer le niveau", - "move_up": "Monter", - "move_down": "Descendre", - "tiebreak_note": "Jusqu'à trois niveaux ; les niveaux suivants départagent les égalités. « Plus récents d'abord » s'applique toujours en dernier.", - "scope": { - "label": "Appliquer à tous les dossiers", - "description": "Désactivé, l'ordre ne s'applique qu'à la boîte de réception ; les autres dossiers restent chronologiques." - }, - "unsupported_note": "Votre serveur de messagerie ne prend pas en charge le tri par état de lecture, suivi ou étiquette. Ces niveaux sont ignorés et la liste reste chronologique." - }, - "title": "Apparence", - "description": "Personnalisez l'apparence de votre webmail", - "theme": { - "label": "Thème", - "description": "Choisissez votre schéma de couleurs préféré", - "light": "Clair", - "dark": "Sombre", - "system": "Système" - }, - "language": { - "label": "Langue", - "description": "Choisissez votre langue préférée" - }, - "font_size": { - "label": "Taille de police", - "description": "Ajustez la taille du texte pour une meilleure lisibilité", - "small": "Petite", - "medium": "Moyenne", - "large": "Grande" - }, - "list_density": { - "label": "Densité", - "description": "Contrôlez l'espacement dans toute l'interface", - "extra_compact": "Très compacte", - "compact": "Compacte", - "regular": "Normale", - "comfortable": "Confortable" - }, - "animations": { - "label": "Activer les animations", - "description": "Afficher les transitions et effets fluides" - }, - "toolbar_position": { - "label": "Position de la barre d'outils", - "description": "Où afficher les boutons d'action des e-mails (Répondre, Archiver, Supprimer, etc.)", - "top": "En haut", - "below_subject": "Sous l'objet" - }, - "toolbar_labels": { - "label": "Afficher les libellés de la barre d'outils", - "description": "Afficher les libellés textuels à côté des icônes de la barre d'outils. Désactiver pour économiser de l'espace une fois familiarisé avec les icônes." - }, - "hide_account_switcher": { - "label": "Masquer le sélecteur de compte de la barre latérale", - "description": "Masquer le sélecteur de compte en haut de la barre latérale des dossiers. Vous pouvez toujours changer de compte depuis la barre de navigation inférieure." - }, - "show_rail_account_list": { - "label": "Afficher les avatars de compte sur la barre de navigation", - "description": "Afficher les cercles de comptes individuels en bas de la barre de navigation pour un changement rapide, avec un bouton de déconnexion en dessous." - }, - "unified_mailbox": { - "label": "Boîte aux lettres unifiée", - "description": "Afficher les dossiers combinés (Réception, Envoyés, etc.) de tous les comptes connectés", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Inclure les boîtes de groupe", - "description": "Inclure également les boîtes partagées ou de groupe dans la vue unifiée." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Icônes colorées dans la barre latérale", - "description": "Colore les icônes de dossiers et d'étiquettes par type (Boîte de réception en bleu, Indésirable en rouge, Envoyés en vert, etc.). Désactivez pour une barre latérale monochrome." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Afficher le nombre total de messages", - "description": "Affiche le nombre total de messages à côté des dossiers et des étiquettes, en plus du nombre de messages non lus. Désactivez pour n'afficher que les messages non lus." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Interface Pro (expérimental)", - "description": "Disposition pour utilisateurs avancés (bureau uniquement) avec navigation des messages multi-onglets et flux de travail multi-comptes. L'interface standard n'est pas affectée ; vous pouvez revenir à tout moment.", - "open_label": "Ouvrir l'interface Pro", - "back_to_standard": "Retour au standard" - }, - "cross_unread": { - "label": "Tous les comptes : Non lus", - "description": "Affiche une entrée dans la section Tous les comptes répertoriant les messages non lus de tous les comptes, couvrant tous les dossiers sauf indésirables, envoyés, archives, corbeille et brouillons." - }, - "cross_starred": { - "label": "Tous les comptes : Favoris", - "description": "Affiche une entrée dans la section Tous les comptes répertoriant les messages favoris de tous les comptes, couvrant tous les dossiers sauf indésirables, envoyés, archives, corbeille et brouillons." - }, - "cross_all": { - "label": "Tous les comptes : Tout le courrier", - "description": "Affiche une entrée dans la section Tous les comptes répertoriant tout le courrier de tous les comptes, couvrant tous les dossiers sauf indésirables, envoyés, archives, corbeille et brouillons." - } - }, - "keywords": { - "title": "Étiquettes de messagerie", - "description": "Définissez des étiquettes pour organiser vos e-mails avec des couleurs. Elles sont stockées sous forme de mots-clés JMAP sur le serveur.", - "add_keyword": "Ajouter une étiquette", - "label_field": "Nom d'affichage", - "label_placeholder": "ex. Travail, Personnel, Urgent", - "id_field": "ID d'étiquette", - "id_placeholder": "ex. travail, personnel", - "color_field": "Couleur", - "id_exists": "Cet ID d'étiquette existe déjà", - "edit": "Éditer l'étiquette", - "delete": "Supprimer l'étiquette", - "save": "Enregistrer", - "add": "Ajouter", - "cancel": "Annuler", - "migrating": "Mise à jour de l'étiquette sur les e-mails existants…", - "migration_error": "Impossible de mettre à jour l'étiquette sur les e-mails existants", - "nesting": { - "label": "Étiquettes imbriquées", - "description": "Imbriquez des étiquettes sous d'autres étiquettes et affichez-les sous forme d'arborescence dans la barre latérale." - }, - "parent_field": "Étiquette parente", - "no_parent": "Aucune étiquette parente", - "too_long": "Ce chemin d'étiquette est trop long ({max} caractères au maximum)", - "has_children_locked": "D'autres étiquettes sont imbriquées sous celle-ci, son nom et son étiquette parente sont donc verrouillés. Déplacez-les ou supprimez-les d'abord.", - "has_children_delete": "Retirez d'abord les étiquettes imbriquées sous celle-ci", - "visibility_field": "Visibilité dans la barre latérale", - "visibility": { - "show": "Afficher", - "unread": "Afficher si non lus", - "hide": "Masquer" - }, - "unrecognized": { - "title": "Récupérer les étiquettes depuis le serveur", - "description": "Les noms et couleurs des étiquettes sont enregistrés dans ce navigateur, mais les étiquettes elles-mêmes se trouvent sur vos e-mails sous forme de mots-clés JMAP. Analysez votre messagerie à la recherche de mots-clés qu'aucune étiquette ici n'explique, puis rajoutez-les.", - "scan": "Rechercher des étiquettes", - "rescan": "Analyser à nouveau", - "scanning": "Analyse en cours… {scanned} e-mails sur {total}", - "none": "Toutes les étiquettes présentes sur vos e-mails sont déjà définies ici.", - "messages": "{count, plural, one {# e-mail} other {# e-mails}}", - "add_all": "Ajouter les {count}", - "partial": "Seuls les {scanned} e-mails les plus récents sur {total} ont été analysés. Les étiquettes utilisées uniquement sur des e-mails plus anciens peuvent manquer.", - "error": "Impossible d'analyser votre messagerie à la recherche d'étiquettes." - } - }, - "notifications": { - "test_sound": "Tester le son de notification", - "sounds": { - "default": "Par défaut (Bip)", - "cheerful": "Joyeux", - "involved": "Élaboré", - "swift": "Geste rapide", - "relax": "Détente" - }, - "sound_selection": { - "title": "Son de notification", - "description": "Choisissez le son à jouer pour les notifications", - "choose": "Son", - "choose_desc": "Sélectionnez une sonnerie et cliquez sur l'icône du haut-parleur pour l'écouter" - }, - "email": { - "title": "Notifications par e-mail", - "description": "Configurer les notifications pour les e-mails entrants", - "enabled": "Notifications par e-mail", - "enabled_desc": "Afficher des notifications à l'arrivée de nouveaux e-mails", - "sound": "Son de notification", - "sound_desc": "Jouer un son à l'arrivée de nouveaux e-mails", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Notifications de calendrier", - "description": "Configurer les notifications pour les événements du calendrier", - "enabled": "Notifications d'événements", - "enabled_desc": "Afficher des alertes pour les événements à venir", - "sound": "Son de notification", - "sound_desc": "Jouer un son pour les rappels de calendrier", - "invitation_parsing": "Analyser les invitations par e-mail", - "invitation_parsing_desc": "Détecter les invitations de calendrier dans les pièces jointes et afficher les actions de calendrier" - }, - "push": { - "confirm_disable_message": "Cet appareil cessera de recevoir des alertes lorsque le site est fermé.", - "confirm_disable_title": "Désactiver les notifications en arrière-plan ?", - "description": "Recevez des notifications système pour les nouveaux courriers quand ce site est fermé. Livré via le relais push Bulwark ; le relais ne voit jamais le contenu des courriers.", - "disable": "Désactiver", - "enable": "Activer", - "dismiss_aria": "Fermer l'invite de notifications", - "ios_hint": "Sur iOS, installez d'abord le site sur l'écran d'accueil – Safari ne livre Web Push qu'aux PWA installées.", - "reenable": "Réenregistrer", - "relay_desc": "Utilise par défaut le relais Bulwark hébergé. Ne le changez que si vous l'hébergez vous-même.", - "relay_label": "Relais push", - "relay_locked": "Défini par l'administrateur", - "relay_locked_desc": "L'URL du relais push a été définie par votre administrateur et ne peut pas être modifiée.", - "status_active": "Actif sur cet appareil", - "status_busy": "Traitement en cours…", - "status_inactive": "Non activé sur cet appareil", - "status_unsupported": "Ce navigateur ne prend pas en charge Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Notifications en arrière-plan", - "devices_title": "Appareils enregistrés", - "devices_desc": "Tous les appareils enregistrés pour recevoir les notifications en arrière-plan de ce compte. Révoquer un appareil l’empêche immédiatement d’en recevoir.", - "devices_loading": "Chargement des appareils enregistrés…", - "devices_empty": "Aucun appareil n’est enregistré pour les notifications en arrière-plan.", - "devices_error": "Impossible de charger les appareils enregistrés.", - "devices_refresh": "Actualiser", - "device_this": "Cet appareil", - "device_other": "Autre appareil", - "device_status_active": "Distribue les notifications", - "device_status_inactive": "N’a jamais distribué de notification", - "device_status_unknown": "Inconnu de ce relais", - "device_expires": "Expire le {date}", - "revoke": "Révoquer", - "confirm_revoke_title": "Révoquer cet appareil ?", - "confirm_revoke_message": "Il cessera immédiatement de recevoir les notifications en arrière-plan. Réactiver les notifications push sur cet appareil l’enregistrera à nouveau.", - "confirm_revoke_message_this": "Cet appareil cessera immédiatement de recevoir les notifications en arrière-plan." - } - }, - "language_region": { - "title": "Langue, région et heure", - "description": "Langue, format de date, format d'heure et autres préférences régionales", - "language": { - "label": "Langue", - "description": "Choisissez votre langue préférée", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Format de date", - "description": "Comment les dates apparaissent dans la liste des e-mails", - "smart": "Intelligent (adapté à la région)", - "relative": "Relatif (il y a 1 h, il y a 2 j)", - "full": "Toujours la date complète", - "preview_today": "Aujourd'hui :", - "preview_this_week": "Cette semaine :", - "preview_older": "Plus ancien :" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Format d'heure", - "description": "Choisissez entre 12 heures ou 24 heures", - "12h": "12 heures", - "24h": "24 heures" - }, - "first_day": { - "label": "Premier jour de la semaine", - "description": "Commencer la semaine le dimanche ou le lundi", - "sunday": "Dimanche", - "monday": "Lundi" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Effacer la recherche lors du changement de dossier", - "description": "Cliquer sur un dossier (Boîte de réception, Envoyés…) efface la recherche active et affiche ce dossier. Désactivé, la recherche reste appliquée et est relancée dans le dossier choisi." - }, - "request_read_receipt": { - "label": "Demander un accusé de lecture par défaut", - "description": "Activer au préalable la demande d''accusé de lecture lors de la rédaction d''un nouveau message." - }, - "read_receipt_response": { - "label": "Répondre aux demandes d''accusé de lecture", - "description": "Que faire lorsqu''un message entrant demande un accusé de lecture.", - "ask": "Demander à chaque fois", - "always": "Toujours envoyer", - "never": "Ne jamais envoyer" - }, - "title": "Comportement email", - "description": "Configurez la gestion des emails", - "mark_read": { - "label": "Marquer comme lu", - "description": "Quand marquer les emails comme lus à l'ouverture", - "instant": "Instantanément", - "delay_3s": "Après 3 secondes", - "delay_5s": "Après 5 secondes", - "never": "Jamais" - }, - "delete_action": { - "label": "Action de suppression", - "description": "Que se passe-t-il quand vous supprimez un email", - "trash": "Déplacer vers la corbeille", - "trash_and_read": "Déplacer vers la corbeille et marquer comme lu", - "permanent": "Supprimer définitivement", - "warning": "Les emails seront supprimés définitivement et ne pourront pas être récupérés. Cette action est irréversible." - }, - "message_spacing": { - "label": "Espacement du message", - "description": "Marge autour du corps du message dans le lecteur", - "auto": "Automatique (recommandé)", - "always": "Toujours ajouter un espacement", - "edge": "Bord à bord" - }, - "plain_text_font": { - "label": "Police du texte brut", - "description": "Police utilisée pour les e-mails en texte brut", - "mono": "À chasse fixe", - "sans": "Normale (comme l'application)" - }, - "archive_mode": { - "label": "Archiver dans", - "description": "Comment organiser les emails lors de l'archivage", - "single": "Un seul dossier", - "year": "Un dossier par année", - "month": "Un dossier par mois", - "reorganize": "Réorganiser l'archive existante", - "reorganize_success": "{count, plural, =0 {Aucun email à réorganiser} =1 {1 email réorganisé} other {# emails réorganisés}}", - "reorganize_error": "Échec de la réorganisation de l'archive" - }, - "permanently_delete_junk": { - "label": "Supprimer définitivement les indésirables", - "description": "Supprimer définitivement les e-mails du dossier Indésirables au lieu de les déplacer vers la corbeille" - }, - "mail_layout": { - "label": "Disposition du courrier", - "description": "Choisissez entre le volet de lecture divisé classique et un flux de lecture focalisé style Gmail.", - "split": "Volet divisé", - "split_description": "Garder la liste des messages et le volet de lecture visibles côte à côte.", - "focus": "Liste focalisée", - "focus_description": "Afficher une ligne par message et ouvrir le courrier en pleine largeur tout en gardant la barre latérale des dossiers visible.", - "horizontal": "Volet de lecture en bas", - "horizontal_description": "Afficher la liste des messages en haut et ouvrir le message sélectionné dans un volet de lecture en dessous." - }, - "disable_threading": { - "label": "Désactiver le regroupement par conversation", - "description": "Afficher les e-mails comme messages individuels plutôt que groupés par conversation" - }, - "plain_text_mode": { - "label": "Texte brut uniquement", - "description": "Désactiver l'éditeur de texte enrichi et envoyer tous les e-mails en texte brut uniquement, y compris les réponses et les transferts" - }, - "auto_select_reply_identity": { - "label": "Sélection automatique de l'adresse de réponse", - "description": "Lors d'une réponse, bascule automatiquement l'adresse d'expédition vers l'identité qui a reçu le message d'origine" - }, - "reply_identity_match": { - "label": "Correspondance de l'adresse de réception", - "description": "Quelles adresses de réception comptent comme les vôtres. « Adresse exacte uniquement » ne choisit qu'une de vos identités configurées ; « même domaine » traite aussi toute autre adresse de vos domaines d'identité comme un alias catch-all et y réécrit l'en-tête De. Choisissez l'adresse exacte si ces adresses sont des listes de diffusion.", - "exact": "Adresse exacte uniquement", - "domain": "Toute adresse de mes domaines" - }, - "signature_position": { - "label": "Position de la signature", - "description": "Où insérer votre signature dans les réponses et les transferts. Au-dessus du texte cité, elle se lit naturellement comme la conclusion de la réponse ; en dessous, elle garde le message d'origine contigu.", - "above_quote": "Avant le texte cité", - "below_quote": "Après le texte cité" - }, - "signature_separator": { - "label": "Délimiteur de signature", - "description": "Préfixer la signature par la ligne de délimitation standard \"-- \" (RFC 3676). Désactivez si vous préférez enchaîner directement du message à la signature." - }, - "sub_address_delimiter": { - "label": "Délimiteur de sous-adresse", - "description": "Caractère séparant votre nom d'utilisateur de l'étiquette de sous-adresse. Utilisez le délimiteur configuré sur votre serveur de messagerie (par ex. utilisateur{delimiter}tag@domaine.com).", - "option": "{delimiter} (utilisateur{delimiter}tag@domaine.com)", - "custom": "Personnalisé…", - "custom_input_label": "Caractère de délimiteur personnalisé" - }, - "show_preview": { - "label": "Afficher l'aperçu", - "description": "Afficher l'aperçu de l'email dans la liste", - "focus_description": "Afficher le texte d'aperçu en ligne dans la liste de messages focalisée sur une seule ligne" - }, - "attachment_click_action": { - "label": "Action au clic sur les pièces jointes", - "description": "Choisissez si un clic sur une pièce jointe ouvre un aperçu ou lance immédiatement le téléchargement", - "preview": "Aperçu si possible", - "download": "Télécharger immédiatement" - }, - "attachment_position": { - "label": "Position des pièces jointes", - "description": "Où afficher les pièces jointes dans l'en-tête de l'email", - "beside-sender": "À côté de l'expéditeur", - "below-header": "Sous l'en-tête" - }, - "emails_per_page": { - "10": "10 e-mails", - "25": "25 e-mails", - "50": "50 e-mails", - "100": "100 e-mails", - "label": "Emails par page", - "description": "Nombre d'emails à charger à la fois" - }, - "always_light_mode": { - "label": "Toujours afficher les e-mails en mode clair", - "description": "Afficher le contenu des e-mails en mode clair même lorsque l'application est en mode sombre, évitant les problèmes de conversion" - }, - "external_content": { - "label": "Contenu externe", - "description": "Comment gérer les images et le contenu externe", - "ask": "Toujours demander", - "block": "Toujours bloquer", - "allow": "Toujours autoriser" - }, - "trusted_senders": { - "label": "Expéditeurs de confiance", - "description": "Gérer les expéditeurs dont les images se chargent automatiquement", - "count_zero": "Aucun", - "count_one": "1 expéditeur", - "count_other": "{count} expéditeurs", - "modal_title": "Expéditeurs de confiance", - "empty_title": "Aucun expéditeur de confiance", - "empty_description": "Lorsque vous consultez un email avec des images bloquées, cliquez sur « Toujours faire confiance à cet expéditeur » pour l'ajouter ici.", - "add_manually": "Ajouter manuellement", - "add_button": "Ajouter", - "add_placeholder": "Entrez une adresse email", - "search_placeholder": "Rechercher...", - "no_results": "Aucun expéditeur ne correspond à votre recherche", - "remove": "Supprimer", - "close": "Fermer", - "invalid_email": "Veuillez entrer une adresse email valide", - "already_added": "Cet expéditeur est déjà de confiance", - "save_error": "Échec de l'enregistrement - consultez le journal de débogage Contacts pour plus de détails", - "use_address_book_label": "Synchroniser avec le carnet d'adresses", - "use_address_book_description": "Enregistrez les expéditeurs de confiance dans un carnet \"Expéditeurs de confiance\" dédié pour les synchroniser sur tous vos appareils" - }, - "hover_actions": { - "label": "Actions rapides au survol", - "description": "Choisissez les actions rapides qui apparaissent au survol d'un e-mail dans la liste", - "delete": "Supprimer", - "star": "Étoile / Retirer l'étoile", - "mark_read": "Marquer lu / non lu", - "archive": "Archiver", - "tag": "Étiquette", - "spam": "Marquer comme spam", - "not_spam": "Non indésirable", - "none_selected": "Aucune action sélectionnée", - "mode_label": "Mode d'affichage", - "mode_inline": "En ligne", - "mode_floating": "Flottant", - "corner_label": "Position flottante", - "corner_top-left": "Haut gauche", - "corner_top-right": "Haut droit", - "corner_bottom-left": "Bas gauche", - "corner_bottom-right": "Bas droit" - }, - "default_mail_program": { - "label": "Programme de messagerie par défaut", - "description": "Enregistrer {appName} comme programme de messagerie par défaut pour les liens mailto:", - "button": "Définir par défaut", - "success": "Le navigateur a été invité à définir par défaut", - "error": "Votre navigateur ne prend pas en charge cette fonctionnalité" - }, - "attachment_reminder": { - "label": "Rappel de pièce jointe", - "description": "Avertir avant l'envoi lorsque votre message mentionne des pièces jointes mais qu'aucune n'est jointe", - "keywords_label": "Mots-clés déclencheurs", - "keywords_description": "Mots ou phrases qui déclenchent le rappel lorsqu'ils sont trouvés dans votre message", - "add_placeholder": "Ajouter un mot-clé...", - "add": "Add", - "remove": "Supprimer" - }, - "empty_subject_warning": { - "label": "Avertissement d'objet vide", - "description": "Demander confirmation avant d'envoyer un message sans objet." - }, - "hide_inline_image_attachments": { - "label": "Masquer les images intégrées des pièces jointes", - "description": "Les images intégrées au corps du message ne sont pas listées comme pièces jointes séparées" - }, - "attachment_image_previews": { - "label": "Afficher les aperçus d'images dans les pièces jointes", - "description": "Afficher les images jointes sous forme de vignettes plutôt qu'avec des icônes de fichier génériques" - }, - "send_delay": { - "label": "Annuler l’envoi / délai d’envoi", - "description": "Retarde les envois normaux avec une courte fenêtre côté serveur.", - "off": "Désactivé", - "seconds": "{seconds} secondes", - "unsupported": "Le compte actuel n’annonce pas la prise en charge de l’envoi différé. Le réglage reste enregistré pour d’autres comptes." - }, - "return_to_list_after_action": { - "label": "Revenir à la liste après suppression ou marquage comme non lu", - "description": "Après avoir supprimé ou marqué comme non lu le message ouvert, revenir à la liste des messages au lieu d'ouvrir le message suivant." - }, - "swipe_right_action": { - "label": "Action du balayage vers la droite (mobile)", - "description": "Sur les appareils tactiles, l'action effectuée lorsque vous balayez un message vers la droite dans la liste." - }, - "swipe_left_action": { - "label": "Action du balayage vers la gauche (mobile)", - "description": "Sur les appareils tactiles, l'action effectuée lorsque vous balayez un message vers la gauche dans la liste." - }, - "swipe_actions": { - "none": "Aucune", - "archive": "Archiver", - "delete": "Supprimer", - "mark_read": "Basculer lu/non lu", - "star": "Basculer le favori", - "spam": "Marquer comme spam" - }, - "rtl_editing": { - "label": "Prise en charge de la saisie de droite à gauche", - "description": "Ajoute un bouton de direction à la barre d'outils de l'éditeur pour mettre les paragraphes de gauche à droite ou de droite à gauche" - } - }, - "composer": { - "title": "Compositeur", - "description": "Configurez les paramètres de composition d'email", - "autosave": { - "label": "Intervalle de sauvegarde automatique", - "description": "Fréquence de sauvegarde automatique des brouillons", - "30s": "Toutes les 30 secondes", - "1m": "Toutes les minutes", - "2m": "Toutes les 2 minutes", - "5m": "Toutes les 5 minutes" - }, - "send_confirmation": { - "label": "Confirmation d'envoi", - "description": "Demander une confirmation avant d'envoyer les emails" - }, - "default_reply": { - "label": "Mode de réponse par défaut", - "description": "Action par défaut lors du clic sur répondre", - "reply": "Répondre", - "reply_all": "Répondre à tous" - } - }, - "privacy": { - "title": "Confidentialité et sécurité", - "description": "Gérez vos paramètres de confidentialité et sécurité", - "external_images": { - "label": "Bloquer les images externes", - "description": "Empêcher le suivi par les images externes" - }, - "session_timeout": { - "label": "Délai d'expiration de session", - "description": "Déconnexion automatique après inactivité", - "never": "Jamais", - "30m": "30 minutes", - "1h": "1 heure", - "4h": "4 heures" - }, - "clear_cache": { - "label": "Vider le cache", - "description": "Supprimer les données en cache et fichiers temporaires", - "button": "Vider le cache", - "confirm": "Êtes-vous sûr de vouloir vider le cache ?", - "success": "Cache vidé avec succès" - } - }, - "account": { - "title": "Compte", - "description": "Consultez les informations de votre compte", - "name_label": "Nom d'affichage", - "username_label": "Nom d'utilisateur", - "account_type_label": "Type de compte", - "auth_method_label": "Authentification", - "auth_method_oauth": "Authentification unique (OAuth/OIDC)", - "auth_method_basic": "Mot de passe", - "demo_account": "Compte de démonstration", - "email": { - "label": "Adresse email", - "value": "{email}" - }, - "server": { - "label": "Serveur JMAP", - "value": "{server}" - }, - "storage": { - "label": "Utilisation du stockage", - "used": "{used} sur {total} utilisés", - "percentage": "{percent}% utilisé" - }, - "last_sync": { - "label": "Dernière synchronisation", - "value": "{time}" - }, - "accounts": { - "title": "Comptes connectés", - "description": "Faites glisser pour réorganiser l'affichage des comptes dans le menu déroulant", - "active": "Compte actuellement actif", - "default_badge": "Compte par défaut", - "set_default": "Définir par défaut", - "switch_to": "Basculer vers ce compte", - "move_up": "Déplacer vers le haut", - "move_down": "Déplacer vers le bas", - "drag_handle": "Faire glisser pour réorganiser", - "add": "Ajouter un compte" - }, - "shared_accounts": { - "title": "Partagé avec moi", - "description": "Comptes de groupe et partagés que vous pouvez gérer. Sélectionnez-en un pour modifier ses filtres, son répondeur d'absence, ses calendriers et ses contacts.", - "shared_label": "Compte partagé" - } - }, - "security": { - "title": "Sécurité du compte", - "description": "Gérez votre mot de passe, l'authentification à deux facteurs et les paramètres de sécurité", - "detecting": "Détection des capacités du serveur...", - "not_available": "La gestion de la sécurité du compte n'est pas disponible pour ce serveur de messagerie. Les permissions requises peuvent être désactivées. Consultez la documentation pour plus de détails.", - "password": { - "title": "Changer le mot de passe", - "current": "Mot de passe actuel", - "new": "Nouveau mot de passe", - "confirm": "Confirmer le nouveau mot de passe", - "submit": "Changer le mot de passe", - "success": "Mot de passe changé avec succès", - "error_title": "Échec du changement de mot de passe", - "error_mismatch": "Les nouveaux mots de passe ne correspondent pas", - "error_min_length": "Le mot de passe doit comporter au moins 8 caractères", - "error_generic": "Impossible de changer le mot de passe" - }, - "display_name": { - "label": "Nom d'affichage", - "description": "Votre nom tel qu'il apparaît sur le serveur", - "placeholder": "Entrez votre nom d'affichage", - "save": "Enregistrer", - "success": "Nom d'affichage mis à jour", - "error": "Impossible de mettre à jour le nom d'affichage" - }, - "totp": { - "section_title": "Authentification à deux facteurs", - "label": "Authentification TOTP", - "description": "Ajoutez une couche de sécurité supplémentaire avec un mot de passe à usage unique basé sur le temps", - "active": "Activé", - "inactive": "Désactivé", - "enabled": "Authentification à deux facteurs activée", - "disabled": "Authentification à deux facteurs désactivée", - "enable_error": "Impossible d'activer la 2FA", - "disable_error": "Impossible de désactiver la 2FA", - "setup_instructions": "Copiez cette URL dans votre application d'authentification (Google Authenticator, Authy, etc.) :", - "verification_code": "Code de vérification", - "confirm": "Confirmer", - "disable": "Désactiver", - "disable_confirm_prompt": "Saisissez votre mot de passe pour désactiver l'authentification à deux facteurs.", - "password_required": "Le mot de passe est requis", - "code_required": "Le code de vérification est requis", - "code_invalid": "Code de vérification non valide. Vérifiez votre application d'authentification et réessayez." - }, - "app_passwords": { - "title": "Mots de passe d'application", - "description": "Créez des mots de passe pour les applications qui ne prennent pas en charge l'authentification à deux facteurs", - "add": "Ajouter", - "create": "Créer", - "cancel": "Annuler", - "generate": "Générer", - "name_label": "Nom de l'application", - "name_placeholder": "ex. Thunderbird, iPhone Mail", - "password_label": "Mot de passe (laisser vide pour auto-générer)", - "password_placeholder": "Auto-généré si vide", - "added": "Mot de passe d'application créé", - "removed": "Mot de passe d'application supprimé", - "add_error": "Impossible de créer le mot de passe d'application", - "remove_error": "Impossible de supprimer le mot de passe d'application", - "none": "Aucun mot de passe d'application configuré", - "done": "Terminé", - "expires_label": "Expire (facultatif)", - "copy_now_warning": "Copiez ce mot de passe maintenant - il ne sera plus affiché.", - "allowed_ips_label": "IP autorisées (facultatif)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Séparées par virgule ou espace. Laisser vide pour autoriser toute IP." - }, - "api_keys": { - "title": "Clés API", - "description": "Créez des clés API pour les scripts et intégrations qui communiquent directement avec le serveur", - "name_label": "Nom de la clé", - "name_placeholder": "ex. Script de sauvegarde, CI runner", - "copy_now_warning": "Copiez cette clé API maintenant - elle ne sera plus affichée.", - "added": "Clé API créée", - "removed": "Clé API supprimée", - "add_error": "Échec de la création de la clé API", - "remove_error": "Échec de la suppression de la clé API", - "none": "Aucune clé API configurée" - }, - "encryption": { - "section_title": "Chiffrement au repos", - "label": "Chiffrement des e-mails", - "description": "Chiffrez les e-mails stockés sur le serveur pour une confidentialité accrue", - "active": "Chiffrement {type} activé", - "inactive": "Désactivé", - "enabled": "Chiffrement au repos activé", - "disabled_success": "Chiffrement au repos désactivé", - "error": "Impossible de mettre à jour les paramètres de chiffrement", - "algorithm_label": "Algorithme de chiffrement", - "encrypt_on_append": "Chiffrer les nouveaux e-mails lors du téléversement", - "allow_spam_training": "Autoriser l'apprentissage antispam avant le chiffrement des e-mails", - "enable_encryption": "Activer le chiffrement", - "enable_tooltip": "Activer le chiffrement avec cette clé", - "configure_title": "Activer le chiffrement avec cette clé", - "enable_button": "Activer", - "disable_tooltip": "Désactiver le chiffrement", - "enabled_success": "Chiffrement au repos activé" - }, - "email_client": { - "title": "Configuration du client de messagerie", - "description": "Utilisez ces identifiants pour configurer votre client de messagerie de bureau ou mobile (Thunderbird, Apple Mail, Outlook, etc.)", - "jmap_username_label": "Nom d'utilisateur JMAP", - "copy": "Copier", - "copied": "Copié", - "password_instructions": "Utilisez votre nom d'utilisateur JMAP ci-dessus avec un mot de passe d'application pour vous connecter à votre client de messagerie. Créez un mot de passe d'application dans la section ci-dessus si vous ne l'avez pas encore fait." - }, - "link_device": { - "title": "Associer l'application mobile", - "description": "Connectez-vous à l'application mobile Bulwark Mail sans rien saisir. Générez un QR code ici et scannez-le depuis l'écran de connexion de l'application.", - "generate": "Afficher le QR code", - "regenerate": "Afficher un nouveau code", - "instructions": "Ouvrez l'application Bulwark Mail, appuyez sur \"Scanner le QR code\" sur l'écran de connexion et pointez votre caméra ici.", - "expires_in": "Ce code expire dans {seconds} secondes. Il ne peut être utilisé qu'une seule fois.", - "expired": "Ce code a expiré.", - "generating": "Génération…", - "error": "Impossible de créer un code d'association. Veuillez réessayer." - }, - "public_keys": { - "name_label": "Nom de la clé publique", - "name_placeholder": "ex. Ma clé", - "key_label": "Clé publique (S/MIME ou PGP au format ASCII)", - "key_placeholder": "Collez votre clé publique ici", - "none": "Aucune clé publique configurée" - } - }, - "identities": { - "title": "Identités d'envoi", - "description": "Gérer les adresses email depuis lesquelles vous pouvez envoyer", - "identities_count": { - "label": "Vos identités", - "description": "Adresses email configurées pour l'envoi", - "count_zero": "Aucune identité", - "count_one": "1 identité", - "count_other": "{count} identités" - }, - "manage": "Gérer les identités", - "sub_addressing": { - "label": "Sous-adressage", - "description": "Utilisez des tags comme utilisateur+tag@domaine.com pour organiser votre courrier", - "learn_more": "En savoir plus" - } - }, - "vacation": { - "title": "Répondeur d'absence", - "description": "Répondre automatiquement aux emails entrants pendant votre absence", - "loading": "Chargement des paramètres d'absence...", - "not_supported": "Votre serveur de messagerie ne prend pas en charge les réponses d'absence.", - "fetch_error": "Échec du chargement des paramètres d'absence. Veuillez réessayer.", - "status": { - "label": "Répondeur d'absence", - "description": "Envoyer une réponse automatique aux personnes qui vous envoient un email", - "active": "Actif", - "inactive": "Inactif" - }, - "date_range": { - "title": "Période", - "description": "Limiter optionnellement la réponse automatique à une période spécifique", - "start": "Date de début", - "start_description": "Laisser vide pour aucune limite de début", - "end": "Date de fin", - "end_description": "Laisser vide pour aucune limite de fin" - }, - "message": { - "title": "Message de réponse automatique", - "description": "Le message qui sera envoyé en réponse", - "subject_label": "Objet", - "subject_description": "Ligne d'objet de la réponse automatique", - "subject_placeholder": "Absence du bureau", - "body_label": "Corps du message", - "body_description": "Contenu du message en texte brut", - "body_placeholder": "Merci pour votre email. Je suis actuellement absent du bureau et vous répondrai à mon retour.", - "html_label": "Message mis en forme (HTML)", - "html_description": "Ajoutez une version mise en forme avec des liens et du style. Les destinataires dont la messagerie ne peut pas l'afficher verront le texte brut ci-dessus.", - "html_placeholder": "Rédigez une réponse d'absence mise en forme…" - }, - "preview": { - "title": "Aperçu", - "show": "Afficher l'aperçu", - "hide": "Masquer l'aperçu" - }, - "save": "Enregistrer les modifications", - "saving": "Enregistrement...", - "warnings": { - "end_before_start": "La date de fin doit être postérieure à la date de début", - "start_in_past": "La date de début est dans le passé", - "empty_body": "Le corps du message est vide - les destinataires recevront une réponse vide", - "subject_too_long": "L'objet est trop long pour le serveur ({max} octets au maximum).", - "body_too_long": "Le message est trop long pour le serveur ({max} octets au maximum, mise en forme comprise)." - } - }, - "folders": { - "role_memos": "Notes", - "title": "Dossiers", - "description": "Gérez vos dossiers de messagerie et attribuez des rôles standard", - "folder_list": "Vos dossiers", - "folder_list_description": "Cliquez sur l'icône d'un dossier pour le personnaliser", - "standard_roles": "Rôles standard des dossiers", - "standard_roles_description": "Définissez quels dossiers sont utilisés pour les rôles standard comme Boîte de réception, Envoyés, Corbeille, etc.", - "role_inbox": "Boîte de réception", - "role_drafts": "Brouillons", - "role_sent": "Envoyés", - "role_trash": "Corbeille", - "role_junk": "Spam / Indésirables", - "role_archive": "Archives", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Aucun", - "create_folder": "Créer un dossier", - "create_subfolder": "Créer un sous-dossier", - "reorder": "Glissez pour réorganiser", - "reorder_error": "Échec de la réorganisation des dossiers", - "move_error": "Échec du déplacement du dossier", - "subfolder_of": "Dans {name}", - "subfolder_name": "Nom du sous-dossier", - "new_folder_name": "Nom du dossier", - "rename": "Renommer", - "change_icon": "Changer l'icône", - "delete": "Supprimer", - "confirm_delete": "Êtes-vous sûr de vouloir supprimer \"{name}\" ? Les e-mails de ce dossier seront déplacés dans la Corbeille.", - "create": "Créer", - "cancel": "Annuler", - "no_folders": "Aucun dossier personnalisé", - "cannot_delete_role": "Impossible de supprimer un dossier avec un rôle standard. Retirez d'abord le rôle.", - "folder_created": "Dossier créé", - "folder_renamed": "Dossier renommé", - "folder_deleted": "Dossier supprimé", - "role_updated": "Rôle du dossier mis à jour", - "error_create": "Échec de la création du dossier", - "error_rename": "Échec du renommage du dossier", - "error_delete": "Échec de la suppression du dossier", - "error_delete_has_children": "Impossible de supprimer le dossier : il contient encore des sous-dossiers. Supprimez-les ou déplacez-les d'abord.", - "error_delete_has_email": "Impossible de supprimer le dossier : il contient encore des e-mails. Déplacez-les ou supprimez-les d'abord.", - "error_role": "Échec de la mise à jour du rôle du dossier" - }, - "advanced": { - "title": "Avancé", - "description": "Options avancées et paramètres développeur", - "debug_mode": { - "label": "Mode débogage", - "description": "Activer la journalisation détaillée pour le dépannage" - }, - "debug_categories": { - "description": "Sélectionnez les catégories à journaliser. Désactivez celles dont vous n'avez pas besoin pour réduire le bruit dans la console.", - "jmap": "Client JMAP", - "jmap_description": "Opérations de boîte aux lettres, récupération des e-mails et requêtes du protocole JMAP", - "calendar": "Calendrier", - "calendar_description": "Événements du calendrier, importations et messages de planification", - "tasks": "Tâches", - "tasks_description": "Création, récupération et mise à jour des tâches du calendrier", - "auth": "Authentification", - "auth_description": "Connexion, TOTP, échange de jetons et gestion de session", - "filters": "Filtres", - "filters_description": "Règles de filtre Sieve et scripts d'absence", - "email": "Affichage des e-mails", - "email_description": "Rendu des e-mails, traitement TNEF et marquage comme lu", - "push": "Notifications push", - "push_description": "Configuration et distribution des notifications push", - "contacts": "Contacts et carnets d'adresses", - "contacts_description": "Synchronisation des contacts, opérations de carnet d'adresses et expéditeurs de confiance" - }, - "settings_sync": { - "label": "Synchronisation des paramètres", - "description": "Synchronisez vos paramètres entre navigateurs et appareils" - }, - "sender_favicons": { - "label": "Favicons des expéditeurs", - "description": "Afficher les icônes de sites web comme photos de profil pour les expéditeurs professionnels" - }, - "show_avatars_in_junk": { - "label": "Afficher les avatars dans le dossier indésirable", - "description": "Afficher les photos de profil et favicons des expéditeurs dans le dossier indésirable. Désactivé par défaut pour éviter de donner une apparence légitime aux tentatives d'hameçonnage." - }, - "keyboard_shortcuts": { - "label": "Raccourcis clavier", - "description": "Voir les raccourcis clavier disponibles", - "button": "Voir les raccourcis" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Réinitialiser les paramètres", - "description": "Restaurer tous les paramètres par défaut", - "button": "Réinitialiser" - }, - "export_settings": { - "label": "Exporter les paramètres", - "description": "Télécharger vos paramètres au format JSON", - "button": "Exporter" - }, - "import_settings": { - "label": "Importer les paramètres", - "description": "Charger les paramètres depuis un fichier JSON", - "button": "Importer" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Apps de la barre latérale", - "description": "Gérer les applications et liens personnalisés dans la barre latérale", - "keep_loaded": "Garder les apps chargées", - "keep_loaded_description": "Garder les apps en ligne en arrière-plan lors du basculement pour éviter le rechargement", - "managed_title": "Fournies par votre administrateur", - "managed_description": "Ces applications sont configurées pour tout le monde et ne peuvent pas être modifiées.", - "manage_title": "Apps personnalisées", - "manage_description": "Ajouter, modifier ou supprimer des apps personnalisées de la barre latérale" - }, - "contacts": { - "title": "Contacts", - "description": "Importer et exporter vos contacts", - "import_label": "Importer des contacts", - "import_description": "Importer des contacts depuis un fichier vCard (.vcf)", - "export_label": "Exporter des contacts", - "export_description": "Exporter tous les contacts au format vCard (.vcf)", - "manage_title": "Carnets d'adresses", - "manage_description": "Renommer vos carnets d'adresses", - "no_address_books": "Aucun carnet d'adresses trouvé", - "categories_title": "Catégories", - "categories_description": "Renommer les catégories de contacts", - "no_categories": "Aucune catégorie trouvée", - "group_by_letter_description": "Afficher des en-têtes alphabétiques dans la liste de contacts", - "group_by_letter_label": "Grouper par première lettre", - "sort_by_last_name_label": "Trier par nom de famille", - "sort_by_last_name_description": "Classer la liste des contacts par nom de famille pour regrouper les membres d'une même famille" - }, - "downloads": { - "title": "Téléchargements", - "description": "Personnalisez la façon dont les e-mails et pièces jointes téléchargés sont nommés.", - "reset": "Restaurer la valeur par défaut", - "preview": "Aperçu :", - "email_template": { - "label": "Nom du fichier e-mail (.eml)", - "description": "Modèle utilisé lors de l'export ou du glisser-déposer d'un e-mail vers le système de fichiers. L'extension .eml est ajoutée automatiquement." - }, - "attachment_template": { - "label": "Nom du fichier de pièce jointe", - "description": "Modèle utilisé lors du téléchargement ou du glisser-déposer d'une pièce jointe. Si vous omettez '{filename}' et '{ext}', l'extension d'origine est conservée." - }, - "bundle_template": { - "label": "Nom du fichier .zip multi-e-mails", - "description": "Modèle utilisé lorsque vous faites glisser ou téléchargez plusieurs e-mails sélectionnés sous forme d'archive .zip unique. L'extension .zip est ajoutée automatiquement." - }, - "spaces": { - "label": "Espaces", - "description": "Remplacer les espaces dans le nom de fichier final par un autre caractère.", - "keep": "Conserver les espaces", - "underscore": "Remplacer par _", - "dash": "Remplacer par -" - }, - "lowercase": { - "label": "Minuscules", - "description": "Forcer tout le nom de fichier en minuscules." - }, - "strip_diacritics": { - "label": "Supprimer les diacritiques", - "description": "Convertir les lettres accentuées en leur équivalent ASCII (ä → a, é → e). Utile pour les outils qui gèrent mal les noms Unicode." - }, - "collapse_separators": { - "label": "Fusionner les séparateurs répétés", - "description": "Fusionner les suites d’espaces, de tirets bas ou de tirets en un seul caractère." - }, - "after_export": { - "label": "Après l’export", - "description": "Déplacer éventuellement l’e-mail après l’avoir exporté en .eml.", - "keep": "Garder dans la boîte", - "archive": "Déplacer vers les archives", - "trash": "Déplacer vers la corbeille" - } - }, - "filters": { - "title": "Filtres de courrier", - "description": "Créez des règles pour trier, étiqueter et gérer automatiquement les courriers entrants", - "add_rule": "Ajouter une règle", - "no_rules": "Aucune règle de filtrage", - "vacation_active": "Le répondeur d'absence est actif", - "vacation_active_description": "La réponse automatique est activée pour les messages entrants", - "vacation_configure": "Configurer", - "no_rules_description": "Créez des règles pour organiser automatiquement vos courriers entrants", - "edit_rule": "Modifier la règle", - "new_rule": "Nouvelle règle", - "delete_rule": "Supprimer la règle", - "delete_confirm": "Êtes-vous sûr de vouloir supprimer cette règle ?", - "enable": "Activer", - "disable": "Désactiver", - "raw_editor": "Éditeur Sieve brut", - "raw_editor_warning": "La modification du script Sieve brut peut casser l'édition visuelle des règles. Les modifications effectuées ici remplacent le constructeur visuel.", - "validate": "Valider", - "validation_success": "Le script est valide", - "validation_error": "Le script contient des erreurs", - "save": "Enregistrer les règles", - "saving": "Enregistrement...", - "saved": "Filtres enregistrés avec succès", - "save_failed": "Échec de l'enregistrement des filtres", - "loading": "Chargement des filtres...", - "not_supported": "Votre serveur de messagerie ne prend pas en charge les filtres de courrier.", - "rule_name": "Nom de la règle", - "rule_name_placeholder": "ex. Trier les newsletters", - "match_all": "Correspondre à TOUTES les conditions", - "match_any": "Correspondre à N'IMPORTE QUELLE condition", - "conditions": "Conditions", - "add_condition": "Ajouter une condition", - "actions": "Actions", - "add_action": "Ajouter une action", - "stop_processing": "Arrêter le traitement des règles suivantes", - "include_spam": "Déplacer aussi les messages marqués comme spam", - "attachment_type_placeholder": "p. ex. pdf, doc, jpg", - "value_placeholder_multi": "Valeur (plusieurs séparées par des virgules)", - "condition_fields": { - "attachment": "Pièce jointe", - "from": "De", - "to": "À", - "cc": "Cc", - "subject": "Objet", - "header": "En-tête personnalisé", - "size": "Taille", - "body": "Corps" - }, - "comparators": { - "has_any": "est présent", - "has_type": "de type", - "contains": "contient", - "not_contains": "ne contient pas", - "is": "est exactement", - "not_is": "n'est pas", - "starts_with": "commence par", - "ends_with": "se termine par", - "matches": "correspond au motif", - "greater_than": "est supérieur à", - "less_than": "est inférieur à" - }, - "action_types": { - "move": "Déplacer vers le dossier", - "copy": "Copier dans le dossier", - "forward": "Transférer à", - "mark_read": "Marquer comme lu", - "star": "Marquer d'une étoile", - "add_label": "Ajouter une étiquette", - "discard": "Supprimer silencieusement", - "reject": "Rejeter avec un message", - "keep": "Conserver dans la boîte de réception", - "stop": "Arrêter le traitement" - }, - "move_to_folder": "Sélectionner le dossier", - "copy_to_folder": "Sélectionner le dossier", - "forward_to": "Transférer à l'adresse e-mail", - "forward_placeholder": "email@exemple.com", - "forward_keep_copy": "Garder une copie", - "forward_limit": "Limite de transferts par message sur ce serveur : {count}. Les transferts en trop sont ignorés.", - "reject_message": "Message de rejet", - "reject_placeholder": "Votre e-mail a été rejeté", - "label_name": "Nom de l'étiquette", - "label_placeholder": "Sélectionner une étiquette", - "header_name": "Nom de l'en-tête", - "header_placeholder": "ex. X-Mailing-List", - "size_bytes": "Taille en octets", - "size_placeholder": "ex. 1000000", - "system_managed": "Règle gérée par le système", - "opaque_warning": "Ce script a été modifié en dehors du constructeur visuel. Seule l'édition Sieve brute est disponible.", - "open_sieve_editor": "Ouvrir l'éditeur Sieve brut", - "fetch_error": "Échec du chargement des filtres", - "expanded_view": "Vue étendue", - "expanded_view_description": "Afficher les règles de filtre avec des blocs de conditions et d'actions détaillés", - "if": "Si", - "then": "Alors", - "match_all_conditions": "toutes correspondent", - "match_any_condition": "une correspond", - "and": "et", - "or": "ou", - "cancel": "Annuler", - "confirm_delete": "Supprimer", - "rule_list": "Règles de filtrage", - "drag_to_reorder": "Glisser pour réorganiser", - "match_type": "Type de correspondance", - "reset_to_visual": "Revenir au constructeur visuel", - "reset_warning": "Cela supprimera le script actuel et recommencera à zéro.", - "confirm_reset": "Réinitialiser", - "validation_empty_name": "Le nom de la règle est requis", - "validation_empty_conditions": "Au moins une condition avec une valeur est requise", - "validation_empty_actions": "Au moins une action est requise", - "templates_section": "Partir d'un modèle", - "template_newsletters": "Déplacer les newsletters dans un dossier", - "template_receipts": "Archiver les reçus automatiquement", - "template_important": "Marquer les e-mails importants", - "template_notifications": "Filtrer les notifications", - "sieve_editor": { - "title": "Éditeur de script Sieve", - "warning": "La modification du script Sieve brut peut casser l'édition visuelle des règles. Les modifications effectuées ici remplacent le constructeur visuel.", - "script_content": "Script Sieve", - "valid": "Le script est valide", - "invalid": "Le script contient des erreurs", - "save_warning": "L'enregistrement écrasera toutes les règles visuelles. Cette action est irréversible. Cliquez à nouveau sur Enregistrer pour confirmer.", - "validating": "Validation...", - "validate": "Valider", - "cancel": "Annuler", - "save": "Enregistrer", - "confirm_save": "Confirmer l'enregistrement", - "validation_failed": "Échec de la requête de validation" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# condition} other {# conditions}}", - "actions_count": "{count, plural, one {# action} other {# actions}}" - }, - "origin_external": "Externe", - "managed_by_tooltip": "Géré par {source}. Modifiez-le dans cette application, ou utilisez l'éditeur Sieve brut." - }, - "templates": { - "title": "Modèles d'e-mails", - "description": "Créez des modèles d'e-mails réutilisables avec des variables", - "add": "Nouveau modèle", - "edit": "Modifier le modèle", - "name": "Nom du modèle", - "name_placeholder": "ex : E-mail de suivi", - "category": "Catégorie", - "category_placeholder": "ex : Travail, Personnel", - "subject": "Objet", - "subject_placeholder": "Objet de l'e-mail", - "body": "Corps", - "body_placeholder": "Contenu de l'e-mail...", - "recipients_placeholder": "email@example.com", - "identity": "Envoyer en tant que", - "default_identity": "Identité par défaut", - "favorite": "Favori", - "cancel": "Annuler", - "create": "Créer", - "update": "Mettre à jour", - "confirm_delete": "Supprimer", - "no_templates": "Aucun modèle", - "manage": "Gérer les modèles", - "count": "{count, plural, one {# modèle} other {# modèles}}", - "export_import": "Exporter et importer", - "export_import_description": "Sauvegardez vos modèles ou transférez-les sur un autre appareil", - "export": "Exporter", - "import": "Importer", - "validation": { - "empty": "Le nom du modèle est requis", - "too_long": "Le nom du modèle ne doit pas dépasser 200 caractères" - } - }, - "files": { - "display": { - "title": "Affichage", - "description": "Configurez l'affichage des fichiers et dossiers" - }, - "folder_layout": { - "label": "Navigation des dossiers", - "description": "Choisissez comment les dossiers sont affichés : intégrés avec les fichiers ou dans une arborescence latérale", - "inline": "Intégré", - "sidebar": "Barre latérale" - }, - "default_view": { - "label": "Vue par défaut", - "description": "Choisissez entre la disposition en grille et en liste", - "list": "Liste", - "grid": "Grille" - }, - "default_sort": { - "label": "Tri par défaut", - "description": "Choisissez le tri par défaut pour les fichiers", - "name": "Nom", - "size": "Taille", - "modified": "Modifié" - }, - "sort_direction": { - "label": "Sens du tri", - "description": "Choisissez l'ordre croissant ou décroissant", - "ascending": "Croissant", - "descending": "Décroissant" - }, - "icons": { - "title": "Icônes", - "description": "Configurez l'apparence des icônes de fichiers" - }, - "show_icons": { - "label": "Afficher les icônes", - "description": "Afficher les icônes à côté des fichiers et dossiers" - }, - "colored_icons": { - "label": "Icônes colorées", - "description": "Utiliser des icônes colorées au lieu de monochromes" - }, - "show_thumbnails": { - "label": "Afficher les miniatures", - "description": "Afficher les aperçus d'images au lieu des icônes" - }, - "behavior": { - "title": "Comportement", - "description": "Configurez le comportement du gestionnaire de fichiers" - }, - "show_hidden": { - "label": "Afficher les fichiers cachés", - "description": "Afficher les fichiers et dossiers commençant par un point" - }, - "preview": { - "label": "Aperçu" - } - }, - "search_placeholder": "Rechercher dans les paramètres", - "search_clear": "Effacer la recherche", - "search_no_results": "Aucun paramètre correspondant", - "scoped": { - "back": "Retour à mon compte", - "managing": "Gestion : {name}" - } - }, - "errors": { - "page_error_title": "Une erreur s'est produite", - "page_error_description": "Nous avons rencontré une erreur inattendue. Veuillez réessayer ou retourner à la page d'accueil.", - "sidebar_error": "Impossible de charger les boîtes mail", - "email_list_error": "Impossible de charger les emails", - "viewer_error_title": "Impossible d'afficher l'email", - "viewer_error_description": "Un problème est survenu lors de l'affichage de cet email. Il peut contenir du contenu non pris en charge.", - "composer_error": "Impossible de charger le compositeur", - "settings_error_title": "Paramètres indisponibles", - "settings_error_description": "Impossible de charger les paramètres. Vos préférences peuvent ne pas être sauvegardées.", - "try_again": "Réessayer", - "reload": "Recharger", - "reload_emails": "Recharger les emails", - "reload_settings": "Recharger les paramètres", - "retry": "Réessayer", - "go_home": "Aller à la boîte de réception" - }, - "context_menu": { - "reply": "Répondre", - "reply_all": "Répondre à tous", - "forward": "Transférer", - "mark_read": "Marquer comme lu", - "mark_unread": "Marquer comme non lu", - "star": "Marquer comme favori", - "unstar": "Retirer des favoris", - "pin": "Épingler", - "unpin": "Désépingler", - "move_to": "Déplacer vers...", - "archive": "Archiver", - "delete": "Supprimer", - "mark_as_spam": "Signaler comme spam", - "not_spam": "Pas un spam", - "tag": "Étiquette", - "items_selected": "{count} emails sélectionnés", - "edit_draft": "Modifier le brouillon", - "cancel_scheduled_send": "Annuler l’envoi", - "reschedule_send": "Replanifier", - "cancel_and_edit": "Annuler et modifier", - "cancel_and_compose_again": "Annuler et rédiger à nouveau" - }, - "mailbox_context_menu": { - "mark_folder_read": "Marquer le dossier comme lu", - "mark_folder_tree_read": "Marquer le dossier et les sous-dossiers comme lus", - "mark_all_folders_read": "Marquer tous les dossiers comme lus", - "new_subfolder": "Nouveau sous-dossier...", - "new_folder": "Nouveau dossier...", - "rename": "Renommer...", - "share": "Partager...", - "import_email": "Importer .eml ou .zip...", - "empty_folder": "Vider le dossier", - "empty_folder_generic": "Vider le dossier", - "delete_folder": "Supprimer le dossier", - "refresh": "Actualiser", - "mark_all_confirm_title": "Marquer tous les dossiers comme lus", - "mark_all_confirm_message": "Marquer tous les messages non lus de votre compte personnel comme lus ?", - "delete_confirm_title": "Supprimer le dossier", - "delete_confirm_message": "Supprimer définitivement le dossier \"{name}\" ? Cette action est irréversible.", - "prompt_new_subfolder": "Entrez un nom pour le nouveau sous-dossier.", - "prompt_new_folder": "Entrez un nom pour le nouveau dossier.", - "prompt_rename": "Entrez un nouveau nom pour ce dossier.", - "toast_marked_read": "Dossier marqué comme lu", - "toast_marked_read_count": "{count, plural, one {1 message marqué} other {# messages marqués}} comme lu(s)", - "toast_already_read": "Aucun message non lu", - "toast_marked_all_read": "Tous les dossiers marqués comme lus", - "toast_emptied": "Dossier vidé", - "toast_folder_created": "Dossier créé", - "toast_folder_renamed": "Dossier renommé", - "toast_folder_deleted": "Dossier supprimé", - "toast_error_mark_read": "Échec du marquage comme lu", - "toast_error_empty": "Échec du vidage du dossier", - "toast_error_create": "Échec de la création du dossier", - "toast_error_rename": "Échec du renommage du dossier", - "toast_error_delete": "Échec de la suppression du dossier", - "toast_error_delete_has_children": "Le dossier contient des sous-dossiers. Supprimez-les d'abord.", - "toast_error_delete_has_email": "Le dossier n'est pas vide. Videz-le d'abord.", - "delete_with_contents_title": "Le dossier n'est pas vide", - "delete_with_contents_message": "Le dossier « {name} » contient encore {count, plural, one {1 message} other {# messages}}. Supprimer le dossier et tous ses messages ? Cette action est irréversible.", - "delete_with_contents_confirm": "Supprimer le dossier et les messages", - "placeholder_folder_name": "Nom du dossier", - "create": "Créer", - "rename_confirm": "Renommer" - }, - "shortcuts": { - "title": "Raccourcis clavier", - "tip": "Appuyez sur ? à tout moment pour afficher cette aide", - "sections": { - "navigation": "Navigation", - "actions": "Actions email", - "global": "Global", - "threads": "Conversations", - "composer": "Rédaction" - }, - "navigation": { - "next_email": "Email suivant", - "previous_email": "Email précédent", - "open_email": "Ouvrir l'email", - "close_email": "Fermer / Désélectionner" - }, - "actions": { - "reply": "Répondre", - "reply_all": "Répondre à tous", - "forward": "Transférer", - "star": "Basculer favori", - "archive": "Archiver", - "delete": "Supprimer", - "mark_unread": "Marquer comme non lu", - "mark_read": "Marquer comme lu", - "toggle_spam": "Signaler / Pas un spam" - }, - "global": { - "compose": "Composer un email", - "search": "Rechercher", - "help": "Afficher les raccourcis", - "refresh": "Actualiser les emails", - "select_all": "Tout sélectionner", - "global_search": "Tout rechercher" - }, - "threads": { - "expand_collapse": "Développer/réduire la conversation" - }, - "composer": { - "send": "Envoyer l'e-mail", - "schedule_send": "Planifier l'envoi", - "template_picker": "Ouvrir le sélecteur de modèles" - } - }, - "threads": { - "messages_one": "{count} message", - "messages_other": "{count} messages", - "messages_tooltip": "{count, plural, one {# message dans cette conversation} other {# messages dans cette conversation}}", - "expand": "Développer la conversation", - "collapse": "Réduire la conversation", - "loading": "Chargement de la conversation...", - "mark_read": "Marquer la conversation comme lue", - "mark_unread": "Marquer la conversation comme non lue", - "archive": "Archiver la conversation", - "delete": "Supprimer la conversation", - "star": "Marquer la conversation comme favorite", - "unstar": "Retirer des favoris", - "toggle_thread": "Déplier/replier la discussion" - }, - "identities": { - "modal_title": "Gérer les identités d'envoi", - "create_new": "Créer une nouvelle identité", - "edit_identity": "Modifier l'identité", - "delete_confirm": "Supprimer cette identité ? Cette action est irréversible.", - "cannot_delete": "Cette identité ne peut pas être supprimée", - "primary_identity": "Principale", - "set_as_primary": "Définir comme principale", - "no_identities": "Aucune identité trouvée", - "display": { - "reply_to": "Répondre à :", - "bcc": "CCI :", - "signature": "Signature :", - "preview": "Aperçu :" - }, - "validation_errors": { - "invalid_emails": "Emails invalides : {emails}", - "unknown_error": "Erreur inconnue" - }, - "form": { - "name_label": "Nom d'affichage", - "name_placeholder": "ex: Email professionnel, Personnel", - "name_required": "Le nom est requis", - "email_label": "Adresse email", - "email_placeholder": "votre.email@exemple.com", - "email_required": "L'email est requis", - "email_invalid": "Veuillez entrer une adresse email valide", - "email_immutable": "L'adresse email ne peut pas être modifiée après création", - "reply_to_label": "Répondre à (optionnel)", - "reply_to_placeholder": "autre@example.com", - "bcc_label": "Copie cachée automatique (optionnel)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "Signature texte", - "html_signature_label": "Signature HTML", - "signature_byte_counter": "{bytes} / {max} octets", - "signature_byte_limit_reached": "Limite du serveur atteinte", - "save": "Enregistrer l'identité", - "cancel": "Annuler", - "creating": "Création...", - "updating": "Mise à jour..." - }, - "sub_address": { - "button_tooltip": "Utiliser le sous-adressage", - "popover_title": "Ajouter un tag de sous-adresse", - "tag_input_placeholder": "Entrez un tag (ex: shopping)", - "preview_label": "Aperçu:", - "recent_tags": "Tags récents", - "suggested_tags": "Suggérés", - "use_address": "Utiliser cette adresse", - "invalid_tag": "Le tag doit contenir uniquement des lettres, chiffres et tirets", - "tag_too_long": "Le tag doit faire 30 caractères ou moins", - "help_text": "Les emails envoyés à utilisateur{delimiter}tag@domaine.com arriveront dans votre boîte de réception", - "validation": { - "empty": "Le tag ne peut pas être vide", - "too_long": "Le tag doit faire {max} caractères ou moins", - "invalid_chars": "Le tag doit contenir uniquement des lettres, chiffres et tirets" - } - }, - "badge": { - "sent_via": "via", - "sub_address_tag": "Envoyé avec le sous-adresse: {tag}", - "identity_name": "Envoyé avec l'identité: {name}", - "identity_short": "via {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Supprimer", - "delete_confirm_title": "Supprimer l'identité" - }, - "templates": { - "picker_title": "Choisir un modèle", - "search_placeholder": "Rechercher des modèles...", - "section_favorites": "Favoris", - "section_recent": "Récents", - "section_uncategorized": "Autres", - "no_templates": "Aucun modèle", - "no_results": "Aucun modèle trouvé", - "fill_placeholders": "Remplir les variables", - "enter_value": "Entrez une valeur...", - "preview": "Aperçu", - "insert_with_values": "Insérer avec les valeurs", - "insert_raw": "Insérer brut", - "copy_suffix": "(copie)", - "placeholder": "Variable", - "placeholders": { - "recipient_name": "Nom du destinataire", - "company": "Nom de l'entreprise", - "date": "Date du jour", - "day_of_week": "Jour de la semaine", - "sender_name": "Votre nom" - } - }, - "contacts": { - "title": "Contacts", - "search_placeholder": "Rechercher des contacts...", - "create_new": "Nouveau contact", - "no_category": "Sans catégorie", - "rename_category": "Renommer la catégorie", - "category_name_label": "Nom de la catégorie", - "category_renamed": "Catégorie renommée", - "category_rename_failed": "Échec du renommage de la catégorie", - "category_added": "Contact ajouté à {name}", - "category_added_plural": "{count} contacts ajoutés à {name}", - "empty_state": "Aucun contact", - "empty_state_title": "Aucun contact", - "empty_state_subtitle": "Créez votre premier contact ou importez depuis un fichier vCard", - "empty_search": "Aucun contact ne correspond à votre recherche", - "empty_search_hint": "Essayez un autre terme de recherche", - "empty_filtered": "Aucun contact ne correspond à vos filtres", - "empty_filtered_hint": "Ajustez ou effacez les filtres", - "clear_search": "Effacer la recherche", - "import_vcard": "Importer vCard", - "delete_confirm_title": "Supprimer le contact", - "delete_confirm": "Êtes-vous sûr de vouloir supprimer ce contact ?", - "local_mode": "Les contacts sont stockés localement (le serveur ne prend pas en charge JMAP Contacts)", - "back_to_contacts": "Retour aux contacts", - "back_to_email": "Retour à l'e-mail", - "tabs": { - "all": "Tous", - "groups": "Groupes" - }, - "shared": { - "title": "Partagés" - }, - "address_books": { - "create": "Nouveau carnet d'adresses", - "created": "Carnet d'adresses créé", - "create_failed": "Échec de la création du carnet d'adresses", - "title": "Mes Carnets d'adresses", - "shared_prefix": "Partagé : {name}", - "moved": "Contact déplacé vers {name}", - "moved_plural": "{count} contacts déplacés vers {name}", - "move_failed": "Échec du déplacement du contact", - "address_book": "Carnet d'adresses", - "rename": "Renommer le carnet d'adresses", - "name_label": "Nom du carnet d'adresses", - "renamed": "Carnet d'adresses renommé", - "rename_failed": "Échec du renommage du carnet d'adresses", - "default": "Par défaut", - "set_default": "Définir par défaut", - "default_updated": "Carnet d’adresses par défaut mis à jour", - "set_default_failed": "Échec de la définition du carnet d’adresses par défaut", - "manage": "Gérer les carnets d'adresses", - "share": "Partager le carnet d'adresses", - "new_contact_in_book": "Nouveau contact dans ce carnet d'adresses", - "delete": "Supprimer le carnet d'adresses", - "confirm_delete": "Supprimer « {name} » ? Tous les contacts de ce carnet d'adresses seront supprimés.", - "deleted": "Carnet d'adresses supprimé", - "delete_failed": "Échec de la suppression du carnet d'adresses" - }, - "detail": { - "emails": "Adresses e-mail", - "phones": "Numéros de téléphone", - "organizations": "Organisations", - "addresses": "Adresses", - "notes": "Notes", - "titles": "Titres et rôles", - "online_services": "Services en ligne", - "anniversaries": "Anniversaires", - "personal_info": "Informations personnelles", - "languages": "Langues", - "categories": "Catégories", - "related_contacts": "Contacts associés", - "crypto_keys": "Clés cryptographiques", - "no_contact_selected": "Sélectionnez un contact pour voir les détails", - "compose_email": "Écrire un e-mail", - "copy_email": "Copier l'e-mail", - "copy_phone": "Copier le numéro", - "copy_url": "Copier l'URL", - "copied": "Copié dans le presse-papiers", - "copy_failed": "Échec de la copie dans le presse-papiers", - "created": "Créé", - "updated": "Dernière mise à jour", - "timezone": "Fuseau horaire", - "anniversary_birth": "Anniversaire", - "anniversary_death": "Décès", - "anniversary_wedding": "Anniversaire de mariage", - "anniversary_other": "Autre", - "personal_expertise": "Expertise", - "personal_hobby": "Loisir", - "personal_interest": "Intérêt", - "personal_other": "Autre", - "gender": "Genre", - "gender_masculine": "Masculin", - "gender_feminine": "Féminin", - "gender_other": "Autre", - "gender_none": "Non applicable", - "gender_unknown": "Inconnu", - "calendar": "Calendrier", - "calendar_uri": "URL du calendrier", - "scheduling_uri": "URL de planification", - "freebusy_uri": "URL de disponibilité", - "cert_issuer": "Émetteur", - "cert_expires": "Expire le", - "cert_expired": "Expiré", - "cert_fingerprint": "Empreinte", - "cert_algorithm": "Algorithme", - "import_to_smime": "Importer dans S/MIME", - "cert_already_imported": "Certificat déjà importé", - "cert_imported": "Certificat importé", - "cert_import_failed": "Échec de l'import du certificat", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Plus d'actions", - "age_years": "{count, plural, one {1 an} other {# ans}}", - "years_since": "{count, plural, one {il y a 1 an} other {il y a # ans}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Nouveau contact", - "edit_title": "Modifier le contact", - "section_address_book": "Répertoire", - "select_address_book": "Sélectionner un répertoire...", - "section_identity": "Nom et identité", - "contact_type": "Type de contact", - "type_person": "Personne", - "type_organization": "Organisation", - "section_work": "Travail et organisation", - "prefix": "Préfixe", - "prefix_placeholder": "Dr., M., Mme", - "given_name": "Prénom", - "middle_name": "Deuxième prénom", - "surname": "Nom", - "suffix": "Suffixe", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Surnom", - "nickname_placeholder": "Surnom", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Téléphone", - "phone_placeholder": "+33 1 23 45 67 89", - "phone_type": "Type", - "phone_voice": "Voix", - "phone_cell": "Mobile", - "phone_fax": "Fax", - "phone_pager": "Bip", - "phone_video": "Vidéo", - "phone_text": "SMS", - "organization": "Organisation", - "organization_placeholder": "Nom de l'entreprise", - "department": "Département", - "department_placeholder": "Département", - "job_title": "Intitulé du poste", - "job_title_placeholder": "p. ex., Ingénieur logiciel", - "role": "Rôle", - "role_placeholder": "p. ex., Chef d'équipe", - "addresses": "Adresses", - "add_address": "Ajouter une adresse", - "street": "Rue", - "city": "Ville", - "region": "État / Région", - "postcode": "Code postal", - "country": "Pays", - "online_services": "Services en ligne", - "add_online_service": "Ajouter un service en ligne", - "url_placeholder": "https://...", - "service_placeholder": "Service", - "anniversaries": "Anniversaires", - "add_anniversary": "Ajouter une date", - "anniversary_birth": "Anniversaire", - "anniversary_wedding": "Anniversaire de mariage", - "anniversary_death": "Commémoration", - "anniversary_other": "Autre", - "personal_info": "Informations personnelles", - "add_personal_info": "Ajouter une entrée", - "personal_info_placeholder": "p. ex., Photographie", - "personal_expertise": "Expertise", - "personal_hobby": "Loisir", - "personal_interest": "Intérêt", - "personal_other": "Autre", - "level": "Niveau", - "level_high": "Élevé", - "level_medium": "Moyen", - "level_low": "Faible", - "categories": "Catégories", - "categories_placeholder": "p. ex., Famille, Amis, Collègues", - "categories_hint": "Tapez pour rechercher ou ajouter", - "category_add": "Ajouter", - "note": "Notes", - "note_placeholder": "Ajouter une note...", - "gender": "Genre", - "gender_sex": "Sexe", - "gender_male": "Masculin", - "gender_female": "Féminin", - "gender_other": "Autre", - "gender_none": "Non applicable", - "gender_unknown": "Inconnu", - "gender_identity": "Identité de genre", - "gender_identity_placeholder": "Identité de genre...", - "calendar": "Calendrier", - "calendar_uri": "URL du calendrier", - "scheduling_uri": "URL de planification", - "freebusy_uri": "URL de disponibilité", - "context_work": "Professionnel", - "context_private": "Personnel", - "add_email": "Ajouter un e-mail", - "add_phone": "Ajouter un téléphone", - "save": "Enregistrer", - "cancel": "Annuler", - "creating": "Création...", - "updating": "Mise à jour...", - "name_required": "Saisissez un prénom, un nom ou une organisation", - "email_invalid": "Veuillez saisir une adresse e-mail valide", - "email_error_inline": "Format d'e-mail invalide", - "save_failed": "Échec de l'enregistrement du contact", - "delete": "Supprimer", - "upload_photo": "Téléverser une photo", - "remove_photo": "Supprimer la photo", - "photo_hint": "JPG ou PNG, jusqu’à 10 Mo. Sera redimensionnée.", - "photo_too_large": "L’image est trop grande (max 10 Mo)", - "photo_invalid": "Fichier image invalide", - "change_photo": "Modifier" - }, - "groups": { - "create": "Nouveau groupe", - "edit": "Modifier le groupe", - "empty": "Aucun groupe", - "delete_confirm_title": "Supprimer le groupe", - "delete_confirm": "Êtes-vous sûr de vouloir supprimer ce groupe ?", - "name_label": "Nom du groupe", - "name_placeholder": "ex. Équipe, Famille", - "name_required": "Le nom du groupe est requis", - "save_failed": "Échec de l'enregistrement du groupe", - "members_label": "Membres", - "search_members": "Rechercher des contacts à ajouter...", - "no_members": "Aucun membre dans ce groupe", - "member_count": "{count, plural, =0 {Aucun membre} one {1 membre} other {# membres}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Importer des contacts", - "drop_hint": "Cliquez pour sélectionner un fichier vCard", - "file_types": "Fichiers .vcf ou .vcard", - "no_contacts": "Aucun contact trouvé dans le fichier", - "parse_error": "Échec de l'analyse du fichier vCard", - "found": "{count, plural, one {1 contact trouvé} other {# contacts trouvés}}", - "duplicate": "Doublon", - "select_all": "Tout sélectionner", - "deselect_all": "Tout désélectionner", - "selected": "{count, plural, one {1 sélectionné} other {# sélectionnés}}", - "import_button": "Importer", - "importing": "Importation...", - "success": "{count, plural, one {1 contact importé} other {# contacts importés}}", - "failed": "Échec de l'importation", - "close": "Fermer", - "file_too_large": "Fichier trop volumineux (max 5 Mo)" - }, - "export": { - "title": "Exporter les contacts", - "success": "{count, plural, one {1 contact exporté} other {# contacts exportés}}" - }, - "bulk": { - "selected": "{count, plural, one {1 sélectionné} other {# sélectionnés}}", - "select_all": "Tout sélectionner", - "delete": "Supprimer", - "delete_confirm_title": "Supprimer les contacts", - "delete_confirm": "Supprimer {count, plural, one {1 contact} other {# contacts}} ?", - "deleted": "{count, plural, one {1 contact supprimé} other {# contacts supprimés}}", - "add_to_group": "Ajouter au groupe", - "choose_group": "Choisir un groupe", - "adding_contacts": "Ajout de {count, plural, one {1 contact} other {# contacts}}", - "added_to_group": "Contacts ajoutés au groupe", - "export": "Exporter", - "clear": "Effacer la sélection" - }, - "toast": { - "created": "Contact créé", - "updated": "Contact mis à jour", - "deleted": "Contact supprimé", - "error_create": "Échec de la création du contact", - "error_update": "Échec de la mise à jour du contact", - "error_delete": "Échec de la suppression du contact" - }, - "context_menu": { - "open": "Ouvrir", - "edit": "Modifier", - "send_email": "Envoyer un courriel", - "add_to_group": "Ajouter au groupe", - "export_vcard": "Exporter en vCard", - "delete": "Supprimer", - "call": "Appeler", - "duplicate": "Dupliquer", - "print": "Imprimer" - }, - "filters": { - "toggle": "Filtres", - "select": "Sélectionner", - "clear": "Effacer", - "close": "Fermer", - "title": "Filtres avancés", - "organization": "Entreprise", - "organization_placeholder": "ex. Acme", - "job_title": "Poste", - "job_title_placeholder": "ex. Designer", - "location": "Lieu", - "location_placeholder": "Ville ou pays", - "email_domain": "Domaine e-mail", - "email_domain_placeholder": "exemple.com", - "birthday_month": "Anniversaire en", - "any_month": "Tous les mois", - "has_email": "Avec e-mail", - "has_phone": "Avec téléphone", - "has_photo": "Avec photo", - "sort_by": "Trier par", - "sort_first_name": "Prénom", - "sort_last_name": "Nom de famille" - }, - "open_categories": "Ouvrir les catégories" - }, - "calendar": { - "title": "Calendrier", - "back_to_email": "Retour aux e-mails", - "back_to_month": "Retour au mois", - "my_calendars": "Mes calendriers", - "birthday_calendar": "Anniversaires", - "mini_calendar_change": "Cliquer pour changer de mois", - "views": { - "month": "Mois", - "week": "Semaine", - "day": "Jour", - "agenda": "Agenda", - "today": "Aujourd'hui", - "month_hint": "Mois (m)", - "week_hint": "Semaine (w)", - "day_hint": "Jour (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Tâches", - "tasks_hint": "Tâches (k)" - }, - "events": { - "create": "Créer un événement", - "edit": "Modifier l'événement", - "delete": "Supprimer l'événement", - "details": "Détails de l'événement", - "no_events": "Aucun événement", - "all_day": "Toute la journée", - "more": "+{count} de plus", - "no_title": "(Sans titre)", - "resize": "Redimensionner l'événement", - "duplicate": "Dupliquer", - "today_header": "Aujourd'hui", - "tomorrow_header": "Demain", - "agenda_show_earlier": "Afficher les événements précédents", - "agenda_loading": "Chargement…", - "agenda_range_start": "Événements à partir du {date}", - "agenda_range_end": "Événements jusqu'au {date}", - "export_ics": "Exporter en .ics", - "copy_title": "Copier le titre", - "copy_link": "Copier le lien de réunion", - "go_to_today": "Aller à aujourd'hui", - "new_all_day_event": "Nouvel événement sur la journée", - "new_event": "Nouvel événement", - "new_task": "Nouvelle tâche" - }, - "detail": { - "add_note": "Ajouter une note...", - "save_note": "Enregistrer", - "note_saved": "Note ajoutée", - "open_link": "Ouvrir le lien", - "meeting_link": "Lien de réunion", - "tentative": "Provisoire", - "cancelled": "Annulé", - "delete_confirm": "Supprimer cet événement ?" - }, - "form": { - "title": "Titre", - "description": "Description", - "location": "Lieu", - "meeting_link": "Lien de réunion", - "start_date": "Date de début", - "end_date": "Date de fin", - "start_time": "Heure de début", - "end_time": "Heure de fin", - "all_day_event": "Événement sur toute la journée", - "calendar_select": "Calendrier", - "save": "Enregistrer", - "cancel": "Annuler", - "delete_confirm": "Êtes-vous sûr de vouloir supprimer cet événement ?", - "color": "Couleur" - }, - "participants": { - "availability": { - "title": "Disponibilité", - "free": "Disponible", - "busy": "Occupé", - "tentative": "Provisoire", - "unknown": "Pas un utilisateur de ce serveur", - "checking": "Vérification…" - }, - "title": "Participants", - "add": "Ajouter un participant", - "organizer": "Organisateur", - "attendee": "Participant", - "accepted": "Accepté", - "declined": "Refusé", - "tentative": "Provisoire", - "needs_action": "En attente de réponse", - "remove": "Retirer", - "edit": "Modifier", - "email_placeholder": "Ajouter une adresse e-mail ou chercher des contacts", - "send_invitations": "Envoyer les invitations aux participants", - "status_summary": "{accepted} accepté(s), {pending} en attente", - "invited_by": "Invitation de {name}", - "respond_below": "Répondez avec les boutons ci-dessous", - "rsvp_label": "Votre réponse", - "cancel_notification": "Les participants seront informés de l'annulation", - "you_organizer": "Vous êtes l'organisateur", - "you_attendee": "Vous êtes un participant", - "no_participants": "Aucun participant", - "count": "{count, plural, one {# participant} other {# participants}}" - }, - "recurrence": { - "title": "Récurrence", - "none": "Ne se répète pas", - "daily": "Quotidien", - "weekly": "Hebdomadaire", - "monthly": "Mensuel", - "yearly": "Annuel", - "every_n_days": "Tous les {count} jours", - "every_n_weeks": "Toutes les {count} semaines", - "every_n_months": "Tous les {count} mois", - "until": "Jusqu'au", - "occurrences": "{count} occurrences", - "custom": "Personnalisé…", - "edit_custom": "Modifier la récurrence personnalisée", - "every_n_years": "Tous les {count} ans", - "on_days": "le {days}", - "on_day_n": "le jour {day}", - "on_the_nth": "le {nth} {day}", - "in_month": "en {month}", - "nth_1": "premier", - "nth_2": "deuxième", - "nth_3": "troisième", - "nth_4": "quatrième", - "nth_last": "dernier", - "editor_freq_day": "Jour", - "editor_freq_week": "Semaine", - "editor_freq_month": "Mois", - "editor_freq_year": "Année", - "editor_repeats_on": "Se répète", - "editor_every": "Tous les", - "editor_unit_days": "jour(s)", - "editor_unit_weeks": "semaine(s)", - "editor_unit_months": "mois", - "editor_unit_years": "an(s)", - "editor_on_day": "le jour", - "editor_on_the": "le", - "editor_in": "en", - "editor_ends": "Se termine", - "editor_never": "Jamais", - "editor_ends_on": "Le", - "editor_ends_after": "Après", - "editor_occurrences": "occurrences" - }, - "recurrence_scope": { - "edit_title": "Modifier l'événement récurrent", - "delete_title": "Supprimer l'événement récurrent", - "description": "Ceci est un événement récurrent. Quels événements souhaitez-vous modifier ?", - "this_event": "Cet événement uniquement", - "this_and_future": "Cet événement et les suivants", - "all_events": "Tous les événements", - "cancel": "Annuler", - "save": "Enregistrer", - "delete": "Supprimer", - "rsvp_title": "Répondre à l'événement récurrent", - "rsvp_description": "Ceci est un événement récurrent. À quels événements votre réponse s'applique-t-elle ?", - "respond": "Répondre" - }, - "alerts": { - "title": "Rappel", - "none": "Aucun rappel", - "at_time": "Au moment de l'événement", - "minutes_before": "{count, plural, one {# minute avant} other {# minutes avant}}", - "hours_before": "{count, plural, one {# heure avant} other {# heures avant}}", - "days_before": "{count, plural, one {# jour avant} other {# jours avant}}", - "weeks_before": "{count, plural, one {# semaine avant} other {# semaines avant}}", - "unit_minutes_before": "minutes avant", - "unit_hours_before": "heures avant", - "unit_days_before": "jours avant", - "unit_weeks_before": "semaines avant", - "add": "Ajouter un rappel", - "remove": "Supprimer le rappel", - "amount": "Quantité de rappel", - "unit": "Unité de rappel" - }, - "settings": { - "title": "Paramètres du calendrier", - "default_view": "Vue par défaut", - "week_starts_on": "La semaine commence le", - "time_format": "Format de l'heure", - "default_calendar": "Calendrier par défaut", - "default_reminder": "Rappel par défaut", - "time_format_12h": "12 heures", - "time_format_24h": "24 heures", - "notifications_enabled": "Notifications d'événements", - "notifications_enabled_desc": "Afficher les alertes pour les événements à venir", - "notification_sound": "Son de notification", - "notification_sound_desc": "Jouer un son pour les alertes de calendrier", - "invitation_parsing": "Analyser les invitations par e-mail", - "invitation_parsing_desc": "Détecter les invitations de calendrier dans les pièces jointes des e-mails et afficher les actions du calendrier", - "show_time_in_month_view": "Afficher l'heure dans la vue mensuelle", - "show_time_in_month_view_desc": "Afficher les heures des événements dans la vue mensuelle du calendrier", - "show_week_numbers": "Afficher les numéros de semaine", - "show_week_numbers_desc": "Afficher les numéros de semaine dans le mini-calendrier", - "calendar_free_scroll": "Défilement libre", - "calendar_free_scroll_desc": "Faire défiler les mois, semaines et jours en continu au lieu d'une période à la fois", - "enable_tasks": "Activer les tâches", - "enable_tasks_desc": "Afficher une vue des tâches dans le calendrier pour gérer les choses à faire", - "show_tasks_on_calendar": "Afficher les tâches dans le calendrier", - "show_tasks_on_calendar_desc": "Afficher les pastilles des tâches dans les vues jour et semaine du calendrier", - "hover_preview": "Aperçu au survol", - "hover_preview_desc": "Afficher un popover de détails au survol des événements du calendrier", - "hover_preview_instant": "Instantané", - "hover_preview_delay_500ms": "Délai de 0,5 seconde", - "hover_preview_delay_1s": "Délai de 1 seconde", - "hover_preview_delay_2s": "Délai de 2 secondes", - "hover_preview_off": "Désactivé", - "organizer_identity": "Organiser les invitations en tant que", - "organizer_identity_desc": "L'adresse d'agenda utilisée comme organisateur lorsque vous invitez des personnes à des événements.", - "organizer_identity_failed": "Impossible de modifier l'adresse de l'organisateur", - "show_birthday_calendar": "Calendrier des anniversaires", - "show_birthday_calendar_desc": "Afficher un calendrier virtuel avec les anniversaires de vos contacts" - }, - "days": { - "monday": "Lundi", - "tuesday": "Mardi", - "wednesday": "Mercredi", - "thursday": "Jeudi", - "friday": "Vendredi", - "saturday": "Samedi", - "sunday": "Dimanche", - "mon": "Lun", - "tue": "Mar", - "wed": "Mer", - "thu": "Jeu", - "fri": "Ven", - "sat": "Sam", - "sun": "Dim" - }, - "months": { - "jan": "janv.", - "feb": "févr.", - "mar": "mars", - "apr": "avr.", - "may": "mai", - "jun": "juin", - "jul": "juil.", - "aug": "août", - "sep": "sept.", - "oct": "oct.", - "nov": "nov.", - "dec": "déc.", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Événement créé", - "event_updated": "Événement mis à jour", - "event_deleted": "Événement supprimé", - "calendar_created": "Calendrier créé", - "calendar_deleted": "Calendrier supprimé", - "event_move_error": "Échec du déplacement de l'événement", - "event_resize_error": "Échec du redimensionnement de l'événement", - "alert_title": "Événement à venir", - "alert_now": "Commence maintenant", - "alert_in_minutes": "Dans {count} min", - "invitation_sent": "Invitations envoyées", - "invitations_denied_title": "Impossible d'envoyer les invitations", - "invitations_denied": "Le serveur a refusé d'envoyer les invitations : {reason}. Enregistrer l'événement sans les envoyer ?", - "save_without_invitations": "Enregistrer sans invitations", - "rsvp_updated": "Réponse mise à jour", - "rsvp_error": "Échec de la mise à jour de la réponse", - "event_duplicated": "Événement dupliqué", - "event_error": "Échec de l'enregistrement de l'événement", - "task_due": "Tâche à échéance", - "event_exported": "Événement exporté", - "title_copied": "Titre copié", - "link_copied": "Lien copié" - }, - "status": { - "loading_calendars": "Chargement des calendriers...", - "loading_events": "Chargement des événements..." - }, - "quick_create": { - "placeholder": "Titre du nouvel événement", - "aria_label": "Création rapide d'événement" - }, - "nav_prev": "Précédent", - "nav_next": "Suivant", - "import": { - "title": "Importer un calendrier", - "tab_file": "Fichier", - "tab_url": "URL", - "select_file": "Sélectionner un fichier .ics", - "drop_file": "ou déposez le fichier ici", - "supported_formats": "Fichiers iCalendar (.ics) supportés", - "url_description": "Entrez l'URL d'un flux iCalendar (.ics) externe pour importer des événements.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Prend en charge les URLs iCalendar (.ics)", - "fetch": "Récupérer", - "invalid_url": "Veuillez entrer une URL valide", - "url_fetch_failed": "Impossible de récupérer le calendrier depuis l'URL", - "parsing": "Analyse du fichier en cours...", - "parsed_events": "{count} événements trouvés", - "no_events": "Aucun événement trouvé dans le fichier", - "select_all": "Tout sélectionner", - "deselect_all": "Tout désélectionner", - "target_calendar": "Importer dans le calendrier", - "import_button": "Importer la sélection", - "importing": "Importation en cours...", - "success": "{count} événements importés avec succès", - "error": "Échec de l'importation du calendrier", - "file_too_large": "Le fichier dépasse la limite de 10 Mo", - "invalid_format": "Format de fichier calendrier invalide" - }, - "webcal_action": { - "title": "Ouvrir le lien de calendrier", - "description": "Comment souhaitez-vous utiliser \"{name}\" ?", - "import_title": "Importer une fois", - "import_description": "Récupérer les événements maintenant et les copier dans l'un de vos calendriers.", - "subscribe_title": "S'abonner", - "subscribe_description": "Synchroniser automatiquement ce calendrier comme calendrier séparé.", - "cancel": "Annuler" - }, - "management": { - "title": "Gestion des calendriers", - "description": "Créez, renommez et personnalisez vos calendriers. Clic droit sur un calendrier dans la barre latérale pour changer rapidement sa couleur.", - "name": "Nom", - "name_placeholder": "Nom du calendrier", - "color": "Couleur", - "change_color": "Changer la couleur", - "random_color": "Nouvelle couleur aléatoire", - "add_calendar": "Ajouter un calendrier", - "edit": "Modifier", - "delete": "Supprimer", - "save": "Enregistrer", - "create": "Créer", - "cancel": "Annuler", - "default": "Par défaut", - "confirm_delete": "Supprimer \"{name}\" ? Tous les événements de ce calendrier seront supprimés.", - "calendar_created": "Calendrier créé", - "calendar_updated": "Calendrier mis à jour", - "calendar_deleted": "Calendrier supprimé", - "color_updated": "Couleur du calendrier mise à jour", - "error_create": "Échec de la création du calendrier", - "error_update": "Échec de la mise à jour du calendrier", - "error_delete": "Échec de la suppression du calendrier", - "caldav_url": "URL CalDAV", - "copy_url": "Copier l'URL CalDAV", - "url_copied": "URL CalDAV copiée dans le presse-papiers", - "confirm_clear": "Supprimer tous les événements de \"{name}\" ? Cette action est irréversible.", - "set_default": "Définir par défaut", - "default_updated": "Calendrier par défaut mis à jour", - "error_default": "Échec de la définition du calendrier par défaut", - "clear_events": "Supprimer les événements", - "events_cleared": "{count} événements supprimés", - "error_clear": "Impossible de supprimer les événements du calendrier", - "share": "Partager le calendrier", - "new_event_in_calendar": "Nouvel événement dans ce calendrier", - "kind": "Contenu", - "kind_events": "Événements", - "kind_tasks": "Tâches", - "kind_both": "Événements et tâches", - "kind_hint": "Détermine quelles applications voient ce calendrier lorsqu'il est synchronisé via CalDAV. Ne peut pas être modifié par la suite." - }, - "subscription": { - "title": "Abonnement iCal", - "section_title": "Abonnements iCal", - "description": "Abonnez-vous à un flux iCalendar externe. Les événements seront synchronisés automatiquement dans leur propre calendrier. Prend en charge les URL https:// et webcal://.", - "url_label": "URL du calendrier", - "url_placeholder": "https://example.com/calendar.ics ou webcal://...", - "name_label": "Nom du calendrier", - "name_placeholder": "ex. Jours fériés", - "color_label": "Couleur", - "refresh_interval": "Intervalle de rafraîchissement", - "interval_15": "Toutes les 15 minutes", - "interval_30": "Toutes les 30 minutes", - "interval_60": "Toutes les heures", - "interval_360": "Toutes les 6 heures", - "interval_1440": "Tous les jours", - "subscribe": "S'abonner", - "subscribing": "Abonnement en cours...", - "save": "Enregistrer les modifications", - "saving": "Enregistrement...", - "edit": "Modifier", - "edit_title": "Modifier l'abonnement", - "updated": "\"{name}\" mis à jour", - "update_error": "Échec de la mise à jour de l'abonnement", - "invalid_url": "Veuillez entrer une URL valide", - "success": "Abonné à \"{name}\"", - "error": "Impossible d'ajouter l'abonnement", - "refresh": "Rafraîchir maintenant", - "refresh_success": "Abonnement rafraîchi", - "refresh_error": "Impossible de rafraîchir l'abonnement", - "unsubscribe": "Se désabonner", - "confirm_delete": "Se désabonner de \"{name}\" ? Le calendrier et tous ses événements seront supprimés.", - "deleted": "Abonnement supprimé", - "delete_error": "Impossible de supprimer l'abonnement", - "last_refreshed": "Dernière mise à jour : {time}" - }, - "tasks": { - "label": "Tâches", - "no_tasks": "Aucune tâche", - "no_title": "(Sans titre)", - "mark_complete": "Marquer comme terminée", - "mark_incomplete": "Marquer comme non terminée", - "filter_all": "Toutes", - "filter_pending": "En attente", - "filter_completed": "Terminées", - "filter_overdue": "En retard", - "show_completed": "Afficher les tâches terminées", - "create": "Nouvelle tâche", - "edit": "Modifier la tâche", - "title_placeholder": "Titre de la tâche", - "description_placeholder": "Ajouter une description...", - "due_date": "Date d'échéance", - "include_time": "Inclure l'heure", - "priority": "Priorité", - "priority_none": "Aucune", - "priority_high": "Haute", - "priority_medium": "Moyenne", - "priority_low": "Basse", - "progress": "Statut", - "progress_needs_action": "Action requise", - "progress_in_process": "En cours", - "progress_completed": "Terminée", - "progress_cancelled": "Annulée", - "calendar": "Calendrier", - "alert": "Rappel", - "alert_none": "Aucun", - "alert_at_time": "À la date et à l'heure d'échéance", - "alert_5min": "5 minutes avant", - "alert_15min": "15 minutes avant", - "alert_30min": "30 minutes avant", - "alert_1hr": "1 heure avant", - "alert_1day": "1 jour avant", - "delete": "Supprimer", - "cancel": "Annuler", - "save": "Enregistrer", - "quick_add_placeholder": "Ajouter une tâche...", - "due_today": "Échéance aujourd'hui", - "due_tomorrow": "Échéance demain", - "overdue": "En retard" - }, - "nav_open_menu": "Ouvrir le menu" - }, - "advanced_search": { - "title": "Recherche avancée", - "from": "De", - "from_placeholder": "Email ou nom de l'expéditeur", - "to": "À", - "to_placeholder": "Email ou nom du destinataire", - "subject": "Objet", - "subject_placeholder": "L'objet contient...", - "body": "Corps", - "body_placeholder": "Le corps contient...", - "folder": "Dossier", - "all_folders": "Tous les dossiers", - "has_attachment": "Pièces jointes", - "date_after": "Après le", - "date_before": "Avant le", - "size_min": "Plus grand que (Ko)", - "size_max": "Plus petit que (Ko)", - "starred": "Suivis", - "unread": "Non lus", - "read": "Lus", - "yes": "Oui", - "no": "Non", - "clear": "Effacer", - "clear_all": "Tout effacer", - "filters_active": "{count} filtre", - "filters_active_plural": "{count} filtres", - "toggle_filters": "Filtres", - "search_hint": "Utilisez les filtres avancés pour une recherche précise", - "advanced_filters_tooltip": "Filtres de recherche avancés", - "results_found": "{count, plural, =0 {Aucun résultat trouvé} one {# résultat trouvé} other {# résultats trouvés}}", - "results_found_more": "{count}+ résultats trouvés", - "suggestions_recent": "Recherches récentes", - "suggestions_people": "Personnes", - "suggestions_from": "Rechercher les e-mails de {address}", - "suggestions_to": "Rechercher les e-mails à {address}", - "suggestions_remove_recent": "Retirer des recherches récentes" - }, - "welcome": { - "title": "Bienvenue dans votre boite mail", - "tip_compose": "Appuyez sur c pour composer un nouvel email", - "tip_shortcuts": "Appuyez sur ? pour voir tous les raccourcis clavier", - "tip_sidebar": "Retrouvez le Calendrier, les Contacts et les Parametres dans le menu lateral", - "tip_settings": "Personnalisez votre experience dans les Parametres", - "got_it": "Compris", - "settings": "Paramètres", - "dismiss": "Fermer", - "start_tour": "Démarrer la visite" - }, - "demo_welcome": { - "title": "Bienvenue sur Bulwark Mail", - "description": "Explorez un client webmail complet - directement dans votre navigateur. Toutes les données restent sur votre appareil, alors testez tout.", - "feature_email": "Lire et rédiger des e-mails", - "feature_organize": "Tags, étoiles et dossiers", - "feature_shortcuts": "Raccourcis clavier", - "feature_privacy": "Démo 100 % privée", - "hint": "Cliquez sur un e-mail à gauche pour commencer, ou lancez la visite." - }, - "files": { - "office_edit": "Modifier le document", - "office_loading": "Ouverture de l'éditeur de documents…", - "office_error": "L'éditeur de documents n'a pas pu être ouvert.", - "office_close": "Fermer l'éditeur", - "open_in_new_tab": "Ouvrir dans un nouvel onglet", - "title": "Fichiers", - "search_placeholder": "Rechercher des fichiers...", - "empty_state_title": "Aucun fichier pour le moment", - "empty_state_description": "Téléversez des fichiers ou créez des dossiers pour commencer", - "upload": "Téléverser", - "upload_files": "Téléverser des fichiers", - "new_folder": "Nouveau dossier", - "new_folder_name": "Nom du dossier", - "rename": "Renommer", - "rename_title": "Renommer", - "new_name": "Nouveau nom", - "name_forbidden_chars": "Le serveur n'autorise pas ces caractères dans les noms : {chars}", - "name_reserved": "Ce nom est réservé sur le serveur.", - "delete": "Supprimer", - "delete_confirm_title": "Supprimer la ressource", - "delete_confirm_message": "Êtes-vous sûr de vouloir supprimer \"{name}\" ? Cette action est irréversible.", - "download": "Télécharger", - "name": "Nom", - "size": "Taille", - "modified": "Modifié", - "type": "Type", - "folder": "Dossier", - "file": "Fichier", - "parent_directory": "Répertoire parent", - "breadcrumb_root": "Accueil", - "drop_files_here": "Déposez des fichiers ou dossiers ici pour les téléverser", - "uploading": "Téléversement en cours...", - "upload_success": "{count, plural, one {1 fichier téléversé} other {# fichiers téléversés}}", - "upload_error": "Échec du téléversement du fichier", - "create_folder_success": "Dossier créé", - "create_folder_error": "Échec de la création du dossier", - "delete_success": "Supprimé avec succès", - "delete_error": "Échec de la suppression", - "rename_success": "Renommé avec succès", - "rename_error": "Échec du renommage", - "download_error": "Échec du téléchargement", - "not_available": "Le stockage de fichiers n'est pas disponible sur ce serveur", - "cancel": "Annuler", - "create": "Créer", - "save": "Enregistrer", - "no_results": "Aucun fichier ne correspond à votre recherche", - "batch_delete_confirm_message": "Êtes-vous sûr de vouloir supprimer {count, plural, one {1 élément} other {# éléments}} ? Cette action est irréversible.", - "batch_delete_success": "{count, plural, one {1 élément supprimé} other {# éléments supprimés}}", - "grid_view": "Vue en grille", - "list_view": "Vue en liste", - "details": "Détails", - "path": "Chemin", - "preview": "Aperçu", - "preview_error": "Échec du chargement de l'aperçu", - "cut": "Couper", - "copy": "Copier", - "paste": "Coller", - "move_success": "{count, plural, one {1 élément déplacé} other {# éléments déplacés}}", - "move_error": "Échec du déplacement", - "paste_success": "Collé avec succès", - "paste_error": "Échec du collage", - "new_text_file": "Nouveau fichier texte", - "file_name": "Nom du fichier", - "retry": "Réessayer", - "refresh": "Actualiser", - "toggle_favorite": "Basculer le favori", - "duplicate": "Dupliquer", - "duplicate_success": "Dupliqué avec succès", - "duplicate_error": "Échec de la duplication", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Fichier créé", - "create_file_error": "Échec de la création du fichier", - "favorites": "Favoris", - "recent": "Récents", - "properties": "Propriétés", - "open_folder": "Ouvrir le dossier", - "upload_folder": "Téléverser un dossier", - "file_too_large": "\"{name}\" dépasse la taille maximale du fichier ({max})", - "undo": "Annuler", - "undo_success": "Action annulée", - "undo_error": "Échec de l'annulation", - "toolbar": "Actions sur les fichiers", - "file_list": "Fichiers et dossiers", - "context_menu": "Actions", - "settings_title": "Paramètres des fichiers", - "settings_display": "Affichage", - "settings_default_view": "Vue par défaut", - "settings_default_view_desc": "Choisissez entre la disposition en grille et en liste", - "settings_default_sort": "Tri par défaut", - "settings_default_sort_desc": "Choisissez le tri par défaut pour les fichiers", - "settings_sort_direction": "Sens du tri", - "settings_sort_direction_desc": "Choisissez l'ordre croissant ou décroissant", - "settings_ascending": "Croissant", - "settings_descending": "Décroissant", - "settings_icons": "Icônes", - "settings_show_icons": "Afficher les icônes", - "settings_show_icons_desc": "Afficher les icônes à côté des fichiers et dossiers", - "settings_colored_icons": "Icônes colorées", - "settings_colored_icons_desc": "Utiliser des icônes colorées au lieu de monochromes", - "settings_show_thumbnails": "Afficher les miniatures", - "settings_show_thumbnails_desc": "Afficher les aperçus d'images au lieu des icônes", - "settings_behavior": "Comportement", - "settings_show_hidden": "Afficher les fichiers cachés", - "settings_show_hidden_desc": "Afficher les fichiers et dossiers commençant par un point", - "settings_folder_layout": "Navigation des dossiers", - "settings_folder_layout_desc": "Choisissez comment les dossiers sont affichés : intégrés avec les fichiers ou dans une arborescence latérale", - "settings_folder_layout_inline": "Intégré", - "settings_folder_layout_sidebar": "Barre latérale", - "disabled_title": "La fonctionnalité Fichiers a été désactivée par votre administrateur", - "disabled_description": "Les téléchargements de fichiers volumineux via WebDAV peuvent provoquer une instabilité de Stalwart/RocksDB, y compris des crashs de mémoire et une utilisation irrécupérable du disque. Les fichiers supprimés peuvent ne pas être immédiatement purgés du stockage. Cette fonctionnalité n'est pas recommandée pour les environnements de production.", - "stability_warning": "Les téléchargements de fichiers volumineux peuvent provoquer une instabilité du serveur. Les fichiers supprimés peuvent ne pas être immédiatement purgés du stockage. À utiliser avec prudence.", - "no_accounts": "Aucun compte connecté.", - "open_folder_tree": "Ouvrir l'arborescence des dossiers", - "other_accounts": "Autres comptes", - "migration_title": "Mise à jour de vos fichiers…", - "migration_description": "Organisation des dossiers et fichiers dans leur structure appropriée. Cela ne se produit qu'une seule fois." - }, - "smime": { - "your_certificates": "Vos certificats", - "your_certificates_desc": "Importez et gérez vos certificats S/MIME pour signer et chiffrer les e-mails", - "recipient_certificates": "Certificats des destinataires", - "recipient_certificates_desc": "Certificats publics pour chiffrer les e-mails destinés aux destinataires", - "identity_bindings": "Liaisons identité-clé", - "identity_bindings_desc": "Associez des certificats S/MIME à vos identités de messagerie", - "defaults_title": "Valeurs par défaut", - "defaults_desc": "Configurez le comportement par défaut de signature et de chiffrement", - "import_pkcs12": "Importer PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importer un certificat", - "no_certificates": "Aucun certificat importé pour le moment", - "no_recipient_certs": "Aucun certificat destinataire", - "expires": "Expire le", - "expired": "Expiré", - "bound_to": "Lié à", - "no_key_bound": "Aucun", - "lock": "Verrouiller la clé", - "unlock": "Déverrouiller la clé", - "details": "Voir les détails", - "delete": "Supprimer", - "encrypt_by_default": "Chiffrer par défaut", - "encrypt_by_default_desc": "Chiffrer automatiquement les e-mails lorsque tous les destinataires ont un certificat", - "remember_unlocked": "Mémoriser les clés déverrouillées", - "remember_unlocked_desc": "Conserver les clés déverrouillées pendant cette session du navigateur", - "sign_default_for": "Signer par défaut pour", - "enter_p12_passphrase": "Entrer la phrase secrète PKCS#12", - "p12_passphrase_desc": "Entrez la phrase secrète qui protège ce fichier de certificat", - "enter_storage_passphrase": "Définir la phrase secrète de stockage", - "storage_passphrase_desc": "Choisissez une phrase secrète pour protéger cette clé dans votre navigateur", - "next": "Suivant", - "import": "Importer", - "unlock_key": "Déverrouiller la clé", - "unlock_key_desc": "Entrez la phrase secrète de stockage pour déverrouiller cette clé pour la signature ou le déchiffrement", - "passphrase_placeholder": "Entrez la phrase secrète", - "confirm_passphrase_placeholder": "Confirmez la phrase secrète", - "passphrase_mismatch": "Les phrases secrètes ne correspondent pas", - "cancel": "Annuler", - "processing": "Traitement…", - "close": "Fermer", - "certificate_details": "Détails du certificat", - "cert_subject": "Sujet", - "cert_issuer": "Émetteur", - "cert_email": "E-mail", - "cert_serial": "Numéro de série", - "cert_validity": "Validité", - "cert_fingerprint": "Empreinte (SHA-256)", - "cert_algorithm": "Algorithme", - "cert_capabilities": "Capacités", - "cert_source": "Source", - "cert_expired": "Ce certificat a expiré", - "cert_not_yet_valid": "Ce certificat n'est pas encore valide", - "cap_sign": "Signature", - "cap_encrypt": "Chiffrement", - "cap_none": "Aucune", - "show_passphrase": "Afficher la phrase secrète", - "hide_passphrase": "Masquer la phrase secrète", - "sign_toggle": "Signer", - "encrypt_toggle": "Chiffrer", - "missing_recipient_certs": "Certificats manquants pour : {emails}", - "missing_sender_cert": "Aucun certificat n'est lié à cette identité", - "status_encrypted_ok": "Ce message a été chiffré", - "status_encrypted_no_key": "Ce message est chiffré, mais aucune clé correspondante n'a été trouvée", - "status_encrypted_failed": "Impossible de déchiffrer ce message", - "status_signed_valid": "Signature vérifiée", - "status_signed_invalid": "Échec de la vérification de la signature", - "status_signed_expired_cert": "Signé avec un certificat expiré", - "status_signed_self_signed": "Signature valide, mais le certificat est auto-signé (non fiable)", - "status_signed_mismatch": "La signature est valide, mais le signataire ne correspond pas à l'expéditeur", - "status_unsupported": "Format S/MIME non pris en charge", - "auto_import_signer_certs": "Importer automatiquement les certificats des signataires", - "auto_import_signer_certs_desc": "Enregistrer automatiquement les certificats des e-mails signés vérifiés pour un futur chiffrement", - "export": "Exporter", - "enter_export_passphrase": "Définir la phrase secrète d'export", - "export_passphrase_desc": "Choisissez une phrase secrète pour protéger le fichier PKCS#12 exporté", - "export_storage_desc": "Entrez la phrase secrète de stockage pour déchiffrer la clé avant l'export", - "incorrect_passphrase": "Phrase secrète incorrecte" - }, - "tour": { - "step_counter": "Étape {current} sur {total}", - "skip": "Passer la visite", - "back": "Retour", - "next": "Suivant", - "finish": "Terminer", - "take_a_tour": "Faire une visite de l'interface", - "restart_title": "Visite d'introduction", - "restart_desc": "Rejouer la visite guidée de l'interface", - "restart_button": "Relancer la visite", - "show_on_new_devices_title": "Afficher sur les nouveaux appareils", - "show_on_new_devices_desc": "Rejouer la bannière d'accueil et la visite lors de votre première connexion sur un nouvel appareil, même si vous les avez déjà terminées ailleurs", - "sidebar_title": "Vos boîtes mail", - "sidebar_desc": "Voici votre barre latérale de dossiers. Cliquez sur une boîte pour voir ses emails. Vous pouvez créer des dossiers, glisser des emails entre eux et voir les compteurs de non lus.", - "compose_title": "Rédiger un email", - "compose_desc": "Cliquez ici pour écrire un nouvel email. Ajoutez des destinataires, des pièces jointes et utilisez la mise en forme enrichie.", - "search_title": "Rechercher vos emails", - "search_desc": "Recherchez par expéditeur, objet ou contenu. Cliquez sur l'icône de filtre pour les options avancées comme la plage de dates, les pièces jointes et les messages suivis.", - "email_list_title": "Votre liste d'emails", - "email_list_desc": "Les emails apparaissent ici. Cliquez sur un email pour le lire à droite. Utilisez la case à cocher pour en sélectionner plusieurs, puis déplacez, supprimez ou étiquetez-les.", - "email_viewer_title": "Panneau de lecture", - "email_viewer_desc": "L'email sélectionné s'ouvre ici. Répondez, transférez, archivez ou supprimez avec les boutons de la barre d'outils. Vous pouvez aussi marquer les emails ou ajouter des étiquettes de couleur.", - "keywords_title": "Étiquettes de couleur", - "keywords_desc": "Organisez vos emails avec des étiquettes colorées. Glissez un email sur une étiquette pour le marquer, ou faites un clic droit pour assigner des étiquettes.", - "calendar_title": "Calendrier", - "calendar_desc": "Accédez au calendrier pour gérer vos événements. Créez des événements, définissez des rappels et consultez les vues jour, semaine ou mois.", - "contacts_title": "Contacts", - "contacts_desc": "Votre carnet d'adresses se trouve ici. Importez des contacts, créez des groupes et cliquez sur un contact pour voir ses détails.", - "settings_title": "Paramètres", - "settings_desc": "Personnalisez tout : thème, densité, signatures, filtres, raccourcis clavier, paramètres du calendrier et plus encore.", - "shortcuts_title": "Raccourcis clavier", - "shortcuts_desc": "Les utilisateurs avancés adorent ça. Appuyez sur ? à tout moment pour voir tous les raccourcis disponibles. Naviguez, rédigez et gérez vos emails sans toucher à la souris.", - "compose_open_title": "Le compositeur", - "compose_open_desc": "Voici le compositeur d'e-mail. Ajoutez des destinataires, rédigez votre message, joignez des fichiers et utilisez la mise en forme enrichie. Vous pouvez aussi sauvegarder des brouillons et utiliser des modèles.", - "calendar_view_title": "Votre calendrier", - "calendar_view_desc": "Voici votre calendrier avec des événements exemples. Basculez entre les vues jour, semaine, mois et agenda avec la barre d'outils.", - "create_event_title": "Créer un événement", - "create_event_desc": "Cliquez sur ce bouton pour créer un nouvel événement. Vous pouvez définir un titre, une date, une heure et ajouter des participants.", - "event_modal_title": "Détails de l'événement", - "event_modal_desc": "Voici le formulaire de l'événement. Remplissez le titre, choisissez une date et une heure, ajoutez un lieu ou des participants. Cliquez sur enregistrer quand vous avez terminé - ou fermez-le et passez à autre chose.", - "contacts_list_title": "Vos contacts", - "contacts_list_desc": "Voici vos contacts. Cliquez sur un contact pour voir ses détails à droite. Vous pouvez aussi créer de nouveaux contacts, importer des vCards ou organiser les contacts en groupes.", - "files_title": "Stockage de fichiers", - "settings_tabs_title": "Menu des paramètres", - "settings_tabs_desc": "Voici toutes les catégories de paramètres. Personnalisez l'apparence, gérez les identités, configurez les filtres de messagerie, paramétrez votre calendrier et bien plus encore.", - "files_desc": "Votre gestionnaire de fichiers vous permet de téléverser, organiser et partager des fichiers - comme un cloud personnel intégré à votre messagerie.", - "demo_banner_title": "Contrôles de démo", - "demo_banner_desc": "Vous êtes en mode démo - tout reste dans votre navigateur. Cliquez sur 'Réinitialiser la démo' à tout moment pour repartir avec des données fraîches.", - "quota_title": "Utilisation du stockage", - "quota_desc": "Suivez la taille de votre boîte mail ici. Le cercle se remplit au fur et à mesure que vous utilisez plus d'espace." - }, - "unified_mailbox": { - "search_unavailable": "La recherche n'est pas disponible dans la vue unifiée" - }, - "calendar_event_notifications": { - "someone": "Quelqu'un", - "invited": "{name} vous a invité à « {title} »", - "updated": "{name} a mis à jour « {title} »", - "cancelled": "{name} a annulé « {title} »", - "untitled": "Événement sans titre" - }, - "share_notifications": { - "someone": "Quelqu'un", - "shared": "{name} a partagé {kind} « {object} » avec vous", - "changed": "{name} a modifié votre accès à {kind} « {object} »", - "revoked": "{name} a retiré votre accès à {kind} « {object} »", - "object": { - "folder": "le dossier", - "calendar": "l'agenda", - "address_book": "le carnet d'adresses", - "files": "le dossier de fichiers" - } - }, - "sharing": { - "title": "Partager « {name} »", - "description": "Accordez l'accès à d'autres utilisateurs ou groupes de ce serveur. Les modifications sont immédiates.", - "no_shares": "Pas encore partagé.", - "add_person": "Ajouter une personne ou un groupe", - "search_placeholder": "Rechercher par nom ou e-mail…", - "loading_principals": "Chargement des utilisateurs…", - "no_principals": "Aucun autre utilisateur ou groupe trouvé.", - "no_match": "Aucun résultat.", - "remove": "Révoquer l'accès", - "group": "Groupe", - "share_added": "Accès accordé", - "share_updated": "Accès mis à jour", - "share_removed": "Accès révoqué", - "share_failed": "Échec de la mise à jour du partage", - "preset": { - "freeBusy": "Disponibilité uniquement", - "read": "Lecture seule", - "readWrite": "Lecture & écriture", - "manager": "Gestionnaire", - "custom": "Personnalisé" - } - }, - "quote_header": { - "reply_line": "Le {date}, {from} a écrit :", - "forwarded_separator": "---------- Message transféré ----------", - "from_label": "De", - "date_label": "Date", - "subject_label": "Sujet" - }, - "pwa_install": { - "title": "Installer {appName}", - "description": "Installez notre application pour un accès rapide et un support hors ligne.", - "not_now": "Pas maintenant", - "install": "Installer", - "dont_remind": "Ne plus me le rappeler", - "dismiss_aria": "Fermer l'invite d'installation" - }, - "deep_link": { - "message_not_found": "Ce message n'est plus disponible.", - "thread_not_found": "Cette conversation n'est plus disponible.", - "folder_not_found": "Ce dossier n'est plus disponible.", - "event_not_found": "Cet événement n'est plus disponible.", - "file_not_found": "Ce fichier n'est plus disponible.", - "account_unavailable": "Connectez-vous à ce compte pour ouvrir ce lien.", - "copy_message": "Copier le lien du message", - "copy_conversation": "Copier le lien de la conversation", - "copy_event": "Copier le lien de l'événement", - "copied": "Lien copié", - "copy_failed": "Impossible de copier le lien" - }, - "plugins": { - "oauth_callback": { - "completing": "Finalisation de l'autorisation…", - "completing_message": "Veuillez patienter pendant l'établissement de la connexion.", - "received_title": "Autorisation reçue", - "received_message": "Vous pouvez revenir à l'onglet où vous avez commencé la connexion.", - "failed_title": "Échec de l'autorisation", - "denied": "Lautorisation a été refusée.", - "failed_detail": "Échec de lautorisation : {error}", - "missing_params": "Code dautorisation ou état manquant.", - "delivery_failed": "Le résultat de lautorisation na pas pu être transmis à longlet de lapplication. Fermez cette fenêtre et réessayez." - } - }, - "global_search": { - "title": "Tout rechercher", - "placeholder": "Rechercher courriels, contacts, calendrier, fichiers...", - "scope_label": "Rechercher dans", - "account_label": "Compte", - "scope_all": "Tout", - "scope_mail": "Messagerie", - "scope_contacts": "Contacts", - "scope_calendar": "Calendrier", - "scope_files": "Fichiers", - "all_accounts": "Tous les comptes", - "show_all": "Afficher tous les résultats", - "no_results": "Aucun résultat pour « {query} »", - "server_hint": "Les mots complets interrogent le serveur ; les mots partiels correspondent aux éléments déjà chargés", - "searching": "Recherche...", - "account_error": "Impossible de rechercher dans {account}", - "recent_searches": "Recherches récentes", - "recurring": "Récurrent", - "no_subject": "(no subject)", - "empty_hint": "Recherchez courriels, contacts, événements et fichiers sur tous les comptes. Essayez from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "À plat par date", - "grouped": "Groupé", - "load_more": "Charger plus", - "preview_empty": "Sélectionnez un résultat pour l'aperçu", - "open_full": "Ouvrir", - "view_label": "Affichage" - } -} +{ + "meta_description": "Client de messagerie web minimaliste utilisant le protocole JMAP", + "login": { + "title": "Webmail", + "username_label": "Email", + "username_placeholder": "utilisateur@exemple.com", + "password_label": "Mot de passe", + "password_placeholder": "Entrez votre mot de passe", + "jmap_endpoint_label": "Serveur JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Le serveur doit autoriser les requêtes CORS depuis ce domaine.", + "jmap_server_label": "Serveur", + "jmap_server_auto_picked": "Serveur sélectionné à partir de votre domaine de messagerie.", + "sign_in": "Se connecter", + "signing_in": "Connexion en cours...", + "loading": "Chargement...", + "reconnecting": "Connexion perdue. Tentative de reconnexion…", + "error": { + "invalid_credentials": "Email ou mot de passe invalide", + "invalid_token": "Le serveur n'a pas accepté ce jeton d'accès. Vérifiez qu'il est complet et qu'il n'a pas expiré ni été révoqué.", + "connection_failed": "Échec de la connexion au serveur", + "cors_blocked": "Le serveur est joignable mais bloque les requêtes cross-origin. Vérifiez la configuration CORS de votre serveur JMAP et autorisez ce domaine.", + "server_error": "Le serveur est temporairement indisponible. Veuillez réessayer plus tard.", + "generic": "Une erreur s'est produite. Veuillez réessayer.", + "totp_required": "Un code d'authentification à deux facteurs est requis. Veuillez saisir votre code ci-dessous.", + "totp_invalid": "Code d'authentification invalide. Vérifiez votre application d'authentification.", + "oauth_discovery_failed": "Le SSO est activé mais le fournisseur d'identité est injoignable. Vérifiez votre configuration OAuth.", + "token_exchange_failed": "Votre mot de passe et votre code ont été acceptés, mais le serveur de messagerie a refusé d'ouvrir une session pour cette application. Demandez à votre administrateur de vérifier les paramètres de son client OAuth." + }, + "show_password": "Afficher le mot de passe", + "hide_password": "Masquer le mot de passe", + "totp_toggle": "J'ai un code 2FA", + "token_toggle": "Se connecter avec un jeton d'accès", + "password_toggle": "Se connecter avec un mot de passe", + "token_label": "Jeton d'accès", + "token_placeholder": "Collez votre jeton d'API", + "token_hint": "Créez un jeton d'API dans les paramètres de votre fournisseur de messagerie, puis collez-le ici.", + "remember_me": "Se souvenir de moi", + "config_error": { + "title": "Erreur de configuration", + "fetch_failed": "Impossible de charger la configuration de l'application. Veuillez réessayer plus tard.", + "server_not_configured": "Le serveur de messagerie n'a pas été configuré. Veuillez contacter votre administrateur." + }, + "remove_from_history": "Supprimer de l'historique", + "totp_label": "Code d'authentification", + "totp_placeholder": "000000", + "session_expired": "Votre session a expiré. Veuillez vous reconnecter.", + "dismiss": "Fermer", + "or": "ou", + "sign_in_sso": "Se connecter avec SSO", + "add_account_title": "Ajouter un compte", + "add_account_subtitle": "Se connecter avec un autre compte", + "cancel": "Annuler", + "website": "Site web", + "imprint": "Mentions légales", + "privacy_policy": "Politique de confidentialité", + "try_demo": "Essayer la démo", + "demo_description": "Explorez avec des données d'exemple - aucun compte nécessaire", + "demo_launching": "Lancement de la démo...", + "demo_login_button": "Lancer la démo", + "demo_tagline": "Découvrez un client de messagerie complet. Aucun compte requis.", + "demo_no_signup": "Aucune inscription - explorez librement avec des données d'exemple", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Connexion en cours...", + "oauth_error": { + "title": "Échec de l'authentification", + "invalid_state": "La validation de sécurité a échoué. Veuillez réessayer.", + "missing_params": "Données d'autorisation manquantes. Veuillez réessayer.", + "token_exchange_failed": "Impossible de finaliser l'authentification. Veuillez réessayer.", + "access_denied": "L'accès a été refusé. Veuillez contacter votre administrateur.", + "back_to_login": "Retour à la connexion" + } + }, + "sidebar": { + "close": "Fermer", + "compose": "Composer", + "compose_hint": "Composer (c)", + "search_placeholder": "Rechercher un email...", + "search_placeholder_hint": "Rechercher... (appuyez sur /)", + "storage": "Stockage", + "storage_used": "Utilisé", + "storage_free": "Libre", + "storage_total": "Total", + "sign_out": "Se déconnecter", + "sign_out_of": "Se déconnecter de {account}", + "sign_out_all": "Se déconnecter de tous les comptes", + "add_account": "Ajouter un compte", + "set_as_default": "Définir par défaut", + "switch_account": "Changer de compte", + "contacts": "Contacts", + "calendar": "Calendrier", + "settings": "Paramètres", + "admin": "Admin", + "files": "Fichiers", + "loading_mailboxes": "Chargement des boîtes mail...", + "push_connected": "Mises à jour en temps réel actives", + "push_disconnected": "Mises à jour en temps réel inactives", + "keyboard_shortcuts": "Raccourcis clavier", + "theme": { + "light": "Mode clair", + "dark": "Mode sombre", + "system": "Thème système" + }, + "language": { + "title": "Langue" + }, + "mailboxes": { + "inbox": "Boîte de réception", + "sent": "Envoyés", + "drafts": "Brouillons", + "trash": "Corbeille", + "archive": "Archives", + "starred": "Favoris", + "all_mail": "Tous les messages", + "spam": "Spam", + "important": "Important" + }, + "unified_inbox": "Boîte de réception unifiée", + "unified_sent": "Tous les envoyés", + "unified_drafts": "Tous les brouillons", + "unified_trash": "Toutes les corbeilles", + "unified_archive": "Toutes les archives", + "unified_junk": "Tous les indésirables", + "all_accounts": "Tous les comptes", + "all_accounts_loading": "Chargement des comptes : {loaded} sur {total}", + "unified_mailbox": "Boîte aux lettres unifiée", + "expand": "Développer", + "collapse": "Réduire", + "expand_tooltip": "Développer", + "collapse_tooltip": "Réduire", + "mobile": { + "search": "Rechercher", + "compose": "Composer", + "go_back": "Retour", + "toggle_menu": "Afficher/masquer le menu" + }, + "clear_search": "Effacer la recherche", + "vacation_active": "Répondeur d'absence activé", + "demo_banner": "Mode démo", + "demo_reset": "Réinitialiser", + "demo_tour": "Visite", + "tags": "Étiquettes", + "show_all_tags": "Tout afficher ({count})", + "show_fewer_tags": "Afficher moins", + "folders": "Dossiers", + "your_folders": "Vos dossiers", + "create_folder": "Créer un dossier", + "show_more": "Plus", + "show_fewer": "Moins", + "mail": "Messagerie", + "nav_label": "Navigation", + "add_app": "Apps", + "shared": "Partagé", + "shared_account_hidden": "Masqué de la barre latérale. Réaffichez-le dans Paramètres › Mise en page.", + "notifications_muted": "Notifications désactivées", + "scheduled": "Planifiés", + "unified_all_unread": "Tous les non lus", + "unified_all_starred": "Tous les favoris", + "unified_all_mail": "Tout le courrier", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Libellés", + "create_label": "Créer un libellé", + "mv_tasks": "Tâches", + "mv_side_panel": "Panneau latéral", + "mv_open_full": "Ouvrir en plein écran", + "mv_previous_day": "Jour précédent", + "mv_next_day": "Jour suivant", + "mv_add_another_account": "Ajouter un autre compte", + "mv_manage_account": "Gérer votre compte", + "mv_storage_used": "{percent} % de {total} utilisés" + }, + "protocol_handlers": { + "title": "Applications par défaut", + "description": "Choisissez si les liens d'e-mail et de calendrier s'ouvrent dans Bulwark. Techniquement, Bulwark s'enregistre comme gestionnaire de protocole pour les liens mailto: et webcal:.", + "unsupported": "Ce navigateur ou cette connexion ne prend pas en charge l'enregistrement manuel des gestionnaires de protocole. Vous pourrez peut-être quand même utiliser la PWA installée via les paramètres du navigateur ou du système.", + "mailto_label": "Liens e-mail", + "mailto_description": "Ouvre les liens mailto: dans Bulwark avec un éditeur prérempli.", + "protocol_open_mode_label": "À l’ouverture des liens de protocole", + "protocol_open_mode_description": "Choisissez si Bulwark ouvre les liens mailto: et webcal: dans un nouvel onglet ou réutilise une session ouverte. L’option de session active nécessite l’autorisation des notifications afin que vous puissiez cliquer sur une notification de secours pour ramener Bulwark au premier plan si le navigateur bloque le focus.", + "protocol_open_mode_active_session": "Ouvrir dans la session active si possible", + "protocol_open_mode_new_tab": "Toujours ouvrir un nouvel onglet", + "focus_notification_title": "Ouvrir Bulwark", + "focus_notification_body": "Le lien a été ouvert dans Bulwark. Cliquez pour ramener la fenêtre au premier plan.", + "webcal_label": "Liens de calendrier", + "webcal_description": "Ouvre les liens webcal: dans Bulwark avec une boîte de dialogue d'abonnement au calendrier préremplie.", + "register_mailto": "Enregistrer l'application e-mail", + "register_webcal": "Enregistrer l'application de calendrier", + "mailto_registered": "Enregistrement du gestionnaire d'e-mail demandé", + "webcal_registered": "Enregistrement du gestionnaire de calendrier demandé", + "registration_failed": "Échec de l'enregistrement du gestionnaire de protocole", + "opening_mailto": "Ouverture de l'éditeur...", + "opening_webcal": "Ouverture du calendrier...", + "browser_note": "Votre navigateur ou système d'exploitation peut vous demander de confirmer et peut exiger que Bulwark soit installé avant de pouvoir le sélectionner comme application par défaut.", + "select_account_title": "Choisir un compte", + "select_mailto_account": "Choisissez le compte qui doit ouvrir ce lien e-mail.", + "select_webcal_account": "Choisissez le compte qui doit ouvrir ce lien de calendrier.", + "select_account_note": "Cette sélection s'applique uniquement à ce lien de protocole.", + "detail_to": "À", + "detail_subject": "Objet", + "detail_no_subject": "Sans objet", + "detail_calendar": "Calendrier", + "detail_source": "Source", + "active_account": "Actif", + "switching_account": "Changement de compte..." + }, + "sidebar_apps": { + "modal_title": "Applications de la barre latérale", + "add_new": "Ajouter une application", + "edit_app": "Modifier l'application", + "name_label": "Nom", + "name_placeholder": "Mon application", + "name_required": "Le nom est requis", + "url_label": "URL", + "url_required": "L'URL est requise", + "url_invalid": "Entrez une URL http ou https valide", + "icon_label": "Icône", + "icon_required": "L'icône est requise", + "open_mode_label": "Mode d'ouverture", + "open_new_tab": "Nouvel onglet", + "open_inline": "Intégré", + "cancel": "Annuler", + "add": "Ajouter", + "update": "Mettre à jour", + "delete": "Supprimer", + "delete_confirm_title": "Supprimer l'application", + "delete_confirm": "Êtes-vous sûr de vouloir supprimer \"{name}\" ?", + "no_apps": "Aucune application ajoutée", + "no_apps_hint": "Ajoutez des applications et liens personnalisés à votre barre latérale", + "managed_title": "Fournies par votre administrateur", + "managed_description": "Ces applications sont configurées pour tout le monde et ne peuvent pas être modifiées.", + "managed_badge": "Gérée", + "search_icons": "Rechercher des icônes...", + "show_popular": "Populaires", + "show_all": "Toutes", + "no_icons_found": "Aucune icône trouvée", + "inline_badge": "Intégré", + "tab_badge": "Onglet", + "show_on_mobile": "Afficher sur mobile" + }, + "email_list": { + "columns": { + "from": "De", + "to": "À", + "subject": "Objet", + "date": "Date", + "size": "Taille" + }, + "no_emails": "Aucun message trouvé", + "no_emails_description": "Cette boîte est vide", + "no_search_results": "Aucun résultat trouvé", + "no_search_results_description": "Essayez de modifier votre recherche ou vos filtres", + "loading": "Chargement des emails...", + "unread": "non lu", + "to_me": "À moi", + "to_recipients": "À {count} destinataires", + "and_others": "et {count} autres", + "draft": "Brouillon", + "starred": "Favori", + "pinned": "Épinglé", + "replied": "Répondu", + "forwarded": "Transféré", + "has_attachment": "Avec pièce jointe", + "conversations_range": "{from}–{to, number} sur {total, number}", + "conversations_count": "{count} sur {total} conversations", + "conversations_count_plus": "{count}+ conversations", + "conversations_count_simple": "{count} conversations", + "no_conversations": "Aucune conversation", + "loading_more": "Chargement d'autres emails...", + "no_more_emails": "Plus d'emails à charger", + "batch_actions": { + "select": "Sélectionner les e-mails", + "select_all": "Tout sélectionner", + "selected_messages": "{count, plural, one {1 e-mail sélectionné} other {# e-mails sélectionnés}}", + "mark_read": "Marquer comme lu", + "mark_unread": "Marquer comme non lu", + "delete": "Supprimer", + "delete_confirm_title": "Supprimer les e-mails", + "delete_confirm_message": "Voulez-vous vraiment supprimer {count, plural, one {1 e-mail} other {# e-mails}} ?", + "clear_selection": "Effacer la sélection" + }, + "permanent_delete": "Supprimer définitivement", + "permanent_delete_confirm_title": "Supprimer définitivement", + "permanent_delete_confirm_message": "Cet e-mail sera définitivement supprimé. Cette action est irréversible.", + "permanent_delete_confirm_batch_message": "Ces {count, plural, one {1 e-mail} other {# e-mails}} seront définitivement supprimés. Cette action est irréversible.", + "empty_folder": { + "button": "Vider le dossier", + "confirm_title": "Vider le dossier", + "confirm_message": "Tous les e-mails de ce dossier seront définitivement supprimés. Cette action est irréversible.", + "confirm_message_trash": "Tous les e-mails de ce dossier seront déplacés vers la corbeille.", + "confirm_button": "Vider le dossier", + "junk_hint": "Vous pouvez vider le dossier Indésirables pour supprimer définitivement tous les messages.", + "trash_hint": "Vous pouvez vider la corbeille pour supprimer définitivement tous les messages." + }, + "no_scheduled_emails": "Aucun e-mail planifié", + "no_scheduled_emails_description": "Les messages planifiés pour plus tard apparaîtront ici.", + "scheduled_count": "{count, plural, one {1 e-mail planifié} other {# e-mails planifiés}}", + "scheduled_actions_hint": "Les messages planifiés peuvent être annulés, replanifiés ou modifiés depuis leurs actions.", + "cancel_scheduled_send": "Annuler l’envoi", + "reschedule_send": "Replanifier", + "cancel_and_edit": "Annuler et modifier", + "cancel_and_compose_again": "Annuler et rédiger à nouveau", + "reschedule_prompt": "Saisissez une nouvelle date/heure, p. ex. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "L''expéditeur demande un accusé de lecture :", + "send": "Envoyer l''accusé", + "ignore": "Ignorer", + "sent": "Accusé de lecture envoyé.", + "send_failed": "Impossible d''envoyer l''accusé de lecture", + "mdn_subject": "Lu : {subject}", + "mdn_body": "Ceci est un accusé de réception du message que vous avez envoyé à {recipient}.\n\nRemarque : cet accusé confirme uniquement que le message a été affiché sur l''ordinateur du destinataire. Il ne garantit pas que le destinataire a lu ou compris le contenu." + }, + "no_email_selected": "Aucun email sélectionné", + "no_email_description": "Sélectionnez un email dans la liste pour le voir ici", + "no_conversation_selected": "Aucune conversation sélectionnée", + "no_conversation_description": "Choisissez une conversation dans la liste pour la lire ici", + "compose": "Rédiger", + "compose_hint": "Rédiger un nouveau message", + "no_subject": "(Sans objet)", + "no_body_content": "(Aucun contenu disponible)", + "no_preview_available": "Aucun aperçu disponible", + "show_quoted_text": "Afficher le texte cité", + "hide_quoted_text": "Masquer le texte cité", + "loading_email": "Chargement de l'email...", + "loading": "Chargement...", + "reply": "Répondre", + "reply_all": "Répondre à tous", + "forward": "Transférer", + "delete": "Supprimer", + "archive": "Archiver", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Marquer comme favori", + "unstar": "Retirer des favoris", + "mark_unread": "Marquer comme non lu", + "mark_read": "Marquer comme lu", + "unread": "Non lu", + "read": "Lu", + "spam_short": "Spam", + "not_spam_short": "Non spam", + "move": "Déplacer", + "print": "Imprimer", + "view_source": "Voir la source", + "fullscreen": "Plein écran", + "exit_fullscreen": "Quitter le plein écran", + "view_in_dark_mode": "Afficher en mode sombre", + "view_in_light_mode": "Afficher en mode clair", + "export_email": "Exporter en .eml", + "forward_as_attachment": "Transférer en pièce jointe", + "import_email": "Importer .eml ou .zip", + "keyboard_shortcuts": "Raccourcis clavier (?)", + "email_source": "Source de l'email", + "draft_banner": "Ce message est un brouillon", + "edit_draft": "Modifier", + "copy_source": "Copier dans le presse-papiers", + "source_copied": "Source copiée dans le presse-papiers", + "attachments": "Pièces jointes", + "important": "Important", + "download": "Télécharger", + "download_all": "Tout télécharger", + "from": "De", + "to": "À", + "cc": "CC", + "bcc": "CCI", + "date": "Date", + "subject": "Objet", + "show_details": "Afficher les détails", + "hide_details": "Masquer les détails", + "external_content_warning": "Les images et le contenu externe ont été bloqués", + "load_external_content": "Charger les images", + "trust_sender": "Toujours faire confiance à cet expéditeur", + "sender_check": { + "unverified_label": "Expéditeur non vérifié", + "failed_label": "Échec de la vérification de l'expéditeur", + "unverified": "Ce message prétend venir de {domain}, mais rien ne le confirme. Il ne porte aucune signature valide de {domain} et ne provient pas d'un serveur autorisé par {domain}.", + "unverified_sent_from": "Ce message prétend venir de {domain}, mais il a été envoyé depuis {host} et ne porte aucune signature valide de {domain}.", + "failed": "Ce message prétend venir de {domain}, mais il a échoué aux vérifications d'expéditeur de ce domaine. Il est peut-être falsifié.", + "failed_sent_from": "Ce message prétend venir de {domain}, mais il a été envoyé depuis {host} et a échoué aux vérifications d'expéditeur de {domain}. Il est peut-être falsifié.", + "caution": "Méfiez-vous des liens, des pièces jointes et des demandes de mots de passe ou d'informations de paiement." + }, + "back_to_list": "Retour à la liste", + "view_contact": "Voir le contact", + "message_details": "Détails du message", + "more_reply_options": "Plus d'options de réponse", + "set_tag": "Définir l'étiquette", + "tag": "Étiquette", + "more_actions": "Plus d'actions", + "move_to": "Déplacer vers...", + "remove_tag": "Retirer l'étiquette", + "tag_filter_placeholder": "Filtrer les étiquettes", + "tag_no_matches": "Aucune étiquette correspondante", + "more_count": "+{count} de plus", + "characters_count": "{count} caractères", + "quick_reply_placeholder": "Écrivez une réponse rapide...", + "more_options": "Plus d'options", + "sending": "Envoi en cours...", + "security_authentication": "Sécurité et authentification", + "technical_details": "Détails techniques", + "message_id_label": "ID du message :", + "reply_to_label": "Répondre à :", + "delivery_time_label": "Temps de livraison :", + "conversation_part_label": "Partie de la conversation :", + "previous_messages": "{count} message précédent", + "previous_messages_plural": "{count} messages précédents", + "time": { + "day": "jour", + "days": "jours", + "hour": "heure", + "hours": "heures", + "minute": "minute", + "minutes": "minutes" + }, + "unknown_sender": "Inconnu", + "recipient_me": "moi", + "recipient_and_others": "{name} et {count} autres", + "recipient_to_prefix": "À :", + "authentication": { + "title": "Authentification", + "status": { + "verified": "Vérifié", + "warning": "Attention", + "none": "Non authentifié" + }, + "spf": { + "pass": "SPF Validé", + "fail": "SPF Échoué", + "none": "Pas de SPF" + }, + "dkim": { + "pass": "DKIM Valide", + "fail": "DKIM Invalide", + "none": "Pas de DKIM" + }, + "dmarc": { + "pass": "DMARC Validé", + "fail": "DMARC Échoué", + "none": "Pas de DMARC" + }, + "spam_score": "Score de spam", + "tooltip_spf": "Sender Policy Framework : vérifie que le serveur d'envoi est autorisé à envoyer des e-mails au nom du domaine", + "tooltip_dkim": "DomainKeys Identified Mail : confirme que l'e-mail n'a pas été modifié en transit grâce à une signature cryptographique", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance : garantit l'alignement de SPF et DKIM avec le domaine de l'expéditeur et définit une politique en cas d'échec", + "policy": "Politique", + "result": { + "pass": "Validé", + "fail": "Échoué", + "softfail": "Échec partiel", + "neutral": "Neutre", + "permerror": "Erreur permanente", + "temperror": "Erreur temporaire", + "none": "Aucun" + } + }, + "headers": { + "routing": "Routage", + "received": "Reçu", + "message_id": "ID du message", + "list_info": "Information de liste" + }, + "tooltips": { + "reply": "Répondre", + "reply_all": "Répondre à tous (a)", + "forward": "Transférer (f)", + "archive": "Archiver", + "delete": "Supprimer", + "star": "Suivre (s)", + "unstar": "Ne plus suivre (s)", + "compose": "Rédiger (c)", + "previous": "E-mail précédent", + "next": "E-mail suivant", + "edit_draft": "Modifier le brouillon" + }, + "spam": { + "button_title": "Signaler comme spam", + "not_spam_title": "Marquer comme légitime", + "toast_success": "Déplacé vers Indésirables", + "toast_batch": "{count} e-mails déplacés vers Indésirables", + "toast_undo": "Annuler", + "toast_not_spam_success": "Déplacé vers Boîte de réception", + "toast_not_spam_batch": "{count} e-mails déplacés vers Boîte de réception", + "error": "Échec du signalement spam", + "error_not_spam": "Échec de la restauration" + }, + "unsubscribe_banner": { + "label": "Newsletter", + "button": "Se désabonner", + "confirm_title": "Se désabonner de cet expéditeur ?", + "confirm_button": "Confirmer", + "cancel": "Annuler", + "success_http": "Page de désabonnement ouverte dans un nouvel onglet", + "success_mailto": "E-mail de désabonnement envoyé", + "confirm_message_http": "La page de désabonnement s'ouvrira dans un nouvel onglet.", + "confirm_message_mailto": "Un e-mail de désabonnement sera envoyé à l'expéditeur.", + "error": "Impossible de se désabonner", + "dismiss": "Ignorer" + }, + "calendar_invitation": { + "loading": "Chargement des détails…", + "title": "Invitation calendrier", + "published_title": "Événement publié", + "response_title": "Réponse à l'événement", + "update_title": "Mise à jour de l'événement", + "counter_title": "Contre-proposition", + "refresh_title": "Demande d'actualisation", + "declined_counter_title": "Contre-proposition refusée", + "cancelled_title": "Événement annulé", + "organizer": "Organisé par {name}", + "organizer_label": "Organisé par", + "attendees": "{count, plural, one {# participant} other {# participants}}", + "accept": "Accepter", + "maybe": "Peut-être", + "decline": "Refuser", + "add_to_calendar": "Ajouter au calendrier", + "added": "Ajouté au calendrier", + "rsvp_sent": "Réponse envoyée", + "parse_error": "Impossible de lire l'invitation", + "action_failed": "Impossible d'effectuer cette action de calendrier.", + "no_calendar": "Calendrier non disponible", + "published_info": "Cet événement a été partagé à titre informatif.", + "response_info": "Ce message contient la réponse d'un participant.", + "response_info_organizer": "Cette réponse met à jour votre événement.", + "update_info": "Ce message met à jour un événement existant.", + "counter_info": "Ce message propose des modifications à un événement.", + "counter_info_organizer": "Ce participant a proposé des modifications à votre événement.", + "refresh_info": "Ce message demande les derniers détails de l'événement.", + "refresh_info_organizer": "Un participant a demandé les derniers détails de l'événement.", + "declined_counter_info": "L'organisateur a refusé une contre-proposition.", + "authentication_failed_info": "Les contrôles d'authentification de cette invitation ont échoué. Traitez les actions de calendrier avec prudence.", + "authentication_missing_info": "Cette invitation ne comporte pas d'authentification de messagerie vérifiée. Vérifiez les détails avec l'organisateur si quelque chose semble inhabituel.", + "sender_mismatch_info": "Cette invitation a été envoyée depuis {sender}, alors que l'organisateur indiqué dans les données du calendrier est {organizer}.", + "sender_mismatch_unverified_info": "Cette invitation a été envoyée depuis {sender}, alors que l'organisateur indiqué dans les données du calendrier est {organizer}, et le message n'a pas pu être vérifié.", + "organizer_role": "Vous organisez cet événement", + "your_response": "Votre réponse : {status}", + "response_needed": "Réponse requise", + "response_accepted": "Acceptée", + "response_tentative": "Provisoire", + "response_declined": "Refusée", + "response_delegated": "Déléguée", + "actor_sent_info": "Envoyé par {name}.", + "actor_response_info": "{name} a répondu {status}.", + "actor_counter_info": "{name} a proposé des modifications à cet événement.", + "actor_refresh_info": "{name} a demandé les derniers détails de l'événement.", + "actor_declined_counter_info": "{name} a refusé la contre-proposition.", + "actor_note": "Note : {comment}", + "actor_unknown": "Quelqu'un", + "proposed_changes": "Modifications proposées", + "change_title": "Titre", + "change_time": "Horaire", + "change_location": "Lieu", + "change_description": "Description", + "change_empty": "Aucune", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Appliquer les modifications proposées", + "proposal_applied": "Les modifications proposées ont été appliquées.", + "review_proposal": "Examiner la proposition", + "review_request": "Examiner la demande", + "view_in_calendar": "Voir dans le calendrier", + "select_calendar": "Choisir un calendrier", + "already_in_calendar": "Déjà dans votre calendrier", + "request_info": "Vous êtes invité à cet événement. Répondez pour informer l'organisateur de votre disponibilité.", + "cancel_info": "L'organisateur a annulé cet événement.", + "event_updated": "Mise à jour #{sequence}", + "event_status_tentative": "Provisoire", + "event_status_cancelled": "Annulé", + "collapse": "Masquer les détails", + "expand": "Afficher les détails" + }, + "previous": "Précédent", + "next": "Suivant", + "send": "Envoyer", + "more": "plus", + "details": { + "recipients_routing": "Destinataires & routage", + "authentication_security": "Authentification & sécurité", + "identifiers_threading": "Identifiants & fil de discussion", + "mailing_list": "Liste de diffusion", + "message_properties": "Propriétés du message", + "sent": "Envoyé", + "received": "Reçu", + "delivery_time": "Délai de livraison", + "in_reply_to": "En-réponse-à", + "references": "Références", + "thread_id": "ID de fil", + "size": "Taille", + "mime_type": "Type MIME", + "attachments_summary": "{count} fichiers · {size}", + "list_id": "ID de liste", + "list_help": "Aide de liste", + "list_post": "Publication de liste", + "list_unsubscribe": "Se désinscrire", + "iprev": "DNS inversé", + "spam_status": "Statut spam", + "ai_verdict": "Verdict IA", + "account": "Compte", + "no_subject": "(sans objet)" + }, + "scheduled_banner": "Envoi planifié à {date}", + "scheduled_send_created": "E-mail planifié pour envoi", + "sending_in": "Envoi dans {seconds} s", + "undo_send_failed": "Impossible d'annuler. Le message a peut-être déjà été envoyé.", + "cancel_scheduled_send": "Annuler l’envoi", + "reschedule_send": "Replanifier", + "cancel_and_edit": "Annuler et modifier", + "cancel_and_compose_again": "Annuler et rédiger à nouveau", + "reschedule_prompt": "Saisissez une nouvelle date/heure, p. ex. 2026-05-04T15:30", + "scheduled_actions_only": "La vue Planifiés prend uniquement en charge les actions de planification", + "undo_send_scheduled": "Message planifié pour envoi", + "undo_send": "Annuler l’envoi", + "contact_sidebar": { + "title": "Contact", + "close": "Fermer le panneau", + "action_email": "E-mail", + "action_email_title": "Envoyer un e-mail", + "action_copy": "Copier", + "action_copy_title": "Copier l'adresse e-mail", + "action_edit_title": "Modifier le contact", + "section_emails": "E-mails", + "section_phones": "Téléphones", + "section_organizations": "Organisations", + "section_addresses": "Adresses", + "section_notes": "Notes", + "not_in_contacts": "Pas dans vos contacts", + "add_to_contacts": "Ajouter aux contacts", + "copied": "Copié !", + "copy_failed": "Échec de la copie", + "view_contact": "Voir le contact", + "view_details": "Voir les détails" + }, + "send_now": "Envoyer maintenant", + "verification_code": { + "copy": "Copier le code {code}", + "copied": "Code copié", + "copy_failed": "Impossible de copier le code" + }, + "mv_position": "{index} sur {total}", + "plugin_panel_show": "Afficher le panneau du plugin", + "plugin_panel_hide": "Masquer le panneau du plugin" + }, + "email_composer": { + "select_from_directory": "Choisir dans l'annuaire", + "minimize": "Réduire", + "maximize": "Plein écran", + "restore": "Restaurer", + "new_message": "Nouveau message", + "signature_edit_hint": "Double-cliquez pour modifier la signature", + "reply": "Répondre", + "reply_all": "Répondre à tous", + "forward": "Transférer", + "reply_to": "Répondre", + "reply_all_to": "Répondre à tous", + "forward_message": "Transférer", + "from": "De", + "to": "À", + "cc": "CC", + "bcc": "CCI", + "subject": "Objet", + "body_placeholder": "Écrivez votre message...", + "send": "Envoyer", + "cancel": "Annuler", + "attach": "Joindre", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Joindre depuis Fichiers", + "files_picker": { + "attach": "{count, plural, =0 {Joindre} one {Joindre 1 fichier} other {Joindre # fichiers}}", + "empty_folder": "Ce dossier est vide", + "load_failed": "Impossible de charger vos fichiers" + }, + "discard": "Supprimer", + "discard_draft_title": "Supprimer le brouillon ?", + "discard_draft_confirm": "Vous avez des modifications non enregistrées. Voulez-vous supprimer ce brouillon ?", + "discard_draft_message": "Le brouillon sera supprimé.", + "saving": "Enregistrement...", + "draft_saved": "Brouillon enregistré", + "save_failed": "Échec de l'enregistrement", + "to_placeholder": "Adresses email des destinataires", + "cc_placeholder": "Destinataires en copie", + "bcc_placeholder": "Destinataires en copie cachée", + "subject_placeholder": "Objet", + "cc_label": "Cc :", + "bcc_label": "Cci :", + "subject_label": "Objet :", + "file_size_kb": "Ko", + "prefix": { + "forward": "Tr:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} other {# e-mails}}", + "no_subject": "(Sans objet)", + "unknown_sender": "Inconnu", + "quote": { + "reply_header": "Le {date}, {sender} a écrit :", + "forward_header": "---------- Message transféré ----------", + "from": "De : {sender}", + "date": "Date : {date}", + "subject": "Objet : {subject}", + "to": "À : {recipients}" + }, + "remove_sub_address": "Retirer le sous-adressage", + "from_override": { + "toggle_off": "Remplacer", + "toggle_on": "Annuler le remplacement", + "toggle_tooltip": "Modifiez librement le nom et l'adresse d'expéditeur. Le courrier est toujours envoyé via votre identité.", + "name_label": "Nom de l'expéditeur", + "name_placeholder": "Nom", + "email_label": "Adresse e-mail de l'expéditeur", + "email_placeholder": "alias@example.com", + "envelope_notice": "Si votre serveur n'accepte pas cette adresse comme expéditeur d'enveloppe, {identity} est utilisée à la place, et les destinataires peuvent la voir dans l'en-tête Return-Path." + }, + "save_as_template": "Enregistrer comme modèle", + "attach_from_device": "Téléverser depuis cet appareil", + "templates_menu": "Modèles", + "insert_template": "Insérer un modèle", + "options": "Options", + "option_plain_text": "Texte brut", + "option_plain_text_hint": "Envoyer sans mise en forme", + "option_read_receipt": "Demander un accusé de lecture", + "option_read_receipt_hint": "Demander au destinataire de confirmer l'ouverture", + "option_dsn": "Notification de remise", + "option_dsn_hint": "Recevoir un rapport quand le serveur le remet", + "option_require_tls": "Exiger une remise chiffrée", + "option_require_tls_hint": "Remettre uniquement via TLS, sinon rejeter", + "option_priority": "Priorité", + "option_priority_high": "Haute", + "option_priority_normal": "Normale", + "option_priority_low": "Basse", + "validation": { + "recipient_required": "Ajoutez un destinataire pour envoyer", + "body_required": "Rédigez un message ou joignez un fichier", + "invalid_recipients": "Non envoyé - vérifiez ces adresses : {addresses}", + "attachments_uploading": "Les pièces jointes sont en cours d'envoi — le message partira une fois terminé", + "attachment_upload_failed": "Non envoyé - le téléversement d'une pièce jointe a échoué. Supprimez-la et réessayez." + }, + "upload_progress": "Envoi {uploaded} / {total}", + "upload_cancel": "Annuler l'envoi", + "upload_failed": "Échec du téléversement de {filename}", + "attachment_too_large": "« {name} » dépasse la taille autorisée par le serveur ({max} par fichier)", + "attachments_total_too_large": "Les pièces jointes dépasseraient la limite du serveur de {max} par message", + "send_failed": "Échec de l'envoi de l'e-mail", + "smime_sign_on": "Signature S/MIME activée", + "smime_sign_off": "Signature S/MIME désactivée", + "smime_encrypt_on": "Chiffrement S/MIME activé", + "smime_encrypt_off": "Chiffrement S/MIME désactivé", + "smime_encrypt_unavailable": "Chiffrement S/MIME indisponible – certificats des destinataires manquants", + "smime_unlock_title": "Déverrouiller la clé S/MIME", + "smime_unlock_message": "Entrez la phrase secrète pour déverrouiller votre clé S/MIME.", + "smime_unlock_button": "Déverrouiller", + "smime_passphrase_placeholder": "Phrase secrète S/MIME", + "send_timeout": "Le serveur ne répond plus. Vérifiez le dossier Envoyés avant de renvoyer : le message est peut-être déjà parti.", + "send_offline": "Vous semblez hors ligne. Le message est toujours là ; renvoyez-le une fois de nouveau en ligne.", + "formatting": "Mise en forme", + "send_recipients_rejected": "Non envoyé : le serveur a refusé tous les destinataires.", + "send_plugin_failed": "Non envoyé : un plugin chargé de l'envoi a échoué ou a pris trop de temps. Vérifiez le dossier Envoyés avant de renvoyer le message.", + "send_some_recipients_rejected": "Envoyé, mais pas à ces destinataires : le serveur les a refusés.", + "continue_draft": "Continuer le brouillon", + "close_draft_title": "Enregistrer ou supprimer le brouillon ?", + "close_draft_message": "Vous avez des modifications non enregistrées. Voulez-vous enregistrer comme brouillon ou supprimer ?", + "drop_files": "Déposez les fichiers à joindre", + "show_less": "Afficher moins", + "forgot_attachment": { + "title": "Avez-vous oublié une pièce jointe ?", + "message": "Votre message mentionne \"{keyword}\" mais aucun fichier n'est joint. Envoyer quand même ?", + "send_anyway": "Envoyer quand même", + "back": "Retour à l'édition" + }, + "empty_subject": { + "title": "Envoyer sans objet ?", + "message": "Ce message n'a pas d'objet. L'envoyer quand même ?", + "dont_ask_again": "Ne plus me demander", + "send_anyway": "Envoyer quand même", + "back": "Retour à l'édition" + }, + "add_link": "Ajouter un lien", + "link_url_prompt": "Saisissez l'URL", + "sending": "Envoi...", + "schedule_send": "Planifier l’envoi", + "schedule_send_description": "Choisissez quand le serveur doit libérer ce message.", + "schedule_preset": { + "tomorrow_morning": "Demain matin", + "tomorrow_afternoon": "Demain après-midi", + "monday_morning": "Lundi matin" + }, + "schedule_send_required": "Choisissez une date et une heure.", + "schedule_send_invalid": "Saisissez une date et une heure valides.", + "schedule_send_future": "Choisissez une date et une heure futures.", + "schedule_send_too_late": "Cette heure est au-delà de ce que le serveur autorise.", + "schedule_send_unsupported": "L’envoi planifié n’est pas pris en charge pour ce compte.", + "schedule_send_cleanup_warning": "L’envoi planifié a été créé, mais le nettoyage du brouillon a échoué.", + "send_delay_unsupported": "Le délai d’envoi n’est pas pris en charge pour ce compte.", + "send_delay_unsupported_confirm": "Ce compte ne prend pas en charge le délai d’envoi. Envoyer immédiatement ?", + "recipient_edit_email": "Modifier l'adresse e-mail", + "recipient_edit_name": "Modifier le nom d'affichage", + "recipient_email_placeholder": "Adresse e-mail", + "recipient_name_placeholder": "Nom d'affichage", + "invalid_recipient": "Adresse e-mail non valide", + "autocomplete_search_server": "Rechercher sur le serveur", + "autocomplete_searching": "Recherche en cours...", + "mention_recipients": "Destinataires", + "mention_card_not_recipient": "Absent de À et de Cc : cette personne ne recevra pas ce message.", + "mention_card_add": "Ajouter à {field}", + "mention_card_remove": "Retirer la mention", + "toolbar": { + "bold": "Gras", + "italic": "Italique", + "underline": "Souligné", + "strikethrough": "Barré", + "text_color": "Couleur du texte", + "remove_color": "Supprimer la couleur", + "heading_1": "Titre 1", + "heading_2": "Titre 2", + "bullet_list": "Liste à puces", + "ordered_list": "Liste numérotée", + "quote": "Citation", + "code_block": "Bloc de code", + "align_left": "Aligner à gauche", + "align_center": "Centrer", + "align_right": "Aligner à droite", + "text_direction": "Sens du texte (RTL/LTR)", + "link": "Lien", + "table": "Tableau", + "clear_formatting": "Effacer la mise en forme", + "undo": "Annuler", + "redo": "Rétablir", + "add_row_above": "Insérer une ligne au-dessus", + "add_row_below": "Insérer une ligne en dessous", + "add_column_before": "Insérer une colonne avant", + "add_column_after": "Insérer une colonne après", + "delete_row": "Supprimer la ligne", + "delete_column": "Supprimer la colonne", + "toggle_header_row": "Basculer la ligne d'en-tête", + "delete_table": "Supprimer le tableau", + "pick_size": "Choisir la taille", + "font_size": "Taille de police", + "font_size_default": "Par défaut", + "background_color": "Couleur d'arrière-plan", + "remove_background_color": "Supprimer la couleur d'arrière-plan" + }, + "send_filing_warning": "Envoyé - mais le nettoyage après envoi a échoué, un ancien brouillon peut subsister.", + "pop_out": "Ouvrir la réponse dans une fenêtre", + "send_guard_failed": "Non envoyé - un plugin qui vérifie les messages avant l'envoi a échoué ou n'a pas répondu." + }, + "confirm_dialog": { + "confirm": "Confirmer", + "cancel": "Annuler" + }, + "common": { + "loading": "Chargement...", + "error": "Erreur", + "success": "Succès", + "cancel": "Annuler", + "save": "Enregistrer", + "delete": "Supprimer", + "edit": "Modifier", + "close": "Fermer", + "search": "Rechercher", + "refresh": "Actualiser", + "settings": "Paramètres", + "help": "Aide", + "logout": "Déconnexion", + "yes": "Oui", + "no": "Non", + "unknown": "Inconnu", + "app_title": "Webmail", + "reconnecting": "Connexion perdue. Tentative de reconnexion…", + "rate_limited_title": "L'authentification du serveur est temporairement limitee.", + "rate_limited_detail": "Bulwark a suspendu les requetes en arriere-plan pour eviter un blocage. Nouvelle tentative dans {seconds}s.", + "rate_limited_action_title": "Requete suspendue pour eviter le blocage.", + "rate_limited_action_detail": "Bulwark attend la fin du delai impose par le serveur avant d'envoyer d'autres requetes authentifiees. Reessayez dans {seconds}s.", + "pull_to_refresh": "Tirez pour actualiser", + "release_to_refresh": "Relâchez pour actualiser", + "refreshing": "Actualisation…" + }, + "notifications": { + "email_sent": "Email envoyé avec succès", + "email_deleted": "Email supprimé", + "email_archived": "Email archivé", + "emails_deleted": "{count, plural, one {# e-mail supprimé} other {# e-mails supprimés}}", + "emails_archived": "{count, plural, one {# e-mail archivé} other {# e-mails archivés}}", + "error_archiving": "Impossible d'archiver l'e-mail", + "error_updating": "Impossible de mettre à jour l'e-mail", + "email_starred": "Email ajouté aux favoris", + "email_unstarred": "Email retiré des favoris", + "email_marked_read": "Email marqué comme lu", + "email_marked_unread": "Email marqué comme non lu", + "copied_to_clipboard": "Copié dans le presse-papiers", + "source_copied": "Source copiée dans le presse-papiers", + "error_sending": "Échec de l'envoi de l'email", + "error_deleting": "Échec de la suppression de l'email", + "error_loading": "Échec du chargement des emails", + "new_email": "Nouvel email", + "new_email_from": "De {sender}", + "click_to_view": "Cliquer pour voir", + "email_moved": "Email déplacé", + "emails_moved": "{count} emails déplacés", + "moved_to_mailbox": "Déplacé vers {mailbox}", + "move_failed": "Échec du déplacement", + "removed_from_folder": "Retiré de {mailbox}", + "remove_from_folder_failed": "Impossible de retirer de ce dossier", + "emails_copied": "{count} emails copiés", + "copy_failed": "Échec de la copie", + "move_error": "Impossible de déplacer les emails vers le dossier sélectionné", + "email_tagged": "E-mail étiqueté", + "emails_tagged": "{count} e-mails étiquetés", + "tag_failed": "Échec de l'étiquetage", + "identity_created": "Identité créée avec succès", + "identity_updated": "Identité mise à jour avec succès", + "identity_deleted": "Identité supprimée", + "identity_set_primary": "Identité principale mise à jour", + "identity_create_failed": "Échec de la création de l'identité: {error}", + "identity_update_failed": "Échec de la mise à jour de l'identité: {error}", + "identity_delete_failed": "Échec de la suppression de l'identité: {error}", + "identity_unauthorized": "Vous n'êtes pas autorisé à envoyer depuis cette adresse email", + "identity_not_found": "Identité introuvable", + "vacation_saved": "Paramètres d'absence enregistrés", + "vacation_save_failed": "Échec de l'enregistrement des paramètres d'absence", + "filters_saved": "Filtres enregistrés avec succès", + "filters_save_failed": "Échec de l'enregistrement des filtres", + "filters_deleted": "Règle de filtrage supprimée", + "templates_exported": "Modèles exportés avec succès", + "templates_imported": "{count, plural, one {# modèle importé} other {# modèles importés}}", + "templates_import_errors": "Certains modèles n'ont pas pu être importés", + "templates_import_empty": "Aucun modèle trouvé dans le fichier", + "export_email_error": "Échec de l'exportation de l'e-mail", + "import_email_success": "E-mail importé avec succès", + "import_email_error": "Échec de l'importation de l'e-mail", + "rule_created": "Règle créée", + "rule_merged": "Ajouté à la règle « {name} »", + "rule_already_covered": "Déjà couvert par la règle « {name} »", + "rule_apply_existing": "{count, plural, one {Appliquer à # message existant} other {Appliquer à # messages existants}}", + "rule_applied": "{count, plural, =0 {Aucun message à modifier} one {Règle appliquée à # message} other {Règle appliquée à # messages}}", + "rule_apply_failed": "Impossible d'appliquer la règle aux messages existants", + "rule_edit": "Modifier la règle", + "rule_undo": "Annuler", + "rule_undone": "Modification de la règle annulée", + "rule_undo_failed": "Impossible d'annuler la modification de la règle", + "rule_undo_conflict": "Vos filtres ont changé entre-temps. Annulez la modification dans les réglages des filtres.", + "rule_folder_failed": "Impossible de créer le dossier", + "vacation_filters_save_failed": "Paramètres d'absence enregistrés, mais le transfert et les destinataires de la réponse n'ont pas pu être enregistrés" + }, + "date": { + "today": "Aujourd'hui", + "yesterday": "Hier", + "this_week": "Cette semaine", + "last_week": "La semaine dernière", + "this_month": "Ce mois-ci", + "older": "Plus ancien", + "just_now": "À l'instant", + "minutes_ago": "Il y a {count} minute", + "minutes_ago_plural": "Il y a {count} minutes", + "hours_ago": "Il y a {count} heure", + "hours_ago_plural": "Il y a {count} heures", + "days_ago": "Il y a {count} jour", + "days_ago_plural": "Il y a {count} jours" + }, + "language": { + "title": "Langue", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Sélectionner la langue", + "switch_to_english": "Passer à l'anglais", + "switch_to_french": "Passer au français", + "switch_to_japanese": "Passer au japonais", + "switch_to_spanish": "Passer à l'espagnol", + "switch_to_italian": "Passer à l'italien", + "switch_to_german": "Passer à l'allemand", + "switch_to_dutch": "Passer au néerlandais", + "switch_to_portuguese": "Passer au portugais", + "switch_to_russian": "Passer au russe", + "switching": "Changement de langue...", + "switch": "Changer de langue", + "current": "Langue actuelle", + "polish": "Polski", + "switch_to_polish": "Passer au polonais", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Passer au persan", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Passer au mongol" + }, + "settings": { + "title": "Paramètres", + "back_to_mail": "Retour aux emails", + "save_success": "Paramètres enregistrés avec succès", + "import_success": "Paramètres importés avec succès", + "import_error": "Échec de l'importation des paramètres", + "reset_confirm": "Êtes-vous sûr de vouloir réinitialiser tous les paramètres ?", + "unsaved_changes": "Vous avez des modifications non enregistrées", + "discard_changes": "Abandonner les modifications ?", + "discard": "Abandonner", + "keep_editing": "Continuer l'édition", + "tabs": { + "appearance": "Apparence", + "language": "Langue, région et heure", + "email": "Comportement email", + "composer": "Rédaction", + "privacy": "Confidentialité et sécurité", + "account": "Compte", + "identities": "Identités", + "vacation": "Absence", + "advanced": "Avancé", + "calendar": "Calendrier", + "filters": "Filtres", + "templates": "Modèles", + "folders": "Dossiers", + "keywords": "Étiquettes", + "security": "Sécurité", + "encryption": "Chiffrement", + "files": "Fichiers", + "contacts": "Contacts", + "protocol_handlers": "Applications par défaut", + "sidebar_apps": "Apps de la barre latérale", + "notifications": "Notifications", + "layout": "Mise en page", + "themes": "Thèmes", + "reading": "Lecture", + "composing": "Rédaction", + "downloads": "Téléchargements", + "content_senders": "Contenu et expéditeurs", + "about_data": "À propos et données", + "debug": "Débogage" + }, + "themes": { + "title": "Thèmes", + "description": "Choisissez parmi les thèmes déployés par votre administrateur et les préréglages intégrés.", + "locked": "La sélection du thème est verrouillée par un administrateur.", + "forced": "Le thème « {name} » est imposé par l'administrateur et ne peut pas être modifié", + "admin_theme": "Thème de l'administrateur", + "activated": "Thème activé", + "default_restored": "Thème par défaut restauré", + "default_name": "Par défaut", + "default_badge": "Par défaut", + "admin_enforced": "Imposé par l'administrateur" + }, + "tab_groups": { + "general": "Général", + "account": "Compte & Identité", + "organization": "Organisation des e-mails", + "apps": "Applications", + "system": "Système", + "appearance": "Apparence", + "mail": "Courrier", + "privacy": "Confidentialité et sécurité", + "advanced": "Avancé" + }, + "appearance": { + "message_list_order": { + "label": "Ordre de la liste des messages", + "description": "Comment les messages sont ordonnés dans la liste. Le tri est effectué par le serveur et couvre donc tout le dossier, pas seulement les messages chargés.", + "preset": { + "chronological": "Chronologique (plus récents d'abord)", + "unread_first": "Non lus d'abord", + "starred_first": "Suivis d'abord", + "tagged_first": "Étiquetés d'abord", + "custom": "Personnalisé" + }, + "tag_label": "Étiquette", + "advanced_show": "Avancé : modifier les niveaux de tri", + "advanced_hide": "Masquer les options avancées", + "criterion_label": "Critère du niveau {n}", + "direction_label": "Sens du niveau {n}", + "criteria": { + "unread": "Non lu", + "starred": "Suivi", + "tag": "Étiquette", + "receivedAt": "Date de réception", + "sentAt": "Date d'envoi", + "from": "Expéditeur", + "subject": "Objet", + "size": "Taille" + }, + "direction": { + "unread_first": "Non lus d'abord", + "read_first": "Lus d'abord", + "starred_first": "Suivis d'abord", + "unstarred_first": "Non suivis d'abord", + "tagged_first": "Étiquetés d'abord", + "untagged_first": "Non étiquetés d'abord", + "newest_first": "Plus récents d'abord", + "oldest_first": "Plus anciens d'abord", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Plus volumineux d'abord", + "smallest_first": "Plus petits d'abord" + }, + "add_level": "Ajouter un niveau", + "remove_level": "Supprimer le niveau", + "move_up": "Monter", + "move_down": "Descendre", + "tiebreak_note": "Jusqu'à trois niveaux ; les niveaux suivants départagent les égalités. « Plus récents d'abord » s'applique toujours en dernier.", + "scope": { + "label": "Appliquer à tous les dossiers", + "description": "Désactivé, l'ordre ne s'applique qu'à la boîte de réception ; les autres dossiers restent chronologiques." + }, + "unsupported_note": "Votre serveur de messagerie ne prend pas en charge le tri par état de lecture, suivi ou étiquette. Ces niveaux sont ignorés et la liste reste chronologique." + }, + "title": "Apparence", + "description": "Personnalisez l'apparence de votre webmail", + "theme": { + "label": "Thème", + "description": "Choisissez votre schéma de couleurs préféré", + "light": "Clair", + "dark": "Sombre", + "system": "Système" + }, + "language": { + "label": "Langue", + "description": "Choisissez votre langue préférée" + }, + "font_size": { + "label": "Taille de police", + "description": "Ajustez la taille du texte pour une meilleure lisibilité", + "small": "Petite", + "medium": "Moyenne", + "large": "Grande" + }, + "list_density": { + "label": "Densité", + "description": "Contrôlez l'espacement dans toute l'interface", + "extra_compact": "Très compacte", + "compact": "Compacte", + "regular": "Normale", + "comfortable": "Confortable" + }, + "interface_layout": { + "label": "Disposition de l'interface", + "description": "Où se trouvent la recherche, la navigation et les barres d'outils", + "default": "Par défaut", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Activer les animations", + "description": "Afficher les transitions et effets fluides" + }, + "toolbar_position": { + "label": "Position de la barre d'outils", + "description": "Où afficher les boutons d'action des e-mails (Répondre, Archiver, Supprimer, etc.)", + "top": "En haut", + "below_subject": "Sous l'objet" + }, + "toolbar_labels": { + "label": "Afficher les libellés de la barre d'outils", + "description": "Afficher les libellés textuels à côté des icônes de la barre d'outils. Désactiver pour économiser de l'espace une fois familiarisé avec les icônes." + }, + "hide_account_switcher": { + "label": "Masquer le sélecteur de compte de la barre latérale", + "description": "Masquer le sélecteur de compte en haut de la barre latérale des dossiers. Vous pouvez toujours changer de compte depuis la barre de navigation inférieure." + }, + "show_rail_account_list": { + "label": "Afficher les avatars de compte sur la barre de navigation", + "description": "Afficher les cercles de comptes individuels en bas de la barre de navigation pour un changement rapide, avec un bouton de déconnexion en dessous." + }, + "unified_mailbox": { + "label": "Boîte aux lettres unifiée", + "description": "Afficher les dossiers combinés (Réception, Envoyés, etc.) de tous les comptes connectés", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Inclure les boîtes de groupe", + "description": "Inclure également les boîtes partagées ou de groupe dans la vue unifiée." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Icônes colorées dans la barre latérale", + "description": "Colore les icônes de dossiers et d'étiquettes par type (Boîte de réception en bleu, Indésirable en rouge, Envoyés en vert, etc.). Désactivez pour une barre latérale monochrome." + }, + "tint_list_rows": { + "label": "Lignes de liste colorées selon la couleur d'étiquette", + "description": "Appliquez une couleur de fond à chaque ligne de message en fonction de la couleur de sa première étiquette. Désactivez cette option pour conserver des lignes unies. Les points et les pastilles d'étiquette continueront d'afficher la couleur." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Afficher le nombre total de messages", + "description": "Affiche le nombre total de messages à côté des dossiers et des étiquettes, en plus du nombre de messages non lus. Désactivez pour n'afficher que les messages non lus." + }, + "favicon_unread_badge": { + "label": "Nombre de non-lu sur l'icône de l'onglet", + "description": "Afficher le nombre de messages non lus de la boîte de réception sous forme de badge sur l’icône de l’onglet du navigateur, afin que les nouveaux messages restent visibles lorsque l’onglet n’est pas actif. Désactiver cette option pour ne pas afficher de badge sur l’icône." + }, + "show_shared_mail_accounts": { + "label": "Afficher les comptes de messagerie partagés", + "description": "Afficher les dossiers des boîtes partagées avec vous sous Partagé dans la barre latérale. Désactivez pour les masquer tous, ou masquez des comptes un par un ci-dessous. Leurs agendas et contacts restent visibles." + }, + "pro_interface": { + "label": "Interface Pro (expérimental)", + "description": "Disposition pour utilisateurs avancés (bureau uniquement) avec navigation des messages multi-onglets et flux de travail multi-comptes. L'interface standard n'est pas affectée ; vous pouvez revenir à tout moment.", + "open_label": "Ouvrir l'interface Pro", + "back_to_standard": "Retour au standard" + }, + "cross_unread": { + "label": "Tous les comptes : Non lus", + "description": "Affiche une entrée dans la section Tous les comptes répertoriant les messages non lus de tous les comptes, couvrant tous les dossiers sauf indésirables, envoyés, archives, corbeille et brouillons." + }, + "cross_starred": { + "label": "Tous les comptes : Favoris", + "description": "Affiche une entrée dans la section Tous les comptes répertoriant les messages favoris de tous les comptes, couvrant tous les dossiers sauf indésirables, envoyés, archives, corbeille et brouillons." + }, + "cross_all": { + "label": "Tous les comptes : Tout le courrier", + "description": "Affiche une entrée dans la section Tous les comptes répertoriant tout le courrier de tous les comptes, couvrant tous les dossiers sauf indésirables, envoyés, archives, corbeille et brouillons." + } + }, + "keywords": { + "title": "Étiquettes de messagerie", + "description": "Définissez des étiquettes pour organiser vos e-mails avec des couleurs. Elles sont stockées sous forme de mots-clés JMAP sur le serveur.", + "add_keyword": "Ajouter une étiquette", + "label_field": "Nom d'affichage", + "label_placeholder": "ex. Travail, Personnel, Urgent", + "id_field": "ID d'étiquette", + "id_placeholder": "ex. travail, personnel", + "color_field": "Couleur", + "id_exists": "Cet ID d'étiquette existe déjà", + "edit": "Éditer l'étiquette", + "delete": "Supprimer l'étiquette", + "save": "Enregistrer", + "add": "Ajouter", + "cancel": "Annuler", + "migrating": "Mise à jour de l'étiquette sur les e-mails existants…", + "migration_error": "Impossible de mettre à jour l'étiquette sur les e-mails existants", + "filters_migration_error": "Certaines règles de filtrage utilisent encore l'ancienne étiquette — mettez-les à jour manuellement", + "nesting": { + "label": "Étiquettes imbriquées", + "description": "Imbriquez des étiquettes sous d'autres étiquettes et affichez-les sous forme d'arborescence dans la barre latérale." + }, + "parent_field": "Étiquette parente", + "no_parent": "Aucune étiquette parente", + "too_long": "Ce chemin d'étiquette est trop long ({max} caractères au maximum)", + "has_children_locked": "D'autres étiquettes sont imbriquées sous celle-ci, son nom et son étiquette parente sont donc verrouillés. Déplacez-les ou supprimez-les d'abord.", + "has_children_delete": "Retirez d'abord les étiquettes imbriquées sous celle-ci", + "visibility_field": "Visibilité dans la barre latérale", + "visibility": { + "show": "Afficher", + "unread": "Afficher si non lus", + "hide": "Masquer" + }, + "unrecognized": { + "title": "Récupérer les étiquettes depuis le serveur", + "description": "Les noms et couleurs des étiquettes sont enregistrés dans ce navigateur, mais les étiquettes elles-mêmes se trouvent sur vos e-mails sous forme de mots-clés JMAP. Analysez votre messagerie à la recherche de mots-clés qu'aucune étiquette ici n'explique, puis rajoutez-les.", + "scan": "Rechercher des étiquettes", + "rescan": "Analyser à nouveau", + "scanning": "Analyse en cours… {scanned} e-mails sur {total}", + "none": "Toutes les étiquettes présentes sur vos e-mails sont déjà définies ici.", + "messages": "{count, plural, one {# e-mail} other {# e-mails}}", + "add_all": "Ajouter les {count}", + "partial": "Seuls les {scanned} e-mails les plus récents sur {total} ont été analysés. Les étiquettes utilisées uniquement sur des e-mails plus anciens peuvent manquer.", + "error": "Impossible d'analyser votre messagerie à la recherche d'étiquettes." + } + }, + "notifications": { + "test_sound": "Tester le son de notification", + "sounds": { + "default": "Par défaut (Bip)", + "cheerful": "Joyeux", + "involved": "Élaboré", + "swift": "Geste rapide", + "relax": "Détente" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Son de notification", + "description": "Choisissez le son à jouer pour les notifications", + "choose": "Son", + "choose_desc": "Sélectionnez une sonnerie et cliquez sur l'icône du haut-parleur pour l'écouter" + }, + "email": { + "title": "Notifications par e-mail", + "description": "Configurer les notifications pour les e-mails entrants", + "enabled": "Notifications par e-mail", + "enabled_desc": "Afficher des notifications à l'arrivée de nouveaux e-mails", + "sound": "Son de notification", + "sound_desc": "Jouer un son à l'arrivée de nouveaux e-mails", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Notifications de calendrier", + "description": "Configurer les notifications pour les événements du calendrier", + "enabled": "Notifications d'événements", + "enabled_desc": "Afficher des alertes pour les événements à venir", + "sound": "Son de notification", + "sound_desc": "Jouer un son pour les rappels de calendrier", + "invitation_parsing": "Analyser les invitations par e-mail", + "invitation_parsing_desc": "Détecter les invitations de calendrier dans les pièces jointes et afficher les actions de calendrier" + }, + "push": { + "confirm_disable_message": "Cet appareil cessera de recevoir des alertes lorsque le site est fermé.", + "confirm_disable_title": "Désactiver les notifications en arrière-plan ?", + "description": "Recevez des notifications système pour les nouveaux courriers quand ce site est fermé. Livré via le relais push Bulwark ; le relais ne voit jamais le contenu des courriers.", + "disable": "Désactiver", + "enable": "Activer", + "dismiss_aria": "Fermer l'invite de notifications", + "ios_hint": "Sur iOS, installez d'abord le site sur l'écran d'accueil – Safari ne livre Web Push qu'aux PWA installées.", + "reenable": "Réenregistrer", + "relay_desc": "Utilise par défaut le relais Bulwark hébergé. Ne le changez que si vous l'hébergez vous-même.", + "relay_label": "Relais push", + "relay_locked": "Défini par l'administrateur", + "relay_locked_desc": "L'URL du relais push a été définie par votre administrateur et ne peut pas être modifiée.", + "status_active": "Actif sur cet appareil", + "status_busy": "Traitement en cours…", + "status_inactive": "Non activé sur cet appareil", + "status_unsupported": "Ce navigateur ne prend pas en charge Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Notifications en arrière-plan", + "devices_title": "Appareils enregistrés", + "devices_desc": "Tous les appareils enregistrés pour recevoir les notifications en arrière-plan de ce compte. Révoquer un appareil l’empêche immédiatement d’en recevoir.", + "devices_loading": "Chargement des appareils enregistrés…", + "devices_empty": "Aucun appareil n’est enregistré pour les notifications en arrière-plan.", + "devices_error": "Impossible de charger les appareils enregistrés.", + "devices_refresh": "Actualiser", + "device_this": "Cet appareil", + "device_other": "Autre appareil", + "device_status_active": "Distribue les notifications", + "device_status_inactive": "N’a jamais distribué de notification", + "device_status_unknown": "Inconnu de ce relais", + "device_expires": "Expire le {date}", + "revoke": "Révoquer", + "confirm_revoke_title": "Révoquer cet appareil ?", + "confirm_revoke_message": "Il cessera immédiatement de recevoir les notifications en arrière-plan. Réactiver les notifications push sur cet appareil l’enregistrera à nouveau.", + "confirm_revoke_message_this": "Cet appareil cessera immédiatement de recevoir les notifications en arrière-plan." + } + }, + "language_region": { + "title": "Langue, région et heure", + "description": "Langue, format de date, format d'heure et autres préférences régionales", + "language": { + "label": "Langue", + "description": "Choisissez votre langue préférée", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Format de date", + "description": "Comment les dates apparaissent dans la liste des e-mails", + "smart": "Intelligent", + "relative": "Relatif", + "full": "Toujours la date complète", + "preview_today": "Aujourd'hui :", + "preview_this_week": "Cette semaine :", + "preview_older": "Plus ancien :" + }, + "date_locale": { + "label": "Format de date", + "description": "Comment sont ordonnées des dates (jour, mois, année)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Jour/Mois/Année", + "mdy": "Mois/Jour/Année" + }, + "time_format": { + "label": "Format d'heure", + "description": "Choisissez entre 12 heures ou 24 heures", + "12h": "12 heures", + "24h": "24 heures" + }, + "first_day": { + "label": "Premier jour de la semaine", + "description": "Commencer la semaine le dimanche ou le lundi", + "sunday": "Dimanche", + "monday": "Lundi" + }, + "time_zone": { + "label": "Fuseau horaire", + "description": "Afficher les heures des e-mails et du calendrier dans ce fuseau horaire plutôt que dans celui indiqué par votre navigateur", + "auto": "Automatique ({zone})", + "preview_now": "Maintenant :" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Effacer la recherche lors du changement de dossier", + "description": "Cliquer sur un dossier (Boîte de réception, Envoyés…) efface la recherche active et affiche ce dossier. Désactivé, la recherche reste appliquée et est relancée dans le dossier choisi." + }, + "request_read_receipt": { + "label": "Demander un accusé de lecture par défaut", + "description": "Activer au préalable la demande d''accusé de lecture lors de la rédaction d''un nouveau message." + }, + "read_receipt_response": { + "label": "Répondre aux demandes d''accusé de lecture", + "description": "Que faire lorsqu''un message entrant demande un accusé de lecture.", + "ask": "Demander à chaque fois", + "always": "Toujours envoyer", + "never": "Ne jamais envoyer" + }, + "title": "Comportement email", + "description": "Configurez la gestion des emails", + "mark_read": { + "label": "Marquer comme lu", + "description": "Quand marquer les emails comme lus à l'ouverture", + "instant": "Instantanément", + "delay_3s": "Après 3 secondes", + "delay_5s": "Après 5 secondes", + "never": "Jamais" + }, + "delete_action": { + "label": "Action de suppression", + "description": "Que se passe-t-il quand vous supprimez un email", + "trash": "Déplacer vers la corbeille", + "trash_and_read": "Déplacer vers la corbeille et marquer comme lu", + "permanent": "Supprimer définitivement", + "warning": "Les emails seront supprimés définitivement et ne pourront pas être récupérés. Cette action est irréversible." + }, + "message_spacing": { + "label": "Espacement du message", + "description": "Marge autour du corps du message dans le lecteur", + "auto": "Automatique (recommandé)", + "always": "Toujours ajouter un espacement", + "edge": "Bord à bord" + }, + "plain_text_font": { + "label": "Police du texte brut", + "description": "Police utilisée pour les e-mails en texte brut", + "mono": "À chasse fixe", + "sans": "Normale (comme l'application)" + }, + "archive_mode": { + "label": "Archiver dans", + "description": "Comment organiser les emails lors de l'archivage", + "single": "Un seul dossier", + "year": "Un dossier par année", + "month": "Un dossier par mois", + "reorganize": "Réorganiser l'archive existante", + "reorganize_success": "{count, plural, =0 {Aucun email à réorganiser} =1 {1 email réorganisé} other {# emails réorganisés}}", + "reorganize_error": "Échec de la réorganisation de l'archive" + }, + "permanently_delete_junk": { + "label": "Supprimer définitivement les indésirables", + "description": "Supprimer définitivement les e-mails du dossier Indésirables au lieu de les déplacer vers la corbeille" + }, + "confirm_bulk_delete": { + "label": "Confirmer avant de supprimer plusieurs messages", + "description": "Demander avant de déplacer plusieurs messages sélectionnés vers la corbeille. La suppression définitive demande toujours." + }, + "mail_layout": { + "label": "Disposition du courrier", + "description": "Choisissez entre le volet de lecture divisé classique et un flux de lecture focalisé style Gmail.", + "split": "Volet divisé", + "split_description": "Garder la liste des messages et le volet de lecture visibles côte à côte.", + "focus": "Liste focalisée", + "focus_description": "Afficher une ligne par message et ouvrir le courrier en pleine largeur tout en gardant la barre latérale des dossiers visible.", + "horizontal": "Volet de lecture en bas", + "horizontal_description": "Afficher la liste des messages en haut et ouvrir le message sélectionné dans un volet de lecture en dessous." + }, + "list_columns": { + "label": "Afficher la liste en colonnes", + "description": "Affiche les lignes sur une seule ligne sous forme de tableau avec les colonnes De, Objet, Date et Taille sous un en-tête." + }, + "disable_threading": { + "label": "Désactiver le regroupement par conversation", + "description": "Afficher les e-mails comme messages individuels plutôt que groupés par conversation" + }, + "plain_text_mode": { + "label": "Texte brut uniquement", + "description": "Désactiver l'éditeur de texte enrichi et envoyer tous les e-mails en texte brut uniquement, y compris les réponses et les transferts" + }, + "auto_select_reply_identity": { + "label": "Sélection automatique de l'adresse de réponse", + "description": "Lors d'une réponse, bascule automatiquement l'adresse d'expédition vers l'identité qui a reçu le message d'origine" + }, + "reply_identity_match": { + "label": "Correspondance de l'adresse de réception", + "description": "Quelles adresses de réception comptent comme les vôtres. « Adresse exacte uniquement » ne choisit qu'une de vos identités configurées ; « même domaine » traite aussi toute autre adresse de vos domaines d'identité comme un alias catch-all et y réécrit l'en-tête De. Choisissez l'adresse exacte si ces adresses sont des listes de diffusion.", + "exact": "Adresse exacte uniquement", + "domain": "Toute adresse de mes domaines" + }, + "signature_position": { + "label": "Position de la signature", + "description": "Où insérer votre signature dans les réponses et les transferts. Au-dessus du texte cité, elle se lit naturellement comme la conclusion de la réponse ; en dessous, elle garde le message d'origine contigu.", + "above_quote": "Avant le texte cité", + "below_quote": "Après le texte cité" + }, + "signature_separator": { + "label": "Délimiteur de signature", + "description": "Préfixer la signature par la ligne de délimitation standard \"-- \" (RFC 3676). Désactivez si vous préférez enchaîner directement du message à la signature." + }, + "sub_address_delimiter": { + "label": "Délimiteur de sous-adresse", + "description": "Caractère séparant votre nom d'utilisateur de l'étiquette de sous-adresse. Utilisez le délimiteur configuré sur votre serveur de messagerie (par ex. utilisateur{delimiter}tag@domaine.com).", + "option": "{delimiter} (utilisateur{delimiter}tag@domaine.com)", + "custom": "Personnalisé…", + "custom_input_label": "Caractère de délimiteur personnalisé" + }, + "show_preview": { + "label": "Afficher l'aperçu", + "description": "Afficher l'aperçu de l'email dans la liste", + "focus_description": "Afficher le texte d'aperçu en ligne dans la liste de messages focalisée sur une seule ligne" + }, + "show_verification_codes": { + "label": "Afficher les codes de vérification", + "description": "Proposer le code des e-mails de connexion et de confirmation pour le copier en un clic, dans la liste pendant un jour et dans le message" + }, + "attachment_click_action": { + "label": "Action au clic sur les pièces jointes", + "description": "Choisissez si un clic sur une pièce jointe ouvre un aperçu ou lance immédiatement le téléchargement", + "preview": "Aperçu si possible", + "download": "Télécharger immédiatement" + }, + "attachment_position": { + "label": "Position des pièces jointes", + "description": "Où afficher les pièces jointes dans l'en-tête de l'email", + "beside-sender": "À côté de l'expéditeur", + "below-header": "Sous l'en-tête" + }, + "emails_per_page": { + "10": "10 e-mails", + "25": "25 e-mails", + "50": "50 e-mails", + "100": "100 e-mails", + "label": "Emails par page", + "description": "Nombre d'emails à charger à la fois" + }, + "always_light_mode": { + "label": "Toujours afficher les e-mails en mode clair", + "description": "Afficher le contenu des e-mails en mode clair même lorsque l'application est en mode sombre, évitant les problèmes de conversion" + }, + "external_content": { + "label": "Contenu externe", + "description": "Comment gérer les images et le contenu externe", + "ask": "Toujours demander", + "block": "Toujours bloquer", + "allow": "Toujours autoriser" + }, + "trusted_senders": { + "label": "Expéditeurs de confiance", + "description": "Gérer les expéditeurs dont les images se chargent automatiquement", + "count_zero": "Aucun", + "count_one": "1 expéditeur", + "count_other": "{count} expéditeurs", + "modal_title": "Expéditeurs de confiance", + "empty_title": "Aucun expéditeur de confiance", + "empty_description": "Lorsque vous consultez un email avec des images bloquées, cliquez sur « Toujours faire confiance à cet expéditeur » pour l'ajouter ici.", + "add_manually": "Ajouter manuellement", + "add_button": "Ajouter", + "add_placeholder": "Entrez une adresse email", + "search_placeholder": "Rechercher...", + "no_results": "Aucun expéditeur ne correspond à votre recherche", + "remove": "Supprimer", + "close": "Fermer", + "invalid_email": "Veuillez entrer une adresse email valide", + "already_added": "Cet expéditeur est déjà de confiance", + "save_error": "Échec de l'enregistrement - consultez le journal de débogage Contacts pour plus de détails", + "use_address_book_label": "Synchroniser avec le carnet d'adresses", + "use_address_book_description": "Enregistrez les expéditeurs de confiance dans un carnet \"Expéditeurs de confiance\" dédié pour les synchroniser sur tous vos appareils" + }, + "hover_actions": { + "label": "Actions rapides au survol", + "description": "Choisissez les actions rapides qui apparaissent au survol d'un e-mail dans la liste", + "delete": "Supprimer", + "star": "Étoile / Retirer l'étoile", + "mark_read": "Marquer lu / non lu", + "archive": "Archiver", + "tag": "Étiquette", + "spam": "Marquer comme spam", + "not_spam": "Non indésirable", + "none_selected": "Aucune action sélectionnée", + "mode_label": "Mode d'affichage", + "mode_inline": "En ligne", + "mode_floating": "Flottant", + "corner_label": "Position flottante", + "corner_top-left": "Haut gauche", + "corner_top-right": "Haut droit", + "corner_bottom-left": "Bas gauche", + "corner_bottom-right": "Bas droit" + }, + "default_mail_program": { + "label": "Programme de messagerie par défaut", + "description": "Enregistrer {appName} comme programme de messagerie par défaut pour les liens mailto:", + "button": "Définir par défaut", + "success": "Le navigateur a été invité à définir par défaut", + "error": "Votre navigateur ne prend pas en charge cette fonctionnalité" + }, + "attachment_reminder": { + "label": "Rappel de pièce jointe", + "description": "Avertir avant l'envoi lorsque votre message mentionne des pièces jointes mais qu'aucune n'est jointe", + "keywords_label": "Mots-clés déclencheurs", + "keywords_description": "Mots ou phrases qui déclenchent le rappel lorsqu'ils sont trouvés dans votre message", + "add_placeholder": "Ajouter un mot-clé...", + "add": "Add", + "remove": "Supprimer" + }, + "empty_subject_warning": { + "label": "Avertissement d'objet vide", + "description": "Demander confirmation avant d'envoyer un message sans objet." + }, + "recipient_mentions": { + "label": "Mentionner les destinataires avec @", + "description": "Saisir @ dans le message propose ses destinataires et insère le prénom de celui choisi. Entrée ou Tab sélectionne le nom en surbrillance." + }, + "mention_adds_recipient": { + "label": "Contacts dans les mentions @", + "description": "Taper @ propose aussi vos contacts et les personnes de l'annuaire. Choisir quelqu'un qui n'est pas encore destinataire l'ajoute au message.", + "to": "Ajouter à À", + "cc": "Ajouter en Cc", + "off": "Destinataires uniquement" + }, + "hide_inline_image_attachments": { + "label": "Masquer les images intégrées des pièces jointes", + "description": "Les images intégrées au corps du message ne sont pas listées comme pièces jointes séparées" + }, + "attachment_image_previews": { + "label": "Afficher les aperçus d'images dans les pièces jointes", + "description": "Afficher les images jointes sous forme de vignettes plutôt qu'avec des icônes de fichier génériques" + }, + "send_delay": { + "label": "Annuler l’envoi / délai d’envoi", + "description": "Retarde les envois normaux avec une courte fenêtre côté serveur.", + "off": "Désactivé", + "seconds": "{seconds} secondes", + "unsupported": "Le compte actuel n’annonce pas la prise en charge de l’envoi différé. Le réglage reste enregistré pour d’autres comptes." + }, + "return_to_list_after_action": { + "label": "Revenir à la liste après suppression ou marquage comme non lu", + "description": "Après avoir supprimé ou marqué comme non lu le message ouvert, revenir à la liste des messages au lieu d'ouvrir le message suivant." + }, + "swipe_right_action": { + "label": "Action du balayage vers la droite (mobile)", + "description": "Sur les appareils tactiles, l'action effectuée lorsque vous balayez un message vers la droite dans la liste." + }, + "swipe_left_action": { + "label": "Action du balayage vers la gauche (mobile)", + "description": "Sur les appareils tactiles, l'action effectuée lorsque vous balayez un message vers la gauche dans la liste." + }, + "swipe_actions": { + "none": "Aucune", + "archive": "Archiver", + "delete": "Supprimer", + "mark_read": "Basculer lu/non lu", + "star": "Basculer le favori", + "spam": "Marquer comme spam" + }, + "rtl_editing": { + "label": "Prise en charge de la saisie de droite à gauche", + "description": "Ajoute un bouton de direction à la barre d'outils de l'éditeur pour mettre les paragraphes de gauche à droite ou de droite à gauche" + } + }, + "composer": { + "title": "Rédaction", + "description": "Configurez les paramètres de composition d'email", + "autosave": { + "label": "Intervalle de sauvegarde automatique", + "description": "Fréquence de sauvegarde automatique des brouillons", + "30s": "Toutes les 30 secondes", + "1m": "Toutes les minutes", + "2m": "Toutes les 2 minutes", + "5m": "Toutes les 5 minutes" + }, + "send_confirmation": { + "label": "Confirmation d'envoi", + "description": "Demander une confirmation avant d'envoyer les emails" + }, + "default_reply": { + "label": "Mode de réponse par défaut", + "description": "Action par défaut lors du clic sur répondre", + "reply": "Répondre", + "reply_all": "Répondre à tous" + } + }, + "privacy": { + "title": "Confidentialité et sécurité", + "description": "Gérez vos paramètres de confidentialité et sécurité", + "external_images": { + "label": "Bloquer les images externes", + "description": "Empêcher le suivi par les images externes" + }, + "session_timeout": { + "label": "Délai d'expiration de session", + "description": "Déconnexion automatique après inactivité", + "never": "Jamais", + "30m": "30 minutes", + "1h": "1 heure", + "4h": "4 heures" + }, + "clear_cache": { + "label": "Vider le cache", + "description": "Supprimer les données en cache et fichiers temporaires", + "button": "Vider le cache", + "confirm": "Êtes-vous sûr de vouloir vider le cache ?", + "success": "Cache vidé avec succès" + } + }, + "account": { + "title": "Compte", + "description": "Consultez les informations de votre compte", + "name_label": "Nom d'affichage", + "username_label": "Nom d'utilisateur", + "account_type_label": "Type de compte", + "auth_method_label": "Authentification", + "auth_method_oauth": "Authentification unique (OAuth/OIDC)", + "auth_method_basic": "Mot de passe", + "auth_method_token": "Jeton d'accès", + "demo_account": "Compte de démonstration", + "email": { + "label": "Adresse email", + "value": "{email}" + }, + "server": { + "label": "Serveur JMAP", + "value": "{server}" + }, + "storage": { + "label": "Utilisation du stockage", + "used": "{used} sur {total} utilisés", + "percentage": "{percent}% utilisé" + }, + "last_sync": { + "label": "Dernière synchronisation", + "value": "{time}" + }, + "accounts": { + "title": "Comptes connectés", + "description": "Faites glisser pour réorganiser l'affichage des comptes dans le menu déroulant", + "active": "Compte actuellement actif", + "default_badge": "Compte par défaut", + "set_default": "Définir par défaut", + "switch_to": "Basculer vers ce compte", + "move_up": "Déplacer vers le haut", + "move_down": "Déplacer vers le bas", + "drag_handle": "Faire glisser pour réorganiser", + "add": "Ajouter un compte" + }, + "backup": { + "title": "Sauvegarde de la liste des comptes", + "description": "Conservez sur le serveur la liste des comptes de ce navigateur, chiffrée avec un mot de passe que vous seul connaissez, et retrouvez-la sur un autre appareil. Les mots de passe des boîtes ne sont pas enregistrés : vous vous connectez vous-même à chaque compte restauré.", + "description_passwords": "Conservez sur le serveur la liste des comptes de ce navigateur, chiffrée avec un mot de passe que vous seul connaissez, et retrouvez-la sur un autre appareil. Vous pouvez inclure les mots de passe des boîtes : les comptes restaurés se connectent alors tout seuls.", + "status_none": "Aucune liste enregistrée pour {account}.", + "status_saved": "Une liste est enregistrée pour {account}.", + "save": "Enregistrer la liste", + "restore": "Restaurer la liste", + "delete": "Supprimer la liste enregistrée", + "cancel": "Annuler", + "delete_confirm": "Supprimer du serveur la liste de comptes enregistrée ?", + "password": "Mot de passe de la sauvegarde", + "password_hint": "Au moins 10 caractères. Il ne peut pas être récupéré : si vous l'oubliez, enregistrez à nouveau la liste avec un nouveau mot de passe.", + "restore_hint": "Le mot de passe choisi lors de l'enregistrement de la liste.", + "confirm": "Confirmer le mot de passe", + "include_passwords": "Inclure les mots de passe des boîtes", + "include_passwords_hint": "Les comptes connectés ici avec un mot de passe se connecteront tout seuls après une restauration. Quiconque connaît le mot de passe de la sauvegarde accède à toutes les boîtes de la liste.", + "saved": "Liste enregistrée ({count, plural, one {# compte} other {# comptes}}).", + "restored": "Comptes ajoutés : {added}. Comptes déjà présents et mis à jour : {updated}. Ouvrez chaque nouveau compte depuis la liste des comptes pour vous connecter.", + "restored_signed_in": "Comptes ajoutés : {added}. Comptes déjà présents et mis à jour : {updated}.", + "skipped": "{count, plural, one {# compte n'a pas été ajouté} other {# comptes n'ont pas été ajoutés}} : la limite de comptes est atteinte.", + "saved_passwords": "Mots de passe inclus pour {count, plural, one {# compte} other {# comptes}}.", + "signed_in": "Connecté à {count, plural, one {# compte} other {# comptes}} avec le mot de passe enregistré.", + "sign_in_failed": "{count, plural, one {# mot de passe enregistré a été refusé} other {# mots de passe enregistrés ont été refusés}} : connectez-vous à ces comptes depuis la liste des comptes.", + "deleted": "Liste enregistrée supprimée.", + "errors": { + "mismatch": "Les mots de passe ne correspondent pas.", + "password_length": "Utilisez au moins 10 caractères.", + "unlock_failed": "Mot de passe incorrect, ou la liste a été enregistrée pour un autre compte.", + "owner_signin_required": "Reconnectez-vous à ce compte, puis réessayez.", + "https_required": "Une connexion sécurisée (HTTPS) est nécessaire.", + "storage_failed": "Le serveur n'a pas pu lire ou enregistrer la liste. Réessayez plus tard." + } + }, + "shared_accounts": { + "title": "Partagé avec moi", + "description": "Comptes de groupe et partagés que vous pouvez gérer. Sélectionnez-en un pour modifier ses filtres, son répondeur d'absence, ses calendriers et ses contacts.", + "shared_label": "Compte partagé" + } + }, + "security": { + "title": "Sécurité du compte", + "description": "Gérez votre mot de passe, l'authentification à deux facteurs et les paramètres de sécurité", + "detecting": "Détection des capacités du serveur...", + "not_available": "La gestion de la sécurité du compte n'est pas disponible pour ce serveur de messagerie. Les permissions requises peuvent être désactivées. Consultez la documentation pour plus de détails.", + "password": { + "title": "Changer le mot de passe", + "current": "Mot de passe actuel", + "new": "Nouveau mot de passe", + "confirm": "Confirmer le nouveau mot de passe", + "submit": "Changer le mot de passe", + "success": "Mot de passe changé avec succès", + "error_title": "Échec du changement de mot de passe", + "error_mismatch": "Les nouveaux mots de passe ne correspondent pas", + "error_min_length": "Le mot de passe doit comporter au moins 8 caractères", + "error_generic": "Impossible de changer le mot de passe" + }, + "display_name": { + "label": "Nom d'affichage", + "description": "Votre nom tel qu'il apparaît sur le serveur", + "placeholder": "Entrez votre nom d'affichage", + "save": "Enregistrer", + "success": "Nom d'affichage mis à jour", + "error": "Impossible de mettre à jour le nom d'affichage" + }, + "totp": { + "section_title": "Authentification à deux facteurs", + "label": "Authentification TOTP", + "description": "Ajoutez une couche de sécurité supplémentaire avec un mot de passe à usage unique basé sur le temps", + "active": "Activé", + "inactive": "Désactivé", + "enabled": "Authentification à deux facteurs activée", + "disabled": "Authentification à deux facteurs désactivée", + "enable_error": "Impossible d'activer la 2FA", + "disable_error": "Impossible de désactiver la 2FA", + "setup_instructions": "Copiez cette URL dans votre application d'authentification (Google Authenticator, Authy, etc.) :", + "verification_code": "Code de vérification", + "confirm": "Confirmer", + "disable": "Désactiver", + "disable_confirm_prompt": "Saisissez votre mot de passe pour désactiver l'authentification à deux facteurs.", + "password_required": "Le mot de passe est requis", + "code_required": "Le code de vérification est requis", + "code_invalid": "Code de vérification non valide. Vérifiez votre application d'authentification et réessayez." + }, + "app_passwords": { + "title": "Mots de passe d'application", + "description": "Créez des mots de passe pour les applications qui ne prennent pas en charge l'authentification à deux facteurs", + "add": "Ajouter", + "create": "Créer", + "cancel": "Annuler", + "generate": "Générer", + "name_label": "Nom de l'application", + "name_placeholder": "ex. Thunderbird, iPhone Mail", + "password_label": "Mot de passe (laisser vide pour auto-générer)", + "password_placeholder": "Auto-généré si vide", + "added": "Mot de passe d'application créé", + "removed": "Mot de passe d'application supprimé", + "add_error": "Impossible de créer le mot de passe d'application", + "remove_error": "Impossible de supprimer le mot de passe d'application", + "none": "Aucun mot de passe d'application configuré", + "done": "Terminé", + "expires_label": "Expire (facultatif)", + "copy_now_warning": "Copiez ce mot de passe maintenant - il ne sera plus affiché.", + "allowed_ips_label": "IP autorisées (facultatif)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Séparées par virgule ou espace. Laisser vide pour autoriser toute IP." + }, + "api_keys": { + "title": "Clés API", + "description": "Créez des clés API pour les scripts et intégrations qui communiquent directement avec le serveur", + "name_label": "Nom de la clé", + "name_placeholder": "ex. Script de sauvegarde, CI runner", + "copy_now_warning": "Copiez cette clé API maintenant - elle ne sera plus affichée.", + "added": "Clé API créée", + "removed": "Clé API supprimée", + "add_error": "Échec de la création de la clé API", + "remove_error": "Échec de la suppression de la clé API", + "none": "Aucune clé API configurée" + }, + "encryption": { + "section_title": "Chiffrement au repos", + "label": "Chiffrement des e-mails", + "description": "Chiffrez les e-mails stockés sur le serveur pour une confidentialité accrue", + "active": "Chiffrement {type} activé", + "inactive": "Désactivé", + "enabled": "Chiffrement au repos activé", + "disabled_success": "Chiffrement au repos désactivé", + "error": "Impossible de mettre à jour les paramètres de chiffrement", + "algorithm_label": "Algorithme de chiffrement", + "encrypt_on_append": "Chiffrer les nouveaux e-mails lors du téléversement", + "allow_spam_training": "Autoriser l'apprentissage antispam avant le chiffrement des e-mails", + "enable_encryption": "Activer le chiffrement", + "enable_tooltip": "Activer le chiffrement avec cette clé", + "configure_title": "Activer le chiffrement avec cette clé", + "enable_button": "Activer", + "disable_tooltip": "Désactiver le chiffrement", + "enabled_success": "Chiffrement au repos activé" + }, + "email_client": { + "title": "Configuration du client de messagerie", + "description": "Utilisez ces identifiants pour configurer votre client de messagerie de bureau ou mobile (Thunderbird, Apple Mail, Outlook, etc.)", + "jmap_username_label": "Nom d'utilisateur JMAP", + "copy": "Copier", + "copied": "Copié", + "password_instructions": "Utilisez votre nom d'utilisateur JMAP ci-dessus avec un mot de passe d'application pour vous connecter à votre client de messagerie. Créez un mot de passe d'application dans la section ci-dessus si vous ne l'avez pas encore fait." + }, + "link_device": { + "title": "Associer l'application mobile", + "description": "Connectez-vous à l'application Bulwark Mail sur votre téléphone sans y saisir votre mot de passe. Votre téléphone dispose de sa propre connexion : vous déconnecter ici ne le déconnecte pas.", + "generate": "Afficher le QR code", + "regenerate": "Afficher un nouveau code", + "instructions": "Ouvrez l'application Bulwark Mail et appuyez sur \"Scan a sign-in code\" sur l'écran de connexion (ou sur \"Add account\" → \"Scan a sign-in code\" si vous êtes déjà connecté), puis pointez la caméra ici.", + "expires_in": "Ce code expire dans {seconds} secondes. Il ne peut être utilisé qu'une seule fois.", + "expired": "Ce code a expiré.", + "error": "Impossible de créer un code d'association. Veuillez réessayer.", + "password_prompt": "Saisissez votre mot de passe pour confirmer votre identité. Votre téléphone dispose de sa propre connexion ; votre mot de passe ne lui est pas envoyé.", + "show_password": "Afficher le mot de passe", + "hide_password": "Masquer le mot de passe", + "continue": "Continuer", + "error_wrong_password": "Mot de passe incorrect. Veuillez réessayer.", + "totp_prompt": "Saisissez le code de votre application d'authentification.", + "error_password_or_code": "Le mot de passe ou le code est incorrect.", + "error_not_signed_in": "Votre session a expiré. Reconnectez-vous pour associer un appareil.", + "error_impersonation": "Impossible d'associer un appareil pendant que vous consultez la boîte aux lettres d'un autre utilisateur.", + "error_too_many_attempts": "Trop de tentatives. Patientez un instant, puis réessayez.", + "error_session_secret": "L'association d'appareils n'est pas configurée sur ce serveur (SESSION_SECRET manquant). Contactez votre administrateur.", + "error_server_unreachable": "Impossible de joindre votre serveur de messagerie. Veuillez réessayer.", + "error_pairing_unavailable": "Votre serveur de messagerie ne permet pas d'associer un appareil de cette façon. Contactez votre administrateur.", + "qr_alt": "QR code de connexion", + "link_label": "Lien de connexion", + "copy_link": "Copier le lien de connexion", + "copied": "Copié", + "copy_hint": "Pas de caméra ? Copiez le lien et collez-le dans l'application avec \"Paste sign-in link\".", + "success_title": "Connecté sur votre téléphone", + "success_body": "Vous pouvez fermer cette page. Pour associer un autre appareil, affichez un nouveau code.", + "link_another": "Associer un autre appareil", + "use_sso": "Utiliser plutôt l'authentification unique", + "error_insecure_server": "L'application mobile ne se connecte aux serveurs de messagerie qu'en https, et celui-ci utilise http." + }, + "public_keys": { + "name_label": "Nom de la clé publique", + "name_placeholder": "ex. Ma clé", + "key_label": "Clé publique (S/MIME ou PGP au format ASCII)", + "key_placeholder": "Collez votre clé publique ici", + "none": "Aucune clé publique configurée" + } + }, + "identities": { + "title": "Identités d'envoi", + "description": "Gérer les adresses email depuis lesquelles vous pouvez envoyer", + "identities_count": { + "label": "Vos identités", + "description": "Adresses email configurées pour l'envoi", + "count_zero": "Aucune identité", + "count_one": "1 identité", + "count_other": "{count} identités" + }, + "manage": "Gérer les identités", + "sub_addressing": { + "label": "Sous-adressage", + "description": "Utilisez des tags comme utilisateur+tag@domaine.com pour organiser votre courrier", + "learn_more": "En savoir plus" + } + }, + "vacation": { + "title": "Absence", + "description": "Traiter automatiquement les emails entrants pendant votre absence", + "loading": "Chargement des paramètres d'absence...", + "not_supported": "Votre serveur de messagerie ne prend pas en charge les réponses d'absence.", + "fetch_error": "Échec du chargement des paramètres d'absence. Veuillez réessayer.", + "status": { + "label": "Répondre automatiquement", + "description": "Envoyer une réponse automatique aux personnes qui vous envoient un email", + "active": "Actif", + "inactive": "Inactif" + }, + "date_range": { + "start": "Date de début", + "start_description": "Laisser vide pour aucune limite de début", + "end": "Date de fin", + "end_description": "Laisser vide pour aucune limite de fin" + }, + "message": { + "subject_label": "Objet", + "subject_description": "Ligne d'objet de la réponse automatique", + "subject_placeholder": "Absence du bureau", + "body_label": "Corps du message", + "body_description": "Contenu du message en texte brut", + "body_placeholder": "Merci pour votre email. Je suis actuellement absent du bureau et vous répondrai à mon retour.", + "html_label": "Message mis en forme (HTML)", + "html_description": "Ajoutez une version mise en forme avec des liens et du style. Les destinataires dont la messagerie ne peut pas l'afficher verront le texte brut ci-dessus.", + "html_placeholder": "Rédigez une réponse d'absence mise en forme…" + }, + "preview": { + "title": "Aperçu", + "show": "Afficher l'aperçu", + "hide": "Masquer l'aperçu" + }, + "save": "Enregistrer les modifications", + "saving": "Enregistrement...", + "warnings": { + "end_before_start": "La date de fin doit être postérieure à la date de début", + "invalid_date": "Indiquez une date de début et de fin valides avec l'heure, ou laissez le champ vide", + "start_in_past": "La date de début est dans le passé", + "empty_body": "Le corps du message est vide - les destinataires recevront une réponse vide", + "subject_too_long": "L'objet est trop long pour le serveur ({max} octets au maximum).", + "body_too_long": "Le message est trop long pour le serveur ({max} octets au maximum, mise en forme comprise).", + "forward_address": "Indiquez une adresse valide pour le transfert", + "not_running": "Le transfert ou les destinataires de la réponse sont enregistrés mais pas actifs pour le moment. Enregistrez pour les réactiver." + }, + "forward": { + "title": "Transfert", + "description": "Transfère les messages entrants à une autre adresse, avec ou sans réponse automatique, pendant la période ci-dessus (sans date de fin, jusqu'à sa désactivation). Les spams ne sont pas transférés.", + "enabled_label": "Transférer les messages", + "to_label": "Transférer à", + "to_placeholder": "email@exemple.com", + "keep_label": "Conserver dans la boîte de réception", + "keep_description": "Sinon, le message est seulement transféré : il n'est pas conservé dans cette boîte aux lettres et aucune règle de filtre ne s'y applique." + }, + "audience": { + "label": "Répondre à", + "description": "Expéditeurs internes : {domains}", + "all": "Tous les expéditeurs", + "internal": "Expéditeurs internes uniquement", + "external": "Expéditeurs externes uniquement" + }, + "auto_reply": { + "title": "Réponse automatique" + } + }, + "folders": { + "role_memos": "Notes", + "title": "Dossiers", + "description": "Gérez vos dossiers de messagerie et attribuez des rôles standard", + "folder_list": "Vos dossiers", + "folder_list_description": "Cliquez sur l'icône d'un dossier pour le personnaliser", + "standard_roles": "Rôles standard des dossiers", + "standard_roles_description": "Définissez quels dossiers sont utilisés pour les rôles standard comme Boîte de réception, Envoyés, Corbeille, etc.", + "role_inbox": "Boîte de réception", + "role_drafts": "Brouillons", + "role_sent": "Envoyés", + "role_trash": "Corbeille", + "role_junk": "Spam / Indésirables", + "role_archive": "Archives", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Aucun", + "create_folder": "Créer un dossier", + "create_subfolder": "Créer un sous-dossier", + "reorder": "Glissez pour réorganiser", + "reorder_error": "Échec de la réorganisation des dossiers", + "move_error": "Échec du déplacement du dossier", + "select_folder": "Sélectionner {name}", + "select_hint": "Sélectionner. Maj+clic sélectionne tous les dossiers jusqu'au dernier sur lequel vous avez cliqué.", + "selected_count": "{count, plural, one {# dossier sélectionné} other {# dossiers sélectionnés}}", + "move_to": "Déplacer vers...", + "move_to_top_level": "Niveau supérieur", + "move": "Déplacer", + "clear_selection": "Effacer la sélection", + "folders_moved": "{count, plural, one {# dossier déplacé} other {# dossiers déplacés}}", + "move_partial_error": "{count, plural, one {# dossier n'a pas pu être déplacé} other {# dossiers n'ont pas pu être déplacés}}", + "subfolder_of": "Dans {name}", + "subfolder_name": "Nom du sous-dossier", + "new_folder_name": "Nom du dossier", + "rename": "Renommer", + "change_icon": "Changer l'icône", + "count_mode": "Compteur pour {name}", + "count_mode_default": "Compteur : par défaut", + "count_mode_unread": "Non lus uniquement", + "count_mode_all": "Non lus et total", + "count_mode_none": "Aucun compteur", + "delete": "Supprimer", + "confirm_delete": "Êtes-vous sûr de vouloir supprimer \"{name}\" ? Les e-mails de ce dossier seront déplacés dans la Corbeille.", + "create": "Créer", + "cancel": "Annuler", + "no_folders": "Aucun dossier personnalisé", + "cannot_delete_role": "Impossible de supprimer un dossier avec un rôle standard. Retirez d'abord le rôle.", + "folder_created": "Dossier créé", + "folder_renamed": "Dossier renommé", + "folder_deleted": "Dossier supprimé", + "role_updated": "Rôle du dossier mis à jour", + "error_create": "Échec de la création du dossier", + "error_rename": "Échec du renommage du dossier", + "error_delete": "Échec de la suppression du dossier", + "error_delete_has_children": "Impossible de supprimer le dossier : il contient encore des sous-dossiers. Supprimez-les ou déplacez-les d'abord.", + "error_delete_has_email": "Impossible de supprimer le dossier : il contient encore des e-mails. Déplacez-les ou supprimez-les d'abord.", + "error_role": "Échec de la mise à jour du rôle du dossier" + }, + "advanced": { + "title": "Avancé", + "description": "Options avancées et paramètres développeur", + "debug_mode": { + "label": "Mode débogage", + "description": "Activer la journalisation détaillée pour le dépannage" + }, + "debug_categories": { + "description": "Sélectionnez les catégories à journaliser. Désactivez celles dont vous n'avez pas besoin pour réduire le bruit dans la console.", + "jmap": "Client JMAP", + "jmap_description": "Opérations de boîte aux lettres, récupération des e-mails et requêtes du protocole JMAP", + "calendar": "Calendrier", + "calendar_description": "Événements du calendrier, importations et messages de planification", + "tasks": "Tâches", + "tasks_description": "Création, récupération et mise à jour des tâches du calendrier", + "auth": "Authentification", + "auth_description": "Connexion, TOTP, échange de jetons et gestion de session", + "filters": "Filtres", + "filters_description": "Règles de filtre Sieve et scripts d'absence", + "email": "Affichage des e-mails", + "email_description": "Rendu des e-mails, traitement TNEF et marquage comme lu", + "push": "Notifications push", + "push_description": "Configuration et distribution des notifications push", + "contacts": "Contacts et carnets d'adresses", + "contacts_description": "Synchronisation des contacts, opérations de carnet d'adresses et expéditeurs de confiance" + }, + "settings_sync": { + "label": "Synchronisation des paramètres", + "description": "Synchronisez vos paramètres entre navigateurs et appareils" + }, + "sender_favicons": { + "label": "Favicons des expéditeurs", + "description": "Afficher les icônes de sites web comme photos de profil pour les expéditeurs professionnels", + "refresh_label": "Actualiser les icônes des expéditeurs", + "refresh_description": "Récupérer à nouveau les icônes des sites web, par exemple quand un expéditeur a changé la sienne", + "refresh_button": "Actualiser", + "refresh_done": "Les icônes des expéditeurs vont être récupérées à nouveau" + }, + "show_avatars_in_junk": { + "label": "Afficher les avatars dans le dossier indésirable", + "description": "Afficher les photos de profil et favicons des expéditeurs dans le dossier indésirable. Désactivé par défaut pour éviter de donner une apparence légitime aux tentatives d'hameçonnage." + }, + "keyboard_shortcuts": { + "label": "Raccourcis clavier", + "description": "Voir les raccourcis clavier disponibles", + "button": "Voir les raccourcis" + }, + "refresh_cache": { + "label": "Actualiser les données en cache", + "description": "Actualiser les contacts, calendriers et dossiers depuis le serveur. Vos comptes et sessions sont conservés. Corrige un affichage obsolète ou incorrect sans vous déconnecter.", + "button": "Actualiser" + }, + "reset_settings": { + "label": "Réinitialiser les paramètres", + "description": "Restaurer tous les paramètres par défaut", + "button": "Réinitialiser" + }, + "export_settings": { + "label": "Exporter les paramètres", + "description": "Télécharger vos paramètres au format JSON", + "button": "Exporter" + }, + "import_settings": { + "label": "Importer les paramètres", + "description": "Charger les paramètres depuis un fichier JSON", + "button": "Importer" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Code source" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Cliquez sur le spam et le phishing avant qu'ils n'atteignent votre boîte de réception. Laissez passer le vrai courrier. Si vous perdez vos trois boucliers, la boîte tombe.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Vrai courrier" + }, + "click_to_block": "Cliquez pour bloquer", + "let_through": "Laissez passer", + "start": "Commencer", + "play_again": "Rejouer", + "score": "Score", + "combo": "Bonus de série", + "shields_left": "{count} boucliers sur {total} restants", + "seconds": "{seconds} s", + "best": "Record : {score}", + "new_best": "Nouveau record", + "held": "Boîte de réception défendue", + "overrun": "Boîte de réception submergée", + "stats": { + "caught": "Bloqués", + "delivered": "Distribués", + "mistakes": "Erreurs" + } + } + }, + "sidebar_apps": { + "title": "Apps de la barre latérale", + "description": "Gérer les applications et liens personnalisés dans la barre latérale", + "keep_loaded": "Garder les apps chargées", + "keep_loaded_description": "Garder les apps en ligne en arrière-plan lors du basculement pour éviter le rechargement", + "managed_title": "Fournies par votre administrateur", + "managed_description": "Ces applications sont configurées pour tout le monde et ne peuvent pas être modifiées.", + "manage_title": "Apps personnalisées", + "manage_description": "Ajouter, modifier ou supprimer des apps personnalisées de la barre latérale" + }, + "contacts": { + "title": "Contacts", + "description": "Importer et exporter vos contacts", + "import_label": "Importer des contacts", + "import_description": "Importer des contacts depuis un fichier vCard (.vcf)", + "export_label": "Exporter des contacts", + "export_description": "Exporter tous les contacts au format vCard (.vcf)", + "manage_title": "Carnets d'adresses", + "manage_description": "Renommer vos carnets d'adresses", + "no_address_books": "Aucun carnet d'adresses trouvé", + "categories_title": "Catégories", + "categories_description": "Renommer les catégories de contacts", + "no_categories": "Aucune catégorie trouvée", + "group_by_letter_description": "Afficher des en-têtes alphabétiques dans la liste de contacts", + "group_by_letter_label": "Grouper par première lettre", + "sort_by_last_name_label": "Trier par nom de famille", + "sort_by_last_name_description": "Classer la liste des contacts par nom de famille pour regrouper les membres d'une même famille" + }, + "downloads": { + "title": "Téléchargements", + "description": "Personnalisez la façon dont les e-mails et pièces jointes téléchargés sont nommés.", + "reset": "Restaurer la valeur par défaut", + "preview": "Aperçu :", + "email_template": { + "label": "Nom du fichier e-mail (.eml)", + "description": "Modèle utilisé lors de l'export ou du glisser-déposer d'un e-mail vers le système de fichiers. L'extension .eml est ajoutée automatiquement." + }, + "attachment_template": { + "label": "Nom du fichier de pièce jointe", + "description": "Modèle utilisé lors du téléchargement ou du glisser-déposer d'une pièce jointe. Si vous omettez '{filename}' et '{ext}', l'extension d'origine est conservée." + }, + "bundle_template": { + "label": "Nom du fichier .zip multi-e-mails", + "description": "Modèle utilisé lorsque vous faites glisser ou téléchargez plusieurs e-mails sélectionnés sous forme d'archive .zip unique. L'extension .zip est ajoutée automatiquement." + }, + "spaces": { + "label": "Espaces", + "description": "Remplacer les espaces dans le nom de fichier final par un autre caractère.", + "keep": "Conserver les espaces", + "underscore": "Remplacer par _", + "dash": "Remplacer par -" + }, + "lowercase": { + "label": "Minuscules", + "description": "Forcer tout le nom de fichier en minuscules." + }, + "strip_diacritics": { + "label": "Supprimer les diacritiques", + "description": "Convertir les lettres accentuées en leur équivalent ASCII (ä → a, é → e). Utile pour les outils qui gèrent mal les noms Unicode." + }, + "collapse_separators": { + "label": "Fusionner les séparateurs répétés", + "description": "Fusionner les suites d’espaces, de tirets bas ou de tirets en un seul caractère." + }, + "after_export": { + "label": "Après l’export", + "description": "Déplacer éventuellement l’e-mail après l’avoir exporté en .eml.", + "keep": "Garder dans la boîte", + "archive": "Déplacer vers les archives", + "trash": "Déplacer vers la corbeille" + } + }, + "filters": { + "title": "Filtres de courrier", + "description": "Créez des règles pour trier, étiqueter et gérer automatiquement les courriers entrants", + "add_rule": "Ajouter une règle", + "no_rules": "Aucune règle de filtrage", + "vacation_active": "Le répondeur d'absence est actif", + "vacation_active_description": "La réponse automatique est activée pour les messages entrants", + "vacation_configure": "Configurer", + "no_rules_description": "Créez des règles pour organiser automatiquement vos courriers entrants", + "edit_rule": "Modifier la règle", + "new_rule": "Nouvelle règle", + "delete_rule": "Supprimer la règle", + "delete_confirm": "Êtes-vous sûr de vouloir supprimer cette règle ?", + "enable": "Activer", + "disable": "Désactiver", + "raw_editor": "Éditeur Sieve brut", + "raw_editor_warning": "La modification du script Sieve brut peut casser l'édition visuelle des règles. Les modifications effectuées ici remplacent le constructeur visuel.", + "validate": "Valider", + "validation_success": "Le script est valide", + "validation_error": "Le script contient des erreurs", + "save": "Enregistrer les règles", + "saving": "Enregistrement...", + "saved": "Filtres enregistrés avec succès", + "save_failed": "Échec de l'enregistrement des filtres", + "loading": "Chargement des filtres...", + "not_supported": "Votre serveur de messagerie ne prend pas en charge les filtres de courrier.", + "rule_name": "Nom de la règle", + "rule_name_placeholder": "ex. Trier les newsletters", + "match_all": "Correspondre à TOUTES les conditions", + "match_any": "Correspondre à N'IMPORTE QUELLE condition", + "conditions": "Conditions", + "add_condition": "Ajouter une condition", + "actions": "Actions", + "add_action": "Ajouter une action", + "stop_processing": "Arrêter le traitement des règles suivantes", + "include_spam": "Déplacer aussi les messages marqués comme spam", + "attachment_type_placeholder": "p. ex. pdf, doc, jpg", + "value_placeholder_multi": "Valeur (plusieurs séparées par des virgules)", + "condition_fields": { + "attachment": "Pièce jointe", + "from": "De", + "to": "À", + "cc": "Cc", + "subject": "Objet", + "header": "En-tête personnalisé", + "size": "Taille", + "body": "Corps", + "all": "Tous les messages" + }, + "comparators": { + "has_any": "est présent", + "has_type": "de type", + "contains": "contient", + "not_contains": "ne contient pas", + "is": "est exactement", + "not_is": "n'est pas", + "starts_with": "commence par", + "ends_with": "se termine par", + "matches": "correspond au motif", + "greater_than": "est supérieur à", + "less_than": "est inférieur à", + "address_is": "est l'adresse", + "domain_is": "a pour domaine" + }, + "action_types": { + "move": "Déplacer vers le dossier", + "copy": "Copier dans le dossier", + "forward": "Transférer à", + "mark_read": "Marquer comme lu", + "star": "Marquer d'une étoile", + "add_label": "Ajouter une étiquette", + "discard": "Supprimer silencieusement", + "reject": "Rejeter avec un message", + "keep": "Conserver dans la boîte de réception", + "stop": "Arrêter le traitement" + }, + "move_to_folder": "Sélectionner le dossier", + "copy_to_folder": "Sélectionner le dossier", + "forward_to": "Transférer à l'adresse e-mail", + "forward_placeholder": "email@exemple.com", + "forward_keep_copy": "Garder une copie", + "forward_limit": "Limite de transferts par message sur ce serveur : {count}. Les transferts en trop sont ignorés.", + "forward_unavailable": "Ce serveur n'autorise pas le transfert. Les actions de transfert des règles de filtrage n'ont aucun effet.", + "reject_message": "Message de rejet", + "reject_placeholder": "Votre e-mail a été rejeté", + "label_name": "Nom de l'étiquette", + "label_placeholder": "Sélectionner une étiquette", + "header_name": "Nom de l'en-tête", + "header_placeholder": "ex. X-Mailing-List", + "size_bytes": "Taille en octets", + "size_placeholder": "ex. 1000000", + "system_managed": "Règle gérée par le système", + "opaque_warning": "Ce script a été modifié en dehors du constructeur visuel. Seule l'édition Sieve brute est disponible.", + "open_sieve_editor": "Ouvrir l'éditeur Sieve brut", + "fetch_error": "Échec du chargement des filtres", + "expanded_view": "Vue étendue", + "expanded_view_description": "Afficher les règles de filtre avec des blocs de conditions et d'actions détaillés", + "if": "Si", + "then": "Alors", + "match_all_conditions": "toutes correspondent", + "match_any_condition": "une correspond", + "and": "et", + "or": "ou", + "cancel": "Annuler", + "confirm_delete": "Supprimer", + "rule_list": "Règles de filtrage", + "drag_to_reorder": "Glisser pour réorganiser", + "match_type": "Type de correspondance", + "reset_to_visual": "Revenir au constructeur visuel", + "reset_warning": "Cela supprimera le script actuel et recommencera à zéro.", + "confirm_reset": "Réinitialiser", + "validation_empty_name": "Le nom de la règle est requis", + "validation_empty_conditions": "Au moins une condition avec une valeur est requise", + "validation_empty_actions": "Au moins une action est requise", + "templates_section": "Partir d'un modèle", + "template_newsletters": "Déplacer les newsletters dans un dossier", + "template_receipts": "Archiver les reçus automatiquement", + "template_important": "Marquer les e-mails importants", + "template_notifications": "Filtrer les notifications", + "sieve_editor": { + "title": "Éditeur de script Sieve", + "warning": "La modification du script Sieve brut peut casser l'édition visuelle des règles. Les modifications effectuées ici remplacent le constructeur visuel.", + "script_content": "Script Sieve", + "valid": "Le script est valide", + "invalid": "Le script contient des erreurs", + "save_warning": "L'enregistrement écrasera toutes les règles visuelles. Cette action est irréversible. Cliquez à nouveau sur Enregistrer pour confirmer.", + "validating": "Validation...", + "validate": "Valider", + "cancel": "Annuler", + "save": "Enregistrer", + "confirm_save": "Confirmer l'enregistrement", + "validation_failed": "Échec de la requête de validation" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# condition} other {# conditions}}", + "actions_count": "{count, plural, one {# action} other {# actions}}" + }, + "origin_external": "Externe", + "managed_by_tooltip": "Géré par {source}. Modifiez-le dans cette application, ou utilisez l'éditeur Sieve brut.", + "quick_rule_names": { + "move_sender": "Déplacer de {sender} vers {folder}", + "move_domain": "Déplacer de @{domain} vers {folder}", + "move_list": "Déplacer la liste {list} vers {folder}", + "mark_read": "Marquer comme lus les messages de {sender}", + "tag": "Étiqueter {tag} les messages de {sender}", + "block": "Bloquer {sender}", + "custom": "Messages de {sender}", + "custom_plain": "Nouvelle règle", + "senders": "{count, plural, one {# expéditeur} other {# expéditeurs}}" + }, + "suggestions": "Suggestions", + "suggest_subject": "Objet « {subject} »", + "suggest_to": "À {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Appliquer aussi aux messages déjà présents dans ce dossier", + "apply_existing_unsupported": "Seules les règles qui vérifient l'expéditeur, les destinataires, l'objet ou les en-têtes, et qui déplacent, copient, marquent comme lu, suivent ou étiquettent, peuvent s'appliquer aux messages existants.", + "period_title": "Période", + "period_start": "Date de début", + "period_end": "Date de fin", + "validation_period_order": "La date de fin doit être postérieure à la date de début", + "period_toggle": "Active uniquement pendant une période", + "period_hint": "S'applique aux messages qui arrivent pendant cette période. Laissez un champ vide pour ne pas limiter ce côté.", + "validation_period_empty": "Indiquez une date de début ou de fin pour la période", + "validation_period_invalid": "Indiquez une date valide avec l'heure pour la période, ou laissez le champ vide", + "apply_existing_period": "Une règle avec une période n'agit que sur les messages qui arrivent pendant celle-ci ; elle ne peut donc pas s'appliquer aux messages existants.", + "period_status_scheduled": "planifiée", + "period_status_active": "active", + "period_status_expired": "expirée" + }, + "templates": { + "title": "Modèles d'e-mails", + "description": "Créez des modèles d'e-mails réutilisables avec des variables", + "add": "Nouveau modèle", + "edit": "Modifier le modèle", + "name": "Nom du modèle", + "name_placeholder": "ex : E-mail de suivi", + "category": "Catégorie", + "category_placeholder": "ex : Travail, Personnel", + "subject": "Objet", + "subject_placeholder": "Objet de l'e-mail", + "body": "Corps", + "body_placeholder": "Contenu de l'e-mail...", + "recipients_placeholder": "email@example.com", + "identity": "Envoyer en tant que", + "default_identity": "Identité par défaut", + "favorite": "Favori", + "cancel": "Annuler", + "create": "Créer", + "update": "Mettre à jour", + "confirm_delete": "Supprimer", + "no_templates": "Aucun modèle", + "manage": "Gérer les modèles", + "count": "{count, plural, one {# modèle} other {# modèles}}", + "export_import": "Exporter et importer", + "export_import_description": "Sauvegardez vos modèles ou transférez-les sur un autre appareil", + "export": "Exporter", + "import": "Importer", + "validation": { + "empty": "Le nom du modèle est requis", + "too_long": "Le nom du modèle ne doit pas dépasser 200 caractères" + } + }, + "files": { + "display": { + "title": "Affichage", + "description": "Configurez l'affichage des fichiers et dossiers" + }, + "default_view": { + "label": "Vue par défaut", + "description": "Choisissez entre la disposition en grille et en liste", + "list": "Liste", + "grid": "Grille" + }, + "default_sort": { + "label": "Tri par défaut", + "description": "Choisissez le tri par défaut pour les fichiers", + "name": "Nom", + "size": "Taille", + "modified": "Modifié" + }, + "sort_direction": { + "label": "Sens du tri", + "description": "Choisissez l'ordre croissant ou décroissant", + "ascending": "Croissant", + "descending": "Décroissant" + }, + "icons": { + "title": "Icônes", + "description": "Configurez l'apparence des icônes de fichiers" + }, + "show_icons": { + "label": "Afficher les icônes", + "description": "Afficher les icônes à côté des fichiers et dossiers" + }, + "colored_icons": { + "label": "Icônes colorées", + "description": "Utiliser des icônes colorées au lieu de monochromes" + }, + "show_thumbnails": { + "label": "Afficher les miniatures", + "description": "Afficher les aperçus d'images au lieu des icônes" + }, + "behavior": { + "title": "Comportement", + "description": "Configurez le comportement du gestionnaire de fichiers" + }, + "show_hidden": { + "label": "Afficher les fichiers cachés", + "description": "Afficher les fichiers et dossiers commençant par un point" + }, + "preview": { + "label": "Aperçu" + } + }, + "search_placeholder": "Rechercher dans les paramètres", + "search_clear": "Effacer la recherche", + "search_no_results": "Aucun paramètre correspondant", + "scoped": { + "back": "Retour à mon compte", + "managing": "Gestion : {name}" + } + }, + "errors": { + "page_error_title": "Une erreur s'est produite", + "page_error_description": "Nous avons rencontré une erreur inattendue. Veuillez réessayer ou retourner à la page d'accueil.", + "sidebar_error": "Impossible de charger les boîtes mail", + "email_list_error": "Impossible de charger les emails", + "viewer_error_title": "Impossible d'afficher l'email", + "viewer_error_description": "Un problème est survenu lors de l'affichage de cet email. Il peut contenir du contenu non pris en charge.", + "composer_error": "Impossible de charger l'éditeur", + "settings_error_title": "Paramètres indisponibles", + "settings_error_description": "Impossible de charger les paramètres. Vos préférences peuvent ne pas être sauvegardées.", + "try_again": "Réessayer", + "reload": "Recharger", + "reload_emails": "Recharger les emails", + "reload_settings": "Recharger les paramètres", + "retry": "Réessayer", + "go_home": "Aller à la boîte de réception" + }, + "context_menu": { + "reply": "Répondre", + "reply_all": "Répondre à tous", + "forward": "Transférer", + "mark_read": "Marquer comme lu", + "mark_unread": "Marquer comme non lu", + "star": "Marquer comme favori", + "unstar": "Retirer des favoris", + "pin": "Épingler", + "unpin": "Désépingler", + "move_to": "Déplacer vers...", + "copy_to_account": "Copier vers un compte...", + "remove_from_folder": "Retirer de ce dossier", + "archive": "Archiver", + "delete": "Supprimer", + "mark_as_spam": "Signaler comme spam", + "not_spam": "Pas un spam", + "tag": "Étiquette", + "items_selected": "{count} emails sélectionnés", + "forward_as_attachments": "Transférer en pièces jointes", + "edit_draft": "Modifier le brouillon", + "cancel_scheduled_send": "Annuler l’envoi", + "reschedule_send": "Replanifier", + "cancel_and_edit": "Annuler et modifier", + "cancel_and_compose_again": "Annuler et rédiger à nouveau", + "rules": { + "title": "Règles", + "move_from": "Toujours déplacer les messages de {sender}", + "move_from_domain": "Toujours déplacer les messages de @{domain}", + "move_from_list": "Toujours déplacer les messages de cette liste", + "mark_read_from": "Toujours marquer comme lus les messages de {sender}", + "tag_with": "Toujours étiqueter avec", + "block": "{count, plural, one {Bloquer l'expéditeur} other {Bloquer # expéditeurs}}", + "no_junk": "Ce compte n'a pas de dossier Indésirables", + "create": "Créer une règle…", + "manage": "Gérer les règles", + "senders_count": "{count, plural, one {# expéditeur} other {# expéditeurs}}", + "cross_account": "Sélectionnez des messages d'un seul compte", + "opaque_hint": "Vos filtres ont été modifiés à la main. Ouvrez les réglages des filtres" , + "loading": "Chargement des règles…", + "load_failed": "Impossible de charger les règles", + "none": "Aucune règle pour l'instant", + "new_mail_only": "Uniquement pour les nouveaux messages", + "running": "Exécution de « {name} » sur {folder}…" + }, + "find_emails_from": "Rechercher les e-mails de {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Marquer le dossier comme lu", + "mark_folder_tree_read": "Marquer le dossier et les sous-dossiers comme lus", + "mark_all_folders_read": "Marquer tous les dossiers comme lus", + "new_subfolder": "Nouveau sous-dossier...", + "new_folder": "Nouveau dossier...", + "rename": "Renommer...", + "share": "Partager...", + "import_email": "Importer .eml ou .zip...", + "empty_folder": "Vider le dossier", + "empty_folder_generic": "Vider le dossier", + "delete_folder": "Supprimer le dossier", + "refresh": "Actualiser", + "hide_shared_account": "Masquer de la barre latérale", + "mute_notifications": "Désactiver les notifications", + "unmute_notifications": "Réactiver les notifications", + "mute_account_notifications": "Désactiver les notifications de ce compte", + "unmute_account_notifications": "Réactiver les notifications de ce compte", + "reset_folder_notifications": "Réinitialiser les notifications des dossiers", + "mark_all_confirm_title": "Marquer tous les dossiers comme lus", + "mark_all_confirm_message": "Marquer tous les messages non lus de votre compte personnel comme lus ?", + "delete_confirm_title": "Supprimer le dossier", + "delete_confirm_message": "Supprimer définitivement le dossier \"{name}\" ? Cette action est irréversible.", + "prompt_new_subfolder": "Entrez un nom pour le nouveau sous-dossier.", + "prompt_new_folder": "Entrez un nom pour le nouveau dossier.", + "prompt_rename": "Entrez un nouveau nom pour ce dossier.", + "toast_marked_read": "Dossier marqué comme lu", + "toast_marked_read_count": "{count, plural, one {1 message marqué} other {# messages marqués}} comme lu(s)", + "toast_already_read": "Aucun message non lu", + "toast_marked_all_read": "Tous les dossiers marqués comme lus", + "toast_emptied": "Dossier vidé", + "toast_folder_created": "Dossier créé", + "toast_folder_renamed": "Dossier renommé", + "toast_folder_deleted": "Dossier supprimé", + "toast_error_mark_read": "Échec du marquage comme lu", + "toast_error_empty": "Échec du vidage du dossier", + "toast_error_create": "Échec de la création du dossier", + "toast_error_rename": "Échec du renommage du dossier", + "toast_error_delete": "Échec de la suppression du dossier", + "toast_error_delete_has_children": "Le dossier contient des sous-dossiers. Supprimez-les d'abord.", + "toast_error_delete_has_email": "Le dossier n'est pas vide. Videz-le d'abord.", + "delete_with_contents_title": "Le dossier n'est pas vide", + "delete_with_contents_message": "Le dossier « {name} » contient encore {count, plural, one {1 message} other {# messages}}. Supprimer le dossier et tous ses messages ? Cette action est irréversible.", + "delete_with_contents_confirm": "Supprimer le dossier et les messages", + "placeholder_folder_name": "Nom du dossier", + "create": "Créer", + "rename_confirm": "Renommer" + }, + "shortcuts": { + "title": "Raccourcis clavier", + "tip": "Appuyez sur ? à tout moment pour afficher cette aide", + "sections": { + "navigation": "Navigation", + "actions": "Actions email", + "global": "Global", + "threads": "Conversations", + "composer": "Rédaction" + }, + "navigation": { + "next_email": "Email suivant", + "previous_email": "Email précédent", + "open_email": "Ouvrir l'email", + "close_email": "Fermer / Désélectionner", + "go_to_folder": "Aller au dossier", + "next_unread": "E-mail non lu suivant", + "no_more_unread": "Aucun e-mail non lu plus bas dans la liste" + }, + "actions": { + "reply": "Répondre", + "reply_all": "Répondre à tous", + "forward": "Transférer", + "star": "Basculer favori", + "archive": "Archiver", + "delete": "Supprimer", + "mark_unread": "Marquer comme non lu", + "mark_read": "Marquer comme lu", + "toggle_spam": "Signaler / Pas un spam" + }, + "global": { + "compose": "Composer un email", + "search": "Rechercher", + "help": "Afficher les raccourcis", + "refresh": "Actualiser les emails", + "select_all": "Tout sélectionner", + "global_search": "Tout rechercher", + "extend_selection": "Étendre la sélection vers le bas / le haut" + }, + "threads": { + "expand_collapse": "Développer/réduire la conversation", + "select_mountain_view": "Sélectionner la conversation" + }, + "composer": { + "send": "Envoyer l'e-mail", + "schedule_send": "Planifier l'envoi", + "template_picker": "Ouvrir le sélecteur de modèles" + } + }, + "threads": { + "messages_one": "{count} message", + "messages_other": "{count} messages", + "messages_tooltip": "{count, plural, one {# message dans cette conversation} other {# messages dans cette conversation}}", + "expand": "Développer la conversation", + "collapse": "Réduire la conversation", + "loading": "Chargement de la conversation...", + "mark_read": "Marquer la conversation comme lue", + "mark_unread": "Marquer la conversation comme non lue", + "archive": "Archiver la conversation", + "delete": "Supprimer la conversation", + "star": "Marquer la conversation comme favorite", + "unstar": "Retirer des favoris", + "toggle_thread": "Déplier/replier la discussion" + }, + "identities": { + "modal_title": "Gérer les identités d'envoi", + "create_new": "Créer une nouvelle identité", + "edit_identity": "Modifier l'identité", + "delete_confirm": "Supprimer cette identité ? Cette action est irréversible.", + "cannot_delete": "Cette identité ne peut pas être supprimée", + "primary_identity": "Principale", + "set_as_primary": "Définir comme principale", + "no_identities": "Aucune identité trouvée", + "display": { + "reply_to": "Répondre à :", + "bcc": "CCI :", + "signature": "Signature :", + "preview": "Aperçu :" + }, + "validation_errors": { + "invalid_emails": "Emails invalides : {emails}", + "unknown_error": "Erreur inconnue" + }, + "form": { + "name_label": "Nom d'affichage", + "name_placeholder": "ex: Email professionnel, Personnel", + "name_required": "Le nom est requis", + "email_label": "Adresse email", + "email_placeholder": "votre.email@exemple.com", + "email_required": "L'email est requis", + "email_invalid": "Veuillez entrer une adresse email valide", + "email_immutable": "L'adresse email ne peut pas être modifiée après création", + "reply_to_label": "Répondre à (optionnel)", + "reply_to_placeholder": "autre@example.com", + "bcc_label": "Copie cachée automatique (optionnel)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Signature texte", + "html_signature_label": "Signature HTML", + "signature_byte_counter": "{bytes} / {max} octets", + "signature_byte_limit_reached": "Limite du serveur atteinte", + "save": "Enregistrer l'identité", + "cancel": "Annuler", + "creating": "Création...", + "updating": "Mise à jour..." + }, + "sub_address": { + "button_tooltip": "Utiliser le sous-adressage", + "popover_title": "Ajouter un tag de sous-adresse", + "tag_input_placeholder": "Entrez un tag (ex: shopping)", + "preview_label": "Aperçu:", + "recent_tags": "Tags récents", + "suggested_tags": "Suggérés", + "use_address": "Utiliser cette adresse", + "invalid_tag": "Le tag doit contenir uniquement des lettres, chiffres et tirets", + "tag_too_long": "Le tag doit faire 30 caractères ou moins", + "help_text": "Les emails envoyés à utilisateur{delimiter}tag@domaine.com arriveront dans votre boîte de réception", + "validation": { + "empty": "Le tag ne peut pas être vide", + "too_long": "Le tag doit faire {max} caractères ou moins", + "invalid_chars": "Le tag doit contenir uniquement des lettres, chiffres et tirets" + } + }, + "badge": { + "sent_via": "via", + "sub_address_tag": "Envoyé avec le sous-adresse: {tag}", + "identity_name": "Envoyé avec l'identité: {name}", + "identity_short": "via {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Supprimer", + "delete_confirm_title": "Supprimer l'identité" + }, + "templates": { + "picker_title": "Choisir un modèle", + "search_placeholder": "Rechercher des modèles...", + "section_favorites": "Favoris", + "section_recent": "Récents", + "section_uncategorized": "Autres", + "no_templates": "Aucun modèle", + "no_results": "Aucun modèle trouvé", + "fill_placeholders": "Remplir les variables", + "enter_value": "Entrez une valeur...", + "preview": "Aperçu", + "insert_with_values": "Insérer avec les valeurs", + "insert_raw": "Insérer brut", + "copy_suffix": "(copie)", + "placeholder": "Variable", + "placeholders": { + "recipient_name": "Nom du destinataire", + "company": "Nom de l'entreprise", + "date": "Date du jour", + "day_of_week": "Jour de la semaine", + "sender_name": "Votre nom" + } + }, + "contacts": { + "directory": { + "title": "Annuaire", + "search_placeholder": "Rechercher un nom ou une adresse e-mail", + "refresh": "Actualiser l'annuaire", + "retry": "Réessayer", + "loading": "Chargement de l'annuaire…", + "unavailable_title": "Pas d'annuaire", + "unavailable_hint": "Votre serveur de messagerie ne propose pas d'annuaire des comptes.", + "forbidden_title": "Annuaire indisponible", + "forbidden_hint": "Votre serveur de messagerie n'autorise pas ce compte à parcourir l'annuaire. Contactez votre administrateur.", + "error_title": "Impossible de charger l'annuaire", + "error_hint": "Vérifiez votre connexion et réessayez.", + "empty_title": "L'annuaire est vide", + "empty_hint": "Votre serveur de messagerie ne liste aucun compte avec une adresse e-mail.", + "no_match": "Personne dans l'annuaire ne correspond à votre recherche", + "refresh_failed": "Impossible d'actualiser l'annuaire. Affichage de la dernière liste chargée.", + "select_page": "Sélectionner tout le monde sur cette page", + "deselect_page": "Désélectionner tout le monde sur cette page", + "select_entry": "Sélectionner {name}", + "range": "{from}–{to} sur {total}", + "page": "Page {page} sur {count}", + "previous_page": "Page précédente", + "next_page": "Page suivante", + "picker_title": "Choisir dans l'annuaire", + "cancel": "Annuler", + "selected_count": "{count} sélectionné(s)", + "add_to": "Ajouter à À", + "add_cc": "Ajouter à Cc", + "add_bcc": "Ajouter à Cci", + "select_hint": "Sélectionnez une personne pour voir ses détails", + "write_email": "Écrire un e-mail", + "add_to_contacts": "Ajouter aux contacts", + "email": "E-mail", + "read_only_note": "Les entrées de l'annuaire proviennent de votre serveur de messagerie et ne peuvent pas être modifiées ici.", + "type": { + "individual": "Personne", + "group": "Groupe", + "resource": "Ressource", + "location": "Lieu", + "other": "Autre" + } + }, + "title": "Contacts", + "search_placeholder": "Rechercher des contacts...", + "create_new": "Nouveau contact", + "no_category": "Sans catégorie", + "rename_category": "Renommer la catégorie", + "category_name_label": "Nom de la catégorie", + "category_renamed": "Catégorie renommée", + "category_rename_failed": "Échec du renommage de la catégorie", + "category_added": "Contact ajouté à {name}", + "category_added_plural": "{count} contacts ajoutés à {name}", + "empty_state": "Aucun contact", + "empty_state_title": "Aucun contact", + "empty_state_subtitle": "Créez votre premier contact ou importez depuis un fichier vCard", + "empty_search": "Aucun contact ne correspond à votre recherche", + "empty_search_hint": "Essayez un autre terme de recherche", + "empty_filtered": "Aucun contact ne correspond à vos filtres", + "empty_filtered_hint": "Ajustez ou effacez les filtres", + "clear_search": "Effacer la recherche", + "import_vcard": "Importer vCard", + "delete_confirm_title": "Supprimer le contact", + "delete_confirm": "Êtes-vous sûr de vouloir supprimer ce contact ?", + "local_mode": "Les contacts sont stockés localement (le serveur ne prend pas en charge JMAP Contacts)", + "back_to_contacts": "Retour aux contacts", + "back_to_email": "Retour à l'e-mail", + "tabs": { + "all": "Tous", + "groups": "Groupes" + }, + "shared": { + "title": "Partagés" + }, + "address_books": { + "create": "Nouveau carnet d'adresses", + "created": "Carnet d'adresses créé", + "create_failed": "Échec de la création du carnet d'adresses", + "title": "Mes Carnets d'adresses", + "shared_prefix": "Partagé : {name}", + "moved": "Contact déplacé vers {name}", + "moved_plural": "{count} contacts déplacés vers {name}", + "move_failed": "Échec du déplacement du contact", + "address_book": "Carnet d'adresses", + "rename": "Renommer le carnet d'adresses", + "name_label": "Nom du carnet d'adresses", + "renamed": "Carnet d'adresses renommé", + "rename_failed": "Échec du renommage du carnet d'adresses", + "default": "Par défaut", + "set_default": "Définir par défaut", + "default_updated": "Carnet d’adresses par défaut mis à jour", + "set_default_failed": "Échec de la définition du carnet d’adresses par défaut", + "manage": "Gérer les carnets d'adresses", + "share": "Partager le carnet d'adresses", + "new_contact_in_book": "Nouveau contact dans ce carnet d'adresses", + "delete": "Supprimer le carnet d'adresses", + "confirm_delete": "Supprimer « {name} » ? Tous les contacts de ce carnet d'adresses seront supprimés.", + "deleted": "Carnet d'adresses supprimé", + "delete_failed": "Échec de la suppression du carnet d'adresses" + }, + "detail": { + "emails": "Adresses e-mail", + "phones": "Numéros de téléphone", + "organizations": "Organisations", + "addresses": "Adresses", + "notes": "Notes", + "titles": "Titres et rôles", + "online_services": "Services en ligne", + "anniversaries": "Anniversaires", + "personal_info": "Informations personnelles", + "languages": "Langues", + "categories": "Catégories", + "related_contacts": "Contacts associés", + "crypto_keys": "Clés cryptographiques", + "no_contact_selected": "Sélectionnez un contact pour voir les détails", + "compose_email": "Écrire un e-mail", + "copy_email": "Copier l'e-mail", + "copy_phone": "Copier le numéro", + "copy_url": "Copier l'URL", + "copied": "Copié dans le presse-papiers", + "copy_failed": "Échec de la copie dans le presse-papiers", + "created": "Créé", + "updated": "Dernière mise à jour", + "timezone": "Fuseau horaire", + "anniversary_birth": "Anniversaire", + "anniversary_death": "Décès", + "anniversary_wedding": "Anniversaire de mariage", + "anniversary_other": "Autre", + "personal_expertise": "Expertise", + "personal_hobby": "Loisir", + "personal_interest": "Intérêt", + "personal_other": "Autre", + "gender": "Genre", + "gender_masculine": "Masculin", + "gender_feminine": "Féminin", + "gender_other": "Autre", + "gender_none": "Non applicable", + "gender_unknown": "Inconnu", + "calendar": "Calendrier", + "calendar_uri": "URL du calendrier", + "scheduling_uri": "URL de planification", + "freebusy_uri": "URL de disponibilité", + "cert_issuer": "Émetteur", + "cert_expires": "Expire le", + "cert_expired": "Expiré", + "cert_fingerprint": "Empreinte", + "cert_algorithm": "Algorithme", + "import_to_smime": "Importer dans S/MIME", + "cert_already_imported": "Certificat déjà importé", + "cert_imported": "Certificat importé", + "cert_import_failed": "Échec de l'import du certificat", + "section_contact": "Détails du contact", + "section_work": "Travail", + "section_personal": "Personnel", + "email_default_label": "E-mail", + "phone_default_label": "Téléphone", + "address_default_label": "Adresse", + "online_service_default_label": "En ligne", + "organization_label": "Organisation", + "title_label": "Titre", + "role_label": "Rôle", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Plus d'actions", + "age_years": "{count, plural, one {1 an} other {# ans}}", + "years_since": "{count, plural, one {il y a 1 an} other {il y a # ans}}" + }, + "activity": { + "recent_emails": "Courriels récents", + "upcoming_events": "Évènements à venir", + "no_emails": "Aucun courriel récent", + "no_events": "Aucun évènement à venir", + "no_subject": "(Aucun objet)", + "no_title": "(Aucun titre)", + "load_failed": "Échec du chargement", + "unknown_sender": "Unknown sender", + "all_day": "Toute la journée" + }, + "form": { + "create_title": "Nouveau contact", + "edit_title": "Modifier le contact", + "section_address_book": "Répertoire", + "select_address_book": "Sélectionner un répertoire...", + "section_identity": "Nom et identité", + "contact_type": "Type de contact", + "type_person": "Personne", + "type_organization": "Organisation", + "section_work": "Travail et organisation", + "prefix": "Préfixe", + "prefix_placeholder": "Dr., M., Mme", + "given_name": "Prénom", + "middle_name": "Deuxième prénom", + "surname": "Nom", + "suffix": "Suffixe", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Surnom", + "nickname_placeholder": "Surnom", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Téléphone", + "phone_placeholder": "+33 1 23 45 67 89", + "phone_type": "Type", + "phone_voice": "Voix", + "phone_cell": "Mobile", + "phone_fax": "Fax", + "phone_pager": "Bip", + "phone_video": "Vidéo", + "phone_text": "SMS", + "organization": "Organisation", + "organization_placeholder": "Nom de l'entreprise", + "department": "Département", + "department_placeholder": "Département", + "job_title": "Intitulé du poste", + "job_title_placeholder": "p. ex., Ingénieur logiciel", + "role": "Rôle", + "role_placeholder": "p. ex., Chef d'équipe", + "addresses": "Adresses", + "add_address": "Ajouter une adresse", + "street": "Rue", + "city": "Ville", + "region": "État / Région", + "postcode": "Code postal", + "country": "Pays", + "online_services": "Services en ligne", + "add_online_service": "Ajouter un service en ligne", + "url_placeholder": "https://...", + "service_placeholder": "Service", + "anniversaries": "Anniversaires", + "add_anniversary": "Ajouter une date", + "anniversary_birth": "Anniversaire", + "anniversary_wedding": "Anniversaire de mariage", + "anniversary_death": "Commémoration", + "anniversary_other": "Autre", + "personal_info": "Informations personnelles", + "add_personal_info": "Ajouter une entrée", + "personal_info_placeholder": "p. ex., Photographie", + "personal_expertise": "Expertise", + "personal_hobby": "Loisir", + "personal_interest": "Intérêt", + "personal_other": "Autre", + "level": "Niveau", + "level_high": "Élevé", + "level_medium": "Moyen", + "level_low": "Faible", + "categories": "Catégories", + "categories_placeholder": "p. ex., Famille, Amis, Collègues", + "categories_hint": "Tapez pour rechercher ou ajouter", + "category_add": "Ajouter", + "note": "Notes", + "note_placeholder": "Ajouter une note...", + "gender": "Genre", + "gender_sex": "Sexe", + "gender_male": "Masculin", + "gender_female": "Féminin", + "gender_other": "Autre", + "gender_none": "Non applicable", + "gender_unknown": "Inconnu", + "gender_identity": "Identité de genre", + "gender_identity_placeholder": "Identité de genre...", + "calendar": "Calendrier", + "calendar_uri": "URL du calendrier", + "scheduling_uri": "URL de planification", + "freebusy_uri": "URL de disponibilité", + "context_work": "Professionnel", + "context_private": "Personnel", + "add_email": "Ajouter un e-mail", + "add_phone": "Ajouter un téléphone", + "save": "Enregistrer", + "cancel": "Annuler", + "creating": "Création...", + "updating": "Mise à jour...", + "name_required": "Saisissez un prénom, un nom ou une organisation", + "email_invalid": "Veuillez saisir une adresse e-mail valide", + "email_error_inline": "Format d'e-mail invalide", + "save_failed": "Échec de l'enregistrement du contact", + "delete": "Supprimer", + "upload_photo": "Téléverser une photo", + "remove_photo": "Supprimer la photo", + "photo_hint": "JPG ou PNG, jusqu’à 10 Mo. Sera redimensionnée.", + "photo_too_large": "L’image est trop grande (max 10 Mo)", + "photo_invalid": "Fichier image invalide", + "change_photo": "Modifier" + }, + "groups": { + "create": "Nouveau groupe", + "edit": "Modifier le groupe", + "empty": "Aucun groupe", + "delete_confirm_title": "Supprimer le groupe", + "delete_confirm": "Êtes-vous sûr de vouloir supprimer ce groupe ?", + "name_label": "Nom du groupe", + "name_placeholder": "ex. Équipe, Famille", + "name_required": "Le nom du groupe est requis", + "save_failed": "Échec de l'enregistrement du groupe", + "members_label": "Membres", + "search_members": "Rechercher des contacts à ajouter...", + "no_members": "Aucun membre dans ce groupe", + "member_count": "{count, plural, =0 {Aucun membre} one {1 membre} other {# membres}}", + "send_email": "Send email to group", + "send_email_to": "À", + "send_email_cc": "Cc", + "send_email_bcc": "Cci", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Importer des contacts", + "drop_hint": "Cliquez pour sélectionner un fichier vCard", + "file_types": "Fichiers .vcf ou .vcard", + "no_contacts": "Aucun contact trouvé dans le fichier", + "parse_error": "Échec de l'analyse du fichier vCard", + "found": "{count, plural, one {1 contact trouvé} other {# contacts trouvés}}", + "duplicate": "Doublon", + "select_all": "Tout sélectionner", + "deselect_all": "Tout désélectionner", + "selected": "{count, plural, one {1 sélectionné} other {# sélectionnés}}", + "import_button": "Importer", + "importing": "Importation...", + "success": "{count, plural, one {1 contact importé} other {# contacts importés}}", + "failed": "Échec de l'importation", + "close": "Fermer", + "file_too_large": "Fichier trop volumineux (max 5 Mo)" + }, + "export": { + "title": "Exporter les contacts", + "success": "{count, plural, one {1 contact exporté} other {# contacts exportés}}" + }, + "bulk": { + "selected": "{count, plural, one {1 sélectionné} other {# sélectionnés}}", + "select_all": "Tout sélectionner", + "delete": "Supprimer", + "delete_confirm_title": "Supprimer les contacts", + "delete_confirm": "Supprimer {count, plural, one {1 contact} other {# contacts}} ?", + "deleted": "{count, plural, one {1 contact supprimé} other {# contacts supprimés}}", + "add_to_group": "Ajouter au groupe", + "choose_group": "Choisir un groupe", + "adding_contacts": "Ajout de {count, plural, one {1 contact} other {# contacts}}", + "added_to_group": "Contacts ajoutés au groupe", + "export": "Exporter", + "clear": "Effacer la sélection" + }, + "toast": { + "created": "Contact créé", + "updated": "Contact mis à jour", + "deleted": "Contact supprimé", + "error_create": "Échec de la création du contact", + "error_update": "Échec de la mise à jour du contact", + "error_delete": "Échec de la suppression du contact" + }, + "context_menu": { + "open": "Ouvrir", + "edit": "Modifier", + "send_email": "Envoyer un courriel", + "add_to_group": "Ajouter au groupe", + "export_vcard": "Exporter en vCard", + "delete": "Supprimer", + "call": "Appeler", + "duplicate": "Dupliquer", + "print": "Imprimer" + }, + "filters": { + "toggle": "Filtres", + "select": "Sélectionner", + "clear": "Effacer", + "close": "Fermer", + "title": "Filtres avancés", + "organization": "Entreprise", + "organization_placeholder": "ex. Acme", + "job_title": "Poste", + "job_title_placeholder": "ex. Designer", + "location": "Lieu", + "location_placeholder": "Ville ou pays", + "email_domain": "Domaine e-mail", + "email_domain_placeholder": "exemple.com", + "birthday_month": "Anniversaire en", + "any_month": "Tous les mois", + "has_email": "Avec e-mail", + "has_phone": "Avec téléphone", + "has_photo": "Avec photo", + "sort_by": "Trier par", + "sort_first_name": "Prénom", + "sort_last_name": "Nom de famille" + }, + "open_categories": "Ouvrir les catégories" + }, + "calendar": { + "title": "Calendrier", + "back_to_email": "Retour aux e-mails", + "back_to_month": "Retour au mois", + "my_calendars": "Mes calendriers", + "birthday_calendar": "Anniversaires", + "mini_calendar_change": "Cliquer pour changer de mois", + "views": { + "month": "Mois", + "week": "Semaine", + "day": "Jour", + "agenda": "Agenda", + "today": "Aujourd'hui", + "month_hint": "Mois (m)", + "week_hint": "Semaine (w)", + "day_hint": "Jour (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Tâches", + "tasks_hint": "Tâches (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# événement plus tôt} other {# événements plus tôt}}", + "hidden_after": "{count, plural, one {# événement plus tard} other {# événements plus tard}}", + "show_all_hours": "Afficher toutes les heures", + "show_display_hours": "Afficher seulement {start} – {end}", + "create": "Créer un événement", + "edit": "Modifier l'événement", + "delete": "Supprimer l'événement", + "details": "Détails de l'événement", + "no_events": "Aucun événement", + "all_day": "Toute la journée", + "more": "+{count} de plus", + "show_more": "Afficher plus", + "show_less": "Afficher moins", + "no_title": "(Sans titre)", + "resize": "Redimensionner l'événement", + "duplicate": "Dupliquer", + "today_header": "Aujourd'hui", + "tomorrow_header": "Demain", + "agenda_show_earlier": "Afficher les événements précédents", + "agenda_loading": "Chargement…", + "agenda_range_start": "Événements à partir du {date}", + "agenda_range_end": "Événements jusqu'au {date}", + "export_ics": "Exporter en .ics", + "copy_title": "Copier le titre", + "copy_link": "Copier le lien de réunion", + "go_to_today": "Aller à aujourd'hui", + "new_all_day_event": "Nouvel événement sur la journée", + "new_event": "Nouvel événement", + "new_task": "Nouvelle tâche" + }, + "detail": { + "add_note": "Ajouter une note...", + "save_note": "Enregistrer", + "note_saved": "Note ajoutée", + "open_link": "Ouvrir le lien", + "copy_location": "Copier le lieu", + "meeting_link": "Lien de réunion", + "tentative": "Provisoire", + "cancelled": "Annulé", + "delete_confirm": "Supprimer cet événement ?" + }, + "form": { + "title": "Titre", + "description": "Description", + "location": "Lieu", + "meeting_link": "Lien de réunion", + "start_date": "Date de début", + "end_date": "Date de fin", + "start_time": "Heure de début", + "end_time": "Heure de fin", + "all_day_event": "Événement sur toute la journée", + "calendar_select": "Calendrier", + "save": "Enregistrer", + "cancel": "Annuler", + "delete_confirm": "Êtes-vous sûr de vouloir supprimer cet événement ?", + "color": "Couleur" + }, + "participants": { + "availability": { + "title": "Disponibilité", + "free": "Disponible", + "busy": "Occupé", + "tentative": "Provisoire", + "unknown": "Pas un utilisateur de ce serveur", + "checking": "Vérification…" + }, + "title": "Participants", + "add": "Ajouter un participant", + "organizer": "Organisateur", + "attendee": "Participant", + "accepted": "Accepté", + "declined": "Refusé", + "tentative": "Provisoire", + "needs_action": "En attente de réponse", + "remove": "Retirer", + "edit": "Modifier", + "email_placeholder": "Ajouter une adresse e-mail ou chercher des contacts", + "send_invitations": "Envoyer les invitations aux participants", + "status_summary": "{accepted} accepté(s), {pending} en attente", + "invited_by": "Invitation de {name}", + "respond_below": "Répondez avec les boutons ci-dessous", + "rsvp_label": "Votre réponse", + "cancel_notification": "Les participants seront informés de l'annulation", + "you_organizer": "Vous êtes l'organisateur", + "you_attendee": "Vous êtes un participant", + "no_participants": "Aucun participant", + "count": "{count, plural, one {# participant} other {# participants}}" + }, + "recurrence": { + "title": "Récurrence", + "none": "Ne se répète pas", + "daily": "Quotidien", + "weekly": "Hebdomadaire", + "monthly": "Mensuel", + "yearly": "Annuel", + "every_n_days": "Tous les {count} jours", + "every_n_weeks": "Toutes les {count} semaines", + "every_n_months": "Tous les {count} mois", + "until": "Jusqu'au", + "occurrences": "{count} occurrences", + "custom": "Personnalisé…", + "edit_custom": "Modifier la récurrence personnalisée", + "every_n_years": "Tous les {count} ans", + "on_days": "le {days}", + "on_day_n": "le jour {day}", + "on_the_nth": "le {nth} {day}", + "in_month": "en {month}", + "nth_1": "premier", + "nth_2": "deuxième", + "nth_3": "troisième", + "nth_4": "quatrième", + "nth_last": "dernier", + "editor_freq_day": "Jour", + "editor_freq_week": "Semaine", + "editor_freq_month": "Mois", + "editor_freq_year": "Année", + "editor_repeats_on": "Se répète", + "editor_every": "Tous les", + "editor_unit_days": "jour(s)", + "editor_unit_weeks": "semaine(s)", + "editor_unit_months": "mois", + "editor_unit_years": "an(s)", + "editor_on_day": "le jour", + "editor_on_the": "le", + "editor_in": "en", + "editor_ends": "Se termine", + "editor_never": "Jamais", + "editor_ends_on": "Le", + "editor_ends_after": "Après", + "editor_occurrences": "occurrences" + }, + "recurrence_scope": { + "edit_title": "Modifier l'événement récurrent", + "delete_title": "Supprimer l'événement récurrent", + "description": "Ceci est un événement récurrent. Quels événements souhaitez-vous modifier ?", + "this_event": "Cet événement uniquement", + "this_and_future": "Cet événement et les suivants", + "all_events": "Tous les événements", + "cancel": "Annuler", + "save": "Enregistrer", + "delete": "Supprimer", + "rsvp_title": "Répondre à l'événement récurrent", + "rsvp_description": "Ceci est un événement récurrent. À quels événements votre réponse s'applique-t-elle ?", + "respond": "Répondre" + }, + "alerts": { + "title": "Rappel", + "none": "Aucun rappel", + "at_time": "Au moment de l'événement", + "minutes_before": "{count, plural, one {# minute avant} other {# minutes avant}}", + "hours_before": "{count, plural, one {# heure avant} other {# heures avant}}", + "days_before": "{count, plural, one {# jour avant} other {# jours avant}}", + "weeks_before": "{count, plural, one {# semaine avant} other {# semaines avant}}", + "unit_minutes_before": "minutes avant", + "unit_hours_before": "heures avant", + "unit_days_before": "jours avant", + "unit_weeks_before": "semaines avant", + "add": "Ajouter un rappel", + "remove": "Supprimer le rappel", + "amount": "Quantité de rappel", + "unit": "Unité de rappel", + "action": "Type de rappel", + "action_display": "Notification", + "action_email": "E-mail", + "by_email": "{label} par e-mail", + "kept": "{count, plural, one {# autre rappel non modifiable ici est conservé} other {# autres rappels non modifiables ici sont conservés}}", + "other": "{count, plural, one {# autre rappel} other {# autres rappels}}", + "calendar_defaults": "Rappels par défaut du calendrier", + "customize": "Personnaliser", + "use_defaults": "Utiliser les rappels par défaut du calendrier" + }, + "settings": { + "limit_hours": "Limiter les heures affichées", + "limit_hours_desc": "N'afficher que ces heures dans les vues jour et semaine. Les événements en dehors restent accessibles via les flèches en haut et en bas de chaque jour.", + "visible_hours": "Heures affichées", + "visible_hours_start": "Début des heures affichées", + "visible_hours_end": "Fin des heures affichées", + "hide_non_working_days": "Masquer les jours non travaillés", + "hide_non_working_days_desc": "Retirer de la vue semaine les jours où vous ne travaillez pas. Leurs événements restent visibles dans les vues mois, jour et agenda.", + "working_days": "Jours travaillés", + "title": "Paramètres du calendrier", + "default_view": "Vue par défaut", + "week_starts_on": "La semaine commence le", + "time_format": "Format de l'heure", + "default_calendar": "Calendrier par défaut", + "default_reminder": "Rappel par défaut", + "time_format_12h": "12 heures", + "time_format_24h": "24 heures", + "notifications_enabled": "Notifications d'événements", + "notifications_enabled_desc": "Afficher les alertes pour les événements à venir", + "notification_sound": "Son de notification", + "notification_sound_desc": "Jouer un son pour les alertes de calendrier", + "invitation_parsing": "Analyser les invitations par e-mail", + "invitation_parsing_desc": "Détecter les invitations de calendrier dans les pièces jointes des e-mails et afficher les actions du calendrier", + "show_time_in_month_view": "Afficher l'heure dans la vue mensuelle", + "show_time_in_month_view_desc": "Afficher les heures des événements dans la vue mensuelle du calendrier", + "show_week_numbers": "Afficher les numéros de semaine", + "show_week_numbers_desc": "Afficher les numéros de semaine dans le mini-calendrier", + "calendar_free_scroll": "Défilement libre", + "calendar_free_scroll_desc": "Faire défiler les mois, semaines et jours en continu au lieu d'une période à la fois", + "enable_tasks": "Activer les tâches", + "enable_tasks_desc": "Afficher une vue des tâches dans le calendrier pour gérer les choses à faire", + "show_tasks_on_calendar": "Afficher les tâches dans le calendrier", + "show_tasks_on_calendar_desc": "Afficher les tâches dans les vues mois, semaine et jour du calendrier", + "hover_preview": "Aperçu au survol", + "hover_preview_desc": "Afficher un popover de détails au survol des événements du calendrier", + "hover_preview_instant": "Instantané", + "hover_preview_delay_500ms": "Délai de 0,5 seconde", + "hover_preview_delay_1s": "Délai de 1 seconde", + "hover_preview_delay_2s": "Délai de 2 secondes", + "hover_preview_off": "Désactivé", + "organizer_identity": "Organiser les invitations en tant que", + "organizer_identity_desc": "L'adresse d'agenda utilisée comme organisateur lorsque vous invitez des personnes à des événements.", + "organizer_identity_failed": "Impossible de modifier l'adresse de l'organisateur", + "show_birthday_calendar": "Calendrier des anniversaires", + "show_birthday_calendar_desc": "Afficher un calendrier virtuel avec les anniversaires de vos contacts" + }, + "days": { + "monday": "Lundi", + "tuesday": "Mardi", + "wednesday": "Mercredi", + "thursday": "Jeudi", + "friday": "Vendredi", + "saturday": "Samedi", + "sunday": "Dimanche", + "mon": "Lun", + "tue": "Mar", + "wed": "Mer", + "thu": "Jeu", + "fri": "Ven", + "sat": "Sam", + "sun": "Dim" + }, + "months": { + "jan": "janv.", + "feb": "févr.", + "mar": "mars", + "apr": "avr.", + "may": "mai", + "jun": "juin", + "jul": "juil.", + "aug": "août", + "sep": "sept.", + "oct": "oct.", + "nov": "nov.", + "dec": "déc.", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Événement créé", + "event_updated": "Événement mis à jour", + "event_deleted": "Événement supprimé", + "calendar_created": "Calendrier créé", + "calendar_deleted": "Calendrier supprimé", + "event_move_error": "Échec du déplacement de l'événement", + "event_resize_error": "Échec du redimensionnement de l'événement", + "alert_title": "Événement à venir", + "alert_now": "Commence maintenant", + "alert_in_minutes": "Dans {count} min", + "invitation_sent": "Invitations envoyées", + "invitations_denied_title": "Impossible d'envoyer les invitations", + "invitations_denied": "Le serveur a refusé d'envoyer les invitations : {reason}. Enregistrer l'événement sans les envoyer ?", + "save_without_invitations": "Enregistrer sans invitations", + "rsvp_updated": "Réponse mise à jour", + "rsvp_error": "Échec de la mise à jour de la réponse", + "event_duplicated": "Événement dupliqué", + "event_error": "Échec de l'enregistrement de l'événement", + "task_due": "Tâche à échéance", + "event_exported": "Événement exporté", + "title_copied": "Titre copié", + "link_copied": "Lien copié" + }, + "status": { + "loading_calendars": "Chargement des calendriers...", + "loading_events": "Chargement des événements..." + }, + "quick_create": { + "placeholder": "Titre du nouvel événement", + "aria_label": "Création rapide d'événement", + "add_title": "Ajouter un titre", + "more_options": "Plus d'options" + }, + "nav_prev": "Précédent", + "nav_next": "Suivant", + "import": { + "title": "Importer un calendrier", + "tab_file": "Fichier", + "tab_url": "URL", + "select_file": "Sélectionner un fichier .ics", + "drop_file": "ou déposez le fichier ici", + "supported_formats": "Fichiers iCalendar (.ics) supportés", + "url_description": "Entrez l'URL d'un flux iCalendar (.ics) externe pour importer des événements.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Prend en charge les URLs iCalendar (.ics)", + "fetch": "Récupérer", + "invalid_url": "Veuillez entrer une URL valide", + "url_fetch_failed": "Impossible de récupérer le calendrier depuis l'URL", + "parsing": "Analyse du fichier en cours...", + "parsed_events": "{count} événements trouvés", + "no_events": "Aucun événement trouvé dans le fichier", + "select_all": "Tout sélectionner", + "deselect_all": "Tout désélectionner", + "target_calendar": "Importer dans le calendrier", + "import_button": "Importer la sélection", + "importing": "Importation en cours...", + "success": "{count} événements importés avec succès", + "error": "Échec de l'importation du calendrier", + "file_too_large": "Le fichier dépasse la limite de 10 Mo", + "invalid_format": "Format de fichier calendrier invalide" + }, + "webcal_action": { + "title": "Ouvrir le lien de calendrier", + "description": "Comment souhaitez-vous utiliser \"{name}\" ?", + "import_title": "Importer une fois", + "import_description": "Récupérer les événements maintenant et les copier dans l'un de vos calendriers.", + "subscribe_title": "S'abonner", + "subscribe_description": "Synchroniser automatiquement ce calendrier comme calendrier séparé.", + "cancel": "Annuler" + }, + "management": { + "title": "Gestion des calendriers", + "description": "Créez, renommez et personnalisez vos calendriers. Clic droit sur un calendrier dans la barre latérale pour changer rapidement sa couleur.", + "name": "Nom", + "name_placeholder": "Nom du calendrier", + "color": "Couleur", + "change_color": "Changer la couleur", + "random_color": "Nouvelle couleur aléatoire", + "add_calendar": "Ajouter un calendrier", + "edit": "Modifier", + "delete": "Supprimer", + "save": "Enregistrer", + "create": "Créer", + "cancel": "Annuler", + "default": "Par défaut", + "confirm_delete": "Supprimer \"{name}\" ? Tous les événements de ce calendrier seront supprimés.", + "calendar_created": "Calendrier créé", + "calendar_updated": "Calendrier mis à jour", + "calendar_deleted": "Calendrier supprimé", + "color_updated": "Couleur du calendrier mise à jour", + "error_create": "Échec de la création du calendrier", + "error_update": "Échec de la mise à jour du calendrier", + "error_delete": "Échec de la suppression du calendrier", + "caldav_url": "URL CalDAV", + "copy_url": "Copier l'URL CalDAV", + "url_copied": "URL CalDAV copiée dans le presse-papiers", + "confirm_clear": "Supprimer tous les événements de \"{name}\" ? Cette action est irréversible.", + "set_default": "Définir par défaut", + "default_updated": "Calendrier par défaut mis à jour", + "error_default": "Échec de la définition du calendrier par défaut", + "clear_events": "Supprimer les événements", + "events_cleared": "{count} événements supprimés", + "error_clear": "Impossible de supprimer les événements du calendrier", + "share": "Partager le calendrier", + "new_event_in_calendar": "Nouvel événement dans ce calendrier", + "shared_with": "Partagé avec {names}", + "shared_with_count": "{count, plural, one {Partagé avec 1 personne} other {Partagé avec # personnes}}", + "move_up": "Déplacer vers le haut", + "move_down": "Déplacer vers le bas", + "kind": "Contenu", + "kind_events": "Événements", + "kind_tasks": "Tâches", + "kind_both": "Événements et tâches", + "kind_hint": "Détermine quelles applications voient ce calendrier lorsqu'il est synchronisé via CalDAV. Ne peut pas être modifié par la suite.", + "default_alerts": "Rappels par défaut", + "default_alerts_title": "Rappels par défaut pour « {name} »", + "default_alerts_hint": "Les nouveaux événements de ce calendrier reçoivent ces rappels, sauf s'ils sont modifiés dans l'événement. Les modifier ici ne change pas les événements existants. Les rappels par e-mail sont envoyés par le serveur de messagerie.", + "default_alerts_timed": "Événements avec une heure", + "default_alerts_all_day": "Événements sur toute la journée", + "default_alerts_updated": "Rappels par défaut mis à jour" + }, + "subscription": { + "title": "Abonnement iCal", + "section_title": "Abonnements iCal", + "description": "Abonnez-vous à un flux iCalendar externe. Les événements seront synchronisés automatiquement dans leur propre calendrier. Prend en charge les URL https:// et webcal://.", + "url_label": "URL du calendrier", + "url_placeholder": "https://example.com/calendar.ics ou webcal://...", + "name_label": "Nom du calendrier", + "name_placeholder": "ex. Jours fériés", + "color_label": "Couleur", + "refresh_interval": "Intervalle de rafraîchissement", + "interval_15": "Toutes les 15 minutes", + "interval_30": "Toutes les 30 minutes", + "interval_60": "Toutes les heures", + "interval_360": "Toutes les 6 heures", + "interval_1440": "Tous les jours", + "subscribe": "S'abonner", + "subscribing": "Abonnement en cours...", + "save": "Enregistrer les modifications", + "saving": "Enregistrement...", + "edit": "Modifier", + "edit_title": "Modifier l'abonnement", + "updated": "\"{name}\" mis à jour", + "update_error": "Échec de la mise à jour de l'abonnement", + "invalid_url": "Veuillez entrer une URL valide", + "success": "Abonné à \"{name}\"", + "error": "Impossible d'ajouter l'abonnement", + "refresh": "Rafraîchir maintenant", + "refresh_success": "Abonnement rafraîchi", + "refresh_error": "Impossible de rafraîchir l'abonnement", + "unsubscribe": "Se désabonner", + "confirm_delete": "Se désabonner de \"{name}\" ? Le calendrier et tous ses événements seront supprimés.", + "deleted": "Abonnement supprimé", + "delete_error": "Impossible de supprimer l'abonnement", + "last_refreshed": "Dernière mise à jour : {time}" + }, + "tasks": { + "label": "Tâches", + "no_tasks": "Aucune tâche", + "no_title": "(Sans titre)", + "mark_complete": "Marquer comme terminée", + "mark_incomplete": "Marquer comme non terminée", + "filter_all": "Toutes", + "filter_pending": "En attente", + "filter_completed": "Terminées", + "filter_overdue": "En retard", + "show_completed": "Afficher les tâches terminées", + "create": "Nouvelle tâche", + "edit": "Modifier la tâche", + "title_placeholder": "Titre de la tâche", + "description_placeholder": "Ajouter une description...", + "due_date": "Date d'échéance", + "include_time": "Inclure l'heure", + "priority": "Priorité", + "priority_none": "Aucune", + "priority_high": "Haute", + "priority_medium": "Moyenne", + "priority_low": "Basse", + "progress": "Statut", + "progress_needs_action": "Action requise", + "progress_in_process": "En cours", + "progress_completed": "Terminée", + "progress_cancelled": "Annulée", + "calendar": "Calendrier", + "alert": "Rappel", + "alert_none": "Aucun", + "alert_at_time": "À la date et à l'heure d'échéance", + "alert_5min": "5 minutes avant", + "alert_15min": "15 minutes avant", + "alert_30min": "30 minutes avant", + "alert_1hr": "1 heure avant", + "alert_1day": "1 jour avant", + "delete": "Supprimer", + "cancel": "Annuler", + "save": "Enregistrer", + "quick_add_placeholder": "Ajouter une tâche...", + "due_today": "Échéance aujourd'hui", + "due_tomorrow": "Échéance demain", + "overdue": "En retard" + }, + "nav_open_menu": "Ouvrir le menu", + "toolbar": { + "more": "Plus d'actions", + "select_view": "Changer de vue, actuellement {view}" + }, + "other_calendars": "Autres agendas", + "calendar_options": "Options de {name}" + }, + "advanced_search": { + "title": "Recherche avancée", + "from": "De", + "from_placeholder": "Email ou nom de l'expéditeur", + "to": "À", + "to_placeholder": "Email ou nom du destinataire", + "subject": "Objet", + "subject_placeholder": "L'objet contient...", + "body": "Corps", + "body_placeholder": "Le corps contient...", + "folder": "Dossier", + "all_folders": "Tous les dossiers", + "all_folders_except_spam_trash": "Tous les dossiers sauf Spam et Corbeille", + "has_attachment": "Pièces jointes", + "date_after": "Après le", + "date_before": "Avant le", + "size_min": "Plus grand que (Ko)", + "size_max": "Plus petit que (Ko)", + "starred": "Suivis", + "unread": "Non lus", + "read": "Lus", + "yes": "Oui", + "no": "Non", + "clear": "Effacer", + "clear_all": "Tout effacer", + "filters_active": "{count} filtre", + "filters_active_plural": "{count} filtres", + "toggle_filters": "Filtres", + "search_hint": "Utilisez les filtres avancés pour une recherche précise", + "advanced_filters_tooltip": "Filtres de recherche avancés", + "results_found": "{count, plural, =0 {Aucun résultat trouvé} one {# résultat trouvé} other {# résultats trouvés}}", + "results_found_more": "{count}+ résultats trouvés", + "suggestions_recent": "Recherches récentes", + "suggestions_people": "Personnes", + "suggestions_from": "Rechercher les e-mails de {address}", + "suggestions_to": "Rechercher les e-mails à {address}", + "suggestions_remove_recent": "Retirer des recherches récentes", + "has_words": "Contient les mots", + "size": "Taille", + "date_within": "Date", + "search_in": "Rechercher dans", + "has_attachment_check": "Contient une pièce jointe", + "search": "Rechercher" + }, + "welcome": { + "title": "Bienvenue dans votre boite mail", + "tip_compose": "Appuyez sur c pour composer un nouvel email", + "tip_shortcuts": "Appuyez sur ? pour voir tous les raccourcis clavier", + "tip_sidebar": "Retrouvez le Calendrier, les Contacts et les Parametres dans le menu lateral", + "tip_settings": "Personnalisez votre experience dans les Parametres", + "tip_compose_touch": "Touchez le bouton crayon pour écrire un nouvel e-mail", + "tip_sidebar_touch": "Agenda, Contacts et Paramètres se trouvent dans la barre du bas", + "got_it": "Compris", + "settings": "Paramètres", + "dismiss": "Fermer", + "start_tour": "Démarrer la visite" + }, + "demo_welcome": { + "title": "Bienvenue sur Bulwark Mail", + "description": "Explorez un client webmail complet - directement dans votre navigateur. Toutes les données restent sur votre appareil, alors testez tout.", + "feature_email": "Lire et rédiger des e-mails", + "feature_organize": "Tags, étoiles et dossiers", + "feature_shortcuts": "Raccourcis clavier", + "feature_privacy": "Démo 100 % privée", + "hint": "Cliquez sur un e-mail à gauche pour commencer, ou lancez la visite." + }, + "files": { + "office_edit": "Modifier le document", + "office_loading": "Ouverture de l'éditeur de documents…", + "office_error": "L'éditeur de documents n'a pas pu être ouvert.", + "office_close": "Fermer l'éditeur", + "open_in_new_tab": "Ouvrir dans un nouvel onglet", + "title": "Fichiers", + "search_placeholder": "Rechercher des fichiers...", + "empty_state_title": "Aucun fichier pour le moment", + "upload_files": "Téléverser des fichiers", + "new_folder": "Nouveau dossier", + "new_folder_name": "Nom du dossier", + "rename": "Renommer", + "rename_title": "Renommer", + "new_name": "Nouveau nom", + "name_forbidden_chars": "Le serveur n'autorise pas ces caractères dans les noms : {chars}", + "name_reserved": "Ce nom est réservé sur le serveur.", + "delete": "Supprimer", + "delete_confirm_title": "Supprimer la ressource", + "delete_confirm_message": "Êtes-vous sûr de vouloir supprimer \"{name}\" ? Cette action est irréversible.", + "download": "Télécharger", + "name": "Nom", + "size": "Taille", + "modified": "Modifié", + "type": "Type", + "folder": "Dossier", + "file": "Fichier", + "uploading": "Téléversement en cours...", + "upload_success": "{count, plural, one {1 fichier téléversé} other {# fichiers téléversés}}", + "upload_canceled": "Téléversement annulé", + "upload_canceled_partial": "{count, plural, one {1 fichier a été téléversé} other {# fichiers ont été téléversés}} avant l'annulation", + "upload_error": "Échec du téléversement du fichier", + "create_folder_success": "Dossier créé", + "create_folder_error": "Échec de la création du dossier", + "delete_success": "Supprimé avec succès", + "delete_error": "Échec de la suppression", + "rename_success": "Renommé avec succès", + "rename_error": "Échec du renommage", + "download_error": "Échec du téléchargement", + "not_available": "Le stockage de fichiers n'est pas disponible sur ce serveur", + "cancel": "Annuler", + "create": "Créer", + "save": "Enregistrer", + "no_results": "Aucun fichier ne correspond à votre recherche", + "batch_delete_confirm_message": "Êtes-vous sûr de vouloir supprimer {count, plural, one {1 élément} other {# éléments}} ? Cette action est irréversible.", + "batch_delete_success": "{count, plural, one {1 élément supprimé} other {# éléments supprimés}}", + "grid_view": "Vue en grille", + "list_view": "Vue en liste", + "details": "Détails", + "path": "Chemin", + "preview": "Aperçu", + "preview_error": "Échec du chargement de l'aperçu", + "cut": "Couper", + "copy": "Copier", + "paste": "Coller", + "move_success": "{count, plural, one {1 élément déplacé} other {# éléments déplacés}}", + "move_error": "Échec du déplacement", + "paste_success": "Collé avec succès", + "paste_error": "Échec du collage", + "new_text_file": "Nouveau fichier texte", + "file_name": "Nom du fichier", + "retry": "Réessayer", + "refresh": "Actualiser", + "duplicate": "Dupliquer", + "duplicate_success": "Dupliqué avec succès", + "duplicate_error": "Échec de la duplication", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Fichier créé", + "create_file_error": "Échec de la création du fichier", + "favorites": "Favoris", + "recent": "Récents", + "show_in_folder": "Afficher dans le dossier", + "remove_from_recent": "Retirer des récents", + "upload_folder": "Téléverser un dossier", + "file_too_large": "\"{name}\" dépasse la taille maximale du fichier ({max})", + "undo": "Annuler", + "undo_success": "Action annulée", + "undo_error": "Échec de l'annulation", + "toolbar": "Actions sur les fichiers", + "file_list": "Fichiers et dossiers", + "context_menu": "Actions", + "settings_ascending": "Croissant", + "settings_descending": "Décroissant", + "disabled_title": "La fonctionnalité Fichiers a été désactivée par votre administrateur", + "disabled_description": "Les téléchargements de fichiers volumineux via WebDAV peuvent provoquer une instabilité de Stalwart/RocksDB, y compris des crashs de mémoire et une utilisation irrécupérable du disque. Les fichiers supprimés peuvent ne pas être immédiatement purgés du stockage. Cette fonctionnalité n'est pas recommandée pour les environnements de production.", + "no_accounts": "Aucun compte connecté.", + "open_folder_tree": "Ouvrir l'arborescence des dossiers", + "migration_title": "Mise à jour de vos fichiers…", + "migration_description": "Organisation des dossiers et fichiers dans leur structure appropriée. Cela ne se produit qu'une seule fois.", + "new": "Nouveau", + "my_files": "Mes fichiers", + "folders": "Dossiers", + "files_heading": "Fichiers", + "open": "Ouvrir", + "star": "Ajouter aux favoris", + "unstar": "Retirer des favoris", + "expand_folder": "Développer {name}", + "collapse_folder": "Réduire {name}", + "sidebar_hint": "Ajoutez des dossiers aux favoris et ouvrez des fichiers pour les retrouver ici.", + "breadcrumbs": "Chemin du dossier", + "show_path": "Afficher les dossiers masqués du chemin", + "selection_actions": "Actions sur les éléments sélectionnés", + "selected_count": "{count, plural, one {1 sélectionné} other {# sélectionnés}}", + "clear_selection": "Effacer la sélection", + "select_all": "Tout sélectionner", + "select_item": "Sélectionner {name}", + "more_actions": "Plus d'actions pour {name}", + "paste_here": "Coller dans ce dossier", + "paste_count": "{count, plural, one {Coller 1 élément} other {Coller # éléments}}", + "sort_by": "Trier par", + "view_mode": "Affichage", + "clear_search": "Effacer la recherche", + "no_results_for": "Aucun élément de ce dossier ne correspond à \"{query}\"", + "download_count": "{count, plural, one {Télécharger 1 fichier} other {Télécharger # fichiers}}", + "delete_count": "{count, plural, one {Supprimer 1 élément} other {Supprimer # éléments}}", + "close_details": "Fermer les détails", + "file_kind": "Fichier {ext}", + "folder_summary": "{folders, plural, =0 {Aucun dossier} one {1 dossier} other {# dossiers}}, {files, plural, =0 {aucun fichier} one {1 fichier} other {# fichiers}}", + "details_hint": "Sélectionnez un fichier ou un dossier pour afficher ses détails.", + "sharing": "Partage", + "shared_with_count": "{count, plural, one {Partagé avec 1 personne} other {Partagé avec # personnes}}", + "only_you": "Vous seul", + "empty_folder_title": "Ce dossier est vide", + "empty_folder_description": "Déposez des fichiers ici ou téléversez-les depuis votre appareil.", + "empty_folder_read_only": "Vous ne pouvez pas ajouter de fichiers à ce dossier.", + "choose_account": "Choisir un compte", + "drop_to_upload": "Déposer pour téléverser dans {folder}", + "uploading_count": "Téléversement {current} sur {total}", + "dismiss": "Fermer", + "zoom_in": "Zoom avant", + "zoom_out": "Zoom arrière", + "zoom_reset": "Ajuster à l'écran", + "rotate": "Pivoter", + "close_preview": "Fermer" + }, + "smime": { + "your_certificates": "Vos certificats", + "your_certificates_desc": "Importez et gérez vos certificats S/MIME pour signer et chiffrer les e-mails", + "recipient_certificates": "Certificats des destinataires", + "recipient_certificates_desc": "Certificats publics pour chiffrer les e-mails destinés aux destinataires", + "identity_bindings": "Liaisons identité-clé", + "identity_bindings_desc": "Associez des certificats S/MIME à vos identités de messagerie", + "defaults_title": "Valeurs par défaut", + "defaults_desc": "Configurez le comportement par défaut de signature et de chiffrement", + "import_pkcs12": "Importer PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importer un certificat", + "no_certificates": "Aucun certificat importé pour le moment", + "no_recipient_certs": "Aucun certificat destinataire", + "expires": "Expire le", + "expired": "Expiré", + "bound_to": "Lié à", + "no_key_bound": "Aucun", + "lock": "Verrouiller la clé", + "unlock": "Déverrouiller la clé", + "details": "Voir les détails", + "delete": "Supprimer", + "encrypt_by_default": "Chiffrer par défaut", + "encrypt_by_default_desc": "Chiffrer automatiquement les e-mails lorsque tous les destinataires ont un certificat", + "remember_unlocked": "Mémoriser les clés déverrouillées", + "remember_unlocked_desc": "Conserver les clés déverrouillées pendant cette session du navigateur", + "sign_default_for": "Signer par défaut pour", + "enter_p12_passphrase": "Entrer la phrase secrète PKCS#12", + "p12_passphrase_desc": "Entrez la phrase secrète qui protège ce fichier de certificat", + "enter_storage_passphrase": "Définir la phrase secrète de stockage", + "storage_passphrase_desc": "Choisissez une phrase secrète pour protéger cette clé dans votre navigateur", + "next": "Suivant", + "import": "Importer", + "unlock_key": "Déverrouiller la clé", + "unlock_key_desc": "Entrez la phrase secrète de stockage pour déverrouiller cette clé pour la signature ou le déchiffrement", + "passphrase_placeholder": "Entrez la phrase secrète", + "confirm_passphrase_placeholder": "Confirmez la phrase secrète", + "passphrase_mismatch": "Les phrases secrètes ne correspondent pas", + "cancel": "Annuler", + "processing": "Traitement…", + "close": "Fermer", + "certificate_details": "Détails du certificat", + "cert_subject": "Sujet", + "cert_issuer": "Émetteur", + "cert_email": "E-mail", + "cert_serial": "Numéro de série", + "cert_validity": "Validité", + "cert_fingerprint": "Empreinte (SHA-256)", + "cert_algorithm": "Algorithme", + "cert_capabilities": "Capacités", + "cert_source": "Source", + "cert_expired": "Ce certificat a expiré", + "cert_not_yet_valid": "Ce certificat n'est pas encore valide", + "cap_sign": "Signature", + "cap_encrypt": "Chiffrement", + "cap_none": "Aucune", + "show_passphrase": "Afficher la phrase secrète", + "hide_passphrase": "Masquer la phrase secrète", + "sign_toggle": "Signer", + "encrypt_toggle": "Chiffrer", + "missing_recipient_certs": "Certificats manquants pour : {emails}", + "missing_sender_cert": "Aucun certificat n'est lié à cette identité", + "status_encrypted_ok": "Ce message a été chiffré", + "status_encrypted_no_key": "Ce message est chiffré, mais aucune clé correspondante n'a été trouvée", + "status_encrypted_failed": "Impossible de déchiffrer ce message", + "status_signed_valid": "Signature vérifiée", + "status_signed_invalid": "Échec de la vérification de la signature", + "status_signed_expired_cert": "Signé avec un certificat expiré", + "status_signed_self_signed": "Signature valide, mais le certificat est auto-signé (non fiable)", + "status_signed_mismatch": "La signature est valide, mais le signataire ne correspond pas à l'expéditeur", + "status_unsupported": "Format S/MIME non pris en charge", + "auto_import_signer_certs": "Importer automatiquement les certificats des signataires", + "auto_import_signer_certs_desc": "Enregistrer automatiquement les certificats des e-mails signés vérifiés pour un futur chiffrement", + "export": "Exporter", + "enter_export_passphrase": "Définir la phrase secrète d'export", + "export_passphrase_desc": "Choisissez une phrase secrète pour protéger le fichier PKCS#12 exporté", + "export_storage_desc": "Entrez la phrase secrète de stockage pour déchiffrer la clé avant l'export", + "incorrect_passphrase": "Phrase secrète incorrecte" + }, + "tour": { + "step_counter": "Étape {current} sur {total}", + "skip": "Passer la visite", + "back": "Retour", + "next": "Suivant", + "finish": "Terminer", + "take_a_tour": "Faire une visite de l'interface", + "restart_title": "Visite d'introduction", + "restart_desc": "Rejouer la visite guidée de l'interface", + "restart_button": "Relancer la visite", + "show_on_new_devices_title": "Afficher sur les nouveaux appareils", + "show_on_new_devices_desc": "Rejouer la bannière d'accueil et la visite lors de votre première connexion sur un nouvel appareil, même si vous les avez déjà terminées ailleurs", + "sidebar_title": "Vos boîtes mail", + "sidebar_desc": "Voici votre barre latérale de dossiers. Cliquez sur une boîte pour voir ses emails. Vous pouvez créer des dossiers, glisser des emails entre eux et voir les compteurs de non lus.", + "compose_title": "Rédiger un email", + "compose_desc": "Cliquez ici pour écrire un nouvel email. Ajoutez des destinataires, des pièces jointes et utilisez la mise en forme enrichie.", + "search_title": "Rechercher vos emails", + "search_desc": "Recherchez par expéditeur, objet ou contenu. Cliquez sur l'icône de filtre pour les options avancées comme la plage de dates, les pièces jointes et les messages suivis.", + "email_list_title": "Votre liste d'emails", + "email_list_desc": "Les emails apparaissent ici. Cliquez sur un email pour le lire à droite. Utilisez la case à cocher pour en sélectionner plusieurs, puis déplacez, supprimez ou étiquetez-les.", + "email_viewer_title": "Panneau de lecture", + "email_viewer_desc": "L'email sélectionné s'ouvre ici. Répondez, transférez, archivez ou supprimez avec les boutons de la barre d'outils. Vous pouvez aussi marquer les emails ou ajouter des étiquettes de couleur.", + "keywords_title": "Étiquettes de couleur", + "keywords_desc": "Organisez vos emails avec des étiquettes colorées. Glissez un email sur une étiquette pour le marquer, ou faites un clic droit pour assigner des étiquettes.", + "calendar_title": "Calendrier", + "calendar_desc": "Accédez au calendrier pour gérer vos événements. Créez des événements, définissez des rappels et consultez les vues jour, semaine ou mois.", + "contacts_title": "Contacts", + "contacts_desc": "Votre carnet d'adresses se trouve ici. Importez des contacts, créez des groupes et cliquez sur un contact pour voir ses détails.", + "settings_title": "Paramètres", + "settings_desc": "Personnalisez tout : thème, densité, signatures, filtres, raccourcis clavier, paramètres du calendrier et plus encore.", + "shortcuts_title": "Raccourcis clavier", + "shortcuts_desc": "Les utilisateurs avancés adorent ça. Appuyez sur ? à tout moment pour voir tous les raccourcis disponibles. Naviguez, rédigez et gérez vos emails sans toucher à la souris.", + "compose_open_title": "L'éditeur", + "compose_open_desc": "Voici l'éditeur d'e-mail. Ajoutez des destinataires, rédigez votre message, joignez des fichiers et utilisez la mise en forme enrichie. Vous pouvez aussi sauvegarder des brouillons et utiliser des modèles.", + "calendar_view_title": "Votre calendrier", + "calendar_view_desc": "Voici votre calendrier avec des événements exemples. Basculez entre les vues jour, semaine, mois et agenda avec la barre d'outils.", + "create_event_title": "Créer un événement", + "create_event_desc": "Cliquez sur ce bouton pour créer un nouvel événement. Vous pouvez définir un titre, une date, une heure et ajouter des participants.", + "event_modal_title": "Détails de l'événement", + "event_modal_desc": "Voici le formulaire de l'événement. Remplissez le titre, choisissez une date et une heure, ajoutez un lieu ou des participants. Cliquez sur enregistrer quand vous avez terminé - ou fermez-le et passez à autre chose.", + "contacts_list_title": "Vos contacts", + "contacts_list_desc": "Voici vos contacts. Cliquez sur un contact pour voir ses détails à droite. Vous pouvez aussi créer de nouveaux contacts, importer des vCards ou organiser les contacts en groupes.", + "files_title": "Stockage de fichiers", + "settings_tabs_title": "Menu des paramètres", + "settings_tabs_desc": "Voici toutes les catégories de paramètres. Personnalisez l'apparence, gérez les identités, configurez les filtres de messagerie, paramétrez votre calendrier et bien plus encore.", + "files_desc": "Votre gestionnaire de fichiers vous permet de téléverser, organiser et partager des fichiers - comme un cloud personnel intégré à votre messagerie.", + "demo_banner_title": "Contrôles de démo", + "demo_banner_desc": "Vous êtes en mode démo - tout reste dans votre navigateur. Cliquez sur 'Réinitialiser la démo' à tout moment pour repartir avec des données fraîches.", + "quota_title": "Utilisation du stockage", + "quota_desc": "Suivez la taille de votre boîte mail ici. Le cercle se remplit au fur et à mesure que vous utilisez plus d'espace." + }, + "unified_mailbox": { + "search_unavailable": "La recherche n'est pas disponible dans la vue unifiée" + }, + "calendar_event_notifications": { + "someone": "Quelqu'un", + "invited": "{name} vous a invité à « {title} »", + "updated": "{name} a mis à jour « {title} »", + "cancelled": "{name} a annulé « {title} »", + "untitled": "Événement sans titre" + }, + "share_notifications": { + "someone": "Quelqu'un", + "shared": "{name} a partagé {kind} « {object} » avec vous", + "changed": "{name} a modifié votre accès à {kind} « {object} »", + "revoked": "{name} a retiré votre accès à {kind} « {object} »", + "object": { + "folder": "le dossier", + "calendar": "l'agenda", + "address_book": "le carnet d'adresses", + "files": "le dossier de fichiers" + } + }, + "sharing": { + "title": "Partager « {name} »", + "description": "Accordez l'accès à d'autres utilisateurs ou groupes de ce serveur. Les modifications sont immédiates.", + "no_shares": "Pas encore partagé.", + "add_person": "Ajouter une personne ou un groupe", + "search_placeholder": "Rechercher par nom ou e-mail…", + "loading_principals": "Chargement des utilisateurs…", + "no_principals": "Aucun autre utilisateur ou groupe trouvé.", + "no_match": "Aucun résultat.", + "remove": "Révoquer l'accès", + "group": "Groupe", + "share_added": "Accès accordé", + "share_updated": "Accès mis à jour", + "share_removed": "Accès révoqué", + "share_failed": "Échec de la mise à jour du partage", + "preset": { + "freeBusy": "Disponibilité uniquement", + "read": "Lecture seule", + "readWrite": "Lecture & écriture", + "manager": "Gestionnaire", + "custom": "Personnalisé" + } + }, + "quote_header": { + "reply_line": "Le {date}, {from} a écrit :", + "forwarded_separator": "---------- Message transféré ----------", + "from_label": "De", + "date_label": "Date", + "subject_label": "Sujet" + }, + "pwa_install": { + "title": "Installer {appName}", + "description": "Installez notre application pour un accès rapide et un support hors ligne.", + "not_now": "Pas maintenant", + "install": "Installer", + "dont_remind": "Ne plus me le rappeler", + "dismiss_aria": "Fermer l'invite d'installation" + }, + "deep_link": { + "message_not_found": "Ce message n'est plus disponible.", + "thread_not_found": "Cette conversation n'est plus disponible.", + "folder_not_found": "Ce dossier n'est plus disponible.", + "event_not_found": "Cet événement n'est plus disponible.", + "file_not_found": "Ce fichier n'est plus disponible.", + "account_unavailable": "Connectez-vous à ce compte pour ouvrir ce lien.", + "copy_message": "Copier le lien du message", + "copy_conversation": "Copier le lien de la conversation", + "copy_event": "Copier le lien de l'événement", + "copied": "Lien copié", + "copy_failed": "Impossible de copier le lien" + }, + "plugins": { + "oauth_callback": { + "completing": "Finalisation de l'autorisation…", + "completing_message": "Veuillez patienter pendant l'établissement de la connexion.", + "received_title": "Autorisation reçue", + "received_message": "Vous pouvez revenir à l'onglet où vous avez commencé la connexion.", + "failed_title": "Échec de l'autorisation", + "denied": "Lautorisation a été refusée.", + "failed_detail": "Échec de lautorisation : {error}", + "missing_params": "Code dautorisation ou état manquant.", + "delivery_failed": "Le résultat de lautorisation na pas pu être transmis à longlet de lapplication. Fermez cette fenêtre et réessayez." + }, + "settings": { + "title": "Plugins", + "description": "Les plugins ajoutés par votre administrateur. Activez-les ou désactivez-les pour votre compte.", + "description_uploads": "Les plugins ajoutent des fonctionnalités au webmail. Activez-les ou désactivez-les pour votre compte, ou installez les vôtres.", + "description_demo": "Les plugins ne sont pas disponibles dans la démo.", + "install_button": "Installer un plugin", + "install_hint": "Choisissez le fichier ZIP d'un plugin.", + "install_hint_approval": "Choisissez le fichier ZIP d'un plugin. Un administrateur doit l'approuver avant que vous puissiez l'activer.", + "install_uploads_off": "Votre administrateur n'autorise pas l'installation de plugins.", + "install_managed_id": "Votre administrateur fournit déjà un plugin portant ce nom, il ne peut donc pas être remplacé.", + "install_invalid": "Ce fichier n'est pas un plugin installable : {details}", + "toast_installed": "Plugin installé. Activez-le ci-dessous.", + "toast_installed_pending": "Plugin installé et envoyé à votre administrateur pour approbation.", + "toast_enabled": "{name} est activé", + "toast_disabled": "{name} est désactivé", + "toast_removed": "{name} a été supprimé", + "toast_forced": "Votre administrateur maintient {name} activé", + "toast_approval_requested": "{name} attend l'approbation de votre administrateur", + "empty_title": "Aucun plugin pour l'instant", + "empty_admin": "Votre administrateur n'a ajouté aucun plugin.", + "empty_uploads": "Installez un plugin à partir d'un fichier ZIP pour commencer.", + "empty_demo": "Les plugins nécessitent un vrai compte, la démo n'en a donc aucun.", + "syncing": "Vérification des plugins auprès du serveur…", + "status_installed": "Désactivé", + "status_enabled": "Démarrage", + "status_running": "Activé", + "status_disabled": "Désactivé", + "status_error": "Problème", + "badge_forced": "Obligatoire", + "badge_managed": "De votre administrateur", + "badge_yours": "Installé par vous", + "badge_awaiting": "En attente d'approbation", + "toggle_label": "Activer ou désactiver {name}", + "forced_note": "Votre administrateur impose ce plugin, il ne peut donc pas être désactivé.", + "permissions": "Ce qu'il peut faire", + "permissions_managed": "Ce qu'il peut faire (approuvé par votre administrateur)", + "settings": "Paramètres", + "remove": "Supprimer", + "remove_title": "Supprimer {name} ?", + "remove_message": "Le plugin et tout ce qu'il a enregistré dans ce navigateur seront supprimés. Vous pourrez le réinstaller plus tard.", + "remove_confirm": "Supprimer", + "error_user_plugins_off": "Votre administrateur a désactivé les plugins installés par les utilisateurs, ce plugin ne s'exécute donc pas.", + "error_approval_unavailable": "Ce plugin a été installé avant que l'approbation soit requise. Supprimez-le et réinstallez-le pour l'envoyer en approbation.", + "error_approval_denied": "Votre administrateur n'a pas approuvé ce plugin.", + "error_approval_pending": "En attente de l'approbation de ce plugin par votre administrateur.", + "error_auto_disabled": "Désactivé parce qu'il échouait sans cesse. Réactivez-le pour réessayer.", + "accounts_heading": "Comptes connectés", + "account_on": "{label} sur {host}", + "account_waiting": "{label} sur {host} (connexion non terminée)", + "disconnect": "Déconnecter", + "disconnect_failed": "Impossible de déconnecter {label}" + }, + "consent": { + "title": "Autoriser {name} à faire ceci ?", + "intro": "Le plugin demande les autorisations ci-dessous. Il ne les obtient que si vous les accordez.", + "high_risk_note": "Les éléments signalés donnent au plugin accès au contenu de vos e-mails ou lui permettent d'envoyer des données ailleurs. Ne les autorisez que pour des plugins de confiance.", + "revoke_hint": "Vous pouvez désactiver ou supprimer le plugin à tout moment dans les Paramètres.", + "deny": "Ne pas autoriser", + "allow": "Autoriser" + }, + "permissions": { + "none": "Il n'a besoin d'aucune autorisation particulière.", + "high_risk": "Prudence", + "email_read": { + "title": "Lire vos e-mails", + "body": "Voir les expéditeurs, les destinataires, les objets et le texte complet de n'importe quel message de votre compte, ainsi que vos dossiers." + }, + "email_write": { + "title": "Modifier et organiser vos e-mails", + "body": "Ajouter ou retirer des étiquettes, des indicateurs et d'autres mots-clés sur vos messages, les déplacer entre vos dossiers et vers les archives, et enregistrer des brouillons." + }, + "email_send": { + "title": "Modifier les e-mails avant leur envoi", + "body": "Lire et modifier les destinataires, l'objet, le texte et les pièces jointes des messages que vous rédigez, et empêcher l'envoi d'un message." + }, + "crypto_full": { + "title": "Accès complet à cette application", + "body": "S'exécute avec les mêmes droits que le webmail lui-même. Il peut lire tous vos e-mails et vos clés enregistrées, et agir en votre nom. N'autorisez ceci que pour des plugins en qui vous avez entièrement confiance." + }, + "email_raw_send": { + "title": "Envoyer des messages qu'il a lui-même créés", + "body": "Envoyer des messages complets, par exemple signés ou chiffrés, depuis votre compte." + }, + "email_blob_read": { + "title": "Lire le contenu brut des messages", + "body": "Lire les données d'origine de vos messages et pièces jointes, ainsi que de chaque fichier que vous joignez avant l'envoi." + }, + "email_blob_write": { + "title": "Remplacer les pièces jointes avant l'envoi", + "body": "Téléverser des fichiers sur votre serveur de messagerie et remplacer un fichier joint par un autre avant l'envoi du message." + }, + "email_render_takeover": { + "title": "Modifier l'affichage des messages", + "body": "Remplacer le texte des messages que vous ouvrez et de la liste des messages, par exemple pour afficher un contenu déchiffré." + }, + "calendar_read": { + "title": "Lire votre calendrier", + "body": "Voir vos calendriers et leurs événements." + }, + "calendar_write": { + "title": "Modifier votre calendrier", + "body": "Créer, modifier et supprimer des événements." + }, + "contacts_read": { + "title": "Lire vos contacts", + "body": "Rechercher et lire vos carnets d'adresses." + }, + "contacts_write": { + "title": "Modifier vos contacts", + "body": "Créer, modifier et supprimer des contacts et des carnets d'adresses." + }, + "files_read": { + "title": "Lire vos fichiers", + "body": "Parcourir et ouvrir les fichiers stockés dans votre compte." + }, + "files_write": { + "title": "Modifier vos fichiers", + "body": "Téléverser, renommer, déplacer et supprimer des fichiers dans votre compte." + }, + "identity_read": { + "title": "Voir vos adresses d'expéditeur", + "body": "Voir les adresses depuis lesquelles vous envoyez et leurs signatures." + }, + "identity_write": { + "title": "Modifier vos adresses d'expéditeur", + "body": "Créer, modifier et supprimer des adresses d'expéditeur et des signatures." + }, + "filters_read": { + "title": "Lire vos filtres de messagerie", + "body": "Voir les règles qui trient vos e-mails entrants." + }, + "filters_write": { + "title": "Modifier vos filtres de messagerie", + "body": "Ajouter des règles à vos filtres côté serveur. Ces règles peuvent déplacer, transférer ou supprimer les e-mails entrants." + }, + "tasks_read": { + "title": "Lire vos tâches", + "body": "Voir vos listes de tâches et vos tâches." + }, + "tasks_write": { + "title": "Modifier vos tâches", + "body": "Créer, modifier, terminer et supprimer des tâches." + }, + "templates_read": { + "title": "Lire vos modèles", + "body": "Voir vos modèles de message enregistrés." + }, + "templates_write": { + "title": "Modifier vos modèles", + "body": "Créer, modifier et supprimer des modèles de message." + }, + "smime_read": { + "title": "Voir votre statut S/MIME", + "body": "Voir si vous avez des clés et certificats S/MIME, sans accès aux clés." + }, + "vacation_read": { + "title": "Voir votre réponse d'absence", + "body": "Voir si votre réponse automatique est activée et ce qu'elle dit." + }, + "vacation_write": { + "title": "Modifier votre réponse d'absence", + "body": "Activer ou désactiver votre réponse automatique et modifier son texte et ses dates." + }, + "settings_read": { + "title": "Lire vos paramètres", + "body": "Voir vos préférences et la définition de vos étiquettes." + }, + "settings_write": { + "title": "Modifier vos paramètres", + "body": "Ajouter et réordonner des étiquettes et modifier vos préférences." + }, + "security_read": { + "title": "Voir la sécurité de votre compte", + "body": "Voir si la connexion à deux facteurs et le chiffrement sont activés, sans accès aux secrets." + }, + "auth_observe": { + "title": "Savoir quand vous vous connectez", + "body": "Être averti lorsque vous vous connectez, changez de compte ou que votre session est renouvelée." + }, + "auth_emit": { + "title": "Vous déconnecter", + "body": "Vous déconnecter du webmail." + }, + "account_read": { + "title": "Voir vos comptes", + "body": "Voir les comptes que vous avez ajoutés, avec leurs adresses et leurs serveurs." + }, + "http_post": { + "title": "Utiliser le serveur du webmail pour vous", + "body": "Appeler ces points de terminaison du serveur avec votre connexion :" + }, + "http_fetch": { + "title": "Se connecter à d'autres sites web", + "body": "Envoyer des requêtes à ces sites. Tout ce que le plugin peut lire peut y être envoyé :" + }, + "admin_config": { + "title": "Lire ses paramètres définis par votre administrateur", + "body": "Lire les paramètres que votre administrateur a enregistrés pour ce plugin." + }, + "ui_observe": { + "title": "Suivre où vous êtes dans l'application", + "body": "Savoir quelle page est ouverte et réagir aux liens sur lesquels vous cliquez." + }, + "ui_toolbar": { + "title": "Ajouter des boutons aux barres d'outils", + "body": "Afficher ses propres boutons dans la barre d'outils des messages, sur un message ouvert et dans la barre supérieure." + }, + "ui_app_top_banner": { + "title": "Afficher une bannière en haut", + "body": "Afficher un avis sur toute la largeur en haut de l'application." + }, + "ui_email_banner": { + "title": "Afficher des avis sur les messages", + "body": "Afficher ses propres avis et résumés au-dessus, à l'intérieur et entre les parties d'un message ou d'une conversation ouverts." + }, + "ui_email_footer": { + "title": "Ajouter du contenu sous les messages", + "body": "Afficher son propre contenu sous un message ouvert." + }, + "ui_contact_cryptokeys": { + "title": "Afficher les clés de chiffrement des contacts", + "body": "Afficher et gérer les clés de chiffrement d'un contact." + }, + "ui_email_details": { + "title": "Ajouter des panneaux aux détails des messages", + "body": "Afficher son propre panneau à côté ou en dessous d'un message ouvert." + }, + "ui_download_file": { + "title": "Enregistrer des fichiers sur votre appareil", + "body": "Proposer au téléchargement des fichiers qu'il a créés." + }, + "ui_message_list_tabs": { + "title": "Ajouter des onglets à votre boîte de réception", + "body": "Répartir votre boîte de réception en onglets comme Mises à jour ou Promotions." + }, + "ui_composer_toolbar": { + "title": "Ajouter à l'éditeur", + "body": "Afficher ses propres boutons, entrées de menu et sections pendant que vous rédigez un message." + }, + "ui_composer_sidebar": { + "title": "Ajouter un panneau à l'éditeur", + "body": "Afficher son propre panneau à côté du message que vous rédigez." + }, + "ui_sidebar_widget": { + "title": "Ajouter à la barre latérale", + "body": "Afficher ses propres encadrés et sections dans la barre latérale de la messagerie." + }, + "ui_settings_section": { + "title": "Ajouter une page de paramètres", + "body": "Afficher sa propre page dans les Paramètres." + }, + "ui_context_menu": { + "title": "Ajouter au menu des messages", + "body": "Ajouter des entrées au menu qui s'ouvre lorsque vous faites un clic droit sur un message." + }, + "ui_navigation_rail": { + "title": "Ajouter à la barre de navigation", + "body": "Afficher son propre bouton dans la barre de navigation." + }, + "ui_keyboard": { + "title": "Ajouter des raccourcis clavier", + "body": "Réagir aux raccourcis clavier qu'il définit." + }, + "ui_calendar_action": { + "title": "Ajouter à l'affichage du calendrier", + "body": "Afficher ses propres boutons et sections sur les événements, ainsi que de courtes étiquettes et des points sur les jours du calendrier." + }, + "ui_admin_page": { + "title": "Ajouter une page d'administration", + "body": "Afficher sa propre page dans les paramètres d'administration." + }, + "app_lifecycle": { + "title": "Savoir quand l'application démarre et s'arrête", + "body": "Être averti lorsque l'application démarre, passe hors ligne ou que vous vous déconnectez." + }, + "compose_open": { + "title": "Ouvrir l'éditeur pour vous", + "body": "Ouvrir un nouveau message avec les destinataires, le texte ou les pièces jointes déjà remplis. C'est toujours vous qui décidez de l'envoyer." + }, + "compose_read": { + "title": "Lire le message que vous écrivez", + "body": "Lire le texte d'un message pendant que vous l'écrivez, quand vous utilisez le plugin dans l'éditeur. Ni la signature ni le message cité." + }, + "compose_write": { + "title": "Modifier le message que vous écrivez", + "body": "Remplacer ou ajouter du texte dans un message pendant que vous l'écrivez, quand vous utilisez le plugin dans l'éditeur. Chaque modification peut être annulée, et c'est toujours vous qui décidez de l'envoyer." + }, + "ui_notifications": { + "title": "Afficher des notifications", + "body": "Afficher des notifications sur votre appareil, ou dans l'application si vous ne les avez pas autorisées. Chacune indique le nom du plugin." + }, + "ui_message_list": { + "title": "Ajouter à la liste des messages", + "body": "Afficher son propre contenu au-dessus de la liste des messages, ainsi que des boutons sur les messages et dans la barre des messages sélectionnés." + }, + "ui_contacts": { + "title": "Ajouter à l'affichage des contacts", + "body": "Afficher ses propres sections dans les détails d'un contact et des boutons dans la liste des contacts." + }, + "ui_tasks": { + "title": "Ajouter à l'affichage des tâches", + "body": "Afficher ses propres sections dans les détails d'une tâche et des boutons dans la liste des tâches." + }, + "ui_files": { + "title": "Ajouter à l'affichage des fichiers", + "body": "Afficher ses propres sections dans l'aperçu des fichiers et des entrées dans le menu d'un fichier." + }, + "ui_commands": { + "title": "Ajouter des commandes", + "body": "Ajouter ses propres entrées à la palette de commandes (Ctrl+K)." + }, + "ui_app_page": { + "title": "Ajouter des pages", + "body": "Ajouter ses propres pages à l'application, chacune avec un bouton dans la barre de navigation." + }, + "http_proxy": { + "title": "Utiliser les comptes que vous connectez", + "body": "Envoyer des requêtes via le serveur de Bulwark avec un compte ou une clé configurés par vous ou votre administrateur. Le plugin ne voit jamais le mot de passe, mais il peut faire tout ce que ce compte peut faire sur son propre serveur." + } + }, + "dialog": { + "from_plugin": "Du plugin {name}", + "ok": "OK", + "submit": "Envoyer", + "confirm": "Confirmer", + "cancel": "Annuler", + "close": "Fermer", + "turn_off": "Désactiver {name}", + "plugin_menu": "Options de {name}", + "password_note": "Ce formulaire provient du plugin {name}, pas du webmail. Ne saisissez un mot de passe que si vous faites confiance à ce plugin.", + "toast_auto_disabled": "{name} a été désactivé parce qu'il échouait sans cesse." + }, + "connect": { + "title": "Connecter {label}", + "intro_password": "{name} souhaite utiliser votre compte {label}. Bulwark garde le mot de passe sur son serveur et ne l'envoie qu'à {host}. Le plugin ne le voit jamais.", + "intro_password_any": "{name} souhaite utiliser votre compte {label}. Bulwark garde le mot de passe sur son serveur et ne l'envoie qu'au serveur que vous saisissez ici. Le plugin ne le voit jamais.", + "intro_signin": "{name} souhaite se connecter à votre compte {label}. Saisissez le serveur ici, puis connectez-vous sur sa propre page. Bulwark garde l'accès obtenu sur son serveur ; le plugin ne le voit jamais.", + "server": "Adresse du serveur", + "server_hint": "cloud.example.com", + "username": "Nom d'utilisateur", + "secret": "Mot de passe ou clé", + "connect": "Connecter", + "continue": "Continuer", + "cancel": "Annuler", + "saving": "Enregistrement…", + "error_server": "Saisissez l'adresse du serveur, par exemple https://cloud.example.com", + "error_save": "Impossible d'enregistrer : {error}" + } + }, + "global_search": { + "title": "Tout rechercher", + "placeholder": "Rechercher courriels, contacts, calendrier, fichiers...", + "scope_label": "Rechercher dans", + "account_label": "Compte", + "scope_all": "Tout", + "scope_mail": "Messagerie", + "scope_contacts": "Contacts", + "scope_calendar": "Calendrier", + "scope_files": "Fichiers", + "all_accounts": "Tous les comptes", + "show_all": "Afficher tous les résultats", + "no_results": "Aucun résultat pour « {query} »", + "server_hint": "Les mots complets interrogent le serveur ; les mots partiels correspondent aux éléments déjà chargés", + "searching": "Recherche...", + "account_error": "Impossible de rechercher dans {account}", + "recent_searches": "Recherches récentes", + "recurring": "Récurrent", + "no_subject": "(no subject)", + "empty_hint": "Recherchez courriels, contacts, événements et fichiers sur tous les comptes. Essayez from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "À plat par date", + "grouped": "Groupé", + "load_more": "Charger plus", + "preview_empty": "Sélectionnez un résultat pour l'aperçu", + "open_full": "Ouvrir", + "view_label": "Affichage" + }, + "top_bar": { + "more": "Plus d'options" + }, + "plugin_pages": { + "loading": "Chargement de la page", + "not_available_title": "Cette page n'est pas disponible", + "not_available_description": "Le plugin qui l'a ajoutée est désactivé ou n'est plus installé.", + "back_to_mail": "Retour à la messagerie", + "settings_group": "Plugins" + }, + "command_palette": { + "title": "Palette de commandes", + "open": "Ouvrir la palette de commandes", + "placeholder": "Tapez une commande", + "no_results": "Aucune commande correspondante", + "go_to": "Aller à {name}", + "compose": "Nouveau message", + "search": "Rechercher dans la messagerie", + "theme_dark": "Passer au thème sombre", + "theme_light": "Passer au thème clair", + "shortcuts": "Afficher les raccourcis clavier", + "group_navigation": "Aller à", + "group_actions": "Actions", + "group_plugins": "Plugins" + }, + "notification_center": { + "title": "Notifications", + "mark_all_read": "Tout marquer comme lu", + "clear": "Effacer", + "empty": "Aucune notification", + "close": "Fermer", + "unread": "Non lu", + "unread_count": "Non lues : {count}", + "level_error": "Erreur", + "level_warning": "Avertissement" + } +} diff --git a/locales/he/common.json b/locales/he/common.json index ffe01d85..96a7e3a2 100644 --- a/locales/he/common.json +++ b/locales/he/common.json @@ -1,3646 +1,4507 @@ -{ - "login": { - "title": "Webmail", - "username_label": "דוא״ל", - "username_placeholder": "user@example.com", - "password_label": "סיסמה", - "password_placeholder": "הזן את הסיסמה שלך", - "jmap_endpoint_label": "שרת JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "השרת חייב לאפשר בקשות CORS מהדומיין הזה.", - "sign_in": "היכנס", - "signing_in": "נכנס...", - "loading": "טוען...", - "reconnecting": "החיבור אבד. מנסה להתחבר מחדש...", - "error": { - "invalid_credentials": "אימייל או סיסמה לא חוקיים. אנא בדוק את האישורים שלך ונסה שוב.", - "connection_failed": "לא ניתן להגיע לשרת. בדוק את חיבור האינטרנט שלך ונסה שוב.", - "cors_blocked": "השרת נגיש אך חוסם בקשות חוצות מוצא. בדוק את הגדרות CORS של שרת ה-JMAP שלך ואפשר את הדומיין הזה.", - "server_error": "השרת אינו זמין באופן זמני. אנא נסה שוב מאוחר יותר.", - "generic": "אירעה שגיאה בלתי צפויה. אם זה נמשך, פנה למנהל המערכת שלך.", - "totp_required": "נדרש קוד אימות דו-גורמי. אנא הזן את הקוד שלך למטה.", - "totp_invalid": "קוד אימות לא חוקי. בדוק את אפליקציית המאמת שלך ונסה שוב.", - "oauth_discovery_failed": "SSO מופעל אך לא ניתן היה להשיג את ספק הזהות. בדוק את תצורת ה-OAuth שלך." - }, - "show_password": "הצג סיסמה", - "hide_password": "הסתר את הסיסמה", - "totp_toggle": "יש לי קוד 2FA", - "remember_me": "תזכור אותי", - "config_error": { - "title": "שגיאת תצורה", - "fetch_failed": "לא ניתן לטעון את תצורת האפליקציה. אנא נסה שוב מאוחר יותר.", - "server_not_configured": "שרת הדואר לא הוגדר. אנא פנה למנהל המערכת שלך." - }, - "remove_from_history": "הסר מההיסטוריה", - "totp_label": "קוד אימות", - "totp_placeholder": "000000", - "session_expired": "פג תוקף ההפעלה שלך. נא להיכנס שוב.", - "dismiss": "סגור", - "or": "אוֹ", - "sign_in_sso": "היכנס באמצעות SSO", - "add_account_title": "הוסף חשבון", - "add_account_subtitle": "היכנס עם חשבון אחר", - "cancel": "ביטול", - "website": "אתר", - "imprint": "פרטי מוציא לאור", - "privacy_policy": "מדיניות פרטיות", - "try_demo": "נסה הדגמה", - "demo_description": "חקור עם נתונים לדוגמה - אין צורך בחשבון", - "demo_launching": "משיק הדגמה...", - "demo_login_button": "הפעל הדגמה", - "demo_tagline": "התנסה בלקוח דוא\"ל מלא. אין צורך בחשבון.", - "demo_no_signup": "אין צורך בהרשמה - חקור בחופשיות עם נתונים לדוגמה", - "oauth_completing": "משלים כניסה...", - "oauth_error": { - "title": "האימות נכשל", - "invalid_state": "אימות האבטחה נכשל. אנא נסה להיכנס שוב.", - "missing_params": "חסרים נתוני הרשאה. אנא נסה להיכנס שוב.", - "token_exchange_failed": "השלמת האימות נכשלה. אנא נסה שוב.", - "access_denied": "הגישה נדחתה. אנא פנה למנהל המערכת שלך.", - "back_to_login": "חזרה לכניסה" - }, - "jmap_server_label": "שרת", - "jmap_server_auto_picked": "שרת שנבחר מדומיין הדוא״ל שלך." - }, - "sidebar": { - "unified_mailbox": "תיבת דואר מאוחדת", - "close": "סגור", - "compose": "כתיבה", - "compose_hint": "כתיבה (c)", - "search_placeholder": "חפש בדואר...", - "search_placeholder_hint": "חפש דואר... (הקש /)", - "storage": "אחסון", - "storage_used": "בשימוש", - "storage_free": "פנוי", - "storage_total": "סה״כ", - "sign_out": "צא", - "sign_out_of": "צא מ-{account}", - "sign_out_all": "צא מכל החשבונות", - "add_account": "הוסף חשבון", - "set_as_default": "הגדר כברירת מחדל", - "switch_account": "החלף חשבון", - "contacts": "אנשי קשר", - "calendar": "לוּחַ שָׁנָה", - "settings": "הגדרות", - "admin": "מנהל מערכת", - "files": "קבצים", - "loading_mailboxes": "טוען תיבות דואר...", - "push_connected": "עדכונים בזמן אמת פעילים", - "push_disconnected": "עדכונים בזמן אמת אינם פעילים", - "keyboard_shortcuts": "קיצורי מקשים", - "theme": { - "light": "מצב בהיר", - "dark": "מצב כהה", - "system": "ערכת נושא של המערכת" - }, - "language": { - "title": "שפה" - }, - "mailboxes": { - "inbox": "דואר נכנס", - "sent": "נשלח", - "drafts": "טיוטות", - "trash": "אשפה", - "archive": "ארכיון", - "starred": "מסומן בכוכב", - "all_mail": "כל הדואר", - "spam": "ספאם", - "important": "חשוב" - }, - "expand": "לְהַרְחִיב", - "collapse": "הִתמוֹטְטוּת", - "expand_tooltip": "לְהַרְחִיב", - "collapse_tooltip": "הִתמוֹטְטוּת", - "mobile": { - "search": "לְחַפֵּשׂ", - "compose": "לְהַלחִין", - "go_back": "לַחֲזוֹר", - "toggle_menu": "החלף תפריט" - }, - "clear_search": "נקה חיפוש", - "vacation_active": "משיב חופשה פעיל", - "demo_banner": "מצב הדגמה", - "demo_reset": "אִתחוּל", - "demo_tour": "סִיוּר", - "tags": "תגים", - "show_all_tags": "הצג הכל ({count})", - "show_fewer_tags": "הצג פחות", - "folders": "תיקיות", - "mail": "דוֹאַר", - "nav_label": "ניווט", - "add_app": "אפליקציות", - "unified_inbox": "תיבת דואר מאוחדת", - "unified_sent": "הכל נשלח", - "unified_drafts": "הכל טיוטות", - "unified_trash": "הכל אשפה", - "unified_archive": "הכל ארכיון", - "unified_junk": "הכל דואר זבל", - "all_accounts": "כל החשבונות", - "shared": "משותף", - "scheduled": "מתוכננים", - "unified_all_unread": "הכל שלא נקראו", - "unified_all_starred": "הכל מסומנים", - "unified_all_mail": "הכל דוא״ל", - "remove_account": "הסרת חשבון", - "remove_account_confirm": "להסיר את {account} מהמכשיר הזה? ניתן להוסיף אותו שוב מאוחר יותר." - }, - "sidebar_apps": { - "modal_title": "אפליקציות בסרגל הצד", - "add_new": "הוסף אפליקציה", - "edit_app": "ערוך אפליקציה", - "name_label": "שֵׁם", - "name_placeholder": "האפליקציה שלי", - "name_required": "נדרש שם", - "url_label": "כתובת אתר", - "url_required": "נדרשת כתובת URL", - "url_invalid": "הזן כתובת URL חוקית של http או https", - "icon_label": "סמל", - "icon_required": "סמל נדרש", - "open_mode_label": "מצב פתוח", - "open_new_tab": "כרטיסייה חדשה", - "open_inline": "מוטבע", - "cancel": "לְבַטֵל", - "add": "לְהוֹסִיף", - "update": "לְעַדְכֵּן", - "delete": "לִמְחוֹק", - "delete_confirm_title": "מחק אפליקציה", - "delete_confirm": "האם אתה בטוח שברצונך למחוק את \"{name}\"?", - "no_apps": "עדיין לא נוספו אפליקציות", - "no_apps_hint": "הוסף אפליקציות וקישורים מותאמים אישית לסרגל הצד שלך", - "managed_title": "סופקו על ידי מנהל המערכת", - "managed_description": "אפליקציות אלו מוגדרות עבור כולם ולא ניתן לשנות אותן.", - "managed_badge": "מנוהלת", - "search_icons": "חיפוש אייקונים...", - "show_popular": "פּוֹפּוּלָרִי", - "show_all": "כֹּל", - "no_icons_found": "לא נמצאו אייקונים", - "inline_badge": "מוטבע", - "tab_badge": "כרטיסייה", - "show_on_mobile": "הצג בנייד" - }, - "email_list": { - "no_emails": "לא נמצאו הודעות", - "no_emails_description": "תיבת הדואר הזו ריקה", - "no_search_results": "לא נמצאו תוצאות", - "no_search_results_description": "נסה להתאים את החיפוש או המסננים שלך", - "loading": "טוען אימיילים...", - "unread": "לא נקרא", - "to_me": "לִי", - "to_recipients": "לנמעני{count}", - "and_others": "ו-{count}אחרים", - "draft": "טְיוּטָה", - "starred": "מסומן בכוכב", - "pinned": "מוצמד", - "replied": "נענה", - "forwarded": "הועבר", - "has_attachment": "כולל קובץ מצורף", - "conversations_count": "{count}מתוך שיחות{total}", - "conversations_count_plus": "{count}+ שיחות", - "conversations_count_simple": "שיחות{count}", - "no_conversations": "אין שיחות", - "loading_more": "טוען אימיילים נוספים...", - "no_more_emails": "אין יותר מיילים לטעינה", - "batch_actions": { - "select": "בחר אימיילים", - "select_all": "בחר הכל", - "mark_read": "סמן כנקרא", - "mark_unread": "סמן כלא נקרא", - "delete": "לִמְחוֹק", - "delete_confirm_title": "מחק מיילים", - "delete_confirm_message": "האם למחוק {count, plural, one {הודעת דוא״ל אחת} other {# הודעות דוא״ל}}?", - "clear_selection": "נקה בחירה", - "selected_messages": "{count, plural, one {דוא״ל 1} other {# דוא״ל}} שנבחרו" - }, - "permanent_delete": "מחק לצמיתות", - "permanent_delete_confirm_title": "מחק לצמיתות", - "permanent_delete_confirm_message": "אימייל זה יימחק לצמיתות. לא ניתן לבטל פעולה זו.", - "permanent_delete_confirm_batch_message": "{count, plural, one {הודעת דוא״ל אחת תימחק לצמיתות} other {# הודעות דוא״ל יימחקו לצמיתות}}. לא ניתן לבטל פעולה זו.", - "empty_folder": { - "button": "רוקן תיקייה", - "confirm_title": "רוקן תיקייה", - "confirm_message": "כל הודעות האימייל בתיקייה זו יימחקו לצמיתות. לא ניתן לבטל פעולה זו.", - "confirm_button": "רוקן תיקייה", - "junk_hint": "אתה יכול לרוקן את תיקיית הזבל כדי להסיר לצמיתות את כל ההודעות.", - "trash_hint": "אתה יכול לרוקן את תיקיית האשפה כדי להסיר לצמיתות את כל ההודעות." - }, - "no_scheduled_emails": "אין דוא״ל מתוכננים", - "no_scheduled_emails_description": "הודעות המתוכננות למועד מאוחר יוצגו כאן.", - "scheduled_count": "{count, plural, one {דוא״ל 1 מתוכנן} other {# דוא״ל מתוכננים}}", - "scheduled_actions_hint": "הודעות מתוכננות יכולות להיות מבוטלות, מעודכנות או ערוכות מפעולות המתוכננות שלהן.", - "cancel_scheduled_send": "ביטול שליחה", - "reschedule_send": "תזמן מחדש", - "cancel_and_edit": "בטל וערוך", - "cancel_and_compose_again": "בטל והרכיב שוב", - "reschedule_prompt": "הזן תאריך/שעה חדש, למשל 2026-05-04T15:30" - }, - "email_viewer": { - "no_email_selected": "לא נבחר אימייל", - "no_email_description": "בחר אימייל מהרשימה כדי להציג אותו כאן", - "no_conversation_selected": "לא נבחרה שיחה", - "no_conversation_description": "בחר שיחה מהרשימה כדי לקרוא אותה כאן", - "compose": "כתיבה", - "compose_hint": "כתוב הודעה חדשה", - "no_subject": "(ללא נושא)", - "loading_email": "טוען אימייל...", - "loading": "טְעִינָה...", - "reply": "השב", - "reply_all": "השב לכולם", - "forward": "העבר", - "delete": "מחיקה", - "archive": "ארכיון", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "כוכב", - "unstar": "הסר כוכב", - "mark_unread": "סמן כלא נקרא", - "mark_read": "סמן כנקרא", - "print": "הדפס", - "view_source": "צפה במקור", - "fullscreen": "מסך מלא", - "exit_fullscreen": "יציאה ממסך מלא", - "export_email": "ייצא כ-.eml", - "forward_as_attachment": "העבר כקובץ מצורף", - "import_email": "ייבוא .eml", - "keyboard_shortcuts": "קיצורי מקשים (?)", - "email_source": "מקור דוא\"ל", - "draft_banner": "הודעה זו היא טיוטה", - "edit_draft": "ערוך", - "copy_source": "העתק ללוח", - "source_copied": "המקור הועתק ללוח", - "attachments": "קבצים מצורפים", - "important": "חָשׁוּב", - "download": "הורד", - "download_all": "הורד הכל", - "from": "מאת", - "to": "אל", - "cc": "עותק", - "bcc": "עותק מוסתר", - "date": "תאריך", - "subject": "נושא", - "show_details": "הצג פרטים", - "hide_details": "הסתר פרטים", - "external_content_warning": "תמונות ותוכן חיצוני נחסמו", - "load_external_content": "טען תמונות", - "trust_sender": "בטח תמיד בשולח הזה", - "back_to_list": "חזרה לרשימה", - "view_contact": "הצג איש קשר", - "message_details": "פרטי הודעה", - "more_reply_options": "אפשרויות תשובה נוספות", - "set_tag": "הגדר תג", - "tag": "תג", - "more_actions": "עוד פעולות", - "previous": "הקודם", - "next": "הבא", - "move_to": "העבר ל...", - "remove_tag": "הסר תג", - "tag_filter_placeholder": "סינון תגים", - "tag_no_matches": "אין תגים תואמים", - "more_count": "+{count}נוספים", - "characters_count": "{count} תווים", - "quick_reply_placeholder": "תשובה מהירה", - "more_options": "אפשרויות נוספות", - "sending": "שולח...", - "security_authentication": "אבטחה ואימות", - "technical_details": "פרטים טכניים", - "message_id_label": "מזהה הודעה:", - "reply_to_label": "תגובה ל:", - "delivery_time_label": "זמן אספקה:", - "conversation_part_label": "חלק מהשיחה:", - "previous_messages": "{count}הודעה קודמת", - "previous_messages_plural": "{count}הודעות קודמות", - "time": { - "day": "יום", - "days": "ימים", - "hour": "שעה", - "hours": "שעות", - "minute": "דקה", - "minutes": "דקות" - }, - "unknown_sender": "לא ידוע", - "recipient_me": "אותי", - "recipient_and_others": "{name}ו-{count}אחרים", - "recipient_to_prefix": "אל:", - "authentication": { - "title": "אימות", - "status": { - "verified": "מאומת", - "warning": "אזהרה", - "none": "לא מאומת" - }, - "spf": { - "pass": "SPF עבר", - "fail": "SPF נכשל", - "none": "אין SPF" - }, - "dkim": { - "pass": "DKIM תקין", - "fail": "DKIM לא תקין", - "none": "אין DKIM" - }, - "dmarc": { - "pass": "DMARC עבר", - "fail": "DMARC נכשל", - "none": "אין DMARC" - }, - "spam_score": "ציון ספאם", - "tooltip_spf": "Sender Policy Framework: מאמת שהשרת השוגר מורשה לשלוח דוא״ל בשם התחום", - "tooltip_dkim": "DomainKeys Identified Mail: מאשר שהדוא״ל לא שונה בדרך באמצעות חתימה קריפטוגרפית", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: מבטיח שSPF וDKIM יישתרו עם התחום של השולח וקובע מדיניות לכשלים", - "policy": "מדיניות", - "result": { - "pass": "עבר", - "fail": "נכשל", - "softfail": "כישלון רך", - "neutral": "ניטרלי", - "permerror": "שגיאה קבועה", - "temperror": "שגיאה זמנית", - "none": "ללא" - } - }, - "headers": { - "routing": "ניתוב", - "received": "התקבל", - "message_id": "מזהה הודעה", - "list_info": "רשימת מידע" - }, - "tooltips": { - "reply": "תשובה (ר)", - "reply_all": "השב לכולם (א)", - "forward": "קדימה (ו)", - "archive": "ארכיון (ה)", - "delete": "מחק (# או דל)", - "star": "כוכב (ים)", - "unstar": "הסר כוכב (ים)", - "compose": "חיבור (ג)", - "previous": "אימייל קודם", - "next": "המייל הבא", - "edit_draft": "ערוך טיוטה" - }, - "spam": { - "button_title": "דווח על ספאם", - "not_spam_title": "סמן כלגיטימי", - "toast_success": "עבר לזבל", - "toast_batch": "הודעות דוא\"ל{count}הועברו ל-Junk", - "toast_undo": "בטל", - "toast_not_spam_success": "הועבר לתיבת הדואר הנכנס", - "toast_not_spam_batch": "הודעות דוא\"ל{count}הועברו ל-Inbox", - "error": "נכשל הדיווח על ספאם", - "error_not_spam": "שחזור האימייל נכשל" - }, - "unsubscribe_banner": { - "label": "ניוזלטר", - "button": "בטל את המנוי", - "confirm_title": "לבטל את המנוי מהשולח הזה?", - "confirm_button": "אשר", - "cancel": "לְבַטֵל", - "success_http": "דף ביטול הרשמה נפתח בכרטיסייה חדשה", - "success_mailto": "אימייל ביטול ההרשמה נשלח", - "confirm_message_http": "דף ביטול ההרשמה ייפתח בכרטיסייה חדשה.", - "confirm_message_mailto": "אימייל ביטול הרשמה יישלח לשולח.", - "error": "לא ניתן לבטל את המנוי", - "dismiss": "לְפַטֵר" - }, - "calendar_invitation": { - "loading": "טוען את פרטי האירוע...", - "title": "הזמנה ללוח שנה", - "published_title": "אירוע שפורסם", - "response_title": "תגובה לאירוע", - "update_title": "עדכון אירוע", - "counter_title": "הצעה נגדית", - "refresh_title": "רענן בקשה", - "declined_counter_title": "ההצעה הנגדית נדחתה", - "cancelled_title": "האירוע בוטל", - "organizer": "מאורגן על ידי{name}", - "attendees": "{count, plural, one {משתתף אחד} other {# משתתפים}}", - "accept": "קבל", - "maybe": "אולי", - "decline": "דחה", - "add_to_calendar": "הוסף ליומן", - "added": "נוסף ליומן", - "rsvp_sent": "נשלחה תגובה", - "parse_error": "לא ניתן היה לקרוא את ההזמנה", - "action_failed": "לא ניתן היה להשלים את פעולת היומן הזו.", - "no_calendar": "לוח השנה אינו זמין", - "published_info": "אירוע זה שותף לעיון.", - "response_info": "הודעה זו מכילה תגובת משתתף.", - "response_info_organizer": "תגובת המשתתפים הזו מעדכנת את האירוע שלך.", - "update_info": "הודעה זו מעדכנת אירוע קיים.", - "counter_info": "הודעה זו מציעה שינויים באירוע.", - "counter_info_organizer": "משתתף זה הציע שינויים באירוע שלך.", - "refresh_info": "הודעה זו מבקשת את פרטי האירוע העדכניים ביותר.", - "refresh_info_organizer": "משתתף ביקש את פרטי האירוע העדכניים ביותר.", - "declined_counter_info": "המארגן דחה הצעה נגדית.", - "authentication_failed_info": "בדיקות אימות דואר עבור הזמנה זו נכשלו. התייחסו לפעולות לוח שנה בזהירות.", - "authentication_missing_info": "הזמנה זו אינה כוללת אימות דואר מאומת. אשר את הפרטים עם המארגן אם משהו נראה חריג.", - "sender_mismatch_info": "הזמנה זו נשלחה מ-{sender}, בעוד שהמארגן הרשום בנתוני היומן הוא{organizer}.", - "sender_mismatch_unverified_info": "הזמנה זו נשלחה מ-{sender}, בעוד שהמארגן הרשום בנתוני היומן הוא{organizer}, ולא ניתן לאמת את ההודעה.", - "organizer_role": "אתה מארגן את האירוע הזה", - "your_response": "תגובתך:{status}", - "response_needed": "צריך תגובה", - "response_accepted": "התקבל", - "response_tentative": "טנטטיבי", - "response_declined": "נדחה", - "response_delegated": "האצלה", - "actor_sent_info": "נשלח על ידי{name}.", - "actor_response_info": "{name}הגיב{status}.", - "actor_counter_info": "{name}הציע שינויים לאירוע זה.", - "actor_refresh_info": "{name}ביקש את פרטי האירוע העדכניים ביותר.", - "actor_declined_counter_info": "{name}דחה את ההצעה הנגדית.", - "actor_note": "הערה:{comment}", - "actor_unknown": "מישהו", - "proposed_changes": "שינויים מוצעים", - "change_title": "כותרת", - "change_time": "זמן", - "change_location": "מיקום", - "change_description": "תיאור", - "change_empty": "אין", - "change_from_to": "{before}->{after}", - "apply_proposal": "החל את השינויים המוצעים", - "proposal_applied": "השינויים שהוצעו הוחלו.", - "review_proposal": "בדוק את ההצעה", - "review_request": "בדיקת בקשה", - "view_in_calendar": "הצג בלוח שנה", - "select_calendar": "בחר לוח שנה", - "already_in_calendar": "כבר בלוח השנה שלך", - "request_info": "הוזמנת לאירוע זה. השב כדי ליידע את המארגן על זמינותך.", - "cancel_info": "המארגן ביטל את האירוע הזה.", - "event_updated": "עדכון #{sequence}", - "event_status_tentative": "טנטטיבי", - "event_status_cancelled": "בוטל", - "organizer_label": "מוארגן על ידי", - "expand": "הצג פרטים", - "collapse": "הסתר פרטים" - }, - "send": "שלח", - "more": "יותר", - "read_receipt": { - "prompt": "השולח ביקש קבלת דואר:", - "send": "שלח קבלה", - "ignore": "התעלם", - "sent": "קבלת דואר נשלחה.", - "send_failed": "לא היה ניתן לשלוח קבלת דואר", - "mdn_subject": "קרא: {subject}", - "mdn_body": "זו קבלת חזרה עבור ההודעה ששלחת אל {recipient}.\n\nהערה: קבלה זו רק מאשרת שההודעה הוצגה בהן של המקבל. אין ערובה שהמקבל קרא או הבין את תוכן ההודעה." - }, - "no_body_content": "(אין תוכן גוף זמין)", - "no_preview_available": "אין תצוגה מקדימה זמינה", - "show_quoted_text": "הצג טקסט מצוטט", - "hide_quoted_text": "הסתר טקסט מצוטט", - "unread": "סמן כלא נקרא", - "read": "קרא", - "spam_short": "דואר זבל", - "not_spam_short": "לא דואר זבל", - "move": "העברה", - "details": { - "recipients_routing": "נמענים ו־ניתוב", - "authentication_security": "אימות וביטחון", - "identifiers_threading": "מזהים ושרשורים", - "mailing_list": "רשימת תפוצה", - "message_properties": "מאפייני הודעה", - "sent": "נשלח", - "received": "התקבל", - "delivery_time": "זמן הסגרה", - "in_reply_to": "In-Reply-To", - "references": "הפניות", - "thread_id": "מזהה חוט", - "size": "גודל", - "mime_type": "סוג MIME", - "attachments_summary": "{count} קבצים · {size}", - "list_id": "מזהה רשימה", - "list_help": "עזרה לרשימה", - "list_post": "פוסט ברשימה", - "list_unsubscribe": "בטל הרשמה", - "iprev": "DNS הפוך", - "spam_status": "סטטוס דואר זבל", - "ai_verdict": "פסק דין AI", - "account": "חשבון", - "no_subject": "(ללא נושא)" - }, - "scheduled_banner": "מתוכנן לשליחה ב־{date}", - "scheduled_send_created": "דוא״ל תוכנן לשליחה", - "cancel_scheduled_send": "ביטול שליחה", - "reschedule_send": "תזמן מחדש", - "cancel_and_edit": "בטל וערוך", - "cancel_and_compose_again": "בטל והרכיב שוב", - "reschedule_prompt": "הזן תאריך/שעה חדש, למשל 2026-05-04T15:30", - "scheduled_actions_only": "תצוגה מתוכננת תומכת בפעולות מתוכננות בלבד", - "undo_send_scheduled": "הודעה תוכננת לשליחה", - "undo_send": "בטל שליחה", - "contact_sidebar": { - "title": "איש קשר", - "close": "סגור סרגל צד", - "action_email": "דוא״ל", - "action_email_title": "שלח דוא״ל", - "action_copy": "העתק", - "action_copy_title": "העתק דוא״ל", - "action_edit_title": "ערוך איש קשר", - "section_emails": "דוא״ל", - "section_phones": "טלפונים", - "section_organizations": "ארגונים", - "section_addresses": "כתובות", - "section_notes": "הערות", - "not_in_contacts": "לא בקשרים שלך", - "add_to_contacts": "הוסף לקשרים", - "copied": "הועתק!", - "copy_failed": "העתקה נכשלה" - }, - "send_now": "שלח עכשיו" - }, - "email_composer": { - "new_message": "הודעה חדשה", - "signature_edit_hint": "לחץ לחיצה כפולה לעריכת החתימה", - "reply": "השב", - "reply_all": "השב לכולם", - "forward": "קדימה", - "reply_to": "השב", - "reply_all_to": "השב לכולם", - "forward_message": "קדימה", - "from": "מאת", - "to": "אל", - "cc": "עותק", - "bcc": "עותק מוסתר", - "subject": "נושא", - "body_placeholder": "כתוב את הודעתך...", - "send": "שלח", - "cancel": "ביטול", - "attach": "צרף", - "discard": "מחק טיוטה", - "discard_draft_title": "למחוק את הטיוטה?", - "discard_draft_confirm": "יש לך שינויים שלא נשמרו. האם ברצונך למחוק את הטיוטה הזו?", - "saving": "שומר...", - "draft_saved": "הטיוטה נשמרה", - "save_failed": "השמירה נכשלה", - "to_placeholder": "כתובות אימייל של נמענים", - "cc_placeholder": "נמענים עותק", - "bcc_placeholder": "נמעני עותק מוסתר", - "subject_placeholder": "נושא", - "cc_label": "עותק:", - "bcc_label": "עותק מוסתר:", - "subject_label": "נושא:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(ללא נושא)", - "unknown_sender": "לא ידוע", - "quote": { - "reply_header": "ב-{date},{sender}כתב:", - "forward_header": "---------- הודעה הועברה ----------", - "from": "מאת:{sender}", - "date": "תאריך:{date}", - "subject": "נושא:{subject}", - "to": "אל:{recipients}" - }, - "remove_sub_address": "הסר כתובת משנה", - "use_template": "תבנית", - "save_as_template": "שמור כתבנית", - "validation": { - "recipient_required": "הוסף נמען לשליחה", - "body_required": "כתוב הודעה או צרף קובץ", - "attachments_uploading": "קבצים מצורפים עדיין בהעלאה - ההודעה תישלח מיד בסיום", - "attachment_upload_failed": "לא נשלח - העלאת קובץ מצורף נכשלה. הסירו אותו ונסו שוב." - }, - "upload_progress": "מעלה את{uploaded}/{total}", - "upload_cancel": "בטל העלאה", - "upload_failed": "העלאת{filename}נכשלה", - "attachment_too_large": "\"{name}\" גדול ממה שהשרת מאפשר ({max} לקובץ)", - "attachments_total_too_large": "הקבצים המצורפים יחרגו ממגבלת השרת של {max} להודעה", - "drop_files": "שחרר קבצים לצירוף", - "show_less": "הצג פחות", - "send_failed": "שליחת האימייל נכשלה", - "send_timeout": "השרת הפסיק להגיב. בדקו את תיקיית הנשלחים לפני שליחה חוזרת — ייתכן שההודעה כבר נשלחה.", - "continue_draft": "המשך טיוטה", - "close_draft_title": "לשמור או למחוק טיוטה?", - "close_draft_message": "יש לך שינויים שלא נשמרו. האם תרצה לשמור את זה כטיוטה או למחוק אותו?", - "smime_sign_on": "חתימת S/MIME מופעלת", - "smime_sign_off": "אפשר חתימת S/MIME", - "smime_encrypt_on": "הצפנת S/MIME מופעלת", - "smime_encrypt_off": "אפשר הצפנת S/MIME", - "smime_encrypt_unavailable": "הצפנת S/MIME אינה זמינה - אישורי נמען חסרים", - "smime_unlock_title": "בטל את נעילת מפתח S/MIME", - "smime_unlock_message": "הזן את משפט הסיסמה כדי לפתוח את מפתח החתימה ב-S/MIME שלך.", - "smime_unlock_button": "פתח את הנעילה", - "smime_passphrase_placeholder": "ביטוי סיסמה", - "read_receipt_on": "קבלת דואר מבוקשת (לחץ כדי להשבית)", - "read_receipt_off": "בקש קבלת דואר", - "dsn_on": "התבקש אישור מסירה (לחצו לביטול)", - "dsn_off": "בקשת אישור מסירה", - "format_plain_text": "מעבר לטקסט רגיל", - "format_rich_text": "מעבר לטקסט עשיר (HTML)", - "require_tls_on": "נדרשת מסירה מוצפנת (לחצו לביטול)", - "require_tls_off": "דרישת מסירה מוצפנת (TLS)", - "attach_photos": "תמונות וסרטונים", - "attach_files": "קבצים", - "sending": "שולח…", - "add_link": "הוסף קישור", - "link_url_prompt": "הזן את כתובת ה־URL", - "from_override": { - "toggle_off": "עקוף", - "toggle_on": "בטל עקיפה", - "toggle_tooltip": "ערוך בחופשיות את שם וכתובת השולח. הדוא״ל עדיין נשלח דרך הזהות שלך.", - "name_label": "שם ממנו", - "name_placeholder": "שם", - "email_label": "כתובת דוא״ל ממנו", - "email_placeholder": "alias@example.com", - "envelope_notice": "אם השרת שלך לא מקבל את הכתובת הזו כשולח המעטפה, ייעשה שימוש ב-{identity} במקומה, והנמענים יכולים לראות אותה בכותרת Return-Path." - }, - "forgot_attachment": { - "title": "נשכחת קובץ מצורף?", - "message": "ההודעה שלך מתייחסת ל״{keyword}״ אך לא צורף קובץ. שלח בכל זאת?", - "send_anyway": "שלח בכל זאת", - "back": "חזור לעריכה" - }, - "empty_subject": { - "title": "לשלוח ללא נושא?", - "message": "להודעה זו אין נושא. לשלוח בכל זאת?", - "dont_ask_again": "אל תשאל שוב", - "send_anyway": "שלח בכל זאת", - "back": "חזור לעריכה" - }, - "schedule_send": "תזמן שליחה", - "schedule_send_description": "בחר מתי השרת צריך לשחרר הודעה זו.", - "schedule_send_required": "בחר תאריך ושעה.", - "schedule_send_invalid": "הזן תאריך ושעה חוקיים.", - "schedule_send_future": "בחר תאריך ושעה עתידית.", - "schedule_send_too_late": "שעה זו מאוחרת יותר מכפי שהשרת מאפשר.", - "schedule_send_unsupported": "תזמון שליחה אינו נתמך עבור חשבון זה.", - "schedule_send_cleanup_warning": "תזמון שליחה נוצר, אך ניקוי טיוטה נכשל.", - "send_delay_unsupported": "השהיית שליחה אינה נתמכת עבור חשבון זה.", - "send_delay_unsupported_confirm": "חשבון זה אינו תומך בהשהיית שליחה. שלח מיד במקום זאת?", - "recipient_edit_email": "ערוך כתובת דוא״ל", - "recipient_edit_name": "ערוך שם תצוגה", - "recipient_email_placeholder": "כתובת דוא״ל", - "recipient_name_placeholder": "שם תצוגה", - "autocomplete_search_server": "חיפוש בשרת", - "autocomplete_searching": "מחפש...", - "toolbar": { - "bold": "מודגש", - "italic": "נטוי", - "underline": "קו תחתון", - "strikethrough": "קו חוצה", - "text_color": "צבע טקסט", - "remove_color": "הסרת צבע", - "heading_1": "כותרת 1", - "heading_2": "כותרת 2", - "bullet_list": "רשימת תבליטים", - "ordered_list": "רשימה ממוספרת", - "quote": "ציטוט", - "code_block": "בלוק קוד", - "align_left": "יישור לשמאל", - "align_center": "מרכוז", - "align_right": "יישור לימין", - "text_direction": "כיוון טקסט (RTL/LTR)", - "link": "קישור", - "table": "טבלה", - "clear_formatting": "ניקוי עיצוב", - "undo": "ביטול", - "redo": "ביצוע מחדש", - "add_row_above": "הוספת שורה מעל", - "add_row_below": "הוספת שורה מתחת", - "add_column_before": "הוספת עמודה לפני", - "add_column_after": "הוספת עמודה אחרי", - "delete_row": "מחיקת שורה", - "delete_column": "מחיקת עמודה", - "toggle_header_row": "החלפת שורת כותרת", - "delete_table": "מחיקת טבלה", - "pick_size": "בחירת גודל", - "font_size": "גודל גופן", - "font_size_default": "ברירת מחדל", - "background_color": "צבע רקע", - "remove_background_color": "הסר צבע רקע" - }, - "send_filing_warning": "נשלח - אך הניקוי שלאחר השליחה נכשל, ייתכן שתישאר טיוטה ישנה." - }, - "confirm_dialog": { - "confirm": "אשר", - "cancel": "לְבַטֵל" - }, - "common": { - "loading": "טוען...", - "error": "שגיאה", - "success": "הצלחה", - "cancel": "ביטול", - "save": "שמירה", - "delete": "מחיקה", - "edit": "עריכה", - "close": "סגירה", - "search": "חיפוש", - "refresh": "רענון", - "settings": "הגדרות", - "help": "עזרה", - "logout": "התנתקות", - "yes": "כן", - "no": "לא", - "unknown": "לא ידוע", - "app_title": "Webmail", - "reconnecting": "החיבור אבד. מנסה להתחבר מחדש...", - "rate_limited_title": "אימות השרת מוגבל באופן זמני בקצב.", - "rate_limited_detail": "Bulwark השהתה בקשות ברקע כדי למנוע נעילה. מנסה שוב ב-{seconds}s.", - "rate_limited_action_title": "הבקשה הושהתה כדי למנוע נעילה.", - "rate_limited_action_detail": "Bulwark ממתין עד שקירור השרת יסתיים לפני שליחת בקשות מאומתות יותר. נסה שוב ב-{seconds}s.", - "pull_to_refresh": "משוך לרענון", - "release_to_refresh": "שחרר לרענון", - "refreshing": "מרענן…" - }, - "notifications": { - "email_sent": "האימייל נשלח בהצלחה", - "email_deleted": "האימייל נמחק", - "email_archived": "האימייל הועבר לארכיון", - "emails_deleted": "{count, plural, one {הודעה אחת נמחקה} other {# הודעות נמחקו}}", - "emails_archived": "{count, plural, one {הודעה אחת הועברה לארכיון} other {# הודעות הועברו לארכיון}}", - "error_archiving": "העברת ההודעה לארכיון נכשלה", - "error_updating": "עדכון ההודעה נכשל", - "email_starred": "אימייל מסומן בכוכב", - "email_unstarred": "דוא\"ל בוטל בכוכב", - "email_marked_read": "אימייל סומן כנקראה", - "email_marked_unread": "אימייל סומן כלא נקרא", - "copied_to_clipboard": "הועתק ללוח", - "source_copied": "המקור הועתק ללוח", - "error_sending": "שליחת האימייל נכשלה", - "error_deleting": "מחיקת האימייל נכשלה", - "error_loading": "טעינת האימייל נכשלה", - "new_email": "מייל חדש", - "new_email_from": "מ-{sender}", - "click_to_view": "לחץ לצפייה", - "email_moved": "האימייל הועבר", - "emails_moved": "דוא\"ל{count}הועברו", - "moved_to_mailbox": "הועבר אל{mailbox}", - "move_failed": "המהלך נכשל", - "move_error": "לא ניתן היה להעביר הודעות דוא\"ל לתיקיה שנבחרה", - "email_tagged": "אימייל מתויג", - "emails_tagged": "דוא\"ל{count}מתויג", - "tag_failed": "התיוג נכשל", - "identity_created": "הזהות נוצרה בהצלחה", - "identity_updated": "הזהות עודכנה בהצלחה", - "identity_deleted": "הזהות נמחקה", - "identity_set_primary": "הזהות הראשית עודכנה", - "identity_create_failed": "יצירת זהות נכשלה:{error}", - "identity_update_failed": "עדכון הזהות נכשל:{error}", - "identity_delete_failed": "מחיקת הזהות נכשלה:{error}", - "identity_unauthorized": "אינך מורשה לשלוח מכתובת דוא\"ל זו", - "identity_not_found": "זהות לא נמצאה", - "vacation_saved": "הגדרות משיב חופשה נשמרו", - "vacation_save_failed": "שמירת הגדרות משיב חופשה נכשלה", - "filters_saved": "המסננים נשמרו בהצלחה", - "filters_save_failed": "שמירת המסננים נכשלה", - "filters_deleted": "כלל הסינון נמחק", - "templates_exported": "תבניות יוצאו בהצלחה", - "templates_imported": "יובאו {count, plural, one {תבנית אחת} other {# תבניות}}", - "templates_import_errors": "לא ניתן לייבא חלק מהתבניות", - "templates_import_empty": "לא נמצאו תבניות בקובץ", - "export_email_error": "ייצוא האימייל נכשל", - "import_email_success": "אימייל יובא בהצלחה", - "import_email_error": "ייבוא האימייל נכשל" - }, - "date": { - "today": "היום", - "yesterday": "אתמול", - "this_week": "השבוע", - "last_week": "שבוע שעבר", - "this_month": "החודש הזה", - "older": "ישן יותר", - "just_now": "רק עכשיו", - "minutes_ago": "{count}לפני דקה", - "minutes_ago_plural": "{count}לפני דקות", - "hours_ago": "{count}לפני שעה", - "hours_ago_plural": "{count}לפני שעות", - "days_ago": "{count}לפני יום", - "days_ago_plural": "{count}לפני ימים" - }, - "language": { - "title": "שפה", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "בחירת שפה", - "switch_to_english": "עבור לאנגלית", - "switch_to_french": "תעבור לצרפתית", - "switch_to_japanese": "תעבור ליפנית", - "switch_to_spanish": "עבור לספרדית", - "switch_to_italian": "תעבור לאיטלקית", - "switch_to_german": "תעבור לגרמנית", - "switch_to_dutch": "עבור להולנדית", - "switch_to_portuguese": "עבור לפורטוגזית", - "switch_to_russian": "תעבור לרוסית", - "switching": "מחליף שפה...", - "switch": "החלף שפה", - "current": "שפה נוכחית", - "polish": "Polski", - "switch_to_polish": "עבור לפולנית", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "עבור לפרסית", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "עבור למונגולית" - }, - "settings": { - "title": "הגדרות", - "back_to_mail": "חזרה לדואר", - "save_success": "ההגדרות נשמרו בהצלחה", - "import_success": "ההגדרות יובאו בהצלחה", - "import_error": "ייבוא ההגדרות נכשל", - "reset_confirm": "האם אתה בטוח שברצונך לאפס את כל ההגדרות לברירות מחדל?", - "unsaved_changes": "יש לך שינויים שלא נשמרו", - "discard_changes": "למחוק שינויים שלא נשמרו?", - "discard": "לזרוק", - "keep_editing": "תמשיך לערוך", - "tabs": { - "appearance": "מראה", - "language": "שפה ואזור", - "email": "התנהגות אימייל", - "composer": "עורך", - "privacy": "פרטיות ואבטחה", - "account": "חשבון", - "identities": "זהויות", - "vacation": "משיב נופש", - "advanced": "מתקדם", - "calendar": "לוּחַ שָׁנָה", - "filters": "מסננים", - "templates": "תבניות", - "folders": "תיקיות", - "keywords": "מילות מפתח", - "security": "אבטחה", - "files": "קבצים", - "contacts": "אנשי קשר", - "encryption": "הצפנה", - "sidebar_apps": "אפליקציות בסרגל הצד", - "notifications": "התראות", - "protocol_handlers": "יישומים ברירת מחדל", - "layout": "פריסה", - "reading": "קריאה", - "composing": "הרכבה", - "downloads": "הורדות", - "content_senders": "תוכן ושולחים", - "about_data": "בערך וגדול", - "debug": "ניפוי שגיאות" - }, - "tab_groups": { - "general": "כללי", - "account": "חשבון וזהות", - "organization": "ארגון דואר", - "apps": "אפליקציות", - "system": "מערכת", - "appearance": "הופעה", - "mail": "דוא״ל", - "privacy": "פרטיות וביטחון", - "advanced": "מתקדם" - }, - "appearance": { - "message_list_order": { - "label": "סדר רשימת ההודעות", - "description": "איך ההודעות מסודרות ברשימה. המיון מתבצע בשרת, ולכן חל על כל התיקייה ולא רק על ההודעות שנטענו.", - "preset": { - "chronological": "כרונולוגי (החדשות תחילה)", - "unread_first": "שלא נקראו תחילה", - "starred_first": "מסומנות בכוכב תחילה", - "tagged_first": "מתויגות תחילה", - "custom": "מותאם אישית" - }, - "tag_label": "תגית", - "advanced_show": "מתקדם: עריכת רמות המיון", - "advanced_hide": "הסתרת מתקדם", - "criterion_label": "קריטריון רמה {n}", - "direction_label": "כיוון רמה {n}", - "criteria": { - "unread": "לא נקרא", - "starred": "מסומן בכוכב", - "tag": "תגית", - "receivedAt": "תאריך קבלה", - "sentAt": "תאריך שליחה", - "from": "שולח", - "subject": "נושא", - "size": "גודל" - }, - "direction": { - "unread_first": "שלא נקראו תחילה", - "read_first": "שנקראו תחילה", - "starred_first": "מסומנות בכוכב תחילה", - "unstarred_first": "ללא כוכב תחילה", - "tagged_first": "מתויגות תחילה", - "untagged_first": "ללא תגית תחילה", - "newest_first": "החדשות תחילה", - "oldest_first": "הישנות תחילה", - "a_to_z": "א → ת", - "z_to_a": "ת → א", - "largest_first": "הגדולות תחילה", - "smallest_first": "הקטנות תחילה" - }, - "add_level": "הוספת רמה", - "remove_level": "הסרת רמה", - "move_up": "הזזה למעלה", - "move_down": "הזזה למטה", - "tiebreak_note": "עד שלוש רמות; הרמות הבאות מכריעות במקרה של שוויון. \"החדשות תחילה\" תמיד מוחל אחרון.", - "scope": { - "label": "החלה על כל התיקיות", - "description": "כשכבוי, הסדר חל על תיבת הדואר הנכנס בלבד; שאר התיקיות נשארות כרונולוגיות." - }, - "unsupported_note": "שרת הדואר שלך אינו תומך במיון לפי מצב קריאה, כוכב או תגיות. רמות אלה מתעלמות והרשימה נשארת כרונולוגית." - }, - "title": "מראה", - "description": "התאם אישית את המראה והתחושה של דואר האינטרנט שלך", - "theme": { - "label": "נושא", - "description": "בחר את ערכת הצבעים המועדפת עליך", - "light": "בהיר", - "dark": "כהה", - "system": "מערכת" - }, - "language": { - "label": "שָׂפָה", - "description": "בחר את השפה המועדפת עליך" - }, - "font_size": { - "label": "גודל גופן", - "description": "התאם את גודל הטקסט לקריאות טובה יותר", - "small": "קטן", - "medium": "בינוני", - "large": "גדול" - }, - "list_density": { - "label": "צפיפות", - "description": "שליטה במרווחים ובריפוד על פני ממשק המשתמש", - "extra_compact": "קומפקטי במיוחד", - "compact": "קומפקטי", - "regular": "רגיל", - "comfortable": "נוח" - }, - "animations": { - "label": "הפעל אנימציות", - "description": "הצג מעברים ואפקטים חלקים" - }, - "toolbar_position": { - "label": "מיקום סרגל הכלים", - "description": "היכן להציג לחצני פעולה בדוא\"ל (השב, ארכיון, מחק וכו')", - "top": "למעלה", - "below_subject": "מתחת לנושא" - }, - "toolbar_labels": { - "label": "הצג תוויות של סרגל הכלים", - "description": "הצג תוויות טקסט לצד סמלי סרגל הכלים. השבת כדי לחסוך במקום ברגע שאתה מכיר את הסמלים." - }, - "hide_account_switcher": { - "label": "הסתר את מחליף החשבונות בסרגל הצד", - "description": "הסתר את בורר החשבונות בחלק העליון של סרגל הצד של התיקיות. אתה עדיין יכול להחליף חשבונות ממסילת הניווט התחתונה." - }, - "show_rail_account_list": { - "label": "הצג אווטרים של חשבון על רכבת ניווט", - "description": "הצג עיגולי חשבון בודדים בתחתית מסילת הניווט למעבר מהיר, עם לחצן יציאה למטה." - }, - "unified_mailbox": { - "label": "תיבת דואר מאוחדת", - "description": "הצג תיקיות משולבות (דואר נכנס, נשלח וכו׳) בכל החשבונות המחוברים", - "cross_account": { - "label": "בכל החשבונות", - "description": "מזג את תיבת הדואר המאוחדת בכל החשבונות המחוברים במקום להישאר בחשבון הפעיל." - }, - "include_group": { - "label": "כלול תיבות דואר קבוצתיות", - "description": "גם מזג תיבות דואר משותפות/קבוצתיות לתצוגה המאוחדת." - } - }, - "all_mail": { - "folders_label": "תיקיות בכל הדוא״ל", - "folders_description": "בחר אילו תיקיות מוזגות לתצוגת כל הדוא״ל.", - "account_hint": "חל על {account}.", - "no_folders": "אין תיקיות זמינות." - }, - "colorful_sidebar_icons": { - "label": "סמלי סרגל צד צבעוניים", - "description": "צבע אייקונים של תיקיות וקוביות לפי סוג (כחול Inbox, אדום Junk, ירוק Sent וכו׳). השבת עבור סרגל צד חד צבע." - }, - "tint_list_rows": { - "label": "צביעת שורות הרשימה לפי צבע התגית", - "description": "צובע כל שורת הודעה בצבע התגית הראשונה שלה. כבו כדי להשאיר את השורות ללא צבע; נקודות ותוויות התגיות עדיין יציגו את הצבע." - }, - "show_folder_total_count": { - "label": "הצגת מספר ההודעות הכולל", - "description": "מציג את מספר ההודעות הכולל לצד תיקיות ותגיות, לצד מספר ההודעות שלא נקראו. כבו כדי להציג רק את מספר ההודעות שלא נקראו." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "ממשק Pro (ניסיוני)", - "description": "פריסה של משתמש כוח לשולחן עבודה בלבד עם גלישה מרובת-הודעות וזרימות עבודה חוצות-חשבונות. הממשק הסטנדרטי אינו מושפע; תוכל להחזור בכל עת.", - "open_label": "פתח ממשק Pro", - "back_to_standard": "חזור לתקן" - }, - "cross_unread": { - "label": "כל החשבונות: שלא נקראו", - "description": "הצג ערך בסעיף כל החשבונות המפרט דוא״ל שלא נקראו בכל חשבון, המשתרע על כל התיקיות למעט זבל, נשלח, ארכיון, אשפה וטיוטות." - }, - "cross_starred": { - "label": "כל החשבונות: מסומן", - "description": "הצג ערך בסעיף כל החשבונות המפרט דוא״ל מסומן בכל חשבון, המשתרע על כל התיקיות למעט זבל, נשלח, ארכיון, אשפה וטיוטות." - }, - "cross_all": { - "label": "כל החשבונות: כל הדוא״ל", - "description": "הצג ערך בסעיף כל החשבונות המפרט את כל הדוא״ל בכל חשבון, המשתרע על כל התיקיות למעט זבל, נשלח, ארכיון, אשפה וטיוטות." - } - }, - "keywords": { - "title": "מילות מפתח בדוא\"ל", - "description": "הגדר מילות מפתח (תוויות/תגים) כדי לארגן את המיילים שלך עם צבעים. אלו מאוחסנות כמילות מפתח JMAP בשרת.", - "add_keyword": "הוסף מילת מפתח", - "label_field": "שם תצוגה", - "label_placeholder": "למשל עבודה, אישי, דחוף", - "id_field": "מזהה מילת מפתח", - "id_placeholder": "למשל עבודה, אישית", - "color_field": "צבע", - "id_exists": "מזהה מילת מפתח זה כבר קיים", - "edit": "ערוך מילת מפתח", - "delete": "מחק מילת מפתח", - "save": "שמור", - "add": "לְהוֹסִיף", - "cancel": "לְבַטֵל", - "migrating": "מעדכן מילת מפתח באימיילים קיימים...", - "migration_error": "נכשל עדכון מילת המפתח בהודעות דוא\"ל קיימות", - "nesting": { - "label": "תגים מקוננים", - "description": "קנן תגים תחת תגים אחרים והצג אותם כעץ בסרגל הצד." - }, - "parent_field": "תג אב", - "no_parent": "ללא תג אב", - "too_long": "נתיב התג ארוך מדי (עד {max} תווים)", - "has_children_locked": "תגים אחרים מקוננים תחת תג זה, ולכן שמו ותג האב שלו נעולים. העבר או הסר אותם תחילה.", - "has_children_delete": "הסר תחילה את התגים המקוננים תחת תג זה", - "visibility_field": "הצגה בסרגל הצד", - "visibility": { - "show": "הצג", - "unread": "הצג כשיש שלא נקראו", - "hide": "הסתר" - }, - "unrecognized": { - "title": "שחזור תגים מהשרת", - "description": "שמות התגים והצבעים שלהם נשמרים בדפדפן הזה, אך התגים עצמם מאוחסנים על ההודעות שלך כמילות מפתח JMAP. סרוק את הדואר שלך לאיתור מילות מפתח שאין להן תג כאן, והוסף אותן בחזרה.", - "scan": "סריקה לאיתור תגים", - "rescan": "סרוק שוב", - "scanning": "סורק… {scanned} מתוך {total} הודעות", - "none": "כל התגים שעל ההודעות שלך כבר מוגדרים כאן.", - "messages": "{count, plural, one {הודעה אחת} other {# הודעות}}", - "add_all": "הוסף את כל {count}", - "partial": "נסרקו רק {scanned} ההודעות האחרונות מתוך {total}. תגים שנמצאים רק בדואר ישן יותר עשויים להיעדר.", - "error": "לא ניתן היה לסרוק את הדואר לאיתור תגים." - } - }, - "notifications": { - "test_sound": "צליל הודעת בדיקה", - "sounds": { - "default": "ברירת מחדל (ביפ)", - "cheerful": "עליז", - "involved": "מעורב", - "swift": "מחווה מהירה", - "relax": "תירגע" - }, - "sound_selection": { - "title": "צליל התראה", - "description": "בחר איזה צליל להפעיל עבור התראות", - "choose": "צליל", - "choose_desc": "בחר צליל התראה ולחץ על סמל הרמקול כדי להציג אותו בתצוגה מקדימה" - }, - "email": { - "title": "הודעות אימייל", - "description": "הגדר התראות עבור הודעות דוא\"ל נכנסות", - "enabled": "התראות באימייל", - "enabled_desc": "הצג התראות כשמגיעות אימיילים חדשים", - "sound": "צליל התראה", - "sound_desc": "הפעל התראת שמע כשמגיעות הודעות דוא\"ל חדשות", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "הודעות לוח שנה", - "description": "הגדר התראות עבור אירועי לוח שנה", - "enabled": "התראות על אירועים", - "enabled_desc": "הצג התראות עבור אירועי לוח שנה קרובים", - "sound": "צליל התראה", - "sound_desc": "הפעל התראה קולית עבור תזכורות יומן", - "invitation_parsing": "נתח הזמנות באימייל", - "invitation_parsing_desc": "זיהוי הזמנות ליומן בקבצים מצורפים לאימייל והצג פעולות ביומן" - }, - "push": { - "title": "התראות בדפדפן", - "description": "קבל עדכונים מערכת עבור דוא״ל חדש כאשר אתר זה סגור. מסופק דרך ממסר Bulwark; הממסר לא רואה תוכן דוא״ל.", - "relay_label": "ממסר דחיפה", - "relay_desc": "ברירת מחדל של ממסר Bulwark המתורח. שנה רק אם אתה מארח בעצמך.", - "relay_locked": "הוגדר על ידי מנהל", - "relay_locked_desc": "כתובת ממסר הדחיפה הוגדרה על ידי מנהל שלך ולא ניתן לשנות אותה.", - "status_active": "פעיל בהתקן זה", - "status_inactive": "לא מופעל בהתקן זה", - "status_unsupported": "דפדפן זה אינו תומך בדחיפה ברשת", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "עובד…", - "enable": "הפעל", - "reenable": "רשום מחדש", - "disable": "השבת", - "confirm_disable_title": "השבת התראות בדפדפן?", - "confirm_disable_message": "התקן זה יפסיק לקבל התראות כאשר האתר סגור.", - "dismiss_aria": "בטל הודעת התראות", - "ios_hint": "ב־iOS, התקן קודם כל את האתר בהיתוך שלך - Safari מספק Web Push רק לPWA מותקנות.", - "devices_title": "מכשירים רשומים", - "devices_desc": "כל המכשירים הרשומים לקבלת התראות ברקע עבור חשבון זה. ביטול מכשיר מפסיק את הקבלה מיד.", - "devices_loading": "טוען מכשירים רשומים…", - "devices_empty": "אין מכשירים רשומים להתראות ברקע.", - "devices_error": "לא ניתן לטעון את המכשירים הרשומים.", - "devices_refresh": "רענון", - "device_this": "מכשיר זה", - "device_other": "מכשיר אחר", - "device_status_active": "מספק התראות", - "device_status_inactive": "מעולם לא סיפק התראה", - "device_status_unknown": "לא מוכר לממסר הזה", - "device_expires": "פג בתאריך {date}", - "revoke": "ביטול", - "confirm_revoke_title": "לבטל את המכשיר הזה?", - "confirm_revoke_message": "הוא יפסיק לקבל התראות ברקע מיד. הפעלה מחדש של פוש באותו מכשיר תרשום אותו מחדש.", - "confirm_revoke_message_this": "מכשיר זה יפסיק לקבל התראות ברקע מיד." - } - }, - "language_region": { - "title": "שפה ואזור", - "description": "הגדר את העדפות השפה והאזור", - "language": { - "label": "שָׂפָה", - "description": "בחר את השפה המועדפת עליך", - "english": "אנגלית", - "french": "Français" - }, - "date_format": { - "label": "פורמט תאריך", - "description": "כיצד יש להציג תאריכים", - "smart": "חכם (מותאם לאזור)", - "relative": "יחסית (לפני שעה, לפני יומיים)", - "full": "תמיד תאריך מלא", - "preview_today": "היום:", - "preview_this_week": "השבוע הזה:", - "preview_older": "ישן יותר:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "פורמט זמן", - "description": "בחר בין שעון של 12 שעות או 24 שעות", - "12h": "12 שעות", - "24h": "24 שעות" - }, - "first_day": { - "label": "היום הראשון בשבוע", - "description": "התחל את השבוע ביום ראשון או שני", - "sunday": "ראשון", - "monday": "שני" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "נקה את החיפוש בעת מעבר בין תיקיות", - "description": "לחיצה על תיקייה (דואר נכנס, נשלח…) מנקה את החיפוש הפעיל ומציגה את התיקייה. כשהאפשרות כבויה, החיפוש נשאר ומופעל מחדש בתיקייה שאליה עברת." - }, - "title": "התנהגות אימייל", - "description": "קבע את אופן הטיפול בהודעות דוא\"ל", - "mark_read": { - "label": "סמן כקריאה", - "description": "מתי לסמן הודעות דוא\"ל כנקראו בעת פתיחה", - "instant": "באופן מיידי", - "delay_3s": "לאחר 3 שניות", - "delay_5s": "לאחר 5 שניות", - "never": "לעולם לא" - }, - "delete_action": { - "label": "מחק את הפעולה", - "description": "מה קורה כשמוחקים אימייל", - "trash": "העבר לאשפה", - "permanent": "מחק לצמיתות", - "warning": "הודעות אימייל יימחקו לצמיתות ולא ניתן לשחזר אותן. פעולה זו היא בלתי הפיכה.", - "trash_and_read": "העבר לאשפה וסמן כקרא" - }, - "message_spacing": { - "label": "מרווח סביב ההודעה", - "description": "ריווח מסביב לגוף ההודעה בתצוגת הקריאה", - "auto": "אוטומטי (מומלץ)", - "always": "תמיד להוסיף מרווח", - "edge": "מקצה לקצה" - }, - "plain_text_font": { - "label": "גופן טקסט פשוט", - "description": "הגופן המשמש להודעות בטקסט פשוט", - "mono": "ברוחב קבוע", - "sans": "רגיל (כמו היישום)" - }, - "archive_mode": { - "label": "ארכיון ב", - "description": "כיצד לארגן מיילים בעת אחסון בארכיון", - "single": "תיקיה בודדת", - "year": "תיקייה לשנה", - "month": "תיקייה לחודש", - "reorganize": "ארגן מחדש את הארכיון הקיים", - "reorganize_success": "{count, plural, =0 {אין הודעות דוא״ל לארגון מחדש} =1 {הודעת דוא״ל אחת אורגנה מחדש} other {# הודעות דוא״ל אורגנו מחדש}}", - "reorganize_error": "נכשל ארגון מחדש של הארכיון" - }, - "permanently_delete_junk": { - "label": "מחק זבל לצמיתות", - "description": "מחק לצמיתות הודעות דוא\"ל מתיקיית הזבל/ספאם במקום להעביר אותן לאשפה" - }, - "mail_layout": { - "label": "פריסת דואר", - "description": "בחר בין חלונית הקריאה המפוצלת הקלאסית לבין זרימת קריאה ממוקדת בסגנון Gmail.", - "split": "חלונית מפוצלת", - "split_description": "השאר את רשימת ההודעות וחלונית הקריאה גלויים זה לצד זה.", - "focus": "רשימה ממוקדת", - "focus_description": "הצג שורה אחת לכל הודעה ופתח דואר ברוחב מלא תוך שמירה על סרגל הצד של התיקייה גלוי.", - "horizontal": "חלונית קריאה בתחתית", - "horizontal_description": "הצג את רשימת ההודעות בחלק העליון וכנס את ההודעה שנבחרה בחלונית קריאה למטה." - }, - "show_preview": { - "label": "הצג טקסט תצוגה מקדימה", - "description": "הצג תצוגה מקדימה של דואר אלקטרוני ברשימה", - "focus_description": "הצג טקסט תצוגה מקדימה מוטבע בתוך רשימת ההודעות הממוקדת בשורה אחת" - }, - "disable_threading": { - "label": "השבת את קיבוץ השיחות", - "description": "הצג הודעות אימייל כהודעות בודדות במקום מקובצות לפי שיחה" - }, - "plain_text_mode": { - "label": "טקסט רגיל בלבד", - "description": "השבת את עורך הטקסט העשיר ושלח את כל המיילים כטקסט רגיל בלבד, כולל תשובות והעברה" - }, - "auto_select_reply_identity": { - "label": "בחירה אוטומטית בכתובת תשובה", - "description": "בעת תשובה, העבר אוטומטית את הכתובת מאת לזהות שקיבלה את ההודעה במקור" - }, - "reply_identity_match": { - "label": "התאמת כתובת הקבלה", - "description": "אילו כתובות קבלה נחשבות לשלך. „כתובת מדויקת בלבד“ בוחרת רק אחת מהזהויות שהגדרת; „אותו דומיין“ מתייחסת גם לכל כתובת אחרת בדומייני הזהויות שלך ככינוי catch-all ומשכתבת אליה את כותרת השולח. בחר בכתובת מדויקת אם כתובות אלה הן רשימות תפוצה.", - "exact": "כתובת מדויקת בלבד", - "domain": "כל כתובת בדומיינים שלי" - }, - "attachment_click_action": { - "label": "קובץ מצורף לחץ על פעולה", - "description": "בחר אם לחיצה על קובץ מצורף תציג אותו בתצוגה מקדימה או תוריד אותו מיד", - "preview": "תצוגה מקדימה כשאפשר", - "download": "הורד מיד" - }, - "attachment_position": { - "label": "עמדת התקשרות", - "description": "היכן להציג קבצים מצורפים בכותרת הדוא\"ל", - "beside-sender": "ליד השולח", - "below-header": "מתחת לכותרת" - }, - "emails_per_page": { - "10": "10 מיילים", - "25": "25 מיילים", - "50": "50 מיילים", - "100": "100 מיילים", - "label": "מיילים לכל עמוד", - "description": "מספר הודעות האימייל לטעינה בבת אחת" - }, - "always_light_mode": { - "label": "הצג תמיד הודעות דוא\"ל במצב קל", - "description": "עבד את תוכן האימייל במצב בהיר גם כשהאפליקציה במצב כהה, תוך הימנעות מבעיות המרה במצב כהה" - }, - "external_content": { - "label": "תוכן חיצוני", - "description": "כיצד לטפל בתמונות ובתוכן חיצוני", - "ask": "תמיד תשאל", - "block": "תמיד לחסום", - "allow": "תמיד אפשר" - }, - "trusted_senders": { - "label": "שולחים מהימנים", - "description": "נהל שולחים שהתמונות שלהם נטענות אוטומטית", - "count_zero": "אין", - "count_one": "שולח 1", - "count_other": "שולחי{count}", - "modal_title": "שולחים מהימנים", - "empty_title": "עדיין אין שולחים מהימנים", - "empty_description": "בעת הצגת אימייל עם תמונות חסומות, לחץ על \"תמיד בטח בשולח זה\" כדי להוסיף אותן כאן.", - "add_manually": "הוסף שולח באופן ידני", - "add_button": "לְהוֹסִיף", - "add_placeholder": "הזן כתובת אימייל", - "search_placeholder": "חפש שולחים...", - "no_results": "אין שולחים שתואמים לחיפוש שלך", - "remove": "הסר", - "close": "לִסְגוֹר", - "invalid_email": "נא להזין כתובת אימייל חוקית", - "already_added": "השולח הזה כבר מהימן", - "save_error": "נכשל לשמור - בדוק את יומן ניפוי הנתונים של אנשי קשר לפרטים", - "use_address_book_label": "סנכרן עם ספר כתובות", - "use_address_book_description": "שמור שולחים מהימנים בספר כתובות ״שולחים מהימנים״ ייעודי כדי שהם יסתנכרנו בכל ההתקנים שלך" - }, - "hover_actions": { - "label": "פעולות ריחוף מהירות", - "description": "בחר אילו פעולות מהירות יופיעו בעת ריחוף מעל הודעת דוא\"ל ברשימה", - "delete": "לִמְחוֹק", - "star": "כוכב / הסר כוכב", - "mark_read": "סמן שנקרא/לא נקרא", - "archive": "ארכיון", - "tag": "תג", - "spam": "סמן כדואר זבל", - "none_selected": "לא נבחרו פעולות", - "mode_label": "מצב תצוגה", - "mode_inline": "מוטבע", - "mode_floating": "צף", - "corner_label": "מיקום צף", - "corner_top-left": "שמאל למעלה", - "corner_top-right": "למעלה מימין", - "corner_bottom-left": "שמאל למטה", - "corner_bottom-right": "ימין למטה", - "not_spam": "לא דואר זבל" - }, - "default_mail_program": { - "label": "תוכנית דואר ברירת מחדל", - "description": "רשום את{appName}כתוכנית הדואר המוגדרת כברירת מחדל עבור קישורי mailto:", - "button": "הגדר כברירת מחדל", - "success": "דפדפן התבקש להגדיר כברירת מחדל", - "error": "הדפדפן שלך אינו תומך בתכונה זו" - }, - "request_read_receipt": { - "label": "בקש קבלות דואר כברירת מחדל", - "description": "הפעל מראש את בקשת קבלת הדואר בעת הרכבת הודעה חדשה." - }, - "read_receipt_response": { - "label": "הגב לבקשות קבלת דואר", - "description": "מה לעשות כאשר הודעה נכנסת מבקשת קבלת דואר.", - "ask": "שאל כל פעם", - "always": "תמיד שלח", - "never": "לא תמיד שלח" - }, - "signature_position": { - "label": "מיקום חתימה", - "description": "היכן להוסיף את החתימה שלך בתשובות והעברות. מעל הטקסט המצוטט קורא באופן טבעי כסגירה לתשובה; למטה מפיקה את ההודעה המקורית רציפה.", - "above_quote": "לפני טקסט מצוטט", - "below_quote": "אחרי טקסט מצוטט" - }, - "signature_separator": { - "label": "מפריד חתימה", - "description": "הוסף קידומת לחתימה בעדכון סטנדרטי ״-- ״ (RFC 3676). כבה אם אתה מעדיף לזרום ישירות מההודעה שלך לחתימה." - }, - "sub_address_delimiter": { - "label": "מפריד כתובת משנה", - "description": "תו המפריד בין שם המשתמש שלך לתג כתובת משנה. תאם את המפריד שדואר השרת שלך משתמש בו (למשל user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "מותאם אישית…", - "custom_input_label": "תו מפריד מותאם אישית" - }, - "attachment_reminder": { - "label": "תזכורת קובץ מצורף", - "description": "הזהר לפני שליחה כאשר ההודעה שלך מתייחסת לקבצים מצורפים אך אף אחד לא צורף", - "keywords_label": "מילות חיוג הטריגר", - "keywords_description": "מילים או ביטויים המעוררים את התזכורת כאשר נמצאים בהודעה שלך", - "add_placeholder": "הוסף מילת חיוג…", - "add": "הוסף", - "remove": "הסר" - }, - "empty_subject_warning": { - "label": "אזהרה על נושא ריק", - "description": "בקש אישור לפני שליחת הודעה ללא נושא." - }, - "hide_inline_image_attachments": { - "label": "הסתר תמונות מוטבעות מקבצים מצורפים", - "description": "תמונות מוטבעות בגוף ההודעה אינן מופיעות כקבצים מצורפים נפרדים" - }, - "attachment_image_previews": { - "label": "הצג תצוגות מקדימות בתמונות בקבצים מצורפים", - "description": "עבור קבצי תמונה מצורפים כקלפי תמונה קטנה במקום סמלים כלליים של קובץ" - }, - "send_delay": { - "label": "בטל שליחה / השהיית שליחה", - "description": "השהה שליחות נורמליות בחלון בצד השרת הקצר.", - "off": "כבוי", - "seconds": "{seconds} שניות", - "unsupported": "החשבון הנוכחי אינו פרסום תמיכה בשליחה מעוכבת. ההגדרה עדיין שמורה לחשבונות אחרים." - }, - "return_to_list_after_action": { - "label": "חזור לרשימה לאחר מחיקה או סימון שלא נקראו", - "description": "לאחר מחיקה או סימון ההודעה הפתוחה כלא קרויה, חזור לרשימת ההודעות במקום לפתוח את ההודעה הבאה." - }, - "swipe_right_action": { - "label": "פעולת החלקה ימינה (נייד)", - "description": "במכשירי מגע, הפעולה שמתבצעת כשמחליקים הודעה ימינה ברשימה." - }, - "swipe_left_action": { - "label": "פעולת החלקה שמאלה (נייד)", - "description": "במכשירי מגע, הפעולה שמתבצעת כשמחליקים הודעה שמאלה ברשימה." - }, - "swipe_actions": { - "none": "ללא", - "archive": "העבר לארכיון", - "delete": "מחק", - "mark_read": "החלף מצב נקרא", - "star": "החלף כוכב", - "spam": "סמן כספאם" - }, - "rtl_editing": { - "label": "תמיכה בעריכה מימין לשמאל", - "description": "מוסיף כפתור כיווניות לסרגל הכלים של העורך כדי להגדיר פסקאות משמאל לימין או מימין לשמאל" - } - }, - "composer": { - "title": "עורך", - "description": "הגדר את הגדרות הרכב דוא\"ל", - "autosave": { - "label": "מרווח שמירה אוטומטית", - "description": "באיזו תדירות לשמור טיוטות באופן אוטומטי", - "30s": "כל 30 שניות", - "1m": "כל דקה", - "2m": "כל 2 דקות", - "5m": "כל 5 דקות" - }, - "send_confirmation": { - "label": "שלח אישור", - "description": "בקשו אישור לפני שליחת מיילים" - }, - "default_reply": { - "label": "מצב תגובה ברירת מחדל", - "description": "פעולת ברירת מחדל בעת לחיצה על השב", - "reply": "השב", - "reply_all": "השב לכולם" - } - }, - "privacy": { - "title": "פרטיות ואבטחה", - "description": "נהל את הגדרות הפרטיות והאבטחה שלך", - "external_images": { - "label": "חסום תמונות חיצוניות", - "description": "מנע מעקב דרך תמונות חיצוניות" - }, - "session_timeout": { - "label": "זמן קצוב להפעלה", - "description": "התנתק אוטומטית לאחר חוסר פעילות", - "never": "לעולם לא", - "30m": "30 דקות", - "1h": "שעה אחת", - "4h": "4 שעות" - }, - "clear_cache": { - "label": "נקה את המטמון", - "description": "הסר נתונים בקובץ שמור וקבצים זמניים", - "button": "נקה את המטמון", - "confirm": "האם אתה בטוח שברצונך לנקות את המטמון?", - "success": "המטמון נוקה בהצלחה" - } - }, - "account": { - "title": "חשבון", - "description": "הצג את פרטי החשבון שלך", - "name_label": "שם תצוגה", - "username_label": "שם משתמש", - "account_type_label": "סוג חשבון", - "auth_method_label": "אימות", - "auth_method_oauth": "כניסה יחידה (OAuth/OIDC)", - "auth_method_basic": "סִיסמָה", - "demo_account": "חשבון הדגמה", - "email": { - "label": "כתובת אימייל", - "value": "{email}" - }, - "server": { - "label": "שרת JMAP", - "value": "{server}" - }, - "storage": { - "label": "שימוש באחסון", - "used": "{used}של{total}בשימוש", - "percentage": "{percent}% בשימוש" - }, - "last_sync": { - "label": "סנכרון אחרון", - "value": "{time}" - }, - "accounts": { - "title": "חשבונות מחוברים", - "description": "גרור לסדר כיצד חשבונות מופיעים בתפריט הנפתח", - "active": "חשבון פעיל כרגע", - "default_badge": "חשבון ברירת מחדל", - "set_default": "הגדר כברירת מחדל", - "switch_to": "עבור לחשבון זה", - "move_up": "עבור למעלה", - "move_down": "עבור למטה", - "drag_handle": "גרור לסדר", - "add": "הוסף חשבון" - }, - "shared_accounts": { - "title": "משותף איתי", - "description": "חשבונות קבוצתיים ומשותפים שאתה יכול לנהל. בחר אחד כדי לערוך את המסננים, המגיב לחופשה, לוחות השנה והאנשים שלו.", - "shared_label": "חשבון משותף" - } - }, - "security": { - "title": "אבטחת חשבון", - "description": "נהל את הסיסמה שלך, אימות דו-שלבי והגדרות אבטחה", - "detecting": "מזהה יכולות שרת...", - "not_available": "ניהול אבטחת חשבון אינו זמין עבור שרת דואר זה. הרשאות נדרשות עשויות להיות מושבתות. עיין בתיעודלפרטים.", - "password": { - "title": "שנה סיסמה", - "current": "סיסמה נוכחית", - "new": "סיסמה חדשה", - "confirm": "אשר סיסמה חדשה", - "submit": "שנה סיסמה", - "success": "הסיסמה שונתה בהצלחה", - "error_title": "שינוי הסיסמה נכשל", - "error_mismatch": "סיסמאות חדשות אינן תואמות", - "error_min_length": "הסיסמה חייבת להיות לפחות 8 תווים", - "error_generic": "שינוי הסיסמה נכשל" - }, - "display_name": { - "label": "שם תצוגה", - "description": "השם שלך כפי שהוא מופיע בשרת", - "placeholder": "הזן את שם התצוגה שלך", - "save": "שמור", - "success": "שם התצוגה עודכן", - "error": "עדכון שם התצוגה נכשל" - }, - "totp": { - "section_title": "אימות דו-גורמי", - "label": "אימות TOTP", - "description": "הוסף שכבת אבטחה נוספת עם סיסמה חד פעמית מבוססת זמן", - "active": "מופעל", - "inactive": "מושבת", - "enabled": "אימות דו-גורמי מופעל", - "disabled": "אימות דו-גורמי מושבת", - "enable_error": "הפעלת 2FA נכשלה", - "disable_error": "נכשל השבתת 2FA", - "setup_instructions": "העתק את כתובת האתר הזו לאפליקציית המאמת שלך (Google Authenticator, Authy וכו'):", - "verification_code": "קוד אימות", - "confirm": "אשר", - "disable": "השבת", - "disable_confirm_prompt": "הזן את הסיסמה שלך כדי להשבית אימות דו־שלבי.", - "password_required": "נדרשת סיסמה", - "code_required": "נדרש קוד אימות", - "code_invalid": "קוד אימות לא חוקי. בדוק את יישום ההאימות שלך ונסה שוב." - }, - "app_passwords": { - "title": "סיסמאות לאפליקציה", - "description": "צור סיסמאות לאפליקציות שאינן תומכות באימות דו-שלבי", - "add": "לְהוֹסִיף", - "create": "צור", - "cancel": "לְבַטֵל", - "generate": "צור", - "name_label": "שם האפליקציה", - "name_placeholder": "למשל Thunderbird, אייפון מייל", - "password_label": "סיסמה (השאירו ריקה ליצירה אוטומטית)", - "password_placeholder": "נוצר אוטומטית אם ריק", - "added": "נוצרה סיסמת אפליקציה", - "removed": "סיסמת האפליקציה הוסרה", - "add_error": "יצירת סיסמת האפליקציה נכשלה", - "remove_error": "הסרת סיסמת האפליקציה נכשלה", - "none": "לא הוגדרו סיסמאות לאפליקציה", - "done": "בוצע", - "expires_label": "פג תוקף (אופציונלי)", - "allowed_ips_label": "כתובות IP מורשות (אופציונלי)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "מופרד בפסיקים או רווחים. השאר ריק כדי לאפשר כל IP.", - "copy_now_warning": "העתק סיסמה זו כעת - היא לא תיוצג שוב." - }, - "encryption": { - "section_title": "הצפנה במנוחה", - "label": "הצפנת דואר אלקטרוני", - "description": "הצפין מיילים מאוחסנים בשרת לפרטיות נוספת", - "active": "הצפנת{type}מופעלת", - "inactive": "מושבת", - "enabled": "הצפנה במנוחה מופעלת", - "disabled_success": "ההצפנה במנוחה מושבתת", - "error": "עדכון הגדרות ההצפנה נכשל", - "algorithm_label": "אלגוריתם הצפנה", - "encrypt_on_append": "הצפן הודעות חדשות בעת ההעלאה", - "allow_spam_training": "אפשר אימון זיהוי דואר זבל לפני הצפנת ההודעות", - "enable_encryption": "הפעל הצפנה", - "enable_tooltip": "הפעל הצפנה עם מפתח זה", - "configure_title": "הפעל הצפנה עם מפתח זה", - "enable_button": "הפעל", - "disable_tooltip": "השבת הצפנה", - "enabled_success": "הצפנה במנוחה מופעלת" - }, - "email_client": { - "title": "הגדרת לקוח אימייל", - "description": "השתמש באישורים אלה כדי להגדיר את לקוח הדוא\"ל של שולחן העבודה או הנייד שלך (Thunderbird, Apple Mail, Outlook וכו')", - "jmap_username_label": "שם משתמש JMAP", - "copy": "העתק", - "copied": "הועתק", - "password_instructions": "השתמש בשם המשתמש שלך ב-JMAP למעלה יחד עם סיסמת אפליקציה כדי להיכנס ללקוח הדוא\"ל שלך. צור סיסמת אפליקציה בסעיף למעלה אם עדיין לא עשית זאת." - }, - "api_keys": { - "title": "מפתחות API", - "description": "צור מפתחות API עבור סקריפטים ואינטגרציות המדברות ישירות לשרת", - "name_label": "שם מפתח", - "name_placeholder": "למשל סקריפט גיבוי, פעיל CI", - "copy_now_warning": "העתק מפתח API זה כעת - הוא לא יוצג שוב.", - "added": "מפתח API נוצר", - "removed": "מפתח API הוסר", - "add_error": "נכשל ביצירת מפתח API", - "remove_error": "נכשל בהסרת מפתח API", - "none": "אין מפתחות API מוגדרים" - }, - "link_device": { - "title": "הקשר יישום נייד", - "description": "היכנס ליישום הנייד Bulwark Mail ללא הקלדת דבר. צור קוד QR כאן וסרוק אותו מהמסך ההתחברות של היישום.", - "generate": "הצג קוד QR", - "regenerate": "הצג קוד חדש", - "instructions": "פתח את אפליקציית Bulwark Mail, לחץ ״סרוק קוד QR״ במסך ההתחברות, וכיוון את המצלמה שלך לכאן.", - "expires_in": "קוד זה פוקע בעוד {seconds} שניות. ניתן להשתמש בו רק פעם אחת.", - "expired": "קוד זה פקע.", - "generating": "יצירה…", - "error": "לא היה ניתן ליצור קוד זיווג. אנא נסה שוב." - }, - "public_keys": { - "name_label": "שם המפתח הציבורי", - "name_placeholder": "לדוגמה: המפתח שלי", - "key_label": "מפתח ציבורי (S/MIME או PGP בפורמט ASCII)", - "key_placeholder": "הדבק כאן את המפתח הציבורי שלך", - "none": "לא הוגדרו מפתחות ציבוריים" - } - }, - "identities": { - "title": "שליחת זהויות", - "description": "נהל כתובות דוא\"ל שמהן תוכל לשלוח", - "identities_count": { - "label": "הזהויות שלך", - "description": "כתובות דוא\"ל מוגדרות לשליחה", - "count_zero": "אין זהויות", - "count_one": "1 זהות", - "count_other": "זהויות{count}" - }, - "manage": "ניהול זהויות", - "sub_addressing": { - "label": "כתובת משנה", - "description": "השתמש בתגים כמוuser+tag@domain.comכדי לארגן דואר נכנס", - "learn_more": "למידע נוסף" - } - }, - "vacation": { - "title": "משיב נופש", - "description": "הגב באופן אוטומטי להודעות דוא״ל נכנסות בזמן שאתה רחוק", - "loading": "טוען הגדרות חופשה...", - "not_supported": "שרת הדואר שלך אינו תומך בתגובות חופשה.", - "fetch_error": "טעינת הגדרות החופשה נכשלה. אנא נסה שוב.", - "status": { - "label": "משיב נופש", - "description": "שלח תשובה אוטומטית לאנשים ששלחו לך דוא\"ל", - "active": "פעיל", - "inactive": "לא פעיל" - }, - "date_range": { - "title": "טווח תאריכים", - "description": "אפשר להגביל את המענה האוטומטי לתקופה מסוימת", - "start": "תאריך התחלה", - "start_description": "השאר ריק ללא מגבלת התחלה", - "end": "תאריך סיום", - "end_description": "השאר ריק ללא הגבלה" - }, - "message": { - "title": "הודעת תשובה אוטומטית", - "description": "ההודעה שתישלח כתשובה", - "subject_label": "נושא", - "subject_description": "שורת הנושא של התשובה האוטומטית", - "subject_placeholder": "מחוץ למשרד", - "body_label": "גוף ההודעה", - "body_description": "תוכן הודעת טקסט רגילה", - "body_placeholder": "תודה על המייל שלך. אני כרגע מחוץ למשרד ואענה כשאחזור.", - "html_label": "הודעה מעוצבת (HTML)", - "html_description": "הוסיפו גרסה מעוצבת עם קישורים וסגנון. נמענים שתוכנת הדוא״ל שלהם אינה יכולה להציג אותה יראו את הטקסט הרגיל שלמעלה.", - "html_placeholder": "כתבו תשובת היעדרות מעוצבת…" - }, - "preview": { - "title": "תצוגה מקדימה", - "show": "הצג תצוגה מקדימה", - "hide": "הסתר תצוגה מקדימה" - }, - "save": "שמור שינויים", - "saving": "שומר...", - "warnings": { - "end_before_start": "תאריך הסיום חייב להיות אחרי תאריך ההתחלה", - "start_in_past": "תאריך ההתחלה הוא בעבר", - "empty_body": "גוף ההודעה ריק - הנמענים יקבלו תשובה ריקה", - "subject_too_long": "הנושא ארוך מדי עבור השרת (לכל היותר {max} בתים).", - "body_too_long": "ההודעה ארוכה מדי עבור השרת (לכל היותר {max} בתים, כולל עיצוב)." - } - }, - "folders": { - "title": "תיקיות", - "description": "נהל את תיקיות הדוא\"ל שלך והקצה תפקידים סטנדרטיים", - "folder_list": "התיקיות שלך", - "folder_list_description": "לחץ על סמל תיקיה כדי להתאים אותו", - "standard_roles": "תפקידי תיקייה סטנדרטיים", - "standard_roles_description": "הקצה אילו תיקיות משמשות לתפקידי תיבת דואר סטנדרטיים כמו Inbox, Sent, Trash וכו׳.", - "role_inbox": "תיבת דואר נכנס", - "role_drafts": "טיוטות", - "role_sent": "נשלח", - "role_trash": "אַשׁפָּה", - "role_junk": "ספאם / זבל", - "role_archive": "ארכיון", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "אין", - "create_folder": "צור תיקיה", - "create_subfolder": "צור תיקיית משנה", - "reorder": "גרור לשינוי הסדר", - "reorder_error": "שינוי סדר התיקיות נכשל", - "move_error": "העברת התיקייה נכשלה", - "subfolder_of": "בתוך{name}", - "subfolder_name": "שם תיקיית משנה", - "new_folder_name": "שם התיקיה", - "rename": "שנה שם", - "change_icon": "שנה סמל", - "delete": "לִמְחוֹק", - "confirm_delete": "האם אתה בטוח שברצונך למחוק את \"{name}\"? הודעות אימייל בתיקייה זו יועברו לאשפה.", - "create": "צור", - "cancel": "לְבַטֵל", - "no_folders": "אין תיקיות מותאמות אישית", - "cannot_delete_role": "לא ניתן למחוק תיקיה עם תפקיד סטנדרטי. Remove the role first.", - "folder_created": "התיקיה נוצרה", - "folder_renamed": "שם התיקיה שונה", - "folder_deleted": "התיקיה נמחקה", - "role_updated": "תפקיד התיקיה עודכן", - "error_create": "יצירת התיקיה נכשלה", - "error_rename": "שינוי שם התיקייה נכשל", - "error_delete": "מחיקת התיקייה נכשלה", - "error_delete_has_children": "לא ניתן למחוק תיקייה: היא עדיין מכילה תיקיות משנה. מחק או העבר אותם תחילה.", - "error_delete_has_email": "לא ניתן למחוק תיקייה: היא עדיין מכילה הודעות דוא\"ל. העבר או מחק אותם תחילה.", - "error_role": "עדכון תפקיד התיקייה נכשל", - "role_memos": "הערות" - }, - "advanced": { - "title": "מתקדם", - "description": "אפשרויות מתקדמות והגדרות מפתח", - "debug_mode": { - "label": "מצב ניפוי באגים", - "description": "אפשר רישום מפורט לצורך פתרון בעיות" - }, - "debug_categories": { - "description": "בחר אילו קטגוריות לרשום. השבת קטגוריות שאינך צריך כדי להפחית את רעש המסוף.", - "jmap": "לקוח JMAP", - "jmap_description": "פעולות תיבת דואר, שליפת דואר אלקטרוני ובקשות פרוטוקול JMAP", - "calendar": "לוּחַ שָׁנָה", - "calendar_description": "אירועי לוח שנה, ייבוא ותזמון הודעות", - "tasks": "משימות", - "tasks_description": "יצירה, אחזור ועדכונים של משימות יומן", - "auth": "אימות", - "auth_description": "כניסה, TOTP, החלפת אסימונים וניהול הפעלה", - "filters": "מסננים", - "filters_description": "כללי סינון ותסריטי חופשה", - "email": "צפייה באימייל", - "email_description": "עיבוד דוא\"ל, עיבוד TNEF וסימון כקריאה", - "push": "הודעות דחיפה", - "push_description": "הגדרה ומשלוח של התראות בדחיפה", - "contacts": "אנשי קשר וספרי כתובות", - "contacts_description": "סנכרון אנשי קשר, פעולות ספר כתובות ושולחים מהימנים" - }, - "settings_sync": { - "label": "סנכרון הגדרות", - "description": "סנכרן את ההגדרות שלך בין דפדפנים ומכשירים" - }, - "sender_favicons": { - "label": "סמלים של השולח (ניסיוני)", - "description": "הצג סמלי אתרים כתמונות פרופיל עבור שולחים עסקיים" - }, - "sender_avatars": {}, - "keyboard_shortcuts": { - "label": "קיצורי מקלדת", - "description": "הצג קיצורי מקשים זמינים", - "button": "הצג קיצורי דרך" - }, - "refresh_cache": { - "label": "רענון נתונים שמורים", - "description": "טעינה מחדש של אנשי קשר, לוחות שנה ותיקיות מהשרת. שומר על החשבונות וההתחברויות שלך — מתקן תצוגה מיושנת או שגויה ללא התנתקות.", - "button": "רענון" - }, - "reset_settings": { - "label": "אפס את ההגדרות", - "description": "שחזר את כל ההגדרות לערכי ברירת המחדל", - "button": "אפס לברירות מחדל" - }, - "export_settings": { - "label": "ייצוא הגדרות", - "description": "הורד את ההגדרות שלך בתור JSON", - "button": "ייצוא" - }, - "import_settings": { - "label": "הגדרות ייבוא", - "description": "העלה הגדרות מקובץ JSON", - "button": "ייבוא" - }, - "about": { - "title": "Bulwark Webmail" - }, - "show_avatars_in_junk": { - "label": "הצג אווטרים בתיקייה זבל", - "description": "הצג תמונות פרופיל וייקוני אתרים עבור שולחים בתיקייה זבל. מושבת כברירת מחדל כדי להימנע מהענקת לגיטימציה חזותית לניסיונות דיוג." - } - }, - "sidebar_apps": { - "title": "אפליקציות בסרגל הצד", - "description": "נהל אפליקציות וקישורים מותאמים אישית בסרגל הצד שלך", - "keep_loaded": "השאר אפליקציות טעונות", - "keep_loaded_description": "השאר אפליקציות מוטבעות פועלות ברקע בעת המעבר ביניהן כדי למנוע טעינה מחדש", - "managed_title": "סופקו על ידי מנהל המערכת", - "managed_description": "אפליקציות אלו מוגדרות עבור כולם ולא ניתן לשנות אותן.", - "manage_title": "אפליקציות מותאמות אישית", - "manage_description": "הוסף, ערוך או הסר אפליקציות מותאמות אישית מהסרגל הצדדי שלך" - }, - "contacts": { - "title": "אנשי קשר", - "description": "ייבא וייצא את אנשי הקשר שלך", - "import_label": "ייבוא אנשי קשר", - "import_description": "ייבא אנשי קשר מקובץ vCard (.vcf).", - "export_label": "ייצוא אנשי קשר", - "export_description": "ייצא את כל אנשי הקשר כקובץ vCard (.vcf).", - "manage_title": "ספרי כתובות", - "manage_description": "שנה את שם פנקסי הכתובות שלך", - "no_address_books": "לא נמצאו פנקסי כתובות", - "categories_title": "קטגוריות", - "categories_description": "שנה שם של קטגוריות אנשי קשר", - "no_categories": "לא נמצאו קטגוריות", - "group_by_letter_label": "קבוצה לפי אות ראשונה", - "sort_by_last_name_label": "מיון לפי שם משפחה", - "sort_by_last_name_description": "סידור רשימת אנשי הקשר לפי שם משפחה כך שבני אותה משפחה יופיעו יחד", - "group_by_letter_description": "הצג כותרות חלקים אלפביתיים ברשימת אנשי הקשר" - }, - "filters": { - "title": "מסנני דואר אלקטרוני", - "description": "צור כללים למיון, תיוג וניהול אוטומטי של הודעות דוא\"ל נכנסות", - "add_rule": "הוסף כלל", - "no_rules": "אין חוקי סינון", - "no_rules_description": "צור כללים לארגון אוטומטי של הודעות האימייל הנכנסות שלך", - "vacation_active": "Vacation Responder פעיל", - "vacation_active_description": "תשובה אוטומטית מופעלת עבור הודעות נכנסות", - "vacation_configure": "הגדר", - "edit_rule": "ערוך כלל", - "new_rule": "כלל חדש", - "delete_rule": "מחק כלל", - "delete_confirm": "האם אתה בטוח שברצונך למחוק את הכלל הזה?", - "enable": "אפשר", - "disable": "השבת", - "raw_editor": "Raw Sieve עורך", - "raw_editor_warning": "עריכת הסקריפט הגולמי של Sieve עלולה לשבור את עריכת הכללים החזותיים. שינויים שבוצעו כאן עוקפים את הבונה החזותי.", - "validate": "אימות", - "validation_success": "הסקריפט תקף", - "validation_error": "בסקריפט יש שגיאות", - "save": "שמור כללים", - "saving": "שומר...", - "saved": "המסננים נשמרו בהצלחה", - "save_failed": "שמירת המסננים נכשלה", - "loading": "טוען מסננים...", - "not_supported": "שרת הדואר שלך אינו תומך במסנני דואר אלקטרוני.", - "rule_name": "שם כלל", - "rule_name_placeholder": "למשל, מיון ניוזלטרים", - "match_all": "התאם את כל התנאים", - "match_any": "התאם לכל תנאי", - "conditions": "תנאים", - "add_condition": "הוסף מצב", - "actions": "פעולות", - "add_action": "הוסף פעולה", - "stop_processing": "הפסק לעבד את הכללים הבאים", - "include_spam": "להעביר גם הודעות שסומנו כדואר זבל", - "condition_fields": { - "from": "מ", - "to": "ל", - "cc": "עותק", - "subject": "נושא", - "header": "כותרת מותאמת אישית", - "size": "גודל", - "body": "גוף", - "attachment": "קובץ מצורף" - }, - "comparators": { - "contains": "מכיל", - "not_contains": "אינו מכיל", - "is": "הוא בדיוק", - "not_is": "אינו", - "starts_with": "מתחיל עם", - "ends_with": "מסתיים ב", - "matches": "תואם דפוס", - "greater_than": "גדול מ", - "less_than": "הוא פחות מ", - "has_any": "קיים", - "has_type": "מסוג" - }, - "action_types": { - "move": "העבר לתיקיה", - "copy": "העתק לתיקיה", - "forward": "קדימה ל", - "mark_read": "סמן כנקרא", - "star": "הודעת כוכב", - "add_label": "הוסף תווית", - "discard": "מחק (מחק בשקט)", - "reject": "דחה עם הודעה", - "keep": "שמור בתיבת הדואר הנכנס", - "stop": "תפסיק לעבד" - }, - "move_to_folder": "בחר תיקייה", - "copy_to_folder": "בחר תיקייה", - "forward_to": "העבר לכתובת המייל", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "שמור עותק", - "forward_limit": "מגבלת העברות לכל הודעה בשרת זה: {count}. העברות נוספות מדולגות.", - "reject_message": "הודעת דחייה", - "reject_placeholder": "האימייל שלך נדחה", - "label_name": "שם התווית", - "label_placeholder": "למשל, חשוב", - "header_name": "שם הכותרת", - "header_placeholder": "למשל, X-Mailing-list", - "size_bytes": "גודל בבתים", - "size_placeholder": "למשל, 1000000", - "system_managed": "כלל מנוהל מערכת", - "opaque_warning": "סקריפט זה נערך מחוץ לבונה החזותי. רק עריכת Sieve גולמית זמינה.", - "open_sieve_editor": "פתח את עורך Sieve הגולמי", - "fetch_error": "טעינת המסננים נכשלה", - "expanded_view": "תצוגה מורחבת", - "expanded_view_description": "הצג כללי סינון עם תנאים מפורטים ואבני פעולה", - "if": "אם", - "then": "ואז", - "match_all_conditions": "כולם תואמים", - "match_any_condition": "כל התאמות", - "and": "ו", - "or": "אוֹ", - "cancel": "לְבַטֵל", - "confirm_delete": "לִמְחוֹק", - "rule_list": "כללי סינון", - "drag_to_reorder": "גרור כדי לסדר מחדש", - "match_type": "סוג התאמה", - "reset_to_visual": "איפוס לבונה חזותי", - "reset_warning": "פעולה זו תמחק את הסקריפט הנוכחי ויתחיל מחדש.", - "confirm_reset": "אִתחוּל", - "validation_empty_name": "נדרש שם כלל", - "validation_empty_conditions": "נדרש לפחות תנאי אחד עם ערך", - "validation_empty_actions": "נדרשת לפחות פעולה אחת", - "templates_section": "התחל מהתבנית", - "template_newsletters": "העבר ניוזלטרים לתיקיה", - "template_receipts": "העבר קבלות לארכיון אוטומטי", - "template_important": "סמן אימיילים חשובים", - "template_notifications": "סינון הודעות", - "sieve_editor": { - "title": "עורך תסריט מסננת", - "warning": "עריכת הסקריפט הגולמי של Sieve עלולה לשבור את עריכת הכללים החזותיים. שינויים שבוצעו כאן עוקפים את הבונה החזותי.", - "script_content": "תסריט מסננת", - "valid": "הסקריפט תקף", - "invalid": "בסקריפט יש שגיאות", - "save_warning": "שמירה תחליף את כל הכללים החזותיים. לא ניתן לבטל זאת. לחץ שוב על שמור כדי לאשר.", - "validating": "מאמת...", - "validate": "אימות", - "cancel": "לְבַטֵל", - "save": "שמור", - "confirm_save": "אשר את השמירה", - "validation_failed": "בקשת האימות נכשלה" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {תנאי אחד} other {# תנאים}}", - "actions_count": "{count, plural, one {פעולה אחת} other {# פעולות}}" - }, - "attachment_type_placeholder": "למשל pdf, doc, jpg", - "value_placeholder_multi": "ערך (מרובים מופרדים בפסיקים)", - "origin_external": "חיצוני", - "managed_by_tooltip": "מנוהל על ידי {source}. ערוך אותו ביישום זה, או השתמש בעורך Sieve הגולמי." - }, - "templates": { - "title": "תבניות דואר אלקטרוני", - "description": "צור תבניות דוא\"ל הניתנות לשימוש חוזר עם משתני מיקום", - "add": "תבנית חדשה", - "edit": "ערוך תבנית", - "name": "שם התבנית", - "name_placeholder": "למשל, דוא\"ל מעקב", - "category": "קטגוריה", - "category_placeholder": "למשל, עבודה, אישי", - "subject": "נושא", - "subject_placeholder": "שורת הנושא באימייל", - "body": "גוף", - "body_placeholder": "תוכן גוף בדוא\"ל...", - "recipients_placeholder": "email@example.com", - "identity": "שלח בשם", - "default_identity": "זהות ברירת מחדל", - "favorite": "מועדף", - "cancel": "לְבַטֵל", - "create": "צור", - "update": "לְעַדְכֵּן", - "confirm_delete": "לִמְחוֹק", - "no_templates": "עדיין אין תבניות", - "manage": "נהל תבניות", - "count": "{count, plural, one {תבנית אחת} other {# תבניות}}", - "export_import": "ייצוא וייבוא", - "export_import_description": "גבה את התבניות שלך או העבר אותן למכשיר אחר", - "export": "ייצוא", - "import": "ייבוא", - "validation": { - "empty": "נדרש שם תבנית", - "too_long": "שם התבנית חייב להכיל 200 תווים או פחות" - } - }, - "files": { - "display": { - "title": "תצוגה", - "description": "קבע את אופן הצגת הקבצים והתיקיות" - }, - "folder_layout": { - "label": "ניווט תיקיות", - "description": "בחר כיצד יוצגו תיקיות: בשורה עם קבצים או בעץ סרגל צד", - "inline": "מוטבע", - "sidebar": "סרגל צד" - }, - "default_view": { - "label": "תצוגת ברירת מחדל", - "description": "בחר בין פריסת רשת לרשימה", - "list": "רשימה", - "grid": "רשת" - }, - "default_sort": { - "label": "מיון ברירת מחדל", - "description": "בחר את מיון ברירת המחדל עבור קבצים", - "name": "שֵׁם", - "size": "גודל", - "modified": "שונה" - }, - "sort_direction": { - "label": "כיוון מיון", - "description": "בחר בסדר עולה או יורד", - "ascending": "עולה", - "descending": "יורד" - }, - "icons": { - "title": "אייקונים", - "description": "הגדר את מראה סמל הקובץ" - }, - "show_icons": { - "label": "הצג סמלי קבצים", - "description": "הצג סמלים לצד קבצים ותיקיות" - }, - "colored_icons": { - "label": "סמלים צבעוניים", - "description": "השתמש בסמלים צבעוניים במקום מונוכרום" - }, - "show_thumbnails": { - "label": "הצג תמונות ממוזערות", - "description": "הצג תצוגות מקדימות של תמונה במקום סמלים עבור קובצי תמונה" - }, - "behavior": { - "title": "התנהגות", - "description": "הגדר את התנהגות דפדפן הקבצים" - }, - "show_hidden": { - "label": "הצג קבצים מוסתרים", - "description": "הצג קבצים ותיקיות שמתחילים בנקודה" - }, - "preview": { - "label": "תצוגה מקדימה" - } - }, - "search_placeholder": "חפש בהגדרות", - "search_clear": "נקה חיפוש", - "search_no_results": "לא נמצאו הגדרות תואמות", - "scoped": { - "back": "חזור לחשבוני", - "managing": "ניהול: {name}" - }, - "downloads": { - "title": "הורדות", - "description": "התאם אישית כיצד נקראות הודעות דוא״ל והורדות מקבצים מצורפים.", - "reset": "שחזר ברירת מחדל", - "preview": "תצוגה מקדימה:", - "email_template": { - "label": "שם קובץ דוא״ל (.eml)", - "description": "תבנית המשמשת כאשר אתה ייצוא הודעה דוא״ל או גרור אחת לצורה של קובץ. הסיומת .eml מתוספת באופן אוטומטי." - }, - "attachment_template": { - "label": "שם קובץ מצורף", - "description": "תבנית המשמשת בעת הורדה או גרירת קובץ מצורף. אם תשמיט את ׳{filename}׳ או ׳{ext}׳, הסיומת המקורית נשמרת." - }, - "bundle_template": { - "label": "שם קובץ .zip מרובה-דוא״ל", - "description": "תבנית המשמשת כאשר אתה גורר או מורד כמה הודעות דוא״ל שנבחרו כזיפ יחיד. הסיומת .zip מתוספת באופן אוטומטי." - }, - "spaces": { - "label": "רווחים", - "description": "החלף רווחים בשם הקובץ המתקבל בתו אחר.", - "keep": "שמור רווחים", - "underscore": "החלף בעזר קו תחתי", - "dash": "החלף בעזר מקף" - }, - "lowercase": { - "label": "אותיות קטנות", - "description": "הכוח את כל שם הקובץ לאותיות קטנות." - }, - "strip_diacritics": { - "label": "הסר הנקודות", - "description": "המר אותיות עם דיאקריטיקה לשקולים ASCII שלהם (ä → a, é → e). שימושי לכלים שמטבעו הורגים שמות קבצים Unicode." - }, - "collapse_separators": { - "label": "צמצם מפרידים חוזרים", - "description": "צמצם רצפים של רווחים, קווים תחתיים או מקפים לתו אחד." - }, - "after_export": { - "label": "אחרי ייצוא", - "description": "בחר סלקטיבי להעביר את הדוא״ל לאחר ייצואו כ .eml.", - "keep": "שמור בתיבת דואר", - "archive": "העבר לארכיון", - "trash": "העבר לאשפה" - } - } - }, - "errors": { - "page_error_title": "משהו השתבש", - "page_error_description": "נתקלנו בשגיאה בלתי צפויה. אנא נסה שוב או חזור לדף הבית.", - "sidebar_error": "לא ניתן לטעון תיבות דואר", - "email_list_error": "לא ניתן לטעון מיילים", - "viewer_error_title": "לא ניתן להציג אימייל", - "viewer_error_description": "הייתה בעיה בעיבוד האימייל הזה. הוא עשוי להכיל תוכן שאינו נתמך.", - "composer_error": "לא ניתן לטעון את העורך", - "settings_error_title": "ההגדרות לא זמינות", - "settings_error_description": "לא ניתן לטעון הגדרות. ייתכן שההעדפות שלך לא יישמרו.", - "try_again": "נסה שוב", - "reload": "טען מחדש", - "reload_emails": "טען מחדש מיילים", - "reload_settings": "טען מחדש את ההגדרות", - "retry": "נסה שוב", - "go_home": "עבור לתיבת הדואר הנכנס" - }, - "context_menu": { - "reply": "השב", - "reply_all": "השב לכולם", - "forward": "קדימה", - "mark_read": "סמן כקריאה", - "mark_unread": "סמן כלא נקרא", - "star": "כוכב", - "unstar": "הסר כוכב", - "pin": "הצמד", - "unpin": "בטל הצמדה", - "move_to": "העבר ל...", - "archive": "ארכיון", - "delete": "לִמְחוֹק", - "mark_as_spam": "דווח על ספאם", - "not_spam": "לא ספאם", - "tag": "תווית", - "items_selected": "נבחרו הודעות דוא\"ל מסוג{count}", - "edit_draft": "ערוך טיוטה", - "cancel_scheduled_send": "ביטול שליחה", - "reschedule_send": "תזמן מחדש", - "cancel_and_edit": "בטל וערוך", - "cancel_and_compose_again": "בטל והרכיב שוב" - }, - "shortcuts": { - "title": "קיצורי מקלדת", - "tip": "לחץ על? בכל עת כדי להראות את העזרה הזו", - "sections": { - "navigation": "ניווט", - "actions": "פעולות אימייל", - "global": "גלובלי", - "threads": "שרשורים", - "composer": "עורך" - }, - "navigation": { - "next_email": "המייל הבא", - "previous_email": "אימייל קודם", - "open_email": "פתח אימייל", - "close_email": "סגור / בטל בחירה" - }, - "actions": { - "reply": "השב", - "reply_all": "השב לכולם", - "forward": "קדימה", - "star": "החלף כוכב", - "archive": "ארכיון", - "delete": "לִמְחוֹק", - "mark_unread": "סמן כלא נקרא", - "mark_read": "סמן כנקרא", - "toggle_spam": "דווח על ספאם / לא ספאם" - }, - "global": { - "compose": "כתוב אימייל חדש", - "search": "התמקד בחיפוש", - "help": "הצג קיצורי דרך", - "refresh": "רענן מיילים", - "select_all": "בחר הכל", - "global_search": "חיפוש בכל מקום" - }, - "threads": { - "expand_collapse": "הרחב/כווץ את השרשור" - }, - "composer": { - "template_picker": "פתח את בוחר התבניות", - "send": "שלח דוא״ל", - "schedule_send": "תזמן שליחה" - } - }, - "threads": { - "messages_one": "הודעת{count}", - "messages_other": "הודעות{count}", - "messages_tooltip": "{count, plural, one {הודעה אחת בשיחה זו} other {# הודעות בשיחה זו}}", - "expand": "הרחב את השיחה", - "collapse": "כווץ השיחה", - "loading": "טוען שיחה...", - "mark_read": "סמן את השיחה כקריאה", - "mark_unread": "סמן את השיחה כלא נקראה", - "archive": "שיחת ארכיון", - "delete": "מחק את השיחה", - "star": "שיחת כוכב", - "unstar": "הסר שיחה בכוכב", - "toggle_thread": "החלף חוט" - }, - "identities": { - "modal_title": "ניהול זהויות שליחה", - "create_new": "צור זהות חדשה", - "edit_identity": "ערוך זהות", - "delete_confirm": "למחוק את הזהות הזו? לא ניתן לבטל זאת.", - "cannot_delete": "לא ניתן למחוק את הזהות הזו", - "primary_identity": "ראשוני", - "set_as_primary": "הגדר כראשי", - "no_identities": "לא נמצאו זהויות", - "display": { - "reply_to": "תגובה ל:", - "bcc": "עותק נסתר:", - "signature": "חתימה:", - "preview": "תצוגה מקדימה:" - }, - "validation_errors": { - "invalid_emails": "הודעות אימייל לא חוקיות:{emails}", - "unknown_error": "שגיאה לא ידועה" - }, - "form": { - "name_label": "שם תצוגה", - "name_placeholder": "למשל, אימייל לעבודה, אישי", - "name_required": "נדרש שם", - "email_label": "כתובת אימייל", - "email_placeholder": "your.email@example.com", - "email_required": "יש צורך באימייל", - "email_invalid": "נא להזין כתובת אימייל חוקית", - "email_immutable": "לא ניתן לשנות כתובת אימייל לאחר היצירה", - "reply_to_label": "תגובה ל (אופציונלי)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "עותק מוסתר אוטומטי (אופציונלי)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "חתימת טקסט", - "html_signature_label": "חתימת HTML", - "save": "שמור זהות", - "cancel": "לְבַטֵל", - "creating": "יוצר...", - "updating": "מעדכן...", - "signature_byte_counter": "{bytes} / {max} בתים", - "signature_byte_limit_reached": "הגבול של השרת הושג" - }, - "sub_address": { - "button_tooltip": "השתמש בכתובת משנה", - "popover_title": "הוסף תג כתובת משנה", - "tag_input_placeholder": "הזן תג (למשל, קניות)", - "preview_label": "תצוגה מקדימה:", - "recent_tags": "תגים אחרונים", - "suggested_tags": "מוצע", - "use_address": "השתמש בכתובת זו", - "invalid_tag": "התג חייב להיות אלפאנומרי ומקפים בלבד", - "tag_too_long": "התג חייב להכיל 30 תווים או פחות", - "help_text": "הודעות דוא\"ל שנשלחות אלuser+tag@domain.comיגיעו לתיבת הדואר הנכנס שלך", - "validation": { - "empty": "התג לא יכול להיות ריק", - "too_long": "התג חייב להיות{max}תווים או פחות", - "invalid_chars": "התג חייב להכיל רק אותיות, מספרים ומקפים" - } - }, - "badge": { - "sent_via": "דרך", - "sub_address_tag": "נשלח באמצעות כתובת משנה:{tag}", - "identity_name": "נשלח באמצעות זהות:{name}", - "identity_short": "דרך{name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "לִמְחוֹק", - "delete_confirm_title": "מחק זהות" - }, - "templates": { - "picker_title": "בחר תבנית", - "search_placeholder": "חפש תבניות...", - "section_favorites": "מועדפים", - "section_recent": "לאחרונה", - "section_uncategorized": "אחר", - "no_templates": "עדיין אין תבניות", - "no_results": "לא נמצאו תבניות", - "fill_placeholders": "מלא ערכי מציין מיקום", - "enter_value": "הזן ערך...", - "preview": "תצוגה מקדימה", - "insert_with_values": "הוסף עם ערכים", - "insert_raw": "הכנס Raw", - "copy_suffix": "(העתק)", - "placeholder": "משתנה", - "placeholders": { - "recipient_name": "שם הנמען", - "company": "שם החברה", - "date": "תאריך נוכחי", - "day_of_week": "יום בשבוע", - "sender_name": "השם שלך" - } - }, - "contacts": { - "title": "אנשי קשר", - "search_placeholder": "חפש אנשי קשר...", - "create_new": "איש קשר חדש", - "no_category": "אין קטגוריה", - "rename_category": "שנה את שם הקטגוריה", - "category_name_label": "שם קטגוריה", - "category_renamed": "שם קטגוריה שונה", - "category_rename_failed": "שינוי שם הקטגוריה נכשל", - "category_added": "איש קשר נוסף ל-{name}", - "category_added_plural": "אנשי הקשר{count}נוספו ל-{name}", - "empty_state": "עדיין אין אנשי קשר", - "empty_state_title": "עדיין אין אנשי קשר", - "empty_state_subtitle": "צור את איש הקשר הראשון שלך או ייבא מקובץ vCard", - "empty_search": "אין אנשי קשר שתואמים לחיפוש שלך", - "empty_search_hint": "נסה מונח חיפוש אחר", - "clear_search": "נקה חיפוש", - "import_vcard": "ייבוא vCard", - "delete_confirm_title": "מחק איש קשר", - "delete_confirm": "האם אתה בטוח שברצונך למחוק איש קשר זה?", - "local_mode": "אנשי הקשר מאוחסנים באופן מקומי (שרת אינו תומך באנשי קשר ב-JMAP)", - "back_to_contacts": "חזור לאנשי קשר", - "tabs": { - "all": "כֹּל", - "groups": "קבוצות" - }, - "shared": { - "title": "משותף" - }, - "address_books": { - "title": "ספרי הכתובות שלי", - "shared_prefix": "משותף:{name}", - "moved": "איש הקשר הועבר אל{name}", - "moved_plural": "אנשי הקשר של{count}הועברו ל-{name}", - "move_failed": "העברת איש הקשר נכשלה", - "address_book": "ספר כתובות", - "rename": "שנה את שם ספר הכתובות", - "name_label": "שם ספר הכתובות", - "renamed": "שם ספר הכתובות שונה", - "rename_failed": "שינוי שם פנקס הכתובות נכשל", - "default": "ברירת מחדל", - "set_default": "הגדר כברירת מחדל", - "default_updated": "פנקס הכתובות ברירת המחדל עודכן", - "set_default_failed": "נכשל בהגדרת פנקס כתובות ברירת מחדל", - "manage": "ניהול פנקסי כתובות", - "create": "ספר כתובות חדש", - "created": "ספר כתובות נוצר", - "create_failed": "נכשל ביצירת ספר כתובות", - "share": "שתף ספר כתובות", - "new_contact_in_book": "איש קשר חדש בספר כתובות זה", - "delete": "מחק ספר כתובות", - "confirm_delete": "מחק \"{name}\"? כל אנשי הקשר בספר כתובות זה יוסרו.", - "deleted": "ספר כתובות נמחק", - "delete_failed": "נכשל במחיקת ספר כתובות" - }, - "detail": { - "emails": "כתובות אימייל", - "phones": "מספרי טלפון", - "organizations": "ארגונים", - "addresses": "כתובות", - "notes": "הערות", - "titles": "כותרות ותפקידים", - "online_services": "שירותים מקוונים", - "anniversaries": "ימי נישואין", - "personal_info": "מידע אישי", - "languages": "שפות", - "categories": "קטגוריות", - "related_contacts": "אנשי קשר קשורים", - "crypto_keys": "מפתחות קריפטו", - "cert_issuer": "מנפיק", - "cert_expires": "פג תוקף", - "cert_expired": "פג תוקף", - "cert_fingerprint": "טביעת אצבע", - "cert_algorithm": "אלגוריתם", - "import_to_smime": "ייבוא ל-S/MIME", - "cert_already_imported": "כבר מיובא ל-S/MIME", - "cert_imported": "אישור מיובא לחנות S/MIME", - "cert_import_failed": "ייבוא האישור נכשל", - "no_contact_selected": "בחר איש קשר כדי להציג פרטים", - "compose_email": "כתוב אימייל", - "copy_email": "העתק אימייל", - "copy_phone": "העתק את מספר הטלפון", - "copy_url": "העתק כתובת URL", - "copied": "הועתק ללוח", - "copy_failed": "ההעתקה ללוח נכשלה", - "created": "נוצר", - "updated": "עודכן לאחרונה", - "timezone": "אזור זמן", - "anniversary_birth": "יום הולדת", - "anniversary_death": "נפטר", - "anniversary_wedding": "יום נישואין", - "anniversary_other": "אחר", - "personal_expertise": "מומחיות", - "personal_hobby": "תחביב", - "personal_interest": "עניין", - "personal_other": "אחר", - "gender": "מגדר", - "gender_masculine": "זכר", - "gender_feminine": "נקבה", - "gender_other": "אחר", - "gender_none": "לא ישים", - "gender_unknown": "לא ידוע", - "calendar": "לוּחַ שָׁנָה", - "calendar_uri": "כתובת אתר של לוח שנה", - "scheduling_uri": "כתובת אתר לתזמון", - "freebusy_uri": "כתובת אתר פנוי/עסוקה", - "section_contact": "פרטי איש קשר", - "section_work": "עבודה", - "section_personal": "אישי", - "email_default_label": "דוא״ל", - "phone_default_label": "טלפון", - "address_default_label": "כתובת", - "online_service_default_label": "מקוון", - "organization_label": "ארגון", - "title_label": "כותרת", - "role_label": "תפקיד", - "language_label": "שפה", - "related_default_label": "קשור", - "more_actions": "פעולות נוספות", - "age_years": "{count, plural, one {בן שנה} other {בן # שנים}}", - "years_since": "{count, plural, one {שנה} other {# שנים}}" - }, - "form": { - "create_title": "איש קשר חדש", - "edit_title": "ערוך איש קשר", - "section_address_book": "ספרייה", - "select_address_book": "בחר ספרייה...", - "section_identity": "שם וזהות", - "contact_type": "סוג איש קשר", - "type_person": "אדם", - "type_organization": "ארגון", - "section_work": "עבודה וארגון", - "prefix": "קידומת", - "prefix_placeholder": "ד\"ר, מר, גברת.", - "given_name": "שם פרטי", - "middle_name": "שם אמצעי", - "surname": "שם משפחה", - "suffix": "סיומת", - "suffix_placeholder": "ג'וניור, האב, ג'", - "nickname": "כינוי", - "nickname_placeholder": "כינוי", - "email": "אֶלֶקטרוֹנִי", - "email_placeholder": "email@example.com", - "phone": "טלפון", - "phone_placeholder": "+1 234 567 890", - "phone_type": "הקלד", - "phone_voice": "קול", - "phone_cell": "נייד", - "phone_fax": "פקס", - "phone_pager": "ביפר", - "phone_video": "וידאו", - "phone_text": "טקסט", - "organization": "ארגון", - "organization_placeholder": "שם החברה", - "department": "מחלקה", - "department_placeholder": "מחלקה", - "job_title": "תואר עבודה", - "job_title_placeholder": "למשל, מהנדס תוכנה", - "role": "תפקיד", - "role_placeholder": "למשל, ראש צוות", - "addresses": "כתובות", - "add_address": "הוסף כתובת", - "street": "רחוב", - "city": "עיר", - "region": "מדינה/אזור", - "postcode": "מיקוד", - "country": "מדינה", - "online_services": "שירותים מקוונים", - "add_online_service": "הוסף שירות מקוון", - "url_placeholder": "https://...", - "service_placeholder": "שירות", - "anniversaries": "ימי נישואין", - "add_anniversary": "הוסף תאריך", - "anniversary_birth": "יום הולדת", - "anniversary_wedding": "יום נישואין", - "anniversary_death": "זיכרון", - "anniversary_other": "אחר", - "personal_info": "מידע אישי", - "add_personal_info": "הוסף ערך", - "personal_info_placeholder": "למשל, צילום", - "personal_expertise": "מומחיות", - "personal_hobby": "תחביב", - "personal_interest": "עניין", - "personal_other": "אחר", - "level": "רמה", - "level_high": "גבוה", - "level_medium": "בינוני", - "level_low": "נמוך", - "categories": "קטגוריות", - "categories_placeholder": "למשל, משפחה, חברים, עמיתים", - "categories_hint": "הקלד כדי לחפש או להוסיף קטגוריות", - "category_add": "לְהוֹסִיף", - "note": "הערות", - "note_placeholder": "הוסף הערה...", - "gender": "מגדר", - "gender_sex": "סקס", - "gender_male": "זכר", - "gender_female": "נקבה", - "gender_other": "אחר", - "gender_none": "לא ישים", - "gender_unknown": "לא ידוע", - "gender_identity": "זהות מגדרית", - "gender_identity_placeholder": "זהות מגדרית...", - "calendar": "לוּחַ שָׁנָה", - "calendar_uri": "כתובת אתר של לוח שנה", - "scheduling_uri": "כתובת אתר לתזמון", - "freebusy_uri": "כתובת אתר פנוי/עסוקה", - "context_work": "עבודה", - "context_private": "פרטי", - "add_email": "הוסף אימייל", - "add_phone": "הוסף טלפון", - "save": "שמור", - "cancel": "לְבַטֵל", - "creating": "יוצר...", - "updating": "מעדכן...", - "name_required": "יש להזין שם פרטי, שם משפחה או ארגון", - "email_invalid": "נא להזין כתובת אימייל חוקית", - "email_error_inline": "פורמט אימייל לא חוקי", - "save_failed": "שמירת איש הקשר נכשלה", - "delete": "לִמְחוֹק", - "upload_photo": "העלה תמונה", - "remove_photo": "הסר תמונה", - "photo_hint": "JPG או PNG, עד 10 MB. יוגדל מחדש.", - "photo_too_large": "התמונה גדולה מדי (מקס 10 MB)", - "photo_invalid": "קובץ תמונה לא חוקי", - "change_photo": "שנה" - }, - "groups": { - "create": "קבוצה חדשה", - "edit": "ערוך קבוצה", - "empty": "עדיין אין קבוצות", - "delete_confirm_title": "מחק את הקבוצה", - "delete_confirm": "האם אתה בטוח שברצונך למחוק את הקבוצה הזו?", - "name_label": "שם הקבוצה", - "name_placeholder": "למשל, צוות, משפחה", - "name_required": "נדרש שם הקבוצה", - "save_failed": "שמירת הקבוצה נכשלה", - "members_label": "חברים", - "search_members": "חפש אנשי קשר להוספה...", - "no_members": "אין חברים בקבוצה זו", - "member_count": "{count, plural, =0 {אין חברים} one {חבר אחד} other {# חברים}}", - "send_email": "שלח דוא״ל לקבוצה", - "send_email_to": "אל", - "send_email_cc": "עותק", - "send_email_bcc": "עותק נסתר", - "no_member_emails": "לקבוצה זו אין חברים עם כתובת דוא״ל." - }, - "import": { - "title": "ייבוא אנשי קשר", - "drop_hint": "לחץ כדי לבחור קובץ vCard", - "file_types": "קבצי .vcf או .vcard", - "no_contacts": "לא נמצאו אנשי קשר בקובץ", - "parse_error": "ניתוח קובץ vCard נכשל", - "found": "{count, plural, one {נמצא איש קשר אחד} other {נמצאו # אנשי קשר}}", - "duplicate": "שכפול", - "select_all": "בחר הכל", - "deselect_all": "בטל את הבחירה בכולם", - "selected": "{count, plural, one {נבחר אחד} other {נבחרו #}}", - "import_button": "ייבוא", - "importing": "מייבא...", - "success": "{count, plural, one {יובא איש קשר אחד} other {יובאו # אנשי קשר}}", - "failed": "הייבוא נכשל", - "close": "לִסְגוֹר", - "file_too_large": "הקובץ גדול מדי (מקסימום 5MB)" - }, - "export": { - "title": "ייצוא אנשי קשר", - "success": "{count, plural, one {יוצא איש קשר אחד} other {יוצאו # אנשי קשר}}" - }, - "bulk": { - "selected": "{count, plural, one {נבחר אחד} other {נבחרו #}}", - "select_all": "בחר הכל", - "delete": "לִמְחוֹק", - "delete_confirm_title": "מחק אנשי קשר", - "delete_confirm": "למחוק {count, plural, one {איש קשר אחד} other {# אנשי קשר}}?", - "deleted": "{count, plural, one {נמחק איש קשר אחד} other {נמחקו # אנשי קשר}}", - "add_to_group": "הוסף לקבוצה", - "choose_group": "בחר קבוצה", - "adding_contacts": "מוסיף {count, plural, one {איש קשר אחד} other {# אנשי קשר}}", - "added_to_group": "אנשי קשר נוספו לקבוצה", - "export": "ייצוא", - "clear": "נקה בחירה" - }, - "toast": { - "created": "איש קשר נוצר", - "updated": "איש הקשר עודכן", - "deleted": "איש הקשר נמחק", - "error_create": "יצירת איש הקשר נכשלה", - "error_update": "עדכון איש הקשר נכשל", - "error_delete": "מחיקת איש הקשר נכשלה" - }, - "empty_filtered": "אין אנשי קשר תואמים לסננים שלך", - "empty_filtered_hint": "נסה להתאים או להביא סננים", - "back_to_email": "חזור לדוא״ל", - "open_categories": "קטגוריות פתוחות", - "activity": { - "recent_emails": "דוא״ל אחרונים", - "upcoming_events": "אירועים קרובים", - "no_emails": "אין דוא״ל אחרונים", - "no_events": "אין אירועים קרובים", - "no_subject": "(אין נושא)", - "no_title": "(ללא כותרת)", - "load_failed": "נכשל בטעינה", - "unknown_sender": "שולח לא ידוע", - "all_day": "כל היום" - }, - "context_menu": { - "open": "פתח", - "edit": "ערוך", - "send_email": "שלח דוא״ל", - "add_to_group": "הוסף לקבוצה", - "export_vcard": "יצא כ־vCard", - "delete": "מחק", - "call": "קרא", - "duplicate": "שכפל", - "print": "הדפס" - }, - "filters": { - "toggle": "סננים", - "select": "בחר", - "clear": "נקה", - "close": "סגור", - "title": "סננים מתקדמים", - "organization": "חברה", - "organization_placeholder": "למשל Acme Corp", - "job_title": "תפקיד עבודה", - "job_title_placeholder": "למשל עיצוב", - "location": "מיקום", - "location_placeholder": "עיר או מדינה", - "email_domain": "דומיין דוא״ל", - "email_domain_placeholder": "example.com", - "birthday_month": "יום הולדת ב", - "any_month": "כל חודש", - "has_email": "יש דוא״ל", - "has_phone": "יש טלפון", - "has_photo": "יש תמונה", - "sort_by": "מיון לפי", - "sort_first_name": "שם פרטי", - "sort_last_name": "שם משפחה" - } - }, - "calendar": { - "title": "לוח שנה", - "back_to_email": "חזור לדוא״ל", - "back_to_month": "חזרה לחודש", - "my_calendars": "לוחות שנה", - "birthday_calendar": "ימי הולדת", - "mini_calendar_change": "לחץ כדי לשנות חודש", - "views": { - "month": "חודש", - "week": "שבוע", - "day": "יום", - "agenda": "סדר יום", - "today": "היום", - "month_hint": "חודש (מ')", - "week_hint": "שבוע (w)", - "day_hint": "יום (ד)", - "agenda_hint": "סדר יום (א)", - "tasks": "משימות", - "tasks_hint": "משימות (ק)" - }, - "events": { - "create": "צור אירוע", - "edit": "ערוך אירוע", - "delete": "מחק את האירוע", - "details": "פרטי האירוע", - "no_events": "אין אירועים", - "all_day": "כל היום", - "more": "+{count}נוספים", - "no_title": "(ללא כותרת)", - "resize": "שנה את גודל האירוע", - "duplicate": "שכפול", - "today_header": "היום", - "tomorrow_header": "מחר", - "agenda_show_earlier": "הצגת אירועים קודמים", - "agenda_loading": "טוען…", - "agenda_range_start": "אירועים מ-{date}", - "agenda_range_end": "אירועים עד {date}", - "export_ics": "יצא כ .ics", - "copy_title": "עותק כותרת", - "copy_link": "עותק קישור פגישה", - "new_event": "אירוע חדש", - "new_all_day_event": "אירוע חדש למשך כל היום", - "new_task": "משימה חדשה", - "go_to_today": "עבור להיום" - }, - "detail": { - "add_note": "הוסף הערה...", - "save_note": "שמור", - "note_saved": "הערה נוספה", - "open_link": "פתח קישור", - "meeting_link": "קישור לפגישה", - "tentative": "טנטטיבי", - "cancelled": "בוטל", - "delete_confirm": "למחוק את האירוע הזה?" - }, - "form": { - "title": "כותרת", - "description": "תיאור", - "location": "מיקום", - "meeting_link": "קישור לפגישה", - "start_date": "תאריך התחלה", - "end_date": "תאריך סיום", - "start_time": "שעת התחלה", - "end_time": "שעת סיום", - "all_day_event": "אירוע כל היום", - "calendar_select": "לוּחַ שָׁנָה", - "save": "שמור", - "cancel": "לְבַטֵל", - "delete_confirm": "האם אתה בטוח שברצונך למחוק את האירוע הזה?", - "color": "צבע" - }, - "participants": { - "availability": { - "title": "זמינות", - "free": "פנוי", - "busy": "עסוק", - "tentative": "טנטטיבי", - "unknown": "לא משתמש בשרת זה", - "checking": "בודק…" - }, - "title": "משתתפים", - "add": "הוסף משתתף", - "organizer": "מארגן", - "attendee": "משתתף", - "accepted": "התקבל", - "declined": "נדחה", - "tentative": "טנטטיבי", - "needs_action": "צריך פעולה", - "remove": "הסר", - "email_placeholder": "הוסף כתובת אימייל או חפש אנשי קשר", - "send_invitations": "שלח הזמנות למשתתפים", - "status_summary": "{accepted}התקבל,{pending}בהמתנה", - "invited_by": "הזמנה מאת{name}", - "respond_below": "השב באמצעות הכפתורים למטה", - "rsvp_label": "התגובה שלך", - "cancel_notification": "המשתתפים יקבלו הודעה על הביטול", - "you_organizer": "אתה המארגן", - "you_attendee": "אתה משתתף", - "no_participants": "אין משתתפים", - "count": "{count, plural, one {משתתף אחד} other {# משתתפים}}", - "edit": "ערוך" - }, - "recurrence": { - "title": "הישנות", - "none": "לא חוזר", - "daily": "יומי", - "weekly": "שבועי", - "monthly": "חודשי", - "yearly": "מדי שנה", - "every_n_days": "כל{count}ימים", - "every_n_weeks": "כל{count}שבועות", - "every_n_months": "כל{count}חודשים", - "until": "עד", - "occurrences": "מופעי{count}", - "custom": "מותאם אישית…", - "edit_custom": "ערוך חזרה מותאמת אישית", - "every_n_years": "כל {count} שנים", - "on_days": "ב־{days}", - "on_day_n": "ביום {day}", - "on_the_nth": "ב־{nth} {day}", - "in_month": "ב־{month}", - "nth_1": "ראשון", - "nth_2": "שני", - "nth_3": "שלישי", - "nth_4": "רביעי", - "nth_last": "אחרון", - "editor_freq_day": "יום", - "editor_freq_week": "שבוע", - "editor_freq_month": "חודש", - "editor_freq_year": "שנה", - "editor_repeats_on": "חוזר ב", - "editor_every": "כל", - "editor_unit_days": "יום(ים)", - "editor_unit_weeks": "שבוע(ות)", - "editor_unit_months": "חודש(ים)", - "editor_unit_years": "שנה(ה)", - "editor_on_day": "ביום", - "editor_on_the": "ב", - "editor_in": "ב", - "editor_ends": "מסתיים", - "editor_never": "לעולם לא", - "editor_ends_on": "ב", - "editor_ends_after": "לאחר", - "editor_occurrences": "הופעות" - }, - "recurrence_scope": { - "edit_title": "ערוך אירוע חוזר", - "delete_title": "מחק אירוע חוזר", - "description": "זהו אירוע שחוזר על עצמו. אילו אירועים תרצה לשנות?", - "this_event": "אירוע זה בלבד", - "this_and_future": "זה והאירועים הבאים", - "all_events": "כל האירועים", - "cancel": "לְבַטֵל", - "save": "שמור", - "delete": "לִמְחוֹק", - "rsvp_title": "השב לאירוע חוזר", - "rsvp_description": "זהו אירוע שחוזר על עצמו. על אילו אירועים חלה התשובה שלך?", - "respond": "השב" - }, - "alerts": { - "title": "תזכורת", - "none": "אין תזכורת", - "at_time": "בזמן האירוע", - "minutes_before": "{count, plural, one {דקה אחת לפני} other {# דקות לפני}}", - "hours_before": "{count, plural, one {שעה אחת לפני} other {# שעות לפני}}", - "days_before": "{count, plural, one {יום אחד לפני} other {# ימים לפני}}", - "weeks_before": "{count, plural, one {שבוע #} other {# שבועות}} לפני", - "unit_minutes_before": "דקות לפני", - "unit_hours_before": "שעות לפני", - "unit_days_before": "ימים לפני", - "unit_weeks_before": "שבועות לפני", - "add": "הוסף תזכורת", - "remove": "הסר תזכורת", - "amount": "כמות תזכורת", - "unit": "יחידת תזכורת" - }, - "settings": { - "title": "הגדרות לוח שנה", - "default_view": "תצוגת ברירת מחדל", - "week_starts_on": "השבוע מתחיל ב", - "time_format": "פורמט זמן", - "default_calendar": "לוח שנה ברירת מחדל", - "default_reminder": "תזכורת ברירת מחדל", - "time_format_12h": "12 שעות", - "time_format_24h": "24 שעות", - "notifications_enabled": "התראות על אירועים", - "notifications_enabled_desc": "הצג התראות עבור אירועי לוח שנה קרובים", - "notification_sound": "צליל התראה", - "notification_sound_desc": "השמע צליל להתראות לוח שנה", - "invitation_parsing": "נתח הזמנות באימייל", - "invitation_parsing_desc": "זיהוי הזמנות ליומן בקבצים מצורפים לאימייל והצג פעולות ביומן", - "show_time_in_month_view": "הצג זמן בתצוגת חודש", - "show_time_in_month_view_desc": "הצג זמני אירועים בתצוגת לוח השנה של החודש", - "show_week_numbers": "הצג מספרי שבוע", - "show_week_numbers_desc": "הצג מספרי שבוע במיני-לוח שנה", - "calendar_free_scroll": "גלילה חופשית", - "calendar_free_scroll_desc": "גלילה רציפה בין חודשים, שבועות וימים במקום תקופה אחת בכל פעם", - "enable_tasks": "אפשר משימות", - "enable_tasks_desc": "הצג תצוגת משימות בלוח השנה לניהול מטלות", - "show_tasks_on_calendar": "הצג משימות בלוח השנה", - "show_tasks_on_calendar_desc": "הצג שבבי משימות בתצוגות יומן היום והשבוע", - "hover_preview": "תצוגה מקדימה של ריחוף לאירוע", - "hover_preview_desc": "הצג חלון קופץ לפרטי בעת ריחוף מעל אירועי לוח שנה", - "hover_preview_instant": "מיידי", - "hover_preview_delay_500ms": "עיכוב של 0.5 שניות", - "hover_preview_delay_1s": "עיכוב של שנייה", - "hover_preview_delay_2s": "עיכוב של 2 שניות", - "hover_preview_off": "מושבת", - "organizer_identity": "ארגון הזמנות בתור", - "organizer_identity_desc": "כתובת היומן שתשמש כמארגן כשמזמינים אנשים לאירועים.", - "organizer_identity_failed": "שינוי כתובת המארגן נכשל", - "show_birthday_calendar": "צור קשר עם יומן יום הולדת", - "show_birthday_calendar_desc": "הצג לוח שנה וירטואלי עם ימי הולדת מאנשי הקשר שלך" - }, - "days": { - "monday": "שני", - "tuesday": "שלישי", - "wednesday": "רביעי", - "thursday": "חמישי", - "friday": "שישי", - "saturday": "שבת", - "sunday": "ראשון", - "mon": "שני", - "tue": "שלישי", - "wed": "רביעי", - "thu": "חמישי", - "fri": "שישי", - "sat": "שבת", - "sun": "ראשון" - }, - "notifications": { - "event_created": "האירוע נוצר", - "event_updated": "האירוע עודכן", - "event_deleted": "האירוע נמחק", - "calendar_created": "יומן נוצר", - "calendar_deleted": "לוח השנה נמחק", - "event_move_error": "העברת האירוע נכשלה", - "event_resize_error": "שינוי גודל האירוע נכשל", - "alert_title": "אירוע קרוב", - "alert_now": "מתחיל עכשיו", - "alert_in_minutes": "ב-{count}דקות", - "invitation_sent": "נשלחו הזמנות", - "invitations_denied_title": "לא ניתן לשלוח את ההזמנות", - "invitations_denied": "השרת סירב לשלוח את ההזמנות: {reason}. לשמור את האירוע בלי לשלוח אותן?", - "save_without_invitations": "שמור בלי הזמנות", - "rsvp_updated": "התגובה עודכנה", - "rsvp_error": "עדכון התגובה נכשל", - "event_duplicated": "האירוע שוכפל", - "event_error": "שמירת האירוע נכשלה", - "task_due": "משימה בשל", - "event_exported": "אירוע ייצא", - "title_copied": "כותרת הועתקה", - "link_copied": "קישור הועתק" - }, - "status": { - "loading_calendars": "טוען יומנים...", - "loading_events": "טוען אירועים..." - }, - "quick_create": { - "placeholder": "כותרת אירוע חדשה", - "aria_label": "אירוע יצירה מהיר" - }, - "nav_prev": "הקודם", - "nav_next": "הבא", - "import": { - "title": "ייבוא לוח שנה", - "tab_file": "קובץ", - "tab_url": "כתובת אתר", - "select_file": "בחר קובץ .ics", - "drop_file": "או שחרר את הקובץ כאן", - "supported_formats": "תומך בקבצי iCalendar (.ics).", - "url_description": "הזן את כתובת האתר של עדכון iCalendar (.ics) חיצוני כדי לייבא אירועים.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "תומך בכתובות URL של iCalendar (.ics).", - "fetch": "אחזר", - "invalid_url": "אנא הזן כתובת אתר חוקית", - "url_fetch_failed": "אחזור היומן מ-URL נכשל", - "parsing": "מנתח קובץ לוח שנה...", - "parsed_events": "נמצאו אירועים של{count}", - "no_events": "לא נמצאו אירועים בקובץ", - "select_all": "בחר הכל", - "deselect_all": "בטל את הבחירה בכולם", - "target_calendar": "ייבא ליומן", - "import_button": "ייבא נבחר", - "importing": "מייבא אירועים...", - "success": "אירועי{count}יובאו בהצלחה", - "error": "ייבוא היומן נכשל", - "file_too_large": "הקובץ חורג ממגבלת 10MB", - "invalid_format": "פורמט קובץ לוח שנה לא חוקי" - }, - "management": { - "title": "ניהול לוח שנה", - "description": "צור, שנה שם והתאם אישית את היומנים שלך. לחץ לחיצה ימנית על לוח שנה בסרגל הצד כדי לשנות במהירות את צבעו.", - "name": "שֵׁם", - "name_placeholder": "שם לוח שנה", - "color": "צבע", - "change_color": "שנה צבע", - "add_calendar": "הוסף לוח שנה", - "edit": "ערוך", - "delete": "לִמְחוֹק", - "save": "שמור", - "create": "צור", - "cancel": "לְבַטֵל", - "default": "ברירת מחדל", - "confirm_delete": "למחוק את \"{name}\"? כל האירועים בלוח השנה הזה יוסרו.", - "confirm_clear": "לנקות את כל האירועים מ-\"{name}\"? לא ניתן לבטל זאת.", - "clear_events": "נקה אירועים", - "events_cleared": "אירועי{count}נוקו", - "error_clear": "נכשל ניקוי אירועים ביומן", - "calendar_created": "יומן נוצר", - "calendar_updated": "לוח השנה עודכן", - "calendar_deleted": "לוח השנה נמחק", - "color_updated": "צבע לוח השנה עודכן", - "error_create": "יצירת היומן נכשלה", - "error_update": "עדכון היומן נכשל", - "error_delete": "מחיקת היומן נכשלה", - "caldav_url": "כתובת CalDAV", - "copy_url": "העתק כתובת URL של CalDAV", - "url_copied": "כתובת האתר של CalDAV הועתקה ללוח", - "random_color": "צבע אקראי חדש", - "set_default": "הגדר כברירת מחדל", - "default_updated": "לוח שנה ברירת המחדל עודכן", - "error_default": "נכשל בהגדרת לוח שנה ברירת מחדל", - "share": "שתף לוח שנה", - "new_event_in_calendar": "אירוע חדש בלוח שנה זה", - "kind": "תוכן", - "kind_events": "אירועים", - "kind_tasks": "משימות", - "kind_both": "אירועים ומשימות", - "kind_hint": "קובע אילו אפליקציות יראו את היומן הזה כשהוא מסונכרן דרך CalDAV. לא ניתן לשנות זאת מאוחר יותר." - }, - "subscription": { - "title": "מנוי iCal", - "section_title": "מנויי iCal", - "description": "הירשם לעדכון iCalendar חיצוני. אירועים יסונכרנו אוטומטית עם היומן שלהם. תומך בכתובות URL של https:// ו-webcal://.", - "url_label": "כתובת אתר של לוח שנה", - "url_placeholder": "https://example.com/calendar.icsאו webcal://...", - "name_label": "שם לוח שנה", - "name_placeholder": "למשל חגים ציבוריים", - "color_label": "צבע", - "refresh_interval": "מרווח רענון", - "interval_15": "כל 15 דקות", - "interval_30": "כל 30 דקות", - "interval_60": "כל שעה", - "interval_360": "כל 6 שעות", - "interval_1440": "כל יום", - "subscribe": "הירשם", - "subscribing": "נרשם כמנוי...", - "save": "שמור שינויים", - "saving": "שומר...", - "edit": "ערוך", - "edit_title": "ערוך מנוי", - "updated": "עודכן \"{name}\"", - "update_error": "עדכון המנוי נכשל", - "invalid_url": "אנא הזן כתובת אתר חוקית", - "success": "נרשם ל-\"{name}\"", - "error": "הוספת המנוי נכשלה", - "refresh": "רענן עכשיו", - "refresh_success": "רענון המנוי", - "refresh_error": "רענון המנוי נכשל", - "unsubscribe": "בטל את המנוי", - "confirm_delete": "לבטל את המנוי ל-\"{name}\"? היומן וכל אירועיו יוסרו.", - "deleted": "המנוי הוסר", - "delete_error": "הסרת המנוי נכשלה", - "last_refreshed": "עודכן לאחרונה:{time}" - }, - "tasks": { - "label": "משימות", - "no_tasks": "אין משימות", - "no_title": "(ללא כותרת)", - "mark_complete": "סמן כהשלמה", - "mark_incomplete": "סמן כלא שלם", - "filter_all": "כֹּל", - "filter_pending": "בהמתנה", - "filter_completed": "הושלם", - "filter_overdue": "באיחור", - "show_completed": "ההצגה הושלמה", - "create": "משימה חדשה", - "edit": "ערוך משימה", - "title_placeholder": "כותרת המשימה", - "description_placeholder": "הוסף תיאור...", - "due_date": "תאריך יעד", - "include_time": "כלול זמן", - "priority": "עדיפות", - "priority_none": "אין", - "priority_high": "גבוה", - "priority_medium": "בינוני", - "priority_low": "נמוך", - "progress": "סטטוס", - "progress_needs_action": "צריך פעולה", - "progress_in_process": "בתהליך", - "progress_completed": "הושלם", - "progress_cancelled": "בוטל", - "calendar": "לוּחַ שָׁנָה", - "alert": "תזכורת", - "alert_none": "אין", - "alert_at_time": "בזמן תאריך היעד", - "alert_5min": "5 דקות לפני", - "alert_15min": "15 דקות לפני", - "alert_30min": "30 דקות לפני", - "alert_1hr": "שעה אחת לפני", - "alert_1day": "יום אחד לפני", - "delete": "לִמְחוֹק", - "cancel": "לְבַטֵל", - "save": "שמור", - "quick_add_placeholder": "הוסף משימה...", - "due_today": "היום", - "due_tomorrow": "מחר", - "overdue": "באיחור" - }, - "months": { - "jan": "ינו׳", - "feb": "פבר׳", - "mar": "מרץ", - "apr": "אפר׳", - "may": "מאי", - "jun": "יון׳", - "jul": "יול׳", - "aug": "אוג׳", - "sep": "ספט׳", - "oct": "אוק׳", - "nov": "נוב׳", - "dec": "דצמ׳", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "פתח תפריט", - "webcal_action": { - "title": "פתח קישור לוח שנה", - "description": "כיצד היית רוצה להשתמש ב־\"{name}\"?", - "import_title": "ייבא פעם אחת", - "import_description": "קבל את האירועים כעת והעתק אותם לאחד לוחות השנה שלך.", - "subscribe_title": "הירשם", - "subscribe_description": "שמור את הלוח שנה הזה בסנכרון אוטומטי כלוח שנה נפרד.", - "cancel": "בטל" - } - }, - "advanced_search": { - "title": "חיפוש מתקדם", - "from": "מ", - "from_placeholder": "אימייל או שם השולח", - "to": "ל", - "to_placeholder": "אימייל או שם של הנמען", - "subject": "נושא", - "subject_placeholder": "הנושא מכיל...", - "body": "גוף", - "body_placeholder": "הגוף מכיל...", - "folder": "תיקיה", - "all_folders": "כל התיקיות", - "has_attachment": "קבצים מצורפים", - "date_after": "אחרי", - "date_before": "לפני", - "size_min": "גדול מ־(KB)", - "size_max": "קטן מ־(KB)", - "starred": "מסומן בכוכב", - "unread": "לא נקרא", - "read": "קרא", - "yes": "כן", - "no": "לא", - "clear": "ברור", - "clear_all": "נקה הכל", - "filters_active": "מסנן{count}", - "filters_active_plural": "מסנני{count}", - "toggle_filters": "עוד", - "search_hint": "השתמש במסננים מתקדמים לחיפוש מדויק", - "advanced_filters_tooltip": "מסנני חיפוש מתקדמים", - "results_found": "{count, plural, =0 {לא נמצאו תוצאות} one {נמצאה תוצאה אחת} other {נמצאו # תוצאות}}", - "results_found_more": "נמצאו תוצאות{count}+", - "suggestions_recent": "חיפושים אחרונים", - "suggestions_people": "אנשים", - "suggestions_from": "חיפוש הודעות מאת {address}", - "suggestions_to": "חיפוש הודעות אל {address}", - "suggestions_remove_recent": "הסרה מהחיפושים האחרונים" - }, - "welcome": { - "title": "ברוך הבא לתיבת הדואר שלך", - "tip_compose": "הקש c כדי לכתוב דוא\"ל חדש", - "tip_shortcuts": "לחץ על? כדי לראות את כל קיצורי המקלדת", - "tip_sidebar": "מצא את לוח השנה, אנשי הקשר וההגדרות בתפריט סרגל הצד", - "tip_settings": "התאם אישית את החוויה שלך בהגדרות", - "got_it": "הבנתי", - "settings": "הגדרות", - "dismiss": "לְפַטֵר", - "start_tour": "התחל סיור" - }, - "demo_welcome": { - "title": "ברוכים הבאים ל-Bolwark Mail", - "description": "חקור לקוח דואר אינטרנט עם תכונות מלאות - ישירות בדפדפן שלך. כל הנתונים נשארים במכשיר שלך, אז אל תהסס לבדוק הכל.", - "feature_email": "קרא וכתוב אימייל", - "feature_organize": "תגיות, כוכבים ותיקיות", - "feature_shortcuts": "קיצורי מקלדת", - "feature_privacy": "100% הדגמה פרטית", - "hint": "לחץ על אימייל כלשהו מימין כדי להתחיל, או צא לסיור למטה." - }, - "files": { - "office_edit": "עריכת מסמך", - "office_loading": "פותח את עורך המסמכים…", - "office_error": "לא ניתן היה לפתוח את עורך המסמכים.", - "office_close": "סגירת העורך", - "title": "קבצים", - "search_placeholder": "חפש קבצים...", - "empty_state_title": "אין עדיין קבצים", - "empty_state_description": "העלה קבצים או צור תיקיות כדי להתחיל", - "upload": "העלה", - "upload_files": "העלה קבצים", - "new_folder": "תיקיה חדשה", - "new_folder_name": "שם התיקיה", - "rename": "שנה שם", - "rename_title": "שנה שם", - "new_name": "שם חדש", - "name_forbidden_chars": "השרת אינו מתיר את התווים האלה בשמות: {chars}", - "name_reserved": "השם הזה שמור בשרת.", - "delete": "לִמְחוֹק", - "delete_confirm_title": "מחק משאב", - "delete_confirm_message": "האם אתה בטוח שברצונך למחוק את \"{name}\"? לא ניתן לבטל זאת.", - "download": "הורד", - "name": "שֵׁם", - "size": "גודל", - "modified": "שונה", - "type": "הקלד", - "folder": "תיקיה", - "file": "קובץ", - "parent_directory": "ספריית הורים", - "breadcrumb_root": "בית", - "drop_files_here": "שחרר כאן קבצים או תיקיות כדי להעלות", - "uploading": "מעלה...", - "upload_success": "{count, plural, one {קובץ אחד הועלה} other {# קבצים הועלו}}", - "upload_error": "העלאת הקובץ נכשלה", - "create_folder_success": "התיקיה נוצרה", - "create_folder_error": "יצירת התיקיה נכשלה", - "delete_success": "נמחק בהצלחה", - "delete_error": "המחיקה נכשלה", - "rename_success": "השם שונה בהצלחה", - "rename_error": "שינוי השם נכשל", - "download_error": "ההורדה נכשלה", - "not_available": "אחסון קבצים אינו זמין בשרת זה", - "cancel": "לְבַטֵל", - "create": "צור", - "save": "שמור", - "no_results": "אין קבצים שתואמים לחיפוש שלך", - "batch_delete_confirm_message": "האם למחוק {count, plural, one {פריט אחד} other {# פריטים}}? לא ניתן לבטל זאת.", - "batch_delete_success": "{count, plural, one {פריט אחד נמחק} other {# פריטים נמחקו}}", - "grid_view": "תצוגת רשת", - "list_view": "תצוגת רשימה", - "details": "פרטים", - "path": "נתיב", - "preview": "תצוגה מקדימה", - "preview_error": "טעינת התצוגה המקדימה נכשלה", - "cut": "גזור", - "copy": "העתק", - "paste": "הדבק", - "move_success": "{count, plural, one {פריט אחד הועבר} other {# פריטים הועברו}}", - "move_error": "לא הצליח לזוז", - "paste_success": "הודבק בהצלחה", - "paste_error": "ההדבקה נכשלה", - "new_text_file": "קובץ טקסט חדש", - "file_name": "שם הקובץ", - "retry": "נסה שוב", - "refresh": "רענן", - "toggle_favorite": "החלף מועדף", - "duplicate": "שכפול", - "duplicate_success": "שוכפל בהצלחה", - "duplicate_error": "השכפול נכשל", - "create_file_success": "הקובץ נוצר", - "create_file_error": "יצירת הקובץ נכשלה", - "favorites": "מועדפים", - "recent": "לאחרונה", - "properties": "נכסים", - "open_folder": "פתח תיקייה", - "upload_folder": "העלה תיקייה", - "file_too_large": "\"{name}\" חורג מגודל הקובץ המרבי ({max})", - "undo": "בטל", - "undo_success": "הפעולה בוטלה", - "undo_error": "הביטול נכשל", - "toolbar": "פעולות קובץ", - "file_list": "קבצים ותיקיות", - "context_menu": "פעולות", - "settings_title": "הגדרות קובץ", - "settings_display": "תצוגה", - "settings_default_view": "תצוגת ברירת מחדל", - "settings_default_view_desc": "בחר בין פריסת רשת לרשימה", - "settings_default_sort": "מיון ברירת מחדל", - "settings_default_sort_desc": "בחר את מיון ברירת המחדל עבור קבצים", - "settings_sort_direction": "כיוון מיון", - "settings_sort_direction_desc": "בחר בסדר עולה או יורד", - "settings_ascending": "עולה", - "settings_descending": "יורד", - "settings_icons": "אייקונים", - "settings_show_icons": "הצג סמלי קבצים", - "settings_show_icons_desc": "הצג סמלים לצד קבצים ותיקיות", - "settings_colored_icons": "סמלים צבעוניים", - "settings_colored_icons_desc": "השתמש בסמלים צבעוניים במקום מונוכרום", - "settings_show_thumbnails": "הצג תמונות ממוזערות", - "settings_show_thumbnails_desc": "הצג תצוגות מקדימות של תמונה במקום סמלים עבור קובצי תמונה", - "settings_behavior": "התנהגות", - "settings_show_hidden": "הצג קבצים מוסתרים", - "settings_show_hidden_desc": "הצג קבצים ותיקיות שמתחילים בנקודה", - "settings_folder_layout": "ניווט תיקיות", - "settings_folder_layout_desc": "בחר כיצד יוצגו תיקיות: בשורה עם קבצים או בעץ סרגל צד", - "settings_folder_layout_inline": "מוטבע", - "settings_folder_layout_sidebar": "סרגל צד", - "disabled_title": "תכונת הקבצים מושבתת על ידי מנהל המערכת שלך", - "disabled_description": "העלאות קבצים גדולות דרך WebDAV יכולות לגרום לחוסר יציבות של Stalwart/RocksDB, כולל קריסות מחוץ לזיכרון ושימוש בדיסק בלתי ניתן לשחזור. לא ניתן לנקות קבצים שנמחקו באופן מיידי מאחסון כתמים. תכונה זו אינה מומלצת עבור סביבות ייצור.", - "stability_warning": "העלאות קבצים גדולות עלולות לגרום לאי יציבות של השרת. לא ניתן לנקות קבצים שנמחקו מיד מהאחסון. השתמש בזהירות.", - "open_in_new_tab": "פתח בלשונית חדשה", - "other_accounts": "חשבונות אחרים", - "no_accounts": "אין חשבונות מחוברים.", - "share": "שתף", - "shared": "משותף", - "shared_with_me": "משותף איתי", - "shared_by": "משותף על ידי {name}", - "open_folder_tree": "פתח עץ תיקייה", - "migration_title": "עדכון הקבצים שלך…", - "migration_description": "ארגון תיקיות וקבצים לתוך המבנה הנכון שלהם. זה קורה רק פעם אחת." - }, - "smime": { - "your_certificates": "התעודות שלך", - "your_certificates_desc": "ייבא ונהל את אישורי ה-S/MIME שלך עבור חתימה והצפנה של הודעות דוא\"ל", - "recipient_certificates": "אישורי נמען", - "recipient_certificates_desc": "אישורים ציבוריים להצפנת מיילים לנמענים", - "identity_bindings": "כריכות מפתח זהות", - "identity_bindings_desc": "קשר אישורי S/MIME לזהויות הדוא\"ל שלך", - "defaults_title": "ברירות מחדל", - "defaults_desc": "הגדר התנהגות ברירת מחדל של חתימה והצפנה", - "import_pkcs12": "ייבוא PKCS#12 (.p12/.pfx)", - "import_public_cert": "אישור ייבוא", - "no_certificates": "עדיין לא יובאו אישורים", - "no_recipient_certs": "אין אישורי נמען", - "expires": "פג תוקף", - "expired": "פג תוקף", - "bound_to": "מחויב ל", - "no_key_bound": "אין", - "lock": "מפתח נעילה", - "unlock": "מפתח נעילה", - "details": "הצג פרטים", - "delete": "לִמְחוֹק", - "encrypt_by_default": "הצפנה כברירת מחדל", - "encrypt_by_default_desc": "הצפן הודעות דוא״ל באופן אוטומטי כאשר לכל הנמענים יש תעודות", - "remember_unlocked": "זכור מפתחות לא נעולים", - "remember_unlocked_desc": "השאר את המפתחות פתוחים למשך הפעלת הדפדפן הזו", - "sign_default_for": "חתום כברירת מחדל עבור", - "enter_p12_passphrase": "הזן ביטוי סיסמה PKCS#12", - "p12_passphrase_desc": "הזן את ביטוי הסיסמה שמגן על קובץ האישור הזה", - "enter_storage_passphrase": "הגדר ביטוי סיסמה לאחסון", - "storage_passphrase_desc": "בחר ביטוי סיסמה כדי להגן על מפתח זה במצב מנוחה בדפדפן שלך", - "next": "הבא", - "import": "ייבוא", - "unlock_key": "מפתח נעילה", - "unlock_key_desc": "הזן את משפט הסיסמה לאחסון כדי לפתוח מפתח זה לצורך חתימה או פענוח", - "passphrase_placeholder": "הזן ביטוי סיסמה", - "confirm_passphrase_placeholder": "אשר את ביטוי הסיסמה", - "passphrase_mismatch": "ביטויי הסיסמה אינם תואמים", - "cancel": "לְבַטֵל", - "processing": "מעבד...", - "close": "לִסְגוֹר", - "certificate_details": "פרטי תעודה", - "cert_subject": "נושא", - "cert_issuer": "מנפיק", - "cert_email": "אֶלֶקטרוֹנִי", - "cert_serial": "מספר סידורי", - "cert_validity": "תוקף", - "cert_fingerprint": "טביעת אצבע (SHA-256)", - "cert_algorithm": "אלגוריתם", - "cert_capabilities": "יכולות", - "cert_source": "מקור", - "cert_expired": "תוקף האישור הזה פג", - "cert_not_yet_valid": "אישור זה עדיין אינו תקף", - "cap_sign": "חתימה", - "cap_encrypt": "הצפנה", - "cap_none": "אין", - "show_passphrase": "הצג ביטוי סיסמה", - "hide_passphrase": "הסתר ביטוי סיסמה", - "sign_toggle": "לחתום", - "encrypt_toggle": "הצפין", - "missing_recipient_certs": "חסרים תעודות עבור:{emails}", - "missing_sender_cert": "אין אישור הקשור לזהות זו", - "status_encrypted_ok": "הודעה זו הוצפנה", - "status_encrypted_no_key": "הודעה זו מוצפנת אך לא נמצא מפתח תואם", - "status_encrypted_failed": "פענוח ההודעה הזו נכשל", - "status_signed_valid": "חתימה מאומתת", - "status_signed_invalid": "אימות החתימה נכשל", - "status_signed_expired_cert": "חתום עם תעודה שפג תוקפו", - "status_signed_self_signed": "החתימה תקפה, אבל האישור חתום בעצמו (לא מהימן)", - "status_signed_mismatch": "החתימה תקפה, אך החותם אינו תואם לשולח", - "status_unsupported": "פורמט S/MIME לא נתמך", - "auto_import_signer_certs": "ייבוא אוטומטי של אישורי חתימה", - "auto_import_signer_certs_desc": "שמור באופן אוטומטי תעודות מהודעות חתומות מאומתות לקריפציה עתידית", - "export": "ייצוא", - "enter_export_passphrase": "הגדר לייצא ביטוי סיסמה", - "export_passphrase_desc": "בחר ביטוי סיסמה כדי להגן על קובץ PKCS#12 המיוצא", - "export_storage_desc": "הזן את ביטוי הסיסמה לאחסון כדי לפענח את המפתח לייצוא", - "incorrect_passphrase": "ביטוי סיסמה שגוי" - }, - "tour": { - "step_counter": "שלב{current}של{total}", - "skip": "דלג על סיור", - "back": "חזרה", - "next": "הבא", - "finish": "סיים", - "take_a_tour": "צא לסיור בממשק", - "restart_title": "סיור היכרות", - "restart_desc": "הפעל מחדש את ההליכה המודרכת של הממשק", - "restart_button": "התחל את הסיור מחדש", - "sidebar_title": "תיבות הדואר שלך", - "sidebar_desc": "זהו סרגל הצד של התיקיות שלך. לחץ על כל תיבת דואר כדי להציג את המיילים שלה. אתה יכול ליצור תיקיות, לגרור אימיילים ביניהן ולראות ספירות שלא נקראו במבט אחד.", - "compose_title": "כתוב אימייל", - "compose_desc": "לחץ כאן כדי לכתוב מייל חדש. אתה יכול להוסיף נמענים, קבצים מצורפים ולהשתמש בעיצוב טקסט עשיר.", - "search_title": "חפש בדואר שלך", - "search_desc": "חפש לפי שולח, נושא או תוכן. לחץ על סמל הסינון עבור אפשרויות מתקדמות כמו טווח תאריכים, קבצים מצורפים והודעות מסומנות בכוכב.", - "email_list_title": "רשימת הדוא\"ל שלך", - "email_list_desc": "מיילים מופיעים כאן. לחץ על אחד כדי לקרוא אותו בצד ימין. השתמש בתיבת הסימון כדי לבחור מרובים, ולאחר מכן העבר, מחק או תיוג אותם בכמות גדולה.", - "email_viewer_title": "חלונית קריאה", - "email_viewer_desc": "האימייל שנבחר נפתח כאן. השב, העבר, העבר לארכיון או מחק באמצעות לחצני סרגל הכלים. אתה יכול גם לסמן מיילים בכוכב או להוסיף תגיות צבע.", - "keywords_title": "תגי צבע", - "keywords_desc": "ארגן את הדואר האלקטרוני שלך עם תגים מקודדים בצבע. גרור הודעת דוא\"ל לתג כדי לתייג אותו, או לחץ באמצעות לחצן העכבר הימני על דוא\"ל כדי להקצות תגים.", - "calendar_title": "לוּחַ שָׁנָה", - "calendar_desc": "עבור ליומן כדי לנהל את האירועים שלך. צור אירועים, הגדר תזכורות והצג פריסות של יום, שבוע או חודש.", - "contacts_title": "אנשי קשר", - "contacts_desc": "ספר הכתובות שלך גר כאן. ייבא אנשי קשר, צור קבוצות ולחץ על כל איש קשר כדי לראות את הפרטים המלאים שלהם.", - "settings_title": "הגדרות", - "settings_desc": "התאם אישית הכל: ערכת נושא, צפיפות, חתימות, מסננים, קיצורי מקלדת, ברירות מחדל של לוח שנה ועוד.", - "shortcuts_title": "קיצורי מקלדת", - "shortcuts_desc": "משתמשי כוח אוהבים את זה. לחץ על? בכל עת כדי לראות את כל קיצורי הדרך הזמינים. אתה יכול לנווט, לכתוב ולנהל מיילים מבלי לגעת בעכבר.", - "compose_open_title": "העורך", - "compose_open_desc": "זה כותב הדוא\"ל. הוסף נמענים, כתוב את ההודעה שלך, צרף קבצים והשתמש בעיצוב טקסט עשיר. אתה יכול גם לשמור טיוטות ולהשתמש בתבניות.", - "calendar_view_title": "לוח השנה שלך", - "calendar_view_desc": "הנה היומן שלך עם אירועים לדוגמה. אתה יכול לעבור בין תצוגות יום, שבוע, חודש וסדר יום באמצעות סרגל הכלים.", - "create_event_title": "צור אירוע", - "create_event_desc": "לחץ על כפתור זה כדי ליצור אירוע לוח שנה חדש. אתה יכול להגדיר כותרת, תאריך, שעה ולהוסיף משתתפים.", - "event_modal_title": "פרטי האירוע", - "event_modal_desc": "הנה טופס האירוע. מלא את הכותרת, בחר תאריך ושעה, הוסף מיקום או משתתפים. לחץ על שמור כשתסיים - או סגור אותו והמשיך הלאה.", - "contacts_list_title": "אנשי הקשר שלך", - "contacts_list_desc": "הנה אנשי הקשר שלך. לחץ על כל איש קשר כדי לראות את הפרטים המלאים שלו בצד ימין. אתה יכול גם ליצור אנשי קשר חדשים, לייבא כרטיסי vCard או לארגן אנשי קשר בקבוצות.", - "settings_tabs_title": "תפריט הגדרות", - "settings_tabs_desc": "להלן כל קטגוריות ההגדרות. התאם אישית את המראה שלך, נהל זהויות, הגדר מסנני דוא\"ל, הגדר את היומן שלך ועוד הרבה יותר.", - "files_title": "אחסון קבצים", - "files_desc": "דפדפן הקבצים שלך מאפשר לך להעלות, לארגן ולשתף קבצים - כמו כונן ענן אישי המובנה בדואר שלך.", - "demo_banner_title": "בקרות הדגמה", - "demo_banner_desc": "אתה במצב הדגמה - הכל נשאר בדפדפן שלך. לחץ על 'אפס הדגמה' בכל עת כדי להתחיל מחדש עם נתוני מדגם נקיים.", - "quota_title": "שימוש באחסון", - "quota_desc": "עקוב אחר גודל תיבת הדואר שלך כאן. העיגול מתמלא ככל שאתה מנצל יותר מקום.", - "show_on_new_devices_title": "הצג בהתקנים חדשים", - "show_on_new_devices_desc": "הפעל מחדש את בר הברכה ושיוך את הסיור בפעם הראשונה שאתה נכנס בהתקן חדש, גם אם כבר השלמת אותו במקום אחר" - }, - "meta_description": "לקוח webmail מינימליסטי באמצעות פרוטוקול JMAP", - "protocol_handlers": { - "title": "יישומים ברירת מחדל", - "description": "בחר אם קישורי דוא״ל ולוח שנה ייפתחו ב־Bulwark. מבחינה טכנית, Bulwark רשום כטיפול בפרוטוקול עבור קישורי mailto: ו־webcal:.", - "unsupported": "דפדפן זה או חיבור זה לא תומך בהרשמה ידנית של טיפול בפרוטוקול. ייתכן שעדיין תוכל להשתמש ב־PWA המותקנת דרך הדפדפן או הגדרות מערכת ההפעלה.", - "mailto_label": "קישורי דוא״ל", - "mailto_description": "פתח קישורי mailto: ב־Bulwark עם מחבר שמלא מראש.", - "protocol_open_mode_label": "בעת פתיחת קישורי פרוטוקול", - "protocol_open_mode_description": "בחר אם Bulwark יפתח קישורי mailto: ו־webcal: בלשונית חדשה או ישתמש בחיבור פתוח. אפשרות הישיבה הפעילה דורשת הרשאת עדכון כדי שתוכל ללחוץ על עדכון נופל כדי להביא את Bulwark לחזית אם הדפדפן חוסם מיקוד.", - "protocol_open_mode_active_session": "פתח בישיבה פעילה אם אפשר", - "protocol_open_mode_new_tab": "פתח תמיד לשונית חדשה", - "focus_notification_title": "פתח את Bulwark", - "focus_notification_body": "הקישור נפתח ב־Bulwark. לחץ כדי להביא את החלון לחזית.", - "webcal_label": "קישורי לוח שנה", - "webcal_description": "פתח קישורי webcal: ב־Bulwark עם תיבת דו־שיח מלא מראש להרשמה ללוח שנה.", - "register_mailto": "רשום יישום דוא״ל", - "register_webcal": "רשום יישום לוח שנה", - "mailto_registered": "בקש הרשמה של מטפל דוא״ל", - "webcal_registered": "בקש הרשמה של מטפל לוח שנה", - "registration_failed": "הרשמת טיפול בפרוטוקול נכשלה", - "opening_mailto": "פתיחת מחבר…", - "opening_webcal": "פתיחת לוח שנה…", - "browser_note": "הדפדפן או מערכת ההפעלה שלך עשויים לבקש ממך לאשר זאת ועשויים לדרוש שBulwark יותקן לפני שניתן לבחור אותו כיישום ברירת המחדל.", - "select_account_title": "בחר חשבון", - "select_mailto_account": "בחר איזה חשבון צריך לפתוח קישור דוא״ל זה.", - "select_webcal_account": "בחר איזה חשבון צריך לפתוח קישור לוח שנה זה.", - "select_account_note": "זה חל רק על קישור פרוטוקול זה.", - "detail_to": "אל", - "detail_subject": "נושא", - "detail_no_subject": "אין נושא", - "detail_calendar": "לוח שנה", - "detail_source": "מקור", - "active_account": "פעיל", - "switching_account": "החלפת חשבון…" - }, - "mailbox_context_menu": { - "mark_folder_read": "סמן תיקייה כקרויה", - "mark_folder_tree_read": "סמן תיקייה ותת־תיקיות כקרויות", - "mark_all_folders_read": "סמן את כל התיקיות כקרויות", - "new_subfolder": "תת־תיקייה חדשה…", - "new_folder": "תיקייה חדשה…", - "rename": "שנה שם…", - "share": "שיתוף...", - "import_email": "ייבא .eml או .zip…", - "empty_folder": "תיקייה ריקה", - "empty_folder_generic": "תיקייה ריקה", - "delete_folder": "מחק תיקייה", - "refresh": "רענן", - "mark_all_confirm_title": "סמן את כל התיקיות כקרויות", - "mark_all_confirm_message": "סמן כל הודעה שלא קרויה בחשבון האישי שלך כקרויה?", - "delete_confirm_title": "מחק תיקייה", - "delete_confirm_message": "מחק לצמיתות את התיקייה \"{name}\"? לא ניתן לבטל פעולה זו.", - "prompt_new_subfolder": "הזן שם לתת־תיקייה החדשה.", - "prompt_new_folder": "הזן שם לתיקייה החדשה.", - "prompt_rename": "הזן שם חדש לתיקייה זו.", - "placeholder_folder_name": "שם תיקייה", - "create": "צור", - "rename_confirm": "שנה שם", - "toast_marked_read": "התיקייה סומנה כקרויה", - "toast_marked_read_count": "סומנו {count, plural, one {הודעה 1} other {# הודעות}} כקרויות", - "toast_already_read": "אין הודעות שלא קרויות", - "toast_marked_all_read": "כל התיקיות סומנו כקרויות", - "toast_emptied": "התיקייה התרוקנה", - "toast_folder_created": "תיקייה נוצרה", - "toast_folder_renamed": "שם התיקייה שונה", - "toast_folder_deleted": "התיקייה נמחקה", - "toast_error_mark_read": "נכשל בסימון כקרויה", - "toast_error_empty": "נכשל בתרוקנון תיקייה", - "toast_error_create": "נכשל ביצירת תיקייה", - "toast_error_rename": "נכשל בשינוי שם תיקייה", - "toast_error_delete": "נכשל במחיקת תיקייה", - "toast_error_delete_has_children": "לתיקייה יש תת־תיקיות. הסר אותן קודם.", - "toast_error_delete_has_email": "התיקייה אינה ריקה. תרוקנה קודם.", - "delete_with_contents_title": "התיקייה אינה ריקה", - "delete_with_contents_message": "התיקייה \"{name}\" עדיין מכילה {count, plural, one {הודעה אחת} other {# הודעות}}. למחוק את התיקייה ואת כל ההודעות שבה? לא ניתן לבטל פעולה זו.", - "delete_with_contents_confirm": "מחיקת התיקייה וההודעות" - }, - "calendar_event_notifications": { - "someone": "מישהו", - "invited": "{name} הזמין/ה אותך ל\"{title}\"", - "updated": "{name} עדכן/ה את \"{title}\"", - "cancelled": "{name} ביטל/ה את \"{title}\"", - "untitled": "אירוע ללא כותרת" - }, - "share_notifications": { - "someone": "מישהו", - "shared": "{name} שיתף/ה איתך את {kind} \"{object}\"", - "changed": "{name} שינה/תה את הגישה שלך ל{kind} \"{object}\"", - "revoked": "{name} הסיר/ה את הגישה שלך ל{kind} \"{object}\"", - "object": { - "folder": "התיקייה", - "calendar": "היומן", - "address_book": "פנקס הכתובות", - "files": "תיקיית הקבצים" - } - }, - "sharing": { - "title": "שתף \"{name}\"", - "description": "הענק גישה למשתמשים או קבוצות אחרות בשרת זה. השינויים יופעלו מיד.", - "no_shares": "לא משותף עם מישהו עדיין.", - "add_person": "הוסף אדם או קבוצה", - "search_placeholder": "חפש לפי שם או דוא״ל…", - "loading_principals": "טעינת משתמשים…", - "no_principals": "לא נמצאו משתמשים או קבוצות אחרים.", - "no_match": "אין התאמות.", - "remove": "הסר גישה", - "group": "קבוצה", - "share_added": "גישה ניתנה", - "share_updated": "גישה עודכנה", - "share_removed": "גישה הוסרה", - "share_failed": "נכשל בעדכון שיתוף", - "preset": { - "freeBusy": "חופשי/תפוס בלבד", - "read": "קריאה בלבד", - "readWrite": "קרא וכתוב", - "manager": "מנהל", - "custom": "מותאם אישית" - } - }, - "unified_mailbox": { - "search_unavailable": "חיפוש אינו זמין בתצוגה המאוחדת" - }, - "quote_header": { - "reply_line": "ב־{date}, {from} כתב:", - "forwarded_separator": "---------- הודעה קדימה ----------", - "from_label": "מ", - "date_label": "תאריך", - "subject_label": "נושא" - }, - "pwa_install": { - "title": "התקן {appName}", - "description": "התקן את היישום שלנו לגישה מהירה ותמיכה בלא־אינטרנט.", - "not_now": "לא עכשיו", - "install": "התקן", - "dont_remind": "אל תזכיר לי שוב", - "dismiss_aria": "בטל הודעת התקנה" - }, - "deep_link": { - "message_not_found": "הודעה זו אינה זמינה עוד.", - "thread_not_found": "שיחה זו אינה זמינה עוד.", - "folder_not_found": "תיקייה זו אינה זמינה עוד.", - "event_not_found": "אירוע זה אינו זמין עוד.", - "file_not_found": "קובץ זה אינו זמין עוד.", - "account_unavailable": "היכנס לחשבון הזה כדי לפתוח את הקישור.", - "copy_message": "העתק קישור להודעה", - "copy_conversation": "העתק קישור לשיחה", - "copy_event": "העתק קישור לאירוע", - "copied": "הקישור הועתק", - "copy_failed": "לא ניתן להעתיק את הקישור" - }, - "plugins": { - "oauth_callback": { - "completing": "משלים אימות…", - "completing_message": "אנא המתן בזמן יצירת החיבור.", - "received_title": "האימות התקבל", - "received_message": "באפשרותך לחזור לכרטיסייה שבה התחלת את החיבור.", - "failed_title": "האימות נכשל", - "denied": "האימות נדחה.", - "failed_detail": "האימות נכשל: {error}", - "missing_params": "חסר קוד אימות או מצב.", - "delivery_failed": "לא ניתן היה למסור את תוצאת האימות לכרטיסיית האפליקציה. סגרו חלון זה ונסו שוב." - } - }, - "global_search": { - "title": "חיפוש בכל מקום", - "placeholder": "חיפוש בדואר, אנשי קשר, יומן, קבצים...", - "scope_label": "חיפוש ב", - "account_label": "חשבון", - "scope_all": "הכל", - "scope_mail": "דוֹאַר", - "scope_contacts": "אנשי קשר", - "scope_calendar": "לוּחַ שָׁנָה", - "scope_files": "קבצים", - "all_accounts": "כל החשבונות", - "show_all": "הצגת כל התוצאות", - "no_results": "אין תוצאות עבור “{query}”", - "server_hint": "מילים שלמות מחפשות בשרת; חלקי מילים תואמים פריטים שכבר נטענו", - "searching": "מחפש...", - "account_error": "לא ניתן היה לחפש ב-{account}", - "recent_searches": "חיפושים אחרונים", - "recurring": "חוזר", - "no_subject": "(no subject)", - "empty_hint": "חפשו דואר, אנשי קשר, אירועים וקבצים בכל החשבונות. נסו: from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "שטוח לפי תאריך", - "grouped": "מקובץ", - "load_more": "טען עוד", - "preview_empty": "בחרו תוצאה לתצוגה מקדימה", - "open_full": "פתיחה", - "view_label": "תצוגה" - } -} +{ + "login": { + "title": "Webmail", + "username_label": "דוא״ל", + "username_placeholder": "user@example.com", + "password_label": "סיסמה", + "password_placeholder": "הזן את הסיסמה שלך", + "jmap_endpoint_label": "שרת JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "השרת חייב לאפשר בקשות CORS מהדומיין הזה.", + "sign_in": "היכנס", + "signing_in": "נכנס...", + "loading": "טוען...", + "reconnecting": "החיבור אבד. מנסה להתחבר מחדש...", + "error": { + "invalid_credentials": "אימייל או סיסמה לא חוקיים. אנא בדוק את האישורים שלך ונסה שוב.", + "invalid_token": "השרת לא קיבל את אסימון הגישה הזה. בדוק שהוא הועתק במלואו, שתוקפו לא פג ושהוא לא בוטל.", + "connection_failed": "לא ניתן להגיע לשרת. בדוק את חיבור האינטרנט שלך ונסה שוב.", + "cors_blocked": "השרת נגיש אך חוסם בקשות חוצות מוצא. בדוק את הגדרות CORS של שרת ה-JMAP שלך ואפשר את הדומיין הזה.", + "server_error": "השרת אינו זמין באופן זמני. אנא נסה שוב מאוחר יותר.", + "generic": "אירעה שגיאה בלתי צפויה. אם זה נמשך, פנה למנהל המערכת שלך.", + "totp_required": "נדרש קוד אימות דו-גורמי. אנא הזן את הקוד שלך למטה.", + "totp_invalid": "קוד אימות לא חוקי. בדוק את אפליקציית המאמת שלך ונסה שוב.", + "oauth_discovery_failed": "SSO מופעל אך לא ניתן היה להשיג את ספק הזהות. בדוק את תצורת ה-OAuth שלך.", + "token_exchange_failed": "הסיסמה והקוד התקבלו, אך שרת הדואר סירב לפתוח הפעלה עבור האפליקציה הזו. בקשו ממנהל המערכת לבדוק את הגדרות לקוח ה-OAuth." + }, + "show_password": "הצג סיסמה", + "hide_password": "הסתר את הסיסמה", + "totp_toggle": "יש לי קוד 2FA", + "token_toggle": "כניסה באמצעות אסימון גישה", + "password_toggle": "כניסה באמצעות סיסמה", + "token_label": "אסימון גישה", + "token_placeholder": "הדבק את אסימון ה-API שלך", + "token_hint": "צור אסימון API בהגדרות של ספק הדואר שלך והדבק אותו כאן.", + "remember_me": "תזכור אותי", + "config_error": { + "title": "שגיאת תצורה", + "fetch_failed": "לא ניתן לטעון את תצורת האפליקציה. אנא נסה שוב מאוחר יותר.", + "server_not_configured": "שרת הדואר לא הוגדר. אנא פנה למנהל המערכת שלך." + }, + "remove_from_history": "הסר מההיסטוריה", + "totp_label": "קוד אימות", + "totp_placeholder": "000000", + "session_expired": "פג תוקף ההפעלה שלך. נא להיכנס שוב.", + "dismiss": "סגור", + "or": "אוֹ", + "sign_in_sso": "היכנס באמצעות SSO", + "add_account_title": "הוסף חשבון", + "add_account_subtitle": "היכנס עם חשבון אחר", + "cancel": "ביטול", + "website": "אתר", + "imprint": "פרטי מוציא לאור", + "privacy_policy": "מדיניות פרטיות", + "try_demo": "נסה הדגמה", + "demo_description": "חקור עם נתונים לדוגמה - אין צורך בחשבון", + "demo_launching": "משיק הדגמה...", + "demo_login_button": "הפעל הדגמה", + "demo_tagline": "התנסה בלקוח דוא\"ל מלא. אין צורך בחשבון.", + "demo_no_signup": "אין צורך בהרשמה - חקור בחופשיות עם נתונים לדוגמה", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "משלים כניסה...", + "oauth_error": { + "title": "האימות נכשל", + "invalid_state": "אימות האבטחה נכשל. אנא נסה להיכנס שוב.", + "missing_params": "חסרים נתוני הרשאה. אנא נסה להיכנס שוב.", + "token_exchange_failed": "השלמת האימות נכשלה. אנא נסה שוב.", + "access_denied": "הגישה נדחתה. אנא פנה למנהל המערכת שלך.", + "back_to_login": "חזרה לכניסה" + }, + "jmap_server_label": "שרת", + "jmap_server_auto_picked": "שרת שנבחר מדומיין הדוא״ל שלך." + }, + "sidebar": { + "unified_mailbox": "תיבת דואר מאוחדת", + "close": "סגור", + "compose": "כתיבה", + "compose_hint": "כתיבה (c)", + "search_placeholder": "חפש בדואר...", + "search_placeholder_hint": "חפש דואר... (הקש /)", + "storage": "אחסון", + "storage_used": "בשימוש", + "storage_free": "פנוי", + "storage_total": "סה״כ", + "sign_out": "צא", + "sign_out_of": "צא מ-{account}", + "sign_out_all": "צא מכל החשבונות", + "add_account": "הוסף חשבון", + "set_as_default": "הגדר כברירת מחדל", + "switch_account": "החלף חשבון", + "contacts": "אנשי קשר", + "calendar": "לוּחַ שָׁנָה", + "settings": "הגדרות", + "admin": "מנהל מערכת", + "files": "קבצים", + "loading_mailboxes": "טוען תיבות דואר...", + "push_connected": "עדכונים בזמן אמת פעילים", + "push_disconnected": "עדכונים בזמן אמת אינם פעילים", + "keyboard_shortcuts": "קיצורי מקשים", + "theme": { + "light": "מצב בהיר", + "dark": "מצב כהה", + "system": "ערכת נושא של המערכת" + }, + "language": { + "title": "שפה" + }, + "mailboxes": { + "inbox": "דואר נכנס", + "sent": "נשלח", + "drafts": "טיוטות", + "trash": "אשפה", + "archive": "ארכיון", + "starred": "מסומן בכוכב", + "all_mail": "כל הדואר", + "spam": "ספאם", + "important": "חשוב" + }, + "expand": "לְהַרְחִיב", + "collapse": "הִתמוֹטְטוּת", + "expand_tooltip": "לְהַרְחִיב", + "collapse_tooltip": "הִתמוֹטְטוּת", + "mobile": { + "search": "לְחַפֵּשׂ", + "compose": "לְהַלחִין", + "go_back": "לַחֲזוֹר", + "toggle_menu": "החלף תפריט" + }, + "clear_search": "נקה חיפוש", + "vacation_active": "משיב חופשה פעיל", + "demo_banner": "מצב הדגמה", + "demo_reset": "אִתחוּל", + "demo_tour": "סִיוּר", + "tags": "תגים", + "show_all_tags": "הצג הכל ({count})", + "show_fewer_tags": "הצג פחות", + "folders": "תיקיות", + "your_folders": "התיקיות שלך", + "create_folder": "יצירת תיקייה", + "show_more": "עוד", + "show_fewer": "פחות", + "mail": "דוֹאַר", + "nav_label": "ניווט", + "add_app": "אפליקציות", + "unified_inbox": "תיבת דואר מאוחדת", + "unified_sent": "הכל נשלח", + "unified_drafts": "הכל טיוטות", + "unified_trash": "הכל אשפה", + "unified_archive": "הכל ארכיון", + "unified_junk": "הכל דואר זבל", + "all_accounts": "כל החשבונות", + "all_accounts_loading": "טוען חשבונות: {loaded} מתוך {total}", + "shared": "משותף", + "shared_account_hidden": "הוסתר מסרגל הצד. אפשר להציג שוב בהגדרות › פריסה.", + "notifications_muted": "ההתראות מושתקות", + "scheduled": "מתוכננים", + "unified_all_unread": "הכל שלא נקראו", + "unified_all_starred": "הכל מסומנים", + "unified_all_mail": "הכל דוא״ל", + "remove_account": "הסרת חשבון", + "remove_account_confirm": "להסיר את {account} מהמכשיר הזה? ניתן להוסיף אותו שוב מאוחר יותר.", + "labels": "תוויות", + "create_label": "יצירת תווית חדשה", + "mv_tasks": "משימות", + "mv_side_panel": "חלונית צד", + "mv_open_full": "פתיחה בתצוגה מלאה", + "mv_previous_day": "היום הקודם", + "mv_next_day": "היום הבא", + "mv_add_another_account": "הוספת חשבון נוסף", + "mv_manage_account": "ניהול החשבון", + "mv_storage_used": "נוצלו {percent}% מתוך {total}" + }, + "sidebar_apps": { + "modal_title": "אפליקציות בסרגל הצד", + "add_new": "הוסף אפליקציה", + "edit_app": "ערוך אפליקציה", + "name_label": "שֵׁם", + "name_placeholder": "האפליקציה שלי", + "name_required": "נדרש שם", + "url_label": "כתובת אתר", + "url_required": "נדרשת כתובת URL", + "url_invalid": "הזן כתובת URL חוקית של http או https", + "icon_label": "סמל", + "icon_required": "סמל נדרש", + "open_mode_label": "מצב פתוח", + "open_new_tab": "כרטיסייה חדשה", + "open_inline": "מוטבע", + "cancel": "לְבַטֵל", + "add": "לְהוֹסִיף", + "update": "לְעַדְכֵּן", + "delete": "לִמְחוֹק", + "delete_confirm_title": "מחק אפליקציה", + "delete_confirm": "האם אתה בטוח שברצונך למחוק את \"{name}\"?", + "no_apps": "עדיין לא נוספו אפליקציות", + "no_apps_hint": "הוסף אפליקציות וקישורים מותאמים אישית לסרגל הצד שלך", + "managed_title": "סופקו על ידי מנהל המערכת", + "managed_description": "אפליקציות אלו מוגדרות עבור כולם ולא ניתן לשנות אותן.", + "managed_badge": "מנוהלת", + "search_icons": "חיפוש אייקונים...", + "show_popular": "פּוֹפּוּלָרִי", + "show_all": "כֹּל", + "no_icons_found": "לא נמצאו אייקונים", + "inline_badge": "מוטבע", + "tab_badge": "כרטיסייה", + "show_on_mobile": "הצג בנייד" + }, + "email_list": { + "columns": { + "from": "מאת", + "to": "אל", + "subject": "נושא", + "date": "תאריך", + "size": "גודל" + }, + "no_emails": "לא נמצאו הודעות", + "no_emails_description": "תיבת הדואר הזו ריקה", + "no_search_results": "לא נמצאו תוצאות", + "no_search_results_description": "נסה להתאים את החיפוש או המסננים שלך", + "loading": "טוען אימיילים...", + "unread": "לא נקרא", + "to_me": "לִי", + "to_recipients": "לנמעני{count}", + "and_others": "ו-{count}אחרים", + "draft": "טְיוּטָה", + "starred": "מסומן בכוכב", + "pinned": "מוצמד", + "replied": "נענה", + "forwarded": "הועבר", + "has_attachment": "כולל קובץ מצורף", + "conversations_range": "{from}–{to, number} מתוך {total, number}", + "conversations_count": "{count}מתוך שיחות{total}", + "conversations_count_plus": "{count}+ שיחות", + "conversations_count_simple": "שיחות{count}", + "no_conversations": "אין שיחות", + "loading_more": "טוען אימיילים נוספים...", + "no_more_emails": "אין יותר מיילים לטעינה", + "batch_actions": { + "select": "בחר אימיילים", + "select_all": "בחר הכל", + "mark_read": "סמן כנקרא", + "mark_unread": "סמן כלא נקרא", + "delete": "לִמְחוֹק", + "delete_confirm_title": "מחק מיילים", + "delete_confirm_message": "האם למחוק {count, plural, one {הודעת דוא״ל אחת} other {# הודעות דוא״ל}}?", + "clear_selection": "נקה בחירה", + "selected_messages": "{count, plural, one {דוא״ל 1} other {# דוא״ל}} שנבחרו" + }, + "permanent_delete": "מחק לצמיתות", + "permanent_delete_confirm_title": "מחק לצמיתות", + "permanent_delete_confirm_message": "אימייל זה יימחק לצמיתות. לא ניתן לבטל פעולה זו.", + "permanent_delete_confirm_batch_message": "{count, plural, one {הודעת דוא״ל אחת תימחק לצמיתות} other {# הודעות דוא״ל יימחקו לצמיתות}}. לא ניתן לבטל פעולה זו.", + "empty_folder": { + "button": "רוקן תיקייה", + "confirm_title": "רוקן תיקייה", + "confirm_message": "כל הודעות האימייל בתיקייה זו יימחקו לצמיתות. לא ניתן לבטל פעולה זו.", + "confirm_message_trash": "כל הודעות הדוא״ל בתיקייה זו יועברו לאשפה.", + "confirm_button": "רוקן תיקייה", + "junk_hint": "אתה יכול לרוקן את תיקיית הזבל כדי להסיר לצמיתות את כל ההודעות.", + "trash_hint": "אתה יכול לרוקן את תיקיית האשפה כדי להסיר לצמיתות את כל ההודעות." + }, + "no_scheduled_emails": "אין דוא״ל מתוכננים", + "no_scheduled_emails_description": "הודעות המתוכננות למועד מאוחר יוצגו כאן.", + "scheduled_count": "{count, plural, one {דוא״ל 1 מתוכנן} other {# דוא״ל מתוכננים}}", + "scheduled_actions_hint": "הודעות מתוכננות יכולות להיות מבוטלות, מעודכנות או ערוכות מפעולות המתוכננות שלהן.", + "cancel_scheduled_send": "ביטול שליחה", + "reschedule_send": "תזמן מחדש", + "cancel_and_edit": "בטל וערוך", + "cancel_and_compose_again": "בטל והרכיב שוב", + "reschedule_prompt": "הזן תאריך/שעה חדש, למשל 2026-05-04T15:30" + }, + "email_viewer": { + "no_email_selected": "לא נבחר אימייל", + "no_email_description": "בחר אימייל מהרשימה כדי להציג אותו כאן", + "no_conversation_selected": "לא נבחרה שיחה", + "no_conversation_description": "בחר שיחה מהרשימה כדי לקרוא אותה כאן", + "compose": "כתיבה", + "compose_hint": "כתוב הודעה חדשה", + "no_subject": "(ללא נושא)", + "loading_email": "טוען אימייל...", + "loading": "טְעִינָה...", + "reply": "השב", + "reply_all": "השב לכולם", + "forward": "העבר", + "delete": "מחיקה", + "archive": "ארכיון", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "כוכב", + "unstar": "הסר כוכב", + "mark_unread": "סמן כלא נקרא", + "mark_read": "סמן כנקרא", + "print": "הדפס", + "view_source": "צפה במקור", + "fullscreen": "מסך מלא", + "exit_fullscreen": "יציאה ממסך מלא", + "view_in_dark_mode": "הצגה במצב כהה", + "view_in_light_mode": "הצגה במצב בהיר", + "export_email": "ייצא כ-.eml", + "forward_as_attachment": "העבר כקובץ מצורף", + "import_email": "ייבוא .eml", + "keyboard_shortcuts": "קיצורי מקשים (?)", + "email_source": "מקור דוא\"ל", + "draft_banner": "הודעה זו היא טיוטה", + "edit_draft": "ערוך", + "copy_source": "העתק ללוח", + "source_copied": "המקור הועתק ללוח", + "attachments": "קבצים מצורפים", + "important": "חָשׁוּב", + "download": "הורד", + "download_all": "הורד הכל", + "from": "מאת", + "to": "אל", + "cc": "עותק", + "bcc": "עותק מוסתר", + "date": "תאריך", + "subject": "נושא", + "show_details": "הצג פרטים", + "hide_details": "הסתר פרטים", + "external_content_warning": "תמונות ותוכן חיצוני נחסמו", + "load_external_content": "טען תמונות", + "trust_sender": "בטח תמיד בשולח הזה", + "sender_check": { + "unverified_label": "שולח לא מאומת", + "failed_label": "בדיקת השולח נכשלה", + "unverified": "ההודעה הזו טוענת שהיא מהדומיין {domain}, אבל שום דבר לא מאשר זאת. אין בה חתימה תקפה של {domain} והיא לא הגיעה משרת שמורשה על ידי {domain}.", + "unverified_sent_from": "ההודעה הזו טוענת שהיא מהדומיין {domain}, אבל היא נשלחה מהדומיין {host} ואין בה חתימה תקפה של {domain}.", + "failed": "ההודעה הזו טוענת שהיא מהדומיין {domain}, אבל היא נכשלה בבדיקות השולח של הדומיין הזה. ייתכן שהיא מזויפת.", + "failed_sent_from": "ההודעה הזו טוענת שהיא מהדומיין {domain}, אבל היא נשלחה מהדומיין {host} ונכשלה בבדיקות השולח של {domain}. ייתכן שהיא מזויפת.", + "caution": "היזהר מקישורים, מקבצים מצורפים ומבקשות לסיסמאות או לפרטי תשלום." + }, + "back_to_list": "חזרה לרשימה", + "view_contact": "הצג איש קשר", + "message_details": "פרטי הודעה", + "more_reply_options": "אפשרויות תשובה נוספות", + "set_tag": "הגדר תג", + "tag": "תג", + "more_actions": "עוד פעולות", + "previous": "הקודם", + "next": "הבא", + "move_to": "העבר ל...", + "remove_tag": "הסר תג", + "tag_filter_placeholder": "סינון תגים", + "tag_no_matches": "אין תגים תואמים", + "more_count": "+{count}נוספים", + "characters_count": "{count} תווים", + "quick_reply_placeholder": "תשובה מהירה", + "more_options": "אפשרויות נוספות", + "sending": "שולח...", + "security_authentication": "אבטחה ואימות", + "technical_details": "פרטים טכניים", + "message_id_label": "מזהה הודעה:", + "reply_to_label": "תגובה ל:", + "delivery_time_label": "זמן אספקה:", + "conversation_part_label": "חלק מהשיחה:", + "previous_messages": "{count}הודעה קודמת", + "previous_messages_plural": "{count}הודעות קודמות", + "time": { + "day": "יום", + "days": "ימים", + "hour": "שעה", + "hours": "שעות", + "minute": "דקה", + "minutes": "דקות" + }, + "unknown_sender": "לא ידוע", + "recipient_me": "אותי", + "recipient_and_others": "{name}ו-{count}אחרים", + "recipient_to_prefix": "אל:", + "authentication": { + "title": "אימות", + "status": { + "verified": "מאומת", + "warning": "אזהרה", + "none": "לא מאומת" + }, + "spf": { + "pass": "SPF עבר", + "fail": "SPF נכשל", + "none": "אין SPF" + }, + "dkim": { + "pass": "DKIM תקין", + "fail": "DKIM לא תקין", + "none": "אין DKIM" + }, + "dmarc": { + "pass": "DMARC עבר", + "fail": "DMARC נכשל", + "none": "אין DMARC" + }, + "spam_score": "ציון ספאם", + "tooltip_spf": "Sender Policy Framework: מאמת שהשרת השוגר מורשה לשלוח דוא״ל בשם התחום", + "tooltip_dkim": "DomainKeys Identified Mail: מאשר שהדוא״ל לא שונה בדרך באמצעות חתימה קריפטוגרפית", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: מבטיח שSPF וDKIM יישתרו עם התחום של השולח וקובע מדיניות לכשלים", + "policy": "מדיניות", + "result": { + "pass": "עבר", + "fail": "נכשל", + "softfail": "כישלון רך", + "neutral": "ניטרלי", + "permerror": "שגיאה קבועה", + "temperror": "שגיאה זמנית", + "none": "ללא" + } + }, + "headers": { + "routing": "ניתוב", + "received": "התקבל", + "message_id": "מזהה הודעה", + "list_info": "רשימת מידע" + }, + "tooltips": { + "reply": "תשובה (ר)", + "reply_all": "השב לכולם (א)", + "forward": "קדימה (ו)", + "archive": "ארכיון (ה)", + "delete": "מחק (# או דל)", + "star": "כוכב (ים)", + "unstar": "הסר כוכב (ים)", + "compose": "חיבור (ג)", + "previous": "אימייל קודם", + "next": "המייל הבא", + "edit_draft": "ערוך טיוטה" + }, + "spam": { + "button_title": "דווח על ספאם", + "not_spam_title": "סמן כלגיטימי", + "toast_success": "עבר לזבל", + "toast_batch": "הודעות דוא\"ל{count}הועברו ל-Junk", + "toast_undo": "בטל", + "toast_not_spam_success": "הועבר לתיבת הדואר הנכנס", + "toast_not_spam_batch": "הודעות דוא\"ל{count}הועברו ל-Inbox", + "error": "נכשל הדיווח על ספאם", + "error_not_spam": "שחזור האימייל נכשל" + }, + "unsubscribe_banner": { + "label": "ניוזלטר", + "button": "בטל את המנוי", + "confirm_title": "לבטל את המנוי מהשולח הזה?", + "confirm_button": "אשר", + "cancel": "לְבַטֵל", + "success_http": "דף ביטול הרשמה נפתח בכרטיסייה חדשה", + "success_mailto": "אימייל ביטול ההרשמה נשלח", + "confirm_message_http": "דף ביטול ההרשמה ייפתח בכרטיסייה חדשה.", + "confirm_message_mailto": "אימייל ביטול הרשמה יישלח לשולח.", + "error": "לא ניתן לבטל את המנוי", + "dismiss": "לְפַטֵר" + }, + "calendar_invitation": { + "loading": "טוען את פרטי האירוע...", + "title": "הזמנה ללוח שנה", + "published_title": "אירוע שפורסם", + "response_title": "תגובה לאירוע", + "update_title": "עדכון אירוע", + "counter_title": "הצעה נגדית", + "refresh_title": "רענן בקשה", + "declined_counter_title": "ההצעה הנגדית נדחתה", + "cancelled_title": "האירוע בוטל", + "organizer": "מאורגן על ידי{name}", + "attendees": "{count, plural, one {משתתף אחד} other {# משתתפים}}", + "accept": "קבל", + "maybe": "אולי", + "decline": "דחה", + "add_to_calendar": "הוסף ליומן", + "added": "נוסף ליומן", + "rsvp_sent": "נשלחה תגובה", + "parse_error": "לא ניתן היה לקרוא את ההזמנה", + "action_failed": "לא ניתן היה להשלים את פעולת היומן הזו.", + "no_calendar": "לוח השנה אינו זמין", + "published_info": "אירוע זה שותף לעיון.", + "response_info": "הודעה זו מכילה תגובת משתתף.", + "response_info_organizer": "תגובת המשתתפים הזו מעדכנת את האירוע שלך.", + "update_info": "הודעה זו מעדכנת אירוע קיים.", + "counter_info": "הודעה זו מציעה שינויים באירוע.", + "counter_info_organizer": "משתתף זה הציע שינויים באירוע שלך.", + "refresh_info": "הודעה זו מבקשת את פרטי האירוע העדכניים ביותר.", + "refresh_info_organizer": "משתתף ביקש את פרטי האירוע העדכניים ביותר.", + "declined_counter_info": "המארגן דחה הצעה נגדית.", + "authentication_failed_info": "בדיקות אימות דואר עבור הזמנה זו נכשלו. התייחסו לפעולות לוח שנה בזהירות.", + "authentication_missing_info": "הזמנה זו אינה כוללת אימות דואר מאומת. אשר את הפרטים עם המארגן אם משהו נראה חריג.", + "sender_mismatch_info": "הזמנה זו נשלחה מ-{sender}, בעוד שהמארגן הרשום בנתוני היומן הוא{organizer}.", + "sender_mismatch_unverified_info": "הזמנה זו נשלחה מ-{sender}, בעוד שהמארגן הרשום בנתוני היומן הוא{organizer}, ולא ניתן לאמת את ההודעה.", + "organizer_role": "אתה מארגן את האירוע הזה", + "your_response": "תגובתך:{status}", + "response_needed": "צריך תגובה", + "response_accepted": "התקבל", + "response_tentative": "טנטטיבי", + "response_declined": "נדחה", + "response_delegated": "האצלה", + "actor_sent_info": "נשלח על ידי{name}.", + "actor_response_info": "{name}הגיב{status}.", + "actor_counter_info": "{name}הציע שינויים לאירוע זה.", + "actor_refresh_info": "{name}ביקש את פרטי האירוע העדכניים ביותר.", + "actor_declined_counter_info": "{name}דחה את ההצעה הנגדית.", + "actor_note": "הערה:{comment}", + "actor_unknown": "מישהו", + "proposed_changes": "שינויים מוצעים", + "change_title": "כותרת", + "change_time": "זמן", + "change_location": "מיקום", + "change_description": "תיאור", + "change_empty": "אין", + "change_from_to": "{before}->{after}", + "apply_proposal": "החל את השינויים המוצעים", + "proposal_applied": "השינויים שהוצעו הוחלו.", + "review_proposal": "בדוק את ההצעה", + "review_request": "בדיקת בקשה", + "view_in_calendar": "הצג בלוח שנה", + "select_calendar": "בחר לוח שנה", + "already_in_calendar": "כבר בלוח השנה שלך", + "request_info": "הוזמנת לאירוע זה. השב כדי ליידע את המארגן על זמינותך.", + "cancel_info": "המארגן ביטל את האירוע הזה.", + "event_updated": "עדכון #{sequence}", + "event_status_tentative": "טנטטיבי", + "event_status_cancelled": "בוטל", + "organizer_label": "מוארגן על ידי", + "expand": "הצג פרטים", + "collapse": "הסתר פרטים" + }, + "send": "שלח", + "more": "יותר", + "read_receipt": { + "prompt": "השולח ביקש קבלת דואר:", + "send": "שלח קבלה", + "ignore": "התעלם", + "sent": "קבלת דואר נשלחה.", + "send_failed": "לא היה ניתן לשלוח קבלת דואר", + "mdn_subject": "קרא: {subject}", + "mdn_body": "זו קבלת חזרה עבור ההודעה ששלחת אל {recipient}.\n\nהערה: קבלה זו רק מאשרת שההודעה הוצגה בהן של המקבל. אין ערובה שהמקבל קרא או הבין את תוכן ההודעה." + }, + "no_body_content": "(אין תוכן גוף זמין)", + "no_preview_available": "אין תצוגה מקדימה זמינה", + "show_quoted_text": "הצג טקסט מצוטט", + "hide_quoted_text": "הסתר טקסט מצוטט", + "unread": "סמן כלא נקרא", + "read": "קרא", + "spam_short": "דואר זבל", + "not_spam_short": "לא דואר זבל", + "move": "העברה", + "details": { + "recipients_routing": "נמענים ו־ניתוב", + "authentication_security": "אימות וביטחון", + "identifiers_threading": "מזהים ושרשורים", + "mailing_list": "רשימת תפוצה", + "message_properties": "מאפייני הודעה", + "sent": "נשלח", + "received": "התקבל", + "delivery_time": "זמן הסגרה", + "in_reply_to": "In-Reply-To", + "references": "הפניות", + "thread_id": "מזהה חוט", + "size": "גודל", + "mime_type": "סוג MIME", + "attachments_summary": "{count} קבצים · {size}", + "list_id": "מזהה רשימה", + "list_help": "עזרה לרשימה", + "list_post": "פוסט ברשימה", + "list_unsubscribe": "בטל הרשמה", + "iprev": "DNS הפוך", + "spam_status": "סטטוס דואר זבל", + "ai_verdict": "פסק דין AI", + "account": "חשבון", + "no_subject": "(ללא נושא)" + }, + "scheduled_banner": "מתוכנן לשליחה ב־{date}", + "scheduled_send_created": "דוא״ל תוכנן לשליחה", + "sending_in": "שליחה בעוד {seconds} שנ'", + "undo_send_failed": "לא ניתן לבטל. ייתכן שההודעה כבר נשלחה.", + "cancel_scheduled_send": "ביטול שליחה", + "reschedule_send": "תזמן מחדש", + "cancel_and_edit": "בטל וערוך", + "cancel_and_compose_again": "בטל והרכיב שוב", + "reschedule_prompt": "הזן תאריך/שעה חדש, למשל 2026-05-04T15:30", + "scheduled_actions_only": "תצוגה מתוכננת תומכת בפעולות מתוכננות בלבד", + "undo_send_scheduled": "הודעה תוכננת לשליחה", + "undo_send": "בטל שליחה", + "contact_sidebar": { + "title": "איש קשר", + "close": "סגור סרגל צד", + "action_email": "דוא״ל", + "action_email_title": "שלח דוא״ל", + "action_copy": "העתק", + "action_copy_title": "העתק דוא״ל", + "action_edit_title": "ערוך איש קשר", + "section_emails": "דוא״ל", + "section_phones": "טלפונים", + "section_organizations": "ארגונים", + "section_addresses": "כתובות", + "section_notes": "הערות", + "not_in_contacts": "לא בקשרים שלך", + "add_to_contacts": "הוסף לקשרים", + "copied": "הועתק!", + "copy_failed": "העתקה נכשלה", + "view_contact": "הצגת איש הקשר", + "view_details": "הצגת פרטים" + }, + "send_now": "שלח עכשיו", + "verification_code": { + "copy": "העתקת הקוד {code}", + "copied": "הקוד הועתק", + "copy_failed": "לא ניתן להעתיק את הקוד" + }, + "mv_position": "{index} מתוך {total}", + "plugin_panel_show": "הצגת חלונית התוסף", + "plugin_panel_hide": "הסתרת חלונית התוסף" + }, + "email_composer": { + "select_from_directory": "בחירה מהספרייה", + "minimize": "מזעור", + "maximize": "מסך מלא", + "restore": "שחזור", + "new_message": "הודעה חדשה", + "signature_edit_hint": "לחץ לחיצה כפולה לעריכת החתימה", + "reply": "השב", + "reply_all": "השב לכולם", + "forward": "קדימה", + "reply_to": "השב", + "reply_all_to": "השב לכולם", + "forward_message": "קדימה", + "from": "מאת", + "to": "אל", + "cc": "עותק", + "bcc": "עותק מוסתר", + "subject": "נושא", + "body_placeholder": "כתוב את הודעתך...", + "send": "שלח", + "cancel": "ביטול", + "attach": "צרף", + "discard": "מחק טיוטה", + "discard_draft_title": "למחוק את הטיוטה?", + "discard_draft_confirm": "יש לך שינויים שלא נשמרו. האם ברצונך למחוק את הטיוטה הזו?", + "discard_draft_message": "הטיוטה תימחק.", + "saving": "שומר...", + "draft_saved": "הטיוטה נשמרה", + "save_failed": "השמירה נכשלה", + "to_placeholder": "כתובות אימייל של נמענים", + "cc_placeholder": "נמענים עותק", + "bcc_placeholder": "נמעני עותק מוסתר", + "subject_placeholder": "נושא", + "cc_label": "עותק:", + "bcc_label": "עותק מוסתר:", + "subject_label": "נושא:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {הודעת דוא״ל אחת} other {# הודעות דוא״ל}}", + "no_subject": "(ללא נושא)", + "unknown_sender": "לא ידוע", + "quote": { + "reply_header": "ב-{date},{sender}כתב:", + "forward_header": "---------- הודעה הועברה ----------", + "from": "מאת:{sender}", + "date": "תאריך:{date}", + "subject": "נושא:{subject}", + "to": "אל:{recipients}" + }, + "remove_sub_address": "הסר כתובת משנה", + "save_as_template": "שמור כתבנית", + "attach_from_device": "העלאה מהמכשיר הזה", + "templates_menu": "תבניות", + "insert_template": "הוספת תבנית", + "options": "אפשרויות", + "option_plain_text": "טקסט פשוט", + "option_plain_text_hint": "שליחה ללא עיצוב", + "option_read_receipt": "בקשת אישור קריאה", + "option_read_receipt_hint": "לבקש מהנמען לאשר שפתח את ההודעה", + "option_dsn": "הודעת מסירה", + "option_dsn_hint": "לקבל דוח כשהשרת מוסר את ההודעה", + "option_require_tls": "חיוב מסירה מוצפנת", + "option_require_tls_hint": "למסור רק דרך TLS, אחרת להחזיר", + "option_priority": "עדיפות", + "option_priority_high": "גבוהה", + "option_priority_normal": "רגילה", + "option_priority_low": "נמוכה", + "validation": { + "recipient_required": "הוסף נמען לשליחה", + "body_required": "כתוב הודעה או צרף קובץ", + "invalid_recipients": "לא נשלח - בדקו את הכתובות האלה: {addresses}", + "attachments_uploading": "קבצים מצורפים עדיין בהעלאה - ההודעה תישלח מיד בסיום", + "attachment_upload_failed": "לא נשלח - העלאת קובץ מצורף נכשלה. הסירו אותו ונסו שוב." + }, + "upload_progress": "מעלה את{uploaded}/{total}", + "upload_cancel": "בטל העלאה", + "upload_failed": "העלאת{filename}נכשלה", + "attachment_too_large": "\"{name}\" גדול ממה שהשרת מאפשר ({max} לקובץ)", + "attachments_total_too_large": "הקבצים המצורפים יחרגו ממגבלת השרת של {max} להודעה", + "drop_files": "שחרר קבצים לצירוף", + "show_less": "הצג פחות", + "send_failed": "שליחת האימייל נכשלה", + "send_timeout": "השרת הפסיק להגיב. בדקו את תיקיית הנשלחים לפני שליחה חוזרת — ייתכן שההודעה כבר נשלחה.", + "send_offline": "נראה שאין חיבור לרשת. ההודעה עדיין כאן, שלחו אותה שוב כשתחזרו לרשת.", + "formatting": "עיצוב", + "send_recipients_rejected": "לא נשלח - השרת דחה את כל הנמענים.", + "send_plugin_failed": "לא נשלח - תוסף שמטפל בשליחה נכשל או לקח יותר מדי זמן. בדקו את תיקיית דואר יוצא לפני שליחה חוזרת.", + "send_some_recipients_rejected": "נשלח, אך לא לנמענים האלה - השרת דחה אותם.", + "continue_draft": "המשך טיוטה", + "close_draft_title": "לשמור או למחוק טיוטה?", + "close_draft_message": "יש לך שינויים שלא נשמרו. האם תרצה לשמור את זה כטיוטה או למחוק אותו?", + "smime_sign_on": "חתימת S/MIME מופעלת", + "smime_sign_off": "אפשר חתימת S/MIME", + "smime_encrypt_on": "הצפנת S/MIME מופעלת", + "smime_encrypt_off": "אפשר הצפנת S/MIME", + "smime_encrypt_unavailable": "הצפנת S/MIME אינה זמינה - אישורי נמען חסרים", + "smime_unlock_title": "בטל את נעילת מפתח S/MIME", + "smime_unlock_message": "הזן את משפט הסיסמה כדי לפתוח את מפתח החתימה ב-S/MIME שלך.", + "smime_unlock_button": "פתח את הנעילה", + "smime_passphrase_placeholder": "ביטוי סיסמה", + "attach_photos": "תמונות וסרטונים", + "attach_files": "קבצים", + "attach_from_files": "צירוף מקבצים", + "files_picker": { + "attach": "{count, plural, =0 {צירוף} one {צירוף קובץ 1} other {צירוף # קבצים}}", + "empty_folder": "התיקייה הזו ריקה", + "load_failed": "לא ניתן היה לטעון את הקבצים שלך" + }, + "sending": "שולח…", + "add_link": "הוסף קישור", + "link_url_prompt": "הזן את כתובת ה־URL", + "from_override": { + "toggle_off": "עקוף", + "toggle_on": "בטל עקיפה", + "toggle_tooltip": "ערוך בחופשיות את שם וכתובת השולח. הדוא״ל עדיין נשלח דרך הזהות שלך.", + "name_label": "שם ממנו", + "name_placeholder": "שם", + "email_label": "כתובת דוא״ל ממנו", + "email_placeholder": "alias@example.com", + "envelope_notice": "אם השרת שלך לא מקבל את הכתובת הזו כשולח המעטפה, ייעשה שימוש ב-{identity} במקומה, והנמענים יכולים לראות אותה בכותרת Return-Path." + }, + "forgot_attachment": { + "title": "נשכחת קובץ מצורף?", + "message": "ההודעה שלך מתייחסת ל״{keyword}״ אך לא צורף קובץ. שלח בכל זאת?", + "send_anyway": "שלח בכל זאת", + "back": "חזור לעריכה" + }, + "empty_subject": { + "title": "לשלוח ללא נושא?", + "message": "להודעה זו אין נושא. לשלוח בכל זאת?", + "dont_ask_again": "אל תשאל שוב", + "send_anyway": "שלח בכל זאת", + "back": "חזור לעריכה" + }, + "schedule_send": "תזמן שליחה", + "schedule_send_description": "בחר מתי השרת צריך לשחרר הודעה זו.", + "schedule_preset": { + "tomorrow_morning": "מחר בבוקר", + "tomorrow_afternoon": "מחר אחר הצהריים", + "monday_morning": "יום שני בבוקר" + }, + "schedule_send_required": "בחר תאריך ושעה.", + "schedule_send_invalid": "הזן תאריך ושעה חוקיים.", + "schedule_send_future": "בחר תאריך ושעה עתידית.", + "schedule_send_too_late": "שעה זו מאוחרת יותר מכפי שהשרת מאפשר.", + "schedule_send_unsupported": "תזמון שליחה אינו נתמך עבור חשבון זה.", + "schedule_send_cleanup_warning": "תזמון שליחה נוצר, אך ניקוי טיוטה נכשל.", + "send_delay_unsupported": "השהיית שליחה אינה נתמכת עבור חשבון זה.", + "send_delay_unsupported_confirm": "חשבון זה אינו תומך בהשהיית שליחה. שלח מיד במקום זאת?", + "recipient_edit_email": "ערוך כתובת דוא״ל", + "recipient_edit_name": "ערוך שם תצוגה", + "recipient_email_placeholder": "כתובת דוא״ל", + "recipient_name_placeholder": "שם תצוגה", + "invalid_recipient": "כתובת דוא\"ל לא תקינה", + "autocomplete_search_server": "חיפוש בשרת", + "autocomplete_searching": "מחפש...", + "mention_recipients": "נמענים", + "mention_card_not_recipient": "לא נמצא ב\"אל\" או ב\"עותק\", ולכן לא יקבל את ההודעה.", + "mention_card_add": "הוספה ל{field}", + "mention_card_remove": "הסרת האזכור", + "toolbar": { + "bold": "מודגש", + "italic": "נטוי", + "underline": "קו תחתון", + "strikethrough": "קו חוצה", + "text_color": "צבע טקסט", + "remove_color": "הסרת צבע", + "heading_1": "כותרת 1", + "heading_2": "כותרת 2", + "bullet_list": "רשימת תבליטים", + "ordered_list": "רשימה ממוספרת", + "quote": "ציטוט", + "code_block": "בלוק קוד", + "align_left": "יישור לשמאל", + "align_center": "מרכוז", + "align_right": "יישור לימין", + "text_direction": "כיוון טקסט (RTL/LTR)", + "link": "קישור", + "table": "טבלה", + "clear_formatting": "ניקוי עיצוב", + "undo": "ביטול", + "redo": "ביצוע מחדש", + "add_row_above": "הוספת שורה מעל", + "add_row_below": "הוספת שורה מתחת", + "add_column_before": "הוספת עמודה לפני", + "add_column_after": "הוספת עמודה אחרי", + "delete_row": "מחיקת שורה", + "delete_column": "מחיקת עמודה", + "toggle_header_row": "החלפת שורת כותרת", + "delete_table": "מחיקת טבלה", + "pick_size": "בחירת גודל", + "font_size": "גודל גופן", + "font_size_default": "ברירת מחדל", + "background_color": "צבע רקע", + "remove_background_color": "הסר צבע רקע" + }, + "send_filing_warning": "נשלח - אך הניקוי שלאחר השליחה נכשל, ייתכן שתישאר טיוטה ישנה.", + "pop_out": "פתיחת התשובה בחלון נפרד", + "send_guard_failed": "לא נשלח - תוסף שבודק הודעות לפני השליחה נכשל או לא הגיב." + }, + "confirm_dialog": { + "confirm": "אשר", + "cancel": "לְבַטֵל" + }, + "common": { + "loading": "טוען...", + "error": "שגיאה", + "success": "הצלחה", + "cancel": "ביטול", + "save": "שמירה", + "delete": "מחיקה", + "edit": "עריכה", + "close": "סגירה", + "search": "חיפוש", + "refresh": "רענון", + "settings": "הגדרות", + "help": "עזרה", + "logout": "התנתקות", + "yes": "כן", + "no": "לא", + "unknown": "לא ידוע", + "app_title": "Webmail", + "reconnecting": "החיבור אבד. מנסה להתחבר מחדש...", + "rate_limited_title": "אימות השרת מוגבל באופן זמני בקצב.", + "rate_limited_detail": "Bulwark השהתה בקשות ברקע כדי למנוע נעילה. מנסה שוב ב-{seconds}s.", + "rate_limited_action_title": "הבקשה הושהתה כדי למנוע נעילה.", + "rate_limited_action_detail": "Bulwark ממתין עד שקירור השרת יסתיים לפני שליחת בקשות מאומתות יותר. נסה שוב ב-{seconds}s.", + "pull_to_refresh": "משוך לרענון", + "release_to_refresh": "שחרר לרענון", + "refreshing": "מרענן…" + }, + "notifications": { + "email_sent": "האימייל נשלח בהצלחה", + "email_deleted": "האימייל נמחק", + "email_archived": "האימייל הועבר לארכיון", + "emails_deleted": "{count, plural, one {הודעה אחת נמחקה} other {# הודעות נמחקו}}", + "emails_archived": "{count, plural, one {הודעה אחת הועברה לארכיון} other {# הודעות הועברו לארכיון}}", + "error_archiving": "העברת ההודעה לארכיון נכשלה", + "error_updating": "עדכון ההודעה נכשל", + "email_starred": "אימייל מסומן בכוכב", + "email_unstarred": "דוא\"ל בוטל בכוכב", + "email_marked_read": "אימייל סומן כנקראה", + "email_marked_unread": "אימייל סומן כלא נקרא", + "copied_to_clipboard": "הועתק ללוח", + "source_copied": "המקור הועתק ללוח", + "error_sending": "שליחת האימייל נכשלה", + "error_deleting": "מחיקת האימייל נכשלה", + "error_loading": "טעינת האימייל נכשלה", + "new_email": "מייל חדש", + "new_email_from": "מ-{sender}", + "click_to_view": "לחץ לצפייה", + "email_moved": "האימייל הועבר", + "emails_moved": "דוא\"ל{count}הועברו", + "moved_to_mailbox": "הועבר אל{mailbox}", + "move_failed": "המהלך נכשל", + "removed_from_folder": "הוסר מ-{mailbox}", + "remove_from_folder_failed": "לא ניתן להסיר מתיקייה זו", + "emails_copied": "{count} הודעות הועתקו", + "copy_failed": "ההעתקה נכשלה", + "move_error": "לא ניתן היה להעביר הודעות דוא\"ל לתיקיה שנבחרה", + "email_tagged": "אימייל מתויג", + "emails_tagged": "דוא\"ל{count}מתויג", + "tag_failed": "התיוג נכשל", + "identity_created": "הזהות נוצרה בהצלחה", + "identity_updated": "הזהות עודכנה בהצלחה", + "identity_deleted": "הזהות נמחקה", + "identity_set_primary": "הזהות הראשית עודכנה", + "identity_create_failed": "יצירת זהות נכשלה:{error}", + "identity_update_failed": "עדכון הזהות נכשל:{error}", + "identity_delete_failed": "מחיקת הזהות נכשלה:{error}", + "identity_unauthorized": "אינך מורשה לשלוח מכתובת דוא\"ל זו", + "identity_not_found": "זהות לא נמצאה", + "vacation_saved": "הגדרות ההיעדרות נשמרו", + "vacation_save_failed": "שמירת הגדרות ההיעדרות נכשלה", + "filters_saved": "המסננים נשמרו בהצלחה", + "filters_save_failed": "שמירת המסננים נכשלה", + "filters_deleted": "כלל הסינון נמחק", + "templates_exported": "תבניות יוצאו בהצלחה", + "templates_imported": "יובאו {count, plural, one {תבנית אחת} other {# תבניות}}", + "templates_import_errors": "לא ניתן לייבא חלק מהתבניות", + "templates_import_empty": "לא נמצאו תבניות בקובץ", + "export_email_error": "ייצוא האימייל נכשל", + "import_email_success": "אימייל יובא בהצלחה", + "import_email_error": "ייבוא האימייל נכשל", + "rule_created": "הכלל נוצר", + "rule_merged": "נוסף לכלל „{name}”", + "rule_already_covered": "כבר מכוסה על ידי הכלל „{name}”", + "rule_apply_existing": "{count, plural, one {החל על הודעה קיימת אחת} other {החל על # הודעות קיימות}}", + "rule_applied": "{count, plural, =0 {אף הודעה לא דרשה שינוי} one {הכלל הוחל על הודעה אחת} other {הכלל הוחל על # הודעות}}", + "rule_apply_failed": "לא ניתן היה להחיל את הכלל על הודעות קיימות", + "rule_edit": "ערוך כלל", + "rule_undo": "בטל", + "rule_undone": "שינוי הכלל בוטל", + "rule_undo_failed": "לא ניתן היה לבטל את שינוי הכלל", + "rule_undo_conflict": "המסננים שלך השתנו מאז. בטל את השינוי בהגדרות המסננים.", + "rule_folder_failed": "לא ניתן היה ליצור את התיקייה", + "vacation_filters_save_failed": "הגדרות ההיעדרות נשמרו, אך שמירת ההעברה ונמעני המענה נכשלה" + }, + "date": { + "today": "היום", + "yesterday": "אתמול", + "this_week": "השבוע", + "last_week": "שבוע שעבר", + "this_month": "החודש הזה", + "older": "ישן יותר", + "just_now": "רק עכשיו", + "minutes_ago": "{count}לפני דקה", + "minutes_ago_plural": "{count}לפני דקות", + "hours_ago": "{count}לפני שעה", + "hours_ago_plural": "{count}לפני שעות", + "days_ago": "{count}לפני יום", + "days_ago_plural": "{count}לפני ימים" + }, + "language": { + "title": "שפה", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "בחירת שפה", + "switch_to_english": "עבור לאנגלית", + "switch_to_french": "תעבור לצרפתית", + "switch_to_japanese": "תעבור ליפנית", + "switch_to_spanish": "עבור לספרדית", + "switch_to_italian": "תעבור לאיטלקית", + "switch_to_german": "תעבור לגרמנית", + "switch_to_dutch": "עבור להולנדית", + "switch_to_portuguese": "עבור לפורטוגזית", + "switch_to_russian": "תעבור לרוסית", + "switching": "מחליף שפה...", + "switch": "החלף שפה", + "current": "שפה נוכחית", + "polish": "Polski", + "switch_to_polish": "עבור לפולנית", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "עבור לפרסית", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "עבור למונגולית" + }, + "settings": { + "title": "הגדרות", + "back_to_mail": "חזרה לדואר", + "save_success": "ההגדרות נשמרו בהצלחה", + "import_success": "ההגדרות יובאו בהצלחה", + "import_error": "ייבוא ההגדרות נכשל", + "reset_confirm": "האם אתה בטוח שברצונך לאפס את כל ההגדרות לברירות מחדל?", + "unsaved_changes": "יש לך שינויים שלא נשמרו", + "discard_changes": "למחוק שינויים שלא נשמרו?", + "discard": "לזרוק", + "keep_editing": "תמשיך לערוך", + "tabs": { + "appearance": "מראה", + "language": "שפה ואזור", + "email": "התנהגות אימייל", + "composer": "עורך", + "privacy": "פרטיות ואבטחה", + "account": "חשבון", + "identities": "זהויות", + "vacation": "היעדרות", + "advanced": "מתקדם", + "calendar": "לוּחַ שָׁנָה", + "filters": "מסננים", + "templates": "תבניות", + "folders": "תיקיות", + "keywords": "מילות מפתח", + "security": "אבטחה", + "files": "קבצים", + "contacts": "אנשי קשר", + "encryption": "הצפנה", + "sidebar_apps": "אפליקציות בסרגל הצד", + "notifications": "התראות", + "protocol_handlers": "יישומים ברירת מחדל", + "layout": "פריסה", + "themes": "ערכות נושא", + "reading": "קריאה", + "composing": "הרכבה", + "downloads": "הורדות", + "content_senders": "תוכן ושולחים", + "about_data": "בערך וגדול", + "debug": "ניפוי שגיאות" + }, + "themes": { + "title": "ערכות נושא", + "description": "בחר מתוך ערכות הנושא שפרס מנהל המערכת ומתוך ערכות מובנות.", + "locked": "בחירת ערכת הנושא ננעלה על ידי מנהל המערכת.", + "forced": "ערכת הנושא \"{name}\" נאכפת על ידי מנהל המערכת ולא ניתן לשנות אותה", + "admin_theme": "ערכת נושא של המנהל", + "activated": "ערכת הנושא הופעלה", + "default_restored": "ערכת הנושא המוגדרת כברירת מחדל שוחזרה", + "default_name": "ברירת מחדל", + "default_badge": "ברירת מחדל", + "admin_enforced": "נאכף על ידי המנהל" + }, + "tab_groups": { + "general": "כללי", + "account": "חשבון וזהות", + "organization": "ארגון דואר", + "apps": "אפליקציות", + "system": "מערכת", + "appearance": "הופעה", + "mail": "דוא״ל", + "privacy": "פרטיות וביטחון", + "advanced": "מתקדם" + }, + "appearance": { + "message_list_order": { + "label": "סדר רשימת ההודעות", + "description": "איך ההודעות מסודרות ברשימה. המיון מתבצע בשרת, ולכן חל על כל התיקייה ולא רק על ההודעות שנטענו.", + "preset": { + "chronological": "כרונולוגי (החדשות תחילה)", + "unread_first": "שלא נקראו תחילה", + "starred_first": "מסומנות בכוכב תחילה", + "tagged_first": "מתויגות תחילה", + "custom": "מותאם אישית" + }, + "tag_label": "תגית", + "advanced_show": "מתקדם: עריכת רמות המיון", + "advanced_hide": "הסתרת מתקדם", + "criterion_label": "קריטריון רמה {n}", + "direction_label": "כיוון רמה {n}", + "criteria": { + "unread": "לא נקרא", + "starred": "מסומן בכוכב", + "tag": "תגית", + "receivedAt": "תאריך קבלה", + "sentAt": "תאריך שליחה", + "from": "שולח", + "subject": "נושא", + "size": "גודל" + }, + "direction": { + "unread_first": "שלא נקראו תחילה", + "read_first": "שנקראו תחילה", + "starred_first": "מסומנות בכוכב תחילה", + "unstarred_first": "ללא כוכב תחילה", + "tagged_first": "מתויגות תחילה", + "untagged_first": "ללא תגית תחילה", + "newest_first": "החדשות תחילה", + "oldest_first": "הישנות תחילה", + "a_to_z": "א → ת", + "z_to_a": "ת → א", + "largest_first": "הגדולות תחילה", + "smallest_first": "הקטנות תחילה" + }, + "add_level": "הוספת רמה", + "remove_level": "הסרת רמה", + "move_up": "הזזה למעלה", + "move_down": "הזזה למטה", + "tiebreak_note": "עד שלוש רמות; הרמות הבאות מכריעות במקרה של שוויון. \"החדשות תחילה\" תמיד מוחל אחרון.", + "scope": { + "label": "החלה על כל התיקיות", + "description": "כשכבוי, הסדר חל על תיבת הדואר הנכנס בלבד; שאר התיקיות נשארות כרונולוגיות." + }, + "unsupported_note": "שרת הדואר שלך אינו תומך במיון לפי מצב קריאה, כוכב או תגיות. רמות אלה מתעלמות והרשימה נשארת כרונולוגית." + }, + "title": "מראה", + "description": "התאם אישית את המראה והתחושה של דואר האינטרנט שלך", + "theme": { + "label": "נושא", + "description": "בחר את ערכת הצבעים המועדפת עליך", + "light": "בהיר", + "dark": "כהה", + "system": "מערכת" + }, + "language": { + "label": "שָׂפָה", + "description": "בחר את השפה המועדפת עליך" + }, + "font_size": { + "label": "גודל גופן", + "description": "התאם את גודל הטקסט לקריאות טובה יותר", + "small": "קטן", + "medium": "בינוני", + "large": "גדול" + }, + "list_density": { + "label": "צפיפות", + "description": "שליטה במרווחים ובריפוד על פני ממשק המשתמש", + "extra_compact": "קומפקטי במיוחד", + "compact": "קומפקטי", + "regular": "רגיל", + "comfortable": "נוח" + }, + "interface_layout": { + "label": "פריסת הממשק", + "description": "היכן ממוקמים החיפוש, הניווט וסרגלי הכלים", + "default": "ברירת מחדל", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "הפעל אנימציות", + "description": "הצג מעברים ואפקטים חלקים" + }, + "toolbar_position": { + "label": "מיקום סרגל הכלים", + "description": "היכן להציג לחצני פעולה בדוא\"ל (השב, ארכיון, מחק וכו')", + "top": "למעלה", + "below_subject": "מתחת לנושא" + }, + "toolbar_labels": { + "label": "הצג תוויות של סרגל הכלים", + "description": "הצג תוויות טקסט לצד סמלי סרגל הכלים. השבת כדי לחסוך במקום ברגע שאתה מכיר את הסמלים." + }, + "hide_account_switcher": { + "label": "הסתר את מחליף החשבונות בסרגל הצד", + "description": "הסתר את בורר החשבונות בחלק העליון של סרגל הצד של התיקיות. אתה עדיין יכול להחליף חשבונות ממסילת הניווט התחתונה." + }, + "show_rail_account_list": { + "label": "הצג אווטרים של חשבון על רכבת ניווט", + "description": "הצג עיגולי חשבון בודדים בתחתית מסילת הניווט למעבר מהיר, עם לחצן יציאה למטה." + }, + "unified_mailbox": { + "label": "תיבת דואר מאוחדת", + "description": "הצג תיקיות משולבות (דואר נכנס, נשלח וכו׳) בכל החשבונות המחוברים", + "cross_account": { + "label": "בכל החשבונות", + "description": "מזג את תיבת הדואר המאוחדת בכל החשבונות המחוברים במקום להישאר בחשבון הפעיל." + }, + "include_group": { + "label": "כלול תיבות דואר קבוצתיות", + "description": "גם מזג תיבות דואר משותפות/קבוצתיות לתצוגה המאוחדת." + } + }, + "all_mail": { + "folders_label": "תיקיות בכל הדוא״ל", + "folders_description": "בחר אילו תיקיות מוזגות לתצוגת כל הדוא״ל.", + "account_hint": "חל על {account}.", + "no_folders": "אין תיקיות זמינות." + }, + "colorful_sidebar_icons": { + "label": "סמלי סרגל צד צבעוניים", + "description": "צבע אייקונים של תיקיות וקוביות לפי סוג (כחול Inbox, אדום Junk, ירוק Sent וכו׳). השבת עבור סרגל צד חד צבע." + }, + "tint_list_rows": { + "label": "צביעת שורות הרשימה לפי צבע התגית", + "description": "צובע כל שורת הודעה בצבע התגית הראשונה שלה. כבו כדי להשאיר את השורות ללא צבע; נקודות ותוויות התגיות עדיין יציגו את הצבע." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "הצגת מספר ההודעות הכולל", + "description": "מציג את מספר ההודעות הכולל לצד תיקיות ותגיות, לצד מספר ההודעות שלא נקראו. כבו כדי להציג רק את מספר ההודעות שלא נקראו." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "הצגת חשבונות דואר משותפים", + "description": "הצגת התיקיות של תיבות דואר ששותפו איתך תחת \"משותף\" בסרגל הצד. כבה כדי להסתיר את כולן, או הסתר חשבונות בודדים למטה. לוחות השנה ואנשי הקשר שלהם נשארים." + }, + "pro_interface": { + "label": "ממשק Pro (ניסיוני)", + "description": "פריסה של משתמש כוח לשולחן עבודה בלבד עם גלישה מרובת-הודעות וזרימות עבודה חוצות-חשבונות. הממשק הסטנדרטי אינו מושפע; תוכל להחזור בכל עת.", + "open_label": "פתח ממשק Pro", + "back_to_standard": "חזור לתקן" + }, + "cross_unread": { + "label": "כל החשבונות: שלא נקראו", + "description": "הצג ערך בסעיף כל החשבונות המפרט דוא״ל שלא נקראו בכל חשבון, המשתרע על כל התיקיות למעט זבל, נשלח, ארכיון, אשפה וטיוטות." + }, + "cross_starred": { + "label": "כל החשבונות: מסומן", + "description": "הצג ערך בסעיף כל החשבונות המפרט דוא״ל מסומן בכל חשבון, המשתרע על כל התיקיות למעט זבל, נשלח, ארכיון, אשפה וטיוטות." + }, + "cross_all": { + "label": "כל החשבונות: כל הדוא״ל", + "description": "הצג ערך בסעיף כל החשבונות המפרט את כל הדוא״ל בכל חשבון, המשתרע על כל התיקיות למעט זבל, נשלח, ארכיון, אשפה וטיוטות." + } + }, + "keywords": { + "title": "מילות מפתח בדוא\"ל", + "description": "הגדר מילות מפתח (תוויות/תגים) כדי לארגן את המיילים שלך עם צבעים. אלו מאוחסנות כמילות מפתח JMAP בשרת.", + "add_keyword": "הוסף מילת מפתח", + "label_field": "שם תצוגה", + "label_placeholder": "למשל עבודה, אישי, דחוף", + "id_field": "מזהה מילת מפתח", + "id_placeholder": "למשל עבודה, אישית", + "color_field": "צבע", + "id_exists": "מזהה מילת מפתח זה כבר קיים", + "edit": "ערוך מילת מפתח", + "delete": "מחק מילת מפתח", + "save": "שמור", + "add": "לְהוֹסִיף", + "cancel": "לְבַטֵל", + "migrating": "מעדכן מילת מפתח באימיילים קיימים...", + "migration_error": "נכשל עדכון מילת המפתח בהודעות דוא\"ל קיימות", + "filters_migration_error": "חלק מכללי הסינון עדיין משתמשים בתגית הישנה — עדכנו אותם ידנית", + "nesting": { + "label": "תגים מקוננים", + "description": "קנן תגים תחת תגים אחרים והצג אותם כעץ בסרגל הצד." + }, + "parent_field": "תג אב", + "no_parent": "ללא תג אב", + "too_long": "נתיב התג ארוך מדי (עד {max} תווים)", + "has_children_locked": "תגים אחרים מקוננים תחת תג זה, ולכן שמו ותג האב שלו נעולים. העבר או הסר אותם תחילה.", + "has_children_delete": "הסר תחילה את התגים המקוננים תחת תג זה", + "visibility_field": "הצגה בסרגל הצד", + "visibility": { + "show": "הצג", + "unread": "הצג כשיש שלא נקראו", + "hide": "הסתר" + }, + "unrecognized": { + "title": "שחזור תגים מהשרת", + "description": "שמות התגים והצבעים שלהם נשמרים בדפדפן הזה, אך התגים עצמם מאוחסנים על ההודעות שלך כמילות מפתח JMAP. סרוק את הדואר שלך לאיתור מילות מפתח שאין להן תג כאן, והוסף אותן בחזרה.", + "scan": "סריקה לאיתור תגים", + "rescan": "סרוק שוב", + "scanning": "סורק… {scanned} מתוך {total} הודעות", + "none": "כל התגים שעל ההודעות שלך כבר מוגדרים כאן.", + "messages": "{count, plural, one {הודעה אחת} other {# הודעות}}", + "add_all": "הוסף את כל {count}", + "partial": "נסרקו רק {scanned} ההודעות האחרונות מתוך {total}. תגים שנמצאים רק בדואר ישן יותר עשויים להיעדר.", + "error": "לא ניתן היה לסרוק את הדואר לאיתור תגים." + } + }, + "notifications": { + "test_sound": "צליל הודעת בדיקה", + "sounds": { + "default": "ברירת מחדל (ביפ)", + "cheerful": "עליז", + "involved": "מעורב", + "swift": "מחווה מהירה", + "relax": "תירגע" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "צליל התראה", + "description": "בחר איזה צליל להפעיל עבור התראות", + "choose": "צליל", + "choose_desc": "בחר צליל התראה ולחץ על סמל הרמקול כדי להציג אותו בתצוגה מקדימה" + }, + "email": { + "title": "הודעות אימייל", + "description": "הגדר התראות עבור הודעות דוא\"ל נכנסות", + "enabled": "התראות באימייל", + "enabled_desc": "הצג התראות כשמגיעות אימיילים חדשים", + "sound": "צליל התראה", + "sound_desc": "הפעל התראת שמע כשמגיעות הודעות דוא\"ל חדשות", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "הודעות לוח שנה", + "description": "הגדר התראות עבור אירועי לוח שנה", + "enabled": "התראות על אירועים", + "enabled_desc": "הצג התראות עבור אירועי לוח שנה קרובים", + "sound": "צליל התראה", + "sound_desc": "הפעל התראה קולית עבור תזכורות יומן", + "invitation_parsing": "נתח הזמנות באימייל", + "invitation_parsing_desc": "זיהוי הזמנות ליומן בקבצים מצורפים לאימייל והצג פעולות ביומן" + }, + "push": { + "title": "התראות בדפדפן", + "description": "קבל עדכונים מערכת עבור דוא״ל חדש כאשר אתר זה סגור. מסופק דרך ממסר Bulwark; הממסר לא רואה תוכן דוא״ל.", + "relay_label": "ממסר דחיפה", + "relay_desc": "ברירת מחדל של ממסר Bulwark המתורח. שנה רק אם אתה מארח בעצמך.", + "relay_locked": "הוגדר על ידי מנהל", + "relay_locked_desc": "כתובת ממסר הדחיפה הוגדרה על ידי מנהל שלך ולא ניתן לשנות אותה.", + "status_active": "פעיל בהתקן זה", + "status_inactive": "לא מופעל בהתקן זה", + "status_unsupported": "דפדפן זה אינו תומך בדחיפה ברשת", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "עובד…", + "enable": "הפעל", + "reenable": "רשום מחדש", + "disable": "השבת", + "confirm_disable_title": "השבת התראות בדפדפן?", + "confirm_disable_message": "התקן זה יפסיק לקבל התראות כאשר האתר סגור.", + "dismiss_aria": "בטל הודעת התראות", + "ios_hint": "ב־iOS, התקן קודם כל את האתר בהיתוך שלך - Safari מספק Web Push רק לPWA מותקנות.", + "devices_title": "מכשירים רשומים", + "devices_desc": "כל המכשירים הרשומים לקבלת התראות ברקע עבור חשבון זה. ביטול מכשיר מפסיק את הקבלה מיד.", + "devices_loading": "טוען מכשירים רשומים…", + "devices_empty": "אין מכשירים רשומים להתראות ברקע.", + "devices_error": "לא ניתן לטעון את המכשירים הרשומים.", + "devices_refresh": "רענון", + "device_this": "מכשיר זה", + "device_other": "מכשיר אחר", + "device_status_active": "מספק התראות", + "device_status_inactive": "מעולם לא סיפק התראה", + "device_status_unknown": "לא מוכר לממסר הזה", + "device_expires": "פג בתאריך {date}", + "revoke": "ביטול", + "confirm_revoke_title": "לבטל את המכשיר הזה?", + "confirm_revoke_message": "הוא יפסיק לקבל התראות ברקע מיד. הפעלה מחדש של פוש באותו מכשיר תרשום אותו מחדש.", + "confirm_revoke_message_this": "מכשיר זה יפסיק לקבל התראות ברקע מיד." + } + }, + "language_region": { + "title": "שפה ואזור", + "description": "הגדר את העדפות השפה והאזור", + "language": { + "label": "שָׂפָה", + "description": "בחר את השפה המועדפת עליך", + "english": "אנגלית", + "french": "Français" + }, + "date_format": { + "label": "פורמט תאריך", + "description": "כיצד יש להציג תאריכים", + "smart": "חכם", + "relative": "יחסית", + "full": "תמיד תאריך מלא", + "preview_today": "היום:", + "preview_this_week": "השבוע הזה:", + "preview_older": "ישן יותר:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "פורמט זמן", + "description": "בחר בין שעון של 12 שעות או 24 שעות", + "12h": "12 שעות", + "24h": "24 שעות" + }, + "first_day": { + "label": "היום הראשון בשבוע", + "description": "התחל את השבוע ביום ראשון או שני", + "sunday": "ראשון", + "monday": "שני" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "נקה את החיפוש בעת מעבר בין תיקיות", + "description": "לחיצה על תיקייה (דואר נכנס, נשלח…) מנקה את החיפוש הפעיל ומציגה את התיקייה. כשהאפשרות כבויה, החיפוש נשאר ומופעל מחדש בתיקייה שאליה עברת." + }, + "title": "התנהגות אימייל", + "description": "קבע את אופן הטיפול בהודעות דוא\"ל", + "mark_read": { + "label": "סמן כקריאה", + "description": "מתי לסמן הודעות דוא\"ל כנקראו בעת פתיחה", + "instant": "באופן מיידי", + "delay_3s": "לאחר 3 שניות", + "delay_5s": "לאחר 5 שניות", + "never": "לעולם לא" + }, + "delete_action": { + "label": "מחק את הפעולה", + "description": "מה קורה כשמוחקים אימייל", + "trash": "העבר לאשפה", + "permanent": "מחק לצמיתות", + "warning": "הודעות אימייל יימחקו לצמיתות ולא ניתן לשחזר אותן. פעולה זו היא בלתי הפיכה.", + "trash_and_read": "העבר לאשפה וסמן כקרא" + }, + "message_spacing": { + "label": "מרווח סביב ההודעה", + "description": "ריווח מסביב לגוף ההודעה בתצוגת הקריאה", + "auto": "אוטומטי (מומלץ)", + "always": "תמיד להוסיף מרווח", + "edge": "מקצה לקצה" + }, + "plain_text_font": { + "label": "גופן טקסט פשוט", + "description": "הגופן המשמש להודעות בטקסט פשוט", + "mono": "ברוחב קבוע", + "sans": "רגיל (כמו היישום)" + }, + "archive_mode": { + "label": "ארכיון ב", + "description": "כיצד לארגן מיילים בעת אחסון בארכיון", + "single": "תיקיה בודדת", + "year": "תיקייה לשנה", + "month": "תיקייה לחודש", + "reorganize": "ארגן מחדש את הארכיון הקיים", + "reorganize_success": "{count, plural, =0 {אין הודעות דוא״ל לארגון מחדש} =1 {הודעת דוא״ל אחת אורגנה מחדש} other {# הודעות דוא״ל אורגנו מחדש}}", + "reorganize_error": "נכשל ארגון מחדש של הארכיון" + }, + "permanently_delete_junk": { + "label": "מחק זבל לצמיתות", + "description": "מחק לצמיתות הודעות דוא\"ל מתיקיית הזבל/ספאם במקום להעביר אותן לאשפה" + }, + "confirm_bulk_delete": { + "label": "אישור לפני מחיקת כמה הודעות", + "description": "לשאול לפני העברת כמה הודעות שנבחרו לאשפה. מחיקה לצמיתות תמיד שואלת." + }, + "mail_layout": { + "label": "פריסת דואר", + "description": "בחר בין חלונית הקריאה המפוצלת הקלאסית לבין זרימת קריאה ממוקדת בסגנון Gmail.", + "split": "חלונית מפוצלת", + "split_description": "השאר את רשימת ההודעות וחלונית הקריאה גלויים זה לצד זה.", + "focus": "רשימה ממוקדת", + "focus_description": "הצג שורה אחת לכל הודעה ופתח דואר ברוחב מלא תוך שמירה על סרגל הצד של התיקייה גלוי.", + "horizontal": "חלונית קריאה בתחתית", + "horizontal_description": "הצג את רשימת ההודעות בחלק העליון וכנס את ההודעה שנבחרה בחלונית קריאה למטה." + }, + "list_columns": { + "label": "הצגת הרשימה בעמודות", + "description": "הצגת שורות בנות שורה אחת כטבלה עם העמודות מאת, נושא, תאריך וגודל מתחת לכותרת." + }, + "show_preview": { + "label": "הצג טקסט תצוגה מקדימה", + "description": "הצג תצוגה מקדימה של דואר אלקטרוני ברשימה", + "focus_description": "הצג טקסט תצוגה מקדימה מוטבע בתוך רשימת ההודעות הממוקדת בשורה אחת" + }, + "show_verification_codes": { + "label": "הצגת קודי אימות", + "description": "הצעת הקוד מהודעות כניסה ואישור להעתקה בלחיצה אחת, ברשימה למשך יום ובהודעה" + }, + "disable_threading": { + "label": "השבת את קיבוץ השיחות", + "description": "הצג הודעות אימייל כהודעות בודדות במקום מקובצות לפי שיחה" + }, + "plain_text_mode": { + "label": "טקסט רגיל בלבד", + "description": "השבת את עורך הטקסט העשיר ושלח את כל המיילים כטקסט רגיל בלבד, כולל תשובות והעברה" + }, + "auto_select_reply_identity": { + "label": "בחירה אוטומטית בכתובת תשובה", + "description": "בעת תשובה, העבר אוטומטית את הכתובת מאת לזהות שקיבלה את ההודעה במקור" + }, + "reply_identity_match": { + "label": "התאמת כתובת הקבלה", + "description": "אילו כתובות קבלה נחשבות לשלך. „כתובת מדויקת בלבד“ בוחרת רק אחת מהזהויות שהגדרת; „אותו דומיין“ מתייחסת גם לכל כתובת אחרת בדומייני הזהויות שלך ככינוי catch-all ומשכתבת אליה את כותרת השולח. בחר בכתובת מדויקת אם כתובות אלה הן רשימות תפוצה.", + "exact": "כתובת מדויקת בלבד", + "domain": "כל כתובת בדומיינים שלי" + }, + "attachment_click_action": { + "label": "קובץ מצורף לחץ על פעולה", + "description": "בחר אם לחיצה על קובץ מצורף תציג אותו בתצוגה מקדימה או תוריד אותו מיד", + "preview": "תצוגה מקדימה כשאפשר", + "download": "הורד מיד" + }, + "attachment_position": { + "label": "עמדת התקשרות", + "description": "היכן להציג קבצים מצורפים בכותרת הדוא\"ל", + "beside-sender": "ליד השולח", + "below-header": "מתחת לכותרת" + }, + "emails_per_page": { + "10": "10 מיילים", + "25": "25 מיילים", + "50": "50 מיילים", + "100": "100 מיילים", + "label": "מיילים לכל עמוד", + "description": "מספר הודעות האימייל לטעינה בבת אחת" + }, + "always_light_mode": { + "label": "הצג תמיד הודעות דוא\"ל במצב קל", + "description": "עבד את תוכן האימייל במצב בהיר גם כשהאפליקציה במצב כהה, תוך הימנעות מבעיות המרה במצב כהה" + }, + "external_content": { + "label": "תוכן חיצוני", + "description": "כיצד לטפל בתמונות ובתוכן חיצוני", + "ask": "תמיד תשאל", + "block": "תמיד לחסום", + "allow": "תמיד אפשר" + }, + "trusted_senders": { + "label": "שולחים מהימנים", + "description": "נהל שולחים שהתמונות שלהם נטענות אוטומטית", + "count_zero": "אין", + "count_one": "שולח 1", + "count_other": "שולחי{count}", + "modal_title": "שולחים מהימנים", + "empty_title": "עדיין אין שולחים מהימנים", + "empty_description": "בעת הצגת אימייל עם תמונות חסומות, לחץ על \"תמיד בטח בשולח זה\" כדי להוסיף אותן כאן.", + "add_manually": "הוסף שולח באופן ידני", + "add_button": "לְהוֹסִיף", + "add_placeholder": "הזן כתובת אימייל", + "search_placeholder": "חפש שולחים...", + "no_results": "אין שולחים שתואמים לחיפוש שלך", + "remove": "הסר", + "close": "לִסְגוֹר", + "invalid_email": "נא להזין כתובת אימייל חוקית", + "already_added": "השולח הזה כבר מהימן", + "save_error": "נכשל לשמור - בדוק את יומן ניפוי הנתונים של אנשי קשר לפרטים", + "use_address_book_label": "סנכרן עם ספר כתובות", + "use_address_book_description": "שמור שולחים מהימנים בספר כתובות ״שולחים מהימנים״ ייעודי כדי שהם יסתנכרנו בכל ההתקנים שלך" + }, + "hover_actions": { + "label": "פעולות ריחוף מהירות", + "description": "בחר אילו פעולות מהירות יופיעו בעת ריחוף מעל הודעת דוא\"ל ברשימה", + "delete": "לִמְחוֹק", + "star": "כוכב / הסר כוכב", + "mark_read": "סמן שנקרא/לא נקרא", + "archive": "ארכיון", + "tag": "תג", + "spam": "סמן כדואר זבל", + "none_selected": "לא נבחרו פעולות", + "mode_label": "מצב תצוגה", + "mode_inline": "מוטבע", + "mode_floating": "צף", + "corner_label": "מיקום צף", + "corner_top-left": "שמאל למעלה", + "corner_top-right": "למעלה מימין", + "corner_bottom-left": "שמאל למטה", + "corner_bottom-right": "ימין למטה", + "not_spam": "לא דואר זבל" + }, + "default_mail_program": { + "label": "תוכנית דואר ברירת מחדל", + "description": "רשום את{appName}כתוכנית הדואר המוגדרת כברירת מחדל עבור קישורי mailto:", + "button": "הגדר כברירת מחדל", + "success": "דפדפן התבקש להגדיר כברירת מחדל", + "error": "הדפדפן שלך אינו תומך בתכונה זו" + }, + "request_read_receipt": { + "label": "בקש קבלות דואר כברירת מחדל", + "description": "הפעל מראש את בקשת קבלת הדואר בעת הרכבת הודעה חדשה." + }, + "read_receipt_response": { + "label": "הגב לבקשות קבלת דואר", + "description": "מה לעשות כאשר הודעה נכנסת מבקשת קבלת דואר.", + "ask": "שאל כל פעם", + "always": "תמיד שלח", + "never": "לא תמיד שלח" + }, + "signature_position": { + "label": "מיקום חתימה", + "description": "היכן להוסיף את החתימה שלך בתשובות והעברות. מעל הטקסט המצוטט קורא באופן טבעי כסגירה לתשובה; למטה מפיקה את ההודעה המקורית רציפה.", + "above_quote": "לפני טקסט מצוטט", + "below_quote": "אחרי טקסט מצוטט" + }, + "signature_separator": { + "label": "מפריד חתימה", + "description": "הוסף קידומת לחתימה בעדכון סטנדרטי ״-- ״ (RFC 3676). כבה אם אתה מעדיף לזרום ישירות מההודעה שלך לחתימה." + }, + "sub_address_delimiter": { + "label": "מפריד כתובת משנה", + "description": "תו המפריד בין שם המשתמש שלך לתג כתובת משנה. תאם את המפריד שדואר השרת שלך משתמש בו (למשל user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "מותאם אישית…", + "custom_input_label": "תו מפריד מותאם אישית" + }, + "attachment_reminder": { + "label": "תזכורת קובץ מצורף", + "description": "הזהר לפני שליחה כאשר ההודעה שלך מתייחסת לקבצים מצורפים אך אף אחד לא צורף", + "keywords_label": "מילות חיוג הטריגר", + "keywords_description": "מילים או ביטויים המעוררים את התזכורת כאשר נמצאים בהודעה שלך", + "add_placeholder": "הוסף מילת חיוג…", + "add": "הוסף", + "remove": "הסר" + }, + "empty_subject_warning": { + "label": "אזהרה על נושא ריק", + "description": "בקש אישור לפני שליחת הודעה ללא נושא." + }, + "recipient_mentions": { + "label": "אזכור נמענים עם @", + "description": "הקלדת @ בהודעה מציעה את הנמענים ומכניסה את השם הפרטי של הנמען שנבחר. Enter או Tab בוחרים את השם המסומן." + }, + "mention_adds_recipient": { + "label": "אנשי קשר באזכורים עם @", + "description": "הקלדת @ מציעה גם את אנשי הקשר שלך ואנשים מהספרייה. בחירה במי שעדיין אינו נמען מוסיפה אותו להודעה.", + "to": "הוספה ל\"אל\"", + "cc": "הוספה ל\"עותק\"", + "off": "להציע נמענים בלבד" + }, + "hide_inline_image_attachments": { + "label": "הסתר תמונות מוטבעות מקבצים מצורפים", + "description": "תמונות מוטבעות בגוף ההודעה אינן מופיעות כקבצים מצורפים נפרדים" + }, + "attachment_image_previews": { + "label": "הצג תצוגות מקדימות בתמונות בקבצים מצורפים", + "description": "עבור קבצי תמונה מצורפים כקלפי תמונה קטנה במקום סמלים כלליים של קובץ" + }, + "send_delay": { + "label": "בטל שליחה / השהיית שליחה", + "description": "השהה שליחות נורמליות בחלון בצד השרת הקצר.", + "off": "כבוי", + "seconds": "{seconds} שניות", + "unsupported": "החשבון הנוכחי אינו פרסום תמיכה בשליחה מעוכבת. ההגדרה עדיין שמורה לחשבונות אחרים." + }, + "return_to_list_after_action": { + "label": "חזור לרשימה לאחר מחיקה או סימון שלא נקראו", + "description": "לאחר מחיקה או סימון ההודעה הפתוחה כלא קרויה, חזור לרשימת ההודעות במקום לפתוח את ההודעה הבאה." + }, + "swipe_right_action": { + "label": "פעולת החלקה ימינה (נייד)", + "description": "במכשירי מגע, הפעולה שמתבצעת כשמחליקים הודעה ימינה ברשימה." + }, + "swipe_left_action": { + "label": "פעולת החלקה שמאלה (נייד)", + "description": "במכשירי מגע, הפעולה שמתבצעת כשמחליקים הודעה שמאלה ברשימה." + }, + "swipe_actions": { + "none": "ללא", + "archive": "העבר לארכיון", + "delete": "מחק", + "mark_read": "החלף מצב נקרא", + "star": "החלף כוכב", + "spam": "סמן כספאם" + }, + "rtl_editing": { + "label": "תמיכה בעריכה מימין לשמאל", + "description": "מוסיף כפתור כיווניות לסרגל הכלים של העורך כדי להגדיר פסקאות משמאל לימין או מימין לשמאל" + } + }, + "composer": { + "title": "עורך", + "description": "הגדר את הגדרות הרכב דוא\"ל", + "autosave": { + "label": "מרווח שמירה אוטומטית", + "description": "באיזו תדירות לשמור טיוטות באופן אוטומטי", + "30s": "כל 30 שניות", + "1m": "כל דקה", + "2m": "כל 2 דקות", + "5m": "כל 5 דקות" + }, + "send_confirmation": { + "label": "שלח אישור", + "description": "בקשו אישור לפני שליחת מיילים" + }, + "default_reply": { + "label": "מצב תגובה ברירת מחדל", + "description": "פעולת ברירת מחדל בעת לחיצה על השב", + "reply": "השב", + "reply_all": "השב לכולם" + } + }, + "privacy": { + "title": "פרטיות ואבטחה", + "description": "נהל את הגדרות הפרטיות והאבטחה שלך", + "external_images": { + "label": "חסום תמונות חיצוניות", + "description": "מנע מעקב דרך תמונות חיצוניות" + }, + "session_timeout": { + "label": "זמן קצוב להפעלה", + "description": "התנתק אוטומטית לאחר חוסר פעילות", + "never": "לעולם לא", + "30m": "30 דקות", + "1h": "שעה אחת", + "4h": "4 שעות" + }, + "clear_cache": { + "label": "נקה את המטמון", + "description": "הסר נתונים בקובץ שמור וקבצים זמניים", + "button": "נקה את המטמון", + "confirm": "האם אתה בטוח שברצונך לנקות את המטמון?", + "success": "המטמון נוקה בהצלחה" + } + }, + "account": { + "title": "חשבון", + "description": "הצג את פרטי החשבון שלך", + "name_label": "שם תצוגה", + "username_label": "שם משתמש", + "account_type_label": "סוג חשבון", + "auth_method_label": "אימות", + "auth_method_oauth": "כניסה יחידה (OAuth/OIDC)", + "auth_method_basic": "סִיסמָה", + "auth_method_token": "אסימון גישה", + "demo_account": "חשבון הדגמה", + "email": { + "label": "כתובת אימייל", + "value": "{email}" + }, + "server": { + "label": "שרת JMAP", + "value": "{server}" + }, + "storage": { + "label": "שימוש באחסון", + "used": "{used}של{total}בשימוש", + "percentage": "{percent}% בשימוש" + }, + "last_sync": { + "label": "סנכרון אחרון", + "value": "{time}" + }, + "accounts": { + "title": "חשבונות מחוברים", + "description": "גרור לסדר כיצד חשבונות מופיעים בתפריט הנפתח", + "active": "חשבון פעיל כרגע", + "default_badge": "חשבון ברירת מחדל", + "set_default": "הגדר כברירת מחדל", + "switch_to": "עבור לחשבון זה", + "move_up": "עבור למעלה", + "move_down": "עבור למטה", + "drag_handle": "גרור לסדר", + "add": "הוסף חשבון" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "משותף איתי", + "description": "חשבונות קבוצתיים ומשותפים שאתה יכול לנהל. בחר אחד כדי לערוך את המסננים, המגיב לחופשה, לוחות השנה והאנשים שלו.", + "shared_label": "חשבון משותף" + } + }, + "security": { + "title": "אבטחת חשבון", + "description": "נהל את הסיסמה שלך, אימות דו-שלבי והגדרות אבטחה", + "detecting": "מזהה יכולות שרת...", + "not_available": "ניהול אבטחת חשבון אינו זמין עבור שרת דואר זה. הרשאות נדרשות עשויות להיות מושבתות. עיין בתיעודלפרטים.", + "password": { + "title": "שנה סיסמה", + "current": "סיסמה נוכחית", + "new": "סיסמה חדשה", + "confirm": "אשר סיסמה חדשה", + "submit": "שנה סיסמה", + "success": "הסיסמה שונתה בהצלחה", + "error_title": "שינוי הסיסמה נכשל", + "error_mismatch": "סיסמאות חדשות אינן תואמות", + "error_min_length": "הסיסמה חייבת להיות לפחות 8 תווים", + "error_generic": "שינוי הסיסמה נכשל" + }, + "display_name": { + "label": "שם תצוגה", + "description": "השם שלך כפי שהוא מופיע בשרת", + "placeholder": "הזן את שם התצוגה שלך", + "save": "שמור", + "success": "שם התצוגה עודכן", + "error": "עדכון שם התצוגה נכשל" + }, + "totp": { + "section_title": "אימות דו-גורמי", + "label": "אימות TOTP", + "description": "הוסף שכבת אבטחה נוספת עם סיסמה חד פעמית מבוססת זמן", + "active": "מופעל", + "inactive": "מושבת", + "enabled": "אימות דו-גורמי מופעל", + "disabled": "אימות דו-גורמי מושבת", + "enable_error": "הפעלת 2FA נכשלה", + "disable_error": "נכשל השבתת 2FA", + "setup_instructions": "העתק את כתובת האתר הזו לאפליקציית המאמת שלך (Google Authenticator, Authy וכו'):", + "verification_code": "קוד אימות", + "confirm": "אשר", + "disable": "השבת", + "disable_confirm_prompt": "הזן את הסיסמה שלך כדי להשבית אימות דו־שלבי.", + "password_required": "נדרשת סיסמה", + "code_required": "נדרש קוד אימות", + "code_invalid": "קוד אימות לא חוקי. בדוק את יישום ההאימות שלך ונסה שוב." + }, + "app_passwords": { + "title": "סיסמאות לאפליקציה", + "description": "צור סיסמאות לאפליקציות שאינן תומכות באימות דו-שלבי", + "add": "לְהוֹסִיף", + "create": "צור", + "cancel": "לְבַטֵל", + "generate": "צור", + "name_label": "שם האפליקציה", + "name_placeholder": "למשל Thunderbird, אייפון מייל", + "password_label": "סיסמה (השאירו ריקה ליצירה אוטומטית)", + "password_placeholder": "נוצר אוטומטית אם ריק", + "added": "נוצרה סיסמת אפליקציה", + "removed": "סיסמת האפליקציה הוסרה", + "add_error": "יצירת סיסמת האפליקציה נכשלה", + "remove_error": "הסרת סיסמת האפליקציה נכשלה", + "none": "לא הוגדרו סיסמאות לאפליקציה", + "done": "בוצע", + "expires_label": "פג תוקף (אופציונלי)", + "allowed_ips_label": "כתובות IP מורשות (אופציונלי)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "מופרד בפסיקים או רווחים. השאר ריק כדי לאפשר כל IP.", + "copy_now_warning": "העתק סיסמה זו כעת - היא לא תיוצג שוב." + }, + "encryption": { + "section_title": "הצפנה במנוחה", + "label": "הצפנת דואר אלקטרוני", + "description": "הצפין מיילים מאוחסנים בשרת לפרטיות נוספת", + "active": "הצפנת{type}מופעלת", + "inactive": "מושבת", + "enabled": "הצפנה במנוחה מופעלת", + "disabled_success": "ההצפנה במנוחה מושבתת", + "error": "עדכון הגדרות ההצפנה נכשל", + "algorithm_label": "אלגוריתם הצפנה", + "encrypt_on_append": "הצפן הודעות חדשות בעת ההעלאה", + "allow_spam_training": "אפשר אימון זיהוי דואר זבל לפני הצפנת ההודעות", + "enable_encryption": "הפעל הצפנה", + "enable_tooltip": "הפעל הצפנה עם מפתח זה", + "configure_title": "הפעל הצפנה עם מפתח זה", + "enable_button": "הפעל", + "disable_tooltip": "השבת הצפנה", + "enabled_success": "הצפנה במנוחה מופעלת" + }, + "email_client": { + "title": "הגדרת לקוח אימייל", + "description": "השתמש באישורים אלה כדי להגדיר את לקוח הדוא\"ל של שולחן העבודה או הנייד שלך (Thunderbird, Apple Mail, Outlook וכו')", + "jmap_username_label": "שם משתמש JMAP", + "copy": "העתק", + "copied": "הועתק", + "password_instructions": "השתמש בשם המשתמש שלך ב-JMAP למעלה יחד עם סיסמת אפליקציה כדי להיכנס ללקוח הדוא\"ל שלך. צור סיסמת אפליקציה בסעיף למעלה אם עדיין לא עשית זאת." + }, + "api_keys": { + "title": "מפתחות API", + "description": "צור מפתחות API עבור סקריפטים ואינטגרציות המדברות ישירות לשרת", + "name_label": "שם מפתח", + "name_placeholder": "למשל סקריפט גיבוי, פעיל CI", + "copy_now_warning": "העתק מפתח API זה כעת - הוא לא יוצג שוב.", + "added": "מפתח API נוצר", + "removed": "מפתח API הוסר", + "add_error": "נכשל ביצירת מפתח API", + "remove_error": "נכשל בהסרת מפתח API", + "none": "אין מפתחות API מוגדרים" + }, + "link_device": { + "title": "הקשר יישום נייד", + "description": "התחבר לאפליקציית Bulwark Mail בטלפון בלי להקליד שם את הסיסמה. הטלפון מקבל התחברות משלו, כך שהתנתקות כאן לא מנתקת אותו.", + "generate": "הצג קוד QR", + "regenerate": "הצג קוד חדש", + "instructions": "פתח את אפליקציית Bulwark Mail והקש על \"Scan a sign-in code\" במסך ההתחברות (או \"Add account\" → \"Scan a sign-in code\" אם כבר התחברת), ואז כוון את המצלמה לכאן.", + "expires_in": "קוד זה פוקע בעוד {seconds} שניות. ניתן להשתמש בו רק פעם אחת.", + "expired": "קוד זה פקע.", + "error": "לא היה ניתן ליצור קוד זיווג. אנא נסה שוב.", + "password_prompt": "הזן את הסיסמה כדי לאשר שזה אתה. הטלפון מקבל התחברות משלו; הסיסמה שלך לא נשלחת לטלפון.", + "show_password": "הצג סיסמה", + "hide_password": "הסתר סיסמה", + "continue": "המשך", + "error_wrong_password": "סיסמה שגויה. נסה שוב.", + "totp_prompt": "הזן את הקוד מאפליקציית האימות.", + "error_password_or_code": "הסיסמה או הקוד שגויים.", + "error_not_signed_in": "פג תוקף ההפעלה שלך. התחבר מחדש כדי לקשר מכשיר.", + "error_impersonation": "לא ניתן לקשר מכשירים בזמן צפייה בתיבת הדואר של משתמש אחר.", + "error_too_many_attempts": "יותר מדי ניסיונות. המתן רגע ונסה שוב.", + "error_session_secret": "קישור מכשירים לא הוגדר בשרת זה (SESSION_SECRET חסר). פנה למנהל המערכת.", + "error_server_unreachable": "לא ניתן להתחבר לשרת הדואר שלך. נסה שוב.", + "error_pairing_unavailable": "שרת הדואר שלך לא מאפשר לקשר מכשיר בדרך זו. פנה למנהל המערכת.", + "qr_alt": "קוד QR להתחברות", + "link_label": "קישור התחברות", + "copy_link": "העתק קישור התחברות", + "copied": "הועתק", + "copy_hint": "אין מצלמה? העתק את הקישור והדבק אותו באפליקציה באמצעות \"Paste sign-in link\".", + "success_title": "ההתחברות בטלפון הושלמה", + "success_body": "אפשר לסגור את הדף הזה. כדי לקשר מכשיר נוסף, הצג קוד חדש.", + "link_another": "קשר מכשיר נוסף", + "use_sso": "השתמש בכניסה יחידה במקום", + "error_insecure_server": "האפליקציה מתחברת לשרתי דואר רק ב-https, והשרת הזה משתמש ב-http." + }, + "public_keys": { + "name_label": "שם המפתח הציבורי", + "name_placeholder": "לדוגמה: המפתח שלי", + "key_label": "מפתח ציבורי (S/MIME או PGP בפורמט ASCII)", + "key_placeholder": "הדבק כאן את המפתח הציבורי שלך", + "none": "לא הוגדרו מפתחות ציבוריים" + } + }, + "identities": { + "title": "שליחת זהויות", + "description": "נהל כתובות דוא\"ל שמהן תוכל לשלוח", + "identities_count": { + "label": "הזהויות שלך", + "description": "כתובות דוא\"ל מוגדרות לשליחה", + "count_zero": "אין זהויות", + "count_one": "1 זהות", + "count_other": "זהויות{count}" + }, + "manage": "ניהול זהויות", + "sub_addressing": { + "label": "כתובת משנה", + "description": "השתמש בתגים כמוuser+tag@domain.comכדי לארגן דואר נכנס", + "learn_more": "למידע נוסף" + } + }, + "vacation": { + "title": "היעדרות", + "description": "טפל באופן אוטומטי בהודעות דוא״ל נכנסות בזמן שאתה רחוק", + "loading": "טוען הגדרות חופשה...", + "not_supported": "שרת הדואר שלך אינו תומך בתגובות חופשה.", + "fetch_error": "טעינת הגדרות החופשה נכשלה. אנא נסה שוב.", + "status": { + "label": "שליחת מענה אוטומטי", + "description": "שלח תשובה אוטומטית לאנשים ששלחו לך דוא\"ל", + "active": "פעיל", + "inactive": "לא פעיל" + }, + "date_range": { + "start": "תאריך התחלה", + "start_description": "השאר ריק ללא מגבלת התחלה", + "end": "תאריך סיום", + "end_description": "השאר ריק ללא הגבלה" + }, + "message": { + "subject_label": "נושא", + "subject_description": "שורת הנושא של התשובה האוטומטית", + "subject_placeholder": "מחוץ למשרד", + "body_label": "גוף ההודעה", + "body_description": "תוכן הודעת טקסט רגילה", + "body_placeholder": "תודה על המייל שלך. אני כרגע מחוץ למשרד ואענה כשאחזור.", + "html_label": "הודעה מעוצבת (HTML)", + "html_description": "הוסיפו גרסה מעוצבת עם קישורים וסגנון. נמענים שתוכנת הדוא״ל שלהם אינה יכולה להציג אותה יראו את הטקסט הרגיל שלמעלה.", + "html_placeholder": "כתבו תשובת היעדרות מעוצבת…" + }, + "preview": { + "title": "תצוגה מקדימה", + "show": "הצג תצוגה מקדימה", + "hide": "הסתר תצוגה מקדימה" + }, + "save": "שמור שינויים", + "saving": "שומר...", + "warnings": { + "end_before_start": "תאריך הסיום חייב להיות אחרי תאריך ההתחלה", + "invalid_date": "יש להזין תאריך התחלה וסיום תקינים עם שעה, או להשאיר את השדה ריק", + "start_in_past": "תאריך ההתחלה הוא בעבר", + "empty_body": "גוף ההודעה ריק - הנמענים יקבלו תשובה ריקה", + "subject_too_long": "הנושא ארוך מדי עבור השרת (לכל היותר {max} בתים).", + "body_too_long": "ההודעה ארוכה מדי עבור השרת (לכל היותר {max} בתים, כולל עיצוב).", + "forward_address": "יש להזין כתובת תקינה להעברה", + "not_running": "ההעברה או נמעני המענה שמורים אך אינם פעילים כרגע. יש לשמור כדי להפעיל אותם מחדש." + }, + "forward": { + "title": "העברה", + "description": "מעביר הודעות נכנסות לכתובת אחרת, עם מענה אוטומטי או בלעדיו, בטווח הזמן שלמעלה (ללא תאריך סיום, עד שיכובה). דואר זבל אינו מועבר.", + "enabled_label": "העברת הודעות", + "to_label": "העבר אל", + "to_placeholder": "email@example.com", + "keep_label": "שמור בתיבת הדואר הנכנס", + "keep_description": "אחרת ההודעה רק מועברת: היא לא נשמרת בתיבת דואר זו, ואף כלל סינון לא חל עליה." + }, + "audience": { + "label": "להשיב אל", + "description": "שולחים פנימיים: {domains}", + "all": "כל השולחים", + "internal": "שולחים פנימיים בלבד", + "external": "שולחים חיצוניים בלבד" + }, + "auto_reply": { + "title": "מענה אוטומטי" + } + }, + "folders": { + "title": "תיקיות", + "description": "נהל את תיקיות הדוא\"ל שלך והקצה תפקידים סטנדרטיים", + "folder_list": "התיקיות שלך", + "folder_list_description": "לחץ על סמל תיקיה כדי להתאים אותו", + "standard_roles": "תפקידי תיקייה סטנדרטיים", + "standard_roles_description": "הקצה אילו תיקיות משמשות לתפקידי תיבת דואר סטנדרטיים כמו Inbox, Sent, Trash וכו׳.", + "role_inbox": "תיבת דואר נכנס", + "role_drafts": "טיוטות", + "role_sent": "נשלח", + "role_trash": "אַשׁפָּה", + "role_junk": "ספאם / זבל", + "role_archive": "ארכיון", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "אין", + "create_folder": "צור תיקיה", + "create_subfolder": "צור תיקיית משנה", + "reorder": "גרור לשינוי הסדר", + "reorder_error": "שינוי סדר התיקיות נכשל", + "move_error": "העברת התיקייה נכשלה", + "select_folder": "בחירת {name}", + "select_hint": "בחירה. Shift+לחיצה בוחרת את כל התיקיות עד האחרונה שלחצת עליה.", + "selected_count": "{count, plural, one {נבחרה תיקייה #} other {נבחרו # תיקיות}}", + "move_to": "העברה אל...", + "move_to_top_level": "רמה עליונה", + "move": "העברה", + "clear_selection": "ניקוי הבחירה", + "folders_moved": "{count, plural, one {תיקייה # הועברה} other {# תיקיות הועברו}}", + "move_partial_error": "{count, plural, one {לא ניתן היה להעביר תיקייה #} other {לא ניתן היה להעביר # תיקיות}}", + "subfolder_of": "בתוך{name}", + "subfolder_name": "שם תיקיית משנה", + "new_folder_name": "שם התיקיה", + "rename": "שנה שם", + "change_icon": "שנה סמל", + "count_mode": "מונה עבור {name}", + "count_mode_default": "מונה: ברירת מחדל", + "count_mode_unread": "רק שלא נקראו", + "count_mode_all": "שלא נקראו וסך הכול", + "count_mode_none": "ללא מונה", + "delete": "לִמְחוֹק", + "confirm_delete": "האם אתה בטוח שברצונך למחוק את \"{name}\"? הודעות אימייל בתיקייה זו יועברו לאשפה.", + "create": "צור", + "cancel": "לְבַטֵל", + "no_folders": "אין תיקיות מותאמות אישית", + "cannot_delete_role": "לא ניתן למחוק תיקיה עם תפקיד סטנדרטי. Remove the role first.", + "folder_created": "התיקיה נוצרה", + "folder_renamed": "שם התיקיה שונה", + "folder_deleted": "התיקיה נמחקה", + "role_updated": "תפקיד התיקיה עודכן", + "error_create": "יצירת התיקיה נכשלה", + "error_rename": "שינוי שם התיקייה נכשל", + "error_delete": "מחיקת התיקייה נכשלה", + "error_delete_has_children": "לא ניתן למחוק תיקייה: היא עדיין מכילה תיקיות משנה. מחק או העבר אותם תחילה.", + "error_delete_has_email": "לא ניתן למחוק תיקייה: היא עדיין מכילה הודעות דוא\"ל. העבר או מחק אותם תחילה.", + "error_role": "עדכון תפקיד התיקייה נכשל", + "role_memos": "הערות" + }, + "advanced": { + "title": "מתקדם", + "description": "אפשרויות מתקדמות והגדרות מפתח", + "debug_mode": { + "label": "מצב ניפוי באגים", + "description": "אפשר רישום מפורט לצורך פתרון בעיות" + }, + "debug_categories": { + "description": "בחר אילו קטגוריות לרשום. השבת קטגוריות שאינך צריך כדי להפחית את רעש המסוף.", + "jmap": "לקוח JMAP", + "jmap_description": "פעולות תיבת דואר, שליפת דואר אלקטרוני ובקשות פרוטוקול JMAP", + "calendar": "לוּחַ שָׁנָה", + "calendar_description": "אירועי לוח שנה, ייבוא ותזמון הודעות", + "tasks": "משימות", + "tasks_description": "יצירה, אחזור ועדכונים של משימות יומן", + "auth": "אימות", + "auth_description": "כניסה, TOTP, החלפת אסימונים וניהול הפעלה", + "filters": "מסננים", + "filters_description": "כללי סינון ותסריטי חופשה", + "email": "צפייה באימייל", + "email_description": "עיבוד דוא\"ל, עיבוד TNEF וסימון כקריאה", + "push": "הודעות דחיפה", + "push_description": "הגדרה ומשלוח של התראות בדחיפה", + "contacts": "אנשי קשר וספרי כתובות", + "contacts_description": "סנכרון אנשי קשר, פעולות ספר כתובות ושולחים מהימנים" + }, + "settings_sync": { + "label": "סנכרון הגדרות", + "description": "סנכרן את ההגדרות שלך בין דפדפנים ומכשירים" + }, + "sender_favicons": { + "label": "סמלים של השולח (ניסיוני)", + "description": "הצג סמלי אתרים כתמונות פרופיל עבור שולחים עסקיים", + "refresh_label": "רענון סמלי שולחים", + "refresh_description": "טעינה מחדש של סמלי האתרים, למשל כששולח החליף את הסמל שלו", + "refresh_button": "רענון", + "refresh_done": "סמלי השולחים ייטענו מחדש" + }, + "sender_avatars": {}, + "keyboard_shortcuts": { + "label": "קיצורי מקלדת", + "description": "הצג קיצורי מקשים זמינים", + "button": "הצג קיצורי דרך" + }, + "refresh_cache": { + "label": "רענון נתונים שמורים", + "description": "טעינה מחדש של אנשי קשר, לוחות שנה ותיקיות מהשרת. שומר על החשבונות וההתחברויות שלך — מתקן תצוגה מיושנת או שגויה ללא התנתקות.", + "button": "רענון" + }, + "reset_settings": { + "label": "אפס את ההגדרות", + "description": "שחזר את כל ההגדרות לערכי ברירת המחדל", + "button": "אפס לברירות מחדל" + }, + "export_settings": { + "label": "ייצוא הגדרות", + "description": "הורד את ההגדרות שלך בתור JSON", + "button": "ייצוא" + }, + "import_settings": { + "label": "הגדרות ייבוא", + "description": "העלה הגדרות מקובץ JSON", + "button": "ייבוא" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "קוד מקור" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "לחצו על ספאם ופישינג לפני שהם מגיעים לתיבת הדואר הנכנס. תנו לדואר אמיתי לעבור. אם תאבדו את שלושת המגנים, התיבה נופלת.", + "kinds": { + "spam": "ספאם", + "phishing": "פישינג", + "legit": "דואר אמיתי" + }, + "click_to_block": "לחצו לחסימה", + "let_through": "תנו לעבור", + "start": "התחלה", + "play_again": "משחק חוזר", + "score": "ניקוד", + "combo": "בונוס רצף", + "shields_left": "נותרו {count} מתוך {total} מגנים", + "seconds": "{seconds} ש׳", + "best": "שיא: {score}", + "new_best": "שיא חדש", + "held": "התיבה הוגנה", + "overrun": "התיבה נפרצה", + "stats": { + "caught": "נחסמו", + "delivered": "נמסרו", + "mistakes": "טעויות" + } + }, + "show_avatars_in_junk": { + "label": "הצג אווטרים בתיקייה זבל", + "description": "הצג תמונות פרופיל וייקוני אתרים עבור שולחים בתיקייה זבל. מושבת כברירת מחדל כדי להימנע מהענקת לגיטימציה חזותית לניסיונות דיוג." + } + }, + "sidebar_apps": { + "title": "אפליקציות בסרגל הצד", + "description": "נהל אפליקציות וקישורים מותאמים אישית בסרגל הצד שלך", + "keep_loaded": "השאר אפליקציות טעונות", + "keep_loaded_description": "השאר אפליקציות מוטבעות פועלות ברקע בעת המעבר ביניהן כדי למנוע טעינה מחדש", + "managed_title": "סופקו על ידי מנהל המערכת", + "managed_description": "אפליקציות אלו מוגדרות עבור כולם ולא ניתן לשנות אותן.", + "manage_title": "אפליקציות מותאמות אישית", + "manage_description": "הוסף, ערוך או הסר אפליקציות מותאמות אישית מהסרגל הצדדי שלך" + }, + "contacts": { + "title": "אנשי קשר", + "description": "ייבא וייצא את אנשי הקשר שלך", + "import_label": "ייבוא אנשי קשר", + "import_description": "ייבא אנשי קשר מקובץ vCard (.vcf).", + "export_label": "ייצוא אנשי קשר", + "export_description": "ייצא את כל אנשי הקשר כקובץ vCard (.vcf).", + "manage_title": "ספרי כתובות", + "manage_description": "שנה את שם פנקסי הכתובות שלך", + "no_address_books": "לא נמצאו פנקסי כתובות", + "categories_title": "קטגוריות", + "categories_description": "שנה שם של קטגוריות אנשי קשר", + "no_categories": "לא נמצאו קטגוריות", + "group_by_letter_label": "קבוצה לפי אות ראשונה", + "sort_by_last_name_label": "מיון לפי שם משפחה", + "sort_by_last_name_description": "סידור רשימת אנשי הקשר לפי שם משפחה כך שבני אותה משפחה יופיעו יחד", + "group_by_letter_description": "הצג כותרות חלקים אלפביתיים ברשימת אנשי הקשר" + }, + "filters": { + "title": "מסנני דואר אלקטרוני", + "description": "צור כללים למיון, תיוג וניהול אוטומטי של הודעות דוא\"ל נכנסות", + "add_rule": "הוסף כלל", + "no_rules": "אין חוקי סינון", + "no_rules_description": "צור כללים לארגון אוטומטי של הודעות האימייל הנכנסות שלך", + "vacation_active": "Vacation Responder פעיל", + "vacation_active_description": "תשובה אוטומטית מופעלת עבור הודעות נכנסות", + "vacation_configure": "הגדר", + "edit_rule": "ערוך כלל", + "new_rule": "כלל חדש", + "delete_rule": "מחק כלל", + "delete_confirm": "האם אתה בטוח שברצונך למחוק את הכלל הזה?", + "enable": "אפשר", + "disable": "השבת", + "raw_editor": "Raw Sieve עורך", + "raw_editor_warning": "עריכת הסקריפט הגולמי של Sieve עלולה לשבור את עריכת הכללים החזותיים. שינויים שבוצעו כאן עוקפים את הבונה החזותי.", + "validate": "אימות", + "validation_success": "הסקריפט תקף", + "validation_error": "בסקריפט יש שגיאות", + "save": "שמור כללים", + "saving": "שומר...", + "saved": "המסננים נשמרו בהצלחה", + "save_failed": "שמירת המסננים נכשלה", + "loading": "טוען מסננים...", + "not_supported": "שרת הדואר שלך אינו תומך במסנני דואר אלקטרוני.", + "rule_name": "שם כלל", + "rule_name_placeholder": "למשל, מיון ניוזלטרים", + "match_all": "התאם את כל התנאים", + "match_any": "התאם לכל תנאי", + "conditions": "תנאים", + "add_condition": "הוסף מצב", + "actions": "פעולות", + "add_action": "הוסף פעולה", + "stop_processing": "הפסק לעבד את הכללים הבאים", + "include_spam": "להעביר גם הודעות שסומנו כדואר זבל", + "condition_fields": { + "from": "מ", + "to": "ל", + "cc": "עותק", + "subject": "נושא", + "header": "כותרת מותאמת אישית", + "size": "גודל", + "body": "גוף", + "attachment": "קובץ מצורף", + "all": "כל ההודעות" + }, + "comparators": { + "contains": "מכיל", + "not_contains": "אינו מכיל", + "is": "הוא בדיוק", + "not_is": "אינו", + "starts_with": "מתחיל עם", + "ends_with": "מסתיים ב", + "matches": "תואם דפוס", + "greater_than": "גדול מ", + "less_than": "הוא פחות מ", + "has_any": "קיים", + "has_type": "מסוג", + "address_is": "הוא הכתובת", + "domain_is": "שייך לדומיין" + }, + "action_types": { + "move": "העבר לתיקיה", + "copy": "העתק לתיקיה", + "forward": "קדימה ל", + "mark_read": "סמן כנקרא", + "star": "הודעת כוכב", + "add_label": "הוסף תווית", + "discard": "מחק (מחק בשקט)", + "reject": "דחה עם הודעה", + "keep": "שמור בתיבת הדואר הנכנס", + "stop": "תפסיק לעבד" + }, + "move_to_folder": "בחר תיקייה", + "copy_to_folder": "בחר תיקייה", + "forward_to": "העבר לכתובת המייל", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "שמור עותק", + "forward_limit": "מגבלת העברות לכל הודעה בשרת זה: {count}. העברות נוספות מדולגות.", + "forward_unavailable": "שרת זה אינו מאפשר העברה. פעולות העברה בכללי סינון לא ישפיעו.", + "reject_message": "הודעת דחייה", + "reject_placeholder": "האימייל שלך נדחה", + "label_name": "שם התווית", + "label_placeholder": "למשל, חשוב", + "header_name": "שם הכותרת", + "header_placeholder": "למשל, X-Mailing-list", + "size_bytes": "גודל בבתים", + "size_placeholder": "למשל, 1000000", + "system_managed": "כלל מנוהל מערכת", + "opaque_warning": "סקריפט זה נערך מחוץ לבונה החזותי. רק עריכת Sieve גולמית זמינה.", + "open_sieve_editor": "פתח את עורך Sieve הגולמי", + "fetch_error": "טעינת המסננים נכשלה", + "expanded_view": "תצוגה מורחבת", + "expanded_view_description": "הצג כללי סינון עם תנאים מפורטים ואבני פעולה", + "if": "אם", + "then": "ואז", + "match_all_conditions": "כולם תואמים", + "match_any_condition": "כל התאמות", + "and": "ו", + "or": "אוֹ", + "cancel": "לְבַטֵל", + "confirm_delete": "לִמְחוֹק", + "rule_list": "כללי סינון", + "drag_to_reorder": "גרור כדי לסדר מחדש", + "match_type": "סוג התאמה", + "reset_to_visual": "איפוס לבונה חזותי", + "reset_warning": "פעולה זו תמחק את הסקריפט הנוכחי ויתחיל מחדש.", + "confirm_reset": "אִתחוּל", + "validation_empty_name": "נדרש שם כלל", + "validation_empty_conditions": "נדרש לפחות תנאי אחד עם ערך", + "validation_empty_actions": "נדרשת לפחות פעולה אחת", + "templates_section": "התחל מהתבנית", + "template_newsletters": "העבר ניוזלטרים לתיקיה", + "template_receipts": "העבר קבלות לארכיון אוטומטי", + "template_important": "סמן אימיילים חשובים", + "template_notifications": "סינון הודעות", + "sieve_editor": { + "title": "עורך תסריט מסננת", + "warning": "עריכת הסקריפט הגולמי של Sieve עלולה לשבור את עריכת הכללים החזותיים. שינויים שבוצעו כאן עוקפים את הבונה החזותי.", + "script_content": "תסריט מסננת", + "valid": "הסקריפט תקף", + "invalid": "בסקריפט יש שגיאות", + "save_warning": "שמירה תחליף את כל הכללים החזותיים. לא ניתן לבטל זאת. לחץ שוב על שמור כדי לאשר.", + "validating": "מאמת...", + "validate": "אימות", + "cancel": "לְבַטֵל", + "save": "שמור", + "confirm_save": "אשר את השמירה", + "validation_failed": "בקשת האימות נכשלה" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {תנאי אחד} other {# תנאים}}", + "actions_count": "{count, plural, one {פעולה אחת} other {# פעולות}}" + }, + "attachment_type_placeholder": "למשל pdf, doc, jpg", + "value_placeholder_multi": "ערך (מרובים מופרדים בפסיקים)", + "origin_external": "חיצוני", + "managed_by_tooltip": "מנוהל על ידי {source}. ערוך אותו ביישום זה, או השתמש בעורך Sieve הגולמי.", + "quick_rule_names": { + "move_sender": "העבר מ-{sender} אל {folder}", + "move_domain": "העבר מ-@{domain} אל {folder}", + "move_list": "העבר את הרשימה {list} אל {folder}", + "mark_read": "סמן כנקראו מ-{sender}", + "tag": "הוסף תווית {tag} להודעות מ-{sender}", + "block": "חסום את {sender}", + "custom": "הודעות מ-{sender}", + "custom_plain": "כלל חדש", + "senders": "{count, plural, one {שולח אחד} other {# שולחים}}" + }, + "suggestions": "הצעות", + "suggest_subject": "נושא „{subject}”", + "suggest_to": "אל {address}", + "suggest_cc": "עותק {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "החל גם על הודעות שכבר נמצאות בתיקייה זו", + "apply_existing_unsupported": "אפשר להחיל על הודעות קיימות רק כללים שבודקים שולח, נמענים, נושא או כותרות, ושמעבירים, מעתיקים, מסמנים כנקרא, בכוכב או בתווית.", + "period_title": "טווח תאריכים", + "period_start": "תאריך התחלה", + "period_end": "תאריך סיום", + "validation_period_order": "תאריך הסיום חייב להיות אחרי תאריך ההתחלה", + "period_toggle": "פעיל רק בטווח זמן מסוים", + "period_hint": "חל על הודעות שמגיעות בטווח זה. השאירו שדה ריק כדי להשאיר את הקצה הזה פתוח.", + "validation_period_empty": "יש להזין תאריך התחלה או סיום לטווח", + "validation_period_invalid": "יש להזין תאריך תקין עם שעה לטווח, או להשאיר את השדה ריק", + "apply_existing_period": "כלל עם טווח זמן פועל רק על הודעות שמגיעות בתוכו, ולכן לא ניתן להחיל אותו על הודעות קיימות.", + "period_status_scheduled": "מתוזמן", + "period_status_active": "פעיל", + "period_status_expired": "פג תוקף" + }, + "templates": { + "title": "תבניות דואר אלקטרוני", + "description": "צור תבניות דוא\"ל הניתנות לשימוש חוזר עם משתני מיקום", + "add": "תבנית חדשה", + "edit": "ערוך תבנית", + "name": "שם התבנית", + "name_placeholder": "למשל, דוא\"ל מעקב", + "category": "קטגוריה", + "category_placeholder": "למשל, עבודה, אישי", + "subject": "נושא", + "subject_placeholder": "שורת הנושא באימייל", + "body": "גוף", + "body_placeholder": "תוכן גוף בדוא\"ל...", + "recipients_placeholder": "email@example.com", + "identity": "שלח בשם", + "default_identity": "זהות ברירת מחדל", + "favorite": "מועדף", + "cancel": "לְבַטֵל", + "create": "צור", + "update": "לְעַדְכֵּן", + "confirm_delete": "לִמְחוֹק", + "no_templates": "עדיין אין תבניות", + "manage": "נהל תבניות", + "count": "{count, plural, one {תבנית אחת} other {# תבניות}}", + "export_import": "ייצוא וייבוא", + "export_import_description": "גבה את התבניות שלך או העבר אותן למכשיר אחר", + "export": "ייצוא", + "import": "ייבוא", + "validation": { + "empty": "נדרש שם תבנית", + "too_long": "שם התבנית חייב להכיל 200 תווים או פחות" + } + }, + "files": { + "display": { + "title": "תצוגה", + "description": "קבע את אופן הצגת הקבצים והתיקיות" + }, + "default_view": { + "label": "תצוגת ברירת מחדל", + "description": "בחר בין פריסת רשת לרשימה", + "list": "רשימה", + "grid": "רשת" + }, + "default_sort": { + "label": "מיון ברירת מחדל", + "description": "בחר את מיון ברירת המחדל עבור קבצים", + "name": "שֵׁם", + "size": "גודל", + "modified": "שונה" + }, + "sort_direction": { + "label": "כיוון מיון", + "description": "בחר בסדר עולה או יורד", + "ascending": "עולה", + "descending": "יורד" + }, + "icons": { + "title": "אייקונים", + "description": "הגדר את מראה סמל הקובץ" + }, + "show_icons": { + "label": "הצג סמלי קבצים", + "description": "הצג סמלים לצד קבצים ותיקיות" + }, + "colored_icons": { + "label": "סמלים צבעוניים", + "description": "השתמש בסמלים צבעוניים במקום מונוכרום" + }, + "show_thumbnails": { + "label": "הצג תמונות ממוזערות", + "description": "הצג תצוגות מקדימות של תמונה במקום סמלים עבור קובצי תמונה" + }, + "behavior": { + "title": "התנהגות", + "description": "הגדר את התנהגות דפדפן הקבצים" + }, + "show_hidden": { + "label": "הצג קבצים מוסתרים", + "description": "הצג קבצים ותיקיות שמתחילים בנקודה" + }, + "preview": { + "label": "תצוגה מקדימה" + } + }, + "search_placeholder": "חפש בהגדרות", + "search_clear": "נקה חיפוש", + "search_no_results": "לא נמצאו הגדרות תואמות", + "scoped": { + "back": "חזור לחשבוני", + "managing": "ניהול: {name}" + }, + "downloads": { + "title": "הורדות", + "description": "התאם אישית כיצד נקראות הודעות דוא״ל והורדות מקבצים מצורפים.", + "reset": "שחזר ברירת מחדל", + "preview": "תצוגה מקדימה:", + "email_template": { + "label": "שם קובץ דוא״ל (.eml)", + "description": "תבנית המשמשת כאשר אתה ייצוא הודעה דוא״ל או גרור אחת לצורה של קובץ. הסיומת .eml מתוספת באופן אוטומטי." + }, + "attachment_template": { + "label": "שם קובץ מצורף", + "description": "תבנית המשמשת בעת הורדה או גרירת קובץ מצורף. אם תשמיט את ׳{filename}׳ או ׳{ext}׳, הסיומת המקורית נשמרת." + }, + "bundle_template": { + "label": "שם קובץ .zip מרובה-דוא״ל", + "description": "תבנית המשמשת כאשר אתה גורר או מורד כמה הודעות דוא״ל שנבחרו כזיפ יחיד. הסיומת .zip מתוספת באופן אוטומטי." + }, + "spaces": { + "label": "רווחים", + "description": "החלף רווחים בשם הקובץ המתקבל בתו אחר.", + "keep": "שמור רווחים", + "underscore": "החלף בעזר קו תחתי", + "dash": "החלף בעזר מקף" + }, + "lowercase": { + "label": "אותיות קטנות", + "description": "הכוח את כל שם הקובץ לאותיות קטנות." + }, + "strip_diacritics": { + "label": "הסר הנקודות", + "description": "המר אותיות עם דיאקריטיקה לשקולים ASCII שלהם (ä → a, é → e). שימושי לכלים שמטבעו הורגים שמות קבצים Unicode." + }, + "collapse_separators": { + "label": "צמצם מפרידים חוזרים", + "description": "צמצם רצפים של רווחים, קווים תחתיים או מקפים לתו אחד." + }, + "after_export": { + "label": "אחרי ייצוא", + "description": "בחר סלקטיבי להעביר את הדוא״ל לאחר ייצואו כ .eml.", + "keep": "שמור בתיבת דואר", + "archive": "העבר לארכיון", + "trash": "העבר לאשפה" + } + } + }, + "errors": { + "page_error_title": "משהו השתבש", + "page_error_description": "נתקלנו בשגיאה בלתי צפויה. אנא נסה שוב או חזור לדף הבית.", + "sidebar_error": "לא ניתן לטעון תיבות דואר", + "email_list_error": "לא ניתן לטעון מיילים", + "viewer_error_title": "לא ניתן להציג אימייל", + "viewer_error_description": "הייתה בעיה בעיבוד האימייל הזה. הוא עשוי להכיל תוכן שאינו נתמך.", + "composer_error": "לא ניתן לטעון את העורך", + "settings_error_title": "ההגדרות לא זמינות", + "settings_error_description": "לא ניתן לטעון הגדרות. ייתכן שההעדפות שלך לא יישמרו.", + "try_again": "נסה שוב", + "reload": "טען מחדש", + "reload_emails": "טען מחדש מיילים", + "reload_settings": "טען מחדש את ההגדרות", + "retry": "נסה שוב", + "go_home": "עבור לתיבת הדואר הנכנס" + }, + "context_menu": { + "reply": "השב", + "reply_all": "השב לכולם", + "forward": "קדימה", + "mark_read": "סמן כקריאה", + "mark_unread": "סמן כלא נקרא", + "star": "כוכב", + "unstar": "הסר כוכב", + "pin": "הצמד", + "unpin": "בטל הצמדה", + "move_to": "העבר ל...", + "copy_to_account": "העתק לחשבון...", + "remove_from_folder": "הסרה מתיקייה זו", + "archive": "ארכיון", + "delete": "לִמְחוֹק", + "mark_as_spam": "דווח על ספאם", + "not_spam": "לא ספאם", + "tag": "תווית", + "items_selected": "נבחרו הודעות דוא\"ל מסוג{count}", + "forward_as_attachments": "העבר כקבצים מצורפים", + "edit_draft": "ערוך טיוטה", + "cancel_scheduled_send": "ביטול שליחה", + "reschedule_send": "תזמן מחדש", + "cancel_and_edit": "בטל וערוך", + "cancel_and_compose_again": "בטל והרכיב שוב", + "rules": { + "title": "כללים", + "move_from": "תמיד להעביר הודעות מ-{sender}", + "move_from_domain": "תמיד להעביר הודעות מ-@{domain}", + "move_from_list": "תמיד להעביר הודעות מרשימה זו", + "mark_read_from": "תמיד לסמן הודעות מ-{sender} כנקראו", + "tag_with": "תמיד להוסיף תווית", + "block": "{count, plural, one {חסום שולח} other {חסום # שולחים}}", + "no_junk": "לחשבון זה אין תיקיית דואר זבל", + "create": "צור כלל…", + "manage": "ניהול כללים", + "senders_count": "{count, plural, one {שולח אחד} other {# שולחים}}", + "cross_account": "בחר הודעות מחשבון אחד", + "opaque_hint": "המסננים שלך נערכו ידנית. פתח את הגדרות המסננים" , + "loading": "טוען כללים…", + "load_failed": "לא ניתן לטעון את הכללים", + "none": "אין עדיין כללים", + "new_mail_only": "רק להודעות חדשות", + "running": "מריץ את „{name}“ על {folder}…" + }, + "find_emails_from": "חיפוש הודעות מ{name}" + }, + "shortcuts": { + "title": "קיצורי מקלדת", + "tip": "לחץ על? בכל עת כדי להראות את העזרה הזו", + "sections": { + "navigation": "ניווט", + "actions": "פעולות אימייל", + "global": "גלובלי", + "threads": "שרשורים", + "composer": "עורך" + }, + "navigation": { + "next_email": "המייל הבא", + "previous_email": "אימייל קודם", + "open_email": "פתח אימייל", + "close_email": "סגור / בטל בחירה", + "go_to_folder": "מעבר לתיקייה", + "next_unread": "ההודעה הבאה שלא נקראה", + "no_more_unread": "אין הודעות שלא נקראו בהמשך הרשימה" + }, + "actions": { + "reply": "השב", + "reply_all": "השב לכולם", + "forward": "קדימה", + "star": "החלף כוכב", + "archive": "ארכיון", + "delete": "לִמְחוֹק", + "mark_unread": "סמן כלא נקרא", + "mark_read": "סמן כנקרא", + "toggle_spam": "דווח על ספאם / לא ספאם" + }, + "global": { + "compose": "כתוב אימייל חדש", + "search": "התמקד בחיפוש", + "help": "הצג קיצורי דרך", + "refresh": "רענן מיילים", + "select_all": "בחר הכל", + "global_search": "חיפוש בכל מקום", + "extend_selection": "הרחבת הבחירה למטה / למעלה" + }, + "threads": { + "expand_collapse": "הרחב/כווץ את השרשור", + "select_mountain_view": "בחירת השיחה" + }, + "composer": { + "template_picker": "פתח את בוחר התבניות", + "send": "שלח דוא״ל", + "schedule_send": "תזמן שליחה" + } + }, + "threads": { + "messages_one": "הודעת{count}", + "messages_other": "הודעות{count}", + "messages_tooltip": "{count, plural, one {הודעה אחת בשיחה זו} other {# הודעות בשיחה זו}}", + "expand": "הרחב את השיחה", + "collapse": "כווץ השיחה", + "loading": "טוען שיחה...", + "mark_read": "סמן את השיחה כקריאה", + "mark_unread": "סמן את השיחה כלא נקראה", + "archive": "שיחת ארכיון", + "delete": "מחק את השיחה", + "star": "שיחת כוכב", + "unstar": "הסר שיחה בכוכב", + "toggle_thread": "החלף חוט" + }, + "identities": { + "modal_title": "ניהול זהויות שליחה", + "create_new": "צור זהות חדשה", + "edit_identity": "ערוך זהות", + "delete_confirm": "למחוק את הזהות הזו? לא ניתן לבטל זאת.", + "cannot_delete": "לא ניתן למחוק את הזהות הזו", + "primary_identity": "ראשוני", + "set_as_primary": "הגדר כראשי", + "no_identities": "לא נמצאו זהויות", + "display": { + "reply_to": "תגובה ל:", + "bcc": "עותק נסתר:", + "signature": "חתימה:", + "preview": "תצוגה מקדימה:" + }, + "validation_errors": { + "invalid_emails": "הודעות אימייל לא חוקיות:{emails}", + "unknown_error": "שגיאה לא ידועה" + }, + "form": { + "name_label": "שם תצוגה", + "name_placeholder": "למשל, אימייל לעבודה, אישי", + "name_required": "נדרש שם", + "email_label": "כתובת אימייל", + "email_placeholder": "your.email@example.com", + "email_required": "יש צורך באימייל", + "email_invalid": "נא להזין כתובת אימייל חוקית", + "email_immutable": "לא ניתן לשנות כתובת אימייל לאחר היצירה", + "reply_to_label": "תגובה ל (אופציונלי)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "עותק מוסתר אוטומטי (אופציונלי)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "חתימת טקסט", + "html_signature_label": "חתימת HTML", + "save": "שמור זהות", + "cancel": "לְבַטֵל", + "creating": "יוצר...", + "updating": "מעדכן...", + "signature_byte_counter": "{bytes} / {max} בתים", + "signature_byte_limit_reached": "הגבול של השרת הושג" + }, + "sub_address": { + "button_tooltip": "השתמש בכתובת משנה", + "popover_title": "הוסף תג כתובת משנה", + "tag_input_placeholder": "הזן תג (למשל, קניות)", + "preview_label": "תצוגה מקדימה:", + "recent_tags": "תגים אחרונים", + "suggested_tags": "מוצע", + "use_address": "השתמש בכתובת זו", + "invalid_tag": "התג חייב להיות אלפאנומרי ומקפים בלבד", + "tag_too_long": "התג חייב להכיל 30 תווים או פחות", + "help_text": "הודעות דוא\"ל שנשלחות אל user{delimiter}tag@domain.com יגיעו לתיבת הדואר הנכנס שלך", + "validation": { + "empty": "התג לא יכול להיות ריק", + "too_long": "התג חייב להיות{max}תווים או פחות", + "invalid_chars": "התג חייב להכיל רק אותיות, מספרים ומקפים" + } + }, + "badge": { + "sent_via": "דרך", + "sub_address_tag": "נשלח באמצעות כתובת משנה:{tag}", + "identity_name": "נשלח באמצעות זהות:{name}", + "identity_short": "דרך{name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "לִמְחוֹק", + "delete_confirm_title": "מחק זהות" + }, + "templates": { + "picker_title": "בחר תבנית", + "search_placeholder": "חפש תבניות...", + "section_favorites": "מועדפים", + "section_recent": "לאחרונה", + "section_uncategorized": "אחר", + "no_templates": "עדיין אין תבניות", + "no_results": "לא נמצאו תבניות", + "fill_placeholders": "מלא ערכי מציין מיקום", + "enter_value": "הזן ערך...", + "preview": "תצוגה מקדימה", + "insert_with_values": "הוסף עם ערכים", + "insert_raw": "הכנס Raw", + "copy_suffix": "(העתק)", + "placeholder": "משתנה", + "placeholders": { + "recipient_name": "שם הנמען", + "company": "שם החברה", + "date": "תאריך נוכחי", + "day_of_week": "יום בשבוע", + "sender_name": "השם שלך" + } + }, + "contacts": { + "directory": { + "title": "ספרייה", + "search_placeholder": "חיפוש שם או כתובת דוא״ל", + "refresh": "רענון הספרייה", + "retry": "ניסיון חוזר", + "loading": "טוען את הספרייה…", + "unavailable_title": "אין ספרייה", + "unavailable_hint": "שרת הדואר שלך אינו מציע ספריית חשבונות.", + "forbidden_title": "הספרייה אינה זמינה", + "forbidden_hint": "שרת הדואר אינו מאפשר לחשבון זה לעיין בספרייה. פנה למנהל המערכת.", + "error_title": "לא ניתן לטעון את הספרייה", + "error_hint": "בדוק את החיבור ונסה שוב.", + "empty_title": "הספרייה ריקה", + "empty_hint": "שרת הדואר אינו מציג חשבונות עם כתובת דוא״ל.", + "no_match": "אף אחד בספרייה אינו תואם לחיפוש", + "refresh_failed": "לא ניתן לרענן את הספרייה. מוצגת הרשימה האחרונה שנטענה.", + "select_page": "בחירת כולם בעמוד זה", + "deselect_page": "ביטול הבחירה בעמוד זה", + "select_entry": "בחירת {name}", + "range": "{from}–{to} מתוך {total}", + "page": "עמוד {page} מתוך {count}", + "previous_page": "העמוד הקודם", + "next_page": "העמוד הבא", + "picker_title": "בחירה מהספרייה", + "cancel": "ביטול", + "selected_count": "{count} נבחרו", + "add_to": "הוספה לשדה אל", + "add_cc": "הוספה לעותק", + "add_bcc": "הוספה לעותק מוסתר", + "select_hint": "בחר אדם כדי לראות את פרטיו", + "write_email": "כתיבת הודעה", + "add_to_contacts": "הוספה לאנשי קשר", + "email": "דוא״ל", + "read_only_note": "רשומות הספרייה מגיעות משרת הדואר ואי אפשר לערוך אותן כאן.", + "type": { + "individual": "אדם", + "group": "קבוצה", + "resource": "משאב", + "location": "מיקום", + "other": "אחר" + } + }, + "title": "אנשי קשר", + "search_placeholder": "חפש אנשי קשר...", + "create_new": "איש קשר חדש", + "no_category": "אין קטגוריה", + "rename_category": "שנה את שם הקטגוריה", + "category_name_label": "שם קטגוריה", + "category_renamed": "שם קטגוריה שונה", + "category_rename_failed": "שינוי שם הקטגוריה נכשל", + "category_added": "איש קשר נוסף ל-{name}", + "category_added_plural": "אנשי הקשר{count}נוספו ל-{name}", + "empty_state": "עדיין אין אנשי קשר", + "empty_state_title": "עדיין אין אנשי קשר", + "empty_state_subtitle": "צור את איש הקשר הראשון שלך או ייבא מקובץ vCard", + "empty_search": "אין אנשי קשר שתואמים לחיפוש שלך", + "empty_search_hint": "נסה מונח חיפוש אחר", + "clear_search": "נקה חיפוש", + "import_vcard": "ייבוא vCard", + "delete_confirm_title": "מחק איש קשר", + "delete_confirm": "האם אתה בטוח שברצונך למחוק איש קשר זה?", + "local_mode": "אנשי הקשר מאוחסנים באופן מקומי (שרת אינו תומך באנשי קשר ב-JMAP)", + "back_to_contacts": "חזור לאנשי קשר", + "tabs": { + "all": "כֹּל", + "groups": "קבוצות" + }, + "shared": { + "title": "משותף" + }, + "address_books": { + "title": "ספרי הכתובות שלי", + "shared_prefix": "משותף:{name}", + "moved": "איש הקשר הועבר אל{name}", + "moved_plural": "אנשי הקשר של{count}הועברו ל-{name}", + "move_failed": "העברת איש הקשר נכשלה", + "address_book": "ספר כתובות", + "rename": "שנה את שם ספר הכתובות", + "name_label": "שם ספר הכתובות", + "renamed": "שם ספר הכתובות שונה", + "rename_failed": "שינוי שם פנקס הכתובות נכשל", + "default": "ברירת מחדל", + "set_default": "הגדר כברירת מחדל", + "default_updated": "פנקס הכתובות ברירת המחדל עודכן", + "set_default_failed": "נכשל בהגדרת פנקס כתובות ברירת מחדל", + "manage": "ניהול פנקסי כתובות", + "create": "ספר כתובות חדש", + "created": "ספר כתובות נוצר", + "create_failed": "נכשל ביצירת ספר כתובות", + "share": "שתף ספר כתובות", + "new_contact_in_book": "איש קשר חדש בספר כתובות זה", + "delete": "מחק ספר כתובות", + "confirm_delete": "מחק \"{name}\"? כל אנשי הקשר בספר כתובות זה יוסרו.", + "deleted": "ספר כתובות נמחק", + "delete_failed": "נכשל במחיקת ספר כתובות" + }, + "detail": { + "emails": "כתובות אימייל", + "phones": "מספרי טלפון", + "organizations": "ארגונים", + "addresses": "כתובות", + "notes": "הערות", + "titles": "כותרות ותפקידים", + "online_services": "שירותים מקוונים", + "anniversaries": "ימי נישואין", + "personal_info": "מידע אישי", + "languages": "שפות", + "categories": "קטגוריות", + "related_contacts": "אנשי קשר קשורים", + "crypto_keys": "מפתחות קריפטו", + "cert_issuer": "מנפיק", + "cert_expires": "פג תוקף", + "cert_expired": "פג תוקף", + "cert_fingerprint": "טביעת אצבע", + "cert_algorithm": "אלגוריתם", + "import_to_smime": "ייבוא ל-S/MIME", + "cert_already_imported": "כבר מיובא ל-S/MIME", + "cert_imported": "אישור מיובא לחנות S/MIME", + "cert_import_failed": "ייבוא האישור נכשל", + "no_contact_selected": "בחר איש קשר כדי להציג פרטים", + "compose_email": "כתוב אימייל", + "copy_email": "העתק אימייל", + "copy_phone": "העתק את מספר הטלפון", + "copy_url": "העתק כתובת URL", + "copied": "הועתק ללוח", + "copy_failed": "ההעתקה ללוח נכשלה", + "created": "נוצר", + "updated": "עודכן לאחרונה", + "timezone": "אזור זמן", + "anniversary_birth": "יום הולדת", + "anniversary_death": "נפטר", + "anniversary_wedding": "יום נישואין", + "anniversary_other": "אחר", + "personal_expertise": "מומחיות", + "personal_hobby": "תחביב", + "personal_interest": "עניין", + "personal_other": "אחר", + "gender": "מגדר", + "gender_masculine": "זכר", + "gender_feminine": "נקבה", + "gender_other": "אחר", + "gender_none": "לא ישים", + "gender_unknown": "לא ידוע", + "calendar": "לוּחַ שָׁנָה", + "calendar_uri": "כתובת אתר של לוח שנה", + "scheduling_uri": "כתובת אתר לתזמון", + "freebusy_uri": "כתובת אתר פנוי/עסוקה", + "section_contact": "פרטי איש קשר", + "section_work": "עבודה", + "section_personal": "אישי", + "email_default_label": "דוא״ל", + "phone_default_label": "טלפון", + "address_default_label": "כתובת", + "online_service_default_label": "מקוון", + "organization_label": "ארגון", + "title_label": "כותרת", + "role_label": "תפקיד", + "language_label": "שפה", + "related_default_label": "קשור", + "more_actions": "פעולות נוספות", + "age_years": "{count, plural, one {בן שנה} other {בן # שנים}}", + "years_since": "{count, plural, one {שנה} other {# שנים}}" + }, + "form": { + "create_title": "איש קשר חדש", + "edit_title": "ערוך איש קשר", + "section_address_book": "ספרייה", + "select_address_book": "בחר ספרייה...", + "section_identity": "שם וזהות", + "contact_type": "סוג איש קשר", + "type_person": "אדם", + "type_organization": "ארגון", + "section_work": "עבודה וארגון", + "prefix": "קידומת", + "prefix_placeholder": "ד\"ר, מר, גברת.", + "given_name": "שם פרטי", + "middle_name": "שם אמצעי", + "surname": "שם משפחה", + "suffix": "סיומת", + "suffix_placeholder": "ג'וניור, האב, ג'", + "nickname": "כינוי", + "nickname_placeholder": "כינוי", + "email": "אֶלֶקטרוֹנִי", + "email_placeholder": "email@example.com", + "phone": "טלפון", + "phone_placeholder": "+1 234 567 890", + "phone_type": "הקלד", + "phone_voice": "קול", + "phone_cell": "נייד", + "phone_fax": "פקס", + "phone_pager": "ביפר", + "phone_video": "וידאו", + "phone_text": "טקסט", + "organization": "ארגון", + "organization_placeholder": "שם החברה", + "department": "מחלקה", + "department_placeholder": "מחלקה", + "job_title": "תואר עבודה", + "job_title_placeholder": "למשל, מהנדס תוכנה", + "role": "תפקיד", + "role_placeholder": "למשל, ראש צוות", + "addresses": "כתובות", + "add_address": "הוסף כתובת", + "street": "רחוב", + "city": "עיר", + "region": "מדינה/אזור", + "postcode": "מיקוד", + "country": "מדינה", + "online_services": "שירותים מקוונים", + "add_online_service": "הוסף שירות מקוון", + "url_placeholder": "https://...", + "service_placeholder": "שירות", + "anniversaries": "ימי נישואין", + "add_anniversary": "הוסף תאריך", + "anniversary_birth": "יום הולדת", + "anniversary_wedding": "יום נישואין", + "anniversary_death": "זיכרון", + "anniversary_other": "אחר", + "personal_info": "מידע אישי", + "add_personal_info": "הוסף ערך", + "personal_info_placeholder": "למשל, צילום", + "personal_expertise": "מומחיות", + "personal_hobby": "תחביב", + "personal_interest": "עניין", + "personal_other": "אחר", + "level": "רמה", + "level_high": "גבוה", + "level_medium": "בינוני", + "level_low": "נמוך", + "categories": "קטגוריות", + "categories_placeholder": "למשל, משפחה, חברים, עמיתים", + "categories_hint": "הקלד כדי לחפש או להוסיף קטגוריות", + "category_add": "לְהוֹסִיף", + "note": "הערות", + "note_placeholder": "הוסף הערה...", + "gender": "מגדר", + "gender_sex": "סקס", + "gender_male": "זכר", + "gender_female": "נקבה", + "gender_other": "אחר", + "gender_none": "לא ישים", + "gender_unknown": "לא ידוע", + "gender_identity": "זהות מגדרית", + "gender_identity_placeholder": "זהות מגדרית...", + "calendar": "לוּחַ שָׁנָה", + "calendar_uri": "כתובת אתר של לוח שנה", + "scheduling_uri": "כתובת אתר לתזמון", + "freebusy_uri": "כתובת אתר פנוי/עסוקה", + "context_work": "עבודה", + "context_private": "פרטי", + "add_email": "הוסף אימייל", + "add_phone": "הוסף טלפון", + "save": "שמור", + "cancel": "לְבַטֵל", + "creating": "יוצר...", + "updating": "מעדכן...", + "name_required": "יש להזין שם פרטי, שם משפחה או ארגון", + "email_invalid": "נא להזין כתובת אימייל חוקית", + "email_error_inline": "פורמט אימייל לא חוקי", + "save_failed": "שמירת איש הקשר נכשלה", + "delete": "לִמְחוֹק", + "upload_photo": "העלה תמונה", + "remove_photo": "הסר תמונה", + "photo_hint": "JPG או PNG, עד 10 MB. יוגדל מחדש.", + "photo_too_large": "התמונה גדולה מדי (מקס 10 MB)", + "photo_invalid": "קובץ תמונה לא חוקי", + "change_photo": "שנה" + }, + "groups": { + "create": "קבוצה חדשה", + "edit": "ערוך קבוצה", + "empty": "עדיין אין קבוצות", + "delete_confirm_title": "מחק את הקבוצה", + "delete_confirm": "האם אתה בטוח שברצונך למחוק את הקבוצה הזו?", + "name_label": "שם הקבוצה", + "name_placeholder": "למשל, צוות, משפחה", + "name_required": "נדרש שם הקבוצה", + "save_failed": "שמירת הקבוצה נכשלה", + "members_label": "חברים", + "search_members": "חפש אנשי קשר להוספה...", + "no_members": "אין חברים בקבוצה זו", + "member_count": "{count, plural, =0 {אין חברים} one {חבר אחד} other {# חברים}}", + "send_email": "שלח דוא״ל לקבוצה", + "send_email_to": "אל", + "send_email_cc": "עותק", + "send_email_bcc": "עותק נסתר", + "no_member_emails": "לקבוצה זו אין חברים עם כתובת דוא״ל." + }, + "import": { + "title": "ייבוא אנשי קשר", + "drop_hint": "לחץ כדי לבחור קובץ vCard", + "file_types": "קבצי .vcf או .vcard", + "no_contacts": "לא נמצאו אנשי קשר בקובץ", + "parse_error": "ניתוח קובץ vCard נכשל", + "found": "{count, plural, one {נמצא איש קשר אחד} other {נמצאו # אנשי קשר}}", + "duplicate": "שכפול", + "select_all": "בחר הכל", + "deselect_all": "בטל את הבחירה בכולם", + "selected": "{count, plural, one {נבחר אחד} other {נבחרו #}}", + "import_button": "ייבוא", + "importing": "מייבא...", + "success": "{count, plural, one {יובא איש קשר אחד} other {יובאו # אנשי קשר}}", + "failed": "הייבוא נכשל", + "close": "לִסְגוֹר", + "file_too_large": "הקובץ גדול מדי (מקסימום 5MB)" + }, + "export": { + "title": "ייצוא אנשי קשר", + "success": "{count, plural, one {יוצא איש קשר אחד} other {יוצאו # אנשי קשר}}" + }, + "bulk": { + "selected": "{count, plural, one {נבחר אחד} other {נבחרו #}}", + "select_all": "בחר הכל", + "delete": "לִמְחוֹק", + "delete_confirm_title": "מחק אנשי קשר", + "delete_confirm": "למחוק {count, plural, one {איש קשר אחד} other {# אנשי קשר}}?", + "deleted": "{count, plural, one {נמחק איש קשר אחד} other {נמחקו # אנשי קשר}}", + "add_to_group": "הוסף לקבוצה", + "choose_group": "בחר קבוצה", + "adding_contacts": "מוסיף {count, plural, one {איש קשר אחד} other {# אנשי קשר}}", + "added_to_group": "אנשי קשר נוספו לקבוצה", + "export": "ייצוא", + "clear": "נקה בחירה" + }, + "toast": { + "created": "איש קשר נוצר", + "updated": "איש הקשר עודכן", + "deleted": "איש הקשר נמחק", + "error_create": "יצירת איש הקשר נכשלה", + "error_update": "עדכון איש הקשר נכשל", + "error_delete": "מחיקת איש הקשר נכשלה" + }, + "empty_filtered": "אין אנשי קשר תואמים לסננים שלך", + "empty_filtered_hint": "נסה להתאים או להביא סננים", + "back_to_email": "חזור לדוא״ל", + "open_categories": "קטגוריות פתוחות", + "activity": { + "recent_emails": "דוא״ל אחרונים", + "upcoming_events": "אירועים קרובים", + "no_emails": "אין דוא״ל אחרונים", + "no_events": "אין אירועים קרובים", + "no_subject": "(אין נושא)", + "no_title": "(ללא כותרת)", + "load_failed": "נכשל בטעינה", + "unknown_sender": "שולח לא ידוע", + "all_day": "כל היום" + }, + "context_menu": { + "open": "פתח", + "edit": "ערוך", + "send_email": "שלח דוא״ל", + "add_to_group": "הוסף לקבוצה", + "export_vcard": "יצא כ־vCard", + "delete": "מחק", + "call": "קרא", + "duplicate": "שכפל", + "print": "הדפס" + }, + "filters": { + "toggle": "סננים", + "select": "בחר", + "clear": "נקה", + "close": "סגור", + "title": "סננים מתקדמים", + "organization": "חברה", + "organization_placeholder": "למשל Acme Corp", + "job_title": "תפקיד עבודה", + "job_title_placeholder": "למשל עיצוב", + "location": "מיקום", + "location_placeholder": "עיר או מדינה", + "email_domain": "דומיין דוא״ל", + "email_domain_placeholder": "example.com", + "birthday_month": "יום הולדת ב", + "any_month": "כל חודש", + "has_email": "יש דוא״ל", + "has_phone": "יש טלפון", + "has_photo": "יש תמונה", + "sort_by": "מיון לפי", + "sort_first_name": "שם פרטי", + "sort_last_name": "שם משפחה" + } + }, + "calendar": { + "title": "לוח שנה", + "back_to_email": "חזור לדוא״ל", + "back_to_month": "חזרה לחודש", + "my_calendars": "לוחות שנה", + "birthday_calendar": "ימי הולדת", + "mini_calendar_change": "לחץ כדי לשנות חודש", + "views": { + "month": "חודש", + "week": "שבוע", + "day": "יום", + "agenda": "סדר יום", + "today": "היום", + "month_hint": "חודש (מ')", + "week_hint": "שבוע (w)", + "day_hint": "יום (ד)", + "agenda_hint": "סדר יום (א)", + "tasks": "משימות", + "tasks_hint": "משימות (ק)" + }, + "events": { + "hidden_before": "{count, plural, one {אירוע מוקדם יותר אחד} other {# אירועים מוקדמים יותר}}", + "hidden_after": "{count, plural, one {אירוע מאוחר יותר אחד} other {# אירועים מאוחרים יותר}}", + "show_all_hours": "הצגת כל השעות", + "show_display_hours": "הצגת {start} – {end} בלבד", + "create": "צור אירוע", + "edit": "ערוך אירוע", + "delete": "מחק את האירוע", + "details": "פרטי האירוע", + "no_events": "אין אירועים", + "all_day": "כל היום", + "more": "+{count}נוספים", + "show_more": "הצג עוד", + "show_less": "הצג פחות", + "no_title": "(ללא כותרת)", + "resize": "שנה את גודל האירוע", + "duplicate": "שכפול", + "today_header": "היום", + "tomorrow_header": "מחר", + "agenda_show_earlier": "הצגת אירועים קודמים", + "agenda_loading": "טוען…", + "agenda_range_start": "אירועים מ-{date}", + "agenda_range_end": "אירועים עד {date}", + "export_ics": "יצא כ .ics", + "copy_title": "עותק כותרת", + "copy_link": "עותק קישור פגישה", + "new_event": "אירוע חדש", + "new_all_day_event": "אירוע חדש למשך כל היום", + "new_task": "משימה חדשה", + "go_to_today": "עבור להיום" + }, + "detail": { + "add_note": "הוסף הערה...", + "save_note": "שמור", + "note_saved": "הערה נוספה", + "open_link": "פתח קישור", + "copy_location": "העתק מיקום", + "meeting_link": "קישור לפגישה", + "tentative": "טנטטיבי", + "cancelled": "בוטל", + "delete_confirm": "למחוק את האירוע הזה?" + }, + "form": { + "title": "כותרת", + "description": "תיאור", + "location": "מיקום", + "meeting_link": "קישור לפגישה", + "start_date": "תאריך התחלה", + "end_date": "תאריך סיום", + "start_time": "שעת התחלה", + "end_time": "שעת סיום", + "all_day_event": "אירוע כל היום", + "calendar_select": "לוּחַ שָׁנָה", + "save": "שמור", + "cancel": "לְבַטֵל", + "delete_confirm": "האם אתה בטוח שברצונך למחוק את האירוע הזה?", + "color": "צבע" + }, + "participants": { + "availability": { + "title": "זמינות", + "free": "פנוי", + "busy": "עסוק", + "tentative": "טנטטיבי", + "unknown": "לא משתמש בשרת זה", + "checking": "בודק…" + }, + "title": "משתתפים", + "add": "הוסף משתתף", + "organizer": "מארגן", + "attendee": "משתתף", + "accepted": "התקבל", + "declined": "נדחה", + "tentative": "טנטטיבי", + "needs_action": "צריך פעולה", + "remove": "הסר", + "email_placeholder": "הוסף כתובת אימייל או חפש אנשי קשר", + "send_invitations": "שלח הזמנות למשתתפים", + "status_summary": "{accepted}התקבל,{pending}בהמתנה", + "invited_by": "הזמנה מאת{name}", + "respond_below": "השב באמצעות הכפתורים למטה", + "rsvp_label": "התגובה שלך", + "cancel_notification": "המשתתפים יקבלו הודעה על הביטול", + "you_organizer": "אתה המארגן", + "you_attendee": "אתה משתתף", + "no_participants": "אין משתתפים", + "count": "{count, plural, one {משתתף אחד} other {# משתתפים}}", + "edit": "ערוך" + }, + "recurrence": { + "title": "הישנות", + "none": "לא חוזר", + "daily": "יומי", + "weekly": "שבועי", + "monthly": "חודשי", + "yearly": "מדי שנה", + "every_n_days": "כל{count}ימים", + "every_n_weeks": "כל{count}שבועות", + "every_n_months": "כל{count}חודשים", + "until": "עד", + "occurrences": "מופעי{count}", + "custom": "מותאם אישית…", + "edit_custom": "ערוך חזרה מותאמת אישית", + "every_n_years": "כל {count} שנים", + "on_days": "ב־{days}", + "on_day_n": "ביום {day}", + "on_the_nth": "ב־{nth} {day}", + "in_month": "ב־{month}", + "nth_1": "ראשון", + "nth_2": "שני", + "nth_3": "שלישי", + "nth_4": "רביעי", + "nth_last": "אחרון", + "editor_freq_day": "יום", + "editor_freq_week": "שבוע", + "editor_freq_month": "חודש", + "editor_freq_year": "שנה", + "editor_repeats_on": "חוזר ב", + "editor_every": "כל", + "editor_unit_days": "יום(ים)", + "editor_unit_weeks": "שבוע(ות)", + "editor_unit_months": "חודש(ים)", + "editor_unit_years": "שנה(ה)", + "editor_on_day": "ביום", + "editor_on_the": "ב", + "editor_in": "ב", + "editor_ends": "מסתיים", + "editor_never": "לעולם לא", + "editor_ends_on": "ב", + "editor_ends_after": "לאחר", + "editor_occurrences": "הופעות" + }, + "recurrence_scope": { + "edit_title": "ערוך אירוע חוזר", + "delete_title": "מחק אירוע חוזר", + "description": "זהו אירוע שחוזר על עצמו. אילו אירועים תרצה לשנות?", + "this_event": "אירוע זה בלבד", + "this_and_future": "זה והאירועים הבאים", + "all_events": "כל האירועים", + "cancel": "לְבַטֵל", + "save": "שמור", + "delete": "לִמְחוֹק", + "rsvp_title": "השב לאירוע חוזר", + "rsvp_description": "זהו אירוע שחוזר על עצמו. על אילו אירועים חלה התשובה שלך?", + "respond": "השב" + }, + "alerts": { + "title": "תזכורת", + "none": "אין תזכורת", + "at_time": "בזמן האירוע", + "minutes_before": "{count, plural, one {דקה אחת לפני} other {# דקות לפני}}", + "hours_before": "{count, plural, one {שעה אחת לפני} other {# שעות לפני}}", + "days_before": "{count, plural, one {יום אחד לפני} other {# ימים לפני}}", + "weeks_before": "{count, plural, one {שבוע #} other {# שבועות}} לפני", + "unit_minutes_before": "דקות לפני", + "unit_hours_before": "שעות לפני", + "unit_days_before": "ימים לפני", + "unit_weeks_before": "שבועות לפני", + "add": "הוסף תזכורת", + "remove": "הסר תזכורת", + "amount": "כמות תזכורת", + "unit": "יחידת תזכורת", + "action": "סוג תזכורת", + "action_display": "התראה", + "action_email": "אימייל", + "by_email": "{label} באימייל", + "kept": "{count, plural, one {נשמרת תזכורת נוספת אחת שלא ניתן לערוך כאן} other {נשמרות # תזכורות נוספות שלא ניתן לערוך כאן}}", + "other": "{count, plural, one {תזכורת נוספת אחת} other {# תזכורות נוספות}}", + "calendar_defaults": "תזכורות ברירת המחדל של היומן", + "customize": "התאמה אישית", + "use_defaults": "שימוש בתזכורות ברירת המחדל של היומן" + }, + "settings": { + "limit_hours": "הגבלת השעות המוצגות", + "limit_hours_desc": "הצגת שעות אלה בלבד בתצוגת היום והשבוע. אירועים מחוץ להן נשארים נגישים דרך החצים בראש ובתחתית כל יום.", + "visible_hours": "שעות מוצגות", + "visible_hours_start": "תחילת השעות המוצגות", + "visible_hours_end": "סוף השעות המוצגות", + "hide_non_working_days": "הסתרת ימים שאינם ימי עבודה", + "hide_non_working_days_desc": "השמטת הימים שבהם אינך עובד/ת מתצוגת השבוע. האירועים שלהם עדיין מופיעים בתצוגות החודש, היום וסדר היום.", + "working_days": "ימי עבודה", + "title": "הגדרות לוח שנה", + "default_view": "תצוגת ברירת מחדל", + "week_starts_on": "השבוע מתחיל ב", + "time_format": "פורמט זמן", + "default_calendar": "לוח שנה ברירת מחדל", + "default_reminder": "תזכורת ברירת מחדל", + "time_format_12h": "12 שעות", + "time_format_24h": "24 שעות", + "notifications_enabled": "התראות על אירועים", + "notifications_enabled_desc": "הצג התראות עבור אירועי לוח שנה קרובים", + "notification_sound": "צליל התראה", + "notification_sound_desc": "השמע צליל להתראות לוח שנה", + "invitation_parsing": "נתח הזמנות באימייל", + "invitation_parsing_desc": "זיהוי הזמנות ליומן בקבצים מצורפים לאימייל והצג פעולות ביומן", + "show_time_in_month_view": "הצג זמן בתצוגת חודש", + "show_time_in_month_view_desc": "הצג זמני אירועים בתצוגת לוח השנה של החודש", + "show_week_numbers": "הצג מספרי שבוע", + "show_week_numbers_desc": "הצג מספרי שבוע במיני-לוח שנה", + "calendar_free_scroll": "גלילה חופשית", + "calendar_free_scroll_desc": "גלילה רציפה בין חודשים, שבועות וימים במקום תקופה אחת בכל פעם", + "enable_tasks": "אפשר משימות", + "enable_tasks_desc": "הצג תצוגת משימות בלוח השנה לניהול מטלות", + "show_tasks_on_calendar": "הצג משימות בלוח השנה", + "show_tasks_on_calendar_desc": "הצג משימות בתצוגות החודש, השבוע והיום של היומן", + "hover_preview": "תצוגה מקדימה של ריחוף לאירוע", + "hover_preview_desc": "הצג חלון קופץ לפרטי בעת ריחוף מעל אירועי לוח שנה", + "hover_preview_instant": "מיידי", + "hover_preview_delay_500ms": "עיכוב של 0.5 שניות", + "hover_preview_delay_1s": "עיכוב של שנייה", + "hover_preview_delay_2s": "עיכוב של 2 שניות", + "hover_preview_off": "מושבת", + "organizer_identity": "ארגון הזמנות בתור", + "organizer_identity_desc": "כתובת היומן שתשמש כמארגן כשמזמינים אנשים לאירועים.", + "organizer_identity_failed": "שינוי כתובת המארגן נכשל", + "show_birthday_calendar": "צור קשר עם יומן יום הולדת", + "show_birthday_calendar_desc": "הצג לוח שנה וירטואלי עם ימי הולדת מאנשי הקשר שלך" + }, + "days": { + "monday": "שני", + "tuesday": "שלישי", + "wednesday": "רביעי", + "thursday": "חמישי", + "friday": "שישי", + "saturday": "שבת", + "sunday": "ראשון", + "mon": "שני", + "tue": "שלישי", + "wed": "רביעי", + "thu": "חמישי", + "fri": "שישי", + "sat": "שבת", + "sun": "ראשון" + }, + "notifications": { + "event_created": "האירוע נוצר", + "event_updated": "האירוע עודכן", + "event_deleted": "האירוע נמחק", + "calendar_created": "יומן נוצר", + "calendar_deleted": "לוח השנה נמחק", + "event_move_error": "העברת האירוע נכשלה", + "event_resize_error": "שינוי גודל האירוע נכשל", + "alert_title": "אירוע קרוב", + "alert_now": "מתחיל עכשיו", + "alert_in_minutes": "ב-{count}דקות", + "invitation_sent": "נשלחו הזמנות", + "invitations_denied_title": "לא ניתן לשלוח את ההזמנות", + "invitations_denied": "השרת סירב לשלוח את ההזמנות: {reason}. לשמור את האירוע בלי לשלוח אותן?", + "save_without_invitations": "שמור בלי הזמנות", + "rsvp_updated": "התגובה עודכנה", + "rsvp_error": "עדכון התגובה נכשל", + "event_duplicated": "האירוע שוכפל", + "event_error": "שמירת האירוע נכשלה", + "task_due": "משימה בשל", + "event_exported": "אירוע ייצא", + "title_copied": "כותרת הועתקה", + "link_copied": "קישור הועתק" + }, + "status": { + "loading_calendars": "טוען יומנים...", + "loading_events": "טוען אירועים..." + }, + "quick_create": { + "placeholder": "כותרת אירוע חדשה", + "aria_label": "אירוע יצירה מהיר", + "add_title": "הוספת כותרת", + "more_options": "אפשרויות נוספות" + }, + "nav_prev": "הקודם", + "nav_next": "הבא", + "import": { + "title": "ייבוא לוח שנה", + "tab_file": "קובץ", + "tab_url": "כתובת אתר", + "select_file": "בחר קובץ .ics", + "drop_file": "או שחרר את הקובץ כאן", + "supported_formats": "תומך בקבצי iCalendar (.ics).", + "url_description": "הזן את כתובת האתר של עדכון iCalendar (.ics) חיצוני כדי לייבא אירועים.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "תומך בכתובות URL של iCalendar (.ics).", + "fetch": "אחזר", + "invalid_url": "אנא הזן כתובת אתר חוקית", + "url_fetch_failed": "אחזור היומן מ-URL נכשל", + "parsing": "מנתח קובץ לוח שנה...", + "parsed_events": "נמצאו אירועים של{count}", + "no_events": "לא נמצאו אירועים בקובץ", + "select_all": "בחר הכל", + "deselect_all": "בטל את הבחירה בכולם", + "target_calendar": "ייבא ליומן", + "import_button": "ייבא נבחר", + "importing": "מייבא אירועים...", + "success": "אירועי{count}יובאו בהצלחה", + "error": "ייבוא היומן נכשל", + "file_too_large": "הקובץ חורג ממגבלת 10MB", + "invalid_format": "פורמט קובץ לוח שנה לא חוקי" + }, + "management": { + "title": "ניהול לוח שנה", + "description": "צור, שנה שם והתאם אישית את היומנים שלך. לחץ לחיצה ימנית על לוח שנה בסרגל הצד כדי לשנות במהירות את צבעו.", + "name": "שֵׁם", + "name_placeholder": "שם לוח שנה", + "color": "צבע", + "change_color": "שנה צבע", + "add_calendar": "הוסף לוח שנה", + "edit": "ערוך", + "delete": "לִמְחוֹק", + "save": "שמור", + "create": "צור", + "cancel": "לְבַטֵל", + "default": "ברירת מחדל", + "confirm_delete": "למחוק את \"{name}\"? כל האירועים בלוח השנה הזה יוסרו.", + "confirm_clear": "לנקות את כל האירועים מ-\"{name}\"? לא ניתן לבטל זאת.", + "clear_events": "נקה אירועים", + "events_cleared": "אירועי{count}נוקו", + "error_clear": "נכשל ניקוי אירועים ביומן", + "calendar_created": "יומן נוצר", + "calendar_updated": "לוח השנה עודכן", + "calendar_deleted": "לוח השנה נמחק", + "color_updated": "צבע לוח השנה עודכן", + "error_create": "יצירת היומן נכשלה", + "error_update": "עדכון היומן נכשל", + "error_delete": "מחיקת היומן נכשלה", + "caldav_url": "כתובת CalDAV", + "copy_url": "העתק כתובת URL של CalDAV", + "url_copied": "כתובת האתר של CalDAV הועתקה ללוח", + "random_color": "צבע אקראי חדש", + "set_default": "הגדר כברירת מחדל", + "default_updated": "לוח שנה ברירת המחדל עודכן", + "error_default": "נכשל בהגדרת לוח שנה ברירת מחדל", + "share": "שתף לוח שנה", + "new_event_in_calendar": "אירוע חדש בלוח שנה זה", + "shared_with": "משותף עם {names}", + "shared_with_count": "{count, plural, one {משותף עם אדם אחד} two {משותף עם שני אנשים} other {משותף עם # אנשים}}", + "move_up": "עבור למעלה", + "move_down": "עבור למטה", + "kind": "תוכן", + "kind_events": "אירועים", + "kind_tasks": "משימות", + "kind_both": "אירועים ומשימות", + "kind_hint": "קובע אילו אפליקציות יראו את היומן הזה כשהוא מסונכרן דרך CalDAV. לא ניתן לשנות זאת מאוחר יותר.", + "default_alerts": "תזכורות ברירת מחדל", + "default_alerts_title": "תזכורות ברירת מחדל עבור \"{name}\"", + "default_alerts_hint": "אירועים חדשים ביומן הזה יקבלו את התזכורות האלה, אלא אם ישונו באירוע עצמו. שינוי כאן לא משנה אירועים קיימים. תזכורות באימייל נשלחות על ידי שרת הדואר.", + "default_alerts_timed": "אירועים עם שעה", + "default_alerts_all_day": "אירועים של יום שלם", + "default_alerts_updated": "תזכורות ברירת המחדל עודכנו" + }, + "subscription": { + "title": "מנוי iCal", + "section_title": "מנויי iCal", + "description": "הירשם לעדכון iCalendar חיצוני. אירועים יסונכרנו אוטומטית עם היומן שלהם. תומך בכתובות URL של https:// ו-webcal://.", + "url_label": "כתובת אתר של לוח שנה", + "url_placeholder": "https://example.com/calendar.icsאו webcal://...", + "name_label": "שם לוח שנה", + "name_placeholder": "למשל חגים ציבוריים", + "color_label": "צבע", + "refresh_interval": "מרווח רענון", + "interval_15": "כל 15 דקות", + "interval_30": "כל 30 דקות", + "interval_60": "כל שעה", + "interval_360": "כל 6 שעות", + "interval_1440": "כל יום", + "subscribe": "הירשם", + "subscribing": "נרשם כמנוי...", + "save": "שמור שינויים", + "saving": "שומר...", + "edit": "ערוך", + "edit_title": "ערוך מנוי", + "updated": "עודכן \"{name}\"", + "update_error": "עדכון המנוי נכשל", + "invalid_url": "אנא הזן כתובת אתר חוקית", + "success": "נרשם ל-\"{name}\"", + "error": "הוספת המנוי נכשלה", + "refresh": "רענן עכשיו", + "refresh_success": "רענון המנוי", + "refresh_error": "רענון המנוי נכשל", + "unsubscribe": "בטל את המנוי", + "confirm_delete": "לבטל את המנוי ל-\"{name}\"? היומן וכל אירועיו יוסרו.", + "deleted": "המנוי הוסר", + "delete_error": "הסרת המנוי נכשלה", + "last_refreshed": "עודכן לאחרונה:{time}" + }, + "tasks": { + "label": "משימות", + "no_tasks": "אין משימות", + "no_title": "(ללא כותרת)", + "mark_complete": "סמן כהשלמה", + "mark_incomplete": "סמן כלא שלם", + "filter_all": "כֹּל", + "filter_pending": "בהמתנה", + "filter_completed": "הושלם", + "filter_overdue": "באיחור", + "show_completed": "ההצגה הושלמה", + "create": "משימה חדשה", + "edit": "ערוך משימה", + "title_placeholder": "כותרת המשימה", + "description_placeholder": "הוסף תיאור...", + "due_date": "תאריך יעד", + "include_time": "כלול זמן", + "priority": "עדיפות", + "priority_none": "אין", + "priority_high": "גבוה", + "priority_medium": "בינוני", + "priority_low": "נמוך", + "progress": "סטטוס", + "progress_needs_action": "צריך פעולה", + "progress_in_process": "בתהליך", + "progress_completed": "הושלם", + "progress_cancelled": "בוטל", + "calendar": "לוּחַ שָׁנָה", + "alert": "תזכורת", + "alert_none": "אין", + "alert_at_time": "בזמן תאריך היעד", + "alert_5min": "5 דקות לפני", + "alert_15min": "15 דקות לפני", + "alert_30min": "30 דקות לפני", + "alert_1hr": "שעה אחת לפני", + "alert_1day": "יום אחד לפני", + "delete": "לִמְחוֹק", + "cancel": "לְבַטֵל", + "save": "שמור", + "quick_add_placeholder": "הוסף משימה...", + "due_today": "היום", + "due_tomorrow": "מחר", + "overdue": "באיחור" + }, + "months": { + "jan": "ינו׳", + "feb": "פבר׳", + "mar": "מרץ", + "apr": "אפר׳", + "may": "מאי", + "jun": "יון׳", + "jul": "יול׳", + "aug": "אוג׳", + "sep": "ספט׳", + "oct": "אוק׳", + "nov": "נוב׳", + "dec": "דצמ׳", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "פתח תפריט", + "webcal_action": { + "title": "פתח קישור לוח שנה", + "description": "כיצד היית רוצה להשתמש ב־\"{name}\"?", + "import_title": "ייבא פעם אחת", + "import_description": "קבל את האירועים כעת והעתק אותם לאחד לוחות השנה שלך.", + "subscribe_title": "הירשם", + "subscribe_description": "שמור את הלוח שנה הזה בסנכרון אוטומטי כלוח שנה נפרד.", + "cancel": "בטל" + }, + "toolbar": { + "more": "פעולות נוספות", + "select_view": "החלפת תצוגה, כעת {view}" + }, + "other_calendars": "יומנים אחרים", + "calendar_options": "אפשרויות עבור {name}" + }, + "advanced_search": { + "title": "חיפוש מתקדם", + "from": "מ", + "from_placeholder": "אימייל או שם השולח", + "to": "ל", + "to_placeholder": "אימייל או שם של הנמען", + "subject": "נושא", + "subject_placeholder": "הנושא מכיל...", + "body": "גוף", + "body_placeholder": "הגוף מכיל...", + "folder": "תיקיה", + "all_folders": "כל התיקיות", + "all_folders_except_spam_trash": "כל התיקיות מלבד ספאם ואשפה", + "has_attachment": "קבצים מצורפים", + "date_after": "אחרי", + "date_before": "לפני", + "size_min": "גדול מ־(KB)", + "size_max": "קטן מ־(KB)", + "starred": "מסומן בכוכב", + "unread": "לא נקרא", + "read": "קרא", + "yes": "כן", + "no": "לא", + "clear": "ברור", + "clear_all": "נקה הכל", + "filters_active": "מסנן{count}", + "filters_active_plural": "מסנני{count}", + "toggle_filters": "עוד", + "search_hint": "השתמש במסננים מתקדמים לחיפוש מדויק", + "advanced_filters_tooltip": "מסנני חיפוש מתקדמים", + "results_found": "{count, plural, =0 {לא נמצאו תוצאות} one {נמצאה תוצאה אחת} other {נמצאו # תוצאות}}", + "results_found_more": "נמצאו תוצאות{count}+", + "suggestions_recent": "חיפושים אחרונים", + "suggestions_people": "אנשים", + "suggestions_from": "חיפוש הודעות מאת {address}", + "suggestions_to": "חיפוש הודעות אל {address}", + "suggestions_remove_recent": "הסרה מהחיפושים האחרונים", + "has_words": "מכיל את המילים", + "size": "גודל", + "date_within": "תאריך", + "search_in": "חיפוש ב", + "has_attachment_check": "יש קובץ מצורף", + "search": "חיפוש" + }, + "welcome": { + "title": "ברוך הבא לתיבת הדואר שלך", + "tip_compose": "הקש c כדי לכתוב דוא\"ל חדש", + "tip_shortcuts": "לחץ על? כדי לראות את כל קיצורי המקלדת", + "tip_sidebar": "מצא את לוח השנה, אנשי הקשר וההגדרות בתפריט סרגל הצד", + "tip_settings": "התאם אישית את החוויה שלך בהגדרות", + "tip_compose_touch": "הקישו על כפתור העיפרון כדי לכתוב הודעה חדשה", + "tip_sidebar_touch": "יומן, אנשי קשר והגדרות נמצאים בסרגל התחתון", + "got_it": "הבנתי", + "settings": "הגדרות", + "dismiss": "לְפַטֵר", + "start_tour": "התחל סיור" + }, + "demo_welcome": { + "title": "ברוכים הבאים ל-Bolwark Mail", + "description": "חקור לקוח דואר אינטרנט עם תכונות מלאות - ישירות בדפדפן שלך. כל הנתונים נשארים במכשיר שלך, אז אל תהסס לבדוק הכל.", + "feature_email": "קרא וכתוב אימייל", + "feature_organize": "תגיות, כוכבים ותיקיות", + "feature_shortcuts": "קיצורי מקלדת", + "feature_privacy": "100% הדגמה פרטית", + "hint": "לחץ על אימייל כלשהו מימין כדי להתחיל, או צא לסיור למטה." + }, + "files": { + "office_edit": "עריכת מסמך", + "office_loading": "פותח את עורך המסמכים…", + "office_error": "לא ניתן היה לפתוח את עורך המסמכים.", + "office_close": "סגירת העורך", + "title": "קבצים", + "search_placeholder": "חפש קבצים...", + "empty_state_title": "אין עדיין קבצים", + "upload_files": "העלה קבצים", + "new_folder": "תיקיה חדשה", + "new_folder_name": "שם התיקיה", + "rename": "שנה שם", + "rename_title": "שנה שם", + "new_name": "שם חדש", + "name_forbidden_chars": "השרת אינו מתיר את התווים האלה בשמות: {chars}", + "name_reserved": "השם הזה שמור בשרת.", + "delete": "לִמְחוֹק", + "delete_confirm_title": "מחק משאב", + "delete_confirm_message": "האם אתה בטוח שברצונך למחוק את \"{name}\"? לא ניתן לבטל זאת.", + "download": "הורד", + "name": "שֵׁם", + "size": "גודל", + "modified": "שונה", + "type": "הקלד", + "folder": "תיקיה", + "file": "קובץ", + "uploading": "מעלה...", + "upload_success": "{count, plural, one {קובץ אחד הועלה} other {# קבצים הועלו}}", + "upload_canceled": "ההעלאה בוטלה", + "upload_canceled_partial": "{count, plural, one {קובץ אחד הועלה} two {שני קבצים הועלו} other {# קבצים הועלו}} לפני הביטול", + "upload_error": "העלאת הקובץ נכשלה", + "create_folder_success": "התיקיה נוצרה", + "create_folder_error": "יצירת התיקיה נכשלה", + "delete_success": "נמחק בהצלחה", + "delete_error": "המחיקה נכשלה", + "rename_success": "השם שונה בהצלחה", + "rename_error": "שינוי השם נכשל", + "download_error": "ההורדה נכשלה", + "not_available": "אחסון קבצים אינו זמין בשרת זה", + "cancel": "לְבַטֵל", + "create": "צור", + "save": "שמור", + "no_results": "אין קבצים שתואמים לחיפוש שלך", + "batch_delete_confirm_message": "האם למחוק {count, plural, one {פריט אחד} other {# פריטים}}? לא ניתן לבטל זאת.", + "batch_delete_success": "{count, plural, one {פריט אחד נמחק} other {# פריטים נמחקו}}", + "grid_view": "תצוגת רשת", + "list_view": "תצוגת רשימה", + "details": "פרטים", + "path": "נתיב", + "preview": "תצוגה מקדימה", + "preview_error": "טעינת התצוגה המקדימה נכשלה", + "cut": "גזור", + "copy": "העתק", + "paste": "הדבק", + "move_success": "{count, plural, one {פריט אחד הועבר} other {# פריטים הועברו}}", + "move_error": "לא הצליח לזוז", + "paste_success": "הודבק בהצלחה", + "paste_error": "ההדבקה נכשלה", + "new_text_file": "קובץ טקסט חדש", + "file_name": "שם הקובץ", + "retry": "נסה שוב", + "refresh": "רענן", + "duplicate": "שכפול", + "duplicate_success": "שוכפל בהצלחה", + "duplicate_error": "השכפול נכשל", + "create_file_success": "הקובץ נוצר", + "create_file_error": "יצירת הקובץ נכשלה", + "favorites": "מועדפים", + "recent": "לאחרונה", + "show_in_folder": "הצג בתיקייה", + "remove_from_recent": "הסר מהאחרונים", + "upload_folder": "העלה תיקייה", + "file_too_large": "\"{name}\" חורג מגודל הקובץ המרבי ({max})", + "undo": "בטל", + "undo_success": "הפעולה בוטלה", + "undo_error": "הביטול נכשל", + "toolbar": "פעולות קובץ", + "file_list": "קבצים ותיקיות", + "context_menu": "פעולות", + "settings_ascending": "עולה", + "settings_descending": "יורד", + "disabled_title": "תכונת הקבצים מושבתת על ידי מנהל המערכת שלך", + "disabled_description": "העלאות קבצים גדולות דרך WebDAV יכולות לגרום לחוסר יציבות של Stalwart/RocksDB, כולל קריסות מחוץ לזיכרון ושימוש בדיסק בלתי ניתן לשחזור. לא ניתן לנקות קבצים שנמחקו באופן מיידי מאחסון כתמים. תכונה זו אינה מומלצת עבור סביבות ייצור.", + "open_in_new_tab": "פתח בלשונית חדשה", + "no_accounts": "אין חשבונות מחוברים.", + "share": "שתף", + "shared": "משותף", + "shared_with_me": "משותף איתי", + "shared_by": "משותף על ידי {name}", + "open_folder_tree": "פתח עץ תיקייה", + "migration_title": "עדכון הקבצים שלך…", + "migration_description": "ארגון תיקיות וקבצים לתוך המבנה הנכון שלהם. זה קורה רק פעם אחת.", + "new": "חדש", + "my_files": "הקבצים שלי", + "folders": "תיקיות", + "files_heading": "קבצים", + "open": "פתח", + "star": "סמן בכוכב", + "unstar": "הסר כוכב", + "expand_folder": "הרחב את {name}", + "collapse_folder": "כווץ את {name}", + "sidebar_hint": "סמן תיקיות בכוכב ופתח קבצים כדי למצוא אותם כאן.", + "breadcrumbs": "נתיב התיקייה", + "show_path": "הצג תיקיות מוסתרות בנתיב", + "selection_actions": "פעולות לפריטים שנבחרו", + "selected_count": "{count, plural, one {פריט אחד נבחר} two {2 נבחרו} other {# נבחרו}}", + "clear_selection": "נקה בחירה", + "select_all": "בחר הכול", + "select_item": "בחר את {name}", + "more_actions": "פעולות נוספות עבור {name}", + "paste_here": "הדבק בתיקייה זו", + "paste_count": "{count, plural, one {הדבק פריט אחד} two {הדבק שני פריטים} other {הדבק # פריטים}}", + "sort_by": "מיין לפי", + "view_mode": "פריסה", + "clear_search": "נקה חיפוש", + "no_results_for": "אין בתיקייה זו פריטים שתואמים ל-\"{query}\"", + "download_count": "{count, plural, one {הורד קובץ אחד} two {הורד שני קבצים} other {הורד # קבצים}}", + "delete_count": "{count, plural, one {מחק פריט אחד} two {מחק שני פריטים} other {מחק # פריטים}}", + "close_details": "סגור פרטים", + "file_kind": "קובץ {ext}", + "folder_summary": "{folders, plural, =0 {אין תיקיות} one {תיקייה אחת} two {שתי תיקיות} other {# תיקיות}}, {files, plural, =0 {אין קבצים} one {קובץ אחד} two {שני קבצים} other {# קבצים}}", + "details_hint": "בחר קובץ או תיקייה כדי לראות את הפרטים.", + "sharing": "שיתוף", + "shared_with_count": "{count, plural, one {משותף עם אדם אחד} two {משותף עם שני אנשים} other {משותף עם # אנשים}}", + "only_you": "רק אתה", + "empty_folder_title": "התיקייה הזו ריקה", + "empty_folder_description": "גרור קבצים לכאן, או העלה אותם מהמכשיר שלך.", + "empty_folder_read_only": "אין אפשרות להוסיף קבצים לתיקייה זו.", + "choose_account": "בחר חשבון", + "drop_to_upload": "שחרר כדי להעלות אל {folder}", + "uploading_count": "מעלה {current} מתוך {total}", + "dismiss": "סגור", + "zoom_in": "הגדל", + "zoom_out": "הקטן", + "zoom_reset": "התאם למסך", + "rotate": "סובב", + "close_preview": "סגור" + }, + "smime": { + "your_certificates": "התעודות שלך", + "your_certificates_desc": "ייבא ונהל את אישורי ה-S/MIME שלך עבור חתימה והצפנה של הודעות דוא\"ל", + "recipient_certificates": "אישורי נמען", + "recipient_certificates_desc": "אישורים ציבוריים להצפנת מיילים לנמענים", + "identity_bindings": "כריכות מפתח זהות", + "identity_bindings_desc": "קשר אישורי S/MIME לזהויות הדוא\"ל שלך", + "defaults_title": "ברירות מחדל", + "defaults_desc": "הגדר התנהגות ברירת מחדל של חתימה והצפנה", + "import_pkcs12": "ייבוא PKCS#12 (.p12/.pfx)", + "import_public_cert": "אישור ייבוא", + "no_certificates": "עדיין לא יובאו אישורים", + "no_recipient_certs": "אין אישורי נמען", + "expires": "פג תוקף", + "expired": "פג תוקף", + "bound_to": "מחויב ל", + "no_key_bound": "אין", + "lock": "מפתח נעילה", + "unlock": "מפתח נעילה", + "details": "הצג פרטים", + "delete": "לִמְחוֹק", + "encrypt_by_default": "הצפנה כברירת מחדל", + "encrypt_by_default_desc": "הצפן הודעות דוא״ל באופן אוטומטי כאשר לכל הנמענים יש תעודות", + "remember_unlocked": "זכור מפתחות לא נעולים", + "remember_unlocked_desc": "השאר את המפתחות פתוחים למשך הפעלת הדפדפן הזו", + "sign_default_for": "חתום כברירת מחדל עבור", + "enter_p12_passphrase": "הזן ביטוי סיסמה PKCS#12", + "p12_passphrase_desc": "הזן את ביטוי הסיסמה שמגן על קובץ האישור הזה", + "enter_storage_passphrase": "הגדר ביטוי סיסמה לאחסון", + "storage_passphrase_desc": "בחר ביטוי סיסמה כדי להגן על מפתח זה במצב מנוחה בדפדפן שלך", + "next": "הבא", + "import": "ייבוא", + "unlock_key": "מפתח נעילה", + "unlock_key_desc": "הזן את משפט הסיסמה לאחסון כדי לפתוח מפתח זה לצורך חתימה או פענוח", + "passphrase_placeholder": "הזן ביטוי סיסמה", + "confirm_passphrase_placeholder": "אשר את ביטוי הסיסמה", + "passphrase_mismatch": "ביטויי הסיסמה אינם תואמים", + "cancel": "לְבַטֵל", + "processing": "מעבד...", + "close": "לִסְגוֹר", + "certificate_details": "פרטי תעודה", + "cert_subject": "בעל האישור", + "cert_issuer": "מנפיק", + "cert_email": "אֶלֶקטרוֹנִי", + "cert_serial": "מספר סידורי", + "cert_validity": "תוקף", + "cert_fingerprint": "טביעת אצבע (SHA-256)", + "cert_algorithm": "אלגוריתם", + "cert_capabilities": "יכולות", + "cert_source": "מקור", + "cert_expired": "תוקף האישור הזה פג", + "cert_not_yet_valid": "אישור זה עדיין אינו תקף", + "cap_sign": "חתימה", + "cap_encrypt": "הצפנה", + "cap_none": "אין", + "show_passphrase": "הצג ביטוי סיסמה", + "hide_passphrase": "הסתר ביטוי סיסמה", + "sign_toggle": "לחתום", + "encrypt_toggle": "הצפין", + "missing_recipient_certs": "חסרים תעודות עבור:{emails}", + "missing_sender_cert": "אין אישור הקשור לזהות זו", + "status_encrypted_ok": "הודעה זו הוצפנה", + "status_encrypted_no_key": "הודעה זו מוצפנת אך לא נמצא מפתח תואם", + "status_encrypted_failed": "פענוח ההודעה הזו נכשל", + "status_signed_valid": "חתימה מאומתת", + "status_signed_invalid": "אימות החתימה נכשל", + "status_signed_expired_cert": "חתום עם תעודה שפג תוקפו", + "status_signed_self_signed": "החתימה תקפה, אבל האישור חתום בעצמו (לא מהימן)", + "status_signed_mismatch": "החתימה תקפה, אך החותם אינו תואם לשולח", + "status_unsupported": "פורמט S/MIME לא נתמך", + "auto_import_signer_certs": "ייבוא אוטומטי של אישורי חתימה", + "auto_import_signer_certs_desc": "שמור באופן אוטומטי תעודות מהודעות חתומות מאומתות לקריפציה עתידית", + "export": "ייצוא", + "enter_export_passphrase": "הגדר לייצא ביטוי סיסמה", + "export_passphrase_desc": "בחר ביטוי סיסמה כדי להגן על קובץ PKCS#12 המיוצא", + "export_storage_desc": "הזן את ביטוי הסיסמה לאחסון כדי לפענח את המפתח לייצוא", + "incorrect_passphrase": "ביטוי סיסמה שגוי" + }, + "tour": { + "step_counter": "שלב{current}של{total}", + "skip": "דלג על סיור", + "back": "חזרה", + "next": "הבא", + "finish": "סיים", + "take_a_tour": "צא לסיור בממשק", + "restart_title": "סיור היכרות", + "restart_desc": "הפעל מחדש את ההליכה המודרכת של הממשק", + "restart_button": "התחל את הסיור מחדש", + "sidebar_title": "תיבות הדואר שלך", + "sidebar_desc": "זהו סרגל הצד של התיקיות שלך. לחץ על כל תיבת דואר כדי להציג את המיילים שלה. אתה יכול ליצור תיקיות, לגרור אימיילים ביניהן ולראות ספירות שלא נקראו במבט אחד.", + "compose_title": "כתוב אימייל", + "compose_desc": "לחץ כאן כדי לכתוב מייל חדש. אתה יכול להוסיף נמענים, קבצים מצורפים ולהשתמש בעיצוב טקסט עשיר.", + "search_title": "חפש בדואר שלך", + "search_desc": "חפש לפי שולח, נושא או תוכן. לחץ על סמל הסינון עבור אפשרויות מתקדמות כמו טווח תאריכים, קבצים מצורפים והודעות מסומנות בכוכב.", + "email_list_title": "רשימת הדוא\"ל שלך", + "email_list_desc": "מיילים מופיעים כאן. לחץ על אחד כדי לקרוא אותו בצד ימין. השתמש בתיבת הסימון כדי לבחור מרובים, ולאחר מכן העבר, מחק או תיוג אותם בכמות גדולה.", + "email_viewer_title": "חלונית קריאה", + "email_viewer_desc": "האימייל שנבחר נפתח כאן. השב, העבר, העבר לארכיון או מחק באמצעות לחצני סרגל הכלים. אתה יכול גם לסמן מיילים בכוכב או להוסיף תגיות צבע.", + "keywords_title": "תגי צבע", + "keywords_desc": "ארגן את הדואר האלקטרוני שלך עם תגים מקודדים בצבע. גרור הודעת דוא\"ל לתג כדי לתייג אותו, או לחץ באמצעות לחצן העכבר הימני על דוא\"ל כדי להקצות תגים.", + "calendar_title": "לוּחַ שָׁנָה", + "calendar_desc": "עבור ליומן כדי לנהל את האירועים שלך. צור אירועים, הגדר תזכורות והצג פריסות של יום, שבוע או חודש.", + "contacts_title": "אנשי קשר", + "contacts_desc": "ספר הכתובות שלך גר כאן. ייבא אנשי קשר, צור קבוצות ולחץ על כל איש קשר כדי לראות את הפרטים המלאים שלהם.", + "settings_title": "הגדרות", + "settings_desc": "התאם אישית הכל: ערכת נושא, צפיפות, חתימות, מסננים, קיצורי מקלדת, ברירות מחדל של לוח שנה ועוד.", + "shortcuts_title": "קיצורי מקלדת", + "shortcuts_desc": "משתמשי כוח אוהבים את זה. לחץ על? בכל עת כדי לראות את כל קיצורי הדרך הזמינים. אתה יכול לנווט, לכתוב ולנהל מיילים מבלי לגעת בעכבר.", + "compose_open_title": "העורך", + "compose_open_desc": "זה כותב הדוא\"ל. הוסף נמענים, כתוב את ההודעה שלך, צרף קבצים והשתמש בעיצוב טקסט עשיר. אתה יכול גם לשמור טיוטות ולהשתמש בתבניות.", + "calendar_view_title": "לוח השנה שלך", + "calendar_view_desc": "הנה היומן שלך עם אירועים לדוגמה. אתה יכול לעבור בין תצוגות יום, שבוע, חודש וסדר יום באמצעות סרגל הכלים.", + "create_event_title": "צור אירוע", + "create_event_desc": "לחץ על כפתור זה כדי ליצור אירוע לוח שנה חדש. אתה יכול להגדיר כותרת, תאריך, שעה ולהוסיף משתתפים.", + "event_modal_title": "פרטי האירוע", + "event_modal_desc": "הנה טופס האירוע. מלא את הכותרת, בחר תאריך ושעה, הוסף מיקום או משתתפים. לחץ על שמור כשתסיים - או סגור אותו והמשיך הלאה.", + "contacts_list_title": "אנשי הקשר שלך", + "contacts_list_desc": "הנה אנשי הקשר שלך. לחץ על כל איש קשר כדי לראות את הפרטים המלאים שלו בצד ימין. אתה יכול גם ליצור אנשי קשר חדשים, לייבא כרטיסי vCard או לארגן אנשי קשר בקבוצות.", + "settings_tabs_title": "תפריט הגדרות", + "settings_tabs_desc": "להלן כל קטגוריות ההגדרות. התאם אישית את המראה שלך, נהל זהויות, הגדר מסנני דוא\"ל, הגדר את היומן שלך ועוד הרבה יותר.", + "files_title": "אחסון קבצים", + "files_desc": "דפדפן הקבצים שלך מאפשר לך להעלות, לארגן ולשתף קבצים - כמו כונן ענן אישי המובנה בדואר שלך.", + "demo_banner_title": "בקרות הדגמה", + "demo_banner_desc": "אתה במצב הדגמה - הכל נשאר בדפדפן שלך. לחץ על 'אפס הדגמה' בכל עת כדי להתחיל מחדש עם נתוני מדגם נקיים.", + "quota_title": "שימוש באחסון", + "quota_desc": "עקוב אחר גודל תיבת הדואר שלך כאן. העיגול מתמלא ככל שאתה מנצל יותר מקום.", + "show_on_new_devices_title": "הצג בהתקנים חדשים", + "show_on_new_devices_desc": "הפעל מחדש את בר הברכה ושיוך את הסיור בפעם הראשונה שאתה נכנס בהתקן חדש, גם אם כבר השלמת אותו במקום אחר" + }, + "meta_description": "לקוח webmail מינימליסטי באמצעות פרוטוקול JMAP", + "protocol_handlers": { + "title": "יישומים ברירת מחדל", + "description": "בחר אם קישורי דוא״ל ולוח שנה ייפתחו ב־Bulwark. מבחינה טכנית, Bulwark רשום כטיפול בפרוטוקול עבור קישורי mailto: ו־webcal:.", + "unsupported": "דפדפן זה או חיבור זה לא תומך בהרשמה ידנית של טיפול בפרוטוקול. ייתכן שעדיין תוכל להשתמש ב־PWA המותקנת דרך הדפדפן או הגדרות מערכת ההפעלה.", + "mailto_label": "קישורי דוא״ל", + "mailto_description": "פתח קישורי mailto: ב־Bulwark עם מחבר שמלא מראש.", + "protocol_open_mode_label": "בעת פתיחת קישורי פרוטוקול", + "protocol_open_mode_description": "בחר אם Bulwark יפתח קישורי mailto: ו־webcal: בלשונית חדשה או ישתמש בחיבור פתוח. אפשרות הישיבה הפעילה דורשת הרשאת עדכון כדי שתוכל ללחוץ על עדכון נופל כדי להביא את Bulwark לחזית אם הדפדפן חוסם מיקוד.", + "protocol_open_mode_active_session": "פתח בישיבה פעילה אם אפשר", + "protocol_open_mode_new_tab": "פתח תמיד לשונית חדשה", + "focus_notification_title": "פתח את Bulwark", + "focus_notification_body": "הקישור נפתח ב־Bulwark. לחץ כדי להביא את החלון לחזית.", + "webcal_label": "קישורי לוח שנה", + "webcal_description": "פתח קישורי webcal: ב־Bulwark עם תיבת דו־שיח מלא מראש להרשמה ללוח שנה.", + "register_mailto": "רשום יישום דוא״ל", + "register_webcal": "רשום יישום לוח שנה", + "mailto_registered": "בקש הרשמה של מטפל דוא״ל", + "webcal_registered": "בקש הרשמה של מטפל לוח שנה", + "registration_failed": "הרשמת טיפול בפרוטוקול נכשלה", + "opening_mailto": "פתיחת מחבר…", + "opening_webcal": "פתיחת לוח שנה…", + "browser_note": "הדפדפן או מערכת ההפעלה שלך עשויים לבקש ממך לאשר זאת ועשויים לדרוש שBulwark יותקן לפני שניתן לבחור אותו כיישום ברירת המחדל.", + "select_account_title": "בחר חשבון", + "select_mailto_account": "בחר איזה חשבון צריך לפתוח קישור דוא״ל זה.", + "select_webcal_account": "בחר איזה חשבון צריך לפתוח קישור לוח שנה זה.", + "select_account_note": "זה חל רק על קישור פרוטוקול זה.", + "detail_to": "אל", + "detail_subject": "נושא", + "detail_no_subject": "אין נושא", + "detail_calendar": "לוח שנה", + "detail_source": "מקור", + "active_account": "פעיל", + "switching_account": "החלפת חשבון…" + }, + "mailbox_context_menu": { + "mark_folder_read": "סמן תיקייה כקרויה", + "mark_folder_tree_read": "סמן תיקייה ותת־תיקיות כקרויות", + "mark_all_folders_read": "סמן את כל התיקיות כקרויות", + "new_subfolder": "תת־תיקייה חדשה…", + "new_folder": "תיקייה חדשה…", + "rename": "שנה שם…", + "share": "שיתוף...", + "import_email": "ייבא .eml או .zip…", + "empty_folder": "תיקייה ריקה", + "empty_folder_generic": "תיקייה ריקה", + "delete_folder": "מחק תיקייה", + "refresh": "רענן", + "hide_shared_account": "הסתרה מסרגל הצד", + "mute_notifications": "השתקת התראות", + "unmute_notifications": "ביטול השתקת התראות", + "mute_account_notifications": "השתקת התראות לחשבון זה", + "unmute_account_notifications": "ביטול השתקת התראות לחשבון זה", + "reset_folder_notifications": "איפוס התראות התיקיות", + "mark_all_confirm_title": "סמן את כל התיקיות כקרויות", + "mark_all_confirm_message": "סמן כל הודעה שלא קרויה בחשבון האישי שלך כקרויה?", + "delete_confirm_title": "מחק תיקייה", + "delete_confirm_message": "מחק לצמיתות את התיקייה \"{name}\"? לא ניתן לבטל פעולה זו.", + "prompt_new_subfolder": "הזן שם לתת־תיקייה החדשה.", + "prompt_new_folder": "הזן שם לתיקייה החדשה.", + "prompt_rename": "הזן שם חדש לתיקייה זו.", + "placeholder_folder_name": "שם תיקייה", + "create": "צור", + "rename_confirm": "שנה שם", + "toast_marked_read": "התיקייה סומנה כקרויה", + "toast_marked_read_count": "סומנו {count, plural, one {הודעה 1} other {# הודעות}} כקרויות", + "toast_already_read": "אין הודעות שלא קרויות", + "toast_marked_all_read": "כל התיקיות סומנו כקרויות", + "toast_emptied": "התיקייה התרוקנה", + "toast_folder_created": "תיקייה נוצרה", + "toast_folder_renamed": "שם התיקייה שונה", + "toast_folder_deleted": "התיקייה נמחקה", + "toast_error_mark_read": "נכשל בסימון כקרויה", + "toast_error_empty": "נכשל בתרוקנון תיקייה", + "toast_error_create": "נכשל ביצירת תיקייה", + "toast_error_rename": "נכשל בשינוי שם תיקייה", + "toast_error_delete": "נכשל במחיקת תיקייה", + "toast_error_delete_has_children": "לתיקייה יש תת־תיקיות. הסר אותן קודם.", + "toast_error_delete_has_email": "התיקייה אינה ריקה. תרוקנה קודם.", + "delete_with_contents_title": "התיקייה אינה ריקה", + "delete_with_contents_message": "התיקייה \"{name}\" עדיין מכילה {count, plural, one {הודעה אחת} other {# הודעות}}. למחוק את התיקייה ואת כל ההודעות שבה? לא ניתן לבטל פעולה זו.", + "delete_with_contents_confirm": "מחיקת התיקייה וההודעות" + }, + "calendar_event_notifications": { + "someone": "מישהו", + "invited": "{name} הזמין/ה אותך ל\"{title}\"", + "updated": "{name} עדכן/ה את \"{title}\"", + "cancelled": "{name} ביטל/ה את \"{title}\"", + "untitled": "אירוע ללא כותרת" + }, + "share_notifications": { + "someone": "מישהו", + "shared": "{name} שיתף/ה איתך את {kind} \"{object}\"", + "changed": "{name} שינה/תה את הגישה שלך ל{kind} \"{object}\"", + "revoked": "{name} הסיר/ה את הגישה שלך ל{kind} \"{object}\"", + "object": { + "folder": "התיקייה", + "calendar": "היומן", + "address_book": "פנקס הכתובות", + "files": "תיקיית הקבצים" + } + }, + "sharing": { + "title": "שתף \"{name}\"", + "description": "הענק גישה למשתמשים או קבוצות אחרות בשרת זה. השינויים יופעלו מיד.", + "no_shares": "לא משותף עם מישהו עדיין.", + "add_person": "הוסף אדם או קבוצה", + "search_placeholder": "חפש לפי שם או דוא״ל…", + "loading_principals": "טעינת משתמשים…", + "no_principals": "לא נמצאו משתמשים או קבוצות אחרים.", + "no_match": "אין התאמות.", + "remove": "הסר גישה", + "group": "קבוצה", + "share_added": "גישה ניתנה", + "share_updated": "גישה עודכנה", + "share_removed": "גישה הוסרה", + "share_failed": "נכשל בעדכון שיתוף", + "preset": { + "freeBusy": "חופשי/תפוס בלבד", + "read": "קריאה בלבד", + "readWrite": "קרא וכתוב", + "manager": "מנהל", + "custom": "מותאם אישית" + } + }, + "unified_mailbox": { + "search_unavailable": "חיפוש אינו זמין בתצוגה המאוחדת" + }, + "quote_header": { + "reply_line": "ב־{date}, {from} כתב:", + "forwarded_separator": "---------- הודעה קדימה ----------", + "from_label": "מ", + "date_label": "תאריך", + "subject_label": "נושא" + }, + "pwa_install": { + "title": "התקן {appName}", + "description": "התקן את היישום שלנו לגישה מהירה ותמיכה בלא־אינטרנט.", + "not_now": "לא עכשיו", + "install": "התקן", + "dont_remind": "אל תזכיר לי שוב", + "dismiss_aria": "בטל הודעת התקנה" + }, + "deep_link": { + "message_not_found": "הודעה זו אינה זמינה עוד.", + "thread_not_found": "שיחה זו אינה זמינה עוד.", + "folder_not_found": "תיקייה זו אינה זמינה עוד.", + "event_not_found": "אירוע זה אינו זמין עוד.", + "file_not_found": "קובץ זה אינו זמין עוד.", + "account_unavailable": "היכנס לחשבון הזה כדי לפתוח את הקישור.", + "copy_message": "העתק קישור להודעה", + "copy_conversation": "העתק קישור לשיחה", + "copy_event": "העתק קישור לאירוע", + "copied": "הקישור הועתק", + "copy_failed": "לא ניתן להעתיק את הקישור" + }, + "plugins": { + "oauth_callback": { + "completing": "משלים אימות…", + "completing_message": "אנא המתן בזמן יצירת החיבור.", + "received_title": "האימות התקבל", + "received_message": "באפשרותך לחזור לכרטיסייה שבה התחלת את החיבור.", + "failed_title": "האימות נכשל", + "denied": "האימות נדחה.", + "failed_detail": "האימות נכשל: {error}", + "missing_params": "חסר קוד אימות או מצב.", + "delivery_failed": "לא ניתן היה למסור את תוצאת האימות לכרטיסיית האפליקציה. סגרו חלון זה ונסו שוב." + }, + "settings": { + "title": "תוספים", + "description": "תוספים שמנהל המערכת הוסיף. הפעילו או כבו אותם עבור החשבון שלכם.", + "description_uploads": "תוספים מוסיפים יכולות ל-Webmail. הפעילו או כבו אותם עבור החשבון שלכם, או התקינו תוספים משלכם.", + "description_demo": "תוספים אינם זמינים במצב הדגמה.", + "install_button": "התקנת תוסף", + "install_hint": "בחרו קובץ ZIP של תוסף.", + "install_hint_approval": "בחרו קובץ ZIP של תוסף. מנהל המערכת צריך לאשר אותו לפני שתוכלו להפעיל אותו.", + "install_uploads_off": "מנהל המערכת אינו מאפשר התקנת תוספים.", + "install_managed_id": "מנהל המערכת כבר מספק תוסף בשם הזה, ולכן אי אפשר להחליף אותו.", + "install_invalid": "הקובץ הזה אינו תוסף שניתן להתקין: {details}", + "toast_installed": "התוסף הותקן. הפעילו אותו למטה.", + "toast_installed_pending": "התוסף הותקן ונשלח לאישור מנהל המערכת.", + "toast_enabled": "{name} פועל", + "toast_disabled": "{name} כבוי", + "toast_removed": "{name} הוסר", + "toast_forced": "מנהל המערכת משאיר את {name} פועל", + "toast_approval_requested": "{name} ממתין לאישור מנהל המערכת", + "empty_title": "אין עדיין תוספים", + "empty_admin": "מנהל המערכת לא הוסיף תוספים.", + "empty_uploads": "כדי להתחיל, התקינו תוסף מקובץ ZIP.", + "empty_demo": "תוספים דורשים חשבון אמיתי, ולכן אין תוספים במצב הדגמה.", + "syncing": "בודק תוספים מול השרת…", + "status_installed": "כבוי", + "status_enabled": "בהפעלה", + "status_running": "פועל", + "status_disabled": "כבוי", + "status_error": "תקלה", + "badge_forced": "חובה", + "badge_managed": "ממנהל המערכת", + "badge_yours": "הותקן על ידיכם", + "badge_awaiting": "ממתין לאישור", + "toggle_label": "הפעלה או כיבוי של {name}", + "forced_note": "מנהל המערכת מחייב את התוסף הזה, ולכן אי אפשר לכבות אותו.", + "permissions": "מה הוא יכול לעשות", + "permissions_managed": "מה הוא יכול לעשות (באישור מנהל המערכת)", + "settings": "הגדרות", + "remove": "הסרה", + "remove_title": "להסיר את {name}?", + "remove_message": "התוסף וכל מה שהוא שמר בדפדפן הזה יימחקו. אפשר להתקין אותו שוב מאוחר יותר.", + "remove_confirm": "הסרה", + "error_user_plugins_off": "מנהל המערכת כיבה תוספים שמשתמשים התקינו, ולכן התוסף הזה לא פועל.", + "error_approval_unavailable": "התוסף הזה הותקן לפני שנדרש אישור. הסירו אותו והתקינו אותו מחדש כדי לשלוח אותו לאישור.", + "error_approval_denied": "מנהל המערכת לא אישר את התוסף הזה.", + "error_approval_pending": "ממתין לאישור התוסף הזה על ידי מנהל המערכת.", + "error_auto_disabled": "כובה כי נכשל שוב ושוב. הפעילו אותו מחדש כדי לנסות שוב.", + "accounts_heading": "חשבונות מחוברים", + "account_on": "{label} בשרת {host}", + "account_waiting": "{label} בשרת {host} (ההתחברות לא הושלמה)", + "disconnect": "ניתוק", + "disconnect_failed": "לא ניתן לנתק את {label}" + }, + "consent": { + "title": "לאפשר ל-{name} לעשות זאת?", + "intro": "התוסף מבקש את ההרשאות שלהלן. הוא יקבל אותן רק אם תאפשרו זאת.", + "high_risk_note": "פריטים מסומנים נותנים לתוסף גישה לתוכן הדואר שלכם או מאפשרים לו לשלוח נתונים למקום אחר. אפשרו אותם רק לתוספים שאתם סומכים עליהם.", + "revoke_hint": "אפשר לכבות את התוסף או להסיר אותו בהגדרות בכל עת.", + "deny": "לא לאפשר", + "allow": "לאפשר" + }, + "permissions": { + "none": "אינו צריך הרשאות מיוחדות.", + "high_risk": "דורש זהירות", + "email_read": { + "title": "קריאת הדואר שלכם", + "body": "צפייה בשולחים, בנמענים, בנושאים ובטקסט המלא של כל הודעה בחשבון שלכם, ובתיקיות שלכם." + }, + "email_write": { + "title": "שינוי וארגון הדואר שלכם", + "body": "הוספה או הסרה של תגים, סימונים ומילות מפתח אחרות בהודעות שלכם, העברתן בין התיקיות שלכם ולארכיון, ושמירת טיוטות." + }, + "email_send": { + "title": "שינוי דואר לפני שליחתו", + "body": "קריאה ושינוי של הנמענים, הנושא, הטקסט והקבצים המצורפים של הודעות שאתם כותבים, ומניעת שליחה של הודעה." + }, + "crypto_full": { + "title": "גישה מלאה לאפליקציה הזו", + "body": "פועל עם אותן הרשאות כמו ה-Webmail עצמו. הוא יכול לקרוא את כל הדואר והמפתחות השמורים שלכם ולפעול בשמכם. אפשרו זאת רק לתוספים שאתם סומכים עליהם לגמרי." + }, + "email_raw_send": { + "title": "שליחת הודעות שהוא בנה בעצמו", + "body": "שליחת הודעות שלמות, למשל חתומות או מוצפנות, מהחשבון שלכם." + }, + "email_blob_read": { + "title": "קריאת התוכן הגולמי של הודעות", + "body": "קריאת הנתונים המקוריים של ההודעות והקבצים המצורפים שלכם, ושל כל קובץ שאתם מצרפים לפני שליחתו." + }, + "email_blob_write": { + "title": "החלפת קבצים מצורפים לפני שליחה", + "body": "העלאת קבצים לשרת הדואר שלכם והחלפת קובץ שצירפתם בקובץ אחר לפני שההודעה נשלחת." + }, + "email_render_takeover": { + "title": "שינוי אופן הצגת ההודעות", + "body": "החלפת הטקסט של הודעות שאתם פותחים ושל רשימת ההודעות, למשל כדי להציג תוכן מפוענח." + }, + "calendar_read": { + "title": "קריאת היומן שלכם", + "body": "צפייה ביומנים שלכם ובאירועים שבהם." + }, + "calendar_write": { + "title": "שינוי היומן שלכם", + "body": "יצירה, שינוי ומחיקה של אירועים." + }, + "contacts_read": { + "title": "קריאת אנשי הקשר שלכם", + "body": "חיפוש וקריאה בספרי הכתובות שלכם." + }, + "contacts_write": { + "title": "שינוי אנשי הקשר שלכם", + "body": "יצירה, שינוי ומחיקה של אנשי קשר וספרי כתובות." + }, + "files_read": { + "title": "קריאת הקבצים שלכם", + "body": "עיון בקבצים השמורים בחשבון שלכם ופתיחתם." + }, + "files_write": { + "title": "שינוי הקבצים שלכם", + "body": "העלאה, שינוי שם, העברה ומחיקה של קבצים בחשבון שלכם." + }, + "identity_read": { + "title": "צפייה בכתובות השולח שלכם", + "body": "צפייה בכתובות שמהן אתם שולחים ובחתימות שלהן." + }, + "identity_write": { + "title": "שינוי כתובות השולח שלכם", + "body": "יצירה, שינוי ומחיקה של כתובות שולח וחתימות." + }, + "filters_read": { + "title": "קריאת מסנני הדואר שלכם", + "body": "צפייה בכללים שממיינים את הדואר הנכנס שלכם." + }, + "filters_write": { + "title": "שינוי מסנני הדואר שלכם", + "body": "הוספת כללים למסננים בצד השרת. כללים כאלה יכולים להעביר לתיקייה, להעביר הלאה או למחוק דואר נכנס." + }, + "tasks_read": { + "title": "קריאת המשימות שלכם", + "body": "צפייה ברשימות המשימות ובמשימות שלכם." + }, + "tasks_write": { + "title": "שינוי המשימות שלכם", + "body": "יצירה, שינוי, השלמה ומחיקה של משימות." + }, + "templates_read": { + "title": "קריאת התבניות שלכם", + "body": "צפייה בתבניות ההודעה השמורות שלכם." + }, + "templates_write": { + "title": "שינוי התבניות שלכם", + "body": "יצירה, שינוי ומחיקה של תבניות הודעה." + }, + "smime_read": { + "title": "צפייה במצב S/MIME שלכם", + "body": "בדיקה אם יש לכם מפתחות ואישורי S/MIME, ללא גישה למפתחות." + }, + "vacation_read": { + "title": "צפייה בתשובת ההיעדרות שלכם", + "body": "בדיקה אם התשובה האוטומטית שלכם פעילה ומה כתוב בה." + }, + "vacation_write": { + "title": "שינוי תשובת ההיעדרות שלכם", + "body": "הפעלה או כיבוי של התשובה האוטומטית ושינוי הטקסט והתאריכים שלה." + }, + "settings_read": { + "title": "קריאת ההגדרות שלכם", + "body": "צפייה בהעדפות ובהגדרות התגים שלכם." + }, + "settings_write": { + "title": "שינוי ההגדרות שלכם", + "body": "הוספה וסידור מחדש של תגים ושינוי ההעדפות שלכם." + }, + "security_read": { + "title": "צפייה באבטחת החשבון שלכם", + "body": "בדיקה אם אימות דו-שלבי והצפנה פעילים, ללא גישה לסודות." + }, + "auth_observe": { + "title": "לדעת מתי אתם נכנסים", + "body": "לקבל עדכון כשאתם נכנסים, מחליפים חשבון או כשההפעלה שלכם מתחדשת." + }, + "auth_emit": { + "title": "ניתוק שלכם", + "body": "ניתוק שלכם מה-Webmail." + }, + "account_read": { + "title": "צפייה בחשבונות שלכם", + "body": "צפייה בחשבונות שהוספתם, עם הכתובות והשרתים שלהם." + }, + "http_post": { + "title": "שימוש בשרת ה-Webmail בשמכם", + "body": "פנייה לנקודות הקצה האלה בשרת עם פרטי ההתחברות שלכם:" + }, + "http_fetch": { + "title": "התחברות לאתרים אחרים", + "body": "שליחת בקשות לאתרים האלה. כל מה שהתוסף יכול לקרוא עלול להישלח אליהם:" + }, + "admin_config": { + "title": "קריאת ההגדרות שלו ממנהל המערכת", + "body": "קריאת ההגדרות שמנהל המערכת שמר עבור התוסף הזה." + }, + "ui_observe": { + "title": "מעקב אחר המיקום שלכם באפליקציה", + "body": "לדעת איזה עמוד פתוח ולהגיב לקישורים שאתם לוחצים עליהם." + }, + "ui_toolbar": { + "title": "הוספת כפתורים לסרגלי כלים", + "body": "הצגת כפתורים משלו בסרגל הכלים של ההודעות, על הודעה פתוחה ובסרגל העליון." + }, + "ui_app_top_banner": { + "title": "הצגת באנר בחלק העליון", + "body": "הצגת הודעה לרוחב החלק העליון של האפליקציה." + }, + "ui_email_banner": { + "title": "הצגת הודעות על גבי הודעות דואר", + "body": "הצגת הודעות וסיכומים משלו מעל החלקים של הודעה או שיחה פתוחה, בתוכם וביניהם." + }, + "ui_email_footer": { + "title": "הוספת תוכן מתחת להודעות", + "body": "הצגת תוכן משלו מתחת להודעה פתוחה." + }, + "ui_contact_cryptokeys": { + "title": "הצגת מפתחות הצפנה של אנשי קשר", + "body": "הצגה וניהול של מפתחות ההצפנה של איש קשר." + }, + "ui_email_details": { + "title": "הוספת חלוניות לפרטי ההודעה", + "body": "הצגת חלונית משלו לצד הודעה פתוחה או מתחתיה." + }, + "ui_download_file": { + "title": "שמירת קבצים במכשיר שלכם", + "body": "הצעת קבצים שהוא יצר להורדה." + }, + "ui_message_list_tabs": { + "title": "הוספת לשוניות לדואר הנכנס", + "body": "מיון הדואר הנכנס שלכם ללשוניות כמו עדכונים או קידומי מכירות." + }, + "ui_composer_toolbar": { + "title": "הוספה לעורך", + "body": "הצגת כפתורים, פריטי תפריט ומקטעים משלו בזמן שאתם כותבים הודעה." + }, + "ui_composer_sidebar": { + "title": "הוספת חלונית לעורך", + "body": "הצגת חלונית משלו לצד ההודעה שאתם כותבים." + }, + "ui_sidebar_widget": { + "title": "הוספה לסרגל הצד", + "body": "הצגת תיבות ומקטעים משלו בסרגל הצד של הדואר." + }, + "ui_settings_section": { + "title": "הוספת עמוד הגדרות", + "body": "הצגת עמוד משלו בהגדרות." + }, + "ui_context_menu": { + "title": "הוספה לתפריט ההודעה", + "body": "הוספת פריטים לתפריט שנפתח בלחיצה ימנית על הודעה." + }, + "ui_navigation_rail": { + "title": "הוספה לסרגל הניווט", + "body": "הצגת כפתור משלו בסרגל הניווט." + }, + "ui_keyboard": { + "title": "הוספת קיצורי מקשים", + "body": "תגובה לקיצורי מקשים שהוא מגדיר." + }, + "ui_calendar_action": { + "title": "הוספה לתצוגת לוח השנה", + "body": "הצגת כפתורים ומקטעים משלו על אירועים, ותוויות קצרות ונקודות על ימים בלוח השנה." + }, + "ui_admin_page": { + "title": "הוספת עמוד ניהול", + "body": "הצגת עמוד משלו בהגדרות הניהול." + }, + "app_lifecycle": { + "title": "לדעת מתי האפליקציה מופעלת ונעצרת", + "body": "לקבל עדכון כשהאפליקציה מופעלת, עוברת למצב לא מקוון או כשאתם מתנתקים." + }, + "compose_open": { + "title": "פתיחת העורך עבורכם", + "body": "פתיחת הודעה חדשה עם נמענים, טקסט או קבצים מצורפים שכבר מולאו. ההחלטה אם לשלוח אותה נשארת שלכם." + }, + "compose_read": { + "title": "קריאת ההודעה שאתם כותבים", + "body": "קריאת הטקסט של הודעה בזמן הכתיבה, כשמשתמשים בתוסף בעורך. לא החתימה ולא ההודעה המצוטטת." + }, + "compose_write": { + "title": "שינוי ההודעה שאתם כותבים", + "body": "החלפה או הוספה של טקסט בהודעה בזמן הכתיבה, כשמשתמשים בתוסף בעורך. אפשר לבטל כל שינוי, וההחלטה אם לשלוח נשארת שלכם." + }, + "ui_notifications": { + "title": "הצגת התראות", + "body": "הצגת התראות במכשיר שלכם, או בתוך האפליקציה אם לא אישרתם התראות. כל התראה מציינת את שם התוסף." + }, + "ui_message_list": { + "title": "הוספה לרשימת ההודעות", + "body": "הצגת תוכן משלו מעל רשימת ההודעות, וכפתורים על הודעות ובסרגל הכלים של ההודעות שנבחרו." + }, + "ui_contacts": { + "title": "הוספה לתצוגת אנשי הקשר", + "body": "הצגת מקטעים משלו בפרטים של איש קשר וכפתורים ברשימת אנשי הקשר." + }, + "ui_tasks": { + "title": "הוספה לתצוגת המשימות", + "body": "הצגת מקטעים משלו בפרטים של משימה וכפתורים ברשימת המשימות." + }, + "ui_files": { + "title": "הוספה לתצוגת הקבצים", + "body": "הצגת מקטעים משלו בתצוגה המקדימה של קבצים ופריטים בתפריט של קובץ." + }, + "ui_commands": { + "title": "הוספת פקודות", + "body": "הוספת פריטים משלו ללוח הפקודות (Ctrl+K)." + }, + "ui_app_page": { + "title": "הוספת עמודים", + "body": "הוספת עמודים משלו לאפליקציה, כל אחד עם כפתור בסרגל הניווט." + }, + "http_proxy": { + "title": "שימוש בחשבונות שאתם מחברים", + "body": "שליחת בקשות דרך השרת של Bulwark עם חשבון או מפתח שהוגדרו על ידיכם או על ידי מנהל המערכת. התוסף אף פעם לא רואה את הסיסמה, אבל הוא יכול לעשות כל מה שהחשבון הזה יכול לעשות בשרת שלו." + } + }, + "dialog": { + "from_plugin": "מהתוסף {name}", + "ok": "אישור", + "submit": "שליחה", + "confirm": "אישור", + "cancel": "ביטול", + "close": "סגירה", + "turn_off": "כיבוי {name}", + "plugin_menu": "אפשרויות עבור {name}", + "password_note": "הטופס הזה מגיע מהתוסף {name}, לא מהדואר האינטרנטי. הזינו סיסמה רק אם אתם סומכים על התוסף הזה.", + "toast_auto_disabled": "{name} כובה כי נכשל שוב ושוב." + }, + "connect": { + "title": "חיבור {label}", + "intro_password": "{name} רוצה להשתמש בחשבון {label} שלכם. Bulwark שומר את הסיסמה בשרת שלו ושולח אותה רק אל {host}. התוסף אף פעם לא רואה אותה.", + "intro_password_any": "{name} רוצה להשתמש בחשבון {label} שלכם. Bulwark שומר את הסיסמה בשרת שלו ושולח אותה רק לשרת שתזינו כאן. התוסף אף פעם לא רואה אותה.", + "intro_signin": "{name} רוצה להתחבר לחשבון {label} שלכם. הזינו כאן את השרת, ואז התחברו בדף של השרת עצמו. Bulwark שומר בשרת שלו את הגישה שהוא מקבל; התוסף אף פעם לא רואה אותה.", + "server": "כתובת השרת", + "server_hint": "cloud.example.com", + "username": "שם משתמש", + "secret": "סיסמה או מפתח", + "connect": "חיבור", + "continue": "המשך", + "cancel": "ביטול", + "saving": "שומר…", + "error_server": "הזינו את כתובת השרת, למשל https://cloud.example.com", + "error_save": "השמירה נכשלה: {error}" + } + }, + "global_search": { + "title": "חיפוש בכל מקום", + "placeholder": "חיפוש בדואר, אנשי קשר, יומן, קבצים...", + "scope_label": "חיפוש ב", + "account_label": "חשבון", + "scope_all": "הכל", + "scope_mail": "דוֹאַר", + "scope_contacts": "אנשי קשר", + "scope_calendar": "לוּחַ שָׁנָה", + "scope_files": "קבצים", + "all_accounts": "כל החשבונות", + "show_all": "הצגת כל התוצאות", + "no_results": "אין תוצאות עבור “{query}”", + "server_hint": "מילים שלמות מחפשות בשרת; חלקי מילים תואמים פריטים שכבר נטענו", + "searching": "מחפש...", + "account_error": "לא ניתן היה לחפש ב-{account}", + "recent_searches": "חיפושים אחרונים", + "recurring": "חוזר", + "no_subject": "(no subject)", + "empty_hint": "חפשו דואר, אנשי קשר, אירועים וקבצים בכל החשבונות. נסו: from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "שטוח לפי תאריך", + "grouped": "מקובץ", + "load_more": "טען עוד", + "preview_empty": "בחרו תוצאה לתצוגה מקדימה", + "open_full": "פתיחה", + "view_label": "תצוגה" + }, + "top_bar": { + "more": "אפשרויות נוספות" + }, + "plugin_pages": { + "loading": "טוען את הדף", + "not_available_title": "הדף הזה אינו זמין", + "not_available_description": "התוסף שהוסיף אותו כבוי או שאינו מותקן עוד.", + "back_to_mail": "חזרה לדואר", + "settings_group": "תוספים" + }, + "command_palette": { + "title": "לוח פקודות", + "open": "פתיחת לוח הפקודות", + "placeholder": "הקלידו פקודה", + "no_results": "אין פקודות תואמות", + "go_to": "מעבר אל {name}", + "compose": "הודעה חדשה", + "search": "חיפוש בדואר", + "theme_dark": "מעבר לערכת נושא כהה", + "theme_light": "מעבר לערכת נושא בהירה", + "shortcuts": "הצגת קיצורי מקשים", + "group_navigation": "מעבר אל", + "group_actions": "פעולות", + "group_plugins": "תוספים" + }, + "notification_center": { + "title": "התראות", + "mark_all_read": "סימון הכול כנקרא", + "clear": "ניקוי", + "empty": "אין התראות", + "close": "סגירה", + "unread": "לא נקרא", + "unread_count": "לא נקראו: {count}", + "level_error": "שגיאה", + "level_warning": "אזהרה" + } +} diff --git a/locales/hu/common.json b/locales/hu/common.json index 1bce930a..29fd5076 100644 --- a/locales/hu/common.json +++ b/locales/hu/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimalista webmail kliens JMAP protokollal", - "login": { - "title": "Webmail", - "username_label": "E-mail", - "username_placeholder": "user@example.com", - "password_label": "Jelszó", - "password_placeholder": "Add meg a jelszavad", - "jmap_endpoint_label": "JMAP Szerver", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "A szervernek engedélyeznie kell a CORS kéréseket erről a tartományról.", - "jmap_server_label": "Szerver", - "jmap_server_auto_picked": "Szerver kiválasztva az e-mail tartományodból.", - "sign_in": "Bejelentkezés", - "signing_in": "Bejelentkezés...", - "loading": "Betöltés...", - "reconnecting": "Kapcsolat megszakadt. Újracsatlakozás...", - "error": { - "invalid_credentials": "Helytelen e-mail vagy jelszó. Ellenőrizd a megadott adatokat és próbáld újra.", - "connection_failed": "Nem sikerült elérni a szervert. Ellenőrizd az internetkapcsolatod és próbáld újra.", - "cors_blocked": "A szerver elérhető, de blokkolja a több tartományból érkező kéréseket. Ellenőrizd a JMAP szerver CORS beállításait és engedélyezd ezt a tartományt.", - "server_error": "A szerver átmenetileg nem elérhető. Kérjük, próbáld újra később.", - "generic": "Váratlan hiba történt. Ha a probléma továbbra is fennáll, vedd fel a kapcsolatot a rendszergazdával.", - "totp_required": "Kétfaktoros hitelesítési kód szükséges. Kérjük, add meg a kódot lent.", - "totp_invalid": "Érvénytelen hitelesítési kód. Ellenőrizd a hitelesítő alkalmazásod és próbáld újra.", - "oauth_discovery_failed": "SSO engedélyezve van, de az identitásszolgáltató nem érhető el. Ellenőrizd az OAuth konfigurációt." - }, - "show_password": "Jelszó megjelenítése", - "hide_password": "Jelszó elrejtése", - "totp_toggle": "Van egy 2FA kódom", - "remember_me": "Emlékezz rám", - "config_error": { - "title": "Konfigurációs hiba", - "fetch_failed": "Nem sikerült betölteni az alkalmazás konfigurációját. Kérjük, próbáld újra később.", - "server_not_configured": "A levelezőszerver nincs beállítva. Kérjük, vedd fel a kapcsolatot a rendszergazdával." - }, - "remove_from_history": "Eltávolítás az előzményekből", - "totp_label": "Hitelesítési kód", - "totp_placeholder": "000000", - "session_expired": "A munkamenet lejárt. Kérjük, jelentkezz be újra.", - "dismiss": "Elutasítás", - "or": "vagy", - "sign_in_sso": "Bejelentkezés SSO-val", - "add_account_title": "Fiók hozzáadása", - "add_account_subtitle": "Bejelentkezés másik fiókkal", - "cancel": "Mégse", - "website": "Weboldal", - "imprint": "Impresszum", - "privacy_policy": "Adatvédelmi irányelvek", - "try_demo": "Demó kipróbálása", - "demo_description": "Fedezd fel mintaadatokkal - nincs szükség fiókra", - "demo_launching": "Demó indítása...", - "demo_login_button": "Demó indítása", - "demo_tagline": "Tapasztalj meg egy teljes értékű e-mail klienst. Nincs szükség fiókra.", - "demo_no_signup": "Nincs szükség regisztrációra - fedezd fel szabadon mintaadatokkal", - "oauth_completing": "Bejelentkezés befejezése...", - "oauth_error": { - "title": "Hitelesítés sikertelen", - "invalid_state": "Biztonsági ellenőrzés sikertelen. Kérjük, próbáld meg újra a bejelentkezést.", - "missing_params": "Hiányzó engedélyezési adatok. Kérjük, próbáld meg újra a bejelentkezést.", - "token_exchange_failed": "Nem sikerült befejezni a hitelesítést. Kérjük, próbáld újra.", - "access_denied": "A hozzáférés megtagadva. Kérjük, vedd fel a kapcsolatot a rendszergazdával.", - "back_to_login": "Vissza a bejelentkezéshez" - } - }, - "sidebar": { - "close": "Bezárás", - "compose": "Új levél", - "compose_hint": "Új levél (c)", - "search_placeholder": "Keresés a levelekben...", - "search_placeholder_hint": "Keresés a levelekben... (nyomd meg a / billentyűt)", - "storage": "Tárhely", - "storage_used": "Használt", - "storage_free": "Szabad", - "storage_total": "Összesen", - "sign_out": "Kijelentkezés", - "sign_out_of": "Kijelentkezés erről: {account}", - "sign_out_all": "Kijelentkezés minden fiókból", - "add_account": "Fiók hozzáadása", - "set_as_default": "Beállítás alapértelmezettként", - "switch_account": "Fiók váltása", - "contacts": "Névjegyek", - "calendar": "Naptár", - "settings": "Beállítások", - "admin": "Adminisztráció", - "files": "Fájlok", - "loading_mailboxes": "Postafiókok betöltése...", - "push_connected": "Valós idejű frissítések aktívak", - "push_disconnected": "Valós idejű frissítések inaktívak", - "keyboard_shortcuts": "Billentyűparancsok", - "theme": { - "light": "Világos mód", - "dark": "Sötét mód", - "system": "Rendszer téma" - }, - "language": { - "title": "Nyelv" - }, - "mailboxes": { - "inbox": "Beérkező levelek", - "sent": "Elküldött", - "drafts": "Piszkozatok", - "trash": "Kuka", - "archive": "Archívum", - "starred": "Csillagozott", - "all_mail": "Minden levél", - "spam": "Spam", - "important": "Fontos" - }, - "unified_inbox": "Egységes beérkező", - "unified_sent": "Összes elküldött", - "unified_drafts": "Összes piszkozat", - "unified_trash": "Összes kuka", - "unified_archive": "Összes archívum", - "unified_junk": "Összes spam", - "all_accounts": "Minden fiók", - "unified_mailbox": "Egységes postafiók", - "expand": "Kibontás", - "collapse": "Összecsukás", - "expand_tooltip": "Kibontás", - "collapse_tooltip": "Összecsukás", - "mobile": { - "search": "Keresés", - "compose": "Új levél", - "go_back": "Vissza", - "toggle_menu": "Menü be/ki" - }, - "clear_search": "Keresés törlése", - "vacation_active": "Automatikus válasz aktív", - "demo_banner": "Demó mód", - "demo_reset": "Visszaállítás", - "demo_tour": "Bemutató", - "tags": "Címkék", - "show_all_tags": "Összes megjelenítése ({count})", - "show_fewer_tags": "Kevesebb megjelenítése", - "folders": "Mappák", - "shared": "Megosztott", - "mail": "Levelek", - "nav_label": "Navigáció", - "add_app": "Alkalmazások", - "scheduled": "Ütemezett", - "unified_all_unread": "Összes olvasatlan", - "unified_all_starred": "Összes csillagozott", - "unified_all_mail": "Összes levél", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Alapértelmezett alkalmazások", - "description": "Válaszd ki, hogy az e-mail és naptár hivatkozások a Bulwarkban nyíljanak meg. Technikailag a Bulwark protokollkezelőként regisztrálja magát a mailto: és webcal: hivatkozásokhoz.", - "unsupported": "Ez a böngésző vagy kapcsolat nem támogatja a kézi protokollkezelő regisztrációt. Lehetőség lehet a telepített PWA használatára a böngésző vagy operációs rendszer beállításain keresztül.", - "mailto_label": "E-mail hivatkozások", - "mailto_description": "Mailto: hivatkozások megnyitása a Bulwarkban egy előre kitöltött szerkesztővel.", - "protocol_open_mode_label": "Protokoll hivatkozások megnyitásakor", - "protocol_open_mode_description": "Válaszd ki, hogy a Bulwark a mailto: és webcal: hivatkozásokat új lapon vagy a meglévő munkamenetben nyissa meg. Az aktív munkamenet opcióhoz értesítési engedély szükséges, hogy egy értesítésre kattintva előtérbe hozd a Bulwarkot, ha a böngésző blokkolja a fókuszt.", - "protocol_open_mode_active_session": "Megnyitás aktív munkamenetben, ha lehetséges", - "protocol_open_mode_new_tab": "Mindig új lapon nyisson", - "focus_notification_title": "Bulwark megnyitása", - "focus_notification_body": "A hivatkozás megnyílt a Bulwarkban. Kattints az ablak előtérbe hozásához.", - "webcal_label": "Naptár hivatkozások", - "webcal_description": "Webcal: hivatkozások megnyitása a Bulwarkban egy előre kitöltött naptárfeliratkozási párbeszédpanellel.", - "register_mailto": "E-mail alkalmazás regisztrálása", - "register_webcal": "Naptár alkalmazás regisztrálása", - "mailto_registered": "E-mail kezelő regisztráció kérve", - "webcal_registered": "Naptár kezelő regisztráció kérve", - "registration_failed": "Protokollkezelő regisztráció sikertelen", - "opening_mailto": "Szerkesztő megnyitása...", - "opening_webcal": "Naptár megnyitása...", - "browser_note": "A böngésző vagy operációs rendszered megerősítést kérhet, és előfordulhat, hogy a Bulwarkot telepíteni kell, mielőtt alapértelmezett alkalmazásként kiválasztható.", - "select_account_title": "Fiók kiválasztása", - "select_mailto_account": "Válaszd ki, melyik fiók nyissa meg ezt az e-mail hivatkozást.", - "select_webcal_account": "Válaszd ki, melyik fiók nyissa meg ezt a naptár hivatkozást.", - "select_account_note": "Ez csak erre a protokoll hivatkozásra vonatkozik.", - "detail_to": "Címzett", - "detail_subject": "Tárgy", - "detail_no_subject": "Nincs tárgy", - "detail_calendar": "Naptár", - "detail_source": "Forrás", - "active_account": "Aktív", - "switching_account": "Fiók váltása..." - }, - "sidebar_apps": { - "modal_title": "Oldalsáv alkalmazások", - "add_new": "Alkalmazás hozzáadása", - "edit_app": "Alkalmazás szerkesztése", - "name_label": "Név", - "name_placeholder": "Saját alkalmazás", - "name_required": "Név megadása kötelező", - "url_label": "URL", - "url_required": "URL megadása kötelező", - "url_invalid": "Érvényes http vagy https URL-t adj meg", - "icon_label": "Ikon", - "icon_required": "Ikon megadása kötelező", - "open_mode_label": "Megnyitás módja", - "open_new_tab": "Új lap", - "open_inline": "Beágyazva", - "cancel": "Mégse", - "add": "Hozzáadás", - "update": "Frissítés", - "delete": "Törlés", - "delete_confirm_title": "Alkalmazás törlése", - "delete_confirm": "Biztosan törölni szeretnéd a \"{name}\" alkalmazást?", - "no_apps": "Még nincsenek alkalmazások", - "no_apps_hint": "Egyedi alkalmazások és hivatkozások hozzáadása az oldalsávhoz", - "managed_title": "A rendszergazda biztosítja", - "managed_description": "Ezeket az alkalmazásokat mindenki számára beállították, és nem módosíthatók.", - "managed_badge": "Felügyelt", - "search_icons": "Ikonok keresése...", - "show_popular": "Népszerű", - "show_all": "Összes", - "no_icons_found": "Nem található ikon", - "inline_badge": "Beágyazott", - "tab_badge": "Lap", - "show_on_mobile": "Megjelenítés mobilon" - }, - "email_list": { - "no_emails": "Nincs üzenet", - "no_emails_description": "Ez a postafiók üres", - "no_search_results": "Nincs találat", - "no_search_results_description": "Próbáld módosítani a keresést vagy a szűrőket", - "loading": "E-mailek betöltése...", - "unread": "olvasatlan", - "to_me": "Nekem", - "to_recipients": "{count} címzettnek", - "and_others": "és {count} további", - "draft": "Piszkozat", - "starred": "Csillagozott", - "pinned": "Kitűzve", - "replied": "Megválaszolva", - "forwarded": "Továbbítva", - "has_attachment": "Melléklettel", - "conversations_count": "{count} / {total} beszélgetés", - "conversations_count_plus": "{count}+ beszélgetés", - "conversations_count_simple": "{count} beszélgetés", - "no_conversations": "Nincs beszélgetés", - "loading_more": "További e-mailek betöltése...", - "no_more_emails": "Nincs több betölthető e-mail", - "batch_actions": { - "select": "E-mailek kijelölése", - "select_all": "Összes kijelölése", - "selected_messages": "{count, plural, one {1 e-mail} other {# e-mail}} kijelölve", - "mark_read": "Megjelölés olvasottként", - "mark_unread": "Megjelölés olvasatlanként", - "delete": "Törlés", - "delete_confirm_title": "E-mailek törlése", - "delete_confirm_message": "Biztosan törölni szeretnéd {count, plural, one {az 1 e-mailt} other {a # e-mailt}}?", - "clear_selection": "Kijelölés megszüntetése" - }, - "permanent_delete": "Végleges törlés", - "permanent_delete_confirm_title": "Végleges törlés", - "permanent_delete_confirm_message": "Ez az e-mail véglegesen törlődik. Ez a művelet nem vonható vissza.", - "permanent_delete_confirm_batch_message": "Ez a(z) {count, plural, one {1 e-mail} other {# e-mail}} véglegesen törlődik. Ez a művelet nem vonható vissza.", - "empty_folder": { - "button": "Mappa ürítése", - "confirm_title": "Mappa ürítése", - "confirm_message": "A mappa összes e-mailje véglegesen törlődik. Ez a művelet nem vonható vissza.", - "confirm_button": "Mappa ürítése", - "junk_hint": "A Spam mappa ürítésével véglegesen eltávolíthatod az összes üzenetet.", - "trash_hint": "A Kuka mappa ürítésével véglegesen eltávolíthatod az összes üzenetet." - }, - "no_scheduled_emails": "Nincsenek ütemezett e-mailek", - "no_scheduled_emails_description": "A későbbre ütemezett üzenetek itt jelennek meg.", - "scheduled_count": "{count, plural, one {1 ütemezett e-mail} other {# ütemezett e-mail}}", - "scheduled_actions_hint": "Az ütemezett üzenetek megszakíthatók, átidőzíthetők vagy szerkeszthetők az ütemezett műveleteknél.", - "cancel_scheduled_send": "Küldés megszakítása", - "reschedule_send": "Átütemezés", - "cancel_and_edit": "Mégse és szerkesztés", - "cancel_and_compose_again": "Mégse és újraírás", - "reschedule_prompt": "Adj meg egy új dátumot/időpontot, pl. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "A feladó olvasási visszaigazolást kért:", - "send": "Visszaigazolás küldése", - "ignore": "Figyelmen kívül hagyás", - "sent": "Olvasási visszaigazolás elküldve.", - "send_failed": "Az olvasási visszaigazolás nem volt elküldhető", - "mdn_subject": "Olvasva: {subject}", - "mdn_body": "Ez egy visszaigazolás a(z) {recipient} címzettnek küldött üzenetről.\n\nMegjegyzés: Ez a visszaigazolás csak azt rögzíti, hogy az üzenet megjelent a címzett számítógépén. Nem garantálja, hogy a címzett elolvasta vagy megértette az üzenet tartalmát." - }, - "no_email_selected": "Nincs e-mail kijelölve", - "no_email_description": "Válassz ki egy e-mailt a listából a megtekintéshez", - "no_conversation_selected": "Nincs beszélgetés kijelölve", - "no_conversation_description": "Válassz egy beszélgetést a listából az olvasáshoz", - "compose": "Új levél", - "compose_hint": "Új üzenet írása", - "no_subject": "(Nincs tárgy)", - "no_body_content": "(Nincs elérhető tartalom)", - "no_preview_available": "Nincs előnézet", - "show_quoted_text": "Idézett szöveg megjelenítése", - "hide_quoted_text": "Idézett szöveg elrejtése", - "loading_email": "E-mail betöltése...", - "loading": "Betöltés...", - "reply": "Válasz", - "reply_all": "Válasz mindenkinek", - "forward": "Továbbítás", - "delete": "Törlés", - "archive": "Archiválás", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Csillagozás", - "unstar": "Csillagozás megszüntetése", - "mark_unread": "Megjelölés olvasatlanként", - "mark_read": "Megjelölés olvasottként", - "unread": "Olvasatlan", - "read": "Olvasott", - "spam_short": "Spam", - "not_spam_short": "Nem spam", - "move": "Áthelyezés", - "print": "Nyomtatás", - "view_source": "Forrás megtekintése", - "fullscreen": "Teljes képernyő", - "exit_fullscreen": "Kilépés a teljes képernyőből", - "export_email": "Exportálás .eml-ként", - "forward_as_attachment": "Továbbítás mellékletként", - "import_email": "Importálás .eml vagy .zip fájlból", - "keyboard_shortcuts": "Billentyűparancsok (?)", - "email_source": "E-mail forrás", - "draft_banner": "Ez az üzenet egy piszkozat", - "edit_draft": "Szerkesztés", - "copy_source": "Másolás a vágólapra", - "source_copied": "Forrás a vágólapra másolva", - "attachments": "Csatolmányok", - "important": "Fontos", - "download": "Letöltés", - "download_all": "Összes letöltése", - "from": "Feladó", - "to": "Címzett", - "cc": "Másolat", - "bcc": "Titkos másolat", - "date": "Dátum", - "subject": "Tárgy", - "show_details": "Részletek megjelenítése", - "hide_details": "Részletek elrejtése", - "external_content_warning": "Képek és külső tartalmak blokkolva", - "load_external_content": "Képek betöltése", - "trust_sender": "Feladó mindig megbízható", - "back_to_list": "Vissza a listához", - "view_contact": "Névjegy megtekintése", - "message_details": "Üzenet részletei", - "more_reply_options": "További válasz opciók", - "set_tag": "Címke beállítása", - "tag": "Címke", - "more_actions": "További műveletek", - "previous": "Előző", - "next": "Következő", - "move_to": "Áthelyezés ide...", - "remove_tag": "Címke eltávolítása", - "tag_filter_placeholder": "Címkék szűrése", - "tag_no_matches": "Nincs találat a címkék közt", - "more_count": "+{count} további", - "characters_count": "{count} karakter", - "quick_reply_placeholder": "Gyors válasz írása...", - "more_options": "További opciók", - "sending": "Küldés...", - "security_authentication": "Biztonság és hitelesítés", - "technical_details": "Technikai részletek", - "message_id_label": "Üzenetazonosító:", - "reply_to_label": "Válaszcím:", - "delivery_time_label": "Kézbesítési idő:", - "conversation_part_label": "Beszélgetés része:", - "previous_messages": "{count} előző üzenet", - "previous_messages_plural": "{count} előző üzenet", - "time": { - "day": "nap", - "days": "nap", - "hour": "óra", - "hours": "óra", - "minute": "perc", - "minutes": "perc" - }, - "unknown_sender": "Ismeretlen", - "recipient_me": "én", - "recipient_and_others": "{name} és {count} további", - "recipient_to_prefix": "Címzett:", - "authentication": { - "title": "Hitelesítés", - "status": { - "verified": "Hitelesítve", - "warning": "Figyelmeztetés", - "none": "Nincs hitelesítve" - }, - "spf": { - "pass": "SPF sikeres", - "fail": "SPF sikertelen", - "none": "Nincs SPF" - }, - "dkim": { - "pass": "DKIM érvényes", - "fail": "DKIM érvénytelen", - "none": "Nincs DKIM" - }, - "dmarc": { - "pass": "DMARC sikeres", - "fail": "DMARC sikertelen", - "none": "Nincs DMARC" - }, - "spam_score": "Spam pontszám", - "tooltip_spf": "Sender Policy Framework: Ellenőrzi, hogy a küldő szerver jogosult-e e-mailek küldésére a tartomány nevében", - "tooltip_dkim": "DomainKeys Identified Mail: Kriptográfiai aláírással erősíti meg, hogy az e-mailt nem változtatták meg a továbbítás során", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: Biztosítja, hogy az SPF és DKIM összhangban van a feladó tartományával, és szabályzatot állít be a hibák esetére", - "policy": "Szabályzat", - "result": { - "pass": "Sikeres", - "fail": "Sikertelen", - "softfail": "Gyenge hiba", - "neutral": "Semleges", - "permerror": "Állandó hiba", - "temperror": "Ideiglenes hiba", - "none": "Nincs" - } - }, - "details": { - "recipients_routing": "Címzettek és útvonal", - "authentication_security": "Hitelesítés és biztonság", - "identifiers_threading": "Azonosítók és fűzések", - "mailing_list": "Levelezőlista", - "message_properties": "Üzenet tulajdonságai", - "sent": "Küldve", - "received": "Fogadva", - "delivery_time": "Kézbesítési idő", - "in_reply_to": "Válasz erre", - "references": "Hivatkozások", - "thread_id": "Beszélgetés azonosító", - "size": "Méret", - "mime_type": "MIME típus", - "attachments_summary": "{count} fájl · {size}", - "list_id": "Lista azonosító", - "list_help": "Lista súgó", - "list_post": "Lista küldés", - "list_unsubscribe": "Leiratkozás", - "iprev": "Fordított DNS", - "spam_status": "Spam státusz", - "ai_verdict": "AI döntés", - "account": "Fiók", - "no_subject": "(nincs tárgy)" - }, - "headers": { - "routing": "Útvonal", - "received": "Fogadva", - "message_id": "Üzenet azonosító", - "list_info": "Lista információk" - }, - "tooltips": { - "reply": "Válasz (r)", - "reply_all": "Válasz mindenkinek (a)", - "forward": "Továbbítás (f)", - "archive": "Archiválás (e)", - "delete": "Törlés (# vagy Del)", - "star": "Csillagozás (s)", - "unstar": "Csillagozás megszüntetése (s)", - "compose": "Új levél (c)", - "previous": "Előző e-mail", - "next": "Következő e-mail", - "edit_draft": "Piszkozat szerkesztése" - }, - "spam": { - "button_title": "Spam jelentése", - "not_spam_title": "Megjelölés jogosként", - "toast_success": "Áthelyezve a Spam mappába", - "toast_batch": "{count} e-mail áthelyezve a Spam mappába", - "toast_undo": "Visszavonás", - "toast_not_spam_success": "Áthelyezve a Beérkező levelek közé", - "toast_not_spam_batch": "{count} e-mail áthelyezve a Beérkező levelek közé", - "error": "Nem sikerült spamként jelenteni", - "error_not_spam": "Nem sikerült visszaállítani az e-mailt" - }, - "unsubscribe_banner": { - "label": "Hírlevél", - "button": "Leiratkozás", - "confirm_title": "Leiratkozás ettől a feladótól?", - "confirm_button": "Megerősítés", - "cancel": "Mégse", - "success_http": "Leiratkozási oldal megnyílt egy új lapon", - "success_mailto": "Leiratkozó e-mail elküldve", - "confirm_message_http": "A leiratkozási oldal új lapon nyílik meg.", - "confirm_message_mailto": "Leiratkozó e-mailt küldünk a feladónak.", - "error": "Nem sikerült leiratkozni", - "dismiss": "Elutasítás" - }, - "calendar_invitation": { - "loading": "Esemény részleteinek betöltése...", - "title": "Naptár meghívó", - "published_title": "Közzétett esemény", - "response_title": "Esemény válasz", - "update_title": "Esemény frissítés", - "counter_title": "Ellenjavaslat", - "refresh_title": "Frissítési kérelem", - "declined_counter_title": "Ellenjavaslat elutasítva", - "cancelled_title": "Esemény lemondva", - "organizer": "Szervező: {name}", - "organizer_label": "Szervező", - "attendees": "{count, plural, one {# résztvevő} other {# résztvevő}}", - "accept": "Elfogadás", - "maybe": "Talán", - "decline": "Elutasítás", - "add_to_calendar": "Hozzáadás a naptárhoz", - "added": "Hozzáadva a naptárhoz", - "rsvp_sent": "Válasz elküldve", - "parse_error": "Nem sikerült értelmezni a meghívót", - "action_failed": "Nem sikerült végrehajtani a naptár műveletet.", - "no_calendar": "Naptár nem elérhető", - "published_info": "Ez az esemény referencia célból lett megosztva.", - "response_info": "Ez az üzenet egy résztvevői választ tartalmaz.", - "response_info_organizer": "Ez a résztvevői válasz frissíti az eseményed.", - "update_info": "Ez az üzenet egy meglévő eseményt frissít.", - "counter_info": "Ez az üzenet módosításokat javasol egy eseményhez.", - "counter_info_organizer": "Ez a résztvevő módosításokat javasolt az eseményedhez.", - "refresh_info": "Ez az üzenet a legújabb esemény adatait kéri.", - "refresh_info_organizer": "Egy résztvevő a legújabb esemény adatokat kérte.", - "declined_counter_info": "A szervező elutasította az ellenjavaslatot.", - "authentication_failed_info": "Az e-mail hitelesítési ellenőrzések ehhez a meghívóhoz sikertelenek voltak. Óvatosan kezeld a naptár műveleteket.", - "authentication_missing_info": "Ez a meghívó nem tartalmaz ellenőrzött e-mail hitelesítést. Ha bármi szokatlannak tűnik, erősítsd meg a részleteket a szervezővel.", - "sender_mismatch_info": "Ezt a meghívót {sender} küldte, míg a naptár adatokban szereplő szervező {organizer}.", - "sender_mismatch_unverified_info": "Ezt a meghívót {sender} küldte, míg a naptár adatokban szereplő szervező {organizer}, és az üzenet nem volt hitelesíthető.", - "organizer_role": "Te szervezed ezt az eseményt", - "your_response": "Válaszod: {status}", - "response_needed": "Válasz szükséges", - "response_accepted": "Elfogadva", - "response_tentative": "Előzetes", - "response_declined": "Elutasítva", - "response_delegated": "Delegálva", - "actor_sent_info": "Küldte: {name}.", - "actor_response_info": "{name} válasza: {status}.", - "actor_counter_info": "{name} módosításokat javasolt ehhez az eseményhez.", - "actor_refresh_info": "{name} a legújabb esemény adatokat kérte.", - "actor_declined_counter_info": "{name} elutasította az ellenjavaslatot.", - "actor_note": "Megjegyzés: {comment}", - "actor_unknown": "Valaki", - "proposed_changes": "Javasolt módosítások", - "change_title": "Cím", - "change_time": "Idő", - "change_location": "Helyszín", - "change_description": "Leírás", - "change_empty": "Nincs", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Javasolt módosítások alkalmazása", - "proposal_applied": "Javasolt módosítások alkalmazva.", - "review_proposal": "Javaslat áttekintése", - "review_request": "Kérelem áttekintése", - "view_in_calendar": "Megtekintés a naptárban", - "select_calendar": "Naptár kiválasztása", - "already_in_calendar": "Már a naptáradban van", - "request_info": "Meghívtak erre az eseményre. Válaszolj, hogy a szervező tudja a rendelkezésre állásod.", - "cancel_info": "A szervező lemondta ezt az eseményt.", - "event_updated": "Frissítés #{sequence}", - "event_status_tentative": "Előzetes", - "event_status_cancelled": "Lemondva", - "expand": "Részletek mutatása", - "collapse": "Részletek elrejtése" - }, - "send": "Küldés", - "more": "további", - "scheduled_banner": "Küldés ütemezve: {date}", - "scheduled_send_created": "E-mail ütemezve a küldéshez", - "cancel_scheduled_send": "Küldés megszakítása", - "reschedule_send": "Átütemezés", - "cancel_and_edit": "Mégse és szerkesztés", - "cancel_and_compose_again": "Mégse és újraírás", - "reschedule_prompt": "Adj meg egy új dátumot/időpontot, pl. 2026-05-04T15:30", - "scheduled_actions_only": "Az ütemezett nézet csak ütemezett műveleteket támogat", - "undo_send_scheduled": "Üzenet ütemezve a küldéshez", - "undo_send": "Küldés visszavonása", - "contact_sidebar": { - "title": "Névjegy", - "close": "Oldalsáv bezárása", - "action_email": "E-mail", - "action_email_title": "E-mail küldése", - "action_copy": "Másolás", - "action_copy_title": "E-mail másolása", - "action_edit_title": "Névjegy szerkesztése", - "section_emails": "E-mailek", - "section_phones": "Telefonszámok", - "section_organizations": "Szervezetek", - "section_addresses": "Címek", - "section_notes": "Jegyzetek", - "not_in_contacts": "Nincs a névjegyei között", - "add_to_contacts": "Hozzáadás a névjegyekhez", - "copied": "Másolva!", - "copy_failed": "A másolás nem sikerült" - }, - "send_now": "Küldés most" - }, - "email_composer": { - "read_receipt_on": "Olvasási visszaigazolás kérve (kattints a letiltáshoz)", - "read_receipt_off": "Olvasási visszaigazolás kérése", - "dsn_on": "Kézbesítési értesítés kérve (kattints a kikapcsoláshoz)", - "dsn_off": "Kézbesítési értesítés kérése", - "format_plain_text": "Váltás egyszerű szövegre", - "format_rich_text": "Váltás formázott szövegre (HTML)", - "require_tls_on": "Titkosított kézbesítés kötelező (kattints a kikapcsoláshoz)", - "require_tls_off": "Titkosított kézbesítés megkövetelése (TLS)", - "new_message": "Új üzenet", - "signature_edit_hint": "Kattintson duplán az aláírás szerkesztéséhez", - "reply": "Válasz", - "reply_all": "Válasz mindenkinek", - "forward": "Továbbítás", - "reply_to": "Válasz", - "reply_all_to": "Válasz mindenkinek", - "forward_message": "Továbbítás", - "from": "Feladó", - "to": "Címzett", - "cc": "Másolat", - "bcc": "Titkos másolat", - "subject": "Tárgy", - "body_placeholder": "Üzenet írása...", - "send": "Küldés", - "cancel": "Mégse", - "attach": "Csatolás", - "attach_photos": "Fényképek és videók", - "attach_files": "Fájlok", - "discard": "Elvetés", - "discard_draft_title": "Piszkozat elvetése?", - "discard_draft_confirm": "Nem mentett változtatások vannak. El akarod vetni ezt a piszkozatot?", - "saving": "Mentés...", - "sending": "Küldés...", - "add_link": "Hivatkozás hozzáadása", - "link_url_prompt": "Add meg az URL-t", - "draft_saved": "Piszkozat elmentve", - "save_failed": "Mentés sikertelen", - "to_placeholder": "Címzett e-mail címek", - "cc_placeholder": "Másolat címzettek", - "bcc_placeholder": "Titkos másolat címzettek", - "subject_placeholder": "Tárgy", - "cc_label": "Másolat:", - "bcc_label": "Titkos másolat:", - "subject_label": "Tárgy:", - "file_size_kb": "KB", - "prefix": { - "forward": "Továbbítás:", - "reply": "Válasz:" - }, - "no_subject": "(Nincs tárgy)", - "unknown_sender": "Ismeretlen", - "quote": { - "reply_header": "{date} -án/én {sender} írta:", - "forward_header": "---------- Továbbított üzenet ----------", - "from": "Feladó: {sender}", - "date": "Dátum: {date}", - "subject": "Tárgy: {subject}", - "to": "Címzett: {recipients}" - }, - "remove_sub_address": "Alcím eltávolítása", - "from_override": { - "toggle_off": "Felülbírálás", - "toggle_on": "Felülbírálás megszüntetése", - "toggle_tooltip": "A Feladó név és cím szabad szerkesztése. A levél továbbra is az identitásodon keresztül kerül elküldésre.", - "name_label": "Feladó neve", - "name_placeholder": "Név", - "email_label": "Feladó e-mail címe", - "email_placeholder": "alias@example.com", - "envelope_notice": "Ha a szervered nem fogadja el ezt a címet borítékfeladóként, helyette a(z) {identity} cím kerül oda, és a címzettek láthatják a Return-Path fejlécben." - }, - "use_template": "Sablon", - "save_as_template": "Mentés sablonként", - "validation": { - "recipient_required": "Adj meg egy címzettet a küldéshez", - "body_required": "Írj üzenetet vagy csatolj fájlt", - "attachments_uploading": "A mellékletek még feltöltés alatt vannak – küldés a feltöltés befejezése után", - "attachment_upload_failed": "Nincs elküldve - egy melléklet feltöltése nem sikerült. Távolítsa el, és próbálja újra." - }, - "upload_progress": "Feltöltés {uploaded} / {total}", - "upload_cancel": "Feltöltés megszakítása", - "upload_failed": "Nem sikerült feltölteni: {filename}", - "attachment_too_large": "A(z) „{name}” nagyobb, mint amit a kiszolgáló enged (fájlonként {max})", - "attachments_total_too_large": "A mellékletek túllépnék a kiszolgáló üzenetenkénti {max} korlátját", - "drop_files": "Fájlok ejtése a csatoláshoz", - "show_less": "Kevesebb mutatása", - "send_failed": "Nem sikerült elküldeni az e-mailt", - "send_timeout": "A kiszolgáló nem válaszol többé. Újraküldés előtt ellenőrizze az Elküldött mappát – az üzenet lehet, hogy már kiment.", - "continue_draft": "Piszkozat folytatása", - "close_draft_title": "Piszkozat mentése vagy elvetése?", - "close_draft_message": "Nem mentett változtatások vannak. Szeretnéd piszkozatként menteni vagy elvetni?", - "smime_sign_on": "S/MIME aláírás engedélyezve", - "smime_sign_off": "S/MIME aláírás engedélyezése", - "smime_encrypt_on": "S/MIME titkosítás engedélyezve", - "smime_encrypt_off": "S/MIME titkosítás engedélyezése", - "smime_encrypt_unavailable": "S/MIME titkosítás nem elérhető – hiányoznak a címzett tanúsítványai", - "smime_unlock_title": "S/MIME kulcs feloldása", - "smime_unlock_message": "Add meg a jelszót az S/MIME aláíró kulcs feloldásához.", - "smime_unlock_button": "Feloldás", - "smime_passphrase_placeholder": "Jelszó", - "forgot_attachment": { - "title": "Elfelejtettél egy csatolmányt?", - "message": "Az üzeneted említi a \"{keyword}\" kifejezést, de nincs fájl csatolva. Elküldöd így is?", - "send_anyway": "Küldés így is", - "back": "Vissza a szerkesztéshez" - }, - "empty_subject": { - "title": "Küldés tárgy nélkül?", - "message": "Ennek az üzenetnek nincs tárgya. Mégis elküldi?", - "dont_ask_again": "Ne kérdezze többé", - "send_anyway": "Küldés így is", - "back": "Vissza a szerkesztéshez" - }, - "schedule_send": "Késleltetett küldés", - "schedule_send_description": "Válaszd ki, mikor küldje el a szerver az üzenetet.", - "schedule_send_required": "Válassz dátumot és időpontot.", - "schedule_send_invalid": "Adj meg egy érvényes dátumot és időpontot.", - "schedule_send_future": "Válassz egy jövőbeli dátumot és időpontot.", - "schedule_send_too_late": "Ez az időpont későbbi, mint amit a szerver engedélyez.", - "schedule_send_unsupported": "Az ütemezett küldés nem támogatott ehhez a fiókhoz.", - "schedule_send_cleanup_warning": "Az ütemezett küldés létrejött, de a piszkozat tisztítása sikertelen volt.", - "send_delay_unsupported": "A késleltetett küldés nem támogatott ehhez a fiókhoz.", - "send_delay_unsupported_confirm": "Ez a fiók nem támogatja a késleltetett küldést. Elküldjük azonnal?", - "recipient_edit_email": "E-mail-cím szerkesztése", - "recipient_edit_name": "Megjelenített név szerkesztése", - "recipient_email_placeholder": "E-mail-cím", - "recipient_name_placeholder": "Megjelenített név", - "autocomplete_search_server": "Keresés a kiszolgálón", - "autocomplete_searching": "Keresés...", - "toolbar": { - "bold": "Félkövér", - "italic": "Dőlt", - "underline": "Aláhúzott", - "strikethrough": "Áthúzott", - "text_color": "Betűszín", - "remove_color": "Szín eltávolítása", - "heading_1": "Címsor 1", - "heading_2": "Címsor 2", - "bullet_list": "Felsorolás", - "ordered_list": "Számozott lista", - "quote": "Idézet", - "code_block": "Kódblokk", - "align_left": "Balra igazítás", - "align_center": "Középre igazítás", - "align_right": "Jobbra igazítás", - "text_direction": "Szövegirány (RTL/LTR)", - "link": "Hivatkozás", - "table": "Táblázat", - "clear_formatting": "Formázás törlése", - "undo": "Visszavonás", - "redo": "Újra", - "add_row_above": "Sor beszúrása fölé", - "add_row_below": "Sor beszúrása alá", - "add_column_before": "Oszlop beszúrása elé", - "add_column_after": "Oszlop beszúrása mögé", - "delete_row": "Sor törlése", - "delete_column": "Oszlop törlése", - "toggle_header_row": "Fejlécsor váltása", - "delete_table": "Táblázat törlése", - "pick_size": "Méret kiválasztása", - "font_size": "Betűméret", - "font_size_default": "Alapértelmezett", - "background_color": "Háttérszín", - "remove_background_color": "Háttérszín eltávolítása" - }, - "send_filing_warning": "Elküldve - de az utólagos rendrakás nem sikerült, egy elavult piszkozat megmaradhat." - }, - "confirm_dialog": { - "confirm": "Megerősítés", - "cancel": "Mégse" - }, - "common": { - "loading": "Betöltés...", - "error": "Hiba", - "success": "Siker", - "cancel": "Mégse", - "save": "Mentés", - "delete": "Törlés", - "edit": "Szerkesztés", - "close": "Bezárás", - "search": "Keresés", - "refresh": "Frissítés", - "settings": "Beállítások", - "help": "Súgó", - "logout": "Kijelentkezés", - "yes": "Igen", - "no": "Nem", - "unknown": "Ismeretlen", - "app_title": "Webmail", - "reconnecting": "Kapcsolat megszakadt. Újracsatlakozás...", - "rate_limited_title": "A szerver hitelesítés átmenetileg korlátozva van.", - "rate_limited_detail": "A Bulwark szüneteltette a háttérkéréseket a lezárás elkerülése érdekében. Újrapróbálkozás {seconds} másodperc múlva.", - "rate_limited_action_title": "Kérés szüneteltetve a lezárás elkerülése érdekében.", - "rate_limited_action_detail": "A Bulwark várja a szerver lehűlését, mielőtt további hitelesített kéréseket küldene. Próbáld újra {seconds} másodperc múlva.", - "pull_to_refresh": "Húzza le a frissítéshez", - "release_to_refresh": "Engedje el a frissítéshez", - "refreshing": "Frissítés…" - }, - "notifications": { - "email_sent": "E-mail sikeresen elküldve", - "email_deleted": "E-mail törölve", - "email_archived": "E-mail archiválva", - "emails_deleted": "{count, plural, other {# e-mail törölve}}", - "emails_archived": "{count, plural, other {# e-mail archiválva}}", - "error_archiving": "Nem sikerült archiválni az e-mailt", - "error_updating": "Nem sikerült frissíteni az e-mailt", - "email_starred": "E-mail csillagozva", - "email_unstarred": "E-mail csillagozás megszüntetve", - "email_marked_read": "E-mail olvasottnak jelölve", - "email_marked_unread": "E-mail olvasatlannak jelölve", - "copied_to_clipboard": "Vágólapra másolva", - "source_copied": "Forrás a vágólapra másolva", - "error_sending": "Nem sikerült elküldeni az e-mailt", - "error_deleting": "Nem sikerült törölni az e-mailt", - "error_loading": "Nem sikerült betölteni az e-maileket", - "new_email": "Új e-mail", - "new_email_from": "{sender} feladótól", - "click_to_view": "Kattints a megtekintéshez", - "email_moved": "E-mail áthelyezve", - "emails_moved": "{count} e-mail áthelyezve", - "moved_to_mailbox": "Áthelyezve ide: {mailbox}", - "move_failed": "Áthelyezés sikertelen", - "move_error": "Nem sikerült áthelyezni az e-maileket a kiválasztott mappába", - "email_tagged": "E-mail címkézve", - "emails_tagged": "{count} e-mail címkézve", - "tag_failed": "Címkézés sikertelen", - "identity_created": "Identitás sikeresen létrehozva", - "identity_updated": "Identitás sikeresen frissítve", - "identity_deleted": "Identitás törölve", - "identity_set_primary": "Elsődleges identitás frissítve", - "identity_create_failed": "Nem sikerült létrehozni az identitást: {error}", - "identity_update_failed": "Nem sikerült frissíteni az identitást: {error}", - "identity_delete_failed": "Nem sikerült törölni az identitást: {error}", - "identity_unauthorized": "Nem vagy jogosult erről az e-mail címről küldeni", - "identity_not_found": "Identitás nem található", - "vacation_saved": "Automatikus válasz beállítások elmentve", - "vacation_save_failed": "Nem sikerült menteni az automatikus válasz beállításokat", - "filters_saved": "Szűrők sikeresen elmentve", - "filters_save_failed": "Nem sikerült menteni a szűrőket", - "filters_deleted": "Szűrő szabály törölve", - "templates_exported": "Sablonok sikeresen exportálva", - "templates_imported": "{count, plural, one {# sablon} other {# sablon}} importálva", - "templates_import_errors": "Néhány sablon importálása sikertelen", - "templates_import_empty": "Nem található sablon a fájlban", - "export_email_error": "Nem sikerült exportálni az e-mailt", - "import_email_success": "E-mail sikeresen importálva", - "import_email_error": "Nem sikerült importálni az e-mailt" - }, - "date": { - "today": "Ma", - "yesterday": "Tegnap", - "this_week": "Ezen a héten", - "last_week": "Múlt héten", - "this_month": "Ebben a hónapban", - "older": "Régebbi", - "just_now": "Épp most", - "minutes_ago": "{count} perce", - "minutes_ago_plural": "{count} perce", - "hours_ago": "{count} órája", - "hours_ago_plural": "{count} órája", - "days_ago": "{count} napja", - "days_ago_plural": "{count} napja" - }, - "language": { - "title": "Nyelv", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Nyelv kiválasztása", - "switch_to_english": "Váltás angolra", - "switch_to_french": "Váltás franciára", - "switch_to_japanese": "Váltás japánra", - "switch_to_spanish": "Váltás spanyolra", - "switch_to_italian": "Váltás olaszra", - "switch_to_german": "Váltás németre", - "switch_to_dutch": "Váltás hollandra", - "switch_to_portuguese": "Váltás portugálra", - "switch_to_russian": "Váltás oroszra", - "switching": "Nyelv váltása...", - "switch": "Nyelv váltása", - "current": "Aktuális nyelv", - "polish": "Polski", - "switch_to_polish": "Váltás lengyelre", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Váltás perzsára", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Váltás mongolra" - }, - "settings": { - "title": "Beállítások", - "back_to_mail": "Vissza a levelekhez", - "save_success": "Beállítások sikeresen elmentve", - "import_success": "Beállítások sikeresen importálva", - "import_error": "Nem sikerült importálni a beállításokat", - "reset_confirm": "Biztosan vissza akarod állítani az összes beállítást az alapértelmezettre?", - "unsaved_changes": "Nem mentett változtatások vannak", - "discard_changes": "Nem mentett változtatások elvetése?", - "discard": "Elvetés", - "keep_editing": "Szerkesztés folytatása", - "search_placeholder": "Beállítások keresése", - "search_clear": "Keresés törlése", - "search_no_results": "Nincs találat a beállításokban", - "tabs": { - "appearance": "Megjelenés", - "language": "Nyelv, régió és idő", - "email": "E-mail viselkedés", - "composer": "Szerkesztő", - "privacy": "Adatvédelem és biztonság", - "account": "Fiók", - "identities": "Azonosságok", - "vacation": "Automatikus válasz", - "advanced": "Speciális", - "calendar": "Naptár", - "filters": "Szűrők", - "templates": "Sablonok", - "folders": "Mappák", - "keywords": "Címkék", - "security": "Biztonság", - "files": "Fájlok", - "contacts": "Névjegyek", - "encryption": "Titkosítás", - "protocol_handlers": "Alapértelmezett alkalmazások", - "sidebar_apps": "Oldalsáv alkalmazások", - "notifications": "Értesítések", - "layout": "Elrendezés", - "reading": "Olvasás", - "composing": "Írás", - "downloads": "Letöltések", - "content_senders": "Tartalom és feladók", - "about_data": "Névjegy és adatok", - "debug": "Hibakeresés" - }, - "tab_groups": { - "general": "Általános", - "account": "Fiók és azonosság", - "organization": "Levelezés szervezése", - "apps": "Alkalmazások", - "system": "Rendszer", - "appearance": "Megjelenés", - "mail": "Levelek", - "privacy": "Adatvédelem és biztonság", - "advanced": "Speciális" - }, - "appearance": { - "message_list_order": { - "label": "Üzenetlista sorrendje", - "description": "Hogyan rendeződnek az üzenetek a listában. A rendezést a kiszolgáló végzi, így a teljes mappára vonatkozik, nem csak a betöltött üzenetekre.", - "preset": { - "chronological": "Időrendi (legújabb elöl)", - "unread_first": "Olvasatlanok elöl", - "starred_first": "Csillagozottak elöl", - "tagged_first": "Címkézettek elöl", - "custom": "Egyéni" - }, - "tag_label": "Címke", - "advanced_show": "Speciális: rendezési szintek szerkesztése", - "advanced_hide": "Speciális elrejtése", - "criterion_label": "{n}. szint feltétele", - "direction_label": "{n}. szint iránya", - "criteria": { - "unread": "Olvasatlan", - "starred": "Csillagozott", - "tag": "Címke", - "receivedAt": "Érkezés dátuma", - "sentAt": "Küldés dátuma", - "from": "Feladó", - "subject": "Tárgy", - "size": "Méret" - }, - "direction": { - "unread_first": "Olvasatlanok elöl", - "read_first": "Olvasottak elöl", - "starred_first": "Csillagozottak elöl", - "unstarred_first": "Csillag nélküliek elöl", - "tagged_first": "Címkézettek elöl", - "untagged_first": "Címkézetlenek elöl", - "newest_first": "Legújabb elöl", - "oldest_first": "Legrégebbi elöl", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Legnagyobb elöl", - "smallest_first": "Legkisebb elöl" - }, - "add_level": "Szint hozzáadása", - "remove_level": "Szint eltávolítása", - "move_up": "Feljebb", - "move_down": "Lejjebb", - "tiebreak_note": "Legfeljebb három szint; a további szintek döntenek holtverseny esetén. A „legújabb elöl” mindig utolsóként érvényesül.", - "scope": { - "label": "Alkalmazás minden mappára", - "description": "Kikapcsolva a sorrend csak a Beérkezett üzenetekre vonatkozik; a többi mappa időrendi marad." - }, - "unsupported_note": "A levelezőkiszolgáló nem támogatja az olvasottság, csillag vagy címke szerinti rendezést. Ezeket a szinteket figyelmen kívül hagyja, és a lista időrendi marad." - }, - "title": "Megjelenés", - "description": "Webmail megjelenésének testreszabása", - "theme": { - "label": "Téma", - "description": "Válaszd ki a preferált színösszeállítást", - "light": "Világos", - "dark": "Sötét", - "system": "Rendszer" - }, - "language": { - "label": "Nyelv", - "description": "Válaszd ki a preferált nyelvet" - }, - "font_size": { - "label": "Betűméret", - "description": "Szövegméret beállítása a jobb olvashatóság érdekében", - "small": "Kicsi", - "medium": "Közepes", - "large": "Nagy" - }, - "list_density": { - "label": "Sűrűség", - "description": "Távolság és kitöltés szabályozása a felületen", - "extra_compact": "Extra kompakt", - "compact": "Kompakt", - "regular": "Normál", - "comfortable": "Kényelmes" - }, - "animations": { - "label": "Animációk engedélyezése", - "description": "Simább átmenetek és effektek megjelenítése" - }, - "toolbar_position": { - "label": "Eszköztár pozíciója", - "description": "Hol jelenjenek meg az e-mail műveleti gombok (Válasz, Archiválás, Törlés, stb.)", - "top": "Felül", - "below_subject": "Tárgy alatt" - }, - "toolbar_labels": { - "label": "Eszköztár feliratok mutatása", - "description": "Szöveges feliratok megjelenítése az eszköztár ikonok mellett. Kapcsold ki a helytakarékosság érdekében, ha már ismered az ikonokat." - }, - "hide_account_switcher": { - "label": "Oldalsáv fiókváltó elrejtése", - "description": "Fiókválasztó elrejtése a mappa oldalsáv tetején. A fiókokat továbbra is válthatod az alsó navigációs sávból." - }, - "show_rail_account_list": { - "label": "Fiók avatárok mutatása a navigációs sávon", - "description": "Egyedi fiókkörök megjelenítése a navigációs sáv alján a gyors váltáshoz, kijelentkezés gombbal alatta." - }, - "unified_mailbox": { - "label": "Egységes postafiók", - "description": "Összevont mappák (Beérkező, Elküldött, stb.) megjelenítése az összes csatlakoztatott fiók között", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Csoportos postafiókok belefoglalása", - "description": "Megosztott/csoportos postafiókok egyesítése az egységes nézetbe." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Színes oldalsáv ikonok", - "description": "Mappa és címke ikonok színezése típus szerint (kék Beérkező, piros Spam, zöld Elküldött, stb.). Kapcsold ki az egyszínű oldalsávhoz." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Összes üzenet számának megjelenítése", - "description": "Megjeleníti az üzenetek teljes számát a mappák és címkék mellett, az olvasatlanok számán túl. Kapcsolja ki, ha csak az olvasatlanok számát szeretné látni." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro felület (Kísérleti)", - "description": "Asztali számítógépes erőfelhasználói elrendezés több lapos üzenetböngészéssel és fiókok közötti munkafolyamatokkal. A szabványos felületet nem érinti; bármikor visszaválthatsz.", - "open_label": "Pro felület megnyitása", - "back_to_standard": "Vissza a szabványoshoz" - }, - "cross_unread": { - "label": "Összes fiók: Olvasatlan", - "description": "Megjelenít egy bejegyzést az Összes fiók szakaszban, amely az összes fiók olvasatlan leveleit listázza, a levélszemét, elküldött, archívum, kuka és piszkozatok mappák kivételével minden mappára kiterjedően." - }, - "cross_starred": { - "label": "Összes fiók: Csillagozott", - "description": "Megjelenít egy bejegyzést az Összes fiók szakaszban, amely az összes fiók csillagozott leveleit listázza, a levélszemét, elküldött, archívum, kuka és piszkozatok mappák kivételével minden mappára kiterjedően." - }, - "cross_all": { - "label": "Összes fiók: Összes levél", - "description": "Megjelenít egy bejegyzést az Összes fiók szakaszban, amely az összes fiók összes levelét listázza, a levélszemét, elküldött, archívum, kuka és piszkozatok mappák kivételével minden mappára kiterjedően." - } - }, - "keywords": { - "title": "E-mail címkék", - "description": "Címkék definiálása az e-mailek színekkel történő rendezéséhez. Ezek JMAP kulcsszavakként tárolódnak a szerveren.", - "add_keyword": "Címke hozzáadása", - "label_field": "Megjelenített név", - "label_placeholder": "pl. Munka, Személyes, Sürgős", - "id_field": "Címke azonosító", - "id_placeholder": "pl. munka, szemelyes", - "color_field": "Szín", - "id_exists": "Ez a címke azonosító már létezik", - "edit": "Címke szerkesztése", - "delete": "Címke törlése", - "save": "Mentés", - "add": "Hozzáadás", - "cancel": "Mégse", - "migrating": "Címke frissítése a meglévő e-maileken...", - "migration_error": "Nem sikerült frissíteni a címkét a meglévő e-maileken", - "nesting": { - "label": "Beágyazott címkék", - "description": "Ágyazzon címkéket más címkék alá, és jelenítse meg őket fastruktúraként az oldalsávon." - }, - "parent_field": "Szülőcímke", - "no_parent": "Nincs szülőcímke", - "too_long": "Ez a címkeútvonal túl hosszú (legfeljebb {max} karakter)", - "has_children_locked": "Más címkék vannak beágyazva ez alá, ezért a neve és a szülőcímkéje zárolva van. Előbb helyezze át vagy távolítsa el őket.", - "has_children_delete": "Előbb távolítsa el az ez alá beágyazott címkéket", - "visibility_field": "Láthatóság az oldalsávon", - "visibility": { - "show": "Megjelenítés", - "unread": "Megjelenítés olvasatlanoknál", - "hide": "Elrejtés" - }, - "unrecognized": { - "title": "Címkék helyreállítása a szerverről", - "description": "A címkék nevét és színét ez a böngésző tárolja, maguk a címkék azonban JMAP kulcsszavakként az e-mailjein vannak. Keresse meg a levelein azokat a kulcsszavakat, amelyekhez itt nem tartozik címke, majd adja hozzá őket újra.", - "scan": "Címkék keresése", - "rescan": "Keresés újra", - "scanning": "Keresés… {total} e-mailből {scanned}", - "none": "Az e-mailjein szereplő összes címke már definiálva van itt.", - "messages": "{count, plural, one {# e-mail} other {# e-mail}}", - "add_all": "Mind a(z) {count} hozzáadása", - "partial": "A(z) {total} e-mailből csak a {scanned} legutóbbi lett átvizsgálva. A csak régebbi leveleken használt címkék hiányozhatnak.", - "error": "Nem sikerült átvizsgálni a leveleket címkék után." - } - }, - "notifications": { - "test_sound": "Értesítési hang tesztelése", - "sounds": { - "default": "Alapértelmezett (sípolás)", - "cheerful": "Vidám", - "involved": "Lelkes", - "swift": "Gyors mozdulat", - "relax": "Pihenés" - }, - "push": { - "title": "Háttérértesítések", - "description": "Rendszerértesítések fogadása új levelekről, amikor ez az oldal be van zárva. A Bulwark push relén keresztül kézbesítve; a relay soha nem látja a levél tartalmát.", - "relay_label": "Push relé", - "relay_desc": "Alapértelmezés szerint a tárolt Bulwark relay. Csak akkor módosítsd, ha sajátot üzemeltetsz.", - "relay_locked": "Rendszergazda által beállítva", - "relay_locked_desc": "A push relay URL-t a rendszergazda állította be, és nem módosítható.", - "status_active": "Aktív ezen az eszközön", - "status_inactive": "Nincs engedélyezve ezen az eszközön", - "status_unsupported": "Ez a böngésző nem támogatja a Web Push-t", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "Munka...", - "enable": "Engedélyezés", - "reenable": "Újraregisztráció", - "disable": "Letiltás", - "confirm_disable_title": "Háttérértesítések letiltása?", - "confirm_disable_message": "Ez az eszköz nem fog figyelmeztetéseket kapni, amikor az oldal be van zárva.", - "dismiss_aria": "Értesítési ablak elutasítása", - "ios_hint": "iOS-en először telepítsd az oldalt a kezdőképernyőre - a Safari csak telepített PWA-knak kézbesít Web Push értesítéseket.", - "devices_title": "Regisztrált eszközök", - "devices_desc": "Minden eszköz, amely háttérértesítéseket kap ehhez a fiókhoz. A visszavonás után az adott eszköz azonnal nem kap többet.", - "devices_loading": "Regisztrált eszközök betöltése…", - "devices_empty": "Nincs eszköz regisztrálva háttérértesítésekhez.", - "devices_error": "A regisztrált eszközöket nem sikerült betölteni.", - "devices_refresh": "Frissítés", - "device_this": "Ez az eszköz", - "device_other": "Másik eszköz", - "device_status_active": "Értesítéseket kézbesít", - "device_status_inactive": "Még soha nem kézbesített értesítést", - "device_status_unknown": "Ez az átjáró nem ismeri", - "device_expires": "Lejár: {date}", - "revoke": "Visszavonás", - "confirm_revoke_title": "Visszavonja ezt az eszközt?", - "confirm_revoke_message": "Azonnal nem kap több háttérértesítést. Ha az eszközön újra bekapcsolja a push értesítéseket, újra regisztrálódik.", - "confirm_revoke_message_this": "Ez az eszköz azonnal nem kap több háttérértesítést." - }, - "sound_selection": { - "title": "Értesítési hang", - "description": "Válaszd ki az értesítésekhez használt hangot", - "choose": "Hang", - "choose_desc": "Válassz ki egy értesítési hangot, és kattints a hangszóró ikonra az előnézethez" - }, - "email": { - "title": "E-mail értesítések", - "description": "Értesítések beállítása a beérkező e-mailekhez", - "enabled": "E-mail értesítések", - "enabled_desc": "Értesítések megjelenítése új e-mailek érkezésekor", - "sound": "Értesítési hang", - "sound_desc": "Hangjelzés lejátszása új e-mailek érkezésekor", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Naptár értesítések", - "description": "Értesítések beállítása a naptári eseményekhez", - "enabled": "Esemény értesítések", - "enabled_desc": "Figyelmeztetések megjelenítése a közelgő naptári eseményekhez", - "sound": "Értesítési hang", - "sound_desc": "Hangjelzés lejátszása naptári emlékeztetőkhöz", - "invitation_parsing": "E-mail meghívók feldolgozása", - "invitation_parsing_desc": "Naptármeghívók észlelése az e-mail csatolmányokban és naptár műveletek megjelenítése" - } - }, - "language_region": { - "title": "Nyelv, régió és idő", - "description": "Nyelv, dátumformátum, időformátum és egyéb regionális beállítások", - "language": { - "label": "Nyelv", - "description": "Válaszd ki a preferált nyelvet", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Dátum formátum", - "description": "Hogyan jelenjenek meg a dátumok az e-mail listában", - "smart": "Intelligens (nyelvfüggő)", - "relative": "Relatív (1 órája, 2 napja)", - "full": "Mindig teljes dátum", - "preview_today": "Ma:", - "preview_this_week": "Ezen a héten:", - "preview_older": "Régebbi:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Idő formátum", - "description": "Válassz a 12 vagy 24 órás időformátum között", - "12h": "12 órás", - "24h": "24 órás" - }, - "first_day": { - "label": "Hét első napja", - "description": "Hét kezdése vasárnappal vagy hétfővel", - "sunday": "Vasárnap", - "monday": "Hétfő" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Keresés törlése mappaváltáskor", - "description": "Egy mappára kattintva (Beérkezett, Elküldött…) törlődik az aktív keresés, és a mappa jelenik meg. Kikapcsolva a keresés megmarad, és újra lefut a választott mappában." - }, - "request_read_receipt": { - "label": "Olvasási visszaigazolás kérése alapértelmezés szerint", - "description": "Az olvasási visszaigazolás kérésének előre bekapcsolása új üzenet írásakor." - }, - "read_receipt_response": { - "label": "Válasz az olvasási visszaigazolásokra", - "description": "Mi történjen, ha egy beérkező üzenet olvasási visszaigazolást kér.", - "ask": "Kérdezzen minden alkalommal", - "always": "Mindig elküldi", - "never": "Soha nem küldi el" - }, - "title": "E-mail viselkedés", - "description": "Az e-mailek kezelésének beállítása", - "mark_read": { - "label": "Olvasottnak jelölés", - "description": "Mikor jelölje olvasottnak az e-maileket megnyitáskor", - "instant": "Azonnal", - "delay_3s": "3 másodperc után", - "delay_5s": "5 másodperc után", - "never": "Soha" - }, - "delete_action": { - "label": "Törlés művelet", - "description": "Mi történjen az e-mail törlésekor", - "trash": "Áthelyezés a Kukába", - "trash_and_read": "Áthelyezés a Kukába és olvasottnak jelölés", - "permanent": "Végleges törlés", - "warning": "Az e-mailek véglegesen törlődnek és nem állíthatók vissza. Ez a művelet visszafordíthatatlan." - }, - "message_spacing": { - "label": "Üzenet térköze", - "description": "Térköz az üzenet törzse körül olvasáskor", - "auto": "Automatikus (ajánlott)", - "always": "Mindig legyen térköz", - "edge": "Széltől szélig" - }, - "plain_text_font": { - "label": "Egyszerű szöveg betűtípusa", - "description": "Az egyszerű szöveges e-mailekhez használt betűtípus", - "mono": "Rögzített szélességű", - "sans": "Normál (mint az alkalmazás)" - }, - "archive_mode": { - "label": "Archiválás helye", - "description": "Hogyan rendezze az e-maileket archiváláskor", - "single": "Egyetlen mappa", - "year": "Mappa évenként", - "month": "Mappa havonta", - "reorganize": "Meglévő archívum átszervezése", - "reorganize_success": "{count, plural, =0 {Nincs átszervezendő e-mail} =1 {1 e-mail átszervezve} other {# e-mail átszervezve}}", - "reorganize_error": "Nem sikerült átszervezni az archívumot" - }, - "permanently_delete_junk": { - "label": "Spam végleges törlése", - "description": "E-mailek végleges törlése a Spam mappából ahelyett, hogy a Kukába kerülnének" - }, - "mail_layout": { - "label": "Levelezés elrendezés", - "description": "Válassz a klasszikus osztott olvasóablak, a Gmail-stílusú fókuszált olvasás vagy a Zimbra-stílusú alsó olvasóablak között.", - "split": "Osztott ablak", - "split_description": "Üzenetlista és olvasóablak egymás mellett.", - "focus": "Fókuszált lista", - "focus_description": "Egy sor üzenetenként, és a levél teljes szélességben nyílik meg, miközben a mappa oldalsáv látható marad.", - "horizontal": "Olvasóablak alul", - "horizontal_description": "Üzenetlista felül, a kiválasztott üzenet pedig egy olvasóablakban alatta." - }, - "show_preview": { - "label": "Előnézeti szöveg mutatása", - "description": "E-mail előnézet megjelenítése a listában", - "focus_description": "Beágyazott előnézeti szöveg megjelenítése a fókuszált egysoros üzenetlistában" - }, - "disable_threading": { - "label": "Beszélgetéscsoportosítás letiltása", - "description": "E-mailek megjelenítése egyedi üzenetként ahelyett, hogy beszélgetésekbe lennének csoportosítva" - }, - "plain_text_mode": { - "label": "Csak szöveges mód", - "description": "Rich text szerkesztő letiltása és az összes e-mail küldése csak szöveges formátumban, beleértve a válaszokat és továbbításokat" - }, - "auto_select_reply_identity": { - "label": "Válasz a fogadott címről", - "description": "Válaszküldéskor arról a címről küldjön, amelyre az üzenet eredetileg érkezett. Először identitásokat keres; tartományi catch-all kézbesítések esetén átírja a Feladó fejlécet az aliasra, miközben az elsődleges identitáson keresztül küld." - }, - "reply_identity_match": { - "label": "Fogadó cím egyeztetése", - "description": "Mely fogadó címek számítanak a sajátjának. A „Csak pontos cím” kizárólag a beállított identitásai közül választ; az „azonos domain” az identitás-domainjein lévő bármely más címet is catch-all aliasként kezeli, és arra írja át a Feladó fejlécet. Válassza a pontos címet, ha ezek a címek terjesztési listák.", - "exact": "Csak pontos cím", - "domain": "Bármely cím a domainjeimen" - }, - "signature_position": { - "label": "Aláírás pozíciója", - "description": "Hova kerüljön az aláírás a válaszokban és továbbításokban. Az idézett szöveg felett természetes lezárásként olvasható; alatta az eredeti üzenet marad egybefüggő.", - "above_quote": "Idézett szöveg előtt", - "below_quote": "Idézett szöveg után" - }, - "signature_separator": { - "label": "Aláírás elválasztó", - "description": "Aláírás előtagjaként a szabványos \"-- \" elválasztó sor (RFC 3676). Kapcsold ki, ha közvetlenül az üzenetből szeretnél az aláírásba folyni." - }, - "sub_address_delimiter": { - "label": "Alcím elválasztó", - "description": "Karakter, amely elválasztja a felhasználónevet az alcím címkétől. Illeszkedjen a levelezőszerver által használt elválasztóhoz (pl. felhasználó{elválasztó}cimke@domain.com).", - "option": "{elválasztó} (felhasználó{elválasztó}cimke@domain.com)", - "custom": "Egyéni...", - "custom_input_label": "Egyéni elválasztó karakter" - }, - "attachment_click_action": { - "label": "Csatolmány kattintási művelet", - "description": "Válaszd ki, hogy a csatolmányra kattintva előnézet vagy azonnali letöltés történjen", - "preview": "Előnézet, ha lehetséges", - "download": "Azonnali letöltés" - }, - "attachment_position": { - "label": "Csatolmány pozíciója", - "description": "Hol jelenjenek meg a csatolmányok az e-mail fejlécben", - "beside-sender": "Feladó mellett", - "below-header": "Fejléc alatt" - }, - "emails_per_page": { - "10": "10 e-mail", - "25": "25 e-mail", - "50": "50 e-mail", - "100": "100 e-mail", - "label": "E-mailek oldalanként", - "description": "Egyszerre betöltendő e-mailek száma" - }, - "always_light_mode": { - "label": "E-mailek mindig világos módban", - "description": "E-mail tartalom megjelenítése világos módban akkor is, ha az alkalmazás sötét módban van, elkerülve a sötét mód konverziós problémáit" - }, - "external_content": { - "label": "Külső tartalom", - "description": "Képek és külső tartalmak kezelése", - "ask": "Mindig kérdezzen", - "block": "Mindig blokkoljon", - "allow": "Mindig engedélyezzen" - }, - "trusted_senders": { - "label": "Megbízható feladók", - "description": "Azon feladók kezelése, akiknek a képei automatikusan betöltődnek", - "count_zero": "Nincs", - "count_one": "1 feladó", - "count_other": "{count} feladó", - "modal_title": "Megbízható feladók", - "empty_title": "Még nincsenek megbízható feladók", - "empty_description": "Amikor blokkolt képekkel rendelkező e-mailt nézel, kattints a \"Feladó megbízható\" gombra a hozzáadáshoz.", - "add_manually": "Feladó kézi hozzáadása", - "add_button": "Hozzáadás", - "add_placeholder": "E-mail cím megadása", - "search_placeholder": "Feladók keresése...", - "no_results": "Nincs a keresésnek megfelelő feladó", - "remove": "Eltávolítás", - "close": "Bezárás", - "invalid_email": "Kérjük, adj meg egy érvényes e-mail címet", - "already_added": "Ez a feladó már megbízható", - "save_error": "Nem sikerült menteni - ellenőrizd a Névjegyek hibakeresési naplót a részletekért", - "use_address_book_label": "Szinkronizálás a címjegyzékkel", - "use_address_book_description": "Megbízható feladók tárolása egy dedikált \"Megbízható feladók\" címjegyzékben, hogy szinkronizálódjanak az összes eszközöd között" - }, - "hover_actions": { - "label": "Gyors egérműveletek", - "description": "Válaszd ki, mely gyorsműveletek jelenjenek meg az e-mail fölé húzva a listában", - "delete": "Törlés", - "star": "Csillagozás / Csillagozás megszüntetése", - "mark_read": "Olvasott / Olvasatlan jelölés", - "archive": "Archiválás", - "tag": "Címkézés", - "spam": "Megjelölés spamként", - "not_spam": "Nem spam", - "none_selected": "Nincs művelet kiválasztva", - "mode_label": "Megjelenítési mód", - "mode_inline": "Beágyazott", - "mode_floating": "Lebegő", - "corner_label": "Lebegő pozíció", - "corner_top-left": "Bal felső", - "corner_top-right": "Jobb felső", - "corner_bottom-left": "Bal alsó", - "corner_bottom-right": "Jobb alsó" - }, - "default_mail_program": { - "label": "Alapértelmezett levelezőprogram", - "description": "{appName} regisztrálása alapértelmezett levelezőprogramként a mailto: hivatkozásokhoz", - "button": "Beállítás alapértelmezettként", - "success": "Böngésző felszólítva az alapértelmezett beállításra", - "error": "A böngésződ nem támogatja ezt a funkciót" - }, - "attachment_reminder": { - "label": "Csatolmány emlékeztető", - "description": "Figyelmeztetés küldés előtt, ha az üzenet csatolmányokat említ, de nincs csatolva", - "keywords_label": "Trigger kulcsszavak", - "keywords_description": "Szavak vagy kifejezések, amelyek kiváltják az emlékeztetőt, ha megtalálhatók az üzenetben", - "add_placeholder": "Kulcsszó hozzáadása...", - "add": "Hozzáadás", - "remove": "Eltávolítás" - }, - "empty_subject_warning": { - "label": "Figyelmeztetés üres tárgy esetén", - "description": "Megerősítés kérése tárgy nélküli üzenet küldése előtt." - }, - "hide_inline_image_attachments": { - "label": "Beágyazott képek elrejtése a csatolmányok közül", - "description": "Az üzenet törzsébe ágyazott képek nem jelennek meg külön csatolmányként" - }, - "attachment_image_previews": { - "label": "Kép előnézetek mutatása a csatolmányokban", - "description": "Képcsatolmányok megjelenítése miniatűr kártyákként az általános fájlikonok helyett" - }, - "send_delay": { - "label": "Küldés visszavonása / késleltetett küldés", - "description": "A normál küldések késleltetése egy rövid, szerver oldali időablakkal.", - "off": "Kikapcsolva", - "seconds": "{seconds} másodperc", - "unsupported": "A jelenlegi fiók nem támogatja a késleltetett küldést. A beállítás más fiókokhoz elmentésre kerül." - }, - "return_to_list_after_action": { - "label": "Visszatérés a listához törlés vagy olvasatlanként jelölés után", - "description": "A megnyitott üzenet törlése vagy olvasatlanként való megjelölése után visszatérés az üzenetlistához a következő üzenet megnyitása helyett." - }, - "swipe_right_action": { - "label": "Jobbra húzás művelete (mobil)", - "description": "Érintőképernyős eszközökön az a művelet, amely akkor hajtódik végre, amikor egy üzenetet jobbra húz a listában." - }, - "swipe_left_action": { - "label": "Balra húzás művelete (mobil)", - "description": "Érintőképernyős eszközökön az a művelet, amely akkor hajtódik végre, amikor egy üzenetet balra húz a listában." - }, - "swipe_actions": { - "none": "Nincs", - "archive": "Archiválás", - "delete": "Törlés", - "mark_read": "Olvasott állapot váltása", - "star": "Csillag váltása", - "spam": "Megjelölés spamként" - }, - "rtl_editing": { - "label": "Jobbról balra írás támogatása", - "description": "Iránygombot ad a szerkesztő eszköztárához, amellyel a bekezdések balról jobbra vagy jobbról balra állíthatók" - } - }, - "composer": { - "title": "Szerkesztő", - "description": "E-mail írási beállítások konfigurálása", - "autosave": { - "label": "Automatikus mentés időköze", - "description": "Milyen gyakran mentse a piszkozatokat automatikusan", - "30s": "30 másodpercenként", - "1m": "Percre", - "2m": "2 percenként", - "5m": "5 percenként" - }, - "send_confirmation": { - "label": "Küldés megerősítés", - "description": "Megerősítés kérése e-mailek küldése előtt" - }, - "default_reply": { - "label": "Alapértelmezett válasz mód", - "description": "Alapértelmezett művelet a válasz gombra kattintáskor", - "reply": "Válasz", - "reply_all": "Válasz mindenkinek" - } - }, - "privacy": { - "title": "Adatvédelem és biztonság", - "description": "Adatvédelmi és biztonsági beállítások kezelése", - "external_images": { - "label": "Külső képek blokkolása", - "description": "Követés megakadályozása külső képeken keresztül" - }, - "session_timeout": { - "label": "Munkamenet időtúllépés", - "description": "Automatikus kijelentkezés inaktivitás után", - "never": "Soha", - "30m": "30 perc", - "1h": "1 óra", - "4h": "4 óra" - }, - "clear_cache": { - "label": "Gyorsítótár törlése", - "description": "Gyorsítótárazott adatok és ideiglenes fájlok eltávolítása", - "button": "Gyorsítótár törlése", - "confirm": "Biztosan törölni szeretnéd a gyorsítótárat?", - "success": "Gyorsítótár sikeresen törölve" - } - }, - "account": { - "title": "Fiók", - "description": "Fiók információid megtekintése", - "name_label": "Megjelenített név", - "username_label": "Felhasználónév", - "account_type_label": "Fiók típusa", - "auth_method_label": "Hitelesítés", - "auth_method_oauth": "Egyszeri bejelentkezés (OAuth/OIDC)", - "auth_method_basic": "Jelszó", - "demo_account": "Demó fiók", - "email": { - "label": "E-mail cím", - "value": "{email}" - }, - "server": { - "label": "JMAP szerver", - "value": "{server}" - }, - "storage": { - "label": "Tárhely használat", - "used": "{used} / {total} használva", - "percentage": "{percent}% használva" - }, - "last_sync": { - "label": "Utolsó szinkronizálás", - "value": "{time}" - }, - "accounts": { - "title": "Bejelentkezett fiókok", - "description": "Húzd át a sorrend módosításához, hogy a fiókok hogyan jelenjenek meg a fiók legördülőben", - "active": "Jelenleg aktív fiók", - "default_badge": "Alapértelmezett fiók", - "set_default": "Beállítás alapértelmezettként", - "switch_to": "Váltás erre a fiókra", - "move_up": "Mozgatás fel", - "move_down": "Mozgatás le", - "drag_handle": "Húzd át a sorrend módosításához", - "add": "Fiók hozzáadása" - }, - "shared_accounts": { - "title": "Velem megosztva", - "description": "Csoportos és megosztott fiókok, amelyeket kezelhet. Válasszon egyet a szűrői, távolléti válasza, naptárai és névjegyei szerkesztéséhez.", - "shared_label": "Megosztott fiók" - } - }, - "security": { - "title": "Fiók biztonság", - "description": "Jelszó, kétfaktoros hitelesítés és biztonsági beállítások kezelése", - "detecting": "Szerver képességek érzékelése...", - "not_available": "A fiók biztonsági kezelés nem érhető el ehhez a levelezőszerverhez. A szükséges engedélyek lehet, hogy le vannak tiltva. Lásd a dokumentációt a részletekért.", - "password": { - "title": "Jelszó módosítása", - "current": "Jelenlegi jelszó", - "new": "Új jelszó", - "confirm": "Új jelszó megerősítése", - "submit": "Jelszó módosítása", - "success": "Jelszó sikeresen módosítva", - "error_title": "Jelszó módosítás sikertelen", - "error_mismatch": "Az új jelszavak nem egyeznek", - "error_min_length": "A jelszónak legalább 8 karakter hosszúnak kell lennie", - "error_generic": "Nem sikerült módosítani a jelszót" - }, - "display_name": { - "label": "Megjelenített név", - "description": "Neved, ahogy a szerveren megjelenik", - "placeholder": "Add meg a megjelenített neved", - "save": "Mentés", - "success": "Megjelenített név frissítve", - "error": "Nem sikerült frissíteni a megjelenített nevet" - }, - "totp": { - "section_title": "Kétfaktoros hitelesítés", - "label": "TOTP hitelesítés", - "description": "Extra biztonsági réteg egy időalapú egyszeri jelszóval", - "active": "Engedélyezve", - "inactive": "Letiltva", - "enabled": "Kétfaktoros hitelesítés engedélyezve", - "disabled": "Kétfaktoros hitelesítés letiltva", - "enable_error": "Nem sikerült engedélyezni a 2FA-t", - "disable_error": "Nem sikerült letiltani a 2FA-t", - "setup_instructions": "Másold ezt az URL-t a hitelesítő alkalmazásodba (Google Authenticator, Authy, stb.):", - "verification_code": "Ellenőrző kód", - "confirm": "Megerősítés", - "disable": "Letiltás", - "disable_confirm_prompt": "Add meg a jelszavad a kétfaktoros hitelesítés letiltásához.", - "password_required": "Jelszó megadása kötelező", - "code_required": "Ellenőrző kód megadása kötelező", - "code_invalid": "Érvénytelen ellenőrző kód. Ellenőrizd a hitelesítő alkalmazásod és próbáld újra." - }, - "app_passwords": { - "title": "Alkalmazás jelszavak", - "description": "Jelszavak létrehozása olyan alkalmazásokhoz, amelyek nem támogatják a kétfaktoros hitelesítést", - "add": "Hozzáadás", - "create": "Létrehozás", - "cancel": "Mégse", - "done": "Kész", - "generate": "Generálás", - "name_label": "Alkalmazás neve", - "name_placeholder": "pl. Thunderbird, iPhone Mail", - "expires_label": "Lejárat (opcionális)", - "allowed_ips_label": "Engedélyezett IP-k (opcionális)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Vesszővel vagy szóközzel elválasztva. Hagyd üresen bármely IP engedélyezéséhez.", - "password_label": "Jelszó (hagyd üresen az automatikus generáláshoz)", - "password_placeholder": "Automatikusan generált, ha üres", - "copy_now_warning": "Másold ki most ezt a jelszót - nem lesz újra megjelenítve.", - "added": "Alkalmazás jelszó létrehozva", - "removed": "Alkalmazás jelszó eltávolítva", - "add_error": "Nem sikerült létrehozni az alkalmazás jelszót", - "remove_error": "Nem sikerült eltávolítani az alkalmazás jelszót", - "none": "Nincs alkalmazás jelszó beállítva" - }, - "api_keys": { - "title": "API kulcsok", - "description": "API kulcsok létrehozása szkriptekhez és integrációkhoz, amelyek közvetlenül a szerverrel kommunikálnak", - "name_label": "Kulcs neve", - "name_placeholder": "pl. Biztonsági mentés szkript, CI futató", - "copy_now_warning": "Másold ki most ezt az API kulcsot - nem lesz újra megjelenítve.", - "added": "API kulcs létrehozva", - "removed": "API kulcs eltávolítva", - "add_error": "Nem sikerült létrehozni az API kulcsot", - "remove_error": "Nem sikerült eltávolítani az API kulcsot", - "none": "Nincs API kulcs beállítva" - }, - "encryption": { - "section_title": "Titkosítás nyugalmi állapotban", - "label": "E-mail titkosítás", - "description": "Tárolt e-mailek titkosítása a szerveren a további adatvédelem érdekében", - "active": "{type} titkosítás engedélyezve", - "inactive": "Letiltva", - "enabled": "Titkosítás nyugalmi állapotban engedélyezve", - "disabled_success": "Titkosítás nyugalmi állapotban letiltva", - "error": "Nem sikerült frissíteni a titkosítási beállításokat", - "algorithm_label": "Titkosítási algoritmus", - "encrypt_on_append": "Új e-mailek titkosítása feltöltéskor", - "allow_spam_training": "Spam-tanítás engedélyezése az e-mailek titkosítása előtt", - "enable_encryption": "Titkosítás engedélyezése", - "enable_tooltip": "Titkosítás engedélyezése ezzel a kulccsal", - "configure_title": "Titkosítás engedélyezése ezzel a kulccsal", - "enable_button": "Engedélyezés", - "disable_tooltip": "Titkosítás letiltása", - "enabled_success": "Titkosítás nyugalmi állapotban engedélyezve" - }, - "email_client": { - "title": "E-mail kliens beállítás", - "description": "Használd ezeket a hitelesítő adatokat az asztali vagy mobil e-mail kliensed beállításához (Thunderbird, Apple Mail, Outlook, stb.)", - "jmap_username_label": "JMAP felhasználónév", - "copy": "Másolás", - "copied": "Másolva", - "password_instructions": "Használd a fenti JMAP felhasználóneved egy alkalmazás jelszóval együtt az e-mail kliensbe való bejelentkezéshez. Hozz létre egy alkalmazás jelszót a fenti részben, ha még nem tetted." - }, - "link_device": { - "title": "Mobilalkalmazás összekapcsolása", - "description": "Jelentkezz be a Bulwark Mail mobilalkalmazásba gépelés nélkül. Generálj itt egy QR-kódot, és olvasd be az alkalmazás bejelentkezési képernyőjén.", - "generate": "QR-kód megjelenítése", - "regenerate": "Új kód megjelenítése", - "instructions": "Nyisd meg a Bulwark Mail alkalmazást, koppints a \"QR-kód beolvasása\" lehetőségre a bejelentkezési képernyőn, és irányítsd ide a kamerát.", - "expires_in": "Ez a kód {seconds} másodperc múlva lejár. Csak egyszer használható.", - "expired": "Ez a kód lejárt.", - "generating": "Generálás…", - "error": "Nem sikerült párosítási kódot létrehozni. Próbáld újra." - }, - "public_keys": { - "name_label": "Nyilvános kulcs neve", - "name_placeholder": "pl. A kulcsom", - "key_label": "Nyilvános kulcs (S/MIME vagy PGP ASCII formátumban)", - "key_placeholder": "Illessze be ide a nyilvános kulcsát", - "none": "Nincs beállítva nyilvános kulcs" - } - }, - "identities": { - "title": "Küldő azonosságok", - "description": "Azon e-mail címek kezelése, amelyekről küldhetsz", - "identities_count": { - "label": "Azonosságaid", - "description": "Küldésre beállított e-mail címek", - "count_zero": "Nincs azonosság", - "count_one": "1 azonosság", - "count_other": "{count} azonosság" - }, - "manage": "Azonosságok kezelése", - "sub_addressing": { - "label": "Alcímzés", - "description": "Címkék használata, mint pl. felhasználó+tag@domain.com a bejövő levelek rendezéséhez", - "learn_more": "További információk" - } - }, - "vacation": { - "title": "Automatikus válasz", - "description": "Automatikus válasz a beérkező e-mailekre távolléted alatt", - "loading": "Automatikus válasz beállítások betöltése...", - "not_supported": "A levelezőszervered nem támogatja az automatikus válaszokat.", - "fetch_error": "Nem sikerült betölteni az automatikus válasz beállításokat. Kérjük, próbáld újra.", - "status": { - "label": "Automatikus válasz", - "description": "Automatikus válasz küldése azoknak, akik e-mailt írnak neked", - "active": "Aktív", - "inactive": "Inaktív" - }, - "date_range": { - "title": "Dátumtartomány", - "description": "Opcionálisan korlátozd az automatikus választ egy adott időszakra", - "start": "Kezdő dátum", - "start_description": "Hagyd üresen, ha nincs kezdő korlát", - "end": "Befejező dátum", - "end_description": "Hagyd üresen, ha nincs befejező korlát" - }, - "message": { - "title": "Automatikus válasz üzenet", - "description": "Az üzenet, amely válaszként lesz elküldve", - "subject_label": "Tárgy", - "subject_description": "Az automatikus válasz tárgysora", - "subject_placeholder": "Távol vagyok", - "body_label": "Üzenet szövege", - "body_description": "Egyszerű szöveges üzenet tartalma", - "body_placeholder": "Köszönöm az e-mailed. Jelenleg távol vagyok az irodától, és visszatérésemkor válaszolok.", - "html_label": "Formázott üzenet (HTML)", - "html_description": "Adjon hozzá egy formázott változatot hivatkozásokkal és stílussal. Azok a címzettek, akiknek a levelezőprogramja nem tudja megjeleníteni, a fenti egyszerű szöveget látják.", - "html_placeholder": "Írjon formázott távolléti választ…" - }, - "preview": { - "title": "Előnézet", - "show": "Előnézet mutatása", - "hide": "Előnézet elrejtése" - }, - "save": "Változtatások mentése", - "saving": "Mentés...", - "warnings": { - "end_before_start": "A befejező dátumnak a kezdő dátum után kell lennie", - "start_in_past": "A kezdő dátum a múltban van", - "empty_body": "Az üzenet szövege üres - a címzettek üres választ kapnak", - "subject_too_long": "A tárgy túl hosszú a kiszolgálónak (legfeljebb {max} bájt).", - "body_too_long": "Az üzenet túl hosszú a kiszolgálónak (legfeljebb {max} bájt a formázással együtt)." - } - }, - "folders": { - "role_memos": "Jegyzetek", - "title": "Mappák", - "description": "E-mail mappák kezelése és szabványos szerepkörök hozzárendelése", - "folder_list": "Mappáid", - "folder_list_description": "Kattints egy mappa ikonra a testreszabáshoz", - "standard_roles": "Szabványos mappa szerepkörök", - "standard_roles_description": "Válaszd ki, mely mappák szolgálnak szabványos postafiók szerepkörökként, mint Beérkező, Elküldött, Kuka, stb.", - "role_inbox": "Beérkező", - "role_drafts": "Piszkozatok", - "role_sent": "Elküldött", - "role_trash": "Kuka", - "role_junk": "Spam", - "role_archive": "Archívum", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Nincs", - "create_folder": "Mappa létrehozása", - "create_subfolder": "Almappa létrehozása", - "reorder": "Húzza az átrendezéshez", - "reorder_error": "Nem sikerült átrendezni a mappákat", - "move_error": "Nem sikerült áthelyezni a mappát", - "subfolder_of": "Itt: {name}", - "subfolder_name": "Almappa neve", - "new_folder_name": "Mappa neve", - "rename": "Átnevezés", - "change_icon": "Ikon módosítása", - "delete": "Törlés", - "confirm_delete": "Biztosan törölni szeretnéd a \"{name}\" mappát? A mappában lévő e-mailek a Kukába kerülnek.", - "create": "Létrehozás", - "cancel": "Mégse", - "no_folders": "Nincsenek egyéni mappák", - "cannot_delete_role": "Nem törölhető szabványos szerepkörrel rendelkező mappa. Távolítsd el előbb a szerepkört.", - "folder_created": "Mappa létrehozva", - "folder_renamed": "Mappa átnevezve", - "folder_deleted": "Mappa törölve", - "role_updated": "Mappa szerepkör frissítve", - "error_create": "Nem sikerült létrehozni a mappát", - "error_rename": "Nem sikerült átnevezni a mappát", - "error_delete": "Nem sikerült törölni a mappát", - "error_delete_has_children": "Nem törölhető a mappa: almappákat tartalmaz. Előbb töröld vagy helyezd át azokat.", - "error_delete_has_email": "Nem törölhető a mappa: e-maileket tartalmaz. Előbb helyezd át vagy töröld azokat.", - "error_role": "Nem sikerült frissíteni a mappa szerepkört" - }, - "advanced": { - "title": "Speciális", - "description": "Speciális opciók és fejlesztői beállítások", - "debug_mode": { - "label": "Hibakeresési mód", - "description": "Részletes naplózás engedélyezése hibaelhárításhoz" - }, - "debug_categories": { - "description": "Válaszd ki a naplózandó kategóriákat. Kapcsold ki a nem szükséges kategóriákat a konzol zaj csökkentéséhez.", - "jmap": "JMAP kliens", - "jmap_description": "Postafiók műveletek, e-mail lekérések és JMAP protokoll kérések", - "calendar": "Naptár", - "calendar_description": "Naptári események, importálások és ütemezési üzenetek", - "tasks": "Feladatok", - "tasks_description": "Naptári feladatok létrehozása, lekérése és frissítése", - "auth": "Hitelesítés", - "auth_description": "Bejelentkezés, TOTP, token csere és munkamenet kezelés", - "filters": "Szűrők", - "filters_description": "Sieve szűrőszabályok és automatikus válasz szkriptek", - "email": "E-mail megtekintés", - "email_description": "E-mail megjelenítés, TNEF feldolgozás és olvasottnak jelölés", - "push": "Push értesítések", - "push_description": "Push értesítés beállítása és kézbesítése", - "contacts": "Névjegyek és címjegyzékek", - "contacts_description": "Névjegy szinkronizálás, címjegyzék műveletek és megbízható feladók" - }, - "settings_sync": { - "label": "Beállítások szinkronizálása", - "description": "Beállítások szinkronizálása a böngészők és eszközök között" - }, - "sender_favicons": { - "label": "Feladó faviconok", - "description": "Weboldal ikonok megjelenítése profilképekként az üzleti feladók számára" - }, - "show_avatars_in_junk": { - "label": "Avatárok mutatása a Spam mappában", - "description": "Profilképek és faviconok megjelenítése a feladókhoz a spam mappában. Alapértelmezés szerint letiltva, hogy ne adjon vizuális legitimitást az adathalász kísérleteknek." - }, - "keyboard_shortcuts": { - "label": "Billentyűparancsok", - "description": "Elérhető billentyűparancsok megtekintése", - "button": "Parancsok megtekintése" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Beállítások visszaállítása", - "description": "Összes beállítás visszaállítása az alapértelmezett értékekre", - "button": "Visszaállítás alapértelmezettre" - }, - "export_settings": { - "label": "Beállítások exportálása", - "description": "Beállítások letöltése JSON-ként", - "button": "Exportálás" - }, - "import_settings": { - "label": "Beállítások importálása", - "description": "Beállítások feltöltése JSON fájlból", - "button": "Importálás" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Oldalsáv alkalmazások", - "description": "Egyedi alkalmazások és hivatkozások kezelése az oldalsávon", - "keep_loaded": "Alkalmazások betöltve tartása", - "keep_loaded_description": "Beágyazott alkalmazások futtatása a háttérben váltáskor az újratöltés elkerülése érdekében", - "managed_title": "A rendszergazda biztosítja", - "managed_description": "Ezeket az alkalmazásokat mindenki számára beállították, és nem módosíthatók.", - "manage_title": "Egyedi alkalmazások", - "manage_description": "Egyedi alkalmazások hozzáadása, szerkesztése vagy eltávolítása az oldalsávról" - }, - "contacts": { - "title": "Névjegyek", - "description": "Névjegyek importálása és exportálása", - "group_by_letter_label": "Csoportosítás kezdőbetű szerint", - "sort_by_last_name_label": "Rendezés vezetéknév szerint", - "sort_by_last_name_description": "A névjegylista vezetéknév szerinti rendezése, hogy a családtagok egymás mellett legyenek", - "group_by_letter_description": "ABC sorrendű fejléc mutatása a névjegy listában", - "import_label": "Névjegyek importálása", - "import_description": "Névjegyek importálása vCard (.vcf) fájlból", - "export_label": "Névjegyek exportálása", - "export_description": "Összes névjegy exportálása vCard (.vcf) fájlként", - "manage_title": "Címjegyzékek", - "manage_description": "Címjegyzékeid átnevezése", - "no_address_books": "Nincs címjegyzék", - "categories_title": "Kategóriák", - "categories_description": "Névjegy kategóriák átnevezése", - "no_categories": "Nincs kategória" - }, - "downloads": { - "title": "Letöltések", - "description": "Letöltött e-mailek és csatolmányok elnevezésének testreszabása.", - "reset": "Alapértelmezett visszaállítása", - "preview": "Előnézet:", - "email_template": { - "label": "E-mail (.eml) fájlnév", - "description": "Sablon e-mailek exportálásakor vagy fájlrendszerbe húzásakor. A .eml kiterjesztés automatikusan hozzáadásra kerül." - }, - "attachment_template": { - "label": "Csatolmány fájlnév", - "description": "Sablon csatolmány letöltésekor vagy kihúzásakor. Ha kihagyod a '{filename}' és '{ext}' elemeket, az eredeti kiterjesztés megmarad." - }, - "bundle_template": { - "label": "Több e-mail .zip fájlnév", - "description": "Sablon több kijelölt e-mail .zip archívumként történő kihúzásakor vagy letöltésekor. A .zip kiterjesztés automatikusan hozzáadásra kerül." - }, - "spaces": { - "label": "Szóközök", - "description": "Szóközök cseréje a fájlnévben egy másik karakterre.", - "keep": "Szóközök megtartása", - "underscore": "Csere _ karakterre", - "dash": "Csere - karakterre" - }, - "lowercase": { - "label": "Kisbetűs", - "description": "Teljes fájlnév kisbetűssé alakítása." - }, - "strip_diacritics": { - "label": "Ékezetek eltávolítása", - "description": "Ékezetes betűk átalakítása ASCII megfelelőjükre (á → a, é → e). Hasznos olyan eszközökhöz, amelyek torzítják az Unicode fájlneveket." - }, - "collapse_separators": { - "label": "Ismétlődő elválasztók összevonása", - "description": "Szóközök, aláhúzásjelek vagy kötőjelek sorozatának összevonása egyetlen karakterré." - }, - "after_export": { - "label": "Exportálás után", - "description": "Opcionálisan helyezd át az e-mailt .eml exportálása után.", - "keep": "Megtartás a postafiókban", - "archive": "Áthelyezés az archívumba", - "trash": "Áthelyezés a kukába" - } - }, - "filters": { - "title": "E-mail szűrők", - "description": "Szabályok létrehozása a beérkező e-mailek automatikus rendezéséhez, címkézéséhez és kezeléséhez", - "add_rule": "Szabály hozzáadása", - "no_rules": "Nincs szűrőszabály", - "no_rules_description": "Hozz létre szabályokat a beérkező e-mailek automatikus rendezéséhez", - "vacation_active": "Automatikus válasz aktív", - "vacation_active_description": "Automatikus válasz engedélyezve a beérkező üzenetekhez", - "vacation_configure": "Beállítás", - "edit_rule": "Szabály szerkesztése", - "new_rule": "Új szabály", - "delete_rule": "Szabály törlése", - "delete_confirm": "Biztosan törölni szeretnéd ezt a szabályt?", - "enable": "Engedélyezés", - "disable": "Letiltás", - "raw_editor": "Nyers Sieve szerkesztő", - "raw_editor_warning": "A nyers Siev szkript szerkesztése megszakíthatja a vizuális szabályszerkesztést. Az itt végzett módosítások felülírják a vizuális szerkesztőt.", - "validate": "Ellenőrzés", - "validation_success": "A szkript érvényes", - "validation_error": "A szkript hibákat tartalmaz", - "save": "Szabályok mentése", - "saving": "Mentés...", - "saved": "Szűrők sikeresen elmentve", - "save_failed": "Nem sikerült menteni a szűrőket", - "loading": "Szűrők betöltése...", - "not_supported": "A levelezőszervered nem támogatja az e-mail szűrőket.", - "rule_name": "Szabály neve", - "rule_name_placeholder": "pl. Hírlevelek rendezése", - "match_all": "MINDEN feltétel teljesüljön", - "match_any": "BÁRMELYIK feltétel teljesüljön", - "conditions": "Feltételek", - "add_condition": "Feltétel hozzáadása", - "actions": "Műveletek", - "add_action": "Művelet hozzáadása", - "stop_processing": "További szabályok feldolgozásának leállítása", - "include_spam": "A levélszemétnek jelölt üzenetek áthelyezése is", - "attachment_type_placeholder": "pl. pdf, doc, jpg", - "value_placeholder_multi": "Érték (több érték vesszővel elválasztva)", - "condition_fields": { - "attachment": "Csatolmány", - "from": "Feladó", - "to": "Címzett", - "cc": "Másolat", - "subject": "Tárgy", - "header": "Egyéni fejléc", - "size": "Méret", - "body": "Törzs" - }, - "comparators": { - "has_any": "jelen van", - "has_type": "típusú", - "contains": "tartalmazza", - "not_contains": "nem tartalmazza", - "is": "pontosan", - "not_is": "nem", - "starts_with": "kezdődik", - "ends_with": "végződik", - "matches": "illeszkedik a mintára", - "greater_than": "nagyobb, mint", - "less_than": "kisebb, mint" - }, - "action_types": { - "move": "Áthelyezés mappába", - "copy": "Másolás mappába", - "forward": "Továbbítás", - "mark_read": "Olvasottnak jelölés", - "star": "Csillagozás", - "add_label": "Címke hozzáadása", - "discard": "Elvetés (csendes törlés)", - "reject": "Elutasítás üzenettel", - "keep": "Megtartás a beérkezőben", - "stop": "Feldolgozás leállítása" - }, - "move_to_folder": "Mappa kiválasztása", - "copy_to_folder": "Mappa kiválasztása", - "forward_to": "Továbbítás e-mail címre", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Másolat megtartása", - "forward_limit": "Továbbítási korlát üzenetenként ezen a kiszolgálón: {count}. A további továbbítások kimaradnak.", - "reject_message": "Elutasító üzenet", - "reject_placeholder": "Az e-mailed elutasításra került", - "label_name": "Címke neve", - "label_placeholder": "Címke kiválasztása", - "header_name": "Fejléc neve", - "header_placeholder": "pl. X-Mailing-List", - "size_bytes": "Méret bájtokban", - "size_placeholder": "pl. 1000000", - "system_managed": "Rendszer által kezelt szabály", - "opaque_warning": "Ezt a szkriptet a vizuális szerkesztőn kívül szerkesztették. Csak nyers Sieve szerkesztés érhető el.", - "open_sieve_editor": "Nyers Sieve szerkesztő megnyitása", - "fetch_error": "Nem sikerült betölteni a szűrőket", - "expanded_view": "Részletes nézet", - "expanded_view_description": "Szűrőszabályok megjelenítése részletes feltétel- és műveletblokkokkal", - "if": "Ha", - "then": "Akkor", - "match_all_conditions": "minden feltétel teljesül", - "match_any_condition": "bármelyik feltétel teljesül", - "and": "és", - "or": "vagy", - "cancel": "Mégse", - "confirm_delete": "Törlés", - "rule_list": "Szűrőszabályok", - "drag_to_reorder": "Húzd át a sorrend módosításához", - "match_type": "Egyezés típusa", - "reset_to_visual": "Visszaállítás vizuális szerkesztőre", - "reset_warning": "Ez elveti az aktuális szkriptet és újat kezd.", - "confirm_reset": "Visszaállítás", - "validation_empty_name": "A szabály neve kötelező", - "validation_empty_conditions": "Legalább egy feltétel szükséges értékkel", - "validation_empty_actions": "Legalább egy művelet szükséges", - "templates_section": "Kezdés sablonból", - "template_newsletters": "Hírlevelek áthelyezése mappába", - "template_receipts": "Nyugták automatikus archiválása", - "template_important": "Fontos e-mailek megjelölése", - "template_notifications": "Értesítések szűrése", - "sieve_editor": { - "title": "Sieve szkript szerkesztő", - "warning": "A nyers Sieve szkript szerkesztése megszakíthatja a vizuális szabályszerkesztést. Az itt végzett módosítások felülírják a vizuális szerkesztőt.", - "script_content": "Sieve szkript", - "valid": "A szkript érvényes", - "invalid": "A szkript hibákat tartalmaz", - "save_warning": "A mentés felülír minden vizuális szabályt. Ez nem vonható vissza. Kattints újra a Mentés gombra a megerősítéshez.", - "validating": "Ellenőrzés...", - "validate": "Ellenőrzés", - "cancel": "Mégse", - "save": "Mentés", - "confirm_save": "Mentés megerősítése", - "validation_failed": "Ellenőrzési kérés sikertelen" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# feltétel} other {# feltétel}}", - "actions_count": "{count, plural, one {# művelet} other {# művelet}}" - }, - "origin_external": "Külső", - "managed_by_tooltip": "Kezeli: {source}. Szerkeszd az alkalmazásban, vagy használd a nyers Sieve szerkesztőt." - }, - "templates": { - "title": "E-mail sablonok", - "description": "Újrafelhasználható e-mail sablonok létrehozása helyettesítő változókkal", - "add": "Új sablon", - "edit": "Sablon szerkesztése", - "name": "Sablon neve", - "name_placeholder": "pl. Követő e-mail", - "category": "Kategória", - "category_placeholder": "pl. Munka, Személyes", - "subject": "Tárgy", - "subject_placeholder": "E-mail tárgysora", - "body": "Törzs", - "body_placeholder": "E-mail tartalma...", - "recipients_placeholder": "email@example.com", - "identity": "Küldés mint", - "default_identity": "Alapértelmezett azonosság", - "favorite": "Kedvenc", - "cancel": "Mégse", - "create": "Létrehozás", - "update": "Frissítés", - "confirm_delete": "Törlés", - "no_templates": "Még nincsenek sablonok", - "manage": "Sablonok kezelése", - "count": "{count, plural, one {# sablon} other {# sablon}}", - "export_import": "Exportálás és importálás", - "export_import_description": "Sablonok biztonsági mentése vagy áthelyezése másik eszközre", - "export": "Exportálás", - "import": "Importálás", - "validation": { - "empty": "A sablon neve kötelező", - "too_long": "A sablon neve legfeljebb 200 karakter lehet" - } - }, - "files": { - "display": { - "title": "Megjelenítés", - "description": "Fájlok és mappák megjelenésének beállítása" - }, - "folder_layout": { - "label": "Mappa navigáció", - "description": "Mappák megjelenítése: fájlokkal együtt vagy oldalsáv fában", - "inline": "Beágyazva", - "sidebar": "Oldalsáv" - }, - "default_view": { - "label": "Alapértelmezett nézet", - "description": "Válassz rács vagy lista elrendezés között", - "list": "Lista", - "grid": "Rács" - }, - "default_sort": { - "label": "Alapértelmezett rendezés", - "description": "A fájlok alapértelmezett rendezésének kiválasztása", - "name": "Név", - "size": "Méret", - "modified": "Módosítás dátuma" - }, - "sort_direction": { - "label": "Rendezés iránya", - "description": "Növekvő vagy csökkenő sorrend kiválasztása", - "ascending": "Növekvő", - "descending": "Csökkenő" - }, - "icons": { - "title": "Ikonok", - "description": "Fájlikonok megjelenésének beállítása" - }, - "show_icons": { - "label": "Fájlikonok mutatása", - "description": "Ikonok megjelenítése a fájlok és mappák mellett" - }, - "colored_icons": { - "label": "Színes ikonok", - "description": "Színes ikonok használata egyszínű helyett" - }, - "show_thumbnails": { - "label": "Miniatűrök mutatása", - "description": "Kép előnézetek megjelenítése ikonok helyett a képfájlokhoz" - }, - "behavior": { - "title": "Viselkedés", - "description": "Fájlböngésző viselkedésének beállítása" - }, - "show_hidden": { - "label": "Rejtett fájlok mutatása", - "description": "Ponttal kezdődő fájlok és mappák megjelenítése" - }, - "preview": { - "label": "Előnézet" - } - }, - "scoped": { - "back": "Vissza a saját fiókomhoz", - "managing": "Kezelés: {name}" - } - }, - "errors": { - "page_error_title": "Valami hiba történt", - "page_error_description": "Váratlan hibával találkoztunk. Kérjük, próbáld újra, vagy térj vissza a kezdőoldalra.", - "sidebar_error": "Nem sikerült betölteni a postafiókokat", - "email_list_error": "Nem sikerült betölteni az e-maileket", - "viewer_error_title": "Nem sikerült megjeleníteni az e-mailt", - "viewer_error_description": "Probléma merült fel az e-mail megjelenítésekor. Lehet, hogy nem támogatott tartalmat tartalmaz.", - "composer_error": "Nem sikerült betölteni a szerkesztőt", - "settings_error_title": "Beállítások nem elérhetők", - "settings_error_description": "Nem sikerült betölteni a beállításokat. A preferenciáid nem biztos, hogy elmentődnek.", - "try_again": "Próbáld újra", - "reload": "Újratöltés", - "reload_emails": "E-mailek újratöltése", - "reload_settings": "Beállítások újratöltése", - "retry": "Újra", - "go_home": "Ugrás a beérkezőhöz" - }, - "context_menu": { - "reply": "Válasz", - "reply_all": "Válasz mindenkinek", - "forward": "Továbbítás", - "mark_read": "Olvasottnak jelölés", - "mark_unread": "Olvasatlannak jelölés", - "star": "Csillagozás", - "unstar": "Csillagozás megszüntetése", - "pin": "Rögzítés", - "unpin": "Rögzítés feloldása", - "move_to": "Áthelyezés ide...", - "archive": "Archiválás", - "delete": "Törlés", - "mark_as_spam": "Spam jelentése", - "not_spam": "Nem spam", - "tag": "Címke", - "items_selected": "{count} e-mail kijelölve", - "edit_draft": "Piszkozat szerkesztése", - "cancel_scheduled_send": "Küldés megszakítása", - "reschedule_send": "Átütemezés", - "cancel_and_edit": "Mégse és szerkesztés", - "cancel_and_compose_again": "Mégse és újraírás" - }, - "mailbox_context_menu": { - "mark_folder_read": "Mappa olvasottnak jelölése", - "mark_folder_tree_read": "Mappa és almappák olvasottnak jelölése", - "mark_all_folders_read": "Összes mappa olvasottnak jelölése", - "new_subfolder": "Új almappa...", - "new_folder": "Új mappa...", - "rename": "Átnevezés...", - "share": "Megosztás...", - "import_email": "Importálás .eml vagy .zip fájlból...", - "empty_folder": "Mappa ürítése", - "empty_folder_generic": "Mappa ürítése", - "delete_folder": "Mappa törlése", - "refresh": "Frissítés", - "mark_all_confirm_title": "Összes mappa olvasottnak jelölése", - "mark_all_confirm_message": "Minden olvasatlan üzenetet olvasottnak jelölsz a személyes fiókodban?", - "delete_confirm_title": "Mappa törlése", - "delete_confirm_message": "Véglegesen törlöd a \"{name}\" mappát? Ez a művelet nem vonható vissza.", - "prompt_new_subfolder": "Add meg az új almappa nevét.", - "prompt_new_folder": "Add meg az új mappa nevét.", - "prompt_rename": "Add meg a mappa új nevét.", - "placeholder_folder_name": "Mappa neve", - "create": "Létrehozás", - "rename_confirm": "Átnevezés", - "toast_marked_read": "Mappa olvasottnak jelölve", - "toast_marked_read_count": "{count, plural, one {1 üzenet} other {# üzenet}} olvasottnak jelölve", - "toast_already_read": "Nincs olvasatlan üzenet", - "toast_marked_all_read": "Összes mappa olvasottnak jelölve", - "toast_emptied": "Mappa ürítve", - "toast_folder_created": "Mappa létrehozva", - "toast_folder_renamed": "Mappa átnevezve", - "toast_folder_deleted": "Mappa törölve", - "toast_error_mark_read": "Nem sikerült olvasottnak jelölni", - "toast_error_empty": "Nem sikerült üríteni a mappát", - "toast_error_create": "Nem sikerült létrehozni a mappát", - "toast_error_rename": "Nem sikerült átnevezni a mappát", - "toast_error_delete": "Nem sikerült törölni a mappát", - "toast_error_delete_has_children": "A mappának almappái vannak. Távolítsd el azokat először.", - "toast_error_delete_has_email": "A mappa nem üres. Ürítsd ki először.", - "delete_with_contents_title": "A mappa nem üres", - "delete_with_contents_message": "A(z) „{name}” mappa még {count, plural, one {1 üzenetet} other {# üzenetet}} tartalmaz. Törlöd a mappát az összes üzenetével együtt? Ez nem vonható vissza.", - "delete_with_contents_confirm": "Mappa és üzenetek törlése" - }, - "shortcuts": { - "title": "Billentyűparancsok", - "tip": "Nyomd meg a ? gombot bármikor a súgó megjelenítéséhez", - "sections": { - "navigation": "Navigáció", - "actions": "E-mail műveletek", - "global": "Globális", - "threads": "Beszélgetések", - "composer": "Szerkesztő" - }, - "navigation": { - "next_email": "Következő e-mail", - "previous_email": "Előző e-mail", - "open_email": "E-mail megnyitása", - "close_email": "Bezárás / Kijelölés megszüntetése" - }, - "actions": { - "reply": "Válasz", - "reply_all": "Válasz mindenkinek", - "forward": "Továbbítás", - "star": "Csillagozás váltása", - "archive": "Archiválás", - "delete": "Törlés", - "mark_unread": "Olvasatlannak jelölés", - "mark_read": "Olvasottnak jelölés", - "toggle_spam": "Spam / Nem spam" - }, - "global": { - "compose": "Új e-mail írása", - "search": "Keresés fókusz", - "help": "Parancsok mutatása", - "refresh": "E-mailek frissítése", - "select_all": "Összes kijelölése", - "global_search": "Keresés mindenben" - }, - "threads": { - "expand_collapse": "Beszélgetés kibontása/összecsukása" - }, - "composer": { - "send": "E-mail küldése", - "schedule_send": "Késleltetett küldés", - "template_picker": "Sablonválasztó megnyitása" - } - }, - "threads": { - "messages_one": "{count} üzenet", - "messages_other": "{count} üzenet", - "messages_tooltip": "{count, plural, one {# üzenet ebben a beszélgetésben} other {# üzenet ebben a beszélgetésben}}", - "expand": "Beszélgetés kibontása", - "collapse": "Beszélgetés összecsukása", - "loading": "Beszélgetés betöltése...", - "mark_read": "Beszélgetés olvasottnak jelölése", - "mark_unread": "Beszélgetés olvasatlannak jelölése", - "archive": "Beszélgetés archiválása", - "delete": "Beszélgetés törlése", - "star": "Beszélgetés csillagozása", - "unstar": "Beszélgetés csillagozásának megszüntetése", - "toggle_thread": "Beszélgetés váltása" - }, - "identities": { - "modal_title": "Küldő azonosságok kezelése", - "create_new": "Új azonosság létrehozása", - "edit_identity": "Azonosság szerkesztése", - "delete_confirm": "Törlöd ezt az azonosságot? Ez nem vonható vissza.", - "cannot_delete": "Ez az azonosság nem törölhető", - "primary_identity": "Elsődleges", - "set_as_primary": "Beállítás elsődlegesként", - "no_identities": "Nincs azonosság", - "display": { - "reply_to": "Válaszcím:", - "bcc": "Titkos másolat:", - "signature": "Aláírás:", - "preview": "Előnézet:" - }, - "validation_errors": { - "invalid_emails": "Érvénytelen e-mailek: {emails}", - "unknown_error": "Ismeretlen hiba" - }, - "form": { - "name_label": "Megjelenített név", - "name_placeholder": "pl. Munkahelyi e-mail, Személyes", - "name_required": "Név megadása kötelező", - "email_label": "E-mail cím", - "email_placeholder": "your.email@example.com", - "email_required": "E-mail megadása kötelező", - "email_invalid": "Kérjük, adj meg egy érvényes e-mail címet", - "email_immutable": "Az e-mail cím létrehozás után nem módosítható", - "reply_to_label": "Válaszcím (opcionális)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "Automatikus titkos másolat (opcionális)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "Szöveges aláírás", - "html_signature_label": "HTML aláírás", - "signature_byte_counter": "{bytes} / {max} bájt", - "signature_byte_limit_reached": "Szerverkorlát elérve", - "save": "Azonosság mentése", - "cancel": "Mégse", - "creating": "Létrehozás...", - "updating": "Frissítés..." - }, - "sub_address": { - "button_tooltip": "Alcím használata", - "popover_title": "Alcím címke hozzáadása", - "tag_input_placeholder": "Címke megadása (pl. vasarlas)", - "preview_label": "Előnézet:", - "recent_tags": "Legutóbbi címkék", - "suggested_tags": "Javasolt", - "use_address": "Cím használata", - "invalid_tag": "A címke csak betűket, számokat és kötőjeleket tartalmazhat", - "tag_too_long": "A címke legfeljebb 30 karakter lehet", - "help_text": "A felhasználó{elválasztó}cimke@domain.com címre küldött e-mailek a beérkeződbe érkeznek", - "validation": { - "empty": "A címke nem lehet üres", - "too_long": "A címke legfeljebb {max} karakter lehet", - "invalid_chars": "A címke csak betűket, számokat és kötőjeleket tartalmazhat" - } - }, - "badge": { - "sent_via": "ezen keresztül", - "sub_address_tag": "Küldve alcím használatával: {tag}", - "identity_name": "Küldve azonossággal: {name}", - "identity_short": "{name} néven", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Törlés", - "delete_confirm_title": "Azonosság törlése" - }, - "templates": { - "picker_title": "Sablon kiválasztása", - "search_placeholder": "Sablonok keresése...", - "section_favorites": "Kedvencek", - "section_recent": "Legutóbbi", - "section_uncategorized": "Egyéb", - "no_templates": "Még nincsenek sablonok", - "no_results": "Nincs találat", - "fill_placeholders": "Helyettesítő értékek kitöltése", - "enter_value": "Érték megadása...", - "preview": "Előnézet", - "insert_with_values": "Beszúrás értékekkel", - "insert_raw": "Nyers beszúrás", - "copy_suffix": "(másolat)", - "placeholder": "Változó", - "placeholders": { - "recipient_name": "Címzett neve", - "company": "Cégnév", - "date": "Aktuális dátum", - "day_of_week": "A hét napja", - "sender_name": "A te neved" - } - }, - "contacts": { - "title": "Névjegyek", - "search_placeholder": "Névjegyek keresése...", - "create_new": "Új névjegy", - "no_category": "Nincs kategória", - "rename_category": "Kategória átnevezése", - "category_name_label": "Kategória neve", - "category_renamed": "Kategória átnevezve", - "category_rename_failed": "Nem sikerült átnevezni a kategóriát", - "category_added": "Névjegy hozzáadva ide: {name}", - "category_added_plural": "{count} névjegy hozzáadva ide: {name}", - "empty_state": "Még nincsenek névjegyek", - "empty_state_title": "Még nincsenek névjegyek", - "empty_state_subtitle": "Hozd létre az első névjegyed vagy importálj vCard fájlból", - "empty_search": "Nincs a keresésnek megfelelő névjegy", - "empty_search_hint": "Próbálj meg más keresési kifejezést", - "empty_filtered": "Nincs a szűrőknek megfelelő névjegy", - "empty_filtered_hint": "Próbáld módosítani vagy törölni a szűrőket", - "clear_search": "Keresés törlése", - "import_vcard": "vCard importálása", - "delete_confirm_title": "Névjegy törlése", - "delete_confirm": "Biztosan törölni szeretnéd ezt a névjegyet?", - "local_mode": "A névjegyek helyben tárolódnak (a szerver nem támogatja a JMAP Névjegyeket)", - "back_to_contacts": "Vissza a névjegyekhez", - "back_to_email": "Vissza az e-mailekhez", - "open_categories": "Kategóriák megnyitása", - "tabs": { - "all": "Összes", - "groups": "Csoportok" - }, - "shared": { - "title": "Megosztott" - }, - "address_books": { - "create": "Új címjegyzék", - "created": "Címjegyzék létrehozva", - "create_failed": "Nem sikerült létrehozni a címjegyzéket", - "title": "Címjegyzékeim", - "shared_prefix": "Megosztott: {name}", - "moved": "Névjegy áthelyezve ide: {name}", - "moved_plural": "{count} névjegy áthelyezve ide: {name}", - "move_failed": "Nem sikerült áthelyezni a névjegyet", - "address_book": "Címjegyzék", - "rename": "Címjegyzék átnevezése", - "name_label": "Címjegyzék neve", - "renamed": "Címjegyzék átnevezve", - "rename_failed": "Nem sikerült átnevezni a címjegyzéket", - "default": "Alapértelmezett", - "set_default": "Beállítás alapértelmezettként", - "default_updated": "Alapértelmezett címjegyzék frissítve", - "set_default_failed": "Nem sikerült beállítani az alapértelmezett címjegyzéket", - "manage": "Címjegyzékek kezelése", - "share": "Címjegyzék megosztása", - "new_contact_in_book": "Új névjegy ebben a címjegyzékben", - "delete": "Címjegyzék törlése", - "confirm_delete": "Törlöd a \"{name}\" címjegyzéket? Az összes névjegy eltávolításra kerül.", - "deleted": "Címjegyzék törölve", - "delete_failed": "Nem sikerült törölni a címjegyzéket" - }, - "detail": { - "emails": "E-mail címek", - "phones": "Telefonszámok", - "organizations": "Szervezetek", - "addresses": "Címek", - "notes": "Megjegyzések", - "titles": "Címek és szerepkörök", - "online_services": "Online szolgáltatások", - "anniversaries": "Évfordulók", - "personal_info": "Személyes adatok", - "languages": "Nyelvek", - "categories": "Kategóriák", - "related_contacts": "Kapcsolódó névjegyek", - "crypto_keys": "Kriptográfiai kulcsok", - "cert_issuer": "Kibocsátó", - "cert_expires": "Lejárat", - "cert_expired": "Lejárt", - "cert_fingerprint": "Ujjlenyomat", - "cert_algorithm": "Algoritmus", - "import_to_smime": "Importálás S/MIME-be", - "cert_already_imported": "Már importálva S/MIME-be", - "cert_imported": "Tanúsítvány importálva az S/MIME tárolóba", - "cert_import_failed": "Nem sikerült importálni a tanúsítványt", - "no_contact_selected": "Válassz ki egy névjegyet a részletek megtekintéséhez", - "compose_email": "E-mail írása", - "copy_email": "E-mail másolása", - "copy_phone": "Telefonszám másolása", - "copy_url": "URL másolása", - "copied": "Vágólapra másolva", - "copy_failed": "Nem sikerült a vágólapra másolni", - "created": "Létrehozva", - "updated": "Utolsó frissítés", - "timezone": "Időzóna", - "anniversary_birth": "Születésnap", - "anniversary_death": "Elhunyt", - "anniversary_wedding": "Házassági évforduló", - "anniversary_other": "Egyéb", - "personal_expertise": "Szakértelem", - "personal_hobby": "Hobbi", - "personal_interest": "Érdeklődés", - "personal_other": "Egyéb", - "gender": "Nem", - "gender_masculine": "Férfi", - "gender_feminine": "Nő", - "gender_other": "Egyéb", - "gender_none": "Nem alkalmazható", - "gender_unknown": "Ismeretlen", - "calendar": "Naptár", - "calendar_uri": "Naptár URL", - "scheduling_uri": "Ütemezési URL", - "freebusy_uri": "Szabad/elfoglalt URL", - "section_contact": "Névjegy adatok", - "section_work": "Munka", - "section_personal": "Személyes", - "email_default_label": "E-mail", - "phone_default_label": "Telefon", - "address_default_label": "Cím", - "online_service_default_label": "Online", - "organization_label": "Szervezet", - "title_label": "Cím", - "role_label": "Szerepkör", - "language_label": "Nyelv", - "related_default_label": "Kapcsolódó", - "more_actions": "További műveletek", - "age_years": "{count, plural, one {1 éves} other {# éves}}", - "years_since": "{count, plural, one {1 év} other {# év}}" - }, - "activity": { - "recent_emails": "Legutóbbi e-mailek", - "upcoming_events": "Közelgő események", - "no_emails": "Nincsenek legutóbbi e-mailek", - "no_events": "Nincsenek közelgő események", - "no_subject": "(Nincs tárgy)", - "no_title": "(Nincs cím)", - "load_failed": "Nem sikerült betölteni", - "unknown_sender": "Ismeretlen feladó", - "all_day": "Egész napos" - }, - "form": { - "create_title": "Új névjegy", - "edit_title": "Névjegy szerkesztése", - "section_address_book": "Címtár", - "select_address_book": "Címtár kiválasztása...", - "section_identity": "Név és azonosság", - "contact_type": "Névjegy típusa", - "type_person": "Személy", - "type_organization": "Szervezet", - "section_work": "Munka és szervezet", - "prefix": "Előtag", - "prefix_placeholder": "Dr., Úr., Mrs.", - "given_name": "Keresztnév", - "middle_name": "Középső név", - "surname": "Vezetéknév", - "suffix": "Utótag", - "suffix_placeholder": "Ifj., Sr., III", - "nickname": "Becenév", - "nickname_placeholder": "Becenév", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Telefon", - "phone_placeholder": "+36 20 123 4567", - "phone_type": "Típus", - "phone_voice": "Vonalas", - "phone_cell": "Mobil", - "phone_fax": "Fax", - "phone_pager": "Személyhívó", - "phone_video": "Videó", - "phone_text": "SMS", - "organization": "Szervezet", - "organization_placeholder": "Cégnév", - "department": "Részleg", - "department_placeholder": "Részleg", - "job_title": "Beosztás", - "job_title_placeholder": "pl. Szoftvermérnök", - "role": "Szerepkör", - "role_placeholder": "pl. Csapatvezető", - "addresses": "Címek", - "add_address": "Cím hozzáadása", - "street": "Utca", - "city": "Város", - "region": "Megye / Régió", - "postcode": "Irányítószám", - "country": "Ország", - "online_services": "Online szolgáltatások", - "add_online_service": "Online szolgáltatás hozzáadása", - "url_placeholder": "https://...", - "service_placeholder": "Szolgáltatás", - "anniversaries": "Évfordulók", - "add_anniversary": "Dátum hozzáadása", - "anniversary_birth": "Születésnap", - "anniversary_wedding": "Házassági évforduló", - "anniversary_death": "Megemlékezés", - "anniversary_other": "Egyéb", - "personal_info": "Személyes adatok", - "add_personal_info": "Bejegyzés hozzáadása", - "personal_info_placeholder": "pl. Fotózás", - "personal_expertise": "Szakértelem", - "personal_hobby": "Hobbi", - "personal_interest": "Érdeklődés", - "personal_other": "Egyéb", - "level": "Szint", - "level_high": "Magas", - "level_medium": "Közepes", - "level_low": "Alacsony", - "categories": "Kategóriak", - "categories_placeholder": "pl. Család, Barátok, Kollégák", - "categories_hint": "Írj a kereséshez vagy kategóriák hozzáadásához", - "category_add": "Hozzáadás", - "note": "Megjegyzések", - "note_placeholder": "Megjegyzés hozzáadása...", - "gender": "Nem", - "gender_sex": "Nem", - "gender_male": "Férfi", - "gender_female": "Nő", - "gender_other": "Egyéb", - "gender_none": "Nem alkalmazható", - "gender_unknown": "Ismeretlen", - "gender_identity": "Nem identitás", - "gender_identity_placeholder": "Nem identitás...", - "calendar": "Naptár", - "calendar_uri": "Naptár URL", - "scheduling_uri": "Ütemezési URL", - "freebusy_uri": "Szabad/elfoglalt URL", - "context_work": "Munka", - "context_private": "Személyes", - "add_email": "E-mail hozzáadása", - "add_phone": "Telefon hozzáadása", - "save": "Mentés", - "cancel": "Mégse", - "creating": "Létrehozás...", - "updating": "Frissítés...", - "name_required": "Adjon meg egy keresztnevet, vezetéknevet vagy szervezetet", - "email_invalid": "Kérjük, adj meg egy érvényes e-mail címet", - "email_error_inline": "Érvénytelen e-mail formátum", - "save_failed": "Nem sikerült menteni a névjegyet", - "delete": "Törlés", - "upload_photo": "Fotó feltöltése", - "remove_photo": "Fotó eltávolítása", - "photo_hint": "JPG vagy PNG, max 10 MB. Átméretezésre kerül.", - "photo_too_large": "A kép túl nagy (max 10 MB)", - "photo_invalid": "Érvénytelen képfájl", - "change_photo": "Módosítás" - }, - "groups": { - "create": "Új csoport", - "edit": "Csoport szerkesztése", - "empty": "Még nincsenek csoportok", - "delete_confirm_title": "Csoport törlése", - "delete_confirm": "Biztosan törölni szeretnéd ezt a csoportot?", - "name_label": "Csoport neve", - "name_placeholder": "pl. Csapat, Család", - "name_required": "A csoport neve kötelező", - "save_failed": "Nem sikerült menteni a csoportot", - "members_label": "Tagok", - "search_members": "Névjegyek keresése a hozzáadáshoz...", - "no_members": "Nincs tag ebben a csoportban", - "member_count": "{count, plural, =0 {Nincs tag} one {1 tag} other {# tag}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Névjegyek importálása", - "drop_hint": "Kattints a vCard fájl kiválasztásához", - "file_types": ".vcf vagy .vcard fájlok", - "no_contacts": "Nincs névjegy a fájlban", - "parse_error": "Nem sikerült feldolgozni a vCard fájlt", - "found": "{count, plural, one {1 névjegy található} other {# névjegy található}}", - "duplicate": "Duplikált", - "select_all": "Összes kijelölése", - "deselect_all": "Összes kijelölés megszüntetése", - "selected": "{count, plural, one {1 kijelölve} other {# kijelölve}}", - "import_button": "Importálás", - "importing": "Importálás...", - "success": "{count, plural, one {1 névjegy importálva} other {# névjegy importálva}}", - "failed": "Importálás sikertelen", - "close": "Bezárás", - "file_too_large": "A fájl túl nagy (max 5 MB)" - }, - "export": { - "title": "Névjegyek exportálása", - "success": "{count, plural, one {1 névjegy exportálva} other {# névjegy exportálva}}" - }, - "bulk": { - "selected": "{count, plural, one {1 kijelölve} other {# kijelölve}}", - "select_all": "Összes kijelölése", - "delete": "Törlés", - "delete_confirm_title": "Névjegyek törlése", - "delete_confirm": "Törlöd {count, plural, one {az 1 névjegyet} other {a # névjegyet}}?", - "deleted": "{count, plural, one {1 névjegy törölve} other {# névjegy törölve}}", - "add_to_group": "Hozzáadás a csoporthoz", - "choose_group": "Csoport kiválasztása", - "adding_contacts": "{count, plural, one {1 névjegy} other {# névjegy}} hozzáadása", - "added_to_group": "Névjegyek hozzáadva a csoporthoz", - "export": "Exportálás", - "clear": "Kijelölés megszüntetése" - }, - "toast": { - "created": "Névjegy létrehozva", - "updated": "Névjegy frissítve", - "deleted": "Névjegy törölve", - "error_create": "Nem sikerült létrehozni a névjegyet", - "error_update": "Nem sikerült frissíteni a névjegyet", - "error_delete": "Nem sikerült törölni a névjegyet" - }, - "context_menu": { - "open": "Megnyitás", - "edit": "Szerkesztés", - "send_email": "E-mail küldése", - "add_to_group": "Hozzáadás a csoporthoz", - "export_vcard": "Exportálás vCard-ként", - "delete": "Törlés", - "call": "Hívás", - "duplicate": "Duplikálás", - "print": "Nyomtatás" - }, - "filters": { - "toggle": "Szűrők", - "select": "Kiválasztás", - "clear": "Törlés", - "close": "Bezárás", - "title": "Speciális szűrők", - "organization": "Cég", - "organization_placeholder": "pl. Acme Corp", - "job_title": "Beosztás", - "job_title_placeholder": "pl. Tervező", - "location": "Helyszín", - "location_placeholder": "Város vagy ország", - "email_domain": "E-mail tartomány", - "email_domain_placeholder": "example.com", - "birthday_month": "Születésnap hónapja", - "any_month": "Bármely hónap", - "has_email": "Van e-mail", - "has_phone": "Van telefon", - "has_photo": "Van fotó", - "sort_by": "Rendezés", - "sort_first_name": "Keresztnév", - "sort_last_name": "Vezetéknév" - } - }, - "calendar": { - "title": "Naptár", - "back_to_email": "Vissza az e-mailekhez", - "back_to_month": "Vissza a hónaphoz", - "my_calendars": "Naptárak", - "birthday_calendar": "Születésnapok", - "mini_calendar_change": "Kattints a hónap váltásához", - "views": { - "month": "Hónap", - "week": "Hét", - "day": "Nap", - "agenda": "Napirend", - "today": "Ma", - "month_hint": "Hónap (m)", - "week_hint": "Hét (w)", - "day_hint": "Nap (d)", - "agenda_hint": "Napirend (a)", - "tasks": "Feladatok", - "tasks_hint": "Feladatok (k)" - }, - "events": { - "create": "Esemény létrehozása", - "edit": "Esemény szerkesztése", - "delete": "Esemény törlése", - "details": "Esemény részletei", - "no_events": "Nincs esemény", - "all_day": "Egész napos", - "more": "+{count} további", - "no_title": "(Nincs cím)", - "resize": "Esemény átméretezése", - "duplicate": "Duplikálás", - "today_header": "Ma", - "tomorrow_header": "Holnap", - "agenda_show_earlier": "Korábbi események megjelenítése", - "agenda_loading": "Betöltés…", - "agenda_range_start": "Események ettől: {date}", - "agenda_range_end": "Események eddig: {date}", - "export_ics": "Exportálás .ics-ként", - "copy_title": "Cím másolása", - "copy_link": "Találkozó link másolása", - "new_event": "Új esemény", - "new_all_day_event": "Új egész napos esemény", - "new_task": "Új feladat", - "go_to_today": "Ugrás a mai napra" - }, - "detail": { - "add_note": "Megjegyzés hozzáadása...", - "save_note": "Mentés", - "note_saved": "Megjegyzés hozzáadva", - "open_link": "Link megnyitása", - "meeting_link": "Találkozó link", - "tentative": "Előzetes", - "cancelled": "Lemondva", - "delete_confirm": "Törlöd ezt az eseményt?" - }, - "form": { - "title": "Cím", - "description": "Leírás", - "location": "Helyszín", - "meeting_link": "Találkozó link", - "start_date": "Kezdő dátum", - "end_date": "Befejező dátum", - "start_time": "Kezdő idő", - "end_time": "Befejező idő", - "all_day_event": "Egész napos esemény", - "calendar_select": "Naptár", - "save": "Mentés", - "cancel": "Mégse", - "delete_confirm": "Biztosan törölni szeretnéd ezt az eseményt?", - "color": "Szín" - }, - "participants": { - "availability": { - "title": "Elérhetőség", - "free": "Szabad", - "busy": "Foglalt", - "tentative": "Feltételes", - "unknown": "Nem felhasználó ezen a kiszolgálón", - "checking": "Ellenőrzés…" - }, - "title": "Résztvevők", - "add": "Résztvevő hozzáadása", - "organizer": "Szervező", - "attendee": "Résztvevő", - "accepted": "Elfogadva", - "declined": "Elutasítva", - "tentative": "Előzetes", - "needs_action": "Művelet szükséges", - "remove": "Eltávolítás", - "edit": "Szerkesztés", - "email_placeholder": "E-mail cím hozzáadása vagy névjegy keresése", - "send_invitations": "Meghívók küldése a résztvevőknek", - "status_summary": "{accepted} elfogadta, {pending} függőben", - "invited_by": "Meghívó tőle: {name}", - "respond_below": "Válaszolj az alábbi gombok segítségével", - "rsvp_label": "Válaszod", - "cancel_notification": "A résztvevők értesítést kapnak a lemondásról", - "you_organizer": "Te vagy a szervező", - "you_attendee": "Résztvevő vagy", - "no_participants": "Nincs résztvevő", - "count": "{count, plural, one {# résztvevő} other {# résztvevő}}" - }, - "recurrence": { - "title": "Ismétlődés", - "none": "Nem ismétlődik", - "daily": "Naponta", - "weekly": "Hetente", - "monthly": "Havonta", - "yearly": "Évente", - "every_n_days": "Minden {count} nap", - "every_n_weeks": "Minden {count} hét", - "every_n_months": "Minden {count} hónap", - "until": "Amíg", - "occurrences": "{count} alkalom", - "custom": "Egyéni…", - "edit_custom": "Egyéni ismétlődés szerkesztése", - "every_n_years": "Minden {count}. évben", - "on_days": "ezeken a napokon: {days}", - "on_day_n": "a hónap {day}. napján", - "on_the_nth": "a(z) {nth} {day}", - "in_month": "{month} hónapban", - "nth_1": "első", - "nth_2": "második", - "nth_3": "harmadik", - "nth_4": "negyedik", - "nth_last": "utolsó", - "editor_freq_day": "Nap", - "editor_freq_week": "Hét", - "editor_freq_month": "Hónap", - "editor_freq_year": "Év", - "editor_repeats_on": "Ismétlődés", - "editor_every": "Minden", - "editor_unit_days": "nap", - "editor_unit_weeks": "hét", - "editor_unit_months": "hónap", - "editor_unit_years": "év", - "editor_on_day": "a hónap napján:", - "editor_on_the": "a(z)", - "editor_in": "hónap:", - "editor_ends": "Vége", - "editor_never": "Soha", - "editor_ends_on": "Ekkor", - "editor_ends_after": "Ennyi után:", - "editor_occurrences": "alkalom" - }, - "recurrence_scope": { - "edit_title": "Ismétlődő esemény szerkesztése", - "delete_title": "Ismétlődő esemény törlése", - "description": "Ez egy ismétlődő esemény. Mely eseményeket szeretnéd módosítani?", - "this_event": "Csak ez az esemény", - "this_and_future": "Ez és a következő események", - "all_events": "Minden esemény", - "cancel": "Mégse", - "save": "Mentés", - "delete": "Törlés", - "rsvp_title": "Válasz ismétlődő eseményre", - "rsvp_description": "Ez egy ismétlődő esemény. Mely eseményekre vonatkozzon a válaszod?", - "respond": "Válasz" - }, - "alerts": { - "title": "Emlékeztetők", - "none": "Nincs emlékeztető", - "at_time": "Az esemény időpontjában", - "minutes_before": "{count, plural, one {# perccel előtte} other {# perccel előtte}}", - "hours_before": "{count, plural, one {# órával előtte} other {# órával előtte}}", - "days_before": "{count, plural, one {# nappal előtte} other {# nappal előtte}}", - "weeks_before": "{count, plural, one {# héttel előtte} other {# héttel előtte}}", - "unit_minutes_before": "perccel előtte", - "unit_hours_before": "órával előtte", - "unit_days_before": "nappal előtte", - "unit_weeks_before": "héttel előtte", - "add": "Emlékeztető hozzáadása", - "remove": "Emlékeztető eltávolítása", - "amount": "Emlékeztető mennyisége", - "unit": "Emlékeztető egysége" - }, - "settings": { - "title": "Naptár beállítások", - "default_view": "Alapértelmezett nézet", - "week_starts_on": "Hét kezdőnapja", - "time_format": "Idő formátum", - "default_calendar": "Alapértelmezett naptár", - "default_reminder": "Alapértelmezett emlékeztető", - "time_format_12h": "12 órás", - "time_format_24h": "24 órás", - "notifications_enabled": "Esemény értesítések", - "notifications_enabled_desc": "Figyelmeztetések megjelenítése a közelgő naptári eseményekhez", - "notification_sound": "Értesítési hang", - "notification_sound_desc": "Hang lejátszása naptári figyelmeztetésekhez", - "invitation_parsing": "E-mail meghívók feldolgozása", - "invitation_parsing_desc": "Naptármeghívók észlelése az e-mail csatolmányokban és naptár műveletek megjelenítése", - "show_time_in_month_view": "Idő mutatása a hónap nézetben", - "show_time_in_month_view_desc": "Események időpontjának mutatása a havi naptár nézetben", - "show_week_numbers": "Hétszámok mutatása", - "show_week_numbers_desc": "Hétszámok mutatása a mini naptárban", - "calendar_free_scroll": "Szabad görgetés", - "calendar_free_scroll_desc": "Folyamatos görgetés hónapok, hetek és napok között egyetlen időszak helyett", - "enable_tasks": "Feladatok engedélyezése", - "enable_tasks_desc": "Feladatnézet mutatása a naptárban a teendők kezeléséhez", - "show_tasks_on_calendar": "Feladatok mutatása a naptárban", - "show_tasks_on_calendar_desc": "Feladat chip-ek mutatása a nap és hét naptár nézetekben", - "hover_preview": "Esemény előnézet egérrel", - "hover_preview_desc": "Részletes felugró ablak mutatása az események fölé húzva", - "hover_preview_instant": "Azonnali", - "hover_preview_delay_500ms": "0,5 másodperc késleltetés", - "hover_preview_delay_1s": "1 másodperc késleltetés", - "hover_preview_delay_2s": "2 másodperc késleltetés", - "hover_preview_off": "Letiltva", - "organizer_identity": "Meghívók szervezése mint", - "organizer_identity_desc": "A szervezőként használt naptárcím, amikor embereket hívsz meg eseményekre.", - "organizer_identity_failed": "Nem sikerült módosítani a szervező címét", - "show_birthday_calendar": "Névjegy születésnap naptár", - "show_birthday_calendar_desc": "Virtuális naptár mutatása a névjegyeid születésnapjaival" - }, - "days": { - "monday": "Hétfő", - "tuesday": "Kedd", - "wednesday": "Szerda", - "thursday": "Csütörtök", - "friday": "Péntek", - "saturday": "Szombat", - "sunday": "Vasárnap", - "mon": "H", - "tue": "K", - "wed": "Sze", - "thu": "Cs", - "fri": "P", - "sat": "Szo", - "sun": "V" - }, - "months": { - "jan": "Jan", - "feb": "Feb", - "mar": "Már", - "apr": "Ápr", - "may": "Máj", - "jun": "Jún", - "jul": "Júl", - "aug": "Aug", - "sep": "Szep", - "oct": "Okt", - "nov": "Nov", - "dec": "Dec", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Esemény létrehozva", - "event_updated": "Esemény frissítve", - "event_deleted": "Esemény törölve", - "calendar_created": "Naptár létrehozva", - "calendar_deleted": "Naptár törölve", - "event_move_error": "Nem sikerült áthelyezni az eseményt", - "event_resize_error": "Nem sikerült átméretezni az eseményt", - "alert_title": "Közelgő esemény", - "alert_now": "Most kezdődik", - "alert_in_minutes": "{count} perc múlva", - "invitation_sent": "Meghívók elküldve", - "invitations_denied_title": "A meghívók nem küldhetők el", - "invitations_denied": "A kiszolgáló nem küldte el a meghívókat: {reason}. Mentsük az eseményt a meghívók nélkül?", - "save_without_invitations": "Mentés meghívók nélkül", - "rsvp_updated": "Válasz frissítve", - "rsvp_error": "Nem sikerült frissíteni a választ", - "event_duplicated": "Esemény duplikálva", - "event_error": "Nem sikerült menteni az eseményt", - "task_due": "Feladat határidős", - "event_exported": "Esemény exportálva", - "title_copied": "Cím másolva", - "link_copied": "Link másolva" - }, - "status": { - "loading_calendars": "Naptárak betöltése...", - "loading_events": "Események betöltése..." - }, - "quick_create": { - "placeholder": "Új esemény címe", - "aria_label": "Gyors esemény létrehozása" - }, - "nav_prev": "Előző", - "nav_next": "Következő", - "nav_open_menu": "Menü megnyitása", - "import": { - "title": "Naptár importálása", - "tab_file": "Fájl", - "tab_url": "URL", - "select_file": ".ics fájl kiválasztása", - "drop_file": "vagy fájl ejtése ide", - "supported_formats": "iCalendar (.ics) fájlok támogatottak", - "url_description": "Add meg egy külső iCalendar (.ics) feed URL-jét az események importálásához.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "iCalendar (.ics) URL-ek támogatottak", - "fetch": "Lekérés", - "invalid_url": "Kérjük, adj meg egy érvényes URL-t", - "url_fetch_failed": "Nem sikerült lekérni a naptárt az URL-ről", - "parsing": "Naptár fájl feldolgozása...", - "parsed_events": "{count} esemény található", - "no_events": "Nincs esemény a fájlban", - "select_all": "Összes kijelölése", - "deselect_all": "Összes kijelölés megszüntetése", - "target_calendar": "Importálás ebbe a naptárba", - "import_button": "Kijelöltek importálása", - "importing": "Események importálása...", - "success": "{count} esemény sikeresen importálva", - "error": "Nem sikerült importálni a naptárt", - "file_too_large": "A fájl meghaladja a 10 MB korlátot", - "invalid_format": "Érvénytelen naptár fájl formátum" - }, - "webcal_action": { - "title": "Naptár hivatkozás megnyitása", - "description": "Hogyan szeretnéd használni a \"{name}\"-t?", - "import_title": "Egyszeri importálás", - "import_description": "Események lekérése és másolása az egyik naptáradba.", - "subscribe_title": "Feliratkozás", - "subscribe_description": "Naptár automatikus szinkronizálása külön naptárként.", - "cancel": "Mégse" - }, - "management": { - "title": "Naptár kezelés", - "description": "Naptárak létrehozása, átnevezése és testreszabása. Kattints jobb gombbal egy naptárra az oldalsávban a szín gyors módosításához.", - "name": "Név", - "name_placeholder": "Naptár neve", - "color": "Szín", - "change_color": "Szín módosítása", - "random_color": "Véletlenszerű szín", - "add_calendar": "Naptár hozzáadása", - "edit": "Szerkesztés", - "delete": "Törlés", - "save": "Mentés", - "create": "Létrehozás", - "cancel": "Mégse", - "default": "Alapértelmezett", - "confirm_delete": "Törlöd a \"{name}\" naptárat? Az összes esemény eltávolításra kerül.", - "confirm_clear": "Törlöd az összes eseményt a \"{name}\" naptárból? Ez nem vonható vissza.", - "set_default": "Beállítás alapértelmezettként", - "default_updated": "Alapértelmezett naptár frissítve", - "error_default": "Nem sikerült beállítani az alapértelmezett naptárat", - "clear_events": "Események törlése", - "events_cleared": "{count} esemény törölve", - "error_clear": "Nem sikerült törölni a naptár eseményeket", - "calendar_created": "Naptár létrehozva", - "calendar_updated": "Naptár frissítve", - "calendar_deleted": "Naptár törölve", - "color_updated": "Naptár szín frissítve", - "error_create": "Nem sikerült létrehozni a naptárat", - "error_update": "Nem sikerült frissíteni a naptárat", - "error_delete": "Nem sikerült törölni a naptárat", - "caldav_url": "CalDAV URL", - "copy_url": "CalDAV URL másolása", - "url_copied": "CalDAV URL a vágólapra másolva", - "share": "Naptár megosztása", - "new_event_in_calendar": "Új esemény ebben a naptárban", - "kind": "Tartalom", - "kind_events": "Események", - "kind_tasks": "Feladatok", - "kind_both": "Események és feladatok", - "kind_hint": "Meghatározza, hogy mely alkalmazások látják ezt a naptárat CalDAV-szinkronizáláskor. Később nem módosítható." - }, - "subscription": { - "title": "iCal feliratkozás", - "section_title": "iCal feliratkozások", - "description": "Feliratkozás külső iCalendar feed-re. Az események automatikusan szinkronizálódnak a saját naptárukba. https:// és webcal:// URL-ek támogatottak.", - "url_label": "Naptár URL", - "url_placeholder": "https://example.com/calendar.ics vagy webcal://...", - "name_label": "Naptár neve", - "name_placeholder": "pl. Ünnepnapok", - "color_label": "Szín", - "refresh_interval": "Frissítési időköz", - "interval_15": "15 percenként", - "interval_30": "30 percenként", - "interval_60": "Óránként", - "interval_360": "6 óránként", - "interval_1440": "Naponta", - "subscribe": "Feliratkozás", - "subscribing": "Feliratkozás...", - "save": "Változtatások mentése", - "saving": "Mentés...", - "edit": "Szerkesztés", - "edit_title": "Feliratkozás szerkesztése", - "updated": "\"{name}\" frissítve", - "update_error": "Nem sikerült frissíteni a feliratkozást", - "invalid_url": "Kérjük, adj meg egy érvényes URL-t", - "success": "Feliratkozva: \"{name}\"", - "error": "Nem sikerült hozzáadni a feliratkozást", - "refresh": "Frissítés most", - "refresh_success": "Feliratkozás frissítve", - "refresh_error": "Nem sikerült frissíteni a feliratkozást", - "unsubscribe": "Leiratkozás", - "confirm_delete": "Leiratkozás a \"{name}\" naptárról? A naptár és az összes esemény eltávolításra kerül.", - "deleted": "Feliratkozás eltávolítva", - "delete_error": "Nem sikerült eltávolítani a feliratkozást", - "last_refreshed": "Utolsó frissítés: {time}" - }, - "tasks": { - "label": "Feladatok", - "no_tasks": "Nincs feladat", - "no_title": "(Nincs cím)", - "mark_complete": "Késznek jelölés", - "mark_incomplete": "Befejezetlennek jelölés", - "filter_all": "Összes", - "filter_pending": "Függőben", - "filter_completed": "Kész", - "filter_overdue": "Lejárt", - "show_completed": "Kész mutatása", - "create": "Új feladat", - "edit": "Feladat szerkesztése", - "title_placeholder": "Feladat címe", - "description_placeholder": "Leírás hozzáadása...", - "due_date": "Határidő", - "include_time": "Időpont megadása", - "priority": "Prioritás", - "priority_none": "Nincs", - "priority_high": "Magas", - "priority_medium": "Közepes", - "priority_low": "Alacsony", - "progress": "Állapot", - "progress_needs_action": "Művelet szükséges", - "progress_in_process": "Folyamatban", - "progress_completed": "Kész", - "progress_cancelled": "Lemondva", - "calendar": "Naptár", - "alert": "Emlékeztető", - "alert_none": "Nincs", - "alert_at_time": "A határidő időpontjában", - "alert_5min": "5 perccel előtte", - "alert_15min": "15 perccel előtte", - "alert_30min": "30 perccel előtte", - "alert_1hr": "1 órával előtte", - "alert_1day": "1 nappal előtte", - "delete": "Törlés", - "cancel": "Mégse", - "save": "Mentés", - "quick_add_placeholder": "Feladat hozzáadása...", - "due_today": "Ma", - "due_tomorrow": "Holnap", - "overdue": "Lejárt" - } - }, - "calendar_event_notifications": { - "someone": "Valaki", - "invited": "{name} meghívott a(z) „{title}” eseményre", - "updated": "{name} frissítette a(z) „{title}” eseményt", - "cancelled": "{name} lemondta a(z) „{title}” eseményt", - "untitled": "Névtelen esemény" - }, - "share_notifications": { - "someone": "Valaki", - "shared": "{name} megosztotta veled a(z) {kind} „{object}” elemet", - "changed": "{name} módosította a hozzáférésedet a(z) {kind} „{object}” elemhez", - "revoked": "{name} visszavonta a hozzáférésedet a(z) {kind} „{object}” elemhez", - "object": { - "folder": "mappa", - "calendar": "naptár", - "address_book": "címjegyzék", - "files": "fájlmappa" - } - }, - "sharing": { - "title": "\"{name}\" megosztása", - "description": "Hozzáférés biztosítása más felhasználók vagy csoportok számára ezen a szerveren. A módosítások azonnal életbe lépnek.", - "no_shares": "Még nincs megosztva senkivel.", - "add_person": "Személy vagy csoport hozzáadása", - "search_placeholder": "Keresés név vagy e-mail alapján...", - "loading_principals": "Felhasználók betöltése...", - "no_principals": "Nincs más felhasználó vagy csoport.", - "no_match": "Nincs találat.", - "remove": "Hozzáférés eltávolítása", - "group": "Csoport", - "share_added": "Hozzáférést biztosítva", - "share_updated": "Hozzáférés frissítve", - "share_removed": "Hozzáférés eltávolítva", - "share_failed": "Nem sikerült frissíteni a megosztást", - "preset": { - "freeBusy": "Csak szabad/elfoglalt", - "read": "Csak olvasás", - "readWrite": "Olvasás és írás", - "manager": "Kezelő", - "custom": "Egyéni" - } - }, - "advanced_search": { - "title": "Speciális keresés", - "from": "Feladó", - "from_placeholder": "Feladó e-mail vagy név", - "to": "Címzett", - "to_placeholder": "Címzett e-mail vagy név", - "subject": "Tárgy", - "subject_placeholder": "Tárgy tartalma...", - "body": "Törzs", - "body_placeholder": "Törzs tartalma...", - "folder": "Mappa", - "all_folders": "Minden mappa", - "has_attachment": "Csatolmányok", - "date_after": "Ettől", - "date_before": "Eddig", - "size_min": "Nagyobb mint (KB)", - "size_max": "Kisebb mint (KB)", - "starred": "Csillagozott", - "unread": "Olvasatlan", - "read": "Olvasott", - "yes": "Igen", - "no": "Nem", - "clear": "Törlés", - "clear_all": "Összes törlése", - "filters_active": "{count} szűrő", - "filters_active_plural": "{count} szűrő", - "toggle_filters": "Továbbiak", - "search_hint": "Használj speciális szűrőket a pontos kereséshez", - "advanced_filters_tooltip": "Speciális keresőszűrők", - "results_found": "{count, plural, =0 {Nincs találat} one {# találat} other {# találat}}", - "results_found_more": "{count}+ találat", - "suggestions_recent": "Legutóbbi keresések", - "suggestions_people": "Személyek", - "suggestions_from": "Levelek keresése tőle: {address}", - "suggestions_to": "Levelek keresése neki: {address}", - "suggestions_remove_recent": "Eltávolítás a legutóbbi keresésekből" - }, - "welcome": { - "title": "Üdvözlünk a postafiókodban", - "tip_compose": "Nyomd meg a c billentyűt új e-mail írásához", - "tip_shortcuts": "Nyomd meg a ? billentyűt az összes billentyűparancs megtekintéséhez", - "tip_sidebar": "Találd meg a Naptár, Névjegyek és Beállítások menüpontokat az oldalsávban", - "tip_settings": "Testreszabhatod a felhasználói élményt a Beállításokban", - "got_it": "Értem", - "settings": "Beállítások", - "dismiss": "Elutasítás", - "start_tour": "Bemutató indítása" - }, - "demo_welcome": { - "title": "Üdvözlünk a Bulwark Mail-ben", - "description": "Fedezz fel egy teljes funkciókkal rendelkező webmail klienst - közvetlenül a böngésződben. Az összes adat az eszközödön marad, nyugodtan tesztelj mindent.", - "feature_email": "E-mailek olvasása és írása", - "feature_organize": "Címkék, csillagok és mappák", - "feature_shortcuts": "Billentyűparancsok", - "feature_privacy": "100%-ban privát demó", - "hint": "Kattints bármely e-mailre a bal oldalon a kezdéshez, vagy nézd meg az alábbi bemutatót." - }, - "files": { - "office_edit": "Dokumentum szerkesztése", - "office_loading": "Dokumentumszerkesztő megnyitása…", - "office_error": "A dokumentumszerkesztőt nem sikerült megnyitni.", - "office_close": "Szerkesztő bezárása", - "open_in_new_tab": "Megnyitás új lapon", - "title": "Fájlok", - "search_placeholder": "Fájlok keresése...", - "empty_state_title": "Még nincsenek fájlok", - "empty_state_description": "Tölts fel fájlokat vagy hozz létre mappákat a kezdéshez", - "upload": "Feltöltés", - "upload_files": "Fájlok feltöltése", - "new_folder": "Új mappa", - "new_folder_name": "Mappa neve", - "rename": "Átnevezés", - "rename_title": "Átnevezés", - "new_name": "Új név", - "name_forbidden_chars": "A kiszolgáló nem engedi ezeket a karaktereket a nevekben: {chars}", - "name_reserved": "Ez a név a kiszolgálón foglalt.", - "delete": "Törlés", - "delete_confirm_title": "Erőforrás törlése", - "delete_confirm_message": "Biztosan törölni szeretnéd a \"{name}\" elemet? Ez nem vonható vissza.", - "download": "Letöltés", - "name": "Név", - "size": "Méret", - "modified": "Módosítva", - "type": "Típus", - "folder": "Mappa", - "file": "Fájl", - "parent_directory": "Szülő könyvtár", - "breadcrumb_root": "Kezdőlap", - "other_accounts": "További fiókok", - "no_accounts": "Nincs csatlakoztatott fiók.", - "drop_files_here": "Fájlok vagy mappák ejtése ide a feltöltéshez", - "uploading": "Feltöltés...", - "upload_success": "{count, plural, one {1 fájl feltöltve} other {# fájl feltöltve}}", - "upload_error": "Nem sikerült feltölteni a fájlt", - "create_folder_success": "Mappa létrehozva", - "create_folder_error": "Nem sikerült létrehozni a mappát", - "delete_success": "Sikeresen törölve", - "delete_error": "Nem sikerült törölni", - "rename_success": "Sikeresen átnevezve", - "rename_error": "Nem sikerült átnevezni", - "download_error": "Nem sikerült letölteni", - "not_available": "A fájltárolás nem elérhető ezen a szerveren", - "cancel": "Mégse", - "create": "Létrehozás", - "save": "Mentés", - "no_results": "Nincs a keresésnek megfelelő fájl", - "batch_delete_confirm_message": "Biztosan törölni szeretnéd {count, plural, one {az 1 elemet} other {a # elemet}}? Ez nem vonható vissza.", - "batch_delete_success": "{count, plural, one {1 elem törölve} other {# elem törölve}}", - "grid_view": "Rács nézet", - "list_view": "Lista nézet", - "details": "Részletek", - "path": "Útvonal", - "preview": "Előnézet", - "preview_error": "Nem sikerült betölteni az előnézetet", - "cut": "Kivágás", - "copy": "Másolás", - "paste": "Beillesztés", - "move_success": "{count, plural, one {1 elem áthelyezve} other {# elem áthelyezve}}", - "move_error": "Nem sikerült áthelyezni", - "paste_success": "Sikeresen beillesztve", - "paste_error": "Nem sikerült beilleszteni", - "new_text_file": "Új szöveges fájl", - "file_name": "Fájl neve", - "retry": "Újra", - "refresh": "Frissítés", - "toggle_favorite": "Kedvenc váltása", - "duplicate": "Duplikálás", - "duplicate_success": "Sikeresen duplikálva", - "duplicate_error": "Nem sikerült duplikálni", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Fájl létrehozva", - "create_file_error": "Nem sikerült létrehozni a fájlt", - "favorites": "Kedvencek", - "recent": "Legutóbbi", - "properties": "Tulajdonságok", - "open_folder": "Mappa megnyitása", - "upload_folder": "Mappa feltöltése", - "file_too_large": "\"{name}\" meghaladja a maximális fájlméretet ({max})", - "undo": "Visszavonás", - "undo_success": "Művelet visszavonva", - "undo_error": "Nem sikerült visszavonni", - "toolbar": "Fájl műveletek", - "open_folder_tree": "Mappa fa megnyitása", - "file_list": "Fájlok és mappák", - "context_menu": "Műveletek", - "settings_title": "Fájl beállítások", - "settings_display": "Megjelenítés", - "settings_default_view": "Alapértelmezett nézet", - "settings_default_view_desc": "Válassz rács vagy lista elrendezés között", - "settings_default_sort": "Alapértelmezett rendezés", - "settings_default_sort_desc": "Válaszd ki a fájlok alapértelmezett rendezését", - "settings_sort_direction": "Rendezés iránya", - "settings_sort_direction_desc": "Válassz növekvő vagy csökkenő sorrendet", - "settings_ascending": "Növekvő", - "settings_descending": "Csökkenő", - "settings_icons": "Ikonok", - "settings_show_icons": "Fájlikonok mutatása", - "settings_show_icons_desc": "Ikonok megjelenítése a fájlok és mappák mellett", - "settings_colored_icons": "Színes ikonok", - "settings_colored_icons_desc": "Színes ikonok használata egyszínű helyett", - "settings_show_thumbnails": "Miniatűrök mutatása", - "settings_show_thumbnails_desc": "Kép előnézetek megjelenítése ikonok helyett a képfájlokhoz", - "settings_behavior": "Viselkedés", - "settings_show_hidden": "Rejtett fájlok mutatása", - "settings_show_hidden_desc": "Ponttal kezdődő fájlok és mappák megjelenítése", - "settings_folder_layout": "Mappa navigáció", - "settings_folder_layout_desc": "Mappák megjelenítése: fájlokkal együtt vagy oldalsáv fában", - "settings_folder_layout_inline": "Beágyazva", - "settings_folder_layout_sidebar": "Oldalsáv", - "disabled_title": "A fájlok funkciót a rendszergazda letiltotta", - "disabled_description": "Nagyméretű fájlok WebDAV-on keresztüli feltöltése Stalwart/RocksDB instabilitást okozhat, beleértve a memóriahiányos összeomlásokat és a helyreállíthatatlan lemezhasználatot. A törölt fájlok nem feltétlenül kerülnek azonnal eltávolításra a blob tárolóból. Ez a funkció nem ajánlott éles környezetben.", - "stability_warning": "Nagyméretű fájlok feltöltése szerver instabilitást okozhat. A törölt fájlok nem feltétlenül kerülnek azonnal eltávolításra a tárolóból. Használat óvatosan.", - "migration_title": "Fájlok frissítése…", - "migration_description": "A mappák és fájlok a megfelelő struktúrába rendeződnek. Ez csak egyszer történik meg." - }, - "smime": { - "your_certificates": "Tanúsítványaid", - "your_certificates_desc": "S/MIME tanúsítványok importálása és kezelése e-mailek aláírásához és titkosításához", - "recipient_certificates": "Címzett tanúsítványok", - "recipient_certificates_desc": "Nyilvános tanúsítványok az e-mailek címzetteknek történő titkosításához", - "identity_bindings": "Azonosság kulcs hozzárendelések", - "identity_bindings_desc": "S/MIME tanúsítványok hozzárendelése az e-mail azonosságaidhoz", - "defaults_title": "Alapértelmezettek", - "defaults_desc": "Alapértelmezett aláírási és titkosítási viselkedés beállítása", - "import_pkcs12": "PKCS#12 (.p12/.pfx) importálása", - "import_public_cert": "Tanúsítvány importálása", - "no_certificates": "Még nincs tanúsítvány importálva", - "no_recipient_certs": "Nincs címzett tanúsítvány", - "expires": "Lejárat", - "expired": "Lejárt", - "bound_to": "Hozzárendelve", - "no_key_bound": "Nincs", - "lock": "Kulcs zárolása", - "unlock": "Kulcs feloldása", - "details": "Részletek megtekintése", - "delete": "Törlés", - "encrypt_by_default": "Titkosítás alapértelmezés szerint", - "encrypt_by_default_desc": "E-mailek automatikus titkosítása, ha minden címzett rendelkezik tanúsítvánnyal", - "remember_unlocked": "Feloldott kulcsok megjegyzése", - "remember_unlocked_desc": "Kulcsok feloldva tartása a böngésző munkamenet idejére", - "sign_default_for": "Aláírás alapértelmezés szerint ehhez", - "enter_p12_passphrase": "PKCS#12 jelszó megadása", - "p12_passphrase_desc": "Add meg a tanúsítvány fájlt védő jelszót", - "enter_storage_passphrase": "Tárolási jelszó beállítása", - "storage_passphrase_desc": "Válassz egy jelszót a kulcs böngészőben történő védelméhez", - "next": "Következő", - "import": "Importálás", - "unlock_key": "Kulcs feloldása", - "unlock_key_desc": "Add meg a tárolási jelszót a kulcs feloldásához aláíráshoz vagy visszafejtéshez", - "passphrase_placeholder": "Jelszó megadása", - "confirm_passphrase_placeholder": "Jelszó megerősítése", - "passphrase_mismatch": "A jelszavak nem egyeznek", - "cancel": "Mégse", - "processing": "Feldolgozás...", - "close": "Bezárás", - "certificate_details": "Tanúsítvány részletei", - "cert_subject": "Tulajdonos", - "cert_issuer": "Kibocsátó", - "cert_email": "E-mail", - "cert_serial": "Sorozatszám", - "cert_validity": "Érvényesség", - "cert_fingerprint": "Ujjlenyomat (SHA-256)", - "cert_algorithm": "Algoritmus", - "cert_capabilities": "Képességek", - "cert_source": "Forrás", - "cert_expired": "Ez a tanúsítvány lejárt", - "cert_not_yet_valid": "Ez a tanúsítvány még nem érvényes", - "cap_sign": "Aláírás", - "cap_encrypt": "Titkosítás", - "cap_none": "Nincs", - "show_passphrase": "Jelszó mutatása", - "hide_passphrase": "Jelszó elrejtése", - "sign_toggle": "Aláírás", - "encrypt_toggle": "Titkosítás", - "missing_recipient_certs": "Hiányzó tanúsítványok ehhez: {emails}", - "missing_sender_cert": "Nincs tanúsítvány hozzárendelve ehhez az azonossághoz", - "status_encrypted_ok": "Ez az üzenet titkosítva volt", - "status_encrypted_no_key": "Ez az üzenet titkosítva van, de nem található hozzá kulcs", - "status_encrypted_failed": "Nem sikerült visszafejteni ezt az üzenetet", - "status_signed_valid": "Aláírás hitelesítve", - "status_signed_invalid": "Aláírás hitelesítése sikertelen", - "status_signed_expired_cert": "Aláírva egy lejárt tanúsítvánnyal", - "status_signed_self_signed": "Aláírás érvényes, de a tanúsítvány önaláírt (nem megbízható)", - "status_signed_mismatch": "Aláírás érvényes, de az aláíró nem egyezik a feladóval", - "status_unsupported": "Nem támogatott S/MIME formátum", - "auto_import_signer_certs": "Aláíró tanúsítványok automatikus importálása", - "auto_import_signer_certs_desc": "Hitelesített aláírt e-mailekből származó tanúsítványok automatikus mentése a jövőbeli titkosításhoz", - "export": "Exportálás", - "enter_export_passphrase": "Export jelszó beállítása", - "export_passphrase_desc": "Válassz egy jelszót az exportált PKCS#12 fájl védelméhez", - "export_storage_desc": "Add meg a tárolási jelszót a kulcs visszafejtéséhez az exportáláshoz", - "incorrect_passphrase": "Helytelen jelszó" - }, - "tour": { - "step_counter": "{current}. lépés a(z) {total} közül", - "skip": "Bemutató kihagyása", - "back": "Vissza", - "next": "Következő", - "finish": "Befejezés", - "take_a_tour": "A felület bemutatása", - "restart_title": "Bevezető bemutató", - "restart_desc": "A vezetett bemutató újrajátszása", - "restart_button": "Bemutató újraindítása", - "show_on_new_devices_title": "Megjelenítés új eszközökön", - "show_on_new_devices_desc": "Az üdvözlő banner és bemutató újrajátszása, amikor először jelentkezel be egy új eszközön, még ha már elvégezted máshol", - "sidebar_title": "Postafiókjaid", - "sidebar_desc": "Ez a mappa oldalsávod. Kattints bármely postafiókra az e-mailek megtekintéséhez. Létrehozhatsz mappákat, húzhatsz e-maileket közöttük, és egy pillantással láthatod az olvasatlanok számát.", - "compose_title": "E-mail írása", - "compose_desc": "Kattints ide egy új e-mail írásához. Hozzáadhatsz címzetteket, csatolmányokat, és használhatsz formázott szöveget.", - "search_title": "Keresés a levelek között", - "search_desc": "Keresés feladó, tárgy vagy tartalom alapján. Kattints a szűrő ikonra a speciális opciókért, mint dátumtartomány, csatolmányok és csillagozott üzenetek.", - "email_list_title": "E-mail listád", - "email_list_desc": "Az e-mailek itt jelennek meg. Kattints egyre az olvasáshoz a jobb oldalon. Használd a jelölőnégyzetet több kijelöléséhez, majd tömeges áthelyezéshez, törléshez vagy címkézéshez.", - "email_viewer_title": "Olvasóablak", - "email_viewer_desc": "A kiválasztott e-mail itt nyílik meg. Válaszolj, továbbíts, archiválj vagy törölj az eszköztár gombjaival. Csillagozhatsz is, vagy színes címkéket adhatsz hozzá.", - "keywords_title": "Színes címkék", - "keywords_desc": "E-mailek rendezése színkódolt címkékkel. Húzz egy e-mailt egy címkére a felcímkézéshez, vagy kattints jobb gombbal egy e-mailre a címkék hozzárendeléséhez.", - "calendar_title": "Naptár", - "calendar_desc": "Válts a naptárra az események kezeléséhez. Hozz létre eseményeket, állíts be emlékeztetőket, és nézd meg a nap, hét vagy hónap elrendezést.", - "contacts_title": "Névjegyek", - "contacts_desc": "A címjegyzéked itt található. Importálj névjegyeket, hozz létre csoportokat, és kattints bármely névjegyre a teljes adatainak megtekintéséhez.", - "settings_title": "Beállítások", - "settings_desc": "Testreszabhatsz mindent: témát, sűrűséget, aláírásokat, szűrőket, billentyűparancsokat, naptár alapértelmezetteket és még sok mást.", - "shortcuts_title": "Billentyűparancsok", - "shortcuts_desc": "A haladó felhasználók imádni fogják. Nyomd meg a ? gombot bármikor az összes elérhető parancs megtekintéséhez. Navigálhatsz, írhatsz és kezelheted az e-maileket egér nélkül.", - "compose_open_title": "A szerkesztő", - "compose_open_desc": "Ez az e-mail szerkesztő. Adj hozzá címzetteket, írd meg az üzeneted, csatolj fájlokat, és használj formázott szöveget. Piszkozatokat is menthetsz és sablonokat használhatsz.", - "calendar_view_title": "Naptárad", - "calendar_view_desc": "Itt a naptárad minta eseményekkel. Válthatsz nap, hét, hónap és napirend nézetek között az eszköztár segítségével.", - "create_event_title": "Esemény létrehozása", - "create_event_desc": "Kattints erre a gombra egy új naptári esemény létrehozásához. Beállíthatsz címet, dátumot, időpontot, és résztvevőket adhatsz hozzá.", - "event_modal_title": "Esemény részletei", - "event_modal_desc": "Itt az esemény űrlap. Töltsd ki a címet, válassz dátumot és időpontot, adj hozzá helyszínt vagy résztvevőket. Nyomj a Mentés gombra ha kész, vagy zárd be és lépj tovább.", - "contacts_list_title": "Névjegyeid", - "contacts_list_desc": "Itt vannak a névjegyeid. Kattints bármelyikre a teljes adatainak megtekintéséhez a jobb oldalon. Létrehozhatsz új névjegyeket, importálhatsz vCard-okat, vagy csoportokba rendezheted a névjegyeket.", - "settings_tabs_title": "Beállítások menü", - "settings_tabs_desc": "Itt vannak az összes beállítás kategória. Testreszabhatod a megjelenést, kezelheted az azonosságokat, beállíthatsz e-mail szűrőket, konfigurálhatod a naptárat és még sok mást.", - "files_title": "Fájltároló", - "files_desc": "A fájlböngésződ lehetővé teszi fájlok feltöltését, rendezését és megosztását - mint egy személyes felhőmeghajtó a levelezésedbe építve.", - "demo_banner_title": "Demó vezérlők", - "demo_banner_desc": "Demó módban vagy - minden a böngésződben marad. Nyomd meg a 'Demó visszaállítása' gombot bármikor a tiszta mintaadatokkal való újrakezdéshez.", - "quota_title": "Tárhely használat", - "quota_desc": "Kövesd nyomon a postafiókod méretét itt. A kör kitöltődik, ahogy egyre több helyet használsz." - }, - "unified_mailbox": { - "search_unavailable": "A keresés nem érhető el az egységes nézetben" - }, - "quote_header": { - "reply_line": "{from} ezt írta ({date}):", - "forwarded_separator": "---------- Továbbított üzenet ----------", - "from_label": "Feladó", - "date_label": "Dátum", - "subject_label": "Tárgy" - }, - "pwa_install": { - "title": "{appName} telepítése", - "description": "Telepítsd az alkalmazásunkat a gyors eléréshez és offline támogatáshoz.", - "not_now": "Később", - "install": "Telepítés", - "dont_remind": "Ne emlékeztess többet", - "dismiss_aria": "Telepítési ablak elutasítása" - }, - "deep_link": { - "message_not_found": "Ez az üzenet már nem érhető el.", - "thread_not_found": "Ez a beszélgetés már nem érhető el.", - "folder_not_found": "Ez a mappa már nem érhető el.", - "event_not_found": "Ez az esemény már nem érhető el.", - "file_not_found": "Ez a fájl már nem érhető el.", - "account_unavailable": "Jelentkezz be ebbe a fiókba a link megnyitásához.", - "copy_message": "Üzenet linkjének másolása", - "copy_conversation": "Beszélgetés linkjének másolása", - "copy_event": "Esemény linkjének másolása", - "copied": "Link másolva", - "copy_failed": "Nem sikerült másolni a linket" - }, - "plugins": { - "oauth_callback": { - "completing": "Hitelesítés befejezése…", - "completing_message": "Kérjük, várjon, amíg a kapcsolat létrejön.", - "received_title": "Hitelesítés fogadva", - "received_message": "Visszatérhet arra a lapra, ahol elindította a kapcsolódást.", - "failed_title": "A hitelesítés sikertelen", - "denied": "A hitelesítés megtagadva.", - "failed_detail": "A hitelesítés sikertelen: {error}", - "missing_params": "Hiányzik a hitelesítési kód vagy az állapot.", - "delivery_failed": "A hitelesítés eredményét nem sikerült átadni az alkalmazás lapjának. Zárja be ezt az ablakot, és próbálja újra." - } - }, - "global_search": { - "title": "Keresés mindenben", - "placeholder": "Keresés a levélben, névjegyekben, naptárban, fájlokban...", - "scope_label": "Keresés itt", - "account_label": "Fiók", - "scope_all": "Minden", - "scope_mail": "Levelek", - "scope_contacts": "Névjegyek", - "scope_calendar": "Naptár", - "scope_files": "Fájlok", - "all_accounts": "Minden fiók", - "show_all": "Összes találat megjelenítése", - "no_results": "Nincs találat erre: „{query}”", - "server_hint": "A teljes szavak a szerveren keresnek; a szórészletek a már betöltött elemekre illeszkednek", - "searching": "Keresés...", - "account_error": "Nem sikerült keresni itt: {account}", - "recent_searches": "Legutóbbi keresések", - "recurring": "Ismétlődő", - "no_subject": "(no subject)", - "empty_hint": "Keressen levelet, névjegyet, eseményt és fájlt minden fiókban. Próbálja ki: from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Lapos, dátum szerint", - "grouped": "Csoportosított", - "load_more": "Továbbiak betöltése", - "preview_empty": "Válasszon egy találatot az előnézethez", - "open_full": "Megnyitás", - "view_label": "Nézet" - } -} +{ + "meta_description": "Minimalista webmail kliens JMAP protokollal", + "login": { + "title": "Webmail", + "username_label": "E-mail", + "username_placeholder": "user@example.com", + "password_label": "Jelszó", + "password_placeholder": "Add meg a jelszavad", + "jmap_endpoint_label": "JMAP Szerver", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "A szervernek engedélyeznie kell a CORS kéréseket erről a tartományról.", + "jmap_server_label": "Szerver", + "jmap_server_auto_picked": "Szerver kiválasztva az e-mail tartományodból.", + "sign_in": "Bejelentkezés", + "signing_in": "Bejelentkezés...", + "loading": "Betöltés...", + "reconnecting": "Kapcsolat megszakadt. Újracsatlakozás...", + "error": { + "invalid_credentials": "Helytelen e-mail vagy jelszó. Ellenőrizd a megadott adatokat és próbáld újra.", + "invalid_token": "A szerver nem fogadta el ezt a hozzáférési tokent. Ellenőrizd, hogy teljes-e, és hogy nem járt-e le, vagy nem vonták-e vissza.", + "connection_failed": "Nem sikerült elérni a szervert. Ellenőrizd az internetkapcsolatod és próbáld újra.", + "cors_blocked": "A szerver elérhető, de blokkolja a több tartományból érkező kéréseket. Ellenőrizd a JMAP szerver CORS beállításait és engedélyezd ezt a tartományt.", + "server_error": "A szerver átmenetileg nem elérhető. Kérjük, próbáld újra később.", + "generic": "Váratlan hiba történt. Ha a probléma továbbra is fennáll, vedd fel a kapcsolatot a rendszergazdával.", + "totp_required": "Kétfaktoros hitelesítési kód szükséges. Kérjük, add meg a kódot lent.", + "totp_invalid": "Érvénytelen hitelesítési kód. Ellenőrizd a hitelesítő alkalmazásod és próbáld újra.", + "oauth_discovery_failed": "SSO engedélyezve van, de az identitásszolgáltató nem érhető el. Ellenőrizd az OAuth konfigurációt.", + "token_exchange_failed": "A jelszót és a kódot elfogadtuk, de a levelezőszerver nem indított munkamenetet ehhez az alkalmazáshoz. Kérje meg a rendszergazdát, hogy ellenőrizze az OAuth-kliens beállításait." + }, + "show_password": "Jelszó megjelenítése", + "hide_password": "Jelszó elrejtése", + "totp_toggle": "Van egy 2FA kódom", + "token_toggle": "Bejelentkezés hozzáférési tokennel", + "password_toggle": "Bejelentkezés jelszóval", + "token_label": "Hozzáférési token", + "token_placeholder": "Illeszd be az API-tokened", + "token_hint": "Hozz létre egy API-tokent a levelezési szolgáltatód beállításaiban, és illeszd be ide.", + "remember_me": "Emlékezz rám", + "config_error": { + "title": "Konfigurációs hiba", + "fetch_failed": "Nem sikerült betölteni az alkalmazás konfigurációját. Kérjük, próbáld újra később.", + "server_not_configured": "A levelezőszerver nincs beállítva. Kérjük, vedd fel a kapcsolatot a rendszergazdával." + }, + "remove_from_history": "Eltávolítás az előzményekből", + "totp_label": "Hitelesítési kód", + "totp_placeholder": "000000", + "session_expired": "A munkamenet lejárt. Kérjük, jelentkezz be újra.", + "dismiss": "Elutasítás", + "or": "vagy", + "sign_in_sso": "Bejelentkezés SSO-val", + "add_account_title": "Fiók hozzáadása", + "add_account_subtitle": "Bejelentkezés másik fiókkal", + "cancel": "Mégse", + "website": "Weboldal", + "imprint": "Impresszum", + "privacy_policy": "Adatvédelmi irányelvek", + "try_demo": "Demó kipróbálása", + "demo_description": "Fedezd fel mintaadatokkal - nincs szükség fiókra", + "demo_launching": "Demó indítása...", + "demo_login_button": "Demó indítása", + "demo_tagline": "Tapasztalj meg egy teljes értékű e-mail klienst. Nincs szükség fiókra.", + "demo_no_signup": "Nincs szükség regisztrációra - fedezd fel szabadon mintaadatokkal", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Bejelentkezés befejezése...", + "oauth_error": { + "title": "Hitelesítés sikertelen", + "invalid_state": "Biztonsági ellenőrzés sikertelen. Kérjük, próbáld meg újra a bejelentkezést.", + "missing_params": "Hiányzó engedélyezési adatok. Kérjük, próbáld meg újra a bejelentkezést.", + "token_exchange_failed": "Nem sikerült befejezni a hitelesítést. Kérjük, próbáld újra.", + "access_denied": "A hozzáférés megtagadva. Kérjük, vedd fel a kapcsolatot a rendszergazdával.", + "back_to_login": "Vissza a bejelentkezéshez" + } + }, + "sidebar": { + "close": "Bezárás", + "compose": "Új levél", + "compose_hint": "Új levél (c)", + "search_placeholder": "Keresés a levelekben...", + "search_placeholder_hint": "Keresés a levelekben... (nyomd meg a / billentyűt)", + "storage": "Tárhely", + "storage_used": "Használt", + "storage_free": "Szabad", + "storage_total": "Összesen", + "sign_out": "Kijelentkezés", + "sign_out_of": "Kijelentkezés erről: {account}", + "sign_out_all": "Kijelentkezés minden fiókból", + "add_account": "Fiók hozzáadása", + "set_as_default": "Beállítás alapértelmezettként", + "switch_account": "Fiók váltása", + "contacts": "Névjegyek", + "calendar": "Naptár", + "settings": "Beállítások", + "admin": "Adminisztráció", + "files": "Fájlok", + "loading_mailboxes": "Postafiókok betöltése...", + "push_connected": "Valós idejű frissítések aktívak", + "push_disconnected": "Valós idejű frissítések inaktívak", + "keyboard_shortcuts": "Billentyűparancsok", + "theme": { + "light": "Világos mód", + "dark": "Sötét mód", + "system": "Rendszer téma" + }, + "language": { + "title": "Nyelv" + }, + "mailboxes": { + "inbox": "Beérkező levelek", + "sent": "Elküldött", + "drafts": "Piszkozatok", + "trash": "Kuka", + "archive": "Archívum", + "starred": "Csillagozott", + "all_mail": "Minden levél", + "spam": "Spam", + "important": "Fontos" + }, + "unified_inbox": "Egységes beérkező", + "unified_sent": "Összes elküldött", + "unified_drafts": "Összes piszkozat", + "unified_trash": "Összes kuka", + "unified_archive": "Összes archívum", + "unified_junk": "Összes spam", + "all_accounts": "Minden fiók", + "all_accounts_loading": "Fiókok betöltése: {loaded} / {total}", + "unified_mailbox": "Egységes postafiók", + "expand": "Kibontás", + "collapse": "Összecsukás", + "expand_tooltip": "Kibontás", + "collapse_tooltip": "Összecsukás", + "mobile": { + "search": "Keresés", + "compose": "Új levél", + "go_back": "Vissza", + "toggle_menu": "Menü be/ki" + }, + "clear_search": "Keresés törlése", + "vacation_active": "Automatikus válasz aktív", + "demo_banner": "Demó mód", + "demo_reset": "Visszaállítás", + "demo_tour": "Bemutató", + "tags": "Címkék", + "show_all_tags": "Összes megjelenítése ({count})", + "show_fewer_tags": "Kevesebb megjelenítése", + "folders": "Mappák", + "your_folders": "Saját mappák", + "create_folder": "Mappa létrehozása", + "show_more": "Több", + "show_fewer": "Kevesebb", + "shared": "Megosztott", + "shared_account_hidden": "Elrejtve az oldalsávról. A Beállítások › Elrendezés alatt jelenítheted meg újra.", + "notifications_muted": "Értesítések némítva", + "mail": "Levelek", + "nav_label": "Navigáció", + "add_app": "Alkalmazások", + "scheduled": "Ütemezett", + "unified_all_unread": "Összes olvasatlan", + "unified_all_starred": "Összes csillagozott", + "unified_all_mail": "Összes levél", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Címkék", + "create_label": "Új címke létrehozása", + "mv_tasks": "Feladatok", + "mv_side_panel": "Oldalsó panel", + "mv_open_full": "Megnyitás teljes nézetben", + "mv_previous_day": "Előző nap", + "mv_next_day": "Következő nap", + "mv_add_another_account": "Másik fiók hozzáadása", + "mv_manage_account": "Fiók kezelése", + "mv_storage_used": "{total} {percent}%-a foglalt" + }, + "protocol_handlers": { + "title": "Alapértelmezett alkalmazások", + "description": "Válaszd ki, hogy az e-mail és naptár hivatkozások a Bulwarkban nyíljanak meg. Technikailag a Bulwark protokollkezelőként regisztrálja magát a mailto: és webcal: hivatkozásokhoz.", + "unsupported": "Ez a böngésző vagy kapcsolat nem támogatja a kézi protokollkezelő regisztrációt. Lehetőség lehet a telepített PWA használatára a böngésző vagy operációs rendszer beállításain keresztül.", + "mailto_label": "E-mail hivatkozások", + "mailto_description": "Mailto: hivatkozások megnyitása a Bulwarkban egy előre kitöltött szerkesztővel.", + "protocol_open_mode_label": "Protokoll hivatkozások megnyitásakor", + "protocol_open_mode_description": "Válaszd ki, hogy a Bulwark a mailto: és webcal: hivatkozásokat új lapon vagy a meglévő munkamenetben nyissa meg. Az aktív munkamenet opcióhoz értesítési engedély szükséges, hogy egy értesítésre kattintva előtérbe hozd a Bulwarkot, ha a böngésző blokkolja a fókuszt.", + "protocol_open_mode_active_session": "Megnyitás aktív munkamenetben, ha lehetséges", + "protocol_open_mode_new_tab": "Mindig új lapon nyisson", + "focus_notification_title": "Bulwark megnyitása", + "focus_notification_body": "A hivatkozás megnyílt a Bulwarkban. Kattints az ablak előtérbe hozásához.", + "webcal_label": "Naptár hivatkozások", + "webcal_description": "Webcal: hivatkozások megnyitása a Bulwarkban egy előre kitöltött naptárfeliratkozási párbeszédpanellel.", + "register_mailto": "E-mail alkalmazás regisztrálása", + "register_webcal": "Naptár alkalmazás regisztrálása", + "mailto_registered": "E-mail kezelő regisztráció kérve", + "webcal_registered": "Naptár kezelő regisztráció kérve", + "registration_failed": "Protokollkezelő regisztráció sikertelen", + "opening_mailto": "Szerkesztő megnyitása...", + "opening_webcal": "Naptár megnyitása...", + "browser_note": "A böngésző vagy operációs rendszered megerősítést kérhet, és előfordulhat, hogy a Bulwarkot telepíteni kell, mielőtt alapértelmezett alkalmazásként kiválasztható.", + "select_account_title": "Fiók kiválasztása", + "select_mailto_account": "Válaszd ki, melyik fiók nyissa meg ezt az e-mail hivatkozást.", + "select_webcal_account": "Válaszd ki, melyik fiók nyissa meg ezt a naptár hivatkozást.", + "select_account_note": "Ez csak erre a protokoll hivatkozásra vonatkozik.", + "detail_to": "Címzett", + "detail_subject": "Tárgy", + "detail_no_subject": "Nincs tárgy", + "detail_calendar": "Naptár", + "detail_source": "Forrás", + "active_account": "Aktív", + "switching_account": "Fiók váltása..." + }, + "sidebar_apps": { + "modal_title": "Oldalsáv alkalmazások", + "add_new": "Alkalmazás hozzáadása", + "edit_app": "Alkalmazás szerkesztése", + "name_label": "Név", + "name_placeholder": "Saját alkalmazás", + "name_required": "Név megadása kötelező", + "url_label": "URL", + "url_required": "URL megadása kötelező", + "url_invalid": "Érvényes http vagy https URL-t adj meg", + "icon_label": "Ikon", + "icon_required": "Ikon megadása kötelező", + "open_mode_label": "Megnyitás módja", + "open_new_tab": "Új lap", + "open_inline": "Beágyazva", + "cancel": "Mégse", + "add": "Hozzáadás", + "update": "Frissítés", + "delete": "Törlés", + "delete_confirm_title": "Alkalmazás törlése", + "delete_confirm": "Biztosan törölni szeretnéd a \"{name}\" alkalmazást?", + "no_apps": "Még nincsenek alkalmazások", + "no_apps_hint": "Egyedi alkalmazások és hivatkozások hozzáadása az oldalsávhoz", + "managed_title": "A rendszergazda biztosítja", + "managed_description": "Ezeket az alkalmazásokat mindenki számára beállították, és nem módosíthatók.", + "managed_badge": "Felügyelt", + "search_icons": "Ikonok keresése...", + "show_popular": "Népszerű", + "show_all": "Összes", + "no_icons_found": "Nem található ikon", + "inline_badge": "Beágyazott", + "tab_badge": "Lap", + "show_on_mobile": "Megjelenítés mobilon" + }, + "email_list": { + "columns": { + "from": "Feladó", + "to": "Címzett", + "subject": "Tárgy", + "date": "Dátum", + "size": "Méret" + }, + "no_emails": "Nincs üzenet", + "no_emails_description": "Ez a postafiók üres", + "no_search_results": "Nincs találat", + "no_search_results_description": "Próbáld módosítani a keresést vagy a szűrőket", + "loading": "E-mailek betöltése...", + "unread": "olvasatlan", + "to_me": "Nekem", + "to_recipients": "{count} címzettnek", + "and_others": "és {count} további", + "draft": "Piszkozat", + "starred": "Csillagozott", + "pinned": "Kitűzve", + "replied": "Megválaszolva", + "forwarded": "Továbbítva", + "has_attachment": "Melléklettel", + "conversations_range": "{from}–{to, number} / {total, number}", + "conversations_count": "{count} / {total} beszélgetés", + "conversations_count_plus": "{count}+ beszélgetés", + "conversations_count_simple": "{count} beszélgetés", + "no_conversations": "Nincs beszélgetés", + "loading_more": "További e-mailek betöltése...", + "no_more_emails": "Nincs több betölthető e-mail", + "batch_actions": { + "select": "E-mailek kijelölése", + "select_all": "Összes kijelölése", + "selected_messages": "{count, plural, one {1 e-mail} other {# e-mail}} kijelölve", + "mark_read": "Megjelölés olvasottként", + "mark_unread": "Megjelölés olvasatlanként", + "delete": "Törlés", + "delete_confirm_title": "E-mailek törlése", + "delete_confirm_message": "Biztosan törölni szeretnéd {count, plural, one {az 1 e-mailt} other {a # e-mailt}}?", + "clear_selection": "Kijelölés megszüntetése" + }, + "permanent_delete": "Végleges törlés", + "permanent_delete_confirm_title": "Végleges törlés", + "permanent_delete_confirm_message": "Ez az e-mail véglegesen törlődik. Ez a művelet nem vonható vissza.", + "permanent_delete_confirm_batch_message": "Ez a(z) {count, plural, one {1 e-mail} other {# e-mail}} véglegesen törlődik. Ez a művelet nem vonható vissza.", + "empty_folder": { + "button": "Mappa ürítése", + "confirm_title": "Mappa ürítése", + "confirm_message": "A mappa összes e-mailje véglegesen törlődik. Ez a művelet nem vonható vissza.", + "confirm_message_trash": "A mappában lévő összes e-mail a Kukába kerül.", + "confirm_button": "Mappa ürítése", + "junk_hint": "A Spam mappa ürítésével véglegesen eltávolíthatod az összes üzenetet.", + "trash_hint": "A Kuka mappa ürítésével véglegesen eltávolíthatod az összes üzenetet." + }, + "no_scheduled_emails": "Nincsenek ütemezett e-mailek", + "no_scheduled_emails_description": "A későbbre ütemezett üzenetek itt jelennek meg.", + "scheduled_count": "{count, plural, one {1 ütemezett e-mail} other {# ütemezett e-mail}}", + "scheduled_actions_hint": "Az ütemezett üzenetek megszakíthatók, átidőzíthetők vagy szerkeszthetők az ütemezett műveleteknél.", + "cancel_scheduled_send": "Küldés megszakítása", + "reschedule_send": "Átütemezés", + "cancel_and_edit": "Mégse és szerkesztés", + "cancel_and_compose_again": "Mégse és újraírás", + "reschedule_prompt": "Adj meg egy új dátumot/időpontot, pl. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "A feladó olvasási visszaigazolást kért:", + "send": "Visszaigazolás küldése", + "ignore": "Figyelmen kívül hagyás", + "sent": "Olvasási visszaigazolás elküldve.", + "send_failed": "Az olvasási visszaigazolás nem volt elküldhető", + "mdn_subject": "Olvasva: {subject}", + "mdn_body": "Ez egy visszaigazolás a(z) {recipient} címzettnek küldött üzenetről.\n\nMegjegyzés: Ez a visszaigazolás csak azt rögzíti, hogy az üzenet megjelent a címzett számítógépén. Nem garantálja, hogy a címzett elolvasta vagy megértette az üzenet tartalmát." + }, + "no_email_selected": "Nincs e-mail kijelölve", + "no_email_description": "Válassz ki egy e-mailt a listából a megtekintéshez", + "no_conversation_selected": "Nincs beszélgetés kijelölve", + "no_conversation_description": "Válassz egy beszélgetést a listából az olvasáshoz", + "compose": "Új levél", + "compose_hint": "Új üzenet írása", + "no_subject": "(Nincs tárgy)", + "no_body_content": "(Nincs elérhető tartalom)", + "no_preview_available": "Nincs előnézet", + "show_quoted_text": "Idézett szöveg megjelenítése", + "hide_quoted_text": "Idézett szöveg elrejtése", + "loading_email": "E-mail betöltése...", + "loading": "Betöltés...", + "reply": "Válasz", + "reply_all": "Válasz mindenkinek", + "forward": "Továbbítás", + "delete": "Törlés", + "archive": "Archiválás", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Csillagozás", + "unstar": "Csillagozás megszüntetése", + "mark_unread": "Megjelölés olvasatlanként", + "mark_read": "Megjelölés olvasottként", + "unread": "Olvasatlan", + "read": "Olvasott", + "spam_short": "Spam", + "not_spam_short": "Nem spam", + "move": "Áthelyezés", + "print": "Nyomtatás", + "view_source": "Forrás megtekintése", + "fullscreen": "Teljes képernyő", + "exit_fullscreen": "Kilépés a teljes képernyőből", + "view_in_dark_mode": "Megjelenítés sötét módban", + "view_in_light_mode": "Megjelenítés világos módban", + "export_email": "Exportálás .eml-ként", + "forward_as_attachment": "Továbbítás mellékletként", + "import_email": "Importálás .eml vagy .zip fájlból", + "keyboard_shortcuts": "Billentyűparancsok (?)", + "email_source": "E-mail forrás", + "draft_banner": "Ez az üzenet egy piszkozat", + "edit_draft": "Szerkesztés", + "copy_source": "Másolás a vágólapra", + "source_copied": "Forrás a vágólapra másolva", + "attachments": "Csatolmányok", + "important": "Fontos", + "download": "Letöltés", + "download_all": "Összes letöltése", + "from": "Feladó", + "to": "Címzett", + "cc": "Másolat", + "bcc": "Titkos másolat", + "date": "Dátum", + "subject": "Tárgy", + "show_details": "Részletek megjelenítése", + "hide_details": "Részletek elrejtése", + "external_content_warning": "Képek és külső tartalmak blokkolva", + "load_external_content": "Képek betöltése", + "trust_sender": "Feladó mindig megbízható", + "sender_check": { + "unverified_label": "Nem ellenőrzött feladó", + "failed_label": "Sikertelen feladóellenőrzés", + "unverified": "Ez az üzenet azt állítja, hogy a(z) {domain} tartományból jött, de ezt semmi sem igazolja. Nincs érvényes aláírása a(z) {domain} tartománytól, és nem olyan szerverről érkezett, amelyet a(z) {domain} tartomány engedélyez.", + "unverified_sent_from": "Ez az üzenet azt állítja, hogy a(z) {domain} tartományból jött, de a(z) {host} tartományból küldték, és nincs érvényes aláírása a(z) {domain} tartománytól.", + "failed": "Ez az üzenet azt állítja, hogy a(z) {domain} tartományból jött, de nem ment át a tartomány feladóellenőrzésén. Lehet, hogy hamisított.", + "failed_sent_from": "Ez az üzenet azt állítja, hogy a(z) {domain} tartományból jött, de a(z) {host} tartományból küldték, és nem ment át a(z) {domain} tartomány feladóellenőrzésén. Lehet, hogy hamisított.", + "caution": "Légy óvatos a linkekkel, a mellékletekkel, és ha jelszót vagy fizetési adatokat kérnek tőled." + }, + "back_to_list": "Vissza a listához", + "view_contact": "Névjegy megtekintése", + "message_details": "Üzenet részletei", + "more_reply_options": "További válasz opciók", + "set_tag": "Címke beállítása", + "tag": "Címke", + "more_actions": "További műveletek", + "previous": "Előző", + "next": "Következő", + "move_to": "Áthelyezés ide...", + "remove_tag": "Címke eltávolítása", + "tag_filter_placeholder": "Címkék szűrése", + "tag_no_matches": "Nincs találat a címkék közt", + "more_count": "+{count} további", + "characters_count": "{count} karakter", + "quick_reply_placeholder": "Gyors válasz írása...", + "more_options": "További opciók", + "sending": "Küldés...", + "security_authentication": "Biztonság és hitelesítés", + "technical_details": "Technikai részletek", + "message_id_label": "Üzenetazonosító:", + "reply_to_label": "Válaszcím:", + "delivery_time_label": "Kézbesítési idő:", + "conversation_part_label": "Beszélgetés része:", + "previous_messages": "{count} előző üzenet", + "previous_messages_plural": "{count} előző üzenet", + "time": { + "day": "nap", + "days": "nap", + "hour": "óra", + "hours": "óra", + "minute": "perc", + "minutes": "perc" + }, + "unknown_sender": "Ismeretlen", + "recipient_me": "én", + "recipient_and_others": "{name} és {count} további", + "recipient_to_prefix": "Címzett:", + "authentication": { + "title": "Hitelesítés", + "status": { + "verified": "Hitelesítve", + "warning": "Figyelmeztetés", + "none": "Nincs hitelesítve" + }, + "spf": { + "pass": "SPF sikeres", + "fail": "SPF sikertelen", + "none": "Nincs SPF" + }, + "dkim": { + "pass": "DKIM érvényes", + "fail": "DKIM érvénytelen", + "none": "Nincs DKIM" + }, + "dmarc": { + "pass": "DMARC sikeres", + "fail": "DMARC sikertelen", + "none": "Nincs DMARC" + }, + "spam_score": "Spam pontszám", + "tooltip_spf": "Sender Policy Framework: Ellenőrzi, hogy a küldő szerver jogosult-e e-mailek küldésére a tartomány nevében", + "tooltip_dkim": "DomainKeys Identified Mail: Kriptográfiai aláírással erősíti meg, hogy az e-mailt nem változtatták meg a továbbítás során", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: Biztosítja, hogy az SPF és DKIM összhangban van a feladó tartományával, és szabályzatot állít be a hibák esetére", + "policy": "Szabályzat", + "result": { + "pass": "Sikeres", + "fail": "Sikertelen", + "softfail": "Gyenge hiba", + "neutral": "Semleges", + "permerror": "Állandó hiba", + "temperror": "Ideiglenes hiba", + "none": "Nincs" + } + }, + "details": { + "recipients_routing": "Címzettek és útvonal", + "authentication_security": "Hitelesítés és biztonság", + "identifiers_threading": "Azonosítók és fűzések", + "mailing_list": "Levelezőlista", + "message_properties": "Üzenet tulajdonságai", + "sent": "Küldve", + "received": "Fogadva", + "delivery_time": "Kézbesítési idő", + "in_reply_to": "Válasz erre", + "references": "Hivatkozások", + "thread_id": "Beszélgetés azonosító", + "size": "Méret", + "mime_type": "MIME típus", + "attachments_summary": "{count} fájl · {size}", + "list_id": "Lista azonosító", + "list_help": "Lista súgó", + "list_post": "Lista küldés", + "list_unsubscribe": "Leiratkozás", + "iprev": "Fordított DNS", + "spam_status": "Spam státusz", + "ai_verdict": "AI döntés", + "account": "Fiók", + "no_subject": "(nincs tárgy)" + }, + "headers": { + "routing": "Útvonal", + "received": "Fogadva", + "message_id": "Üzenet azonosító", + "list_info": "Lista információk" + }, + "tooltips": { + "reply": "Válasz (r)", + "reply_all": "Válasz mindenkinek (a)", + "forward": "Továbbítás (f)", + "archive": "Archiválás (e)", + "delete": "Törlés (# vagy Del)", + "star": "Csillagozás (s)", + "unstar": "Csillagozás megszüntetése (s)", + "compose": "Új levél (c)", + "previous": "Előző e-mail", + "next": "Következő e-mail", + "edit_draft": "Piszkozat szerkesztése" + }, + "spam": { + "button_title": "Spam jelentése", + "not_spam_title": "Megjelölés jogosként", + "toast_success": "Áthelyezve a Spam mappába", + "toast_batch": "{count} e-mail áthelyezve a Spam mappába", + "toast_undo": "Visszavonás", + "toast_not_spam_success": "Áthelyezve a Beérkező levelek közé", + "toast_not_spam_batch": "{count} e-mail áthelyezve a Beérkező levelek közé", + "error": "Nem sikerült spamként jelenteni", + "error_not_spam": "Nem sikerült visszaállítani az e-mailt" + }, + "unsubscribe_banner": { + "label": "Hírlevél", + "button": "Leiratkozás", + "confirm_title": "Leiratkozás ettől a feladótól?", + "confirm_button": "Megerősítés", + "cancel": "Mégse", + "success_http": "Leiratkozási oldal megnyílt egy új lapon", + "success_mailto": "Leiratkozó e-mail elküldve", + "confirm_message_http": "A leiratkozási oldal új lapon nyílik meg.", + "confirm_message_mailto": "Leiratkozó e-mailt küldünk a feladónak.", + "error": "Nem sikerült leiratkozni", + "dismiss": "Elutasítás" + }, + "calendar_invitation": { + "loading": "Esemény részleteinek betöltése...", + "title": "Naptár meghívó", + "published_title": "Közzétett esemény", + "response_title": "Esemény válasz", + "update_title": "Esemény frissítés", + "counter_title": "Ellenjavaslat", + "refresh_title": "Frissítési kérelem", + "declined_counter_title": "Ellenjavaslat elutasítva", + "cancelled_title": "Esemény lemondva", + "organizer": "Szervező: {name}", + "organizer_label": "Szervező", + "attendees": "{count, plural, one {# résztvevő} other {# résztvevő}}", + "accept": "Elfogadás", + "maybe": "Talán", + "decline": "Elutasítás", + "add_to_calendar": "Hozzáadás a naptárhoz", + "added": "Hozzáadva a naptárhoz", + "rsvp_sent": "Válasz elküldve", + "parse_error": "Nem sikerült értelmezni a meghívót", + "action_failed": "Nem sikerült végrehajtani a naptár műveletet.", + "no_calendar": "Naptár nem elérhető", + "published_info": "Ez az esemény referencia célból lett megosztva.", + "response_info": "Ez az üzenet egy résztvevői választ tartalmaz.", + "response_info_organizer": "Ez a résztvevői válasz frissíti az eseményed.", + "update_info": "Ez az üzenet egy meglévő eseményt frissít.", + "counter_info": "Ez az üzenet módosításokat javasol egy eseményhez.", + "counter_info_organizer": "Ez a résztvevő módosításokat javasolt az eseményedhez.", + "refresh_info": "Ez az üzenet a legújabb esemény adatait kéri.", + "refresh_info_organizer": "Egy résztvevő a legújabb esemény adatokat kérte.", + "declined_counter_info": "A szervező elutasította az ellenjavaslatot.", + "authentication_failed_info": "Az e-mail hitelesítési ellenőrzések ehhez a meghívóhoz sikertelenek voltak. Óvatosan kezeld a naptár műveleteket.", + "authentication_missing_info": "Ez a meghívó nem tartalmaz ellenőrzött e-mail hitelesítést. Ha bármi szokatlannak tűnik, erősítsd meg a részleteket a szervezővel.", + "sender_mismatch_info": "Ezt a meghívót {sender} küldte, míg a naptár adatokban szereplő szervező {organizer}.", + "sender_mismatch_unverified_info": "Ezt a meghívót {sender} küldte, míg a naptár adatokban szereplő szervező {organizer}, és az üzenet nem volt hitelesíthető.", + "organizer_role": "Te szervezed ezt az eseményt", + "your_response": "Válaszod: {status}", + "response_needed": "Válasz szükséges", + "response_accepted": "Elfogadva", + "response_tentative": "Előzetes", + "response_declined": "Elutasítva", + "response_delegated": "Delegálva", + "actor_sent_info": "Küldte: {name}.", + "actor_response_info": "{name} válasza: {status}.", + "actor_counter_info": "{name} módosításokat javasolt ehhez az eseményhez.", + "actor_refresh_info": "{name} a legújabb esemény adatokat kérte.", + "actor_declined_counter_info": "{name} elutasította az ellenjavaslatot.", + "actor_note": "Megjegyzés: {comment}", + "actor_unknown": "Valaki", + "proposed_changes": "Javasolt módosítások", + "change_title": "Cím", + "change_time": "Idő", + "change_location": "Helyszín", + "change_description": "Leírás", + "change_empty": "Nincs", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Javasolt módosítások alkalmazása", + "proposal_applied": "Javasolt módosítások alkalmazva.", + "review_proposal": "Javaslat áttekintése", + "review_request": "Kérelem áttekintése", + "view_in_calendar": "Megtekintés a naptárban", + "select_calendar": "Naptár kiválasztása", + "already_in_calendar": "Már a naptáradban van", + "request_info": "Meghívtak erre az eseményre. Válaszolj, hogy a szervező tudja a rendelkezésre állásod.", + "cancel_info": "A szervező lemondta ezt az eseményt.", + "event_updated": "Frissítés #{sequence}", + "event_status_tentative": "Előzetes", + "event_status_cancelled": "Lemondva", + "expand": "Részletek mutatása", + "collapse": "Részletek elrejtése" + }, + "send": "Küldés", + "more": "további", + "scheduled_banner": "Küldés ütemezve: {date}", + "scheduled_send_created": "E-mail ütemezve a küldéshez", + "sending_in": "Küldés {seconds} mp múlva", + "undo_send_failed": "Nem vonható vissza. Lehet, hogy az üzenet már elment.", + "cancel_scheduled_send": "Küldés megszakítása", + "reschedule_send": "Átütemezés", + "cancel_and_edit": "Mégse és szerkesztés", + "cancel_and_compose_again": "Mégse és újraírás", + "reschedule_prompt": "Adj meg egy új dátumot/időpontot, pl. 2026-05-04T15:30", + "scheduled_actions_only": "Az ütemezett nézet csak ütemezett műveleteket támogat", + "undo_send_scheduled": "Üzenet ütemezve a küldéshez", + "undo_send": "Küldés visszavonása", + "contact_sidebar": { + "title": "Névjegy", + "close": "Oldalsáv bezárása", + "action_email": "E-mail", + "action_email_title": "E-mail küldése", + "action_copy": "Másolás", + "action_copy_title": "E-mail másolása", + "action_edit_title": "Névjegy szerkesztése", + "section_emails": "E-mailek", + "section_phones": "Telefonszámok", + "section_organizations": "Szervezetek", + "section_addresses": "Címek", + "section_notes": "Jegyzetek", + "not_in_contacts": "Nincs a névjegyei között", + "add_to_contacts": "Hozzáadás a névjegyekhez", + "copied": "Másolva!", + "copy_failed": "A másolás nem sikerült", + "view_contact": "Névjegy megtekintése", + "view_details": "Részletek megtekintése" + }, + "send_now": "Küldés most", + "verification_code": { + "copy": "{code} kód másolása", + "copied": "Kód másolva", + "copy_failed": "Nem sikerült másolni a kódot" + }, + "mv_position": "{index} / {total}", + "plugin_panel_show": "Bővítménypanel megjelenítése", + "plugin_panel_hide": "Bővítménypanel elrejtése" + }, + "email_composer": { + "select_from_directory": "Kiválasztás a címtárból", + "minimize": "Kis méret", + "maximize": "Teljes képernyő", + "restore": "Visszaállítás", + "new_message": "Új üzenet", + "signature_edit_hint": "Kattintson duplán az aláírás szerkesztéséhez", + "reply": "Válasz", + "reply_all": "Válasz mindenkinek", + "forward": "Továbbítás", + "reply_to": "Válasz", + "reply_all_to": "Válasz mindenkinek", + "forward_message": "Továbbítás", + "from": "Feladó", + "to": "Címzett", + "cc": "Másolat", + "bcc": "Titkos másolat", + "subject": "Tárgy", + "body_placeholder": "Üzenet írása...", + "send": "Küldés", + "cancel": "Mégse", + "attach": "Csatolás", + "attach_photos": "Fényképek és videók", + "attach_files": "Fájlok", + "attach_from_files": "Csatolás a Fájlokból", + "files_picker": { + "attach": "{count, plural, =0 {Csatolás} one {1 fájl csatolása} other {# fájl csatolása}}", + "empty_folder": "Ez a mappa üres", + "load_failed": "A fájlokat nem sikerült betölteni" + }, + "discard": "Elvetés", + "discard_draft_title": "Piszkozat elvetése?", + "discard_draft_confirm": "Nem mentett változtatások vannak. El akarod vetni ezt a piszkozatot?", + "discard_draft_message": "A piszkozat törlődik.", + "saving": "Mentés...", + "sending": "Küldés...", + "add_link": "Hivatkozás hozzáadása", + "link_url_prompt": "Add meg az URL-t", + "draft_saved": "Piszkozat elmentve", + "save_failed": "Mentés sikertelen", + "to_placeholder": "Címzett e-mail címek", + "cc_placeholder": "Másolat címzettek", + "bcc_placeholder": "Titkos másolat címzettek", + "subject_placeholder": "Tárgy", + "cc_label": "Másolat:", + "bcc_label": "Titkos másolat:", + "subject_label": "Tárgy:", + "file_size_kb": "KB", + "prefix": { + "forward": "Továbbítás:", + "reply": "Válasz:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} other {# e-mail}}", + "no_subject": "(Nincs tárgy)", + "unknown_sender": "Ismeretlen", + "quote": { + "reply_header": "{date} -án/én {sender} írta:", + "forward_header": "---------- Továbbított üzenet ----------", + "from": "Feladó: {sender}", + "date": "Dátum: {date}", + "subject": "Tárgy: {subject}", + "to": "Címzett: {recipients}" + }, + "remove_sub_address": "Alcím eltávolítása", + "from_override": { + "toggle_off": "Felülbírálás", + "toggle_on": "Felülbírálás megszüntetése", + "toggle_tooltip": "A Feladó név és cím szabad szerkesztése. A levél továbbra is az identitásodon keresztül kerül elküldésre.", + "name_label": "Feladó neve", + "name_placeholder": "Név", + "email_label": "Feladó e-mail címe", + "email_placeholder": "alias@example.com", + "envelope_notice": "Ha a szervered nem fogadja el ezt a címet borítékfeladóként, helyette a(z) {identity} cím kerül oda, és a címzettek láthatják a Return-Path fejlécben." + }, + "save_as_template": "Mentés sablonként", + "attach_from_device": "Feltöltés erről az eszközről", + "templates_menu": "Sablonok", + "insert_template": "Sablon beszúrása", + "options": "Beállítások", + "option_plain_text": "Egyszerű szöveg", + "option_plain_text_hint": "Küldés formázás nélkül", + "option_read_receipt": "Olvasási visszaigazolás kérése", + "option_read_receipt_hint": "A címzett erősítse meg, hogy megnyitotta", + "option_dsn": "Kézbesítési értesítés", + "option_dsn_hint": "Jelentés, amikor a szerver kézbesíti", + "option_require_tls": "Titkosított kézbesítés megkövetelése", + "option_require_tls_hint": "Csak TLS-en kézbesítse, különben utasítsa vissza", + "option_priority": "Fontosság", + "option_priority_high": "Magas", + "option_priority_normal": "Normál", + "option_priority_low": "Alacsony", + "validation": { + "recipient_required": "Adj meg egy címzettet a küldéshez", + "body_required": "Írj üzenetet vagy csatolj fájlt", + "invalid_recipients": "Nincs elküldve - ellenőrizd ezeket a címeket: {addresses}", + "attachments_uploading": "A mellékletek még feltöltés alatt vannak – küldés a feltöltés befejezése után", + "attachment_upload_failed": "Nincs elküldve - egy melléklet feltöltése nem sikerült. Távolítsa el, és próbálja újra." + }, + "upload_progress": "Feltöltés {uploaded} / {total}", + "upload_cancel": "Feltöltés megszakítása", + "upload_failed": "Nem sikerült feltölteni: {filename}", + "attachment_too_large": "A(z) „{name}” nagyobb, mint amit a kiszolgáló enged (fájlonként {max})", + "attachments_total_too_large": "A mellékletek túllépnék a kiszolgáló üzenetenkénti {max} korlátját", + "drop_files": "Fájlok ejtése a csatoláshoz", + "show_less": "Kevesebb mutatása", + "send_failed": "Nem sikerült elküldeni az e-mailt", + "send_timeout": "A kiszolgáló nem válaszol többé. Újraküldés előtt ellenőrizze az Elküldött mappát – az üzenet lehet, hogy már kiment.", + "send_offline": "Úgy tűnik, nincs kapcsolat. Az üzenet megmaradt, küldd el újra, ha újra online vagy.", + "formatting": "Formázás", + "send_recipients_rejected": "Nem lett elküldve – a szerver minden címzettet elutasított.", + "send_plugin_failed": "Nem lett elküldve – egy küldést végző bővítmény hibát jelzett vagy túl sokáig tartott. Újraküldés előtt nézd meg az Elküldött mappát.", + "send_some_recipients_rejected": "Elküldve, de ezeknek a címzetteknek nem – a szerver elutasította őket.", + "continue_draft": "Piszkozat folytatása", + "close_draft_title": "Piszkozat mentése vagy elvetése?", + "close_draft_message": "Nem mentett változtatások vannak. Szeretnéd piszkozatként menteni vagy elvetni?", + "smime_sign_on": "S/MIME aláírás engedélyezve", + "smime_sign_off": "S/MIME aláírás engedélyezése", + "smime_encrypt_on": "S/MIME titkosítás engedélyezve", + "smime_encrypt_off": "S/MIME titkosítás engedélyezése", + "smime_encrypt_unavailable": "S/MIME titkosítás nem elérhető – hiányoznak a címzett tanúsítványai", + "smime_unlock_title": "S/MIME kulcs feloldása", + "smime_unlock_message": "Add meg a jelszót az S/MIME aláíró kulcs feloldásához.", + "smime_unlock_button": "Feloldás", + "smime_passphrase_placeholder": "Jelszó", + "forgot_attachment": { + "title": "Elfelejtettél egy csatolmányt?", + "message": "Az üzeneted említi a \"{keyword}\" kifejezést, de nincs fájl csatolva. Elküldöd így is?", + "send_anyway": "Küldés így is", + "back": "Vissza a szerkesztéshez" + }, + "empty_subject": { + "title": "Küldés tárgy nélkül?", + "message": "Ennek az üzenetnek nincs tárgya. Mégis elküldi?", + "dont_ask_again": "Ne kérdezze többé", + "send_anyway": "Küldés így is", + "back": "Vissza a szerkesztéshez" + }, + "schedule_send": "Késleltetett küldés", + "schedule_send_description": "Válaszd ki, mikor küldje el a szerver az üzenetet.", + "schedule_preset": { + "tomorrow_morning": "Holnap reggel", + "tomorrow_afternoon": "Holnap délután", + "monday_morning": "Hétfő reggel" + }, + "schedule_send_required": "Válassz dátumot és időpontot.", + "schedule_send_invalid": "Adj meg egy érvényes dátumot és időpontot.", + "schedule_send_future": "Válassz egy jövőbeli dátumot és időpontot.", + "schedule_send_too_late": "Ez az időpont későbbi, mint amit a szerver engedélyez.", + "schedule_send_unsupported": "Az ütemezett küldés nem támogatott ehhez a fiókhoz.", + "schedule_send_cleanup_warning": "Az ütemezett küldés létrejött, de a piszkozat tisztítása sikertelen volt.", + "send_delay_unsupported": "A késleltetett küldés nem támogatott ehhez a fiókhoz.", + "send_delay_unsupported_confirm": "Ez a fiók nem támogatja a késleltetett küldést. Elküldjük azonnal?", + "recipient_edit_email": "E-mail-cím szerkesztése", + "recipient_edit_name": "Megjelenített név szerkesztése", + "recipient_email_placeholder": "E-mail-cím", + "recipient_name_placeholder": "Megjelenített név", + "invalid_recipient": "Érvénytelen e-mail-cím", + "autocomplete_search_server": "Keresés a kiszolgálón", + "autocomplete_searching": "Keresés...", + "mention_recipients": "Címzettek", + "mention_card_not_recipient": "Nincs a Címzett vagy a Másolat mezőben, így nem kapja meg az üzenetet.", + "mention_card_add": "Hozzáadás: {field}", + "mention_card_remove": "Említés eltávolítása", + "toolbar": { + "bold": "Félkövér", + "italic": "Dőlt", + "underline": "Aláhúzott", + "strikethrough": "Áthúzott", + "text_color": "Betűszín", + "remove_color": "Szín eltávolítása", + "heading_1": "Címsor 1", + "heading_2": "Címsor 2", + "bullet_list": "Felsorolás", + "ordered_list": "Számozott lista", + "quote": "Idézet", + "code_block": "Kódblokk", + "align_left": "Balra igazítás", + "align_center": "Középre igazítás", + "align_right": "Jobbra igazítás", + "text_direction": "Szövegirány (RTL/LTR)", + "link": "Hivatkozás", + "table": "Táblázat", + "clear_formatting": "Formázás törlése", + "undo": "Visszavonás", + "redo": "Újra", + "add_row_above": "Sor beszúrása fölé", + "add_row_below": "Sor beszúrása alá", + "add_column_before": "Oszlop beszúrása elé", + "add_column_after": "Oszlop beszúrása mögé", + "delete_row": "Sor törlése", + "delete_column": "Oszlop törlése", + "toggle_header_row": "Fejlécsor váltása", + "delete_table": "Táblázat törlése", + "pick_size": "Méret kiválasztása", + "font_size": "Betűméret", + "font_size_default": "Alapértelmezett", + "background_color": "Háttérszín", + "remove_background_color": "Háttérszín eltávolítása" + }, + "send_filing_warning": "Elküldve - de az utólagos rendrakás nem sikerült, egy elavult piszkozat megmaradhat.", + "pop_out": "Válasz megnyitása külön ablakban", + "send_guard_failed": "Nem lett elküldve - egy bővítmény, amely küldés előtt ellenőrzi az üzeneteket, hibát jelzett vagy nem válaszolt." + }, + "confirm_dialog": { + "confirm": "Megerősítés", + "cancel": "Mégse" + }, + "common": { + "loading": "Betöltés...", + "error": "Hiba", + "success": "Siker", + "cancel": "Mégse", + "save": "Mentés", + "delete": "Törlés", + "edit": "Szerkesztés", + "close": "Bezárás", + "search": "Keresés", + "refresh": "Frissítés", + "settings": "Beállítások", + "help": "Súgó", + "logout": "Kijelentkezés", + "yes": "Igen", + "no": "Nem", + "unknown": "Ismeretlen", + "app_title": "Webmail", + "reconnecting": "Kapcsolat megszakadt. Újracsatlakozás...", + "rate_limited_title": "A szerver hitelesítés átmenetileg korlátozva van.", + "rate_limited_detail": "A Bulwark szüneteltette a háttérkéréseket a lezárás elkerülése érdekében. Újrapróbálkozás {seconds} másodperc múlva.", + "rate_limited_action_title": "Kérés szüneteltetve a lezárás elkerülése érdekében.", + "rate_limited_action_detail": "A Bulwark várja a szerver lehűlését, mielőtt további hitelesített kéréseket küldene. Próbáld újra {seconds} másodperc múlva.", + "pull_to_refresh": "Húzza le a frissítéshez", + "release_to_refresh": "Engedje el a frissítéshez", + "refreshing": "Frissítés…" + }, + "notifications": { + "email_sent": "E-mail sikeresen elküldve", + "email_deleted": "E-mail törölve", + "email_archived": "E-mail archiválva", + "emails_deleted": "{count, plural, other {# e-mail törölve}}", + "emails_archived": "{count, plural, other {# e-mail archiválva}}", + "error_archiving": "Nem sikerült archiválni az e-mailt", + "error_updating": "Nem sikerült frissíteni az e-mailt", + "email_starred": "E-mail csillagozva", + "email_unstarred": "E-mail csillagozás megszüntetve", + "email_marked_read": "E-mail olvasottnak jelölve", + "email_marked_unread": "E-mail olvasatlannak jelölve", + "copied_to_clipboard": "Vágólapra másolva", + "source_copied": "Forrás a vágólapra másolva", + "error_sending": "Nem sikerült elküldeni az e-mailt", + "error_deleting": "Nem sikerült törölni az e-mailt", + "error_loading": "Nem sikerült betölteni az e-maileket", + "new_email": "Új e-mail", + "new_email_from": "{sender} feladótól", + "click_to_view": "Kattints a megtekintéshez", + "email_moved": "E-mail áthelyezve", + "emails_moved": "{count} e-mail áthelyezve", + "moved_to_mailbox": "Áthelyezve ide: {mailbox}", + "move_failed": "Áthelyezés sikertelen", + "removed_from_folder": "Eltávolítva innen: {mailbox}", + "remove_from_folder_failed": "Nem sikerült eltávolítani ebből a mappából", + "emails_copied": "{count} e-mail másolva", + "copy_failed": "Másolás sikertelen", + "move_error": "Nem sikerült áthelyezni az e-maileket a kiválasztott mappába", + "email_tagged": "E-mail címkézve", + "emails_tagged": "{count} e-mail címkézve", + "tag_failed": "Címkézés sikertelen", + "identity_created": "Identitás sikeresen létrehozva", + "identity_updated": "Identitás sikeresen frissítve", + "identity_deleted": "Identitás törölve", + "identity_set_primary": "Elsődleges identitás frissítve", + "identity_create_failed": "Nem sikerült létrehozni az identitást: {error}", + "identity_update_failed": "Nem sikerült frissíteni az identitást: {error}", + "identity_delete_failed": "Nem sikerült törölni az identitást: {error}", + "identity_unauthorized": "Nem vagy jogosult erről az e-mail címről küldeni", + "identity_not_found": "Identitás nem található", + "vacation_saved": "Távolléti beállítások elmentve", + "vacation_save_failed": "Nem sikerült menteni a távolléti beállításokat", + "filters_saved": "Szűrők sikeresen elmentve", + "filters_save_failed": "Nem sikerült menteni a szűrőket", + "filters_deleted": "Szűrő szabály törölve", + "templates_exported": "Sablonok sikeresen exportálva", + "templates_imported": "{count, plural, one {# sablon} other {# sablon}} importálva", + "templates_import_errors": "Néhány sablon importálása sikertelen", + "templates_import_empty": "Nem található sablon a fájlban", + "export_email_error": "Nem sikerült exportálni az e-mailt", + "import_email_success": "E-mail sikeresen importálva", + "import_email_error": "Nem sikerült importálni az e-mailt", + "rule_created": "Szabály létrehozva", + "rule_merged": "Hozzáadva a(z) „{name}” szabályhoz", + "rule_already_covered": "Ezt már lefedi a(z) „{name}” szabály", + "rule_apply_existing": "{count, plural, one {Alkalmazás # meglévő üzenetre} other {Alkalmazás # meglévő üzenetre}}", + "rule_applied": "{count, plural, =0 {Egyik üzenetet sem kellett módosítani} one {A szabály # üzenetre alkalmazva} other {A szabály # üzenetre alkalmazva}}", + "rule_apply_failed": "Nem sikerült a szabályt a meglévő üzenetekre alkalmazni", + "rule_edit": "Szabály szerkesztése", + "rule_undo": "Visszavonás", + "rule_undone": "A szabálymódosítás visszavonva", + "rule_undo_failed": "Nem sikerült visszavonni a szabálymódosítást", + "rule_undo_conflict": "A szűrőid azóta megváltoztak. A módosítást a szűrőbeállításokban vond vissza.", + "rule_folder_failed": "Nem sikerült létrehozni a mappát", + "vacation_filters_save_failed": "Távolléti beállítások elmentve, de a továbbítást és a válasz címzettjeit nem sikerült menteni" + }, + "date": { + "today": "Ma", + "yesterday": "Tegnap", + "this_week": "Ezen a héten", + "last_week": "Múlt héten", + "this_month": "Ebben a hónapban", + "older": "Régebbi", + "just_now": "Épp most", + "minutes_ago": "{count} perce", + "minutes_ago_plural": "{count} perce", + "hours_ago": "{count} órája", + "hours_ago_plural": "{count} órája", + "days_ago": "{count} napja", + "days_ago_plural": "{count} napja" + }, + "language": { + "title": "Nyelv", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Nyelv kiválasztása", + "switch_to_english": "Váltás angolra", + "switch_to_french": "Váltás franciára", + "switch_to_japanese": "Váltás japánra", + "switch_to_spanish": "Váltás spanyolra", + "switch_to_italian": "Váltás olaszra", + "switch_to_german": "Váltás németre", + "switch_to_dutch": "Váltás hollandra", + "switch_to_portuguese": "Váltás portugálra", + "switch_to_russian": "Váltás oroszra", + "switching": "Nyelv váltása...", + "switch": "Nyelv váltása", + "current": "Aktuális nyelv", + "polish": "Polski", + "switch_to_polish": "Váltás lengyelre", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Váltás perzsára", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Váltás mongolra" + }, + "settings": { + "title": "Beállítások", + "back_to_mail": "Vissza a levelekhez", + "save_success": "Beállítások sikeresen elmentve", + "import_success": "Beállítások sikeresen importálva", + "import_error": "Nem sikerült importálni a beállításokat", + "reset_confirm": "Biztosan vissza akarod állítani az összes beállítást az alapértelmezettre?", + "unsaved_changes": "Nem mentett változtatások vannak", + "discard_changes": "Nem mentett változtatások elvetése?", + "discard": "Elvetés", + "keep_editing": "Szerkesztés folytatása", + "search_placeholder": "Beállítások keresése", + "search_clear": "Keresés törlése", + "search_no_results": "Nincs találat a beállításokban", + "tabs": { + "appearance": "Megjelenés", + "language": "Nyelv, régió és idő", + "email": "E-mail viselkedés", + "composer": "Szerkesztő", + "privacy": "Adatvédelem és biztonság", + "account": "Fiók", + "identities": "Azonosságok", + "vacation": "Távollét", + "advanced": "Speciális", + "calendar": "Naptár", + "filters": "Szűrők", + "templates": "Sablonok", + "folders": "Mappák", + "keywords": "Címkék", + "security": "Biztonság", + "files": "Fájlok", + "contacts": "Névjegyek", + "encryption": "Titkosítás", + "protocol_handlers": "Alapértelmezett alkalmazások", + "sidebar_apps": "Oldalsáv alkalmazások", + "notifications": "Értesítések", + "layout": "Elrendezés", + "themes": "Témák", + "reading": "Olvasás", + "composing": "Írás", + "downloads": "Letöltések", + "content_senders": "Tartalom és feladók", + "about_data": "Névjegy és adatok", + "debug": "Hibakeresés" + }, + "themes": { + "title": "Témák", + "description": "Válassz a rendszergazda által telepített témák és a beépített előbeállítások közül.", + "locked": "A témaválasztást egy rendszergazda zárolta.", + "forced": "A(z) „{name}” témát a rendszergazda írja elő, ezért nem módosítható", + "admin_theme": "Rendszergazdai téma", + "activated": "Téma aktiválva", + "default_restored": "Alapértelmezett téma visszaállítva", + "default_name": "Alapértelmezett", + "default_badge": "Alapértelmezett", + "admin_enforced": "Rendszergazda által előírva" + }, + "tab_groups": { + "general": "Általános", + "account": "Fiók és azonosság", + "organization": "Levelezés szervezése", + "apps": "Alkalmazások", + "system": "Rendszer", + "appearance": "Megjelenés", + "mail": "Levelek", + "privacy": "Adatvédelem és biztonság", + "advanced": "Speciális" + }, + "appearance": { + "message_list_order": { + "label": "Üzenetlista sorrendje", + "description": "Hogyan rendeződnek az üzenetek a listában. A rendezést a kiszolgáló végzi, így a teljes mappára vonatkozik, nem csak a betöltött üzenetekre.", + "preset": { + "chronological": "Időrendi (legújabb elöl)", + "unread_first": "Olvasatlanok elöl", + "starred_first": "Csillagozottak elöl", + "tagged_first": "Címkézettek elöl", + "custom": "Egyéni" + }, + "tag_label": "Címke", + "advanced_show": "Speciális: rendezési szintek szerkesztése", + "advanced_hide": "Speciális elrejtése", + "criterion_label": "{n}. szint feltétele", + "direction_label": "{n}. szint iránya", + "criteria": { + "unread": "Olvasatlan", + "starred": "Csillagozott", + "tag": "Címke", + "receivedAt": "Érkezés dátuma", + "sentAt": "Küldés dátuma", + "from": "Feladó", + "subject": "Tárgy", + "size": "Méret" + }, + "direction": { + "unread_first": "Olvasatlanok elöl", + "read_first": "Olvasottak elöl", + "starred_first": "Csillagozottak elöl", + "unstarred_first": "Csillag nélküliek elöl", + "tagged_first": "Címkézettek elöl", + "untagged_first": "Címkézetlenek elöl", + "newest_first": "Legújabb elöl", + "oldest_first": "Legrégebbi elöl", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Legnagyobb elöl", + "smallest_first": "Legkisebb elöl" + }, + "add_level": "Szint hozzáadása", + "remove_level": "Szint eltávolítása", + "move_up": "Feljebb", + "move_down": "Lejjebb", + "tiebreak_note": "Legfeljebb három szint; a további szintek döntenek holtverseny esetén. A „legújabb elöl” mindig utolsóként érvényesül.", + "scope": { + "label": "Alkalmazás minden mappára", + "description": "Kikapcsolva a sorrend csak a Beérkezett üzenetekre vonatkozik; a többi mappa időrendi marad." + }, + "unsupported_note": "A levelezőkiszolgáló nem támogatja az olvasottság, csillag vagy címke szerinti rendezést. Ezeket a szinteket figyelmen kívül hagyja, és a lista időrendi marad." + }, + "title": "Megjelenés", + "description": "Webmail megjelenésének testreszabása", + "theme": { + "label": "Téma", + "description": "Válaszd ki a preferált színösszeállítást", + "light": "Világos", + "dark": "Sötét", + "system": "Rendszer" + }, + "language": { + "label": "Nyelv", + "description": "Válaszd ki a preferált nyelvet" + }, + "font_size": { + "label": "Betűméret", + "description": "Szövegméret beállítása a jobb olvashatóság érdekében", + "small": "Kicsi", + "medium": "Közepes", + "large": "Nagy" + }, + "list_density": { + "label": "Sűrűség", + "description": "Távolság és kitöltés szabályozása a felületen", + "extra_compact": "Extra kompakt", + "compact": "Kompakt", + "regular": "Normál", + "comfortable": "Kényelmes" + }, + "interface_layout": { + "label": "Felület elrendezése", + "description": "Hol helyezkedik el a keresés, a navigáció és az eszköztárak", + "default": "Alapértelmezett", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Animációk engedélyezése", + "description": "Simább átmenetek és effektek megjelenítése" + }, + "toolbar_position": { + "label": "Eszköztár pozíciója", + "description": "Hol jelenjenek meg az e-mail műveleti gombok (Válasz, Archiválás, Törlés, stb.)", + "top": "Felül", + "below_subject": "Tárgy alatt" + }, + "toolbar_labels": { + "label": "Eszköztár feliratok mutatása", + "description": "Szöveges feliratok megjelenítése az eszköztár ikonok mellett. Kapcsold ki a helytakarékosság érdekében, ha már ismered az ikonokat." + }, + "hide_account_switcher": { + "label": "Oldalsáv fiókváltó elrejtése", + "description": "Fiókválasztó elrejtése a mappa oldalsáv tetején. A fiókokat továbbra is válthatod az alsó navigációs sávból." + }, + "show_rail_account_list": { + "label": "Fiók avatárok mutatása a navigációs sávon", + "description": "Egyedi fiókkörök megjelenítése a navigációs sáv alján a gyors váltáshoz, kijelentkezés gombbal alatta." + }, + "unified_mailbox": { + "label": "Egységes postafiók", + "description": "Összevont mappák (Beérkező, Elküldött, stb.) megjelenítése az összes csatlakoztatott fiók között", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Csoportos postafiókok belefoglalása", + "description": "Megosztott/csoportos postafiókok egyesítése az egységes nézetbe." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Színes oldalsáv ikonok", + "description": "Mappa és címke ikonok színezése típus szerint (kék Beérkező, piros Spam, zöld Elküldött, stb.). Kapcsold ki az egyszínű oldalsávhoz." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Összes üzenet számának megjelenítése", + "description": "Megjeleníti az üzenetek teljes számát a mappák és címkék mellett, az olvasatlanok számán túl. Kapcsolja ki, ha csak az olvasatlanok számát szeretné látni." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Megosztott levelezőfiókok megjelenítése", + "description": "A veled megosztott postafiókok mappáinak megjelenítése az oldalsávon, a Megosztott alatt. Kapcsold ki az összes elrejtéséhez, vagy rejts el egyes fiókokat lent. A naptárak és névjegyek megmaradnak." + }, + "pro_interface": { + "label": "Pro felület (Kísérleti)", + "description": "Asztali számítógépes erőfelhasználói elrendezés több lapos üzenetböngészéssel és fiókok közötti munkafolyamatokkal. A szabványos felületet nem érinti; bármikor visszaválthatsz.", + "open_label": "Pro felület megnyitása", + "back_to_standard": "Vissza a szabványoshoz" + }, + "cross_unread": { + "label": "Összes fiók: Olvasatlan", + "description": "Megjelenít egy bejegyzést az Összes fiók szakaszban, amely az összes fiók olvasatlan leveleit listázza, a levélszemét, elküldött, archívum, kuka és piszkozatok mappák kivételével minden mappára kiterjedően." + }, + "cross_starred": { + "label": "Összes fiók: Csillagozott", + "description": "Megjelenít egy bejegyzést az Összes fiók szakaszban, amely az összes fiók csillagozott leveleit listázza, a levélszemét, elküldött, archívum, kuka és piszkozatok mappák kivételével minden mappára kiterjedően." + }, + "cross_all": { + "label": "Összes fiók: Összes levél", + "description": "Megjelenít egy bejegyzést az Összes fiók szakaszban, amely az összes fiók összes levelét listázza, a levélszemét, elküldött, archívum, kuka és piszkozatok mappák kivételével minden mappára kiterjedően." + } + }, + "keywords": { + "title": "E-mail címkék", + "description": "Címkék definiálása az e-mailek színekkel történő rendezéséhez. Ezek JMAP kulcsszavakként tárolódnak a szerveren.", + "add_keyword": "Címke hozzáadása", + "label_field": "Megjelenített név", + "label_placeholder": "pl. Munka, Személyes, Sürgős", + "id_field": "Címke azonosító", + "id_placeholder": "pl. munka, szemelyes", + "color_field": "Szín", + "id_exists": "Ez a címke azonosító már létezik", + "edit": "Címke szerkesztése", + "delete": "Címke törlése", + "save": "Mentés", + "add": "Hozzáadás", + "cancel": "Mégse", + "migrating": "Címke frissítése a meglévő e-maileken...", + "migration_error": "Nem sikerült frissíteni a címkét a meglévő e-maileken", + "filters_migration_error": "Néhány szűrőszabály még a régi címkét használja – frissítse őket kézzel", + "nesting": { + "label": "Beágyazott címkék", + "description": "Ágyazzon címkéket más címkék alá, és jelenítse meg őket fastruktúraként az oldalsávon." + }, + "parent_field": "Szülőcímke", + "no_parent": "Nincs szülőcímke", + "too_long": "Ez a címkeútvonal túl hosszú (legfeljebb {max} karakter)", + "has_children_locked": "Más címkék vannak beágyazva ez alá, ezért a neve és a szülőcímkéje zárolva van. Előbb helyezze át vagy távolítsa el őket.", + "has_children_delete": "Előbb távolítsa el az ez alá beágyazott címkéket", + "visibility_field": "Láthatóság az oldalsávon", + "visibility": { + "show": "Megjelenítés", + "unread": "Megjelenítés olvasatlanoknál", + "hide": "Elrejtés" + }, + "unrecognized": { + "title": "Címkék helyreállítása a szerverről", + "description": "A címkék nevét és színét ez a böngésző tárolja, maguk a címkék azonban JMAP kulcsszavakként az e-mailjein vannak. Keresse meg a levelein azokat a kulcsszavakat, amelyekhez itt nem tartozik címke, majd adja hozzá őket újra.", + "scan": "Címkék keresése", + "rescan": "Keresés újra", + "scanning": "Keresés… {total} e-mailből {scanned}", + "none": "Az e-mailjein szereplő összes címke már definiálva van itt.", + "messages": "{count, plural, one {# e-mail} other {# e-mail}}", + "add_all": "Mind a(z) {count} hozzáadása", + "partial": "A(z) {total} e-mailből csak a {scanned} legutóbbi lett átvizsgálva. A csak régebbi leveleken használt címkék hiányozhatnak.", + "error": "Nem sikerült átvizsgálni a leveleket címkék után." + } + }, + "notifications": { + "test_sound": "Értesítési hang tesztelése", + "sounds": { + "default": "Alapértelmezett (sípolás)", + "cheerful": "Vidám", + "involved": "Lelkes", + "swift": "Gyors mozdulat", + "relax": "Pihenés" + }, + "push": { + "title": "Háttérértesítések", + "description": "Rendszerértesítések fogadása új levelekről, amikor ez az oldal be van zárva. A Bulwark push relén keresztül kézbesítve; a relay soha nem látja a levél tartalmát.", + "relay_label": "Push relé", + "relay_desc": "Alapértelmezés szerint a tárolt Bulwark relay. Csak akkor módosítsd, ha sajátot üzemeltetsz.", + "relay_locked": "Rendszergazda által beállítva", + "relay_locked_desc": "A push relay URL-t a rendszergazda állította be, és nem módosítható.", + "status_active": "Aktív ezen az eszközön", + "status_inactive": "Nincs engedélyezve ezen az eszközön", + "status_unsupported": "Ez a böngésző nem támogatja a Web Push-t", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "Munka...", + "enable": "Engedélyezés", + "reenable": "Újraregisztráció", + "disable": "Letiltás", + "confirm_disable_title": "Háttérértesítések letiltása?", + "confirm_disable_message": "Ez az eszköz nem fog figyelmeztetéseket kapni, amikor az oldal be van zárva.", + "dismiss_aria": "Értesítési ablak elutasítása", + "ios_hint": "iOS-en először telepítsd az oldalt a kezdőképernyőre - a Safari csak telepített PWA-knak kézbesít Web Push értesítéseket.", + "devices_title": "Regisztrált eszközök", + "devices_desc": "Minden eszköz, amely háttérértesítéseket kap ehhez a fiókhoz. A visszavonás után az adott eszköz azonnal nem kap többet.", + "devices_loading": "Regisztrált eszközök betöltése…", + "devices_empty": "Nincs eszköz regisztrálva háttérértesítésekhez.", + "devices_error": "A regisztrált eszközöket nem sikerült betölteni.", + "devices_refresh": "Frissítés", + "device_this": "Ez az eszköz", + "device_other": "Másik eszköz", + "device_status_active": "Értesítéseket kézbesít", + "device_status_inactive": "Még soha nem kézbesített értesítést", + "device_status_unknown": "Ez az átjáró nem ismeri", + "device_expires": "Lejár: {date}", + "revoke": "Visszavonás", + "confirm_revoke_title": "Visszavonja ezt az eszközt?", + "confirm_revoke_message": "Azonnal nem kap több háttérértesítést. Ha az eszközön újra bekapcsolja a push értesítéseket, újra regisztrálódik.", + "confirm_revoke_message_this": "Ez az eszköz azonnal nem kap több háttérértesítést." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Értesítési hang", + "description": "Válaszd ki az értesítésekhez használt hangot", + "choose": "Hang", + "choose_desc": "Válassz ki egy értesítési hangot, és kattints a hangszóró ikonra az előnézethez" + }, + "email": { + "title": "E-mail értesítések", + "description": "Értesítések beállítása a beérkező e-mailekhez", + "enabled": "E-mail értesítések", + "enabled_desc": "Értesítések megjelenítése új e-mailek érkezésekor", + "sound": "Értesítési hang", + "sound_desc": "Hangjelzés lejátszása új e-mailek érkezésekor", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Naptár értesítések", + "description": "Értesítések beállítása a naptári eseményekhez", + "enabled": "Esemény értesítések", + "enabled_desc": "Figyelmeztetések megjelenítése a közelgő naptári eseményekhez", + "sound": "Értesítési hang", + "sound_desc": "Hangjelzés lejátszása naptári emlékeztetőkhöz", + "invitation_parsing": "E-mail meghívók feldolgozása", + "invitation_parsing_desc": "Naptármeghívók észlelése az e-mail csatolmányokban és naptár műveletek megjelenítése" + } + }, + "language_region": { + "title": "Nyelv, régió és idő", + "description": "Nyelv, dátumformátum, időformátum és egyéb regionális beállítások", + "language": { + "label": "Nyelv", + "description": "Válaszd ki a preferált nyelvet", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Dátum formátum", + "description": "Hogyan jelenjenek meg a dátumok az e-mail listában", + "smart": "Intelligens", + "relative": "Relatív", + "full": "Mindig teljes dátum", + "preview_today": "Ma:", + "preview_this_week": "Ezen a héten:", + "preview_older": "Régebbi:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Idő formátum", + "description": "Válassz a 12 vagy 24 órás időformátum között", + "12h": "12 órás", + "24h": "24 órás" + }, + "first_day": { + "label": "Hét első napja", + "description": "Hét kezdése vasárnappal vagy hétfővel", + "sunday": "Vasárnap", + "monday": "Hétfő" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Keresés törlése mappaváltáskor", + "description": "Egy mappára kattintva (Beérkezett, Elküldött…) törlődik az aktív keresés, és a mappa jelenik meg. Kikapcsolva a keresés megmarad, és újra lefut a választott mappában." + }, + "request_read_receipt": { + "label": "Olvasási visszaigazolás kérése alapértelmezés szerint", + "description": "Az olvasási visszaigazolás kérésének előre bekapcsolása új üzenet írásakor." + }, + "read_receipt_response": { + "label": "Válasz az olvasási visszaigazolásokra", + "description": "Mi történjen, ha egy beérkező üzenet olvasási visszaigazolást kér.", + "ask": "Kérdezzen minden alkalommal", + "always": "Mindig elküldi", + "never": "Soha nem küldi el" + }, + "title": "E-mail viselkedés", + "description": "Az e-mailek kezelésének beállítása", + "mark_read": { + "label": "Olvasottnak jelölés", + "description": "Mikor jelölje olvasottnak az e-maileket megnyitáskor", + "instant": "Azonnal", + "delay_3s": "3 másodperc után", + "delay_5s": "5 másodperc után", + "never": "Soha" + }, + "delete_action": { + "label": "Törlés művelet", + "description": "Mi történjen az e-mail törlésekor", + "trash": "Áthelyezés a Kukába", + "trash_and_read": "Áthelyezés a Kukába és olvasottnak jelölés", + "permanent": "Végleges törlés", + "warning": "Az e-mailek véglegesen törlődnek és nem állíthatók vissza. Ez a művelet visszafordíthatatlan." + }, + "message_spacing": { + "label": "Üzenet térköze", + "description": "Térköz az üzenet törzse körül olvasáskor", + "auto": "Automatikus (ajánlott)", + "always": "Mindig legyen térköz", + "edge": "Széltől szélig" + }, + "plain_text_font": { + "label": "Egyszerű szöveg betűtípusa", + "description": "Az egyszerű szöveges e-mailekhez használt betűtípus", + "mono": "Rögzített szélességű", + "sans": "Normál (mint az alkalmazás)" + }, + "archive_mode": { + "label": "Archiválás helye", + "description": "Hogyan rendezze az e-maileket archiváláskor", + "single": "Egyetlen mappa", + "year": "Mappa évenként", + "month": "Mappa havonta", + "reorganize": "Meglévő archívum átszervezése", + "reorganize_success": "{count, plural, =0 {Nincs átszervezendő e-mail} =1 {1 e-mail átszervezve} other {# e-mail átszervezve}}", + "reorganize_error": "Nem sikerült átszervezni az archívumot" + }, + "permanently_delete_junk": { + "label": "Spam végleges törlése", + "description": "E-mailek végleges törlése a Spam mappából ahelyett, hogy a Kukába kerülnének" + }, + "confirm_bulk_delete": { + "label": "Megerősítés több üzenet törlése előtt", + "description": "Kérdezzen, mielőtt több kijelölt üzenetet a Kukába helyez. A végleges törlés mindig kérdez." + }, + "mail_layout": { + "label": "Levelezés elrendezés", + "description": "Válassz a klasszikus osztott olvasóablak, a Gmail-stílusú fókuszált olvasás vagy a Zimbra-stílusú alsó olvasóablak között.", + "split": "Osztott ablak", + "split_description": "Üzenetlista és olvasóablak egymás mellett.", + "focus": "Fókuszált lista", + "focus_description": "Egy sor üzenetenként, és a levél teljes szélességben nyílik meg, miközben a mappa oldalsáv látható marad.", + "horizontal": "Olvasóablak alul", + "horizontal_description": "Üzenetlista felül, a kiválasztott üzenet pedig egy olvasóablakban alatta." + }, + "list_columns": { + "label": "Lista megjelenítése oszlopokban", + "description": "Az egysoros sorokat táblázatként jeleníti meg Feladó, Tárgy, Dátum és Méret oszlopokkal egy fejléc alatt." + }, + "show_preview": { + "label": "Előnézeti szöveg mutatása", + "description": "E-mail előnézet megjelenítése a listában", + "focus_description": "Beágyazott előnézeti szöveg megjelenítése a fókuszált egysoros üzenetlistában" + }, + "show_verification_codes": { + "label": "Ellenőrző kódok megjelenítése", + "description": "A bejelentkezési és megerősítő e-mailek kódjának felajánlása egy kattintásos másolásra, a listában egy napig és az üzenetben" + }, + "disable_threading": { + "label": "Beszélgetéscsoportosítás letiltása", + "description": "E-mailek megjelenítése egyedi üzenetként ahelyett, hogy beszélgetésekbe lennének csoportosítva" + }, + "plain_text_mode": { + "label": "Csak szöveges mód", + "description": "Rich text szerkesztő letiltása és az összes e-mail küldése csak szöveges formátumban, beleértve a válaszokat és továbbításokat" + }, + "auto_select_reply_identity": { + "label": "Válasz a fogadott címről", + "description": "Válaszküldéskor arról a címről küldjön, amelyre az üzenet eredetileg érkezett. Először identitásokat keres; tartományi catch-all kézbesítések esetén átírja a Feladó fejlécet az aliasra, miközben az elsődleges identitáson keresztül küld." + }, + "reply_identity_match": { + "label": "Fogadó cím egyeztetése", + "description": "Mely fogadó címek számítanak a sajátjának. A „Csak pontos cím” kizárólag a beállított identitásai közül választ; az „azonos domain” az identitás-domainjein lévő bármely más címet is catch-all aliasként kezeli, és arra írja át a Feladó fejlécet. Válassza a pontos címet, ha ezek a címek terjesztési listák.", + "exact": "Csak pontos cím", + "domain": "Bármely cím a domainjeimen" + }, + "signature_position": { + "label": "Aláírás pozíciója", + "description": "Hova kerüljön az aláírás a válaszokban és továbbításokban. Az idézett szöveg felett természetes lezárásként olvasható; alatta az eredeti üzenet marad egybefüggő.", + "above_quote": "Idézett szöveg előtt", + "below_quote": "Idézett szöveg után" + }, + "signature_separator": { + "label": "Aláírás elválasztó", + "description": "Aláírás előtagjaként a szabványos \"-- \" elválasztó sor (RFC 3676). Kapcsold ki, ha közvetlenül az üzenetből szeretnél az aláírásba folyni." + }, + "sub_address_delimiter": { + "label": "Alcím elválasztó", + "description": "Karakter, amely elválasztja a felhasználónevet az alcím címkétől. Illeszkedjen a levelezőszerver által használt elválasztóhoz (pl. felhasználó{delimiter}cimke@domain.com).", + "option": "{delimiter} (felhasználó{delimiter}cimke@domain.com)", + "custom": "Egyéni...", + "custom_input_label": "Egyéni elválasztó karakter" + }, + "attachment_click_action": { + "label": "Csatolmány kattintási művelet", + "description": "Válaszd ki, hogy a csatolmányra kattintva előnézet vagy azonnali letöltés történjen", + "preview": "Előnézet, ha lehetséges", + "download": "Azonnali letöltés" + }, + "attachment_position": { + "label": "Csatolmány pozíciója", + "description": "Hol jelenjenek meg a csatolmányok az e-mail fejlécben", + "beside-sender": "Feladó mellett", + "below-header": "Fejléc alatt" + }, + "emails_per_page": { + "10": "10 e-mail", + "25": "25 e-mail", + "50": "50 e-mail", + "100": "100 e-mail", + "label": "E-mailek oldalanként", + "description": "Egyszerre betöltendő e-mailek száma" + }, + "always_light_mode": { + "label": "E-mailek mindig világos módban", + "description": "E-mail tartalom megjelenítése világos módban akkor is, ha az alkalmazás sötét módban van, elkerülve a sötét mód konverziós problémáit" + }, + "external_content": { + "label": "Külső tartalom", + "description": "Képek és külső tartalmak kezelése", + "ask": "Mindig kérdezzen", + "block": "Mindig blokkoljon", + "allow": "Mindig engedélyezzen" + }, + "trusted_senders": { + "label": "Megbízható feladók", + "description": "Azon feladók kezelése, akiknek a képei automatikusan betöltődnek", + "count_zero": "Nincs", + "count_one": "1 feladó", + "count_other": "{count} feladó", + "modal_title": "Megbízható feladók", + "empty_title": "Még nincsenek megbízható feladók", + "empty_description": "Amikor blokkolt képekkel rendelkező e-mailt nézel, kattints a \"Feladó megbízható\" gombra a hozzáadáshoz.", + "add_manually": "Feladó kézi hozzáadása", + "add_button": "Hozzáadás", + "add_placeholder": "E-mail cím megadása", + "search_placeholder": "Feladók keresése...", + "no_results": "Nincs a keresésnek megfelelő feladó", + "remove": "Eltávolítás", + "close": "Bezárás", + "invalid_email": "Kérjük, adj meg egy érvényes e-mail címet", + "already_added": "Ez a feladó már megbízható", + "save_error": "Nem sikerült menteni - ellenőrizd a Névjegyek hibakeresési naplót a részletekért", + "use_address_book_label": "Szinkronizálás a címjegyzékkel", + "use_address_book_description": "Megbízható feladók tárolása egy dedikált \"Megbízható feladók\" címjegyzékben, hogy szinkronizálódjanak az összes eszközöd között" + }, + "hover_actions": { + "label": "Gyors egérműveletek", + "description": "Válaszd ki, mely gyorsműveletek jelenjenek meg az e-mail fölé húzva a listában", + "delete": "Törlés", + "star": "Csillagozás / Csillagozás megszüntetése", + "mark_read": "Olvasott / Olvasatlan jelölés", + "archive": "Archiválás", + "tag": "Címkézés", + "spam": "Megjelölés spamként", + "not_spam": "Nem spam", + "none_selected": "Nincs művelet kiválasztva", + "mode_label": "Megjelenítési mód", + "mode_inline": "Beágyazott", + "mode_floating": "Lebegő", + "corner_label": "Lebegő pozíció", + "corner_top-left": "Bal felső", + "corner_top-right": "Jobb felső", + "corner_bottom-left": "Bal alsó", + "corner_bottom-right": "Jobb alsó" + }, + "default_mail_program": { + "label": "Alapértelmezett levelezőprogram", + "description": "{appName} regisztrálása alapértelmezett levelezőprogramként a mailto: hivatkozásokhoz", + "button": "Beállítás alapértelmezettként", + "success": "Böngésző felszólítva az alapértelmezett beállításra", + "error": "A böngésződ nem támogatja ezt a funkciót" + }, + "attachment_reminder": { + "label": "Csatolmány emlékeztető", + "description": "Figyelmeztetés küldés előtt, ha az üzenet csatolmányokat említ, de nincs csatolva", + "keywords_label": "Trigger kulcsszavak", + "keywords_description": "Szavak vagy kifejezések, amelyek kiváltják az emlékeztetőt, ha megtalálhatók az üzenetben", + "add_placeholder": "Kulcsszó hozzáadása...", + "add": "Hozzáadás", + "remove": "Eltávolítás" + }, + "empty_subject_warning": { + "label": "Figyelmeztetés üres tárgy esetén", + "description": "Megerősítés kérése tárgy nélküli üzenet küldése előtt." + }, + "recipient_mentions": { + "label": "Címzettek említése @ jellel", + "description": "Ha @ jelet ír az üzenetbe, megjelennek a címzettek, és a kiválasztott keresztneve kerül beszúrásra. Az Enter vagy a Tab a kiemelt nevet választja." + }, + "mention_adds_recipient": { + "label": "Névjegyek a @ említéseknél", + "description": "A @ beírásakor a névjegyeid és a címtárban szereplő személyek is megjelennek. Ha olyat választasz, aki még nem címzett, hozzáadjuk az üzenethez.", + "to": "Hozzáadás: Címzett", + "cc": "Hozzáadás: Másolat", + "off": "Csak a címzettek" + }, + "hide_inline_image_attachments": { + "label": "Beágyazott képek elrejtése a csatolmányok közül", + "description": "Az üzenet törzsébe ágyazott képek nem jelennek meg külön csatolmányként" + }, + "attachment_image_previews": { + "label": "Kép előnézetek mutatása a csatolmányokban", + "description": "Képcsatolmányok megjelenítése miniatűr kártyákként az általános fájlikonok helyett" + }, + "send_delay": { + "label": "Küldés visszavonása / késleltetett küldés", + "description": "A normál küldések késleltetése egy rövid, szerver oldali időablakkal.", + "off": "Kikapcsolva", + "seconds": "{seconds} másodperc", + "unsupported": "A jelenlegi fiók nem támogatja a késleltetett küldést. A beállítás más fiókokhoz elmentésre kerül." + }, + "return_to_list_after_action": { + "label": "Visszatérés a listához törlés vagy olvasatlanként jelölés után", + "description": "A megnyitott üzenet törlése vagy olvasatlanként való megjelölése után visszatérés az üzenetlistához a következő üzenet megnyitása helyett." + }, + "swipe_right_action": { + "label": "Jobbra húzás művelete (mobil)", + "description": "Érintőképernyős eszközökön az a művelet, amely akkor hajtódik végre, amikor egy üzenetet jobbra húz a listában." + }, + "swipe_left_action": { + "label": "Balra húzás művelete (mobil)", + "description": "Érintőképernyős eszközökön az a művelet, amely akkor hajtódik végre, amikor egy üzenetet balra húz a listában." + }, + "swipe_actions": { + "none": "Nincs", + "archive": "Archiválás", + "delete": "Törlés", + "mark_read": "Olvasott állapot váltása", + "star": "Csillag váltása", + "spam": "Megjelölés spamként" + }, + "rtl_editing": { + "label": "Jobbról balra írás támogatása", + "description": "Iránygombot ad a szerkesztő eszköztárához, amellyel a bekezdések balról jobbra vagy jobbról balra állíthatók" + } + }, + "composer": { + "title": "Szerkesztő", + "description": "E-mail írási beállítások konfigurálása", + "autosave": { + "label": "Automatikus mentés időköze", + "description": "Milyen gyakran mentse a piszkozatokat automatikusan", + "30s": "30 másodpercenként", + "1m": "Percre", + "2m": "2 percenként", + "5m": "5 percenként" + }, + "send_confirmation": { + "label": "Küldés megerősítés", + "description": "Megerősítés kérése e-mailek küldése előtt" + }, + "default_reply": { + "label": "Alapértelmezett válasz mód", + "description": "Alapértelmezett művelet a válasz gombra kattintáskor", + "reply": "Válasz", + "reply_all": "Válasz mindenkinek" + } + }, + "privacy": { + "title": "Adatvédelem és biztonság", + "description": "Adatvédelmi és biztonsági beállítások kezelése", + "external_images": { + "label": "Külső képek blokkolása", + "description": "Követés megakadályozása külső képeken keresztül" + }, + "session_timeout": { + "label": "Munkamenet időtúllépés", + "description": "Automatikus kijelentkezés inaktivitás után", + "never": "Soha", + "30m": "30 perc", + "1h": "1 óra", + "4h": "4 óra" + }, + "clear_cache": { + "label": "Gyorsítótár törlése", + "description": "Gyorsítótárazott adatok és ideiglenes fájlok eltávolítása", + "button": "Gyorsítótár törlése", + "confirm": "Biztosan törölni szeretnéd a gyorsítótárat?", + "success": "Gyorsítótár sikeresen törölve" + } + }, + "account": { + "title": "Fiók", + "description": "Fiók információid megtekintése", + "name_label": "Megjelenített név", + "username_label": "Felhasználónév", + "account_type_label": "Fiók típusa", + "auth_method_label": "Hitelesítés", + "auth_method_oauth": "Egyszeri bejelentkezés (OAuth/OIDC)", + "auth_method_basic": "Jelszó", + "auth_method_token": "Hozzáférési token", + "demo_account": "Demó fiók", + "email": { + "label": "E-mail cím", + "value": "{email}" + }, + "server": { + "label": "JMAP szerver", + "value": "{server}" + }, + "storage": { + "label": "Tárhely használat", + "used": "{used} / {total} használva", + "percentage": "{percent}% használva" + }, + "last_sync": { + "label": "Utolsó szinkronizálás", + "value": "{time}" + }, + "accounts": { + "title": "Bejelentkezett fiókok", + "description": "Húzd át a sorrend módosításához, hogy a fiókok hogyan jelenjenek meg a fiók legördülőben", + "active": "Jelenleg aktív fiók", + "default_badge": "Alapértelmezett fiók", + "set_default": "Beállítás alapértelmezettként", + "switch_to": "Váltás erre a fiókra", + "move_up": "Mozgatás fel", + "move_down": "Mozgatás le", + "drag_handle": "Húzd át a sorrend módosításához", + "add": "Fiók hozzáadása" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Velem megosztva", + "description": "Csoportos és megosztott fiókok, amelyeket kezelhet. Válasszon egyet a szűrői, távolléti válasza, naptárai és névjegyei szerkesztéséhez.", + "shared_label": "Megosztott fiók" + } + }, + "security": { + "title": "Fiók biztonság", + "description": "Jelszó, kétfaktoros hitelesítés és biztonsági beállítások kezelése", + "detecting": "Szerver képességek érzékelése...", + "not_available": "A fiók biztonsági kezelés nem érhető el ehhez a levelezőszerverhez. A szükséges engedélyek lehet, hogy le vannak tiltva. Lásd a dokumentációt a részletekért.", + "password": { + "title": "Jelszó módosítása", + "current": "Jelenlegi jelszó", + "new": "Új jelszó", + "confirm": "Új jelszó megerősítése", + "submit": "Jelszó módosítása", + "success": "Jelszó sikeresen módosítva", + "error_title": "Jelszó módosítás sikertelen", + "error_mismatch": "Az új jelszavak nem egyeznek", + "error_min_length": "A jelszónak legalább 8 karakter hosszúnak kell lennie", + "error_generic": "Nem sikerült módosítani a jelszót" + }, + "display_name": { + "label": "Megjelenített név", + "description": "Neved, ahogy a szerveren megjelenik", + "placeholder": "Add meg a megjelenített neved", + "save": "Mentés", + "success": "Megjelenített név frissítve", + "error": "Nem sikerült frissíteni a megjelenített nevet" + }, + "totp": { + "section_title": "Kétfaktoros hitelesítés", + "label": "TOTP hitelesítés", + "description": "Extra biztonsági réteg egy időalapú egyszeri jelszóval", + "active": "Engedélyezve", + "inactive": "Letiltva", + "enabled": "Kétfaktoros hitelesítés engedélyezve", + "disabled": "Kétfaktoros hitelesítés letiltva", + "enable_error": "Nem sikerült engedélyezni a 2FA-t", + "disable_error": "Nem sikerült letiltani a 2FA-t", + "setup_instructions": "Másold ezt az URL-t a hitelesítő alkalmazásodba (Google Authenticator, Authy, stb.):", + "verification_code": "Ellenőrző kód", + "confirm": "Megerősítés", + "disable": "Letiltás", + "disable_confirm_prompt": "Add meg a jelszavad a kétfaktoros hitelesítés letiltásához.", + "password_required": "Jelszó megadása kötelező", + "code_required": "Ellenőrző kód megadása kötelező", + "code_invalid": "Érvénytelen ellenőrző kód. Ellenőrizd a hitelesítő alkalmazásod és próbáld újra." + }, + "app_passwords": { + "title": "Alkalmazás jelszavak", + "description": "Jelszavak létrehozása olyan alkalmazásokhoz, amelyek nem támogatják a kétfaktoros hitelesítést", + "add": "Hozzáadás", + "create": "Létrehozás", + "cancel": "Mégse", + "done": "Kész", + "generate": "Generálás", + "name_label": "Alkalmazás neve", + "name_placeholder": "pl. Thunderbird, iPhone Mail", + "expires_label": "Lejárat (opcionális)", + "allowed_ips_label": "Engedélyezett IP-k (opcionális)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Vesszővel vagy szóközzel elválasztva. Hagyd üresen bármely IP engedélyezéséhez.", + "password_label": "Jelszó (hagyd üresen az automatikus generáláshoz)", + "password_placeholder": "Automatikusan generált, ha üres", + "copy_now_warning": "Másold ki most ezt a jelszót - nem lesz újra megjelenítve.", + "added": "Alkalmazás jelszó létrehozva", + "removed": "Alkalmazás jelszó eltávolítva", + "add_error": "Nem sikerült létrehozni az alkalmazás jelszót", + "remove_error": "Nem sikerült eltávolítani az alkalmazás jelszót", + "none": "Nincs alkalmazás jelszó beállítva" + }, + "api_keys": { + "title": "API kulcsok", + "description": "API kulcsok létrehozása szkriptekhez és integrációkhoz, amelyek közvetlenül a szerverrel kommunikálnak", + "name_label": "Kulcs neve", + "name_placeholder": "pl. Biztonsági mentés szkript, CI futató", + "copy_now_warning": "Másold ki most ezt az API kulcsot - nem lesz újra megjelenítve.", + "added": "API kulcs létrehozva", + "removed": "API kulcs eltávolítva", + "add_error": "Nem sikerült létrehozni az API kulcsot", + "remove_error": "Nem sikerült eltávolítani az API kulcsot", + "none": "Nincs API kulcs beállítva" + }, + "encryption": { + "section_title": "Titkosítás nyugalmi állapotban", + "label": "E-mail titkosítás", + "description": "Tárolt e-mailek titkosítása a szerveren a további adatvédelem érdekében", + "active": "{type} titkosítás engedélyezve", + "inactive": "Letiltva", + "enabled": "Titkosítás nyugalmi állapotban engedélyezve", + "disabled_success": "Titkosítás nyugalmi állapotban letiltva", + "error": "Nem sikerült frissíteni a titkosítási beállításokat", + "algorithm_label": "Titkosítási algoritmus", + "encrypt_on_append": "Új e-mailek titkosítása feltöltéskor", + "allow_spam_training": "Spam-tanítás engedélyezése az e-mailek titkosítása előtt", + "enable_encryption": "Titkosítás engedélyezése", + "enable_tooltip": "Titkosítás engedélyezése ezzel a kulccsal", + "configure_title": "Titkosítás engedélyezése ezzel a kulccsal", + "enable_button": "Engedélyezés", + "disable_tooltip": "Titkosítás letiltása", + "enabled_success": "Titkosítás nyugalmi állapotban engedélyezve" + }, + "email_client": { + "title": "E-mail kliens beállítás", + "description": "Használd ezeket a hitelesítő adatokat az asztali vagy mobil e-mail kliensed beállításához (Thunderbird, Apple Mail, Outlook, stb.)", + "jmap_username_label": "JMAP felhasználónév", + "copy": "Másolás", + "copied": "Másolva", + "password_instructions": "Használd a fenti JMAP felhasználóneved egy alkalmazás jelszóval együtt az e-mail kliensbe való bejelentkezéshez. Hozz létre egy alkalmazás jelszót a fenti részben, ha még nem tetted." + }, + "link_device": { + "title": "Mobilalkalmazás összekapcsolása", + "description": "Jelentkezz be a telefonodon a Bulwark Mail alkalmazásba anélkül, hogy ott beírnád a jelszavad. A telefonod saját bejelentkezést kap, így ha itt kijelentkezel, a telefon bejelentkezve marad.", + "generate": "QR-kód megjelenítése", + "regenerate": "Új kód megjelenítése", + "instructions": "Nyisd meg a Bulwark Mail alkalmazást, és a bejelentkezési képernyőn koppints a \"Scan a sign-in code\" lehetőségre (vagy az \"Add account\" → \"Scan a sign-in code\" lehetőségre, ha már be vagy jelentkezve), majd irányítsd ide a kamerát.", + "expires_in": "Ez a kód {seconds} másodperc múlva lejár. Csak egyszer használható.", + "expired": "Ez a kód lejárt.", + "error": "Nem sikerült párosítási kódot létrehozni. Próbáld újra.", + "password_prompt": "Add meg a jelszavad, hogy megerősítsd, te vagy az. A telefonod saját bejelentkezést kap; a jelszavad nem kerül át a telefonra.", + "show_password": "Jelszó megjelenítése", + "hide_password": "Jelszó elrejtése", + "continue": "Tovább", + "error_wrong_password": "Hibás jelszó. Próbáld újra.", + "totp_prompt": "Add meg a hitelesítő alkalmazásodban látható kódot.", + "error_password_or_code": "A jelszó vagy a kód hibás.", + "error_not_signed_in": "A munkameneted lejárt. Jelentkezz be újra az eszköz összekapcsolásához.", + "error_impersonation": "Nem kapcsolhatsz össze eszközt, amíg egy másik felhasználó postafiókját nézed.", + "error_too_many_attempts": "Túl sok próbálkozás. Várj egy kicsit, és próbáld újra.", + "error_session_secret": "Az eszközök összekapcsolása nincs beállítva ezen a szerveren (hiányzik a SESSION_SECRET). Fordulj a rendszergazdához.", + "error_server_unreachable": "Nem sikerült elérni a levelezőszerveredet. Próbáld újra.", + "error_pairing_unavailable": "A levelezőszervered nem teszi lehetővé az eszközök ilyen módon történő összekapcsolását. Fordulj a rendszergazdához.", + "qr_alt": "Bejelentkezési QR-kód", + "link_label": "Bejelentkezési hivatkozás", + "copy_link": "Bejelentkezési hivatkozás másolása", + "copied": "Másolva", + "copy_hint": "Nincs kamerád? Másold ki a hivatkozást, és illeszd be az alkalmazásban a \"Paste sign-in link\" lehetőséggel.", + "success_title": "Bejelentkezve a telefonodon", + "success_body": "Bezárhatod ezt az oldalt. Másik eszköz összekapcsolásához jeleníts meg egy új kódot.", + "link_another": "Másik eszköz összekapcsolása", + "use_sso": "Egyszeri bejelentkezés használata helyette", + "error_insecure_server": "A mobilalkalmazás csak https-en keresztül csatlakozik levelezőszerverekhez, ez pedig http-t használ." + }, + "public_keys": { + "name_label": "Nyilvános kulcs neve", + "name_placeholder": "pl. A kulcsom", + "key_label": "Nyilvános kulcs (S/MIME vagy PGP ASCII formátumban)", + "key_placeholder": "Illessze be ide a nyilvános kulcsát", + "none": "Nincs beállítva nyilvános kulcs" + } + }, + "identities": { + "title": "Küldő azonosságok", + "description": "Azon e-mail címek kezelése, amelyekről küldhetsz", + "identities_count": { + "label": "Azonosságaid", + "description": "Küldésre beállított e-mail címek", + "count_zero": "Nincs azonosság", + "count_one": "1 azonosság", + "count_other": "{count} azonosság" + }, + "manage": "Azonosságok kezelése", + "sub_addressing": { + "label": "Alcímzés", + "description": "Címkék használata, mint pl. felhasználó+tag@domain.com a bejövő levelek rendezéséhez", + "learn_more": "További információk" + } + }, + "vacation": { + "title": "Távollét", + "description": "A beérkező e-mailek automatikus kezelése távolléted alatt", + "loading": "Automatikus válasz beállítások betöltése...", + "not_supported": "A levelezőszervered nem támogatja az automatikus válaszokat.", + "fetch_error": "Nem sikerült betölteni az automatikus válasz beállításokat. Kérjük, próbáld újra.", + "status": { + "label": "Automatikus válasz küldése", + "description": "Automatikus válasz küldése azoknak, akik e-mailt írnak neked", + "active": "Aktív", + "inactive": "Inaktív" + }, + "date_range": { + "start": "Kezdő dátum", + "start_description": "Hagyd üresen, ha nincs kezdő korlát", + "end": "Befejező dátum", + "end_description": "Hagyd üresen, ha nincs befejező korlát" + }, + "message": { + "subject_label": "Tárgy", + "subject_description": "Az automatikus válasz tárgysora", + "subject_placeholder": "Távol vagyok", + "body_label": "Üzenet szövege", + "body_description": "Egyszerű szöveges üzenet tartalma", + "body_placeholder": "Köszönöm az e-mailed. Jelenleg távol vagyok az irodától, és visszatérésemkor válaszolok.", + "html_label": "Formázott üzenet (HTML)", + "html_description": "Adjon hozzá egy formázott változatot hivatkozásokkal és stílussal. Azok a címzettek, akiknek a levelezőprogramja nem tudja megjeleníteni, a fenti egyszerű szöveget látják.", + "html_placeholder": "Írjon formázott távolléti választ…" + }, + "preview": { + "title": "Előnézet", + "show": "Előnézet mutatása", + "hide": "Előnézet elrejtése" + }, + "save": "Változtatások mentése", + "saving": "Mentés...", + "warnings": { + "end_before_start": "A befejező dátumnak a kezdő dátum után kell lennie", + "invalid_date": "Adjon meg érvényes kezdő és befejező dátumot időponttal, vagy hagyja üresen a mezőt", + "start_in_past": "A kezdő dátum a múltban van", + "empty_body": "Az üzenet szövege üres - a címzettek üres választ kapnak", + "subject_too_long": "A tárgy túl hosszú a kiszolgálónak (legfeljebb {max} bájt).", + "body_too_long": "Az üzenet túl hosszú a kiszolgálónak (legfeljebb {max} bájt a formázással együtt).", + "forward_address": "Adjon meg egy érvényes címet a továbbításhoz", + "not_running": "A továbbítás vagy a válasz címzettjei el vannak mentve, de most nem aktívak. Mentéssel újra bekapcsolja őket." + }, + "forward": { + "title": "Továbbítás", + "description": "A beérkező üzeneteket egy másik címre továbbítja, automatikus válasszal vagy anélkül, a fenti időszakban (befejező dátum nélkül a kikapcsolásig). A levélszemét nem lesz továbbítva.", + "enabled_label": "Üzenetek továbbítása", + "to_label": "Továbbítás", + "to_placeholder": "email@example.com", + "keep_label": "Megtartás a beérkezőben", + "keep_description": "Ellenkező esetben az üzenet csak továbbítva lesz: nem marad ebben a postafiókban, és egyetlen szűrőszabály sem fut rá." + }, + "audience": { + "label": "Válasz küldése", + "description": "Belső feladók: {domains}", + "all": "Minden feladónak", + "internal": "Csak belső feladóknak", + "external": "Csak külső feladóknak" + }, + "auto_reply": { + "title": "Automatikus válasz" + } + }, + "folders": { + "role_memos": "Jegyzetek", + "title": "Mappák", + "description": "E-mail mappák kezelése és szabványos szerepkörök hozzárendelése", + "folder_list": "Mappáid", + "folder_list_description": "Kattints egy mappa ikonra a testreszabáshoz", + "standard_roles": "Szabványos mappa szerepkörök", + "standard_roles_description": "Válaszd ki, mely mappák szolgálnak szabványos postafiók szerepkörökként, mint Beérkező, Elküldött, Kuka, stb.", + "role_inbox": "Beérkező", + "role_drafts": "Piszkozatok", + "role_sent": "Elküldött", + "role_trash": "Kuka", + "role_junk": "Spam", + "role_archive": "Archívum", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Nincs", + "create_folder": "Mappa létrehozása", + "create_subfolder": "Almappa létrehozása", + "reorder": "Húzza az átrendezéshez", + "reorder_error": "Nem sikerült átrendezni a mappákat", + "move_error": "Nem sikerült áthelyezni a mappát", + "select_folder": "{name} kijelölése", + "select_hint": "Kijelölés. Shift+kattintás kijelöli az összes mappát az utoljára kattintottig.", + "selected_count": "{count, plural, one {# mappa} other {# mappa}} kijelölve", + "move_to": "Áthelyezés ide...", + "move_to_top_level": "Legfelső szint", + "move": "Áthelyezés", + "clear_selection": "Kijelölés törlése", + "folders_moved": "{count, plural, one {# mappa áthelyezve} other {# mappa áthelyezve}}", + "move_partial_error": "{count, plural, one {# mappát} other {# mappát}} nem sikerült áthelyezni", + "subfolder_of": "Itt: {name}", + "subfolder_name": "Almappa neve", + "new_folder_name": "Mappa neve", + "rename": "Átnevezés", + "change_icon": "Ikon módosítása", + "count_mode": "Számláló: {name}", + "count_mode_default": "Számláló: alapértelmezett", + "count_mode_unread": "Csak olvasatlan", + "count_mode_all": "Olvasatlan és összes", + "count_mode_none": "Nincs számláló", + "delete": "Törlés", + "confirm_delete": "Biztosan törölni szeretnéd a \"{name}\" mappát? A mappában lévő e-mailek a Kukába kerülnek.", + "create": "Létrehozás", + "cancel": "Mégse", + "no_folders": "Nincsenek egyéni mappák", + "cannot_delete_role": "Nem törölhető szabványos szerepkörrel rendelkező mappa. Távolítsd el előbb a szerepkört.", + "folder_created": "Mappa létrehozva", + "folder_renamed": "Mappa átnevezve", + "folder_deleted": "Mappa törölve", + "role_updated": "Mappa szerepkör frissítve", + "error_create": "Nem sikerült létrehozni a mappát", + "error_rename": "Nem sikerült átnevezni a mappát", + "error_delete": "Nem sikerült törölni a mappát", + "error_delete_has_children": "Nem törölhető a mappa: almappákat tartalmaz. Előbb töröld vagy helyezd át azokat.", + "error_delete_has_email": "Nem törölhető a mappa: e-maileket tartalmaz. Előbb helyezd át vagy töröld azokat.", + "error_role": "Nem sikerült frissíteni a mappa szerepkört" + }, + "advanced": { + "title": "Speciális", + "description": "Speciális opciók és fejlesztői beállítások", + "debug_mode": { + "label": "Hibakeresési mód", + "description": "Részletes naplózás engedélyezése hibaelhárításhoz" + }, + "debug_categories": { + "description": "Válaszd ki a naplózandó kategóriákat. Kapcsold ki a nem szükséges kategóriákat a konzol zaj csökkentéséhez.", + "jmap": "JMAP kliens", + "jmap_description": "Postafiók műveletek, e-mail lekérések és JMAP protokoll kérések", + "calendar": "Naptár", + "calendar_description": "Naptári események, importálások és ütemezési üzenetek", + "tasks": "Feladatok", + "tasks_description": "Naptári feladatok létrehozása, lekérése és frissítése", + "auth": "Hitelesítés", + "auth_description": "Bejelentkezés, TOTP, token csere és munkamenet kezelés", + "filters": "Szűrők", + "filters_description": "Sieve szűrőszabályok és automatikus válasz szkriptek", + "email": "E-mail megtekintés", + "email_description": "E-mail megjelenítés, TNEF feldolgozás és olvasottnak jelölés", + "push": "Push értesítések", + "push_description": "Push értesítés beállítása és kézbesítése", + "contacts": "Névjegyek és címjegyzékek", + "contacts_description": "Névjegy szinkronizálás, címjegyzék műveletek és megbízható feladók" + }, + "settings_sync": { + "label": "Beállítások szinkronizálása", + "description": "Beállítások szinkronizálása a böngészők és eszközök között" + }, + "sender_favicons": { + "label": "Feladó faviconok", + "description": "Weboldal ikonok megjelenítése profilképekként az üzleti feladók számára", + "refresh_label": "Feladóikonok frissítése", + "refresh_description": "A webhelyikonok újbóli letöltése, például ha egy feladó lecserélte a sajátját", + "refresh_button": "Frissítés", + "refresh_done": "A feladóikonok újra le lesznek töltve" + }, + "show_avatars_in_junk": { + "label": "Avatárok mutatása a Spam mappában", + "description": "Profilképek és faviconok megjelenítése a feladókhoz a spam mappában. Alapértelmezés szerint letiltva, hogy ne adjon vizuális legitimitást az adathalász kísérleteknek." + }, + "keyboard_shortcuts": { + "label": "Billentyűparancsok", + "description": "Elérhető billentyűparancsok megtekintése", + "button": "Parancsok megtekintése" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Beállítások visszaállítása", + "description": "Összes beállítás visszaállítása az alapértelmezett értékekre", + "button": "Visszaállítás alapértelmezettre" + }, + "export_settings": { + "label": "Beállítások exportálása", + "description": "Beállítások letöltése JSON-ként", + "button": "Exportálás" + }, + "import_settings": { + "label": "Beállítások importálása", + "description": "Beállítások feltöltése JSON fájlból", + "button": "Importálás" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Forráskód" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Kattints a spamre és az adathalász levelekre, mielőtt elérik a beérkezett leveleidet. A valódi leveleket engedd át. Ha mindhárom pajzsot elveszíted, a postafiók elesik.", + "kinds": { + "spam": "Spam", + "phishing": "Adathalászat", + "legit": "Valódi levél" + }, + "click_to_block": "Kattints a blokkoláshoz", + "let_through": "Engedd át", + "start": "Indítás", + "play_again": "Újra", + "score": "Pontszám", + "combo": "Sorozatbónusz", + "shields_left": "{total} pajzsból {count} maradt", + "seconds": "{seconds} mp", + "best": "Rekord: {score}", + "new_best": "Új rekord", + "held": "A postafiók kitartott", + "overrun": "A postafiók elesett", + "stats": { + "caught": "Blokkolva", + "delivered": "Kézbesítve", + "mistakes": "Hibák" + } + } + }, + "sidebar_apps": { + "title": "Oldalsáv alkalmazások", + "description": "Egyedi alkalmazások és hivatkozások kezelése az oldalsávon", + "keep_loaded": "Alkalmazások betöltve tartása", + "keep_loaded_description": "Beágyazott alkalmazások futtatása a háttérben váltáskor az újratöltés elkerülése érdekében", + "managed_title": "A rendszergazda biztosítja", + "managed_description": "Ezeket az alkalmazásokat mindenki számára beállították, és nem módosíthatók.", + "manage_title": "Egyedi alkalmazások", + "manage_description": "Egyedi alkalmazások hozzáadása, szerkesztése vagy eltávolítása az oldalsávról" + }, + "contacts": { + "title": "Névjegyek", + "description": "Névjegyek importálása és exportálása", + "group_by_letter_label": "Csoportosítás kezdőbetű szerint", + "sort_by_last_name_label": "Rendezés vezetéknév szerint", + "sort_by_last_name_description": "A névjegylista vezetéknév szerinti rendezése, hogy a családtagok egymás mellett legyenek", + "group_by_letter_description": "ABC sorrendű fejléc mutatása a névjegy listában", + "import_label": "Névjegyek importálása", + "import_description": "Névjegyek importálása vCard (.vcf) fájlból", + "export_label": "Névjegyek exportálása", + "export_description": "Összes névjegy exportálása vCard (.vcf) fájlként", + "manage_title": "Címjegyzékek", + "manage_description": "Címjegyzékeid átnevezése", + "no_address_books": "Nincs címjegyzék", + "categories_title": "Kategóriák", + "categories_description": "Névjegy kategóriák átnevezése", + "no_categories": "Nincs kategória" + }, + "downloads": { + "title": "Letöltések", + "description": "Letöltött e-mailek és csatolmányok elnevezésének testreszabása.", + "reset": "Alapértelmezett visszaállítása", + "preview": "Előnézet:", + "email_template": { + "label": "E-mail (.eml) fájlnév", + "description": "Sablon e-mailek exportálásakor vagy fájlrendszerbe húzásakor. A .eml kiterjesztés automatikusan hozzáadásra kerül." + }, + "attachment_template": { + "label": "Csatolmány fájlnév", + "description": "Sablon csatolmány letöltésekor vagy kihúzásakor. Ha kihagyod a '{filename}' és '{ext}' elemeket, az eredeti kiterjesztés megmarad." + }, + "bundle_template": { + "label": "Több e-mail .zip fájlnév", + "description": "Sablon több kijelölt e-mail .zip archívumként történő kihúzásakor vagy letöltésekor. A .zip kiterjesztés automatikusan hozzáadásra kerül." + }, + "spaces": { + "label": "Szóközök", + "description": "Szóközök cseréje a fájlnévben egy másik karakterre.", + "keep": "Szóközök megtartása", + "underscore": "Csere _ karakterre", + "dash": "Csere - karakterre" + }, + "lowercase": { + "label": "Kisbetűs", + "description": "Teljes fájlnév kisbetűssé alakítása." + }, + "strip_diacritics": { + "label": "Ékezetek eltávolítása", + "description": "Ékezetes betűk átalakítása ASCII megfelelőjükre (á → a, é → e). Hasznos olyan eszközökhöz, amelyek torzítják az Unicode fájlneveket." + }, + "collapse_separators": { + "label": "Ismétlődő elválasztók összevonása", + "description": "Szóközök, aláhúzásjelek vagy kötőjelek sorozatának összevonása egyetlen karakterré." + }, + "after_export": { + "label": "Exportálás után", + "description": "Opcionálisan helyezd át az e-mailt .eml exportálása után.", + "keep": "Megtartás a postafiókban", + "archive": "Áthelyezés az archívumba", + "trash": "Áthelyezés a kukába" + } + }, + "filters": { + "title": "E-mail szűrők", + "description": "Szabályok létrehozása a beérkező e-mailek automatikus rendezéséhez, címkézéséhez és kezeléséhez", + "add_rule": "Szabály hozzáadása", + "no_rules": "Nincs szűrőszabály", + "no_rules_description": "Hozz létre szabályokat a beérkező e-mailek automatikus rendezéséhez", + "vacation_active": "Automatikus válasz aktív", + "vacation_active_description": "Automatikus válasz engedélyezve a beérkező üzenetekhez", + "vacation_configure": "Beállítás", + "edit_rule": "Szabály szerkesztése", + "new_rule": "Új szabály", + "delete_rule": "Szabály törlése", + "delete_confirm": "Biztosan törölni szeretnéd ezt a szabályt?", + "enable": "Engedélyezés", + "disable": "Letiltás", + "raw_editor": "Nyers Sieve szerkesztő", + "raw_editor_warning": "A nyers Siev szkript szerkesztése megszakíthatja a vizuális szabályszerkesztést. Az itt végzett módosítások felülírják a vizuális szerkesztőt.", + "validate": "Ellenőrzés", + "validation_success": "A szkript érvényes", + "validation_error": "A szkript hibákat tartalmaz", + "save": "Szabályok mentése", + "saving": "Mentés...", + "saved": "Szűrők sikeresen elmentve", + "save_failed": "Nem sikerült menteni a szűrőket", + "loading": "Szűrők betöltése...", + "not_supported": "A levelezőszervered nem támogatja az e-mail szűrőket.", + "rule_name": "Szabály neve", + "rule_name_placeholder": "pl. Hírlevelek rendezése", + "match_all": "MINDEN feltétel teljesüljön", + "match_any": "BÁRMELYIK feltétel teljesüljön", + "conditions": "Feltételek", + "add_condition": "Feltétel hozzáadása", + "actions": "Műveletek", + "add_action": "Művelet hozzáadása", + "stop_processing": "További szabályok feldolgozásának leállítása", + "include_spam": "A levélszemétnek jelölt üzenetek áthelyezése is", + "attachment_type_placeholder": "pl. pdf, doc, jpg", + "value_placeholder_multi": "Érték (több érték vesszővel elválasztva)", + "condition_fields": { + "attachment": "Csatolmány", + "from": "Feladó", + "to": "Címzett", + "cc": "Másolat", + "subject": "Tárgy", + "header": "Egyéni fejléc", + "size": "Méret", + "body": "Törzs", + "all": "Minden üzenet" + }, + "comparators": { + "has_any": "jelen van", + "has_type": "típusú", + "contains": "tartalmazza", + "not_contains": "nem tartalmazza", + "is": "pontosan", + "not_is": "nem", + "starts_with": "kezdődik", + "ends_with": "végződik", + "matches": "illeszkedik a mintára", + "greater_than": "nagyobb, mint", + "less_than": "kisebb, mint", + "address_is": "címe pontosan", + "domain_is": "tartománya" + }, + "action_types": { + "move": "Áthelyezés mappába", + "copy": "Másolás mappába", + "forward": "Továbbítás", + "mark_read": "Olvasottnak jelölés", + "star": "Csillagozás", + "add_label": "Címke hozzáadása", + "discard": "Elvetés (csendes törlés)", + "reject": "Elutasítás üzenettel", + "keep": "Megtartás a beérkezőben", + "stop": "Feldolgozás leállítása" + }, + "move_to_folder": "Mappa kiválasztása", + "copy_to_folder": "Mappa kiválasztása", + "forward_to": "Továbbítás e-mail címre", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Másolat megtartása", + "forward_limit": "Továbbítási korlát üzenetenként ezen a kiszolgálón: {count}. A további továbbítások kimaradnak.", + "forward_unavailable": "Ez a kiszolgáló nem engedélyezi a továbbítást. A szűrőszabályok továbbítási műveletei nem lépnek érvénybe.", + "reject_message": "Elutasító üzenet", + "reject_placeholder": "Az e-mailed elutasításra került", + "label_name": "Címke neve", + "label_placeholder": "Címke kiválasztása", + "header_name": "Fejléc neve", + "header_placeholder": "pl. X-Mailing-List", + "size_bytes": "Méret bájtokban", + "size_placeholder": "pl. 1000000", + "system_managed": "Rendszer által kezelt szabály", + "opaque_warning": "Ezt a szkriptet a vizuális szerkesztőn kívül szerkesztették. Csak nyers Sieve szerkesztés érhető el.", + "open_sieve_editor": "Nyers Sieve szerkesztő megnyitása", + "fetch_error": "Nem sikerült betölteni a szűrőket", + "expanded_view": "Részletes nézet", + "expanded_view_description": "Szűrőszabályok megjelenítése részletes feltétel- és műveletblokkokkal", + "if": "Ha", + "then": "Akkor", + "match_all_conditions": "minden feltétel teljesül", + "match_any_condition": "bármelyik feltétel teljesül", + "and": "és", + "or": "vagy", + "cancel": "Mégse", + "confirm_delete": "Törlés", + "rule_list": "Szűrőszabályok", + "drag_to_reorder": "Húzd át a sorrend módosításához", + "match_type": "Egyezés típusa", + "reset_to_visual": "Visszaállítás vizuális szerkesztőre", + "reset_warning": "Ez elveti az aktuális szkriptet és újat kezd.", + "confirm_reset": "Visszaállítás", + "validation_empty_name": "A szabály neve kötelező", + "validation_empty_conditions": "Legalább egy feltétel szükséges értékkel", + "validation_empty_actions": "Legalább egy művelet szükséges", + "templates_section": "Kezdés sablonból", + "template_newsletters": "Hírlevelek áthelyezése mappába", + "template_receipts": "Nyugták automatikus archiválása", + "template_important": "Fontos e-mailek megjelölése", + "template_notifications": "Értesítések szűrése", + "sieve_editor": { + "title": "Sieve szkript szerkesztő", + "warning": "A nyers Sieve szkript szerkesztése megszakíthatja a vizuális szabályszerkesztést. Az itt végzett módosítások felülírják a vizuális szerkesztőt.", + "script_content": "Sieve szkript", + "valid": "A szkript érvényes", + "invalid": "A szkript hibákat tartalmaz", + "save_warning": "A mentés felülír minden vizuális szabályt. Ez nem vonható vissza. Kattints újra a Mentés gombra a megerősítéshez.", + "validating": "Ellenőrzés...", + "validate": "Ellenőrzés", + "cancel": "Mégse", + "save": "Mentés", + "confirm_save": "Mentés megerősítése", + "validation_failed": "Ellenőrzési kérés sikertelen" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# feltétel} other {# feltétel}}", + "actions_count": "{count, plural, one {# művelet} other {# művelet}}" + }, + "origin_external": "Külső", + "managed_by_tooltip": "Kezeli: {source}. Szerkeszd az alkalmazásban, vagy használd a nyers Sieve szerkesztőt.", + "quick_rule_names": { + "move_sender": "Áthelyezés: {sender} → {folder}", + "move_domain": "Áthelyezés: @{domain} → {folder}", + "move_list": "Áthelyezés: {list} lista → {folder}", + "mark_read": "{sender} olvasottnak jelölése", + "tag": "{sender} címkézése: {tag}", + "block": "{sender} letiltása", + "custom": "{sender} üzenetei", + "custom_plain": "Új szabály", + "senders": "{count, plural, one {# feladó} other {# feladó}}" + }, + "suggestions": "Javaslatok", + "suggest_subject": "Tárgy: „{subject}”", + "suggest_to": "Címzett: {address}", + "suggest_cc": "Másolat: {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Alkalmazás a mappában már meglévő üzenetekre is", + "apply_existing_unsupported": "Meglévő üzenetekre csak olyan szabály alkalmazható, amely a feladót, a címzetteket, a tárgyat vagy a fejléceket vizsgálja, és áthelyez, másol, olvasottnak jelöl, csillagoz vagy címkéz.", + "period_title": "Dátumtartomány", + "period_start": "Kezdő dátum", + "period_end": "Befejező dátum", + "validation_period_order": "A befejező dátumnak a kezdő dátum után kell lennie", + "period_toggle": "Csak egy időszakban aktív", + "period_hint": "Az ebben az időszakban érkező üzenetekre vonatkozik. Hagyjon egy mezőt üresen, ha azt a végét nem szeretné korlátozni.", + "validation_period_empty": "Adjon meg kezdő vagy befejező dátumot az időszakhoz", + "validation_period_invalid": "Adjon meg érvényes dátumot időponttal az időszakhoz, vagy hagyja üresen a mezőt", + "apply_existing_period": "Az időszakhoz kötött szabály csak az abban érkező üzenetekre hat, ezért meglévő üzenetekre nem alkalmazható.", + "period_status_scheduled": "ütemezett", + "period_status_active": "aktív", + "period_status_expired": "lejárt" + }, + "templates": { + "title": "E-mail sablonok", + "description": "Újrafelhasználható e-mail sablonok létrehozása helyettesítő változókkal", + "add": "Új sablon", + "edit": "Sablon szerkesztése", + "name": "Sablon neve", + "name_placeholder": "pl. Követő e-mail", + "category": "Kategória", + "category_placeholder": "pl. Munka, Személyes", + "subject": "Tárgy", + "subject_placeholder": "E-mail tárgysora", + "body": "Törzs", + "body_placeholder": "E-mail tartalma...", + "recipients_placeholder": "email@example.com", + "identity": "Küldés mint", + "default_identity": "Alapértelmezett azonosság", + "favorite": "Kedvenc", + "cancel": "Mégse", + "create": "Létrehozás", + "update": "Frissítés", + "confirm_delete": "Törlés", + "no_templates": "Még nincsenek sablonok", + "manage": "Sablonok kezelése", + "count": "{count, plural, one {# sablon} other {# sablon}}", + "export_import": "Exportálás és importálás", + "export_import_description": "Sablonok biztonsági mentése vagy áthelyezése másik eszközre", + "export": "Exportálás", + "import": "Importálás", + "validation": { + "empty": "A sablon neve kötelező", + "too_long": "A sablon neve legfeljebb 200 karakter lehet" + } + }, + "files": { + "display": { + "title": "Megjelenítés", + "description": "Fájlok és mappák megjelenésének beállítása" + }, + "default_view": { + "label": "Alapértelmezett nézet", + "description": "Válassz rács vagy lista elrendezés között", + "list": "Lista", + "grid": "Rács" + }, + "default_sort": { + "label": "Alapértelmezett rendezés", + "description": "A fájlok alapértelmezett rendezésének kiválasztása", + "name": "Név", + "size": "Méret", + "modified": "Módosítás dátuma" + }, + "sort_direction": { + "label": "Rendezés iránya", + "description": "Növekvő vagy csökkenő sorrend kiválasztása", + "ascending": "Növekvő", + "descending": "Csökkenő" + }, + "icons": { + "title": "Ikonok", + "description": "Fájlikonok megjelenésének beállítása" + }, + "show_icons": { + "label": "Fájlikonok mutatása", + "description": "Ikonok megjelenítése a fájlok és mappák mellett" + }, + "colored_icons": { + "label": "Színes ikonok", + "description": "Színes ikonok használata egyszínű helyett" + }, + "show_thumbnails": { + "label": "Miniatűrök mutatása", + "description": "Kép előnézetek megjelenítése ikonok helyett a képfájlokhoz" + }, + "behavior": { + "title": "Viselkedés", + "description": "Fájlböngésző viselkedésének beállítása" + }, + "show_hidden": { + "label": "Rejtett fájlok mutatása", + "description": "Ponttal kezdődő fájlok és mappák megjelenítése" + }, + "preview": { + "label": "Előnézet" + } + }, + "scoped": { + "back": "Vissza a saját fiókomhoz", + "managing": "Kezelés: {name}" + } + }, + "errors": { + "page_error_title": "Valami hiba történt", + "page_error_description": "Váratlan hibával találkoztunk. Kérjük, próbáld újra, vagy térj vissza a kezdőoldalra.", + "sidebar_error": "Nem sikerült betölteni a postafiókokat", + "email_list_error": "Nem sikerült betölteni az e-maileket", + "viewer_error_title": "Nem sikerült megjeleníteni az e-mailt", + "viewer_error_description": "Probléma merült fel az e-mail megjelenítésekor. Lehet, hogy nem támogatott tartalmat tartalmaz.", + "composer_error": "Nem sikerült betölteni a szerkesztőt", + "settings_error_title": "Beállítások nem elérhetők", + "settings_error_description": "Nem sikerült betölteni a beállításokat. A preferenciáid nem biztos, hogy elmentődnek.", + "try_again": "Próbáld újra", + "reload": "Újratöltés", + "reload_emails": "E-mailek újratöltése", + "reload_settings": "Beállítások újratöltése", + "retry": "Újra", + "go_home": "Ugrás a beérkezőhöz" + }, + "context_menu": { + "reply": "Válasz", + "reply_all": "Válasz mindenkinek", + "forward": "Továbbítás", + "mark_read": "Olvasottnak jelölés", + "mark_unread": "Olvasatlannak jelölés", + "star": "Csillagozás", + "unstar": "Csillagozás megszüntetése", + "pin": "Rögzítés", + "unpin": "Rögzítés feloldása", + "move_to": "Áthelyezés ide...", + "copy_to_account": "Másolás fiókba...", + "remove_from_folder": "Eltávolítás ebből a mappából", + "archive": "Archiválás", + "delete": "Törlés", + "mark_as_spam": "Spam jelentése", + "not_spam": "Nem spam", + "tag": "Címke", + "items_selected": "{count} e-mail kijelölve", + "forward_as_attachments": "Továbbítás mellékletként", + "edit_draft": "Piszkozat szerkesztése", + "cancel_scheduled_send": "Küldés megszakítása", + "reschedule_send": "Átütemezés", + "cancel_and_edit": "Mégse és szerkesztés", + "cancel_and_compose_again": "Mégse és újraírás", + "rules": { + "title": "Szabályok", + "move_from": "{sender} üzeneteinek automatikus áthelyezése", + "move_from_domain": "@{domain} üzeneteinek automatikus áthelyezése", + "move_from_list": "E lista üzeneteinek automatikus áthelyezése", + "mark_read_from": "{sender} üzeneteinek automatikus olvasottnak jelölése", + "tag_with": "Automatikus címkézés ezzel", + "block": "{count, plural, one {Feladó letiltása} other {# feladó letiltása}}", + "no_junk": "Ebben a fiókban nincs Levélszemét mappa", + "create": "Szabály létrehozása…", + "manage": "Szabályok kezelése", + "senders_count": "{count, plural, one {# feladó} other {# feladó}}", + "cross_account": "Egyetlen fiók üzeneteit válaszd ki", + "opaque_hint": "A szűrőidet kézzel szerkesztették. Nyisd meg a szűrőbeállításokat" , + "loading": "Szabályok betöltése…", + "load_failed": "Nem sikerült betölteni a szabályokat", + "none": "Még nincsenek szabályok", + "new_mail_only": "Csak új üzenetekre", + "running": "„{name}” futtatása itt: {folder}…" + }, + "find_emails_from": "{name} e-mailjeinek keresése" + }, + "mailbox_context_menu": { + "mark_folder_read": "Mappa olvasottnak jelölése", + "mark_folder_tree_read": "Mappa és almappák olvasottnak jelölése", + "mark_all_folders_read": "Összes mappa olvasottnak jelölése", + "new_subfolder": "Új almappa...", + "new_folder": "Új mappa...", + "rename": "Átnevezés...", + "share": "Megosztás...", + "import_email": "Importálás .eml vagy .zip fájlból...", + "empty_folder": "Mappa ürítése", + "empty_folder_generic": "Mappa ürítése", + "delete_folder": "Mappa törlése", + "refresh": "Frissítés", + "hide_shared_account": "Elrejtés az oldalsávról", + "mute_notifications": "Értesítések némítása", + "unmute_notifications": "Értesítések bekapcsolása", + "mute_account_notifications": "Fiók értesítéseinek némítása", + "unmute_account_notifications": "Fiók értesítéseinek bekapcsolása", + "reset_folder_notifications": "Mappák értesítési beállításainak visszaállítása", + "mark_all_confirm_title": "Összes mappa olvasottnak jelölése", + "mark_all_confirm_message": "Minden olvasatlan üzenetet olvasottnak jelölsz a személyes fiókodban?", + "delete_confirm_title": "Mappa törlése", + "delete_confirm_message": "Véglegesen törlöd a \"{name}\" mappát? Ez a művelet nem vonható vissza.", + "prompt_new_subfolder": "Add meg az új almappa nevét.", + "prompt_new_folder": "Add meg az új mappa nevét.", + "prompt_rename": "Add meg a mappa új nevét.", + "placeholder_folder_name": "Mappa neve", + "create": "Létrehozás", + "rename_confirm": "Átnevezés", + "toast_marked_read": "Mappa olvasottnak jelölve", + "toast_marked_read_count": "{count, plural, one {1 üzenet} other {# üzenet}} olvasottnak jelölve", + "toast_already_read": "Nincs olvasatlan üzenet", + "toast_marked_all_read": "Összes mappa olvasottnak jelölve", + "toast_emptied": "Mappa ürítve", + "toast_folder_created": "Mappa létrehozva", + "toast_folder_renamed": "Mappa átnevezve", + "toast_folder_deleted": "Mappa törölve", + "toast_error_mark_read": "Nem sikerült olvasottnak jelölni", + "toast_error_empty": "Nem sikerült üríteni a mappát", + "toast_error_create": "Nem sikerült létrehozni a mappát", + "toast_error_rename": "Nem sikerült átnevezni a mappát", + "toast_error_delete": "Nem sikerült törölni a mappát", + "toast_error_delete_has_children": "A mappának almappái vannak. Távolítsd el azokat először.", + "toast_error_delete_has_email": "A mappa nem üres. Ürítsd ki először.", + "delete_with_contents_title": "A mappa nem üres", + "delete_with_contents_message": "A(z) „{name}” mappa még {count, plural, one {1 üzenetet} other {# üzenetet}} tartalmaz. Törlöd a mappát az összes üzenetével együtt? Ez nem vonható vissza.", + "delete_with_contents_confirm": "Mappa és üzenetek törlése" + }, + "shortcuts": { + "title": "Billentyűparancsok", + "tip": "Nyomd meg a ? gombot bármikor a súgó megjelenítéséhez", + "sections": { + "navigation": "Navigáció", + "actions": "E-mail műveletek", + "global": "Globális", + "threads": "Beszélgetések", + "composer": "Szerkesztő" + }, + "navigation": { + "next_email": "Következő e-mail", + "previous_email": "Előző e-mail", + "open_email": "E-mail megnyitása", + "close_email": "Bezárás / Kijelölés megszüntetése", + "go_to_folder": "Ugrás mappára", + "next_unread": "Következő olvasatlan e-mail", + "no_more_unread": "Lejjebb a listában nincs több olvasatlan e-mail" + }, + "actions": { + "reply": "Válasz", + "reply_all": "Válasz mindenkinek", + "forward": "Továbbítás", + "star": "Csillagozás váltása", + "archive": "Archiválás", + "delete": "Törlés", + "mark_unread": "Olvasatlannak jelölés", + "mark_read": "Olvasottnak jelölés", + "toggle_spam": "Spam / Nem spam" + }, + "global": { + "compose": "Új e-mail írása", + "search": "Keresés fókusz", + "help": "Parancsok mutatása", + "refresh": "E-mailek frissítése", + "select_all": "Összes kijelölése", + "global_search": "Keresés mindenben", + "extend_selection": "Kijelölés bővítése lefelé / felfelé" + }, + "threads": { + "expand_collapse": "Beszélgetés kibontása/összecsukása", + "select_mountain_view": "Beszélgetés kijelölése" + }, + "composer": { + "send": "E-mail küldése", + "schedule_send": "Késleltetett küldés", + "template_picker": "Sablonválasztó megnyitása" + } + }, + "threads": { + "messages_one": "{count} üzenet", + "messages_other": "{count} üzenet", + "messages_tooltip": "{count, plural, one {# üzenet ebben a beszélgetésben} other {# üzenet ebben a beszélgetésben}}", + "expand": "Beszélgetés kibontása", + "collapse": "Beszélgetés összecsukása", + "loading": "Beszélgetés betöltése...", + "mark_read": "Beszélgetés olvasottnak jelölése", + "mark_unread": "Beszélgetés olvasatlannak jelölése", + "archive": "Beszélgetés archiválása", + "delete": "Beszélgetés törlése", + "star": "Beszélgetés csillagozása", + "unstar": "Beszélgetés csillagozásának megszüntetése", + "toggle_thread": "Beszélgetés váltása" + }, + "identities": { + "modal_title": "Küldő azonosságok kezelése", + "create_new": "Új azonosság létrehozása", + "edit_identity": "Azonosság szerkesztése", + "delete_confirm": "Törlöd ezt az azonosságot? Ez nem vonható vissza.", + "cannot_delete": "Ez az azonosság nem törölhető", + "primary_identity": "Elsődleges", + "set_as_primary": "Beállítás elsődlegesként", + "no_identities": "Nincs azonosság", + "display": { + "reply_to": "Válaszcím:", + "bcc": "Titkos másolat:", + "signature": "Aláírás:", + "preview": "Előnézet:" + }, + "validation_errors": { + "invalid_emails": "Érvénytelen e-mailek: {emails}", + "unknown_error": "Ismeretlen hiba" + }, + "form": { + "name_label": "Megjelenített név", + "name_placeholder": "pl. Munkahelyi e-mail, Személyes", + "name_required": "Név megadása kötelező", + "email_label": "E-mail cím", + "email_placeholder": "your.email@example.com", + "email_required": "E-mail megadása kötelező", + "email_invalid": "Kérjük, adj meg egy érvényes e-mail címet", + "email_immutable": "Az e-mail cím létrehozás után nem módosítható", + "reply_to_label": "Válaszcím (opcionális)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "Automatikus titkos másolat (opcionális)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Szöveges aláírás", + "html_signature_label": "HTML aláírás", + "signature_byte_counter": "{bytes} / {max} bájt", + "signature_byte_limit_reached": "Szerverkorlát elérve", + "save": "Azonosság mentése", + "cancel": "Mégse", + "creating": "Létrehozás...", + "updating": "Frissítés..." + }, + "sub_address": { + "button_tooltip": "Alcím használata", + "popover_title": "Alcím címke hozzáadása", + "tag_input_placeholder": "Címke megadása (pl. vasarlas)", + "preview_label": "Előnézet:", + "recent_tags": "Legutóbbi címkék", + "suggested_tags": "Javasolt", + "use_address": "Cím használata", + "invalid_tag": "A címke csak betűket, számokat és kötőjeleket tartalmazhat", + "tag_too_long": "A címke legfeljebb 30 karakter lehet", + "help_text": "A felhasználó{delimiter}cimke@domain.com címre küldött e-mailek a beérkeződbe érkeznek", + "validation": { + "empty": "A címke nem lehet üres", + "too_long": "A címke legfeljebb {max} karakter lehet", + "invalid_chars": "A címke csak betűket, számokat és kötőjeleket tartalmazhat" + } + }, + "badge": { + "sent_via": "ezen keresztül", + "sub_address_tag": "Küldve alcím használatával: {tag}", + "identity_name": "Küldve azonossággal: {name}", + "identity_short": "{name} néven", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Törlés", + "delete_confirm_title": "Azonosság törlése" + }, + "templates": { + "picker_title": "Sablon kiválasztása", + "search_placeholder": "Sablonok keresése...", + "section_favorites": "Kedvencek", + "section_recent": "Legutóbbi", + "section_uncategorized": "Egyéb", + "no_templates": "Még nincsenek sablonok", + "no_results": "Nincs találat", + "fill_placeholders": "Helyettesítő értékek kitöltése", + "enter_value": "Érték megadása...", + "preview": "Előnézet", + "insert_with_values": "Beszúrás értékekkel", + "insert_raw": "Nyers beszúrás", + "copy_suffix": "(másolat)", + "placeholder": "Változó", + "placeholders": { + "recipient_name": "Címzett neve", + "company": "Cégnév", + "date": "Aktuális dátum", + "day_of_week": "A hét napja", + "sender_name": "A te neved" + } + }, + "contacts": { + "directory": { + "title": "Címtár", + "search_placeholder": "Név vagy e-mail-cím keresése", + "refresh": "Címtár frissítése", + "retry": "Újra", + "loading": "Címtár betöltése…", + "unavailable_title": "Nincs címtár", + "unavailable_hint": "A levelezőszerver nem kínál fiókcímtárat.", + "forbidden_title": "A címtár nem érhető el", + "forbidden_hint": "A levelezőszerver nem engedi, hogy ez a fiók böngéssze a címtárat. Kérdezd a rendszergazdát.", + "error_title": "Nem sikerült betölteni a címtárat", + "error_hint": "Ellenőrizd a kapcsolatot, és próbáld újra.", + "empty_title": "A címtár üres", + "empty_hint": "A levelezőszerver nem sorol fel e-mail-címmel rendelkező fiókot.", + "no_match": "Senki sem felel meg a keresésnek a címtárban", + "refresh_failed": "Nem sikerült frissíteni a címtárat. A legutóbb betöltött lista látható.", + "select_page": "Mindenki kijelölése ezen az oldalon", + "deselect_page": "Kijelölés megszüntetése ezen az oldalon", + "select_entry": "{name} kijelölése", + "range": "{from}–{to} / {total}", + "page": "{page}. oldal / {count}", + "previous_page": "Előző oldal", + "next_page": "Következő oldal", + "picker_title": "Kiválasztás a címtárból", + "cancel": "Mégse", + "selected_count": "{count} kijelölve", + "add_to": "Hozzáadás: Címzett", + "add_cc": "Hozzáadás: Másolat", + "add_bcc": "Hozzáadás: Titkos másolat", + "select_hint": "Válassz ki valakit a részletekhez", + "write_email": "E-mail írása", + "add_to_contacts": "Hozzáadás a névjegyekhez", + "email": "E-mail", + "read_only_note": "A címtár bejegyzései a levelezőszerverről származnak, itt nem szerkeszthetők.", + "type": { + "individual": "Személy", + "group": "Csoport", + "resource": "Erőforrás", + "location": "Helyszín", + "other": "Egyéb" + } + }, + "title": "Névjegyek", + "search_placeholder": "Névjegyek keresése...", + "create_new": "Új névjegy", + "no_category": "Nincs kategória", + "rename_category": "Kategória átnevezése", + "category_name_label": "Kategória neve", + "category_renamed": "Kategória átnevezve", + "category_rename_failed": "Nem sikerült átnevezni a kategóriát", + "category_added": "Névjegy hozzáadva ide: {name}", + "category_added_plural": "{count} névjegy hozzáadva ide: {name}", + "empty_state": "Még nincsenek névjegyek", + "empty_state_title": "Még nincsenek névjegyek", + "empty_state_subtitle": "Hozd létre az első névjegyed vagy importálj vCard fájlból", + "empty_search": "Nincs a keresésnek megfelelő névjegy", + "empty_search_hint": "Próbálj meg más keresési kifejezést", + "empty_filtered": "Nincs a szűrőknek megfelelő névjegy", + "empty_filtered_hint": "Próbáld módosítani vagy törölni a szűrőket", + "clear_search": "Keresés törlése", + "import_vcard": "vCard importálása", + "delete_confirm_title": "Névjegy törlése", + "delete_confirm": "Biztosan törölni szeretnéd ezt a névjegyet?", + "local_mode": "A névjegyek helyben tárolódnak (a szerver nem támogatja a JMAP Névjegyeket)", + "back_to_contacts": "Vissza a névjegyekhez", + "back_to_email": "Vissza az e-mailekhez", + "open_categories": "Kategóriák megnyitása", + "tabs": { + "all": "Összes", + "groups": "Csoportok" + }, + "shared": { + "title": "Megosztott" + }, + "address_books": { + "create": "Új címjegyzék", + "created": "Címjegyzék létrehozva", + "create_failed": "Nem sikerült létrehozni a címjegyzéket", + "title": "Címjegyzékeim", + "shared_prefix": "Megosztott: {name}", + "moved": "Névjegy áthelyezve ide: {name}", + "moved_plural": "{count} névjegy áthelyezve ide: {name}", + "move_failed": "Nem sikerült áthelyezni a névjegyet", + "address_book": "Címjegyzék", + "rename": "Címjegyzék átnevezése", + "name_label": "Címjegyzék neve", + "renamed": "Címjegyzék átnevezve", + "rename_failed": "Nem sikerült átnevezni a címjegyzéket", + "default": "Alapértelmezett", + "set_default": "Beállítás alapértelmezettként", + "default_updated": "Alapértelmezett címjegyzék frissítve", + "set_default_failed": "Nem sikerült beállítani az alapértelmezett címjegyzéket", + "manage": "Címjegyzékek kezelése", + "share": "Címjegyzék megosztása", + "new_contact_in_book": "Új névjegy ebben a címjegyzékben", + "delete": "Címjegyzék törlése", + "confirm_delete": "Törlöd a \"{name}\" címjegyzéket? Az összes névjegy eltávolításra kerül.", + "deleted": "Címjegyzék törölve", + "delete_failed": "Nem sikerült törölni a címjegyzéket" + }, + "detail": { + "emails": "E-mail címek", + "phones": "Telefonszámok", + "organizations": "Szervezetek", + "addresses": "Címek", + "notes": "Megjegyzések", + "titles": "Címek és szerepkörök", + "online_services": "Online szolgáltatások", + "anniversaries": "Évfordulók", + "personal_info": "Személyes adatok", + "languages": "Nyelvek", + "categories": "Kategóriák", + "related_contacts": "Kapcsolódó névjegyek", + "crypto_keys": "Kriptográfiai kulcsok", + "cert_issuer": "Kibocsátó", + "cert_expires": "Lejárat", + "cert_expired": "Lejárt", + "cert_fingerprint": "Ujjlenyomat", + "cert_algorithm": "Algoritmus", + "import_to_smime": "Importálás S/MIME-be", + "cert_already_imported": "Már importálva S/MIME-be", + "cert_imported": "Tanúsítvány importálva az S/MIME tárolóba", + "cert_import_failed": "Nem sikerült importálni a tanúsítványt", + "no_contact_selected": "Válassz ki egy névjegyet a részletek megtekintéséhez", + "compose_email": "E-mail írása", + "copy_email": "E-mail másolása", + "copy_phone": "Telefonszám másolása", + "copy_url": "URL másolása", + "copied": "Vágólapra másolva", + "copy_failed": "Nem sikerült a vágólapra másolni", + "created": "Létrehozva", + "updated": "Utolsó frissítés", + "timezone": "Időzóna", + "anniversary_birth": "Születésnap", + "anniversary_death": "Elhunyt", + "anniversary_wedding": "Házassági évforduló", + "anniversary_other": "Egyéb", + "personal_expertise": "Szakértelem", + "personal_hobby": "Hobbi", + "personal_interest": "Érdeklődés", + "personal_other": "Egyéb", + "gender": "Nem", + "gender_masculine": "Férfi", + "gender_feminine": "Nő", + "gender_other": "Egyéb", + "gender_none": "Nem alkalmazható", + "gender_unknown": "Ismeretlen", + "calendar": "Naptár", + "calendar_uri": "Naptár URL", + "scheduling_uri": "Ütemezési URL", + "freebusy_uri": "Szabad/elfoglalt URL", + "section_contact": "Névjegy adatok", + "section_work": "Munka", + "section_personal": "Személyes", + "email_default_label": "E-mail", + "phone_default_label": "Telefon", + "address_default_label": "Cím", + "online_service_default_label": "Online", + "organization_label": "Szervezet", + "title_label": "Cím", + "role_label": "Szerepkör", + "language_label": "Nyelv", + "related_default_label": "Kapcsolódó", + "more_actions": "További műveletek", + "age_years": "{count, plural, one {1 éves} other {# éves}}", + "years_since": "{count, plural, one {1 év} other {# év}}" + }, + "activity": { + "recent_emails": "Legutóbbi e-mailek", + "upcoming_events": "Közelgő események", + "no_emails": "Nincsenek legutóbbi e-mailek", + "no_events": "Nincsenek közelgő események", + "no_subject": "(Nincs tárgy)", + "no_title": "(Nincs cím)", + "load_failed": "Nem sikerült betölteni", + "unknown_sender": "Ismeretlen feladó", + "all_day": "Egész napos" + }, + "form": { + "create_title": "Új névjegy", + "edit_title": "Névjegy szerkesztése", + "section_address_book": "Címtár", + "select_address_book": "Címtár kiválasztása...", + "section_identity": "Név és azonosság", + "contact_type": "Névjegy típusa", + "type_person": "Személy", + "type_organization": "Szervezet", + "section_work": "Munka és szervezet", + "prefix": "Előtag", + "prefix_placeholder": "Dr., Úr., Mrs.", + "given_name": "Keresztnév", + "middle_name": "Középső név", + "surname": "Vezetéknév", + "suffix": "Utótag", + "suffix_placeholder": "Ifj., Sr., III", + "nickname": "Becenév", + "nickname_placeholder": "Becenév", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Telefon", + "phone_placeholder": "+36 20 123 4567", + "phone_type": "Típus", + "phone_voice": "Vonalas", + "phone_cell": "Mobil", + "phone_fax": "Fax", + "phone_pager": "Személyhívó", + "phone_video": "Videó", + "phone_text": "SMS", + "organization": "Szervezet", + "organization_placeholder": "Cégnév", + "department": "Részleg", + "department_placeholder": "Részleg", + "job_title": "Beosztás", + "job_title_placeholder": "pl. Szoftvermérnök", + "role": "Szerepkör", + "role_placeholder": "pl. Csapatvezető", + "addresses": "Címek", + "add_address": "Cím hozzáadása", + "street": "Utca", + "city": "Város", + "region": "Megye / Régió", + "postcode": "Irányítószám", + "country": "Ország", + "online_services": "Online szolgáltatások", + "add_online_service": "Online szolgáltatás hozzáadása", + "url_placeholder": "https://...", + "service_placeholder": "Szolgáltatás", + "anniversaries": "Évfordulók", + "add_anniversary": "Dátum hozzáadása", + "anniversary_birth": "Születésnap", + "anniversary_wedding": "Házassági évforduló", + "anniversary_death": "Megemlékezés", + "anniversary_other": "Egyéb", + "personal_info": "Személyes adatok", + "add_personal_info": "Bejegyzés hozzáadása", + "personal_info_placeholder": "pl. Fotózás", + "personal_expertise": "Szakértelem", + "personal_hobby": "Hobbi", + "personal_interest": "Érdeklődés", + "personal_other": "Egyéb", + "level": "Szint", + "level_high": "Magas", + "level_medium": "Közepes", + "level_low": "Alacsony", + "categories": "Kategóriak", + "categories_placeholder": "pl. Család, Barátok, Kollégák", + "categories_hint": "Írj a kereséshez vagy kategóriák hozzáadásához", + "category_add": "Hozzáadás", + "note": "Megjegyzések", + "note_placeholder": "Megjegyzés hozzáadása...", + "gender": "Nem", + "gender_sex": "Nem", + "gender_male": "Férfi", + "gender_female": "Nő", + "gender_other": "Egyéb", + "gender_none": "Nem alkalmazható", + "gender_unknown": "Ismeretlen", + "gender_identity": "Nem identitás", + "gender_identity_placeholder": "Nem identitás...", + "calendar": "Naptár", + "calendar_uri": "Naptár URL", + "scheduling_uri": "Ütemezési URL", + "freebusy_uri": "Szabad/elfoglalt URL", + "context_work": "Munka", + "context_private": "Személyes", + "add_email": "E-mail hozzáadása", + "add_phone": "Telefon hozzáadása", + "save": "Mentés", + "cancel": "Mégse", + "creating": "Létrehozás...", + "updating": "Frissítés...", + "name_required": "Adjon meg egy keresztnevet, vezetéknevet vagy szervezetet", + "email_invalid": "Kérjük, adj meg egy érvényes e-mail címet", + "email_error_inline": "Érvénytelen e-mail formátum", + "save_failed": "Nem sikerült menteni a névjegyet", + "delete": "Törlés", + "upload_photo": "Fotó feltöltése", + "remove_photo": "Fotó eltávolítása", + "photo_hint": "JPG vagy PNG, max 10 MB. Átméretezésre kerül.", + "photo_too_large": "A kép túl nagy (max 10 MB)", + "photo_invalid": "Érvénytelen képfájl", + "change_photo": "Módosítás" + }, + "groups": { + "create": "Új csoport", + "edit": "Csoport szerkesztése", + "empty": "Még nincsenek csoportok", + "delete_confirm_title": "Csoport törlése", + "delete_confirm": "Biztosan törölni szeretnéd ezt a csoportot?", + "name_label": "Csoport neve", + "name_placeholder": "pl. Csapat, Család", + "name_required": "A csoport neve kötelező", + "save_failed": "Nem sikerült menteni a csoportot", + "members_label": "Tagok", + "search_members": "Névjegyek keresése a hozzáadáshoz...", + "no_members": "Nincs tag ebben a csoportban", + "member_count": "{count, plural, =0 {Nincs tag} one {1 tag} other {# tag}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Névjegyek importálása", + "drop_hint": "Kattints a vCard fájl kiválasztásához", + "file_types": ".vcf vagy .vcard fájlok", + "no_contacts": "Nincs névjegy a fájlban", + "parse_error": "Nem sikerült feldolgozni a vCard fájlt", + "found": "{count, plural, one {1 névjegy található} other {# névjegy található}}", + "duplicate": "Duplikált", + "select_all": "Összes kijelölése", + "deselect_all": "Összes kijelölés megszüntetése", + "selected": "{count, plural, one {1 kijelölve} other {# kijelölve}}", + "import_button": "Importálás", + "importing": "Importálás...", + "success": "{count, plural, one {1 névjegy importálva} other {# névjegy importálva}}", + "failed": "Importálás sikertelen", + "close": "Bezárás", + "file_too_large": "A fájl túl nagy (max 5 MB)" + }, + "export": { + "title": "Névjegyek exportálása", + "success": "{count, plural, one {1 névjegy exportálva} other {# névjegy exportálva}}" + }, + "bulk": { + "selected": "{count, plural, one {1 kijelölve} other {# kijelölve}}", + "select_all": "Összes kijelölése", + "delete": "Törlés", + "delete_confirm_title": "Névjegyek törlése", + "delete_confirm": "Törlöd {count, plural, one {az 1 névjegyet} other {a # névjegyet}}?", + "deleted": "{count, plural, one {1 névjegy törölve} other {# névjegy törölve}}", + "add_to_group": "Hozzáadás a csoporthoz", + "choose_group": "Csoport kiválasztása", + "adding_contacts": "{count, plural, one {1 névjegy} other {# névjegy}} hozzáadása", + "added_to_group": "Névjegyek hozzáadva a csoporthoz", + "export": "Exportálás", + "clear": "Kijelölés megszüntetése" + }, + "toast": { + "created": "Névjegy létrehozva", + "updated": "Névjegy frissítve", + "deleted": "Névjegy törölve", + "error_create": "Nem sikerült létrehozni a névjegyet", + "error_update": "Nem sikerült frissíteni a névjegyet", + "error_delete": "Nem sikerült törölni a névjegyet" + }, + "context_menu": { + "open": "Megnyitás", + "edit": "Szerkesztés", + "send_email": "E-mail küldése", + "add_to_group": "Hozzáadás a csoporthoz", + "export_vcard": "Exportálás vCard-ként", + "delete": "Törlés", + "call": "Hívás", + "duplicate": "Duplikálás", + "print": "Nyomtatás" + }, + "filters": { + "toggle": "Szűrők", + "select": "Kiválasztás", + "clear": "Törlés", + "close": "Bezárás", + "title": "Speciális szűrők", + "organization": "Cég", + "organization_placeholder": "pl. Acme Corp", + "job_title": "Beosztás", + "job_title_placeholder": "pl. Tervező", + "location": "Helyszín", + "location_placeholder": "Város vagy ország", + "email_domain": "E-mail tartomány", + "email_domain_placeholder": "example.com", + "birthday_month": "Születésnap hónapja", + "any_month": "Bármely hónap", + "has_email": "Van e-mail", + "has_phone": "Van telefon", + "has_photo": "Van fotó", + "sort_by": "Rendezés", + "sort_first_name": "Keresztnév", + "sort_last_name": "Vezetéknév" + } + }, + "calendar": { + "title": "Naptár", + "back_to_email": "Vissza az e-mailekhez", + "back_to_month": "Vissza a hónaphoz", + "my_calendars": "Naptárak", + "birthday_calendar": "Születésnapok", + "mini_calendar_change": "Kattints a hónap váltásához", + "views": { + "month": "Hónap", + "week": "Hét", + "day": "Nap", + "agenda": "Napirend", + "today": "Ma", + "month_hint": "Hónap (m)", + "week_hint": "Hét (w)", + "day_hint": "Nap (d)", + "agenda_hint": "Napirend (a)", + "tasks": "Feladatok", + "tasks_hint": "Feladatok (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# korábbi esemény} other {# korábbi esemény}}", + "hidden_after": "{count, plural, one {# későbbi esemény} other {# későbbi esemény}}", + "show_all_hours": "Minden óra megjelenítése", + "show_display_hours": "Csak {start} – {end} megjelenítése", + "create": "Esemény létrehozása", + "edit": "Esemény szerkesztése", + "delete": "Esemény törlése", + "details": "Esemény részletei", + "no_events": "Nincs esemény", + "all_day": "Egész napos", + "more": "+{count} további", + "show_more": "Több mutatása", + "show_less": "Kevesebb mutatása", + "no_title": "(Nincs cím)", + "resize": "Esemény átméretezése", + "duplicate": "Duplikálás", + "today_header": "Ma", + "tomorrow_header": "Holnap", + "agenda_show_earlier": "Korábbi események megjelenítése", + "agenda_loading": "Betöltés…", + "agenda_range_start": "Események ettől: {date}", + "agenda_range_end": "Események eddig: {date}", + "export_ics": "Exportálás .ics-ként", + "copy_title": "Cím másolása", + "copy_link": "Találkozó link másolása", + "new_event": "Új esemény", + "new_all_day_event": "Új egész napos esemény", + "new_task": "Új feladat", + "go_to_today": "Ugrás a mai napra" + }, + "detail": { + "add_note": "Megjegyzés hozzáadása...", + "save_note": "Mentés", + "note_saved": "Megjegyzés hozzáadva", + "open_link": "Link megnyitása", + "copy_location": "Helyszín másolása", + "meeting_link": "Találkozó link", + "tentative": "Előzetes", + "cancelled": "Lemondva", + "delete_confirm": "Törlöd ezt az eseményt?" + }, + "form": { + "title": "Cím", + "description": "Leírás", + "location": "Helyszín", + "meeting_link": "Találkozó link", + "start_date": "Kezdő dátum", + "end_date": "Befejező dátum", + "start_time": "Kezdő idő", + "end_time": "Befejező idő", + "all_day_event": "Egész napos esemény", + "calendar_select": "Naptár", + "save": "Mentés", + "cancel": "Mégse", + "delete_confirm": "Biztosan törölni szeretnéd ezt az eseményt?", + "color": "Szín" + }, + "participants": { + "availability": { + "title": "Elérhetőség", + "free": "Szabad", + "busy": "Foglalt", + "tentative": "Feltételes", + "unknown": "Nem felhasználó ezen a kiszolgálón", + "checking": "Ellenőrzés…" + }, + "title": "Résztvevők", + "add": "Résztvevő hozzáadása", + "organizer": "Szervező", + "attendee": "Résztvevő", + "accepted": "Elfogadva", + "declined": "Elutasítva", + "tentative": "Előzetes", + "needs_action": "Művelet szükséges", + "remove": "Eltávolítás", + "edit": "Szerkesztés", + "email_placeholder": "E-mail cím hozzáadása vagy névjegy keresése", + "send_invitations": "Meghívók küldése a résztvevőknek", + "status_summary": "{accepted} elfogadta, {pending} függőben", + "invited_by": "Meghívó tőle: {name}", + "respond_below": "Válaszolj az alábbi gombok segítségével", + "rsvp_label": "Válaszod", + "cancel_notification": "A résztvevők értesítést kapnak a lemondásról", + "you_organizer": "Te vagy a szervező", + "you_attendee": "Résztvevő vagy", + "no_participants": "Nincs résztvevő", + "count": "{count, plural, one {# résztvevő} other {# résztvevő}}" + }, + "recurrence": { + "title": "Ismétlődés", + "none": "Nem ismétlődik", + "daily": "Naponta", + "weekly": "Hetente", + "monthly": "Havonta", + "yearly": "Évente", + "every_n_days": "Minden {count} nap", + "every_n_weeks": "Minden {count} hét", + "every_n_months": "Minden {count} hónap", + "until": "Amíg", + "occurrences": "{count} alkalom", + "custom": "Egyéni…", + "edit_custom": "Egyéni ismétlődés szerkesztése", + "every_n_years": "Minden {count}. évben", + "on_days": "ezeken a napokon: {days}", + "on_day_n": "a hónap {day}. napján", + "on_the_nth": "a(z) {nth} {day}", + "in_month": "{month} hónapban", + "nth_1": "első", + "nth_2": "második", + "nth_3": "harmadik", + "nth_4": "negyedik", + "nth_last": "utolsó", + "editor_freq_day": "Nap", + "editor_freq_week": "Hét", + "editor_freq_month": "Hónap", + "editor_freq_year": "Év", + "editor_repeats_on": "Ismétlődés", + "editor_every": "Minden", + "editor_unit_days": "nap", + "editor_unit_weeks": "hét", + "editor_unit_months": "hónap", + "editor_unit_years": "év", + "editor_on_day": "a hónap napján:", + "editor_on_the": "a(z)", + "editor_in": "hónap:", + "editor_ends": "Vége", + "editor_never": "Soha", + "editor_ends_on": "Ekkor", + "editor_ends_after": "Ennyi után:", + "editor_occurrences": "alkalom" + }, + "recurrence_scope": { + "edit_title": "Ismétlődő esemény szerkesztése", + "delete_title": "Ismétlődő esemény törlése", + "description": "Ez egy ismétlődő esemény. Mely eseményeket szeretnéd módosítani?", + "this_event": "Csak ez az esemény", + "this_and_future": "Ez és a következő események", + "all_events": "Minden esemény", + "cancel": "Mégse", + "save": "Mentés", + "delete": "Törlés", + "rsvp_title": "Válasz ismétlődő eseményre", + "rsvp_description": "Ez egy ismétlődő esemény. Mely eseményekre vonatkozzon a válaszod?", + "respond": "Válasz" + }, + "alerts": { + "title": "Emlékeztetők", + "none": "Nincs emlékeztető", + "at_time": "Az esemény időpontjában", + "minutes_before": "{count, plural, one {# perccel előtte} other {# perccel előtte}}", + "hours_before": "{count, plural, one {# órával előtte} other {# órával előtte}}", + "days_before": "{count, plural, one {# nappal előtte} other {# nappal előtte}}", + "weeks_before": "{count, plural, one {# héttel előtte} other {# héttel előtte}}", + "unit_minutes_before": "perccel előtte", + "unit_hours_before": "órával előtte", + "unit_days_before": "nappal előtte", + "unit_weeks_before": "héttel előtte", + "add": "Emlékeztető hozzáadása", + "remove": "Emlékeztető eltávolítása", + "amount": "Emlékeztető mennyisége", + "unit": "Emlékeztető egysége", + "action": "Emlékeztető típusa", + "action_display": "Értesítés", + "action_email": "E-mail", + "by_email": "{label} e-mailben", + "kept": "{count, plural, one {További # itt nem szerkeszthető emlékeztető megmarad} other {További # itt nem szerkeszthető emlékeztető megmarad}}", + "other": "{count, plural, one {További # emlékeztető} other {További # emlékeztető}}", + "calendar_defaults": "A naptár alapértelmezett emlékeztetői", + "customize": "Testreszabás", + "use_defaults": "A naptár alapértelmezett emlékeztetőinek használata" + }, + "settings": { + "limit_hours": "Látható órák korlátozása", + "limit_hours_desc": "A napi és heti nézetben csak ezeket az órákat mutatja. Az ezen kívül eső események a napok tetején és alján lévő nyilakkal érhetők el.", + "visible_hours": "Látható órák", + "visible_hours_start": "Látható órák kezdete", + "visible_hours_end": "Látható órák vége", + "hide_non_working_days": "Munkaszüneti napok elrejtése", + "hide_non_working_days_desc": "A heti nézetből kimaradnak azok a napok, amikor nem dolgozol. Eseményeik továbbra is megjelennek a havi, napi és napirend nézetben.", + "working_days": "Munkanapok", + "title": "Naptár beállítások", + "default_view": "Alapértelmezett nézet", + "week_starts_on": "Hét kezdőnapja", + "time_format": "Idő formátum", + "default_calendar": "Alapértelmezett naptár", + "default_reminder": "Alapértelmezett emlékeztető", + "time_format_12h": "12 órás", + "time_format_24h": "24 órás", + "notifications_enabled": "Esemény értesítések", + "notifications_enabled_desc": "Figyelmeztetések megjelenítése a közelgő naptári eseményekhez", + "notification_sound": "Értesítési hang", + "notification_sound_desc": "Hang lejátszása naptári figyelmeztetésekhez", + "invitation_parsing": "E-mail meghívók feldolgozása", + "invitation_parsing_desc": "Naptármeghívók észlelése az e-mail csatolmányokban és naptár műveletek megjelenítése", + "show_time_in_month_view": "Idő mutatása a hónap nézetben", + "show_time_in_month_view_desc": "Események időpontjának mutatása a havi naptár nézetben", + "show_week_numbers": "Hétszámok mutatása", + "show_week_numbers_desc": "Hétszámok mutatása a mini naptárban", + "calendar_free_scroll": "Szabad görgetés", + "calendar_free_scroll_desc": "Folyamatos görgetés hónapok, hetek és napok között egyetlen időszak helyett", + "enable_tasks": "Feladatok engedélyezése", + "enable_tasks_desc": "Feladatnézet mutatása a naptárban a teendők kezeléséhez", + "show_tasks_on_calendar": "Feladatok mutatása a naptárban", + "show_tasks_on_calendar_desc": "Feladatok mutatása a havi, heti és napi naptárnézetben", + "hover_preview": "Esemény előnézet egérrel", + "hover_preview_desc": "Részletes felugró ablak mutatása az események fölé húzva", + "hover_preview_instant": "Azonnali", + "hover_preview_delay_500ms": "0,5 másodperc késleltetés", + "hover_preview_delay_1s": "1 másodperc késleltetés", + "hover_preview_delay_2s": "2 másodperc késleltetés", + "hover_preview_off": "Letiltva", + "organizer_identity": "Meghívók szervezése mint", + "organizer_identity_desc": "A szervezőként használt naptárcím, amikor embereket hívsz meg eseményekre.", + "organizer_identity_failed": "Nem sikerült módosítani a szervező címét", + "show_birthday_calendar": "Névjegy születésnap naptár", + "show_birthday_calendar_desc": "Virtuális naptár mutatása a névjegyeid születésnapjaival" + }, + "days": { + "monday": "Hétfő", + "tuesday": "Kedd", + "wednesday": "Szerda", + "thursday": "Csütörtök", + "friday": "Péntek", + "saturday": "Szombat", + "sunday": "Vasárnap", + "mon": "H", + "tue": "K", + "wed": "Sze", + "thu": "Cs", + "fri": "P", + "sat": "Szo", + "sun": "V" + }, + "months": { + "jan": "Jan", + "feb": "Feb", + "mar": "Már", + "apr": "Ápr", + "may": "Máj", + "jun": "Jún", + "jul": "Júl", + "aug": "Aug", + "sep": "Szep", + "oct": "Okt", + "nov": "Nov", + "dec": "Dec", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Esemény létrehozva", + "event_updated": "Esemény frissítve", + "event_deleted": "Esemény törölve", + "calendar_created": "Naptár létrehozva", + "calendar_deleted": "Naptár törölve", + "event_move_error": "Nem sikerült áthelyezni az eseményt", + "event_resize_error": "Nem sikerült átméretezni az eseményt", + "alert_title": "Közelgő esemény", + "alert_now": "Most kezdődik", + "alert_in_minutes": "{count} perc múlva", + "invitation_sent": "Meghívók elküldve", + "invitations_denied_title": "A meghívók nem küldhetők el", + "invitations_denied": "A kiszolgáló nem küldte el a meghívókat: {reason}. Mentsük az eseményt a meghívók nélkül?", + "save_without_invitations": "Mentés meghívók nélkül", + "rsvp_updated": "Válasz frissítve", + "rsvp_error": "Nem sikerült frissíteni a választ", + "event_duplicated": "Esemény duplikálva", + "event_error": "Nem sikerült menteni az eseményt", + "task_due": "Feladat határidős", + "event_exported": "Esemény exportálva", + "title_copied": "Cím másolva", + "link_copied": "Link másolva" + }, + "status": { + "loading_calendars": "Naptárak betöltése...", + "loading_events": "Események betöltése..." + }, + "quick_create": { + "placeholder": "Új esemény címe", + "aria_label": "Gyors esemény létrehozása", + "add_title": "Cím hozzáadása", + "more_options": "További beállítások" + }, + "nav_prev": "Előző", + "nav_next": "Következő", + "nav_open_menu": "Menü megnyitása", + "import": { + "title": "Naptár importálása", + "tab_file": "Fájl", + "tab_url": "URL", + "select_file": ".ics fájl kiválasztása", + "drop_file": "vagy fájl ejtése ide", + "supported_formats": "iCalendar (.ics) fájlok támogatottak", + "url_description": "Add meg egy külső iCalendar (.ics) feed URL-jét az események importálásához.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "iCalendar (.ics) URL-ek támogatottak", + "fetch": "Lekérés", + "invalid_url": "Kérjük, adj meg egy érvényes URL-t", + "url_fetch_failed": "Nem sikerült lekérni a naptárt az URL-ről", + "parsing": "Naptár fájl feldolgozása...", + "parsed_events": "{count} esemény található", + "no_events": "Nincs esemény a fájlban", + "select_all": "Összes kijelölése", + "deselect_all": "Összes kijelölés megszüntetése", + "target_calendar": "Importálás ebbe a naptárba", + "import_button": "Kijelöltek importálása", + "importing": "Események importálása...", + "success": "{count} esemény sikeresen importálva", + "error": "Nem sikerült importálni a naptárt", + "file_too_large": "A fájl meghaladja a 10 MB korlátot", + "invalid_format": "Érvénytelen naptár fájl formátum" + }, + "webcal_action": { + "title": "Naptár hivatkozás megnyitása", + "description": "Hogyan szeretnéd használni a \"{name}\"-t?", + "import_title": "Egyszeri importálás", + "import_description": "Események lekérése és másolása az egyik naptáradba.", + "subscribe_title": "Feliratkozás", + "subscribe_description": "Naptár automatikus szinkronizálása külön naptárként.", + "cancel": "Mégse" + }, + "management": { + "title": "Naptár kezelés", + "description": "Naptárak létrehozása, átnevezése és testreszabása. Kattints jobb gombbal egy naptárra az oldalsávban a szín gyors módosításához.", + "name": "Név", + "name_placeholder": "Naptár neve", + "color": "Szín", + "change_color": "Szín módosítása", + "random_color": "Véletlenszerű szín", + "add_calendar": "Naptár hozzáadása", + "edit": "Szerkesztés", + "delete": "Törlés", + "save": "Mentés", + "create": "Létrehozás", + "cancel": "Mégse", + "default": "Alapértelmezett", + "confirm_delete": "Törlöd a \"{name}\" naptárat? Az összes esemény eltávolításra kerül.", + "confirm_clear": "Törlöd az összes eseményt a \"{name}\" naptárból? Ez nem vonható vissza.", + "set_default": "Beállítás alapértelmezettként", + "default_updated": "Alapértelmezett naptár frissítve", + "error_default": "Nem sikerült beállítani az alapértelmezett naptárat", + "clear_events": "Események törlése", + "events_cleared": "{count} esemény törölve", + "error_clear": "Nem sikerült törölni a naptár eseményeket", + "calendar_created": "Naptár létrehozva", + "calendar_updated": "Naptár frissítve", + "calendar_deleted": "Naptár törölve", + "color_updated": "Naptár szín frissítve", + "error_create": "Nem sikerült létrehozni a naptárat", + "error_update": "Nem sikerült frissíteni a naptárat", + "error_delete": "Nem sikerült törölni a naptárat", + "caldav_url": "CalDAV URL", + "copy_url": "CalDAV URL másolása", + "url_copied": "CalDAV URL a vágólapra másolva", + "share": "Naptár megosztása", + "new_event_in_calendar": "Új esemény ebben a naptárban", + "shared_with": "Megosztva: {names}", + "shared_with_count": "{count, plural, one {Megosztva 1 személlyel} other {Megosztva # személlyel}}", + "move_up": "Mozgatás fel", + "move_down": "Mozgatás le", + "kind": "Tartalom", + "kind_events": "Események", + "kind_tasks": "Feladatok", + "kind_both": "Események és feladatok", + "kind_hint": "Meghatározza, hogy mely alkalmazások látják ezt a naptárat CalDAV-szinkronizáláskor. Később nem módosítható.", + "default_alerts": "Alapértelmezett emlékeztetők", + "default_alerts_title": "Alapértelmezett emlékeztetők: \"{name}\"", + "default_alerts_hint": "A naptár új eseményei ezeket az emlékeztetőket kapják, hacsak nem módosítják őket az eseményben. Az itteni módosítás nem érinti a meglévő eseményeket. Az e-mailes emlékeztetőket a levelezőszerver küldi.", + "default_alerts_timed": "Időponttal rendelkező események", + "default_alerts_all_day": "Egész napos események", + "default_alerts_updated": "Alapértelmezett emlékeztetők frissítve" + }, + "subscription": { + "title": "iCal feliratkozás", + "section_title": "iCal feliratkozások", + "description": "Feliratkozás külső iCalendar feed-re. Az események automatikusan szinkronizálódnak a saját naptárukba. https:// és webcal:// URL-ek támogatottak.", + "url_label": "Naptár URL", + "url_placeholder": "https://example.com/calendar.ics vagy webcal://...", + "name_label": "Naptár neve", + "name_placeholder": "pl. Ünnepnapok", + "color_label": "Szín", + "refresh_interval": "Frissítési időköz", + "interval_15": "15 percenként", + "interval_30": "30 percenként", + "interval_60": "Óránként", + "interval_360": "6 óránként", + "interval_1440": "Naponta", + "subscribe": "Feliratkozás", + "subscribing": "Feliratkozás...", + "save": "Változtatások mentése", + "saving": "Mentés...", + "edit": "Szerkesztés", + "edit_title": "Feliratkozás szerkesztése", + "updated": "\"{name}\" frissítve", + "update_error": "Nem sikerült frissíteni a feliratkozást", + "invalid_url": "Kérjük, adj meg egy érvényes URL-t", + "success": "Feliratkozva: \"{name}\"", + "error": "Nem sikerült hozzáadni a feliratkozást", + "refresh": "Frissítés most", + "refresh_success": "Feliratkozás frissítve", + "refresh_error": "Nem sikerült frissíteni a feliratkozást", + "unsubscribe": "Leiratkozás", + "confirm_delete": "Leiratkozás a \"{name}\" naptárról? A naptár és az összes esemény eltávolításra kerül.", + "deleted": "Feliratkozás eltávolítva", + "delete_error": "Nem sikerült eltávolítani a feliratkozást", + "last_refreshed": "Utolsó frissítés: {time}" + }, + "tasks": { + "label": "Feladatok", + "no_tasks": "Nincs feladat", + "no_title": "(Nincs cím)", + "mark_complete": "Késznek jelölés", + "mark_incomplete": "Befejezetlennek jelölés", + "filter_all": "Összes", + "filter_pending": "Függőben", + "filter_completed": "Kész", + "filter_overdue": "Lejárt", + "show_completed": "Kész mutatása", + "create": "Új feladat", + "edit": "Feladat szerkesztése", + "title_placeholder": "Feladat címe", + "description_placeholder": "Leírás hozzáadása...", + "due_date": "Határidő", + "include_time": "Időpont megadása", + "priority": "Prioritás", + "priority_none": "Nincs", + "priority_high": "Magas", + "priority_medium": "Közepes", + "priority_low": "Alacsony", + "progress": "Állapot", + "progress_needs_action": "Művelet szükséges", + "progress_in_process": "Folyamatban", + "progress_completed": "Kész", + "progress_cancelled": "Lemondva", + "calendar": "Naptár", + "alert": "Emlékeztető", + "alert_none": "Nincs", + "alert_at_time": "A határidő időpontjában", + "alert_5min": "5 perccel előtte", + "alert_15min": "15 perccel előtte", + "alert_30min": "30 perccel előtte", + "alert_1hr": "1 órával előtte", + "alert_1day": "1 nappal előtte", + "delete": "Törlés", + "cancel": "Mégse", + "save": "Mentés", + "quick_add_placeholder": "Feladat hozzáadása...", + "due_today": "Ma", + "due_tomorrow": "Holnap", + "overdue": "Lejárt" + }, + "toolbar": { + "more": "További műveletek", + "select_view": "Nézet váltása, jelenleg {view}" + }, + "other_calendars": "Egyéb naptárak", + "calendar_options": "{name} beállításai" + }, + "calendar_event_notifications": { + "someone": "Valaki", + "invited": "{name} meghívott a(z) „{title}” eseményre", + "updated": "{name} frissítette a(z) „{title}” eseményt", + "cancelled": "{name} lemondta a(z) „{title}” eseményt", + "untitled": "Névtelen esemény" + }, + "share_notifications": { + "someone": "Valaki", + "shared": "{name} megosztotta veled a(z) {kind} „{object}” elemet", + "changed": "{name} módosította a hozzáférésedet a(z) {kind} „{object}” elemhez", + "revoked": "{name} visszavonta a hozzáférésedet a(z) {kind} „{object}” elemhez", + "object": { + "folder": "mappa", + "calendar": "naptár", + "address_book": "címjegyzék", + "files": "fájlmappa" + } + }, + "sharing": { + "title": "\"{name}\" megosztása", + "description": "Hozzáférés biztosítása más felhasználók vagy csoportok számára ezen a szerveren. A módosítások azonnal életbe lépnek.", + "no_shares": "Még nincs megosztva senkivel.", + "add_person": "Személy vagy csoport hozzáadása", + "search_placeholder": "Keresés név vagy e-mail alapján...", + "loading_principals": "Felhasználók betöltése...", + "no_principals": "Nincs más felhasználó vagy csoport.", + "no_match": "Nincs találat.", + "remove": "Hozzáférés eltávolítása", + "group": "Csoport", + "share_added": "Hozzáférést biztosítva", + "share_updated": "Hozzáférés frissítve", + "share_removed": "Hozzáférés eltávolítva", + "share_failed": "Nem sikerült frissíteni a megosztást", + "preset": { + "freeBusy": "Csak szabad/elfoglalt", + "read": "Csak olvasás", + "readWrite": "Olvasás és írás", + "manager": "Kezelő", + "custom": "Egyéni" + } + }, + "advanced_search": { + "title": "Speciális keresés", + "from": "Feladó", + "from_placeholder": "Feladó e-mail vagy név", + "to": "Címzett", + "to_placeholder": "Címzett e-mail vagy név", + "subject": "Tárgy", + "subject_placeholder": "Tárgy tartalma...", + "body": "Törzs", + "body_placeholder": "Törzs tartalma...", + "folder": "Mappa", + "all_folders": "Minden mappa", + "all_folders_except_spam_trash": "Minden mappa, kivéve a Spam és a Kuka", + "has_attachment": "Csatolmányok", + "date_after": "Ettől", + "date_before": "Eddig", + "size_min": "Nagyobb mint (KB)", + "size_max": "Kisebb mint (KB)", + "starred": "Csillagozott", + "unread": "Olvasatlan", + "read": "Olvasott", + "yes": "Igen", + "no": "Nem", + "clear": "Törlés", + "clear_all": "Összes törlése", + "filters_active": "{count} szűrő", + "filters_active_plural": "{count} szűrő", + "toggle_filters": "Továbbiak", + "search_hint": "Használj speciális szűrőket a pontos kereséshez", + "advanced_filters_tooltip": "Speciális keresőszűrők", + "results_found": "{count, plural, =0 {Nincs találat} one {# találat} other {# találat}}", + "results_found_more": "{count}+ találat", + "suggestions_recent": "Legutóbbi keresések", + "suggestions_people": "Személyek", + "suggestions_from": "Levelek keresése tőle: {address}", + "suggestions_to": "Levelek keresése neki: {address}", + "suggestions_remove_recent": "Eltávolítás a legutóbbi keresésekből", + "has_words": "Tartalmazza a szavakat", + "size": "Méret", + "date_within": "Dátum", + "search_in": "Keresés itt", + "has_attachment_check": "Van melléklete", + "search": "Keresés" + }, + "welcome": { + "title": "Üdvözlünk a postafiókodban", + "tip_compose": "Nyomd meg a c billentyűt új e-mail írásához", + "tip_shortcuts": "Nyomd meg a ? billentyűt az összes billentyűparancs megtekintéséhez", + "tip_sidebar": "Találd meg a Naptár, Névjegyek és Beállítások menüpontokat az oldalsávban", + "tip_settings": "Testreszabhatod a felhasználói élményt a Beállításokban", + "tip_compose_touch": "Új e-mail írásához koppints a ceruza gombra", + "tip_sidebar_touch": "A Naptár, a Névjegyek és a Beállítások az alsó sávban vannak", + "got_it": "Értem", + "settings": "Beállítások", + "dismiss": "Elutasítás", + "start_tour": "Bemutató indítása" + }, + "demo_welcome": { + "title": "Üdvözlünk a Bulwark Mail-ben", + "description": "Fedezz fel egy teljes funkciókkal rendelkező webmail klienst - közvetlenül a böngésződben. Az összes adat az eszközödön marad, nyugodtan tesztelj mindent.", + "feature_email": "E-mailek olvasása és írása", + "feature_organize": "Címkék, csillagok és mappák", + "feature_shortcuts": "Billentyűparancsok", + "feature_privacy": "100%-ban privát demó", + "hint": "Kattints bármely e-mailre a bal oldalon a kezdéshez, vagy nézd meg az alábbi bemutatót." + }, + "files": { + "office_edit": "Dokumentum szerkesztése", + "office_loading": "Dokumentumszerkesztő megnyitása…", + "office_error": "A dokumentumszerkesztőt nem sikerült megnyitni.", + "office_close": "Szerkesztő bezárása", + "open_in_new_tab": "Megnyitás új lapon", + "title": "Fájlok", + "search_placeholder": "Fájlok keresése...", + "empty_state_title": "Még nincsenek fájlok", + "upload_files": "Fájlok feltöltése", + "new_folder": "Új mappa", + "new_folder_name": "Mappa neve", + "rename": "Átnevezés", + "rename_title": "Átnevezés", + "new_name": "Új név", + "name_forbidden_chars": "A kiszolgáló nem engedi ezeket a karaktereket a nevekben: {chars}", + "name_reserved": "Ez a név a kiszolgálón foglalt.", + "delete": "Törlés", + "delete_confirm_title": "Erőforrás törlése", + "delete_confirm_message": "Biztosan törölni szeretnéd a \"{name}\" elemet? Ez nem vonható vissza.", + "download": "Letöltés", + "name": "Név", + "size": "Méret", + "modified": "Módosítva", + "type": "Típus", + "folder": "Mappa", + "file": "Fájl", + "no_accounts": "Nincs csatlakoztatott fiók.", + "uploading": "Feltöltés...", + "upload_success": "{count, plural, one {1 fájl feltöltve} other {# fájl feltöltve}}", + "upload_canceled": "Feltöltés megszakítva", + "upload_canceled_partial": "{count, plural, one {1 fájl} other {# fájl}} feltöltődött a megszakítás előtt", + "upload_error": "Nem sikerült feltölteni a fájlt", + "create_folder_success": "Mappa létrehozva", + "create_folder_error": "Nem sikerült létrehozni a mappát", + "delete_success": "Sikeresen törölve", + "delete_error": "Nem sikerült törölni", + "rename_success": "Sikeresen átnevezve", + "rename_error": "Nem sikerült átnevezni", + "download_error": "Nem sikerült letölteni", + "not_available": "A fájltárolás nem elérhető ezen a szerveren", + "cancel": "Mégse", + "create": "Létrehozás", + "save": "Mentés", + "no_results": "Nincs a keresésnek megfelelő fájl", + "batch_delete_confirm_message": "Biztosan törölni szeretnéd {count, plural, one {az 1 elemet} other {a # elemet}}? Ez nem vonható vissza.", + "batch_delete_success": "{count, plural, one {1 elem törölve} other {# elem törölve}}", + "grid_view": "Rács nézet", + "list_view": "Lista nézet", + "details": "Részletek", + "path": "Útvonal", + "preview": "Előnézet", + "preview_error": "Nem sikerült betölteni az előnézetet", + "cut": "Kivágás", + "copy": "Másolás", + "paste": "Beillesztés", + "move_success": "{count, plural, one {1 elem áthelyezve} other {# elem áthelyezve}}", + "move_error": "Nem sikerült áthelyezni", + "paste_success": "Sikeresen beillesztve", + "paste_error": "Nem sikerült beilleszteni", + "new_text_file": "Új szöveges fájl", + "file_name": "Fájl neve", + "retry": "Újra", + "refresh": "Frissítés", + "duplicate": "Duplikálás", + "duplicate_success": "Sikeresen duplikálva", + "duplicate_error": "Nem sikerült duplikálni", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Fájl létrehozva", + "create_file_error": "Nem sikerült létrehozni a fájlt", + "favorites": "Kedvencek", + "recent": "Legutóbbi", + "show_in_folder": "Megjelenítés a mappában", + "remove_from_recent": "Eltávolítás a legutóbbiak közül", + "upload_folder": "Mappa feltöltése", + "file_too_large": "\"{name}\" meghaladja a maximális fájlméretet ({max})", + "undo": "Visszavonás", + "undo_success": "Művelet visszavonva", + "undo_error": "Nem sikerült visszavonni", + "toolbar": "Fájl műveletek", + "open_folder_tree": "Mappa fa megnyitása", + "file_list": "Fájlok és mappák", + "context_menu": "Műveletek", + "settings_ascending": "Növekvő", + "settings_descending": "Csökkenő", + "disabled_title": "A fájlok funkciót a rendszergazda letiltotta", + "disabled_description": "Nagyméretű fájlok WebDAV-on keresztüli feltöltése Stalwart/RocksDB instabilitást okozhat, beleértve a memóriahiányos összeomlásokat és a helyreállíthatatlan lemezhasználatot. A törölt fájlok nem feltétlenül kerülnek azonnal eltávolításra a blob tárolóból. Ez a funkció nem ajánlott éles környezetben.", + "migration_title": "Fájlok frissítése…", + "migration_description": "A mappák és fájlok a megfelelő struktúrába rendeződnek. Ez csak egyszer történik meg.", + "new": "Új", + "my_files": "Saját fájlok", + "folders": "Mappák", + "files_heading": "Fájlok", + "open": "Megnyitás", + "star": "Csillagozás", + "unstar": "Csillag eltávolítása", + "expand_folder": "{name} kibontása", + "collapse_folder": "{name} összecsukása", + "sidebar_hint": "Csillagozz meg mappákat és nyiss meg fájlokat, hogy itt megtaláld őket.", + "breadcrumbs": "Mappa útvonala", + "show_path": "Rejtett mappák megjelenítése az útvonalban", + "selection_actions": "Műveletek a kijelölt elemekkel", + "selected_count": "{count, plural, one {1 kijelölve} other {# kijelölve}}", + "clear_selection": "Kijelölés megszüntetése", + "select_all": "Összes kijelölése", + "select_item": "{name} kijelölése", + "more_actions": "További műveletek: {name}", + "paste_here": "Beillesztés ebbe a mappába", + "paste_count": "{count, plural, one {1 elem beillesztése} other {# elem beillesztése}}", + "sort_by": "Rendezés", + "view_mode": "Elrendezés", + "clear_search": "Keresés törlése", + "no_results_for": "Ebben a mappában semmi sem felel meg ennek: \"{query}\"", + "download_count": "{count, plural, one {1 fájl letöltése} other {# fájl letöltése}}", + "delete_count": "{count, plural, one {1 elem törlése} other {# elem törlése}}", + "close_details": "Részletek bezárása", + "file_kind": "{ext} fájl", + "folder_summary": "{folders, plural, =0 {Nincs mappa} one {1 mappa} other {# mappa}}, {files, plural, =0 {nincs fájl} one {1 fájl} other {# fájl}}", + "details_hint": "Jelölj ki egy fájlt vagy mappát a részletek megtekintéséhez.", + "sharing": "Megosztás", + "shared_with_count": "{count, plural, one {Megosztva 1 személlyel} other {Megosztva # személlyel}}", + "only_you": "Csak te", + "empty_folder_title": "Ez a mappa üres", + "empty_folder_description": "Húzd ide a fájlokat, vagy töltsd fel őket az eszközödről.", + "empty_folder_read_only": "Ebbe a mappába nem adhatsz hozzá fájlokat.", + "choose_account": "Válassz fiókot", + "drop_to_upload": "Engedd el a feltöltéshez ide: {folder}", + "uploading_count": "Feltöltés: {current}/{total}", + "dismiss": "Bezárás", + "zoom_in": "Nagyítás", + "zoom_out": "Kicsinyítés", + "zoom_reset": "Igazítás a képernyőhöz", + "rotate": "Forgatás", + "close_preview": "Bezárás" + }, + "smime": { + "your_certificates": "Tanúsítványaid", + "your_certificates_desc": "S/MIME tanúsítványok importálása és kezelése e-mailek aláírásához és titkosításához", + "recipient_certificates": "Címzett tanúsítványok", + "recipient_certificates_desc": "Nyilvános tanúsítványok az e-mailek címzetteknek történő titkosításához", + "identity_bindings": "Azonosság kulcs hozzárendelések", + "identity_bindings_desc": "S/MIME tanúsítványok hozzárendelése az e-mail azonosságaidhoz", + "defaults_title": "Alapértelmezettek", + "defaults_desc": "Alapértelmezett aláírási és titkosítási viselkedés beállítása", + "import_pkcs12": "PKCS#12 (.p12/.pfx) importálása", + "import_public_cert": "Tanúsítvány importálása", + "no_certificates": "Még nincs tanúsítvány importálva", + "no_recipient_certs": "Nincs címzett tanúsítvány", + "expires": "Lejárat", + "expired": "Lejárt", + "bound_to": "Hozzárendelve", + "no_key_bound": "Nincs", + "lock": "Kulcs zárolása", + "unlock": "Kulcs feloldása", + "details": "Részletek megtekintése", + "delete": "Törlés", + "encrypt_by_default": "Titkosítás alapértelmezés szerint", + "encrypt_by_default_desc": "E-mailek automatikus titkosítása, ha minden címzett rendelkezik tanúsítvánnyal", + "remember_unlocked": "Feloldott kulcsok megjegyzése", + "remember_unlocked_desc": "Kulcsok feloldva tartása a böngésző munkamenet idejére", + "sign_default_for": "Aláírás alapértelmezés szerint ehhez", + "enter_p12_passphrase": "PKCS#12 jelszó megadása", + "p12_passphrase_desc": "Add meg a tanúsítvány fájlt védő jelszót", + "enter_storage_passphrase": "Tárolási jelszó beállítása", + "storage_passphrase_desc": "Válassz egy jelszót a kulcs böngészőben történő védelméhez", + "next": "Következő", + "import": "Importálás", + "unlock_key": "Kulcs feloldása", + "unlock_key_desc": "Add meg a tárolási jelszót a kulcs feloldásához aláíráshoz vagy visszafejtéshez", + "passphrase_placeholder": "Jelszó megadása", + "confirm_passphrase_placeholder": "Jelszó megerősítése", + "passphrase_mismatch": "A jelszavak nem egyeznek", + "cancel": "Mégse", + "processing": "Feldolgozás...", + "close": "Bezárás", + "certificate_details": "Tanúsítvány részletei", + "cert_subject": "Tulajdonos", + "cert_issuer": "Kibocsátó", + "cert_email": "E-mail", + "cert_serial": "Sorozatszám", + "cert_validity": "Érvényesség", + "cert_fingerprint": "Ujjlenyomat (SHA-256)", + "cert_algorithm": "Algoritmus", + "cert_capabilities": "Képességek", + "cert_source": "Forrás", + "cert_expired": "Ez a tanúsítvány lejárt", + "cert_not_yet_valid": "Ez a tanúsítvány még nem érvényes", + "cap_sign": "Aláírás", + "cap_encrypt": "Titkosítás", + "cap_none": "Nincs", + "show_passphrase": "Jelszó mutatása", + "hide_passphrase": "Jelszó elrejtése", + "sign_toggle": "Aláírás", + "encrypt_toggle": "Titkosítás", + "missing_recipient_certs": "Hiányzó tanúsítványok ehhez: {emails}", + "missing_sender_cert": "Nincs tanúsítvány hozzárendelve ehhez az azonossághoz", + "status_encrypted_ok": "Ez az üzenet titkosítva volt", + "status_encrypted_no_key": "Ez az üzenet titkosítva van, de nem található hozzá kulcs", + "status_encrypted_failed": "Nem sikerült visszafejteni ezt az üzenetet", + "status_signed_valid": "Aláírás hitelesítve", + "status_signed_invalid": "Aláírás hitelesítése sikertelen", + "status_signed_expired_cert": "Aláírva egy lejárt tanúsítvánnyal", + "status_signed_self_signed": "Aláírás érvényes, de a tanúsítvány önaláírt (nem megbízható)", + "status_signed_mismatch": "Aláírás érvényes, de az aláíró nem egyezik a feladóval", + "status_unsupported": "Nem támogatott S/MIME formátum", + "auto_import_signer_certs": "Aláíró tanúsítványok automatikus importálása", + "auto_import_signer_certs_desc": "Hitelesített aláírt e-mailekből származó tanúsítványok automatikus mentése a jövőbeli titkosításhoz", + "export": "Exportálás", + "enter_export_passphrase": "Export jelszó beállítása", + "export_passphrase_desc": "Válassz egy jelszót az exportált PKCS#12 fájl védelméhez", + "export_storage_desc": "Add meg a tárolási jelszót a kulcs visszafejtéséhez az exportáláshoz", + "incorrect_passphrase": "Helytelen jelszó" + }, + "tour": { + "step_counter": "{current}. lépés a(z) {total} közül", + "skip": "Bemutató kihagyása", + "back": "Vissza", + "next": "Következő", + "finish": "Befejezés", + "take_a_tour": "A felület bemutatása", + "restart_title": "Bevezető bemutató", + "restart_desc": "A vezetett bemutató újrajátszása", + "restart_button": "Bemutató újraindítása", + "show_on_new_devices_title": "Megjelenítés új eszközökön", + "show_on_new_devices_desc": "Az üdvözlő banner és bemutató újrajátszása, amikor először jelentkezel be egy új eszközön, még ha már elvégezted máshol", + "sidebar_title": "Postafiókjaid", + "sidebar_desc": "Ez a mappa oldalsávod. Kattints bármely postafiókra az e-mailek megtekintéséhez. Létrehozhatsz mappákat, húzhatsz e-maileket közöttük, és egy pillantással láthatod az olvasatlanok számát.", + "compose_title": "E-mail írása", + "compose_desc": "Kattints ide egy új e-mail írásához. Hozzáadhatsz címzetteket, csatolmányokat, és használhatsz formázott szöveget.", + "search_title": "Keresés a levelek között", + "search_desc": "Keresés feladó, tárgy vagy tartalom alapján. Kattints a szűrő ikonra a speciális opciókért, mint dátumtartomány, csatolmányok és csillagozott üzenetek.", + "email_list_title": "E-mail listád", + "email_list_desc": "Az e-mailek itt jelennek meg. Kattints egyre az olvasáshoz a jobb oldalon. Használd a jelölőnégyzetet több kijelöléséhez, majd tömeges áthelyezéshez, törléshez vagy címkézéshez.", + "email_viewer_title": "Olvasóablak", + "email_viewer_desc": "A kiválasztott e-mail itt nyílik meg. Válaszolj, továbbíts, archiválj vagy törölj az eszköztár gombjaival. Csillagozhatsz is, vagy színes címkéket adhatsz hozzá.", + "keywords_title": "Színes címkék", + "keywords_desc": "E-mailek rendezése színkódolt címkékkel. Húzz egy e-mailt egy címkére a felcímkézéshez, vagy kattints jobb gombbal egy e-mailre a címkék hozzárendeléséhez.", + "calendar_title": "Naptár", + "calendar_desc": "Válts a naptárra az események kezeléséhez. Hozz létre eseményeket, állíts be emlékeztetőket, és nézd meg a nap, hét vagy hónap elrendezést.", + "contacts_title": "Névjegyek", + "contacts_desc": "A címjegyzéked itt található. Importálj névjegyeket, hozz létre csoportokat, és kattints bármely névjegyre a teljes adatainak megtekintéséhez.", + "settings_title": "Beállítások", + "settings_desc": "Testreszabhatsz mindent: témát, sűrűséget, aláírásokat, szűrőket, billentyűparancsokat, naptár alapértelmezetteket és még sok mást.", + "shortcuts_title": "Billentyűparancsok", + "shortcuts_desc": "A haladó felhasználók imádni fogják. Nyomd meg a ? gombot bármikor az összes elérhető parancs megtekintéséhez. Navigálhatsz, írhatsz és kezelheted az e-maileket egér nélkül.", + "compose_open_title": "A szerkesztő", + "compose_open_desc": "Ez az e-mail szerkesztő. Adj hozzá címzetteket, írd meg az üzeneted, csatolj fájlokat, és használj formázott szöveget. Piszkozatokat is menthetsz és sablonokat használhatsz.", + "calendar_view_title": "Naptárad", + "calendar_view_desc": "Itt a naptárad minta eseményekkel. Válthatsz nap, hét, hónap és napirend nézetek között az eszköztár segítségével.", + "create_event_title": "Esemény létrehozása", + "create_event_desc": "Kattints erre a gombra egy új naptári esemény létrehozásához. Beállíthatsz címet, dátumot, időpontot, és résztvevőket adhatsz hozzá.", + "event_modal_title": "Esemény részletei", + "event_modal_desc": "Itt az esemény űrlap. Töltsd ki a címet, válassz dátumot és időpontot, adj hozzá helyszínt vagy résztvevőket. Nyomj a Mentés gombra ha kész, vagy zárd be és lépj tovább.", + "contacts_list_title": "Névjegyeid", + "contacts_list_desc": "Itt vannak a névjegyeid. Kattints bármelyikre a teljes adatainak megtekintéséhez a jobb oldalon. Létrehozhatsz új névjegyeket, importálhatsz vCard-okat, vagy csoportokba rendezheted a névjegyeket.", + "settings_tabs_title": "Beállítások menü", + "settings_tabs_desc": "Itt vannak az összes beállítás kategória. Testreszabhatod a megjelenést, kezelheted az azonosságokat, beállíthatsz e-mail szűrőket, konfigurálhatod a naptárat és még sok mást.", + "files_title": "Fájltároló", + "files_desc": "A fájlböngésződ lehetővé teszi fájlok feltöltését, rendezését és megosztását - mint egy személyes felhőmeghajtó a levelezésedbe építve.", + "demo_banner_title": "Demó vezérlők", + "demo_banner_desc": "Demó módban vagy - minden a böngésződben marad. Nyomd meg a 'Demó visszaállítása' gombot bármikor a tiszta mintaadatokkal való újrakezdéshez.", + "quota_title": "Tárhely használat", + "quota_desc": "Kövesd nyomon a postafiókod méretét itt. A kör kitöltődik, ahogy egyre több helyet használsz." + }, + "unified_mailbox": { + "search_unavailable": "A keresés nem érhető el az egységes nézetben" + }, + "quote_header": { + "reply_line": "{from} ezt írta ({date}):", + "forwarded_separator": "---------- Továbbított üzenet ----------", + "from_label": "Feladó", + "date_label": "Dátum", + "subject_label": "Tárgy" + }, + "pwa_install": { + "title": "{appName} telepítése", + "description": "Telepítsd az alkalmazásunkat a gyors eléréshez és offline támogatáshoz.", + "not_now": "Később", + "install": "Telepítés", + "dont_remind": "Ne emlékeztess többet", + "dismiss_aria": "Telepítési ablak elutasítása" + }, + "deep_link": { + "message_not_found": "Ez az üzenet már nem érhető el.", + "thread_not_found": "Ez a beszélgetés már nem érhető el.", + "folder_not_found": "Ez a mappa már nem érhető el.", + "event_not_found": "Ez az esemény már nem érhető el.", + "file_not_found": "Ez a fájl már nem érhető el.", + "account_unavailable": "Jelentkezz be ebbe a fiókba a link megnyitásához.", + "copy_message": "Üzenet linkjének másolása", + "copy_conversation": "Beszélgetés linkjének másolása", + "copy_event": "Esemény linkjének másolása", + "copied": "Link másolva", + "copy_failed": "Nem sikerült másolni a linket" + }, + "plugins": { + "oauth_callback": { + "completing": "Hitelesítés befejezése…", + "completing_message": "Kérjük, várjon, amíg a kapcsolat létrejön.", + "received_title": "Hitelesítés fogadva", + "received_message": "Visszatérhet arra a lapra, ahol elindította a kapcsolódást.", + "failed_title": "A hitelesítés sikertelen", + "denied": "A hitelesítés megtagadva.", + "failed_detail": "A hitelesítés sikertelen: {error}", + "missing_params": "Hiányzik a hitelesítési kód vagy az állapot.", + "delivery_failed": "A hitelesítés eredményét nem sikerült átadni az alkalmazás lapjának. Zárja be ezt az ablakot, és próbálja újra." + }, + "settings": { + "title": "Bővítmények", + "description": "A rendszergazda által hozzáadott bővítmények. A fiókodhoz be- vagy kikapcsolhatod őket.", + "description_uploads": "A bővítmények új funkciókat adnak a webmailhez. A fiókodhoz be- vagy kikapcsolhatod őket, vagy telepíthetsz sajátot.", + "description_demo": "A bővítmények nem érhetők el a demóban.", + "install_button": "Bővítmény telepítése", + "install_hint": "Válassz egy bővítményt tartalmazó ZIP-fájlt.", + "install_hint_approval": "Válassz egy bővítményt tartalmazó ZIP-fájlt. Bekapcsolás előtt a rendszergazdának jóvá kell hagynia.", + "install_uploads_off": "A rendszergazda nem engedélyezi bővítmények telepítését.", + "install_managed_id": "A rendszergazda már biztosít egy ilyen nevű bővítményt, ezért nem cserélhető le.", + "install_invalid": "Ez a fájl nem telepíthető bővítmény: {details}", + "toast_installed": "Bővítmény telepítve. Lent kapcsolhatod be.", + "toast_installed_pending": "Bővítmény telepítve, és jóváhagyásra elküldve a rendszergazdának.", + "toast_enabled": "Bővítmény bekapcsolva: {name}", + "toast_disabled": "Bővítmény kikapcsolva: {name}", + "toast_removed": "Bővítmény eltávolítva: {name}", + "toast_forced": "A rendszergazda bekapcsolva tartja ezt a bővítményt: {name}", + "toast_approval_requested": "Rendszergazdai jóváhagyásra vár: {name}", + "empty_title": "Még nincsenek bővítmények", + "empty_admin": "A rendszergazda még nem adott hozzá bővítményt.", + "empty_uploads": "Kezdésként telepíts egy bővítményt ZIP-fájlból.", + "empty_demo": "A bővítményekhez valódi fiók kell, ezért a demóban nincsenek.", + "syncing": "Bővítmények ellenőrzése a szerveren…", + "status_installed": "Ki", + "status_enabled": "Indítás", + "status_running": "Be", + "status_disabled": "Ki", + "status_error": "Hiba", + "badge_forced": "Kötelező", + "badge_managed": "A rendszergazdától", + "badge_yours": "Saját telepítés", + "badge_awaiting": "Jóváhagyásra vár", + "toggle_label": "Bővítmény be- vagy kikapcsolása: {name}", + "forced_note": "A rendszergazda előírja ezt a bővítményt, ezért nem kapcsolható ki.", + "permissions": "Mire képes", + "permissions_managed": "Mire képes (a rendszergazda jóváhagyta)", + "settings": "Beállítások", + "remove": "Eltávolítás", + "remove_title": "{name} eltávolítása?", + "remove_message": "A bővítmény és minden, amit ebben a böngészőben tárolt, törlődik. Később újra telepítheted.", + "remove_confirm": "Eltávolítás", + "error_user_plugins_off": "A rendszergazda kikapcsolta a felhasználók által telepített bővítményeket, ezért ez a bővítmény nem fut.", + "error_approval_unavailable": "Ezt a bővítményt még azelőtt telepítetted, hogy jóváhagyás kellett volna. Távolítsd el, majd telepítsd újra, hogy jóváhagyásra kerüljön.", + "error_approval_denied": "A rendszergazda nem hagyta jóvá ezt a bővítményt.", + "error_approval_pending": "Várakozás arra, hogy a rendszergazda jóváhagyja ezt a bővítményt.", + "error_auto_disabled": "Kikapcsolva, mert többször is hibát okozott. Kapcsolja be újra, ha ismét meg szeretné próbálni.", + "accounts_heading": "Csatlakoztatott fiókok", + "account_on": "{label} – {host}", + "account_waiting": "{label} – {host} (a bejelentkezés nincs befejezve)", + "disconnect": "Leválasztás", + "disconnect_failed": "Nem sikerült leválasztani: {label}" + }, + "consent": { + "title": "{name}: engedélyezed ezeket a műveleteket?", + "intro": "A bővítmény az alábbi engedélyeket kéri. Csak akkor kapja meg őket, ha engedélyezed.", + "high_risk_note": "A megjelölt elemek hozzáférést adnak a bővítménynek a leveleid tartalmához, vagy lehetővé teszik, hogy adatokat küldjön máshová. Csak megbízható bővítményeknek engedélyezd őket.", + "revoke_hint": "A bővítményt bármikor kikapcsolhatod vagy eltávolíthatod a Beállításokban.", + "deny": "Nem engedélyezem", + "allow": "Engedélyezés" + }, + "permissions": { + "none": "Nincs szüksége különleges engedélyre.", + "high_risk": "Óvatosan", + "email_read": { + "title": "Leveleid olvasása", + "body": "Látja a fiókod bármely üzenetének feladóját, címzettjeit, tárgyát és teljes szövegét, valamint a mappáidat." + }, + "email_write": { + "title": "Leveleid módosítása és rendezése", + "body": "Címkéket, jelölőket és más kulcsszavakat ad hozzá az üzeneteidhez, vagy eltávolítja őket, áthelyezi az üzeneteket a mappáid között és az archívumba, és piszkozatokat ment." + }, + "email_send": { + "title": "Levelek módosítása küldés előtt", + "body": "Olvassa és módosítja az általad írt üzenetek címzettjeit, tárgyát, szövegét és mellékleteit, és megakadályozhatja egy üzenet elküldését." + }, + "crypto_full": { + "title": "Teljes hozzáférés az alkalmazáshoz", + "body": "Ugyanazokkal a jogokkal fut, mint maga a webmail. Olvashatja az összes leveledet és tárolt kulcsodat, és a nevedben járhat el. Csak olyan bővítménynek engedélyezd, amelyben teljesen megbízol." + }, + "email_raw_send": { + "title": "Saját maga által összeállított üzenetek küldése", + "body": "Teljes üzeneteket küld a fiókodból, például aláírt vagy titkosított üzeneteket." + }, + "email_blob_read": { + "title": "Üzenetek nyers tartalmának olvasása", + "body": "Olvassa az üzeneteid és mellékleteid eredeti adatait, valamint minden fájlét, amelyet küldés előtt csatolsz." + }, + "email_blob_write": { + "title": "Mellékletek cseréje küldés előtt", + "body": "Fájlokat tölt fel a levelezőszerveredre, és az üzenet elküldése előtt egy csatolt fájlt másikra cserél." + }, + "email_render_takeover": { + "title": "Üzenetek megjelenítésének módosítása", + "body": "Lecseréli a megnyitott üzenetek és az üzenetlista szövegét, például a visszafejtett tartalom megjelenítéséhez." + }, + "calendar_read": { + "title": "Naptárad olvasása", + "body": "Látja a naptáraidat és azok eseményeit." + }, + "calendar_write": { + "title": "Naptárad módosítása", + "body": "Eseményeket hoz létre, módosít és töröl." + }, + "contacts_read": { + "title": "Névjegyeid olvasása", + "body": "Keres a címjegyzékeidben, és olvassa őket." + }, + "contacts_write": { + "title": "Névjegyeid módosítása", + "body": "Névjegyeket és címjegyzékeket hoz létre, módosít és töröl." + }, + "files_read": { + "title": "Fájljaid olvasása", + "body": "Böngészi és megnyitja a fiókodban tárolt fájlokat." + }, + "files_write": { + "title": "Fájljaid módosítása", + "body": "Fájlokat tölt fel, nevez át, helyez át és töröl a fiókodban." + }, + "identity_read": { + "title": "Küldő azonosságaid megtekintése", + "body": "Látja a címeket, amelyekről küldesz, és azok aláírásait." + }, + "identity_write": { + "title": "Küldő azonosságaid módosítása", + "body": "Küldő azonosságokat és aláírásokat hoz létre, módosít és töröl." + }, + "filters_read": { + "title": "Levélszűrőid olvasása", + "body": "Látja a szabályokat, amelyek a beérkező leveleidet rendezik." + }, + "filters_write": { + "title": "Levélszűrőid módosítása", + "body": "Szabályokat ad a szerveroldali szűrőidhez. Az ilyen szabályok áthelyezhetik, továbbíthatják vagy törölhetik a beérkező leveleket." + }, + "tasks_read": { + "title": "Feladataid olvasása", + "body": "Látja a feladatlistáidat és a feladataidat." + }, + "tasks_write": { + "title": "Feladataid módosítása", + "body": "Feladatokat hoz létre, módosít, teljesít és töröl." + }, + "templates_read": { + "title": "Sablonjaid olvasása", + "body": "Látja a mentett üzenetsablonjaidat." + }, + "templates_write": { + "title": "Sablonjaid módosítása", + "body": "Üzenetsablonokat hoz létre, módosít és töröl." + }, + "smime_read": { + "title": "S/MIME állapotod megtekintése", + "body": "Látja, hogy vannak-e S/MIME kulcsaid és tanúsítványaid, a kulcsokhoz való hozzáférés nélkül." + }, + "vacation_read": { + "title": "Távolléti válaszod megtekintése", + "body": "Látja, hogy be van-e kapcsolva az automatikus válaszod, és mi a szövege." + }, + "vacation_write": { + "title": "Távolléti válaszod módosítása", + "body": "Be- és kikapcsolja az automatikus válaszodat, és módosítja a szövegét és dátumait." + }, + "settings_read": { + "title": "Beállításaid olvasása", + "body": "Látja a beállításaidat és a címkék meghatározásait." + }, + "settings_write": { + "title": "Beállításaid módosítása", + "body": "Címkéket ad hozzá, átrendezi őket, és módosítja a beállításaidat." + }, + "security_read": { + "title": "Fiókod biztonságának megtekintése", + "body": "Látja, hogy be van-e kapcsolva a kétfaktoros hitelesítés és a titkosítás, a titkos adatokhoz való hozzáférés nélkül." + }, + "auth_observe": { + "title": "Tudomást szerez a bejelentkezésedről", + "body": "Értesül, amikor bejelentkezel, fiókot váltasz, vagy megújul a munkameneted." + }, + "auth_emit": { + "title": "Kijelentkeztetés", + "body": "Kijelentkeztethet a webmailből." + }, + "account_read": { + "title": "Fiókjaid megtekintése", + "body": "Látja a hozzáadott fiókjaidat a címükkel és szerverükkel együtt." + }, + "http_post": { + "title": "A webmail szerverének használata a nevedben", + "body": "Meghívja ezeket a szervervégpontokat a bejelentkezéseddel:" + }, + "http_fetch": { + "title": "Kapcsolódás más webhelyekhez", + "body": "Kéréseket küld ezekre a webhelyekre. Bármi, amit a bővítmény olvashat, ide is eljuthat:" + }, + "admin_config": { + "title": "A rendszergazdától kapott beállítások olvasása", + "body": "Olvassa a beállításokat, amelyeket a rendszergazda mentett ehhez a bővítményhez." + }, + "ui_observe": { + "title": "Követi, hol jársz az alkalmazásban", + "body": "Tudja, melyik oldal van megnyitva, és reagál a hivatkozásokra, amelyekre kattintasz." + }, + "ui_toolbar": { + "title": "Gombok az eszköztárakon", + "body": "Saját gombokat jelenít meg az üzenetek eszköztárán, egy megnyitott üzeneten és a felső sávban." + }, + "ui_app_top_banner": { + "title": "Szalag megjelenítése felül", + "body": "Értesítést jelenít meg az alkalmazás tetején." + }, + "ui_email_banner": { + "title": "Értesítések az üzeneteken", + "body": "Saját értesítéseket és összefoglalókat jelenít meg egy megnyitott üzenet vagy beszélgetés részei felett, azokon belül és között." + }, + "ui_email_footer": { + "title": "Tartalom az üzenetek alatt", + "body": "Saját tartalmat jelenít meg egy megnyitott üzenet alatt." + }, + "ui_contact_cryptokeys": { + "title": "Névjegyek titkosítási kulcsainak megjelenítése", + "body": "Megjeleníti és kezeli egy névjegy titkosítási kulcsait." + }, + "ui_email_details": { + "title": "Panelek az üzenet részleteiben", + "body": "Saját panelt jelenít meg egy megnyitott üzenet mellett vagy alatt." + }, + "ui_download_file": { + "title": "Fájlok mentése az eszközödre", + "body": "Letöltésre kínálja az általa létrehozott fájlokat." + }, + "ui_message_list_tabs": { + "title": "Lapok a Beérkező levelekben", + "body": "Lapokra bontja a Beérkező leveleket, például Frissítések vagy Promóciók." + }, + "ui_composer_toolbar": { + "title": "Tartalom a szerkesztőben", + "body": "Saját gombokat, menüpontokat és szakaszokat jelenít meg, miközben üzenetet írsz." + }, + "ui_composer_sidebar": { + "title": "Panel a szerkesztőben", + "body": "Saját panelt jelenít meg az általad írt üzenet mellett." + }, + "ui_sidebar_widget": { + "title": "Tartalom az oldalsávon", + "body": "Saját dobozokat és szakaszokat jelenít meg a levelezés oldalsávján." + }, + "ui_settings_section": { + "title": "Beállítási oldal hozzáadása", + "body": "Saját oldalt jelenít meg a Beállításokban." + }, + "ui_context_menu": { + "title": "Elemek az üzenet menüjében", + "body": "Elemeket ad ahhoz a menühöz, amely egy üzenetre jobb gombbal kattintva nyílik meg." + }, + "ui_navigation_rail": { + "title": "Gomb a navigációs sávon", + "body": "Saját gombot jelenít meg a navigációs sávon." + }, + "ui_keyboard": { + "title": "Billentyűparancsok hozzáadása", + "body": "Reagál az általa meghatározott billentyűparancsokra." + }, + "ui_calendar_action": { + "title": "Tartalom a naptárnézetben", + "body": "Saját gombokat és szakaszokat jelenít meg az eseményeken, valamint rövid címkéket és pontokat a naptár napjain." + }, + "ui_admin_page": { + "title": "Adminisztrációs oldal hozzáadása", + "body": "Saját oldalt jelenít meg az adminisztrációs beállításokban." + }, + "app_lifecycle": { + "title": "Tudomást szerez az alkalmazás indulásáról és leállásáról", + "body": "Értesül, amikor az alkalmazás elindul, offline módba vált, vagy kijelentkezel." + }, + "compose_open": { + "title": "A szerkesztő megnyitása", + "body": "Új üzenetet nyit meg kitöltött címzettekkel, szöveggel vagy mellékletekkel. Az elküldésről továbbra is te döntesz." + }, + "compose_read": { + "title": "Az éppen írt üzeneted olvasása", + "body": "Az üzenet szövegének olvasása írás közben, amikor a bővítményt a szerkesztőben használod. Az aláírást és az idézett üzenetet nem." + }, + "compose_write": { + "title": "Az éppen írt üzeneted módosítása", + "body": "Szöveg cseréje vagy hozzáadása az üzenetben írás közben, amikor a bővítményt a szerkesztőben használod. Minden módosítás visszavonható, és továbbra is te döntöd el, elküldöd-e." + }, + "ui_notifications": { + "title": "Értesítések megjelenítése", + "body": "Értesítéseket jelenít meg az eszközödön, vagy az alkalmazásban, ha nem engedélyezted őket. Mindegyiken szerepel a bővítmény neve." + }, + "ui_message_list": { + "title": "Tartalom az üzenetlistában", + "body": "Saját tartalmat jelenít meg az üzenetlista felett, valamint gombokat az üzeneteken és a kijelölt üzenetek eszköztárán." + }, + "ui_contacts": { + "title": "Tartalom a névjegyek nézetében", + "body": "Saját szakaszokat jelenít meg egy névjegy részleteiben és gombokat a névjegylistában." + }, + "ui_tasks": { + "title": "Tartalom a feladatok nézetében", + "body": "Saját szakaszokat jelenít meg egy feladat részleteiben és gombokat a feladatlistában." + }, + "ui_files": { + "title": "Tartalom a fájlok nézetében", + "body": "Saját szakaszokat jelenít meg a fájlelőnézetben és menüpontokat egy fájl menüjében." + }, + "ui_commands": { + "title": "Saját parancsok", + "body": "Saját elemeket ad a parancspalettához (Ctrl+K)." + }, + "ui_app_page": { + "title": "Saját oldalak", + "body": "Saját oldalakat ad az alkalmazáshoz, mindegyiket egy gombbal a navigációs sávon." + }, + "http_proxy": { + "title": "Az általad csatlakoztatott fiókok használata", + "body": "Kéréseket küld a Bulwark szerverén keresztül egy olyan fiókkal vagy kulccsal, amelyet te vagy a rendszergazdád állított be. A bővítmény soha nem látja a jelszót, de bármit megtehet, amit az a fiók a saját szerverén megtehet." + } + }, + "dialog": { + "from_plugin": "A(z) {name} bővítménytől", + "ok": "OK", + "submit": "Küldés", + "confirm": "Megerősítés", + "cancel": "Mégse", + "close": "Bezárás", + "turn_off": "{name} kikapcsolása", + "plugin_menu": "{name} beállításai", + "password_note": "Ez az űrlap a(z) {name} bővítménytől származik, nem a webmailtől. Csak akkor adjon meg jelszót, ha megbízik ebben a bővítményben.", + "toast_auto_disabled": "{name} ki lett kapcsolva, mert többször is hibát okozott." + }, + "connect": { + "title": "{label} csatlakoztatása", + "intro_password": "A(z) {name} bővítmény használni szeretné a(z) {label} fiókodat. A Bulwark a saját szerverén tárolja a jelszót, és csak ide küldi el: {host}. A bővítmény soha nem látja.", + "intro_password_any": "A(z) {name} bővítmény használni szeretné a(z) {label} fiókodat. A Bulwark a saját szerverén tárolja a jelszót, és csak az itt megadott szervernek küldi el. A bővítmény soha nem látja.", + "intro_signin": "A(z) {name} bővítmény be szeretne jelentkezni a(z) {label} fiókodba. Add meg itt a szervert, majd jelentkezz be a szerver saját oldalán. A Bulwark a saját szerverén tárolja a kapott hozzáférést; a bővítmény soha nem látja.", + "server": "Szervercím", + "server_hint": "cloud.example.com", + "username": "Felhasználónév", + "secret": "Jelszó vagy kulcs", + "connect": "Csatlakoztatás", + "continue": "Tovább", + "cancel": "Mégse", + "saving": "Mentés…", + "error_server": "Add meg a szerver címét, például https://cloud.example.com", + "error_save": "Nem sikerült menteni: {error}" + } + }, + "global_search": { + "title": "Keresés mindenben", + "placeholder": "Keresés a levélben, névjegyekben, naptárban, fájlokban...", + "scope_label": "Keresés itt", + "account_label": "Fiók", + "scope_all": "Minden", + "scope_mail": "Levelek", + "scope_contacts": "Névjegyek", + "scope_calendar": "Naptár", + "scope_files": "Fájlok", + "all_accounts": "Minden fiók", + "show_all": "Összes találat megjelenítése", + "no_results": "Nincs találat erre: „{query}”", + "server_hint": "A teljes szavak a szerveren keresnek; a szórészletek a már betöltött elemekre illeszkednek", + "searching": "Keresés...", + "account_error": "Nem sikerült keresni itt: {account}", + "recent_searches": "Legutóbbi keresések", + "recurring": "Ismétlődő", + "no_subject": "(no subject)", + "empty_hint": "Keressen levelet, névjegyet, eseményt és fájlt minden fiókban. Próbálja ki: from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Lapos, dátum szerint", + "grouped": "Csoportosított", + "load_more": "Továbbiak betöltése", + "preview_empty": "Válasszon egy találatot az előnézethez", + "open_full": "Megnyitás", + "view_label": "Nézet" + }, + "top_bar": { + "more": "További lehetőségek" + }, + "plugin_pages": { + "loading": "Oldal betöltése", + "not_available_title": "Ez az oldal nem érhető el", + "not_available_description": "A bővítmény, amely hozzáadta, ki van kapcsolva, vagy már nincs telepítve.", + "back_to_mail": "Vissza a levelekhez", + "settings_group": "Bővítmények" + }, + "command_palette": { + "title": "Parancspaletta", + "open": "Parancspaletta megnyitása", + "placeholder": "Írjon be egy parancsot", + "no_results": "Nincs egyező parancs", + "go_to": "Ugrás ide: {name}", + "compose": "Új levél", + "search": "Keresés a levelekben", + "theme_dark": "Váltás sötét témára", + "theme_light": "Váltás világos témára", + "shortcuts": "Billentyűparancsok megjelenítése", + "group_navigation": "Ugrás ide", + "group_actions": "Műveletek", + "group_plugins": "Bővítmények" + }, + "notification_center": { + "title": "Értesítések", + "mark_all_read": "Összes megjelölése olvasottként", + "clear": "Törlés", + "empty": "Nincsenek értesítések", + "close": "Bezárás", + "unread": "Olvasatlan", + "unread_count": "{count} olvasatlan", + "level_error": "Hiba", + "level_warning": "Figyelmeztetés" + } +} diff --git a/locales/it/common.json b/locales/it/common.json index e5dc3e0b..487e54f6 100644 --- a/locales/it/common.json +++ b/locales/it/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Client di posta web minimalista che utilizza il protocollo JMAP", - "login": { - "title": "Webmail", - "username_label": "Email", - "username_placeholder": "utente@esempio.com", - "password_label": "Password", - "password_placeholder": "Inserisci la tua password", - "jmap_endpoint_label": "Server JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Il server deve consentire le richieste CORS da questo dominio.", - "jmap_server_label": "Server", - "jmap_server_auto_picked": "Server selezionato in base al tuo dominio email.", - "sign_in": "Accedi", - "signing_in": "Accesso in corso...", - "loading": "Caricamento...", - "reconnecting": "Connessione persa. Tentativo di riconnessione…", - "error": { - "invalid_credentials": "Email o password non valida", - "connection_failed": "Impossibile connettersi al server", - "cors_blocked": "Il server è raggiungibile ma sta bloccando le richieste cross-origin. Controlla le impostazioni CORS del tuo server JMAP e consenti questo dominio.", - "server_error": "Il server non è temporaneamente disponibile. Riprova più tardi.", - "generic": "Si è verificato un errore. Riprova.", - "totp_required": "È richiesto un codice di autenticazione a due fattori. Inserisci il codice qui sotto.", - "totp_invalid": "Codice di autenticazione non valido. Controlla la tua app di autenticazione.", - "oauth_discovery_failed": "SSO è abilitato ma il provider di identità non è raggiungibile. Controlla la configurazione OAuth." - }, - "show_password": "Mostra password", - "hide_password": "Nascondi password", - "totp_toggle": "Ho un codice 2FA", - "remember_me": "Ricordami", - "config_error": { - "title": "Errore di configurazione", - "fetch_failed": "Impossibile caricare la configurazione dell'applicazione. Riprovare più tardi.", - "server_not_configured": "Il server di posta non è stato configurato. Contattare l'amministratore." - }, - "remove_from_history": "Rimuovi dalla cronologia", - "totp_label": "Codice di autenticazione", - "totp_placeholder": "000000", - "session_expired": "La sessione è scaduta. Accedi di nuovo.", - "dismiss": "Chiudi", - "or": "o", - "sign_in_sso": "Accedi con SSO", - "add_account_title": "Aggiungi account", - "add_account_subtitle": "Accedi con un altro account", - "cancel": "Annulla", - "website": "Sito web", - "imprint": "Note legali", - "privacy_policy": "Informativa sulla privacy", - "try_demo": "Prova la demo", - "demo_description": "Esplora con dati di esempio - nessun account necessario", - "demo_launching": "Avvio demo...", - "demo_login_button": "Avvia demo", - "demo_tagline": "Scopri un client di posta completo. Nessun account richiesto.", - "demo_no_signup": "Nessuna registrazione - esplora liberamente con dati di esempio", - "oauth_completing": "Completamento dell'accesso...", - "oauth_error": { - "title": "Autenticazione non riuscita", - "invalid_state": "La convalida di sicurezza è fallita. Prova ad accedere di nuovo.", - "missing_params": "Dati di autorizzazione mancanti. Prova ad accedere di nuovo.", - "token_exchange_failed": "Impossibile completare l'autenticazione. Riprova.", - "access_denied": "Accesso negato. Contatta il tuo amministratore.", - "back_to_login": "Torna al login" - } - }, - "sidebar": { - "close": "Chiudi", - "compose": "Scrivi", - "compose_hint": "Scrivi (c)", - "search_placeholder": "Cerca nella posta...", - "search_placeholder_hint": "Cerca posta... (premi /)", - "storage": "Spazio di archiviazione", - "storage_used": "Utilizzato", - "storage_free": "Libero", - "storage_total": "Totale", - "sign_out": "Esci", - "sign_out_of": "Disconnetti da {account}", - "sign_out_all": "Disconnetti da tutti gli account", - "add_account": "Aggiungi account", - "set_as_default": "Imposta come predefinito", - "switch_account": "Cambia account", - "contacts": "Contatti", - "calendar": "Calendario", - "settings": "Impostazioni", - "admin": "Admin", - "files": "File", - "loading_mailboxes": "Caricamento caselle di posta...", - "push_connected": "Aggiornamenti in tempo reale attivi", - "push_disconnected": "Aggiornamenti in tempo reale non attivi", - "keyboard_shortcuts": "Scorciatoie da tastiera", - "theme": { - "light": "Tema chiaro", - "dark": "Tema scuro", - "system": "Tema di sistema" - }, - "language": { - "title": "Lingua" - }, - "mailboxes": { - "inbox": "Posta in arrivo", - "sent": "Inviati", - "drafts": "Bozze", - "trash": "Cestino", - "archive": "Archivio", - "starred": "Speciali", - "all_mail": "Tutta la posta", - "spam": "Spam", - "important": "Importanti" - }, - "unified_inbox": "Posta in arrivo unificata", - "unified_sent": "Tutti gli inviati", - "unified_drafts": "Tutte le bozze", - "unified_trash": "Tutti i cestini", - "unified_archive": "Tutti gli archivi", - "unified_junk": "Tutto lo spam", - "all_accounts": "Tutti gli account", - "unified_mailbox": "Casella di posta unificata", - "expand": "Espandi", - "collapse": "Comprimi", - "expand_tooltip": "Espandi", - "collapse_tooltip": "Comprimi", - "mobile": { - "search": "Cerca", - "compose": "Scrivi", - "go_back": "Indietro", - "toggle_menu": "Attiva/disattiva menu" - }, - "clear_search": "Cancella ricerca", - "vacation_active": "Risponditore automatico attivo", - "demo_banner": "Modalità demo", - "demo_reset": "Reimposta", - "demo_tour": "Tour", - "tags": "Etichette", - "show_all_tags": "Mostra tutto ({count})", - "show_fewer_tags": "Mostra meno", - "folders": "Cartelle", - "mail": "Posta", - "nav_label": "Navigazione", - "add_app": "App", - "shared": "Condiviso", - "scheduled": "Programmate", - "unified_all_unread": "Tutti i non letti", - "unified_all_starred": "Tutti gli speciali", - "unified_all_mail": "Tutta la posta", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "App predefinite", - "description": "Scegli se i link e-mail e calendario devono aprirsi in Bulwark. Tecnicamente, Bulwark si registra come gestore di protocollo per i link mailto: e webcal:.", - "unsupported": "Questo browser o questa connessione non supporta la registrazione manuale dei gestori di protocollo. Potresti comunque poter usare la PWA installata tramite le impostazioni del browser o del sistema.", - "mailto_label": "Link e-mail", - "mailto_description": "Apre i link mailto: in Bulwark con il compositore precompilato.", - "protocol_open_mode_label": "All'apertura dei link di protocollo", - "protocol_open_mode_description": "Scegli se Bulwark deve aprire i link mailto: e webcal: in una nuova scheda o riutilizzare una sessione aperta. L'opzione sessione attiva richiede l'autorizzazione alle notifiche, così puoi fare clic su una notifica di fallback per portare Bulwark in primo piano se il browser blocca il focus.", - "protocol_open_mode_active_session": "Apri nella sessione attiva se possibile", - "protocol_open_mode_new_tab": "Apri sempre una nuova scheda", - "focus_notification_title": "Apri Bulwark", - "focus_notification_body": "Il link è stato aperto in Bulwark. Fai clic per portare la finestra in primo piano.", - "webcal_label": "Link calendario", - "webcal_description": "Apre i link webcal: in Bulwark con una finestra di dialogo di sottoscrizione al calendario precompilata.", - "register_mailto": "Registra app e-mail", - "register_webcal": "Registra app calendario", - "mailto_registered": "Registrazione del gestore e-mail richiesta", - "webcal_registered": "Registrazione del gestore calendario richiesta", - "registration_failed": "Registrazione del gestore di protocollo non riuscita", - "opening_mailto": "Apertura compositore...", - "opening_webcal": "Apertura calendario...", - "browser_note": "Il browser o il sistema operativo potrebbe chiederti di confermare e potrebbe richiedere che Bulwark sia installato prima di poterlo selezionare come app predefinita.", - "select_account_title": "Scegli account", - "select_mailto_account": "Scegli quale account deve aprire questo link e-mail.", - "select_webcal_account": "Scegli quale account deve aprire questo link calendario.", - "select_account_note": "Questa scelta si applica solo a questo link di protocollo.", - "detail_to": "A", - "detail_subject": "Oggetto", - "detail_no_subject": "Senza oggetto", - "detail_calendar": "Calendario", - "detail_source": "Fonte", - "active_account": "Attivo", - "switching_account": "Cambio account..." - }, - "sidebar_apps": { - "modal_title": "App della barra laterale", - "add_new": "Aggiungi app", - "edit_app": "Modifica app", - "name_label": "Nome", - "name_placeholder": "La mia app", - "name_required": "Il nome è obbligatorio", - "url_label": "URL", - "url_required": "L'URL è obbligatorio", - "url_invalid": "Inserisci un URL http o https valido", - "icon_label": "Icona", - "icon_required": "L'icona è obbligatoria", - "open_mode_label": "Modalità di apertura", - "open_new_tab": "Nuova scheda", - "open_inline": "Integrato", - "cancel": "Annulla", - "add": "Aggiungi", - "update": "Aggiorna", - "delete": "Elimina", - "delete_confirm_title": "Elimina app", - "delete_confirm": "Sei sicuro di voler eliminare \"{name}\"?", - "no_apps": "Nessuna app aggiunta", - "no_apps_hint": "Aggiungi app e link personalizzati alla barra laterale", - "managed_title": "Fornite dall'amministratore", - "managed_description": "Queste app sono configurate per tutti e non possono essere modificate.", - "managed_badge": "Gestita", - "search_icons": "Cerca icone...", - "show_popular": "Popolari", - "show_all": "Tutte", - "no_icons_found": "Nessuna icona trovata", - "inline_badge": "Integrato", - "tab_badge": "Scheda", - "show_on_mobile": "Mostra su mobile" - }, - "email_list": { - "no_emails": "Nessun messaggio trovato", - "no_emails_description": "Questa casella è vuota", - "no_search_results": "Nessun risultato trovato", - "no_search_results_description": "Prova a modificare la ricerca o i filtri", - "loading": "Caricamento messaggi...", - "unread": "non letto", - "to_me": "A me", - "to_recipients": "A {count} destinatari", - "and_others": "e altri {count}", - "draft": "Bozza", - "starred": "Speciale", - "pinned": "Fissato", - "replied": "Risposto", - "forwarded": "Inoltrato", - "has_attachment": "Con allegato", - "conversations_count": "{count} di {total} conversazioni", - "conversations_count_plus": "{count}+ conversazioni", - "conversations_count_simple": "{count} conversazioni", - "no_conversations": "Nessuna conversazione", - "loading_more": "Caricamento altri messaggi...", - "no_more_emails": "Nessun altro messaggio da caricare", - "batch_actions": { - "select": "Seleziona e-mail", - "select_all": "Seleziona tutto", - "selected_messages": "{count} email {count, plural, one {selezionata} other {selezionate}}", - "mark_read": "Segna come letto", - "mark_unread": "Segna come non letto", - "delete": "Elimina", - "delete_confirm_title": "Elimina e-mail", - "delete_confirm_message": "Sei sicuro di voler eliminare {count, plural, one {1 e-mail} other {# e-mail}}?", - "clear_selection": "Cancella selezione" - }, - "permanent_delete": "Elimina definitivamente", - "permanent_delete_confirm_title": "Elimina definitivamente", - "permanent_delete_confirm_message": "Questa e-mail verrà eliminata definitivamente. Questa azione non può essere annullata.", - "permanent_delete_confirm_batch_message": "Queste {count, plural, one {1 e-mail} other {# e-mail}} verranno eliminate definitivamente. Questa azione non può essere annullata.", - "empty_folder": { - "button": "Svuota cartella", - "confirm_title": "Svuota cartella", - "confirm_message": "Tutti i messaggi in questa cartella verranno eliminati definitivamente. Questa azione non può essere annullata.", - "confirm_button": "Svuota cartella", - "junk_hint": "Puoi svuotare la cartella Spam per rimuovere definitivamente tutti i messaggi.", - "trash_hint": "Puoi svuotare il Cestino per rimuovere definitivamente tutti i messaggi." - }, - "no_scheduled_emails": "Nessuna e-mail programmata", - "no_scheduled_emails_description": "I messaggi programmati per più tardi appariranno qui.", - "scheduled_count": "{count, plural, one {1 e-mail programmata} other {# e-mail programmate}}", - "scheduled_actions_hint": "I messaggi programmati possono essere annullati, riprogrammati o modificati dalle azioni di programmazione.", - "cancel_scheduled_send": "Annulla invio", - "reschedule_send": "Riprogramma", - "cancel_and_edit": "Annulla e modifica", - "cancel_and_compose_again": "Annulla e componi di nuovo", - "reschedule_prompt": "Inserisci una nuova data/ora, ad es. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Il mittente richiede una conferma di lettura:", - "send": "Invia conferma", - "ignore": "Ignora", - "sent": "Conferma di lettura inviata.", - "send_failed": "Impossibile inviare la conferma di lettura", - "mdn_subject": "Letto: {subject}", - "mdn_body": "Questa è una conferma di lettura del messaggio che hai inviato a {recipient}.\n\nNota: questa conferma attesta solo che il messaggio è stato visualizzato sul computer del destinatario. Non garantisce che il destinatario abbia letto o compreso il contenuto." - }, - "no_email_selected": "Nessun messaggio selezionato", - "no_email_description": "Seleziona un messaggio dall'elenco per visualizzarlo qui", - "no_conversation_selected": "Nessuna conversazione selezionata", - "no_conversation_description": "Scegli una conversazione dall'elenco per leggerla qui", - "compose": "Componi", - "compose_hint": "Componi nuovo messaggio", - "no_subject": "(Nessun oggetto)", - "no_body_content": "(Nessun contenuto disponibile)", - "no_preview_available": "Anteprima non disponibile", - "show_quoted_text": "Mostra testo citato", - "hide_quoted_text": "Nascondi testo citato", - "loading_email": "Caricamento messaggio...", - "loading": "Caricamento...", - "reply": "Rispondi", - "reply_all": "Rispondi a tutti", - "forward": "Inoltra", - "delete": "Elimina", - "archive": "Archivia", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Aggiungi stella", - "unstar": "Rimuovi stella", - "mark_unread": "Segna come non letto", - "mark_read": "Segna come letto", - "unread": "Non letto", - "read": "Letto", - "spam_short": "Spam", - "not_spam_short": "Non spam", - "move": "Sposta", - "print": "Stampa", - "view_source": "Visualizza sorgente", - "fullscreen": "Schermo intero", - "exit_fullscreen": "Esci da schermo intero", - "export_email": "Esporta come .eml", - "forward_as_attachment": "Inoltra come allegato", - "import_email": "Importa .eml o .zip", - "keyboard_shortcuts": "Scorciatoie da tastiera (?)", - "email_source": "Sorgente del messaggio", - "draft_banner": "Questo messaggio è una bozza", - "edit_draft": "Modifica", - "copy_source": "Copia negli appunti", - "source_copied": "Sorgente copiata negli appunti", - "attachments": "Allegati", - "important": "Importante", - "download": "Scarica", - "download_all": "Scarica tutto", - "from": "Da", - "to": "A", - "cc": "CC", - "bcc": "CCN", - "date": "Data", - "subject": "Oggetto", - "show_details": "Mostra dettagli", - "hide_details": "Nascondi dettagli", - "external_content_warning": "Le immagini e i contenuti esterni sono stati bloccati", - "load_external_content": "Carica immagini", - "trust_sender": "Considera sempre attendibile questo mittente", - "back_to_list": "Torna all'elenco", - "view_contact": "Visualizza contatto", - "message_details": "Dettagli del messaggio", - "more_reply_options": "Più opzioni di risposta", - "set_tag": "Imposta etichetta", - "tag": "Etichetta", - "more_actions": "Altre azioni", - "move_to": "Sposta in...", - "remove_tag": "Rimuovi etichetta", - "tag_filter_placeholder": "Filtra etichette", - "tag_no_matches": "Nessuna etichetta corrispondente", - "more_count": "+{count} altri", - "characters_count": "{count} caratteri", - "quick_reply_placeholder": "Scrivi una risposta veloce...", - "more_options": "Più opzioni", - "sending": "Invio in corso...", - "security_authentication": "Sicurezza e autenticazione", - "technical_details": "Dettagli tecnici", - "message_id_label": "ID messaggio:", - "reply_to_label": "Rispondi a:", - "delivery_time_label": "Orario di consegna:", - "conversation_part_label": "Parte della conversazione:", - "previous_messages": "{count} messaggio precedente", - "previous_messages_plural": "{count} messaggi precedenti", - "time": { - "day": "giorno", - "days": "giorni", - "hour": "ora", - "hours": "ore", - "minute": "minuto", - "minutes": "minuti" - }, - "unknown_sender": "Sconosciuto", - "recipient_me": "io", - "recipient_and_others": "{name} e altri {count}", - "recipient_to_prefix": "A:", - "authentication": { - "title": "Autenticazione", - "status": { - "verified": "Verificato", - "warning": "Attenzione", - "none": "Non autenticato" - }, - "spf": { - "pass": "SPF superato", - "fail": "SPF fallito", - "none": "Nessun SPF" - }, - "dkim": { - "pass": "DKIM valido", - "fail": "DKIM non valido", - "none": "Nessun DKIM" - }, - "dmarc": { - "pass": "DMARC superato", - "fail": "DMARC fallito", - "none": "Nessun DMARC" - }, - "spam_score": "Punteggio spam", - "tooltip_spf": "Sender Policy Framework: verifica che il server mittente sia autorizzato a inviare e-mail per conto del dominio", - "tooltip_dkim": "DomainKeys Identified Mail: conferma tramite firma crittografica che l'e-mail non è stata alterata durante il transito", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: assicura che SPF e DKIM siano allineati al dominio del mittente e definisce una politica in caso di errore", - "policy": "Criterio", - "result": { - "pass": "Superato", - "fail": "Fallito", - "softfail": "Fallimento lieve", - "neutral": "Neutro", - "permerror": "Errore permanente", - "temperror": "Errore temporaneo", - "none": "Nessuno" - } - }, - "headers": { - "routing": "Instradamento", - "received": "Ricevuto", - "message_id": "ID messaggio", - "list_info": "Informazioni lista" - }, - "tooltips": { - "reply": "Rispondi", - "reply_all": "Rispondi a tutti (a)", - "forward": "Inoltra (f)", - "archive": "Archivia", - "delete": "Elimina", - "star": "Aggiungi stella (s)", - "unstar": "Rimuovi stella (s)", - "compose": "Scrivi (c)", - "previous": "Email precedente", - "next": "Email successiva", - "edit_draft": "Modifica bozza" - }, - "spam": { - "button_title": "Segnala come spam", - "not_spam_title": "Segna come legittimo", - "toast_success": "Spostato in Posta indesiderata", - "toast_batch": "{count} messaggi spostati in Posta indesiderata", - "toast_undo": "Annulla", - "toast_not_spam_success": "Spostato in Posta in arrivo", - "toast_not_spam_batch": "{count} messaggi spostati in Posta in arrivo", - "error": "Impossibile segnalare come spam", - "error_not_spam": "Impossibile ripristinare il messaggio" - }, - "unsubscribe_banner": { - "label": "Newsletter", - "button": "Annulla iscrizione", - "confirm_title": "Annullare l'iscrizione da questo mittente?", - "confirm_button": "Conferma", - "cancel": "Annulla", - "success_http": "Pagina di annullamento iscrizione aperta in una nuova scheda", - "success_mailto": "Email di disiscrizione inviata", - "confirm_message_http": "La pagina di disiscrizione si aprirà in una nuova scheda.", - "confirm_message_mailto": "Verrà inviata un'email di disiscrizione al mittente.", - "error": "Impossibile annullare l'iscrizione", - "dismiss": "Ignora" - }, - "calendar_invitation": { - "loading": "Caricamento dettagli evento…", - "title": "Invito calendario", - "published_title": "Evento pubblicato", - "response_title": "Risposta all'evento", - "update_title": "Aggiornamento evento", - "counter_title": "Controproposta", - "refresh_title": "Richiesta di aggiornamento", - "declined_counter_title": "Controproposta rifiutata", - "cancelled_title": "Evento annullato", - "organizer": "Organizzato da {name}", - "organizer_label": "Organizzato da", - "attendees": "{count, plural, one {# partecipante} other {# partecipanti}}", - "accept": "Accetta", - "maybe": "Forse", - "decline": "Rifiuta", - "add_to_calendar": "Aggiungi al calendario", - "added": "Aggiunto al calendario", - "rsvp_sent": "Risposta inviata", - "parse_error": "Impossibile leggere l'invito", - "action_failed": "Impossibile completare questa azione del calendario.", - "no_calendar": "Calendario non disponibile", - "published_info": "Questo evento è stato condiviso come riferimento.", - "response_info": "Questo messaggio contiene la risposta di un partecipante.", - "response_info_organizer": "Questa risposta aggiorna il tuo evento.", - "update_info": "Questo messaggio aggiorna un evento esistente.", - "counter_info": "Questo messaggio propone modifiche a un evento.", - "counter_info_organizer": "Questo partecipante ha proposto modifiche al tuo evento.", - "refresh_info": "Questo messaggio richiede gli ultimi dettagli dell'evento.", - "refresh_info_organizer": "Un partecipante ha richiesto gli ultimi dettagli dell'evento.", - "declined_counter_info": "L'organizzatore ha rifiutato una controproposta.", - "authentication_failed_info": "I controlli di autenticazione di questo invito non sono riusciti. Tratta con cautela le azioni sul calendario.", - "authentication_missing_info": "Questo invito non include un'autenticazione email verificata. Conferma i dettagli con l'organizzatore se qualcosa sembra insolito.", - "sender_mismatch_info": "Questo invito è stato inviato da {sender}, mentre l'organizzatore indicato nei dati del calendario è {organizer}.", - "sender_mismatch_unverified_info": "Questo invito è stato inviato da {sender}, mentre l'organizzatore indicato nei dati del calendario è {organizer}, e il messaggio non ha potuto essere verificato.", - "organizer_role": "Sei l'organizzatore di questo evento", - "your_response": "La tua risposta: {status}", - "response_needed": "Risposta richiesta", - "response_accepted": "Accettata", - "response_tentative": "Provvisoria", - "response_declined": "Rifiutata", - "response_delegated": "Delegata", - "actor_sent_info": "Inviato da {name}.", - "actor_response_info": "{name} ha risposto {status}.", - "actor_counter_info": "{name} ha proposto modifiche a questo evento.", - "actor_refresh_info": "{name} ha chiesto i dettagli più recenti dell'evento.", - "actor_declined_counter_info": "{name} ha rifiutato la controproposta.", - "actor_note": "Nota: {comment}", - "actor_unknown": "Qualcuno", - "proposed_changes": "Modifiche proposte", - "change_title": "Titolo", - "change_time": "Orario", - "change_location": "Luogo", - "change_description": "Descrizione", - "change_empty": "Nessuno", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Applica modifiche proposte", - "proposal_applied": "Le modifiche proposte sono state applicate.", - "review_proposal": "Rivedi proposta", - "review_request": "Rivedi richiesta", - "view_in_calendar": "Vedi nel calendario", - "select_calendar": "Seleziona calendario", - "already_in_calendar": "Già nel tuo calendario", - "request_info": "Sei stato invitato a questo evento. Rispondi per comunicare la tua disponibilità all'organizzatore.", - "cancel_info": "L'organizzatore ha annullato questo evento.", - "event_updated": "Aggiornamento #{sequence}", - "event_status_tentative": "Provvisorio", - "event_status_cancelled": "Annullato", - "collapse": "Nascondi dettagli", - "expand": "Mostra dettagli" - }, - "previous": "Precedente", - "next": "Successivo", - "send": "Invia", - "more": "altri", - "details": { - "recipients_routing": "Destinatari e instradamento", - "authentication_security": "Autenticazione e sicurezza", - "identifiers_threading": "Identificatori e conversazione", - "mailing_list": "Mailing list", - "message_properties": "Proprietà del messaggio", - "sent": "Inviato", - "received": "Ricevuto", - "delivery_time": "Tempo di consegna", - "in_reply_to": "In-Reply-To", - "references": "Riferimenti", - "thread_id": "ID conversazione", - "size": "Dimensione", - "mime_type": "Tipo MIME", - "attachments_summary": "{count} file · {size}", - "list_id": "ID lista", - "list_help": "Guida lista", - "list_post": "Invio alla lista", - "list_unsubscribe": "Annulla iscrizione", - "iprev": "DNS inverso", - "spam_status": "Stato spam", - "ai_verdict": "Verdetto IA", - "account": "Account", - "no_subject": "(nessun oggetto)" - }, - "scheduled_banner": "Programmato per l’invio il {date}", - "scheduled_send_created": "E-mail programmata per l’invio", - "cancel_scheduled_send": "Annulla invio", - "reschedule_send": "Riprogramma", - "cancel_and_edit": "Annulla e modifica", - "cancel_and_compose_again": "Annulla e componi di nuovo", - "reschedule_prompt": "Inserisci una nuova data/ora, ad es. 2026-05-04T15:30", - "scheduled_actions_only": "La vista Programmate supporta solo azioni di programmazione", - "undo_send_scheduled": "Messaggio programmato per l’invio", - "undo_send": "Annulla invio", - "contact_sidebar": { - "title": "Contatto", - "close": "Chiudi barra laterale", - "action_email": "Email", - "action_email_title": "Invia email", - "action_copy": "Copia", - "action_copy_title": "Copia email", - "action_edit_title": "Modifica contatto", - "section_emails": "Email", - "section_phones": "Telefoni", - "section_organizations": "Organizzazioni", - "section_addresses": "Indirizzi", - "section_notes": "Note", - "not_in_contacts": "Non è nei tuoi contatti", - "add_to_contacts": "Aggiungi ai contatti", - "copied": "Copiato!", - "copy_failed": "Copia non riuscita" - }, - "send_now": "Invia ora" - }, - "email_composer": { - "read_receipt_on": "Conferma di lettura richiesta (clicca per disattivare)", - "read_receipt_off": "Richiedi una conferma di lettura", - "dsn_on": "Notifica di consegna richiesta (clic per disattivare)", - "dsn_off": "Richiedi una notifica di consegna", - "format_plain_text": "Passa a testo semplice", - "format_rich_text": "Passa a testo formattato (HTML)", - "require_tls_on": "Consegna cifrata obbligatoria (clic per disattivare)", - "require_tls_off": "Richiedi consegna cifrata (TLS)", - "new_message": "Nuovo messaggio", - "signature_edit_hint": "Doppio clic per modificare la firma", - "reply": "Rispondi", - "reply_all": "Rispondi a tutti", - "forward": "Inoltra", - "reply_to": "Rispondi", - "reply_all_to": "Rispondi a tutti", - "forward_message": "Inoltra", - "from": "Da", - "to": "A", - "cc": "CC", - "bcc": "CCN", - "subject": "Oggetto", - "body_placeholder": "Scrivi il tuo messaggio...", - "send": "Invia", - "cancel": "Annulla", - "attach": "Allega", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Scarta", - "discard_draft_title": "Eliminare la bozza?", - "discard_draft_confirm": "Ci sono modifiche non salvate. Vuoi scartare questa bozza?", - "saving": "Salvataggio...", - "draft_saved": "Bozza salvata", - "save_failed": "Salvataggio non riuscito", - "to_placeholder": "Indirizzi email dei destinatari", - "cc_placeholder": "Destinatari in copia", - "bcc_placeholder": "Destinatari in copia nascosta", - "subject_placeholder": "Oggetto", - "cc_label": "Cc:", - "bcc_label": "Ccn:", - "subject_label": "Oggetto:", - "file_size_kb": "KB", - "prefix": { - "forward": "Inol:", - "reply": "Re:" - }, - "no_subject": "(Nessun oggetto)", - "unknown_sender": "Sconosciuto", - "quote": { - "reply_header": "Il {date}, {sender} ha scritto:", - "forward_header": "---------- Messaggio inoltrato ----------", - "from": "Da: {sender}", - "date": "Data: {date}", - "subject": "Oggetto: {subject}", - "to": "A: {recipients}" - }, - "remove_sub_address": "Rimuovi sotto-indirizzo", - "from_override": { - "toggle_off": "Sovrascrivi", - "toggle_on": "Annulla sovrascrittura", - "toggle_tooltip": "Modifica liberamente nome e indirizzo del mittente. La posta viene comunque inviata tramite la tua identità.", - "name_label": "Nome mittente", - "name_placeholder": "Nome", - "email_label": "Indirizzo email del mittente", - "email_placeholder": "alias@example.com", - "envelope_notice": "Se il tuo server non accetta questo indirizzo come mittente della busta, viene usato {identity} al suo posto, e i destinatari possono vederlo nell'intestazione Return-Path." - }, - "use_template": "Modello", - "save_as_template": "Salva come modello", - "validation": { - "recipient_required": "Aggiungi un destinatario per inviare", - "body_required": "Scrivi un messaggio o allega un file", - "attachments_uploading": "Gli allegati sono ancora in fase di caricamento: verrà inviato al termine", - "attachment_upload_failed": "Non inviato: caricamento di un allegato non riuscito. Rimuovilo e riprova." - }, - "upload_progress": "Caricamento {uploaded} / {total}", - "upload_cancel": "Annulla caricamento", - "upload_failed": "Caricamento di {filename} non riuscito", - "attachment_too_large": "\"{name}\" supera la dimensione consentita dal server ({max} per file)", - "attachments_total_too_large": "Gli allegati supererebbero il limite del server di {max} per messaggio", - "send_failed": "Invio dell'e-mail non riuscito", - "smime_sign_on": "Firma S/MIME attivata", - "smime_sign_off": "Firma S/MIME disattivata", - "smime_encrypt_on": "Cifratura S/MIME attivata", - "smime_encrypt_off": "Cifratura S/MIME disattivata", - "smime_encrypt_unavailable": "Cifratura S/MIME non disponibile – certificati destinatario mancanti", - "smime_unlock_title": "Sblocca chiave S/MIME", - "smime_unlock_message": "Inserisci la passphrase per sbloccare la tua chiave S/MIME.", - "smime_unlock_button": "Sblocca", - "smime_passphrase_placeholder": "Passphrase S/MIME", - "send_timeout": "Il server ha smesso di rispondere. Controlli la cartella Posta inviata prima di inviare di nuovo: il messaggio potrebbe essere già partito.", - "continue_draft": "Continua bozza", - "close_draft_title": "Salvare o eliminare la bozza?", - "close_draft_message": "Hai modifiche non salvate. Vuoi salvare come bozza o eliminare?", - "drop_files": "Trascina i file per allegarli", - "show_less": "Mostra meno", - "forgot_attachment": { - "title": "Hai dimenticato un allegato?", - "message": "Il tuo messaggio menziona \"{keyword}\" ma nessun file è allegato. Inviare comunque?", - "send_anyway": "Invia comunque", - "back": "Torna alla modifica" - }, - "empty_subject": { - "title": "Inviare senza oggetto?", - "message": "Questo messaggio non ha un oggetto. Inviarlo comunque?", - "dont_ask_again": "Non chiedere più", - "send_anyway": "Invia comunque", - "back": "Torna alla modifica" - }, - "add_link": "Aggiungi link", - "link_url_prompt": "Inserisci l'URL", - "sending": "Invio in corso...", - "schedule_send": "Programma invio", - "schedule_send_description": "Scegli quando il server deve rilasciare questo messaggio.", - "schedule_send_required": "Scegli una data e un’ora.", - "schedule_send_invalid": "Inserisci una data e un’ora valide.", - "schedule_send_future": "Scegli una data e un’ora future.", - "schedule_send_too_late": "Questo orario supera il limite consentito dal server.", - "schedule_send_unsupported": "L’invio programmato non è supportato per questo account.", - "schedule_send_cleanup_warning": "L’invio programmato è stato creato, ma la pulizia della bozza non è riuscita.", - "send_delay_unsupported": "Il ritardo di invio non è supportato per questo account.", - "send_delay_unsupported_confirm": "Questo account non supporta il ritardo di invio. Inviare subito?", - "recipient_edit_email": "Modifica indirizzo email", - "recipient_edit_name": "Modifica nome visualizzato", - "recipient_email_placeholder": "Indirizzo email", - "recipient_name_placeholder": "Nome visualizzato", - "autocomplete_search_server": "Cerca nel server", - "autocomplete_searching": "Ricerca in corso...", - "toolbar": { - "bold": "Grassetto", - "italic": "Corsivo", - "underline": "Sottolineato", - "strikethrough": "Barrato", - "text_color": "Colore del testo", - "remove_color": "Rimuovi colore", - "heading_1": "Titolo 1", - "heading_2": "Titolo 2", - "bullet_list": "Elenco puntato", - "ordered_list": "Elenco numerato", - "quote": "Citazione", - "code_block": "Blocco di codice", - "align_left": "Allinea a sinistra", - "align_center": "Centra", - "align_right": "Allinea a destra", - "text_direction": "Direzione del testo (RTL/LTR)", - "link": "Link", - "table": "Tabella", - "clear_formatting": "Cancella formattazione", - "undo": "Annulla", - "redo": "Ripeti", - "add_row_above": "Aggiungi riga sopra", - "add_row_below": "Aggiungi riga sotto", - "add_column_before": "Aggiungi colonna prima", - "add_column_after": "Aggiungi colonna dopo", - "delete_row": "Elimina riga", - "delete_column": "Elimina colonna", - "toggle_header_row": "Attiva/disattiva riga di intestazione", - "delete_table": "Elimina tabella", - "pick_size": "Scegli dimensione", - "font_size": "Dimensione carattere", - "font_size_default": "Predefinita", - "background_color": "Colore di sfondo", - "remove_background_color": "Rimuovi colore di sfondo" - }, - "send_filing_warning": "Inviato - ma la pulizia successiva non è riuscita, potrebbe restare una bozza obsoleta." - }, - "confirm_dialog": { - "confirm": "Conferma", - "cancel": "Annulla" - }, - "common": { - "loading": "Caricamento...", - "error": "Errore", - "success": "Successo", - "cancel": "Annulla", - "save": "Salva", - "delete": "Elimina", - "edit": "Modifica", - "close": "Chiudi", - "search": "Cerca", - "refresh": "Aggiorna", - "settings": "Impostazioni", - "help": "Aiuto", - "logout": "Esci", - "yes": "Sì", - "no": "No", - "unknown": "Sconosciuto", - "app_title": "Webmail", - "reconnecting": "Connessione persa. Tentativo di riconnessione…", - "rate_limited_title": "L'autenticazione del server e temporaneamente limitata.", - "rate_limited_detail": "Bulwark ha sospeso le richieste in background per evitare il blocco. Nuovo tentativo tra {seconds}s.", - "rate_limited_action_title": "Richiesta sospesa per evitare il blocco.", - "rate_limited_action_detail": "Bulwark attende che termini il cooldown del server prima di inviare altre richieste autenticate. Riprova tra {seconds}s.", - "pull_to_refresh": "Trascina per aggiornare", - "release_to_refresh": "Rilascia per aggiornare", - "refreshing": "Aggiornamento…" - }, - "notifications": { - "email_sent": "Messaggio inviato con successo", - "email_deleted": "Messaggio eliminato", - "email_archived": "Messaggio archiviato", - "emails_deleted": "{count, plural, one {# email eliminata} other {# email eliminate}}", - "emails_archived": "{count, plural, one {# email archiviata} other {# email archiviate}}", - "error_archiving": "Impossibile archiviare l'email", - "error_updating": "Impossibile aggiornare l'email", - "email_starred": "Stella aggiunta al messaggio", - "email_unstarred": "Stella rimossa dal messaggio", - "email_marked_read": "Messaggio segnato come letto", - "email_marked_unread": "Messaggio segnato come non letto", - "copied_to_clipboard": "Copiato negli appunti", - "source_copied": "Sorgente copiata negli appunti", - "error_sending": "Impossibile inviare il messaggio", - "error_deleting": "Impossibile eliminare il messaggio", - "error_loading": "Impossibile caricare i messaggi", - "new_email": "Nuovo messaggio", - "new_email_from": "Da {sender}", - "click_to_view": "Clicca per visualizzare", - "email_moved": "Messaggio spostato", - "emails_moved": "{count} messaggi spostati", - "moved_to_mailbox": "Spostato in {mailbox}", - "move_failed": "Spostamento non riuscito", - "move_error": "Impossibile spostare i messaggi nella cartella selezionata", - "email_tagged": "Email etichettata", - "emails_tagged": "{count} email etichettate", - "tag_failed": "Etichettatura fallita", - "identity_created": "Identità creata con successo", - "identity_updated": "Identità aggiornata con successo", - "identity_deleted": "Identità eliminata", - "identity_set_primary": "Identità principale aggiornata", - "identity_create_failed": "Impossibile creare l'identità: {error}", - "identity_update_failed": "Impossibile aggiornare l'identità: {error}", - "identity_delete_failed": "Impossibile eliminare l'identità: {error}", - "identity_unauthorized": "Non sei autorizzato a inviare da questo indirizzo email", - "identity_not_found": "Identità non trovata", - "vacation_saved": "Impostazioni del risponditore automatico salvate", - "vacation_save_failed": "Impossibile salvare le impostazioni del risponditore automatico", - "filters_saved": "Filtri salvati con successo", - "filters_save_failed": "Impossibile salvare i filtri", - "filters_deleted": "Regola di filtraggio eliminata", - "templates_exported": "Modelli esportati con successo", - "templates_imported": "{count, plural, one {# modello importato} other {# modelli importati}}", - "templates_import_errors": "Alcuni modelli non sono stati importati", - "templates_import_empty": "Nessun modello trovato nel file", - "export_email_error": "Impossibile esportare l'e-mail", - "import_email_success": "E-mail importata con successo", - "import_email_error": "Impossibile importare l'e-mail" - }, - "date": { - "today": "Oggi", - "yesterday": "Ieri", - "this_week": "Questa settimana", - "last_week": "Settimana scorsa", - "this_month": "Questo mese", - "older": "Meno recenti", - "just_now": "Proprio ora", - "minutes_ago": "{count} minuto fa", - "minutes_ago_plural": "{count} minuti fa", - "hours_ago": "{count} ora fa", - "hours_ago_plural": "{count} ore fa", - "days_ago": "{count} giorno fa", - "days_ago_plural": "{count} giorni fa" - }, - "language": { - "title": "Lingua", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Seleziona lingua", - "switch_to_english": "Passa all'inglese", - "switch_to_french": "Passa al francese", - "switch_to_japanese": "Passa al giapponese", - "switch_to_spanish": "Passa allo spagnolo", - "switch_to_italian": "Passa all'italiano", - "switch_to_german": "Passa al tedesco", - "switch_to_dutch": "Passa all'olandese", - "switch_to_portuguese": "Passa al portoghese", - "switch_to_russian": "Passa al russo", - "switching": "Cambio lingua in corso...", - "switch": "Cambia lingua", - "current": "Lingua corrente", - "polish": "Polski", - "switch_to_polish": "Passa al polacco", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Passa al persiano", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Passa al mongolo" - }, - "settings": { - "title": "Impostazioni", - "back_to_mail": "Torna alla posta", - "save_success": "Impostazioni salvate con successo", - "import_success": "Impostazioni importate con successo", - "import_error": "Impossibile importare le impostazioni", - "reset_confirm": "Sei sicuro di voler ripristinare tutte le impostazioni ai valori predefiniti?", - "unsaved_changes": "Hai modifiche non salvate", - "discard_changes": "Annullare le modifiche?", - "discard": "Annulla", - "keep_editing": "Continua a modificare", - "tabs": { - "appearance": "Aspetto", - "language": "Lingua, regione e ora", - "email": "Comportamento email", - "composer": "Editor", - "privacy": "Privacy e sicurezza", - "account": "Account", - "identities": "Identità", - "vacation": "Risponditore automatico", - "advanced": "Avanzate", - "calendar": "Calendario", - "filters": "Filtri", - "templates": "Modelli", - "folders": "Cartelle", - "keywords": "Etichette", - "security": "Sicurezza", - "encryption": "Cifratura", - "files": "File", - "contacts": "Contatti", - "protocol_handlers": "App predefinite", - "sidebar_apps": "App nella barra laterale", - "notifications": "Notifiche", - "layout": "Layout", - "reading": "Lettura", - "composing": "Composizione", - "downloads": "Download", - "content_senders": "Contenuto e mittenti", - "about_data": "Informazioni e dati", - "debug": "Debug" - }, - "tab_groups": { - "general": "Generale", - "account": "Account e identità", - "organization": "Organizzazione e-mail", - "apps": "Applicazioni", - "system": "Sistema", - "appearance": "Aspetto", - "mail": "Posta", - "privacy": "Privacy e sicurezza", - "advanced": "Avanzate" - }, - "appearance": { - "message_list_order": { - "label": "Ordine dell'elenco messaggi", - "description": "Come vengono ordinati i messaggi nell'elenco. L'ordinamento è eseguito dal server, quindi riguarda l'intera cartella e non solo i messaggi caricati.", - "preset": { - "chronological": "Cronologico (più recenti prima)", - "unread_first": "Non letti prima", - "starred_first": "Speciali prima", - "tagged_first": "Con tag prima", - "custom": "Personalizzato" - }, - "tag_label": "Tag", - "advanced_show": "Avanzate: modifica i livelli di ordinamento", - "advanced_hide": "Nascondi avanzate", - "criterion_label": "Criterio del livello {n}", - "direction_label": "Direzione del livello {n}", - "criteria": { - "unread": "Non letto", - "starred": "Speciale", - "tag": "Tag", - "receivedAt": "Data di ricezione", - "sentAt": "Data di invio", - "from": "Mittente", - "subject": "Oggetto", - "size": "Dimensione" - }, - "direction": { - "unread_first": "Non letti prima", - "read_first": "Letti prima", - "starred_first": "Speciali prima", - "unstarred_first": "Non speciali prima", - "tagged_first": "Con tag prima", - "untagged_first": "Senza tag prima", - "newest_first": "Più recenti prima", - "oldest_first": "Più vecchi prima", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Più grandi prima", - "smallest_first": "Più piccoli prima" - }, - "add_level": "Aggiungi livello", - "remove_level": "Rimuovi livello", - "move_up": "Sposta su", - "move_down": "Sposta giù", - "tiebreak_note": "Fino a tre livelli; i livelli successivi risolvono i pareggi. «Più recenti prima» viene sempre applicato per ultimo.", - "scope": { - "label": "Applica a tutte le cartelle", - "description": "Se disattivato, l'ordine vale solo per la Posta in arrivo; le altre cartelle restano cronologiche." - }, - "unsupported_note": "Il tuo server di posta non supporta l'ordinamento per stato di lettura, speciali o tag. Questi livelli vengono ignorati e l'elenco resta cronologico." - }, - "title": "Aspetto", - "description": "Personalizza l'aspetto della tua webmail", - "theme": { - "label": "Tema", - "description": "Scegli la tua combinazione di colori preferita", - "light": "Chiaro", - "dark": "Scuro", - "system": "Sistema" - }, - "language": { - "label": "Lingua", - "description": "Scegli la tua lingua preferita" - }, - "font_size": { - "label": "Dimensione carattere", - "description": "Regola la dimensione del testo per una migliore leggibilità", - "small": "Piccolo", - "medium": "Medio", - "large": "Grande" - }, - "list_density": { - "label": "Densità", - "description": "Controlla la spaziatura in tutta l'interfaccia", - "extra_compact": "Molto compatto", - "compact": "Compatto", - "regular": "Normale", - "comfortable": "Comodo" - }, - "animations": { - "label": "Abilita animazioni", - "description": "Mostra transizioni ed effetti fluidi" - }, - "toolbar_position": { - "label": "Posizione barra degli strumenti", - "description": "Dove mostrare i pulsanti azione e-mail (Rispondi, Archivia, Elimina, ecc.)", - "top": "In alto", - "below_subject": "Sotto l'oggetto" - }, - "toolbar_labels": { - "label": "Mostra etichette della barra degli strumenti", - "description": "Mostra le etichette di testo accanto alle icone della barra degli strumenti. Disattivare per risparmiare spazio una volta acquisita familiarità con le icone." - }, - "hide_account_switcher": { - "label": "Nascondi selettore account dalla barra laterale", - "description": "Nascondi il selettore account nella parte superiore della barra laterale delle cartelle. Puoi comunque cambiare account dalla barra di navigazione inferiore." - }, - "show_rail_account_list": { - "label": "Mostra avatar account nella barra di navigazione", - "description": "Visualizza i cerchi degli account individuali nella parte inferiore della barra di navigazione per un cambio rapido, con un pulsante di disconnessione sotto." - }, - "unified_mailbox": { - "label": "Casella di posta unificata", - "description": "Mostra le cartelle combinate (Posta in arrivo, Inviati, ecc.) di tutti gli account collegati", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Includi le caselle di gruppo", - "description": "Includi anche le caselle condivise o di gruppo nella vista unificata." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Icone colorate nella barra laterale", - "description": "Colora le icone di cartelle ed etichette per tipo (Posta in arrivo blu, Spam rosso, Inviati verde, ecc.). Disattiva per una barra laterale monocromatica." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Mostra il numero totale di messaggi", - "description": "Mostra il numero totale di messaggi accanto a cartelle ed etichette, insieme al conteggio dei non letti. Disattiva per mostrare solo i non letti." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Interfaccia Pro (sperimentale)", - "description": "Layout per utenti esperti solo desktop con esplorazione messaggi a più schede e flussi tra account. L'interfaccia standard non è influenzata; puoi tornare indietro in qualsiasi momento.", - "open_label": "Apri interfaccia Pro", - "back_to_standard": "Torna allo standard" - }, - "cross_unread": { - "label": "Tutti gli account: Non letti", - "description": "Mostra una voce nella sezione Tutti gli account che elenca la posta non letta di tutti gli account, in tutte le cartelle tranne posta indesiderata, inviata, archivio, cestino e bozze." - }, - "cross_starred": { - "label": "Tutti gli account: Speciali", - "description": "Mostra una voce nella sezione Tutti gli account che elenca la posta speciale di tutti gli account, in tutte le cartelle tranne posta indesiderata, inviata, archivio, cestino e bozze." - }, - "cross_all": { - "label": "Tutti gli account: Tutta la posta", - "description": "Mostra una voce nella sezione Tutti gli account che elenca tutta la posta di tutti gli account, in tutte le cartelle tranne posta indesiderata, inviata, archivio, cestino e bozze." - } - }, - "keywords": { - "title": "Etichette e-mail", - "description": "Definisci etichette per organizzare le tue e-mail con i colori. Vengono archiviate come parole chiave JMAP sul server.", - "add_keyword": "Aggiungi etichetta", - "label_field": "Nome visualizzato", - "label_placeholder": "es. Lavoro, Personale, Urgente", - "id_field": "ID etichetta", - "id_placeholder": "es. lavoro, personale", - "color_field": "Colore", - "id_exists": "Questo ID etichetta esiste già", - "edit": "Modifica etichetta", - "delete": "Elimina etichetta", - "save": "Salva", - "add": "Aggiungi", - "cancel": "Annulla", - "migrating": "Aggiornamento dell'etichetta nelle e-mail esistenti…", - "migration_error": "Impossibile aggiornare l'etichetta nelle e-mail esistenti", - "nesting": { - "label": "Etichette nidificate", - "description": "Nidifica le etichette sotto altre etichette e mostrale come un albero nella barra laterale." - }, - "parent_field": "Etichetta principale", - "no_parent": "Nessuna etichetta principale", - "too_long": "Questo percorso di etichetta è troppo lungo (al massimo {max} caratteri)", - "has_children_locked": "Altre etichette sono nidificate sotto questa, quindi il suo nome e la sua etichetta principale sono bloccati. Spostale o rimuovile prima.", - "has_children_delete": "Rimuovi prima le etichette nidificate sotto questa", - "visibility_field": "Visibilità nella barra laterale", - "visibility": { - "show": "Mostra", - "unread": "Mostra se non letti", - "hide": "Nascondi" - }, - "unrecognized": { - "title": "Recupera le etichette dal server", - "description": "I nomi e i colori delle etichette sono salvati in questo browser, ma le etichette vere e proprie si trovano sulle tue e-mail come parole chiave JMAP. Analizza la posta alla ricerca di parole chiave che nessuna etichetta qui spiega, poi aggiungile di nuovo.", - "scan": "Cerca etichette", - "rescan": "Analizza di nuovo", - "scanning": "Analisi in corso… {scanned} di {total} e-mail", - "none": "Tutte le etichette presenti sulle tue e-mail sono già definite qui.", - "messages": "{count, plural, one {# e-mail} other {# e-mail}}", - "add_all": "Aggiungi tutte ({count})", - "partial": "Sono state analizzate solo le {scanned} e-mail più recenti su {total}. Le etichette usate solo su messaggi più vecchi potrebbero mancare.", - "error": "Impossibile analizzare la posta alla ricerca di etichette." - } - }, - "notifications": { - "test_sound": "Testa il suono di notifica", - "sounds": { - "default": "Predefinito (Bip)", - "cheerful": "Allegro", - "involved": "Elaborato", - "swift": "Gesto veloce", - "relax": "Rilassante" - }, - "sound_selection": { - "title": "Suono di notifica", - "description": "Scegli quale suono riprodurre per le notifiche", - "choose": "Suono", - "choose_desc": "Seleziona un tono di notifica e clicca sull'icona dell'altoparlante per l'anteprima" - }, - "email": { - "title": "Notifiche e-mail", - "description": "Configura le notifiche per le e-mail in arrivo", - "enabled": "Notifiche e-mail", - "enabled_desc": "Mostra notifiche all'arrivo di nuove e-mail", - "sound": "Suono di notifica", - "sound_desc": "Riproduci un suono all'arrivo di nuove e-mail", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Notifiche calendario", - "description": "Configura le notifiche per gli eventi del calendario", - "enabled": "Notifiche eventi", - "enabled_desc": "Mostra avvisi per i prossimi eventi del calendario", - "sound": "Suono di notifica", - "sound_desc": "Riproduci un suono per i promemoria del calendario", - "invitation_parsing": "Analizza inviti via e-mail", - "invitation_parsing_desc": "Rileva inviti calendario negli allegati e mostra azioni calendario" - }, - "push": { - "confirm_disable_message": "Questo dispositivo non riceverà più avvisi quando il sito è chiuso.", - "confirm_disable_title": "Disabilitare le notifiche in background?", - "description": "Ricevi notifiche di sistema per la nuova posta quando questo sito è chiuso. Consegnato tramite il relay push Bulwark; il relay non vede mai il contenuto della posta.", - "disable": "Disabilita", - "enable": "Abilita", - "dismiss_aria": "Chiudi avviso di notifiche", - "ios_hint": "Su iOS, installa prima il sito sulla schermata Home: Safari consegna Web Push solo alle PWA installate.", - "reenable": "Registra di nuovo", - "relay_desc": "Per impostazione predefinita usa il relay Bulwark ospitato. Cambialo solo se ospiti in autonomia.", - "relay_label": "Relay push", - "relay_locked": "Impostato dall'amministratore", - "relay_locked_desc": "L'URL del relay push è stato impostato dall'amministratore e non può essere modificato.", - "status_active": "Attivo su questo dispositivo", - "status_busy": "In elaborazione…", - "status_inactive": "Non abilitato su questo dispositivo", - "status_unsupported": "Questo browser non supporta Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Notifiche in background", - "devices_title": "Dispositivi registrati", - "devices_desc": "Tutti i dispositivi registrati per ricevere le notifiche in background di questo account. Revocandone uno, smette subito di riceverle.", - "devices_loading": "Caricamento dei dispositivi registrati…", - "devices_empty": "Nessun dispositivo è registrato per le notifiche in background.", - "devices_error": "Impossibile caricare i dispositivi registrati.", - "devices_refresh": "Aggiorna", - "device_this": "Questo dispositivo", - "device_other": "Altro dispositivo", - "device_status_active": "Consegna le notifiche", - "device_status_inactive": "Non ha mai consegnato una notifica", - "device_status_unknown": "Sconosciuto a questo relay", - "device_expires": "Scade il {date}", - "revoke": "Revoca", - "confirm_revoke_title": "Revocare questo dispositivo?", - "confirm_revoke_message": "Smetterà subito di ricevere le notifiche in background. Riattivando il push su quel dispositivo verrà registrato di nuovo.", - "confirm_revoke_message_this": "Questo dispositivo smetterà subito di ricevere le notifiche in background." - } - }, - "language_region": { - "title": "Lingua, regione e ora", - "description": "Lingua, formato data, formato ora e altre preferenze regionali", - "language": { - "label": "Lingua", - "description": "Scegli la tua lingua preferita", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Formato data", - "description": "Come vengono visualizzate le date nell'elenco delle e-mail", - "smart": "Intelligente (in base alla regione)", - "relative": "Relativo (1 h fa, 2 g fa)", - "full": "Sempre data completa", - "preview_today": "Oggi:", - "preview_this_week": "Questa settimana:", - "preview_older": "Più vecchio:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Formato ora", - "description": "Scegli tra formato 12 o 24 ore", - "12h": "12 ore", - "24h": "24 ore" - }, - "first_day": { - "label": "Primo giorno della settimana", - "description": "Inizia la settimana di domenica o lunedì", - "sunday": "Domenica", - "monday": "Lunedì" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Cancella la ricerca quando cambi cartella", - "description": "Facendo clic su una cartella (Posta in arrivo, Inviati…) la ricerca attiva viene cancellata e si apre quella cartella. Se disattivato, la ricerca resta attiva e viene rieseguita nella cartella scelta." - }, - "request_read_receipt": { - "label": "Richiedi conferme di lettura per impostazione predefinita", - "description": "Attiva in anticipo la richiesta di conferma di lettura durante la composizione di un nuovo messaggio." - }, - "read_receipt_response": { - "label": "Rispondi alle richieste di conferma di lettura", - "description": "Cosa fare quando un messaggio in arrivo richiede una conferma di lettura.", - "ask": "Chiedi ogni volta", - "always": "Invia sempre", - "never": "Non inviare mai" - }, - "title": "Comportamento email", - "description": "Configura come vengono gestiti i messaggi", - "mark_read": { - "label": "Segna come letto", - "description": "Quando segnare i messaggi come letti all'apertura", - "instant": "Istantaneamente", - "delay_3s": "Dopo 3 secondi", - "delay_5s": "Dopo 5 secondi", - "never": "Mai" - }, - "delete_action": { - "label": "Azione di eliminazione", - "description": "Cosa accade quando elimini un messaggio", - "trash": "Sposta nel cestino", - "trash_and_read": "Sposta nel cestino e segna come letto", - "permanent": "Elimina definitivamente", - "warning": "I messaggi verranno eliminati definitivamente e non potranno essere recuperati. Questa azione è irreversibile." - }, - "message_spacing": { - "label": "Spaziatura del messaggio", - "description": "Margine attorno al corpo del messaggio nel lettore", - "auto": "Automatica (consigliata)", - "always": "Aggiungi sempre spaziatura", - "edge": "Da bordo a bordo" - }, - "plain_text_font": { - "label": "Carattere del testo semplice", - "description": "Carattere usato per le email in testo semplice", - "mono": "Monospazio", - "sans": "Normale (come l'app)" - }, - "archive_mode": { - "label": "Archivia in", - "description": "Come organizzare le email durante l'archiviazione", - "single": "Una singola cartella", - "year": "Una cartella per anno", - "month": "Una cartella per mese", - "reorganize": "Riorganizza archivio esistente", - "reorganize_success": "{count, plural, =0 {Nessuna email da riorganizzare} =1 {1 email riorganizzata} other {# email riorganizzate}}", - "reorganize_error": "Impossibile riorganizzare l'archivio" - }, - "permanently_delete_junk": { - "label": "Elimina spam definitivamente", - "description": "Elimina definitivamente i messaggi dalla cartella Spam invece di spostarli nel cestino" - }, - "mail_layout": { - "label": "Layout della posta", - "description": "Scegli tra il classico pannello di lettura diviso e un flusso di lettura focalizzato in stile Gmail.", - "split": "Pannello diviso", - "split_description": "Mantieni la lista dei messaggi e il pannello di lettura visibili affiancati.", - "focus": "Lista focalizzata", - "focus_description": "Mostra una riga per messaggio e apri la posta a larghezza piena mantenendo visibile la barra laterale delle cartelle.", - "horizontal": "Riquadro di lettura in basso", - "horizontal_description": "Mostra l'elenco dei messaggi in alto e apri il messaggio selezionato in un riquadro di lettura sotto." - }, - "disable_threading": { - "label": "Disabilita raggruppamento conversazioni", - "description": "Mostra le email come messaggi singoli anziché raggruppati per conversazione" - }, - "plain_text_mode": { - "label": "Solo testo normale", - "description": "Disabilita l'editor di testo formattato e invia tutte le email solo come testo normale, incluse risposte e inoltri" - }, - "auto_select_reply_identity": { - "label": "Seleziona automaticamente l'indirizzo di risposta", - "description": "Quando rispondi, passa automaticamente l'indirizzo mittente all'identità che ha ricevuto il messaggio originale" - }, - "reply_identity_match": { - "label": "Corrispondenza dell'indirizzo di ricezione", - "description": "Quali indirizzi di ricezione contano come tuoi. «Solo indirizzo esatto» sceglie soltanto una delle tue identità configurate; «stesso dominio» tratta anche qualsiasi altro indirizzo dei tuoi domini di identità come alias catch-all e riscrive con esso l'intestazione Da. Scegli l'indirizzo esatto se quegli indirizzi sono liste di distribuzione.", - "exact": "Solo indirizzo esatto", - "domain": "Qualsiasi indirizzo dei miei domini" - }, - "signature_position": { - "label": "Posizione della firma", - "description": "Dove inserire la tua firma nelle risposte e negli inoltri. Sopra il testo citato si legge in modo naturale come chiusura della risposta; sotto mantiene il messaggio originale contiguo.", - "above_quote": "Prima del testo citato", - "below_quote": "Dopo il testo citato" - }, - "signature_separator": { - "label": "Delimitatore firma", - "description": "Anteporre alla firma la riga di delimitazione standard \"-- \" (RFC 3676). Disattiva se preferisci passare direttamente dal messaggio alla firma." - }, - "sub_address_delimiter": { - "label": "Delimitatore sub-indirizzo", - "description": "Carattere che separa il tuo nome utente dall'etichetta del sub-indirizzo. Usa il delimitatore configurato sul tuo server di posta (es. utente{delimiter}tag@dominio.com).", - "option": "{delimiter} (utente{delimiter}tag@dominio.com)", - "custom": "Personalizzato…", - "custom_input_label": "Carattere delimitatore personalizzato" - }, - "show_preview": { - "label": "Mostra anteprima testo", - "description": "Visualizza l'anteprima del messaggio nell'elenco", - "focus_description": "Visualizza il testo di anteprima in linea nella lista di messaggi focalizzata a riga singola" - }, - "attachment_click_action": { - "label": "Azione al clic sugli allegati", - "description": "Scegli se facendo clic su un allegato si apre un'anteprima o si avvia subito il download", - "preview": "Anteprima quando possibile", - "download": "Scarica immediatamente" - }, - "attachment_position": { - "label": "Posizione degli allegati", - "description": "Dove visualizzare gli allegati nell'intestazione dell'email", - "beside-sender": "Accanto al mittente", - "below-header": "Sotto l'intestazione" - }, - "emails_per_page": { - "10": "10 messaggi", - "25": "25 messaggi", - "50": "50 messaggi", - "100": "100 messaggi", - "label": "Messaggi per pagina", - "description": "Numero di messaggi da caricare alla volta" - }, - "always_light_mode": { - "label": "Mostra sempre le email in modalità chiara", - "description": "Visualizza il contenuto delle email in modalità chiara anche quando l'app è in modalità scura, evitando problemi di conversione" - }, - "external_content": { - "label": "Contenuti esterni", - "description": "Come gestire immagini e contenuti esterni", - "ask": "Chiedi sempre", - "block": "Blocca sempre", - "allow": "Consenti sempre" - }, - "trusted_senders": { - "label": "Mittenti attendibili", - "description": "Gestisci i mittenti le cui immagini si caricano automaticamente", - "count_zero": "Nessuno", - "count_one": "1 mittente", - "count_other": "{count} mittenti", - "modal_title": "Mittenti attendibili", - "empty_title": "Nessun mittente attendibile ancora", - "empty_description": "Durante la visualizzazione di un messaggio con immagini bloccate, clicca \"Considera sempre attendibile questo mittente\" per aggiungerlo qui.", - "add_manually": "Aggiungi mittente manualmente", - "add_button": "Aggiungi", - "add_placeholder": "Inserisci indirizzo email", - "search_placeholder": "Cerca mittenti...", - "no_results": "Nessun mittente corrisponde alla tua ricerca", - "remove": "Rimuovi", - "close": "Chiudi", - "invalid_email": "Inserisci un indirizzo email valido", - "already_added": "Questo mittente è già attendibile", - "save_error": "Salvataggio non riuscito - controlla il log di debug Contatti per i dettagli", - "use_address_book_label": "Sincronizza con la rubrica", - "use_address_book_description": "Salva i mittenti attendibili in una rubrica \"Mittenti attendibili\" dedicata per sincronizzarli su tutti i tuoi dispositivi" - }, - "hover_actions": { - "label": "Azioni rapide al passaggio del mouse", - "description": "Scegli quali azioni rapide appaiono al passaggio del mouse su un'email nella lista", - "delete": "Elimina", - "star": "Segna / Rimuovi stella", - "mark_read": "Segna come letto / non letto", - "archive": "Archivia", - "tag": "Etichetta", - "spam": "Segna come spam", - "not_spam": "Non spam", - "none_selected": "Nessuna azione selezionata", - "mode_label": "Modalità di visualizzazione", - "mode_inline": "In linea", - "mode_floating": "Fluttuante", - "corner_label": "Posizione fluttuante", - "corner_top-left": "In alto a sinistra", - "corner_top-right": "In alto a destra", - "corner_bottom-left": "In basso a sinistra", - "corner_bottom-right": "In basso a destra" - }, - "default_mail_program": { - "label": "Programma di posta predefinito", - "description": "Registra {appName} come programma di posta predefinito per i link mailto:", - "button": "Imposta come predefinito", - "success": "Il browser ha chiesto di impostare come predefinito", - "error": "Il tuo browser non supporta questa funzionalità" - }, - "attachment_reminder": { - "label": "Promemoria allegati", - "description": "Avvisa prima dell'invio quando il messaggio menziona allegati ma nessuno è allegato", - "keywords_label": "Parole chiave di attivazione", - "keywords_description": "Parole o frasi che attivano il promemoria quando trovate nel messaggio", - "add_placeholder": "Aggiungi parola chiave...", - "add": "Add", - "remove": "Rimuovi" - }, - "empty_subject_warning": { - "label": "Avviso oggetto vuoto", - "description": "Chiedi conferma prima di inviare un messaggio senza oggetto." - }, - "hide_inline_image_attachments": { - "label": "Nascondi le immagini inline dagli allegati", - "description": "Le immagini incorporate nel corpo del messaggio non vengono elencate come allegati separati" - }, - "attachment_image_previews": { - "label": "Mostra anteprime delle immagini negli allegati", - "description": "Mostra gli allegati immagine come miniature anziché con icone di file generiche" - }, - "send_delay": { - "label": "Annulla invio / ritardo di invio", - "description": "Ritarda gli invii normali con una breve finestra lato server.", - "off": "Disattivato", - "seconds": "{seconds} secondi", - "unsupported": "L’account corrente non dichiara il supporto all’invio ritardato. L’impostazione resta salvata per altri account." - }, - "return_to_list_after_action": { - "label": "Torna all'elenco dopo l'eliminazione o il contrassegno come non letto", - "description": "Dopo aver eliminato o segnato come non letto il messaggio aperto, torna all'elenco dei messaggi invece di aprire il messaggio successivo." - }, - "swipe_right_action": { - "label": "Azione per lo scorrimento a destra (mobile)", - "description": "Sui dispositivi touch, l'azione eseguita quando scorri un messaggio verso destra nell'elenco." - }, - "swipe_left_action": { - "label": "Azione per lo scorrimento a sinistra (mobile)", - "description": "Sui dispositivi touch, l'azione eseguita quando scorri un messaggio verso sinistra nell'elenco." - }, - "swipe_actions": { - "none": "Nessuna", - "archive": "Archivia", - "delete": "Elimina", - "mark_read": "Alterna letto", - "star": "Alterna stella", - "spam": "Segna come spam" - }, - "rtl_editing": { - "label": "Supporto alla scrittura da destra a sinistra", - "description": "Aggiunge un pulsante di direzione alla barra degli strumenti dell'editor per impostare i paragrafi da sinistra a destra o da destra a sinistra" - } - }, - "composer": { - "title": "Editor", - "description": "Configura le impostazioni di composizione email", - "autosave": { - "label": "Intervallo di salvataggio automatico", - "description": "Con quale frequenza salvare automaticamente le bozze", - "30s": "Ogni 30 secondi", - "1m": "Ogni minuto", - "2m": "Ogni 2 minuti", - "5m": "Ogni 5 minuti" - }, - "send_confirmation": { - "label": "Conferma invio", - "description": "Chiedi conferma prima di inviare messaggi" - }, - "default_reply": { - "label": "Modalità risposta predefinita", - "description": "Azione predefinita quando si fa clic su rispondi", - "reply": "Rispondi", - "reply_all": "Rispondi a tutti" - } - }, - "privacy": { - "title": "Privacy e sicurezza", - "description": "Gestisci le tue impostazioni di privacy e sicurezza", - "external_images": { - "label": "Blocca immagini esterne", - "description": "Impedisci il tracciamento attraverso immagini esterne" - }, - "session_timeout": { - "label": "Timeout sessione", - "description": "Disconnetti automaticamente dopo inattività", - "never": "Mai", - "30m": "30 minuti", - "1h": "1 ora", - "4h": "4 ore" - }, - "clear_cache": { - "label": "Cancella cache", - "description": "Rimuovi dati memorizzati nella cache e file temporanei", - "button": "Cancella cache", - "confirm": "Sei sicuro di voler cancellare la cache?", - "success": "Cache cancellata con successo" - } - }, - "account": { - "title": "Account", - "description": "Visualizza le informazioni del tuo account", - "name_label": "Nome visualizzato", - "username_label": "Nome utente", - "account_type_label": "Tipo di account", - "auth_method_label": "Autenticazione", - "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", - "auth_method_basic": "Password", - "demo_account": "Account dimostrativo", - "email": { - "label": "Indirizzo email", - "value": "{email}" - }, - "server": { - "label": "Server JMAP", - "value": "{server}" - }, - "storage": { - "label": "Utilizzo spazio", - "used": "{used} di {total} utilizzati", - "percentage": "{percent}% utilizzato" - }, - "last_sync": { - "label": "Ultima sincronizzazione", - "value": "{time}" - }, - "accounts": { - "title": "Account connessi", - "description": "Trascina per riordinare la visualizzazione degli account nel menu a discesa", - "active": "Account attualmente attivo", - "default_badge": "Account predefinito", - "set_default": "Imposta come predefinito", - "switch_to": "Passa a questo account", - "move_up": "Sposta su", - "move_down": "Sposta giù", - "drag_handle": "Trascina per riordinare", - "add": "Aggiungi account" - }, - "shared_accounts": { - "title": "Condivisi con me", - "description": "Account di gruppo e condivisi che puoi gestire. Selezionane uno per modificarne filtri, risponditore automatico, calendari e contatti.", - "shared_label": "Account condiviso" - } - }, - "security": { - "title": "Sicurezza dell'account", - "description": "Gestisci la tua password, l'autenticazione a due fattori e le impostazioni di sicurezza", - "detecting": "Rilevamento delle funzionalità del server...", - "not_available": "La gestione della sicurezza dell'account non è disponibile per questo server di posta. I permessi richiesti potrebbero essere disabilitati. Consultare la documentazione per i dettagli.", - "password": { - "title": "Cambia password", - "current": "Password attuale", - "new": "Nuova password", - "confirm": "Conferma nuova password", - "submit": "Cambia password", - "success": "Password cambiata con successo", - "error_title": "Cambio password fallito", - "error_mismatch": "Le nuove password non corrispondono", - "error_min_length": "La password deve contenere almeno 8 caratteri", - "error_generic": "Impossibile cambiare la password" - }, - "display_name": { - "label": "Nome visualizzato", - "description": "Il tuo nome come appare sul server", - "placeholder": "Inserisci il tuo nome visualizzato", - "save": "Salva", - "success": "Nome visualizzato aggiornato", - "error": "Impossibile aggiornare il nome visualizzato" - }, - "totp": { - "section_title": "Autenticazione a due fattori", - "label": "Autenticazione TOTP", - "description": "Aggiungi un ulteriore livello di sicurezza con una password monouso basata sul tempo", - "active": "Abilitato", - "inactive": "Disabilitato", - "enabled": "Autenticazione a due fattori abilitata", - "disabled": "Autenticazione a due fattori disabilitata", - "enable_error": "Impossibile abilitare la 2FA", - "disable_error": "Impossibile disabilitare la 2FA", - "setup_instructions": "Copia questo URL nella tua app di autenticazione (Google Authenticator, Authy, ecc.):", - "verification_code": "Codice di verifica", - "confirm": "Conferma", - "disable": "Disattiva", - "disable_confirm_prompt": "Inserisci la tua password per disattivare l'autenticazione a due fattori.", - "password_required": "La password è obbligatoria", - "code_required": "Il codice di verifica è obbligatorio", - "code_invalid": "Codice di verifica non valido. Controlla la tua app di autenticazione e riprova." - }, - "app_passwords": { - "title": "Password per le app", - "description": "Crea password per le app che non supportano l'autenticazione a due fattori", - "add": "Aggiungi", - "create": "Crea", - "cancel": "Annulla", - "generate": "Genera", - "name_label": "Nome dell'app", - "name_placeholder": "es. Thunderbird, iPhone Mail", - "password_label": "Password (lascia vuoto per auto-generare)", - "password_placeholder": "Auto-generata se vuoto", - "added": "Password per l'app creata", - "removed": "Password per l'app rimossa", - "add_error": "Impossibile creare la password per l'app", - "remove_error": "Impossibile rimuovere la password per l'app", - "none": "Nessuna password per le app configurata", - "done": "Fatto", - "expires_label": "Scadenza (facoltativa)", - "copy_now_warning": "Copia questa password ora - non verrà più mostrata.", - "allowed_ips_label": "IP consentiti (opzionale)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Separati da virgola o spazio. Lasciare vuoto per consentire qualsiasi IP." - }, - "api_keys": { - "title": "Chiavi API", - "description": "Crea chiavi API per script e integrazioni che comunicano direttamente con il server", - "name_label": "Nome chiave", - "name_placeholder": "es. Script di backup, CI runner", - "copy_now_warning": "Copia subito questa chiave API - non verrà mostrata di nuovo.", - "added": "Chiave API creata", - "removed": "Chiave API rimossa", - "add_error": "Impossibile creare la chiave API", - "remove_error": "Impossibile rimuovere la chiave API", - "none": "Nessuna chiave API configurata" - }, - "encryption": { - "section_title": "Crittografia a riposo", - "label": "Crittografia email", - "description": "Crittografa le email archiviate sul server per una maggiore privacy", - "active": "Crittografia {type} abilitata", - "inactive": "Disabilitato", - "enabled": "Crittografia a riposo abilitata", - "disabled_success": "Crittografia a riposo disabilitata", - "error": "Impossibile aggiornare le impostazioni di crittografia", - "algorithm_label": "Algoritmo di crittografia", - "encrypt_on_append": "Cripta le nuove e-mail al caricamento", - "allow_spam_training": "Consenti l'addestramento antispam prima di crittografare le e-mail", - "enable_encryption": "Abilita la crittografia", - "enable_tooltip": "Abilita la crittografia con questa chiave", - "configure_title": "Abilita la crittografia con questa chiave", - "enable_button": "Abilita", - "disable_tooltip": "Disabilita la crittografia", - "enabled_success": "Crittografia a riposo abilitata" - }, - "email_client": { - "title": "Configurazione client e-mail", - "description": "Usa queste credenziali per configurare il tuo client e-mail desktop o mobile (Thunderbird, Apple Mail, Outlook, ecc.)", - "jmap_username_label": "Nome utente JMAP", - "copy": "Copia", - "copied": "Copiato", - "password_instructions": "Usa il tuo nome utente JMAP sopra indicato insieme a una password dell'app per accedere al tuo client e-mail. Crea una password dell'app nella sezione sopra se non l'hai ancora fatto." - }, - "link_device": { - "title": "Collega l'app mobile", - "description": "Accedi all'app mobile Bulwark Mail senza digitare nulla. Genera qui un codice QR e scansionalo dalla schermata di accesso dell'app.", - "generate": "Mostra codice QR", - "regenerate": "Mostra un nuovo codice", - "instructions": "Apri l'app Bulwark Mail, tocca \"Scansiona codice QR\" nella schermata di accesso e inquadra qui con la fotocamera.", - "expires_in": "Questo codice scade tra {seconds} secondi. Può essere usato una sola volta.", - "expired": "Questo codice è scaduto.", - "generating": "Generazione…", - "error": "Impossibile creare un codice di associazione. Riprova." - }, - "public_keys": { - "name_label": "Nome della chiave pubblica", - "name_placeholder": "es. La mia chiave", - "key_label": "Chiave pubblica (S/MIME o PGP in formato ASCII)", - "key_placeholder": "Incolla qui la tua chiave pubblica", - "none": "Nessuna chiave pubblica configurata" - } - }, - "identities": { - "title": "Identità di invio", - "description": "Gestisci gli indirizzi email da cui puoi inviare", - "identities_count": { - "label": "Le tue identità", - "description": "Indirizzi email configurati per l'invio", - "count_zero": "Nessuna identità", - "count_one": "1 identità", - "count_other": "{count} identità" - }, - "manage": "Gestisci identità", - "sub_addressing": { - "label": "Sotto-indirizzamento", - "description": "Usa tag come utente+tag@dominio.com per organizzare la posta in arrivo", - "learn_more": "Scopri di più" - } - }, - "vacation": { - "title": "Risponditore automatico", - "description": "Rispondi automaticamente alle email in arrivo durante la tua assenza", - "loading": "Caricamento impostazioni di risposta automatica...", - "not_supported": "Il server di posta non supporta le risposte automatiche di assenza.", - "fetch_error": "Impossibile caricare le impostazioni di risposta automatica. Riprova.", - "status": { - "label": "Risponditore automatico", - "description": "Invia una risposta automatica a chi ti scrive", - "active": "Attivo", - "inactive": "Inattivo" - }, - "date_range": { - "title": "Periodo", - "description": "Limita opzionalmente la risposta automatica a un periodo specifico", - "start": "Data di inizio", - "start_description": "Lascia vuoto per nessun limite di inizio", - "end": "Data di fine", - "end_description": "Lascia vuoto per nessun limite di fine" - }, - "message": { - "title": "Messaggio di risposta automatica", - "description": "Il messaggio che verrà inviato come risposta", - "subject_label": "Oggetto", - "subject_description": "Oggetto della risposta automatica", - "subject_placeholder": "Fuori ufficio", - "body_label": "Corpo del messaggio", - "body_description": "Contenuto del messaggio in testo semplice", - "body_placeholder": "Grazie per la tua email. Sono attualmente fuori ufficio e risponderò al mio ritorno.", - "html_label": "Messaggio formattato (HTML)", - "html_description": "Aggiungi una versione formattata con link e stile. I destinatari il cui client di posta non può visualizzarla vedranno il testo semplice qui sopra.", - "html_placeholder": "Scrivi una risposta di assenza formattata…" - }, - "preview": { - "title": "Anteprima", - "show": "Mostra anteprima", - "hide": "Nascondi anteprima" - }, - "save": "Salva modifiche", - "saving": "Salvataggio...", - "warnings": { - "end_before_start": "La data di fine deve essere successiva alla data di inizio", - "start_in_past": "La data di inizio è nel passato", - "empty_body": "Il corpo del messaggio è vuoto - i destinatari riceveranno una risposta vuota", - "subject_too_long": "L'oggetto è troppo lungo per il server (al massimo {max} byte).", - "body_too_long": "Il messaggio è troppo lungo per il server (al massimo {max} byte, formattazione inclusa)." - } - }, - "folders": { - "role_memos": "Note", - "title": "Cartelle", - "description": "Gestisci le cartelle e-mail e assegna ruoli standard", - "folder_list": "Le tue cartelle", - "folder_list_description": "Clicca sull'icona di una cartella per personalizzarla", - "standard_roles": "Ruoli standard delle cartelle", - "standard_roles_description": "Assegna quali cartelle sono utilizzate per i ruoli standard come Posta in arrivo, Inviati, Cestino, ecc.", - "role_inbox": "Posta in arrivo", - "role_drafts": "Bozze", - "role_sent": "Inviati", - "role_trash": "Cestino", - "role_junk": "Spam / Indesiderata", - "role_archive": "Archivio", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Nessuno", - "create_folder": "Crea cartella", - "create_subfolder": "Crea sottocartella", - "reorder": "Trascina per riordinare", - "reorder_error": "Impossibile riordinare le cartelle", - "move_error": "Impossibile spostare la cartella", - "subfolder_of": "Dentro {name}", - "subfolder_name": "Nome della sottocartella", - "new_folder_name": "Nome della cartella", - "rename": "Rinomina", - "change_icon": "Cambia icona", - "delete": "Elimina", - "confirm_delete": "Sei sicuro di voler eliminare \"{name}\"? Le e-mail in questa cartella verranno spostate nel Cestino.", - "create": "Crea", - "cancel": "Annulla", - "no_folders": "Nessuna cartella personalizzata", - "cannot_delete_role": "Impossibile eliminare una cartella con un ruolo standard. Rimuovi prima il ruolo.", - "folder_created": "Cartella creata", - "folder_renamed": "Cartella rinominata", - "folder_deleted": "Cartella eliminata", - "role_updated": "Ruolo della cartella aggiornato", - "error_create": "Impossibile creare la cartella", - "error_rename": "Impossibile rinominare la cartella", - "error_delete": "Impossibile eliminare la cartella", - "error_delete_has_children": "Impossibile eliminare la cartella: contiene ancora sottocartelle. Eliminale o spostale prima.", - "error_delete_has_email": "Impossibile eliminare la cartella: contiene ancora email. Spostale o eliminale prima.", - "error_role": "Impossibile aggiornare il ruolo della cartella" - }, - "advanced": { - "title": "Avanzate", - "description": "Opzioni avanzate e impostazioni per sviluppatori", - "debug_mode": { - "label": "Modalità debug", - "description": "Abilita registrazione dettagliata per la risoluzione dei problemi" - }, - "debug_categories": { - "description": "Seleziona quali categorie registrare. Disattiva quelle che non ti servono per ridurre il rumore nella console.", - "jmap": "Client JMAP", - "jmap_description": "Operazioni sulle caselle di posta, recupero email e richieste del protocollo JMAP", - "calendar": "Calendario", - "calendar_description": "Eventi del calendario, importazioni e messaggi di pianificazione", - "tasks": "Attività", - "tasks_description": "Creazione, recupero e aggiornamento delle attività del calendario", - "auth": "Autenticazione", - "auth_description": "Accesso, TOTP, scambio di token e gestione della sessione", - "filters": "Filtri", - "filters_description": "Regole di filtro Sieve e script di risposta automatica", - "email": "Visualizzazione email", - "email_description": "Rendering delle email, elaborazione TNEF e segna come letto", - "push": "Notifiche push", - "push_description": "Configurazione e recapito delle notifiche push", - "contacts": "Contatti e rubriche", - "contacts_description": "Sincronizzazione contatti, operazioni rubrica e mittenti attendibili" - }, - "settings_sync": { - "label": "Sincronizzazione impostazioni", - "description": "Sincronizza le impostazioni tra browser e dispositivi" - }, - "sender_favicons": { - "label": "Favicon dei mittenti", - "description": "Mostra le icone dei siti web come immagini profilo per i mittenti aziendali" - }, - "show_avatars_in_junk": { - "label": "Mostra avatar nella cartella spam", - "description": "Mostra immagini profilo e favicon dei mittenti nella cartella spam. Disattivato per impostazione predefinita per non dare apparenza legittima ai tentativi di phishing." - }, - "keyboard_shortcuts": { - "label": "Scorciatoie da tastiera", - "description": "Visualizza le scorciatoie da tastiera disponibili", - "button": "Visualizza scorciatoie" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Ripristina impostazioni", - "description": "Ripristina tutte le impostazioni ai valori predefiniti", - "button": "Ripristina ai valori predefiniti" - }, - "export_settings": { - "label": "Esporta impostazioni", - "description": "Scarica le tue impostazioni come JSON", - "button": "Esporta" - }, - "import_settings": { - "label": "Importa impostazioni", - "description": "Carica impostazioni da file JSON", - "button": "Importa" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "App nella barra laterale", - "description": "Gestisci app e link personalizzati nella barra laterale", - "keep_loaded": "Mantieni app caricate", - "keep_loaded_description": "Mantieni le app inline in esecuzione in background durante il passaggio per evitare il ricaricamento", - "managed_title": "Fornite dall'amministratore", - "managed_description": "Queste app sono configurate per tutti e non possono essere modificate.", - "manage_title": "App personalizzate", - "manage_description": "Aggiungi, modifica o rimuovi app personalizzate dalla barra laterale" - }, - "contacts": { - "title": "Contatti", - "description": "Importa ed esporta i tuoi contatti", - "import_label": "Importa contatti", - "import_description": "Importa contatti da un file vCard (.vcf)", - "export_label": "Esporta contatti", - "export_description": "Esporta tutti i contatti come file vCard (.vcf)", - "manage_title": "Rubriche", - "manage_description": "Rinomina le tue rubriche", - "no_address_books": "Nessuna rubrica trovata", - "categories_title": "Categorie", - "categories_description": "Rinomina le categorie dei contatti", - "no_categories": "Nessuna categoria trovata", - "group_by_letter_description": "Mostra intestazioni alfabetiche nell'elenco dei contatti", - "group_by_letter_label": "Raggruppa per prima lettera", - "sort_by_last_name_label": "Ordina per cognome", - "sort_by_last_name_description": "Ordina l'elenco dei contatti per cognome così i familiari compaiono insieme" - }, - "downloads": { - "title": "Download", - "description": "Personalizza come vengono nominati gli email e gli allegati scaricati.", - "reset": "Ripristina predefinito", - "preview": "Anteprima:", - "email_template": { - "label": "Nome file email (.eml)", - "description": "Modello usato quando esporti un'email o la trascini sul file system. L'estensione .eml viene aggiunta automaticamente." - }, - "attachment_template": { - "label": "Nome file dell’allegato", - "description": "Modello usato quando scarichi o trascini un allegato. Se ometti '{filename}' e '{ext}', l'estensione originale viene mantenuta." - }, - "bundle_template": { - "label": "Nome del file .zip multi-email", - "description": "Modello usato quando trascini o scarichi più email selezionate come un singolo archivio .zip. L'estensione .zip viene aggiunta automaticamente." - }, - "spaces": { - "label": "Spazi", - "description": "Sostituisci gli spazi nel nome file risultante con un altro carattere.", - "keep": "Mantieni gli spazi", - "underscore": "Sostituisci con _", - "dash": "Sostituisci con -" - }, - "lowercase": { - "label": "Minuscolo", - "description": "Forza l'intero nome file in minuscolo." - }, - "strip_diacritics": { - "label": "Rimuovi i segni diacritici", - "description": "Converti le lettere accentate nei loro equivalenti ASCII (ä → a, é → e). Utile per strumenti che gestiscono male i nomi Unicode." - }, - "collapse_separators": { - "label": "Riduci i separatori ripetuti", - "description": "Riduci sequenze di spazi, underscore o trattini a un solo carattere." - }, - "after_export": { - "label": "Dopo l’esportazione", - "description": "Sposta facoltativamente l’email dopo averla esportata come .eml.", - "keep": "Mantieni nella casella", - "archive": "Sposta nell’archivio", - "trash": "Sposta nel cestino" - } - }, - "filters": { - "title": "Filtri email", - "description": "Crea regole per ordinare, etichettare e gestire automaticamente le email in arrivo", - "add_rule": "Aggiungi regola", - "no_rules": "Nessuna regola di filtro", - "vacation_active": "Il risponditore automatico è attivo", - "vacation_active_description": "La risposta automatica è abilitata per i messaggi in arrivo", - "vacation_configure": "Configura", - "no_rules_description": "Crea regole per organizzare automaticamente le tue email in arrivo", - "edit_rule": "Modifica regola", - "new_rule": "Nuova regola", - "delete_rule": "Elimina regola", - "delete_confirm": "Sei sicuro di voler eliminare questa regola?", - "enable": "Attiva", - "disable": "Disattiva", - "raw_editor": "Editor Sieve grezzo", - "raw_editor_warning": "La modifica dello script Sieve grezzo potrebbe compromettere l'editor visuale delle regole. Le modifiche effettuate qui sovrascrivono il costruttore visuale.", - "validate": "Convalida", - "validation_success": "Lo script è valido", - "validation_error": "Lo script contiene errori", - "save": "Salva regole", - "saving": "Salvataggio...", - "saved": "Filtri salvati con successo", - "save_failed": "Impossibile salvare i filtri", - "loading": "Caricamento filtri...", - "not_supported": "Il tuo server di posta non supporta i filtri email.", - "rule_name": "Nome della regola", - "rule_name_placeholder": "es. Ordina newsletter", - "match_all": "Corrispondere a TUTTE le condizioni", - "match_any": "Corrispondere a QUALSIASI condizione", - "conditions": "Condizioni", - "add_condition": "Aggiungi condizione", - "actions": "Azioni", - "add_action": "Aggiungi azione", - "stop_processing": "Interrompere l'elaborazione delle regole successive", - "include_spam": "Sposta anche i messaggi contrassegnati come spam", - "attachment_type_placeholder": "es. pdf, doc, jpg", - "value_placeholder_multi": "Valore (più valori separati da virgole)", - "condition_fields": { - "attachment": "Allegato", - "from": "Da", - "to": "A", - "cc": "Cc", - "subject": "Oggetto", - "header": "Intestazione personalizzata", - "size": "Dimensione", - "body": "Corpo" - }, - "comparators": { - "has_any": "è presente", - "has_type": "di tipo", - "contains": "contiene", - "not_contains": "non contiene", - "is": "è esattamente", - "not_is": "non è", - "starts_with": "inizia con", - "ends_with": "termina con", - "matches": "corrisponde al modello", - "greater_than": "è maggiore di", - "less_than": "è minore di" - }, - "action_types": { - "move": "Sposta nella cartella", - "copy": "Copia nella cartella", - "forward": "Inoltra a", - "mark_read": "Segna come letto", - "star": "Aggiungi stella", - "add_label": "Aggiungi etichetta", - "discard": "Elimina silenziosamente", - "reject": "Rifiuta con messaggio", - "keep": "Mantieni nella posta in arrivo", - "stop": "Interrompi elaborazione" - }, - "move_to_folder": "Seleziona cartella", - "copy_to_folder": "Seleziona cartella", - "forward_to": "Inoltra a indirizzo email", - "forward_placeholder": "email@esempio.com", - "forward_keep_copy": "Conserva una copia", - "forward_limit": "Limite di inoltri per messaggio su questo server: {count}. Gli inoltri in eccesso vengono ignorati.", - "reject_message": "Messaggio di rifiuto", - "reject_placeholder": "La tua email è stata rifiutata", - "label_name": "Nome dell'etichetta", - "label_placeholder": "Seleziona etichetta", - "header_name": "Nome dell'intestazione", - "header_placeholder": "es. X-Mailing-List", - "size_bytes": "Dimensione in byte", - "size_placeholder": "es. 1000000", - "system_managed": "Regola gestita dal sistema", - "opaque_warning": "Questo script è stato modificato al di fuori del costruttore visuale. È disponibile solo la modifica Sieve grezza.", - "open_sieve_editor": "Apri editor Sieve", - "fetch_error": "Impossibile caricare i filtri", - "expanded_view": "Vista espansa", - "expanded_view_description": "Mostra le regole dei filtri con blocchi dettagliati di condizioni e azioni", - "if": "Se", - "then": "Allora", - "match_all_conditions": "tutte corrispondono", - "match_any_condition": "una corrisponde", - "and": "e", - "or": "o", - "cancel": "Annulla", - "confirm_delete": "Elimina", - "rule_list": "Regole di filtraggio", - "drag_to_reorder": "Trascina per riordinare", - "match_type": "Tipo di corrispondenza", - "reset_to_visual": "Ripristina il costruttore visuale", - "reset_warning": "Questo eliminerà lo script attuale e ricomincerà da zero.", - "confirm_reset": "Ripristina", - "validation_empty_name": "Il nome della regola è obbligatorio", - "validation_empty_conditions": "È necessaria almeno una condizione con un valore", - "validation_empty_actions": "È necessaria almeno un'azione", - "templates_section": "Parti da un modello", - "template_newsletters": "Sposta newsletter in cartella", - "template_receipts": "Archivia ricevute automaticamente", - "template_important": "Contrassegna email importanti", - "template_notifications": "Filtra notifiche", - "sieve_editor": { - "title": "Editor script Sieve", - "warning": "La modifica dello script Sieve può interrompere la modifica visuale delle regole. Le modifiche effettuate qui sovrascrivono il costruttore visuale.", - "script_content": "Script Sieve", - "valid": "Lo script è valido", - "invalid": "Lo script contiene errori", - "save_warning": "Il salvataggio sovrascriverà tutte le regole visuali. Questa azione non può essere annullata. Fai clic su Salva di nuovo per confermare.", - "validating": "Validazione...", - "validate": "Valida", - "cancel": "Annulla", - "save": "Salva", - "confirm_save": "Conferma salvataggio", - "validation_failed": "Richiesta di validazione non riuscita" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# condizione} other {# condizioni}}", - "actions_count": "{count, plural, one {# azione} other {# azioni}}" - }, - "origin_external": "Esterno", - "managed_by_tooltip": "Gestito da {source}. Modificalo in quell'app o usa l'editor Sieve grezzo." - }, - "templates": { - "title": "Modelli email", - "description": "Crea modelli email riutilizzabili con variabili segnaposto", - "add": "Nuovo modello", - "edit": "Modifica modello", - "name": "Nome del modello", - "name_placeholder": "es.: Email di follow-up", - "category": "Categoria", - "category_placeholder": "es.: Lavoro, Personale", - "subject": "Oggetto", - "subject_placeholder": "Oggetto dell'email", - "body": "Corpo", - "body_placeholder": "Contenuto dell'email...", - "recipients_placeholder": "email@example.com", - "identity": "Invia come", - "default_identity": "Identità predefinita", - "favorite": "Preferito", - "cancel": "Annulla", - "create": "Crea", - "update": "Aggiorna", - "confirm_delete": "Elimina", - "no_templates": "Nessun modello", - "manage": "Gestisci modelli", - "count": "{count, plural, one {# modello} other {# modelli}}", - "export_import": "Esporta e importa", - "export_import_description": "Esegui il backup dei modelli o trasferiscili su un altro dispositivo", - "export": "Esporta", - "import": "Importa", - "validation": { - "empty": "Il nome del modello è obbligatorio", - "too_long": "Il nome del modello non deve superare i 200 caratteri" - } - }, - "files": { - "display": { - "title": "Visualizzazione", - "description": "Configura come vengono visualizzati file e cartelle" - }, - "folder_layout": { - "label": "Navigazione cartelle", - "description": "Scegli come visualizzare le cartelle: in linea con i file o in un albero nella barra laterale", - "inline": "In linea", - "sidebar": "Barra laterale" - }, - "default_view": { - "label": "Vista predefinita", - "description": "Scegli tra layout a griglia e a lista", - "list": "Lista", - "grid": "Griglia" - }, - "default_sort": { - "label": "Ordinamento predefinito", - "description": "Scegli l'ordinamento predefinito per i file", - "name": "Nome", - "size": "Dimensione", - "modified": "Modificato" - }, - "sort_direction": { - "label": "Direzione ordinamento", - "description": "Scegli ordine crescente o decrescente", - "ascending": "Crescente", - "descending": "Decrescente" - }, - "icons": { - "title": "Icone", - "description": "Configura l'aspetto delle icone dei file" - }, - "show_icons": { - "label": "Mostra icone file", - "description": "Visualizza le icone accanto a file e cartelle" - }, - "colored_icons": { - "label": "Icone colorate", - "description": "Usa icone colorate invece che monocromatiche" - }, - "show_thumbnails": { - "label": "Mostra miniature", - "description": "Mostra anteprime delle immagini al posto delle icone" - }, - "behavior": { - "title": "Comportamento", - "description": "Configura il comportamento del gestore file" - }, - "show_hidden": { - "label": "Mostra file nascosti", - "description": "Visualizza file e cartelle che iniziano con un punto" - }, - "preview": { - "label": "Anteprima" - } - }, - "search_placeholder": "Cerca impostazioni", - "search_clear": "Cancella ricerca", - "search_no_results": "Nessuna impostazione corrispondente", - "scoped": { - "back": "Torna al mio account", - "managing": "Gestione: {name}" - } - }, - "errors": { - "page_error_title": "Qualcosa è andato storto", - "page_error_description": "Si è verificato un errore imprevisto. Riprova o torna alla pagina principale.", - "sidebar_error": "Impossibile caricare le caselle di posta", - "email_list_error": "Impossibile caricare i messaggi", - "viewer_error_title": "Impossibile visualizzare il messaggio", - "viewer_error_description": "Si è verificato un problema nella visualizzazione di questo messaggio. Potrebbe contenere contenuti non supportati.", - "composer_error": "Impossibile caricare l'editor", - "settings_error_title": "Impostazioni non disponibili", - "settings_error_description": "Impossibile caricare le impostazioni. Le tue preferenze potrebbero non essere salvate.", - "try_again": "Riprova", - "reload": "Ricarica", - "reload_emails": "Ricarica messaggi", - "reload_settings": "Ricarica impostazioni", - "retry": "Riprova", - "go_home": "Vai alla posta in arrivo" - }, - "context_menu": { - "reply": "Rispondi", - "reply_all": "Rispondi a tutti", - "forward": "Inoltra", - "mark_read": "Segna come letto", - "mark_unread": "Segna come non letto", - "star": "Aggiungi stella", - "unstar": "Rimuovi stella", - "pin": "Fissa", - "unpin": "Non fissare più", - "move_to": "Sposta in...", - "archive": "Archivia", - "delete": "Elimina", - "mark_as_spam": "Segnala come spam", - "not_spam": "Non spam", - "tag": "Etichetta", - "items_selected": "{count} messaggi selezionati", - "edit_draft": "Modifica bozza", - "cancel_scheduled_send": "Annulla invio", - "reschedule_send": "Riprogramma", - "cancel_and_edit": "Annulla e modifica", - "cancel_and_compose_again": "Annulla e componi di nuovo" - }, - "mailbox_context_menu": { - "mark_folder_read": "Segna cartella come letta", - "mark_folder_tree_read": "Segna cartella e sottocartelle come lette", - "mark_all_folders_read": "Segna tutte le cartelle come lette", - "new_subfolder": "Nuova sottocartella...", - "new_folder": "Nuova cartella...", - "rename": "Rinomina...", - "share": "Condividi...", - "import_email": "Importa .eml o .zip...", - "empty_folder": "Svuota cartella", - "empty_folder_generic": "Svuota cartella", - "delete_folder": "Elimina cartella", - "refresh": "Aggiorna", - "mark_all_confirm_title": "Segna tutte le cartelle come lette", - "mark_all_confirm_message": "Segnare tutti i messaggi non letti del tuo account personale come letti?", - "delete_confirm_title": "Elimina cartella", - "delete_confirm_message": "Eliminare definitivamente la cartella \"{name}\"? Questa azione non può essere annullata.", - "prompt_new_subfolder": "Inserisci un nome per la nuova sottocartella.", - "prompt_new_folder": "Inserisci un nome per la nuova cartella.", - "prompt_rename": "Inserisci un nuovo nome per questa cartella.", - "toast_marked_read": "Cartella segnata come letta", - "toast_marked_read_count": "Segnati {count, plural, one {1 messaggio} other {# messaggi}} come letti", - "toast_already_read": "Nessun messaggio non letto", - "toast_marked_all_read": "Tutte le cartelle segnate come lette", - "toast_emptied": "Cartella svuotata", - "toast_folder_created": "Cartella creata", - "toast_folder_renamed": "Cartella rinominata", - "toast_folder_deleted": "Cartella eliminata", - "toast_error_mark_read": "Impossibile segnare come letto", - "toast_error_empty": "Impossibile svuotare la cartella", - "toast_error_create": "Impossibile creare la cartella", - "toast_error_rename": "Impossibile rinominare la cartella", - "toast_error_delete": "Impossibile eliminare la cartella", - "toast_error_delete_has_children": "La cartella contiene sottocartelle. Rimuoverle prima.", - "toast_error_delete_has_email": "La cartella non è vuota. Svuotarla prima.", - "delete_with_contents_title": "La cartella non è vuota", - "delete_with_contents_message": "La cartella \"{name}\" contiene ancora {count, plural, one {1 messaggio} other {# messaggi}}. Eliminare la cartella e tutti i suoi messaggi? L'operazione non può essere annullata.", - "delete_with_contents_confirm": "Elimina cartella e messaggi", - "placeholder_folder_name": "Nome cartella", - "create": "Crea", - "rename_confirm": "Rinomina" - }, - "shortcuts": { - "title": "Scorciatoie da tastiera", - "tip": "Premi ? in qualsiasi momento per mostrare questo aiuto", - "sections": { - "navigation": "Navigazione", - "actions": "Azioni sui messaggi", - "global": "Globali", - "threads": "Conversazioni", - "composer": "Composizione" - }, - "navigation": { - "next_email": "Messaggio successivo", - "previous_email": "Messaggio precedente", - "open_email": "Apri messaggio", - "close_email": "Chiudi / Deseleziona" - }, - "actions": { - "reply": "Rispondi", - "reply_all": "Rispondi a tutti", - "forward": "Inoltra", - "star": "Attiva/disattiva stella", - "archive": "Archivia", - "delete": "Elimina", - "mark_unread": "Segna come non letto", - "mark_read": "Segna come letto", - "toggle_spam": "Segnala spam / Non spam" - }, - "global": { - "compose": "Scrivi nuovo messaggio", - "search": "Cerca", - "help": "Mostra scorciatoie", - "refresh": "Aggiorna messaggi", - "select_all": "Seleziona tutto", - "global_search": "Cerca ovunque" - }, - "threads": { - "expand_collapse": "Espandi/comprimi conversazione" - }, - "composer": { - "send": "Invia email", - "schedule_send": "Programma invio", - "template_picker": "Apri selettore modelli" - } - }, - "threads": { - "messages_one": "{count} messaggio", - "messages_other": "{count} messaggi", - "messages_tooltip": "{count, plural, one {# messaggio in questa conversazione} other {# messaggi in questa conversazione}}", - "expand": "Espandi conversazione", - "collapse": "Comprimi conversazione", - "loading": "Caricamento conversazione...", - "mark_read": "Segna conversazione come letta", - "mark_unread": "Segna conversazione come non letta", - "archive": "Archivia conversazione", - "delete": "Elimina conversazione", - "star": "Aggiungi stella alla conversazione", - "unstar": "Rimuovi stella dalla conversazione", - "toggle_thread": "Espandi/comprimi discussione" - }, - "identities": { - "modal_title": "Gestisci identità di invio", - "create_new": "Crea nuova identità", - "edit_identity": "Modifica identità", - "delete_confirm": "Eliminare questa identità? Questa azione non può essere annullata.", - "cannot_delete": "Questa identità non può essere eliminata", - "primary_identity": "Principale", - "set_as_primary": "Imposta come principale", - "no_identities": "Nessuna identità trovata", - "display": { - "reply_to": "Rispondi a:", - "bcc": "CCN:", - "signature": "Firma:", - "preview": "Anteprima:" - }, - "validation_errors": { - "invalid_emails": "Email non valide: {emails}", - "unknown_error": "Errore sconosciuto" - }, - "form": { - "name_label": "Nome visualizzato", - "name_placeholder": "es. Email lavoro, Personale", - "name_required": "Il nome è obbligatorio", - "email_label": "Indirizzo email", - "email_placeholder": "tua.email@esempio.com", - "email_required": "L'email è obbligatoria", - "email_invalid": "Inserisci un indirizzo email valido", - "email_immutable": "L'indirizzo email non può essere modificato dopo la creazione", - "reply_to_label": "Rispondi a (facoltativo)", - "reply_to_placeholder": "diversa@example.com", - "bcc_label": "CCN automatica (facoltativo)", - "bcc_placeholder": "archivio@example.com", - "text_signature_label": "Firma di testo", - "html_signature_label": "Firma HTML", - "signature_byte_counter": "{bytes} / {max} byte", - "signature_byte_limit_reached": "Limite del server raggiunto", - "save": "Salva identità", - "cancel": "Annulla", - "creating": "Creazione...", - "updating": "Aggiornamento..." - }, - "sub_address": { - "button_tooltip": "Usa sotto-indirizzo", - "popover_title": "Aggiungi tag sotto-indirizzo", - "tag_input_placeholder": "Inserisci tag (es. shopping)", - "preview_label": "Anteprima:", - "recent_tags": "Tag recenti", - "suggested_tags": "Suggeriti", - "use_address": "Usa questo indirizzo", - "invalid_tag": "Il tag deve contenere solo caratteri alfanumerici e trattini", - "tag_too_long": "Il tag deve essere di massimo 30 caratteri", - "help_text": "I messaggi inviati a utente{delimiter}tag@dominio.com arriveranno nella tua casella di posta", - "validation": { - "empty": "Il tag non può essere vuoto", - "too_long": "Il tag deve essere di massimo {max} caratteri", - "invalid_chars": "Il tag deve contenere solo lettere, numeri e trattini" - } - }, - "badge": { - "sent_via": "tramite", - "sub_address_tag": "Inviato usando sotto-indirizzo: {tag}", - "identity_name": "Inviato usando identità: {name}", - "identity_short": "tramite {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Elimina", - "delete_confirm_title": "Elimina identità" - }, - "templates": { - "picker_title": "Scegli un modello", - "search_placeholder": "Cerca modelli...", - "section_favorites": "Preferiti", - "section_recent": "Recenti", - "section_uncategorized": "Altri", - "no_templates": "Nessun modello", - "no_results": "Nessun modello trovato", - "fill_placeholders": "Compila i valori segnaposto", - "enter_value": "Inserisci un valore...", - "preview": "Anteprima", - "insert_with_values": "Inserisci con valori", - "insert_raw": "Inserisci grezzo", - "copy_suffix": "(copia)", - "placeholder": "Variabile", - "placeholders": { - "recipient_name": "Nome del destinatario", - "company": "Nome dell'azienda", - "date": "Data odierna", - "day_of_week": "Giorno della settimana", - "sender_name": "Il tuo nome" - } - }, - "contacts": { - "title": "Contatti", - "search_placeholder": "Cerca contatti...", - "create_new": "Nuovo contatto", - "no_category": "Senza categoria", - "rename_category": "Rinomina categoria", - "category_name_label": "Nome della categoria", - "category_renamed": "Categoria rinominata", - "category_rename_failed": "Impossibile rinominare la categoria", - "category_added": "Contatto aggiunto a {name}", - "category_added_plural": "{count} contatti aggiunti a {name}", - "empty_state": "Nessun contatto", - "empty_state_title": "Nessun contatto", - "empty_state_subtitle": "Crea il tuo primo contatto o importa da un file vCard", - "empty_search": "Nessun contatto corrisponde alla ricerca", - "empty_search_hint": "Prova con un altro termine di ricerca", - "empty_filtered": "Nessun contatto corrisponde ai filtri", - "empty_filtered_hint": "Modifica o cancella i filtri", - "clear_search": "Cancella ricerca", - "import_vcard": "Importa vCard", - "delete_confirm_title": "Elimina contatto", - "delete_confirm": "Sei sicuro di voler eliminare questo contatto?", - "local_mode": "I contatti sono salvati localmente (il server non supporta JMAP Contacts)", - "back_to_contacts": "Torna ai contatti", - "back_to_email": "Torna all'e-mail", - "tabs": { - "all": "Tutti", - "groups": "Gruppi" - }, - "shared": { - "title": "Condivisi" - }, - "address_books": { - "create": "Nuova rubrica", - "created": "Rubrica creata", - "create_failed": "Impossibile creare la rubrica", - "title": "Le mie Rubriche", - "shared_prefix": "Condiviso: {name}", - "moved": "Contatto spostato in {name}", - "moved_plural": "{count} contatti spostati in {name}", - "move_failed": "Impossibile spostare il contatto", - "address_book": "Rubrica", - "rename": "Rinomina rubrica", - "name_label": "Nome della rubrica", - "renamed": "Rubrica rinominata", - "rename_failed": "Impossibile rinominare la rubrica", - "default": "Predefinita", - "set_default": "Imposta come predefinita", - "default_updated": "Rubrica predefinita aggiornata", - "set_default_failed": "Impossibile impostare la rubrica predefinita", - "manage": "Gestisci rubriche", - "share": "Condividi rubrica", - "new_contact_in_book": "Nuovo contatto in questa rubrica", - "delete": "Elimina rubrica", - "confirm_delete": "Eliminare \"{name}\"? Tutti i contatti in questa rubrica verranno rimossi.", - "deleted": "Rubrica eliminata", - "delete_failed": "Impossibile eliminare la rubrica" - }, - "detail": { - "emails": "Indirizzi email", - "phones": "Numeri di telefono", - "organizations": "Organizzazioni", - "addresses": "Indirizzi", - "notes": "Note", - "titles": "Titoli e ruoli", - "online_services": "Servizi online", - "anniversaries": "Anniversari", - "personal_info": "Informazioni personali", - "languages": "Lingue", - "categories": "Categorie", - "related_contacts": "Contatti correlati", - "crypto_keys": "Chiavi crittografiche", - "no_contact_selected": "Seleziona un contatto per vedere i dettagli", - "compose_email": "Scrivi email", - "copy_email": "Copia email", - "copy_phone": "Copia telefono", - "copy_url": "Copia URL", - "copied": "Copiato negli appunti", - "copy_failed": "Impossibile copiare negli appunti", - "created": "Creato", - "updated": "Ultimo aggiornamento", - "timezone": "Fuso orario", - "anniversary_birth": "Compleanno", - "anniversary_death": "Decesso", - "anniversary_wedding": "Anniversario", - "anniversary_other": "Altro", - "personal_expertise": "Competenza", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Altro", - "gender": "Genere", - "gender_masculine": "Maschile", - "gender_feminine": "Femminile", - "gender_other": "Altro", - "gender_none": "Non applicabile", - "gender_unknown": "Sconosciuto", - "calendar": "Calendario", - "calendar_uri": "URL del calendario", - "scheduling_uri": "URL di pianificazione", - "freebusy_uri": "URL di disponibilità", - "cert_issuer": "Emittente", - "cert_expires": "Scade", - "cert_expired": "Scaduto", - "cert_fingerprint": "Impronta digitale", - "cert_algorithm": "Algoritmo", - "import_to_smime": "Importa in S/MIME", - "cert_already_imported": "Certificato già importato", - "cert_imported": "Certificato importato", - "cert_import_failed": "Importazione del certificato non riuscita", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Altre azioni", - "age_years": "{count, plural, one {1 anno} other {# anni}}", - "years_since": "{count, plural, one {1 anno fa} other {# anni fa}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Nuovo contatto", - "edit_title": "Modifica contatto", - "section_address_book": "Rubrica", - "select_address_book": "Seleziona una rubrica...", - "section_identity": "Nome e identità", - "contact_type": "Tipo di contatto", - "type_person": "Persona", - "type_organization": "Organizzazione", - "section_work": "Lavoro e organizzazione", - "prefix": "Prefisso", - "prefix_placeholder": "Dott., Sig., Sig.ra", - "given_name": "Nome", - "middle_name": "Secondo nome", - "surname": "Cognome", - "suffix": "Suffisso", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Soprannome", - "nickname_placeholder": "Soprannome", - "email": "Email", - "email_placeholder": "email@example.com", - "phone": "Telefono", - "phone_placeholder": "+39 02 1234 5678", - "phone_type": "Tipo", - "phone_voice": "Voce", - "phone_cell": "Cellulare", - "phone_fax": "Fax", - "phone_pager": "Cercapersone", - "phone_video": "Video", - "phone_text": "SMS", - "organization": "Organizzazione", - "organization_placeholder": "Nome azienda", - "department": "Reparto", - "department_placeholder": "Reparto", - "job_title": "Titolo professionale", - "job_title_placeholder": "es., Ingegnere del software", - "role": "Ruolo", - "role_placeholder": "es., Responsabile del team", - "addresses": "Indirizzi", - "add_address": "Aggiungi indirizzo", - "street": "Via", - "city": "Città", - "region": "Stato / Regione", - "postcode": "Codice postale", - "country": "Paese", - "online_services": "Servizi online", - "add_online_service": "Aggiungi servizio online", - "url_placeholder": "https://...", - "service_placeholder": "Servizio", - "anniversaries": "Anniversari", - "add_anniversary": "Aggiungi data", - "anniversary_birth": "Compleanno", - "anniversary_wedding": "Anniversario", - "anniversary_death": "Commemorazione", - "anniversary_other": "Altro", - "personal_info": "Informazioni personali", - "add_personal_info": "Aggiungi voce", - "personal_info_placeholder": "es., Fotografia", - "personal_expertise": "Competenza", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Altro", - "level": "Livello", - "level_high": "Alto", - "level_medium": "Medio", - "level_low": "Basso", - "categories": "Categorie", - "categories_placeholder": "es., Famiglia, Amici, Colleghi", - "categories_hint": "Digita per cercare o aggiungere", - "category_add": "Aggiungi", - "note": "Note", - "note_placeholder": "Aggiungi una nota...", - "gender": "Genere", - "gender_sex": "Sesso", - "gender_male": "Maschile", - "gender_female": "Femminile", - "gender_other": "Altro", - "gender_none": "Non applicabile", - "gender_unknown": "Sconosciuto", - "gender_identity": "Identità di genere", - "gender_identity_placeholder": "Identità di genere...", - "calendar": "Calendario", - "calendar_uri": "URL del calendario", - "scheduling_uri": "URL di pianificazione", - "freebusy_uri": "URL di disponibilità", - "context_work": "Lavoro", - "context_private": "Personale", - "add_email": "Aggiungi email", - "add_phone": "Aggiungi telefono", - "save": "Salva", - "cancel": "Annulla", - "creating": "Creazione...", - "updating": "Aggiornamento...", - "name_required": "Inserisci un nome, un cognome o un'organizzazione", - "email_invalid": "Inserisci un indirizzo email valido", - "email_error_inline": "Formato email non valido", - "save_failed": "Impossibile salvare il contatto", - "delete": "Elimina", - "upload_photo": "Carica foto", - "remove_photo": "Rimuovi foto", - "photo_hint": "JPG o PNG, fino a 10 MB. Verrà ridimensionata.", - "photo_too_large": "L'immagine è troppo grande (max 10 MB)", - "photo_invalid": "File immagine non valido", - "change_photo": "Cambia" - }, - "groups": { - "create": "Nuovo gruppo", - "edit": "Modifica gruppo", - "empty": "Nessun gruppo", - "delete_confirm_title": "Elimina gruppo", - "delete_confirm": "Sei sicuro di voler eliminare questo gruppo?", - "name_label": "Nome del gruppo", - "name_placeholder": "es. Team, Famiglia", - "name_required": "Il nome del gruppo è obbligatorio", - "save_failed": "Impossibile salvare il gruppo", - "members_label": "Membri", - "search_members": "Cerca contatti da aggiungere...", - "no_members": "Nessun membro in questo gruppo", - "member_count": "{count, plural, =0 {Nessun membro} one {1 membro} other {# membri}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Importa contatti", - "drop_hint": "Clicca per selezionare un file vCard", - "file_types": "File .vcf o .vcard", - "no_contacts": "Nessun contatto trovato nel file", - "parse_error": "Impossibile analizzare il file vCard", - "found": "{count, plural, one {1 contatto trovato} other {# contatti trovati}}", - "duplicate": "Duplicato", - "select_all": "Seleziona tutto", - "deselect_all": "Deseleziona tutto", - "selected": "{count, plural, one {1 selezionato} other {# selezionati}}", - "import_button": "Importa", - "importing": "Importazione...", - "success": "{count, plural, one {1 contatto importato} other {# contatti importati}}", - "failed": "Importazione fallita", - "close": "Chiudi", - "file_too_large": "Il file è troppo grande (max 5 MB)" - }, - "export": { - "title": "Esporta contatti", - "success": "{count, plural, one {1 contatto esportato} other {# contatti esportati}}" - }, - "bulk": { - "selected": "{count, plural, one {1 selezionato} other {# selezionati}}", - "select_all": "Seleziona tutto", - "delete": "Elimina", - "delete_confirm_title": "Elimina contatti", - "delete_confirm": "Eliminare {count, plural, one {1 contatto} other {# contatti}}?", - "deleted": "{count, plural, one {1 contatto eliminato} other {# contatti eliminati}}", - "add_to_group": "Aggiungi al gruppo", - "choose_group": "Scegli un gruppo", - "adding_contacts": "Aggiunta di {count, plural, one {1 contatto} other {# contatti}}", - "added_to_group": "Contatti aggiunti al gruppo", - "export": "Esporta", - "clear": "Cancella selezione" - }, - "toast": { - "created": "Contatto creato", - "updated": "Contatto aggiornato", - "deleted": "Contatto eliminato", - "error_create": "Impossibile creare il contatto", - "error_update": "Impossibile aggiornare il contatto", - "error_delete": "Impossibile eliminare il contatto" - }, - "context_menu": { - "open": "Apri", - "edit": "Modifica", - "send_email": "Invia email", - "add_to_group": "Aggiungi al gruppo", - "export_vcard": "Esporta come vCard", - "delete": "Elimina", - "call": "Chiama", - "duplicate": "Duplica", - "print": "Stampa" - }, - "filters": { - "toggle": "Filtri", - "select": "Seleziona", - "clear": "Azzera", - "close": "Chiudi", - "title": "Filtri avanzati", - "organization": "Azienda", - "organization_placeholder": "es. Acme", - "job_title": "Ruolo", - "job_title_placeholder": "es. Designer", - "location": "Luogo", - "location_placeholder": "Città o paese", - "email_domain": "Dominio email", - "email_domain_placeholder": "esempio.com", - "birthday_month": "Compleanno in", - "any_month": "Qualsiasi mese", - "has_email": "Con email", - "has_phone": "Con telefono", - "has_photo": "Con foto", - "sort_by": "Ordina per", - "sort_first_name": "Nome", - "sort_last_name": "Cognome" - }, - "open_categories": "Apri categorie" - }, - "calendar": { - "title": "Calendario", - "back_to_email": "Torna alla posta", - "back_to_month": "Torna al mese", - "my_calendars": "I miei calendari", - "birthday_calendar": "Compleanni", - "mini_calendar_change": "Clicca per cambiare mese", - "views": { - "month": "Mese", - "week": "Settimana", - "day": "Giorno", - "agenda": "Agenda", - "today": "Oggi", - "month_hint": "Mese (m)", - "week_hint": "Settimana (w)", - "day_hint": "Giorno (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Attività", - "tasks_hint": "Attività (k)" - }, - "events": { - "create": "Crea evento", - "edit": "Modifica evento", - "delete": "Elimina evento", - "details": "Dettagli evento", - "no_events": "Nessun evento", - "all_day": "Tutto il giorno", - "more": "+{count} altri", - "no_title": "(Senza titolo)", - "resize": "Ridimensiona evento", - "duplicate": "Duplica", - "today_header": "Oggi", - "tomorrow_header": "Domani", - "agenda_show_earlier": "Mostra eventi precedenti", - "agenda_loading": "Caricamento…", - "agenda_range_start": "Eventi dal {date}", - "agenda_range_end": "Eventi fino al {date}", - "export_ics": "Esporta come .ics", - "copy_title": "Copia titolo", - "copy_link": "Copia link riunione", - "go_to_today": "Vai a oggi", - "new_all_day_event": "Nuovo evento giornata intera", - "new_event": "Nuovo evento", - "new_task": "Nuova attività" - }, - "detail": { - "add_note": "Aggiungi una nota...", - "save_note": "Salva", - "note_saved": "Nota aggiunta", - "open_link": "Apri link", - "meeting_link": "Link riunione", - "tentative": "Provvisorio", - "cancelled": "Annullato", - "delete_confirm": "Eliminare questo evento?" - }, - "form": { - "title": "Titolo", - "description": "Descrizione", - "location": "Luogo", - "meeting_link": "Link riunione", - "start_date": "Data di inizio", - "end_date": "Data di fine", - "start_time": "Ora di inizio", - "end_time": "Ora di fine", - "all_day_event": "Evento giornata intera", - "calendar_select": "Calendario", - "save": "Salva", - "cancel": "Annulla", - "delete_confirm": "Sei sicuro di voler eliminare questo evento?", - "color": "Colore" - }, - "participants": { - "availability": { - "title": "Disponibilità", - "free": "Disponibile", - "busy": "Occupato", - "tentative": "Provvisorio", - "unknown": "Non è un utente di questo server", - "checking": "Verifica in corso…" - }, - "title": "Partecipanti", - "add": "Aggiungi partecipante", - "organizer": "Organizzatore", - "attendee": "Partecipante", - "accepted": "Accettato", - "declined": "Rifiutato", - "tentative": "Provvisorio", - "needs_action": "In attesa di risposta", - "remove": "Rimuovi", - "edit": "Modifica", - "email_placeholder": "Aggiungi indirizzo email o cerca contatti", - "send_invitations": "Invia inviti ai partecipanti", - "status_summary": "{accepted} accettato/i, {pending} in attesa", - "invited_by": "Invito da {name}", - "respond_below": "Rispondi con i pulsanti qui sotto", - "rsvp_label": "La tua risposta", - "cancel_notification": "I partecipanti saranno avvisati della cancellazione", - "you_organizer": "Sei l'organizzatore", - "you_attendee": "Sei un partecipante", - "no_participants": "Nessun partecipante", - "count": "{count, plural, one {# partecipante} other {# partecipanti}}" - }, - "recurrence": { - "title": "Ricorrenza", - "none": "Non si ripete", - "daily": "Giornaliero", - "weekly": "Settimanale", - "monthly": "Mensile", - "yearly": "Annuale", - "every_n_days": "Ogni {count} giorni", - "every_n_weeks": "Ogni {count} settimane", - "every_n_months": "Ogni {count} mesi", - "until": "Fino al", - "occurrences": "{count} ripetizioni", - "custom": "Personalizzato…", - "edit_custom": "Modifica ricorrenza personalizzata", - "every_n_years": "Ogni {count} anni", - "on_days": "di {days}", - "on_day_n": "il giorno {day}", - "on_the_nth": "il {nth} {day}", - "in_month": "a {month}", - "nth_1": "primo", - "nth_2": "secondo", - "nth_3": "terzo", - "nth_4": "quarto", - "nth_last": "ultimo", - "editor_freq_day": "Giorno", - "editor_freq_week": "Settimana", - "editor_freq_month": "Mese", - "editor_freq_year": "Anno", - "editor_repeats_on": "Si ripete", - "editor_every": "Ogni", - "editor_unit_days": "giorno/i", - "editor_unit_weeks": "settimana/e", - "editor_unit_months": "mese/i", - "editor_unit_years": "anno/i", - "editor_on_day": "il giorno", - "editor_on_the": "il", - "editor_in": "a", - "editor_ends": "Termina", - "editor_never": "Mai", - "editor_ends_on": "Il", - "editor_ends_after": "Dopo", - "editor_occurrences": "occorrenze" - }, - "recurrence_scope": { - "edit_title": "Modifica evento ricorrente", - "delete_title": "Elimina evento ricorrente", - "description": "Questo è un evento ricorrente. Quali eventi desideri modificare?", - "this_event": "Solo questo evento", - "this_and_future": "Questo evento e i seguenti", - "all_events": "Tutti gli eventi", - "cancel": "Annulla", - "save": "Salva", - "delete": "Elimina", - "rsvp_title": "Rispondi a evento ricorrente", - "rsvp_description": "Questo è un evento ricorrente. A quali eventi si applica la tua risposta?", - "respond": "Rispondi" - }, - "alerts": { - "title": "Promemoria", - "none": "Nessun promemoria", - "at_time": "All'ora dell'evento", - "minutes_before": "{count, plural, one {# minuto prima} other {# minuti prima}}", - "hours_before": "{count, plural, one {# ora prima} other {# ore prima}}", - "days_before": "{count, plural, one {# giorno prima} other {# giorni prima}}", - "weeks_before": "{count, plural, one {# settimana prima} other {# settimane prima}}", - "unit_minutes_before": "minuti prima", - "unit_hours_before": "ore prima", - "unit_days_before": "giorni prima", - "unit_weeks_before": "settimane prima", - "add": "Aggiungi promemoria", - "remove": "Rimuovi promemoria", - "amount": "Quantità promemoria", - "unit": "Unità promemoria" - }, - "settings": { - "title": "Impostazioni calendario", - "default_view": "Vista predefinita", - "week_starts_on": "La settimana inizia il", - "time_format": "Formato ora", - "default_calendar": "Calendario predefinito", - "default_reminder": "Promemoria predefinito", - "time_format_12h": "12 ore", - "time_format_24h": "24 ore", - "notifications_enabled": "Notifiche eventi", - "notifications_enabled_desc": "Mostra avvisi per gli eventi in arrivo", - "notification_sound": "Suono di notifica", - "notification_sound_desc": "Riproduci un suono per gli avvisi del calendario", - "invitation_parsing": "Analizza gli inviti email", - "invitation_parsing_desc": "Rileva gli inviti del calendario negli allegati email e mostra le azioni del calendario", - "show_time_in_month_view": "Mostra orario nella vista mensile", - "show_time_in_month_view_desc": "Visualizza gli orari degli eventi nella vista mensile del calendario", - "show_week_numbers": "Mostra numeri di settimana", - "show_week_numbers_desc": "Mostra i numeri di settimana nel mini-calendario", - "calendar_free_scroll": "Scorrimento libero", - "calendar_free_scroll_desc": "Scorri in modo continuo tra mesi, settimane e giorni invece di un periodo alla volta", - "enable_tasks": "Abilita attività", - "enable_tasks_desc": "Mostra una vista attività nel calendario per gestire le cose da fare", - "show_tasks_on_calendar": "Mostra attività nel calendario", - "show_tasks_on_calendar_desc": "Mostra gli indicatori delle attività nelle viste giornaliera e settimanale del calendario", - "hover_preview": "Anteprima al passaggio del mouse", - "hover_preview_desc": "Mostra un popover dettagliato al passaggio del mouse sugli eventi del calendario", - "hover_preview_instant": "Istantaneo", - "hover_preview_delay_500ms": "Ritardo di 0,5 secondi", - "hover_preview_delay_1s": "Ritardo di 1 secondo", - "hover_preview_delay_2s": "Ritardo di 2 secondi", - "hover_preview_off": "Disabilitato", - "organizer_identity": "Organizza gli inviti come", - "organizer_identity_desc": "L'indirizzo del calendario usato come organizzatore quando inviti persone agli eventi.", - "organizer_identity_failed": "Impossibile cambiare l'indirizzo dell'organizzatore", - "show_birthday_calendar": "Calendario compleanni", - "show_birthday_calendar_desc": "Mostra un calendario virtuale con i compleanni dei tuoi contatti" - }, - "days": { - "monday": "Lunedì", - "tuesday": "Martedì", - "wednesday": "Mercoledì", - "thursday": "Giovedì", - "friday": "Venerdì", - "saturday": "Sabato", - "sunday": "Domenica", - "mon": "Lun", - "tue": "Mar", - "wed": "Mer", - "thu": "Gio", - "fri": "Ven", - "sat": "Sab", - "sun": "Dom" - }, - "notifications": { - "event_created": "Evento creato", - "event_updated": "Evento aggiornato", - "event_deleted": "Evento eliminato", - "calendar_created": "Calendario creato", - "calendar_deleted": "Calendario eliminato", - "event_move_error": "Spostamento dell'evento non riuscito", - "event_resize_error": "Ridimensionamento dell'evento non riuscito", - "alert_title": "Evento in arrivo", - "alert_now": "Inizia ora", - "alert_in_minutes": "Tra {count} min", - "invitation_sent": "Inviti inviati", - "invitations_denied_title": "Impossibile inviare gli inviti", - "invitations_denied": "Il server ha rifiutato di inviare gli inviti: {reason}. Salvare l'evento senza inviarli?", - "save_without_invitations": "Salva senza inviti", - "rsvp_updated": "Risposta aggiornata", - "rsvp_error": "Impossibile aggiornare la risposta", - "event_duplicated": "Evento duplicato", - "event_error": "Salvataggio dell'evento non riuscito", - "task_due": "Attività in scadenza", - "event_exported": "Evento esportato", - "title_copied": "Titolo copiato", - "link_copied": "Link copiato" - }, - "status": { - "loading_calendars": "Caricamento calendari...", - "loading_events": "Caricamento eventi..." - }, - "quick_create": { - "placeholder": "Titolo del nuovo evento", - "aria_label": "Creazione rapida evento" - }, - "nav_prev": "Precedente", - "nav_next": "Successivo", - "import": { - "title": "Importa calendario", - "tab_file": "File", - "tab_url": "URL", - "select_file": "Seleziona file .ics", - "drop_file": "o trascina il file qui", - "supported_formats": "File iCalendar (.ics) supportati", - "url_description": "Inserisci l'URL di un feed iCalendar (.ics) esterno per importare eventi.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Supporta URL iCalendar (.ics)", - "fetch": "Recupera", - "invalid_url": "Inserisci un URL valido", - "url_fetch_failed": "Impossibile recuperare il calendario dall'URL", - "parsing": "Analisi del file in corso...", - "parsed_events": "{count} eventi trovati", - "no_events": "Nessun evento trovato nel file", - "select_all": "Seleziona tutto", - "deselect_all": "Deseleziona tutto", - "target_calendar": "Importa nel calendario", - "import_button": "Importa selezionati", - "importing": "Importazione eventi...", - "success": "{count} eventi importati con successo", - "error": "Importazione del calendario fallita", - "file_too_large": "Il file supera il limite di 10 MB", - "invalid_format": "Formato del file calendario non valido" - }, - "webcal_action": { - "title": "Apri link calendario", - "description": "Come vuoi usare \"{name}\"?", - "import_title": "Importa una volta", - "import_description": "Recupera subito gli eventi e copiali in uno dei tuoi calendari.", - "subscribe_title": "Abbonati", - "subscribe_description": "Mantieni questo calendario sincronizzato automaticamente come calendario separato.", - "cancel": "Annulla" - }, - "management": { - "title": "Gestione calendari", - "description": "Crea, rinomina e personalizza i tuoi calendari. Fai clic destro su un calendario nella barra laterale per cambiarne rapidamente il colore.", - "name": "Nome", - "name_placeholder": "Nome del calendario", - "color": "Colore", - "change_color": "Cambia colore", - "random_color": "Nuovo colore casuale", - "add_calendar": "Aggiungi calendario", - "edit": "Modifica", - "delete": "Elimina", - "save": "Salva", - "create": "Crea", - "cancel": "Annulla", - "default": "Predefinito", - "confirm_delete": "Eliminare \"{name}\"? Tutti gli eventi in questo calendario verranno rimossi.", - "calendar_created": "Calendario creato", - "calendar_updated": "Calendario aggiornato", - "calendar_deleted": "Calendario eliminato", - "color_updated": "Colore del calendario aggiornato", - "error_create": "Impossibile creare il calendario", - "error_update": "Impossibile aggiornare il calendario", - "error_delete": "Impossibile eliminare il calendario", - "caldav_url": "URL CalDAV", - "copy_url": "Copia URL CalDAV", - "url_copied": "URL CalDAV copiato negli appunti", - "confirm_clear": "Cancellare tutti gli eventi da \"{name}\"? Questa azione non può essere annullata.", - "set_default": "Imposta come predefinito", - "default_updated": "Calendario predefinito aggiornato", - "error_default": "Impossibile impostare il calendario predefinito", - "clear_events": "Cancella eventi", - "events_cleared": "{count} eventi cancellati", - "error_clear": "Impossibile cancellare gli eventi del calendario", - "share": "Condividi calendario", - "new_event_in_calendar": "Nuovo evento in questo calendario", - "kind": "Contenuto", - "kind_events": "Eventi", - "kind_tasks": "Attività", - "kind_both": "Eventi e attività", - "kind_hint": "Determina quali app vedono questo calendario quando viene sincronizzato tramite CalDAV. Non può essere modificato in seguito." - }, - "subscription": { - "title": "Abbonamento iCal", - "section_title": "Abbonamenti iCal", - "description": "Abbonati a un feed iCalendar esterno. Gli eventi verranno sincronizzati automaticamente nel proprio calendario. Supporta URL https:// e webcal://.", - "url_label": "URL del calendario", - "url_placeholder": "https://example.com/calendar.ics o webcal://...", - "name_label": "Nome del calendario", - "name_placeholder": "es. Festività", - "color_label": "Colore", - "refresh_interval": "Intervallo di aggiornamento", - "interval_15": "Ogni 15 minuti", - "interval_30": "Ogni 30 minuti", - "interval_60": "Ogni ora", - "interval_360": "Ogni 6 ore", - "interval_1440": "Ogni giorno", - "subscribe": "Abbonati", - "subscribing": "Abbonamento in corso...", - "save": "Salva modifiche", - "saving": "Salvataggio...", - "edit": "Modifica", - "edit_title": "Modifica abbonamento", - "updated": "\"{name}\" aggiornato", - "update_error": "Impossibile aggiornare l'abbonamento", - "invalid_url": "Inserisci un URL valido", - "success": "Abbonato a \"{name}\"", - "error": "Impossibile aggiungere l'abbonamento", - "refresh": "Aggiorna ora", - "refresh_success": "Abbonamento aggiornato", - "refresh_error": "Impossibile aggiornare l'abbonamento", - "unsubscribe": "Annulla abbonamento", - "confirm_delete": "Annullare l'abbonamento a \"{name}\"? Il calendario e tutti i suoi eventi verranno rimossi.", - "deleted": "Abbonamento rimosso", - "delete_error": "Impossibile rimuovere l'abbonamento", - "last_refreshed": "Ultimo aggiornamento: {time}" - }, - "tasks": { - "label": "Attività", - "no_tasks": "Nessuna attività", - "no_title": "(Senza titolo)", - "mark_complete": "Segna come completata", - "mark_incomplete": "Segna come non completata", - "filter_all": "Tutte", - "filter_pending": "In sospeso", - "filter_completed": "Completate", - "filter_overdue": "Scadute", - "show_completed": "Mostra completate", - "create": "Nuova attività", - "edit": "Modifica attività", - "title_placeholder": "Titolo attività", - "description_placeholder": "Aggiungi una descrizione...", - "due_date": "Data di scadenza", - "include_time": "Includi ora", - "priority": "Priorità", - "priority_none": "Nessuna", - "priority_high": "Alta", - "priority_medium": "Media", - "priority_low": "Bassa", - "progress": "Stato", - "progress_needs_action": "Azione richiesta", - "progress_in_process": "In corso", - "progress_completed": "Completata", - "progress_cancelled": "Annullata", - "calendar": "Calendario", - "alert": "Promemoria", - "alert_none": "Nessuno", - "alert_at_time": "Alla data e ora di scadenza", - "alert_5min": "5 minuti prima", - "alert_15min": "15 minuti prima", - "alert_30min": "30 minuti prima", - "alert_1hr": "1 ora prima", - "alert_1day": "1 giorno prima", - "delete": "Elimina", - "cancel": "Annulla", - "save": "Salva", - "quick_add_placeholder": "Aggiungi un'attività...", - "due_today": "Scade oggi", - "due_tomorrow": "Scade domani", - "overdue": "Scaduta" - }, - "months": { - "jan": "gen", - "feb": "feb", - "mar": "mar", - "apr": "apr", - "may": "mag", - "jun": "giu", - "jul": "lug", - "aug": "ago", - "sep": "set", - "oct": "ott", - "nov": "nov", - "dec": "dic", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Apri menu" - }, - "advanced_search": { - "title": "Ricerca avanzata", - "from": "Da", - "from_placeholder": "Email o nome del mittente", - "to": "A", - "to_placeholder": "Email o nome del destinatario", - "subject": "Oggetto", - "subject_placeholder": "L'oggetto contiene...", - "body": "Corpo", - "body_placeholder": "Il corpo contiene...", - "folder": "Cartella", - "all_folders": "Tutte le cartelle", - "has_attachment": "Allegati", - "date_after": "Dopo il", - "date_before": "Prima del", - "size_min": "Maggiore di (KB)", - "size_max": "Minore di (KB)", - "starred": "Preferiti", - "unread": "Non letti", - "read": "Letti", - "yes": "Sì", - "no": "No", - "clear": "Cancella", - "clear_all": "Cancella tutto", - "filters_active": "{count} filtro", - "filters_active_plural": "{count} filtri", - "toggle_filters": "Filtri", - "search_hint": "Usa i filtri avanzati per una ricerca precisa", - "advanced_filters_tooltip": "Filtri di ricerca avanzati", - "results_found": "{count, plural, =0 {Nessun risultato trovato} one {# risultato trovato} other {# risultati trovati}}", - "results_found_more": "{count}+ risultati trovati", - "suggestions_recent": "Ricerche recenti", - "suggestions_people": "Persone", - "suggestions_from": "Cerca email da {address}", - "suggestions_to": "Cerca email a {address}", - "suggestions_remove_recent": "Rimuovi dalle ricerche recenti" - }, - "welcome": { - "title": "Benvenuto nella tua casella di posta", - "tip_compose": "Premi c per scrivere una nuova email", - "tip_shortcuts": "Premi ? per vedere tutte le scorciatoie da tastiera", - "tip_sidebar": "Trova Calendario, Contatti e Impostazioni nel menu laterale", - "tip_settings": "Personalizza la tua esperienza nelle Impostazioni", - "got_it": "Ho capito", - "settings": "Impostazioni", - "dismiss": "Chiudi", - "start_tour": "Inizia il tour" - }, - "demo_welcome": { - "title": "Benvenuto su Bulwark Mail", - "description": "Esplora un client webmail completo - direttamente nel tuo browser. Tutti i dati restano sul tuo dispositivo, quindi prova tutto.", - "feature_email": "Leggere e scrivere email", - "feature_organize": "Tag, stelle e cartelle", - "feature_shortcuts": "Scorciatoie da tastiera", - "feature_privacy": "Demo 100% privata", - "hint": "Clicca su un'email a sinistra per iniziare, oppure fai il tour." - }, - "files": { - "office_edit": "Modifica documento", - "office_loading": "Apertura dell'editor di documenti…", - "office_error": "Impossibile aprire l'editor di documenti.", - "office_close": "Chiudi editor", - "open_in_new_tab": "Apri in una nuova scheda", - "title": "File", - "search_placeholder": "Cerca file...", - "empty_state_title": "Nessun file ancora", - "empty_state_description": "Carica file o crea cartelle per iniziare", - "upload": "Carica", - "upload_files": "Carica file", - "new_folder": "Nuova cartella", - "new_folder_name": "Nome cartella", - "rename": "Rinomina", - "rename_title": "Rinomina", - "new_name": "Nuovo nome", - "name_forbidden_chars": "Il server non consente questi caratteri nei nomi: {chars}", - "name_reserved": "Questo nome è riservato sul server.", - "delete": "Elimina", - "delete_confirm_title": "Elimina risorsa", - "delete_confirm_message": "Sei sicuro di voler eliminare \"{name}\"? Questa azione non può essere annullata.", - "download": "Scarica", - "name": "Nome", - "size": "Dimensione", - "modified": "Modificato", - "type": "Tipo", - "folder": "Cartella", - "file": "File", - "parent_directory": "Directory superiore", - "breadcrumb_root": "Home", - "drop_files_here": "Trascina file o cartelle qui per caricarli", - "uploading": "Caricamento in corso...", - "upload_success": "{count, plural, one {1 file caricato} other {# file caricati}}", - "upload_error": "Caricamento del file non riuscito", - "create_folder_success": "Cartella creata", - "create_folder_error": "Creazione della cartella non riuscita", - "delete_success": "Eliminato con successo", - "delete_error": "Eliminazione non riuscita", - "rename_success": "Rinominato con successo", - "rename_error": "Rinominazione non riuscita", - "download_error": "Download non riuscito", - "not_available": "L'archiviazione file non è disponibile su questo server", - "cancel": "Annulla", - "create": "Crea", - "save": "Salva", - "no_results": "Nessun file corrisponde alla ricerca", - "batch_delete_confirm_message": "Sei sicuro di voler eliminare {count, plural, one {1 elemento} other {# elementi}}? Questa azione non può essere annullata.", - "batch_delete_success": "{count, plural, one {1 elemento eliminato} other {# elementi eliminati}}", - "grid_view": "Vista a griglia", - "list_view": "Vista a elenco", - "details": "Dettagli", - "path": "Percorso", - "preview": "Anteprima", - "preview_error": "Caricamento dell'anteprima non riuscito", - "cut": "Taglia", - "copy": "Copia", - "paste": "Incolla", - "move_success": "{count, plural, one {1 elemento spostato} other {# elementi spostati}}", - "move_error": "Spostamento non riuscito", - "paste_success": "Incollato con successo", - "paste_error": "Incollaggio non riuscito", - "new_text_file": "Nuovo file di testo", - "file_name": "Nome del file", - "retry": "Riprova", - "refresh": "Aggiorna", - "toggle_favorite": "Attiva/disattiva preferito", - "duplicate": "Duplica", - "duplicate_success": "Duplicato con successo", - "duplicate_error": "Duplicazione non riuscita", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "File creato", - "create_file_error": "Creazione del file non riuscita", - "favorites": "Preferiti", - "recent": "Recenti", - "properties": "Proprietà", - "open_folder": "Apri cartella", - "upload_folder": "Carica cartella", - "file_too_large": "\"{name}\" supera la dimensione massima del file ({max})", - "undo": "Annulla", - "undo_success": "Azione annullata", - "undo_error": "Annullamento non riuscito", - "toolbar": "Azioni file", - "file_list": "File e cartelle", - "context_menu": "Azioni", - "settings_title": "Impostazioni file", - "settings_display": "Visualizzazione", - "settings_default_view": "Vista predefinita", - "settings_default_view_desc": "Scegli tra layout a griglia e a lista", - "settings_default_sort": "Ordinamento predefinito", - "settings_default_sort_desc": "Scegli l'ordinamento predefinito per i file", - "settings_sort_direction": "Direzione ordinamento", - "settings_sort_direction_desc": "Scegli ordine crescente o decrescente", - "settings_ascending": "Crescente", - "settings_descending": "Decrescente", - "settings_icons": "Icone", - "settings_show_icons": "Mostra icone file", - "settings_show_icons_desc": "Visualizza le icone accanto a file e cartelle", - "settings_colored_icons": "Icone colorate", - "settings_colored_icons_desc": "Usa icone colorate invece che monocromatiche", - "settings_show_thumbnails": "Mostra miniature", - "settings_show_thumbnails_desc": "Mostra anteprime delle immagini al posto delle icone", - "settings_behavior": "Comportamento", - "settings_show_hidden": "Mostra file nascosti", - "settings_show_hidden_desc": "Visualizza file e cartelle che iniziano con un punto", - "settings_folder_layout": "Navigazione cartelle", - "settings_folder_layout_desc": "Scegli come visualizzare le cartelle: in linea con i file o in un albero nella barra laterale", - "settings_folder_layout_inline": "In linea", - "settings_folder_layout_sidebar": "Barra laterale", - "disabled_title": "La funzionalità File è stata disabilitata dal tuo amministratore", - "disabled_description": "I caricamenti di file di grandi dimensioni tramite WebDAV possono causare instabilità di Stalwart/RocksDB, inclusi crash di memoria e utilizzo irrecuperabile del disco. I file eliminati potrebbero non essere immediatamente rimossi dallo storage. Questa funzionalità non è consigliata per ambienti di produzione.", - "stability_warning": "I caricamenti di file di grandi dimensioni possono causare instabilità del server. I file eliminati potrebbero non essere immediatamente rimossi dallo storage. Usare con cautela.", - "no_accounts": "Nessun account collegato.", - "open_folder_tree": "Apri albero cartelle", - "other_accounts": "Altri account", - "migration_title": "Aggiornamento dei tuoi file…", - "migration_description": "Organizzazione di cartelle e file nella loro struttura corretta. Questo avviene solo una volta." - }, - "smime": { - "your_certificates": "I tuoi certificati", - "your_certificates_desc": "Importa e gestisci i tuoi certificati S/MIME per firmare e cifrare le email", - "recipient_certificates": "Certificati dei destinatari", - "recipient_certificates_desc": "Certificati pubblici per cifrare le email ai destinatari", - "identity_bindings": "Associazioni identità-chiave", - "identity_bindings_desc": "Associa i certificati S/MIME alle tue identità email", - "defaults_title": "Predefiniti", - "defaults_desc": "Configura il comportamento predefinito di firma e cifratura", - "import_pkcs12": "Importa PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importa certificato", - "no_certificates": "Nessun certificato importato", - "no_recipient_certs": "Nessun certificato destinatario", - "expires": "Scade", - "expired": "Scaduto", - "bound_to": "Associato a", - "no_key_bound": "Nessuno", - "lock": "Blocca chiave", - "unlock": "Sblocca chiave", - "details": "Visualizza dettagli", - "delete": "Elimina", - "encrypt_by_default": "Cifra per impostazione predefinita", - "encrypt_by_default_desc": "Cifra automaticamente le email quando tutti i destinatari hanno un certificato", - "remember_unlocked": "Ricorda le chiavi sbloccate", - "remember_unlocked_desc": "Mantieni le chiavi sbloccate per la durata di questa sessione del browser", - "sign_default_for": "Firma per impostazione predefinita per", - "enter_p12_passphrase": "Inserisci la passphrase PKCS#12", - "p12_passphrase_desc": "Inserisci la passphrase che protegge questo file di certificato", - "enter_storage_passphrase": "Imposta la passphrase di archiviazione", - "storage_passphrase_desc": "Scegli una passphrase per proteggere questa chiave nel browser", - "next": "Avanti", - "import": "Importa", - "unlock_key": "Sblocca chiave", - "unlock_key_desc": "Inserisci la passphrase di archiviazione per sbloccare questa chiave per la firma o la decrittazione", - "passphrase_placeholder": "Inserisci la passphrase", - "confirm_passphrase_placeholder": "Conferma la passphrase", - "passphrase_mismatch": "Le passphrase non corrispondono", - "cancel": "Annulla", - "processing": "Elaborazione…", - "close": "Chiudi", - "certificate_details": "Dettagli del certificato", - "cert_subject": "Oggetto", - "cert_issuer": "Emittente", - "cert_email": "Email", - "cert_serial": "Numero di serie", - "cert_validity": "Validità", - "cert_fingerprint": "Impronta digitale (SHA-256)", - "cert_algorithm": "Algoritmo", - "cert_capabilities": "Capacità", - "cert_source": "Origine", - "cert_expired": "Questo certificato è scaduto", - "cert_not_yet_valid": "Questo certificato non è ancora valido", - "cap_sign": "Firma", - "cap_encrypt": "Cifratura", - "cap_none": "Nessuna", - "show_passphrase": "Mostra passphrase", - "hide_passphrase": "Nascondi passphrase", - "sign_toggle": "Firma", - "encrypt_toggle": "Cifra", - "missing_recipient_certs": "Certificati mancanti per: {emails}", - "missing_sender_cert": "Nessun certificato associato a questa identità", - "status_encrypted_ok": "Questo messaggio è stato cifrato", - "status_encrypted_no_key": "Questo messaggio è cifrato, ma non è stata trovata una chiave corrispondente", - "status_encrypted_failed": "Impossibile decrittare questo messaggio", - "status_signed_valid": "Firma verificata", - "status_signed_invalid": "Verifica della firma non riuscita", - "status_signed_expired_cert": "Firmato con un certificato scaduto", - "status_signed_self_signed": "Firma valida, ma il certificato è autofirmato (non attendibile)", - "status_signed_mismatch": "La firma è valida, ma il firmatario non corrisponde al mittente", - "status_unsupported": "Formato S/MIME non supportato", - "auto_import_signer_certs": "Importa automaticamente i certificati dei firmatari", - "auto_import_signer_certs_desc": "Salva automaticamente i certificati delle email firmate verificate per future cifrature", - "export": "Esporta", - "enter_export_passphrase": "Imposta la passphrase di esportazione", - "export_passphrase_desc": "Scegli una passphrase per proteggere il file PKCS#12 esportato", - "export_storage_desc": "Inserisci la passphrase di archiviazione per decrittare la chiave prima dell'esportazione", - "incorrect_passphrase": "Passphrase errata" - }, - "tour": { - "step_counter": "Passo {current} di {total}", - "skip": "Salta il tour", - "back": "Indietro", - "next": "Avanti", - "finish": "Fine", - "take_a_tour": "Fai un tour dell'interfaccia", - "restart_title": "Tour introduttivo", - "restart_desc": "Rivedi la guida dell'interfaccia", - "restart_button": "Riavvia il tour", - "show_on_new_devices_title": "Mostra sui nuovi dispositivi", - "show_on_new_devices_desc": "Rivedi il banner di benvenuto e il tour al primo accesso su un nuovo dispositivo, anche se li hai già completati altrove", - "sidebar_title": "Le tue caselle di posta", - "sidebar_desc": "Questa è la barra laterale delle cartelle. Clicca su una casella per vedere le email. Puoi creare cartelle, trascinare email tra loro e vedere i conteggi dei non letti.", - "compose_title": "Scrivi un'email", - "compose_desc": "Clicca qui per scrivere una nuova email. Puoi aggiungere destinatari, allegati e usare la formattazione RTF.", - "search_title": "Cerca nella posta", - "search_desc": "Cerca per mittente, oggetto o contenuto. Clicca sull'icona del filtro per opzioni avanzate come intervallo di date, allegati e messaggi speciali.", - "email_list_title": "La tua lista email", - "email_list_desc": "Le email appaiono qui. Clicca su una per leggerla a destra. Usa la casella di controllo per selezionarne più di una, poi sposta, elimina o etichetta in blocco.", - "email_viewer_title": "Pannello di lettura", - "email_viewer_desc": "L'email selezionata si apre qui. Rispondi, inoltra, archivia o elimina con i pulsanti della barra degli strumenti. Puoi anche contrassegnare le email o aggiungere etichette colorate.", - "keywords_title": "Etichette colorate", - "keywords_desc": "Organizza le tue email con etichette colorate. Trascina un'email su un'etichetta per contrassegnarla, o fai clic destro per assegnare etichette.", - "calendar_title": "Calendario", - "calendar_desc": "Passa al calendario per gestire i tuoi eventi. Crea eventi, imposta promemoria e visualizza le viste giorno, settimana o mese.", - "contacts_title": "Contatti", - "contacts_desc": "La tua rubrica si trova qui. Importa contatti, crea gruppi e clicca su un contatto per vedere i suoi dettagli completi.", - "settings_title": "Impostazioni", - "settings_desc": "Personalizza tutto: tema, densità, firme, filtri, scorciatoie da tastiera, impostazioni del calendario e altro ancora.", - "shortcuts_title": "Scorciatoie da tastiera", - "shortcuts_desc": "Gli utenti esperti adorano questo. Premi ? in qualsiasi momento per vedere tutte le scorciatoie disponibili. Puoi navigare, comporre e gestire le email senza toccare il mouse.", - "compose_open_title": "Il compositore", - "compose_open_desc": "Questo è il compositore di email. Aggiungi destinatari, scrivi il tuo messaggio, allega file e usa la formattazione del testo. Puoi anche salvare bozze e usare modelli.", - "calendar_view_title": "Il tuo calendario", - "calendar_view_desc": "Ecco il tuo calendario con eventi di esempio. Puoi passare tra le viste giorno, settimana, mese e agenda usando la barra degli strumenti.", - "create_event_title": "Crea un evento", - "create_event_desc": "Fai clic su questo pulsante per creare un nuovo evento del calendario. Puoi impostare un titolo, data, ora e aggiungere partecipanti.", - "event_modal_title": "Dettagli evento", - "event_modal_desc": "Ecco il modulo dell'evento. Inserisci il titolo, scegli data e ora, aggiungi un luogo o partecipanti. Premi salva quando hai finito - o chiudilo e vai avanti.", - "contacts_list_title": "I tuoi contatti", - "contacts_list_desc": "Ecco i tuoi contatti. Clicca su un contatto per vedere i suoi dettagli a destra. Puoi anche creare nuovi contatti, importare vCard o organizzare i contatti in gruppi.", - "files_title": "Archiviazione file", - "settings_tabs_title": "Menu impostazioni", - "settings_tabs_desc": "Ecco tutte le categorie di impostazioni. Personalizza l'aspetto, gestisci le identità, configura i filtri email, imposta il calendario e molto altro.", - "files_desc": "Il tuo file browser ti permette di caricare, organizzare e condividere file - come un cloud personale integrato nella tua posta.", - "demo_banner_title": "Controlli demo", - "demo_banner_desc": "Sei in modalità demo - tutto rimane nel tuo browser. Premi 'Reimposta Demo' in qualsiasi momento per ricominciare con dati puliti.", - "quota_title": "Utilizzo dello spazio", - "quota_desc": "Monitora le dimensioni della tua casella qui. Il cerchio si riempie man mano che utilizzi più spazio." - }, - "unified_mailbox": { - "search_unavailable": "La ricerca non è disponibile nella vista unificata" - }, - "calendar_event_notifications": { - "someone": "Qualcuno", - "invited": "{name} ti ha invitato a \"{title}\"", - "updated": "{name} ha aggiornato \"{title}\"", - "cancelled": "{name} ha annullato \"{title}\"", - "untitled": "Evento senza titolo" - }, - "share_notifications": { - "someone": "Qualcuno", - "shared": "{name} ha condiviso con te {kind} \"{object}\"", - "changed": "{name} ha modificato il tuo accesso a {kind} \"{object}\"", - "revoked": "{name} ha rimosso il tuo accesso a {kind} \"{object}\"", - "object": { - "folder": "la cartella", - "calendar": "il calendario", - "address_book": "la rubrica", - "files": "la cartella dei file" - } - }, - "sharing": { - "title": "Condividi \"{name}\"", - "description": "Concedi l'accesso ad altri utenti o gruppi su questo server. Le modifiche hanno effetto immediato.", - "no_shares": "Non ancora condiviso.", - "add_person": "Aggiungi persona o gruppo", - "search_placeholder": "Cerca per nome o email…", - "loading_principals": "Caricamento utenti…", - "no_principals": "Nessun altro utente o gruppo trovato.", - "no_match": "Nessun risultato.", - "remove": "Rimuovi accesso", - "group": "Gruppo", - "share_added": "Accesso concesso", - "share_updated": "Accesso aggiornato", - "share_removed": "Accesso rimosso", - "share_failed": "Impossibile aggiornare la condivisione", - "preset": { - "freeBusy": "Solo libero/occupato", - "read": "Sola lettura", - "readWrite": "Lettura e scrittura", - "manager": "Gestore", - "custom": "Personalizzato" - } - }, - "quote_header": { - "reply_line": "Il {date}, {from} ha scritto:", - "forwarded_separator": "---------- Messaggio inoltrato ----------", - "from_label": "Da", - "date_label": "Data", - "subject_label": "Oggetto" - }, - "pwa_install": { - "title": "Installa {appName}", - "description": "Installa la nostra app per un accesso rapido e il supporto offline.", - "not_now": "Non ora", - "install": "Installa", - "dont_remind": "Non ricordarmelo più", - "dismiss_aria": "Chiudi avviso di installazione" - }, - "deep_link": { - "message_not_found": "Questo messaggio non è più disponibile.", - "thread_not_found": "Questa conversazione non è più disponibile.", - "folder_not_found": "Questa cartella non è più disponibile.", - "event_not_found": "Questo evento non è più disponibile.", - "file_not_found": "Questo file non è più disponibile.", - "account_unavailable": "Accedi a quell'account per aprire questo link.", - "copy_message": "Copia link al messaggio", - "copy_conversation": "Copia link alla conversazione", - "copy_event": "Copia link all'evento", - "copied": "Link copiato", - "copy_failed": "Impossibile copiare il link" - }, - "plugins": { - "oauth_callback": { - "completing": "Completamento dell'autorizzazione…", - "completing_message": "Attendi mentre viene stabilita la connessione.", - "received_title": "Autorizzazione ricevuta", - "received_message": "Puoi tornare alla scheda in cui hai avviato la connessione.", - "failed_title": "Autorizzazione non riuscita", - "denied": "Lautorizzazione è stata negata.", - "failed_detail": "Autorizzazione non riuscita: {error}", - "missing_params": "Codice di autorizzazione o stato mancante.", - "delivery_failed": "Impossibile consegnare il risultato dellautorizzazione alla scheda dellapp. Chiudi questa finestra e riprova." - } - }, - "global_search": { - "title": "Cerca ovunque", - "placeholder": "Cerca email, contatti, calendario, file...", - "scope_label": "Cerca in", - "account_label": "Account", - "scope_all": "Tutto", - "scope_mail": "Posta", - "scope_contacts": "Contatti", - "scope_calendar": "Calendario", - "scope_files": "File", - "all_accounts": "Tutti gli account", - "show_all": "Mostra tutti i risultati", - "no_results": "Nessun risultato per “{query}”", - "server_hint": "Le parole complete cercano sul server; quelle parziali corrispondono agli elementi già caricati", - "searching": "Ricerca in corso...", - "account_error": "Impossibile cercare in {account}", - "recent_searches": "Ricerche recenti", - "recurring": "Ricorrente", - "no_subject": "(no subject)", - "empty_hint": "Cerca email, contatti, eventi e file in tutti gli account. Prova from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Piatto per data", - "grouped": "Raggruppato", - "load_more": "Carica altri", - "preview_empty": "Seleziona un risultato per l'anteprima", - "open_full": "Apri", - "view_label": "Vista" - } -} +{ + "meta_description": "Client di posta web minimalista che utilizza il protocollo JMAP", + "login": { + "title": "Webmail", + "username_label": "Email", + "username_placeholder": "utente@esempio.com", + "password_label": "Password", + "password_placeholder": "Inserisci la tua password", + "jmap_endpoint_label": "Server JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Il server deve consentire le richieste CORS da questo dominio.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Server selezionato in base al tuo dominio email.", + "sign_in": "Accedi", + "signing_in": "Accesso in corso...", + "loading": "Caricamento...", + "reconnecting": "Connessione persa. Tentativo di riconnessione…", + "error": { + "invalid_credentials": "Email o password non valida", + "invalid_token": "Il server non ha accettato questo token di accesso. Verifica che sia completo e che non sia scaduto o revocato.", + "connection_failed": "Impossibile connettersi al server", + "cors_blocked": "Il server è raggiungibile ma sta bloccando le richieste cross-origin. Controlla le impostazioni CORS del tuo server JMAP e consenti questo dominio.", + "server_error": "Il server non è temporaneamente disponibile. Riprova più tardi.", + "generic": "Si è verificato un errore. Riprova.", + "totp_required": "È richiesto un codice di autenticazione a due fattori. Inserisci il codice qui sotto.", + "totp_invalid": "Codice di autenticazione non valido. Controlla la tua app di autenticazione.", + "oauth_discovery_failed": "SSO è abilitato ma il provider di identità non è raggiungibile. Controlla la configurazione OAuth.", + "token_exchange_failed": "Password e codice sono stati accettati, ma il server di posta ha rifiutato di avviare una sessione per questa app. Chiedi all'amministratore di controllare le impostazioni del client OAuth." + }, + "show_password": "Mostra password", + "hide_password": "Nascondi password", + "totp_toggle": "Ho un codice 2FA", + "token_toggle": "Accedi con un token di accesso", + "password_toggle": "Accedi con una password", + "token_label": "Token di accesso", + "token_placeholder": "Incolla il tuo token API", + "token_hint": "Crea un token API nelle impostazioni del tuo provider di posta e incollalo qui.", + "remember_me": "Ricordami", + "config_error": { + "title": "Errore di configurazione", + "fetch_failed": "Impossibile caricare la configurazione dell'applicazione. Riprovare più tardi.", + "server_not_configured": "Il server di posta non è stato configurato. Contattare l'amministratore." + }, + "remove_from_history": "Rimuovi dalla cronologia", + "totp_label": "Codice di autenticazione", + "totp_placeholder": "000000", + "session_expired": "La sessione è scaduta. Accedi di nuovo.", + "dismiss": "Chiudi", + "or": "o", + "sign_in_sso": "Accedi con SSO", + "add_account_title": "Aggiungi account", + "add_account_subtitle": "Accedi con un altro account", + "cancel": "Annulla", + "website": "Sito web", + "imprint": "Note legali", + "privacy_policy": "Informativa sulla privacy", + "try_demo": "Prova la demo", + "demo_description": "Esplora con dati di esempio - nessun account necessario", + "demo_launching": "Avvio demo...", + "demo_login_button": "Avvia demo", + "demo_tagline": "Scopri un client di posta completo. Nessun account richiesto.", + "demo_no_signup": "Nessuna registrazione - esplora liberamente con dati di esempio", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Completamento dell'accesso...", + "oauth_error": { + "title": "Autenticazione non riuscita", + "invalid_state": "La convalida di sicurezza è fallita. Prova ad accedere di nuovo.", + "missing_params": "Dati di autorizzazione mancanti. Prova ad accedere di nuovo.", + "token_exchange_failed": "Impossibile completare l'autenticazione. Riprova.", + "access_denied": "Accesso negato. Contatta il tuo amministratore.", + "back_to_login": "Torna al login" + } + }, + "sidebar": { + "close": "Chiudi", + "compose": "Scrivi", + "compose_hint": "Scrivi (c)", + "search_placeholder": "Cerca nella posta...", + "search_placeholder_hint": "Cerca posta... (premi /)", + "storage": "Spazio di archiviazione", + "storage_used": "Utilizzato", + "storage_free": "Libero", + "storage_total": "Totale", + "sign_out": "Esci", + "sign_out_of": "Disconnetti da {account}", + "sign_out_all": "Disconnetti da tutti gli account", + "add_account": "Aggiungi account", + "set_as_default": "Imposta come predefinito", + "switch_account": "Cambia account", + "contacts": "Contatti", + "calendar": "Calendario", + "settings": "Impostazioni", + "admin": "Admin", + "files": "File", + "loading_mailboxes": "Caricamento caselle di posta...", + "push_connected": "Aggiornamenti in tempo reale attivi", + "push_disconnected": "Aggiornamenti in tempo reale non attivi", + "keyboard_shortcuts": "Scorciatoie da tastiera", + "theme": { + "light": "Tema chiaro", + "dark": "Tema scuro", + "system": "Tema di sistema" + }, + "language": { + "title": "Lingua" + }, + "mailboxes": { + "inbox": "Posta in arrivo", + "sent": "Inviati", + "drafts": "Bozze", + "trash": "Cestino", + "archive": "Archivio", + "starred": "Speciali", + "all_mail": "Tutta la posta", + "spam": "Spam", + "important": "Importanti" + }, + "unified_inbox": "Posta in arrivo unificata", + "unified_sent": "Tutti gli inviati", + "unified_drafts": "Tutte le bozze", + "unified_trash": "Tutti i cestini", + "unified_archive": "Tutti gli archivi", + "unified_junk": "Tutto lo spam", + "all_accounts": "Tutti gli account", + "all_accounts_loading": "Caricamento account: {loaded} di {total}", + "unified_mailbox": "Casella di posta unificata", + "expand": "Espandi", + "collapse": "Comprimi", + "expand_tooltip": "Espandi", + "collapse_tooltip": "Comprimi", + "mobile": { + "search": "Cerca", + "compose": "Scrivi", + "go_back": "Indietro", + "toggle_menu": "Attiva/disattiva menu" + }, + "clear_search": "Cancella ricerca", + "vacation_active": "Risponditore automatico attivo", + "demo_banner": "Modalità demo", + "demo_reset": "Reimposta", + "demo_tour": "Tour", + "tags": "Etichette", + "show_all_tags": "Mostra tutto ({count})", + "show_fewer_tags": "Mostra meno", + "folders": "Cartelle", + "your_folders": "Le tue cartelle", + "create_folder": "Crea cartella", + "show_more": "Altro", + "show_fewer": "Riduci", + "mail": "Posta", + "nav_label": "Navigazione", + "add_app": "App", + "shared": "Condiviso", + "shared_account_hidden": "Nascosto dalla barra laterale. Puoi mostrarlo di nuovo in Impostazioni › Layout.", + "notifications_muted": "Notifiche disattivate", + "scheduled": "Programmate", + "unified_all_unread": "Tutti i non letti", + "unified_all_starred": "Tutti gli speciali", + "unified_all_mail": "Tutta la posta", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Etichette", + "create_label": "Crea nuova etichetta", + "mv_tasks": "Attività", + "mv_side_panel": "Riquadro laterale", + "mv_open_full": "Apri a schermo intero", + "mv_previous_day": "Giorno precedente", + "mv_next_day": "Giorno successivo", + "mv_add_another_account": "Aggiungi un altro account", + "mv_manage_account": "Gestisci il tuo account", + "mv_storage_used": "{percent}% di {total} utilizzato" + }, + "protocol_handlers": { + "title": "App predefinite", + "description": "Scegli se i link e-mail e calendario devono aprirsi in Bulwark. Tecnicamente, Bulwark si registra come gestore di protocollo per i link mailto: e webcal:.", + "unsupported": "Questo browser o questa connessione non supporta la registrazione manuale dei gestori di protocollo. Potresti comunque poter usare la PWA installata tramite le impostazioni del browser o del sistema.", + "mailto_label": "Link e-mail", + "mailto_description": "Apre i link mailto: in Bulwark con l'editor precompilato.", + "protocol_open_mode_label": "All'apertura dei link di protocollo", + "protocol_open_mode_description": "Scegli se Bulwark deve aprire i link mailto: e webcal: in una nuova scheda o riutilizzare una sessione aperta. L'opzione sessione attiva richiede l'autorizzazione alle notifiche, così puoi fare clic su una notifica di fallback per portare Bulwark in primo piano se il browser blocca il focus.", + "protocol_open_mode_active_session": "Apri nella sessione attiva se possibile", + "protocol_open_mode_new_tab": "Apri sempre una nuova scheda", + "focus_notification_title": "Apri Bulwark", + "focus_notification_body": "Il link è stato aperto in Bulwark. Fai clic per portare la finestra in primo piano.", + "webcal_label": "Link calendario", + "webcal_description": "Apre i link webcal: in Bulwark con una finestra di dialogo di sottoscrizione al calendario precompilata.", + "register_mailto": "Registra app e-mail", + "register_webcal": "Registra app calendario", + "mailto_registered": "Registrazione del gestore e-mail richiesta", + "webcal_registered": "Registrazione del gestore calendario richiesta", + "registration_failed": "Registrazione del gestore di protocollo non riuscita", + "opening_mailto": "Apertura editor...", + "opening_webcal": "Apertura calendario...", + "browser_note": "Il browser o il sistema operativo potrebbe chiederti di confermare e potrebbe richiedere che Bulwark sia installato prima di poterlo selezionare come app predefinita.", + "select_account_title": "Scegli account", + "select_mailto_account": "Scegli quale account deve aprire questo link e-mail.", + "select_webcal_account": "Scegli quale account deve aprire questo link calendario.", + "select_account_note": "Questa scelta si applica solo a questo link di protocollo.", + "detail_to": "A", + "detail_subject": "Oggetto", + "detail_no_subject": "Senza oggetto", + "detail_calendar": "Calendario", + "detail_source": "Fonte", + "active_account": "Attivo", + "switching_account": "Cambio account..." + }, + "sidebar_apps": { + "modal_title": "App della barra laterale", + "add_new": "Aggiungi app", + "edit_app": "Modifica app", + "name_label": "Nome", + "name_placeholder": "La mia app", + "name_required": "Il nome è obbligatorio", + "url_label": "URL", + "url_required": "L'URL è obbligatorio", + "url_invalid": "Inserisci un URL http o https valido", + "icon_label": "Icona", + "icon_required": "L'icona è obbligatoria", + "open_mode_label": "Modalità di apertura", + "open_new_tab": "Nuova scheda", + "open_inline": "Integrato", + "cancel": "Annulla", + "add": "Aggiungi", + "update": "Aggiorna", + "delete": "Elimina", + "delete_confirm_title": "Elimina app", + "delete_confirm": "Sei sicuro di voler eliminare \"{name}\"?", + "no_apps": "Nessuna app aggiunta", + "no_apps_hint": "Aggiungi app e link personalizzati alla barra laterale", + "managed_title": "Fornite dall'amministratore", + "managed_description": "Queste app sono configurate per tutti e non possono essere modificate.", + "managed_badge": "Gestita", + "search_icons": "Cerca icone...", + "show_popular": "Popolari", + "show_all": "Tutte", + "no_icons_found": "Nessuna icona trovata", + "inline_badge": "Integrato", + "tab_badge": "Scheda", + "show_on_mobile": "Mostra su mobile" + }, + "email_list": { + "columns": { + "from": "Da", + "to": "A", + "subject": "Oggetto", + "date": "Data", + "size": "Dimensione" + }, + "no_emails": "Nessun messaggio trovato", + "no_emails_description": "Questa casella è vuota", + "no_search_results": "Nessun risultato trovato", + "no_search_results_description": "Prova a modificare la ricerca o i filtri", + "loading": "Caricamento messaggi...", + "unread": "non letto", + "to_me": "A me", + "to_recipients": "A {count} destinatari", + "and_others": "e altri {count}", + "draft": "Bozza", + "starred": "Speciale", + "pinned": "Fissato", + "replied": "Risposto", + "forwarded": "Inoltrato", + "has_attachment": "Con allegato", + "conversations_range": "{from}–{to, number} di {total, number}", + "conversations_count": "{count} di {total} conversazioni", + "conversations_count_plus": "{count}+ conversazioni", + "conversations_count_simple": "{count} conversazioni", + "no_conversations": "Nessuna conversazione", + "loading_more": "Caricamento altri messaggi...", + "no_more_emails": "Nessun altro messaggio da caricare", + "batch_actions": { + "select": "Seleziona e-mail", + "select_all": "Seleziona tutto", + "selected_messages": "{count} email {count, plural, one {selezionata} other {selezionate}}", + "mark_read": "Segna come letto", + "mark_unread": "Segna come non letto", + "delete": "Elimina", + "delete_confirm_title": "Elimina e-mail", + "delete_confirm_message": "Sei sicuro di voler eliminare {count, plural, one {1 e-mail} other {# e-mail}}?", + "clear_selection": "Cancella selezione" + }, + "permanent_delete": "Elimina definitivamente", + "permanent_delete_confirm_title": "Elimina definitivamente", + "permanent_delete_confirm_message": "Questa e-mail verrà eliminata definitivamente. Questa azione non può essere annullata.", + "permanent_delete_confirm_batch_message": "Queste {count, plural, one {1 e-mail} other {# e-mail}} verranno eliminate definitivamente. Questa azione non può essere annullata.", + "empty_folder": { + "button": "Svuota cartella", + "confirm_title": "Svuota cartella", + "confirm_message": "Tutti i messaggi in questa cartella verranno eliminati definitivamente. Questa azione non può essere annullata.", + "confirm_message_trash": "Tutte le e-mail di questa cartella verranno spostate nel cestino.", + "confirm_button": "Svuota cartella", + "junk_hint": "Puoi svuotare la cartella Spam per rimuovere definitivamente tutti i messaggi.", + "trash_hint": "Puoi svuotare il Cestino per rimuovere definitivamente tutti i messaggi." + }, + "no_scheduled_emails": "Nessuna e-mail programmata", + "no_scheduled_emails_description": "I messaggi programmati per più tardi appariranno qui.", + "scheduled_count": "{count, plural, one {1 e-mail programmata} other {# e-mail programmate}}", + "scheduled_actions_hint": "I messaggi programmati possono essere annullati, riprogrammati o modificati dalle azioni di programmazione.", + "cancel_scheduled_send": "Annulla invio", + "reschedule_send": "Riprogramma", + "cancel_and_edit": "Annulla e modifica", + "cancel_and_compose_again": "Annulla e componi di nuovo", + "reschedule_prompt": "Inserisci una nuova data/ora, ad es. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Il mittente richiede una conferma di lettura:", + "send": "Invia conferma", + "ignore": "Ignora", + "sent": "Conferma di lettura inviata.", + "send_failed": "Impossibile inviare la conferma di lettura", + "mdn_subject": "Letto: {subject}", + "mdn_body": "Questa è una conferma di lettura del messaggio che hai inviato a {recipient}.\n\nNota: questa conferma attesta solo che il messaggio è stato visualizzato sul computer del destinatario. Non garantisce che il destinatario abbia letto o compreso il contenuto." + }, + "no_email_selected": "Nessun messaggio selezionato", + "no_email_description": "Seleziona un messaggio dall'elenco per visualizzarlo qui", + "no_conversation_selected": "Nessuna conversazione selezionata", + "no_conversation_description": "Scegli una conversazione dall'elenco per leggerla qui", + "compose": "Componi", + "compose_hint": "Componi nuovo messaggio", + "no_subject": "(Nessun oggetto)", + "no_body_content": "(Nessun contenuto disponibile)", + "no_preview_available": "Anteprima non disponibile", + "show_quoted_text": "Mostra testo citato", + "hide_quoted_text": "Nascondi testo citato", + "loading_email": "Caricamento messaggio...", + "loading": "Caricamento...", + "reply": "Rispondi", + "reply_all": "Rispondi a tutti", + "forward": "Inoltra", + "delete": "Elimina", + "archive": "Archivia", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Aggiungi stella", + "unstar": "Rimuovi stella", + "mark_unread": "Segna come non letto", + "mark_read": "Segna come letto", + "unread": "Non letto", + "read": "Letto", + "spam_short": "Spam", + "not_spam_short": "Non spam", + "move": "Sposta", + "print": "Stampa", + "view_source": "Visualizza sorgente", + "fullscreen": "Schermo intero", + "exit_fullscreen": "Esci da schermo intero", + "view_in_dark_mode": "Visualizza in modalità scura", + "view_in_light_mode": "Visualizza in modalità chiara", + "export_email": "Esporta come .eml", + "forward_as_attachment": "Inoltra come allegato", + "import_email": "Importa .eml o .zip", + "keyboard_shortcuts": "Scorciatoie da tastiera (?)", + "email_source": "Sorgente del messaggio", + "draft_banner": "Questo messaggio è una bozza", + "edit_draft": "Modifica", + "copy_source": "Copia negli appunti", + "source_copied": "Sorgente copiata negli appunti", + "attachments": "Allegati", + "important": "Importante", + "download": "Scarica", + "download_all": "Scarica tutto", + "from": "Da", + "to": "A", + "cc": "CC", + "bcc": "CCN", + "date": "Data", + "subject": "Oggetto", + "show_details": "Mostra dettagli", + "hide_details": "Nascondi dettagli", + "external_content_warning": "Le immagini e i contenuti esterni sono stati bloccati", + "load_external_content": "Carica immagini", + "trust_sender": "Considera sempre attendibile questo mittente", + "sender_check": { + "unverified_label": "Mittente non verificato", + "failed_label": "Verifica del mittente non riuscita", + "unverified": "Questo messaggio dichiara di provenire da {domain}, ma nulla lo conferma. Non ha una firma valida di {domain} e non proviene da un server autorizzato da {domain}.", + "unverified_sent_from": "Questo messaggio dichiara di provenire da {domain}, ma è stato inviato da {host} e non ha una firma valida di {domain}.", + "failed": "Questo messaggio dichiara di provenire da {domain}, ma non ha superato i controlli sul mittente di quel dominio. Potrebbe essere falsificato.", + "failed_sent_from": "Questo messaggio dichiara di provenire da {domain}, ma è stato inviato da {host} e non ha superato i controlli sul mittente di {domain}. Potrebbe essere falsificato.", + "caution": "Fai attenzione a link, allegati e richieste di password o dati di pagamento." + }, + "back_to_list": "Torna all'elenco", + "view_contact": "Visualizza contatto", + "message_details": "Dettagli del messaggio", + "more_reply_options": "Più opzioni di risposta", + "set_tag": "Imposta etichetta", + "tag": "Etichetta", + "more_actions": "Altre azioni", + "move_to": "Sposta in...", + "remove_tag": "Rimuovi etichetta", + "tag_filter_placeholder": "Filtra etichette", + "tag_no_matches": "Nessuna etichetta corrispondente", + "more_count": "+{count} altri", + "characters_count": "{count} caratteri", + "quick_reply_placeholder": "Scrivi una risposta veloce...", + "more_options": "Più opzioni", + "sending": "Invio in corso...", + "security_authentication": "Sicurezza e autenticazione", + "technical_details": "Dettagli tecnici", + "message_id_label": "ID messaggio:", + "reply_to_label": "Rispondi a:", + "delivery_time_label": "Orario di consegna:", + "conversation_part_label": "Parte della conversazione:", + "previous_messages": "{count} messaggio precedente", + "previous_messages_plural": "{count} messaggi precedenti", + "time": { + "day": "giorno", + "days": "giorni", + "hour": "ora", + "hours": "ore", + "minute": "minuto", + "minutes": "minuti" + }, + "unknown_sender": "Sconosciuto", + "recipient_me": "io", + "recipient_and_others": "{name} e altri {count}", + "recipient_to_prefix": "A:", + "authentication": { + "title": "Autenticazione", + "status": { + "verified": "Verificato", + "warning": "Attenzione", + "none": "Non autenticato" + }, + "spf": { + "pass": "SPF superato", + "fail": "SPF fallito", + "none": "Nessun SPF" + }, + "dkim": { + "pass": "DKIM valido", + "fail": "DKIM non valido", + "none": "Nessun DKIM" + }, + "dmarc": { + "pass": "DMARC superato", + "fail": "DMARC fallito", + "none": "Nessun DMARC" + }, + "spam_score": "Punteggio spam", + "tooltip_spf": "Sender Policy Framework: verifica che il server mittente sia autorizzato a inviare e-mail per conto del dominio", + "tooltip_dkim": "DomainKeys Identified Mail: conferma tramite firma crittografica che l'e-mail non è stata alterata durante il transito", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: assicura che SPF e DKIM siano allineati al dominio del mittente e definisce una politica in caso di errore", + "policy": "Criterio", + "result": { + "pass": "Superato", + "fail": "Fallito", + "softfail": "Fallimento lieve", + "neutral": "Neutro", + "permerror": "Errore permanente", + "temperror": "Errore temporaneo", + "none": "Nessuno" + } + }, + "headers": { + "routing": "Instradamento", + "received": "Ricevuto", + "message_id": "ID messaggio", + "list_info": "Informazioni lista" + }, + "tooltips": { + "reply": "Rispondi", + "reply_all": "Rispondi a tutti (a)", + "forward": "Inoltra (f)", + "archive": "Archivia", + "delete": "Elimina", + "star": "Aggiungi stella (s)", + "unstar": "Rimuovi stella (s)", + "compose": "Scrivi (c)", + "previous": "Email precedente", + "next": "Email successiva", + "edit_draft": "Modifica bozza" + }, + "spam": { + "button_title": "Segnala come spam", + "not_spam_title": "Segna come legittimo", + "toast_success": "Spostato in Posta indesiderata", + "toast_batch": "{count} messaggi spostati in Posta indesiderata", + "toast_undo": "Annulla", + "toast_not_spam_success": "Spostato in Posta in arrivo", + "toast_not_spam_batch": "{count} messaggi spostati in Posta in arrivo", + "error": "Impossibile segnalare come spam", + "error_not_spam": "Impossibile ripristinare il messaggio" + }, + "unsubscribe_banner": { + "label": "Newsletter", + "button": "Annulla iscrizione", + "confirm_title": "Annullare l'iscrizione da questo mittente?", + "confirm_button": "Conferma", + "cancel": "Annulla", + "success_http": "Pagina di annullamento iscrizione aperta in una nuova scheda", + "success_mailto": "Email di disiscrizione inviata", + "confirm_message_http": "La pagina di disiscrizione si aprirà in una nuova scheda.", + "confirm_message_mailto": "Verrà inviata un'email di disiscrizione al mittente.", + "error": "Impossibile annullare l'iscrizione", + "dismiss": "Ignora" + }, + "calendar_invitation": { + "loading": "Caricamento dettagli evento…", + "title": "Invito calendario", + "published_title": "Evento pubblicato", + "response_title": "Risposta all'evento", + "update_title": "Aggiornamento evento", + "counter_title": "Controproposta", + "refresh_title": "Richiesta di aggiornamento", + "declined_counter_title": "Controproposta rifiutata", + "cancelled_title": "Evento annullato", + "organizer": "Organizzato da {name}", + "organizer_label": "Organizzato da", + "attendees": "{count, plural, one {# partecipante} other {# partecipanti}}", + "accept": "Accetta", + "maybe": "Forse", + "decline": "Rifiuta", + "add_to_calendar": "Aggiungi al calendario", + "added": "Aggiunto al calendario", + "rsvp_sent": "Risposta inviata", + "parse_error": "Impossibile leggere l'invito", + "action_failed": "Impossibile completare questa azione del calendario.", + "no_calendar": "Calendario non disponibile", + "published_info": "Questo evento è stato condiviso come riferimento.", + "response_info": "Questo messaggio contiene la risposta di un partecipante.", + "response_info_organizer": "Questa risposta aggiorna il tuo evento.", + "update_info": "Questo messaggio aggiorna un evento esistente.", + "counter_info": "Questo messaggio propone modifiche a un evento.", + "counter_info_organizer": "Questo partecipante ha proposto modifiche al tuo evento.", + "refresh_info": "Questo messaggio richiede gli ultimi dettagli dell'evento.", + "refresh_info_organizer": "Un partecipante ha richiesto gli ultimi dettagli dell'evento.", + "declined_counter_info": "L'organizzatore ha rifiutato una controproposta.", + "authentication_failed_info": "I controlli di autenticazione di questo invito non sono riusciti. Tratta con cautela le azioni sul calendario.", + "authentication_missing_info": "Questo invito non include un'autenticazione email verificata. Conferma i dettagli con l'organizzatore se qualcosa sembra insolito.", + "sender_mismatch_info": "Questo invito è stato inviato da {sender}, mentre l'organizzatore indicato nei dati del calendario è {organizer}.", + "sender_mismatch_unverified_info": "Questo invito è stato inviato da {sender}, mentre l'organizzatore indicato nei dati del calendario è {organizer}, e il messaggio non ha potuto essere verificato.", + "organizer_role": "Sei l'organizzatore di questo evento", + "your_response": "La tua risposta: {status}", + "response_needed": "Risposta richiesta", + "response_accepted": "Accettata", + "response_tentative": "Provvisoria", + "response_declined": "Rifiutata", + "response_delegated": "Delegata", + "actor_sent_info": "Inviato da {name}.", + "actor_response_info": "{name} ha risposto {status}.", + "actor_counter_info": "{name} ha proposto modifiche a questo evento.", + "actor_refresh_info": "{name} ha chiesto i dettagli più recenti dell'evento.", + "actor_declined_counter_info": "{name} ha rifiutato la controproposta.", + "actor_note": "Nota: {comment}", + "actor_unknown": "Qualcuno", + "proposed_changes": "Modifiche proposte", + "change_title": "Titolo", + "change_time": "Orario", + "change_location": "Luogo", + "change_description": "Descrizione", + "change_empty": "Nessuno", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Applica modifiche proposte", + "proposal_applied": "Le modifiche proposte sono state applicate.", + "review_proposal": "Rivedi proposta", + "review_request": "Rivedi richiesta", + "view_in_calendar": "Vedi nel calendario", + "select_calendar": "Seleziona calendario", + "already_in_calendar": "Già nel tuo calendario", + "request_info": "Sei stato invitato a questo evento. Rispondi per comunicare la tua disponibilità all'organizzatore.", + "cancel_info": "L'organizzatore ha annullato questo evento.", + "event_updated": "Aggiornamento #{sequence}", + "event_status_tentative": "Provvisorio", + "event_status_cancelled": "Annullato", + "collapse": "Nascondi dettagli", + "expand": "Mostra dettagli" + }, + "previous": "Precedente", + "next": "Successivo", + "send": "Invia", + "more": "altri", + "details": { + "recipients_routing": "Destinatari e instradamento", + "authentication_security": "Autenticazione e sicurezza", + "identifiers_threading": "Identificatori e conversazione", + "mailing_list": "Mailing list", + "message_properties": "Proprietà del messaggio", + "sent": "Inviato", + "received": "Ricevuto", + "delivery_time": "Tempo di consegna", + "in_reply_to": "In-Reply-To", + "references": "Riferimenti", + "thread_id": "ID conversazione", + "size": "Dimensione", + "mime_type": "Tipo MIME", + "attachments_summary": "{count} file · {size}", + "list_id": "ID lista", + "list_help": "Guida lista", + "list_post": "Invio alla lista", + "list_unsubscribe": "Annulla iscrizione", + "iprev": "DNS inverso", + "spam_status": "Stato spam", + "ai_verdict": "Verdetto IA", + "account": "Account", + "no_subject": "(nessun oggetto)" + }, + "scheduled_banner": "Programmato per l’invio il {date}", + "scheduled_send_created": "E-mail programmata per l’invio", + "sending_in": "Invio tra {seconds} s", + "undo_send_failed": "Impossibile annullare. Il messaggio potrebbe essere già stato inviato.", + "cancel_scheduled_send": "Annulla invio", + "reschedule_send": "Riprogramma", + "cancel_and_edit": "Annulla e modifica", + "cancel_and_compose_again": "Annulla e componi di nuovo", + "reschedule_prompt": "Inserisci una nuova data/ora, ad es. 2026-05-04T15:30", + "scheduled_actions_only": "La vista Programmate supporta solo azioni di programmazione", + "undo_send_scheduled": "Messaggio programmato per l’invio", + "undo_send": "Annulla invio", + "contact_sidebar": { + "title": "Contatto", + "close": "Chiudi barra laterale", + "action_email": "Email", + "action_email_title": "Invia email", + "action_copy": "Copia", + "action_copy_title": "Copia email", + "action_edit_title": "Modifica contatto", + "section_emails": "Email", + "section_phones": "Telefoni", + "section_organizations": "Organizzazioni", + "section_addresses": "Indirizzi", + "section_notes": "Note", + "not_in_contacts": "Non è nei tuoi contatti", + "add_to_contacts": "Aggiungi ai contatti", + "copied": "Copiato!", + "copy_failed": "Copia non riuscita", + "view_contact": "Visualizza contatto", + "view_details": "Visualizza dettagli" + }, + "send_now": "Invia ora", + "verification_code": { + "copy": "Copia codice {code}", + "copied": "Codice copiato", + "copy_failed": "Impossibile copiare il codice" + }, + "mv_position": "{index} di {total}", + "plugin_panel_show": "Mostra il pannello del plugin", + "plugin_panel_hide": "Nascondi il pannello del plugin" + }, + "email_composer": { + "select_from_directory": "Seleziona dalla rubrica aziendale", + "minimize": "Riduci a icona", + "maximize": "Schermo intero", + "restore": "Ripristina", + "new_message": "Nuovo messaggio", + "signature_edit_hint": "Doppio clic per modificare la firma", + "reply": "Rispondi", + "reply_all": "Rispondi a tutti", + "forward": "Inoltra", + "reply_to": "Rispondi", + "reply_all_to": "Rispondi a tutti", + "forward_message": "Inoltra", + "from": "Da", + "to": "A", + "cc": "CC", + "bcc": "CCN", + "subject": "Oggetto", + "body_placeholder": "Scrivi il tuo messaggio...", + "send": "Invia", + "cancel": "Annulla", + "attach": "Allega", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Allega da File", + "files_picker": { + "attach": "{count, plural, =0 {Allega} one {Allega 1 file} other {Allega # file}}", + "empty_folder": "Questa cartella è vuota", + "load_failed": "Impossibile caricare i tuoi file" + }, + "discard": "Scarta", + "discard_draft_title": "Eliminare la bozza?", + "discard_draft_confirm": "Ci sono modifiche non salvate. Vuoi scartare questa bozza?", + "discard_draft_message": "La bozza verrà eliminata.", + "saving": "Salvataggio...", + "draft_saved": "Bozza salvata", + "save_failed": "Salvataggio non riuscito", + "to_placeholder": "Indirizzi email dei destinatari", + "cc_placeholder": "Destinatari in copia", + "bcc_placeholder": "Destinatari in copia nascosta", + "subject_placeholder": "Oggetto", + "cc_label": "Cc:", + "bcc_label": "Ccn:", + "subject_label": "Oggetto:", + "file_size_kb": "KB", + "prefix": { + "forward": "Inol:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 email} other {# email}}", + "no_subject": "(Nessun oggetto)", + "unknown_sender": "Sconosciuto", + "quote": { + "reply_header": "Il {date}, {sender} ha scritto:", + "forward_header": "---------- Messaggio inoltrato ----------", + "from": "Da: {sender}", + "date": "Data: {date}", + "subject": "Oggetto: {subject}", + "to": "A: {recipients}" + }, + "remove_sub_address": "Rimuovi sotto-indirizzo", + "from_override": { + "toggle_off": "Sovrascrivi", + "toggle_on": "Annulla sovrascrittura", + "toggle_tooltip": "Modifica liberamente nome e indirizzo del mittente. La posta viene comunque inviata tramite la tua identità.", + "name_label": "Nome mittente", + "name_placeholder": "Nome", + "email_label": "Indirizzo email del mittente", + "email_placeholder": "alias@example.com", + "envelope_notice": "Se il tuo server non accetta questo indirizzo come mittente della busta, viene usato {identity} al suo posto, e i destinatari possono vederlo nell'intestazione Return-Path." + }, + "save_as_template": "Salva come modello", + "attach_from_device": "Carica da questo dispositivo", + "templates_menu": "Modelli", + "insert_template": "Inserisci modello", + "options": "Opzioni", + "option_plain_text": "Testo semplice", + "option_plain_text_hint": "Invia senza formattazione", + "option_read_receipt": "Richiedi conferma di lettura", + "option_read_receipt_hint": "Chiedi al destinatario di confermare l'apertura", + "option_dsn": "Notifica di consegna", + "option_dsn_hint": "Ricevi un rapporto quando il server lo consegna", + "option_require_tls": "Richiedi consegna cifrata", + "option_require_tls_hint": "Consegna solo tramite TLS, altrimenti respingi", + "option_priority": "Priorità", + "option_priority_high": "Alta", + "option_priority_normal": "Normale", + "option_priority_low": "Bassa", + "validation": { + "recipient_required": "Aggiungi un destinatario per inviare", + "body_required": "Scrivi un messaggio o allega un file", + "invalid_recipients": "Non inviato - controlla questi indirizzi: {addresses}", + "attachments_uploading": "Gli allegati sono ancora in fase di caricamento: verrà inviato al termine", + "attachment_upload_failed": "Non inviato: caricamento di un allegato non riuscito. Rimuovilo e riprova." + }, + "upload_progress": "Caricamento {uploaded} / {total}", + "upload_cancel": "Annulla caricamento", + "upload_failed": "Caricamento di {filename} non riuscito", + "attachment_too_large": "\"{name}\" supera la dimensione consentita dal server ({max} per file)", + "attachments_total_too_large": "Gli allegati supererebbero il limite del server di {max} per messaggio", + "send_failed": "Invio dell'e-mail non riuscito", + "smime_sign_on": "Firma S/MIME attivata", + "smime_sign_off": "Firma S/MIME disattivata", + "smime_encrypt_on": "Cifratura S/MIME attivata", + "smime_encrypt_off": "Cifratura S/MIME disattivata", + "smime_encrypt_unavailable": "Cifratura S/MIME non disponibile – certificati destinatario mancanti", + "smime_unlock_title": "Sblocca chiave S/MIME", + "smime_unlock_message": "Inserisci la passphrase per sbloccare la tua chiave S/MIME.", + "smime_unlock_button": "Sblocca", + "smime_passphrase_placeholder": "Passphrase S/MIME", + "send_timeout": "Il server ha smesso di rispondere. Controlli la cartella Posta inviata prima di inviare di nuovo: il messaggio potrebbe essere già partito.", + "send_offline": "Sembra che tu sia offline. Il messaggio è ancora qui, invialo di nuovo quando torni online.", + "formatting": "Formattazione", + "send_recipients_rejected": "Non inviato: il server ha rifiutato tutti i destinatari.", + "send_plugin_failed": "Non inviato: un plugin che gestisce l'invio non è riuscito o ha impiegato troppo tempo. Controlla la cartella Inviati prima di inviare di nuovo.", + "send_some_recipients_rejected": "Inviato, ma non a questi destinatari: il server li ha rifiutati.", + "continue_draft": "Continua bozza", + "close_draft_title": "Salvare o eliminare la bozza?", + "close_draft_message": "Hai modifiche non salvate. Vuoi salvare come bozza o eliminare?", + "drop_files": "Trascina i file per allegarli", + "show_less": "Mostra meno", + "forgot_attachment": { + "title": "Hai dimenticato un allegato?", + "message": "Il tuo messaggio menziona \"{keyword}\" ma nessun file è allegato. Inviare comunque?", + "send_anyway": "Invia comunque", + "back": "Torna alla modifica" + }, + "empty_subject": { + "title": "Inviare senza oggetto?", + "message": "Questo messaggio non ha un oggetto. Inviarlo comunque?", + "dont_ask_again": "Non chiedere più", + "send_anyway": "Invia comunque", + "back": "Torna alla modifica" + }, + "add_link": "Aggiungi link", + "link_url_prompt": "Inserisci l'URL", + "sending": "Invio in corso...", + "schedule_send": "Programma invio", + "schedule_send_description": "Scegli quando il server deve rilasciare questo messaggio.", + "schedule_preset": { + "tomorrow_morning": "Domattina", + "tomorrow_afternoon": "Domani pomeriggio", + "monday_morning": "Lunedì mattina" + }, + "schedule_send_required": "Scegli una data e un’ora.", + "schedule_send_invalid": "Inserisci una data e un’ora valide.", + "schedule_send_future": "Scegli una data e un’ora future.", + "schedule_send_too_late": "Questo orario supera il limite consentito dal server.", + "schedule_send_unsupported": "L’invio programmato non è supportato per questo account.", + "schedule_send_cleanup_warning": "L’invio programmato è stato creato, ma la pulizia della bozza non è riuscita.", + "send_delay_unsupported": "Il ritardo di invio non è supportato per questo account.", + "send_delay_unsupported_confirm": "Questo account non supporta il ritardo di invio. Inviare subito?", + "recipient_edit_email": "Modifica indirizzo email", + "recipient_edit_name": "Modifica nome visualizzato", + "recipient_email_placeholder": "Indirizzo email", + "recipient_name_placeholder": "Nome visualizzato", + "invalid_recipient": "Indirizzo email non valido", + "autocomplete_search_server": "Cerca nel server", + "autocomplete_searching": "Ricerca in corso...", + "mention_recipients": "Destinatari", + "mention_card_not_recipient": "Non è in A o CC, quindi non riceverà questo messaggio.", + "mention_card_add": "Aggiungi in {field}", + "mention_card_remove": "Rimuovi menzione", + "toolbar": { + "bold": "Grassetto", + "italic": "Corsivo", + "underline": "Sottolineato", + "strikethrough": "Barrato", + "text_color": "Colore del testo", + "remove_color": "Rimuovi colore", + "heading_1": "Titolo 1", + "heading_2": "Titolo 2", + "bullet_list": "Elenco puntato", + "ordered_list": "Elenco numerato", + "quote": "Citazione", + "code_block": "Blocco di codice", + "align_left": "Allinea a sinistra", + "align_center": "Centra", + "align_right": "Allinea a destra", + "text_direction": "Direzione del testo (RTL/LTR)", + "link": "Link", + "table": "Tabella", + "clear_formatting": "Cancella formattazione", + "undo": "Annulla", + "redo": "Ripeti", + "add_row_above": "Aggiungi riga sopra", + "add_row_below": "Aggiungi riga sotto", + "add_column_before": "Aggiungi colonna prima", + "add_column_after": "Aggiungi colonna dopo", + "delete_row": "Elimina riga", + "delete_column": "Elimina colonna", + "toggle_header_row": "Attiva/disattiva riga di intestazione", + "delete_table": "Elimina tabella", + "pick_size": "Scegli dimensione", + "font_size": "Dimensione carattere", + "font_size_default": "Predefinita", + "background_color": "Colore di sfondo", + "remove_background_color": "Rimuovi colore di sfondo" + }, + "send_filing_warning": "Inviato - ma la pulizia successiva non è riuscita, potrebbe restare una bozza obsoleta.", + "pop_out": "Apri la risposta in una finestra", + "send_guard_failed": "Non inviato - un plugin che controlla i messaggi prima dell'invio non è riuscito o non ha risposto." + }, + "confirm_dialog": { + "confirm": "Conferma", + "cancel": "Annulla" + }, + "common": { + "loading": "Caricamento...", + "error": "Errore", + "success": "Successo", + "cancel": "Annulla", + "save": "Salva", + "delete": "Elimina", + "edit": "Modifica", + "close": "Chiudi", + "search": "Cerca", + "refresh": "Aggiorna", + "settings": "Impostazioni", + "help": "Aiuto", + "logout": "Esci", + "yes": "Sì", + "no": "No", + "unknown": "Sconosciuto", + "app_title": "Webmail", + "reconnecting": "Connessione persa. Tentativo di riconnessione…", + "rate_limited_title": "L'autenticazione del server e temporaneamente limitata.", + "rate_limited_detail": "Bulwark ha sospeso le richieste in background per evitare il blocco. Nuovo tentativo tra {seconds}s.", + "rate_limited_action_title": "Richiesta sospesa per evitare il blocco.", + "rate_limited_action_detail": "Bulwark attende che termini il cooldown del server prima di inviare altre richieste autenticate. Riprova tra {seconds}s.", + "pull_to_refresh": "Trascina per aggiornare", + "release_to_refresh": "Rilascia per aggiornare", + "refreshing": "Aggiornamento…" + }, + "notifications": { + "email_sent": "Messaggio inviato con successo", + "email_deleted": "Messaggio eliminato", + "email_archived": "Messaggio archiviato", + "emails_deleted": "{count, plural, one {# email eliminata} other {# email eliminate}}", + "emails_archived": "{count, plural, one {# email archiviata} other {# email archiviate}}", + "error_archiving": "Impossibile archiviare l'email", + "error_updating": "Impossibile aggiornare l'email", + "email_starred": "Stella aggiunta al messaggio", + "email_unstarred": "Stella rimossa dal messaggio", + "email_marked_read": "Messaggio segnato come letto", + "email_marked_unread": "Messaggio segnato come non letto", + "copied_to_clipboard": "Copiato negli appunti", + "source_copied": "Sorgente copiata negli appunti", + "error_sending": "Impossibile inviare il messaggio", + "error_deleting": "Impossibile eliminare il messaggio", + "error_loading": "Impossibile caricare i messaggi", + "new_email": "Nuovo messaggio", + "new_email_from": "Da {sender}", + "click_to_view": "Clicca per visualizzare", + "email_moved": "Messaggio spostato", + "emails_moved": "{count} messaggi spostati", + "moved_to_mailbox": "Spostato in {mailbox}", + "move_failed": "Spostamento non riuscito", + "removed_from_folder": "Rimosso da {mailbox}", + "remove_from_folder_failed": "Impossibile rimuovere da questa cartella", + "emails_copied": "{count} messaggi copiati", + "copy_failed": "Copia non riuscita", + "move_error": "Impossibile spostare i messaggi nella cartella selezionata", + "email_tagged": "Email etichettata", + "emails_tagged": "{count} email etichettate", + "tag_failed": "Etichettatura fallita", + "identity_created": "Identità creata con successo", + "identity_updated": "Identità aggiornata con successo", + "identity_deleted": "Identità eliminata", + "identity_set_primary": "Identità principale aggiornata", + "identity_create_failed": "Impossibile creare l'identità: {error}", + "identity_update_failed": "Impossibile aggiornare l'identità: {error}", + "identity_delete_failed": "Impossibile eliminare l'identità: {error}", + "identity_unauthorized": "Non sei autorizzato a inviare da questo indirizzo email", + "identity_not_found": "Identità non trovata", + "vacation_saved": "Impostazioni di assenza salvate", + "vacation_save_failed": "Impossibile salvare le impostazioni di assenza", + "filters_saved": "Filtri salvati con successo", + "filters_save_failed": "Impossibile salvare i filtri", + "filters_deleted": "Regola di filtraggio eliminata", + "templates_exported": "Modelli esportati con successo", + "templates_imported": "{count, plural, one {# modello importato} other {# modelli importati}}", + "templates_import_errors": "Alcuni modelli non sono stati importati", + "templates_import_empty": "Nessun modello trovato nel file", + "export_email_error": "Impossibile esportare l'e-mail", + "import_email_success": "E-mail importata con successo", + "import_email_error": "Impossibile importare l'e-mail", + "rule_created": "Regola creata", + "rule_merged": "Aggiunto alla regola «{name}»", + "rule_already_covered": "Già coperto dalla regola «{name}»", + "rule_apply_existing": "{count, plural, one {Applica a # messaggio esistente} other {Applica a # messaggi esistenti}}", + "rule_applied": "{count, plural, =0 {Nessun messaggio da modificare} one {Regola applicata a # messaggio} other {Regola applicata a # messaggi}}", + "rule_apply_failed": "Impossibile applicare la regola ai messaggi esistenti", + "rule_edit": "Modifica regola", + "rule_undo": "Annulla", + "rule_undone": "Modifica della regola annullata", + "rule_undo_failed": "Impossibile annullare la modifica della regola", + "rule_undo_conflict": "I tuoi filtri sono cambiati nel frattempo. Annulla la modifica nelle impostazioni dei filtri.", + "rule_folder_failed": "Impossibile creare la cartella", + "vacation_filters_save_failed": "Impostazioni di assenza salvate, ma non è stato possibile salvare l'inoltro e i destinatari della risposta" + }, + "date": { + "today": "Oggi", + "yesterday": "Ieri", + "this_week": "Questa settimana", + "last_week": "Settimana scorsa", + "this_month": "Questo mese", + "older": "Meno recenti", + "just_now": "Proprio ora", + "minutes_ago": "{count} minuto fa", + "minutes_ago_plural": "{count} minuti fa", + "hours_ago": "{count} ora fa", + "hours_ago_plural": "{count} ore fa", + "days_ago": "{count} giorno fa", + "days_ago_plural": "{count} giorni fa" + }, + "language": { + "title": "Lingua", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Seleziona lingua", + "switch_to_english": "Passa all'inglese", + "switch_to_french": "Passa al francese", + "switch_to_japanese": "Passa al giapponese", + "switch_to_spanish": "Passa allo spagnolo", + "switch_to_italian": "Passa all'italiano", + "switch_to_german": "Passa al tedesco", + "switch_to_dutch": "Passa all'olandese", + "switch_to_portuguese": "Passa al portoghese", + "switch_to_russian": "Passa al russo", + "switching": "Cambio lingua in corso...", + "switch": "Cambia lingua", + "current": "Lingua corrente", + "polish": "Polski", + "switch_to_polish": "Passa al polacco", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Passa al persiano", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Passa al mongolo" + }, + "settings": { + "title": "Impostazioni", + "back_to_mail": "Torna alla posta", + "save_success": "Impostazioni salvate con successo", + "import_success": "Impostazioni importate con successo", + "import_error": "Impossibile importare le impostazioni", + "reset_confirm": "Sei sicuro di voler ripristinare tutte le impostazioni ai valori predefiniti?", + "unsaved_changes": "Hai modifiche non salvate", + "discard_changes": "Annullare le modifiche?", + "discard": "Annulla", + "keep_editing": "Continua a modificare", + "tabs": { + "appearance": "Aspetto", + "language": "Lingua, regione e ora", + "email": "Comportamento email", + "composer": "Editor", + "privacy": "Privacy e sicurezza", + "account": "Account", + "identities": "Identità", + "vacation": "Assenza", + "advanced": "Avanzate", + "calendar": "Calendario", + "filters": "Filtri", + "templates": "Modelli", + "folders": "Cartelle", + "keywords": "Etichette", + "security": "Sicurezza", + "encryption": "Cifratura", + "files": "File", + "contacts": "Contatti", + "protocol_handlers": "App predefinite", + "sidebar_apps": "App nella barra laterale", + "notifications": "Notifiche", + "layout": "Layout", + "themes": "Temi", + "reading": "Lettura", + "composing": "Composizione", + "downloads": "Download", + "content_senders": "Contenuto e mittenti", + "about_data": "Informazioni e dati", + "debug": "Debug" + }, + "themes": { + "title": "Temi", + "description": "Scegli tra i temi distribuiti dal tuo amministratore e i preset integrati.", + "locked": "La selezione del tema è bloccata da un amministratore.", + "forced": "Il tema “{name}” è imposto dall'amministratore e non può essere modificato", + "admin_theme": "Tema dell'amministratore", + "activated": "Tema attivato", + "default_restored": "Tema predefinito ripristinato", + "default_name": "Predefinito", + "default_badge": "Predefinito", + "admin_enforced": "Imposto dall'amministratore" + }, + "tab_groups": { + "general": "Generale", + "account": "Account e identità", + "organization": "Organizzazione e-mail", + "apps": "Applicazioni", + "system": "Sistema", + "appearance": "Aspetto", + "mail": "Posta", + "privacy": "Privacy e sicurezza", + "advanced": "Avanzate" + }, + "appearance": { + "message_list_order": { + "label": "Ordine dell'elenco messaggi", + "description": "Come vengono ordinati i messaggi nell'elenco. L'ordinamento è eseguito dal server, quindi riguarda l'intera cartella e non solo i messaggi caricati.", + "preset": { + "chronological": "Cronologico (più recenti prima)", + "unread_first": "Non letti prima", + "starred_first": "Speciali prima", + "tagged_first": "Con tag prima", + "custom": "Personalizzato" + }, + "tag_label": "Tag", + "advanced_show": "Avanzate: modifica i livelli di ordinamento", + "advanced_hide": "Nascondi avanzate", + "criterion_label": "Criterio del livello {n}", + "direction_label": "Direzione del livello {n}", + "criteria": { + "unread": "Non letto", + "starred": "Speciale", + "tag": "Tag", + "receivedAt": "Data di ricezione", + "sentAt": "Data di invio", + "from": "Mittente", + "subject": "Oggetto", + "size": "Dimensione" + }, + "direction": { + "unread_first": "Non letti prima", + "read_first": "Letti prima", + "starred_first": "Speciali prima", + "unstarred_first": "Non speciali prima", + "tagged_first": "Con tag prima", + "untagged_first": "Senza tag prima", + "newest_first": "Più recenti prima", + "oldest_first": "Più vecchi prima", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Più grandi prima", + "smallest_first": "Più piccoli prima" + }, + "add_level": "Aggiungi livello", + "remove_level": "Rimuovi livello", + "move_up": "Sposta su", + "move_down": "Sposta giù", + "tiebreak_note": "Fino a tre livelli; i livelli successivi risolvono i pareggi. «Più recenti prima» viene sempre applicato per ultimo.", + "scope": { + "label": "Applica a tutte le cartelle", + "description": "Se disattivato, l'ordine vale solo per la Posta in arrivo; le altre cartelle restano cronologiche." + }, + "unsupported_note": "Il tuo server di posta non supporta l'ordinamento per stato di lettura, speciali o tag. Questi livelli vengono ignorati e l'elenco resta cronologico." + }, + "title": "Aspetto", + "description": "Personalizza l'aspetto della tua webmail", + "theme": { + "label": "Tema", + "description": "Scegli la tua combinazione di colori preferita", + "light": "Chiaro", + "dark": "Scuro", + "system": "Sistema" + }, + "language": { + "label": "Lingua", + "description": "Scegli la tua lingua preferita" + }, + "font_size": { + "label": "Dimensione carattere", + "description": "Regola la dimensione del testo per una migliore leggibilità", + "small": "Piccolo", + "medium": "Medio", + "large": "Grande" + }, + "list_density": { + "label": "Densità", + "description": "Controlla la spaziatura in tutta l'interfaccia", + "extra_compact": "Molto compatto", + "compact": "Compatto", + "regular": "Normale", + "comfortable": "Comodo" + }, + "interface_layout": { + "label": "Layout dell'interfaccia", + "description": "Dove si trovano ricerca, navigazione e barre degli strumenti", + "default": "Predefinito", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Abilita animazioni", + "description": "Mostra transizioni ed effetti fluidi" + }, + "toolbar_position": { + "label": "Posizione barra degli strumenti", + "description": "Dove mostrare i pulsanti azione e-mail (Rispondi, Archivia, Elimina, ecc.)", + "top": "In alto", + "below_subject": "Sotto l'oggetto" + }, + "toolbar_labels": { + "label": "Mostra etichette della barra degli strumenti", + "description": "Mostra le etichette di testo accanto alle icone della barra degli strumenti. Disattivare per risparmiare spazio una volta acquisita familiarità con le icone." + }, + "hide_account_switcher": { + "label": "Nascondi selettore account dalla barra laterale", + "description": "Nascondi il selettore account nella parte superiore della barra laterale delle cartelle. Puoi comunque cambiare account dalla barra di navigazione inferiore." + }, + "show_rail_account_list": { + "label": "Mostra avatar account nella barra di navigazione", + "description": "Visualizza i cerchi degli account individuali nella parte inferiore della barra di navigazione per un cambio rapido, con un pulsante di disconnessione sotto." + }, + "unified_mailbox": { + "label": "Casella di posta unificata", + "description": "Mostra le cartelle combinate (Posta in arrivo, Inviati, ecc.) di tutti gli account collegati", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Includi le caselle di gruppo", + "description": "Includi anche le caselle condivise o di gruppo nella vista unificata." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Icone colorate nella barra laterale", + "description": "Colora le icone di cartelle ed etichette per tipo (Posta in arrivo blu, Spam rosso, Inviati verde, ecc.). Disattiva per una barra laterale monocromatica." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Colora le righe in base all'account", + "description": "Nella vista unificata, colora ogni riga con il colore del suo account invece di mostrare il pallino. Ha la precedenza sulla tinta del tag; i tag mantengono il loro colore sulle etichette." + }, + "show_folder_total_count": { + "label": "Mostra il numero totale di messaggi", + "description": "Mostra il numero totale di messaggi accanto a cartelle ed etichette, insieme al conteggio dei non letti. Disattiva per mostrare solo i non letti." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Mostra account di posta condivisi", + "description": "Mostra le cartelle delle caselle condivise con te sotto Condiviso nella barra laterale. Disattiva per nasconderle tutte, oppure nascondi singoli account qui sotto. Calendari e contatti restano visibili." + }, + "pro_interface": { + "label": "Interfaccia Pro (sperimentale)", + "description": "Layout per utenti esperti solo desktop con esplorazione messaggi a più schede e flussi tra account. L'interfaccia standard non è influenzata; puoi tornare indietro in qualsiasi momento.", + "open_label": "Apri interfaccia Pro", + "back_to_standard": "Torna allo standard" + }, + "cross_unread": { + "label": "Tutti gli account: Non letti", + "description": "Mostra una voce nella sezione Tutti gli account che elenca la posta non letta di tutti gli account, in tutte le cartelle tranne posta indesiderata, inviata, archivio, cestino e bozze." + }, + "cross_starred": { + "label": "Tutti gli account: Speciali", + "description": "Mostra una voce nella sezione Tutti gli account che elenca la posta speciale di tutti gli account, in tutte le cartelle tranne posta indesiderata, inviata, archivio, cestino e bozze." + }, + "cross_all": { + "label": "Tutti gli account: Tutta la posta", + "description": "Mostra una voce nella sezione Tutti gli account che elenca tutta la posta di tutti gli account, in tutte le cartelle tranne posta indesiderata, inviata, archivio, cestino e bozze." + } + }, + "keywords": { + "title": "Etichette e-mail", + "description": "Definisci etichette per organizzare le tue e-mail con i colori. Vengono archiviate come parole chiave JMAP sul server.", + "add_keyword": "Aggiungi etichetta", + "label_field": "Nome visualizzato", + "label_placeholder": "es. Lavoro, Personale, Urgente", + "id_field": "ID etichetta", + "id_placeholder": "es. lavoro, personale", + "color_field": "Colore", + "id_exists": "Questo ID etichetta esiste già", + "edit": "Modifica etichetta", + "delete": "Elimina etichetta", + "save": "Salva", + "add": "Aggiungi", + "cancel": "Annulla", + "migrating": "Aggiornamento dell'etichetta nelle e-mail esistenti…", + "migration_error": "Impossibile aggiornare l'etichetta nelle e-mail esistenti", + "filters_migration_error": "Alcune regole dei filtri usano ancora il vecchio tag: aggiornale manualmente", + "nesting": { + "label": "Etichette nidificate", + "description": "Nidifica le etichette sotto altre etichette e mostrale come un albero nella barra laterale." + }, + "parent_field": "Etichetta principale", + "no_parent": "Nessuna etichetta principale", + "too_long": "Questo percorso di etichetta è troppo lungo (al massimo {max} caratteri)", + "has_children_locked": "Altre etichette sono nidificate sotto questa, quindi il suo nome e la sua etichetta principale sono bloccati. Spostale o rimuovile prima.", + "has_children_delete": "Rimuovi prima le etichette nidificate sotto questa", + "visibility_field": "Visibilità nella barra laterale", + "visibility": { + "show": "Mostra", + "unread": "Mostra se non letti", + "hide": "Nascondi" + }, + "unrecognized": { + "title": "Recupera le etichette dal server", + "description": "I nomi e i colori delle etichette sono salvati in questo browser, ma le etichette vere e proprie si trovano sulle tue e-mail come parole chiave JMAP. Analizza la posta alla ricerca di parole chiave che nessuna etichetta qui spiega, poi aggiungile di nuovo.", + "scan": "Cerca etichette", + "rescan": "Analizza di nuovo", + "scanning": "Analisi in corso… {scanned} di {total} e-mail", + "none": "Tutte le etichette presenti sulle tue e-mail sono già definite qui.", + "messages": "{count, plural, one {# e-mail} other {# e-mail}}", + "add_all": "Aggiungi tutte ({count})", + "partial": "Sono state analizzate solo le {scanned} e-mail più recenti su {total}. Le etichette usate solo su messaggi più vecchi potrebbero mancare.", + "error": "Impossibile analizzare la posta alla ricerca di etichette." + } + }, + "notifications": { + "test_sound": "Testa il suono di notifica", + "sounds": { + "default": "Predefinito (Bip)", + "cheerful": "Allegro", + "involved": "Elaborato", + "swift": "Gesto veloce", + "relax": "Rilassante" + }, + "push_all": { + "title": "Tutti gli account", + "description": "Attiva o disattiva le notifiche per più account in una volta sola. Una sola iscrizione del browser li serve tutti.", + "select_all": "Seleziona tutti gli account collegati", + "state_on": "notifiche attive", + "state_off": "notifiche non attive", + "enable_selected": "Attiva per i selezionati", + "disable_selected": "Disattiva per i selezionati", + "result_enabled": "Notifiche attivate per {count} account.", + "result_partial": "Attivate per {enabled} account; {failed} non è stato possibile attivarle.", + "result_disabled": "Notifiche disattivate per {count} account." + }, + "sound_selection": { + "title": "Suono di notifica", + "description": "Scegli quale suono riprodurre per le notifiche", + "choose": "Suono", + "choose_desc": "Seleziona un tono di notifica e clicca sull'icona dell'altoparlante per l'anteprima" + }, + "email": { + "title": "Notifiche e-mail", + "description": "Configura le notifiche per le e-mail in arrivo", + "enabled": "Notifiche e-mail", + "enabled_desc": "Mostra notifiche all'arrivo di nuove e-mail", + "sound": "Suono di notifica", + "sound_desc": "Riproduci un suono all'arrivo di nuove e-mail", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Notifiche calendario", + "description": "Configura le notifiche per gli eventi del calendario", + "enabled": "Notifiche eventi", + "enabled_desc": "Mostra avvisi per i prossimi eventi del calendario", + "sound": "Suono di notifica", + "sound_desc": "Riproduci un suono per i promemoria del calendario", + "invitation_parsing": "Analizza inviti via e-mail", + "invitation_parsing_desc": "Rileva inviti calendario negli allegati e mostra azioni calendario" + }, + "push": { + "confirm_disable_message": "Questo dispositivo non riceverà più avvisi quando il sito è chiuso.", + "confirm_disable_title": "Disabilitare le notifiche in background?", + "description": "Ricevi notifiche di sistema per la nuova posta quando questo sito è chiuso. Consegnato tramite il relay push Bulwark; il relay non vede mai il contenuto della posta.", + "disable": "Disabilita", + "enable": "Abilita", + "dismiss_aria": "Chiudi avviso di notifiche", + "ios_hint": "Su iOS, installa prima il sito sulla schermata Home: Safari consegna Web Push solo alle PWA installate.", + "reenable": "Registra di nuovo", + "relay_desc": "Per impostazione predefinita usa il relay Bulwark ospitato. Cambialo solo se ospiti in autonomia.", + "relay_label": "Relay push", + "relay_locked": "Impostato dall'amministratore", + "relay_locked_desc": "L'URL del relay push è stato impostato dall'amministratore e non può essere modificato.", + "status_active": "Attivo su questo dispositivo", + "status_busy": "In elaborazione…", + "status_inactive": "Non abilitato su questo dispositivo", + "status_unsupported": "Questo browser non supporta Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Notifiche in background", + "devices_title": "Dispositivi registrati", + "devices_desc": "Tutti i dispositivi registrati per ricevere le notifiche in background di questo account. Revocandone uno, smette subito di riceverle.", + "devices_loading": "Caricamento dei dispositivi registrati…", + "devices_empty": "Nessun dispositivo è registrato per le notifiche in background.", + "devices_error": "Impossibile caricare i dispositivi registrati.", + "devices_refresh": "Aggiorna", + "device_this": "Questo dispositivo", + "device_other": "Altro dispositivo", + "device_status_active": "Consegna le notifiche", + "device_status_inactive": "Non ha mai consegnato una notifica", + "device_status_unknown": "Sconosciuto a questo relay", + "device_expires": "Scade il {date}", + "revoke": "Revoca", + "confirm_revoke_title": "Revocare questo dispositivo?", + "confirm_revoke_message": "Smetterà subito di ricevere le notifiche in background. Riattivando il push su quel dispositivo verrà registrato di nuovo.", + "confirm_revoke_message_this": "Questo dispositivo smetterà subito di ricevere le notifiche in background." + } + }, + "language_region": { + "title": "Lingua, regione e ora", + "description": "Lingua, formato data, formato ora e altre preferenze regionali", + "language": { + "label": "Lingua", + "description": "Scegli la tua lingua preferita", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Formato data", + "description": "Come vengono visualizzate le date nell'elenco delle e-mail", + "smart": "Intelligente", + "relative": "Relativo", + "full": "Sempre data completa", + "preview_today": "Oggi:", + "preview_this_week": "Questa settimana:", + "preview_older": "Più vecchio:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Formato ora", + "description": "Scegli tra formato 12 o 24 ore", + "12h": "12 ore", + "24h": "24 ore" + }, + "first_day": { + "label": "Primo giorno della settimana", + "description": "Inizia la settimana di domenica o lunedì", + "sunday": "Domenica", + "monday": "Lunedì" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Cancella la ricerca quando cambi cartella", + "description": "Facendo clic su una cartella (Posta in arrivo, Inviati…) la ricerca attiva viene cancellata e si apre quella cartella. Se disattivato, la ricerca resta attiva e viene rieseguita nella cartella scelta." + }, + "request_read_receipt": { + "label": "Richiedi conferme di lettura per impostazione predefinita", + "description": "Attiva in anticipo la richiesta di conferma di lettura durante la composizione di un nuovo messaggio." + }, + "read_receipt_response": { + "label": "Rispondi alle richieste di conferma di lettura", + "description": "Cosa fare quando un messaggio in arrivo richiede una conferma di lettura.", + "ask": "Chiedi ogni volta", + "always": "Invia sempre", + "never": "Non inviare mai" + }, + "title": "Comportamento email", + "description": "Configura come vengono gestiti i messaggi", + "mark_read": { + "label": "Segna come letto", + "description": "Quando segnare i messaggi come letti all'apertura", + "instant": "Istantaneamente", + "delay_3s": "Dopo 3 secondi", + "delay_5s": "Dopo 5 secondi", + "never": "Mai" + }, + "delete_action": { + "label": "Azione di eliminazione", + "description": "Cosa accade quando elimini un messaggio", + "trash": "Sposta nel cestino", + "trash_and_read": "Sposta nel cestino e segna come letto", + "permanent": "Elimina definitivamente", + "warning": "I messaggi verranno eliminati definitivamente e non potranno essere recuperati. Questa azione è irreversibile." + }, + "message_spacing": { + "label": "Spaziatura del messaggio", + "description": "Margine attorno al corpo del messaggio nel lettore", + "auto": "Automatica (consigliata)", + "always": "Aggiungi sempre spaziatura", + "edge": "Da bordo a bordo" + }, + "plain_text_font": { + "label": "Carattere del testo semplice", + "description": "Carattere usato per le email in testo semplice", + "mono": "Monospazio", + "sans": "Normale (come l'app)" + }, + "archive_mode": { + "label": "Archivia in", + "description": "Come organizzare le email durante l'archiviazione", + "single": "Una singola cartella", + "year": "Una cartella per anno", + "month": "Una cartella per mese", + "reorganize": "Riorganizza archivio esistente", + "reorganize_success": "{count, plural, =0 {Nessuna email da riorganizzare} =1 {1 email riorganizzata} other {# email riorganizzate}}", + "reorganize_error": "Impossibile riorganizzare l'archivio" + }, + "permanently_delete_junk": { + "label": "Elimina spam definitivamente", + "description": "Elimina definitivamente i messaggi dalla cartella Spam invece di spostarli nel cestino" + }, + "confirm_bulk_delete": { + "label": "Conferma prima di eliminare più messaggi", + "description": "Chiedi prima di spostare più messaggi selezionati nel cestino. L'eliminazione definitiva chiede sempre." + }, + "mail_layout": { + "label": "Layout della posta", + "description": "Scegli tra il classico pannello di lettura diviso e un flusso di lettura focalizzato in stile Gmail.", + "split": "Pannello diviso", + "split_description": "Mantieni la lista dei messaggi e il pannello di lettura visibili affiancati.", + "focus": "Lista focalizzata", + "focus_description": "Mostra una riga per messaggio e apri la posta a larghezza piena mantenendo visibile la barra laterale delle cartelle.", + "horizontal": "Riquadro di lettura in basso", + "horizontal_description": "Mostra l'elenco dei messaggi in alto e apri il messaggio selezionato in un riquadro di lettura sotto." + }, + "list_columns": { + "label": "Mostra l'elenco in colonne", + "description": "Mostra le righe su una sola riga come tabella con le colonne Da, Oggetto, Data e Dimensione sotto un'intestazione." + }, + "disable_threading": { + "label": "Disabilita raggruppamento conversazioni", + "description": "Mostra le email come messaggi singoli anziché raggruppati per conversazione" + }, + "plain_text_mode": { + "label": "Solo testo normale", + "description": "Disabilita l'editor di testo formattato e invia tutte le email solo come testo normale, incluse risposte e inoltri" + }, + "auto_select_reply_identity": { + "label": "Seleziona automaticamente l'indirizzo di risposta", + "description": "Quando rispondi, passa automaticamente l'indirizzo mittente all'identità che ha ricevuto il messaggio originale" + }, + "reply_identity_match": { + "label": "Corrispondenza dell'indirizzo di ricezione", + "description": "Quali indirizzi di ricezione contano come tuoi. «Solo indirizzo esatto» sceglie soltanto una delle tue identità configurate; «stesso dominio» tratta anche qualsiasi altro indirizzo dei tuoi domini di identità come alias catch-all e riscrive con esso l'intestazione Da. Scegli l'indirizzo esatto se quegli indirizzi sono liste di distribuzione.", + "exact": "Solo indirizzo esatto", + "domain": "Qualsiasi indirizzo dei miei domini" + }, + "signature_position": { + "label": "Posizione della firma", + "description": "Dove inserire la tua firma nelle risposte e negli inoltri. Sopra il testo citato si legge in modo naturale come chiusura della risposta; sotto mantiene il messaggio originale contiguo.", + "above_quote": "Prima del testo citato", + "below_quote": "Dopo il testo citato" + }, + "signature_separator": { + "label": "Delimitatore firma", + "description": "Anteporre alla firma la riga di delimitazione standard \"-- \" (RFC 3676). Disattiva se preferisci passare direttamente dal messaggio alla firma." + }, + "sub_address_delimiter": { + "label": "Delimitatore sub-indirizzo", + "description": "Carattere che separa il tuo nome utente dall'etichetta del sub-indirizzo. Usa il delimitatore configurato sul tuo server di posta (es. utente{delimiter}tag@dominio.com).", + "option": "{delimiter} (utente{delimiter}tag@dominio.com)", + "custom": "Personalizzato…", + "custom_input_label": "Carattere delimitatore personalizzato" + }, + "show_preview": { + "label": "Mostra anteprima testo", + "description": "Visualizza l'anteprima del messaggio nell'elenco", + "focus_description": "Visualizza il testo di anteprima in linea nella lista di messaggi focalizzata a riga singola" + }, + "show_verification_codes": { + "label": "Mostra codici di verifica", + "description": "Offri il codice delle email di accesso e conferma da copiare con un clic, nell'elenco per un giorno e nel messaggio" + }, + "attachment_click_action": { + "label": "Azione al clic sugli allegati", + "description": "Scegli se facendo clic su un allegato si apre un'anteprima o si avvia subito il download", + "preview": "Anteprima quando possibile", + "download": "Scarica immediatamente" + }, + "attachment_position": { + "label": "Posizione degli allegati", + "description": "Dove visualizzare gli allegati nell'intestazione dell'email", + "beside-sender": "Accanto al mittente", + "below-header": "Sotto l'intestazione" + }, + "emails_per_page": { + "10": "10 messaggi", + "25": "25 messaggi", + "50": "50 messaggi", + "100": "100 messaggi", + "label": "Messaggi per pagina", + "description": "Numero di messaggi da caricare alla volta" + }, + "always_light_mode": { + "label": "Mostra sempre le email in modalità chiara", + "description": "Visualizza il contenuto delle email in modalità chiara anche quando l'app è in modalità scura, evitando problemi di conversione" + }, + "external_content": { + "label": "Contenuti esterni", + "description": "Come gestire immagini e contenuti esterni", + "ask": "Chiedi sempre", + "block": "Blocca sempre", + "allow": "Consenti sempre" + }, + "trusted_senders": { + "label": "Mittenti attendibili", + "description": "Gestisci i mittenti le cui immagini si caricano automaticamente", + "count_zero": "Nessuno", + "count_one": "1 mittente", + "count_other": "{count} mittenti", + "modal_title": "Mittenti attendibili", + "empty_title": "Nessun mittente attendibile ancora", + "empty_description": "Durante la visualizzazione di un messaggio con immagini bloccate, clicca \"Considera sempre attendibile questo mittente\" per aggiungerlo qui.", + "add_manually": "Aggiungi mittente manualmente", + "add_button": "Aggiungi", + "add_placeholder": "Inserisci indirizzo email", + "search_placeholder": "Cerca mittenti...", + "no_results": "Nessun mittente corrisponde alla tua ricerca", + "remove": "Rimuovi", + "close": "Chiudi", + "invalid_email": "Inserisci un indirizzo email valido", + "already_added": "Questo mittente è già attendibile", + "save_error": "Salvataggio non riuscito - controlla il log di debug Contatti per i dettagli", + "use_address_book_label": "Sincronizza con la rubrica", + "use_address_book_description": "Salva i mittenti attendibili in una rubrica \"Mittenti attendibili\" dedicata per sincronizzarli su tutti i tuoi dispositivi" + }, + "hover_actions": { + "label": "Azioni rapide al passaggio del mouse", + "description": "Scegli quali azioni rapide appaiono al passaggio del mouse su un'email nella lista", + "delete": "Elimina", + "star": "Segna / Rimuovi stella", + "mark_read": "Segna come letto / non letto", + "archive": "Archivia", + "tag": "Etichetta", + "spam": "Segna come spam", + "not_spam": "Non spam", + "none_selected": "Nessuna azione selezionata", + "mode_label": "Modalità di visualizzazione", + "mode_inline": "In linea", + "mode_floating": "Fluttuante", + "corner_label": "Posizione fluttuante", + "corner_top-left": "In alto a sinistra", + "corner_top-right": "In alto a destra", + "corner_bottom-left": "In basso a sinistra", + "corner_bottom-right": "In basso a destra" + }, + "default_mail_program": { + "label": "Programma di posta predefinito", + "description": "Registra {appName} come programma di posta predefinito per i link mailto:", + "button": "Imposta come predefinito", + "success": "Il browser ha chiesto di impostare come predefinito", + "error": "Il tuo browser non supporta questa funzionalità" + }, + "attachment_reminder": { + "label": "Promemoria allegati", + "description": "Avvisa prima dell'invio quando il messaggio menziona allegati ma nessuno è allegato", + "keywords_label": "Parole chiave di attivazione", + "keywords_description": "Parole o frasi che attivano il promemoria quando trovate nel messaggio", + "add_placeholder": "Aggiungi parola chiave...", + "add": "Add", + "remove": "Rimuovi" + }, + "empty_subject_warning": { + "label": "Avviso oggetto vuoto", + "description": "Chiedi conferma prima di inviare un messaggio senza oggetto." + }, + "recipient_mentions": { + "label": "Menziona i destinatari con @", + "description": "Digitando @ nel messaggio vengono proposti i destinatari e viene inserito il nome di quello scelto. Invio o Tab seleziona il nome evidenziato." + }, + "mention_adds_recipient": { + "label": "Contatti nelle menzioni con @", + "description": "Digitando @ vengono proposti anche i tuoi contatti e le persone della rubrica aziendale. Se scegli qualcuno che non è ancora destinatario, viene aggiunto al messaggio.", + "to": "Aggiungi in A", + "cc": "Aggiungi in CC", + "off": "Solo destinatari" + }, + "hide_inline_image_attachments": { + "label": "Nascondi le immagini inline dagli allegati", + "description": "Le immagini incorporate nel corpo del messaggio non vengono elencate come allegati separati" + }, + "attachment_image_previews": { + "label": "Mostra anteprime delle immagini negli allegati", + "description": "Mostra gli allegati immagine come miniature anziché con icone di file generiche" + }, + "send_delay": { + "label": "Annulla invio / ritardo di invio", + "description": "Ritarda gli invii normali con una breve finestra lato server.", + "off": "Disattivato", + "seconds": "{seconds} secondi", + "unsupported": "L’account corrente non dichiara il supporto all’invio ritardato. L’impostazione resta salvata per altri account." + }, + "return_to_list_after_action": { + "label": "Torna all'elenco dopo l'eliminazione o il contrassegno come non letto", + "description": "Dopo aver eliminato o segnato come non letto il messaggio aperto, torna all'elenco dei messaggi invece di aprire il messaggio successivo." + }, + "swipe_right_action": { + "label": "Azione per lo scorrimento a destra (mobile)", + "description": "Sui dispositivi touch, l'azione eseguita quando scorri un messaggio verso destra nell'elenco." + }, + "swipe_left_action": { + "label": "Azione per lo scorrimento a sinistra (mobile)", + "description": "Sui dispositivi touch, l'azione eseguita quando scorri un messaggio verso sinistra nell'elenco." + }, + "swipe_actions": { + "none": "Nessuna", + "archive": "Archivia", + "delete": "Elimina", + "mark_read": "Alterna letto", + "star": "Alterna stella", + "spam": "Segna come spam" + }, + "rtl_editing": { + "label": "Supporto alla scrittura da destra a sinistra", + "description": "Aggiunge un pulsante di direzione alla barra degli strumenti dell'editor per impostare i paragrafi da sinistra a destra o da destra a sinistra" + } + }, + "composer": { + "title": "Editor", + "description": "Configura le impostazioni di composizione email", + "autosave": { + "label": "Intervallo di salvataggio automatico", + "description": "Con quale frequenza salvare automaticamente le bozze", + "30s": "Ogni 30 secondi", + "1m": "Ogni minuto", + "2m": "Ogni 2 minuti", + "5m": "Ogni 5 minuti" + }, + "send_confirmation": { + "label": "Conferma invio", + "description": "Chiedi conferma prima di inviare messaggi" + }, + "default_reply": { + "label": "Modalità risposta predefinita", + "description": "Azione predefinita quando si fa clic su rispondi", + "reply": "Rispondi", + "reply_all": "Rispondi a tutti" + } + }, + "privacy": { + "title": "Privacy e sicurezza", + "description": "Gestisci le tue impostazioni di privacy e sicurezza", + "external_images": { + "label": "Blocca immagini esterne", + "description": "Impedisci il tracciamento attraverso immagini esterne" + }, + "session_timeout": { + "label": "Timeout sessione", + "description": "Disconnetti automaticamente dopo inattività", + "never": "Mai", + "30m": "30 minuti", + "1h": "1 ora", + "4h": "4 ore" + }, + "clear_cache": { + "label": "Cancella cache", + "description": "Rimuovi dati memorizzati nella cache e file temporanei", + "button": "Cancella cache", + "confirm": "Sei sicuro di voler cancellare la cache?", + "success": "Cache cancellata con successo" + } + }, + "account": { + "title": "Account", + "description": "Visualizza le informazioni del tuo account", + "name_label": "Nome visualizzato", + "username_label": "Nome utente", + "account_type_label": "Tipo di account", + "auth_method_label": "Autenticazione", + "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", + "auth_method_basic": "Password", + "auth_method_token": "Token di accesso", + "demo_account": "Account dimostrativo", + "email": { + "label": "Indirizzo email", + "value": "{email}" + }, + "server": { + "label": "Server JMAP", + "value": "{server}" + }, + "storage": { + "label": "Utilizzo spazio", + "used": "{used} di {total} utilizzati", + "percentage": "{percent}% utilizzato" + }, + "last_sync": { + "label": "Ultima sincronizzazione", + "value": "{time}" + }, + "accounts": { + "title": "Account connessi", + "description": "Trascina per riordinare la visualizzazione degli account nel menu a discesa", + "active": "Account attualmente attivo", + "default_badge": "Account predefinito", + "set_default": "Imposta come predefinito", + "switch_to": "Passa a questo account", + "move_up": "Sposta su", + "move_down": "Sposta giù", + "drag_handle": "Trascina per riordinare", + "add": "Aggiungi account" + }, + "backup": { + "title": "Backup dell'elenco account", + "description": "Conserva sul server l'elenco degli account di questo browser, cifrato con una password che conosci solo tu, e ripristinalo su un altro dispositivo. Le password delle caselle non vengono salvate: accedi tu a ogni account ripristinato.", + "description_passwords": "Conserva sul server l'elenco degli account di questo browser, cifrato con una password che conosci solo tu, e ripristinalo su un altro dispositivo. Puoi includere le password delle caselle, così gli account ripristinati accedono da soli.", + "status_none": "Nessun elenco salvato per {account}.", + "status_saved": "C'è un elenco salvato per {account}.", + "save": "Salva elenco", + "restore": "Ripristina elenco", + "delete": "Elimina elenco salvato", + "cancel": "Annulla", + "delete_confirm": "Eliminare dal server l'elenco account salvato?", + "password": "Password del backup", + "password_hint": "Almeno 10 caratteri. Non si può recuperare: se la dimentichi, salva di nuovo l'elenco con una nuova password.", + "restore_hint": "La password scelta quando l'elenco è stato salvato.", + "confirm": "Conferma password", + "include_passwords": "Includi le password delle caselle", + "include_passwords_hint": "Gli account collegati qui con una password accederanno da soli dopo il ripristino. Chi conosce la password del backup entra in tutte le caselle dell'elenco.", + "saved": "Elenco salvato ({count, plural, one {# account} other {# account}}).", + "restored": "Account aggiunti: {added}. Account già presenti e aggiornati: {updated}. Apri ogni nuovo account dall'elenco account per accedere.", + "restored_signed_in": "Account aggiunti: {added}. Account già presenti e aggiornati: {updated}.", + "skipped": "{count, plural, one {# account non è stato aggiunto} other {# account non sono stati aggiunti}}: è stato raggiunto il limite di account.", + "saved_passwords": "Password incluse per {count, plural, one {# account} other {# account}}.", + "signed_in": "Accesso fatto a {count, plural, one {# account} other {# account}} con la password salvata.", + "sign_in_failed": "{count, plural, one {# password salvata è stata rifiutata} other {# password salvate sono state rifiutate}}: accedi a quegli account dall'elenco account.", + "deleted": "Elenco salvato eliminato.", + "errors": { + "mismatch": "Le password non coincidono.", + "password_length": "Usa almeno 10 caratteri.", + "unlock_failed": "Password errata, oppure l'elenco è stato salvato per un altro account.", + "owner_signin_required": "Accedi di nuovo a questo account, poi riprova.", + "https_required": "Serve una connessione sicura (HTTPS).", + "storage_failed": "Il server non è riuscito a leggere o salvare l'elenco. Riprova più tardi." + } + }, + "shared_accounts": { + "title": "Condivisi con me", + "description": "Account di gruppo e condivisi che puoi gestire. Selezionane uno per modificarne filtri, risponditore automatico, calendari e contatti.", + "shared_label": "Account condiviso" + } + }, + "security": { + "title": "Sicurezza dell'account", + "description": "Gestisci la tua password, l'autenticazione a due fattori e le impostazioni di sicurezza", + "detecting": "Rilevamento delle funzionalità del server...", + "not_available": "La gestione della sicurezza dell'account non è disponibile per questo server di posta. I permessi richiesti potrebbero essere disabilitati. Consultare la documentazione per i dettagli.", + "password": { + "title": "Cambia password", + "current": "Password attuale", + "new": "Nuova password", + "confirm": "Conferma nuova password", + "submit": "Cambia password", + "success": "Password cambiata con successo", + "error_title": "Cambio password fallito", + "error_mismatch": "Le nuove password non corrispondono", + "error_min_length": "La password deve contenere almeno 8 caratteri", + "error_generic": "Impossibile cambiare la password" + }, + "display_name": { + "label": "Nome visualizzato", + "description": "Il tuo nome come appare sul server", + "placeholder": "Inserisci il tuo nome visualizzato", + "save": "Salva", + "success": "Nome visualizzato aggiornato", + "error": "Impossibile aggiornare il nome visualizzato" + }, + "totp": { + "section_title": "Autenticazione a due fattori", + "label": "Autenticazione TOTP", + "description": "Aggiungi un ulteriore livello di sicurezza con una password monouso basata sul tempo", + "active": "Abilitato", + "inactive": "Disabilitato", + "enabled": "Autenticazione a due fattori abilitata", + "disabled": "Autenticazione a due fattori disabilitata", + "enable_error": "Impossibile abilitare la 2FA", + "disable_error": "Impossibile disabilitare la 2FA", + "setup_instructions": "Copia questo URL nella tua app di autenticazione (Google Authenticator, Authy, ecc.):", + "verification_code": "Codice di verifica", + "confirm": "Conferma", + "disable": "Disattiva", + "disable_confirm_prompt": "Inserisci la tua password per disattivare l'autenticazione a due fattori.", + "password_required": "La password è obbligatoria", + "code_required": "Il codice di verifica è obbligatorio", + "code_invalid": "Codice di verifica non valido. Controlla la tua app di autenticazione e riprova." + }, + "app_passwords": { + "title": "Password per le app", + "description": "Crea password per le app che non supportano l'autenticazione a due fattori", + "add": "Aggiungi", + "create": "Crea", + "cancel": "Annulla", + "generate": "Genera", + "name_label": "Nome dell'app", + "name_placeholder": "es. Thunderbird, iPhone Mail", + "password_label": "Password (lascia vuoto per auto-generare)", + "password_placeholder": "Auto-generata se vuoto", + "added": "Password per l'app creata", + "removed": "Password per l'app rimossa", + "add_error": "Impossibile creare la password per l'app", + "remove_error": "Impossibile rimuovere la password per l'app", + "none": "Nessuna password per le app configurata", + "done": "Fatto", + "expires_label": "Scadenza (facoltativa)", + "copy_now_warning": "Copia questa password ora - non verrà più mostrata.", + "allowed_ips_label": "IP consentiti (opzionale)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Separati da virgola o spazio. Lasciare vuoto per consentire qualsiasi IP." + }, + "api_keys": { + "title": "Chiavi API", + "description": "Crea chiavi API per script e integrazioni che comunicano direttamente con il server", + "name_label": "Nome chiave", + "name_placeholder": "es. Script di backup, CI runner", + "copy_now_warning": "Copia subito questa chiave API - non verrà mostrata di nuovo.", + "added": "Chiave API creata", + "removed": "Chiave API rimossa", + "add_error": "Impossibile creare la chiave API", + "remove_error": "Impossibile rimuovere la chiave API", + "none": "Nessuna chiave API configurata" + }, + "encryption": { + "section_title": "Crittografia a riposo", + "label": "Crittografia email", + "description": "Crittografa le email archiviate sul server per una maggiore privacy", + "active": "Crittografia {type} abilitata", + "inactive": "Disabilitato", + "enabled": "Crittografia a riposo abilitata", + "disabled_success": "Crittografia a riposo disabilitata", + "error": "Impossibile aggiornare le impostazioni di crittografia", + "algorithm_label": "Algoritmo di crittografia", + "encrypt_on_append": "Cripta le nuove e-mail al caricamento", + "allow_spam_training": "Consenti l'addestramento antispam prima di crittografare le e-mail", + "enable_encryption": "Abilita la crittografia", + "enable_tooltip": "Abilita la crittografia con questa chiave", + "configure_title": "Abilita la crittografia con questa chiave", + "enable_button": "Abilita", + "disable_tooltip": "Disabilita la crittografia", + "enabled_success": "Crittografia a riposo abilitata" + }, + "email_client": { + "title": "Configurazione client e-mail", + "description": "Usa queste credenziali per configurare il tuo client e-mail desktop o mobile (Thunderbird, Apple Mail, Outlook, ecc.)", + "jmap_username_label": "Nome utente JMAP", + "copy": "Copia", + "copied": "Copiato", + "password_instructions": "Usa il tuo nome utente JMAP sopra indicato insieme a una password dell'app per accedere al tuo client e-mail. Crea una password dell'app nella sezione sopra se non l'hai ancora fatto." + }, + "link_device": { + "title": "Collega l'app mobile", + "description": "Accedi all'app Bulwark Mail sul telefono senza digitarvi la password. Il telefono ottiene un proprio accesso, quindi disconnetterti qui non lo disconnette.", + "generate": "Mostra codice QR", + "regenerate": "Mostra un nuovo codice", + "instructions": "Apri l'app Bulwark Mail e tocca \"Scan a sign-in code\" nella schermata di accesso (oppure \"Add account\" → \"Scan a sign-in code\" se hai già effettuato l'accesso), poi inquadra qui con la fotocamera.", + "expires_in": "Questo codice scade tra {seconds} secondi. Può essere usato una sola volta.", + "expired": "Questo codice è scaduto.", + "error": "Impossibile creare un codice di associazione. Riprova.", + "password_prompt": "Inserisci la password per confermare la tua identità. Il telefono ottiene un proprio accesso; la password non viene inviata al telefono.", + "show_password": "Mostra password", + "hide_password": "Nascondi password", + "continue": "Continua", + "error_wrong_password": "Password errata. Riprova.", + "totp_prompt": "Inserisci il codice dell'app di autenticazione.", + "error_password_or_code": "La password o il codice non sono corretti.", + "error_not_signed_in": "La sessione è scaduta. Accedi di nuovo per collegare un dispositivo.", + "error_impersonation": "Non puoi collegare dispositivi mentre visualizzi la casella di posta di un altro utente.", + "error_too_many_attempts": "Troppi tentativi. Attendi un momento e riprova.", + "error_session_secret": "Il collegamento dei dispositivi non è configurato su questo server (manca SESSION_SECRET). Rivolgiti al tuo amministratore.", + "error_server_unreachable": "Impossibile raggiungere il server di posta. Riprova.", + "error_pairing_unavailable": "Il tuo server di posta non consente di collegare un dispositivo in questo modo. Rivolgiti al tuo amministratore.", + "qr_alt": "Codice QR di accesso", + "link_label": "Link di accesso", + "copy_link": "Copia link di accesso", + "copied": "Copiato", + "copy_hint": "Niente fotocamera? Copia il link e incollalo nell'app con \"Paste sign-in link\".", + "success_title": "Accesso effettuato sul telefono", + "success_body": "Puoi chiudere questa pagina. Per collegare un altro dispositivo, mostra un nuovo codice.", + "link_another": "Collega un altro dispositivo", + "use_sso": "Usa invece l'accesso unico (SSO)", + "error_insecure_server": "L'app mobile si collega ai server di posta solo tramite https, e questo usa http." + }, + "public_keys": { + "name_label": "Nome della chiave pubblica", + "name_placeholder": "es. La mia chiave", + "key_label": "Chiave pubblica (S/MIME o PGP in formato ASCII)", + "key_placeholder": "Incolla qui la tua chiave pubblica", + "none": "Nessuna chiave pubblica configurata" + } + }, + "identities": { + "title": "Identità di invio", + "description": "Gestisci gli indirizzi email da cui puoi inviare", + "identities_count": { + "label": "Le tue identità", + "description": "Indirizzi email configurati per l'invio", + "count_zero": "Nessuna identità", + "count_one": "1 identità", + "count_other": "{count} identità" + }, + "manage": "Gestisci identità", + "sub_addressing": { + "label": "Sotto-indirizzamento", + "description": "Usa tag come utente+tag@dominio.com per organizzare la posta in arrivo", + "learn_more": "Scopri di più" + } + }, + "vacation": { + "title": "Assenza", + "description": "Gestisci automaticamente le email in arrivo durante la tua assenza", + "loading": "Caricamento impostazioni di risposta automatica...", + "not_supported": "Il server di posta non supporta le risposte automatiche di assenza.", + "fetch_error": "Impossibile caricare le impostazioni di risposta automatica. Riprova.", + "status": { + "label": "Rispondi automaticamente", + "description": "Invia una risposta automatica a chi ti scrive", + "active": "Attivo", + "inactive": "Inattivo" + }, + "date_range": { + "start": "Data di inizio", + "start_description": "Lascia vuoto per nessun limite di inizio", + "end": "Data di fine", + "end_description": "Lascia vuoto per nessun limite di fine" + }, + "message": { + "subject_label": "Oggetto", + "subject_description": "Oggetto della risposta automatica", + "subject_placeholder": "Fuori ufficio", + "body_label": "Corpo del messaggio", + "body_description": "Contenuto del messaggio in testo semplice", + "body_placeholder": "Grazie per la tua email. Sono attualmente fuori ufficio e risponderò al mio ritorno.", + "html_label": "Messaggio formattato (HTML)", + "html_description": "Aggiungi una versione formattata con link e stile. I destinatari il cui client di posta non può visualizzarla vedranno il testo semplice qui sopra.", + "html_placeholder": "Scrivi una risposta di assenza formattata…" + }, + "preview": { + "title": "Anteprima", + "show": "Mostra anteprima", + "hide": "Nascondi anteprima" + }, + "save": "Salva modifiche", + "saving": "Salvataggio...", + "warnings": { + "end_before_start": "La data di fine deve essere successiva alla data di inizio", + "invalid_date": "Inserisci una data di inizio e di fine valide con l'ora oppure lascia vuoto il campo", + "start_in_past": "La data di inizio è nel passato", + "empty_body": "Il corpo del messaggio è vuoto - i destinatari riceveranno una risposta vuota", + "subject_too_long": "L'oggetto è troppo lungo per il server (al massimo {max} byte).", + "body_too_long": "Il messaggio è troppo lungo per il server (al massimo {max} byte, formattazione inclusa).", + "forward_address": "Inserisci un indirizzo valido per l'inoltro", + "not_running": "L'inoltro o i destinatari della risposta sono salvati ma al momento non sono attivi. Salva per riattivarli." + }, + "forward": { + "title": "Inoltro", + "description": "Inoltra i messaggi in arrivo a un altro indirizzo, con o senza risposta automatica, nel periodo indicato sopra (senza data di fine, finché non viene disattivato). Lo spam non viene inoltrato.", + "enabled_label": "Inoltra i messaggi", + "to_label": "Inoltra a", + "to_placeholder": "email@esempio.com", + "keep_label": "Mantieni nella posta in arrivo", + "keep_description": "Altrimenti il messaggio viene solo inoltrato: non resta in questa casella e non gli si applica nessuna regola di filtro." + }, + "audience": { + "label": "Rispondi a", + "description": "Mittenti interni: {domains}", + "all": "Tutti i mittenti", + "internal": "Solo mittenti interni", + "external": "Solo mittenti esterni" + }, + "auto_reply": { + "title": "Risposta automatica" + } + }, + "folders": { + "role_memos": "Note", + "title": "Cartelle", + "description": "Gestisci le cartelle e-mail e assegna ruoli standard", + "folder_list": "Le tue cartelle", + "folder_list_description": "Clicca sull'icona di una cartella per personalizzarla", + "standard_roles": "Ruoli standard delle cartelle", + "standard_roles_description": "Assegna quali cartelle sono utilizzate per i ruoli standard come Posta in arrivo, Inviati, Cestino, ecc.", + "role_inbox": "Posta in arrivo", + "role_drafts": "Bozze", + "role_sent": "Inviati", + "role_trash": "Cestino", + "role_junk": "Spam / Indesiderata", + "role_archive": "Archivio", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Nessuno", + "create_folder": "Crea cartella", + "create_subfolder": "Crea sottocartella", + "reorder": "Trascina per riordinare", + "reorder_error": "Impossibile riordinare le cartelle", + "move_error": "Impossibile spostare la cartella", + "select_folder": "Seleziona {name}", + "select_hint": "Seleziona. Maiusc+clic seleziona tutte le cartelle fino all'ultima su cui hai fatto clic.", + "selected_count": "{count, plural, one {# cartella selezionata} other {# cartelle selezionate}}", + "move_to": "Sposta in...", + "move_to_top_level": "Livello superiore", + "move": "Sposta", + "clear_selection": "Annulla selezione", + "folders_moved": "{count, plural, one {# cartella spostata} other {# cartelle spostate}}", + "move_partial_error": "{count, plural, one {Impossibile spostare # cartella} other {Impossibile spostare # cartelle}}", + "subfolder_of": "Dentro {name}", + "subfolder_name": "Nome della sottocartella", + "new_folder_name": "Nome della cartella", + "rename": "Rinomina", + "change_icon": "Cambia icona", + "count_mode": "Contatore per {name}", + "count_mode_default": "Contatore: predefinito", + "count_mode_unread": "Solo non letti", + "count_mode_all": "Non letti e totale", + "count_mode_none": "Nessun contatore", + "delete": "Elimina", + "confirm_delete": "Sei sicuro di voler eliminare \"{name}\"? Le e-mail in questa cartella verranno spostate nel Cestino.", + "create": "Crea", + "cancel": "Annulla", + "no_folders": "Nessuna cartella personalizzata", + "cannot_delete_role": "Impossibile eliminare una cartella con un ruolo standard. Rimuovi prima il ruolo.", + "folder_created": "Cartella creata", + "folder_renamed": "Cartella rinominata", + "folder_deleted": "Cartella eliminata", + "role_updated": "Ruolo della cartella aggiornato", + "error_create": "Impossibile creare la cartella", + "error_rename": "Impossibile rinominare la cartella", + "error_delete": "Impossibile eliminare la cartella", + "error_delete_has_children": "Impossibile eliminare la cartella: contiene ancora sottocartelle. Eliminale o spostale prima.", + "error_delete_has_email": "Impossibile eliminare la cartella: contiene ancora email. Spostale o eliminale prima.", + "error_role": "Impossibile aggiornare il ruolo della cartella" + }, + "advanced": { + "title": "Avanzate", + "description": "Opzioni avanzate e impostazioni per sviluppatori", + "debug_mode": { + "label": "Modalità debug", + "description": "Abilita registrazione dettagliata per la risoluzione dei problemi" + }, + "debug_categories": { + "description": "Seleziona quali categorie registrare. Disattiva quelle che non ti servono per ridurre il rumore nella console.", + "jmap": "Client JMAP", + "jmap_description": "Operazioni sulle caselle di posta, recupero email e richieste del protocollo JMAP", + "calendar": "Calendario", + "calendar_description": "Eventi del calendario, importazioni e messaggi di pianificazione", + "tasks": "Attività", + "tasks_description": "Creazione, recupero e aggiornamento delle attività del calendario", + "auth": "Autenticazione", + "auth_description": "Accesso, TOTP, scambio di token e gestione della sessione", + "filters": "Filtri", + "filters_description": "Regole di filtro Sieve e script di risposta automatica", + "email": "Visualizzazione email", + "email_description": "Rendering delle email, elaborazione TNEF e segna come letto", + "push": "Notifiche push", + "push_description": "Configurazione e recapito delle notifiche push", + "contacts": "Contatti e rubriche", + "contacts_description": "Sincronizzazione contatti, operazioni rubrica e mittenti attendibili" + }, + "settings_sync": { + "label": "Sincronizzazione impostazioni", + "description": "Sincronizza le impostazioni tra browser e dispositivi" + }, + "sender_favicons": { + "label": "Favicon dei mittenti", + "description": "Mostra le icone dei siti web come immagini profilo per i mittenti aziendali", + "refresh_label": "Aggiorna icone dei mittenti", + "refresh_description": "Scarica di nuovo le icone dei siti web, ad esempio quando un mittente ha cambiato la sua", + "refresh_button": "Aggiorna", + "refresh_done": "Le icone dei mittenti verranno scaricate di nuovo" + }, + "show_avatars_in_junk": { + "label": "Mostra avatar nella cartella spam", + "description": "Mostra immagini profilo e favicon dei mittenti nella cartella spam. Disattivato per impostazione predefinita per non dare apparenza legittima ai tentativi di phishing." + }, + "keyboard_shortcuts": { + "label": "Scorciatoie da tastiera", + "description": "Visualizza le scorciatoie da tastiera disponibili", + "button": "Visualizza scorciatoie" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Ripristina impostazioni", + "description": "Ripristina tutte le impostazioni ai valori predefiniti", + "button": "Ripristina ai valori predefiniti" + }, + "export_settings": { + "label": "Esporta impostazioni", + "description": "Scarica le tue impostazioni come JSON", + "button": "Esporta" + }, + "import_settings": { + "label": "Importa impostazioni", + "description": "Carica impostazioni da file JSON", + "button": "Importa" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Codice sorgente" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Clicca su spam e phishing prima che raggiungano la tua posta in arrivo. Lascia passare la posta vera. Se perdi tutti e tre gli scudi, la casella cade.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Posta vera" + }, + "click_to_block": "Clicca per bloccare", + "let_through": "Lascia passare", + "start": "Inizia", + "play_again": "Gioca ancora", + "score": "Punti", + "combo": "Bonus serie", + "shields_left": "{count} scudi su {total} rimasti", + "seconds": "{seconds} s", + "best": "Record: {score}", + "new_best": "Nuovo record", + "held": "Posta difesa", + "overrun": "Posta invasa", + "stats": { + "caught": "Bloccati", + "delivered": "Consegnati", + "mistakes": "Errori" + } + } + }, + "sidebar_apps": { + "title": "App nella barra laterale", + "description": "Gestisci app e link personalizzati nella barra laterale", + "keep_loaded": "Mantieni app caricate", + "keep_loaded_description": "Mantieni le app inline in esecuzione in background durante il passaggio per evitare il ricaricamento", + "managed_title": "Fornite dall'amministratore", + "managed_description": "Queste app sono configurate per tutti e non possono essere modificate.", + "manage_title": "App personalizzate", + "manage_description": "Aggiungi, modifica o rimuovi app personalizzate dalla barra laterale" + }, + "contacts": { + "title": "Contatti", + "description": "Importa ed esporta i tuoi contatti", + "import_label": "Importa contatti", + "import_description": "Importa contatti da un file vCard (.vcf)", + "export_label": "Esporta contatti", + "export_description": "Esporta tutti i contatti come file vCard (.vcf)", + "manage_title": "Rubriche", + "manage_description": "Rinomina le tue rubriche", + "no_address_books": "Nessuna rubrica trovata", + "categories_title": "Categorie", + "categories_description": "Rinomina le categorie dei contatti", + "no_categories": "Nessuna categoria trovata", + "group_by_letter_description": "Mostra intestazioni alfabetiche nell'elenco dei contatti", + "group_by_letter_label": "Raggruppa per prima lettera", + "sort_by_last_name_label": "Ordina per cognome", + "sort_by_last_name_description": "Ordina l'elenco dei contatti per cognome così i familiari compaiono insieme" + }, + "downloads": { + "title": "Download", + "description": "Personalizza come vengono nominati gli email e gli allegati scaricati.", + "reset": "Ripristina predefinito", + "preview": "Anteprima:", + "email_template": { + "label": "Nome file email (.eml)", + "description": "Modello usato quando esporti un'email o la trascini sul file system. L'estensione .eml viene aggiunta automaticamente." + }, + "attachment_template": { + "label": "Nome file dell’allegato", + "description": "Modello usato quando scarichi o trascini un allegato. Se ometti '{filename}' e '{ext}', l'estensione originale viene mantenuta." + }, + "bundle_template": { + "label": "Nome del file .zip multi-email", + "description": "Modello usato quando trascini o scarichi più email selezionate come un singolo archivio .zip. L'estensione .zip viene aggiunta automaticamente." + }, + "spaces": { + "label": "Spazi", + "description": "Sostituisci gli spazi nel nome file risultante con un altro carattere.", + "keep": "Mantieni gli spazi", + "underscore": "Sostituisci con _", + "dash": "Sostituisci con -" + }, + "lowercase": { + "label": "Minuscolo", + "description": "Forza l'intero nome file in minuscolo." + }, + "strip_diacritics": { + "label": "Rimuovi i segni diacritici", + "description": "Converti le lettere accentate nei loro equivalenti ASCII (ä → a, é → e). Utile per strumenti che gestiscono male i nomi Unicode." + }, + "collapse_separators": { + "label": "Riduci i separatori ripetuti", + "description": "Riduci sequenze di spazi, underscore o trattini a un solo carattere." + }, + "after_export": { + "label": "Dopo l’esportazione", + "description": "Sposta facoltativamente l’email dopo averla esportata come .eml.", + "keep": "Mantieni nella casella", + "archive": "Sposta nell’archivio", + "trash": "Sposta nel cestino" + } + }, + "filters": { + "title": "Filtri email", + "description": "Crea regole per ordinare, etichettare e gestire automaticamente le email in arrivo", + "add_rule": "Aggiungi regola", + "no_rules": "Nessuna regola di filtro", + "vacation_active": "Il risponditore automatico è attivo", + "vacation_active_description": "La risposta automatica è abilitata per i messaggi in arrivo", + "vacation_configure": "Configura", + "no_rules_description": "Crea regole per organizzare automaticamente le tue email in arrivo", + "edit_rule": "Modifica regola", + "new_rule": "Nuova regola", + "delete_rule": "Elimina regola", + "delete_confirm": "Sei sicuro di voler eliminare questa regola?", + "enable": "Attiva", + "disable": "Disattiva", + "raw_editor": "Editor Sieve grezzo", + "raw_editor_warning": "La modifica dello script Sieve grezzo potrebbe compromettere l'editor visuale delle regole. Le modifiche effettuate qui sovrascrivono il costruttore visuale.", + "validate": "Convalida", + "validation_success": "Lo script è valido", + "validation_error": "Lo script contiene errori", + "save": "Salva regole", + "saving": "Salvataggio...", + "saved": "Filtri salvati con successo", + "save_failed": "Impossibile salvare i filtri", + "loading": "Caricamento filtri...", + "not_supported": "Il tuo server di posta non supporta i filtri email.", + "rule_name": "Nome della regola", + "rule_name_placeholder": "es. Ordina newsletter", + "match_all": "Corrispondere a TUTTE le condizioni", + "match_any": "Corrispondere a QUALSIASI condizione", + "conditions": "Condizioni", + "add_condition": "Aggiungi condizione", + "actions": "Azioni", + "add_action": "Aggiungi azione", + "stop_processing": "Interrompere l'elaborazione delle regole successive", + "include_spam": "Sposta anche i messaggi contrassegnati come spam", + "attachment_type_placeholder": "es. pdf, doc, jpg", + "value_placeholder_multi": "Valore (più valori separati da virgole)", + "condition_fields": { + "attachment": "Allegato", + "from": "Da", + "to": "A", + "cc": "Cc", + "subject": "Oggetto", + "header": "Intestazione personalizzata", + "size": "Dimensione", + "body": "Corpo", + "all": "Tutti i messaggi" + }, + "comparators": { + "has_any": "è presente", + "has_type": "di tipo", + "contains": "contiene", + "not_contains": "non contiene", + "is": "è esattamente", + "not_is": "non è", + "starts_with": "inizia con", + "ends_with": "termina con", + "matches": "corrisponde al modello", + "greater_than": "è maggiore di", + "less_than": "è minore di", + "address_is": "è l'indirizzo", + "domain_is": "ha il dominio" + }, + "action_types": { + "move": "Sposta nella cartella", + "copy": "Copia nella cartella", + "forward": "Inoltra a", + "mark_read": "Segna come letto", + "star": "Aggiungi stella", + "add_label": "Aggiungi etichetta", + "discard": "Elimina silenziosamente", + "reject": "Rifiuta con messaggio", + "keep": "Mantieni nella posta in arrivo", + "stop": "Interrompi elaborazione" + }, + "move_to_folder": "Seleziona cartella", + "copy_to_folder": "Seleziona cartella", + "forward_to": "Inoltra a indirizzo email", + "forward_placeholder": "email@esempio.com", + "forward_keep_copy": "Conserva una copia", + "forward_limit": "Limite di inoltri per messaggio su questo server: {count}. Gli inoltri in eccesso vengono ignorati.", + "forward_unavailable": "Questo server non consente l'inoltro. Le azioni di inoltro nelle regole dei filtri non hanno effetto.", + "reject_message": "Messaggio di rifiuto", + "reject_placeholder": "La tua email è stata rifiutata", + "label_name": "Nome dell'etichetta", + "label_placeholder": "Seleziona etichetta", + "header_name": "Nome dell'intestazione", + "header_placeholder": "es. X-Mailing-List", + "size_bytes": "Dimensione in byte", + "size_placeholder": "es. 1000000", + "system_managed": "Regola gestita dal sistema", + "opaque_warning": "Questo script è stato modificato al di fuori del costruttore visuale. È disponibile solo la modifica Sieve grezza.", + "open_sieve_editor": "Apri editor Sieve", + "fetch_error": "Impossibile caricare i filtri", + "expanded_view": "Vista espansa", + "expanded_view_description": "Mostra le regole dei filtri con blocchi dettagliati di condizioni e azioni", + "if": "Se", + "then": "Allora", + "match_all_conditions": "tutte corrispondono", + "match_any_condition": "una corrisponde", + "and": "e", + "or": "o", + "cancel": "Annulla", + "confirm_delete": "Elimina", + "rule_list": "Regole di filtraggio", + "drag_to_reorder": "Trascina per riordinare", + "match_type": "Tipo di corrispondenza", + "reset_to_visual": "Ripristina il costruttore visuale", + "reset_warning": "Questo eliminerà lo script attuale e ricomincerà da zero.", + "confirm_reset": "Ripristina", + "validation_empty_name": "Il nome della regola è obbligatorio", + "validation_empty_conditions": "È necessaria almeno una condizione con un valore", + "validation_empty_actions": "È necessaria almeno un'azione", + "templates_section": "Parti da un modello", + "template_newsletters": "Sposta newsletter in cartella", + "template_receipts": "Archivia ricevute automaticamente", + "template_important": "Contrassegna email importanti", + "template_notifications": "Filtra notifiche", + "sieve_editor": { + "title": "Editor script Sieve", + "warning": "La modifica dello script Sieve può interrompere la modifica visuale delle regole. Le modifiche effettuate qui sovrascrivono il costruttore visuale.", + "script_content": "Script Sieve", + "valid": "Lo script è valido", + "invalid": "Lo script contiene errori", + "save_warning": "Il salvataggio sovrascriverà tutte le regole visuali. Questa azione non può essere annullata. Fai clic su Salva di nuovo per confermare.", + "validating": "Validazione...", + "validate": "Valida", + "cancel": "Annulla", + "save": "Salva", + "confirm_save": "Conferma salvataggio", + "validation_failed": "Richiesta di validazione non riuscita" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# condizione} other {# condizioni}}", + "actions_count": "{count, plural, one {# azione} other {# azioni}}" + }, + "origin_external": "Esterno", + "managed_by_tooltip": "Gestito da {source}. Modificalo in quell'app o usa l'editor Sieve grezzo.", + "quick_rule_names": { + "move_sender": "Sposta da {sender} a {folder}", + "move_domain": "Sposta da @{domain} a {folder}", + "move_list": "Sposta la lista {list} in {folder}", + "mark_read": "Segna come letti i messaggi di {sender}", + "tag": "Etichetta {tag} i messaggi di {sender}", + "block": "Blocca {sender}", + "custom": "Messaggi di {sender}", + "custom_plain": "Nuova regola", + "senders": "{count, plural, one {# mittente} other {# mittenti}}" + }, + "suggestions": "Suggerimenti", + "suggest_subject": "Oggetto «{subject}»", + "suggest_to": "A {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Applica anche ai messaggi già presenti in questa cartella", + "apply_existing_unsupported": "Solo le regole che controllano mittente, destinatari, oggetto o intestazioni, e che spostano, copiano, segnano come letto, contrassegnano con stella o etichettano, si possono applicare ai messaggi esistenti.", + "period_title": "Periodo", + "period_start": "Data di inizio", + "period_end": "Data di fine", + "validation_period_order": "La data di fine deve essere successiva alla data di inizio", + "period_toggle": "Attiva solo in un periodo", + "period_hint": "Si applica ai messaggi che arrivano in questo periodo. Lascia un campo vuoto per non limitare quell'estremo.", + "validation_period_empty": "Inserisci una data di inizio o di fine per il periodo", + "validation_period_invalid": "Inserisci una data valida con l'ora per il periodo oppure lascia vuoto il campo", + "apply_existing_period": "Una regola con periodo agisce solo sui messaggi che arrivano al suo interno, quindi non si può applicare ai messaggi esistenti.", + "period_status_scheduled": "programmata", + "period_status_active": "attiva", + "period_status_expired": "scaduta" + }, + "templates": { + "title": "Modelli email", + "description": "Crea modelli email riutilizzabili con variabili segnaposto", + "add": "Nuovo modello", + "edit": "Modifica modello", + "name": "Nome del modello", + "name_placeholder": "es.: Email di follow-up", + "category": "Categoria", + "category_placeholder": "es.: Lavoro, Personale", + "subject": "Oggetto", + "subject_placeholder": "Oggetto dell'email", + "body": "Corpo", + "body_placeholder": "Contenuto dell'email...", + "recipients_placeholder": "email@example.com", + "identity": "Invia come", + "default_identity": "Identità predefinita", + "favorite": "Preferito", + "cancel": "Annulla", + "create": "Crea", + "update": "Aggiorna", + "confirm_delete": "Elimina", + "no_templates": "Nessun modello", + "manage": "Gestisci modelli", + "count": "{count, plural, one {# modello} other {# modelli}}", + "export_import": "Esporta e importa", + "export_import_description": "Esegui il backup dei modelli o trasferiscili su un altro dispositivo", + "export": "Esporta", + "import": "Importa", + "validation": { + "empty": "Il nome del modello è obbligatorio", + "too_long": "Il nome del modello non deve superare i 200 caratteri" + } + }, + "files": { + "display": { + "title": "Visualizzazione", + "description": "Configura come vengono visualizzati file e cartelle" + }, + "default_view": { + "label": "Vista predefinita", + "description": "Scegli tra layout a griglia e a lista", + "list": "Lista", + "grid": "Griglia" + }, + "default_sort": { + "label": "Ordinamento predefinito", + "description": "Scegli l'ordinamento predefinito per i file", + "name": "Nome", + "size": "Dimensione", + "modified": "Modificato" + }, + "sort_direction": { + "label": "Direzione ordinamento", + "description": "Scegli ordine crescente o decrescente", + "ascending": "Crescente", + "descending": "Decrescente" + }, + "icons": { + "title": "Icone", + "description": "Configura l'aspetto delle icone dei file" + }, + "show_icons": { + "label": "Mostra icone file", + "description": "Visualizza le icone accanto a file e cartelle" + }, + "colored_icons": { + "label": "Icone colorate", + "description": "Usa icone colorate invece che monocromatiche" + }, + "show_thumbnails": { + "label": "Mostra miniature", + "description": "Mostra anteprime delle immagini al posto delle icone" + }, + "behavior": { + "title": "Comportamento", + "description": "Configura il comportamento del gestore file" + }, + "show_hidden": { + "label": "Mostra file nascosti", + "description": "Visualizza file e cartelle che iniziano con un punto" + }, + "preview": { + "label": "Anteprima" + } + }, + "search_placeholder": "Cerca impostazioni", + "search_clear": "Cancella ricerca", + "search_no_results": "Nessuna impostazione corrispondente", + "scoped": { + "back": "Torna al mio account", + "managing": "Gestione: {name}" + } + }, + "errors": { + "page_error_title": "Qualcosa è andato storto", + "page_error_description": "Si è verificato un errore imprevisto. Riprova o torna alla pagina principale.", + "sidebar_error": "Impossibile caricare le caselle di posta", + "email_list_error": "Impossibile caricare i messaggi", + "viewer_error_title": "Impossibile visualizzare il messaggio", + "viewer_error_description": "Si è verificato un problema nella visualizzazione di questo messaggio. Potrebbe contenere contenuti non supportati.", + "composer_error": "Impossibile caricare l'editor", + "settings_error_title": "Impostazioni non disponibili", + "settings_error_description": "Impossibile caricare le impostazioni. Le tue preferenze potrebbero non essere salvate.", + "try_again": "Riprova", + "reload": "Ricarica", + "reload_emails": "Ricarica messaggi", + "reload_settings": "Ricarica impostazioni", + "retry": "Riprova", + "go_home": "Vai alla posta in arrivo" + }, + "context_menu": { + "reply": "Rispondi", + "reply_all": "Rispondi a tutti", + "forward": "Inoltra", + "mark_read": "Segna come letto", + "mark_unread": "Segna come non letto", + "star": "Aggiungi stella", + "unstar": "Rimuovi stella", + "pin": "Fissa", + "unpin": "Non fissare più", + "move_to": "Sposta in...", + "copy_to_account": "Copia in account...", + "remove_from_folder": "Rimuovi da questa cartella", + "archive": "Archivia", + "delete": "Elimina", + "mark_as_spam": "Segnala come spam", + "not_spam": "Non spam", + "tag": "Etichetta", + "items_selected": "{count} messaggi selezionati", + "forward_as_attachments": "Inoltra come allegati", + "edit_draft": "Modifica bozza", + "cancel_scheduled_send": "Annulla invio", + "reschedule_send": "Riprogramma", + "cancel_and_edit": "Annulla e modifica", + "cancel_and_compose_again": "Annulla e componi di nuovo", + "rules": { + "title": "Regole", + "move_from": "Sposta sempre i messaggi di {sender}", + "move_from_domain": "Sposta sempre i messaggi di @{domain}", + "move_from_list": "Sposta sempre i messaggi di questa lista", + "mark_read_from": "Segna sempre come letti i messaggi di {sender}", + "tag_with": "Etichetta sempre con", + "block": "{count, plural, one {Blocca mittente} other {Blocca # mittenti}}", + "no_junk": "Questo account non ha una cartella Posta indesiderata", + "create": "Crea regola…", + "manage": "Gestisci regole", + "senders_count": "{count, plural, one {# mittente} other {# mittenti}}", + "cross_account": "Seleziona messaggi di un solo account", + "opaque_hint": "I tuoi filtri sono stati modificati a mano. Apri le impostazioni dei filtri" , + "loading": "Caricamento delle regole…", + "load_failed": "Impossibile caricare le regole", + "none": "Nessuna regola", + "new_mail_only": "Solo per i nuovi messaggi", + "running": "Esecuzione di «{name}» su {folder}…" + }, + "find_emails_from": "Trova email da {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Segna cartella come letta", + "mark_folder_tree_read": "Segna cartella e sottocartelle come lette", + "mark_all_folders_read": "Segna tutte le cartelle come lette", + "new_subfolder": "Nuova sottocartella...", + "new_folder": "Nuova cartella...", + "rename": "Rinomina...", + "share": "Condividi...", + "import_email": "Importa .eml o .zip...", + "empty_folder": "Svuota cartella", + "empty_folder_generic": "Svuota cartella", + "delete_folder": "Elimina cartella", + "refresh": "Aggiorna", + "hide_shared_account": "Nascondi dalla barra laterale", + "mute_notifications": "Disattiva notifiche", + "unmute_notifications": "Riattiva notifiche", + "mute_account_notifications": "Disattiva notifiche per questo account", + "unmute_account_notifications": "Riattiva notifiche per questo account", + "reset_folder_notifications": "Ripristina notifiche delle cartelle", + "mark_all_confirm_title": "Segna tutte le cartelle come lette", + "mark_all_confirm_message": "Segnare tutti i messaggi non letti del tuo account personale come letti?", + "delete_confirm_title": "Elimina cartella", + "delete_confirm_message": "Eliminare definitivamente la cartella \"{name}\"? Questa azione non può essere annullata.", + "prompt_new_subfolder": "Inserisci un nome per la nuova sottocartella.", + "prompt_new_folder": "Inserisci un nome per la nuova cartella.", + "prompt_rename": "Inserisci un nuovo nome per questa cartella.", + "toast_marked_read": "Cartella segnata come letta", + "toast_marked_read_count": "Segnati {count, plural, one {1 messaggio} other {# messaggi}} come letti", + "toast_already_read": "Nessun messaggio non letto", + "toast_marked_all_read": "Tutte le cartelle segnate come lette", + "toast_emptied": "Cartella svuotata", + "toast_folder_created": "Cartella creata", + "toast_folder_renamed": "Cartella rinominata", + "toast_folder_deleted": "Cartella eliminata", + "toast_error_mark_read": "Impossibile segnare come letto", + "toast_error_empty": "Impossibile svuotare la cartella", + "toast_error_create": "Impossibile creare la cartella", + "toast_error_rename": "Impossibile rinominare la cartella", + "toast_error_delete": "Impossibile eliminare la cartella", + "toast_error_delete_has_children": "La cartella contiene sottocartelle. Rimuoverle prima.", + "toast_error_delete_has_email": "La cartella non è vuota. Svuotarla prima.", + "delete_with_contents_title": "La cartella non è vuota", + "delete_with_contents_message": "La cartella \"{name}\" contiene ancora {count, plural, one {1 messaggio} other {# messaggi}}. Eliminare la cartella e tutti i suoi messaggi? L'operazione non può essere annullata.", + "delete_with_contents_confirm": "Elimina cartella e messaggi", + "placeholder_folder_name": "Nome cartella", + "create": "Crea", + "rename_confirm": "Rinomina" + }, + "shortcuts": { + "title": "Scorciatoie da tastiera", + "tip": "Premi ? in qualsiasi momento per mostrare questo aiuto", + "sections": { + "navigation": "Navigazione", + "actions": "Azioni sui messaggi", + "global": "Globali", + "threads": "Conversazioni", + "composer": "Composizione" + }, + "navigation": { + "next_email": "Messaggio successivo", + "previous_email": "Messaggio precedente", + "open_email": "Apri messaggio", + "close_email": "Chiudi / Deseleziona", + "go_to_folder": "Vai alla cartella", + "next_unread": "Prossima email non letta", + "no_more_unread": "Nessuna email non letta più in basso nell'elenco" + }, + "actions": { + "reply": "Rispondi", + "reply_all": "Rispondi a tutti", + "forward": "Inoltra", + "star": "Attiva/disattiva stella", + "archive": "Archivia", + "delete": "Elimina", + "mark_unread": "Segna come non letto", + "mark_read": "Segna come letto", + "toggle_spam": "Segnala spam / Non spam" + }, + "global": { + "compose": "Scrivi nuovo messaggio", + "search": "Cerca", + "help": "Mostra scorciatoie", + "refresh": "Aggiorna messaggi", + "select_all": "Seleziona tutto", + "global_search": "Cerca ovunque", + "extend_selection": "Estendi la selezione in basso / in alto" + }, + "threads": { + "expand_collapse": "Espandi/comprimi conversazione", + "select_mountain_view": "Seleziona la conversazione" + }, + "composer": { + "send": "Invia email", + "schedule_send": "Programma invio", + "template_picker": "Apri selettore modelli" + } + }, + "threads": { + "messages_one": "{count} messaggio", + "messages_other": "{count} messaggi", + "messages_tooltip": "{count, plural, one {# messaggio in questa conversazione} other {# messaggi in questa conversazione}}", + "expand": "Espandi conversazione", + "collapse": "Comprimi conversazione", + "loading": "Caricamento conversazione...", + "mark_read": "Segna conversazione come letta", + "mark_unread": "Segna conversazione come non letta", + "archive": "Archivia conversazione", + "delete": "Elimina conversazione", + "star": "Aggiungi stella alla conversazione", + "unstar": "Rimuovi stella dalla conversazione", + "toggle_thread": "Espandi/comprimi discussione" + }, + "identities": { + "modal_title": "Gestisci identità di invio", + "create_new": "Crea nuova identità", + "edit_identity": "Modifica identità", + "delete_confirm": "Eliminare questa identità? Questa azione non può essere annullata.", + "cannot_delete": "Questa identità non può essere eliminata", + "primary_identity": "Principale", + "set_as_primary": "Imposta come principale", + "no_identities": "Nessuna identità trovata", + "display": { + "reply_to": "Rispondi a:", + "bcc": "CCN:", + "signature": "Firma:", + "preview": "Anteprima:" + }, + "validation_errors": { + "invalid_emails": "Email non valide: {emails}", + "unknown_error": "Errore sconosciuto" + }, + "form": { + "name_label": "Nome visualizzato", + "name_placeholder": "es. Email lavoro, Personale", + "name_required": "Il nome è obbligatorio", + "email_label": "Indirizzo email", + "email_placeholder": "tua.email@esempio.com", + "email_required": "L'email è obbligatoria", + "email_invalid": "Inserisci un indirizzo email valido", + "email_immutable": "L'indirizzo email non può essere modificato dopo la creazione", + "reply_to_label": "Rispondi a (facoltativo)", + "reply_to_placeholder": "diversa@example.com", + "bcc_label": "CCN automatica (facoltativo)", + "bcc_placeholder": "archivio@example.com", + "text_signature_label": "Firma di testo", + "html_signature_label": "Firma HTML", + "signature_byte_counter": "{bytes} / {max} byte", + "signature_byte_limit_reached": "Limite del server raggiunto", + "save": "Salva identità", + "cancel": "Annulla", + "creating": "Creazione...", + "updating": "Aggiornamento..." + }, + "sub_address": { + "button_tooltip": "Usa sotto-indirizzo", + "popover_title": "Aggiungi tag sotto-indirizzo", + "tag_input_placeholder": "Inserisci tag (es. shopping)", + "preview_label": "Anteprima:", + "recent_tags": "Tag recenti", + "suggested_tags": "Suggeriti", + "use_address": "Usa questo indirizzo", + "invalid_tag": "Il tag deve contenere solo caratteri alfanumerici e trattini", + "tag_too_long": "Il tag deve essere di massimo 30 caratteri", + "help_text": "I messaggi inviati a utente{delimiter}tag@dominio.com arriveranno nella tua casella di posta", + "validation": { + "empty": "Il tag non può essere vuoto", + "too_long": "Il tag deve essere di massimo {max} caratteri", + "invalid_chars": "Il tag deve contenere solo lettere, numeri e trattini" + } + }, + "badge": { + "sent_via": "tramite", + "sub_address_tag": "Inviato usando sotto-indirizzo: {tag}", + "identity_name": "Inviato usando identità: {name}", + "identity_short": "tramite {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Elimina", + "delete_confirm_title": "Elimina identità" + }, + "templates": { + "picker_title": "Scegli un modello", + "search_placeholder": "Cerca modelli...", + "section_favorites": "Preferiti", + "section_recent": "Recenti", + "section_uncategorized": "Altri", + "no_templates": "Nessun modello", + "no_results": "Nessun modello trovato", + "fill_placeholders": "Compila i valori segnaposto", + "enter_value": "Inserisci un valore...", + "preview": "Anteprima", + "insert_with_values": "Inserisci con valori", + "insert_raw": "Inserisci grezzo", + "copy_suffix": "(copia)", + "placeholder": "Variabile", + "placeholders": { + "recipient_name": "Nome del destinatario", + "company": "Nome dell'azienda", + "date": "Data odierna", + "day_of_week": "Giorno della settimana", + "sender_name": "Il tuo nome" + } + }, + "contacts": { + "directory": { + "title": "Rubrica aziendale", + "search_placeholder": "Cerca nome o indirizzo email", + "refresh": "Aggiorna rubrica aziendale", + "retry": "Riprova", + "loading": "Caricamento della rubrica aziendale…", + "unavailable_title": "Nessuna rubrica aziendale", + "unavailable_hint": "Il tuo server di posta non offre un elenco degli account.", + "forbidden_title": "Rubrica aziendale non disponibile", + "forbidden_hint": "Il tuo server di posta non consente a questo account di consultare l'elenco. Chiedi all'amministratore.", + "error_title": "Impossibile caricare la rubrica aziendale", + "error_hint": "Controlla la connessione e riprova.", + "empty_title": "La rubrica aziendale è vuota", + "empty_hint": "Il tuo server di posta non elenca account con un indirizzo email.", + "no_match": "Nessuno nella rubrica aziendale corrisponde alla ricerca", + "refresh_failed": "Impossibile aggiornare la rubrica aziendale. Viene mostrato l'ultimo elenco caricato.", + "select_page": "Seleziona tutti in questa pagina", + "deselect_page": "Deseleziona tutti in questa pagina", + "select_entry": "Seleziona {name}", + "range": "{from}–{to} di {total}", + "page": "Pagina {page} di {count}", + "previous_page": "Pagina precedente", + "next_page": "Pagina successiva", + "picker_title": "Seleziona dalla rubrica aziendale", + "cancel": "Annulla", + "selected_count": "{count} selezionati", + "add_to": "Aggiungi ad A", + "add_cc": "Aggiungi a Cc", + "add_bcc": "Aggiungi a Ccn", + "select_hint": "Seleziona una persona per vederne i dettagli", + "write_email": "Scrivi email", + "add_to_contacts": "Aggiungi ai contatti", + "email": "Email", + "read_only_note": "Le voci provengono dal tuo server di posta e non possono essere modificate qui.", + "type": { + "individual": "Persona", + "group": "Gruppo", + "resource": "Risorsa", + "location": "Luogo", + "other": "Altro" + } + }, + "title": "Contatti", + "search_placeholder": "Cerca contatti...", + "create_new": "Nuovo contatto", + "no_category": "Senza categoria", + "rename_category": "Rinomina categoria", + "category_name_label": "Nome della categoria", + "category_renamed": "Categoria rinominata", + "category_rename_failed": "Impossibile rinominare la categoria", + "category_added": "Contatto aggiunto a {name}", + "category_added_plural": "{count} contatti aggiunti a {name}", + "empty_state": "Nessun contatto", + "empty_state_title": "Nessun contatto", + "empty_state_subtitle": "Crea il tuo primo contatto o importa da un file vCard", + "empty_search": "Nessun contatto corrisponde alla ricerca", + "empty_search_hint": "Prova con un altro termine di ricerca", + "empty_filtered": "Nessun contatto corrisponde ai filtri", + "empty_filtered_hint": "Modifica o cancella i filtri", + "clear_search": "Cancella ricerca", + "import_vcard": "Importa vCard", + "delete_confirm_title": "Elimina contatto", + "delete_confirm": "Sei sicuro di voler eliminare questo contatto?", + "local_mode": "I contatti sono salvati localmente (il server non supporta JMAP Contacts)", + "back_to_contacts": "Torna ai contatti", + "back_to_email": "Torna all'e-mail", + "tabs": { + "all": "Tutti", + "groups": "Gruppi" + }, + "shared": { + "title": "Condivisi" + }, + "address_books": { + "create": "Nuova rubrica", + "created": "Rubrica creata", + "create_failed": "Impossibile creare la rubrica", + "title": "Le mie Rubriche", + "shared_prefix": "Condiviso: {name}", + "moved": "Contatto spostato in {name}", + "moved_plural": "{count} contatti spostati in {name}", + "move_failed": "Impossibile spostare il contatto", + "address_book": "Rubrica", + "rename": "Rinomina rubrica", + "name_label": "Nome della rubrica", + "renamed": "Rubrica rinominata", + "rename_failed": "Impossibile rinominare la rubrica", + "default": "Predefinita", + "set_default": "Imposta come predefinita", + "default_updated": "Rubrica predefinita aggiornata", + "set_default_failed": "Impossibile impostare la rubrica predefinita", + "manage": "Gestisci rubriche", + "share": "Condividi rubrica", + "new_contact_in_book": "Nuovo contatto in questa rubrica", + "delete": "Elimina rubrica", + "confirm_delete": "Eliminare \"{name}\"? Tutti i contatti in questa rubrica verranno rimossi.", + "deleted": "Rubrica eliminata", + "delete_failed": "Impossibile eliminare la rubrica" + }, + "detail": { + "emails": "Indirizzi email", + "phones": "Numeri di telefono", + "organizations": "Organizzazioni", + "addresses": "Indirizzi", + "notes": "Note", + "titles": "Titoli e ruoli", + "online_services": "Servizi online", + "anniversaries": "Anniversari", + "personal_info": "Informazioni personali", + "languages": "Lingue", + "categories": "Categorie", + "related_contacts": "Contatti correlati", + "crypto_keys": "Chiavi crittografiche", + "no_contact_selected": "Seleziona un contatto per vedere i dettagli", + "compose_email": "Scrivi email", + "copy_email": "Copia email", + "copy_phone": "Copia telefono", + "copy_url": "Copia URL", + "copied": "Copiato negli appunti", + "copy_failed": "Impossibile copiare negli appunti", + "created": "Creato", + "updated": "Ultimo aggiornamento", + "timezone": "Fuso orario", + "anniversary_birth": "Compleanno", + "anniversary_death": "Decesso", + "anniversary_wedding": "Anniversario", + "anniversary_other": "Altro", + "personal_expertise": "Competenza", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Altro", + "gender": "Genere", + "gender_masculine": "Maschile", + "gender_feminine": "Femminile", + "gender_other": "Altro", + "gender_none": "Non applicabile", + "gender_unknown": "Sconosciuto", + "calendar": "Calendario", + "calendar_uri": "URL del calendario", + "scheduling_uri": "URL di pianificazione", + "freebusy_uri": "URL di disponibilità", + "cert_issuer": "Emittente", + "cert_expires": "Scade", + "cert_expired": "Scaduto", + "cert_fingerprint": "Impronta digitale", + "cert_algorithm": "Algoritmo", + "import_to_smime": "Importa in S/MIME", + "cert_already_imported": "Certificato già importato", + "cert_imported": "Certificato importato", + "cert_import_failed": "Importazione del certificato non riuscita", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Altre azioni", + "age_years": "{count, plural, one {1 anno} other {# anni}}", + "years_since": "{count, plural, one {1 anno fa} other {# anni fa}}" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "Nuovo contatto", + "edit_title": "Modifica contatto", + "section_address_book": "Rubrica", + "select_address_book": "Seleziona una rubrica...", + "section_identity": "Nome e identità", + "contact_type": "Tipo di contatto", + "type_person": "Persona", + "type_organization": "Organizzazione", + "section_work": "Lavoro e organizzazione", + "prefix": "Prefisso", + "prefix_placeholder": "Dott., Sig., Sig.ra", + "given_name": "Nome", + "middle_name": "Secondo nome", + "surname": "Cognome", + "suffix": "Suffisso", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Soprannome", + "nickname_placeholder": "Soprannome", + "email": "Email", + "email_placeholder": "email@example.com", + "phone": "Telefono", + "phone_placeholder": "+39 02 1234 5678", + "phone_type": "Tipo", + "phone_voice": "Voce", + "phone_cell": "Cellulare", + "phone_fax": "Fax", + "phone_pager": "Cercapersone", + "phone_video": "Video", + "phone_text": "SMS", + "organization": "Organizzazione", + "organization_placeholder": "Nome azienda", + "department": "Reparto", + "department_placeholder": "Reparto", + "job_title": "Titolo professionale", + "job_title_placeholder": "es., Ingegnere del software", + "role": "Ruolo", + "role_placeholder": "es., Responsabile del team", + "addresses": "Indirizzi", + "add_address": "Aggiungi indirizzo", + "street": "Via", + "city": "Città", + "region": "Stato / Regione", + "postcode": "Codice postale", + "country": "Paese", + "online_services": "Servizi online", + "add_online_service": "Aggiungi servizio online", + "url_placeholder": "https://...", + "service_placeholder": "Servizio", + "anniversaries": "Anniversari", + "add_anniversary": "Aggiungi data", + "anniversary_birth": "Compleanno", + "anniversary_wedding": "Anniversario", + "anniversary_death": "Commemorazione", + "anniversary_other": "Altro", + "personal_info": "Informazioni personali", + "add_personal_info": "Aggiungi voce", + "personal_info_placeholder": "es., Fotografia", + "personal_expertise": "Competenza", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Altro", + "level": "Livello", + "level_high": "Alto", + "level_medium": "Medio", + "level_low": "Basso", + "categories": "Categorie", + "categories_placeholder": "es., Famiglia, Amici, Colleghi", + "categories_hint": "Digita per cercare o aggiungere", + "category_add": "Aggiungi", + "note": "Note", + "note_placeholder": "Aggiungi una nota...", + "gender": "Genere", + "gender_sex": "Sesso", + "gender_male": "Maschile", + "gender_female": "Femminile", + "gender_other": "Altro", + "gender_none": "Non applicabile", + "gender_unknown": "Sconosciuto", + "gender_identity": "Identità di genere", + "gender_identity_placeholder": "Identità di genere...", + "calendar": "Calendario", + "calendar_uri": "URL del calendario", + "scheduling_uri": "URL di pianificazione", + "freebusy_uri": "URL di disponibilità", + "context_work": "Lavoro", + "context_private": "Personale", + "add_email": "Aggiungi email", + "add_phone": "Aggiungi telefono", + "save": "Salva", + "cancel": "Annulla", + "creating": "Creazione...", + "updating": "Aggiornamento...", + "name_required": "Inserisci un nome, un cognome o un'organizzazione", + "email_invalid": "Inserisci un indirizzo email valido", + "email_error_inline": "Formato email non valido", + "save_failed": "Impossibile salvare il contatto", + "delete": "Elimina", + "upload_photo": "Carica foto", + "remove_photo": "Rimuovi foto", + "photo_hint": "JPG o PNG, fino a 10 MB. Verrà ridimensionata.", + "photo_too_large": "L'immagine è troppo grande (max 10 MB)", + "photo_invalid": "File immagine non valido", + "change_photo": "Cambia" + }, + "groups": { + "create": "Nuovo gruppo", + "edit": "Modifica gruppo", + "empty": "Nessun gruppo", + "delete_confirm_title": "Elimina gruppo", + "delete_confirm": "Sei sicuro di voler eliminare questo gruppo?", + "name_label": "Nome del gruppo", + "name_placeholder": "es. Team, Famiglia", + "name_required": "Il nome del gruppo è obbligatorio", + "save_failed": "Impossibile salvare il gruppo", + "members_label": "Membri", + "search_members": "Cerca contatti da aggiungere...", + "no_members": "Nessun membro in questo gruppo", + "member_count": "{count, plural, =0 {Nessun membro} one {1 membro} other {# membri}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Importa contatti", + "drop_hint": "Clicca per selezionare un file vCard", + "file_types": "File .vcf o .vcard", + "no_contacts": "Nessun contatto trovato nel file", + "parse_error": "Impossibile analizzare il file vCard", + "found": "{count, plural, one {1 contatto trovato} other {# contatti trovati}}", + "duplicate": "Duplicato", + "select_all": "Seleziona tutto", + "deselect_all": "Deseleziona tutto", + "selected": "{count, plural, one {1 selezionato} other {# selezionati}}", + "import_button": "Importa", + "importing": "Importazione...", + "success": "{count, plural, one {1 contatto importato} other {# contatti importati}}", + "failed": "Importazione fallita", + "close": "Chiudi", + "file_too_large": "Il file è troppo grande (max 5 MB)" + }, + "export": { + "title": "Esporta contatti", + "success": "{count, plural, one {1 contatto esportato} other {# contatti esportati}}" + }, + "bulk": { + "selected": "{count, plural, one {1 selezionato} other {# selezionati}}", + "select_all": "Seleziona tutto", + "delete": "Elimina", + "delete_confirm_title": "Elimina contatti", + "delete_confirm": "Eliminare {count, plural, one {1 contatto} other {# contatti}}?", + "deleted": "{count, plural, one {1 contatto eliminato} other {# contatti eliminati}}", + "add_to_group": "Aggiungi al gruppo", + "choose_group": "Scegli un gruppo", + "adding_contacts": "Aggiunta di {count, plural, one {1 contatto} other {# contatti}}", + "added_to_group": "Contatti aggiunti al gruppo", + "export": "Esporta", + "clear": "Cancella selezione" + }, + "toast": { + "created": "Contatto creato", + "updated": "Contatto aggiornato", + "deleted": "Contatto eliminato", + "error_create": "Impossibile creare il contatto", + "error_update": "Impossibile aggiornare il contatto", + "error_delete": "Impossibile eliminare il contatto" + }, + "context_menu": { + "open": "Apri", + "edit": "Modifica", + "send_email": "Invia email", + "add_to_group": "Aggiungi al gruppo", + "export_vcard": "Esporta come vCard", + "delete": "Elimina", + "call": "Chiama", + "duplicate": "Duplica", + "print": "Stampa" + }, + "filters": { + "toggle": "Filtri", + "select": "Seleziona", + "clear": "Azzera", + "close": "Chiudi", + "title": "Filtri avanzati", + "organization": "Azienda", + "organization_placeholder": "es. Acme", + "job_title": "Ruolo", + "job_title_placeholder": "es. Designer", + "location": "Luogo", + "location_placeholder": "Città o paese", + "email_domain": "Dominio email", + "email_domain_placeholder": "esempio.com", + "birthday_month": "Compleanno in", + "any_month": "Qualsiasi mese", + "has_email": "Con email", + "has_phone": "Con telefono", + "has_photo": "Con foto", + "sort_by": "Ordina per", + "sort_first_name": "Nome", + "sort_last_name": "Cognome" + }, + "open_categories": "Apri categorie" + }, + "calendar": { + "title": "Calendario", + "back_to_email": "Torna alla posta", + "back_to_month": "Torna al mese", + "my_calendars": "I miei calendari", + "birthday_calendar": "Compleanni", + "mini_calendar_change": "Clicca per cambiare mese", + "views": { + "month": "Mese", + "week": "Settimana", + "day": "Giorno", + "agenda": "Agenda", + "today": "Oggi", + "month_hint": "Mese (m)", + "week_hint": "Settimana (w)", + "day_hint": "Giorno (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Attività", + "tasks_hint": "Attività (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# evento precedente} other {# eventi precedenti}}", + "hidden_after": "{count, plural, one {# evento successivo} other {# eventi successivi}}", + "show_all_hours": "Mostra tutte le ore", + "show_display_hours": "Mostra solo {start} – {end}", + "create": "Crea evento", + "edit": "Modifica evento", + "delete": "Elimina evento", + "details": "Dettagli evento", + "no_events": "Nessun evento", + "all_day": "Tutto il giorno", + "more": "+{count} altri", + "show_more": "Mostra di più", + "show_less": "Mostra meno", + "no_title": "(Senza titolo)", + "resize": "Ridimensiona evento", + "duplicate": "Duplica", + "today_header": "Oggi", + "tomorrow_header": "Domani", + "agenda_show_earlier": "Mostra eventi precedenti", + "agenda_loading": "Caricamento…", + "agenda_range_start": "Eventi dal {date}", + "agenda_range_end": "Eventi fino al {date}", + "export_ics": "Esporta come .ics", + "copy_title": "Copia titolo", + "copy_link": "Copia link riunione", + "go_to_today": "Vai a oggi", + "new_all_day_event": "Nuovo evento giornata intera", + "new_event": "Nuovo evento", + "new_task": "Nuova attività" + }, + "detail": { + "add_note": "Aggiungi una nota...", + "save_note": "Salva", + "note_saved": "Nota aggiunta", + "open_link": "Apri link", + "copy_location": "Copia luogo", + "meeting_link": "Link riunione", + "tentative": "Provvisorio", + "cancelled": "Annullato", + "delete_confirm": "Eliminare questo evento?" + }, + "form": { + "title": "Titolo", + "description": "Descrizione", + "location": "Luogo", + "meeting_link": "Link riunione", + "start_date": "Data di inizio", + "end_date": "Data di fine", + "start_time": "Ora di inizio", + "end_time": "Ora di fine", + "all_day_event": "Evento giornata intera", + "calendar_select": "Calendario", + "save": "Salva", + "cancel": "Annulla", + "delete_confirm": "Sei sicuro di voler eliminare questo evento?", + "color": "Colore" + }, + "participants": { + "availability": { + "title": "Disponibilità", + "free": "Disponibile", + "busy": "Occupato", + "tentative": "Provvisorio", + "unknown": "Non è un utente di questo server", + "checking": "Verifica in corso…" + }, + "title": "Partecipanti", + "add": "Aggiungi partecipante", + "organizer": "Organizzatore", + "attendee": "Partecipante", + "accepted": "Accettato", + "declined": "Rifiutato", + "tentative": "Provvisorio", + "needs_action": "In attesa di risposta", + "remove": "Rimuovi", + "edit": "Modifica", + "email_placeholder": "Aggiungi indirizzo email o cerca contatti", + "send_invitations": "Invia inviti ai partecipanti", + "status_summary": "{accepted} accettato/i, {pending} in attesa", + "invited_by": "Invito da {name}", + "respond_below": "Rispondi con i pulsanti qui sotto", + "rsvp_label": "La tua risposta", + "cancel_notification": "I partecipanti saranno avvisati della cancellazione", + "you_organizer": "Sei l'organizzatore", + "you_attendee": "Sei un partecipante", + "no_participants": "Nessun partecipante", + "count": "{count, plural, one {# partecipante} other {# partecipanti}}" + }, + "recurrence": { + "title": "Ricorrenza", + "none": "Non si ripete", + "daily": "Giornaliero", + "weekly": "Settimanale", + "monthly": "Mensile", + "yearly": "Annuale", + "every_n_days": "Ogni {count} giorni", + "every_n_weeks": "Ogni {count} settimane", + "every_n_months": "Ogni {count} mesi", + "until": "Fino al", + "occurrences": "{count} ripetizioni", + "custom": "Personalizzato…", + "edit_custom": "Modifica ricorrenza personalizzata", + "every_n_years": "Ogni {count} anni", + "on_days": "di {days}", + "on_day_n": "il giorno {day}", + "on_the_nth": "il {nth} {day}", + "in_month": "a {month}", + "nth_1": "primo", + "nth_2": "secondo", + "nth_3": "terzo", + "nth_4": "quarto", + "nth_last": "ultimo", + "editor_freq_day": "Giorno", + "editor_freq_week": "Settimana", + "editor_freq_month": "Mese", + "editor_freq_year": "Anno", + "editor_repeats_on": "Si ripete", + "editor_every": "Ogni", + "editor_unit_days": "giorno/i", + "editor_unit_weeks": "settimana/e", + "editor_unit_months": "mese/i", + "editor_unit_years": "anno/i", + "editor_on_day": "il giorno", + "editor_on_the": "il", + "editor_in": "a", + "editor_ends": "Termina", + "editor_never": "Mai", + "editor_ends_on": "Il", + "editor_ends_after": "Dopo", + "editor_occurrences": "occorrenze" + }, + "recurrence_scope": { + "edit_title": "Modifica evento ricorrente", + "delete_title": "Elimina evento ricorrente", + "description": "Questo è un evento ricorrente. Quali eventi desideri modificare?", + "this_event": "Solo questo evento", + "this_and_future": "Questo evento e i seguenti", + "all_events": "Tutti gli eventi", + "cancel": "Annulla", + "save": "Salva", + "delete": "Elimina", + "rsvp_title": "Rispondi a evento ricorrente", + "rsvp_description": "Questo è un evento ricorrente. A quali eventi si applica la tua risposta?", + "respond": "Rispondi" + }, + "alerts": { + "title": "Promemoria", + "none": "Nessun promemoria", + "at_time": "All'ora dell'evento", + "minutes_before": "{count, plural, one {# minuto prima} other {# minuti prima}}", + "hours_before": "{count, plural, one {# ora prima} other {# ore prima}}", + "days_before": "{count, plural, one {# giorno prima} other {# giorni prima}}", + "weeks_before": "{count, plural, one {# settimana prima} other {# settimane prima}}", + "unit_minutes_before": "minuti prima", + "unit_hours_before": "ore prima", + "unit_days_before": "giorni prima", + "unit_weeks_before": "settimane prima", + "add": "Aggiungi promemoria", + "remove": "Rimuovi promemoria", + "amount": "Quantità promemoria", + "unit": "Unità promemoria", + "action": "Tipo di promemoria", + "action_display": "Notifica", + "action_email": "Email", + "by_email": "{label} via email", + "kept": "{count, plural, one {# altro promemoria non modificabile qui viene mantenuto} other {Altri # promemoria non modificabili qui vengono mantenuti}}", + "other": "{count, plural, one {# altro promemoria} other {Altri # promemoria}}", + "calendar_defaults": "Promemoria predefiniti del calendario", + "customize": "Personalizza", + "use_defaults": "Usa i promemoria predefiniti del calendario" + }, + "settings": { + "limit_hours": "Limita le ore visibili", + "limit_hours_desc": "Mostra solo queste ore nelle viste giorno e settimana. Gli eventi al di fuori restano raggiungibili con le frecce in alto e in basso di ogni giorno.", + "visible_hours": "Ore visibili", + "visible_hours_start": "Inizio delle ore visibili", + "visible_hours_end": "Fine delle ore visibili", + "hide_non_working_days": "Nascondi i giorni non lavorativi", + "hide_non_working_days_desc": "Togli dalla vista settimanale i giorni in cui non lavori. I loro eventi restano visibili nelle viste mese, giorno e agenda.", + "working_days": "Giorni lavorativi", + "title": "Impostazioni calendario", + "default_view": "Vista predefinita", + "week_starts_on": "La settimana inizia il", + "time_format": "Formato ora", + "default_calendar": "Calendario predefinito", + "default_reminder": "Promemoria predefinito", + "time_format_12h": "12 ore", + "time_format_24h": "24 ore", + "notifications_enabled": "Notifiche eventi", + "notifications_enabled_desc": "Mostra avvisi per gli eventi in arrivo", + "notification_sound": "Suono di notifica", + "notification_sound_desc": "Riproduci un suono per gli avvisi del calendario", + "invitation_parsing": "Analizza gli inviti email", + "invitation_parsing_desc": "Rileva gli inviti del calendario negli allegati email e mostra le azioni del calendario", + "show_time_in_month_view": "Mostra orario nella vista mensile", + "show_time_in_month_view_desc": "Visualizza gli orari degli eventi nella vista mensile del calendario", + "show_week_numbers": "Mostra numeri di settimana", + "show_week_numbers_desc": "Mostra i numeri di settimana nel mini-calendario", + "calendar_free_scroll": "Scorrimento libero", + "calendar_free_scroll_desc": "Scorri in modo continuo tra mesi, settimane e giorni invece di un periodo alla volta", + "enable_tasks": "Abilita attività", + "enable_tasks_desc": "Mostra una vista attività nel calendario per gestire le cose da fare", + "show_tasks_on_calendar": "Mostra attività nel calendario", + "show_tasks_on_calendar_desc": "Mostra le attività nelle viste mensile, settimanale e giornaliera del calendario", + "hover_preview": "Anteprima al passaggio del mouse", + "hover_preview_desc": "Mostra un popover dettagliato al passaggio del mouse sugli eventi del calendario", + "hover_preview_instant": "Istantaneo", + "hover_preview_delay_500ms": "Ritardo di 0,5 secondi", + "hover_preview_delay_1s": "Ritardo di 1 secondo", + "hover_preview_delay_2s": "Ritardo di 2 secondi", + "hover_preview_off": "Disabilitato", + "organizer_identity": "Organizza gli inviti come", + "organizer_identity_desc": "L'indirizzo del calendario usato come organizzatore quando inviti persone agli eventi.", + "organizer_identity_failed": "Impossibile cambiare l'indirizzo dell'organizzatore", + "show_birthday_calendar": "Calendario compleanni", + "show_birthday_calendar_desc": "Mostra un calendario virtuale con i compleanni dei tuoi contatti" + }, + "days": { + "monday": "Lunedì", + "tuesday": "Martedì", + "wednesday": "Mercoledì", + "thursday": "Giovedì", + "friday": "Venerdì", + "saturday": "Sabato", + "sunday": "Domenica", + "mon": "Lun", + "tue": "Mar", + "wed": "Mer", + "thu": "Gio", + "fri": "Ven", + "sat": "Sab", + "sun": "Dom" + }, + "notifications": { + "event_created": "Evento creato", + "event_updated": "Evento aggiornato", + "event_deleted": "Evento eliminato", + "calendar_created": "Calendario creato", + "calendar_deleted": "Calendario eliminato", + "event_move_error": "Spostamento dell'evento non riuscito", + "event_resize_error": "Ridimensionamento dell'evento non riuscito", + "alert_title": "Evento in arrivo", + "alert_now": "Inizia ora", + "alert_in_minutes": "Tra {count} min", + "invitation_sent": "Inviti inviati", + "invitations_denied_title": "Impossibile inviare gli inviti", + "invitations_denied": "Il server ha rifiutato di inviare gli inviti: {reason}. Salvare l'evento senza inviarli?", + "save_without_invitations": "Salva senza inviti", + "rsvp_updated": "Risposta aggiornata", + "rsvp_error": "Impossibile aggiornare la risposta", + "event_duplicated": "Evento duplicato", + "event_error": "Salvataggio dell'evento non riuscito", + "task_due": "Attività in scadenza", + "event_exported": "Evento esportato", + "title_copied": "Titolo copiato", + "link_copied": "Link copiato" + }, + "status": { + "loading_calendars": "Caricamento calendari...", + "loading_events": "Caricamento eventi..." + }, + "quick_create": { + "placeholder": "Titolo del nuovo evento", + "aria_label": "Creazione rapida evento", + "add_title": "Aggiungi titolo", + "more_options": "Altre opzioni" + }, + "nav_prev": "Precedente", + "nav_next": "Successivo", + "import": { + "title": "Importa calendario", + "tab_file": "File", + "tab_url": "URL", + "select_file": "Seleziona file .ics", + "drop_file": "o trascina il file qui", + "supported_formats": "File iCalendar (.ics) supportati", + "url_description": "Inserisci l'URL di un feed iCalendar (.ics) esterno per importare eventi.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Supporta URL iCalendar (.ics)", + "fetch": "Recupera", + "invalid_url": "Inserisci un URL valido", + "url_fetch_failed": "Impossibile recuperare il calendario dall'URL", + "parsing": "Analisi del file in corso...", + "parsed_events": "{count} eventi trovati", + "no_events": "Nessun evento trovato nel file", + "select_all": "Seleziona tutto", + "deselect_all": "Deseleziona tutto", + "target_calendar": "Importa nel calendario", + "import_button": "Importa selezionati", + "importing": "Importazione eventi...", + "success": "{count} eventi importati con successo", + "error": "Importazione del calendario fallita", + "file_too_large": "Il file supera il limite di 10 MB", + "invalid_format": "Formato del file calendario non valido" + }, + "webcal_action": { + "title": "Apri link calendario", + "description": "Come vuoi usare \"{name}\"?", + "import_title": "Importa una volta", + "import_description": "Recupera subito gli eventi e copiali in uno dei tuoi calendari.", + "subscribe_title": "Abbonati", + "subscribe_description": "Mantieni questo calendario sincronizzato automaticamente come calendario separato.", + "cancel": "Annulla" + }, + "management": { + "title": "Gestione calendari", + "description": "Crea, rinomina e personalizza i tuoi calendari. Fai clic destro su un calendario nella barra laterale per cambiarne rapidamente il colore.", + "name": "Nome", + "name_placeholder": "Nome del calendario", + "color": "Colore", + "change_color": "Cambia colore", + "random_color": "Nuovo colore casuale", + "add_calendar": "Aggiungi calendario", + "edit": "Modifica", + "delete": "Elimina", + "save": "Salva", + "create": "Crea", + "cancel": "Annulla", + "default": "Predefinito", + "confirm_delete": "Eliminare \"{name}\"? Tutti gli eventi in questo calendario verranno rimossi.", + "calendar_created": "Calendario creato", + "calendar_updated": "Calendario aggiornato", + "calendar_deleted": "Calendario eliminato", + "color_updated": "Colore del calendario aggiornato", + "error_create": "Impossibile creare il calendario", + "error_update": "Impossibile aggiornare il calendario", + "error_delete": "Impossibile eliminare il calendario", + "caldav_url": "URL CalDAV", + "copy_url": "Copia URL CalDAV", + "url_copied": "URL CalDAV copiato negli appunti", + "confirm_clear": "Cancellare tutti gli eventi da \"{name}\"? Questa azione non può essere annullata.", + "set_default": "Imposta come predefinito", + "default_updated": "Calendario predefinito aggiornato", + "error_default": "Impossibile impostare il calendario predefinito", + "clear_events": "Cancella eventi", + "events_cleared": "{count} eventi cancellati", + "error_clear": "Impossibile cancellare gli eventi del calendario", + "share": "Condividi calendario", + "new_event_in_calendar": "Nuovo evento in questo calendario", + "shared_with": "Condiviso con {names}", + "shared_with_count": "{count, plural, one {Condiviso con 1 persona} other {Condiviso con # persone}}", + "move_up": "Sposta su", + "move_down": "Sposta giù", + "kind": "Contenuto", + "kind_events": "Eventi", + "kind_tasks": "Attività", + "kind_both": "Eventi e attività", + "kind_hint": "Determina quali app vedono questo calendario quando viene sincronizzato tramite CalDAV. Non può essere modificato in seguito.", + "default_alerts": "Promemoria predefiniti", + "default_alerts_title": "Promemoria predefiniti per \"{name}\"", + "default_alerts_hint": "I nuovi eventi di questo calendario ricevono questi promemoria, a meno che non vengano modificati nell'evento. Modificarli qui non cambia gli eventi esistenti. I promemoria via email vengono inviati dal server di posta.", + "default_alerts_timed": "Eventi con orario", + "default_alerts_all_day": "Eventi di tutto il giorno", + "default_alerts_updated": "Promemoria predefiniti aggiornati" + }, + "subscription": { + "title": "Abbonamento iCal", + "section_title": "Abbonamenti iCal", + "description": "Abbonati a un feed iCalendar esterno. Gli eventi verranno sincronizzati automaticamente nel proprio calendario. Supporta URL https:// e webcal://.", + "url_label": "URL del calendario", + "url_placeholder": "https://example.com/calendar.ics o webcal://...", + "name_label": "Nome del calendario", + "name_placeholder": "es. Festività", + "color_label": "Colore", + "refresh_interval": "Intervallo di aggiornamento", + "interval_15": "Ogni 15 minuti", + "interval_30": "Ogni 30 minuti", + "interval_60": "Ogni ora", + "interval_360": "Ogni 6 ore", + "interval_1440": "Ogni giorno", + "subscribe": "Abbonati", + "subscribing": "Abbonamento in corso...", + "save": "Salva modifiche", + "saving": "Salvataggio...", + "edit": "Modifica", + "edit_title": "Modifica abbonamento", + "updated": "\"{name}\" aggiornato", + "update_error": "Impossibile aggiornare l'abbonamento", + "invalid_url": "Inserisci un URL valido", + "success": "Abbonato a \"{name}\"", + "error": "Impossibile aggiungere l'abbonamento", + "refresh": "Aggiorna ora", + "refresh_success": "Abbonamento aggiornato", + "refresh_error": "Impossibile aggiornare l'abbonamento", + "unsubscribe": "Annulla abbonamento", + "confirm_delete": "Annullare l'abbonamento a \"{name}\"? Il calendario e tutti i suoi eventi verranno rimossi.", + "deleted": "Abbonamento rimosso", + "delete_error": "Impossibile rimuovere l'abbonamento", + "last_refreshed": "Ultimo aggiornamento: {time}" + }, + "tasks": { + "label": "Attività", + "no_tasks": "Nessuna attività", + "no_title": "(Senza titolo)", + "mark_complete": "Segna come completata", + "mark_incomplete": "Segna come non completata", + "filter_all": "Tutte", + "filter_pending": "In sospeso", + "filter_completed": "Completate", + "filter_overdue": "Scadute", + "show_completed": "Mostra completate", + "create": "Nuova attività", + "edit": "Modifica attività", + "title_placeholder": "Titolo attività", + "description_placeholder": "Aggiungi una descrizione...", + "due_date": "Data di scadenza", + "include_time": "Includi ora", + "priority": "Priorità", + "priority_none": "Nessuna", + "priority_high": "Alta", + "priority_medium": "Media", + "priority_low": "Bassa", + "progress": "Stato", + "progress_needs_action": "Azione richiesta", + "progress_in_process": "In corso", + "progress_completed": "Completata", + "progress_cancelled": "Annullata", + "calendar": "Calendario", + "alert": "Promemoria", + "alert_none": "Nessuno", + "alert_at_time": "Alla data e ora di scadenza", + "alert_5min": "5 minuti prima", + "alert_15min": "15 minuti prima", + "alert_30min": "30 minuti prima", + "alert_1hr": "1 ora prima", + "alert_1day": "1 giorno prima", + "delete": "Elimina", + "cancel": "Annulla", + "save": "Salva", + "quick_add_placeholder": "Aggiungi un'attività...", + "due_today": "Scade oggi", + "due_tomorrow": "Scade domani", + "overdue": "Scaduta" + }, + "months": { + "jan": "gen", + "feb": "feb", + "mar": "mar", + "apr": "apr", + "may": "mag", + "jun": "giu", + "jul": "lug", + "aug": "ago", + "sep": "set", + "oct": "ott", + "nov": "nov", + "dec": "dic", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Apri menu", + "toolbar": { + "more": "Altre azioni", + "select_view": "Cambia vista, attualmente {view}" + }, + "other_calendars": "Altri calendari", + "calendar_options": "Opzioni per {name}" + }, + "advanced_search": { + "title": "Ricerca avanzata", + "from": "Da", + "from_placeholder": "Email o nome del mittente", + "to": "A", + "to_placeholder": "Email o nome del destinatario", + "subject": "Oggetto", + "subject_placeholder": "L'oggetto contiene...", + "body": "Corpo", + "body_placeholder": "Il corpo contiene...", + "folder": "Cartella", + "all_folders": "Tutte le cartelle", + "all_folders_except_spam_trash": "Tutte le cartelle tranne Spam e Cestino", + "has_attachment": "Allegati", + "date_after": "Dopo il", + "date_before": "Prima del", + "size_min": "Maggiore di (KB)", + "size_max": "Minore di (KB)", + "starred": "Preferiti", + "unread": "Non letti", + "read": "Letti", + "yes": "Sì", + "no": "No", + "clear": "Cancella", + "clear_all": "Cancella tutto", + "filters_active": "{count} filtro", + "filters_active_plural": "{count} filtri", + "toggle_filters": "Filtri", + "search_hint": "Usa i filtri avanzati per una ricerca precisa", + "advanced_filters_tooltip": "Filtri di ricerca avanzati", + "results_found": "{count, plural, =0 {Nessun risultato trovato} one {# risultato trovato} other {# risultati trovati}}", + "results_found_more": "{count}+ risultati trovati", + "suggestions_recent": "Ricerche recenti", + "suggestions_people": "Persone", + "suggestions_from": "Cerca email da {address}", + "suggestions_to": "Cerca email a {address}", + "suggestions_remove_recent": "Rimuovi dalle ricerche recenti", + "has_words": "Contiene le parole", + "size": "Dimensione", + "date_within": "Data", + "search_in": "Cerca in", + "has_attachment_check": "Con allegati", + "search": "Cerca" + }, + "welcome": { + "title": "Benvenuto nella tua casella di posta", + "tip_compose": "Premi c per scrivere una nuova email", + "tip_shortcuts": "Premi ? per vedere tutte le scorciatoie da tastiera", + "tip_sidebar": "Trova Calendario, Contatti e Impostazioni nel menu laterale", + "tip_settings": "Personalizza la tua esperienza nelle Impostazioni", + "tip_compose_touch": "Tocca il pulsante a matita per scrivere una nuova email", + "tip_sidebar_touch": "Calendario, Contatti e Impostazioni sono nella barra in basso", + "got_it": "Ho capito", + "settings": "Impostazioni", + "dismiss": "Chiudi", + "start_tour": "Inizia il tour" + }, + "demo_welcome": { + "title": "Benvenuto su Bulwark Mail", + "description": "Esplora un client webmail completo - direttamente nel tuo browser. Tutti i dati restano sul tuo dispositivo, quindi prova tutto.", + "feature_email": "Leggere e scrivere email", + "feature_organize": "Tag, stelle e cartelle", + "feature_shortcuts": "Scorciatoie da tastiera", + "feature_privacy": "Demo 100% privata", + "hint": "Clicca su un'email a sinistra per iniziare, oppure fai il tour." + }, + "files": { + "office_edit": "Modifica documento", + "office_loading": "Apertura dell'editor di documenti…", + "office_error": "Impossibile aprire l'editor di documenti.", + "office_close": "Chiudi editor", + "open_in_new_tab": "Apri in una nuova scheda", + "title": "File", + "search_placeholder": "Cerca file...", + "empty_state_title": "Nessun file ancora", + "upload_files": "Carica file", + "new_folder": "Nuova cartella", + "new_folder_name": "Nome cartella", + "rename": "Rinomina", + "rename_title": "Rinomina", + "new_name": "Nuovo nome", + "name_forbidden_chars": "Il server non consente questi caratteri nei nomi: {chars}", + "name_reserved": "Questo nome è riservato sul server.", + "delete": "Elimina", + "delete_confirm_title": "Elimina risorsa", + "delete_confirm_message": "Sei sicuro di voler eliminare \"{name}\"? Questa azione non può essere annullata.", + "download": "Scarica", + "name": "Nome", + "size": "Dimensione", + "modified": "Modificato", + "type": "Tipo", + "folder": "Cartella", + "file": "File", + "uploading": "Caricamento in corso...", + "upload_success": "{count, plural, one {1 file caricato} other {# file caricati}}", + "upload_canceled": "Caricamento annullato", + "upload_canceled_partial": "{count, plural, one {1 file era già stato caricato} other {# file erano già stati caricati}} prima dell'annullamento", + "upload_error": "Caricamento del file non riuscito", + "create_folder_success": "Cartella creata", + "create_folder_error": "Creazione della cartella non riuscita", + "delete_success": "Eliminato con successo", + "delete_error": "Eliminazione non riuscita", + "rename_success": "Rinominato con successo", + "rename_error": "Rinominazione non riuscita", + "download_error": "Download non riuscito", + "not_available": "L'archiviazione file non è disponibile su questo server", + "cancel": "Annulla", + "create": "Crea", + "save": "Salva", + "no_results": "Nessun file corrisponde alla ricerca", + "batch_delete_confirm_message": "Sei sicuro di voler eliminare {count, plural, one {1 elemento} other {# elementi}}? Questa azione non può essere annullata.", + "batch_delete_success": "{count, plural, one {1 elemento eliminato} other {# elementi eliminati}}", + "grid_view": "Vista a griglia", + "list_view": "Vista a elenco", + "details": "Dettagli", + "path": "Percorso", + "preview": "Anteprima", + "preview_error": "Caricamento dell'anteprima non riuscito", + "cut": "Taglia", + "copy": "Copia", + "paste": "Incolla", + "move_success": "{count, plural, one {1 elemento spostato} other {# elementi spostati}}", + "move_error": "Spostamento non riuscito", + "paste_success": "Incollato con successo", + "paste_error": "Incollaggio non riuscito", + "new_text_file": "Nuovo file di testo", + "file_name": "Nome del file", + "retry": "Riprova", + "refresh": "Aggiorna", + "duplicate": "Duplica", + "duplicate_success": "Duplicato con successo", + "duplicate_error": "Duplicazione non riuscita", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "File creato", + "create_file_error": "Creazione del file non riuscita", + "favorites": "Preferiti", + "recent": "Recenti", + "show_in_folder": "Mostra nella cartella", + "remove_from_recent": "Rimuovi dai recenti", + "upload_folder": "Carica cartella", + "file_too_large": "\"{name}\" supera la dimensione massima del file ({max})", + "undo": "Annulla", + "undo_success": "Azione annullata", + "undo_error": "Annullamento non riuscito", + "toolbar": "Azioni file", + "file_list": "File e cartelle", + "context_menu": "Azioni", + "settings_ascending": "Crescente", + "settings_descending": "Decrescente", + "disabled_title": "La funzionalità File è stata disabilitata dal tuo amministratore", + "disabled_description": "I caricamenti di file di grandi dimensioni tramite WebDAV possono causare instabilità di Stalwart/RocksDB, inclusi crash di memoria e utilizzo irrecuperabile del disco. I file eliminati potrebbero non essere immediatamente rimossi dallo storage. Questa funzionalità non è consigliata per ambienti di produzione.", + "no_accounts": "Nessun account collegato.", + "open_folder_tree": "Apri albero cartelle", + "migration_title": "Aggiornamento dei tuoi file…", + "migration_description": "Organizzazione di cartelle e file nella loro struttura corretta. Questo avviene solo una volta.", + "new": "Nuovo", + "my_files": "I miei file", + "folders": "Cartelle", + "files_heading": "File", + "open": "Apri", + "star": "Aggiungi ai preferiti", + "unstar": "Rimuovi dai preferiti", + "expand_folder": "Espandi {name}", + "collapse_folder": "Comprimi {name}", + "sidebar_hint": "Aggiungi cartelle ai preferiti e apri file per ritrovarli qui.", + "breadcrumbs": "Percorso della cartella", + "show_path": "Mostra le cartelle nascoste nel percorso", + "selection_actions": "Azioni per gli elementi selezionati", + "selected_count": "{count, plural, one {1 selezionato} other {# selezionati}}", + "clear_selection": "Annulla selezione", + "select_all": "Seleziona tutto", + "select_item": "Seleziona {name}", + "more_actions": "Altre azioni per {name}", + "paste_here": "Incolla in questa cartella", + "paste_count": "{count, plural, one {Incolla 1 elemento} other {Incolla # elementi}}", + "sort_by": "Ordina per", + "view_mode": "Visualizzazione", + "clear_search": "Cancella ricerca", + "no_results_for": "Nessun elemento in questa cartella corrisponde a \"{query}\"", + "download_count": "{count, plural, one {Scarica 1 file} other {Scarica # file}}", + "delete_count": "{count, plural, one {Elimina 1 elemento} other {Elimina # elementi}}", + "close_details": "Chiudi dettagli", + "file_kind": "File {ext}", + "folder_summary": "{folders, plural, =0 {Nessuna cartella} one {1 cartella} other {# cartelle}}, {files, plural, =0 {nessun file} one {1 file} other {# file}}", + "details_hint": "Seleziona un file o una cartella per vederne i dettagli.", + "sharing": "Condivisione", + "shared_with_count": "{count, plural, one {Condiviso con 1 persona} other {Condiviso con # persone}}", + "only_you": "Solo tu", + "empty_folder_title": "Questa cartella è vuota", + "empty_folder_description": "Trascina qui i file o caricali dal tuo dispositivo.", + "empty_folder_read_only": "Non puoi aggiungere file a questa cartella.", + "choose_account": "Scegli un account", + "drop_to_upload": "Rilascia per caricare in {folder}", + "uploading_count": "Caricamento {current} di {total}", + "dismiss": "Chiudi", + "zoom_in": "Ingrandisci", + "zoom_out": "Riduci", + "zoom_reset": "Adatta allo schermo", + "rotate": "Ruota", + "close_preview": "Chiudi" + }, + "smime": { + "your_certificates": "I tuoi certificati", + "your_certificates_desc": "Importa e gestisci i tuoi certificati S/MIME per firmare e cifrare le email", + "recipient_certificates": "Certificati dei destinatari", + "recipient_certificates_desc": "Certificati pubblici per cifrare le email ai destinatari", + "identity_bindings": "Associazioni identità-chiave", + "identity_bindings_desc": "Associa i certificati S/MIME alle tue identità email", + "defaults_title": "Predefiniti", + "defaults_desc": "Configura il comportamento predefinito di firma e cifratura", + "import_pkcs12": "Importa PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importa certificato", + "no_certificates": "Nessun certificato importato", + "no_recipient_certs": "Nessun certificato destinatario", + "expires": "Scade", + "expired": "Scaduto", + "bound_to": "Associato a", + "no_key_bound": "Nessuno", + "lock": "Blocca chiave", + "unlock": "Sblocca chiave", + "details": "Visualizza dettagli", + "delete": "Elimina", + "encrypt_by_default": "Cifra per impostazione predefinita", + "encrypt_by_default_desc": "Cifra automaticamente le email quando tutti i destinatari hanno un certificato", + "remember_unlocked": "Ricorda le chiavi sbloccate", + "remember_unlocked_desc": "Mantieni le chiavi sbloccate per la durata di questa sessione del browser", + "sign_default_for": "Firma per impostazione predefinita per", + "enter_p12_passphrase": "Inserisci la passphrase PKCS#12", + "p12_passphrase_desc": "Inserisci la passphrase che protegge questo file di certificato", + "enter_storage_passphrase": "Imposta la passphrase di archiviazione", + "storage_passphrase_desc": "Scegli una passphrase per proteggere questa chiave nel browser", + "next": "Avanti", + "import": "Importa", + "unlock_key": "Sblocca chiave", + "unlock_key_desc": "Inserisci la passphrase di archiviazione per sbloccare questa chiave per la firma o la decrittazione", + "passphrase_placeholder": "Inserisci la passphrase", + "confirm_passphrase_placeholder": "Conferma la passphrase", + "passphrase_mismatch": "Le passphrase non corrispondono", + "cancel": "Annulla", + "processing": "Elaborazione…", + "close": "Chiudi", + "certificate_details": "Dettagli del certificato", + "cert_subject": "Soggetto", + "cert_issuer": "Emittente", + "cert_email": "Email", + "cert_serial": "Numero di serie", + "cert_validity": "Validità", + "cert_fingerprint": "Impronta digitale (SHA-256)", + "cert_algorithm": "Algoritmo", + "cert_capabilities": "Capacità", + "cert_source": "Origine", + "cert_expired": "Questo certificato è scaduto", + "cert_not_yet_valid": "Questo certificato non è ancora valido", + "cap_sign": "Firma", + "cap_encrypt": "Cifratura", + "cap_none": "Nessuna", + "show_passphrase": "Mostra passphrase", + "hide_passphrase": "Nascondi passphrase", + "sign_toggle": "Firma", + "encrypt_toggle": "Cifra", + "missing_recipient_certs": "Certificati mancanti per: {emails}", + "missing_sender_cert": "Nessun certificato associato a questa identità", + "status_encrypted_ok": "Questo messaggio è stato cifrato", + "status_encrypted_no_key": "Questo messaggio è cifrato, ma non è stata trovata una chiave corrispondente", + "status_encrypted_failed": "Impossibile decrittare questo messaggio", + "status_signed_valid": "Firma verificata", + "status_signed_invalid": "Verifica della firma non riuscita", + "status_signed_expired_cert": "Firmato con un certificato scaduto", + "status_signed_self_signed": "Firma valida, ma il certificato è autofirmato (non attendibile)", + "status_signed_mismatch": "La firma è valida, ma il firmatario non corrisponde al mittente", + "status_unsupported": "Formato S/MIME non supportato", + "auto_import_signer_certs": "Importa automaticamente i certificati dei firmatari", + "auto_import_signer_certs_desc": "Salva automaticamente i certificati delle email firmate verificate per future cifrature", + "export": "Esporta", + "enter_export_passphrase": "Imposta la passphrase di esportazione", + "export_passphrase_desc": "Scegli una passphrase per proteggere il file PKCS#12 esportato", + "export_storage_desc": "Inserisci la passphrase di archiviazione per decrittare la chiave prima dell'esportazione", + "incorrect_passphrase": "Passphrase errata" + }, + "tour": { + "step_counter": "Passo {current} di {total}", + "skip": "Salta il tour", + "back": "Indietro", + "next": "Avanti", + "finish": "Fine", + "take_a_tour": "Fai un tour dell'interfaccia", + "restart_title": "Tour introduttivo", + "restart_desc": "Rivedi la guida dell'interfaccia", + "restart_button": "Riavvia il tour", + "show_on_new_devices_title": "Mostra sui nuovi dispositivi", + "show_on_new_devices_desc": "Rivedi il banner di benvenuto e il tour al primo accesso su un nuovo dispositivo, anche se li hai già completati altrove", + "sidebar_title": "Le tue caselle di posta", + "sidebar_desc": "Questa è la barra laterale delle cartelle. Clicca su una casella per vedere le email. Puoi creare cartelle, trascinare email tra loro e vedere i conteggi dei non letti.", + "compose_title": "Scrivi un'email", + "compose_desc": "Clicca qui per scrivere una nuova email. Puoi aggiungere destinatari, allegati e usare la formattazione RTF.", + "search_title": "Cerca nella posta", + "search_desc": "Cerca per mittente, oggetto o contenuto. Clicca sull'icona del filtro per opzioni avanzate come intervallo di date, allegati e messaggi speciali.", + "email_list_title": "La tua lista email", + "email_list_desc": "Le email appaiono qui. Clicca su una per leggerla a destra. Usa la casella di controllo per selezionarne più di una, poi sposta, elimina o etichetta in blocco.", + "email_viewer_title": "Pannello di lettura", + "email_viewer_desc": "L'email selezionata si apre qui. Rispondi, inoltra, archivia o elimina con i pulsanti della barra degli strumenti. Puoi anche contrassegnare le email o aggiungere etichette colorate.", + "keywords_title": "Etichette colorate", + "keywords_desc": "Organizza le tue email con etichette colorate. Trascina un'email su un'etichetta per contrassegnarla, o fai clic destro per assegnare etichette.", + "calendar_title": "Calendario", + "calendar_desc": "Passa al calendario per gestire i tuoi eventi. Crea eventi, imposta promemoria e visualizza le viste giorno, settimana o mese.", + "contacts_title": "Contatti", + "contacts_desc": "La tua rubrica si trova qui. Importa contatti, crea gruppi e clicca su un contatto per vedere i suoi dettagli completi.", + "settings_title": "Impostazioni", + "settings_desc": "Personalizza tutto: tema, densità, firme, filtri, scorciatoie da tastiera, impostazioni del calendario e altro ancora.", + "shortcuts_title": "Scorciatoie da tastiera", + "shortcuts_desc": "Gli utenti esperti adorano questo. Premi ? in qualsiasi momento per vedere tutte le scorciatoie disponibili. Puoi navigare, comporre e gestire le email senza toccare il mouse.", + "compose_open_title": "L'editor", + "compose_open_desc": "Questo è l'editor di email. Aggiungi destinatari, scrivi il tuo messaggio, allega file e usa la formattazione del testo. Puoi anche salvare bozze e usare modelli.", + "calendar_view_title": "Il tuo calendario", + "calendar_view_desc": "Ecco il tuo calendario con eventi di esempio. Puoi passare tra le viste giorno, settimana, mese e agenda usando la barra degli strumenti.", + "create_event_title": "Crea un evento", + "create_event_desc": "Fai clic su questo pulsante per creare un nuovo evento del calendario. Puoi impostare un titolo, data, ora e aggiungere partecipanti.", + "event_modal_title": "Dettagli evento", + "event_modal_desc": "Ecco il modulo dell'evento. Inserisci il titolo, scegli data e ora, aggiungi un luogo o partecipanti. Premi salva quando hai finito - o chiudilo e vai avanti.", + "contacts_list_title": "I tuoi contatti", + "contacts_list_desc": "Ecco i tuoi contatti. Clicca su un contatto per vedere i suoi dettagli a destra. Puoi anche creare nuovi contatti, importare vCard o organizzare i contatti in gruppi.", + "files_title": "Archiviazione file", + "settings_tabs_title": "Menu impostazioni", + "settings_tabs_desc": "Ecco tutte le categorie di impostazioni. Personalizza l'aspetto, gestisci le identità, configura i filtri email, imposta il calendario e molto altro.", + "files_desc": "Il tuo file browser ti permette di caricare, organizzare e condividere file - come un cloud personale integrato nella tua posta.", + "demo_banner_title": "Controlli demo", + "demo_banner_desc": "Sei in modalità demo - tutto rimane nel tuo browser. Premi 'Reimposta Demo' in qualsiasi momento per ricominciare con dati puliti.", + "quota_title": "Utilizzo dello spazio", + "quota_desc": "Monitora le dimensioni della tua casella qui. Il cerchio si riempie man mano che utilizzi più spazio." + }, + "unified_mailbox": { + "search_unavailable": "La ricerca non è disponibile nella vista unificata" + }, + "calendar_event_notifications": { + "someone": "Qualcuno", + "invited": "{name} ti ha invitato a \"{title}\"", + "updated": "{name} ha aggiornato \"{title}\"", + "cancelled": "{name} ha annullato \"{title}\"", + "untitled": "Evento senza titolo" + }, + "share_notifications": { + "someone": "Qualcuno", + "shared": "{name} ha condiviso con te {kind} \"{object}\"", + "changed": "{name} ha modificato il tuo accesso a {kind} \"{object}\"", + "revoked": "{name} ha rimosso il tuo accesso a {kind} \"{object}\"", + "object": { + "folder": "la cartella", + "calendar": "il calendario", + "address_book": "la rubrica", + "files": "la cartella dei file" + } + }, + "sharing": { + "title": "Condividi \"{name}\"", + "description": "Concedi l'accesso ad altri utenti o gruppi su questo server. Le modifiche hanno effetto immediato.", + "no_shares": "Non ancora condiviso.", + "add_person": "Aggiungi persona o gruppo", + "search_placeholder": "Cerca per nome o email…", + "loading_principals": "Caricamento utenti…", + "no_principals": "Nessun altro utente o gruppo trovato.", + "no_match": "Nessun risultato.", + "remove": "Rimuovi accesso", + "group": "Gruppo", + "share_added": "Accesso concesso", + "share_updated": "Accesso aggiornato", + "share_removed": "Accesso rimosso", + "share_failed": "Impossibile aggiornare la condivisione", + "preset": { + "freeBusy": "Solo libero/occupato", + "read": "Sola lettura", + "readWrite": "Lettura e scrittura", + "manager": "Gestore", + "custom": "Personalizzato" + } + }, + "quote_header": { + "reply_line": "Il {date}, {from} ha scritto:", + "forwarded_separator": "---------- Messaggio inoltrato ----------", + "from_label": "Da", + "date_label": "Data", + "subject_label": "Oggetto" + }, + "pwa_install": { + "title": "Installa {appName}", + "description": "Installa la nostra app per un accesso rapido e il supporto offline.", + "not_now": "Non ora", + "install": "Installa", + "dont_remind": "Non ricordarmelo più", + "dismiss_aria": "Chiudi avviso di installazione" + }, + "deep_link": { + "message_not_found": "Questo messaggio non è più disponibile.", + "thread_not_found": "Questa conversazione non è più disponibile.", + "folder_not_found": "Questa cartella non è più disponibile.", + "event_not_found": "Questo evento non è più disponibile.", + "file_not_found": "Questo file non è più disponibile.", + "account_unavailable": "Accedi a quell'account per aprire questo link.", + "copy_message": "Copia link al messaggio", + "copy_conversation": "Copia link alla conversazione", + "copy_event": "Copia link all'evento", + "copied": "Link copiato", + "copy_failed": "Impossibile copiare il link" + }, + "plugins": { + "oauth_callback": { + "completing": "Completamento dell'autorizzazione…", + "completing_message": "Attendi mentre viene stabilita la connessione.", + "received_title": "Autorizzazione ricevuta", + "received_message": "Puoi tornare alla scheda in cui hai avviato la connessione.", + "failed_title": "Autorizzazione non riuscita", + "denied": "Lautorizzazione è stata negata.", + "failed_detail": "Autorizzazione non riuscita: {error}", + "missing_params": "Codice di autorizzazione o stato mancante.", + "delivery_failed": "Impossibile consegnare il risultato dellautorizzazione alla scheda dellapp. Chiudi questa finestra e riprova." + }, + "settings": { + "title": "Plugin", + "description": "Plugin aggiunti dal tuo amministratore. Attivali o disattivali per il tuo account.", + "description_uploads": "I plugin aggiungono funzioni alla webmail. Attivali o disattivali per il tuo account, oppure installa i tuoi.", + "description_demo": "I plugin non sono disponibili nella demo.", + "install_button": "Installa plugin", + "install_hint": "Scegli il file ZIP di un plugin.", + "install_hint_approval": "Scegli il file ZIP di un plugin. Un amministratore deve approvarlo prima che tu possa attivarlo.", + "install_uploads_off": "Il tuo amministratore non consente di installare plugin.", + "install_managed_id": "Il tuo amministratore fornisce già un plugin con questo nome, quindi non può essere sostituito.", + "install_invalid": "Questo file non è un plugin installabile: {details}", + "toast_installed": "Plugin installato. Attivalo qui sotto.", + "toast_installed_pending": "Plugin installato e inviato al tuo amministratore per l'approvazione.", + "toast_enabled": "{name} è attivo", + "toast_disabled": "{name} è disattivato", + "toast_removed": "{name} è stato rimosso", + "toast_forced": "Il tuo amministratore mantiene attivo {name}", + "toast_approval_requested": "{name} è in attesa dell'approvazione del tuo amministratore", + "empty_title": "Ancora nessun plugin", + "empty_admin": "Il tuo amministratore non ha aggiunto alcun plugin.", + "empty_uploads": "Installa un plugin da un file ZIP per iniziare.", + "empty_demo": "I plugin richiedono un account reale, quindi la demo non ne ha.", + "syncing": "Verifica dei plugin con il server…", + "status_installed": "Disattivato", + "status_enabled": "Avvio in corso", + "status_running": "Attivo", + "status_disabled": "Disattivato", + "status_error": "Problema", + "badge_forced": "Obbligatorio", + "badge_managed": "Dal tuo amministratore", + "badge_yours": "Installato da te", + "badge_awaiting": "In attesa di approvazione", + "toggle_label": "Attiva o disattiva {name}", + "forced_note": "Il tuo amministratore richiede questo plugin, quindi non può essere disattivato.", + "permissions": "Cosa può fare", + "permissions_managed": "Cosa può fare (approvato dal tuo amministratore)", + "settings": "Impostazioni", + "remove": "Rimuovi", + "remove_title": "Rimuovere {name}?", + "remove_message": "Il plugin e tutto ciò che ha salvato in questo browser verranno eliminati. Potrai installarlo di nuovo in seguito.", + "remove_confirm": "Rimuovi", + "error_user_plugins_off": "Il tuo amministratore ha disattivato i plugin installati dagli utenti, quindi questo plugin non viene eseguito.", + "error_approval_unavailable": "Questo plugin è stato installato prima che fosse richiesta l'approvazione. Rimuovilo e installalo di nuovo per inviarlo in approvazione.", + "error_approval_denied": "Il tuo amministratore non ha approvato questo plugin.", + "error_approval_pending": "In attesa che il tuo amministratore approvi questo plugin.", + "error_auto_disabled": "Disattivato perché continuava a non funzionare. Riattivalo per riprovare.", + "accounts_heading": "Account collegati", + "account_on": "{label} su {host}", + "account_waiting": "{label} su {host} (accesso non completato)", + "disconnect": "Disconnetti", + "disconnect_failed": "Impossibile disconnettere {label}" + }, + "consent": { + "title": "Consentire a {name} di fare questo?", + "intro": "Il plugin chiede le autorizzazioni qui sotto. Le ottiene solo se le concedi.", + "high_risk_note": "Le voci contrassegnate danno al plugin accesso al contenuto della tua posta o gli permettono di inviare dati altrove. Consentile solo ai plugin di cui ti fidi.", + "revoke_hint": "Puoi disattivare o rimuovere il plugin in Impostazioni in qualsiasi momento.", + "deny": "Non consentire", + "allow": "Consenti" + }, + "permissions": { + "none": "Non richiede autorizzazioni particolari.", + "high_risk": "Con cautela", + "email_read": { + "title": "Leggere la tua posta", + "body": "Vedere mittenti, destinatari, oggetti e il testo completo di qualsiasi messaggio del tuo account, e le tue cartelle." + }, + "email_write": { + "title": "Modificare e organizzare la tua posta", + "body": "Aggiungere o rimuovere etichette, contrassegni e altre parole chiave dai tuoi messaggi, spostarli tra le tue cartelle e nell'archivio, e salvare bozze." + }, + "email_send": { + "title": "Modificare la posta prima dell'invio", + "body": "Leggere e modificare destinatari, oggetto, testo e allegati dei messaggi che scrivi, e impedire l'invio di un messaggio." + }, + "crypto_full": { + "title": "Accesso completo a questa app", + "body": "Funziona con gli stessi diritti della webmail stessa. Può leggere tutta la tua posta e le chiavi salvate e agire per conto tuo. Consentilo solo ai plugin di cui ti fidi completamente." + }, + "email_raw_send": { + "title": "Inviare messaggi creati da sé", + "body": "Inviare messaggi completi, ad esempio firmati o cifrati, dal tuo account." + }, + "email_blob_read": { + "title": "Leggere il contenuto grezzo dei messaggi", + "body": "Leggere i dati originali dei tuoi messaggi e allegati, e di ogni file che alleghi prima dell'invio." + }, + "email_blob_write": { + "title": "Sostituire gli allegati prima dell'invio", + "body": "Caricare file sul tuo server di posta e sostituire un file allegato con un altro prima che il messaggio venga inviato." + }, + "email_render_takeover": { + "title": "Modificare come vengono mostrati i messaggi", + "body": "Sostituire il testo dei messaggi che apri e dell'elenco dei messaggi, ad esempio per mostrare contenuti decifrati." + }, + "calendar_read": { + "title": "Leggere il tuo calendario", + "body": "Vedere i tuoi calendari e i relativi eventi." + }, + "calendar_write": { + "title": "Modificare il tuo calendario", + "body": "Creare, modificare ed eliminare eventi." + }, + "contacts_read": { + "title": "Leggere i tuoi contatti", + "body": "Cercare e leggere nelle tue rubriche." + }, + "contacts_write": { + "title": "Modificare i tuoi contatti", + "body": "Creare, modificare ed eliminare contatti e rubriche." + }, + "files_read": { + "title": "Leggere i tuoi file", + "body": "Sfogliare e aprire i file salvati nel tuo account." + }, + "files_write": { + "title": "Modificare i tuoi file", + "body": "Caricare, rinominare, spostare ed eliminare file nel tuo account." + }, + "identity_read": { + "title": "Vedere i tuoi indirizzi mittente", + "body": "Vedere gli indirizzi da cui invii e le relative firme." + }, + "identity_write": { + "title": "Modificare i tuoi indirizzi mittente", + "body": "Creare, modificare ed eliminare indirizzi mittente e firme." + }, + "filters_read": { + "title": "Leggere i tuoi filtri di posta", + "body": "Vedere le regole che ordinano la posta in arrivo." + }, + "filters_write": { + "title": "Modificare i tuoi filtri di posta", + "body": "Aggiungere regole ai tuoi filtri sul server. Queste regole possono spostare, inoltrare o eliminare la posta in arrivo." + }, + "tasks_read": { + "title": "Leggere le tue attività", + "body": "Vedere i tuoi elenchi di attività e le attività." + }, + "tasks_write": { + "title": "Modificare le tue attività", + "body": "Creare, modificare, completare ed eliminare attività." + }, + "templates_read": { + "title": "Leggere i tuoi modelli", + "body": "Vedere i tuoi modelli di messaggio salvati." + }, + "templates_write": { + "title": "Modificare i tuoi modelli", + "body": "Creare, modificare ed eliminare modelli di messaggio." + }, + "smime_read": { + "title": "Vedere il tuo stato S/MIME", + "body": "Vedere se hai chiavi e certificati S/MIME, senza accesso alle chiavi." + }, + "vacation_read": { + "title": "Vedere la tua risposta automatica", + "body": "Vedere se la risposta automatica è attiva e cosa dice." + }, + "vacation_write": { + "title": "Modificare la tua risposta automatica", + "body": "Attivare o disattivare la risposta automatica e modificarne testo e date." + }, + "settings_read": { + "title": "Leggere le tue impostazioni", + "body": "Vedere le tue preferenze e le definizioni delle etichette." + }, + "settings_write": { + "title": "Modificare le tue impostazioni", + "body": "Aggiungere e riordinare etichette e modificare le tue preferenze." + }, + "security_read": { + "title": "Vedere la sicurezza del tuo account", + "body": "Vedere se l'accesso a due fattori e la cifratura sono attivi, senza accesso ai segreti." + }, + "auth_observe": { + "title": "Sapere quando accedi", + "body": "Ricevere un avviso quando accedi, cambi account o la tua sessione viene rinnovata." + }, + "auth_emit": { + "title": "Disconnetterti", + "body": "Disconnetterti dalla webmail." + }, + "account_read": { + "title": "Vedere i tuoi account", + "body": "Vedere gli account che hai aggiunto, con i relativi indirizzi e server." + }, + "http_post": { + "title": "Usare il server della webmail per te", + "body": "Chiamare questi endpoint del server con il tuo accesso:" + }, + "http_fetch": { + "title": "Connettersi ad altri siti web", + "body": "Inviare richieste a questi siti. Tutto ciò che il plugin può leggere può essere inviato lì:" + }, + "admin_config": { + "title": "Leggere le sue impostazioni dal tuo amministratore", + "body": "Leggere le impostazioni che il tuo amministratore ha salvato per questo plugin." + }, + "ui_observe": { + "title": "Seguire dove ti trovi nell'app", + "body": "Sapere quale pagina è aperta e reagire ai link su cui fai clic." + }, + "ui_toolbar": { + "title": "Aggiungere pulsanti alle barre degli strumenti", + "body": "Mostrare i propri pulsanti nella barra degli strumenti dei messaggi, su un messaggio aperto e nella barra superiore." + }, + "ui_app_top_banner": { + "title": "Mostrare un banner in alto", + "body": "Mostrare un avviso lungo tutta la parte superiore dell'app." + }, + "ui_email_banner": { + "title": "Mostrare avvisi sui messaggi", + "body": "Mostrare i propri avvisi e riepiloghi sopra, dentro e tra le parti di un messaggio o di una conversazione aperti." + }, + "ui_email_footer": { + "title": "Aggiungere contenuti sotto i messaggi", + "body": "Mostrare i propri contenuti sotto un messaggio aperto." + }, + "ui_contact_cryptokeys": { + "title": "Mostrare le chiavi di cifratura dei contatti", + "body": "Mostrare e gestire le chiavi di cifratura di un contatto." + }, + "ui_email_details": { + "title": "Aggiungere pannelli ai dettagli del messaggio", + "body": "Mostrare un proprio pannello accanto o sotto un messaggio aperto." + }, + "ui_download_file": { + "title": "Salvare file sul tuo dispositivo", + "body": "Offrire in download i file che ha creato." + }, + "ui_message_list_tabs": { + "title": "Aggiungere schede alla tua posta in arrivo", + "body": "Suddividere la posta in arrivo in schede come Aggiornamenti o Promozioni." + }, + "ui_composer_toolbar": { + "title": "Aggiungere elementi all'editor", + "body": "Mostrare i propri pulsanti, voci di menu e sezioni mentre scrivi un messaggio." + }, + "ui_composer_sidebar": { + "title": "Aggiungere un pannello all'editor", + "body": "Mostrare un proprio pannello accanto al messaggio che scrivi." + }, + "ui_sidebar_widget": { + "title": "Aggiungere elementi alla barra laterale", + "body": "Mostrare i propri riquadri e sezioni nella barra laterale della posta." + }, + "ui_settings_section": { + "title": "Aggiungere una pagina di impostazioni", + "body": "Mostrare una propria pagina in Impostazioni." + }, + "ui_context_menu": { + "title": "Aggiungere voci al menu del messaggio", + "body": "Aggiungere voci al menu che si apre quando fai clic destro su un messaggio." + }, + "ui_navigation_rail": { + "title": "Aggiungere alla barra di navigazione", + "body": "Mostrare un proprio pulsante nella barra di navigazione." + }, + "ui_keyboard": { + "title": "Aggiungere scorciatoie da tastiera", + "body": "Reagire alle scorciatoie da tastiera che definisce." + }, + "ui_calendar_action": { + "title": "Aggiungere elementi alla vista calendario", + "body": "Mostrare i propri pulsanti e sezioni sugli eventi, e brevi etichette e puntini sui giorni del calendario." + }, + "ui_admin_page": { + "title": "Aggiungere una pagina di amministrazione", + "body": "Mostrare una propria pagina nelle impostazioni di amministrazione." + }, + "app_lifecycle": { + "title": "Sapere quando l'app si avvia e si chiude", + "body": "Ricevere un avviso quando l'app si avvia, va offline o ti disconnetti." + }, + "compose_open": { + "title": "Aprire l'editor per te", + "body": "Aprire un nuovo messaggio con destinatari, testo o allegati già compilati. Sei sempre tu a decidere se inviarlo." + }, + "compose_read": { + "title": "Leggere il messaggio che stai scrivendo", + "body": "Leggere il testo di un messaggio mentre lo scrivi, quando usi il plugin nell'editor. Non la firma né il messaggio citato." + }, + "compose_write": { + "title": "Modificare il messaggio che stai scrivendo", + "body": "Sostituire o aggiungere testo in un messaggio mentre lo scrivi, quando usi il plugin nell'editor. Puoi annullare ogni modifica e decidi sempre tu se inviarlo." + }, + "ui_notifications": { + "title": "Mostrare notifiche", + "body": "Mostrare notifiche sul tuo dispositivo, o nell'app se non le hai consentite. Ognuna indica il nome del plugin." + }, + "ui_message_list": { + "title": "Aggiungere elementi all'elenco dei messaggi", + "body": "Mostrare i propri contenuti sopra l'elenco dei messaggi, e pulsanti sui messaggi e nella barra dei messaggi selezionati." + }, + "ui_contacts": { + "title": "Aggiungere elementi alla vista contatti", + "body": "Mostrare le proprie sezioni nei dettagli di un contatto e pulsanti nell'elenco dei contatti." + }, + "ui_tasks": { + "title": "Aggiungere elementi alla vista attività", + "body": "Mostrare le proprie sezioni nei dettagli di un'attività e pulsanti nell'elenco delle attività." + }, + "ui_files": { + "title": "Aggiungere elementi alla vista file", + "body": "Mostrare le proprie sezioni nell'anteprima dei file e voci nel menu di un file." + }, + "ui_commands": { + "title": "Aggiungere comandi", + "body": "Aggiungere le proprie voci alla palette dei comandi (Ctrl+K)." + }, + "ui_app_page": { + "title": "Aggiungere pagine", + "body": "Aggiungere le proprie pagine all'app, ognuna con un pulsante nella barra di navigazione." + }, + "http_proxy": { + "title": "Usare gli account che colleghi", + "body": "Inviare richieste tramite il server di Bulwark con un account o una chiave configurati da te o dal tuo amministratore. Il plugin non vede mai la password, ma può fare tutto ciò che quell'account può fare sul proprio server." + } + }, + "dialog": { + "from_plugin": "Dal plugin {name}", + "ok": "OK", + "submit": "Invia", + "confirm": "Conferma", + "cancel": "Annulla", + "close": "Chiudi", + "turn_off": "Disattiva {name}", + "plugin_menu": "Opzioni di {name}", + "password_note": "Questo modulo proviene dal plugin {name}, non dalla webmail. Inserisci una password solo se ti fidi di questo plugin.", + "toast_auto_disabled": "{name} è stato disattivato perché continuava a non funzionare." + }, + "connect": { + "title": "Connetti {label}", + "intro_password": "{name} vuole usare il tuo account {label}. Bulwark conserva la password sul proprio server e la invia solo a {host}. Il plugin non la vede mai.", + "intro_password_any": "{name} vuole usare il tuo account {label}. Bulwark conserva la password sul proprio server e la invia solo al server che inserisci qui. Il plugin non la vede mai.", + "intro_signin": "{name} vuole accedere al tuo account {label}. Inserisci qui il server, poi accedi sulla sua pagina. Bulwark conserva sul proprio server l'accesso ottenuto; il plugin non lo vede mai.", + "server": "Indirizzo del server", + "server_hint": "cloud.example.com", + "username": "Nome utente", + "secret": "Password o chiave", + "connect": "Connetti", + "continue": "Continua", + "cancel": "Annulla", + "saving": "Salvataggio…", + "error_server": "Inserisci l'indirizzo del server, ad esempio https://cloud.example.com", + "error_save": "Impossibile salvare: {error}" + } + }, + "global_search": { + "title": "Cerca ovunque", + "placeholder": "Cerca email, contatti, calendario, file...", + "scope_label": "Cerca in", + "account_label": "Account", + "scope_all": "Tutto", + "scope_mail": "Posta", + "scope_contacts": "Contatti", + "scope_calendar": "Calendario", + "scope_files": "File", + "all_accounts": "Tutti gli account", + "show_all": "Mostra tutti i risultati", + "no_results": "Nessun risultato per “{query}”", + "server_hint": "Le parole complete cercano sul server; quelle parziali corrispondono agli elementi già caricati", + "searching": "Ricerca in corso...", + "account_error": "Impossibile cercare in {account}", + "recent_searches": "Ricerche recenti", + "recurring": "Ricorrente", + "no_subject": "(no subject)", + "empty_hint": "Cerca email, contatti, eventi e file in tutti gli account. Prova from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Piatto per data", + "grouped": "Raggruppato", + "load_more": "Carica altri", + "preview_empty": "Seleziona un risultato per l'anteprima", + "open_full": "Apri", + "view_label": "Vista" + }, + "top_bar": { + "more": "Altre opzioni" + }, + "plugin_pages": { + "loading": "Caricamento della pagina", + "not_available_title": "Questa pagina non è disponibile", + "not_available_description": "Il plugin che l'ha aggiunta è disattivato o non è più installato.", + "back_to_mail": "Torna alla posta", + "settings_group": "Plugin" + }, + "command_palette": { + "title": "Tavolozza comandi", + "open": "Apri la tavolozza comandi", + "placeholder": "Digita un comando", + "no_results": "Nessun comando corrispondente", + "go_to": "Vai a {name}", + "compose": "Nuovo messaggio", + "search": "Cerca nella posta", + "theme_dark": "Passa al tema scuro", + "theme_light": "Passa al tema chiaro", + "shortcuts": "Mostra le scorciatoie da tastiera", + "group_navigation": "Vai a", + "group_actions": "Azioni", + "group_plugins": "Plugin" + }, + "notification_center": { + "title": "Notifiche", + "mark_all_read": "Segna tutte come lette", + "clear": "Cancella", + "empty": "Nessuna notifica", + "close": "Chiudi", + "unread": "Non letta", + "unread_count": "{count} non lette", + "level_error": "Errore", + "level_warning": "Avviso" + } +} diff --git a/locales/ja/common.json b/locales/ja/common.json index 0d80d2d7..7a4f2a30 100644 --- a/locales/ja/common.json +++ b/locales/ja/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "JMAPプロトコルを使用するミニマルなウェブメールクライアント", - "login": { - "title": "ウェブメール", - "username_label": "メールアドレス", - "username_placeholder": "user@example.com", - "password_label": "パスワード", - "password_placeholder": "パスワードを入力", - "jmap_endpoint_label": "JMAPサーバー", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "サーバーはこのドメインからのCORSリクエストを許可する必要があります。", - "jmap_server_label": "サーバー", - "jmap_server_auto_picked": "メールドメインからサーバーを選択しました。", - "sign_in": "サインイン", - "signing_in": "サインイン中...", - "loading": "読み込み中...", - "reconnecting": "接続が切れました。再接続を試みています…", - "error": { - "invalid_credentials": "メールアドレスまたはパスワードが無効です", - "connection_failed": "サーバーへの接続に失敗しました", - "cors_blocked": "サーバーには到達できますが、クロスオリジンリクエストがブロックされています。JMAPサーバーのCORS設定を確認し、このドメインを許可してください。", - "server_error": "サーバーが一時的に利用できません。後でもう一度お試しください。", - "generic": "エラーが発生しました。もう一度お試しください。", - "totp_required": "二要素認証コードが必要です。以下にコードを入力してください。", - "totp_invalid": "認証コードが無効です。認証アプリを確認してください。", - "oauth_discovery_failed": "SSOは有効ですが、IDプロバイダーに接続できません。OAuth設定を確認してください。" - }, - "show_password": "パスワードを表示", - "hide_password": "パスワードを隠す", - "totp_toggle": "2FAコードを入力", - "remember_me": "ログイン状態を保持", - "config_error": { - "title": "設定エラー", - "fetch_failed": "アプリケーション設定を読み込めません。後でもう一度お試しください。", - "server_not_configured": "メールサーバーが設定されていません。管理者にお問い合わせください。" - }, - "remove_from_history": "履歴から削除", - "totp_label": "認証コード", - "totp_placeholder": "000000", - "session_expired": "セッションが期限切れになりました。再度サインインしてください。", - "dismiss": "閉じる", - "or": "または", - "sign_in_sso": "SSOでサインイン", - "add_account_title": "アカウントを追加", - "add_account_subtitle": "別のアカウントでサインイン", - "cancel": "キャンセル", - "website": "ウェブサイト", - "imprint": "サイト運営者情報", - "privacy_policy": "プライバシーポリシー", - "try_demo": "デモを試す", - "demo_description": "サンプルデータで探索 - アカウント不要", - "demo_launching": "デモを起動中...", - "demo_login_button": "デモを開始", - "demo_tagline": "フル機能のメールクライアントを体験。アカウント不要。", - "demo_no_signup": "登録不要 - サンプルデータで自由に探索", - "oauth_completing": "サインイン処理中...", - "oauth_error": { - "title": "認証に失敗しました", - "invalid_state": "セキュリティ検証に失敗しました。再度サインインしてください。", - "missing_params": "認証データが不足しています。再度サインインしてください。", - "token_exchange_failed": "認証を完了できませんでした。再度お試しください。", - "access_denied": "アクセスが拒否されました。管理者にお問い合わせください。", - "back_to_login": "ログインに戻る" - } - }, - "sidebar": { - "close": "閉じる", - "compose": "作成", - "compose_hint": "作成 (c)", - "search_placeholder": "メールを検索...", - "search_placeholder_hint": "メールを検索... (/ を押す)", - "storage": "ストレージ", - "storage_used": "使用中", - "storage_free": "空き", - "storage_total": "合計", - "sign_out": "サインアウト", - "sign_out_of": "{account} からサインアウト", - "sign_out_all": "すべてのアカウントからサインアウト", - "add_account": "アカウントを追加", - "set_as_default": "デフォルトに設定", - "switch_account": "アカウントを切り替え", - "contacts": "連絡先", - "calendar": "カレンダー", - "settings": "設定", - "admin": "管理", - "files": "ファイル", - "loading_mailboxes": "メールボックスを読み込み中...", - "push_connected": "リアルタイム更新が有効", - "push_disconnected": "リアルタイム更新が無効", - "keyboard_shortcuts": "キーボードショートカット", - "theme": { - "light": "ライトモード", - "dark": "ダークモード", - "system": "システムテーマ" - }, - "language": { - "title": "言語" - }, - "mailboxes": { - "inbox": "受信トレイ", - "sent": "送信済み", - "drafts": "下書き", - "trash": "ゴミ箱", - "archive": "アーカイブ", - "starred": "スター付き", - "all_mail": "すべてのメール", - "spam": "迷惑メール", - "important": "重要" - }, - "unified_inbox": "統合受信トレイ", - "unified_sent": "すべての送信済み", - "unified_drafts": "すべての下書き", - "unified_trash": "すべてのゴミ箱", - "unified_archive": "すべてのアーカイブ", - "unified_junk": "すべての迷惑メール", - "all_accounts": "すべてのアカウント", - "unified_mailbox": "統合メールボックス", - "expand": "展開", - "collapse": "折りたたむ", - "expand_tooltip": "展開", - "collapse_tooltip": "折りたたむ", - "mobile": { - "search": "検索", - "compose": "作成", - "go_back": "戻る", - "toggle_menu": "メニューを切り替え" - }, - "clear_search": "検索をクリア", - "vacation_active": "不在応答が有効です", - "demo_banner": "デモモード", - "demo_reset": "リセット", - "demo_tour": "ツアー", - "tags": "タグ", - "show_all_tags": "すべて表示({count})", - "show_fewer_tags": "表示を減らす", - "folders": "フォルダ", - "mail": "メール", - "nav_label": "ナビゲーション", - "add_app": "アプリ", - "shared": "共有", - "scheduled": "予約済み", - "unified_all_unread": "すべての未読", - "unified_all_starred": "すべてのスター付き", - "unified_all_mail": "すべてのメール", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "既定のアプリ", - "description": "メールとカレンダーのリンクを Bulwark で開くかどうかを選択します。技術的には、Bulwark は mailto: と webcal: リンクのプロトコル ハンドラーとして登録されます。", - "unsupported": "このブラウザーまたは接続では、プロトコル ハンドラーの手動登録がサポートされていません。インストール済みの PWA は、ブラウザーまたはシステム設定から使用できる場合があります。", - "mailto_label": "メールリンク", - "mailto_description": "mailto: リンクを、入力済みの作成画面で Bulwark に開きます。", - "protocol_open_mode_label": "プロトコルリンクを開くとき", - "protocol_open_mode_description": "Bulwark が mailto: と webcal: のリンクを新しいタブで開くか、開いているセッションを再利用するかを選択します。アクティブなセッションのオプションでは通知の許可が必要です。ブラウザーがフォーカスをブロックした場合に、代替通知をクリックして Bulwark を前面に表示できます。", - "protocol_open_mode_active_session": "可能な場合はアクティブなセッションで開く", - "protocol_open_mode_new_tab": "常に新しいタブを開く", - "focus_notification_title": "Bulwark を開く", - "focus_notification_body": "リンクは Bulwark で開かれました。クリックするとウィンドウを前面に表示します。", - "webcal_label": "カレンダーリンク", - "webcal_description": "webcal: リンクを、入力済みのカレンダー購読ダイアログで Bulwark に開きます。", - "register_mailto": "メールアプリを登録", - "register_webcal": "カレンダーアプリを登録", - "mailto_registered": "メール ハンドラーの登録を要求しました", - "webcal_registered": "カレンダー ハンドラーの登録を要求しました", - "registration_failed": "プロトコル ハンドラーの登録に失敗しました", - "opening_mailto": "作成画面を開いています...", - "opening_webcal": "カレンダーを開いています...", - "browser_note": "ブラウザーまたはオペレーティング システムから確認を求められる場合があります。また、既定のアプリとして選択する前に Bulwark のインストールが必要な場合があります。", - "select_account_title": "アカウントを選択", - "select_mailto_account": "このメールリンクを開くアカウントを選択してください。", - "select_webcal_account": "このカレンダーリンクを開くアカウントを選択してください。", - "select_account_note": "この選択は、このプロトコル リンクにのみ適用されます。", - "detail_to": "宛先", - "detail_subject": "件名", - "detail_no_subject": "件名なし", - "detail_calendar": "カレンダー", - "detail_source": "ソース", - "active_account": "アクティブ", - "switching_account": "アカウントを切り替えています..." - }, - "sidebar_apps": { - "modal_title": "サイドバーアプリ", - "add_new": "アプリを追加", - "edit_app": "アプリを編集", - "name_label": "名前", - "name_placeholder": "マイアプリ", - "name_required": "名前は必須です", - "url_label": "URL", - "url_required": "URLは必須です", - "url_invalid": "有効なhttpまたはhttps URLを入力してください", - "icon_label": "アイコン", - "icon_required": "アイコンは必須です", - "open_mode_label": "開き方", - "open_new_tab": "新しいタブ", - "open_inline": "埋め込み", - "cancel": "キャンセル", - "add": "追加", - "update": "更新", - "delete": "削除", - "delete_confirm_title": "アプリを削除", - "delete_confirm": "「{name}」を削除してもよろしいですか?", - "no_apps": "アプリが追加されていません", - "no_apps_hint": "カスタムアプリやリンクをサイドバーに追加できます", - "managed_title": "管理者によって提供", - "managed_description": "これらのアプリは全員向けに設定されており、変更できません。", - "managed_badge": "管理対象", - "search_icons": "アイコンを検索...", - "show_popular": "人気", - "show_all": "すべて", - "no_icons_found": "アイコンが見つかりません", - "inline_badge": "埋め込み", - "tab_badge": "タブ", - "show_on_mobile": "モバイルで表示" - }, - "email_list": { - "no_emails": "メッセージが見つかりません", - "no_emails_description": "このメールボックスは空です", - "no_search_results": "結果が見つかりません", - "no_search_results_description": "検索やフィルターを調整してください", - "loading": "メールを読み込み中...", - "unread": "未読", - "to_me": "宛先: 自分", - "to_recipients": "{count}人の宛先", - "and_others": "他{count}人", - "draft": "下書き", - "starred": "スター付き", - "pinned": "ピン留め", - "replied": "返信済み", - "forwarded": "転送済み", - "has_attachment": "添付ファイルあり", - "conversations_count": "{total}件中{count}件の会話", - "conversations_count_plus": "{count}件以上の会話", - "conversations_count_simple": "{count}件の会話", - "no_conversations": "会話がありません", - "loading_more": "さらにメールを読み込み中...", - "no_more_emails": "読み込むメールがありません", - "batch_actions": { - "select": "メールを選択", - "select_all": "すべて選択", - "selected_messages": "{count, plural, one {1} other {#}}通のメールが選択されました", - "mark_read": "既読にする", - "mark_unread": "未読にする", - "delete": "削除", - "delete_confirm_title": "メールを削除", - "delete_confirm_message": "{count, plural, one {1件} other {#件}}のメールを削除しますか?", - "clear_selection": "選択を解除" - }, - "permanent_delete": "完全に削除", - "permanent_delete_confirm_title": "完全に削除", - "permanent_delete_confirm_message": "このメールは完全に削除されます。この操作は取り消せません。", - "permanent_delete_confirm_batch_message": "{count, plural, one {1件} other {#件}}のメールが完全に削除されます。この操作は取り消せません。", - "empty_folder": { - "button": "フォルダを空にする", - "confirm_title": "フォルダを空にする", - "confirm_message": "このフォルダ内のすべてのメールが完全に削除されます。この操作は取り消せません。", - "confirm_button": "フォルダを空にする", - "junk_hint": "迷惑メールフォルダを空にして、すべてのメールを完全に削除できます。", - "trash_hint": "ゴミ箱を空にして、すべてのメールを完全に削除できます。" - }, - "no_scheduled_emails": "予約送信はありません", - "no_scheduled_emails_description": "後で送信するよう予約したメッセージがここに表示されます。", - "scheduled_count": "{count, plural, one {1 件の予約送信} other {# 件の予約送信}}", - "scheduled_actions_hint": "予約メッセージは予約アクションからキャンセル、再予約、編集できます。", - "cancel_scheduled_send": "送信をキャンセル", - "reschedule_send": "再予約", - "cancel_and_edit": "キャンセルして編集", - "cancel_and_compose_again": "キャンセルして新規作成", - "reschedule_prompt": "新しい日時を入力してください。例: 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "送信者が開封確認を求めています:", - "send": "確認を送信", - "ignore": "無視", - "sent": "開封確認を送信しました。", - "send_failed": "開封確認を送信できませんでした", - "mdn_subject": "開封済み: {subject}", - "mdn_body": "これは、あなたが {recipient} に送信したメッセージの開封確認です。\n\n注意: この確認は、メッセージが受信者のコンピューターに表示されたことを示すだけです。受信者が内容を読んだ、または理解したことを保証するものではありません。" - }, - "no_email_selected": "メールが選択されていません", - "no_email_description": "リストからメールを選択して表示してください", - "no_conversation_selected": "会話が選択されていません", - "no_conversation_description": "リストから会話を選択して読んでください", - "compose": "作成", - "compose_hint": "新しいメッセージを作成", - "no_subject": "(件名なし)", - "no_body_content": "(本文がありません)", - "no_preview_available": "プレビューは利用できません", - "show_quoted_text": "引用テキストを表示", - "hide_quoted_text": "引用テキストを非表示", - "loading_email": "メールを読み込み中...", - "loading": "読み込み中...", - "reply": "返信", - "reply_all": "全員に返信", - "forward": "転送", - "delete": "削除", - "archive": "アーカイブ", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "スターを付ける", - "unstar": "スターを外す", - "mark_unread": "未読にする", - "mark_read": "既読にする", - "unread": "未読", - "read": "既読", - "spam_short": "迷惑メール", - "not_spam_short": "迷惑メールでない", - "move": "移動", - "print": "印刷", - "view_source": "ソースを表示", - "fullscreen": "全画面表示", - "exit_fullscreen": "全画面表示を終了", - "export_email": ".emlとしてエクスポート", - "forward_as_attachment": "添付ファイルとして転送", - "import_email": ".eml または .zip をインポート", - "keyboard_shortcuts": "キーボードショートカット (?)", - "email_source": "メールソース", - "draft_banner": "このメッセージは下書きです", - "edit_draft": "編集", - "copy_source": "クリップボードにコピー", - "source_copied": "ソースをクリップボードにコピーしました", - "attachments": "添付ファイル", - "important": "重要", - "download": "ダウンロード", - "download_all": "すべてダウンロード", - "from": "送信者", - "to": "宛先", - "cc": "CC", - "bcc": "BCC", - "date": "日付", - "subject": "件名", - "show_details": "詳細を表示", - "hide_details": "詳細を非表示", - "external_content_warning": "画像と外部コンテンツがブロックされました", - "load_external_content": "画像を読み込む", - "trust_sender": "常にこの送信者を信頼する", - "back_to_list": "リストに戻る", - "view_contact": "連絡先を表示", - "message_details": "メッセージの詳細", - "more_reply_options": "その他の返信オプション", - "set_tag": "ラベルを設定", - "tag": "ラベル", - "more_actions": "その他の操作", - "move_to": "移動...", - "remove_tag": "ラベルを削除", - "tag_filter_placeholder": "ラベルを絞り込む", - "tag_no_matches": "一致するラベルがありません", - "more_count": "他{count}件", - "characters_count": "{count}文字", - "quick_reply_placeholder": "クイック返信を入力...", - "more_options": "その他のオプション", - "sending": "送信中...", - "security_authentication": "セキュリティと認証", - "technical_details": "技術的な詳細", - "message_id_label": "メッセージID:", - "reply_to_label": "返信先:", - "delivery_time_label": "配信時刻:", - "conversation_part_label": "会話の一部:", - "previous_messages": "{count}件の以前のメッセージ", - "previous_messages_plural": "{count}件の以前のメッセージ", - "time": { - "day": "日", - "days": "日", - "hour": "時間", - "hours": "時間", - "minute": "分", - "minutes": "分" - }, - "unknown_sender": "不明", - "recipient_me": "自分", - "recipient_and_others": "{name}と他{count}人", - "recipient_to_prefix": "宛先:", - "authentication": { - "title": "認証", - "status": { - "verified": "確認済み", - "warning": "警告", - "none": "未認証" - }, - "spf": { - "pass": "SPF合格", - "fail": "SPF不合格", - "none": "SPFなし" - }, - "dkim": { - "pass": "DKIM有効", - "fail": "DKIM無効", - "none": "DKIMなし" - }, - "dmarc": { - "pass": "DMARC合格", - "fail": "DMARC不合格", - "none": "DMARCなし" - }, - "spam_score": "スパムスコア", - "tooltip_spf": "Sender Policy Framework: 送信サーバーがそのドメインを代表してメールを送信する権限を持つことを確認します", - "tooltip_dkim": "DomainKeys Identified Mail: 暗号署名を用いて、メールが送信中に改ざんされていないことを確認します", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: SPF と DKIM が送信者のドメインと一致することを確認し、失敗時のポリシーを定めます", - "policy": "ポリシー", - "result": { - "pass": "合格", - "fail": "不合格", - "softfail": "ソフト失敗", - "neutral": "中立", - "permerror": "永続的エラー", - "temperror": "一時的エラー", - "none": "なし" - } - }, - "headers": { - "routing": "ルーティング", - "received": "受信", - "message_id": "メッセージID", - "list_info": "リスト情報" - }, - "tooltips": { - "reply": "返信", - "reply_all": "全員に返信 (a)", - "forward": "転送 (f)", - "archive": "アーカイブ", - "delete": "削除", - "star": "スター (s)", - "unstar": "スター解除 (s)", - "compose": "新規作成 (c)", - "previous": "前のメール", - "next": "次のメール", - "edit_draft": "下書きを編集" - }, - "spam": { - "button_title": "迷惑メールを報告", - "not_spam_title": "正当なメールとしてマーク", - "toast_success": "迷惑メールに移動しました", - "toast_batch": "{count}件のメールを迷惑メールに移動しました", - "toast_undo": "元に戻す", - "toast_not_spam_success": "受信トレイに移動しました", - "toast_not_spam_batch": "{count}件のメールを受信トレイに移動しました", - "error": "迷惑メールの報告に失敗しました", - "error_not_spam": "メールの復元に失敗しました" - }, - "unsubscribe_banner": { - "label": "ニュースレター", - "button": "購読解除", - "confirm_title": "この送信者の購読を解除しますか?", - "confirm_button": "確認", - "cancel": "キャンセル", - "success_http": "購読解除ページを新しいタブで開きました", - "success_mailto": "配信停止メールを送信しました", - "confirm_message_http": "配信停止ページを新しいタブで開きます。", - "confirm_message_mailto": "送信者に配信停止メールを送信します。", - "error": "購読解除できませんでした", - "dismiss": "閉じる" - }, - "calendar_invitation": { - "loading": "イベント詳細を読み込み中…", - "title": "カレンダー招待", - "published_title": "公開イベント", - "response_title": "イベント返信", - "update_title": "イベント更新", - "counter_title": "対案", - "refresh_title": "更新依頼", - "declined_counter_title": "対案は拒否されました", - "cancelled_title": "イベントがキャンセルされました", - "organizer": "{name} が主催", - "organizer_label": "主催者:", - "attendees": "{count}名の参加者", - "accept": "承諾", - "maybe": "未定", - "decline": "辞退", - "add_to_calendar": "カレンダーに追加", - "added": "カレンダーに追加しました", - "rsvp_sent": "回答を送信しました", - "parse_error": "招待を読み込めませんでした", - "action_failed": "このカレンダー操作を完了できませんでした。", - "no_calendar": "カレンダーが利用できません", - "published_info": "このイベントは参照用として共有されました。", - "response_info": "このメッセージには参加者の返信が含まれています。", - "response_info_organizer": "この参加者の返信はあなたのイベントを更新します。", - "update_info": "このメッセージは既存のイベントを更新します。", - "counter_info": "このメッセージはイベント変更の提案です。", - "counter_info_organizer": "この参加者はあなたのイベントに変更を提案しました。", - "refresh_info": "このメッセージは最新のイベント詳細を要求しています。", - "refresh_info_organizer": "参加者が最新のイベント詳細を要求しました。", - "declined_counter_info": "主催者が対案を拒否しました。", - "authentication_failed_info": "この招待メールの認証チェックに失敗しました。カレンダー操作は慎重に行ってください。", - "authentication_missing_info": "この招待には検証済みのメール認証がありません。不審な点があれば主催者に詳細を確認してください。", - "sender_mismatch_info": "この招待は {sender} から送信されましたが、カレンダーデータ上の主催者は {organizer} です。", - "sender_mismatch_unverified_info": "この招待は {sender} から送信されましたが、カレンダーデータ上の主催者は {organizer} であり、メッセージも検証できませんでした。", - "organizer_role": "あなたがこのイベントの主催者です", - "your_response": "あなたの返答: {status}", - "response_needed": "返答が必要です", - "response_accepted": "承諾", - "response_tentative": "未定", - "response_declined": "辞退", - "response_delegated": "委任済み", - "actor_sent_info": "{name} から送信されました。", - "actor_response_info": "{name} が {status} と返答しました。", - "actor_counter_info": "{name} がこのイベントの変更を提案しました。", - "actor_refresh_info": "{name} が最新のイベント詳細を求めました。", - "actor_declined_counter_info": "{name} が対案を拒否しました。", - "actor_note": "メモ: {comment}", - "actor_unknown": "どなたか", - "proposed_changes": "提案された変更", - "change_title": "件名", - "change_time": "日時", - "change_location": "場所", - "change_description": "説明", - "change_empty": "なし", - "change_from_to": "{before} -> {after}", - "apply_proposal": "提案された変更を適用", - "proposal_applied": "提案された変更を適用しました。", - "review_proposal": "提案を確認", - "review_request": "依頼を確認", - "view_in_calendar": "カレンダーで表示", - "select_calendar": "カレンダーを選択", - "already_in_calendar": "カレンダーに登録済み", - "request_info": "このイベントに招待されました。主催者に参加可否をお知らせください。", - "cancel_info": "主催者がこのイベントをキャンセルしました。", - "event_updated": "更新 #{sequence}", - "event_status_tentative": "仮", - "event_status_cancelled": "キャンセル済み", - "collapse": "詳細を非表示", - "expand": "詳細を表示" - }, - "previous": "前へ", - "next": "次へ", - "send": "送信", - "more": "他", - "details": { - "recipients_routing": "宛先とルーティング", - "authentication_security": "認証とセキュリティ", - "identifiers_threading": "識別子とスレッド", - "mailing_list": "メーリングリスト", - "message_properties": "メッセージのプロパティ", - "sent": "送信日時", - "received": "受信日時", - "delivery_time": "配信時間", - "in_reply_to": "返信先", - "references": "参照", - "thread_id": "スレッドID", - "size": "サイズ", - "mime_type": "MIMEタイプ", - "attachments_summary": "{count} ファイル · {size}", - "list_id": "リストID", - "list_help": "リストヘルプ", - "list_post": "リスト投稿", - "list_unsubscribe": "購読解除", - "iprev": "逆引きDNS", - "spam_status": "スパム状態", - "ai_verdict": "AIの判定", - "account": "アカウント", - "no_subject": "(件名なし)" - }, - "scheduled_banner": "{date} に送信予定", - "scheduled_send_created": "メールを送信予約しました", - "cancel_scheduled_send": "送信をキャンセル", - "reschedule_send": "再予約", - "cancel_and_edit": "キャンセルして編集", - "cancel_and_compose_again": "キャンセルして新規作成", - "reschedule_prompt": "新しい日時を入力してください。例: 2026-05-04T15:30", - "scheduled_actions_only": "予約済みビューでは予約アクションのみ利用できます", - "undo_send_scheduled": "メッセージは送信予約されています", - "undo_send": "送信を取り消す", - "contact_sidebar": { - "title": "連絡先", - "close": "サイドバーを閉じる", - "action_email": "メール", - "action_email_title": "メールを送信", - "action_copy": "コピー", - "action_copy_title": "メールアドレスをコピー", - "action_edit_title": "連絡先を編集", - "section_emails": "メールアドレス", - "section_phones": "電話番号", - "section_organizations": "組織", - "section_addresses": "住所", - "section_notes": "メモ", - "not_in_contacts": "連絡先に登録されていません", - "add_to_contacts": "連絡先に追加", - "copied": "コピーしました!", - "copy_failed": "コピーに失敗しました" - }, - "send_now": "今すぐ送信" - }, - "email_composer": { - "read_receipt_on": "開封確認を要求中(クリックで無効化)", - "read_receipt_off": "開封確認を要求する", - "dsn_on": "配信通知を要求中(クリックで解除)", - "dsn_off": "配信通知を要求する", - "format_plain_text": "プレーンテキストに切り替え", - "format_rich_text": "リッチテキスト (HTML) に切り替え", - "require_tls_on": "暗号化配信を必須にしています(クリックで解除)", - "require_tls_off": "暗号化配信を必須にする(TLS)", - "new_message": "新規メッセージ", - "signature_edit_hint": "ダブルクリックで署名を編集", - "reply": "返信", - "reply_all": "全員に返信", - "forward": "転送", - "reply_to": "返信", - "reply_all_to": "全員に返信", - "forward_message": "転送", - "from": "送信元", - "to": "宛先", - "cc": "CC", - "bcc": "BCC", - "subject": "件名", - "body_placeholder": "メッセージを入力...", - "send": "送信", - "cancel": "キャンセル", - "attach": "添付", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "破棄", - "discard_draft_title": "下書きを破棄しますか?", - "discard_draft_confirm": "未保存の変更があります。この下書きを破棄しますか?", - "saving": "保存中...", - "draft_saved": "下書きを保存しました", - "save_failed": "保存に失敗しました", - "to_placeholder": "受信者のメールアドレス", - "cc_placeholder": "CC受信者", - "bcc_placeholder": "BCC受信者", - "subject_placeholder": "件名", - "cc_label": "CC:", - "bcc_label": "BCC:", - "subject_label": "件名:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(件名なし)", - "unknown_sender": "不明", - "quote": { - "reply_header": "{date}に{sender}が書きました:", - "forward_header": "---------- 転送メッセージ ----------", - "from": "送信者: {sender}", - "date": "日付: {date}", - "subject": "件名: {subject}", - "to": "宛先: {recipients}" - }, - "remove_sub_address": "サブアドレスを削除", - "from_override": { - "toggle_off": "上書き", - "toggle_on": "上書きを取り消す", - "toggle_tooltip": "差出人名とアドレスを自由に編集できます。メールは引き続きあなたのアイデンティティ経由で送信されます。", - "name_label": "差出人名", - "name_placeholder": "名前", - "email_label": "差出人メールアドレス", - "email_placeholder": "alias@example.com", - "envelope_notice": "サーバーがこのアドレスをエンベロープ送信者として受け付けない場合は、代わりに {identity} が使われ、受信者は Return-Path ヘッダーでそのアドレスを確認できます。" - }, - "use_template": "テンプレート", - "save_as_template": "テンプレートとして保存", - "validation": { - "recipient_required": "送信するには宛先を追加してください", - "body_required": "メッセージを入力するかファイルを添付してください", - "attachments_uploading": "添付ファイルをアップロード中です。完了次第送信します", - "attachment_upload_failed": "送信されませんでした。添付ファイルのアップロードに失敗しました。削除してもう一度お試しください。" - }, - "upload_progress": "アップロード中 {uploaded} / {total}", - "upload_cancel": "アップロードをキャンセル", - "upload_failed": "{filename} のアップロードに失敗しました", - "attachment_too_large": "「{name}」はサーバーの上限を超えています(1 ファイルあたり {max})", - "attachments_total_too_large": "添付ファイルがサーバーの上限(1 通あたり {max})を超えます", - "send_failed": "メールの送信に失敗しました", - "smime_sign_on": "S/MIME 署名を有効化しました", - "smime_sign_off": "S/MIME 署名を無効化しました", - "smime_encrypt_on": "S/MIME 暗号化を有効化しました", - "smime_encrypt_off": "S/MIME 暗号化を無効化しました", - "smime_encrypt_unavailable": "S/MIME 暗号化は利用できません – 受信者の証明書がありません", - "smime_unlock_title": "S/MIME 鍵のロックを解除", - "smime_unlock_message": "S/MIME 鍵を解除するためのパスフレーズを入力してください。", - "smime_unlock_button": "ロック解除", - "smime_passphrase_placeholder": "S/MIME パスフレーズ", - "send_timeout": "サーバーが応答しなくなりました。再送信する前に送信済みフォルダーを確認してください。メッセージはすでに送信されている可能性があります。", - "continue_draft": "下書きを続ける", - "close_draft_title": "下書きを保存または破棄しますか?", - "close_draft_message": "未保存の変更があります。下書きとして保存しますか、それとも破棄しますか?", - "drop_files": "ファイルをドロップして添付", - "show_less": "折りたたむ", - "forgot_attachment": { - "title": "添付ファイルを忘れていませんか?", - "message": "メッセージに「{keyword}」が含まれていますが、ファイルが添付されていません。このまま送信しますか?", - "send_anyway": "そのまま送信", - "back": "編集に戻る" - }, - "empty_subject": { - "title": "件名なしで送信しますか?", - "message": "このメッセージには件名がありません。このまま送信しますか?", - "dont_ask_again": "今後は確認しない", - "send_anyway": "そのまま送信", - "back": "編集に戻る" - }, - "add_link": "リンクを追加", - "link_url_prompt": "URLを入力してください", - "sending": "送信中...", - "schedule_send": "送信を予約", - "schedule_send_description": "サーバーがこのメッセージを送信する日時を選択します。", - "schedule_send_required": "日時を選択してください。", - "schedule_send_invalid": "有効な日時を入力してください。", - "schedule_send_future": "未来の日時を選択してください。", - "schedule_send_too_late": "この日時はサーバーが許可する範囲を超えています。", - "schedule_send_unsupported": "このアカウントでは予約送信はサポートされていません。", - "schedule_send_cleanup_warning": "予約送信は作成されましたが、下書きのクリーンアップに失敗しました。", - "send_delay_unsupported": "このアカウントでは送信遅延はサポートされていません。", - "send_delay_unsupported_confirm": "このアカウントは送信遅延に対応していません。すぐに送信しますか?", - "recipient_edit_email": "メールアドレスを編集", - "recipient_edit_name": "表示名を編集", - "recipient_email_placeholder": "メールアドレス", - "recipient_name_placeholder": "表示名", - "autocomplete_search_server": "サーバーを検索", - "autocomplete_searching": "検索中...", - "toolbar": { - "bold": "太字", - "italic": "斜体", - "underline": "下線", - "strikethrough": "取り消し線", - "text_color": "文字色", - "remove_color": "色を解除", - "heading_1": "見出し1", - "heading_2": "見出し2", - "bullet_list": "箇条書き", - "ordered_list": "番号付きリスト", - "quote": "引用", - "code_block": "コードブロック", - "align_left": "左揃え", - "align_center": "中央揃え", - "align_right": "右揃え", - "text_direction": "文字方向 (RTL/LTR)", - "link": "リンク", - "table": "表", - "clear_formatting": "書式をクリア", - "undo": "元に戻す", - "redo": "やり直す", - "add_row_above": "上に行を追加", - "add_row_below": "下に行を追加", - "add_column_before": "左に列を追加", - "add_column_after": "右に列を追加", - "delete_row": "行を削除", - "delete_column": "列を削除", - "toggle_header_row": "ヘッダー行の切り替え", - "delete_table": "表を削除", - "pick_size": "サイズを選択", - "font_size": "文字サイズ", - "font_size_default": "デフォルト", - "background_color": "背景色", - "remove_background_color": "背景色を削除" - }, - "send_filing_warning": "送信されましたが、送信後の整理に失敗しました。古い下書きが残る場合があります。" - }, - "confirm_dialog": { - "confirm": "確認", - "cancel": "キャンセル" - }, - "common": { - "loading": "読み込み中...", - "error": "エラー", - "success": "成功", - "cancel": "キャンセル", - "save": "保存", - "delete": "削除", - "edit": "編集", - "close": "閉じる", - "search": "検索", - "refresh": "更新", - "settings": "設定", - "help": "ヘルプ", - "logout": "ログアウト", - "yes": "はい", - "no": "いいえ", - "unknown": "不明", - "app_title": "ウェブメール", - "reconnecting": "接続が切れました。再接続を試みています…", - "rate_limited_title": "サーバー認証は一時的に制限されています。", - "rate_limited_detail": "ロックアウトを避けるため、Bulwark はバックグラウンド要求を一時停止しました。{seconds} 秒後に再試行します。", - "rate_limited_action_title": "ロックアウトを避けるため要求を一時停止しました。", - "rate_limited_action_detail": "Bulwark はサーバーのクールダウンが終わるまで、追加の認証付きリクエストを送信しません。{seconds} 秒後に再試行してください。", - "pull_to_refresh": "引っ張って更新", - "release_to_refresh": "離して更新", - "refreshing": "更新中…" - }, - "notifications": { - "email_sent": "メールを送信しました", - "email_deleted": "メールを削除しました", - "email_archived": "メールをアーカイブしました", - "emails_deleted": "{count, plural, other {#件のメールを削除しました}}", - "emails_archived": "{count, plural, other {#件のメールをアーカイブしました}}", - "error_archiving": "メールをアーカイブできませんでした", - "error_updating": "メールを更新できませんでした", - "email_starred": "スターを付けました", - "email_unstarred": "スターを外しました", - "email_marked_read": "既読にしました", - "email_marked_unread": "未読にしました", - "copied_to_clipboard": "クリップボードにコピーしました", - "source_copied": "ソースをクリップボードにコピーしました", - "error_sending": "メールの送信に失敗しました", - "error_deleting": "メールの削除に失敗しました", - "error_loading": "メールの読み込みに失敗しました", - "new_email": "新しいメール", - "new_email_from": "{sender}から", - "click_to_view": "クリックして表示", - "email_moved": "メールを移動しました", - "emails_moved": "{count}件のメールを移動しました", - "moved_to_mailbox": "{mailbox}に移動しました", - "move_failed": "移動に失敗しました", - "move_error": "選択したフォルダにメールを移動できませんでした", - "email_tagged": "メールにタグを付けました", - "emails_tagged": "{count}件のメールにタグを付けました", - "tag_failed": "タグ付けに失敗しました", - "identity_created": "送信者情報を作成しました", - "identity_updated": "送信者情報を更新しました", - "identity_deleted": "送信者情報を削除しました", - "identity_set_primary": "プライマリ送信者情報を更新しました", - "identity_create_failed": "送信者情報の作成に失敗しました: {error}", - "identity_update_failed": "送信者情報の更新に失敗しました: {error}", - "identity_delete_failed": "送信者情報の削除に失敗しました: {error}", - "identity_unauthorized": "このメールアドレスからの送信は許可されていません", - "identity_not_found": "送信者情報が見つかりません", - "vacation_saved": "不在応答の設定を保存しました", - "vacation_save_failed": "不在応答の設定の保存に失敗しました", - "filters_saved": "フィルターを保存しました", - "filters_save_failed": "フィルターの保存に失敗しました", - "filters_deleted": "フィルタールールが削除されました", - "templates_exported": "テンプレートをエクスポートしました", - "templates_imported": "{count}件のテンプレートをインポートしました", - "templates_import_errors": "一部のテンプレートをインポートできませんでした", - "templates_import_empty": "ファイルにテンプレートが見つかりません", - "export_email_error": "メールのエクスポートに失敗しました", - "import_email_success": "メールを正常にインポートしました", - "import_email_error": "メールのインポートに失敗しました" - }, - "date": { - "today": "今日", - "yesterday": "昨日", - "this_week": "今週", - "last_week": "先週", - "this_month": "今月", - "older": "それ以前", - "just_now": "たった今", - "minutes_ago": "{count}分前", - "minutes_ago_plural": "{count}分前", - "hours_ago": "{count}時間前", - "hours_ago_plural": "{count}時間前", - "days_ago": "{count}日前", - "days_ago_plural": "{count}日前" - }, - "language": { - "title": "言語", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "言語を選択", - "switch_to_english": "英語に切り替え", - "switch_to_french": "フランス語に切り替え", - "switch_to_japanese": "日本語に切り替え", - "switch_to_spanish": "スペイン語に切り替え", - "switch_to_italian": "イタリア語に切り替え", - "switch_to_german": "ドイツ語に切り替え", - "switch_to_dutch": "オランダ語に切り替え", - "switch_to_portuguese": "ポルトガル語に切り替え", - "switch_to_russian": "ロシア語に切り替え", - "switching": "言語を変更中...", - "switch": "言語を切り替え", - "current": "現在の言語", - "polish": "Polski", - "switch_to_polish": "ポーランド語に切り替え", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "ペルシア語に切り替え", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "モンゴル語に切り替え" - }, - "settings": { - "title": "設定", - "back_to_mail": "メールに戻る", - "save_success": "設定を保存しました", - "import_success": "設定をインポートしました", - "import_error": "設定のインポートに失敗しました", - "reset_confirm": "すべての設定をデフォルトにリセットしてもよろしいですか?", - "unsaved_changes": "保存されていない変更があります", - "discard_changes": "変更を破棄しますか?", - "discard": "破棄", - "keep_editing": "編集を続ける", - "tabs": { - "appearance": "外観", - "language": "言語、地域、時刻", - "email": "メール動作", - "composer": "作成", - "privacy": "プライバシーとセキュリティ", - "account": "アカウント", - "identities": "送信者情報", - "vacation": "不在応答", - "advanced": "詳細設定", - "calendar": "カレンダー", - "filters": "フィルター", - "templates": "テンプレート", - "folders": "フォルダー", - "keywords": "ラベル", - "security": "セキュリティ", - "encryption": "暗号化", - "files": "ファイル", - "contacts": "連絡先", - "protocol_handlers": "既定のアプリ", - "sidebar_apps": "サイドバーアプリ", - "notifications": "通知", - "layout": "レイアウト", - "reading": "閲覧", - "composing": "作成", - "downloads": "ダウンロード", - "content_senders": "コンテンツと送信者", - "about_data": "情報とデータ", - "debug": "デバッグ" - }, - "tab_groups": { - "general": "一般", - "account": "アカウントと身元", - "organization": "メール整理", - "apps": "アプリ", - "system": "システム", - "appearance": "外観", - "mail": "メール", - "privacy": "プライバシーとセキュリティ", - "advanced": "詳細" - }, - "appearance": { - "message_list_order": { - "label": "メッセージ一覧の並び順", - "description": "一覧でメッセージをどう並べるか。並べ替えはサーバーが行うため、読み込み済みのメッセージだけでなくフォルダー全体に適用されます。", - "preset": { - "chronological": "時系列(新しい順)", - "unread_first": "未読を先に", - "starred_first": "スター付きを先に", - "tagged_first": "タグ付きを先に", - "custom": "カスタム" - }, - "tag_label": "タグ", - "advanced_show": "詳細: 並べ替えレベルを編集", - "advanced_hide": "詳細を隠す", - "criterion_label": "レベル {n} の条件", - "direction_label": "レベル {n} の方向", - "criteria": { - "unread": "未読", - "starred": "スター付き", - "tag": "タグ", - "receivedAt": "受信日時", - "sentAt": "送信日時", - "from": "送信者", - "subject": "件名", - "size": "サイズ" - }, - "direction": { - "unread_first": "未読を先に", - "read_first": "既読を先に", - "starred_first": "スター付きを先に", - "unstarred_first": "スターなしを先に", - "tagged_first": "タグ付きを先に", - "untagged_first": "タグなしを先に", - "newest_first": "新しい順", - "oldest_first": "古い順", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "大きい順", - "smallest_first": "小さい順" - }, - "add_level": "レベルを追加", - "remove_level": "レベルを削除", - "move_up": "上へ移動", - "move_down": "下へ移動", - "tiebreak_note": "最大 3 レベル。後のレベルは同順位の場合に使われます。「新しい順」は常に最後に適用されます。", - "scope": { - "label": "すべてのフォルダーに適用", - "description": "オフの場合、並び順は受信トレイにのみ適用され、他のフォルダーは時系列のままです。" - }, - "unsupported_note": "お使いのメールサーバーは既読状態・スター・タグによる並べ替えに対応していません。これらのレベルは無視され、一覧は時系列のままになります。" - }, - "title": "外観", - "description": "ウェブメールの見た目をカスタマイズ", - "theme": { - "label": "テーマ", - "description": "お好みの配色を選択", - "light": "ライト", - "dark": "ダーク", - "system": "システム" - }, - "language": { - "label": "言語", - "description": "お好みの言語を選択" - }, - "font_size": { - "label": "フォントサイズ", - "description": "読みやすさのためにテキストサイズを調整", - "small": "小", - "medium": "中", - "large": "大" - }, - "list_density": { - "label": "密度", - "description": "UI全体の間隔を調整", - "extra_compact": "極小", - "compact": "コンパクト", - "regular": "標準", - "comfortable": "ゆったり" - }, - "animations": { - "label": "アニメーションを有効にする", - "description": "スムーズな遷移とエフェクトを表示" - }, - "toolbar_position": { - "label": "ツールバーの位置", - "description": "メールアクションボタン(返信、アーカイブ、削除など)の表示位置", - "top": "上部", - "below_subject": "件名の下" - }, - "toolbar_labels": { - "label": "ツールバーラベルを表示", - "description": "ツールバーアイコンの横にテキストラベルを表示します。アイコンに慣れたらスペースを節約するために無効にできます。" - }, - "hide_account_switcher": { - "label": "サイドバーのアカウント切替を非表示", - "description": "フォルダサイドバー上部のアカウント選択を非表示にします。下部のナビゲーションレールからアカウントを切り替えることは引き続き可能です。" - }, - "show_rail_account_list": { - "label": "ナビゲーションレールにアカウントアバターを表示", - "description": "ナビゲーションレールの下部に個々のアカウントの丸を表示して素早く切り替えできるようにし、その下にサインアウトボタンを配置します。" - }, - "unified_mailbox": { - "label": "統合メールボックス", - "description": "接続されたすべてのアカウントの統合フォルダ(受信トレイ、送信済みなど)を表示", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "グループ受信トレイを含める", - "description": "共有/グループ受信トレイも統合ビューに含めます。" - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "カラフルなサイドバーアイコン", - "description": "フォルダーとタグのアイコンを種類別に色分けします(受信トレイは青、迷惑メールは赤、送信済みは緑など)。モノクロのサイドバーにするには無効にしてください。" - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "メッセージの総数を表示", - "description": "フォルダーやタグの横に、未読数に加えてメッセージの総数を表示します。未読数のみを表示するには無効にします。" - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro インターフェイス(実験的)", - "description": "デスクトップ専用のパワーユーザー向けレイアウトで、マルチタブのメッセージ閲覧やアカウント横断のワークフローに対応します。標準インターフェイスには影響せず、いつでも元に戻せます。", - "open_label": "Pro インターフェイスを開く", - "back_to_standard": "標準に戻る" - }, - "cross_unread": { - "label": "すべてのアカウント: 未読", - "description": "「すべてのアカウント」セクションに、すべてのアカウントの未読メールを一覧表示する項目を表示します。迷惑メール、送信済み、アーカイブ、ゴミ箱、下書きを除くすべてのフォルダーが対象です。" - }, - "cross_starred": { - "label": "すべてのアカウント: スター付き", - "description": "「すべてのアカウント」セクションに、すべてのアカウントのスター付きメールを一覧表示する項目を表示します。迷惑メール、送信済み、アーカイブ、ゴミ箱、下書きを除くすべてのフォルダーが対象です。" - }, - "cross_all": { - "label": "すべてのアカウント: すべてのメール", - "description": "「すべてのアカウント」セクションに、すべてのアカウントのすべてのメールを一覧表示する項目を表示します。迷惑メール、送信済み、アーカイブ、ゴミ箱、下書きを除くすべてのフォルダーが対象です。" - } - }, - "keywords": { - "title": "メールラベル", - "description": "メールをカラーで整理するためのラベルを定義します。サーバーにJMAPキーワードとして保存されます。", - "add_keyword": "ラベルを追加", - "label_field": "表示名", - "label_placeholder": "例:仕事、個人、緊急", - "id_field": "ラベルID", - "id_placeholder": "例:work、personal", - "color_field": "色", - "id_exists": "このラベルIDは既に存在します", - "edit": "ラベルを編集", - "delete": "ラベルを削除", - "save": "保存", - "add": "追加", - "cancel": "キャンセル", - "migrating": "既存のメールのラベルを更新中…", - "migration_error": "既存のメールのラベルの更新に失敗しました", - "nesting": { - "label": "ネストされたラベル", - "description": "ラベルを他のラベルの下にネストし、サイドバーにツリーとして表示します。" - }, - "parent_field": "親ラベル", - "no_parent": "親ラベルなし", - "too_long": "このラベルのパスが長すぎます(最大{max}文字)", - "has_children_locked": "このラベルの下に他のラベルがネストされているため、名前と親ラベルは変更できません。先に移動または削除してください。", - "has_children_delete": "先にこのラベルの下にネストされたラベルを削除してください", - "visibility_field": "サイドバーでの表示", - "visibility": { - "show": "表示する", - "unread": "未読がある場合に表示", - "hide": "表示しない" - }, - "unrecognized": { - "title": "サーバーからラベルを復元", - "description": "ラベルの名前と色はこのブラウザーに保存されますが、ラベル自体はJMAPキーワードとしてメールに保存されています。ここに定義のないキーワードをメールから検索し、ラベルとして追加し直せます。", - "scan": "ラベルを検索", - "rescan": "再検索", - "scanning": "検索中… {total}件中{scanned}件", - "none": "メールに付いているラベルはすべてここに定義済みです。", - "messages": "{count, plural, one {#件} other {#件}}", - "add_all": "{count}件すべて追加", - "partial": "{total}件のうち最新の{scanned}件のみを検索しました。古いメールにのみ使われているラベルは見つからない場合があります。", - "error": "メールからラベルを検索できませんでした。" - } - }, - "notifications": { - "test_sound": "通知音をテスト", - "sounds": { - "default": "デフォルト(ビープ)", - "cheerful": "チアフル", - "involved": "インボルブド", - "swift": "スウィフトジェスチャー", - "relax": "リラックス" - }, - "sound_selection": { - "title": "通知音", - "description": "通知に使用する音を選択", - "choose": "サウンド", - "choose_desc": "通知音を選択し、スピーカーアイコンをクリックしてプレビュー" - }, - "email": { - "title": "メール通知", - "description": "受信メールの通知を設定", - "enabled": "メール通知", - "enabled_desc": "新しいメールが届いたときに通知を表示", - "sound": "通知音", - "sound_desc": "新しいメールが届いたときに音を鳴らす", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "カレンダー通知", - "description": "カレンダーイベントの通知を設定", - "enabled": "イベント通知", - "enabled_desc": "今後のカレンダーイベントのアラートを表示", - "sound": "通知音", - "sound_desc": "カレンダーリマインダーの音を鳴らす", - "invitation_parsing": "メール招待を解析", - "invitation_parsing_desc": "メール添付ファイルのカレンダー招待を検出し、カレンダーアクションを表示" - }, - "push": { - "confirm_disable_message": "このデバイスは、サイトが閉じているときに通知を受信しなくなります。", - "confirm_disable_title": "バックグラウンド通知を無効にしますか?", - "description": "このサイトが閉じているときに新着メールのシステム通知を受信します。Bulwark プッシュリレー経由で配信され、リレーがメール内容を見ることはありません。", - "disable": "無効化", - "enable": "有効化", - "dismiss_aria": "通知プロンプトを閉じる", - "ios_hint": "iOS では、最初にサイトをホーム画面にインストールしてください。Safari はインストールされた PWA にのみ Web Push を配信します。", - "reenable": "再登録", - "relay_desc": "デフォルトはホストされた Bulwark リレーです。セルフホストする場合のみ変更してください。", - "relay_label": "プッシュリレー", - "relay_locked": "管理者が設定", - "relay_locked_desc": "プッシュリレーの URL は管理者によって設定されており、変更できません。", - "status_active": "このデバイスで有効", - "status_busy": "処理中…", - "status_inactive": "このデバイスでは有効になっていません", - "status_unsupported": "このブラウザは Web Push をサポートしていません", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "バックグラウンド通知", - "devices_title": "登録済みのデバイス", - "devices_desc": "このアカウントのバックグラウンド通知を受け取るよう登録されているデバイスの一覧です。取り消すと、そのデバイスはすぐに受信を停止します。", - "devices_loading": "登録済みのデバイスを読み込んでいます…", - "devices_empty": "バックグラウンド通知に登録されたデバイスはありません。", - "devices_error": "登録済みのデバイスを読み込めませんでした。", - "devices_refresh": "更新", - "device_this": "このデバイス", - "device_other": "別のデバイス", - "device_status_active": "通知を配信中", - "device_status_inactive": "通知を配信したことがありません", - "device_status_unknown": "このリレーでは不明", - "device_expires": "有効期限 {date}", - "revoke": "取り消す", - "confirm_revoke_title": "このデバイスを取り消しますか?", - "confirm_revoke_message": "すぐにバックグラウンド通知の受信を停止します。そのデバイスでプッシュを再度有効にすると、あらためて登録されます。", - "confirm_revoke_message_this": "このデバイスはすぐにバックグラウンド通知の受信を停止します。" - } - }, - "language_region": { - "title": "言語、地域、時刻", - "description": "言語、日付形式、時刻形式、その他の地域設定", - "language": { - "label": "言語", - "description": "お好みの言語を選択", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "日付形式", - "description": "メール一覧での日付の表示方法", - "smart": "スマート(地域に合わせる)", - "relative": "相対表示(1時間前、2日前)", - "full": "常に完全な日付", - "preview_today": "今日:", - "preview_this_week": "今週:", - "preview_older": "それ以前:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "時刻形式", - "description": "12時間制または24時間制を選択", - "12h": "12時間制", - "24h": "24時間制" - }, - "first_day": { - "label": "週の開始日", - "description": "日曜日または月曜日から開始", - "sunday": "日曜日", - "monday": "月曜日" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "フォルダー切り替え時に検索をクリア", - "description": "フォルダー(受信トレイ、送信済みなど)をクリックすると、有効な検索をクリアしてそのフォルダーを表示します。オフの場合、検索は保持され、切り替え先のフォルダーで再実行されます。" - }, - "request_read_receipt": { - "label": "既定で開封確認を要求する", - "description": "新しいメッセージの作成時に、開封確認の要求をあらかじめ有効にします。" - }, - "read_receipt_response": { - "label": "開封確認の要求に応答する", - "description": "受信メッセージが開封確認を求めたときの動作。", - "ask": "毎回確認する", - "always": "常に送信", - "never": "送信しない" - }, - "title": "メール動作", - "description": "メールの処理方法を設定", - "mark_read": { - "label": "既読にする", - "description": "メールを開いたときに既読にするタイミング", - "instant": "即座", - "delay_3s": "3秒後", - "delay_5s": "5秒後", - "never": "既読にしない" - }, - "delete_action": { - "label": "削除動作", - "description": "メール削除時の動作", - "trash": "ゴミ箱に移動", - "trash_and_read": "ゴミ箱に移動して既読にする", - "permanent": "完全に削除", - "warning": "メールは完全に削除され、復元できません。この操作は元に戻せません。" - }, - "message_spacing": { - "label": "メッセージの余白", - "description": "閲覧時のメッセージ本文まわりの余白", - "auto": "自動(推奨)", - "always": "常に余白を追加", - "edge": "余白なし" - }, - "plain_text_font": { - "label": "プレーンテキストのフォント", - "description": "プレーンテキストメールに使用するフォント", - "mono": "等幅", - "sans": "標準(アプリと同じ)" - }, - "archive_mode": { - "label": "アーカイブ先", - "description": "アーカイブ時のメール整理方法", - "single": "単一フォルダ", - "year": "年ごとのフォルダ", - "month": "月ごとのフォルダ", - "reorganize": "既存アーカイブを再整理", - "reorganize_success": "{count, plural, =0 {再整理するメールはありません} other {#通のメールを再整理しました}}", - "reorganize_error": "アーカイブの再整理に失敗しました" - }, - "permanently_delete_junk": { - "label": "迷惑メールを完全に削除", - "description": "迷惑メールフォルダのメールをゴミ箱に移動せずに完全に削除する" - }, - "mail_layout": { - "label": "メールレイアウト", - "description": "クラシックな分割読み取りペインとGmailスタイルの集中読み取りフローから選択します。", - "split": "分割ペイン", - "split_description": "メッセージリストと読み取りペインを並べて表示します。", - "focus": "集中リスト", - "focus_description": "メッセージごとに1行表示し、フォルダサイドバーを表示したままメールを全幅で開きます。", - "horizontal": "閲覧ペインを下に表示", - "horizontal_description": "メッセージ一覧を上に表示し、選択したメッセージを下の閲覧ペインで開きます。" - }, - "disable_threading": { - "label": "会話グループ化を無効にする", - "description": "メールを会話ごとにグループ化せず、個別のメッセージとして表示します" - }, - "plain_text_mode": { - "label": "プレーンテキストのみ", - "description": "リッチテキストエディターを無効にし、返信や転送を含むすべてのメールをプレーンテキストのみで送信します" - }, - "auto_select_reply_identity": { - "label": "返信元アドレスを自動選択", - "description": "返信時に、元のメッセージを受信したIDへ差出人アドレスを自動的に切り替えます" - }, - "reply_identity_match": { - "label": "受信アドレスの照合", - "description": "どの受信アドレスを自分のものとみなすか。「完全一致のみ」は設定済みのアイデンティティからのみ選択します。「同じドメイン」はアイデンティティのドメイン上の他のアドレスもキャッチオールのエイリアスとして扱い、From ヘッダーをそのアドレスに書き換えます。それらのアドレスが配布リストの場合は完全一致を選択してください。", - "exact": "完全一致のみ", - "domain": "自分のドメイン上の任意のアドレス" - }, - "signature_position": { - "label": "署名の位置", - "description": "返信や転送で署名を挿入する位置。引用テキストの上は返信の締めとして自然に読めます。下は元のメッセージを続けて表示します。", - "above_quote": "引用テキストの前", - "below_quote": "引用テキストの後" - }, - "signature_separator": { - "label": "署名区切り", - "description": "署名の前に標準の区切り行「-- 」(RFC 3676)を付けます。本文から署名へ直接続けたい場合はオフにしてください。" - }, - "sub_address_delimiter": { - "label": "サブアドレス区切り文字", - "description": "ユーザー名とサブアドレスタグを区切る文字です。お使いのメールサーバーが使用する区切り文字に合わせてください(例: user{delimiter}tag@domain.com)。", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "カスタム…", - "custom_input_label": "カスタム区切り文字" - }, - "show_preview": { - "label": "プレビューテキストを表示", - "description": "リストにメールのプレビューを表示", - "focus_description": "集中型1行メッセージリスト内にインラインプレビューテキストを表示" - }, - "attachment_click_action": { - "label": "添付ファイルクリック時の動作", - "description": "添付ファイルをクリックしたときに、プレビューを開くかすぐにダウンロードするかを選択します", - "preview": "可能ならプレビューを開く", - "download": "すぐにダウンロード" - }, - "attachment_position": { - "label": "添付ファイルの位置", - "description": "メールヘッダー内での添付ファイルの表示位置", - "beside-sender": "送信者の横", - "below-header": "ヘッダーの下" - }, - "emails_per_page": { - "10": "10件", - "25": "25件", - "50": "50件", - "100": "100件", - "label": "ページごとのメール数", - "description": "一度に読み込むメールの数" - }, - "always_light_mode": { - "label": "メールを常にライトモードで表示", - "description": "アプリがダークモードの場合でもメールをライトモードで表示し、ダークモード変換の問題を回避します" - }, - "external_content": { - "label": "外部コンテンツ", - "description": "画像と外部コンテンツの処理方法", - "ask": "常に確認", - "block": "常にブロック", - "allow": "常に許可" - }, - "trusted_senders": { - "label": "信頼する送信者", - "description": "画像が自動的に読み込まれる送信者を管理", - "count_zero": "なし", - "count_one": "1件", - "count_other": "{count}件", - "modal_title": "信頼する送信者", - "empty_title": "信頼する送信者はまだありません", - "empty_description": "ブロックされた画像を含むメールを表示するときに、「常にこの送信者を信頼する」をクリックしてここに追加します。", - "add_manually": "送信者を手動で追加", - "add_button": "追加", - "add_placeholder": "メールアドレスを入力", - "search_placeholder": "送信者を検索...", - "no_results": "検索に一致する送信者がありません", - "remove": "削除", - "close": "閉じる", - "invalid_email": "有効なメールアドレスを入力してください", - "already_added": "この送信者はすでに信頼されています", - "save_error": "保存に失敗しました - 詳細は連絡先デバッグログを確認してください", - "use_address_book_label": "アドレス帳と同期", - "use_address_book_description": "信頼済み送信者を専用の「信頼済み送信者」アドレス帳に保存し、すべてのデバイスで同期します" - }, - "hover_actions": { - "label": "ホバークイックアクション", - "description": "リスト内のメールにカーソルを合わせたときに表示するクイックアクションを選択", - "delete": "削除", - "star": "スター付け / 解除", - "mark_read": "既読 / 未読にする", - "archive": "アーカイブ", - "tag": "ラベル", - "spam": "スパムとしてマーク", - "not_spam": "迷惑メールではない", - "none_selected": "アクションが選択されていません", - "mode_label": "表示モード", - "mode_inline": "インライン", - "mode_floating": "フローティング", - "corner_label": "フローティング位置", - "corner_top-left": "左上", - "corner_top-right": "右上", - "corner_bottom-left": "左下", - "corner_bottom-right": "右下" - }, - "default_mail_program": { - "label": "既定のメールプログラム", - "description": "{appName}をmailto:リンクの既定のメールプログラムとして登録します", - "button": "既定に設定", - "success": "ブラウザに既定として設定するよう要求しました", - "error": "お使いのブラウザはこの機能をサポートしていません" - }, - "attachment_reminder": { - "label": "添付ファイルリマインダー", - "description": "メッセージに添付ファイルの言及があるのに添付されていない場合、送信前に警告します", - "keywords_label": "トリガーキーワード", - "keywords_description": "メッセージ内で見つかるとリマインダーを起動する単語やフレーズ", - "add_placeholder": "キーワードを追加...", - "add": "Add", - "remove": "削除" - }, - "empty_subject_warning": { - "label": "件名なしの警告", - "description": "件名のないメッセージを送信する前に確認します。" - }, - "hide_inline_image_attachments": { - "label": "添付ファイル一覧からインライン画像を隠す", - "description": "本文に埋め込まれた画像を個別の添付ファイルとして表示しません" - }, - "attachment_image_previews": { - "label": "添付ファイル内の画像プレビューを表示", - "description": "画像の添付ファイルを汎用ファイルアイコンではなくサムネイルカードとして表示します" - }, - "send_delay": { - "label": "送信取り消し / 送信遅延", - "description": "通常の送信をサーバー側で短時間遅延します。", - "off": "オフ", - "seconds": "{seconds} 秒", - "unsupported": "現在のアカウントは遅延送信のサポートを通知していません。この設定は他のアカウント用に保存されます。" - }, - "return_to_list_after_action": { - "label": "削除または未読にした後に一覧へ戻る", - "description": "開いているメッセージを削除または未読にした後、次のメッセージを開かずにメッセージ一覧へ戻ります。" - }, - "swipe_right_action": { - "label": "右スワイプの操作(モバイル)", - "description": "タッチデバイスで、リスト内のメッセージを右にスワイプしたときに実行される操作です。" - }, - "swipe_left_action": { - "label": "左スワイプの操作(モバイル)", - "description": "タッチデバイスで、リスト内のメッセージを左にスワイプしたときに実行される操作です。" - }, - "swipe_actions": { - "none": "なし", - "archive": "アーカイブ", - "delete": "削除", - "mark_read": "既読/未読を切り替え", - "star": "スターを切り替え", - "spam": "迷惑メールに設定" - }, - "rtl_editing": { - "label": "右から左への編集サポート", - "description": "エディターのツールバーに文字方向ボタンを追加し、段落を左から右または右から左に設定できます" - } - }, - "composer": { - "title": "作成", - "description": "メール作成の設定を構成", - "autosave": { - "label": "自動保存間隔", - "description": "下書きを自動保存する頻度", - "30s": "30秒ごと", - "1m": "1分ごと", - "2m": "2分ごと", - "5m": "5分ごと" - }, - "send_confirmation": { - "label": "送信確認", - "description": "メール送信前に確認を求める" - }, - "default_reply": { - "label": "デフォルトの返信モード", - "description": "返信をクリックしたときのデフォルト動作", - "reply": "返信", - "reply_all": "全員に返信" - } - }, - "privacy": { - "title": "プライバシーとセキュリティ", - "description": "プライバシーとセキュリティ設定を管理", - "external_images": { - "label": "外部画像をブロック", - "description": "外部画像による追跡を防止" - }, - "session_timeout": { - "label": "セッションタイムアウト", - "description": "非アクティブ時に自動的にログアウト", - "never": "なし", - "30m": "30分", - "1h": "1時間", - "4h": "4時間" - }, - "clear_cache": { - "label": "キャッシュをクリア", - "description": "キャッシュデータと一時ファイルを削除", - "button": "キャッシュをクリア", - "confirm": "キャッシュをクリアしてもよろしいですか?", - "success": "キャッシュをクリアしました" - } - }, - "account": { - "title": "アカウント", - "description": "アカウント情報を表示", - "name_label": "表示名", - "username_label": "ユーザー名", - "account_type_label": "アカウントタイプ", - "auth_method_label": "認証方法", - "auth_method_oauth": "シングルサインオン (OAuth/OIDC)", - "auth_method_basic": "パスワード", - "demo_account": "デモアカウント", - "email": { - "label": "メールアドレス", - "value": "{email}" - }, - "server": { - "label": "JMAPサーバー", - "value": "{server}" - }, - "storage": { - "label": "ストレージ使用量", - "used": "{total}中{used}使用", - "percentage": "{percent}%使用" - }, - "last_sync": { - "label": "最終同期", - "value": "{time}" - }, - "accounts": { - "title": "ログイン中のアカウント", - "description": "ドラッグしてアカウントメニューに表示される順序を変更", - "active": "現在アクティブなアカウント", - "default_badge": "デフォルトアカウント", - "set_default": "デフォルトに設定", - "switch_to": "このアカウントに切り替え", - "move_up": "上に移動", - "move_down": "下に移動", - "drag_handle": "ドラッグして並べ替え", - "add": "アカウントを追加" - }, - "shared_accounts": { - "title": "自分と共有", - "description": "管理できるグループアカウントと共有アカウントです。1つ選択して、フィルター、不在応答、カレンダー、連絡先を編集します。", - "shared_label": "共有アカウント" - } - }, - "security": { - "title": "アカウントセキュリティ", - "description": "パスワード、二要素認証、セキュリティ設定を管理します", - "detecting": "サーバー機能を検出中...", - "not_available": "このメールサーバーではアカウントセキュリティ管理は利用できません。必要な権限が無効になっている可能性があります。詳細はドキュメントを参照してください。", - "password": { - "title": "パスワードの変更", - "current": "現在のパスワード", - "new": "新しいパスワード", - "confirm": "新しいパスワードの確認", - "submit": "パスワードを変更", - "success": "パスワードが正常に変更されました", - "error_title": "パスワードの変更に失敗しました", - "error_mismatch": "新しいパスワードが一致しません", - "error_min_length": "パスワードは8文字以上である必要があります", - "error_generic": "パスワードを変更できませんでした" - }, - "display_name": { - "label": "表示名", - "description": "サーバー上に表示される名前", - "placeholder": "表示名を入力", - "save": "保存", - "success": "表示名が更新されました", - "error": "表示名を更新できませんでした" - }, - "totp": { - "section_title": "二要素認証", - "label": "TOTP認証", - "description": "時間ベースのワンタイムパスワードで追加のセキュリティレイヤーを追加", - "active": "有効", - "inactive": "無効", - "enabled": "二要素認証が有効になりました", - "disabled": "二要素認証が無効になりました", - "enable_error": "2FAを有効にできませんでした", - "disable_error": "2FAを無効にできませんでした", - "setup_instructions": "このURLを認証アプリ(Google Authenticator、Authyなど)にコピーしてください:", - "verification_code": "確認コード", - "confirm": "確認", - "disable": "無効化", - "disable_confirm_prompt": "二要素認証を無効にするにはパスワードを入力してください。", - "password_required": "パスワードが必要です", - "code_required": "確認コードが必要です", - "code_invalid": "確認コードが無効です。認証アプリを確認してもう一度お試しください。" - }, - "app_passwords": { - "title": "アプリパスワード", - "description": "二要素認証に対応していないアプリ用のパスワードを作成", - "add": "追加", - "create": "作成", - "cancel": "キャンセル", - "generate": "生成", - "name_label": "アプリ名", - "name_placeholder": "例:Thunderbird、iPhone Mail", - "password_label": "パスワード(空欄で自動生成)", - "password_placeholder": "空欄の場合自動生成", - "added": "アプリパスワードが作成されました", - "removed": "アプリパスワードが削除されました", - "add_error": "アプリパスワードを作成できませんでした", - "remove_error": "アプリパスワードを削除できませんでした", - "none": "アプリパスワードは設定されていません", - "done": "完了", - "expires_label": "有効期限(任意)", - "copy_now_warning": "今すぐこのパスワードをコピーしてください - 再表示されません。", - "allowed_ips_label": "許可するIP(任意)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "カンマまたは空白で区切ります。空欄の場合、すべてのIPを許可します。" - }, - "api_keys": { - "title": "APIキー", - "description": "サーバーと直接通信するスクリプトや連携用にAPIキーを作成します", - "name_label": "キー名", - "name_placeholder": "例: バックアップスクリプト、CIランナー", - "copy_now_warning": "このAPIキーを今すぐコピーしてください - 二度と表示されません。", - "added": "APIキーを作成しました", - "removed": "APIキーを削除しました", - "add_error": "APIキーの作成に失敗しました", - "remove_error": "APIキーの削除に失敗しました", - "none": "APIキーは設定されていません" - }, - "encryption": { - "section_title": "保存時の暗号化", - "label": "メール暗号化", - "description": "サーバー上の保存メールを暗号化してプライバシーを強化", - "active": "{type}暗号化が有効", - "inactive": "無効", - "enabled": "保存時の暗号化が有効になりました", - "disabled_success": "保存時の暗号化が無効になりました", - "error": "暗号化設定を更新できませんでした", - "algorithm_label": "暗号化アルゴリズム", - "encrypt_on_append": "アップロード時に新しいメールを暗号化", - "allow_spam_training": "メールを暗号化する前に迷惑メール学習を許可", - "enable_encryption": "暗号化を有効にする", - "enable_tooltip": "この鍵で暗号化を有効にする", - "configure_title": "この鍵で暗号化を有効にする", - "enable_button": "有効にする", - "disable_tooltip": "暗号化を無効にする", - "enabled_success": "保存時の暗号化が有効になりました" - }, - "email_client": { - "title": "メールクライアントの設定", - "description": "これらの資格情報を使用して、デスクトップまたはモバイルのメールクライアント(Thunderbird、Apple Mail、Outlookなど)を設定します", - "jmap_username_label": "JMAPユーザー名", - "copy": "コピー", - "copied": "コピー済み", - "password_instructions": "上記のJMAPユーザー名とアプリパスワードを使用してメールクライアントにサインインします。まだ作成していない場合は、上のセクションでアプリパスワードを作成してください。" - }, - "link_device": { - "title": "モバイルアプリを連携", - "description": "入力なしで Bulwark Mail モバイルアプリにサインインできます。ここで QR コードを生成し、アプリのログイン画面からスキャンしてください。", - "generate": "QRコードを表示", - "regenerate": "新しいコードを表示", - "instructions": "Bulwark Mail アプリを開き、ログイン画面で「QRコードをスキャン」をタップして、カメラをここに向けてください。", - "expires_in": "このコードは{seconds}秒で期限切れになります。一度しか使用できません。", - "expired": "このコードは期限切れです。", - "generating": "生成中…", - "error": "ペアリングコードを作成できませんでした。もう一度お試しください。" - }, - "public_keys": { - "name_label": "公開鍵の名前", - "name_placeholder": "例:マイキー", - "key_label": "公開鍵(S/MIME または PGP の ASCII 形式)", - "key_placeholder": "ここに公開鍵を貼り付けてください", - "none": "公開鍵が設定されていません" - } - }, - "identities": { - "title": "送信者情報", - "description": "送信に使用するメールアドレスを管理", - "identities_count": { - "label": "送信者情報", - "description": "送信用に設定されたメールアドレス", - "count_zero": "送信者情報なし", - "count_one": "1件", - "count_other": "{count}件" - }, - "manage": "送信者情報を管理", - "sub_addressing": { - "label": "サブアドレス", - "description": "user+tag@domain.comのようなタグを使用して受信メールを整理", - "learn_more": "詳細" - } - }, - "vacation": { - "title": "不在応答", - "description": "不在時に受信メールへ自動返信します", - "loading": "不在応答設定を読み込み中...", - "not_supported": "メールサーバーは不在応答をサポートしていません。", - "fetch_error": "不在応答設定の読み込みに失敗しました。もう一度お試しください。", - "status": { - "label": "不在応答", - "description": "メールを送信してきた方に自動返信を送信します", - "active": "有効", - "inactive": "無効" - }, - "date_range": { - "title": "期間", - "description": "オプションで自動返信を特定の期間に限定します", - "start": "開始日", - "start_description": "空白のままにすると開始制限なし", - "end": "終了日", - "end_description": "空白のままにすると終了制限なし" - }, - "message": { - "title": "自動返信メッセージ", - "description": "返信として送信されるメッセージ", - "subject_label": "件名", - "subject_description": "自動返信の件名", - "subject_placeholder": "不在通知", - "body_label": "メッセージ本文", - "body_description": "テキスト形式のメッセージ内容", - "body_placeholder": "メールをいただきありがとうございます。現在不在にしており、戻り次第ご返信いたします。", - "html_label": "書式付きメッセージ(HTML)", - "html_description": "リンクやスタイルを含む書式付きの版を追加します。表示できないメールソフトの受信者には上記のプレーンテキストが表示されます。", - "html_placeholder": "書式付きの不在返信を入力…" - }, - "preview": { - "title": "プレビュー", - "show": "プレビューを表示", - "hide": "プレビューを非表示" - }, - "save": "変更を保存", - "saving": "保存中...", - "warnings": { - "end_before_start": "終了日は開始日より後に設定してください", - "start_in_past": "開始日が過去の日付です", - "empty_body": "メッセージ本文が空です - 受信者には空の返信が届きます", - "subject_too_long": "件名がサーバーの上限を超えています(最大 {max} バイト)。", - "body_too_long": "メッセージがサーバーの上限を超えています(書式を含めて最大 {max} バイト)。" - } - }, - "folders": { - "role_memos": "メモ", - "title": "フォルダー", - "description": "メールフォルダーを管理し、標準の役割を割り当てます", - "folder_list": "フォルダー一覧", - "folder_list_description": "フォルダーアイコンをクリックしてカスタマイズ", - "standard_roles": "標準フォルダーの役割", - "standard_roles_description": "受信トレイ、送信済み、ゴミ箱などの標準の役割に使用するフォルダーを設定します。", - "role_inbox": "受信トレイ", - "role_drafts": "下書き", - "role_sent": "送信済み", - "role_trash": "ゴミ箱", - "role_junk": "迷惑メール", - "role_archive": "アーカイブ", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "なし", - "create_folder": "フォルダーを作成", - "create_subfolder": "サブフォルダーを作成", - "reorder": "ドラッグして並べ替え", - "reorder_error": "フォルダーの並べ替えに失敗しました", - "move_error": "フォルダーの移動に失敗しました", - "subfolder_of": "{name} の中", - "subfolder_name": "サブフォルダー名", - "new_folder_name": "フォルダー名", - "rename": "名前を変更", - "change_icon": "アイコンを変更", - "delete": "削除", - "confirm_delete": "「{name}」を削除してもよろしいですか?このフォルダー内のメールはゴミ箱に移動されます。", - "create": "作成", - "cancel": "キャンセル", - "no_folders": "カスタムフォルダーはありません", - "cannot_delete_role": "標準の役割が割り当てられたフォルダーは削除できません。先に役割を解除してください。", - "folder_created": "フォルダーを作成しました", - "folder_renamed": "フォルダー名を変更しました", - "folder_deleted": "フォルダーを削除しました", - "role_updated": "フォルダーの役割を更新しました", - "error_create": "フォルダーの作成に失敗しました", - "error_rename": "フォルダー名の変更に失敗しました", - "error_delete": "フォルダーの削除に失敗しました", - "error_delete_has_children": "フォルダーを削除できません:サブフォルダーが含まれています。先にサブフォルダーを削除または移動してください。", - "error_delete_has_email": "フォルダーを削除できません:メールが含まれています。先にメールを移動または削除してください。", - "error_role": "フォルダーの役割の更新に失敗しました" - }, - "advanced": { - "title": "詳細設定", - "description": "詳細オプションと開発者設定", - "debug_mode": { - "label": "デバッグモード", - "description": "トラブルシューティング用の詳細ログを有効化" - }, - "debug_categories": { - "description": "記録するカテゴリを選択してください。不要なカテゴリを無効にすると、コンソールのノイズを減らせます。", - "jmap": "JMAPクライアント", - "jmap_description": "メールボックス操作、メール取得、JMAPプロトコル要求", - "calendar": "カレンダー", - "calendar_description": "カレンダーイベント、インポート、スケジュールメッセージ", - "tasks": "タスク", - "tasks_description": "カレンダータスクの作成、取得、更新", - "auth": "認証", - "auth_description": "ログイン、TOTP、トークン交換、セッション管理", - "filters": "フィルター", - "filters_description": "Sieveフィルタールールと休暇スクリプト", - "email": "メール表示", - "email_description": "メールのレンダリング、TNEF処理、既読化", - "push": "プッシュ通知", - "push_description": "プッシュ通知の設定と配信", - "contacts": "連絡先とアドレス帳", - "contacts_description": "連絡先同期、アドレス帳操作、信頼済み送信者" - }, - "settings_sync": { - "label": "設定の同期", - "description": "ブラウザーやデバイス間で設定を同期します" - }, - "sender_favicons": { - "label": "送信者ファビコン", - "description": "ビジネス送信者のプロフィール画像としてウェブサイトアイコンを表示" - }, - "show_avatars_in_junk": { - "label": "迷惑メールフォルダでアバターを表示", - "description": "迷惑メールフォルダの送信者にプロフィール画像とファビコンを表示します。フィッシング詐欺に正規のような見た目を与えないため、既定では無効です。" - }, - "keyboard_shortcuts": { - "label": "キーボードショートカット", - "description": "利用可能なキーボードショートカットを表示", - "button": "ショートカットを表示" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "設定をリセット", - "description": "すべての設定をデフォルト値に戻す", - "button": "デフォルトにリセット" - }, - "export_settings": { - "label": "設定をエクスポート", - "description": "設定をJSONとしてダウンロード", - "button": "エクスポート" - }, - "import_settings": { - "label": "設定をインポート", - "description": "JSONファイルから設定をアップロード", - "button": "インポート" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "サイドバーアプリ", - "description": "サイドバーのカスタムアプリとリンクを管理", - "keep_loaded": "アプリを読み込んだまま保持", - "keep_loaded_description": "切り替え時にインラインアプリをバックグラウンドで実行し続けて再読み込みを回避", - "managed_title": "管理者によって提供", - "managed_description": "これらのアプリは全員向けに設定されており、変更できません。", - "manage_title": "カスタムアプリ", - "manage_description": "サイドバーのカスタムアプリを追加、編集、削除" - }, - "contacts": { - "title": "連絡先", - "description": "連絡先のインポートとエクスポート", - "import_label": "連絡先をインポート", - "import_description": "vCardファイル(.vcf)から連絡先をインポート", - "export_label": "連絡先をエクスポート", - "export_description": "すべての連絡先をvCardファイル(.vcf)としてエクスポート", - "manage_title": "アドレス帳", - "manage_description": "アドレス帳の名前を変更", - "no_address_books": "アドレス帳が見つかりません", - "categories_title": "カテゴリ", - "categories_description": "連絡先カテゴリの名前を変更", - "no_categories": "カテゴリが見つかりません", - "group_by_letter_description": "連絡先リストにアルファベット順のセクション見出しを表示", - "group_by_letter_label": "頭文字でグループ化", - "sort_by_last_name_label": "姓で並べ替え", - "sort_by_last_name_description": "家族が並んで表示されるように、連絡先一覧を姓の順に並べ替えます" - }, - "downloads": { - "title": "ダウンロード", - "description": "ダウンロードしたメールや添付ファイルの名前の付け方をカスタマイズします。", - "reset": "既定に戻す", - "preview": "プレビュー:", - "email_template": { - "label": "メールのファイル名 (.eml)", - "description": "メールをエクスポートするか、ファイルシステムにドラッグしたときに使用するテンプレートです。.eml 拡張子は自動的に付加されます。" - }, - "attachment_template": { - "label": "添付ファイルのファイル名", - "description": "添付ファイルをダウンロードまたはドラッグするときに使用するテンプレートです。'{filename}' と '{ext}' を省略すると、元の拡張子が保持されます。" - }, - "bundle_template": { - "label": "複数メールの .zip ファイル名", - "description": "複数の選択したメールを 1 つの .zip としてドラッグまたはダウンロードするときに使用するテンプレートです。拡張子 .zip は自動的に付加されます。" - }, - "spaces": { - "label": "スペース", - "description": "最終的なファイル名のスペースを別の文字に置き換えます。", - "keep": "スペースを残す", - "underscore": "_ に置換", - "dash": "- に置換" - }, - "lowercase": { - "label": "小文字", - "description": "ファイル名全体を小文字に強制します。" - }, - "strip_diacritics": { - "label": "ダイアクリティカルマークを除去", - "description": "アクセント付き文字を ASCII の同等の文字に変換します (ä → a, é → e)。Unicode のファイル名を正しく扱えないツールで便利です。" - }, - "collapse_separators": { - "label": "連続した区切り文字をまとめる", - "description": "連続するスペース、アンダースコア、ハイフンを 1 文字にまとめます。" - }, - "after_export": { - "label": "エクスポート後", - "description": "メールを .eml としてエクスポートした後、必要に応じて移動します。", - "keep": "メールボックスに残す", - "archive": "アーカイブへ移動", - "trash": "ごみ箱へ移動" - } - }, - "filters": { - "title": "メールフィルター", - "description": "受信メールを自動で振り分け、ラベル付け、管理するルールを作成します", - "add_rule": "ルールを追加", - "no_rules": "フィルタールールなし", - "vacation_active": "不在応答が有効です", - "vacation_active_description": "受信メッセージへの自動返信が有効になっています", - "vacation_configure": "設定", - "no_rules_description": "受信メールを自動で整理するルールを作成してください", - "edit_rule": "ルールを編集", - "new_rule": "新しいルール", - "delete_rule": "ルールを削除", - "delete_confirm": "このルールを削除してもよろしいですか?", - "enable": "有効にする", - "disable": "無効にする", - "raw_editor": "Sieveスクリプトエディター", - "raw_editor_warning": "Sieveスクリプトを直接編集すると、ビジュアルルール編集が使えなくなる場合があります。ここでの変更はビジュアルビルダーを上書きします。", - "validate": "検証", - "validation_success": "スクリプトは有効です", - "validation_error": "スクリプトにエラーがあります", - "save": "ルールを保存", - "saving": "保存中...", - "saved": "フィルターを保存しました", - "save_failed": "フィルターの保存に失敗しました", - "loading": "フィルターを読み込み中...", - "not_supported": "お使いのメールサーバーはメールフィルターに対応していません。", - "rule_name": "ルール名", - "rule_name_placeholder": "例:ニュースレターを振り分け", - "match_all": "すべての条件に一致", - "match_any": "いずれかの条件に一致", - "conditions": "条件", - "add_condition": "条件を追加", - "actions": "アクション", - "add_action": "アクションを追加", - "stop_processing": "以降のルールの処理を停止", - "include_spam": "迷惑メールと判定されたメッセージも移動する", - "attachment_type_placeholder": "例: pdf, doc, jpg", - "value_placeholder_multi": "値(カンマで複数指定可)", - "condition_fields": { - "attachment": "添付ファイル", - "from": "差出人", - "to": "宛先", - "cc": "Cc", - "subject": "件名", - "header": "カスタムヘッダー", - "size": "サイズ", - "body": "本文" - }, - "comparators": { - "has_any": "あり", - "has_type": "タイプ", - "contains": "を含む", - "not_contains": "を含まない", - "is": "と完全一致", - "not_is": "ではない", - "starts_with": "で始まる", - "ends_with": "で終わる", - "matches": "パターンに一致", - "greater_than": "より大きい", - "less_than": "より小さい" - }, - "action_types": { - "move": "フォルダーに移動", - "copy": "フォルダーにコピー", - "forward": "転送先", - "mark_read": "既読にする", - "star": "スターを付ける", - "add_label": "ラベルを追加", - "discard": "サイレント削除", - "reject": "メッセージ付きで拒否", - "keep": "受信トレイに保持", - "stop": "処理を停止" - }, - "move_to_folder": "フォルダーを選択", - "copy_to_folder": "フォルダーを選択", - "forward_to": "転送先メールアドレス", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "コピーを残す", - "forward_limit": "このサーバーでの1通あたりの転送上限: {count}。それを超える転送はスキップされます。", - "reject_message": "拒否メッセージ", - "reject_placeholder": "あなたのメールは拒否されました", - "label_name": "ラベル名", - "label_placeholder": "ラベルを選択", - "header_name": "ヘッダー名", - "header_placeholder": "例:X-Mailing-List", - "size_bytes": "サイズ(バイト)", - "size_placeholder": "例:1000000", - "system_managed": "システム管理ルール", - "opaque_warning": "このスクリプトはビジュアルビルダーの外部で編集されました。Sieveスクリプトの直接編集のみ可能です。", - "open_sieve_editor": "Sieveスクリプトエディタを開く", - "fetch_error": "フィルターの読み込みに失敗しました", - "expanded_view": "詳細表示", - "expanded_view_description": "フィルタールールを条件とアクションのブロックで表示", - "if": "条件", - "then": "実行", - "match_all_conditions": "すべて一致", - "match_any_condition": "いずれか一致", - "and": "かつ", - "or": "または", - "cancel": "キャンセル", - "confirm_delete": "削除", - "rule_list": "フィルタールール", - "drag_to_reorder": "ドラッグして並べ替え", - "match_type": "一致タイプ", - "reset_to_visual": "ビジュアルビルダーにリセット", - "reset_warning": "現在のスクリプトを破棄して最初からやり直します。", - "confirm_reset": "リセット", - "validation_empty_name": "ルール名は必須です", - "validation_empty_conditions": "値を持つ条件が少なくとも1つ必要です", - "validation_empty_actions": "少なくとも1つのアクションが必要です", - "templates_section": "テンプレートから作成", - "template_newsletters": "ニュースレターをフォルダに移動", - "template_receipts": "領収書を自動アーカイブ", - "template_important": "重要なメールにフラグを付ける", - "template_notifications": "通知をフィルター", - "sieve_editor": { - "title": "Sieveスクリプトエディタ", - "warning": "Sieveスクリプトを直接編集すると、ビジュアルルール編集が使用できなくなる場合があります。ここでの変更はビジュアルビルダーを上書きします。", - "script_content": "Sieveスクリプト", - "valid": "スクリプトは有効です", - "invalid": "スクリプトにエラーがあります", - "save_warning": "保存するとすべてのビジュアルルールが上書きされます。この操作は元に戻せません。もう一度「保存」をクリックして確認してください。", - "validating": "検証中...", - "validate": "検証", - "cancel": "キャンセル", - "save": "保存", - "confirm_save": "保存を確認", - "validation_failed": "検証リクエストに失敗しました" - }, - "rule_summary": { - "conditions_count": "{count, plural, other {#個の条件}}", - "actions_count": "{count, plural, other {#個のアクション}}" - }, - "origin_external": "外部", - "managed_by_tooltip": "{source} によって管理されています。そのアプリで編集するか、生の Sieve エディターを使用してください。" - }, - "templates": { - "title": "メールテンプレート", - "description": "プレースホルダー変数付きの再利用可能なメールテンプレートを作成", - "add": "新規テンプレート", - "edit": "テンプレートを編集", - "name": "テンプレート名", - "name_placeholder": "例:フォローアップメール", - "category": "カテゴリー", - "category_placeholder": "例:仕事、個人", - "subject": "件名", - "subject_placeholder": "メールの件名", - "body": "本文", - "body_placeholder": "メール本文...", - "recipients_placeholder": "email@example.com", - "identity": "送信者", - "default_identity": "デフォルトのID", - "favorite": "お気に入り", - "cancel": "キャンセル", - "create": "作成", - "update": "更新", - "confirm_delete": "削除", - "no_templates": "テンプレートがありません", - "manage": "テンプレートを管理", - "count": "{count}件のテンプレート", - "export_import": "エクスポートとインポート", - "export_import_description": "テンプレートのバックアップや別のデバイスへの転送", - "export": "エクスポート", - "import": "インポート", - "validation": { - "empty": "テンプレート名は必須です", - "too_long": "テンプレート名は200文字以内にしてください" - } - }, - "files": { - "display": { - "title": "表示", - "description": "ファイルとフォルダーの表示方法を設定します" - }, - "folder_layout": { - "label": "フォルダナビゲーション", - "description": "フォルダの表示方法を選択:ファイルとインラインで表示するか、サイドバーにツリー表示するか", - "inline": "インライン", - "sidebar": "サイドバー" - }, - "default_view": { - "label": "デフォルトビュー", - "description": "グリッドまたはリストレイアウトを選択します", - "list": "リスト", - "grid": "グリッド" - }, - "default_sort": { - "label": "デフォルトの並べ替え", - "description": "ファイルのデフォルトの並べ替えを選択します", - "name": "名前", - "size": "サイズ", - "modified": "更新日" - }, - "sort_direction": { - "label": "並べ替え方向", - "description": "昇順または降順を選択します", - "ascending": "昇順", - "descending": "降順" - }, - "icons": { - "title": "アイコン", - "description": "ファイルアイコンの外観を設定します" - }, - "show_icons": { - "label": "ファイルアイコンを表示", - "description": "ファイルとフォルダーの横にアイコンを表示します" - }, - "colored_icons": { - "label": "カラーアイコン", - "description": "モノクロの代わりにカラフルなアイコンを使用します" - }, - "show_thumbnails": { - "label": "サムネイルを表示", - "description": "画像ファイルのアイコンの代わりにプレビューを表示します" - }, - "behavior": { - "title": "動作", - "description": "ファイルブラウザーの動作を設定します" - }, - "show_hidden": { - "label": "隠しファイルを表示", - "description": "ドットで始まるファイルとフォルダーを表示します" - }, - "preview": { - "label": "プレビュー" - } - }, - "search_placeholder": "設定を検索", - "search_clear": "検索をクリア", - "search_no_results": "一致する設定はありません", - "scoped": { - "back": "自分のアカウントに戻る", - "managing": "管理中: {name}" - } - }, - "errors": { - "page_error_title": "問題が発生しました", - "page_error_description": "予期しないエラーが発生しました。もう一度お試しいただくか、ホームページに戻ってください。", - "sidebar_error": "メールボックスを読み込めません", - "email_list_error": "メールを読み込めません", - "viewer_error_title": "メールを表示できません", - "viewer_error_description": "このメールのレンダリングに問題が発生しました。サポートされていないコンテンツが含まれている可能性があります。", - "composer_error": "作成画面を読み込めません", - "settings_error_title": "設定を利用できません", - "settings_error_description": "設定を読み込めません。設定が保存されない可能性があります。", - "try_again": "再試行", - "reload": "再読み込み", - "reload_emails": "メールを再読み込み", - "reload_settings": "設定を再読み込み", - "retry": "リトライ", - "go_home": "受信トレイに移動" - }, - "context_menu": { - "reply": "返信", - "reply_all": "全員に返信", - "forward": "転送", - "mark_read": "既読にする", - "mark_unread": "未読にする", - "star": "スターを付ける", - "unstar": "スターを外す", - "pin": "ピン留め", - "unpin": "ピン留めを外す", - "move_to": "移動...", - "archive": "アーカイブ", - "delete": "削除", - "mark_as_spam": "迷惑メールを報告", - "not_spam": "迷惑メールでない", - "tag": "ラベル", - "items_selected": "{count}件のメールを選択", - "edit_draft": "下書きを編集", - "cancel_scheduled_send": "送信をキャンセル", - "reschedule_send": "再予約", - "cancel_and_edit": "キャンセルして編集", - "cancel_and_compose_again": "キャンセルして新規作成" - }, - "mailbox_context_menu": { - "mark_folder_read": "フォルダーを既読にする", - "mark_folder_tree_read": "フォルダーとサブフォルダーを既読にする", - "mark_all_folders_read": "すべてのフォルダーを既読にする", - "new_subfolder": "新しいサブフォルダー...", - "new_folder": "新しいフォルダー...", - "rename": "名前を変更...", - "share": "共有...", - "import_email": ".eml または .zip をインポート...", - "empty_folder": "フォルダーを空にする", - "empty_folder_generic": "フォルダーを空にする", - "delete_folder": "フォルダーを削除", - "refresh": "更新", - "mark_all_confirm_title": "すべてのフォルダーを既読にする", - "mark_all_confirm_message": "個人アカウントのすべての未読メッセージを既読にしますか?", - "delete_confirm_title": "フォルダーを削除", - "delete_confirm_message": "フォルダー \"{name}\" を完全に削除しますか?この操作は元に戻せません。", - "prompt_new_subfolder": "新しいサブフォルダーの名前を入力してください。", - "prompt_new_folder": "新しいフォルダーの名前を入力してください。", - "prompt_rename": "このフォルダーの新しい名前を入力してください。", - "toast_marked_read": "フォルダーを既読にしました", - "toast_marked_read_count": "{count, plural, one {1件のメッセージ} other {#件のメッセージ}}を既読にしました", - "toast_already_read": "未読メッセージはありません", - "toast_marked_all_read": "すべてのフォルダーを既読にしました", - "toast_emptied": "フォルダーを空にしました", - "toast_folder_created": "フォルダーを作成しました", - "toast_folder_renamed": "フォルダー名を変更しました", - "toast_folder_deleted": "フォルダーを削除しました", - "toast_error_mark_read": "既読にできませんでした", - "toast_error_empty": "フォルダーを空にできませんでした", - "toast_error_create": "フォルダーを作成できませんでした", - "toast_error_rename": "フォルダー名を変更できませんでした", - "toast_error_delete": "フォルダーを削除できませんでした", - "toast_error_delete_has_children": "フォルダーにサブフォルダーがあります。先に削除してください。", - "toast_error_delete_has_email": "フォルダーが空ではありません。先に空にしてください。", - "delete_with_contents_title": "フォルダーが空ではありません", - "delete_with_contents_message": "フォルダー「{name}」にはまだ {count} 件のメッセージがあります。フォルダーとすべてのメッセージを削除しますか?この操作は元に戻せません。", - "delete_with_contents_confirm": "フォルダーとメッセージを削除", - "placeholder_folder_name": "フォルダー名", - "create": "作成", - "rename_confirm": "名前を変更" - }, - "shortcuts": { - "title": "キーボードショートカット", - "tip": "? キーを押すといつでもこのヘルプを表示できます", - "sections": { - "navigation": "ナビゲーション", - "actions": "メール操作", - "global": "全般", - "threads": "スレッド", - "composer": "作成" - }, - "navigation": { - "next_email": "次のメール", - "previous_email": "前のメール", - "open_email": "メールを開く", - "close_email": "閉じる / 選択解除" - }, - "actions": { - "reply": "返信", - "reply_all": "全員に返信", - "forward": "転送", - "star": "スターの切り替え", - "archive": "アーカイブ", - "delete": "削除", - "mark_unread": "未読にする", - "mark_read": "既読にする", - "toggle_spam": "迷惑メールを報告 / 迷惑メールでない" - }, - "global": { - "compose": "新規メールを作成", - "search": "検索にフォーカス", - "help": "ショートカットを表示", - "refresh": "メールを更新", - "select_all": "すべて選択", - "global_search": "すべてを検索" - }, - "threads": { - "expand_collapse": "スレッドの展開/折りたたみ" - }, - "composer": { - "send": "メールを送信", - "schedule_send": "送信を予約", - "template_picker": "テンプレートピッカーを開く" - } - }, - "threads": { - "messages_one": "{count}件のメッセージ", - "messages_other": "{count}件のメッセージ", - "messages_tooltip": "{count, plural, other {この会話の#件のメッセージ}}", - "expand": "会話を展開", - "collapse": "会話を折りたたむ", - "loading": "会話を読み込み中...", - "mark_read": "会話を既読にする", - "mark_unread": "会話を未読にする", - "archive": "会話をアーカイブ", - "delete": "会話を削除", - "star": "会話にスターを付ける", - "unstar": "会話のスターを外す", - "toggle_thread": "スレッドの展開/折りたたみ" - }, - "identities": { - "modal_title": "送信者情報を管理", - "create_new": "新しい送信者情報を作成", - "edit_identity": "送信者情報を編集", - "delete_confirm": "この送信者情報を削除しますか?この操作は元に戻せません。", - "cannot_delete": "この送信者情報は削除できません", - "primary_identity": "プライマリ", - "set_as_primary": "プライマリに設定", - "no_identities": "送信者情報が見つかりません", - "display": { - "reply_to": "返信先:", - "bcc": "BCC:", - "signature": "署名:", - "preview": "プレビュー:" - }, - "validation_errors": { - "invalid_emails": "無効なメールアドレス: {emails}", - "unknown_error": "不明なエラー" - }, - "form": { - "name_label": "表示名", - "name_placeholder": "例: 仕事用、個人用", - "name_required": "名前は必須です", - "email_label": "メールアドレス", - "email_placeholder": "your.email@example.com", - "email_required": "メールアドレスは必須です", - "email_invalid": "有効なメールアドレスを入力してください", - "email_immutable": "メールアドレスは作成後に変更できません", - "reply_to_label": "返信先(オプション)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "自動BCC(オプション)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "テキスト署名", - "html_signature_label": "HTML署名", - "signature_byte_counter": "{bytes} / {max} バイト", - "signature_byte_limit_reached": "サーバーの上限に達しました", - "save": "送信者情報を保存", - "cancel": "キャンセル", - "creating": "作成中...", - "updating": "更新中..." - }, - "sub_address": { - "button_tooltip": "サブアドレスを使用", - "popover_title": "サブアドレスタグを追加", - "tag_input_placeholder": "タグを入力(例: shopping)", - "preview_label": "プレビュー:", - "recent_tags": "最近のタグ", - "suggested_tags": "候補", - "use_address": "このアドレスを使用", - "invalid_tag": "タグは英数字とハイフンのみ使用できます", - "tag_too_long": "タグは30文字以内にしてください", - "help_text": "user{delimiter}tag@domain.comに送信されたメールは受信トレイに届きます", - "validation": { - "empty": "タグは空にできません", - "too_long": "タグは{max}文字以内にしてください", - "invalid_chars": "タグは文字、数字、ハイフンのみ使用できます" - } - }, - "badge": { - "sent_via": "経由", - "sub_address_tag": "サブアドレスを使用して送信: {tag}", - "identity_name": "送信者情報を使用して送信: {name}", - "identity_short": "{name}経由", - "subaddress_tag": "+{tag}" - }, - "delete_button": "削除", - "delete_confirm_title": "IDの削除" - }, - "templates": { - "picker_title": "テンプレートを選択", - "search_placeholder": "テンプレートを検索...", - "section_favorites": "お気に入り", - "section_recent": "最近使用", - "section_uncategorized": "その他", - "no_templates": "テンプレートがありません", - "no_results": "テンプレートが見つかりません", - "fill_placeholders": "プレースホルダーの値を入力", - "enter_value": "値を入力...", - "preview": "プレビュー", - "insert_with_values": "値を挿入", - "insert_raw": "そのまま挿入", - "copy_suffix": "(コピー)", - "placeholder": "変数", - "placeholders": { - "recipient_name": "宛先の名前", - "company": "会社名", - "date": "今日の日付", - "day_of_week": "曜日", - "sender_name": "あなたの名前" - } - }, - "contacts": { - "title": "連絡先", - "search_placeholder": "連絡先を検索...", - "create_new": "新しい連絡先", - "no_category": "カテゴリなし", - "rename_category": "カテゴリの名前を変更", - "category_name_label": "カテゴリ名", - "category_renamed": "カテゴリの名前を変更しました", - "category_rename_failed": "カテゴリの名前変更に失敗しました", - "category_added": "{name} に連絡先を追加しました", - "category_added_plural": "{count} 件の連絡先を {name} に追加しました", - "empty_state": "連絡先がありません", - "empty_state_title": "連絡先がありません", - "empty_state_subtitle": "最初の連絡先を作成するか、vCardファイルからインポートしてください", - "empty_search": "検索に一致する連絡先がありません", - "empty_search_hint": "別の検索語をお試しください", - "empty_filtered": "フィルターに一致する連絡先がありません", - "empty_filtered_hint": "フィルターを調整またはクリアしてください", - "clear_search": "検索をクリア", - "import_vcard": "vCardをインポート", - "delete_confirm_title": "連絡先を削除", - "delete_confirm": "この連絡先を削除してもよろしいですか?", - "local_mode": "連絡先はローカルに保存されています(サーバーがJMAPコンタクトをサポートしていません)", - "back_to_contacts": "連絡先に戻る", - "back_to_email": "メールに戻る", - "tabs": { - "all": "すべて", - "groups": "グループ" - }, - "shared": { - "title": "共有" - }, - "address_books": { - "create": "新しいアドレス帳", - "created": "アドレス帳を作成しました", - "create_failed": "アドレス帳の作成に失敗しました", - "title": "マイアドレス帳", - "shared_prefix": "共有: {name}", - "moved": "連絡先を {name} に移動しました", - "moved_plural": "{count} 件の連絡先を {name} に移動しました", - "move_failed": "連絡先の移動に失敗しました", - "address_book": "アドレス帳", - "rename": "アドレス帳の名前を変更", - "name_label": "アドレス帳名", - "renamed": "アドレス帳の名前を変更しました", - "rename_failed": "アドレス帳の名前変更に失敗しました", - "default": "デフォルト", - "set_default": "デフォルトに設定", - "default_updated": "デフォルトのアドレス帳を更新しました", - "set_default_failed": "デフォルトのアドレス帳を設定できませんでした", - "manage": "アドレス帳を管理", - "share": "アドレス帳を共有", - "new_contact_in_book": "このアドレス帳に新規連絡先", - "delete": "アドレス帳を削除", - "confirm_delete": "「{name}」を削除しますか?このアドレス帳のすべての連絡先が削除されます。", - "deleted": "アドレス帳を削除しました", - "delete_failed": "アドレス帳の削除に失敗しました" - }, - "detail": { - "emails": "メールアドレス", - "phones": "電話番号", - "organizations": "組織", - "addresses": "住所", - "notes": "メモ", - "titles": "肩書きと役割", - "online_services": "オンラインサービス", - "anniversaries": "記念日", - "personal_info": "個人情報", - "languages": "言語", - "categories": "カテゴリー", - "related_contacts": "関連する連絡先", - "crypto_keys": "暗号鍵", - "no_contact_selected": "連絡先を選択して詳細を表示", - "compose_email": "メールを作成", - "copy_email": "メールをコピー", - "copy_phone": "電話番号をコピー", - "copy_url": "URLをコピー", - "copied": "クリップボードにコピーしました", - "copy_failed": "クリップボードへのコピーに失敗しました", - "created": "作成日", - "updated": "最終更新", - "timezone": "タイムゾーン", - "anniversary_birth": "誕生日", - "anniversary_death": "命日", - "anniversary_wedding": "結婚記念日", - "anniversary_other": "その他", - "personal_expertise": "専門分野", - "personal_hobby": "趣味", - "personal_interest": "興味", - "personal_other": "その他", - "gender": "性別", - "gender_masculine": "男性", - "gender_feminine": "女性", - "gender_other": "その他", - "gender_none": "該当なし", - "gender_unknown": "不明", - "calendar": "カレンダー", - "calendar_uri": "カレンダーURL", - "scheduling_uri": "スケジュールURL", - "freebusy_uri": "空き状況URL", - "cert_issuer": "発行者", - "cert_expires": "有効期限", - "cert_expired": "期限切れ", - "cert_fingerprint": "フィンガープリント", - "cert_algorithm": "アルゴリズム", - "import_to_smime": "S/MIME にインポート", - "cert_already_imported": "証明書はすでにインポートされています", - "cert_imported": "証明書をインポートしました", - "cert_import_failed": "証明書のインポートに失敗しました", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "その他の操作", - "age_years": "{count}歳", - "years_since": "{count}年前" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "新しい連絡先", - "edit_title": "連絡先を編集", - "section_address_book": "ディレクトリ", - "select_address_book": "ディレクトリを選択...", - "section_identity": "名前と識別情報", - "contact_type": "連絡先の種類", - "type_person": "個人", - "type_organization": "組織", - "section_work": "職業と組織", - "prefix": "敬称", - "prefix_placeholder": "博士、氏", - "given_name": "名", - "middle_name": "ミドルネーム", - "surname": "姓", - "suffix": "接尾辞", - "suffix_placeholder": "Jr.、Sr.、III", - "nickname": "ニックネーム", - "nickname_placeholder": "ニックネーム", - "email": "メール", - "email_placeholder": "email@example.com", - "phone": "電話", - "phone_placeholder": "+81 3 1234 5678", - "phone_type": "種類", - "phone_voice": "音声", - "phone_cell": "携帯", - "phone_fax": "FAX", - "phone_pager": "ポケベル", - "phone_video": "ビデオ", - "phone_text": "テキスト", - "organization": "組織", - "organization_placeholder": "会社名", - "department": "部署", - "department_placeholder": "部署", - "job_title": "役職", - "job_title_placeholder": "例:ソフトウェアエンジニア", - "role": "役割", - "role_placeholder": "例:チームリーダー", - "addresses": "住所", - "add_address": "住所を追加", - "street": "番地", - "city": "市区町村", - "region": "都道府県", - "postcode": "郵便番号", - "country": "国", - "online_services": "オンラインサービス", - "add_online_service": "オンラインサービスを追加", - "url_placeholder": "https://...", - "service_placeholder": "サービス", - "anniversaries": "記念日", - "add_anniversary": "日付を追加", - "anniversary_birth": "誕生日", - "anniversary_wedding": "結婚記念日", - "anniversary_death": "追悼日", - "anniversary_other": "その他", - "personal_info": "個人情報", - "add_personal_info": "項目を追加", - "personal_info_placeholder": "例:写真撮影", - "personal_expertise": "専門分野", - "personal_hobby": "趣味", - "personal_interest": "興味", - "personal_other": "その他", - "level": "レベル", - "level_high": "高", - "level_medium": "中", - "level_low": "低", - "categories": "カテゴリー", - "categories_placeholder": "例:家族、友人、同僚", - "categories_hint": "検索または追加するには入力", - "category_add": "追加", - "note": "メモ", - "note_placeholder": "メモを追加...", - "gender": "性別", - "gender_sex": "生物学的性別", - "gender_male": "男性", - "gender_female": "女性", - "gender_other": "その他", - "gender_none": "該当なし", - "gender_unknown": "不明", - "gender_identity": "性自認", - "gender_identity_placeholder": "性自認...", - "calendar": "カレンダー", - "calendar_uri": "カレンダーURL", - "scheduling_uri": "スケジュールURL", - "freebusy_uri": "空き状況URL", - "context_work": "仕事", - "context_private": "プライベート", - "add_email": "メール追加", - "add_phone": "電話追加", - "save": "保存", - "cancel": "キャンセル", - "creating": "作成中...", - "updating": "更新中...", - "name_required": "名、姓、または組織を入力してください", - "email_invalid": "有効なメールアドレスを入力してください", - "email_error_inline": "メールアドレスの形式が正しくありません", - "save_failed": "連絡先の保存に失敗しました", - "delete": "削除", - "upload_photo": "写真をアップロード", - "remove_photo": "写真を削除", - "photo_hint": "JPGまたはPNG、最大10MB。リサイズされます。", - "photo_too_large": "画像が大きすぎます (最大10MB)", - "photo_invalid": "無効な画像ファイル", - "change_photo": "変更" - }, - "groups": { - "create": "新しいグループ", - "edit": "グループを編集", - "empty": "グループがありません", - "delete_confirm_title": "グループを削除", - "delete_confirm": "このグループを削除してもよろしいですか?", - "name_label": "グループ名", - "name_placeholder": "例:チーム、家族", - "name_required": "グループ名は必須です", - "save_failed": "グループの保存に失敗しました", - "members_label": "メンバー", - "search_members": "追加する連絡先を検索...", - "no_members": "このグループにメンバーがいません", - "member_count": "{count, plural, =0 {メンバーなし} other {#人のメンバー}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "連絡先をインポート", - "drop_hint": "vCardファイルを選択してください", - "file_types": ".vcf または .vcard ファイル", - "no_contacts": "ファイルに連絡先が見つかりません", - "parse_error": "vCardファイルの解析に失敗しました", - "found": "{count, plural, other {#件の連絡先が見つかりました}}", - "duplicate": "重複", - "select_all": "すべて選択", - "deselect_all": "すべて解除", - "selected": "{count, plural, other {#件選択中}}", - "import_button": "インポート", - "importing": "インポート中...", - "success": "{count, plural, other {#件の連絡先をインポートしました}}", - "failed": "インポートに失敗しました", - "close": "閉じる", - "file_too_large": "ファイルが大きすぎます(最大5 MB)" - }, - "export": { - "title": "連絡先をエクスポート", - "success": "{count, plural, other {#件の連絡先をエクスポートしました}}" - }, - "bulk": { - "selected": "{count, plural, other {#件選択中}}", - "select_all": "すべて選択", - "delete": "削除", - "delete_confirm_title": "連絡先を削除", - "delete_confirm": "{count, plural, other {#件の連絡先}}を削除しますか?", - "deleted": "{count, plural, other {#件の連絡先を削除しました}}", - "add_to_group": "グループに追加", - "choose_group": "グループを選択", - "adding_contacts": "{count, plural, other {#件の連絡先}}を追加", - "added_to_group": "連絡先をグループに追加しました", - "export": "エクスポート", - "clear": "選択を解除" - }, - "toast": { - "created": "連絡先を作成しました", - "updated": "連絡先を更新しました", - "deleted": "連絡先を削除しました", - "error_create": "連絡先の作成に失敗しました", - "error_update": "連絡先の更新に失敗しました", - "error_delete": "連絡先の削除に失敗しました" - }, - "context_menu": { - "open": "開く", - "edit": "編集", - "send_email": "メール送信", - "add_to_group": "グループに追加", - "export_vcard": "vCardとしてエクスポート", - "delete": "削除", - "call": "電話する", - "duplicate": "複製", - "print": "印刷" - }, - "filters": { - "toggle": "フィルター", - "select": "選択", - "clear": "クリア", - "close": "閉じる", - "title": "詳細フィルター", - "organization": "会社", - "organization_placeholder": "例: Acme", - "job_title": "役職", - "job_title_placeholder": "例: デザイナー", - "location": "場所", - "location_placeholder": "都市または国", - "email_domain": "メールドメイン", - "email_domain_placeholder": "example.com", - "birthday_month": "誕生月", - "any_month": "すべての月", - "has_email": "メールあり", - "has_phone": "電話あり", - "has_photo": "写真あり", - "sort_by": "並べ替え", - "sort_first_name": "名", - "sort_last_name": "姓" - }, - "open_categories": "カテゴリを開く" - }, - "calendar": { - "title": "カレンダー", - "back_to_email": "メールに戻る", - "back_to_month": "月表示に戻る", - "my_calendars": "マイカレンダー", - "birthday_calendar": "誕生日", - "mini_calendar_change": "クリックで月を変更", - "views": { - "month": "月", - "week": "週", - "day": "日", - "agenda": "予定リスト", - "today": "今日", - "month_hint": "月 (m)", - "week_hint": "週 (w)", - "day_hint": "日 (d)", - "agenda_hint": "予定一覧 (a)", - "tasks": "タスク", - "tasks_hint": "タスク (k)" - }, - "events": { - "create": "予定を作成", - "edit": "予定を編集", - "delete": "予定を削除", - "details": "予定の詳細", - "no_events": "予定なし", - "all_day": "終日", - "more": "他{count}件", - "no_title": "(タイトルなし)", - "resize": "イベントのサイズ変更", - "duplicate": "複製", - "today_header": "今日", - "tomorrow_header": "明日", - "agenda_show_earlier": "以前の予定を表示", - "agenda_loading": "読み込み中…", - "agenda_range_start": "{date}以降の予定", - "agenda_range_end": "{date}までの予定", - "export_ics": ".icsとしてエクスポート", - "copy_title": "タイトルをコピー", - "copy_link": "会議リンクをコピー", - "go_to_today": "今日に移動", - "new_all_day_event": "新しい終日イベント", - "new_event": "新しいイベント", - "new_task": "新しいタスク" - }, - "detail": { - "add_note": "メモを追加...", - "save_note": "保存", - "note_saved": "メモを追加しました", - "open_link": "リンクを開く", - "meeting_link": "会議リンク", - "tentative": "仮", - "cancelled": "キャンセル済み", - "delete_confirm": "このイベントを削除しますか?" - }, - "form": { - "title": "タイトル", - "description": "説明", - "location": "場所", - "meeting_link": "会議リンク", - "start_date": "開始日", - "end_date": "終了日", - "start_time": "開始時刻", - "end_time": "終了時刻", - "all_day_event": "終日の予定", - "calendar_select": "カレンダー", - "save": "保存", - "cancel": "キャンセル", - "delete_confirm": "この予定を削除してもよろしいですか?", - "color": "色" - }, - "participants": { - "availability": { - "title": "空き状況", - "free": "空き", - "busy": "予定あり", - "tentative": "仮", - "unknown": "このサーバーのユーザーではありません", - "checking": "確認中…" - }, - "title": "参加者", - "add": "参加者を追加", - "organizer": "主催者", - "attendee": "参加者", - "accepted": "承諾", - "declined": "辞退", - "tentative": "仮承諾", - "needs_action": "未回答", - "remove": "削除", - "edit": "編集", - "email_placeholder": "メールアドレスを追加または連絡先を検索", - "send_invitations": "参加者に招待を送信", - "status_summary": "{accepted}人承諾、{pending}人保留", - "invited_by": "{name}からの招待", - "respond_below": "以下のボタンで返答してください", - "rsvp_label": "あなたの返答", - "cancel_notification": "キャンセルの通知が参加者に送信されます", - "you_organizer": "あなたは主催者です", - "you_attendee": "あなたは参加者です", - "no_participants": "参加者なし", - "count": "{count}人の参加者" - }, - "recurrence": { - "title": "繰り返し", - "none": "繰り返しなし", - "daily": "毎日", - "weekly": "毎週", - "monthly": "毎月", - "yearly": "毎年", - "every_n_days": "{count}日ごと", - "every_n_weeks": "{count}週間ごと", - "every_n_months": "{count}か月ごと", - "until": "終了日", - "occurrences": "{count}回", - "custom": "カスタム…", - "edit_custom": "カスタム繰り返しを編集", - "every_n_years": "{count}年ごと", - "on_days": "{days}", - "on_day_n": "{day}日", - "on_the_nth": "{nth}{day}", - "in_month": "{month}", - "nth_1": "第1", - "nth_2": "第2", - "nth_3": "第3", - "nth_4": "第4", - "nth_last": "最終", - "editor_freq_day": "日", - "editor_freq_week": "週", - "editor_freq_month": "月", - "editor_freq_year": "年", - "editor_repeats_on": "繰り返し", - "editor_every": "間隔:", - "editor_unit_days": "日ごと", - "editor_unit_weeks": "週間ごと", - "editor_unit_months": "か月ごと", - "editor_unit_years": "年ごと", - "editor_on_day": "日付:", - "editor_on_the": "曜日:", - "editor_in": "月:", - "editor_ends": "終了", - "editor_never": "なし", - "editor_ends_on": "終了日", - "editor_ends_after": "回数", - "editor_occurrences": "回" - }, - "recurrence_scope": { - "edit_title": "繰り返しイベントを編集", - "delete_title": "繰り返しイベントを削除", - "description": "これは繰り返しイベントです。どのイベントを変更しますか?", - "this_event": "このイベントのみ", - "this_and_future": "これ以降のすべてのイベント", - "all_events": "すべてのイベント", - "cancel": "キャンセル", - "save": "保存", - "delete": "削除", - "rsvp_title": "繰り返しイベントに返信", - "rsvp_description": "これは繰り返しイベントです。どのイベントに返信しますか?", - "respond": "返信" - }, - "alerts": { - "title": "リマインダー", - "none": "リマインダーなし", - "at_time": "予定の時刻", - "minutes_before": "{count}分前", - "hours_before": "{count}時間前", - "days_before": "{count}日前", - "weeks_before": "{count}週間前", - "unit_minutes_before": "分前", - "unit_hours_before": "時間前", - "unit_days_before": "日前", - "unit_weeks_before": "週間前", - "add": "リマインダーを追加", - "remove": "リマインダーを削除", - "amount": "リマインダー値", - "unit": "リマインダー単位" - }, - "settings": { - "title": "カレンダー設定", - "default_view": "デフォルトの表示", - "week_starts_on": "週の開始曜日", - "time_format": "時刻の形式", - "default_calendar": "デフォルトのカレンダー", - "default_reminder": "デフォルトのリマインダー", - "time_format_12h": "12時間制", - "time_format_24h": "24時間制", - "notifications_enabled": "イベント通知", - "notifications_enabled_desc": "予定のイベントのアラートを表示する", - "notification_sound": "通知音", - "notification_sound_desc": "カレンダーアラートの音を鳴らす", - "invitation_parsing": "メール招待を解析する", - "invitation_parsing_desc": "メール添付のカレンダー招待を検出してカレンダー操作を表示する", - "show_time_in_month_view": "月表示で時刻を表示", - "show_time_in_month_view_desc": "月カレンダー表示でイベントの時刻を表示する", - "show_week_numbers": "週番号を表示", - "show_week_numbers_desc": "ミニカレンダーに週番号を表示する", - "calendar_free_scroll": "自由スクロール", - "calendar_free_scroll_desc": "1つの期間ずつではなく、月・週・日を連続してスクロールします", - "enable_tasks": "タスクを有効化", - "enable_tasks_desc": "カレンダーにタスク表示を追加して、ToDoを管理します", - "show_tasks_on_calendar": "カレンダーにタスクを表示", - "show_tasks_on_calendar_desc": "日表示と週表示にタスクチップを表示します", - "hover_preview": "イベントホバープレビュー", - "hover_preview_desc": "カレンダーイベントにマウスを合わせたときに詳細ポップオーバーを表示", - "hover_preview_instant": "即時", - "hover_preview_delay_500ms": "0.5秒遅延", - "hover_preview_delay_1s": "1秒遅延", - "hover_preview_delay_2s": "2秒遅延", - "hover_preview_off": "無効", - "organizer_identity": "招待の主催者アドレス", - "organizer_identity_desc": "イベントに人を招待する際に主催者として使用するカレンダーアドレスです。", - "organizer_identity_failed": "主催者アドレスを変更できませんでした", - "show_birthday_calendar": "誕生日カレンダー", - "show_birthday_calendar_desc": "連絡先の誕生日を表示する仮想カレンダーを表示します" - }, - "days": { - "monday": "月曜日", - "tuesday": "火曜日", - "wednesday": "水曜日", - "thursday": "木曜日", - "friday": "金曜日", - "saturday": "土曜日", - "sunday": "日曜日", - "mon": "月", - "tue": "火", - "wed": "水", - "thu": "木", - "fri": "金", - "sat": "土", - "sun": "日" - }, - "notifications": { - "event_created": "予定を作成しました", - "event_updated": "予定を更新しました", - "event_deleted": "予定を削除しました", - "calendar_created": "カレンダーを作成しました", - "calendar_deleted": "カレンダーを削除しました", - "event_move_error": "イベントの移動に失敗しました", - "event_resize_error": "イベントのサイズ変更に失敗しました", - "alert_title": "予定のイベント", - "alert_now": "まもなく開始", - "alert_in_minutes": "{count}分後", - "invitation_sent": "招待を送信しました", - "invitations_denied_title": "招待状を送信できません", - "invitations_denied": "サーバーが招待状の送信を拒否しました: {reason}。招待状を送らずに予定を保存しますか?", - "save_without_invitations": "招待状なしで保存", - "rsvp_updated": "回答を更新しました", - "rsvp_error": "回答の更新に失敗しました", - "event_duplicated": "予定を複製しました", - "event_error": "予定の保存に失敗しました", - "task_due": "タスクの期限です", - "event_exported": "イベントをエクスポートしました", - "title_copied": "タイトルをコピーしました", - "link_copied": "リンクをコピーしました" - }, - "status": { - "loading_calendars": "カレンダーを読み込み中...", - "loading_events": "予定を読み込み中..." - }, - "quick_create": { - "placeholder": "新しい予定のタイトル", - "aria_label": "予定をすばやく作成" - }, - "nav_prev": "前へ", - "nav_next": "次へ", - "import": { - "title": "カレンダーをインポート", - "tab_file": "ファイル", - "tab_url": "URL", - "select_file": ".icsファイルを選択", - "drop_file": "またはファイルをここにドロップ", - "supported_formats": "iCalendar (.ics) ファイルに対応", - "url_description": "外部のiCalendar (.ics) フィードのURLを入力してイベントをインポートします。", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "iCalendar (.ics) URLに対応", - "fetch": "取得", - "invalid_url": "有効なURLを入力してください", - "url_fetch_failed": "URLからカレンダーを取得できませんでした", - "parsing": "カレンダーファイルを解析中...", - "parsed_events": "{count}件のイベントが見つかりました", - "no_events": "ファイルにイベントが見つかりません", - "select_all": "すべて選択", - "deselect_all": "すべて解除", - "target_calendar": "インポート先のカレンダー", - "import_button": "選択をインポート", - "importing": "イベントをインポート中...", - "success": "{count}件のイベントをインポートしました", - "error": "カレンダーのインポートに失敗しました", - "file_too_large": "ファイルサイズが10MBを超えています", - "invalid_format": "無効なカレンダーファイル形式" - }, - "webcal_action": { - "title": "カレンダーリンクを開く", - "description": "\"{name}\"をどのように使用しますか?", - "import_title": "一度だけインポート", - "import_description": "今すぐ予定を取得し、いずれかのカレンダーにコピーします。", - "subscribe_title": "購読", - "subscribe_description": "このカレンダーを別のカレンダーとして自動的に同期します。", - "cancel": "キャンセル" - }, - "management": { - "title": "カレンダー管理", - "description": "カレンダーの作成、名前変更、カスタマイズができます。サイドバーのカレンダーを右クリックして色を素早く変更できます。", - "name": "名前", - "name_placeholder": "カレンダー名", - "color": "色", - "change_color": "色を変更", - "random_color": "新しいランダムな色", - "add_calendar": "カレンダーを追加", - "edit": "編集", - "delete": "削除", - "save": "保存", - "create": "作成", - "cancel": "キャンセル", - "default": "デフォルト", - "confirm_delete": "\"{name}\"を削除しますか?このカレンダーのすべてのイベントが削除されます。", - "calendar_created": "カレンダーを作成しました", - "calendar_updated": "カレンダーを更新しました", - "calendar_deleted": "カレンダーを削除しました", - "color_updated": "カレンダーの色を更新しました", - "error_create": "カレンダーの作成に失敗しました", - "error_update": "カレンダーの更新に失敗しました", - "error_delete": "カレンダーの削除に失敗しました", - "caldav_url": "CalDAV URL", - "copy_url": "CalDAV URLをコピー", - "url_copied": "CalDAV URLをクリップボードにコピーしました", - "confirm_clear": "\"{name}\"のすべてのイベントを削除しますか?この操作は元に戻せません。", - "set_default": "デフォルトに設定", - "default_updated": "デフォルトのカレンダーを更新しました", - "error_default": "デフォルトのカレンダーを設定できませんでした", - "clear_events": "イベントを削除", - "events_cleared": "{count}件のイベントを削除しました", - "error_clear": "カレンダーイベントの削除に失敗しました", - "share": "カレンダーを共有", - "new_event_in_calendar": "このカレンダーに新規イベント", - "kind": "内容", - "kind_events": "イベント", - "kind_tasks": "タスク", - "kind_both": "イベントとタスク", - "kind_hint": "CalDAV で同期したときにこのカレンダーを表示できるアプリを決定します。後から変更することはできません。" - }, - "subscription": { - "title": "iCal購読", - "section_title": "iCal購読", - "description": "外部のiCalendarフィードを購読します。イベントは自動的に専用のカレンダーに同期されます。https://およびwebcal://のURLに対応しています。", - "url_label": "カレンダーURL", - "url_placeholder": "https://example.com/calendar.ics または webcal://...", - "name_label": "カレンダー名", - "name_placeholder": "例:祝日", - "color_label": "色", - "refresh_interval": "更新間隔", - "interval_15": "15分ごと", - "interval_30": "30分ごと", - "interval_60": "1時間ごと", - "interval_360": "6時間ごと", - "interval_1440": "毎日", - "subscribe": "購読する", - "subscribing": "購読中...", - "save": "変更を保存", - "saving": "保存中...", - "edit": "編集", - "edit_title": "購読を編集", - "updated": "\"{name}\" を更新しました", - "update_error": "購読を更新できませんでした", - "invalid_url": "有効なURLを入力してください", - "success": "\"{name}\"を購読しました", - "error": "購読の追加に失敗しました", - "refresh": "今すぐ更新", - "refresh_success": "購読を更新しました", - "refresh_error": "購読の更新に失敗しました", - "unsubscribe": "購読解除", - "confirm_delete": "\"{name}\"の購読を解除しますか?カレンダーとすべてのイベントが削除されます。", - "deleted": "購読を解除しました", - "delete_error": "購読の解除に失敗しました", - "last_refreshed": "最終更新: {time}" - }, - "tasks": { - "label": "タスク", - "no_tasks": "タスクなし", - "no_title": "(タイトルなし)", - "mark_complete": "完了にする", - "mark_incomplete": "未完了にする", - "filter_all": "すべて", - "filter_pending": "未完了", - "filter_completed": "完了", - "filter_overdue": "期限切れ", - "show_completed": "完了済みを表示", - "create": "新しいタスク", - "edit": "タスクを編集", - "title_placeholder": "タスク名", - "description_placeholder": "説明を追加...", - "due_date": "期限日", - "include_time": "時刻を含める", - "priority": "優先度", - "priority_none": "なし", - "priority_high": "高", - "priority_medium": "中", - "priority_low": "低", - "progress": "ステータス", - "progress_needs_action": "要対応", - "progress_in_process": "進行中", - "progress_completed": "完了", - "progress_cancelled": "キャンセル", - "calendar": "カレンダー", - "alert": "リマインダー", - "alert_none": "なし", - "alert_at_time": "期限日時に通知", - "alert_5min": "5分前", - "alert_15min": "15分前", - "alert_30min": "30分前", - "alert_1hr": "1時間前", - "alert_1day": "1日前", - "delete": "削除", - "cancel": "キャンセル", - "save": "保存", - "quick_add_placeholder": "タスクを追加...", - "due_today": "今日が期限", - "due_tomorrow": "明日が期限", - "overdue": "期限切れ" - }, - "months": { - "jan": "1月", - "feb": "2月", - "mar": "3月", - "apr": "4月", - "may": "5月", - "jun": "6月", - "jul": "7月", - "aug": "8月", - "sep": "9月", - "oct": "10月", - "nov": "11月", - "dec": "12月", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "メニューを開く" - }, - "advanced_search": { - "title": "詳細検索", - "from": "差出人", - "from_placeholder": "差出人のメールまたは名前", - "to": "宛先", - "to_placeholder": "宛先のメールまたは名前", - "subject": "件名", - "subject_placeholder": "件名に含まれる...", - "body": "本文", - "body_placeholder": "本文に含む...", - "folder": "フォルダー", - "all_folders": "すべてのフォルダー", - "has_attachment": "添付ファイル", - "date_after": "以降", - "date_before": "以前", - "size_min": "次のサイズ以上 (KB)", - "size_max": "次のサイズ未満 (KB)", - "starred": "スター付き", - "unread": "未読", - "read": "既読", - "yes": "はい", - "no": "いいえ", - "clear": "クリア", - "clear_all": "すべてクリア", - "filters_active": "{count}件のフィルター", - "filters_active_plural": "{count}件のフィルター", - "toggle_filters": "フィルター", - "search_hint": "詳細フィルターで正確に検索", - "advanced_filters_tooltip": "詳細検索フィルター", - "results_found": "{count, plural, =0 {結果が見つかりません} other {#件の結果}}", - "results_found_more": "{count}+件の結果", - "suggestions_recent": "最近の検索", - "suggestions_people": "連絡先", - "suggestions_from": "{address} からのメールを検索", - "suggestions_to": "{address} 宛のメールを検索", - "suggestions_remove_recent": "最近の検索から削除" - }, - "welcome": { - "title": "メールボックスへようこそ", - "tip_compose": "cキーで新しいメールを作成", - "tip_shortcuts": "?キーですべてのショートカットを表示", - "tip_sidebar": "サイドバーからカレンダー、連絡先、設定にアクセス", - "tip_settings": "設定でカスタマイズできます", - "got_it": "了解", - "settings": "設定", - "dismiss": "閉じる", - "start_tour": "ツアーを開始" - }, - "demo_welcome": { - "title": "Bulwark Mail へようこそ", - "description": "フル機能のウェブメールクライアントをブラウザで体験できます。すべてのデータはお使いのデバイスに保存されるので、自由にお試しください。", - "feature_email": "メールの読み書き", - "feature_organize": "タグ・スター・フォルダ", - "feature_shortcuts": "キーボードショートカット", - "feature_privacy": "100% プライベートなデモ", - "hint": "左のメールをクリックして始めるか、ツアーを開始してください。" - }, - "files": { - "office_edit": "ドキュメントを編集", - "office_loading": "ドキュメントエディターを開いています…", - "office_error": "ドキュメントエディターを開けませんでした。", - "office_close": "エディターを閉じる", - "open_in_new_tab": "新しいタブで開く", - "title": "ファイル", - "search_placeholder": "ファイルを検索...", - "empty_state_title": "ファイルがありません", - "empty_state_description": "ファイルをアップロードするかフォルダーを作成して始めましょう", - "upload": "アップロード", - "upload_files": "ファイルをアップロード", - "new_folder": "新しいフォルダー", - "new_folder_name": "フォルダー名", - "rename": "名前を変更", - "rename_title": "名前を変更", - "new_name": "新しい名前", - "name_forbidden_chars": "サーバーでは名前に次の文字を使用できません: {chars}", - "name_reserved": "この名前はサーバーで予約されています。", - "delete": "削除", - "delete_confirm_title": "リソースを削除", - "delete_confirm_message": "\"{name}\"を削除してもよろしいですか?この操作は元に戻せません。", - "download": "ダウンロード", - "name": "名前", - "size": "サイズ", - "modified": "更新日時", - "type": "種類", - "folder": "フォルダー", - "file": "ファイル", - "parent_directory": "親ディレクトリ", - "breadcrumb_root": "ホーム", - "drop_files_here": "ここにファイルまたはフォルダをドロップしてアップロード", - "uploading": "アップロード中...", - "upload_success": "{count, plural, other {#件のファイルをアップロードしました}}", - "upload_error": "ファイルのアップロードに失敗しました", - "create_folder_success": "フォルダーを作成しました", - "create_folder_error": "フォルダーの作成に失敗しました", - "delete_success": "正常に削除しました", - "delete_error": "削除に失敗しました", - "rename_success": "正常に名前を変更しました", - "rename_error": "名前の変更に失敗しました", - "download_error": "ダウンロードに失敗しました", - "not_available": "ファイルストレージはこのサーバーで利用できません", - "cancel": "キャンセル", - "create": "作成", - "save": "保存", - "no_results": "検索に一致するファイルがありません", - "batch_delete_confirm_message": "{count, plural, other {#件のアイテム}}を削除してもよろしいですか?この操作は元に戻せません。", - "batch_delete_success": "{count, plural, other {#件のアイテムを削除しました}}", - "grid_view": "グリッド表示", - "list_view": "リスト表示", - "details": "詳細", - "path": "パス", - "preview": "プレビュー", - "preview_error": "プレビューの読み込みに失敗しました", - "cut": "切り取り", - "copy": "コピー", - "paste": "貼り付け", - "move_success": "{count, plural, other {#件のアイテムを移動しました}}", - "move_error": "移動に失敗しました", - "paste_success": "正常に貼り付けました", - "paste_error": "貼り付けに失敗しました", - "new_text_file": "新規テキストファイル", - "file_name": "ファイル名", - "retry": "再試行", - "refresh": "更新", - "toggle_favorite": "お気に入り切替", - "duplicate": "複製", - "duplicate_success": "正常に複製しました", - "duplicate_error": "複製に失敗しました", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "ファイルを作成しました", - "create_file_error": "ファイルの作成に失敗しました", - "favorites": "お気に入り", - "recent": "最近のファイル", - "properties": "プロパティ", - "open_folder": "フォルダを開く", - "upload_folder": "フォルダをアップロード", - "file_too_large": "\"{name}\" がファイルサイズの上限を超えています({max})", - "undo": "元に戻す", - "undo_success": "操作を元に戻しました", - "undo_error": "元に戻すのに失敗しました", - "toolbar": "ファイル操作", - "file_list": "ファイルとフォルダ", - "context_menu": "操作", - "settings_title": "ファイル設定", - "settings_display": "表示", - "settings_default_view": "デフォルトビュー", - "settings_default_view_desc": "グリッドまたはリストレイアウトを選択します", - "settings_default_sort": "デフォルトの並べ替え", - "settings_default_sort_desc": "ファイルのデフォルトの並べ替えを選択します", - "settings_sort_direction": "並べ替え方向", - "settings_sort_direction_desc": "昇順または降順を選択します", - "settings_ascending": "昇順", - "settings_descending": "降順", - "settings_icons": "アイコン", - "settings_show_icons": "ファイルアイコンを表示", - "settings_show_icons_desc": "ファイルとフォルダーの横にアイコンを表示します", - "settings_colored_icons": "カラーアイコン", - "settings_colored_icons_desc": "モノクロの代わりにカラフルなアイコンを使用します", - "settings_show_thumbnails": "サムネイルを表示", - "settings_show_thumbnails_desc": "画像ファイルのアイコンの代わりにプレビューを表示します", - "settings_behavior": "動作", - "settings_show_hidden": "隠しファイルを表示", - "settings_show_hidden_desc": "ドットで始まるファイルとフォルダーを表示します", - "settings_folder_layout": "フォルダナビゲーション", - "settings_folder_layout_desc": "フォルダの表示方法を選択:ファイルとインラインで表示するか、サイドバーにツリー表示するか", - "settings_folder_layout_inline": "インライン", - "settings_folder_layout_sidebar": "サイドバー", - "disabled_title": "ファイル機能は管理者によって無効にされています", - "disabled_description": "WebDAV経由の大容量ファイルアップロードは、メモリ不足クラッシュや回復不能なディスク使用量など、Stalwart/RocksDBの不安定性を引き起こす可能性があります。削除されたファイルはストレージからすぐに削除されない場合があります。この機能は本番環境では推奨されません。", - "stability_warning": "大容量ファイルのアップロードはサーバーの不安定性を引き起こす可能性があります。削除されたファイルはストレージからすぐに削除されない場合があります。注意して使用してください。", - "no_accounts": "接続されているアカウントはありません。", - "open_folder_tree": "フォルダーツリーを開く", - "other_accounts": "その他のアカウント", - "migration_title": "ファイルを更新しています…", - "migration_description": "フォルダーとファイルを適切な構造に整理しています。これは一度だけ行われます。" - }, - "smime": { - "your_certificates": "あなたの証明書", - "your_certificates_desc": "メールの署名と暗号化のために S/MIME 証明書をインポートして管理します", - "recipient_certificates": "受信者証明書", - "recipient_certificates_desc": "受信者へのメールを暗号化するための公開証明書", - "identity_bindings": "ID と鍵の関連付け", - "identity_bindings_desc": "S/MIME 証明書をメール ID に関連付けます", - "defaults_title": "既定値", - "defaults_desc": "署名と暗号化の既定動作を設定します", - "import_pkcs12": "PKCS#12 をインポート (.p12/.pfx)", - "import_public_cert": "証明書をインポート", - "no_certificates": "まだ証明書はインポートされていません", - "no_recipient_certs": "受信者証明書がありません", - "expires": "有効期限", - "expired": "期限切れ", - "bound_to": "関連付け先", - "no_key_bound": "なし", - "lock": "鍵をロック", - "unlock": "鍵をロック解除", - "details": "詳細を表示", - "delete": "削除", - "encrypt_by_default": "既定で暗号化する", - "encrypt_by_default_desc": "すべての受信者が証明書を持っている場合に自動でメールを暗号化します", - "remember_unlocked": "ロック解除した鍵を記憶する", - "remember_unlocked_desc": "このブラウザーセッションの間は鍵をロック解除したままにします", - "sign_default_for": "既定で署名する対象", - "enter_p12_passphrase": "PKCS#12 パスフレーズを入力", - "p12_passphrase_desc": "この証明書ファイルを保護しているパスフレーズを入力してください", - "enter_storage_passphrase": "保存用パスフレーズを設定", - "storage_passphrase_desc": "ブラウザー内でこの鍵を保護するためのパスフレーズを選択してください", - "next": "次へ", - "import": "インポート", - "unlock_key": "鍵をロック解除", - "unlock_key_desc": "署名または復号に使うため、この鍵の保存用パスフレーズを入力してください", - "passphrase_placeholder": "パスフレーズを入力", - "confirm_passphrase_placeholder": "パスフレーズを確認", - "passphrase_mismatch": "パスフレーズが一致しません", - "cancel": "キャンセル", - "processing": "処理中…", - "close": "閉じる", - "certificate_details": "証明書の詳細", - "cert_subject": "件名", - "cert_issuer": "発行者", - "cert_email": "メールアドレス", - "cert_serial": "シリアル番号", - "cert_validity": "有効期間", - "cert_fingerprint": "フィンガープリント (SHA-256)", - "cert_algorithm": "アルゴリズム", - "cert_capabilities": "機能", - "cert_source": "ソース", - "cert_expired": "この証明書は期限切れです", - "cert_not_yet_valid": "この証明書はまだ有効ではありません", - "cap_sign": "署名", - "cap_encrypt": "暗号化", - "cap_none": "なし", - "show_passphrase": "パスフレーズを表示", - "hide_passphrase": "パスフレーズを隠す", - "sign_toggle": "署名", - "encrypt_toggle": "暗号化", - "missing_recipient_certs": "次の宛先の証明書がありません: {emails}", - "missing_sender_cert": "この ID に関連付けられた証明書がありません", - "status_encrypted_ok": "このメッセージは暗号化されています", - "status_encrypted_no_key": "このメッセージは暗号化されていますが、一致する鍵が見つかりませんでした", - "status_encrypted_failed": "このメッセージを復号できませんでした", - "status_signed_valid": "署名を確認しました", - "status_signed_invalid": "署名の検証に失敗しました", - "status_signed_expired_cert": "期限切れの証明書で署名されています", - "status_signed_self_signed": "署名は有効ですが、証明書は自己署名です(信頼されていません)", - "status_signed_mismatch": "署名は有効ですが、署名者が送信者と一致しません", - "status_unsupported": "未対応の S/MIME 形式です", - "auto_import_signer_certs": "署名者証明書を自動インポート", - "auto_import_signer_certs_desc": "検証済み署名メールの証明書を今後の暗号化用に自動保存します", - "export": "エクスポート", - "enter_export_passphrase": "エクスポート用パスフレーズを設定", - "export_passphrase_desc": "エクスポートする PKCS#12 ファイルを保護するパスフレーズを選択してください", - "export_storage_desc": "エクスポートのために鍵を復号する保存用パスフレーズを入力してください", - "incorrect_passphrase": "パスフレーズが正しくありません" - }, - "tour": { - "step_counter": "ステップ {current} / {total}", - "skip": "ツアーをスキップ", - "back": "戻る", - "next": "次へ", - "finish": "完了", - "take_a_tour": "インターフェースのツアーを見る", - "restart_title": "紹介ツアー", - "restart_desc": "インターフェースのガイドツアーを再生する", - "restart_button": "ツアーを再開", - "show_on_new_devices_title": "新しいデバイスで表示", - "show_on_new_devices_desc": "他のデバイスで完了済みでも、新しいデバイスで初めてサインインしたときにウェルカムバナーとツアーを再表示します", - "sidebar_title": "メールボックス", - "sidebar_desc": "フォルダーサイドバーです。メールボックスをクリックしてメールを表示できます。フォルダーの作成、メールのドラッグ移動、未読数の確認ができます。", - "compose_title": "メールを作成", - "compose_desc": "ここをクリックして新しいメールを作成します。宛先、添付ファイルの追加やリッチテキスト書式が使えます。", - "search_title": "メールを検索", - "search_desc": "送信者、件名、内容で検索できます。フィルターアイコンをクリックすると、日付範囲、添付ファイル、スター付きメッセージなどの詳細オプションが使えます。", - "email_list_title": "メール一覧", - "email_list_desc": "メールがここに表示されます。クリックすると右側で読めます。チェックボックスで複数選択し、一括で移動、削除、タグ付けができます。", - "email_viewer_title": "閲覧パネル", - "email_viewer_desc": "選択したメールがここに開きます。ツールバーのボタンで返信、転送、アーカイブ、削除ができます。メールにスターやカラータグも付けられます。", - "keywords_title": "カラータグ", - "keywords_desc": "色分けされたタグでメールを整理できます。メールをタグにドラッグしてラベル付けするか、右クリックでタグを割り当てられます。", - "calendar_title": "カレンダー", - "calendar_desc": "カレンダーに切り替えてイベントを管理できます。イベントの作成、リマインダーの設定、日・週・月表示の切り替えができます。", - "contacts_title": "連絡先", - "contacts_desc": "アドレス帳がここにあります。連絡先のインポート、グループの作成、連絡先をクリックして詳細を確認できます。", - "settings_title": "設定", - "settings_desc": "すべてをカスタマイズできます:テーマ、表示密度、署名、フィルター、キーボードショートカット、カレンダー設定など。", - "shortcuts_title": "キーボードショートカット", - "shortcuts_desc": "パワーユーザー向けの機能です。いつでも ? を押すと利用可能なすべてのショートカットが表示されます。マウスを使わずにナビゲーション、作成、メール管理ができます。", - "compose_open_title": "メール作成画面", - "compose_open_desc": "これはメール作成画面です。宛先を追加し、メッセージを書き、ファイルを添付し、リッチテキスト書式を使用できます。下書きの保存やテンプレートの使用も可能です。", - "calendar_view_title": "カレンダー表示", - "calendar_view_desc": "サンプルイベント付きのカレンダーです。ツールバーで日、週、月、アジェンダビューを切り替えられます。", - "create_event_title": "イベントを作成", - "create_event_desc": "このボタンをクリックして新しいカレンダーイベントを作成します。タイトル、日付、時間を設定し、参加者を追加できます。", - "event_modal_title": "イベント詳細", - "event_modal_desc": "イベントフォームです。タイトルを入力し、日時を選択し、場所や参加者を追加してください。完了したら保存をクリック - または閉じて次に進みましょう。", - "contacts_list_title": "連絡先一覧", - "contacts_list_desc": "連絡先の一覧です。連絡先をクリックすると右側に詳細が表示されます。新しい連絡先の作成、vCardのインポート、グループへの整理もできます。", - "files_title": "ファイルストレージ", - "settings_tabs_title": "設定メニュー", - "settings_tabs_desc": "すべての設定カテゴリがここにあります。外観のカスタマイズ、IDの管理、メールフィルターの設定、カレンダーの構成など、多数の項目を調整できます。", - "files_desc": "ファイルブラウザでファイルのアップロード、整理、共有ができます。メールに統合されたパーソナルクラウドのようなものです。", - "demo_banner_title": "デモコントロール", - "demo_banner_desc": "デモモードです。すべてブラウザ内に保存されます。「デモをリセット」をクリックすると、いつでもクリーンなサンプルデータで再開できます。", - "quota_title": "ストレージ使用量", - "quota_desc": "メールボックスのサイズをここで確認できます。使用量が増えるとサークルが満たされます。" - }, - "unified_mailbox": { - "search_unavailable": "統合ビューでは検索を利用できません" - }, - "calendar_event_notifications": { - "someone": "誰か", - "invited": "{name} さんが「{title}」に招待しました", - "updated": "{name} さんが「{title}」を更新しました", - "cancelled": "{name} さんが「{title}」をキャンセルしました", - "untitled": "無題の予定" - }, - "share_notifications": { - "someone": "誰か", - "shared": "{name} さんが{kind}「{object}」を共有しました", - "changed": "{name} さんが{kind}「{object}」へのアクセス権を変更しました", - "revoked": "{name} さんが{kind}「{object}」へのアクセス権を削除しました", - "object": { - "folder": "フォルダー", - "calendar": "カレンダー", - "address_book": "アドレス帳", - "files": "ファイルフォルダー" - } - }, - "sharing": { - "title": "「{name}」を共有", - "description": "このサーバー上の他のユーザーまたはグループにアクセス権を付与します。変更はすぐに反映されます。", - "no_shares": "まだ誰にも共有されていません。", - "add_person": "ユーザーまたはグループを追加", - "search_placeholder": "名前またはメールで検索…", - "loading_principals": "ユーザーを読み込み中…", - "no_principals": "他のユーザーまたはグループは見つかりません。", - "no_match": "一致する項目がありません。", - "remove": "アクセス権を削除", - "group": "グループ", - "share_added": "アクセス権を付与しました", - "share_updated": "アクセス権を更新しました", - "share_removed": "アクセス権を削除しました", - "share_failed": "共有の更新に失敗しました", - "preset": { - "freeBusy": "空き時間情報のみ", - "read": "読み取り専用", - "readWrite": "読み取り・書き込み", - "manager": "管理者", - "custom": "カスタム" - } - }, - "quote_header": { - "reply_line": "{date}に{from}が書きました:", - "forwarded_separator": "---------- 転送メッセージ ----------", - "from_label": "差出人", - "date_label": "日付", - "subject_label": "件名" - }, - "pwa_install": { - "title": "{appName} をインストール", - "description": "素早いアクセスとオフライン対応のため、アプリをインストールしましょう。", - "not_now": "後で", - "install": "インストール", - "dont_remind": "今後表示しない", - "dismiss_aria": "インストールプロンプトを閉じる" - }, - "deep_link": { - "message_not_found": "このメッセージは利用できなくなりました。", - "thread_not_found": "この会話は利用できなくなりました。", - "folder_not_found": "このフォルダーは利用できなくなりました。", - "event_not_found": "この予定は利用できなくなりました。", - "file_not_found": "このファイルは利用できなくなりました。", - "account_unavailable": "このリンクを開くには、そのアカウントにサインインしてください。", - "copy_message": "メッセージのリンクをコピー", - "copy_conversation": "会話のリンクをコピー", - "copy_event": "予定のリンクをコピー", - "copied": "リンクをコピーしました", - "copy_failed": "リンクをコピーできませんでした" - }, - "plugins": { - "oauth_callback": { - "completing": "認証を完了しています…", - "completing_message": "接続が確立されるまでお待ちください。", - "received_title": "認証を受信しました", - "received_message": "接続を開始したタブに戻ることができます。", - "failed_title": "認証に失敗しました", - "denied": "認証が拒否されました。", - "failed_detail": "認証に失敗しました: {error}", - "missing_params": "認証コードまたはステートがありません。", - "delivery_failed": "認証結果をアプリのタブに渡せませんでした。このウィンドウを閉じて、もう一度お試しください。" - } - }, - "global_search": { - "title": "すべてを検索", - "placeholder": "メール、連絡先、カレンダー、ファイルを検索...", - "scope_label": "検索対象", - "account_label": "アカウント", - "scope_all": "すべて", - "scope_mail": "メール", - "scope_contacts": "連絡先", - "scope_calendar": "カレンダー", - "scope_files": "ファイル", - "all_accounts": "すべてのアカウント", - "show_all": "すべての結果を表示", - "no_results": "「{query}」に一致する結果はありません", - "server_hint": "完全な単語はサーバーを検索します。部分一致は読み込み済みの項目に適用されます", - "searching": "検索中...", - "account_error": "{account} を検索できませんでした", - "recent_searches": "最近の検索", - "recurring": "繰り返し", - "no_subject": "(no subject)", - "empty_hint": "すべてのアカウントのメール、連絡先、予定、ファイルを検索できます。from:、subject:、has:attachment、after:2026-01-01 なども試せます。", - "flat_by_date": "日付順のフラット表示", - "grouped": "グループ表示", - "load_more": "さらに読み込む", - "preview_empty": "プレビューする結果を選択してください", - "open_full": "開く", - "view_label": "表示" - } -} +{ + "meta_description": "JMAPプロトコルを使用するミニマルなウェブメールクライアント", + "login": { + "title": "ウェブメール", + "username_label": "メールアドレス", + "username_placeholder": "user@example.com", + "password_label": "パスワード", + "password_placeholder": "パスワードを入力", + "jmap_endpoint_label": "JMAPサーバー", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "サーバーはこのドメインからのCORSリクエストを許可する必要があります。", + "jmap_server_label": "サーバー", + "jmap_server_auto_picked": "メールドメインからサーバーを選択しました。", + "sign_in": "サインイン", + "signing_in": "サインイン中...", + "loading": "読み込み中...", + "reconnecting": "接続が切れました。再接続を試みています…", + "error": { + "invalid_credentials": "メールアドレスまたはパスワードが無効です", + "invalid_token": "サーバーがこのアクセストークンを受け付けませんでした。トークンが完全であること、期限切れや取り消しになっていないことを確認してください。", + "connection_failed": "サーバーへの接続に失敗しました", + "cors_blocked": "サーバーには到達できますが、クロスオリジンリクエストがブロックされています。JMAPサーバーのCORS設定を確認し、このドメインを許可してください。", + "server_error": "サーバーが一時的に利用できません。後でもう一度お試しください。", + "generic": "エラーが発生しました。もう一度お試しください。", + "totp_required": "二要素認証コードが必要です。以下にコードを入力してください。", + "totp_invalid": "認証コードが無効です。認証アプリを確認してください。", + "oauth_discovery_failed": "SSOは有効ですが、IDプロバイダーに接続できません。OAuth設定を確認してください。", + "token_exchange_failed": "パスワードとコードは受け付けられましたが、メールサーバーがこのアプリのセッション開始を拒否しました。管理者に OAuth クライアントの設定を確認してもらってください。" + }, + "show_password": "パスワードを表示", + "hide_password": "パスワードを隠す", + "totp_toggle": "2FAコードを入力", + "token_toggle": "アクセストークンでログイン", + "password_toggle": "パスワードでログイン", + "token_label": "アクセストークン", + "token_placeholder": "APIトークンを貼り付け", + "token_hint": "メールプロバイダーの設定でAPIトークンを作成し、ここに貼り付けてください。", + "remember_me": "ログイン状態を保持", + "config_error": { + "title": "設定エラー", + "fetch_failed": "アプリケーション設定を読み込めません。後でもう一度お試しください。", + "server_not_configured": "メールサーバーが設定されていません。管理者にお問い合わせください。" + }, + "remove_from_history": "履歴から削除", + "totp_label": "認証コード", + "totp_placeholder": "000000", + "session_expired": "セッションが期限切れになりました。再度サインインしてください。", + "dismiss": "閉じる", + "or": "または", + "sign_in_sso": "SSOでサインイン", + "add_account_title": "アカウントを追加", + "add_account_subtitle": "別のアカウントでサインイン", + "cancel": "キャンセル", + "website": "ウェブサイト", + "imprint": "サイト運営者情報", + "privacy_policy": "プライバシーポリシー", + "try_demo": "デモを試す", + "demo_description": "サンプルデータで探索 - アカウント不要", + "demo_launching": "デモを起動中...", + "demo_login_button": "デモを開始", + "demo_tagline": "フル機能のメールクライアントを体験。アカウント不要。", + "demo_no_signup": "登録不要 - サンプルデータで自由に探索", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "サインイン処理中...", + "oauth_error": { + "title": "認証に失敗しました", + "invalid_state": "セキュリティ検証に失敗しました。再度サインインしてください。", + "missing_params": "認証データが不足しています。再度サインインしてください。", + "token_exchange_failed": "認証を完了できませんでした。再度お試しください。", + "access_denied": "アクセスが拒否されました。管理者にお問い合わせください。", + "back_to_login": "ログインに戻る" + } + }, + "sidebar": { + "close": "閉じる", + "compose": "作成", + "compose_hint": "作成 (c)", + "search_placeholder": "メールを検索...", + "search_placeholder_hint": "メールを検索... (/ を押す)", + "storage": "ストレージ", + "storage_used": "使用中", + "storage_free": "空き", + "storage_total": "合計", + "sign_out": "サインアウト", + "sign_out_of": "{account} からサインアウト", + "sign_out_all": "すべてのアカウントからサインアウト", + "add_account": "アカウントを追加", + "set_as_default": "デフォルトに設定", + "switch_account": "アカウントを切り替え", + "contacts": "連絡先", + "calendar": "カレンダー", + "settings": "設定", + "admin": "管理", + "files": "ファイル", + "loading_mailboxes": "メールボックスを読み込み中...", + "push_connected": "リアルタイム更新が有効", + "push_disconnected": "リアルタイム更新が無効", + "keyboard_shortcuts": "キーボードショートカット", + "theme": { + "light": "ライトモード", + "dark": "ダークモード", + "system": "システムテーマ" + }, + "language": { + "title": "言語" + }, + "mailboxes": { + "inbox": "受信トレイ", + "sent": "送信済み", + "drafts": "下書き", + "trash": "ゴミ箱", + "archive": "アーカイブ", + "starred": "スター付き", + "all_mail": "すべてのメール", + "spam": "迷惑メール", + "important": "重要" + }, + "unified_inbox": "統合受信トレイ", + "unified_sent": "すべての送信済み", + "unified_drafts": "すべての下書き", + "unified_trash": "すべてのゴミ箱", + "unified_archive": "すべてのアーカイブ", + "unified_junk": "すべての迷惑メール", + "all_accounts": "すべてのアカウント", + "all_accounts_loading": "アカウントを読み込み中: {total} 件中 {loaded} 件", + "unified_mailbox": "統合メールボックス", + "expand": "展開", + "collapse": "折りたたむ", + "expand_tooltip": "展開", + "collapse_tooltip": "折りたたむ", + "mobile": { + "search": "検索", + "compose": "作成", + "go_back": "戻る", + "toggle_menu": "メニューを切り替え" + }, + "clear_search": "検索をクリア", + "vacation_active": "不在応答が有効です", + "demo_banner": "デモモード", + "demo_reset": "リセット", + "demo_tour": "ツアー", + "tags": "タグ", + "show_all_tags": "すべて表示({count})", + "show_fewer_tags": "表示を減らす", + "folders": "フォルダ", + "your_folders": "マイフォルダ", + "create_folder": "フォルダを作成", + "show_more": "もっと見る", + "show_fewer": "折りたたむ", + "mail": "メール", + "nav_label": "ナビゲーション", + "add_app": "アプリ", + "shared": "共有", + "shared_account_hidden": "サイドバーから非表示にしました。設定 › レイアウトで再表示できます。", + "notifications_muted": "通知ミュート中", + "scheduled": "予約済み", + "unified_all_unread": "すべての未読", + "unified_all_starred": "すべてのスター付き", + "unified_all_mail": "すべてのメール", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "ラベル", + "create_label": "新しいラベルを作成", + "mv_tasks": "タスク", + "mv_side_panel": "サイドパネル", + "mv_open_full": "全画面で開く", + "mv_previous_day": "前日", + "mv_next_day": "翌日", + "mv_add_another_account": "別のアカウントを追加", + "mv_manage_account": "アカウントを管理", + "mv_storage_used": "{total} のうち {percent}% を使用中" + }, + "protocol_handlers": { + "title": "既定のアプリ", + "description": "メールとカレンダーのリンクを Bulwark で開くかどうかを選択します。技術的には、Bulwark は mailto: と webcal: リンクのプロトコル ハンドラーとして登録されます。", + "unsupported": "このブラウザーまたは接続では、プロトコル ハンドラーの手動登録がサポートされていません。インストール済みの PWA は、ブラウザーまたはシステム設定から使用できる場合があります。", + "mailto_label": "メールリンク", + "mailto_description": "mailto: リンクを、入力済みの作成画面で Bulwark に開きます。", + "protocol_open_mode_label": "プロトコルリンクを開くとき", + "protocol_open_mode_description": "Bulwark が mailto: と webcal: のリンクを新しいタブで開くか、開いているセッションを再利用するかを選択します。アクティブなセッションのオプションでは通知の許可が必要です。ブラウザーがフォーカスをブロックした場合に、代替通知をクリックして Bulwark を前面に表示できます。", + "protocol_open_mode_active_session": "可能な場合はアクティブなセッションで開く", + "protocol_open_mode_new_tab": "常に新しいタブを開く", + "focus_notification_title": "Bulwark を開く", + "focus_notification_body": "リンクは Bulwark で開かれました。クリックするとウィンドウを前面に表示します。", + "webcal_label": "カレンダーリンク", + "webcal_description": "webcal: リンクを、入力済みのカレンダー購読ダイアログで Bulwark に開きます。", + "register_mailto": "メールアプリを登録", + "register_webcal": "カレンダーアプリを登録", + "mailto_registered": "メール ハンドラーの登録を要求しました", + "webcal_registered": "カレンダー ハンドラーの登録を要求しました", + "registration_failed": "プロトコル ハンドラーの登録に失敗しました", + "opening_mailto": "作成画面を開いています...", + "opening_webcal": "カレンダーを開いています...", + "browser_note": "ブラウザーまたはオペレーティング システムから確認を求められる場合があります。また、既定のアプリとして選択する前に Bulwark のインストールが必要な場合があります。", + "select_account_title": "アカウントを選択", + "select_mailto_account": "このメールリンクを開くアカウントを選択してください。", + "select_webcal_account": "このカレンダーリンクを開くアカウントを選択してください。", + "select_account_note": "この選択は、このプロトコル リンクにのみ適用されます。", + "detail_to": "宛先", + "detail_subject": "件名", + "detail_no_subject": "件名なし", + "detail_calendar": "カレンダー", + "detail_source": "ソース", + "active_account": "アクティブ", + "switching_account": "アカウントを切り替えています..." + }, + "sidebar_apps": { + "modal_title": "サイドバーアプリ", + "add_new": "アプリを追加", + "edit_app": "アプリを編集", + "name_label": "名前", + "name_placeholder": "マイアプリ", + "name_required": "名前は必須です", + "url_label": "URL", + "url_required": "URLは必須です", + "url_invalid": "有効なhttpまたはhttps URLを入力してください", + "icon_label": "アイコン", + "icon_required": "アイコンは必須です", + "open_mode_label": "開き方", + "open_new_tab": "新しいタブ", + "open_inline": "埋め込み", + "cancel": "キャンセル", + "add": "追加", + "update": "更新", + "delete": "削除", + "delete_confirm_title": "アプリを削除", + "delete_confirm": "「{name}」を削除してもよろしいですか?", + "no_apps": "アプリが追加されていません", + "no_apps_hint": "カスタムアプリやリンクをサイドバーに追加できます", + "managed_title": "管理者によって提供", + "managed_description": "これらのアプリは全員向けに設定されており、変更できません。", + "managed_badge": "管理対象", + "search_icons": "アイコンを検索...", + "show_popular": "人気", + "show_all": "すべて", + "no_icons_found": "アイコンが見つかりません", + "inline_badge": "埋め込み", + "tab_badge": "タブ", + "show_on_mobile": "モバイルで表示" + }, + "email_list": { + "columns": { + "from": "差出人", + "to": "宛先", + "subject": "件名", + "date": "日付", + "size": "サイズ" + }, + "no_emails": "メッセージが見つかりません", + "no_emails_description": "このメールボックスは空です", + "no_search_results": "結果が見つかりません", + "no_search_results_description": "検索やフィルターを調整してください", + "loading": "メールを読み込み中...", + "unread": "未読", + "to_me": "宛先: 自分", + "to_recipients": "{count}人の宛先", + "and_others": "他{count}人", + "draft": "下書き", + "starred": "スター付き", + "pinned": "ピン留め", + "replied": "返信済み", + "forwarded": "転送済み", + "has_attachment": "添付ファイルあり", + "conversations_range": "{total, number} 件中 {from}–{to, number} 件", + "conversations_count": "{total}件中{count}件の会話", + "conversations_count_plus": "{count}件以上の会話", + "conversations_count_simple": "{count}件の会話", + "no_conversations": "会話がありません", + "loading_more": "さらにメールを読み込み中...", + "no_more_emails": "読み込むメールがありません", + "batch_actions": { + "select": "メールを選択", + "select_all": "すべて選択", + "selected_messages": "{count, plural, one {1} other {#}}通のメールが選択されました", + "mark_read": "既読にする", + "mark_unread": "未読にする", + "delete": "削除", + "delete_confirm_title": "メールを削除", + "delete_confirm_message": "{count, plural, one {1件} other {#件}}のメールを削除しますか?", + "clear_selection": "選択を解除" + }, + "permanent_delete": "完全に削除", + "permanent_delete_confirm_title": "完全に削除", + "permanent_delete_confirm_message": "このメールは完全に削除されます。この操作は取り消せません。", + "permanent_delete_confirm_batch_message": "{count, plural, one {1件} other {#件}}のメールが完全に削除されます。この操作は取り消せません。", + "empty_folder": { + "button": "フォルダを空にする", + "confirm_title": "フォルダを空にする", + "confirm_message": "このフォルダ内のすべてのメールが完全に削除されます。この操作は取り消せません。", + "confirm_message_trash": "このフォルダー内のすべてのメールがゴミ箱に移動されます。", + "confirm_button": "フォルダを空にする", + "junk_hint": "迷惑メールフォルダを空にして、すべてのメールを完全に削除できます。", + "trash_hint": "ゴミ箱を空にして、すべてのメールを完全に削除できます。" + }, + "no_scheduled_emails": "予約送信はありません", + "no_scheduled_emails_description": "後で送信するよう予約したメッセージがここに表示されます。", + "scheduled_count": "{count, plural, one {1 件の予約送信} other {# 件の予約送信}}", + "scheduled_actions_hint": "予約メッセージは予約アクションからキャンセル、再予約、編集できます。", + "cancel_scheduled_send": "送信をキャンセル", + "reschedule_send": "再予約", + "cancel_and_edit": "キャンセルして編集", + "cancel_and_compose_again": "キャンセルして新規作成", + "reschedule_prompt": "新しい日時を入力してください。例: 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "送信者が開封確認を求めています:", + "send": "確認を送信", + "ignore": "無視", + "sent": "開封確認を送信しました。", + "send_failed": "開封確認を送信できませんでした", + "mdn_subject": "開封済み: {subject}", + "mdn_body": "これは、あなたが {recipient} に送信したメッセージの開封確認です。\n\n注意: この確認は、メッセージが受信者のコンピューターに表示されたことを示すだけです。受信者が内容を読んだ、または理解したことを保証するものではありません。" + }, + "no_email_selected": "メールが選択されていません", + "no_email_description": "リストからメールを選択して表示してください", + "no_conversation_selected": "会話が選択されていません", + "no_conversation_description": "リストから会話を選択して読んでください", + "compose": "作成", + "compose_hint": "新しいメッセージを作成", + "no_subject": "(件名なし)", + "no_body_content": "(本文がありません)", + "no_preview_available": "プレビューは利用できません", + "show_quoted_text": "引用テキストを表示", + "hide_quoted_text": "引用テキストを非表示", + "loading_email": "メールを読み込み中...", + "loading": "読み込み中...", + "reply": "返信", + "reply_all": "全員に返信", + "forward": "転送", + "delete": "削除", + "archive": "アーカイブ", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "スターを付ける", + "unstar": "スターを外す", + "mark_unread": "未読にする", + "mark_read": "既読にする", + "unread": "未読", + "read": "既読", + "spam_short": "迷惑メール", + "not_spam_short": "迷惑メールでない", + "move": "移動", + "print": "印刷", + "view_source": "ソースを表示", + "fullscreen": "全画面表示", + "exit_fullscreen": "全画面表示を終了", + "view_in_dark_mode": "ダークモードで表示", + "view_in_light_mode": "ライトモードで表示", + "export_email": ".emlとしてエクスポート", + "forward_as_attachment": "添付ファイルとして転送", + "import_email": ".eml または .zip をインポート", + "keyboard_shortcuts": "キーボードショートカット (?)", + "email_source": "メールソース", + "draft_banner": "このメッセージは下書きです", + "edit_draft": "編集", + "copy_source": "クリップボードにコピー", + "source_copied": "ソースをクリップボードにコピーしました", + "attachments": "添付ファイル", + "important": "重要", + "download": "ダウンロード", + "download_all": "すべてダウンロード", + "from": "送信者", + "to": "宛先", + "cc": "CC", + "bcc": "BCC", + "date": "日付", + "subject": "件名", + "show_details": "詳細を表示", + "hide_details": "詳細を非表示", + "external_content_warning": "画像と外部コンテンツがブロックされました", + "load_external_content": "画像を読み込む", + "trust_sender": "常にこの送信者を信頼する", + "sender_check": { + "unverified_label": "未確認の送信者", + "failed_label": "送信者の確認に失敗", + "unverified": "このメッセージは{domain}からのものとされていますが、それを裏付けるものはありません。{domain}の有効な署名がなく、{domain}が許可したサーバーから送信されたものでもありません。", + "unverified_sent_from": "このメッセージは{domain}からのものとされていますが、実際には{host}から送信されており、{domain}の有効な署名がありません。", + "failed": "このメッセージは{domain}からのものとされていますが、このドメインの送信者チェックに失敗しました。なりすましの可能性があります。", + "failed_sent_from": "このメッセージは{domain}からのものとされていますが、実際には{host}から送信されており、{domain}の送信者チェックにも失敗しました。なりすましの可能性があります。", + "caution": "リンクや添付ファイル、パスワードや支払い情報の要求には注意してください。" + }, + "back_to_list": "リストに戻る", + "view_contact": "連絡先を表示", + "message_details": "メッセージの詳細", + "more_reply_options": "その他の返信オプション", + "set_tag": "ラベルを設定", + "tag": "ラベル", + "more_actions": "その他の操作", + "move_to": "移動...", + "remove_tag": "ラベルを削除", + "tag_filter_placeholder": "ラベルを絞り込む", + "tag_no_matches": "一致するラベルがありません", + "more_count": "他{count}件", + "characters_count": "{count}文字", + "quick_reply_placeholder": "クイック返信を入力...", + "more_options": "その他のオプション", + "sending": "送信中...", + "security_authentication": "セキュリティと認証", + "technical_details": "技術的な詳細", + "message_id_label": "メッセージID:", + "reply_to_label": "返信先:", + "delivery_time_label": "配信時刻:", + "conversation_part_label": "会話の一部:", + "previous_messages": "{count}件の以前のメッセージ", + "previous_messages_plural": "{count}件の以前のメッセージ", + "time": { + "day": "日", + "days": "日", + "hour": "時間", + "hours": "時間", + "minute": "分", + "minutes": "分" + }, + "unknown_sender": "不明", + "recipient_me": "自分", + "recipient_and_others": "{name}と他{count}人", + "recipient_to_prefix": "宛先:", + "authentication": { + "title": "認証", + "status": { + "verified": "確認済み", + "warning": "警告", + "none": "未認証" + }, + "spf": { + "pass": "SPF合格", + "fail": "SPF不合格", + "none": "SPFなし" + }, + "dkim": { + "pass": "DKIM有効", + "fail": "DKIM無効", + "none": "DKIMなし" + }, + "dmarc": { + "pass": "DMARC合格", + "fail": "DMARC不合格", + "none": "DMARCなし" + }, + "spam_score": "スパムスコア", + "tooltip_spf": "Sender Policy Framework: 送信サーバーがそのドメインを代表してメールを送信する権限を持つことを確認します", + "tooltip_dkim": "DomainKeys Identified Mail: 暗号署名を用いて、メールが送信中に改ざんされていないことを確認します", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: SPF と DKIM が送信者のドメインと一致することを確認し、失敗時のポリシーを定めます", + "policy": "ポリシー", + "result": { + "pass": "合格", + "fail": "不合格", + "softfail": "ソフト失敗", + "neutral": "中立", + "permerror": "永続的エラー", + "temperror": "一時的エラー", + "none": "なし" + } + }, + "headers": { + "routing": "ルーティング", + "received": "受信", + "message_id": "メッセージID", + "list_info": "リスト情報" + }, + "tooltips": { + "reply": "返信", + "reply_all": "全員に返信 (a)", + "forward": "転送 (f)", + "archive": "アーカイブ", + "delete": "削除", + "star": "スター (s)", + "unstar": "スター解除 (s)", + "compose": "新規作成 (c)", + "previous": "前のメール", + "next": "次のメール", + "edit_draft": "下書きを編集" + }, + "spam": { + "button_title": "迷惑メールを報告", + "not_spam_title": "正当なメールとしてマーク", + "toast_success": "迷惑メールに移動しました", + "toast_batch": "{count}件のメールを迷惑メールに移動しました", + "toast_undo": "元に戻す", + "toast_not_spam_success": "受信トレイに移動しました", + "toast_not_spam_batch": "{count}件のメールを受信トレイに移動しました", + "error": "迷惑メールの報告に失敗しました", + "error_not_spam": "メールの復元に失敗しました" + }, + "unsubscribe_banner": { + "label": "ニュースレター", + "button": "購読解除", + "confirm_title": "この送信者の購読を解除しますか?", + "confirm_button": "確認", + "cancel": "キャンセル", + "success_http": "購読解除ページを新しいタブで開きました", + "success_mailto": "配信停止メールを送信しました", + "confirm_message_http": "配信停止ページを新しいタブで開きます。", + "confirm_message_mailto": "送信者に配信停止メールを送信します。", + "error": "購読解除できませんでした", + "dismiss": "閉じる" + }, + "calendar_invitation": { + "loading": "イベント詳細を読み込み中…", + "title": "カレンダー招待", + "published_title": "公開イベント", + "response_title": "イベント返信", + "update_title": "イベント更新", + "counter_title": "対案", + "refresh_title": "更新依頼", + "declined_counter_title": "対案は拒否されました", + "cancelled_title": "イベントがキャンセルされました", + "organizer": "{name} が主催", + "organizer_label": "主催者:", + "attendees": "{count}名の参加者", + "accept": "承諾", + "maybe": "未定", + "decline": "辞退", + "add_to_calendar": "カレンダーに追加", + "added": "カレンダーに追加しました", + "rsvp_sent": "回答を送信しました", + "parse_error": "招待を読み込めませんでした", + "action_failed": "このカレンダー操作を完了できませんでした。", + "no_calendar": "カレンダーが利用できません", + "published_info": "このイベントは参照用として共有されました。", + "response_info": "このメッセージには参加者の返信が含まれています。", + "response_info_organizer": "この参加者の返信はあなたのイベントを更新します。", + "update_info": "このメッセージは既存のイベントを更新します。", + "counter_info": "このメッセージはイベント変更の提案です。", + "counter_info_organizer": "この参加者はあなたのイベントに変更を提案しました。", + "refresh_info": "このメッセージは最新のイベント詳細を要求しています。", + "refresh_info_organizer": "参加者が最新のイベント詳細を要求しました。", + "declined_counter_info": "主催者が対案を拒否しました。", + "authentication_failed_info": "この招待メールの認証チェックに失敗しました。カレンダー操作は慎重に行ってください。", + "authentication_missing_info": "この招待には検証済みのメール認証がありません。不審な点があれば主催者に詳細を確認してください。", + "sender_mismatch_info": "この招待は {sender} から送信されましたが、カレンダーデータ上の主催者は {organizer} です。", + "sender_mismatch_unverified_info": "この招待は {sender} から送信されましたが、カレンダーデータ上の主催者は {organizer} であり、メッセージも検証できませんでした。", + "organizer_role": "あなたがこのイベントの主催者です", + "your_response": "あなたの返答: {status}", + "response_needed": "返答が必要です", + "response_accepted": "承諾", + "response_tentative": "未定", + "response_declined": "辞退", + "response_delegated": "委任済み", + "actor_sent_info": "{name} から送信されました。", + "actor_response_info": "{name} が {status} と返答しました。", + "actor_counter_info": "{name} がこのイベントの変更を提案しました。", + "actor_refresh_info": "{name} が最新のイベント詳細を求めました。", + "actor_declined_counter_info": "{name} が対案を拒否しました。", + "actor_note": "メモ: {comment}", + "actor_unknown": "どなたか", + "proposed_changes": "提案された変更", + "change_title": "件名", + "change_time": "日時", + "change_location": "場所", + "change_description": "説明", + "change_empty": "なし", + "change_from_to": "{before} -> {after}", + "apply_proposal": "提案された変更を適用", + "proposal_applied": "提案された変更を適用しました。", + "review_proposal": "提案を確認", + "review_request": "依頼を確認", + "view_in_calendar": "カレンダーで表示", + "select_calendar": "カレンダーを選択", + "already_in_calendar": "カレンダーに登録済み", + "request_info": "このイベントに招待されました。主催者に参加可否をお知らせください。", + "cancel_info": "主催者がこのイベントをキャンセルしました。", + "event_updated": "更新 #{sequence}", + "event_status_tentative": "仮", + "event_status_cancelled": "キャンセル済み", + "collapse": "詳細を非表示", + "expand": "詳細を表示" + }, + "previous": "前へ", + "next": "次へ", + "send": "送信", + "more": "他", + "details": { + "recipients_routing": "宛先とルーティング", + "authentication_security": "認証とセキュリティ", + "identifiers_threading": "識別子とスレッド", + "mailing_list": "メーリングリスト", + "message_properties": "メッセージのプロパティ", + "sent": "送信日時", + "received": "受信日時", + "delivery_time": "配信時間", + "in_reply_to": "返信先", + "references": "参照", + "thread_id": "スレッドID", + "size": "サイズ", + "mime_type": "MIMEタイプ", + "attachments_summary": "{count} ファイル · {size}", + "list_id": "リストID", + "list_help": "リストヘルプ", + "list_post": "リスト投稿", + "list_unsubscribe": "購読解除", + "iprev": "逆引きDNS", + "spam_status": "スパム状態", + "ai_verdict": "AIの判定", + "account": "アカウント", + "no_subject": "(件名なし)" + }, + "scheduled_banner": "{date} に送信予定", + "scheduled_send_created": "メールを送信予約しました", + "sending_in": "{seconds} 秒後に送信", + "undo_send_failed": "取り消せませんでした。メッセージはすでに送信された可能性があります。", + "cancel_scheduled_send": "送信をキャンセル", + "reschedule_send": "再予約", + "cancel_and_edit": "キャンセルして編集", + "cancel_and_compose_again": "キャンセルして新規作成", + "reschedule_prompt": "新しい日時を入力してください。例: 2026-05-04T15:30", + "scheduled_actions_only": "予約済みビューでは予約アクションのみ利用できます", + "undo_send_scheduled": "メッセージは送信予約されています", + "undo_send": "送信を取り消す", + "contact_sidebar": { + "title": "連絡先", + "close": "サイドバーを閉じる", + "action_email": "メール", + "action_email_title": "メールを送信", + "action_copy": "コピー", + "action_copy_title": "メールアドレスをコピー", + "action_edit_title": "連絡先を編集", + "section_emails": "メールアドレス", + "section_phones": "電話番号", + "section_organizations": "組織", + "section_addresses": "住所", + "section_notes": "メモ", + "not_in_contacts": "連絡先に登録されていません", + "add_to_contacts": "連絡先に追加", + "copied": "コピーしました!", + "copy_failed": "コピーに失敗しました", + "view_contact": "連絡先を表示", + "view_details": "詳細を表示" + }, + "send_now": "今すぐ送信", + "verification_code": { + "copy": "コード {code} をコピー", + "copied": "コードをコピーしました", + "copy_failed": "コードをコピーできませんでした" + }, + "mv_position": "{index} / {total}", + "plugin_panel_show": "プラグインパネルを表示", + "plugin_panel_hide": "プラグインパネルを隠す" + }, + "email_composer": { + "select_from_directory": "ディレクトリから選択", + "minimize": "最小化", + "maximize": "全画面表示", + "restore": "元に戻す", + "new_message": "新規メッセージ", + "signature_edit_hint": "ダブルクリックで署名を編集", + "reply": "返信", + "reply_all": "全員に返信", + "forward": "転送", + "reply_to": "返信", + "reply_all_to": "全員に返信", + "forward_message": "転送", + "from": "送信元", + "to": "宛先", + "cc": "CC", + "bcc": "BCC", + "subject": "件名", + "body_placeholder": "メッセージを入力...", + "send": "送信", + "cancel": "キャンセル", + "attach": "添付", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "ファイルから添付", + "files_picker": { + "attach": "{count, plural, =0 {添付} other {# 件のファイルを添付}}", + "empty_folder": "このフォルダーは空です", + "load_failed": "ファイルを読み込めませんでした" + }, + "discard": "破棄", + "discard_draft_title": "下書きを破棄しますか?", + "discard_draft_confirm": "未保存の変更があります。この下書きを破棄しますか?", + "discard_draft_message": "下書きは削除されます。", + "saving": "保存中...", + "draft_saved": "下書きを保存しました", + "save_failed": "保存に失敗しました", + "to_placeholder": "受信者のメールアドレス", + "cc_placeholder": "CC受信者", + "bcc_placeholder": "BCC受信者", + "subject_placeholder": "件名", + "cc_label": "CC:", + "bcc_label": "BCC:", + "subject_label": "件名:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count}件のメール", + "no_subject": "(件名なし)", + "unknown_sender": "不明", + "quote": { + "reply_header": "{date}に{sender}が書きました:", + "forward_header": "---------- 転送メッセージ ----------", + "from": "送信者: {sender}", + "date": "日付: {date}", + "subject": "件名: {subject}", + "to": "宛先: {recipients}" + }, + "remove_sub_address": "サブアドレスを削除", + "from_override": { + "toggle_off": "上書き", + "toggle_on": "上書きを取り消す", + "toggle_tooltip": "差出人名とアドレスを自由に編集できます。メールは引き続きあなたのアイデンティティ経由で送信されます。", + "name_label": "差出人名", + "name_placeholder": "名前", + "email_label": "差出人メールアドレス", + "email_placeholder": "alias@example.com", + "envelope_notice": "サーバーがこのアドレスをエンベロープ送信者として受け付けない場合は、代わりに {identity} が使われ、受信者は Return-Path ヘッダーでそのアドレスを確認できます。" + }, + "save_as_template": "テンプレートとして保存", + "attach_from_device": "このデバイスからアップロード", + "templates_menu": "テンプレート", + "insert_template": "テンプレートを挿入", + "options": "オプション", + "option_plain_text": "プレーンテキスト", + "option_plain_text_hint": "書式なしで送信", + "option_read_receipt": "開封確認を要求", + "option_read_receipt_hint": "受信者に開封の確認を求めます", + "option_dsn": "配信通知", + "option_dsn_hint": "サーバーが配信したときにレポートを受け取ります", + "option_require_tls": "暗号化された配信を必須にする", + "option_require_tls_hint": "TLS でのみ配信し、できなければ返送します", + "option_priority": "重要度", + "option_priority_high": "高", + "option_priority_normal": "標準", + "option_priority_low": "低", + "validation": { + "recipient_required": "送信するには宛先を追加してください", + "body_required": "メッセージを入力するかファイルを添付してください", + "invalid_recipients": "送信されませんでした - 次のアドレスを確認してください: {addresses}", + "attachments_uploading": "添付ファイルをアップロード中です。完了次第送信します", + "attachment_upload_failed": "送信されませんでした。添付ファイルのアップロードに失敗しました。削除してもう一度お試しください。" + }, + "upload_progress": "アップロード中 {uploaded} / {total}", + "upload_cancel": "アップロードをキャンセル", + "upload_failed": "{filename} のアップロードに失敗しました", + "attachment_too_large": "「{name}」はサーバーの上限を超えています(1 ファイルあたり {max})", + "attachments_total_too_large": "添付ファイルがサーバーの上限(1 通あたり {max})を超えます", + "send_failed": "メールの送信に失敗しました", + "smime_sign_on": "S/MIME 署名を有効化しました", + "smime_sign_off": "S/MIME 署名を無効化しました", + "smime_encrypt_on": "S/MIME 暗号化を有効化しました", + "smime_encrypt_off": "S/MIME 暗号化を無効化しました", + "smime_encrypt_unavailable": "S/MIME 暗号化は利用できません – 受信者の証明書がありません", + "smime_unlock_title": "S/MIME 鍵のロックを解除", + "smime_unlock_message": "S/MIME 鍵を解除するためのパスフレーズを入力してください。", + "smime_unlock_button": "ロック解除", + "smime_passphrase_placeholder": "S/MIME パスフレーズ", + "send_timeout": "サーバーが応答しなくなりました。再送信する前に送信済みフォルダーを確認してください。メッセージはすでに送信されている可能性があります。", + "send_offline": "オフラインのようです。メッセージはそのまま残っています。オンラインに戻ったら再送信してください。", + "formatting": "書式", + "send_recipients_rejected": "送信されませんでした。サーバーがすべての宛先を拒否しました。", + "send_plugin_failed": "送信されませんでした。送信を処理するプラグインが失敗したか、時間がかかりすぎました。もう一度送信する前に送信済みフォルダーを確認してください。", + "send_some_recipients_rejected": "送信しましたが、次の宛先には届きません。サーバーが拒否しました。", + "continue_draft": "下書きを続ける", + "close_draft_title": "下書きを保存または破棄しますか?", + "close_draft_message": "未保存の変更があります。下書きとして保存しますか、それとも破棄しますか?", + "drop_files": "ファイルをドロップして添付", + "show_less": "折りたたむ", + "forgot_attachment": { + "title": "添付ファイルを忘れていませんか?", + "message": "メッセージに「{keyword}」が含まれていますが、ファイルが添付されていません。このまま送信しますか?", + "send_anyway": "そのまま送信", + "back": "編集に戻る" + }, + "empty_subject": { + "title": "件名なしで送信しますか?", + "message": "このメッセージには件名がありません。このまま送信しますか?", + "dont_ask_again": "今後は確認しない", + "send_anyway": "そのまま送信", + "back": "編集に戻る" + }, + "add_link": "リンクを追加", + "link_url_prompt": "URLを入力してください", + "sending": "送信中...", + "schedule_send": "送信を予約", + "schedule_send_description": "サーバーがこのメッセージを送信する日時を選択します。", + "schedule_preset": { + "tomorrow_morning": "明日の朝", + "tomorrow_afternoon": "明日の午後", + "monday_morning": "月曜日の朝" + }, + "schedule_send_required": "日時を選択してください。", + "schedule_send_invalid": "有効な日時を入力してください。", + "schedule_send_future": "未来の日時を選択してください。", + "schedule_send_too_late": "この日時はサーバーが許可する範囲を超えています。", + "schedule_send_unsupported": "このアカウントでは予約送信はサポートされていません。", + "schedule_send_cleanup_warning": "予約送信は作成されましたが、下書きのクリーンアップに失敗しました。", + "send_delay_unsupported": "このアカウントでは送信遅延はサポートされていません。", + "send_delay_unsupported_confirm": "このアカウントは送信遅延に対応していません。すぐに送信しますか?", + "recipient_edit_email": "メールアドレスを編集", + "recipient_edit_name": "表示名を編集", + "recipient_email_placeholder": "メールアドレス", + "recipient_name_placeholder": "表示名", + "invalid_recipient": "有効なメールアドレスではありません", + "autocomplete_search_server": "サーバーを検索", + "autocomplete_searching": "検索中...", + "mention_recipients": "宛先", + "mention_card_not_recipient": "宛先にも CC にも入っていないため、このメッセージは届きません。", + "mention_card_add": "{field}に追加", + "mention_card_remove": "メンションを削除", + "toolbar": { + "bold": "太字", + "italic": "斜体", + "underline": "下線", + "strikethrough": "取り消し線", + "text_color": "文字色", + "remove_color": "色を解除", + "heading_1": "見出し1", + "heading_2": "見出し2", + "bullet_list": "箇条書き", + "ordered_list": "番号付きリスト", + "quote": "引用", + "code_block": "コードブロック", + "align_left": "左揃え", + "align_center": "中央揃え", + "align_right": "右揃え", + "text_direction": "文字方向 (RTL/LTR)", + "link": "リンク", + "table": "表", + "clear_formatting": "書式をクリア", + "undo": "元に戻す", + "redo": "やり直す", + "add_row_above": "上に行を追加", + "add_row_below": "下に行を追加", + "add_column_before": "左に列を追加", + "add_column_after": "右に列を追加", + "delete_row": "行を削除", + "delete_column": "列を削除", + "toggle_header_row": "ヘッダー行の切り替え", + "delete_table": "表を削除", + "pick_size": "サイズを選択", + "font_size": "文字サイズ", + "font_size_default": "デフォルト", + "background_color": "背景色", + "remove_background_color": "背景色を削除" + }, + "send_filing_warning": "送信されましたが、送信後の整理に失敗しました。古い下書きが残る場合があります。", + "pop_out": "返信を別ウィンドウで開く", + "send_guard_failed": "送信されませんでした - 送信前にメッセージを確認するプラグインが失敗したか、応答しませんでした。" + }, + "confirm_dialog": { + "confirm": "確認", + "cancel": "キャンセル" + }, + "common": { + "loading": "読み込み中...", + "error": "エラー", + "success": "成功", + "cancel": "キャンセル", + "save": "保存", + "delete": "削除", + "edit": "編集", + "close": "閉じる", + "search": "検索", + "refresh": "更新", + "settings": "設定", + "help": "ヘルプ", + "logout": "ログアウト", + "yes": "はい", + "no": "いいえ", + "unknown": "不明", + "app_title": "ウェブメール", + "reconnecting": "接続が切れました。再接続を試みています…", + "rate_limited_title": "サーバー認証は一時的に制限されています。", + "rate_limited_detail": "ロックアウトを避けるため、Bulwark はバックグラウンド要求を一時停止しました。{seconds} 秒後に再試行します。", + "rate_limited_action_title": "ロックアウトを避けるため要求を一時停止しました。", + "rate_limited_action_detail": "Bulwark はサーバーのクールダウンが終わるまで、追加の認証付きリクエストを送信しません。{seconds} 秒後に再試行してください。", + "pull_to_refresh": "引っ張って更新", + "release_to_refresh": "離して更新", + "refreshing": "更新中…" + }, + "notifications": { + "email_sent": "メールを送信しました", + "email_deleted": "メールを削除しました", + "email_archived": "メールをアーカイブしました", + "emails_deleted": "{count, plural, other {#件のメールを削除しました}}", + "emails_archived": "{count, plural, other {#件のメールをアーカイブしました}}", + "error_archiving": "メールをアーカイブできませんでした", + "error_updating": "メールを更新できませんでした", + "email_starred": "スターを付けました", + "email_unstarred": "スターを外しました", + "email_marked_read": "既読にしました", + "email_marked_unread": "未読にしました", + "copied_to_clipboard": "クリップボードにコピーしました", + "source_copied": "ソースをクリップボードにコピーしました", + "error_sending": "メールの送信に失敗しました", + "error_deleting": "メールの削除に失敗しました", + "error_loading": "メールの読み込みに失敗しました", + "new_email": "新しいメール", + "new_email_from": "{sender}から", + "click_to_view": "クリックして表示", + "email_moved": "メールを移動しました", + "emails_moved": "{count}件のメールを移動しました", + "moved_to_mailbox": "{mailbox}に移動しました", + "move_failed": "移動に失敗しました", + "removed_from_folder": "{mailbox} から削除しました", + "remove_from_folder_failed": "このフォルダから削除できませんでした", + "emails_copied": "{count}件のメールをコピーしました", + "copy_failed": "コピーに失敗しました", + "move_error": "選択したフォルダにメールを移動できませんでした", + "email_tagged": "メールにタグを付けました", + "emails_tagged": "{count}件のメールにタグを付けました", + "tag_failed": "タグ付けに失敗しました", + "identity_created": "送信者情報を作成しました", + "identity_updated": "送信者情報を更新しました", + "identity_deleted": "送信者情報を削除しました", + "identity_set_primary": "プライマリ送信者情報を更新しました", + "identity_create_failed": "送信者情報の作成に失敗しました: {error}", + "identity_update_failed": "送信者情報の更新に失敗しました: {error}", + "identity_delete_failed": "送信者情報の削除に失敗しました: {error}", + "identity_unauthorized": "このメールアドレスからの送信は許可されていません", + "identity_not_found": "送信者情報が見つかりません", + "vacation_saved": "不在の設定を保存しました", + "vacation_save_failed": "不在の設定の保存に失敗しました", + "filters_saved": "フィルターを保存しました", + "filters_save_failed": "フィルターの保存に失敗しました", + "filters_deleted": "フィルタールールが削除されました", + "templates_exported": "テンプレートをエクスポートしました", + "templates_imported": "{count}件のテンプレートをインポートしました", + "templates_import_errors": "一部のテンプレートをインポートできませんでした", + "templates_import_empty": "ファイルにテンプレートが見つかりません", + "export_email_error": "メールのエクスポートに失敗しました", + "import_email_success": "メールを正常にインポートしました", + "import_email_error": "メールのインポートに失敗しました", + "rule_created": "ルールを作成しました", + "rule_merged": "ルール「{name}」に追加しました", + "rule_already_covered": "ルール「{name}」ですでに対象になっています", + "rule_apply_existing": "{count, plural, other {既存の # 件に適用}}", + "rule_applied": "{count, plural, =0 {変更が必要なメールはありませんでした} other {# 件のメールにルールを適用しました}}", + "rule_apply_failed": "既存のメールにルールを適用できませんでした", + "rule_edit": "ルールを編集", + "rule_undo": "元に戻す", + "rule_undone": "ルールの変更を元に戻しました", + "rule_undo_failed": "ルールの変更を元に戻せませんでした", + "rule_undo_conflict": "その後フィルターが変更されています。フィルター設定で変更を元に戻してください。", + "rule_folder_failed": "フォルダを作成できませんでした", + "vacation_filters_save_failed": "不在の設定を保存しましたが、転送と返信する相手の保存に失敗しました" + }, + "date": { + "today": "今日", + "yesterday": "昨日", + "this_week": "今週", + "last_week": "先週", + "this_month": "今月", + "older": "それ以前", + "just_now": "たった今", + "minutes_ago": "{count}分前", + "minutes_ago_plural": "{count}分前", + "hours_ago": "{count}時間前", + "hours_ago_plural": "{count}時間前", + "days_ago": "{count}日前", + "days_ago_plural": "{count}日前" + }, + "language": { + "title": "言語", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "言語を選択", + "switch_to_english": "英語に切り替え", + "switch_to_french": "フランス語に切り替え", + "switch_to_japanese": "日本語に切り替え", + "switch_to_spanish": "スペイン語に切り替え", + "switch_to_italian": "イタリア語に切り替え", + "switch_to_german": "ドイツ語に切り替え", + "switch_to_dutch": "オランダ語に切り替え", + "switch_to_portuguese": "ポルトガル語に切り替え", + "switch_to_russian": "ロシア語に切り替え", + "switching": "言語を変更中...", + "switch": "言語を切り替え", + "current": "現在の言語", + "polish": "Polski", + "switch_to_polish": "ポーランド語に切り替え", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "ペルシア語に切り替え", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "モンゴル語に切り替え" + }, + "settings": { + "title": "設定", + "back_to_mail": "メールに戻る", + "save_success": "設定を保存しました", + "import_success": "設定をインポートしました", + "import_error": "設定のインポートに失敗しました", + "reset_confirm": "すべての設定をデフォルトにリセットしてもよろしいですか?", + "unsaved_changes": "保存されていない変更があります", + "discard_changes": "変更を破棄しますか?", + "discard": "破棄", + "keep_editing": "編集を続ける", + "tabs": { + "appearance": "外観", + "language": "言語、地域、時刻", + "email": "メール動作", + "composer": "作成", + "privacy": "プライバシーとセキュリティ", + "account": "アカウント", + "identities": "送信者情報", + "vacation": "不在", + "advanced": "詳細設定", + "calendar": "カレンダー", + "filters": "フィルター", + "templates": "テンプレート", + "folders": "フォルダー", + "keywords": "ラベル", + "security": "セキュリティ", + "encryption": "暗号化", + "files": "ファイル", + "contacts": "連絡先", + "protocol_handlers": "既定のアプリ", + "sidebar_apps": "サイドバーアプリ", + "notifications": "通知", + "layout": "レイアウト", + "themes": "テーマ", + "reading": "閲覧", + "composing": "作成", + "downloads": "ダウンロード", + "content_senders": "コンテンツと送信者", + "about_data": "情報とデータ", + "debug": "デバッグ" + }, + "themes": { + "title": "テーマ", + "description": "管理者が配布したテーマと組み込みのプリセットから選択", + "locked": "テーマの選択は管理者によってロックされています。", + "forced": "テーマ「{name}」は管理者によって固定されているため、変更できません", + "admin_theme": "管理者テーマ", + "activated": "テーマを適用しました", + "default_restored": "デフォルトのテーマに戻しました", + "default_name": "デフォルト", + "default_badge": "デフォルト", + "admin_enforced": "管理者により固定" + }, + "tab_groups": { + "general": "一般", + "account": "アカウントと身元", + "organization": "メール整理", + "apps": "アプリ", + "system": "システム", + "appearance": "外観", + "mail": "メール", + "privacy": "プライバシーとセキュリティ", + "advanced": "詳細" + }, + "appearance": { + "message_list_order": { + "label": "メッセージ一覧の並び順", + "description": "一覧でメッセージをどう並べるか。並べ替えはサーバーが行うため、読み込み済みのメッセージだけでなくフォルダー全体に適用されます。", + "preset": { + "chronological": "時系列(新しい順)", + "unread_first": "未読を先に", + "starred_first": "スター付きを先に", + "tagged_first": "タグ付きを先に", + "custom": "カスタム" + }, + "tag_label": "タグ", + "advanced_show": "詳細: 並べ替えレベルを編集", + "advanced_hide": "詳細を隠す", + "criterion_label": "レベル {n} の条件", + "direction_label": "レベル {n} の方向", + "criteria": { + "unread": "未読", + "starred": "スター付き", + "tag": "タグ", + "receivedAt": "受信日時", + "sentAt": "送信日時", + "from": "送信者", + "subject": "件名", + "size": "サイズ" + }, + "direction": { + "unread_first": "未読を先に", + "read_first": "既読を先に", + "starred_first": "スター付きを先に", + "unstarred_first": "スターなしを先に", + "tagged_first": "タグ付きを先に", + "untagged_first": "タグなしを先に", + "newest_first": "新しい順", + "oldest_first": "古い順", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "大きい順", + "smallest_first": "小さい順" + }, + "add_level": "レベルを追加", + "remove_level": "レベルを削除", + "move_up": "上へ移動", + "move_down": "下へ移動", + "tiebreak_note": "最大 3 レベル。後のレベルは同順位の場合に使われます。「新しい順」は常に最後に適用されます。", + "scope": { + "label": "すべてのフォルダーに適用", + "description": "オフの場合、並び順は受信トレイにのみ適用され、他のフォルダーは時系列のままです。" + }, + "unsupported_note": "お使いのメールサーバーは既読状態・スター・タグによる並べ替えに対応していません。これらのレベルは無視され、一覧は時系列のままになります。" + }, + "title": "外観", + "description": "ウェブメールの見た目をカスタマイズ", + "theme": { + "label": "テーマ", + "description": "お好みの配色を選択", + "light": "ライト", + "dark": "ダーク", + "system": "システム" + }, + "language": { + "label": "言語", + "description": "お好みの言語を選択" + }, + "font_size": { + "label": "フォントサイズ", + "description": "読みやすさのためにテキストサイズを調整", + "small": "小", + "medium": "中", + "large": "大" + }, + "list_density": { + "label": "密度", + "description": "UI全体の間隔を調整", + "extra_compact": "極小", + "compact": "コンパクト", + "regular": "標準", + "comfortable": "ゆったり" + }, + "interface_layout": { + "label": "インターフェースのレイアウト", + "description": "検索・ナビゲーション・ツールバーの配置", + "default": "デフォルト", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "アニメーションを有効にする", + "description": "スムーズな遷移とエフェクトを表示" + }, + "toolbar_position": { + "label": "ツールバーの位置", + "description": "メールアクションボタン(返信、アーカイブ、削除など)の表示位置", + "top": "上部", + "below_subject": "件名の下" + }, + "toolbar_labels": { + "label": "ツールバーラベルを表示", + "description": "ツールバーアイコンの横にテキストラベルを表示します。アイコンに慣れたらスペースを節約するために無効にできます。" + }, + "hide_account_switcher": { + "label": "サイドバーのアカウント切替を非表示", + "description": "フォルダサイドバー上部のアカウント選択を非表示にします。下部のナビゲーションレールからアカウントを切り替えることは引き続き可能です。" + }, + "show_rail_account_list": { + "label": "ナビゲーションレールにアカウントアバターを表示", + "description": "ナビゲーションレールの下部に個々のアカウントの丸を表示して素早く切り替えできるようにし、その下にサインアウトボタンを配置します。" + }, + "unified_mailbox": { + "label": "統合メールボックス", + "description": "接続されたすべてのアカウントの統合フォルダ(受信トレイ、送信済みなど)を表示", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "グループ受信トレイを含める", + "description": "共有/グループ受信トレイも統合ビューに含めます。" + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "カラフルなサイドバーアイコン", + "description": "フォルダーとタグのアイコンを種類別に色分けします(受信トレイは青、迷惑メールは赤、送信済みは緑など)。モノクロのサイドバーにするには無効にしてください。" + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "メッセージの総数を表示", + "description": "フォルダーやタグの横に、未読数に加えてメッセージの総数を表示します。未読数のみを表示するには無効にします。" + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "共有メールアカウントを表示", + "description": "共有されたメールボックスのフォルダーをサイドバーの「共有」に表示します。オフにするとすべて非表示になります。下で個別のアカウントだけを非表示にすることもできます。カレンダーと連絡先は表示されたままです。" + }, + "pro_interface": { + "label": "Pro インターフェイス(実験的)", + "description": "デスクトップ専用のパワーユーザー向けレイアウトで、マルチタブのメッセージ閲覧やアカウント横断のワークフローに対応します。標準インターフェイスには影響せず、いつでも元に戻せます。", + "open_label": "Pro インターフェイスを開く", + "back_to_standard": "標準に戻る" + }, + "cross_unread": { + "label": "すべてのアカウント: 未読", + "description": "「すべてのアカウント」セクションに、すべてのアカウントの未読メールを一覧表示する項目を表示します。迷惑メール、送信済み、アーカイブ、ゴミ箱、下書きを除くすべてのフォルダーが対象です。" + }, + "cross_starred": { + "label": "すべてのアカウント: スター付き", + "description": "「すべてのアカウント」セクションに、すべてのアカウントのスター付きメールを一覧表示する項目を表示します。迷惑メール、送信済み、アーカイブ、ゴミ箱、下書きを除くすべてのフォルダーが対象です。" + }, + "cross_all": { + "label": "すべてのアカウント: すべてのメール", + "description": "「すべてのアカウント」セクションに、すべてのアカウントのすべてのメールを一覧表示する項目を表示します。迷惑メール、送信済み、アーカイブ、ゴミ箱、下書きを除くすべてのフォルダーが対象です。" + } + }, + "keywords": { + "title": "メールラベル", + "description": "メールをカラーで整理するためのラベルを定義します。サーバーにJMAPキーワードとして保存されます。", + "add_keyword": "ラベルを追加", + "label_field": "表示名", + "label_placeholder": "例:仕事、個人、緊急", + "id_field": "ラベルID", + "id_placeholder": "例:work、personal", + "color_field": "色", + "id_exists": "このラベルIDは既に存在します", + "edit": "ラベルを編集", + "delete": "ラベルを削除", + "save": "保存", + "add": "追加", + "cancel": "キャンセル", + "migrating": "既存のメールのラベルを更新中…", + "migration_error": "既存のメールのラベルの更新に失敗しました", + "filters_migration_error": "一部のフィルタールールが古いタグを使用しています。手動で更新してください", + "nesting": { + "label": "ネストされたラベル", + "description": "ラベルを他のラベルの下にネストし、サイドバーにツリーとして表示します。" + }, + "parent_field": "親ラベル", + "no_parent": "親ラベルなし", + "too_long": "このラベルのパスが長すぎます(最大{max}文字)", + "has_children_locked": "このラベルの下に他のラベルがネストされているため、名前と親ラベルは変更できません。先に移動または削除してください。", + "has_children_delete": "先にこのラベルの下にネストされたラベルを削除してください", + "visibility_field": "サイドバーでの表示", + "visibility": { + "show": "表示する", + "unread": "未読がある場合に表示", + "hide": "表示しない" + }, + "unrecognized": { + "title": "サーバーからラベルを復元", + "description": "ラベルの名前と色はこのブラウザーに保存されますが、ラベル自体はJMAPキーワードとしてメールに保存されています。ここに定義のないキーワードをメールから検索し、ラベルとして追加し直せます。", + "scan": "ラベルを検索", + "rescan": "再検索", + "scanning": "検索中… {total}件中{scanned}件", + "none": "メールに付いているラベルはすべてここに定義済みです。", + "messages": "{count, plural, one {#件} other {#件}}", + "add_all": "{count}件すべて追加", + "partial": "{total}件のうち最新の{scanned}件のみを検索しました。古いメールにのみ使われているラベルは見つからない場合があります。", + "error": "メールからラベルを検索できませんでした。" + } + }, + "notifications": { + "test_sound": "通知音をテスト", + "sounds": { + "default": "デフォルト(ビープ)", + "cheerful": "チアフル", + "involved": "インボルブド", + "swift": "スウィフトジェスチャー", + "relax": "リラックス" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "通知音", + "description": "通知に使用する音を選択", + "choose": "サウンド", + "choose_desc": "通知音を選択し、スピーカーアイコンをクリックしてプレビュー" + }, + "email": { + "title": "メール通知", + "description": "受信メールの通知を設定", + "enabled": "メール通知", + "enabled_desc": "新しいメールが届いたときに通知を表示", + "sound": "通知音", + "sound_desc": "新しいメールが届いたときに音を鳴らす", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "カレンダー通知", + "description": "カレンダーイベントの通知を設定", + "enabled": "イベント通知", + "enabled_desc": "今後のカレンダーイベントのアラートを表示", + "sound": "通知音", + "sound_desc": "カレンダーリマインダーの音を鳴らす", + "invitation_parsing": "メール招待を解析", + "invitation_parsing_desc": "メール添付ファイルのカレンダー招待を検出し、カレンダーアクションを表示" + }, + "push": { + "confirm_disable_message": "このデバイスは、サイトが閉じているときに通知を受信しなくなります。", + "confirm_disable_title": "バックグラウンド通知を無効にしますか?", + "description": "このサイトが閉じているときに新着メールのシステム通知を受信します。Bulwark プッシュリレー経由で配信され、リレーがメール内容を見ることはありません。", + "disable": "無効化", + "enable": "有効化", + "dismiss_aria": "通知プロンプトを閉じる", + "ios_hint": "iOS では、最初にサイトをホーム画面にインストールしてください。Safari はインストールされた PWA にのみ Web Push を配信します。", + "reenable": "再登録", + "relay_desc": "デフォルトはホストされた Bulwark リレーです。セルフホストする場合のみ変更してください。", + "relay_label": "プッシュリレー", + "relay_locked": "管理者が設定", + "relay_locked_desc": "プッシュリレーの URL は管理者によって設定されており、変更できません。", + "status_active": "このデバイスで有効", + "status_busy": "処理中…", + "status_inactive": "このデバイスでは有効になっていません", + "status_unsupported": "このブラウザは Web Push をサポートしていません", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "バックグラウンド通知", + "devices_title": "登録済みのデバイス", + "devices_desc": "このアカウントのバックグラウンド通知を受け取るよう登録されているデバイスの一覧です。取り消すと、そのデバイスはすぐに受信を停止します。", + "devices_loading": "登録済みのデバイスを読み込んでいます…", + "devices_empty": "バックグラウンド通知に登録されたデバイスはありません。", + "devices_error": "登録済みのデバイスを読み込めませんでした。", + "devices_refresh": "更新", + "device_this": "このデバイス", + "device_other": "別のデバイス", + "device_status_active": "通知を配信中", + "device_status_inactive": "通知を配信したことがありません", + "device_status_unknown": "このリレーでは不明", + "device_expires": "有効期限 {date}", + "revoke": "取り消す", + "confirm_revoke_title": "このデバイスを取り消しますか?", + "confirm_revoke_message": "すぐにバックグラウンド通知の受信を停止します。そのデバイスでプッシュを再度有効にすると、あらためて登録されます。", + "confirm_revoke_message_this": "このデバイスはすぐにバックグラウンド通知の受信を停止します。" + } + }, + "language_region": { + "title": "言語、地域、時刻", + "description": "言語、日付形式、時刻形式、その他の地域設定", + "language": { + "label": "言語", + "description": "お好みの言語を選択", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "日付形式", + "description": "メール一覧での日付の表示方法", + "smart": "スマート", + "relative": "相対表示", + "full": "常に完全な日付", + "preview_today": "今日:", + "preview_this_week": "今週:", + "preview_older": "それ以前:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "時刻形式", + "description": "12時間制または24時間制を選択", + "12h": "12時間制", + "24h": "24時間制" + }, + "first_day": { + "label": "週の開始日", + "description": "日曜日または月曜日から開始", + "sunday": "日曜日", + "monday": "月曜日" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "フォルダー切り替え時に検索をクリア", + "description": "フォルダー(受信トレイ、送信済みなど)をクリックすると、有効な検索をクリアしてそのフォルダーを表示します。オフの場合、検索は保持され、切り替え先のフォルダーで再実行されます。" + }, + "request_read_receipt": { + "label": "既定で開封確認を要求する", + "description": "新しいメッセージの作成時に、開封確認の要求をあらかじめ有効にします。" + }, + "read_receipt_response": { + "label": "開封確認の要求に応答する", + "description": "受信メッセージが開封確認を求めたときの動作。", + "ask": "毎回確認する", + "always": "常に送信", + "never": "送信しない" + }, + "title": "メール動作", + "description": "メールの処理方法を設定", + "mark_read": { + "label": "既読にする", + "description": "メールを開いたときに既読にするタイミング", + "instant": "即座", + "delay_3s": "3秒後", + "delay_5s": "5秒後", + "never": "既読にしない" + }, + "delete_action": { + "label": "削除動作", + "description": "メール削除時の動作", + "trash": "ゴミ箱に移動", + "trash_and_read": "ゴミ箱に移動して既読にする", + "permanent": "完全に削除", + "warning": "メールは完全に削除され、復元できません。この操作は元に戻せません。" + }, + "message_spacing": { + "label": "メッセージの余白", + "description": "閲覧時のメッセージ本文まわりの余白", + "auto": "自動(推奨)", + "always": "常に余白を追加", + "edge": "余白なし" + }, + "plain_text_font": { + "label": "プレーンテキストのフォント", + "description": "プレーンテキストメールに使用するフォント", + "mono": "等幅", + "sans": "標準(アプリと同じ)" + }, + "archive_mode": { + "label": "アーカイブ先", + "description": "アーカイブ時のメール整理方法", + "single": "単一フォルダ", + "year": "年ごとのフォルダ", + "month": "月ごとのフォルダ", + "reorganize": "既存アーカイブを再整理", + "reorganize_success": "{count, plural, =0 {再整理するメールはありません} other {#通のメールを再整理しました}}", + "reorganize_error": "アーカイブの再整理に失敗しました" + }, + "permanently_delete_junk": { + "label": "迷惑メールを完全に削除", + "description": "迷惑メールフォルダのメールをゴミ箱に移動せずに完全に削除する" + }, + "confirm_bulk_delete": { + "label": "複数のメッセージを削除する前に確認する", + "description": "選択した複数のメッセージをゴミ箱に移動する前に確認します。完全に削除する場合は常に確認します。" + }, + "mail_layout": { + "label": "メールレイアウト", + "description": "クラシックな分割読み取りペインとGmailスタイルの集中読み取りフローから選択します。", + "split": "分割ペイン", + "split_description": "メッセージリストと読み取りペインを並べて表示します。", + "focus": "集中リスト", + "focus_description": "メッセージごとに1行表示し、フォルダサイドバーを表示したままメールを全幅で開きます。", + "horizontal": "閲覧ペインを下に表示", + "horizontal_description": "メッセージ一覧を上に表示し、選択したメッセージを下の閲覧ペインで開きます。" + }, + "list_columns": { + "label": "リストを列で表示", + "description": "1行表示の行を、見出しの下に差出人・件名・日付・サイズの列を持つ表として表示します。" + }, + "disable_threading": { + "label": "会話グループ化を無効にする", + "description": "メールを会話ごとにグループ化せず、個別のメッセージとして表示します" + }, + "plain_text_mode": { + "label": "プレーンテキストのみ", + "description": "リッチテキストエディターを無効にし、返信や転送を含むすべてのメールをプレーンテキストのみで送信します" + }, + "auto_select_reply_identity": { + "label": "返信元アドレスを自動選択", + "description": "返信時に、元のメッセージを受信したIDへ差出人アドレスを自動的に切り替えます" + }, + "reply_identity_match": { + "label": "受信アドレスの照合", + "description": "どの受信アドレスを自分のものとみなすか。「完全一致のみ」は設定済みのアイデンティティからのみ選択します。「同じドメイン」はアイデンティティのドメイン上の他のアドレスもキャッチオールのエイリアスとして扱い、From ヘッダーをそのアドレスに書き換えます。それらのアドレスが配布リストの場合は完全一致を選択してください。", + "exact": "完全一致のみ", + "domain": "自分のドメイン上の任意のアドレス" + }, + "signature_position": { + "label": "署名の位置", + "description": "返信や転送で署名を挿入する位置。引用テキストの上は返信の締めとして自然に読めます。下は元のメッセージを続けて表示します。", + "above_quote": "引用テキストの前", + "below_quote": "引用テキストの後" + }, + "signature_separator": { + "label": "署名区切り", + "description": "署名の前に標準の区切り行「-- 」(RFC 3676)を付けます。本文から署名へ直接続けたい場合はオフにしてください。" + }, + "sub_address_delimiter": { + "label": "サブアドレス区切り文字", + "description": "ユーザー名とサブアドレスタグを区切る文字です。お使いのメールサーバーが使用する区切り文字に合わせてください(例: user{delimiter}tag@domain.com)。", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "カスタム…", + "custom_input_label": "カスタム区切り文字" + }, + "show_preview": { + "label": "プレビューテキストを表示", + "description": "リストにメールのプレビューを表示", + "focus_description": "集中型1行メッセージリスト内にインラインプレビューテキストを表示" + }, + "show_verification_codes": { + "label": "確認コードを表示", + "description": "サインインや確認のメールに含まれるコードをワンクリックでコピーできるようにします(一覧では1日間、メッセージ内では常に)" + }, + "attachment_click_action": { + "label": "添付ファイルクリック時の動作", + "description": "添付ファイルをクリックしたときに、プレビューを開くかすぐにダウンロードするかを選択します", + "preview": "可能ならプレビューを開く", + "download": "すぐにダウンロード" + }, + "attachment_position": { + "label": "添付ファイルの位置", + "description": "メールヘッダー内での添付ファイルの表示位置", + "beside-sender": "送信者の横", + "below-header": "ヘッダーの下" + }, + "emails_per_page": { + "10": "10件", + "25": "25件", + "50": "50件", + "100": "100件", + "label": "ページごとのメール数", + "description": "一度に読み込むメールの数" + }, + "always_light_mode": { + "label": "メールを常にライトモードで表示", + "description": "アプリがダークモードの場合でもメールをライトモードで表示し、ダークモード変換の問題を回避します" + }, + "external_content": { + "label": "外部コンテンツ", + "description": "画像と外部コンテンツの処理方法", + "ask": "常に確認", + "block": "常にブロック", + "allow": "常に許可" + }, + "trusted_senders": { + "label": "信頼する送信者", + "description": "画像が自動的に読み込まれる送信者を管理", + "count_zero": "なし", + "count_one": "1件", + "count_other": "{count}件", + "modal_title": "信頼する送信者", + "empty_title": "信頼する送信者はまだありません", + "empty_description": "ブロックされた画像を含むメールを表示するときに、「常にこの送信者を信頼する」をクリックしてここに追加します。", + "add_manually": "送信者を手動で追加", + "add_button": "追加", + "add_placeholder": "メールアドレスを入力", + "search_placeholder": "送信者を検索...", + "no_results": "検索に一致する送信者がありません", + "remove": "削除", + "close": "閉じる", + "invalid_email": "有効なメールアドレスを入力してください", + "already_added": "この送信者はすでに信頼されています", + "save_error": "保存に失敗しました - 詳細は連絡先デバッグログを確認してください", + "use_address_book_label": "アドレス帳と同期", + "use_address_book_description": "信頼済み送信者を専用の「信頼済み送信者」アドレス帳に保存し、すべてのデバイスで同期します" + }, + "hover_actions": { + "label": "ホバークイックアクション", + "description": "リスト内のメールにカーソルを合わせたときに表示するクイックアクションを選択", + "delete": "削除", + "star": "スター付け / 解除", + "mark_read": "既読 / 未読にする", + "archive": "アーカイブ", + "tag": "ラベル", + "spam": "スパムとしてマーク", + "not_spam": "迷惑メールではない", + "none_selected": "アクションが選択されていません", + "mode_label": "表示モード", + "mode_inline": "インライン", + "mode_floating": "フローティング", + "corner_label": "フローティング位置", + "corner_top-left": "左上", + "corner_top-right": "右上", + "corner_bottom-left": "左下", + "corner_bottom-right": "右下" + }, + "default_mail_program": { + "label": "既定のメールプログラム", + "description": "{appName}をmailto:リンクの既定のメールプログラムとして登録します", + "button": "既定に設定", + "success": "ブラウザに既定として設定するよう要求しました", + "error": "お使いのブラウザはこの機能をサポートしていません" + }, + "attachment_reminder": { + "label": "添付ファイルリマインダー", + "description": "メッセージに添付ファイルの言及があるのに添付されていない場合、送信前に警告します", + "keywords_label": "トリガーキーワード", + "keywords_description": "メッセージ内で見つかるとリマインダーを起動する単語やフレーズ", + "add_placeholder": "キーワードを追加...", + "add": "Add", + "remove": "削除" + }, + "empty_subject_warning": { + "label": "件名なしの警告", + "description": "件名のないメッセージを送信する前に確認します。" + }, + "recipient_mentions": { + "label": "@で宛先をメンション", + "description": "本文で@を入力すると宛先の候補が表示され、選んだ相手の名前が挿入されます。EnterまたはTabで強調表示された名前を選びます。" + }, + "mention_adds_recipient": { + "label": "@ メンションに連絡先を含める", + "description": "@ を入力すると、連絡先やディレクトリのユーザーも候補に表示されます。まだ宛先にない人を選ぶと、メッセージに追加されます。", + "to": "宛先に追加", + "cc": "CC に追加", + "off": "宛先のみ表示" + }, + "hide_inline_image_attachments": { + "label": "添付ファイル一覧からインライン画像を隠す", + "description": "本文に埋め込まれた画像を個別の添付ファイルとして表示しません" + }, + "attachment_image_previews": { + "label": "添付ファイル内の画像プレビューを表示", + "description": "画像の添付ファイルを汎用ファイルアイコンではなくサムネイルカードとして表示します" + }, + "send_delay": { + "label": "送信取り消し / 送信遅延", + "description": "通常の送信をサーバー側で短時間遅延します。", + "off": "オフ", + "seconds": "{seconds} 秒", + "unsupported": "現在のアカウントは遅延送信のサポートを通知していません。この設定は他のアカウント用に保存されます。" + }, + "return_to_list_after_action": { + "label": "削除または未読にした後に一覧へ戻る", + "description": "開いているメッセージを削除または未読にした後、次のメッセージを開かずにメッセージ一覧へ戻ります。" + }, + "swipe_right_action": { + "label": "右スワイプの操作(モバイル)", + "description": "タッチデバイスで、リスト内のメッセージを右にスワイプしたときに実行される操作です。" + }, + "swipe_left_action": { + "label": "左スワイプの操作(モバイル)", + "description": "タッチデバイスで、リスト内のメッセージを左にスワイプしたときに実行される操作です。" + }, + "swipe_actions": { + "none": "なし", + "archive": "アーカイブ", + "delete": "削除", + "mark_read": "既読/未読を切り替え", + "star": "スターを切り替え", + "spam": "迷惑メールに設定" + }, + "rtl_editing": { + "label": "右から左への編集サポート", + "description": "エディターのツールバーに文字方向ボタンを追加し、段落を左から右または右から左に設定できます" + } + }, + "composer": { + "title": "作成", + "description": "メール作成の設定を構成", + "autosave": { + "label": "自動保存間隔", + "description": "下書きを自動保存する頻度", + "30s": "30秒ごと", + "1m": "1分ごと", + "2m": "2分ごと", + "5m": "5分ごと" + }, + "send_confirmation": { + "label": "送信確認", + "description": "メール送信前に確認を求める" + }, + "default_reply": { + "label": "デフォルトの返信モード", + "description": "返信をクリックしたときのデフォルト動作", + "reply": "返信", + "reply_all": "全員に返信" + } + }, + "privacy": { + "title": "プライバシーとセキュリティ", + "description": "プライバシーとセキュリティ設定を管理", + "external_images": { + "label": "外部画像をブロック", + "description": "外部画像による追跡を防止" + }, + "session_timeout": { + "label": "セッションタイムアウト", + "description": "非アクティブ時に自動的にログアウト", + "never": "なし", + "30m": "30分", + "1h": "1時間", + "4h": "4時間" + }, + "clear_cache": { + "label": "キャッシュをクリア", + "description": "キャッシュデータと一時ファイルを削除", + "button": "キャッシュをクリア", + "confirm": "キャッシュをクリアしてもよろしいですか?", + "success": "キャッシュをクリアしました" + } + }, + "account": { + "title": "アカウント", + "description": "アカウント情報を表示", + "name_label": "表示名", + "username_label": "ユーザー名", + "account_type_label": "アカウントタイプ", + "auth_method_label": "認証方法", + "auth_method_oauth": "シングルサインオン (OAuth/OIDC)", + "auth_method_basic": "パスワード", + "auth_method_token": "アクセストークン", + "demo_account": "デモアカウント", + "email": { + "label": "メールアドレス", + "value": "{email}" + }, + "server": { + "label": "JMAPサーバー", + "value": "{server}" + }, + "storage": { + "label": "ストレージ使用量", + "used": "{total}中{used}使用", + "percentage": "{percent}%使用" + }, + "last_sync": { + "label": "最終同期", + "value": "{time}" + }, + "accounts": { + "title": "ログイン中のアカウント", + "description": "ドラッグしてアカウントメニューに表示される順序を変更", + "active": "現在アクティブなアカウント", + "default_badge": "デフォルトアカウント", + "set_default": "デフォルトに設定", + "switch_to": "このアカウントに切り替え", + "move_up": "上に移動", + "move_down": "下に移動", + "drag_handle": "ドラッグして並べ替え", + "add": "アカウントを追加" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "自分と共有", + "description": "管理できるグループアカウントと共有アカウントです。1つ選択して、フィルター、不在応答、カレンダー、連絡先を編集します。", + "shared_label": "共有アカウント" + } + }, + "security": { + "title": "アカウントセキュリティ", + "description": "パスワード、二要素認証、セキュリティ設定を管理します", + "detecting": "サーバー機能を検出中...", + "not_available": "このメールサーバーではアカウントセキュリティ管理は利用できません。必要な権限が無効になっている可能性があります。詳細はドキュメントを参照してください。", + "password": { + "title": "パスワードの変更", + "current": "現在のパスワード", + "new": "新しいパスワード", + "confirm": "新しいパスワードの確認", + "submit": "パスワードを変更", + "success": "パスワードが正常に変更されました", + "error_title": "パスワードの変更に失敗しました", + "error_mismatch": "新しいパスワードが一致しません", + "error_min_length": "パスワードは8文字以上である必要があります", + "error_generic": "パスワードを変更できませんでした" + }, + "display_name": { + "label": "表示名", + "description": "サーバー上に表示される名前", + "placeholder": "表示名を入力", + "save": "保存", + "success": "表示名が更新されました", + "error": "表示名を更新できませんでした" + }, + "totp": { + "section_title": "二要素認証", + "label": "TOTP認証", + "description": "時間ベースのワンタイムパスワードで追加のセキュリティレイヤーを追加", + "active": "有効", + "inactive": "無効", + "enabled": "二要素認証が有効になりました", + "disabled": "二要素認証が無効になりました", + "enable_error": "2FAを有効にできませんでした", + "disable_error": "2FAを無効にできませんでした", + "setup_instructions": "このURLを認証アプリ(Google Authenticator、Authyなど)にコピーしてください:", + "verification_code": "確認コード", + "confirm": "確認", + "disable": "無効化", + "disable_confirm_prompt": "二要素認証を無効にするにはパスワードを入力してください。", + "password_required": "パスワードが必要です", + "code_required": "確認コードが必要です", + "code_invalid": "確認コードが無効です。認証アプリを確認してもう一度お試しください。" + }, + "app_passwords": { + "title": "アプリパスワード", + "description": "二要素認証に対応していないアプリ用のパスワードを作成", + "add": "追加", + "create": "作成", + "cancel": "キャンセル", + "generate": "生成", + "name_label": "アプリ名", + "name_placeholder": "例:Thunderbird、iPhone Mail", + "password_label": "パスワード(空欄で自動生成)", + "password_placeholder": "空欄の場合自動生成", + "added": "アプリパスワードが作成されました", + "removed": "アプリパスワードが削除されました", + "add_error": "アプリパスワードを作成できませんでした", + "remove_error": "アプリパスワードを削除できませんでした", + "none": "アプリパスワードは設定されていません", + "done": "完了", + "expires_label": "有効期限(任意)", + "copy_now_warning": "今すぐこのパスワードをコピーしてください - 再表示されません。", + "allowed_ips_label": "許可するIP(任意)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "カンマまたは空白で区切ります。空欄の場合、すべてのIPを許可します。" + }, + "api_keys": { + "title": "APIキー", + "description": "サーバーと直接通信するスクリプトや連携用にAPIキーを作成します", + "name_label": "キー名", + "name_placeholder": "例: バックアップスクリプト、CIランナー", + "copy_now_warning": "このAPIキーを今すぐコピーしてください - 二度と表示されません。", + "added": "APIキーを作成しました", + "removed": "APIキーを削除しました", + "add_error": "APIキーの作成に失敗しました", + "remove_error": "APIキーの削除に失敗しました", + "none": "APIキーは設定されていません" + }, + "encryption": { + "section_title": "保存時の暗号化", + "label": "メール暗号化", + "description": "サーバー上の保存メールを暗号化してプライバシーを強化", + "active": "{type}暗号化が有効", + "inactive": "無効", + "enabled": "保存時の暗号化が有効になりました", + "disabled_success": "保存時の暗号化が無効になりました", + "error": "暗号化設定を更新できませんでした", + "algorithm_label": "暗号化アルゴリズム", + "encrypt_on_append": "アップロード時に新しいメールを暗号化", + "allow_spam_training": "メールを暗号化する前に迷惑メール学習を許可", + "enable_encryption": "暗号化を有効にする", + "enable_tooltip": "この鍵で暗号化を有効にする", + "configure_title": "この鍵で暗号化を有効にする", + "enable_button": "有効にする", + "disable_tooltip": "暗号化を無効にする", + "enabled_success": "保存時の暗号化が有効になりました" + }, + "email_client": { + "title": "メールクライアントの設定", + "description": "これらの資格情報を使用して、デスクトップまたはモバイルのメールクライアント(Thunderbird、Apple Mail、Outlookなど)を設定します", + "jmap_username_label": "JMAPユーザー名", + "copy": "コピー", + "copied": "コピー済み", + "password_instructions": "上記のJMAPユーザー名とアプリパスワードを使用してメールクライアントにサインインします。まだ作成していない場合は、上のセクションでアプリパスワードを作成してください。" + }, + "link_device": { + "title": "モバイルアプリを連携", + "description": "スマートフォンの Bulwark Mail アプリに、そこでパスワードを入力せずにサインインできます。スマートフォンには独自のサインインが発行されるため、ここでサインアウトしてもスマートフォンはサインアウトされません。", + "generate": "QRコードを表示", + "regenerate": "新しいコードを表示", + "instructions": "Bulwark Mail アプリを開き、サインイン画面で「Scan a sign-in code」をタップして(すでにサインインしている場合は「Add account」→「Scan a sign-in code」)、カメラをここに向けてください。", + "expires_in": "このコードは{seconds}秒で期限切れになります。一度しか使用できません。", + "expired": "このコードは期限切れです。", + "error": "ペアリングコードを作成できませんでした。もう一度お試しください。", + "password_prompt": "本人確認のためパスワードを入力してください。スマートフォンには独自のサインインが発行され、パスワードがスマートフォンに送信されることはありません。", + "show_password": "パスワードを表示", + "hide_password": "パスワードを隠す", + "continue": "続行", + "error_wrong_password": "パスワードが正しくありません。もう一度お試しください。", + "totp_prompt": "認証アプリに表示されているコードを入力してください。", + "error_password_or_code": "パスワードまたはコードが正しくありません。", + "error_not_signed_in": "セッションの有効期限が切れました。デバイスを連携するには、もう一度サインインしてください。", + "error_impersonation": "他のユーザーのメールボックスを表示している間は、デバイスを連携できません。", + "error_too_many_attempts": "試行回数が多すぎます。しばらく待ってからもう一度お試しください。", + "error_session_secret": "このサーバーではデバイス連携が設定されていません(SESSION_SECRET がありません)。管理者にお問い合わせください。", + "error_server_unreachable": "メールサーバーに接続できませんでした。もう一度お試しください。", + "error_pairing_unavailable": "お使いのメールサーバーでは、この方法でデバイスを連携できません。管理者にお問い合わせください。", + "qr_alt": "サインイン用 QR コード", + "link_label": "サインインリンク", + "copy_link": "サインインリンクをコピー", + "copied": "コピーしました", + "copy_hint": "カメラがない場合は、リンクをコピーしてアプリの「Paste sign-in link」から貼り付けてください。", + "success_title": "スマートフォンでサインインしました", + "success_body": "このページは閉じてかまいません。別のデバイスを連携するには、新しいコードを表示してください。", + "link_another": "別のデバイスを連携", + "use_sso": "代わりにシングルサインオンを使う", + "error_insecure_server": "モバイルアプリは https のメールサーバーにしか接続できませんが、このサーバーは http を使っています。" + }, + "public_keys": { + "name_label": "公開鍵の名前", + "name_placeholder": "例:マイキー", + "key_label": "公開鍵(S/MIME または PGP の ASCII 形式)", + "key_placeholder": "ここに公開鍵を貼り付けてください", + "none": "公開鍵が設定されていません" + } + }, + "identities": { + "title": "送信者情報", + "description": "送信に使用するメールアドレスを管理", + "identities_count": { + "label": "送信者情報", + "description": "送信用に設定されたメールアドレス", + "count_zero": "送信者情報なし", + "count_one": "1件", + "count_other": "{count}件" + }, + "manage": "送信者情報を管理", + "sub_addressing": { + "label": "サブアドレス", + "description": "user+tag@domain.comのようなタグを使用して受信メールを整理", + "learn_more": "詳細" + } + }, + "vacation": { + "title": "不在", + "description": "不在時に受信メールを自動で処理します", + "loading": "不在応答設定を読み込み中...", + "not_supported": "メールサーバーは不在応答をサポートしていません。", + "fetch_error": "不在応答設定の読み込みに失敗しました。もう一度お試しください。", + "status": { + "label": "自動で返信する", + "description": "メールを送信してきた方に自動返信を送信します", + "active": "有効", + "inactive": "無効" + }, + "date_range": { + "start": "開始日", + "start_description": "空白のままにすると開始制限なし", + "end": "終了日", + "end_description": "空白のままにすると終了制限なし" + }, + "message": { + "subject_label": "件名", + "subject_description": "自動返信の件名", + "subject_placeholder": "不在通知", + "body_label": "メッセージ本文", + "body_description": "テキスト形式のメッセージ内容", + "body_placeholder": "メールをいただきありがとうございます。現在不在にしており、戻り次第ご返信いたします。", + "html_label": "書式付きメッセージ(HTML)", + "html_description": "リンクやスタイルを含む書式付きの版を追加します。表示できないメールソフトの受信者には上記のプレーンテキストが表示されます。", + "html_placeholder": "書式付きの不在返信を入力…" + }, + "preview": { + "title": "プレビュー", + "show": "プレビューを表示", + "hide": "プレビューを非表示" + }, + "save": "変更を保存", + "saving": "保存中...", + "warnings": { + "end_before_start": "終了日は開始日より後に設定してください", + "invalid_date": "開始日と終了日は時刻付きの有効な日付を入力するか、欄を空のままにしてください", + "start_in_past": "開始日が過去の日付です", + "empty_body": "メッセージ本文が空です - 受信者には空の返信が届きます", + "subject_too_long": "件名がサーバーの上限を超えています(最大 {max} バイト)。", + "body_too_long": "メッセージがサーバーの上限を超えています(書式を含めて最大 {max} バイト)。", + "forward_address": "転送先に有効なアドレスを入力してください", + "not_running": "転送または返信する相手は保存されていますが、現在は有効になっていません。保存すると再び有効になります。" + }, + "forward": { + "title": "転送", + "description": "受信したメッセージを、不在応答の有無にかかわらず、上の期間内に別のアドレスへ転送します(終了日がない場合はオフにするまで)。迷惑メールは転送されません。", + "enabled_label": "メッセージを転送する", + "to_label": "転送先", + "to_placeholder": "email@example.com", + "keep_label": "受信トレイに保持", + "keep_description": "オフの場合、メッセージは転送されるだけで、このメールボックスには残らず、フィルタールールも適用されません。" + }, + "audience": { + "label": "返信する相手", + "description": "内部の送信者: {domains}", + "all": "すべての送信者", + "internal": "内部の送信者のみ", + "external": "外部の送信者のみ" + }, + "auto_reply": { + "title": "自動応答" + } + }, + "folders": { + "role_memos": "メモ", + "title": "フォルダー", + "description": "メールフォルダーを管理し、標準の役割を割り当てます", + "folder_list": "フォルダー一覧", + "folder_list_description": "フォルダーアイコンをクリックしてカスタマイズ", + "standard_roles": "標準フォルダーの役割", + "standard_roles_description": "受信トレイ、送信済み、ゴミ箱などの標準の役割に使用するフォルダーを設定します。", + "role_inbox": "受信トレイ", + "role_drafts": "下書き", + "role_sent": "送信済み", + "role_trash": "ゴミ箱", + "role_junk": "迷惑メール", + "role_archive": "アーカイブ", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "なし", + "create_folder": "フォルダーを作成", + "create_subfolder": "サブフォルダーを作成", + "reorder": "ドラッグして並べ替え", + "reorder_error": "フォルダーの並べ替えに失敗しました", + "move_error": "フォルダーの移動に失敗しました", + "select_folder": "{name} を選択", + "select_hint": "選択。Shift+クリックで最後にクリックしたフォルダーまでをすべて選択します。", + "selected_count": "{count, plural, other {# 個のフォルダー}}を選択中", + "move_to": "移動先...", + "move_to_top_level": "最上位", + "move": "移動", + "clear_selection": "選択を解除", + "folders_moved": "{count, plural, other {# 個のフォルダーを移動しました}}", + "move_partial_error": "{count, plural, other {# 個のフォルダー}}を移動できませんでした", + "subfolder_of": "{name} の中", + "subfolder_name": "サブフォルダー名", + "new_folder_name": "フォルダー名", + "rename": "名前を変更", + "change_icon": "アイコンを変更", + "count_mode": "{name} のカウンター", + "count_mode_default": "カウンター: 既定", + "count_mode_unread": "未読のみ", + "count_mode_all": "未読と合計", + "count_mode_none": "カウンターなし", + "delete": "削除", + "confirm_delete": "「{name}」を削除してもよろしいですか?このフォルダー内のメールはゴミ箱に移動されます。", + "create": "作成", + "cancel": "キャンセル", + "no_folders": "カスタムフォルダーはありません", + "cannot_delete_role": "標準の役割が割り当てられたフォルダーは削除できません。先に役割を解除してください。", + "folder_created": "フォルダーを作成しました", + "folder_renamed": "フォルダー名を変更しました", + "folder_deleted": "フォルダーを削除しました", + "role_updated": "フォルダーの役割を更新しました", + "error_create": "フォルダーの作成に失敗しました", + "error_rename": "フォルダー名の変更に失敗しました", + "error_delete": "フォルダーの削除に失敗しました", + "error_delete_has_children": "フォルダーを削除できません:サブフォルダーが含まれています。先にサブフォルダーを削除または移動してください。", + "error_delete_has_email": "フォルダーを削除できません:メールが含まれています。先にメールを移動または削除してください。", + "error_role": "フォルダーの役割の更新に失敗しました" + }, + "advanced": { + "title": "詳細設定", + "description": "詳細オプションと開発者設定", + "debug_mode": { + "label": "デバッグモード", + "description": "トラブルシューティング用の詳細ログを有効化" + }, + "debug_categories": { + "description": "記録するカテゴリを選択してください。不要なカテゴリを無効にすると、コンソールのノイズを減らせます。", + "jmap": "JMAPクライアント", + "jmap_description": "メールボックス操作、メール取得、JMAPプロトコル要求", + "calendar": "カレンダー", + "calendar_description": "カレンダーイベント、インポート、スケジュールメッセージ", + "tasks": "タスク", + "tasks_description": "カレンダータスクの作成、取得、更新", + "auth": "認証", + "auth_description": "ログイン、TOTP、トークン交換、セッション管理", + "filters": "フィルター", + "filters_description": "Sieveフィルタールールと休暇スクリプト", + "email": "メール表示", + "email_description": "メールのレンダリング、TNEF処理、既読化", + "push": "プッシュ通知", + "push_description": "プッシュ通知の設定と配信", + "contacts": "連絡先とアドレス帳", + "contacts_description": "連絡先同期、アドレス帳操作、信頼済み送信者" + }, + "settings_sync": { + "label": "設定の同期", + "description": "ブラウザーやデバイス間で設定を同期します" + }, + "sender_favicons": { + "label": "送信者ファビコン", + "description": "ビジネス送信者のプロフィール画像としてウェブサイトアイコンを表示", + "refresh_label": "送信者アイコンを更新", + "refresh_description": "送信者がアイコンを変更した場合などに、ウェブサイトのアイコンを再取得します", + "refresh_button": "更新", + "refresh_done": "送信者アイコンを再取得します" + }, + "show_avatars_in_junk": { + "label": "迷惑メールフォルダでアバターを表示", + "description": "迷惑メールフォルダの送信者にプロフィール画像とファビコンを表示します。フィッシング詐欺に正規のような見た目を与えないため、既定では無効です。" + }, + "keyboard_shortcuts": { + "label": "キーボードショートカット", + "description": "利用可能なキーボードショートカットを表示", + "button": "ショートカットを表示" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "設定をリセット", + "description": "すべての設定をデフォルト値に戻す", + "button": "デフォルトにリセット" + }, + "export_settings": { + "label": "設定をエクスポート", + "description": "設定をJSONとしてダウンロード", + "button": "エクスポート" + }, + "import_settings": { + "label": "設定をインポート", + "description": "JSONファイルから設定をアップロード", + "button": "インポート" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "ソースコード" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "スパムやフィッシングが受信トレイに届く前にクリックしてブロックしましょう。本物のメールはそのまま通してください。盾を3つすべて失うと受信トレイは陥落します。", + "kinds": { + "spam": "迷惑メール", + "phishing": "フィッシング", + "legit": "本物のメール" + }, + "click_to_block": "クリックでブロック", + "let_through": "そのまま通す", + "start": "スタート", + "play_again": "もう一度", + "score": "スコア", + "combo": "連続ボーナス", + "shields_left": "盾 残り{count}/{total}", + "seconds": "{seconds}秒", + "best": "ベスト: {score}", + "new_best": "ベスト更新", + "held": "受信トレイを守り抜いた", + "overrun": "受信トレイが陥落", + "stats": { + "caught": "ブロック", + "delivered": "配信", + "mistakes": "ミス" + } + } + }, + "sidebar_apps": { + "title": "サイドバーアプリ", + "description": "サイドバーのカスタムアプリとリンクを管理", + "keep_loaded": "アプリを読み込んだまま保持", + "keep_loaded_description": "切り替え時にインラインアプリをバックグラウンドで実行し続けて再読み込みを回避", + "managed_title": "管理者によって提供", + "managed_description": "これらのアプリは全員向けに設定されており、変更できません。", + "manage_title": "カスタムアプリ", + "manage_description": "サイドバーのカスタムアプリを追加、編集、削除" + }, + "contacts": { + "title": "連絡先", + "description": "連絡先のインポートとエクスポート", + "import_label": "連絡先をインポート", + "import_description": "vCardファイル(.vcf)から連絡先をインポート", + "export_label": "連絡先をエクスポート", + "export_description": "すべての連絡先をvCardファイル(.vcf)としてエクスポート", + "manage_title": "アドレス帳", + "manage_description": "アドレス帳の名前を変更", + "no_address_books": "アドレス帳が見つかりません", + "categories_title": "カテゴリ", + "categories_description": "連絡先カテゴリの名前を変更", + "no_categories": "カテゴリが見つかりません", + "group_by_letter_description": "連絡先リストにアルファベット順のセクション見出しを表示", + "group_by_letter_label": "頭文字でグループ化", + "sort_by_last_name_label": "姓で並べ替え", + "sort_by_last_name_description": "家族が並んで表示されるように、連絡先一覧を姓の順に並べ替えます" + }, + "downloads": { + "title": "ダウンロード", + "description": "ダウンロードしたメールや添付ファイルの名前の付け方をカスタマイズします。", + "reset": "既定に戻す", + "preview": "プレビュー:", + "email_template": { + "label": "メールのファイル名 (.eml)", + "description": "メールをエクスポートするか、ファイルシステムにドラッグしたときに使用するテンプレートです。.eml 拡張子は自動的に付加されます。" + }, + "attachment_template": { + "label": "添付ファイルのファイル名", + "description": "添付ファイルをダウンロードまたはドラッグするときに使用するテンプレートです。'{filename}' と '{ext}' を省略すると、元の拡張子が保持されます。" + }, + "bundle_template": { + "label": "複数メールの .zip ファイル名", + "description": "複数の選択したメールを 1 つの .zip としてドラッグまたはダウンロードするときに使用するテンプレートです。拡張子 .zip は自動的に付加されます。" + }, + "spaces": { + "label": "スペース", + "description": "最終的なファイル名のスペースを別の文字に置き換えます。", + "keep": "スペースを残す", + "underscore": "_ に置換", + "dash": "- に置換" + }, + "lowercase": { + "label": "小文字", + "description": "ファイル名全体を小文字に強制します。" + }, + "strip_diacritics": { + "label": "ダイアクリティカルマークを除去", + "description": "アクセント付き文字を ASCII の同等の文字に変換します (ä → a, é → e)。Unicode のファイル名を正しく扱えないツールで便利です。" + }, + "collapse_separators": { + "label": "連続した区切り文字をまとめる", + "description": "連続するスペース、アンダースコア、ハイフンを 1 文字にまとめます。" + }, + "after_export": { + "label": "エクスポート後", + "description": "メールを .eml としてエクスポートした後、必要に応じて移動します。", + "keep": "メールボックスに残す", + "archive": "アーカイブへ移動", + "trash": "ごみ箱へ移動" + } + }, + "filters": { + "title": "メールフィルター", + "description": "受信メールを自動で振り分け、ラベル付け、管理するルールを作成します", + "add_rule": "ルールを追加", + "no_rules": "フィルタールールなし", + "vacation_active": "不在応答が有効です", + "vacation_active_description": "受信メッセージへの自動返信が有効になっています", + "vacation_configure": "設定", + "no_rules_description": "受信メールを自動で整理するルールを作成してください", + "edit_rule": "ルールを編集", + "new_rule": "新しいルール", + "delete_rule": "ルールを削除", + "delete_confirm": "このルールを削除してもよろしいですか?", + "enable": "有効にする", + "disable": "無効にする", + "raw_editor": "Sieveスクリプトエディター", + "raw_editor_warning": "Sieveスクリプトを直接編集すると、ビジュアルルール編集が使えなくなる場合があります。ここでの変更はビジュアルビルダーを上書きします。", + "validate": "検証", + "validation_success": "スクリプトは有効です", + "validation_error": "スクリプトにエラーがあります", + "save": "ルールを保存", + "saving": "保存中...", + "saved": "フィルターを保存しました", + "save_failed": "フィルターの保存に失敗しました", + "loading": "フィルターを読み込み中...", + "not_supported": "お使いのメールサーバーはメールフィルターに対応していません。", + "rule_name": "ルール名", + "rule_name_placeholder": "例:ニュースレターを振り分け", + "match_all": "すべての条件に一致", + "match_any": "いずれかの条件に一致", + "conditions": "条件", + "add_condition": "条件を追加", + "actions": "アクション", + "add_action": "アクションを追加", + "stop_processing": "以降のルールの処理を停止", + "include_spam": "迷惑メールと判定されたメッセージも移動する", + "attachment_type_placeholder": "例: pdf, doc, jpg", + "value_placeholder_multi": "値(カンマで複数指定可)", + "condition_fields": { + "attachment": "添付ファイル", + "from": "差出人", + "to": "宛先", + "cc": "Cc", + "subject": "件名", + "header": "カスタムヘッダー", + "size": "サイズ", + "body": "本文", + "all": "すべてのメール" + }, + "comparators": { + "has_any": "あり", + "has_type": "タイプ", + "contains": "を含む", + "not_contains": "を含まない", + "is": "と完全一致", + "not_is": "ではない", + "starts_with": "で始まる", + "ends_with": "で終わる", + "matches": "パターンに一致", + "greater_than": "より大きい", + "less_than": "より小さい", + "address_is": "のアドレスが次と一致", + "domain_is": "のドメインが次と一致" + }, + "action_types": { + "move": "フォルダーに移動", + "copy": "フォルダーにコピー", + "forward": "転送先", + "mark_read": "既読にする", + "star": "スターを付ける", + "add_label": "ラベルを追加", + "discard": "サイレント削除", + "reject": "メッセージ付きで拒否", + "keep": "受信トレイに保持", + "stop": "処理を停止" + }, + "move_to_folder": "フォルダーを選択", + "copy_to_folder": "フォルダーを選択", + "forward_to": "転送先メールアドレス", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "コピーを残す", + "forward_limit": "このサーバーでの1通あたりの転送上限: {count}。それを超える転送はスキップされます。", + "forward_unavailable": "このサーバーでは転送が許可されていません。フィルタールールの転送アクションは機能しません。", + "reject_message": "拒否メッセージ", + "reject_placeholder": "あなたのメールは拒否されました", + "label_name": "ラベル名", + "label_placeholder": "ラベルを選択", + "header_name": "ヘッダー名", + "header_placeholder": "例:X-Mailing-List", + "size_bytes": "サイズ(バイト)", + "size_placeholder": "例:1000000", + "system_managed": "システム管理ルール", + "opaque_warning": "このスクリプトはビジュアルビルダーの外部で編集されました。Sieveスクリプトの直接編集のみ可能です。", + "open_sieve_editor": "Sieveスクリプトエディタを開く", + "fetch_error": "フィルターの読み込みに失敗しました", + "expanded_view": "詳細表示", + "expanded_view_description": "フィルタールールを条件とアクションのブロックで表示", + "if": "条件", + "then": "実行", + "match_all_conditions": "すべて一致", + "match_any_condition": "いずれか一致", + "and": "かつ", + "or": "または", + "cancel": "キャンセル", + "confirm_delete": "削除", + "rule_list": "フィルタールール", + "drag_to_reorder": "ドラッグして並べ替え", + "match_type": "一致タイプ", + "reset_to_visual": "ビジュアルビルダーにリセット", + "reset_warning": "現在のスクリプトを破棄して最初からやり直します。", + "confirm_reset": "リセット", + "validation_empty_name": "ルール名は必須です", + "validation_empty_conditions": "値を持つ条件が少なくとも1つ必要です", + "validation_empty_actions": "少なくとも1つのアクションが必要です", + "templates_section": "テンプレートから作成", + "template_newsletters": "ニュースレターをフォルダに移動", + "template_receipts": "領収書を自動アーカイブ", + "template_important": "重要なメールにフラグを付ける", + "template_notifications": "通知をフィルター", + "sieve_editor": { + "title": "Sieveスクリプトエディタ", + "warning": "Sieveスクリプトを直接編集すると、ビジュアルルール編集が使用できなくなる場合があります。ここでの変更はビジュアルビルダーを上書きします。", + "script_content": "Sieveスクリプト", + "valid": "スクリプトは有効です", + "invalid": "スクリプトにエラーがあります", + "save_warning": "保存するとすべてのビジュアルルールが上書きされます。この操作は元に戻せません。もう一度「保存」をクリックして確認してください。", + "validating": "検証中...", + "validate": "検証", + "cancel": "キャンセル", + "save": "保存", + "confirm_save": "保存を確認", + "validation_failed": "検証リクエストに失敗しました" + }, + "rule_summary": { + "conditions_count": "{count, plural, other {#個の条件}}", + "actions_count": "{count, plural, other {#個のアクション}}" + }, + "origin_external": "外部", + "managed_by_tooltip": "{source} によって管理されています。そのアプリで編集するか、生の Sieve エディターを使用してください。", + "quick_rule_names": { + "move_sender": "{sender} から {folder} へ移動", + "move_domain": "@{domain} から {folder} へ移動", + "move_list": "リスト {list} を {folder} へ移動", + "mark_read": "{sender} からのメールを既読にする", + "tag": "{sender} からのメールにラベル {tag}", + "block": "{sender} をブロック", + "custom": "{sender} からのメール", + "custom_plain": "新しいルール", + "senders": "{count, plural, other {送信者 # 人}}" + }, + "suggestions": "候補", + "suggest_subject": "件名「{subject}」", + "suggest_to": "宛先 {address}", + "suggest_cc": "CC {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "このフォルダにある既存のメールにも適用", + "apply_existing_unsupported": "既存のメールに適用できるのは、差出人・宛先・件名・ヘッダーを条件とし、移動・コピー・既読・スター・ラベルを実行するルールだけです。", + "period_title": "期間", + "period_start": "開始日", + "period_end": "終了日", + "validation_period_order": "終了日は開始日より後に設定してください", + "period_toggle": "期間を限定して有効にする", + "period_hint": "この期間に届いたメッセージに適用されます。空欄にすると、その側は制限なしになります。", + "validation_period_empty": "期間の開始日または終了日を入力してください", + "validation_period_invalid": "期間には時刻付きの有効な日付を入力するか、欄を空のままにしてください", + "apply_existing_period": "期間付きのルールは期間内に届いたメッセージにのみ適用されるため、既存のメッセージには適用できません。", + "period_status_scheduled": "予定", + "period_status_active": "有効", + "period_status_expired": "期限切れ" + }, + "templates": { + "title": "メールテンプレート", + "description": "プレースホルダー変数付きの再利用可能なメールテンプレートを作成", + "add": "新規テンプレート", + "edit": "テンプレートを編集", + "name": "テンプレート名", + "name_placeholder": "例:フォローアップメール", + "category": "カテゴリー", + "category_placeholder": "例:仕事、個人", + "subject": "件名", + "subject_placeholder": "メールの件名", + "body": "本文", + "body_placeholder": "メール本文...", + "recipients_placeholder": "email@example.com", + "identity": "送信者", + "default_identity": "デフォルトのID", + "favorite": "お気に入り", + "cancel": "キャンセル", + "create": "作成", + "update": "更新", + "confirm_delete": "削除", + "no_templates": "テンプレートがありません", + "manage": "テンプレートを管理", + "count": "{count}件のテンプレート", + "export_import": "エクスポートとインポート", + "export_import_description": "テンプレートのバックアップや別のデバイスへの転送", + "export": "エクスポート", + "import": "インポート", + "validation": { + "empty": "テンプレート名は必須です", + "too_long": "テンプレート名は200文字以内にしてください" + } + }, + "files": { + "display": { + "title": "表示", + "description": "ファイルとフォルダーの表示方法を設定します" + }, + "default_view": { + "label": "デフォルトビュー", + "description": "グリッドまたはリストレイアウトを選択します", + "list": "リスト", + "grid": "グリッド" + }, + "default_sort": { + "label": "デフォルトの並べ替え", + "description": "ファイルのデフォルトの並べ替えを選択します", + "name": "名前", + "size": "サイズ", + "modified": "更新日" + }, + "sort_direction": { + "label": "並べ替え方向", + "description": "昇順または降順を選択します", + "ascending": "昇順", + "descending": "降順" + }, + "icons": { + "title": "アイコン", + "description": "ファイルアイコンの外観を設定します" + }, + "show_icons": { + "label": "ファイルアイコンを表示", + "description": "ファイルとフォルダーの横にアイコンを表示します" + }, + "colored_icons": { + "label": "カラーアイコン", + "description": "モノクロの代わりにカラフルなアイコンを使用します" + }, + "show_thumbnails": { + "label": "サムネイルを表示", + "description": "画像ファイルのアイコンの代わりにプレビューを表示します" + }, + "behavior": { + "title": "動作", + "description": "ファイルブラウザーの動作を設定します" + }, + "show_hidden": { + "label": "隠しファイルを表示", + "description": "ドットで始まるファイルとフォルダーを表示します" + }, + "preview": { + "label": "プレビュー" + } + }, + "search_placeholder": "設定を検索", + "search_clear": "検索をクリア", + "search_no_results": "一致する設定はありません", + "scoped": { + "back": "自分のアカウントに戻る", + "managing": "管理中: {name}" + } + }, + "errors": { + "page_error_title": "問題が発生しました", + "page_error_description": "予期しないエラーが発生しました。もう一度お試しいただくか、ホームページに戻ってください。", + "sidebar_error": "メールボックスを読み込めません", + "email_list_error": "メールを読み込めません", + "viewer_error_title": "メールを表示できません", + "viewer_error_description": "このメールのレンダリングに問題が発生しました。サポートされていないコンテンツが含まれている可能性があります。", + "composer_error": "作成画面を読み込めません", + "settings_error_title": "設定を利用できません", + "settings_error_description": "設定を読み込めません。設定が保存されない可能性があります。", + "try_again": "再試行", + "reload": "再読み込み", + "reload_emails": "メールを再読み込み", + "reload_settings": "設定を再読み込み", + "retry": "リトライ", + "go_home": "受信トレイに移動" + }, + "context_menu": { + "reply": "返信", + "reply_all": "全員に返信", + "forward": "転送", + "mark_read": "既読にする", + "mark_unread": "未読にする", + "star": "スターを付ける", + "unstar": "スターを外す", + "pin": "ピン留め", + "unpin": "ピン留めを外す", + "move_to": "移動...", + "copy_to_account": "アカウントにコピー...", + "remove_from_folder": "このフォルダから削除", + "archive": "アーカイブ", + "delete": "削除", + "mark_as_spam": "迷惑メールを報告", + "not_spam": "迷惑メールでない", + "tag": "ラベル", + "items_selected": "{count}件のメールを選択", + "forward_as_attachments": "添付ファイルとして転送", + "edit_draft": "下書きを編集", + "cancel_scheduled_send": "送信をキャンセル", + "reschedule_send": "再予約", + "cancel_and_edit": "キャンセルして編集", + "cancel_and_compose_again": "キャンセルして新規作成", + "rules": { + "title": "ルール", + "move_from": "{sender} からのメールを常に移動", + "move_from_domain": "@{domain} からのメールを常に移動", + "move_from_list": "このリストのメールを常に移動", + "mark_read_from": "{sender} からのメールを常に既読にする", + "tag_with": "常にラベルを付ける", + "block": "{count, plural, =1 {送信者をブロック} other {送信者 # 人をブロック}}", + "no_junk": "このアカウントには迷惑メールフォルダがありません", + "create": "ルールを作成…", + "manage": "ルールを管理", + "senders_count": "{count, plural, other {送信者 # 人}}", + "cross_account": "1 つのアカウントのメールを選択してください", + "opaque_hint": "フィルターは手動で編集されています。フィルター設定を開いてください" , + "loading": "ルールを読み込み中…", + "load_failed": "ルールを読み込めませんでした", + "none": "ルールはまだありません", + "new_mail_only": "新着メッセージのみ", + "running": "{folder} で「{name}」を実行中…" + }, + "find_emails_from": "{name} からのメールを検索" + }, + "mailbox_context_menu": { + "mark_folder_read": "フォルダーを既読にする", + "mark_folder_tree_read": "フォルダーとサブフォルダーを既読にする", + "mark_all_folders_read": "すべてのフォルダーを既読にする", + "new_subfolder": "新しいサブフォルダー...", + "new_folder": "新しいフォルダー...", + "rename": "名前を変更...", + "share": "共有...", + "import_email": ".eml または .zip をインポート...", + "empty_folder": "フォルダーを空にする", + "empty_folder_generic": "フォルダーを空にする", + "delete_folder": "フォルダーを削除", + "refresh": "更新", + "hide_shared_account": "サイドバーから非表示", + "mute_notifications": "通知をミュート", + "unmute_notifications": "通知のミュートを解除", + "mute_account_notifications": "このアカウントの通知をミュート", + "unmute_account_notifications": "このアカウントの通知のミュートを解除", + "reset_folder_notifications": "フォルダーの通知設定をリセット", + "mark_all_confirm_title": "すべてのフォルダーを既読にする", + "mark_all_confirm_message": "個人アカウントのすべての未読メッセージを既読にしますか?", + "delete_confirm_title": "フォルダーを削除", + "delete_confirm_message": "フォルダー \"{name}\" を完全に削除しますか?この操作は元に戻せません。", + "prompt_new_subfolder": "新しいサブフォルダーの名前を入力してください。", + "prompt_new_folder": "新しいフォルダーの名前を入力してください。", + "prompt_rename": "このフォルダーの新しい名前を入力してください。", + "toast_marked_read": "フォルダーを既読にしました", + "toast_marked_read_count": "{count, plural, one {1件のメッセージ} other {#件のメッセージ}}を既読にしました", + "toast_already_read": "未読メッセージはありません", + "toast_marked_all_read": "すべてのフォルダーを既読にしました", + "toast_emptied": "フォルダーを空にしました", + "toast_folder_created": "フォルダーを作成しました", + "toast_folder_renamed": "フォルダー名を変更しました", + "toast_folder_deleted": "フォルダーを削除しました", + "toast_error_mark_read": "既読にできませんでした", + "toast_error_empty": "フォルダーを空にできませんでした", + "toast_error_create": "フォルダーを作成できませんでした", + "toast_error_rename": "フォルダー名を変更できませんでした", + "toast_error_delete": "フォルダーを削除できませんでした", + "toast_error_delete_has_children": "フォルダーにサブフォルダーがあります。先に削除してください。", + "toast_error_delete_has_email": "フォルダーが空ではありません。先に空にしてください。", + "delete_with_contents_title": "フォルダーが空ではありません", + "delete_with_contents_message": "フォルダー「{name}」にはまだ {count} 件のメッセージがあります。フォルダーとすべてのメッセージを削除しますか?この操作は元に戻せません。", + "delete_with_contents_confirm": "フォルダーとメッセージを削除", + "placeholder_folder_name": "フォルダー名", + "create": "作成", + "rename_confirm": "名前を変更" + }, + "shortcuts": { + "title": "キーボードショートカット", + "tip": "? キーを押すといつでもこのヘルプを表示できます", + "sections": { + "navigation": "ナビゲーション", + "actions": "メール操作", + "global": "全般", + "threads": "スレッド", + "composer": "作成" + }, + "navigation": { + "next_email": "次のメール", + "previous_email": "前のメール", + "open_email": "メールを開く", + "close_email": "閉じる / 選択解除", + "go_to_folder": "フォルダーに移動", + "next_unread": "次の未読メール", + "no_more_unread": "リストのこれより下に未読メールはありません" + }, + "actions": { + "reply": "返信", + "reply_all": "全員に返信", + "forward": "転送", + "star": "スターの切り替え", + "archive": "アーカイブ", + "delete": "削除", + "mark_unread": "未読にする", + "mark_read": "既読にする", + "toggle_spam": "迷惑メールを報告 / 迷惑メールでない" + }, + "global": { + "compose": "新規メールを作成", + "search": "検索にフォーカス", + "help": "ショートカットを表示", + "refresh": "メールを更新", + "select_all": "すべて選択", + "global_search": "すべてを検索", + "extend_selection": "選択範囲を下 / 上に広げる" + }, + "threads": { + "expand_collapse": "スレッドの展開/折りたたみ", + "select_mountain_view": "スレッドを選択" + }, + "composer": { + "send": "メールを送信", + "schedule_send": "送信を予約", + "template_picker": "テンプレートピッカーを開く" + } + }, + "threads": { + "messages_one": "{count}件のメッセージ", + "messages_other": "{count}件のメッセージ", + "messages_tooltip": "{count, plural, other {この会話の#件のメッセージ}}", + "expand": "会話を展開", + "collapse": "会話を折りたたむ", + "loading": "会話を読み込み中...", + "mark_read": "会話を既読にする", + "mark_unread": "会話を未読にする", + "archive": "会話をアーカイブ", + "delete": "会話を削除", + "star": "会話にスターを付ける", + "unstar": "会話のスターを外す", + "toggle_thread": "スレッドの展開/折りたたみ" + }, + "identities": { + "modal_title": "送信者情報を管理", + "create_new": "新しい送信者情報を作成", + "edit_identity": "送信者情報を編集", + "delete_confirm": "この送信者情報を削除しますか?この操作は元に戻せません。", + "cannot_delete": "この送信者情報は削除できません", + "primary_identity": "プライマリ", + "set_as_primary": "プライマリに設定", + "no_identities": "送信者情報が見つかりません", + "display": { + "reply_to": "返信先:", + "bcc": "BCC:", + "signature": "署名:", + "preview": "プレビュー:" + }, + "validation_errors": { + "invalid_emails": "無効なメールアドレス: {emails}", + "unknown_error": "不明なエラー" + }, + "form": { + "name_label": "表示名", + "name_placeholder": "例: 仕事用、個人用", + "name_required": "名前は必須です", + "email_label": "メールアドレス", + "email_placeholder": "your.email@example.com", + "email_required": "メールアドレスは必須です", + "email_invalid": "有効なメールアドレスを入力してください", + "email_immutable": "メールアドレスは作成後に変更できません", + "reply_to_label": "返信先(オプション)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "自動BCC(オプション)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "テキスト署名", + "html_signature_label": "HTML署名", + "signature_byte_counter": "{bytes} / {max} バイト", + "signature_byte_limit_reached": "サーバーの上限に達しました", + "save": "送信者情報を保存", + "cancel": "キャンセル", + "creating": "作成中...", + "updating": "更新中..." + }, + "sub_address": { + "button_tooltip": "サブアドレスを使用", + "popover_title": "サブアドレスタグを追加", + "tag_input_placeholder": "タグを入力(例: shopping)", + "preview_label": "プレビュー:", + "recent_tags": "最近のタグ", + "suggested_tags": "候補", + "use_address": "このアドレスを使用", + "invalid_tag": "タグは英数字とハイフンのみ使用できます", + "tag_too_long": "タグは30文字以内にしてください", + "help_text": "user{delimiter}tag@domain.comに送信されたメールは受信トレイに届きます", + "validation": { + "empty": "タグは空にできません", + "too_long": "タグは{max}文字以内にしてください", + "invalid_chars": "タグは文字、数字、ハイフンのみ使用できます" + } + }, + "badge": { + "sent_via": "経由", + "sub_address_tag": "サブアドレスを使用して送信: {tag}", + "identity_name": "送信者情報を使用して送信: {name}", + "identity_short": "{name}経由", + "subaddress_tag": "+{tag}" + }, + "delete_button": "削除", + "delete_confirm_title": "IDの削除" + }, + "templates": { + "picker_title": "テンプレートを選択", + "search_placeholder": "テンプレートを検索...", + "section_favorites": "お気に入り", + "section_recent": "最近使用", + "section_uncategorized": "その他", + "no_templates": "テンプレートがありません", + "no_results": "テンプレートが見つかりません", + "fill_placeholders": "プレースホルダーの値を入力", + "enter_value": "値を入力...", + "preview": "プレビュー", + "insert_with_values": "値を挿入", + "insert_raw": "そのまま挿入", + "copy_suffix": "(コピー)", + "placeholder": "変数", + "placeholders": { + "recipient_name": "宛先の名前", + "company": "会社名", + "date": "今日の日付", + "day_of_week": "曜日", + "sender_name": "あなたの名前" + } + }, + "contacts": { + "directory": { + "title": "ディレクトリ", + "search_placeholder": "名前またはメールアドレスで検索", + "refresh": "ディレクトリを更新", + "retry": "再試行", + "loading": "ディレクトリを読み込み中…", + "unavailable_title": "ディレクトリがありません", + "unavailable_hint": "メールサーバーはアカウントのディレクトリを提供していません。", + "forbidden_title": "ディレクトリを利用できません", + "forbidden_hint": "メールサーバーがこのアカウントにディレクトリの閲覧を許可していません。管理者にお問い合わせください。", + "error_title": "ディレクトリを読み込めませんでした", + "error_hint": "接続を確認して、もう一度お試しください。", + "empty_title": "ディレクトリは空です", + "empty_hint": "メールサーバーにメールアドレスを持つアカウントがありません。", + "no_match": "検索に一致する人はディレクトリにいません", + "refresh_failed": "ディレクトリを更新できませんでした。最後に読み込んだ一覧を表示しています。", + "select_page": "このページの全員を選択", + "deselect_page": "このページの選択を解除", + "select_entry": "{name} を選択", + "range": "{total} 件中 {from}–{to}", + "page": "{count} ページ中 {page} ページ", + "previous_page": "前のページ", + "next_page": "次のページ", + "picker_title": "ディレクトリから選択", + "cancel": "キャンセル", + "selected_count": "{count} 件選択", + "add_to": "宛先に追加", + "add_cc": "CC に追加", + "add_bcc": "BCC に追加", + "select_hint": "詳細を表示するには相手を選択してください", + "write_email": "メールを作成", + "add_to_contacts": "連絡先に追加", + "email": "メール", + "read_only_note": "ディレクトリの項目はメールサーバーから取得され、ここでは編集できません。", + "type": { + "individual": "個人", + "group": "グループ", + "resource": "リソース", + "location": "場所", + "other": "その他" + } + }, + "title": "連絡先", + "search_placeholder": "連絡先を検索...", + "create_new": "新しい連絡先", + "no_category": "カテゴリなし", + "rename_category": "カテゴリの名前を変更", + "category_name_label": "カテゴリ名", + "category_renamed": "カテゴリの名前を変更しました", + "category_rename_failed": "カテゴリの名前変更に失敗しました", + "category_added": "{name} に連絡先を追加しました", + "category_added_plural": "{count} 件の連絡先を {name} に追加しました", + "empty_state": "連絡先がありません", + "empty_state_title": "連絡先がありません", + "empty_state_subtitle": "最初の連絡先を作成するか、vCardファイルからインポートしてください", + "empty_search": "検索に一致する連絡先がありません", + "empty_search_hint": "別の検索語をお試しください", + "empty_filtered": "フィルターに一致する連絡先がありません", + "empty_filtered_hint": "フィルターを調整またはクリアしてください", + "clear_search": "検索をクリア", + "import_vcard": "vCardをインポート", + "delete_confirm_title": "連絡先を削除", + "delete_confirm": "この連絡先を削除してもよろしいですか?", + "local_mode": "連絡先はローカルに保存されています(サーバーがJMAPコンタクトをサポートしていません)", + "back_to_contacts": "連絡先に戻る", + "back_to_email": "メールに戻る", + "tabs": { + "all": "すべて", + "groups": "グループ" + }, + "shared": { + "title": "共有" + }, + "address_books": { + "create": "新しいアドレス帳", + "created": "アドレス帳を作成しました", + "create_failed": "アドレス帳の作成に失敗しました", + "title": "マイアドレス帳", + "shared_prefix": "共有: {name}", + "moved": "連絡先を {name} に移動しました", + "moved_plural": "{count} 件の連絡先を {name} に移動しました", + "move_failed": "連絡先の移動に失敗しました", + "address_book": "アドレス帳", + "rename": "アドレス帳の名前を変更", + "name_label": "アドレス帳名", + "renamed": "アドレス帳の名前を変更しました", + "rename_failed": "アドレス帳の名前変更に失敗しました", + "default": "デフォルト", + "set_default": "デフォルトに設定", + "default_updated": "デフォルトのアドレス帳を更新しました", + "set_default_failed": "デフォルトのアドレス帳を設定できませんでした", + "manage": "アドレス帳を管理", + "share": "アドレス帳を共有", + "new_contact_in_book": "このアドレス帳に新規連絡先", + "delete": "アドレス帳を削除", + "confirm_delete": "「{name}」を削除しますか?このアドレス帳のすべての連絡先が削除されます。", + "deleted": "アドレス帳を削除しました", + "delete_failed": "アドレス帳の削除に失敗しました" + }, + "detail": { + "emails": "メールアドレス", + "phones": "電話番号", + "organizations": "組織", + "addresses": "住所", + "notes": "メモ", + "titles": "肩書きと役割", + "online_services": "オンラインサービス", + "anniversaries": "記念日", + "personal_info": "個人情報", + "languages": "言語", + "categories": "カテゴリー", + "related_contacts": "関連する連絡先", + "crypto_keys": "暗号鍵", + "no_contact_selected": "連絡先を選択して詳細を表示", + "compose_email": "メールを作成", + "copy_email": "メールをコピー", + "copy_phone": "電話番号をコピー", + "copy_url": "URLをコピー", + "copied": "クリップボードにコピーしました", + "copy_failed": "クリップボードへのコピーに失敗しました", + "created": "作成日", + "updated": "最終更新", + "timezone": "タイムゾーン", + "anniversary_birth": "誕生日", + "anniversary_death": "命日", + "anniversary_wedding": "結婚記念日", + "anniversary_other": "その他", + "personal_expertise": "専門分野", + "personal_hobby": "趣味", + "personal_interest": "興味", + "personal_other": "その他", + "gender": "性別", + "gender_masculine": "男性", + "gender_feminine": "女性", + "gender_other": "その他", + "gender_none": "該当なし", + "gender_unknown": "不明", + "calendar": "カレンダー", + "calendar_uri": "カレンダーURL", + "scheduling_uri": "スケジュールURL", + "freebusy_uri": "空き状況URL", + "cert_issuer": "発行者", + "cert_expires": "有効期限", + "cert_expired": "期限切れ", + "cert_fingerprint": "フィンガープリント", + "cert_algorithm": "アルゴリズム", + "import_to_smime": "S/MIME にインポート", + "cert_already_imported": "証明書はすでにインポートされています", + "cert_imported": "証明書をインポートしました", + "cert_import_failed": "証明書のインポートに失敗しました", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "その他の操作", + "age_years": "{count}歳", + "years_since": "{count}年前" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "新しい連絡先", + "edit_title": "連絡先を編集", + "section_address_book": "ディレクトリ", + "select_address_book": "ディレクトリを選択...", + "section_identity": "名前と識別情報", + "contact_type": "連絡先の種類", + "type_person": "個人", + "type_organization": "組織", + "section_work": "職業と組織", + "prefix": "敬称", + "prefix_placeholder": "博士、氏", + "given_name": "名", + "middle_name": "ミドルネーム", + "surname": "姓", + "suffix": "接尾辞", + "suffix_placeholder": "Jr.、Sr.、III", + "nickname": "ニックネーム", + "nickname_placeholder": "ニックネーム", + "email": "メール", + "email_placeholder": "email@example.com", + "phone": "電話", + "phone_placeholder": "+81 3 1234 5678", + "phone_type": "種類", + "phone_voice": "音声", + "phone_cell": "携帯", + "phone_fax": "FAX", + "phone_pager": "ポケベル", + "phone_video": "ビデオ", + "phone_text": "テキスト", + "organization": "組織", + "organization_placeholder": "会社名", + "department": "部署", + "department_placeholder": "部署", + "job_title": "役職", + "job_title_placeholder": "例:ソフトウェアエンジニア", + "role": "役割", + "role_placeholder": "例:チームリーダー", + "addresses": "住所", + "add_address": "住所を追加", + "street": "番地", + "city": "市区町村", + "region": "都道府県", + "postcode": "郵便番号", + "country": "国", + "online_services": "オンラインサービス", + "add_online_service": "オンラインサービスを追加", + "url_placeholder": "https://...", + "service_placeholder": "サービス", + "anniversaries": "記念日", + "add_anniversary": "日付を追加", + "anniversary_birth": "誕生日", + "anniversary_wedding": "結婚記念日", + "anniversary_death": "追悼日", + "anniversary_other": "その他", + "personal_info": "個人情報", + "add_personal_info": "項目を追加", + "personal_info_placeholder": "例:写真撮影", + "personal_expertise": "専門分野", + "personal_hobby": "趣味", + "personal_interest": "興味", + "personal_other": "その他", + "level": "レベル", + "level_high": "高", + "level_medium": "中", + "level_low": "低", + "categories": "カテゴリー", + "categories_placeholder": "例:家族、友人、同僚", + "categories_hint": "検索または追加するには入力", + "category_add": "追加", + "note": "メモ", + "note_placeholder": "メモを追加...", + "gender": "性別", + "gender_sex": "生物学的性別", + "gender_male": "男性", + "gender_female": "女性", + "gender_other": "その他", + "gender_none": "該当なし", + "gender_unknown": "不明", + "gender_identity": "性自認", + "gender_identity_placeholder": "性自認...", + "calendar": "カレンダー", + "calendar_uri": "カレンダーURL", + "scheduling_uri": "スケジュールURL", + "freebusy_uri": "空き状況URL", + "context_work": "仕事", + "context_private": "プライベート", + "add_email": "メール追加", + "add_phone": "電話追加", + "save": "保存", + "cancel": "キャンセル", + "creating": "作成中...", + "updating": "更新中...", + "name_required": "名、姓、または組織を入力してください", + "email_invalid": "有効なメールアドレスを入力してください", + "email_error_inline": "メールアドレスの形式が正しくありません", + "save_failed": "連絡先の保存に失敗しました", + "delete": "削除", + "upload_photo": "写真をアップロード", + "remove_photo": "写真を削除", + "photo_hint": "JPGまたはPNG、最大10MB。リサイズされます。", + "photo_too_large": "画像が大きすぎます (最大10MB)", + "photo_invalid": "無効な画像ファイル", + "change_photo": "変更" + }, + "groups": { + "create": "新しいグループ", + "edit": "グループを編集", + "empty": "グループがありません", + "delete_confirm_title": "グループを削除", + "delete_confirm": "このグループを削除してもよろしいですか?", + "name_label": "グループ名", + "name_placeholder": "例:チーム、家族", + "name_required": "グループ名は必須です", + "save_failed": "グループの保存に失敗しました", + "members_label": "メンバー", + "search_members": "追加する連絡先を検索...", + "no_members": "このグループにメンバーがいません", + "member_count": "{count, plural, =0 {メンバーなし} other {#人のメンバー}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "連絡先をインポート", + "drop_hint": "vCardファイルを選択してください", + "file_types": ".vcf または .vcard ファイル", + "no_contacts": "ファイルに連絡先が見つかりません", + "parse_error": "vCardファイルの解析に失敗しました", + "found": "{count, plural, other {#件の連絡先が見つかりました}}", + "duplicate": "重複", + "select_all": "すべて選択", + "deselect_all": "すべて解除", + "selected": "{count, plural, other {#件選択中}}", + "import_button": "インポート", + "importing": "インポート中...", + "success": "{count, plural, other {#件の連絡先をインポートしました}}", + "failed": "インポートに失敗しました", + "close": "閉じる", + "file_too_large": "ファイルが大きすぎます(最大5 MB)" + }, + "export": { + "title": "連絡先をエクスポート", + "success": "{count, plural, other {#件の連絡先をエクスポートしました}}" + }, + "bulk": { + "selected": "{count, plural, other {#件選択中}}", + "select_all": "すべて選択", + "delete": "削除", + "delete_confirm_title": "連絡先を削除", + "delete_confirm": "{count, plural, other {#件の連絡先}}を削除しますか?", + "deleted": "{count, plural, other {#件の連絡先を削除しました}}", + "add_to_group": "グループに追加", + "choose_group": "グループを選択", + "adding_contacts": "{count, plural, other {#件の連絡先}}を追加", + "added_to_group": "連絡先をグループに追加しました", + "export": "エクスポート", + "clear": "選択を解除" + }, + "toast": { + "created": "連絡先を作成しました", + "updated": "連絡先を更新しました", + "deleted": "連絡先を削除しました", + "error_create": "連絡先の作成に失敗しました", + "error_update": "連絡先の更新に失敗しました", + "error_delete": "連絡先の削除に失敗しました" + }, + "context_menu": { + "open": "開く", + "edit": "編集", + "send_email": "メール送信", + "add_to_group": "グループに追加", + "export_vcard": "vCardとしてエクスポート", + "delete": "削除", + "call": "電話する", + "duplicate": "複製", + "print": "印刷" + }, + "filters": { + "toggle": "フィルター", + "select": "選択", + "clear": "クリア", + "close": "閉じる", + "title": "詳細フィルター", + "organization": "会社", + "organization_placeholder": "例: Acme", + "job_title": "役職", + "job_title_placeholder": "例: デザイナー", + "location": "場所", + "location_placeholder": "都市または国", + "email_domain": "メールドメイン", + "email_domain_placeholder": "example.com", + "birthday_month": "誕生月", + "any_month": "すべての月", + "has_email": "メールあり", + "has_phone": "電話あり", + "has_photo": "写真あり", + "sort_by": "並べ替え", + "sort_first_name": "名", + "sort_last_name": "姓" + }, + "open_categories": "カテゴリを開く" + }, + "calendar": { + "title": "カレンダー", + "back_to_email": "メールに戻る", + "back_to_month": "月表示に戻る", + "my_calendars": "マイカレンダー", + "birthday_calendar": "誕生日", + "mini_calendar_change": "クリックで月を変更", + "views": { + "month": "月", + "week": "週", + "day": "日", + "agenda": "予定リスト", + "today": "今日", + "month_hint": "月 (m)", + "week_hint": "週 (w)", + "day_hint": "日 (d)", + "agenda_hint": "予定一覧 (a)", + "tasks": "タスク", + "tasks_hint": "タスク (k)" + }, + "events": { + "hidden_before": "{count, plural, other {これより前の予定 # 件}}", + "hidden_after": "{count, plural, other {これより後の予定 # 件}}", + "show_all_hours": "すべての時間を表示", + "show_display_hours": "{start}~{end} のみ表示", + "create": "予定を作成", + "edit": "予定を編集", + "delete": "予定を削除", + "details": "予定の詳細", + "no_events": "予定なし", + "all_day": "終日", + "more": "他{count}件", + "show_more": "展開する", + "show_less": "折りたたむ", + "no_title": "(タイトルなし)", + "resize": "イベントのサイズ変更", + "duplicate": "複製", + "today_header": "今日", + "tomorrow_header": "明日", + "agenda_show_earlier": "以前の予定を表示", + "agenda_loading": "読み込み中…", + "agenda_range_start": "{date}以降の予定", + "agenda_range_end": "{date}までの予定", + "export_ics": ".icsとしてエクスポート", + "copy_title": "タイトルをコピー", + "copy_link": "会議リンクをコピー", + "go_to_today": "今日に移動", + "new_all_day_event": "新しい終日イベント", + "new_event": "新しいイベント", + "new_task": "新しいタスク" + }, + "detail": { + "add_note": "メモを追加...", + "save_note": "保存", + "note_saved": "メモを追加しました", + "open_link": "リンクを開く", + "copy_location": "場所をコピー", + "meeting_link": "会議リンク", + "tentative": "仮", + "cancelled": "キャンセル済み", + "delete_confirm": "このイベントを削除しますか?" + }, + "form": { + "title": "タイトル", + "description": "説明", + "location": "場所", + "meeting_link": "会議リンク", + "start_date": "開始日", + "end_date": "終了日", + "start_time": "開始時刻", + "end_time": "終了時刻", + "all_day_event": "終日の予定", + "calendar_select": "カレンダー", + "save": "保存", + "cancel": "キャンセル", + "delete_confirm": "この予定を削除してもよろしいですか?", + "color": "色" + }, + "participants": { + "availability": { + "title": "空き状況", + "free": "空き", + "busy": "予定あり", + "tentative": "仮", + "unknown": "このサーバーのユーザーではありません", + "checking": "確認中…" + }, + "title": "参加者", + "add": "参加者を追加", + "organizer": "主催者", + "attendee": "参加者", + "accepted": "承諾", + "declined": "辞退", + "tentative": "仮承諾", + "needs_action": "未回答", + "remove": "削除", + "edit": "編集", + "email_placeholder": "メールアドレスを追加または連絡先を検索", + "send_invitations": "参加者に招待を送信", + "status_summary": "{accepted}人承諾、{pending}人保留", + "invited_by": "{name}からの招待", + "respond_below": "以下のボタンで返答してください", + "rsvp_label": "あなたの返答", + "cancel_notification": "キャンセルの通知が参加者に送信されます", + "you_organizer": "あなたは主催者です", + "you_attendee": "あなたは参加者です", + "no_participants": "参加者なし", + "count": "{count}人の参加者" + }, + "recurrence": { + "title": "繰り返し", + "none": "繰り返しなし", + "daily": "毎日", + "weekly": "毎週", + "monthly": "毎月", + "yearly": "毎年", + "every_n_days": "{count}日ごと", + "every_n_weeks": "{count}週間ごと", + "every_n_months": "{count}か月ごと", + "until": "終了日", + "occurrences": "{count}回", + "custom": "カスタム…", + "edit_custom": "カスタム繰り返しを編集", + "every_n_years": "{count}年ごと", + "on_days": "{days}", + "on_day_n": "{day}日", + "on_the_nth": "{nth}{day}", + "in_month": "{month}", + "nth_1": "第1", + "nth_2": "第2", + "nth_3": "第3", + "nth_4": "第4", + "nth_last": "最終", + "editor_freq_day": "日", + "editor_freq_week": "週", + "editor_freq_month": "月", + "editor_freq_year": "年", + "editor_repeats_on": "繰り返し", + "editor_every": "間隔:", + "editor_unit_days": "日ごと", + "editor_unit_weeks": "週間ごと", + "editor_unit_months": "か月ごと", + "editor_unit_years": "年ごと", + "editor_on_day": "日付:", + "editor_on_the": "曜日:", + "editor_in": "月:", + "editor_ends": "終了", + "editor_never": "なし", + "editor_ends_on": "終了日", + "editor_ends_after": "回数", + "editor_occurrences": "回" + }, + "recurrence_scope": { + "edit_title": "繰り返しイベントを編集", + "delete_title": "繰り返しイベントを削除", + "description": "これは繰り返しイベントです。どのイベントを変更しますか?", + "this_event": "このイベントのみ", + "this_and_future": "これ以降のすべてのイベント", + "all_events": "すべてのイベント", + "cancel": "キャンセル", + "save": "保存", + "delete": "削除", + "rsvp_title": "繰り返しイベントに返信", + "rsvp_description": "これは繰り返しイベントです。どのイベントに返信しますか?", + "respond": "返信" + }, + "alerts": { + "title": "リマインダー", + "none": "リマインダーなし", + "at_time": "予定の時刻", + "minutes_before": "{count}分前", + "hours_before": "{count}時間前", + "days_before": "{count}日前", + "weeks_before": "{count}週間前", + "unit_minutes_before": "分前", + "unit_hours_before": "時間前", + "unit_days_before": "日前", + "unit_weeks_before": "週間前", + "add": "リマインダーを追加", + "remove": "リマインダーを削除", + "amount": "リマインダー値", + "unit": "リマインダー単位", + "action": "リマインダーの種類", + "action_display": "通知", + "action_email": "メール", + "by_email": "{label}(メール)", + "kept": "ここでは編集できないリマインダーがあと {count} 件保持されます", + "other": "他 {count} 件のリマインダー", + "calendar_defaults": "カレンダーのデフォルト", + "customize": "カスタマイズ", + "use_defaults": "カレンダーのデフォルトを使用" + }, + "settings": { + "limit_hours": "表示する時間帯を制限", + "limit_hours_desc": "日表示と週表示でこの時間帯だけを表示します。範囲外の予定には各日の上下にある矢印からアクセスできます。", + "visible_hours": "表示する時間帯", + "visible_hours_start": "表示開始時刻", + "visible_hours_end": "表示終了時刻", + "hide_non_working_days": "休日を非表示", + "hide_non_working_days_desc": "勤務しない曜日を週表示から除外します。その日の予定は月表示・日表示・予定リストに引き続き表示されます。", + "working_days": "勤務日", + "title": "カレンダー設定", + "default_view": "デフォルトの表示", + "week_starts_on": "週の開始曜日", + "time_format": "時刻の形式", + "default_calendar": "デフォルトのカレンダー", + "default_reminder": "デフォルトのリマインダー", + "time_format_12h": "12時間制", + "time_format_24h": "24時間制", + "notifications_enabled": "イベント通知", + "notifications_enabled_desc": "予定のイベントのアラートを表示する", + "notification_sound": "通知音", + "notification_sound_desc": "カレンダーアラートの音を鳴らす", + "invitation_parsing": "メール招待を解析する", + "invitation_parsing_desc": "メール添付のカレンダー招待を検出してカレンダー操作を表示する", + "show_time_in_month_view": "月表示で時刻を表示", + "show_time_in_month_view_desc": "月カレンダー表示でイベントの時刻を表示する", + "show_week_numbers": "週番号を表示", + "show_week_numbers_desc": "ミニカレンダーに週番号を表示する", + "calendar_free_scroll": "自由スクロール", + "calendar_free_scroll_desc": "1つの期間ずつではなく、月・週・日を連続してスクロールします", + "enable_tasks": "タスクを有効化", + "enable_tasks_desc": "カレンダーにタスク表示を追加して、ToDoを管理します", + "show_tasks_on_calendar": "カレンダーにタスクを表示", + "show_tasks_on_calendar_desc": "月表示・週表示・日表示にタスクを表示します", + "hover_preview": "イベントホバープレビュー", + "hover_preview_desc": "カレンダーイベントにマウスを合わせたときに詳細ポップオーバーを表示", + "hover_preview_instant": "即時", + "hover_preview_delay_500ms": "0.5秒遅延", + "hover_preview_delay_1s": "1秒遅延", + "hover_preview_delay_2s": "2秒遅延", + "hover_preview_off": "無効", + "organizer_identity": "招待の主催者アドレス", + "organizer_identity_desc": "イベントに人を招待する際に主催者として使用するカレンダーアドレスです。", + "organizer_identity_failed": "主催者アドレスを変更できませんでした", + "show_birthday_calendar": "誕生日カレンダー", + "show_birthday_calendar_desc": "連絡先の誕生日を表示する仮想カレンダーを表示します" + }, + "days": { + "monday": "月曜日", + "tuesday": "火曜日", + "wednesday": "水曜日", + "thursday": "木曜日", + "friday": "金曜日", + "saturday": "土曜日", + "sunday": "日曜日", + "mon": "月", + "tue": "火", + "wed": "水", + "thu": "木", + "fri": "金", + "sat": "土", + "sun": "日" + }, + "notifications": { + "event_created": "予定を作成しました", + "event_updated": "予定を更新しました", + "event_deleted": "予定を削除しました", + "calendar_created": "カレンダーを作成しました", + "calendar_deleted": "カレンダーを削除しました", + "event_move_error": "イベントの移動に失敗しました", + "event_resize_error": "イベントのサイズ変更に失敗しました", + "alert_title": "予定のイベント", + "alert_now": "まもなく開始", + "alert_in_minutes": "{count}分後", + "invitation_sent": "招待を送信しました", + "invitations_denied_title": "招待状を送信できません", + "invitations_denied": "サーバーが招待状の送信を拒否しました: {reason}。招待状を送らずに予定を保存しますか?", + "save_without_invitations": "招待状なしで保存", + "rsvp_updated": "回答を更新しました", + "rsvp_error": "回答の更新に失敗しました", + "event_duplicated": "予定を複製しました", + "event_error": "予定の保存に失敗しました", + "task_due": "タスクの期限です", + "event_exported": "イベントをエクスポートしました", + "title_copied": "タイトルをコピーしました", + "link_copied": "リンクをコピーしました" + }, + "status": { + "loading_calendars": "カレンダーを読み込み中...", + "loading_events": "予定を読み込み中..." + }, + "quick_create": { + "placeholder": "新しい予定のタイトル", + "aria_label": "予定をすばやく作成", + "add_title": "タイトルを追加", + "more_options": "その他のオプション" + }, + "nav_prev": "前へ", + "nav_next": "次へ", + "import": { + "title": "カレンダーをインポート", + "tab_file": "ファイル", + "tab_url": "URL", + "select_file": ".icsファイルを選択", + "drop_file": "またはファイルをここにドロップ", + "supported_formats": "iCalendar (.ics) ファイルに対応", + "url_description": "外部のiCalendar (.ics) フィードのURLを入力してイベントをインポートします。", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "iCalendar (.ics) URLに対応", + "fetch": "取得", + "invalid_url": "有効なURLを入力してください", + "url_fetch_failed": "URLからカレンダーを取得できませんでした", + "parsing": "カレンダーファイルを解析中...", + "parsed_events": "{count}件のイベントが見つかりました", + "no_events": "ファイルにイベントが見つかりません", + "select_all": "すべて選択", + "deselect_all": "すべて解除", + "target_calendar": "インポート先のカレンダー", + "import_button": "選択をインポート", + "importing": "イベントをインポート中...", + "success": "{count}件のイベントをインポートしました", + "error": "カレンダーのインポートに失敗しました", + "file_too_large": "ファイルサイズが10MBを超えています", + "invalid_format": "無効なカレンダーファイル形式" + }, + "webcal_action": { + "title": "カレンダーリンクを開く", + "description": "\"{name}\"をどのように使用しますか?", + "import_title": "一度だけインポート", + "import_description": "今すぐ予定を取得し、いずれかのカレンダーにコピーします。", + "subscribe_title": "購読", + "subscribe_description": "このカレンダーを別のカレンダーとして自動的に同期します。", + "cancel": "キャンセル" + }, + "management": { + "title": "カレンダー管理", + "description": "カレンダーの作成、名前変更、カスタマイズができます。サイドバーのカレンダーを右クリックして色を素早く変更できます。", + "name": "名前", + "name_placeholder": "カレンダー名", + "color": "色", + "change_color": "色を変更", + "random_color": "新しいランダムな色", + "add_calendar": "カレンダーを追加", + "edit": "編集", + "delete": "削除", + "save": "保存", + "create": "作成", + "cancel": "キャンセル", + "default": "デフォルト", + "confirm_delete": "\"{name}\"を削除しますか?このカレンダーのすべてのイベントが削除されます。", + "calendar_created": "カレンダーを作成しました", + "calendar_updated": "カレンダーを更新しました", + "calendar_deleted": "カレンダーを削除しました", + "color_updated": "カレンダーの色を更新しました", + "error_create": "カレンダーの作成に失敗しました", + "error_update": "カレンダーの更新に失敗しました", + "error_delete": "カレンダーの削除に失敗しました", + "caldav_url": "CalDAV URL", + "copy_url": "CalDAV URLをコピー", + "url_copied": "CalDAV URLをクリップボードにコピーしました", + "confirm_clear": "\"{name}\"のすべてのイベントを削除しますか?この操作は元に戻せません。", + "set_default": "デフォルトに設定", + "default_updated": "デフォルトのカレンダーを更新しました", + "error_default": "デフォルトのカレンダーを設定できませんでした", + "clear_events": "イベントを削除", + "events_cleared": "{count}件のイベントを削除しました", + "error_clear": "カレンダーイベントの削除に失敗しました", + "share": "カレンダーを共有", + "new_event_in_calendar": "このカレンダーに新規イベント", + "shared_with": "{names} と共有", + "shared_with_count": "{count, plural, other {#人と共有}}", + "move_up": "上に移動", + "move_down": "下に移動", + "kind": "内容", + "kind_events": "イベント", + "kind_tasks": "タスク", + "kind_both": "イベントとタスク", + "kind_hint": "CalDAV で同期したときにこのカレンダーを表示できるアプリを決定します。後から変更することはできません。", + "default_alerts": "デフォルトのリマインダー", + "default_alerts_title": "「{name}」のデフォルトのリマインダー", + "default_alerts_hint": "このカレンダーの新しい予定には、予定内で変更しない限りこれらのリマインダーが設定されます。ここで変更しても既存の予定は変わりません。メールのリマインダーはメールサーバーから送信されます。", + "default_alerts_timed": "時刻のある予定", + "default_alerts_all_day": "終日の予定", + "default_alerts_updated": "デフォルトのリマインダーを更新しました" + }, + "subscription": { + "title": "iCal購読", + "section_title": "iCal購読", + "description": "外部のiCalendarフィードを購読します。イベントは自動的に専用のカレンダーに同期されます。https://およびwebcal://のURLに対応しています。", + "url_label": "カレンダーURL", + "url_placeholder": "https://example.com/calendar.ics または webcal://...", + "name_label": "カレンダー名", + "name_placeholder": "例:祝日", + "color_label": "色", + "refresh_interval": "更新間隔", + "interval_15": "15分ごと", + "interval_30": "30分ごと", + "interval_60": "1時間ごと", + "interval_360": "6時間ごと", + "interval_1440": "毎日", + "subscribe": "購読する", + "subscribing": "購読中...", + "save": "変更を保存", + "saving": "保存中...", + "edit": "編集", + "edit_title": "購読を編集", + "updated": "\"{name}\" を更新しました", + "update_error": "購読を更新できませんでした", + "invalid_url": "有効なURLを入力してください", + "success": "\"{name}\"を購読しました", + "error": "購読の追加に失敗しました", + "refresh": "今すぐ更新", + "refresh_success": "購読を更新しました", + "refresh_error": "購読の更新に失敗しました", + "unsubscribe": "購読解除", + "confirm_delete": "\"{name}\"の購読を解除しますか?カレンダーとすべてのイベントが削除されます。", + "deleted": "購読を解除しました", + "delete_error": "購読の解除に失敗しました", + "last_refreshed": "最終更新: {time}" + }, + "tasks": { + "label": "タスク", + "no_tasks": "タスクなし", + "no_title": "(タイトルなし)", + "mark_complete": "完了にする", + "mark_incomplete": "未完了にする", + "filter_all": "すべて", + "filter_pending": "未完了", + "filter_completed": "完了", + "filter_overdue": "期限切れ", + "show_completed": "完了済みを表示", + "create": "新しいタスク", + "edit": "タスクを編集", + "title_placeholder": "タスク名", + "description_placeholder": "説明を追加...", + "due_date": "期限日", + "include_time": "時刻を含める", + "priority": "優先度", + "priority_none": "なし", + "priority_high": "高", + "priority_medium": "中", + "priority_low": "低", + "progress": "ステータス", + "progress_needs_action": "要対応", + "progress_in_process": "進行中", + "progress_completed": "完了", + "progress_cancelled": "キャンセル", + "calendar": "カレンダー", + "alert": "リマインダー", + "alert_none": "なし", + "alert_at_time": "期限日時に通知", + "alert_5min": "5分前", + "alert_15min": "15分前", + "alert_30min": "30分前", + "alert_1hr": "1時間前", + "alert_1day": "1日前", + "delete": "削除", + "cancel": "キャンセル", + "save": "保存", + "quick_add_placeholder": "タスクを追加...", + "due_today": "今日が期限", + "due_tomorrow": "明日が期限", + "overdue": "期限切れ" + }, + "months": { + "jan": "1月", + "feb": "2月", + "mar": "3月", + "apr": "4月", + "may": "5月", + "jun": "6月", + "jul": "7月", + "aug": "8月", + "sep": "9月", + "oct": "10月", + "nov": "11月", + "dec": "12月", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "メニューを開く", + "toolbar": { + "more": "その他の操作", + "select_view": "表示を切り替え(現在: {view})" + }, + "other_calendars": "他のカレンダー", + "calendar_options": "{name} のオプション" + }, + "advanced_search": { + "title": "詳細検索", + "from": "差出人", + "from_placeholder": "差出人のメールまたは名前", + "to": "宛先", + "to_placeholder": "宛先のメールまたは名前", + "subject": "件名", + "subject_placeholder": "件名に含まれる...", + "body": "本文", + "body_placeholder": "本文に含む...", + "folder": "フォルダー", + "all_folders": "すべてのフォルダー", + "all_folders_except_spam_trash": "迷惑メールとゴミ箱を除くすべてのフォルダー", + "has_attachment": "添付ファイル", + "date_after": "以降", + "date_before": "以前", + "size_min": "次のサイズ以上 (KB)", + "size_max": "次のサイズ未満 (KB)", + "starred": "スター付き", + "unread": "未読", + "read": "既読", + "yes": "はい", + "no": "いいえ", + "clear": "クリア", + "clear_all": "すべてクリア", + "filters_active": "{count}件のフィルター", + "filters_active_plural": "{count}件のフィルター", + "toggle_filters": "フィルター", + "search_hint": "詳細フィルターで正確に検索", + "advanced_filters_tooltip": "詳細検索フィルター", + "results_found": "{count, plural, =0 {結果が見つかりません} other {#件の結果}}", + "results_found_more": "{count}+件の結果", + "suggestions_recent": "最近の検索", + "suggestions_people": "連絡先", + "suggestions_from": "{address} からのメールを検索", + "suggestions_to": "{address} 宛のメールを検索", + "suggestions_remove_recent": "最近の検索から削除", + "has_words": "含む", + "size": "サイズ", + "date_within": "日付", + "search_in": "検索対象", + "has_attachment_check": "添付ファイルあり", + "search": "検索" + }, + "welcome": { + "title": "メールボックスへようこそ", + "tip_compose": "cキーで新しいメールを作成", + "tip_shortcuts": "?キーですべてのショートカットを表示", + "tip_sidebar": "サイドバーからカレンダー、連絡先、設定にアクセス", + "tip_settings": "設定でカスタマイズできます", + "tip_compose_touch": "鉛筆ボタンをタップして新しいメールを作成", + "tip_sidebar_touch": "カレンダー、連絡先、設定は下部のバーにあります", + "got_it": "了解", + "settings": "設定", + "dismiss": "閉じる", + "start_tour": "ツアーを開始" + }, + "demo_welcome": { + "title": "Bulwark Mail へようこそ", + "description": "フル機能のウェブメールクライアントをブラウザで体験できます。すべてのデータはお使いのデバイスに保存されるので、自由にお試しください。", + "feature_email": "メールの読み書き", + "feature_organize": "タグ・スター・フォルダ", + "feature_shortcuts": "キーボードショートカット", + "feature_privacy": "100% プライベートなデモ", + "hint": "左のメールをクリックして始めるか、ツアーを開始してください。" + }, + "files": { + "office_edit": "ドキュメントを編集", + "office_loading": "ドキュメントエディターを開いています…", + "office_error": "ドキュメントエディターを開けませんでした。", + "office_close": "エディターを閉じる", + "open_in_new_tab": "新しいタブで開く", + "title": "ファイル", + "search_placeholder": "ファイルを検索...", + "empty_state_title": "ファイルがありません", + "upload_files": "ファイルをアップロード", + "new_folder": "新しいフォルダー", + "new_folder_name": "フォルダー名", + "rename": "名前を変更", + "rename_title": "名前を変更", + "new_name": "新しい名前", + "name_forbidden_chars": "サーバーでは名前に次の文字を使用できません: {chars}", + "name_reserved": "この名前はサーバーで予約されています。", + "delete": "削除", + "delete_confirm_title": "リソースを削除", + "delete_confirm_message": "\"{name}\"を削除してもよろしいですか?この操作は元に戻せません。", + "download": "ダウンロード", + "name": "名前", + "size": "サイズ", + "modified": "更新日時", + "type": "種類", + "folder": "フォルダー", + "file": "ファイル", + "uploading": "アップロード中...", + "upload_success": "{count, plural, other {#件のファイルをアップロードしました}}", + "upload_canceled": "アップロードをキャンセルしました", + "upload_canceled_partial": "キャンセル前に{count, plural, other {#件のファイル}}がアップロードされました", + "upload_error": "ファイルのアップロードに失敗しました", + "create_folder_success": "フォルダーを作成しました", + "create_folder_error": "フォルダーの作成に失敗しました", + "delete_success": "正常に削除しました", + "delete_error": "削除に失敗しました", + "rename_success": "正常に名前を変更しました", + "rename_error": "名前の変更に失敗しました", + "download_error": "ダウンロードに失敗しました", + "not_available": "ファイルストレージはこのサーバーで利用できません", + "cancel": "キャンセル", + "create": "作成", + "save": "保存", + "no_results": "検索に一致するファイルがありません", + "batch_delete_confirm_message": "{count, plural, other {#件のアイテム}}を削除してもよろしいですか?この操作は元に戻せません。", + "batch_delete_success": "{count, plural, other {#件のアイテムを削除しました}}", + "grid_view": "グリッド表示", + "list_view": "リスト表示", + "details": "詳細", + "path": "パス", + "preview": "プレビュー", + "preview_error": "プレビューの読み込みに失敗しました", + "cut": "切り取り", + "copy": "コピー", + "paste": "貼り付け", + "move_success": "{count, plural, other {#件のアイテムを移動しました}}", + "move_error": "移動に失敗しました", + "paste_success": "正常に貼り付けました", + "paste_error": "貼り付けに失敗しました", + "new_text_file": "新規テキストファイル", + "file_name": "ファイル名", + "retry": "再試行", + "refresh": "更新", + "duplicate": "複製", + "duplicate_success": "正常に複製しました", + "duplicate_error": "複製に失敗しました", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "ファイルを作成しました", + "create_file_error": "ファイルの作成に失敗しました", + "favorites": "お気に入り", + "recent": "最近のファイル", + "show_in_folder": "フォルダーで表示", + "remove_from_recent": "最近のファイルから削除", + "upload_folder": "フォルダをアップロード", + "file_too_large": "\"{name}\" がファイルサイズの上限を超えています({max})", + "undo": "元に戻す", + "undo_success": "操作を元に戻しました", + "undo_error": "元に戻すのに失敗しました", + "toolbar": "ファイル操作", + "file_list": "ファイルとフォルダ", + "context_menu": "操作", + "settings_ascending": "昇順", + "settings_descending": "降順", + "disabled_title": "ファイル機能は管理者によって無効にされています", + "disabled_description": "WebDAV経由の大容量ファイルアップロードは、メモリ不足クラッシュや回復不能なディスク使用量など、Stalwart/RocksDBの不安定性を引き起こす可能性があります。削除されたファイルはストレージからすぐに削除されない場合があります。この機能は本番環境では推奨されません。", + "no_accounts": "接続されているアカウントはありません。", + "open_folder_tree": "フォルダーツリーを開く", + "migration_title": "ファイルを更新しています…", + "migration_description": "フォルダーとファイルを適切な構造に整理しています。これは一度だけ行われます。", + "new": "新規", + "my_files": "マイファイル", + "folders": "フォルダー", + "files_heading": "ファイル", + "open": "開く", + "star": "スターを付ける", + "unstar": "スターを外す", + "expand_folder": "{name}を展開", + "collapse_folder": "{name}を折りたたむ", + "sidebar_hint": "フォルダーにスターを付けたりファイルを開いたりすると、ここに表示されます。", + "breadcrumbs": "フォルダーのパス", + "show_path": "パス内の非表示のフォルダーを表示", + "selection_actions": "選択したアイテムの操作", + "selected_count": "{count, plural, other {#件選択中}}", + "clear_selection": "選択を解除", + "select_all": "すべて選択", + "select_item": "{name}を選択", + "more_actions": "{name}のその他の操作", + "paste_here": "このフォルダーに貼り付け", + "paste_count": "{count, plural, other {#件のアイテムを貼り付け}}", + "sort_by": "並べ替え", + "view_mode": "レイアウト", + "clear_search": "検索をクリア", + "no_results_for": "このフォルダーには「{query}」に一致するアイテムがありません", + "download_count": "{count, plural, other {#件のファイルをダウンロード}}", + "delete_count": "{count, plural, other {#件のアイテムを削除}}", + "close_details": "詳細を閉じる", + "file_kind": "{ext}ファイル", + "folder_summary": "{folders, plural, =0 {フォルダーなし} other {#個のフォルダー}}、{files, plural, =0 {ファイルなし} other {#個のファイル}}", + "details_hint": "ファイルまたはフォルダーを選択すると詳細が表示されます。", + "sharing": "共有", + "shared_with_count": "{count, plural, other {#人と共有}}", + "only_you": "自分のみ", + "empty_folder_title": "このフォルダーは空です", + "empty_folder_description": "ここにファイルをドロップするか、デバイスからアップロードしてください。", + "empty_folder_read_only": "このフォルダーにはファイルを追加できません。", + "choose_account": "アカウントを選択", + "drop_to_upload": "ドロップして{folder}にアップロード", + "uploading_count": "{total}件中{current}件をアップロード中", + "dismiss": "閉じる", + "zoom_in": "拡大", + "zoom_out": "縮小", + "zoom_reset": "画面に合わせる", + "rotate": "回転", + "close_preview": "閉じる" + }, + "smime": { + "your_certificates": "あなたの証明書", + "your_certificates_desc": "メールの署名と暗号化のために S/MIME 証明書をインポートして管理します", + "recipient_certificates": "受信者証明書", + "recipient_certificates_desc": "受信者へのメールを暗号化するための公開証明書", + "identity_bindings": "ID と鍵の関連付け", + "identity_bindings_desc": "S/MIME 証明書をメール ID に関連付けます", + "defaults_title": "既定値", + "defaults_desc": "署名と暗号化の既定動作を設定します", + "import_pkcs12": "PKCS#12 をインポート (.p12/.pfx)", + "import_public_cert": "証明書をインポート", + "no_certificates": "まだ証明書はインポートされていません", + "no_recipient_certs": "受信者証明書がありません", + "expires": "有効期限", + "expired": "期限切れ", + "bound_to": "関連付け先", + "no_key_bound": "なし", + "lock": "鍵をロック", + "unlock": "鍵をロック解除", + "details": "詳細を表示", + "delete": "削除", + "encrypt_by_default": "既定で暗号化する", + "encrypt_by_default_desc": "すべての受信者が証明書を持っている場合に自動でメールを暗号化します", + "remember_unlocked": "ロック解除した鍵を記憶する", + "remember_unlocked_desc": "このブラウザーセッションの間は鍵をロック解除したままにします", + "sign_default_for": "既定で署名する対象", + "enter_p12_passphrase": "PKCS#12 パスフレーズを入力", + "p12_passphrase_desc": "この証明書ファイルを保護しているパスフレーズを入力してください", + "enter_storage_passphrase": "保存用パスフレーズを設定", + "storage_passphrase_desc": "ブラウザー内でこの鍵を保護するためのパスフレーズを選択してください", + "next": "次へ", + "import": "インポート", + "unlock_key": "鍵をロック解除", + "unlock_key_desc": "署名または復号に使うため、この鍵の保存用パスフレーズを入力してください", + "passphrase_placeholder": "パスフレーズを入力", + "confirm_passphrase_placeholder": "パスフレーズを確認", + "passphrase_mismatch": "パスフレーズが一致しません", + "cancel": "キャンセル", + "processing": "処理中…", + "close": "閉じる", + "certificate_details": "証明書の詳細", + "cert_subject": "サブジェクト", + "cert_issuer": "発行者", + "cert_email": "メールアドレス", + "cert_serial": "シリアル番号", + "cert_validity": "有効期間", + "cert_fingerprint": "フィンガープリント (SHA-256)", + "cert_algorithm": "アルゴリズム", + "cert_capabilities": "機能", + "cert_source": "ソース", + "cert_expired": "この証明書は期限切れです", + "cert_not_yet_valid": "この証明書はまだ有効ではありません", + "cap_sign": "署名", + "cap_encrypt": "暗号化", + "cap_none": "なし", + "show_passphrase": "パスフレーズを表示", + "hide_passphrase": "パスフレーズを隠す", + "sign_toggle": "署名", + "encrypt_toggle": "暗号化", + "missing_recipient_certs": "次の宛先の証明書がありません: {emails}", + "missing_sender_cert": "この ID に関連付けられた証明書がありません", + "status_encrypted_ok": "このメッセージは暗号化されています", + "status_encrypted_no_key": "このメッセージは暗号化されていますが、一致する鍵が見つかりませんでした", + "status_encrypted_failed": "このメッセージを復号できませんでした", + "status_signed_valid": "署名を確認しました", + "status_signed_invalid": "署名の検証に失敗しました", + "status_signed_expired_cert": "期限切れの証明書で署名されています", + "status_signed_self_signed": "署名は有効ですが、証明書は自己署名です(信頼されていません)", + "status_signed_mismatch": "署名は有効ですが、署名者が送信者と一致しません", + "status_unsupported": "未対応の S/MIME 形式です", + "auto_import_signer_certs": "署名者証明書を自動インポート", + "auto_import_signer_certs_desc": "検証済み署名メールの証明書を今後の暗号化用に自動保存します", + "export": "エクスポート", + "enter_export_passphrase": "エクスポート用パスフレーズを設定", + "export_passphrase_desc": "エクスポートする PKCS#12 ファイルを保護するパスフレーズを選択してください", + "export_storage_desc": "エクスポートのために鍵を復号する保存用パスフレーズを入力してください", + "incorrect_passphrase": "パスフレーズが正しくありません" + }, + "tour": { + "step_counter": "ステップ {current} / {total}", + "skip": "ツアーをスキップ", + "back": "戻る", + "next": "次へ", + "finish": "完了", + "take_a_tour": "インターフェースのツアーを見る", + "restart_title": "紹介ツアー", + "restart_desc": "インターフェースのガイドツアーを再生する", + "restart_button": "ツアーを再開", + "show_on_new_devices_title": "新しいデバイスで表示", + "show_on_new_devices_desc": "他のデバイスで完了済みでも、新しいデバイスで初めてサインインしたときにウェルカムバナーとツアーを再表示します", + "sidebar_title": "メールボックス", + "sidebar_desc": "フォルダーサイドバーです。メールボックスをクリックしてメールを表示できます。フォルダーの作成、メールのドラッグ移動、未読数の確認ができます。", + "compose_title": "メールを作成", + "compose_desc": "ここをクリックして新しいメールを作成します。宛先、添付ファイルの追加やリッチテキスト書式が使えます。", + "search_title": "メールを検索", + "search_desc": "送信者、件名、内容で検索できます。フィルターアイコンをクリックすると、日付範囲、添付ファイル、スター付きメッセージなどの詳細オプションが使えます。", + "email_list_title": "メール一覧", + "email_list_desc": "メールがここに表示されます。クリックすると右側で読めます。チェックボックスで複数選択し、一括で移動、削除、タグ付けができます。", + "email_viewer_title": "閲覧パネル", + "email_viewer_desc": "選択したメールがここに開きます。ツールバーのボタンで返信、転送、アーカイブ、削除ができます。メールにスターやカラータグも付けられます。", + "keywords_title": "カラータグ", + "keywords_desc": "色分けされたタグでメールを整理できます。メールをタグにドラッグしてラベル付けするか、右クリックでタグを割り当てられます。", + "calendar_title": "カレンダー", + "calendar_desc": "カレンダーに切り替えてイベントを管理できます。イベントの作成、リマインダーの設定、日・週・月表示の切り替えができます。", + "contacts_title": "連絡先", + "contacts_desc": "アドレス帳がここにあります。連絡先のインポート、グループの作成、連絡先をクリックして詳細を確認できます。", + "settings_title": "設定", + "settings_desc": "すべてをカスタマイズできます:テーマ、表示密度、署名、フィルター、キーボードショートカット、カレンダー設定など。", + "shortcuts_title": "キーボードショートカット", + "shortcuts_desc": "パワーユーザー向けの機能です。いつでも ? を押すと利用可能なすべてのショートカットが表示されます。マウスを使わずにナビゲーション、作成、メール管理ができます。", + "compose_open_title": "メール作成画面", + "compose_open_desc": "これはメール作成画面です。宛先を追加し、メッセージを書き、ファイルを添付し、リッチテキスト書式を使用できます。下書きの保存やテンプレートの使用も可能です。", + "calendar_view_title": "カレンダー表示", + "calendar_view_desc": "サンプルイベント付きのカレンダーです。ツールバーで日、週、月、アジェンダビューを切り替えられます。", + "create_event_title": "イベントを作成", + "create_event_desc": "このボタンをクリックして新しいカレンダーイベントを作成します。タイトル、日付、時間を設定し、参加者を追加できます。", + "event_modal_title": "イベント詳細", + "event_modal_desc": "イベントフォームです。タイトルを入力し、日時を選択し、場所や参加者を追加してください。完了したら保存をクリック - または閉じて次に進みましょう。", + "contacts_list_title": "連絡先一覧", + "contacts_list_desc": "連絡先の一覧です。連絡先をクリックすると右側に詳細が表示されます。新しい連絡先の作成、vCardのインポート、グループへの整理もできます。", + "files_title": "ファイルストレージ", + "settings_tabs_title": "設定メニュー", + "settings_tabs_desc": "すべての設定カテゴリがここにあります。外観のカスタマイズ、IDの管理、メールフィルターの設定、カレンダーの構成など、多数の項目を調整できます。", + "files_desc": "ファイルブラウザでファイルのアップロード、整理、共有ができます。メールに統合されたパーソナルクラウドのようなものです。", + "demo_banner_title": "デモコントロール", + "demo_banner_desc": "デモモードです。すべてブラウザ内に保存されます。「デモをリセット」をクリックすると、いつでもクリーンなサンプルデータで再開できます。", + "quota_title": "ストレージ使用量", + "quota_desc": "メールボックスのサイズをここで確認できます。使用量が増えるとサークルが満たされます。" + }, + "unified_mailbox": { + "search_unavailable": "統合ビューでは検索を利用できません" + }, + "calendar_event_notifications": { + "someone": "誰か", + "invited": "{name} さんが「{title}」に招待しました", + "updated": "{name} さんが「{title}」を更新しました", + "cancelled": "{name} さんが「{title}」をキャンセルしました", + "untitled": "無題の予定" + }, + "share_notifications": { + "someone": "誰か", + "shared": "{name} さんが{kind}「{object}」を共有しました", + "changed": "{name} さんが{kind}「{object}」へのアクセス権を変更しました", + "revoked": "{name} さんが{kind}「{object}」へのアクセス権を削除しました", + "object": { + "folder": "フォルダー", + "calendar": "カレンダー", + "address_book": "アドレス帳", + "files": "ファイルフォルダー" + } + }, + "sharing": { + "title": "「{name}」を共有", + "description": "このサーバー上の他のユーザーまたはグループにアクセス権を付与します。変更はすぐに反映されます。", + "no_shares": "まだ誰にも共有されていません。", + "add_person": "ユーザーまたはグループを追加", + "search_placeholder": "名前またはメールで検索…", + "loading_principals": "ユーザーを読み込み中…", + "no_principals": "他のユーザーまたはグループは見つかりません。", + "no_match": "一致する項目がありません。", + "remove": "アクセス権を削除", + "group": "グループ", + "share_added": "アクセス権を付与しました", + "share_updated": "アクセス権を更新しました", + "share_removed": "アクセス権を削除しました", + "share_failed": "共有の更新に失敗しました", + "preset": { + "freeBusy": "空き時間情報のみ", + "read": "読み取り専用", + "readWrite": "読み取り・書き込み", + "manager": "管理者", + "custom": "カスタム" + } + }, + "quote_header": { + "reply_line": "{date}に{from}が書きました:", + "forwarded_separator": "---------- 転送メッセージ ----------", + "from_label": "差出人", + "date_label": "日付", + "subject_label": "件名" + }, + "pwa_install": { + "title": "{appName} をインストール", + "description": "素早いアクセスとオフライン対応のため、アプリをインストールしましょう。", + "not_now": "後で", + "install": "インストール", + "dont_remind": "今後表示しない", + "dismiss_aria": "インストールプロンプトを閉じる" + }, + "deep_link": { + "message_not_found": "このメッセージは利用できなくなりました。", + "thread_not_found": "この会話は利用できなくなりました。", + "folder_not_found": "このフォルダーは利用できなくなりました。", + "event_not_found": "この予定は利用できなくなりました。", + "file_not_found": "このファイルは利用できなくなりました。", + "account_unavailable": "このリンクを開くには、そのアカウントにサインインしてください。", + "copy_message": "メッセージのリンクをコピー", + "copy_conversation": "会話のリンクをコピー", + "copy_event": "予定のリンクをコピー", + "copied": "リンクをコピーしました", + "copy_failed": "リンクをコピーできませんでした" + }, + "plugins": { + "oauth_callback": { + "completing": "認証を完了しています…", + "completing_message": "接続が確立されるまでお待ちください。", + "received_title": "認証を受信しました", + "received_message": "接続を開始したタブに戻ることができます。", + "failed_title": "認証に失敗しました", + "denied": "認証が拒否されました。", + "failed_detail": "認証に失敗しました: {error}", + "missing_params": "認証コードまたはステートがありません。", + "delivery_failed": "認証結果をアプリのタブに渡せませんでした。このウィンドウを閉じて、もう一度お試しください。" + }, + "settings": { + "title": "プラグイン", + "description": "管理者が追加したプラグインです。アカウントごとにオンとオフを切り替えられます。", + "description_uploads": "プラグインでウェブメールに機能を追加できます。アカウントごとにオンとオフを切り替えたり、自分でインストールしたりできます。", + "description_demo": "デモではプラグインを利用できません。", + "install_button": "プラグインをインストール", + "install_hint": "プラグインの ZIP ファイルを選択してください。", + "install_hint_approval": "プラグインの ZIP ファイルを選択してください。オンにする前に管理者の承認が必要です。", + "install_uploads_off": "管理者がプラグインのインストールを許可していません。", + "install_managed_id": "同じ名前のプラグインが管理者から提供されているため、置き換えられません。", + "install_invalid": "このファイルはインストールできるプラグインではありません: {details}", + "toast_installed": "プラグインをインストールしました。下でオンにしてください。", + "toast_installed_pending": "プラグインをインストールし、管理者に承認を依頼しました。", + "toast_enabled": "{name} をオンにしました", + "toast_disabled": "{name} をオフにしました", + "toast_removed": "{name} を削除しました", + "toast_forced": "{name} は管理者によって常にオンになっています", + "toast_approval_requested": "{name} は管理者の承認を待っています", + "empty_title": "プラグインはまだありません", + "empty_admin": "管理者はまだプラグインを追加していません。", + "empty_uploads": "ZIP ファイルからプラグインをインストールして始めましょう。", + "empty_demo": "プラグインには実際のアカウントが必要なため、デモにはありません。", + "syncing": "サーバーとプラグインを確認しています…", + "status_installed": "オフ", + "status_enabled": "起動中", + "status_running": "オン", + "status_disabled": "オフ", + "status_error": "問題あり", + "badge_forced": "必須", + "badge_managed": "管理者から提供", + "badge_yours": "自分でインストール", + "badge_awaiting": "承認待ち", + "toggle_label": "{name} のオンとオフを切り替え", + "forced_note": "このプラグインは管理者によって必須とされているため、オフにできません。", + "permissions": "できること", + "permissions_managed": "できること(管理者が承認済み)", + "settings": "設定", + "remove": "削除", + "remove_title": "{name} を削除しますか?", + "remove_message": "プラグインと、このブラウザーに保存したデータがすべて削除されます。後でもう一度インストールできます。", + "remove_confirm": "削除", + "error_user_plugins_off": "管理者がユーザーによるプラグインを無効にしたため、このプラグインは動作しません。", + "error_approval_unavailable": "このプラグインは承認が必要になる前にインストールされました。承認を依頼するには、削除してもう一度インストールしてください。", + "error_approval_denied": "管理者はこのプラグインを承認しませんでした。", + "error_approval_pending": "管理者がこのプラグインを承認するのを待っています。", + "error_auto_disabled": "繰り返し失敗したためオフになりました。再試行するにはもう一度オンにしてください。", + "accounts_heading": "接続済みのアカウント", + "account_on": "{host} の {label}", + "account_waiting": "{host} の {label} (サインイン未完了)", + "disconnect": "接続を解除", + "disconnect_failed": "{label} の接続を解除できませんでした" + }, + "consent": { + "title": "{name} にこの操作を許可しますか?", + "intro": "このプラグインは次の権限を求めています。許可した場合にのみ付与されます。", + "high_risk_note": "印の付いた項目は、プラグインにメールの内容へのアクセスや外部へのデータ送信を許可します。信頼できるプラグインにのみ許可してください。", + "revoke_hint": "プラグインはいつでも設定でオフにしたり削除したりできます。", + "deny": "許可しない", + "allow": "許可" + }, + "permissions": { + "none": "特別な権限は必要ありません。", + "high_risk": "注意が必要", + "email_read": { + "title": "メールを読む", + "body": "アカウント内のすべてのメッセージの差出人、宛先、件名、本文全体と、フォルダを見ることができます。" + }, + "email_write": { + "title": "メールの変更と整理", + "body": "メッセージにタグ、フラグ、その他のキーワードを追加したり削除したり、フォルダ間やアーカイブへ移動したり、下書きを保存したりできます。" + }, + "email_send": { + "title": "送信前のメールを変更", + "body": "作成中のメッセージの宛先、件名、本文、添付ファイルを読んで変更したり、メッセージの送信を止めたりできます。" + }, + "crypto_full": { + "title": "このアプリへのフルアクセス", + "body": "ウェブメール本体と同じ権限で動作します。すべてのメールと保存された鍵を読み、あなたに代わって操作できます。完全に信頼できるプラグインにのみ許可してください。" + }, + "email_raw_send": { + "title": "自分で作成したメッセージを送信", + "body": "署名済みや暗号化済みのメッセージなど、完成したメッセージをあなたのアカウントから送信できます。" + }, + "email_blob_read": { + "title": "メッセージの元データを読む", + "body": "メッセージと添付ファイルの元データ、および送信前に添付するすべてのファイルを読むことができます。" + }, + "email_blob_write": { + "title": "送信前に添付ファイルを置き換え", + "body": "メールサーバーにファイルをアップロードし、送信前に添付したファイルを別のファイルに置き換えることができます。" + }, + "email_render_takeover": { + "title": "メッセージの表示方法を変更", + "body": "開いたメッセージとメッセージ一覧の本文を置き換えることができます。たとえば復号した内容を表示するためです。" + }, + "calendar_read": { + "title": "カレンダーを読む", + "body": "カレンダーとその予定を見ることができます。" + }, + "calendar_write": { + "title": "カレンダーを変更", + "body": "予定を作成、変更、削除できます。" + }, + "contacts_read": { + "title": "連絡先を読む", + "body": "アドレス帳を検索して読むことができます。" + }, + "contacts_write": { + "title": "連絡先を変更", + "body": "連絡先とアドレス帳を作成、変更、削除できます。" + }, + "files_read": { + "title": "ファイルを読む", + "body": "アカウントに保存されたファイルを閲覧して開くことができます。" + }, + "files_write": { + "title": "ファイルを変更", + "body": "アカウント内のファイルをアップロード、名前変更、移動、削除できます。" + }, + "identity_read": { + "title": "送信者情報を見る", + "body": "送信に使うアドレスとその署名を見ることができます。" + }, + "identity_write": { + "title": "送信者情報を変更", + "body": "送信者情報と署名を作成、変更、削除できます。" + }, + "filters_read": { + "title": "メールフィルターを読む", + "body": "受信メールを振り分けるルールを見ることができます。" + }, + "filters_write": { + "title": "メールフィルターを変更", + "body": "サーバー側のフィルターにルールを追加できます。このルールは受信メールを移動、転送、削除できます。" + }, + "tasks_read": { + "title": "タスクを読む", + "body": "タスクリストとタスクを見ることができます。" + }, + "tasks_write": { + "title": "タスクを変更", + "body": "タスクを作成、変更、完了、削除できます。" + }, + "templates_read": { + "title": "テンプレートを読む", + "body": "保存したメッセージのテンプレートを見ることができます。" + }, + "templates_write": { + "title": "テンプレートを変更", + "body": "メッセージのテンプレートを作成、変更、削除できます。" + }, + "smime_read": { + "title": "S/MIME の状態を見る", + "body": "S/MIME の鍵と証明書があるかどうかを見ることができます。鍵にはアクセスできません。" + }, + "vacation_read": { + "title": "不在時の自動返信を見る", + "body": "自動返信がオンかどうかと、その内容を見ることができます。" + }, + "vacation_write": { + "title": "不在時の自動返信を変更", + "body": "自動返信のオンとオフを切り替え、本文と期間を変更できます。" + }, + "settings_read": { + "title": "設定を読む", + "body": "環境設定とタグの定義を見ることができます。" + }, + "settings_write": { + "title": "設定を変更", + "body": "タグを追加して並べ替えたり、環境設定を変更したりできます。" + }, + "security_read": { + "title": "アカウントのセキュリティを見る", + "body": "二要素認証と暗号化がオンかどうかを見ることができます。秘密情報にはアクセスできません。" + }, + "auth_observe": { + "title": "サインインを知る", + "body": "サインインしたとき、アカウントを切り替えたとき、セッションが更新されたときに通知を受けます。" + }, + "auth_emit": { + "title": "サインアウトさせる", + "body": "ウェブメールからサインアウトさせることができます。" + }, + "account_read": { + "title": "アカウントを見る", + "body": "追加したアカウントと、そのアドレスとサーバーを見ることができます。" + }, + "http_post": { + "title": "あなたの代わりにウェブメールサーバーを使う", + "body": "あなたのログイン情報で次のサーバーエンドポイントを呼び出せます:" + }, + "http_fetch": { + "title": "他のウェブサイトに接続", + "body": "次のサイトにリクエストを送信できます。プラグインが読めるものはすべて送信される可能性があります:" + }, + "admin_config": { + "title": "管理者が保存した設定を読む", + "body": "管理者がこのプラグイン用に保存した設定を読むことができます。" + }, + "ui_observe": { + "title": "アプリ内の現在地を把握", + "body": "どのページが開いているかを知り、クリックしたリンクに反応できます。" + }, + "ui_toolbar": { + "title": "ツールバーにボタンを追加", + "body": "メッセージのツールバー、開いたメッセージ、上部のバーに独自のボタンを表示します。" + }, + "ui_app_top_banner": { + "title": "上部にバナーを表示", + "body": "アプリの上部にお知らせを表示します。" + }, + "ui_email_banner": { + "title": "メッセージにお知らせを表示", + "body": "開いたメッセージや会話の各部分の上、中、間に独自のお知らせや要約を表示します。" + }, + "ui_email_footer": { + "title": "メッセージの下に内容を追加", + "body": "開いたメッセージの下に独自の内容を表示します。" + }, + "ui_contact_cryptokeys": { + "title": "連絡先の暗号鍵を表示", + "body": "連絡先の暗号鍵を表示して管理します。" + }, + "ui_email_details": { + "title": "メッセージの詳細にパネルを追加", + "body": "開いたメッセージの横または下に独自のパネルを表示します。" + }, + "ui_download_file": { + "title": "デバイスにファイルを保存", + "body": "作成したファイルをダウンロードできるようにします。" + }, + "ui_message_list_tabs": { + "title": "受信トレイにタブを追加", + "body": "受信トレイを「更新」や「プロモーション」などのタブに分けます。" + }, + "ui_composer_toolbar": { + "title": "作成画面に追加", + "body": "メッセージの作成中に独自のボタン、メニュー項目、セクションを表示します。" + }, + "ui_composer_sidebar": { + "title": "作成画面にパネルを追加", + "body": "作成中のメッセージの横に独自のパネルを表示します。" + }, + "ui_sidebar_widget": { + "title": "サイドバーに追加", + "body": "メールのサイドバーに独自のボックスやセクションを表示します。" + }, + "ui_settings_section": { + "title": "設定ページを追加", + "body": "設定に独自のページを表示します。" + }, + "ui_context_menu": { + "title": "メッセージのメニューに追加", + "body": "メッセージを右クリックしたときに開くメニューに項目を追加します。" + }, + "ui_navigation_rail": { + "title": "ナビゲーションバーに追加", + "body": "ナビゲーションバーに独自のボタンを表示します。" + }, + "ui_keyboard": { + "title": "キーボードショートカットを追加", + "body": "独自に定義したキーボードショートカットに反応します。" + }, + "ui_calendar_action": { + "title": "カレンダー画面に追加", + "body": "予定に独自のボタンやセクションを表示し、カレンダーの日付に短いラベルや点を表示します。" + }, + "ui_admin_page": { + "title": "管理ページを追加", + "body": "管理設定に独自のページを表示します。" + }, + "app_lifecycle": { + "title": "アプリの起動と終了を知る", + "body": "アプリが起動したとき、オフラインになったとき、サインアウトしたときに通知を受けます。" + }, + "compose_open": { + "title": "作成画面を開く", + "body": "宛先、本文、添付ファイルを入力した状態で新しいメッセージを開きます。送信するかどうかはあなたが決めます。" + }, + "compose_read": { + "title": "作成中のメッセージを読む", + "body": "エディターでプラグインを使ったときに、作成中のメッセージの本文を読みます。署名と引用されたメッセージは含みません。" + }, + "compose_write": { + "title": "作成中のメッセージを変更する", + "body": "エディターでプラグインを使ったときに、作成中のメッセージのテキストを置き換えたり追加したりします。変更はひとつずつ元に戻せ、送信するかどうかは引き続きあなたが決めます。" + }, + "ui_notifications": { + "title": "通知を表示", + "body": "デバイスに通知を表示します。通知を許可していない場合はアプリ内に表示します。どの通知にもプラグイン名が入ります。" + }, + "ui_message_list": { + "title": "メッセージ一覧に追加", + "body": "メッセージ一覧の上に独自の内容を表示し、各メッセージと選択中のメッセージ用のツールバーにボタンを表示します。" + }, + "ui_contacts": { + "title": "連絡先画面に追加", + "body": "連絡先の詳細に独自のセクションを、連絡先一覧にボタンを表示します。" + }, + "ui_tasks": { + "title": "タスク画面に追加", + "body": "タスクの詳細に独自のセクションを、タスク一覧にボタンを表示します。" + }, + "ui_files": { + "title": "ファイル画面に追加", + "body": "ファイルのプレビューに独自のセクションを、ファイルのメニューに項目を表示します。" + }, + "ui_commands": { + "title": "コマンドを追加", + "body": "コマンドパレット (Ctrl+K) に独自の項目を追加します。" + }, + "ui_app_page": { + "title": "ページを追加", + "body": "アプリに独自のページを追加し、それぞれのボタンをナビゲーションバーに表示します。" + }, + "http_proxy": { + "title": "接続したアカウントを使用", + "body": "あなたまたは管理者が設定したアカウントやキーを使って、Bulwark のサーバー経由でリクエストを送信します。プラグインがパスワードを見ることはありませんが、そのアカウントがそのサーバーでできることはすべて実行できます。" + } + }, + "dialog": { + "from_plugin": "プラグイン {name} から", + "ok": "OK", + "submit": "送信", + "confirm": "確認", + "cancel": "キャンセル", + "close": "閉じる", + "turn_off": "{name} をオフにする", + "plugin_menu": "{name} のオプション", + "password_note": "このフォームはウェブメールではなく、プラグイン {name} が表示しています。このプラグインを信頼できる場合にのみパスワードを入力してください。", + "toast_auto_disabled": "{name} は繰り返し失敗したためオフになりました。" + }, + "connect": { + "title": "{label} に接続", + "intro_password": "{name} が {label} アカウントを使おうとしています。Bulwark はパスワードを自身のサーバーに保存し、{host} にだけ送信します。プラグインがパスワードを見ることはありません。", + "intro_password_any": "{name} が {label} アカウントを使おうとしています。Bulwark はパスワードを自身のサーバーに保存し、ここで入力したサーバーにだけ送信します。プラグインがパスワードを見ることはありません。", + "intro_signin": "{name} が {label} アカウントにサインインしようとしています。ここでサーバーを入力し、そのサーバーのページでサインインしてください。Bulwark は取得したアクセス権を自身のサーバーに保存します。プラグインがそれを見ることはありません。", + "server": "サーバーのアドレス", + "server_hint": "cloud.example.com", + "username": "ユーザー名", + "secret": "パスワードまたはキー", + "connect": "接続", + "continue": "続行", + "cancel": "キャンセル", + "saving": "保存しています…", + "error_server": "サーバーのアドレスを入力してください (例: https://cloud.example.com)", + "error_save": "保存できませんでした: {error}" + } + }, + "global_search": { + "title": "すべてを検索", + "placeholder": "メール、連絡先、カレンダー、ファイルを検索...", + "scope_label": "検索対象", + "account_label": "アカウント", + "scope_all": "すべて", + "scope_mail": "メール", + "scope_contacts": "連絡先", + "scope_calendar": "カレンダー", + "scope_files": "ファイル", + "all_accounts": "すべてのアカウント", + "show_all": "すべての結果を表示", + "no_results": "「{query}」に一致する結果はありません", + "server_hint": "完全な単語はサーバーを検索します。部分一致は読み込み済みの項目に適用されます", + "searching": "検索中...", + "account_error": "{account} を検索できませんでした", + "recent_searches": "最近の検索", + "recurring": "繰り返し", + "no_subject": "(no subject)", + "empty_hint": "すべてのアカウントのメール、連絡先、予定、ファイルを検索できます。from:、subject:、has:attachment、after:2026-01-01 なども試せます。", + "flat_by_date": "日付順のフラット表示", + "grouped": "グループ表示", + "load_more": "さらに読み込む", + "preview_empty": "プレビューする結果を選択してください", + "open_full": "開く", + "view_label": "表示" + }, + "top_bar": { + "more": "その他のオプション" + }, + "plugin_pages": { + "loading": "ページを読み込んでいます", + "not_available_title": "このページは利用できません", + "not_available_description": "このページを追加したプラグインがオフになっているか、インストールされていません。", + "back_to_mail": "メールに戻る", + "settings_group": "プラグイン" + }, + "command_palette": { + "title": "コマンドパレット", + "open": "コマンドパレットを開く", + "placeholder": "コマンドを入力", + "no_results": "一致するコマンドはありません", + "go_to": "{name}に移動", + "compose": "新規メッセージ", + "search": "メールを検索", + "theme_dark": "ダークテーマに切り替え", + "theme_light": "ライトテーマに切り替え", + "shortcuts": "キーボードショートカットを表示", + "group_navigation": "移動", + "group_actions": "操作", + "group_plugins": "プラグイン" + }, + "notification_center": { + "title": "通知", + "mark_all_read": "すべて既読にする", + "clear": "クリア", + "empty": "通知はありません", + "close": "閉じる", + "unread": "未読", + "unread_count": "未読 {count} 件", + "level_error": "エラー", + "level_warning": "警告" + } +} diff --git a/locales/ko/common.json b/locales/ko/common.json index f8dacf19..b5db2cbb 100644 --- a/locales/ko/common.json +++ b/locales/ko/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "JMAP 프로토콜을 사용하는 미니멀한 웹메일 클라이언트", - "login": { - "title": "웹메일", - "username_label": "이메일", - "username_placeholder": "user@example.com", - "password_label": "비밀번호", - "password_placeholder": "비밀번호를 입력해 주세요", - "jmap_endpoint_label": "JMAP 서버", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "서버에서 이 도메인의 CORS 요청을 허용해야 해요.", - "jmap_server_label": "서버", - "jmap_server_auto_picked": "이메일 도메인에서 서버를 선택했습니다.", - "sign_in": "로그인", - "signing_in": "로그인 중...", - "loading": "불러오는 중...", - "reconnecting": "연결이 끊어졌어요. 다시 연결을 시도할게요...", - "error": { - "invalid_credentials": "이메일이나 비밀번호가 맞지 않아요. 다시 확인해 주세요.", - "connection_failed": "서버에 연결할 수 없어요. 인터넷 연결을 확인하고 다시 시도해 주세요.", - "cors_blocked": "서버에는 연결되었지만 교차 출처 요청(CORS)이 차단되었어요. JMAP 서버 설정을 확인해 주세요.", - "server_error": "일시적으로 서버를 사용할 수 없어요. 나중에 다시 시도해 주세요.", - "generic": "예상치 못한 오류가 발생했어요. 계속되면 관리자에게 문의해 주세요.", - "totp_required": "2단계 인증 코드가 필요해요. 아래에 코드를 입력해 주세요.", - "totp_invalid": "인증 코드가 올바르지 않아요. 인증 앱을 확인하고 다시 시도해 주세요.", - "oauth_discovery_failed": "SSO가 활성화되어 있지만 ID 공급자에 연결할 수 없어요. OAuth 설정을 확인해 주세요." - }, - "show_password": "비밀번호 표시", - "hide_password": "비밀번호 숨기기", - "totp_toggle": "2단계 인증 코드가 있어요", - "remember_me": "로그인 유지", - "config_error": { - "title": "설정 오류", - "fetch_failed": "앱 설정을 불러올 수 없어요. 나중에 다시 시도해 주세요.", - "server_not_configured": "메일 서버가 설정되지 않았어요. 관리자에게 문의해 주세요." - }, - "remove_from_history": "기록에서 삭제", - "totp_label": "인증 코드", - "totp_placeholder": "000000", - "session_expired": "세션이 만료되었어요. 다시 로그인해 주세요.", - "dismiss": "닫기", - "or": "또는", - "sign_in_sso": "SSO로 로그인", - "add_account_title": "계정 추가", - "add_account_subtitle": "다른 계정으로 로그인하기", - "cancel": "취소", - "website": "웹사이트", - "imprint": "회사 소개", - "privacy_policy": "개인정보 처리방침", - "try_demo": "데모 체험하기", - "demo_description": "계정 없이 샘플 데이터로 둘러보세요", - "demo_launching": "데모를 시작하는 중...", - "demo_login_button": "데모 시작", - "demo_tagline": "모든 기능을 갖춘 이메일 클라이언트를 경험해 보세요. 계정이 없어도 괜찮아요.", - "demo_no_signup": "가입할 필요 없이 샘플 데이터로 자유롭게 둘러보세요", - "oauth_completing": "로그인을 완료하는 중...", - "oauth_error": { - "title": "인증 실패", - "invalid_state": "보안 검증에 실패했어요. 다시 로그인해 주세요.", - "missing_params": "인증 데이터가 없어요. 다시 로그인해 주세요.", - "token_exchange_failed": "인증을 완료하지 못했어요. 다시 시도해 주세요.", - "access_denied": "접근이 거부되었어요. 관리자에게 문의해 주세요.", - "back_to_login": "로그인 화면으로 돌아가기" - } - }, - "sidebar": { - "close": "닫기", - "compose": "메일 쓰기", - "compose_hint": "메일 쓰기 (c)", - "search_placeholder": "메일 검색...", - "search_placeholder_hint": "메일 검색... (/ 누르기)", - "storage": "저장 공간", - "storage_used": "사용 중", - "storage_free": "여유", - "storage_total": "전체", - "sign_out": "로그아웃", - "sign_out_of": "{account}에서 로그아웃", - "sign_out_all": "모든 계정에서 로그아웃", - "add_account": "계정 추가", - "set_as_default": "기본값으로 설정", - "switch_account": "계정 전환", - "contacts": "연락처", - "calendar": "캘린더", - "settings": "설정", - "admin": "관리자", - "files": "파일", - "loading_mailboxes": "편지함을 불러오는 중...", - "push_connected": "실시간 업데이트 켜짐", - "push_disconnected": "실시간 업데이트 꺼짐", - "keyboard_shortcuts": "단축키", - "theme": { - "light": "라이트 모드", - "dark": "다크 모드", - "system": "시스템 테마" - }, - "language": { - "title": "언어" - }, - "mailboxes": { - "inbox": "받은편지함", - "sent": "보낸편지함", - "drafts": "임시보관함", - "trash": "휴지통", - "archive": "보관함", - "starred": "별표 편지함", - "all_mail": "모든 메일", - "spam": "스팸함", - "important": "중요 편지함" - }, - "unified_inbox": "통합 받은편지함", - "unified_sent": "모든 보낸편지함", - "unified_drafts": "모든 임시보관함", - "unified_trash": "모든 휴지통", - "unified_archive": "모든 보관함", - "unified_junk": "모든 스팸함", - "all_accounts": "모든 계정", - "unified_mailbox": "통합 메일함", - "expand": "펼치기", - "collapse": "접기", - "expand_tooltip": "펼치기", - "collapse_tooltip": "접기", - "mobile": { - "search": "검색", - "compose": "메일 쓰기", - "go_back": "뒤로 가기", - "toggle_menu": "메뉴 전환" - }, - "clear_search": "검색어 지우기", - "vacation_active": "부재중 자동 응답이 켜져 있어요", - "demo_banner": "데모 모드", - "demo_reset": "초기화", - "demo_tour": "둘러보기", - "tags": "태그", - "show_all_tags": "전체 보기({count})", - "show_fewer_tags": "간략히 보기", - "folders": "폴더", - "mail": "메일", - "nav_label": "내비게이션", - "add_app": "앱", - "shared": "공유됨", - "scheduled": "예약됨", - "unified_all_unread": "모든 읽지 않음", - "unified_all_starred": "모든 별표", - "unified_all_mail": "모든 메일", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "기본 앱", - "description": "이메일 및 캘린더 링크를 Bulwark에서 열지 선택하세요. 기술적으로 Bulwark는 mailto: 및 webcal: 링크의 프로토콜 핸들러로 등록됩니다.", - "unsupported": "이 브라우저 또는 연결은 수동 프로토콜 핸들러 등록을 지원하지 않습니다. 설치된 PWA는 브라우저 또는 시스템 설정을 통해 사용할 수 있을 수 있습니다.", - "mailto_label": "이메일 링크", - "mailto_description": "mailto: 링크를 Bulwark의 미리 채워진 작성 창에서 엽니다.", - "protocol_open_mode_label": "프로토콜 링크를 열 때", - "protocol_open_mode_description": "Bulwark가 mailto: 및 webcal: 링크를 새 탭에서 열지, 열린 세션을 재사용할지 선택하세요. 활성 세션 옵션은 브라우저가 포커스를 차단할 때 Bulwark를 앞으로 가져오기 위한 대체 알림을 클릭할 수 있도록 알림 권한이 필요합니다.", - "protocol_open_mode_active_session": "가능하면 활성 세션에서 열기", - "protocol_open_mode_new_tab": "항상 새 탭 열기", - "focus_notification_title": "Bulwark 열기", - "focus_notification_body": "링크가 Bulwark에서 열렸습니다. 창을 앞으로 가져오려면 클릭하세요.", - "webcal_label": "캘린더 링크", - "webcal_description": "webcal: 링크를 Bulwark의 미리 채워진 캘린더 구독 대화상자에서 엽니다.", - "register_mailto": "이메일 앱 등록", - "register_webcal": "캘린더 앱 등록", - "mailto_registered": "이메일 핸들러 등록을 요청했습니다", - "webcal_registered": "캘린더 핸들러 등록을 요청했습니다", - "registration_failed": "프로토콜 핸들러 등록에 실패했습니다", - "opening_mailto": "작성 창을 여는 중...", - "opening_webcal": "캘린더를 여는 중...", - "browser_note": "브라우저 또는 운영 체제에서 확인을 요청할 수 있으며, 기본 앱으로 선택하기 전에 Bulwark 설치가 필요할 수 있습니다.", - "select_account_title": "계정 선택", - "select_mailto_account": "이 이메일 링크를 열 계정을 선택하세요.", - "select_webcal_account": "이 캘린더 링크를 열 계정을 선택하세요.", - "select_account_note": "이 선택은 이 프로토콜 링크에만 적용됩니다.", - "detail_to": "받는 사람", - "detail_subject": "제목", - "detail_no_subject": "제목 없음", - "detail_calendar": "캘린더", - "detail_source": "출처", - "active_account": "활성", - "switching_account": "계정 전환 중..." - }, - "sidebar_apps": { - "modal_title": "사이드바 앱", - "add_new": "앱 추가", - "edit_app": "앱 수정", - "name_label": "이름", - "name_placeholder": "내 앱", - "name_required": "이름을 입력해 주세요", - "url_label": "URL", - "url_required": "URL을 입력해 주세요", - "url_invalid": "올바른 http 또는 https URL을 입력해 주세요", - "icon_label": "아이콘", - "icon_required": "아이콘을 선택해 주세요", - "open_mode_label": "열기 모드", - "open_new_tab": "새 탭", - "open_inline": "인라인", - "cancel": "취소", - "add": "추가", - "update": "업데이트", - "delete": "삭제", - "delete_confirm_title": "앱 삭제", - "delete_confirm": "정말 \"{name}\" 앱을 삭제할까요?", - "no_apps": "아직 추가된 앱이 없어요", - "no_apps_hint": "사이드바에 맞춤형 앱과 링크를 추가해 보세요", - "managed_title": "관리자가 제공", - "managed_description": "이 앱은 모든 사용자를 위해 설정되었으며 변경할 수 없습니다.", - "managed_badge": "관리됨", - "search_icons": "아이콘 검색...", - "show_popular": "인기", - "show_all": "전체", - "no_icons_found": "아이콘을 찾을 수 없어요", - "inline_badge": "인라인", - "tab_badge": "탭", - "show_on_mobile": "모바일에서 보기" - }, - "email_list": { - "no_emails": "메시지가 없어요", - "no_emails_description": "편지함이 비어있어요", - "no_search_results": "검색 결과가 없어요", - "no_search_results_description": "검색어나 필터를 변경해 보세요", - "loading": "메일을 불러오는 중...", - "unread": "읽지 않음", - "to_me": "나에게", - "to_recipients": "수신자 {count}명", - "and_others": "외 {count}명", - "draft": "임시보관", - "starred": "별표 표시됨", - "pinned": "고정됨", - "replied": "답장함", - "forwarded": "전달함", - "has_attachment": "첨부 파일 있음", - "conversations_count": "전체 {total}개 중 {count}개의 대화", - "conversations_count_plus": "{count}개 이상의 대화", - "conversations_count_simple": "{count}개의 대화", - "no_conversations": "대화가 없어요", - "loading_more": "메일을 더 불러오는 중...", - "no_more_emails": "더 이상 메일이 없어요", - "batch_actions": { - "select": "메일 선택", - "select_all": "모두 선택", - "selected_messages": "이메일 {count, plural, one {1개} other {#건}} 선택됨", - "mark_read": "읽은 상태로 표시", - "mark_unread": "읽지 않은 상태로 표시", - "delete": "삭제", - "delete_confirm_title": "메일 삭제", - "delete_confirm_message": "정말 선택한 {count, plural, other {#개의 메일}}을 삭제할까요?", - "clear_selection": "선택 취소" - }, - "permanent_delete": "영구 삭제", - "permanent_delete_confirm_title": "영구 삭제", - "permanent_delete_confirm_message": "이 메일은 영구적으로 삭제되며, 다시 복구할 수 없어요.", - "permanent_delete_confirm_batch_message": "선택한 {count, plural, other {#개의 메일}}이 영구적으로 삭제돼요. 이 작업은 되돌릴 수 없어요.", - "empty_folder": { - "button": "폴더 비우기", - "confirm_title": "폴더 비우기", - "confirm_message": "이 폴더의 모든 메일이 영구적으로 삭제돼요. 이 작업은 되돌릴 수 없어요.", - "confirm_button": "폴더 비우기", - "junk_hint": "스팸함을 비워 모든 메시지를 영구적으로 삭제할 수 있어요.", - "trash_hint": "휴지통을 비워 모든 메시지를 영구적으로 삭제할 수 있어요." - }, - "no_scheduled_emails": "예약된 메일 없음", - "no_scheduled_emails_description": "나중에 보내도록 예약한 메시지가 여기에 표시됩니다.", - "scheduled_count": "{count, plural, one {예약된 메일 1개} other {예약된 메일 #개}}", - "scheduled_actions_hint": "예약된 메시지는 예약 작업에서 취소, 재예약 또는 편집할 수 있습니다.", - "cancel_scheduled_send": "보내기 취소", - "reschedule_send": "다시 예약", - "cancel_and_edit": "취소하고 편집", - "cancel_and_compose_again": "취소하고 다시 작성", - "reschedule_prompt": "새 날짜/시간을 입력하세요. 예: 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "보낸 사람이 읽음 확인을 요청합니다:", - "send": "확인 보내기", - "ignore": "무시", - "sent": "읽음 확인을 보냈습니다.", - "send_failed": "읽음 확인을 보낼 수 없습니다", - "mdn_subject": "읽음: {subject}", - "mdn_body": "이것은 {recipient}(으)로 보낸 메시지에 대한 읽음 확인입니다.\n\n참고: 이 확인은 메시지가 수신자의 컴퓨터에 표시되었음을 알릴 뿐입니다. 수신자가 내용을 읽거나 이해했다는 보장은 없습니다." - }, - "no_email_selected": "메일이 선택되지 않았어요", - "no_email_description": "목록에서 메일을 선택하면 여기에 내용이 표시돼요", - "no_conversation_selected": "대화가 선택되지 않았어요", - "no_conversation_description": "목록에서 대화를 선택해 주세요", - "compose": "메일 쓰기", - "compose_hint": "새 메시지 작성", - "no_subject": "(제목 없음)", - "no_body_content": "(본문 내용 없음)", - "no_preview_available": "미리 보기를 사용할 수 없습니다", - "show_quoted_text": "인용된 텍스트 표시", - "hide_quoted_text": "인용된 텍스트 숨기기", - "loading_email": "메일을 불러오는 중...", - "loading": "불러오는 중...", - "reply": "답장", - "reply_all": "전체 답장", - "forward": "전달", - "delete": "삭제", - "archive": "보관", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "별표 달기", - "unstar": "별표 해제", - "mark_unread": "읽지 않은 상태로 표시", - "mark_read": "읽은 상태로 표시", - "unread": "안 읽음", - "read": "읽음", - "spam_short": "스팸", - "not_spam_short": "스팸 아님", - "move": "이동", - "print": "인쇄", - "view_source": "원본 보기", - "fullscreen": "전체 화면", - "exit_fullscreen": "전체 화면 종료", - "export_email": ".eml 파일로 내보내기", - "forward_as_attachment": "첨부 파일로 전달", - "import_email": ".eml 또는 .zip 가져오기", - "keyboard_shortcuts": "단축키 (?)", - "email_source": "메일 원본", - "draft_banner": "작성 중인 임시보관 메일이에요", - "edit_draft": "수정", - "copy_source": "클립보드에 복사", - "source_copied": "원본이 클립보드에 복사되었어요", - "attachments": "첨부파일", - "important": "중요", - "download": "다운로드", - "download_all": "모두 다운로드", - "from": "보낸 사람", - "to": "받는 사람", - "cc": "참조", - "bcc": "숨은 참조", - "date": "날짜", - "subject": "제목", - "show_details": "자세히 보기", - "hide_details": "간략히 보기", - "external_content_warning": "이미지와 외부 콘텐츠가 차단되었어요", - "load_external_content": "이미지 불러오기", - "trust_sender": "이 보낸 사람 항상 신뢰하기", - "back_to_list": "목록으로", - "view_contact": "연락처 보기", - "message_details": "메시지 상세 정보", - "more_reply_options": "답장 옵션 더보기", - "set_tag": "태그 설정", - "tag": "태그", - "more_actions": "작업 더보기", - "previous": "이전", - "next": "다음", - "move_to": "이동...", - "remove_tag": "태그 제거", - "tag_filter_placeholder": "태그 검색", - "tag_no_matches": "일치하는 태그 없음", - "more_count": "+{count}개 더보기", - "characters_count": "{count}자", - "quick_reply_placeholder": "간단하게 답장을 작성해 보세요...", - "more_options": "옵션 더보기", - "sending": "보내는 중...", - "security_authentication": "보안 및 인증", - "technical_details": "기술 세부 정보", - "message_id_label": "Message-ID:", - "reply_to_label": "Reply-To:", - "delivery_time_label": "수신 시간:", - "conversation_part_label": "포함된 대화:", - "previous_messages": "이전 메시지 {count}개", - "previous_messages_plural": "이전 메시지 {count}개", - "time": { - "day": "일", - "days": "일", - "hour": "시간", - "hours": "시간", - "minute": "분", - "minutes": "분" - }, - "unknown_sender": "알 수 없음", - "recipient_me": "나", - "recipient_and_others": "{name}님 외 {count}명", - "recipient_to_prefix": "받는 사람:", - "authentication": { - "title": "인증", - "status": { - "verified": "확인됨", - "warning": "주의", - "none": "인증되지 않음" - }, - "spf": { - "pass": "SPF 통과", - "fail": "SPF 실패", - "none": "SPF 없음" - }, - "dkim": { - "pass": "DKIM 유효", - "fail": "DKIM 무효", - "none": "DKIM 없음" - }, - "dmarc": { - "pass": "DMARC 통과", - "fail": "DMARC 실패", - "none": "DMARC 없음" - }, - "spam_score": "스팸 점수", - "tooltip_spf": "Sender Policy Framework: 발신 서버가 해당 도메인을 대신해 메일을 보낼 권한이 있는지 확인합니다", - "tooltip_dkim": "DomainKeys Identified Mail: 암호화 서명을 통해 메일이 전송 중에 변경되지 않았음을 확인합니다", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: SPF와 DKIM이 발신자 도메인과 일치하는지 확인하고 실패에 대한 정책을 설정합니다", - "policy": "정책", - "result": { - "pass": "통과", - "fail": "실패", - "softfail": "경미한 실패", - "neutral": "중립", - "permerror": "영구 오류", - "temperror": "일시적 오류", - "none": "없음" - } - }, - "headers": { - "routing": "라우팅", - "received": "수신됨", - "message_id": "메시지 ID", - "list_info": "목록 정보" - }, - "tooltips": { - "reply": "답장 (r)", - "reply_all": "전체 답장 (a)", - "forward": "전달 (f)", - "archive": "보관 (e)", - "delete": "삭제 (# 또는 Del)", - "star": "별표 달기 (s)", - "unstar": "별표 해제 (s)", - "compose": "메일 쓰기 (c)", - "previous": "이전 메일", - "next": "다음 메일", - "edit_draft": "임시보관 메일 수정" - }, - "spam": { - "button_title": "스팸 신고", - "not_spam_title": "정상 메일로 표시", - "toast_success": "스팸함으로 이동되었어요", - "toast_batch": "{count}개의 메일이 스팸함으로 이동되었어요", - "toast_undo": "실행 취소", - "toast_not_spam_success": "받은편지함으로 이동되었어요", - "toast_not_spam_batch": "{count}개의 메일이 받은편지함으로 이동되었어요", - "error": "스팸 신고에 실패했어요", - "error_not_spam": "메일 복구에 실패했어요" - }, - "unsubscribe_banner": { - "label": "뉴스레터", - "button": "구독 취소", - "confirm_title": "이 발신자의 메일 구독을 취소할까요?", - "confirm_button": "확인", - "cancel": "취소", - "success_http": "새 탭에서 구독 취소 페이지가 열렸어요", - "success_mailto": "수신 거부 이메일을 보냈습니다", - "confirm_message_http": "수신 거부 페이지가 새 탭에서 열립니다.", - "confirm_message_mailto": "발신자에게 수신 거부 이메일을 보냅니다.", - "error": "구독을 취소할 수 없어요", - "dismiss": "닫기" - }, - "calendar_invitation": { - "loading": "일정 세부 정보를 불러오는 중...", - "title": "캘린더 초대", - "published_title": "공개된 일정", - "response_title": "일정 응답", - "update_title": "일정 업데이트", - "counter_title": "시간 제안", - "refresh_title": "새로고침 요청", - "declined_counter_title": "시간 제안이 거절되었어요", - "cancelled_title": "일정이 취소되었어요", - "organizer": "주최자: {name}", - "organizer_label": "주최자:", - "attendees": "{count, plural, other {참석자 #명}}", - "accept": "수락", - "maybe": "미정", - "decline": "거절", - "add_to_calendar": "캘린더에 추가", - "added": "캘린더에 추가되었어요", - "rsvp_sent": "응답이 전송되었어요", - "parse_error": "초대장을 읽을 수 없어요", - "action_failed": "캘린더 작업을 완료할 수 없어요.", - "no_calendar": "캘린더를 사용할 수 없어요", - "published_info": "참고용으로 공유된 일정이에요.", - "response_info": "참석자의 응답이 포함된 메시지예요.", - "response_info_organizer": "참석자의 응답이 일정에 업데이트돼요.", - "update_info": "기존 일정이 업데이트된 메시지예요.", - "counter_info": "일정 변경을 제안하는 메시지예요.", - "counter_info_organizer": "참석자가 일정 변경을 제안했어요.", - "refresh_info": "최신 일정 세부 정보를 요청하는 메시지예요.", - "refresh_info_organizer": "참석자가 최신 일정 세부 정보를 요청했어요.", - "declined_counter_info": "주최자가 제안한 변경 사항을 거절했어요.", - "authentication_failed_info": "이 초대장의 메일 인증이 실패했어요. 캘린더 작업 시 주의가 필요해요.", - "authentication_missing_info": "이 초대장에는 확인된 메일 인증 정보가 없어요. 이상한 점이 있다면 주최자에게 확인해 보세요.", - "sender_mismatch_info": "초대장은 {sender}님이 보냈지만, 캘린더에 등록된 주최자는 {organizer}님이에요.", - "sender_mismatch_unverified_info": "초대장은 {sender}님이 보냈지만 캘린더의 주최자는 {organizer}님이며, 메시지를 검증할 수 없어요.", - "organizer_role": "내가 주최하는 일정이에요", - "your_response": "내 응답: {status}", - "response_needed": "응답이 필요해요", - "response_accepted": "수락함", - "response_tentative": "미정", - "response_declined": "거절함", - "response_delegated": "위임됨", - "actor_sent_info": "{name}님이 보냈어요.", - "actor_response_info": "{name}님이 {status}했어요.", - "actor_counter_info": "{name}님이 일정 변경을 제안했어요.", - "actor_refresh_info": "{name}님이 최신 일정 정보를 요청했어요.", - "actor_declined_counter_info": "{name}님이 변경 제안을 거절했어요.", - "actor_note": "메모: {comment}", - "actor_unknown": "알 수 없는 사용자", - "proposed_changes": "제안된 변경 사항", - "change_title": "제목", - "change_time": "시간", - "change_location": "위치", - "change_description": "설명", - "change_empty": "없음", - "change_from_to": "{before} -> {after}", - "apply_proposal": "제안된 변경 사항 적용", - "proposal_applied": "제안된 변경 사항이 적용되었어요.", - "review_proposal": "제안 검토", - "review_request": "요청 검토", - "view_in_calendar": "캘린더에서 보기", - "select_calendar": "캘린더 선택", - "already_in_calendar": "이미 내 캘린더에 있어요", - "request_info": "이 일정에 초대받았어요. 참석 가능 여부를 주최자에게 알려주세요.", - "cancel_info": "주최자가 이 일정을 취소했어요.", - "event_updated": "업데이트 #{sequence}", - "event_status_tentative": "미정", - "event_status_cancelled": "취소됨", - "collapse": "세부정보 숨기기", - "expand": "세부정보 표시" - }, - "send": "보내기", - "more": "더보기", - "details": { - "recipients_routing": "수신자 및 라우팅", - "authentication_security": "인증 및 보안", - "identifiers_threading": "식별자 및 스레드", - "mailing_list": "메일링 리스트", - "message_properties": "메시지 속성", - "sent": "보낸 시각", - "received": "받은 시각", - "delivery_time": "전달 시간", - "in_reply_to": "회신 대상", - "references": "참조", - "thread_id": "스레드 ID", - "size": "크기", - "mime_type": "MIME 타입", - "attachments_summary": "{count}개 파일 · {size}", - "list_id": "리스트 ID", - "list_help": "리스트 도움말", - "list_post": "리스트 게시", - "list_unsubscribe": "구독 해지", - "iprev": "역방향 DNS", - "spam_status": "스팸 상태", - "ai_verdict": "AI 판정", - "account": "계정", - "no_subject": "(제목 없음)" - }, - "scheduled_banner": "{date}에 보내도록 예약됨", - "scheduled_send_created": "메일 보내기가 예약되었습니다", - "cancel_scheduled_send": "보내기 취소", - "reschedule_send": "다시 예약", - "cancel_and_edit": "취소하고 편집", - "cancel_and_compose_again": "취소하고 다시 작성", - "reschedule_prompt": "새 날짜/시간을 입력하세요. 예: 2026-05-04T15:30", - "scheduled_actions_only": "예약됨 보기에서는 예약 작업만 사용할 수 있습니다", - "undo_send_scheduled": "메시지가 보내기로 예약되었습니다", - "undo_send": "보내기 실행 취소", - "contact_sidebar": { - "title": "연락처", - "close": "사이드바 닫기", - "action_email": "이메일", - "action_email_title": "이메일 보내기", - "action_copy": "복사", - "action_copy_title": "이메일 복사", - "action_edit_title": "연락처 편집", - "section_emails": "이메일", - "section_phones": "전화번호", - "section_organizations": "조직", - "section_addresses": "주소", - "section_notes": "메모", - "not_in_contacts": "연락처에 없음", - "add_to_contacts": "연락처에 추가", - "copied": "복사됨!", - "copy_failed": "복사하지 못했습니다" - }, - "send_now": "지금 보내기" - }, - "email_composer": { - "read_receipt_on": "읽음 확인 요청됨 (클릭하여 해제)", - "read_receipt_off": "읽음 확인 요청", - "dsn_on": "배달 알림 요청됨 (클릭하여 해제)", - "dsn_off": "배달 알림 요청", - "format_plain_text": "일반 텍스트로 전환", - "format_rich_text": "서식 있는 텍스트(HTML)로 전환", - "require_tls_on": "암호화 전송 필수 (클릭하여 해제)", - "require_tls_off": "암호화 전송 필수 (TLS)", - "new_message": "새 메시지", - "signature_edit_hint": "서명을 편집하려면 두 번 클릭하세요", - "reply": "답장", - "reply_all": "전체 답장", - "forward": "전달", - "reply_to": "답장", - "reply_all_to": "전체 답장", - "forward_message": "전달", - "from": "보내는 사람", - "to": "받는 사람", - "cc": "참조", - "bcc": "숨은 참조", - "subject": "제목", - "body_placeholder": "내용을 작성해 주세요...", - "send": "보내기", - "cancel": "취소", - "attach": "첨부", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "삭제", - "discard_draft_title": "임시보관 메일을 삭제할까요?", - "discard_draft_confirm": "저장되지 않은 내용이 있어요. 이 메일을 삭제할까요?", - "saving": "저장 중...", - "draft_saved": "임시보관함에 저장되었어요", - "save_failed": "저장에 실패했어요", - "to_placeholder": "받는 사람 이메일 주소", - "cc_placeholder": "참조 수신자", - "bcc_placeholder": "숨은 참조 수신자", - "subject_placeholder": "제목", - "cc_label": "참조:", - "bcc_label": "숨은 참조:", - "subject_label": "제목:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(제목 없음)", - "unknown_sender": "알 수 없음", - "quote": { - "reply_header": "{date}, {sender}님이 작성함:", - "forward_header": "---------- 전달된 메시지 ----------", - "from": "보낸 사람: {sender}", - "date": "날짜: {date}", - "subject": "제목: {subject}", - "to": "받는 사람: {recipients}" - }, - "remove_sub_address": "서브 어드레스 삭제", - "from_override": { - "toggle_off": "재정의", - "toggle_on": "재정의 취소", - "toggle_tooltip": "보낸 사람 이름과 주소를 자유롭게 편집하세요. 메일은 여전히 사용자의 ID를 통해 전송됩니다.", - "name_label": "보낸 사람 이름", - "name_placeholder": "이름", - "email_label": "보낸 사람 이메일 주소", - "email_placeholder": "alias@example.com", - "envelope_notice": "서버가 이 주소를 봉투 발신자로 허용하지 않으면 대신 {identity}이(가) 사용되며, 수신자는 Return-Path 헤더에서 이 주소를 볼 수 있습니다." - }, - "use_template": "템플릿", - "save_as_template": "템플릿으로 저장", - "validation": { - "recipient_required": "보내려면 받는 사람을 추가해 주세요", - "body_required": "메시지를 작성하거나 파일을 첨부해 주세요", - "attachments_uploading": "첨부 파일을 업로드하는 중입니다. 완료되면 자동으로 전송됩니다", - "attachment_upload_failed": "전송되지 않았습니다. 첨부 파일 업로드에 실패했습니다. 해당 파일을 제거하고 다시 시도하세요." - }, - "upload_progress": "{uploaded} / {total} 업로드 중", - "upload_cancel": "업로드 취소", - "upload_failed": "{filename} 파일 업로드에 실패했어요", - "attachment_too_large": "\"{name}\"이(가) 서버 허용 크기보다 커요(파일당 {max})", - "attachments_total_too_large": "첨부 파일이 서버의 메시지당 한도({max})를 넘어요", - "drop_files": "첨부할 파일을 여기에 놓아주세요", - "show_less": "간략히 보기", - "send_failed": "메일 전송에 실패했어요", - "send_timeout": "서버가 응답을 멈췄습니다. 다시 보내기 전에 보낸 편지함을 확인하세요. 메시지가 이미 발송되었을 수 있습니다.", - "continue_draft": "이어서 작성하기", - "close_draft_title": "임시보관함에 저장할까요?", - "close_draft_message": "저장되지 않은 내용이 있어요. 임시보관함에 저장할까요, 아니면 삭제할까요?", - "smime_sign_on": "S/MIME 서명 켜짐", - "smime_sign_off": "S/MIME 서명 켜기", - "smime_encrypt_on": "S/MIME 암호화 켜짐", - "smime_encrypt_off": "S/MIME 암호화 켜기", - "smime_encrypt_unavailable": "S/MIME 암호화를 사용할 수 없어요 – 수신자의 인증서가 필요해요", - "smime_unlock_title": "S/MIME 키 잠금 해제", - "smime_unlock_message": "S/MIME 서명 키의 잠금을 해제하려면 비밀번호를 입력해 주세요.", - "smime_unlock_button": "잠금 해제", - "smime_passphrase_placeholder": "비밀번호", - "forgot_attachment": { - "title": "첨부 파일을 잊으셨나요?", - "message": "메시지에 \"{keyword}\"이(가) 언급되어 있지만 파일이 첨부되지 않았습니다. 그래도 보내시겠습니까?", - "send_anyway": "그래도 보내기", - "back": "편집으로 돌아가기" - }, - "empty_subject": { - "title": "제목 없이 보낼까요?", - "message": "이 메시지에는 제목이 없습니다. 그래도 보낼까요?", - "dont_ask_again": "다시 묻지 않기", - "send_anyway": "그래도 보내기", - "back": "편집으로 돌아가기" - }, - "add_link": "링크 추가", - "link_url_prompt": "URL을 입력하세요", - "sending": "전송 중...", - "schedule_send": "보내기 예약", - "schedule_send_description": "서버가 이 메시지를 보낼 시간을 선택하세요.", - "schedule_send_required": "날짜와 시간을 선택하세요.", - "schedule_send_invalid": "유효한 날짜와 시간을 입력하세요.", - "schedule_send_future": "미래의 날짜와 시간을 선택하세요.", - "schedule_send_too_late": "이 시간은 서버가 허용하는 범위를 벗어납니다.", - "schedule_send_unsupported": "이 계정은 예약 보내기를 지원하지 않습니다.", - "schedule_send_cleanup_warning": "예약 보내기는 생성되었지만 초안 정리에 실패했습니다.", - "send_delay_unsupported": "이 계정은 보내기 지연을 지원하지 않습니다.", - "send_delay_unsupported_confirm": "이 계정은 보내기 지연을 지원하지 않습니다. 바로 보낼까요?", - "recipient_edit_email": "이메일 주소 편집", - "recipient_edit_name": "표시 이름 편집", - "recipient_email_placeholder": "이메일 주소", - "recipient_name_placeholder": "표시 이름", - "autocomplete_search_server": "서버에서 검색", - "autocomplete_searching": "검색 중...", - "toolbar": { - "bold": "굵게", - "italic": "기울임꼴", - "underline": "밑줄", - "strikethrough": "취소선", - "text_color": "글자 색", - "remove_color": "색 제거", - "heading_1": "제목 1", - "heading_2": "제목 2", - "bullet_list": "글머리 기호 목록", - "ordered_list": "번호 매기기 목록", - "quote": "인용", - "code_block": "코드 블록", - "align_left": "왼쪽 정렬", - "align_center": "가운데 정렬", - "align_right": "오른쪽 정렬", - "text_direction": "텍스트 방향 (RTL/LTR)", - "link": "링크", - "table": "표", - "clear_formatting": "서식 지우기", - "undo": "실행 취소", - "redo": "다시 실행", - "add_row_above": "위에 행 추가", - "add_row_below": "아래에 행 추가", - "add_column_before": "앞에 열 추가", - "add_column_after": "뒤에 열 추가", - "delete_row": "행 삭제", - "delete_column": "열 삭제", - "toggle_header_row": "머리글 행 전환", - "delete_table": "표 삭제", - "pick_size": "크기 선택", - "font_size": "글자 크기", - "font_size_default": "기본값", - "background_color": "배경색", - "remove_background_color": "배경색 제거" - }, - "send_filing_warning": "보냈지만 전송 후 정리에 실패했습니다. 오래된 임시 보관 메일이 남아 있을 수 있습니다." - }, - "confirm_dialog": { - "confirm": "확인", - "cancel": "취소" - }, - "common": { - "loading": "불러오는 중...", - "error": "오류", - "success": "성공", - "cancel": "취소", - "save": "저장", - "delete": "삭제", - "edit": "수정", - "close": "닫기", - "search": "검색", - "refresh": "새로고침", - "settings": "설정", - "help": "도움말", - "logout": "로그아웃", - "yes": "네", - "no": "아니요", - "unknown": "알 수 없음", - "app_title": "웹메일", - "reconnecting": "연결이 끊어졌어요. 다시 연결을 시도할게요...", - "rate_limited_title": "서버 인증이 일시적으로 제한되었어요.", - "rate_limited_detail": "Bulwark가 계정 잠금을 방지하기 위해 백그라운드 요청을 일시 중지했어요. {seconds}초 후에 다시 시도할게요.", - "rate_limited_action_title": "계정 잠금 방지를 위해 요청이 일시 중지되었어요.", - "rate_limited_action_detail": "추가 인증 요청을 보내기 전에 서버의 쿨다운이 끝날 때까지 대기 중이에요. {seconds}초 후에 다시 시도해 주세요.", - "pull_to_refresh": "당겨서 새로고침", - "release_to_refresh": "놓으면 새로고침", - "refreshing": "새로고침 중…" - }, - "notifications": { - "email_sent": "메일이 전송되었어요", - "email_deleted": "메일이 삭제되었어요", - "email_archived": "메일이 보관되었어요", - "emails_deleted": "{count, plural, other {이메일 #개 삭제됨}}", - "emails_archived": "{count, plural, other {이메일 #개 보관됨}}", - "error_archiving": "이메일을 보관하지 못했습니다", - "error_updating": "이메일을 업데이트하지 못했습니다", - "email_starred": "별표가 추가되었어요", - "email_unstarred": "별표가 해제되었어요", - "email_marked_read": "읽은 상태로 변경되었어요", - "email_marked_unread": "읽지 않은 상태로 변경되었어요", - "copied_to_clipboard": "클립보드에 복사되었어요", - "source_copied": "원본이 클립보드에 복사되었어요", - "error_sending": "메일 전송에 실패했어요", - "error_deleting": "메일 삭제에 실패했어요", - "error_loading": "메일을 불러오지 못했어요", - "new_email": "새 메일이 도착했어요", - "new_email_from": "{sender}님이 보낸 메일", - "click_to_view": "클릭해서 보기", - "email_moved": "메일이 이동되었어요", - "emails_moved": "{count}개의 메일이 이동되었어요", - "moved_to_mailbox": "{mailbox}함으로 이동되었어요", - "move_failed": "이동에 실패했어요", - "move_error": "선택한 폴더로 메일을 이동할 수 없어요", - "email_tagged": "태그가 추가되었어요", - "emails_tagged": "{count}개의 메일에 태그가 추가되었어요", - "tag_failed": "태그 추가에 실패했어요", - "identity_created": "보내는 사람이 추가되었어요", - "identity_updated": "보내는 사람 정보가 업데이트되었어요", - "identity_deleted": "보내는 사람이 삭제되었어요", - "identity_set_primary": "기본 보내는 사람이 변경되었어요", - "identity_create_failed": "보내는 사람 추가 실패: {error}", - "identity_update_failed": "보내는 사람 업데이트 실패: {error}", - "identity_delete_failed": "보내는 사람 삭제 실패: {error}", - "identity_unauthorized": "이 이메일 주소로 메일을 보낼 권한이 없어요", - "identity_not_found": "해당 계정을 찾을 수 없어요", - "vacation_saved": "부재중 자동 응답 설정이 저장되었어요", - "vacation_save_failed": "부재중 자동 응답 설정을 저장하지 못했어요", - "filters_saved": "필터가 저장되었어요", - "filters_save_failed": "필터 저장에 실패했어요", - "filters_deleted": "필터 규칙이 삭제되었어요", - "templates_exported": "템플릿을 성공적으로 내보냈어요", - "templates_imported": "{count, plural, other {#개의 템플릿}}을 가져왔어요", - "templates_import_errors": "일부 템플릿을 가져오지 못했어요", - "templates_import_empty": "파일에서 템플릿을 찾을 수 없어요", - "export_email_error": "메일 내보내기에 실패했어요", - "import_email_success": "메일을 성공적으로 가져왔어요", - "import_email_error": "메일 가져오기에 실패했어요" - }, - "date": { - "today": "오늘", - "yesterday": "어제", - "this_week": "이번 주", - "last_week": "지난주", - "this_month": "이번 달", - "older": "이전", - "just_now": "방금 전", - "minutes_ago": "{count}분 전", - "minutes_ago_plural": "{count}분 전", - "hours_ago": "{count}시간 전", - "hours_ago_plural": "{count}시간 전", - "days_ago": "{count}일 전", - "days_ago_plural": "{count}일 전" - }, - "language": { - "title": "언어", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "언어 선택", - "switch_to_english": "영어로 변경", - "switch_to_french": "프랑스어로 변경", - "switch_to_japanese": "일본어로 변경", - "switch_to_spanish": "스페인어로 변경", - "switch_to_italian": "이탈리아어로 변경", - "switch_to_german": "독일어로 변경", - "switch_to_dutch": "네덜란드어로 변경", - "switch_to_portuguese": "포르투갈어로 변경", - "switch_to_russian": "러시아어로 변경", - "switching": "언어를 변경하는 중...", - "switch": "언어 변경", - "current": "현재 언어", - "polish": "Polski", - "switch_to_polish": "폴란드어로 전환", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "페르시아어로 전환", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "몽골어로 전환" - }, - "settings": { - "title": "설정", - "back_to_mail": "메일로 돌아가기", - "save_success": "설정이 저장되었어요", - "import_success": "설정을 성공적으로 가져왔어요", - "import_error": "설정 가져오기에 실패했어요", - "reset_confirm": "모든 설정을 기본값으로 되돌릴까요?", - "unsaved_changes": "저장되지 않은 변경 사항이 있어요", - "discard_changes": "변경 사항을 취소할까요?", - "discard": "취소하기", - "keep_editing": "계속 수정하기", - "tabs": { - "appearance": "화면 설정", - "language": "언어, 지역 및 시간", - "email": "메일 동작", - "composer": "메일 쓰기", - "privacy": "개인정보 및 보안", - "account": "계정", - "identities": "보내는 사람", - "vacation": "부재중 자동 응답", - "advanced": "고급 설정", - "calendar": "캘린더", - "filters": "필터", - "templates": "템플릿", - "folders": "폴더", - "keywords": "태그", - "security": "보안", - "files": "파일", - "contacts": "연락처", - "encryption": "암호화", - "protocol_handlers": "기본 앱", - "sidebar_apps": "사이드바 앱", - "notifications": "알림", - "layout": "레이아웃", - "reading": "읽기", - "composing": "작성", - "downloads": "다운로드", - "content_senders": "콘텐츠 및 발신자", - "about_data": "정보 및 데이터", - "debug": "디버그" - }, - "tab_groups": { - "general": "일반", - "account": "계정 및 인증", - "organization": "메일 정리", - "apps": "앱", - "system": "시스템", - "appearance": "모양", - "mail": "메일", - "privacy": "개인정보 및 보안", - "advanced": "고급" - }, - "appearance": { - "message_list_order": { - "label": "메시지 목록 정렬 순서", - "description": "목록에서 메시지를 정렬하는 방식입니다. 정렬은 서버가 수행하므로 불러온 메시지뿐 아니라 폴더 전체에 적용됩니다.", - "preset": { - "chronological": "시간순 (최신 먼저)", - "unread_first": "읽지 않음 먼저", - "starred_first": "별표 먼저", - "tagged_first": "태그 먼저", - "custom": "사용자 지정" - }, - "tag_label": "태그", - "advanced_show": "고급: 정렬 단계 편집", - "advanced_hide": "고급 숨기기", - "criterion_label": "{n}단계 기준", - "direction_label": "{n}단계 방향", - "criteria": { - "unread": "읽지 않음", - "starred": "별표", - "tag": "태그", - "receivedAt": "받은 날짜", - "sentAt": "보낸 날짜", - "from": "보낸 사람", - "subject": "제목", - "size": "크기" - }, - "direction": { - "unread_first": "읽지 않음 먼저", - "read_first": "읽음 먼저", - "starred_first": "별표 먼저", - "unstarred_first": "별표 없음 먼저", - "tagged_first": "태그 있음 먼저", - "untagged_first": "태그 없음 먼저", - "newest_first": "최신 먼저", - "oldest_first": "오래된 순", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "큰 순서", - "smallest_first": "작은 순서" - }, - "add_level": "단계 추가", - "remove_level": "단계 제거", - "move_up": "위로 이동", - "move_down": "아래로 이동", - "tiebreak_note": "최대 3단계이며, 이후 단계는 동순위일 때 적용됩니다. '최신 먼저'는 항상 마지막에 적용됩니다.", - "scope": { - "label": "모든 폴더에 적용", - "description": "꺼져 있으면 정렬 순서가 받은편지함에만 적용되고 다른 폴더는 시간순으로 유지됩니다." - }, - "unsupported_note": "메일 서버가 읽음 상태, 별표 또는 태그 기준 정렬을 지원하지 않습니다. 해당 단계는 무시되며 목록은 시간순으로 유지됩니다." - }, - "title": "화면 설정", - "description": "웹메일의 디자인과 화면을 내 취향에 맞게 꾸며보세요", - "theme": { - "label": "테마", - "description": "선호하는 색상 테마를 선택해 주세요", - "light": "라이트", - "dark": "다크", - "system": "시스템" - }, - "language": { - "label": "언어", - "description": "사용할 언어를 선택해 주세요" - }, - "font_size": { - "label": "글꼴 크기", - "description": "읽기 편한 글꼴 크기로 조절해 보세요", - "small": "작게", - "medium": "보통", - "large": "크게" - }, - "list_density": { - "label": "간격", - "description": "UI의 간격과 여백을 조절할 수 있어요", - "extra_compact": "매우 좁게", - "compact": "좁게", - "regular": "보통", - "comfortable": "넓게" - }, - "animations": { - "label": "애니메이션 켜기", - "description": "부드러운 전환 효과를 사용해요" - }, - "toolbar_position": { - "label": "도구 모음 위치", - "description": "메일 작업 버튼(답장, 보관, 삭제 등)이 표시될 위치예요", - "top": "위쪽", - "below_subject": "제목 아래" - }, - "toolbar_labels": { - "label": "도구 모음 라벨 표시", - "description": "아이콘 옆에 텍스트 설명을 함께 보여줘요. 아이콘이 익숙해지면 꺼서 공간을 절약할 수 있어요." - }, - "hide_account_switcher": { - "label": "사이드바 계정 전환기 숨기기", - "description": "폴더 사이드바 상단의 계정 선택기를 숨겨요. 하단 내비게이션 바에서 여전히 계정을 전환할 수 있어요." - }, - "show_rail_account_list": { - "label": "내비게이션 바에 계정 아바타 표시", - "description": "내비게이션 바 아래에 계정 프로필을 표시해서 빠르게 전환할 수 있어요." - }, - "unified_mailbox": { - "label": "통합 메일함", - "description": "연결된 모든 계정의 통합 폴더(받은편지함, 보낸편지함 등)를 표시합니다", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "그룹 받은편지함 포함", - "description": "공유/그룹 받은편지함도 통합 보기에 포함합니다." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "컬러풀한 사이드바 아이콘", - "description": "폴더와 태그 아이콘을 유형별로 색상 표시합니다(받은편지함 파란색, 스팸 빨간색, 보낸편지함 녹색 등). 모노크롬 사이드바를 원하면 비활성화하세요." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "전체 메시지 수 표시", - "description": "읽지 않은 수와 함께 폴더 및 태그 옆에 전체 메시지 수를 표시합니다. 읽지 않은 수만 표시하려면 비활성화하세요." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro 인터페이스 (실험적)", - "description": "데스크톱 전용 파워 유저 레이아웃으로, 다중 탭 메시지 탐색과 계정 간 워크플로우를 지원합니다. 표준 인터페이스에는 영향이 없으며 언제든지 되돌릴 수 있습니다.", - "open_label": "Pro 인터페이스 열기", - "back_to_standard": "표준으로 돌아가기" - }, - "cross_unread": { - "label": "모든 계정: 읽지 않음", - "description": "모든 계정 섹션에 모든 계정의 읽지 않은 메일을 나열하는 항목을 표시합니다. 스팸, 보낸편지함, 보관함, 휴지통, 임시보관함을 제외한 모든 폴더가 대상입니다." - }, - "cross_starred": { - "label": "모든 계정: 별표", - "description": "모든 계정 섹션에 모든 계정의 별표 메일을 나열하는 항목을 표시합니다. 스팸, 보낸편지함, 보관함, 휴지통, 임시보관함을 제외한 모든 폴더가 대상입니다." - }, - "cross_all": { - "label": "모든 계정: 모든 메일", - "description": "모든 계정 섹션에 모든 계정의 모든 메일을 나열하는 항목을 표시합니다. 스팸, 보낸편지함, 보관함, 휴지통, 임시보관함을 제외한 모든 폴더가 대상입니다." - } - }, - "keywords": { - "title": "이메일 태그", - "description": "색상으로 이메일을 정리하기 위한 태그를 정의합니다. 서버에 JMAP 키워드로 저장됩니다.", - "add_keyword": "태그 추가", - "label_field": "표시 이름", - "label_placeholder": "예: 업무, 개인, 긴급", - "id_field": "태그 ID", - "id_placeholder": "예: work, personal", - "color_field": "색상", - "id_exists": "이 태그 ID는 이미 존재합니다", - "edit": "태그 편집", - "delete": "태그 삭제", - "save": "저장", - "add": "추가", - "cancel": "취소", - "migrating": "기존 이메일의 태그 업데이트 중…", - "migration_error": "기존 이메일의 태그 업데이트에 실패했습니다", - "nesting": { - "label": "중첩 태그", - "description": "태그를 다른 태그 아래에 중첩하고 사이드바에 트리로 표시합니다." - }, - "parent_field": "상위 태그", - "no_parent": "상위 태그 없음", - "too_long": "이 태그 경로가 너무 깁니다(최대 {max}자)", - "has_children_locked": "이 태그 아래에 다른 태그가 중첩되어 있어 이름과 상위 태그가 잠겨 있습니다. 먼저 옮기거나 삭제하세요.", - "has_children_delete": "이 태그 아래에 중첩된 태그를 먼저 삭제하세요", - "visibility_field": "사이드바 표시", - "visibility": { - "show": "표시", - "unread": "읽지 않음이 있을 때 표시", - "hide": "숨기기" - }, - "unrecognized": { - "title": "서버에서 태그 복구", - "description": "태그 이름과 색상은 이 브라우저에 저장되지만, 태그 자체는 JMAP 키워드로 메일에 저장돼요. 여기에 정의되지 않은 키워드를 메일에서 검색해서 다시 추가할 수 있어요.", - "scan": "태그 검색", - "rescan": "다시 검색", - "scanning": "검색 중… {total}개 중 {scanned}개", - "none": "메일에 있는 모든 태그가 여기에 이미 정의돼 있어요.", - "messages": "{count, plural, other {메일 #개}}", - "add_all": "{count}개 모두 추가", - "partial": "{total}개 중 최근 {scanned}개만 검색했어요. 오래된 메일에만 쓰인 태그는 빠져 있을 수 있어요.", - "error": "메일에서 태그를 검색하지 못했어요." - } - }, - "notifications": { - "test_sound": "알림음 테스트", - "sounds": { - "default": "기본 (비프음)", - "cheerful": "밝게", - "involved": "경쾌하게", - "swift": "빠르게", - "relax": "차분하게" - }, - "sound_selection": { - "title": "알림음", - "description": "알림이 올 때 재생할 소리를 선택해 주세요", - "choose": "알림음", - "choose_desc": "알림음을 선택하고 스피커 아이콘을 눌러 미리 들어보세요" - }, - "email": { - "title": "이메일 알림", - "description": "새 이메일이 올 때의 알림을 설정해요", - "enabled": "이메일 알림", - "enabled_desc": "새 이메일이 도착하면 알림을 보여줘요", - "sound": "알림음", - "sound_desc": "새 이메일이 도착하면 소리로 알려줘요", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "캘린더 알림", - "description": "캘린더 일정 알림을 설정해요", - "enabled": "일정 알림", - "enabled_desc": "다가오는 일정에 대한 알림을 보여줘요", - "sound": "알림음", - "sound_desc": "일정 알림이 올 때 소리로 알려줘요", - "invitation_parsing": "이메일 초대장 분석", - "invitation_parsing_desc": "이메일 첨부파일에서 캘린더 초대장을 감지하고 캘린더에 표시해요" - }, - "push": { - "confirm_disable_message": "이 사이트가 닫혀 있을 때 이 기기는 알림을 더 이상 받지 않습니다.", - "confirm_disable_title": "백그라운드 알림을 비활성화하시겠습니까?", - "description": "이 사이트가 닫혀 있을 때 새 메일에 대한 시스템 알림을 받습니다. Bulwark 푸시 릴레이를 통해 전달되며, 릴레이는 메일 내용을 절대 보지 않습니다.", - "disable": "비활성화", - "enable": "활성화", - "dismiss_aria": "알림 프롬프트 닫기", - "ios_hint": "iOS에서는 먼저 사이트를 홈 화면에 설치하세요. Safari는 설치된 PWA에만 Web Push를 전달합니다.", - "reenable": "다시 등록", - "relay_desc": "기본값은 호스팅된 Bulwark 릴레이입니다. 셀프 호스팅 시에만 변경하세요.", - "relay_label": "푸시 릴레이", - "relay_locked": "관리자에 의해 설정됨", - "relay_locked_desc": "푸시 릴레이 URL은 관리자가 설정했으며 변경할 수 없어요.", - "status_active": "이 기기에서 활성", - "status_busy": "처리 중…", - "status_inactive": "이 기기에서 활성화되지 않음", - "status_unsupported": "이 브라우저는 Web Push를 지원하지 않습니다", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "백그라운드 알림", - "devices_title": "등록된 기기", - "devices_desc": "이 계정의 백그라운드 알림을 받도록 등록된 모든 기기입니다. 해지하면 해당 기기는 즉시 수신을 멈추니다.", - "devices_loading": "등록된 기기를 불러오는 중…", - "devices_empty": "백그라운드 알림에 등록된 기기가 없습니다.", - "devices_error": "등록된 기기를 불러오지 못했습니다.", - "devices_refresh": "새로 고침", - "device_this": "이 기기", - "device_other": "다른 기기", - "device_status_active": "알림 전달 중", - "device_status_inactive": "알림을 전달한 적 없음", - "device_status_unknown": "이 릴레이에서 알 수 없음", - "device_expires": "{date} 만료", - "revoke": "해지", - "confirm_revoke_title": "이 기기를 해지할까요?", - "confirm_revoke_message": "즉시 백그라운드 알림 수신을 멈추게 됩니다. 해당 기기에서 푸시를 다시 켜면 새로 등록됩니다.", - "confirm_revoke_message_this": "이 기기는 즉시 백그라운드 알림 수신을 멈추게 됩니다." - } - }, - "language_region": { - "title": "언어, 지역 및 시간", - "description": "언어, 날짜 형식, 시간 형식 및 기타 지역 환경설정", - "language": { - "label": "언어", - "description": "사용할 언어를 선택해 주세요", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "날짜 형식", - "description": "이메일 목록에 날짜가 표시되는 방식", - "smart": "스마트 (지역에 맞춤)", - "relative": "상대 시간 (1시간 전, 2일 전)", - "full": "항상 전체 날짜", - "preview_today": "오늘:", - "preview_this_week": "이번 주:", - "preview_older": "이전:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "시간 형식", - "description": "12시간제 또는 24시간제를 선택해 주세요", - "12h": "12시간제", - "24h": "24시간제" - }, - "first_day": { - "label": "한 주의 시작", - "description": "일주일이 일요일부터 시작할지 월요일부터 시작할지 설정해요", - "sunday": "일요일", - "monday": "월요일" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "폴더 전환 시 검색 지우기", - "description": "폴더(받은편지함, 보낸편지함 등)를 클릭하면 활성 검색을 지우고 해당 폴더를 표시합니다. 꺼져 있으면 검색이 유지되고 이동한 폴더에서 다시 실행됩니다." - }, - "request_read_receipt": { - "label": "기본적으로 읽음 확인 요청", - "description": "새 메시지를 작성할 때 읽음 확인 요청을 미리 켭니다." - }, - "read_receipt_response": { - "label": "읽음 확인 요청에 응답", - "description": "수신 메시지가 읽음 확인을 요청할 때의 동작.", - "ask": "매번 묻기", - "always": "항상 보내기", - "never": "보내지 않음" - }, - "title": "메일 동작", - "description": "이메일 관련 동작 방식을 설정해 주세요", - "mark_read": { - "label": "읽은 상태로 표시", - "description": "메일을 열었을 때 언제 읽은 상태로 변경할지 설정해요", - "instant": "즉시", - "delay_3s": "3초 후", - "delay_5s": "5초 후", - "never": "표시하지 않음" - }, - "delete_action": { - "label": "삭제 동작", - "description": "메일을 삭제할 때의 동작을 선택해 주세요", - "trash": "휴지통으로 이동", - "trash_and_read": "휴지통으로 이동하고 읽음으로 표시", - "permanent": "영구 삭제", - "warning": "메일이 영구적으로 삭제되며 복구할 수 없어요. 주의해서 사용해 주세요." - }, - "message_spacing": { - "label": "메시지 여백", - "description": "읽기 화면에서 메시지 본문 주위의 여백", - "auto": "자동 (권장)", - "always": "항상 여백 추가", - "edge": "가장자리까지 채우기" - }, - "plain_text_font": { - "label": "일반 텍스트 글꼴", - "description": "일반 텍스트 이메일에 사용되는 글꼴", - "mono": "고정폭", - "sans": "기본(앱과 동일)" - }, - "archive_mode": { - "label": "보관 방식", - "description": "메일을 보관할 때 어떻게 분류할지 설정해요", - "single": "단일 폴더에 모두 보관", - "year": "연도별 폴더로 분류", - "month": "월별 폴더로 분류", - "reorganize": "기존 보관함 다시 정리하기", - "reorganize_success": "{count, plural, =0 {정리할 메일이 없어요} other {#개의 메일을 다시 정리했어요}}", - "reorganize_error": "보관함을 다시 정리하는 데 실패했어요" - }, - "permanently_delete_junk": { - "label": "스팸 영구 삭제", - "description": "스팸함에 있는 메일을 휴지통으로 보내지 않고 바로 영구 삭제해요" - }, - "mail_layout": { - "label": "메일 레이아웃", - "description": "기존의 분할 화면 방식과 Gmail 스타일의 집중형 화면 중 선택해 주세요.", - "split": "화면 분할", - "split_description": "메일 목록과 읽기 창을 나란히 보여줘요.", - "focus": "집중형 목록", - "focus_description": "메일을 한 줄로 보여주고, 클릭하면 폴더 사이드바는 남겨둔 채 메일 내용을 넓게 보여줘요.", - "horizontal": "읽기 창 하단 표시", - "horizontal_description": "메시지 목록을 상단에 표시하고 선택한 메시지를 하단 읽기 창에서 엽니다." - }, - "show_preview": { - "label": "미리보기 텍스트 표시", - "description": "목록에 메일 내용의 일부를 미리 보여줘요", - "focus_description": "집중형 한 줄 목록에서 메일 내용의 일부를 함께 보여줘요" - }, - "disable_threading": { - "label": "대화형 보기 끄기", - "description": "관련된 메일을 그룹으로 묶지 않고 개별 메시지로 보여줘요" - }, - "plain_text_mode": { - "label": "일반 텍스트만 사용", - "description": "서식 있는 텍스트 편집기를 끄고, 답장이나 전달을 포함한 모든 메일을 일반 텍스트로만 보내요" - }, - "auto_select_reply_identity": { - "label": "답장 시 보내는 사람 자동 선택", - "description": "답장할 때 메일을 받았던 주소로 보내는 사람을 자동으로 변경해요" - }, - "reply_identity_match": { - "label": "수신 주소 일치 방식", - "description": "어떤 수신 주소를 내 주소로 간주할지 정합니다. '정확한 주소만'은 설정된 ID 중에서만 선택합니다. '같은 도메인'은 ID 도메인의 다른 주소도 캐치올 별칭으로 간주하여 보낸 사람 헤더를 해당 주소로 바꿉니다. 해당 주소가 배포 목록이라면 정확한 주소를 선택하세요.", - "exact": "정확한 주소만", - "domain": "내 도메인의 모든 주소" - }, - "signature_position": { - "label": "서명 위치", - "description": "답장과 전달에서 서명을 삽입할 위치. 인용된 텍스트 위에 두면 답장의 마무리처럼 자연스럽게 읽히고, 아래에 두면 원본 메시지가 이어져 보입니다.", - "above_quote": "인용 텍스트 앞", - "below_quote": "인용 텍스트 뒤" - }, - "signature_separator": { - "label": "서명 구분선", - "description": "서명 앞에 표준 구분선 \"-- \" (RFC 3676)을 추가합니다. 본문에서 바로 서명으로 이어지길 원하면 해제하세요." - }, - "sub_address_delimiter": { - "label": "서브 주소 구분자", - "description": "사용자 이름과 서브 주소 태그를 나누는 문자예요. 메일 서버가 사용하는 구분자에 맞춰 주세요 (예: user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "사용자 지정…", - "custom_input_label": "사용자 지정 구분 문자" - }, - "attachment_click_action": { - "label": "첨부파일 클릭 동작", - "description": "파일을 클릭했을 때 미리보기를 할지, 바로 다운로드할지 선택해 주세요", - "preview": "가능하면 미리보기", - "download": "즉시 다운로드" - }, - "attachment_position": { - "label": "첨부파일 위치", - "description": "메일 상단에서 첨부파일을 어디에 표시할지 설정해요", - "beside-sender": "보낸 사람 옆", - "below-header": "제목 아래" - }, - "emails_per_page": { - "10": "10개", - "25": "25개", - "50": "50개", - "100": "100개", - "label": "페이지당 메일 수", - "description": "한 번에 불러올 메일 개수를 설정해요" - }, - "always_light_mode": { - "label": "메일 내용은 항상 라이트 모드로 보기", - "description": "다크 모드를 사용할 때도 메일 본문은 라이트 모드로 표시해서 화면이 깨지는 걸 방지해요" - }, - "external_content": { - "label": "외부 콘텐츠", - "description": "이미지나 외부 콘텐츠를 어떻게 처리할지 설정해요", - "ask": "항상 묻기", - "block": "항상 차단", - "allow": "항상 허용" - }, - "trusted_senders": { - "label": "신뢰하는 보낸 사람", - "description": "이미지를 자동으로 불러올 보낸 사람을 관리해요", - "count_zero": "없음", - "count_one": "1명", - "count_other": "{count}명", - "modal_title": "신뢰하는 보낸 사람", - "empty_title": "아직 신뢰하는 보낸 사람이 없어요", - "empty_description": "이미지가 차단된 메일에서 \"이 보낸 사람 항상 신뢰하기\"를 클릭하면 여기에 추가돼요.", - "add_manually": "직접 추가하기", - "add_button": "추가", - "add_placeholder": "이메일 주소 입력", - "search_placeholder": "보낸 사람 검색...", - "no_results": "검색된 보낸 사람이 없어요", - "remove": "삭제", - "close": "닫기", - "invalid_email": "올바른 이메일 주소를 입력해 주세요", - "already_added": "이미 신뢰하는 보낸 사람으로 추가되어 있어요", - "save_error": "저장에 실패했습니다 - 자세한 내용은 연락처 디버그 로그를 확인하세요", - "use_address_book_label": "주소록과 동기화", - "use_address_book_description": "신뢰 발신자를 전용 \"신뢰 발신자\" 주소록에 저장하여 모든 기기에서 동기화합니다" - }, - "hover_actions": { - "label": "빠른 호버 액션", - "description": "메일 목록에서 마우스를 올렸을 때 표시할 빠른 액션을 선택해 주세요", - "delete": "삭제", - "star": "별표 달기 / 해제", - "mark_read": "읽음 / 안 읽음 표시", - "archive": "보관", - "tag": "태그", - "spam": "스팸으로 표시", - "not_spam": "스팸 아님", - "none_selected": "선택된 액션 없음", - "mode_label": "표시 모드", - "mode_inline": "목록 안쪽", - "mode_floating": "플로팅", - "corner_label": "플로팅 위치", - "corner_top-left": "왼쪽 위", - "corner_top-right": "오른쪽 위", - "corner_bottom-left": "왼쪽 아래", - "corner_bottom-right": "오른쪽 아래" - }, - "default_mail_program": { - "label": "기본 메일 프로그램 설정", - "description": "mailto: 링크를 클릭할 때 {appName}이(가) 기본으로 열리도록 설정해요", - "button": "기본값으로 설정", - "success": "브라우저에서 기본 설정 팝업이 뜰 거예요", - "error": "이 브라우저에서는 이 기능을 지원하지 않아요" - }, - "attachment_reminder": { - "label": "첨부 파일 알림", - "description": "메시지에 첨부 파일이 언급되었지만 첨부되지 않은 경우 보내기 전에 경고합니다", - "keywords_label": "트리거 키워드", - "keywords_description": "메시지에서 발견되면 알림을 표시하는 단어 또는 구문", - "add_placeholder": "키워드 추가...", - "add": "Add", - "remove": "삭제" - }, - "empty_subject_warning": { - "label": "빈 제목 경고", - "description": "제목 없는 메시지를 보내기 전에 확인을 요청합니다." - }, - "hide_inline_image_attachments": { - "label": "첨부 파일 목록에서 인라인 이미지 숨기기", - "description": "메시지 본문에 포함된 이미지는 별도의 첨부 파일로 표시되지 않습니다" - }, - "attachment_image_previews": { - "label": "첨부 파일에 이미지 미리보기 표시", - "description": "이미지 첨부 파일을 일반 파일 아이콘 대신 썸네일 카드로 표시합니다" - }, - "send_delay": { - "label": "보내기 취소 / 보내기 지연", - "description": "일반 보내기를 서버 측 짧은 시간 동안 지연합니다.", - "off": "끔", - "seconds": "{seconds}초", - "unsupported": "현재 계정은 지연 보내기 지원을 알리지 않습니다. 설정은 다른 계정을 위해 계속 저장됩니다." - }, - "return_to_list_after_action": { - "label": "삭제 또는 읽지 않음으로 표시 후 목록으로 돌아가기", - "description": "열려 있는 메시지를 삭제하거나 읽지 않음으로 표시한 후 다음 메시지를 열지 않고 메시지 목록으로 돌아갑니다." - }, - "swipe_right_action": { - "label": "오른쪽으로 스와이프 동작 (모바일)", - "description": "터치 기기에서 목록의 메시지를 오른쪽으로 스와이프할 때 실행되는 동작입니다." - }, - "swipe_left_action": { - "label": "왼쪽으로 스와이프 동작 (모바일)", - "description": "터치 기기에서 목록의 메시지를 왼쪽으로 스와이프할 때 실행되는 동작입니다." - }, - "swipe_actions": { - "none": "없음", - "archive": "보관", - "delete": "삭제", - "mark_read": "읽음 상태 전환", - "star": "별표 전환", - "spam": "스팸으로 표시" - }, - "rtl_editing": { - "label": "오른쪽에서 왼쪽 편집 지원", - "description": "편집기 도구 모음에 방향 버튼을 추가하여 단락을 왼쪽에서 오른쪽 또는 오른쪽에서 왼쪽으로 설정할 수 있습니다" - } - }, - "composer": { - "title": "메일 쓰기", - "description": "메일 작성과 관련된 설정을 확인해 보세요", - "autosave": { - "label": "자동 저장 간격", - "description": "작성 중인 메일을 임시보관함에 자동으로 저장하는 간격이에요", - "30s": "30초마다", - "1m": "1분마다", - "2m": "2분마다", - "5m": "5분마다" - }, - "send_confirmation": { - "label": "전송 전 확인", - "description": "메일을 보내기 전에 한 번 더 물어볼까요?" - }, - "default_reply": { - "label": "기본 답장 모드", - "description": "답장 버튼을 눌렀을 때의 기본 동작을 설정해요", - "reply": "답장", - "reply_all": "전체 답장" - } - }, - "privacy": { - "title": "개인정보 및 보안", - "description": "개인정보와 보안 설정을 관리해 보세요", - "external_images": { - "label": "외부 이미지 차단", - "description": "외부 이미지를 통한 추적을 방지해요" - }, - "session_timeout": { - "label": "세션 만료 시간", - "description": "일정 시간 동안 사용하지 않으면 자동으로 로그아웃돼요", - "never": "사용 안 함", - "30m": "30분", - "1h": "1시간", - "4h": "4시간" - }, - "clear_cache": { - "label": "캐시 지우기", - "description": "저장된 임시 데이터와 캐시를 모두 삭제해요", - "button": "캐시 지우기", - "confirm": "정말 캐시를 모두 지울까요?", - "success": "캐시가 깔끔하게 지워졌어요!" - } - }, - "account": { - "title": "계정", - "description": "내 계정 정보를 확인해 보세요", - "name_label": "표시 이름", - "username_label": "사용자 이름", - "account_type_label": "계정 유형", - "auth_method_label": "인증 방법", - "auth_method_oauth": "SSO (OAuth/OIDC)", - "auth_method_basic": "비밀번호", - "demo_account": "데모 계정", - "email": { - "label": "이메일 주소", - "value": "{email}" - }, - "server": { - "label": "JMAP 서버", - "value": "{server}" - }, - "storage": { - "label": "저장 공간 사용량", - "used": "전체 {total} 중 {used} 사용", - "percentage": "{percent}% 사용 중" - }, - "last_sync": { - "label": "마지막 동기화", - "value": "{time}" - }, - "accounts": { - "title": "로그인된 계정", - "description": "끌어서 계정 드롭다운에 표시되는 순서를 변경합니다", - "active": "현재 활성 계정", - "default_badge": "기본 계정", - "set_default": "기본으로 설정", - "switch_to": "이 계정으로 전환", - "move_up": "위로 이동", - "move_down": "아래로 이동", - "drag_handle": "끌어서 순서 변경", - "add": "계정 추가" - }, - "shared_accounts": { - "title": "나와 공유됨", - "description": "관리할 수 있는 그룹 및 공유 계정입니다. 하나를 선택하여 필터, 부재 중 자동 응답, 캘린더, 연락처를 편집하세요.", - "shared_label": "공유 계정" - } - }, - "security": { - "title": "계정 보안", - "description": "비밀번호, 2단계 인증 및 보안 설정을 관리해요", - "detecting": "서버 기능을 확인하는 중...", - "not_available": "이 메일 서버에서는 계정 보안 관리를 사용할 수 없어요. 필요한 권한이 비활성화되어 있을 수 있어요. 자세한 내용은 문서를 참고해 주세요.", - "password": { - "title": "비밀번호 변경", - "current": "현재 비밀번호", - "new": "새 비밀번호", - "confirm": "새 비밀번호 확인", - "submit": "비밀번호 변경", - "success": "비밀번호가 성공적으로 변경되었어요", - "error_title": "비밀번호 변경 실패", - "error_mismatch": "새 비밀번호가 서로 일치하지 않아요", - "error_min_length": "비밀번호는 최소 8자 이상이어야 해요", - "error_generic": "비밀번호를 변경하지 못했어요" - }, - "display_name": { - "label": "표시 이름", - "description": "서버에 표시되는 내 이름이에요", - "placeholder": "표시할 이름을 입력해 주세요", - "save": "저장", - "success": "표시 이름이 업데이트되었어요", - "error": "표시 이름을 업데이트하지 못했어요" - }, - "totp": { - "section_title": "2단계 인증 (2FA)", - "label": "TOTP 인증", - "description": "시간 기반 일회용 비밀번호로 보안을 한 층 더 강화해 보세요", - "active": "사용 중", - "inactive": "사용 안 함", - "enabled": "2단계 인증이 켜졌어요", - "disabled": "2단계 인증이 꺼졌어요", - "enable_error": "2단계 인증을 켜지 못했어요", - "disable_error": "2단계 인증을 끄지 못했어요", - "setup_instructions": "이 URL을 인증 앱(Google Authenticator, Authy 등)에 복사해 주세요:", - "verification_code": "확인 코드", - "confirm": "확인", - "disable": "비활성화", - "disable_confirm_prompt": "2단계 인증을 비활성화하려면 비밀번호를 입력하세요.", - "password_required": "비밀번호가 필요합니다", - "code_required": "확인 코드가 필요합니다", - "code_invalid": "유효하지 않은 확인 코드입니다. 인증 앱을 확인하고 다시 시도하세요." - }, - "app_passwords": { - "title": "앱 비밀번호", - "description": "2단계 인증을 지원하지 않는 앱을 위한 전용 비밀번호를 만드세요", - "add": "추가", - "create": "만들기", - "cancel": "취소", - "generate": "생성", - "name_label": "앱 이름", - "name_placeholder": "예: Thunderbird, iPhone Mail", - "password_label": "비밀번호 (비워두면 자동으로 생성돼요)", - "password_placeholder": "비워두면 자동 생성", - "added": "앱 비밀번호가 만들어졌어요", - "removed": "앱 비밀번호가 삭제되었어요", - "add_error": "앱 비밀번호를 만들지 못했어요", - "remove_error": "앱 비밀번호를 삭제하지 못했어요", - "none": "설정된 앱 비밀번호가 없어요", - "done": "완료", - "expires_label": "만료 (선택 사항)", - "copy_now_warning": "지금 이 비밀번호를 복사하세요 - 다시 표시되지 않습니다.", - "allowed_ips_label": "허용된 IP(선택)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "쉼표 또는 공백으로 구분합니다. 비워두면 모든 IP가 허용됩니다." - }, - "api_keys": { - "title": "API 키", - "description": "서버와 직접 통신하는 스크립트 및 통합용 API 키를 만듭니다", - "name_label": "키 이름", - "name_placeholder": "예: 백업 스크립트, CI 러너", - "copy_now_warning": "이 API 키를 지금 복사하세요 - 다시 표시되지 않습니다.", - "added": "API 키가 생성되었습니다", - "removed": "API 키가 삭제되었습니다", - "add_error": "API 키 생성에 실패했습니다", - "remove_error": "API 키 삭제에 실패했습니다", - "none": "구성된 API 키가 없습니다" - }, - "encryption": { - "section_title": "저장 데이터 암호화", - "label": "이메일 암호화", - "description": "서버에 저장된 이메일을 암호화해서 프라이버시를 보호해요", - "active": "{type} 암호화 사용 중", - "inactive": "사용 안 함", - "enabled": "저장 데이터 암호화가 켜졌어요", - "disabled_success": "저장 데이터 암호화가 꺼졌어요", - "error": "암호화 설정을 업데이트하지 못했어요", - "algorithm_label": "암호화 알고리즘", - "encrypt_on_append": "새 메일을 업로드할 때 암호화", - "allow_spam_training": "메일을 암호화하기 전에 스팸 학습 허용", - "enable_encryption": "암호화 사용", - "enable_tooltip": "이 키로 암호화 사용", - "configure_title": "이 키로 암호화 사용", - "enable_button": "사용", - "disable_tooltip": "암호화 사용 안 함", - "enabled_success": "저장 데이터 암호화가 켜졌어요" - }, - "email_client": { - "title": "이메일 클라이언트 설정", - "description": "데스크톱이나 모바일 이메일 클라이언트(Thunderbird, Apple Mail, Outlook 등)를 설정할 때 사용할 정보예요", - "jmap_username_label": "JMAP 사용자 이름", - "copy": "복사", - "copied": "복사되었어요", - "password_instructions": "위의 JMAP 사용자 이름과 앱 비밀번호를 사용해서 이메일 클라이언트에 로그인하세요. 아직 만들지 않았다면 위에서 앱 비밀번호를 먼저 만들어 주세요." - }, - "link_device": { - "title": "모바일 앱 연결", - "description": "아무것도 입력하지 않고 Bulwark Mail 모바일 앱에 로그인하세요. 여기에서 QR 코드를 생성하고 앱의 로그인 화면에서 스캔하세요.", - "generate": "QR 코드 표시", - "regenerate": "새 코드 표시", - "instructions": "Bulwark Mail 앱을 열고 로그인 화면에서 \"QR 코드 스캔\"을 누른 다음 카메라를 여기에 비추세요.", - "expires_in": "이 코드는 {seconds}초 후에 만료됩니다. 한 번만 사용할 수 있습니다.", - "expired": "이 코드는 만료되었습니다.", - "generating": "생성 중…", - "error": "페어링 코드를 만들지 못했습니다. 다시 시도해 주세요." - }, - "public_keys": { - "name_label": "공개 키 이름", - "name_placeholder": "예: 내 키", - "key_label": "공개 키(S/MIME 또는 PGP ASCII 형식)", - "key_placeholder": "여기에 공개 키를 붙여넣으세요", - "none": "설정된 공개 키가 없어요" - } - }, - "identities": { - "title": "보내는 사람", - "description": "메일을 보낼 때 사용할 이메일 주소를 관리해요", - "identities_count": { - "label": "내 이메일 주소", - "description": "메일을 보낼 수 있도록 설정된 주소들이에요", - "count_zero": "설정된 주소가 없어요", - "count_one": "1개의 주소", - "count_other": "{count}개의 주소" - }, - "manage": "보내는 사람 관리", - "sub_addressing": { - "label": "서브 어드레싱", - "description": "user+tag@domain.com 처럼 태그를 사용해서 수신 메일을 정리해 보세요", - "learn_more": "자세히 알아보기" - } - }, - "vacation": { - "title": "부재중 자동 응답", - "description": "자리를 비웠을 때 도착한 메일에 자동으로 답장을 보내요", - "loading": "자동 응답 설정을 불러오는 중...", - "not_supported": "사용 중인 메일 서버는 자동 응답 기능을 지원하지 않아요.", - "fetch_error": "자동 응답 설정을 불러오지 못했어요. 다시 시도해 주세요.", - "status": { - "label": "자동 응답", - "description": "나에게 메일을 보낸 사람에게 자동으로 답장을 보내요", - "active": "사용 중", - "inactive": "사용 안 함" - }, - "date_range": { - "title": "기간 설정", - "description": "자동 응답을 보낼 특정 기간을 설정할 수 있어요", - "start": "시작일", - "start_description": "비워두면 지금부터 바로 시작해요", - "end": "종료일", - "end_description": "비워두면 계속해서 켜져 있어요" - }, - "message": { - "title": "자동 응답 메시지", - "description": "답장으로 보낼 메시지 내용을 작성해 주세요", - "subject_label": "제목", - "subject_description": "자동 응답 메일의 제목이에요", - "subject_placeholder": "부재중 알림", - "body_label": "메시지 본문", - "body_description": "일반 텍스트로 보낼 메시지 내용이에요", - "body_placeholder": "메일을 보내주셔서 감사합니다. 현재 자리를 비우고 있어 돌아오는 대로 답변드리겠습니다.", - "html_label": "서식 있는 메시지 (HTML)", - "html_description": "링크와 스타일이 포함된 서식 있는 버전을 추가합니다. 메일 클라이언트가 이를 표시할 수 없는 수신자에게는 위의 일반 텍스트가 표시됩니다.", - "html_placeholder": "서식 있는 부재중 자동 회신 작성…" - }, - "preview": { - "title": "미리보기", - "show": "미리보기 열기", - "hide": "미리보기 닫기" - }, - "save": "변경 사항 저장", - "saving": "저장 중...", - "warnings": { - "end_before_start": "종료일은 시작일보다 나중이어야 해요", - "start_in_past": "시작일이 이미 지났어요", - "empty_body": "메시지 내용이 비어있어요. 상대방에게 빈 메일이 가게 돼요", - "subject_too_long": "제목이 서버에 비해 너무 깁니다(최대 {max}바이트).", - "body_too_long": "메시지가 서버에 비해 너무 깁니다(서식 포함 최대 {max}바이트)." - } - }, - "folders": { - "role_memos": "메모", - "title": "폴더", - "description": "이메일 폴더를 관리하고 기본 역할을 지정해 보세요", - "folder_list": "내 폴더", - "folder_list_description": "폴더 아이콘을 클릭해서 설정할 수 있어요", - "standard_roles": "기본 폴더 역할", - "standard_roles_description": "받은편지함, 보낸편지함, 휴지통 등 기본 역할로 사용할 폴더를 지정해요", - "role_inbox": "받은편지함", - "role_drafts": "임시보관함", - "role_sent": "보낸편지함", - "role_trash": "휴지통", - "role_junk": "스팸/정크함", - "role_archive": "보관함", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "없음", - "create_folder": "폴더 만들기", - "create_subfolder": "하위 폴더 만들기", - "reorder": "드래그하여 순서 변경", - "reorder_error": "폴더 순서 변경에 실패했습니다", - "move_error": "폴더 이동에 실패했습니다", - "subfolder_of": "{name} 내부에 생성", - "subfolder_name": "하위 폴더 이름", - "new_folder_name": "폴더 이름", - "rename": "이름 바꾸기", - "change_icon": "아이콘 바꾸기", - "delete": "삭제", - "confirm_delete": "정말 \"{name}\" 폴더를 삭제할까요? 폴더 안의 메일은 휴지통으로 이동돼요.", - "create": "만들기", - "cancel": "취소", - "no_folders": "만든 폴더가 없어요", - "cannot_delete_role": "기본 역할이 지정된 폴더는 삭제할 수 없어요. 먼저 역할을 해제해 주세요.", - "folder_created": "폴더가 만들어졌어요", - "folder_renamed": "폴더 이름이 변경되었어요", - "folder_deleted": "폴더가 삭제되었어요", - "role_updated": "폴더 역할이 업데이트되었어요", - "error_create": "폴더를 만들지 못했어요", - "error_rename": "폴더 이름을 바꾸지 못했어요", - "error_delete": "폴더를 삭제하지 못했어요", - "error_delete_has_children": "안에 하위 폴더가 있어서 삭제할 수 없어요. 먼저 이동하거나 삭제해 주세요.", - "error_delete_has_email": "안에 메일이 있어서 삭제할 수 없어요. 메일을 이동하거나 삭제해 주세요.", - "error_role": "폴더 역할을 업데이트하지 못했어요" - }, - "advanced": { - "title": "고급 설정", - "description": "고급 옵션 및 개발자 설정이에요", - "debug_mode": { - "label": "디버그 모드", - "description": "문제 해결을 위해 자세한 로그를 기록해요" - }, - "debug_categories": { - "description": "기록할 카테고리를 선택해 주세요. 필요 없는 항목은 꺼두면 좋아요.", - "jmap": "JMAP 클라이언트", - "jmap_description": "편지함 작업, 메일 가져오기, JMAP 프로토콜 요청", - "calendar": "캘린더", - "calendar_description": "일정, 가져오기, 예약 메시지", - "tasks": "할 일(Tasks)", - "tasks_description": "할 일 생성, 가져오기 및 업데이트", - "auth": "인증", - "auth_description": "로그인, TOTP, 토큰 교환 및 세션 관리", - "filters": "필터", - "filters_description": "Sieve 필터 규칙 및 자동 응답 스크립트", - "email": "이메일 보기", - "email_description": "이메일 렌더링, TNEF 처리 및 읽음 표시", - "push": "푸시 알림", - "push_description": "푸시 알림 설정 및 전송", - "contacts": "연락처 및 주소록", - "contacts_description": "연락처 동기화, 주소록 작업, 신뢰 발신자" - }, - "settings_sync": { - "label": "설정 동기화", - "description": "여러 브라우저나 기기에서 내 설정을 동기화해요" - }, - "sender_favicons": { - "label": "보낸 사람 파비콘", - "description": "비즈니스 메일의 경우 해당 웹사이트의 아이콘을 프로필 사진으로 보여줘요" - }, - "show_avatars_in_junk": { - "label": "스팸함에서 아바타 표시", - "description": "스팸함의 보낸 사람에 대한 프로필 이미지와 파비콘을 표시해요. 피싱 메일이 정상적인 메일처럼 보이지 않도록 기본적으로 꺼져 있어요." - }, - "keyboard_shortcuts": { - "label": "단축키", - "description": "사용 가능한 키보드 단축키를 확인해 보세요", - "button": "단축키 보기" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "설정 초기화", - "description": "모든 설정을 기본값으로 되돌려요", - "button": "기본값으로 초기화" - }, - "export_settings": { - "label": "설정 내보내기", - "description": "내 설정을 JSON 파일로 다운로드해요", - "button": "내보내기" - }, - "import_settings": { - "label": "설정 가져오기", - "description": "JSON 파일에서 설정을 불러와요", - "button": "가져오기" - }, - "about": { - "title": "Bulwark Webmail 정보" - } - }, - "sidebar_apps": { - "title": "사이드바 앱", - "description": "사이드바의 커스텀 앱과 링크를 관리해요", - "keep_loaded": "앱 실행 유지", - "keep_loaded_description": "인라인 앱을 전환할 때 매번 새로고침되지 않도록 백그라운드에서 실행 상태를 유지해요", - "managed_title": "관리자가 제공", - "managed_description": "이 앱은 모든 사용자를 위해 설정되었으며 변경할 수 없습니다.", - "manage_title": "커스텀 앱", - "manage_description": "사이드바에 나만의 앱을 추가, 수정, 삭제해 보세요" - }, - "contacts": { - "title": "연락처", - "description": "연락처를 가져오거나 내보낼 수 있어요", - "import_label": "연락처 가져오기", - "import_description": "vCard(.vcf) 파일에서 연락처를 가져와요", - "export_label": "연락처 내보내기", - "export_description": "모든 연락처를 vCard(.vcf) 파일로 저장해요", - "manage_title": "주소록", - "manage_description": "주소록 이름 변경", - "no_address_books": "주소록을 찾을 수 없음", - "categories_title": "카테고리", - "categories_description": "연락처 카테고리 이름 변경", - "no_categories": "카테고리를 찾을 수 없음", - "group_by_letter_description": "연락처 목록에 알파벳순 섹션 헤더 표시", - "group_by_letter_label": "첫 글자로 그룹화", - "sort_by_last_name_label": "성으로 정렬", - "sort_by_last_name_description": "가족 구성원이 함께 표시되도록 연락처 목록을 성 기준으로 정렬합니다" - }, - "downloads": { - "title": "다운로드", - "description": "다운로드한 이메일과 첨부 파일의 이름을 지정하는 방식을 사용자 정의합니다.", - "reset": "기본값 복원", - "preview": "미리 보기:", - "email_template": { - "label": "이메일 파일 이름 (.eml)", - "description": "이메일을 내보내거나 파일 시스템으로 드래그할 때 사용하는 템플릿입니다. .eml 확장자는 자동으로 추가됩니다." - }, - "attachment_template": { - "label": "첨부 파일 이름", - "description": "첨부 파일을 다운로드하거나 드래그할 때 사용하는 템플릿입니다. '{filename}'과 '{ext}'를 생략하면 원래 확장자가 유지됩니다." - }, - "bundle_template": { - "label": "다중 이메일 .zip 파일 이름", - "description": "여러 선택한 이메일을 하나의 .zip 아카이브로 드래그하거나 다운로드할 때 사용하는 템플릿입니다. .zip 확장자는 자동으로 추가됩니다." - }, - "spaces": { - "label": "공백", - "description": "결과 파일 이름의 공백을 다른 문자로 바꿉니다.", - "keep": "공백 유지", - "underscore": "_로 바꾸기", - "dash": "-로 바꾸기" - }, - "lowercase": { - "label": "소문자", - "description": "전체 파일 이름을 소문자로 강제 변환합니다." - }, - "strip_diacritics": { - "label": "발음 구별 부호 제거", - "description": "악센트가 있는 문자를 ASCII 등가 문자로 변환합니다 (ä → a, é → e). 유니코드 파일 이름을 제대로 처리하지 못하는 도구에 유용합니다." - }, - "collapse_separators": { - "label": "반복된 구분자 결합", - "description": "연속된 공백, 밑줄, 대시를 단일 문자로 결합합니다." - }, - "after_export": { - "label": "내보낸 후", - "description": ".eml로 내보낸 후 이메일을 선택적으로 이동합니다.", - "keep": "메일함에 유지", - "archive": "보관함으로 이동", - "trash": "휴지통으로 이동" - } - }, - "filters": { - "title": "이메일 필터", - "description": "새로 온 메일을 자동으로 분류하고 라벨을 붙이는 규칙을 만들어 보세요", - "add_rule": "규칙 추가", - "no_rules": "필터 규칙이 없어요", - "no_rules_description": "수신 메일을 자동으로 정리할 규칙을 만들어 보세요", - "vacation_active": "부재중 자동 응답이 켜져 있어요", - "vacation_active_description": "받는 메일에 대해 자동으로 답장을 보내고 있어요", - "vacation_configure": "설정", - "edit_rule": "규칙 수정", - "new_rule": "새 규칙", - "delete_rule": "규칙 삭제", - "delete_confirm": "정말 이 규칙을 삭제할까요?", - "enable": "켜기", - "disable": "끄기", - "raw_editor": "원본 Sieve 에디터", - "raw_editor_warning": "원본 Sieve 스크립트를 수정하면 시각적 에디터가 망가질 수 있어요. 여기서 수정하면 시각적 에디터의 내용을 덮어쓰게 돼요.", - "validate": "검증", - "validation_success": "스크립트가 유효해요", - "validation_error": "스크립트에 오류가 있어요", - "save": "규칙 저장", - "saving": "저장 중...", - "saved": "필터가 성공적으로 저장되었어요", - "save_failed": "필터를 저장하지 못했어요", - "loading": "필터를 불러오는 중...", - "not_supported": "사용 중인 메일 서버는 이메일 필터를 지원하지 않아요.", - "rule_name": "규칙 이름", - "rule_name_placeholder": "예: 뉴스레터 분류", - "match_all": "모든 조건 만족 (AND)", - "match_any": "조건 중 하나라도 만족 (OR)", - "conditions": "조건", - "add_condition": "조건 추가", - "actions": "동작", - "add_action": "동작 추가", - "stop_processing": "이후 규칙 무시하기", - "include_spam": "스팸으로 표시된 메시지도 이동", - "attachment_type_placeholder": "예: pdf, doc, jpg", - "value_placeholder_multi": "값 (쉼표로 여러 개 구분)", - "condition_fields": { - "attachment": "첨부 파일", - "from": "보낸 사람", - "to": "받는 사람", - "cc": "참조", - "subject": "제목", - "header": "사용자 지정 헤더", - "size": "크기", - "body": "본문" - }, - "comparators": { - "has_any": "있음", - "has_type": "유형", - "contains": "포함함", - "not_contains": "포함하지 않음", - "is": "정확히 일치", - "not_is": "일치하지 않음", - "starts_with": "다음으로 시작", - "ends_with": "다음으로 끝남", - "matches": "패턴 일치", - "greater_than": "다음보다 큼", - "less_than": "다음보다 작음" - }, - "action_types": { - "move": "폴더로 이동", - "copy": "폴더로 복사", - "forward": "다음으로 전달", - "mark_read": "읽은 상태로 표시", - "star": "별표 달기", - "add_label": "태그 추가", - "discard": "삭제 (조용히 지움)", - "reject": "메시지와 함께 수신 거부", - "keep": "받은편지함에 유지", - "stop": "더 이상 처리 안 함" - }, - "move_to_folder": "폴더 선택", - "copy_to_folder": "폴더 선택", - "forward_to": "전달할 이메일 주소", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "사본 보관", - "forward_limit": "이 서버의 메시지당 전달 한도: {count}. 초과된 전달은 건너뜁니다.", - "reject_message": "거부 메시지", - "reject_placeholder": "메일 수신이 거부되었습니다", - "label_name": "태그 이름", - "label_placeholder": "태그 선택", - "header_name": "헤더 이름", - "header_placeholder": "예: X-Mailing-List", - "size_bytes": "크기 (바이트)", - "size_placeholder": "예: 1000000", - "system_managed": "시스템 관리 규칙", - "opaque_warning": "시각적 에디터 외부에서 수정된 스크립트예요. 원본 Sieve 편집만 가능해요.", - "open_sieve_editor": "원본 Sieve 에디터 열기", - "fetch_error": "필터를 불러오지 못했어요", - "expanded_view": "자세히 보기", - "expanded_view_description": "필터 규칙의 조건과 동작을 모두 펼쳐서 보여줘요", - "if": "만약 (If)", - "then": "다음 동작 실행 (Then)", - "match_all_conditions": "모두 만족할 때", - "match_any_condition": "하나라도 만족할 때", - "and": "그리고 (AND)", - "or": "또는 (OR)", - "cancel": "취소", - "confirm_delete": "삭제", - "rule_list": "필터 규칙", - "drag_to_reorder": "드래그해서 순서 변경", - "match_type": "일치 유형", - "reset_to_visual": "시각적 에디터로 초기화", - "reset_warning": "현재 스크립트가 지워지고 처음부터 다시 시작하게 돼요.", - "confirm_reset": "초기화", - "validation_empty_name": "규칙 이름을 입력해 주세요", - "validation_empty_conditions": "값을 포함한 조건이 하나 이상 필요해요", - "validation_empty_actions": "동작이 하나 이상 필요해요", - "templates_section": "템플릿으로 시작하기", - "template_newsletters": "뉴스레터를 특정 폴더로 이동", - "template_receipts": "영수증 자동 보관", - "template_important": "중요 메일 표시", - "template_notifications": "알림 메일 분류", - "sieve_editor": { - "title": "Sieve 스크립트 에디터", - "warning": "원본 Sieve 스크립트를 수정하면 시각적 규칙 편집이 망가질 수 있어요. 여기서 변경한 내용이 시각적 에디터를 덮어씁니다.", - "script_content": "Sieve 스크립트", - "valid": "유효한 스크립트예요", - "invalid": "스크립트에 오류가 있어요", - "save_warning": "저장하면 시각적 규칙이 덮어쓰기 되며, 이 작업은 되돌릴 수 없어요. 확인을 위해 한 번 더 저장을 눌러주세요.", - "validating": "검증 중...", - "validate": "검증", - "cancel": "취소", - "save": "저장", - "confirm_save": "저장 확인", - "validation_failed": "검증 요청에 실패했어요" - }, - "rule_summary": { - "conditions_count": "조건 {count}개", - "actions_count": "동작 {count}개" - }, - "origin_external": "외부", - "managed_by_tooltip": "{source}에서 관리됩니다. 해당 앱에서 편집하거나 원시 Sieve 편집기를 사용하세요." - }, - "templates": { - "title": "이메일 템플릿", - "description": "변수를 넣을 수 있는 재사용 가능한 메일 템플릿을 만들어 보세요", - "add": "새 템플릿", - "edit": "템플릿 수정", - "name": "템플릿 이름", - "name_placeholder": "예: 팔로업 메일", - "category": "카테고리", - "category_placeholder": "예: 업무, 개인", - "subject": "제목", - "subject_placeholder": "메일 제목", - "body": "본문", - "body_placeholder": "메일 내용...", - "recipients_placeholder": "email@example.com", - "identity": "보내는 사람", - "default_identity": "기본 보내는 사람", - "favorite": "즐겨찾기", - "cancel": "취소", - "create": "만들기", - "update": "업데이트", - "confirm_delete": "삭제", - "no_templates": "아직 템플릿이 없어요", - "manage": "템플릿 관리", - "count": "{count}개의 템플릿", - "export_import": "가져오기 & 내보내기", - "export_import_description": "템플릿을 백업하거나 다른 기기로 옮겨보세요", - "export": "내보내기", - "import": "가져오기", - "validation": { - "empty": "템플릿 이름이 필요해요", - "too_long": "템플릿 이름은 200자 이하여야 해요" - } - }, - "files": { - "display": { - "title": "화면 표시", - "description": "파일과 폴더를 보여주는 방식을 설정해요" - }, - "folder_layout": { - "label": "폴더 내비게이션", - "description": "폴더를 파일과 같이 보여줄지, 사이드바 트리 형태로 보여줄지 선택해요", - "inline": "인라인", - "sidebar": "사이드바" - }, - "default_view": { - "label": "기본 뷰", - "description": "그리드와 리스트 레이아웃 중 선택해 주세요", - "list": "리스트", - "grid": "그리드" - }, - "default_sort": { - "label": "기본 정렬", - "description": "파일의 기본 정렬 기준을 선택해요", - "name": "이름", - "size": "크기", - "modified": "수정일" - }, - "sort_direction": { - "label": "정렬 방향", - "description": "오름차순 또는 내림차순을 선택해요", - "ascending": "오름차순", - "descending": "내림차순" - }, - "icons": { - "title": "아이콘", - "description": "파일 아이콘의 모양을 설정해요" - }, - "show_icons": { - "label": "파일 아이콘 표시", - "description": "파일과 폴더 옆에 아이콘을 보여줘요" - }, - "colored_icons": { - "label": "컬러 아이콘", - "description": "단색 대신 화사한 컬러 아이콘을 사용해요" - }, - "show_thumbnails": { - "label": "미리보기(썸네일) 표시", - "description": "이미지 파일의 경우 아이콘 대신 미리보기를 보여줘요" - }, - "behavior": { - "title": "동작", - "description": "파일 탐색기의 동작 방식을 설정해요" - }, - "show_hidden": { - "label": "숨긴 파일 표시", - "description": "이름이 점(.)으로 시작하는 숨긴 파일과 폴더를 보여줘요" - }, - "preview": { - "label": "미리보기" - } - }, - "search_placeholder": "설정 검색", - "search_clear": "검색 지우기", - "search_no_results": "일치하는 설정이 없습니다", - "scoped": { - "back": "내 계정으로 돌아가기", - "managing": "관리 중: {name}" - } - }, - "errors": { - "page_error_title": "문제가 발생했어요", - "page_error_description": "예상치 못한 오류가 생겼어요. 다시 시도하거나 홈 화면으로 돌아가 주세요.", - "sidebar_error": "편지함을 불러올 수 없어요", - "email_list_error": "메일을 불러올 수 없어요", - "viewer_error_title": "메일을 표시할 수 없어요", - "viewer_error_description": "이 메일을 화면에 그리는 데 문제가 있어요. 지원하지 않는 콘텐츠가 포함되어 있을 수 있어요.", - "composer_error": "메일 쓰기 창을 불러올 수 없어요", - "settings_error_title": "설정을 사용할 수 없어요", - "settings_error_description": "설정을 불러오지 못했어요. 변경 사항이 저장되지 않을 수 있어요.", - "try_again": "다시 시도", - "reload": "새로고침", - "reload_emails": "메일 새로고침", - "reload_settings": "설정 새로고침", - "retry": "재시도", - "go_home": "받은편지함으로 이동" - }, - "context_menu": { - "reply": "답장", - "reply_all": "전체 답장", - "forward": "전달", - "mark_read": "읽은 상태로 표시", - "mark_unread": "읽지 않은 상태로 표시", - "star": "별표 달기", - "unstar": "별표 해제", - "pin": "고정", - "unpin": "고정 해제", - "move_to": "이동...", - "archive": "보관", - "delete": "삭제", - "mark_as_spam": "스팸 신고", - "not_spam": "정상 메일", - "tag": "태그", - "items_selected": "{count}개의 메일 선택됨", - "edit_draft": "임시보관 메일 수정", - "cancel_scheduled_send": "보내기 취소", - "reschedule_send": "다시 예약", - "cancel_and_edit": "취소하고 편집", - "cancel_and_compose_again": "취소하고 다시 작성" - }, - "mailbox_context_menu": { - "mark_folder_read": "폴더를 읽음으로 표시", - "mark_folder_tree_read": "폴더 및 하위 폴더를 읽음으로 표시", - "mark_all_folders_read": "모든 폴더를 읽음으로 표시", - "new_subfolder": "새 하위 폴더...", - "new_folder": "새 폴더...", - "rename": "이름 바꾸기...", - "share": "공유...", - "import_email": ".eml 또는 .zip 가져오기...", - "empty_folder": "폴더 비우기", - "empty_folder_generic": "폴더 비우기", - "delete_folder": "폴더 삭제", - "refresh": "새로 고침", - "mark_all_confirm_title": "모든 폴더를 읽음으로 표시", - "mark_all_confirm_message": "개인 계정의 모든 읽지 않은 메시지를 읽음으로 표시하시겠습니까?", - "delete_confirm_title": "폴더 삭제", - "delete_confirm_message": "폴더 \"{name}\"을(를) 영구적으로 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", - "prompt_new_subfolder": "새 하위 폴더의 이름을 입력하세요.", - "prompt_new_folder": "새 폴더의 이름을 입력하세요.", - "prompt_rename": "이 폴더의 새 이름을 입력하세요.", - "toast_marked_read": "폴더를 읽음으로 표시했습니다", - "toast_marked_read_count": "{count, plural, one {메시지 1개} other {메시지 #개}}을(를) 읽음으로 표시했습니다", - "toast_already_read": "읽지 않은 메시지가 없습니다", - "toast_marked_all_read": "모든 폴더를 읽음으로 표시했습니다", - "toast_emptied": "폴더를 비웠습니다", - "toast_folder_created": "폴더가 생성되었습니다", - "toast_folder_renamed": "폴더 이름이 변경되었습니다", - "toast_folder_deleted": "폴더가 삭제되었습니다", - "toast_error_mark_read": "읽음으로 표시하지 못했습니다", - "toast_error_empty": "폴더를 비우지 못했습니다", - "toast_error_create": "폴더를 생성하지 못했습니다", - "toast_error_rename": "폴더 이름을 변경하지 못했습니다", - "toast_error_delete": "폴더를 삭제하지 못했습니다", - "toast_error_delete_has_children": "폴더에 하위 폴더가 있습니다. 먼저 제거하세요.", - "toast_error_delete_has_email": "폴더가 비어 있지 않습니다. 먼저 비우세요.", - "delete_with_contents_title": "폴더가 비어 있지 않습니다", - "delete_with_contents_message": "\"{name}\" 폴더에 아직 {count}개의 메시지가 있습니다. 폴더와 모든 메시지를 삭제할까요? 이 작업은 취소할 수 없습니다.", - "delete_with_contents_confirm": "폴더와 메시지 삭제", - "placeholder_folder_name": "폴더 이름", - "create": "만들기", - "rename_confirm": "이름 바꾸기" - }, - "shortcuts": { - "title": "단축키", - "tip": "언제든 ? 키를 누르면 이 도움말을 볼 수 있어요", - "sections": { - "navigation": "이동", - "actions": "메일 작업", - "global": "공통", - "threads": "대화(스레드)", - "composer": "메일 쓰기" - }, - "navigation": { - "next_email": "다음 메일", - "previous_email": "이전 메일", - "open_email": "메일 열기", - "close_email": "닫기 / 선택 해제" - }, - "actions": { - "reply": "답장", - "reply_all": "전체 답장", - "forward": "전달", - "star": "별표 켜기/끄기", - "archive": "보관", - "delete": "삭제", - "mark_unread": "읽지 않음으로 표시", - "mark_read": "읽음으로 표시", - "toggle_spam": "스팸 신고 / 정상 표시" - }, - "global": { - "compose": "새 메일 쓰기", - "search": "검색창 이동", - "help": "단축키 보기", - "refresh": "메일 새로고침", - "select_all": "모두 선택", - "global_search": "전체 검색" - }, - "threads": { - "expand_collapse": "대화 펼치기/접기" - }, - "composer": { - "send": "이메일 보내기", - "schedule_send": "보내기 예약", - "template_picker": "템플릿 선택기 열기" - } - }, - "threads": { - "messages_one": "메시지 1개", - "messages_other": "메시지 {count}개", - "messages_tooltip": "이 대화에 {count}개의 메시지가 있어요", - "expand": "대화 펼치기", - "collapse": "대화 접기", - "loading": "대화를 불러오는 중...", - "mark_read": "대화 전체를 읽음으로 표시", - "mark_unread": "대화 전체를 읽지 않음으로 표시", - "archive": "대화 보관", - "delete": "대화 삭제", - "star": "대화 전체에 별표 달기", - "unstar": "대화 전체 별표 해제", - "toggle_thread": "스레드 켜기/끄기" - }, - "identities": { - "modal_title": "보내는 사람 관리", - "create_new": "새 보내는 사람 추가", - "edit_identity": "정보 수정", - "delete_confirm": "이 주소를 삭제할까요? 이 작업은 되돌릴 수 없어요.", - "cannot_delete": "이 주소는 삭제할 수 없어요", - "primary_identity": "기본", - "set_as_primary": "기본값으로 설정", - "no_identities": "저장된 주소가 없어요", - "display": { - "reply_to": "회신 주소 (Reply-To):", - "bcc": "숨은 참조 (BCC):", - "signature": "서명:", - "preview": "미리보기:" - }, - "validation_errors": { - "invalid_emails": "잘못된 이메일: {emails}", - "unknown_error": "알 수 없는 오류" - }, - "form": { - "name_label": "표시 이름", - "name_placeholder": "예: 업무용 메일, 개인", - "name_required": "이름을 입력해 주세요", - "email_label": "이메일 주소", - "email_placeholder": "your.email@example.com", - "email_required": "이메일 주소가 필요해요", - "email_invalid": "올바른 이메일 주소를 입력해 주세요", - "email_immutable": "한 번 만든 이메일 주소는 바꿀 수 없어요", - "reply_to_label": "회신 주소 (선택 사항)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "자동 숨은 참조 (선택 사항)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "텍스트 서명", - "html_signature_label": "HTML 서명", - "signature_byte_counter": "{bytes} / {max} 바이트", - "signature_byte_limit_reached": "서버 한도 도달", - "save": "저장", - "cancel": "취소", - "creating": "만드는 중...", - "updating": "업데이트 중..." - }, - "sub_address": { - "button_tooltip": "서브 어드레스 사용", - "popover_title": "서브 어드레스 태그 추가", - "tag_input_placeholder": "태그 입력 (예: shopping)", - "preview_label": "미리보기:", - "recent_tags": "최근 태그", - "suggested_tags": "추천", - "use_address": "이 주소 사용하기", - "invalid_tag": "태그는 알파벳, 숫자, 대시(-)만 쓸 수 있어요", - "tag_too_long": "태그는 30자 이하여야 해요", - "help_text": "user{delimiter}tag@domain.com 으로 보낸 메일은 내 받은편지함으로 들어와요", - "validation": { - "empty": "태그를 비워둘 수 없어요", - "too_long": "태그는 {max}자 이하여야 해요", - "invalid_chars": "태그에는 영문, 숫자, 대시(-)만 쓸 수 있어요" - } - }, - "badge": { - "sent_via": "경유:", - "sub_address_tag": "서브 어드레스 사용: {tag}", - "identity_name": "다음 주소로 전송됨: {name}", - "identity_short": "{name} 주소 사용", - "subaddress_tag": "+{tag}" - }, - "delete_button": "삭제", - "delete_confirm_title": "주소 삭제" - }, - "templates": { - "picker_title": "템플릿 선택", - "search_placeholder": "템플릿 검색...", - "section_favorites": "즐겨찾기", - "section_recent": "최근", - "section_uncategorized": "기타", - "no_templates": "아직 템플릿이 없어요", - "no_results": "템플릿을 찾을 수 없어요", - "fill_placeholders": "변수 값 입력하기", - "enter_value": "값을 입력해 주세요...", - "preview": "미리보기", - "insert_with_values": "값과 함께 삽입", - "insert_raw": "원본 삽입", - "copy_suffix": "(복사본)", - "placeholder": "변수", - "placeholders": { - "recipient_name": "받는 사람 이름", - "company": "회사 이름", - "date": "오늘 날짜", - "day_of_week": "요일", - "sender_name": "내 이름" - } - }, - "contacts": { - "title": "연락처", - "search_placeholder": "연락처 검색...", - "create_new": "새 연락처", - "no_category": "카테고리 없음", - "rename_category": "카테고리 이름 변경", - "category_name_label": "카테고리 이름", - "category_renamed": "카테고리 이름이 변경되었습니다", - "category_rename_failed": "카테고리 이름 변경 실패", - "category_added": "{name}에 연락처가 추가되었어요", - "category_added_plural": "{name}에 {count}개의 연락처가 추가되었어요", - "empty_state": "아직 연락처가 없어요", - "empty_state_title": "아직 연락처가 없어요", - "empty_state_subtitle": "새 연락처를 만들거나 vCard 파일에서 가져와 보세요", - "empty_search": "검색된 연락처가 없어요", - "empty_search_hint": "다른 검색어로 다시 시도해 보세요", - "empty_filtered": "필터에 일치하는 연락처가 없어요", - "empty_filtered_hint": "필터를 조정하거나 지워보세요", - "clear_search": "검색 지우기", - "import_vcard": "vCard 가져오기", - "delete_confirm_title": "연락처 삭제", - "delete_confirm": "정말 이 연락처를 삭제할까요?", - "local_mode": "연락처가 로컬에 저장돼요 (서버가 JMAP Contacts를 지원하지 않아요)", - "back_to_contacts": "연락처로 돌아가기", - "back_to_email": "이메일로 돌아가기", - "tabs": { - "all": "전체", - "groups": "그룹" - }, - "shared": { - "title": "공유됨" - }, - "address_books": { - "create": "새 주소록", - "created": "주소록이 생성되었습니다", - "create_failed": "주소록을 만들지 못했습니다", - "title": "내 주소록", - "shared_prefix": "공유됨: {name}", - "moved": "연락처가 {name}(으)로 이동되었어요", - "moved_plural": "{count}개의 연락처가 {name}(으)로 이동되었어요", - "move_failed": "연락처를 이동하지 못했어요", - "address_book": "주소록", - "rename": "주소록 이름 변경", - "name_label": "주소록 이름", - "renamed": "주소록 이름이 변경되었습니다", - "rename_failed": "주소록 이름 변경 실패", - "default": "기본", - "set_default": "기본으로 설정", - "default_updated": "기본 주소록이 업데이트되었습니다", - "set_default_failed": "기본 주소록을 설정하지 못했습니다", - "manage": "주소록 관리", - "share": "주소록 공유", - "new_contact_in_book": "이 주소록에 새 연락처", - "delete": "주소록 삭제", - "confirm_delete": "\"{name}\"을(를) 삭제하시겠습니까? 이 주소록의 모든 연락처가 삭제됩니다.", - "deleted": "주소록이 삭제되었습니다", - "delete_failed": "주소록 삭제에 실패했습니다" - }, - "detail": { - "emails": "이메일", - "phones": "전화번호", - "organizations": "소속", - "addresses": "주소", - "notes": "메모", - "titles": "직함 및 역할", - "online_services": "온라인 서비스", - "anniversaries": "기념일", - "personal_info": "개인 정보", - "languages": "언어", - "categories": "카테고리", - "related_contacts": "관련 연락처", - "crypto_keys": "암호화 키", - "cert_issuer": "발급자", - "cert_expires": "만료일", - "cert_expired": "만료됨", - "cert_fingerprint": "지문(Fingerprint)", - "cert_algorithm": "알고리즘", - "import_to_smime": "S/MIME으로 가져오기", - "cert_already_imported": "이미 S/MIME에 등록되어 있어요", - "cert_imported": "인증서가 S/MIME 저장소에 추가되었어요", - "cert_import_failed": "인증서를 가져오지 못했어요", - "no_contact_selected": "세부 정보를 보려면 연락처를 선택해 주세요", - "compose_email": "메일 쓰기", - "copy_email": "이메일 복사", - "copy_phone": "전화번호 복사", - "copy_url": "URL 복사", - "copied": "클립보드에 복사되었어요", - "copy_failed": "복사하지 못했어요", - "created": "생성일", - "updated": "마지막 업데이트", - "timezone": "시간대", - "anniversary_birth": "생일", - "anniversary_death": "기일", - "anniversary_wedding": "결혼기념일", - "anniversary_other": "기타", - "personal_expertise": "전문 분야", - "personal_hobby": "취미", - "personal_interest": "관심사", - "personal_other": "기타", - "gender": "성별", - "gender_masculine": "남성", - "gender_feminine": "여성", - "gender_other": "기타", - "gender_none": "해당 없음", - "gender_unknown": "알 수 없음", - "calendar": "캘린더", - "calendar_uri": "캘린더 URL", - "scheduling_uri": "스케줄링 URL", - "freebusy_uri": "Free/Busy URL", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "작업 더보기", - "age_years": "{count}세", - "years_since": "{count}년 전" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "새 연락처", - "edit_title": "연락처 수정", - "section_address_book": "디렉터리", - "select_address_book": "디렉터리 선택...", - "section_identity": "이름 및 신원", - "contact_type": "연락처 유형", - "type_person": "개인", - "type_organization": "소속(회사)", - "section_work": "직장 및 소속", - "prefix": "호칭", - "prefix_placeholder": "예: Dr., Mr., Mrs.", - "given_name": "이름", - "middle_name": "중간 이름", - "surname": "성", - "suffix": "접미사", - "suffix_placeholder": "예: Jr., Sr.", - "nickname": "별명", - "nickname_placeholder": "별명", - "email": "이메일", - "email_placeholder": "email@example.com", - "phone": "전화번호", - "phone_placeholder": "010-1234-5678", - "phone_type": "유형", - "phone_voice": "음성", - "phone_cell": "휴대전화", - "phone_fax": "팩스", - "phone_pager": "호출기", - "phone_video": "화상", - "phone_text": "문자", - "organization": "소속(회사)", - "organization_placeholder": "회사 이름", - "department": "부서", - "department_placeholder": "부서명", - "job_title": "직함", - "job_title_placeholder": "예: 소프트웨어 엔지니어", - "role": "역할", - "role_placeholder": "예: 팀장", - "addresses": "주소", - "add_address": "주소 추가", - "street": "도로명 주소", - "city": "시/도", - "region": "지역", - "postcode": "우편번호", - "country": "국가", - "online_services": "온라인 서비스", - "add_online_service": "온라인 서비스 추가", - "url_placeholder": "https://...", - "service_placeholder": "서비스", - "anniversaries": "기념일", - "add_anniversary": "날짜 추가", - "anniversary_birth": "생일", - "anniversary_wedding": "결혼기념일", - "anniversary_death": "기일", - "anniversary_other": "기타", - "personal_info": "개인 정보", - "add_personal_info": "항목 추가", - "personal_info_placeholder": "예: 사진 촬영", - "personal_expertise": "전문 분야", - "personal_hobby": "취미", - "personal_interest": "관심사", - "personal_other": "기타", - "level": "수준", - "level_high": "상", - "level_medium": "중", - "level_low": "하", - "categories": "카테고리", - "categories_placeholder": "예: 가족, 친구, 동료", - "categories_hint": "입력하여 검색하거나 카테고리를 추가하세요", - "category_add": "추가", - "note": "메모", - "note_placeholder": "메모를 남겨주세요...", - "gender": "성별", - "gender_sex": "성별(생물학적)", - "gender_male": "남성", - "gender_female": "여성", - "gender_other": "기타", - "gender_none": "해당 없음", - "gender_unknown": "알 수 없음", - "gender_identity": "성 정체성", - "gender_identity_placeholder": "성 정체성...", - "calendar": "캘린더", - "calendar_uri": "캘린더 URL", - "scheduling_uri": "스케줄링 URL", - "freebusy_uri": "Free/Busy URL", - "context_work": "직장", - "context_private": "개인", - "add_email": "이메일 추가", - "add_phone": "전화번호 추가", - "save": "저장", - "cancel": "취소", - "creating": "만드는 중...", - "updating": "업데이트 중...", - "name_required": "이름, 성 또는 조직을 입력하세요", - "email_invalid": "올바른 이메일 주소를 입력해 주세요", - "email_error_inline": "이메일 형식이 잘못되었어요", - "save_failed": "연락처를 저장하지 못했어요", - "delete": "삭제", - "upload_photo": "사진 업로드", - "remove_photo": "사진 제거", - "photo_hint": "JPG 또는 PNG, 최도 10MB. 크기가 조정됩니다.", - "photo_too_large": "이미지가 너무 큽니다 (최대 10MB)", - "photo_invalid": "잘못된 이미지 파일", - "change_photo": "변경" - }, - "groups": { - "create": "새 그룹", - "edit": "그룹 수정", - "empty": "아직 그룹이 없어요", - "delete_confirm_title": "그룹 삭제", - "delete_confirm": "정말 이 그룹을 삭제할까요?", - "name_label": "그룹 이름", - "name_placeholder": "예: 팀, 가족", - "name_required": "그룹 이름이 필요해요", - "save_failed": "그룹을 저장하지 못했어요", - "members_label": "멤버", - "search_members": "추가할 연락처 검색...", - "no_members": "이 그룹에는 멤버가 없어요", - "member_count": "멤버 {count}명", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "연락처 가져오기", - "drop_hint": "클릭해서 vCard 파일을 선택해 주세요", - "file_types": ".vcf 또는 .vcard 파일", - "no_contacts": "파일에서 연락처를 찾을 수 없어요", - "parse_error": "vCard 파일을 분석하는 데 실패했어요", - "found": "{count}개의 연락처를 찾았어요", - "duplicate": "중복", - "select_all": "모두 선택", - "deselect_all": "모두 선택 해제", - "selected": "{count}개 선택됨", - "import_button": "가져오기", - "importing": "가져오는 중...", - "success": "{count}개의 연락처를 성공적으로 가져왔어요", - "failed": "가져오기 실패", - "close": "닫기", - "file_too_large": "파일이 너무 커요 (최대 5MB)" - }, - "export": { - "title": "연락처 내보내기", - "success": "{count}개의 연락처를 성공적으로 내보냈어요" - }, - "bulk": { - "selected": "{count}개 선택됨", - "select_all": "모두 선택", - "delete": "삭제", - "delete_confirm_title": "연락처 삭제", - "delete_confirm": "선택한 {count}개의 연락처를 삭제할까요?", - "deleted": "{count}개의 연락처가 삭제되었어요", - "add_to_group": "그룹에 추가", - "choose_group": "그룹 선택", - "adding_contacts": "{count}개의 연락처를 추가하는 중...", - "added_to_group": "연락처가 그룹에 추가되었어요", - "export": "내보내기", - "clear": "선택 취소" - }, - "toast": { - "created": "연락처가 만들어졌어요", - "updated": "연락처가 업데이트되었어요", - "deleted": "연락처가 삭제되었어요", - "error_create": "연락처를 만들지 못했어요", - "error_update": "연락처를 업데이트하지 못했어요", - "error_delete": "연락처를 삭제하지 못했어요" - }, - "context_menu": { - "open": "열기", - "edit": "편집", - "send_email": "이메일 보내기", - "add_to_group": "그룹에 추가", - "export_vcard": "vCard로 내보내기", - "delete": "삭제", - "call": "통화", - "duplicate": "복제", - "print": "인쇄" - }, - "filters": { - "toggle": "필터", - "select": "선택", - "clear": "지우기", - "close": "닫기", - "title": "고급 필터", - "organization": "회사", - "organization_placeholder": "예: Acme", - "job_title": "직책", - "job_title_placeholder": "예: 디자이너", - "location": "위치", - "location_placeholder": "도시 또는 국가", - "email_domain": "이메일 도메인", - "email_domain_placeholder": "example.com", - "birthday_month": "생일 월", - "any_month": "모든 달", - "has_email": "이메일 있음", - "has_phone": "전화번호 있음", - "has_photo": "사진 있음", - "sort_by": "정렬 기준", - "sort_first_name": "이름", - "sort_last_name": "성" - }, - "open_categories": "카테고리 열기" - }, - "calendar": { - "title": "캘린더", - "back_to_email": "메일로 돌아가기", - "back_to_month": "월간 보기로 돌아가기", - "my_calendars": "내 캘린더", - "birthday_calendar": "생일", - "mini_calendar_change": "클릭해서 달을 변경하세요", - "views": { - "month": "월", - "week": "주", - "day": "일", - "agenda": "일정 목록", - "today": "오늘", - "month_hint": "월간 (m)", - "week_hint": "주간 (w)", - "day_hint": "일간 (d)", - "agenda_hint": "일정 목록 (a)", - "tasks": "할 일", - "tasks_hint": "할 일 (k)" - }, - "events": { - "create": "일정 만들기", - "edit": "일정 수정", - "delete": "일정 삭제", - "details": "일정 세부 정보", - "no_events": "일정이 없어요", - "all_day": "하루 종일", - "more": "+{count}개 더보기", - "no_title": "(제목 없음)", - "resize": "일정 크기 조절", - "duplicate": "복제", - "today_header": "오늘", - "tomorrow_header": "내일", - "agenda_show_earlier": "이전 일정 보기", - "agenda_loading": "불러오는 중…", - "agenda_range_start": "{date}부터의 일정", - "agenda_range_end": "{date}까지의 일정", - "export_ics": ".ics로 내보내기", - "copy_title": "제목 복사", - "copy_link": "회의 링크 복사", - "go_to_today": "오늘로 이동", - "new_all_day_event": "새 종일 이벤트", - "new_event": "새 이벤트", - "new_task": "새 작업" - }, - "detail": { - "add_note": "메모 추가...", - "save_note": "저장", - "note_saved": "메모가 추가되었어요", - "open_link": "링크 열기", - "meeting_link": "화상 회의 링크", - "tentative": "미정", - "cancelled": "취소됨", - "delete_confirm": "이 일정을 삭제할까요?" - }, - "form": { - "title": "제목", - "description": "설명", - "location": "위치", - "meeting_link": "화상 회의 링크", - "start_date": "시작 날짜", - "end_date": "종료 날짜", - "start_time": "시작 시간", - "end_time": "종료 시간", - "all_day_event": "하루 종일 일정", - "calendar_select": "캘린더", - "save": "저장", - "cancel": "취소", - "delete_confirm": "정말 이 일정을 삭제할까요?", - "color": "색상" - }, - "participants": { - "availability": { - "title": "일정 상태", - "free": "가능", - "busy": "바쁨", - "tentative": "미정", - "unknown": "이 서버의 사용자가 아님", - "checking": "확인 중…" - }, - "title": "참석자", - "add": "참석자 추가", - "organizer": "주최자", - "attendee": "참석자", - "accepted": "수락함", - "declined": "거절함", - "tentative": "미정", - "needs_action": "응답 필요", - "remove": "삭제", - "edit": "편집", - "email_placeholder": "이메일을 추가하거나 연락처를 검색하세요", - "send_invitations": "참석자에게 초대장 보내기", - "status_summary": "{accepted}명 수락, {pending}명 대기 중", - "invited_by": "{name}님의 초대", - "respond_below": "아래 버튼을 눌러 응답해 주세요", - "rsvp_label": "내 응답", - "cancel_notification": "참석자들에게 취소 알림이 전송돼요", - "you_organizer": "내가 주최자예요", - "you_attendee": "나는 참석자예요", - "no_participants": "참석자가 없어요", - "count": "참석자 {count}명" - }, - "recurrence": { - "title": "반복 일정", - "none": "반복 안 함", - "daily": "매일", - "weekly": "매주", - "monthly": "매월", - "yearly": "매년", - "every_n_days": "{count}일마다", - "every_n_weeks": "{count}주마다", - "every_n_months": "{count}개월마다", - "until": "종료일:", - "occurrences": "{count}회 반복", - "custom": "맞춤…", - "edit_custom": "맞춤 반복 수정", - "every_n_years": "{count}년마다", - "on_days": "{days}", - "on_day_n": "{day}일", - "on_the_nth": "{nth} {day}", - "in_month": "{month}", - "nth_1": "첫째", - "nth_2": "둘째", - "nth_3": "셋째", - "nth_4": "넷째", - "nth_last": "마지막", - "editor_freq_day": "일", - "editor_freq_week": "주", - "editor_freq_month": "월", - "editor_freq_year": "년", - "editor_repeats_on": "반복", - "editor_every": "매", - "editor_unit_days": "일", - "editor_unit_weeks": "주", - "editor_unit_months": "개월", - "editor_unit_years": "년", - "editor_on_day": "날짜:", - "editor_on_the": "요일:", - "editor_in": "월:", - "editor_ends": "종료", - "editor_never": "안 함", - "editor_ends_on": "종료일", - "editor_ends_after": "횟수", - "editor_occurrences": "회" - }, - "recurrence_scope": { - "edit_title": "반복 일정 수정", - "delete_title": "반복 일정 삭제", - "description": "반복되는 일정이에요. 어떤 일정을 수정할까요?", - "this_event": "이 일정만", - "this_and_future": "이 일정 및 향후 일정 모두", - "all_events": "모든 일정", - "cancel": "취소", - "save": "저장", - "delete": "삭제", - "rsvp_title": "반복 일정에 응답", - "rsvp_description": "반복되는 일정이에요. 어떤 일정에 응답할까요?", - "respond": "응답" - }, - "alerts": { - "title": "알림", - "none": "알림 없음", - "at_time": "일정 시작 시", - "minutes_before": "{count}분 전", - "hours_before": "{count}시간 전", - "days_before": "{count}일 전", - "weeks_before": "{count}주 전", - "unit_minutes_before": "분 전", - "unit_hours_before": "시간 전", - "unit_days_before": "일 전", - "unit_weeks_before": "주 전", - "add": "알림 추가", - "remove": "알림 제거", - "amount": "알림 값", - "unit": "알림 단위" - }, - "settings": { - "title": "캘린더 설정", - "default_view": "기본 뷰", - "week_starts_on": "한 주의 시작", - "time_format": "시간 형식", - "default_calendar": "기본 캘린더", - "default_reminder": "기본 알림", - "time_format_12h": "12시간제", - "time_format_24h": "24시간제", - "notifications_enabled": "일정 알림", - "notifications_enabled_desc": "다가오는 일정에 대한 알림을 보여줘요", - "notification_sound": "알림음", - "notification_sound_desc": "일정 알림 시 소리를 재생해요", - "invitation_parsing": "이메일 초대장 분석", - "invitation_parsing_desc": "이메일 첨부파일에서 캘린더 초대장을 감지하고 보여줘요", - "show_time_in_month_view": "월간 보기에 시간 표시", - "show_time_in_month_view_desc": "월간 캘린더에서도 일정 시간을 표시해요", - "show_week_numbers": "주 차 번호 표시", - "show_week_numbers_desc": "미니 캘린더에 1년 중 몇 주 차인지 표시해요", - "calendar_free_scroll": "자유 스크롤", - "calendar_free_scroll_desc": "한 번에 한 기간씩 보는 대신 월, 주, 일을 연속으로 스크롤합니다", - "enable_tasks": "할 일 사용", - "enable_tasks_desc": "캘린더에 할 일을 관리할 수 있는 메뉴를 추가해요", - "show_tasks_on_calendar": "캘린더에 할 일 표시", - "show_tasks_on_calendar_desc": "일간 및 주간 캘린더에 할 일 배지를 보여줘요", - "hover_preview": "마우스 호버 미리보기", - "hover_preview_desc": "캘린더 일정 위에 마우스를 올리면 세부 정보 팝업을 띄워요", - "hover_preview_instant": "즉시", - "hover_preview_delay_500ms": "0.5초 지연", - "hover_preview_delay_1s": "1초 지연", - "hover_preview_delay_2s": "2초 지연", - "hover_preview_off": "사용 안 함", - "organizer_identity": "초대 주최자 주소", - "organizer_identity_desc": "이벤트에 사람들을 초대할 때 주최자로 사용되는 캘린더 주소입니다.", - "organizer_identity_failed": "주최자 주소를 변경하지 못했습니다", - "show_birthday_calendar": "연락처 생일 캘린더", - "show_birthday_calendar_desc": "연락처에 등록된 생일을 가상 캘린더로 보여줘요" - }, - "days": { - "monday": "월요일", - "tuesday": "화요일", - "wednesday": "수요일", - "thursday": "목요일", - "friday": "금요일", - "saturday": "토요일", - "sunday": "일요일", - "mon": "월", - "tue": "화", - "wed": "수", - "thu": "목", - "fri": "금", - "sat": "토", - "sun": "일" - }, - "notifications": { - "event_created": "일정이 만들어졌어요", - "event_updated": "일정이 업데이트되었어요", - "event_deleted": "일정이 삭제되었어요", - "calendar_created": "캘린더가 만들어졌어요", - "calendar_deleted": "캘린더가 삭제되었어요", - "event_move_error": "일정을 이동하지 못했어요", - "event_resize_error": "일정 시간을 조절하지 못했어요", - "alert_title": "다가오는 일정", - "alert_now": "지금 시작해요", - "alert_in_minutes": "{count}분 후 시작", - "invitation_sent": "초대장이 전송되었어요", - "invitations_denied_title": "초대장을 보낼 수 없어요", - "invitations_denied": "서버가 초대장 발송을 거부했어요: {reason}. 초대장 없이 일정을 저장할까요?", - "save_without_invitations": "초대장 없이 저장", - "rsvp_updated": "응답이 업데이트되었어요", - "rsvp_error": "응답을 업데이트하지 못했어요", - "event_duplicated": "일정이 복제되었어요", - "event_error": "일정을 저장하지 못했어요", - "task_due": "할 일 기한이 다가와요", - "event_exported": "이벤트 내보내기 완료", - "title_copied": "제목이 복사되었습니다", - "link_copied": "링크가 복사되었습니다" - }, - "status": { - "loading_calendars": "캘린더를 불러오는 중...", - "loading_events": "일정을 불러오는 중..." - }, - "quick_create": { - "placeholder": "새 일정 제목", - "aria_label": "빠른 일정 만들기" - }, - "nav_prev": "이전", - "nav_next": "다음", - "import": { - "title": "캘린더 가져오기", - "tab_file": "파일", - "tab_url": "URL", - "select_file": ".ics 파일 선택", - "drop_file": "또는 여기에 파일을 놓아주세요", - "supported_formats": "iCalendar (.ics) 파일을 지원해요", - "url_description": "외부 iCalendar (.ics) 피드의 URL을 입력해서 일정을 가져와요.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "iCalendar (.ics) URL을 지원해요", - "fetch": "가져오기", - "invalid_url": "올바른 URL을 입력해 주세요", - "url_fetch_failed": "URL에서 캘린더를 가져오지 못했어요", - "parsing": "캘린더 파일을 분석하는 중...", - "parsed_events": "{count}개의 일정을 찾았어요", - "no_events": "파일에서 일정을 찾을 수 없어요", - "select_all": "모두 선택", - "deselect_all": "모두 선택 해제", - "target_calendar": "저장할 캘린더", - "import_button": "선택 항목 가져오기", - "importing": "일정을 가져오는 중...", - "success": "{count}개의 일정을 성공적으로 가져왔어요", - "error": "캘린더를 가져오지 못했어요", - "file_too_large": "파일이 10MB 제한을 넘었어요", - "invalid_format": "캘린더 파일 형식이 올바르지 않아요" - }, - "webcal_action": { - "title": "캘린더 링크 열기", - "description": "\"{name}\"을 어떻게 사용할까요?", - "import_title": "한 번 가져오기", - "import_description": "지금 일정을 가져와 내 캘린더 중 하나에 복사합니다.", - "subscribe_title": "구독", - "subscribe_description": "이 캘린더를 별도의 캘린더로 자동 동기화합니다.", - "cancel": "취소" - }, - "management": { - "title": "캘린더 관리", - "description": "캘린더를 만들고 이름을 바꾸거나 색상을 꾸며보세요. 사이드바에서 캘린더를 우클릭하면 색상을 빠르게 바꿀 수 있어요.", - "name": "이름", - "name_placeholder": "캘린더 이름", - "color": "색상", - "change_color": "색상 변경", - "random_color": "새 무작위 색상", - "add_calendar": "캘린더 추가", - "edit": "수정", - "delete": "삭제", - "save": "저장", - "create": "만들기", - "cancel": "취소", - "default": "기본", - "confirm_delete": "\"{name}\" 캘린더를 삭제할까요? 캘린더 안의 모든 일정이 함께 지워져요.", - "confirm_clear": "\"{name}\" 캘린더의 모든 일정을 지울까요? 이 작업은 되돌릴 수 없어요.", - "set_default": "기본으로 설정", - "default_updated": "기본 캘린더가 업데이트되었습니다", - "error_default": "기본 캘린더를 설정하지 못했습니다", - "clear_events": "일정 모두 지우기", - "events_cleared": "{count}개의 일정을 지웠어요", - "error_clear": "일정을 지우지 못했어요", - "calendar_created": "캘린더가 만들어졌어요", - "calendar_updated": "캘린더가 업데이트되었어요", - "calendar_deleted": "캘린더가 삭제되었어요", - "color_updated": "캘린더 색상이 변경되었어요", - "error_create": "캘린더를 만들지 못했어요", - "error_update": "캘린더를 업데이트하지 못했어요", - "error_delete": "캘린더를 삭제하지 못했어요", - "caldav_url": "CalDAV URL", - "copy_url": "CalDAV URL 복사", - "url_copied": "CalDAV URL이 클립보드에 복사되었어요", - "share": "캘린더 공유", - "new_event_in_calendar": "이 캘린더에 새 일정", - "kind": "내용", - "kind_events": "일정", - "kind_tasks": "할 일", - "kind_both": "일정 및 할 일", - "kind_hint": "CalDAV로 동기화할 때 이 캘린더를 볼 수 있는 앱을 결정합니다. 나중에 변경할 수 없습니다." - }, - "subscription": { - "title": "iCal 구독", - "section_title": "iCal 구독", - "description": "외부 iCalendar 피드를 구독해 보세요. 일정들이 전용 캘린더에 자동으로 동기화돼요. (https:// 및 webcal:// 지원)", - "url_label": "캘린더 URL", - "url_placeholder": "https://example.com/calendar.ics 또는 webcal://...", - "name_label": "캘린더 이름", - "name_placeholder": "예: 공휴일", - "color_label": "색상", - "refresh_interval": "새로고침 간격", - "interval_15": "15분마다", - "interval_30": "30분마다", - "interval_60": "1시간마다", - "interval_360": "6시간마다", - "interval_1440": "매일", - "subscribe": "구독하기", - "subscribing": "구독 중...", - "save": "변경 사항 저장", - "saving": "저장 중...", - "edit": "수정", - "edit_title": "구독 수정", - "updated": "\"{name}\" 구독이 업데이트되었어요", - "update_error": "구독을 업데이트하지 못했어요", - "invalid_url": "올바른 URL을 입력해 주세요", - "success": "\"{name}\" 캘린더를 구독했어요", - "error": "구독을 추가하지 못했어요", - "refresh": "지금 새로고침", - "refresh_success": "구독이 새로고침되었어요", - "refresh_error": "구독을 새로고침하지 못했어요", - "unsubscribe": "구독 취소", - "confirm_delete": "\"{name}\" 구독을 취소할까요? 캘린더와 모든 일정이 삭제돼요.", - "deleted": "구독이 삭제되었어요", - "delete_error": "구독을 삭제하지 못했어요", - "last_refreshed": "마지막 업데이트: {time}" - }, - "tasks": { - "label": "할 일", - "no_tasks": "할 일이 없어요", - "no_title": "(제목 없음)", - "mark_complete": "완료로 표시", - "mark_incomplete": "미완료로 표시", - "filter_all": "전체", - "filter_pending": "대기 중", - "filter_completed": "완료됨", - "filter_overdue": "기한 지남", - "show_completed": "완료된 항목 보기", - "create": "새 할 일", - "edit": "할 일 수정", - "title_placeholder": "할 일 제목", - "description_placeholder": "설명 추가...", - "due_date": "마감일", - "include_time": "시간 포함", - "priority": "우선순위", - "priority_none": "없음", - "priority_high": "높음", - "priority_medium": "보통", - "priority_low": "낮음", - "progress": "상태", - "progress_needs_action": "진행 필요", - "progress_in_process": "진행 중", - "progress_completed": "완료", - "progress_cancelled": "취소됨", - "calendar": "캘린더", - "alert": "알림", - "alert_none": "없음", - "alert_at_time": "마감 시간에", - "alert_5min": "5분 전", - "alert_15min": "15분 전", - "alert_30min": "30분 전", - "alert_1hr": "1시간 전", - "alert_1day": "1일 전", - "delete": "삭제", - "cancel": "취소", - "save": "저장", - "quick_add_placeholder": "할 일 추가...", - "due_today": "오늘 마감", - "due_tomorrow": "내일 마감", - "overdue": "기한 지남" - }, - "months": { - "jan": "1월", - "feb": "2월", - "mar": "3월", - "apr": "4월", - "may": "5월", - "jun": "6월", - "jul": "7월", - "aug": "8월", - "sep": "9월", - "oct": "10월", - "nov": "11월", - "dec": "12월", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "메뉴 열기" - }, - "advanced_search": { - "title": "상세 검색", - "from": "보낸 사람", - "from_placeholder": "이메일이나 이름", - "to": "받는 사람", - "to_placeholder": "이메일이나 이름", - "subject": "제목", - "subject_placeholder": "제목에 포함된 단어...", - "body": "본문", - "body_placeholder": "내용에 포함된 단어...", - "folder": "폴더", - "all_folders": "모든 폴더", - "has_attachment": "첨부파일 있음", - "date_after": "이후", - "date_before": "이전", - "size_min": "다음 크기 이상 (KB)", - "size_max": "다음 크기 미만 (KB)", - "starred": "별표 있음", - "unread": "읽지 않음", - "read": "읽음", - "yes": "예", - "no": "아니요", - "clear": "초기화", - "clear_all": "모두 초기화", - "filters_active": "{count}개의 필터 적용됨", - "filters_active_plural": "{count}개의 필터 적용됨", - "toggle_filters": "더보기", - "search_hint": "고급 필터로 더 정확하게 검색해 보세요", - "advanced_filters_tooltip": "상세 검색 필터", - "results_found": "{count}개의 검색 결과", - "results_found_more": "{count}개 이상의 검색 결과", - "suggestions_recent": "최근 검색", - "suggestions_people": "사람", - "suggestions_from": "{address}에서 온 메일 검색", - "suggestions_to": "{address}에게 보낸 메일 검색", - "suggestions_remove_recent": "최근 검색에서 삭제" - }, - "welcome": { - "title": "웹메일에 오신 것을 환영해요!", - "tip_compose": "c 키를 누르면 바로 새 메일을 쓸 수 있어요", - "tip_shortcuts": "? 키를 누르면 모든 단축키를 확인할 수 있어요", - "tip_sidebar": "캘린더, 연락처, 설정은 사이드바 메뉴에 있어요", - "tip_settings": "설정에서 나만의 스타일로 꾸며보세요", - "got_it": "확인했어요", - "settings": "설정", - "dismiss": "닫기", - "start_tour": "둘러보기 시작" - }, - "demo_welcome": { - "title": "Bulwark Mail에 오신 것을 환영해요", - "description": "브라우저에서 바로 완전한 기능을 갖춘 웹메일 클라이언트를 경험해 보세요. 모든 데이터는 내 기기에만 저장되니 안심하고 테스트해 보셔도 좋아요.", - "feature_email": "메일 읽기 및 쓰기", - "feature_organize": "태그, 별표, 폴더 관리", - "feature_shortcuts": "키보드 단축키", - "feature_privacy": "100% 프라이버시 보호 데모", - "hint": "왼쪽에서 메일을 클릭해서 시작하거나, 아래의 둘러보기를 클릭해 보세요." - }, - "files": { - "office_edit": "문서 편집", - "office_loading": "문서 편집기를 여는 중…", - "office_error": "문서 편집기를 열 수 없습니다.", - "office_close": "편집기 닫기", - "open_in_new_tab": "새 탭에서 열기", - "title": "파일", - "search_placeholder": "파일 검색...", - "empty_state_title": "아직 파일이 없어요", - "empty_state_description": "파일을 업로드하거나 폴더를 만들어 보세요", - "upload": "업로드", - "upload_files": "파일 업로드", - "new_folder": "새 폴더", - "new_folder_name": "폴더 이름", - "rename": "이름 바꾸기", - "rename_title": "이름 바꾸기", - "new_name": "새 이름", - "name_forbidden_chars": "서버에서 이름에 다음 문자를 허용하지 않습니다: {chars}", - "name_reserved": "이 이름은 서버에서 예약되어 있습니다.", - "delete": "삭제", - "delete_confirm_title": "항목 삭제", - "delete_confirm_message": "정말 \"{name}\"을(를) 삭제할까요? 이 작업은 되돌릴 수 없어요.", - "download": "다운로드", - "name": "이름", - "size": "크기", - "modified": "수정일", - "type": "유형", - "folder": "폴더", - "file": "파일", - "parent_directory": "상위 폴더", - "breadcrumb_root": "홈", - "drop_files_here": "여기에 파일이나 폴더를 끌어다 놓으세요", - "uploading": "업로드 중...", - "upload_success": "{count}개의 파일이 업로드되었어요", - "upload_error": "파일 업로드에 실패했어요", - "create_folder_success": "폴더가 만들어졌어요", - "create_folder_error": "폴더를 만들지 못했어요", - "delete_success": "성공적으로 삭제되었어요", - "delete_error": "삭제에 실패했어요", - "rename_success": "이름이 변경되었어요", - "rename_error": "이름을 바꾸지 못했어요", - "download_error": "다운로드에 실패했어요", - "not_available": "이 서버에서는 파일 저장소를 사용할 수 없어요", - "cancel": "취소", - "create": "만들기", - "save": "저장", - "no_results": "검색된 파일이 없어요", - "batch_delete_confirm_message": "선택한 {count}개의 항목을 정말 삭제할까요? 이 작업은 되돌릴 수 없어요.", - "batch_delete_success": "{count}개의 항목이 삭제되었어요", - "grid_view": "그리드 뷰", - "list_view": "리스트 뷰", - "details": "세부 정보", - "path": "경로", - "preview": "미리보기", - "preview_error": "미리보기를 불러오지 못했어요", - "cut": "잘라내기", - "copy": "복사", - "paste": "붙여넣기", - "move_success": "{count}개의 항목이 이동되었어요", - "move_error": "이동에 실패했어요", - "paste_success": "붙여넣기 성공", - "paste_error": "붙여넣기에 실패했어요", - "new_text_file": "새 텍스트 파일", - "file_name": "파일 이름", - "retry": "재시도", - "refresh": "새로고침", - "toggle_favorite": "즐겨찾기 켜기/끄기", - "duplicate": "복제", - "duplicate_success": "성공적으로 복제되었어요", - "duplicate_error": "복제에 실패했어요", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "파일이 만들어졌어요", - "create_file_error": "파일을 만들지 못했어요", - "favorites": "즐겨찾기", - "recent": "최근 파일", - "properties": "속성", - "open_folder": "폴더 열기", - "upload_folder": "폴더 업로드", - "file_too_large": "\"{name}\" 파일이 최대 크기({max})를 초과해요", - "undo": "실행 취소", - "undo_success": "작업이 취소되었어요", - "undo_error": "작업을 취소하지 못했어요", - "toolbar": "파일 액션", - "file_list": "파일 및 폴더", - "context_menu": "액션", - "settings_title": "파일 설정", - "settings_display": "화면 표시", - "settings_default_view": "기본 뷰", - "settings_default_view_desc": "그리드와 리스트 레이아웃 중 선택해 주세요", - "settings_default_sort": "기본 정렬", - "settings_default_sort_desc": "파일의 기본 정렬 기준을 선택해요", - "settings_sort_direction": "정렬 방향", - "settings_sort_direction_desc": "오름차순 또는 내림차순을 선택해요", - "settings_ascending": "오름차순", - "settings_descending": "내림차순", - "settings_icons": "아이콘", - "settings_show_icons": "파일 아이콘 표시", - "settings_show_icons_desc": "파일과 폴더 옆에 아이콘을 보여줘요", - "settings_colored_icons": "컬러 아이콘", - "settings_colored_icons_desc": "단색 대신 화사한 컬러 아이콘을 사용해요", - "settings_show_thumbnails": "미리보기 표시", - "settings_show_thumbnails_desc": "이미지 파일의 경우 아이콘 대신 미리보기를 보여줘요", - "settings_behavior": "동작", - "settings_show_hidden": "숨긴 파일 표시", - "settings_show_hidden_desc": "이름이 점(.)으로 시작하는 숨긴 파일과 폴더를 보여줘요", - "settings_folder_layout": "폴더 내비게이션", - "settings_folder_layout_desc": "폴더를 파일과 같이 보여줄지, 사이드바 트리 형태로 보여줄지 선택해요", - "settings_folder_layout_inline": "인라인", - "settings_folder_layout_sidebar": "사이드바", - "disabled_title": "관리자가 파일 기능을 비활성화했어요", - "disabled_description": "WebDAV를 통한 대용량 파일 업로드는 Stalwart/RocksDB의 불안정을 일으킬 수 있어요. 메모리 부족 크래시나 복구 불가능한 디스크 사용 문제가 발생할 수 있으며, 삭제된 파일이 즉시 삭제되지 않을 수 있습니다. 운영 환경에서는 이 기능을 권장하지 않아요.", - "stability_warning": "대용량 파일 업로드는 서버 불안정을 초래할 수 있습니다. 삭제된 파일이 즉각적으로 저장소에서 지워지지 않을 수 있으니 주의해서 사용해 주세요.", - "no_accounts": "연결된 계정이 없습니다.", - "open_folder_tree": "폴더 트리 열기", - "other_accounts": "다른 계정", - "migration_title": "파일 업데이트 중…", - "migration_description": "폴더와 파일을 올바른 구조로 정리하고 있습니다. 이 작업은 한 번만 수행됩니다." - }, - "smime": { - "your_certificates": "내 인증서", - "your_certificates_desc": "이메일 서명과 암호화를 위한 S/MIME 인증서를 가져오고 관리해요", - "recipient_certificates": "수신자 인증서", - "recipient_certificates_desc": "상대방에게 암호화된 메일을 보낼 때 필요한 공개 인증서예요", - "identity_bindings": "주소-키 연결", - "identity_bindings_desc": "내 이메일 주소와 S/MIME 인증서를 연결해 주세요", - "defaults_title": "기본 설정", - "defaults_desc": "기본 서명 및 암호화 동작을 설정해요", - "import_pkcs12": "PKCS#12 (.p12/.pfx) 가져오기", - "import_public_cert": "인증서 가져오기", - "no_certificates": "아직 가져온 인증서가 없어요", - "no_recipient_certs": "수신자 인증서가 없어요", - "expires": "만료일", - "expired": "만료됨", - "bound_to": "연결된 주소", - "no_key_bound": "없음", - "lock": "키 잠금", - "unlock": "키 잠금 해제", - "details": "자세히 보기", - "delete": "삭제", - "encrypt_by_default": "기본으로 암호화", - "encrypt_by_default_desc": "모든 수신자의 인증서가 있다면 자동으로 메일을 암호화해요", - "remember_unlocked": "잠금 해제 상태 유지", - "remember_unlocked_desc": "현재 브라우저 세션 동안 키의 잠금 해제 상태를 유지해요", - "sign_default_for": "기본으로 서명할 주소", - "enter_p12_passphrase": "PKCS#12 비밀번호 입력", - "p12_passphrase_desc": "이 인증서 파일을 보호하고 있는 비밀번호를 입력해 주세요", - "enter_storage_passphrase": "저장소 비밀번호 설정", - "storage_passphrase_desc": "브라우저에 저장될 키를 보호할 새로운 비밀번호를 설정해 주세요", - "next": "다음", - "import": "가져오기", - "unlock_key": "키 잠금 해제", - "unlock_key_desc": "서명하거나 암호를 풀기 위해 저장소 비밀번호를 입력해 주세요", - "passphrase_placeholder": "비밀번호 입력", - "confirm_passphrase_placeholder": "비밀번호 확인", - "passphrase_mismatch": "비밀번호가 서로 일치하지 않아요", - "cancel": "취소", - "processing": "처리 중…", - "close": "닫기", - "certificate_details": "인증서 세부 정보", - "cert_subject": "대상 (Subject)", - "cert_issuer": "발급자 (Issuer)", - "cert_email": "이메일", - "cert_serial": "일련번호", - "cert_validity": "유효 기간", - "cert_fingerprint": "지문 (SHA-256)", - "cert_algorithm": "알고리즘", - "cert_capabilities": "기능", - "cert_source": "출처", - "cert_expired": "이 인증서는 만료되었어요", - "cert_not_yet_valid": "이 인증서는 아직 유효하지 않아요", - "cap_sign": "서명", - "cap_encrypt": "암호화", - "cap_none": "없음", - "show_passphrase": "비밀번호 표시", - "hide_passphrase": "비밀번호 숨기기", - "sign_toggle": "서명", - "encrypt_toggle": "암호화", - "missing_recipient_certs": "다음 수신자의 인증서가 없어요: {emails}", - "missing_sender_cert": "이 이메일 주소에 연결된 인증서가 없어요", - "status_encrypted_ok": "암호화된 메시지예요", - "status_encrypted_no_key": "암호화된 메시지지만 일치하는 키를 찾지 못했어요", - "status_encrypted_failed": "이 메시지의 암호를 풀지 못했어요", - "status_signed_valid": "서명이 확인되었어요", - "status_signed_invalid": "서명 확인에 실패했어요", - "status_signed_expired_cert": "만료된 인증서로 서명되었어요", - "status_signed_self_signed": "서명은 유효하지만 인증서가 자체 서명되어 신뢰할 수 없어요", - "status_signed_mismatch": "서명은 유효하지만 서명자가 보낸 사람과 일치하지 않아요", - "status_unsupported": "지원하지 않는 S/MIME 형식이에요", - "auto_import_signer_certs": "발신자 인증서 자동 저장", - "auto_import_signer_certs_desc": "향후 암호화 메일을 보낼 수 있도록, 검증된 서명 메일에서 인증서를 자동으로 저장해요", - "export": "내보내기", - "enter_export_passphrase": "내보내기 비밀번호 설정", - "export_passphrase_desc": "내보낼 PKCS#12 파일을 보호할 비밀번호를 설정해 주세요", - "export_storage_desc": "내보내기를 위해 키를 해독해야 해요. 저장소 비밀번호를 입력해 주세요", - "incorrect_passphrase": "비밀번호가 올바르지 않아요" - }, - "tour": { - "step_counter": "총 {total}단계 중 {current}단계", - "skip": "투어 건너뛰기", - "back": "이전", - "next": "다음", - "finish": "완료", - "take_a_tour": "화면을 함께 둘러볼까요?", - "restart_title": "소개 투어", - "restart_desc": "인터페이스를 설명해 주는 투어를 다시 시작해요", - "restart_button": "투어 다시 시작", - "show_on_new_devices_title": "새 기기에서 표시", - "show_on_new_devices_desc": "다른 곳에서 이미 완료했더라도 새 기기에 처음 로그인할 때 환영 배너와 투어를 다시 표시해요", - "sidebar_title": "편지함", - "sidebar_desc": "여기는 폴더 사이드바예요. 폴더를 클릭하면 그 안의 메일을 볼 수 있어요. 폴더를 만들거나, 메일을 드래그해서 옮길 수 있고 안 읽은 메일 개수도 한눈에 확인돼요.", - "compose_title": "메일 쓰기", - "compose_desc": "여기를 클릭하면 새 메일을 쓸 수 있어요. 받는 사람을 추가하고, 파일을 첨부하거나 서식을 마음껏 꾸며보세요.", - "search_title": "메일 검색", - "search_desc": "보낸 사람, 제목, 내용으로 메일을 검색해 보세요. 필터 아이콘을 누르면 날짜, 첨부파일, 별표 여부 등 더 자세한 조건으로 찾을 수 있어요.", - "email_list_title": "메일 목록", - "email_list_desc": "메일들이 여기에 표시돼요. 하나를 클릭하면 오른쪽에 내용이 열립니다. 체크박스로 여러 개를 선택해서 한 번에 지우거나 태그를 달 수도 있어요.", - "email_viewer_title": "읽기 창", - "email_viewer_desc": "선택한 메일이 여기서 열려요. 상단 버튼으로 바로 답장, 전달, 보관, 삭제할 수 있고 별표나 색상 태그를 달아 예쁘게 정리할 수 있어요.", - "keywords_title": "색상 태그", - "keywords_desc": "색상 태그로 메일을 분류해 보세요. 메일을 끌어다 태그 위에 놓거나, 메일을 우클릭해서 태그를 지정할 수 있답니다.", - "calendar_title": "캘린더", - "calendar_desc": "일정을 관리하려면 캘린더로 이동해 보세요. 일정을 만들고, 알림을 설정하며, 일간/주간/월간 뷰로 깔끔하게 확인할 수 있어요.", - "contacts_title": "연락처", - "contacts_desc": "내 주소록이 있는 곳이에요. 연락처를 가져오거나 그룹을 만들 수 있고, 연락처를 클릭하면 세부 정보를 모두 볼 수 있어요.", - "settings_title": "설정", - "settings_desc": "모든 걸 내 취향에 맞게 바꿔보세요! 테마, 밀도, 서명, 필터 규칙, 단축키, 캘린더 기본값까지 입맛대로 설정할 수 있어요.", - "shortcuts_title": "키보드 단축키", - "shortcuts_desc": "파워 유저라면 필수죠! 언제든 ? 키를 누르면 단축키 목록이 나와요. 마우스 없이도 이동하고, 메일을 쓰고 관리할 수 있어요.", - "compose_open_title": "메일 쓰기 창", - "compose_open_desc": "메일 작성기예요. 수신자를 넣고 내용을 쓰며, 파일을 첨부하거나 서식을 적용할 수 있어요. 임시보관이나 템플릿 기능도 알차게 준비되어 있어요.", - "calendar_view_title": "내 캘린더", - "calendar_view_desc": "샘플 일정이 들어있는 캘린더 화면이에요. 위쪽 도구 모음에서 일간, 주간, 월간, 목록 뷰로 자유롭게 전환해 보세요.", - "create_event_title": "일정 만들기", - "create_event_desc": "이 버튼을 눌러 새 일정을 만들어 보세요. 제목과 날짜, 시간을 정하고 함께할 사람을 초대할 수 있어요.", - "event_modal_title": "일정 세부 정보", - "event_modal_desc": "일정 입력 폼이에요. 제목, 날짜, 시간, 위치를 채우고 참석자를 추가해 보세요. 다 썼다면 저장을 누르면 돼요!", - "contacts_list_title": "내 연락처", - "contacts_list_desc": "등록된 연락처들이에요. 클릭하면 오른쪽에 자세한 내용이 나와요. 새 연락처를 추가하거나 vCard로 가져오고, 그룹으로 예쁘게 묶어보세요.", - "settings_tabs_title": "설정 메뉴", - "settings_tabs_desc": "설정 카테고리들이 모여있어요. 화면 테마를 바꾸고, 보내는 사람 주소를 관리하고, 자동 필터를 만들며, 캘린더 설정 등 아주 많은 걸 할 수 있어요.", - "files_title": "파일 저장소", - "files_desc": "파일 탐색기를 통해 파일을 업로드하고, 폴더로 정리하며 공유할 수 있어요. 메일함 안의 작은 개인 클라우드 드라이브라고 생각하시면 돼요.", - "demo_banner_title": "데모 컨트롤러", - "demo_banner_desc": "현재 데모 모드예요. 모든 작업은 브라우저 안에서만 이뤄집니다. 언제든 '초기화'를 누르면 처음의 깨끗한 샘플 데이터로 돌아가요.", - "quota_title": "저장 공간 사용량", - "quota_desc": "편지함 용량을 여기서 확인하세요. 공간을 많이 쓸수록 원이 점점 채워질 거예요." - }, - "unified_mailbox": { - "search_unavailable": "통합 보기에서는 검색을 사용할 수 없습니다" - }, - "calendar_event_notifications": { - "someone": "누군가", - "invited": "{name}님이 \"{title}\"에 초대했습니다", - "updated": "{name}님이 \"{title}\"을(를) 업데이트했습니다", - "cancelled": "{name}님이 \"{title}\"을(를) 취소했습니다", - "untitled": "제목 없는 일정" - }, - "share_notifications": { - "someone": "누군가", - "shared": "{name}님이 {kind} \"{object}\"을(를) 공유했습니다", - "changed": "{name}님이 {kind} \"{object}\"에 대한 접근 권한을 변경했습니다", - "revoked": "{name}님이 {kind} \"{object}\"에 대한 접근 권한을 제거했습니다", - "object": { - "folder": "폴더", - "calendar": "캘린더", - "address_book": "주소록", - "files": "파일 폴더" - } - }, - "sharing": { - "title": "\"{name}\" 공유", - "description": "이 서버의 다른 사용자나 그룹에 액세스 권한을 부여합니다. 변경 사항은 즉시 적용됩니다.", - "no_shares": "아직 공유되지 않았습니다.", - "add_person": "사용자 또는 그룹 추가", - "search_placeholder": "이름 또는 이메일로 검색…", - "loading_principals": "사용자 불러오는 중…", - "no_principals": "다른 사용자나 그룹을 찾을 수 없습니다.", - "no_match": "일치하는 항목이 없습니다.", - "remove": "액세스 권한 제거", - "group": "그룹", - "share_added": "액세스 권한이 부여되었습니다", - "share_updated": "액세스 권한이 업데이트되었습니다", - "share_removed": "액세스 권한이 제거되었습니다", - "share_failed": "공유 업데이트에 실패했습니다", - "preset": { - "freeBusy": "한가함/바쁨만", - "read": "읽기 전용", - "readWrite": "읽기 및 쓰기", - "manager": "관리자", - "custom": "사용자 지정" - } - }, - "quote_header": { - "reply_line": "{date}에 {from}님이 작성:", - "forwarded_separator": "---------- 전달된 메시지 ----------", - "from_label": "보낸 사람", - "date_label": "날짜", - "subject_label": "제목" - }, - "pwa_install": { - "title": "{appName} 설치", - "description": "빠른 액세스와 오프라인 지원을 위해 앱을 설치하세요.", - "not_now": "나중에", - "install": "설치", - "dont_remind": "다시 알리지 않음", - "dismiss_aria": "설치 프롬프트 닫기" - }, - "deep_link": { - "message_not_found": "이 메시지를 더 이상 사용할 수 없습니다.", - "thread_not_found": "이 대화를 더 이상 사용할 수 없습니다.", - "folder_not_found": "이 폴더를 더 이상 사용할 수 없습니다.", - "event_not_found": "이 일정을 더 이상 사용할 수 없습니다.", - "file_not_found": "이 파일을 더 이상 사용할 수 없습니다.", - "account_unavailable": "이 링크를 열려면 해당 계정으로 로그인하세요.", - "copy_message": "메시지 링크 복사", - "copy_conversation": "대화 링크 복사", - "copy_event": "일정 링크 복사", - "copied": "링크가 복사되었습니다", - "copy_failed": "링크를 복사하지 못했습니다" - }, - "plugins": { - "oauth_callback": { - "completing": "인증 완료 중…", - "completing_message": "연결이 설정되는 동안 잠시 기다려 주세요.", - "received_title": "인증 수신됨", - "received_message": "연결을 시작했던 탭으로 돌아갈 수 있습니다.", - "failed_title": "인증 실패", - "denied": "인증이 거부되었습니다.", - "failed_detail": "인증 실패: {error}", - "missing_params": "인증 코드 또는 상태가 누락되었습니다.", - "delivery_failed": "인증 결과를 앱 탭에 전달하지 못했습니다. 이 창을 닫고 다시 시도해 주세요." - } - }, - "global_search": { - "title": "전체 검색", - "placeholder": "메일, 연락처, 캘린더, 파일 검색...", - "scope_label": "검색 범위", - "account_label": "계정", - "scope_all": "전체", - "scope_mail": "메일", - "scope_contacts": "연락처", - "scope_calendar": "캘린더", - "scope_files": "파일", - "all_accounts": "모든 계정", - "show_all": "모든 결과 보기", - "no_results": "“{query}”에 대한 결과가 없습니다", - "server_hint": "완전한 단어는 서버에서 검색되며, 단어 일부는 이미 불러온 항목과 일치합니다", - "searching": "검색 중...", - "account_error": "{account}을(를) 검색할 수 없습니다", - "recent_searches": "최근 검색", - "recurring": "반복", - "no_subject": "(no subject)", - "empty_hint": "모든 계정의 메일, 연락처, 일정, 파일을 검색하세요. from:, subject:, has:attachment, after:2026-01-01 도 사용할 수 있습니다.", - "flat_by_date": "날짜순 평뱀 보기", - "grouped": "그룹 보기", - "load_more": "더 불러오기", - "preview_empty": "미리 볼 결과를 선택하세요", - "open_full": "열기", - "view_label": "보기" - } -} +{ + "meta_description": "JMAP 프로토콜을 사용하는 미니멀한 웹메일 클라이언트", + "login": { + "title": "웹메일", + "username_label": "이메일", + "username_placeholder": "user@example.com", + "password_label": "비밀번호", + "password_placeholder": "비밀번호를 입력해 주세요", + "jmap_endpoint_label": "JMAP 서버", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "서버에서 이 도메인의 CORS 요청을 허용해야 해요.", + "jmap_server_label": "서버", + "jmap_server_auto_picked": "이메일 도메인에서 서버를 선택했습니다.", + "sign_in": "로그인", + "signing_in": "로그인 중...", + "loading": "불러오는 중...", + "reconnecting": "연결이 끊어졌어요. 다시 연결을 시도할게요...", + "error": { + "invalid_credentials": "이메일이나 비밀번호가 맞지 않아요. 다시 확인해 주세요.", + "invalid_token": "서버가 이 액세스 토큰을 받아들이지 않았어요. 토큰이 완전한지, 만료되거나 취소되지 않았는지 확인해 주세요.", + "connection_failed": "서버에 연결할 수 없어요. 인터넷 연결을 확인하고 다시 시도해 주세요.", + "cors_blocked": "서버에는 연결되었지만 교차 출처 요청(CORS)이 차단되었어요. JMAP 서버 설정을 확인해 주세요.", + "server_error": "일시적으로 서버를 사용할 수 없어요. 나중에 다시 시도해 주세요.", + "generic": "예상치 못한 오류가 발생했어요. 계속되면 관리자에게 문의해 주세요.", + "totp_required": "2단계 인증 코드가 필요해요. 아래에 코드를 입력해 주세요.", + "totp_invalid": "인증 코드가 올바르지 않아요. 인증 앱을 확인하고 다시 시도해 주세요.", + "oauth_discovery_failed": "SSO가 활성화되어 있지만 ID 공급자에 연결할 수 없어요. OAuth 설정을 확인해 주세요.", + "token_exchange_failed": "비밀번호와 코드는 확인되었지만 메일 서버가 이 앱의 세션 시작을 거부했습니다. 관리자에게 OAuth 클라이언트 설정을 확인해 달라고 요청하세요." + }, + "show_password": "비밀번호 표시", + "hide_password": "비밀번호 숨기기", + "totp_toggle": "2단계 인증 코드가 있어요", + "token_toggle": "액세스 토큰으로 로그인", + "password_toggle": "비밀번호로 로그인", + "token_label": "액세스 토큰", + "token_placeholder": "API 토큰을 붙여 넣으세요", + "token_hint": "메일 서비스 설정에서 API 토큰을 만든 후 여기에 붙여 넣으세요.", + "remember_me": "로그인 유지", + "config_error": { + "title": "설정 오류", + "fetch_failed": "앱 설정을 불러올 수 없어요. 나중에 다시 시도해 주세요.", + "server_not_configured": "메일 서버가 설정되지 않았어요. 관리자에게 문의해 주세요." + }, + "remove_from_history": "기록에서 삭제", + "totp_label": "인증 코드", + "totp_placeholder": "000000", + "session_expired": "세션이 만료되었어요. 다시 로그인해 주세요.", + "dismiss": "닫기", + "or": "또는", + "sign_in_sso": "SSO로 로그인", + "add_account_title": "계정 추가", + "add_account_subtitle": "다른 계정으로 로그인하기", + "cancel": "취소", + "website": "웹사이트", + "imprint": "회사 소개", + "privacy_policy": "개인정보 처리방침", + "try_demo": "데모 체험하기", + "demo_description": "계정 없이 샘플 데이터로 둘러보세요", + "demo_launching": "데모를 시작하는 중...", + "demo_login_button": "데모 시작", + "demo_tagline": "모든 기능을 갖춘 이메일 클라이언트를 경험해 보세요. 계정이 없어도 괜찮아요.", + "demo_no_signup": "가입할 필요 없이 샘플 데이터로 자유롭게 둘러보세요", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "로그인을 완료하는 중...", + "oauth_error": { + "title": "인증 실패", + "invalid_state": "보안 검증에 실패했어요. 다시 로그인해 주세요.", + "missing_params": "인증 데이터가 없어요. 다시 로그인해 주세요.", + "token_exchange_failed": "인증을 완료하지 못했어요. 다시 시도해 주세요.", + "access_denied": "접근이 거부되었어요. 관리자에게 문의해 주세요.", + "back_to_login": "로그인 화면으로 돌아가기" + } + }, + "sidebar": { + "close": "닫기", + "compose": "메일 쓰기", + "compose_hint": "메일 쓰기 (c)", + "search_placeholder": "메일 검색...", + "search_placeholder_hint": "메일 검색... (/ 누르기)", + "storage": "저장 공간", + "storage_used": "사용 중", + "storage_free": "여유", + "storage_total": "전체", + "sign_out": "로그아웃", + "sign_out_of": "{account}에서 로그아웃", + "sign_out_all": "모든 계정에서 로그아웃", + "add_account": "계정 추가", + "set_as_default": "기본값으로 설정", + "switch_account": "계정 전환", + "contacts": "연락처", + "calendar": "캘린더", + "settings": "설정", + "admin": "관리자", + "files": "파일", + "loading_mailboxes": "편지함을 불러오는 중...", + "push_connected": "실시간 업데이트 켜짐", + "push_disconnected": "실시간 업데이트 꺼짐", + "keyboard_shortcuts": "단축키", + "theme": { + "light": "라이트 모드", + "dark": "다크 모드", + "system": "시스템 테마" + }, + "language": { + "title": "언어" + }, + "mailboxes": { + "inbox": "받은편지함", + "sent": "보낸편지함", + "drafts": "임시보관함", + "trash": "휴지통", + "archive": "보관함", + "starred": "별표 편지함", + "all_mail": "모든 메일", + "spam": "스팸함", + "important": "중요 편지함" + }, + "unified_inbox": "통합 받은편지함", + "unified_sent": "모든 보낸편지함", + "unified_drafts": "모든 임시보관함", + "unified_trash": "모든 휴지통", + "unified_archive": "모든 보관함", + "unified_junk": "모든 스팸함", + "all_accounts": "모든 계정", + "all_accounts_loading": "계정 불러오는 중: {total}개 중 {loaded}개", + "unified_mailbox": "통합 메일함", + "expand": "펼치기", + "collapse": "접기", + "expand_tooltip": "펼치기", + "collapse_tooltip": "접기", + "mobile": { + "search": "검색", + "compose": "메일 쓰기", + "go_back": "뒤로 가기", + "toggle_menu": "메뉴 전환" + }, + "clear_search": "검색어 지우기", + "vacation_active": "부재중 자동 응답이 켜져 있어요", + "demo_banner": "데모 모드", + "demo_reset": "초기화", + "demo_tour": "둘러보기", + "tags": "태그", + "show_all_tags": "전체 보기({count})", + "show_fewer_tags": "간략히 보기", + "folders": "폴더", + "your_folders": "내 폴더", + "create_folder": "폴더 만들기", + "show_more": "더보기", + "show_fewer": "간략히", + "mail": "메일", + "nav_label": "내비게이션", + "add_app": "앱", + "shared": "공유됨", + "shared_account_hidden": "사이드바에서 숨겼습니다. 설정 › 레이아웃에서 다시 표시할 수 있습니다.", + "notifications_muted": "알림 꺼짐", + "scheduled": "예약됨", + "unified_all_unread": "모든 읽지 않음", + "unified_all_starred": "모든 별표", + "unified_all_mail": "모든 메일", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "라벨", + "create_label": "새 라벨 만들기", + "mv_tasks": "할 일", + "mv_side_panel": "측면 패널", + "mv_open_full": "전체 보기로 열기", + "mv_previous_day": "이전 날", + "mv_next_day": "다음 날", + "mv_add_another_account": "다른 계정 추가", + "mv_manage_account": "계정 관리", + "mv_storage_used": "{total} 중 {percent}% 사용" + }, + "protocol_handlers": { + "title": "기본 앱", + "description": "이메일 및 캘린더 링크를 Bulwark에서 열지 선택하세요. 기술적으로 Bulwark는 mailto: 및 webcal: 링크의 프로토콜 핸들러로 등록됩니다.", + "unsupported": "이 브라우저 또는 연결은 수동 프로토콜 핸들러 등록을 지원하지 않습니다. 설치된 PWA는 브라우저 또는 시스템 설정을 통해 사용할 수 있을 수 있습니다.", + "mailto_label": "이메일 링크", + "mailto_description": "mailto: 링크를 Bulwark의 미리 채워진 작성 창에서 엽니다.", + "protocol_open_mode_label": "프로토콜 링크를 열 때", + "protocol_open_mode_description": "Bulwark가 mailto: 및 webcal: 링크를 새 탭에서 열지, 열린 세션을 재사용할지 선택하세요. 활성 세션 옵션은 브라우저가 포커스를 차단할 때 Bulwark를 앞으로 가져오기 위한 대체 알림을 클릭할 수 있도록 알림 권한이 필요합니다.", + "protocol_open_mode_active_session": "가능하면 활성 세션에서 열기", + "protocol_open_mode_new_tab": "항상 새 탭 열기", + "focus_notification_title": "Bulwark 열기", + "focus_notification_body": "링크가 Bulwark에서 열렸습니다. 창을 앞으로 가져오려면 클릭하세요.", + "webcal_label": "캘린더 링크", + "webcal_description": "webcal: 링크를 Bulwark의 미리 채워진 캘린더 구독 대화상자에서 엽니다.", + "register_mailto": "이메일 앱 등록", + "register_webcal": "캘린더 앱 등록", + "mailto_registered": "이메일 핸들러 등록을 요청했습니다", + "webcal_registered": "캘린더 핸들러 등록을 요청했습니다", + "registration_failed": "프로토콜 핸들러 등록에 실패했습니다", + "opening_mailto": "작성 창을 여는 중...", + "opening_webcal": "캘린더를 여는 중...", + "browser_note": "브라우저 또는 운영 체제에서 확인을 요청할 수 있으며, 기본 앱으로 선택하기 전에 Bulwark 설치가 필요할 수 있습니다.", + "select_account_title": "계정 선택", + "select_mailto_account": "이 이메일 링크를 열 계정을 선택하세요.", + "select_webcal_account": "이 캘린더 링크를 열 계정을 선택하세요.", + "select_account_note": "이 선택은 이 프로토콜 링크에만 적용됩니다.", + "detail_to": "받는 사람", + "detail_subject": "제목", + "detail_no_subject": "제목 없음", + "detail_calendar": "캘린더", + "detail_source": "출처", + "active_account": "활성", + "switching_account": "계정 전환 중..." + }, + "sidebar_apps": { + "modal_title": "사이드바 앱", + "add_new": "앱 추가", + "edit_app": "앱 수정", + "name_label": "이름", + "name_placeholder": "내 앱", + "name_required": "이름을 입력해 주세요", + "url_label": "URL", + "url_required": "URL을 입력해 주세요", + "url_invalid": "올바른 http 또는 https URL을 입력해 주세요", + "icon_label": "아이콘", + "icon_required": "아이콘을 선택해 주세요", + "open_mode_label": "열기 모드", + "open_new_tab": "새 탭", + "open_inline": "인라인", + "cancel": "취소", + "add": "추가", + "update": "업데이트", + "delete": "삭제", + "delete_confirm_title": "앱 삭제", + "delete_confirm": "정말 \"{name}\" 앱을 삭제할까요?", + "no_apps": "아직 추가된 앱이 없어요", + "no_apps_hint": "사이드바에 맞춤형 앱과 링크를 추가해 보세요", + "managed_title": "관리자가 제공", + "managed_description": "이 앱은 모든 사용자를 위해 설정되었으며 변경할 수 없습니다.", + "managed_badge": "관리됨", + "search_icons": "아이콘 검색...", + "show_popular": "인기", + "show_all": "전체", + "no_icons_found": "아이콘을 찾을 수 없어요", + "inline_badge": "인라인", + "tab_badge": "탭", + "show_on_mobile": "모바일에서 보기" + }, + "email_list": { + "columns": { + "from": "보낸 사람", + "to": "받는 사람", + "subject": "제목", + "date": "날짜", + "size": "크기" + }, + "no_emails": "메시지가 없어요", + "no_emails_description": "편지함이 비어있어요", + "no_search_results": "검색 결과가 없어요", + "no_search_results_description": "검색어나 필터를 변경해 보세요", + "loading": "메일을 불러오는 중...", + "unread": "읽지 않음", + "to_me": "나에게", + "to_recipients": "수신자 {count}명", + "and_others": "외 {count}명", + "draft": "임시보관", + "starred": "별표 표시됨", + "pinned": "고정됨", + "replied": "답장함", + "forwarded": "전달함", + "has_attachment": "첨부 파일 있음", + "conversations_range": "{total, number}개 중 {from}–{to, number}", + "conversations_count": "전체 {total}개 중 {count}개의 대화", + "conversations_count_plus": "{count}개 이상의 대화", + "conversations_count_simple": "{count}개의 대화", + "no_conversations": "대화가 없어요", + "loading_more": "메일을 더 불러오는 중...", + "no_more_emails": "더 이상 메일이 없어요", + "batch_actions": { + "select": "메일 선택", + "select_all": "모두 선택", + "selected_messages": "이메일 {count, plural, one {1개} other {#건}} 선택됨", + "mark_read": "읽은 상태로 표시", + "mark_unread": "읽지 않은 상태로 표시", + "delete": "삭제", + "delete_confirm_title": "메일 삭제", + "delete_confirm_message": "정말 선택한 {count, plural, other {#개의 메일}}을 삭제할까요?", + "clear_selection": "선택 취소" + }, + "permanent_delete": "영구 삭제", + "permanent_delete_confirm_title": "영구 삭제", + "permanent_delete_confirm_message": "이 메일은 영구적으로 삭제되며, 다시 복구할 수 없어요.", + "permanent_delete_confirm_batch_message": "선택한 {count, plural, other {#개의 메일}}이 영구적으로 삭제돼요. 이 작업은 되돌릴 수 없어요.", + "empty_folder": { + "button": "폴더 비우기", + "confirm_title": "폴더 비우기", + "confirm_message": "이 폴더의 모든 메일이 영구적으로 삭제돼요. 이 작업은 되돌릴 수 없어요.", + "confirm_message_trash": "이 폴더의 모든 메일이 휴지통으로 이동돼요.", + "confirm_button": "폴더 비우기", + "junk_hint": "스팸함을 비워 모든 메시지를 영구적으로 삭제할 수 있어요.", + "trash_hint": "휴지통을 비워 모든 메시지를 영구적으로 삭제할 수 있어요." + }, + "no_scheduled_emails": "예약된 메일 없음", + "no_scheduled_emails_description": "나중에 보내도록 예약한 메시지가 여기에 표시됩니다.", + "scheduled_count": "{count, plural, one {예약된 메일 1개} other {예약된 메일 #개}}", + "scheduled_actions_hint": "예약된 메시지는 예약 작업에서 취소, 재예약 또는 편집할 수 있습니다.", + "cancel_scheduled_send": "보내기 취소", + "reschedule_send": "다시 예약", + "cancel_and_edit": "취소하고 편집", + "cancel_and_compose_again": "취소하고 다시 작성", + "reschedule_prompt": "새 날짜/시간을 입력하세요. 예: 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "보낸 사람이 읽음 확인을 요청합니다:", + "send": "확인 보내기", + "ignore": "무시", + "sent": "읽음 확인을 보냈습니다.", + "send_failed": "읽음 확인을 보낼 수 없습니다", + "mdn_subject": "읽음: {subject}", + "mdn_body": "이것은 {recipient}(으)로 보낸 메시지에 대한 읽음 확인입니다.\n\n참고: 이 확인은 메시지가 수신자의 컴퓨터에 표시되었음을 알릴 뿐입니다. 수신자가 내용을 읽거나 이해했다는 보장은 없습니다." + }, + "no_email_selected": "메일이 선택되지 않았어요", + "no_email_description": "목록에서 메일을 선택하면 여기에 내용이 표시돼요", + "no_conversation_selected": "대화가 선택되지 않았어요", + "no_conversation_description": "목록에서 대화를 선택해 주세요", + "compose": "메일 쓰기", + "compose_hint": "새 메시지 작성", + "no_subject": "(제목 없음)", + "no_body_content": "(본문 내용 없음)", + "no_preview_available": "미리 보기를 사용할 수 없습니다", + "show_quoted_text": "인용된 텍스트 표시", + "hide_quoted_text": "인용된 텍스트 숨기기", + "loading_email": "메일을 불러오는 중...", + "loading": "불러오는 중...", + "reply": "답장", + "reply_all": "전체 답장", + "forward": "전달", + "delete": "삭제", + "archive": "보관", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "별표 달기", + "unstar": "별표 해제", + "mark_unread": "읽지 않은 상태로 표시", + "mark_read": "읽은 상태로 표시", + "unread": "안 읽음", + "read": "읽음", + "spam_short": "스팸", + "not_spam_short": "스팸 아님", + "move": "이동", + "print": "인쇄", + "view_source": "원본 보기", + "fullscreen": "전체 화면", + "exit_fullscreen": "전체 화면 종료", + "view_in_dark_mode": "다크 모드로 보기", + "view_in_light_mode": "라이트 모드로 보기", + "export_email": ".eml 파일로 내보내기", + "forward_as_attachment": "첨부 파일로 전달", + "import_email": ".eml 또는 .zip 가져오기", + "keyboard_shortcuts": "단축키 (?)", + "email_source": "메일 원본", + "draft_banner": "작성 중인 임시보관 메일이에요", + "edit_draft": "수정", + "copy_source": "클립보드에 복사", + "source_copied": "원본이 클립보드에 복사되었어요", + "attachments": "첨부파일", + "important": "중요", + "download": "다운로드", + "download_all": "모두 다운로드", + "from": "보낸 사람", + "to": "받는 사람", + "cc": "참조", + "bcc": "숨은 참조", + "date": "날짜", + "subject": "제목", + "show_details": "자세히 보기", + "hide_details": "간략히 보기", + "external_content_warning": "이미지와 외부 콘텐츠가 차단되었어요", + "load_external_content": "이미지 불러오기", + "trust_sender": "이 보낸 사람 항상 신뢰하기", + "sender_check": { + "unverified_label": "확인되지 않은 보낸 사람", + "failed_label": "보낸 사람 확인 실패", + "unverified": "이 메시지는 {domain}에서 보냈다고 하지만 이를 뒷받침하는 근거가 없어요. {domain}의 유효한 서명이 없고, {domain}에서 허용한 서버에서 보낸 것도 아니에요.", + "unverified_sent_from": "이 메시지는 {domain}에서 보냈다고 하지만 실제로는 {host}에서 발송되었고 {domain}의 유효한 서명이 없어요.", + "failed": "이 메시지는 {domain}에서 보냈다고 하지만 해당 도메인의 보낸 사람 확인을 통과하지 못했어요. 위조된 메시지일 수 있어요.", + "failed_sent_from": "이 메시지는 {domain}에서 보냈다고 하지만 실제로는 {host}에서 발송되었고 {domain}의 보낸 사람 확인을 통과하지 못했어요. 위조된 메시지일 수 있어요.", + "caution": "링크, 첨부 파일, 비밀번호나 결제 정보 요청에 주의하세요." + }, + "back_to_list": "목록으로", + "view_contact": "연락처 보기", + "message_details": "메시지 상세 정보", + "more_reply_options": "답장 옵션 더보기", + "set_tag": "태그 설정", + "tag": "태그", + "more_actions": "작업 더보기", + "previous": "이전", + "next": "다음", + "move_to": "이동...", + "remove_tag": "태그 제거", + "tag_filter_placeholder": "태그 검색", + "tag_no_matches": "일치하는 태그 없음", + "more_count": "+{count}개 더보기", + "characters_count": "{count}자", + "quick_reply_placeholder": "간단하게 답장을 작성해 보세요...", + "more_options": "옵션 더보기", + "sending": "보내는 중...", + "security_authentication": "보안 및 인증", + "technical_details": "기술 세부 정보", + "message_id_label": "Message-ID:", + "reply_to_label": "Reply-To:", + "delivery_time_label": "수신 시간:", + "conversation_part_label": "포함된 대화:", + "previous_messages": "이전 메시지 {count}개", + "previous_messages_plural": "이전 메시지 {count}개", + "time": { + "day": "일", + "days": "일", + "hour": "시간", + "hours": "시간", + "minute": "분", + "minutes": "분" + }, + "unknown_sender": "알 수 없음", + "recipient_me": "나", + "recipient_and_others": "{name}님 외 {count}명", + "recipient_to_prefix": "받는 사람:", + "authentication": { + "title": "인증", + "status": { + "verified": "확인됨", + "warning": "주의", + "none": "인증되지 않음" + }, + "spf": { + "pass": "SPF 통과", + "fail": "SPF 실패", + "none": "SPF 없음" + }, + "dkim": { + "pass": "DKIM 유효", + "fail": "DKIM 무효", + "none": "DKIM 없음" + }, + "dmarc": { + "pass": "DMARC 통과", + "fail": "DMARC 실패", + "none": "DMARC 없음" + }, + "spam_score": "스팸 점수", + "tooltip_spf": "Sender Policy Framework: 발신 서버가 해당 도메인을 대신해 메일을 보낼 권한이 있는지 확인합니다", + "tooltip_dkim": "DomainKeys Identified Mail: 암호화 서명을 통해 메일이 전송 중에 변경되지 않았음을 확인합니다", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: SPF와 DKIM이 발신자 도메인과 일치하는지 확인하고 실패에 대한 정책을 설정합니다", + "policy": "정책", + "result": { + "pass": "통과", + "fail": "실패", + "softfail": "경미한 실패", + "neutral": "중립", + "permerror": "영구 오류", + "temperror": "일시적 오류", + "none": "없음" + } + }, + "headers": { + "routing": "라우팅", + "received": "수신됨", + "message_id": "메시지 ID", + "list_info": "목록 정보" + }, + "tooltips": { + "reply": "답장 (r)", + "reply_all": "전체 답장 (a)", + "forward": "전달 (f)", + "archive": "보관 (e)", + "delete": "삭제 (# 또는 Del)", + "star": "별표 달기 (s)", + "unstar": "별표 해제 (s)", + "compose": "메일 쓰기 (c)", + "previous": "이전 메일", + "next": "다음 메일", + "edit_draft": "임시보관 메일 수정" + }, + "spam": { + "button_title": "스팸 신고", + "not_spam_title": "정상 메일로 표시", + "toast_success": "스팸함으로 이동되었어요", + "toast_batch": "{count}개의 메일이 스팸함으로 이동되었어요", + "toast_undo": "실행 취소", + "toast_not_spam_success": "받은편지함으로 이동되었어요", + "toast_not_spam_batch": "{count}개의 메일이 받은편지함으로 이동되었어요", + "error": "스팸 신고에 실패했어요", + "error_not_spam": "메일 복구에 실패했어요" + }, + "unsubscribe_banner": { + "label": "뉴스레터", + "button": "구독 취소", + "confirm_title": "이 발신자의 메일 구독을 취소할까요?", + "confirm_button": "확인", + "cancel": "취소", + "success_http": "새 탭에서 구독 취소 페이지가 열렸어요", + "success_mailto": "수신 거부 이메일을 보냈습니다", + "confirm_message_http": "수신 거부 페이지가 새 탭에서 열립니다.", + "confirm_message_mailto": "발신자에게 수신 거부 이메일을 보냅니다.", + "error": "구독을 취소할 수 없어요", + "dismiss": "닫기" + }, + "calendar_invitation": { + "loading": "일정 세부 정보를 불러오는 중...", + "title": "캘린더 초대", + "published_title": "공개된 일정", + "response_title": "일정 응답", + "update_title": "일정 업데이트", + "counter_title": "시간 제안", + "refresh_title": "새로고침 요청", + "declined_counter_title": "시간 제안이 거절되었어요", + "cancelled_title": "일정이 취소되었어요", + "organizer": "주최자: {name}", + "organizer_label": "주최자:", + "attendees": "{count, plural, other {참석자 #명}}", + "accept": "수락", + "maybe": "미정", + "decline": "거절", + "add_to_calendar": "캘린더에 추가", + "added": "캘린더에 추가되었어요", + "rsvp_sent": "응답이 전송되었어요", + "parse_error": "초대장을 읽을 수 없어요", + "action_failed": "캘린더 작업을 완료할 수 없어요.", + "no_calendar": "캘린더를 사용할 수 없어요", + "published_info": "참고용으로 공유된 일정이에요.", + "response_info": "참석자의 응답이 포함된 메시지예요.", + "response_info_organizer": "참석자의 응답이 일정에 업데이트돼요.", + "update_info": "기존 일정이 업데이트된 메시지예요.", + "counter_info": "일정 변경을 제안하는 메시지예요.", + "counter_info_organizer": "참석자가 일정 변경을 제안했어요.", + "refresh_info": "최신 일정 세부 정보를 요청하는 메시지예요.", + "refresh_info_organizer": "참석자가 최신 일정 세부 정보를 요청했어요.", + "declined_counter_info": "주최자가 제안한 변경 사항을 거절했어요.", + "authentication_failed_info": "이 초대장의 메일 인증이 실패했어요. 캘린더 작업 시 주의가 필요해요.", + "authentication_missing_info": "이 초대장에는 확인된 메일 인증 정보가 없어요. 이상한 점이 있다면 주최자에게 확인해 보세요.", + "sender_mismatch_info": "초대장은 {sender}님이 보냈지만, 캘린더에 등록된 주최자는 {organizer}님이에요.", + "sender_mismatch_unverified_info": "초대장은 {sender}님이 보냈지만 캘린더의 주최자는 {organizer}님이며, 메시지를 검증할 수 없어요.", + "organizer_role": "내가 주최하는 일정이에요", + "your_response": "내 응답: {status}", + "response_needed": "응답이 필요해요", + "response_accepted": "수락함", + "response_tentative": "미정", + "response_declined": "거절함", + "response_delegated": "위임됨", + "actor_sent_info": "{name}님이 보냈어요.", + "actor_response_info": "{name}님이 {status}했어요.", + "actor_counter_info": "{name}님이 일정 변경을 제안했어요.", + "actor_refresh_info": "{name}님이 최신 일정 정보를 요청했어요.", + "actor_declined_counter_info": "{name}님이 변경 제안을 거절했어요.", + "actor_note": "메모: {comment}", + "actor_unknown": "알 수 없는 사용자", + "proposed_changes": "제안된 변경 사항", + "change_title": "제목", + "change_time": "시간", + "change_location": "위치", + "change_description": "설명", + "change_empty": "없음", + "change_from_to": "{before} -> {after}", + "apply_proposal": "제안된 변경 사항 적용", + "proposal_applied": "제안된 변경 사항이 적용되었어요.", + "review_proposal": "제안 검토", + "review_request": "요청 검토", + "view_in_calendar": "캘린더에서 보기", + "select_calendar": "캘린더 선택", + "already_in_calendar": "이미 내 캘린더에 있어요", + "request_info": "이 일정에 초대받았어요. 참석 가능 여부를 주최자에게 알려주세요.", + "cancel_info": "주최자가 이 일정을 취소했어요.", + "event_updated": "업데이트 #{sequence}", + "event_status_tentative": "미정", + "event_status_cancelled": "취소됨", + "collapse": "세부정보 숨기기", + "expand": "세부정보 표시" + }, + "send": "보내기", + "more": "더보기", + "details": { + "recipients_routing": "수신자 및 라우팅", + "authentication_security": "인증 및 보안", + "identifiers_threading": "식별자 및 스레드", + "mailing_list": "메일링 리스트", + "message_properties": "메시지 속성", + "sent": "보낸 시각", + "received": "받은 시각", + "delivery_time": "전달 시간", + "in_reply_to": "회신 대상", + "references": "참조", + "thread_id": "스레드 ID", + "size": "크기", + "mime_type": "MIME 타입", + "attachments_summary": "{count}개 파일 · {size}", + "list_id": "리스트 ID", + "list_help": "리스트 도움말", + "list_post": "리스트 게시", + "list_unsubscribe": "구독 해지", + "iprev": "역방향 DNS", + "spam_status": "스팸 상태", + "ai_verdict": "AI 판정", + "account": "계정", + "no_subject": "(제목 없음)" + }, + "scheduled_banner": "{date}에 보내도록 예약됨", + "scheduled_send_created": "메일 보내기가 예약되었습니다", + "sending_in": "{seconds}초 후 전송", + "undo_send_failed": "실행 취소할 수 없습니다. 메시지가 이미 전송되었을 수 있습니다.", + "cancel_scheduled_send": "보내기 취소", + "reschedule_send": "다시 예약", + "cancel_and_edit": "취소하고 편집", + "cancel_and_compose_again": "취소하고 다시 작성", + "reschedule_prompt": "새 날짜/시간을 입력하세요. 예: 2026-05-04T15:30", + "scheduled_actions_only": "예약됨 보기에서는 예약 작업만 사용할 수 있습니다", + "undo_send_scheduled": "메시지가 보내기로 예약되었습니다", + "undo_send": "보내기 실행 취소", + "contact_sidebar": { + "title": "연락처", + "close": "사이드바 닫기", + "action_email": "이메일", + "action_email_title": "이메일 보내기", + "action_copy": "복사", + "action_copy_title": "이메일 복사", + "action_edit_title": "연락처 편집", + "section_emails": "이메일", + "section_phones": "전화번호", + "section_organizations": "조직", + "section_addresses": "주소", + "section_notes": "메모", + "not_in_contacts": "연락처에 없음", + "add_to_contacts": "연락처에 추가", + "copied": "복사됨!", + "copy_failed": "복사하지 못했습니다", + "view_contact": "연락처 보기", + "view_details": "세부 정보 보기" + }, + "send_now": "지금 보내기", + "verification_code": { + "copy": "코드 {code} 복사", + "copied": "코드를 복사했습니다", + "copy_failed": "코드를 복사할 수 없습니다" + }, + "mv_position": "{index} / {total}", + "plugin_panel_show": "플러그인 패널 표시", + "plugin_panel_hide": "플러그인 패널 숨기기" + }, + "email_composer": { + "select_from_directory": "디렉터리에서 선택", + "minimize": "최소화", + "maximize": "전체 화면", + "restore": "복원", + "new_message": "새 메시지", + "signature_edit_hint": "서명을 편집하려면 두 번 클릭하세요", + "reply": "답장", + "reply_all": "전체 답장", + "forward": "전달", + "reply_to": "답장", + "reply_all_to": "전체 답장", + "forward_message": "전달", + "from": "보내는 사람", + "to": "받는 사람", + "cc": "참조", + "bcc": "숨은 참조", + "subject": "제목", + "body_placeholder": "내용을 작성해 주세요...", + "send": "보내기", + "cancel": "취소", + "attach": "첨부", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "파일에서 첨부", + "files_picker": { + "attach": "{count, plural, =0 {첨부} other {파일 #개 첨부}}", + "empty_folder": "이 폴더는 비어 있습니다", + "load_failed": "파일을 불러올 수 없습니다" + }, + "discard": "삭제", + "discard_draft_title": "임시보관 메일을 삭제할까요?", + "discard_draft_confirm": "저장되지 않은 내용이 있어요. 이 메일을 삭제할까요?", + "discard_draft_message": "임시 보관 메일이 삭제됩니다.", + "saving": "저장 중...", + "draft_saved": "임시보관함에 저장되었어요", + "save_failed": "저장에 실패했어요", + "to_placeholder": "받는 사람 이메일 주소", + "cc_placeholder": "참조 수신자", + "bcc_placeholder": "숨은 참조 수신자", + "subject_placeholder": "제목", + "cc_label": "참조:", + "bcc_label": "숨은 참조:", + "subject_label": "제목:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count}개의 메일", + "no_subject": "(제목 없음)", + "unknown_sender": "알 수 없음", + "quote": { + "reply_header": "{date}, {sender}님이 작성함:", + "forward_header": "---------- 전달된 메시지 ----------", + "from": "보낸 사람: {sender}", + "date": "날짜: {date}", + "subject": "제목: {subject}", + "to": "받는 사람: {recipients}" + }, + "remove_sub_address": "서브 어드레스 삭제", + "from_override": { + "toggle_off": "재정의", + "toggle_on": "재정의 취소", + "toggle_tooltip": "보낸 사람 이름과 주소를 자유롭게 편집하세요. 메일은 여전히 사용자의 ID를 통해 전송됩니다.", + "name_label": "보낸 사람 이름", + "name_placeholder": "이름", + "email_label": "보낸 사람 이메일 주소", + "email_placeholder": "alias@example.com", + "envelope_notice": "서버가 이 주소를 봉투 발신자로 허용하지 않으면 대신 {identity}이(가) 사용되며, 수신자는 Return-Path 헤더에서 이 주소를 볼 수 있습니다." + }, + "save_as_template": "템플릿으로 저장", + "attach_from_device": "이 기기에서 업로드", + "templates_menu": "템플릿", + "insert_template": "템플릿 삽입", + "options": "옵션", + "option_plain_text": "일반 텍스트", + "option_plain_text_hint": "서식 없이 보내기", + "option_read_receipt": "읽음 확인 요청", + "option_read_receipt_hint": "수신자에게 열람 확인을 요청합니다", + "option_dsn": "배달 알림", + "option_dsn_hint": "서버가 배달하면 보고서를 받습니다", + "option_require_tls": "암호화된 배달 필수", + "option_require_tls_hint": "TLS로만 배달하고, 불가능하면 반송합니다", + "option_priority": "중요도", + "option_priority_high": "높음", + "option_priority_normal": "보통", + "option_priority_low": "낮음", + "validation": { + "recipient_required": "보내려면 받는 사람을 추가해 주세요", + "body_required": "메시지를 작성하거나 파일을 첨부해 주세요", + "invalid_recipients": "보내지 않음 - 다음 주소를 확인하세요: {addresses}", + "attachments_uploading": "첨부 파일을 업로드하는 중입니다. 완료되면 자동으로 전송됩니다", + "attachment_upload_failed": "전송되지 않았습니다. 첨부 파일 업로드에 실패했습니다. 해당 파일을 제거하고 다시 시도하세요." + }, + "upload_progress": "{uploaded} / {total} 업로드 중", + "upload_cancel": "업로드 취소", + "upload_failed": "{filename} 파일 업로드에 실패했어요", + "attachment_too_large": "\"{name}\"이(가) 서버 허용 크기보다 커요(파일당 {max})", + "attachments_total_too_large": "첨부 파일이 서버의 메시지당 한도({max})를 넘어요", + "drop_files": "첨부할 파일을 여기에 놓아주세요", + "show_less": "간략히 보기", + "send_failed": "메일 전송에 실패했어요", + "send_timeout": "서버가 응답을 멈췄습니다. 다시 보내기 전에 보낸 편지함을 확인하세요. 메시지가 이미 발송되었을 수 있습니다.", + "send_offline": "오프라인 상태인 것 같습니다. 메시지는 그대로 있으니 다시 연결되면 보내세요.", + "formatting": "서식", + "send_recipients_rejected": "보내지 못했습니다. 서버가 모든 수신자를 거부했습니다.", + "send_plugin_failed": "보내지 않았습니다. 전송을 처리하는 플러그인이 실패했거나 너무 오래 걸렸습니다. 다시 보내기 전에 보낸 편지함을 확인하세요.", + "send_some_recipients_rejected": "보냈지만 다음 수신자에게는 전달되지 않습니다. 서버가 거부했습니다.", + "continue_draft": "이어서 작성하기", + "close_draft_title": "임시보관함에 저장할까요?", + "close_draft_message": "저장되지 않은 내용이 있어요. 임시보관함에 저장할까요, 아니면 삭제할까요?", + "smime_sign_on": "S/MIME 서명 켜짐", + "smime_sign_off": "S/MIME 서명 켜기", + "smime_encrypt_on": "S/MIME 암호화 켜짐", + "smime_encrypt_off": "S/MIME 암호화 켜기", + "smime_encrypt_unavailable": "S/MIME 암호화를 사용할 수 없어요 – 수신자의 인증서가 필요해요", + "smime_unlock_title": "S/MIME 키 잠금 해제", + "smime_unlock_message": "S/MIME 서명 키의 잠금을 해제하려면 비밀번호를 입력해 주세요.", + "smime_unlock_button": "잠금 해제", + "smime_passphrase_placeholder": "비밀번호", + "forgot_attachment": { + "title": "첨부 파일을 잊으셨나요?", + "message": "메시지에 \"{keyword}\"이(가) 언급되어 있지만 파일이 첨부되지 않았습니다. 그래도 보내시겠습니까?", + "send_anyway": "그래도 보내기", + "back": "편집으로 돌아가기" + }, + "empty_subject": { + "title": "제목 없이 보낼까요?", + "message": "이 메시지에는 제목이 없습니다. 그래도 보낼까요?", + "dont_ask_again": "다시 묻지 않기", + "send_anyway": "그래도 보내기", + "back": "편집으로 돌아가기" + }, + "add_link": "링크 추가", + "link_url_prompt": "URL을 입력하세요", + "sending": "전송 중...", + "schedule_send": "보내기 예약", + "schedule_send_description": "서버가 이 메시지를 보낼 시간을 선택하세요.", + "schedule_preset": { + "tomorrow_morning": "내일 오전", + "tomorrow_afternoon": "내일 오후", + "monday_morning": "월요일 오전" + }, + "schedule_send_required": "날짜와 시간을 선택하세요.", + "schedule_send_invalid": "유효한 날짜와 시간을 입력하세요.", + "schedule_send_future": "미래의 날짜와 시간을 선택하세요.", + "schedule_send_too_late": "이 시간은 서버가 허용하는 범위를 벗어납니다.", + "schedule_send_unsupported": "이 계정은 예약 보내기를 지원하지 않습니다.", + "schedule_send_cleanup_warning": "예약 보내기는 생성되었지만 초안 정리에 실패했습니다.", + "send_delay_unsupported": "이 계정은 보내기 지연을 지원하지 않습니다.", + "send_delay_unsupported_confirm": "이 계정은 보내기 지연을 지원하지 않습니다. 바로 보낼까요?", + "recipient_edit_email": "이메일 주소 편집", + "recipient_edit_name": "표시 이름 편집", + "recipient_email_placeholder": "이메일 주소", + "recipient_name_placeholder": "표시 이름", + "invalid_recipient": "올바른 이메일 주소가 아닙니다", + "autocomplete_search_server": "서버에서 검색", + "autocomplete_searching": "검색 중...", + "mention_recipients": "수신자", + "mention_card_not_recipient": "받는 사람이나 참조에 없어서 이 메시지를 받지 못합니다.", + "mention_card_add": "{field}에 추가", + "mention_card_remove": "멘션 삭제", + "toolbar": { + "bold": "굵게", + "italic": "기울임꼴", + "underline": "밑줄", + "strikethrough": "취소선", + "text_color": "글자 색", + "remove_color": "색 제거", + "heading_1": "제목 1", + "heading_2": "제목 2", + "bullet_list": "글머리 기호 목록", + "ordered_list": "번호 매기기 목록", + "quote": "인용", + "code_block": "코드 블록", + "align_left": "왼쪽 정렬", + "align_center": "가운데 정렬", + "align_right": "오른쪽 정렬", + "text_direction": "텍스트 방향 (RTL/LTR)", + "link": "링크", + "table": "표", + "clear_formatting": "서식 지우기", + "undo": "실행 취소", + "redo": "다시 실행", + "add_row_above": "위에 행 추가", + "add_row_below": "아래에 행 추가", + "add_column_before": "앞에 열 추가", + "add_column_after": "뒤에 열 추가", + "delete_row": "행 삭제", + "delete_column": "열 삭제", + "toggle_header_row": "머리글 행 전환", + "delete_table": "표 삭제", + "pick_size": "크기 선택", + "font_size": "글자 크기", + "font_size_default": "기본값", + "background_color": "배경색", + "remove_background_color": "배경색 제거" + }, + "send_filing_warning": "보냈지만 전송 후 정리에 실패했습니다. 오래된 임시 보관 메일이 남아 있을 수 있습니다.", + "pop_out": "답장을 별도 창에서 열기", + "send_guard_failed": "보내지 않았습니다 - 보내기 전에 메시지를 확인하는 플러그인이 실패했거나 응답하지 않았습니다." + }, + "confirm_dialog": { + "confirm": "확인", + "cancel": "취소" + }, + "common": { + "loading": "불러오는 중...", + "error": "오류", + "success": "성공", + "cancel": "취소", + "save": "저장", + "delete": "삭제", + "edit": "수정", + "close": "닫기", + "search": "검색", + "refresh": "새로고침", + "settings": "설정", + "help": "도움말", + "logout": "로그아웃", + "yes": "네", + "no": "아니요", + "unknown": "알 수 없음", + "app_title": "웹메일", + "reconnecting": "연결이 끊어졌어요. 다시 연결을 시도할게요...", + "rate_limited_title": "서버 인증이 일시적으로 제한되었어요.", + "rate_limited_detail": "Bulwark가 계정 잠금을 방지하기 위해 백그라운드 요청을 일시 중지했어요. {seconds}초 후에 다시 시도할게요.", + "rate_limited_action_title": "계정 잠금 방지를 위해 요청이 일시 중지되었어요.", + "rate_limited_action_detail": "추가 인증 요청을 보내기 전에 서버의 쿨다운이 끝날 때까지 대기 중이에요. {seconds}초 후에 다시 시도해 주세요.", + "pull_to_refresh": "당겨서 새로고침", + "release_to_refresh": "놓으면 새로고침", + "refreshing": "새로고침 중…" + }, + "notifications": { + "email_sent": "메일이 전송되었어요", + "email_deleted": "메일이 삭제되었어요", + "email_archived": "메일이 보관되었어요", + "emails_deleted": "{count, plural, other {이메일 #개 삭제됨}}", + "emails_archived": "{count, plural, other {이메일 #개 보관됨}}", + "error_archiving": "이메일을 보관하지 못했습니다", + "error_updating": "이메일을 업데이트하지 못했습니다", + "email_starred": "별표가 추가되었어요", + "email_unstarred": "별표가 해제되었어요", + "email_marked_read": "읽은 상태로 변경되었어요", + "email_marked_unread": "읽지 않은 상태로 변경되었어요", + "copied_to_clipboard": "클립보드에 복사되었어요", + "source_copied": "원본이 클립보드에 복사되었어요", + "error_sending": "메일 전송에 실패했어요", + "error_deleting": "메일 삭제에 실패했어요", + "error_loading": "메일을 불러오지 못했어요", + "new_email": "새 메일이 도착했어요", + "new_email_from": "{sender}님이 보낸 메일", + "click_to_view": "클릭해서 보기", + "email_moved": "메일이 이동되었어요", + "emails_moved": "{count}개의 메일이 이동되었어요", + "moved_to_mailbox": "{mailbox}함으로 이동되었어요", + "move_failed": "이동에 실패했어요", + "removed_from_folder": "{mailbox}에서 제거됨", + "remove_from_folder_failed": "이 폴더에서 제거하지 못했습니다", + "emails_copied": "{count}개의 메일이 복사되었어요", + "copy_failed": "복사에 실패했어요", + "move_error": "선택한 폴더로 메일을 이동할 수 없어요", + "email_tagged": "태그가 추가되었어요", + "emails_tagged": "{count}개의 메일에 태그가 추가되었어요", + "tag_failed": "태그 추가에 실패했어요", + "identity_created": "보내는 사람이 추가되었어요", + "identity_updated": "보내는 사람 정보가 업데이트되었어요", + "identity_deleted": "보내는 사람이 삭제되었어요", + "identity_set_primary": "기본 보내는 사람이 변경되었어요", + "identity_create_failed": "보내는 사람 추가 실패: {error}", + "identity_update_failed": "보내는 사람 업데이트 실패: {error}", + "identity_delete_failed": "보내는 사람 삭제 실패: {error}", + "identity_unauthorized": "이 이메일 주소로 메일을 보낼 권한이 없어요", + "identity_not_found": "해당 계정을 찾을 수 없어요", + "vacation_saved": "부재중 설정이 저장되었어요", + "vacation_save_failed": "부재중 설정을 저장하지 못했어요", + "filters_saved": "필터가 저장되었어요", + "filters_save_failed": "필터 저장에 실패했어요", + "filters_deleted": "필터 규칙이 삭제되었어요", + "templates_exported": "템플릿을 성공적으로 내보냈어요", + "templates_imported": "{count, plural, other {#개의 템플릿}}을 가져왔어요", + "templates_import_errors": "일부 템플릿을 가져오지 못했어요", + "templates_import_empty": "파일에서 템플릿을 찾을 수 없어요", + "export_email_error": "메일 내보내기에 실패했어요", + "import_email_success": "메일을 성공적으로 가져왔어요", + "import_email_error": "메일 가져오기에 실패했어요", + "rule_created": "규칙을 만들었어요", + "rule_merged": "“{name}” 규칙에 추가했어요", + "rule_already_covered": "이미 “{name}” 규칙에 포함되어 있어요", + "rule_apply_existing": "{count, plural, other {기존 메일 #개에 적용}}", + "rule_applied": "{count, plural, =0 {바꿀 메일이 없었어요} other {메일 #개에 규칙을 적용했어요}}", + "rule_apply_failed": "기존 메일에 규칙을 적용하지 못했어요", + "rule_edit": "규칙 편집", + "rule_undo": "실행 취소", + "rule_undone": "규칙 변경을 취소했어요", + "rule_undo_failed": "규칙 변경을 취소하지 못했어요", + "rule_undo_conflict": "그 사이에 필터가 바뀌었어요. 필터 설정에서 변경을 취소하세요.", + "rule_folder_failed": "폴더를 만들지 못했어요", + "vacation_filters_save_failed": "부재중 설정은 저장되었지만 전달 및 응답 대상 설정을 저장하지 못했어요" + }, + "date": { + "today": "오늘", + "yesterday": "어제", + "this_week": "이번 주", + "last_week": "지난주", + "this_month": "이번 달", + "older": "이전", + "just_now": "방금 전", + "minutes_ago": "{count}분 전", + "minutes_ago_plural": "{count}분 전", + "hours_ago": "{count}시간 전", + "hours_ago_plural": "{count}시간 전", + "days_ago": "{count}일 전", + "days_ago_plural": "{count}일 전" + }, + "language": { + "title": "언어", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "언어 선택", + "switch_to_english": "영어로 변경", + "switch_to_french": "프랑스어로 변경", + "switch_to_japanese": "일본어로 변경", + "switch_to_spanish": "스페인어로 변경", + "switch_to_italian": "이탈리아어로 변경", + "switch_to_german": "독일어로 변경", + "switch_to_dutch": "네덜란드어로 변경", + "switch_to_portuguese": "포르투갈어로 변경", + "switch_to_russian": "러시아어로 변경", + "switching": "언어를 변경하는 중...", + "switch": "언어 변경", + "current": "현재 언어", + "polish": "Polski", + "switch_to_polish": "폴란드어로 전환", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "페르시아어로 전환", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "몽골어로 전환" + }, + "settings": { + "title": "설정", + "back_to_mail": "메일로 돌아가기", + "save_success": "설정이 저장되었어요", + "import_success": "설정을 성공적으로 가져왔어요", + "import_error": "설정 가져오기에 실패했어요", + "reset_confirm": "모든 설정을 기본값으로 되돌릴까요?", + "unsaved_changes": "저장되지 않은 변경 사항이 있어요", + "discard_changes": "변경 사항을 취소할까요?", + "discard": "취소하기", + "keep_editing": "계속 수정하기", + "tabs": { + "appearance": "화면 설정", + "language": "언어, 지역 및 시간", + "email": "메일 동작", + "composer": "메일 쓰기", + "privacy": "개인정보 및 보안", + "account": "계정", + "identities": "보내는 사람", + "vacation": "부재중", + "advanced": "고급 설정", + "calendar": "캘린더", + "filters": "필터", + "templates": "템플릿", + "folders": "폴더", + "keywords": "태그", + "security": "보안", + "files": "파일", + "contacts": "연락처", + "encryption": "암호화", + "protocol_handlers": "기본 앱", + "sidebar_apps": "사이드바 앱", + "notifications": "알림", + "layout": "레이아웃", + "themes": "테마", + "reading": "읽기", + "composing": "작성", + "downloads": "다운로드", + "content_senders": "콘텐츠 및 발신자", + "about_data": "정보 및 데이터", + "debug": "디버그" + }, + "themes": { + "title": "테마", + "description": "관리자가 배포한 테마와 기본 제공 프리셋 중에서 선택해 주세요.", + "locked": "관리자가 테마 선택을 잠갔습니다.", + "forced": "테마 “{name}”은(는) 관리자가 지정했으므로 변경할 수 없습니다", + "admin_theme": "관리자 테마", + "activated": "테마가 적용되었습니다", + "default_restored": "기본 테마로 복원되었습니다", + "default_name": "기본", + "default_badge": "기본", + "admin_enforced": "관리자가 지정함" + }, + "tab_groups": { + "general": "일반", + "account": "계정 및 인증", + "organization": "메일 정리", + "apps": "앱", + "system": "시스템", + "appearance": "모양", + "mail": "메일", + "privacy": "개인정보 및 보안", + "advanced": "고급" + }, + "appearance": { + "message_list_order": { + "label": "메시지 목록 정렬 순서", + "description": "목록에서 메시지를 정렬하는 방식입니다. 정렬은 서버가 수행하므로 불러온 메시지뿐 아니라 폴더 전체에 적용됩니다.", + "preset": { + "chronological": "시간순 (최신 먼저)", + "unread_first": "읽지 않음 먼저", + "starred_first": "별표 먼저", + "tagged_first": "태그 먼저", + "custom": "사용자 지정" + }, + "tag_label": "태그", + "advanced_show": "고급: 정렬 단계 편집", + "advanced_hide": "고급 숨기기", + "criterion_label": "{n}단계 기준", + "direction_label": "{n}단계 방향", + "criteria": { + "unread": "읽지 않음", + "starred": "별표", + "tag": "태그", + "receivedAt": "받은 날짜", + "sentAt": "보낸 날짜", + "from": "보낸 사람", + "subject": "제목", + "size": "크기" + }, + "direction": { + "unread_first": "읽지 않음 먼저", + "read_first": "읽음 먼저", + "starred_first": "별표 먼저", + "unstarred_first": "별표 없음 먼저", + "tagged_first": "태그 있음 먼저", + "untagged_first": "태그 없음 먼저", + "newest_first": "최신 먼저", + "oldest_first": "오래된 순", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "큰 순서", + "smallest_first": "작은 순서" + }, + "add_level": "단계 추가", + "remove_level": "단계 제거", + "move_up": "위로 이동", + "move_down": "아래로 이동", + "tiebreak_note": "최대 3단계이며, 이후 단계는 동순위일 때 적용됩니다. '최신 먼저'는 항상 마지막에 적용됩니다.", + "scope": { + "label": "모든 폴더에 적용", + "description": "꺼져 있으면 정렬 순서가 받은편지함에만 적용되고 다른 폴더는 시간순으로 유지됩니다." + }, + "unsupported_note": "메일 서버가 읽음 상태, 별표 또는 태그 기준 정렬을 지원하지 않습니다. 해당 단계는 무시되며 목록은 시간순으로 유지됩니다." + }, + "title": "화면 설정", + "description": "웹메일의 디자인과 화면을 내 취향에 맞게 꾸며보세요", + "theme": { + "label": "테마", + "description": "선호하는 색상 테마를 선택해 주세요", + "light": "라이트", + "dark": "다크", + "system": "시스템" + }, + "language": { + "label": "언어", + "description": "사용할 언어를 선택해 주세요" + }, + "font_size": { + "label": "글꼴 크기", + "description": "읽기 편한 글꼴 크기로 조절해 보세요", + "small": "작게", + "medium": "보통", + "large": "크게" + }, + "list_density": { + "label": "간격", + "description": "UI의 간격과 여백을 조절할 수 있어요", + "extra_compact": "매우 좁게", + "compact": "좁게", + "regular": "보통", + "comfortable": "넓게" + }, + "interface_layout": { + "label": "인터페이스 레이아웃", + "description": "검색, 탐색, 도구 모음의 위치", + "default": "기본", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "애니메이션 켜기", + "description": "부드러운 전환 효과를 사용해요" + }, + "toolbar_position": { + "label": "도구 모음 위치", + "description": "메일 작업 버튼(답장, 보관, 삭제 등)이 표시될 위치예요", + "top": "위쪽", + "below_subject": "제목 아래" + }, + "toolbar_labels": { + "label": "도구 모음 라벨 표시", + "description": "아이콘 옆에 텍스트 설명을 함께 보여줘요. 아이콘이 익숙해지면 꺼서 공간을 절약할 수 있어요." + }, + "hide_account_switcher": { + "label": "사이드바 계정 전환기 숨기기", + "description": "폴더 사이드바 상단의 계정 선택기를 숨겨요. 하단 내비게이션 바에서 여전히 계정을 전환할 수 있어요." + }, + "show_rail_account_list": { + "label": "내비게이션 바에 계정 아바타 표시", + "description": "내비게이션 바 아래에 계정 프로필을 표시해서 빠르게 전환할 수 있어요." + }, + "unified_mailbox": { + "label": "통합 메일함", + "description": "연결된 모든 계정의 통합 폴더(받은편지함, 보낸편지함 등)를 표시합니다", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "그룹 받은편지함 포함", + "description": "공유/그룹 받은편지함도 통합 보기에 포함합니다." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "컬러풀한 사이드바 아이콘", + "description": "폴더와 태그 아이콘을 유형별로 색상 표시합니다(받은편지함 파란색, 스팸 빨간색, 보낸편지함 녹색 등). 모노크롬 사이드바를 원하면 비활성화하세요." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "전체 메시지 수 표시", + "description": "읽지 않은 수와 함께 폴더 및 태그 옆에 전체 메시지 수를 표시합니다. 읽지 않은 수만 표시하려면 비활성화하세요." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "공유 메일 계정 표시", + "description": "나와 공유된 메일함의 폴더를 사이드바의 '공유됨' 아래에 표시합니다. 끄면 모두 숨겨지며, 아래에서 계정별로 숨길 수도 있습니다. 캘린더와 연락처는 그대로 유지됩니다." + }, + "pro_interface": { + "label": "Pro 인터페이스 (실험적)", + "description": "데스크톱 전용 파워 유저 레이아웃으로, 다중 탭 메시지 탐색과 계정 간 워크플로우를 지원합니다. 표준 인터페이스에는 영향이 없으며 언제든지 되돌릴 수 있습니다.", + "open_label": "Pro 인터페이스 열기", + "back_to_standard": "표준으로 돌아가기" + }, + "cross_unread": { + "label": "모든 계정: 읽지 않음", + "description": "모든 계정 섹션에 모든 계정의 읽지 않은 메일을 나열하는 항목을 표시합니다. 스팸, 보낸편지함, 보관함, 휴지통, 임시보관함을 제외한 모든 폴더가 대상입니다." + }, + "cross_starred": { + "label": "모든 계정: 별표", + "description": "모든 계정 섹션에 모든 계정의 별표 메일을 나열하는 항목을 표시합니다. 스팸, 보낸편지함, 보관함, 휴지통, 임시보관함을 제외한 모든 폴더가 대상입니다." + }, + "cross_all": { + "label": "모든 계정: 모든 메일", + "description": "모든 계정 섹션에 모든 계정의 모든 메일을 나열하는 항목을 표시합니다. 스팸, 보낸편지함, 보관함, 휴지통, 임시보관함을 제외한 모든 폴더가 대상입니다." + } + }, + "keywords": { + "title": "이메일 태그", + "description": "색상으로 이메일을 정리하기 위한 태그를 정의합니다. 서버에 JMAP 키워드로 저장됩니다.", + "add_keyword": "태그 추가", + "label_field": "표시 이름", + "label_placeholder": "예: 업무, 개인, 긴급", + "id_field": "태그 ID", + "id_placeholder": "예: work, personal", + "color_field": "색상", + "id_exists": "이 태그 ID는 이미 존재합니다", + "edit": "태그 편집", + "delete": "태그 삭제", + "save": "저장", + "add": "추가", + "cancel": "취소", + "migrating": "기존 이메일의 태그 업데이트 중…", + "migration_error": "기존 이메일의 태그 업데이트에 실패했습니다", + "filters_migration_error": "일부 필터 규칙이 여전히 이전 태그를 사용합니다. 수동으로 업데이트하세요", + "nesting": { + "label": "중첩 태그", + "description": "태그를 다른 태그 아래에 중첩하고 사이드바에 트리로 표시합니다." + }, + "parent_field": "상위 태그", + "no_parent": "상위 태그 없음", + "too_long": "이 태그 경로가 너무 깁니다(최대 {max}자)", + "has_children_locked": "이 태그 아래에 다른 태그가 중첩되어 있어 이름과 상위 태그가 잠겨 있습니다. 먼저 옮기거나 삭제하세요.", + "has_children_delete": "이 태그 아래에 중첩된 태그를 먼저 삭제하세요", + "visibility_field": "사이드바 표시", + "visibility": { + "show": "표시", + "unread": "읽지 않음이 있을 때 표시", + "hide": "숨기기" + }, + "unrecognized": { + "title": "서버에서 태그 복구", + "description": "태그 이름과 색상은 이 브라우저에 저장되지만, 태그 자체는 JMAP 키워드로 메일에 저장돼요. 여기에 정의되지 않은 키워드를 메일에서 검색해서 다시 추가할 수 있어요.", + "scan": "태그 검색", + "rescan": "다시 검색", + "scanning": "검색 중… {total}개 중 {scanned}개", + "none": "메일에 있는 모든 태그가 여기에 이미 정의돼 있어요.", + "messages": "{count, plural, other {메일 #개}}", + "add_all": "{count}개 모두 추가", + "partial": "{total}개 중 최근 {scanned}개만 검색했어요. 오래된 메일에만 쓰인 태그는 빠져 있을 수 있어요.", + "error": "메일에서 태그를 검색하지 못했어요." + } + }, + "notifications": { + "test_sound": "알림음 테스트", + "sounds": { + "default": "기본 (비프음)", + "cheerful": "밝게", + "involved": "경쾌하게", + "swift": "빠르게", + "relax": "차분하게" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "알림음", + "description": "알림이 올 때 재생할 소리를 선택해 주세요", + "choose": "알림음", + "choose_desc": "알림음을 선택하고 스피커 아이콘을 눌러 미리 들어보세요" + }, + "email": { + "title": "이메일 알림", + "description": "새 이메일이 올 때의 알림을 설정해요", + "enabled": "이메일 알림", + "enabled_desc": "새 이메일이 도착하면 알림을 보여줘요", + "sound": "알림음", + "sound_desc": "새 이메일이 도착하면 소리로 알려줘요", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "캘린더 알림", + "description": "캘린더 일정 알림을 설정해요", + "enabled": "일정 알림", + "enabled_desc": "다가오는 일정에 대한 알림을 보여줘요", + "sound": "알림음", + "sound_desc": "일정 알림이 올 때 소리로 알려줘요", + "invitation_parsing": "이메일 초대장 분석", + "invitation_parsing_desc": "이메일 첨부파일에서 캘린더 초대장을 감지하고 캘린더에 표시해요" + }, + "push": { + "confirm_disable_message": "이 사이트가 닫혀 있을 때 이 기기는 알림을 더 이상 받지 않습니다.", + "confirm_disable_title": "백그라운드 알림을 비활성화하시겠습니까?", + "description": "이 사이트가 닫혀 있을 때 새 메일에 대한 시스템 알림을 받습니다. Bulwark 푸시 릴레이를 통해 전달되며, 릴레이는 메일 내용을 절대 보지 않습니다.", + "disable": "비활성화", + "enable": "활성화", + "dismiss_aria": "알림 프롬프트 닫기", + "ios_hint": "iOS에서는 먼저 사이트를 홈 화면에 설치하세요. Safari는 설치된 PWA에만 Web Push를 전달합니다.", + "reenable": "다시 등록", + "relay_desc": "기본값은 호스팅된 Bulwark 릴레이입니다. 셀프 호스팅 시에만 변경하세요.", + "relay_label": "푸시 릴레이", + "relay_locked": "관리자에 의해 설정됨", + "relay_locked_desc": "푸시 릴레이 URL은 관리자가 설정했으며 변경할 수 없어요.", + "status_active": "이 기기에서 활성", + "status_busy": "처리 중…", + "status_inactive": "이 기기에서 활성화되지 않음", + "status_unsupported": "이 브라우저는 Web Push를 지원하지 않습니다", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "백그라운드 알림", + "devices_title": "등록된 기기", + "devices_desc": "이 계정의 백그라운드 알림을 받도록 등록된 모든 기기입니다. 해지하면 해당 기기는 즉시 수신을 멈추니다.", + "devices_loading": "등록된 기기를 불러오는 중…", + "devices_empty": "백그라운드 알림에 등록된 기기가 없습니다.", + "devices_error": "등록된 기기를 불러오지 못했습니다.", + "devices_refresh": "새로 고침", + "device_this": "이 기기", + "device_other": "다른 기기", + "device_status_active": "알림 전달 중", + "device_status_inactive": "알림을 전달한 적 없음", + "device_status_unknown": "이 릴레이에서 알 수 없음", + "device_expires": "{date} 만료", + "revoke": "해지", + "confirm_revoke_title": "이 기기를 해지할까요?", + "confirm_revoke_message": "즉시 백그라운드 알림 수신을 멈추게 됩니다. 해당 기기에서 푸시를 다시 켜면 새로 등록됩니다.", + "confirm_revoke_message_this": "이 기기는 즉시 백그라운드 알림 수신을 멈추게 됩니다." + } + }, + "language_region": { + "title": "언어, 지역 및 시간", + "description": "언어, 날짜 형식, 시간 형식 및 기타 지역 환경설정", + "language": { + "label": "언어", + "description": "사용할 언어를 선택해 주세요", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "날짜 형식", + "description": "이메일 목록에 날짜가 표시되는 방식", + "smart": "스마트", + "relative": "상대 시간", + "full": "항상 전체 날짜", + "preview_today": "오늘:", + "preview_this_week": "이번 주:", + "preview_older": "이전:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "시간 형식", + "description": "12시간제 또는 24시간제를 선택해 주세요", + "12h": "12시간제", + "24h": "24시간제" + }, + "first_day": { + "label": "한 주의 시작", + "description": "일주일이 일요일부터 시작할지 월요일부터 시작할지 설정해요", + "sunday": "일요일", + "monday": "월요일" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "폴더 전환 시 검색 지우기", + "description": "폴더(받은편지함, 보낸편지함 등)를 클릭하면 활성 검색을 지우고 해당 폴더를 표시합니다. 꺼져 있으면 검색이 유지되고 이동한 폴더에서 다시 실행됩니다." + }, + "request_read_receipt": { + "label": "기본적으로 읽음 확인 요청", + "description": "새 메시지를 작성할 때 읽음 확인 요청을 미리 켭니다." + }, + "read_receipt_response": { + "label": "읽음 확인 요청에 응답", + "description": "수신 메시지가 읽음 확인을 요청할 때의 동작.", + "ask": "매번 묻기", + "always": "항상 보내기", + "never": "보내지 않음" + }, + "title": "메일 동작", + "description": "이메일 관련 동작 방식을 설정해 주세요", + "mark_read": { + "label": "읽은 상태로 표시", + "description": "메일을 열었을 때 언제 읽은 상태로 변경할지 설정해요", + "instant": "즉시", + "delay_3s": "3초 후", + "delay_5s": "5초 후", + "never": "표시하지 않음" + }, + "delete_action": { + "label": "삭제 동작", + "description": "메일을 삭제할 때의 동작을 선택해 주세요", + "trash": "휴지통으로 이동", + "trash_and_read": "휴지통으로 이동하고 읽음으로 표시", + "permanent": "영구 삭제", + "warning": "메일이 영구적으로 삭제되며 복구할 수 없어요. 주의해서 사용해 주세요." + }, + "message_spacing": { + "label": "메시지 여백", + "description": "읽기 화면에서 메시지 본문 주위의 여백", + "auto": "자동 (권장)", + "always": "항상 여백 추가", + "edge": "가장자리까지 채우기" + }, + "plain_text_font": { + "label": "일반 텍스트 글꼴", + "description": "일반 텍스트 이메일에 사용되는 글꼴", + "mono": "고정폭", + "sans": "기본(앱과 동일)" + }, + "archive_mode": { + "label": "보관 방식", + "description": "메일을 보관할 때 어떻게 분류할지 설정해요", + "single": "단일 폴더에 모두 보관", + "year": "연도별 폴더로 분류", + "month": "월별 폴더로 분류", + "reorganize": "기존 보관함 다시 정리하기", + "reorganize_success": "{count, plural, =0 {정리할 메일이 없어요} other {#개의 메일을 다시 정리했어요}}", + "reorganize_error": "보관함을 다시 정리하는 데 실패했어요" + }, + "permanently_delete_junk": { + "label": "스팸 영구 삭제", + "description": "스팸함에 있는 메일을 휴지통으로 보내지 않고 바로 영구 삭제해요" + }, + "confirm_bulk_delete": { + "label": "여러 메시지를 삭제하기 전에 확인", + "description": "선택한 여러 메시지를 휴지통으로 옮기기 전에 묻습니다. 영구 삭제는 항상 묻습니다." + }, + "mail_layout": { + "label": "메일 레이아웃", + "description": "기존의 분할 화면 방식과 Gmail 스타일의 집중형 화면 중 선택해 주세요.", + "split": "화면 분할", + "split_description": "메일 목록과 읽기 창을 나란히 보여줘요.", + "focus": "집중형 목록", + "focus_description": "메일을 한 줄로 보여주고, 클릭하면 폴더 사이드바는 남겨둔 채 메일 내용을 넓게 보여줘요.", + "horizontal": "읽기 창 하단 표시", + "horizontal_description": "메시지 목록을 상단에 표시하고 선택한 메시지를 하단 읽기 창에서 엽니다." + }, + "list_columns": { + "label": "목록을 열로 표시", + "description": "한 줄 행을 머리글 아래 보낸 사람, 제목, 날짜, 크기 열이 있는 표로 표시합니다." + }, + "show_preview": { + "label": "미리보기 텍스트 표시", + "description": "목록에 메일 내용의 일부를 미리 보여줘요", + "focus_description": "집중형 한 줄 목록에서 메일 내용의 일부를 함께 보여줘요" + }, + "show_verification_codes": { + "label": "인증 코드 표시", + "description": "로그인 및 확인 메일의 코드를 한 번의 클릭으로 복사할 수 있게 합니다(목록에서는 하루 동안, 메시지에서는 항상)" + }, + "disable_threading": { + "label": "대화형 보기 끄기", + "description": "관련된 메일을 그룹으로 묶지 않고 개별 메시지로 보여줘요" + }, + "plain_text_mode": { + "label": "일반 텍스트만 사용", + "description": "서식 있는 텍스트 편집기를 끄고, 답장이나 전달을 포함한 모든 메일을 일반 텍스트로만 보내요" + }, + "auto_select_reply_identity": { + "label": "답장 시 보내는 사람 자동 선택", + "description": "답장할 때 메일을 받았던 주소로 보내는 사람을 자동으로 변경해요" + }, + "reply_identity_match": { + "label": "수신 주소 일치 방식", + "description": "어떤 수신 주소를 내 주소로 간주할지 정합니다. '정확한 주소만'은 설정된 ID 중에서만 선택합니다. '같은 도메인'은 ID 도메인의 다른 주소도 캐치올 별칭으로 간주하여 보낸 사람 헤더를 해당 주소로 바꿉니다. 해당 주소가 배포 목록이라면 정확한 주소를 선택하세요.", + "exact": "정확한 주소만", + "domain": "내 도메인의 모든 주소" + }, + "signature_position": { + "label": "서명 위치", + "description": "답장과 전달에서 서명을 삽입할 위치. 인용된 텍스트 위에 두면 답장의 마무리처럼 자연스럽게 읽히고, 아래에 두면 원본 메시지가 이어져 보입니다.", + "above_quote": "인용 텍스트 앞", + "below_quote": "인용 텍스트 뒤" + }, + "signature_separator": { + "label": "서명 구분선", + "description": "서명 앞에 표준 구분선 \"-- \" (RFC 3676)을 추가합니다. 본문에서 바로 서명으로 이어지길 원하면 해제하세요." + }, + "sub_address_delimiter": { + "label": "서브 주소 구분자", + "description": "사용자 이름과 서브 주소 태그를 나누는 문자예요. 메일 서버가 사용하는 구분자에 맞춰 주세요 (예: user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "사용자 지정…", + "custom_input_label": "사용자 지정 구분 문자" + }, + "attachment_click_action": { + "label": "첨부파일 클릭 동작", + "description": "파일을 클릭했을 때 미리보기를 할지, 바로 다운로드할지 선택해 주세요", + "preview": "가능하면 미리보기", + "download": "즉시 다운로드" + }, + "attachment_position": { + "label": "첨부파일 위치", + "description": "메일 상단에서 첨부파일을 어디에 표시할지 설정해요", + "beside-sender": "보낸 사람 옆", + "below-header": "제목 아래" + }, + "emails_per_page": { + "10": "10개", + "25": "25개", + "50": "50개", + "100": "100개", + "label": "페이지당 메일 수", + "description": "한 번에 불러올 메일 개수를 설정해요" + }, + "always_light_mode": { + "label": "메일 내용은 항상 라이트 모드로 보기", + "description": "다크 모드를 사용할 때도 메일 본문은 라이트 모드로 표시해서 화면이 깨지는 걸 방지해요" + }, + "external_content": { + "label": "외부 콘텐츠", + "description": "이미지나 외부 콘텐츠를 어떻게 처리할지 설정해요", + "ask": "항상 묻기", + "block": "항상 차단", + "allow": "항상 허용" + }, + "trusted_senders": { + "label": "신뢰하는 보낸 사람", + "description": "이미지를 자동으로 불러올 보낸 사람을 관리해요", + "count_zero": "없음", + "count_one": "1명", + "count_other": "{count}명", + "modal_title": "신뢰하는 보낸 사람", + "empty_title": "아직 신뢰하는 보낸 사람이 없어요", + "empty_description": "이미지가 차단된 메일에서 \"이 보낸 사람 항상 신뢰하기\"를 클릭하면 여기에 추가돼요.", + "add_manually": "직접 추가하기", + "add_button": "추가", + "add_placeholder": "이메일 주소 입력", + "search_placeholder": "보낸 사람 검색...", + "no_results": "검색된 보낸 사람이 없어요", + "remove": "삭제", + "close": "닫기", + "invalid_email": "올바른 이메일 주소를 입력해 주세요", + "already_added": "이미 신뢰하는 보낸 사람으로 추가되어 있어요", + "save_error": "저장에 실패했습니다 - 자세한 내용은 연락처 디버그 로그를 확인하세요", + "use_address_book_label": "주소록과 동기화", + "use_address_book_description": "신뢰 발신자를 전용 \"신뢰 발신자\" 주소록에 저장하여 모든 기기에서 동기화합니다" + }, + "hover_actions": { + "label": "빠른 호버 액션", + "description": "메일 목록에서 마우스를 올렸을 때 표시할 빠른 액션을 선택해 주세요", + "delete": "삭제", + "star": "별표 달기 / 해제", + "mark_read": "읽음 / 안 읽음 표시", + "archive": "보관", + "tag": "태그", + "spam": "스팸으로 표시", + "not_spam": "스팸 아님", + "none_selected": "선택된 액션 없음", + "mode_label": "표시 모드", + "mode_inline": "목록 안쪽", + "mode_floating": "플로팅", + "corner_label": "플로팅 위치", + "corner_top-left": "왼쪽 위", + "corner_top-right": "오른쪽 위", + "corner_bottom-left": "왼쪽 아래", + "corner_bottom-right": "오른쪽 아래" + }, + "default_mail_program": { + "label": "기본 메일 프로그램 설정", + "description": "mailto: 링크를 클릭할 때 {appName}이(가) 기본으로 열리도록 설정해요", + "button": "기본값으로 설정", + "success": "브라우저에서 기본 설정 팝업이 뜰 거예요", + "error": "이 브라우저에서는 이 기능을 지원하지 않아요" + }, + "attachment_reminder": { + "label": "첨부 파일 알림", + "description": "메시지에 첨부 파일이 언급되었지만 첨부되지 않은 경우 보내기 전에 경고합니다", + "keywords_label": "트리거 키워드", + "keywords_description": "메시지에서 발견되면 알림을 표시하는 단어 또는 구문", + "add_placeholder": "키워드 추가...", + "add": "Add", + "remove": "삭제" + }, + "empty_subject_warning": { + "label": "빈 제목 경고", + "description": "제목 없는 메시지를 보내기 전에 확인을 요청합니다." + }, + "recipient_mentions": { + "label": "@로 받는 사람 언급", + "description": "본문에 @를 입력하면 받는 사람이 제안되고 선택한 사람의 이름이 삽입됩니다. Enter 또는 Tab으로 강조된 이름을 선택합니다." + }, + "mention_adds_recipient": { + "label": "@ 멘션에 연락처 포함", + "description": "@를 입력하면 연락처와 디렉터리의 사용자도 제안됩니다. 아직 수신자가 아닌 사람을 고르면 메시지에 추가됩니다.", + "to": "받는 사람에 추가", + "cc": "참조에 추가", + "off": "수신자만 표시" + }, + "hide_inline_image_attachments": { + "label": "첨부 파일 목록에서 인라인 이미지 숨기기", + "description": "메시지 본문에 포함된 이미지는 별도의 첨부 파일로 표시되지 않습니다" + }, + "attachment_image_previews": { + "label": "첨부 파일에 이미지 미리보기 표시", + "description": "이미지 첨부 파일을 일반 파일 아이콘 대신 썸네일 카드로 표시합니다" + }, + "send_delay": { + "label": "보내기 취소 / 보내기 지연", + "description": "일반 보내기를 서버 측 짧은 시간 동안 지연합니다.", + "off": "끔", + "seconds": "{seconds}초", + "unsupported": "현재 계정은 지연 보내기 지원을 알리지 않습니다. 설정은 다른 계정을 위해 계속 저장됩니다." + }, + "return_to_list_after_action": { + "label": "삭제 또는 읽지 않음으로 표시 후 목록으로 돌아가기", + "description": "열려 있는 메시지를 삭제하거나 읽지 않음으로 표시한 후 다음 메시지를 열지 않고 메시지 목록으로 돌아갑니다." + }, + "swipe_right_action": { + "label": "오른쪽으로 스와이프 동작 (모바일)", + "description": "터치 기기에서 목록의 메시지를 오른쪽으로 스와이프할 때 실행되는 동작입니다." + }, + "swipe_left_action": { + "label": "왼쪽으로 스와이프 동작 (모바일)", + "description": "터치 기기에서 목록의 메시지를 왼쪽으로 스와이프할 때 실행되는 동작입니다." + }, + "swipe_actions": { + "none": "없음", + "archive": "보관", + "delete": "삭제", + "mark_read": "읽음 상태 전환", + "star": "별표 전환", + "spam": "스팸으로 표시" + }, + "rtl_editing": { + "label": "오른쪽에서 왼쪽 편집 지원", + "description": "편집기 도구 모음에 방향 버튼을 추가하여 단락을 왼쪽에서 오른쪽 또는 오른쪽에서 왼쪽으로 설정할 수 있습니다" + } + }, + "composer": { + "title": "메일 쓰기", + "description": "메일 작성과 관련된 설정을 확인해 보세요", + "autosave": { + "label": "자동 저장 간격", + "description": "작성 중인 메일을 임시보관함에 자동으로 저장하는 간격이에요", + "30s": "30초마다", + "1m": "1분마다", + "2m": "2분마다", + "5m": "5분마다" + }, + "send_confirmation": { + "label": "전송 전 확인", + "description": "메일을 보내기 전에 한 번 더 물어볼까요?" + }, + "default_reply": { + "label": "기본 답장 모드", + "description": "답장 버튼을 눌렀을 때의 기본 동작을 설정해요", + "reply": "답장", + "reply_all": "전체 답장" + } + }, + "privacy": { + "title": "개인정보 및 보안", + "description": "개인정보와 보안 설정을 관리해 보세요", + "external_images": { + "label": "외부 이미지 차단", + "description": "외부 이미지를 통한 추적을 방지해요" + }, + "session_timeout": { + "label": "세션 만료 시간", + "description": "일정 시간 동안 사용하지 않으면 자동으로 로그아웃돼요", + "never": "사용 안 함", + "30m": "30분", + "1h": "1시간", + "4h": "4시간" + }, + "clear_cache": { + "label": "캐시 지우기", + "description": "저장된 임시 데이터와 캐시를 모두 삭제해요", + "button": "캐시 지우기", + "confirm": "정말 캐시를 모두 지울까요?", + "success": "캐시가 깔끔하게 지워졌어요!" + } + }, + "account": { + "title": "계정", + "description": "내 계정 정보를 확인해 보세요", + "name_label": "표시 이름", + "username_label": "사용자 이름", + "account_type_label": "계정 유형", + "auth_method_label": "인증 방법", + "auth_method_oauth": "SSO (OAuth/OIDC)", + "auth_method_basic": "비밀번호", + "auth_method_token": "액세스 토큰", + "demo_account": "데모 계정", + "email": { + "label": "이메일 주소", + "value": "{email}" + }, + "server": { + "label": "JMAP 서버", + "value": "{server}" + }, + "storage": { + "label": "저장 공간 사용량", + "used": "전체 {total} 중 {used} 사용", + "percentage": "{percent}% 사용 중" + }, + "last_sync": { + "label": "마지막 동기화", + "value": "{time}" + }, + "accounts": { + "title": "로그인된 계정", + "description": "끌어서 계정 드롭다운에 표시되는 순서를 변경합니다", + "active": "현재 활성 계정", + "default_badge": "기본 계정", + "set_default": "기본으로 설정", + "switch_to": "이 계정으로 전환", + "move_up": "위로 이동", + "move_down": "아래로 이동", + "drag_handle": "끌어서 순서 변경", + "add": "계정 추가" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "나와 공유됨", + "description": "관리할 수 있는 그룹 및 공유 계정입니다. 하나를 선택하여 필터, 부재 중 자동 응답, 캘린더, 연락처를 편집하세요.", + "shared_label": "공유 계정" + } + }, + "security": { + "title": "계정 보안", + "description": "비밀번호, 2단계 인증 및 보안 설정을 관리해요", + "detecting": "서버 기능을 확인하는 중...", + "not_available": "이 메일 서버에서는 계정 보안 관리를 사용할 수 없어요. 필요한 권한이 비활성화되어 있을 수 있어요. 자세한 내용은 문서를 참고해 주세요.", + "password": { + "title": "비밀번호 변경", + "current": "현재 비밀번호", + "new": "새 비밀번호", + "confirm": "새 비밀번호 확인", + "submit": "비밀번호 변경", + "success": "비밀번호가 성공적으로 변경되었어요", + "error_title": "비밀번호 변경 실패", + "error_mismatch": "새 비밀번호가 서로 일치하지 않아요", + "error_min_length": "비밀번호는 최소 8자 이상이어야 해요", + "error_generic": "비밀번호를 변경하지 못했어요" + }, + "display_name": { + "label": "표시 이름", + "description": "서버에 표시되는 내 이름이에요", + "placeholder": "표시할 이름을 입력해 주세요", + "save": "저장", + "success": "표시 이름이 업데이트되었어요", + "error": "표시 이름을 업데이트하지 못했어요" + }, + "totp": { + "section_title": "2단계 인증 (2FA)", + "label": "TOTP 인증", + "description": "시간 기반 일회용 비밀번호로 보안을 한 층 더 강화해 보세요", + "active": "사용 중", + "inactive": "사용 안 함", + "enabled": "2단계 인증이 켜졌어요", + "disabled": "2단계 인증이 꺼졌어요", + "enable_error": "2단계 인증을 켜지 못했어요", + "disable_error": "2단계 인증을 끄지 못했어요", + "setup_instructions": "이 URL을 인증 앱(Google Authenticator, Authy 등)에 복사해 주세요:", + "verification_code": "확인 코드", + "confirm": "확인", + "disable": "비활성화", + "disable_confirm_prompt": "2단계 인증을 비활성화하려면 비밀번호를 입력하세요.", + "password_required": "비밀번호가 필요합니다", + "code_required": "확인 코드가 필요합니다", + "code_invalid": "유효하지 않은 확인 코드입니다. 인증 앱을 확인하고 다시 시도하세요." + }, + "app_passwords": { + "title": "앱 비밀번호", + "description": "2단계 인증을 지원하지 않는 앱을 위한 전용 비밀번호를 만드세요", + "add": "추가", + "create": "만들기", + "cancel": "취소", + "generate": "생성", + "name_label": "앱 이름", + "name_placeholder": "예: Thunderbird, iPhone Mail", + "password_label": "비밀번호 (비워두면 자동으로 생성돼요)", + "password_placeholder": "비워두면 자동 생성", + "added": "앱 비밀번호가 만들어졌어요", + "removed": "앱 비밀번호가 삭제되었어요", + "add_error": "앱 비밀번호를 만들지 못했어요", + "remove_error": "앱 비밀번호를 삭제하지 못했어요", + "none": "설정된 앱 비밀번호가 없어요", + "done": "완료", + "expires_label": "만료 (선택 사항)", + "copy_now_warning": "지금 이 비밀번호를 복사하세요 - 다시 표시되지 않습니다.", + "allowed_ips_label": "허용된 IP(선택)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "쉼표 또는 공백으로 구분합니다. 비워두면 모든 IP가 허용됩니다." + }, + "api_keys": { + "title": "API 키", + "description": "서버와 직접 통신하는 스크립트 및 통합용 API 키를 만듭니다", + "name_label": "키 이름", + "name_placeholder": "예: 백업 스크립트, CI 러너", + "copy_now_warning": "이 API 키를 지금 복사하세요 - 다시 표시되지 않습니다.", + "added": "API 키가 생성되었습니다", + "removed": "API 키가 삭제되었습니다", + "add_error": "API 키 생성에 실패했습니다", + "remove_error": "API 키 삭제에 실패했습니다", + "none": "구성된 API 키가 없습니다" + }, + "encryption": { + "section_title": "저장 데이터 암호화", + "label": "이메일 암호화", + "description": "서버에 저장된 이메일을 암호화해서 프라이버시를 보호해요", + "active": "{type} 암호화 사용 중", + "inactive": "사용 안 함", + "enabled": "저장 데이터 암호화가 켜졌어요", + "disabled_success": "저장 데이터 암호화가 꺼졌어요", + "error": "암호화 설정을 업데이트하지 못했어요", + "algorithm_label": "암호화 알고리즘", + "encrypt_on_append": "새 메일을 업로드할 때 암호화", + "allow_spam_training": "메일을 암호화하기 전에 스팸 학습 허용", + "enable_encryption": "암호화 사용", + "enable_tooltip": "이 키로 암호화 사용", + "configure_title": "이 키로 암호화 사용", + "enable_button": "사용", + "disable_tooltip": "암호화 사용 안 함", + "enabled_success": "저장 데이터 암호화가 켜졌어요" + }, + "email_client": { + "title": "이메일 클라이언트 설정", + "description": "데스크톱이나 모바일 이메일 클라이언트(Thunderbird, Apple Mail, Outlook 등)를 설정할 때 사용할 정보예요", + "jmap_username_label": "JMAP 사용자 이름", + "copy": "복사", + "copied": "복사되었어요", + "password_instructions": "위의 JMAP 사용자 이름과 앱 비밀번호를 사용해서 이메일 클라이언트에 로그인하세요. 아직 만들지 않았다면 위에서 앱 비밀번호를 먼저 만들어 주세요." + }, + "link_device": { + "title": "모바일 앱 연결", + "description": "휴대폰의 Bulwark Mail 앱에 비밀번호를 입력하지 않고 로그인하세요. 휴대폰은 별도의 로그인을 받으므로 여기에서 로그아웃해도 휴대폰은 로그아웃되지 않습니다.", + "generate": "QR 코드 표시", + "regenerate": "새 코드 표시", + "instructions": "Bulwark Mail 앱을 열고 로그인 화면에서 \"Scan a sign-in code\"를 누른 다음(이미 로그인되어 있다면 \"Add account\" → \"Scan a sign-in code\") 카메라를 여기에 비추세요.", + "expires_in": "이 코드는 {seconds}초 후에 만료됩니다. 한 번만 사용할 수 있습니다.", + "expired": "이 코드는 만료되었습니다.", + "error": "페어링 코드를 만들지 못했습니다. 다시 시도해 주세요.", + "password_prompt": "본인 확인을 위해 비밀번호를 입력하세요. 휴대폰은 별도의 로그인을 받으며, 비밀번호는 휴대폰으로 전송되지 않습니다.", + "show_password": "비밀번호 표시", + "hide_password": "비밀번호 숨기기", + "continue": "계속", + "error_wrong_password": "비밀번호가 올바르지 않습니다. 다시 시도하세요.", + "totp_prompt": "인증 앱에 표시된 코드를 입력하세요.", + "error_password_or_code": "비밀번호 또는 코드가 올바르지 않습니다.", + "error_not_signed_in": "세션이 만료되었습니다. 기기를 연결하려면 다시 로그인하세요.", + "error_impersonation": "다른 사용자의 사서함을 보는 동안에는 기기를 연결할 수 없습니다.", + "error_too_many_attempts": "시도 횟수가 너무 많습니다. 잠시 후 다시 시도하세요.", + "error_session_secret": "이 서버에는 기기 연결이 설정되어 있지 않습니다(SESSION_SECRET 없음). 관리자에게 문의하세요.", + "error_server_unreachable": "메일 서버에 연결할 수 없습니다. 다시 시도하세요.", + "error_pairing_unavailable": "메일 서버에서 이 방법으로 기기를 연결하는 것을 허용하지 않습니다. 관리자에게 문의하세요.", + "qr_alt": "로그인 QR 코드", + "link_label": "로그인 링크", + "copy_link": "로그인 링크 복사", + "copied": "복사됨", + "copy_hint": "카메라가 없나요? 링크를 복사한 다음 앱에서 \"Paste sign-in link\"로 붙여 넣으세요.", + "success_title": "휴대폰에서 로그인되었습니다", + "success_body": "이 페이지를 닫아도 됩니다. 다른 기기를 연결하려면 새 코드를 표시하세요.", + "link_another": "다른 기기 연결", + "use_sso": "대신 SSO(통합 로그인) 사용", + "error_insecure_server": "모바일 앱은 https로만 메일 서버에 연결하는데, 이 서버는 http를 사용합니다." + }, + "public_keys": { + "name_label": "공개 키 이름", + "name_placeholder": "예: 내 키", + "key_label": "공개 키(S/MIME 또는 PGP ASCII 형식)", + "key_placeholder": "여기에 공개 키를 붙여넣으세요", + "none": "설정된 공개 키가 없어요" + } + }, + "identities": { + "title": "보내는 사람", + "description": "메일을 보낼 때 사용할 이메일 주소를 관리해요", + "identities_count": { + "label": "내 이메일 주소", + "description": "메일을 보낼 수 있도록 설정된 주소들이에요", + "count_zero": "설정된 주소가 없어요", + "count_one": "1개의 주소", + "count_other": "{count}개의 주소" + }, + "manage": "보내는 사람 관리", + "sub_addressing": { + "label": "서브 어드레싱", + "description": "user+tag@domain.com 처럼 태그를 사용해서 수신 메일을 정리해 보세요", + "learn_more": "자세히 알아보기" + } + }, + "vacation": { + "title": "부재중", + "description": "자리를 비웠을 때 도착한 메일을 자동으로 처리해요", + "loading": "자동 응답 설정을 불러오는 중...", + "not_supported": "사용 중인 메일 서버는 자동 응답 기능을 지원하지 않아요.", + "fetch_error": "자동 응답 설정을 불러오지 못했어요. 다시 시도해 주세요.", + "status": { + "label": "자동으로 응답하기", + "description": "나에게 메일을 보낸 사람에게 자동으로 답장을 보내요", + "active": "사용 중", + "inactive": "사용 안 함" + }, + "date_range": { + "start": "시작일", + "start_description": "비워두면 지금부터 바로 시작해요", + "end": "종료일", + "end_description": "비워두면 계속해서 켜져 있어요" + }, + "message": { + "subject_label": "제목", + "subject_description": "자동 응답 메일의 제목이에요", + "subject_placeholder": "부재중 알림", + "body_label": "메시지 본문", + "body_description": "일반 텍스트로 보낼 메시지 내용이에요", + "body_placeholder": "메일을 보내주셔서 감사합니다. 현재 자리를 비우고 있어 돌아오는 대로 답변드리겠습니다.", + "html_label": "서식 있는 메시지 (HTML)", + "html_description": "링크와 스타일이 포함된 서식 있는 버전을 추가합니다. 메일 클라이언트가 이를 표시할 수 없는 수신자에게는 위의 일반 텍스트가 표시됩니다.", + "html_placeholder": "서식 있는 부재중 자동 회신 작성…" + }, + "preview": { + "title": "미리보기", + "show": "미리보기 열기", + "hide": "미리보기 닫기" + }, + "save": "변경 사항 저장", + "saving": "저장 중...", + "warnings": { + "end_before_start": "종료일은 시작일보다 나중이어야 해요", + "invalid_date": "시작일과 종료일에 시간을 포함한 올바른 날짜를 입력하거나 칸을 비워 두세요", + "start_in_past": "시작일이 이미 지났어요", + "empty_body": "메시지 내용이 비어있어요. 상대방에게 빈 메일이 가게 돼요", + "subject_too_long": "제목이 서버에 비해 너무 깁니다(최대 {max}바이트).", + "body_too_long": "메시지가 서버에 비해 너무 깁니다(서식 포함 최대 {max}바이트).", + "forward_address": "전달받을 올바른 주소를 입력해 주세요", + "not_running": "전달 또는 응답 대상이 저장되어 있지만 지금은 작동하지 않아요. 저장하면 다시 켜져요." + }, + "forward": { + "title": "전달", + "description": "받은 메시지를 자동 응답 여부와 관계없이 위 기간 동안 다른 주소로 전달해요(종료일이 없으면 끌 때까지). 스팸은 전달하지 않아요.", + "enabled_label": "메시지 전달", + "to_label": "다음으로 전달", + "to_placeholder": "email@example.com", + "keep_label": "받은편지함에 유지", + "keep_description": "끄면 메시지는 전달만 되고, 이 메일함에 남지 않으며 필터 규칙도 적용되지 않아요." + }, + "audience": { + "label": "응답 대상", + "description": "내부 보낸 사람: {domains}", + "all": "모든 보낸 사람", + "internal": "내부 보낸 사람만", + "external": "외부 보낸 사람만" + }, + "auto_reply": { + "title": "자동 응답" + } + }, + "folders": { + "role_memos": "메모", + "title": "폴더", + "description": "이메일 폴더를 관리하고 기본 역할을 지정해 보세요", + "folder_list": "내 폴더", + "folder_list_description": "폴더 아이콘을 클릭해서 설정할 수 있어요", + "standard_roles": "기본 폴더 역할", + "standard_roles_description": "받은편지함, 보낸편지함, 휴지통 등 기본 역할로 사용할 폴더를 지정해요", + "role_inbox": "받은편지함", + "role_drafts": "임시보관함", + "role_sent": "보낸편지함", + "role_trash": "휴지통", + "role_junk": "스팸/정크함", + "role_archive": "보관함", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "없음", + "create_folder": "폴더 만들기", + "create_subfolder": "하위 폴더 만들기", + "reorder": "드래그하여 순서 변경", + "reorder_error": "폴더 순서 변경에 실패했습니다", + "move_error": "폴더 이동에 실패했습니다", + "select_folder": "{name} 선택", + "select_hint": "선택. Shift+클릭하면 마지막으로 클릭한 폴더까지 모두 선택됩니다.", + "selected_count": "{count, plural, other {폴더 #개}} 선택됨", + "move_to": "이동할 위치...", + "move_to_top_level": "최상위", + "move": "이동", + "clear_selection": "선택 해제", + "folders_moved": "{count, plural, other {폴더 #개를 이동했습니다}}", + "move_partial_error": "{count, plural, other {폴더 #개}}를 이동할 수 없습니다", + "subfolder_of": "{name} 내부에 생성", + "subfolder_name": "하위 폴더 이름", + "new_folder_name": "폴더 이름", + "rename": "이름 바꾸기", + "change_icon": "아이콘 바꾸기", + "count_mode": "{name} 카운터", + "count_mode_default": "카운터: 기본값", + "count_mode_unread": "읽지 않음만", + "count_mode_all": "읽지 않음 및 전체", + "count_mode_none": "카운터 없음", + "delete": "삭제", + "confirm_delete": "정말 \"{name}\" 폴더를 삭제할까요? 폴더 안의 메일은 휴지통으로 이동돼요.", + "create": "만들기", + "cancel": "취소", + "no_folders": "만든 폴더가 없어요", + "cannot_delete_role": "기본 역할이 지정된 폴더는 삭제할 수 없어요. 먼저 역할을 해제해 주세요.", + "folder_created": "폴더가 만들어졌어요", + "folder_renamed": "폴더 이름이 변경되었어요", + "folder_deleted": "폴더가 삭제되었어요", + "role_updated": "폴더 역할이 업데이트되었어요", + "error_create": "폴더를 만들지 못했어요", + "error_rename": "폴더 이름을 바꾸지 못했어요", + "error_delete": "폴더를 삭제하지 못했어요", + "error_delete_has_children": "안에 하위 폴더가 있어서 삭제할 수 없어요. 먼저 이동하거나 삭제해 주세요.", + "error_delete_has_email": "안에 메일이 있어서 삭제할 수 없어요. 메일을 이동하거나 삭제해 주세요.", + "error_role": "폴더 역할을 업데이트하지 못했어요" + }, + "advanced": { + "title": "고급 설정", + "description": "고급 옵션 및 개발자 설정이에요", + "debug_mode": { + "label": "디버그 모드", + "description": "문제 해결을 위해 자세한 로그를 기록해요" + }, + "debug_categories": { + "description": "기록할 카테고리를 선택해 주세요. 필요 없는 항목은 꺼두면 좋아요.", + "jmap": "JMAP 클라이언트", + "jmap_description": "편지함 작업, 메일 가져오기, JMAP 프로토콜 요청", + "calendar": "캘린더", + "calendar_description": "일정, 가져오기, 예약 메시지", + "tasks": "할 일(Tasks)", + "tasks_description": "할 일 생성, 가져오기 및 업데이트", + "auth": "인증", + "auth_description": "로그인, TOTP, 토큰 교환 및 세션 관리", + "filters": "필터", + "filters_description": "Sieve 필터 규칙 및 자동 응답 스크립트", + "email": "이메일 보기", + "email_description": "이메일 렌더링, TNEF 처리 및 읽음 표시", + "push": "푸시 알림", + "push_description": "푸시 알림 설정 및 전송", + "contacts": "연락처 및 주소록", + "contacts_description": "연락처 동기화, 주소록 작업, 신뢰 발신자" + }, + "settings_sync": { + "label": "설정 동기화", + "description": "여러 브라우저나 기기에서 내 설정을 동기화해요" + }, + "sender_favicons": { + "label": "보낸 사람 파비콘", + "description": "비즈니스 메일의 경우 해당 웹사이트의 아이콘을 프로필 사진으로 보여줘요", + "refresh_label": "발신자 아이콘 새로 고침", + "refresh_description": "발신자가 아이콘을 바꾼 경우 등에 웹사이트 아이콘을 다시 가져옵니다", + "refresh_button": "새로 고침", + "refresh_done": "발신자 아이콘을 다시 가져옵니다" + }, + "show_avatars_in_junk": { + "label": "스팸함에서 아바타 표시", + "description": "스팸함의 보낸 사람에 대한 프로필 이미지와 파비콘을 표시해요. 피싱 메일이 정상적인 메일처럼 보이지 않도록 기본적으로 꺼져 있어요." + }, + "keyboard_shortcuts": { + "label": "단축키", + "description": "사용 가능한 키보드 단축키를 확인해 보세요", + "button": "단축키 보기" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "설정 초기화", + "description": "모든 설정을 기본값으로 되돌려요", + "button": "기본값으로 초기화" + }, + "export_settings": { + "label": "설정 내보내기", + "description": "내 설정을 JSON 파일로 다운로드해요", + "button": "내보내기" + }, + "import_settings": { + "label": "설정 가져오기", + "description": "JSON 파일에서 설정을 불러와요", + "button": "가져오기" + }, + "about": { + "title": "Bulwark Webmail 정보", + "source_code": "소스 코드" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "스팸과 피싱이 받은편지함에 닿기 전에 클릭해서 막아 주세요. 진짜 메일은 통과시켜 주세요. 방패 세 개를 모두 잃으면 받은편지함이 함락돼요.", + "kinds": { + "spam": "스팸", + "phishing": "피싱", + "legit": "진짜 메일" + }, + "click_to_block": "클릭해서 차단", + "let_through": "통과시키기", + "start": "시작", + "play_again": "다시 하기", + "score": "점수", + "combo": "연속 보너스", + "shields_left": "방패 {total}개 중 {count}개 남음", + "seconds": "{seconds}초", + "best": "최고: {score}", + "new_best": "최고 기록 경신", + "held": "받은편지함을 지켰어요", + "overrun": "받은편지함이 함락됐어요", + "stats": { + "caught": "차단", + "delivered": "배달", + "mistakes": "실수" + } + } + }, + "sidebar_apps": { + "title": "사이드바 앱", + "description": "사이드바의 커스텀 앱과 링크를 관리해요", + "keep_loaded": "앱 실행 유지", + "keep_loaded_description": "인라인 앱을 전환할 때 매번 새로고침되지 않도록 백그라운드에서 실행 상태를 유지해요", + "managed_title": "관리자가 제공", + "managed_description": "이 앱은 모든 사용자를 위해 설정되었으며 변경할 수 없습니다.", + "manage_title": "커스텀 앱", + "manage_description": "사이드바에 나만의 앱을 추가, 수정, 삭제해 보세요" + }, + "contacts": { + "title": "연락처", + "description": "연락처를 가져오거나 내보낼 수 있어요", + "import_label": "연락처 가져오기", + "import_description": "vCard(.vcf) 파일에서 연락처를 가져와요", + "export_label": "연락처 내보내기", + "export_description": "모든 연락처를 vCard(.vcf) 파일로 저장해요", + "manage_title": "주소록", + "manage_description": "주소록 이름 변경", + "no_address_books": "주소록을 찾을 수 없음", + "categories_title": "카테고리", + "categories_description": "연락처 카테고리 이름 변경", + "no_categories": "카테고리를 찾을 수 없음", + "group_by_letter_description": "연락처 목록에 알파벳순 섹션 헤더 표시", + "group_by_letter_label": "첫 글자로 그룹화", + "sort_by_last_name_label": "성으로 정렬", + "sort_by_last_name_description": "가족 구성원이 함께 표시되도록 연락처 목록을 성 기준으로 정렬합니다" + }, + "downloads": { + "title": "다운로드", + "description": "다운로드한 이메일과 첨부 파일의 이름을 지정하는 방식을 사용자 정의합니다.", + "reset": "기본값 복원", + "preview": "미리 보기:", + "email_template": { + "label": "이메일 파일 이름 (.eml)", + "description": "이메일을 내보내거나 파일 시스템으로 드래그할 때 사용하는 템플릿입니다. .eml 확장자는 자동으로 추가됩니다." + }, + "attachment_template": { + "label": "첨부 파일 이름", + "description": "첨부 파일을 다운로드하거나 드래그할 때 사용하는 템플릿입니다. '{filename}'과 '{ext}'를 생략하면 원래 확장자가 유지됩니다." + }, + "bundle_template": { + "label": "다중 이메일 .zip 파일 이름", + "description": "여러 선택한 이메일을 하나의 .zip 아카이브로 드래그하거나 다운로드할 때 사용하는 템플릿입니다. .zip 확장자는 자동으로 추가됩니다." + }, + "spaces": { + "label": "공백", + "description": "결과 파일 이름의 공백을 다른 문자로 바꿉니다.", + "keep": "공백 유지", + "underscore": "_로 바꾸기", + "dash": "-로 바꾸기" + }, + "lowercase": { + "label": "소문자", + "description": "전체 파일 이름을 소문자로 강제 변환합니다." + }, + "strip_diacritics": { + "label": "발음 구별 부호 제거", + "description": "악센트가 있는 문자를 ASCII 등가 문자로 변환합니다 (ä → a, é → e). 유니코드 파일 이름을 제대로 처리하지 못하는 도구에 유용합니다." + }, + "collapse_separators": { + "label": "반복된 구분자 결합", + "description": "연속된 공백, 밑줄, 대시를 단일 문자로 결합합니다." + }, + "after_export": { + "label": "내보낸 후", + "description": ".eml로 내보낸 후 이메일을 선택적으로 이동합니다.", + "keep": "메일함에 유지", + "archive": "보관함으로 이동", + "trash": "휴지통으로 이동" + } + }, + "filters": { + "title": "이메일 필터", + "description": "새로 온 메일을 자동으로 분류하고 라벨을 붙이는 규칙을 만들어 보세요", + "add_rule": "규칙 추가", + "no_rules": "필터 규칙이 없어요", + "no_rules_description": "수신 메일을 자동으로 정리할 규칙을 만들어 보세요", + "vacation_active": "부재중 자동 응답이 켜져 있어요", + "vacation_active_description": "받는 메일에 대해 자동으로 답장을 보내고 있어요", + "vacation_configure": "설정", + "edit_rule": "규칙 수정", + "new_rule": "새 규칙", + "delete_rule": "규칙 삭제", + "delete_confirm": "정말 이 규칙을 삭제할까요?", + "enable": "켜기", + "disable": "끄기", + "raw_editor": "원본 Sieve 에디터", + "raw_editor_warning": "원본 Sieve 스크립트를 수정하면 시각적 에디터가 망가질 수 있어요. 여기서 수정하면 시각적 에디터의 내용을 덮어쓰게 돼요.", + "validate": "검증", + "validation_success": "스크립트가 유효해요", + "validation_error": "스크립트에 오류가 있어요", + "save": "규칙 저장", + "saving": "저장 중...", + "saved": "필터가 성공적으로 저장되었어요", + "save_failed": "필터를 저장하지 못했어요", + "loading": "필터를 불러오는 중...", + "not_supported": "사용 중인 메일 서버는 이메일 필터를 지원하지 않아요.", + "rule_name": "규칙 이름", + "rule_name_placeholder": "예: 뉴스레터 분류", + "match_all": "모든 조건 만족 (AND)", + "match_any": "조건 중 하나라도 만족 (OR)", + "conditions": "조건", + "add_condition": "조건 추가", + "actions": "동작", + "add_action": "동작 추가", + "stop_processing": "이후 규칙 무시하기", + "include_spam": "스팸으로 표시된 메시지도 이동", + "attachment_type_placeholder": "예: pdf, doc, jpg", + "value_placeholder_multi": "값 (쉼표로 여러 개 구분)", + "condition_fields": { + "attachment": "첨부 파일", + "from": "보낸 사람", + "to": "받는 사람", + "cc": "참조", + "subject": "제목", + "header": "사용자 지정 헤더", + "size": "크기", + "body": "본문", + "all": "모든 메일" + }, + "comparators": { + "has_any": "있음", + "has_type": "유형", + "contains": "포함함", + "not_contains": "포함하지 않음", + "is": "정확히 일치", + "not_is": "일치하지 않음", + "starts_with": "다음으로 시작", + "ends_with": "다음으로 끝남", + "matches": "패턴 일치", + "greater_than": "다음보다 큼", + "less_than": "다음보다 작음", + "address_is": "주소가 다음과 같음", + "domain_is": "도메인이 다음과 같음" + }, + "action_types": { + "move": "폴더로 이동", + "copy": "폴더로 복사", + "forward": "다음으로 전달", + "mark_read": "읽은 상태로 표시", + "star": "별표 달기", + "add_label": "태그 추가", + "discard": "삭제 (조용히 지움)", + "reject": "메시지와 함께 수신 거부", + "keep": "받은편지함에 유지", + "stop": "더 이상 처리 안 함" + }, + "move_to_folder": "폴더 선택", + "copy_to_folder": "폴더 선택", + "forward_to": "전달할 이메일 주소", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "사본 보관", + "forward_limit": "이 서버의 메시지당 전달 한도: {count}. 초과된 전달은 건너뜁니다.", + "forward_unavailable": "이 서버는 전달을 허용하지 않습니다. 필터 규칙의 전달 작업은 적용되지 않습니다.", + "reject_message": "거부 메시지", + "reject_placeholder": "메일 수신이 거부되었습니다", + "label_name": "태그 이름", + "label_placeholder": "태그 선택", + "header_name": "헤더 이름", + "header_placeholder": "예: X-Mailing-List", + "size_bytes": "크기 (바이트)", + "size_placeholder": "예: 1000000", + "system_managed": "시스템 관리 규칙", + "opaque_warning": "시각적 에디터 외부에서 수정된 스크립트예요. 원본 Sieve 편집만 가능해요.", + "open_sieve_editor": "원본 Sieve 에디터 열기", + "fetch_error": "필터를 불러오지 못했어요", + "expanded_view": "자세히 보기", + "expanded_view_description": "필터 규칙의 조건과 동작을 모두 펼쳐서 보여줘요", + "if": "만약 (If)", + "then": "다음 동작 실행 (Then)", + "match_all_conditions": "모두 만족할 때", + "match_any_condition": "하나라도 만족할 때", + "and": "그리고 (AND)", + "or": "또는 (OR)", + "cancel": "취소", + "confirm_delete": "삭제", + "rule_list": "필터 규칙", + "drag_to_reorder": "드래그해서 순서 변경", + "match_type": "일치 유형", + "reset_to_visual": "시각적 에디터로 초기화", + "reset_warning": "현재 스크립트가 지워지고 처음부터 다시 시작하게 돼요.", + "confirm_reset": "초기화", + "validation_empty_name": "규칙 이름을 입력해 주세요", + "validation_empty_conditions": "값을 포함한 조건이 하나 이상 필요해요", + "validation_empty_actions": "동작이 하나 이상 필요해요", + "templates_section": "템플릿으로 시작하기", + "template_newsletters": "뉴스레터를 특정 폴더로 이동", + "template_receipts": "영수증 자동 보관", + "template_important": "중요 메일 표시", + "template_notifications": "알림 메일 분류", + "sieve_editor": { + "title": "Sieve 스크립트 에디터", + "warning": "원본 Sieve 스크립트를 수정하면 시각적 규칙 편집이 망가질 수 있어요. 여기서 변경한 내용이 시각적 에디터를 덮어씁니다.", + "script_content": "Sieve 스크립트", + "valid": "유효한 스크립트예요", + "invalid": "스크립트에 오류가 있어요", + "save_warning": "저장하면 시각적 규칙이 덮어쓰기 되며, 이 작업은 되돌릴 수 없어요. 확인을 위해 한 번 더 저장을 눌러주세요.", + "validating": "검증 중...", + "validate": "검증", + "cancel": "취소", + "save": "저장", + "confirm_save": "저장 확인", + "validation_failed": "검증 요청에 실패했어요" + }, + "rule_summary": { + "conditions_count": "조건 {count}개", + "actions_count": "동작 {count}개" + }, + "origin_external": "외부", + "managed_by_tooltip": "{source}에서 관리됩니다. 해당 앱에서 편집하거나 원시 Sieve 편집기를 사용하세요.", + "quick_rule_names": { + "move_sender": "{sender}에서 {folder}(으)로 이동", + "move_domain": "@{domain}에서 {folder}(으)로 이동", + "move_list": "리스트 {list}를 {folder}(으)로 이동", + "mark_read": "{sender}의 메일을 읽음으로 표시", + "tag": "{sender}의 메일에 {tag} 태그", + "block": "{sender} 차단", + "custom": "{sender}의 메일", + "custom_plain": "새 규칙", + "senders": "{count, plural, other {보낸 사람 #명}}" + }, + "suggestions": "제안", + "suggest_subject": "제목 “{subject}”", + "suggest_to": "받는 사람 {address}", + "suggest_cc": "참조 {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "이 폴더에 이미 있는 메일에도 적용", + "apply_existing_unsupported": "보낸 사람, 받는 사람, 제목 또는 헤더를 확인하고 이동, 복사, 읽음 표시, 별표 또는 태그를 하는 규칙만 기존 메일에 적용할 수 있어요.", + "period_title": "기간 설정", + "period_start": "시작일", + "period_end": "종료일", + "validation_period_order": "종료일은 시작일보다 나중이어야 해요", + "period_toggle": "특정 기간에만 활성화", + "period_hint": "이 기간에 도착하는 메시지에 적용돼요. 한쪽을 비워 두면 그쪽은 제한이 없어요.", + "validation_period_empty": "기간의 시작일 또는 종료일을 입력해 주세요", + "validation_period_invalid": "기간에 시간을 포함한 올바른 날짜를 입력하거나 칸을 비워 두세요", + "apply_existing_period": "기간이 설정된 규칙은 그 기간에 도착한 메시지에만 적용되므로 기존 메시지에는 적용할 수 없어요.", + "period_status_scheduled": "예정", + "period_status_active": "활성", + "period_status_expired": "만료" + }, + "templates": { + "title": "이메일 템플릿", + "description": "변수를 넣을 수 있는 재사용 가능한 메일 템플릿을 만들어 보세요", + "add": "새 템플릿", + "edit": "템플릿 수정", + "name": "템플릿 이름", + "name_placeholder": "예: 팔로업 메일", + "category": "카테고리", + "category_placeholder": "예: 업무, 개인", + "subject": "제목", + "subject_placeholder": "메일 제목", + "body": "본문", + "body_placeholder": "메일 내용...", + "recipients_placeholder": "email@example.com", + "identity": "보내는 사람", + "default_identity": "기본 보내는 사람", + "favorite": "즐겨찾기", + "cancel": "취소", + "create": "만들기", + "update": "업데이트", + "confirm_delete": "삭제", + "no_templates": "아직 템플릿이 없어요", + "manage": "템플릿 관리", + "count": "{count}개의 템플릿", + "export_import": "가져오기 & 내보내기", + "export_import_description": "템플릿을 백업하거나 다른 기기로 옮겨보세요", + "export": "내보내기", + "import": "가져오기", + "validation": { + "empty": "템플릿 이름이 필요해요", + "too_long": "템플릿 이름은 200자 이하여야 해요" + } + }, + "files": { + "display": { + "title": "화면 표시", + "description": "파일과 폴더를 보여주는 방식을 설정해요" + }, + "default_view": { + "label": "기본 뷰", + "description": "그리드와 리스트 레이아웃 중 선택해 주세요", + "list": "리스트", + "grid": "그리드" + }, + "default_sort": { + "label": "기본 정렬", + "description": "파일의 기본 정렬 기준을 선택해요", + "name": "이름", + "size": "크기", + "modified": "수정일" + }, + "sort_direction": { + "label": "정렬 방향", + "description": "오름차순 또는 내림차순을 선택해요", + "ascending": "오름차순", + "descending": "내림차순" + }, + "icons": { + "title": "아이콘", + "description": "파일 아이콘의 모양을 설정해요" + }, + "show_icons": { + "label": "파일 아이콘 표시", + "description": "파일과 폴더 옆에 아이콘을 보여줘요" + }, + "colored_icons": { + "label": "컬러 아이콘", + "description": "단색 대신 화사한 컬러 아이콘을 사용해요" + }, + "show_thumbnails": { + "label": "미리보기(썸네일) 표시", + "description": "이미지 파일의 경우 아이콘 대신 미리보기를 보여줘요" + }, + "behavior": { + "title": "동작", + "description": "파일 탐색기의 동작 방식을 설정해요" + }, + "show_hidden": { + "label": "숨긴 파일 표시", + "description": "이름이 점(.)으로 시작하는 숨긴 파일과 폴더를 보여줘요" + }, + "preview": { + "label": "미리보기" + } + }, + "search_placeholder": "설정 검색", + "search_clear": "검색 지우기", + "search_no_results": "일치하는 설정이 없습니다", + "scoped": { + "back": "내 계정으로 돌아가기", + "managing": "관리 중: {name}" + } + }, + "errors": { + "page_error_title": "문제가 발생했어요", + "page_error_description": "예상치 못한 오류가 생겼어요. 다시 시도하거나 홈 화면으로 돌아가 주세요.", + "sidebar_error": "편지함을 불러올 수 없어요", + "email_list_error": "메일을 불러올 수 없어요", + "viewer_error_title": "메일을 표시할 수 없어요", + "viewer_error_description": "이 메일을 화면에 그리는 데 문제가 있어요. 지원하지 않는 콘텐츠가 포함되어 있을 수 있어요.", + "composer_error": "메일 쓰기 창을 불러올 수 없어요", + "settings_error_title": "설정을 사용할 수 없어요", + "settings_error_description": "설정을 불러오지 못했어요. 변경 사항이 저장되지 않을 수 있어요.", + "try_again": "다시 시도", + "reload": "새로고침", + "reload_emails": "메일 새로고침", + "reload_settings": "설정 새로고침", + "retry": "재시도", + "go_home": "받은편지함으로 이동" + }, + "context_menu": { + "reply": "답장", + "reply_all": "전체 답장", + "forward": "전달", + "mark_read": "읽은 상태로 표시", + "mark_unread": "읽지 않은 상태로 표시", + "star": "별표 달기", + "unstar": "별표 해제", + "pin": "고정", + "unpin": "고정 해제", + "move_to": "이동...", + "copy_to_account": "계정으로 복사...", + "remove_from_folder": "이 폴더에서 제거", + "archive": "보관", + "delete": "삭제", + "mark_as_spam": "스팸 신고", + "not_spam": "정상 메일", + "tag": "태그", + "items_selected": "{count}개의 메일 선택됨", + "forward_as_attachments": "첨부 파일로 전달", + "edit_draft": "임시보관 메일 수정", + "cancel_scheduled_send": "보내기 취소", + "reschedule_send": "다시 예약", + "cancel_and_edit": "취소하고 편집", + "cancel_and_compose_again": "취소하고 다시 작성", + "rules": { + "title": "규칙", + "move_from": "{sender}의 메일을 항상 이동", + "move_from_domain": "@{domain}의 메일을 항상 이동", + "move_from_list": "이 메일링 리스트의 메일을 항상 이동", + "mark_read_from": "{sender}의 메일을 항상 읽음으로 표시", + "tag_with": "항상 태그 추가", + "block": "{count, plural, =1 {보낸 사람 차단} other {보낸 사람 #명 차단}}", + "no_junk": "이 계정에는 스팸 폴더가 없어요", + "create": "규칙 만들기…", + "manage": "규칙 관리", + "senders_count": "{count, plural, other {보낸 사람 #명}}", + "cross_account": "한 계정의 메일만 선택하세요", + "opaque_hint": "필터가 직접 편집되었어요. 필터 설정을 여세요" , + "loading": "규칙을 불러오는 중…", + "load_failed": "규칙을 불러올 수 없습니다", + "none": "아직 규칙이 없습니다", + "new_mail_only": "새 메시지에만 적용", + "running": "{folder}에서 \"{name}\" 실행 중…" + }, + "find_emails_from": "{name}님이 보낸 이메일 찾기" + }, + "mailbox_context_menu": { + "mark_folder_read": "폴더를 읽음으로 표시", + "mark_folder_tree_read": "폴더 및 하위 폴더를 읽음으로 표시", + "mark_all_folders_read": "모든 폴더를 읽음으로 표시", + "new_subfolder": "새 하위 폴더...", + "new_folder": "새 폴더...", + "rename": "이름 바꾸기...", + "share": "공유...", + "import_email": ".eml 또는 .zip 가져오기...", + "empty_folder": "폴더 비우기", + "empty_folder_generic": "폴더 비우기", + "delete_folder": "폴더 삭제", + "refresh": "새로 고침", + "hide_shared_account": "사이드바에서 숨기기", + "mute_notifications": "알림 끄기", + "unmute_notifications": "알림 켜기", + "mute_account_notifications": "이 계정의 알림 끄기", + "unmute_account_notifications": "이 계정의 알림 켜기", + "reset_folder_notifications": "폴더 알림 설정 초기화", + "mark_all_confirm_title": "모든 폴더를 읽음으로 표시", + "mark_all_confirm_message": "개인 계정의 모든 읽지 않은 메시지를 읽음으로 표시하시겠습니까?", + "delete_confirm_title": "폴더 삭제", + "delete_confirm_message": "폴더 \"{name}\"을(를) 영구적으로 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.", + "prompt_new_subfolder": "새 하위 폴더의 이름을 입력하세요.", + "prompt_new_folder": "새 폴더의 이름을 입력하세요.", + "prompt_rename": "이 폴더의 새 이름을 입력하세요.", + "toast_marked_read": "폴더를 읽음으로 표시했습니다", + "toast_marked_read_count": "{count, plural, one {메시지 1개} other {메시지 #개}}을(를) 읽음으로 표시했습니다", + "toast_already_read": "읽지 않은 메시지가 없습니다", + "toast_marked_all_read": "모든 폴더를 읽음으로 표시했습니다", + "toast_emptied": "폴더를 비웠습니다", + "toast_folder_created": "폴더가 생성되었습니다", + "toast_folder_renamed": "폴더 이름이 변경되었습니다", + "toast_folder_deleted": "폴더가 삭제되었습니다", + "toast_error_mark_read": "읽음으로 표시하지 못했습니다", + "toast_error_empty": "폴더를 비우지 못했습니다", + "toast_error_create": "폴더를 생성하지 못했습니다", + "toast_error_rename": "폴더 이름을 변경하지 못했습니다", + "toast_error_delete": "폴더를 삭제하지 못했습니다", + "toast_error_delete_has_children": "폴더에 하위 폴더가 있습니다. 먼저 제거하세요.", + "toast_error_delete_has_email": "폴더가 비어 있지 않습니다. 먼저 비우세요.", + "delete_with_contents_title": "폴더가 비어 있지 않습니다", + "delete_with_contents_message": "\"{name}\" 폴더에 아직 {count}개의 메시지가 있습니다. 폴더와 모든 메시지를 삭제할까요? 이 작업은 취소할 수 없습니다.", + "delete_with_contents_confirm": "폴더와 메시지 삭제", + "placeholder_folder_name": "폴더 이름", + "create": "만들기", + "rename_confirm": "이름 바꾸기" + }, + "shortcuts": { + "title": "단축키", + "tip": "언제든 ? 키를 누르면 이 도움말을 볼 수 있어요", + "sections": { + "navigation": "이동", + "actions": "메일 작업", + "global": "공통", + "threads": "대화(스레드)", + "composer": "메일 쓰기" + }, + "navigation": { + "next_email": "다음 메일", + "previous_email": "이전 메일", + "open_email": "메일 열기", + "close_email": "닫기 / 선택 해제", + "go_to_folder": "폴더로 이동", + "next_unread": "다음 읽지 않은 메일", + "no_more_unread": "목록 아래쪽에 읽지 않은 메일이 더 없습니다" + }, + "actions": { + "reply": "답장", + "reply_all": "전체 답장", + "forward": "전달", + "star": "별표 켜기/끄기", + "archive": "보관", + "delete": "삭제", + "mark_unread": "읽지 않음으로 표시", + "mark_read": "읽음으로 표시", + "toggle_spam": "스팸 신고 / 정상 표시" + }, + "global": { + "compose": "새 메일 쓰기", + "search": "검색창 이동", + "help": "단축키 보기", + "refresh": "메일 새로고침", + "select_all": "모두 선택", + "global_search": "전체 검색", + "extend_selection": "선택 범위를 아래 / 위로 확장" + }, + "threads": { + "expand_collapse": "대화 펼치기/접기", + "select_mountain_view": "대화 선택" + }, + "composer": { + "send": "이메일 보내기", + "schedule_send": "보내기 예약", + "template_picker": "템플릿 선택기 열기" + } + }, + "threads": { + "messages_one": "메시지 1개", + "messages_other": "메시지 {count}개", + "messages_tooltip": "이 대화에 {count}개의 메시지가 있어요", + "expand": "대화 펼치기", + "collapse": "대화 접기", + "loading": "대화를 불러오는 중...", + "mark_read": "대화 전체를 읽음으로 표시", + "mark_unread": "대화 전체를 읽지 않음으로 표시", + "archive": "대화 보관", + "delete": "대화 삭제", + "star": "대화 전체에 별표 달기", + "unstar": "대화 전체 별표 해제", + "toggle_thread": "스레드 켜기/끄기" + }, + "identities": { + "modal_title": "보내는 사람 관리", + "create_new": "새 보내는 사람 추가", + "edit_identity": "정보 수정", + "delete_confirm": "이 주소를 삭제할까요? 이 작업은 되돌릴 수 없어요.", + "cannot_delete": "이 주소는 삭제할 수 없어요", + "primary_identity": "기본", + "set_as_primary": "기본값으로 설정", + "no_identities": "저장된 주소가 없어요", + "display": { + "reply_to": "회신 주소 (Reply-To):", + "bcc": "숨은 참조 (BCC):", + "signature": "서명:", + "preview": "미리보기:" + }, + "validation_errors": { + "invalid_emails": "잘못된 이메일: {emails}", + "unknown_error": "알 수 없는 오류" + }, + "form": { + "name_label": "표시 이름", + "name_placeholder": "예: 업무용 메일, 개인", + "name_required": "이름을 입력해 주세요", + "email_label": "이메일 주소", + "email_placeholder": "your.email@example.com", + "email_required": "이메일 주소가 필요해요", + "email_invalid": "올바른 이메일 주소를 입력해 주세요", + "email_immutable": "한 번 만든 이메일 주소는 바꿀 수 없어요", + "reply_to_label": "회신 주소 (선택 사항)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "자동 숨은 참조 (선택 사항)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "텍스트 서명", + "html_signature_label": "HTML 서명", + "signature_byte_counter": "{bytes} / {max} 바이트", + "signature_byte_limit_reached": "서버 한도 도달", + "save": "저장", + "cancel": "취소", + "creating": "만드는 중...", + "updating": "업데이트 중..." + }, + "sub_address": { + "button_tooltip": "서브 어드레스 사용", + "popover_title": "서브 어드레스 태그 추가", + "tag_input_placeholder": "태그 입력 (예: shopping)", + "preview_label": "미리보기:", + "recent_tags": "최근 태그", + "suggested_tags": "추천", + "use_address": "이 주소 사용하기", + "invalid_tag": "태그는 알파벳, 숫자, 대시(-)만 쓸 수 있어요", + "tag_too_long": "태그는 30자 이하여야 해요", + "help_text": "user{delimiter}tag@domain.com 으로 보낸 메일은 내 받은편지함으로 들어와요", + "validation": { + "empty": "태그를 비워둘 수 없어요", + "too_long": "태그는 {max}자 이하여야 해요", + "invalid_chars": "태그에는 영문, 숫자, 대시(-)만 쓸 수 있어요" + } + }, + "badge": { + "sent_via": "경유:", + "sub_address_tag": "서브 어드레스 사용: {tag}", + "identity_name": "다음 주소로 전송됨: {name}", + "identity_short": "{name} 주소 사용", + "subaddress_tag": "+{tag}" + }, + "delete_button": "삭제", + "delete_confirm_title": "주소 삭제" + }, + "templates": { + "picker_title": "템플릿 선택", + "search_placeholder": "템플릿 검색...", + "section_favorites": "즐겨찾기", + "section_recent": "최근", + "section_uncategorized": "기타", + "no_templates": "아직 템플릿이 없어요", + "no_results": "템플릿을 찾을 수 없어요", + "fill_placeholders": "변수 값 입력하기", + "enter_value": "값을 입력해 주세요...", + "preview": "미리보기", + "insert_with_values": "값과 함께 삽입", + "insert_raw": "원본 삽입", + "copy_suffix": "(복사본)", + "placeholder": "변수", + "placeholders": { + "recipient_name": "받는 사람 이름", + "company": "회사 이름", + "date": "오늘 날짜", + "day_of_week": "요일", + "sender_name": "내 이름" + } + }, + "contacts": { + "directory": { + "title": "디렉터리", + "search_placeholder": "이름 또는 이메일 주소 검색", + "refresh": "디렉터리 새로 고침", + "retry": "다시 시도", + "loading": "디렉터리 불러오는 중…", + "unavailable_title": "디렉터리 없음", + "unavailable_hint": "메일 서버가 계정 디렉터리를 제공하지 않습니다.", + "forbidden_title": "디렉터리를 사용할 수 없음", + "forbidden_hint": "메일 서버가 이 계정의 디렉터리 탐색을 허용하지 않습니다. 관리자에게 문의하세요.", + "error_title": "디렉터리를 불러오지 못했습니다", + "error_hint": "연결을 확인하고 다시 시도하세요.", + "empty_title": "디렉터리가 비어 있습니다", + "empty_hint": "메일 서버에 이메일 주소가 있는 계정이 없습니다.", + "no_match": "검색과 일치하는 사람이 디렉터리에 없습니다", + "refresh_failed": "디렉터리를 새로 고치지 못했습니다. 마지막으로 불러온 목록을 표시합니다.", + "select_page": "이 페이지의 모두 선택", + "deselect_page": "이 페이지의 선택 해제", + "select_entry": "{name} 선택", + "range": "{total}개 중 {from}–{to}", + "page": "{count}페이지 중 {page}페이지", + "previous_page": "이전 페이지", + "next_page": "다음 페이지", + "picker_title": "디렉터리에서 선택", + "cancel": "취소", + "selected_count": "{count}명 선택됨", + "add_to": "받는 사람에 추가", + "add_cc": "참조에 추가", + "add_bcc": "숨은 참조에 추가", + "select_hint": "세부 정보를 보려면 사람을 선택하세요", + "write_email": "메일 쓰기", + "add_to_contacts": "연락처에 추가", + "email": "이메일", + "read_only_note": "디렉터리 항목은 메일 서버에서 가져오며 여기에서 편집할 수 없습니다.", + "type": { + "individual": "개인", + "group": "그룹", + "resource": "리소스", + "location": "위치", + "other": "기타" + } + }, + "title": "연락처", + "search_placeholder": "연락처 검색...", + "create_new": "새 연락처", + "no_category": "카테고리 없음", + "rename_category": "카테고리 이름 변경", + "category_name_label": "카테고리 이름", + "category_renamed": "카테고리 이름이 변경되었습니다", + "category_rename_failed": "카테고리 이름 변경 실패", + "category_added": "{name}에 연락처가 추가되었어요", + "category_added_plural": "{name}에 {count}개의 연락처가 추가되었어요", + "empty_state": "아직 연락처가 없어요", + "empty_state_title": "아직 연락처가 없어요", + "empty_state_subtitle": "새 연락처를 만들거나 vCard 파일에서 가져와 보세요", + "empty_search": "검색된 연락처가 없어요", + "empty_search_hint": "다른 검색어로 다시 시도해 보세요", + "empty_filtered": "필터에 일치하는 연락처가 없어요", + "empty_filtered_hint": "필터를 조정하거나 지워보세요", + "clear_search": "검색 지우기", + "import_vcard": "vCard 가져오기", + "delete_confirm_title": "연락처 삭제", + "delete_confirm": "정말 이 연락처를 삭제할까요?", + "local_mode": "연락처가 로컬에 저장돼요 (서버가 JMAP Contacts를 지원하지 않아요)", + "back_to_contacts": "연락처로 돌아가기", + "back_to_email": "이메일로 돌아가기", + "tabs": { + "all": "전체", + "groups": "그룹" + }, + "shared": { + "title": "공유됨" + }, + "address_books": { + "create": "새 주소록", + "created": "주소록이 생성되었습니다", + "create_failed": "주소록을 만들지 못했습니다", + "title": "내 주소록", + "shared_prefix": "공유됨: {name}", + "moved": "연락처가 {name}(으)로 이동되었어요", + "moved_plural": "{count}개의 연락처가 {name}(으)로 이동되었어요", + "move_failed": "연락처를 이동하지 못했어요", + "address_book": "주소록", + "rename": "주소록 이름 변경", + "name_label": "주소록 이름", + "renamed": "주소록 이름이 변경되었습니다", + "rename_failed": "주소록 이름 변경 실패", + "default": "기본", + "set_default": "기본으로 설정", + "default_updated": "기본 주소록이 업데이트되었습니다", + "set_default_failed": "기본 주소록을 설정하지 못했습니다", + "manage": "주소록 관리", + "share": "주소록 공유", + "new_contact_in_book": "이 주소록에 새 연락처", + "delete": "주소록 삭제", + "confirm_delete": "\"{name}\"을(를) 삭제하시겠습니까? 이 주소록의 모든 연락처가 삭제됩니다.", + "deleted": "주소록이 삭제되었습니다", + "delete_failed": "주소록 삭제에 실패했습니다" + }, + "detail": { + "emails": "이메일", + "phones": "전화번호", + "organizations": "소속", + "addresses": "주소", + "notes": "메모", + "titles": "직함 및 역할", + "online_services": "온라인 서비스", + "anniversaries": "기념일", + "personal_info": "개인 정보", + "languages": "언어", + "categories": "카테고리", + "related_contacts": "관련 연락처", + "crypto_keys": "암호화 키", + "cert_issuer": "발급자", + "cert_expires": "만료일", + "cert_expired": "만료됨", + "cert_fingerprint": "지문(Fingerprint)", + "cert_algorithm": "알고리즘", + "import_to_smime": "S/MIME으로 가져오기", + "cert_already_imported": "이미 S/MIME에 등록되어 있어요", + "cert_imported": "인증서가 S/MIME 저장소에 추가되었어요", + "cert_import_failed": "인증서를 가져오지 못했어요", + "no_contact_selected": "세부 정보를 보려면 연락처를 선택해 주세요", + "compose_email": "메일 쓰기", + "copy_email": "이메일 복사", + "copy_phone": "전화번호 복사", + "copy_url": "URL 복사", + "copied": "클립보드에 복사되었어요", + "copy_failed": "복사하지 못했어요", + "created": "생성일", + "updated": "마지막 업데이트", + "timezone": "시간대", + "anniversary_birth": "생일", + "anniversary_death": "기일", + "anniversary_wedding": "결혼기념일", + "anniversary_other": "기타", + "personal_expertise": "전문 분야", + "personal_hobby": "취미", + "personal_interest": "관심사", + "personal_other": "기타", + "gender": "성별", + "gender_masculine": "남성", + "gender_feminine": "여성", + "gender_other": "기타", + "gender_none": "해당 없음", + "gender_unknown": "알 수 없음", + "calendar": "캘린더", + "calendar_uri": "캘린더 URL", + "scheduling_uri": "스케줄링 URL", + "freebusy_uri": "Free/Busy URL", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "작업 더보기", + "age_years": "{count}세", + "years_since": "{count}년 전" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "새 연락처", + "edit_title": "연락처 수정", + "section_address_book": "디렉터리", + "select_address_book": "디렉터리 선택...", + "section_identity": "이름 및 신원", + "contact_type": "연락처 유형", + "type_person": "개인", + "type_organization": "소속(회사)", + "section_work": "직장 및 소속", + "prefix": "호칭", + "prefix_placeholder": "예: Dr., Mr., Mrs.", + "given_name": "이름", + "middle_name": "중간 이름", + "surname": "성", + "suffix": "접미사", + "suffix_placeholder": "예: Jr., Sr.", + "nickname": "별명", + "nickname_placeholder": "별명", + "email": "이메일", + "email_placeholder": "email@example.com", + "phone": "전화번호", + "phone_placeholder": "010-1234-5678", + "phone_type": "유형", + "phone_voice": "음성", + "phone_cell": "휴대전화", + "phone_fax": "팩스", + "phone_pager": "호출기", + "phone_video": "화상", + "phone_text": "문자", + "organization": "소속(회사)", + "organization_placeholder": "회사 이름", + "department": "부서", + "department_placeholder": "부서명", + "job_title": "직함", + "job_title_placeholder": "예: 소프트웨어 엔지니어", + "role": "역할", + "role_placeholder": "예: 팀장", + "addresses": "주소", + "add_address": "주소 추가", + "street": "도로명 주소", + "city": "시/도", + "region": "지역", + "postcode": "우편번호", + "country": "국가", + "online_services": "온라인 서비스", + "add_online_service": "온라인 서비스 추가", + "url_placeholder": "https://...", + "service_placeholder": "서비스", + "anniversaries": "기념일", + "add_anniversary": "날짜 추가", + "anniversary_birth": "생일", + "anniversary_wedding": "결혼기념일", + "anniversary_death": "기일", + "anniversary_other": "기타", + "personal_info": "개인 정보", + "add_personal_info": "항목 추가", + "personal_info_placeholder": "예: 사진 촬영", + "personal_expertise": "전문 분야", + "personal_hobby": "취미", + "personal_interest": "관심사", + "personal_other": "기타", + "level": "수준", + "level_high": "상", + "level_medium": "중", + "level_low": "하", + "categories": "카테고리", + "categories_placeholder": "예: 가족, 친구, 동료", + "categories_hint": "입력하여 검색하거나 카테고리를 추가하세요", + "category_add": "추가", + "note": "메모", + "note_placeholder": "메모를 남겨주세요...", + "gender": "성별", + "gender_sex": "성별(생물학적)", + "gender_male": "남성", + "gender_female": "여성", + "gender_other": "기타", + "gender_none": "해당 없음", + "gender_unknown": "알 수 없음", + "gender_identity": "성 정체성", + "gender_identity_placeholder": "성 정체성...", + "calendar": "캘린더", + "calendar_uri": "캘린더 URL", + "scheduling_uri": "스케줄링 URL", + "freebusy_uri": "Free/Busy URL", + "context_work": "직장", + "context_private": "개인", + "add_email": "이메일 추가", + "add_phone": "전화번호 추가", + "save": "저장", + "cancel": "취소", + "creating": "만드는 중...", + "updating": "업데이트 중...", + "name_required": "이름, 성 또는 조직을 입력하세요", + "email_invalid": "올바른 이메일 주소를 입력해 주세요", + "email_error_inline": "이메일 형식이 잘못되었어요", + "save_failed": "연락처를 저장하지 못했어요", + "delete": "삭제", + "upload_photo": "사진 업로드", + "remove_photo": "사진 제거", + "photo_hint": "JPG 또는 PNG, 최도 10MB. 크기가 조정됩니다.", + "photo_too_large": "이미지가 너무 큽니다 (최대 10MB)", + "photo_invalid": "잘못된 이미지 파일", + "change_photo": "변경" + }, + "groups": { + "create": "새 그룹", + "edit": "그룹 수정", + "empty": "아직 그룹이 없어요", + "delete_confirm_title": "그룹 삭제", + "delete_confirm": "정말 이 그룹을 삭제할까요?", + "name_label": "그룹 이름", + "name_placeholder": "예: 팀, 가족", + "name_required": "그룹 이름이 필요해요", + "save_failed": "그룹을 저장하지 못했어요", + "members_label": "멤버", + "search_members": "추가할 연락처 검색...", + "no_members": "이 그룹에는 멤버가 없어요", + "member_count": "멤버 {count}명", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "연락처 가져오기", + "drop_hint": "클릭해서 vCard 파일을 선택해 주세요", + "file_types": ".vcf 또는 .vcard 파일", + "no_contacts": "파일에서 연락처를 찾을 수 없어요", + "parse_error": "vCard 파일을 분석하는 데 실패했어요", + "found": "{count}개의 연락처를 찾았어요", + "duplicate": "중복", + "select_all": "모두 선택", + "deselect_all": "모두 선택 해제", + "selected": "{count}개 선택됨", + "import_button": "가져오기", + "importing": "가져오는 중...", + "success": "{count}개의 연락처를 성공적으로 가져왔어요", + "failed": "가져오기 실패", + "close": "닫기", + "file_too_large": "파일이 너무 커요 (최대 5MB)" + }, + "export": { + "title": "연락처 내보내기", + "success": "{count}개의 연락처를 성공적으로 내보냈어요" + }, + "bulk": { + "selected": "{count}개 선택됨", + "select_all": "모두 선택", + "delete": "삭제", + "delete_confirm_title": "연락처 삭제", + "delete_confirm": "선택한 {count}개의 연락처를 삭제할까요?", + "deleted": "{count}개의 연락처가 삭제되었어요", + "add_to_group": "그룹에 추가", + "choose_group": "그룹 선택", + "adding_contacts": "{count}개의 연락처를 추가하는 중...", + "added_to_group": "연락처가 그룹에 추가되었어요", + "export": "내보내기", + "clear": "선택 취소" + }, + "toast": { + "created": "연락처가 만들어졌어요", + "updated": "연락처가 업데이트되었어요", + "deleted": "연락처가 삭제되었어요", + "error_create": "연락처를 만들지 못했어요", + "error_update": "연락처를 업데이트하지 못했어요", + "error_delete": "연락처를 삭제하지 못했어요" + }, + "context_menu": { + "open": "열기", + "edit": "편집", + "send_email": "이메일 보내기", + "add_to_group": "그룹에 추가", + "export_vcard": "vCard로 내보내기", + "delete": "삭제", + "call": "통화", + "duplicate": "복제", + "print": "인쇄" + }, + "filters": { + "toggle": "필터", + "select": "선택", + "clear": "지우기", + "close": "닫기", + "title": "고급 필터", + "organization": "회사", + "organization_placeholder": "예: Acme", + "job_title": "직책", + "job_title_placeholder": "예: 디자이너", + "location": "위치", + "location_placeholder": "도시 또는 국가", + "email_domain": "이메일 도메인", + "email_domain_placeholder": "example.com", + "birthday_month": "생일 월", + "any_month": "모든 달", + "has_email": "이메일 있음", + "has_phone": "전화번호 있음", + "has_photo": "사진 있음", + "sort_by": "정렬 기준", + "sort_first_name": "이름", + "sort_last_name": "성" + }, + "open_categories": "카테고리 열기" + }, + "calendar": { + "title": "캘린더", + "back_to_email": "메일로 돌아가기", + "back_to_month": "월간 보기로 돌아가기", + "my_calendars": "내 캘린더", + "birthday_calendar": "생일", + "mini_calendar_change": "클릭해서 달을 변경하세요", + "views": { + "month": "월", + "week": "주", + "day": "일", + "agenda": "일정 목록", + "today": "오늘", + "month_hint": "월간 (m)", + "week_hint": "주간 (w)", + "day_hint": "일간 (d)", + "agenda_hint": "일정 목록 (a)", + "tasks": "할 일", + "tasks_hint": "할 일 (k)" + }, + "events": { + "hidden_before": "{count, plural, other {이전 일정 #개}}", + "hidden_after": "{count, plural, other {이후 일정 #개}}", + "show_all_hours": "모든 시간 표시", + "show_display_hours": "{start} – {end}만 표시", + "create": "일정 만들기", + "edit": "일정 수정", + "delete": "일정 삭제", + "details": "일정 세부 정보", + "no_events": "일정이 없어요", + "all_day": "하루 종일", + "more": "+{count}개 더보기", + "show_more": "더 보기", + "show_less": "간략히 보기", + "no_title": "(제목 없음)", + "resize": "일정 크기 조절", + "duplicate": "복제", + "today_header": "오늘", + "tomorrow_header": "내일", + "agenda_show_earlier": "이전 일정 보기", + "agenda_loading": "불러오는 중…", + "agenda_range_start": "{date}부터의 일정", + "agenda_range_end": "{date}까지의 일정", + "export_ics": ".ics로 내보내기", + "copy_title": "제목 복사", + "copy_link": "회의 링크 복사", + "go_to_today": "오늘로 이동", + "new_all_day_event": "새 종일 이벤트", + "new_event": "새 이벤트", + "new_task": "새 작업" + }, + "detail": { + "add_note": "메모 추가...", + "save_note": "저장", + "note_saved": "메모가 추가되었어요", + "open_link": "링크 열기", + "copy_location": "위치 복사", + "meeting_link": "화상 회의 링크", + "tentative": "미정", + "cancelled": "취소됨", + "delete_confirm": "이 일정을 삭제할까요?" + }, + "form": { + "title": "제목", + "description": "설명", + "location": "위치", + "meeting_link": "화상 회의 링크", + "start_date": "시작 날짜", + "end_date": "종료 날짜", + "start_time": "시작 시간", + "end_time": "종료 시간", + "all_day_event": "하루 종일 일정", + "calendar_select": "캘린더", + "save": "저장", + "cancel": "취소", + "delete_confirm": "정말 이 일정을 삭제할까요?", + "color": "색상" + }, + "participants": { + "availability": { + "title": "일정 상태", + "free": "가능", + "busy": "바쁨", + "tentative": "미정", + "unknown": "이 서버의 사용자가 아님", + "checking": "확인 중…" + }, + "title": "참석자", + "add": "참석자 추가", + "organizer": "주최자", + "attendee": "참석자", + "accepted": "수락함", + "declined": "거절함", + "tentative": "미정", + "needs_action": "응답 필요", + "remove": "삭제", + "edit": "편집", + "email_placeholder": "이메일을 추가하거나 연락처를 검색하세요", + "send_invitations": "참석자에게 초대장 보내기", + "status_summary": "{accepted}명 수락, {pending}명 대기 중", + "invited_by": "{name}님의 초대", + "respond_below": "아래 버튼을 눌러 응답해 주세요", + "rsvp_label": "내 응답", + "cancel_notification": "참석자들에게 취소 알림이 전송돼요", + "you_organizer": "내가 주최자예요", + "you_attendee": "나는 참석자예요", + "no_participants": "참석자가 없어요", + "count": "참석자 {count}명" + }, + "recurrence": { + "title": "반복 일정", + "none": "반복 안 함", + "daily": "매일", + "weekly": "매주", + "monthly": "매월", + "yearly": "매년", + "every_n_days": "{count}일마다", + "every_n_weeks": "{count}주마다", + "every_n_months": "{count}개월마다", + "until": "종료일:", + "occurrences": "{count}회 반복", + "custom": "맞춤…", + "edit_custom": "맞춤 반복 수정", + "every_n_years": "{count}년마다", + "on_days": "{days}", + "on_day_n": "{day}일", + "on_the_nth": "{nth} {day}", + "in_month": "{month}", + "nth_1": "첫째", + "nth_2": "둘째", + "nth_3": "셋째", + "nth_4": "넷째", + "nth_last": "마지막", + "editor_freq_day": "일", + "editor_freq_week": "주", + "editor_freq_month": "월", + "editor_freq_year": "년", + "editor_repeats_on": "반복", + "editor_every": "매", + "editor_unit_days": "일", + "editor_unit_weeks": "주", + "editor_unit_months": "개월", + "editor_unit_years": "년", + "editor_on_day": "날짜:", + "editor_on_the": "요일:", + "editor_in": "월:", + "editor_ends": "종료", + "editor_never": "안 함", + "editor_ends_on": "종료일", + "editor_ends_after": "횟수", + "editor_occurrences": "회" + }, + "recurrence_scope": { + "edit_title": "반복 일정 수정", + "delete_title": "반복 일정 삭제", + "description": "반복되는 일정이에요. 어떤 일정을 수정할까요?", + "this_event": "이 일정만", + "this_and_future": "이 일정 및 향후 일정 모두", + "all_events": "모든 일정", + "cancel": "취소", + "save": "저장", + "delete": "삭제", + "rsvp_title": "반복 일정에 응답", + "rsvp_description": "반복되는 일정이에요. 어떤 일정에 응답할까요?", + "respond": "응답" + }, + "alerts": { + "title": "알림", + "none": "알림 없음", + "at_time": "일정 시작 시", + "minutes_before": "{count}분 전", + "hours_before": "{count}시간 전", + "days_before": "{count}일 전", + "weeks_before": "{count}주 전", + "unit_minutes_before": "분 전", + "unit_hours_before": "시간 전", + "unit_days_before": "일 전", + "unit_weeks_before": "주 전", + "add": "알림 추가", + "remove": "알림 제거", + "amount": "알림 값", + "unit": "알림 단위", + "action": "알림 유형", + "action_display": "알림", + "action_email": "이메일", + "by_email": "{label} (이메일)", + "kept": "여기서 편집할 수 없는 알림 {count}개가 유지돼요", + "other": "그 외 알림 {count}개", + "calendar_defaults": "캘린더 기본값", + "customize": "사용자 지정", + "use_defaults": "캘린더 기본값 사용" + }, + "settings": { + "limit_hours": "표시 시간 제한", + "limit_hours_desc": "일간 및 주간 보기에서 이 시간대만 표시합니다. 범위 밖의 일정은 각 날짜 위아래의 화살표로 볼 수 있습니다.", + "visible_hours": "표시 시간", + "visible_hours_start": "표시 시작 시간", + "visible_hours_end": "표시 종료 시간", + "hide_non_working_days": "휴무일 숨기기", + "hide_non_working_days_desc": "근무하지 않는 요일을 주간 보기에서 제외합니다. 해당 일정은 월간, 일간, 일정 목록 보기에 계속 표시됩니다.", + "working_days": "근무일", + "title": "캘린더 설정", + "default_view": "기본 뷰", + "week_starts_on": "한 주의 시작", + "time_format": "시간 형식", + "default_calendar": "기본 캘린더", + "default_reminder": "기본 알림", + "time_format_12h": "12시간제", + "time_format_24h": "24시간제", + "notifications_enabled": "일정 알림", + "notifications_enabled_desc": "다가오는 일정에 대한 알림을 보여줘요", + "notification_sound": "알림음", + "notification_sound_desc": "일정 알림 시 소리를 재생해요", + "invitation_parsing": "이메일 초대장 분석", + "invitation_parsing_desc": "이메일 첨부파일에서 캘린더 초대장을 감지하고 보여줘요", + "show_time_in_month_view": "월간 보기에 시간 표시", + "show_time_in_month_view_desc": "월간 캘린더에서도 일정 시간을 표시해요", + "show_week_numbers": "주 차 번호 표시", + "show_week_numbers_desc": "미니 캘린더에 1년 중 몇 주 차인지 표시해요", + "calendar_free_scroll": "자유 스크롤", + "calendar_free_scroll_desc": "한 번에 한 기간씩 보는 대신 월, 주, 일을 연속으로 스크롤합니다", + "enable_tasks": "할 일 사용", + "enable_tasks_desc": "캘린더에 할 일을 관리할 수 있는 메뉴를 추가해요", + "show_tasks_on_calendar": "캘린더에 할 일 표시", + "show_tasks_on_calendar_desc": "월간, 주간 및 일간 캘린더에 할 일을 보여줘요", + "hover_preview": "마우스 호버 미리보기", + "hover_preview_desc": "캘린더 일정 위에 마우스를 올리면 세부 정보 팝업을 띄워요", + "hover_preview_instant": "즉시", + "hover_preview_delay_500ms": "0.5초 지연", + "hover_preview_delay_1s": "1초 지연", + "hover_preview_delay_2s": "2초 지연", + "hover_preview_off": "사용 안 함", + "organizer_identity": "초대 주최자 주소", + "organizer_identity_desc": "이벤트에 사람들을 초대할 때 주최자로 사용되는 캘린더 주소입니다.", + "organizer_identity_failed": "주최자 주소를 변경하지 못했습니다", + "show_birthday_calendar": "연락처 생일 캘린더", + "show_birthday_calendar_desc": "연락처에 등록된 생일을 가상 캘린더로 보여줘요" + }, + "days": { + "monday": "월요일", + "tuesday": "화요일", + "wednesday": "수요일", + "thursday": "목요일", + "friday": "금요일", + "saturday": "토요일", + "sunday": "일요일", + "mon": "월", + "tue": "화", + "wed": "수", + "thu": "목", + "fri": "금", + "sat": "토", + "sun": "일" + }, + "notifications": { + "event_created": "일정이 만들어졌어요", + "event_updated": "일정이 업데이트되었어요", + "event_deleted": "일정이 삭제되었어요", + "calendar_created": "캘린더가 만들어졌어요", + "calendar_deleted": "캘린더가 삭제되었어요", + "event_move_error": "일정을 이동하지 못했어요", + "event_resize_error": "일정 시간을 조절하지 못했어요", + "alert_title": "다가오는 일정", + "alert_now": "지금 시작해요", + "alert_in_minutes": "{count}분 후 시작", + "invitation_sent": "초대장이 전송되었어요", + "invitations_denied_title": "초대장을 보낼 수 없어요", + "invitations_denied": "서버가 초대장 발송을 거부했어요: {reason}. 초대장 없이 일정을 저장할까요?", + "save_without_invitations": "초대장 없이 저장", + "rsvp_updated": "응답이 업데이트되었어요", + "rsvp_error": "응답을 업데이트하지 못했어요", + "event_duplicated": "일정이 복제되었어요", + "event_error": "일정을 저장하지 못했어요", + "task_due": "할 일 기한이 다가와요", + "event_exported": "이벤트 내보내기 완료", + "title_copied": "제목이 복사되었습니다", + "link_copied": "링크가 복사되었습니다" + }, + "status": { + "loading_calendars": "캘린더를 불러오는 중...", + "loading_events": "일정을 불러오는 중..." + }, + "quick_create": { + "placeholder": "새 일정 제목", + "aria_label": "빠른 일정 만들기", + "add_title": "제목 추가", + "more_options": "추가 옵션" + }, + "nav_prev": "이전", + "nav_next": "다음", + "import": { + "title": "캘린더 가져오기", + "tab_file": "파일", + "tab_url": "URL", + "select_file": ".ics 파일 선택", + "drop_file": "또는 여기에 파일을 놓아주세요", + "supported_formats": "iCalendar (.ics) 파일을 지원해요", + "url_description": "외부 iCalendar (.ics) 피드의 URL을 입력해서 일정을 가져와요.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "iCalendar (.ics) URL을 지원해요", + "fetch": "가져오기", + "invalid_url": "올바른 URL을 입력해 주세요", + "url_fetch_failed": "URL에서 캘린더를 가져오지 못했어요", + "parsing": "캘린더 파일을 분석하는 중...", + "parsed_events": "{count}개의 일정을 찾았어요", + "no_events": "파일에서 일정을 찾을 수 없어요", + "select_all": "모두 선택", + "deselect_all": "모두 선택 해제", + "target_calendar": "저장할 캘린더", + "import_button": "선택 항목 가져오기", + "importing": "일정을 가져오는 중...", + "success": "{count}개의 일정을 성공적으로 가져왔어요", + "error": "캘린더를 가져오지 못했어요", + "file_too_large": "파일이 10MB 제한을 넘었어요", + "invalid_format": "캘린더 파일 형식이 올바르지 않아요" + }, + "webcal_action": { + "title": "캘린더 링크 열기", + "description": "\"{name}\"을 어떻게 사용할까요?", + "import_title": "한 번 가져오기", + "import_description": "지금 일정을 가져와 내 캘린더 중 하나에 복사합니다.", + "subscribe_title": "구독", + "subscribe_description": "이 캘린더를 별도의 캘린더로 자동 동기화합니다.", + "cancel": "취소" + }, + "management": { + "title": "캘린더 관리", + "description": "캘린더를 만들고 이름을 바꾸거나 색상을 꾸며보세요. 사이드바에서 캘린더를 우클릭하면 색상을 빠르게 바꿀 수 있어요.", + "name": "이름", + "name_placeholder": "캘린더 이름", + "color": "색상", + "change_color": "색상 변경", + "random_color": "새 무작위 색상", + "add_calendar": "캘린더 추가", + "edit": "수정", + "delete": "삭제", + "save": "저장", + "create": "만들기", + "cancel": "취소", + "default": "기본", + "confirm_delete": "\"{name}\" 캘린더를 삭제할까요? 캘린더 안의 모든 일정이 함께 지워져요.", + "confirm_clear": "\"{name}\" 캘린더의 모든 일정을 지울까요? 이 작업은 되돌릴 수 없어요.", + "set_default": "기본으로 설정", + "default_updated": "기본 캘린더가 업데이트되었습니다", + "error_default": "기본 캘린더를 설정하지 못했습니다", + "clear_events": "일정 모두 지우기", + "events_cleared": "{count}개의 일정을 지웠어요", + "error_clear": "일정을 지우지 못했어요", + "calendar_created": "캘린더가 만들어졌어요", + "calendar_updated": "캘린더가 업데이트되었어요", + "calendar_deleted": "캘린더가 삭제되었어요", + "color_updated": "캘린더 색상이 변경되었어요", + "error_create": "캘린더를 만들지 못했어요", + "error_update": "캘린더를 업데이트하지 못했어요", + "error_delete": "캘린더를 삭제하지 못했어요", + "caldav_url": "CalDAV URL", + "copy_url": "CalDAV URL 복사", + "url_copied": "CalDAV URL이 클립보드에 복사되었어요", + "share": "캘린더 공유", + "new_event_in_calendar": "이 캘린더에 새 일정", + "shared_with": "공유 대상: {names}", + "shared_with_count": "{count, plural, other {#명과 공유됨}}", + "move_up": "위로 이동", + "move_down": "아래로 이동", + "kind": "내용", + "kind_events": "일정", + "kind_tasks": "할 일", + "kind_both": "일정 및 할 일", + "kind_hint": "CalDAV로 동기화할 때 이 캘린더를 볼 수 있는 앱을 결정합니다. 나중에 변경할 수 없습니다.", + "default_alerts": "기본 알림", + "default_alerts_title": "\"{name}\" 기본 알림", + "default_alerts_hint": "이 캘린더의 새 일정에는 일정에서 변경하지 않는 한 이 알림이 적용돼요. 여기서 변경해도 기존 일정은 바뀌지 않아요. 이메일 알림은 메일 서버가 보내요.", + "default_alerts_timed": "시간이 있는 일정", + "default_alerts_all_day": "종일 일정", + "default_alerts_updated": "기본 알림이 업데이트되었어요" + }, + "subscription": { + "title": "iCal 구독", + "section_title": "iCal 구독", + "description": "외부 iCalendar 피드를 구독해 보세요. 일정들이 전용 캘린더에 자동으로 동기화돼요. (https:// 및 webcal:// 지원)", + "url_label": "캘린더 URL", + "url_placeholder": "https://example.com/calendar.ics 또는 webcal://...", + "name_label": "캘린더 이름", + "name_placeholder": "예: 공휴일", + "color_label": "색상", + "refresh_interval": "새로고침 간격", + "interval_15": "15분마다", + "interval_30": "30분마다", + "interval_60": "1시간마다", + "interval_360": "6시간마다", + "interval_1440": "매일", + "subscribe": "구독하기", + "subscribing": "구독 중...", + "save": "변경 사항 저장", + "saving": "저장 중...", + "edit": "수정", + "edit_title": "구독 수정", + "updated": "\"{name}\" 구독이 업데이트되었어요", + "update_error": "구독을 업데이트하지 못했어요", + "invalid_url": "올바른 URL을 입력해 주세요", + "success": "\"{name}\" 캘린더를 구독했어요", + "error": "구독을 추가하지 못했어요", + "refresh": "지금 새로고침", + "refresh_success": "구독이 새로고침되었어요", + "refresh_error": "구독을 새로고침하지 못했어요", + "unsubscribe": "구독 취소", + "confirm_delete": "\"{name}\" 구독을 취소할까요? 캘린더와 모든 일정이 삭제돼요.", + "deleted": "구독이 삭제되었어요", + "delete_error": "구독을 삭제하지 못했어요", + "last_refreshed": "마지막 업데이트: {time}" + }, + "tasks": { + "label": "할 일", + "no_tasks": "할 일이 없어요", + "no_title": "(제목 없음)", + "mark_complete": "완료로 표시", + "mark_incomplete": "미완료로 표시", + "filter_all": "전체", + "filter_pending": "대기 중", + "filter_completed": "완료됨", + "filter_overdue": "기한 지남", + "show_completed": "완료된 항목 보기", + "create": "새 할 일", + "edit": "할 일 수정", + "title_placeholder": "할 일 제목", + "description_placeholder": "설명 추가...", + "due_date": "마감일", + "include_time": "시간 포함", + "priority": "우선순위", + "priority_none": "없음", + "priority_high": "높음", + "priority_medium": "보통", + "priority_low": "낮음", + "progress": "상태", + "progress_needs_action": "진행 필요", + "progress_in_process": "진행 중", + "progress_completed": "완료", + "progress_cancelled": "취소됨", + "calendar": "캘린더", + "alert": "알림", + "alert_none": "없음", + "alert_at_time": "마감 시간에", + "alert_5min": "5분 전", + "alert_15min": "15분 전", + "alert_30min": "30분 전", + "alert_1hr": "1시간 전", + "alert_1day": "1일 전", + "delete": "삭제", + "cancel": "취소", + "save": "저장", + "quick_add_placeholder": "할 일 추가...", + "due_today": "오늘 마감", + "due_tomorrow": "내일 마감", + "overdue": "기한 지남" + }, + "months": { + "jan": "1월", + "feb": "2월", + "mar": "3월", + "apr": "4월", + "may": "5월", + "jun": "6월", + "jul": "7월", + "aug": "8월", + "sep": "9월", + "oct": "10월", + "nov": "11월", + "dec": "12월", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "메뉴 열기", + "toolbar": { + "more": "추가 작업", + "select_view": "보기 변경, 현재 {view}" + }, + "other_calendars": "다른 캘린더", + "calendar_options": "{name} 옵션" + }, + "advanced_search": { + "title": "상세 검색", + "from": "보낸 사람", + "from_placeholder": "이메일이나 이름", + "to": "받는 사람", + "to_placeholder": "이메일이나 이름", + "subject": "제목", + "subject_placeholder": "제목에 포함된 단어...", + "body": "본문", + "body_placeholder": "내용에 포함된 단어...", + "folder": "폴더", + "all_folders": "모든 폴더", + "all_folders_except_spam_trash": "스팸함과 휴지통을 제외한 모든 폴더", + "has_attachment": "첨부파일 있음", + "date_after": "이후", + "date_before": "이전", + "size_min": "다음 크기 이상 (KB)", + "size_max": "다음 크기 미만 (KB)", + "starred": "별표 있음", + "unread": "읽지 않음", + "read": "읽음", + "yes": "예", + "no": "아니요", + "clear": "초기화", + "clear_all": "모두 초기화", + "filters_active": "{count}개의 필터 적용됨", + "filters_active_plural": "{count}개의 필터 적용됨", + "toggle_filters": "더보기", + "search_hint": "고급 필터로 더 정확하게 검색해 보세요", + "advanced_filters_tooltip": "상세 검색 필터", + "results_found": "{count}개의 검색 결과", + "results_found_more": "{count}개 이상의 검색 결과", + "suggestions_recent": "최근 검색", + "suggestions_people": "사람", + "suggestions_from": "{address}에서 온 메일 검색", + "suggestions_to": "{address}에게 보낸 메일 검색", + "suggestions_remove_recent": "최근 검색에서 삭제", + "has_words": "포함하는 단어", + "size": "크기", + "date_within": "날짜", + "search_in": "검색 위치", + "has_attachment_check": "첨부파일 있음", + "search": "검색" + }, + "welcome": { + "title": "웹메일에 오신 것을 환영해요!", + "tip_compose": "c 키를 누르면 바로 새 메일을 쓸 수 있어요", + "tip_shortcuts": "? 키를 누르면 모든 단축키를 확인할 수 있어요", + "tip_sidebar": "캘린더, 연락처, 설정은 사이드바 메뉴에 있어요", + "tip_settings": "설정에서 나만의 스타일로 꾸며보세요", + "tip_compose_touch": "연필 버튼을 눌러 새 메일을 작성하세요", + "tip_sidebar_touch": "캘린더, 연락처, 설정은 하단 막대에 있습니다", + "got_it": "확인했어요", + "settings": "설정", + "dismiss": "닫기", + "start_tour": "둘러보기 시작" + }, + "demo_welcome": { + "title": "Bulwark Mail에 오신 것을 환영해요", + "description": "브라우저에서 바로 완전한 기능을 갖춘 웹메일 클라이언트를 경험해 보세요. 모든 데이터는 내 기기에만 저장되니 안심하고 테스트해 보셔도 좋아요.", + "feature_email": "메일 읽기 및 쓰기", + "feature_organize": "태그, 별표, 폴더 관리", + "feature_shortcuts": "키보드 단축키", + "feature_privacy": "100% 프라이버시 보호 데모", + "hint": "왼쪽에서 메일을 클릭해서 시작하거나, 아래의 둘러보기를 클릭해 보세요." + }, + "files": { + "office_edit": "문서 편집", + "office_loading": "문서 편집기를 여는 중…", + "office_error": "문서 편집기를 열 수 없습니다.", + "office_close": "편집기 닫기", + "open_in_new_tab": "새 탭에서 열기", + "title": "파일", + "search_placeholder": "파일 검색...", + "empty_state_title": "아직 파일이 없어요", + "upload_files": "파일 업로드", + "new_folder": "새 폴더", + "new_folder_name": "폴더 이름", + "rename": "이름 바꾸기", + "rename_title": "이름 바꾸기", + "new_name": "새 이름", + "name_forbidden_chars": "서버에서 이름에 다음 문자를 허용하지 않습니다: {chars}", + "name_reserved": "이 이름은 서버에서 예약되어 있습니다.", + "delete": "삭제", + "delete_confirm_title": "항목 삭제", + "delete_confirm_message": "정말 \"{name}\"을(를) 삭제할까요? 이 작업은 되돌릴 수 없어요.", + "download": "다운로드", + "name": "이름", + "size": "크기", + "modified": "수정일", + "type": "유형", + "folder": "폴더", + "file": "파일", + "uploading": "업로드 중...", + "upload_success": "{count}개의 파일이 업로드되었어요", + "upload_canceled": "업로드가 취소되었어요", + "upload_canceled_partial": "취소하기 전에 {count, plural, other {#개의 파일}}이 업로드되었어요", + "upload_error": "파일 업로드에 실패했어요", + "create_folder_success": "폴더가 만들어졌어요", + "create_folder_error": "폴더를 만들지 못했어요", + "delete_success": "성공적으로 삭제되었어요", + "delete_error": "삭제에 실패했어요", + "rename_success": "이름이 변경되었어요", + "rename_error": "이름을 바꾸지 못했어요", + "download_error": "다운로드에 실패했어요", + "not_available": "이 서버에서는 파일 저장소를 사용할 수 없어요", + "cancel": "취소", + "create": "만들기", + "save": "저장", + "no_results": "검색된 파일이 없어요", + "batch_delete_confirm_message": "선택한 {count}개의 항목을 정말 삭제할까요? 이 작업은 되돌릴 수 없어요.", + "batch_delete_success": "{count}개의 항목이 삭제되었어요", + "grid_view": "그리드 뷰", + "list_view": "리스트 뷰", + "details": "세부 정보", + "path": "경로", + "preview": "미리보기", + "preview_error": "미리보기를 불러오지 못했어요", + "cut": "잘라내기", + "copy": "복사", + "paste": "붙여넣기", + "move_success": "{count}개의 항목이 이동되었어요", + "move_error": "이동에 실패했어요", + "paste_success": "붙여넣기 성공", + "paste_error": "붙여넣기에 실패했어요", + "new_text_file": "새 텍스트 파일", + "file_name": "파일 이름", + "retry": "재시도", + "refresh": "새로고침", + "duplicate": "복제", + "duplicate_success": "성공적으로 복제되었어요", + "duplicate_error": "복제에 실패했어요", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "파일이 만들어졌어요", + "create_file_error": "파일을 만들지 못했어요", + "favorites": "즐겨찾기", + "recent": "최근 파일", + "show_in_folder": "폴더에서 보기", + "remove_from_recent": "최근 파일에서 제거", + "upload_folder": "폴더 업로드", + "file_too_large": "\"{name}\" 파일이 최대 크기({max})를 초과해요", + "undo": "실행 취소", + "undo_success": "작업이 취소되었어요", + "undo_error": "작업을 취소하지 못했어요", + "toolbar": "파일 액션", + "file_list": "파일 및 폴더", + "context_menu": "액션", + "settings_ascending": "오름차순", + "settings_descending": "내림차순", + "disabled_title": "관리자가 파일 기능을 비활성화했어요", + "disabled_description": "WebDAV를 통한 대용량 파일 업로드는 Stalwart/RocksDB의 불안정을 일으킬 수 있어요. 메모리 부족 크래시나 복구 불가능한 디스크 사용 문제가 발생할 수 있으며, 삭제된 파일이 즉시 삭제되지 않을 수 있습니다. 운영 환경에서는 이 기능을 권장하지 않아요.", + "no_accounts": "연결된 계정이 없습니다.", + "open_folder_tree": "폴더 트리 열기", + "migration_title": "파일 업데이트 중…", + "migration_description": "폴더와 파일을 올바른 구조로 정리하고 있습니다. 이 작업은 한 번만 수행됩니다.", + "new": "새로 만들기", + "my_files": "내 파일", + "folders": "폴더", + "files_heading": "파일", + "open": "열기", + "star": "별표 표시", + "unstar": "별표 해제", + "expand_folder": "{name} 펼치기", + "collapse_folder": "{name} 접기", + "sidebar_hint": "폴더에 별표를 표시하거나 파일을 열면 여기에 나타나요.", + "breadcrumbs": "폴더 경로", + "show_path": "경로에서 숨겨진 폴더 표시", + "selection_actions": "선택한 항목 작업", + "selected_count": "{count, plural, other {#개 선택됨}}", + "clear_selection": "선택 해제", + "select_all": "모두 선택", + "select_item": "{name} 선택", + "more_actions": "{name} 추가 작업", + "paste_here": "이 폴더에 붙여넣기", + "paste_count": "{count, plural, other {#개 항목 붙여넣기}}", + "sort_by": "정렬 기준", + "view_mode": "레이아웃", + "clear_search": "검색 지우기", + "no_results_for": "이 폴더에 \"{query}\"와(과) 일치하는 항목이 없어요", + "download_count": "{count, plural, other {#개 파일 다운로드}}", + "delete_count": "{count, plural, other {#개 항목 삭제}}", + "close_details": "세부 정보 닫기", + "file_kind": "{ext} 파일", + "folder_summary": "{folders, plural, =0 {폴더 없음} other {폴더 #개}}, {files, plural, =0 {파일 없음} other {파일 #개}}", + "details_hint": "파일이나 폴더를 선택하면 세부 정보가 표시돼요.", + "sharing": "공유", + "shared_with_count": "{count, plural, other {#명과 공유됨}}", + "only_you": "나만", + "empty_folder_title": "이 폴더는 비어 있어요", + "empty_folder_description": "파일을 여기에 끌어다 놓거나 기기에서 업로드하세요.", + "empty_folder_read_only": "이 폴더에는 파일을 추가할 수 없어요.", + "choose_account": "계정 선택", + "drop_to_upload": "놓으면 {folder}에 업로드돼요", + "uploading_count": "{total}개 중 {current}개 업로드 중", + "dismiss": "닫기", + "zoom_in": "확대", + "zoom_out": "축소", + "zoom_reset": "화면에 맞추기", + "rotate": "회전", + "close_preview": "닫기" + }, + "smime": { + "your_certificates": "내 인증서", + "your_certificates_desc": "이메일 서명과 암호화를 위한 S/MIME 인증서를 가져오고 관리해요", + "recipient_certificates": "수신자 인증서", + "recipient_certificates_desc": "상대방에게 암호화된 메일을 보낼 때 필요한 공개 인증서예요", + "identity_bindings": "주소-키 연결", + "identity_bindings_desc": "내 이메일 주소와 S/MIME 인증서를 연결해 주세요", + "defaults_title": "기본 설정", + "defaults_desc": "기본 서명 및 암호화 동작을 설정해요", + "import_pkcs12": "PKCS#12 (.p12/.pfx) 가져오기", + "import_public_cert": "인증서 가져오기", + "no_certificates": "아직 가져온 인증서가 없어요", + "no_recipient_certs": "수신자 인증서가 없어요", + "expires": "만료일", + "expired": "만료됨", + "bound_to": "연결된 주소", + "no_key_bound": "없음", + "lock": "키 잠금", + "unlock": "키 잠금 해제", + "details": "자세히 보기", + "delete": "삭제", + "encrypt_by_default": "기본으로 암호화", + "encrypt_by_default_desc": "모든 수신자의 인증서가 있다면 자동으로 메일을 암호화해요", + "remember_unlocked": "잠금 해제 상태 유지", + "remember_unlocked_desc": "현재 브라우저 세션 동안 키의 잠금 해제 상태를 유지해요", + "sign_default_for": "기본으로 서명할 주소", + "enter_p12_passphrase": "PKCS#12 비밀번호 입력", + "p12_passphrase_desc": "이 인증서 파일을 보호하고 있는 비밀번호를 입력해 주세요", + "enter_storage_passphrase": "저장소 비밀번호 설정", + "storage_passphrase_desc": "브라우저에 저장될 키를 보호할 새로운 비밀번호를 설정해 주세요", + "next": "다음", + "import": "가져오기", + "unlock_key": "키 잠금 해제", + "unlock_key_desc": "서명하거나 암호를 풀기 위해 저장소 비밀번호를 입력해 주세요", + "passphrase_placeholder": "비밀번호 입력", + "confirm_passphrase_placeholder": "비밀번호 확인", + "passphrase_mismatch": "비밀번호가 서로 일치하지 않아요", + "cancel": "취소", + "processing": "처리 중…", + "close": "닫기", + "certificate_details": "인증서 세부 정보", + "cert_subject": "대상 (Subject)", + "cert_issuer": "발급자 (Issuer)", + "cert_email": "이메일", + "cert_serial": "일련번호", + "cert_validity": "유효 기간", + "cert_fingerprint": "지문 (SHA-256)", + "cert_algorithm": "알고리즘", + "cert_capabilities": "기능", + "cert_source": "출처", + "cert_expired": "이 인증서는 만료되었어요", + "cert_not_yet_valid": "이 인증서는 아직 유효하지 않아요", + "cap_sign": "서명", + "cap_encrypt": "암호화", + "cap_none": "없음", + "show_passphrase": "비밀번호 표시", + "hide_passphrase": "비밀번호 숨기기", + "sign_toggle": "서명", + "encrypt_toggle": "암호화", + "missing_recipient_certs": "다음 수신자의 인증서가 없어요: {emails}", + "missing_sender_cert": "이 이메일 주소에 연결된 인증서가 없어요", + "status_encrypted_ok": "암호화된 메시지예요", + "status_encrypted_no_key": "암호화된 메시지지만 일치하는 키를 찾지 못했어요", + "status_encrypted_failed": "이 메시지의 암호를 풀지 못했어요", + "status_signed_valid": "서명이 확인되었어요", + "status_signed_invalid": "서명 확인에 실패했어요", + "status_signed_expired_cert": "만료된 인증서로 서명되었어요", + "status_signed_self_signed": "서명은 유효하지만 인증서가 자체 서명되어 신뢰할 수 없어요", + "status_signed_mismatch": "서명은 유효하지만 서명자가 보낸 사람과 일치하지 않아요", + "status_unsupported": "지원하지 않는 S/MIME 형식이에요", + "auto_import_signer_certs": "발신자 인증서 자동 저장", + "auto_import_signer_certs_desc": "향후 암호화 메일을 보낼 수 있도록, 검증된 서명 메일에서 인증서를 자동으로 저장해요", + "export": "내보내기", + "enter_export_passphrase": "내보내기 비밀번호 설정", + "export_passphrase_desc": "내보낼 PKCS#12 파일을 보호할 비밀번호를 설정해 주세요", + "export_storage_desc": "내보내기를 위해 키를 해독해야 해요. 저장소 비밀번호를 입력해 주세요", + "incorrect_passphrase": "비밀번호가 올바르지 않아요" + }, + "tour": { + "step_counter": "총 {total}단계 중 {current}단계", + "skip": "투어 건너뛰기", + "back": "이전", + "next": "다음", + "finish": "완료", + "take_a_tour": "화면을 함께 둘러볼까요?", + "restart_title": "소개 투어", + "restart_desc": "인터페이스를 설명해 주는 투어를 다시 시작해요", + "restart_button": "투어 다시 시작", + "show_on_new_devices_title": "새 기기에서 표시", + "show_on_new_devices_desc": "다른 곳에서 이미 완료했더라도 새 기기에 처음 로그인할 때 환영 배너와 투어를 다시 표시해요", + "sidebar_title": "편지함", + "sidebar_desc": "여기는 폴더 사이드바예요. 폴더를 클릭하면 그 안의 메일을 볼 수 있어요. 폴더를 만들거나, 메일을 드래그해서 옮길 수 있고 안 읽은 메일 개수도 한눈에 확인돼요.", + "compose_title": "메일 쓰기", + "compose_desc": "여기를 클릭하면 새 메일을 쓸 수 있어요. 받는 사람을 추가하고, 파일을 첨부하거나 서식을 마음껏 꾸며보세요.", + "search_title": "메일 검색", + "search_desc": "보낸 사람, 제목, 내용으로 메일을 검색해 보세요. 필터 아이콘을 누르면 날짜, 첨부파일, 별표 여부 등 더 자세한 조건으로 찾을 수 있어요.", + "email_list_title": "메일 목록", + "email_list_desc": "메일들이 여기에 표시돼요. 하나를 클릭하면 오른쪽에 내용이 열립니다. 체크박스로 여러 개를 선택해서 한 번에 지우거나 태그를 달 수도 있어요.", + "email_viewer_title": "읽기 창", + "email_viewer_desc": "선택한 메일이 여기서 열려요. 상단 버튼으로 바로 답장, 전달, 보관, 삭제할 수 있고 별표나 색상 태그를 달아 예쁘게 정리할 수 있어요.", + "keywords_title": "색상 태그", + "keywords_desc": "색상 태그로 메일을 분류해 보세요. 메일을 끌어다 태그 위에 놓거나, 메일을 우클릭해서 태그를 지정할 수 있답니다.", + "calendar_title": "캘린더", + "calendar_desc": "일정을 관리하려면 캘린더로 이동해 보세요. 일정을 만들고, 알림을 설정하며, 일간/주간/월간 뷰로 깔끔하게 확인할 수 있어요.", + "contacts_title": "연락처", + "contacts_desc": "내 주소록이 있는 곳이에요. 연락처를 가져오거나 그룹을 만들 수 있고, 연락처를 클릭하면 세부 정보를 모두 볼 수 있어요.", + "settings_title": "설정", + "settings_desc": "모든 걸 내 취향에 맞게 바꿔보세요! 테마, 밀도, 서명, 필터 규칙, 단축키, 캘린더 기본값까지 입맛대로 설정할 수 있어요.", + "shortcuts_title": "키보드 단축키", + "shortcuts_desc": "파워 유저라면 필수죠! 언제든 ? 키를 누르면 단축키 목록이 나와요. 마우스 없이도 이동하고, 메일을 쓰고 관리할 수 있어요.", + "compose_open_title": "메일 쓰기 창", + "compose_open_desc": "메일 작성기예요. 수신자를 넣고 내용을 쓰며, 파일을 첨부하거나 서식을 적용할 수 있어요. 임시보관이나 템플릿 기능도 알차게 준비되어 있어요.", + "calendar_view_title": "내 캘린더", + "calendar_view_desc": "샘플 일정이 들어있는 캘린더 화면이에요. 위쪽 도구 모음에서 일간, 주간, 월간, 목록 뷰로 자유롭게 전환해 보세요.", + "create_event_title": "일정 만들기", + "create_event_desc": "이 버튼을 눌러 새 일정을 만들어 보세요. 제목과 날짜, 시간을 정하고 함께할 사람을 초대할 수 있어요.", + "event_modal_title": "일정 세부 정보", + "event_modal_desc": "일정 입력 폼이에요. 제목, 날짜, 시간, 위치를 채우고 참석자를 추가해 보세요. 다 썼다면 저장을 누르면 돼요!", + "contacts_list_title": "내 연락처", + "contacts_list_desc": "등록된 연락처들이에요. 클릭하면 오른쪽에 자세한 내용이 나와요. 새 연락처를 추가하거나 vCard로 가져오고, 그룹으로 예쁘게 묶어보세요.", + "settings_tabs_title": "설정 메뉴", + "settings_tabs_desc": "설정 카테고리들이 모여있어요. 화면 테마를 바꾸고, 보내는 사람 주소를 관리하고, 자동 필터를 만들며, 캘린더 설정 등 아주 많은 걸 할 수 있어요.", + "files_title": "파일 저장소", + "files_desc": "파일 탐색기를 통해 파일을 업로드하고, 폴더로 정리하며 공유할 수 있어요. 메일함 안의 작은 개인 클라우드 드라이브라고 생각하시면 돼요.", + "demo_banner_title": "데모 컨트롤러", + "demo_banner_desc": "현재 데모 모드예요. 모든 작업은 브라우저 안에서만 이뤄집니다. 언제든 '초기화'를 누르면 처음의 깨끗한 샘플 데이터로 돌아가요.", + "quota_title": "저장 공간 사용량", + "quota_desc": "편지함 용량을 여기서 확인하세요. 공간을 많이 쓸수록 원이 점점 채워질 거예요." + }, + "unified_mailbox": { + "search_unavailable": "통합 보기에서는 검색을 사용할 수 없습니다" + }, + "calendar_event_notifications": { + "someone": "누군가", + "invited": "{name}님이 \"{title}\"에 초대했습니다", + "updated": "{name}님이 \"{title}\"을(를) 업데이트했습니다", + "cancelled": "{name}님이 \"{title}\"을(를) 취소했습니다", + "untitled": "제목 없는 일정" + }, + "share_notifications": { + "someone": "누군가", + "shared": "{name}님이 {kind} \"{object}\"을(를) 공유했습니다", + "changed": "{name}님이 {kind} \"{object}\"에 대한 접근 권한을 변경했습니다", + "revoked": "{name}님이 {kind} \"{object}\"에 대한 접근 권한을 제거했습니다", + "object": { + "folder": "폴더", + "calendar": "캘린더", + "address_book": "주소록", + "files": "파일 폴더" + } + }, + "sharing": { + "title": "\"{name}\" 공유", + "description": "이 서버의 다른 사용자나 그룹에 액세스 권한을 부여합니다. 변경 사항은 즉시 적용됩니다.", + "no_shares": "아직 공유되지 않았습니다.", + "add_person": "사용자 또는 그룹 추가", + "search_placeholder": "이름 또는 이메일로 검색…", + "loading_principals": "사용자 불러오는 중…", + "no_principals": "다른 사용자나 그룹을 찾을 수 없습니다.", + "no_match": "일치하는 항목이 없습니다.", + "remove": "액세스 권한 제거", + "group": "그룹", + "share_added": "액세스 권한이 부여되었습니다", + "share_updated": "액세스 권한이 업데이트되었습니다", + "share_removed": "액세스 권한이 제거되었습니다", + "share_failed": "공유 업데이트에 실패했습니다", + "preset": { + "freeBusy": "한가함/바쁨만", + "read": "읽기 전용", + "readWrite": "읽기 및 쓰기", + "manager": "관리자", + "custom": "사용자 지정" + } + }, + "quote_header": { + "reply_line": "{date}에 {from}님이 작성:", + "forwarded_separator": "---------- 전달된 메시지 ----------", + "from_label": "보낸 사람", + "date_label": "날짜", + "subject_label": "제목" + }, + "pwa_install": { + "title": "{appName} 설치", + "description": "빠른 액세스와 오프라인 지원을 위해 앱을 설치하세요.", + "not_now": "나중에", + "install": "설치", + "dont_remind": "다시 알리지 않음", + "dismiss_aria": "설치 프롬프트 닫기" + }, + "deep_link": { + "message_not_found": "이 메시지를 더 이상 사용할 수 없습니다.", + "thread_not_found": "이 대화를 더 이상 사용할 수 없습니다.", + "folder_not_found": "이 폴더를 더 이상 사용할 수 없습니다.", + "event_not_found": "이 일정을 더 이상 사용할 수 없습니다.", + "file_not_found": "이 파일을 더 이상 사용할 수 없습니다.", + "account_unavailable": "이 링크를 열려면 해당 계정으로 로그인하세요.", + "copy_message": "메시지 링크 복사", + "copy_conversation": "대화 링크 복사", + "copy_event": "일정 링크 복사", + "copied": "링크가 복사되었습니다", + "copy_failed": "링크를 복사하지 못했습니다" + }, + "plugins": { + "oauth_callback": { + "completing": "인증 완료 중…", + "completing_message": "연결이 설정되는 동안 잠시 기다려 주세요.", + "received_title": "인증 수신됨", + "received_message": "연결을 시작했던 탭으로 돌아갈 수 있습니다.", + "failed_title": "인증 실패", + "denied": "인증이 거부되었습니다.", + "failed_detail": "인증 실패: {error}", + "missing_params": "인증 코드 또는 상태가 누락되었습니다.", + "delivery_failed": "인증 결과를 앱 탭에 전달하지 못했습니다. 이 창을 닫고 다시 시도해 주세요." + }, + "settings": { + "title": "플러그인", + "description": "관리자가 추가한 플러그인이에요. 내 계정에서 켜거나 끌 수 있어요.", + "description_uploads": "플러그인으로 웹메일에 기능을 더할 수 있어요. 내 계정에서 켜거나 끄고, 직접 설치할 수도 있어요.", + "description_demo": "데모에서는 플러그인을 사용할 수 없어요.", + "install_button": "플러그인 설치", + "install_hint": "플러그인 ZIP 파일을 선택하세요.", + "install_hint_approval": "플러그인 ZIP 파일을 선택하세요. 켜기 전에 관리자의 승인이 필요해요.", + "install_uploads_off": "관리자가 플러그인 설치를 허용하지 않아요.", + "install_managed_id": "관리자가 같은 이름의 플러그인을 이미 제공하고 있어 바꿀 수 없어요.", + "install_invalid": "이 파일은 설치할 수 있는 플러그인이 아니에요: {details}", + "toast_installed": "플러그인을 설치했어요. 아래에서 켜세요.", + "toast_installed_pending": "플러그인을 설치하고 관리자에게 승인을 요청했어요.", + "toast_enabled": "{name} 켜짐", + "toast_disabled": "{name} 꺼짐", + "toast_removed": "{name}을(를) 삭제했어요", + "toast_forced": "관리자가 {name}을(를) 항상 켜 두도록 설정했어요", + "toast_approval_requested": "{name}이(가) 관리자의 승인을 기다리고 있어요", + "empty_title": "아직 플러그인이 없어요", + "empty_admin": "관리자가 추가한 플러그인이 없어요.", + "empty_uploads": "ZIP 파일에서 플러그인을 설치해 시작해 보세요.", + "empty_demo": "플러그인은 실제 계정이 필요해서 데모에는 없어요.", + "syncing": "서버에서 플러그인을 확인하는 중…", + "status_installed": "꺼짐", + "status_enabled": "시작 중", + "status_running": "켜짐", + "status_disabled": "꺼짐", + "status_error": "문제 있음", + "badge_forced": "필수", + "badge_managed": "관리자 제공", + "badge_yours": "내가 설치함", + "badge_awaiting": "승인 대기 중", + "toggle_label": "{name} 켜기 또는 끄기", + "forced_note": "관리자가 이 플러그인을 필수로 지정해서 끌 수 없어요.", + "permissions": "할 수 있는 작업", + "permissions_managed": "할 수 있는 작업 (관리자가 승인함)", + "settings": "설정", + "remove": "삭제", + "remove_title": "{name}을(를) 삭제할까요?", + "remove_message": "플러그인과 플러그인이 이 브라우저에 저장한 모든 데이터가 삭제돼요. 나중에 다시 설치할 수 있어요.", + "remove_confirm": "삭제", + "error_user_plugins_off": "관리자가 사용자가 설치한 플러그인을 꺼 두어서 이 플러그인은 실행되지 않아요.", + "error_approval_unavailable": "이 플러그인은 승인이 필요해지기 전에 설치되었어요. 승인을 요청하려면 삭제한 뒤 다시 설치하세요.", + "error_approval_denied": "관리자가 이 플러그인을 승인하지 않았어요.", + "error_approval_pending": "관리자가 이 플러그인을 승인하기를 기다리고 있어요.", + "error_auto_disabled": "계속 실패해서 꺼졌습니다. 다시 시도하려면 다시 켜세요.", + "accounts_heading": "연결된 계정", + "account_on": "{host}의 {label}", + "account_waiting": "{host}의 {label} (로그인 미완료)", + "disconnect": "연결 해제", + "disconnect_failed": "{label} 연결을 해제하지 못했어요" + }, + "consent": { + "title": "{name}에 이 권한을 허용할까요?", + "intro": "플러그인이 아래 권한을 요청해요. 허용한 경우에만 권한이 주어져요.", + "high_risk_note": "표시된 항목은 플러그인이 메일 내용에 접근하거나 데이터를 외부로 보낼 수 있게 해요. 신뢰하는 플러그인에만 허용하세요.", + "revoke_hint": "언제든지 설정에서 플러그인을 끄거나 삭제할 수 있어요.", + "deny": "허용 안 함", + "allow": "허용" + }, + "permissions": { + "none": "특별한 권한이 필요하지 않아요.", + "high_risk": "주의 필요", + "email_read": { + "title": "메일 읽기", + "body": "계정에 있는 모든 메시지의 보낸 사람, 받는 사람, 제목, 전체 본문과 폴더를 볼 수 있어요." + }, + "email_write": { + "title": "메일 변경 및 정리", + "body": "메시지에 태그, 플래그 및 기타 키워드를 추가하거나 제거하고, 메시지를 폴더 간에 또는 보관함으로 이동하고, 임시보관함에 메일을 저장할 수 있어요." + }, + "email_send": { + "title": "보내기 전에 메일 변경", + "body": "작성 중인 메시지의 받는 사람, 제목, 본문, 첨부 파일을 읽고 바꾸거나, 메시지가 전송되지 않게 막을 수 있어요." + }, + "crypto_full": { + "title": "이 앱에 대한 전체 접근 권한", + "body": "웹메일과 같은 권한으로 실행돼요. 모든 메일과 저장된 키를 읽고 사용자를 대신해 작업할 수 있어요. 완전히 신뢰하는 플러그인에만 허용하세요." + }, + "email_raw_send": { + "title": "직접 만든 메시지 보내기", + "body": "서명되거나 암호화된 메시지 같은 완성된 메시지를 내 계정에서 보낼 수 있어요." + }, + "email_blob_read": { + "title": "메시지 원본 읽기", + "body": "메시지와 첨부 파일의 원본 데이터, 그리고 보내기 전에 첨부하는 모든 파일을 읽을 수 있어요." + }, + "email_blob_write": { + "title": "보내기 전에 첨부 파일 바꾸기", + "body": "메일 서버에 파일을 업로드하고, 메시지를 보내기 전에 첨부한 파일을 다른 파일로 바꿀 수 있어요." + }, + "email_render_takeover": { + "title": "메시지 표시 방식 변경", + "body": "열어 본 메시지와 메시지 목록의 본문을 바꿀 수 있어요. 예를 들어 복호화된 내용을 보여 줄 때 사용해요." + }, + "calendar_read": { + "title": "캘린더 읽기", + "body": "캘린더와 일정을 볼 수 있어요." + }, + "calendar_write": { + "title": "캘린더 변경", + "body": "일정을 만들고, 바꾸고, 삭제할 수 있어요." + }, + "contacts_read": { + "title": "연락처 읽기", + "body": "주소록을 검색하고 읽을 수 있어요." + }, + "contacts_write": { + "title": "연락처 변경", + "body": "연락처와 주소록을 만들고, 바꾸고, 삭제할 수 있어요." + }, + "files_read": { + "title": "파일 읽기", + "body": "계정에 저장된 파일을 둘러보고 열 수 있어요." + }, + "files_write": { + "title": "파일 변경", + "body": "계정의 파일을 업로드하고, 이름을 바꾸고, 이동하고, 삭제할 수 있어요." + }, + "identity_read": { + "title": "보내는 사람 주소 보기", + "body": "메일을 보낼 때 쓰는 주소와 서명을 볼 수 있어요." + }, + "identity_write": { + "title": "보내는 사람 주소 변경", + "body": "보내는 사람 주소와 서명을 만들고, 바꾸고, 삭제할 수 있어요." + }, + "filters_read": { + "title": "메일 필터 읽기", + "body": "받은 메일을 분류하는 규칙을 볼 수 있어요." + }, + "filters_write": { + "title": "메일 필터 변경", + "body": "서버 측 필터에 규칙을 추가할 수 있어요. 이런 규칙은 받은 메일을 이동, 전달하거나 삭제할 수 있어요." + }, + "tasks_read": { + "title": "할 일 읽기", + "body": "할 일 목록과 할 일을 볼 수 있어요." + }, + "tasks_write": { + "title": "할 일 변경", + "body": "할 일을 만들고, 바꾸고, 완료하고, 삭제할 수 있어요." + }, + "templates_read": { + "title": "템플릿 읽기", + "body": "저장된 메시지 템플릿을 볼 수 있어요." + }, + "templates_write": { + "title": "템플릿 변경", + "body": "메시지 템플릿을 만들고, 바꾸고, 삭제할 수 있어요." + }, + "smime_read": { + "title": "S/MIME 상태 보기", + "body": "S/MIME 키와 인증서가 있는지 볼 수 있어요. 키에는 접근할 수 없어요." + }, + "vacation_read": { + "title": "부재중 자동 응답 보기", + "body": "자동 응답이 켜져 있는지와 그 내용을 볼 수 있어요." + }, + "vacation_write": { + "title": "부재중 자동 응답 변경", + "body": "자동 응답을 켜거나 끄고, 내용과 날짜를 바꿀 수 있어요." + }, + "settings_read": { + "title": "설정 읽기", + "body": "환경설정과 태그 정의를 볼 수 있어요." + }, + "settings_write": { + "title": "설정 변경", + "body": "태그를 추가하고 순서를 바꾸며, 환경설정을 변경할 수 있어요." + }, + "security_read": { + "title": "계정 보안 보기", + "body": "2단계 인증과 암호화가 켜져 있는지 볼 수 있어요. 비밀 정보에는 접근할 수 없어요." + }, + "auth_observe": { + "title": "로그인 알림 받기", + "body": "로그인하거나, 계정을 전환하거나, 세션이 갱신될 때 알림을 받아요." + }, + "auth_emit": { + "title": "로그아웃시키기", + "body": "웹메일에서 로그아웃시킬 수 있어요." + }, + "account_read": { + "title": "계정 보기", + "body": "추가한 계정과 그 주소 및 서버를 볼 수 있어요." + }, + "http_post": { + "title": "나 대신 웹메일 서버 사용", + "body": "내 로그인 정보로 다음 서버 엔드포인트를 호출할 수 있어요:" + }, + "http_fetch": { + "title": "다른 웹사이트에 연결", + "body": "다음 사이트에 요청을 보낼 수 있어요. 플러그인이 읽을 수 있는 모든 내용이 그곳으로 전송될 수 있어요:" + }, + "admin_config": { + "title": "관리자가 저장한 설정 읽기", + "body": "관리자가 이 플러그인용으로 저장한 설정을 읽을 수 있어요." + }, + "ui_observe": { + "title": "앱에서 현재 위치 파악", + "body": "어떤 페이지가 열려 있는지 알고, 클릭한 링크에 반응할 수 있어요." + }, + "ui_toolbar": { + "title": "도구 모음에 버튼 추가", + "body": "메시지 도구 모음, 열어 본 메시지, 상단 바에 자체 버튼을 표시해요." + }, + "ui_app_top_banner": { + "title": "상단에 배너 표시", + "body": "앱 상단에 안내를 표시해요." + }, + "ui_email_banner": { + "title": "메시지에 안내 표시", + "body": "열어 본 메시지나 대화의 각 부분 위, 안, 사이에 자체 안내와 요약을 표시해요." + }, + "ui_email_footer": { + "title": "메시지 아래에 내용 추가", + "body": "열어 본 메시지 아래에 자체 내용을 표시해요." + }, + "ui_contact_cryptokeys": { + "title": "연락처의 암호화 키 표시", + "body": "연락처의 암호화 키를 표시하고 관리해요." + }, + "ui_email_details": { + "title": "메시지 세부 정보에 패널 추가", + "body": "열어 본 메시지 옆이나 아래에 자체 패널을 표시해요." + }, + "ui_download_file": { + "title": "기기에 파일 저장", + "body": "직접 만든 파일을 다운로드할 수 있게 제공해요." + }, + "ui_message_list_tabs": { + "title": "받은편지함에 탭 추가", + "body": "받은편지함을 업데이트나 프로모션 같은 탭으로 나눠요." + }, + "ui_composer_toolbar": { + "title": "작성 창에 추가", + "body": "메시지를 작성하는 동안 자체 버튼, 메뉴 항목, 섹션을 표시해요." + }, + "ui_composer_sidebar": { + "title": "작성 창에 패널 추가", + "body": "작성 중인 메시지 옆에 자체 패널을 표시해요." + }, + "ui_sidebar_widget": { + "title": "사이드바에 추가", + "body": "메일 사이드바에 자체 상자와 섹션을 표시해요." + }, + "ui_settings_section": { + "title": "설정 페이지 추가", + "body": "설정에 자체 페이지를 표시해요." + }, + "ui_context_menu": { + "title": "메시지 메뉴에 추가", + "body": "메시지를 마우스 오른쪽 버튼으로 클릭하면 열리는 메뉴에 항목을 추가해요." + }, + "ui_navigation_rail": { + "title": "내비게이션 바에 추가", + "body": "내비게이션 바에 자체 버튼을 표시해요." + }, + "ui_keyboard": { + "title": "단축키 추가", + "body": "직접 정의한 단축키에 반응해요." + }, + "ui_calendar_action": { + "title": "캘린더 화면에 추가", + "body": "일정에 자체 버튼과 섹션을, 캘린더 날짜에 짧은 라벨과 점을 표시해요." + }, + "ui_admin_page": { + "title": "관리자 페이지 추가", + "body": "관리자 설정에 자체 페이지를 표시해요." + }, + "app_lifecycle": { + "title": "앱 시작과 종료 알림 받기", + "body": "앱이 시작되거나, 오프라인이 되거나, 로그아웃할 때 알림을 받아요." + }, + "compose_open": { + "title": "작성 창 열기", + "body": "받는 사람, 본문, 첨부 파일이 채워진 새 메시지를 열어요. 보낼지는 직접 결정해요." + }, + "compose_read": { + "title": "작성 중인 메시지 읽기", + "body": "편집기에서 플러그인을 사용할 때 작성 중인 메시지의 본문을 읽어요. 서명과 인용된 메시지는 읽지 않아요." + }, + "compose_write": { + "title": "작성 중인 메시지 변경", + "body": "편집기에서 플러그인을 사용할 때 작성 중인 메시지의 텍스트를 바꾸거나 추가해요. 각 변경은 되돌릴 수 있고, 보낼지는 여전히 직접 결정해요." + }, + "ui_notifications": { + "title": "알림 표시", + "body": "기기에 알림을 표시하고, 알림을 허용하지 않았다면 앱 안에 표시해요. 모든 알림에 플러그인 이름이 나와요." + }, + "ui_message_list": { + "title": "메시지 목록에 추가", + "body": "메시지 목록 위에 자체 내용을 표시하고, 메시지와 선택한 메시지의 도구 모음에 버튼을 표시해요." + }, + "ui_contacts": { + "title": "연락처 화면에 추가", + "body": "연락처 세부 정보에 자체 섹션을, 연락처 목록에 버튼을 표시해요." + }, + "ui_tasks": { + "title": "할 일 화면에 추가", + "body": "할 일 세부 정보에 자체 섹션을, 할 일 목록에 버튼을 표시해요." + }, + "ui_files": { + "title": "파일 화면에 추가", + "body": "파일 미리보기에 자체 섹션을, 파일 메뉴에 항목을 표시해요." + }, + "ui_commands": { + "title": "명령 추가", + "body": "명령 팔레트(Ctrl+K)에 자체 항목을 추가해요." + }, + "ui_app_page": { + "title": "페이지 추가", + "body": "앱에 자체 페이지를 추가하고, 각 페이지의 버튼을 내비게이션 바에 표시해요." + }, + "http_proxy": { + "title": "연결한 계정 사용", + "body": "나 또는 관리자가 설정한 계정이나 키로 Bulwark 서버를 거쳐 요청을 보낼 수 있어요. 플러그인은 비밀번호를 볼 수 없지만, 해당 계정이 그 서버에서 할 수 있는 모든 작업을 할 수 있어요." + } + }, + "dialog": { + "from_plugin": "플러그인 {name}에서", + "ok": "확인", + "submit": "제출", + "confirm": "확인", + "cancel": "취소", + "close": "닫기", + "turn_off": "{name} 끄기", + "plugin_menu": "{name} 옵션", + "password_note": "이 양식은 웹메일이 아니라 플러그인 {name}에서 표시한 것입니다. 이 플러그인을 신뢰할 때만 비밀번호를 입력하세요.", + "toast_auto_disabled": "{name}이(가) 계속 실패해서 꺼졌습니다." + }, + "connect": { + "title": "{label} 연결", + "intro_password": "{name}이(가) {label} 계정을 사용하려고 해요. Bulwark는 비밀번호를 자체 서버에 보관하고 {host}에만 보내요. 플러그인은 비밀번호를 볼 수 없어요.", + "intro_password_any": "{name}이(가) {label} 계정을 사용하려고 해요. Bulwark는 비밀번호를 자체 서버에 보관하고 여기에 입력한 서버에만 보내요. 플러그인은 비밀번호를 볼 수 없어요.", + "intro_signin": "{name}이(가) {label} 계정에 로그인하려고 해요. 여기에 서버를 입력한 다음 해당 서버의 페이지에서 로그인하세요. Bulwark는 받은 접근 권한을 자체 서버에 보관하며, 플러그인은 이를 볼 수 없어요.", + "server": "서버 주소", + "server_hint": "cloud.example.com", + "username": "사용자 이름", + "secret": "비밀번호 또는 키", + "connect": "연결", + "continue": "계속", + "cancel": "취소", + "saving": "저장하는 중…", + "error_server": "서버 주소를 입력하세요. 예: https://cloud.example.com", + "error_save": "저장하지 못했어요: {error}" + } + }, + "global_search": { + "title": "전체 검색", + "placeholder": "메일, 연락처, 캘린더, 파일 검색...", + "scope_label": "검색 범위", + "account_label": "계정", + "scope_all": "전체", + "scope_mail": "메일", + "scope_contacts": "연락처", + "scope_calendar": "캘린더", + "scope_files": "파일", + "all_accounts": "모든 계정", + "show_all": "모든 결과 보기", + "no_results": "“{query}”에 대한 결과가 없습니다", + "server_hint": "완전한 단어는 서버에서 검색되며, 단어 일부는 이미 불러온 항목과 일치합니다", + "searching": "검색 중...", + "account_error": "{account}을(를) 검색할 수 없습니다", + "recent_searches": "최근 검색", + "recurring": "반복", + "no_subject": "(no subject)", + "empty_hint": "모든 계정의 메일, 연락처, 일정, 파일을 검색하세요. from:, subject:, has:attachment, after:2026-01-01 도 사용할 수 있습니다.", + "flat_by_date": "날짜순 평뱀 보기", + "grouped": "그룹 보기", + "load_more": "더 불러오기", + "preview_empty": "미리 볼 결과를 선택하세요", + "open_full": "열기", + "view_label": "보기" + }, + "top_bar": { + "more": "추가 옵션" + }, + "plugin_pages": { + "loading": "페이지를 불러오는 중", + "not_available_title": "이 페이지를 사용할 수 없습니다", + "not_available_description": "이 페이지를 추가한 플러그인이 꺼져 있거나 더 이상 설치되어 있지 않습니다.", + "back_to_mail": "메일로 돌아가기", + "settings_group": "플러그인" + }, + "command_palette": { + "title": "명령 팔레트", + "open": "명령 팔레트 열기", + "placeholder": "명령을 입력하세요", + "no_results": "일치하는 명령이 없습니다", + "go_to": "{name}(으)로 이동", + "compose": "새 메일", + "search": "메일 검색", + "theme_dark": "어두운 테마로 전환", + "theme_light": "밝은 테마로 전환", + "shortcuts": "단축키 보기", + "group_navigation": "이동", + "group_actions": "작업", + "group_plugins": "플러그인" + }, + "notification_center": { + "title": "알림", + "mark_all_read": "모두 읽음으로 표시", + "clear": "지우기", + "empty": "알림이 없습니다", + "close": "닫기", + "unread": "읽지 않음", + "unread_count": "읽지 않음 {count}개", + "level_error": "오류", + "level_warning": "경고" + } +} diff --git a/locales/lv/common.json b/locales/lv/common.json index 7ffb248f..82e05f54 100644 --- a/locales/lv/common.json +++ b/locales/lv/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimālistisks tīmekļa pasta klients, kas izmanto JMAP protokolu", - "login": { - "title": "Tīmekļa pasts", - "username_label": "E-pasta adrese", - "username_placeholder": "lietotajs@piemers.lv", - "password_label": "Parole", - "password_placeholder": "Ievadiet paroli", - "jmap_endpoint_label": "JMAP serveris", - "jmap_endpoint_placeholder": "https://pasts.piemers.lv", - "jmap_endpoint_cors_hint": "Serverim ir jāatļauj CORS pieprasījumi no šī domēna.", - "jmap_server_label": "Serveris", - "jmap_server_auto_picked": "Serveris izvēlēts no jūsu e-pasta domēna.", - "sign_in": "Pierakstīties", - "signing_in": "Notiek pierakstīšanās...", - "loading": "Ielāde...", - "reconnecting": "Savienojums pārtraukts. Mēģina izveidot savienojumu...", - "error": { - "invalid_credentials": "Nepareiza e-pasta adrese vai parole. Pārbaudiet datus un mēģiniet vēlreiz.", - "connection_failed": "Neizdevās izveidot savienojumu ar serveri. Pārbaudiet interneta pieslēgumu.", - "cors_blocked": "Serveris ir pieejams, bet bloķē pieprasījumus no citiem avotiem. Pārbaudiet JMAP servera CORS iestatījumus.", - "server_error": "Serveris īslaicīgi nav pieejams. Mēģiniet vēlāk.", - "generic": "Notikusi neparedzēta kļūda. Ja tā atkārtojas, sazinieties ar administratoru.", - "totp_invalid": "Nepareizs autentifikācijas kods. Pārbaudiet lietotni un mēģiniet vēlreiz.", - "oauth_discovery_failed": "SSO ir iespējots, bet autentifikācijas pakalpojums nav pieejams. Pārbaudiet OAuth iestatījumus.", - "totp_required": "Nepieciešams divu faktoru autentifikācijas kods. Lūdzu, ievadiet kodu zemāk." - }, - "show_password": "Rādīt paroli", - "hide_password": "Slēpt paroli", - "totp_toggle": "Man ir 2FA kods", - "remember_me": "Atcerēties mani", - "config_error": { - "title": "Konfigurācijas kļūda", - "fetch_failed": "Neizdevās ielādēt lietotnes konfigurāciju. Mēģiniet vēlāk.", - "server_not_configured": "Pasta serveris nav konfigurēts. Sazinieties ar administratoru." - }, - "remove_from_history": "Noņemt no vēstures", - "totp_label": "Autentifikācijas kods", - "totp_placeholder": "000000", - "session_expired": "Sesija ir beigusies. Lūdzu, pierakstieties vēlreiz.", - "dismiss": "Aizvērt", - "or": "vai", - "sign_in_sso": "Pierakstīties ar SSO", - "add_account_title": "Pievienot kontu", - "add_account_subtitle": "Pierakstīties citā kontā", - "cancel": "Atcelt", - "website": "Tīmekļa vietne", - "imprint": "Rekvizīti", - "privacy_policy": "Privātuma politika", - "try_demo": "Izmēģināt demo", - "demo_description": "Izpētiet ar paraugdatiem - konts nav nepieciešams", - "demo_launching": "Demo palaišana...", - "demo_login_button": "Palaist demo", - "demo_tagline": "Izmēģiniet pilnvērtīgu e-pasta klientu bez konta izveides.", - "demo_no_signup": "Reģistrācija nav nepieciešama - brīvi pētiet paraugdatus", - "oauth_completing": "Pabeidz pierakstīšanos...", - "oauth_error": { - "title": "Autentifikācijas kļūda", - "invalid_state": "Drošības pārbaudes kļūda. Mēģiniet pierakstīties vēlreiz.", - "missing_params": "Trūkst autorizācijas datu. Mēģiniet pierakstīties vēlreiz.", - "token_exchange_failed": "Neizdevās pabeigt autentifikāciju. Mēģiniet vēlreiz.", - "access_denied": "Piekļuve liegta. Sazinieties ar administratoru.", - "back_to_login": "Atgriezties pie pierakstīšanās" - } - }, - "sidebar": { - "close": "Aizvērt", - "compose": "Rakstīt", - "compose_hint": "Rakstīt (c)", - "search_placeholder": "Meklēt pastā...", - "search_placeholder_hint": "Meklēt pastā... (nospiediet /)", - "storage": "Krātuve", - "storage_used": "Izmantots", - "storage_free": "Brīvs", - "storage_total": "Kopā", - "sign_out": "Izrakstīties", - "sign_out_of": "Izrakstīties no {account}", - "sign_out_all": "Izrakstīties no visiem kontiem", - "add_account": "Pievienot kontu", - "set_as_default": "Iestatīt kā noklusējumu", - "switch_account": "Mainīt kontu", - "contacts": "Kontakti", - "calendar": "Kalendārs", - "settings": "Iestatījumi", - "admin": "Admin", - "files": "Faili", - "loading_mailboxes": "Ielādē mapes...", - "push_connected": "Reāllaika atjauninājumi ir aktīvi", - "push_disconnected": "Reāllaika atjauninājumi nav aktīvi", - "keyboard_shortcuts": "Tastatūras īsinājumtaustiņi", - "theme": { - "light": "Gaišā tēma", - "dark": "Tumšā tēma", - "system": "Sistēmas tēma" - }, - "language": { - "title": "Valoda" - }, - "mailboxes": { - "inbox": "Iesūtne", - "sent": "Nosūtītie", - "drafts": "Melnraksti", - "trash": "Mēstule", - "archive": "Arhīvs", - "starred": "Ar zvaigznīti", - "all_mail": "Visas vēstules", - "spam": "Mēstules", - "important": "Svarīgi" - }, - "unified_inbox": "Apvienotā iesūtne", - "unified_sent": "Visi nosūtītie", - "unified_drafts": "Visi melnraksti", - "unified_trash": "Visas mēstules", - "unified_archive": "Visi arhīvi", - "unified_junk": "Viss mēstules", - "all_accounts": "Visi konti", - "unified_mailbox": "Apvienotā pastkaste", - "expand": "Izvērst", - "collapse": "Sairt", - "expand_tooltip": "Izvērst", - "collapse_tooltip": "Sairt", - "mobile": { - "search": "Meklēt", - "compose": "Rakstīt", - "go_back": "Atpakaļ", - "toggle_menu": "Pārslēgt izvēlni" - }, - "clear_search": "Notīrīt meklēšanu", - "vacation_active": "Atvaļinājuma atbilde ir aktīva", - "demo_banner": "Demo režīms", - "demo_reset": "Atiestatīt", - "demo_tour": "Ekskursija", - "tags": "Tagi", - "show_all_tags": "Rādīt visus ({count})", - "show_fewer_tags": "Rādīt mazāk", - "folders": "Mapes", - "mail": "Pasts", - "nav_label": "Navigācija", - "add_app": "Lietotnes", - "shared": "Koplietots", - "scheduled": "Ieplānots", - "unified_all_unread": "Visi nelasītie", - "unified_all_starred": "Visi ar zvaigzni", - "unified_all_mail": "Visas vēstules", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Noklusējuma lietotnes", - "description": "Izvēlieties, vai e-pasta un kalendāra saites atvērt Bulwark. Tehniski Bulwark reģistrējas kā protokola apstrādātājs mailto: un webcal: saitēm.", - "unsupported": "Šī pārlūkprogramma vai savienojums neatbalsta manuālu protokola apstrādātāja reģistrāciju. Iespējams, instalēto PWA joprojām var izmantot pārlūkprogrammas vai sistēmas iestatījumos.", - "mailto_label": "E-pasta saites", - "mailto_description": "Atver mailto: saites Bulwark ar iepriekš aizpildītu ziņojuma redaktoru.", - "protocol_open_mode_label": "Atverot protokola saites", - "protocol_open_mode_description": "Izvēlieties, vai Bulwark atver mailto: un webcal: saites jaunā cilnē vai atkārtoti izmanto atvērtu sesiju. Aktīvās sesijas opcijai nepieciešama paziņojumu atļauja, lai jūs varētu noklikšķināt uz rezerves paziņojuma un izcelt Bulwark priekšplānā, ja pārlūkprogramma bloķē fokusu.", - "protocol_open_mode_active_session": "Ja iespējams, atvērt aktīvajā sesijā", - "protocol_open_mode_new_tab": "Vienmēr atvērt jaunu cilni", - "focus_notification_title": "Atvērt Bulwark", - "focus_notification_body": "Saite tika atvērta Bulwark. Noklikšķiniet, lai izceltu logu priekšplānā.", - "webcal_label": "Kalendāra saites", - "webcal_description": "Atver webcal: saites Bulwark ar iepriekš aizpildītu kalendāra abonēšanas dialogu.", - "register_mailto": "Reģistrēt e-pasta lietotni", - "register_webcal": "Reģistrēt kalendāra lietotni", - "mailto_registered": "E-pasta apstrādātāja reģistrācija pieprasīta", - "webcal_registered": "Kalendāra apstrādātāja reģistrācija pieprasīta", - "registration_failed": "Protokola apstrādātāja reģistrācija neizdevās", - "opening_mailto": "Tiek atvērts redaktors...", - "opening_webcal": "Tiek atvērts kalendārs...", - "browser_note": "Pārlūkprogramma vai operētājsistēma var lūgt apstiprinājumu un var prasīt, lai Bulwark būtu instalēts, pirms to var izvēlēties kā noklusējuma lietotni.", - "select_account_title": "Izvēlieties kontu", - "select_mailto_account": "Izvēlieties, kurā kontā atvērt šo e-pasta saiti.", - "select_webcal_account": "Izvēlieties, kurā kontā atvērt šo kalendāra saiti.", - "select_account_note": "Šī izvēle attiecas tikai uz šo protokola saiti.", - "detail_to": "Kam", - "detail_subject": "Temats", - "detail_no_subject": "Bez temata", - "detail_calendar": "Kalendārs", - "detail_source": "Avots", - "active_account": "Aktīvs", - "switching_account": "Notiek konta pārslēgšana..." - }, - "sidebar_apps": { - "modal_title": "Sānu joslas lietotnes", - "add_new": "Pievienot lietotni", - "edit_app": "Rediģēt lietotni", - "name_label": "Nosaukums", - "name_placeholder": "Mana lietotne", - "name_required": "Nosaukums ir obligāts", - "url_label": "URL adrese", - "url_required": "URL ir obligāts", - "url_invalid": "Ievadiet derīgu http vai https URL", - "icon_label": "Ikona", - "icon_required": "Ikona ir obligāta", - "open_mode_label": "Atvēršanas režīms", - "open_new_tab": "Jaunā cilnē", - "open_inline": "Iebūvēts", - "cancel": "Atcelt", - "add": "Pievienot", - "update": "Atjaunināt", - "delete": "Dzēst", - "delete_confirm_title": "Dzēst lietotni", - "delete_confirm": "Vai tiešām vēlaties dzēst \"{name}\"?", - "no_apps": "Lietotnes vēl nav pievienotas", - "no_apps_hint": "Pievienojiet pielāgotas lietotnes un saites sānu joslā", - "managed_title": "Nodrošina administrators", - "managed_description": "Šīs lietotnes ir iestatītas visiem, un tās nevar mainīt.", - "managed_badge": "Pārvaldīta", - "search_icons": "Meklēt ikonas...", - "show_popular": "Populārākās", - "show_all": "Visas", - "no_icons_found": "Ikonas nav atrastas", - "inline_badge": "Iebūvēts", - "tab_badge": "Cilne", - "show_on_mobile": "Rādīt mobilajā ierīcē" - }, - "email_list": { - "no_emails": "Vēstules nav atrastas", - "no_emails_description": "Mape ir tukša", - "no_search_results": "Nekas netika atrasts", - "no_search_results_description": "Mēģiniet mainīt meklēšanas vaicājumu vai filtrus", - "loading": "Ielādē vēstules...", - "unread": "nelasītas", - "to_me": "Man", - "to_recipients": "{count} saņēmējiem", - "and_others": "un vēl {count}", - "draft": "Melnraksts", - "starred": "Ar zvaigznīti", - "pinned": "Piesprausts", - "replied": "Atbildēts", - "forwarded": "Pārsūtīts", - "has_attachment": "Ar pielikumu", - "conversations_count": "{count} no {total} sarunām", - "conversations_count_plus": "{count}+ sarunas", - "conversations_count_simple": "{count} sarunas", - "no_conversations": "Nav sarunu", - "loading_more": "Ielādē vēstules...", - "no_more_emails": "Vairāk vēstuļu nav", - "batch_actions": { - "select": "Atlasīt vēstules", - "select_all": "Atlasīt visas", - "selected_messages": "{count, plural, one {Izvēlēta 1 e-pasta vēstule} other {Izvēlētas # e-pasta vēstules}}", - "mark_read": "Atzīmēt kā izlasītu", - "mark_unread": "Atzīmēt kā nelasītu", - "delete": "Dzēst", - "delete_confirm_title": "Dzēst vēstules", - "delete_confirm_message": "Vai tiešām vēlaties dzēst {count, plural, one {1 vēstuli} other {# vēstules}}?", - "clear_selection": "Noņemt atlasi" - }, - "permanent_delete": "Dzēst neatgriezeniski", - "permanent_delete_confirm_title": "Dzēst neatgriezeniski", - "permanent_delete_confirm_message": "Šī vēstule tiks dzēsta neatgriezeniski. Šo darbību nevar atcelt.", - "permanent_delete_confirm_batch_message": "{count, plural, one {1 vēstule} other {# vēstules}} tiks dzēstas neatgriezeniski. Šo darbību nevar atcelt.", - "empty_folder": { - "button": "Iztīrīt mapi", - "confirm_title": "Iztīrīt mapi", - "confirm_message": "Visas vēstules šajā mapē tiks dzēstas neatgriezeniski. Šo darbību nevar atcelt.", - "confirm_button": "Iztīrīt mapi", - "junk_hint": "Jūs varat iztīrīt mapi Mēstules, lai neatgriezeniski dzēstu visus ziņojumus.", - "trash_hint": "Jūs varat iztīrīt Atkritni, lai neatgriezeniski dzēstu visus ziņojumus." - }, - "no_scheduled_emails": "Nav ieplānotu e-pastu", - "no_scheduled_emails_description": "Ziņojumi, kas ieplānoti vēlākai nosūtīšanai, būs redzami šeit.", - "scheduled_count": "{count, plural, one {1 ieplānots e-pasts} other {# ieplānoti e-pasti}}", - "scheduled_actions_hint": "Ieplānotos ziņojumus var atcelt, pārplānot vai rediģēt no ieplānotajām darbībām.", - "cancel_scheduled_send": "Atcelt sūtīšanu", - "reschedule_send": "Pārplānot", - "cancel_and_edit": "Atcelt un rediģēt", - "cancel_and_compose_again": "Atcelt un rakstīt no jauna", - "reschedule_prompt": "Ievadiet jaunu datumu/laiku, piem. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Sūtītājs pieprasa lasīšanas apstiprinājumu:", - "send": "Sūtīt apstiprinājumu", - "ignore": "Ignorēt", - "sent": "Lasīšanas apstiprinājums nosūtīts.", - "send_failed": "Neizdevās nosūtīt lasīšanas apstiprinājumu", - "mdn_subject": "Izlasīts: {subject}", - "mdn_body": "Šis ir lasīšanas apstiprinājums ziņojumam, ko nosūtījāt uz {recipient}.\n\nPiezīme: šis apstiprinājums tikai apliecina, ka ziņojums tika parādīts saņēmēja datorā. Tas negarantē, ka saņēmējs ir izlasījis vai sapratis saturu." - }, - "no_email_selected": "Nav atlasīta neviena vēstule", - "no_email_description": "Atlasiet vēstuli no saraksta, lai to skatītu šeit", - "no_conversation_selected": "Nav atlasīta saruna", - "no_conversation_description": "Atlasiet sarunu no saraksta, lai to lasītu šeit", - "compose": "Rakstīt", - "compose_hint": "Rakstīt jaunu ziņojumu", - "no_subject": "(nav temata)", - "no_body_content": "(nav satura)", - "no_preview_available": "Priekšskatījums nav pieejams", - "show_quoted_text": "Rādīt citēto tekstu", - "hide_quoted_text": "Paslēpt citēto tekstu", - "loading_email": "Ielādē vēstuli...", - "loading": "Ielādē...", - "reply": "Atbildēt", - "reply_all": "Atbildēt visiem", - "forward": "Pārsūtīt", - "delete": "Dzēst", - "archive": "Arhivēt", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Pievienot zvaigznīti", - "unstar": "Noņemt zvaigznīti", - "mark_unread": "Atzīmēt kā nelasītu", - "mark_read": "Atzīmēt kā izlasītu", - "unread": "Nelasīts", - "read": "Lasīts", - "spam_short": "Spams", - "not_spam_short": "Nav spams", - "move": "Pārvietot", - "print": "Drukāt", - "view_source": "Skatīt avota kodu", - "fullscreen": "Pilnekrāna režīms", - "exit_fullscreen": "Iziet no pilnekrāna režīma", - "export_email": "Eksportēt kā .eml", - "forward_as_attachment": "Pārsūtīt kā pielikumu", - "import_email": "Importēt .eml vai .zip", - "keyboard_shortcuts": "Īsinājumtaustiņi (?)", - "email_source": "Vēstules avota kods", - "draft_banner": "Šī vēstule ir melnraksts", - "edit_draft": "Rediģēt", - "copy_source": "Kopēt starpliktuvē", - "source_copied": "Avota kods nokopēts starpliktuvē", - "attachments": "Pielikumi", - "important": "Svarīgi", - "download": "Lejupielādēt", - "download_all": "Lejupielādēt visu", - "from": "No", - "to": "Kam", - "cc": "Kopija", - "bcc": "Slepenā kopija", - "date": "Datums", - "subject": "Temats", - "show_details": "Rādīt sīkāku informāciju", - "hide_details": "Slēpt sīkāku informāciju", - "external_content_warning": "Attēli un ārējais saturs ir bloķēts", - "load_external_content": "Ielādēt attēlus", - "trust_sender": "Vienmēr uzticēties šim sūtītājam", - "back_to_list": "Atpakaļ uz sarakstu", - "view_contact": "Skatīt kontaktu", - "message_details": "Informācija par ziņojumu", - "more_reply_options": "Papildu atbildēšanas iespējas", - "set_tag": "Iestatīt tagu", - "tag": "Tags", - "more_actions": "Citas darbības", - "previous": "Iepr.", - "next": "Nāk.", - "move_to": "Pārvietot uz...", - "remove_tag": "Noņemt tagu", - "tag_filter_placeholder": "Filtrēt tagus", - "tag_no_matches": "Nav atbilstošu tagu", - "more_count": "+vairāk {count}", - "characters_count": "{count} rakstzīmes", - "quick_reply_placeholder": "Rakstīt ātru atbildi...", - "more_options": "Citas iespējas", - "sending": "Sūta...", - "security_authentication": "Drošība un autentifikācija", - "technical_details": "Tehniskā informācija", - "message_id_label": "Ziņojuma ID:", - "reply_to_label": "Atbildes adrese:", - "delivery_time_label": "Piegādes laiks:", - "conversation_part_label": "Sarunas daļa:", - "previous_messages": "{count} iepriekšējais ziņojums", - "previous_messages_plural": "{count} iepriekšējie ziņojumi", - "time": { - "day": "diena", - "days": "dienas", - "hour": "stunda", - "hours": "stundas", - "minute": "minūte", - "minutes": "minūtes" - }, - "unknown_sender": "Nezināms", - "recipient_me": "es", - "recipient_and_others": "{name} un vēl {count}", - "recipient_to_prefix": "Kam:", - "authentication": { - "title": "Autentifikācija", - "status": { - "verified": "Apstiprināts", - "warning": "Brīdinājums", - "none": "Nav autentificēts" - }, - "spf": { - "pass": "SPF izturēts", - "fail": "SPF nav izturēts", - "none": "SPF nav atrasts" - }, - "dkim": { - "pass": "DKIM derīgs", - "fail": "DKIM nederīgs", - "none": "DKIM nav atrasts" - }, - "dmarc": { - "pass": "DMARC izturēts", - "fail": "DMARC nav izturēts", - "none": "DMARC nav atrasts" - }, - "spam_score": "Mēstuļu vērtējums", - "tooltip_spf": "Sender Policy Framework: pārbauda, vai sūtošajam serverim ir tiesības sūtīt e-pastu domēna vārdā", - "tooltip_dkim": "DomainKeys Identified Mail: ar kriptogrāfisku parakstu apstiprina, ka vēstule pārsūtīšanas laikā nav grozīta", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: nodrošina, ka SPF un DKIM atbilst sūtītāja domēnam, un nosaka politiku kļūmju gadījumiem", - "policy": "Politika", - "result": { - "pass": "Izturēts", - "fail": "Nav izturēts", - "softfail": "Daļēja kļūme", - "neutral": "Neitrāli", - "permerror": "Pastāvīga kļūda", - "temperror": "Pagaidu kļūda", - "none": "Nav" - } - }, - "headers": { - "routing": "Maršrutēšana", - "received": "Saņemts", - "message_id": "Ziņojuma ID", - "list_info": "Informācija par adresātu sarakstu" - }, - "tooltips": { - "reply": "Atbildēt (r)", - "reply_all": "Atbildēt visiem (a)", - "forward": "Pārsūtīt (f)", - "archive": "Arhivēt (e)", - "delete": "Dzēst (# vai Del)", - "star": "Pievienot zvaigznīti (s)", - "unstar": "Noņemt zvaigznīti (s)", - "compose": "Rakstīt (c)", - "previous": "Iepriekšējā vēstule", - "next": "Nākamā vēstule", - "edit_draft": "Rediģēt melnrakstu" - }, - "spam": { - "button_title": "Ziņot par mēstuli", - "not_spam_title": "Nav mēstule", - "toast_success": "Pārvietots uz mēstulēm", - "toast_batch": "{count} vēstules pārvietotas uz mēstulēm", - "toast_undo": "Atsaukt", - "toast_not_spam_success": "Pārvietots uz iesūtni", - "toast_not_spam_batch": "{count} vēstules pārvietotas uz iesūtni", - "error": "Neizdevās ziņot par mēstuli", - "error_not_spam": "Neizdevās atjaunot vēstuli" - }, - "unsubscribe_banner": { - "label": "Adresātu saraksts", - "button": "Atteikties", - "confirm_title": "Atteikties no šī sūtītāja?", - "confirm_button": "Apstiprināt", - "cancel": "Atcelt", - "success_http": "Atteikšanās lapa atvērta jaunā cilnē", - "success_mailto": "Atrakstīšanās e-pasts nosūtīts", - "confirm_message_http": "Atrakstīšanās lapa tiks atvērta jaunā cilnē.", - "confirm_message_mailto": "Sūtītājam tiks nosūtīts atrakstīšanās e-pasts.", - "error": "Neizdevās atteikties", - "dismiss": "Aizvērt" - }, - "calendar_invitation": { - "loading": "Ielādē pasākuma informāciju...", - "title": "Kalendāra uzaicinājums", - "published_title": "Publicēts pasākums", - "response_title": "Atbilde uz pasākumu", - "update_title": "Pasākuma atjauninājums", - "counter_title": "Pretpiedāvājums", - "refresh_title": "Atjaunināšanas pieprasījums", - "declined_counter_title": "Pretpiedāvājums noraidīts", - "cancelled_title": "Pasākums atcelts", - "organizer": "Organizators: {name}", - "organizer_label": "Organizators:", - "attendees": "{count, plural, one {# dalībnieks} other {# dalībnieki}}", - "accept": "Pieņemt", - "maybe": "Varbūt", - "decline": "Noraidīt", - "add_to_calendar": "Pievienot kalendāram", - "added": "Pievienots kalendāram", - "rsvp_sent": "Atbilde nosūtīta", - "parse_error": "Neizdevās nolasīt uzaicinājumu", - "action_failed": "Neizdevās veikt darbību kalendārā.", - "no_calendar": "Kalendārs nav pieejams", - "published_info": "Šis pasākums ir publicēts informācijai.", - "response_info": "Šis ziņojums satur dalībnieka atbildi.", - "response_info_organizer": "Šī dalībnieka atbilde atjaunina jūsu pasākumu.", - "update_info": "Šis ziņojums atjaunina esošu pasākumu.", - "counter_info": "Šis ziņojums piedāvā izmaiņas pasākumā.", - "counter_info_organizer": "Šis dalībnieks ierosināja izmaiņas jūsu pasākumā.", - "refresh_info": "Šis ziņojums pieprasa jaunāko informāciju par pasākumu.", - "refresh_info_organizer": "Dalībnieks pieprasīja jaunāko informāciju par pasākumu.", - "declined_counter_info": "Organizators noraidīja pretpiedāvājumu.", - "authentication_failed_info": "Šī uzaicinājuma pasta autentifikācija neizdevās. Esiet uzmanīgi.", - "authentication_missing_info": "Šim uzaicinājumam nav apstiprinātas autentifikācijas.", - "sender_mismatch_info": "Uzaicinājumu nosūtīja {sender}, bet organizators ir {organizer}.", - "sender_mismatch_unverified_info": "Uzaicinājumu nosūtīja {sender}, bet norādītais organizators ir {organizer}, un ziņojumu nevarēja pārbaudīt.", - "organizer_role": "Jūs esat šī pasākuma organizators", - "your_response": "Jūsu atbilde: {status}", - "response_needed": "Nepieciešama atbilde", - "response_accepted": "Pieņemts", - "response_tentative": "Pagaidām", - "response_declined": "Noraidīts", - "response_delegated": "Deleģēts", - "actor_sent_info": "Nosūtīja {name}.", - "actor_response_info": "{name} atbildēja: {status}.", - "actor_counter_info": "{name} ierosināja izmaiņas pasākumā.", - "actor_refresh_info": "{name} pieprasīja jaunāko informāciju.", - "actor_declined_counter_info": "{name} noraidīja pretpiedāvājumu.", - "actor_note": "Piezīme: {comment}", - "actor_unknown": "Kāds", - "proposed_changes": "Ierosinātās izmaiņas", - "change_title": "Virsraksts", - "change_time": "Laiks", - "change_location": "Atrašanās vieta", - "change_description": "Apraksts", - "change_empty": "Nav", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Apstiprināt izmaiņas", - "proposal_applied": "Izmaiņas ir apstiprinātas.", - "review_proposal": "Pārskatīt piedāvājumu", - "review_request": "Pārskatīt pieprasījumu", - "view_in_calendar": "Atvērt kalendārā", - "select_calendar": "Izvēlēties kalendāru", - "already_in_calendar": "Jau ir jūsu kalendārā", - "request_info": "Jūs esat uzaicināts uz šo pasākumu. Lūdzu, atbildiet.", - "cancel_info": "Organizators atcēla šo pasākumu.", - "event_updated": "Atjauninājums Nr. {sequence}", - "event_status_tentative": "Pagaidām", - "event_status_cancelled": "Atcelts", - "collapse": "Paslēpt detaļas", - "expand": "Rādīt detaļas" - }, - "send": "Sūtīt", - "more": "vairāk", - "details": { - "recipients_routing": "Adresāti un maršrutēšana", - "authentication_security": "Autentifikācija un drošība", - "identifiers_threading": "Identifikatori un sarakste", - "mailing_list": "Adresātu saraksts", - "message_properties": "Vēstules īpašības", - "sent": "Nosūtīts", - "received": "Saņemts", - "delivery_time": "Piegādes laiks", - "in_reply_to": "Atbildot uz", - "references": "Atsauces", - "thread_id": "Sarakstes ID", - "size": "Lielums", - "mime_type": "MIME tips", - "attachments_summary": "{count} faili · {size}", - "list_id": "Saraksta ID", - "list_help": "Saraksta palīdzība", - "list_post": "Sūtīšana sarakstam", - "list_unsubscribe": "Atteikties", - "iprev": "Apgrieztais DNS", - "spam_status": "Mēstuļu statuss", - "ai_verdict": "AI vērtējums", - "account": "Konts", - "no_subject": "(bez temata)" - }, - "scheduled_banner": "Ieplānots nosūtīšanai {date}", - "scheduled_send_created": "E-pasts ieplānots nosūtīšanai", - "cancel_scheduled_send": "Atcelt sūtīšanu", - "reschedule_send": "Pārplānot", - "cancel_and_edit": "Atcelt un rediģēt", - "cancel_and_compose_again": "Atcelt un rakstīt no jauna", - "reschedule_prompt": "Ievadiet jaunu datumu/laiku, piem. 2026-05-04T15:30", - "scheduled_actions_only": "Ieplānoto skatā ir pieejamas tikai ieplānošanas darbības", - "undo_send_scheduled": "Ziņojums ir ieplānots nosūtīšanai", - "undo_send": "Atsaukt sūtīšanu", - "contact_sidebar": { - "title": "Kontaktpersona", - "close": "Aizvērt sānjoslu", - "action_email": "E-pasts", - "action_email_title": "Sūtīt e-pastu", - "action_copy": "Kopēt", - "action_copy_title": "Kopēt e-pastu", - "action_edit_title": "Rediģēt kontaktpersonu", - "section_emails": "E-pasti", - "section_phones": "Tālruņi", - "section_organizations": "Organizācijas", - "section_addresses": "Adreses", - "section_notes": "Piezīmes", - "not_in_contacts": "Nav jūsu kontaktpersonās", - "add_to_contacts": "Pievienot kontaktpersonām", - "copied": "Nokopēts!", - "copy_failed": "Neizdevās nokopēt" - }, - "send_now": "Sūtīt tagad" - }, - "email_composer": { - "read_receipt_on": "Pieprasīts lasīšanas apstiprinājums (noklikšķiniet, lai atspējotu)", - "read_receipt_off": "Pieprasīt lasīšanas apstiprinājumu", - "dsn_on": "Piegādes paziņojums pieprasīts (noklikšķiniet, lai atspējotu)", - "dsn_off": "Pieprasīt piegādes paziņojumu", - "format_plain_text": "Pārslēgt uz vienkāršu tekstu", - "format_rich_text": "Pārslēgt uz formatētu tekstu (HTML)", - "require_tls_on": "Nepieciešama šifrēta piegāde (noklikšķiniet, lai atspējotu)", - "require_tls_off": "Pieprasīt šifrētu piegādi (TLS)", - "new_message": "Jauns ziņojums", - "signature_edit_hint": "Veiciet dubultklikšķi, lai rediģētu parakstu", - "reply": "Atbildēt", - "reply_all": "Atbildēt visiem", - "forward": "Pārsūtīt", - "reply_to": "Atbildēt", - "reply_all_to": "Atbildēt visiem", - "forward_message": "Pārsūtīt ziņojumu", - "from": "No", - "to": "Kam", - "cc": "Kopija", - "bcc": "Slepenā kopija", - "subject": "Temats", - "body_placeholder": "Rakstiet ziņojumu...", - "send": "Sūtīt", - "cancel": "Atcelt", - "attach": "Pievienot", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Atmest", - "discard_draft_title": "Atmest melnrakstu?", - "discard_draft_confirm": "Ir nesaglabātas izmaiņas. Vai tiešām vēlaties atmest šo melnrakstu?", - "saving": "Saglabā...", - "draft_saved": "Melnraksts saglabāts", - "save_failed": "Neizdevās saglabāt", - "to_placeholder": "Saņēmēji", - "cc_placeholder": "Kopijas saņēmēji", - "bcc_placeholder": "Slepenās kopijas saņēmēji", - "subject_placeholder": "Temats", - "cc_label": "Kopija:", - "bcc_label": "Slepenā kopija:", - "subject_label": "Temats:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(nav temata)", - "unknown_sender": "Nezināms", - "quote": { - "reply_header": "{date} persona {sender} rakstīja:", - "forward_header": "---------- Pārsūtītais ziņojums ----------", - "from": "No: {sender}", - "date": "Datums: {date}", - "subject": "Temats: {subject}", - "to": "Kam: {recipients}" - }, - "remove_sub_address": "Noņemt apakšadresi", - "from_override": { - "toggle_off": "Pārrakstīt", - "toggle_on": "Atcelt pārrakstīšanu", - "toggle_tooltip": "Brīvi rediģējiet sūtītāja vārdu un adresi. Pasts joprojām tiek sūtīts caur jūsu identitāti.", - "name_label": "Sūtītāja vārds", - "name_placeholder": "Vārds", - "email_label": "Sūtītāja e-pasta adrese", - "email_placeholder": "alias@example.com", - "envelope_notice": "Ja jūsu serveris nepieņem šo adresi kā aploksnes sūtītāju, tās vietā tiek izmantota {identity}, un adresāti to var redzēt galvenē Return-Path." - }, - "use_template": "Veidne", - "save_as_template": "Saglabāt kā veidni", - "validation": { - "recipient_required": "Norādiet vismaz vienu saņēmēju", - "body_required": "Ierakstiet ziņojumu vai pievienojiet failu", - "attachments_uploading": "Pielikumi joprojām tiek augšupielādēti — nosūtīs, tiklīdz tas pabeigts", - "attachment_upload_failed": "Nav nosūtīts - pielikumu neizdevās augšupielādēt. Noņemiet to un mēģiniet vēlreiz." - }, - "upload_progress": "Augšupielāde {uploaded} / {total}", - "upload_cancel": "Atcelt augšupielādi", - "upload_failed": "Neizdevās augšupielādēt {filename}", - "attachment_too_large": "“{name}” ir lielāks, nekā serveris atļauj ({max} vienam failam)", - "attachments_total_too_large": "Pielikumi pārsniegtu servera ierobežojumu – {max} vienai ziņai", - "drop_files": "Velciet failus šeit, lai pievienotu", - "show_less": "Rādīt mazāk", - "send_failed": "Neizdevās nosūtīt vēstuli", - "send_timeout": "Serveris pārstāja atbildēt. Pirms sūtīt vēlreiz, pārbaudiet mapi Nosūtītie — ziņojums, iespējams, jau ir aizsūtīts.", - "continue_draft": "Turpināt melnrakstu", - "close_draft_title": "Saglabāt vai atmest melnrakstu?", - "close_draft_message": "Ir nesaglabātas izmaiņas. Vai vēlaties saglabāt kā melnrakstu vai atmest?", - "smime_sign_on": "S/MIME paraksts iespējots", - "smime_sign_off": "Iespējot S/MIME parakstu", - "smime_encrypt_on": "S/MIME šifrēšana iespējota", - "smime_encrypt_off": "Iespējot S/MIME šifrēšanu", - "smime_encrypt_unavailable": "S/MIME šifrēšana nav pieejama - trūkst saņēmēja sertifikāta", - "smime_unlock_title": "Atbloķēt S/MIME atslēgu", - "smime_unlock_message": "Ievadiet paroli, lai atbloķētu savu S/MIME parakstīšanas atslēgu.", - "smime_unlock_button": "Atbloķēt", - "smime_passphrase_placeholder": "Parole", - "forgot_attachment": { - "title": "Vai aizmirsāt pielikumu?", - "message": "Jūsu ziņojumā minēts \"{keyword}\", bet nav pievienots neviens fails. Vai tomēr sūtīt?", - "send_anyway": "Sūtīt tik un tā", - "back": "Atpakaļ pie rediģēšanas" - }, - "empty_subject": { - "title": "Sūtīt bez temata?", - "message": "Šai ziņai nav temata. Vai tomēr sūtīt?", - "dont_ask_again": "Vairs nejautāt", - "send_anyway": "Sūtīt tik un tā", - "back": "Atpakaļ pie rediģēšanas" - }, - "add_link": "Pievienot saiti", - "link_url_prompt": "Ievadiet URL", - "sending": "Sūta...", - "schedule_send": "Ieplānot sūtīšanu", - "schedule_send_description": "Izvēlieties, kad serverim jānosūta šis ziņojums.", - "schedule_send_required": "Izvēlieties datumu un laiku.", - "schedule_send_invalid": "Ievadiet derīgu datumu un laiku.", - "schedule_send_future": "Izvēlieties datumu un laiku nākotnē.", - "schedule_send_too_late": "Šis laiks ir vēlāk, nekā serveris atļauj.", - "schedule_send_unsupported": "Šim kontam ieplānota sūtīšana netiek atbalstīta.", - "schedule_send_cleanup_warning": "Ieplānotā sūtīšana tika izveidota, bet melnraksta tīrīšana neizdevās.", - "send_delay_unsupported": "Šim kontam sūtīšanas aizture netiek atbalstīta.", - "send_delay_unsupported_confirm": "Šis konts neatbalsta sūtīšanas aizturi. Sūtīt uzreiz?", - "recipient_edit_email": "Rediģēt e-pasta adresi", - "recipient_edit_name": "Rediģēt parādāmo vārdu", - "recipient_email_placeholder": "E-pasta adrese", - "recipient_name_placeholder": "Parādāmais vārds", - "autocomplete_search_server": "Meklēt serverī", - "autocomplete_searching": "Meklē...", - "toolbar": { - "bold": "Treknraksts", - "italic": "Kursīvs", - "underline": "Pasvītrots", - "strikethrough": "Pārsvītrots", - "text_color": "Teksta krāsa", - "remove_color": "Noņemt krāsu", - "heading_1": "Virsraksts 1", - "heading_2": "Virsraksts 2", - "bullet_list": "Aizzīmju saraksts", - "ordered_list": "Numurēts saraksts", - "quote": "Citāts", - "code_block": "Koda bloks", - "align_left": "Līdzināt pa kreisi", - "align_center": "Centrēt", - "align_right": "Līdzināt pa labi", - "text_direction": "Teksta virziens (RTL/LTR)", - "link": "Saite", - "table": "Tabula", - "clear_formatting": "Notīrīt formatējumu", - "undo": "Atsaukt", - "redo": "Atkārtot", - "add_row_above": "Pievienot rindu virs", - "add_row_below": "Pievienot rindu zem", - "add_column_before": "Pievienot kolonnu pirms", - "add_column_after": "Pievienot kolonnu pēc", - "delete_row": "Dzēst rindu", - "delete_column": "Dzēst kolonnu", - "toggle_header_row": "Pārslēgt galvenes rindu", - "delete_table": "Dzēst tabulu", - "pick_size": "Izvēlēties izmēru", - "font_size": "Fonta izmērs", - "font_size_default": "Noklusējums", - "background_color": "Fona krāsa", - "remove_background_color": "Noņemt fona krāsu" - }, - "send_filing_warning": "Nosūtīts - bet pēcapstrāde neizdevās, var palikt novecojis melnraksts." - }, - "confirm_dialog": { - "confirm": "Apstiprināt", - "cancel": "Atcelt" - }, - "common": { - "loading": "Ielāde...", - "error": "Kļūda", - "success": "Gatavs", - "cancel": "Atcelt", - "save": "Saglabāt", - "delete": "Dzēst", - "edit": "Rediģēt", - "close": "Aizvērt", - "search": "Meklēt", - "refresh": "Atsvaidzināt", - "settings": "Iestatījumi", - "help": "Palīdzība", - "logout": "Izrakstīties", - "yes": "Jā", - "no": "Nē", - "unknown": "Nezināms", - "app_title": "Tīmekļa pasts", - "reconnecting": "Savienojums pārtraukts. Mēģina izveidot savienojumu...", - "rate_limited_title": "Autentifikācija serverī ir īslaicīgi ierobežota.", - "rate_limited_detail": "Bulwark ir apturējis fonā veiktos pieprasījumus, lai izvairītos no bloķēšanas. Mēģinās vēlreiz pēc {seconds} s.", - "rate_limited_action_title": "Pieprasījums apturēts, lai izvairītos no bloķēšanas.", - "rate_limited_action_detail": "Bulwark gaida servera noildzes beigas pirms jaunu autentificētu pieprasījumu sūtīšanas. Mēģiniet vēlreiz pēc {seconds} s.", - "pull_to_refresh": "Velciet, lai atsvaidzinātu", - "release_to_refresh": "Atlaidiet, lai atsvaidzinātu", - "refreshing": "Atsvaidzina…" - }, - "notifications": { - "email_sent": "Vēstule veiksmīgi nosūtīta", - "email_deleted": "Vēstule izdzēsta", - "email_archived": "Vēstule arhivēta", - "emails_deleted": "{count, plural, zero {# e-pasti dzēsti} one {# e-pasts dzēsts} other {# e-pasti dzēsti}}", - "emails_archived": "{count, plural, zero {# e-pasti arhivēti} one {# e-pasts arhivēts} other {# e-pasti arhivēti}}", - "error_archiving": "Neizdevās arhivēt e-pastu", - "error_updating": "Neizdevās atjaunināt e-pastu", - "email_starred": "Vēstulei pievienota zvaigznīte", - "email_unstarred": "Zvaigznīte noņemta", - "email_marked_read": "Vēstule atzīmēta kā izlasīta", - "email_marked_unread": "Vēstule atzīmēta kā nelasīta", - "copied_to_clipboard": "Nokopēts starpliktuvē", - "source_copied": "Avota kods nokopēts starpliktuvē", - "error_sending": "Neizdevās nosūtīt vēstuli", - "error_deleting": "Neizdevās izdzēst vēstuli", - "error_loading": "Neizdevās ielādēt vēstules", - "new_email": "Jauna vēstule", - "new_email_from": "No: {sender}", - "click_to_view": "Noklikšķiniet, lai skatītu", - "email_moved": "Vēstule pārvietota", - "emails_moved": "{count} vēstules pārvietotas", - "moved_to_mailbox": "Pārvietots uz {mailbox}", - "move_failed": "Pārvietošanas kļūda", - "move_error": "Neizdevās pārvietot vēstules uz izvēlēto mapi", - "email_tagged": "Vēstulei pievienots tags", - "emails_tagged": "{count} vēstulēm pievienots tags", - "tag_failed": "Tagu pievienošanas kļūda", - "identity_created": "Identitāte veiksmīgi izveidota", - "identity_updated": "Identitāte veiksmīgi atjaunināta", - "identity_deleted": "Identitāte izdzēsta", - "identity_set_primary": "Galvenā identitāte atjaunināta", - "identity_create_failed": "Neizdevās izveidot identitāti: {error}", - "identity_update_failed": "Neizdevās atjaunināt identitāti: {error}", - "identity_delete_failed": "Neizdevās izdzēst identitāti: {error}", - "identity_unauthorized": "Jums nav tiesību sūtīt no šīs e-pasta adreses", - "identity_not_found": "Identitāte netika atrasta", - "vacation_saved": "Atvaļinājuma atbildes iestatījumi saglabāti", - "vacation_save_failed": "Neizdevās saglabāt atvaļinājuma atbildes iestatījumus", - "filters_saved": "Filtri veiksmīgi saglabāti", - "filters_save_failed": "Neizdevās saglabāt filtrus", - "filters_deleted": "Filtra nosacījums izdzēsts", - "templates_exported": "Veidnes veiksmīgi eksportētas", - "templates_imported": "Importētas {count, plural, one {# veidne} other {# veidnes}}", - "templates_import_errors": "Dažas veidnes neizdevās importēt", - "templates_import_empty": "Failā netika atrastas veidnes", - "export_email_error": "Neizdevās eksportēt vēstuli", - "import_email_success": "Vēstule veiksmīgi importēta", - "import_email_error": "Neizdevās importēt vēstuli" - }, - "date": { - "today": "Šodien", - "yesterday": "Vakar", - "this_week": "Šonedēļ", - "last_week": "Pagājušajā nedēļā", - "this_month": "Šomēnes", - "older": "Senāk", - "just_now": "Tikko", - "minutes_ago": "Pirms {count} minūtes", - "minutes_ago_plural": "Pirms {count} minūtēm", - "hours_ago": "Pirms {count} stundas", - "hours_ago_plural": "Pirms {count} stundām", - "days_ago": "Pirms {count} dienas", - "days_ago_plural": "Pirms {count} dienām" - }, - "language": { - "title": "Valoda", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Izvēlieties valodu", - "switch_to_english": "Pārslēgt uz angļu valodu", - "switch_to_french": "Pārslēgt uz franču valodu", - "switch_to_japanese": "Pārslēgt uz japāņu valodu", - "switch_to_spanish": "Pārslēgt uz spāņu valodu", - "switch_to_italian": "Pārslēgt uz itāļu valodu", - "switch_to_german": "Pārslēgt uz vācu valodu", - "switch_to_dutch": "Pārslēgt uz holandiešu valodu", - "switch_to_portuguese": "Pārslēgt uz portugāļu valodu", - "switch_to_russian": "Pārslēgt uz krievu valodu", - "switching": "Maina valodu...", - "switch": "Mainīt valodu", - "current": "Pašreizējā valoda", - "polish": "Polski", - "switch_to_polish": "Pārslēgt uz poļu valodu", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Pārslēgt uz persiešu valodu", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Pārslēgt uz mongoļu valodu" - }, - "settings": { - "title": "Iestatījumi", - "back_to_mail": "Atgriezties pie pasta", - "save_success": "Iestatījumi veiksmīgi saglabāti", - "import_success": "Iestatījumi veiksmīgi importēti", - "import_error": "Neizdevās importēt iestatījumus", - "reset_confirm": "Vai tiešām vēlaties atiestatīt visus iestatījumus uz noklusējuma vērtībām?", - "unsaved_changes": "Jums ir nesaglabātas izmaiņas", - "discard_changes": "Atmest nesaglabātās izmaiņas?", - "discard": "Atmest", - "keep_editing": "Turpināt rediģēšanu", - "tabs": { - "appearance": "Izskats", - "language": "Valoda, reģions un laiks", - "email": "Pasta darbība", - "composer": "Redaktors", - "privacy": "Privātums un drošība", - "account": "Konts", - "identities": "Identitātes", - "vacation": "Atvaļinājuma atbilde", - "advanced": "Papildu", - "calendar": "Kalendārs", - "filters": "Filtri", - "templates": "Veidnes", - "folders": "Mapes", - "keywords": "Tagi", - "security": "Drošība", - "files": "Faili", - "contacts": "Kontakti", - "encryption": "Šifrēšana", - "protocol_handlers": "Noklusējuma lietotnes", - "sidebar_apps": "Sānu joslas lietotnes", - "notifications": "Paziņojumi", - "layout": "Izkārtojums", - "reading": "Lasīšana", - "composing": "Rakstīšana", - "downloads": "Lejupielādes", - "content_senders": "Saturs un sūtītāji", - "about_data": "Par un dati", - "debug": "Atkļūdošana" - }, - "tab_groups": { - "general": "Vispārīgi", - "account": "Konts un identitāte", - "organization": "Pasta organizēšana", - "apps": "Lietotnes", - "system": "Sistēma", - "appearance": "Izskats", - "mail": "Pasts", - "privacy": "Privātums un drošība", - "advanced": "Papildu" - }, - "appearance": { - "message_list_order": { - "label": "Ziņu saraksta secība", - "description": "Kā ziņas tiek sakārtotas sarakstā. Kārtošanu veic serveris, tāpēc tā attiecas uz visu mapi, ne tikai uz ielādētajām ziņām.", - "preset": { - "chronological": "Hronoloģiski (jaunākās vispirms)", - "unread_first": "Nelasītās vispirms", - "starred_first": "Ar zvaigznīti vispirms", - "tagged_first": "Ar birku vispirms", - "custom": "Pielāgota" - }, - "tag_label": "Birka", - "advanced_show": "Papildu: rediģēt kārtošanas līmeņus", - "advanced_hide": "Paslēpt papildu", - "criterion_label": "{n}. līmeņa kritērijs", - "direction_label": "{n}. līmeņa virziens", - "criteria": { - "unread": "Nelasīta", - "starred": "Ar zvaigznīti", - "tag": "Birka", - "receivedAt": "Saņemšanas datums", - "sentAt": "Nosūtīšanas datums", - "from": "Sūtītājs", - "subject": "Tēma", - "size": "Izmērs" - }, - "direction": { - "unread_first": "Nelasītās vispirms", - "read_first": "Lasītās vispirms", - "starred_first": "Ar zvaigznīti vispirms", - "unstarred_first": "Bez zvaigznītes vispirms", - "tagged_first": "Ar birku vispirms", - "untagged_first": "Bez birkas vispirms", - "newest_first": "Jaunākās vispirms", - "oldest_first": "Vecākās vispirms", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Lielākās vispirms", - "smallest_first": "Mazākās vispirms" - }, - "add_level": "Pievienot līmeni", - "remove_level": "Noņemt līmeni", - "move_up": "Pārvietot augstāk", - "move_down": "Pārvietot zemāk", - "tiebreak_note": "Līdz trim līmeņiem; nākamie līmeņi izšķir neizšķirtu. „Jaunākās vispirms” vienmēr tiek piemērots pēdējais.", - "scope": { - "label": "Piemērot visām mapēm", - "description": "Ja izslēgts, secība attiecas tikai uz Iesūtni; pārējās mapes paliek hronoloģiskas." - }, - "unsupported_note": "Jūsu pasta serveris neatbalsta kārtošanu pēc lasīšanas stāvokļa, zvaigznītēm vai birkām. Šie līmeņi tiek ignorēti, un saraksts paliek hronoloģisks." - }, - "title": "Izskats", - "description": "Pielāgojiet tīmekļa pasta izskatu", - "theme": { - "label": "Tēma", - "description": "Izvēlieties vēlamo krāsu shēmu", - "light": "Gaišā", - "dark": "Tumšā", - "system": "Sistēmas" - }, - "language": { - "label": "Valoda", - "description": "Izvēlieties vēlamo valodu" - }, - "font_size": { - "label": "Fonta lielums", - "description": "Pielāgojiet teksta izmēru labākai lasāmībai", - "small": "Mazs", - "medium": "Vidējs", - "large": "Liels" - }, - "list_density": { - "label": "Blīvums", - "description": "Pielāgojiet saskarnes elementu attālumus", - "extra_compact": "Īpaši kompakts", - "compact": "Kompakts", - "regular": "Parasts", - "comfortable": "Plašs" - }, - "animations": { - "label": "Iespējot animācijas", - "description": "Rādīt plūstošas pārejas un efektus" - }, - "toolbar_position": { - "label": "Rīkjoslas novietojums", - "description": "Kur rādīt vēstules darbību pogas (Atbildēt, Arhivēt utt.)", - "top": "Augšā", - "below_subject": "Zem temata" - }, - "toolbar_labels": { - "label": "Rādīt rīkjoslas aprakstus", - "description": "Rādīt teksta aprakstus blakus ikonām. Izslēdziet, lai ietaupītu vietu." - }, - "hide_account_switcher": { - "label": "Slēpt kontu pārslēdzēju sānu joslā", - "description": "Slēpt kontu izvēli sānu joslas augšpusē. Kontus joprojām varēsiet pārslēgt navigācijas joslā apakšā." - }, - "show_rail_account_list": { - "label": "Rādīt kontu avatarus navigācijas joslā", - "description": "Rādīt kontu apļus navigācijas joslas apakšā ātrai pārslēgšanai." - }, - "unified_mailbox": { - "label": "Apvienotā pastkaste", - "description": "Rādīt apvienotās mapes (Iesūtne, Nosūtītie u.c.) no visiem pievienotajiem kontiem", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Iekļaut grupas pastkastes", - "description": "Iekļaut apvienotajā skatā arī koplietotās/grupas pastkastes." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Krāsainas sānjoslas ikonas", - "description": "Iekrāsojiet mapju un birku ikonas pēc to veida (zila Iesūtne, sarkana Mēstules, zaļa Nosūtītie utt.). Atspējojiet, lai iegūtu vienkrāsainu sānjoslu." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Rādīt kopējo ziņojumu skaitu", - "description": "Rāda kopējo ziņojumu skaitu blakus mapēm un tagiem, kā arī nelasīto ziņojumu skaitu. Atspējojiet, lai rādītu tikai nelasītos." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro saskarne (eksperimentāla)", - "description": "Tikai darbvirsmas pieredzējušu lietotāju izkārtojums ar ziņojumu pārlūkošanu vairākās cilnēs un kontu pārvaldību. Standarta saskarne netiek ietekmēta; varat jebkurā brīdī pārslēgties atpakaļ.", - "open_label": "Atvērt Pro saskarni", - "back_to_standard": "Atpakaļ uz standartu" - }, - "cross_unread": { - "label": "Visi konti: Nelasītie", - "description": "Rāda ierakstu sadaļā “Visi konti”, kas uzskaita nelasītās vēstules no visiem kontiem, aptverot visas mapes, izņemot mēstules, nosūtītās, arhīvu, miskasti un melnrakstus." - }, - "cross_starred": { - "label": "Visi konti: Ar zvaigzni", - "description": "Rāda ierakstu sadaļā “Visi konti”, kas uzskaita ar zvaigzni atzīmētās vēstules no visiem kontiem, aptverot visas mapes, izņemot mēstules, nosūtītās, arhīvu, miskasti un melnrakstus." - }, - "cross_all": { - "label": "Visi konti: Visas vēstules", - "description": "Rāda ierakstu sadaļā “Visi konti”, kas uzskaita visas vēstules no visiem kontiem, aptverot visas mapes, izņemot mēstules, nosūtītās, arhīvu, miskasti un melnrakstus." - } - }, - "keywords": { - "title": "E-pasta tagi", - "description": "Definējiet tagus, lai organizētu e-pastus ar krāsām. Tie tiek saglabāti kā JMAP atslēgvārdi serverī.", - "add_keyword": "Pievienot tagu", - "label_field": "Redzamais nosaukums", - "label_placeholder": "piem., Darbs, Personīgi, Steidzami", - "id_field": "Taga identifikators", - "id_placeholder": "piem., darbs, personigi", - "color_field": "Krāsa", - "id_exists": "Šāds taga identifikators jau pastāv", - "edit": "Rediģēt tagu", - "delete": "Dzēst tagu", - "save": "Saglabāt", - "add": "Pievienot", - "cancel": "Atcelt", - "migrating": "Taga atjaunināšana esošajos e-pastos…", - "migration_error": "Neizdevās atjaunināt tagu esošajos e-pastos", - "nesting": { - "label": "Ligzdoti tagi", - "description": "Ligzdojiet tagus zem citiem tagiem un rādiet tos sānjoslā kā koku." - }, - "parent_field": "Vecāktags", - "no_parent": "Nav vecāktaga", - "too_long": "Šis taga ceļš ir pārāk garš (ne vairāk kā {max} rakstzīmes)", - "has_children_locked": "Zem šī taga ir ligzdoti citi tagi, tāpēc tā nosaukums un vecāktags ir bloķēti. Vispirms pārvietojiet vai noņemiet tos.", - "has_children_delete": "Vispirms noņemiet zem šī ligzdotos tagus", - "visibility_field": "Redzamība sānjoslā", - "visibility": { - "show": "Rādīt", - "unread": "Rādīt, ja ir nelasīti", - "hide": "Slēpt" - }, - "unrecognized": { - "title": "Atgūt tagus no servera", - "description": "Tagu nosaukumi un krāsas tiek glabāti šajā pārlūkā, bet paši tagi atrodas jūsu vēstulēs kā JMAP atslēgvārdi. Pārmeklējiet pastu, lai atrastu atslēgvārdus, kuriem šeit nav taga, un pievienojiet tos atpakaļ.", - "scan": "Meklēt tagus", - "rescan": "Meklēt vēlreiz", - "scanning": "Notiek meklēšana… {scanned} no {total} vēstulēm", - "none": "Visi jūsu vēstulēs esošie tagi jau ir definēti šeit.", - "messages": "{count, plural, one {# vēstule} other {# vēstules}}", - "add_all": "Pievienot visus ({count})", - "partial": "Tika pārmeklētas tikai {scanned} jaunākās no {total} vēstulēm. Tagi, kas izmantoti tikai vecākās vēstulēs, var nebūt atrasti.", - "error": "Neizdevās pārmeklēt pastu, lai atrastu tagus." - } - }, - "notifications": { - "test_sound": "Pārbaudīt paziņojuma skaņu", - "sounds": { - "default": "Noklusējuma (Signāls)", - "cheerful": "Priecīgs", - "involved": "Iesaistošs", - "swift": "Ātrs", - "relax": "Relaksējošs" - }, - "sound_selection": { - "title": "Paziņojuma skaņa", - "description": "Izvēlieties skaņu paziņojumiem", - "choose": "Skaņa", - "choose_desc": "Izvēlieties paziņojuma skaņu un nospiediet skaļruņa ikonu, lai to noklausītos" - }, - "email": { - "title": "E-pasta paziņojumi", - "description": "Iestatiet paziņojumus par ienākošajām vēstulēm", - "enabled": "E-pasta paziņojumi", - "enabled_desc": "Rādīt paziņojumus, saņemot jaunas vēstules", - "sound": "Paziņojuma skaņa", - "sound_desc": "Atskaņot skaņas signālu, saņemot jaunas vēstules", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Kalendāra paziņojumi", - "description": "Iestatiet paziņojumus par kalendāra pasākumiem", - "enabled": "Pasākumu paziņojumi", - "enabled_desc": "Rādīt brīdinājumus par gaidāmajiem kalendāra pasākumiem", - "sound": "Paziņojuma skaņa", - "sound_desc": "Atskaņot skaņas signālu kalendāra atgādinājumiem", - "invitation_parsing": "Atpazīt uzaicinājumus e-pastā", - "invitation_parsing_desc": "Noteikt kalendāra uzaicinājumus pielikumos un rādīt kalendāra darbības" - }, - "push": { - "confirm_disable_message": "Šī ierīce vairs nesaņems brīdinājumus, kad vietne būs aizvērta.", - "confirm_disable_title": "Atspējot fona paziņojumus?", - "description": "Saņem sistēmas paziņojumus par jaunu pastu, kad šī vietne ir aizvērta. Piegādāts caur Bulwark push releju; relejs nekad neredz pasta saturu.", - "disable": "Atspējot", - "enable": "Iespējot", - "dismiss_aria": "Aizvērt paziņojumu uzaicinājumu", - "ios_hint": "Operētājsistēmā iOS vispirms instalējiet vietni sākuma ekrānā – Safari piegādā Web Push tikai instalētajām PWA.", - "reenable": "Reģistrēt vēlreiz", - "relay_desc": "Pēc noklusējuma izmanto izmitināto Bulwark releju. Mainiet tikai tad, ja izmitināt pats.", - "relay_label": "Push relejs", - "relay_locked": "Iestatījis administrators", - "relay_locked_desc": "Push releja URL ir iestatījis administrators, un to nevar mainīt.", - "status_active": "Aktīvs šajā ierīcē", - "status_busy": "Notiek darbs…", - "status_inactive": "Nav iespējots šajā ierīcē", - "status_unsupported": "Šī pārlūkprogramma neatbalsta Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Fona paziņojumi", - "devices_title": "Reģistrētās ierīces", - "devices_desc": "Visas ierīces, kas reģistrētas šī konta fona paziņojumu saņemšanai. Atsaucot ierīci, tā nekavējoties pārstāj tās saņemt.", - "devices_loading": "Notiek reģistrēto ierīču ielāde…", - "devices_empty": "Fona paziņojumiem nav reģistrēta neviena ierīce.", - "devices_error": "Neizdevās ielādēt reģistrētās ierīces.", - "devices_refresh": "Atsvaidzināt", - "device_this": "Šī ierīce", - "device_other": "Cita ierīce", - "device_status_active": "Piegādā paziņojumus", - "device_status_inactive": "Nekad nav piegādājusi paziņojumu", - "device_status_unknown": "Šim retranslatoram nezināma", - "device_expires": "Beidzas {date}", - "revoke": "Atsaukt", - "confirm_revoke_title": "Atsaukt šo ierīci?", - "confirm_revoke_message": "Tā nekavējoties pārstās saņemt fona paziņojumus. Atkārtoti ieslēdzot push šajā ierīcē, tā tiks reģistrēta no jauna.", - "confirm_revoke_message_this": "Šī ierīce nekavējoties pārstās saņemt fona paziņojumus." - } - }, - "language_region": { - "title": "Valoda, reģions un laiks", - "description": "Valoda, datuma formāts, laika formāts un citas reģionālās preferences", - "language": { - "label": "Valoda", - "description": "Izvēlieties vēlamo valodu", - "english": "Angļu", - "french": "Franču" - }, - "date_format": { - "label": "Datuma formāts", - "description": "Kā datumi tiek attēloti e-pasta sarakstā", - "smart": "Gudrs (atbilstoši reģionam)", - "relative": "Relatīvs (pirms 1 st., pirms 2 d.)", - "full": "Vienmēr pilns datums", - "preview_today": "Šodien:", - "preview_this_week": "Šajā nedēļā:", - "preview_older": "Vecāks:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Laika formāts", - "description": "Izvēlieties starp 12 vai 24 stundu formātu", - "12h": "12 stundu", - "24h": "24 stundu" - }, - "first_day": { - "label": "Nedēļas pirmā diena", - "description": "Sākt nedēļu ar svētdienu vai pirmdienu", - "sunday": "Svētdiena", - "monday": "Pirmdiena" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Notīrīt meklēšanu, mainot mapi", - "description": "Noklikšķinot uz mapes (Iesūtne, Nosūtītie…), aktīvā meklēšana tiek notīrīta un tiek atvērta šī mape. Kad izslēgts, meklēšana paliek un tiek atkārtota izvēlētajā mapē." - }, - "request_read_receipt": { - "label": "Pēc noklusējuma pieprasīt lasīšanas apstiprinājumus", - "description": "Sastādot jaunu ziņojumu, iepriekš ieslēgt lasīšanas apstiprinājuma pieprasījumu." - }, - "read_receipt_response": { - "label": "Atbildēt uz lasīšanas apstiprinājuma pieprasījumiem", - "description": "Ko darīt, kad ienākošs ziņojums pieprasa lasīšanas apstiprinājumu.", - "ask": "Vaicāt katru reizi", - "always": "Vienmēr sūtīt", - "never": "Nekad nesūtīt" - }, - "title": "Pasta darbība", - "description": "Pielāgojiet vēstuļu apstrādi", - "mark_read": { - "label": "Atzīmēt kā izlasītu", - "description": "Kad atzīmēt vēstuli kā izlasītu, to atverot", - "instant": "Nekavējoties", - "delay_3s": "Pēc 3 sekundēm", - "delay_5s": "Pēc 5 sekundēm", - "never": "Nekad" - }, - "delete_action": { - "label": "Darbība dzēšot", - "description": "Kas notiek, izdzēšot vēstuli", - "trash": "Pārvietot uz atkritni", - "trash_and_read": "Pārvietot uz atkritni un atzīmēt kā lasītu", - "permanent": "Dzēst neatgriezeniski", - "warning": "Vēstules tiks dzēstas neatgriezeniski. Šī darbība nav atceļama." - }, - "message_spacing": { - "label": "Ziņojuma atstarpes", - "description": "Atstarpe ap ziņojuma tekstu lasītājā", - "auto": "Automātiski (ieteicams)", - "always": "Vienmēr pievienot atstarpi", - "edge": "No malas līdz malai" - }, - "plain_text_font": { - "label": "Vienkārša teksta fonts", - "description": "Fonts vienkārša teksta e-pastiem", - "mono": "Vienplatuma", - "sans": "Parasts (tāpat kā lietotnē)" - }, - "archive_mode": { - "label": "Arhivēt mapē", - "description": "Kā organizēt vēstules arhivējot", - "single": "Viena mape", - "year": "Mape katram gadam", - "month": "Mape katram mēnesim", - "reorganize": "Reorganizēt esošo arhīvu", - "reorganize_success": "{count, plural, =0 {Nav vēstuļu reorganizācijai} =1 {1 vēstule reorganizēta} other {# vēstules reorganizētas}}", - "reorganize_error": "Neizdevās reorganizēt arhīvu" - }, - "permanently_delete_junk": { - "label": "Mēstules dzēst neatgriezeniski", - "description": "Dzēst vēstules no mapes \"Mēstules\" neatgriezeniski, nepārvietojot uz atkritni" - }, - "mail_layout": { - "label": "Pasta izkārtojums", - "description": "Izvēlieties starp klasisko dalīto skatu vai fokusa sarakstu.", - "split": "Dalīts skats", - "split_description": "Ziņojumu saraksts un lasīšanas rūts ir redzamas blakus.", - "focus": "Fokusa saraksts", - "focus_description": "Rādīt vienu rindu katram ziņojumam un atvērt vēstuli pilnā platumā.", - "horizontal": "Lasīšanas rūts apakšā", - "horizontal_description": "Rādīt ziņojumu sarakstu augšā un atvērt atlasīto ziņojumu lasīšanas rūtī zem tā." - }, - "disable_threading": { - "label": "Izslēgt sarunu grupēšanu", - "description": "Rādīt vēstules kā atsevišķus ziņojumus, nevis sērijveidā" - }, - "plain_text_mode": { - "label": "Tikai parasts teksts", - "description": "Izslēgt formatētā teksta redaktoru un sūtīt vēstules tikai kā parastu tekstu" - }, - "auto_select_reply_identity": { - "label": "Automātiski izvēlēties atbildes adresi", - "description": "Atbildot automātiski izmantot to kontu, uz kuru vēstule tika saņemta" - }, - "reply_identity_match": { - "label": "Saņemšanas adreses atbilstība", - "description": "Kuras saņemšanas adreses tiek uzskatītas par jūsu. „Tikai precīza adrese“ izvēlas vienīgi kādu no jūsu konfigurētajām identitātēm; „tas pats domēns“ arī jebkuru citu adresi jūsu identitāšu domēnos uzskata par catch-all aizstājvārdu un pārraksta ar to galveni „No“. Izvēlieties precīzu adresi, ja šīs adreses ir izplatīšanas saraksti.", - "exact": "Tikai precīza adrese", - "domain": "Jebkura adrese manos domēnos" - }, - "signature_position": { - "label": "Paraksta novietojums", - "description": "Kur ievietot jūsu parakstu atbildēs un pārsūtīšanā. Virs citētā teksta tas dabiski lasās kā atbildes noslēgums; zem tā saglabā oriģinālo ziņojumu vienkopus.", - "above_quote": "Pirms citētā teksta", - "below_quote": "Pēc citētā teksta" - }, - "signature_separator": { - "label": "Paraksta atdalītājs", - "description": "Pirms paraksta pievienot standarta atdalītāju \"-- \" (RFC 3676). Izslēdziet, ja vēlaties pāriet no ziņojuma tieši uz parakstu." - }, - "sub_address_delimiter": { - "label": "Apakšadreses atdalītājs", - "description": "Zīme, kas atdala lietotājvārdu no apakšadreses tagu. Izvēlieties atdalītāju, ko lieto jūsu pasta serveris (piem. lietotajs{delimiter}tags@domens.lv).", - "option": "{delimiter} (lietotajs{delimiter}tags@domens.lv)", - "custom": "Pielāgots…", - "custom_input_label": "Pielāgota atdalītāja zīme" - }, - "show_preview": { - "label": "Rādīt priekšskatījuma tekstu", - "description": "Rādīt vēstules fragmentu sarakstā", - "focus_description": "Rādīt priekšskatījuma tekstu sarakstā" - }, - "attachment_click_action": { - "label": "Darbība, noklikšķinot uz pielikuma", - "description": "Izvēlieties priekšskatījumu vai tūlītēju lejupielādi", - "preview": "Priekšskatīt, ja iespējams", - "download": "Lejupielādēt nekavējoties" - }, - "attachment_position": { - "label": "Pielikumu novietojums", - "description": "Kur rādīt pielikumus vēstules galvenē", - "beside-sender": "Blakus sūtītājam", - "below-header": "Zem galvenes" - }, - "emails_per_page": { - "10": "10 vēstules", - "25": "25 vēstules", - "50": "50 vēstules", - "100": "100 vēstules", - "label": "Vēstules lapā", - "description": "Vienlaicīgi ielādējamo vēstuļu skaits" - }, - "always_light_mode": { - "label": "Vienmēr rādīt vēstules gaišajā režīmā", - "description": "Rādīt vēstules saturu gaišajā režīmā, pat ja lietotne ir tumšajā režīmā" - }, - "external_content": { - "label": "Ārējais saturs", - "description": "Kā apstrādāt attēlus un ārējo saturu", - "ask": "Vienmēr jautāt", - "block": "Vienmēr bloķēt", - "allow": "Vienmēr atļaut" - }, - "trusted_senders": { - "label": "Uzticamie sūtītāji", - "description": "Pārvaldiet sūtītājus, kuru attēli tiek ielādēti automātiski", - "count_zero": "Nav", - "count_one": "1 sūtītājs", - "count_other": "{count} sūtītāji", - "modal_title": "Uzticamie sūtītāji", - "empty_title": "Nav uzticamo sūtītāju", - "empty_description": "Skatot vēstuli ar bloķētiem attēliem, nospiediet \"Vienmēr uzticēties\", lai pievienotu sūtītāju šeit.", - "add_manually": "Pievienot sūtītāju manuāli", - "add_button": "Pievienot", - "add_placeholder": "Ievadiet e-pasta adresi", - "search_placeholder": "Meklēt sūtītājus...", - "no_results": "Sūtītāji nav atrasti", - "remove": "Noņemt", - "close": "Aizvērt", - "invalid_email": "Ievadiet derīgu e-pasta adresi", - "already_added": "Šis sūtītājs jau ir sarakstā", - "save_error": "Neizdevās saglabāt - sīkāk skatiet Kontaktu atkļūdošanas žurnālā", - "use_address_book_label": "Sinhronizēt ar adrešu grāmatu", - "use_address_book_description": "Saglabāt uzticamos sūtītājus atsevišķā adrešu grāmatā \"Uzticamie sūtītāji\", lai tie sinhronizētos visās jūsu ierīcēs" - }, - "hover_actions": { - "label": "Ātrās darbības, uzvirzot kursoru", - "description": "Izvēlieties darbības, kas redzamas, uzvirzot kursoru uz vēstules sarakstā", - "delete": "Dzēst", - "star": "Zvaigznīte", - "mark_read": "Izlasīts/Nelasīts", - "archive": "Arhivēt", - "tag": "Tags", - "spam": "Mēstule", - "not_spam": "Nav mēstule", - "none_selected": "Nav atlasītu darbību", - "mode_label": "Attēlošanas režīms", - "mode_inline": "Iebūvēts", - "mode_floating": "Peldošs", - "corner_label": "Peldošo darbību pozīcija", - "corner_top-left": "Augšā pa kreisi", - "corner_top-right": "Augšā pa labi", - "corner_bottom-left": "Apakšā pa kreisi", - "corner_bottom-right": "Apakšā pa labi" - }, - "default_mail_program": { - "label": "Noklusējuma pasta programma", - "description": "Reģistrēt {appName} kā noklusējuma programmu mailto: saitēm", - "button": "Iestatīt kā noklusējumu", - "success": "Pārlūkam nosūtīts pieprasījums iestatīt kā noklusējumu", - "error": "Jūsu pārlūks neatbalsta šo funkciju" - }, - "attachment_reminder": { - "label": "Pielikumu atgādinājums", - "description": "Brīdināt pirms sūtīšanas, ja ziņojumā minēti pielikumi, bet neviens nav pievienots", - "keywords_label": "Aktivizēšanas atslēgvārdi", - "keywords_description": "Vārdi vai frāzes, kas aktivizē atgādinājumu, ja tie tiek atrasti jūsu ziņojumā", - "add_placeholder": "Pievienot atslēgvārdu...", - "add": "Add", - "remove": "Noņemt" - }, - "empty_subject_warning": { - "label": "Brīdinājums par tukšu tematu", - "description": "Pieprasīt apstiprinājumu pirms ziņas sūtīšanas bez temata." - }, - "hide_inline_image_attachments": { - "label": "Slēpt iegultos attēlus no pielikumu saraksta", - "description": "Ziņojuma pamattekstā iegultie attēli netiek rādīti kā atsevišķi pielikumi" - }, - "attachment_image_previews": { - "label": "Rādīt attēlu priekšskatījumus pielikumos", - "description": "Rādīt attēlu pielikumus kā sīktēlu kartītes, nevis vispārīgas failu ikonas" - }, - "send_delay": { - "label": "Atsaukt sūtīšanu / sūtīšanas aizture", - "description": "Aizturiet parastu sūtīšanu īsā servera puses logā.", - "off": "Izslēgts", - "seconds": "{seconds} sekundes", - "unsupported": "Pašreizējais konts neziņo par aizturētas sūtīšanas atbalstu. Iestatījums joprojām tiks saglabāts citiem kontiem." - }, - "return_to_list_after_action": { - "label": "Atgriezties sarakstā pēc dzēšanas vai atzīmēšanas kā nelasītu", - "description": "Pēc atvērtā ziņojuma dzēšanas vai atzīmēšanas kā nelasīta atgriezieties ziņojumu sarakstā, nevis atveriet nākamo ziņojumu." - }, - "swipe_right_action": { - "label": "Darbība, velkot pa labi (mobilajā ierīcē)", - "description": "Skārienierīcēs darbība, kas tiek veikta, kad sarakstā ziņu pavelk pa labi." - }, - "swipe_left_action": { - "label": "Darbība, velkot pa kreisi (mobilajā ierīcē)", - "description": "Skārienierīcēs darbība, kas tiek veikta, kad sarakstā ziņu pavelk pa kreisi." - }, - "swipe_actions": { - "none": "Nav", - "archive": "Arhivēt", - "delete": "Dzēst", - "mark_read": "Pārslēgt lasīts/nelasīts", - "star": "Pārslēgt zvaigznīti", - "spam": "Atzīmēt kā mēstuli" - }, - "rtl_editing": { - "label": "Rakstīšanas no labās uz kreiso atbalsts", - "description": "Pievieno redaktora rīkjoslai virziena pogu, ar kuru rindkopas var iestatīt no kreisās uz labo vai no labās uz kreiso" - } - }, - "composer": { - "title": "Redaktors", - "description": "Pielāgojiet vēstuļu rakstīšanas parametrus", - "autosave": { - "label": "Automātiskās saglabāšanas intervāls", - "description": "Cik bieži automātiski saglabāt melnrakstus", - "30s": "Ik pēc 30 sekundēm", - "1m": "Katru minūti", - "2m": "Ik pēc 2 minūtēm", - "5m": "Ik pēc 5 minūtēm" - }, - "send_confirmation": { - "label": "Sūtīšanas apstiprinājums", - "description": "Jautāt apstiprinājumu pirms vēstules sūtīšanas" - }, - "default_reply": { - "label": "Noklusējuma atbildes režīms", - "description": "Darbība, nospiežot \"Atbildēt\"", - "reply": "Atbildēt", - "reply_all": "Atbildēt visiem" - } - }, - "privacy": { - "title": "Privātums un drošība", - "description": "Pārvaldiet privātuma un drošības iestatījumus", - "external_images": { - "label": "Bloķēt ārējos attēlus", - "description": "Novērst izsekošanu caur ārējiem attēliem" - }, - "session_timeout": { - "label": "Sesijas noildze", - "description": "Automātiski izrakstīties pēc dīkstāves", - "never": "Nekad", - "30m": "30 minūtes", - "1h": "1 stunda", - "4h": "4 stundas" - }, - "clear_cache": { - "label": "Notīrīt kešatmiņu", - "description": "Dzēst kešotos datus un pagaidu failus", - "button": "Notīrīt kešatmiņu", - "confirm": "Vai tiešām vēlaties notīrīt kešatmiņu?", - "success": "Kešatmiņa veiksmīgi notīrīta" - } - }, - "account": { - "title": "Konts", - "description": "Skatīt sava konta informāciju", - "name_label": "Redzamais vārds", - "username_label": "Lietotājvārds", - "account_type_label": "Konta veids", - "auth_method_label": "Autentifikācija", - "auth_method_oauth": "Vienotā pierakstīšanās (OAuth/OIDC)", - "auth_method_basic": "Parole", - "demo_account": "Demo konts", - "email": { - "label": "E-pasta adrese", - "value": "{email}" - }, - "server": { - "label": "JMAP serveris", - "value": "{server}" - }, - "storage": { - "label": "Krātuves izmantošana", - "used": "Izmantots {used} no {total}", - "percentage": "Izmantots {percent}%" - }, - "last_sync": { - "label": "Pēdējā sinhronizācija", - "value": "{time}" - }, - "accounts": { - "title": "Pieteiktie konti", - "description": "Velciet, lai mainītu kontu secību kontu izvēlnē", - "active": "Pašlaik aktīvais konts", - "default_badge": "Noklusējuma konts", - "set_default": "Iestatīt kā noklusējumu", - "switch_to": "Pārslēgties uz šo kontu", - "move_up": "Pārvietot uz augšu", - "move_down": "Pārvietot uz leju", - "drag_handle": "Velciet, lai pārkārtotu", - "add": "Pievienot kontu" - }, - "shared_accounts": { - "title": "Kopīgots ar mani", - "description": "Grupas un kopīgotie konti, kurus varat pārvaldīt. Atlasiet vienu, lai rediģētu tā filtrus, prombūtnes atbildētāju, kalendārus un kontaktus.", - "shared_label": "Kopīgots konts" - } - }, - "security": { - "title": "Konta drošība", - "description": "Pārvaldiet paroli, 2FA un drošības iestatījumus", - "detecting": "Nosaka servera iespējas...", - "not_available": "Konta drošības pārvaldība šim serverim nav pieejama.", - "password": { - "title": "Mainīt paroli", - "current": "Pašreizējā parole", - "new": "Jaunā parole", - "confirm": "Apstipriniet jauno paroli", - "submit": "Mainīt paroli", - "success": "Parole veiksmīgi mainīta", - "error_title": "Neizdevās mainīt paroli", - "error_mismatch": "Jaunās paroles nesakrīt", - "error_min_length": "Parolei jābūt vismaz 8 rakstzīmes garai", - "error_generic": "Neizdevās mainīt paroli" - }, - "display_name": { - "label": "Redzamais vārds", - "description": "Jūsu vārds, kāds tas redzams serverī", - "save": "Saglabāt", - "success": "Redzamais vārds atjaunināts", - "error": "Neizdevās atjaunināt redzamo vārdu", - "placeholder": "Ievadiet savu parādāmo vārdu" - }, - "totp": { - "section_title": "Divfaktoru autentifikācija", - "label": "TOTP autentifikācija", - "description": "Pievienojiet papildu drošības slāni ar vienreizēju paroli", - "active": "Iespējota", - "inactive": "Izslēgta", - "enabled": "2FA ir iespējota", - "disabled": "2FA ir izslēgta", - "enable_error": "Neizdevās iespējot 2FA", - "disable_error": "Neizdevās izslēgt 2FA", - "setup_instructions": "Nokopējiet šo URL savā autentifikācijas lietotnē (Google Authenticator, Authy utt.):", - "verification_code": "Verifikācijas kods", - "confirm": "Apstiprināt", - "disable": "Atspējot", - "disable_confirm_prompt": "Ievadiet paroli, lai atspējotu divpakāpju autentifikāciju.", - "password_required": "Nepieciešama parole", - "code_required": "Nepieciešams verifikācijas kods", - "code_invalid": "Nederīgs verifikācijas kods. Pārbaudiet autentifikācijas lietotni un mēģiniet vēlreiz." - }, - "app_passwords": { - "title": "Lietotņu paroles", - "description": "Izveidojiet paroles lietotnēm, kas neatbalsta 2FA", - "add": "Pievienot", - "create": "Izveidot", - "cancel": "Atcelt", - "generate": "Ģenerēt", - "name_label": "Lietotnes nosaukums", - "name_placeholder": "piem., Thunderbird, iPhone Mail", - "password_label": "Parole (atstājiet tukšu automātiskai ģenerēšanai)", - "password_placeholder": "Automātiska ģenerēšana, ja tukšs", - "added": "Lietotnes parole izveidota", - "removed": "Lietotnes parole izdzēsta", - "add_error": "Neizdevās izveidot lietotnes paroli", - "remove_error": "Neizdevās izdzēst lietotnes paroli", - "none": "Lietotņu paroles nav iestatītas", - "done": "Gatavs", - "expires_label": "Derīguma termiņš (pēc izvēles)", - "copy_now_warning": "Kopējiet šo paroli tagad - tā vairs netiks rādīta.", - "allowed_ips_label": "Atļautās IP (neobligāti)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Atdalītas ar komatu vai atstarpi. Atstājiet tukšu, lai atļautu jebkuru IP." - }, - "api_keys": { - "title": "API atslēgas", - "description": "Izveidojiet API atslēgas skriptiem un integrācijām, kas tieši sazinās ar serveri", - "name_label": "Atslēgas nosaukums", - "name_placeholder": "piem. Dublēšanas skripts, CI palaidējs", - "copy_now_warning": "Kopējiet šo API atslēgu tagad - tā vairs netiks parādīta.", - "added": "API atslēga izveidota", - "removed": "API atslēga noņemta", - "add_error": "Neizdevās izveidot API atslēgu", - "remove_error": "Neizdevās noņemt API atslēgu", - "none": "API atslēgas nav konfigurētas" - }, - "encryption": { - "section_title": "Krātuves šifrēšana", - "label": "E-pasta šifrēšana", - "description": "Šifrēt vēstules serverī papildu privātumam", - "active": "Šifrēšana {type} ir ieslēgta", - "inactive": "Izslēgta", - "enabled": "Krātuves šifrēšana ir iespējota", - "disabled_success": "Krātuves šifrēšana ir izslēgta", - "error": "Neizdevās atjaunināt šifrēšanas iestatījumus", - "algorithm_label": "Šifrēšanas algoritms", - "encrypt_on_append": "Šifrēt jaunās vēstules to augšupielādes laikā", - "allow_spam_training": "Atļaut mēstuļu filtra apmācību pirms vēstuļu šifrēšanas", - "enable_encryption": "Ieslēgt šifrēšanu", - "enable_tooltip": "Ieslēgt šifrēšanu ar šo atslēgu", - "configure_title": "Ieslēgt šifrēšanu ar šo atslēgu", - "enable_button": "Ieslēgt", - "disable_tooltip": "Izslēgt šifrēšanu", - "enabled_success": "Krātuves šifrēšana ir iespējota" - }, - "email_client": { - "title": "Pasta klienta iestatīšana", - "description": "Izmantojiet šos datus, lai iestatītu Thunderbird, Apple Mail vai Outlook", - "jmap_username_label": "JMAP lietotājvārds", - "copy": "Kopēt", - "copied": "Nokopēts", - "password_instructions": "Izmantojiet norādīto JMAP lietotājvārdu kopā ar lietotnes paroli. Izveidojiet lietotnes paroli sadaļā augstāk." - }, - "link_device": { - "title": "Saistīt mobilo lietotni", - "description": "Pierakstieties Bulwark Mail mobilajā lietotnē, neko nerakstot. Šeit izveidojiet QR kodu un noskenējiet to lietotnes pierakstīšanās ekrānā.", - "generate": "Rādīt QR kodu", - "regenerate": "Rādīt jaunu kodu", - "instructions": "Atveriet Bulwark Mail lietotni, pierakstīšanās ekrānā pieskarieties \"Skenēt QR kodu\" un pavērsiet kameru šeit.", - "expires_in": "Šī koda derīgums beigsies pēc {seconds} sekundēm. To var izmantot tikai vienu reizi.", - "expired": "Šī koda derīgums ir beidzies.", - "generating": "Ģenerē…", - "error": "Neizdevās izveidot pārošanas kodu. Lūdzu, mēģiniet vēlreiz." - }, - "public_keys": { - "name_label": "Publiskās atslēgas nosaukums", - "name_placeholder": "piem., Mana atslēga", - "key_label": "Publiskā atslēga (S/MIME vai PGP ASCII formātā)", - "key_placeholder": "Ielīmējiet šeit savu publisko atslēgu", - "none": "Nav konfigurēta neviena publiskā atslēga" - } - }, - "identities": { - "title": "Sūtītāja identitātes", - "description": "Pārvaldiet e-pasta adreses, no kurām varat sūtīt vēstules", - "identities_count": { - "label": "Jūsu identitātes", - "description": "Konfigurētās sūtīšanas adreses", - "count_zero": "Nav identitāšu", - "count_one": "1 identitāte", - "count_other": "{count} identitātes" - }, - "manage": "Pārvaldīt identitātes", - "sub_addressing": { - "label": "Apakšadresēšana (Sub-addressing)", - "description": "Izmantojiet lietotajs+tags@domens.lv, lai organizētu ienākošo pastu", - "learn_more": "Uzzināt vairāk" - } - }, - "vacation": { - "title": "Atvaļinājuma atbilde", - "description": "Automātiski atbildēt uz ienākošajām vēstulēm prombūtnes laikā", - "loading": "Ielādē atvaļinājuma iestatījumus...", - "not_supported": "Jūsu pasta serveris neatbalsta atvaļinājuma atbildes.", - "fetch_error": "Neizdevās ielādēt atvaļinājuma iestatījumus.", - "status": { - "label": "Atvaļinājuma atbilde", - "description": "Sūtīt automātisku atbildi uz ienākošajām vēstulēm", - "active": "Aktīva", - "inactive": "Neaktīva" - }, - "date_range": { - "title": "Datumu diapazons", - "description": "Ierobežojiet atbildi noteiktā periodā (pēc izvēles)", - "start": "Sākuma datums", - "start_description": "Atstājiet tukšu, lai nebūtu ierobežojuma", - "end": "Beigu datums", - "end_description": "Atstājiet tukšu, lai nebūtu ierobežojuma" - }, - "message": { - "title": "Atbildes ziņojums", - "description": "Ziņojums, kas tiks nosūtīts kā atbilde", - "subject_label": "Temats", - "subject_description": "Atvaļinājuma atbildes temats", - "subject_placeholder": "Atvaļinājumā / Esmu prom", - "body_label": "Ziņojuma teksts", - "body_description": "Ziņojuma saturs parastā tekstā", - "body_placeholder": "Paldies par e-pastu. Pašlaik esmu prombūtnē un atbildēšu, kad atgriezīšos.", - "html_label": "Formatēts ziņojums (HTML)", - "html_description": "Pievienojiet formatētu versiju ar saitēm un noformējumu. Saņēmēji, kuru e-pasta programma to nevar parādīt, redzēs iepriekš redzamo vienkāršo tekstu.", - "html_placeholder": "Rakstiet formatētu prombūtnes atbildi…" - }, - "preview": { - "title": "Priekšskatījums", - "show": "Rādīt priekšskatījumu", - "hide": "Slēpt priekšskatījumu" - }, - "save": "Saglabāt izmaiņas", - "saving": "Saglabā...", - "warnings": { - "end_before_start": "Beigu datumam jābūt vēlāk par sākuma datumu", - "start_in_past": "Sākuma datums ir pagātnē", - "empty_body": "Ziņojuma teksts ir tukšs - saņēmēji saņems tukšu atbildi", - "subject_too_long": "Temats serverim ir pārāk garš (ne vairāk kā {max} baiti).", - "body_too_long": "Ziņa serverim ir pārāk gara (ne vairāk kā {max} baiti, ieskaitot formatējumu)." - } - }, - "folders": { - "role_memos": "Piezīmes", - "title": "Mapes", - "description": "Pārvaldiet pasta mapes un piešķiriet tām lomas", - "folder_list": "Jūsu mapes", - "folder_list_description": "Noklikšķiniet uz mapes ikonas, lai to konfigurētu", - "standard_roles": "Standarta mapju lomas", - "standard_roles_description": "Piešķiriet mapes tādām lomām kā Iesūtne, Nosūtītie, Atkritne utt.", - "role_inbox": "Iesūtne", - "role_drafts": "Melnraksti", - "role_sent": "Nosūtītie", - "role_trash": "Atkritne", - "role_junk": "Mēstules", - "role_archive": "Arhīvs", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Nav", - "create_folder": "Izveidot mapi", - "create_subfolder": "Izveidot apakšmapi", - "reorder": "Velciet, lai pārkārtotu", - "reorder_error": "Neizdevās pārkārtot mapes", - "move_error": "Neizdevās pārvietot mapi", - "subfolder_of": "Mapē {name}", - "subfolder_name": "Apakšmapes nosaukums", - "new_folder_name": "Mapes nosaukums", - "rename": "Pārdēvēt", - "change_icon": "Mainīt ikonu", - "delete": "Dzēst", - "confirm_delete": "Vai tiešām vēlaties dzēst \"{name}\"? Vēstules tiks pārvietotas uz atkritni.", - "create": "Izveidot", - "cancel": "Atcelt", - "no_folders": "Nav pielāgotu mapju", - "cannot_delete_role": "Nevar dzēst mapi ar standarta lomu. Vispirms noņemiet lomu.", - "folder_created": "Mape izveidota", - "folder_renamed": "Mape pārdēvēta", - "folder_deleted": "Mape izdzēsta", - "role_updated": "Mapes loma atjaunināta", - "error_create": "Neizdevās izveidot mapi", - "error_rename": "Neizdevās pārdēvēt mapi", - "error_delete": "Neizdevās izdzēst mapi", - "error_role": "Neizdevās atjaunināt mapes lomu", - "error_delete_has_children": "Nevar dzēst mapi: tajā joprojām ir apakšmapes. Vispirms tās dzēsiet vai pārvietojiet.", - "error_delete_has_email": "Nevar dzēst mapi: tajā joprojām ir e-pasti. Vispirms tos pārvietojiet vai dzēsiet." - }, - "advanced": { - "title": "Papildu", - "description": "Paplašinātie iestatījumi un izstrādātāja rīki", - "debug_mode": { - "label": "Atkļūdošanas režīms", - "description": "Iespējot detalizētu žurnālēšanu diagnostikai" - }, - "settings_sync": { - "label": "Iestatījumu sinhronizācija", - "description": "Sinhronizēt iestatījumus starp dažādiem pārlūkiem un ierīcēm" - }, - "sender_favicons": { - "label": "Sūtītāju ikonas", - "description": "Rādīt vietņu ikonas kā sūtītāju avatarus" - }, - "show_avatars_in_junk": { - "label": "Rādīt avatarus mēstuļu mapē", - "description": "Rādīt sūtītāju profila attēlus un ikonas mēstuļu mapē. Pēc noklusējuma izslēgts, lai pikšķerēšanas mēģinājumi neizskatītos uzticami." - }, - "keyboard_shortcuts": { - "label": "Īsinājumtaustiņi", - "description": "Skatīt pieejamos tastatūras īsinājumtaustiņus", - "button": "Rādīt īsinājumtaustiņus" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Atiestatīt iestatījumus", - "description": "Atjaunot visus iestatījumus uz noklusējuma vērtībām", - "button": "Atiestatīt" - }, - "export_settings": { - "label": "Eksportēt iestatījumus", - "description": "Lejupielādēt iestatījumus JSON formātā", - "button": "Eksportēt" - }, - "import_settings": { - "label": "Importēt iestatījumus", - "description": "Augšupielādēt iestatījumus no JSON faila", - "button": "Importēt" - }, - "debug_categories": { - "description": "Izvēlieties, kuras kategorijas žurnalēt. Atspējojiet nevajadzīgās kategorijas, lai samazinātu konsoles troksni.", - "jmap": "JMAP klients", - "jmap_description": "Pastkastes darbības, e-pastu ielāde un JMAP protokola pieprasījumi", - "calendar": "Kalendārs", - "calendar_description": "Kalendāra notikumi, importi un plānošanas ziņas", - "tasks": "Uzdevumi", - "tasks_description": "Kalendāra uzdevumu izveide, ielāde un atjaunināšana", - "auth": "Autentifikācija", - "auth_description": "Pieteikšanās, TOTP, tokenu apmaiņa un sesiju pārvaldība", - "filters": "Filtri", - "filters_description": "Sieve filtru noteikumi un prombūtnes skripti", - "email": "E-pasta skatīšana", - "email_description": "E-pasta renderēšana, TNEF apstrāde un atzīmēšana kā lasīts", - "push": "Push paziņojumi", - "push_description": "Push paziņojumu iestatīšana un piegāde", - "contacts": "Kontakti un adrešu grāmatas", - "contacts_description": "Kontaktu sinhronizācija, adrešu grāmatu darbības un uzticamie sūtītāji" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Sānu joslas lietotnes", - "description": "Pārvaldiet pielāgotas lietotnes un saites sānu joslā", - "keep_loaded": "Turēt lietotnes ielādētas", - "keep_loaded_description": "Turēt iebūvētās lietotnes fonā, lai izvairītos no pārlādēšanas", - "managed_title": "Nodrošina administrators", - "managed_description": "Šīs lietotnes ir iestatītas visiem, un tās nevar mainīt.", - "manage_title": "Pielāgotas lietotnes", - "manage_description": "Pievienojiet, rediģējiet vai dzēsiet sānu joslas lietotnes" - }, - "contacts": { - "title": "Kontakti", - "description": "Importējiet un eksportējiet savus kontaktus", - "import_label": "Kontaktu imports", - "import_description": "Importēt kontaktus no vCard faila (.vcf)", - "export_label": "Kontaktu eksports", - "export_description": "Eksportēt visus kontaktus vCard failā (.vcf)", - "manage_title": "Adrešu grāmatas", - "manage_description": "Pārdēvēt savas adrešu grāmatas", - "no_address_books": "Adrešu grāmatas nav atrastas", - "categories_title": "Kategorijas", - "categories_description": "Pārdēvēt kontaktu kategorijas", - "no_categories": "Kategorijas nav atrastas", - "group_by_letter_description": "Rādīt alfabētiskos sadaļu virsrakstus kontaktu sarakstā", - "group_by_letter_label": "Grupēt pēc pirmā burta", - "sort_by_last_name_label": "Kārtot pēc uzvārda", - "sort_by_last_name_description": "Kārtot kontaktu sarakstu pēc uzvārda, lai ģimenes locekļi būtu kopā" - }, - "downloads": { - "title": "Lejupielādes", - "description": "Pielāgojiet, kā tiek nosaukti lejupielādētie e-pasti un pielikumi.", - "reset": "Atjaunot noklusējumu", - "preview": "Priekšskatījums:", - "email_template": { - "label": "E-pasta faila nosaukums (.eml)", - "description": "Veidne, ko izmanto, eksportējot e-pastu vai velkot to uz failu sistēmu. Paplašinājums .eml tiek pievienots automātiski." - }, - "attachment_template": { - "label": "Pielikuma faila nosaukums", - "description": "Veidne, ko izmanto, lejupielādējot vai velkot pielikumu. Ja izlaižat '{filename}' un '{ext}', sākotnējais paplašinājums tiek saglabāts." - }, - "bundle_template": { - "label": "Vairāku e-pastu .zip faila nosaukums", - "description": "Veidne, ko izmanto, kad velkat vai lejupielādējat vairākus atlasītos e-pastus kā vienu .zip arhīvu. Paplašinājums .zip tiek pievienots automātiski." - }, - "spaces": { - "label": "Atstarpes", - "description": "Aizstāt atstarpes izvades faila nosaukumā ar citu rakstzīmi.", - "keep": "Saglabāt atstarpes", - "underscore": "Aizstāt ar _", - "dash": "Aizstāt ar -" - }, - "lowercase": { - "label": "Mazie burti", - "description": "Piespiest visu faila nosaukumu uz mazajiem burtiem." - }, - "strip_diacritics": { - "label": "Noņemt diakritikas zīmes", - "description": "Pārvērst burtus ar akcentiem to ASCII ekvivalentos (ä → a, é → e). Noderīgi rīkiem, kas slikti apstrādā Unicode failu nosaukumus." - }, - "collapse_separators": { - "label": "Apvienot atkārtotus atdalītājus", - "description": "Apvienot virknes atstarpju, pasvītrojumu vai defisu vienā rakstzīmē." - }, - "after_export": { - "label": "Pēc eksporta", - "description": "Pēc e-pasta eksportēšanas kā .eml to var pārvietot.", - "keep": "Paturēt pastkastē", - "archive": "Pārvietot uz arhīvu", - "trash": "Pārvietot uz miskasti" - } - }, - "filters": { - "title": "Pasta filtri", - "description": "Izveidojiet noteikumus automātiskai vēstuļu šķirošanai un pārvaldībai", - "add_rule": "Pievienot noteikumu", - "no_rules": "Nav filtra noteikumu", - "vacation_active": "Atvaļinājuma atbilde ir aktīva", - "vacation_active_description": "Automātiska atbilde ir ieslēgta ienākošajām vēstulēm", - "vacation_configure": "Konfigurēt", - "no_rules_description": "Izveidojiet noteikumus automātiskai ienākošā pasta organizēšanai", - "edit_rule": "Rediģēt noteikumu", - "new_rule": "Jauns noteikums", - "delete_rule": "Dzēst noteikumu", - "delete_confirm": "Vai tiešām vēlaties dzēst šo noteikumu?", - "enable": "Ieslēgt", - "disable": "Izslēgt", - "raw_editor": "Sieve redaktors", - "raw_editor_warning": "Sieve skripta rediģēšana var sabojāt vizuālo noteikumu konstruktoru.", - "validate": "Pārbaudīt", - "validation_success": "Skripts ir derīgs", - "validation_error": "Skriptā ir kļūdas", - "save": "Saglabāt noteikumus", - "saving": "Saglabā...", - "saved": "Filtri veiksmīgi saglabāti", - "save_failed": "Neizdevās saglabāt filtrus", - "loading": "Ielādē filtrus...", - "not_supported": "Jūsu pasta serveris neatbalsta filtrus.", - "rule_name": "Noteikuma nosaukums", - "rule_name_placeholder": "piem., Ziņu izdevumu šķirošana", - "match_all": "Atbilst VISIEM nosacījumiem", - "match_any": "Atbilst JEBKURAM nosacījumam", - "conditions": "Nosacījumi", - "add_condition": "Pievienot nosacījumu", - "actions": "Darbības", - "add_action": "Pievienot darbību", - "stop_processing": "Pārtraukt nākamo noteikumu apstrādi", - "include_spam": "Pārvietot arī ziņas, kas atzīmētas kā mēstules", - "attachment_type_placeholder": "piem. pdf, doc, jpg", - "value_placeholder_multi": "Vērtība (vairākas atdalītas ar komatu)", - "condition_fields": { - "attachment": "Pielikums", - "from": "No", - "to": "Kam", - "cc": "Kopija", - "subject": "Temats", - "header": "Pielāgota galvene", - "size": "Izmērs", - "body": "Teksts" - }, - "comparators": { - "has_any": "ir klāt", - "has_type": "tipa", - "contains": "satur", - "not_contains": "nesatur", - "is": "precīzi sakrīt", - "not_is": "nav vienāds", - "starts_with": "sākas ar", - "ends_with": "beidzas ar", - "matches": "atbilst paraugam", - "greater_than": "lielāks nekā", - "less_than": "mazāks nekā" - }, - "action_types": { - "move": "Pārvietot uz mapi", - "copy": "Kopēt uz mapi", - "forward": "Pārsūtīt uz", - "mark_read": "Atzīmēt kā izlasītu", - "star": "Pievienot zvaigznīti", - "add_label": "Pievienot tagu", - "discard": "Dzēst (bez paziņojuma)", - "reject": "Noraidīt ar ziņojumu", - "keep": "Atstāt iesūtnē", - "stop": "Pārtraukt apstrādi" - }, - "move_to_folder": "Izvēlēties mapi", - "copy_to_folder": "Izvēlēties mapi", - "forward_to": "Pārsūtīt uz e-pastu", - "forward_placeholder": "lietotajs@piemers.lv", - "forward_keep_copy": "Saglabāt kopiju", - "forward_limit": "Pārsūtīšanas ierobežojums vienai ziņai šajā serverī: {count}. Papildu pārsūtīšanas tiek izlaistas.", - "reject_message": "Noraidīšanas ziņojums", - "reject_placeholder": "Jūsu e-pasts tika noraidīts", - "label_name": "Taga nosaukums", - "label_placeholder": "Izvēlēties tagu", - "header_name": "Galvenes nosaukums", - "header_placeholder": "piem., X-Mailing-List", - "size_bytes": "Izmērs baitos", - "size_placeholder": "piem., 1000000", - "system_managed": "Sistēmas pārvaldīts noteikums", - "opaque_warning": "Šis skripts tika rediģēts ārpus vizuālā konstruktora. Pieejama tikai Sieve rediģēšana.", - "open_sieve_editor": "Atvērt Sieve redaktoru", - "fetch_error": "Neizdevās ielādēt filtrus", - "expanded_view": "Izvērstais skats", - "expanded_view_description": "Rādīt filtra noteikumus ar detalizētiem nosacījumiem un darbībām", - "if": "Ja", - "then": "Tad", - "match_all_conditions": "atbilst visi", - "match_any_condition": "atbilst jebkurš", - "and": "un", - "or": "vai", - "cancel": "Atcelt", - "confirm_delete": "Dzēst", - "rule_list": "Filtru noteikumi", - "drag_to_reorder": "Velciet, lai mainītu secību", - "match_type": "Atbilstības veids", - "reset_to_visual": "Atiestatīt uz vizuālo konstruktoru", - "reset_warning": "Tas dzēsīs pašreizējo skriptu un sāks no jauna.", - "confirm_reset": "Atiestatīt", - "validation_empty_name": "Nepieciešams noteikuma nosaukums", - "validation_empty_conditions": "Nepieciešams vismaz viens nosacījums ar vērtību", - "validation_empty_actions": "Nepieciešama vismaz viena darbība", - "templates_section": "Sākt no veidnes", - "template_newsletters": "Pārvietot ziņu izdevumus uz mapi", - "template_receipts": "Automātiski arhivēt rēķinus", - "template_important": "Atzīmēt svarīgas vēstules", - "template_notifications": "Filtrēt paziņojumus", - "sieve_editor": { - "title": "Sieve skripta redaktors", - "warning": "Sieve skripta rediģēšana var sabojāt vizuālo konstruktoru.", - "script_content": "Sieve skripts", - "valid": "Skripts ir derīgs", - "invalid": "Skriptā ir kļūdas", - "save_warning": "Saglabāšana pārrakstīs vizuālos noteikumus. To nevar atcelt. Nospiediet \"Saglabāt\" vēlreiz.", - "validating": "Pārbauda...", - "validate": "Pārbaudīt", - "cancel": "Atcelt", - "save": "Saglabāt", - "confirm_save": "Apstiprināt saglabāšanu", - "validation_failed": "Pārbaudes pieprasījuma kļūda" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# nosacījums} other {# nosacījumi}}", - "actions_count": "{count, plural, one {# darbība} other {# darbības}}" - }, - "origin_external": "Ārējs", - "managed_by_tooltip": "Pārvalda {source}. Rediģējiet to šajā lietotnē vai izmantojiet neapstrādāto Sieve redaktoru." - }, - "templates": { - "title": "Vēstuļu veidnes", - "description": "Izveidojiet atkārtoti lietojamas veidnes ar mainīgajiem lielumiem", - "add": "Jauna veidne", - "edit": "Rediģēt veidni", - "name": "Veidnes nosaukums", - "name_placeholder": "piem., Atgādinājuma e-pasts", - "category": "Kategorija", - "category_placeholder": "piem., Darbs, Personīgi", - "subject": "Temats", - "subject_placeholder": "Vēstules temats", - "body": "Teksts", - "body_placeholder": "Vēstules saturs...", - "recipients_placeholder": "lietotajs@piemers.lv", - "identity": "Sūtīt kā", - "default_identity": "Noklusējuma identitāte", - "favorite": "Izlase", - "cancel": "Atcelt", - "create": "Izveidot", - "update": "Atjaunināt", - "confirm_delete": "Dzēst", - "no_templates": "Nav veidņu", - "manage": "Pārvaldīt veidnes", - "count": "{count, plural, one {# veidne} other {# veidnes}}", - "export_import": "Eksports un imports", - "export_import_description": "Dublējiet veidnes vai pārvietojiet uz citu ierīci", - "export": "Eksportēt", - "import": "Importēt", - "validation": { - "empty": "Veidnes nosaukums ir obligāts", - "too_long": "Veidnes nosaukums nedrīkst pārsniegt 200 rakstzīmes" - } - }, - "files": { - "display": { - "title": "Attēlošana", - "description": "Pielāgojiet failu un mapju attēlošanu" - }, - "folder_layout": { - "label": "Mapju navigācija", - "description": "Izvēlieties, kā rādīt mapes: kopā ar failiem vai sānu joslā", - "inline": "Kopā ar failiem", - "sidebar": "Sānu josla" - }, - "default_view": { - "label": "Noklusējuma skats", - "description": "Izvēlieties starp režģi un sarakstu", - "list": "Saraksts", - "grid": "Režģis" - }, - "default_sort": { - "label": "Noklusējuma kārtošana", - "description": "Izvēlieties failu kārtošanas veidu", - "name": "Nosaukums", - "size": "Izmērs", - "modified": "Modificēts" - }, - "sort_direction": { - "label": "Kārtošanas virziens", - "description": "Augošā vai dilstošā secībā", - "ascending": "Augošā", - "descending": "Dilstošā" - }, - "icons": { - "title": "Ikonas", - "description": "Pielāgojiet failu ikonu izskatu" - }, - "show_icons": { - "label": "Rādīt failu ikonas", - "description": "Rādīt ikonas blakus failiem un mapēm" - }, - "colored_icons": { - "label": "Krāsainas ikonas", - "description": "Izmantot krāsainas ikonas melnbalto vietā" - }, - "show_thumbnails": { - "label": "Rādīt sīktēlus", - "description": "Rādīt attēlu priekšskatījumus ikonu vietā" - }, - "behavior": { - "title": "Darbība", - "description": "Pielāgojiet failu pārvaldnieka darbību" - }, - "show_hidden": { - "label": "Rādīt slēptos failus", - "description": "Rādīt failus un mapes, kas sākas ar punktu" - }, - "preview": { - "label": "Priekšskatījums" - } - }, - "search_placeholder": "Meklēt iestatījumos", - "search_clear": "Notīrīt meklēšanu", - "search_no_results": "Nav atbilstošu iestatījumu", - "scoped": { - "back": "Atpakaļ uz manu kontu", - "managing": "Pārvalda: {name}" - } - }, - "errors": { - "page_error_title": "Kaut kas nogāja griezi", - "page_error_description": "Notikusi neparedzēta kļūda. Lūdzu, mēģiniet vēlreiz vai atgriezieties sākumlapā.", - "sidebar_error": "Neizdevās ielādēt pastkastes", - "email_list_error": "Neizdevās ielādēt vēstules", - "viewer_error_title": "Neizdevās parādīt vēstuli", - "viewer_error_description": "Radās problēma, attēlojot šo vēstuli. Tā var saturēt neatbalstītu saturu.", - "composer_error": "Neizdevās ielādēt vēstuļu redaktoru", - "settings_error_title": "Iestatījumi nav pieejami", - "settings_error_description": "Neizdevās ielādēt iestatījumus. Jūsu izvēles var netikt saglabātas.", - "try_again": "Mēģināt vēlreiz", - "reload": "Pārlādēt", - "reload_emails": "Pārlādēt vēstules", - "reload_settings": "Pārlādēt iestatījumus", - "retry": "Mēģināt vēlreiz", - "go_home": "Uz iesūtni" - }, - "context_menu": { - "reply": "Atbildēt", - "reply_all": "Atbildēt visiem", - "forward": "Pārsūtīt", - "mark_read": "Atzīmēt kā izlasītu", - "mark_unread": "Atzīmēt kā nelasītu", - "star": "Pievienot zvaigznīti", - "unstar": "Noņemt zvaigznīti", - "pin": "Piespraust", - "unpin": "Atspraust", - "move_to": "Pārvietot uz...", - "archive": "Arhivēt", - "delete": "Dzēst", - "mark_as_spam": "Atzīmēt kā mēstuli", - "not_spam": "Nav mēstule", - "tag": "Tags", - "items_selected": "{count} vēstules atlasītas", - "edit_draft": "Rediģēt melnrakstu", - "cancel_scheduled_send": "Atcelt sūtīšanu", - "reschedule_send": "Pārplānot", - "cancel_and_edit": "Atcelt un rediģēt", - "cancel_and_compose_again": "Atcelt un rakstīt no jauna" - }, - "mailbox_context_menu": { - "mark_folder_read": "Atzīmēt mapi kā lasītu", - "mark_folder_tree_read": "Atzīmēt mapi un apakšmapes kā lasītas", - "mark_all_folders_read": "Atzīmēt visas mapes kā lasītas", - "new_subfolder": "Jauna apakšmape...", - "new_folder": "Jauna mape...", - "rename": "Pārsaukt...", - "share": "Kopīgot...", - "import_email": "Importēt .eml vai .zip...", - "empty_folder": "Iztukšot mapi", - "empty_folder_generic": "Iztukšot mapi", - "delete_folder": "Dzēst mapi", - "refresh": "Atjaunināt", - "mark_all_confirm_title": "Atzīmēt visas mapes kā lasītas", - "mark_all_confirm_message": "Atzīmēt visas nelasītās ziņas jūsu personīgajā kontā kā lasītas?", - "delete_confirm_title": "Dzēst mapi", - "delete_confirm_message": "Neatgriezeniski dzēst mapi \"{name}\"? Šo darbību nevar atsaukt.", - "prompt_new_subfolder": "Ievadiet nosaukumu jaunajai apakšmapei.", - "prompt_new_folder": "Ievadiet nosaukumu jaunajai mapei.", - "prompt_rename": "Ievadiet jaunu nosaukumu šai mapei.", - "toast_marked_read": "Mape atzīmēta kā lasīta", - "toast_marked_read_count": "{count, plural, one {1 ziņa atzīmēta} other {# ziņas atzīmētas}} kā lasītas", - "toast_already_read": "Nav nelasītu ziņu", - "toast_marked_all_read": "Visas mapes atzīmētas kā lasītas", - "toast_emptied": "Mape iztukšota", - "toast_folder_created": "Mape izveidota", - "toast_folder_renamed": "Mape pārsaukta", - "toast_folder_deleted": "Mape dzēsta", - "toast_error_mark_read": "Neizdevās atzīmēt kā lasītu", - "toast_error_empty": "Neizdevās iztukšot mapi", - "toast_error_create": "Neizdevās izveidot mapi", - "toast_error_rename": "Neizdevās pārsaukt mapi", - "toast_error_delete": "Neizdevās dzēst mapi", - "toast_error_delete_has_children": "Mapei ir apakšmapes. Vispirms noņemiet tās.", - "toast_error_delete_has_email": "Mape nav tukša. Vispirms iztukšojiet to.", - "delete_with_contents_title": "Mape nav tukša", - "delete_with_contents_message": "Mapē “{name}” joprojām ir {count, plural, one {1 ziņa} other {# ziņas}}. Dzēst mapi un visas tās ziņas? To nevar atsaukt.", - "delete_with_contents_confirm": "Dzēst mapi un ziņas", - "placeholder_folder_name": "Mapes nosaukums", - "create": "Izveidot", - "rename_confirm": "Pārsaukt" - }, - "shortcuts": { - "title": "Īsinājumtaustiņi", - "tip": "Nospiediet ? jebkurā laikā, lai skatītu palīdzību", - "sections": { - "navigation": "Navigācija", - "actions": "Vēstules darbības", - "global": "Globālie", - "threads": "Sarunas", - "composer": "Redaktors" - }, - "navigation": { - "next_email": "Nākamā vēstule", - "previous_email": "Iepriekšējā vēstule", - "open_email": "Atvērt vēstuli", - "close_email": "Aizvērt / Noņemt atlasi" - }, - "actions": { - "reply": "Atbildēt", - "reply_all": "Atbildēt visiem", - "forward": "Pārsūtīt", - "star": "Pārslēgt zvaigznīti", - "archive": "Arhivēt", - "delete": "Dzēst", - "mark_unread": "Atzīmēt kā nelasītu", - "mark_read": "Atzīmēt kā izlasītu", - "toggle_spam": "Mēstule / Nav mēstule" - }, - "global": { - "compose": "Rakstīt jaunu vēstuli", - "search": "Iet uz meklēšanu", - "help": "Rādīt īsinājumtaustiņus", - "refresh": "Atsvaidzināt vēstules", - "select_all": "Atlasīt visas", - "global_search": "Meklēt visā" - }, - "threads": { - "expand_collapse": "Izvērst/sairt sarunu" - }, - "composer": { - "send": "Nosūtīt e-pastu", - "schedule_send": "Ieplānot nosūtīšanu", - "template_picker": "Atvērt veidņu izvēli" - } - }, - "threads": { - "messages_one": "{count} ziņojums", - "messages_other": "{count} ziņojumi", - "messages_tooltip": "{count, plural, one {# ziņojums šajā sarunā} other {# ziņojumi šajā sarunā}}", - "expand": "Izvērst sarunu", - "collapse": "Sairt sarunu", - "loading": "Ielādē sarunu...", - "mark_read": "Atzīmēt sarunu kā izlasītu", - "mark_unread": "Atzīmēt sarunu kā nelasītu", - "archive": "Arhivēt sarunu", - "delete": "Dzēst sarunu", - "star": "Pievienot zvaigznīti sarunai", - "unstar": "Noņemt zvaigznīti sarunai", - "toggle_thread": "Pārslēgt sarunu" - }, - "identities": { - "modal_title": "Pārvaldīt sūtītāja identitātes", - "create_new": "Izveidot jaunu identitāti", - "edit_identity": "Rediģēt identitāti", - "delete_confirm": "Vai dzēst šo identitāti? Šo darbību nevar atcelt.", - "cannot_delete": "Šo identitāti nevar dzēst", - "primary_identity": "Galvenā", - "set_as_primary": "Iestatīt kā galveno", - "no_identities": "Identitātes nav atrastas", - "display": { - "reply_to": "Atbildēt uz:", - "bcc": "SK:", - "signature": "Paraksts:", - "preview": "Priekšskatījums:" - }, - "validation_errors": { - "invalid_emails": "Nederīgas adreses: {emails}", - "unknown_error": "Nezināma kļūda" - }, - "form": { - "name_label": "Redzamais vārds", - "name_placeholder": "piem., Darba e-pasts, Personīgais", - "name_required": "Vārds ir obligāts", - "email_label": "E-pasta adrese", - "email_placeholder": "lietotajs@piemers.lv", - "email_required": "E-pasts ir obligāts", - "email_invalid": "Ievadiet derīgu e-pasta adresi", - "email_immutable": "E-pasta adresi nevar mainīt pēc izveides", - "reply_to_label": "Atbildēt uz (pēc izvēles)", - "reply_to_placeholder": "cits@e-pasts.lv", - "bcc_label": "Automātiska SK (pēc izvēles)", - "bcc_placeholder": "arhivs@piemers.lv", - "text_signature_label": "Teksta paraksts", - "html_signature_label": "HTML paraksts", - "signature_byte_counter": "{bytes} / {max} baiti", - "signature_byte_limit_reached": "Sasniegts servera ierobežojums", - "save": "Saglabāt identitāti", - "cancel": "Atcelt", - "creating": "Izveido...", - "updating": "Atjaunina..." - }, - "sub_address": { - "button_tooltip": "Izmantot apakšadresi", - "popover_title": "Pievienot apakšadreses tagu", - "tag_input_placeholder": "Ievadiet tagu (piem., iepirksanas)", - "preview_label": "Priekšskatījums:", - "recent_tags": "Nesenie tagi", - "suggested_tags": "Ieteikumi", - "use_address": "Izmantot šo adresi", - "invalid_tag": "Tags var saturēt tikai burtus, ciparus un domuzīmes", - "tag_too_long": "Tags nedrīkst pārsniegt 30 rakstzīmes", - "help_text": "Vēstules uz lietotajs{delimiter}tags@domens.lv nonāks jūsu pastkastē", - "validation": { - "empty": "Tags nevar būt tukšs", - "too_long": "Tags nedrīkst pārsniegt {max} rakstzīmes", - "invalid_chars": "Tags var saturēt tikai burtus, ciparus un domuzīmes" - } - }, - "badge": { - "sent_via": "caur", - "sub_address_tag": "Sūtīts caur apakšadresi: {tag}", - "identity_name": "Sūtīts caur identitāti: {name}", - "identity_short": "caur {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Dzēst", - "delete_confirm_title": "Dzēst identitāti" - }, - "templates": { - "picker_title": "Izvēlēties veidni", - "search_placeholder": "Meklēt veidnes...", - "section_favorites": "Izlase", - "section_recent": "Nesenie", - "section_uncategorized": "Citi", - "no_templates": "Veidnes vēl nav izveidotas", - "no_results": "Veidnes nav atrastas", - "fill_placeholders": "Aizpildīt mainīgo vērtības", - "enter_value": "Ievadiet vērtību...", - "preview": "Priekšskatījums", - "insert_with_values": "Ievietot ar vērtībām", - "insert_raw": "Ievietot bez izmaiņām", - "copy_suffix": "(kopija)", - "placeholder": "Mainīgais", - "placeholders": { - "recipient_name": "Saņēmēja vārds", - "company": "Uzņēmuma nosaukums", - "date": "Pašreizējais datums", - "day_of_week": "Nedēļas diena", - "sender_name": "Jūsu vārds" - } - }, - "contacts": { - "title": "Kontakti", - "search_placeholder": "Meklēt kontaktus...", - "create_new": "Jauns kontakts", - "no_category": "Bez kategorijas", - "category_added": "Kontakts pievienots {name}", - "category_added_plural": "{count} kontakti pievienoti {name}", - "empty_state": "Kontaktu vēl nav", - "empty_state_title": "Kontaktu vēl nav", - "empty_state_subtitle": "Izveidojiet pirmo kontaktu vai importējiet no vCard faila", - "empty_search": "Nav kontaktu, kas atbilstu vaicājumam", - "empty_search_hint": "Mēģiniet citu meklēšanas vaicājumu", - "empty_filtered": "Nav kontaktu, kas atbilstu filtriem", - "empty_filtered_hint": "Pielāgojiet vai notīriet filtrus", - "clear_search": "Notīrīt meklēšanu", - "import_vcard": "vCard imports", - "delete_confirm_title": "Dzēst kontaktu", - "delete_confirm": "Vai tiešām vēlaties dzēst šo kontaktu?", - "local_mode": "Kontakti tiek glabāti lokāli (serveris neatbalsta JMAP Contacts)", - "back_to_contacts": "Atpakaļ pie kontaktiem", - "back_to_email": "Atpakaļ pie e-pasta", - "tabs": { - "all": "Visi", - "groups": "Grupas" - }, - "shared": { - "title": "Koplietotie" - }, - "address_books": { - "create": "Jauna adrešu grāmata", - "created": "Adrešu grāmata izveidota", - "create_failed": "Neizdevās izveidot adrešu grāmatu", - "title": "Manas adrešu grāmatas", - "shared_prefix": "Koplietotā: {name}", - "moved": "Kontakts pārvietots uz {name}", - "moved_plural": "{count} kontakti pārvietoti uz {name}", - "move_failed": "Neizdevās pārvietot kontaktu", - "address_book": "Adrešu grāmata", - "rename": "Pārdēvēt adrešu grāmatu", - "name_label": "Adrešu grāmatas nosaukums", - "renamed": "Adrešu grāmata pārdēvēta", - "rename_failed": "Neizdevās pārdēvēt adrešu grāmatu", - "default": "Noklusējuma", - "set_default": "Iestatīt kā noklusējumu", - "default_updated": "Noklusējuma adrešu grāmata atjaunināta", - "set_default_failed": "Neizdevās iestatīt noklusējuma adrešu grāmatu", - "manage": "Pārvaldīt adrešu grāmatas", - "share": "Kopīgot adrešu grāmatu", - "new_contact_in_book": "Jauns kontakts šajā adrešu grāmatā", - "delete": "Dzēst adrešu grāmatu", - "confirm_delete": "Dzēst \"{name}\"? Visi kontakti šajā adrešu grāmatā tiks noņemti.", - "deleted": "Adrešu grāmata dzēsta", - "delete_failed": "Neizdevās dzēst adrešu grāmatu" - }, - "detail": { - "emails": "E-pasta adreses", - "phones": "Tālruņa numuri", - "organizations": "Organizācijas", - "addresses": "Adreses", - "notes": "Piezīmes", - "titles": "Amati un lomas", - "online_services": "Tiešsaistes pakalpojumi", - "anniversaries": "Datumi", - "personal_info": "Personīgā informācija", - "languages": "Valodas", - "categories": "Kategorijas", - "related_contacts": "Saistītie kontakti", - "crypto_keys": "Kriptogrāfijas atslēgas", - "cert_issuer": "Izdevējs", - "cert_expires": "Termiņš", - "cert_expired": "Beidzies", - "cert_fingerprint": "Nospiedums", - "cert_algorithm": "Algoritms", - "import_to_smime": "Importēt uz S/MIME", - "cert_already_imported": "Jau importēts S/MIME", - "cert_imported": "Sertifikāts importēts S/MIME krātuvē", - "cert_import_failed": "Neizdevās importēt sertifikātu", - "no_contact_selected": "Atlasiet kontaktu, lai skatītu informāciju", - "compose_email": "Rakstīt e-pastu", - "copy_email": "Kopēt e-pastu", - "copy_phone": "Kopēt tālruni", - "copy_url": "Kopēt URL", - "copied": "Nokopēts starpliktuvē", - "copy_failed": "Neizdevās nokopēt starpliktuvē", - "created": "Izveidots", - "updated": "Pēdējo reizi atjaunināts", - "timezone": "Laika josla", - "anniversary_birth": "Dzimšanas diena", - "anniversary_death": "Miršanas datums", - "anniversary_wedding": "Kāzu gadadiena", - "anniversary_other": "Cits", - "personal_expertise": "Ekspertīze", - "personal_hobby": "Hobijs", - "personal_interest": "Intereses", - "personal_other": "Cits", - "gender": "Dzimums", - "gender_masculine": "Vīrietis", - "gender_feminine": "Sieviete", - "gender_other": "Cits", - "gender_none": "Nav attiecināms", - "gender_unknown": "Nezināms", - "calendar": "Kalendārs", - "calendar_uri": "Kalendāra URL", - "scheduling_uri": "Plānošanas URL", - "freebusy_uri": "Pieejamības URL", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Citas darbības", - "age_years": "{count, plural, one {1 gads} other {# gadi}}", - "years_since": "{count, plural, one {pirms 1 gada} other {pirms # gadiem}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Jauns kontakts", - "edit_title": "Rediģēt kontaktu", - "section_address_book": "Katalogs", - "select_address_book": "Izvēlieties katalogu...", - "section_identity": "Vārds un identitāte", - "contact_type": "Kontakta veids", - "type_person": "Persona", - "type_organization": "Organizācija", - "section_work": "Darbs un organizācija", - "prefix": "Prefikss", - "prefix_placeholder": "Dr., kungs, kundze", - "given_name": "Vārds", - "middle_name": "Otrais vārds", - "surname": "Uzvārds", - "suffix": "Sufikss", - "suffix_placeholder": "jr., sr.", - "nickname": "Segvārds", - "nickname_placeholder": "Pseidonīms", - "email": "E-pasts", - "email_placeholder": "lietotajs@piemers.lv", - "phone": "Tālrunis", - "phone_placeholder": "+371 23456789", - "phone_type": "Veids", - "phone_voice": "Balss", - "phone_cell": "Mobilais", - "phone_fax": "Fakss", - "phone_pager": "Peidžeris", - "phone_video": "Video", - "phone_text": "Teksts", - "organization": "Organizācija", - "organization_placeholder": "Uzņēmuma nosaukums", - "department": "Nodaļa", - "department_placeholder": "Nodaļa", - "job_title": "Amats", - "job_title_placeholder": "piem., programmatūras inženieris", - "role": "Loma", - "role_placeholder": "piem., komandas vadītājs", - "addresses": "Adreses", - "add_address": "Pievienot adresi", - "street": "Iela", - "city": "Pilsēta", - "region": "Reģions / novads", - "postcode": "Pasta indekss", - "country": "Valsts", - "online_services": "Tiešsaistes pakalpojumi", - "add_online_service": "Pievienot pakalpojumu", - "url_placeholder": "https://piemers.lv", - "service_placeholder": "Pakalpojums", - "anniversaries": "Datumi", - "add_anniversary": "Pievienot datumu", - "anniversary_birth": "Dzimšanas diena", - "anniversary_wedding": "Gadadiena", - "anniversary_death": "Piemiņas diena", - "anniversary_other": "Cits", - "personal_info": "Personīgā informācija", - "add_personal_info": "Pievienot ierakstu", - "personal_info_placeholder": "piem., Fotogrāfija", - "personal_expertise": "Ekspertīze", - "personal_hobby": "Hobijs", - "personal_interest": "Interese", - "personal_other": "Cits", - "level": "Līmenis", - "level_high": "Augsts", - "level_medium": "Vidējs", - "level_low": "Zems", - "categories": "Kategorijas", - "categories_placeholder": "piem., Ģimene, Draugi, Kolēģi", - "categories_hint": "Ievadiet, lai meklētu vai pievienotu", - "category_add": "Pievienot", - "note": "Piezīmes", - "note_placeholder": "Pievienojiet piezīmi...", - "gender": "Dzimums", - "gender_sex": "Bioloģiskais dzimums", - "gender_male": "Vīrietis", - "gender_female": "Sieviete", - "gender_other": "Cits", - "gender_none": "Nav attiecināms", - "gender_unknown": "Nezināms", - "gender_identity": "Dzimumidentitāte", - "gender_identity_placeholder": "Dzimumidentitāte...", - "calendar": "Kalendārs", - "calendar_uri": "Kalendāra URL", - "scheduling_uri": "Plānošanas URL", - "freebusy_uri": "Pieejamības URL", - "context_work": "Darba", - "context_private": "Privāts", - "add_email": "Pievienot e-pastu", - "add_phone": "Pievienot tālruni", - "save": "Saglabāt", - "cancel": "Atcelt", - "creating": "Izveido...", - "updating": "Atjaunina...", - "name_required": "Ievadiet vārdu, uzvārdu vai organizāciju", - "email_invalid": "Ievadiet derīgu e-pasta adresi", - "email_error_inline": "Nederīgs e-pasta formāts", - "save_failed": "Neizdevās saglabāt kontaktu", - "delete": "Dzēst", - "upload_photo": "Augšupielādēt foto", - "remove_photo": "Noņemt foto", - "photo_hint": "JPG vai PNG, līdz 10 MB. Tiks mainīts izmērs.", - "photo_too_large": "Attēls ir pārāk liels (maks. 10 MB)", - "photo_invalid": "Nederīgs attēla fails", - "change_photo": "Mainīt" - }, - "groups": { - "create": "Jauna grupa", - "edit": "Rediģēt grupu", - "empty": "Grupu vēl nav", - "delete_confirm_title": "Dzēst grupu", - "delete_confirm": "Vai tiešām vēlaties dzēst šo grupu?", - "name_label": "Grupas nosaukums", - "name_placeholder": "piem., Komanda, Ģimene", - "name_required": "Grupas nosaukums ir obligāts", - "save_failed": "Neizdevās saglabāt grupu", - "members_label": "Dalībnieki", - "search_members": "Meklēt kontaktus, ko pievienot...", - "no_members": "Šajā grupā nav dalībnieku", - "member_count": "{count, plural, =0 {Nav dalībnieku} one {1 dalībnieks} other {# dalībnieki}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Kontaktu imports", - "drop_hint": "Noklikšķiniet, lai izvēlētos vCard failu", - "file_types": ".vcf vai .vcard faili", - "no_contacts": "Failā netika atrasti kontakti", - "parse_error": "Neizdevās nolasīt vCard failu", - "found": "Atrasts {count, plural, one {1 kontakts} other {# kontakti}}", - "duplicate": "Dublikāts", - "select_all": "Atlasīt visus", - "deselect_all": "Noņemt atlasi", - "selected": "Atlasīts {count, plural, one {1 kontakts} other {# kontakti}}", - "import_button": "Importēt", - "importing": "Importē...", - "success": "Importēts {count, plural, one {1 kontakts} other {# kontakti}}", - "failed": "Imports neizdevās", - "close": "Aizvērt", - "file_too_large": "Fails ir pārāk liels (maks. 5 MB)" - }, - "export": { - "title": "Kontaktu eksports", - "success": "Eksportēts {count, plural, one {1 kontakts} other {# kontakti}}" - }, - "bulk": { - "selected": "Atlasīts {count, plural, one {1 kontakts} other {# kontakti}}", - "select_all": "Atlasīt visus", - "delete": "Dzēst", - "delete_confirm_title": "Dzēst kontaktus", - "delete_confirm": "Vai dzēst {count, plural, one {1 kontaktu} other {# kontaktus}}?", - "deleted": "Izdzēsts {count, plural, one {1 kontakts} other {# kontakti}}", - "add_to_group": "Pievienot grupai", - "choose_group": "Izvēlēties grupu", - "adding_contacts": "Pievieno {count, plural, one {1 kontaktu} other {# kontaktus}}", - "added_to_group": "Kontakti pievienoti grupai", - "export": "Eksportēt", - "clear": "Notīrīt atlasi" - }, - "toast": { - "created": "Kontakts izveidots", - "updated": "Kontakts atjaunināts", - "deleted": "Kontakts izdzēsts", - "error_create": "Neizdevās izveidot kontaktu", - "error_update": "Neizdevās atjaunināt kontaktu", - "error_delete": "Neizdevās izdzēst kontaktu" - }, - "rename_category": "Pārdēvēt kategoriju", - "category_name_label": "Kategorijas nosaukums", - "category_renamed": "Kategorija pārdēvēta", - "category_rename_failed": "Neizdevās pārdēvēt kategoriju", - "context_menu": { - "open": "Atvērt", - "edit": "Rediģēt", - "send_email": "Sūtīt e-pastu", - "add_to_group": "Pievienot grupai", - "export_vcard": "Eksportēt kā vCard", - "delete": "Dzēst", - "call": "Zvanīt", - "duplicate": "Dublēt", - "print": "Drukāt" - }, - "filters": { - "toggle": "Filtri", - "select": "Atlasīt", - "clear": "Notīrīt", - "close": "Aizvērt", - "title": "Paplašinātie filtri", - "organization": "Uzņēmums", - "organization_placeholder": "piem. Acme", - "job_title": "Amats", - "job_title_placeholder": "piem. Dizainers", - "location": "Atrašanās vieta", - "location_placeholder": "Pilsēta vai valsts", - "email_domain": "E-pasta domēns", - "email_domain_placeholder": "piemers.lv", - "birthday_month": "Dzimšanas diena", - "any_month": "Jebkurš mēnesis", - "has_email": "Ar e-pastu", - "has_phone": "Ar tālruni", - "has_photo": "Ar foto", - "sort_by": "Kārtot pēc", - "sort_first_name": "Vārds", - "sort_last_name": "Uzvārds" - }, - "open_categories": "Atvērt kategorijas" - }, - "calendar": { - "title": "Kalendārs", - "back_to_email": "Atgriezties pie pasta", - "back_to_month": "Atpakaļ uz mēneša skatu", - "my_calendars": "Kalendāri", - "mini_calendar_change": "Noklikšķiniet, lai mainītu mēnesi", - "views": { - "month": "Mēnesis", - "week": "Nedēļa", - "day": "Diena", - "agenda": "Darba kārtība", - "today": "Šodien", - "month_hint": "Mēnesis (m)", - "week_hint": "Nedēļa (w)", - "day_hint": "Diena (d)", - "agenda_hint": "Darba kārtība (a)", - "tasks": "Uzdevumi", - "tasks_hint": "Uzdevumi (k)" - }, - "events": { - "create": "Izveidot pasākumu", - "edit": "Rediģēt pasākumu", - "delete": "Dzēst pasākumu", - "details": "Pasākuma informācija", - "no_events": "Nav pasākumu", - "all_day": "Visa diena", - "more": "vēl {count}", - "no_title": "(Bez virsraksta)", - "resize": "Mainīt pasākuma laiku", - "duplicate": "Dublēt", - "today_header": "Šodien", - "tomorrow_header": "Rīt", - "agenda_show_earlier": "Rādīt agrākos notikumus", - "agenda_loading": "Ielādē…", - "agenda_range_start": "Notikumi no {date}", - "agenda_range_end": "Notikumi līdz {date}", - "export_ics": "Eksportēt kā .ics", - "copy_title": "Kopēt nosaukumu", - "copy_link": "Kopēt sapulces saiti", - "go_to_today": "Pāriet uz šodienu", - "new_all_day_event": "Jauns visas dienas notikums", - "new_event": "Jauns notikums", - "new_task": "Jauns uzdevums" - }, - "detail": { - "add_note": "Pievienot piezīmi...", - "save_note": "Saglabāt", - "note_saved": "Piezīme pievienota", - "open_link": "Atvērt saiti", - "meeting_link": "Sapulces saite", - "tentative": "Pagaidām", - "cancelled": "Atcelts", - "delete_confirm": "Vai dzēst šo pasākumu?" - }, - "form": { - "title": "Nosaukums", - "description": "Apraksts", - "location": "Vieta", - "meeting_link": "Sapulces saite", - "start_date": "Sākuma datums", - "end_date": "Beigu datums", - "start_time": "Sākuma laiks", - "end_time": "Beigu laiks", - "all_day_event": "Visas dienas pasākums", - "calendar_select": "Kalendārs", - "save": "Saglabāt", - "cancel": "Atcelt", - "delete_confirm": "Vai tiešām vēlaties dzēst šo pasākumu?", - "color": "Krāsa" - }, - "participants": { - "availability": { - "title": "Pieejamība", - "free": "Pieejams", - "busy": "Aizņemts", - "tentative": "Provizoriski", - "unknown": "Nav šī servera lietotājs", - "checking": "Pārbauda…" - }, - "title": "Dalībnieki", - "add": "Pievienot dalībnieku", - "organizer": "Organizators", - "attendee": "Dalībnieks", - "accepted": "Pieņemts", - "declined": "Noraidīts", - "tentative": "Pagaidām", - "needs_action": "Nepieciešama atbilde", - "remove": "Noņemt", - "edit": "Rediģēt", - "email_placeholder": "Pievienojiet adresi vai kontaktu", - "send_invitations": "Sūtīt uzaicinājumus dalībniekiem", - "status_summary": "{accepted} pieņēmuši, {pending} gaida", - "invited_by": "Uzaicināja {name}", - "respond_below": "Atbildiet, izmantojot pogas zemāk", - "rsvp_label": "Jūsu atbilde", - "cancel_notification": "Dalībnieki tiks informēti par atcelšanu", - "you_organizer": "Jūs esat organizators", - "you_attendee": "Jūs esat dalībnieks", - "no_participants": "Nav dalībnieku", - "count": "{count, plural, one {# dalībnieks} other {# dalībnieki}}" - }, - "recurrence": { - "title": "Atkārtošana", - "none": "Neatkārtojas", - "daily": "Katru dienu", - "weekly": "Katru nedēļu", - "monthly": "Katru mēnesi", - "yearly": "Katru gadu", - "every_n_days": "Ik pēc {count} dienām", - "every_n_weeks": "Ik pēc {count} nedēļām", - "every_n_months": "Ik pēc {count} mēnešiem", - "until": "Līdz", - "occurrences": "{count} reizes", - "custom": "Pielāgots…", - "edit_custom": "Rediģēt pielāgotu atkārtošanos", - "every_n_years": "Ik pēc {count} gadiem", - "on_days": "šajās dienās: {days}", - "on_day_n": "mēneša {day}. dienā", - "on_the_nth": "{nth} {day}", - "in_month": "{month} mēnesī", - "nth_1": "pirmajā", - "nth_2": "otrajā", - "nth_3": "trešajā", - "nth_4": "ceturtajā", - "nth_last": "pēdējā", - "editor_freq_day": "Diena", - "editor_freq_week": "Nedēļa", - "editor_freq_month": "Mēnesis", - "editor_freq_year": "Gads", - "editor_repeats_on": "Atkārtojas", - "editor_every": "Ik pēc", - "editor_unit_days": "dienām", - "editor_unit_weeks": "nedēļām", - "editor_unit_months": "mēnešiem", - "editor_unit_years": "gadiem", - "editor_on_day": "mēneša dienā", - "editor_on_the": "šajā:", - "editor_in": "mēnesī", - "editor_ends": "Beidzas", - "editor_never": "Nekad", - "editor_ends_on": "Datumā", - "editor_ends_after": "Pēc", - "editor_occurrences": "reizēm" - }, - "recurrence_scope": { - "edit_title": "Rediģēt atkārtotu pasākumu", - "delete_title": "Dzēst atkārtotu pasākumu", - "description": "Šis ir atkārtots pasākums. Kuras instances vēlaties mainīt?", - "this_event": "Tikai šo pasākumu", - "this_and_future": "Šo un turpmākos", - "all_events": "Visus pasākumus", - "cancel": "Atcelt", - "save": "Saglabāt", - "delete": "Dzēst", - "rsvp_title": "Atbildēt uz atkārtotu pasākumu", - "rsvp_description": "Šis ir atkārtots pasākums. Uz kurām instancēm attiecas jūsu atbilde?", - "respond": "Atbildēt" - }, - "alerts": { - "title": "Atgādinājums", - "none": "Bez atgādinājuma", - "at_time": "Pasākuma laikā", - "minutes_before": "Pirms {count} minūtes", - "hours_before": "Pirms {count} stundas", - "days_before": "Pirms {count} dienas", - "weeks_before": "Pirms {count} nedēļām", - "unit_minutes_before": "minūtes pirms", - "unit_hours_before": "stundas pirms", - "unit_days_before": "dienas pirms", - "unit_weeks_before": "nedēļas pirms", - "add": "Pievienot atgādinājumu", - "remove": "Noņemt atgādinājumu", - "amount": "Atgādinājuma daudzums", - "unit": "Atgādinājuma vienība" - }, - "settings": { - "title": "Kalendāra iestatījumi", - "default_view": "Noklusējuma skats", - "week_starts_on": "Nedēļa sākas ar", - "time_format": "Laika formāts", - "default_calendar": "Noklusējuma kalendārs", - "default_reminder": "Noklusējuma atgādinājums", - "time_format_12h": "12 stundu", - "time_format_24h": "24 stundu", - "notifications_enabled": "Pasākumu paziņojumi", - "notifications_enabled_desc": "Rādīt brīdinājumus par gaidāmajiem pasākumiem", - "notification_sound": "Paziņojuma skaņa", - "notification_sound_desc": "Atskaņot skaņu kalendāra brīdinājumiem", - "invitation_parsing": "Uzaicinājumu atpazīšana e-pastā", - "invitation_parsing_desc": "Meklēt kalendāra uzaicinājumus e-pasta pielikumos", - "show_time_in_month_view": "Rādīt laiku mēneša skatā", - "show_time_in_month_view_desc": "Rādīt pasākuma laiku mēneša kalendārā", - "show_week_numbers": "Rādīt nedēļu numurus", - "show_week_numbers_desc": "Rādīt nedēļu numurus mini kalendārā", - "calendar_free_scroll": "Brīva ritināšana", - "calendar_free_scroll_desc": "Nepārtraukti ritināt cauri mēnešiem, nedēļām un dienām, nevis pa vienam periodam", - "enable_tasks": "Iespējot uzdevumus", - "enable_tasks_desc": "Rādīt uzdevumu sadaļu kalendārā", - "show_tasks_on_calendar": "Rādīt uzdevumus kalendārā", - "show_tasks_on_calendar_desc": "Rādīt uzdevumu ierakstus dienas un nedēļas skatā", - "hover_preview": "Priekšskatījums uzvirzot", - "hover_preview_desc": "Rādīt informāciju, uzvirzot kursoru uz pasākuma", - "hover_preview_instant": "Uzreiz", - "hover_preview_delay_500ms": "0,5 sekundes aizture", - "hover_preview_delay_1s": "1 sekundes aizture", - "hover_preview_delay_2s": "2 sekunžu aizture", - "hover_preview_off": "Izslēgts", - "organizer_identity": "Organizēt ielūgumus kā", - "organizer_identity_desc": "Kalendāra adrese, ko izmanto kā organizatoru, uzaicinot cilvēkus uz notikumiem.", - "organizer_identity_failed": "Neizdevās mainīt organizatora adresi", - "show_birthday_calendar": "Kontaktu dzimšanas dienu kalendārs", - "show_birthday_calendar_desc": "Rādīt virtuālu kalendāru ar kontaktu dzimšanas dienām" - }, - "days": { - "monday": "Pirmdiena", - "tuesday": "Otrdiena", - "wednesday": "Trešdiena", - "thursday": "Ceturtdiena", - "friday": "Piektdiena", - "saturday": "Sestdiena", - "sunday": "Svētdiena", - "mon": "Pr", - "tue": "Ot", - "wed": "Tr", - "thu": "Ce", - "fri": "Pk", - "sat": "Se", - "sun": "Sv" - }, - "notifications": { - "event_created": "Pasākums izveidots", - "event_updated": "Pasākums atjaunināts", - "event_deleted": "Pasākums izdzēsts", - "calendar_created": "Kalendārs izveidots", - "calendar_deleted": "Kalendārs izdzēsts", - "event_move_error": "Neizdevās pārvietot pasākumu", - "event_resize_error": "Neizdevās mainīt pasākuma laiku", - "alert_title": "Gaidāms pasākums", - "alert_now": "Sākas tagad", - "alert_in_minutes": "Pēc {count} min", - "invitation_sent": "Uzaicinājumi nosūtīti", - "invitations_denied_title": "Ielūgumus nevar nosūtīt", - "invitations_denied": "Serveris atteicās nosūtīt ielūgumus: {reason}. Saglabāt notikumu, tos nenosūtot?", - "save_without_invitations": "Saglabāt bez ielūgumiem", - "rsvp_updated": "Atbilde atjaunināta", - "rsvp_error": "Neizdevās atjaunināt atbildi", - "event_duplicated": "Pasākums dublēts", - "event_error": "Neizdevās saglabāt pasākumu", - "task_due": "Uzdevuma termiņš", - "event_exported": "Notikums eksportēts", - "title_copied": "Nosaukums nokopēts", - "link_copied": "Saite nokopēta" - }, - "status": { - "loading_calendars": "Ielādē kalendārus...", - "loading_events": "Ielādē pasākumus..." - }, - "quick_create": { - "placeholder": "Jauna pasākuma nosaukums", - "aria_label": "Ātra pasākuma izveide" - }, - "nav_prev": "Iepriekšējais", - "nav_next": "Nākamais", - "import": { - "title": "Kalendāra imports", - "tab_file": "Fails", - "tab_url": "URL adrese", - "select_file": "Izvēlieties .ics failu", - "drop_file": "vai velciet failu šeit", - "supported_formats": "Atbalsta iCalendar (.ics) failus", - "url_description": "Ievadiet ārējas iCalendar plūsmas URL.", - "url_placeholder": "https://piemers.lv/kalendars.ics", - "url_hint": "Atbalsta iCalendar (.ics) saites", - "fetch": "Iegūt", - "invalid_url": "Ievadiet derīgu URL", - "url_fetch_failed": "Neizdevās iegūt kalendāru no saites", - "parsing": "Nolasa kalendāra failu...", - "parsed_events": "Atrasti {count} pasākumi", - "no_events": "Failā netika atrasti pasākumi", - "select_all": "Atlasīt visus", - "deselect_all": "Noņemt atlasi", - "target_calendar": "Importēt kalendārā", - "import_button": "Importēt atlasītos", - "importing": "Importē pasākumus...", - "success": "{count} pasākumi veiksmīgi importēti", - "error": "Neizdevās importēt kalendāru", - "file_too_large": "Fails pārsniedz 10 MB limitu", - "invalid_format": "Nederīgs kalendāra faila formāts" - }, - "webcal_action": { - "title": "Atvērt kalendāra saiti", - "description": "Kā vēlaties izmantot \"{name}\"?", - "import_title": "Importēt vienreiz", - "import_description": "Ielādēt notikumus tagad un kopēt tos vienā no jūsu kalendāriem.", - "subscribe_title": "Abonēt", - "subscribe_description": "Automātiski sinhronizēt šo kalendāru kā atsevišķu kalendāru.", - "cancel": "Atcelt" - }, - "management": { - "title": "Kalendāru pārvaldība", - "description": "Izveidojiet, pārdēvējiet un konfigurējiet kalendārus. Ar labo klikšķi varat mainīt kalendāra krāsu.", - "name": "Nosaukums", - "name_placeholder": "Kalendāra nosaukums", - "color": "Krāsa", - "change_color": "Mainīt krāsu", - "random_color": "Jauna nejauša krāsa", - "add_calendar": "Pievienot kalendāru", - "edit": "Rediģēt", - "delete": "Dzēst", - "save": "Saglabāt", - "create": "Izveidot", - "cancel": "Atcelt", - "default": "Noklusējuma", - "confirm_delete": "Dzēst «{name}»? Visi pasākumi šajā kalendārā tiks dzēsti.", - "confirm_clear": "Iztīrīt visus pasākumus no «{name}»? Šo darbību nevar atcelt.", - "set_default": "Iestatīt kā noklusējumu", - "default_updated": "Noklusējuma kalendārs atjaunināts", - "error_default": "Neizdevās iestatīt noklusējuma kalendāru", - "clear_events": "Iztīrīt pasākumus", - "events_cleared": "Iztīrīti {count} pasākumi", - "error_clear": "Neizdevās iztīrīt kalendāra pasākumus", - "calendar_created": "Kalendārs izveidots", - "calendar_updated": "Kalendārs atjaunināts", - "calendar_deleted": "Kalendārs izdzēsts", - "color_updated": "Kalendāra krāsa atjaunināta", - "error_create": "Neizdevās izveidot kalendāru", - "error_update": "Neizdevās atjaunināt kalendāru", - "error_delete": "Neizdevās izdzēst kalendāru", - "caldav_url": "CalDAV URL", - "copy_url": "Kopēt CalDAV URL", - "url_copied": "CalDAV URL nokopēts starpliktuvē", - "share": "Kopīgot kalendāru", - "new_event_in_calendar": "Jauns notikums šajā kalendārā", - "kind": "Saturs", - "kind_events": "Notikumi", - "kind_tasks": "Uzdevumi", - "kind_both": "Notikumi un uzdevumi", - "kind_hint": "Nosaka, kuras lietotnes redz šo kalendāru, kad tas tiek sinhronizēts caur CalDAV. Vēlāk to nevar mainīt." - }, - "subscription": { - "title": "iCal abonements", - "section_title": "iCal abonementi", - "description": "Abonējiet ārēju iCalendar plūsmu. Pasākumi tiks sinhronizēti automātiski.", - "url_label": "Kalendāra URL", - "url_placeholder": "https://example.com/calendar.ics vai webcal://...", - "name_label": "Kalendāra nosaukums", - "name_placeholder": "piem., Svētku dienas", - "color_label": "Krāsa", - "refresh_interval": "Atjaunināšanas intervāls", - "interval_15": "Ik pēc 15 minūtēm", - "interval_30": "Ik pēc 30 minūtēm", - "interval_60": "Ik pēc stundas", - "interval_360": "Ik pēc 6 stundām", - "interval_1440": "Katru dienu", - "subscribe": "Abonēt", - "subscribing": "Abonē...", - "invalid_url": "Ievadiet derīgu URL", - "success": "Pieteikts abonements «{name}»", - "error": "Neizdevās pievienot abonementu", - "refresh": "Atjaunināt tūlīt", - "refresh_success": "Abonements atjaunināts", - "refresh_error": "Neizdevās atjaunināt abonementu", - "unsubscribe": "Atteikties", - "confirm_delete": "Atteikties no «{name}»? Kalendārs un tā pasākumi tiks dzēsti.", - "deleted": "Abonements dzēsts", - "delete_error": "Neizdevās dzēst abonementu", - "last_refreshed": "Pēdējo reizi atjaunināts: {time}", - "save": "Saglabāt izmaiņas", - "saving": "Saglabā...", - "edit": "Rediģēt", - "edit_title": "Rediģēt abonementu", - "updated": "Atjaunināts \"{name}\"", - "update_error": "Neizdevās atjaunināt abonementu" - }, - "tasks": { - "label": "Uzdevumi", - "no_tasks": "Nav uzdevumu", - "no_title": "(Bez nosaukuma)", - "mark_complete": "Atzīmēt kā izpildītu", - "mark_incomplete": "Atzīmēt kā neizpildītu", - "filter_all": "Visi", - "filter_pending": "Gaidāmie", - "filter_completed": "Pabeigtie", - "filter_overdue": "Kavētie", - "show_completed": "Rādīt pabeigtos", - "create": "Jauns uzdevums", - "edit": "Rediģēt uzdevumu", - "title_placeholder": "Uzdevuma nosaukums", - "description_placeholder": "Pievienojiet aprakstu...", - "due_date": "Izpildes termiņš", - "include_time": "Iekļaut laiku", - "priority": "Prioritāte", - "priority_none": "Nav", - "priority_high": "Augsta", - "priority_medium": "Vidēja", - "priority_low": "Zema", - "progress": "Statuss", - "progress_needs_action": "Jārīkojas", - "progress_in_process": "Procesā", - "progress_completed": "Pabeigts", - "progress_cancelled": "Atcelts", - "calendar": "Kalendārs", - "alert": "Atgādinājums", - "alert_none": "Nav", - "alert_at_time": "Termiņa brīdī", - "alert_5min": "Pirms 5 minūtēm", - "alert_15min": "Pirms 15 minūtēm", - "alert_30min": "Pirms 30 minūtēm", - "alert_1hr": "Pirms 1 stundas", - "alert_1day": "Pirms 1 dienas", - "delete": "Dzēst", - "cancel": "Atcelt", - "save": "Saglabāt", - "quick_add_placeholder": "Pievienot uzdevumu...", - "due_today": "Šodien", - "due_tomorrow": "Rīt", - "overdue": "Kavēts" - }, - "birthday_calendar": "Dzimšanas dienas", - "months": { - "jan": "janv.", - "feb": "febr.", - "mar": "marts", - "apr": "apr.", - "may": "maijs", - "jun": "jūn.", - "jul": "jūl.", - "aug": "aug.", - "sep": "sept.", - "oct": "okt.", - "nov": "nov.", - "dec": "dec.", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Atvērt izvēlni" - }, - "advanced_search": { - "title": "Izvērstā meklēšana", - "from": "No", - "from_placeholder": "Sūtītāja e-pasts vai vārds", - "to": "Kam", - "to_placeholder": "Saņēmēja e-pasts vai vārds", - "subject": "Temats", - "subject_placeholder": "Temats satur...", - "body": "Teksts", - "body_placeholder": "Teksts satur...", - "folder": "Mape", - "all_folders": "Visas mapes", - "has_attachment": "Ar pielikumu", - "date_after": "Pēc", - "date_before": "Pirms", - "size_min": "Lielāks par (KB)", - "size_max": "Mazāks par (KB)", - "starred": "Ar zvaigznīti", - "unread": "Nelasītas", - "read": "Izlasītas", - "yes": "Jā", - "no": "Nē", - "clear": "Notīrīt", - "clear_all": "Notīrīt visu", - "filters_active": "{count} filtrs", - "filters_active_plural": "{count} filtri", - "toggle_filters": "Vairāk", - "search_hint": "Izmantojiet filtrus precīzākai meklēšanai", - "advanced_filters_tooltip": "Izvērstie meklēšanas filtri", - "results_found": "{count, plural, =0 {Nekas netika atrasts} one {# rezultāts} other {# rezultāti}}", - "results_found_more": "Atrasti {count}+ rezultāti", - "suggestions_recent": "Nesenie meklējumi", - "suggestions_people": "Personas", - "suggestions_from": "Meklēt e-pastus no {address}", - "suggestions_to": "Meklēt e-pastus adresātam {address}", - "suggestions_remove_recent": "Noņemt no nesenajiem meklējumiem" - }, - "welcome": { - "title": "Laipni lūdzam jūsu pastkastē", - "tip_compose": "Nospiediet c, lai rakstītu jaunu vēstuli", - "tip_shortcuts": "Nospiediet ?, lai skatītu visus īsinājumtaustiņus", - "tip_sidebar": "Kalendāru, Kontaktus un Iestatījumus atradīsiet sānu joslā", - "tip_settings": "Pielāgojiet saskarni sadaļā «Iestatījumi»", - "got_it": "Skaidrs", - "settings": "Iestatījumi", - "dismiss": "Aizvērt", - "start_tour": "Sākt ekskursiju" - }, - "demo_welcome": { - "title": "Laipni lūdzam Bulwark Mail", - "description": "Atklājiet pilnvērtīgu tīmekļa e-pasta klientu tieši pārlūkā. Visi dati tiek glabāti jūsu ierīcē.", - "feature_email": "Vēstuļu lasīšana un rakstīšana", - "feature_organize": "Etiķetes, zvaigznītes un mapes", - "feature_shortcuts": "Īsinājumtaustiņi", - "feature_privacy": "100% privāts demo režīms", - "hint": "Noklikšķiniet uz jebkuru vēstuli kreisajā pusē, lai sāktu." - }, - "files": { - "office_edit": "Rediģēt dokumentu", - "office_loading": "Atver dokumentu redaktoru…", - "office_error": "Neizdevās atvērt dokumentu redaktoru.", - "office_close": "Aizvērt redaktoru", - "open_in_new_tab": "Atvērt jaunā cilnē", - "title": "Faili", - "search_placeholder": "Meklēt failus...", - "empty_state_title": "Failu vēl nav", - "empty_state_description": "Augšupielādējiet failus vai izveidojiet mapes, lai sāktu", - "upload": "Augšupielādēt", - "upload_files": "Augšupielādēt failus", - "new_folder": "Jauna mape", - "new_folder_name": "Mapes nosaukums", - "rename": "Pārdēvēt", - "rename_title": "Pārdēvēt", - "new_name": "Jauns nosaukums", - "name_forbidden_chars": "Serveris neatļauj šīs rakstzīmes nosaukumos: {chars}", - "name_reserved": "Šis nosaukums serverī ir rezervēts.", - "delete": "Dzēst", - "delete_confirm_title": "Dzēst resursu", - "delete_confirm_message": "Vai tiešām vēlaties dzēst «{name}»? Šo darbību nevar atcelt.", - "download": "Lejupielādēt", - "name": "Nosaukums", - "size": "Izmērs", - "modified": "Modificēts", - "type": "Veids", - "folder": "Mape", - "file": "Fails", - "parent_directory": "Augstāka līmeņa mape", - "breadcrumb_root": "Sākums", - "drop_files_here": "Velciet failus vai mapes šeit, lai augšupielādētu", - "uploading": "Augšupielādē...", - "upload_success": "Augšupielādēts {count, plural, one {1 fails} other {# faili}}", - "upload_error": "Neizdevās augšupielādēt failu", - "create_folder_success": "Mape izveidota", - "create_folder_error": "Neizdevās izveidot mapi", - "delete_success": "Sekmīgi izdzēsts", - "delete_error": "Neizdevās izdzēst", - "rename_success": "Sekmīgi pārdēvēts", - "rename_error": "Neizdevās pārdēvēt", - "download_error": "Neizdevās lejupielādēt", - "not_available": "Failu krātuve šajā serverī nav pieejama", - "cancel": "Atcelt", - "create": "Izveidot", - "save": "Saglabāt", - "no_results": "Nav failu, kas atbilstu vaicājumam", - "batch_delete_confirm_message": "Vai tiešām vēlaties dzēst {count, plural, one {1 elementu} other {# elementus}}?", - "batch_delete_success": "Izdzēsts {count, plural, one {1 elements} other {# elementi}}", - "grid_view": "Režģis", - "list_view": "Saraksts", - "details": "Sīkāka informācija", - "path": "Ceļš", - "preview": "Priekšskatījums", - "preview_error": "Neizdevās ielādēt priekšskatījumu", - "cut": "Izgriezt", - "copy": "Kopēt", - "paste": "Ielīmēt", - "move_success": "Pārvietots {count, plural, one {1 elements} other {# elementi}}", - "move_error": "Neizdevās pārvietot", - "paste_success": "Ielīmēts sekmīgi", - "paste_error": "Neizdevās ielīmēt", - "new_text_file": "Jauns teksta fails", - "file_name": "Faila nosaukums", - "retry": "Mēģināt vēlreiz", - "refresh": "Atsvaidzināt", - "toggle_favorite": "Pārslēgt izlasi", - "duplicate": "Dublēt", - "duplicate_success": "Dublēšana pabeigta", - "duplicate_error": "Neizdevās dublēt", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Fails izveidots", - "create_file_error": "Neizdevās izveidot failu", - "favorites": "Izlase", - "recent": "Nesenie", - "properties": "Īpašības", - "open_folder": "Atvērt mapi", - "upload_folder": "Augšupielādēt mapi", - "file_too_large": "«{name}» pārsniedz maksimālo faila izmēru ({max})", - "undo": "Atsaukt", - "undo_success": "Darbība atsaukta", - "undo_error": "Neizdevās atsaukt", - "toolbar": "Failu rīkjosla", - "file_list": "Failu un mapju saraksts", - "context_menu": "Darbības", - "settings_title": "Failu iestatījumi", - "settings_display": "Attēlošana", - "settings_default_view": "Noklusējuma skats", - "settings_default_view_desc": "Izvēlieties starp režģi un sarakstu", - "settings_default_sort": "Noklusējuma kārtošana", - "settings_default_sort_desc": "Kā kārtot failus pēc noklusējuma", - "settings_sort_direction": "Kārtošanas virziens", - "settings_sort_direction_desc": "Augošā vai dilstošā secībā", - "settings_ascending": "Augošā", - "settings_descending": "Dilstošā", - "settings_icons": "Ikonas", - "settings_show_icons": "Rādīt failu ikonas", - "settings_show_icons_desc": "Rādīt ikonas blakus nosaukumiem", - "settings_colored_icons": "Krāsainas ikonas", - "settings_colored_icons_desc": "Izmantot krāsainas ikonas melnbalto vietā", - "settings_show_thumbnails": "Rādīt sīktēlus", - "settings_show_thumbnails_desc": "Rādīt attēlu priekšskatījumus", - "settings_behavior": "Darbība", - "settings_show_hidden": "Rādīt slēptos failus", - "settings_show_hidden_desc": "Rādīt failus, kas sākas ar punktu", - "settings_folder_layout": "Mapju izkārtojums", - "settings_folder_layout_desc": "Kā attēlot mapes navigācijā", - "settings_folder_layout_inline": "Kopā ar failiem", - "settings_folder_layout_sidebar": "Sānu josla", - "disabled_title": "Failu funkciju ir atspējojis administrators", - "disabled_description": "Lielu failu augšupielāde, izmantojot WebDAV, var radīt Stalwart/RocksDB nestabilitāti, tostarp atmiņas izsīkumu un neatkopjamu diska izmantojumu. Dzēstie faili var netikt nekavējoties izņemti no blob glabātuves. Šī funkcija nav ieteicama produkcijas vidēm.", - "stability_warning": "Lielu failu augšupielāde var radīt servera nestabilitāti. Dzēstie faili var netikt nekavējoties izņemti no glabātuves. Lietojiet piesardzīgi.", - "no_accounts": "Nav pievienotu kontu.", - "open_folder_tree": "Atvērt mapju koku", - "other_accounts": "Citi konti", - "migration_title": "Notiek jūsu failu atjaunināšana…", - "migration_description": "Mapes un faili tiek sakārtoti pareizajā struktūrā. Tas notiek tikai vienu reizi." - }, - "smime": { - "your_certificates": "Jūsu sertifikāti", - "your_certificates_desc": "Importējiet un pārvaldiet savus S/MIME sertifikātus parakstīšanai un šifrēšanai", - "recipient_certificates": "Saņēmēju sertifikāti", - "recipient_certificates_desc": "Publiskie sertifikāti saņēmēju vēstuļu šifrēšanai", - "identity_bindings": "Atslēgu piesaiste identitātēm", - "identity_bindings_desc": "Piesaistiet S/MIME sertifikātus savām pasta identitātēm", - "defaults_title": "Noklusējuma parametri", - "defaults_desc": "Iestatiet noklusējuma parakstīšanas un šifrēšanas darbības", - "import_pkcs12": "Importēt PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importēt sertifikātu", - "no_certificates": "Sertifikāti vēl nav importēti", - "no_recipient_certs": "Nav saņēmēju sertifikātu", - "expires": "Termiņš", - "expired": "Beidzies", - "bound_to": "Piesaistīts", - "no_key_bound": "Nav", - "lock": "Bloķēt atslēgu", - "unlock": "Atbloķēt atslēgu", - "details": "Rādīt sīkāku informāciju", - "delete": "Dzēst", - "encrypt_by_default": "Šifrēt pēc noklusējuma", - "encrypt_by_default_desc": "Automātiski šifrēt, ja visiem saņēmējiem ir sertifikāti", - "remember_unlocked": "Atcerēties atbloķētās atslēgas", - "remember_unlocked_desc": "Atstāt atslēgas atbloķētas šīs sesijas laikā", - "sign_default_for": "Parakstīt pēc noklusējuma", - "enter_p12_passphrase": "Ievadiet PKCS#12 paroli", - "p12_passphrase_desc": "Ievadiet paroli, kas aizsargā šo sertifikāta failu", - "enter_storage_passphrase": "Iestatiet krātuves paroli", - "storage_passphrase_desc": "Izvēlieties paroli atslēgas aizsardzībai pārlūkā", - "next": "Tālāk", - "import": "Importēt", - "unlock_key": "Atbloķēt atslēgu", - "unlock_key_desc": "Ievadiet krātuves paroli, lai atbloķētu atslēgu", - "passphrase_placeholder": "Ievadiet paroli", - "confirm_passphrase_placeholder": "Apstipriniet paroli", - "passphrase_mismatch": "Paroles nesakrīt", - "cancel": "Atcelt", - "processing": "Apstrādā…", - "close": "Aizvērt", - "certificate_details": "Sertifikāta dati", - "cert_subject": "Subjekts", - "cert_issuer": "Izdevējs", - "cert_email": "E-pasts", - "cert_serial": "Sērijas numurs", - "cert_validity": "Derīguma termiņš", - "cert_fingerprint": "Nospiedums (SHA-256)", - "cert_algorithm": "Algoritms", - "cert_capabilities": "Iespējas", - "cert_source": "Avots", - "cert_expired": "Šī sertifikāta termiņš ir beidzies", - "cert_not_yet_valid": "Šis sertifikāts vēl nav derīgs", - "cap_sign": "Paraksts", - "cap_encrypt": "Šifrēšana", - "cap_none": "Nav", - "show_passphrase": "Rādīt paroli", - "hide_passphrase": "Slēpt paroli", - "sign_toggle": "Parakstīt", - "encrypt_toggle": "Šifrēt", - "missing_recipient_certs": "Trūkst sertifikātu: {emails}", - "missing_sender_cert": "Šai identitātei nav piesaistīta sertifikāta", - "status_encrypted_ok": "Šis ziņojums tika šifrēts", - "status_encrypted_no_key": "Ziņojums ir šifrēts, bet piemērota atslēga nav atrasta", - "status_encrypted_failed": "Neizdevās atšifrēt šo ziņojumu", - "status_signed_valid": "Paraksts ir pārbaudīts", - "status_signed_invalid": "Paraksta pārbaude neizdevās", - "status_signed_expired_cert": "Parakstīts ar sertifikātu, kura termiņš beidzies", - "status_signed_mismatch": "Paraksts ir derīgs, bet parakstītājs nesakrīt ar sūtītāju", - "status_unsupported": "Neatbalstīts S/MIME formāts", - "auto_import_signer_certs": "Automātiski importēt parakstītāju sertifikātus", - "auto_import_signer_certs_desc": "Automātiski saglabāt sertifikātus no pārbaudītām vēstulēm", - "export": "Eksportēt", - "enter_export_passphrase": "Iestatiet eksporta paroli", - "export_passphrase_desc": "Izvēlieties paroli eksportējamajam PKCS#12 failam", - "export_storage_desc": "Ievadiet krātuves paroli atslēgas atšifrēšanai pirms eksporta", - "incorrect_passphrase": "Nepareiza parole", - "status_signed_self_signed": "Paraksts ir derīgs, bet sertifikāts ir pašparakstīts (nav uzticams)" - }, - "tour": { - "step_counter": "{current}. solis no {total}", - "skip": "Izlaist ekskursiju", - "back": "Atpakaļ", - "next": "Tālāk", - "finish": "Pabeigt", - "take_a_tour": "Sākt saskarnes ekskursiju", - "restart_title": "Iepazīšanās ekskursija", - "restart_desc": "Atkārtot soli pa solim pamācību par saskarni", - "restart_button": "Restartēt ekskursiju", - "show_on_new_devices_title": "Rādīt jaunās ierīcēs", - "show_on_new_devices_desc": "Atkārtot sveiciena reklāmkarogu un ekskursiju, pirmoreiz pierakstoties jaunā ierīcē, pat ja esat tos jau pabeidzis citur", - "sidebar_title": "Jūsu pastkastes", - "sidebar_desc": "Šī ir sānu josla ar mapēm. Noklikšķiniet uz jebkuras pastkastes, lai skatītu vēstules. Varat izveidot mapes un pārvietot vēstules.", - "compose_title": "Rakstīt vēstuli", - "compose_desc": "Nospiediet šeit, lai rakstītu jaunu vēstuli. Varat pievienot saņēmējus, pielikumus un formatēt tekstu.", - "search_title": "Pasta meklēšana", - "search_desc": "Meklējiet pēc sūtītāja, temata vai satura. Izmantojiet filtra ikonu izvērstai meklēšanai.", - "email_list_title": "Vēstuļu saraksts", - "email_list_desc": "Šeit redzamas vēstules. Noklikšķiniet uz vēstules, lai to lasītu. Izmantojiet izvēles rūtiņu vairāku vēstuļu atlasīšanai.", - "email_viewer_title": "Lasīšanas rūts", - "email_viewer_desc": "Atlasītā vēstule tiek atvērta šeit. Izmantojiet pogas, lai atbildētu, pārsūtītu, arhivētu vai dzēstu.", - "keywords_title": "Krāsu etiķetes", - "keywords_desc": "Organizējiet pastu ar krāsu etiķetēm. Velciet vēstuli uz etiķeti vai izmantojiet labo klikšķi.", - "calendar_title": "Kalendārs", - "calendar_desc": "Pārvaldiet savus pasākumus. Izveidojiet ierakstus, iestatiet atgādinājumus un mainiet skatus.", - "contacts_title": "Kontakti", - "contacts_desc": "Šeit atrodas jūsu adrešu grāmata. Importējiet kontaktus un organizējiet tos grupās.", - "settings_title": "Iestatījumi", - "settings_desc": "Pielāgojiet tēmu, parakstus, filtrus, īsinājumtaustiņus un daudz ko citu.", - "shortcuts_title": "Īsinājumtaustiņi", - "shortcuts_desc": "Nospiediet ?, lai skatītu visus īsinājumtaustiņus un pārvaldītu pastu bez peles.", - "compose_open_title": "Vēstuļu redaktors", - "compose_open_desc": "Šeit varat rakstīt ziņojumu, pievienot failus un izmantot veidnes.", - "calendar_view_title": "Jūsu kalendārs", - "calendar_view_desc": "Šis ir kalendārs ar parauga pasākumiem. Mainiet skatus starp dienu, nedēļu un mēnesi.", - "create_event_title": "Izveidot pasākumu", - "create_event_desc": "Nospiediet šo pogu, lai pievienotu jaunu pasākumu, norādot laiku un dalībniekus.", - "event_modal_title": "Pasākuma informācija", - "event_modal_desc": "Aizpildiet pasākuma formu un nospiediet «Saglabāt», kad esat pabeidzis.", - "contacts_list_title": "Jūsu kontakti", - "contacts_list_desc": "Skatīt un rediģēt kontaktus, importēt vCard vai veidot grupas.", - "settings_tabs_title": "Iestatījumu izvēlne", - "settings_tabs_desc": "Šeit atradīsiet visas iestatījumu kategorijas - no izskata līdz drošībai.", - "files_title": "Failu krātuve", - "files_desc": "Failu pārvaldnieks ļauj augšupielādēt un organizēt failus kā personīgo mākoņkrātuvi.", - "demo_banner_title": "Demo vadība", - "demo_banner_desc": "Jūs esat demo režīmā - visi dati paliek jūsu pārlūkā. Nospiediet «Atiestatīt demo», lai sāktu no jauna.", - "quota_title": "Krātuves izmantošana", - "quota_desc": "Sekojiet līdzi savas pastkastes aizpildījumam šeit." - }, - "unified_mailbox": { - "search_unavailable": "Meklēšana nav pieejama apvienotajā skatā" - }, - "calendar_event_notifications": { - "someone": "Kāds", - "invited": "{name} uzaicināja jūs uz “{title}”", - "updated": "{name} atjaunināja “{title}”", - "cancelled": "{name} atcēla “{title}”", - "untitled": "Notikums bez nosaukuma" - }, - "share_notifications": { - "someone": "Kāds", - "shared": "{name} kopīgoja ar jums {kind} “{object}”", - "changed": "{name} mainīja jūsu piekļuvi {kind} “{object}”", - "revoked": "{name} noņēma jūsu piekļuvi {kind} “{object}”", - "object": { - "folder": "mapi", - "calendar": "kalendāru", - "address_book": "adrešu grāmatu", - "files": "failu mapi" - } - }, - "sharing": { - "title": "Kopīgot \"{name}\"", - "description": "Piešķiriet piekļuvi citiem lietotājiem vai grupām šajā serverī. Izmaiņas stājas spēkā nekavējoties.", - "no_shares": "Vēl nav kopīgots.", - "add_person": "Pievienot personu vai grupu", - "search_placeholder": "Meklēt pēc vārda vai e-pasta…", - "loading_principals": "Ielādē lietotājus…", - "no_principals": "Citi lietotāji vai grupas nav atrastas.", - "no_match": "Nav atbilstību.", - "remove": "Noņemt piekļuvi", - "group": "Grupa", - "share_added": "Piekļuve piešķirta", - "share_updated": "Piekļuve atjaunināta", - "share_removed": "Piekļuve noņemta", - "share_failed": "Neizdevās atjaunināt kopīgošanu", - "preset": { - "freeBusy": "Tikai brīvs/aizņemts", - "read": "Tikai lasīšana", - "readWrite": "Lasīšana un rakstīšana", - "manager": "Pārvaldnieks", - "custom": "Pielāgots" - } - }, - "quote_header": { - "reply_line": "{date} {from} rakstīja:", - "forwarded_separator": "---------- Pārsūtītā ziņa ----------", - "from_label": "No", - "date_label": "Datums", - "subject_label": "Tēma" - }, - "pwa_install": { - "title": "Instalēt {appName}", - "description": "Instalējiet mūsu lietotni ātrai piekļuvei un bezsaistes atbalstam.", - "not_now": "Ne tagad", - "install": "Instalēt", - "dont_remind": "Vairs man neatgādināt", - "dismiss_aria": "Aizvērt instalēšanas paziņojumu" - }, - "deep_link": { - "message_not_found": "Šī ziņa vairs nav pieejama.", - "thread_not_found": "Šī sarakste vairs nav pieejama.", - "folder_not_found": "Šī mape vairs nav pieejama.", - "event_not_found": "Šis notikums vairs nav pieejams.", - "file_not_found": "Šis fails vairs nav pieejams.", - "account_unavailable": "Piesakieties šajā kontā, lai atvērtu saiti.", - "copy_message": "Kopēt saiti uz ziņu", - "copy_conversation": "Kopēt saiti uz saraksti", - "copy_event": "Kopēt saiti uz notikumu", - "copied": "Saite nokopēta", - "copy_failed": "Neizdevās nokopēt saiti" - }, - "plugins": { - "oauth_callback": { - "completing": "Pabeidz autorizāciju…", - "completing_message": "Lūdzu, uzgaidiet, kamēr tiek izveidots savienojums.", - "received_title": "Autorizācija saņemta", - "received_message": "Varat atgriezties cilnē, kurā sākāt savienojumu.", - "failed_title": "Autorizācija neizdevās", - "denied": "Autorizācija tika atteikta.", - "failed_detail": "Autorizācija neizdevās: {error}", - "missing_params": "Trūkst autorizācijas kods vai stāvoklis.", - "delivery_failed": "Autorizācijas rezultātu neizdevās nodot lietotnes cilnei. Aizveriet šo logu un mēģiniet vēlreiz." - } - }, - "global_search": { - "title": "Meklēt visā", - "placeholder": "Meklēt pastā, kontaktos, kalendārā, failos...", - "scope_label": "Meklēt", - "account_label": "Konts", - "scope_all": "Viss", - "scope_mail": "Pasts", - "scope_contacts": "Kontakti", - "scope_calendar": "Kalendārs", - "scope_files": "Faili", - "all_accounts": "Visi konti", - "show_all": "Rādīt visus rezultātus", - "no_results": "Nav rezultātu vaicājumam “{query}”", - "server_hint": "Pilni vārdi meklē serverī; vārdu daļas atbilst jau ielādētajiem vienumiem", - "searching": "Meklē...", - "account_error": "Neizdevās meklēt kontā {account}", - "recent_searches": "Pēdējie meklējumi", - "recurring": "Period isks", - "no_subject": "(no subject)", - "empty_hint": "Meklējiet pastu, kontaktus, notikumus un failus visos kontos. Izmēģiniet from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Plakani pēc datuma", - "grouped": "Grupēts", - "load_more": "Ielādēt vairāk", - "preview_empty": "Izvēlieties rezultātu, lai to priekšskatītu", - "open_full": "Atvērt", - "view_label": "Skats" - } -} +{ + "meta_description": "Minimālistisks tīmekļa pasta klients, kas izmanto JMAP protokolu", + "login": { + "title": "Tīmekļa pasts", + "username_label": "E-pasta adrese", + "username_placeholder": "lietotajs@piemers.lv", + "password_label": "Parole", + "password_placeholder": "Ievadiet paroli", + "jmap_endpoint_label": "JMAP serveris", + "jmap_endpoint_placeholder": "https://pasts.piemers.lv", + "jmap_endpoint_cors_hint": "Serverim ir jāatļauj CORS pieprasījumi no šī domēna.", + "jmap_server_label": "Serveris", + "jmap_server_auto_picked": "Serveris izvēlēts no jūsu e-pasta domēna.", + "sign_in": "Pierakstīties", + "signing_in": "Notiek pierakstīšanās...", + "loading": "Ielāde...", + "reconnecting": "Savienojums pārtraukts. Mēģina izveidot savienojumu...", + "error": { + "invalid_credentials": "Nepareiza e-pasta adrese vai parole. Pārbaudiet datus un mēģiniet vēlreiz.", + "invalid_token": "Serveris nepieņēma šo piekļuves marķieri. Pārbaudiet, vai tas ir pilnīgs un nav beidzies vai atsaukts.", + "connection_failed": "Neizdevās izveidot savienojumu ar serveri. Pārbaudiet interneta pieslēgumu.", + "cors_blocked": "Serveris ir pieejams, bet bloķē pieprasījumus no citiem avotiem. Pārbaudiet JMAP servera CORS iestatījumus.", + "server_error": "Serveris īslaicīgi nav pieejams. Mēģiniet vēlāk.", + "generic": "Notikusi neparedzēta kļūda. Ja tā atkārtojas, sazinieties ar administratoru.", + "totp_invalid": "Nepareizs autentifikācijas kods. Pārbaudiet lietotni un mēģiniet vēlreiz.", + "oauth_discovery_failed": "SSO ir iespējots, bet autentifikācijas pakalpojums nav pieejams. Pārbaudiet OAuth iestatījumus.", + "token_exchange_failed": "Parole un kods tika pieņemti, taču pasta serveris atteicās sākt sesiju šai lietotnei. Lūdziet administratoram pārbaudīt OAuth klienta iestatījumus.", + "totp_required": "Nepieciešams divu faktoru autentifikācijas kods. Lūdzu, ievadiet kodu zemāk." + }, + "show_password": "Rādīt paroli", + "hide_password": "Slēpt paroli", + "totp_toggle": "Man ir 2FA kods", + "token_toggle": "Pierakstīties ar piekļuves marķieri", + "password_toggle": "Pierakstīties ar paroli", + "token_label": "Piekļuves marķieris", + "token_placeholder": "Ielīmējiet savu API marķieri", + "token_hint": "Izveidojiet API marķieri sava e-pasta pakalpojumu sniedzēja iestatījumos un ielīmējiet to šeit.", + "remember_me": "Atcerēties mani", + "config_error": { + "title": "Konfigurācijas kļūda", + "fetch_failed": "Neizdevās ielādēt lietotnes konfigurāciju. Mēģiniet vēlāk.", + "server_not_configured": "Pasta serveris nav konfigurēts. Sazinieties ar administratoru." + }, + "remove_from_history": "Noņemt no vēstures", + "totp_label": "Autentifikācijas kods", + "totp_placeholder": "000000", + "session_expired": "Sesija ir beigusies. Lūdzu, pierakstieties vēlreiz.", + "dismiss": "Aizvērt", + "or": "vai", + "sign_in_sso": "Pierakstīties ar SSO", + "add_account_title": "Pievienot kontu", + "add_account_subtitle": "Pierakstīties citā kontā", + "cancel": "Atcelt", + "website": "Tīmekļa vietne", + "imprint": "Rekvizīti", + "privacy_policy": "Privātuma politika", + "try_demo": "Izmēģināt demo", + "demo_description": "Izpētiet ar paraugdatiem - konts nav nepieciešams", + "demo_launching": "Demo palaišana...", + "demo_login_button": "Palaist demo", + "demo_tagline": "Izmēģiniet pilnvērtīgu e-pasta klientu bez konta izveides.", + "demo_no_signup": "Reģistrācija nav nepieciešama - brīvi pētiet paraugdatus", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Pabeidz pierakstīšanos...", + "oauth_error": { + "title": "Autentifikācijas kļūda", + "invalid_state": "Drošības pārbaudes kļūda. Mēģiniet pierakstīties vēlreiz.", + "missing_params": "Trūkst autorizācijas datu. Mēģiniet pierakstīties vēlreiz.", + "token_exchange_failed": "Neizdevās pabeigt autentifikāciju. Mēģiniet vēlreiz.", + "access_denied": "Piekļuve liegta. Sazinieties ar administratoru.", + "back_to_login": "Atgriezties pie pierakstīšanās" + } + }, + "sidebar": { + "close": "Aizvērt", + "compose": "Rakstīt", + "compose_hint": "Rakstīt (c)", + "search_placeholder": "Meklēt pastā...", + "search_placeholder_hint": "Meklēt pastā... (nospiediet /)", + "storage": "Krātuve", + "storage_used": "Izmantots", + "storage_free": "Brīvs", + "storage_total": "Kopā", + "sign_out": "Izrakstīties", + "sign_out_of": "Izrakstīties no {account}", + "sign_out_all": "Izrakstīties no visiem kontiem", + "add_account": "Pievienot kontu", + "set_as_default": "Iestatīt kā noklusējumu", + "switch_account": "Mainīt kontu", + "contacts": "Kontakti", + "calendar": "Kalendārs", + "settings": "Iestatījumi", + "admin": "Admin", + "files": "Faili", + "loading_mailboxes": "Ielādē mapes...", + "push_connected": "Reāllaika atjauninājumi ir aktīvi", + "push_disconnected": "Reāllaika atjauninājumi nav aktīvi", + "keyboard_shortcuts": "Tastatūras īsinājumtaustiņi", + "theme": { + "light": "Gaišā tēma", + "dark": "Tumšā tēma", + "system": "Sistēmas tēma" + }, + "language": { + "title": "Valoda" + }, + "mailboxes": { + "inbox": "Iesūtne", + "sent": "Nosūtītie", + "drafts": "Melnraksti", + "trash": "Mēstule", + "archive": "Arhīvs", + "starred": "Ar zvaigznīti", + "all_mail": "Visas vēstules", + "spam": "Mēstules", + "important": "Svarīgi" + }, + "unified_inbox": "Apvienotā iesūtne", + "unified_sent": "Visi nosūtītie", + "unified_drafts": "Visi melnraksti", + "unified_trash": "Visas mēstules", + "unified_archive": "Visi arhīvi", + "unified_junk": "Viss mēstules", + "all_accounts": "Visi konti", + "all_accounts_loading": "Ielādē kontus: {loaded} no {total}", + "unified_mailbox": "Apvienotā pastkaste", + "expand": "Izvērst", + "collapse": "Sairt", + "expand_tooltip": "Izvērst", + "collapse_tooltip": "Sairt", + "mobile": { + "search": "Meklēt", + "compose": "Rakstīt", + "go_back": "Atpakaļ", + "toggle_menu": "Pārslēgt izvēlni" + }, + "clear_search": "Notīrīt meklēšanu", + "vacation_active": "Atvaļinājuma atbilde ir aktīva", + "demo_banner": "Demo režīms", + "demo_reset": "Atiestatīt", + "demo_tour": "Ekskursija", + "tags": "Tagi", + "show_all_tags": "Rādīt visus ({count})", + "show_fewer_tags": "Rādīt mazāk", + "folders": "Mapes", + "your_folders": "Jūsu mapes", + "create_folder": "Izveidot mapi", + "show_more": "Vairāk", + "show_fewer": "Mazāk", + "mail": "Pasts", + "nav_label": "Navigācija", + "add_app": "Lietotnes", + "shared": "Koplietots", + "shared_account_hidden": "Paslēpts no sānjoslas. Atkal parādiet sadaļā Iestatījumi › Izkārtojums.", + "notifications_muted": "Paziņojumi izslēgti", + "scheduled": "Ieplānots", + "unified_all_unread": "Visi nelasītie", + "unified_all_starred": "Visi ar zvaigzni", + "unified_all_mail": "Visas vēstules", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Etiķetes", + "create_label": "Izveidot jaunu etiķeti", + "mv_tasks": "Uzdevumi", + "mv_side_panel": "Sānu panelis", + "mv_open_full": "Atvērt pilnā skatā", + "mv_previous_day": "Iepriekšējā diena", + "mv_next_day": "Nākamā diena", + "mv_add_another_account": "Pievienot citu kontu", + "mv_manage_account": "Pārvaldīt kontu", + "mv_storage_used": "Izmantoti {percent}% no {total}" + }, + "protocol_handlers": { + "title": "Noklusējuma lietotnes", + "description": "Izvēlieties, vai e-pasta un kalendāra saites atvērt Bulwark. Tehniski Bulwark reģistrējas kā protokola apstrādātājs mailto: un webcal: saitēm.", + "unsupported": "Šī pārlūkprogramma vai savienojums neatbalsta manuālu protokola apstrādātāja reģistrāciju. Iespējams, instalēto PWA joprojām var izmantot pārlūkprogrammas vai sistēmas iestatījumos.", + "mailto_label": "E-pasta saites", + "mailto_description": "Atver mailto: saites Bulwark ar iepriekš aizpildītu ziņojuma redaktoru.", + "protocol_open_mode_label": "Atverot protokola saites", + "protocol_open_mode_description": "Izvēlieties, vai Bulwark atver mailto: un webcal: saites jaunā cilnē vai atkārtoti izmanto atvērtu sesiju. Aktīvās sesijas opcijai nepieciešama paziņojumu atļauja, lai jūs varētu noklikšķināt uz rezerves paziņojuma un izcelt Bulwark priekšplānā, ja pārlūkprogramma bloķē fokusu.", + "protocol_open_mode_active_session": "Ja iespējams, atvērt aktīvajā sesijā", + "protocol_open_mode_new_tab": "Vienmēr atvērt jaunu cilni", + "focus_notification_title": "Atvērt Bulwark", + "focus_notification_body": "Saite tika atvērta Bulwark. Noklikšķiniet, lai izceltu logu priekšplānā.", + "webcal_label": "Kalendāra saites", + "webcal_description": "Atver webcal: saites Bulwark ar iepriekš aizpildītu kalendāra abonēšanas dialogu.", + "register_mailto": "Reģistrēt e-pasta lietotni", + "register_webcal": "Reģistrēt kalendāra lietotni", + "mailto_registered": "E-pasta apstrādātāja reģistrācija pieprasīta", + "webcal_registered": "Kalendāra apstrādātāja reģistrācija pieprasīta", + "registration_failed": "Protokola apstrādātāja reģistrācija neizdevās", + "opening_mailto": "Tiek atvērts redaktors...", + "opening_webcal": "Tiek atvērts kalendārs...", + "browser_note": "Pārlūkprogramma vai operētājsistēma var lūgt apstiprinājumu un var prasīt, lai Bulwark būtu instalēts, pirms to var izvēlēties kā noklusējuma lietotni.", + "select_account_title": "Izvēlieties kontu", + "select_mailto_account": "Izvēlieties, kurā kontā atvērt šo e-pasta saiti.", + "select_webcal_account": "Izvēlieties, kurā kontā atvērt šo kalendāra saiti.", + "select_account_note": "Šī izvēle attiecas tikai uz šo protokola saiti.", + "detail_to": "Kam", + "detail_subject": "Temats", + "detail_no_subject": "Bez temata", + "detail_calendar": "Kalendārs", + "detail_source": "Avots", + "active_account": "Aktīvs", + "switching_account": "Notiek konta pārslēgšana..." + }, + "sidebar_apps": { + "modal_title": "Sānu joslas lietotnes", + "add_new": "Pievienot lietotni", + "edit_app": "Rediģēt lietotni", + "name_label": "Nosaukums", + "name_placeholder": "Mana lietotne", + "name_required": "Nosaukums ir obligāts", + "url_label": "URL adrese", + "url_required": "URL ir obligāts", + "url_invalid": "Ievadiet derīgu http vai https URL", + "icon_label": "Ikona", + "icon_required": "Ikona ir obligāta", + "open_mode_label": "Atvēršanas režīms", + "open_new_tab": "Jaunā cilnē", + "open_inline": "Iebūvēts", + "cancel": "Atcelt", + "add": "Pievienot", + "update": "Atjaunināt", + "delete": "Dzēst", + "delete_confirm_title": "Dzēst lietotni", + "delete_confirm": "Vai tiešām vēlaties dzēst \"{name}\"?", + "no_apps": "Lietotnes vēl nav pievienotas", + "no_apps_hint": "Pievienojiet pielāgotas lietotnes un saites sānu joslā", + "managed_title": "Nodrošina administrators", + "managed_description": "Šīs lietotnes ir iestatītas visiem, un tās nevar mainīt.", + "managed_badge": "Pārvaldīta", + "search_icons": "Meklēt ikonas...", + "show_popular": "Populārākās", + "show_all": "Visas", + "no_icons_found": "Ikonas nav atrastas", + "inline_badge": "Iebūvēts", + "tab_badge": "Cilne", + "show_on_mobile": "Rādīt mobilajā ierīcē" + }, + "email_list": { + "columns": { + "from": "No", + "to": "Kam", + "subject": "Temats", + "date": "Datums", + "size": "Izmērs" + }, + "no_emails": "Vēstules nav atrastas", + "no_emails_description": "Mape ir tukša", + "no_search_results": "Nekas netika atrasts", + "no_search_results_description": "Mēģiniet mainīt meklēšanas vaicājumu vai filtrus", + "loading": "Ielādē vēstules...", + "unread": "nelasītas", + "to_me": "Man", + "to_recipients": "{count} saņēmējiem", + "and_others": "un vēl {count}", + "draft": "Melnraksts", + "starred": "Ar zvaigznīti", + "pinned": "Piesprausts", + "replied": "Atbildēts", + "forwarded": "Pārsūtīts", + "has_attachment": "Ar pielikumu", + "conversations_range": "{from}–{to, number} no {total, number}", + "conversations_count": "{count} no {total} sarunām", + "conversations_count_plus": "{count}+ sarunas", + "conversations_count_simple": "{count} sarunas", + "no_conversations": "Nav sarunu", + "loading_more": "Ielādē vēstules...", + "no_more_emails": "Vairāk vēstuļu nav", + "batch_actions": { + "select": "Atlasīt vēstules", + "select_all": "Atlasīt visas", + "selected_messages": "{count, plural, one {Izvēlēta 1 e-pasta vēstule} other {Izvēlētas # e-pasta vēstules}}", + "mark_read": "Atzīmēt kā izlasītu", + "mark_unread": "Atzīmēt kā nelasītu", + "delete": "Dzēst", + "delete_confirm_title": "Dzēst vēstules", + "delete_confirm_message": "Vai tiešām vēlaties dzēst {count, plural, one {1 vēstuli} other {# vēstules}}?", + "clear_selection": "Noņemt atlasi" + }, + "permanent_delete": "Dzēst neatgriezeniski", + "permanent_delete_confirm_title": "Dzēst neatgriezeniski", + "permanent_delete_confirm_message": "Šī vēstule tiks dzēsta neatgriezeniski. Šo darbību nevar atcelt.", + "permanent_delete_confirm_batch_message": "{count, plural, one {1 vēstule} other {# vēstules}} tiks dzēstas neatgriezeniski. Šo darbību nevar atcelt.", + "empty_folder": { + "button": "Iztīrīt mapi", + "confirm_title": "Iztīrīt mapi", + "confirm_message": "Visas vēstules šajā mapē tiks dzēstas neatgriezeniski. Šo darbību nevar atcelt.", + "confirm_message_trash": "Visas šīs mapes vēstules tiks pārvietotas uz atkritni.", + "confirm_button": "Iztīrīt mapi", + "junk_hint": "Jūs varat iztīrīt mapi Mēstules, lai neatgriezeniski dzēstu visus ziņojumus.", + "trash_hint": "Jūs varat iztīrīt Atkritni, lai neatgriezeniski dzēstu visus ziņojumus." + }, + "no_scheduled_emails": "Nav ieplānotu e-pastu", + "no_scheduled_emails_description": "Ziņojumi, kas ieplānoti vēlākai nosūtīšanai, būs redzami šeit.", + "scheduled_count": "{count, plural, one {1 ieplānots e-pasts} other {# ieplānoti e-pasti}}", + "scheduled_actions_hint": "Ieplānotos ziņojumus var atcelt, pārplānot vai rediģēt no ieplānotajām darbībām.", + "cancel_scheduled_send": "Atcelt sūtīšanu", + "reschedule_send": "Pārplānot", + "cancel_and_edit": "Atcelt un rediģēt", + "cancel_and_compose_again": "Atcelt un rakstīt no jauna", + "reschedule_prompt": "Ievadiet jaunu datumu/laiku, piem. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Sūtītājs pieprasa lasīšanas apstiprinājumu:", + "send": "Sūtīt apstiprinājumu", + "ignore": "Ignorēt", + "sent": "Lasīšanas apstiprinājums nosūtīts.", + "send_failed": "Neizdevās nosūtīt lasīšanas apstiprinājumu", + "mdn_subject": "Izlasīts: {subject}", + "mdn_body": "Šis ir lasīšanas apstiprinājums ziņojumam, ko nosūtījāt uz {recipient}.\n\nPiezīme: šis apstiprinājums tikai apliecina, ka ziņojums tika parādīts saņēmēja datorā. Tas negarantē, ka saņēmējs ir izlasījis vai sapratis saturu." + }, + "no_email_selected": "Nav atlasīta neviena vēstule", + "no_email_description": "Atlasiet vēstuli no saraksta, lai to skatītu šeit", + "no_conversation_selected": "Nav atlasīta saruna", + "no_conversation_description": "Atlasiet sarunu no saraksta, lai to lasītu šeit", + "compose": "Rakstīt", + "compose_hint": "Rakstīt jaunu ziņojumu", + "no_subject": "(nav temata)", + "no_body_content": "(nav satura)", + "no_preview_available": "Priekšskatījums nav pieejams", + "show_quoted_text": "Rādīt citēto tekstu", + "hide_quoted_text": "Paslēpt citēto tekstu", + "loading_email": "Ielādē vēstuli...", + "loading": "Ielādē...", + "reply": "Atbildēt", + "reply_all": "Atbildēt visiem", + "forward": "Pārsūtīt", + "delete": "Dzēst", + "archive": "Arhivēt", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Pievienot zvaigznīti", + "unstar": "Noņemt zvaigznīti", + "mark_unread": "Atzīmēt kā nelasītu", + "mark_read": "Atzīmēt kā izlasītu", + "unread": "Nelasīts", + "read": "Lasīts", + "spam_short": "Spams", + "not_spam_short": "Nav spams", + "move": "Pārvietot", + "print": "Drukāt", + "view_source": "Skatīt avota kodu", + "fullscreen": "Pilnekrāna režīms", + "exit_fullscreen": "Iziet no pilnekrāna režīma", + "view_in_dark_mode": "Skatīt tumšajā režīmā", + "view_in_light_mode": "Skatīt gaišajā režīmā", + "export_email": "Eksportēt kā .eml", + "forward_as_attachment": "Pārsūtīt kā pielikumu", + "import_email": "Importēt .eml vai .zip", + "keyboard_shortcuts": "Īsinājumtaustiņi (?)", + "email_source": "Vēstules avota kods", + "draft_banner": "Šī vēstule ir melnraksts", + "edit_draft": "Rediģēt", + "copy_source": "Kopēt starpliktuvē", + "source_copied": "Avota kods nokopēts starpliktuvē", + "attachments": "Pielikumi", + "important": "Svarīgi", + "download": "Lejupielādēt", + "download_all": "Lejupielādēt visu", + "from": "No", + "to": "Kam", + "cc": "Kopija", + "bcc": "Slepenā kopija", + "date": "Datums", + "subject": "Temats", + "show_details": "Rādīt sīkāku informāciju", + "hide_details": "Slēpt sīkāku informāciju", + "external_content_warning": "Attēli un ārējais saturs ir bloķēts", + "load_external_content": "Ielādēt attēlus", + "trust_sender": "Vienmēr uzticēties šim sūtītājam", + "sender_check": { + "unverified_label": "Nepārbaudīts sūtītājs", + "failed_label": "Sūtītāja pārbaude neizdevās", + "unverified": "Šis ziņojums apgalvo, ka ir no domēna {domain}, taču nekas to neapstiprina. Tam nav derīga domēna {domain} paraksta, un tas nav nācis no servera, ko domēns {domain} ir atļāvis.", + "unverified_sent_from": "Šis ziņojums apgalvo, ka ir no domēna {domain}, taču tas nosūtīts no domēna {host}, un tam nav derīga domēna {domain} paraksta.", + "failed": "Šis ziņojums apgalvo, ka ir no domēna {domain}, taču tas neizturēja šī domēna sūtītāja pārbaudes. Tas var būt viltots.", + "failed_sent_from": "Šis ziņojums apgalvo, ka ir no domēna {domain}, taču tas nosūtīts no domēna {host} un neizturēja domēna {domain} sūtītāja pārbaudes. Tas var būt viltots.", + "caution": "Esiet piesardzīgi ar saitēm, pielikumiem un paroļu vai maksājumu datu pieprasījumiem." + }, + "back_to_list": "Atpakaļ uz sarakstu", + "view_contact": "Skatīt kontaktu", + "message_details": "Informācija par ziņojumu", + "more_reply_options": "Papildu atbildēšanas iespējas", + "set_tag": "Iestatīt tagu", + "tag": "Tags", + "more_actions": "Citas darbības", + "previous": "Iepr.", + "next": "Nāk.", + "move_to": "Pārvietot uz...", + "remove_tag": "Noņemt tagu", + "tag_filter_placeholder": "Filtrēt tagus", + "tag_no_matches": "Nav atbilstošu tagu", + "more_count": "+vairāk {count}", + "characters_count": "{count} rakstzīmes", + "quick_reply_placeholder": "Rakstīt ātru atbildi...", + "more_options": "Citas iespējas", + "sending": "Sūta...", + "security_authentication": "Drošība un autentifikācija", + "technical_details": "Tehniskā informācija", + "message_id_label": "Ziņojuma ID:", + "reply_to_label": "Atbildes adrese:", + "delivery_time_label": "Piegādes laiks:", + "conversation_part_label": "Sarunas daļa:", + "previous_messages": "{count} iepriekšējais ziņojums", + "previous_messages_plural": "{count} iepriekšējie ziņojumi", + "time": { + "day": "diena", + "days": "dienas", + "hour": "stunda", + "hours": "stundas", + "minute": "minūte", + "minutes": "minūtes" + }, + "unknown_sender": "Nezināms", + "recipient_me": "es", + "recipient_and_others": "{name} un vēl {count}", + "recipient_to_prefix": "Kam:", + "authentication": { + "title": "Autentifikācija", + "status": { + "verified": "Apstiprināts", + "warning": "Brīdinājums", + "none": "Nav autentificēts" + }, + "spf": { + "pass": "SPF izturēts", + "fail": "SPF nav izturēts", + "none": "SPF nav atrasts" + }, + "dkim": { + "pass": "DKIM derīgs", + "fail": "DKIM nederīgs", + "none": "DKIM nav atrasts" + }, + "dmarc": { + "pass": "DMARC izturēts", + "fail": "DMARC nav izturēts", + "none": "DMARC nav atrasts" + }, + "spam_score": "Mēstuļu vērtējums", + "tooltip_spf": "Sender Policy Framework: pārbauda, vai sūtošajam serverim ir tiesības sūtīt e-pastu domēna vārdā", + "tooltip_dkim": "DomainKeys Identified Mail: ar kriptogrāfisku parakstu apstiprina, ka vēstule pārsūtīšanas laikā nav grozīta", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: nodrošina, ka SPF un DKIM atbilst sūtītāja domēnam, un nosaka politiku kļūmju gadījumiem", + "policy": "Politika", + "result": { + "pass": "Izturēts", + "fail": "Nav izturēts", + "softfail": "Daļēja kļūme", + "neutral": "Neitrāli", + "permerror": "Pastāvīga kļūda", + "temperror": "Pagaidu kļūda", + "none": "Nav" + } + }, + "headers": { + "routing": "Maršrutēšana", + "received": "Saņemts", + "message_id": "Ziņojuma ID", + "list_info": "Informācija par adresātu sarakstu" + }, + "tooltips": { + "reply": "Atbildēt (r)", + "reply_all": "Atbildēt visiem (a)", + "forward": "Pārsūtīt (f)", + "archive": "Arhivēt (e)", + "delete": "Dzēst (# vai Del)", + "star": "Pievienot zvaigznīti (s)", + "unstar": "Noņemt zvaigznīti (s)", + "compose": "Rakstīt (c)", + "previous": "Iepriekšējā vēstule", + "next": "Nākamā vēstule", + "edit_draft": "Rediģēt melnrakstu" + }, + "spam": { + "button_title": "Ziņot par mēstuli", + "not_spam_title": "Nav mēstule", + "toast_success": "Pārvietots uz mēstulēm", + "toast_batch": "{count} vēstules pārvietotas uz mēstulēm", + "toast_undo": "Atsaukt", + "toast_not_spam_success": "Pārvietots uz iesūtni", + "toast_not_spam_batch": "{count} vēstules pārvietotas uz iesūtni", + "error": "Neizdevās ziņot par mēstuli", + "error_not_spam": "Neizdevās atjaunot vēstuli" + }, + "unsubscribe_banner": { + "label": "Adresātu saraksts", + "button": "Atteikties", + "confirm_title": "Atteikties no šī sūtītāja?", + "confirm_button": "Apstiprināt", + "cancel": "Atcelt", + "success_http": "Atteikšanās lapa atvērta jaunā cilnē", + "success_mailto": "Atrakstīšanās e-pasts nosūtīts", + "confirm_message_http": "Atrakstīšanās lapa tiks atvērta jaunā cilnē.", + "confirm_message_mailto": "Sūtītājam tiks nosūtīts atrakstīšanās e-pasts.", + "error": "Neizdevās atteikties", + "dismiss": "Aizvērt" + }, + "calendar_invitation": { + "loading": "Ielādē pasākuma informāciju...", + "title": "Kalendāra uzaicinājums", + "published_title": "Publicēts pasākums", + "response_title": "Atbilde uz pasākumu", + "update_title": "Pasākuma atjauninājums", + "counter_title": "Pretpiedāvājums", + "refresh_title": "Atjaunināšanas pieprasījums", + "declined_counter_title": "Pretpiedāvājums noraidīts", + "cancelled_title": "Pasākums atcelts", + "organizer": "Organizators: {name}", + "organizer_label": "Organizators:", + "attendees": "{count, plural, one {# dalībnieks} other {# dalībnieki}}", + "accept": "Pieņemt", + "maybe": "Varbūt", + "decline": "Noraidīt", + "add_to_calendar": "Pievienot kalendāram", + "added": "Pievienots kalendāram", + "rsvp_sent": "Atbilde nosūtīta", + "parse_error": "Neizdevās nolasīt uzaicinājumu", + "action_failed": "Neizdevās veikt darbību kalendārā.", + "no_calendar": "Kalendārs nav pieejams", + "published_info": "Šis pasākums ir publicēts informācijai.", + "response_info": "Šis ziņojums satur dalībnieka atbildi.", + "response_info_organizer": "Šī dalībnieka atbilde atjaunina jūsu pasākumu.", + "update_info": "Šis ziņojums atjaunina esošu pasākumu.", + "counter_info": "Šis ziņojums piedāvā izmaiņas pasākumā.", + "counter_info_organizer": "Šis dalībnieks ierosināja izmaiņas jūsu pasākumā.", + "refresh_info": "Šis ziņojums pieprasa jaunāko informāciju par pasākumu.", + "refresh_info_organizer": "Dalībnieks pieprasīja jaunāko informāciju par pasākumu.", + "declined_counter_info": "Organizators noraidīja pretpiedāvājumu.", + "authentication_failed_info": "Šī uzaicinājuma pasta autentifikācija neizdevās. Esiet uzmanīgi.", + "authentication_missing_info": "Šim uzaicinājumam nav apstiprinātas autentifikācijas.", + "sender_mismatch_info": "Uzaicinājumu nosūtīja {sender}, bet organizators ir {organizer}.", + "sender_mismatch_unverified_info": "Uzaicinājumu nosūtīja {sender}, bet norādītais organizators ir {organizer}, un ziņojumu nevarēja pārbaudīt.", + "organizer_role": "Jūs esat šī pasākuma organizators", + "your_response": "Jūsu atbilde: {status}", + "response_needed": "Nepieciešama atbilde", + "response_accepted": "Pieņemts", + "response_tentative": "Pagaidām", + "response_declined": "Noraidīts", + "response_delegated": "Deleģēts", + "actor_sent_info": "Nosūtīja {name}.", + "actor_response_info": "{name} atbildēja: {status}.", + "actor_counter_info": "{name} ierosināja izmaiņas pasākumā.", + "actor_refresh_info": "{name} pieprasīja jaunāko informāciju.", + "actor_declined_counter_info": "{name} noraidīja pretpiedāvājumu.", + "actor_note": "Piezīme: {comment}", + "actor_unknown": "Kāds", + "proposed_changes": "Ierosinātās izmaiņas", + "change_title": "Virsraksts", + "change_time": "Laiks", + "change_location": "Atrašanās vieta", + "change_description": "Apraksts", + "change_empty": "Nav", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Apstiprināt izmaiņas", + "proposal_applied": "Izmaiņas ir apstiprinātas.", + "review_proposal": "Pārskatīt piedāvājumu", + "review_request": "Pārskatīt pieprasījumu", + "view_in_calendar": "Atvērt kalendārā", + "select_calendar": "Izvēlēties kalendāru", + "already_in_calendar": "Jau ir jūsu kalendārā", + "request_info": "Jūs esat uzaicināts uz šo pasākumu. Lūdzu, atbildiet.", + "cancel_info": "Organizators atcēla šo pasākumu.", + "event_updated": "Atjauninājums Nr. {sequence}", + "event_status_tentative": "Pagaidām", + "event_status_cancelled": "Atcelts", + "collapse": "Paslēpt detaļas", + "expand": "Rādīt detaļas" + }, + "send": "Sūtīt", + "more": "vairāk", + "details": { + "recipients_routing": "Adresāti un maršrutēšana", + "authentication_security": "Autentifikācija un drošība", + "identifiers_threading": "Identifikatori un sarakste", + "mailing_list": "Adresātu saraksts", + "message_properties": "Vēstules īpašības", + "sent": "Nosūtīts", + "received": "Saņemts", + "delivery_time": "Piegādes laiks", + "in_reply_to": "Atbildot uz", + "references": "Atsauces", + "thread_id": "Sarakstes ID", + "size": "Lielums", + "mime_type": "MIME tips", + "attachments_summary": "{count} faili · {size}", + "list_id": "Saraksta ID", + "list_help": "Saraksta palīdzība", + "list_post": "Sūtīšana sarakstam", + "list_unsubscribe": "Atteikties", + "iprev": "Apgrieztais DNS", + "spam_status": "Mēstuļu statuss", + "ai_verdict": "AI vērtējums", + "account": "Konts", + "no_subject": "(bez temata)" + }, + "scheduled_banner": "Ieplānots nosūtīšanai {date}", + "scheduled_send_created": "E-pasts ieplānots nosūtīšanai", + "sending_in": "Sūtīšana pēc {seconds} s", + "undo_send_failed": "Nevarēja atsaukt. Ziņojums, iespējams, jau ir nosūtīts.", + "cancel_scheduled_send": "Atcelt sūtīšanu", + "reschedule_send": "Pārplānot", + "cancel_and_edit": "Atcelt un rediģēt", + "cancel_and_compose_again": "Atcelt un rakstīt no jauna", + "reschedule_prompt": "Ievadiet jaunu datumu/laiku, piem. 2026-05-04T15:30", + "scheduled_actions_only": "Ieplānoto skatā ir pieejamas tikai ieplānošanas darbības", + "undo_send_scheduled": "Ziņojums ir ieplānots nosūtīšanai", + "undo_send": "Atsaukt sūtīšanu", + "contact_sidebar": { + "title": "Kontaktpersona", + "close": "Aizvērt sānjoslu", + "action_email": "E-pasts", + "action_email_title": "Sūtīt e-pastu", + "action_copy": "Kopēt", + "action_copy_title": "Kopēt e-pastu", + "action_edit_title": "Rediģēt kontaktpersonu", + "section_emails": "E-pasti", + "section_phones": "Tālruņi", + "section_organizations": "Organizācijas", + "section_addresses": "Adreses", + "section_notes": "Piezīmes", + "not_in_contacts": "Nav jūsu kontaktpersonās", + "add_to_contacts": "Pievienot kontaktpersonām", + "copied": "Nokopēts!", + "copy_failed": "Neizdevās nokopēt", + "view_contact": "Skatīt kontaktu", + "view_details": "Skatīt informāciju" + }, + "send_now": "Sūtīt tagad", + "verification_code": { + "copy": "Kopēt kodu {code}", + "copied": "Kods nokopēts", + "copy_failed": "Neizdevās nokopēt kodu" + }, + "mv_position": "{index} no {total}", + "plugin_panel_show": "Rādīt spraudņa paneli", + "plugin_panel_hide": "Slēpt spraudņa paneli" + }, + "email_composer": { + "select_from_directory": "Izvēlēties no kataloga", + "minimize": "Minimizēt", + "maximize": "Pilnekrāns", + "restore": "Atjaunot", + "new_message": "Jauns ziņojums", + "signature_edit_hint": "Veiciet dubultklikšķi, lai rediģētu parakstu", + "reply": "Atbildēt", + "reply_all": "Atbildēt visiem", + "forward": "Pārsūtīt", + "reply_to": "Atbildēt", + "reply_all_to": "Atbildēt visiem", + "forward_message": "Pārsūtīt ziņojumu", + "from": "No", + "to": "Kam", + "cc": "Kopija", + "bcc": "Slepenā kopija", + "subject": "Temats", + "body_placeholder": "Rakstiet ziņojumu...", + "send": "Sūtīt", + "cancel": "Atcelt", + "attach": "Pievienot", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Pievienot no Failiem", + "files_picker": { + "attach": "{count, plural, =0 {Pievienot} one {Pievienot # failu} other {Pievienot # failus}}", + "empty_folder": "Šī mape ir tukša", + "load_failed": "Jūsu failus neizdevās ielādēt" + }, + "discard": "Atmest", + "discard_draft_title": "Atmest melnrakstu?", + "discard_draft_confirm": "Ir nesaglabātas izmaiņas. Vai tiešām vēlaties atmest šo melnrakstu?", + "discard_draft_message": "Melnraksts tiks dzēsts.", + "saving": "Saglabā...", + "draft_saved": "Melnraksts saglabāts", + "save_failed": "Neizdevās saglabāt", + "to_placeholder": "Saņēmēji", + "cc_placeholder": "Kopijas saņēmēji", + "bcc_placeholder": "Slepenās kopijas saņēmēji", + "subject_placeholder": "Temats", + "cc_label": "Kopija:", + "bcc_label": "Slepenā kopija:", + "subject_label": "Temats:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 vēstule} other {# vēstules}}", + "no_subject": "(nav temata)", + "unknown_sender": "Nezināms", + "quote": { + "reply_header": "{date} persona {sender} rakstīja:", + "forward_header": "---------- Pārsūtītais ziņojums ----------", + "from": "No: {sender}", + "date": "Datums: {date}", + "subject": "Temats: {subject}", + "to": "Kam: {recipients}" + }, + "remove_sub_address": "Noņemt apakšadresi", + "from_override": { + "toggle_off": "Pārrakstīt", + "toggle_on": "Atcelt pārrakstīšanu", + "toggle_tooltip": "Brīvi rediģējiet sūtītāja vārdu un adresi. Pasts joprojām tiek sūtīts caur jūsu identitāti.", + "name_label": "Sūtītāja vārds", + "name_placeholder": "Vārds", + "email_label": "Sūtītāja e-pasta adrese", + "email_placeholder": "alias@example.com", + "envelope_notice": "Ja jūsu serveris nepieņem šo adresi kā aploksnes sūtītāju, tās vietā tiek izmantota {identity}, un adresāti to var redzēt galvenē Return-Path." + }, + "save_as_template": "Saglabāt kā veidni", + "attach_from_device": "Augšupielādēt no šīs ierīces", + "templates_menu": "Veidnes", + "insert_template": "Ievietot veidni", + "options": "Opcijas", + "option_plain_text": "Vienkāršs teksts", + "option_plain_text_hint": "Sūtīt bez formatējuma", + "option_read_receipt": "Pieprasīt izlasīšanas apstiprinājumu", + "option_read_receipt_hint": "Lūgt adresātu apstiprināt atvēršanu", + "option_dsn": "Piegādes paziņojums", + "option_dsn_hint": "Saņemt atskaiti, kad serveris to piegādā", + "option_require_tls": "Pieprasīt šifrētu piegādi", + "option_require_tls_hint": "Piegādāt tikai caur TLS, citādi atgriezt", + "option_priority": "Prioritāte", + "option_priority_high": "Augsta", + "option_priority_normal": "Parasta", + "option_priority_low": "Zema", + "validation": { + "recipient_required": "Norādiet vismaz vienu saņēmēju", + "body_required": "Ierakstiet ziņojumu vai pievienojiet failu", + "invalid_recipients": "Nav nosūtīts - pārbaudiet šīs adreses: {addresses}", + "attachments_uploading": "Pielikumi joprojām tiek augšupielādēti — nosūtīs, tiklīdz tas pabeigts", + "attachment_upload_failed": "Nav nosūtīts - pielikumu neizdevās augšupielādēt. Noņemiet to un mēģiniet vēlreiz." + }, + "upload_progress": "Augšupielāde {uploaded} / {total}", + "upload_cancel": "Atcelt augšupielādi", + "upload_failed": "Neizdevās augšupielādēt {filename}", + "attachment_too_large": "“{name}” ir lielāks, nekā serveris atļauj ({max} vienam failam)", + "attachments_total_too_large": "Pielikumi pārsniegtu servera ierobežojumu – {max} vienai ziņai", + "drop_files": "Velciet failus šeit, lai pievienotu", + "show_less": "Rādīt mazāk", + "send_failed": "Neizdevās nosūtīt vēstuli", + "send_timeout": "Serveris pārstāja atbildēt. Pirms sūtīt vēlreiz, pārbaudiet mapi Nosūtītie — ziņojums, iespējams, jau ir aizsūtīts.", + "send_offline": "Šķiet, esat bezsaistē. Ziņojums joprojām ir šeit, nosūtiet to vēlreiz, kad būsiet tiešsaistē.", + "formatting": "Formatējums", + "send_recipients_rejected": "Nav nosūtīts — serveris noraidīja visus adresātus.", + "send_plugin_failed": "Nav nosūtīts – spraudnis, kas veic sūtīšanu, kļūdījās vai strādāja pārāk ilgi. Pirms sūtīt vēlreiz, pārbaudiet mapi Nosūtītie.", + "send_some_recipients_rejected": "Nosūtīts, taču ne šiem adresātiem — serveris tos noraidīja.", + "continue_draft": "Turpināt melnrakstu", + "close_draft_title": "Saglabāt vai atmest melnrakstu?", + "close_draft_message": "Ir nesaglabātas izmaiņas. Vai vēlaties saglabāt kā melnrakstu vai atmest?", + "smime_sign_on": "S/MIME paraksts iespējots", + "smime_sign_off": "Iespējot S/MIME parakstu", + "smime_encrypt_on": "S/MIME šifrēšana iespējota", + "smime_encrypt_off": "Iespējot S/MIME šifrēšanu", + "smime_encrypt_unavailable": "S/MIME šifrēšana nav pieejama - trūkst saņēmēja sertifikāta", + "smime_unlock_title": "Atbloķēt S/MIME atslēgu", + "smime_unlock_message": "Ievadiet paroli, lai atbloķētu savu S/MIME parakstīšanas atslēgu.", + "smime_unlock_button": "Atbloķēt", + "smime_passphrase_placeholder": "Parole", + "forgot_attachment": { + "title": "Vai aizmirsāt pielikumu?", + "message": "Jūsu ziņojumā minēts \"{keyword}\", bet nav pievienots neviens fails. Vai tomēr sūtīt?", + "send_anyway": "Sūtīt tik un tā", + "back": "Atpakaļ pie rediģēšanas" + }, + "empty_subject": { + "title": "Sūtīt bez temata?", + "message": "Šai ziņai nav temata. Vai tomēr sūtīt?", + "dont_ask_again": "Vairs nejautāt", + "send_anyway": "Sūtīt tik un tā", + "back": "Atpakaļ pie rediģēšanas" + }, + "add_link": "Pievienot saiti", + "link_url_prompt": "Ievadiet URL", + "sending": "Sūta...", + "schedule_send": "Ieplānot sūtīšanu", + "schedule_send_description": "Izvēlieties, kad serverim jānosūta šis ziņojums.", + "schedule_preset": { + "tomorrow_morning": "Rīt no rīta", + "tomorrow_afternoon": "Rīt pēcpusdienā", + "monday_morning": "Pirmdien no rīta" + }, + "schedule_send_required": "Izvēlieties datumu un laiku.", + "schedule_send_invalid": "Ievadiet derīgu datumu un laiku.", + "schedule_send_future": "Izvēlieties datumu un laiku nākotnē.", + "schedule_send_too_late": "Šis laiks ir vēlāk, nekā serveris atļauj.", + "schedule_send_unsupported": "Šim kontam ieplānota sūtīšana netiek atbalstīta.", + "schedule_send_cleanup_warning": "Ieplānotā sūtīšana tika izveidota, bet melnraksta tīrīšana neizdevās.", + "send_delay_unsupported": "Šim kontam sūtīšanas aizture netiek atbalstīta.", + "send_delay_unsupported_confirm": "Šis konts neatbalsta sūtīšanas aizturi. Sūtīt uzreiz?", + "recipient_edit_email": "Rediģēt e-pasta adresi", + "recipient_edit_name": "Rediģēt parādāmo vārdu", + "recipient_email_placeholder": "E-pasta adrese", + "recipient_name_placeholder": "Parādāmais vārds", + "invalid_recipient": "Nederīga e-pasta adrese", + "autocomplete_search_server": "Meklēt serverī", + "autocomplete_searching": "Meklē...", + "mention_recipients": "Adresāti", + "mention_card_not_recipient": "Nav laukā Kam vai Kopija, tāpēc šo vēstuli nesaņems.", + "mention_card_add": "Pievienot laukam {field}", + "mention_card_remove": "Noņemt pieminējumu", + "toolbar": { + "bold": "Treknraksts", + "italic": "Kursīvs", + "underline": "Pasvītrots", + "strikethrough": "Pārsvītrots", + "text_color": "Teksta krāsa", + "remove_color": "Noņemt krāsu", + "heading_1": "Virsraksts 1", + "heading_2": "Virsraksts 2", + "bullet_list": "Aizzīmju saraksts", + "ordered_list": "Numurēts saraksts", + "quote": "Citāts", + "code_block": "Koda bloks", + "align_left": "Līdzināt pa kreisi", + "align_center": "Centrēt", + "align_right": "Līdzināt pa labi", + "text_direction": "Teksta virziens (RTL/LTR)", + "link": "Saite", + "table": "Tabula", + "clear_formatting": "Notīrīt formatējumu", + "undo": "Atsaukt", + "redo": "Atkārtot", + "add_row_above": "Pievienot rindu virs", + "add_row_below": "Pievienot rindu zem", + "add_column_before": "Pievienot kolonnu pirms", + "add_column_after": "Pievienot kolonnu pēc", + "delete_row": "Dzēst rindu", + "delete_column": "Dzēst kolonnu", + "toggle_header_row": "Pārslēgt galvenes rindu", + "delete_table": "Dzēst tabulu", + "pick_size": "Izvēlēties izmēru", + "font_size": "Fonta izmērs", + "font_size_default": "Noklusējums", + "background_color": "Fona krāsa", + "remove_background_color": "Noņemt fona krāsu" + }, + "send_filing_warning": "Nosūtīts - bet pēcapstrāde neizdevās, var palikt novecojis melnraksts.", + "pop_out": "Atvērt atbildi atsevišķā logā", + "send_guard_failed": "Nav nosūtīts - spraudnis, kas pārbauda ziņojumus pirms sūtīšanas, neizdevās vai neatbildēja." + }, + "confirm_dialog": { + "confirm": "Apstiprināt", + "cancel": "Atcelt" + }, + "common": { + "loading": "Ielāde...", + "error": "Kļūda", + "success": "Gatavs", + "cancel": "Atcelt", + "save": "Saglabāt", + "delete": "Dzēst", + "edit": "Rediģēt", + "close": "Aizvērt", + "search": "Meklēt", + "refresh": "Atsvaidzināt", + "settings": "Iestatījumi", + "help": "Palīdzība", + "logout": "Izrakstīties", + "yes": "Jā", + "no": "Nē", + "unknown": "Nezināms", + "app_title": "Tīmekļa pasts", + "reconnecting": "Savienojums pārtraukts. Mēģina izveidot savienojumu...", + "rate_limited_title": "Autentifikācija serverī ir īslaicīgi ierobežota.", + "rate_limited_detail": "Bulwark ir apturējis fonā veiktos pieprasījumus, lai izvairītos no bloķēšanas. Mēģinās vēlreiz pēc {seconds} s.", + "rate_limited_action_title": "Pieprasījums apturēts, lai izvairītos no bloķēšanas.", + "rate_limited_action_detail": "Bulwark gaida servera noildzes beigas pirms jaunu autentificētu pieprasījumu sūtīšanas. Mēģiniet vēlreiz pēc {seconds} s.", + "pull_to_refresh": "Velciet, lai atsvaidzinātu", + "release_to_refresh": "Atlaidiet, lai atsvaidzinātu", + "refreshing": "Atsvaidzina…" + }, + "notifications": { + "email_sent": "Vēstule veiksmīgi nosūtīta", + "email_deleted": "Vēstule izdzēsta", + "email_archived": "Vēstule arhivēta", + "emails_deleted": "{count, plural, zero {# e-pasti dzēsti} one {# e-pasts dzēsts} other {# e-pasti dzēsti}}", + "emails_archived": "{count, plural, zero {# e-pasti arhivēti} one {# e-pasts arhivēts} other {# e-pasti arhivēti}}", + "error_archiving": "Neizdevās arhivēt e-pastu", + "error_updating": "Neizdevās atjaunināt e-pastu", + "email_starred": "Vēstulei pievienota zvaigznīte", + "email_unstarred": "Zvaigznīte noņemta", + "email_marked_read": "Vēstule atzīmēta kā izlasīta", + "email_marked_unread": "Vēstule atzīmēta kā nelasīta", + "copied_to_clipboard": "Nokopēts starpliktuvē", + "source_copied": "Avota kods nokopēts starpliktuvē", + "error_sending": "Neizdevās nosūtīt vēstuli", + "error_deleting": "Neizdevās izdzēst vēstuli", + "error_loading": "Neizdevās ielādēt vēstules", + "new_email": "Jauna vēstule", + "new_email_from": "No: {sender}", + "click_to_view": "Noklikšķiniet, lai skatītu", + "email_moved": "Vēstule pārvietota", + "emails_moved": "{count} vēstules pārvietotas", + "moved_to_mailbox": "Pārvietots uz {mailbox}", + "move_failed": "Pārvietošanas kļūda", + "removed_from_folder": "Noņemts no {mailbox}", + "remove_from_folder_failed": "Neizdevās noņemt no šīs mapes", + "emails_copied": "{count} vēstules nokopētas", + "copy_failed": "Kopēšanas kļūda", + "move_error": "Neizdevās pārvietot vēstules uz izvēlēto mapi", + "email_tagged": "Vēstulei pievienots tags", + "emails_tagged": "{count} vēstulēm pievienots tags", + "tag_failed": "Tagu pievienošanas kļūda", + "identity_created": "Identitāte veiksmīgi izveidota", + "identity_updated": "Identitāte veiksmīgi atjaunināta", + "identity_deleted": "Identitāte izdzēsta", + "identity_set_primary": "Galvenā identitāte atjaunināta", + "identity_create_failed": "Neizdevās izveidot identitāti: {error}", + "identity_update_failed": "Neizdevās atjaunināt identitāti: {error}", + "identity_delete_failed": "Neizdevās izdzēst identitāti: {error}", + "identity_unauthorized": "Jums nav tiesību sūtīt no šīs e-pasta adreses", + "identity_not_found": "Identitāte netika atrasta", + "vacation_saved": "Prombūtnes iestatījumi saglabāti", + "vacation_save_failed": "Neizdevās saglabāt prombūtnes iestatījumus", + "filters_saved": "Filtri veiksmīgi saglabāti", + "filters_save_failed": "Neizdevās saglabāt filtrus", + "filters_deleted": "Filtra nosacījums izdzēsts", + "templates_exported": "Veidnes veiksmīgi eksportētas", + "templates_imported": "Importētas {count, plural, one {# veidne} other {# veidnes}}", + "templates_import_errors": "Dažas veidnes neizdevās importēt", + "templates_import_empty": "Failā netika atrastas veidnes", + "export_email_error": "Neizdevās eksportēt vēstuli", + "import_email_success": "Vēstule veiksmīgi importēta", + "import_email_error": "Neizdevās importēt vēstuli", + "rule_created": "Noteikums izveidots", + "rule_merged": "Pievienots noteikumam “{name}”", + "rule_already_covered": "To jau aptver noteikums “{name}”", + "rule_apply_existing": "{count, plural, zero {Piemērot # esošajām vēstulēm} one {Piemērot # esošajai vēstulei} other {Piemērot # esošajām vēstulēm}}", + "rule_applied": "{count, plural, =0 {Nevienu vēstuli nevajadzēja mainīt} one {Noteikums piemērots # vēstulei} other {Noteikums piemērots # vēstulēm}}", + "rule_apply_failed": "Neizdevās piemērot noteikumu esošajām vēstulēm", + "rule_edit": "Rediģēt noteikumu", + "rule_undo": "Atsaukt", + "rule_undone": "Noteikuma izmaiņa atsaukta", + "rule_undo_failed": "Neizdevās atsaukt noteikuma izmaiņu", + "rule_undo_conflict": "Jūsu filtri kopš tā laika ir mainījušies. Atsauciet izmaiņu filtru iestatījumos.", + "rule_folder_failed": "Neizdevās izveidot mapi", + "vacation_filters_save_failed": "Prombūtnes iestatījumi saglabāti, taču pārsūtīšanu un atbildes saņēmējus neizdevās saglabāt" + }, + "date": { + "today": "Šodien", + "yesterday": "Vakar", + "this_week": "Šonedēļ", + "last_week": "Pagājušajā nedēļā", + "this_month": "Šomēnes", + "older": "Senāk", + "just_now": "Tikko", + "minutes_ago": "Pirms {count} minūtes", + "minutes_ago_plural": "Pirms {count} minūtēm", + "hours_ago": "Pirms {count} stundas", + "hours_ago_plural": "Pirms {count} stundām", + "days_ago": "Pirms {count} dienas", + "days_ago_plural": "Pirms {count} dienām" + }, + "language": { + "title": "Valoda", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Izvēlieties valodu", + "switch_to_english": "Pārslēgt uz angļu valodu", + "switch_to_french": "Pārslēgt uz franču valodu", + "switch_to_japanese": "Pārslēgt uz japāņu valodu", + "switch_to_spanish": "Pārslēgt uz spāņu valodu", + "switch_to_italian": "Pārslēgt uz itāļu valodu", + "switch_to_german": "Pārslēgt uz vācu valodu", + "switch_to_dutch": "Pārslēgt uz holandiešu valodu", + "switch_to_portuguese": "Pārslēgt uz portugāļu valodu", + "switch_to_russian": "Pārslēgt uz krievu valodu", + "switching": "Maina valodu...", + "switch": "Mainīt valodu", + "current": "Pašreizējā valoda", + "polish": "Polski", + "switch_to_polish": "Pārslēgt uz poļu valodu", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Pārslēgt uz persiešu valodu", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Pārslēgt uz mongoļu valodu" + }, + "settings": { + "title": "Iestatījumi", + "back_to_mail": "Atgriezties pie pasta", + "save_success": "Iestatījumi veiksmīgi saglabāti", + "import_success": "Iestatījumi veiksmīgi importēti", + "import_error": "Neizdevās importēt iestatījumus", + "reset_confirm": "Vai tiešām vēlaties atiestatīt visus iestatījumus uz noklusējuma vērtībām?", + "unsaved_changes": "Jums ir nesaglabātas izmaiņas", + "discard_changes": "Atmest nesaglabātās izmaiņas?", + "discard": "Atmest", + "keep_editing": "Turpināt rediģēšanu", + "tabs": { + "appearance": "Izskats", + "language": "Valoda, reģions un laiks", + "email": "Pasta darbība", + "composer": "Redaktors", + "privacy": "Privātums un drošība", + "account": "Konts", + "identities": "Identitātes", + "vacation": "Prombūtne", + "advanced": "Papildu", + "calendar": "Kalendārs", + "filters": "Filtri", + "templates": "Veidnes", + "folders": "Mapes", + "keywords": "Tagi", + "security": "Drošība", + "files": "Faili", + "contacts": "Kontakti", + "encryption": "Šifrēšana", + "protocol_handlers": "Noklusējuma lietotnes", + "sidebar_apps": "Sānu joslas lietotnes", + "notifications": "Paziņojumi", + "layout": "Izkārtojums", + "themes": "Tēmas", + "reading": "Lasīšana", + "composing": "Rakstīšana", + "downloads": "Lejupielādes", + "content_senders": "Saturs un sūtītāji", + "about_data": "Par un dati", + "debug": "Atkļūdošana" + }, + "themes": { + "title": "Tēmas", + "description": "Izvēlieties kādu no administratora izvietotajām tēmām vai iebūvētajiem priekšiestatījumiem.", + "locked": "Tēmas izvēli ir bloķējis administrators.", + "forced": "Tēmu “{name}” ir noteicis administrators, un to nevar mainīt", + "admin_theme": "Administratora tēma", + "activated": "Tēma aktivizēta", + "default_restored": "Noklusējuma tēma atjaunota", + "default_name": "Noklusējuma", + "default_badge": "Noklusējuma", + "admin_enforced": "Noteicis administrators" + }, + "tab_groups": { + "general": "Vispārīgi", + "account": "Konts un identitāte", + "organization": "Pasta organizēšana", + "apps": "Lietotnes", + "system": "Sistēma", + "appearance": "Izskats", + "mail": "Pasts", + "privacy": "Privātums un drošība", + "advanced": "Papildu" + }, + "appearance": { + "message_list_order": { + "label": "Ziņu saraksta secība", + "description": "Kā ziņas tiek sakārtotas sarakstā. Kārtošanu veic serveris, tāpēc tā attiecas uz visu mapi, ne tikai uz ielādētajām ziņām.", + "preset": { + "chronological": "Hronoloģiski (jaunākās vispirms)", + "unread_first": "Nelasītās vispirms", + "starred_first": "Ar zvaigznīti vispirms", + "tagged_first": "Ar birku vispirms", + "custom": "Pielāgota" + }, + "tag_label": "Birka", + "advanced_show": "Papildu: rediģēt kārtošanas līmeņus", + "advanced_hide": "Paslēpt papildu", + "criterion_label": "{n}. līmeņa kritērijs", + "direction_label": "{n}. līmeņa virziens", + "criteria": { + "unread": "Nelasīta", + "starred": "Ar zvaigznīti", + "tag": "Birka", + "receivedAt": "Saņemšanas datums", + "sentAt": "Nosūtīšanas datums", + "from": "Sūtītājs", + "subject": "Tēma", + "size": "Izmērs" + }, + "direction": { + "unread_first": "Nelasītās vispirms", + "read_first": "Lasītās vispirms", + "starred_first": "Ar zvaigznīti vispirms", + "unstarred_first": "Bez zvaigznītes vispirms", + "tagged_first": "Ar birku vispirms", + "untagged_first": "Bez birkas vispirms", + "newest_first": "Jaunākās vispirms", + "oldest_first": "Vecākās vispirms", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Lielākās vispirms", + "smallest_first": "Mazākās vispirms" + }, + "add_level": "Pievienot līmeni", + "remove_level": "Noņemt līmeni", + "move_up": "Pārvietot augstāk", + "move_down": "Pārvietot zemāk", + "tiebreak_note": "Līdz trim līmeņiem; nākamie līmeņi izšķir neizšķirtu. „Jaunākās vispirms” vienmēr tiek piemērots pēdējais.", + "scope": { + "label": "Piemērot visām mapēm", + "description": "Ja izslēgts, secība attiecas tikai uz Iesūtni; pārējās mapes paliek hronoloģiskas." + }, + "unsupported_note": "Jūsu pasta serveris neatbalsta kārtošanu pēc lasīšanas stāvokļa, zvaigznītēm vai birkām. Šie līmeņi tiek ignorēti, un saraksts paliek hronoloģisks." + }, + "title": "Izskats", + "description": "Pielāgojiet tīmekļa pasta izskatu", + "theme": { + "label": "Tēma", + "description": "Izvēlieties vēlamo krāsu shēmu", + "light": "Gaišā", + "dark": "Tumšā", + "system": "Sistēmas" + }, + "language": { + "label": "Valoda", + "description": "Izvēlieties vēlamo valodu" + }, + "font_size": { + "label": "Fonta lielums", + "description": "Pielāgojiet teksta izmēru labākai lasāmībai", + "small": "Mazs", + "medium": "Vidējs", + "large": "Liels" + }, + "list_density": { + "label": "Blīvums", + "description": "Pielāgojiet saskarnes elementu attālumus", + "extra_compact": "Īpaši kompakts", + "compact": "Kompakts", + "regular": "Parasts", + "comfortable": "Plašs" + }, + "interface_layout": { + "label": "Saskarnes izkārtojums", + "description": "Kur atrodas meklēšana, navigācija un rīkjoslas", + "default": "Noklusējuma", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Iespējot animācijas", + "description": "Rādīt plūstošas pārejas un efektus" + }, + "toolbar_position": { + "label": "Rīkjoslas novietojums", + "description": "Kur rādīt vēstules darbību pogas (Atbildēt, Arhivēt utt.)", + "top": "Augšā", + "below_subject": "Zem temata" + }, + "toolbar_labels": { + "label": "Rādīt rīkjoslas aprakstus", + "description": "Rādīt teksta aprakstus blakus ikonām. Izslēdziet, lai ietaupītu vietu." + }, + "hide_account_switcher": { + "label": "Slēpt kontu pārslēdzēju sānu joslā", + "description": "Slēpt kontu izvēli sānu joslas augšpusē. Kontus joprojām varēsiet pārslēgt navigācijas joslā apakšā." + }, + "show_rail_account_list": { + "label": "Rādīt kontu avatarus navigācijas joslā", + "description": "Rādīt kontu apļus navigācijas joslas apakšā ātrai pārslēgšanai." + }, + "unified_mailbox": { + "label": "Apvienotā pastkaste", + "description": "Rādīt apvienotās mapes (Iesūtne, Nosūtītie u.c.) no visiem pievienotajiem kontiem", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Iekļaut grupas pastkastes", + "description": "Iekļaut apvienotajā skatā arī koplietotās/grupas pastkastes." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Krāsainas sānjoslas ikonas", + "description": "Iekrāsojiet mapju un birku ikonas pēc to veida (zila Iesūtne, sarkana Mēstules, zaļa Nosūtītie utt.). Atspējojiet, lai iegūtu vienkrāsainu sānjoslu." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Rādīt kopējo ziņojumu skaitu", + "description": "Rāda kopējo ziņojumu skaitu blakus mapēm un tagiem, kā arī nelasīto ziņojumu skaitu. Atspējojiet, lai rādītu tikai nelasītos." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Rādīt koplietotos pasta kontus", + "description": "Rādīt ar jums koplietoto pastkastīšu mapes sānjoslā sadaļā Koplietots. Izslēdziet, lai paslēptu visas, vai paslēpiet atsevišķus kontus zemāk. To kalendāri un kontakti paliek redzami." + }, + "pro_interface": { + "label": "Pro saskarne (eksperimentāla)", + "description": "Tikai darbvirsmas pieredzējušu lietotāju izkārtojums ar ziņojumu pārlūkošanu vairākās cilnēs un kontu pārvaldību. Standarta saskarne netiek ietekmēta; varat jebkurā brīdī pārslēgties atpakaļ.", + "open_label": "Atvērt Pro saskarni", + "back_to_standard": "Atpakaļ uz standartu" + }, + "cross_unread": { + "label": "Visi konti: Nelasītie", + "description": "Rāda ierakstu sadaļā “Visi konti”, kas uzskaita nelasītās vēstules no visiem kontiem, aptverot visas mapes, izņemot mēstules, nosūtītās, arhīvu, miskasti un melnrakstus." + }, + "cross_starred": { + "label": "Visi konti: Ar zvaigzni", + "description": "Rāda ierakstu sadaļā “Visi konti”, kas uzskaita ar zvaigzni atzīmētās vēstules no visiem kontiem, aptverot visas mapes, izņemot mēstules, nosūtītās, arhīvu, miskasti un melnrakstus." + }, + "cross_all": { + "label": "Visi konti: Visas vēstules", + "description": "Rāda ierakstu sadaļā “Visi konti”, kas uzskaita visas vēstules no visiem kontiem, aptverot visas mapes, izņemot mēstules, nosūtītās, arhīvu, miskasti un melnrakstus." + } + }, + "keywords": { + "title": "E-pasta tagi", + "description": "Definējiet tagus, lai organizētu e-pastus ar krāsām. Tie tiek saglabāti kā JMAP atslēgvārdi serverī.", + "add_keyword": "Pievienot tagu", + "label_field": "Redzamais nosaukums", + "label_placeholder": "piem., Darbs, Personīgi, Steidzami", + "id_field": "Taga identifikators", + "id_placeholder": "piem., darbs, personigi", + "color_field": "Krāsa", + "id_exists": "Šāds taga identifikators jau pastāv", + "edit": "Rediģēt tagu", + "delete": "Dzēst tagu", + "save": "Saglabāt", + "add": "Pievienot", + "cancel": "Atcelt", + "migrating": "Taga atjaunināšana esošajos e-pastos…", + "migration_error": "Neizdevās atjaunināt tagu esošajos e-pastos", + "filters_migration_error": "Dažas filtru kārtulas joprojām izmanto veco tagu — atjauniniet tās manuāli", + "nesting": { + "label": "Ligzdoti tagi", + "description": "Ligzdojiet tagus zem citiem tagiem un rādiet tos sānjoslā kā koku." + }, + "parent_field": "Vecāktags", + "no_parent": "Nav vecāktaga", + "too_long": "Šis taga ceļš ir pārāk garš (ne vairāk kā {max} rakstzīmes)", + "has_children_locked": "Zem šī taga ir ligzdoti citi tagi, tāpēc tā nosaukums un vecāktags ir bloķēti. Vispirms pārvietojiet vai noņemiet tos.", + "has_children_delete": "Vispirms noņemiet zem šī ligzdotos tagus", + "visibility_field": "Redzamība sānjoslā", + "visibility": { + "show": "Rādīt", + "unread": "Rādīt, ja ir nelasīti", + "hide": "Slēpt" + }, + "unrecognized": { + "title": "Atgūt tagus no servera", + "description": "Tagu nosaukumi un krāsas tiek glabāti šajā pārlūkā, bet paši tagi atrodas jūsu vēstulēs kā JMAP atslēgvārdi. Pārmeklējiet pastu, lai atrastu atslēgvārdus, kuriem šeit nav taga, un pievienojiet tos atpakaļ.", + "scan": "Meklēt tagus", + "rescan": "Meklēt vēlreiz", + "scanning": "Notiek meklēšana… {scanned} no {total} vēstulēm", + "none": "Visi jūsu vēstulēs esošie tagi jau ir definēti šeit.", + "messages": "{count, plural, one {# vēstule} other {# vēstules}}", + "add_all": "Pievienot visus ({count})", + "partial": "Tika pārmeklētas tikai {scanned} jaunākās no {total} vēstulēm. Tagi, kas izmantoti tikai vecākās vēstulēs, var nebūt atrasti.", + "error": "Neizdevās pārmeklēt pastu, lai atrastu tagus." + } + }, + "notifications": { + "test_sound": "Pārbaudīt paziņojuma skaņu", + "sounds": { + "default": "Noklusējuma (Signāls)", + "cheerful": "Priecīgs", + "involved": "Iesaistošs", + "swift": "Ātrs", + "relax": "Relaksējošs" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Paziņojuma skaņa", + "description": "Izvēlieties skaņu paziņojumiem", + "choose": "Skaņa", + "choose_desc": "Izvēlieties paziņojuma skaņu un nospiediet skaļruņa ikonu, lai to noklausītos" + }, + "email": { + "title": "E-pasta paziņojumi", + "description": "Iestatiet paziņojumus par ienākošajām vēstulēm", + "enabled": "E-pasta paziņojumi", + "enabled_desc": "Rādīt paziņojumus, saņemot jaunas vēstules", + "sound": "Paziņojuma skaņa", + "sound_desc": "Atskaņot skaņas signālu, saņemot jaunas vēstules", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Kalendāra paziņojumi", + "description": "Iestatiet paziņojumus par kalendāra pasākumiem", + "enabled": "Pasākumu paziņojumi", + "enabled_desc": "Rādīt brīdinājumus par gaidāmajiem kalendāra pasākumiem", + "sound": "Paziņojuma skaņa", + "sound_desc": "Atskaņot skaņas signālu kalendāra atgādinājumiem", + "invitation_parsing": "Atpazīt uzaicinājumus e-pastā", + "invitation_parsing_desc": "Noteikt kalendāra uzaicinājumus pielikumos un rādīt kalendāra darbības" + }, + "push": { + "confirm_disable_message": "Šī ierīce vairs nesaņems brīdinājumus, kad vietne būs aizvērta.", + "confirm_disable_title": "Atspējot fona paziņojumus?", + "description": "Saņem sistēmas paziņojumus par jaunu pastu, kad šī vietne ir aizvērta. Piegādāts caur Bulwark push releju; relejs nekad neredz pasta saturu.", + "disable": "Atspējot", + "enable": "Iespējot", + "dismiss_aria": "Aizvērt paziņojumu uzaicinājumu", + "ios_hint": "Operētājsistēmā iOS vispirms instalējiet vietni sākuma ekrānā – Safari piegādā Web Push tikai instalētajām PWA.", + "reenable": "Reģistrēt vēlreiz", + "relay_desc": "Pēc noklusējuma izmanto izmitināto Bulwark releju. Mainiet tikai tad, ja izmitināt pats.", + "relay_label": "Push relejs", + "relay_locked": "Iestatījis administrators", + "relay_locked_desc": "Push releja URL ir iestatījis administrators, un to nevar mainīt.", + "status_active": "Aktīvs šajā ierīcē", + "status_busy": "Notiek darbs…", + "status_inactive": "Nav iespējots šajā ierīcē", + "status_unsupported": "Šī pārlūkprogramma neatbalsta Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Fona paziņojumi", + "devices_title": "Reģistrētās ierīces", + "devices_desc": "Visas ierīces, kas reģistrētas šī konta fona paziņojumu saņemšanai. Atsaucot ierīci, tā nekavējoties pārstāj tās saņemt.", + "devices_loading": "Notiek reģistrēto ierīču ielāde…", + "devices_empty": "Fona paziņojumiem nav reģistrēta neviena ierīce.", + "devices_error": "Neizdevās ielādēt reģistrētās ierīces.", + "devices_refresh": "Atsvaidzināt", + "device_this": "Šī ierīce", + "device_other": "Cita ierīce", + "device_status_active": "Piegādā paziņojumus", + "device_status_inactive": "Nekad nav piegādājusi paziņojumu", + "device_status_unknown": "Šim retranslatoram nezināma", + "device_expires": "Beidzas {date}", + "revoke": "Atsaukt", + "confirm_revoke_title": "Atsaukt šo ierīci?", + "confirm_revoke_message": "Tā nekavējoties pārstās saņemt fona paziņojumus. Atkārtoti ieslēdzot push šajā ierīcē, tā tiks reģistrēta no jauna.", + "confirm_revoke_message_this": "Šī ierīce nekavējoties pārstās saņemt fona paziņojumus." + } + }, + "language_region": { + "title": "Valoda, reģions un laiks", + "description": "Valoda, datuma formāts, laika formāts un citas reģionālās preferences", + "language": { + "label": "Valoda", + "description": "Izvēlieties vēlamo valodu", + "english": "Angļu", + "french": "Franču" + }, + "date_format": { + "label": "Datuma formāts", + "description": "Kā datumi tiek attēloti e-pasta sarakstā", + "smart": "Gudrs", + "relative": "Relatīvs", + "full": "Vienmēr pilns datums", + "preview_today": "Šodien:", + "preview_this_week": "Šajā nedēļā:", + "preview_older": "Vecāks:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Laika formāts", + "description": "Izvēlieties starp 12 vai 24 stundu formātu", + "12h": "12 stundu", + "24h": "24 stundu" + }, + "first_day": { + "label": "Nedēļas pirmā diena", + "description": "Sākt nedēļu ar svētdienu vai pirmdienu", + "sunday": "Svētdiena", + "monday": "Pirmdiena" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Notīrīt meklēšanu, mainot mapi", + "description": "Noklikšķinot uz mapes (Iesūtne, Nosūtītie…), aktīvā meklēšana tiek notīrīta un tiek atvērta šī mape. Kad izslēgts, meklēšana paliek un tiek atkārtota izvēlētajā mapē." + }, + "request_read_receipt": { + "label": "Pēc noklusējuma pieprasīt lasīšanas apstiprinājumus", + "description": "Sastādot jaunu ziņojumu, iepriekš ieslēgt lasīšanas apstiprinājuma pieprasījumu." + }, + "read_receipt_response": { + "label": "Atbildēt uz lasīšanas apstiprinājuma pieprasījumiem", + "description": "Ko darīt, kad ienākošs ziņojums pieprasa lasīšanas apstiprinājumu.", + "ask": "Vaicāt katru reizi", + "always": "Vienmēr sūtīt", + "never": "Nekad nesūtīt" + }, + "title": "Pasta darbība", + "description": "Pielāgojiet vēstuļu apstrādi", + "mark_read": { + "label": "Atzīmēt kā izlasītu", + "description": "Kad atzīmēt vēstuli kā izlasītu, to atverot", + "instant": "Nekavējoties", + "delay_3s": "Pēc 3 sekundēm", + "delay_5s": "Pēc 5 sekundēm", + "never": "Nekad" + }, + "delete_action": { + "label": "Darbība dzēšot", + "description": "Kas notiek, izdzēšot vēstuli", + "trash": "Pārvietot uz atkritni", + "trash_and_read": "Pārvietot uz atkritni un atzīmēt kā lasītu", + "permanent": "Dzēst neatgriezeniski", + "warning": "Vēstules tiks dzēstas neatgriezeniski. Šī darbība nav atceļama." + }, + "message_spacing": { + "label": "Ziņojuma atstarpes", + "description": "Atstarpe ap ziņojuma tekstu lasītājā", + "auto": "Automātiski (ieteicams)", + "always": "Vienmēr pievienot atstarpi", + "edge": "No malas līdz malai" + }, + "plain_text_font": { + "label": "Vienkārša teksta fonts", + "description": "Fonts vienkārša teksta e-pastiem", + "mono": "Vienplatuma", + "sans": "Parasts (tāpat kā lietotnē)" + }, + "archive_mode": { + "label": "Arhivēt mapē", + "description": "Kā organizēt vēstules arhivējot", + "single": "Viena mape", + "year": "Mape katram gadam", + "month": "Mape katram mēnesim", + "reorganize": "Reorganizēt esošo arhīvu", + "reorganize_success": "{count, plural, =0 {Nav vēstuļu reorganizācijai} =1 {1 vēstule reorganizēta} other {# vēstules reorganizētas}}", + "reorganize_error": "Neizdevās reorganizēt arhīvu" + }, + "permanently_delete_junk": { + "label": "Mēstules dzēst neatgriezeniski", + "description": "Dzēst vēstules no mapes \"Mēstules\" neatgriezeniski, nepārvietojot uz atkritni" + }, + "confirm_bulk_delete": { + "label": "Apstiprināt pirms vairāku ziņojumu dzēšanas", + "description": "Jautāt pirms vairāku atlasītu ziņojumu pārvietošanas uz miskasti. Neatgriezeniska dzēšana vienmēr jautā." + }, + "mail_layout": { + "label": "Pasta izkārtojums", + "description": "Izvēlieties starp klasisko dalīto skatu vai fokusa sarakstu.", + "split": "Dalīts skats", + "split_description": "Ziņojumu saraksts un lasīšanas rūts ir redzamas blakus.", + "focus": "Fokusa saraksts", + "focus_description": "Rādīt vienu rindu katram ziņojumam un atvērt vēstuli pilnā platumā.", + "horizontal": "Lasīšanas rūts apakšā", + "horizontal_description": "Rādīt ziņojumu sarakstu augšā un atvērt atlasīto ziņojumu lasīšanas rūtī zem tā." + }, + "list_columns": { + "label": "Rādīt sarakstu kolonnās", + "description": "Rāda vienas rindas ierakstus kā tabulu ar kolonnām No, Temats, Datums un Izmērs zem galvenes." + }, + "disable_threading": { + "label": "Izslēgt sarunu grupēšanu", + "description": "Rādīt vēstules kā atsevišķus ziņojumus, nevis sērijveidā" + }, + "plain_text_mode": { + "label": "Tikai parasts teksts", + "description": "Izslēgt formatētā teksta redaktoru un sūtīt vēstules tikai kā parastu tekstu" + }, + "auto_select_reply_identity": { + "label": "Automātiski izvēlēties atbildes adresi", + "description": "Atbildot automātiski izmantot to kontu, uz kuru vēstule tika saņemta" + }, + "reply_identity_match": { + "label": "Saņemšanas adreses atbilstība", + "description": "Kuras saņemšanas adreses tiek uzskatītas par jūsu. „Tikai precīza adrese“ izvēlas vienīgi kādu no jūsu konfigurētajām identitātēm; „tas pats domēns“ arī jebkuru citu adresi jūsu identitāšu domēnos uzskata par catch-all aizstājvārdu un pārraksta ar to galveni „No“. Izvēlieties precīzu adresi, ja šīs adreses ir izplatīšanas saraksti.", + "exact": "Tikai precīza adrese", + "domain": "Jebkura adrese manos domēnos" + }, + "signature_position": { + "label": "Paraksta novietojums", + "description": "Kur ievietot jūsu parakstu atbildēs un pārsūtīšanā. Virs citētā teksta tas dabiski lasās kā atbildes noslēgums; zem tā saglabā oriģinālo ziņojumu vienkopus.", + "above_quote": "Pirms citētā teksta", + "below_quote": "Pēc citētā teksta" + }, + "signature_separator": { + "label": "Paraksta atdalītājs", + "description": "Pirms paraksta pievienot standarta atdalītāju \"-- \" (RFC 3676). Izslēdziet, ja vēlaties pāriet no ziņojuma tieši uz parakstu." + }, + "sub_address_delimiter": { + "label": "Apakšadreses atdalītājs", + "description": "Zīme, kas atdala lietotājvārdu no apakšadreses tagu. Izvēlieties atdalītāju, ko lieto jūsu pasta serveris (piem. lietotajs{delimiter}tags@domens.lv).", + "option": "{delimiter} (lietotajs{delimiter}tags@domens.lv)", + "custom": "Pielāgots…", + "custom_input_label": "Pielāgota atdalītāja zīme" + }, + "show_preview": { + "label": "Rādīt priekšskatījuma tekstu", + "description": "Rādīt vēstules fragmentu sarakstā", + "focus_description": "Rādīt priekšskatījuma tekstu sarakstā" + }, + "show_verification_codes": { + "label": "Rādīt verifikācijas kodus", + "description": "Piedāvāt kodu no pieteikšanās un apstiprinājuma e-pastiem kopēšanai ar vienu klikšķi, sarakstā vienu dienu un ziņojumā" + }, + "attachment_click_action": { + "label": "Darbība, noklikšķinot uz pielikuma", + "description": "Izvēlieties priekšskatījumu vai tūlītēju lejupielādi", + "preview": "Priekšskatīt, ja iespējams", + "download": "Lejupielādēt nekavējoties" + }, + "attachment_position": { + "label": "Pielikumu novietojums", + "description": "Kur rādīt pielikumus vēstules galvenē", + "beside-sender": "Blakus sūtītājam", + "below-header": "Zem galvenes" + }, + "emails_per_page": { + "10": "10 vēstules", + "25": "25 vēstules", + "50": "50 vēstules", + "100": "100 vēstules", + "label": "Vēstules lapā", + "description": "Vienlaicīgi ielādējamo vēstuļu skaits" + }, + "always_light_mode": { + "label": "Vienmēr rādīt vēstules gaišajā režīmā", + "description": "Rādīt vēstules saturu gaišajā režīmā, pat ja lietotne ir tumšajā režīmā" + }, + "external_content": { + "label": "Ārējais saturs", + "description": "Kā apstrādāt attēlus un ārējo saturu", + "ask": "Vienmēr jautāt", + "block": "Vienmēr bloķēt", + "allow": "Vienmēr atļaut" + }, + "trusted_senders": { + "label": "Uzticamie sūtītāji", + "description": "Pārvaldiet sūtītājus, kuru attēli tiek ielādēti automātiski", + "count_zero": "Nav", + "count_one": "1 sūtītājs", + "count_other": "{count} sūtītāji", + "modal_title": "Uzticamie sūtītāji", + "empty_title": "Nav uzticamo sūtītāju", + "empty_description": "Skatot vēstuli ar bloķētiem attēliem, nospiediet \"Vienmēr uzticēties\", lai pievienotu sūtītāju šeit.", + "add_manually": "Pievienot sūtītāju manuāli", + "add_button": "Pievienot", + "add_placeholder": "Ievadiet e-pasta adresi", + "search_placeholder": "Meklēt sūtītājus...", + "no_results": "Sūtītāji nav atrasti", + "remove": "Noņemt", + "close": "Aizvērt", + "invalid_email": "Ievadiet derīgu e-pasta adresi", + "already_added": "Šis sūtītājs jau ir sarakstā", + "save_error": "Neizdevās saglabāt - sīkāk skatiet Kontaktu atkļūdošanas žurnālā", + "use_address_book_label": "Sinhronizēt ar adrešu grāmatu", + "use_address_book_description": "Saglabāt uzticamos sūtītājus atsevišķā adrešu grāmatā \"Uzticamie sūtītāji\", lai tie sinhronizētos visās jūsu ierīcēs" + }, + "hover_actions": { + "label": "Ātrās darbības, uzvirzot kursoru", + "description": "Izvēlieties darbības, kas redzamas, uzvirzot kursoru uz vēstules sarakstā", + "delete": "Dzēst", + "star": "Zvaigznīte", + "mark_read": "Izlasīts/Nelasīts", + "archive": "Arhivēt", + "tag": "Tags", + "spam": "Mēstule", + "not_spam": "Nav mēstule", + "none_selected": "Nav atlasītu darbību", + "mode_label": "Attēlošanas režīms", + "mode_inline": "Iebūvēts", + "mode_floating": "Peldošs", + "corner_label": "Peldošo darbību pozīcija", + "corner_top-left": "Augšā pa kreisi", + "corner_top-right": "Augšā pa labi", + "corner_bottom-left": "Apakšā pa kreisi", + "corner_bottom-right": "Apakšā pa labi" + }, + "default_mail_program": { + "label": "Noklusējuma pasta programma", + "description": "Reģistrēt {appName} kā noklusējuma programmu mailto: saitēm", + "button": "Iestatīt kā noklusējumu", + "success": "Pārlūkam nosūtīts pieprasījums iestatīt kā noklusējumu", + "error": "Jūsu pārlūks neatbalsta šo funkciju" + }, + "attachment_reminder": { + "label": "Pielikumu atgādinājums", + "description": "Brīdināt pirms sūtīšanas, ja ziņojumā minēti pielikumi, bet neviens nav pievienots", + "keywords_label": "Aktivizēšanas atslēgvārdi", + "keywords_description": "Vārdi vai frāzes, kas aktivizē atgādinājumu, ja tie tiek atrasti jūsu ziņojumā", + "add_placeholder": "Pievienot atslēgvārdu...", + "add": "Add", + "remove": "Noņemt" + }, + "empty_subject_warning": { + "label": "Brīdinājums par tukšu tematu", + "description": "Pieprasīt apstiprinājumu pirms ziņas sūtīšanas bez temata." + }, + "recipient_mentions": { + "label": "Pieminēt saņēmējus ar @", + "description": "Ierakstot @ ziņojumā, tiek piedāvāti tā saņēmēji un ievietots izvēlētā vārds. Enter vai Tab izvēlas iezīmēto vārdu." + }, + "mention_adds_recipient": { + "label": "Kontakti @ pieminējumos", + "description": "Ierakstot @, tiek piedāvāti arī tavi kontakti un cilvēki no direktorija. Ja izvēlies kādu, kas vēl nav adresāts, viņš tiek pievienots vēstulei.", + "to": "Pievienot laukam Kam", + "cc": "Pievienot laukam Kopija", + "off": "Piedāvāt tikai adresātus" + }, + "hide_inline_image_attachments": { + "label": "Slēpt iegultos attēlus no pielikumu saraksta", + "description": "Ziņojuma pamattekstā iegultie attēli netiek rādīti kā atsevišķi pielikumi" + }, + "attachment_image_previews": { + "label": "Rādīt attēlu priekšskatījumus pielikumos", + "description": "Rādīt attēlu pielikumus kā sīktēlu kartītes, nevis vispārīgas failu ikonas" + }, + "send_delay": { + "label": "Atsaukt sūtīšanu / sūtīšanas aizture", + "description": "Aizturiet parastu sūtīšanu īsā servera puses logā.", + "off": "Izslēgts", + "seconds": "{seconds} sekundes", + "unsupported": "Pašreizējais konts neziņo par aizturētas sūtīšanas atbalstu. Iestatījums joprojām tiks saglabāts citiem kontiem." + }, + "return_to_list_after_action": { + "label": "Atgriezties sarakstā pēc dzēšanas vai atzīmēšanas kā nelasītu", + "description": "Pēc atvērtā ziņojuma dzēšanas vai atzīmēšanas kā nelasīta atgriezieties ziņojumu sarakstā, nevis atveriet nākamo ziņojumu." + }, + "swipe_right_action": { + "label": "Darbība, velkot pa labi (mobilajā ierīcē)", + "description": "Skārienierīcēs darbība, kas tiek veikta, kad sarakstā ziņu pavelk pa labi." + }, + "swipe_left_action": { + "label": "Darbība, velkot pa kreisi (mobilajā ierīcē)", + "description": "Skārienierīcēs darbība, kas tiek veikta, kad sarakstā ziņu pavelk pa kreisi." + }, + "swipe_actions": { + "none": "Nav", + "archive": "Arhivēt", + "delete": "Dzēst", + "mark_read": "Pārslēgt lasīts/nelasīts", + "star": "Pārslēgt zvaigznīti", + "spam": "Atzīmēt kā mēstuli" + }, + "rtl_editing": { + "label": "Rakstīšanas no labās uz kreiso atbalsts", + "description": "Pievieno redaktora rīkjoslai virziena pogu, ar kuru rindkopas var iestatīt no kreisās uz labo vai no labās uz kreiso" + } + }, + "composer": { + "title": "Redaktors", + "description": "Pielāgojiet vēstuļu rakstīšanas parametrus", + "autosave": { + "label": "Automātiskās saglabāšanas intervāls", + "description": "Cik bieži automātiski saglabāt melnrakstus", + "30s": "Ik pēc 30 sekundēm", + "1m": "Katru minūti", + "2m": "Ik pēc 2 minūtēm", + "5m": "Ik pēc 5 minūtēm" + }, + "send_confirmation": { + "label": "Sūtīšanas apstiprinājums", + "description": "Jautāt apstiprinājumu pirms vēstules sūtīšanas" + }, + "default_reply": { + "label": "Noklusējuma atbildes režīms", + "description": "Darbība, nospiežot \"Atbildēt\"", + "reply": "Atbildēt", + "reply_all": "Atbildēt visiem" + } + }, + "privacy": { + "title": "Privātums un drošība", + "description": "Pārvaldiet privātuma un drošības iestatījumus", + "external_images": { + "label": "Bloķēt ārējos attēlus", + "description": "Novērst izsekošanu caur ārējiem attēliem" + }, + "session_timeout": { + "label": "Sesijas noildze", + "description": "Automātiski izrakstīties pēc dīkstāves", + "never": "Nekad", + "30m": "30 minūtes", + "1h": "1 stunda", + "4h": "4 stundas" + }, + "clear_cache": { + "label": "Notīrīt kešatmiņu", + "description": "Dzēst kešotos datus un pagaidu failus", + "button": "Notīrīt kešatmiņu", + "confirm": "Vai tiešām vēlaties notīrīt kešatmiņu?", + "success": "Kešatmiņa veiksmīgi notīrīta" + } + }, + "account": { + "title": "Konts", + "description": "Skatīt sava konta informāciju", + "name_label": "Redzamais vārds", + "username_label": "Lietotājvārds", + "account_type_label": "Konta veids", + "auth_method_label": "Autentifikācija", + "auth_method_oauth": "Vienotā pierakstīšanās (OAuth/OIDC)", + "auth_method_basic": "Parole", + "auth_method_token": "Piekļuves marķieris", + "demo_account": "Demo konts", + "email": { + "label": "E-pasta adrese", + "value": "{email}" + }, + "server": { + "label": "JMAP serveris", + "value": "{server}" + }, + "storage": { + "label": "Krātuves izmantošana", + "used": "Izmantots {used} no {total}", + "percentage": "Izmantots {percent}%" + }, + "last_sync": { + "label": "Pēdējā sinhronizācija", + "value": "{time}" + }, + "accounts": { + "title": "Pieteiktie konti", + "description": "Velciet, lai mainītu kontu secību kontu izvēlnē", + "active": "Pašlaik aktīvais konts", + "default_badge": "Noklusējuma konts", + "set_default": "Iestatīt kā noklusējumu", + "switch_to": "Pārslēgties uz šo kontu", + "move_up": "Pārvietot uz augšu", + "move_down": "Pārvietot uz leju", + "drag_handle": "Velciet, lai pārkārtotu", + "add": "Pievienot kontu" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Kopīgots ar mani", + "description": "Grupas un kopīgotie konti, kurus varat pārvaldīt. Atlasiet vienu, lai rediģētu tā filtrus, prombūtnes atbildētāju, kalendārus un kontaktus.", + "shared_label": "Kopīgots konts" + } + }, + "security": { + "title": "Konta drošība", + "description": "Pārvaldiet paroli, 2FA un drošības iestatījumus", + "detecting": "Nosaka servera iespējas...", + "not_available": "Konta drošības pārvaldība šim serverim nav pieejama.", + "password": { + "title": "Mainīt paroli", + "current": "Pašreizējā parole", + "new": "Jaunā parole", + "confirm": "Apstipriniet jauno paroli", + "submit": "Mainīt paroli", + "success": "Parole veiksmīgi mainīta", + "error_title": "Neizdevās mainīt paroli", + "error_mismatch": "Jaunās paroles nesakrīt", + "error_min_length": "Parolei jābūt vismaz 8 rakstzīmes garai", + "error_generic": "Neizdevās mainīt paroli" + }, + "display_name": { + "label": "Redzamais vārds", + "description": "Jūsu vārds, kāds tas redzams serverī", + "save": "Saglabāt", + "success": "Redzamais vārds atjaunināts", + "error": "Neizdevās atjaunināt redzamo vārdu", + "placeholder": "Ievadiet savu parādāmo vārdu" + }, + "totp": { + "section_title": "Divfaktoru autentifikācija", + "label": "TOTP autentifikācija", + "description": "Pievienojiet papildu drošības slāni ar vienreizēju paroli", + "active": "Iespējota", + "inactive": "Izslēgta", + "enabled": "2FA ir iespējota", + "disabled": "2FA ir izslēgta", + "enable_error": "Neizdevās iespējot 2FA", + "disable_error": "Neizdevās izslēgt 2FA", + "setup_instructions": "Nokopējiet šo URL savā autentifikācijas lietotnē (Google Authenticator, Authy utt.):", + "verification_code": "Verifikācijas kods", + "confirm": "Apstiprināt", + "disable": "Atspējot", + "disable_confirm_prompt": "Ievadiet paroli, lai atspējotu divpakāpju autentifikāciju.", + "password_required": "Nepieciešama parole", + "code_required": "Nepieciešams verifikācijas kods", + "code_invalid": "Nederīgs verifikācijas kods. Pārbaudiet autentifikācijas lietotni un mēģiniet vēlreiz." + }, + "app_passwords": { + "title": "Lietotņu paroles", + "description": "Izveidojiet paroles lietotnēm, kas neatbalsta 2FA", + "add": "Pievienot", + "create": "Izveidot", + "cancel": "Atcelt", + "generate": "Ģenerēt", + "name_label": "Lietotnes nosaukums", + "name_placeholder": "piem., Thunderbird, iPhone Mail", + "password_label": "Parole (atstājiet tukšu automātiskai ģenerēšanai)", + "password_placeholder": "Automātiska ģenerēšana, ja tukšs", + "added": "Lietotnes parole izveidota", + "removed": "Lietotnes parole izdzēsta", + "add_error": "Neizdevās izveidot lietotnes paroli", + "remove_error": "Neizdevās izdzēst lietotnes paroli", + "none": "Lietotņu paroles nav iestatītas", + "done": "Gatavs", + "expires_label": "Derīguma termiņš (pēc izvēles)", + "copy_now_warning": "Kopējiet šo paroli tagad - tā vairs netiks rādīta.", + "allowed_ips_label": "Atļautās IP (neobligāti)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Atdalītas ar komatu vai atstarpi. Atstājiet tukšu, lai atļautu jebkuru IP." + }, + "api_keys": { + "title": "API atslēgas", + "description": "Izveidojiet API atslēgas skriptiem un integrācijām, kas tieši sazinās ar serveri", + "name_label": "Atslēgas nosaukums", + "name_placeholder": "piem. Dublēšanas skripts, CI palaidējs", + "copy_now_warning": "Kopējiet šo API atslēgu tagad - tā vairs netiks parādīta.", + "added": "API atslēga izveidota", + "removed": "API atslēga noņemta", + "add_error": "Neizdevās izveidot API atslēgu", + "remove_error": "Neizdevās noņemt API atslēgu", + "none": "API atslēgas nav konfigurētas" + }, + "encryption": { + "section_title": "Krātuves šifrēšana", + "label": "E-pasta šifrēšana", + "description": "Šifrēt vēstules serverī papildu privātumam", + "active": "Šifrēšana {type} ir ieslēgta", + "inactive": "Izslēgta", + "enabled": "Krātuves šifrēšana ir iespējota", + "disabled_success": "Krātuves šifrēšana ir izslēgta", + "error": "Neizdevās atjaunināt šifrēšanas iestatījumus", + "algorithm_label": "Šifrēšanas algoritms", + "encrypt_on_append": "Šifrēt jaunās vēstules to augšupielādes laikā", + "allow_spam_training": "Atļaut mēstuļu filtra apmācību pirms vēstuļu šifrēšanas", + "enable_encryption": "Ieslēgt šifrēšanu", + "enable_tooltip": "Ieslēgt šifrēšanu ar šo atslēgu", + "configure_title": "Ieslēgt šifrēšanu ar šo atslēgu", + "enable_button": "Ieslēgt", + "disable_tooltip": "Izslēgt šifrēšanu", + "enabled_success": "Krātuves šifrēšana ir iespējota" + }, + "email_client": { + "title": "Pasta klienta iestatīšana", + "description": "Izmantojiet šos datus, lai iestatītu Thunderbird, Apple Mail vai Outlook", + "jmap_username_label": "JMAP lietotājvārds", + "copy": "Kopēt", + "copied": "Nokopēts", + "password_instructions": "Izmantojiet norādīto JMAP lietotājvārdu kopā ar lietotnes paroli. Izveidojiet lietotnes paroli sadaļā augstāk." + }, + "link_device": { + "title": "Saistīt mobilo lietotni", + "description": "Pierakstieties Bulwark Mail lietotnē savā tālrunī, tur neievadot paroli. Tālrunis saņem savu pierakstīšanos, tāpēc izrakstīšanās šeit to neizraksta.", + "generate": "Rādīt QR kodu", + "regenerate": "Rādīt jaunu kodu", + "instructions": "Atveriet Bulwark Mail lietotni un pierakstīšanās ekrānā pieskarieties \"Scan a sign-in code\" (vai \"Add account\" → \"Scan a sign-in code\", ja jau esat pierakstījies), pēc tam pavērsiet kameru šeit.", + "expires_in": "Šī koda derīgums beigsies pēc {seconds} sekundēm. To var izmantot tikai vienu reizi.", + "expired": "Šī koda derīgums ir beidzies.", + "error": "Neizdevās izveidot pārošanas kodu. Lūdzu, mēģiniet vēlreiz.", + "password_prompt": "Ievadiet paroli, lai apstiprinātu savu identitāti. Tālrunis saņem savu pierakstīšanos; jūsu parole uz tālruni netiek sūtīta.", + "show_password": "Rādīt paroli", + "hide_password": "Slēpt paroli", + "continue": "Turpināt", + "error_wrong_password": "Nepareiza parole. Mēģiniet vēlreiz.", + "totp_prompt": "Ievadiet kodu no autentifikācijas lietotnes.", + "error_password_or_code": "Parole vai kods ir nepareizs.", + "error_not_signed_in": "Jūsu sesija ir beigusies. Pierakstieties vēlreiz, lai saistītu ierīci.", + "error_impersonation": "Ierīces nevar saistīt, kamēr skatāt cita lietotāja pastkasti.", + "error_too_many_attempts": "Pārāk daudz mēģinājumu. Brīdi uzgaidiet un mēģiniet vēlreiz.", + "error_session_secret": "Ierīču saistīšana šajā serverī nav iestatīta (trūkst SESSION_SECRET). Sazinieties ar administratoru.", + "error_server_unreachable": "Neizdevās sazināties ar jūsu pasta serveri. Mēģiniet vēlreiz.", + "error_pairing_unavailable": "Jūsu pasta serveris neļauj šādā veidā saistīt ierīces. Sazinieties ar administratoru.", + "qr_alt": "Pierakstīšanās QR kods", + "link_label": "Pierakstīšanās saite", + "copy_link": "Kopēt pierakstīšanās saiti", + "copied": "Nokopēts", + "copy_hint": "Nav kameras? Nokopējiet saiti un ielīmējiet to lietotnē, izmantojot \"Paste sign-in link\".", + "success_title": "Pierakstīšanās tālrunī izdevās", + "success_body": "Varat aizvērt šo lapu. Lai saistītu citu ierīci, parādiet jaunu kodu.", + "link_another": "Saistīt citu ierīci", + "use_sso": "Tā vietā izmantot vienoto pieteikšanos", + "error_insecure_server": "Mobilā lietotne ar pasta serveriem savienojas tikai caur https, bet šis izmanto http." + }, + "public_keys": { + "name_label": "Publiskās atslēgas nosaukums", + "name_placeholder": "piem., Mana atslēga", + "key_label": "Publiskā atslēga (S/MIME vai PGP ASCII formātā)", + "key_placeholder": "Ielīmējiet šeit savu publisko atslēgu", + "none": "Nav konfigurēta neviena publiskā atslēga" + } + }, + "identities": { + "title": "Sūtītāja identitātes", + "description": "Pārvaldiet e-pasta adreses, no kurām varat sūtīt vēstules", + "identities_count": { + "label": "Jūsu identitātes", + "description": "Konfigurētās sūtīšanas adreses", + "count_zero": "Nav identitāšu", + "count_one": "1 identitāte", + "count_other": "{count} identitātes" + }, + "manage": "Pārvaldīt identitātes", + "sub_addressing": { + "label": "Apakšadresēšana (Sub-addressing)", + "description": "Izmantojiet lietotajs+tags@domens.lv, lai organizētu ienākošo pastu", + "learn_more": "Uzzināt vairāk" + } + }, + "vacation": { + "title": "Prombūtne", + "description": "Automātiski apstrādāt ienākošās vēstules prombūtnes laikā", + "loading": "Ielādē atvaļinājuma iestatījumus...", + "not_supported": "Jūsu pasta serveris neatbalsta atvaļinājuma atbildes.", + "fetch_error": "Neizdevās ielādēt atvaļinājuma iestatījumus.", + "status": { + "label": "Atbildēt automātiski", + "description": "Sūtīt automātisku atbildi uz ienākošajām vēstulēm", + "active": "Aktīva", + "inactive": "Neaktīva" + }, + "date_range": { + "start": "Sākuma datums", + "start_description": "Atstājiet tukšu, lai nebūtu ierobežojuma", + "end": "Beigu datums", + "end_description": "Atstājiet tukšu, lai nebūtu ierobežojuma" + }, + "message": { + "subject_label": "Temats", + "subject_description": "Atvaļinājuma atbildes temats", + "subject_placeholder": "Atvaļinājumā / Esmu prom", + "body_label": "Ziņojuma teksts", + "body_description": "Ziņojuma saturs parastā tekstā", + "body_placeholder": "Paldies par e-pastu. Pašlaik esmu prombūtnē un atbildēšu, kad atgriezīšos.", + "html_label": "Formatēts ziņojums (HTML)", + "html_description": "Pievienojiet formatētu versiju ar saitēm un noformējumu. Saņēmēji, kuru e-pasta programma to nevar parādīt, redzēs iepriekš redzamo vienkāršo tekstu.", + "html_placeholder": "Rakstiet formatētu prombūtnes atbildi…" + }, + "preview": { + "title": "Priekšskatījums", + "show": "Rādīt priekšskatījumu", + "hide": "Slēpt priekšskatījumu" + }, + "save": "Saglabāt izmaiņas", + "saving": "Saglabā...", + "warnings": { + "end_before_start": "Beigu datumam jābūt vēlāk par sākuma datumu", + "invalid_date": "Norādiet derīgu sākuma un beigu datumu ar laiku vai atstājiet lauku tukšu", + "start_in_past": "Sākuma datums ir pagātnē", + "empty_body": "Ziņojuma teksts ir tukšs - saņēmēji saņems tukšu atbildi", + "subject_too_long": "Temats serverim ir pārāk garš (ne vairāk kā {max} baiti).", + "body_too_long": "Ziņa serverim ir pārāk gara (ne vairāk kā {max} baiti, ieskaitot formatējumu).", + "forward_address": "Norādiet derīgu adresi pārsūtīšanai", + "not_running": "Pārsūtīšana vai atbildes saņēmēji ir saglabāti, taču pašlaik nav aktīvi. Saglabājiet, lai tos atkal ieslēgtu." + }, + "forward": { + "title": "Pārsūtīšana", + "description": "Pārsūta ienākošos ziņojumus uz citu adresi, ar automātisko atbildi vai bez tās, iepriekš norādītajā laika posmā (bez beigu datuma, līdz to izslēdz). Mēstules netiek pārsūtītas.", + "enabled_label": "Pārsūtīt ziņojumus", + "to_label": "Pārsūtīt uz", + "to_placeholder": "lietotajs@piemers.lv", + "keep_label": "Atstāt iesūtnē", + "keep_description": "Pretējā gadījumā ziņojums tiek tikai pārsūtīts: tas nepaliek šajā pastkastē, un tam netiek piemērots neviens filtra noteikums." + }, + "audience": { + "label": "Atbildēt", + "description": "Iekšējie sūtītāji: {domains}", + "all": "Visiem sūtītājiem", + "internal": "Tikai iekšējiem sūtītājiem", + "external": "Tikai ārējiem sūtītājiem" + }, + "auto_reply": { + "title": "Automātiskā atbilde" + } + }, + "folders": { + "role_memos": "Piezīmes", + "title": "Mapes", + "description": "Pārvaldiet pasta mapes un piešķiriet tām lomas", + "folder_list": "Jūsu mapes", + "folder_list_description": "Noklikšķiniet uz mapes ikonas, lai to konfigurētu", + "standard_roles": "Standarta mapju lomas", + "standard_roles_description": "Piešķiriet mapes tādām lomām kā Iesūtne, Nosūtītie, Atkritne utt.", + "role_inbox": "Iesūtne", + "role_drafts": "Melnraksti", + "role_sent": "Nosūtītie", + "role_trash": "Atkritne", + "role_junk": "Mēstules", + "role_archive": "Arhīvs", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Nav", + "create_folder": "Izveidot mapi", + "create_subfolder": "Izveidot apakšmapi", + "reorder": "Velciet, lai pārkārtotu", + "reorder_error": "Neizdevās pārkārtot mapes", + "move_error": "Neizdevās pārvietot mapi", + "select_folder": "Atlasīt {name}", + "select_hint": "Atlasīt. Shift+klikšķis atlasa visas mapes līdz pēdējai, uz kuras noklikšķinājāt.", + "selected_count": "{count, plural, zero {Atlasītas # mapes} one {Atlasīta # mape} other {Atlasītas # mapes}}", + "move_to": "Pārvietot uz...", + "move_to_top_level": "Augšējais līmenis", + "move": "Pārvietot", + "clear_selection": "Notīrīt atlasi", + "folders_moved": "{count, plural, zero {Pārvietotas # mapes} one {Pārvietota # mape} other {Pārvietotas # mapes}}", + "move_partial_error": "{count, plural, zero {# mapes neizdevās pārvietot} one {# mapi neizdevās pārvietot} other {# mapes neizdevās pārvietot}}", + "subfolder_of": "Mapē {name}", + "subfolder_name": "Apakšmapes nosaukums", + "new_folder_name": "Mapes nosaukums", + "rename": "Pārdēvēt", + "change_icon": "Mainīt ikonu", + "count_mode": "Skaitītājs mapei {name}", + "count_mode_default": "Skaitītājs: noklusējums", + "count_mode_unread": "Tikai nelasītie", + "count_mode_all": "Nelasītie un kopā", + "count_mode_none": "Bez skaitītāja", + "delete": "Dzēst", + "confirm_delete": "Vai tiešām vēlaties dzēst \"{name}\"? Vēstules tiks pārvietotas uz atkritni.", + "create": "Izveidot", + "cancel": "Atcelt", + "no_folders": "Nav pielāgotu mapju", + "cannot_delete_role": "Nevar dzēst mapi ar standarta lomu. Vispirms noņemiet lomu.", + "folder_created": "Mape izveidota", + "folder_renamed": "Mape pārdēvēta", + "folder_deleted": "Mape izdzēsta", + "role_updated": "Mapes loma atjaunināta", + "error_create": "Neizdevās izveidot mapi", + "error_rename": "Neizdevās pārdēvēt mapi", + "error_delete": "Neizdevās izdzēst mapi", + "error_role": "Neizdevās atjaunināt mapes lomu", + "error_delete_has_children": "Nevar dzēst mapi: tajā joprojām ir apakšmapes. Vispirms tās dzēsiet vai pārvietojiet.", + "error_delete_has_email": "Nevar dzēst mapi: tajā joprojām ir e-pasti. Vispirms tos pārvietojiet vai dzēsiet." + }, + "advanced": { + "title": "Papildu", + "description": "Paplašinātie iestatījumi un izstrādātāja rīki", + "debug_mode": { + "label": "Atkļūdošanas režīms", + "description": "Iespējot detalizētu žurnālēšanu diagnostikai" + }, + "settings_sync": { + "label": "Iestatījumu sinhronizācija", + "description": "Sinhronizēt iestatījumus starp dažādiem pārlūkiem un ierīcēm" + }, + "sender_favicons": { + "label": "Sūtītāju ikonas", + "description": "Rādīt vietņu ikonas kā sūtītāju avatarus", + "refresh_label": "Atsvaidzināt sūtītāju ikonas", + "refresh_description": "Vēlreiz ielādēt vietņu ikonas, piemēram, ja sūtītājs ir nomainījis savu ikonu", + "refresh_button": "Atsvaidzināt", + "refresh_done": "Sūtītāju ikonas tiks ielādētas vēlreiz" + }, + "show_avatars_in_junk": { + "label": "Rādīt avatarus mēstuļu mapē", + "description": "Rādīt sūtītāju profila attēlus un ikonas mēstuļu mapē. Pēc noklusējuma izslēgts, lai pikšķerēšanas mēģinājumi neizskatītos uzticami." + }, + "keyboard_shortcuts": { + "label": "Īsinājumtaustiņi", + "description": "Skatīt pieejamos tastatūras īsinājumtaustiņus", + "button": "Rādīt īsinājumtaustiņus" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Atiestatīt iestatījumus", + "description": "Atjaunot visus iestatījumus uz noklusējuma vērtībām", + "button": "Atiestatīt" + }, + "export_settings": { + "label": "Eksportēt iestatījumus", + "description": "Lejupielādēt iestatījumus JSON formātā", + "button": "Eksportēt" + }, + "import_settings": { + "label": "Importēt iestatījumus", + "description": "Augšupielādēt iestatījumus no JSON faila", + "button": "Importēt" + }, + "debug_categories": { + "description": "Izvēlieties, kuras kategorijas žurnalēt. Atspējojiet nevajadzīgās kategorijas, lai samazinātu konsoles troksni.", + "jmap": "JMAP klients", + "jmap_description": "Pastkastes darbības, e-pastu ielāde un JMAP protokola pieprasījumi", + "calendar": "Kalendārs", + "calendar_description": "Kalendāra notikumi, importi un plānošanas ziņas", + "tasks": "Uzdevumi", + "tasks_description": "Kalendāra uzdevumu izveide, ielāde un atjaunināšana", + "auth": "Autentifikācija", + "auth_description": "Pieteikšanās, TOTP, tokenu apmaiņa un sesiju pārvaldība", + "filters": "Filtri", + "filters_description": "Sieve filtru noteikumi un prombūtnes skripti", + "email": "E-pasta skatīšana", + "email_description": "E-pasta renderēšana, TNEF apstrāde un atzīmēšana kā lasīts", + "push": "Push paziņojumi", + "push_description": "Push paziņojumu iestatīšana un piegāde", + "contacts": "Kontakti un adrešu grāmatas", + "contacts_description": "Kontaktu sinhronizācija, adrešu grāmatu darbības un uzticamie sūtītāji" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Pirmkods" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Noklikšķiniet uz mēstulēm un pikšķerēšanas vēstulēm, pirms tās sasniedz iesūtni. Īstās vēstules laidiet cauri. Zaudējot visus trīs vairogus, iesūtne krīt.", + "kinds": { + "spam": "Mēstule", + "phishing": "Pikšķerēšana", + "legit": "Īsta vēstule" + }, + "click_to_block": "Noklikšķiniet, lai bloķētu", + "let_through": "Laidiet cauri", + "start": "Sākt", + "play_again": "Spēlēt vēlreiz", + "score": "Punkti", + "combo": "Sērijas bonuss", + "shields_left": "Atlikuši {count} no {total} vairogiem", + "seconds": "{seconds} s", + "best": "Rekords: {score}", + "new_best": "Jauns rekords", + "held": "Iesūtne nosargāta", + "overrun": "Iesūtne krita", + "stats": { + "caught": "Bloķētas", + "delivered": "Piegādātas", + "mistakes": "Kļūdas" + } + } + }, + "sidebar_apps": { + "title": "Sānu joslas lietotnes", + "description": "Pārvaldiet pielāgotas lietotnes un saites sānu joslā", + "keep_loaded": "Turēt lietotnes ielādētas", + "keep_loaded_description": "Turēt iebūvētās lietotnes fonā, lai izvairītos no pārlādēšanas", + "managed_title": "Nodrošina administrators", + "managed_description": "Šīs lietotnes ir iestatītas visiem, un tās nevar mainīt.", + "manage_title": "Pielāgotas lietotnes", + "manage_description": "Pievienojiet, rediģējiet vai dzēsiet sānu joslas lietotnes" + }, + "contacts": { + "title": "Kontakti", + "description": "Importējiet un eksportējiet savus kontaktus", + "import_label": "Kontaktu imports", + "import_description": "Importēt kontaktus no vCard faila (.vcf)", + "export_label": "Kontaktu eksports", + "export_description": "Eksportēt visus kontaktus vCard failā (.vcf)", + "manage_title": "Adrešu grāmatas", + "manage_description": "Pārdēvēt savas adrešu grāmatas", + "no_address_books": "Adrešu grāmatas nav atrastas", + "categories_title": "Kategorijas", + "categories_description": "Pārdēvēt kontaktu kategorijas", + "no_categories": "Kategorijas nav atrastas", + "group_by_letter_description": "Rādīt alfabētiskos sadaļu virsrakstus kontaktu sarakstā", + "group_by_letter_label": "Grupēt pēc pirmā burta", + "sort_by_last_name_label": "Kārtot pēc uzvārda", + "sort_by_last_name_description": "Kārtot kontaktu sarakstu pēc uzvārda, lai ģimenes locekļi būtu kopā" + }, + "downloads": { + "title": "Lejupielādes", + "description": "Pielāgojiet, kā tiek nosaukti lejupielādētie e-pasti un pielikumi.", + "reset": "Atjaunot noklusējumu", + "preview": "Priekšskatījums:", + "email_template": { + "label": "E-pasta faila nosaukums (.eml)", + "description": "Veidne, ko izmanto, eksportējot e-pastu vai velkot to uz failu sistēmu. Paplašinājums .eml tiek pievienots automātiski." + }, + "attachment_template": { + "label": "Pielikuma faila nosaukums", + "description": "Veidne, ko izmanto, lejupielādējot vai velkot pielikumu. Ja izlaižat '{filename}' un '{ext}', sākotnējais paplašinājums tiek saglabāts." + }, + "bundle_template": { + "label": "Vairāku e-pastu .zip faila nosaukums", + "description": "Veidne, ko izmanto, kad velkat vai lejupielādējat vairākus atlasītos e-pastus kā vienu .zip arhīvu. Paplašinājums .zip tiek pievienots automātiski." + }, + "spaces": { + "label": "Atstarpes", + "description": "Aizstāt atstarpes izvades faila nosaukumā ar citu rakstzīmi.", + "keep": "Saglabāt atstarpes", + "underscore": "Aizstāt ar _", + "dash": "Aizstāt ar -" + }, + "lowercase": { + "label": "Mazie burti", + "description": "Piespiest visu faila nosaukumu uz mazajiem burtiem." + }, + "strip_diacritics": { + "label": "Noņemt diakritikas zīmes", + "description": "Pārvērst burtus ar akcentiem to ASCII ekvivalentos (ä → a, é → e). Noderīgi rīkiem, kas slikti apstrādā Unicode failu nosaukumus." + }, + "collapse_separators": { + "label": "Apvienot atkārtotus atdalītājus", + "description": "Apvienot virknes atstarpju, pasvītrojumu vai defisu vienā rakstzīmē." + }, + "after_export": { + "label": "Pēc eksporta", + "description": "Pēc e-pasta eksportēšanas kā .eml to var pārvietot.", + "keep": "Paturēt pastkastē", + "archive": "Pārvietot uz arhīvu", + "trash": "Pārvietot uz miskasti" + } + }, + "filters": { + "title": "Pasta filtri", + "description": "Izveidojiet noteikumus automātiskai vēstuļu šķirošanai un pārvaldībai", + "add_rule": "Pievienot noteikumu", + "no_rules": "Nav filtra noteikumu", + "vacation_active": "Atvaļinājuma atbilde ir aktīva", + "vacation_active_description": "Automātiska atbilde ir ieslēgta ienākošajām vēstulēm", + "vacation_configure": "Konfigurēt", + "no_rules_description": "Izveidojiet noteikumus automātiskai ienākošā pasta organizēšanai", + "edit_rule": "Rediģēt noteikumu", + "new_rule": "Jauns noteikums", + "delete_rule": "Dzēst noteikumu", + "delete_confirm": "Vai tiešām vēlaties dzēst šo noteikumu?", + "enable": "Ieslēgt", + "disable": "Izslēgt", + "raw_editor": "Sieve redaktors", + "raw_editor_warning": "Sieve skripta rediģēšana var sabojāt vizuālo noteikumu konstruktoru.", + "validate": "Pārbaudīt", + "validation_success": "Skripts ir derīgs", + "validation_error": "Skriptā ir kļūdas", + "save": "Saglabāt noteikumus", + "saving": "Saglabā...", + "saved": "Filtri veiksmīgi saglabāti", + "save_failed": "Neizdevās saglabāt filtrus", + "loading": "Ielādē filtrus...", + "not_supported": "Jūsu pasta serveris neatbalsta filtrus.", + "rule_name": "Noteikuma nosaukums", + "rule_name_placeholder": "piem., Ziņu izdevumu šķirošana", + "match_all": "Atbilst VISIEM nosacījumiem", + "match_any": "Atbilst JEBKURAM nosacījumam", + "conditions": "Nosacījumi", + "add_condition": "Pievienot nosacījumu", + "actions": "Darbības", + "add_action": "Pievienot darbību", + "stop_processing": "Pārtraukt nākamo noteikumu apstrādi", + "include_spam": "Pārvietot arī ziņas, kas atzīmētas kā mēstules", + "attachment_type_placeholder": "piem. pdf, doc, jpg", + "value_placeholder_multi": "Vērtība (vairākas atdalītas ar komatu)", + "condition_fields": { + "attachment": "Pielikums", + "from": "No", + "to": "Kam", + "cc": "Kopija", + "subject": "Temats", + "header": "Pielāgota galvene", + "size": "Izmērs", + "body": "Teksts", + "all": "Visas vēstules" + }, + "comparators": { + "has_any": "ir klāt", + "has_type": "tipa", + "contains": "satur", + "not_contains": "nesatur", + "is": "precīzi sakrīt", + "not_is": "nav vienāds", + "starts_with": "sākas ar", + "ends_with": "beidzas ar", + "matches": "atbilst paraugam", + "greater_than": "lielāks nekā", + "less_than": "mazāks nekā", + "address_is": "ir adrese", + "domain_is": "ir domēns" + }, + "action_types": { + "move": "Pārvietot uz mapi", + "copy": "Kopēt uz mapi", + "forward": "Pārsūtīt uz", + "mark_read": "Atzīmēt kā izlasītu", + "star": "Pievienot zvaigznīti", + "add_label": "Pievienot tagu", + "discard": "Dzēst (bez paziņojuma)", + "reject": "Noraidīt ar ziņojumu", + "keep": "Atstāt iesūtnē", + "stop": "Pārtraukt apstrādi" + }, + "move_to_folder": "Izvēlēties mapi", + "copy_to_folder": "Izvēlēties mapi", + "forward_to": "Pārsūtīt uz e-pastu", + "forward_placeholder": "lietotajs@piemers.lv", + "forward_keep_copy": "Saglabāt kopiju", + "forward_limit": "Pārsūtīšanas ierobežojums vienai ziņai šajā serverī: {count}. Papildu pārsūtīšanas tiek izlaistas.", + "forward_unavailable": "Šis serveris neatļauj pārsūtīšanu. Pārsūtīšanas darbībām filtru kārtulās nav nekādas ietekmes.", + "reject_message": "Noraidīšanas ziņojums", + "reject_placeholder": "Jūsu e-pasts tika noraidīts", + "label_name": "Taga nosaukums", + "label_placeholder": "Izvēlēties tagu", + "header_name": "Galvenes nosaukums", + "header_placeholder": "piem., X-Mailing-List", + "size_bytes": "Izmērs baitos", + "size_placeholder": "piem., 1000000", + "system_managed": "Sistēmas pārvaldīts noteikums", + "opaque_warning": "Šis skripts tika rediģēts ārpus vizuālā konstruktora. Pieejama tikai Sieve rediģēšana.", + "open_sieve_editor": "Atvērt Sieve redaktoru", + "fetch_error": "Neizdevās ielādēt filtrus", + "expanded_view": "Izvērstais skats", + "expanded_view_description": "Rādīt filtra noteikumus ar detalizētiem nosacījumiem un darbībām", + "if": "Ja", + "then": "Tad", + "match_all_conditions": "atbilst visi", + "match_any_condition": "atbilst jebkurš", + "and": "un", + "or": "vai", + "cancel": "Atcelt", + "confirm_delete": "Dzēst", + "rule_list": "Filtru noteikumi", + "drag_to_reorder": "Velciet, lai mainītu secību", + "match_type": "Atbilstības veids", + "reset_to_visual": "Atiestatīt uz vizuālo konstruktoru", + "reset_warning": "Tas dzēsīs pašreizējo skriptu un sāks no jauna.", + "confirm_reset": "Atiestatīt", + "validation_empty_name": "Nepieciešams noteikuma nosaukums", + "validation_empty_conditions": "Nepieciešams vismaz viens nosacījums ar vērtību", + "validation_empty_actions": "Nepieciešama vismaz viena darbība", + "templates_section": "Sākt no veidnes", + "template_newsletters": "Pārvietot ziņu izdevumus uz mapi", + "template_receipts": "Automātiski arhivēt rēķinus", + "template_important": "Atzīmēt svarīgas vēstules", + "template_notifications": "Filtrēt paziņojumus", + "sieve_editor": { + "title": "Sieve skripta redaktors", + "warning": "Sieve skripta rediģēšana var sabojāt vizuālo konstruktoru.", + "script_content": "Sieve skripts", + "valid": "Skripts ir derīgs", + "invalid": "Skriptā ir kļūdas", + "save_warning": "Saglabāšana pārrakstīs vizuālos noteikumus. To nevar atcelt. Nospiediet \"Saglabāt\" vēlreiz.", + "validating": "Pārbauda...", + "validate": "Pārbaudīt", + "cancel": "Atcelt", + "save": "Saglabāt", + "confirm_save": "Apstiprināt saglabāšanu", + "validation_failed": "Pārbaudes pieprasījuma kļūda" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# nosacījums} other {# nosacījumi}}", + "actions_count": "{count, plural, one {# darbība} other {# darbības}}" + }, + "origin_external": "Ārējs", + "managed_by_tooltip": "Pārvalda {source}. Rediģējiet to šajā lietotnē vai izmantojiet neapstrādāto Sieve redaktoru.", + "quick_rule_names": { + "move_sender": "Pārvietot no {sender} uz {folder}", + "move_domain": "Pārvietot no @{domain} uz {folder}", + "move_list": "Pārvietot sarakstu {list} uz {folder}", + "mark_read": "Atzīmēt no {sender} kā izlasītas", + "tag": "Pievienot tagu {tag} vēstulēm no {sender}", + "block": "Bloķēt {sender}", + "custom": "Vēstules no {sender}", + "custom_plain": "Jauns noteikums", + "senders": "{count, plural, zero {# sūtītājiem} one {# sūtītāja} other {# sūtītājiem}}" + }, + "suggestions": "Ieteikumi", + "suggest_subject": "Temats “{subject}”", + "suggest_to": "Kam {address}", + "suggest_cc": "Kopija {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Piemērot arī vēstulēm, kas jau ir šajā mapē", + "apply_existing_unsupported": "Esošajām vēstulēm var piemērot tikai noteikumus, kas pārbauda sūtītāju, adresātus, tematu vai galvenes un kas pārvieto, kopē, atzīmē kā izlasītu, ar zvaigznīti vai tagu.", + "period_title": "Datumu diapazons", + "period_start": "Sākuma datums", + "period_end": "Beigu datums", + "validation_period_order": "Beigu datumam jābūt vēlāk par sākuma datumu", + "period_toggle": "Aktīvs tikai noteiktā laika posmā", + "period_hint": "Attiecas uz ziņojumiem, kas pienāk šajā laika posmā. Atstājiet lauku tukšu, lai šo galu neierobežotu.", + "validation_period_empty": "Norādiet laika posma sākuma vai beigu datumu", + "validation_period_invalid": "Norādiet laika posmam derīgu datumu ar laiku vai atstājiet lauku tukšu", + "apply_existing_period": "Noteikums ar laika posmu darbojas tikai ar ziņojumiem, kas pienāk šajā posmā, tāpēc to nevar lietot esošajiem ziņojumiem.", + "period_status_scheduled": "ieplānots", + "period_status_active": "aktīvs", + "period_status_expired": "beidzies" + }, + "templates": { + "title": "Vēstuļu veidnes", + "description": "Izveidojiet atkārtoti lietojamas veidnes ar mainīgajiem lielumiem", + "add": "Jauna veidne", + "edit": "Rediģēt veidni", + "name": "Veidnes nosaukums", + "name_placeholder": "piem., Atgādinājuma e-pasts", + "category": "Kategorija", + "category_placeholder": "piem., Darbs, Personīgi", + "subject": "Temats", + "subject_placeholder": "Vēstules temats", + "body": "Teksts", + "body_placeholder": "Vēstules saturs...", + "recipients_placeholder": "lietotajs@piemers.lv", + "identity": "Sūtīt kā", + "default_identity": "Noklusējuma identitāte", + "favorite": "Izlase", + "cancel": "Atcelt", + "create": "Izveidot", + "update": "Atjaunināt", + "confirm_delete": "Dzēst", + "no_templates": "Nav veidņu", + "manage": "Pārvaldīt veidnes", + "count": "{count, plural, one {# veidne} other {# veidnes}}", + "export_import": "Eksports un imports", + "export_import_description": "Dublējiet veidnes vai pārvietojiet uz citu ierīci", + "export": "Eksportēt", + "import": "Importēt", + "validation": { + "empty": "Veidnes nosaukums ir obligāts", + "too_long": "Veidnes nosaukums nedrīkst pārsniegt 200 rakstzīmes" + } + }, + "files": { + "display": { + "title": "Attēlošana", + "description": "Pielāgojiet failu un mapju attēlošanu" + }, + "default_view": { + "label": "Noklusējuma skats", + "description": "Izvēlieties starp režģi un sarakstu", + "list": "Saraksts", + "grid": "Režģis" + }, + "default_sort": { + "label": "Noklusējuma kārtošana", + "description": "Izvēlieties failu kārtošanas veidu", + "name": "Nosaukums", + "size": "Izmērs", + "modified": "Modificēts" + }, + "sort_direction": { + "label": "Kārtošanas virziens", + "description": "Augošā vai dilstošā secībā", + "ascending": "Augošā", + "descending": "Dilstošā" + }, + "icons": { + "title": "Ikonas", + "description": "Pielāgojiet failu ikonu izskatu" + }, + "show_icons": { + "label": "Rādīt failu ikonas", + "description": "Rādīt ikonas blakus failiem un mapēm" + }, + "colored_icons": { + "label": "Krāsainas ikonas", + "description": "Izmantot krāsainas ikonas melnbalto vietā" + }, + "show_thumbnails": { + "label": "Rādīt sīktēlus", + "description": "Rādīt attēlu priekšskatījumus ikonu vietā" + }, + "behavior": { + "title": "Darbība", + "description": "Pielāgojiet failu pārvaldnieka darbību" + }, + "show_hidden": { + "label": "Rādīt slēptos failus", + "description": "Rādīt failus un mapes, kas sākas ar punktu" + }, + "preview": { + "label": "Priekšskatījums" + } + }, + "search_placeholder": "Meklēt iestatījumos", + "search_clear": "Notīrīt meklēšanu", + "search_no_results": "Nav atbilstošu iestatījumu", + "scoped": { + "back": "Atpakaļ uz manu kontu", + "managing": "Pārvalda: {name}" + } + }, + "errors": { + "page_error_title": "Kaut kas nogāja griezi", + "page_error_description": "Notikusi neparedzēta kļūda. Lūdzu, mēģiniet vēlreiz vai atgriezieties sākumlapā.", + "sidebar_error": "Neizdevās ielādēt pastkastes", + "email_list_error": "Neizdevās ielādēt vēstules", + "viewer_error_title": "Neizdevās parādīt vēstuli", + "viewer_error_description": "Radās problēma, attēlojot šo vēstuli. Tā var saturēt neatbalstītu saturu.", + "composer_error": "Neizdevās ielādēt vēstuļu redaktoru", + "settings_error_title": "Iestatījumi nav pieejami", + "settings_error_description": "Neizdevās ielādēt iestatījumus. Jūsu izvēles var netikt saglabātas.", + "try_again": "Mēģināt vēlreiz", + "reload": "Pārlādēt", + "reload_emails": "Pārlādēt vēstules", + "reload_settings": "Pārlādēt iestatījumus", + "retry": "Mēģināt vēlreiz", + "go_home": "Uz iesūtni" + }, + "context_menu": { + "reply": "Atbildēt", + "reply_all": "Atbildēt visiem", + "forward": "Pārsūtīt", + "mark_read": "Atzīmēt kā izlasītu", + "mark_unread": "Atzīmēt kā nelasītu", + "star": "Pievienot zvaigznīti", + "unstar": "Noņemt zvaigznīti", + "pin": "Piespraust", + "unpin": "Atspraust", + "move_to": "Pārvietot uz...", + "copy_to_account": "Kopēt uz kontu...", + "remove_from_folder": "Noņemt no šīs mapes", + "archive": "Arhivēt", + "delete": "Dzēst", + "mark_as_spam": "Atzīmēt kā mēstuli", + "not_spam": "Nav mēstule", + "tag": "Tags", + "items_selected": "{count} vēstules atlasītas", + "forward_as_attachments": "Pārsūtīt kā pielikumiem", + "edit_draft": "Rediģēt melnrakstu", + "cancel_scheduled_send": "Atcelt sūtīšanu", + "reschedule_send": "Pārplānot", + "cancel_and_edit": "Atcelt un rediģēt", + "cancel_and_compose_again": "Atcelt un rakstīt no jauna", + "rules": { + "title": "Noteikumi", + "move_from": "Vienmēr pārvietot vēstules no {sender}", + "move_from_domain": "Vienmēr pārvietot vēstules no @{domain}", + "move_from_list": "Vienmēr pārvietot vēstules no šī saraksta", + "mark_read_from": "Vienmēr atzīmēt vēstules no {sender} kā izlasītas", + "tag_with": "Vienmēr pievienot tagu", + "block": "{count, plural, zero {Bloķēt # sūtītājus} one {Bloķēt sūtītāju} other {Bloķēt # sūtītājus}}", + "no_junk": "Šim kontam nav mēstuļu mapes", + "create": "Izveidot noteikumu…", + "manage": "Pārvaldīt noteikumus", + "senders_count": "{count, plural, zero {# sūtītājiem} one {# sūtītāja} other {# sūtītājiem}}", + "cross_account": "Izvēlieties vēstules no viena konta", + "opaque_hint": "Jūsu filtri ir rediģēti manuāli. Atveriet filtru iestatījumus" , + "loading": "Ielādē kārtulas…", + "load_failed": "Neizdevās ielādēt kārtulas", + "none": "Vēl nav kārtulu", + "new_mail_only": "Tikai jaunām ziņām", + "running": "Izpilda “{name}” mapē {folder}…" + }, + "find_emails_from": "Atrast e-pastus no {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Atzīmēt mapi kā lasītu", + "mark_folder_tree_read": "Atzīmēt mapi un apakšmapes kā lasītas", + "mark_all_folders_read": "Atzīmēt visas mapes kā lasītas", + "new_subfolder": "Jauna apakšmape...", + "new_folder": "Jauna mape...", + "rename": "Pārsaukt...", + "share": "Kopīgot...", + "import_email": "Importēt .eml vai .zip...", + "empty_folder": "Iztukšot mapi", + "empty_folder_generic": "Iztukšot mapi", + "delete_folder": "Dzēst mapi", + "refresh": "Atjaunināt", + "hide_shared_account": "Paslēpt no sānjoslas", + "mute_notifications": "Izslēgt paziņojumus", + "unmute_notifications": "Ieslēgt paziņojumus", + "mute_account_notifications": "Izslēgt šī konta paziņojumus", + "unmute_account_notifications": "Ieslēgt šī konta paziņojumus", + "reset_folder_notifications": "Atiestatīt mapju paziņojumus", + "mark_all_confirm_title": "Atzīmēt visas mapes kā lasītas", + "mark_all_confirm_message": "Atzīmēt visas nelasītās ziņas jūsu personīgajā kontā kā lasītas?", + "delete_confirm_title": "Dzēst mapi", + "delete_confirm_message": "Neatgriezeniski dzēst mapi \"{name}\"? Šo darbību nevar atsaukt.", + "prompt_new_subfolder": "Ievadiet nosaukumu jaunajai apakšmapei.", + "prompt_new_folder": "Ievadiet nosaukumu jaunajai mapei.", + "prompt_rename": "Ievadiet jaunu nosaukumu šai mapei.", + "toast_marked_read": "Mape atzīmēta kā lasīta", + "toast_marked_read_count": "{count, plural, one {1 ziņa atzīmēta} other {# ziņas atzīmētas}} kā lasītas", + "toast_already_read": "Nav nelasītu ziņu", + "toast_marked_all_read": "Visas mapes atzīmētas kā lasītas", + "toast_emptied": "Mape iztukšota", + "toast_folder_created": "Mape izveidota", + "toast_folder_renamed": "Mape pārsaukta", + "toast_folder_deleted": "Mape dzēsta", + "toast_error_mark_read": "Neizdevās atzīmēt kā lasītu", + "toast_error_empty": "Neizdevās iztukšot mapi", + "toast_error_create": "Neizdevās izveidot mapi", + "toast_error_rename": "Neizdevās pārsaukt mapi", + "toast_error_delete": "Neizdevās dzēst mapi", + "toast_error_delete_has_children": "Mapei ir apakšmapes. Vispirms noņemiet tās.", + "toast_error_delete_has_email": "Mape nav tukša. Vispirms iztukšojiet to.", + "delete_with_contents_title": "Mape nav tukša", + "delete_with_contents_message": "Mapē “{name}” joprojām ir {count, plural, one {1 ziņa} other {# ziņas}}. Dzēst mapi un visas tās ziņas? To nevar atsaukt.", + "delete_with_contents_confirm": "Dzēst mapi un ziņas", + "placeholder_folder_name": "Mapes nosaukums", + "create": "Izveidot", + "rename_confirm": "Pārsaukt" + }, + "shortcuts": { + "title": "Īsinājumtaustiņi", + "tip": "Nospiediet ? jebkurā laikā, lai skatītu palīdzību", + "sections": { + "navigation": "Navigācija", + "actions": "Vēstules darbības", + "global": "Globālie", + "threads": "Sarunas", + "composer": "Redaktors" + }, + "navigation": { + "next_email": "Nākamā vēstule", + "previous_email": "Iepriekšējā vēstule", + "open_email": "Atvērt vēstuli", + "close_email": "Aizvērt / Noņemt atlasi", + "go_to_folder": "Pāriet uz mapi", + "next_unread": "Nākamais nelasītais e-pasts", + "no_more_unread": "Sarakstā zemāk vairs nav nelasītu e-pastu" + }, + "actions": { + "reply": "Atbildēt", + "reply_all": "Atbildēt visiem", + "forward": "Pārsūtīt", + "star": "Pārslēgt zvaigznīti", + "archive": "Arhivēt", + "delete": "Dzēst", + "mark_unread": "Atzīmēt kā nelasītu", + "mark_read": "Atzīmēt kā izlasītu", + "toggle_spam": "Mēstule / Nav mēstule" + }, + "global": { + "compose": "Rakstīt jaunu vēstuli", + "search": "Iet uz meklēšanu", + "help": "Rādīt īsinājumtaustiņus", + "refresh": "Atsvaidzināt vēstules", + "select_all": "Atlasīt visas", + "global_search": "Meklēt visā", + "extend_selection": "Paplašināt atlasi uz leju / uz augšu" + }, + "threads": { + "expand_collapse": "Izvērst/sairt sarunu", + "select_mountain_view": "Atlasīt sarunu" + }, + "composer": { + "send": "Nosūtīt e-pastu", + "schedule_send": "Ieplānot nosūtīšanu", + "template_picker": "Atvērt veidņu izvēli" + } + }, + "threads": { + "messages_one": "{count} ziņojums", + "messages_other": "{count} ziņojumi", + "messages_tooltip": "{count, plural, one {# ziņojums šajā sarunā} other {# ziņojumi šajā sarunā}}", + "expand": "Izvērst sarunu", + "collapse": "Sairt sarunu", + "loading": "Ielādē sarunu...", + "mark_read": "Atzīmēt sarunu kā izlasītu", + "mark_unread": "Atzīmēt sarunu kā nelasītu", + "archive": "Arhivēt sarunu", + "delete": "Dzēst sarunu", + "star": "Pievienot zvaigznīti sarunai", + "unstar": "Noņemt zvaigznīti sarunai", + "toggle_thread": "Pārslēgt sarunu" + }, + "identities": { + "modal_title": "Pārvaldīt sūtītāja identitātes", + "create_new": "Izveidot jaunu identitāti", + "edit_identity": "Rediģēt identitāti", + "delete_confirm": "Vai dzēst šo identitāti? Šo darbību nevar atcelt.", + "cannot_delete": "Šo identitāti nevar dzēst", + "primary_identity": "Galvenā", + "set_as_primary": "Iestatīt kā galveno", + "no_identities": "Identitātes nav atrastas", + "display": { + "reply_to": "Atbildēt uz:", + "bcc": "SK:", + "signature": "Paraksts:", + "preview": "Priekšskatījums:" + }, + "validation_errors": { + "invalid_emails": "Nederīgas adreses: {emails}", + "unknown_error": "Nezināma kļūda" + }, + "form": { + "name_label": "Redzamais vārds", + "name_placeholder": "piem., Darba e-pasts, Personīgais", + "name_required": "Vārds ir obligāts", + "email_label": "E-pasta adrese", + "email_placeholder": "lietotajs@piemers.lv", + "email_required": "E-pasts ir obligāts", + "email_invalid": "Ievadiet derīgu e-pasta adresi", + "email_immutable": "E-pasta adresi nevar mainīt pēc izveides", + "reply_to_label": "Atbildēt uz (pēc izvēles)", + "reply_to_placeholder": "cits@e-pasts.lv", + "bcc_label": "Automātiska SK (pēc izvēles)", + "bcc_placeholder": "arhivs@piemers.lv", + "text_signature_label": "Teksta paraksts", + "html_signature_label": "HTML paraksts", + "signature_byte_counter": "{bytes} / {max} baiti", + "signature_byte_limit_reached": "Sasniegts servera ierobežojums", + "save": "Saglabāt identitāti", + "cancel": "Atcelt", + "creating": "Izveido...", + "updating": "Atjaunina..." + }, + "sub_address": { + "button_tooltip": "Izmantot apakšadresi", + "popover_title": "Pievienot apakšadreses tagu", + "tag_input_placeholder": "Ievadiet tagu (piem., iepirksanas)", + "preview_label": "Priekšskatījums:", + "recent_tags": "Nesenie tagi", + "suggested_tags": "Ieteikumi", + "use_address": "Izmantot šo adresi", + "invalid_tag": "Tags var saturēt tikai burtus, ciparus un domuzīmes", + "tag_too_long": "Tags nedrīkst pārsniegt 30 rakstzīmes", + "help_text": "Vēstules uz lietotajs{delimiter}tags@domens.lv nonāks jūsu pastkastē", + "validation": { + "empty": "Tags nevar būt tukšs", + "too_long": "Tags nedrīkst pārsniegt {max} rakstzīmes", + "invalid_chars": "Tags var saturēt tikai burtus, ciparus un domuzīmes" + } + }, + "badge": { + "sent_via": "caur", + "sub_address_tag": "Sūtīts caur apakšadresi: {tag}", + "identity_name": "Sūtīts caur identitāti: {name}", + "identity_short": "caur {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Dzēst", + "delete_confirm_title": "Dzēst identitāti" + }, + "templates": { + "picker_title": "Izvēlēties veidni", + "search_placeholder": "Meklēt veidnes...", + "section_favorites": "Izlase", + "section_recent": "Nesenie", + "section_uncategorized": "Citi", + "no_templates": "Veidnes vēl nav izveidotas", + "no_results": "Veidnes nav atrastas", + "fill_placeholders": "Aizpildīt mainīgo vērtības", + "enter_value": "Ievadiet vērtību...", + "preview": "Priekšskatījums", + "insert_with_values": "Ievietot ar vērtībām", + "insert_raw": "Ievietot bez izmaiņām", + "copy_suffix": "(kopija)", + "placeholder": "Mainīgais", + "placeholders": { + "recipient_name": "Saņēmēja vārds", + "company": "Uzņēmuma nosaukums", + "date": "Pašreizējais datums", + "day_of_week": "Nedēļas diena", + "sender_name": "Jūsu vārds" + } + }, + "contacts": { + "directory": { + "title": "Katalogs", + "search_placeholder": "Meklēt vārdu vai e-pasta adresi", + "refresh": "Atsvaidzināt katalogu", + "retry": "Mēģināt vēlreiz", + "loading": "Ielādē katalogu…", + "unavailable_title": "Nav kataloga", + "unavailable_hint": "Jūsu pasta serveris nepiedāvā kontu katalogu.", + "forbidden_title": "Katalogs nav pieejams", + "forbidden_hint": "Jūsu pasta serveris neļauj šim kontam pārlūkot katalogu. Jautājiet administratoram.", + "error_title": "Neizdevās ielādēt katalogu", + "error_hint": "Pārbaudiet savienojumu un mēģiniet vēlreiz.", + "empty_title": "Katalogs ir tukšs", + "empty_hint": "Jūsu pasta serveris nerāda nevienu kontu ar e-pasta adresi.", + "no_match": "Neviens katalogā neatbilst meklējumam", + "refresh_failed": "Neizdevās atsvaidzināt katalogu. Tiek rādīts pēdējais ielādētais saraksts.", + "select_page": "Atlasīt visus šajā lapā", + "deselect_page": "Noņemt atlasi šajā lapā", + "select_entry": "Atlasīt {name}", + "range": "{from}–{to} no {total}", + "page": "{page}. lapa no {count}", + "previous_page": "Iepriekšējā lapa", + "next_page": "Nākamā lapa", + "picker_title": "Izvēlēties no kataloga", + "cancel": "Atcelt", + "selected_count": "Atlasīti: {count}", + "add_to": "Pievienot laukam Kam", + "add_cc": "Pievienot laukam Kopija", + "add_bcc": "Pievienot laukam Slēptā kopija", + "select_hint": "Atlasiet personu, lai redzētu informāciju", + "write_email": "Rakstīt e-pastu", + "add_to_contacts": "Pievienot kontaktiem", + "email": "E-pasts", + "read_only_note": "Kataloga ieraksti nāk no jūsu pasta servera, un tos šeit nevar rediģēt.", + "type": { + "individual": "Persona", + "group": "Grupa", + "resource": "Resurss", + "location": "Vieta", + "other": "Cits" + } + }, + "title": "Kontakti", + "search_placeholder": "Meklēt kontaktus...", + "create_new": "Jauns kontakts", + "no_category": "Bez kategorijas", + "category_added": "Kontakts pievienots {name}", + "category_added_plural": "{count} kontakti pievienoti {name}", + "empty_state": "Kontaktu vēl nav", + "empty_state_title": "Kontaktu vēl nav", + "empty_state_subtitle": "Izveidojiet pirmo kontaktu vai importējiet no vCard faila", + "empty_search": "Nav kontaktu, kas atbilstu vaicājumam", + "empty_search_hint": "Mēģiniet citu meklēšanas vaicājumu", + "empty_filtered": "Nav kontaktu, kas atbilstu filtriem", + "empty_filtered_hint": "Pielāgojiet vai notīriet filtrus", + "clear_search": "Notīrīt meklēšanu", + "import_vcard": "vCard imports", + "delete_confirm_title": "Dzēst kontaktu", + "delete_confirm": "Vai tiešām vēlaties dzēst šo kontaktu?", + "local_mode": "Kontakti tiek glabāti lokāli (serveris neatbalsta JMAP Contacts)", + "back_to_contacts": "Atpakaļ pie kontaktiem", + "back_to_email": "Atpakaļ pie e-pasta", + "tabs": { + "all": "Visi", + "groups": "Grupas" + }, + "shared": { + "title": "Koplietotie" + }, + "address_books": { + "create": "Jauna adrešu grāmata", + "created": "Adrešu grāmata izveidota", + "create_failed": "Neizdevās izveidot adrešu grāmatu", + "title": "Manas adrešu grāmatas", + "shared_prefix": "Koplietotā: {name}", + "moved": "Kontakts pārvietots uz {name}", + "moved_plural": "{count} kontakti pārvietoti uz {name}", + "move_failed": "Neizdevās pārvietot kontaktu", + "address_book": "Adrešu grāmata", + "rename": "Pārdēvēt adrešu grāmatu", + "name_label": "Adrešu grāmatas nosaukums", + "renamed": "Adrešu grāmata pārdēvēta", + "rename_failed": "Neizdevās pārdēvēt adrešu grāmatu", + "default": "Noklusējuma", + "set_default": "Iestatīt kā noklusējumu", + "default_updated": "Noklusējuma adrešu grāmata atjaunināta", + "set_default_failed": "Neizdevās iestatīt noklusējuma adrešu grāmatu", + "manage": "Pārvaldīt adrešu grāmatas", + "share": "Kopīgot adrešu grāmatu", + "new_contact_in_book": "Jauns kontakts šajā adrešu grāmatā", + "delete": "Dzēst adrešu grāmatu", + "confirm_delete": "Dzēst \"{name}\"? Visi kontakti šajā adrešu grāmatā tiks noņemti.", + "deleted": "Adrešu grāmata dzēsta", + "delete_failed": "Neizdevās dzēst adrešu grāmatu" + }, + "detail": { + "emails": "E-pasta adreses", + "phones": "Tālruņa numuri", + "organizations": "Organizācijas", + "addresses": "Adreses", + "notes": "Piezīmes", + "titles": "Amati un lomas", + "online_services": "Tiešsaistes pakalpojumi", + "anniversaries": "Datumi", + "personal_info": "Personīgā informācija", + "languages": "Valodas", + "categories": "Kategorijas", + "related_contacts": "Saistītie kontakti", + "crypto_keys": "Kriptogrāfijas atslēgas", + "cert_issuer": "Izdevējs", + "cert_expires": "Termiņš", + "cert_expired": "Beidzies", + "cert_fingerprint": "Nospiedums", + "cert_algorithm": "Algoritms", + "import_to_smime": "Importēt uz S/MIME", + "cert_already_imported": "Jau importēts S/MIME", + "cert_imported": "Sertifikāts importēts S/MIME krātuvē", + "cert_import_failed": "Neizdevās importēt sertifikātu", + "no_contact_selected": "Atlasiet kontaktu, lai skatītu informāciju", + "compose_email": "Rakstīt e-pastu", + "copy_email": "Kopēt e-pastu", + "copy_phone": "Kopēt tālruni", + "copy_url": "Kopēt URL", + "copied": "Nokopēts starpliktuvē", + "copy_failed": "Neizdevās nokopēt starpliktuvē", + "created": "Izveidots", + "updated": "Pēdējo reizi atjaunināts", + "timezone": "Laika josla", + "anniversary_birth": "Dzimšanas diena", + "anniversary_death": "Miršanas datums", + "anniversary_wedding": "Kāzu gadadiena", + "anniversary_other": "Cits", + "personal_expertise": "Ekspertīze", + "personal_hobby": "Hobijs", + "personal_interest": "Intereses", + "personal_other": "Cits", + "gender": "Dzimums", + "gender_masculine": "Vīrietis", + "gender_feminine": "Sieviete", + "gender_other": "Cits", + "gender_none": "Nav attiecināms", + "gender_unknown": "Nezināms", + "calendar": "Kalendārs", + "calendar_uri": "Kalendāra URL", + "scheduling_uri": "Plānošanas URL", + "freebusy_uri": "Pieejamības URL", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Citas darbības", + "age_years": "{count, plural, one {1 gads} other {# gadi}}", + "years_since": "{count, plural, one {pirms 1 gada} other {pirms # gadiem}}" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "Jauns kontakts", + "edit_title": "Rediģēt kontaktu", + "section_address_book": "Katalogs", + "select_address_book": "Izvēlieties katalogu...", + "section_identity": "Vārds un identitāte", + "contact_type": "Kontakta veids", + "type_person": "Persona", + "type_organization": "Organizācija", + "section_work": "Darbs un organizācija", + "prefix": "Prefikss", + "prefix_placeholder": "Dr., kungs, kundze", + "given_name": "Vārds", + "middle_name": "Otrais vārds", + "surname": "Uzvārds", + "suffix": "Sufikss", + "suffix_placeholder": "jr., sr.", + "nickname": "Segvārds", + "nickname_placeholder": "Pseidonīms", + "email": "E-pasts", + "email_placeholder": "lietotajs@piemers.lv", + "phone": "Tālrunis", + "phone_placeholder": "+371 23456789", + "phone_type": "Veids", + "phone_voice": "Balss", + "phone_cell": "Mobilais", + "phone_fax": "Fakss", + "phone_pager": "Peidžeris", + "phone_video": "Video", + "phone_text": "Teksts", + "organization": "Organizācija", + "organization_placeholder": "Uzņēmuma nosaukums", + "department": "Nodaļa", + "department_placeholder": "Nodaļa", + "job_title": "Amats", + "job_title_placeholder": "piem., programmatūras inženieris", + "role": "Loma", + "role_placeholder": "piem., komandas vadītājs", + "addresses": "Adreses", + "add_address": "Pievienot adresi", + "street": "Iela", + "city": "Pilsēta", + "region": "Reģions / novads", + "postcode": "Pasta indekss", + "country": "Valsts", + "online_services": "Tiešsaistes pakalpojumi", + "add_online_service": "Pievienot pakalpojumu", + "url_placeholder": "https://piemers.lv", + "service_placeholder": "Pakalpojums", + "anniversaries": "Datumi", + "add_anniversary": "Pievienot datumu", + "anniversary_birth": "Dzimšanas diena", + "anniversary_wedding": "Gadadiena", + "anniversary_death": "Piemiņas diena", + "anniversary_other": "Cits", + "personal_info": "Personīgā informācija", + "add_personal_info": "Pievienot ierakstu", + "personal_info_placeholder": "piem., Fotogrāfija", + "personal_expertise": "Ekspertīze", + "personal_hobby": "Hobijs", + "personal_interest": "Interese", + "personal_other": "Cits", + "level": "Līmenis", + "level_high": "Augsts", + "level_medium": "Vidējs", + "level_low": "Zems", + "categories": "Kategorijas", + "categories_placeholder": "piem., Ģimene, Draugi, Kolēģi", + "categories_hint": "Ievadiet, lai meklētu vai pievienotu", + "category_add": "Pievienot", + "note": "Piezīmes", + "note_placeholder": "Pievienojiet piezīmi...", + "gender": "Dzimums", + "gender_sex": "Bioloģiskais dzimums", + "gender_male": "Vīrietis", + "gender_female": "Sieviete", + "gender_other": "Cits", + "gender_none": "Nav attiecināms", + "gender_unknown": "Nezināms", + "gender_identity": "Dzimumidentitāte", + "gender_identity_placeholder": "Dzimumidentitāte...", + "calendar": "Kalendārs", + "calendar_uri": "Kalendāra URL", + "scheduling_uri": "Plānošanas URL", + "freebusy_uri": "Pieejamības URL", + "context_work": "Darba", + "context_private": "Privāts", + "add_email": "Pievienot e-pastu", + "add_phone": "Pievienot tālruni", + "save": "Saglabāt", + "cancel": "Atcelt", + "creating": "Izveido...", + "updating": "Atjaunina...", + "name_required": "Ievadiet vārdu, uzvārdu vai organizāciju", + "email_invalid": "Ievadiet derīgu e-pasta adresi", + "email_error_inline": "Nederīgs e-pasta formāts", + "save_failed": "Neizdevās saglabāt kontaktu", + "delete": "Dzēst", + "upload_photo": "Augšupielādēt foto", + "remove_photo": "Noņemt foto", + "photo_hint": "JPG vai PNG, līdz 10 MB. Tiks mainīts izmērs.", + "photo_too_large": "Attēls ir pārāk liels (maks. 10 MB)", + "photo_invalid": "Nederīgs attēla fails", + "change_photo": "Mainīt" + }, + "groups": { + "create": "Jauna grupa", + "edit": "Rediģēt grupu", + "empty": "Grupu vēl nav", + "delete_confirm_title": "Dzēst grupu", + "delete_confirm": "Vai tiešām vēlaties dzēst šo grupu?", + "name_label": "Grupas nosaukums", + "name_placeholder": "piem., Komanda, Ģimene", + "name_required": "Grupas nosaukums ir obligāts", + "save_failed": "Neizdevās saglabāt grupu", + "members_label": "Dalībnieki", + "search_members": "Meklēt kontaktus, ko pievienot...", + "no_members": "Šajā grupā nav dalībnieku", + "member_count": "{count, plural, =0 {Nav dalībnieku} one {1 dalībnieks} other {# dalībnieki}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Kontaktu imports", + "drop_hint": "Noklikšķiniet, lai izvēlētos vCard failu", + "file_types": ".vcf vai .vcard faili", + "no_contacts": "Failā netika atrasti kontakti", + "parse_error": "Neizdevās nolasīt vCard failu", + "found": "Atrasts {count, plural, one {1 kontakts} other {# kontakti}}", + "duplicate": "Dublikāts", + "select_all": "Atlasīt visus", + "deselect_all": "Noņemt atlasi", + "selected": "Atlasīts {count, plural, one {1 kontakts} other {# kontakti}}", + "import_button": "Importēt", + "importing": "Importē...", + "success": "Importēts {count, plural, one {1 kontakts} other {# kontakti}}", + "failed": "Imports neizdevās", + "close": "Aizvērt", + "file_too_large": "Fails ir pārāk liels (maks. 5 MB)" + }, + "export": { + "title": "Kontaktu eksports", + "success": "Eksportēts {count, plural, one {1 kontakts} other {# kontakti}}" + }, + "bulk": { + "selected": "Atlasīts {count, plural, one {1 kontakts} other {# kontakti}}", + "select_all": "Atlasīt visus", + "delete": "Dzēst", + "delete_confirm_title": "Dzēst kontaktus", + "delete_confirm": "Vai dzēst {count, plural, one {1 kontaktu} other {# kontaktus}}?", + "deleted": "Izdzēsts {count, plural, one {1 kontakts} other {# kontakti}}", + "add_to_group": "Pievienot grupai", + "choose_group": "Izvēlēties grupu", + "adding_contacts": "Pievieno {count, plural, one {1 kontaktu} other {# kontaktus}}", + "added_to_group": "Kontakti pievienoti grupai", + "export": "Eksportēt", + "clear": "Notīrīt atlasi" + }, + "toast": { + "created": "Kontakts izveidots", + "updated": "Kontakts atjaunināts", + "deleted": "Kontakts izdzēsts", + "error_create": "Neizdevās izveidot kontaktu", + "error_update": "Neizdevās atjaunināt kontaktu", + "error_delete": "Neizdevās izdzēst kontaktu" + }, + "rename_category": "Pārdēvēt kategoriju", + "category_name_label": "Kategorijas nosaukums", + "category_renamed": "Kategorija pārdēvēta", + "category_rename_failed": "Neizdevās pārdēvēt kategoriju", + "context_menu": { + "open": "Atvērt", + "edit": "Rediģēt", + "send_email": "Sūtīt e-pastu", + "add_to_group": "Pievienot grupai", + "export_vcard": "Eksportēt kā vCard", + "delete": "Dzēst", + "call": "Zvanīt", + "duplicate": "Dublēt", + "print": "Drukāt" + }, + "filters": { + "toggle": "Filtri", + "select": "Atlasīt", + "clear": "Notīrīt", + "close": "Aizvērt", + "title": "Paplašinātie filtri", + "organization": "Uzņēmums", + "organization_placeholder": "piem. Acme", + "job_title": "Amats", + "job_title_placeholder": "piem. Dizainers", + "location": "Atrašanās vieta", + "location_placeholder": "Pilsēta vai valsts", + "email_domain": "E-pasta domēns", + "email_domain_placeholder": "piemers.lv", + "birthday_month": "Dzimšanas diena", + "any_month": "Jebkurš mēnesis", + "has_email": "Ar e-pastu", + "has_phone": "Ar tālruni", + "has_photo": "Ar foto", + "sort_by": "Kārtot pēc", + "sort_first_name": "Vārds", + "sort_last_name": "Uzvārds" + }, + "open_categories": "Atvērt kategorijas" + }, + "calendar": { + "title": "Kalendārs", + "back_to_email": "Atgriezties pie pasta", + "back_to_month": "Atpakaļ uz mēneša skatu", + "my_calendars": "Kalendāri", + "mini_calendar_change": "Noklikšķiniet, lai mainītu mēnesi", + "views": { + "month": "Mēnesis", + "week": "Nedēļa", + "day": "Diena", + "agenda": "Darba kārtība", + "today": "Šodien", + "month_hint": "Mēnesis (m)", + "week_hint": "Nedēļa (w)", + "day_hint": "Diena (d)", + "agenda_hint": "Darba kārtība (a)", + "tasks": "Uzdevumi", + "tasks_hint": "Uzdevumi (k)" + }, + "events": { + "hidden_before": "{count, plural, zero {# agrāku notikumu} one {# agrāks notikums} other {# agrāki notikumi}}", + "hidden_after": "{count, plural, zero {# vēlāku notikumu} one {# vēlāks notikums} other {# vēlāki notikumi}}", + "show_all_hours": "Rādīt visas stundas", + "show_display_hours": "Rādīt tikai {start} – {end}", + "create": "Izveidot pasākumu", + "edit": "Rediģēt pasākumu", + "delete": "Dzēst pasākumu", + "details": "Pasākuma informācija", + "no_events": "Nav pasākumu", + "all_day": "Visa diena", + "more": "vēl {count}", + "show_more": "Rādīt vairāk", + "show_less": "Rādīt mazāk", + "no_title": "(Bez virsraksta)", + "resize": "Mainīt pasākuma laiku", + "duplicate": "Dublēt", + "today_header": "Šodien", + "tomorrow_header": "Rīt", + "agenda_show_earlier": "Rādīt agrākos notikumus", + "agenda_loading": "Ielādē…", + "agenda_range_start": "Notikumi no {date}", + "agenda_range_end": "Notikumi līdz {date}", + "export_ics": "Eksportēt kā .ics", + "copy_title": "Kopēt nosaukumu", + "copy_link": "Kopēt sapulces saiti", + "go_to_today": "Pāriet uz šodienu", + "new_all_day_event": "Jauns visas dienas notikums", + "new_event": "Jauns notikums", + "new_task": "Jauns uzdevums" + }, + "detail": { + "add_note": "Pievienot piezīmi...", + "save_note": "Saglabāt", + "note_saved": "Piezīme pievienota", + "open_link": "Atvērt saiti", + "copy_location": "Kopēt vietu", + "meeting_link": "Sapulces saite", + "tentative": "Pagaidām", + "cancelled": "Atcelts", + "delete_confirm": "Vai dzēst šo pasākumu?" + }, + "form": { + "title": "Nosaukums", + "description": "Apraksts", + "location": "Vieta", + "meeting_link": "Sapulces saite", + "start_date": "Sākuma datums", + "end_date": "Beigu datums", + "start_time": "Sākuma laiks", + "end_time": "Beigu laiks", + "all_day_event": "Visas dienas pasākums", + "calendar_select": "Kalendārs", + "save": "Saglabāt", + "cancel": "Atcelt", + "delete_confirm": "Vai tiešām vēlaties dzēst šo pasākumu?", + "color": "Krāsa" + }, + "participants": { + "availability": { + "title": "Pieejamība", + "free": "Pieejams", + "busy": "Aizņemts", + "tentative": "Provizoriski", + "unknown": "Nav šī servera lietotājs", + "checking": "Pārbauda…" + }, + "title": "Dalībnieki", + "add": "Pievienot dalībnieku", + "organizer": "Organizators", + "attendee": "Dalībnieks", + "accepted": "Pieņemts", + "declined": "Noraidīts", + "tentative": "Pagaidām", + "needs_action": "Nepieciešama atbilde", + "remove": "Noņemt", + "edit": "Rediģēt", + "email_placeholder": "Pievienojiet adresi vai kontaktu", + "send_invitations": "Sūtīt uzaicinājumus dalībniekiem", + "status_summary": "{accepted} pieņēmuši, {pending} gaida", + "invited_by": "Uzaicināja {name}", + "respond_below": "Atbildiet, izmantojot pogas zemāk", + "rsvp_label": "Jūsu atbilde", + "cancel_notification": "Dalībnieki tiks informēti par atcelšanu", + "you_organizer": "Jūs esat organizators", + "you_attendee": "Jūs esat dalībnieks", + "no_participants": "Nav dalībnieku", + "count": "{count, plural, one {# dalībnieks} other {# dalībnieki}}" + }, + "recurrence": { + "title": "Atkārtošana", + "none": "Neatkārtojas", + "daily": "Katru dienu", + "weekly": "Katru nedēļu", + "monthly": "Katru mēnesi", + "yearly": "Katru gadu", + "every_n_days": "Ik pēc {count} dienām", + "every_n_weeks": "Ik pēc {count} nedēļām", + "every_n_months": "Ik pēc {count} mēnešiem", + "until": "Līdz", + "occurrences": "{count} reizes", + "custom": "Pielāgots…", + "edit_custom": "Rediģēt pielāgotu atkārtošanos", + "every_n_years": "Ik pēc {count} gadiem", + "on_days": "šajās dienās: {days}", + "on_day_n": "mēneša {day}. dienā", + "on_the_nth": "{nth} {day}", + "in_month": "{month} mēnesī", + "nth_1": "pirmajā", + "nth_2": "otrajā", + "nth_3": "trešajā", + "nth_4": "ceturtajā", + "nth_last": "pēdējā", + "editor_freq_day": "Diena", + "editor_freq_week": "Nedēļa", + "editor_freq_month": "Mēnesis", + "editor_freq_year": "Gads", + "editor_repeats_on": "Atkārtojas", + "editor_every": "Ik pēc", + "editor_unit_days": "dienām", + "editor_unit_weeks": "nedēļām", + "editor_unit_months": "mēnešiem", + "editor_unit_years": "gadiem", + "editor_on_day": "mēneša dienā", + "editor_on_the": "šajā:", + "editor_in": "mēnesī", + "editor_ends": "Beidzas", + "editor_never": "Nekad", + "editor_ends_on": "Datumā", + "editor_ends_after": "Pēc", + "editor_occurrences": "reizēm" + }, + "recurrence_scope": { + "edit_title": "Rediģēt atkārtotu pasākumu", + "delete_title": "Dzēst atkārtotu pasākumu", + "description": "Šis ir atkārtots pasākums. Kuras instances vēlaties mainīt?", + "this_event": "Tikai šo pasākumu", + "this_and_future": "Šo un turpmākos", + "all_events": "Visus pasākumus", + "cancel": "Atcelt", + "save": "Saglabāt", + "delete": "Dzēst", + "rsvp_title": "Atbildēt uz atkārtotu pasākumu", + "rsvp_description": "Šis ir atkārtots pasākums. Uz kurām instancēm attiecas jūsu atbilde?", + "respond": "Atbildēt" + }, + "alerts": { + "title": "Atgādinājums", + "none": "Bez atgādinājuma", + "at_time": "Pasākuma laikā", + "minutes_before": "Pirms {count} minūtes", + "hours_before": "Pirms {count} stundas", + "days_before": "Pirms {count} dienas", + "weeks_before": "Pirms {count} nedēļām", + "unit_minutes_before": "minūtes pirms", + "unit_hours_before": "stundas pirms", + "unit_days_before": "dienas pirms", + "unit_weeks_before": "nedēļas pirms", + "add": "Pievienot atgādinājumu", + "remove": "Noņemt atgādinājumu", + "amount": "Atgādinājuma daudzums", + "unit": "Atgādinājuma vienība", + "action": "Atgādinājuma veids", + "action_display": "Paziņojums", + "action_email": "E-pasts", + "by_email": "{label} pa e-pastu", + "kept": "{count, plural, zero {Tiek saglabāti vēl # atgādinājumi, kurus šeit nevar rediģēt} one {Tiek saglabāts vēl # atgādinājums, kuru šeit nevar rediģēt} other {Tiek saglabāti vēl # atgādinājumi, kurus šeit nevar rediģēt}}", + "other": "{count, plural, zero {Vēl # atgādinājumi} one {Vēl # atgādinājums} other {Vēl # atgādinājumi}}", + "calendar_defaults": "Kalendāra noklusējuma atgādinājumi", + "customize": "Pielāgot", + "use_defaults": "Izmantot kalendāra noklusējuma atgādinājumus" + }, + "settings": { + "limit_hours": "Ierobežot redzamās stundas", + "limit_hours_desc": "Dienas un nedēļas skatā rādīt tikai šīs stundas. Notikumi ārpus tām ir pieejami ar bultiņām katras dienas augšā un apakšā.", + "visible_hours": "Redzamās stundas", + "visible_hours_start": "Redzamo stundu sākums", + "visible_hours_end": "Redzamo stundu beigas", + "hide_non_working_days": "Slēpt brīvdienas", + "hide_non_working_days_desc": "Izlaist no nedēļas skata dienas, kurās nestrādājat. To notikumi joprojām redzami mēneša, dienas un darba kārtības skatā.", + "working_days": "Darba dienas", + "title": "Kalendāra iestatījumi", + "default_view": "Noklusējuma skats", + "week_starts_on": "Nedēļa sākas ar", + "time_format": "Laika formāts", + "default_calendar": "Noklusējuma kalendārs", + "default_reminder": "Noklusējuma atgādinājums", + "time_format_12h": "12 stundu", + "time_format_24h": "24 stundu", + "notifications_enabled": "Pasākumu paziņojumi", + "notifications_enabled_desc": "Rādīt brīdinājumus par gaidāmajiem pasākumiem", + "notification_sound": "Paziņojuma skaņa", + "notification_sound_desc": "Atskaņot skaņu kalendāra brīdinājumiem", + "invitation_parsing": "Uzaicinājumu atpazīšana e-pastā", + "invitation_parsing_desc": "Meklēt kalendāra uzaicinājumus e-pasta pielikumos", + "show_time_in_month_view": "Rādīt laiku mēneša skatā", + "show_time_in_month_view_desc": "Rādīt pasākuma laiku mēneša kalendārā", + "show_week_numbers": "Rādīt nedēļu numurus", + "show_week_numbers_desc": "Rādīt nedēļu numurus mini kalendārā", + "calendar_free_scroll": "Brīva ritināšana", + "calendar_free_scroll_desc": "Nepārtraukti ritināt cauri mēnešiem, nedēļām un dienām, nevis pa vienam periodam", + "enable_tasks": "Iespējot uzdevumus", + "enable_tasks_desc": "Rādīt uzdevumu sadaļu kalendārā", + "show_tasks_on_calendar": "Rādīt uzdevumus kalendārā", + "show_tasks_on_calendar_desc": "Rādīt uzdevumus mēneša, nedēļas un dienas skatā", + "hover_preview": "Priekšskatījums uzvirzot", + "hover_preview_desc": "Rādīt informāciju, uzvirzot kursoru uz pasākuma", + "hover_preview_instant": "Uzreiz", + "hover_preview_delay_500ms": "0,5 sekundes aizture", + "hover_preview_delay_1s": "1 sekundes aizture", + "hover_preview_delay_2s": "2 sekunžu aizture", + "hover_preview_off": "Izslēgts", + "organizer_identity": "Organizēt ielūgumus kā", + "organizer_identity_desc": "Kalendāra adrese, ko izmanto kā organizatoru, uzaicinot cilvēkus uz notikumiem.", + "organizer_identity_failed": "Neizdevās mainīt organizatora adresi", + "show_birthday_calendar": "Kontaktu dzimšanas dienu kalendārs", + "show_birthday_calendar_desc": "Rādīt virtuālu kalendāru ar kontaktu dzimšanas dienām" + }, + "days": { + "monday": "Pirmdiena", + "tuesday": "Otrdiena", + "wednesday": "Trešdiena", + "thursday": "Ceturtdiena", + "friday": "Piektdiena", + "saturday": "Sestdiena", + "sunday": "Svētdiena", + "mon": "Pr", + "tue": "Ot", + "wed": "Tr", + "thu": "Ce", + "fri": "Pk", + "sat": "Se", + "sun": "Sv" + }, + "notifications": { + "event_created": "Pasākums izveidots", + "event_updated": "Pasākums atjaunināts", + "event_deleted": "Pasākums izdzēsts", + "calendar_created": "Kalendārs izveidots", + "calendar_deleted": "Kalendārs izdzēsts", + "event_move_error": "Neizdevās pārvietot pasākumu", + "event_resize_error": "Neizdevās mainīt pasākuma laiku", + "alert_title": "Gaidāms pasākums", + "alert_now": "Sākas tagad", + "alert_in_minutes": "Pēc {count} min", + "invitation_sent": "Uzaicinājumi nosūtīti", + "invitations_denied_title": "Ielūgumus nevar nosūtīt", + "invitations_denied": "Serveris atteicās nosūtīt ielūgumus: {reason}. Saglabāt notikumu, tos nenosūtot?", + "save_without_invitations": "Saglabāt bez ielūgumiem", + "rsvp_updated": "Atbilde atjaunināta", + "rsvp_error": "Neizdevās atjaunināt atbildi", + "event_duplicated": "Pasākums dublēts", + "event_error": "Neizdevās saglabāt pasākumu", + "task_due": "Uzdevuma termiņš", + "event_exported": "Notikums eksportēts", + "title_copied": "Nosaukums nokopēts", + "link_copied": "Saite nokopēta" + }, + "status": { + "loading_calendars": "Ielādē kalendārus...", + "loading_events": "Ielādē pasākumus..." + }, + "quick_create": { + "placeholder": "Jauna pasākuma nosaukums", + "aria_label": "Ātra pasākuma izveide", + "add_title": "Pievienot nosaukumu", + "more_options": "Vairāk opciju" + }, + "nav_prev": "Iepriekšējais", + "nav_next": "Nākamais", + "import": { + "title": "Kalendāra imports", + "tab_file": "Fails", + "tab_url": "URL adrese", + "select_file": "Izvēlieties .ics failu", + "drop_file": "vai velciet failu šeit", + "supported_formats": "Atbalsta iCalendar (.ics) failus", + "url_description": "Ievadiet ārējas iCalendar plūsmas URL.", + "url_placeholder": "https://piemers.lv/kalendars.ics", + "url_hint": "Atbalsta iCalendar (.ics) saites", + "fetch": "Iegūt", + "invalid_url": "Ievadiet derīgu URL", + "url_fetch_failed": "Neizdevās iegūt kalendāru no saites", + "parsing": "Nolasa kalendāra failu...", + "parsed_events": "Atrasti {count} pasākumi", + "no_events": "Failā netika atrasti pasākumi", + "select_all": "Atlasīt visus", + "deselect_all": "Noņemt atlasi", + "target_calendar": "Importēt kalendārā", + "import_button": "Importēt atlasītos", + "importing": "Importē pasākumus...", + "success": "{count} pasākumi veiksmīgi importēti", + "error": "Neizdevās importēt kalendāru", + "file_too_large": "Fails pārsniedz 10 MB limitu", + "invalid_format": "Nederīgs kalendāra faila formāts" + }, + "webcal_action": { + "title": "Atvērt kalendāra saiti", + "description": "Kā vēlaties izmantot \"{name}\"?", + "import_title": "Importēt vienreiz", + "import_description": "Ielādēt notikumus tagad un kopēt tos vienā no jūsu kalendāriem.", + "subscribe_title": "Abonēt", + "subscribe_description": "Automātiski sinhronizēt šo kalendāru kā atsevišķu kalendāru.", + "cancel": "Atcelt" + }, + "management": { + "title": "Kalendāru pārvaldība", + "description": "Izveidojiet, pārdēvējiet un konfigurējiet kalendārus. Ar labo klikšķi varat mainīt kalendāra krāsu.", + "name": "Nosaukums", + "name_placeholder": "Kalendāra nosaukums", + "color": "Krāsa", + "change_color": "Mainīt krāsu", + "random_color": "Jauna nejauša krāsa", + "add_calendar": "Pievienot kalendāru", + "edit": "Rediģēt", + "delete": "Dzēst", + "save": "Saglabāt", + "create": "Izveidot", + "cancel": "Atcelt", + "default": "Noklusējuma", + "confirm_delete": "Dzēst «{name}»? Visi pasākumi šajā kalendārā tiks dzēsti.", + "confirm_clear": "Iztīrīt visus pasākumus no «{name}»? Šo darbību nevar atcelt.", + "set_default": "Iestatīt kā noklusējumu", + "default_updated": "Noklusējuma kalendārs atjaunināts", + "error_default": "Neizdevās iestatīt noklusējuma kalendāru", + "clear_events": "Iztīrīt pasākumus", + "events_cleared": "Iztīrīti {count} pasākumi", + "error_clear": "Neizdevās iztīrīt kalendāra pasākumus", + "calendar_created": "Kalendārs izveidots", + "calendar_updated": "Kalendārs atjaunināts", + "calendar_deleted": "Kalendārs izdzēsts", + "color_updated": "Kalendāra krāsa atjaunināta", + "error_create": "Neizdevās izveidot kalendāru", + "error_update": "Neizdevās atjaunināt kalendāru", + "error_delete": "Neizdevās izdzēst kalendāru", + "caldav_url": "CalDAV URL", + "copy_url": "Kopēt CalDAV URL", + "url_copied": "CalDAV URL nokopēts starpliktuvē", + "share": "Kopīgot kalendāru", + "new_event_in_calendar": "Jauns notikums šajā kalendārā", + "shared_with": "Kopīgots ar: {names}", + "shared_with_count": "{count, plural, zero {Kopīgots ar # personām} one {Kopīgots ar # personu} other {Kopīgots ar # personām}}", + "move_up": "Pārvietot uz augšu", + "move_down": "Pārvietot uz leju", + "kind": "Saturs", + "kind_events": "Notikumi", + "kind_tasks": "Uzdevumi", + "kind_both": "Notikumi un uzdevumi", + "kind_hint": "Nosaka, kuras lietotnes redz šo kalendāru, kad tas tiek sinhronizēts caur CalDAV. Vēlāk to nevar mainīt.", + "default_alerts": "Noklusējuma atgādinājumi", + "default_alerts_title": "Noklusējuma atgādinājumi kalendāram «{name}»", + "default_alerts_hint": "Jauni pasākumi šajā kalendārā saņem šos atgādinājumus, ja vien tie netiek mainīti pasākumā. Izmaiņas šeit neietekmē esošos pasākumus. E-pasta atgādinājumus sūta pasta serveris.", + "default_alerts_timed": "Pasākumi ar laiku", + "default_alerts_all_day": "Visas dienas pasākumi", + "default_alerts_updated": "Noklusējuma atgādinājumi atjaunināti" + }, + "subscription": { + "title": "iCal abonements", + "section_title": "iCal abonementi", + "description": "Abonējiet ārēju iCalendar plūsmu. Pasākumi tiks sinhronizēti automātiski.", + "url_label": "Kalendāra URL", + "url_placeholder": "https://example.com/calendar.ics vai webcal://...", + "name_label": "Kalendāra nosaukums", + "name_placeholder": "piem., Svētku dienas", + "color_label": "Krāsa", + "refresh_interval": "Atjaunināšanas intervāls", + "interval_15": "Ik pēc 15 minūtēm", + "interval_30": "Ik pēc 30 minūtēm", + "interval_60": "Ik pēc stundas", + "interval_360": "Ik pēc 6 stundām", + "interval_1440": "Katru dienu", + "subscribe": "Abonēt", + "subscribing": "Abonē...", + "invalid_url": "Ievadiet derīgu URL", + "success": "Pieteikts abonements «{name}»", + "error": "Neizdevās pievienot abonementu", + "refresh": "Atjaunināt tūlīt", + "refresh_success": "Abonements atjaunināts", + "refresh_error": "Neizdevās atjaunināt abonementu", + "unsubscribe": "Atteikties", + "confirm_delete": "Atteikties no «{name}»? Kalendārs un tā pasākumi tiks dzēsti.", + "deleted": "Abonements dzēsts", + "delete_error": "Neizdevās dzēst abonementu", + "last_refreshed": "Pēdējo reizi atjaunināts: {time}", + "save": "Saglabāt izmaiņas", + "saving": "Saglabā...", + "edit": "Rediģēt", + "edit_title": "Rediģēt abonementu", + "updated": "Atjaunināts \"{name}\"", + "update_error": "Neizdevās atjaunināt abonementu" + }, + "tasks": { + "label": "Uzdevumi", + "no_tasks": "Nav uzdevumu", + "no_title": "(Bez nosaukuma)", + "mark_complete": "Atzīmēt kā izpildītu", + "mark_incomplete": "Atzīmēt kā neizpildītu", + "filter_all": "Visi", + "filter_pending": "Gaidāmie", + "filter_completed": "Pabeigtie", + "filter_overdue": "Kavētie", + "show_completed": "Rādīt pabeigtos", + "create": "Jauns uzdevums", + "edit": "Rediģēt uzdevumu", + "title_placeholder": "Uzdevuma nosaukums", + "description_placeholder": "Pievienojiet aprakstu...", + "due_date": "Izpildes termiņš", + "include_time": "Iekļaut laiku", + "priority": "Prioritāte", + "priority_none": "Nav", + "priority_high": "Augsta", + "priority_medium": "Vidēja", + "priority_low": "Zema", + "progress": "Statuss", + "progress_needs_action": "Jārīkojas", + "progress_in_process": "Procesā", + "progress_completed": "Pabeigts", + "progress_cancelled": "Atcelts", + "calendar": "Kalendārs", + "alert": "Atgādinājums", + "alert_none": "Nav", + "alert_at_time": "Termiņa brīdī", + "alert_5min": "Pirms 5 minūtēm", + "alert_15min": "Pirms 15 minūtēm", + "alert_30min": "Pirms 30 minūtēm", + "alert_1hr": "Pirms 1 stundas", + "alert_1day": "Pirms 1 dienas", + "delete": "Dzēst", + "cancel": "Atcelt", + "save": "Saglabāt", + "quick_add_placeholder": "Pievienot uzdevumu...", + "due_today": "Šodien", + "due_tomorrow": "Rīt", + "overdue": "Kavēts" + }, + "birthday_calendar": "Dzimšanas dienas", + "months": { + "jan": "janv.", + "feb": "febr.", + "mar": "marts", + "apr": "apr.", + "may": "maijs", + "jun": "jūn.", + "jul": "jūl.", + "aug": "aug.", + "sep": "sept.", + "oct": "okt.", + "nov": "nov.", + "dec": "dec.", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Atvērt izvēlni", + "toolbar": { + "more": "Citas darbības", + "select_view": "Mainīt skatu, pašlaik {view}" + }, + "other_calendars": "Citi kalendāri", + "calendar_options": "{name} opcijas" + }, + "advanced_search": { + "title": "Izvērstā meklēšana", + "from": "No", + "from_placeholder": "Sūtītāja e-pasts vai vārds", + "to": "Kam", + "to_placeholder": "Saņēmēja e-pasts vai vārds", + "subject": "Temats", + "subject_placeholder": "Temats satur...", + "body": "Teksts", + "body_placeholder": "Teksts satur...", + "folder": "Mape", + "all_folders": "Visas mapes", + "all_folders_except_spam_trash": "Visas mapes, izņemot mēstules un atkritnes", + "has_attachment": "Ar pielikumu", + "date_after": "Pēc", + "date_before": "Pirms", + "size_min": "Lielāks par (KB)", + "size_max": "Mazāks par (KB)", + "starred": "Ar zvaigznīti", + "unread": "Nelasītas", + "read": "Izlasītas", + "yes": "Jā", + "no": "Nē", + "clear": "Notīrīt", + "clear_all": "Notīrīt visu", + "filters_active": "{count} filtrs", + "filters_active_plural": "{count} filtri", + "toggle_filters": "Vairāk", + "search_hint": "Izmantojiet filtrus precīzākai meklēšanai", + "advanced_filters_tooltip": "Izvērstie meklēšanas filtri", + "results_found": "{count, plural, =0 {Nekas netika atrasts} one {# rezultāts} other {# rezultāti}}", + "results_found_more": "Atrasti {count}+ rezultāti", + "suggestions_recent": "Nesenie meklējumi", + "suggestions_people": "Personas", + "suggestions_from": "Meklēt e-pastus no {address}", + "suggestions_to": "Meklēt e-pastus adresātam {address}", + "suggestions_remove_recent": "Noņemt no nesenajiem meklējumiem", + "has_words": "Satur vārdus", + "size": "Izmērs", + "date_within": "Datums", + "search_in": "Meklēt", + "has_attachment_check": "Ar pielikumu", + "search": "Meklēt" + }, + "welcome": { + "title": "Laipni lūdzam jūsu pastkastē", + "tip_compose": "Nospiediet c, lai rakstītu jaunu vēstuli", + "tip_shortcuts": "Nospiediet ?, lai skatītu visus īsinājumtaustiņus", + "tip_sidebar": "Kalendāru, Kontaktus un Iestatījumus atradīsiet sānu joslā", + "tip_settings": "Pielāgojiet saskarni sadaļā «Iestatījumi»", + "tip_compose_touch": "Pieskarieties zīmuļa pogai, lai rakstītu jaunu e-pastu", + "tip_sidebar_touch": "Kalendārs, Kontakti un Iestatījumi ir apakšējā joslā", + "got_it": "Skaidrs", + "settings": "Iestatījumi", + "dismiss": "Aizvērt", + "start_tour": "Sākt ekskursiju" + }, + "demo_welcome": { + "title": "Laipni lūdzam Bulwark Mail", + "description": "Atklājiet pilnvērtīgu tīmekļa e-pasta klientu tieši pārlūkā. Visi dati tiek glabāti jūsu ierīcē.", + "feature_email": "Vēstuļu lasīšana un rakstīšana", + "feature_organize": "Etiķetes, zvaigznītes un mapes", + "feature_shortcuts": "Īsinājumtaustiņi", + "feature_privacy": "100% privāts demo režīms", + "hint": "Noklikšķiniet uz jebkuru vēstuli kreisajā pusē, lai sāktu." + }, + "files": { + "office_edit": "Rediģēt dokumentu", + "office_loading": "Atver dokumentu redaktoru…", + "office_error": "Neizdevās atvērt dokumentu redaktoru.", + "office_close": "Aizvērt redaktoru", + "open_in_new_tab": "Atvērt jaunā cilnē", + "title": "Faili", + "search_placeholder": "Meklēt failus...", + "empty_state_title": "Failu vēl nav", + "upload_files": "Augšupielādēt failus", + "new_folder": "Jauna mape", + "new_folder_name": "Mapes nosaukums", + "rename": "Pārdēvēt", + "rename_title": "Pārdēvēt", + "new_name": "Jauns nosaukums", + "name_forbidden_chars": "Serveris neatļauj šīs rakstzīmes nosaukumos: {chars}", + "name_reserved": "Šis nosaukums serverī ir rezervēts.", + "delete": "Dzēst", + "delete_confirm_title": "Dzēst resursu", + "delete_confirm_message": "Vai tiešām vēlaties dzēst «{name}»? Šo darbību nevar atcelt.", + "download": "Lejupielādēt", + "name": "Nosaukums", + "size": "Izmērs", + "modified": "Modificēts", + "type": "Veids", + "folder": "Mape", + "file": "Fails", + "uploading": "Augšupielādē...", + "upload_success": "Augšupielādēts {count, plural, one {1 fails} other {# faili}}", + "upload_canceled": "Augšupielāde atcelta", + "upload_canceled_partial": "{count, plural, zero {# faili tika augšupielādēti} one {# fails tika augšupielādēts} other {# faili tika augšupielādēti}} pirms atcelšanas", + "upload_error": "Neizdevās augšupielādēt failu", + "create_folder_success": "Mape izveidota", + "create_folder_error": "Neizdevās izveidot mapi", + "delete_success": "Sekmīgi izdzēsts", + "delete_error": "Neizdevās izdzēst", + "rename_success": "Sekmīgi pārdēvēts", + "rename_error": "Neizdevās pārdēvēt", + "download_error": "Neizdevās lejupielādēt", + "not_available": "Failu krātuve šajā serverī nav pieejama", + "cancel": "Atcelt", + "create": "Izveidot", + "save": "Saglabāt", + "no_results": "Nav failu, kas atbilstu vaicājumam", + "batch_delete_confirm_message": "Vai tiešām vēlaties dzēst {count, plural, one {1 elementu} other {# elementus}}?", + "batch_delete_success": "Izdzēsts {count, plural, one {1 elements} other {# elementi}}", + "grid_view": "Režģis", + "list_view": "Saraksts", + "details": "Sīkāka informācija", + "path": "Ceļš", + "preview": "Priekšskatījums", + "preview_error": "Neizdevās ielādēt priekšskatījumu", + "cut": "Izgriezt", + "copy": "Kopēt", + "paste": "Ielīmēt", + "move_success": "Pārvietots {count, plural, one {1 elements} other {# elementi}}", + "move_error": "Neizdevās pārvietot", + "paste_success": "Ielīmēts sekmīgi", + "paste_error": "Neizdevās ielīmēt", + "new_text_file": "Jauns teksta fails", + "file_name": "Faila nosaukums", + "retry": "Mēģināt vēlreiz", + "refresh": "Atsvaidzināt", + "duplicate": "Dublēt", + "duplicate_success": "Dublēšana pabeigta", + "duplicate_error": "Neizdevās dublēt", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Fails izveidots", + "create_file_error": "Neizdevās izveidot failu", + "favorites": "Izlase", + "recent": "Nesenie", + "show_in_folder": "Rādīt mapē", + "remove_from_recent": "Noņemt no nesenajiem", + "upload_folder": "Augšupielādēt mapi", + "file_too_large": "«{name}» pārsniedz maksimālo faila izmēru ({max})", + "undo": "Atsaukt", + "undo_success": "Darbība atsaukta", + "undo_error": "Neizdevās atsaukt", + "toolbar": "Failu rīkjosla", + "file_list": "Failu un mapju saraksts", + "context_menu": "Darbības", + "settings_ascending": "Augošā", + "settings_descending": "Dilstošā", + "disabled_title": "Failu funkciju ir atspējojis administrators", + "disabled_description": "Lielu failu augšupielāde, izmantojot WebDAV, var radīt Stalwart/RocksDB nestabilitāti, tostarp atmiņas izsīkumu un neatkopjamu diska izmantojumu. Dzēstie faili var netikt nekavējoties izņemti no blob glabātuves. Šī funkcija nav ieteicama produkcijas vidēm.", + "no_accounts": "Nav pievienotu kontu.", + "open_folder_tree": "Atvērt mapju koku", + "migration_title": "Notiek jūsu failu atjaunināšana…", + "migration_description": "Mapes un faili tiek sakārtoti pareizajā struktūrā. Tas notiek tikai vienu reizi.", + "new": "Jauns", + "my_files": "Mani faili", + "folders": "Mapes", + "files_heading": "Faili", + "open": "Atvērt", + "star": "Atzīmēt ar zvaigznīti", + "unstar": "Noņemt zvaigznīti", + "expand_folder": "Izvērst {name}", + "collapse_folder": "Sakļaut {name}", + "sidebar_hint": "Atzīmējiet mapes ar zvaigznīti un atveriet failus, lai tos atrastu šeit.", + "breadcrumbs": "Mapes ceļš", + "show_path": "Rādīt ceļā paslēptās mapes", + "selection_actions": "Darbības ar atlasītajiem elementiem", + "selected_count": "{count, plural, zero {Atlasīti #} one {Atlasīts #} other {Atlasīti #}}", + "clear_selection": "Notīrīt atlasi", + "select_all": "Atlasīt visu", + "select_item": "Atlasīt {name}", + "more_actions": "Citas darbības: {name}", + "paste_here": "Ielīmēt šajā mapē", + "paste_count": "{count, plural, zero {Ielīmēt # elementus} one {Ielīmēt # elementu} other {Ielīmēt # elementus}}", + "sort_by": "Kārtot pēc", + "view_mode": "Izkārtojums", + "clear_search": "Notīrīt meklēšanu", + "no_results_for": "Šajā mapē nekas neatbilst «{query}»", + "download_count": "{count, plural, zero {Lejupielādēt # failus} one {Lejupielādēt # failu} other {Lejupielādēt # failus}}", + "delete_count": "{count, plural, zero {Dzēst # elementus} one {Dzēst # elementu} other {Dzēst # elementus}}", + "close_details": "Aizvērt sīkāku informāciju", + "file_kind": "{ext} fails", + "folder_summary": "{folders, plural, =0 {Nav mapju} zero {# mapes} one {# mape} other {# mapes}}, {files, plural, =0 {nav failu} zero {# faili} one {# fails} other {# faili}}", + "details_hint": "Atlasiet failu vai mapi, lai skatītu sīkāku informāciju.", + "sharing": "Kopīgošana", + "shared_with_count": "{count, plural, zero {Kopīgots ar # personām} one {Kopīgots ar # personu} other {Kopīgots ar # personām}}", + "only_you": "Tikai jūs", + "empty_folder_title": "Šī mape ir tukša", + "empty_folder_description": "Nometiet failus šeit vai augšupielādējiet tos no savas ierīces.", + "empty_folder_read_only": "Šai mapei nevar pievienot failus.", + "choose_account": "Izvēlieties kontu", + "drop_to_upload": "Nometiet, lai augšupielādētu mapē {folder}", + "uploading_count": "Augšupielādē {current} no {total}", + "dismiss": "Aizvērt", + "zoom_in": "Tuvināt", + "zoom_out": "Tālināt", + "zoom_reset": "Ietilpināt ekrānā", + "rotate": "Pagriezt", + "close_preview": "Aizvērt" + }, + "smime": { + "your_certificates": "Jūsu sertifikāti", + "your_certificates_desc": "Importējiet un pārvaldiet savus S/MIME sertifikātus parakstīšanai un šifrēšanai", + "recipient_certificates": "Saņēmēju sertifikāti", + "recipient_certificates_desc": "Publiskie sertifikāti saņēmēju vēstuļu šifrēšanai", + "identity_bindings": "Atslēgu piesaiste identitātēm", + "identity_bindings_desc": "Piesaistiet S/MIME sertifikātus savām pasta identitātēm", + "defaults_title": "Noklusējuma parametri", + "defaults_desc": "Iestatiet noklusējuma parakstīšanas un šifrēšanas darbības", + "import_pkcs12": "Importēt PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importēt sertifikātu", + "no_certificates": "Sertifikāti vēl nav importēti", + "no_recipient_certs": "Nav saņēmēju sertifikātu", + "expires": "Termiņš", + "expired": "Beidzies", + "bound_to": "Piesaistīts", + "no_key_bound": "Nav", + "lock": "Bloķēt atslēgu", + "unlock": "Atbloķēt atslēgu", + "details": "Rādīt sīkāku informāciju", + "delete": "Dzēst", + "encrypt_by_default": "Šifrēt pēc noklusējuma", + "encrypt_by_default_desc": "Automātiski šifrēt, ja visiem saņēmējiem ir sertifikāti", + "remember_unlocked": "Atcerēties atbloķētās atslēgas", + "remember_unlocked_desc": "Atstāt atslēgas atbloķētas šīs sesijas laikā", + "sign_default_for": "Parakstīt pēc noklusējuma", + "enter_p12_passphrase": "Ievadiet PKCS#12 paroli", + "p12_passphrase_desc": "Ievadiet paroli, kas aizsargā šo sertifikāta failu", + "enter_storage_passphrase": "Iestatiet krātuves paroli", + "storage_passphrase_desc": "Izvēlieties paroli atslēgas aizsardzībai pārlūkā", + "next": "Tālāk", + "import": "Importēt", + "unlock_key": "Atbloķēt atslēgu", + "unlock_key_desc": "Ievadiet krātuves paroli, lai atbloķētu atslēgu", + "passphrase_placeholder": "Ievadiet paroli", + "confirm_passphrase_placeholder": "Apstipriniet paroli", + "passphrase_mismatch": "Paroles nesakrīt", + "cancel": "Atcelt", + "processing": "Apstrādā…", + "close": "Aizvērt", + "certificate_details": "Sertifikāta dati", + "cert_subject": "Subjekts", + "cert_issuer": "Izdevējs", + "cert_email": "E-pasts", + "cert_serial": "Sērijas numurs", + "cert_validity": "Derīguma termiņš", + "cert_fingerprint": "Nospiedums (SHA-256)", + "cert_algorithm": "Algoritms", + "cert_capabilities": "Iespējas", + "cert_source": "Avots", + "cert_expired": "Šī sertifikāta termiņš ir beidzies", + "cert_not_yet_valid": "Šis sertifikāts vēl nav derīgs", + "cap_sign": "Paraksts", + "cap_encrypt": "Šifrēšana", + "cap_none": "Nav", + "show_passphrase": "Rādīt paroli", + "hide_passphrase": "Slēpt paroli", + "sign_toggle": "Parakstīt", + "encrypt_toggle": "Šifrēt", + "missing_recipient_certs": "Trūkst sertifikātu: {emails}", + "missing_sender_cert": "Šai identitātei nav piesaistīta sertifikāta", + "status_encrypted_ok": "Šis ziņojums tika šifrēts", + "status_encrypted_no_key": "Ziņojums ir šifrēts, bet piemērota atslēga nav atrasta", + "status_encrypted_failed": "Neizdevās atšifrēt šo ziņojumu", + "status_signed_valid": "Paraksts ir pārbaudīts", + "status_signed_invalid": "Paraksta pārbaude neizdevās", + "status_signed_expired_cert": "Parakstīts ar sertifikātu, kura termiņš beidzies", + "status_signed_mismatch": "Paraksts ir derīgs, bet parakstītājs nesakrīt ar sūtītāju", + "status_unsupported": "Neatbalstīts S/MIME formāts", + "auto_import_signer_certs": "Automātiski importēt parakstītāju sertifikātus", + "auto_import_signer_certs_desc": "Automātiski saglabāt sertifikātus no pārbaudītām vēstulēm", + "export": "Eksportēt", + "enter_export_passphrase": "Iestatiet eksporta paroli", + "export_passphrase_desc": "Izvēlieties paroli eksportējamajam PKCS#12 failam", + "export_storage_desc": "Ievadiet krātuves paroli atslēgas atšifrēšanai pirms eksporta", + "incorrect_passphrase": "Nepareiza parole", + "status_signed_self_signed": "Paraksts ir derīgs, bet sertifikāts ir pašparakstīts (nav uzticams)" + }, + "tour": { + "step_counter": "{current}. solis no {total}", + "skip": "Izlaist ekskursiju", + "back": "Atpakaļ", + "next": "Tālāk", + "finish": "Pabeigt", + "take_a_tour": "Sākt saskarnes ekskursiju", + "restart_title": "Iepazīšanās ekskursija", + "restart_desc": "Atkārtot soli pa solim pamācību par saskarni", + "restart_button": "Restartēt ekskursiju", + "show_on_new_devices_title": "Rādīt jaunās ierīcēs", + "show_on_new_devices_desc": "Atkārtot sveiciena reklāmkarogu un ekskursiju, pirmoreiz pierakstoties jaunā ierīcē, pat ja esat tos jau pabeidzis citur", + "sidebar_title": "Jūsu pastkastes", + "sidebar_desc": "Šī ir sānu josla ar mapēm. Noklikšķiniet uz jebkuras pastkastes, lai skatītu vēstules. Varat izveidot mapes un pārvietot vēstules.", + "compose_title": "Rakstīt vēstuli", + "compose_desc": "Nospiediet šeit, lai rakstītu jaunu vēstuli. Varat pievienot saņēmējus, pielikumus un formatēt tekstu.", + "search_title": "Pasta meklēšana", + "search_desc": "Meklējiet pēc sūtītāja, temata vai satura. Izmantojiet filtra ikonu izvērstai meklēšanai.", + "email_list_title": "Vēstuļu saraksts", + "email_list_desc": "Šeit redzamas vēstules. Noklikšķiniet uz vēstules, lai to lasītu. Izmantojiet izvēles rūtiņu vairāku vēstuļu atlasīšanai.", + "email_viewer_title": "Lasīšanas rūts", + "email_viewer_desc": "Atlasītā vēstule tiek atvērta šeit. Izmantojiet pogas, lai atbildētu, pārsūtītu, arhivētu vai dzēstu.", + "keywords_title": "Krāsu etiķetes", + "keywords_desc": "Organizējiet pastu ar krāsu etiķetēm. Velciet vēstuli uz etiķeti vai izmantojiet labo klikšķi.", + "calendar_title": "Kalendārs", + "calendar_desc": "Pārvaldiet savus pasākumus. Izveidojiet ierakstus, iestatiet atgādinājumus un mainiet skatus.", + "contacts_title": "Kontakti", + "contacts_desc": "Šeit atrodas jūsu adrešu grāmata. Importējiet kontaktus un organizējiet tos grupās.", + "settings_title": "Iestatījumi", + "settings_desc": "Pielāgojiet tēmu, parakstus, filtrus, īsinājumtaustiņus un daudz ko citu.", + "shortcuts_title": "Īsinājumtaustiņi", + "shortcuts_desc": "Nospiediet ?, lai skatītu visus īsinājumtaustiņus un pārvaldītu pastu bez peles.", + "compose_open_title": "Vēstuļu redaktors", + "compose_open_desc": "Šeit varat rakstīt ziņojumu, pievienot failus un izmantot veidnes.", + "calendar_view_title": "Jūsu kalendārs", + "calendar_view_desc": "Šis ir kalendārs ar parauga pasākumiem. Mainiet skatus starp dienu, nedēļu un mēnesi.", + "create_event_title": "Izveidot pasākumu", + "create_event_desc": "Nospiediet šo pogu, lai pievienotu jaunu pasākumu, norādot laiku un dalībniekus.", + "event_modal_title": "Pasākuma informācija", + "event_modal_desc": "Aizpildiet pasākuma formu un nospiediet «Saglabāt», kad esat pabeidzis.", + "contacts_list_title": "Jūsu kontakti", + "contacts_list_desc": "Skatīt un rediģēt kontaktus, importēt vCard vai veidot grupas.", + "settings_tabs_title": "Iestatījumu izvēlne", + "settings_tabs_desc": "Šeit atradīsiet visas iestatījumu kategorijas - no izskata līdz drošībai.", + "files_title": "Failu krātuve", + "files_desc": "Failu pārvaldnieks ļauj augšupielādēt un organizēt failus kā personīgo mākoņkrātuvi.", + "demo_banner_title": "Demo vadība", + "demo_banner_desc": "Jūs esat demo režīmā - visi dati paliek jūsu pārlūkā. Nospiediet «Atiestatīt demo», lai sāktu no jauna.", + "quota_title": "Krātuves izmantošana", + "quota_desc": "Sekojiet līdzi savas pastkastes aizpildījumam šeit." + }, + "unified_mailbox": { + "search_unavailable": "Meklēšana nav pieejama apvienotajā skatā" + }, + "calendar_event_notifications": { + "someone": "Kāds", + "invited": "{name} uzaicināja jūs uz “{title}”", + "updated": "{name} atjaunināja “{title}”", + "cancelled": "{name} atcēla “{title}”", + "untitled": "Notikums bez nosaukuma" + }, + "share_notifications": { + "someone": "Kāds", + "shared": "{name} kopīgoja ar jums {kind} “{object}”", + "changed": "{name} mainīja jūsu piekļuvi {kind} “{object}”", + "revoked": "{name} noņēma jūsu piekļuvi {kind} “{object}”", + "object": { + "folder": "mapi", + "calendar": "kalendāru", + "address_book": "adrešu grāmatu", + "files": "failu mapi" + } + }, + "sharing": { + "title": "Kopīgot \"{name}\"", + "description": "Piešķiriet piekļuvi citiem lietotājiem vai grupām šajā serverī. Izmaiņas stājas spēkā nekavējoties.", + "no_shares": "Vēl nav kopīgots.", + "add_person": "Pievienot personu vai grupu", + "search_placeholder": "Meklēt pēc vārda vai e-pasta…", + "loading_principals": "Ielādē lietotājus…", + "no_principals": "Citi lietotāji vai grupas nav atrastas.", + "no_match": "Nav atbilstību.", + "remove": "Noņemt piekļuvi", + "group": "Grupa", + "share_added": "Piekļuve piešķirta", + "share_updated": "Piekļuve atjaunināta", + "share_removed": "Piekļuve noņemta", + "share_failed": "Neizdevās atjaunināt kopīgošanu", + "preset": { + "freeBusy": "Tikai brīvs/aizņemts", + "read": "Tikai lasīšana", + "readWrite": "Lasīšana un rakstīšana", + "manager": "Pārvaldnieks", + "custom": "Pielāgots" + } + }, + "quote_header": { + "reply_line": "{date} {from} rakstīja:", + "forwarded_separator": "---------- Pārsūtītā ziņa ----------", + "from_label": "No", + "date_label": "Datums", + "subject_label": "Tēma" + }, + "pwa_install": { + "title": "Instalēt {appName}", + "description": "Instalējiet mūsu lietotni ātrai piekļuvei un bezsaistes atbalstam.", + "not_now": "Ne tagad", + "install": "Instalēt", + "dont_remind": "Vairs man neatgādināt", + "dismiss_aria": "Aizvērt instalēšanas paziņojumu" + }, + "deep_link": { + "message_not_found": "Šī ziņa vairs nav pieejama.", + "thread_not_found": "Šī sarakste vairs nav pieejama.", + "folder_not_found": "Šī mape vairs nav pieejama.", + "event_not_found": "Šis notikums vairs nav pieejams.", + "file_not_found": "Šis fails vairs nav pieejams.", + "account_unavailable": "Piesakieties šajā kontā, lai atvērtu saiti.", + "copy_message": "Kopēt saiti uz ziņu", + "copy_conversation": "Kopēt saiti uz saraksti", + "copy_event": "Kopēt saiti uz notikumu", + "copied": "Saite nokopēta", + "copy_failed": "Neizdevās nokopēt saiti" + }, + "plugins": { + "oauth_callback": { + "completing": "Pabeidz autorizāciju…", + "completing_message": "Lūdzu, uzgaidiet, kamēr tiek izveidots savienojums.", + "received_title": "Autorizācija saņemta", + "received_message": "Varat atgriezties cilnē, kurā sākāt savienojumu.", + "failed_title": "Autorizācija neizdevās", + "denied": "Autorizācija tika atteikta.", + "failed_detail": "Autorizācija neizdevās: {error}", + "missing_params": "Trūkst autorizācijas kods vai stāvoklis.", + "delivery_failed": "Autorizācijas rezultātu neizdevās nodot lietotnes cilnei. Aizveriet šo logu un mēģiniet vēlreiz." + }, + "settings": { + "title": "Spraudņi", + "description": "Spraudņi, ko pievienojis jūsu administrators. Ieslēdziet vai izslēdziet tos savam kontam.", + "description_uploads": "Spraudņi papildina webmail ar jaunām funkcijām. Ieslēdziet vai izslēdziet tos savam kontam vai instalējiet savus.", + "description_demo": "Demo režīmā spraudņi nav pieejami.", + "install_button": "Instalēt spraudni", + "install_hint": "Izvēlieties spraudņa ZIP failu.", + "install_hint_approval": "Izvēlieties spraudņa ZIP failu. Pirms varat to ieslēgt, tas jāapstiprina administratoram.", + "install_uploads_off": "Jūsu administrators neļauj instalēt spraudņus.", + "install_managed_id": "Jūsu administrators jau nodrošina spraudni ar šādu nosaukumu, tāpēc to nevar aizstāt.", + "install_invalid": "Šis fails nav instalējams spraudnis: {details}", + "toast_installed": "Spraudnis instalēts. Ieslēdziet to zemāk.", + "toast_installed_pending": "Spraudnis instalēts un nosūtīts jūsu administratoram apstiprināšanai.", + "toast_enabled": "Spraudnis {name} ir ieslēgts", + "toast_disabled": "Spraudnis {name} ir izslēgts", + "toast_removed": "Spraudnis {name} ir noņemts", + "toast_forced": "Jūsu administrators patur spraudni {name} ieslēgtu", + "toast_approval_requested": "Spraudnis {name} gaida jūsu administratora apstiprinājumu", + "empty_title": "Vēl nav spraudņu", + "empty_admin": "Jūsu administrators nav pievienojis nevienu spraudni.", + "empty_uploads": "Lai sāktu, instalējiet spraudni no ZIP faila.", + "empty_demo": "Spraudņiem vajadzīgs īsts konts, tāpēc demo režīmā to nav.", + "syncing": "Pārbauda spraudņus serverī…", + "status_installed": "Izslēgts", + "status_enabled": "Tiek palaists", + "status_running": "Ieslēgts", + "status_disabled": "Izslēgts", + "status_error": "Problēma", + "badge_forced": "Obligāts", + "badge_managed": "No jūsu administratora", + "badge_yours": "Instalējāt jūs", + "badge_awaiting": "Gaida apstiprinājumu", + "toggle_label": "Ieslēgt vai izslēgt spraudni {name}", + "forced_note": "Jūsu administrators pieprasa šo spraudni, tāpēc to nevar izslēgt.", + "permissions": "Ko tas var darīt", + "permissions_managed": "Ko tas var darīt (apstiprinājis jūsu administrators)", + "settings": "Iestatījumi", + "remove": "Noņemt", + "remove_title": "Noņemt spraudni {name}?", + "remove_message": "Spraudnis un viss, ko tas saglabājis šajā pārlūkā, tiks dzēsts. Vēlāk to var instalēt atkal.", + "remove_confirm": "Noņemt", + "error_user_plugins_off": "Jūsu administrators ir izslēdzis lietotāju instalētos spraudņus, tāpēc šis spraudnis nedarbojas.", + "error_approval_unavailable": "Šis spraudnis tika instalēts, pirms bija vajadzīgs apstiprinājums. Noņemiet to un instalējiet vēlreiz, lai nosūtītu apstiprināšanai.", + "error_approval_denied": "Jūsu administrators neapstiprināja šo spraudni.", + "error_approval_pending": "Gaida, kad jūsu administrators apstiprinās šo spraudni.", + "error_auto_disabled": "Izslēgts, jo atkārtoti neizdevās. Ieslēdziet to vēlreiz, lai mēģinātu vēlreiz.", + "accounts_heading": "Savienotie konti", + "account_on": "{label} serverī {host}", + "account_waiting": "{label} serverī {host} (pierakstīšanās nav pabeigta)", + "disconnect": "Atvienot", + "disconnect_failed": "Neizdevās atvienot {label}" + }, + "consent": { + "title": "Atļaut spraudnim {name} to darīt?", + "intro": "Spraudnis pieprasa tālāk norādītās atļaujas. Tas tās saņems tikai tad, ja jūs atļausiet.", + "high_risk_note": "Atzīmētie punkti dod spraudnim piekļuvi jūsu pasta saturam vai ļauj tam sūtīt datus citur. Atļaujiet tos tikai spraudņiem, kuriem uzticaties.", + "revoke_hint": "Spraudni jebkurā laikā varat izslēgt vai noņemt iestatījumos.", + "deny": "Neatļaut", + "allow": "Atļaut" + }, + "permissions": { + "none": "Tam nav vajadzīgas īpašas atļaujas.", + "high_risk": "Nepieciešama piesardzība", + "email_read": { + "title": "Lasīt jūsu pastu", + "body": "Redzēt sūtītājus, adresātus, tēmas un visu tekstu jebkurai vēstulei jūsu kontā, kā arī jūsu mapes." + }, + "email_write": { + "title": "Mainīt un kārtot jūsu pastu", + "body": "Pievienot vai noņemt tagus, karodziņus un citus atslēgvārdus jūsu vēstulēm, pārvietot tās starp jūsu mapēm un uz arhīvu un saglabāt melnrakstus." + }, + "email_send": { + "title": "Mainīt vēstules pirms nosūtīšanas", + "body": "Lasīt un mainīt adresātus, tēmu, tekstu un pielikumus vēstulēm, ko rakstāt, un apturēt vēstules nosūtīšanu." + }, + "crypto_full": { + "title": "Pilna piekļuve lietotnei", + "body": "Darbojas ar tādām pašām tiesībām kā pats webmail. Var lasīt visu jūsu pastu un saglabātās atslēgas un rīkoties jūsu vārdā. Atļaujiet to tikai spraudņiem, kuriem pilnībā uzticaties." + }, + "email_raw_send": { + "title": "Sūtīt paša izveidotas vēstules", + "body": "Sūtīt no jūsu konta pilnīgas vēstules, piemēram, parakstītas vai šifrētas." + }, + "email_blob_read": { + "title": "Lasīt vēstuļu neapstrādātos datus", + "body": "Lasīt jūsu vēstuļu un pielikumu sākotnējos datus, kā arī katru failu, ko pievienojat, pirms tas tiek nosūtīts." + }, + "email_blob_write": { + "title": "Aizstāt pielikumus pirms nosūtīšanas", + "body": "Augšupielādēt failus jūsu pasta serverī un aizstāt pievienoto failu ar citu, pirms vēstule tiek nosūtīta." + }, + "email_render_takeover": { + "title": "Mainīt vēstuļu attēlojumu", + "body": "Aizstāt tekstu vēstulēm, ko atverat, un vēstulēm sarakstā, piemēram, lai parādītu atšifrētu saturu." + }, + "calendar_read": { + "title": "Lasīt jūsu kalendāru", + "body": "Redzēt jūsu kalendārus un to notikumus." + }, + "calendar_write": { + "title": "Mainīt jūsu kalendāru", + "body": "Izveidot, mainīt un dzēst notikumus." + }, + "contacts_read": { + "title": "Lasīt jūsu kontaktus", + "body": "Meklēt un lasīt jūsu adrešu grāmatās." + }, + "contacts_write": { + "title": "Mainīt jūsu kontaktus", + "body": "Izveidot, mainīt un dzēst kontaktus un adrešu grāmatas." + }, + "files_read": { + "title": "Lasīt jūsu failus", + "body": "Pārlūkot un atvērt jūsu kontā glabātos failus." + }, + "files_write": { + "title": "Mainīt jūsu failus", + "body": "Augšupielādēt, pārdēvēt, pārvietot un dzēst failus jūsu kontā." + }, + "identity_read": { + "title": "Redzēt jūsu sūtītāja adreses", + "body": "Redzēt adreses, no kurām sūtāt, un to parakstus." + }, + "identity_write": { + "title": "Mainīt jūsu sūtītāja adreses", + "body": "Izveidot, mainīt un dzēst sūtītāja adreses un parakstus." + }, + "filters_read": { + "title": "Lasīt jūsu pasta filtrus", + "body": "Redzēt noteikumus, kas šķiro jūsu ienākošo pastu." + }, + "filters_write": { + "title": "Mainīt jūsu pasta filtrus", + "body": "Pievienot noteikumus jūsu servera filtriem. Šādi noteikumi var pārvietot, pārsūtīt vai dzēst ienākošo pastu." + }, + "tasks_read": { + "title": "Lasīt jūsu uzdevumus", + "body": "Redzēt jūsu uzdevumu sarakstus un uzdevumus." + }, + "tasks_write": { + "title": "Mainīt jūsu uzdevumus", + "body": "Izveidot, mainīt, pabeigt un dzēst uzdevumus." + }, + "templates_read": { + "title": "Lasīt jūsu veidnes", + "body": "Redzēt jūsu saglabātās vēstuļu veidnes." + }, + "templates_write": { + "title": "Mainīt jūsu veidnes", + "body": "Izveidot, mainīt un dzēst vēstuļu veidnes." + }, + "smime_read": { + "title": "Redzēt jūsu S/MIME statusu", + "body": "Redzēt, vai jums ir S/MIME atslēgas un sertifikāti, bez piekļuves atslēgām." + }, + "vacation_read": { + "title": "Redzēt jūsu prombūtnes atbildi", + "body": "Redzēt, vai automātiskā atbilde ir ieslēgta un kas tajā rakstīts." + }, + "vacation_write": { + "title": "Mainīt jūsu prombūtnes atbildi", + "body": "Ieslēgt vai izslēgt automātisko atbildi un mainīt tās tekstu un datumus." + }, + "settings_read": { + "title": "Lasīt jūsu iestatījumus", + "body": "Redzēt jūsu preferences un tagu definīcijas." + }, + "settings_write": { + "title": "Mainīt jūsu iestatījumus", + "body": "Pievienot tagus, mainīt to secību un mainīt jūsu preferences." + }, + "security_read": { + "title": "Redzēt jūsu konta drošību", + "body": "Redzēt, vai divfaktoru autentifikācija un šifrēšana ir ieslēgtas, bez piekļuves noslēpumiem." + }, + "auth_observe": { + "title": "Zināt, kad pierakstāties", + "body": "Saņemt ziņu, kad pierakstāties, pārslēdzat kontu vai jūsu sesija tiek atjaunota." + }, + "auth_emit": { + "title": "Izrakstīt jūs", + "body": "Izrakstīt jūs no webmail." + }, + "account_read": { + "title": "Redzēt jūsu kontus", + "body": "Redzēt jūsu pievienotos kontus ar to adresēm un serveriem." + }, + "http_post": { + "title": "Izmantot webmail serveri jūsu vārdā", + "body": "Izsaukt šos servera galapunktus ar jūsu pieteikšanās datiem:" + }, + "http_fetch": { + "title": "Savienoties ar citām vietnēm", + "body": "Sūtīt pieprasījumus uz šīm vietnēm. Uz turieni var nosūtīt visu, ko spraudnis var nolasīt:" + }, + "admin_config": { + "title": "Lasīt savus iestatījumus no administratora", + "body": "Lasīt iestatījumus, ko jūsu administrators saglabājis šim spraudnim." + }, + "ui_observe": { + "title": "Sekot, kur atrodaties lietotnē", + "body": "Zināt, kura lapa ir atvērta, un reaģēt uz saitēm, uz kurām noklikšķināt." + }, + "ui_toolbar": { + "title": "Pievienot pogas rīkjoslām", + "body": "Rādīt savas pogas vēstuļu rīkjoslā, atvērtā vēstulē un augšējā joslā." + }, + "ui_app_top_banner": { + "title": "Rādīt reklāmkarogu augšā", + "body": "Rādīt paziņojumu pāri lietotnes augšdaļai." + }, + "ui_email_banner": { + "title": "Rādīt paziņojumus vēstulēs", + "body": "Rādīt savus paziņojumus un kopsavilkumus virs atvērtas vēstules vai sarunas daļām, to iekšpusē un starp tām." + }, + "ui_email_footer": { + "title": "Pievienot saturu zem vēstulēm", + "body": "Rādīt savu saturu zem atvērtas vēstules." + }, + "ui_contact_cryptokeys": { + "title": "Rādīt kontaktu šifrēšanas atslēgas", + "body": "Rādīt un pārvaldīt kontakta šifrēšanas atslēgas." + }, + "ui_email_details": { + "title": "Pievienot paneļus vēstules informācijai", + "body": "Rādīt savu paneli blakus atvērtai vēstulei vai zem tās." + }, + "ui_download_file": { + "title": "Saglabāt failus jūsu ierīcē", + "body": "Piedāvāt lejupielādei paša izveidotus failus." + }, + "ui_message_list_tabs": { + "title": "Pievienot cilnes iesūtnei", + "body": "Sadalīt jūsu iesūtni cilnēs, piemēram, Jaunumi vai Akcijas." + }, + "ui_composer_toolbar": { + "title": "Papildināt redaktoru", + "body": "Rādīt savas pogas, izvēlnes ierakstus un sadaļas, kamēr rakstāt vēstuli." + }, + "ui_composer_sidebar": { + "title": "Pievienot paneli redaktoram", + "body": "Rādīt savu paneli blakus vēstulei, ko rakstāt." + }, + "ui_sidebar_widget": { + "title": "Papildināt sānjoslu", + "body": "Rādīt savus blokus un sadaļas pasta sānjoslā." + }, + "ui_settings_section": { + "title": "Pievienot iestatījumu lapu", + "body": "Rādīt savu lapu iestatījumos." + }, + "ui_context_menu": { + "title": "Papildināt vēstules izvēlni", + "body": "Pievienot ierakstus izvēlnei, kas atveras, ar peles labo pogu noklikšķinot uz vēstules." + }, + "ui_navigation_rail": { + "title": "Papildināt navigācijas joslu", + "body": "Rādīt savu pogu navigācijas joslā." + }, + "ui_keyboard": { + "title": "Pievienot īsinājumtaustiņus", + "body": "Reaģēt uz paša definētiem īsinājumtaustiņiem." + }, + "ui_calendar_action": { + "title": "Papildināt kalendāra skatu", + "body": "Rādīt savas pogas un sadaļas pie notikumiem un īsas birkas un punktus kalendāra dienās." + }, + "ui_admin_page": { + "title": "Pievienot administrēšanas lapu", + "body": "Rādīt savu lapu administratora iestatījumos." + }, + "app_lifecycle": { + "title": "Zināt, kad lietotne startē un apstājas", + "body": "Saņemt ziņu, kad lietotne startē, pāriet bezsaistē vai jūs izrakstāties." + }, + "compose_open": { + "title": "Atvērt redaktoru jūsu vietā", + "body": "Atvērt jaunu vēstuli ar jau aizpildītiem adresātiem, tekstu vai pielikumiem. Vai to nosūtīt, joprojām izlemjat jūs." + }, + "compose_read": { + "title": "Lasīt ziņojumu, ko rakstāt", + "body": "Lasīt ziņojuma tekstu rakstīšanas laikā, kad izmantojat spraudni redaktorā. Ne parakstu, ne citēto ziņojumu." + }, + "compose_write": { + "title": "Mainīt ziņojumu, ko rakstāt", + "body": "Aizstāt vai pievienot tekstu ziņojumā rakstīšanas laikā, kad izmantojat spraudni redaktorā. Katru izmaiņu var atsaukt, un jūs joprojām izlemjat, vai sūtīt." + }, + "ui_notifications": { + "title": "Rādīt paziņojumus", + "body": "Rādīt paziņojumus jūsu ierīcē vai lietotnē, ja neesat tos atļāvis. Katrā ir spraudņa nosaukums." + }, + "ui_message_list": { + "title": "Papildināt vēstuļu sarakstu", + "body": "Rādīt savu saturu virs vēstuļu saraksta un pogas pie vēstulēm un atlasīto vēstuļu rīkjoslā." + }, + "ui_contacts": { + "title": "Papildināt kontaktu skatu", + "body": "Rādīt savas sadaļas kontakta informācijā un pogas kontaktu sarakstā." + }, + "ui_tasks": { + "title": "Papildināt uzdevumu skatu", + "body": "Rādīt savas sadaļas uzdevuma informācijā un pogas uzdevumu sarakstā." + }, + "ui_files": { + "title": "Papildināt failu skatu", + "body": "Rādīt savas sadaļas failu priekšskatījumā un ierakstus faila izvēlnē." + }, + "ui_commands": { + "title": "Pievienot komandas", + "body": "Pievienot savus ierakstus komandu paletei (Ctrl+K)." + }, + "ui_app_page": { + "title": "Pievienot lapas", + "body": "Pievienot lietotnei savas lapas, katru ar pogu navigācijas joslā." + }, + "http_proxy": { + "title": "Izmantot jūsu savienotos kontus", + "body": "Sūtīt pieprasījumus caur Bulwark serveri ar kontu vai atslēgu, ko iestatījāt jūs vai jūsu administrators. Spraudnis nekad neredz paroli, taču tas var darīt visu, ko šis konts var darīt savā serverī." + } + }, + "dialog": { + "from_plugin": "No spraudņa {name}", + "ok": "Labi", + "submit": "Nosūtīt", + "confirm": "Apstiprināt", + "cancel": "Atcelt", + "close": "Aizvērt", + "turn_off": "Izslēgt {name}", + "plugin_menu": "{name} opcijas", + "password_note": "Šī veidlapa nāk no spraudņa {name}, nevis no tīmekļa pasta. Ievadiet paroli tikai tad, ja uzticaties šim spraudnim.", + "toast_auto_disabled": "{name} tika izslēgts, jo atkārtoti neizdevās." + }, + "connect": { + "title": "Savienot {label}", + "intro_password": "{name} vēlas izmantot jūsu {label} kontu. Bulwark glabā paroli savā serverī un sūta to tikai uz {host}. Spraudnis to nekad neredz.", + "intro_password_any": "{name} vēlas izmantot jūsu {label} kontu. Bulwark glabā paroli savā serverī un sūta to tikai uz serveri, ko šeit ievadāt. Spraudnis to nekad neredz.", + "intro_signin": "{name} vēlas pierakstīties jūsu {label} kontā. Ievadiet šeit serveri un pēc tam pierakstieties šī servera lapā. Bulwark glabā iegūto piekļuvi savā serverī; spraudnis to nekad neredz.", + "server": "Servera adrese", + "server_hint": "cloud.example.com", + "username": "Lietotājvārds", + "secret": "Parole vai atslēga", + "connect": "Savienot", + "continue": "Turpināt", + "cancel": "Atcelt", + "saving": "Saglabā…", + "error_server": "Ievadiet servera adresi, piemēram, https://cloud.example.com", + "error_save": "Neizdevās saglabāt: {error}" + } + }, + "global_search": { + "title": "Meklēt visā", + "placeholder": "Meklēt pastā, kontaktos, kalendārā, failos...", + "scope_label": "Meklēt", + "account_label": "Konts", + "scope_all": "Viss", + "scope_mail": "Pasts", + "scope_contacts": "Kontakti", + "scope_calendar": "Kalendārs", + "scope_files": "Faili", + "all_accounts": "Visi konti", + "show_all": "Rādīt visus rezultātus", + "no_results": "Nav rezultātu vaicājumam “{query}”", + "server_hint": "Pilni vārdi meklē serverī; vārdu daļas atbilst jau ielādētajiem vienumiem", + "searching": "Meklē...", + "account_error": "Neizdevās meklēt kontā {account}", + "recent_searches": "Pēdējie meklējumi", + "recurring": "Period isks", + "no_subject": "(no subject)", + "empty_hint": "Meklējiet pastu, kontaktus, notikumus un failus visos kontos. Izmēģiniet from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Plakani pēc datuma", + "grouped": "Grupēts", + "load_more": "Ielādēt vairāk", + "preview_empty": "Izvēlieties rezultātu, lai to priekšskatītu", + "open_full": "Atvērt", + "view_label": "Skats" + }, + "top_bar": { + "more": "Citas iespējas" + }, + "plugin_pages": { + "loading": "Ielādē lapu", + "not_available_title": "Šī lapa nav pieejama", + "not_available_description": "Spraudnis, kas to pievienoja, ir izslēgts vai vairs nav instalēts.", + "back_to_mail": "Atpakaļ uz pastu", + "settings_group": "Spraudņi" + }, + "command_palette": { + "title": "Komandu palete", + "open": "Atvērt komandu paleti", + "placeholder": "Ierakstiet komandu", + "no_results": "Nav atbilstošu komandu", + "go_to": "Pāriet uz: {name}", + "compose": "Jauna vēstule", + "search": "Meklēt pastā", + "theme_dark": "Pārslēgt uz tumšo motīvu", + "theme_light": "Pārslēgt uz gaišo motīvu", + "shortcuts": "Rādīt tastatūras īsinājumtaustiņus", + "group_navigation": "Pāriet uz", + "group_actions": "Darbības", + "group_plugins": "Spraudņi" + }, + "notification_center": { + "title": "Paziņojumi", + "mark_all_read": "Atzīmēt visus kā lasītus", + "clear": "Notīrīt", + "empty": "Nav paziņojumu", + "close": "Aizvērt", + "unread": "Nelasīts", + "unread_count": "Nelasīti: {count}", + "level_error": "Kļūda", + "level_warning": "Brīdinājums" + } +} diff --git a/locales/mn/common.json b/locales/mn/common.json index 16a06dad..7bf1f9cb 100644 --- a/locales/mn/common.json +++ b/locales/mn/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "JMAP протоколыг ашигладаг минималист вэб шуудангийн клиент", - "login": { - "title": "Вэбмэйл", - "username_label": "Имэйл", - "username_placeholder": "user@example.com", - "password_label": "Нууц үг", - "password_placeholder": "Нууц үгээ оруулна уу", - "jmap_endpoint_label": "JMAP сервер", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Сервер энэ домэйноос ирэх CORS хүсэлтийг зөвшөөрсөн байх ёстой.", - "jmap_server_label": "Сервер", - "jmap_server_auto_picked": "Таны имэйл хаягийн домэйноос серверийг автоматаар сонголоо.", - "sign_in": "Нэвтрэх", - "signing_in": "Нэвтэрч байна...", - "loading": "Ачаалж байна...", - "reconnecting": "Холболт тасарлаа. Дахин холбогдохыг оролдож байна…", - "error": { - "invalid_credentials": "Имэйл эсвэл нууц үг буруу байна. Мэдээллээ шалгаад дахин оролдоно уу.", - "connection_failed": "Сервертэй холбогдож чадсангүй. Интернэт холболтоо шалгаад дахин оролдоно уу.", - "cors_blocked": "Серверт хүрч байна, гэхдээ cross-origin хүсэлтийг хориглож байна. JMAP серверийн CORS тохиргоог шалгаад энэ домэйныг зөвшөөрнө үү.", - "server_error": "Сервер түр ажиллахгүй байна. Дараа дахин оролдоно уу.", - "generic": "Санамсаргүй алдаа гарлаа. Хэрэв давтагдсаар байвал админтайгаа холбогдоно уу.", - "totp_required": "Хоёр шаттай баталгаажуулалтын код шаардлагатай. Доор кодоо оруулна уу.", - "totp_invalid": "Баталгаажуулалтын код буруу байна. Authenticator апп-аа шалгаад дахин оролдоно уу.", - "oauth_discovery_failed": "SSO идэвхтэй боловч identity provider-д хүрч чадсангүй. OAuth тохиргоогоо шалгана уу." - }, - "show_password": "Нууц үг харуулах", - "hide_password": "Нууц үг нуух", - "totp_toggle": "Надад 2FA код байгаа", - "remember_me": "Намайг сана", - "config_error": { - "title": "Тохиргооны алдаа", - "fetch_failed": "Аппликэйшний тохиргоог ачаалж чадсангүй. Дараа дахин оролдоно уу.", - "server_not_configured": "Мэйл сервер тохируулагдаагүй байна. Админтайгаа холбогдоно уу." - }, - "remove_from_history": "Түүхээс устгах", - "totp_label": "Баталгаажуулах код", - "totp_placeholder": "000000", - "session_expired": "Таны сесс дууссан байна. Дахин нэвтэрнэ үү.", - "dismiss": "Хаах", - "or": "эсвэл", - "sign_in_sso": "SSO-ээр нэвтрэх", - "add_account_title": "Бүртгэл нэмэх", - "add_account_subtitle": "Өөр бүртгэлээр нэвтрэх", - "cancel": "Болих", - "website": "Вэбсайт", - "imprint": "Хуулийн мэдээлэл", - "privacy_policy": "Нууцлалын бодлого", - "try_demo": "Демо үзэх", - "demo_description": "Жишээ өгөгдлөөр туршиж үзээрэй - бүртгэл шаардлагагүй", - "demo_launching": "Демо эхлүүлж байна...", - "demo_login_button": "Демо нээх", - "demo_tagline": "Бүрэн боломжтой имэйлийн клиентийг туршиж үзээрэй. Бүртгэл шаардлагагүй.", - "demo_no_signup": "Бүртгүүлэх шаардлагагүй - жишээ өгөгдлөөр чөлөөтэй үзэж сонирхоно уу", - "oauth_completing": "Нэвтрэлтийг дуусгаж байна...", - "oauth_error": { - "title": "Баталгаажуулалт амжилтгүй", - "invalid_state": "Аюулгүй байдлын шалгалт амжилтгүй боллоо. Дахин нэвтэрнэ үү.", - "missing_params": "Зөвшөөрлийн өгөгдөл дутуу байна. Дахин нэвтэрнэ үү.", - "token_exchange_failed": "Баталгаажуулалтыг дуусгаж чадсангүй. Дахин оролдоно уу.", - "access_denied": "Хандалтыг зөвшөөрсөнгүй. Админтайгаа холбогдоно уу.", - "back_to_login": "Нэвтрэх рүү буцах" - } - }, - "sidebar": { - "close": "Хаах", - "compose": "Бичилэх", - "compose_hint": "Бичих (c)", - "search_placeholder": "Мэйл хайх...", - "search_placeholder_hint": "Мэйл хайх... (/ дарна уу)", - "storage": "Хадгалах", - "storage_used": "Ашигласан", - "storage_free": "Үнэгүй", - "storage_total": "Нийт", - "sign_out": "Гарах", - "sign_out_of": "{account}", - "sign_out_all": "Бүх бүртгэлээс гарах", - "add_account": "Бүртгэл нэмэх", - "set_as_default": "Өгөгдмөлөөр тохируулах", - "switch_account": "Бүртгэл солих", - "contacts": "Харилцах утас", - "calendar": "Хуанли", - "settings": "Тохиргоо", - "admin": "Админ", - "files": "Файл", - "loading_mailboxes": "Шуудангийн хайрцгийг ачаалж байна...", - "push_connected": "Бодит цагийн шинэчлэлт идэвхтэй", - "push_disconnected": "Бодит цагийн шинэчлэл идэвхгүй байна", - "keyboard_shortcuts": "Гарын товчлол", - "theme": { - "light": "Гэрлийн горим", - "dark": "Харанхуй горим", - "system": "Системийн сэдэв" - }, - "language": { - "title": "Хэл" - }, - "mailboxes": { - "inbox": "Ирсэн имэйл", - "sent": "Илгээсэн", - "drafts": "Ноорог", - "trash": "Хогийн сав", - "archive": "Архив", - "starred": "Одтой", - "all_mail": "Бүх шуудан", - "spam": "Спам", - "important": "Чухал" - }, - "unified_inbox": "Нэгдсэн ирсэн имэйл", - "unified_sent": "Бүгд илгээсэн", - "unified_drafts": "Бүх ноорог", - "unified_trash": "Бүх хогийн сав", - "unified_archive": "Бүх архив", - "unified_junk": "Бүх хог", - "all_accounts": "Бүх дансууд", - "unified_mailbox": "Нэгдсэн шуудангийн хайрцаг", - "expand": "Өргөтгөх", - "collapse": "Нуналт", - "expand_tooltip": "Дэлгэх", - "collapse_tooltip": "Нуналт", - "mobile": { - "search": "Хайлт", - "compose": "Бичилэх", - "go_back": "Буцах", - "toggle_menu": "Цэсийг сэлгэх" - }, - "clear_search": "Хайлтыг арилгах", - "vacation_active": "Амралтын хариулагч идэвхтэй байна", - "demo_banner": "Демо горим", - "demo_reset": "Дахин тохируулах", - "demo_tour": "Аялал", - "tags": "Тагууд", - "show_all_tags": "Бүгдийг нь харуулах ({count})", - "show_fewer_tags": "Бага харуулах", - "folders": "Хавтас", - "shared": "Хуваалцсан", - "mail": "Мэйл", - "nav_label": "Навигаци", - "add_app": "Програмууд", - "scheduled": "Хуваарьтай", - "unified_all_unread": "Бүгд уншаагүй", - "unified_all_starred": "Бүх одтой", - "unified_all_mail": "Бүх шуудан", - "remove_account": "Бүртгэл устгах", - "remove_account_confirm": "Энэ төхөөрөмжөөс {account}-г устгах уу? Та дараа дахин нэмж болно." - }, - "protocol_handlers": { - "title": "Өгөгдмөл програмууд", - "description": "И-мэйл болон хуанлийн холбоосууд Bulwark дээр нээгдэх эсэхийг сонгоно уу. Техникийн хувьд Bulwark нь mailto: болон webcal: холбоосуудад протокол зохицуулагчаар бүртгүүлдэг.", - "unsupported": "Энэ хөтөч эсвэл холболт нь протокол зохицуулагчийн гарын авлагын бүртгэлийг дэмждэггүй. Та суулгасан PWA-г хөтөч эсвэл үйлдлийн системээр дамжуулан ашиглах боломжтой хэвээр байж магадгүй.", - "mailto_label": "И-мэйл холбоосууд", - "mailto_description": "Bulwark дээр урьдчилан бөглөсөн mailto: холбоосуудыг нээнэ.", - "protocol_open_mode_label": "Протоколын холбоосыг нээх үед", - "protocol_open_mode_description": "Bulwark нь mailto: болон webcal: холбоосыг шинэ таб дээр нээх эсвэл нээлттэй сессийг дахин ашиглах эсэхийг сонгоно уу. Идэвхтэй сессийн сонголтод мэдэгдлийн зөвшөөрөл шаардлагатай тул хэрэв хөтөч фокусыг блоклодог бол та Bulwark-г урд нь авчрахын тулд нөөц мэдэгдэл дээр дарж болно.", - "protocol_open_mode_active_session": "Боломжтой бол идэвхтэй сессээр нээх", - "protocol_open_mode_new_tab": "Үргэлж шинэ таб нээх", - "focus_notification_title": "Холбоос нээгдлээ", - "focus_notification_body": "Холбоос Bulwark-д нээгдлээ. Цонхыг урд талд гаргахын тулд товшино уу.", - "webcal_label": "Календарийн холбоосууд", - "webcal_description": "Bulwark дээр урьдчилан бөглөсөн webcal: холбоосуудыг нээнэ.", - "register_mailto": "Имэйл програмыг бүртгүүлэх", - "register_webcal": "Календарийн програмыг бүртгэх", - "mailto_registered": "И-мэйл зохицуулагчийн бүртгэл хүссэн", - "webcal_registered": "Хуанли зохицуулагчийн бүртгэлийг хүссэн", - "registration_failed": "Протокол зохицуулагчийн бүртгэл амжилтгүй боллоо", - "opening_mailto": "Хөгжмийн зохиолч нээж байна...", - "opening_webcal": "Нээлтийн хуанли...", - "browser_note": "Таны хөтөч эсвэл үйлдлийн систем танаас үүнийг баталгаажуулахыг хүсэх ба Bulwark-ыг үндсэн програмаар сонгохын өмнө суулгахыг шаардаж магадгүй.", - "select_account_title": "Бүртгэл сонгох", - "select_mailto_account": "Энэ имэйлийн холбоосыг аль бүртгэлээр нээхийг сонгоно уу.", - "select_webcal_account": "Энэ хуанлийн холбоосыг аль данс нээхийг сонгоно уу.", - "select_account_note": "Энэ нь зөвхөн энэ протоколын холбоост хамаарна.", - "detail_to": "Хэнд", - "detail_subject": "Сэдэв", - "detail_no_subject": "Сэдэв байхгүй", - "detail_calendar": "Хуанли", - "detail_source": "Эх сурвалж", - "active_account": "Идэвхтэй", - "switching_account": "Бүртгэл сэлгэж байна..." - }, - "sidebar_apps": { - "modal_title": "Sidebar програмууд", - "add_new": "Апп нэмэх", - "edit_app": "Аппликешн засах", - "name_label": "Нэр", - "name_placeholder": "Миний програм", - "name_required": "Нэр оруулах шаардлагатай", - "url_label": "URL", - "url_required": "URL шаардлагатай", - "url_invalid": "Хүчтэй http эсвэл https URL оруулна уу", - "icon_label": "Дүрс", - "icon_required": "Икон тэмдэг шаардлагатай", - "open_mode_label": "Нээлттэй горим", - "open_new_tab": "Шинэ таб", - "open_inline": "Мөр дотор", - "cancel": "Цуцлах", - "add": "Нэмэх", - "update": "Шинэчлэлт", - "delete": "Устгах", - "delete_confirm_title": "Апп устгах", - "delete_confirm": "Та \"{name}\"-г устгахдаа итгэлтэй байна уу?", - "no_apps": "Одоохондоо апп нэмээгүй байна", - "no_apps_hint": "Хажуугийн самбартаа захиалгат програм, холбоос нэмнэ үү", - "managed_title": "Администратороос нийлүүлсэн", - "managed_description": "Эдгээр аппыг бүх хэрэглэгчид тохируулсан бөгөөд өөрчлөх боломжгүй.", - "managed_badge": "Удирдсан", - "search_icons": "Хайлтын дүрсүүд...", - "show_popular": "Алдартай", - "show_all": "Бүгд", - "no_icons_found": "Ямар ч дүрс олдсонгүй", - "inline_badge": "Мөр дотор", - "tab_badge": "Таб", - "show_on_mobile": "Мобайл дээр харуулах" - }, - "email_list": { - "no_emails": "Ямар ч зурвас олдсонгүй", - "no_emails_description": "Энэ шуудангийн хайрцаг хоосон", - "no_search_results": "Илэрц олдсонгүй", - "no_search_results_description": "Хайлт эсвэл шүүлтүүрээ тохируулаад үзээрэй", - "loading": "Имэйл ачаалж байна...", - "unread": "уншаагүй", - "to_me": "Надад", - "to_recipients": "{count} хүлээн авагчдад", - "and_others": "болон {count} бусад", - "draft": "Ноорог", - "starred": "Одтой", - "pinned": "Бэхэлсэн", - "replied": "Хариулсан", - "forwarded": "Дамжуулсан", - "has_attachment": "Хавсралттай", - "conversations_count": "{total} харилцан ярианы {count}", - "conversations_count_plus": "{count}+ харилцан яриа", - "conversations_count_simple": "{count} харилцан яриа", - "no_conversations": "Ярилцлага байхгүй", - "loading_more": "Дэлгэрэнгүй имэйл ачаалж байна...", - "no_more_emails": "Ачаалах имэйл байхгүй", - "batch_actions": { - "select": "Имэйл сонгох", - "select_all": "Бүгдийг нь сонго", - "selected_messages": "{count, plural, one {1 имэйл} other {# имэйл}} сонгосон", - "mark_read": "Уншсан гэж тэмдэглэх", - "mark_unread": "Уншаагүй гэж тэмдэглэх", - "delete": "Устгах", - "delete_confirm_title": "Имэйл устгах", - "delete_confirm_message": "Та устгахдаа итгэлтэй байна уу{count, plural, one {1 имэйл} other {# имэйл}}?", - "clear_selection": "Сонголтыг арилгах" - }, - "permanent_delete": "Бүрмөсөн устгах", - "permanent_delete_confirm_title": "Бүрмөсөн устгах", - "permanent_delete_confirm_message": "Энэ имэйлийг бүрмөсөн устгах болно. Энэ үйлдлийг буцаах боломжгүй.", - "permanent_delete_confirm_batch_message": "Эдгээр{count, plural, one {1 имэйл} other {# имэйл}}бүрмөсөн устгагдах болно. Энэ үйлдлийг буцаах боломжгүй.", - "empty_folder": { - "button": "Хоосон хавтас", - "confirm_title": "Хоосон хавтас", - "confirm_message": "Энэ хавтсанд байгаа бүх имэйлүүд бүрмөсөн устах болно. Энэ үйлдлийг буцаах боломжгүй.", - "confirm_button": "Хоосон хавтас", - "junk_hint": "Та бүх мессежийг бүрмөсөн устгахын тулд хэрэггүй хавтасыг хоослох боломжтой.", - "trash_hint": "Та бүх мессежийг бүрмөсөн устгахын тулд Хогийн савны хавтсыг хоослох боломжтой." - }, - "no_scheduled_emails": "Хуваарьт имэйл байхгүй", - "no_scheduled_emails_description": "Дараа нь хийхээр төлөвлөсөн мессежүүд энд харагдах болно.", - "scheduled_count": "{count, plural, one {Төлөвлөсөн 1 имэйл} other {# хуваарьтай имэйл}}", - "scheduled_actions_hint": "Хуваарьт мессежийг цуцлах, дахин төлөвлөх эсвэл төлөвлөсөн үйлдлээсээ засварлах боломжтой.", - "cancel_scheduled_send": "Илгээхийг цуцлах", - "reschedule_send": "Хуваарь солих", - "cancel_and_edit": "Цуцлах, засах", - "cancel_and_compose_again": "Цуцлаад дахин бичих", - "reschedule_prompt": "Шинэ огноо/цаг оруулна, ж.нь. 2026-05-04Т15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Илгээгч таныг энэ мессежийг нээх үед мэдэгдэхийг хүссэн.", - "send": "Баримт илгээх", - "ignore": "Үл тоох", - "sent": "Уншсан баримтыг илгээсэн.", - "send_failed": "Уншсан баримтыг илгээж чадсангүй", - "mdn_subject": "Унш: {subject}", - "mdn_body": "Энэ нь таны {recipient} руу илгээсэн мессежийг буцаах баримт юм.\n\nТэмдэглэл: Энэхүү баримт нь зөвхөн мессежийг хүлээн авагчийн компьютер дээр харуулсан гэдгийг хүлээн зөвшөөрнө. Хүлээн авагч мессежийн агуулгыг уншсан эсвэл ойлгосон гэсэн баталгаа байхгүй." - }, - "no_email_selected": "Имэйл сонгоогүй", - "no_email_description": "Жагсаалтаас имэйл сонгоод эндээс харна уу", - "no_conversation_selected": "Ярилцлага сонгоогүй", - "no_conversation_description": "Жагсаалтаас харилцан яриаг сонгон эндээс уншина уу", - "compose": "Зохих", - "compose_hint": "Шинэ мессеж бичих", - "no_subject": "(Сэдэв байхгүй)", - "no_body_content": "(Биеийн агуулга байхгүй)", - "no_preview_available": "Урьдчилан үзэх боломжгүй", - "show_quoted_text": "Ишлэсэн текстийг харуулах", - "hide_quoted_text": "Иш татсан текстийг нуух", - "loading_email": "Имэйл ачаалж байна...", - "loading": "Ачааж байна...", - "reply": "Хариулах", - "reply_all": "Бүгдэд нь хариулах", - "forward": "Урагшаа", - "delete": "Устгах", - "archive": "Архив", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "од", - "unstar": "Одгүй болгох", - "mark_unread": "Уншаагүй гэж тэмдэглэх", - "mark_read": "Уншсан гэж тэмдэглэх", - "unread": "Уншаагүй", - "read": "унших", - "spam_short": "Спам", - "not_spam_short": "Спам биш", - "move": "Хөдөлгөөн", - "print": "Хэвлэх", - "view_source": "Эх сурвалжийг харах", - "fullscreen": "Бүтэн дэлгэц", - "exit_fullscreen": "Бүтэн дэлгэцээс гарах", - "export_email": ".eml", - "forward_as_attachment": "Хавсралт болгон дамжуулах", - "import_email": "Импорт .eml эсвэл .zip", - "keyboard_shortcuts": "Гарын товчлол (?)", - "email_source": "Имэйл эх сурвалж", - "draft_banner": "Энэ зурвас нь ноорог байна", - "edit_draft": "Засварлах", - "copy_source": " Түр санах ой руу хуулах", - "source_copied": "Эх сурвалжийг санах ой руу хуулсан", - "attachments": "Хавсралт", - "important": "Чухал", - "download": "Татаж авах", - "download_all": "Бүгдийг нь татаж авах", - "from": "", - "to": "Хэнд", - "cc": "Хуулбар", - "bcc": "Нууц хуулбар", - "date": "Огноо", - "subject": "Сэдэв", - "show_details": "Дэлгэрэнгүйг харуулах", - "hide_details": "Дэлгэрэнгүй мэдээллийг нуух", - "external_content_warning": "Зураг болон гадаад контентыг хаасан", - "load_external_content": "Зураг ачаалах", - "trust_sender": "Энэ илгээгчид үргэлж итгэ", - "back_to_list": "Жагсаалт руу буцах", - "view_contact": "Харилцагчийг харах", - "message_details": "Зурвасны дэлгэрэнгүй мэдээлэл", - "more_reply_options": "Бусад хариултын сонголтууд", - "set_tag": "Таг тавих", - "tag": "Таг", - "more_actions": "Дэлгэрэнгүй үйлдэл", - "previous": "Өмнөх", - "next": "Дараагийн", - "move_to": "...", - "remove_tag": "Ташгыг арилгах", - "tag_filter_placeholder": "Шүүлтүүрийн шошго", - "tag_no_matches": "Тохирох шошго байхгүй", - "more_count": "+{count} дэлгэрэнгүй", - "characters_count": "{count} тэмдэгт", - "quick_reply_placeholder": "Шуурхай хариу бичнэ үү...", - "more_options": "Бусад сонголтууд", - "sending": "Илгээж байна...", - "security_authentication": "Аюулгүй байдал & Баталгаажуулалт", - "technical_details": "Техникийн мэдээлэл", - "message_id_label": "Мессежийн дугаар:", - "reply_to_label": "Хариу бичих:", - "delivery_time_label": "Хүргэх хугацаа:", - "conversation_part_label": "Ярилцлагын хэсэг:", - "previous_messages": "{count} өмнөх зурвас", - "previous_messages_plural": "{count} өмнөх зурвасууд", - "time": { - "day": "өдөр", - "days": "өдөр", - "hour": "цаг", - "hours": "цаг", - "minute": "мин", - "minutes": "минут" - }, - "unknown_sender": "Үл мэдэгдэх", - "recipient_me": "би", - "recipient_and_others": "{name} ба {count} бусад", - "recipient_to_prefix": "Хэнд:", - "authentication": { - "title": "Баталгаажуулалт", - "status": { - "verified": "Баталгаажсан", - "warning": "Анхааруулга", - "none": "Баталгаажуулаагүй" - }, - "spf": { - "pass": "SPF нэвтрүүлэх", - "fail": "SPF амжилтгүй боллоо", - "none": "SPF-гүй" - }, - "dkim": { - "pass": "DKIM хүчинтэй", - "fail": "DKIM хүчингүй", - "none": "DKIM байхгүй" - }, - "dmarc": { - "pass": "DMARC амжилттай", - "fail": "DMARC амжилтгүй боллоо", - "none": "DMARC байхгүй" - }, - "spam_score": "Спамын оноо", - "tooltip_spf": "Илгээгчийн бодлогын хүрээ: Илгээж буй сервер нь домэйны нэрийн өмнөөс имэйл илгээх эрхтэй болохыг баталгаажуулна", - "tooltip_dkim": "DomainKeys Identified Mail: Имэйлийг криптограф гарын үсэг ашиглан дамжуулж байхдаа өөрчлөөгүйг баталгаажуулна", - "tooltip_dmarc": "Домэйн дээр суурилсан мессежийн баталгаажуулалт, тайлагнах, нийцүүлэх: SPF болон DKIM нь илгээгчийн домайнтай нийцэж байгаа эсэхийг баталгаажуулж, алдаа дутагдлын бодлогыг тогтооно", - "policy": "Бодлого", - "result": { - "pass": "Дамжуулах", - "fail": "Бүтэлгүйтсэн", - "softfail": "Зөөлөн алдаа", - "neutral": "Төвийг сахисан", - "permerror": "Байнгын алдаа", - "temperror": "Түр зуурын алдаа", - "none": "Байхгүй" - } - }, - "details": { - "recipients_routing": "Хүлээн авагч ба чиглүүлэлт", - "authentication_security": "Баталгаажуулалт ба аюулгүй байдал", - "identifiers_threading": "Таниулагч ба утас холбох", - "mailing_list": "Мэйлийн жагсаалт", - "message_properties": "Мессежийн шинж чанарууд", - "sent": "Илгээсэн", - "received": "Хүлээн авсан", - "delivery_time": "Хүргэх хугацаа", - "in_reply_to": "Хариу бичих", - "references": "Ашигласан материал", - "thread_id": "Сэдвийн ID", - "size": "Хэмжээ", - "mime_type": "MIME төрөл", - "attachments_summary": "{count} файлууд · {size}", - "list_id": "Жагсаалтын ID", - "list_help": "Тусламжийн жагсаалт", - "list_post": "Жагсаалтын нийтлэл", - "list_unsubscribe": "Бүртгэлээ цуцлах", - "iprev": "Урвуу DNS", - "spam_status": "Спамын статус", - "ai_verdict": "AI-ийн шийдвэр", - "account": " Данс", - "no_subject": "(сэдэв байхгүй)" - }, - "headers": { - "routing": "Чиглүүлэлт", - "received": "Хүлээн авсан", - "message_id": "Зурвасны ID", - "list_info": "Мэдээллийн жагсаалт" - }, - "tooltips": { - "reply": "Хариулт (r)", - "reply_all": "Бүгдэд нь хариулах (a)", - "forward": "Урагшаа (f)", - "archive": "Архив (д)", - "delete": "Устгах (# эсвэл Del)", - "star": "Од(ууд)", - "unstar": "Одгүй(s)", - "compose": "Бичих (c)", - "previous": "Өмнөх имэйл", - "next": "Дараагийн имэйл", - "edit_draft": "Ноорог засах" - }, - "spam": { - "button_title": "Спам мэдээлэх", - "not_spam_title": "Хууль ёсны гэж тэмдэглэх", - "toast_success": "Хог хаяг руу шилжсэн", - "toast_batch": "{count} имэйлийг хог хаяг руу зөөсөн", - "toast_undo": "Буцаах", - "toast_not_spam_success": "Ирсэн имэйл рүү шилжсэн", - "toast_not_spam_batch": "{count} имэйлийг Ирсэн имэйл рүү зөөсөн", - "error": "Спамыг мэдээлж чадсангүй", - "error_not_spam": "Имэйл сэргээж чадсангүй" - }, - "unsubscribe_banner": { - "label": "Мэдээллийн хуудас", - "button": "Бүртгэлээ цуцлах", - "confirm_title": "Энэ илгээгчийн бүртгэлээ цуцлах уу?", - "confirm_button": "Баталгаажуулах", - "cancel": "Цуцлах", - "success_http": "Бүртгэлээ цуцлах хуудас шинэ таб дээр нээгдсэн", - "success_mailto": "Захиалгаа цуцлах имэйл илгээсэн", - "confirm_message_http": "Бүртгэлээ цуцлах хуудас шинэ таб дээр нээгдэнэ.", - "confirm_message_mailto": "Илгээгч рүү бүртгэлээ цуцлах имэйл илгээгдэх болно.", - "error": "Бүртгэлээ цуцлах боломжгүй", - "dismiss": "Хаах" - }, - "calendar_invitation": { - "loading": "Үйл явдлын дэлгэрэнгүй мэдээллийг ачаалж байна...", - "title": "Календарийн урилга", - "published_title": "Нийтлэгдсэн арга хэмжээ", - "response_title": "Үйл явдлын хариу үйлдэл", - "update_title": "Үйл явдлын шинэчлэлт", - "counter_title": "Сөрөг санал", - "refresh_title": "Хүсэлтийг сэргээх", - "declined_counter_title": "Сөрөг саналаас татгалзсан", - "cancelled_title": "Үйл явдал цуцлагдсан", - "organizer": "Зохион байгуулсан {name}", - "organizer_label": "Зохион байгуулсан", - "attendees": "{count, plural, one {# оролцогч} other {# оролцогч}}", - "accept": "Зөвшөөрөх", - "maybe": "Магадгүй", - "decline": "Бууралт", - "add_to_calendar": "Хуанлид нэмэх", - "added": "Хуанлид нэмсэн", - "rsvp_sent": "Хариулт илгээсэн", - "parse_error": "Урилгыг уншиж чадсангүй", - "action_failed": "Тэр хуанлийн үйлдлийг хийж чадсангүй.", - "no_calendar": "Хуанли байхгүй", - "published_info": "Энэ үйл явдлыг лавлах зорилгоор хуваалцсан.", - "response_info": "Энэ мессеж нь оролцогчийн хариултыг агуулж байна.", - "response_info_organizer": "Энэ оролцогчийн хариулт нь таны үйл явдлыг шинэчилнэ.", - "update_info": "Энэ зурвас нь одоо байгаа үйл явдлыг шинэчилнэ.", - "counter_info": "Энэ зурвас нь үйл явдалд өөрчлөлт оруулахыг санал болгож байна.", - "counter_info_organizer": "Энэ оролцогч таны арга хэмжээнд өөрчлөлт оруулахыг санал болгосон.", - "refresh_info": "Энэ зурвас нь хамгийн сүүлийн үеийн үйл явдлын дэлгэрэнгүй мэдээллийг шаардаж байна.", - "refresh_info_organizer": "Оролцогч хамгийн сүүлийн үеийн үйл явдлын дэлгэрэнгүй мэдээллийг хүссэн.", - "declined_counter_info": "Зохион байгуулагч сөрөг саналаас татгалзсан.", - "authentication_failed_info": "Энэ урилгын имэйлийн баталгаажуулалтыг шалгаж чадсангүй. Хуанлийн үйлдэлд болгоомжтой хандах.", - "authentication_missing_info": "Энэ урилгад баталгаажуулсан имэйлийн баталгаажуулалт ороогүй болно. Ер бусын зүйл байвал зохион байгуулагчтай дэлгэрэнгүй мэдээллийг баталгаажуулна уу.", - "sender_mismatch_info": "Энэ урилгыг {sender}-с илгээсэн бол хуанлийн өгөгдөлд жагсаасан зохион байгуулагч нь {organizer} байна.", - "sender_mismatch_unverified_info": "Энэ урилгыг {sender}-с илгээсэн бөгөөд хуанлийн өгөгдөлд жагсаасан зохион байгуулагч нь {organizer} бөгөөд мессежийг баталгаажуулж чадсангүй.", - "organizer_role": "Та энэ арга хэмжээг зохион байгуулна", - "your_response": "Таны хариулт: {status}", - "response_needed": "Хариулт хэрэгтэй", - "response_accepted": "Хүлээн зөвшөөрсөн", - "response_tentative": "Урьдчилсан", - "response_declined": "Татгалзсан", - "response_delegated": "Төлөөлөгдсөн", - "actor_sent_info": "{name} илгээсэн.", - "actor_response_info": "{name} {status} гэж хариулав.", - "actor_counter_info": "{name} энэ үйл явдалд өөрчлөлт оруулахыг санал болгосон.", - "actor_refresh_info": "{name} хамгийн сүүлийн үеийн үйл явдлын дэлгэрэнгүй мэдээллийг асуув.", - "actor_declined_counter_info": "{name} эсрэг саналаас татгалзсан.", - "actor_note": "Жич: {comment}", - "actor_unknown": "Хэн нэгэн", - "proposed_changes": "Санал болгож буй өөрчлөлтүүд", - "change_title": "Гарчиг", - "change_time": "Цаг", - "change_location": "Байршил", - "change_description": "Тодорхойлолт", - "change_empty": "Байхгүй", - "change_from_to": "{from} → {to}", - "apply_proposal": "Санал болгож буй өөрчлөлтийг хэрэгжүүлэх", - "proposal_applied": "Санал болгож буй өөрчлөлтүүдийг хэрэгжүүлсэн.", - "review_proposal": "Саналыг хянан үзэх", - "review_request": "Хүсэлтийг хянан үзэх", - "view_in_calendar": "Хуанлиар харах", - "select_calendar": "Хуанли сонгох", - "already_in_calendar": "Таны хуанлид аль хэдийн орсон", - "request_info": "Таныг энэ арга хэмжээнд урьсан. Зохион байгуулагчид боломжоо мэдэгдэхийн тулд хариу бичнэ үү.", - "cancel_info": "Зохион байгуулагч энэ арга хэмжээг цуцалсан.", - "event_updated": "Шинэчлэх #{sequence}", - "event_status_tentative": "Урьдчилсан", - "event_status_cancelled": "Цуцлагдсан", - "expand": "Дэлгэрэнгүйг харуулах", - "collapse": "Дэлгэрэнгүй мэдээллийг нуух" - }, - "send": "Илгээх", - "more": "илүү", - "scheduled_banner": "{date}", - "scheduled_send_created": "Имэйл илгээхээр товлосон", - "cancel_scheduled_send": "Илгээхийг цуцлах", - "reschedule_send": "Хуваарь солих", - "cancel_and_edit": "Цуцлах, засварлах", - "cancel_and_compose_again": "Цуцлаад дахин бичих", - "reschedule_prompt": "Шинэ огноо/цаг оруулна, ж.нь. 2026-05-04Т15:30", - "scheduled_actions_only": "Хуваарьт харах нь зөвхөн төлөвлөсөн үйлдлүүдийг дэмждэг", - "undo_send_scheduled": "Илгээхээр товлосон мессеж", - "undo_send": "Илгээхийг буцаах", - "contact_sidebar": { - "title": "Холбоо барих", - "close": "Хажуугийн самбарыг хаах", - "action_email": "И-мэйл", - "action_email_title": "Имэйл илгээх", - "action_copy": "Хуулбар", - "action_copy_title": "Имэйл хуулах", - "action_edit_title": "Харилцагчийг засах", - "section_emails": "Имэйл", - "section_phones": "Утас", - "section_organizations": "Байгууллага", - "section_addresses": "Хаяг", - "section_notes": "Тэмдэглэл", - "not_in_contacts": "Таны харилцагчид байхгүй", - "add_to_contacts": "Харилцагчдад нэмэх", - "copied": "Хуулбарласан!", - "copy_failed": "Хуулж чадсангүй" - }, - "send_now": "Одоо илгээх" - }, - "email_composer": { - "read_receipt_on": "Уншсан баримт хүссэн (идэвхгүй болгохын тулд дарна уу)", - "read_receipt_off": "Уншсан баримт хүсэх", - "dsn_on": "Хүргэлтийн мэдэгдэл хүссэн (идэвхгүй болгох бол дарна уу)", - "dsn_off": "Хүргэлтийн мэдэгдэл хүсэх", - "format_plain_text": "Энгийн текст рүү шилжих", - "format_rich_text": "Форматтай текст (HTML) рүү шилжих", - "require_tls_on": "Шифрлэгдсэн хүргэлт шаардлагатай (идэвхгүй болгох бол дарна уу)", - "require_tls_off": "Шифрлэгдсэн хүргэлт шаардах (TLS)", - "new_message": "Шинэ мессеж", - "signature_edit_hint": "Гарын үсгийг засахын тулд давхар товшино уу", - "reply": "Хариулах", - "reply_all": "Бүгдэд нь хариулах", - "forward": "Урагшаа", - "reply_to": "Хариулах", - "reply_all_to": "Бүгдэд нь хариулах", - "forward_message": "Урагшаа", - "from": "", - "to": "руу", - "cc": "Хуулбар", - "bcc": "Нууц хуулбар", - "subject": "Сэдэв", - "body_placeholder": "Мессеж бичнэ үү...", - "send": "Илгээх", - "cancel": "Цуцлах", - "attach": "Хавсаргах", - "attach_photos": "Зураг, Видео", - "attach_files": "Файл", - "discard": "Хаях", - "discard_draft_title": "Ноорог устгах уу?", - "discard_draft_confirm": "Танд хадгалагдаагүй өөрчлөлтүүд байна. Та энэ ноорогоос татгалзахыг хүсэж байна уу?", - "saving": "Хадгалж байна...", - "sending": "Илгээж байна...", - "add_link": "Холбоос нэмэх", - "link_url_prompt": "URL-г оруулна уу", - "draft_saved": "Ноорог хадгалсан", - "save_failed": "Хадгалж чадсангүй", - "to_placeholder": "Хүлээн авагчийн имэйл хаягууд", - "cc_placeholder": "Cc хүлээн авагчид", - "bcc_placeholder": "Bcc хүлээн авагчид", - "subject_placeholder": "Сэдэв", - "cc_label": "Хуулбар", - "bcc_label": "Нууц хуулбар", - "subject_label": "Гэдэв:", - "file_size_kb": "КБ", - "prefix": { - "forward": "Дахин:", - "reply": "Дахин:" - }, - "no_subject": "(Сэдэв байхгүй)", - "unknown_sender": "Үл мэдэгдэх", - "quote": { - "reply_header": "{date} дээр {sender} бичсэн:", - "forward_header": "---------- Дамжуулсан мессеж ----------", - "from": "Хэнээс: {sender}", - "date": "Огноо: {date}", - "subject": "Гэдэв: {subject}", - "to": "Хэнд: {recipients}" - }, - "remove_sub_address": "Дэд хаягийг устгах", - "from_override": { - "toggle_off": "Дараах", - "toggle_on": "Дараахыг цуцлах", - "toggle_tooltip": "From нэр хаягийг чөлөөтэй засварлана. Таны хувийн мэдээллээр шуудан илгээгдсэн хэвээр байна.", - "name_label": "Нэрнээс", - "name_placeholder": "Нэр", - "email_label": "Имэйл хаягаас", - "email_placeholder": "user@example.com", - "envelope_notice": "Хэрэв таны сервер энэ хаягийг дугтуйн илгээгчээр хүлээн авахгүй бол оронд нь {identity} ашиглагдах бөгөөд хүлээн авагчид үүнийг Return-Path гарчигт харж болно." - }, - "use_template": "Загвар", - "save_as_template": "Загвар болгон хадгалах", - "validation": { - "recipient_required": "Илгээхийн тулд хүлээн авагч нэмэх", - "body_required": "Мессеж бичих эсвэл файл хавсаргах", - "attachments_uploading": "Хавсралтуудыг байршуулсаар байгаа бөгөөд дуусмагц илгээх болно", - "attachment_upload_failed": "Илгээгүй — хавсралтыг байршуулж чадсангүй. Үүнийг устгаад дахин оролдоно уу." - }, - "upload_progress": "{uploaded} / {total}", - "upload_cancel": "Байршуулалтыг цуцлах", - "upload_failed": "{filename}", - "attachment_too_large": "\"{name}\" сервер зөвшөөрөхөөс том байна (нэг файлд {max})", - "attachments_total_too_large": "Хавсралтууд серверийн нэг захидалд ногдох {max} хязгаараас хэтэрнэ", - "drop_files": "Файлуудыг хавсаргах", - "show_less": "Бага харуулах", - "send_failed": "Имэйл илгээж чадсангүй", - "send_timeout": "Сервер хариу өгөхөө больсон. Дахин илгээхийн өмнө \"Илгээсэн\" фолдероо шалгана уу — мессеж унтарсан байж магадгүй.", - "continue_draft": "Ноорог үргэлжлүүлэх", - "close_draft_title": "Ноорог хадгалах эсвэл устгах уу?", - "close_draft_message": "Танд хадгалагдаагүй өөрчлөлтүүд байна. Та үүнийг ноорог болгон хадгалах уу эсвэл хаях уу?", - "smime_sign_on": "S/MIME гарын үсэг зурахыг идэвхжүүлсэн", - "smime_sign_off": "S/MIME гарын үсэг зурахыг идэвхжүүлэх", - "smime_encrypt_on": "S/MIME шифрлэлтийг идэвхжүүлсэн", - "smime_encrypt_off": "S/MIME шифрлэлтийг идэвхжүүлэх", - "smime_encrypt_unavailable": "S/MIME шифрлэлт боломжгүй - хүлээн авагчийн гэрчилгээ алга", - "smime_unlock_title": "S/MIME түлхүүрийн түгжээг тайлах", - "smime_unlock_message": "S/MIME гарын үсэг зурах түлхүүрийнхээ түгжээг тайлахын тулд нэвтрэх үгийг оруулна уу.", - "smime_unlock_button": "Түгжээг тайлах", - "smime_passphrase_placeholder": "Нууц үг", - "forgot_attachment": { - "title": "Та хавсралтаа мартсан уу?", - "message": "Таны зурваст \"{keyword}\" гэж дурдсан боловч ямар ч файл хавсаргаагүй байна. Ямартай ч явуулах уу?", - "send_anyway": "Ямар ч байсан илгээх", - "back": "Засварлах руу буцах" - }, - "empty_subject": { - "title": "Гарчиггүй илгээх үү?", - "message": "Энэ зурваст гарчиг байхгүй. Ямартай ч явуулах уу?", - "dont_ask_again": "Дахиж битгий асуу", - "send_anyway": "Ямар ч байсан илгээнэ үү", - "back": "Засварлах руу буцах" - }, - "schedule_send": "Хуваарь илгээх", - "schedule_send_description": "Сервер энэ мессежийг хэзээ гаргахыг сонгоно уу.", - "schedule_send_required": "Огноо, цаг сонгох.", - "schedule_send_invalid": "Хүчинтэй огноо, цагаа оруулна уу.", - "schedule_send_future": "Ирээдүйн огноо, цагийг сонгох.", - "schedule_send_too_late": "Энэ хугацаа нь серверийн зөвшөөрснөөс хойш байна.", - "schedule_send_unsupported": "Энэ бүртгэлд хуваарьт илгээхийг дэмждэггүй.", - "schedule_send_cleanup_warning": "Хуваарьт илгээлт үүсгэсэн боловч ноорог цэвэрлэж чадсангүй.", - "send_delay_unsupported": "Илгээлтийн саатал энэ бүртгэлд дэмжигдээгүй.", - "send_delay_unsupported_confirm": "Энэ бүртгэл нь илгээх саатлыг дэмждэггүй. Оронд нь шууд явуулах уу?", - "recipient_edit_email": "Имэйл хаягийг засах", - "recipient_edit_name": "Дэлгэцийн нэрийг засах", - "recipient_email_placeholder": "Имэйл хаяг", - "recipient_name_placeholder": "Дэлгэцийн нэр", - "autocomplete_search_server": "Серверээс хайх", - "autocomplete_searching": "Хайж байна...", - "toolbar": { - "bold": "Болд", - "italic": "Налуу", - "underline": "Доогуур зураас", - "strikethrough": "Үзсэн зураас", - "text_color": "Текстийн өнгө", - "remove_color": "Өнгийг арилгах", - "heading_1": "Гарчиг 1", - "heading_2": "Гарчиг 2", - "bullet_list": "Сумын жагсаалт", - "ordered_list": "Захиалгат жагсаалт", - "quote": "Эшлэл", - "code_block": "Код блок", - "align_left": "Зүүнд зэрэгцүүлэх", - "align_center": "Төвийг тэгшлэх", - "align_right": "Баруун тийш зэрэгцүүлэх", - "text_direction": "Текстийн чиглэл (RTL/LTR)", - "link": "Холбоос", - "table": "Хүснэгт", - "clear_formatting": "Форматыг арилгах", - "undo": "Буцах", - "redo": "Дахин хийх", - "add_row_above": "Дээрх мөр нэмэх", - "add_row_below": "Доор нэмэх", - "add_column_before": "Өмнө нь багана нэмнэ үү", - "add_column_after": "0", - "delete_row": "Мөр устгах", - "delete_column": "Баганыг устгах", - "toggle_header_row": "Толгойн мөрийг сэлгэх", - "delete_table": "Хүснэгт устгах", - "pick_size": "Хэмжээ сонгох", - "font_size": "Фонтын хэмжээ", - "font_size_default": "Үндсэн", - "background_color": "Дэвсгэр өнгө", - "remove_background_color": "Дэвсгэр өнгийг арилгах" - }, - "send_filing_warning": "Илгээсэн - гэхдээ илгээсний дараах цэвэрлэгээ амжилтгүй болсон тул хуучирсан ноорог үлдэж болзошгүй." - }, - "confirm_dialog": { - "confirm": "Баталгаажуулах", - "cancel": "Цуцлах" - }, - "common": { - "loading": "Ачааж байна...", - "error": "Алдаа", - "success": "Амжилт", - "cancel": "Цуцлах", - "save": "Хадгалах", - "delete": "Устгах", - "edit": "Засварлах", - "close": "Хаах", - "search": "Хайлт", - "refresh": "Сэргээх", - "settings": "Тохиргоо", - "help": "Тусламж", - "logout": "Гарах", - "yes": "Тийм", - "no": "Үгүй", - "unknown": "Үл мэдэгдэх", - "app_title": "Вэбмэйл", - "reconnecting": "Холболт тасарсан. Дахин холбогдохоор оролдож байна...", - "rate_limited_title": "Сервер баталгаажуулалтын оролдлогыг түр хугацаанд хязгаарлалаа.", - "rate_limited_detail": "Bulwark түгжигдэхээс зайлсхийхийн тулд арын хүсэлтийг түр зогсоосон. {seconds}-р дахин оролдож байна.", - "rate_limited_action_title": " Түгжигдэхээс зайлсхийхийн тулд хүсэлтийг түр зогсоосон.", - "rate_limited_action_detail": "Bulwark илүү баталгаажуулсан хүсэлт илгээхийн өмнө серверийн хөргөлт дуусахыг хүлээж байна. {seconds}-ээр дахин оролдоно уу.", - "pull_to_refresh": "Шинэчлэхийн тулд татна уу", - "release_to_refresh": "Шинэчлэхийн тулд суллана уу", - "refreshing": "Шинэчилж байна…" - }, - "notifications": { - "email_sent": "Имэйл амжилттай илгээгдсэн", - "email_deleted": "Имэйл устгасан", - "email_archived": "Имэйл архивлагдсан", - "emails_deleted": "{count, plural, other {# имэйл устгагдлаа}}", - "emails_archived": "{count, plural, other {# имэйл архивлагдлаа}}", - "error_archiving": "Имэйлийг архивлаж чадсангүй", - "error_updating": "Имэйлийг шинэчилж чадсангүй", - "email_starred": "И-мэйл одоор тэмдэглэгдсэн", - "email_unstarred": "И-мэйл одыг арилгасан", - "email_marked_read": "И-мэйл уншсан гэж тэмдэглэсэн", - "email_marked_unread": "И-мэйл уншаагүй гэж тэмдэглэгдсэн", - "copied_to_clipboard": " Түр санах ой руу хуулсан", - "source_copied": "Эх сурвалжийг санах ой руу хуулсан", - "error_sending": "Имэйл илгээж чадсангүй", - "error_deleting": "Имэйл устгаж чадсангүй", - "error_loading": "Имэйл ачаалж чадсангүй", - "new_email": "Шинэ имэйл", - "new_email_from": "{sender}", - "click_to_view": "Үзэх бол товшино уу", - "email_moved": "И-мэйл шилжүүлсэн", - "emails_moved": "{count} имэйлийг зөөсөн", - "moved_to_mailbox": "{mailbox}", - "move_failed": "Зөөж чадсангүй", - "move_error": "Имэйлүүдийг сонгосон хавтас руу зөөж чадсангүй", - "email_tagged": "И-мэйл хаяглагдсан", - "emails_tagged": "{count} мэйл хаяглагдсан", - "tag_failed": "Табга хийж чадсангүй", - "identity_created": "Таниулбарыг амжилттай үүсгэлээ", - "identity_updated": "Таниулбарыг амжилттай шинэчиллээ", - "identity_deleted": "Таниулбарыг устгалаа", - "identity_set_primary": "Үндсэн таниулбар шинэчлэгдсэн", - "identity_create_failed": "Таниулбар үүсгэж чадсангүй: {error}", - "identity_update_failed": "Таниулбарыг шинэчилж чадсангүй: {error}", - "identity_delete_failed": "Таниулбарыг устгаж чадсангүй: {error}", - "identity_unauthorized": "Та энэ имэйл хаягаас илгээх эрхгүй", - "identity_not_found": "Таниулбар олдсонгүй", - "vacation_saved": "Амралтын хариулагчийн тохиргоог хадгалсан", - "vacation_save_failed": "Амралтын хариулагчийн тохиргоог хадгалж чадсангүй", - "filters_saved": "Шүүлтүүрийг амжилттай хадгаллаа", - "filters_save_failed": "Шүүлтүүрийг хадгалж чадсангүй", - "filters_deleted": "Шүүлтүүрийн дүрмийг устгасан", - "templates_exported": "Загваруудыг амжилттай экспортоллоо", - "templates_imported": "{count, plural, one {# загвар} other {# загвар}}импортолсон", - "templates_import_errors": "Зарим загварыг импортлох боломжгүй байна", - "templates_import_empty": "Файлд ямар ч загвар олдсонгүй", - "export_email_error": "Имэйл экспорт хийж чадсангүй", - "import_email_success": "Имэйл амжилттай импортлогдсон", - "import_email_error": "Имэйл импортлож чадсангүй" - }, - "date": { - "today": "Өнөөдөр", - "yesterday": "Өчигдөр", - "this_week": "Энэ долоо хоногт", - "last_week": "Өнгөрсөн долоо хоног", - "this_month": "Энэ сард", - "older": "Ахмад", - "just_now": "Дөнгөж сая", - "minutes_ago": "{count} минутын өмнө", - "minutes_ago_plural": "{count} минутын өмнө", - "hours_ago": "{count} цагийн өмнө", - "hours_ago_plural": "{count} цагийн өмнө", - "days_ago": "{count} өдрийн өмнө", - "days_ago_plural": "{count} хоногийн өмнө" - }, - "language": { - "title": "Хэл", - "english": "Англи", - "french": "Франсаис", - "japanese": "日本語", - "spanish": "Испанол", - "italian": "Италиано", - "german": "Deutsch", - "dutch": "Нидерланд", - "portuguese": "Португали", - "russian": "Русский", - "select_language": "Хэл сонгох", - "switch_to_english": "Англи хэл рүү шилжих", - "switch_to_french": "Франц хэл рүү шилжих", - "switch_to_japanese": "Япон хэл рүү шилжих", - "switch_to_spanish": "Испани хэл рүү шилжих", - "switch_to_italian": "Итали хэл рүү шилжих", - "switch_to_german": "Герман хэл рүү шилжих", - "switch_to_dutch": "Нидерланд хэл рүү шилжих", - "switch_to_portuguese": "Португал хэл рүү шилжих", - "switch_to_russian": "Орос хэл рүү шилжих", - "switching": "Хэл сольж байна...", - "switch": "Хэл солих", - "current": "Одоогийн хэл", - "polish": "Польски", - "switch_to_polish": "Польш хэл рүү шилжих", - "en": "Англи хэл", - "fr": "Франсаис", - "de": "Deutsch", - "es": "Испанол", - "it": "Италиано", - "ja": "日本語", - "ko": "한국어", - "lv": "Латвиешу", - "nl": "Нидерланд", - "pl": "Полски", - "pt": "Португали", - "he": "עברית", - "ru": "Русский", - "sk": "Словенчина", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Фарси хэл рүү шилжих", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Монгол хэл рүү шилжих" - }, - "settings": { - "title": "Тохиргоо", - "back_to_mail": "Мэйл рүү буцах", - "save_success": "Тохиргоог амжилттай хадгаллаа", - "import_success": "Тохиргоог амжилттай импортоллоо", - "import_error": "Тохиргоог оруулж чадсангүй", - "reset_confirm": "Та бүх тохиргоог өгөгдмөл рүү буцаахдаа итгэлтэй байна уу?", - "unsaved_changes": "Танд хадгалагдаагүй өөрчлөлтүүд байна", - "discard_changes": "Хадгалагдаагүй өөрчлөлтүүдийг устгах уу?", - "discard": "Хасах", - "keep_editing": "Засварлаж байгаарай", - "search_placeholder": "Хайлтын тохиргоо", - "search_clear": "Хайлтыг арилгах", - "search_no_results": "Тохирох тохиргоо байхгүй", - "tabs": { - "appearance": "Гадаад төрх", - "language": "Хэл, бүс, цаг", - "email": "Имэйлийн зан байдал", - "composer": "Хөгжмийн зохиолч", - "privacy": "Нууцлал ба аюулгүй байдал", - "account": " Данс", - "identities": "Тэмдэгтүүд", - "vacation": "Амралтын хариулагч", - "advanced": "Дэвшилтэт", - "calendar": "Хуанли", - "filters": "Шүүлтүүр", - "templates": "Загварууд", - "folders": "Хавтас", - "keywords": "Тагууд", - "security": "Аюулгүй байдал", - "files": "Файл", - "contacts": "Харилцах утас", - "encryption": "Шифрлэлт", - "protocol_handlers": "Өгөгдмөл програмууд", - "sidebar_apps": "Хажуугийн аппууд", - "notifications": "Мэдэгдэл", - "layout": "Байршил", - "reading": "Унших", - "composing": "Зохиоглох", - "downloads": "Татаж авах", - "content_senders": "Агуулга ба Илгээгчид", - "about_data": "Тухай & Өгөгдөл", - "debug": "Дабаглах" - }, - "tab_groups": { - "general": "Ерөнхий", - "account": "Бүртгэл & Иргэний үнэмлэх", - "organization": "Шуудангийн байгууллага", - "apps": "Програмууд", - "system": "Систем", - "appearance": "Гадаад төрх", - "mail": "Мэйл", - "privacy": "Нууцлал ба Аюулгүй байдал", - "advanced": "Дэвшилтэт" - }, - "appearance": { - "message_list_order": { - "label": "Зурвасын жагсаалтын дараалал", - "description": "Зурвасууд жагсаалтад хэрхэн эрэмбэлэгдэх. Эрэмбэлэлтийг сервер хийдэг тул зөвхөн ачаалагдсан зурвас биш, бүх хавтсанд үйлчилнэ.", - "preset": { - "chronological": "Он цагийн дарааллаар (шинэ нь эхэнд)", - "unread_first": "Уншаагүй нь эхэнд", - "starred_first": "Одтой нь эхэнд", - "tagged_first": "Шошготой нь эхэнд", - "custom": "Өөрийн" - }, - "tag_label": "Шошго", - "advanced_show": "Нарийвчилсан: эрэмбийн түвшнүүдийг засах", - "advanced_hide": "Нарийвчилсныг нуух", - "criterion_label": "{n}-р түвшний шалгуур", - "direction_label": "{n}-р түвшний чиглэл", - "criteria": { - "unread": "Уншаагүй", - "starred": "Одтой", - "tag": "Шошго", - "receivedAt": "Хүлээн авсан огноо", - "sentAt": "Илгээсэн огноо", - "from": "Илгээгч", - "subject": "Гарчиг", - "size": "Хэмжээ" - }, - "direction": { - "unread_first": "Уншаагүй нь эхэнд", - "read_first": "Уншсан нь эхэнд", - "starred_first": "Одтой нь эхэнд", - "unstarred_first": "Одгүй нь эхэнд", - "tagged_first": "Шошготой нь эхэнд", - "untagged_first": "Шошгогүй нь эхэнд", - "newest_first": "Шинэ нь эхэнд", - "oldest_first": "Хуучин нь эхэнд", - "a_to_z": "А → Я", - "z_to_a": "Я → А", - "largest_first": "Том нь эхэнд", - "smallest_first": "Жижиг нь эхэнд" - }, - "add_level": "Түвшин нэмэх", - "remove_level": "Түвшин устгах", - "move_up": "Дээш зөөх", - "move_down": "Доош зөөх", - "tiebreak_note": "Дээд тал нь гурван түвшин; дараагийн түвшнүүд тэнцсэн үед шийднэ. «Шинэ нь эхэнд» үргэлж хамгийн сүүлд хэрэглэгдэнэ.", - "scope": { - "label": "Бүх хавтсанд хэрэглэх", - "description": "Унтраалттай үед дараалал зөвхөн Ирсэн хавтсанд үйлчилнэ; бусад хавтас он цагийн дарааллаараа үлдэнэ." - }, - "unsupported_note": "Таны мэйл сервер уншсан төлөв, од эсвэл шошгоор эрэмбэлэхийг дэмждэггүй. Эдгээр түвшнийг алгасаж, жагсаалт он цагийн дарааллаараа үлдэнэ." - }, - "title": "Гадаад төрх", - "description": "Вэбмэйлийнхээ харагдах байдал, мэдрэмжийг өөрчилнө", - "theme": { - "label": "Сэдэв", - "description": "Өөрийн хүссэн өнгөний загвараа сонго", - "light": "Гэрэл", - "dark": "Харанхуй", - "system": "Систем" - }, - "language": { - "label": "Хэл", - "description": "Та дуртай хэлээ сонгоно уу" - }, - "font_size": { - "label": "фонтын хэмжээ", - "description": "Текстийн хэмжээг илүү сайн уншихын тулд тохируулна уу", - "small": "Жижиг", - "medium": "Дунд", - "large": "Том" - }, - "list_density": { - "label": "Нягт", - "description": "UI-ийн хоорондох зай болон дэвсгэрийг хянах", - "extra_compact": "Нэмэлт авсаархан", - "compact": "Компакт", - "regular": "Тогтмол", - "comfortable": "Тав тухтай" - }, - "animations": { - "label": "Анимацийг идэвхжүүлэх", - "description": "Гөлгөр шилжилт, эффектийг харуулах" - }, - "toolbar_position": { - "label": "Хэрэгслийн мөрний байрлал", - "description": "И-мэйл үйлдлийн товчлууруудыг (Хариулах, Архивлах, Устгах гэх мэт) хаана харуулах вэ", - "top": "Шилдэг", - "below_subject": "Сэдвийн доор" - }, - "toolbar_labels": { - "label": "Хэрэгслийн самбарын шошгыг харуулах", - "description": "Текст шошгыг багаж самбарын дүрсний хажууд харуул. Дүрстэй танилцсаны дараа зай хэмнэхийн тулд идэвхгүй болгоно уу." - }, - "hide_account_switcher": { - "label": "Sidebar Account Switcher-ийг нуух", - "description": "Хавтрын хажуугийн дээд хэсэгт байрлах бүртгэл сонгогчийг нуу. Та доод навигацийн төмөр замаас бүртгэлээ сольж болно." - }, - "show_rail_account_list": { - "label": "Навигацийн төмөр зам дээр дансны аватаруудыг харуулах", - "description": "Хурдан сэлгэхийн тулд навигацийн төмөр замын доод хэсэгт бүртгэлээс гарах товчийг ашиглан хувийн дансны хүрээг харуул." - }, - "unified_mailbox": { - "label": "Нэгдсэн шуудангийн хайрцаг", - "description": "Идэвхтэй акаунт болон түүний хуваалцсан фолдеруудын нэгдсэн фолдеруудыг (Ирсэн имэйл, Илгээсэн гэх мэт) харуулах.", - "cross_account": { - "label": "Бүх дансаар", - "description": "Идэвхтэй акаунтад үлдэхийн оронд нэгдсэн шуудангийн хайрцгийг холбогдсон бүртгэл бүрт нэгтгэнэ үү." - }, - "include_group": { - "label": "Групп ирсэн имэйлийг оруулах", - "description": "Мөн хуваалцсан/бүлэглэсэн ирсэн имэйлийг нэгдсэн харагдац болгон нэгтгэнэ үү." - } - }, - "all_mail": { - "folders_label": "Нэгдсэн жагсаалт дахь хавтаснууд", - "folders_description": "Энэ бүртгэлийн фолдеруудын аль нь Бүх имэйл / Уншаагүй / Одтой жагсаалтад нэгтгэгдэхийг сонгоно уу.", - "account_hint": "{account}.", - "no_folders": "Полдер байхгүй." - }, - "colorful_sidebar_icons": { - "label": "Хажуугийн өнгөт дүрсүүд", - "description": "Tint хавтас болон тэмдэглэгээг төрлөөр нь (цэнхэр Ирсэн хайрцаг, улаан хог, ногоон Илгээсэн гэх мэт). Монохром хажуугийн самбарыг идэвхгүй болгох." - }, - "tint_list_rows": { - "label": "Тагийн өнгөөр эгнээнүүдийг будах", - "description": "Мессежийн мөр бүрийг эхний шошгоны өнгөөр сүүдэрлээрэй. Мөрүүдийг энгийн байлгахын тулд идэвхгүй болгох; шошгоны цэгүүд болон чипүүд нь өнгийг харуулсан хэвээр байна." - }, - "show_folder_total_count": { - "label": "Нийт мессежийн тоог харуулах", - "description": "Нийт мессежийн тоог хавтас, шошгоны хажууд уншаагүй тооны хажууд харуул. Зөвхөн уншаагүй тоонуудыг харуулахын тулд идэвхгүй болго." - }, - "favicon_unread_badge": { - "label": "Таб дээрх уншаагүй тооны дүрс", - "description": "Ирсэн имэйлийн уншаагүй тоог хөтчийн табын дүрс дээр тэмдэг болгон харуулснаар таб анхаарлаа төвлөрүүлээгүй үед шинэ имэйл харагдах болно. Дүрс тэмдгийг тэмдэггүй байлгахын тулд идэвхгүй болгоно уу." - }, - "pro_interface": { - "label": "Pro интерфейс (туршилтын)", - "description": "Зөвхөн ширээний компьютерт зориулагдсан, олон цонхтой мессеж үзэх, данс хоорондын ажлын урсгалтай. Стандарт интерфэйс нь нөлөөлөлд өртөөгүй; та хүссэн үедээ буцааж сольж болно.", - "open_label": "Нээлттэй Pro интерфэйс", - "back_to_standard": "Стандарт руу буцах" - }, - "cross_unread": { - "label": "Уншаагүй", - "description": "Сонгосон фолдерууд дээрх уншаагүй захидлын жагсаалтыг нэгдсэн шуудангийн хайрцгийн уншаагүй бичилтийг харуулах." - }, - "cross_starred": { - "label": "Одтой", - "description": "Сонгосон фолдерууд дээр дарцагласан/одтой захидлын жагсаалтын нэгдсэн шуудангийн хайрцгийн одтой оруулгыг харуулах." - }, - "cross_all": { - "label": "Бүх шуудан", - "description": "Нэгдсэн шуудангийн хайрцганд сонгогдсон фолдер дээрх бүх имэйлийг жагсаасан Бүх имэйлийн оруулгыг харуул." - } - }, - "keywords": { - "title": "Имэйл хаягууд", - "description": "Имэйлээ цэгцлэх шошгуудыг тодорхойл. Эдгээр нь сервер дээр JMAP түлхүүр үг хэлбэрээр хадгалагддаг.", - "add_keyword": "Таг нэмэх", - "label_field": "Дэлгэцийн нэр", - "label_placeholder": "жишээ нь Ажил, Хувийн, Яаралтай", - "id_field": "Тагийн ID", - "id_placeholder": "жишээ нь. ажил, хувийн", - "color_field": "Өнгө", - "id_exists": "Энэ хаягийн ID аль хэдийн байна", - "edit": "Таг засах", - "delete": "Тагыг устгах", - "save": "Хадгалах", - "add": "Нэмэх", - "cancel": "Цуцлах", - "migrating": "Одоо байгаа имэйлүүд дээрх шошгыг шинэчилж байна...", - "migration_error": "Одоо байгаа имэйлийн шошгыг шинэчилж чадсангүй", - "nesting": { - "label": "Оруулсан шошго", - "description": "Тагуудыг бусад шошгоны доор байрлуулж, хажуугийн самбарт мод хэлбэрээр харуул." - }, - "parent_field": "Эцэг эхийн шошго", - "no_parent": "Эцэг эх байхгүй", - "too_long": "Энэ хаягийн зам хэт урт байна (хамгийн ихдээ {max} тэмдэгт)", - "has_children_locked": "Бусад шошгуудыг үүний доор байрлуулсан тул түүний нэр болон эцэг эх нь түгжигдсэн байна. Эхлээд зөөж эсвэл устгана уу.", - "has_children_delete": "Эхлээд үүний доор байрлуулсан шошгыг устгана уу", - "visibility_field": "Хажуугийн самбарын харагдах байдал", - "visibility": { - "show": "Үзүүлэх", - "unread": "Уншаагүй бол харуулах", - "hide": "Нуух" - }, - "unrecognized": { - "title": "Серверээс хаягуудыг сэргээх", - "description": "Тагийн нэр, өнгө нь энэ хөтөч дээр хадгалагддаг боловч шошгууд нь JMAP түлхүүр үг болгон таны мессеж дээр байдаг. Энд ямар ч шошго тайлбарлаагүй түлхүүр үгсийг хайж олохын тулд имэйлээ сканнердаж, дараа нь буцааж нэмнэ үү.", - "scan": "Таг хайх", - "rescan": "Дахин скан хийх", - "scanning": "Скан хийж байна… {total} мессежийн {scanned}", - "none": "Таны мессеж дээрх шошго бүр энд тодорхойлогдсон байна.", - "messages": "{count, plural, one {# мессеж} other {# мессеж}}", - "add_all": "Бүх {count}", - "partial": "Зөвхөн {scanned} хамгийн сүүлийн үеийн {total} мессежийг сканнердсан. Зөвхөн хуучин имэйлд ашигласан шошго байхгүй байж магадгүй.", - "error": "Таны имэйлийг шошго хайж чадсангүй." - } - }, - "notifications": { - "test_sound": "Туршилтын мэдэгдлийн дуу", - "sounds": { - "default": "Өгөгдмөл (Дохио)", - "cheerful": "Хөгжилтэй", - "involved": "Оролцсон", - "swift": "Шуурхай зангаа", - "relax": "Амрах" - }, - "push": { - "title": "Арын мэдэгдлүүд", - "description": "Энэ сайт хаагдсан үед шинэ имэйлийн системийн мэдэгдлийг хүлээн авах. Bulwark түлхэх буухиагаар дамжуулан хүргэгдсэн; реле хэзээ ч захидлын агуулгыг хардаггүй.", - "relay_label": "Түлхэх реле", - "relay_desc": "Байршуулсан Bulwark буухиа өгөгдмөл. Зөвхөн өөрөө зохион байгуулж байгаа тохиолдолд л өөрчилнө үү.", - "relay_locked": "Админ тохируулсан", - "relay_locked_desc": "Түлхэх реле URL-г таны админ тохируулсан бөгөөд өөрчлөх боломжгүй.", - "status_active": "Энэ төхөөрөмжид идэвхтэй байна", - "status_inactive": "Энэ төхөөрөмж дээр идэвхжээгүй", - "status_unsupported": "Энэ хөтөч Web Push-ийг дэмждэггүй", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "Ажиллаж байна…", - "enable": "Идэвхжүүлэх", - "reenable": "Дахин бүртгүүлэх", - "disable": "Идэвхгүй болгох", - "confirm_disable_title": "Арын мэдэгдлийг идэвхгүй болгох уу?", - "confirm_disable_message": "Сайтыг хаах үед энэ төхөөрөмж дохио хүлээн авахаа болино.", - "dismiss_aria": "Мэдэгдэл хүлээх мөрийг хаах", - "ios_hint": "iOS дээр эхлээд сайтаа үндсэн дэлгэц дээрээ суулгаарай - Safari нь зөвхөн суулгасан PWA-д Web Push-г хүргэдэг.", - "devices_title": "Бүртгэлтэй төхөөрөмжүүд", - "devices_desc": "Энэ бүртгэлийн араарх мэдэгдлийг хүлээн авахаар бүртгэгдсэн бүх төхөөрөмж. Цуцлахад тухайн төхөөрөмж шууд хүлээн авахаа болино.", - "devices_loading": "Бүртгэлтэй төхөөрөмжүүдийг ачаалж байна…", - "devices_empty": "Араарх мэдэгдэлд бүртгэгдсэн төхөөрөмж алга.", - "devices_error": "Бүртгэлтэй төхөөрөмжүүдийг ачаалж чадсангүй.", - "devices_refresh": "Шинэчлэх", - "device_this": "Энэ төхөөрөмж", - "device_other": "Өөр төхөөрөмж", - "device_status_active": "Мэдэгдэл хүргэж байна", - "device_status_inactive": "Хэзээ ч мэдэгдэл хүргээгүй", - "device_status_unknown": "Энэ реле танихгүй", - "device_expires": "{date}-нд дуусна", - "revoke": "Цуцлах", - "confirm_revoke_title": "Энэ төхөөрөмжийг цуцлах уу?", - "confirm_revoke_message": "Тэр даруй араарх мэдэгдэл хүлээн авахаа болино. Тухайн төхөөрөмж дээр push-ыг дахин асаавал дахин бүртгэгднэ.", - "confirm_revoke_message_this": "Энэ төхөөрөмж тэр даруй араарх мэдэгдэл хүлээн авахаа болино." - }, - "sound_selection": { - "title": "Мэдэгдэлийн дуу", - "description": "Мэдэгдэл авахын тулд ямар дууг тоглуулахаа сонгоно уу", - "choose": "Дуу", - "choose_desc": "Мэдэгдэлийн аяыг сонгоод чанга яригчийн дүрс дээр дарж урьдчилан үзэх" - }, - "email": { - "title": "И-мэйл мэдэгдэл", - "description": "Ирж буй имэйлийн мэдэгдлийг тохируулах", - "enabled": "И-мэйл мэдэгдэл", - "enabled_desc": "Шинэ имэйл ирэх үед мэдэгдлийг харуулах", - "sound": "Мэдэгдэлийн дуу", - "sound_desc": "Шинэ имэйл ирэх үед аудио дохиог тоглуулах", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Календарийн мэдэгдэл", - "description": "Хуанлийн арга хэмжээний мэдэгдлийг тохируулах", - "enabled": "Үйл явдлын мэдэгдэл", - "enabled_desc": "Удахгүй болох хуанлийн арга хэмжээний тухай сэрэмжлүүлэг харуулах", - "sound": "Мэдэгдэлийн дуу", - "sound_desc": "Хуанли сануулагчийн аудио дохиог тоглуулах", - "invitation_parsing": "Имэйл урилгыг задлан шинжлэх", - "invitation_parsing_desc": "Имэйл хавсралтаас хуанлийн урилгыг илрүүлж, календарийн үйлдлийг харуулах" - } - }, - "language_region": { - "title": "Хэл, Бүс, Цаг", - "description": "Хэл, огнооны формат, цагийн формат болон бусад бүс нутгийн тохиргоо", - "language": { - "label": "Хэл", - "description": "Та дуртай хэлээ сонгоно уу", - "english": "Англи", - "french": "Франсаис" - }, - "date_format": { - "label": "Огнооны формат", - "description": "Имэйл жагсаалтад огноог хэрхэн харуулах вэ", - "smart": "Ухаалаг (орон нутгийн онцлог шинж чанартай)", - "relative": "Харилцан (1 цагийн өмнө, 2 өдрийн өмнө)", - "full": "Үргэлж бүтэн болзоо", - "preview_today": "Өнөөдөр:", - "preview_this_week": "Энэ долоо хоногт:", - "preview_older": "Хөгшин:" - }, - "date_locale": { - "label": "Огнооны форматын бүс", - "description": "Тоон огноог хэрхэн эрэмбэлэх вэ (өдөр, сар, жил)", - "auto": "Автомат (тохирох хэл)", - "iso": "ISO", - "dmy": "Өдөр/Сар/Жил", - "mdy": "Сар/Өдөр/Жил" - }, - "time_format": { - "label": "Цагийн формат", - "description": "12 эсвэл 24 цагийн аль нэгийг сонгох", - "12h": "12 цаг", - "24h": "24 цагийн" - }, - "first_day": { - "label": "Долоо хоногийн эхний өдөр", - "description": "Долоо хоногийн Ням эсвэл Даваа гарагт эхэлнэ", - "sunday": "Ням гараг", - "monday": "Даваа гараг" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Хавтас солих үед хайлтыг арилгах", - "description": "Хавтас дээр дарахад (Ирсэн, Илгээсэн…) идэвхтэй хайлт арилж, тухайн хавтас нээгдэнэ. Унтраасан үед хайлт хадгалагдаж, шилжсэн хавтаст дахин ажиллана." - }, - "request_read_receipt": { - "label": "Анхдагчаар уншсан баримтыг хүсэх", - "description": "Шинэ мессеж бичихдээ уншсан хүлээн авах хүсэлтийг урьдчилан идэвхжүүлнэ." - }, - "read_receipt_response": { - "label": "Уншсан хүлээн авах хүсэлтэд хариу өгөх", - "description": "Ирж буй мессеж уншсан баримт асуухад яах вэ.", - "ask": "Тухайн бүрт нь асуух", - "always": "Үргэлж илгээх", - "never": "Хэзээ ч битгий илгээгээрэй" - }, - "title": "Имэйлийн зан байдал", - "description": "Имэйл хэрхэн ажиллахыг тохируулах", - "mark_read": { - "label": "Уншсан гэж тэмдэглэх", - "description": "Имэйлийг нээх үед уншсан гэж тэмдэглэх нь", - "instant": "Шууд", - "delay_3s": "3 секундын дараа", - "delay_5s": "5 секундын дараа", - "never": "Хэзээ ч" - }, - "delete_action": { - "label": "Устгах үйлдэл", - "description": "Та имэйл устгахад юу болдог вэ", - "trash": "Хогийн сав руу зөөх", - "trash_and_read": "Хогийн сав руу зөөж уншсан гэж тэмдэглээрэй", - "permanent": "Бүрмөсөн устгах", - "warning": "И-мэйл бүрмөсөн устах ба сэргээх боломжгүй. Энэ үйлдэл нь эргэлт буцалтгүй." - }, - "message_spacing": { - "label": "Мессеж хоорондын зай", - "description": "Уншигч доторх мессежийн хэсгийг тойруулан дүүргэх", - "auto": "Авто (санал болгож байна)", - "always": "Үргэлж хоорондын зайг нэмэх", - "edge": "Ирмэгээс ирмэг хүртэл" - }, - "plain_text_font": { - "label": "Энгийн текстийн фонт", - "description": "Энгийн текстэн имэйлд ашиглах фонт", - "mono": "Тогтмол өргөнтэй", - "sans": "Энгийн (апптай ижил)" - }, - "archive_mode": { - "label": "Архив", - "description": "Архивлахдаа имэйлийг хэрхэн зохион байгуулах вэ", - "single": "Нэг хавтас", - "year": "Жилд хавтас", - "month": "Сард нэг хавтас", - "reorganize": "Одоо байгаа архивыг дахин цэгцлэх", - "reorganize_success": "{count, plural, =0 {Дахин зохион байгуулах имэйл байхгүй} =1 {1 имэйлийг дахин зохион байгуулсан} other {# имэйлийг дахин зохион байгууллаа}}", - "reorganize_error": "Архивыг дахин зохион байгуулж чадсангүй" - }, - "permanently_delete_junk": { - "label": "Хог хаягдлыг бүрмөсөн устгах", - "description": "Имэйлийг Хогийн сав руу зөөхийн оронд Хогийн/Спам хавтсаас бүрмөсөн устгах" - }, - "mail_layout": { - "label": "Мэйлийн зохион байгуулалт", - "description": "Сонгодог хуваах унших самбар, Gmail маягийн төвлөрсөн унших урсгал эсвэл Зимбра маягийн доод унших самбараас сонгоно уу.", - "split": "Хуваах самбар", - "split_description": "Мессежийн жагсаалт болон унших самбарыг зэрэгцүүлэн харагдуулах.", - "focus": "Төвлөрсөн жагсаалт", - "focus_description": "Мессеж болгонд нэг мөр үзүүлж, хавтасны хажуугийн самбарыг харагдуулахын зэрэгцээ имэйлийг бүхэлд нь нээнэ үү.", - "horizontal": "Доор талд Унших самбар", - "horizontal_description": "Дээр талд мессежийн жагсаалтыг үзүүлж, доор нь унших хэсэгт сонгосон мессежийг нээнэ үү." - }, - "show_preview": { - "label": "Урьдчилан үзэх текстийг харуулах", - "description": "Жагсаалтад имэйл урьдчилан харахыг харуулах", - "focus_description": "Анхааруулсан нэг мөрт мессежийн жагсаалт доторх мөрний урьдчилан харах текстийг харуулах" - }, - "disable_threading": { - "label": "Харилцааны бүлэглэлийг идэвхгүй болгох", - "description": "Имэйлүүдийг харилцан яриагаар бүлэглэхийн оронд тус тусад нь мессеж хэлбэрээр харуулах" - }, - "plain_text_mode": { - "label": "Зөвхөн энгийн текст", - "description": "Баян текст засварлагчийг идэвхгүй болгож, хариу болон дамжуулалт зэрэг бүх имэйлийг зөвхөн энгийн текст хэлбэрээр илгээх боломжтой" - }, - "auto_select_reply_identity": { - "label": "Хүлээн авсан хаягаас хариу бичих", - "description": "Хариу бичихдээ мессежийг анх илгээсэн хаягаар нь илгээнэ үү. Эхлээд таних тэмдэгтэй таарах; Домэйн бүх хүргэлтийн хувьд, таны үндсэн таниулбараар дамжуулан илгээхдээ From толгой хэсгийг өөр нэр рүү дахин бичнэ." - }, - "reply_identity_match": { - "label": "Хүлээн авах хаягийн тохирол", - "description": "Хүлээн авах ямар хаягуудыг таных гэж үзэх. „Зөвхөн яг таарах хаяг“ нь зөвхөн таны тохируулсан identity-уудаас сонгоно; „ижил домэйн“ нь таны identity-ийн домэйн дээрх бусад хаягийг ч catch-all нэр болгон үзэж, Илгээгч толгойг тэр хаягаар солино. Эдгээр хаяг нь түгээлтийн жагсаалт бол яг таарах хаягийг сонгоно уу.", - "exact": "Зөвхөн яг таарах хаяг", - "domain": "Миний домэйн дээрх дурын хаяг" - }, - "signature_position": { - "label": "Гарын үсэг зурах албан тушаал", - "description": "Хариулт болон дамжуулалтад гарын үсгээ хаана оруулах вэ. Иш татсан бичвэрийн дээрх хариултыг хаах нь ойлгомжтой; доор нь эх мессежийг залгаж хадгална.", - "above_quote": "Ишлэлээс өмнө", - "below_quote": "Иш иш татсаны дараа" - }, - "signature_separator": { - "label": "Гарын үсэг хязгаарлагч", - "description": "Гарын үсгийн угтварыг стандарт \"--\" хязгаарлагч шугамаар (RFC 3676) оруулна. Хэрэв та мессежээсээ шууд гарын үсэг рүү шилжихийг хүсвэл унтраа." - }, - "sub_address_delimiter": { - "label": "Дэд хаяг хязгаарлагч", - "description": "Хэрэглэгчийн нэрийг дэд хаягийн шошгоос тусгаарлах тэмдэгт. Таны мэйл сервер ашигладаг хязгаарлагчтай тааруулна уу (жишээ нь user+tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "Захиалгат…", - "custom_input_label": "Захиалгат хязгаарлагч тэмдэгт" - }, - "attachment_click_action": { - "label": "Хавсралт дарах үйлдэл", - "description": "Файлын хавсралт дээр дарснаар урьдчилан харах эсвэл шууд татаж авах эсэхээ сонгоно уу", - "preview": "Боломжтой бол урьдчилан харах", - "download": "Шууд татаж авах" - }, - "attachment_position": { - "label": "Хавсралтын байрлал", - "description": "Имэйл толгой хэсэгт хавсралтуудыг хаана харуулах вэ", - "beside-sender": "Илгээгчийн хажууд", - "below-header": "Толгойн доор" - }, - "emails_per_page": { - "10": "10 имэйл", - "25": "25 имэйл", - "50": "50 имэйл", - "100": "100 имэйл", - "label": "Нэг хуудасны имэйл", - "description": "Нэг удаад ачаалах имэйлийн тоо" - }, - "always_light_mode": { - "label": "И-мэйлүүдийг үргэлж гэрэлтэй горимоор харуулах", - "description": "Апп нь харанхуй горимд байсан ч харанхуй горимд хөрвүүлэхтэй холбоотой асуудлаас зайлсхийж, имэйлийн агуулгыг цайвар горимд үзүүлээрэй" - }, - "external_content": { - "label": "Гадаад контент", - "description": "Зураг болон гадаад контенттой хэрхэн ажиллах талаар", - "ask": "Үргэлж асуух", - "block": "Үргэлж блоклох", - "allow": "Үргэлж зөвшөөрөх" - }, - "trusted_senders": { - "label": "Итгэмжлэгдсэн илгээгчид", - "description": "Зураг нь автоматаар ачаалагддаг илгээгчийг удирдах", - "count_zero": "Байхгүй", - "count_one": "1 илгээгч", - "count_other": "{count} илгээгч", - "modal_title": "Итгэмжлэгдсэн илгээгчид", - "empty_title": "Одоохондоо итгэмжлэгдсэн илгээгч алга", - "empty_description": "Хориглогдсон зураг бүхий имэйлийг үзэхдээ \"Энэ илгээгчид үргэлж итгэ\" дээр дарж тэдгээрийг энд нэмнэ үү.", - "add_manually": "Илгээгчийг гараар нэмэх", - "add_button": "Нэмэх", - "add_placeholder": "Имэйл хаягаа оруулна уу", - "search_placeholder": "Илгээгч хайх...", - "no_results": "Таны хайлтад тохирох илгээгч алга", - "remove": "Устгах", - "close": "Хаах", - "invalid_email": "Хүчинтэй имэйл хаяг оруулна уу", - "already_added": "Энэ илгээгч аль хэдийн итгэсэн", - "save_error": "Хадгалж чадсангүй - дэлгэрэнгүй мэдээллийг Харилцагчийн дибаг хийх бүртгэлээс шалгана уу", - "use_address_book_label": "Хаягийн дэвтэртэй синк хийх", - "use_address_book_description": "Итгэмжлэгдсэн илгээгчдийг \"Итгэмжлэгдсэн илгээгчид\" хаягийн дэвтэрт хадгалснаар таны бүх төхөөрөмжид синк хийнэ" - }, - "hover_actions": { - "label": "Хурдан зөөвөрлөх үйлдэл", - "description": "Жагсаалтад байгаа имэйл дээр гүйлгэх үед гарч ирэх хурдан үйлдлүүдийг сонгох", - "delete": "Устгах", - "star": "Од / Одгүй", - "mark_read": "Уншсан / Уншаагүй гэж тэмдэглэнэ", - "archive": "Архив", - "tag": "Таг", - "spam": "Спам гэж тэмдэглэх", - "not_spam": "Спам биш", - "none_selected": "Ямар ч үйлдэл сонгоогүй", - "mode_label": "Дэлгэцийн горим", - "mode_inline": "Шугаман", - "mode_floating": "Хөвөгч", - "corner_label": "Хөвөгч байрлал", - "corner_top-left": "Зүүн дээд", - "corner_top-right": "Баруун дээд", - "corner_bottom-left": "Зүүн доод", - "corner_bottom-right": "Баруун доод" - }, - "default_mail_program": { - "label": "Өгөгдмөл шуудангийн програм", - "description": "{appName}-г mailto: холбоос нээх өгөгдмөл имэйл программаар бүртгүүлнэ.", - "button": "Өгөгдмөлөөр тохируулах", - "success": "Хөтчөөс анхдагчаар тохируулахыг сануулсан", - "error": "Таны хөтөч энэ функцийг дэмждэггүй" - }, - "attachment_reminder": { - "label": "Хавсралт сануулагч", - "description": "Таны зурваст хавсралт дурдсан боловч хавсралт байхгүй үед илгээхээсээ өмнө анхааруулаарай", - "keywords_label": "Триггер түлхүүр үгс", - "keywords_description": "Таны зурвасаас олдсон сануулагчийг өдөөх үг, хэллэгүүд", - "add_placeholder": "Түлхүүр үг нэмэх...", - "add": "Нэмэх", - "remove": "Устгах" - }, - "empty_subject_warning": { - "label": "Хоосон сэдвийн анхааруулга", - "description": "Гарчиггүй мессеж илгээхээсээ өмнө баталгаажуулахыг хүс." - }, - "hide_inline_image_attachments": { - "label": "Хавсралтаас дотортой зургийг нуух", - "description": "Зурвасны үндсэн хэсэгт оруулсан зургуудыг тусад нь хавсралт хэлбэрээр оруулаагүй болно" - }, - "attachment_image_previews": { - "label": "Хавсралтаас зургийн урьдчилан харахыг харуулах", - "description": "Зургийн хавсралтыг ерөнхий файлын дүрсийн оронд өнгөц зургийн карт болгон гаргах" - }, - "send_delay": { - "label": "Илгээх / илгээх саатлыг буцаах", - "description": "Сервер талын богино цонхоор илгээх хэвийн саатал.", - "off": " Унтраах", - "seconds": "{seconds} секунд", - "unsupported": "Харин данс нь хойшлуулсан-илгээлтийн дэмжлэгийг сурталчилдаггүй. Тохиргоог бусад бүртгэлд хадгалсан хэвээр байна." - }, - "return_to_list_after_action": { - "label": "Устгасны дараа жагсаалт руу буцах эсвэл уншаагүй гэж тэмдэглэнэ", - "description": "Нээлттэй мессежийг устгасан эсвэл уншаагүй гэж тэмдэглэсний дараа дараагийн мессежийг нээхийн оронд мессежийн жагсаалт руу буцна уу." - }, - "swipe_right_action": { - "label": "Баруун тийш шудрах үйлдэл (гар утас)", - "description": "Мэдрэгчтэй төхөөрөмж дээр жагсаалт дахь мессежийг баруун тийш шудрахад гүйцэтгэгдэх үйлдэл." - }, - "swipe_left_action": { - "label": "Зүүн тийш шудрах үйлдэл (гар утас)", - "description": "Мэдрэгчтэй төхөөрөмж дээр жагсаалт дахь мессежийг зүүн тийш шудрахад гүйцэтгэгдэх үйлдэл." - }, - "swipe_actions": { - "none": "Байхгүй", - "archive": "Архивлах", - "delete": "Устгах", - "mark_read": "Уншсан төлөвийг сэлгэх", - "star": "Одыг сэлгэх", - "spam": "Спам гэж тэмдэглэх" - }, - "rtl_editing": { - "label": "Баруунаас зүүн тийш засварлах дэмжлэг", - "description": "Хөгжмийн зохиолчийн хэрэгслийн самбарт чиглэлийн товчлуур нэмснээр та догол мөрүүдийг зүүнээс баруун тийш эсвэл баруунаас зүүн тийш тохируулах боломжтой" - } - }, - "composer": { - "title": "Хөгжмийн зохиолч", - "description": "Имэйл бичих тохиргоог тохируулах", - "autosave": { - "label": "Автоматаар хадгалах интервал", - "description": "Ноорог автоматаар хэр олон удаа хадгалах вэ", - "30s": "30 секунд тутамд", - "1m": "Минут тутам", - "2m": "2 минут тутамд", - "5m": "5 минут тутамд" - }, - "send_confirmation": { - "label": "Баталгаажуулах", - "description": "Имэйл илгээхээсээ өмнө баталгаажуулахыг хүсэх" - }, - "default_reply": { - "label": "Өгөгдмөл хариу бичих горим", - "description": "Хариулах товчийг дарах үндсэн үйлдэл", - "reply": "Хариулах", - "reply_all": "Бүгдэд нь хариулах" - } - }, - "privacy": { - "title": "Нууцлал ба Аюулгүй байдал", - "description": "Нууцлал болон аюулгүй байдлын тохиргоогоо удирдах", - "external_images": { - "label": "Гадаад зургийг блоклох", - "description": "Гадны зургаар мөрдөхөөс урьдчилан сэргийлэх" - }, - "session_timeout": { - "label": "Хугацаа хэтэрсэн", - "description": "Идэвхгүй болсны дараа автоматаар гарах", - "never": "Хэзээ ч", - "30m": "30 минут", - "1h": "1 цаг", - "4h": "4 цаг" - }, - "clear_cache": { - "label": "Кэшийг арилгах", - "description": "Кэшд хадгалагдсан өгөгдөл болон түр файлуудыг устгах", - "button": "Кэшийг арилгах", - "confirm": "Та кэшийг цэвэрлэхдээ итгэлтэй байна уу?", - "success": "Кэшийг амжилттай цэвэрлэв" - } - }, - "account": { - "title": " Данс", - "description": "Өөрийн дансны мэдээллийг харах", - "name_label": "Дэлгэцийн нэр", - "username_label": "Хэрэглэгчийн нэр", - "account_type_label": "Дансны төрөл", - "auth_method_label": "Баталгаажуулалт", - "auth_method_oauth": "Нэг удаа нэвтрэх (OAuth/OIDC)", - "auth_method_basic": "Нууц үг", - "demo_account": "Демо данс", - "email": { - "label": "Имэйл хаяг", - "value": "Имэйл" - }, - "server": { - "label": "JMAP сервер", - "value": "Сервер" - }, - "storage": { - "label": "Хадгалалтын ашиглалт", - "used": "{total}-н {used} ашигласан", - "percentage": "{percent}% ашигласан" - }, - "last_sync": { - "label": "Сүүлийн синк", - "value": "Сүүлд синк хийсэн" - }, - "accounts": { - "title": "Нэвтэрсэн бүртгэлүүд", - "description": "Бүртгэл унадаг цэсэнд бүртгэлүүд хэрхэн харагдахыг өөрчлөхийн тулд чирнэ үү", - "active": "Одоогоор идэвхтэй байгаа данс", - "default_badge": "Үндсэн данс", - "set_default": "Өгөгдмөлөөр тохируулах", - "switch_to": "Энэ данс руу шилжих", - "move_up": "Дээш хөдлөх", - "move_down": "Доошоо шилжих", - "drag_handle": "Дахин эрэмбэлэхийн тулд чирнэ үү", - "add": "Бүртгэл нэмэх" - }, - "shared_accounts": { - "title": "Надтай хуваалцсан", - "description": "Бүлэг болон хуваалцсан акаунтуудыг удирдах боломжтой. Шүүлтүүр, амралтын хариулагч, хуанли, харилцагчдыг засахын тулд нэгийг нь сонгоно уу.", - "shared_label": "Хуваалцсан бүртгэл" - } - }, - "scoped": { - "back": "Миний данс руу буцах", - "managing": "Удирдлага: {name}" - }, - "security": { - "title": "Дансны аюулгүй байдал", - "description": "Нууц үг, хоёр хүчин зүйлийн баталгаажуулалт, аюулгүй байдлын тохиргоогоо удирдах", - "detecting": "Серверийн чадварыг илрүүлж байна...", - "not_available": "Энэ имэйл сервер дээр бүртгэлийн аюулгүй байдлын удирдлага ашиглах боломжгүй. Шаардлагатай зөвшөөрлүүд идэвхгүй байж болно. Дэлгэрэнгүйг баримт бичгээс үзнэ үү.", - "password": { - "title": "Нууц үг солих", - "current": "Одоогийн нууц үг", - "new": "Шинэ нууц үг", - "confirm": "Шинэ нууц үгээ баталгаажуулах", - "submit": "Нууц үг солих", - "success": "Нууц үг амжилттай өөрчлөгдсөн", - "error_title": "Нууц үг солигдож чадсангүй", - "error_mismatch": "Шинэ нууц үг таарахгүй байна", - "error_min_length": "Нууц үг хамгийн багадаа 8 тэмдэгт байх ёстой", - "error_generic": "Нууц үгээ сольж чадсангүй" - }, - "display_name": { - "label": "Дэлгэцийн нэр", - "description": "Таны сервер дээр байгаа нэр", - "placeholder": "Дэлгэцийн нэрээ оруулна уу", - "save": "Хадгалах", - "success": "Дэлгэцийн нэрийг шинэчилсэн", - "error": "Дэлгэцийн нэрийг шинэчилж чадсангүй" - }, - "totp": { - "section_title": "Хоёр хүчин зүйлийн баталгаажуулалт", - "label": "TOTP баталгаажуулалт", - "description": "Нэг удаагийн нууц үгээр аюулгүй байдлын нэмэлт давхаргыг нэмнэ үү", - "active": "Идэвхжүүлсэн", - "inactive": "Хөгжлийн бэрхшээлтэй", - "enabled": "Хоёр хүчин зүйлийн баталгаажуулалтыг идэвхжүүлсэн", - "disabled": "Хоёр хүчин зүйлийн баталгаажуулалтыг идэвхгүй болгосон", - "enable_error": "2FA", - "disable_error": "2FA", - "setup_instructions": "Энэ URL-г баталгаажуулагч програмдаа хуулна уу (Google Authenticator, Authy гэх мэт):", - "verification_code": "Баталгаажуулах код", - "confirm": "Баталгаажуулах", - "disable": "Идэвхгүй болгох", - "disable_confirm_prompt": "Хоёр хүчин зүйлийн баталгаажуулалтыг идэвхгүй болгохын тулд нууц үгээ оруулна уу.", - "password_required": "Нууц үг шаардлагатай", - "code_required": "Баталгаажуулах код шаардлагатай", - "code_invalid": "Баталгаажуулах код буруу байна. Authenticator програмаа шалгаад дахин оролдоно уу." - }, - "app_passwords": { - "title": "Аппын нууц үг", - "description": "Хоёр хүчин зүйлийн баталгаажуулалтыг дэмждэггүй програмуудад нууц үг үүсгэх", - "add": "Нэмэх", - "create": "Үйлдвэрлэх", - "cancel": "Цуцлах", - "done": "Дууссан", - "generate": "Үйлдвэрлэх", - "name_label": "Аппын нэр", - "name_placeholder": "жишээ нь. Thunderbird, iPhone Mail", - "expires_label": "Хугацаа нь дуусна (заавал биш)", - "allowed_ips_label": "Зөвшөөрөгдсөн IP (заавал биш)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Таслалаар эсвэл зайгаар тусгаарласан. Ямар ч IP зөвшөөрөхийн тулд хоосон орхино уу.", - "password_label": "Нууц үг (автоматаар үүсгэхийн тулд хоосон орхино уу)", - "password_placeholder": "Хоосон бол автоматаар үүсгэнэ", - "copy_now_warning": "Энэ нууц үгийг одоо хуулна - энэ нь дахиж харагдахгүй.", - "added": "Аппын нууц үгийг үүсгэсэн", - "removed": "Аппын нууц үгийг устгасан", - "add_error": "Аппын нууц үгийг үүсгэж чадсангүй", - "remove_error": "Аппын нууц үгийг устгаж чадсангүй", - "none": "Аппын нууц үгийг тохируулаагүй" - }, - "api_keys": { - "title": "API түлхүүрүүд", - "description": "Сервертэй шууд харьцдаг скрипт болон интеграцид зориулсан API түлхүүрүүдийг үүсгэх", - "name_label": "Түлхүүр нэр", - "name_placeholder": "жишээ нь. Нөөц скрипт, CI runner", - "copy_now_warning": "Энэ API түлхүүрийг одоо хуулна уу - энэ нь дахиж харагдахгүй.", - "added": "API түлхүүр үүсгэсэн", - "removed": "API түлхүүрийг устгасан", - "add_error": "API түлхүүр үүсгэж чадсангүй", - "remove_error": "API түлхүүрийг устгаж чадсангүй", - "none": " API түлхүүр тохируулаагүй" - }, - "encryption": { - "section_title": "Амарсан үед шифрлэлт", - "label": "И-мэйл шифрлэлт", - "description": "Нэмэлт нууцлалыг хадгалахын тулд сервер дээр хадгалагдсан имэйлийг шифрлээрэй", - "active": "{type} шифрлэлтийг идэвхжүүлсэн", - "inactive": "Хөгжлийн бэрхшээлтэй", - "enabled": "Амарсан үед шифрлэлтийг идэвхжүүлсэн", - "disabled_success": "Хамгийн үед шифрлэлт идэвхгүй болсон", - "error": "Шифрлэлтийн тохиргоог шинэчилж чадсангүй", - "algorithm_label": "Шифрлэлтийн алгоритм", - "encrypt_on_append": "Байршуулахдаа шинэ имэйлийг шифрлэх", - "allow_spam_training": "Имэйл шифрлэхээс өмнө спам сургалт явуулахыг зөвшөөрнө үү", - "enable_encryption": "Шифрлэлтийг идэвхжүүлэх", - "enable_tooltip": "Энэ түлхүүрээр шифрлэлтийг идэвхжүүлнэ", - "configure_title": "Энэ түлхүүрээр шифрлэлтийг идэвхжүүлнэ", - "enable_button": "Идэвхжүүлэх", - "disable_tooltip": "Шифрлэлтийг идэвхгүй болгох", - "enabled_success": "Амарсан үед шифрлэлт идэвхжсэн" - }, - "public_keys": { - "name_label": "Нийтийн түлхүүрийн нэр", - "name_placeholder": "жишээ нь. Миний түлхүүр", - "key_label": "Нийтийн түлхүүр (S/MIME эсвэл PGP ASCII хуягт)", - "key_placeholder": "Нийтийн түлхүүрээ энд буулгана уу", - "none": "Нийтийн түлхүүр тохируулаагүй" - }, - "email_client": { - "title": "И-мэйл үйлчлүүлэгчийн тохиргоо", - "description": "Эдгээр итгэмжлэлийг ашиглан ширээний компьютер эсвэл гар утасны имэйл клиентээ (Thunderbird, Apple Mail, Outlook гэх мэт) тохируулна уу", - "jmap_username_label": "JMAP хэрэглэгчийн нэр", - "copy": "Хуулбар", - "copied": "Хуулбарласан", - "password_instructions": "Имэйл клиентдээ нэвтрэхийн тулд дээрх JMAP хэрэглэгчийн нэрээ програмын нууц үгийн хамт ашиглана уу. Хэрэв та хийгээгүй бол дээрх хэсэгт програмын нууц үг үүсгэнэ үү." - }, - "link_device": { - "title": "Мобайл програмыг холбоно уу", - "description": "Юу ч бичихгүйгээр Bulwark Mail гар утасны програмд нэвтэрнэ үү. Энд QR код үүсгээд програмын нэвтрэх дэлгэцээс скан хийнэ үү.", - "generate": "QR код харуулах", - "regenerate": "Шинэ код харуулах", - "instructions": "Bulwark Mail програмыг нээгээд, нэвтрэх дэлгэцэн дээрх \"QR кодыг скан\" гэснийг товшоод камераа энд чиглүүлээрэй.", - "expires_in": "Энэ кодын хугацаа {seconds} секундын дотор дуусна. Үүнийг зөвхөн нэг удаа ашиглах боломжтой.", - "expired": "Энэ кодын хугацаа дууссан.", - "generating": "Үйлдвэрлэж байна...", - "error": "Хослуулах код үүсгэж чадсангүй. Дахин оролдоно уу." - } - }, - "identities": { - "title": "Танилцуулга илгээх", - "description": "Таны илгээх боломжтой имэйл хаягаа удирдах", - "identities_count": { - "label": "Таны таних тэмдэг", - "description": "Имэйл хаягуудыг илгээхээр тохируулсан", - "count_zero": "Хувийн мэдээлэл байхгүй", - "count_one": "1 таних тэмдэг", - "count_other": "{count} таних тэмдэг" - }, - "manage": "Identities удирдах", - "sub_addressing": { - "label": "Дэд хаяглалт", - "description": "Ирж буй имэйлийг цэгцлэхийн тулд user+tag@domain.com гэх мэт хаягуудыг ашиглана уу", - "learn_more": "Дэлгэрэнгүй үзэх" - } - }, - "vacation": { - "title": "Амралтын хариулагч", - "description": "Таныг эзгүй байхад ирж буй имэйлд автоматаар хариу бичих", - "loading": "Амралтын тохиргоог ачаалж байна...", - "not_supported": "Таны мэйл сервер амралтын хариуг дэмждэггүй.", - "fetch_error": "Амралтын тохиргоог ачаалж чадсангүй. Дахин оролдоно уу.", - "status": { - "label": "Амралтын хариулагч", - "description": "Тан руу имэйл илгээсэн хүмүүст автоматаар хариу илгээх", - "active": "Идэвхтэй", - "inactive": "Идэвхгүй" - }, - "date_range": { - "title": "Огнооны муж", - "description": "Заавал автомат хариуг тодорхой хугацаанд хязгаарлах", - "start": "Эхлэх огноо", - "start_description": "Эхлэх хязгааргүй хоосон орхино", - "end": "Төгсгөлийн огноо", - "end_description": "Төгсгөлгүй хоосон орхино" - }, - "message": { - "title": "Автоматаар хариулах мессеж", - "description": "Хариулт хэлбэрээр илгээх мессеж", - "subject_label": "Сэдэв", - "subject_description": "Автомат хариултын сэдвийн мөр", - "subject_placeholder": "Оффисоос гадуур", - "body_label": "Зурвасны үндсэн хэсэг", - "body_description": "Энгийн мессежийн агуулга", - "body_placeholder": "Имэйл илгээсэнд баярлалаа. Би одоогоор оффисгүй байгаа бөгөөд буцаж ирэхдээ хариулах болно.", - "html_label": "Форматлагдсан мессеж (HTML)", - "html_description": "Холбоос болон загвар бүхий баялаг, форматтай хувилбарыг нэмнэ үү. Мэйл үйлчлүүлэгч нь үүнийг харуулах боломжгүй хүлээн авагчид дээрх энгийн текст рүү буцна.", - "html_placeholder": "Оффисоос гадуур форматлагдсан хариу бичих..." - }, - "preview": { - "title": "Урьдчилан харах", - "show": "Урьдчилан үзэхийг харуулах", - "hide": "Урьдчилан харахыг нуух" - }, - "save": "Өөрчлөлтийг хадгалах", - "saving": "Хадгалж байна...", - "warnings": { - "end_before_start": "Төгсгөлийн огноо нь эхлэх огнооноос хойш байх ёстой", - "start_in_past": "Эхлэх огноо өнгөрсөн байна", - "empty_body": "Зурвасны хэсэг хоосон байна - хүлээн авагчид хоосон хариу ирэх болно", - "subject_too_long": "Гарчиг сервертэй харьцуулахад хэт урт байна (дээд тал нь {max} байт).", - "body_too_long": "Захидал сервертэй харьцуулахад хэт урт байна (форматыг оруулаад дээд тал нь {max} байт)." - } - }, - "folders": { - "role_memos": "Тэмдэглэл", - "title": "Хавтас", - "description": "Имэйл хавтасаа удирдаж, стандарт үүрэг оноох", - "folder_list": "Таны хавтаснууд", - "folder_list_description": "Хавтасны дүрс дээр дарж өөрчилнө үү", - "standard_roles": "Стандарт хавтасны үүрэг", - "standard_roles_description": "Ирсэн имэйл, Илгээсэн, Хогийн сав гэх мэт стандарт шуудангийн хайрцгийн үүрэгт ямар фолдерыг ашиглахыг зааж өгнө үү.", - "role_inbox": "Ирсэн имэйл", - "role_drafts": "Ноорог", - "role_sent": "Илгээсэн", - "role_trash": "Хогийн сав", - "role_junk": "Спам / Хог", - "role_archive": "Архив", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Байхгүй", - "create_folder": "Хавтас үүсгэх", - "create_subfolder": "Дэд хавтас үүсгэх", - "reorder": "Дахин эрэмбэлэхийн тулд чирнэ үү", - "reorder_error": "Фолдеруудыг дахин эрэмбэлж чадсангүй", - "move_error": "Фолдерыг зөөж чадсангүй", - "subfolder_of": "{name}", - "subfolder_name": "Дэд хавтасны нэр", - "new_folder_name": "Хавтасны нэр", - "rename": "Нэрээ өөрчлөх", - "change_icon": " Дүрсийг өөрчлөх", - "delete": "Устгах", - "confirm_delete": "Та \"{name}\"-г устгахдаа итгэлтэй байна уу? Энэ хавтсанд байгаа имэйлийг Хогийн сав руу зөөх болно.", - "create": "Үйлдвэрлэх", - "cancel": "Цуцлах", - "no_folders": "Захиалгат хавтас байхгүй", - "cannot_delete_role": "Стандарт үүрэг бүхий хавтсыг устгах боломжгүй. Эхлээд дүрээ хас.", - "folder_created": "Хавтас үүсгэсэн", - "folder_renamed": "Хавтасны нэрийг өөрчилсөн", - "folder_deleted": "Хавтас устгасан", - "role_updated": "Фолдерын үүрэг шинэчлэгдсэн", - "error_create": "Хавтас үүсгэж чадсангүй", - "error_rename": "Фолдерын нэрийг өөрчилж чадсангүй", - "error_delete": "Хавтсыг устгаж чадсангүй", - "error_delete_has_children": "Фолдерыг устгах боломжгүй: энэ нь дэд хавтастай хэвээр байна. Эхлээд устгах эсвэл зөөх.", - "error_delete_has_email": "Фолдерыг устгах боломжгүй: энэ нь имэйл агуулсан хэвээр байна. Эхлээд зөөж эсвэл устгана уу.", - "error_role": "Фолдерын үүргийг шинэчилж чадсангүй" - }, - "advanced": { - "title": "Дэвшилтэт", - "description": "Дэвшилтэт сонголтууд болон хөгжүүлэгчийн тохиргоо", - "debug_mode": { - "label": "Дбаг хийх горим", - "description": "Алдааг олж засварлахын тулд нарийвчилсан бүртгэлийг идэвхжүүлэх" - }, - "debug_categories": { - "description": "Ямар ангилалд бүртгүүлэхээ сонгоно уу. Консолын дуу чимээг багасгахын тулд шаардлагагүй ангиллыг идэвхгүй болго.", - "jmap": "JMAP", - "jmap_description": "Шуудангийн хайрцгийн ажиллагаа, имэйл татах, JMAP протоколын хүсэлтүүд", - "calendar": "Хуанли", - "calendar_description": "Календарийн арга хэмжээ, импорт болон хуваарийн мессежүүд", - "tasks": "Даалгавар", - "tasks_description": "Календарийн даалгавар үүсгэх, татах, шинэчлэх", - "auth": "Баталгаажуулалт", - "auth_description": "Нэвтрэх, TOTP, токен солилцох, сесс удирдах", - "filters": "Шүүлтүүр", - "filters_description": "Шүүлтүүрийн дүрэм болон амралтын скриптүүдийг шүүнэ", - "email": "И-мэйл үзэх", - "email_description": "И-мэйл дүрслэх, TNEF боловсруулах, уншсан гэж тэмдэглэх", - "push": "Түлхэх мэдэгдэл", - "push_description": "Push мэдэгдлийн тохиргоо болон хүргэлт", - "contacts": "Харилцах утас, хаягийн дэвтэр", - "contacts_description": "Холбоо барих синхрончлол, хаягийн дэвтрийн үйл ажиллагаа, итгэмжлэгдсэн илгээгчид" - }, - "settings_sync": { - "label": "Тохиргоо Синк", - "description": "Тохиргоогоо хөтөч болон төхөөрөмжүүдийн хооронд синк хийх" - }, - "sender_favicons": { - "label": "Илгээгчийн Favicons", - "description": "Вэб сайтын дүрсийг бизнес илгээгчдэд профайл зураг болгон харуулах" - }, - "show_avatars_in_junk": { - "label": "Хогийн хавтас дахь аватаруудыг харуулах", - "description": "Хогийн хавтсанд илгээгчдийн профайлын зураг болон фавиконыг харуул. Фишингийн оролдлогод харааны хууль ёсны байдлыг өгөхгүйн тулд анхдагчаар идэвхгүй болгосон." - }, - "keyboard_shortcuts": { - "label": "Гарын товчлол", - "description": "Боломжтой гарын товчлолуудыг харах", - "button": "Товчлол харах" - }, - "refresh_cache": { - "label": "Кэш хийсэн өгөгдлийг сэргээх", - "description": "Харилцагч, хуанли, фолдеруудыг серверээс дахин ачаална уу. Таны бүртгэл болон сешнүүдийг хадгална — гаралгүй хуучирсан эсвэл буруу харагдацыг засна.", - "button": "Сэргээх" - }, - "reset_settings": { - "label": "Тохиргоог дахин тохируулах", - "description": "Бүх тохиргоог анхдагч утга руу нь сэргээнэ", - "button": "Өгөгдмөл рүү дахин тохируулах" - }, - "export_settings": { - "label": "Экспортын тохиргоо", - "description": "Тохиргоогоо JSON", - "button": "Экспорт" - }, - "import_settings": { - "label": "Импортын тохиргоо", - "description": "JSON файлаас тохиргоог байршуулах", - "button": "Импорт" - }, - "about": { - "title": "Тухай" - } - }, - "sidebar_apps": { - "title": "Хажуугийн самбарын програмууд", - "description": "Хажуугийн самбар дээрх захиалгат програмууд болон холбоосуудыг удирдах", - "keep_loaded": "Апп-уудыг ачаалалтай байлгах", - "keep_loaded_description": "Дахин ачаалахаас зайлсхийхийн тулд шугаман програмуудыг хооронд нь сэлгэхдээ ар талд нь ажиллуулаарай", - "managed_title": "Администратороос нийлүүлсэн", - "managed_description": "Эдгээр аппыг бүх хэрэглэгчид тохируулсан бөгөөд өөрчлөх боломжгүй.", - "manage_title": "Захиалгат програмууд", - "manage_description": "Хажуугийн цэсэндээ тусгай програм нэмэх, засах, устгах боломжтой" - }, - "contacts": { - "title": "Харилцах утас", - "description": "Харилцагчаа импортлох, экспортлох", - "group_by_letter_label": "Эхний үсгээр бүлэглэх", - "sort_by_last_name_label": "Овгоор эрэмбэлэх", - "sort_by_last_name_description": "Гэр бүлийн гишүүд хамт харагдахаар харилцах хүмүүсийн жагсаалтыг овгоор эрэмбэлэх", - "group_by_letter_description": "Харилцах хүмүүсийн жагсаалтад цагаан толгойн үсгийн дарааллаар хэсгийн толгойг харуулах", - "import_label": "Харилцагчдыг импортлох", - "import_description": "vCard (.vcf) файлаас харилцагчдыг импортлох", - "export_label": "Харилцагчдыг экспортлох", - "export_description": "Бүх харилцагчдыг vCard (.vcf) файл болгон экспортлох", - "manage_title": "Хаягийн дэвтэр", - "manage_description": "Хаягийн дэвтэрийнхээ нэрийг өөрчлөх", - "no_address_books": "Хаягийн дэвтэр олдсонгүй", - "categories_title": "Ангилал", - "categories_description": "Харилцах ангиллын нэрийг өөрчлөх", - "no_categories": "Ангилал олдсонгүй" - }, - "downloads": { - "title": "Татаж авах", - "description": "Татаж авсан имэйл болон хавсралтуудыг хэрхэн нэрлэхийг тохируулах.", - "reset": "Өгөгдмөлийг сэргээх", - "preview": "Урьдчилан үзэх:", - "email_template": { - "label": "И-мэйл (.eml) файлын нэр", - "description": "Загвар нь таныг имэйл экспортлох эсвэл файлын систем рүү чирэх үед ашигладаг. .eml өргөтгөл автоматаар нэмэгддэг." - }, - "attachment_template": { - "label": "Хавсралт файлын нэр", - "description": "Загвар нь хавсралтыг татаж авах эсвэл чирэх үед ашиглагддаг. Хэрэв та '{filename}' болон '{ext}'-г орхивол анхны өргөтгөл хадгалагдана." - }, - "bundle_template": { - "label": "Олон имэйл .zip файлын нэр", - "description": "Загвар нь таныг сонгосон хэд хэдэн имэйлийг нэг .zip архив болгон чирэх эсвэл татах үед ашигладаг. .zip өргөтгөл автоматаар нэмэгддэг." - }, - "spaces": { - "label": "Зайнууд", - "description": "Үзэгдэх файлын нэрний хоосон зайг өөр тэмдэгтээр солино.", - "keep": "Зай үлдээх", - "underscore": "_", - "dash": "-" - }, - "lowercase": { - "label": "Жижиг үсэг", - "description": "Файлын нэрийг бүхэлд нь жижиг үсгээр бичнэ үү." - }, - "strip_diacritics": { - "label": "Судасны диакритик", - "description": "Өргөлттэй үсгүүдийг ASCII эквивалент (ä → a, é → e) болгон хувиргана. Юникод файлын нэрийг өөрчлөх хэрэгсэлд хэрэг болно." - }, - "collapse_separators": { - "label": "Дахин давтагдсан тусгаарлагчийг нураах", - "description": "Хоосон зай, доогуур зураас эсвэл зураасыг нэг тэмдэгт болгон буулгана." - }, - "after_export": { - "label": "Экспортын дараа", - "description": "Заавал имэйлийг .eml.", - "keep": "Шуудангийн хайрцагт хадгалах", - "archive": "Архив руу шилжих", - "trash": "Хогийн сав руу зөөх" - } - }, - "filters": { - "title": "Имэйл шүүлтүүрүүд", - "description": "Ирж буй имэйлийг автоматаар ангилах, шошголох, удирдах дүрэм бий болгох", - "add_rule": "Дүрэм нэмэх", - "no_rules": "Шүүлтүүрийн дүрэм байхгүй", - "no_rules_description": "Ирж буй имэйлээ автоматаар цэгцлэх дүрэм бий болгох", - "vacation_active": "Амралтын хариулагч идэвхтэй байна", - "vacation_active_description": "Ирж буй мессежүүдэд автоматаар хариу бичихийг идэвхжүүлсэн", - "vacation_configure": "Тохируулах", - "edit_rule": "Засах дүрэм", - "new_rule": "Шинэ дүрэм", - "delete_rule": "Дүрэмийг устгах", - "delete_confirm": "Та энэ дүрмийг устгахдаа итгэлтэй байна уу?", - "enable": "Идэвхжүүлэх", - "disable": "Идэвхгүй болгох", - "raw_editor": "Түүхий шигшүүр засварлагч", - "raw_editor_warning": "Түүхий Sieve скриптийг засварлах нь дүрмийн засварыг эвдэж болзошгүй. Энд хийсэн өөрчлөлтүүд нь харааны бүтээгчийг дарж байна.", - "validate": "Баталгаажуулах", - "validation_success": "Скрипт хүчинтэй", - "validation_error": "Script алдаатай байна", - "save": "Дүрэм хадгалах", - "saving": "Хадгалж байна...", - "saved": "Шүүлтүүрийг амжилттай хадгаллаа", - "save_failed": "Шүүлтүүрийг хадгалж чадсангүй", - "loading": "Шүүлтүүрийг ачаалж байна...", - "not_supported": "Таны мэйл сервер имэйл шүүлтүүрийг дэмждэггүй.", - "rule_name": "Дүрмийн нэр", - "rule_name_placeholder": "жишээ нь, Мэдээллийн хуудсыг эрэмбэлэх", - "match_all": "БҮХ нөхцөлийг тааруулах", - "match_any": "Ямар ч нөхцөлтэй тохирно", - "conditions": "Нөхцөлүүд", - "add_condition": "Нөхцөл нэмэх", - "actions": "Үйлдэл", - "add_action": "Үйлдэл нэмэх", - "stop_processing": "Дараагийн дүрмийг боловсруулахаа зогсоох", - "include_spam": "Спам гэж тэмдэглэсэн захидлыг мөн зөөх", - "attachment_type_placeholder": "жишээ нь pdf, doc, jpg", - "value_placeholder_multi": "Утга (олон тоог таслалаар тусгаарласан)", - "condition_fields": { - "attachment": "Хавсралт", - "from": "-аас", - "to": "Хэнд", - "cc": "Хуулбар", - "subject": "Сэдэв", - "header": "Захиалгат толгой хэсэг", - "size": "Хэмжээ", - "body": "Бие" - }, - "comparators": { - "has_any": " байгаа", - "has_type": "төрөл", - "contains": "агуулсан", - "not_contains": " агуулаагүй", - "is": " яг", - "not_is": " нь", - "starts_with": "", - "ends_with": "", - "matches": "загварт тохирно", - "greater_than": " нь", - "less_than": " нь" - }, - "action_types": { - "move": "Хавтас руу зөөх", - "copy": "Хавтас руу хуулах", - "forward": "Дашлуул", - "mark_read": "Уншсан гэж тэмдэглэх", - "star": "Одтой мессеж", - "add_label": "Таг нэмэх", - "discard": "Тайх (чимээгүй устгах)", - "reject": "Мессежээр татгалзах", - "keep": "Ирсэн имэйлд хадгалах", - "stop": "Боловсруулалтыг зогсоох" - }, - "move_to_folder": "Фолдер сонгох", - "copy_to_folder": "Фолдер сонгох", - "forward_to": "Имэйл хаяг руу шилжүүлэх", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Хуулбар хадгалах", - "forward_limit": "Энэ сервер дээрх нэг захидлын дамжуулах хязгаар: {count}. Илүү дамжуулалтыг алгасна.", - "reject_message": "Татгалзсан мессеж", - "reject_placeholder": "Таны имэйл татгалзсан байна", - "label_name": "Тагийн нэр", - "label_placeholder": "Таг сонгох", - "header_name": "Толгойн нэр", - "header_placeholder": "жишээ нь, X-мэйл хаягийн жагсаалт", - "size_bytes": "Хэмжээ байтаар", - "size_placeholder": "жишээ нь, 1000000", - "system_managed": "Системээр удирддаг дүрэм", - "opaque_warning": "Энэ скриптийг визуал бүтээгчээс гадуур засварласан. Зөвхөн түүхий шигшүүрээр засварлах боломжтой.", - "open_sieve_editor": "Raw Sieve редакторыг нээнэ үү", - "fetch_error": "Шүүлтүүрийг ачаалж чадсангүй", - "expanded_view": "Өргөтгөсөн харах", - "expanded_view_description": "Шүүлтүүрийн дүрмийг нарийвчилсан нөхцөл, үйлдлийн блокоор харуулах", - "if": "Хэрэв", - "then": "Дараа нь", - "match_all_conditions": "бүх тоглолт", - "match_any_condition": "ямар ч тоглолт", - "and": "ба", - "or": "эсвэл", - "cancel": "Цуцлах", - "confirm_delete": "Устгах", - "rule_list": "Шүүлтүүрийн дүрэм", - "drag_to_reorder": "Дахин эрэмбэлэхийн тулд чирнэ үү", - "match_type": "Тохирох төрөл", - "reset_to_visual": "Visual builder руу дахин тохируулах", - "reset_warning": "Энэ нь одоогийн скриптийг устгаж, шинээр эхэлнэ.", - "confirm_reset": "Дахин тохируулах", - "validation_empty_name": "Дүрмийн нэр шаардлагатай", - "validation_empty_conditions": "Дор хаяж нэг утга бүхий нөхцөл шаардлагатай", - "validation_empty_actions": "Дор хаяж нэг үйлдэл хийх шаардлагатай", - "templates_section": "Загвар", - "template_newsletters": "Мэдээллийн хуудсыг хавтас руу зөөх", - "template_receipts": "Автоматаар архивлах баримт", - "template_important": "Чухал имэйлийг дарцаглах", - "template_notifications": "Мэдэгдэл шүүх", - "sieve_editor": { - "title": "Шигшүүрийн скрипт засварлагч", - "warning": "Түүхий Sieve скриптийг засварлах нь дүрмийн засварыг эвдэж болзошгүй. Энд хийсэн өөрчлөлтүүд нь харааны бүтээгчийг хүчингүй болгодог.", - "script_content": "Шигшүүрийн скрипт", - "valid": "Скрипт хүчинтэй", - "invalid": "Script-д алдаа байна", - "save_warning": "Хадгалснаар ямар ч харааны дүрмийг дарж бичнэ. Үүнийг буцаах боломжгүй. Баталгаажуулахын тулд Save дахин дарна уу.", - "validating": "Баталгаажуулж байна...", - "validate": "Баталгаажуулах", - "cancel": "Цуцлах", - "save": "Хадгалах", - "confirm_save": "Хадгалахыг баталгаажуулах", - "validation_failed": "Баталгаажуулах хүсэлт амжилтгүй боллоо" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {#нөхцөл} other {# нөхцөл}}", - "actions_count": "{count, plural, one {# үйлдэл} other {# үйлдэл}}" - }, - "origin_external": "Гадаад", - "managed_by_tooltip": "{source} удирддаг. Үүнийг тухайн аппликешнд засварлах эсвэл түүхий шигшүүр засварлагчийг ашиглана уу." - }, - "templates": { - "title": "И-мэйл загварууд", - "description": "Орлуулагч хувьсагчтай дахин ашиглах боломжтой имэйлийн загваруудыг үүсгэх", - "add": "Шинэ загвар", - "edit": "Загвар засах", - "name": "Загвар нэр", - "name_placeholder": "жишээ нь, дагах имэйл", - "category": "Ангилал", - "category_placeholder": "жишээ нь, Ажил, Хувийн", - "subject": "Сэдэв", - "subject_placeholder": "И-мэйл гарчиг", - "body": "Бие", - "body_placeholder": "И-мэйл үндсэн контент...", - "recipients_placeholder": "email@example.com", - "identity": "Send as", - "default_identity": "Өгөгдмөл таниулбар", - "favorite": "Дуртай", - "cancel": "Цуцлах", - "create": "Үйлдвэрлэх", - "update": "Шинэчлэлт", - "confirm_delete": "Устгах", - "no_templates": "Одоохондоо загвар байхгүй", - "manage": "Загваруудыг удирдах", - "count": "{count, plural, one {# загвар} other {# загвар}}", - "export_import": "Экспорт & Импорт", - "export_import_description": "Загваруудаа нөөцлөх эсвэл өөр төхөөрөмж рүү шилжүүлэх", - "export": "Экспорт хийх", - "import": "Импорт", - "validation": { - "empty": "Загварын нэр шаардлагатай", - "too_long": "Загварын нэр 200 ба түүнээс бага тэмдэгт байх ёстой" - } - }, - "files": { - "display": { - "title": "Дэлгэц", - "description": "Файл, фолдерыг хэрхэн харуулахыг тохируулах" - }, - "folder_layout": { - "label": "Хавтасны зохион байгуулалт", - "description": "Фолдеруудыг хэрхэн харуулахыг сонгоно уу: файлын дотор эсвэл хажуугийн модонд", - "inline": "Мөр дотор", - "sidebar": "Хажуугийн самбар" - }, - "default_view": { - "label": "Өгөгдмөл харах", - "description": "Сүлжээ болон жагсаалтын байршлаас сонгох", - "list": "Жагсаалт", - "grid": "Сүлжээ" - }, - "default_sort": { - "label": "Өгөгдмөл эрэмбэлэх", - "description": "Файлуудын анхдагч эрэмбэлэхийг сонгоно уу", - "name": "Нэр", - "size": "Хэмжээ", - "modified": "Өөрчлөгдсөн" - }, - "sort_direction": { - "label": "Ангилах чиглэл", - "description": "Өсөх эсвэл буурах дарааллыг сонгоно", - "ascending": "Өсөх", - "descending": "Буурах" - }, - "icons": { - "title": "Тэмдэгтүүд", - "description": "Файлын дүрсний харагдах байдлыг тохируулах" - }, - "show_icons": { - "label": "Файлын дүрсийг харуулах", - "description": "Файл, фолдерын хажууд дүрсүүдийг харуулах" - }, - "colored_icons": { - "label": "Өнгөт дүрсүүд", - "description": "Нэг өнгийн дүрсний оронд өнгөлөг дүрс ашиглах" - }, - "show_thumbnails": { - "label": "Дөнгөж зураг харуулах", - "description": "Зургийн файлуудын дүрсийн оронд зургийн урьдчилан харахыг харуулах" - }, - "behavior": { - "title": "Зан төлөв", - "description": "Файлын хөтөчийн ажиллагааг тохируулах" - }, - "show_hidden": { - "label": "Нуугдсан файлуудыг харуулах", - "description": "Цэгээр эхэлсэн файл, хавтсыг харуулах" - }, - "preview": { - "label": "Урьдчилан харах" - } - } - }, - "errors": { - "page_error_title": "Алдаа гарлаа", - "page_error_description": "Бидэнд гэнэтийн алдаа гарлаа. Дахин оролдох эсвэл нүүр хуудас руу буцна уу.", - "sidebar_error": "Шуудангийн хайрцгийг ачаалах боломжгүй", - "email_list_error": "Имэйл ачаалах боломжгүй байна", - "viewer_error_title": "Имэйл харуулах боломжгүй", - "viewer_error_description": "Энэ имэйлийг үзүүлэхэд асуудал гарлаа. Энэ нь дэмжигдээгүй контент агуулж болзошгүй.", - "composer_error": "Хөгжмийн зохиолчийг ачаалах боломжгүй", - "settings_error_title": "Тохиргоо боломжгүй", - "settings_error_description": "Тохиргоог ачаалах боломжгүй. Таны тохиргоо хадгалагдахгүй байж магадгүй.", - "try_again": "Дахин оролдоно уу", - "reload": "Дахин ачаалах", - "reload_emails": "Имэйл дахин ачаалах", - "reload_settings": "Тохиргоог дахин ачаалах", - "retry": "Дахин оролдох", - "go_home": "Ирсэн имэйл рүү очих" - }, - "context_menu": { - "reply": "Хариулах", - "reply_all": "Бүгдэд нь хариулах", - "forward": "Урагшаа", - "mark_read": "Уншсан гэж тэмдэглэх", - "mark_unread": "Уншаагүй гэж тэмдэглэх", - "star": "од", - "unstar": "Одгүй", - "pin": "Бэхлэх", - "unpin": "Буулгах", - "move_to": "...", - "archive": "Архив", - "delete": "Устгах", - "mark_as_spam": "Спам мэдээлэх", - "not_spam": "Спам биш", - "tag": "Таг", - "items_selected": "{count} имэйл сонгосон", - "edit_draft": "Ноорог засах", - "cancel_scheduled_send": "Илгээхийг цуцлах", - "reschedule_send": "Хуваарь солих", - "cancel_and_edit": "Цуцлах, засварлах", - "cancel_and_compose_again": "Цуцлаад дахин бичих" - }, - "mailbox_context_menu": { - "mark_folder_read": "Хавтсыг уншсан гэж тэмдэглэх", - "mark_folder_tree_read": "Хавтас болон дэд хавтаснуудыг уншсан гэж тэмдэглэх", - "mark_all_folders_read": "Бүх хавтсыг уншсан гэж тэмдэглэх", - "new_subfolder": "Шинэ дэд хавтас...", - "new_folder": "Шинэ хавтас...", - "rename": "Нэр өөрчлөх...", - "share": "Хуваалцах...", - "import_email": "Импорт .eml эсвэл .zip...", - "empty_folder": "Хоосон хавтас", - "empty_folder_generic": "Хоосон хавтас", - "delete_folder": "Хавтас устгах", - "refresh": "Сэргээх", - "mark_all_confirm_title": "Бүх хавтсыг уншсан гэж тэмдэглэх", - "mark_all_confirm_message": "Хувийн бүртгэлийнхээ уншаагүй мессеж бүрийг уншсан гэж тэмдэглэх үү?", - "delete_confirm_title": "Хавтас устгах", - "delete_confirm_message": "\"{name}\" хавтсыг бүрмөсөн устгах уу? Энэ үйлдлийг буцаах боломжгүй.", - "prompt_new_subfolder": "Шинэ дэд хавтасны нэрийг оруулна уу.", - "prompt_new_folder": "Шинэ хавтсын нэрийг оруулна уу.", - "prompt_rename": "Энэ хавтсанд шинэ нэр оруулна уу.", - "placeholder_folder_name": "Хавтасны нэр", - "create": "Бүтээх", - "rename_confirm": "Нэрээ өөрчлөх", - "toast_marked_read": "Хавтсыг уншсан гэж тэмдэглэлээ", - "toast_marked_read_count": "{count, plural, one {1 зурвасыг уншсан гэж тэмдэглэлээ} other {# зурвасыг уншсан гэж тэмдэглэлээ}}", - "toast_already_read": "Уншаагүй мессеж байхгүй", - "toast_marked_all_read": "Бүх хавтсыг уншсан гэж тэмдэглэлээ", - "toast_emptied": "Хавтас хоослогдсон", - "toast_folder_created": "Хавтас үүслээ", - "toast_folder_renamed": "Хавтсын нэрийг өөрчиллөө", - "toast_folder_deleted": "Хавтас устлаа", - "toast_error_mark_read": "Уншсан гэж тэмдэглэж чадсангүй", - "toast_error_empty": "Хавтсыг хоосолж чадсангүй", - "toast_error_create": "Хавтас үүсгэж чадсангүй", - "toast_error_rename": "Хавтсын нэрийг өөрчилж чадсангүй", - "toast_error_delete": "Хавтсыг устгаж чадсангүй", - "toast_error_delete_has_children": "Хавтас дэд хавтастай байна. Эхлээд тэдгээрийг устгана уу.", - "toast_error_delete_has_email": "Хавтас хоосон биш байна. Эхлээд үүнийг хоосло.", - "delete_with_contents_title": "Хавтас хоосон биш байна", - "delete_with_contents_message": "\"{name}\" хавтсанд одоо ч {count} зурвас байна. Хавтас болон бүх зурвасыг устгах уу? Үүнийг буцаах боломжгүй.", - "delete_with_contents_confirm": "Хавтас болон зурвасыг устгах" - }, - "shortcuts": { - "title": "Гарын товчлол", - "tip": "Энэ тусламжийг хүссэн үедээ ? дарж нээж болно", - "sections": { - "navigation": "Навигаци", - "actions": "Имэйлийн үйлдлүүд", - "global": "Ерөнхий", - "threads": "Хэлхээсүүд", - "composer": "Бичих цонх" - }, - "navigation": { - "next_email": "Дараагийн имэйл", - "previous_email": "Өмнөх имэйл", - "open_email": "Имэйл нээх", - "close_email": "Хаах / Сонголтыг цуцлах" - }, - "actions": { - "reply": "Хариулах", - "reply_all": "Бүгдэд нь хариулах", - "forward": "Урагшаа", - "star": "Одыг асаах/унтраах", - "archive": "Архив", - "delete": "Устгах", - "mark_unread": "Уншаагүй гэж тэмдэглэх", - "mark_read": "Уншсан гэж тэмдэглэх", - "toggle_spam": "Спам мэдээлэх / Спам биш" - }, - "global": { - "compose": "Шинэ имэйл бичих", - "search": "Хайлт руу шилжих", - "help": "Товчлол харуулах", - "refresh": "Имэйл сэргээх", - "select_all": "Бүгдийг сонго", - "global_search": "Бүгдийг хайх" - }, - "threads": { - "expand_collapse": "Хэлхээсийг дэлгэх/хумих" - }, - "composer": { - "send": "Имэйл илгээх", - "schedule_send": "Хуваарь илгээх", - "template_picker": "Загвар сонгогчийг нээх" - } - }, - "threads": { - "messages_one": "{count} мессеж", - "messages_other": "{count} мессежүүд", - "messages_tooltip": "{count, plural, one {Энэ харилцан ярианд # зурвас байна} other {Энэ харилцан ярианд # зурвас байна}}", - "expand": "Харилцан яриаг өргөжүүлэх", - "collapse": "Харилцан яриаг хураах", - "loading": "Харилцан яриаг ачаалж байна...", - "mark_read": "Харилцан яриаг уншсан гэж тэмдэглэх", - "mark_unread": "Харилцан яриаг уншаагүй гэж тэмдэглэх", - "archive": "Харилцан яриаг архивлах", - "delete": "Харилцан яриаг устгах", - "star": "Харилцан яриаг одтой болгох", - "unstar": "Харилцан ярианы одыг авах", - "toggle_thread": "Хэлхээсийг дэлгэх/хумих" - }, - "identities": { - "modal_title": "Илгээж буй таниулбаруудыг удирдах", - "create_new": "Шинэ таниулбар үүсгэх", - "edit_identity": "Таниулбар засах", - "delete_confirm": "Энэ таниулбарыг устгах уу? Үүнийг буцаах боломжгүй.", - "cannot_delete": "Энэ таниулбарыг устгах боломжгүй", - "primary_identity": "Үндсэн", - "set_as_primary": "Үндсэн гэж тохируулах", - "no_identities": "Таниулбар олдсонгүй", - "display": { - "reply_to": "Хариулт:", - "bcc": "BCC:", - "signature": "Гарын үсэг:", - "preview": "Урьдчилан үзэх:" - }, - "validation_errors": { - "invalid_emails": "Буруу имэйл: {emails}", - "unknown_error": "Үл мэдэгдэх алдаа" - }, - "form": { - "name_label": "Дэлгэцийн нэр", - "name_placeholder": "жишээ нь, Ажлын имэйл, Хувийн", - "name_required": "Нэр оруулах шаардлагатай", - "email_label": "Имэйл хаяг", - "email_placeholder": "your.email@example.com", - "email_required": "И-мэйл шаардлагатай", - "email_invalid": "Хүчинтэй имэйл хаяг оруулна уу", - "email_immutable": "И-мэйл хаягийг үүсгэсний дараа өөрчлөх боломжгүй", - "reply_to_label": "Хариу бичих (заавал биш)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "Авто BCC (заавал биш)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "Текст гарын үсэг", - "html_signature_label": "HTML гарын үсэг", - "signature_byte_counter": "{bytes} / {max} байт", - "signature_byte_limit_reached": "Серверийн хязгаарт хүрсэн", - "save": "Таниулбарыг хадгалах", - "cancel": "Цуцлах", - "creating": "Бүтээж байна...", - "updating": "Шинэчилж байна..." - }, - "sub_address": { - "button_tooltip": "Дэд хаяг ашиглах", - "popover_title": "Дэд хаягийн шошго нэмэх", - "tag_input_placeholder": "Таг оруулна уу (жишээ нь, дэлгүүр хэсэх)", - "preview_label": "Урьдчилан үзэх:", - "recent_tags": "Сүүлийн шошго", - "suggested_tags": "Санал болгосон", - "use_address": "Энэ хаягийг ашиглах", - "invalid_tag": "Таг нь зөвхөн үсэг, тоо, зураастай байх ёстой", - "tag_too_long": "Таг нь 30 тэмдэгт буюу түүнээс бага байх ёстой", - "help_text": "user+tag@domain.com руу ирсэн имэйлүүд таны ирсэн хайрцагт хүрнэ", - "validation": { - "empty": "Таг хоосон байж болохгүй", - "too_long": "Таг нь {max} буюу түүнээс бага тэмдэгт байх ёстой", - "invalid_chars": "Таг нь зөвхөн үсэг, тоо, зураас агуулсан байх ёстой" - } - }, - "badge": { - "sent_via": "дамжуулан", - "sub_address_tag": "Дэд хаягийг ашиглан илгээсэн: {tag}", - "identity_name": "{name} таниулбараар илгээсэн", - "identity_short": "{name}-ээр дамжуулан", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Устгах", - "delete_confirm_title": "Таниулбар устгах" - }, - "templates": { - "picker_title": "Загвар сонгох", - "search_placeholder": "Загвар хайх...", - "section_favorites": "Дуртай зүйлс", - "section_recent": "Саяхан", - "section_uncategorized": "Бусад", - "no_templates": "Одоохондоо загвар байхгүй", - "no_results": "Загвар олдсонгүй", - "fill_placeholders": "Орлуулагчийн утгыг бөглөх", - "enter_value": "Утгыг оруулна уу...", - "preview": "Урьдчилан харах", - "insert_with_values": "Утгатай оруулах", - "insert_raw": "Түүхий оруулах", - "copy_suffix": "(хуулбар)", - "placeholder": "Хувьсагч", - "placeholders": { - "recipient_name": "Хүлээн авагчийн нэр", - "company": "Компанийн нэр", - "date": "Одоогийн огноо", - "day_of_week": "Долоо хоногийн өдөр", - "sender_name": "Таны нэр" - } - }, - "contacts": { - "title": "Харилцах утас", - "search_placeholder": "Харилцагч хайх...", - "create_new": "Шинэ холбоо барих", - "no_category": "Ангилалгүй", - "rename_category": "Ангиллын нэрийг өөрчлөх", - "category_name_label": "Ангиллын нэр", - "category_renamed": "Ангиллын нэрийг өөрчилсөн", - "category_rename_failed": "Ангиллын нэрийг өөрчилж чадсангүй", - "category_added": "Харилцагчийг {name}", - "category_added_plural": "{count} харилцагчдыг {name}", - "empty_state": "Харилцагч байхгүй", - "empty_state_title": "Хараахан холбоо барих хүн алга", - "empty_state_subtitle": "Анхны харилцагчаа үүсгэх эсвэл vCard файлаас импортлох", - "empty_search": "Таны хайлтад тохирох харилцагч алга", - "empty_search_hint": "Өөр хайлтын үг хэрэглэж үзнэ үү", - "empty_filtered": "Таны шүүлтүүрт тохирох харилцагч алга", - "empty_filtered_hint": "Шүүлтүүрийг тохируулж эсвэл цэвэрлэж үзнэ үү", - "clear_search": "Хайлтыг арилгах", - "import_vcard": "vCard импортлох", - "delete_confirm_title": "Харилцагчийг устгах", - "delete_confirm": "Та энэ харилцагчийг устгахдаа итгэлтэй байна уу?", - "local_mode": "Харилцагчдыг дотооддоо хадгалдаг (сервер нь JMAP Contacts-ийг дэмждэггүй)", - "back_to_contacts": "Харилцагч руу буцах", - "back_to_email": "Имэйл рүү буцах", - "open_categories": "Нээлттэй ангилалууд", - "tabs": { - "all": "Бүгд", - "groups": "Бүлэг" - }, - "shared": { - "title": "Хуваалцсан" - }, - "address_books": { - "create": "Шинэ хаягийн дэвтэр", - "created": "Хаягийн дэвтэр үүсгэсэн", - "create_failed": "Хаягийн дэвтэр үүсгэж чадсангүй", - "title": "Миний хаягийн дэвтэр", - "shared_prefix": "Хуваалцсан: {name}", - "moved": "Харилцагчийг {name}", - "moved_plural": "{count} харилцагчдыг {name}", - "move_failed": "Харилцагчийг зөөж чадсангүй", - "address_book": "Хаягийн дэвтэр", - "rename": "Хаягийн дэвтрийн нэрийг өөрчлөх", - "name_label": "Хаяг номын нэр", - "renamed": "Хаягийн дэвтрийн нэрийг өөрчилсөн", - "rename_failed": "Хаягийн дэвтрийн нэрийг өөрчилж чадсангүй", - "default": "Өгөгдмөл", - "set_default": "Өгөгдмөлөөр тохируулах", - "default_updated": "Өгөгдмөл хаягийн дэвтэр шинэчлэгдсэн", - "set_default_failed": "Өгөгдмөл хаягийн дэвтрийг тохируулж чадсангүй", - "manage": "Хаягийн дэвтэр удирдах", - "share": "Хаягийн дэвтэр хуваалцах", - "new_contact_in_book": "Энэ хаягийн дэвтэрт шинэ харилцагч", - "delete": "Хаягийн дэвтэр устгах", - "confirm_delete": "\"{name}\"-г устгах уу? Энэ хаягийн дэвтэр дэх бүх харилцагчийг устгах болно.", - "deleted": "Хаягийн дэвтэр устсан", - "delete_failed": "Хаягийн дэвтрийг устгаж чадсангүй" - }, - "detail": { - "emails": "Имэйл хаягууд", - "phones": "Утасны дугаар", - "organizations": "Байгууллага", - "addresses": "Хаяг", - "notes": "Тэмдэглэл", - "titles": "Гарчиг ба үүрэг", - "online_services": "Онлайн үйлчилгээ", - "anniversaries": "Тэмдэг өдөр", - "personal_info": "Хувийн мэдээлэл", - "languages": "Хэлнүүд", - "categories": "Ангилал", - "related_contacts": "Холбогдох холбоо барих хаягууд", - "crypto_keys": "Крипто түлхүүр", - "cert_issuer": " Үнэт цаас гаргагч", - "cert_expires": "Хугацаа нь дуусна", - "cert_expired": "Хугацаа дууссан", - "cert_fingerprint": "Хурууны хээ", - "cert_algorithm": "Алгоритм", - "import_to_smime": "S/MIME", - "cert_already_imported": "S/MIME", - "cert_imported": "Сертификат S/MIME дэлгүүрт импортлогдсон", - "cert_import_failed": "Сертификат импортлож чадсангүй", - "no_contact_selected": "Дэлгэрэнгүйг харахын тулд харилцагчаа сонгоно уу", - "compose_email": "Имэйл бичих", - "copy_email": "Имэйл хуулах", - "copy_phone": "Утасны дугаарыг хуулах", - "copy_url": "URL хуулах", - "copied": " Түр санах ой руу хуулсан", - "copy_failed": " Түр санах ой руу хуулж чадсангүй", - "created": "Бүтээсэн", - "updated": "Сүүлд шинэчлэгдсэн", - "timezone": "Цагийн бүс", - "anniversary_birth": "Төрсөн өдөр", - "anniversary_death": "Нас барсан", - "anniversary_wedding": "жилийн ой", - "anniversary_other": "Бусад", - "personal_expertise": "Мэргэжил", - "personal_hobby": "Хобби", - "personal_interest": "Сонирхол", - "personal_other": "Бусад", - "gender": "Хүйс", - "gender_masculine": "Эрэгтэй", - "gender_feminine": "Эмэгтэй", - "gender_other": "Бусад", - "gender_none": "Хэрэглэхгүй", - "gender_unknown": "Тодорхойгүй", - "calendar": "Хуанли", - "calendar_uri": "Календарийн URL", - "scheduling_uri": "Хуваарь URL", - "freebusy_uri": "Үнэгүй/Завгүй URL", - "section_contact": "Холбоо барих мэдээлэл", - "section_work": "Ажил", - "section_personal": "Хувийн", - "email_default_label": "Имэйл", - "phone_default_label": "Утас", - "address_default_label": "Хаяг", - "online_service_default_label": "Онлайн", - "organization_label": "Байгууллага", - "title_label": "Гарчиг", - "role_label": "Үүрэг", - "language_label": "Хэл", - "related_default_label": "Холбогдох", - "more_actions": "Дэлгэрэнгүй үйлдэл", - "age_years": "{count, plural, one {1 настай} other {# настай}}", - "years_since": "{count, plural, one {1 жил} other {# жил}}" - }, - "activity": { - "recent_emails": "Саяхан ирсэн имэйлүүд", - "upcoming_events": "Удахгүй болох үйл явдлууд", - "no_emails": "Саяхны имэйл байхгүй", - "no_events": "Удахгүй болох арга хэмжээ байхгүй", - "no_subject": "(Сэдэв байхгүй)", - "no_title": "(Гарчиггүй)", - "load_failed": "Ачааж чадсангүй", - "unknown_sender": "Үл мэдэгдэх илгээгч", - "all_day": "Бүтэн өдөр" - }, - "form": { - "create_title": "Шинэ холбоо барих", - "edit_title": "Харилцагчийг засах", - "section_address_book": "Лавлах", - "select_address_book": "Лавлах сонгох...", - "section_identity": "Нэр & Хувийн мэдээлэл", - "contact_type": "Холбоо барих төрөл", - "type_person": "Хүн", - "type_organization": "Байгууллага", - "section_work": "Ажил, Байгууллага", - "prefix": "Угтвар", - "prefix_placeholder": "Доктор, ноён, хатагтай", - "given_name": "Нэр", - "middle_name": "Дундад нэр", - "surname": "Овог", - "suffix": "Дагавар", - "suffix_placeholder": "Бага, ах, III", - "nickname": "Хоч", - "nickname_placeholder": "Хоч", - "email": "И-мэйл", - "email_placeholder": "email@example.com", - "phone": "Утас", - "phone_placeholder": "+1 234 567 890", - "phone_type": "Төрөл", - "phone_voice": "Дуу хоолой", - "phone_cell": "Гар утас", - "phone_fax": "Факс", - "phone_pager": "Пэйжэр", - "phone_video": "Видео", - "phone_text": "Текст", - "organization": "Байгууллага", - "organization_placeholder": "Компанийн нэр", - "department": "Хэлтэс", - "department_placeholder": "Хэлтэс", - "job_title": "Ажлын байр", - "job_title_placeholder": "жишээ нь, Програм хангамжийн инженер", - "role": "Үүрэг", - "role_placeholder": "жишээ нь, багийн ахлагч", - "addresses": "Хаяг", - "add_address": "Хаяг нэмэх", - "street": "Гудамж", - "city": "Хот", - "region": "Муж/Бүс", - "postcode": "Шуудангийн код", - "country": "Улс", - "online_services": "Онлайн үйлчилгээ", - "add_online_service": "Онлайн үйлчилгээ нэмэх", - "url_placeholder": "https://...", - "service_placeholder": "Үйлчилгээ", - "anniversaries": "Тэмдэг ой", - "add_anniversary": "Огноо нэмэх", - "anniversary_birth": "Төрсөн өдөр", - "anniversary_wedding": "ойн өдөр", - "anniversary_death": "Дурсамж", - "anniversary_other": "Бусад", - "personal_info": "Хувийн мэдээлэл", - "add_personal_info": "Оруулга нэмэх", - "personal_info_placeholder": "жишээ нь, гэрэл зураг", - "personal_expertise": "Мэргэжлийн", - "personal_hobby": "Хобби", - "personal_interest": "Сонирхол", - "personal_other": "Бусад", - "level": "Түвшин", - "level_high": "Өндөр", - "level_medium": "Дунд", - "level_low": "Бага", - "categories": "Ангилал", - "categories_placeholder": "жишээ нь, гэр бүл, найз нөхөд, хамт ажиллагсад", - "categories_hint": "Ангилал нэмэх эсвэл хайхын тулд бичнэ үү", - "category_add": "Нэмэх", - "note": "Тэмдэглэл", - "note_placeholder": "Тэмдэглэл нэмэх...", - "gender": "Хүйс", - "gender_sex": "Секс", - "gender_male": "Эрэгтэй", - "gender_female": "Эмэгтэй", - "gender_other": "Бусад", - "gender_none": "Хэрэглэхгүй", - "gender_unknown": "Тодорхойгүй", - "gender_identity": "Жендэрийн баримжаа", - "gender_identity_placeholder": "Жендэрийн баримжаа...", - "calendar": "Хуанли", - "calendar_uri": "Календарийн URL", - "scheduling_uri": "Хуваарьлах URL", - "freebusy_uri": "Үнэгүй/Завгүй URL", - "context_work": "Ажил", - "context_private": "Хувийн", - "add_email": "Имэйл нэмэх", - "add_phone": "Утас нэмэх", - "save": "Хадгалах", - "cancel": "Цуцлах", - "creating": "Бүтээж байна...", - "updating": "Шинэчилж байна...", - "name_required": "Нэр, овог, байгууллагыг оруулна уу", - "email_invalid": "Хүчинтэй имэйл хаяг оруулна уу", - "email_error_inline": "Имэйл формат буруу", - "save_failed": "Харилцагчийг хадгалж чадсангүй", - "delete": "Устгах", - "upload_photo": "Зураг байршуулах", - "remove_photo": "Зураг арилгах", - "photo_hint": "JPG эсвэл PNG, 10 МБ хүртэл. Хэмжээ өөрчлөгдөнө.", - "photo_too_large": "Зураг хэт том байна (хамгийн ихдээ 10 MB)", - "photo_invalid": "Зургийн файл буруу", - "change_photo": "Өөрчлөлт" - }, - "groups": { - "create": "Шинэ групп", - "edit": "Засварлах групп", - "empty": "Одоохондоо бүлэг байхгүй", - "delete_confirm_title": "Бүлгийг устгах", - "delete_confirm": "Та энэ бүлгийг устгахдаа итгэлтэй байна уу?", - "name_label": "Бүлгийн нэр", - "name_placeholder": "жишээ нь, Баг, Гэр бүл", - "name_required": "Бүлгийн нэр шаардлагатай", - "save_failed": "Бүлгийг хадгалж чадсангүй", - "members_label": "Гишүүд", - "search_members": "Нэмэх харилцагчдыг хайх...", - "no_members": "Энэ бүлэгт гишүүн алга", - "member_count": "{count, plural, =0 {Гишүүд байхгүй} one {1 гишүүн} other {# гишүүн}}", - "send_email": "Групп руу имэйл илгээх", - "send_email_to": "руу", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "Энэ бүлэгт имэйл хаягтай гишүүн байхгүй." - }, - "import": { - "title": "Харилцагчдыг импортлох", - "drop_hint": "vCard файлыг сонгохын тулд товшино уу", - "file_types": ".vcf эсвэл .vcard файлууд", - "no_contacts": "Файлд харилцагч олдсонгүй", - "parse_error": "vCard файлыг задлан шинжилж чадсангүй", - "found": "{count, plural, one {1 харилцагч олдсон} other {# харилцагч олдсон}}", - "duplicate": "Давхардах", - "select_all": "Бүгдийг нь сонго", - "deselect_all": "Бүх сонголтыг цуцлах", - "selected": "{count, plural, one {1 сонгосон} other {# сонгосон}}", - "import_button": "Импорт", - "importing": "Импорт хийж байна...", - "success": "{count, plural, one {1 харилцагч импортлогдсон} other {# харилцагч импортлогдсон}}", - "failed": "Импорт амжилтгүй боллоо", - "close": "Хаах", - "file_too_large": "Файл хэт том байна (хамгийн ихдээ 5 MB)" - }, - "export": { - "title": "Харилцагчдыг экспортлох", - "success": "{count, plural, one {1 контакт экспортлогдлоо} other {# контакт экспортлогдлоо}}" - }, - "bulk": { - "selected": "{count, plural, one {1 сонгогдсон} other {# сонгогдсон}}", - "select_all": "Бүгдийг нь сонго", - "delete": "Устгах", - "delete_confirm_title": "Харилцагчдыг устгах", - "delete_confirm": "{count, plural, one {1 контакт} other {# контакт}} устгах уу?", - "deleted": "{count, plural, one {1 контакт устгагдлаа} other {# контакт устгагдлаа}}", - "add_to_group": "Бүлэгт нэмэх", - "choose_group": "Бүлэг сонгох", - "adding_contacts": "{count, plural, one {1 контакт} other {# контакт}} нэмэж байна", - "added_to_group": "Харилцагчдыг группт нэмсэн", - "export": "Экспорт хийх", - "clear": "Сонголтыг арилгах" - }, - "toast": { - "created": "Харилцагч үүсгэсэн", - "updated": "Харилцагч шинэчлэгдсэн", - "deleted": "Харилцагчийг устгасан", - "error_create": "Харилцагч үүсгэж чадсангүй", - "error_update": "Харилцагчийг шинэчилж чадсангүй", - "error_delete": "Харилцагчийг устгаж чадсангүй" - }, - "context_menu": { - "open": "Нээлттэй", - "edit": "Засварлах", - "send_email": "Имэйл илгээх", - "add_to_group": "Бүлэгт нэмэх", - "export_vcard": "vCard хэлбэрээр экспортлох", - "delete": "Устгах", - "call": "Дуудлага", - "duplicate": "Давхардах", - "print": "Хэвлэх" - }, - "filters": { - "toggle": "Шүүлтүүр", - "select": "Сонгох", - "clear": "Цэвэр", - "close": "Хаах", - "title": "Дэвшилтэт шүүлтүүрүүд", - "organization": "Компани", - "organization_placeholder": "жишээ нь. Acme Corp", - "job_title": "Ажлын байр", - "job_title_placeholder": "жишээ нь. Дизайнер", - "location": "Байршил", - "location_placeholder": "Хот эсвэл улс", - "email_domain": "Имэйл домэйн", - "email_domain_placeholder": "example.com", - "birthday_month": "Төрсөн өдөр", - "any_month": "Ямар ч сар", - "has_email": "Имэйл хаягтай", - "has_phone": "Утастай", - "has_photo": "Зурагтай", - "sort_by": "Эрэмбэлэх", - "sort_first_name": "Нэр", - "sort_last_name": "Овог" - } - }, - "calendar": { - "title": "Хуанли", - "back_to_email": "Имэйл рүү буцах", - "back_to_month": "Сар руу буцах", - "my_calendars": "Хуанли", - "birthday_calendar": "Төрсөн өдөр", - "mini_calendar_change": "Сарыг өөрчлөхийн тулд дарна уу", - "views": { - "month": "сар", - "week": "Долоо хоног", - "day": "Өдөр", - "agenda": "Хэлэлцэх асуудал", - "today": "Өнөөдөр", - "month_hint": "Сар (м)", - "week_hint": "Долоо хоног (w)", - "day_hint": "Өдөр (г)", - "agenda_hint": "Хэлэлцэх асуудал (a)", - "tasks": "Даалгаварууд", - "tasks_hint": "Даалгавар (k)" - }, - "events": { - "create": "Үйл явдал үүсгэх", - "edit": "Арга хэмжээг засах", - "delete": "Үйл явдлыг устгах", - "details": "Үйл явдлын дэлгэрэнгүй мэдээлэл", - "no_events": "Үйл явдал байхгүй", - "all_day": "Бүтэн өдөр", - "more": "+{count} дэлгэрэнгүй", - "no_title": "(Гарчиггүй)", - "resize": "Үйл явдлын хэмжээг өөрчлөх", - "duplicate": "Давхардсан", - "today_header": "Өнөөдөр", - "tomorrow_header": "Маргааш", - "agenda_show_earlier": "Өмнөх үйл явдлуудыг харуулах", - "agenda_loading": "Ачаалж байна…", - "agenda_range_start": "{date}-с хойшхи үйл явдлууд", - "agenda_range_end": "{date} хүртэлх үйл явдлууд", - "export_ics": ".ics", - "copy_title": "Гарчиг хуулах", - "copy_link": "Уулзалтын холбоосыг хуулах", - "new_event": "Шинэ арга хэмжээ", - "new_all_day_event": "Бүтэн өдрийн шинэ арга хэмжээ", - "new_task": "Шинэ даалгавар", - "go_to_today": "Өнөөдөр рүү очих" - }, - "detail": { - "add_note": "Тэмдэглэл нэмэх...", - "save_note": "Хадгалах", - "note_saved": "Тэмдэглэл нэмсэн", - "open_link": "Нээлттэй холбоос", - "meeting_link": "Уулзалтын холбоос", - "tentative": "Урьдчилсан", - "cancelled": "Цуцлагдсан", - "delete_confirm": "Энэ үйл явдлыг устгах уу?" - }, - "form": { - "title": "Гарчиг", - "description": "Тодорхойлолт", - "location": "Байршил", - "meeting_link": "Уулзалтын холбоос", - "start_date": "Эхлэх огноо", - "end_date": "Төгсгөлийн огноо", - "start_time": "Эхлэх цаг", - "end_time": "Төгсгөлийн цаг", - "all_day_event": "Бүтэн өдрийн арга хэмжээ", - "calendar_select": "Хуанли", - "save": "Хадгалах", - "cancel": "Цуцлах", - "delete_confirm": "Та энэ үйл явдлыг устгахдаа итгэлтэй байна уу?", - "color": "Өнгө" - }, - "participants": { - "availability": { - "title": "Боломжтой эсэх", - "free": "Боломжтой", - "busy": "Завгүй", - "tentative": "Урьдчилсан", - "unknown": "Энэ серверийн хэрэглэгч биш", - "checking": "Шалгаж байна…" - }, - "title": "Оролцогчид", - "add": "Оролцогч нэмэх", - "organizer": "Зохион байгуулагч", - "attendee": "Оролцогч", - "accepted": "Хүлээн зөвшөөрсөн", - "declined": "Татгалзсан", - "tentative": "Урьдчилсан", - "needs_action": "Арга хэмжээ авах шаардлагатай", - "remove": "Устгах", - "edit": "Засварлах", - "email_placeholder": "Имэйл хаяг нэмэх эсвэл харилцагч хайх", - "send_invitations": "Оролцогчдод урилга илгээх", - "status_summary": "{accepted} зөвшөөрсөн, {pending} хүлээгдэж байна", - "invited_by": "{name}", - "respond_below": "Доорх товчлууруудыг ашиглан хариу бичнэ үү", - "rsvp_label": "Таны хариулт", - "cancel_notification": "Оролцогчдод цуцлагдсан тухай мэдэгдэх болно", - "you_organizer": "Та бол зохион байгуулагч", - "you_attendee": "Та оролцогч байна", - "no_participants": "Оролцогчгүй", - "count": "{count, plural, one {# оролцогч} other {# оролцогч}}" - }, - "recurrence": { - "title": "Дахилт", - "none": "Дахин давтагдахгүй", - "daily": "Өдөр бүр", - "weekly": "Долоо хоног бүр", - "monthly": "Сарын", - "yearly": "Жил", - "every_n_days": "{count} өдөр бүр", - "every_n_weeks": "{count} долоо хоног бүр", - "every_n_months": "{count} сар бүр", - "until": "хүртэл", - "occurrences": "{count} тохиолдлууд", - "custom": "Захиалгат…", - "edit_custom": "Захиалгат дахилтыг засах", - "every_n_years": "{count} жил бүр", - "on_days": "{days}-ны өдөр", - "on_day_n": "өдөр {day}", - "on_the_nth": "{nth} {day}", - "in_month": "{month}", - "nth_1": "эхний", - "nth_2": "секунд", - "nth_3": "гурав дахь", - "nth_4": "дөрөвдүгээр", - "nth_last": "сүүлийн", - "editor_freq_day": "Өдөр", - "editor_freq_week": "Долоо хоног", - "editor_freq_month": "сар", - "editor_freq_year": "Жил", - "editor_repeats_on": "Дахин давтагдана", - "editor_every": "Бүр", - "editor_unit_days": "өдөр", - "editor_unit_weeks": "долоо хоног(ууд)", - "editor_unit_months": "сар", - "editor_unit_years": "жил", - "editor_on_day": "өдөр", - "editor_on_the": "", - "editor_in": "дахь", - "editor_ends": "Төгсгөл", - "editor_never": "Хэзээ ч", - "editor_ends_on": "Асаалттай", - "editor_ends_after": "Дараа", - "editor_occurrences": "тохиолдол" - }, - "recurrence_scope": { - "edit_title": "Давтах үйл явдлыг засах", - "delete_title": "Дахин давтагдах үйл явдлыг устгах", - "description": "Энэ бол давтагдах үйл явдал. Та ямар үйл явдлыг өөрчлөхийг хүсэж байна вэ?", - "this_event": "Зөвхөн энэ арга хэмжээ", - "this_and_future": "Энэ болон дараах үйл явдлууд", - "all_events": "Бүх арга хэмжээ", - "cancel": "Цуцлах", - "save": "Хадгалах", - "delete": "Устгах", - "rsvp_title": "Давтагдах үйл явдалд хариулах", - "rsvp_description": "Энэ бол давтагдах үйл явдал. Таны хариу аль үйл явдлуудад хамаарах вэ?", - "respond": "Хариулах" - }, - "alerts": { - "title": "Сануулгууд", - "none": "Сануулагч байхгүй", - "at_time": "Үйл явдал болох үед", - "minutes_before": "{count, plural, one {# минут өмнө} other {# минут өмнө}}", - "hours_before": "{count, plural, one {# цаг өмнө} other {# цаг өмнө}}", - "days_before": "{count, plural, one {# өдөр өмнө} other {# өдөр өмнө}}", - "weeks_before": "{count, plural, one {# долоо хоног өмнө} other {# долоо хоног өмнө}}", - "unit_minutes_before": "минутын өмнө", - "unit_hours_before": "цагын өмнө", - "unit_days_before": "хоногийн өмнө", - "unit_weeks_before": "долоо хоногийн өмнө", - "add": "Сануулагч нэмэх", - "remove": "Сануулагч устгах", - "amount": "Сануулагчийн хэмжээ", - "unit": "Сануулах нэгж" - }, - "settings": { - "title": "Календарийн тохиргоо", - "default_view": "Өгөгдмөл харагдах байдал", - "week_starts_on": "Долоо хоног", - "time_format": "Цагийн формат", - "default_calendar": "Өгөгдмөл календарь", - "default_reminder": "Өгөгдмөл сануулагч", - "time_format_12h": "12 цаг", - "time_format_24h": "24 цагийн", - "notifications_enabled": "Үйл явдлын мэдэгдэл", - "notifications_enabled_desc": "Удахгүй болох хуанлийн арга хэмжээний тухай сэрэмжлүүлэг харуулах", - "notification_sound": "Мэдэгдэлийн дуу", - "notification_sound_desc": "Календарийн дохиоллын дууг тоглуулах", - "invitation_parsing": "Имэйл урилгыг задлан шинжлэх", - "invitation_parsing_desc": "Имэйл хавсралтаас хуанлийн урилгыг илрүүлж, календарийн үйлдлийг харуулах", - "show_time_in_month_view": "Хугацааг сараар харуулах", - "show_time_in_month_view_desc": "Сарын хуанлийн харагдац дээр үйл явдлын цагийг харуулах. Жижиг дэлгэц дээр энэ нь цэгийн оронд үйл явдлын бүрэн оруулгуудыг харуулдаг.", - "show_week_numbers": "Долоо хоногийн дугаарыг харуулах", - "show_week_numbers_desc": "Ми-календарт долоо хоногийн тоог харуулах", - "calendar_free_scroll": "Чөлөөт гүйлгэлт", - "calendar_free_scroll_desc": "Нэг үеийг харуулахын оронд сар, долоо хоног, өдрүүдийг тасралтгүй гүйлгэх", - "enable_tasks": "Даалгавруудыг идэвхжүүлэх", - "enable_tasks_desc": "Хийх ажлуудыг удирдахын тулд календарьт ажлын харагдах байдлыг харуулах", - "show_tasks_on_calendar": "Хуанли дээр даалгавруудыг харуулах", - "show_tasks_on_calendar_desc": "Даалгаврын чипийг өдөр, долоо хоногийн хуанлийн харагдац дээр харуулах", - "hover_preview": "Үйл явдлыг товшино уу", - "hover_preview_desc": "Календарийн үйл явдлууд дээр хулганыг гүйлгэх үед дэлгэрэнгүй поповыг харуулах", - "hover_preview_instant": "Шуурхай", - "hover_preview_delay_500ms": "0.5 секундын саатал", - "hover_preview_delay_1s": "1 секундын саатал", - "hover_preview_delay_2s": "2 секундын саатал", - "hover_preview_off": "Хөгжлийн бэрхшээлтэй", - "organizer_identity": "Урилгыг зохион байгуулагч", - "organizer_identity_desc": "Хүмүүсийг арга хэмжээнд урихад зохион байгуулагчаар ашиглах хуанлийн хаяг.", - "organizer_identity_failed": "Зохион байгуулагчийн хаягийг өөрчилж чадсангүй", - "show_birthday_calendar": "Холбоо барих төрсөн өдрийн хуанли", - "show_birthday_calendar_desc": "Харилцагчдаас төрсөн өдөр бүхий виртуал хуанли харуулах" - }, - "days": { - "monday": "Даваа гараг", - "tuesday": "Мягмар гараг", - "wednesday": "Лхагва гараг", - "thursday": "Пүрэв гараг", - "friday": "Баасан гараг", - "saturday": "Бямба гариг", - "sunday": "Ням гараг", - "mon": "Даваа", - "tue": "Мягмар", - "wed": "Лхагва", - "thu": "Пүрэв", - "fri": "Баасан", - "sat": "Бямба", - "sun": "Нар" - }, - "months": { - "jan": "1-р сар", - "feb": "Хоёрдугаар сар", - "mar": "3-р сар", - "apr": "4-р сарын", - "may": "Тавдугаар сар", - "jun": "6-р сар", - "jul": "7-р сар", - "aug": "Наймдугаар сар", - "sep": "9-р сар", - "oct": "10-р сарын", - "nov": "11-р сарын", - "dec": "12-р сар", - "far": "Фарвардин", - "ord": "Ордибэхэшт", - "kho": "Хордад", - "tir": "Тир", - "mor": "Мордад", - "sha": "Шахривар", - "meh": "Mehr", - "aba": "Абан", - "aza": "Азар", - "dey": "Дэй", - "bah": "Бахман", - "esf": "Эсфанд" - }, - "notifications": { - "event_created": "Үйл явдал үүсгэсэн", - "event_updated": "Үйл явдал шинэчлэгдсэн", - "event_deleted": "Үйл явдал устгагдсан", - "calendar_created": "Хуанли үүсгэсэн", - "calendar_deleted": "Хуанли устгасан", - "event_move_error": "Үйл явдлыг зөөж чадсангүй", - "event_resize_error": "Үйл явдлын хэмжээг өөрчилж чадсангүй", - "alert_title": "Удахгүй болох арга хэмжээ", - "alert_now": "Одооноос эхлэн", - "alert_in_minutes": "{count} мин", - "invitation_sent": "Урилга илгээсэн", - "invitations_denied_title": "Урилга илгээх боломжгүй", - "invitations_denied": "Сервер урилга илгээхээс татгалзлаа: {reason}. Урилга илгээхгүйгээр үйл явдлыг хадгалах уу?", - "save_without_invitations": "Урилгагүйгээр хадгалах", - "rsvp_updated": "Хариулт шинэчлэгдсэн", - "rsvp_error": "Хариултыг шинэчилж чадсангүй", - "event_duplicated": "Үйл явдал давхардсан", - "event_error": "Үйл явдлыг хадгалж чадсангүй", - "task_due": "Даалгавар дуусах хугацаа", - "event_exported": "Үйл явдал экспортлогдсон", - "title_copied": "Гарчиг хуулсан", - "link_copied": "Холбоос хуулсан" - }, - "status": { - "loading_calendars": "Хуанли ачаалж байна...", - "loading_events": "Арга хэмжээг ачаалж байна..." - }, - "quick_create": { - "placeholder": "Шинэ үйл явдлын гарчиг", - "aria_label": "Түргэн үүсгэх арга хэмжээ" - }, - "nav_prev": "Өмнөх", - "nav_next": "Дараагийн", - "nav_open_menu": "Нээлттэй цэс", - "import": { - "title": "Хуанли импортлох", - "tab_file": "Файл", - "tab_url": "URL", - "select_file": ".ics файлыг сонгоно уу", - "drop_file": "эсвэл файлыг энд хаяна уу", - "supported_formats": "iCalendar (.ics) файлуудыг дэмждэг", - "url_description": "Арга хэмжээг импортлохын тулд гадаад iCalendar (.ics) хангамжийн URL хаягийг оруулна уу.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "iCalendar (.ics) URL-г дэмждэг", - "fetch": "Авах", - "invalid_url": "Хүчинтэй URL оруулна уу", - "url_fetch_failed": "URL", - "parsing": "Календарийн файлыг задлан шинжилж байна...", - "parsed_events": "{count} үйл явдал олдсон", - "no_events": "Файлд ямар ч үйл явдал олдсонгүй", - "select_all": "Бүгдийг нь сонго", - "deselect_all": "Бүгдийг нь сонгоогүй", - "target_calendar": "Хуанли руу импортлох", - "import_button": "Импортыг сонгосон", - "importing": "Үйл явдал импортлож байна...", - "success": "{count} арга хэмжээг амжилттай импортолсон", - "error": "Хуанли импорт хийж чадсангүй", - "file_too_large": "Файл 10МБ-ын хязгаараас хэтэрсэн байна", - "invalid_format": "Хуанли файлын формат буруу байна" - }, - "webcal_action": { - "title": "Календарийн холбоосыг нээх", - "description": "Та \"{name}\"-г хэрхэн ашиглахыг хүсэж байна вэ?", - "import_title": "Нэг удаа импортлох", - "import_description": "Үйл явдлуудыг яг одоо татаж аваад хуанлиныхаа аль нэгэнд хуулна уу.", - "subscribe_title": "Захиалах", - "subscribe_description": "Энэ календарийг автоматаар тусдаа календарь болгон синк хий.", - "cancel": "Цуцлах" - }, - "management": { - "title": "Календарийн менежмент", - "description": "Хуанлигаа үүсгэх, нэрийг нь өөрчлөх, өөрчлөх. Хажуугийн самбар дээрх хуанли дээр хулганы баруун товчийг дарж өнгийг нь хурдан солино.", - "name": "Нэр", - "name_placeholder": "Календарийн нэр", - "color": "Өнгө", - "change_color": "Өнгийг өөрчлөх", - "random_color": "Шинэ санамсаргүй өнгө", - "add_calendar": "Хуанли нэмэх", - "edit": "Засварлах", - "delete": "Устгах", - "save": "Хадгалах", - "create": "Бүтээх", - "cancel": "Цуцлах", - "default": "Өгөгдмөл", - "confirm_delete": "\"{name}\"-г устгах уу? Энэ хуанли дахь бүх үйл явдлууд устах болно.", - "confirm_clear": "\"{name}\"-с бүх үйл явдлыг арилгах уу? Үүнийг буцаах боломжгүй.", - "set_default": "Өгөгдмөлөөр тохируулах", - "default_updated": "Өгөгдмөл календарь шинэчлэгдсэн", - "error_default": "Өгөгдмөл календарийг тохируулж чадсангүй", - "clear_events": "Тодорхой үйл явдлууд", - "events_cleared": "{count} үйл явдлууд арилсан", - "error_clear": "Календарийн үйл явдлуудыг арилгаж чадсангүй", - "calendar_created": "Хуанли үүсгэсэн", - "calendar_updated": "Хуанли шинэчлэгдсэн", - "calendar_deleted": "Хуанли устгасан", - "color_updated": "Календарийн өнгө шинэчлэгдсэн", - "error_create": "Хуанли үүсгэж чадсангүй", - "error_update": "Хуанли шинэчлэгдэж чадсангүй", - "error_delete": "Календарийг устгаж чадсангүй", - "caldav_url": "CalDAV URL", - "copy_url": "CalDAV URL-г хуулах", - "url_copied": "CalDAV URL-г санах ой руу хуулсан", - "share": "Хуанлигаа хуваалцах", - "new_event_in_calendar": "Энэ хуанли дахь шинэ арга хэмжээ", - "kind": "Агуулга", - "kind_events": "Үйл явдлууд", - "kind_tasks": "Даалгаврууд", - "kind_both": "Үйл явдал болон даалгаврууд", - "kind_hint": "CalDAV-аар синк хийхэд энэ хуанлийг аль аппууд харахыг тодорхойлно. Дараа нь өөрчлөх боломжгүй." - }, - "subscription": { - "title": "iCal захиалга", - "section_title": "iCal захиалга", - "description": "Гадны iCalendar хангамжид бүртгүүлнэ үү. Үйл явдлууд автоматаар өөрсдийн календарьт синк хийгдэнэ. https:// болон webcal:// URL-г дэмждэг.", - "url_label": "Календарийн URL", - "url_placeholder": "https://example.com/calendar.ics эсвэл webcal://...", - "name_label": "Календарийн нэр", - "name_placeholder": "жишээ нь. Бүх нийтийн амралтын өдрүүд", - "color_label": "Өнгө", - "refresh_interval": "Сэргээх интервал", - "interval_15": "15 минут тутамд", - "interval_30": "30 минут тутамд", - "interval_60": "Цаг бүр", - "interval_360": "6 цаг тутамд", - "interval_1440": "Өдөр бүр", - "subscribe": "Захиалга өгөх", - "subscribing": "Захиалгаж байна...", - "save": "Өөрчлөлтийг хадгалах", - "saving": "Хадгалж байна...", - "edit": "Засварлах", - "edit_title": "Захиалга засах", - "updated": "Шинэчилсэн \"{name}\"", - "update_error": "Бүртгэлийг шинэчилж чадсангүй", - "invalid_url": "Хүчинтэй URL оруулна уу", - "success": "\"{name}\"-д бүртгүүлсэн", - "error": "Захиалга нэмж чадсангүй", - "refresh": "Одоо сэргээнэ үү", - "refresh_success": "Захиалга шинэчлэгдсэн", - "refresh_error": "Захиалгыг сэргээж чадсангүй", - "unsubscribe": "Бүртгэлээ цуцлах", - "confirm_delete": "\"{name}\"-н бүртгэлээ цуцлах уу? Хуанли болон түүний бүх үйл явдлыг устгах болно.", - "deleted": "Захиалга хасагдсан", - "delete_error": "Захиалга устгаж чадсангүй", - "last_refreshed": "Сүүлд шинэчлэгдсэн: {time}" - }, - "tasks": { - "label": "Даалгаварууд", - "no_tasks": "Даалгавар байхгүй", - "no_title": "(Гарчиггүй)", - "mark_complete": "Бүрэн гэж тэмдэглэх", - "mark_incomplete": "Дутуу гэж тэмдэглэх", - "filter_all": "Бүгд", - "filter_pending": "Хүлээгдэж байна", - "filter_completed": "Дууссан", - "filter_overdue": "Хугацаа хэтэрсэн", - "show_completed": "Үзэсгэлэн дууссан", - "create": "Шинэ даалгавар", - "edit": "Даалгавар засах", - "title_placeholder": "Даалгаврын гарчиг", - "description_placeholder": "Тодорхойлолт нэмэх...", - "due_date": "Хугацаа дуусах хугацаа", - "include_time": "Цаг оруулах", - "priority": "Тэргүүлэл", - "priority_none": "Байхгүй", - "priority_high": "Өндөр", - "priority_medium": "Дунд", - "priority_low": "Бага", - "progress": "Төлөв", - "progress_needs_action": "Арга хэмжээ авах шаардлагатай", - "progress_in_process": "Үйл ажиллагаа явагдаж байна", - "progress_completed": "Дууссан", - "progress_cancelled": "Цуцлагдсан", - "calendar": "Хуанли", - "alert": "Сануулга", - "alert_none": "Байхгүй", - "alert_at_time": "Хугацаа дуусахаар", - "alert_5min": "5 минутын өмнө", - "alert_15min": "15 минутын өмнө", - "alert_30min": "30 минутын өмнө", - "alert_1hr": "1 цагийн өмнө", - "alert_1day": "1 өдрийн өмнө", - "delete": "Устгах", - "cancel": "Цуцлах", - "save": "Хадгалах", - "quick_add_placeholder": "Даалгавар нэмэх...", - "due_today": "Өнөөдөр", - "due_tomorrow": "Маргааш", - "overdue": "Хугацаа хэтэрсэн" - } - }, - "calendar_event_notifications": { - "someone": "Хэн нэгэн", - "invited": "{name} таныг \"{title}\"-д урилаа", - "updated": "{name} \"{title}\"-г шинэчиллээ", - "cancelled": "{name} \"{title}\"-г цуцаллаа", - "untitled": "Нэргүй арга хэмжээ" - }, - "share_notifications": { - "someone": "Хэн нэгэн", - "shared": "{name} танд {kind} \"{object}\"-г хуваалцлаа", - "changed": "{name} таны {kind} \"{object}\"-д хандах эрхийг өөрчиллөө", - "revoked": "{name} таны {kind} \"{object}\"-д хандах эрхийг хаслаа", - "object": { - "folder": "хавтас", - "calendar": "хуанли", - "address_book": "хаягийн дэвтэр", - "files": "файлын хавтас" - } - }, - "sharing": { - "title": "\"{name}\"-г хуваалцаарай", - "description": "Энэ сервер дээрх бусад хэрэглэгчид эсвэл бүлгүүдэд хандах эрх олгох. Өөрчлөлтүүд шууд хүчин төгөлдөр болно.", - "no_shares": "Одоохондоо хэнтэй ч хуваалцаагүй.", - "add_person": "Хүн эсвэл бүлэг нэмэх", - "search_placeholder": "Нэр эсвэл имэйлээр хайх...", - "loading_principals": "Хэрэглэгчдийг ачаалж байна…", - "no_principals": "Өөр хэрэглэгч болон групп олдсонгүй.", - "no_match": "Тохирох зүйл алга.", - "remove": "Хандалтыг арилгах", - "group": "Бүлэг", - "share_added": "Хандалт олгосон", - "share_updated": "Хандалт шинэчлэгдсэн", - "share_removed": "Хандалт хасагдсан", - "share_failed": "Хуваалцалтыг шинэчилж чадсангүй", - "preset": { - "freeBusy": "Үнэгүй/зөвхөн завгүй", - "read": "Зөвхөн унших", - "readWrite": "Унших, бичих", - "manager": "Менежер", - "custom": "Захиалгат" - } - }, - "advanced_search": { - "title": "Нарийвчилсан хайлт", - "from": "-аас", - "from_placeholder": "Илгээгчийн имэйл эсвэл нэр", - "to": "Хэнд", - "to_placeholder": "Хүлээн авагчийн имэйл эсвэл нэр", - "subject": "Сэдэв", - "subject_placeholder": "Сэдвэд...", - "body": "Бие", - "body_placeholder": "Биед агуулагдах...", - "folder": "Хавтас", - "all_folders": "Бүх хавтаснууд", - "has_attachment": "Хавсралт", - "date_after": "Дараа", - "date_before": "Өмнө нь", - "size_min": "Ихээс (KB)", - "size_max": "Багаас (KB)", - "starred": "Одтой", - "unread": "Уншаагүй", - "read": "унших", - "yes": "Тийм", - "no": "Үгүй", - "clear": "Цэвэр", - "clear_all": "Бүгдийг арилгах", - "filters_active": "{count} шүүлтүүр", - "filters_active_plural": "{count} шүүлтүүрүүд", - "toggle_filters": "Илүү", - "search_hint": "Нарийвчилсан хайлт хийхийн тулд дэвшилтэт шүүлтүүр ашиглах", - "advanced_filters_tooltip": "Дэвшилтэт хайлтын шүүлтүүрүүд", - "results_found": "{count, plural, =0 {Үр дүн олдсонгүй} one {# үр дүн олдлоо} other {# үр дүн олдлоо}}", - "results_found_more": "{count}+ илэрц олдлоо", - "suggestions_recent": "Сүүлийн хайлтууд", - "suggestions_people": "Хүмүүс", - "suggestions_from": "{address}-аас ирсэн захидал хайх", - "suggestions_to": "{address} руу илгээсэн захидал хайх", - "suggestions_remove_recent": "Сүүлийн хайлтуудаас хасах" - }, - "welcome": { - "title": "Таны шуудангийн хайрцагт тавтай морил", - "tip_compose": "Шинэ имэйл бичихийн тулд c товчийг дарна уу", - "tip_shortcuts": "Дарах уу? бүх гарын товчлолыг харахын тулд", - "tip_sidebar": "Хажуугийн цэснээс Хуанли, Харилцагчид болон Тохиргоог олоорой", - "tip_settings": "Тохиргооноос өөрийн туршлагаа тохируулна уу", - "got_it": "Ойлголоо", - "settings": "Тохиргоо", - "dismiss": "Хаах", - "start_tour": "Аялал эхлэх" - }, - "demo_welcome": { - "title": "Bulwark Mail", - "description": "Бүрэн боломжит вэбмэйл клиентийг шууд хөтөч дээрээ судлаарай. Бүх өгөгдөл таны төхөөрөмж дээр үлдэх тул бүх зүйлийг туршиж үзээрэй.", - "feature_email": "Унших, имэйл бичих", - "feature_organize": "Таг, од, хавтас", - "feature_shortcuts": "Гарын товчлол", - "feature_privacy": "100% хувийн демо", - "hint": "Эхлэхийн тулд зүүн талд байгаа дурын имэйл дээр дарж эсвэл доорх аялалд хамрагдаарай." - }, - "files": { - "office_edit": "Баримт засах", - "office_loading": "Баримтын засварлагчийг нээж байна…", - "office_error": "Баримтын засварлагчийг нээж чадсангүй.", - "office_close": "Засварлагчийг хаах", - "open_in_new_tab": "Шинэ табаар нээх", - "title": "Файл", - "search_placeholder": "Файл хайх...", - "empty_state_title": "Одоохондоо файл алга", - "empty_state_description": "Эхлэхийн тулд файл байршуулах эсвэл хавтас үүсгэнэ үү", - "upload": "Байршуулах", - "upload_files": "Файл байршуулах", - "new_folder": "Шинэ хавтас", - "new_folder_name": "Хавтасны нэр", - "rename": "Нэрээ өөрчлөх", - "rename_title": "Нэрээ өөрчлөх", - "new_name": "Шинэ нэр", - "name_forbidden_chars": "Сервер нэрэнд эдгээр тэмдэгтийг зөвшөөрдөггүй: {chars}", - "name_reserved": "Энэ нэр сервер дээр нөөцлөгдсөн.", - "delete": "Устгах", - "delete_confirm_title": "Нөөцийг устгах", - "delete_confirm_message": "Та \"{name}\"-г устгахдаа итгэлтэй байна уу? Үүнийг буцаах боломжгүй.", - "download": "Татаж авах", - "name": "Нэр", - "size": "Хэмжээ", - "modified": "Өөрчлөгдсөн", - "type": "Төрөл", - "folder": "Хавтас", - "file": "Файл", - "parent_directory": "Эцэг эх лавлах", - "breadcrumb_root": "Гэр", - "other_accounts": "Бусад дансууд", - "no_accounts": "Холбогдсон бүртгэл байхгүй.", - "drop_files_here": "Байршуулахын тулд файл эсвэл фолдерыг энд буулгана уу", - "uploading": "Байршуулж байна...", - "upload_success": "{count, plural, one {1 файл дээш ачигдлаа} other {# файл дээш ачигдлаа}}", - "upload_error": "Файл байршуулж чадсангүй", - "create_folder_success": "Хавтас үүсгэсэн", - "create_folder_error": "Хавтас үүсгэж чадсангүй", - "delete_success": "Амжилттай устгасан", - "delete_error": "Устгаж чадсангүй", - "rename_success": "Нэрээ амжилттай өөрчилсөн", - "rename_error": "Нэрээ өөрчилж чадсангүй", - "download_error": "Татаж авч чадсангүй", - "not_available": "Энэ сервер дээр файл хадгалах боломжгүй", - "cancel": "Цуцлах", - "create": "Үйлдвэрлэх", - "save": "Хадгалах", - "no_results": "Таны хайлтад тохирох файл алга", - "batch_delete_confirm_message": "{count, plural, one {1 элемент} other {# элемент}} устгахыг баталгаажуулж байна уу? Энэ үйлдлийг буцаах боломжгүй.", - "batch_delete_success": "{count, plural, one {1 элемент устгагдлаа} other {# элемент устгагдлаа}}", - "grid_view": "Сүлжээ харах", - "list_view": "Жагсаалт харах", - "details": "Дэлгэрэнгүй мэдээлэл", - "path": "Зам", - "preview": "Урьдчилан харах", - "preview_error": "Урьдчилан үзэхийг ачаалж чадсангүй", - "cut": "Таслах", - "copy": "Хуулбар", - "paste": "Наах", - "move_success": "{count, plural, one {1 зүйл зөөгдлөө} other {# зүйл зөөгдлөө}}", - "move_error": "Хөдөлж чадсангүй", - "paste_success": "Амжилттай буулгалаа", - "paste_error": "Буулгаж чадсангүй", - "new_text_file": "Шинэ текст файл", - "file_name": "Файлын нэр", - "retry": "Дахин оролдох", - "refresh": "Сэргээх", - "toggle_favorite": "Дуртай болгох/болих", - "duplicate": "Хуулбарлах", - "duplicate_success": "Амжилттай хуулбарлалаа", - "duplicate_error": "Хуулбарлаж чадсангүй", - "share": "Хуваалцах", - "shared": "Хуваалцсан", - "shared_with_me": "Надтай хуваалцсан", - "shared_by": "{name}", - "create_file_success": "Файл үүсгэсэн", - "create_file_error": "Файлыг үүсгэж чадсангүй", - "favorites": "Дуртай зүйлс", - "recent": "Саяхан", - "properties": "Шинж чанар", - "open_folder": "Хавтас нээх", - "upload_folder": "Хавтас байршуулах", - "file_too_large": "\"{name}\" нь файлын дээд хэмжээ ({max})-ээс их байна", - "undo": "Цуцлах", - "undo_success": "Үйлдэл цуцлагдлаа", - "undo_error": "Буцааж чадсангүй", - "toolbar": "Файл үйлдлүүд", - "open_folder_tree": "Хавтсын модыг нээх", - "file_list": "Файл ба хавтас", - "context_menu": "Үйлдлүүд", - "settings_title": "Файлын тохиргоо", - "settings_display": "Харагдац", - "settings_default_view": "Анхдагч харагдац", - "settings_default_view_desc": "Тор эсвэл жагсаалт харагдацын аль нэгийг сонгоно уу", - "settings_default_sort": "Анхдагч эрэмбэлэлт", - "settings_default_sort_desc": "Файлуудын анхдагч эрэмбэлэлтийг сонгоно уу", - "settings_sort_direction": "Эрэмбэлэлтийн чиглэл", - "settings_sort_direction_desc": "Өсөх эсвэл буурах дарааллыг сонгоно уу", - "settings_ascending": "Өсөх", - "settings_descending": "Буурах", - "settings_icons": "Дүрс", - "settings_show_icons": "Файлын дүрсийг харуулах", - "settings_show_icons_desc": "Файл болон хавтасны хажууд дүрс харуулах", - "settings_colored_icons": "Өнгөт дүрс", - "settings_colored_icons_desc": "Монохром оронд өнгөт дүрс ашиглах", - "settings_show_thumbnails": "Нэрийн зургийг харуулах", - "settings_show_thumbnails_desc": "Зургийн файлуудад дүрсийн оронд жижиг зургийг харуулах", - "settings_behavior": "Төлөв байдал", - "settings_show_hidden": "Нуугдсан файлуудыг харуулах", - "settings_show_hidden_desc": "Цэгээр эхэлдэг файл болон хавтсыг харуулах", - "settings_folder_layout": "Хавтсын навигаци", - "settings_folder_layout_desc": "Хавтсуудыг хэрхэн харуулахаа сонгоно уу: файлуудтай хамт эсвэл хажуугийн мод хэлбэрээр", - "settings_folder_layout_inline": "Шугаман", - "settings_folder_layout_sidebar": "Хажуугийн самбар", - "disabled_title": "Файлын боломжийг админ идэвхгүй болгосон", - "disabled_description": "WebDAV-ээр том файл байршуулах нь Stalwart/RocksDB-ийг тогтворгүй болгож, санах ойн доголдол эсвэл сэргээхэд хүндрэлтэй диск дүүргэлт үүсгэж болзошгүй. Устгасан файлууд blob хадгалалтаас шууд арилахгүй байж болно. Иймээс энэ боломжийг үйлдвэрлэлийн орчинд ашиглахыг зөвлөдөггүй.", - "stability_warning": "Том файл байршуулах нь серверийг тогтворгүй болгож болзошгүй. Устгасан файлууд хадгалалтаас шууд арилахгүй байж болно. Болгоомжтой ашиглана уу.", - "migration_title": "Таны файлуудыг шинэчилж байна…", - "migration_description": "Хавтас болон файлуудыг зөв бүтэц рүү нь зохион байгуулж байна. Энэ нь зөвхөн нэг удаа хийгдэнэ." - }, - "smime": { - "your_certificates": "Таны сертификатууд", - "your_certificates_desc": "Имэйлд гарын үсэг зурах болон шифрлэхэд ашиглах S/MIME сертификатаа импортолж, удирдана", - "recipient_certificates": "Хүлээн авагчийн сертификатууд", - "recipient_certificates_desc": "Хүлээн авагч руу шифрлэсэн имэйл илгээхэд ашиглах нийтийн сертификатууд", - "identity_bindings": "Таниулбарын түлхүүрийн холболтууд", - "identity_bindings_desc": "S/MIME сертификатыг өөрийн имэйл таниулбаруудтай холбох", - "defaults_title": "Анхдагчууд", - "defaults_desc": "Гарын үсэг болон шифрлэлтийн анхдагч үйлдлийг тохируулах", - "import_pkcs12": "PKCS#12 импортлох (.p12/.pfx)", - "import_public_cert": "Сертификат импортлох", - "no_certificates": "Сертификатууд импортлогдсонгүй", - "no_recipient_certs": "Хүлээн авагчийн сертификатууд байхгүй", - "expires": "Дуусах огноо", - "expired": "Дуусагдлаа", - "bound_to": "Холбогдсон", - "no_key_bound": "Холбогдоогүй", - "lock": "Түлхүүр түгжих", - "unlock": "Түлхүүр нээх", - "details": "Дэлгэрэнгүй үзэх", - "delete": "Устгах", - "encrypt_by_default": "Анхдагчаар шифрлэх", - "encrypt_by_default_desc": "Бүх хүлээн авагчийн сертификат бэлэн үед имэйлийг автоматаар шифрлэх", - "remember_unlocked": "Нээгдсэн түлхүүрийг санах", - "remember_unlocked_desc": "Энэ хөтчийн сессийн хугацаанд түлхүүрийг нээлттэй байлгах", - "sign_default_for": "Анхдагчаар гарын үсэг зурах", - "enter_p12_passphrase": "PKCS#12 Нууцын үг оруулах", - "p12_passphrase_desc": "Энэ сертификатын файлыг хамгаалдаг нууцын үг оруулна уу", - "enter_storage_passphrase": "Хадгалалтын нууцын үг оруулах", - "storage_passphrase_desc": "Энэ түлхүүрийг хөтөчид хадгалахдаа хамгаалах нууц үг сонгоно уу", - "next": "Дараа", - "import": "Импортлох", - "unlock_key": "Түлхүүр нээх", - "unlock_key_desc": "Гарын үсэг зурах эсвэл тайлахын тулд хадгалалтын нууц үгээ оруулж түлхүүрээ нээнэ үү", - "passphrase_placeholder": "Нууцын үг оруулна уу", - "confirm_passphrase_placeholder": "Нууцын үгийг баталгаажуулна уу", - "passphrase_mismatch": "Нууцын үг таарахгүй байна", - "cancel": "Цуцлах", - "processing": "Боловсруулж байна…", - "close": "Хаах", - "certificate_details": "Сертификатын дэлгэрэнгүй", - "cert_subject": "Сэдэв", - "cert_issuer": "Гаргагч", - "cert_email": "Имэйл", - "cert_serial": "Цуврал дугаар", - "cert_validity": "Хүчин төгөлдөр байдал", - "cert_fingerprint": "Хурууны хээ (SHA-256)", - "cert_algorithm": "Алгоритм", - "cert_capabilities": "Чадавхууд", - "cert_source": "Эх сурвалж", - "cert_expired": "Энэ сертификат дуусагдлаа", - "cert_not_yet_valid": "Энэ сертификат хараахан хүчинтэй болоогүй байна", - "cap_sign": "Гарын үс", - "cap_encrypt": "Шифрлэлт", - "cap_none": "Байхгүй", - "show_passphrase": "Нууцын үгийг харуулах", - "hide_passphrase": "Нууцын үгийг нуух", - "sign_toggle": "Гарын үс", - "encrypt_toggle": "Шифрлэх", - "missing_recipient_certs": "{emails}-д сертификатууд байхгүй", - "missing_sender_cert": "Энэ таниулбарт холбогдсон сертификат байхгүй", - "status_encrypted_ok": "Энэ зурвас шифрлэгдсэн байна", - "status_encrypted_no_key": "Энэ зурвас шифрлэгдсэн боловч тохирох түлхүүр олдсонгүй", - "status_encrypted_failed": "Энэ зурвасыг тайлж чадсангүй", - "status_signed_valid": "Гарын үс баталгаажуулагдлаа", - "status_signed_invalid": "Гарын үсийн баталгаажуулалт бүтэлгүйтэв", - "status_signed_expired_cert": "Дууссан сертификатаар гарын үсэг зурсан байна", - "status_signed_self_signed": "Гарын үсэг хүчинтэй боловч сертификат нь self-signed тул итгэмжлэгдээгүй", - "status_signed_mismatch": "Гарын үсэг хүчинтэй боловч гарын үсэг зурсан этгээд илгээгчтэй таарахгүй", - "status_unsupported": "Дэмжихгүй S/MIME формат", - "auto_import_signer_certs": "Гарын үсэг зурсан сертификатыг автоматаар импортлох", - "auto_import_signer_certs_desc": "Баталгаажсан гарын үсэгтэй имэйлээс сертификатыг автоматаар хадгалж, дараагийн шифрлэлтийн үед ашиглах", - "export": "Экспортлох", - "enter_export_passphrase": "Экспортын нууцын үг оруулах", - "export_passphrase_desc": "Экспортлогдсон PKCS#12 файлыг хамгаалдаг нууцын үг сонгоно уу", - "export_storage_desc": "Экспортын өмнө түлхүүрийг тайлахын тулд хадгалалтын нууц үгээ оруулна уу", - "incorrect_passphrase": "Буруу нууцын үг" - }, - "tour": { - "step_counter": "{current}-р алхам {total}-аас", - "skip": "Аялалыг алгасах", - "back": "Буцаа", - "next": "Дараа", - "finish": "Дуусгах", - "take_a_tour": "Интерфэйсийн аяллыг эхлүүлнэ үү", - "restart_title": "Оршил аялал", - "restart_desc": "Интерфэйсийн танилцах аяллыг дахин үзэх", - "restart_button": "Аялалыг дахин эхлүүлэх", - "show_on_new_devices_title": "Шинэ төхөөрөмжүүдэд харуулах", - "show_on_new_devices_desc": "Шинэ төхөөрөмж дээр анх нэвтрэхэд мэндчилгээ болон аяллыг дахин харуулна, өмнө нь дуусгасан байсан ч гэсэн", - "sidebar_title": "Таны шуудангийн хайрцгууд", - "sidebar_desc": "Энэ бол хавтасны хажуугийн самбар. Аль ч хавтсыг дарж имэйлүүдийг нь үзэж болно. Мөн шинэ хавтас үүсгэх, имэйлийг чирж зөөх, уншаагүй тоог нэг дор харах боломжтой.", - "compose_title": "Имэйл зохиох", - "compose_desc": "Шинэ имэйл бичихийн тулд энд дарна уу. Хүлээн авагч нэмэх, файл хавсаргах, баялаг текст формат ашиглах боломжтой.", - "search_title": "Таны имэйлийг хайх", - "search_desc": "Илгээгч, сэдэв, эсвэл агуулгаар хайж болно. Шүүлтүүрийн дүрсээр огноо, хавсралт, одтой имэйл зэрэг нарийвчилсан сонголтыг нээнэ.", - "email_list_title": "Таны имэйлийн жагсаалт", - "email_list_desc": "Имэйлүүд энд харагдана. Нэгийг нь дарж баруун талд уншина уу. Олон имэйл сонгоод бөөнөөр нь зөөх, устгах, шошголох боломжтой.", - "email_viewer_title": "Уншилтын самбар", - "email_viewer_desc": "Сонгосон имэйл энд нээгдэнэ. Дээрх хэрэгслийн мөрөөс хариулах, дамжуулах, архивлах, эсвэл устгах боломжтой. Мөн од тавих, өнгийн шошго нэмэх боломжтой.", - "keywords_title": "Өнгөний шошгууд", - "keywords_desc": "Өнгөөр ялгасан шошго ашиглан имэйлээ зохион байгуулна уу. Имэйлийг шошгон дээр чирэх эсвэл баруун товшоод шошго өгч болно.", - "calendar_title": "Хуанли", - "calendar_desc": "Арга хэмжээгээ удирдахын тулд хуанли руу шилжинэ үү. Шинэ арга хэмжээ үүсгэх, сануулга тохируулах, өдөр, долоо хоног, сар харагдацаар үзэх боломжтой.", - "contacts_title": "Контактууд", - "contacts_desc": "Таны хаягийн дэвтэр энд байрлана. Харилцагч импортлох, бүлэг үүсгэх, аль ч харилцагчийн дэлгэрэнгүйг нээх боломжтой.", - "settings_title": "Тохиргоо", - "settings_desc": "Сэдэв, нягтрал, гарын үсэг, шүүлтүүр, товчлол, хуанлийн анхдагч утга зэрэг олон тохиргоог эндээс өөрчилж болно.", - "shortcuts_title": "Гарын товчлол", - "shortcuts_desc": "Хурдан ажиллах дуртай хэрэглэгчдэд зориулсан товчлолууд энд байна. ? дарж бүх товчлолыг хүссэн үедээ харж болно.", - "compose_open_title": "Бичих цонх", - "compose_open_desc": "Энэ бол имэйл бичих цонх. Хүлээн авагч нэмэх, зурвас бичих, файл хавсаргах, баялаг текст формат ашиглах боломжтой. Мөн ноорог хадгалах, загвар ашиглах боломжтой.", - "calendar_view_title": "Таны хуанли", - "calendar_view_desc": "Энд таны хуанли жишээ арга хэмжээнүүдтэй харагдана. Хэрэгслийн мөрөөр өдөр, долоо хоног, сар, жагсаалт харагдацын хооронд шилжиж болно.", - "create_event_title": "Арга хэмжээ үүсгэх", - "create_event_desc": "Шинэ хуанлийн арга хэмжээ үүсгэхийн тулд энэ товчийг дарна уу. Гарчиг, огноо, цаг тохируулж, оролцогч нэмж болно.", - "event_modal_title": "Арга хэмжээний дэлгэрэнгүй", - "event_modal_desc": "Энэ цонхонд арга хэмжээний мэдээллээ бөглөнө. Гарчиг, огноо, цаг, байршил, оролцогчоо оруулаад хадгална уу.", - "contacts_list_title": "Таны контактууд", - "contacts_list_desc": "Энд таны харилцагчид харагдана. Аль нэгийг нь дарж баруун талд дэлгэрэнгүйг нь үзэж болно. Мөн шинэ харилцагч үүсгэх, vCard импортлох, бүлэг үүсгэх боломжтой.", - "settings_tabs_title": "Тохиргооны цэс", - "settings_tabs_desc": "Энд бүх тохиргооны ангилал байна. Гадаад төрх, таниулбар, шүүлтүүр, хуанли болон бусад тохиргоогоо эндээс удирдана.", - "files_title": "Файлын хадгалалт", - "files_desc": "Файлын хэсэгт та файл байршуулах, зохион байгуулах, хуваалцах боломжтой. Энэ нь таны мэйлд суурилсан хувийн хадгалалт шиг ажиллана.", - "demo_banner_title": "Демо удирдлага", - "demo_banner_desc": "Та демо горимд байна. Бүх өгөгдөл зөвхөн таны хөтөч дээр хадгалагдана. Хүссэн үедээ Reset Demo дарж дахин эхлүүлж болно.", - "quota_title": "Хадгалалтын ашиглалт", - "quota_desc": "Энд таны шуудангийн хайрцгийн ашиглаж буй хэмжээг харуулна. Илүү их зай ашиглах тусам тойрог дүүрнэ." - }, - "unified_mailbox": { - "search_unavailable": "Нэгдсэн харагдац дээр хайлт ашиглах боломжгүй" - }, - "quote_header": { - "reply_line": "{date}-нд {from} бичсэн:", - "forwarded_separator": "---------- Урагшлуулсан мессеж ----------", - "from_label": "Илгээгч", - "date_label": "Огноо", - "subject_label": "Сэдэв" - }, - "pwa_install": { - "title": "{appName} суулгах", - "description": "Хурдан нэвтрэх болон оффлайн дэмжлэг авахын тулд аппыг суулгана уу.", - "not_now": "Одоохондоо биш", - "install": "Суулгах", - "dont_remind": "Дахиж сануулахгүй", - "dismiss_aria": "Суулгах сануулгыг хаах" - }, - "deep_link": { - "message_not_found": "Энэ зурвас дахин ашиглах боломжгүй болсон.", - "thread_not_found": "Энэ харилцан яриа дахин ашиглах боломжгүй болсон.", - "folder_not_found": "Энэ хавтас дахин ашиглах боломжгүй болсон.", - "event_not_found": "Энэ арга хэмжээ дахин ашиглах боломжгүй болсон.", - "file_not_found": "Энэ файл дахин ашиглах боломжгүй болсон.", - "account_unavailable": "Энэ холбоосыг нээхийн тулд энэ дансанд нэвтрэнэ үү.", - "copy_message": "Мессежийн холбоосыг хуулах", - "copy_conversation": "Харилцан ярианы холбоосыг хуулах", - "copy_event": "Арга хэмжээний холбоосыг хуулах", - "copied": "Холбоос хуулагдлаа", - "copy_failed": "Холбоосыг хуулж чадсангүй" - }, - "plugins": { - "oauth_callback": { - "completing": "Зөвшөөрлийг дуусгаж байна…", - "completing_message": "Холболт үүсэх хүртэл түр хүлээнэ үү.", - "received_title": "Зөвшөөрөл хүлээн авсан", - "received_message": "Та холболт эхлүүлсэн таб руугаа буцаж болно.", - "failed_title": "Зөвшөөрөл амжилтгүй болсон", - "denied": "Зөвшөөрөл олгохыг татгалзсан.", - "failed_detail": "Зөвшөөрөл амжилтгүй болсон: {error}", - "missing_params": "Зөвшөөрлийн код эсвэл төлөв байхгүй байна.", - "delivery_failed": "Зөвшөөрлийн үр дүнг аппын таб руу дамжуулж чадсангүй. Энэ цонхыг хаагаад дахин оролдоно уу." - } - }, - "global_search": { - "title": "Бүгдийг хайх", - "placeholder": "Шуудан, харилцагч, хуанли, файлаас хайх...", - "scope_label": "Хайх хүрээ", - "account_label": "Бүртгэл", - "scope_all": "Бүгд", - "scope_mail": "Мэйл", - "scope_contacts": "Харилцах утас", - "scope_calendar": "Хуанли", - "scope_files": "Файл", - "all_accounts": "Бүх бүртгэл", - "show_all": "Бүх үр дүнг харуулах", - "no_results": "“{query}”-д үр дүн олдсонгүй", - "server_hint": "Бүтэн үгс серверээс хайна; үгийн хэсэг ачаалагдсан зүйлстэй таарна", - "searching": "Хайж байна...", - "account_error": "{account}-аас хайж чадсангүй", - "recent_searches": "Сүүлийн хайлтууд", - "recurring": "Давтагдах", - "no_subject": "(no subject)", - "empty_hint": "Бүх бүртгэлийн шуудан, харилцагч, үйл явдал, файлаас хайгаарай. from:, subject:, has:attachment, after:2026-01-01 гэж туршиж үзээрэй.", - "flat_by_date": "Огноогоор жагсаах", - "grouped": "Бүлэглэсэн", - "load_more": "Цааш ачаалах", - "preview_empty": "Урьдчилан харах үр дүнгээ сонгоно уу", - "open_full": "Нээх", - "view_label": "Харагдац" - } -} +{ + "meta_description": "JMAP протоколыг ашигладаг минималист вэб шуудангийн клиент", + "login": { + "title": "Вэбмэйл", + "username_label": "Имэйл", + "username_placeholder": "user@example.com", + "password_label": "Нууц үг", + "password_placeholder": "Нууц үгээ оруулна уу", + "jmap_endpoint_label": "JMAP сервер", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Сервер энэ домэйноос ирэх CORS хүсэлтийг зөвшөөрсөн байх ёстой.", + "jmap_server_label": "Сервер", + "jmap_server_auto_picked": "Таны имэйл хаягийн домэйноос серверийг автоматаар сонголоо.", + "sign_in": "Нэвтрэх", + "signing_in": "Нэвтэрч байна...", + "loading": "Ачаалж байна...", + "reconnecting": "Холболт тасарлаа. Дахин холбогдохыг оролдож байна…", + "error": { + "invalid_credentials": "Имэйл эсвэл нууц үг буруу байна. Мэдээллээ шалгаад дахин оролдоно уу.", + "invalid_token": "Сервер энэ хандалтын токеныг хүлээн аваагүй. Токен бүрэн эсэх, хугацаа нь дуусаагүй, цуцлагдаагүй эсэхийг шалгана уу.", + "connection_failed": "Сервертэй холбогдож чадсангүй. Интернэт холболтоо шалгаад дахин оролдоно уу.", + "cors_blocked": "Серверт хүрч байна, гэхдээ cross-origin хүсэлтийг хориглож байна. JMAP серверийн CORS тохиргоог шалгаад энэ домэйныг зөвшөөрнө үү.", + "server_error": "Сервер түр ажиллахгүй байна. Дараа дахин оролдоно уу.", + "generic": "Санамсаргүй алдаа гарлаа. Хэрэв давтагдсаар байвал админтайгаа холбогдоно уу.", + "totp_required": "Хоёр шаттай баталгаажуулалтын код шаардлагатай. Доор кодоо оруулна уу.", + "totp_invalid": "Баталгаажуулалтын код буруу байна. Authenticator апп-аа шалгаад дахин оролдоно уу.", + "oauth_discovery_failed": "SSO идэвхтэй боловч identity provider-д хүрч чадсангүй. OAuth тохиргоогоо шалгана уу.", + "token_exchange_failed": "Нууц үг болон код зөв байсан ч шуудангийн сервер энэ аппад сешн эхлүүлэхээс татгалзлаа. Админаасаа OAuth клиентийн тохиргоог шалгахыг хүснэ үү." + }, + "show_password": "Нууц үг харуулах", + "hide_password": "Нууц үг нуух", + "totp_toggle": "Надад 2FA код байгаа", + "token_toggle": "Хандалтын токеноор нэвтрэх", + "password_toggle": "Нууц үгээр нэвтрэх", + "token_label": "Хандалтын токен", + "token_placeholder": "API токеноо буулгана уу", + "token_hint": "Имэйл үйлчилгээ үзүүлэгчийнхээ тохиргоонд API токен үүсгээд энд буулгана уу.", + "remember_me": "Намайг сана", + "config_error": { + "title": "Тохиргооны алдаа", + "fetch_failed": "Аппликэйшний тохиргоог ачаалж чадсангүй. Дараа дахин оролдоно уу.", + "server_not_configured": "Мэйл сервер тохируулагдаагүй байна. Админтайгаа холбогдоно уу." + }, + "remove_from_history": "Түүхээс устгах", + "totp_label": "Баталгаажуулах код", + "totp_placeholder": "000000", + "session_expired": "Таны сесс дууссан байна. Дахин нэвтэрнэ үү.", + "dismiss": "Хаах", + "or": "эсвэл", + "sign_in_sso": "SSO-ээр нэвтрэх", + "add_account_title": "Бүртгэл нэмэх", + "add_account_subtitle": "Өөр бүртгэлээр нэвтрэх", + "cancel": "Болих", + "website": "Вэбсайт", + "imprint": "Хуулийн мэдээлэл", + "privacy_policy": "Нууцлалын бодлого", + "try_demo": "Демо үзэх", + "demo_description": "Жишээ өгөгдлөөр туршиж үзээрэй - бүртгэл шаардлагагүй", + "demo_launching": "Демо эхлүүлж байна...", + "demo_login_button": "Демо нээх", + "demo_tagline": "Бүрэн боломжтой имэйлийн клиентийг туршиж үзээрэй. Бүртгэл шаардлагагүй.", + "demo_no_signup": "Бүртгүүлэх шаардлагагүй - жишээ өгөгдлөөр чөлөөтэй үзэж сонирхоно уу", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Нэвтрэлтийг дуусгаж байна...", + "oauth_error": { + "title": "Баталгаажуулалт амжилтгүй", + "invalid_state": "Аюулгүй байдлын шалгалт амжилтгүй боллоо. Дахин нэвтэрнэ үү.", + "missing_params": "Зөвшөөрлийн өгөгдөл дутуу байна. Дахин нэвтэрнэ үү.", + "token_exchange_failed": "Баталгаажуулалтыг дуусгаж чадсангүй. Дахин оролдоно уу.", + "access_denied": "Хандалтыг зөвшөөрсөнгүй. Админтайгаа холбогдоно уу.", + "back_to_login": "Нэвтрэх рүү буцах" + } + }, + "sidebar": { + "close": "Хаах", + "compose": "Бичилэх", + "compose_hint": "Бичих (c)", + "search_placeholder": "Мэйл хайх...", + "search_placeholder_hint": "Мэйл хайх... (/ дарна уу)", + "storage": "Хадгалах", + "storage_used": "Ашигласан", + "storage_free": "Үнэгүй", + "storage_total": "Нийт", + "sign_out": "Гарах", + "sign_out_of": "{account}", + "sign_out_all": "Бүх бүртгэлээс гарах", + "add_account": "Бүртгэл нэмэх", + "set_as_default": "Өгөгдмөлөөр тохируулах", + "switch_account": "Бүртгэл солих", + "contacts": "Харилцах утас", + "calendar": "Хуанли", + "settings": "Тохиргоо", + "admin": "Админ", + "files": "Файл", + "loading_mailboxes": "Шуудангийн хайрцгийг ачаалж байна...", + "push_connected": "Бодит цагийн шинэчлэлт идэвхтэй", + "push_disconnected": "Бодит цагийн шинэчлэл идэвхгүй байна", + "keyboard_shortcuts": "Гарын товчлол", + "theme": { + "light": "Гэрлийн горим", + "dark": "Харанхуй горим", + "system": "Системийн сэдэв" + }, + "language": { + "title": "Хэл" + }, + "mailboxes": { + "inbox": "Ирсэн имэйл", + "sent": "Илгээсэн", + "drafts": "Ноорог", + "trash": "Хогийн сав", + "archive": "Архив", + "starred": "Одтой", + "all_mail": "Бүх шуудан", + "spam": "Спам", + "important": "Чухал" + }, + "unified_inbox": "Нэгдсэн ирсэн имэйл", + "unified_sent": "Бүгд илгээсэн", + "unified_drafts": "Бүх ноорог", + "unified_trash": "Бүх хогийн сав", + "unified_archive": "Бүх архив", + "unified_junk": "Бүх хог", + "all_accounts": "Бүх дансууд", + "all_accounts_loading": "Бүртгэлүүдийг ачаалж байна: {total}-аас {loaded}", + "unified_mailbox": "Нэгдсэн шуудангийн хайрцаг", + "expand": "Өргөтгөх", + "collapse": "Нуналт", + "expand_tooltip": "Дэлгэх", + "collapse_tooltip": "Нуналт", + "mobile": { + "search": "Хайлт", + "compose": "Бичилэх", + "go_back": "Буцах", + "toggle_menu": "Цэсийг сэлгэх" + }, + "clear_search": "Хайлтыг арилгах", + "vacation_active": "Амралтын хариулагч идэвхтэй байна", + "demo_banner": "Демо горим", + "demo_reset": "Дахин тохируулах", + "demo_tour": "Аялал", + "tags": "Тагууд", + "show_all_tags": "Бүгдийг нь харуулах ({count})", + "show_fewer_tags": "Бага харуулах", + "folders": "Хавтас", + "your_folders": "Таны хавтаснууд", + "create_folder": "Хавтас үүсгэх", + "show_more": "Илүү", + "show_fewer": "Бага", + "shared": "Хуваалцсан", + "shared_account_hidden": "Хажуугийн самбараас нуусан. Тохиргоо › Байршил хэсгээс дахин харуулна уу.", + "notifications_muted": "Мэдэгдэл хаалттай", + "mail": "Мэйл", + "nav_label": "Навигаци", + "add_app": "Програмууд", + "scheduled": "Хуваарьтай", + "unified_all_unread": "Бүгд уншаагүй", + "unified_all_starred": "Бүх одтой", + "unified_all_mail": "Бүх шуудан", + "remove_account": "Бүртгэл устгах", + "remove_account_confirm": "Энэ төхөөрөмжөөс {account}-г устгах уу? Та дараа дахин нэмж болно.", + "labels": "Шошго", + "create_label": "Шинэ шошго үүсгэх", + "mv_tasks": "Даалгаврууд", + "mv_side_panel": "Хажуугийн самбар", + "mv_open_full": "Бүтэн харагдацаар нээх", + "mv_previous_day": "Өмнөх өдөр", + "mv_next_day": "Дараах өдөр", + "mv_add_another_account": "Өөр бүртгэл нэмэх", + "mv_manage_account": "Бүртгэлээ удирдах", + "mv_storage_used": "{total}-с {percent}% ашигласан" + }, + "protocol_handlers": { + "title": "Өгөгдмөл програмууд", + "description": "И-мэйл болон хуанлийн холбоосууд Bulwark дээр нээгдэх эсэхийг сонгоно уу. Техникийн хувьд Bulwark нь mailto: болон webcal: холбоосуудад протокол зохицуулагчаар бүртгүүлдэг.", + "unsupported": "Энэ хөтөч эсвэл холболт нь протокол зохицуулагчийн гарын авлагын бүртгэлийг дэмждэггүй. Та суулгасан PWA-г хөтөч эсвэл үйлдлийн системээр дамжуулан ашиглах боломжтой хэвээр байж магадгүй.", + "mailto_label": "И-мэйл холбоосууд", + "mailto_description": "Bulwark дээр урьдчилан бөглөсөн mailto: холбоосуудыг нээнэ.", + "protocol_open_mode_label": "Протоколын холбоосыг нээх үед", + "protocol_open_mode_description": "Bulwark нь mailto: болон webcal: холбоосыг шинэ таб дээр нээх эсвэл нээлттэй сессийг дахин ашиглах эсэхийг сонгоно уу. Идэвхтэй сессийн сонголтод мэдэгдлийн зөвшөөрөл шаардлагатай тул хэрэв хөтөч фокусыг блоклодог бол та Bulwark-г урд нь авчрахын тулд нөөц мэдэгдэл дээр дарж болно.", + "protocol_open_mode_active_session": "Боломжтой бол идэвхтэй сессээр нээх", + "protocol_open_mode_new_tab": "Үргэлж шинэ таб нээх", + "focus_notification_title": "Холбоос нээгдлээ", + "focus_notification_body": "Холбоос Bulwark-д нээгдлээ. Цонхыг урд талд гаргахын тулд товшино уу.", + "webcal_label": "Календарийн холбоосууд", + "webcal_description": "Bulwark дээр урьдчилан бөглөсөн webcal: холбоосуудыг нээнэ.", + "register_mailto": "Имэйл програмыг бүртгүүлэх", + "register_webcal": "Календарийн програмыг бүртгэх", + "mailto_registered": "И-мэйл зохицуулагчийн бүртгэл хүссэн", + "webcal_registered": "Хуанли зохицуулагчийн бүртгэлийг хүссэн", + "registration_failed": "Протокол зохицуулагчийн бүртгэл амжилтгүй боллоо", + "opening_mailto": "Бичих цонх нээж байна...", + "opening_webcal": "Нээлтийн хуанли...", + "browser_note": "Таны хөтөч эсвэл үйлдлийн систем танаас үүнийг баталгаажуулахыг хүсэх ба Bulwark-ыг үндсэн програмаар сонгохын өмнө суулгахыг шаардаж магадгүй.", + "select_account_title": "Бүртгэл сонгох", + "select_mailto_account": "Энэ имэйлийн холбоосыг аль бүртгэлээр нээхийг сонгоно уу.", + "select_webcal_account": "Энэ хуанлийн холбоосыг аль данс нээхийг сонгоно уу.", + "select_account_note": "Энэ нь зөвхөн энэ протоколын холбоост хамаарна.", + "detail_to": "Хэнд", + "detail_subject": "Сэдэв", + "detail_no_subject": "Сэдэв байхгүй", + "detail_calendar": "Хуанли", + "detail_source": "Эх сурвалж", + "active_account": "Идэвхтэй", + "switching_account": "Бүртгэл сэлгэж байна..." + }, + "sidebar_apps": { + "modal_title": "Sidebar програмууд", + "add_new": "Апп нэмэх", + "edit_app": "Аппликешн засах", + "name_label": "Нэр", + "name_placeholder": "Миний програм", + "name_required": "Нэр оруулах шаардлагатай", + "url_label": "URL", + "url_required": "URL шаардлагатай", + "url_invalid": "Хүчтэй http эсвэл https URL оруулна уу", + "icon_label": "Дүрс", + "icon_required": "Икон тэмдэг шаардлагатай", + "open_mode_label": "Нээлттэй горим", + "open_new_tab": "Шинэ таб", + "open_inline": "Мөр дотор", + "cancel": "Цуцлах", + "add": "Нэмэх", + "update": "Шинэчлэлт", + "delete": "Устгах", + "delete_confirm_title": "Апп устгах", + "delete_confirm": "Та \"{name}\"-г устгахдаа итгэлтэй байна уу?", + "no_apps": "Одоохондоо апп нэмээгүй байна", + "no_apps_hint": "Хажуугийн самбартаа захиалгат програм, холбоос нэмнэ үү", + "managed_title": "Администратороос нийлүүлсэн", + "managed_description": "Эдгээр аппыг бүх хэрэглэгчид тохируулсан бөгөөд өөрчлөх боломжгүй.", + "managed_badge": "Удирдсан", + "search_icons": "Хайлтын дүрсүүд...", + "show_popular": "Алдартай", + "show_all": "Бүгд", + "no_icons_found": "Ямар ч дүрс олдсонгүй", + "inline_badge": "Мөр дотор", + "tab_badge": "Таб", + "show_on_mobile": "Мобайл дээр харуулах" + }, + "email_list": { + "columns": { + "from": "Илгээгч", + "to": "Хүлээн авагч", + "subject": "Гарчиг", + "date": "Огноо", + "size": "Хэмжээ" + }, + "no_emails": "Ямар ч зурвас олдсонгүй", + "no_emails_description": "Энэ шуудангийн хайрцаг хоосон", + "no_search_results": "Илэрц олдсонгүй", + "no_search_results_description": "Хайлт эсвэл шүүлтүүрээ тохируулаад үзээрэй", + "loading": "Имэйл ачаалж байна...", + "unread": "уншаагүй", + "to_me": "Надад", + "to_recipients": "{count} хүлээн авагчдад", + "and_others": "болон {count} бусад", + "draft": "Ноорог", + "starred": "Одтой", + "pinned": "Бэхэлсэн", + "replied": "Хариулсан", + "forwarded": "Дамжуулсан", + "has_attachment": "Хавсралттай", + "conversations_range": "{from}–{to, number} / {total, number}", + "conversations_count": "{total} харилцан ярианы {count}", + "conversations_count_plus": "{count}+ харилцан яриа", + "conversations_count_simple": "{count} харилцан яриа", + "no_conversations": "Ярилцлага байхгүй", + "loading_more": "Дэлгэрэнгүй имэйл ачаалж байна...", + "no_more_emails": "Ачаалах имэйл байхгүй", + "batch_actions": { + "select": "Имэйл сонгох", + "select_all": "Бүгдийг нь сонго", + "selected_messages": "{count, plural, one {1 имэйл} other {# имэйл}} сонгосон", + "mark_read": "Уншсан гэж тэмдэглэх", + "mark_unread": "Уншаагүй гэж тэмдэглэх", + "delete": "Устгах", + "delete_confirm_title": "Имэйл устгах", + "delete_confirm_message": "Та устгахдаа итгэлтэй байна уу{count, plural, one {1 имэйл} other {# имэйл}}?", + "clear_selection": "Сонголтыг арилгах" + }, + "permanent_delete": "Бүрмөсөн устгах", + "permanent_delete_confirm_title": "Бүрмөсөн устгах", + "permanent_delete_confirm_message": "Энэ имэйлийг бүрмөсөн устгах болно. Энэ үйлдлийг буцаах боломжгүй.", + "permanent_delete_confirm_batch_message": "Эдгээр{count, plural, one {1 имэйл} other {# имэйл}}бүрмөсөн устгагдах болно. Энэ үйлдлийг буцаах боломжгүй.", + "empty_folder": { + "button": "Хоосон хавтас", + "confirm_title": "Хоосон хавтас", + "confirm_message": "Энэ хавтсанд байгаа бүх имэйлүүд бүрмөсөн устах болно. Энэ үйлдлийг буцаах боломжгүй.", + "confirm_message_trash": "Энэ хавтасны бүх имэйл хогийн сав руу зөөгдөнө.", + "confirm_button": "Хоосон хавтас", + "junk_hint": "Та бүх мессежийг бүрмөсөн устгахын тулд хэрэггүй хавтасыг хоослох боломжтой.", + "trash_hint": "Та бүх мессежийг бүрмөсөн устгахын тулд Хогийн савны хавтсыг хоослох боломжтой." + }, + "no_scheduled_emails": "Хуваарьт имэйл байхгүй", + "no_scheduled_emails_description": "Дараа нь хийхээр төлөвлөсөн мессежүүд энд харагдах болно.", + "scheduled_count": "{count, plural, one {Төлөвлөсөн 1 имэйл} other {# хуваарьтай имэйл}}", + "scheduled_actions_hint": "Хуваарьт мессежийг цуцлах, дахин төлөвлөх эсвэл төлөвлөсөн үйлдлээсээ засварлах боломжтой.", + "cancel_scheduled_send": "Илгээхийг цуцлах", + "reschedule_send": "Хуваарь солих", + "cancel_and_edit": "Цуцлах, засах", + "cancel_and_compose_again": "Цуцлаад дахин бичих", + "reschedule_prompt": "Шинэ огноо/цаг оруулна, ж.нь. 2026-05-04Т15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Илгээгч таныг энэ мессежийг нээх үед мэдэгдэхийг хүссэн.", + "send": "Баримт илгээх", + "ignore": "Үл тоох", + "sent": "Уншсан баримтыг илгээсэн.", + "send_failed": "Уншсан баримтыг илгээж чадсангүй", + "mdn_subject": "Унш: {subject}", + "mdn_body": "Энэ нь таны {recipient} руу илгээсэн мессежийг буцаах баримт юм.\n\nТэмдэглэл: Энэхүү баримт нь зөвхөн мессежийг хүлээн авагчийн компьютер дээр харуулсан гэдгийг хүлээн зөвшөөрнө. Хүлээн авагч мессежийн агуулгыг уншсан эсвэл ойлгосон гэсэн баталгаа байхгүй." + }, + "no_email_selected": "Имэйл сонгоогүй", + "no_email_description": "Жагсаалтаас имэйл сонгоод эндээс харна уу", + "no_conversation_selected": "Ярилцлага сонгоогүй", + "no_conversation_description": "Жагсаалтаас харилцан яриаг сонгон эндээс уншина уу", + "compose": "Зохих", + "compose_hint": "Шинэ мессеж бичих", + "no_subject": "(Сэдэв байхгүй)", + "no_body_content": "(Биеийн агуулга байхгүй)", + "no_preview_available": "Урьдчилан үзэх боломжгүй", + "show_quoted_text": "Ишлэсэн текстийг харуулах", + "hide_quoted_text": "Иш татсан текстийг нуух", + "loading_email": "Имэйл ачаалж байна...", + "loading": "Ачааж байна...", + "reply": "Хариулах", + "reply_all": "Бүгдэд нь хариулах", + "forward": "Урагшаа", + "delete": "Устгах", + "archive": "Архив", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "од", + "unstar": "Одгүй болгох", + "mark_unread": "Уншаагүй гэж тэмдэглэх", + "mark_read": "Уншсан гэж тэмдэглэх", + "unread": "Уншаагүй", + "read": "унших", + "spam_short": "Спам", + "not_spam_short": "Спам биш", + "move": "Хөдөлгөөн", + "print": "Хэвлэх", + "view_source": "Эх сурвалжийг харах", + "fullscreen": "Бүтэн дэлгэц", + "exit_fullscreen": "Бүтэн дэлгэцээс гарах", + "view_in_dark_mode": "Хар горимд харах", + "view_in_light_mode": "Цайвар горимд харах", + "export_email": ".eml", + "forward_as_attachment": "Хавсралт болгон дамжуулах", + "import_email": "Импорт .eml эсвэл .zip", + "keyboard_shortcuts": "Гарын товчлол (?)", + "email_source": "Имэйл эх сурвалж", + "draft_banner": "Энэ зурвас нь ноорог байна", + "edit_draft": "Засварлах", + "copy_source": " Түр санах ой руу хуулах", + "source_copied": "Эх сурвалжийг санах ой руу хуулсан", + "attachments": "Хавсралт", + "important": "Чухал", + "download": "Татаж авах", + "download_all": "Бүгдийг нь татаж авах", + "from": "", + "to": "Хэнд", + "cc": "Хуулбар", + "bcc": "Нууц хуулбар", + "date": "Огноо", + "subject": "Сэдэв", + "show_details": "Дэлгэрэнгүйг харуулах", + "hide_details": "Дэлгэрэнгүй мэдээллийг нуух", + "external_content_warning": "Зураг болон гадаад контентыг хаасан", + "load_external_content": "Зураг ачаалах", + "trust_sender": "Энэ илгээгчид үргэлж итгэ", + "sender_check": { + "unverified_label": "Баталгаажаагүй илгээгч", + "failed_label": "Илгээгчийн шалгалт амжилтгүй болсон", + "unverified": "Энэ зурвас {domain} домэйнээс ирсэн гэж мэдэгдэж байгаа ч үүнийг баталгаажуулах зүйл алга. Үүнд {domain} домэйны хүчинтэй гарын үсэг байхгүй бөгөөд {domain} домэйны зөвшөөрсөн серверээс ирээгүй байна.", + "unverified_sent_from": "Энэ зурвас {domain} домэйнээс ирсэн гэж мэдэгдэж байгаа ч {host} домэйнээс илгээгдсэн бөгөөд {domain} домэйны хүчинтэй гарын үсэг байхгүй байна.", + "failed": "Энэ зурвас {domain} домэйнээс ирсэн гэж мэдэгдэж байгаа ч тухайн домэйны илгээгчийн шалгалтыг давж чадаагүй. Хуурамч байж магадгүй.", + "failed_sent_from": "Энэ зурвас {domain} домэйнээс ирсэн гэж мэдэгдэж байгаа ч {host} домэйнээс илгээгдсэн бөгөөд {domain} домэйны илгээгчийн шалгалтыг давж чадаагүй. Хуурамч байж магадгүй.", + "caution": "Холбоос, хавсралт болон нууц үг, төлбөрийн мэдээлэл асуусан хүсэлтэд болгоомжтой хандаарай." + }, + "back_to_list": "Жагсаалт руу буцах", + "view_contact": "Харилцагчийг харах", + "message_details": "Зурвасны дэлгэрэнгүй мэдээлэл", + "more_reply_options": "Бусад хариултын сонголтууд", + "set_tag": "Таг тавих", + "tag": "Таг", + "more_actions": "Дэлгэрэнгүй үйлдэл", + "previous": "Өмнөх", + "next": "Дараагийн", + "move_to": "...", + "remove_tag": "Ташгыг арилгах", + "tag_filter_placeholder": "Шүүлтүүрийн шошго", + "tag_no_matches": "Тохирох шошго байхгүй", + "more_count": "+{count} дэлгэрэнгүй", + "characters_count": "{count} тэмдэгт", + "quick_reply_placeholder": "Шуурхай хариу бичнэ үү...", + "more_options": "Бусад сонголтууд", + "sending": "Илгээж байна...", + "security_authentication": "Аюулгүй байдал & Баталгаажуулалт", + "technical_details": "Техникийн мэдээлэл", + "message_id_label": "Мессежийн дугаар:", + "reply_to_label": "Хариу бичих:", + "delivery_time_label": "Хүргэх хугацаа:", + "conversation_part_label": "Ярилцлагын хэсэг:", + "previous_messages": "{count} өмнөх зурвас", + "previous_messages_plural": "{count} өмнөх зурвасууд", + "time": { + "day": "өдөр", + "days": "өдөр", + "hour": "цаг", + "hours": "цаг", + "minute": "мин", + "minutes": "минут" + }, + "unknown_sender": "Үл мэдэгдэх", + "recipient_me": "би", + "recipient_and_others": "{name} ба {count} бусад", + "recipient_to_prefix": "Хэнд:", + "authentication": { + "title": "Баталгаажуулалт", + "status": { + "verified": "Баталгаажсан", + "warning": "Анхааруулга", + "none": "Баталгаажуулаагүй" + }, + "spf": { + "pass": "SPF нэвтрүүлэх", + "fail": "SPF амжилтгүй боллоо", + "none": "SPF-гүй" + }, + "dkim": { + "pass": "DKIM хүчинтэй", + "fail": "DKIM хүчингүй", + "none": "DKIM байхгүй" + }, + "dmarc": { + "pass": "DMARC амжилттай", + "fail": "DMARC амжилтгүй боллоо", + "none": "DMARC байхгүй" + }, + "spam_score": "Спамын оноо", + "tooltip_spf": "Илгээгчийн бодлогын хүрээ: Илгээж буй сервер нь домэйны нэрийн өмнөөс имэйл илгээх эрхтэй болохыг баталгаажуулна", + "tooltip_dkim": "DomainKeys Identified Mail: Имэйлийг криптограф гарын үсэг ашиглан дамжуулж байхдаа өөрчлөөгүйг баталгаажуулна", + "tooltip_dmarc": "Домэйн дээр суурилсан мессежийн баталгаажуулалт, тайлагнах, нийцүүлэх: SPF болон DKIM нь илгээгчийн домайнтай нийцэж байгаа эсэхийг баталгаажуулж, алдаа дутагдлын бодлогыг тогтооно", + "policy": "Бодлого", + "result": { + "pass": "Дамжуулах", + "fail": "Бүтэлгүйтсэн", + "softfail": "Зөөлөн алдаа", + "neutral": "Төвийг сахисан", + "permerror": "Байнгын алдаа", + "temperror": "Түр зуурын алдаа", + "none": "Байхгүй" + } + }, + "details": { + "recipients_routing": "Хүлээн авагч ба чиглүүлэлт", + "authentication_security": "Баталгаажуулалт ба аюулгүй байдал", + "identifiers_threading": "Таниулагч ба утас холбох", + "mailing_list": "Мэйлийн жагсаалт", + "message_properties": "Мессежийн шинж чанарууд", + "sent": "Илгээсэн", + "received": "Хүлээн авсан", + "delivery_time": "Хүргэх хугацаа", + "in_reply_to": "Хариу бичих", + "references": "Ашигласан материал", + "thread_id": "Сэдвийн ID", + "size": "Хэмжээ", + "mime_type": "MIME төрөл", + "attachments_summary": "{count} файлууд · {size}", + "list_id": "Жагсаалтын ID", + "list_help": "Тусламжийн жагсаалт", + "list_post": "Жагсаалтын нийтлэл", + "list_unsubscribe": "Бүртгэлээ цуцлах", + "iprev": "Урвуу DNS", + "spam_status": "Спамын статус", + "ai_verdict": "AI-ийн шийдвэр", + "account": " Данс", + "no_subject": "(сэдэв байхгүй)" + }, + "headers": { + "routing": "Чиглүүлэлт", + "received": "Хүлээн авсан", + "message_id": "Зурвасны ID", + "list_info": "Мэдээллийн жагсаалт" + }, + "tooltips": { + "reply": "Хариулт (r)", + "reply_all": "Бүгдэд нь хариулах (a)", + "forward": "Урагшаа (f)", + "archive": "Архив (д)", + "delete": "Устгах (# эсвэл Del)", + "star": "Од(ууд)", + "unstar": "Одгүй(s)", + "compose": "Бичих (c)", + "previous": "Өмнөх имэйл", + "next": "Дараагийн имэйл", + "edit_draft": "Ноорог засах" + }, + "spam": { + "button_title": "Спам мэдээлэх", + "not_spam_title": "Хууль ёсны гэж тэмдэглэх", + "toast_success": "Хог хаяг руу шилжсэн", + "toast_batch": "{count} имэйлийг хог хаяг руу зөөсөн", + "toast_undo": "Буцаах", + "toast_not_spam_success": "Ирсэн имэйл рүү шилжсэн", + "toast_not_spam_batch": "{count} имэйлийг Ирсэн имэйл рүү зөөсөн", + "error": "Спамыг мэдээлж чадсангүй", + "error_not_spam": "Имэйл сэргээж чадсангүй" + }, + "unsubscribe_banner": { + "label": "Мэдээллийн хуудас", + "button": "Бүртгэлээ цуцлах", + "confirm_title": "Энэ илгээгчийн бүртгэлээ цуцлах уу?", + "confirm_button": "Баталгаажуулах", + "cancel": "Цуцлах", + "success_http": "Бүртгэлээ цуцлах хуудас шинэ таб дээр нээгдсэн", + "success_mailto": "Захиалгаа цуцлах имэйл илгээсэн", + "confirm_message_http": "Бүртгэлээ цуцлах хуудас шинэ таб дээр нээгдэнэ.", + "confirm_message_mailto": "Илгээгч рүү бүртгэлээ цуцлах имэйл илгээгдэх болно.", + "error": "Бүртгэлээ цуцлах боломжгүй", + "dismiss": "Хаах" + }, + "calendar_invitation": { + "loading": "Үйл явдлын дэлгэрэнгүй мэдээллийг ачаалж байна...", + "title": "Календарийн урилга", + "published_title": "Нийтлэгдсэн арга хэмжээ", + "response_title": "Үйл явдлын хариу үйлдэл", + "update_title": "Үйл явдлын шинэчлэлт", + "counter_title": "Сөрөг санал", + "refresh_title": "Хүсэлтийг сэргээх", + "declined_counter_title": "Сөрөг саналаас татгалзсан", + "cancelled_title": "Үйл явдал цуцлагдсан", + "organizer": "Зохион байгуулсан {name}", + "organizer_label": "Зохион байгуулсан", + "attendees": "{count, plural, one {# оролцогч} other {# оролцогч}}", + "accept": "Зөвшөөрөх", + "maybe": "Магадгүй", + "decline": "Бууралт", + "add_to_calendar": "Хуанлид нэмэх", + "added": "Хуанлид нэмсэн", + "rsvp_sent": "Хариулт илгээсэн", + "parse_error": "Урилгыг уншиж чадсангүй", + "action_failed": "Тэр хуанлийн үйлдлийг хийж чадсангүй.", + "no_calendar": "Хуанли байхгүй", + "published_info": "Энэ үйл явдлыг лавлах зорилгоор хуваалцсан.", + "response_info": "Энэ мессеж нь оролцогчийн хариултыг агуулж байна.", + "response_info_organizer": "Энэ оролцогчийн хариулт нь таны үйл явдлыг шинэчилнэ.", + "update_info": "Энэ зурвас нь одоо байгаа үйл явдлыг шинэчилнэ.", + "counter_info": "Энэ зурвас нь үйл явдалд өөрчлөлт оруулахыг санал болгож байна.", + "counter_info_organizer": "Энэ оролцогч таны арга хэмжээнд өөрчлөлт оруулахыг санал болгосон.", + "refresh_info": "Энэ зурвас нь хамгийн сүүлийн үеийн үйл явдлын дэлгэрэнгүй мэдээллийг шаардаж байна.", + "refresh_info_organizer": "Оролцогч хамгийн сүүлийн үеийн үйл явдлын дэлгэрэнгүй мэдээллийг хүссэн.", + "declined_counter_info": "Зохион байгуулагч сөрөг саналаас татгалзсан.", + "authentication_failed_info": "Энэ урилгын имэйлийн баталгаажуулалтыг шалгаж чадсангүй. Хуанлийн үйлдэлд болгоомжтой хандах.", + "authentication_missing_info": "Энэ урилгад баталгаажуулсан имэйлийн баталгаажуулалт ороогүй болно. Ер бусын зүйл байвал зохион байгуулагчтай дэлгэрэнгүй мэдээллийг баталгаажуулна уу.", + "sender_mismatch_info": "Энэ урилгыг {sender}-с илгээсэн бол хуанлийн өгөгдөлд жагсаасан зохион байгуулагч нь {organizer} байна.", + "sender_mismatch_unverified_info": "Энэ урилгыг {sender}-с илгээсэн бөгөөд хуанлийн өгөгдөлд жагсаасан зохион байгуулагч нь {organizer} бөгөөд мессежийг баталгаажуулж чадсангүй.", + "organizer_role": "Та энэ арга хэмжээг зохион байгуулна", + "your_response": "Таны хариулт: {status}", + "response_needed": "Хариулт хэрэгтэй", + "response_accepted": "Хүлээн зөвшөөрсөн", + "response_tentative": "Урьдчилсан", + "response_declined": "Татгалзсан", + "response_delegated": "Төлөөлөгдсөн", + "actor_sent_info": "{name} илгээсэн.", + "actor_response_info": "{name} {status} гэж хариулав.", + "actor_counter_info": "{name} энэ үйл явдалд өөрчлөлт оруулахыг санал болгосон.", + "actor_refresh_info": "{name} хамгийн сүүлийн үеийн үйл явдлын дэлгэрэнгүй мэдээллийг асуув.", + "actor_declined_counter_info": "{name} эсрэг саналаас татгалзсан.", + "actor_note": "Жич: {comment}", + "actor_unknown": "Хэн нэгэн", + "proposed_changes": "Санал болгож буй өөрчлөлтүүд", + "change_title": "Гарчиг", + "change_time": "Цаг", + "change_location": "Байршил", + "change_description": "Тодорхойлолт", + "change_empty": "Байхгүй", + "change_from_to": "{before} → {after}", + "apply_proposal": "Санал болгож буй өөрчлөлтийг хэрэгжүүлэх", + "proposal_applied": "Санал болгож буй өөрчлөлтүүдийг хэрэгжүүлсэн.", + "review_proposal": "Саналыг хянан үзэх", + "review_request": "Хүсэлтийг хянан үзэх", + "view_in_calendar": "Хуанлиар харах", + "select_calendar": "Хуанли сонгох", + "already_in_calendar": "Таны хуанлид аль хэдийн орсон", + "request_info": "Таныг энэ арга хэмжээнд урьсан. Зохион байгуулагчид боломжоо мэдэгдэхийн тулд хариу бичнэ үү.", + "cancel_info": "Зохион байгуулагч энэ арга хэмжээг цуцалсан.", + "event_updated": "Шинэчлэх #{sequence}", + "event_status_tentative": "Урьдчилсан", + "event_status_cancelled": "Цуцлагдсан", + "expand": "Дэлгэрэнгүйг харуулах", + "collapse": "Дэлгэрэнгүй мэдээллийг нуух" + }, + "send": "Илгээх", + "more": "илүү", + "scheduled_banner": "{date}", + "scheduled_send_created": "Имэйл илгээхээр товлосон", + "sending_in": "{seconds} секундын дараа илгээнэ", + "undo_send_failed": "Буцаах боломжгүй. Мессеж аль хэдийн илгээгдсэн байж магадгүй.", + "cancel_scheduled_send": "Илгээхийг цуцлах", + "reschedule_send": "Хуваарь солих", + "cancel_and_edit": "Цуцлах, засварлах", + "cancel_and_compose_again": "Цуцлаад дахин бичих", + "reschedule_prompt": "Шинэ огноо/цаг оруулна, ж.нь. 2026-05-04Т15:30", + "scheduled_actions_only": "Хуваарьт харах нь зөвхөн төлөвлөсөн үйлдлүүдийг дэмждэг", + "undo_send_scheduled": "Илгээхээр товлосон мессеж", + "undo_send": "Илгээхийг буцаах", + "contact_sidebar": { + "title": "Холбоо барих", + "close": "Хажуугийн самбарыг хаах", + "action_email": "И-мэйл", + "action_email_title": "Имэйл илгээх", + "action_copy": "Хуулбар", + "action_copy_title": "Имэйл хуулах", + "action_edit_title": "Харилцагчийг засах", + "section_emails": "Имэйл", + "section_phones": "Утас", + "section_organizations": "Байгууллага", + "section_addresses": "Хаяг", + "section_notes": "Тэмдэглэл", + "not_in_contacts": "Таны харилцагчид байхгүй", + "add_to_contacts": "Харилцагчдад нэмэх", + "copied": "Хуулбарласан!", + "copy_failed": "Хуулж чадсангүй", + "view_contact": "Харилцагчийг харах", + "view_details": "Дэлгэрэнгүй харах" + }, + "send_now": "Одоо илгээх", + "verification_code": { + "copy": "{code} кодыг хуулах", + "copied": "Код хуулагдлаа", + "copy_failed": "Кодыг хуулж чадсангүй" + }, + "mv_position": "{total}-с {index}", + "plugin_panel_show": "Залгаасын самбарыг харуулах", + "plugin_panel_hide": "Залгаасын самбарыг нуух" + }, + "email_composer": { + "select_from_directory": "Лавлахаас сонгох", + "minimize": "Багасгах", + "maximize": "Бүтэн дэлгэц", + "restore": "Сэргээх", + "new_message": "Шинэ мессеж", + "signature_edit_hint": "Гарын үсгийг засахын тулд давхар товшино уу", + "reply": "Хариулах", + "reply_all": "Бүгдэд нь хариулах", + "forward": "Урагшаа", + "reply_to": "Хариулах", + "reply_all_to": "Бүгдэд нь хариулах", + "forward_message": "Урагшаа", + "from": "", + "to": "руу", + "cc": "Хуулбар", + "bcc": "Нууц хуулбар", + "subject": "Сэдэв", + "body_placeholder": "Мессеж бичнэ үү...", + "send": "Илгээх", + "cancel": "Цуцлах", + "attach": "Хавсаргах", + "attach_photos": "Зураг, Видео", + "attach_files": "Файл", + "attach_from_files": "Файлаас хавсаргах", + "files_picker": { + "attach": "{count, plural, =0 {Хавсаргах} one {1 файл хавсаргах} other {# файл хавсаргах}}", + "empty_folder": "Энэ фолдер хоосон байна", + "load_failed": "Таны файлуудыг ачаалж чадсангүй" + }, + "discard": "Хаях", + "discard_draft_title": "Ноорог устгах уу?", + "discard_draft_confirm": "Танд хадгалагдаагүй өөрчлөлтүүд байна. Та энэ ноорогоос татгалзахыг хүсэж байна уу?", + "discard_draft_message": "Ноорог устгагдана.", + "saving": "Хадгалж байна...", + "sending": "Илгээж байна...", + "add_link": "Холбоос нэмэх", + "link_url_prompt": "URL-г оруулна уу", + "draft_saved": "Ноорог хадгалсан", + "save_failed": "Хадгалж чадсангүй", + "to_placeholder": "Хүлээн авагчийн имэйл хаягууд", + "cc_placeholder": "Cc хүлээн авагчид", + "bcc_placeholder": "Bcc хүлээн авагчид", + "subject_placeholder": "Сэдэв", + "cc_label": "Хуулбар", + "bcc_label": "Нууц хуулбар", + "subject_label": "Гэдэв:", + "file_size_kb": "КБ", + "prefix": { + "forward": "Дахин:", + "reply": "Дахин:" + }, + "forward_batch_count": "{count, plural, one {1 имэйл} other {# имэйл}}", + "no_subject": "(Сэдэв байхгүй)", + "unknown_sender": "Үл мэдэгдэх", + "quote": { + "reply_header": "{date} дээр {sender} бичсэн:", + "forward_header": "---------- Дамжуулсан мессеж ----------", + "from": "Хэнээс: {sender}", + "date": "Огноо: {date}", + "subject": "Гэдэв: {subject}", + "to": "Хэнд: {recipients}" + }, + "remove_sub_address": "Дэд хаягийг устгах", + "from_override": { + "toggle_off": "Дараах", + "toggle_on": "Дараахыг цуцлах", + "toggle_tooltip": "From нэр хаягийг чөлөөтэй засварлана. Таны хувийн мэдээллээр шуудан илгээгдсэн хэвээр байна.", + "name_label": "Нэрнээс", + "name_placeholder": "Нэр", + "email_label": "Имэйл хаягаас", + "email_placeholder": "user@example.com", + "envelope_notice": "Хэрэв таны сервер энэ хаягийг дугтуйн илгээгчээр хүлээн авахгүй бол оронд нь {identity} ашиглагдах бөгөөд хүлээн авагчид үүнийг Return-Path гарчигт харж болно." + }, + "save_as_template": "Загвар болгон хадгалах", + "attach_from_device": "Энэ төхөөрөмжөөс байршуулах", + "templates_menu": "Загварууд", + "insert_template": "Загвар оруулах", + "options": "Тохиргоо", + "option_plain_text": "Энгийн текст", + "option_plain_text_hint": "Форматгүй илгээх", + "option_read_receipt": "Уншсан баталгаа хүсэх", + "option_read_receipt_hint": "Хүлээн авагчаас нээснээ баталгаажуулахыг хүсэх", + "option_dsn": "Хүргэлтийн мэдэгдэл", + "option_dsn_hint": "Сервер хүргэх үед тайлан авах", + "option_require_tls": "Шифрлэгдсэн хүргэлт шаардах", + "option_require_tls_hint": "Зөвхөн TLS-ээр хүргэх, үгүй бол буцаах", + "option_priority": "Чухал байдал", + "option_priority_high": "Өндөр", + "option_priority_normal": "Энгийн", + "option_priority_low": "Бага", + "validation": { + "recipient_required": "Илгээхийн тулд хүлээн авагч нэмэх", + "body_required": "Мессеж бичих эсвэл файл хавсаргах", + "invalid_recipients": "Илгээгдсэнгүй - эдгээр хаягийг шалгана уу: {addresses}", + "attachments_uploading": "Хавсралтуудыг байршуулсаар байгаа бөгөөд дуусмагц илгээх болно", + "attachment_upload_failed": "Илгээгүй — хавсралтыг байршуулж чадсангүй. Үүнийг устгаад дахин оролдоно уу." + }, + "upload_progress": "{uploaded} / {total}", + "upload_cancel": "Байршуулалтыг цуцлах", + "upload_failed": "{filename}", + "attachment_too_large": "\"{name}\" сервер зөвшөөрөхөөс том байна (нэг файлд {max})", + "attachments_total_too_large": "Хавсралтууд серверийн нэг захидалд ногдох {max} хязгаараас хэтэрнэ", + "drop_files": "Файлуудыг хавсаргах", + "show_less": "Бага харуулах", + "send_failed": "Имэйл илгээж чадсангүй", + "send_timeout": "Сервер хариу өгөхөө больсон. Дахин илгээхийн өмнө \"Илгээсэн\" фолдероо шалгана уу — мессеж унтарсан байж магадгүй.", + "send_offline": "Та офлайн байх шиг байна. Мессеж энд хадгалагдсан тул онлайн болмогц дахин илгээнэ үү.", + "formatting": "Формат", + "send_recipients_rejected": "Илгээгдсэнгүй - сервер бүх хүлээн авагчийг татгалзсан.", + "send_plugin_failed": "Илгээгдсэнгүй - илгээлтийг гүйцэтгэдэг залгаас алдаа гаргасан эсвэл хэт удсан. Дахин илгээхээсээ өмнө Илгээсэн хавтсаа шалгана уу.", + "send_some_recipients_rejected": "Илгээсэн, гэхдээ эдгээр хүлээн авагчид хүрэхгүй - сервер тэднийг татгалзсан.", + "continue_draft": "Ноорог үргэлжлүүлэх", + "close_draft_title": "Ноорог хадгалах эсвэл устгах уу?", + "close_draft_message": "Танд хадгалагдаагүй өөрчлөлтүүд байна. Та үүнийг ноорог болгон хадгалах уу эсвэл хаях уу?", + "smime_sign_on": "S/MIME гарын үсэг зурахыг идэвхжүүлсэн", + "smime_sign_off": "S/MIME гарын үсэг зурахыг идэвхжүүлэх", + "smime_encrypt_on": "S/MIME шифрлэлтийг идэвхжүүлсэн", + "smime_encrypt_off": "S/MIME шифрлэлтийг идэвхжүүлэх", + "smime_encrypt_unavailable": "S/MIME шифрлэлт боломжгүй - хүлээн авагчийн гэрчилгээ алга", + "smime_unlock_title": "S/MIME түлхүүрийн түгжээг тайлах", + "smime_unlock_message": "S/MIME гарын үсэг зурах түлхүүрийнхээ түгжээг тайлахын тулд нэвтрэх үгийг оруулна уу.", + "smime_unlock_button": "Түгжээг тайлах", + "smime_passphrase_placeholder": "Нууц үг", + "forgot_attachment": { + "title": "Та хавсралтаа мартсан уу?", + "message": "Таны зурваст \"{keyword}\" гэж дурдсан боловч ямар ч файл хавсаргаагүй байна. Ямартай ч явуулах уу?", + "send_anyway": "Ямар ч байсан илгээх", + "back": "Засварлах руу буцах" + }, + "empty_subject": { + "title": "Гарчиггүй илгээх үү?", + "message": "Энэ зурваст гарчиг байхгүй. Ямартай ч явуулах уу?", + "dont_ask_again": "Дахиж битгий асуу", + "send_anyway": "Ямар ч байсан илгээнэ үү", + "back": "Засварлах руу буцах" + }, + "schedule_send": "Хуваарь илгээх", + "schedule_send_description": "Сервер энэ мессежийг хэзээ гаргахыг сонгоно уу.", + "schedule_preset": { + "tomorrow_morning": "Маргааш өглөө", + "tomorrow_afternoon": "Маргааш үдээс хойш", + "monday_morning": "Даваа гарагийн өглөө" + }, + "schedule_send_required": "Огноо, цаг сонгох.", + "schedule_send_invalid": "Хүчинтэй огноо, цагаа оруулна уу.", + "schedule_send_future": "Ирээдүйн огноо, цагийг сонгох.", + "schedule_send_too_late": "Энэ хугацаа нь серверийн зөвшөөрснөөс хойш байна.", + "schedule_send_unsupported": "Энэ бүртгэлд хуваарьт илгээхийг дэмждэггүй.", + "schedule_send_cleanup_warning": "Хуваарьт илгээлт үүсгэсэн боловч ноорог цэвэрлэж чадсангүй.", + "send_delay_unsupported": "Илгээлтийн саатал энэ бүртгэлд дэмжигдээгүй.", + "send_delay_unsupported_confirm": "Энэ бүртгэл нь илгээх саатлыг дэмждэггүй. Оронд нь шууд явуулах уу?", + "recipient_edit_email": "Имэйл хаягийг засах", + "recipient_edit_name": "Дэлгэцийн нэрийг засах", + "recipient_email_placeholder": "Имэйл хаяг", + "recipient_name_placeholder": "Дэлгэцийн нэр", + "invalid_recipient": "Хүчинтэй имэйл хаяг биш", + "autocomplete_search_server": "Серверээс хайх", + "autocomplete_searching": "Хайж байна...", + "mention_recipients": "Хүлээн авагчид", + "mention_card_not_recipient": "Хэнд эсвэл CC-д байхгүй тул энэ захидлыг хүлээн авахгүй.", + "mention_card_add": "{field}-д нэмэх", + "mention_card_remove": "Дурдалтыг хасах", + "toolbar": { + "bold": "Болд", + "italic": "Налуу", + "underline": "Доогуур зураас", + "strikethrough": "Үзсэн зураас", + "text_color": "Текстийн өнгө", + "remove_color": "Өнгийг арилгах", + "heading_1": "Гарчиг 1", + "heading_2": "Гарчиг 2", + "bullet_list": "Сумын жагсаалт", + "ordered_list": "Захиалгат жагсаалт", + "quote": "Эшлэл", + "code_block": "Код блок", + "align_left": "Зүүнд зэрэгцүүлэх", + "align_center": "Төвийг тэгшлэх", + "align_right": "Баруун тийш зэрэгцүүлэх", + "text_direction": "Текстийн чиглэл (RTL/LTR)", + "link": "Холбоос", + "table": "Хүснэгт", + "clear_formatting": "Форматыг арилгах", + "undo": "Буцах", + "redo": "Дахин хийх", + "add_row_above": "Дээрх мөр нэмэх", + "add_row_below": "Доор нэмэх", + "add_column_before": "Өмнө нь багана нэмнэ үү", + "add_column_after": "0", + "delete_row": "Мөр устгах", + "delete_column": "Баганыг устгах", + "toggle_header_row": "Толгойн мөрийг сэлгэх", + "delete_table": "Хүснэгт устгах", + "pick_size": "Хэмжээ сонгох", + "font_size": "Фонтын хэмжээ", + "font_size_default": "Үндсэн", + "background_color": "Дэвсгэр өнгө", + "remove_background_color": "Дэвсгэр өнгийг арилгах" + }, + "send_filing_warning": "Илгээсэн - гэхдээ илгээсний дараах цэвэрлэгээ амжилтгүй болсон тул хуучирсан ноорог үлдэж болзошгүй.", + "pop_out": "Хариуг тусдаа цонхонд нээх", + "send_guard_failed": "Илгээгдсэнгүй - илгээхийн өмнө мессежийг шалгадаг залгаас алдаа гаргасан эсвэл хариу өгсөнгүй." + }, + "confirm_dialog": { + "confirm": "Баталгаажуулах", + "cancel": "Цуцлах" + }, + "common": { + "loading": "Ачааж байна...", + "error": "Алдаа", + "success": "Амжилт", + "cancel": "Цуцлах", + "save": "Хадгалах", + "delete": "Устгах", + "edit": "Засварлах", + "close": "Хаах", + "search": "Хайлт", + "refresh": "Сэргээх", + "settings": "Тохиргоо", + "help": "Тусламж", + "logout": "Гарах", + "yes": "Тийм", + "no": "Үгүй", + "unknown": "Үл мэдэгдэх", + "app_title": "Вэбмэйл", + "reconnecting": "Холболт тасарсан. Дахин холбогдохоор оролдож байна...", + "rate_limited_title": "Сервер баталгаажуулалтын оролдлогыг түр хугацаанд хязгаарлалаа.", + "rate_limited_detail": "Bulwark түгжигдэхээс зайлсхийхийн тулд арын хүсэлтийг түр зогсоосон. {seconds}-р дахин оролдож байна.", + "rate_limited_action_title": " Түгжигдэхээс зайлсхийхийн тулд хүсэлтийг түр зогсоосон.", + "rate_limited_action_detail": "Bulwark илүү баталгаажуулсан хүсэлт илгээхийн өмнө серверийн хөргөлт дуусахыг хүлээж байна. {seconds}-ээр дахин оролдоно уу.", + "pull_to_refresh": "Шинэчлэхийн тулд татна уу", + "release_to_refresh": "Шинэчлэхийн тулд суллана уу", + "refreshing": "Шинэчилж байна…" + }, + "notifications": { + "email_sent": "Имэйл амжилттай илгээгдсэн", + "email_deleted": "Имэйл устгасан", + "email_archived": "Имэйл архивлагдсан", + "emails_deleted": "{count, plural, other {# имэйл устгагдлаа}}", + "emails_archived": "{count, plural, other {# имэйл архивлагдлаа}}", + "error_archiving": "Имэйлийг архивлаж чадсангүй", + "error_updating": "Имэйлийг шинэчилж чадсангүй", + "email_starred": "И-мэйл одоор тэмдэглэгдсэн", + "email_unstarred": "И-мэйл одыг арилгасан", + "email_marked_read": "И-мэйл уншсан гэж тэмдэглэсэн", + "email_marked_unread": "И-мэйл уншаагүй гэж тэмдэглэгдсэн", + "copied_to_clipboard": " Түр санах ой руу хуулсан", + "source_copied": "Эх сурвалжийг санах ой руу хуулсан", + "error_sending": "Имэйл илгээж чадсангүй", + "error_deleting": "Имэйл устгаж чадсангүй", + "error_loading": "Имэйл ачаалж чадсангүй", + "new_email": "Шинэ имэйл", + "new_email_from": "{sender}", + "click_to_view": "Үзэх бол товшино уу", + "email_moved": "И-мэйл шилжүүлсэн", + "emails_moved": "{count} имэйлийг зөөсөн", + "moved_to_mailbox": "{mailbox}", + "move_failed": "Зөөж чадсангүй", + "removed_from_folder": "{mailbox}-с хасагдлаа", + "remove_from_folder_failed": "Энэ хавтаснаас хасаж чадсангүй", + "emails_copied": "{count} имэйлийг хуулсан", + "copy_failed": "Хуулж чадсангүй", + "move_error": "Имэйлүүдийг сонгосон хавтас руу зөөж чадсангүй", + "email_tagged": "И-мэйл хаяглагдсан", + "emails_tagged": "{count} мэйл хаяглагдсан", + "tag_failed": "Табга хийж чадсангүй", + "identity_created": "Таниулбарыг амжилттай үүсгэлээ", + "identity_updated": "Таниулбарыг амжилттай шинэчиллээ", + "identity_deleted": "Таниулбарыг устгалаа", + "identity_set_primary": "Үндсэн таниулбар шинэчлэгдсэн", + "identity_create_failed": "Таниулбар үүсгэж чадсангүй: {error}", + "identity_update_failed": "Таниулбарыг шинэчилж чадсангүй: {error}", + "identity_delete_failed": "Таниулбарыг устгаж чадсангүй: {error}", + "identity_unauthorized": "Та энэ имэйл хаягаас илгээх эрхгүй", + "identity_not_found": "Таниулбар олдсонгүй", + "vacation_saved": "Эзгүй байх тохиргоог хадгалсан", + "vacation_save_failed": "Эзгүй байх тохиргоог хадгалж чадсангүй", + "filters_saved": "Шүүлтүүрийг амжилттай хадгаллаа", + "filters_save_failed": "Шүүлтүүрийг хадгалж чадсангүй", + "filters_deleted": "Шүүлтүүрийн дүрмийг устгасан", + "templates_exported": "Загваруудыг амжилттай экспортоллоо", + "templates_imported": "{count, plural, one {# загвар} other {# загвар}}импортолсон", + "templates_import_errors": "Зарим загварыг импортлох боломжгүй байна", + "templates_import_empty": "Файлд ямар ч загвар олдсонгүй", + "export_email_error": "Имэйл экспорт хийж чадсангүй", + "import_email_success": "Имэйл амжилттай импортлогдсон", + "import_email_error": "Имэйл импортлож чадсангүй", + "rule_created": "Дүрэм үүслээ", + "rule_merged": "“{name}” дүрэмд нэмлээ", + "rule_already_covered": "“{name}” дүрэм үүнийг аль хэдийн хамарсан", + "rule_apply_existing": "{count, plural, one {Байгаа # захианд хэрэглэх} other {Байгаа # захианд хэрэглэх}}", + "rule_applied": "{count, plural, =0 {Өөрчлөх захиа байсангүй} one {Дүрмийг # захианд хэрэглэлээ} other {Дүрмийг # захианд хэрэглэлээ}}", + "rule_apply_failed": "Дүрмийг байгаа захиануудад хэрэглэж чадсангүй", + "rule_edit": "Дүрэм засах", + "rule_undo": "Буцаах", + "rule_undone": "Дүрмийн өөрчлөлтийг буцаалаа", + "rule_undo_failed": "Дүрмийн өөрчлөлтийг буцааж чадсангүй", + "rule_undo_conflict": "Үүнээс хойш таны шүүлтүүр өөрчлөгдсөн. Өөрчлөлтийг шүүлтүүрийн тохиргооноос буцаана уу.", + "rule_folder_failed": "Хавтас үүсгэж чадсангүй", + "vacation_filters_save_failed": "Эзгүй байх тохиргоог хадгалсан боловч дамжуулалт болон хариу хүлээн авагчдыг хадгалж чадсангүй" + }, + "date": { + "today": "Өнөөдөр", + "yesterday": "Өчигдөр", + "this_week": "Энэ долоо хоногт", + "last_week": "Өнгөрсөн долоо хоног", + "this_month": "Энэ сард", + "older": "Ахмад", + "just_now": "Дөнгөж сая", + "minutes_ago": "{count} минутын өмнө", + "minutes_ago_plural": "{count} минутын өмнө", + "hours_ago": "{count} цагийн өмнө", + "hours_ago_plural": "{count} цагийн өмнө", + "days_ago": "{count} өдрийн өмнө", + "days_ago_plural": "{count} хоногийн өмнө" + }, + "language": { + "title": "Хэл", + "english": "Англи", + "french": "Франсаис", + "japanese": "日本語", + "spanish": "Испанол", + "italian": "Италиано", + "german": "Deutsch", + "dutch": "Нидерланд", + "portuguese": "Португали", + "russian": "Русский", + "select_language": "Хэл сонгох", + "switch_to_english": "Англи хэл рүү шилжих", + "switch_to_french": "Франц хэл рүү шилжих", + "switch_to_japanese": "Япон хэл рүү шилжих", + "switch_to_spanish": "Испани хэл рүү шилжих", + "switch_to_italian": "Итали хэл рүү шилжих", + "switch_to_german": "Герман хэл рүү шилжих", + "switch_to_dutch": "Нидерланд хэл рүү шилжих", + "switch_to_portuguese": "Португал хэл рүү шилжих", + "switch_to_russian": "Орос хэл рүү шилжих", + "switching": "Хэл сольж байна...", + "switch": "Хэл солих", + "current": "Одоогийн хэл", + "polish": "Польски", + "switch_to_polish": "Польш хэл рүү шилжих", + "en": "Англи хэл", + "fr": "Франсаис", + "de": "Deutsch", + "es": "Испанол", + "it": "Италиано", + "ja": "日本語", + "ko": "한국어", + "lv": "Латвиешу", + "nl": "Нидерланд", + "pl": "Полски", + "pt": "Португали", + "he": "עברית", + "ru": "Русский", + "sk": "Словенчина", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Фарси хэл рүү шилжих", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Монгол хэл рүү шилжих" + }, + "settings": { + "title": "Тохиргоо", + "back_to_mail": "Мэйл рүү буцах", + "save_success": "Тохиргоог амжилттай хадгаллаа", + "import_success": "Тохиргоог амжилттай импортоллоо", + "import_error": "Тохиргоог оруулж чадсангүй", + "reset_confirm": "Та бүх тохиргоог өгөгдмөл рүү буцаахдаа итгэлтэй байна уу?", + "unsaved_changes": "Танд хадгалагдаагүй өөрчлөлтүүд байна", + "discard_changes": "Хадгалагдаагүй өөрчлөлтүүдийг устгах уу?", + "discard": "Хасах", + "keep_editing": "Засварлаж байгаарай", + "search_placeholder": "Хайлтын тохиргоо", + "search_clear": "Хайлтыг арилгах", + "search_no_results": "Тохирох тохиргоо байхгүй", + "tabs": { + "appearance": "Гадаад төрх", + "language": "Хэл, бүс, цаг", + "email": "Имэйлийн зан байдал", + "composer": "Бичих цонх", + "privacy": "Нууцлал ба аюулгүй байдал", + "account": " Данс", + "identities": "Тэмдэгтүүд", + "vacation": "Эзгүй байх", + "advanced": "Дэвшилтэт", + "calendar": "Хуанли", + "filters": "Шүүлтүүр", + "templates": "Загварууд", + "folders": "Хавтас", + "keywords": "Тагууд", + "security": "Аюулгүй байдал", + "files": "Файл", + "contacts": "Харилцах утас", + "encryption": "Шифрлэлт", + "protocol_handlers": "Өгөгдмөл програмууд", + "sidebar_apps": "Хажуугийн аппууд", + "notifications": "Мэдэгдэл", + "layout": "Байршил", + "themes": "Сэдэв", + "reading": "Унших", + "composing": "Зохиоглох", + "downloads": "Татаж авах", + "content_senders": "Агуулга ба Илгээгчид", + "about_data": "Тухай & Өгөгдөл", + "debug": "Дабаглах" + }, + "themes": { + "title": "Сэдэв", + "description": "Администраторын байршуулсан сэдвүүд болон суулгасан бэлэн загваруудаас сонго.", + "locked": "Сэдэв сонголтыг администратор түгжсэн байна.", + "forced": "«{name}» сэдвийг администратор тогтоосон тул өөрчлөх боломжгүй", + "admin_theme": "Администраторын сэдэв", + "activated": "Сэдэв идэвхжлээ", + "default_restored": "Үндсэн сэдвийг сэргээлээ", + "default_name": "Үндсэн", + "default_badge": "Үндсэн", + "admin_enforced": "Администратор тогтоосон" + }, + "tab_groups": { + "general": "Ерөнхий", + "account": "Бүртгэл & Иргэний үнэмлэх", + "organization": "Шуудангийн байгууллага", + "apps": "Програмууд", + "system": "Систем", + "appearance": "Гадаад төрх", + "mail": "Мэйл", + "privacy": "Нууцлал ба Аюулгүй байдал", + "advanced": "Дэвшилтэт" + }, + "appearance": { + "message_list_order": { + "label": "Зурвасын жагсаалтын дараалал", + "description": "Зурвасууд жагсаалтад хэрхэн эрэмбэлэгдэх. Эрэмбэлэлтийг сервер хийдэг тул зөвхөн ачаалагдсан зурвас биш, бүх хавтсанд үйлчилнэ.", + "preset": { + "chronological": "Он цагийн дарааллаар (шинэ нь эхэнд)", + "unread_first": "Уншаагүй нь эхэнд", + "starred_first": "Одтой нь эхэнд", + "tagged_first": "Шошготой нь эхэнд", + "custom": "Өөрийн" + }, + "tag_label": "Шошго", + "advanced_show": "Нарийвчилсан: эрэмбийн түвшнүүдийг засах", + "advanced_hide": "Нарийвчилсныг нуух", + "criterion_label": "{n}-р түвшний шалгуур", + "direction_label": "{n}-р түвшний чиглэл", + "criteria": { + "unread": "Уншаагүй", + "starred": "Одтой", + "tag": "Шошго", + "receivedAt": "Хүлээн авсан огноо", + "sentAt": "Илгээсэн огноо", + "from": "Илгээгч", + "subject": "Гарчиг", + "size": "Хэмжээ" + }, + "direction": { + "unread_first": "Уншаагүй нь эхэнд", + "read_first": "Уншсан нь эхэнд", + "starred_first": "Одтой нь эхэнд", + "unstarred_first": "Одгүй нь эхэнд", + "tagged_first": "Шошготой нь эхэнд", + "untagged_first": "Шошгогүй нь эхэнд", + "newest_first": "Шинэ нь эхэнд", + "oldest_first": "Хуучин нь эхэнд", + "a_to_z": "А → Я", + "z_to_a": "Я → А", + "largest_first": "Том нь эхэнд", + "smallest_first": "Жижиг нь эхэнд" + }, + "add_level": "Түвшин нэмэх", + "remove_level": "Түвшин устгах", + "move_up": "Дээш зөөх", + "move_down": "Доош зөөх", + "tiebreak_note": "Дээд тал нь гурван түвшин; дараагийн түвшнүүд тэнцсэн үед шийднэ. «Шинэ нь эхэнд» үргэлж хамгийн сүүлд хэрэглэгдэнэ.", + "scope": { + "label": "Бүх хавтсанд хэрэглэх", + "description": "Унтраалттай үед дараалал зөвхөн Ирсэн хавтсанд үйлчилнэ; бусад хавтас он цагийн дарааллаараа үлдэнэ." + }, + "unsupported_note": "Таны мэйл сервер уншсан төлөв, од эсвэл шошгоор эрэмбэлэхийг дэмждэггүй. Эдгээр түвшнийг алгасаж, жагсаалт он цагийн дарааллаараа үлдэнэ." + }, + "title": "Гадаад төрх", + "description": "Вэбмэйлийнхээ харагдах байдал, мэдрэмжийг өөрчилнө", + "theme": { + "label": "Сэдэв", + "description": "Өөрийн хүссэн өнгөний загвараа сонго", + "light": "Гэрэл", + "dark": "Харанхуй", + "system": "Систем" + }, + "language": { + "label": "Хэл", + "description": "Та дуртай хэлээ сонгоно уу" + }, + "font_size": { + "label": "фонтын хэмжээ", + "description": "Текстийн хэмжээг илүү сайн уншихын тулд тохируулна уу", + "small": "Жижиг", + "medium": "Дунд", + "large": "Том" + }, + "list_density": { + "label": "Нягт", + "description": "UI-ийн хоорондох зай болон дэвсгэрийг хянах", + "extra_compact": "Нэмэлт авсаархан", + "compact": "Компакт", + "regular": "Тогтмол", + "comfortable": "Тав тухтай" + }, + "interface_layout": { + "label": "Интерфейсийн байрлал", + "description": "Хайлт, навигаци, хэрэгслийн мөр хаана байрлах", + "default": "Үндсэн", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Анимацийг идэвхжүүлэх", + "description": "Гөлгөр шилжилт, эффектийг харуулах" + }, + "toolbar_position": { + "label": "Хэрэгслийн мөрний байрлал", + "description": "И-мэйл үйлдлийн товчлууруудыг (Хариулах, Архивлах, Устгах гэх мэт) хаана харуулах вэ", + "top": "Шилдэг", + "below_subject": "Сэдвийн доор" + }, + "toolbar_labels": { + "label": "Хэрэгслийн самбарын шошгыг харуулах", + "description": "Текст шошгыг багаж самбарын дүрсний хажууд харуул. Дүрстэй танилцсаны дараа зай хэмнэхийн тулд идэвхгүй болгоно уу." + }, + "hide_account_switcher": { + "label": "Sidebar Account Switcher-ийг нуух", + "description": "Хавтрын хажуугийн дээд хэсэгт байрлах бүртгэл сонгогчийг нуу. Та доод навигацийн төмөр замаас бүртгэлээ сольж болно." + }, + "show_rail_account_list": { + "label": "Навигацийн төмөр зам дээр дансны аватаруудыг харуулах", + "description": "Хурдан сэлгэхийн тулд навигацийн төмөр замын доод хэсэгт бүртгэлээс гарах товчийг ашиглан хувийн дансны хүрээг харуул." + }, + "unified_mailbox": { + "label": "Нэгдсэн шуудангийн хайрцаг", + "description": "Идэвхтэй акаунт болон түүний хуваалцсан фолдеруудын нэгдсэн фолдеруудыг (Ирсэн имэйл, Илгээсэн гэх мэт) харуулах.", + "cross_account": { + "label": "Бүх дансаар", + "description": "Идэвхтэй акаунтад үлдэхийн оронд нэгдсэн шуудангийн хайрцгийг холбогдсон бүртгэл бүрт нэгтгэнэ үү." + }, + "include_group": { + "label": "Групп ирсэн имэйлийг оруулах", + "description": "Мөн хуваалцсан/бүлэглэсэн ирсэн имэйлийг нэгдсэн харагдац болгон нэгтгэнэ үү." + } + }, + "all_mail": { + "folders_label": "Нэгдсэн жагсаалт дахь хавтаснууд", + "folders_description": "Энэ бүртгэлийн фолдеруудын аль нь Бүх имэйл / Уншаагүй / Одтой жагсаалтад нэгтгэгдэхийг сонгоно уу.", + "account_hint": "{account}.", + "no_folders": "Полдер байхгүй." + }, + "colorful_sidebar_icons": { + "label": "Хажуугийн өнгөт дүрсүүд", + "description": "Tint хавтас болон тэмдэглэгээг төрлөөр нь (цэнхэр Ирсэн хайрцаг, улаан хог, ногоон Илгээсэн гэх мэт). Монохром хажуугийн самбарыг идэвхгүй болгох." + }, + "tint_list_rows": { + "label": "Тагийн өнгөөр эгнээнүүдийг будах", + "description": "Мессежийн мөр бүрийг эхний шошгоны өнгөөр сүүдэрлээрэй. Мөрүүдийг энгийн байлгахын тулд идэвхгүй болгох; шошгоны цэгүүд болон чипүүд нь өнгийг харуулсан хэвээр байна." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Нийт мессежийн тоог харуулах", + "description": "Нийт мессежийн тоог хавтас, шошгоны хажууд уншаагүй тооны хажууд харуул. Зөвхөн уншаагүй тоонуудыг харуулахын тулд идэвхгүй болго." + }, + "favicon_unread_badge": { + "label": "Таб дээрх уншаагүй тооны дүрс", + "description": "Ирсэн имэйлийн уншаагүй тоог хөтчийн табын дүрс дээр тэмдэг болгон харуулснаар таб анхаарлаа төвлөрүүлээгүй үед шинэ имэйл харагдах болно. Дүрс тэмдгийг тэмдэггүй байлгахын тулд идэвхгүй болгоно уу." + }, + "show_shared_mail_accounts": { + "label": "Хуваалцсан шуудангийн бүртгэлийг харуулах", + "description": "Тантай хуваалцсан шуудангийн хайрцгийн хавтаснуудыг хажуугийн самбарын \"Хуваалцсан\" хэсэгт харуулна. Бүгдийг нуухын тулд унтраана уу, эсвэл доор тус тусад нь нууна уу. Тэдний хуанли, харилцагчид хэвээр үлдэнэ." + }, + "pro_interface": { + "label": "Pro интерфейс (туршилтын)", + "description": "Зөвхөн ширээний компьютерт зориулагдсан, олон цонхтой мессеж үзэх, данс хоорондын ажлын урсгалтай. Стандарт интерфэйс нь нөлөөлөлд өртөөгүй; та хүссэн үедээ буцааж сольж болно.", + "open_label": "Нээлттэй Pro интерфэйс", + "back_to_standard": "Стандарт руу буцах" + }, + "cross_unread": { + "label": "Уншаагүй", + "description": "Сонгосон фолдерууд дээрх уншаагүй захидлын жагсаалтыг нэгдсэн шуудангийн хайрцгийн уншаагүй бичилтийг харуулах." + }, + "cross_starred": { + "label": "Одтой", + "description": "Сонгосон фолдерууд дээр дарцагласан/одтой захидлын жагсаалтын нэгдсэн шуудангийн хайрцгийн одтой оруулгыг харуулах." + }, + "cross_all": { + "label": "Бүх шуудан", + "description": "Нэгдсэн шуудангийн хайрцганд сонгогдсон фолдер дээрх бүх имэйлийг жагсаасан Бүх имэйлийн оруулгыг харуул." + } + }, + "keywords": { + "title": "Имэйл хаягууд", + "description": "Имэйлээ цэгцлэх шошгуудыг тодорхойл. Эдгээр нь сервер дээр JMAP түлхүүр үг хэлбэрээр хадгалагддаг.", + "add_keyword": "Таг нэмэх", + "label_field": "Дэлгэцийн нэр", + "label_placeholder": "жишээ нь Ажил, Хувийн, Яаралтай", + "id_field": "Тагийн ID", + "id_placeholder": "жишээ нь. ажил, хувийн", + "color_field": "Өнгө", + "id_exists": "Энэ хаягийн ID аль хэдийн байна", + "edit": "Таг засах", + "delete": "Тагыг устгах", + "save": "Хадгалах", + "add": "Нэмэх", + "cancel": "Цуцлах", + "migrating": "Одоо байгаа имэйлүүд дээрх шошгыг шинэчилж байна...", + "migration_error": "Одоо байгаа имэйлийн шошгыг шинэчилж чадсангүй", + "filters_migration_error": "Зарим шүүлтүүрийн дүрэм хуучин шошгыг ашигласаар байна — гараар шинэчилнэ үү", + "nesting": { + "label": "Оруулсан шошго", + "description": "Тагуудыг бусад шошгоны доор байрлуулж, хажуугийн самбарт мод хэлбэрээр харуул." + }, + "parent_field": "Эцэг эхийн шошго", + "no_parent": "Эцэг эх байхгүй", + "too_long": "Энэ хаягийн зам хэт урт байна (хамгийн ихдээ {max} тэмдэгт)", + "has_children_locked": "Бусад шошгуудыг үүний доор байрлуулсан тул түүний нэр болон эцэг эх нь түгжигдсэн байна. Эхлээд зөөж эсвэл устгана уу.", + "has_children_delete": "Эхлээд үүний доор байрлуулсан шошгыг устгана уу", + "visibility_field": "Хажуугийн самбарын харагдах байдал", + "visibility": { + "show": "Үзүүлэх", + "unread": "Уншаагүй бол харуулах", + "hide": "Нуух" + }, + "unrecognized": { + "title": "Серверээс хаягуудыг сэргээх", + "description": "Тагийн нэр, өнгө нь энэ хөтөч дээр хадгалагддаг боловч шошгууд нь JMAP түлхүүр үг болгон таны мессеж дээр байдаг. Энд ямар ч шошго тайлбарлаагүй түлхүүр үгсийг хайж олохын тулд имэйлээ сканнердаж, дараа нь буцааж нэмнэ үү.", + "scan": "Таг хайх", + "rescan": "Дахин скан хийх", + "scanning": "Скан хийж байна… {total} мессежийн {scanned}", + "none": "Таны мессеж дээрх шошго бүр энд тодорхойлогдсон байна.", + "messages": "{count, plural, one {# мессеж} other {# мессеж}}", + "add_all": "Бүх {count}", + "partial": "Зөвхөн {scanned} хамгийн сүүлийн үеийн {total} мессежийг сканнердсан. Зөвхөн хуучин имэйлд ашигласан шошго байхгүй байж магадгүй.", + "error": "Таны имэйлийг шошго хайж чадсангүй." + } + }, + "notifications": { + "test_sound": "Туршилтын мэдэгдлийн дуу", + "sounds": { + "default": "Өгөгдмөл (Дохио)", + "cheerful": "Хөгжилтэй", + "involved": "Оролцсон", + "swift": "Шуурхай зангаа", + "relax": "Амрах" + }, + "push": { + "title": "Арын мэдэгдлүүд", + "description": "Энэ сайт хаагдсан үед шинэ имэйлийн системийн мэдэгдлийг хүлээн авах. Bulwark түлхэх буухиагаар дамжуулан хүргэгдсэн; реле хэзээ ч захидлын агуулгыг хардаггүй.", + "relay_label": "Түлхэх реле", + "relay_desc": "Байршуулсан Bulwark буухиа өгөгдмөл. Зөвхөн өөрөө зохион байгуулж байгаа тохиолдолд л өөрчилнө үү.", + "relay_locked": "Админ тохируулсан", + "relay_locked_desc": "Түлхэх реле URL-г таны админ тохируулсан бөгөөд өөрчлөх боломжгүй.", + "status_active": "Энэ төхөөрөмжид идэвхтэй байна", + "status_inactive": "Энэ төхөөрөмж дээр идэвхжээгүй", + "status_unsupported": "Энэ хөтөч Web Push-ийг дэмждэггүй", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "Ажиллаж байна…", + "enable": "Идэвхжүүлэх", + "reenable": "Дахин бүртгүүлэх", + "disable": "Идэвхгүй болгох", + "confirm_disable_title": "Арын мэдэгдлийг идэвхгүй болгох уу?", + "confirm_disable_message": "Сайтыг хаах үед энэ төхөөрөмж дохио хүлээн авахаа болино.", + "dismiss_aria": "Мэдэгдэл хүлээх мөрийг хаах", + "ios_hint": "iOS дээр эхлээд сайтаа үндсэн дэлгэц дээрээ суулгаарай - Safari нь зөвхөн суулгасан PWA-д Web Push-г хүргэдэг.", + "devices_title": "Бүртгэлтэй төхөөрөмжүүд", + "devices_desc": "Энэ бүртгэлийн араарх мэдэгдлийг хүлээн авахаар бүртгэгдсэн бүх төхөөрөмж. Цуцлахад тухайн төхөөрөмж шууд хүлээн авахаа болино.", + "devices_loading": "Бүртгэлтэй төхөөрөмжүүдийг ачаалж байна…", + "devices_empty": "Араарх мэдэгдэлд бүртгэгдсэн төхөөрөмж алга.", + "devices_error": "Бүртгэлтэй төхөөрөмжүүдийг ачаалж чадсангүй.", + "devices_refresh": "Шинэчлэх", + "device_this": "Энэ төхөөрөмж", + "device_other": "Өөр төхөөрөмж", + "device_status_active": "Мэдэгдэл хүргэж байна", + "device_status_inactive": "Хэзээ ч мэдэгдэл хүргээгүй", + "device_status_unknown": "Энэ реле танихгүй", + "device_expires": "{date}-нд дуусна", + "revoke": "Цуцлах", + "confirm_revoke_title": "Энэ төхөөрөмжийг цуцлах уу?", + "confirm_revoke_message": "Тэр даруй араарх мэдэгдэл хүлээн авахаа болино. Тухайн төхөөрөмж дээр push-ыг дахин асаавал дахин бүртгэгднэ.", + "confirm_revoke_message_this": "Энэ төхөөрөмж тэр даруй араарх мэдэгдэл хүлээн авахаа болино." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Мэдэгдэлийн дуу", + "description": "Мэдэгдэл авахын тулд ямар дууг тоглуулахаа сонгоно уу", + "choose": "Дуу", + "choose_desc": "Мэдэгдэлийн аяыг сонгоод чанга яригчийн дүрс дээр дарж урьдчилан үзэх" + }, + "email": { + "title": "И-мэйл мэдэгдэл", + "description": "Ирж буй имэйлийн мэдэгдлийг тохируулах", + "enabled": "И-мэйл мэдэгдэл", + "enabled_desc": "Шинэ имэйл ирэх үед мэдэгдлийг харуулах", + "sound": "Мэдэгдэлийн дуу", + "sound_desc": "Шинэ имэйл ирэх үед аудио дохиог тоглуулах", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Календарийн мэдэгдэл", + "description": "Хуанлийн арга хэмжээний мэдэгдлийг тохируулах", + "enabled": "Үйл явдлын мэдэгдэл", + "enabled_desc": "Удахгүй болох хуанлийн арга хэмжээний тухай сэрэмжлүүлэг харуулах", + "sound": "Мэдэгдэлийн дуу", + "sound_desc": "Хуанли сануулагчийн аудио дохиог тоглуулах", + "invitation_parsing": "Имэйл урилгыг задлан шинжлэх", + "invitation_parsing_desc": "Имэйл хавсралтаас хуанлийн урилгыг илрүүлж, календарийн үйлдлийг харуулах" + } + }, + "language_region": { + "title": "Хэл, Бүс, Цаг", + "description": "Хэл, огнооны формат, цагийн формат болон бусад бүс нутгийн тохиргоо", + "language": { + "label": "Хэл", + "description": "Та дуртай хэлээ сонгоно уу", + "english": "Англи", + "french": "Франсаис" + }, + "date_format": { + "label": "Огнооны формат", + "description": "Имэйл жагсаалтад огноог хэрхэн харуулах вэ", + "smart": "Ухаалаг", + "relative": "Харилцан", + "full": "Үргэлж бүтэн болзоо", + "preview_today": "Өнөөдөр:", + "preview_this_week": "Энэ долоо хоногт:", + "preview_older": "Хөгшин:" + }, + "date_locale": { + "label": "Огнооны форматын бүс", + "description": "Тоон огноог хэрхэн эрэмбэлэх вэ (өдөр, сар, жил)", + "auto": "Автомат", + "iso": "ISO", + "dmy": "Өдөр/Сар/Жил", + "mdy": "Сар/Өдөр/Жил" + }, + "time_format": { + "label": "Цагийн формат", + "description": "12 эсвэл 24 цагийн аль нэгийг сонгох", + "12h": "12 цаг", + "24h": "24 цагийн" + }, + "first_day": { + "label": "Долоо хоногийн эхний өдөр", + "description": "Долоо хоногийн Ням эсвэл Даваа гарагт эхэлнэ", + "sunday": "Ням гараг", + "monday": "Даваа гараг" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Хавтас солих үед хайлтыг арилгах", + "description": "Хавтас дээр дарахад (Ирсэн, Илгээсэн…) идэвхтэй хайлт арилж, тухайн хавтас нээгдэнэ. Унтраасан үед хайлт хадгалагдаж, шилжсэн хавтаст дахин ажиллана." + }, + "request_read_receipt": { + "label": "Анхдагчаар уншсан баримтыг хүсэх", + "description": "Шинэ мессеж бичихдээ уншсан хүлээн авах хүсэлтийг урьдчилан идэвхжүүлнэ." + }, + "read_receipt_response": { + "label": "Уншсан хүлээн авах хүсэлтэд хариу өгөх", + "description": "Ирж буй мессеж уншсан баримт асуухад яах вэ.", + "ask": "Тухайн бүрт нь асуух", + "always": "Үргэлж илгээх", + "never": "Хэзээ ч битгий илгээгээрэй" + }, + "title": "Имэйлийн зан байдал", + "description": "Имэйл хэрхэн ажиллахыг тохируулах", + "mark_read": { + "label": "Уншсан гэж тэмдэглэх", + "description": "Имэйлийг нээх үед уншсан гэж тэмдэглэх нь", + "instant": "Шууд", + "delay_3s": "3 секундын дараа", + "delay_5s": "5 секундын дараа", + "never": "Хэзээ ч" + }, + "delete_action": { + "label": "Устгах үйлдэл", + "description": "Та имэйл устгахад юу болдог вэ", + "trash": "Хогийн сав руу зөөх", + "trash_and_read": "Хогийн сав руу зөөж уншсан гэж тэмдэглээрэй", + "permanent": "Бүрмөсөн устгах", + "warning": "И-мэйл бүрмөсөн устах ба сэргээх боломжгүй. Энэ үйлдэл нь эргэлт буцалтгүй." + }, + "message_spacing": { + "label": "Мессеж хоорондын зай", + "description": "Уншигч доторх мессежийн хэсгийг тойруулан дүүргэх", + "auto": "Авто (санал болгож байна)", + "always": "Үргэлж хоорондын зайг нэмэх", + "edge": "Ирмэгээс ирмэг хүртэл" + }, + "plain_text_font": { + "label": "Энгийн текстийн фонт", + "description": "Энгийн текстэн имэйлд ашиглах фонт", + "mono": "Тогтмол өргөнтэй", + "sans": "Энгийн (апптай ижил)" + }, + "archive_mode": { + "label": "Архив", + "description": "Архивлахдаа имэйлийг хэрхэн зохион байгуулах вэ", + "single": "Нэг хавтас", + "year": "Жилд хавтас", + "month": "Сард нэг хавтас", + "reorganize": "Одоо байгаа архивыг дахин цэгцлэх", + "reorganize_success": "{count, plural, =0 {Дахин зохион байгуулах имэйл байхгүй} =1 {1 имэйлийг дахин зохион байгуулсан} other {# имэйлийг дахин зохион байгууллаа}}", + "reorganize_error": "Архивыг дахин зохион байгуулж чадсангүй" + }, + "permanently_delete_junk": { + "label": "Хог хаягдлыг бүрмөсөн устгах", + "description": "Имэйлийг Хогийн сав руу зөөхийн оронд Хогийн/Спам хавтсаас бүрмөсөн устгах" + }, + "confirm_bulk_delete": { + "label": "Олон захиа устгахаас өмнө баталгаажуулах", + "description": "Сонгосон олон захиаг хогийн сав руу зөөхөөс өмнө асуух. Бүрмөсөн устгах үед үргэлж асууна." + }, + "mail_layout": { + "label": "Мэйлийн зохион байгуулалт", + "description": "Сонгодог хуваах унших самбар, Gmail маягийн төвлөрсөн унших урсгал эсвэл Зимбра маягийн доод унших самбараас сонгоно уу.", + "split": "Хуваах самбар", + "split_description": "Мессежийн жагсаалт болон унших самбарыг зэрэгцүүлэн харагдуулах.", + "focus": "Төвлөрсөн жагсаалт", + "focus_description": "Мессеж болгонд нэг мөр үзүүлж, хавтасны хажуугийн самбарыг харагдуулахын зэрэгцээ имэйлийг бүхэлд нь нээнэ үү.", + "horizontal": "Доор талд Унших самбар", + "horizontal_description": "Дээр талд мессежийн жагсаалтыг үзүүлж, доор нь унших хэсэгт сонгосон мессежийг нээнэ үү." + }, + "list_columns": { + "label": "Жагсаалтыг баганаар харуулах", + "description": "Нэг мөрт мөрүүдийг толгойн доор Илгээгч, Гарчиг, Огноо, Хэмжээ баганатай хүснэгтээр харуулна." + }, + "show_preview": { + "label": "Урьдчилан үзэх текстийг харуулах", + "description": "Жагсаалтад имэйл урьдчилан харахыг харуулах", + "focus_description": "Анхааруулсан нэг мөрт мессежийн жагсаалт доторх мөрний урьдчилан харах текстийг харуулах" + }, + "show_verification_codes": { + "label": "Баталгаажуулах кодыг харуулах", + "description": "Нэвтрэх болон баталгаажуулах имэйлийн кодыг нэг товшилтоор хуулах боломжтой болгох: жагсаалтад нэг өдөр, мессеж дотор" + }, + "disable_threading": { + "label": "Харилцааны бүлэглэлийг идэвхгүй болгох", + "description": "Имэйлүүдийг харилцан яриагаар бүлэглэхийн оронд тус тусад нь мессеж хэлбэрээр харуулах" + }, + "plain_text_mode": { + "label": "Зөвхөн энгийн текст", + "description": "Баян текст засварлагчийг идэвхгүй болгож, хариу болон дамжуулалт зэрэг бүх имэйлийг зөвхөн энгийн текст хэлбэрээр илгээх боломжтой" + }, + "auto_select_reply_identity": { + "label": "Хүлээн авсан хаягаас хариу бичих", + "description": "Хариу бичихдээ мессежийг анх илгээсэн хаягаар нь илгээнэ үү. Эхлээд таних тэмдэгтэй таарах; Домэйн бүх хүргэлтийн хувьд, таны үндсэн таниулбараар дамжуулан илгээхдээ From толгой хэсгийг өөр нэр рүү дахин бичнэ." + }, + "reply_identity_match": { + "label": "Хүлээн авах хаягийн тохирол", + "description": "Хүлээн авах ямар хаягуудыг таных гэж үзэх. „Зөвхөн яг таарах хаяг“ нь зөвхөн таны тохируулсан identity-уудаас сонгоно; „ижил домэйн“ нь таны identity-ийн домэйн дээрх бусад хаягийг ч catch-all нэр болгон үзэж, Илгээгч толгойг тэр хаягаар солино. Эдгээр хаяг нь түгээлтийн жагсаалт бол яг таарах хаягийг сонгоно уу.", + "exact": "Зөвхөн яг таарах хаяг", + "domain": "Миний домэйн дээрх дурын хаяг" + }, + "signature_position": { + "label": "Гарын үсэг зурах албан тушаал", + "description": "Хариулт болон дамжуулалтад гарын үсгээ хаана оруулах вэ. Иш татсан бичвэрийн дээрх хариултыг хаах нь ойлгомжтой; доор нь эх мессежийг залгаж хадгална.", + "above_quote": "Ишлэлээс өмнө", + "below_quote": "Иш иш татсаны дараа" + }, + "signature_separator": { + "label": "Гарын үсэг хязгаарлагч", + "description": "Гарын үсгийн угтварыг стандарт \"--\" хязгаарлагч шугамаар (RFC 3676) оруулна. Хэрэв та мессежээсээ шууд гарын үсэг рүү шилжихийг хүсвэл унтраа." + }, + "sub_address_delimiter": { + "label": "Дэд хаяг хязгаарлагч", + "description": "Хэрэглэгчийн нэрийг дэд хаягийн шошгоос тусгаарлах тэмдэгт. Таны мэйл сервер ашигладаг хязгаарлагчтай тааруулна уу (жишээ нь user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "Захиалгат…", + "custom_input_label": "Захиалгат хязгаарлагч тэмдэгт" + }, + "attachment_click_action": { + "label": "Хавсралт дарах үйлдэл", + "description": "Файлын хавсралт дээр дарснаар урьдчилан харах эсвэл шууд татаж авах эсэхээ сонгоно уу", + "preview": "Боломжтой бол урьдчилан харах", + "download": "Шууд татаж авах" + }, + "attachment_position": { + "label": "Хавсралтын байрлал", + "description": "Имэйл толгой хэсэгт хавсралтуудыг хаана харуулах вэ", + "beside-sender": "Илгээгчийн хажууд", + "below-header": "Толгойн доор" + }, + "emails_per_page": { + "10": "10 имэйл", + "25": "25 имэйл", + "50": "50 имэйл", + "100": "100 имэйл", + "label": "Нэг хуудасны имэйл", + "description": "Нэг удаад ачаалах имэйлийн тоо" + }, + "always_light_mode": { + "label": "И-мэйлүүдийг үргэлж гэрэлтэй горимоор харуулах", + "description": "Апп нь харанхуй горимд байсан ч харанхуй горимд хөрвүүлэхтэй холбоотой асуудлаас зайлсхийж, имэйлийн агуулгыг цайвар горимд үзүүлээрэй" + }, + "external_content": { + "label": "Гадаад контент", + "description": "Зураг болон гадаад контенттой хэрхэн ажиллах талаар", + "ask": "Үргэлж асуух", + "block": "Үргэлж блоклох", + "allow": "Үргэлж зөвшөөрөх" + }, + "trusted_senders": { + "label": "Итгэмжлэгдсэн илгээгчид", + "description": "Зураг нь автоматаар ачаалагддаг илгээгчийг удирдах", + "count_zero": "Байхгүй", + "count_one": "1 илгээгч", + "count_other": "{count} илгээгч", + "modal_title": "Итгэмжлэгдсэн илгээгчид", + "empty_title": "Одоохондоо итгэмжлэгдсэн илгээгч алга", + "empty_description": "Хориглогдсон зураг бүхий имэйлийг үзэхдээ \"Энэ илгээгчид үргэлж итгэ\" дээр дарж тэдгээрийг энд нэмнэ үү.", + "add_manually": "Илгээгчийг гараар нэмэх", + "add_button": "Нэмэх", + "add_placeholder": "Имэйл хаягаа оруулна уу", + "search_placeholder": "Илгээгч хайх...", + "no_results": "Таны хайлтад тохирох илгээгч алга", + "remove": "Устгах", + "close": "Хаах", + "invalid_email": "Хүчинтэй имэйл хаяг оруулна уу", + "already_added": "Энэ илгээгч аль хэдийн итгэсэн", + "save_error": "Хадгалж чадсангүй - дэлгэрэнгүй мэдээллийг Харилцагчийн дибаг хийх бүртгэлээс шалгана уу", + "use_address_book_label": "Хаягийн дэвтэртэй синк хийх", + "use_address_book_description": "Итгэмжлэгдсэн илгээгчдийг \"Итгэмжлэгдсэн илгээгчид\" хаягийн дэвтэрт хадгалснаар таны бүх төхөөрөмжид синк хийнэ" + }, + "hover_actions": { + "label": "Хурдан зөөвөрлөх үйлдэл", + "description": "Жагсаалтад байгаа имэйл дээр гүйлгэх үед гарч ирэх хурдан үйлдлүүдийг сонгох", + "delete": "Устгах", + "star": "Од / Одгүй", + "mark_read": "Уншсан / Уншаагүй гэж тэмдэглэнэ", + "archive": "Архив", + "tag": "Таг", + "spam": "Спам гэж тэмдэглэх", + "not_spam": "Спам биш", + "none_selected": "Ямар ч үйлдэл сонгоогүй", + "mode_label": "Дэлгэцийн горим", + "mode_inline": "Шугаман", + "mode_floating": "Хөвөгч", + "corner_label": "Хөвөгч байрлал", + "corner_top-left": "Зүүн дээд", + "corner_top-right": "Баруун дээд", + "corner_bottom-left": "Зүүн доод", + "corner_bottom-right": "Баруун доод" + }, + "default_mail_program": { + "label": "Өгөгдмөл шуудангийн програм", + "description": "{appName}-г mailto: холбоос нээх өгөгдмөл имэйл программаар бүртгүүлнэ.", + "button": "Өгөгдмөлөөр тохируулах", + "success": "Хөтчөөс анхдагчаар тохируулахыг сануулсан", + "error": "Таны хөтөч энэ функцийг дэмждэггүй" + }, + "attachment_reminder": { + "label": "Хавсралт сануулагч", + "description": "Таны зурваст хавсралт дурдсан боловч хавсралт байхгүй үед илгээхээсээ өмнө анхааруулаарай", + "keywords_label": "Триггер түлхүүр үгс", + "keywords_description": "Таны зурвасаас олдсон сануулагчийг өдөөх үг, хэллэгүүд", + "add_placeholder": "Түлхүүр үг нэмэх...", + "add": "Нэмэх", + "remove": "Устгах" + }, + "empty_subject_warning": { + "label": "Хоосон сэдвийн анхааруулга", + "description": "Гарчиггүй мессеж илгээхээсээ өмнө баталгаажуулахыг хүс." + }, + "recipient_mentions": { + "label": "Хүлээн авагчдыг @-ээр дурдах", + "description": "Захидалд @ бичихэд хүлээн авагчдыг санал болгож, сонгосон хүний нэрийг оруулна. Enter эсвэл Tab тодруулсан нэрийг сонгоно." + }, + "mention_adds_recipient": { + "label": "@ дурдалтад харилцагчид", + "description": "@ бичихэд таны харилцагчид болон лавлахын хүмүүс мөн санал болгогдоно. Хүлээн авагч биш хүнийг сонговол түүнийг захидалд нэмнэ.", + "to": "Хэнд-д нэмэх", + "cc": "CC-д нэмэх", + "off": "Зөвхөн хүлээн авагчид" + }, + "hide_inline_image_attachments": { + "label": "Хавсралтаас дотортой зургийг нуух", + "description": "Зурвасны үндсэн хэсэгт оруулсан зургуудыг тусад нь хавсралт хэлбэрээр оруулаагүй болно" + }, + "attachment_image_previews": { + "label": "Хавсралтаас зургийн урьдчилан харахыг харуулах", + "description": "Зургийн хавсралтыг ерөнхий файлын дүрсийн оронд өнгөц зургийн карт болгон гаргах" + }, + "send_delay": { + "label": "Илгээх / илгээх саатлыг буцаах", + "description": "Сервер талын богино цонхоор илгээх хэвийн саатал.", + "off": " Унтраах", + "seconds": "{seconds} секунд", + "unsupported": "Харин данс нь хойшлуулсан-илгээлтийн дэмжлэгийг сурталчилдаггүй. Тохиргоог бусад бүртгэлд хадгалсан хэвээр байна." + }, + "return_to_list_after_action": { + "label": "Устгасны дараа жагсаалт руу буцах эсвэл уншаагүй гэж тэмдэглэнэ", + "description": "Нээлттэй мессежийг устгасан эсвэл уншаагүй гэж тэмдэглэсний дараа дараагийн мессежийг нээхийн оронд мессежийн жагсаалт руу буцна уу." + }, + "swipe_right_action": { + "label": "Баруун тийш шудрах үйлдэл (гар утас)", + "description": "Мэдрэгчтэй төхөөрөмж дээр жагсаалт дахь мессежийг баруун тийш шудрахад гүйцэтгэгдэх үйлдэл." + }, + "swipe_left_action": { + "label": "Зүүн тийш шудрах үйлдэл (гар утас)", + "description": "Мэдрэгчтэй төхөөрөмж дээр жагсаалт дахь мессежийг зүүн тийш шудрахад гүйцэтгэгдэх үйлдэл." + }, + "swipe_actions": { + "none": "Байхгүй", + "archive": "Архивлах", + "delete": "Устгах", + "mark_read": "Уншсан төлөвийг сэлгэх", + "star": "Одыг сэлгэх", + "spam": "Спам гэж тэмдэглэх" + }, + "rtl_editing": { + "label": "Баруунаас зүүн тийш засварлах дэмжлэг", + "description": "Бичих цонхны хэрэгслийн самбарт чиглэлийн товчлуур нэмснээр та догол мөрүүдийг зүүнээс баруун тийш эсвэл баруунаас зүүн тийш тохируулах боломжтой" + } + }, + "composer": { + "title": "Бичих цонх", + "description": "Имэйл бичих тохиргоог тохируулах", + "autosave": { + "label": "Автоматаар хадгалах интервал", + "description": "Ноорог автоматаар хэр олон удаа хадгалах вэ", + "30s": "30 секунд тутамд", + "1m": "Минут тутам", + "2m": "2 минут тутамд", + "5m": "5 минут тутамд" + }, + "send_confirmation": { + "label": "Баталгаажуулах", + "description": "Имэйл илгээхээсээ өмнө баталгаажуулахыг хүсэх" + }, + "default_reply": { + "label": "Өгөгдмөл хариу бичих горим", + "description": "Хариулах товчийг дарах үндсэн үйлдэл", + "reply": "Хариулах", + "reply_all": "Бүгдэд нь хариулах" + } + }, + "privacy": { + "title": "Нууцлал ба Аюулгүй байдал", + "description": "Нууцлал болон аюулгүй байдлын тохиргоогоо удирдах", + "external_images": { + "label": "Гадаад зургийг блоклох", + "description": "Гадны зургаар мөрдөхөөс урьдчилан сэргийлэх" + }, + "session_timeout": { + "label": "Хугацаа хэтэрсэн", + "description": "Идэвхгүй болсны дараа автоматаар гарах", + "never": "Хэзээ ч", + "30m": "30 минут", + "1h": "1 цаг", + "4h": "4 цаг" + }, + "clear_cache": { + "label": "Кэшийг арилгах", + "description": "Кэшд хадгалагдсан өгөгдөл болон түр файлуудыг устгах", + "button": "Кэшийг арилгах", + "confirm": "Та кэшийг цэвэрлэхдээ итгэлтэй байна уу?", + "success": "Кэшийг амжилттай цэвэрлэв" + } + }, + "account": { + "title": " Данс", + "description": "Өөрийн дансны мэдээллийг харах", + "name_label": "Дэлгэцийн нэр", + "username_label": "Хэрэглэгчийн нэр", + "account_type_label": "Дансны төрөл", + "auth_method_label": "Баталгаажуулалт", + "auth_method_oauth": "Нэг удаа нэвтрэх (OAuth/OIDC)", + "auth_method_basic": "Нууц үг", + "auth_method_token": "Хандалтын токен", + "demo_account": "Демо данс", + "email": { + "label": "Имэйл хаяг", + "value": "{email}" + }, + "server": { + "label": "JMAP сервер", + "value": "{server}" + }, + "storage": { + "label": "Хадгалалтын ашиглалт", + "used": "{total}-н {used} ашигласан", + "percentage": "{percent}% ашигласан" + }, + "last_sync": { + "label": "Сүүлийн синк", + "value": "{time}" + }, + "accounts": { + "title": "Нэвтэрсэн бүртгэлүүд", + "description": "Бүртгэл унадаг цэсэнд бүртгэлүүд хэрхэн харагдахыг өөрчлөхийн тулд чирнэ үү", + "active": "Одоогоор идэвхтэй байгаа данс", + "default_badge": "Үндсэн данс", + "set_default": "Өгөгдмөлөөр тохируулах", + "switch_to": "Энэ данс руу шилжих", + "move_up": "Дээш хөдлөх", + "move_down": "Доошоо шилжих", + "drag_handle": "Дахин эрэмбэлэхийн тулд чирнэ үү", + "add": "Бүртгэл нэмэх" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Надтай хуваалцсан", + "description": "Бүлэг болон хуваалцсан акаунтуудыг удирдах боломжтой. Шүүлтүүр, амралтын хариулагч, хуанли, харилцагчдыг засахын тулд нэгийг нь сонгоно уу.", + "shared_label": "Хуваалцсан бүртгэл" + } + }, + "scoped": { + "back": "Миний данс руу буцах", + "managing": "Удирдлага: {name}" + }, + "security": { + "title": "Дансны аюулгүй байдал", + "description": "Нууц үг, хоёр хүчин зүйлийн баталгаажуулалт, аюулгүй байдлын тохиргоогоо удирдах", + "detecting": "Серверийн чадварыг илрүүлж байна...", + "not_available": "Энэ имэйл сервер дээр бүртгэлийн аюулгүй байдлын удирдлага ашиглах боломжгүй. Шаардлагатай зөвшөөрлүүд идэвхгүй байж болно. Дэлгэрэнгүйг баримт бичгээс үзнэ үү.", + "password": { + "title": "Нууц үг солих", + "current": "Одоогийн нууц үг", + "new": "Шинэ нууц үг", + "confirm": "Шинэ нууц үгээ баталгаажуулах", + "submit": "Нууц үг солих", + "success": "Нууц үг амжилттай өөрчлөгдсөн", + "error_title": "Нууц үг солигдож чадсангүй", + "error_mismatch": "Шинэ нууц үг таарахгүй байна", + "error_min_length": "Нууц үг хамгийн багадаа 8 тэмдэгт байх ёстой", + "error_generic": "Нууц үгээ сольж чадсангүй" + }, + "display_name": { + "label": "Дэлгэцийн нэр", + "description": "Таны сервер дээр байгаа нэр", + "placeholder": "Дэлгэцийн нэрээ оруулна уу", + "save": "Хадгалах", + "success": "Дэлгэцийн нэрийг шинэчилсэн", + "error": "Дэлгэцийн нэрийг шинэчилж чадсангүй" + }, + "totp": { + "section_title": "Хоёр хүчин зүйлийн баталгаажуулалт", + "label": "TOTP баталгаажуулалт", + "description": "Нэг удаагийн нууц үгээр аюулгүй байдлын нэмэлт давхаргыг нэмнэ үү", + "active": "Идэвхжүүлсэн", + "inactive": "Хөгжлийн бэрхшээлтэй", + "enabled": "Хоёр хүчин зүйлийн баталгаажуулалтыг идэвхжүүлсэн", + "disabled": "Хоёр хүчин зүйлийн баталгаажуулалтыг идэвхгүй болгосон", + "enable_error": "2FA", + "disable_error": "2FA", + "setup_instructions": "Энэ URL-г баталгаажуулагч програмдаа хуулна уу (Google Authenticator, Authy гэх мэт):", + "verification_code": "Баталгаажуулах код", + "confirm": "Баталгаажуулах", + "disable": "Идэвхгүй болгох", + "disable_confirm_prompt": "Хоёр хүчин зүйлийн баталгаажуулалтыг идэвхгүй болгохын тулд нууц үгээ оруулна уу.", + "password_required": "Нууц үг шаардлагатай", + "code_required": "Баталгаажуулах код шаардлагатай", + "code_invalid": "Баталгаажуулах код буруу байна. Authenticator програмаа шалгаад дахин оролдоно уу." + }, + "app_passwords": { + "title": "Аппын нууц үг", + "description": "Хоёр хүчин зүйлийн баталгаажуулалтыг дэмждэггүй програмуудад нууц үг үүсгэх", + "add": "Нэмэх", + "create": "Үүсгэх", + "cancel": "Цуцлах", + "done": "Дууссан", + "generate": "Үүсгэх", + "name_label": "Аппын нэр", + "name_placeholder": "жишээ нь. Thunderbird, iPhone Mail", + "expires_label": "Хугацаа нь дуусна (заавал биш)", + "allowed_ips_label": "Зөвшөөрөгдсөн IP (заавал биш)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Таслалаар эсвэл зайгаар тусгаарласан. Ямар ч IP зөвшөөрөхийн тулд хоосон орхино уу.", + "password_label": "Нууц үг (автоматаар үүсгэхийн тулд хоосон орхино уу)", + "password_placeholder": "Хоосон бол автоматаар үүсгэнэ", + "copy_now_warning": "Энэ нууц үгийг одоо хуулна - энэ нь дахиж харагдахгүй.", + "added": "Аппын нууц үгийг үүсгэсэн", + "removed": "Аппын нууц үгийг устгасан", + "add_error": "Аппын нууц үгийг үүсгэж чадсангүй", + "remove_error": "Аппын нууц үгийг устгаж чадсангүй", + "none": "Аппын нууц үгийг тохируулаагүй" + }, + "api_keys": { + "title": "API түлхүүрүүд", + "description": "Сервертэй шууд харьцдаг скрипт болон интеграцид зориулсан API түлхүүрүүдийг үүсгэх", + "name_label": "Түлхүүр нэр", + "name_placeholder": "жишээ нь. Нөөц скрипт, CI runner", + "copy_now_warning": "Энэ API түлхүүрийг одоо хуулна уу - энэ нь дахиж харагдахгүй.", + "added": "API түлхүүр үүсгэсэн", + "removed": "API түлхүүрийг устгасан", + "add_error": "API түлхүүр үүсгэж чадсангүй", + "remove_error": "API түлхүүрийг устгаж чадсангүй", + "none": " API түлхүүр тохируулаагүй" + }, + "encryption": { + "section_title": "Амарсан үед шифрлэлт", + "label": "И-мэйл шифрлэлт", + "description": "Нэмэлт нууцлалыг хадгалахын тулд сервер дээр хадгалагдсан имэйлийг шифрлээрэй", + "active": "{type} шифрлэлтийг идэвхжүүлсэн", + "inactive": "Хөгжлийн бэрхшээлтэй", + "enabled": "Амарсан үед шифрлэлтийг идэвхжүүлсэн", + "disabled_success": "Хамгийн үед шифрлэлт идэвхгүй болсон", + "error": "Шифрлэлтийн тохиргоог шинэчилж чадсангүй", + "algorithm_label": "Шифрлэлтийн алгоритм", + "encrypt_on_append": "Байршуулахдаа шинэ имэйлийг шифрлэх", + "allow_spam_training": "Имэйл шифрлэхээс өмнө спам сургалт явуулахыг зөвшөөрнө үү", + "enable_encryption": "Шифрлэлтийг идэвхжүүлэх", + "enable_tooltip": "Энэ түлхүүрээр шифрлэлтийг идэвхжүүлнэ", + "configure_title": "Энэ түлхүүрээр шифрлэлтийг идэвхжүүлнэ", + "enable_button": "Идэвхжүүлэх", + "disable_tooltip": "Шифрлэлтийг идэвхгүй болгох", + "enabled_success": "Амарсан үед шифрлэлт идэвхжсэн" + }, + "public_keys": { + "name_label": "Нийтийн түлхүүрийн нэр", + "name_placeholder": "жишээ нь. Миний түлхүүр", + "key_label": "Нийтийн түлхүүр (S/MIME эсвэл PGP ASCII хуягт)", + "key_placeholder": "Нийтийн түлхүүрээ энд буулгана уу", + "none": "Нийтийн түлхүүр тохируулаагүй" + }, + "email_client": { + "title": "И-мэйл үйлчлүүлэгчийн тохиргоо", + "description": "Эдгээр итгэмжлэлийг ашиглан ширээний компьютер эсвэл гар утасны имэйл клиентээ (Thunderbird, Apple Mail, Outlook гэх мэт) тохируулна уу", + "jmap_username_label": "JMAP хэрэглэгчийн нэр", + "copy": "Хуулбар", + "copied": "Хуулбарласан", + "password_instructions": "Имэйл клиентдээ нэвтрэхийн тулд дээрх JMAP хэрэглэгчийн нэрээ програмын нууц үгийн хамт ашиглана уу. Хэрэв та хийгээгүй бол дээрх хэсэгт програмын нууц үг үүсгэнэ үү." + }, + "link_device": { + "title": "Мобайл програмыг холбоно уу", + "description": "Утсан дээрх Bulwark Mail програмд нууц үгээ тэнд бичихгүйгээр нэвтэрнэ үү. Утас тань өөрийн гэсэн нэвтрэлттэй болох тул эндээс гарахад утаснаас гарахгүй.", + "generate": "QR код харуулах", + "regenerate": "Шинэ код харуулах", + "instructions": "Bulwark Mail програмыг нээгээд нэвтрэх дэлгэц дээрх \"Scan a sign-in code\" гэснийг товшино уу (хэрэв аль хэдийн нэвтэрсэн бол \"Add account\" → \"Scan a sign-in code\"), дараа нь камераа энд чиглүүлнэ үү.", + "expires_in": "Энэ кодын хугацаа {seconds} секундын дотор дуусна. Үүнийг зөвхөн нэг удаа ашиглах боломжтой.", + "expired": "Энэ кодын хугацаа дууссан.", + "error": "Хослуулах код үүсгэж чадсангүй. Дахин оролдоно уу.", + "password_prompt": "Та өөрөө гэдгээ баталгаажуулахын тулд нууц үгээ оруулна уу. Утас тань өөрийн гэсэн нэвтрэлттэй болно; таны нууц үг утас руу илгээгдэхгүй.", + "show_password": "Нууц үгийг харуулах", + "hide_password": "Нууц үгийг нуух", + "continue": "Үргэлжлүүлэх", + "error_wrong_password": "Нууц үг буруу байна. Дахин оролдоно уу.", + "totp_prompt": "Баталгаажуулах програм дахь кодыг оруулна уу.", + "error_password_or_code": "Нууц үг эсвэл код буруу байна.", + "error_not_signed_in": "Таны сессийн хугацаа дууссан. Төхөөрөмж холбохын тулд дахин нэвтэрнэ үү.", + "error_impersonation": "Өөр хэрэглэгчийн шуудангийн хайрцгийг үзэж байх үед төхөөрөмж холбох боломжгүй.", + "error_too_many_attempts": "Хэт олон оролдлого хийлээ. Түр хүлээгээд дахин оролдоно уу.", + "error_session_secret": "Энэ сервер дээр төхөөрөмж холбох тохиргоо хийгдээгүй байна (SESSION_SECRET алга). Администратортоо хандана уу.", + "error_server_unreachable": "Таны шуудангийн серверт холбогдож чадсангүй. Дахин оролдоно уу.", + "error_pairing_unavailable": "Таны шуудангийн сервер ийм аргаар төхөөрөмж холбохыг зөвшөөрдөггүй. Администратортоо хандана уу.", + "qr_alt": "Нэвтрэх QR код", + "link_label": "Нэвтрэх холбоос", + "copy_link": "Нэвтрэх холбоосыг хуулах", + "copied": "Хуулсан", + "copy_hint": "Камер байхгүй юу? Холбоосыг хуулаад програмын \"Paste sign-in link\" хэсэгт буулгана уу.", + "success_title": "Утсан дээрээ нэвтэрлээ", + "success_body": "Та энэ хуудсыг хааж болно. Өөр төхөөрөмж холбохын тулд шинэ код харуулна уу.", + "link_another": "Өөр төхөөрөмж холбох", + "use_sso": "Оронд нь нэгдсэн нэвтрэлт ашиглах", + "error_insecure_server": "Гар утасны апп зөвхөн https-ээр мэйл серверт холбогддог бөгөөд энэ сервер http ашиглаж байна." + } + }, + "identities": { + "title": "Танилцуулга илгээх", + "description": "Таны илгээх боломжтой имэйл хаягаа удирдах", + "identities_count": { + "label": "Таны таних тэмдэг", + "description": "Имэйл хаягуудыг илгээхээр тохируулсан", + "count_zero": "Хувийн мэдээлэл байхгүй", + "count_one": "1 таних тэмдэг", + "count_other": "{count} таних тэмдэг" + }, + "manage": "Identities удирдах", + "sub_addressing": { + "label": "Дэд хаяглалт", + "description": "Ирж буй имэйлийг цэгцлэхийн тулд user+tag@domain.com гэх мэт хаягуудыг ашиглана уу", + "learn_more": "Дэлгэрэнгүй үзэх" + } + }, + "vacation": { + "title": "Эзгүй байх", + "description": "Таныг эзгүй байхад ирж буй имэйлийг автоматаар боловсруулах", + "loading": "Амралтын тохиргоог ачаалж байна...", + "not_supported": "Таны мэйл сервер амралтын хариуг дэмждэггүй.", + "fetch_error": "Амралтын тохиргоог ачаалж чадсангүй. Дахин оролдоно уу.", + "status": { + "label": "Автоматаар хариулах", + "description": "Тан руу имэйл илгээсэн хүмүүст автоматаар хариу илгээх", + "active": "Идэвхтэй", + "inactive": "Идэвхгүй" + }, + "date_range": { + "start": "Эхлэх огноо", + "start_description": "Эхлэх хязгааргүй хоосон орхино", + "end": "Төгсгөлийн огноо", + "end_description": "Төгсгөлгүй хоосон орхино" + }, + "message": { + "subject_label": "Сэдэв", + "subject_description": "Автомат хариултын сэдвийн мөр", + "subject_placeholder": "Оффисоос гадуур", + "body_label": "Зурвасны үндсэн хэсэг", + "body_description": "Энгийн мессежийн агуулга", + "body_placeholder": "Имэйл илгээсэнд баярлалаа. Би одоогоор оффисгүй байгаа бөгөөд буцаж ирэхдээ хариулах болно.", + "html_label": "Форматлагдсан мессеж (HTML)", + "html_description": "Холбоос болон загвар бүхий баялаг, форматтай хувилбарыг нэмнэ үү. Мэйл үйлчлүүлэгч нь үүнийг харуулах боломжгүй хүлээн авагчид дээрх энгийн текст рүү буцна.", + "html_placeholder": "Оффисоос гадуур форматлагдсан хариу бичих..." + }, + "preview": { + "title": "Урьдчилан харах", + "show": "Урьдчилан үзэхийг харуулах", + "hide": "Урьдчилан харахыг нуух" + }, + "save": "Өөрчлөлтийг хадгалах", + "saving": "Хадгалж байна...", + "warnings": { + "end_before_start": "Төгсгөлийн огноо нь эхлэх огнооноос хойш байх ёстой", + "invalid_date": "Цагтай хүчинтэй эхлэх болон дуусах огноо оруулах эсвэл талбарыг хоосон үлдээнэ үү", + "start_in_past": "Эхлэх огноо өнгөрсөн байна", + "empty_body": "Зурвасны хэсэг хоосон байна - хүлээн авагчид хоосон хариу ирэх болно", + "subject_too_long": "Гарчиг сервертэй харьцуулахад хэт урт байна (дээд тал нь {max} байт).", + "body_too_long": "Захидал сервертэй харьцуулахад хэт урт байна (форматыг оруулаад дээд тал нь {max} байт).", + "forward_address": "Дамжуулах хүчинтэй хаяг оруулна уу", + "not_running": "Дамжуулалт эсвэл хариу хүлээн авагчид хадгалагдсан ч одоогоор идэвхгүй байна. Дахин идэвхжүүлэхийн тулд хадгална уу." + }, + "forward": { + "title": "Дамжуулалт", + "description": "Ирсэн мессежийг автомат хариутай эсвэл хариугүйгээр дээрх хугацаанд өөр хаяг руу дамжуулна (дуусах огноогүй бол унтраах хүртэл). Спамыг дамжуулахгүй.", + "enabled_label": "Мессеж дамжуулах", + "to_label": "Дамжуулах хаяг", + "to_placeholder": "email@example.com", + "keep_label": "Ирсэн имэйлд хадгалах", + "keep_description": "Эс бөгөөс мессежийг зөвхөн дамжуулна: энэ шуудангийн хайрцагт үлдэхгүй бөгөөд ямар ч шүүлтүүрийн дүрэм ажиллахгүй." + }, + "audience": { + "label": "Хариу илгээх", + "description": "Дотоод илгээгчид: {domains}", + "all": "Бүх илгээгчид", + "internal": "Зөвхөн дотоод илгээгчид", + "external": "Зөвхөн гадаад илгээгчид" + }, + "auto_reply": { + "title": "Автомат хариу" + } + }, + "folders": { + "role_memos": "Тэмдэглэл", + "title": "Хавтас", + "description": "Имэйл хавтасаа удирдаж, стандарт үүрэг оноох", + "folder_list": "Таны хавтаснууд", + "folder_list_description": "Хавтасны дүрс дээр дарж өөрчилнө үү", + "standard_roles": "Стандарт хавтасны үүрэг", + "standard_roles_description": "Ирсэн имэйл, Илгээсэн, Хогийн сав гэх мэт стандарт шуудангийн хайрцгийн үүрэгт ямар фолдерыг ашиглахыг зааж өгнө үү.", + "role_inbox": "Ирсэн имэйл", + "role_drafts": "Ноорог", + "role_sent": "Илгээсэн", + "role_trash": "Хогийн сав", + "role_junk": "Спам / Хог", + "role_archive": "Архив", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Байхгүй", + "create_folder": "Хавтас үүсгэх", + "create_subfolder": "Дэд хавтас үүсгэх", + "reorder": "Дахин эрэмбэлэхийн тулд чирнэ үү", + "reorder_error": "Фолдеруудыг дахин эрэмбэлж чадсангүй", + "move_error": "Фолдерыг зөөж чадсангүй", + "select_folder": "{name}-г сонгох", + "select_hint": "Сонгох. Shift+товшилт нь хамгийн сүүлд товшсон фолдер хүртэлх бүх фолдерыг сонгоно.", + "selected_count": "{count, plural, one {# фолдер} other {# фолдер}} сонгогдсон", + "move_to": "Зөөх...", + "move_to_top_level": "Дээд түвшин", + "move": "Зөөх", + "clear_selection": "Сонголтыг цуцлах", + "folders_moved": "{count, plural, one {# фолдер зөөгдлөө} other {# фолдер зөөгдлөө}}", + "move_partial_error": "{count, plural, one {# фолдерыг} other {# фолдерыг}} зөөж чадсангүй", + "subfolder_of": "{name}", + "subfolder_name": "Дэд хавтасны нэр", + "new_folder_name": "Хавтасны нэр", + "rename": "Нэрээ өөрчлөх", + "change_icon": " Дүрсийг өөрчлөх", + "count_mode": "{name}-ийн тоолуур", + "count_mode_default": "Тоолуур: үндсэн", + "count_mode_unread": "Зөвхөн уншаагүй", + "count_mode_all": "Уншаагүй ба нийт", + "count_mode_none": "Тоолуургүй", + "delete": "Устгах", + "confirm_delete": "Та \"{name}\"-г устгахдаа итгэлтэй байна уу? Энэ хавтсанд байгаа имэйлийг Хогийн сав руу зөөх болно.", + "create": "Үүсгэх", + "cancel": "Цуцлах", + "no_folders": "Захиалгат хавтас байхгүй", + "cannot_delete_role": "Стандарт үүрэг бүхий хавтсыг устгах боломжгүй. Эхлээд дүрээ хас.", + "folder_created": "Хавтас үүсгэсэн", + "folder_renamed": "Хавтасны нэрийг өөрчилсөн", + "folder_deleted": "Хавтас устгасан", + "role_updated": "Фолдерын үүрэг шинэчлэгдсэн", + "error_create": "Хавтас үүсгэж чадсангүй", + "error_rename": "Фолдерын нэрийг өөрчилж чадсангүй", + "error_delete": "Хавтсыг устгаж чадсангүй", + "error_delete_has_children": "Фолдерыг устгах боломжгүй: энэ нь дэд хавтастай хэвээр байна. Эхлээд устгах эсвэл зөөх.", + "error_delete_has_email": "Фолдерыг устгах боломжгүй: энэ нь имэйл агуулсан хэвээр байна. Эхлээд зөөж эсвэл устгана уу.", + "error_role": "Фолдерын үүргийг шинэчилж чадсангүй" + }, + "advanced": { + "title": "Дэвшилтэт", + "description": "Дэвшилтэт сонголтууд болон хөгжүүлэгчийн тохиргоо", + "debug_mode": { + "label": "Дбаг хийх горим", + "description": "Алдааг олж засварлахын тулд нарийвчилсан бүртгэлийг идэвхжүүлэх" + }, + "debug_categories": { + "description": "Ямар ангилалд бүртгүүлэхээ сонгоно уу. Консолын дуу чимээг багасгахын тулд шаардлагагүй ангиллыг идэвхгүй болго.", + "jmap": "JMAP", + "jmap_description": "Шуудангийн хайрцгийн ажиллагаа, имэйл татах, JMAP протоколын хүсэлтүүд", + "calendar": "Хуанли", + "calendar_description": "Календарийн арга хэмжээ, импорт болон хуваарийн мессежүүд", + "tasks": "Даалгавар", + "tasks_description": "Календарийн даалгавар үүсгэх, татах, шинэчлэх", + "auth": "Баталгаажуулалт", + "auth_description": "Нэвтрэх, TOTP, токен солилцох, сесс удирдах", + "filters": "Шүүлтүүр", + "filters_description": "Шүүлтүүрийн дүрэм болон амралтын скриптүүдийг шүүнэ", + "email": "И-мэйл үзэх", + "email_description": "И-мэйл дүрслэх, TNEF боловсруулах, уншсан гэж тэмдэглэх", + "push": "Түлхэх мэдэгдэл", + "push_description": "Push мэдэгдлийн тохиргоо болон хүргэлт", + "contacts": "Харилцах утас, хаягийн дэвтэр", + "contacts_description": "Холбоо барих синхрончлол, хаягийн дэвтрийн үйл ажиллагаа, итгэмжлэгдсэн илгээгчид" + }, + "settings_sync": { + "label": "Тохиргоо Синк", + "description": "Тохиргоогоо хөтөч болон төхөөрөмжүүдийн хооронд синк хийх" + }, + "sender_favicons": { + "label": "Илгээгчийн Favicons", + "description": "Вэб сайтын дүрсийг бизнес илгээгчдэд профайл зураг болгон харуулах", + "refresh_label": "Илгээгчийн дүрсийг шинэчлэх", + "refresh_description": "Вэбсайтын дүрсийг дахин татах, жишээ нь илгээгч дүрсээ сольсон үед", + "refresh_button": "Шинэчлэх", + "refresh_done": "Илгээгчийн дүрсийг дахин татна" + }, + "show_avatars_in_junk": { + "label": "Хогийн хавтас дахь аватаруудыг харуулах", + "description": "Хогийн хавтсанд илгээгчдийн профайлын зураг болон фавиконыг харуул. Фишингийн оролдлогод харааны хууль ёсны байдлыг өгөхгүйн тулд анхдагчаар идэвхгүй болгосон." + }, + "keyboard_shortcuts": { + "label": "Гарын товчлол", + "description": "Боломжтой гарын товчлолуудыг харах", + "button": "Товчлол харах" + }, + "refresh_cache": { + "label": "Кэш хийсэн өгөгдлийг сэргээх", + "description": "Харилцагч, хуанли, фолдеруудыг серверээс дахин ачаална уу. Таны бүртгэл болон сешнүүдийг хадгална — гаралгүй хуучирсан эсвэл буруу харагдацыг засна.", + "button": "Сэргээх" + }, + "reset_settings": { + "label": "Тохиргоог дахин тохируулах", + "description": "Бүх тохиргоог анхдагч утга руу нь сэргээнэ", + "button": "Өгөгдмөл рүү дахин тохируулах" + }, + "export_settings": { + "label": "Экспортын тохиргоо", + "description": "Тохиргоогоо JSON", + "button": "Экспорт" + }, + "import_settings": { + "label": "Импортын тохиргоо", + "description": "JSON файлаас тохиргоог байршуулах", + "button": "Импорт" + }, + "about": { + "title": "Тухай", + "source_code": "Эх код" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Спам болон фишинг ирсэн хайрцагт хүрэхээс өмнө дээр нь дарна уу. Жинхэнэ захидлыг нэвтрүүлээрэй. Гурван бамбайгаа бүгдийг алдвал ирсэн хайрцаг унана.", + "kinds": { + "spam": "Спам", + "phishing": "Фишинг", + "legit": "Жинхэнэ захидал" + }, + "click_to_block": "Блоклохын тулд дарна уу", + "let_through": "Нэвтрүүлэх", + "start": "Эхлэх", + "play_again": "Дахин тоглох", + "score": "Оноо", + "combo": "Цувааны урамшуулал", + "shields_left": "{total} бамбайгаас {count} үлдсэн", + "seconds": "{seconds} с", + "best": "Шилдэг: {score}", + "new_best": "Шинэ дээд амжилт", + "held": "Ирсэн хайрцаг хамгаалагдлаа", + "overrun": "Ирсэн хайрцаг эзлэгдлээ", + "stats": { + "caught": "Блоклосон", + "delivered": "Хүргэсэн", + "mistakes": "Алдаа" + } + } + }, + "sidebar_apps": { + "title": "Хажуугийн самбарын програмууд", + "description": "Хажуугийн самбар дээрх захиалгат програмууд болон холбоосуудыг удирдах", + "keep_loaded": "Апп-уудыг ачаалалтай байлгах", + "keep_loaded_description": "Дахин ачаалахаас зайлсхийхийн тулд шугаман програмуудыг хооронд нь сэлгэхдээ ар талд нь ажиллуулаарай", + "managed_title": "Администратороос нийлүүлсэн", + "managed_description": "Эдгээр аппыг бүх хэрэглэгчид тохируулсан бөгөөд өөрчлөх боломжгүй.", + "manage_title": "Захиалгат програмууд", + "manage_description": "Хажуугийн цэсэндээ тусгай програм нэмэх, засах, устгах боломжтой" + }, + "contacts": { + "title": "Харилцах утас", + "description": "Харилцагчаа импортлох, экспортлох", + "group_by_letter_label": "Эхний үсгээр бүлэглэх", + "sort_by_last_name_label": "Овгоор эрэмбэлэх", + "sort_by_last_name_description": "Гэр бүлийн гишүүд хамт харагдахаар харилцах хүмүүсийн жагсаалтыг овгоор эрэмбэлэх", + "group_by_letter_description": "Харилцах хүмүүсийн жагсаалтад цагаан толгойн үсгийн дарааллаар хэсгийн толгойг харуулах", + "import_label": "Харилцагчдыг импортлох", + "import_description": "vCard (.vcf) файлаас харилцагчдыг импортлох", + "export_label": "Харилцагчдыг экспортлох", + "export_description": "Бүх харилцагчдыг vCard (.vcf) файл болгон экспортлох", + "manage_title": "Хаягийн дэвтэр", + "manage_description": "Хаягийн дэвтэрийнхээ нэрийг өөрчлөх", + "no_address_books": "Хаягийн дэвтэр олдсонгүй", + "categories_title": "Ангилал", + "categories_description": "Харилцах ангиллын нэрийг өөрчлөх", + "no_categories": "Ангилал олдсонгүй" + }, + "downloads": { + "title": "Татаж авах", + "description": "Татаж авсан имэйл болон хавсралтуудыг хэрхэн нэрлэхийг тохируулах.", + "reset": "Өгөгдмөлийг сэргээх", + "preview": "Урьдчилан үзэх:", + "email_template": { + "label": "И-мэйл (.eml) файлын нэр", + "description": "Загвар нь таныг имэйл экспортлох эсвэл файлын систем рүү чирэх үед ашигладаг. .eml өргөтгөл автоматаар нэмэгддэг." + }, + "attachment_template": { + "label": "Хавсралт файлын нэр", + "description": "Загвар нь хавсралтыг татаж авах эсвэл чирэх үед ашиглагддаг. Хэрэв та '{filename}' болон '{ext}'-г орхивол анхны өргөтгөл хадгалагдана." + }, + "bundle_template": { + "label": "Олон имэйл .zip файлын нэр", + "description": "Загвар нь таныг сонгосон хэд хэдэн имэйлийг нэг .zip архив болгон чирэх эсвэл татах үед ашигладаг. .zip өргөтгөл автоматаар нэмэгддэг." + }, + "spaces": { + "label": "Зайнууд", + "description": "Үзэгдэх файлын нэрний хоосон зайг өөр тэмдэгтээр солино.", + "keep": "Зай үлдээх", + "underscore": "_", + "dash": "-" + }, + "lowercase": { + "label": "Жижиг үсэг", + "description": "Файлын нэрийг бүхэлд нь жижиг үсгээр бичнэ үү." + }, + "strip_diacritics": { + "label": "Судасны диакритик", + "description": "Өргөлттэй үсгүүдийг ASCII эквивалент (ä → a, é → e) болгон хувиргана. Юникод файлын нэрийг өөрчлөх хэрэгсэлд хэрэг болно." + }, + "collapse_separators": { + "label": "Дахин давтагдсан тусгаарлагчийг нураах", + "description": "Хоосон зай, доогуур зураас эсвэл зураасыг нэг тэмдэгт болгон буулгана." + }, + "after_export": { + "label": "Экспортын дараа", + "description": "Заавал имэйлийг .eml.", + "keep": "Шуудангийн хайрцагт хадгалах", + "archive": "Архив руу шилжих", + "trash": "Хогийн сав руу зөөх" + } + }, + "filters": { + "title": "Имэйл шүүлтүүрүүд", + "description": "Ирж буй имэйлийг автоматаар ангилах, шошголох, удирдах дүрэм бий болгох", + "add_rule": "Дүрэм нэмэх", + "no_rules": "Шүүлтүүрийн дүрэм байхгүй", + "no_rules_description": "Ирж буй имэйлээ автоматаар цэгцлэх дүрэм бий болгох", + "vacation_active": "Амралтын хариулагч идэвхтэй байна", + "vacation_active_description": "Ирж буй мессежүүдэд автоматаар хариу бичихийг идэвхжүүлсэн", + "vacation_configure": "Тохируулах", + "edit_rule": "Засах дүрэм", + "new_rule": "Шинэ дүрэм", + "delete_rule": "Дүрэмийг устгах", + "delete_confirm": "Та энэ дүрмийг устгахдаа итгэлтэй байна уу?", + "enable": "Идэвхжүүлэх", + "disable": "Идэвхгүй болгох", + "raw_editor": "Түүхий шигшүүр засварлагч", + "raw_editor_warning": "Түүхий Sieve скриптийг засварлах нь дүрмийн засварыг эвдэж болзошгүй. Энд хийсэн өөрчлөлтүүд нь харааны бүтээгчийг дарж байна.", + "validate": "Баталгаажуулах", + "validation_success": "Скрипт хүчинтэй", + "validation_error": "Script алдаатай байна", + "save": "Дүрэм хадгалах", + "saving": "Хадгалж байна...", + "saved": "Шүүлтүүрийг амжилттай хадгаллаа", + "save_failed": "Шүүлтүүрийг хадгалж чадсангүй", + "loading": "Шүүлтүүрийг ачаалж байна...", + "not_supported": "Таны мэйл сервер имэйл шүүлтүүрийг дэмждэггүй.", + "rule_name": "Дүрмийн нэр", + "rule_name_placeholder": "жишээ нь, Мэдээллийн хуудсыг эрэмбэлэх", + "match_all": "БҮХ нөхцөлийг тааруулах", + "match_any": "Ямар ч нөхцөлтэй тохирно", + "conditions": "Нөхцөлүүд", + "add_condition": "Нөхцөл нэмэх", + "actions": "Үйлдэл", + "add_action": "Үйлдэл нэмэх", + "stop_processing": "Дараагийн дүрмийг боловсруулахаа зогсоох", + "include_spam": "Спам гэж тэмдэглэсэн захидлыг мөн зөөх", + "attachment_type_placeholder": "жишээ нь pdf, doc, jpg", + "value_placeholder_multi": "Утга (олон тоог таслалаар тусгаарласан)", + "condition_fields": { + "attachment": "Хавсралт", + "from": "-аас", + "to": "Хэнд", + "cc": "Хуулбар", + "subject": "Сэдэв", + "header": "Захиалгат толгой хэсэг", + "size": "Хэмжээ", + "body": "Бие", + "all": "Бүх захиа" + }, + "comparators": { + "has_any": " байгаа", + "has_type": "төрөл", + "contains": "агуулсан", + "not_contains": " агуулаагүй", + "is": " яг", + "not_is": " нь", + "starts_with": "", + "ends_with": "", + "matches": "загварт тохирно", + "greater_than": " нь", + "less_than": " нь", + "address_is": "хаяг нь", + "domain_is": "домэйн нь" + }, + "action_types": { + "move": "Хавтас руу зөөх", + "copy": "Хавтас руу хуулах", + "forward": "Дашлуул", + "mark_read": "Уншсан гэж тэмдэглэх", + "star": "Одтой мессеж", + "add_label": "Таг нэмэх", + "discard": "Тайх (чимээгүй устгах)", + "reject": "Мессежээр татгалзах", + "keep": "Ирсэн имэйлд хадгалах", + "stop": "Боловсруулалтыг зогсоох" + }, + "move_to_folder": "Фолдер сонгох", + "copy_to_folder": "Фолдер сонгох", + "forward_to": "Имэйл хаяг руу шилжүүлэх", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Хуулбар хадгалах", + "forward_limit": "Энэ сервер дээрх нэг захидлын дамжуулах хязгаар: {count}. Илүү дамжуулалтыг алгасна.", + "forward_unavailable": "Энэ сервер дамжуулахыг зөвшөөрдөггүй. Шүүлтүүрийн дүрэм дэх дамжуулах үйлдэл ажиллахгүй.", + "reject_message": "Татгалзсан мессеж", + "reject_placeholder": "Таны имэйл татгалзсан байна", + "label_name": "Тагийн нэр", + "label_placeholder": "Таг сонгох", + "header_name": "Толгойн нэр", + "header_placeholder": "жишээ нь, X-мэйл хаягийн жагсаалт", + "size_bytes": "Хэмжээ байтаар", + "size_placeholder": "жишээ нь, 1000000", + "system_managed": "Системээр удирддаг дүрэм", + "opaque_warning": "Энэ скриптийг визуал бүтээгчээс гадуур засварласан. Зөвхөн түүхий шигшүүрээр засварлах боломжтой.", + "open_sieve_editor": "Raw Sieve редакторыг нээнэ үү", + "fetch_error": "Шүүлтүүрийг ачаалж чадсангүй", + "expanded_view": "Өргөтгөсөн харах", + "expanded_view_description": "Шүүлтүүрийн дүрмийг нарийвчилсан нөхцөл, үйлдлийн блокоор харуулах", + "if": "Хэрэв", + "then": "Дараа нь", + "match_all_conditions": "бүх тоглолт", + "match_any_condition": "ямар ч тоглолт", + "and": "ба", + "or": "эсвэл", + "cancel": "Цуцлах", + "confirm_delete": "Устгах", + "rule_list": "Шүүлтүүрийн дүрэм", + "drag_to_reorder": "Дахин эрэмбэлэхийн тулд чирнэ үү", + "match_type": "Тохирох төрөл", + "reset_to_visual": "Visual builder руу дахин тохируулах", + "reset_warning": "Энэ нь одоогийн скриптийг устгаж, шинээр эхэлнэ.", + "confirm_reset": "Дахин тохируулах", + "validation_empty_name": "Дүрмийн нэр шаардлагатай", + "validation_empty_conditions": "Дор хаяж нэг утга бүхий нөхцөл шаардлагатай", + "validation_empty_actions": "Дор хаяж нэг үйлдэл хийх шаардлагатай", + "templates_section": "Загвар", + "template_newsletters": "Мэдээллийн хуудсыг хавтас руу зөөх", + "template_receipts": "Автоматаар архивлах баримт", + "template_important": "Чухал имэйлийг дарцаглах", + "template_notifications": "Мэдэгдэл шүүх", + "sieve_editor": { + "title": "Шигшүүрийн скрипт засварлагч", + "warning": "Түүхий Sieve скриптийг засварлах нь дүрмийн засварыг эвдэж болзошгүй. Энд хийсэн өөрчлөлтүүд нь харааны бүтээгчийг хүчингүй болгодог.", + "script_content": "Шигшүүрийн скрипт", + "valid": "Скрипт хүчинтэй", + "invalid": "Script-д алдаа байна", + "save_warning": "Хадгалснаар ямар ч харааны дүрмийг дарж бичнэ. Үүнийг буцаах боломжгүй. Баталгаажуулахын тулд Save дахин дарна уу.", + "validating": "Баталгаажуулж байна...", + "validate": "Баталгаажуулах", + "cancel": "Цуцлах", + "save": "Хадгалах", + "confirm_save": "Хадгалахыг баталгаажуулах", + "validation_failed": "Баталгаажуулах хүсэлт амжилтгүй боллоо" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {#нөхцөл} other {# нөхцөл}}", + "actions_count": "{count, plural, one {# үйлдэл} other {# үйлдэл}}" + }, + "origin_external": "Гадаад", + "managed_by_tooltip": "{source} удирддаг. Үүнийг тухайн аппликешнд засварлах эсвэл түүхий шигшүүр засварлагчийг ашиглана уу.", + "quick_rule_names": { + "move_sender": "{sender}-ээс {folder} руу зөөх", + "move_domain": "@{domain}-ээс {folder} руу зөөх", + "move_list": "{list} жагсаалтыг {folder} руу зөөх", + "mark_read": "{sender}-ийн захиаг уншсан болгох", + "tag": "{sender}-ийн захианд {tag} таг нэмэх", + "block": "{sender}-ийг хаах", + "custom": "{sender}-ийн захиа", + "custom_plain": "Шинэ дүрэм", + "senders": "{count, plural, one {# илгээгч} other {# илгээгч}}" + }, + "suggestions": "Санал", + "suggest_subject": "Гарчиг “{subject}”", + "suggest_to": "Хүлээн авагч {address}", + "suggest_cc": "Хуулбар {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Энэ хавтсанд байгаа захиануудад ч мөн хэрэглэх", + "apply_existing_unsupported": "Илгээгч, хүлээн авагч, гарчиг эсвэл толгойг шалгаж, зөөх, хуулах, уншсан болгох, од тавих эсвэл таг нэмэх дүрмүүдийг л байгаа захиануудад хэрэглэж болно.", + "period_title": "Огнооны муж", + "period_start": "Эхлэх огноо", + "period_end": "Төгсгөлийн огноо", + "validation_period_order": "Төгсгөлийн огноо нь эхлэх огнооноос хойш байх ёстой", + "period_toggle": "Зөвхөн тодорхой хугацаанд идэвхтэй", + "period_hint": "Энэ хугацаанд ирсэн мессежид хамаарна. Аль нэг талыг хязгаарлахгүй бол талбарыг хоосон үлдээнэ үү.", + "validation_period_empty": "Хугацааны эхлэх эсвэл төгсгөлийн огноог оруулна уу", + "validation_period_invalid": "Хугацаанд цагтай хүчинтэй огноо оруулах эсвэл талбарыг хоосон үлдээнэ үү", + "apply_existing_period": "Хугацаатай дүрэм зөвхөн тэр хугацаанд ирсэн мессежид үйлчилдэг тул одоо байгаа мессежид хэрэглэх боломжгүй.", + "period_status_scheduled": "төлөвлөсөн", + "period_status_active": "идэвхтэй", + "period_status_expired": "хугацаа дууссан" + }, + "templates": { + "title": "И-мэйл загварууд", + "description": "Орлуулагч хувьсагчтай дахин ашиглах боломжтой имэйлийн загваруудыг үүсгэх", + "add": "Шинэ загвар", + "edit": "Загвар засах", + "name": "Загвар нэр", + "name_placeholder": "жишээ нь, дагах имэйл", + "category": "Ангилал", + "category_placeholder": "жишээ нь, Ажил, Хувийн", + "subject": "Сэдэв", + "subject_placeholder": "И-мэйл гарчиг", + "body": "Бие", + "body_placeholder": "И-мэйл үндсэн контент...", + "recipients_placeholder": "email@example.com", + "identity": "Send as", + "default_identity": "Өгөгдмөл таниулбар", + "favorite": "Дуртай", + "cancel": "Цуцлах", + "create": "Үүсгэх", + "update": "Шинэчлэлт", + "confirm_delete": "Устгах", + "no_templates": "Одоохондоо загвар байхгүй", + "manage": "Загваруудыг удирдах", + "count": "{count, plural, one {# загвар} other {# загвар}}", + "export_import": "Экспорт & Импорт", + "export_import_description": "Загваруудаа нөөцлөх эсвэл өөр төхөөрөмж рүү шилжүүлэх", + "export": "Экспорт хийх", + "import": "Импорт", + "validation": { + "empty": "Загварын нэр шаардлагатай", + "too_long": "Загварын нэр 200 ба түүнээс бага тэмдэгт байх ёстой" + } + }, + "files": { + "display": { + "title": "Дэлгэц", + "description": "Файл, фолдерыг хэрхэн харуулахыг тохируулах" + }, + "default_view": { + "label": "Өгөгдмөл харах", + "description": "Сүлжээ болон жагсаалтын байршлаас сонгох", + "list": "Жагсаалт", + "grid": "Сүлжээ" + }, + "default_sort": { + "label": "Өгөгдмөл эрэмбэлэх", + "description": "Файлуудын анхдагч эрэмбэлэхийг сонгоно уу", + "name": "Нэр", + "size": "Хэмжээ", + "modified": "Өөрчлөгдсөн" + }, + "sort_direction": { + "label": "Ангилах чиглэл", + "description": "Өсөх эсвэл буурах дарааллыг сонгоно", + "ascending": "Өсөх", + "descending": "Буурах" + }, + "icons": { + "title": "Тэмдэгтүүд", + "description": "Файлын дүрсний харагдах байдлыг тохируулах" + }, + "show_icons": { + "label": "Файлын дүрсийг харуулах", + "description": "Файл, фолдерын хажууд дүрсүүдийг харуулах" + }, + "colored_icons": { + "label": "Өнгөт дүрсүүд", + "description": "Нэг өнгийн дүрсний оронд өнгөлөг дүрс ашиглах" + }, + "show_thumbnails": { + "label": "Дөнгөж зураг харуулах", + "description": "Зургийн файлуудын дүрсийн оронд зургийн урьдчилан харахыг харуулах" + }, + "behavior": { + "title": "Зан төлөв", + "description": "Файлын хөтөчийн ажиллагааг тохируулах" + }, + "show_hidden": { + "label": "Нуугдсан файлуудыг харуулах", + "description": "Цэгээр эхэлсэн файл, хавтсыг харуулах" + }, + "preview": { + "label": "Урьдчилан харах" + } + } + }, + "errors": { + "page_error_title": "Алдаа гарлаа", + "page_error_description": "Бидэнд гэнэтийн алдаа гарлаа. Дахин оролдох эсвэл нүүр хуудас руу буцна уу.", + "sidebar_error": "Шуудангийн хайрцгийг ачаалах боломжгүй", + "email_list_error": "Имэйл ачаалах боломжгүй байна", + "viewer_error_title": "Имэйл харуулах боломжгүй", + "viewer_error_description": "Энэ имэйлийг үзүүлэхэд асуудал гарлаа. Энэ нь дэмжигдээгүй контент агуулж болзошгүй.", + "composer_error": "Бичих цонхыг ачаалах боломжгүй", + "settings_error_title": "Тохиргоо боломжгүй", + "settings_error_description": "Тохиргоог ачаалах боломжгүй. Таны тохиргоо хадгалагдахгүй байж магадгүй.", + "try_again": "Дахин оролдоно уу", + "reload": "Дахин ачаалах", + "reload_emails": "Имэйл дахин ачаалах", + "reload_settings": "Тохиргоог дахин ачаалах", + "retry": "Дахин оролдох", + "go_home": "Ирсэн имэйл рүү очих" + }, + "context_menu": { + "reply": "Хариулах", + "reply_all": "Бүгдэд нь хариулах", + "forward": "Урагшаа", + "mark_read": "Уншсан гэж тэмдэглэх", + "mark_unread": "Уншаагүй гэж тэмдэглэх", + "star": "од", + "unstar": "Одгүй", + "pin": "Бэхлэх", + "unpin": "Буулгах", + "move_to": "...", + "copy_to_account": "Бүртгэл рүү хуулах...", + "remove_from_folder": "Энэ хавтаснаас хасах", + "archive": "Архив", + "delete": "Устгах", + "mark_as_spam": "Спам мэдээлэх", + "not_spam": "Спам биш", + "tag": "Таг", + "items_selected": "{count} имэйл сонгосон", + "forward_as_attachments": "Хавсралт болгон дамжуулах", + "edit_draft": "Ноорог засах", + "cancel_scheduled_send": "Илгээхийг цуцлах", + "reschedule_send": "Хуваарь солих", + "cancel_and_edit": "Цуцлах, засварлах", + "cancel_and_compose_again": "Цуцлаад дахин бичих", + "rules": { + "title": "Дүрмүүд", + "move_from": "{sender}-ийн захиаг үргэлж зөөх", + "move_from_domain": "@{domain}-ийн захиаг үргэлж зөөх", + "move_from_list": "Энэ жагсаалтын захиаг үргэлж зөөх", + "mark_read_from": "{sender}-ийн захиаг үргэлж уншсан гэж тэмдэглэх", + "tag_with": "Үргэлж таг нэмэх", + "block": "{count, plural, one {Илгээгчийг хаах} other {# илгээгчийг хаах}}", + "no_junk": "Энэ бүртгэлд спам хавтас байхгүй", + "create": "Дүрэм үүсгэх…", + "manage": "Дүрмүүдийг удирдах", + "senders_count": "{count, plural, one {# илгээгч} other {# илгээгч}}", + "cross_account": "Нэг бүртгэлийн захиануудыг сонгоно уу", + "opaque_hint": "Таны шүүлтүүрийг гараар засварласан байна. Шүүлтүүрийн тохиргоог нээнэ үү" , + "loading": "Дүрмүүдийг ачаалж байна…", + "load_failed": "Дүрмүүдийг ачаалж чадсангүй", + "none": "Одоогоор дүрэм алга", + "new_mail_only": "Зөвхөн шинэ мессежүүдэд", + "running": "{folder} дээр \"{name}\"-г ажиллуулж байна…" + }, + "find_emails_from": "{name}-с ирсэн имэйлүүдийг хайх" + }, + "mailbox_context_menu": { + "mark_folder_read": "Хавтсыг уншсан гэж тэмдэглэх", + "mark_folder_tree_read": "Хавтас болон дэд хавтаснуудыг уншсан гэж тэмдэглэх", + "mark_all_folders_read": "Бүх хавтсыг уншсан гэж тэмдэглэх", + "new_subfolder": "Шинэ дэд хавтас...", + "new_folder": "Шинэ хавтас...", + "rename": "Нэр өөрчлөх...", + "share": "Хуваалцах...", + "import_email": "Импорт .eml эсвэл .zip...", + "empty_folder": "Хоосон хавтас", + "empty_folder_generic": "Хоосон хавтас", + "delete_folder": "Хавтас устгах", + "refresh": "Сэргээх", + "hide_shared_account": "Хажуугийн самбараас нуух", + "mute_notifications": "Мэдэгдлийг хаах", + "unmute_notifications": "Мэдэгдлийг нээх", + "mute_account_notifications": "Энэ бүртгэлийн мэдэгдлийг хаах", + "unmute_account_notifications": "Энэ бүртгэлийн мэдэгдлийг нээх", + "reset_folder_notifications": "Хавтасны мэдэгдлийн тохиргоог сэргээх", + "mark_all_confirm_title": "Бүх хавтсыг уншсан гэж тэмдэглэх", + "mark_all_confirm_message": "Хувийн бүртгэлийнхээ уншаагүй мессеж бүрийг уншсан гэж тэмдэглэх үү?", + "delete_confirm_title": "Хавтас устгах", + "delete_confirm_message": "\"{name}\" хавтсыг бүрмөсөн устгах уу? Энэ үйлдлийг буцаах боломжгүй.", + "prompt_new_subfolder": "Шинэ дэд хавтасны нэрийг оруулна уу.", + "prompt_new_folder": "Шинэ хавтсын нэрийг оруулна уу.", + "prompt_rename": "Энэ хавтсанд шинэ нэр оруулна уу.", + "placeholder_folder_name": "Хавтасны нэр", + "create": "Бүтээх", + "rename_confirm": "Нэрээ өөрчлөх", + "toast_marked_read": "Хавтсыг уншсан гэж тэмдэглэлээ", + "toast_marked_read_count": "{count, plural, one {1 зурвасыг уншсан гэж тэмдэглэлээ} other {# зурвасыг уншсан гэж тэмдэглэлээ}}", + "toast_already_read": "Уншаагүй мессеж байхгүй", + "toast_marked_all_read": "Бүх хавтсыг уншсан гэж тэмдэглэлээ", + "toast_emptied": "Хавтас хоослогдсон", + "toast_folder_created": "Хавтас үүслээ", + "toast_folder_renamed": "Хавтсын нэрийг өөрчиллөө", + "toast_folder_deleted": "Хавтас устлаа", + "toast_error_mark_read": "Уншсан гэж тэмдэглэж чадсангүй", + "toast_error_empty": "Хавтсыг хоосолж чадсангүй", + "toast_error_create": "Хавтас үүсгэж чадсангүй", + "toast_error_rename": "Хавтсын нэрийг өөрчилж чадсангүй", + "toast_error_delete": "Хавтсыг устгаж чадсангүй", + "toast_error_delete_has_children": "Хавтас дэд хавтастай байна. Эхлээд тэдгээрийг устгана уу.", + "toast_error_delete_has_email": "Хавтас хоосон биш байна. Эхлээд үүнийг хоосло.", + "delete_with_contents_title": "Хавтас хоосон биш байна", + "delete_with_contents_message": "\"{name}\" хавтсанд одоо ч {count} зурвас байна. Хавтас болон бүх зурвасыг устгах уу? Үүнийг буцаах боломжгүй.", + "delete_with_contents_confirm": "Хавтас болон зурвасыг устгах" + }, + "shortcuts": { + "title": "Гарын товчлол", + "tip": "Энэ тусламжийг хүссэн үедээ ? дарж нээж болно", + "sections": { + "navigation": "Навигаци", + "actions": "Имэйлийн үйлдлүүд", + "global": "Ерөнхий", + "threads": "Хэлхээсүүд", + "composer": "Бичих цонх" + }, + "navigation": { + "next_email": "Дараагийн имэйл", + "previous_email": "Өмнөх имэйл", + "open_email": "Имэйл нээх", + "close_email": "Хаах / Сонголтыг цуцлах", + "go_to_folder": "Хавтас руу очих", + "next_unread": "Дараагийн уншаагүй имэйл", + "no_more_unread": "Жагсаалтын доод хэсэгт уншаагүй имэйл алга" + }, + "actions": { + "reply": "Хариулах", + "reply_all": "Бүгдэд нь хариулах", + "forward": "Урагшаа", + "star": "Одыг асаах/унтраах", + "archive": "Архив", + "delete": "Устгах", + "mark_unread": "Уншаагүй гэж тэмдэглэх", + "mark_read": "Уншсан гэж тэмдэглэх", + "toggle_spam": "Спам мэдээлэх / Спам биш" + }, + "global": { + "compose": "Шинэ имэйл бичих", + "search": "Хайлт руу шилжих", + "help": "Товчлол харуулах", + "refresh": "Имэйл сэргээх", + "select_all": "Бүгдийг сонго", + "global_search": "Бүгдийг хайх", + "extend_selection": "Сонголтыг доош / дээш өргөтгөх" + }, + "threads": { + "expand_collapse": "Хэлхээсийг дэлгэх/хумих", + "select_mountain_view": "Харилцааг сонгох" + }, + "composer": { + "send": "Имэйл илгээх", + "schedule_send": "Хуваарь илгээх", + "template_picker": "Загвар сонгогчийг нээх" + } + }, + "threads": { + "messages_one": "{count} мессеж", + "messages_other": "{count} мессежүүд", + "messages_tooltip": "{count, plural, one {Энэ харилцан ярианд # зурвас байна} other {Энэ харилцан ярианд # зурвас байна}}", + "expand": "Харилцан яриаг өргөжүүлэх", + "collapse": "Харилцан яриаг хураах", + "loading": "Харилцан яриаг ачаалж байна...", + "mark_read": "Харилцан яриаг уншсан гэж тэмдэглэх", + "mark_unread": "Харилцан яриаг уншаагүй гэж тэмдэглэх", + "archive": "Харилцан яриаг архивлах", + "delete": "Харилцан яриаг устгах", + "star": "Харилцан яриаг одтой болгох", + "unstar": "Харилцан ярианы одыг авах", + "toggle_thread": "Хэлхээсийг дэлгэх/хумих" + }, + "identities": { + "modal_title": "Илгээж буй таниулбаруудыг удирдах", + "create_new": "Шинэ таниулбар үүсгэх", + "edit_identity": "Таниулбар засах", + "delete_confirm": "Энэ таниулбарыг устгах уу? Үүнийг буцаах боломжгүй.", + "cannot_delete": "Энэ таниулбарыг устгах боломжгүй", + "primary_identity": "Үндсэн", + "set_as_primary": "Үндсэн гэж тохируулах", + "no_identities": "Таниулбар олдсонгүй", + "display": { + "reply_to": "Хариулт:", + "bcc": "BCC:", + "signature": "Гарын үсэг:", + "preview": "Урьдчилан үзэх:" + }, + "validation_errors": { + "invalid_emails": "Буруу имэйл: {emails}", + "unknown_error": "Үл мэдэгдэх алдаа" + }, + "form": { + "name_label": "Дэлгэцийн нэр", + "name_placeholder": "жишээ нь, Ажлын имэйл, Хувийн", + "name_required": "Нэр оруулах шаардлагатай", + "email_label": "Имэйл хаяг", + "email_placeholder": "your.email@example.com", + "email_required": "И-мэйл шаардлагатай", + "email_invalid": "Хүчинтэй имэйл хаяг оруулна уу", + "email_immutable": "И-мэйл хаягийг үүсгэсний дараа өөрчлөх боломжгүй", + "reply_to_label": "Хариу бичих (заавал биш)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "Авто BCC (заавал биш)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Текст гарын үсэг", + "html_signature_label": "HTML гарын үсэг", + "signature_byte_counter": "{bytes} / {max} байт", + "signature_byte_limit_reached": "Серверийн хязгаарт хүрсэн", + "save": "Таниулбарыг хадгалах", + "cancel": "Цуцлах", + "creating": "Бүтээж байна...", + "updating": "Шинэчилж байна..." + }, + "sub_address": { + "button_tooltip": "Дэд хаяг ашиглах", + "popover_title": "Дэд хаягийн шошго нэмэх", + "tag_input_placeholder": "Таг оруулна уу (жишээ нь, дэлгүүр хэсэх)", + "preview_label": "Урьдчилан үзэх:", + "recent_tags": "Сүүлийн шошго", + "suggested_tags": "Санал болгосон", + "use_address": "Энэ хаягийг ашиглах", + "invalid_tag": "Таг нь зөвхөн үсэг, тоо, зураастай байх ёстой", + "tag_too_long": "Таг нь 30 тэмдэгт буюу түүнээс бага байх ёстой", + "help_text": "user{delimiter}tag@domain.com руу ирсэн имэйлүүд таны ирсэн хайрцагт хүрнэ", + "validation": { + "empty": "Таг хоосон байж болохгүй", + "too_long": "Таг нь {max} буюу түүнээс бага тэмдэгт байх ёстой", + "invalid_chars": "Таг нь зөвхөн үсэг, тоо, зураас агуулсан байх ёстой" + } + }, + "badge": { + "sent_via": "дамжуулан", + "sub_address_tag": "Дэд хаягийг ашиглан илгээсэн: {tag}", + "identity_name": "{name} таниулбараар илгээсэн", + "identity_short": "{name}-ээр дамжуулан", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Устгах", + "delete_confirm_title": "Таниулбар устгах" + }, + "templates": { + "picker_title": "Загвар сонгох", + "search_placeholder": "Загвар хайх...", + "section_favorites": "Дуртай зүйлс", + "section_recent": "Саяхан", + "section_uncategorized": "Бусад", + "no_templates": "Одоохондоо загвар байхгүй", + "no_results": "Загвар олдсонгүй", + "fill_placeholders": "Орлуулагчийн утгыг бөглөх", + "enter_value": "Утгыг оруулна уу...", + "preview": "Урьдчилан харах", + "insert_with_values": "Утгатай оруулах", + "insert_raw": "Түүхий оруулах", + "copy_suffix": "(хуулбар)", + "placeholder": "Хувьсагч", + "placeholders": { + "recipient_name": "Хүлээн авагчийн нэр", + "company": "Компанийн нэр", + "date": "Одоогийн огноо", + "day_of_week": "Долоо хоногийн өдөр", + "sender_name": "Таны нэр" + } + }, + "contacts": { + "directory": { + "title": "Лавлах", + "search_placeholder": "Нэр эсвэл имэйл хаяг хайх", + "refresh": "Лавлахыг шинэчлэх", + "retry": "Дахин оролдох", + "loading": "Лавлахыг ачаалж байна…", + "unavailable_title": "Лавлах алга", + "unavailable_hint": "Таны шуудангийн сервер бүртгэлийн лавлах санал болгодоггүй.", + "forbidden_title": "Лавлах ашиглах боломжгүй", + "forbidden_hint": "Таны шуудангийн сервер энэ бүртгэлд лавлахыг үзэхийг зөвшөөрөхгүй байна. Админаасаа асууна уу.", + "error_title": "Лавлахыг ачаалж чадсангүй", + "error_hint": "Холболтоо шалгаад дахин оролдоно уу.", + "empty_title": "Лавлах хоосон байна", + "empty_hint": "Таны шуудангийн сервер имэйл хаягтай бүртгэл жагсаахгүй байна.", + "no_match": "Лавлахад хайлтад тохирох хүн алга", + "refresh_failed": "Лавлахыг шинэчилж чадсангүй. Сүүлд ачаалсан жагсаалтыг харуулж байна.", + "select_page": "Энэ хуудсан дээрх бүгдийг сонгох", + "deselect_page": "Энэ хуудсан дээрх сонголтыг цуцлах", + "select_entry": "{name}-г сонгох", + "range": "{total}-аас {from}–{to}", + "page": "{count}-аас {page}-р хуудас", + "previous_page": "Өмнөх хуудас", + "next_page": "Дараагийн хуудас", + "picker_title": "Лавлахаас сонгох", + "cancel": "Цуцлах", + "selected_count": "{count} сонгосон", + "add_to": "Хэнд-д нэмэх", + "add_cc": "Cc-д нэмэх", + "add_bcc": "Bcc-д нэмэх", + "select_hint": "Дэлгэрэнгүйг харахын тулд хэн нэгнийг сонгоно уу", + "write_email": "Имэйл бичих", + "add_to_contacts": "Харилцагчид нэмэх", + "email": "Имэйл", + "read_only_note": "Лавлахын бичлэгүүд таны шуудангийн серверээс ирдэг тул энд засах боломжгүй.", + "type": { + "individual": "Хүн", + "group": "Бүлэг", + "resource": "Нөөц", + "location": "Байршил", + "other": "Бусад" + } + }, + "title": "Харилцах утас", + "search_placeholder": "Харилцагч хайх...", + "create_new": "Шинэ холбоо барих", + "no_category": "Ангилалгүй", + "rename_category": "Ангиллын нэрийг өөрчлөх", + "category_name_label": "Ангиллын нэр", + "category_renamed": "Ангиллын нэрийг өөрчилсөн", + "category_rename_failed": "Ангиллын нэрийг өөрчилж чадсангүй", + "category_added": "Харилцагчийг {name}", + "category_added_plural": "{count} харилцагчдыг {name}", + "empty_state": "Харилцагч байхгүй", + "empty_state_title": "Хараахан холбоо барих хүн алга", + "empty_state_subtitle": "Анхны харилцагчаа үүсгэх эсвэл vCard файлаас импортлох", + "empty_search": "Таны хайлтад тохирох харилцагч алга", + "empty_search_hint": "Өөр хайлтын үг хэрэглэж үзнэ үү", + "empty_filtered": "Таны шүүлтүүрт тохирох харилцагч алга", + "empty_filtered_hint": "Шүүлтүүрийг тохируулж эсвэл цэвэрлэж үзнэ үү", + "clear_search": "Хайлтыг арилгах", + "import_vcard": "vCard импортлох", + "delete_confirm_title": "Харилцагчийг устгах", + "delete_confirm": "Та энэ харилцагчийг устгахдаа итгэлтэй байна уу?", + "local_mode": "Харилцагчдыг дотооддоо хадгалдаг (сервер нь JMAP Contacts-ийг дэмждэггүй)", + "back_to_contacts": "Харилцагч руу буцах", + "back_to_email": "Имэйл рүү буцах", + "open_categories": "Нээлттэй ангилалууд", + "tabs": { + "all": "Бүгд", + "groups": "Бүлэг" + }, + "shared": { + "title": "Хуваалцсан" + }, + "address_books": { + "create": "Шинэ хаягийн дэвтэр", + "created": "Хаягийн дэвтэр үүсгэсэн", + "create_failed": "Хаягийн дэвтэр үүсгэж чадсангүй", + "title": "Миний хаягийн дэвтэр", + "shared_prefix": "Хуваалцсан: {name}", + "moved": "Харилцагчийг {name}", + "moved_plural": "{count} харилцагчдыг {name}", + "move_failed": "Харилцагчийг зөөж чадсангүй", + "address_book": "Хаягийн дэвтэр", + "rename": "Хаягийн дэвтрийн нэрийг өөрчлөх", + "name_label": "Хаяг номын нэр", + "renamed": "Хаягийн дэвтрийн нэрийг өөрчилсөн", + "rename_failed": "Хаягийн дэвтрийн нэрийг өөрчилж чадсангүй", + "default": "Өгөгдмөл", + "set_default": "Өгөгдмөлөөр тохируулах", + "default_updated": "Өгөгдмөл хаягийн дэвтэр шинэчлэгдсэн", + "set_default_failed": "Өгөгдмөл хаягийн дэвтрийг тохируулж чадсангүй", + "manage": "Хаягийн дэвтэр удирдах", + "share": "Хаягийн дэвтэр хуваалцах", + "new_contact_in_book": "Энэ хаягийн дэвтэрт шинэ харилцагч", + "delete": "Хаягийн дэвтэр устгах", + "confirm_delete": "\"{name}\"-г устгах уу? Энэ хаягийн дэвтэр дэх бүх харилцагчийг устгах болно.", + "deleted": "Хаягийн дэвтэр устсан", + "delete_failed": "Хаягийн дэвтрийг устгаж чадсангүй" + }, + "detail": { + "emails": "Имэйл хаягууд", + "phones": "Утасны дугаар", + "organizations": "Байгууллага", + "addresses": "Хаяг", + "notes": "Тэмдэглэл", + "titles": "Гарчиг ба үүрэг", + "online_services": "Онлайн үйлчилгээ", + "anniversaries": "Тэмдэг өдөр", + "personal_info": "Хувийн мэдээлэл", + "languages": "Хэлнүүд", + "categories": "Ангилал", + "related_contacts": "Холбогдох холбоо барих хаягууд", + "crypto_keys": "Крипто түлхүүр", + "cert_issuer": "Гаргагч", + "cert_expires": "Хугацаа нь дуусна", + "cert_expired": "Хугацаа дууссан", + "cert_fingerprint": "Хурууны хээ", + "cert_algorithm": "Алгоритм", + "import_to_smime": "S/MIME", + "cert_already_imported": "S/MIME", + "cert_imported": "Сертификат S/MIME дэлгүүрт импортлогдсон", + "cert_import_failed": "Сертификат импортлож чадсангүй", + "no_contact_selected": "Дэлгэрэнгүйг харахын тулд харилцагчаа сонгоно уу", + "compose_email": "Имэйл бичих", + "copy_email": "Имэйл хуулах", + "copy_phone": "Утасны дугаарыг хуулах", + "copy_url": "URL хуулах", + "copied": " Түр санах ой руу хуулсан", + "copy_failed": " Түр санах ой руу хуулж чадсангүй", + "created": "Бүтээсэн", + "updated": "Сүүлд шинэчлэгдсэн", + "timezone": "Цагийн бүс", + "anniversary_birth": "Төрсөн өдөр", + "anniversary_death": "Нас барсан", + "anniversary_wedding": "жилийн ой", + "anniversary_other": "Бусад", + "personal_expertise": "Мэргэжил", + "personal_hobby": "Хобби", + "personal_interest": "Сонирхол", + "personal_other": "Бусад", + "gender": "Хүйс", + "gender_masculine": "Эрэгтэй", + "gender_feminine": "Эмэгтэй", + "gender_other": "Бусад", + "gender_none": "Хэрэглэхгүй", + "gender_unknown": "Тодорхойгүй", + "calendar": "Хуанли", + "calendar_uri": "Календарийн URL", + "scheduling_uri": "Хуваарь URL", + "freebusy_uri": "Үнэгүй/Завгүй URL", + "section_contact": "Холбоо барих мэдээлэл", + "section_work": "Ажил", + "section_personal": "Хувийн", + "email_default_label": "Имэйл", + "phone_default_label": "Утас", + "address_default_label": "Хаяг", + "online_service_default_label": "Онлайн", + "organization_label": "Байгууллага", + "title_label": "Гарчиг", + "role_label": "Үүрэг", + "language_label": "Хэл", + "related_default_label": "Холбогдох", + "more_actions": "Дэлгэрэнгүй үйлдэл", + "age_years": "{count, plural, one {1 настай} other {# настай}}", + "years_since": "{count, plural, one {1 жил} other {# жил}}" + }, + "activity": { + "recent_emails": "Саяхан ирсэн имэйлүүд", + "upcoming_events": "Удахгүй болох үйл явдлууд", + "no_emails": "Саяхны имэйл байхгүй", + "no_events": "Удахгүй болох арга хэмжээ байхгүй", + "no_subject": "(Сэдэв байхгүй)", + "no_title": "(Гарчиггүй)", + "load_failed": "Ачааж чадсангүй", + "unknown_sender": "Үл мэдэгдэх илгээгч", + "all_day": "Бүтэн өдөр" + }, + "form": { + "create_title": "Шинэ холбоо барих", + "edit_title": "Харилцагчийг засах", + "section_address_book": "Лавлах", + "select_address_book": "Лавлах сонгох...", + "section_identity": "Нэр & Хувийн мэдээлэл", + "contact_type": "Холбоо барих төрөл", + "type_person": "Хүн", + "type_organization": "Байгууллага", + "section_work": "Ажил, Байгууллага", + "prefix": "Угтвар", + "prefix_placeholder": "Доктор, ноён, хатагтай", + "given_name": "Нэр", + "middle_name": "Дундад нэр", + "surname": "Овог", + "suffix": "Дагавар", + "suffix_placeholder": "Бага, ах, III", + "nickname": "Хоч", + "nickname_placeholder": "Хоч", + "email": "И-мэйл", + "email_placeholder": "email@example.com", + "phone": "Утас", + "phone_placeholder": "+1 234 567 890", + "phone_type": "Төрөл", + "phone_voice": "Дуу хоолой", + "phone_cell": "Гар утас", + "phone_fax": "Факс", + "phone_pager": "Пэйжэр", + "phone_video": "Видео", + "phone_text": "Текст", + "organization": "Байгууллага", + "organization_placeholder": "Компанийн нэр", + "department": "Хэлтэс", + "department_placeholder": "Хэлтэс", + "job_title": "Ажлын байр", + "job_title_placeholder": "жишээ нь, Програм хангамжийн инженер", + "role": "Үүрэг", + "role_placeholder": "жишээ нь, багийн ахлагч", + "addresses": "Хаяг", + "add_address": "Хаяг нэмэх", + "street": "Гудамж", + "city": "Хот", + "region": "Муж/Бүс", + "postcode": "Шуудангийн код", + "country": "Улс", + "online_services": "Онлайн үйлчилгээ", + "add_online_service": "Онлайн үйлчилгээ нэмэх", + "url_placeholder": "https://...", + "service_placeholder": "Үйлчилгээ", + "anniversaries": "Тэмдэг ой", + "add_anniversary": "Огноо нэмэх", + "anniversary_birth": "Төрсөн өдөр", + "anniversary_wedding": "ойн өдөр", + "anniversary_death": "Дурсамж", + "anniversary_other": "Бусад", + "personal_info": "Хувийн мэдээлэл", + "add_personal_info": "Оруулга нэмэх", + "personal_info_placeholder": "жишээ нь, гэрэл зураг", + "personal_expertise": "Мэргэжлийн", + "personal_hobby": "Хобби", + "personal_interest": "Сонирхол", + "personal_other": "Бусад", + "level": "Түвшин", + "level_high": "Өндөр", + "level_medium": "Дунд", + "level_low": "Бага", + "categories": "Ангилал", + "categories_placeholder": "жишээ нь, гэр бүл, найз нөхөд, хамт ажиллагсад", + "categories_hint": "Ангилал нэмэх эсвэл хайхын тулд бичнэ үү", + "category_add": "Нэмэх", + "note": "Тэмдэглэл", + "note_placeholder": "Тэмдэглэл нэмэх...", + "gender": "Хүйс", + "gender_sex": "Секс", + "gender_male": "Эрэгтэй", + "gender_female": "Эмэгтэй", + "gender_other": "Бусад", + "gender_none": "Хэрэглэхгүй", + "gender_unknown": "Тодорхойгүй", + "gender_identity": "Жендэрийн баримжаа", + "gender_identity_placeholder": "Жендэрийн баримжаа...", + "calendar": "Хуанли", + "calendar_uri": "Календарийн URL", + "scheduling_uri": "Хуваарьлах URL", + "freebusy_uri": "Үнэгүй/Завгүй URL", + "context_work": "Ажил", + "context_private": "Хувийн", + "add_email": "Имэйл нэмэх", + "add_phone": "Утас нэмэх", + "save": "Хадгалах", + "cancel": "Цуцлах", + "creating": "Бүтээж байна...", + "updating": "Шинэчилж байна...", + "name_required": "Нэр, овог, байгууллагыг оруулна уу", + "email_invalid": "Хүчинтэй имэйл хаяг оруулна уу", + "email_error_inline": "Имэйл формат буруу", + "save_failed": "Харилцагчийг хадгалж чадсангүй", + "delete": "Устгах", + "upload_photo": "Зураг байршуулах", + "remove_photo": "Зураг арилгах", + "photo_hint": "JPG эсвэл PNG, 10 МБ хүртэл. Хэмжээ өөрчлөгдөнө.", + "photo_too_large": "Зураг хэт том байна (хамгийн ихдээ 10 MB)", + "photo_invalid": "Зургийн файл буруу", + "change_photo": "Өөрчлөлт" + }, + "groups": { + "create": "Шинэ групп", + "edit": "Засварлах групп", + "empty": "Одоохондоо бүлэг байхгүй", + "delete_confirm_title": "Бүлгийг устгах", + "delete_confirm": "Та энэ бүлгийг устгахдаа итгэлтэй байна уу?", + "name_label": "Бүлгийн нэр", + "name_placeholder": "жишээ нь, Баг, Гэр бүл", + "name_required": "Бүлгийн нэр шаардлагатай", + "save_failed": "Бүлгийг хадгалж чадсангүй", + "members_label": "Гишүүд", + "search_members": "Нэмэх харилцагчдыг хайх...", + "no_members": "Энэ бүлэгт гишүүн алга", + "member_count": "{count, plural, =0 {Гишүүд байхгүй} one {1 гишүүн} other {# гишүүн}}", + "send_email": "Групп руу имэйл илгээх", + "send_email_to": "руу", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "Энэ бүлэгт имэйл хаягтай гишүүн байхгүй." + }, + "import": { + "title": "Харилцагчдыг импортлох", + "drop_hint": "vCard файлыг сонгохын тулд товшино уу", + "file_types": ".vcf эсвэл .vcard файлууд", + "no_contacts": "Файлд харилцагч олдсонгүй", + "parse_error": "vCard файлыг задлан шинжилж чадсангүй", + "found": "{count, plural, one {1 харилцагч олдсон} other {# харилцагч олдсон}}", + "duplicate": "Давхардах", + "select_all": "Бүгдийг нь сонго", + "deselect_all": "Бүх сонголтыг цуцлах", + "selected": "{count, plural, one {1 сонгосон} other {# сонгосон}}", + "import_button": "Импорт", + "importing": "Импорт хийж байна...", + "success": "{count, plural, one {1 харилцагч импортлогдсон} other {# харилцагч импортлогдсон}}", + "failed": "Импорт амжилтгүй боллоо", + "close": "Хаах", + "file_too_large": "Файл хэт том байна (хамгийн ихдээ 5 MB)" + }, + "export": { + "title": "Харилцагчдыг экспортлох", + "success": "{count, plural, one {1 контакт экспортлогдлоо} other {# контакт экспортлогдлоо}}" + }, + "bulk": { + "selected": "{count, plural, one {1 сонгогдсон} other {# сонгогдсон}}", + "select_all": "Бүгдийг нь сонго", + "delete": "Устгах", + "delete_confirm_title": "Харилцагчдыг устгах", + "delete_confirm": "{count, plural, one {1 контакт} other {# контакт}} устгах уу?", + "deleted": "{count, plural, one {1 контакт устгагдлаа} other {# контакт устгагдлаа}}", + "add_to_group": "Бүлэгт нэмэх", + "choose_group": "Бүлэг сонгох", + "adding_contacts": "{count, plural, one {1 контакт} other {# контакт}} нэмэж байна", + "added_to_group": "Харилцагчдыг группт нэмсэн", + "export": "Экспорт хийх", + "clear": "Сонголтыг арилгах" + }, + "toast": { + "created": "Харилцагч үүсгэсэн", + "updated": "Харилцагч шинэчлэгдсэн", + "deleted": "Харилцагчийг устгасан", + "error_create": "Харилцагч үүсгэж чадсангүй", + "error_update": "Харилцагчийг шинэчилж чадсангүй", + "error_delete": "Харилцагчийг устгаж чадсангүй" + }, + "context_menu": { + "open": "Нээлттэй", + "edit": "Засварлах", + "send_email": "Имэйл илгээх", + "add_to_group": "Бүлэгт нэмэх", + "export_vcard": "vCard хэлбэрээр экспортлох", + "delete": "Устгах", + "call": "Дуудлага", + "duplicate": "Давхардах", + "print": "Хэвлэх" + }, + "filters": { + "toggle": "Шүүлтүүр", + "select": "Сонгох", + "clear": "Цэвэр", + "close": "Хаах", + "title": "Дэвшилтэт шүүлтүүрүүд", + "organization": "Компани", + "organization_placeholder": "жишээ нь. Acme Corp", + "job_title": "Ажлын байр", + "job_title_placeholder": "жишээ нь. Дизайнер", + "location": "Байршил", + "location_placeholder": "Хот эсвэл улс", + "email_domain": "Имэйл домэйн", + "email_domain_placeholder": "example.com", + "birthday_month": "Төрсөн өдөр", + "any_month": "Ямар ч сар", + "has_email": "Имэйл хаягтай", + "has_phone": "Утастай", + "has_photo": "Зурагтай", + "sort_by": "Эрэмбэлэх", + "sort_first_name": "Нэр", + "sort_last_name": "Овог" + } + }, + "calendar": { + "title": "Хуанли", + "back_to_email": "Имэйл рүү буцах", + "back_to_month": "Сар руу буцах", + "my_calendars": "Хуанли", + "birthday_calendar": "Төрсөн өдөр", + "mini_calendar_change": "Сарыг өөрчлөхийн тулд дарна уу", + "views": { + "month": "сар", + "week": "Долоо хоног", + "day": "Өдөр", + "agenda": "Хэлэлцэх асуудал", + "today": "Өнөөдөр", + "month_hint": "Сар (м)", + "week_hint": "Долоо хоног (w)", + "day_hint": "Өдөр (г)", + "agenda_hint": "Хэлэлцэх асуудал (a)", + "tasks": "Даалгаварууд", + "tasks_hint": "Даалгавар (k)" + }, + "events": { + "hidden_before": "{count, plural, other {Өмнөх # үйл явдал}}", + "hidden_after": "{count, plural, other {Дараах # үйл явдал}}", + "show_all_hours": "Бүх цагийг харуулах", + "show_display_hours": "Зөвхөн {start} – {end}-ийг харуулах", + "create": "Үйл явдал үүсгэх", + "edit": "Арга хэмжээг засах", + "delete": "Үйл явдлыг устгах", + "details": "Үйл явдлын дэлгэрэнгүй мэдээлэл", + "no_events": "Үйл явдал байхгүй", + "all_day": "Бүтэн өдөр", + "more": "+{count} дэлгэрэнгүй", + "show_more": "Илүү харуулах", + "show_less": "Бага харуулах", + "no_title": "(Гарчиггүй)", + "resize": "Үйл явдлын хэмжээг өөрчлөх", + "duplicate": "Давхардсан", + "today_header": "Өнөөдөр", + "tomorrow_header": "Маргааш", + "agenda_show_earlier": "Өмнөх үйл явдлуудыг харуулах", + "agenda_loading": "Ачаалж байна…", + "agenda_range_start": "{date}-с хойшхи үйл явдлууд", + "agenda_range_end": "{date} хүртэлх үйл явдлууд", + "export_ics": ".ics", + "copy_title": "Гарчиг хуулах", + "copy_link": "Уулзалтын холбоосыг хуулах", + "new_event": "Шинэ арга хэмжээ", + "new_all_day_event": "Бүтэн өдрийн шинэ арга хэмжээ", + "new_task": "Шинэ даалгавар", + "go_to_today": "Өнөөдөр рүү очих" + }, + "detail": { + "add_note": "Тэмдэглэл нэмэх...", + "save_note": "Хадгалах", + "note_saved": "Тэмдэглэл нэмсэн", + "open_link": "Нээлттэй холбоос", + "copy_location": "Байршлыг хуулах", + "meeting_link": "Уулзалтын холбоос", + "tentative": "Урьдчилсан", + "cancelled": "Цуцлагдсан", + "delete_confirm": "Энэ үйл явдлыг устгах уу?" + }, + "form": { + "title": "Гарчиг", + "description": "Тодорхойлолт", + "location": "Байршил", + "meeting_link": "Уулзалтын холбоос", + "start_date": "Эхлэх огноо", + "end_date": "Төгсгөлийн огноо", + "start_time": "Эхлэх цаг", + "end_time": "Төгсгөлийн цаг", + "all_day_event": "Бүтэн өдрийн арга хэмжээ", + "calendar_select": "Хуанли", + "save": "Хадгалах", + "cancel": "Цуцлах", + "delete_confirm": "Та энэ үйл явдлыг устгахдаа итгэлтэй байна уу?", + "color": "Өнгө" + }, + "participants": { + "availability": { + "title": "Боломжтой эсэх", + "free": "Боломжтой", + "busy": "Завгүй", + "tentative": "Урьдчилсан", + "unknown": "Энэ серверийн хэрэглэгч биш", + "checking": "Шалгаж байна…" + }, + "title": "Оролцогчид", + "add": "Оролцогч нэмэх", + "organizer": "Зохион байгуулагч", + "attendee": "Оролцогч", + "accepted": "Хүлээн зөвшөөрсөн", + "declined": "Татгалзсан", + "tentative": "Урьдчилсан", + "needs_action": "Арга хэмжээ авах шаардлагатай", + "remove": "Устгах", + "edit": "Засварлах", + "email_placeholder": "Имэйл хаяг нэмэх эсвэл харилцагч хайх", + "send_invitations": "Оролцогчдод урилга илгээх", + "status_summary": "{accepted} зөвшөөрсөн, {pending} хүлээгдэж байна", + "invited_by": "{name}", + "respond_below": "Доорх товчлууруудыг ашиглан хариу бичнэ үү", + "rsvp_label": "Таны хариулт", + "cancel_notification": "Оролцогчдод цуцлагдсан тухай мэдэгдэх болно", + "you_organizer": "Та бол зохион байгуулагч", + "you_attendee": "Та оролцогч байна", + "no_participants": "Оролцогчгүй", + "count": "{count, plural, one {# оролцогч} other {# оролцогч}}" + }, + "recurrence": { + "title": "Дахилт", + "none": "Дахин давтагдахгүй", + "daily": "Өдөр бүр", + "weekly": "Долоо хоног бүр", + "monthly": "Сарын", + "yearly": "Жил", + "every_n_days": "{count} өдөр бүр", + "every_n_weeks": "{count} долоо хоног бүр", + "every_n_months": "{count} сар бүр", + "until": "хүртэл", + "occurrences": "{count} тохиолдлууд", + "custom": "Захиалгат…", + "edit_custom": "Захиалгат дахилтыг засах", + "every_n_years": "{count} жил бүр", + "on_days": "{days}-ны өдөр", + "on_day_n": "өдөр {day}", + "on_the_nth": "{nth} {day}", + "in_month": "{month}", + "nth_1": "эхний", + "nth_2": "секунд", + "nth_3": "гурав дахь", + "nth_4": "дөрөвдүгээр", + "nth_last": "сүүлийн", + "editor_freq_day": "Өдөр", + "editor_freq_week": "Долоо хоног", + "editor_freq_month": "сар", + "editor_freq_year": "Жил", + "editor_repeats_on": "Дахин давтагдана", + "editor_every": "Бүр", + "editor_unit_days": "өдөр", + "editor_unit_weeks": "долоо хоног(ууд)", + "editor_unit_months": "сар", + "editor_unit_years": "жил", + "editor_on_day": "өдөр", + "editor_on_the": "", + "editor_in": "дахь", + "editor_ends": "Төгсгөл", + "editor_never": "Хэзээ ч", + "editor_ends_on": "Асаалттай", + "editor_ends_after": "Дараа", + "editor_occurrences": "тохиолдол" + }, + "recurrence_scope": { + "edit_title": "Давтах үйл явдлыг засах", + "delete_title": "Дахин давтагдах үйл явдлыг устгах", + "description": "Энэ бол давтагдах үйл явдал. Та ямар үйл явдлыг өөрчлөхийг хүсэж байна вэ?", + "this_event": "Зөвхөн энэ арга хэмжээ", + "this_and_future": "Энэ болон дараах үйл явдлууд", + "all_events": "Бүх арга хэмжээ", + "cancel": "Цуцлах", + "save": "Хадгалах", + "delete": "Устгах", + "rsvp_title": "Давтагдах үйл явдалд хариулах", + "rsvp_description": "Энэ бол давтагдах үйл явдал. Таны хариу аль үйл явдлуудад хамаарах вэ?", + "respond": "Хариулах" + }, + "alerts": { + "title": "Сануулгууд", + "none": "Сануулагч байхгүй", + "at_time": "Үйл явдал болох үед", + "minutes_before": "{count, plural, one {# минут өмнө} other {# минут өмнө}}", + "hours_before": "{count, plural, one {# цаг өмнө} other {# цаг өмнө}}", + "days_before": "{count, plural, one {# өдөр өмнө} other {# өдөр өмнө}}", + "weeks_before": "{count, plural, one {# долоо хоног өмнө} other {# долоо хоног өмнө}}", + "unit_minutes_before": "минутын өмнө", + "unit_hours_before": "цагын өмнө", + "unit_days_before": "хоногийн өмнө", + "unit_weeks_before": "долоо хоногийн өмнө", + "add": "Сануулагч нэмэх", + "remove": "Сануулагч устгах", + "amount": "Сануулагчийн хэмжээ", + "unit": "Сануулах нэгж", + "action": "Сануулгын төрөл", + "action_display": "Мэдэгдэл", + "action_email": "Имэйл", + "by_email": "{label} имэйлээр", + "kept": "{count, plural, one {Энд засах боломжгүй өөр # сануулга хадгалагдана} other {Энд засах боломжгүй өөр # сануулга хадгалагдана}}", + "other": "{count, plural, one {Өөр # сануулга} other {Өөр # сануулга}}", + "calendar_defaults": "Хуанлийн өгөгдмөл сануулгууд", + "customize": "Тохируулах", + "use_defaults": "Хуанлийн өгөгдмөл сануулгуудыг ашиглах" + }, + "settings": { + "limit_hours": "Харагдах цагийг хязгаарлах", + "limit_hours_desc": "Өдөр ба долоо хоногийн харагдацад зөвхөн эдгээр цагийг харуулна. Хүрээнээс гадуурх үйл явдлыг өдөр бүрийн дээд ба доод талын сумаар нээж болно.", + "visible_hours": "Харагдах цаг", + "visible_hours_start": "Харагдах цагийн эхлэл", + "visible_hours_end": "Харагдах цагийн төгсгөл", + "hide_non_working_days": "Ажлын бус өдрүүдийг нуух", + "hide_non_working_days_desc": "Ажиллахгүй өдрүүдээ долоо хоногийн харагдацаас хасна. Тэдгээр өдрийн үйл явдлууд сар, өдөр, хөтөлбөрийн харагдацад харагдсаар байна.", + "working_days": "Ажлын өдрүүд", + "title": "Календарийн тохиргоо", + "default_view": "Өгөгдмөл харагдах байдал", + "week_starts_on": "Долоо хоног", + "time_format": "Цагийн формат", + "default_calendar": "Өгөгдмөл календарь", + "default_reminder": "Өгөгдмөл сануулагч", + "time_format_12h": "12 цаг", + "time_format_24h": "24 цагийн", + "notifications_enabled": "Үйл явдлын мэдэгдэл", + "notifications_enabled_desc": "Удахгүй болох хуанлийн арга хэмжээний тухай сэрэмжлүүлэг харуулах", + "notification_sound": "Мэдэгдэлийн дуу", + "notification_sound_desc": "Календарийн дохиоллын дууг тоглуулах", + "invitation_parsing": "Имэйл урилгыг задлан шинжлэх", + "invitation_parsing_desc": "Имэйл хавсралтаас хуанлийн урилгыг илрүүлж, календарийн үйлдлийг харуулах", + "show_time_in_month_view": "Хугацааг сараар харуулах", + "show_time_in_month_view_desc": "Сарын хуанлийн харагдац дээр үйл явдлын цагийг харуулах. Жижиг дэлгэц дээр энэ нь цэгийн оронд үйл явдлын бүрэн оруулгуудыг харуулдаг.", + "show_week_numbers": "Долоо хоногийн дугаарыг харуулах", + "show_week_numbers_desc": "Ми-календарт долоо хоногийн тоог харуулах", + "calendar_free_scroll": "Чөлөөт гүйлгэлт", + "calendar_free_scroll_desc": "Нэг үеийг харуулахын оронд сар, долоо хоног, өдрүүдийг тасралтгүй гүйлгэх", + "enable_tasks": "Даалгавруудыг идэвхжүүлэх", + "enable_tasks_desc": "Хийх ажлуудыг удирдахын тулд календарьт ажлын харагдах байдлыг харуулах", + "show_tasks_on_calendar": "Хуанли дээр даалгавруудыг харуулах", + "show_tasks_on_calendar_desc": "Даалгаврыг сар, долоо хоног, өдрийн хуанлийн харагдац дээр харуулах", + "hover_preview": "Үйл явдлыг товшино уу", + "hover_preview_desc": "Календарийн үйл явдлууд дээр хулганыг гүйлгэх үед дэлгэрэнгүй поповыг харуулах", + "hover_preview_instant": "Шуурхай", + "hover_preview_delay_500ms": "0.5 секундын саатал", + "hover_preview_delay_1s": "1 секундын саатал", + "hover_preview_delay_2s": "2 секундын саатал", + "hover_preview_off": "Хөгжлийн бэрхшээлтэй", + "organizer_identity": "Урилгыг зохион байгуулагч", + "organizer_identity_desc": "Хүмүүсийг арга хэмжээнд урихад зохион байгуулагчаар ашиглах хуанлийн хаяг.", + "organizer_identity_failed": "Зохион байгуулагчийн хаягийг өөрчилж чадсангүй", + "show_birthday_calendar": "Холбоо барих төрсөн өдрийн хуанли", + "show_birthday_calendar_desc": "Харилцагчдаас төрсөн өдөр бүхий виртуал хуанли харуулах" + }, + "days": { + "monday": "Даваа гараг", + "tuesday": "Мягмар гараг", + "wednesday": "Лхагва гараг", + "thursday": "Пүрэв гараг", + "friday": "Баасан гараг", + "saturday": "Бямба гариг", + "sunday": "Ням гараг", + "mon": "Даваа", + "tue": "Мягмар", + "wed": "Лхагва", + "thu": "Пүрэв", + "fri": "Баасан", + "sat": "Бямба", + "sun": "Нар" + }, + "months": { + "jan": "1-р сар", + "feb": "Хоёрдугаар сар", + "mar": "3-р сар", + "apr": "4-р сарын", + "may": "Тавдугаар сар", + "jun": "6-р сар", + "jul": "7-р сар", + "aug": "Наймдугаар сар", + "sep": "9-р сар", + "oct": "10-р сарын", + "nov": "11-р сарын", + "dec": "12-р сар", + "far": "Фарвардин", + "ord": "Ордибэхэшт", + "kho": "Хордад", + "tir": "Тир", + "mor": "Мордад", + "sha": "Шахривар", + "meh": "Mehr", + "aba": "Абан", + "aza": "Азар", + "dey": "Дэй", + "bah": "Бахман", + "esf": "Эсфанд" + }, + "notifications": { + "event_created": "Үйл явдал үүсгэсэн", + "event_updated": "Үйл явдал шинэчлэгдсэн", + "event_deleted": "Үйл явдал устгагдсан", + "calendar_created": "Хуанли үүсгэсэн", + "calendar_deleted": "Хуанли устгасан", + "event_move_error": "Үйл явдлыг зөөж чадсангүй", + "event_resize_error": "Үйл явдлын хэмжээг өөрчилж чадсангүй", + "alert_title": "Удахгүй болох арга хэмжээ", + "alert_now": "Одооноос эхлэн", + "alert_in_minutes": "{count} мин", + "invitation_sent": "Урилга илгээсэн", + "invitations_denied_title": "Урилга илгээх боломжгүй", + "invitations_denied": "Сервер урилга илгээхээс татгалзлаа: {reason}. Урилга илгээхгүйгээр үйл явдлыг хадгалах уу?", + "save_without_invitations": "Урилгагүйгээр хадгалах", + "rsvp_updated": "Хариулт шинэчлэгдсэн", + "rsvp_error": "Хариултыг шинэчилж чадсангүй", + "event_duplicated": "Үйл явдал давхардсан", + "event_error": "Үйл явдлыг хадгалж чадсангүй", + "task_due": "Даалгавар дуусах хугацаа", + "event_exported": "Үйл явдал экспортлогдсон", + "title_copied": "Гарчиг хуулсан", + "link_copied": "Холбоос хуулсан" + }, + "status": { + "loading_calendars": "Хуанли ачаалж байна...", + "loading_events": "Арга хэмжээг ачаалж байна..." + }, + "quick_create": { + "placeholder": "Шинэ үйл явдлын гарчиг", + "aria_label": "Түргэн үүсгэх арга хэмжээ", + "add_title": "Гарчиг нэмэх", + "more_options": "Бусад тохиргоо" + }, + "nav_prev": "Өмнөх", + "nav_next": "Дараагийн", + "nav_open_menu": "Нээлттэй цэс", + "import": { + "title": "Хуанли импортлох", + "tab_file": "Файл", + "tab_url": "URL", + "select_file": ".ics файлыг сонгоно уу", + "drop_file": "эсвэл файлыг энд хаяна уу", + "supported_formats": "iCalendar (.ics) файлуудыг дэмждэг", + "url_description": "Арга хэмжээг импортлохын тулд гадаад iCalendar (.ics) хангамжийн URL хаягийг оруулна уу.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "iCalendar (.ics) URL-г дэмждэг", + "fetch": "Авах", + "invalid_url": "Хүчинтэй URL оруулна уу", + "url_fetch_failed": "URL", + "parsing": "Календарийн файлыг задлан шинжилж байна...", + "parsed_events": "{count} үйл явдал олдсон", + "no_events": "Файлд ямар ч үйл явдал олдсонгүй", + "select_all": "Бүгдийг нь сонго", + "deselect_all": "Бүгдийг нь сонгоогүй", + "target_calendar": "Хуанли руу импортлох", + "import_button": "Импортыг сонгосон", + "importing": "Үйл явдал импортлож байна...", + "success": "{count} арга хэмжээг амжилттай импортолсон", + "error": "Хуанли импорт хийж чадсангүй", + "file_too_large": "Файл 10МБ-ын хязгаараас хэтэрсэн байна", + "invalid_format": "Хуанли файлын формат буруу байна" + }, + "webcal_action": { + "title": "Календарийн холбоосыг нээх", + "description": "Та \"{name}\"-г хэрхэн ашиглахыг хүсэж байна вэ?", + "import_title": "Нэг удаа импортлох", + "import_description": "Үйл явдлуудыг яг одоо татаж аваад хуанлиныхаа аль нэгэнд хуулна уу.", + "subscribe_title": "Захиалах", + "subscribe_description": "Энэ календарийг автоматаар тусдаа календарь болгон синк хий.", + "cancel": "Цуцлах" + }, + "management": { + "title": "Календарийн менежмент", + "description": "Хуанлигаа үүсгэх, нэрийг нь өөрчлөх, өөрчлөх. Хажуугийн самбар дээрх хуанли дээр хулганы баруун товчийг дарж өнгийг нь хурдан солино.", + "name": "Нэр", + "name_placeholder": "Календарийн нэр", + "color": "Өнгө", + "change_color": "Өнгийг өөрчлөх", + "random_color": "Шинэ санамсаргүй өнгө", + "add_calendar": "Хуанли нэмэх", + "edit": "Засварлах", + "delete": "Устгах", + "save": "Хадгалах", + "create": "Бүтээх", + "cancel": "Цуцлах", + "default": "Өгөгдмөл", + "confirm_delete": "\"{name}\"-г устгах уу? Энэ хуанли дахь бүх үйл явдлууд устах болно.", + "confirm_clear": "\"{name}\"-с бүх үйл явдлыг арилгах уу? Үүнийг буцаах боломжгүй.", + "set_default": "Өгөгдмөлөөр тохируулах", + "default_updated": "Өгөгдмөл календарь шинэчлэгдсэн", + "error_default": "Өгөгдмөл календарийг тохируулж чадсангүй", + "clear_events": "Тодорхой үйл явдлууд", + "events_cleared": "{count} үйл явдлууд арилсан", + "error_clear": "Календарийн үйл явдлуудыг арилгаж чадсангүй", + "calendar_created": "Хуанли үүсгэсэн", + "calendar_updated": "Хуанли шинэчлэгдсэн", + "calendar_deleted": "Хуанли устгасан", + "color_updated": "Календарийн өнгө шинэчлэгдсэн", + "error_create": "Хуанли үүсгэж чадсангүй", + "error_update": "Хуанли шинэчлэгдэж чадсангүй", + "error_delete": "Календарийг устгаж чадсангүй", + "caldav_url": "CalDAV URL", + "copy_url": "CalDAV URL-г хуулах", + "url_copied": "CalDAV URL-г санах ой руу хуулсан", + "share": "Хуанлигаа хуваалцах", + "new_event_in_calendar": "Энэ хуанли дахь шинэ арга хэмжээ", + "shared_with": "Хуваалцсан: {names}", + "shared_with_count": "{count, plural, one {1 хүнтэй хуваалцсан} other {# хүнтэй хуваалцсан}}", + "move_up": "Дээш хөдлөх", + "move_down": "Доошоо шилжих", + "kind": "Агуулга", + "kind_events": "Үйл явдлууд", + "kind_tasks": "Даалгаврууд", + "kind_both": "Үйл явдал болон даалгаврууд", + "kind_hint": "CalDAV-аар синк хийхэд энэ хуанлийг аль аппууд харахыг тодорхойлно. Дараа нь өөрчлөх боломжгүй.", + "default_alerts": "Өгөгдмөл сануулгууд", + "default_alerts_title": "\"{name}\"-ийн өгөгдмөл сануулгууд", + "default_alerts_hint": "Энэ хуанлийн шинэ үйл явдлууд үйл явдал дотор нь өөрчлөхгүй бол эдгээр сануулгыг авна. Энд өөрчлөх нь одоо байгаа үйл явдлуудад нөлөөлөхгүй. Имэйл сануулгыг мэйл сервер илгээнэ.", + "default_alerts_timed": "Цагтай үйл явдлууд", + "default_alerts_all_day": "Бүтэн өдрийн үйл явдлууд", + "default_alerts_updated": "Өгөгдмөл сануулгууд шинэчлэгдлээ" + }, + "subscription": { + "title": "iCal захиалга", + "section_title": "iCal захиалга", + "description": "Гадны iCalendar хангамжид бүртгүүлнэ үү. Үйл явдлууд автоматаар өөрсдийн календарьт синк хийгдэнэ. https:// болон webcal:// URL-г дэмждэг.", + "url_label": "Календарийн URL", + "url_placeholder": "https://example.com/calendar.ics эсвэл webcal://...", + "name_label": "Календарийн нэр", + "name_placeholder": "жишээ нь. Бүх нийтийн амралтын өдрүүд", + "color_label": "Өнгө", + "refresh_interval": "Сэргээх интервал", + "interval_15": "15 минут тутамд", + "interval_30": "30 минут тутамд", + "interval_60": "Цаг бүр", + "interval_360": "6 цаг тутамд", + "interval_1440": "Өдөр бүр", + "subscribe": "Захиалга өгөх", + "subscribing": "Захиалгаж байна...", + "save": "Өөрчлөлтийг хадгалах", + "saving": "Хадгалж байна...", + "edit": "Засварлах", + "edit_title": "Захиалга засах", + "updated": "Шинэчилсэн \"{name}\"", + "update_error": "Бүртгэлийг шинэчилж чадсангүй", + "invalid_url": "Хүчинтэй URL оруулна уу", + "success": "\"{name}\"-д бүртгүүлсэн", + "error": "Захиалга нэмж чадсангүй", + "refresh": "Одоо сэргээнэ үү", + "refresh_success": "Захиалга шинэчлэгдсэн", + "refresh_error": "Захиалгыг сэргээж чадсангүй", + "unsubscribe": "Бүртгэлээ цуцлах", + "confirm_delete": "\"{name}\"-н бүртгэлээ цуцлах уу? Хуанли болон түүний бүх үйл явдлыг устгах болно.", + "deleted": "Захиалга хасагдсан", + "delete_error": "Захиалга устгаж чадсангүй", + "last_refreshed": "Сүүлд шинэчлэгдсэн: {time}" + }, + "tasks": { + "label": "Даалгаварууд", + "no_tasks": "Даалгавар байхгүй", + "no_title": "(Гарчиггүй)", + "mark_complete": "Бүрэн гэж тэмдэглэх", + "mark_incomplete": "Дутуу гэж тэмдэглэх", + "filter_all": "Бүгд", + "filter_pending": "Хүлээгдэж байна", + "filter_completed": "Дууссан", + "filter_overdue": "Хугацаа хэтэрсэн", + "show_completed": "Үзэсгэлэн дууссан", + "create": "Шинэ даалгавар", + "edit": "Даалгавар засах", + "title_placeholder": "Даалгаврын гарчиг", + "description_placeholder": "Тодорхойлолт нэмэх...", + "due_date": "Хугацаа дуусах хугацаа", + "include_time": "Цаг оруулах", + "priority": "Тэргүүлэл", + "priority_none": "Байхгүй", + "priority_high": "Өндөр", + "priority_medium": "Дунд", + "priority_low": "Бага", + "progress": "Төлөв", + "progress_needs_action": "Арга хэмжээ авах шаардлагатай", + "progress_in_process": "Үйл ажиллагаа явагдаж байна", + "progress_completed": "Дууссан", + "progress_cancelled": "Цуцлагдсан", + "calendar": "Хуанли", + "alert": "Сануулга", + "alert_none": "Байхгүй", + "alert_at_time": "Хугацаа дуусахаар", + "alert_5min": "5 минутын өмнө", + "alert_15min": "15 минутын өмнө", + "alert_30min": "30 минутын өмнө", + "alert_1hr": "1 цагийн өмнө", + "alert_1day": "1 өдрийн өмнө", + "delete": "Устгах", + "cancel": "Цуцлах", + "save": "Хадгалах", + "quick_add_placeholder": "Даалгавар нэмэх...", + "due_today": "Өнөөдөр", + "due_tomorrow": "Маргааш", + "overdue": "Хугацаа хэтэрсэн" + }, + "toolbar": { + "more": "Бусад үйлдэл", + "select_view": "Харагдац солих, одоо {view}" + }, + "other_calendars": "Бусад хуанли", + "calendar_options": "{name}-ийн тохиргоо" + }, + "calendar_event_notifications": { + "someone": "Хэн нэгэн", + "invited": "{name} таныг \"{title}\"-д урилаа", + "updated": "{name} \"{title}\"-г шинэчиллээ", + "cancelled": "{name} \"{title}\"-г цуцаллаа", + "untitled": "Нэргүй арга хэмжээ" + }, + "share_notifications": { + "someone": "Хэн нэгэн", + "shared": "{name} танд {kind} \"{object}\"-г хуваалцлаа", + "changed": "{name} таны {kind} \"{object}\"-д хандах эрхийг өөрчиллөө", + "revoked": "{name} таны {kind} \"{object}\"-д хандах эрхийг хаслаа", + "object": { + "folder": "хавтас", + "calendar": "хуанли", + "address_book": "хаягийн дэвтэр", + "files": "файлын хавтас" + } + }, + "sharing": { + "title": "\"{name}\"-г хуваалцаарай", + "description": "Энэ сервер дээрх бусад хэрэглэгчид эсвэл бүлгүүдэд хандах эрх олгох. Өөрчлөлтүүд шууд хүчин төгөлдөр болно.", + "no_shares": "Одоохондоо хэнтэй ч хуваалцаагүй.", + "add_person": "Хүн эсвэл бүлэг нэмэх", + "search_placeholder": "Нэр эсвэл имэйлээр хайх...", + "loading_principals": "Хэрэглэгчдийг ачаалж байна…", + "no_principals": "Өөр хэрэглэгч болон групп олдсонгүй.", + "no_match": "Тохирох зүйл алга.", + "remove": "Хандалтыг арилгах", + "group": "Бүлэг", + "share_added": "Хандалт олгосон", + "share_updated": "Хандалт шинэчлэгдсэн", + "share_removed": "Хандалт хасагдсан", + "share_failed": "Хуваалцалтыг шинэчилж чадсангүй", + "preset": { + "freeBusy": "Үнэгүй/зөвхөн завгүй", + "read": "Зөвхөн унших", + "readWrite": "Унших, бичих", + "manager": "Менежер", + "custom": "Захиалгат" + } + }, + "advanced_search": { + "title": "Нарийвчилсан хайлт", + "from": "-аас", + "from_placeholder": "Илгээгчийн имэйл эсвэл нэр", + "to": "Хэнд", + "to_placeholder": "Хүлээн авагчийн имэйл эсвэл нэр", + "subject": "Сэдэв", + "subject_placeholder": "Сэдвэд...", + "body": "Бие", + "body_placeholder": "Биед агуулагдах...", + "folder": "Хавтас", + "all_folders": "Бүх хавтаснууд", + "all_folders_except_spam_trash": "Спам болон Хогийн савнаас бусад бүх хавтас", + "has_attachment": "Хавсралт", + "date_after": "Дараа", + "date_before": "Өмнө нь", + "size_min": "Ихээс (KB)", + "size_max": "Багаас (KB)", + "starred": "Одтой", + "unread": "Уншаагүй", + "read": "унших", + "yes": "Тийм", + "no": "Үгүй", + "clear": "Цэвэр", + "clear_all": "Бүгдийг арилгах", + "filters_active": "{count} шүүлтүүр", + "filters_active_plural": "{count} шүүлтүүрүүд", + "toggle_filters": "Илүү", + "search_hint": "Нарийвчилсан хайлт хийхийн тулд дэвшилтэт шүүлтүүр ашиглах", + "advanced_filters_tooltip": "Дэвшилтэт хайлтын шүүлтүүрүүд", + "results_found": "{count, plural, =0 {Үр дүн олдсонгүй} one {# үр дүн олдлоо} other {# үр дүн олдлоо}}", + "results_found_more": "{count}+ илэрц олдлоо", + "suggestions_recent": "Сүүлийн хайлтууд", + "suggestions_people": "Хүмүүс", + "suggestions_from": "{address}-аас ирсэн захидал хайх", + "suggestions_to": "{address} руу илгээсэн захидал хайх", + "suggestions_remove_recent": "Сүүлийн хайлтуудаас хасах", + "has_words": "Агуулсан үгс", + "size": "Хэмжээ", + "date_within": "Огноо", + "search_in": "Хайх газар", + "has_attachment_check": "Хавсралттай", + "search": "Хайх" + }, + "welcome": { + "title": "Таны шуудангийн хайрцагт тавтай морил", + "tip_compose": "Шинэ имэйл бичихийн тулд c товчийг дарна уу", + "tip_shortcuts": "Дарах уу? бүх гарын товчлолыг харахын тулд", + "tip_sidebar": "Хажуугийн цэснээс Хуанли, Харилцагчид болон Тохиргоог олоорой", + "tip_settings": "Тохиргооноос өөрийн туршлагаа тохируулна уу", + "tip_compose_touch": "Шинэ имэйл бичихийн тулд харандаа товчийг дарна уу", + "tip_sidebar_touch": "Хуанли, Харилцагчид болон Тохиргоо доод самбарт байна", + "got_it": "Ойлголоо", + "settings": "Тохиргоо", + "dismiss": "Хаах", + "start_tour": "Аялал эхлэх" + }, + "demo_welcome": { + "title": "Bulwark Mail", + "description": "Бүрэн боломжит вэбмэйл клиентийг шууд хөтөч дээрээ судлаарай. Бүх өгөгдөл таны төхөөрөмж дээр үлдэх тул бүх зүйлийг туршиж үзээрэй.", + "feature_email": "Унших, имэйл бичих", + "feature_organize": "Таг, од, хавтас", + "feature_shortcuts": "Гарын товчлол", + "feature_privacy": "100% хувийн демо", + "hint": "Эхлэхийн тулд зүүн талд байгаа дурын имэйл дээр дарж эсвэл доорх аялалд хамрагдаарай." + }, + "files": { + "office_edit": "Баримт засах", + "office_loading": "Баримтын засварлагчийг нээж байна…", + "office_error": "Баримтын засварлагчийг нээж чадсангүй.", + "office_close": "Засварлагчийг хаах", + "open_in_new_tab": "Шинэ табаар нээх", + "title": "Файл", + "search_placeholder": "Файл хайх...", + "empty_state_title": "Одоохондоо файл алга", + "upload_files": "Файл байршуулах", + "new_folder": "Шинэ хавтас", + "new_folder_name": "Хавтасны нэр", + "rename": "Нэрээ өөрчлөх", + "rename_title": "Нэрээ өөрчлөх", + "new_name": "Шинэ нэр", + "name_forbidden_chars": "Сервер нэрэнд эдгээр тэмдэгтийг зөвшөөрдөггүй: {chars}", + "name_reserved": "Энэ нэр сервер дээр нөөцлөгдсөн.", + "delete": "Устгах", + "delete_confirm_title": "Нөөцийг устгах", + "delete_confirm_message": "Та \"{name}\"-г устгахдаа итгэлтэй байна уу? Үүнийг буцаах боломжгүй.", + "download": "Татаж авах", + "name": "Нэр", + "size": "Хэмжээ", + "modified": "Өөрчлөгдсөн", + "type": "Төрөл", + "folder": "Хавтас", + "file": "Файл", + "no_accounts": "Холбогдсон бүртгэл байхгүй.", + "uploading": "Байршуулж байна...", + "upload_success": "{count, plural, one {1 файл дээш ачигдлаа} other {# файл дээш ачигдлаа}}", + "upload_canceled": "Байршуулалт цуцлагдлаа", + "upload_canceled_partial": "Цуцлахаас өмнө {count, plural, one {1 файл} other {# файл}} байршуулагдсан", + "upload_error": "Файл байршуулж чадсангүй", + "create_folder_success": "Хавтас үүсгэсэн", + "create_folder_error": "Хавтас үүсгэж чадсангүй", + "delete_success": "Амжилттай устгасан", + "delete_error": "Устгаж чадсангүй", + "rename_success": "Нэрээ амжилттай өөрчилсөн", + "rename_error": "Нэрээ өөрчилж чадсангүй", + "download_error": "Татаж авч чадсангүй", + "not_available": "Энэ сервер дээр файл хадгалах боломжгүй", + "cancel": "Цуцлах", + "create": "Үүсгэх", + "save": "Хадгалах", + "no_results": "Таны хайлтад тохирох файл алга", + "batch_delete_confirm_message": "{count, plural, one {1 элемент} other {# элемент}} устгахыг баталгаажуулж байна уу? Энэ үйлдлийг буцаах боломжгүй.", + "batch_delete_success": "{count, plural, one {1 элемент устгагдлаа} other {# элемент устгагдлаа}}", + "grid_view": "Сүлжээ харах", + "list_view": "Жагсаалт харах", + "details": "Дэлгэрэнгүй мэдээлэл", + "path": "Зам", + "preview": "Урьдчилан харах", + "preview_error": "Урьдчилан үзэхийг ачаалж чадсангүй", + "cut": "Таслах", + "copy": "Хуулбар", + "paste": "Наах", + "move_success": "{count, plural, one {1 зүйл зөөгдлөө} other {# зүйл зөөгдлөө}}", + "move_error": "Хөдөлж чадсангүй", + "paste_success": "Амжилттай буулгалаа", + "paste_error": "Буулгаж чадсангүй", + "new_text_file": "Шинэ текст файл", + "file_name": "Файлын нэр", + "retry": "Дахин оролдох", + "refresh": "Сэргээх", + "duplicate": "Хуулбарлах", + "duplicate_success": "Амжилттай хуулбарлалаа", + "duplicate_error": "Хуулбарлаж чадсангүй", + "share": "Хуваалцах", + "shared": "Хуваалцсан", + "shared_with_me": "Надтай хуваалцсан", + "shared_by": "{name}", + "create_file_success": "Файл үүсгэсэн", + "create_file_error": "Файлыг үүсгэж чадсангүй", + "favorites": "Дуртай зүйлс", + "recent": "Саяхан", + "show_in_folder": "Хавтсанд харуулах", + "remove_from_recent": "Саяхныхаас хасах", + "upload_folder": "Хавтас байршуулах", + "file_too_large": "\"{name}\" нь файлын дээд хэмжээ ({max})-ээс их байна", + "undo": "Цуцлах", + "undo_success": "Үйлдэл цуцлагдлаа", + "undo_error": "Буцааж чадсангүй", + "toolbar": "Файл үйлдлүүд", + "open_folder_tree": "Хавтсын модыг нээх", + "file_list": "Файл ба хавтас", + "context_menu": "Үйлдлүүд", + "settings_ascending": "Өсөх", + "settings_descending": "Буурах", + "disabled_title": "Файлын боломжийг админ идэвхгүй болгосон", + "disabled_description": "WebDAV-ээр том файл байршуулах нь Stalwart/RocksDB-ийг тогтворгүй болгож, санах ойн доголдол эсвэл сэргээхэд хүндрэлтэй диск дүүргэлт үүсгэж болзошгүй. Устгасан файлууд blob хадгалалтаас шууд арилахгүй байж болно. Иймээс энэ боломжийг үйлдвэрлэлийн орчинд ашиглахыг зөвлөдөггүй.", + "migration_title": "Таны файлуудыг шинэчилж байна…", + "migration_description": "Хавтас болон файлуудыг зөв бүтэц рүү нь зохион байгуулж байна. Энэ нь зөвхөн нэг удаа хийгдэнэ.", + "new": "Шинэ", + "my_files": "Миний файлууд", + "folders": "Хавтаснууд", + "files_heading": "Файлууд", + "open": "Нээх", + "star": "Од тавих", + "unstar": "Одыг авах", + "expand_folder": "{name}-г дэлгэх", + "collapse_folder": "{name}-г хураах", + "sidebar_hint": "Хавтсанд од тавьж, файл нээвэл энд харагдана.", + "breadcrumbs": "Хавтасны зам", + "show_path": "Замд нуугдсан хавтсуудыг харуулах", + "selection_actions": "Сонгосон зүйлсийн үйлдлүүд", + "selected_count": "{count, plural, one {1 сонгогдсон} other {# сонгогдсон}}", + "clear_selection": "Сонголтыг арилгах", + "select_all": "Бүгдийг сонгох", + "select_item": "{name}-г сонгох", + "more_actions": "{name}-д зориулсан бусад үйлдэл", + "paste_here": "Энэ хавтсанд наах", + "paste_count": "{count, plural, one {1 зүйл наах} other {# зүйл наах}}", + "sort_by": "Эрэмбэлэх", + "view_mode": "Харагдац", + "clear_search": "Хайлтыг арилгах", + "no_results_for": "Энэ хавтсанд \"{query}\"-д тохирох зүйл алга", + "download_count": "{count, plural, one {1 файл татах} other {# файл татах}}", + "delete_count": "{count, plural, one {1 зүйл устгах} other {# зүйл устгах}}", + "close_details": "Дэлгэрэнгүйг хаах", + "file_kind": "{ext} файл", + "folder_summary": "{folders, plural, =0 {Хавтас алга} one {1 хавтас} other {# хавтас}}, {files, plural, =0 {файл алга} one {1 файл} other {# файл}}", + "details_hint": "Дэлгэрэнгүйг харахын тулд файл эсвэл хавтас сонгоно уу.", + "sharing": "Хуваалцах", + "shared_with_count": "{count, plural, one {1 хүнтэй хуваалцсан} other {# хүнтэй хуваалцсан}}", + "only_you": "Зөвхөн та", + "empty_folder_title": "Энэ хавтас хоосон байна", + "empty_folder_description": "Файлаа энд чирж оруулах эсвэл төхөөрөмжөөсөө байршуулна уу.", + "empty_folder_read_only": "Та энэ хавтсанд файл нэмэх боломжгүй.", + "choose_account": "Бүртгэл сонгох", + "drop_to_upload": "{folder} руу байршуулахын тулд энд тавина уу", + "uploading_count": "{total}-аас {current}-г байршуулж байна", + "dismiss": "Хаах", + "zoom_in": "Томруулах", + "zoom_out": "Жижигрүүлэх", + "zoom_reset": "Дэлгэцэнд тааруулах", + "rotate": "Эргүүлэх", + "close_preview": "Хаах" + }, + "smime": { + "your_certificates": "Таны сертификатууд", + "your_certificates_desc": "Имэйлд гарын үсэг зурах болон шифрлэхэд ашиглах S/MIME сертификатаа импортолж, удирдана", + "recipient_certificates": "Хүлээн авагчийн сертификатууд", + "recipient_certificates_desc": "Хүлээн авагч руу шифрлэсэн имэйл илгээхэд ашиглах нийтийн сертификатууд", + "identity_bindings": "Таниулбарын түлхүүрийн холболтууд", + "identity_bindings_desc": "S/MIME сертификатыг өөрийн имэйл таниулбаруудтай холбох", + "defaults_title": "Анхдагчууд", + "defaults_desc": "Гарын үсэг болон шифрлэлтийн анхдагч үйлдлийг тохируулах", + "import_pkcs12": "PKCS#12 импортлох (.p12/.pfx)", + "import_public_cert": "Сертификат импортлох", + "no_certificates": "Сертификатууд импортлогдсонгүй", + "no_recipient_certs": "Хүлээн авагчийн сертификатууд байхгүй", + "expires": "Дуусах огноо", + "expired": "Дуусагдлаа", + "bound_to": "Холбогдсон", + "no_key_bound": "Холбогдоогүй", + "lock": "Түлхүүр түгжих", + "unlock": "Түлхүүр нээх", + "details": "Дэлгэрэнгүй үзэх", + "delete": "Устгах", + "encrypt_by_default": "Анхдагчаар шифрлэх", + "encrypt_by_default_desc": "Бүх хүлээн авагчийн сертификат бэлэн үед имэйлийг автоматаар шифрлэх", + "remember_unlocked": "Нээгдсэн түлхүүрийг санах", + "remember_unlocked_desc": "Энэ хөтчийн сессийн хугацаанд түлхүүрийг нээлттэй байлгах", + "sign_default_for": "Анхдагчаар гарын үсэг зурах", + "enter_p12_passphrase": "PKCS#12 Нууцын үг оруулах", + "p12_passphrase_desc": "Энэ сертификатын файлыг хамгаалдаг нууцын үг оруулна уу", + "enter_storage_passphrase": "Хадгалалтын нууцын үг оруулах", + "storage_passphrase_desc": "Энэ түлхүүрийг хөтөчид хадгалахдаа хамгаалах нууц үг сонгоно уу", + "next": "Дараа", + "import": "Импортлох", + "unlock_key": "Түлхүүр нээх", + "unlock_key_desc": "Гарын үсэг зурах эсвэл тайлахын тулд хадгалалтын нууц үгээ оруулж түлхүүрээ нээнэ үү", + "passphrase_placeholder": "Нууцын үг оруулна уу", + "confirm_passphrase_placeholder": "Нууцын үгийг баталгаажуулна уу", + "passphrase_mismatch": "Нууцын үг таарахгүй байна", + "cancel": "Цуцлах", + "processing": "Боловсруулж байна…", + "close": "Хаах", + "certificate_details": "Сертификатын дэлгэрэнгүй", + "cert_subject": "Эзэмшигч", + "cert_issuer": "Гаргагч", + "cert_email": "Имэйл", + "cert_serial": "Цуврал дугаар", + "cert_validity": "Хүчин төгөлдөр байдал", + "cert_fingerprint": "Хурууны хээ (SHA-256)", + "cert_algorithm": "Алгоритм", + "cert_capabilities": "Чадавхууд", + "cert_source": "Эх сурвалж", + "cert_expired": "Энэ сертификат дуусагдлаа", + "cert_not_yet_valid": "Энэ сертификат хараахан хүчинтэй болоогүй байна", + "cap_sign": "Гарын үс", + "cap_encrypt": "Шифрлэлт", + "cap_none": "Байхгүй", + "show_passphrase": "Нууцын үгийг харуулах", + "hide_passphrase": "Нууцын үгийг нуух", + "sign_toggle": "Гарын үс", + "encrypt_toggle": "Шифрлэх", + "missing_recipient_certs": "{emails}-д сертификатууд байхгүй", + "missing_sender_cert": "Энэ таниулбарт холбогдсон сертификат байхгүй", + "status_encrypted_ok": "Энэ зурвас шифрлэгдсэн байна", + "status_encrypted_no_key": "Энэ зурвас шифрлэгдсэн боловч тохирох түлхүүр олдсонгүй", + "status_encrypted_failed": "Энэ зурвасыг тайлж чадсангүй", + "status_signed_valid": "Гарын үс баталгаажуулагдлаа", + "status_signed_invalid": "Гарын үсийн баталгаажуулалт бүтэлгүйтэв", + "status_signed_expired_cert": "Дууссан сертификатаар гарын үсэг зурсан байна", + "status_signed_self_signed": "Гарын үсэг хүчинтэй боловч сертификат нь self-signed тул итгэмжлэгдээгүй", + "status_signed_mismatch": "Гарын үсэг хүчинтэй боловч гарын үсэг зурсан этгээд илгээгчтэй таарахгүй", + "status_unsupported": "Дэмжихгүй S/MIME формат", + "auto_import_signer_certs": "Гарын үсэг зурсан сертификатыг автоматаар импортлох", + "auto_import_signer_certs_desc": "Баталгаажсан гарын үсэгтэй имэйлээс сертификатыг автоматаар хадгалж, дараагийн шифрлэлтийн үед ашиглах", + "export": "Экспортлох", + "enter_export_passphrase": "Экспортын нууцын үг оруулах", + "export_passphrase_desc": "Экспортлогдсон PKCS#12 файлыг хамгаалдаг нууцын үг сонгоно уу", + "export_storage_desc": "Экспортын өмнө түлхүүрийг тайлахын тулд хадгалалтын нууц үгээ оруулна уу", + "incorrect_passphrase": "Буруу нууцын үг" + }, + "tour": { + "step_counter": "{current}-р алхам {total}-аас", + "skip": "Аялалыг алгасах", + "back": "Буцаа", + "next": "Дараа", + "finish": "Дуусгах", + "take_a_tour": "Интерфэйсийн аяллыг эхлүүлнэ үү", + "restart_title": "Оршил аялал", + "restart_desc": "Интерфэйсийн танилцах аяллыг дахин үзэх", + "restart_button": "Аялалыг дахин эхлүүлэх", + "show_on_new_devices_title": "Шинэ төхөөрөмжүүдэд харуулах", + "show_on_new_devices_desc": "Шинэ төхөөрөмж дээр анх нэвтрэхэд мэндчилгээ болон аяллыг дахин харуулна, өмнө нь дуусгасан байсан ч гэсэн", + "sidebar_title": "Таны шуудангийн хайрцгууд", + "sidebar_desc": "Энэ бол хавтасны хажуугийн самбар. Аль ч хавтсыг дарж имэйлүүдийг нь үзэж болно. Мөн шинэ хавтас үүсгэх, имэйлийг чирж зөөх, уншаагүй тоог нэг дор харах боломжтой.", + "compose_title": "Имэйл зохиох", + "compose_desc": "Шинэ имэйл бичихийн тулд энд дарна уу. Хүлээн авагч нэмэх, файл хавсаргах, баялаг текст формат ашиглах боломжтой.", + "search_title": "Таны имэйлийг хайх", + "search_desc": "Илгээгч, сэдэв, эсвэл агуулгаар хайж болно. Шүүлтүүрийн дүрсээр огноо, хавсралт, одтой имэйл зэрэг нарийвчилсан сонголтыг нээнэ.", + "email_list_title": "Таны имэйлийн жагсаалт", + "email_list_desc": "Имэйлүүд энд харагдана. Нэгийг нь дарж баруун талд уншина уу. Олон имэйл сонгоод бөөнөөр нь зөөх, устгах, шошголох боломжтой.", + "email_viewer_title": "Уншилтын самбар", + "email_viewer_desc": "Сонгосон имэйл энд нээгдэнэ. Дээрх хэрэгслийн мөрөөс хариулах, дамжуулах, архивлах, эсвэл устгах боломжтой. Мөн од тавих, өнгийн шошго нэмэх боломжтой.", + "keywords_title": "Өнгөний шошгууд", + "keywords_desc": "Өнгөөр ялгасан шошго ашиглан имэйлээ зохион байгуулна уу. Имэйлийг шошгон дээр чирэх эсвэл баруун товшоод шошго өгч болно.", + "calendar_title": "Хуанли", + "calendar_desc": "Арга хэмжээгээ удирдахын тулд хуанли руу шилжинэ үү. Шинэ арга хэмжээ үүсгэх, сануулга тохируулах, өдөр, долоо хоног, сар харагдацаар үзэх боломжтой.", + "contacts_title": "Контактууд", + "contacts_desc": "Таны хаягийн дэвтэр энд байрлана. Харилцагч импортлох, бүлэг үүсгэх, аль ч харилцагчийн дэлгэрэнгүйг нээх боломжтой.", + "settings_title": "Тохиргоо", + "settings_desc": "Сэдэв, нягтрал, гарын үсэг, шүүлтүүр, товчлол, хуанлийн анхдагч утга зэрэг олон тохиргоог эндээс өөрчилж болно.", + "shortcuts_title": "Гарын товчлол", + "shortcuts_desc": "Хурдан ажиллах дуртай хэрэглэгчдэд зориулсан товчлолууд энд байна. ? дарж бүх товчлолыг хүссэн үедээ харж болно.", + "compose_open_title": "Бичих цонх", + "compose_open_desc": "Энэ бол имэйл бичих цонх. Хүлээн авагч нэмэх, зурвас бичих, файл хавсаргах, баялаг текст формат ашиглах боломжтой. Мөн ноорог хадгалах, загвар ашиглах боломжтой.", + "calendar_view_title": "Таны хуанли", + "calendar_view_desc": "Энд таны хуанли жишээ арга хэмжээнүүдтэй харагдана. Хэрэгслийн мөрөөр өдөр, долоо хоног, сар, жагсаалт харагдацын хооронд шилжиж болно.", + "create_event_title": "Арга хэмжээ үүсгэх", + "create_event_desc": "Шинэ хуанлийн арга хэмжээ үүсгэхийн тулд энэ товчийг дарна уу. Гарчиг, огноо, цаг тохируулж, оролцогч нэмж болно.", + "event_modal_title": "Арга хэмжээний дэлгэрэнгүй", + "event_modal_desc": "Энэ цонхонд арга хэмжээний мэдээллээ бөглөнө. Гарчиг, огноо, цаг, байршил, оролцогчоо оруулаад хадгална уу.", + "contacts_list_title": "Таны контактууд", + "contacts_list_desc": "Энд таны харилцагчид харагдана. Аль нэгийг нь дарж баруун талд дэлгэрэнгүйг нь үзэж болно. Мөн шинэ харилцагч үүсгэх, vCard импортлох, бүлэг үүсгэх боломжтой.", + "settings_tabs_title": "Тохиргооны цэс", + "settings_tabs_desc": "Энд бүх тохиргооны ангилал байна. Гадаад төрх, таниулбар, шүүлтүүр, хуанли болон бусад тохиргоогоо эндээс удирдана.", + "files_title": "Файлын хадгалалт", + "files_desc": "Файлын хэсэгт та файл байршуулах, зохион байгуулах, хуваалцах боломжтой. Энэ нь таны мэйлд суурилсан хувийн хадгалалт шиг ажиллана.", + "demo_banner_title": "Демо удирдлага", + "demo_banner_desc": "Та демо горимд байна. Бүх өгөгдөл зөвхөн таны хөтөч дээр хадгалагдана. Хүссэн үедээ Reset Demo дарж дахин эхлүүлж болно.", + "quota_title": "Хадгалалтын ашиглалт", + "quota_desc": "Энд таны шуудангийн хайрцгийн ашиглаж буй хэмжээг харуулна. Илүү их зай ашиглах тусам тойрог дүүрнэ." + }, + "unified_mailbox": { + "search_unavailable": "Нэгдсэн харагдац дээр хайлт ашиглах боломжгүй" + }, + "quote_header": { + "reply_line": "{date}-нд {from} бичсэн:", + "forwarded_separator": "---------- Урагшлуулсан мессеж ----------", + "from_label": "Илгээгч", + "date_label": "Огноо", + "subject_label": "Сэдэв" + }, + "pwa_install": { + "title": "{appName} суулгах", + "description": "Хурдан нэвтрэх болон оффлайн дэмжлэг авахын тулд аппыг суулгана уу.", + "not_now": "Одоохондоо биш", + "install": "Суулгах", + "dont_remind": "Дахиж сануулахгүй", + "dismiss_aria": "Суулгах сануулгыг хаах" + }, + "deep_link": { + "message_not_found": "Энэ зурвас дахин ашиглах боломжгүй болсон.", + "thread_not_found": "Энэ харилцан яриа дахин ашиглах боломжгүй болсон.", + "folder_not_found": "Энэ хавтас дахин ашиглах боломжгүй болсон.", + "event_not_found": "Энэ арга хэмжээ дахин ашиглах боломжгүй болсон.", + "file_not_found": "Энэ файл дахин ашиглах боломжгүй болсон.", + "account_unavailable": "Энэ холбоосыг нээхийн тулд энэ дансанд нэвтрэнэ үү.", + "copy_message": "Мессежийн холбоосыг хуулах", + "copy_conversation": "Харилцан ярианы холбоосыг хуулах", + "copy_event": "Арга хэмжээний холбоосыг хуулах", + "copied": "Холбоос хуулагдлаа", + "copy_failed": "Холбоосыг хуулж чадсангүй" + }, + "plugins": { + "oauth_callback": { + "completing": "Зөвшөөрлийг дуусгаж байна…", + "completing_message": "Холболт үүсэх хүртэл түр хүлээнэ үү.", + "received_title": "Зөвшөөрөл хүлээн авсан", + "received_message": "Та холболт эхлүүлсэн таб руугаа буцаж болно.", + "failed_title": "Зөвшөөрөл амжилтгүй болсон", + "denied": "Зөвшөөрөл олгохыг татгалзсан.", + "failed_detail": "Зөвшөөрөл амжилтгүй болсон: {error}", + "missing_params": "Зөвшөөрлийн код эсвэл төлөв байхгүй байна.", + "delivery_failed": "Зөвшөөрлийн үр дүнг аппын таб руу дамжуулж чадсангүй. Энэ цонхыг хаагаад дахин оролдоно уу." + }, + "settings": { + "title": "Залгаасууд", + "description": "Таны администраторын нэмсэн залгаасууд. Өөрийн бүртгэлд асааж эсвэл унтрааж болно.", + "description_uploads": "Залгаасууд webmail-д шинэ боломж нэмдэг. Өөрийн бүртгэлд асааж, унтрааж эсвэл өөрийнхөө залгаасыг суулгаж болно.", + "description_demo": "Демо горимд залгаас ашиглах боломжгүй.", + "install_button": "Залгаас суулгах", + "install_hint": "Залгаасын ZIP файлыг сонгоно уу.", + "install_hint_approval": "Залгаасын ZIP файлыг сонгоно уу. Асаахаас өмнө администратор үүнийг зөвшөөрөх ёстой.", + "install_uploads_off": "Таны администратор залгаас суулгахыг зөвшөөрөөгүй байна.", + "install_managed_id": "Таны администратор ийм нэртэй залгаасыг аль хэдийн олгосон тул үүнийг солих боломжгүй.", + "install_invalid": "Энэ файл суулгаж болох залгаас биш байна: {details}", + "toast_installed": "Залгаас суулгалаа. Доороос асаана уу.", + "toast_installed_pending": "Залгаас суулгаж, зөвшөөрөл авахаар таны администратор руу илгээлээ.", + "toast_enabled": "{name} залгаас асаалттай", + "toast_disabled": "{name} залгаас унтарсан", + "toast_removed": "{name} залгаасыг устгалаа", + "toast_forced": "Таны администратор {name} залгаасыг асаалттай байлгахыг шаарддаг", + "toast_approval_requested": "{name} залгаас таны администраторын зөвшөөрлийг хүлээж байна", + "empty_title": "Одоогоор залгаас алга", + "empty_admin": "Таны администратор ямар ч залгаас нэмээгүй байна.", + "empty_uploads": "Эхлэхийн тулд ZIP файлаас залгаас суулгана уу.", + "empty_demo": "Залгаасуудад жинхэнэ бүртгэл хэрэгтэй тул демо горимд байхгүй.", + "syncing": "Залгаасуудыг сервертэй тулгаж байна…", + "status_installed": "Унтарсан", + "status_enabled": "Эхэлж байна", + "status_running": "Асаалттай", + "status_disabled": "Унтарсан", + "status_error": "Асуудалтай", + "badge_forced": "Заавал", + "badge_managed": "Таны администратороос", + "badge_yours": "Та суулгасан", + "badge_awaiting": "Зөвшөөрөл хүлээж байна", + "toggle_label": "{name} залгаасыг асаах эсвэл унтраах", + "forced_note": "Таны администратор энэ залгаасыг шаарддаг тул унтраах боломжгүй.", + "permissions": "Юу хийж чадах вэ", + "permissions_managed": "Юу хийж чадах вэ (таны администратор зөвшөөрсөн)", + "settings": "Тохиргоо", + "remove": "Устгах", + "remove_title": "{name} залгаасыг устгах уу?", + "remove_message": "Залгаас болон түүний энэ хөтөчид хадгалсан бүх зүйл устна. Дараа нь дахин суулгаж болно.", + "remove_confirm": "Устгах", + "error_user_plugins_off": "Таны администратор хэрэглэгчийн суулгасан залгаасуудыг унтраасан тул энэ залгаас ажиллахгүй.", + "error_approval_unavailable": "Энэ залгаасыг зөвшөөрөл шаарддаг болохоос өмнө суулгасан. Зөвшөөрөлд илгээхийн тулд устгаад дахин суулгана уу.", + "error_approval_denied": "Таны администратор энэ залгаасыг зөвшөөрөөгүй.", + "error_approval_pending": "Таны администратор энэ залгаасыг зөвшөөрөхийг хүлээж байна.", + "error_auto_disabled": "Дахин дахин алдаа гаргасан тул унтраасан. Дахин оролдохын тулд асаана уу.", + "accounts_heading": "Холбогдсон бүртгэлүүд", + "account_on": "{host} дээрх {label}", + "account_waiting": "{host} дээрх {label} (нэвтрэлт дуусаагүй)", + "disconnect": "Салгах", + "disconnect_failed": "{label} салгаж чадсангүй" + }, + "consent": { + "title": "{name} залгаасад үүнийг зөвшөөрөх үү?", + "intro": "Залгаас доорх зөвшөөрлүүдийг хүсэж байна. Та зөвшөөрсөн тохиолдолд л авна.", + "high_risk_note": "Тэмдэглэсэн зүйлс нь залгаасад таны имэйлийн агуулгыг харах эсвэл өгөгдлийг өөр газар илгээх боломж олгоно. Зөвхөн итгэдэг залгаасдаа зөвшөөрнө үү.", + "revoke_hint": "Та хүссэн үедээ Тохиргоо хэсэгт залгаасыг унтрааж эсвэл устгаж болно.", + "deny": "Зөвшөөрөхгүй", + "allow": "Зөвшөөрөх" + }, + "permissions": { + "none": "Тусгай зөвшөөрөл шаардлагагүй.", + "high_risk": "Болгоомжтой хандах", + "email_read": { + "title": "Таны имэйлийг унших", + "body": "Таны бүртгэл дэх аль ч имэйлийн илгээгч, хүлээн авагч, гарчиг, бүрэн текст болон таны хавтаснуудыг харах." + }, + "email_write": { + "title": "Таны имэйлийг өөрчлөх, цэгцлэх", + "body": "Таны имэйлүүдэд таг, туг болон бусад түлхүүр үг нэмэх эсвэл хасах, тэдгээрийг таны хавтаснуудын хооронд болон архив руу зөөх, мөн ноорог хадгалах." + }, + "email_send": { + "title": "Илгээхээс өмнө имэйлийг өөрчлөх", + "body": "Таны бичиж буй имэйлийн хүлээн авагч, гарчиг, текст, хавсралтыг унших, өөрчлөх, мөн имэйл илгээхийг зогсоох." + }, + "crypto_full": { + "title": "Аппад бүрэн хандах", + "body": "Webmail-тэй адил эрхээр ажиллана. Таны бүх имэйл болон хадгалсан түлхүүрүүдийг уншиж, таны нэрийн өмнөөс үйлдэл хийж чадна. Зөвхөн бүрэн итгэдэг залгаасдаа үүнийг зөвшөөрнө үү." + }, + "email_raw_send": { + "title": "Өөрөө бэлтгэсэн имэйл илгээх", + "body": "Таны бүртгэлээс бүрэн бэлэн имэйл, жишээ нь гарын үсэг зурсан эсвэл шифрлэсэн имэйл илгээх." + }, + "email_blob_read": { + "title": "Имэйлийн түүхий агуулгыг унших", + "body": "Таны имэйл, хавсралтуудын эх өгөгдөл болон илгээхээс өмнө хавсаргасан файл бүрийг унших." + }, + "email_blob_write": { + "title": "Илгээхээс өмнө хавсралтыг солих", + "body": "Таны мэйл сервер рүү файл байршуулж, имэйл илгээгдэхээс өмнө хавсаргасан файлыг өөр файлаар солих." + }, + "email_render_takeover": { + "title": "Имэйлийг харуулах байдлыг өөрчлөх", + "body": "Таны нээсэн имэйлүүд болон имэйлийн жагсаалтын текстийг солих, жишээ нь тайлсан агуулгыг харуулах." + }, + "calendar_read": { + "title": "Таны календарийг унших", + "body": "Таны календарь болон тэдгээрийн үйл явдлуудыг харах." + }, + "calendar_write": { + "title": "Таны календарийг өөрчлөх", + "body": "Үйл явдал үүсгэх, өөрчлөх, устгах." + }, + "contacts_read": { + "title": "Таны харилцагчдыг унших", + "body": "Таны хаягийн дэвтрүүдээс хайх, унших." + }, + "contacts_write": { + "title": "Таны харилцагчдыг өөрчлөх", + "body": "Харилцагч болон хаягийн дэвтэр үүсгэх, өөрчлөх, устгах." + }, + "files_read": { + "title": "Таны файлуудыг унших", + "body": "Таны бүртгэлд хадгалсан файлуудыг үзэх, нээх." + }, + "files_write": { + "title": "Таны файлуудыг өөрчлөх", + "body": "Таны бүртгэл дэх файлуудыг байршуулах, нэрийг өөрчлөх, зөөх, устгах." + }, + "identity_read": { + "title": "Таны илгээгчийн хаягуудыг харах", + "body": "Таны илгээдэг хаягууд болон тэдгээрийн гарын үсгийг харах." + }, + "identity_write": { + "title": "Таны илгээгчийн хаягуудыг өөрчлөх", + "body": "Илгээгчийн хаяг болон гарын үсэг үүсгэх, өөрчлөх, устгах." + }, + "filters_read": { + "title": "Таны имэйл шүүлтүүрийг унших", + "body": "Ирж буй имэйлийг ангилдаг дүрмүүдийг харах." + }, + "filters_write": { + "title": "Таны имэйл шүүлтүүрийг өөрчлөх", + "body": "Сервер дээрх шүүлтүүрт дүрэм нэмэх. Ийм дүрэм ирж буй имэйлийг зөөх, дамжуулах эсвэл устгах боломжтой." + }, + "tasks_read": { + "title": "Таны даалгавруудыг унших", + "body": "Таны даалгаврын жагсаалт болон даалгавруудыг харах." + }, + "tasks_write": { + "title": "Таны даалгавруудыг өөрчлөх", + "body": "Даалгавар үүсгэх, өөрчлөх, дуусгах, устгах." + }, + "templates_read": { + "title": "Таны загваруудыг унших", + "body": "Таны хадгалсан имэйлийн загваруудыг харах." + }, + "templates_write": { + "title": "Таны загваруудыг өөрчлөх", + "body": "Имэйлийн загвар үүсгэх, өөрчлөх, устгах." + }, + "smime_read": { + "title": "Таны S/MIME төлөвийг харах", + "body": "Танд S/MIME түлхүүр, гэрчилгээ байгаа эсэхийг түлхүүрт хандахгүйгээр харах." + }, + "vacation_read": { + "title": "Таны эзгүй үеийн хариуг харах", + "body": "Автомат хариу асаалттай эсэх, юу бичсэнийг харах." + }, + "vacation_write": { + "title": "Таны эзгүй үеийн хариуг өөрчлөх", + "body": "Автомат хариуг асаах, унтраах, текст болон огноог нь өөрчлөх." + }, + "settings_read": { + "title": "Таны тохиргоог унших", + "body": "Таны тохиргоо болон тагуудын тодорхойлолтыг харах." + }, + "settings_write": { + "title": "Таны тохиргоог өөрчлөх", + "body": "Таг нэмэх, дарааллыг нь өөрчлөх, таны тохиргоог өөрчлөх." + }, + "security_read": { + "title": "Бүртгэлийн аюулгүй байдлыг харах", + "body": "Хоёр хүчин зүйлийн баталгаажуулалт болон шифрлэлт асаалттай эсэхийг нууц мэдээлэлд хандахгүйгээр харах." + }, + "auth_observe": { + "title": "Таныг нэвтрэхийг мэдэх", + "body": "Таныг нэвтрэх, бүртгэл солих эсвэл сесс сунгагдах үед мэдэгдэл авах." + }, + "auth_emit": { + "title": "Таныг гаргах", + "body": "Таныг webmail-ээс гаргах." + }, + "account_read": { + "title": "Таны бүртгэлүүдийг харах", + "body": "Таны нэмсэн бүртгэлүүдийг хаяг, серверийн хамт харах." + }, + "http_post": { + "title": "Таны өмнөөс webmail серверийг ашиглах", + "body": "Эдгээр серверийн хаяг руу таны нэвтрэх мэдээллээр хандах:" + }, + "http_fetch": { + "title": "Бусад вэбсайттай холбогдох", + "body": "Эдгээр сайт руу хүсэлт илгээх. Залгаасын уншиж чадах бүх зүйлийг тийш илгээж болзошгүй:" + }, + "admin_config": { + "title": "Администраторын тохиргоог унших", + "body": "Таны администраторын энэ залгаасад хадгалсан тохиргоог унших." + }, + "ui_observe": { + "title": "Апп дотор хаана байгааг дагах", + "body": "Аль хуудас нээлттэй байгааг мэдэх, таны дарсан холбоосод хариу үйлдэл үзүүлэх." + }, + "ui_toolbar": { + "title": "Хэрэгслийн мөрөнд товч нэмэх", + "body": "Имэйлийн хэрэгслийн мөр, нээлттэй имэйл болон дээд мөрөнд өөрийн товчуудыг харуулах." + }, + "ui_app_top_banner": { + "title": "Дээд хэсэгт баннер харуулах", + "body": "Аппын дээд хэсэгт мэдэгдэл харуулах." + }, + "ui_email_banner": { + "title": "Имэйл дээр мэдэгдэл харуулах", + "body": "Нээлттэй имэйл эсвэл ярилцлагын хэсгүүдийн дээр, дотор болон хооронд өөрийн мэдэгдэл, хураангуйг харуулах." + }, + "ui_email_footer": { + "title": "Имэйлийн доор агуулга нэмэх", + "body": "Нээлттэй имэйлийн доор өөрийн агуулгыг харуулах." + }, + "ui_contact_cryptokeys": { + "title": "Харилцагчийн шифрлэлтийн түлхүүрийг харуулах", + "body": "Харилцагчийн шифрлэлтийн түлхүүрүүдийг харуулах, удирдах." + }, + "ui_email_details": { + "title": "Имэйлийн дэлгэрэнгүйд самбар нэмэх", + "body": "Нээлттэй имэйлийн хажууд эсвэл доор өөрийн самбарыг харуулах." + }, + "ui_download_file": { + "title": "Файлыг таны төхөөрөмжид хадгалах", + "body": "Өөрийн үүсгэсэн файлуудыг татаж авахыг санал болгох." + }, + "ui_message_list_tabs": { + "title": "Ирсэн имэйлд таб нэмэх", + "body": "Ирсэн имэйлийг Шинэчлэлт, Сурталчилгаа зэрэг табуудад ангилах." + }, + "ui_composer_toolbar": { + "title": "Имэйл бичих цонхонд нэмэх", + "body": "Таныг имэйл бичих үед өөрийн товч, цэсийн зүйл, хэсгүүдийг харуулах." + }, + "ui_composer_sidebar": { + "title": "Имэйл бичих цонхонд самбар нэмэх", + "body": "Таны бичиж буй имэйлийн хажууд өөрийн самбарыг харуулах." + }, + "ui_sidebar_widget": { + "title": "Хажуугийн самбарт нэмэх", + "body": "Имэйлийн хажуугийн самбарт өөрийн хайрцаг, хэсгүүдийг харуулах." + }, + "ui_settings_section": { + "title": "Тохиргооны хуудас нэмэх", + "body": "Тохиргоо хэсэгт өөрийн хуудсыг харуулах." + }, + "ui_context_menu": { + "title": "Имэйлийн цэсэнд нэмэх", + "body": "Имэйл дээр баруун товшиход нээгддэг цэсэнд зүйл нэмэх." + }, + "ui_navigation_rail": { + "title": "Навигацын мөрөнд нэмэх", + "body": "Навигацын мөрөнд өөрийн товчийг харуулах." + }, + "ui_keyboard": { + "title": "Товчлуурын товчлол нэмэх", + "body": "Өөрийн тодорхойлсон товчлуурын товчлолд хариу үйлдэл үзүүлэх." + }, + "ui_calendar_action": { + "title": "Хуанлийн харагдацад нэмэх", + "body": "Үйл явдал дээр өөрийн товч, хэсгүүдийг, хуанлийн өдрүүд дээр богино шошго, цэгүүдийг харуулах." + }, + "ui_admin_page": { + "title": "Админ хуудас нэмэх", + "body": "Админы тохиргоонд өөрийн хуудсыг харуулах." + }, + "app_lifecycle": { + "title": "Апп эхлэх, зогсохыг мэдэх", + "body": "Апп эхлэх, офлайн болох эсвэл таныг гарах үед мэдэгдэл авах." + }, + "compose_open": { + "title": "Имэйл бичих цонх нээх", + "body": "Хүлээн авагч, текст эсвэл хавсралтыг нь бөглөсөн шинэ имэйл нээх. Илгээх эсэхийг та өөрөө шийднэ." + }, + "compose_read": { + "title": "Бичиж буй захидлаа унших", + "body": "Засварлагч дээр плагиныг ашиглах үед бичиж буй захидлын текстийг уншина. Гарын үсэг болон иш татсан захидлыг уншихгүй." + }, + "compose_write": { + "title": "Бичиж буй захидлаа өөрчлөх", + "body": "Засварлагч дээр плагиныг ашиглах үед бичиж буй захидлын текстийг солих эсвэл нэмэх. Өөрчлөлт бүрийг буцааж болох бөгөөд илгээх эсэхийг та өөрөө шийднэ." + }, + "ui_notifications": { + "title": "Мэдэгдэл харуулах", + "body": "Таны төхөөрөмж дээр мэдэгдэл харуулах, эсвэл мэдэгдлийг зөвшөөрөөгүй бол аппын дотор харуулах. Мэдэгдэл бүр залгаасын нэрийг заана." + }, + "ui_message_list": { + "title": "Имэйлийн жагсаалтад нэмэх", + "body": "Имэйлийн жагсаалтын дээр өөрийн агуулгыг, имэйлүүд дээр болон сонгосон имэйлийн хэрэгслийн мөрөнд товчуудыг харуулах." + }, + "ui_contacts": { + "title": "Харилцагчдын харагдацад нэмэх", + "body": "Харилцагчийн дэлгэрэнгүйд өөрийн хэсгүүдийг, харилцагчдын жагсаалтад товчуудыг харуулах." + }, + "ui_tasks": { + "title": "Даалгаврын харагдацад нэмэх", + "body": "Даалгаврын дэлгэрэнгүйд өөрийн хэсгүүдийг, даалгаврын жагсаалтад товчуудыг харуулах." + }, + "ui_files": { + "title": "Файлын харагдацад нэмэх", + "body": "Файлын урьдчилан харахад өөрийн хэсгүүдийг, файлын цэсэнд зүйлсийг харуулах." + }, + "ui_commands": { + "title": "Тушаал нэмэх", + "body": "Тушаалын самбарт (Ctrl+K) өөрийн зүйлсийг нэмэх." + }, + "ui_app_page": { + "title": "Хуудас нэмэх", + "body": "Аппад өөрийн хуудсуудыг нэмэх, тус бүр навигацын мөрөнд товчтой." + }, + "http_proxy": { + "title": "Таны холбосон бүртгэлүүдийг ашиглах", + "body": "Таны эсвэл администраторын тохируулсан бүртгэл эсвэл түлхүүрээр Bulwark-ийн серверээр дамжуулан хүсэлт илгээх. Залгаас нууц үгийг хэзээ ч харахгүй, гэхдээ тухайн бүртгэлийн өөрийн сервер дээр хийж чадах бүх зүйлийг хийж чадна." + } + }, + "dialog": { + "from_plugin": "{name} залгаасаас", + "ok": "OK", + "submit": "Илгээх", + "confirm": "Баталгаажуулах", + "cancel": "Цуцлах", + "close": "Хаах", + "turn_off": "{name}-г унтраах", + "plugin_menu": "{name}-ийн тохиргоо", + "password_note": "Энэ маягт вэб шуудангаас биш, {name} залгаасаас ирсэн. Энэ залгаасанд итгэдэг бол л нууц үгээ оруулна уу.", + "toast_auto_disabled": "{name} дахин дахин алдаа гаргасан тул унтраагдлаа." + }, + "connect": { + "title": "{label} холбох", + "intro_password": "{name} таны {label} бүртгэлийг ашиглахыг хүсэж байна. Bulwark нууц үгийг өөрийн сервер дээр хадгалж, зөвхөн {host} руу илгээнэ. Залгаас үүнийг хэзээ ч харахгүй.", + "intro_password_any": "{name} таны {label} бүртгэлийг ашиглахыг хүсэж байна. Bulwark нууц үгийг өөрийн сервер дээр хадгалж, зөвхөн таны энд оруулсан сервер рүү илгээнэ. Залгаас үүнийг хэзээ ч харахгүй.", + "intro_signin": "{name} таны {label} бүртгэлд нэвтрэхийг хүсэж байна. Серверийг энд оруулаад, дараа нь тухайн серверийн өөрийн хуудсанд нэвтэрнэ үү. Bulwark олж авсан хандалтаа өөрийн сервер дээр хадгална; залгаас үүнийг хэзээ ч харахгүй.", + "server": "Серверийн хаяг", + "server_hint": "cloud.example.com", + "username": "Хэрэглэгчийн нэр", + "secret": "Нууц үг эсвэл түлхүүр", + "connect": "Холбох", + "continue": "Үргэлжлүүлэх", + "cancel": "Цуцлах", + "saving": "Хадгалж байна…", + "error_server": "Серверийн хаягийг оруулна уу, жишээ нь https://cloud.example.com", + "error_save": "Хадгалж чадсангүй: {error}" + } + }, + "global_search": { + "title": "Бүгдийг хайх", + "placeholder": "Шуудан, харилцагч, хуанли, файлаас хайх...", + "scope_label": "Хайх хүрээ", + "account_label": "Бүртгэл", + "scope_all": "Бүгд", + "scope_mail": "Мэйл", + "scope_contacts": "Харилцах утас", + "scope_calendar": "Хуанли", + "scope_files": "Файл", + "all_accounts": "Бүх бүртгэл", + "show_all": "Бүх үр дүнг харуулах", + "no_results": "“{query}”-д үр дүн олдсонгүй", + "server_hint": "Бүтэн үгс серверээс хайна; үгийн хэсэг ачаалагдсан зүйлстэй таарна", + "searching": "Хайж байна...", + "account_error": "{account}-аас хайж чадсангүй", + "recent_searches": "Сүүлийн хайлтууд", + "recurring": "Давтагдах", + "no_subject": "(no subject)", + "empty_hint": "Бүх бүртгэлийн шуудан, харилцагч, үйл явдал, файлаас хайгаарай. from:, subject:, has:attachment, after:2026-01-01 гэж туршиж үзээрэй.", + "flat_by_date": "Огноогоор жагсаах", + "grouped": "Бүлэглэсэн", + "load_more": "Цааш ачаалах", + "preview_empty": "Урьдчилан харах үр дүнгээ сонгоно уу", + "open_full": "Нээх", + "view_label": "Харагдац" + }, + "top_bar": { + "more": "Бусад сонголт" + }, + "plugin_pages": { + "loading": "Хуудсыг ачаалж байна", + "not_available_title": "Энэ хуудас боломжгүй байна", + "not_available_description": "Үүнийг нэмсэн залгаас унтарсан эсвэл суулгаагүй болсон байна.", + "back_to_mail": "Мэйл рүү буцах", + "settings_group": "Залгаасууд" + }, + "command_palette": { + "title": "Командын самбар", + "open": "Командын самбарыг нээх", + "placeholder": "Команд бичнэ үү", + "no_results": "Тохирох команд алга", + "go_to": "{name} руу очих", + "compose": "Шинэ захидал", + "search": "Мэйлээс хайх", + "theme_dark": "Бараан загвар руу шилжих", + "theme_light": "Цайвар загвар руу шилжих", + "shortcuts": "Гарын товчлолыг харуулах", + "group_navigation": "Очих", + "group_actions": "Үйлдлүүд", + "group_plugins": "Залгаасууд" + }, + "notification_center": { + "title": "Мэдэгдэл", + "mark_all_read": "Бүгдийг уншсан болгох", + "clear": "Цэвэрлэх", + "empty": "Мэдэгдэл алга", + "close": "Хаах", + "unread": "Уншаагүй", + "unread_count": "Уншаагүй: {count}", + "level_error": "Алдаа", + "level_warning": "Анхааруулга" + } +} diff --git a/locales/nb/common.json b/locales/nb/common.json index 510e6519..309a8485 100644 --- a/locales/nb/common.json +++ b/locales/nb/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimalistisk nettpostklient som bruker JMAP-protokollen", - "login": { - "title": "Nettpost", - "username_label": "E-post", - "username_placeholder": "user@example.com", - "password_label": "Passord", - "password_placeholder": "Skriv inn passordet ditt", - "jmap_endpoint_label": "JMAP-server", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Serveren må tillate CORS-forespørsler fra dette domenet.", - "jmap_server_label": "Server", - "jmap_server_auto_picked": "Serveren ble valgt ut fra e-postdomenet ditt.", - "sign_in": "Logg på", - "signing_in": "Logger på...", - "loading": "Laster inn...", - "reconnecting": "Tilkoblingen ble brutt. Forsøker å koble til på nytt...", - "error": { - "invalid_credentials": "Ugyldig e-postadresse eller passord. Sjekk påloggingsopplysningene dine og prøv igjen.", - "connection_failed": "Kan ikke nå serveren. Sjekk internettforbindelsen og prøv igjen.", - "cors_blocked": "Serveren er tilgjengelig, men blokkerer forespørsler fra andre opprinnelser. Sjekk JMAP-serverens CORS-innstillinger og tillat dette domenet.", - "server_error": "Serveren er midlertidig utilgjengelig. Prøv igjen senere.", - "generic": "Det oppstod en uventet feil. Kontakt administratoren din hvis feilen vedvarer.", - "totp_required": "Du må skrive inn en tofaktorautentiseringskode nedenfor.", - "totp_invalid": "Ugyldig autentiseringskode. Sjekk autentiseringsappen din og prøv igjen.", - "oauth_discovery_failed": "SSO er aktivert, men identitetsleverandøren kunne ikke nås. Sjekk OAuth-konfigurasjonen." - }, - "show_password": "Vis passord", - "hide_password": "Skjul passord", - "totp_toggle": "Jeg har en kode for tofaktorautentisering", - "remember_me": "Husk meg", - "config_error": { - "title": "Konfigurasjonsfeil", - "fetch_failed": "Kan ikke laste inn programoppsettet. Prøv igjen senere.", - "server_not_configured": "E-postserveren er ikke konfigurert. Kontakt administratoren din." - }, - "remove_from_history": "Fjern fra historien", - "totp_label": "Autentiseringskode", - "totp_placeholder": "000 000", - "session_expired": "Økten din er utløpt. Logg på igjen.", - "dismiss": "Lukk", - "or": "eller", - "sign_in_sso": "Logg på med SSO", - "add_account_title": "Legg til konto", - "add_account_subtitle": "Logg på med en annen konto", - "cancel": "Avbryt", - "website": "Nettsted", - "imprint": "Juridisk informasjon", - "privacy_policy": "Personvernerklæring", - "try_demo": "Prøv demoen", - "demo_description": "Utforsk med eksempeldata – ingen konto nødvendig", - "demo_launching": "Starter demoen...", - "demo_login_button": "Start demoen", - "demo_tagline": "Prøv en fullverdig e-postklient. Du trenger ingen konto.", - "demo_no_signup": "Ingen registrering nødvendig – utforsk fritt med eksempeldata", - "oauth_completing": "Fullfører pålogging...", - "oauth_error": { - "title": "Autentisering mislyktes", - "invalid_state": "Sikkerhetskontrollen mislyktes. Prøv å logge på igjen.", - "missing_params": "Manglende autorisasjonsdata. Prøv å logge på igjen.", - "token_exchange_failed": "Kunne ikke fullføre autentiseringen. Prøv igjen.", - "access_denied": "Du fikk ikke tilgang. Kontakt administratoren din.", - "back_to_login": "Tilbake til pålogging" - } - }, - "sidebar": { - "close": "Lukk", - "compose": "Skriv", - "compose_hint": "Skriv (c)", - "search_placeholder": "Søk i e-post...", - "search_placeholder_hint": "Søk i e-post... (trykk /)", - "storage": "Lagring", - "storage_used": "Brukt", - "storage_free": "Ledig", - "storage_total": "Totalt", - "sign_out": "Logg ut", - "sign_out_of": "Logg ut av {account}", - "sign_out_all": "Logg ut av alle kontoer", - "add_account": "Legg til konto", - "set_as_default": "Sett som standard", - "switch_account": "Bytt konto", - "contacts": "Kontakter", - "calendar": "Kalender", - "settings": "Innstillinger", - "admin": "Administrasjon", - "files": "Filer", - "loading_mailboxes": "Laster inn postkasser...", - "push_connected": "Sanntidsoppdateringer er aktive", - "push_disconnected": "Sanntidsoppdateringer er inaktive", - "keyboard_shortcuts": "Tastatursnarveier", - "theme": { - "light": "Lys modus", - "dark": "Mørk modus", - "system": "Systemtema" - }, - "language": { - "title": "Språk" - }, - "mailboxes": { - "inbox": "Innboks", - "sent": "Sendt", - "drafts": "Utkast", - "trash": "Papirkurv", - "archive": "Arkiv", - "starred": "Stjernemerket", - "all_mail": "All e-post", - "spam": "Søppelpost", - "important": "Viktig" - }, - "unified_inbox": "Samlet innboks", - "unified_sent": "Alle sendte meldinger", - "unified_drafts": "Alle utkast", - "unified_trash": "Alle papirkurver", - "unified_archive": "Alle arkiver", - "unified_junk": "All søppelpost", - "all_accounts": "Alle kontoer", - "unified_mailbox": "Samlet postkasse", - "expand": "Utvid", - "collapse": "Fold sammen", - "expand_tooltip": "Utvid", - "collapse_tooltip": "Fold sammen", - "mobile": { - "search": "Søk", - "compose": "Skriv", - "go_back": "Gå tilbake", - "toggle_menu": "Vis eller skjul menyen" - }, - "clear_search": "Tøm søket", - "vacation_active": "Feriesvar er aktivt", - "demo_banner": "Demomodus", - "demo_reset": "Tilbakestill", - "demo_tour": "Omvisning", - "tags": "Etiketter", - "show_all_tags": "Vis alle ({count})", - "show_fewer_tags": "Vis færre", - "folders": "Mapper", - "shared": "Delt", - "mail": "E-post", - "nav_label": "Navigasjon", - "add_app": "Apper", - "scheduled": "Planlagt", - "unified_all_unread": "Alle uleste", - "unified_all_starred": "Alle stjernemerkede", - "unified_all_mail": "All e-post", - "remove_account": "Fjern konto", - "remove_account_confirm": "Vil du fjerne {account} fra denne enheten? Du kan legge den til igjen senere." - }, - "protocol_handlers": { - "title": "Standardapper", - "description": "Velg om e-post- og kalenderlenker skal åpnes i Bulwark. Teknisk sett registrerer Bulwark seg som protokollbehandler for mailto:- og webcal:-lenker.", - "unsupported": "Denne nettleseren eller tilkoblingen støtter ikke manuell registrering av protokollbehandlere. Det kan hende du likevel kan velge den installerte PWA-en i innstillingene for nettleseren eller operativsystemet.", - "mailto_label": "E-postlenker", - "mailto_description": "Åpne mailto:-lenker i Bulwark med et forhåndsutfylt meldingsvindu.", - "protocol_open_mode_label": "Når mailto:- og webcal:-lenker åpnes", - "protocol_open_mode_description": "Velg om Bulwark skal åpne mailto:- og webcal:-lenker i en ny fane eller bruke en økt som allerede er åpen. Alternativet for aktiv økt krever tillatelse til varsler. Hvis nettleseren hindrer Bulwark i å få fokus, kan du klikke på et varsel for å vise vinduet.", - "protocol_open_mode_active_session": "Åpne i aktiv økt hvis mulig", - "protocol_open_mode_new_tab": "Åpne alltid en ny fane", - "focus_notification_title": "Åpne Bulwark", - "focus_notification_body": "Lenken ble åpnet i Bulwark. Klikk for å vise vinduet.", - "webcal_label": "Kalenderlenker", - "webcal_description": "Åpne webcal:-lenker i Bulwark med en forhåndsutfylt dialog for kalenderabonnement.", - "register_mailto": "Registrer e-postprogram", - "register_webcal": "Registrer kalenderprogram", - "mailto_registered": "Forespørsel om registrering av e-postprogram er sendt", - "webcal_registered": "Forespørsel om registrering av kalenderprogram er sendt", - "registration_failed": "Registrering av protokollbehandler mislyktes", - "opening_mailto": "Åpner meldingsvinduet...", - "opening_webcal": "Åpner kalenderen...", - "browser_note": "Nettleseren eller operativsystemet kan be deg bekrefte dette. Det kan også hende at Bulwark må installeres før programmet kan velges som standardapp.", - "select_account_title": "Velg konto", - "select_mailto_account": "Velg hvilken konto som skal åpne denne e-postlenken.", - "select_webcal_account": "Velg hvilken konto som skal åpne denne kalenderlenken.", - "select_account_note": "Dette gjelder bare denne lenken.", - "detail_to": "Til", - "detail_subject": "Emne", - "detail_no_subject": "Uten emne", - "detail_calendar": "Kalender", - "detail_source": "Kilde", - "active_account": "Aktiv", - "switching_account": "Bytter konto..." - }, - "sidebar_apps": { - "modal_title": "Apper i sidefeltet", - "add_new": "Legg til app", - "edit_app": "Rediger app", - "name_label": "Navn", - "name_placeholder": "Min app", - "name_required": "Navn må fylles ut", - "url_label": "URL", - "url_required": "URL må fylles ut", - "url_invalid": "Skriv inn en gyldig HTTP- eller HTTPS-URL", - "icon_label": "Ikon", - "icon_required": "Du må velge et ikon", - "open_mode_label": "Åpningsmodus", - "open_new_tab": "Ny fane", - "open_inline": "I sidefeltet", - "cancel": "Avbryt", - "add": "Legg til", - "update": "Oppdater", - "delete": "Slett", - "delete_confirm_title": "Slett appen", - "delete_confirm": "Er du sikker på at du vil slette «{name}»?", - "no_apps": "Ingen apper er lagt til ennå", - "no_apps_hint": "Legg til egendefinerte apper og lenker i sidefeltet", - "managed_title": "Levert av administratoren din", - "managed_description": "Disse appene er satt opp for alle og kan ikke endres.", - "managed_badge": "Administrert", - "search_icons": "Søk etter ikoner...", - "show_popular": "Populære", - "show_all": "Alle", - "no_icons_found": "Ingen ikoner funnet", - "inline_badge": "Sidefelt", - "tab_badge": "Fane", - "show_on_mobile": "Vis på mobil" - }, - "email_list": { - "no_emails": "Ingen meldinger funnet", - "no_emails_description": "Denne postkassen er tom", - "no_search_results": "Ingen resultater funnet", - "no_search_results_description": "Prøv å justere søket eller filtrene", - "loading": "Laster inn e-poster...", - "unread": "ulest", - "to_me": "Til meg", - "to_recipients": "Til {count} mottakere", - "and_others": "og {count} andre", - "draft": "Utkast", - "starred": "Stjernemerket", - "pinned": "Festet", - "replied": "Besvart", - "forwarded": "Videresendt", - "has_attachment": "Har vedlegg", - "conversations_count": "{count} av {total} samtaler", - "conversations_count_plus": "{count}+ samtaler", - "conversations_count_simple": "{count} samtaler", - "no_conversations": "Ingen samtaler", - "loading_more": "Laster inn flere e-poster...", - "no_more_emails": "Ingen flere e-poster å laste inn", - "batch_actions": { - "select": "Velg e-poster", - "select_all": "Velg alle", - "selected_messages": "{count, plural, one {1 e-post} other {# e-poster}} valgt", - "mark_read": "Merk som lest", - "mark_unread": "Merk som ulest", - "delete": "Slett", - "delete_confirm_title": "Slett e-poster", - "delete_confirm_message": "Er du sikker på at du vil slette {count, plural, one {1 e-post} other {# e-poster}}?", - "clear_selection": "Fjern valg" - }, - "permanent_delete": "Slett permanent", - "permanent_delete_confirm_title": "Slett permanent", - "permanent_delete_confirm_message": "Denne e-posten slettes permanent. Denne handlingen kan ikke angres.", - "permanent_delete_confirm_batch_message": "{count, plural, one {Denne e-posten} other {Disse # e-postene}} blir slettet permanent. Denne handlingen kan ikke angres.", - "empty_folder": { - "button": "Tøm mappe", - "confirm_title": "Tøm mappe", - "confirm_message": "Alle e-postene i denne mappen slettes permanent. Denne handlingen kan ikke angres.", - "confirm_button": "Tøm mappe", - "junk_hint": "Du kan tømme søppelpostmappen for å fjerne alle meldinger permanent.", - "trash_hint": "Du kan tømme papirkurven for å fjerne alle meldinger permanent." - }, - "no_scheduled_emails": "Ingen planlagte e-poster", - "no_scheduled_emails_description": "Meldinger som er planlagt til senere, vises her.", - "scheduled_count": "{count, plural, one {1 planlagt e-post} other {# planlagte e-poster}}", - "scheduled_actions_hint": "Bruk handlingene for planlagte meldinger til å avbryte, planlegge på nytt eller redigere dem.", - "cancel_scheduled_send": "Avbryt sending", - "reschedule_send": "Planlegg på nytt", - "cancel_and_edit": "Avbryt og rediger", - "cancel_and_compose_again": "Avbryt og skriv meldingen på nytt", - "reschedule_prompt": "Skriv inn ny dato og klokkeslett, for eksempel 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Avsenderen ba om å bli varslet når du åpner denne meldingen.", - "send": "Send lesebekreftelse", - "ignore": "Ignorer", - "sent": "Lesebekreftelsen er sendt.", - "send_failed": "Lesebekreftelsen kunne ikke sendes", - "mdn_subject": "Lest: {subject}", - "mdn_body": "Dette er en lesebekreftelse for meldingen du sendte til {recipient}.\n\nMerk: Denne bekreftelsen viser bare at meldingen ble vist på mottakerens datamaskin. Det er ingen garanti for at mottakeren har lest eller forstått meldingsinnholdet." - }, - "no_email_selected": "Ingen e-post er valgt", - "no_email_description": "Velg en e-post fra listen for å se den her", - "no_conversation_selected": "Ingen samtale er valgt", - "no_conversation_description": "Velg en samtale fra listen for å lese den her", - "compose": "Skriv", - "compose_hint": "Skriv ny melding", - "no_subject": "(Uten emne)", - "no_body_content": "(Meldingen har ikke noe innhold)", - "no_preview_available": "Ingen forhåndsvisning er tilgjengelig", - "show_quoted_text": "Vis sitert tekst", - "hide_quoted_text": "Skjul sitert tekst", - "loading_email": "Laster inn e-post...", - "loading": "Laster inn...", - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "delete": "Slett", - "archive": "Arkiver", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Stjernemerk", - "unstar": "Fjern stjerne", - "mark_unread": "Merk som ulest", - "mark_read": "Merk som lest", - "unread": "Ulest", - "read": "Lest", - "spam_short": "Søppelpost", - "not_spam_short": "Ikke søppelpost", - "move": "Flytt", - "print": "Skriv ut", - "view_source": "Vis meldingskilde", - "fullscreen": "Fullskjerm", - "exit_fullscreen": "Avslutt fullskjerm", - "export_email": "Eksporter som .eml", - "forward_as_attachment": "Videresend som vedlegg", - "import_email": "Importer .eml eller .zip", - "keyboard_shortcuts": "Tastatursnarveier (?)", - "email_source": "Meldingskilde", - "draft_banner": "Denne meldingen er et utkast", - "edit_draft": "Rediger", - "copy_source": "Kopier til utklippstavlen", - "source_copied": "Meldingskilden er kopiert til utklippstavlen", - "attachments": "Vedlegg", - "important": "Viktig", - "download": "Last ned", - "download_all": "Last ned alle", - "from": "Fra", - "to": "Til", - "cc": "CC", - "bcc": "BCC", - "date": "Dato", - "subject": "Emne", - "show_details": "Vis detaljer", - "hide_details": "Skjul detaljer", - "external_content_warning": "Bilder og eksternt innhold er blokkert", - "load_external_content": "Last inn bilder", - "trust_sender": "Stol alltid på denne avsenderen", - "back_to_list": "Tilbake til listen", - "view_contact": "Se kontakt", - "message_details": "Meldingsdetaljer", - "more_reply_options": "Flere svaralternativer", - "set_tag": "Legg til etikett", - "tag": "Etikett", - "more_actions": "Flere handlinger", - "previous": "Forrige", - "next": "Neste", - "move_to": "Flytt til...", - "remove_tag": "Fjern etiketten", - "tag_filter_placeholder": "Filtrer etiketter", - "tag_no_matches": "Ingen etiketter passer med søket", - "more_count": "+{count} til", - "characters_count": "{count} tegn", - "quick_reply_placeholder": "Skriv et kort svar...", - "more_options": "Flere alternativer", - "sending": "Sender...", - "security_authentication": "Sikkerhet og autentisering", - "technical_details": "Tekniske detaljer", - "message_id_label": "Meldings-ID:", - "reply_to_label": "Svar til:", - "delivery_time_label": "Leveringstid:", - "conversation_part_label": "En del av samtalen:", - "previous_messages": "{count} tidligere melding", - "previous_messages_plural": "{count} tidligere meldinger", - "time": { - "day": "dag", - "days": "dager", - "hour": "time", - "hours": "timer", - "minute": "minutt", - "minutes": "minutter" - }, - "unknown_sender": "Ukjent", - "recipient_me": "meg", - "recipient_and_others": "{name} og {count} andre", - "recipient_to_prefix": "Til:", - "authentication": { - "title": "Autentisering", - "status": { - "verified": "Verifisert", - "warning": "Advarsel", - "none": "Ikke autentisert" - }, - "spf": { - "pass": "SPF godkjent", - "fail": "SPF ikke godkjent", - "none": "Ingen SPF" - }, - "dkim": { - "pass": "DKIM gyldig", - "fail": "DKIM ugyldig", - "none": "Ingen DKIM" - }, - "dmarc": { - "pass": "DMARC godkjent", - "fail": "DMARC ikke godkjent", - "none": "Ingen DMARC" - }, - "spam_score": "Poengsum for søppelpost", - "tooltip_spf": "Sender Policy Framework: Verifiserer at avsenderserveren er autorisert til å sende e-post på vegne av domenet", - "tooltip_dkim": "DomainKeys Identified Mail: Bekrefter at e-posten ikke ble endret under overføringen ved hjelp av en kryptografisk signatur", - "tooltip_dmarc": "Domenebasert meldingsautentisering, rapportering og samsvar: Kontrollerer at SPF og DKIM samsvarer med avsenderens domene, og fastsetter hvordan meldinger som ikke godkjennes, skal behandles", - "policy": "Retningslinje", - "result": { - "pass": "Godkjent", - "fail": "Mislykket", - "softfail": "Myk feil", - "neutral": "Nøytral", - "permerror": "Permanent feil", - "temperror": "Midlertidig feil", - "none": "Ingen" - } - }, - "details": { - "recipients_routing": "Mottakere og ruting", - "authentication_security": "Autentisering og sikkerhet", - "identifiers_threading": "Identifikatorer og tråder", - "mailing_list": "Postliste", - "message_properties": "Meldingsegenskaper", - "sent": "Sendt", - "received": "Mottatt", - "delivery_time": "Leveringstid", - "in_reply_to": "In-Reply-To", - "references": "Referanser", - "thread_id": "Tråd-ID", - "size": "Størrelse", - "mime_type": "MIME-type", - "attachments_summary": "{count} filer · {size}", - "list_id": "Liste-ID", - "list_help": "Listehjelp", - "list_post": "Listepost", - "list_unsubscribe": "Avslutt abonnementet", - "iprev": "Omvendt DNS", - "spam_status": "Søppelpoststatus", - "ai_verdict": "KI-vurdering", - "account": "Konto", - "no_subject": "(uten emne)" - }, - "headers": { - "routing": "Ruting", - "received": "Mottatt", - "message_id": "Meldings-ID", - "list_info": "Listeinformasjon" - }, - "tooltips": { - "reply": "Svar (r)", - "reply_all": "Svar alle (a)", - "forward": "Videresend (f)", - "archive": "Arkiver (e)", - "delete": "Slett (# eller Del)", - "star": "Stjernemerk (s)", - "unstar": "Fjern stjerne (s)", - "compose": "Skriv (c)", - "previous": "Forrige e-post", - "next": "Neste e-post", - "edit_draft": "Rediger utkast" - }, - "spam": { - "button_title": "Rapporter som søppelpost", - "not_spam_title": "Merk som ikke søppelpost", - "toast_success": "Flyttet til søppelpost", - "toast_batch": "{count} e-poster flyttet til søppelpost", - "toast_undo": "Angre", - "toast_not_spam_success": "Flyttet til innboksen", - "toast_not_spam_batch": "{count} e-poster flyttet til innboksen", - "error": "Kunne ikke rapportere søppelpost", - "error_not_spam": "Kunne ikke gjenopprette e-post" - }, - "unsubscribe_banner": { - "label": "Nyhetsbrev", - "button": "Avslutt abonnementet", - "confirm_title": "Vil du melde deg av denne avsenderens e-postliste?", - "confirm_button": "Bekreft", - "cancel": "Avbryt", - "success_http": "Avmeldingssiden er åpnet i en ny fane", - "success_mailto": "Forespørselen om avmelding er sendt", - "confirm_message_http": "Avmeldingssiden åpnes i en ny fane.", - "confirm_message_mailto": "En forespørsel om avmelding sendes til avsenderen.", - "error": "Kan ikke avslutte abonnementet", - "dismiss": "Lukk" - }, - "calendar_invitation": { - "loading": "Laster inn hendelsesdetaljer...", - "title": "Kalenderinvitasjon", - "published_title": "Publisert hendelse", - "response_title": "Svar på hendelse", - "update_title": "Hendelsesoppdatering", - "counter_title": "Motforslag", - "refresh_title": "Forespørsel om oppdatering", - "declined_counter_title": "Motforslag avvist", - "cancelled_title": "Hendelsen er avlyst", - "organizer": "Arrangør: {name}", - "organizer_label": "Arrangør", - "attendees": "{count, plural, one {# deltaker} other {# deltakere}}", - "accept": "Godta", - "maybe": "Kanskje", - "decline": "Avslå", - "add_to_calendar": "Legg til i kalenderen", - "added": "Lagt til i kalenderen", - "rsvp_sent": "Svar sendt", - "parse_error": "Kunne ikke lese invitasjonen", - "action_failed": "Kunne ikke fullføre handlingen i kalenderen.", - "no_calendar": "Kalenderen er ikke tilgjengelig", - "published_info": "Denne hendelsen ble delt til informasjon.", - "response_info": "Denne meldingen inneholder et svar fra en deltaker.", - "response_info_organizer": "Dette svaret fra deltakeren oppdaterer hendelsen din.", - "update_info": "Denne meldingen oppdaterer en eksisterende hendelse.", - "counter_info": "Denne meldingen foreslår endringer i en hendelse.", - "counter_info_organizer": "Denne deltakeren foreslo endringer i hendelsen din.", - "refresh_info": "Denne meldingen ber om de siste hendelsesdetaljene.", - "refresh_info_organizer": "En deltaker ba om de siste hendelsesdetaljene.", - "declined_counter_info": "Arrangøren avslo et motforslag.", - "authentication_failed_info": "Kontrollen av e-postautentisering for denne invitasjonen mislyktes. Vær forsiktig med handlingene i kalenderinvitasjonen.", - "authentication_missing_info": "Denne invitasjonen har ingen bekreftet e-postautentisering. Avklar detaljene med arrangøren hvis noe ser uvanlig ut.", - "sender_mismatch_info": "Denne invitasjonen ble sendt fra {sender}, mens arrangøren som er oppført i kalenderdataene er {organizer}.", - "sender_mismatch_unverified_info": "Denne invitasjonen ble sendt fra {sender}, mens arrangøren som er oppført i kalenderdataene er {organizer}, og meldingen kunne ikke bekreftes.", - "organizer_role": "Du er arrangør for denne hendelsen", - "your_response": "Ditt svar: {status}", - "response_needed": "Svar kreves", - "response_accepted": "Godtatt", - "response_tentative": "Kanskje", - "response_declined": "Avslått", - "response_delegated": "Delegert", - "actor_sent_info": "Sendt av {name}.", - "actor_response_info": "{name} svarte: {status}.", - "actor_counter_info": "{name} foreslo endringer i denne hendelsen.", - "actor_refresh_info": "{name} ba om de siste hendelsesdetaljene.", - "actor_declined_counter_info": "{name} avslo motforslaget.", - "actor_note": "Merk: {comment}", - "actor_unknown": "Noen", - "proposed_changes": "Foreslåtte endringer", - "change_title": "Tittel", - "change_time": "Tid", - "change_location": "Sted", - "change_description": "Beskrivelse", - "change_empty": "Ingen", - "change_from_to": "{before} → {after}", - "apply_proposal": "Bruk foreslåtte endringer", - "proposal_applied": "De foreslåtte endringene er tatt i bruk.", - "review_proposal": "Gjennomgå forslaget", - "review_request": "Gjennomgå forespørselen", - "view_in_calendar": "Se i kalenderen", - "select_calendar": "Velg kalender", - "already_in_calendar": "Allerede i kalenderen din", - "request_info": "Du er invitert til denne hendelsen. Svar for å gi arrangøren beskjed om du kan delta.", - "cancel_info": "Arrangøren har avlyst denne hendelsen.", - "event_updated": "Oppdatering #{sequence}", - "event_status_tentative": "Foreløpig", - "event_status_cancelled": "Avlyst", - "expand": "Vis detaljer", - "collapse": "Skjul detaljer" - }, - "send": "Send", - "more": "mer", - "scheduled_banner": "Planlagt sending: {date}", - "scheduled_send_created": "Sendingen er planlagt", - "cancel_scheduled_send": "Avbryt sending", - "reschedule_send": "Planlegg på nytt", - "cancel_and_edit": "Avbryt og rediger", - "cancel_and_compose_again": "Avbryt og skriv meldingen på nytt", - "reschedule_prompt": "Skriv inn ny dato og klokkeslett, for eksempel 2026-05-04T15:30", - "scheduled_actions_only": "I denne visningen kan du bare bruke handlinger for planlagte meldinger", - "undo_send_scheduled": "Meldingen er planlagt for sending", - "undo_send": "Angre sending", - "contact_sidebar": { - "title": "Kontakt", - "close": "Lukk sidefeltet", - "action_email": "E-post", - "action_email_title": "Send e-post", - "action_copy": "Kopier", - "action_copy_title": "Kopier e-postadresse", - "action_edit_title": "Rediger kontakt", - "section_emails": "E-postadresser", - "section_phones": "Telefonnumre", - "section_organizations": "Organisasjoner", - "section_addresses": "Adresser", - "section_notes": "Notater", - "not_in_contacts": "Ikke i kontaktene dine", - "add_to_contacts": "Legg til som kontakt", - "copied": "Kopiert!", - "copy_failed": "Kunne ikke kopiere" - }, - "send_now": "Send nå" - }, - "email_composer": { - "read_receipt_on": "Ber om lesebekreftelse (klikk for å slå av)", - "read_receipt_off": "Be om lesebekreftelse", - "dsn_on": "Leveringsvarsel forespurt (klikk for å slå av)", - "dsn_off": "Be om leveringsvarsel", - "format_plain_text": "Bytt til ren tekst", - "format_rich_text": "Bytt til formatert tekst (HTML)", - "require_tls_on": "Kryptert levering kreves (klikk for å slå av)", - "require_tls_off": "Krev kryptert levering (TLS)", - "new_message": "Ny melding", - "signature_edit_hint": "Dobbeltklikk for å redigere signaturen", - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "reply_to": "Svar", - "reply_all_to": "Svar alle", - "forward_message": "Videresend", - "from": "Fra", - "to": "Til", - "cc": "CC", - "bcc": "BCC", - "subject": "Emne", - "body_placeholder": "Skriv meldingen din...", - "send": "Send", - "cancel": "Avbryt", - "attach": "Legg ved", - "attach_photos": "Bilder og videoer", - "attach_files": "Filer", - "discard": "Forkast", - "discard_draft_title": "Vil du forkaste utkastet?", - "discard_draft_confirm": "Du har ulagrede endringer. Vil du forkaste dette utkastet?", - "saving": "Lagrer...", - "sending": "Sender...", - "add_link": "Legg til lenke", - "link_url_prompt": "Skriv inn nettadressen", - "draft_saved": "Utkastet er lagret", - "save_failed": "Kunne ikke lagre utkastet", - "to_placeholder": "E-postadresser til mottakere", - "cc_placeholder": "Kopimottakere", - "bcc_placeholder": "Blindkopimottakere", - "subject_placeholder": "Emne", - "cc_label": "Kopi:", - "bcc_label": "Blindkopi:", - "subject_label": "Emne:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(Uten emne)", - "unknown_sender": "Ukjent", - "quote": { - "reply_header": "Den {date} skrev {sender}:", - "forward_header": "---------- Videresendt melding ----------", - "from": "Fra: {sender}", - "date": "Dato: {date}", - "subject": "Emne: {subject}", - "to": "Til: {recipients}" - }, - "remove_sub_address": "Fjern underadressen", - "from_override": { - "toggle_off": "Endre avsender", - "toggle_on": "Avbryt endringen", - "toggle_tooltip": "Rediger Fra-navnet og -adressen fritt. E-posten sendes fortsatt gjennom identiteten din.", - "name_label": "Navn i Fra-feltet", - "name_placeholder": "Navn", - "email_label": "E-postadresse i Fra-feltet", - "email_placeholder": "alias@example.com", - "envelope_notice": "Hvis serveren din ikke godtar denne adressen som konvoluttavsender, brukes {identity} i stedet, og mottakerne kan se den i Return-Path-overskriften." - }, - "use_template": "Mal", - "save_as_template": "Lagre som mal", - "validation": { - "recipient_required": "Legg til minst én mottaker før du sender", - "body_required": "Skriv en melding eller legg ved en fil", - "attachments_uploading": "Vedlegg lastes fortsatt opp – meldingen sendes så snart opplastingen er ferdig", - "attachment_upload_failed": "Ikke sendt – et vedlegg kunne ikke lastes opp. Fjern det og prøv igjen." - }, - "upload_progress": "Laster opp {uploaded} / {total}", - "upload_cancel": "Avbryt opplasting", - "upload_failed": "Kunne ikke laste opp {filename}", - "attachment_too_large": "«{name}» er større enn serveren tillater ({max} per fil)", - "attachments_total_too_large": "Vedleggene ville overskride serverens grense på {max} per melding", - "drop_files": "Slipp filer for å legge ved", - "show_less": "Vis færre", - "send_failed": "Kunne ikke sende e-post", - "send_timeout": "Serveren sluttet å svare. Sjekk Sendt-mappen før du sender på nytt – meldingen kan ha blitt sendt.", - "continue_draft": "Fortsett å skrive", - "close_draft_title": "Lagre eller forkast utkast?", - "close_draft_message": "Du har ulagrede endringer. Vil du lagre dette som et utkast eller forkaste det?", - "smime_sign_on": "S/MIME-signering aktivert", - "smime_sign_off": "Aktiver S/MIME-signering", - "smime_encrypt_on": "S/MIME-kryptering aktivert", - "smime_encrypt_off": "Aktiver S/MIME-kryptering", - "smime_encrypt_unavailable": "S/MIME-kryptering er utilgjengelig – mottakersertifikater mangler", - "smime_unlock_title": "Lås opp S/MIME-nøkkel", - "smime_unlock_message": "Skriv inn passordfrasen for å låse opp S/MIME-signeringsnøkkelen.", - "smime_unlock_button": "Lås opp", - "smime_passphrase_placeholder": "Passordfrase", - "forgot_attachment": { - "title": "Har du glemt et vedlegg?", - "message": "Meldingen din nevner «{keyword}», men ingen fil er vedlagt. Sende likevel?", - "send_anyway": "Send likevel", - "back": "Tilbake til redigering" - }, - "empty_subject": { - "title": "Sende uten emne?", - "message": "Denne meldingen har ikke noe emne. Sende den likevel?", - "dont_ask_again": "Ikke spør igjen", - "send_anyway": "Send likevel", - "back": "Tilbake til redigering" - }, - "schedule_send": "Planlegg sending", - "schedule_send_description": "Velg når serveren skal sende denne meldingen.", - "schedule_send_required": "Velg dato og klokkeslett.", - "schedule_send_invalid": "Skriv inn en gyldig dato og klokkeslett.", - "schedule_send_future": "Velg et tidspunkt i fremtiden.", - "schedule_send_too_late": "Dette tidspunktet er senere enn serveren tillater.", - "schedule_send_unsupported": "Planlagt sending støttes ikke for denne kontoen.", - "schedule_send_cleanup_warning": "Sendingen ble planlagt, men oppryddingen av utkastet mislyktes.", - "send_delay_unsupported": "Sendeforsinkelse støttes ikke for denne kontoen.", - "send_delay_unsupported_confirm": "Denne kontoen støtter ikke sendeforsinkelse. Send umiddelbart i stedet?", - "recipient_edit_email": "Rediger e-postadresse", - "recipient_edit_name": "Rediger visningsnavn", - "recipient_email_placeholder": "E-postadresse", - "recipient_name_placeholder": "Visningsnavn", - "autocomplete_search_server": "Søk på serveren", - "autocomplete_searching": "Søker...", - "toolbar": { - "bold": "Fet skrift", - "italic": "Kursiv", - "underline": "Understreking", - "strikethrough": "Gjennomstreking", - "text_color": "Tekstfarge", - "remove_color": "Fjern farge", - "heading_1": "Overskrift 1", - "heading_2": "Overskrift 2", - "bullet_list": "Punktliste", - "ordered_list": "Nummerert liste", - "quote": "Sitat", - "code_block": "Kodeblokk", - "align_left": "Venstrejuster", - "align_center": "Midtstill", - "align_right": "Høyrejuster", - "text_direction": "Tekstretning (RTL/LTR)", - "link": "Lenke", - "table": "Tabell", - "clear_formatting": "Fjern formatering", - "undo": "Angre", - "redo": "Gjenta", - "add_row_above": "Legg til rad over", - "add_row_below": "Legg til rad nedenfor", - "add_column_before": "Legg til kolonne før", - "add_column_after": "Legg til kolonne etter", - "delete_row": "Slett rad", - "delete_column": "Slett kolonne", - "toggle_header_row": "Slå overskriftsraden av eller på", - "delete_table": "Slett tabell", - "pick_size": "Velg størrelse", - "font_size": "Skriftstørrelse", - "font_size_default": "Standard", - "background_color": "Bakgrunnsfarge", - "remove_background_color": "Fjern bakgrunnsfarge" - }, - "send_filing_warning": "Meldingen ble sendt, men oppryddingen etterpå mislyktes. Et gammelt utkast kan bli liggende." - }, - "confirm_dialog": { - "confirm": "Bekreft", - "cancel": "Avbryt" - }, - "common": { - "loading": "Laster inn...", - "error": "Feil", - "success": "Vellykket", - "cancel": "Avbryt", - "save": "Lagre", - "delete": "Slett", - "edit": "Rediger", - "close": "Lukk", - "search": "Søk", - "refresh": "Oppdater", - "settings": "Innstillinger", - "help": "Hjelp", - "logout": "Logg ut", - "yes": "Ja", - "no": "Nei", - "unknown": "Ukjent", - "app_title": "Nettpost", - "reconnecting": "Tilkoblingen ble brutt. Forsøker å koble til på nytt...", - "rate_limited_title": "Serveren begrenser midlertidig antall autentiseringsforsøk.", - "rate_limited_detail": "Bulwark har satt bakgrunnsforespørsler på pause for å unngå at kontoen blir låst. Prøver på nytt om {seconds} sekunder.", - "rate_limited_action_title": "Forespørselen er satt på pause for å unngå at kontoen blir låst.", - "rate_limited_action_detail": "Bulwark venter til sperreperioden på serveren er over før flere autentiserte forespørsler sendes. Prøv igjen om {seconds} sekunder.", - "pull_to_refresh": "Dra ned for å oppdatere", - "release_to_refresh": "Slipp for å oppdatere", - "refreshing": "Oppdaterer..." - }, - "notifications": { - "email_sent": "E-posten er sendt", - "email_deleted": "E-posten er slettet", - "email_archived": "E-posten er arkivert", - "emails_deleted": "{count, plural, one {# e-post slettet} other {# e-poster slettet}}", - "emails_archived": "{count, plural, one {# e-post arkivert} other {# e-poster arkivert}}", - "error_archiving": "Kunne ikke arkivere e-posten", - "error_updating": "Kunne ikke oppdatere e-posten", - "email_starred": "E-posten er stjernemerket", - "email_unstarred": "E-posten er ikke lenger stjernemerket", - "email_marked_read": "E-posten er merket som lest", - "email_marked_unread": "E-posten er merket som ulest", - "copied_to_clipboard": "Kopiert til utklippstavlen", - "source_copied": "Meldingskilden er kopiert til utklippstavlen", - "error_sending": "Kunne ikke sende e-post", - "error_deleting": "Kunne ikke slette e-post", - "error_loading": "Kunne ikke laste inn e-poster", - "new_email": "Ny e-post", - "new_email_from": "Fra {sender}", - "click_to_view": "Klikk for å se", - "email_moved": "E-posten er flyttet", - "emails_moved": "{count} e-poster er flyttet", - "moved_to_mailbox": "Flyttet til {mailbox}", - "move_failed": "Kunne ikke flytte e-posten", - "move_error": "Kunne ikke flytte e-poster til den valgte mappen", - "email_tagged": "E-posten er merket med en etikett", - "emails_tagged": "{count} e-poster er merket med en etikett", - "tag_failed": "Kunne ikke legge til etiketten", - "identity_created": "Identiteten er opprettet", - "identity_updated": "Identiteten er oppdatert", - "identity_deleted": "Identiteten er slettet", - "identity_set_primary": "Hovedidentiteten er oppdatert", - "identity_create_failed": "Kunne ikke opprette identitet: {error}", - "identity_update_failed": "Kunne ikke oppdatere identitet: {error}", - "identity_delete_failed": "Kunne ikke slette identitet: {error}", - "identity_unauthorized": "Du er ikke autorisert til å sende fra denne e-postadressen", - "identity_not_found": "Finner ikke identiteten", - "vacation_saved": "Innstillinger for feriesvar er lagret", - "vacation_save_failed": "Kunne ikke lagre innstillingene for feriesvar", - "filters_saved": "Filtrene er lagret", - "filters_save_failed": "Kunne ikke lagre filtrene", - "filters_deleted": "Filterregelen er slettet", - "templates_exported": "Malene er eksportert", - "templates_imported": "{count, plural, one {# mal} other {# maler}} importert", - "templates_import_errors": "Noen maler kunne ikke importeres", - "templates_import_empty": "Fant ingen maler i filen", - "export_email_error": "Kunne ikke eksportere e-post", - "import_email_success": "E-posten er importert", - "import_email_error": "Kunne ikke importere e-post" - }, - "date": { - "today": "I dag", - "yesterday": "I går", - "this_week": "Denne uken", - "last_week": "Forrige uke", - "this_month": "Denne måneden", - "older": "Eldre", - "just_now": "Akkurat nå", - "minutes_ago": "{count} minutt siden", - "minutes_ago_plural": "{count} minutter siden", - "hours_ago": "{count} time siden", - "hours_ago_plural": "{count} timer siden", - "days_ago": "{count} dag siden", - "days_ago_plural": "{count} dager siden" - }, - "language": { - "title": "Språk", - "english": "engelsk", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Velg språk", - "switch_to_english": "Bytt til engelsk", - "switch_to_french": "Bytt til fransk", - "switch_to_japanese": "Bytt til japansk", - "switch_to_spanish": "Bytt til spansk", - "switch_to_italian": "Bytt til italiensk", - "switch_to_german": "Bytt til tysk", - "switch_to_dutch": "Bytt til nederlandsk", - "switch_to_portuguese": "Bytt til portugisisk", - "switch_to_russian": "Bytt til russisk", - "switching": "Bytter språk...", - "switch": "Bytt språk", - "current": "Gjeldende språk", - "polish": "Polski", - "switch_to_polish": "Bytt til polsk", - "en": "engelsk", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Bytt til farsi", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Bytt til mongolsk" - }, - "settings": { - "title": "Innstillinger", - "back_to_mail": "Tilbake til e-post", - "save_success": "Innstillingene er lagret", - "import_success": "Innstillingene er importert", - "import_error": "Kunne ikke importere innstillinger", - "reset_confirm": "Vil du tilbakestille alle innstillingene?", - "unsaved_changes": "Du har ulagrede endringer", - "discard_changes": "Vil du forkaste ulagrede endringer?", - "discard": "Forkast", - "keep_editing": "Fortsett å redigere", - "search_placeholder": "Søk i innstillinger", - "search_clear": "Tøm søket", - "search_no_results": "Ingen innstillinger passer med søket", - "tabs": { - "appearance": "Utseende", - "language": "Språk, region og tid", - "email": "E-posthåndtering", - "composer": "Skriving", - "privacy": "Personvern og sikkerhet", - "account": "Konto", - "identities": "Identiteter", - "vacation": "Feriesvar", - "advanced": "Avansert", - "calendar": "Kalender", - "filters": "Filtre", - "templates": "Maler", - "folders": "Mapper", - "keywords": "Etiketter", - "security": "Sikkerhet", - "files": "Filer", - "contacts": "Kontakter", - "encryption": "Kryptering", - "protocol_handlers": "Standardapper", - "sidebar_apps": "Apper i sidefeltet", - "notifications": "Varsler", - "layout": "Oppsett", - "reading": "Lesing", - "composing": "Skriving", - "downloads": "Nedlastinger", - "content_senders": "Innhold og avsendere", - "about_data": "Om programmet og data", - "debug": "Feilsøking" - }, - "tab_groups": { - "general": "Generelt", - "account": "Konto og identitet", - "organization": "Organisering av e-post", - "apps": "Apper", - "system": "System", - "appearance": "Utseende", - "mail": "E-post", - "privacy": "Personvern og sikkerhet", - "advanced": "Avansert" - }, - "appearance": { - "message_list_order": { - "label": "Rekkefølge i meldingslisten", - "description": "Hvordan meldingene sorteres i listen. Sorteringen utføres av serveren og gjelder derfor hele mappen, ikke bare meldingene som er lastet inn.", - "preset": { - "chronological": "Kronologisk (nyeste først)", - "unread_first": "Uleste først", - "starred_first": "Stjernemerkede først", - "tagged_first": "Meldinger med etikett først", - "custom": "Egendefinert" - }, - "tag_label": "Etikett", - "advanced_show": "Avansert: Rediger sorteringsnivåer", - "advanced_hide": "Skjul avanserte innstillinger", - "criterion_label": "Sorteringskriterium på nivå {n}", - "direction_label": "Sorteringsretning på nivå {n}", - "criteria": { - "unread": "Ulest", - "starred": "Stjernemerket", - "tag": "Etikett", - "receivedAt": "Mottaksdato", - "sentAt": "Sendingsdato", - "from": "Avsender", - "subject": "Emne", - "size": "Størrelse" - }, - "direction": { - "unread_first": "Uleste først", - "read_first": "Leste først", - "starred_first": "Stjernemerkede først", - "unstarred_first": "Uten stjerne først", - "tagged_first": "Med etikett først", - "untagged_first": "Uten etikett først", - "newest_first": "Nyeste først", - "oldest_first": "Eldste først", - "a_to_z": "A → Å", - "z_to_a": "Å → A", - "largest_first": "Største først", - "smallest_first": "Minste først" - }, - "add_level": "Legg til nivå", - "remove_level": "Fjern nivå", - "move_up": "Flytt opp", - "move_down": "Flytt ned", - "tiebreak_note": "Opptil tre nivåer. Senere nivåer avgjør rekkefølgen ved likhet. Nyeste først brukes alltid til slutt.", - "scope": { - "label": "Bruk på alle mapper", - "description": "Når dette er slått av, gjelder rekkefølgen bare for innboksen. Andre mapper forblir kronologiske." - }, - "unsupported_note": "E-postserveren din støtter ikke sortering etter lesestatus, stjerner eller etiketter. Disse nivåene ignoreres, og listen forblir kronologisk." - }, - "title": "Utseende", - "description": "Tilpass utseendet til nettposten", - "theme": { - "label": "Tema", - "description": "Velg fargevalget du foretrekker", - "light": "Lyst", - "dark": "Mørkt", - "system": "System" - }, - "language": { - "label": "Språk", - "description": "Velg språket du foretrekker" - }, - "font_size": { - "label": "Skriftstørrelse", - "description": "Juster tekststørrelsen for bedre lesbarhet", - "small": "Liten", - "medium": "Middels", - "large": "Stor" - }, - "list_density": { - "label": "Tetthet", - "description": "Juster avstand og luft i brukergrensesnittet", - "extra_compact": "Ekstra kompakt", - "compact": "Kompakt", - "regular": "Vanlig", - "comfortable": "Luftig" - }, - "animations": { - "label": "Aktiver animasjoner", - "description": "Vis jevne overganger og effekter" - }, - "toolbar_position": { - "label": "Plassering av verktøylinjen", - "description": "Velg hvor handlingsknapper for e-post skal vises (Svar, Arkiver, Slett osv.)", - "top": "Topp", - "below_subject": "Under emnet" - }, - "toolbar_labels": { - "label": "Vis tekst ved ikonene", - "description": "Vis tekst ved siden av ikonene på verktøylinjen. Deaktiver dette for å spare plass når du har blitt kjent med ikonene." - }, - "hide_account_switcher": { - "label": "Skjul kontobytter i sidefeltet", - "description": "Skjul kontovelgeren øverst i mappesidefeltet. Du kan fortsatt bytte konto fra navigasjonsfeltet nederst." - }, - "show_rail_account_list": { - "label": "Vis kontobilder i navigasjonsfeltet", - "description": "Vis et kontobilde for hver konto nederst i navigasjonsfeltet, slik at du raskt kan bytte konto. En utloggingsknapp vises nedenfor." - }, - "unified_mailbox": { - "label": "Samlet postkasse", - "description": "Vis kombinerte mapper (innboks, sendt osv.) for den aktive kontoen og dens delte mapper.", - "cross_account": { - "label": "På tvers av alle kontoer", - "description": "Slå sammen den samlede postkassen på tvers av alle tilkoblede kontoer, i stedet for å begrense den til den aktive kontoen." - }, - "include_group": { - "label": "Ta med gruppeinnbokser", - "description": "Ta også med delte innbokser og gruppeinnbokser i den samlede visningen." - } - }, - "all_mail": { - "folders_label": "Mapper i de samlede listene", - "folders_description": "Velg hvilke mapper fra denne kontoen som skal tas med i listene All e-post, Ulest og Stjernemerket.", - "account_hint": "Gjelder {account}.", - "no_folders": "Ingen mapper er tilgjengelige." - }, - "colorful_sidebar_icons": { - "label": "Fargede ikoner i sidefeltet", - "description": "Fargelegg mappe- og etikettikoner etter type (blått ikon for Innboks, rødt for Søppelpost, grønt for Sendt osv.). Deaktiver dette for et ensfarget sidefelt." - }, - "tint_list_rows": { - "label": "Fargelegg meldingsrader etter etikett", - "description": "Gi hver meldingsrad en svak bakgrunnsfarge basert på den første etiketten. Deaktiver dette for nøytrale rader; etikettmarkørene viser fortsatt fargen." - }, - "show_folder_total_count": { - "label": "Vis totalt antall meldinger", - "description": "Vis totalt antall meldinger ved siden av mapper og etiketter, sammen med antall uleste. Deaktiver for å vise bare antall uleste." - }, - "favicon_unread_badge": { - "label": "Antall uleste på faneikonet", - "description": "Vis antall uleste meldinger i innboksen som et merke på nettleserfaneikonet, slik at ny e-post er synlig når fanen ikke er fokusert. Deaktiver for å beholde ikonet uten merke." - }, - "pro_interface": { - "label": "Pro-grensesnitt (eksperimentelt)", - "description": "Skrivebordsoppsett for erfarne brukere, med meldinger i flere faner og arbeidsflyter på tvers av kontoer. Standardgrensesnittet påvirkes ikke, og du kan bytte tilbake når som helst.", - "open_label": "Åpne Pro-grensesnittet", - "back_to_standard": "Tilbake til standardgrensesnittet" - }, - "cross_unread": { - "label": "Ulest", - "description": "Vis Ulest i den samlede postkassen, med ulest e-post fra de valgte mappene." - }, - "cross_starred": { - "label": "Stjernemerket", - "description": "Vis Stjernemerket i den samlede postkassen, med stjernemerket e-post fra de valgte mappene." - }, - "cross_all": { - "label": "All e-post", - "description": "Vis All e-post i den samlede postkassen, med all e-post fra de valgte mappene." - } - }, - "keywords": { - "title": "E-postetiketter", - "description": "Definer etiketter for å organisere e-postene dine. De lagres som JMAP-nøkkelord på serveren.", - "add_keyword": "Legg til etikett", - "label_field": "Visningsnavn", - "label_placeholder": "f.eks. arbeid, personlig, haster", - "id_field": "Etikett-ID", - "id_placeholder": "f.eks. jobb, personlig", - "color_field": "Farge", - "id_exists": "Denne etikett-ID-en finnes allerede", - "edit": "Rediger etikett", - "delete": "Slett etikett", - "save": "Lagre", - "add": "Legg til", - "cancel": "Avbryt", - "migrating": "Oppdaterer etiketten på eksisterende e-poster...", - "migration_error": "Kunne ikke oppdatere etiketten på eksisterende e-poster", - "nesting": { - "label": "Nestede etiketter", - "description": "Plasser etiketter under hverandre og vis dem som et hierarki i sidefeltet." - }, - "parent_field": "Overordnet etikett", - "no_parent": "Ingen overordnet", - "too_long": "Denne etikettstien er for lang (høyst {max} tegn)", - "has_children_locked": "Andre etiketter er nestet under denne, så navnet og den overordnede etiketten er låst. Flytt eller fjern dem først.", - "has_children_delete": "Fjern etikettene som er nestet under denne først", - "visibility_field": "Synlighet i sidefeltet", - "visibility": { - "show": "Vis", - "unread": "Vis hvis ulest", - "hide": "Skjul" - }, - "unrecognized": { - "title": "Gjenopprett etiketter fra serveren", - "description": "Navn og farger på etiketter lagres i denne nettleseren, men selve etikettene finnes på meldingene som JMAP-nøkkelord. Søk gjennom meldingene etter nøkkelord som ikke er definert her, og legg dem til igjen.", - "scan": "Søk etter etiketter", - "rescan": "Søk på nytt", - "scanning": "Søker... {scanned} av {total} meldinger", - "none": "Alle etikettene på meldingene dine er allerede definert her.", - "messages": "{count, plural, one {# melding} other {# meldinger}}", - "add_all": "Legg til alle {count}", - "partial": "Bare de {scanned} siste av {total} meldingene ble skannet. Etiketter som bare brukes på eldre e-poster, kan mangle.", - "error": "Kunne ikke søke gjennom meldingene etter etiketter." - } - }, - "notifications": { - "test_sound": "Test varslingslyden", - "sounds": { - "default": "Standard (pip)", - "cheerful": "Munter", - "involved": "Engasjert", - "swift": "Rask", - "relax": "Rolig" - }, - "push": { - "title": "Bakgrunnsvarsler", - "description": "Motta systemvarsler om ny e-post selv når denne siden ikke er åpen. Varslene leveres via Bulwarks push-relé, som aldri ser innholdet i e-posten.", - "relay_label": "Push-relé", - "relay_desc": "Reléet som driftes av Bulwark, brukes som standard. Endre dette bare hvis du drifter et eget relé.", - "relay_locked": "Angitt av administrator", - "relay_locked_desc": "URL-en til push-reléet er angitt av administratoren og kan ikke endres.", - "status_active": "Aktiv på denne enheten", - "status_inactive": "Ikke aktivert på denne enheten", - "status_unsupported": "Denne nettleseren støtter ikke Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "Behandler...", - "enable": "Aktiver", - "reenable": "Registrer på nytt", - "disable": "Deaktiver", - "confirm_disable_title": "Vil du deaktivere bakgrunnsvarsler?", - "confirm_disable_message": "Denne enheten slutter å motta varsler når Bulwark ikke er åpent.", - "dismiss_aria": "Lukk forespørselen om varsler", - "ios_hint": "På iOS må du først installere nettstedet på startskjermen – Safari leverer bare Web Push til installerte PWA-er.", - "devices_title": "Registrerte enheter", - "devices_desc": "Alle enheter som er registrert for å motta bakgrunnsvarsler for denne kontoen. Hvis du tilbakekaller tilgangen for en enhet, slutter den umiddelbart å motta varsler.", - "devices_loading": "Laster inn registrerte enheter...", - "devices_empty": "Ingen enheter er registrert for bakgrunnsvarsler.", - "devices_error": "Kunne ikke laste inn registrerte enheter.", - "devices_refresh": "Oppdater", - "device_this": "Denne enheten", - "device_other": "Annen enhet", - "device_status_active": "Mottar varsler", - "device_status_inactive": "Har aldri mottatt et varsel", - "device_status_unknown": "Ukjent for dette reléet", - "device_expires": "Utløper {date}", - "revoke": "Tilbakekall", - "confirm_revoke_title": "Vil du tilbakekalle tilgangen for denne enheten?", - "confirm_revoke_message": "Enheten slutter umiddelbart å motta bakgrunnsvarsler. Hvis pushvarsler aktiveres på nytt på enheten, registreres den på nytt.", - "confirm_revoke_message_this": "Denne enheten slutter umiddelbart å motta bakgrunnsvarsler." - }, - "sound_selection": { - "title": "Varslingslyd", - "description": "Velg hvilken lyd som skal spilles av for varsler", - "choose": "Lyd", - "choose_desc": "Velg en varseltone og klikk på høyttalerikonet for å forhåndsvise den" - }, - "email": { - "title": "E-postvarsler", - "description": "Velg innstillinger for varsler om innkommende e-post", - "enabled": "E-postvarsler", - "enabled_desc": "Vis varsler når nye e-poster kommer", - "sound": "Varslingslyd", - "sound_desc": "Spill av et lydvarsel når nye e-poster kommer", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Kalendervarsler", - "description": "Velg innstillinger for varsler om kalenderhendelser", - "enabled": "Hendelsesvarsler", - "enabled_desc": "Vis varsler for kommende kalenderhendelser", - "sound": "Varslingslyd", - "sound_desc": "Spill av et lydvarsel for kalenderpåminnelser", - "invitation_parsing": "Les kalenderinvitasjoner i e-post", - "invitation_parsing_desc": "Finn kalenderinvitasjoner i e-postvedlegg og vis tilgjengelige kalenderhandlinger" - } - }, - "language_region": { - "title": "Språk, region og tid", - "description": "Språk, datoformat, tidsformat og andre regionale preferanser", - "language": { - "label": "Språk", - "description": "Velg språket du foretrekker", - "english": "engelsk", - "french": "Français" - }, - "date_format": { - "label": "Datoformat", - "description": "Hvordan datoer vises i e-postlisten", - "smart": "Automatisk (tilpasset språk og region)", - "relative": "Relativ (1 t siden, 2 d siden)", - "full": "Alltid fullstendig dato", - "preview_today": "I dag:", - "preview_this_week": "Denne uken:", - "preview_older": "Eldre:" - }, - "date_locale": { - "label": "Regionalt datoformat", - "description": "Rekkefølgen i numeriske datoer (dag, måned og år)", - "auto": "Automatisk (følger språket)", - "iso": "ISO 8601 (ÅÅÅÅ-MM-DD)", - "dmy": "Dag/måned/år", - "mdy": "Måned/dag/år" - }, - "time_format": { - "label": "Tidsformat", - "description": "Velg mellom 12- og 24-timersklokke", - "12h": "12-timersformat", - "24h": "24-timersformat" - }, - "first_day": { - "label": "Første dag i uken", - "description": "Start uken på søndag eller mandag", - "sunday": "søndag", - "monday": "mandag" - }, - "time_zone": { - "label": "Tidssone", - "description": "Vis klokkeslett for e-post og kalender i denne tidssonen i stedet for tidssonen som nettleseren oppgir", - "auto": "Automatisk ({zone})", - "preview_now": "Nå:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Tøm søket ved bytte av mappe", - "description": "Å klikke på en mappe (Innboks, Sendt …) tømmer det aktive søket og viser mappen. Når det er av, beholdes søket og kjøres på nytt i mappen du bytter til." - }, - "request_read_receipt": { - "label": "Be om lesebekreftelser som standard", - "description": "Be automatisk om lesebekreftelse når du skriver en ny melding." - }, - "read_receipt_response": { - "label": "Svar på forespørsler om lesebekreftelse", - "description": "Velg hva som skal skje når en innkommende melding ber om lesebekreftelse.", - "ask": "Spør hver gang", - "always": "Send alltid", - "never": "Send aldri" - }, - "title": "E-posthåndtering", - "description": "Velg hvordan e-post skal håndteres", - "mark_read": { - "label": "Merk som lest", - "description": "Velg når e-poster skal merkes som lest etter at de er åpnet", - "instant": "Øyeblikkelig", - "delay_3s": "Etter 3 sekunder", - "delay_5s": "Etter 5 sekunder", - "never": "Aldri" - }, - "delete_action": { - "label": "Handling ved sletting", - "description": "Velg hva som skal skje når du sletter en e-post", - "trash": "Flytt til papirkurven", - "trash_and_read": "Flytt til papirkurven og merk som lest", - "permanent": "Slett permanent", - "warning": "E-poster slettes permanent og kan ikke gjenopprettes. Handlingen kan ikke angres." - }, - "message_spacing": { - "label": "Luft rundt meldingen", - "description": "Luft rundt meldingsteksten i leseren", - "auto": "Automatisk (anbefalt)", - "always": "Legg alltid til luft", - "edge": "Kant til kant" - }, - "plain_text_font": { - "label": "Skrifttype for ren tekst", - "description": "Skrifttypen som brukes i e-poster med ren tekst", - "mono": "Fast tegnbredde", - "sans": "Normal (samme som programmet)" - }, - "archive_mode": { - "label": "Arkiver i", - "description": "Hvordan organisere e-poster ved arkivering", - "single": "En enkelt mappe", - "year": "En mappe per år", - "month": "En mappe per måned", - "reorganize": "Omorganiser eksisterende arkiv", - "reorganize_success": "{count, plural, =0 {Ingen e-poster å omorganisere} =1 {1 e-post omorganisert} other {# e-poster omorganisert}}", - "reorganize_error": "Kunne ikke omorganisere arkivet" - }, - "permanently_delete_junk": { - "label": "Slett søppelpost permanent", - "description": "Slett e-poster permanent fra søppelpostmappen i stedet for å flytte dem til papirkurven" - }, - "mail_layout": { - "label": "E-postoppsett", - "description": "Velg mellom den klassiske delte leseruten, en fokusert leseflyt i Gmail-stil eller en bunnleserute i Zimbra-stil.", - "split": "Delt rute", - "split_description": "Hold meldingslisten og leseruten synlige side ved side.", - "focus": "Fokusert liste", - "focus_description": "Vis én linje per melding, og åpne meldingen i full bredde samtidig som mappesidefeltet forblir synlig.", - "horizontal": "Leseruten nederst", - "horizontal_description": "Vis meldingslisten øverst og åpne den valgte meldingen i en leserute under den." - }, - "show_preview": { - "label": "Vis forhåndsvisningstekst", - "description": "Vis forhåndsvisning av e-post i listen", - "focus_description": "Vis innebygd forhåndsvisningstekst i den fokuserte meldingslisten på én linje" - }, - "disable_threading": { - "label": "Deaktiver samtalegruppering", - "description": "Vis e-poster som individuelle meldinger i stedet for gruppert etter samtale" - }, - "plain_text_mode": { - "label": "Kun ren tekst", - "description": "Deaktiver redigering av formatert tekst, og send all e-post som ren tekst, også svar og videresendinger" - }, - "auto_select_reply_identity": { - "label": "Svar fra mottatt adresse", - "description": "Når du svarer, bruker Bulwark adressen meldingen opprinnelig ble sendt til. Programmet ser først etter en samsvarende identitet. For meldinger til et oppsamlingsdomene brukes aliaset i Fra-feltet, mens sendingen går gjennom hovedidentiteten din." - }, - "reply_identity_match": { - "label": "Samsvar for mottaksadresse", - "description": "Hvilke mottaksadresser som regnes som dine. «Kun nøyaktig adresse» velger bare en av dine oppsatte identiteter; «samme domene» behandler i tillegg enhver annen adresse på identitetsdomenene dine som et catch-all-alias og skriver om Fra-headeren til den. Velg nøyaktig adresse hvis disse adressene er distribusjonslister.", - "exact": "Kun nøyaktig adresse", - "domain": "Enhver adresse på mine domener" - }, - "signature_position": { - "label": "Signaturposisjon", - "description": "Velg hvor signaturen skal settes inn i svar og videresendinger. Over den siterte teksten fungerer den som en naturlig avslutning på svaret. Under den siterte teksten holdes den opprinnelige meldingen samlet.", - "above_quote": "Før sitert tekst", - "below_quote": "Etter sitert tekst" - }, - "signature_separator": { - "label": "Signaturavgrensning", - "description": "Sett standardavgrensningen «-- » på en egen linje foran signaturen (RFC 3676). Slå av hvis meldingen skal gå rett over i signaturen." - }, - "sub_address_delimiter": { - "label": "Skilletegn for underadresser", - "description": "Tegnet som skiller brukernavnet fra en underadressekode. Bruk samme skilletegn som e-postserveren (f.eks. user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "Egendefinert...", - "custom_input_label": "Egendefinert skilletegn" - }, - "attachment_click_action": { - "label": "Handling ved klikk på vedlegg", - "description": "Velg om et klikk på et filvedlegg skal forhåndsvise det eller laste det ned umiddelbart", - "preview": "Forhåndsvis når det er mulig", - "download": "Last ned umiddelbart" - }, - "attachment_position": { - "label": "Plassering av vedlegg", - "description": "Velg hvor vedlegg skal vises i e-postoverskriften", - "beside-sender": "Ved siden av avsender", - "below-header": "Under overskriften" - }, - "emails_per_page": { - "10": "10 e-poster", - "25": "25 e-poster", - "50": "50 e-poster", - "100": "100 e-poster", - "label": "E-poster per side", - "description": "Antall e-poster som skal lastes inn samtidig" - }, - "always_light_mode": { - "label": "Vis alltid e-poster i lysmodus", - "description": "Vis e-postinnhold i lys modus selv når programmet bruker mørk modus. Dette unngår problemer ved konvertering til mørk modus." - }, - "external_content": { - "label": "Eksternt innhold", - "description": "Hvordan håndtere bilder og eksternt innhold", - "ask": "Spør alltid", - "block": "Blokker alltid", - "allow": "Tillat alltid" - }, - "trusted_senders": { - "label": "Pålitelige avsendere", - "description": "Administrer avsendere du alltid vil laste inn bilder fra", - "count_zero": "Ingen", - "count_one": "1 avsender", - "count_other": "{count} avsendere", - "modal_title": "Pålitelige avsendere", - "empty_title": "Ingen pålitelige avsendere ennå", - "empty_description": "Når du ser på en e-post med blokkerte bilder, klikker du «Stol alltid på denne avsenderen» for å legge til avsenderen her.", - "add_manually": "Legg til avsender manuelt", - "add_button": "Legg til", - "add_placeholder": "Skriv inn e-postadresse", - "search_placeholder": "Søk etter avsendere...", - "no_results": "Ingen avsendere passer med søket ditt", - "remove": "Fjern", - "close": "Lukk", - "invalid_email": "Skriv inn en gyldig e-postadresse", - "already_added": "Denne avsenderen står allerede på listen over pålitelige avsendere", - "save_error": "Kunne ikke lagre – se feilsøkingsloggen for kontakter for mer informasjon", - "use_address_book_label": "Synkroniser med adressebok", - "use_address_book_description": "Lagre pålitelige avsendere i en egen «Trusted Senders»-adressebok, slik at de synkroniseres på tvers av alle enhetene dine" - }, - "hover_actions": { - "label": "Hurtighandlinger når du peker", - "description": "Velg hvilke hurtighandlinger som skal vises når du holder musepekeren over en e-post i listen", - "delete": "Slett", - "star": "Legg til eller fjern stjerne", - "mark_read": "Merk som lest eller ulest", - "archive": "Arkiver", - "tag": "Etikett", - "spam": "Merk som søppelpost", - "not_spam": "Ikke søppelpost", - "none_selected": "Ingen handlinger er valgt", - "mode_label": "Visningsmodus", - "mode_inline": "På raden", - "mode_floating": "Flytende", - "corner_label": "Plassering av flytende handlinger", - "corner_top-left": "Øverst til venstre", - "corner_top-right": "Øverst til høyre", - "corner_bottom-left": "Nederst til venstre", - "corner_bottom-right": "Nederst til høyre" - }, - "default_mail_program": { - "label": "Standard e-postprogram", - "description": "Registrer {appName} som standard e-postprogram for mailto:-lenker", - "button": "Sett som standard", - "success": "Nettleseren ba deg angi programmet som standard", - "error": "Nettleseren din støtter ikke denne funksjonen" - }, - "attachment_reminder": { - "label": "Vedleggspåminnelse", - "description": "Advar før sending når meldingen nevner vedlegg, men ingen er vedlagt", - "keywords_label": "Ord som utløser påminnelsen", - "keywords_description": "Ord eller uttrykk som utløser påminnelsen når de finnes i meldingen", - "add_placeholder": "Legg til søkeord...", - "add": "Legg til", - "remove": "Fjern" - }, - "empty_subject_warning": { - "label": "Advarsel om tomt emne", - "description": "Be om bekreftelse før du sender en melding uten emne." - }, - "hide_inline_image_attachments": { - "label": "Skjul innebygde bilder i vedleggslisten", - "description": "Bilder som er innebygd i meldingsteksten, er ikke oppført som separate vedlegg" - }, - "attachment_image_previews": { - "label": "Vis forhåndsvisninger av bilder i vedlegg", - "description": "Vis bildevedlegg som miniatyrer i stedet for vanlige filikoner" - }, - "send_delay": { - "label": "Angre sending (sendeforsinkelse)", - "description": "Forsink vanlige sendinger i et kort tidsrom på serveren.", - "off": "Av", - "seconds": "{seconds} sekunder", - "unsupported": "Denne kontoen støtter ikke forsinket sending. Innstillingen lagres likevel for andre kontoer." - }, - "return_to_list_after_action": { - "label": "Gå tilbake til listen etter sletting eller merking som ulest", - "description": "Gå tilbake til meldingslisten i stedet for å åpne neste melding etter at du har slettet den åpne meldingen eller merket den som ulest." - }, - "swipe_right_action": { - "label": "Handling ved sveip mot høyre (mobil)", - "description": "Handlingen som utføres når du sveiper en melding mot høyre i listen på en berøringsenhet." - }, - "swipe_left_action": { - "label": "Handling ved sveip mot venstre (mobil)", - "description": "Handlingen som utføres når du sveiper en melding mot venstre i listen på en berøringsenhet." - }, - "swipe_actions": { - "none": "Ingen", - "archive": "Arkiver", - "delete": "Slett", - "mark_read": "Veksle mellom lest og ulest", - "star": "Legg til eller fjern stjerne", - "spam": "Merk som søppelpost" - }, - "rtl_editing": { - "label": "Støtte for redigering fra høyre mot venstre", - "description": "Legger til en retningsknapp på verktøylinjen for å angi avsnitt fra venstre mot høyre eller høyre mot venstre" - } - }, - "composer": { - "title": "Skriving", - "description": "Velg innstillinger for skriving av e-post", - "autosave": { - "label": "Intervall for automatisk lagring", - "description": "Hvor ofte utkast skal lagres automatisk", - "30s": "Hvert 30. sekund", - "1m": "Hvert minutt", - "2m": "Hvert 2. minutt", - "5m": "Hvert 5. minutt" - }, - "send_confirmation": { - "label": "Bekreft før sending", - "description": "Be om bekreftelse før du sender e-post" - }, - "default_reply": { - "label": "Standardhandling for svar", - "description": "Handlingen som brukes når du klikker på «Svar»", - "reply": "Svar", - "reply_all": "Svar alle" - } - }, - "privacy": { - "title": "Personvern og sikkerhet", - "description": "Velg innstillinger for personvern og sikkerhet", - "external_images": { - "label": "Blokker eksterne bilder", - "description": "Forhindre sporing gjennom eksterne bilder" - }, - "session_timeout": { - "label": "Tidsavbrudd for økten", - "description": "Logg ut automatisk etter inaktivitet", - "never": "Aldri", - "30m": "30 minutter", - "1h": "1 time", - "4h": "4 timer" - }, - "clear_cache": { - "label": "Tøm hurtigbuffer", - "description": "Fjern data i hurtigbufferen og midlertidige filer", - "button": "Tøm hurtigbuffer", - "confirm": "Er du sikker på at du vil tømme hurtigbufferen?", - "success": "Hurtigbufferen er tømt" - } - }, - "account": { - "title": "Konto", - "description": "Se kontoinformasjonen din", - "name_label": "Visningsnavn", - "username_label": "Brukernavn", - "account_type_label": "Kontotype", - "auth_method_label": "Autentisering", - "auth_method_oauth": "Enkel pålogging (OAuth/OIDC)", - "auth_method_basic": "Passord", - "demo_account": "Demokonto", - "email": { - "label": "E-postadresse", - "value": "{email}" - }, - "server": { - "label": "JMAP-server", - "value": "{server}" - }, - "storage": { - "label": "Lagringsbruk", - "used": "{used} av {total} brukt", - "percentage": "{percent}% brukt" - }, - "last_sync": { - "label": "Siste synkronisering", - "value": "{time}" - }, - "accounts": { - "title": "Innloggede kontoer", - "description": "Dra for å endre rekkefølgen på kontoene i kontomenyen", - "active": "Aktiv konto", - "default_badge": "Standardkonto", - "set_default": "Sett som standard", - "switch_to": "Bytt til denne kontoen", - "move_up": "Flytt opp", - "move_down": "Flytt ned", - "drag_handle": "Dra for å omorganisere", - "add": "Legg til konto" - }, - "shared_accounts": { - "title": "Delt med meg", - "description": "Gruppekontoer og delte kontoer du kan administrere. Velg en konto for å redigere filtre, feriesvar, kalendere og kontakter.", - "shared_label": "Delt konto" - } - }, - "scoped": { - "back": "Tilbake til kontoen min", - "managing": "Innstillinger for: {name}" - }, - "security": { - "title": "Kontosikkerhet", - "description": "Administrer passordet ditt, tofaktorautentisering og sikkerhetsinnstillinger", - "detecting": "Kontrollerer hvilke funksjoner serveren støtter...", - "not_available": "Administrasjon av kontosikkerhet er ikke tilgjengelig på denne e-postserveren. Nødvendige tillatelser kan være deaktivert. Se dokumentasjonen for mer informasjon.", - "password": { - "title": "Endre passord", - "current": "Gjeldende passord", - "new": "Nytt passord", - "confirm": "Bekreft nytt passord", - "submit": "Endre passord", - "success": "Passordet ble endret", - "error_title": "Passordendring mislyktes", - "error_mismatch": "De nye passordene er ikke like", - "error_min_length": "Passordet må inneholde minst 8 tegn", - "error_generic": "Kunne ikke endre passord" - }, - "display_name": { - "label": "Visningsnavn", - "description": "Navnet ditt slik det vises på serveren", - "placeholder": "Skriv inn visningsnavnet ditt", - "save": "Lagre", - "success": "Visningsnavnet er oppdatert", - "error": "Kunne ikke oppdatere visningsnavnet" - }, - "totp": { - "section_title": "Tofaktorautentisering", - "label": "TOTP-autentisering", - "description": "Legg til et ekstra lag med sikkerhet med et tidsbasert engangspassord", - "active": "Aktivert", - "inactive": "Deaktivert", - "enabled": "Tofaktorautentisering aktivert", - "disabled": "Tofaktorautentisering deaktivert", - "enable_error": "Kunne ikke aktivere tofaktorautentisering", - "disable_error": "Kunne ikke deaktivere tofaktorautentisering", - "setup_instructions": "Kopier denne URL-en til autentiseringsappen din (Google Authenticator, Authy osv.):", - "verification_code": "Bekreftelseskode", - "confirm": "Bekreft", - "disable": "Deaktiver", - "disable_confirm_prompt": "Skriv inn passordet ditt for å deaktivere tofaktorautentisering.", - "password_required": "Passord må fylles ut", - "code_required": "Bekreftelseskode må fylles ut", - "code_invalid": "Ugyldig bekreftelseskode. Sjekk autentiseringsappen din og prøv igjen." - }, - "app_passwords": { - "title": "App-passord", - "description": "Lag passord for apper som ikke støtter tofaktorautentisering", - "add": "Legg til", - "create": "Opprett", - "cancel": "Avbryt", - "done": "Ferdig", - "generate": "Generer", - "name_label": "Appnavn", - "name_placeholder": "f.eks. Thunderbird, iPhone Mail", - "expires_label": "Utløper (valgfritt)", - "allowed_ips_label": "Tillatte IP-er (valgfritt)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Skill adressene med komma eller mellomrom. La feltet stå tomt for å tillate alle IP-adresser.", - "password_label": "Passord (la feltet stå tomt for å generere automatisk)", - "password_placeholder": "Genereres automatisk hvis feltet er tomt", - "copy_now_warning": "Kopier dette passordet nå – det vises ikke igjen.", - "added": "App-passordet er opprettet", - "removed": "App-passordet er fjernet", - "add_error": "Kunne ikke opprette app-passord", - "remove_error": "Kunne ikke fjerne app-passordet", - "none": "Ingen app-passord er opprettet" - }, - "api_keys": { - "title": "API-nøkler", - "description": "Lag API-nøkler for skript og integrasjoner som kommuniserer direkte med serveren", - "name_label": "Nøkkelnavn", - "name_placeholder": "f.eks. sikkerhetskopieringsskript, CI-løper", - "copy_now_warning": "Kopier denne API-nøkkelen nå – den vises ikke igjen.", - "added": "API-nøkkelen er opprettet", - "removed": "API-nøkkelen er fjernet", - "add_error": "Kunne ikke opprette API-nøkkel", - "remove_error": "Kunne ikke fjerne API-nøkkelen", - "none": "Ingen API-nøkler er opprettet" - }, - "encryption": { - "section_title": "Kryptering av lagrede data", - "label": "E-postkryptering", - "description": "Krypter lagrede e-poster på serveren for ekstra personvern", - "active": "{type}-kryptering er aktivert", - "inactive": "Deaktivert", - "enabled": "Kryptering av lagrede data er aktivert", - "disabled_success": "Kryptering av lagrede data er deaktivert", - "error": "Kunne ikke oppdatere krypteringsinnstillingene", - "algorithm_label": "Krypteringsalgoritme", - "encrypt_on_append": "Krypter nye e-poster ved opplasting", - "allow_spam_training": "Tillat opplæring av søppelpostfilteret før e-post krypteres", - "enable_encryption": "Aktiver kryptering", - "enable_tooltip": "Aktiver kryptering med denne nøkkelen", - "configure_title": "Aktiver kryptering med denne nøkkelen", - "enable_button": "Aktiver", - "disable_tooltip": "Deaktiver kryptering", - "enabled_success": "Kryptering av lagrede data er aktivert" - }, - "public_keys": { - "name_label": "Navn på offentlig nøkkel", - "name_placeholder": "f.eks. Min nøkkel", - "key_label": "Offentlig nøkkel (S/MIME eller ASCII-armert PGP)", - "key_placeholder": "Lim inn den offentlige nøkkelen din her", - "none": "Ingen offentlige nøkler er lagt til" - }, - "email_client": { - "title": "Oppsett av e-postklient", - "description": "Bruk denne påloggingsinformasjonen til å sette opp en e-postklient på datamaskinen eller mobilen (Thunderbird, Apple Mail, Outlook osv.)", - "jmap_username_label": "JMAP-brukernavn", - "copy": "Kopier", - "copied": "Kopiert", - "password_instructions": "Bruk JMAP-brukernavnet ovenfor sammen med et app-passord for å logge på e-postklienten. Opprett et app-passord i delen ovenfor hvis du ikke allerede har gjort det." - }, - "link_device": { - "title": "Koble til mobilappen", - "description": "Logg på Bulwark Mail-mobilappen uten å skrive inn påloggingsopplysninger. Lag en QR-kode her, og skann den fra påloggingsskjermen i appen.", - "generate": "Vis QR-kode", - "regenerate": "Vis en ny kode", - "instructions": "Åpne Bulwark Mail-appen, trykk på «Skann QR-kode» på påloggingsskjermen, og rett kameraet mot denne koden.", - "expires_in": "Denne koden utløper om {seconds} sekunder. Den kan bare brukes én gang.", - "expired": "Denne koden er utløpt.", - "generating": "Genererer...", - "error": "Kunne ikke opprette en sammenkoblingskode. Prøv igjen." - } - }, - "identities": { - "title": "Avsenderidentiteter", - "description": "Administrer e-postadresser du kan sende fra", - "identities_count": { - "label": "Dine identiteter", - "description": "E-postadresser som er satt opp for sending", - "count_zero": "Ingen identiteter", - "count_one": "1 identitet", - "count_other": "{count} identiteter" - }, - "manage": "Administrer identiteter", - "sub_addressing": { - "label": "Underadressering", - "description": "Bruk koder som user+tag@domain.com for å organisere innkommende e-post", - "learn_more": "Lær mer" - } - }, - "vacation": { - "title": "Feriesvar", - "description": "Svar automatisk på innkommende e-poster mens du er borte", - "loading": "Laster inn innstillinger for feriesvar...", - "not_supported": "E-postserveren din støtter ikke feriesvar.", - "fetch_error": "Kunne ikke laste inn innstillingene for feriesvar. Prøv igjen.", - "status": { - "label": "Feriesvar", - "description": "Send et automatisk svar til folk som sender deg e-post", - "active": "Aktiv", - "inactive": "Inaktiv" - }, - "date_range": { - "title": "Datoperiode", - "description": "Begrens om ønskelig autosvaret til en bestemt periode", - "start": "Startdato", - "start_description": "La feltet stå tomt hvis det ikke skal være noen startdato", - "end": "Sluttdato", - "end_description": "La feltet stå tomt hvis det ikke skal være noen sluttdato" - }, - "message": { - "title": "Melding for automatisk svar", - "description": "Meldingen som sendes som svar", - "subject_label": "Emne", - "subject_description": "Emnelinje for autosvaret", - "subject_placeholder": "Ikke på kontoret", - "body_label": "Meldingstekst", - "body_description": "Meldingsinnhold i ren tekst", - "body_placeholder": "Takk for e-posten. Jeg er ikke på kontoret for øyeblikket og svarer når jeg er tilbake.", - "html_label": "Formatert melding (HTML)", - "html_description": "Legg til en versjon med rik formatering, lenker og stil. Mottakere med e-postklienter som ikke kan vise den, får versjonen i ren tekst ovenfor.", - "html_placeholder": "Skriv et formatert fraværssvar..." - }, - "preview": { - "title": "Forhåndsvisning", - "show": "Vis forhåndsvisning", - "hide": "Skjul forhåndsvisning" - }, - "save": "Lagre endringer", - "saving": "Lagrer...", - "warnings": { - "end_before_start": "Sluttdatoen må være etter startdatoen", - "start_in_past": "Startdatoen er i fortiden", - "empty_body": "Meldingsteksten er tom – mottakerne får et tomt svar", - "subject_too_long": "Emnet er for langt for serveren (maks. {max} byte).", - "body_too_long": "Meldingen er for lang for serveren (maks. {max} byte inkludert formatering)." - } - }, - "folders": { - "role_memos": "Notater", - "title": "Mapper", - "description": "Administrer e-postmappene dine og tildel standardroller", - "folder_list": "Dine mapper", - "folder_list_description": "Klikk på et mappeikon for å tilpasse det", - "standard_roles": "Standardroller for mapper", - "standard_roles_description": "Velg hvilke mapper som skal ha standardroller som innboks, sendt, papirkurv osv.", - "role_inbox": "Innboks", - "role_drafts": "Utkast", - "role_sent": "Sendt", - "role_trash": "Papirkurv", - "role_junk": "Søppelpost", - "role_archive": "Arkiv", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Ingen", - "create_folder": "Opprett mappe", - "create_subfolder": "Opprett undermappe", - "reorder": "Dra for å endre rekkefølgen", - "reorder_error": "Kunne ikke endre rekkefølgen på mappene", - "move_error": "Kunne ikke flytte mappen", - "subfolder_of": "I {name}", - "subfolder_name": "Navn på undermappe", - "new_folder_name": "Mappenavn", - "rename": "Gi nytt navn", - "change_icon": "Endre ikon", - "delete": "Slett", - "confirm_delete": "Er du sikker på at du vil slette «{name}»? E-postene i denne mappen flyttes til papirkurven.", - "create": "Opprett", - "cancel": "Avbryt", - "no_folders": "Ingen egendefinerte mapper", - "cannot_delete_role": "Kan ikke slette en mappe med en standardrolle. Fjern rollen først.", - "folder_created": "Mappen er opprettet", - "folder_renamed": "Mappen har fått nytt navn", - "folder_deleted": "Mappen er slettet", - "role_updated": "Mapperollen er oppdatert", - "error_create": "Kunne ikke opprette mappe", - "error_rename": "Kunne ikke gi nytt navn til mappen", - "error_delete": "Kunne ikke slette mappen", - "error_delete_has_children": "Kan ikke slette mappen: den inneholder fortsatt undermapper. Slett eller flytt dem først.", - "error_delete_has_email": "Kan ikke slette mappen: den inneholder fortsatt e-poster. Flytt eller slett dem først.", - "error_role": "Kunne ikke oppdatere mapperollen" - }, - "advanced": { - "title": "Avansert", - "description": "Avanserte alternativer og utviklerinnstillinger", - "debug_mode": { - "label": "Feilsøkingsmodus", - "description": "Aktiver detaljert logging for feilsøking" - }, - "debug_categories": { - "description": "Velg hvilke kategorier som skal logges. Deaktiver kategorier du ikke trenger, for å redusere antallet meldinger i konsollen.", - "jmap": "JMAP-klient", - "jmap_description": "Postkasseoperasjoner, e-posthenting og JMAP-protokollforespørsler", - "calendar": "Kalender", - "calendar_description": "Kalenderhendelser, import og planleggingsmeldinger", - "tasks": "Oppgaver", - "tasks_description": "Oppretting, henting og oppdatering av kalenderoppgaver", - "auth": "Autentisering", - "auth_description": "Pålogging, TOTP, tokenutveksling og øktadministrasjon", - "filters": "Filtre", - "filters_description": "Sieve-filterregler og ferieskript", - "email": "E-postvisning", - "email_description": "Visning av e-post, TNEF-behandling og merking som lest", - "push": "Pushvarsler", - "push_description": "Oppsett og levering av pushvarsler", - "contacts": "Kontakter og adressebøker", - "contacts_description": "Kontaktsynkronisering, adressebokoperasjoner og pålitelige avsendere" - }, - "settings_sync": { - "label": "Synkronisering av innstillinger", - "description": "Synkroniser innstillingene dine på tvers av nettlesere og enheter" - }, - "sender_favicons": { - "label": "Nettstedsikoner for avsendere", - "description": "Vis nettstedsikoner som profilbilder for bedriftsavsendere" - }, - "show_avatars_in_junk": { - "label": "Vis profilbilder i søppelpostmappen", - "description": "Vis profilbilder og nettstedsikoner for avsendere i søppelpostmappen. Dette er deaktivert som standard for å unngå at svindelforsøk virker mer troverdige." - }, - "keyboard_shortcuts": { - "label": "Tastatursnarveier", - "description": "Se tilgjengelige tastatursnarveier", - "button": "Vis snarveier" - }, - "refresh_cache": { - "label": "Oppdater bufrede data", - "description": "Last inn kontakter, kalendere og mapper på nytt fra serveren. Kontoene og øktene beholdes, slik at du kan rette en utdatert eller feil visning uten å logge av.", - "button": "Oppdater" - }, - "reset_settings": { - "label": "Tilbakestill innstillinger", - "description": "Gjenopprett standardinnstillingene", - "button": "Tilbakestill til standard" - }, - "export_settings": { - "label": "Eksporter innstillinger", - "description": "Last ned innstillingene dine som JSON", - "button": "Eksporter" - }, - "import_settings": { - "label": "Importer innstillinger", - "description": "Last opp innstillinger fra en JSON-fil", - "button": "Importer" - }, - "about": { - "title": "Bulwark nettpost" - } - }, - "sidebar_apps": { - "title": "Apper i sidefeltet", - "description": "Administrer egendefinerte apper og lenker i sidefeltet", - "keep_loaded": "Hold apper åpne i bakgrunnen", - "keep_loaded_description": "La apper i sidefeltet fortsette å kjøre i bakgrunnen når du bytter mellom dem, slik at de ikke må lastes inn på nytt", - "managed_title": "Levert av administratoren din", - "managed_description": "Disse appene er satt opp for alle og kan ikke endres.", - "manage_title": "Egendefinerte apper", - "manage_description": "Legg til, rediger eller fjern egendefinerte apper fra sidefeltet" - }, - "contacts": { - "title": "Kontakter", - "description": "Importer og eksporter kontaktene dine", - "group_by_letter_label": "Grupper etter første bokstav", - "sort_by_last_name_label": "Sorter etter etternavn", - "sort_by_last_name_description": "Sorter kontaktlisten etter etternavn slik at familiemedlemmer står samlet", - "group_by_letter_description": "Vis alfabetiske overskrifter i kontaktlisten", - "import_label": "Importer kontakter", - "import_description": "Importer kontakter fra en vCard-fil (.vcf).", - "export_label": "Eksporter kontakter", - "export_description": "Eksporter alle kontakter som en vCard-fil (.vcf).", - "manage_title": "Adressebøker", - "manage_description": "Gi nytt navn til adressebøkene dine", - "no_address_books": "Ingen adressebøker funnet", - "categories_title": "Kategorier", - "categories_description": "Gi nytt navn til kontaktkategorier", - "no_categories": "Ingen kategorier funnet" - }, - "downloads": { - "title": "Nedlastinger", - "description": "Tilpass hvordan nedlastede e-poster og vedlegg navngis.", - "reset": "Gjenopprett standardinnstillinger", - "preview": "Forhåndsvisning:", - "email_template": { - "label": "Filnavn for e-post (.eml)", - "description": "Malen som brukes når du eksporterer en e-post eller drar den ut til filsystemet. Filtypen .eml legges til automatisk." - }, - "attachment_template": { - "label": "Filnavn for vedlegg", - "description": "Malen som brukes når du laster ned eller drar ut et vedlegg. Hvis du utelater «{filename}» og «{ext}», beholdes den opprinnelige filtypen." - }, - "bundle_template": { - "label": "ZIP-filnavn for flere e-poster", - "description": "Malen som brukes når du drar ut eller laster ned flere valgte e-poster som ett .zip-arkiv. Filtypen .zip legges til automatisk." - }, - "spaces": { - "label": "Mellomrom", - "description": "Erstatt mellomrom i det ferdige filnavnet med et annet tegn.", - "keep": "Behold mellomrom", - "underscore": "Erstatt med _", - "dash": "Erstatt med -" - }, - "lowercase": { - "label": "Små bokstaver", - "description": "Gjør hele filnavnet om til små bokstaver." - }, - "strip_diacritics": { - "label": "Fjern diakritiske tegn", - "description": "Konverter bokstaver med diakritiske tegn til tilsvarende ASCII-tegn (ä → a, é → e). Nyttig for verktøy som ikke håndterer Unicode-filnavn riktig." - }, - "collapse_separators": { - "label": "Slå sammen gjentatte skilletegn", - "description": "Erstatt sammenhengende mellomrom, understreker eller bindestreker med ett tegn." - }, - "after_export": { - "label": "Etter eksport", - "description": "Flytt om ønskelig e-posten etter at den er eksportert som .eml.", - "keep": "Behold i postkassen", - "archive": "Flytt til arkiv", - "trash": "Flytt til papirkurven" - } - }, - "filters": { - "title": "E-postfiltre", - "description": "Lag regler for automatisk å sortere, merke og administrere innkommende e-poster", - "add_rule": "Legg til regel", - "no_rules": "Ingen filterregler", - "no_rules_description": "Lag regler for automatisk å organisere innkommende e-poster", - "vacation_active": "Feriesvar er aktivt", - "vacation_active_description": "Autosvar er aktivert for innkommende meldinger", - "vacation_configure": "Sett opp", - "edit_rule": "Rediger regel", - "new_rule": "Ny regel", - "delete_rule": "Slett regel", - "delete_confirm": "Er du sikker på at du vil slette denne regelen?", - "enable": "Aktiver", - "disable": "Deaktiver", - "raw_editor": "Rediger rå Sieve-kode", - "raw_editor_warning": "Redigering av rå Sieve-kode kan gjøre at reglene ikke lenger kan redigeres visuelt. Endringer som gjøres her, overstyrer den visuelle regelbyggeren.", - "validate": "Valider", - "validation_success": "Skriptet er gyldig", - "validation_error": "Skriptet har feil", - "save": "Lagre regler", - "saving": "Lagrer...", - "saved": "Filtrene er lagret", - "save_failed": "Kunne ikke lagre filtrene", - "loading": "Laster inn filtre...", - "not_supported": "E-postserveren din støtter ikke e-postfiltre.", - "rule_name": "Regelnavn", - "rule_name_placeholder": "f.eks. sorter nyhetsbrev", - "match_all": "ALLE vilkår må samsvare", - "match_any": "Minst ETT vilkår må samsvare", - "conditions": "Vilkår", - "add_condition": "Legg til vilkår", - "actions": "Handlinger", - "add_action": "Legg til handling", - "stop_processing": "Ikke behandle flere regler", - "include_spam": "Flytt også meldinger som er merket som søppelpost", - "attachment_type_placeholder": "f.eks. pdf, doc, jpg", - "value_placeholder_multi": "Verdi (skill flere verdier med komma)", - "condition_fields": { - "attachment": "Vedlegg", - "from": "Fra", - "to": "Til", - "cc": "Kopi", - "subject": "Emne", - "header": "Egendefinert meldingshode", - "size": "Størrelse", - "body": "Meldingstekst" - }, - "comparators": { - "has_any": "finnes", - "has_type": "av typen", - "contains": "inneholder", - "not_contains": "inneholder ikke", - "is": "er lik", - "not_is": "er ikke", - "starts_with": "starter med", - "ends_with": "slutter med", - "matches": "samsvarer med mønsteret", - "greater_than": "er større enn", - "less_than": "er mindre enn" - }, - "action_types": { - "move": "Flytt til mappe", - "copy": "Kopier til mappe", - "forward": "Videresend til", - "mark_read": "Merk som lest", - "star": "Stjernemerk melding", - "add_label": "Legg til etikett", - "discard": "Forkast (slett uten varsel)", - "reject": "Avvis med melding", - "keep": "Behold i innboksen", - "stop": "Stopp behandlingen" - }, - "move_to_folder": "Velg mappe", - "copy_to_folder": "Velg mappe", - "forward_to": "Videresend til e-postadresse", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Behold en kopi", - "forward_limit": "Grense for videresendinger per melding på denne serveren: {count}. Flere videresendinger hoppes over.", - "reject_message": "Avvisningsmelding", - "reject_placeholder": "Meldingen din er avvist", - "label_name": "Etikettnavn", - "label_placeholder": "Velg etikett", - "header_name": "Navn på meldingshode", - "header_placeholder": "f.eks. X-Mailing-List", - "size_bytes": "Størrelse i byte", - "size_placeholder": "f.eks. 1000000", - "system_managed": "Regel som styres av systemet", - "opaque_warning": "Dette skriptet ble redigert utenfor den visuelle regelbyggeren. Bare redigering av rå Sieve-kode er tilgjengelig.", - "open_sieve_editor": "Åpne redigereren for Sieve-kode", - "fetch_error": "Kunne ikke laste inn filtre", - "expanded_view": "Utvidet visning", - "expanded_view_description": "Vis filterregler med detaljerte blokker for vilkår og handlinger", - "if": "Hvis", - "then": "Gjør følgende", - "match_all_conditions": "alle samsvarer", - "match_any_condition": "minst ett samsvarer", - "and": "og", - "or": "eller", - "cancel": "Avbryt", - "confirm_delete": "Slett", - "rule_list": "Filterregler", - "drag_to_reorder": "Dra for å endre rekkefølgen", - "match_type": "Samsvar", - "reset_to_visual": "Gå tilbake til visuell redigering", - "reset_warning": "Dette forkaster det gjeldende skriptet og lar deg begynne på nytt.", - "confirm_reset": "Tilbakestill", - "validation_empty_name": "Regelnavn må fylles ut", - "validation_empty_conditions": "Minst ett vilkår må ha en verdi", - "validation_empty_actions": "Du må legge til minst én handling", - "templates_section": "Start med en mal", - "template_newsletters": "Flytt nyhetsbrev til mappe", - "template_receipts": "Arkiver kvitteringer automatisk", - "template_important": "Stjernemerk viktige e-poster", - "template_notifications": "Filtrer varsler", - "sieve_editor": { - "title": "Rediger Sieve-skript", - "warning": "Redigering av rå Sieve-kode kan gjøre at reglene ikke lenger kan redigeres visuelt. Endringer som gjøres her, overstyrer den visuelle regelbyggeren.", - "script_content": "Sieve-skript", - "valid": "Skriptet er gyldig", - "invalid": "Skriptet har feil", - "save_warning": "Lagring overskriver eventuelle visuelle regler. Dette kan ikke angres. Klikk på «Lagre» igjen for å bekrefte.", - "validating": "Validerer...", - "validate": "Valider", - "cancel": "Avbryt", - "save": "Lagre", - "confirm_save": "Bekreft lagring", - "validation_failed": "Kunne ikke validere skriptet" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# vilkår} other {# vilkår}}", - "actions_count": "{count, plural, one {# handling} other {# handlinger}}" - }, - "origin_external": "Ekstern", - "managed_by_tooltip": "Denne regelen administreres av {source}. Rediger den der, eller bruk redigereren for rå Sieve-kode." - }, - "templates": { - "title": "E-postmaler", - "description": "Lag gjenbrukbare e-postmaler med variabler som plassholdere", - "add": "Ny mal", - "edit": "Rediger mal", - "name": "Malnavn", - "name_placeholder": "f.eks. oppfølgingsmelding", - "category": "Kategori", - "category_placeholder": "f.eks. jobb, personlig", - "subject": "Emne", - "subject_placeholder": "Emne i e-posten", - "body": "Meldingstekst", - "body_placeholder": "Innhold i e-posten...", - "recipients_placeholder": "email@example.com", - "identity": "Send som", - "default_identity": "Standardidentitet", - "favorite": "Favoritt", - "cancel": "Avbryt", - "create": "Opprett", - "update": "Oppdater", - "confirm_delete": "Slett", - "no_templates": "Ingen maler ennå", - "manage": "Administrer maler", - "count": "{count, plural, one {# mal} other {# maler}}", - "export_import": "Eksport og import", - "export_import_description": "Sikkerhetskopier malene dine eller overfør dem til en annen enhet", - "export": "Eksporter", - "import": "Importer", - "validation": { - "empty": "Malnavn må fylles ut", - "too_long": "Malnavnet kan inneholde høyst 200 tegn" - } - }, - "files": { - "display": { - "title": "Visning", - "description": "Velg hvordan filer og mapper skal vises" - }, - "folder_layout": { - "label": "Mappenavigering", - "description": "Velg om mapper skal vises sammen med filene eller i et tre i sidefeltet", - "inline": "Sammen med filer", - "sidebar": "Sidefelt" - }, - "default_view": { - "label": "Standardvisning", - "description": "Velg mellom rutenett og listeoppsett", - "list": "Liste", - "grid": "Rutenett" - }, - "default_sort": { - "label": "Standardsortering", - "description": "Velg standardsortering for filer", - "name": "Navn", - "size": "Størrelse", - "modified": "Endret" - }, - "sort_direction": { - "label": "Sorteringsretning", - "description": "Velg stigende eller synkende rekkefølge", - "ascending": "Stigende", - "descending": "Synkende" - }, - "icons": { - "title": "Ikoner", - "description": "Velg hvordan filikonene skal se ut" - }, - "show_icons": { - "label": "Vis filikoner", - "description": "Vis ikoner ved siden av filer og mapper" - }, - "colored_icons": { - "label": "Fargede ikoner", - "description": "Bruk fargede ikoner i stedet for ensfargede" - }, - "show_thumbnails": { - "label": "Vis miniatyrbilder", - "description": "Vis bildeforhåndsvisninger i stedet for ikoner for bildefiler" - }, - "behavior": { - "title": "Virkemåte", - "description": "Velg hvordan filutforskeren skal fungere" - }, - "show_hidden": { - "label": "Vis skjulte filer", - "description": "Vis filer og mapper som starter med en prikk" - }, - "preview": { - "label": "Forhåndsvisning" - } - } - }, - "errors": { - "page_error_title": "Noe gikk galt", - "page_error_description": "Det oppstod en uventet feil. Prøv igjen eller gå tilbake til innboksen.", - "sidebar_error": "Kan ikke laste inn postkasser", - "email_list_error": "Kan ikke laste inn e-poster", - "viewer_error_title": "Kan ikke vise e-post", - "viewer_error_description": "Det oppstod et problem med å vise denne e-posten. Den kan inneholde innhold som ikke støttes.", - "composer_error": "Kan ikke laste inn meldingsvinduet", - "settings_error_title": "Innstillinger utilgjengelige", - "settings_error_description": "Kan ikke laste inn innstillinger. Det kan hende at innstillingene dine ikke lagres.", - "try_again": "Prøv igjen", - "reload": "Last inn på nytt", - "reload_emails": "Last inn e-poster på nytt", - "reload_settings": "Last inn innstillinger på nytt", - "retry": "Prøv på nytt", - "go_home": "Gå til innboksen" - }, - "context_menu": { - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "mark_read": "Merk som lest", - "mark_unread": "Merk som ulest", - "star": "Stjernemerk", - "unstar": "Fjern stjerne", - "pin": "Fest", - "unpin": "Løsne", - "move_to": "Flytt til...", - "archive": "Arkiver", - "delete": "Slett", - "mark_as_spam": "Rapporter som søppelpost", - "not_spam": "Ikke søppelpost", - "tag": "Etikett", - "items_selected": "Valgt: {count}", - "edit_draft": "Rediger utkast", - "cancel_scheduled_send": "Avbryt sending", - "reschedule_send": "Planlegg på nytt", - "cancel_and_edit": "Avbryt og rediger", - "cancel_and_compose_again": "Avbryt og skriv meldingen på nytt" - }, - "mailbox_context_menu": { - "mark_folder_read": "Merk mappen som lest", - "mark_folder_tree_read": "Merk mappe og undermapper som lest", - "mark_all_folders_read": "Merk alle mapper som lest", - "new_subfolder": "Ny undermappe...", - "new_folder": "Ny mappe...", - "rename": "Gi nytt navn...", - "share": "Del...", - "import_email": "Importer .eml eller .zip...", - "empty_folder": "Tøm mappe", - "empty_folder_generic": "Tøm mappe", - "delete_folder": "Slett mappe", - "refresh": "Oppdater", - "mark_all_confirm_title": "Merk alle mapper som lest", - "mark_all_confirm_message": "Vil du merke alle uleste meldinger på den personlige kontoen din som lest?", - "delete_confirm_title": "Slett mappe", - "delete_confirm_message": "Vil du slette mappen «{name}» permanent? Denne handlingen kan ikke angres.", - "prompt_new_subfolder": "Skriv inn et navn for den nye undermappen.", - "prompt_new_folder": "Skriv inn et navn for den nye mappen.", - "prompt_rename": "Skriv inn et nytt navn for denne mappen.", - "placeholder_folder_name": "Mappenavn", - "create": "Opprett", - "rename_confirm": "Gi nytt navn", - "toast_marked_read": "Mappen er merket som lest", - "toast_marked_read_count": "Merket {count, plural, one {1 melding} other {# meldinger}} som lest", - "toast_already_read": "Ingen uleste meldinger", - "toast_marked_all_read": "Alle mapper er merket som lest", - "toast_emptied": "Mappen er tømt", - "toast_folder_created": "Mappen er opprettet", - "toast_folder_renamed": "Mappen har fått nytt navn", - "toast_folder_deleted": "Mappen er slettet", - "toast_error_mark_read": "Kunne ikke merke som lest", - "toast_error_empty": "Kunne ikke tømme mappen", - "toast_error_create": "Kunne ikke opprette mappe", - "toast_error_rename": "Kunne ikke gi nytt navn til mappen", - "toast_error_delete": "Kunne ikke slette mappen", - "toast_error_delete_has_children": "Mappen har undermapper. Fjern dem først.", - "toast_error_delete_has_email": "Mappen er ikke tom. Tøm den først.", - "delete_with_contents_title": "Mappen er ikke tom", - "delete_with_contents_message": "Mappen «{name}» inneholder fortsatt {count, plural, one {1 melding} other {# meldinger}}. Slette mappen og alle meldingene i den? Dette kan ikke angres.", - "delete_with_contents_confirm": "Slett mappe og meldinger" - }, - "shortcuts": { - "title": "Tastatursnarveier", - "tip": "Trykk på ? når som helst for å vise denne hjelpen", - "sections": { - "navigation": "Navigasjon", - "actions": "E-posthandlinger", - "global": "Generelt", - "threads": "Tråder", - "composer": "Skriving" - }, - "navigation": { - "next_email": "Neste e-post", - "previous_email": "Forrige e-post", - "open_email": "Åpne e-post", - "close_email": "Lukk / fjern valget" - }, - "actions": { - "reply": "Svar", - "reply_all": "Svar alle", - "forward": "Videresend", - "star": "Slå stjernemerking av eller på", - "archive": "Arkiver", - "delete": "Slett", - "mark_unread": "Merk som ulest", - "mark_read": "Merk som lest", - "toggle_spam": "Rapporter som søppelpost / Ikke søppelpost" - }, - "global": { - "compose": "Skriv ny e-post", - "search": "Aktiver søkefeltet", - "help": "Vis tastatursnarveier", - "refresh": "Oppdater e-poster", - "select_all": "Velg alle", - "global_search": "Søk i alt" - }, - "threads": { - "expand_collapse": "Utvid eller fold sammen tråden" - }, - "composer": { - "send": "Send e-post", - "schedule_send": "Planlegg sending", - "template_picker": "Åpne malvelgeren" - } - }, - "threads": { - "messages_one": "{count} melding", - "messages_other": "{count} meldinger", - "messages_tooltip": "{count, plural, one {# melding i denne samtalen} other {# meldinger i denne samtalen}}", - "expand": "Utvid samtalen", - "collapse": "Fold sammen samtalen", - "loading": "Laster inn samtale...", - "mark_read": "Merk samtalen som lest", - "mark_unread": "Merk samtalen som ulest", - "archive": "Arkiver samtale", - "delete": "Slett samtalen", - "star": "Stjernemerk samtalen", - "unstar": "Fjern stjernemerking fra samtalen", - "toggle_thread": "Utvid eller fold sammen tråden" - }, - "identities": { - "modal_title": "Administrer avsenderidentiteter", - "create_new": "Opprett ny identitet", - "edit_identity": "Rediger identitet", - "delete_confirm": "Vil du slette denne identiteten? Dette kan ikke angres.", - "cannot_delete": "Denne identiteten kan ikke slettes", - "primary_identity": "Hovedidentitet", - "set_as_primary": "Angi som hovedidentitet", - "no_identities": "Ingen identiteter", - "display": { - "reply_to": "Svar til:", - "bcc": "BCC:", - "signature": "Signatur:", - "preview": "Forhåndsvisning:" - }, - "validation_errors": { - "invalid_emails": "Ugyldige e-postadresser: {emails}", - "unknown_error": "Ukjent feil" - }, - "form": { - "name_label": "Visningsnavn", - "name_placeholder": "f.eks. jobb, privat", - "name_required": "Navn må fylles ut", - "email_label": "E-postadresse", - "email_placeholder": "your.email@example.com", - "email_required": "E-postadresse må fylles ut", - "email_invalid": "Skriv inn en gyldig e-postadresse", - "email_immutable": "E-postadressen kan ikke endres etter at identiteten er opprettet", - "reply_to_label": "Svar til (valgfritt)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "Automatisk blindkopi (valgfritt)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "Signatur i ren tekst", - "html_signature_label": "HTML-signatur", - "signature_byte_counter": "{bytes} / {max} byte", - "signature_byte_limit_reached": "Serverens grense er nådd", - "save": "Lagre identitet", - "cancel": "Avbryt", - "creating": "Oppretter...", - "updating": "Oppdaterer..." - }, - "sub_address": { - "button_tooltip": "Bruk underadresse", - "popover_title": "Legg til underadressekode", - "tag_input_placeholder": "Skriv inn kode (for eksempel shopping)", - "preview_label": "Forhåndsvisning:", - "recent_tags": "Nylige koder", - "suggested_tags": "Foreslåtte koder", - "use_address": "Bruk denne adressen", - "invalid_tag": "Koden kan bare inneholde bokstaver, tall og bindestreker", - "tag_too_long": "Koden kan inneholde opptil 30 tegn", - "help_text": "E-poster som sendes til user{delimiter}tag@domain.com, havner i innboksen din", - "validation": { - "empty": "Koden kan ikke være tom", - "too_long": "Koden kan inneholde opptil {max} tegn", - "invalid_chars": "Koden kan bare inneholde bokstaver, tall og bindestreker" - } - }, - "badge": { - "sent_via": "sendt via", - "sub_address_tag": "Sendt med underadresse: {tag}", - "identity_name": "Sendt med identitet: {name}", - "identity_short": "via {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Slett", - "delete_confirm_title": "Slett identitet" - }, - "templates": { - "picker_title": "Velg en mal", - "search_placeholder": "Søk etter maler...", - "section_favorites": "Favoritter", - "section_recent": "Nylige", - "section_uncategorized": "Annet", - "no_templates": "Ingen maler ennå", - "no_results": "Ingen maler funnet", - "fill_placeholders": "Fyll inn verdier for plassholdere", - "enter_value": "Skriv inn en verdi...", - "preview": "Forhåndsvisning", - "insert_with_values": "Sett inn ferdig utfylt", - "insert_raw": "Sett inn uten å fylle ut", - "copy_suffix": "(kopi)", - "placeholder": "Variabel", - "placeholders": { - "recipient_name": "Mottakerens navn", - "company": "Selskapsnavn", - "date": "Gjeldende dato", - "day_of_week": "Ukedag", - "sender_name": "Navnet ditt" - } - }, - "contacts": { - "title": "Kontakter", - "search_placeholder": "Søk etter kontakter...", - "create_new": "Ny kontakt", - "no_category": "Ingen kategori", - "rename_category": "Gi nytt navn til kategori", - "category_name_label": "Kategorinavn", - "category_renamed": "Kategorien har fått nytt navn", - "category_rename_failed": "Kunne ikke gi kategorien nytt navn", - "category_added": "Kontakt lagt til i {name}", - "category_added_plural": "{count} kontakter lagt til i {name}", - "empty_state": "Ingen kontakter ennå", - "empty_state_title": "Ingen kontakter ennå", - "empty_state_subtitle": "Opprett din første kontakt eller importer fra en vCard-fil", - "empty_search": "Ingen kontakter passer med søket ditt", - "empty_search_hint": "Prøv et annet søkeord", - "empty_filtered": "Ingen kontakter passer med filtrene dine", - "empty_filtered_hint": "Prøv å endre eller fjerne filtrene", - "clear_search": "Tøm søket", - "import_vcard": "Importer vCard", - "delete_confirm_title": "Slett kontakt", - "delete_confirm": "Er du sikker på at du vil slette denne kontakten?", - "local_mode": "Kontakter lagres lokalt (serveren støtter ikke JMAP-kontakter)", - "back_to_contacts": "Tilbake til kontaktene", - "back_to_email": "Tilbake til e-post", - "open_categories": "Åpne kategorier", - "tabs": { - "all": "Alle", - "groups": "Grupper" - }, - "shared": { - "title": "Delt" - }, - "address_books": { - "create": "Ny adressebok", - "created": "Adresseboken er opprettet", - "create_failed": "Kunne ikke opprette adresseboken", - "title": "Mine adressebøker", - "shared_prefix": "Delt: {name}", - "moved": "Kontakten er flyttet til {name}", - "moved_plural": "{count} kontakter er flyttet til {name}", - "move_failed": "Kunne ikke flytte kontakten", - "address_book": "Adressebok", - "rename": "Gi nytt navn til adresseboken", - "name_label": "Adresseboknavn", - "renamed": "Adresseboken har fått nytt navn", - "rename_failed": "Kunne ikke gi adresseboken nytt navn", - "default": "Standard", - "set_default": "Sett som standard", - "default_updated": "Standardadresseboken er oppdatert", - "set_default_failed": "Kunne ikke angi standardadressebok", - "manage": "Administrer adressebøker", - "share": "Del adressebok", - "new_contact_in_book": "Ny kontakt i denne adresseboken", - "delete": "Slett adressebok", - "confirm_delete": "Vil du slette «{name}»? Alle kontaktene i denne adresseboken fjernes.", - "deleted": "Adresseboken er slettet", - "delete_failed": "Kunne ikke slette adresseboken" - }, - "detail": { - "emails": "E-postadresser", - "phones": "Telefonnumre", - "organizations": "Organisasjoner", - "addresses": "Adresser", - "notes": "Notater", - "titles": "Titler og roller", - "online_services": "Nettjenester", - "anniversaries": "Merkedager", - "personal_info": "Personlig informasjon", - "languages": "Språk", - "categories": "Kategorier", - "related_contacts": "Tilknyttede kontakter", - "crypto_keys": "Krypteringsnøkler", - "cert_issuer": "Utsteder", - "cert_expires": "Utløper", - "cert_expired": "Utløpt", - "cert_fingerprint": "Fingeravtrykk", - "cert_algorithm": "Algoritme", - "import_to_smime": "Importer til S/MIME", - "cert_already_imported": "Allerede importert til S/MIME", - "cert_imported": "Sertifikatet er importert til S/MIME-lageret", - "cert_import_failed": "Kunne ikke importere sertifikatet", - "no_contact_selected": "Velg en kontakt for å se detaljer", - "compose_email": "Skriv en e-post", - "copy_email": "Kopier e-postadresse", - "copy_phone": "Kopier telefonnummer", - "copy_url": "Kopier nettadresse", - "copied": "Kopiert til utklippstavlen", - "copy_failed": "Kunne ikke kopiere til utklippstavlen", - "created": "Opprettet", - "updated": "Sist oppdatert", - "timezone": "Tidssone", - "anniversary_birth": "Bursdag", - "anniversary_death": "Dødsdag", - "anniversary_wedding": "Bryllupsdag", - "anniversary_other": "Annet", - "personal_expertise": "Ekspertise", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Annet", - "gender": "Kjønn", - "gender_masculine": "Mann", - "gender_feminine": "Kvinne", - "gender_other": "Annet", - "gender_none": "Ikke aktuelt", - "gender_unknown": "Ukjent", - "calendar": "Kalender", - "calendar_uri": "Kalender-URL", - "scheduling_uri": "Planleggings-URL", - "freebusy_uri": "Ledig/opptatt-URL", - "section_contact": "Kontaktinformasjon", - "section_work": "Arbeid", - "section_personal": "Personlig", - "email_default_label": "E-post", - "phone_default_label": "Telefon", - "address_default_label": "Adresse", - "online_service_default_label": "Nettjeneste", - "organization_label": "Organisasjon", - "title_label": "Tittel", - "role_label": "Rolle", - "language_label": "Språk", - "related_default_label": "Tilknyttet", - "more_actions": "Flere handlinger", - "age_years": "{count, plural, one {1 år gammel} other {# år gammel}}", - "years_since": "{count, plural, one {1 år} other {# år}}" - }, - "activity": { - "recent_emails": "Nylige e-poster", - "upcoming_events": "Kommende hendelser", - "no_emails": "Ingen nylige e-poster", - "no_events": "Ingen kommende hendelser", - "no_subject": "(Uten emne)", - "no_title": "(Ingen tittel)", - "load_failed": "Kunne ikke laste inn", - "unknown_sender": "Ukjent avsender", - "all_day": "Hele dagen" - }, - "form": { - "create_title": "Ny kontakt", - "edit_title": "Rediger kontakt", - "section_address_book": "Adressebok", - "select_address_book": "Velg en adressebok...", - "section_identity": "Navn og identitet", - "contact_type": "Kontakttype", - "type_person": "Person", - "type_organization": "Organisasjon", - "section_work": "Arbeid og organisasjon", - "prefix": "Tittel", - "prefix_placeholder": "dr., herr, fru", - "given_name": "Fornavn", - "middle_name": "Mellomnavn", - "surname": "Etternavn", - "suffix": "Navnetillegg", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Kallenavn", - "nickname_placeholder": "Kallenavn", - "email": "E-post", - "email_placeholder": "email@example.com", - "phone": "Telefon", - "phone_placeholder": "+1 234 567 890", - "phone_type": "Type", - "phone_voice": "Taleanrop", - "phone_cell": "Mobil", - "phone_fax": "Faks", - "phone_pager": "Personsøker", - "phone_video": "Video", - "phone_text": "SMS", - "organization": "Organisasjon", - "organization_placeholder": "Selskapsnavn", - "department": "Avdeling", - "department_placeholder": "Avdeling", - "job_title": "Stillingstittel", - "job_title_placeholder": "f.eks. programvareutvikler", - "role": "Rolle", - "role_placeholder": "f.eks. teamleder", - "addresses": "Adresser", - "add_address": "Legg til adresse", - "street": "Gateadresse", - "city": "By", - "region": "Fylke/region", - "postcode": "Postnummer", - "country": "Land", - "online_services": "Nettjenester", - "add_online_service": "Legg til nettjeneste", - "url_placeholder": "https://...", - "service_placeholder": "Tjeneste", - "anniversaries": "Merkedager", - "add_anniversary": "Legg til merkedag", - "anniversary_birth": "Bursdag", - "anniversary_wedding": "Bryllupsdag", - "anniversary_death": "Dødsdag", - "anniversary_other": "Annet", - "personal_info": "Personlig informasjon", - "add_personal_info": "Legg til oppføring", - "personal_info_placeholder": "f.eks. fotografi", - "personal_expertise": "Ekspertise", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Annet", - "level": "Nivå", - "level_high": "Høyt", - "level_medium": "Middels", - "level_low": "Lavt", - "categories": "Kategorier", - "categories_placeholder": "f.eks. familie, venner, kolleger", - "categories_hint": "Skriv for å søke eller legge til kategorier", - "category_add": "Legg til", - "note": "Notater", - "note_placeholder": "Legg til et notat...", - "gender": "Kjønn", - "gender_sex": "Biologisk kjønn", - "gender_male": "Mann", - "gender_female": "Kvinne", - "gender_other": "Annet", - "gender_none": "Ikke aktuelt", - "gender_unknown": "Ukjent", - "gender_identity": "Kjønnsidentitet", - "gender_identity_placeholder": "Kjønnsidentitet...", - "calendar": "Kalender", - "calendar_uri": "Kalender-URL", - "scheduling_uri": "Planleggings-URL", - "freebusy_uri": "Ledig/opptatt-URL", - "context_work": "Arbeid", - "context_private": "Privat", - "add_email": "Legg til e-postadresse", - "add_phone": "Legg til telefonnummer", - "save": "Lagre", - "cancel": "Avbryt", - "creating": "Oppretter...", - "updating": "Oppdaterer...", - "name_required": "Skriv inn et fornavn, etternavn eller organisasjon", - "email_invalid": "Skriv inn en gyldig e-postadresse", - "email_error_inline": "Ugyldig e-postformat", - "save_failed": "Kunne ikke lagre kontakten", - "delete": "Slett", - "upload_photo": "Last opp bilde", - "remove_photo": "Fjern bildet", - "photo_hint": "JPG eller PNG, opptil 10 MB. Bildet skaleres automatisk.", - "photo_too_large": "Bildet er for stort (maks. 10 MB)", - "photo_invalid": "Ugyldig bildefil", - "change_photo": "Endre" - }, - "groups": { - "create": "Ny gruppe", - "edit": "Rediger gruppe", - "empty": "Ingen grupper ennå", - "delete_confirm_title": "Slett gruppe", - "delete_confirm": "Er du sikker på at du vil slette denne gruppen?", - "name_label": "Gruppenavn", - "name_placeholder": "f.eks. team, familie", - "name_required": "Gruppenavn må fylles ut", - "save_failed": "Kunne ikke lagre gruppen", - "members_label": "Medlemmer", - "search_members": "Søk etter kontakter som skal legges til...", - "no_members": "Ingen medlemmer i denne gruppen", - "member_count": "{count, plural, =0 {Ingen medlemmer} one {1 medlem} other {# medlemmer}}", - "send_email": "Send e-post til gruppen", - "send_email_to": "Til", - "send_email_cc": "Kopi", - "send_email_bcc": "Blindkopi", - "no_member_emails": "Denne gruppen har ingen medlemmer med en e-postadresse." - }, - "import": { - "title": "Importer kontakter", - "drop_hint": "Klikk for å velge en vCard-fil", - "file_types": ".vcf- eller .vcard-filer", - "no_contacts": "Ingen kontakter funnet i filen", - "parse_error": "Kunne ikke analysere vCard-filen", - "found": "{count, plural, one {1 kontakt funnet} other {# kontakter funnet}}", - "duplicate": "Dobbeltoppføring", - "select_all": "Velg alle", - "deselect_all": "Fjern alle markeringer", - "selected": "{count, plural, one {1 valgt} other {# valgte}}", - "import_button": "Importer", - "importing": "Importerer...", - "success": "{count, plural, one {1 kontakt importert} other {# kontakter importert}}", - "failed": "Importen mislyktes", - "close": "Lukk", - "file_too_large": "Filen er for stor (maks. 5 MB)" - }, - "export": { - "title": "Eksporter kontakter", - "success": "{count, plural, one {1 kontakt eksportert} other {# kontakter eksportert}}" - }, - "bulk": { - "selected": "{count, plural, one {1 valgt} other {# valgte}}", - "select_all": "Velg alle", - "delete": "Slett", - "delete_confirm_title": "Slett kontakter", - "delete_confirm": "Vil du slette {count, plural, one {1 kontakt} other {# kontakter}}?", - "deleted": "{count, plural, one {1 kontakt slettet} other {# kontakter slettet}}", - "add_to_group": "Legg til i gruppe", - "choose_group": "Velg en gruppe", - "adding_contacts": "Legger til {count, plural, one {1 kontakt} other {# kontakter}}", - "added_to_group": "Kontaktene er lagt til i gruppen", - "export": "Eksporter", - "clear": "Fjern valg" - }, - "toast": { - "created": "Kontakten er opprettet", - "updated": "Kontakten er oppdatert", - "deleted": "Kontakten er slettet", - "error_create": "Kunne ikke opprette kontakt", - "error_update": "Kunne ikke oppdatere kontakten", - "error_delete": "Kunne ikke slette kontakten" - }, - "context_menu": { - "open": "Åpne", - "edit": "Rediger", - "send_email": "Send e-post", - "add_to_group": "Legg til i gruppe", - "export_vcard": "Eksporter som vCard", - "delete": "Slett", - "call": "Ring", - "duplicate": "Dupliser", - "print": "Skriv ut" - }, - "filters": { - "toggle": "Filtre", - "select": "Velg", - "clear": "Tøm", - "close": "Lukk", - "title": "Avanserte filtre", - "organization": "Selskap", - "organization_placeholder": "f.eks. Acme Corp", - "job_title": "Stillingstittel", - "job_title_placeholder": "f.eks. designer", - "location": "Sted", - "location_placeholder": "By eller land", - "email_domain": "E-postdomene", - "email_domain_placeholder": "eksempel.com", - "birthday_month": "Bursdag i", - "any_month": "Alle måneder", - "has_email": "Har e-postadresse", - "has_phone": "Har telefonnummer", - "has_photo": "Har bilde", - "sort_by": "Sorter etter", - "sort_first_name": "Fornavn", - "sort_last_name": "Etternavn" - } - }, - "calendar": { - "title": "Kalender", - "back_to_email": "Tilbake til e-post", - "back_to_month": "Tilbake til måneden", - "my_calendars": "Kalendere", - "birthday_calendar": "Bursdager", - "mini_calendar_change": "Klikk for å endre måned", - "views": { - "month": "Måned", - "week": "Uke", - "day": "Dag", - "agenda": "Agenda", - "today": "I dag", - "month_hint": "Måned (m)", - "week_hint": "Uke (w)", - "day_hint": "Dag (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Oppgaver", - "tasks_hint": "Oppgaver (k)" - }, - "events": { - "create": "Opprett hendelse", - "edit": "Rediger hendelse", - "delete": "Slett hendelse", - "details": "Hendelsesdetaljer", - "no_events": "Ingen hendelser", - "all_day": "Hele dagen", - "more": "+{count} til", - "no_title": "(Ingen tittel)", - "resize": "Endre størrelsen på hendelsen", - "duplicate": "Dupliser", - "today_header": "I dag", - "tomorrow_header": "I morgen", - "agenda_show_earlier": "Vis tidligere hendelser", - "agenda_loading": "Laster…", - "agenda_range_start": "Hendelser fra {date}", - "agenda_range_end": "Hendelser til {date}", - "export_ics": "Eksporter som .ics", - "copy_title": "Kopier tittel", - "copy_link": "Kopier møtelenken", - "new_event": "Ny hendelse", - "new_all_day_event": "Ny heldagshendelse", - "new_task": "Ny oppgave", - "go_to_today": "Gå til i dag" - }, - "detail": { - "add_note": "Legg til et notat...", - "save_note": "Lagre", - "note_saved": "Notatet er lagret", - "open_link": "Åpne lenken", - "meeting_link": "Møtelenke", - "tentative": "Foreløpig", - "cancelled": "Avlyst", - "delete_confirm": "Vil du slette denne hendelsen?" - }, - "form": { - "title": "Tittel", - "description": "Beskrivelse", - "location": "Sted", - "meeting_link": "Møtelenke", - "start_date": "Startdato", - "end_date": "Sluttdato", - "start_time": "Starttid", - "end_time": "Sluttid", - "all_day_event": "Heldagshendelse", - "calendar_select": "Kalender", - "save": "Lagre", - "cancel": "Avbryt", - "delete_confirm": "Er du sikker på at du vil slette denne hendelsen?", - "color": "Farge" - }, - "participants": { - "availability": { - "title": "Tilgjengelighet", - "free": "Ledig", - "busy": "Opptatt", - "tentative": "Foreløpig", - "unknown": "Ikke en bruker på denne serveren", - "checking": "Sjekker…" - }, - "title": "Deltakere", - "add": "Legg til deltaker", - "organizer": "Arrangør", - "attendee": "Deltaker", - "accepted": "Godtatt", - "declined": "Avslått", - "tentative": "Kanskje", - "needs_action": "Har ikke svart", - "remove": "Fjern", - "edit": "Rediger", - "email_placeholder": "Legg til e-postadresse eller søk etter kontakter", - "send_invitations": "Send invitasjoner til deltakerne", - "status_summary": "{accepted} har godtatt, {pending} har ikke svart", - "invited_by": "Invitasjon fra {name}", - "respond_below": "Bruk knappene nedenfor til å svare", - "rsvp_label": "Ditt svar", - "cancel_notification": "Deltakerne blir varslet om avlysningen", - "you_organizer": "Du er arrangøren", - "you_attendee": "Du er deltaker", - "no_participants": "Ingen deltakere", - "count": "{count, plural, one {# deltaker} other {# deltakere}}" - }, - "recurrence": { - "title": "Gjentakelse", - "none": "Gjentas ikke", - "daily": "Daglig", - "weekly": "Ukentlig", - "monthly": "Månedlig", - "yearly": "Årlig", - "every_n_days": "Hver {count}. dag", - "every_n_weeks": "Hver {count}. uke", - "every_n_months": "Hver {count}. måned", - "until": "senest", - "occurrences": "{count} forekomster", - "custom": "Egendefinert...", - "edit_custom": "Rediger egendefinert gjentakelse", - "every_n_years": "Hvert {count}. år", - "on_days": "på {days}", - "on_day_n": "på dag {day}", - "on_the_nth": "på {nth} {day}", - "in_month": "i {month}", - "nth_1": "første", - "nth_2": "andre", - "nth_3": "tredje", - "nth_4": "fjerde", - "nth_last": "siste", - "editor_freq_day": "Dag", - "editor_freq_week": "Uke", - "editor_freq_month": "Måned", - "editor_freq_year": "År", - "editor_repeats_on": "Gjentas på", - "editor_every": "Hver", - "editor_unit_days": "dag(er)", - "editor_unit_weeks": "uke(r)", - "editor_unit_months": "måned(er)", - "editor_unit_years": "år", - "editor_on_day": "på dag", - "editor_on_the": "på", - "editor_in": "i", - "editor_ends": "Slutter", - "editor_never": "Aldri", - "editor_ends_on": "På", - "editor_ends_after": "Etter", - "editor_occurrences": "forekomster" - }, - "recurrence_scope": { - "edit_title": "Rediger gjentakende hendelse", - "delete_title": "Slett gjentakende hendelse", - "description": "Dette er en gjentakende hendelse. Hvilke hendelser vil du endre?", - "this_event": "Bare denne hendelsen", - "this_and_future": "Dette og følgende hendelser", - "all_events": "Alle hendelser", - "cancel": "Avbryt", - "save": "Lagre", - "delete": "Slett", - "rsvp_title": "Svar på gjentakende hendelse", - "rsvp_description": "Dette er en gjentakende hendelse. Hvilke hendelser gjelder svaret ditt for?", - "respond": "Svar" - }, - "alerts": { - "title": "Påminnelser", - "none": "Ingen påminnelser", - "at_time": "Når hendelsen starter", - "minutes_before": "{count, plural, one {# minutt før} other {# minutter før}}", - "hours_before": "{count, plural, one {# time før} other {# timer før}}", - "days_before": "{count, plural, one {# dag før} other {# dager før}}", - "weeks_before": "{count, plural, one {# uke før} other {# uker før}}", - "unit_minutes_before": "minutter før", - "unit_hours_before": "timer før", - "unit_days_before": "dager før", - "unit_weeks_before": "uker før", - "add": "Legg til påminnelse", - "remove": "Fjern påminnelsen", - "amount": "Antall", - "unit": "Tidsenhet" - }, - "settings": { - "title": "Kalenderinnstillinger", - "default_view": "Standardvisning", - "week_starts_on": "Uken starter på", - "time_format": "Tidsformat", - "default_calendar": "Standardkalender", - "default_reminder": "Standardpåminnelse", - "time_format_12h": "12-timersformat", - "time_format_24h": "24-timersformat", - "notifications_enabled": "Hendelsesvarsler", - "notifications_enabled_desc": "Vis varsler for kommende kalenderhendelser", - "notification_sound": "Varslingslyd", - "notification_sound_desc": "Spill av en lyd for kalendervarsler", - "invitation_parsing": "Les kalenderinvitasjoner i e-post", - "invitation_parsing_desc": "Finn kalenderinvitasjoner i e-postvedlegg og vis tilgjengelige kalenderhandlinger", - "show_time_in_month_view": "Vis klokkeslett i månedsvisning", - "show_time_in_month_view_desc": "Vis klokkeslettet for hendelser i månedsvisningen. På små skjermer vises hele hendelsesoppføringen i stedet for en prikk.", - "show_week_numbers": "Vis ukenummer", - "show_week_numbers_desc": "Vis ukenummer i minikalenderen", - "calendar_free_scroll": "Fri rulling", - "calendar_free_scroll_desc": "Rull fortløpende gjennom måneder, uker og dager i stedet for én periode om gangen", - "enable_tasks": "Aktiver oppgaver", - "enable_tasks_desc": "Vis en oppgavevisning i kalenderen for å administrere gjøremål", - "show_tasks_on_calendar": "Vis oppgaver i kalenderen", - "show_tasks_on_calendar_desc": "Vis oppgavemarkører i dags- og ukesvisningen", - "hover_preview": "Forhåndsvis hendelser når du peker", - "hover_preview_desc": "Vis et detaljvindu når du holder markøren over kalenderhendelser", - "hover_preview_instant": "Øyeblikkelig", - "hover_preview_delay_500ms": "Forsinkelse på 0,5 sekunder", - "hover_preview_delay_1s": "Forsinkelse på 1 sekund", - "hover_preview_delay_2s": "Forsinkelse på 2 sekunder", - "hover_preview_off": "Deaktivert", - "organizer_identity": "Organiser invitasjoner som", - "organizer_identity_desc": "Kalenderadressen som brukes som arrangør når du inviterer folk til hendelser.", - "organizer_identity_failed": "Kunne ikke endre arrangøradressen", - "show_birthday_calendar": "Fødselsdager fra kontakter", - "show_birthday_calendar_desc": "Vis en virtuell kalender med bursdager fra kontaktene dine" - }, - "days": { - "monday": "mandag", - "tuesday": "tirsdag", - "wednesday": "onsdag", - "thursday": "torsdag", - "friday": "fredag", - "saturday": "lørdag", - "sunday": "søndag", - "mon": "man", - "tue": "tir", - "wed": "ons", - "thu": "tor", - "fri": "fre", - "sat": "lør", - "sun": "søn" - }, - "months": { - "jan": "jan.", - "feb": "feb.", - "mar": "mars", - "apr": "apr.", - "may": "mai", - "jun": "juni", - "jul": "juli", - "aug": "aug.", - "sep": "sep.", - "oct": "okt.", - "nov": "nov.", - "dec": "des.", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Hendelsen er opprettet", - "event_updated": "Hendelsen er oppdatert", - "event_deleted": "Hendelsen er slettet", - "calendar_created": "Kalenderen er opprettet", - "calendar_deleted": "Kalenderen er slettet", - "event_move_error": "Kunne ikke flytte hendelsen", - "event_resize_error": "Kunne ikke endre størrelsen på hendelsen", - "alert_title": "Kommende hendelse", - "alert_now": "Starter nå", - "alert_in_minutes": "Om {count} min", - "invitation_sent": "Invitasjonene er sendt", - "invitations_denied_title": "Invitasjonene kan ikke sendes", - "invitations_denied": "Serveren nektet å sende invitasjonene: {reason}. Lagre hendelsen uten å sende dem?", - "save_without_invitations": "Lagre uten invitasjoner", - "rsvp_updated": "Svaret er oppdatert", - "rsvp_error": "Kunne ikke oppdatere svaret", - "event_duplicated": "Hendelsen er duplisert", - "event_error": "Kunne ikke lagre hendelsen", - "task_due": "Oppgaven forfaller", - "event_exported": "Hendelsen er eksportert", - "title_copied": "Tittelen er kopiert", - "link_copied": "Lenken er kopiert" - }, - "status": { - "loading_calendars": "Laster inn kalendere...", - "loading_events": "Laster inn hendelser..." - }, - "quick_create": { - "placeholder": "Tittel på ny hendelse", - "aria_label": "Opprett en hendelse raskt" - }, - "nav_prev": "Forrige", - "nav_next": "Neste", - "nav_open_menu": "Åpne menyen", - "import": { - "title": "Importer kalender", - "tab_file": "Fil", - "tab_url": "URL", - "select_file": "Velg .ics-fil", - "drop_file": "eller slipp filen her", - "supported_formats": "Støtter iCalendar (.ics)-filer", - "url_description": "Skriv inn URL-en til en ekstern iCalendar-strøm (.ics) for å importere hendelser.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Støtter iCalendar-URL-er (.ics)", - "fetch": "Hent", - "invalid_url": "Skriv inn en gyldig URL", - "url_fetch_failed": "Kunne ikke hente kalenderen fra nettadressen", - "parsing": "Leser kalenderfilen...", - "parsed_events": "Hendelser funnet: {count}", - "no_events": "Ingen hendelser funnet i filen", - "select_all": "Velg alle", - "deselect_all": "Fjern alle markeringer", - "target_calendar": "Importer til kalender", - "import_button": "Importer valgte hendelser", - "importing": "Importerer hendelser...", - "success": "Importerte hendelser: {count}", - "error": "Kunne ikke importere kalenderen", - "file_too_large": "Filen er større enn grensen på 10 MB", - "invalid_format": "Ugyldig kalenderfilformat" - }, - "webcal_action": { - "title": "Åpne kalenderlenken", - "description": "Hvordan vil du bruke «{name}»?", - "import_title": "Importer én gang", - "import_description": "Hent hendelsene nå og kopier dem inn i en av kalenderne dine.", - "subscribe_title": "Abonner", - "subscribe_description": "Kalenderen synkroniseres automatisk som en egen kalender.", - "cancel": "Avbryt" - }, - "management": { - "title": "Administrer kalendere", - "description": "Opprett, gi nytt navn og tilpass kalenderne dine. Høyreklikk på en kalender i sidefeltet for raskt å endre fargen.", - "name": "Navn", - "name_placeholder": "Kalendernavn", - "color": "Farge", - "change_color": "Endre farge", - "random_color": "Ny tilfeldig farge", - "add_calendar": "Legg til kalender", - "edit": "Rediger", - "delete": "Slett", - "save": "Lagre", - "create": "Opprett", - "cancel": "Avbryt", - "default": "Standard", - "confirm_delete": "Vil du slette «{name}»? Alle hendelsene i kalenderen fjernes.", - "confirm_clear": "Vil du fjerne alle hendelser fra «{name}»? Dette kan ikke angres.", - "set_default": "Sett som standard", - "default_updated": "Standardkalenderen er oppdatert", - "error_default": "Kunne ikke angi standardkalender", - "clear_events": "Fjern alle hendelser", - "events_cleared": "Hendelser fjernet: {count}", - "error_clear": "Kunne ikke fjerne kalenderhendelsene", - "calendar_created": "Kalenderen er opprettet", - "calendar_updated": "Kalenderen er oppdatert", - "calendar_deleted": "Kalenderen er slettet", - "color_updated": "Kalenderfargen er oppdatert", - "error_create": "Kunne ikke opprette kalender", - "error_update": "Kunne ikke oppdatere kalenderen", - "error_delete": "Kunne ikke slette kalenderen", - "caldav_url": "CalDAV-URL", - "copy_url": "Kopier CalDAV-URL", - "url_copied": "CalDAV-URL kopiert til utklippstavlen", - "share": "Del kalender", - "new_event_in_calendar": "Ny hendelse i denne kalenderen", - "kind": "Innhold", - "kind_events": "Hendelser", - "kind_tasks": "Oppgaver", - "kind_both": "Hendelser og oppgaver", - "kind_hint": "Bestemmer hvilke apper som ser denne kalenderen når den synkroniseres via CalDAV. Dette kan ikke endres senere." - }, - "subscription": { - "title": "iCal-abonnement", - "section_title": "iCal-abonnementer", - "description": "Abonner på en ekstern iCalendar-strøm. Hendelser synkroniseres automatisk til en egen kalender. Du kan bruke URL-er med https:// og webcal://.", - "url_label": "Kalender-URL", - "url_placeholder": "https://example.com/calendar.ics eller webcal://...", - "name_label": "Kalendernavn", - "name_placeholder": "f.eks. Helligdager", - "color_label": "Farge", - "refresh_interval": "Oppdateringsintervall", - "interval_15": "Hvert 15. minutt", - "interval_30": "Hvert 30. minutt", - "interval_60": "Hver time", - "interval_360": "Hver 6. time", - "interval_1440": "Hver dag", - "subscribe": "Abonner", - "subscribing": "Abonnerer...", - "save": "Lagre endringer", - "saving": "Lagrer...", - "edit": "Rediger", - "edit_title": "Rediger abonnement", - "updated": "«{name}» er oppdatert", - "update_error": "Kunne ikke oppdatere abonnementet", - "invalid_url": "Skriv inn en gyldig URL", - "success": "Abonnementet på «{name}» er lagt til", - "error": "Kunne ikke legge til abonnement", - "refresh": "Oppdater nå", - "refresh_success": "Abonnementet er oppdatert", - "refresh_error": "Kunne ikke oppdatere abonnementet", - "unsubscribe": "Fjern abonnement", - "confirm_delete": "Vil du fjerne abonnementet på «{name}»? Kalenderen og alle hendelsene i den fjernes.", - "deleted": "Abonnementet er fjernet", - "delete_error": "Kunne ikke fjerne abonnementet", - "last_refreshed": "Sist oppdatert: {time}" - }, - "tasks": { - "label": "Oppgaver", - "no_tasks": "Ingen oppgaver", - "no_title": "(Ingen tittel)", - "mark_complete": "Merk som fullført", - "mark_incomplete": "Merk som ikke fullført", - "filter_all": "Alle", - "filter_pending": "Ventende", - "filter_completed": "Fullførte", - "filter_overdue": "Forfalte", - "show_completed": "Vis fullførte oppgaver", - "create": "Ny oppgave", - "edit": "Rediger oppgave", - "title_placeholder": "Oppgavetittel", - "description_placeholder": "Legg til en beskrivelse...", - "due_date": "Forfallsdato", - "include_time": "Ta med klokkeslett", - "priority": "Prioritet", - "priority_none": "Ingen", - "priority_high": "Høy", - "priority_medium": "Middels", - "priority_low": "Lav", - "progress": "Status", - "progress_needs_action": "Ikke startet", - "progress_in_process": "Pågår", - "progress_completed": "Fullført", - "progress_cancelled": "Avbrutt", - "calendar": "Kalender", - "alert": "Påminnelse", - "alert_none": "Ingen", - "alert_at_time": "På forfallstidspunktet", - "alert_5min": "5 minutter før", - "alert_15min": "15 minutter før", - "alert_30min": "30 minutter før", - "alert_1hr": "1 time før", - "alert_1day": "1 dag før", - "delete": "Slett", - "cancel": "Avbryt", - "save": "Lagre", - "quick_add_placeholder": "Legg til en oppgave...", - "due_today": "I dag", - "due_tomorrow": "I morgen", - "overdue": "Forfalt" - } - }, - "calendar_event_notifications": { - "someone": "Noen", - "invited": "{name} har invitert deg til «{title}»", - "updated": "{name} har oppdatert «{title}»", - "cancelled": "{name} har avlyst «{title}»", - "untitled": "Hendelse uten tittel" - }, - "share_notifications": { - "someone": "Noen", - "shared": "{name} har delt {kind} «{object}» med deg", - "changed": "{name} har endret tilgangen din til {kind} «{object}»", - "revoked": "{name} har fjernet tilgangen din til {kind} «{object}»", - "object": { - "folder": "mappen", - "calendar": "kalenderen", - "address_book": "adresseboken", - "files": "filmappen" - } - }, - "sharing": { - "title": "Del «{name}»", - "description": "Gi andre brukere eller grupper på denne serveren tilgang. Endringer trer i kraft umiddelbart.", - "no_shares": "Ikke delt med noen ennå.", - "add_person": "Legg til person eller gruppe", - "search_placeholder": "Søk etter navn eller e-postadresse...", - "loading_principals": "Laster inn brukere...", - "no_principals": "Ingen andre brukere eller grupper funnet.", - "no_match": "Ingen treff.", - "remove": "Fjern tilgang", - "group": "Gruppe", - "share_added": "Tilgang er gitt", - "share_updated": "Tilgangen er oppdatert", - "share_removed": "Tilgangen er fjernet", - "share_failed": "Kunne ikke oppdatere deling", - "preset": { - "freeBusy": "Bare ledig/opptatt", - "read": "Kan lese", - "readWrite": "Kan lese og skrive", - "manager": "Kan administrere", - "custom": "Egendefinert" - } - }, - "advanced_search": { - "title": "Avansert søk", - "from": "Fra", - "from_placeholder": "Avsenderens e-postadresse eller navn", - "to": "Til", - "to_placeholder": "Mottakerens e-postadresse eller navn", - "subject": "Emne", - "subject_placeholder": "Emnet inneholder...", - "body": "Meldingstekst", - "body_placeholder": "Meldingsteksten inneholder...", - "folder": "Mappe", - "all_folders": "Alle mapper", - "has_attachment": "Vedlegg", - "date_after": "Etter", - "date_before": "Før", - "size_min": "Større enn (KB)", - "size_max": "Mindre enn (KB)", - "starred": "Stjernemerket", - "unread": "Ulest", - "read": "Lest", - "yes": "Ja", - "no": "Nei", - "clear": "Tøm", - "clear_all": "Fjern alle", - "filters_active": "{count} filter", - "filters_active_plural": "{count} filtre", - "toggle_filters": "Flere", - "search_hint": "Bruk avanserte filtre for mer presise søk", - "advanced_filters_tooltip": "Avanserte søkefiltre", - "results_found": "{count, plural, =0 {Ingen treff} one {# treff} other {# treff}}", - "results_found_more": "{count}+ treff", - "suggestions_recent": "Nylige søk", - "suggestions_people": "Personer", - "suggestions_from": "Søk etter e-post fra {address}", - "suggestions_to": "Søk etter e-post til {address}", - "suggestions_remove_recent": "Fjern fra nylige søk" - }, - "welcome": { - "title": "Velkommen til postkassen din", - "tip_compose": "Trykk c for å skrive en ny e-post", - "tip_shortcuts": "Trykk på ? for å se alle tastatursnarveier", - "tip_sidebar": "Finn kalender, kontakter og innstillinger i menyen i sidefeltet", - "tip_settings": "Tilpass nettposten under Innstillinger", - "got_it": "Skjønner", - "settings": "Innstillinger", - "dismiss": "Lukk", - "start_tour": "Start omvisningen" - }, - "demo_welcome": { - "title": "Velkommen til Bulwark Mail", - "description": "Utforsk en fullverdig nettpostklient – rett i nettleseren. Alle data forblir på enheten din, så du kan gjerne prøve alt.", - "feature_email": "Les og skriv e-post", - "feature_organize": "Etiketter, stjerner og mapper", - "feature_shortcuts": "Tastatursnarveier", - "feature_privacy": "100 % privat demo", - "hint": "Klikk på en av e-postene til venstre for å komme i gang, eller start omvisningen nedenfor." - }, - "files": { - "office_edit": "Rediger dokument", - "office_loading": "Åpner dokumentredigering…", - "office_error": "Dokumentredigeringen kunne ikke åpnes.", - "office_close": "Lukk redigering", - "open_in_new_tab": "Åpne i ny fane", - "title": "Filer", - "search_placeholder": "Søk etter filer...", - "empty_state_title": "Ingen filer ennå", - "empty_state_description": "Last opp filer eller opprett mapper for å komme i gang", - "upload": "Last opp", - "upload_files": "Last opp filer", - "new_folder": "Ny mappe", - "new_folder_name": "Mappenavn", - "rename": "Gi nytt navn", - "rename_title": "Gi nytt navn", - "new_name": "Nytt navn", - "name_forbidden_chars": "Serveren tillater ikke disse tegnene i navn: {chars}", - "name_reserved": "Dette navnet er reservert på serveren.", - "delete": "Slett", - "delete_confirm_title": "Slett fil eller mappe", - "delete_confirm_message": "Er du sikker på at du vil slette «{name}»? Dette kan ikke angres.", - "download": "Last ned", - "name": "Navn", - "size": "Størrelse", - "modified": "Endret", - "type": "Type", - "folder": "Mappe", - "file": "Fil", - "parent_directory": "Overordnet mappe", - "breadcrumb_root": "Hjem", - "other_accounts": "Andre kontoer", - "no_accounts": "Ingen tilkoblede kontoer.", - "drop_files_here": "Slipp filer eller mapper her for å laste opp", - "uploading": "Laster opp...", - "upload_success": "{count, plural, one {1 fil lastet opp} other {# filer lastet opp}}", - "upload_error": "Kunne ikke laste opp filen", - "create_folder_success": "Mappen er opprettet", - "create_folder_error": "Kunne ikke opprette mappe", - "delete_success": "Elementet er slettet", - "delete_error": "Kunne ikke slette elementet", - "rename_success": "Navnet er endret", - "rename_error": "Kunne ikke endre navnet", - "download_error": "Kunne ikke laste ned", - "not_available": "Fillagring er ikke tilgjengelig på denne serveren", - "cancel": "Avbryt", - "create": "Opprett", - "save": "Lagre", - "no_results": "Ingen filer passer med søket ditt", - "batch_delete_confirm_message": "Er du sikker på at du vil slette {count, plural, one {1 element} other {# elementer}}? Dette kan ikke angres.", - "batch_delete_success": "{count, plural, one {1 element slettet} other {# elementer slettet}}", - "grid_view": "Rutenettvisning", - "list_view": "Listevisning", - "details": "Detaljer", - "path": "Sti", - "preview": "Forhåndsvisning", - "preview_error": "Kunne ikke laste inn forhåndsvisningen", - "cut": "Klipp ut", - "copy": "Kopier", - "paste": "Lim inn", - "move_success": "{count, plural, one {1 element flyttet} other {# elementer flyttet}}", - "move_error": "Kunne ikke flytte elementet", - "paste_success": "Limt inn", - "paste_error": "Kunne ikke lime inn", - "new_text_file": "Ny tekstfil", - "file_name": "Filnavn", - "retry": "Prøv på nytt", - "refresh": "Oppdater", - "toggle_favorite": "Legg til eller fjern favoritt", - "duplicate": "Dupliser", - "duplicate_success": "Duplisert", - "duplicate_error": "Kunne ikke duplisere", - "share": "Del", - "shared": "Delt", - "shared_with_me": "Delt med meg", - "shared_by": "Delt av {name}", - "create_file_success": "Filen er opprettet", - "create_file_error": "Kunne ikke opprette filen", - "favorites": "Favoritter", - "recent": "Nylige", - "properties": "Egenskaper", - "open_folder": "Åpne mappen", - "upload_folder": "Last opp mappe", - "file_too_large": "«{name}» overskrider den maksimale filstørrelsen på {max}", - "undo": "Angre", - "undo_success": "Handlingen er angret", - "undo_error": "Kunne ikke angre", - "toolbar": "Filhandlinger", - "open_folder_tree": "Åpne mappetreet", - "file_list": "Filer og mapper", - "context_menu": "Handlinger", - "settings_title": "Filinnstillinger", - "settings_display": "Visning", - "settings_default_view": "Standardvisning", - "settings_default_view_desc": "Velg mellom rutenett og listeoppsett", - "settings_default_sort": "Standardsortering", - "settings_default_sort_desc": "Velg standardsortering for filer", - "settings_sort_direction": "Sorteringsretning", - "settings_sort_direction_desc": "Velg stigende eller synkende rekkefølge", - "settings_ascending": "Stigende", - "settings_descending": "Synkende", - "settings_icons": "Ikoner", - "settings_show_icons": "Vis filikoner", - "settings_show_icons_desc": "Vis ikoner ved siden av filer og mapper", - "settings_colored_icons": "Fargede ikoner", - "settings_colored_icons_desc": "Bruk fargede ikoner i stedet for ensfargede", - "settings_show_thumbnails": "Vis miniatyrbilder", - "settings_show_thumbnails_desc": "Vis bildeforhåndsvisninger i stedet for ikoner for bildefiler", - "settings_behavior": "Virkemåte", - "settings_show_hidden": "Vis skjulte filer", - "settings_show_hidden_desc": "Vis filer og mapper som starter med en prikk", - "settings_folder_layout": "Mappenavigering", - "settings_folder_layout_desc": "Velg om mapper skal vises sammen med filene eller i et tre i sidefeltet", - "settings_folder_layout_inline": "Sammen med filer", - "settings_folder_layout_sidebar": "Sidefelt", - "disabled_title": "Administratoren har deaktivert filfunksjonen", - "disabled_description": "Store filopplastinger via WebDAV kan føre til ustabilitet i Stalwart/RocksDB, blant annet krasj på grunn av for lite minne og diskplass som ikke blir frigjort. Slettede filer kan ikke fjernes umiddelbart fra blob-lagringen. Denne funksjonen anbefales ikke for produksjonsmiljøer.", - "stability_warning": "Store filopplastinger kan gjøre serveren ustabil. Slettede filer kan ikke fjernes umiddelbart fra lagringen. Bruk med forsiktighet.", - "migration_title": "Oppdaterer filene dine...", - "migration_description": "Mapper og filer organiseres i riktig struktur. Dette skjer bare én gang." - }, - "smime": { - "your_certificates": "Dine sertifikater", - "your_certificates_desc": "Importer og administrer S/MIME-sertifikatene dine for signering og kryptering av e-post", - "recipient_certificates": "Mottakersertifikater", - "recipient_certificates_desc": "Offentlige sertifikater for kryptering av e-post til mottakere", - "identity_bindings": "Sertifikater for avsenderidentiteter", - "identity_bindings_desc": "Knytt S/MIME-sertifikater til avsenderidentitetene dine", - "defaults_title": "Standardinnstillinger", - "defaults_desc": "Velg standardinnstillinger for signering og kryptering", - "import_pkcs12": "Importer PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importer sertifikat", - "no_certificates": "Ingen sertifikater er importert ennå", - "no_recipient_certs": "Ingen mottakersertifikater", - "expires": "Utløper", - "expired": "Utløpt", - "bound_to": "Knyttet til", - "no_key_bound": "Ingen", - "lock": "Lås nøkkelen", - "unlock": "Lås opp nøkkelen", - "details": "Se detaljer", - "delete": "Slett", - "encrypt_by_default": "Krypter som standard", - "encrypt_by_default_desc": "Krypter e-post automatisk når alle mottakere har sertifikater", - "remember_unlocked": "Husk ulåste nøkler", - "remember_unlocked_desc": "Hold nøklene ulåste så lenge denne nettleserøkten varer", - "sign_default_for": "Signer som standard for", - "enter_p12_passphrase": "Skriv inn passordfrasen til PKCS#12-filen", - "p12_passphrase_desc": "Skriv inn passordfrasen som beskytter denne sertifikatfilen", - "enter_storage_passphrase": "Angi passordfrase for lagring", - "storage_passphrase_desc": "Velg en passordfrase som beskytter nøkkelen når den er lagret i nettleseren", - "next": "Neste", - "import": "Importer", - "unlock_key": "Lås opp nøkkelen", - "unlock_key_desc": "Skriv inn passordfrasen for lagring for å låse opp nøkkelen til signering eller dekryptering", - "passphrase_placeholder": "Skriv inn passordfrase", - "confirm_passphrase_placeholder": "Bekreft passordfrase", - "passphrase_mismatch": "Passordfrasene er ikke like", - "cancel": "Avbryt", - "processing": "Behandler...", - "close": "Lukk", - "certificate_details": "Sertifikatdetaljer", - "cert_subject": "Innehaver", - "cert_issuer": "Utsteder", - "cert_email": "E-post", - "cert_serial": "Serienummer", - "cert_validity": "Gyldighet", - "cert_fingerprint": "Fingeravtrykk (SHA-256)", - "cert_algorithm": "Algoritme", - "cert_capabilities": "Bruksområder", - "cert_source": "Kilde", - "cert_expired": "Dette sertifikatet har utløpt", - "cert_not_yet_valid": "Dette sertifikatet er ikke gyldig ennå", - "cap_sign": "Signering", - "cap_encrypt": "Kryptering", - "cap_none": "Ingen", - "show_passphrase": "Vis passordfrase", - "hide_passphrase": "Skjul passordfrase", - "sign_toggle": "Signer", - "encrypt_toggle": "Krypter", - "missing_recipient_certs": "Manglende sertifikater for: {emails}", - "missing_sender_cert": "Det er ikke knyttet noe sertifikat til denne identiteten", - "status_encrypted_ok": "Denne meldingen ble kryptert", - "status_encrypted_no_key": "Denne meldingen er kryptert, men det ble ikke funnet noen nøkkel som passer", - "status_encrypted_failed": "Kunne ikke dekryptere denne meldingen", - "status_signed_valid": "Signaturen er bekreftet", - "status_signed_invalid": "Kunne ikke bekrefte signaturen", - "status_signed_expired_cert": "Signert med et utløpt sertifikat", - "status_signed_self_signed": "Signaturen er gyldig, men sertifikatet er selvsignert og regnes ikke som pålitelig", - "status_signed_mismatch": "Signaturen er gyldig, men den som signerte, samsvarer ikke med avsenderen", - "status_unsupported": "S/MIME-formatet støttes ikke", - "auto_import_signer_certs": "Importer signeringssertifikater automatisk", - "auto_import_signer_certs_desc": "Lagre automatisk sertifikater fra e-poster med bekreftet signatur for fremtidig kryptering", - "export": "Eksporter", - "enter_export_passphrase": "Angi passordfrase for eksport", - "export_passphrase_desc": "Velg en passordfrase for å beskytte den eksporterte PKCS#12-filen", - "export_storage_desc": "Skriv inn passordfrasen for lagring for å dekryptere nøkkelen før eksport", - "incorrect_passphrase": "Feil passordfrase" - }, - "tour": { - "step_counter": "Trinn {current} av {total}", - "skip": "Hopp over omvisningen", - "back": "Tilbake", - "next": "Neste", - "finish": "Fullfør", - "take_a_tour": "Ta en omvisning i grensesnittet", - "restart_title": "Introduksjonsomvisning", - "restart_desc": "Vis den veiledede omvisningen i grensesnittet på nytt", - "restart_button": "Start omvisningen på nytt", - "show_on_new_devices_title": "Vis på nye enheter", - "show_on_new_devices_desc": "Vis velkomstbanneret og omvisningen første gang du logger på en ny enhet, selv om du allerede har fullført dem andre steder", - "sidebar_title": "Dine postkasser", - "sidebar_desc": "Dette er mappesidefeltet. Klikk på en postkasse for å se e-postene. Du kan opprette mapper, dra e-poster mellom dem og se antall uleste meldinger med et øyekast.", - "compose_title": "Skriv en e-post", - "compose_desc": "Klikk her for å skrive en ny e-post. Du kan legge til mottakere og vedlegg og bruke formatert tekst.", - "search_title": "Søk i e-posten din", - "search_desc": "Søk etter avsender, emne eller innhold. Klikk på filterikonet for avanserte alternativer som datoperiode, vedlegg og stjernemerkede meldinger.", - "email_list_title": "Din e-postliste", - "email_list_desc": "E-poster vises her. Klikk på én for å lese den til høyre. Bruk avmerkingsboksen til å velge flere, og flytt, slett eller merk dem samlet.", - "email_viewer_title": "Leseruten", - "email_viewer_desc": "Den valgte e-posten åpnes her. Svar, videresend, arkiver eller slett med knappene på verktøylinjen. Du kan også stjernemerke e-poster eller legge til fargeetiketter.", - "keywords_title": "Fargeetiketter", - "keywords_desc": "Organiser e-posten med fargekodede etiketter. Dra en e-post over på en etikett for å merke den, eller høyreklikk på e-posten for å velge etiketter.", - "calendar_title": "Kalender", - "calendar_desc": "Bytt til kalenderen for å administrere hendelsene dine. Opprett hendelser, angi påminnelser og velg dags-, ukes- eller månedsvisning.", - "contacts_title": "Kontakter", - "contacts_desc": "Her finner du adresseboken din. Importer kontakter, opprett grupper og klikk på en kontakt for å se alle detaljene.", - "settings_title": "Innstillinger", - "settings_desc": "Tilpass alt: tema, tetthet, signaturer, filtre, tastatursnarveier, standardinnstillinger for kalenderen og mer.", - "shortcuts_title": "Tastatursnarveier", - "shortcuts_desc": "Erfarne brukere vil like dette. Trykk på ? når som helst for å se alle tilgjengelige tastatursnarveier. Du kan navigere, skrive og administrere e-post uten å bruke musen.", - "compose_open_title": "Meldingsvinduet", - "compose_open_desc": "Dette er meldingsvinduet. Legg til mottakere, skriv meldingen, legg ved filer og bruk formatert tekst. Du kan også lagre utkast og bruke maler.", - "calendar_view_title": "Kalenderen din", - "calendar_view_desc": "Her er kalenderen din med eksempler på hendelser. Du kan bytte mellom dag, uke, måned og agendavisning ved å bruke verktøylinjen.", - "create_event_title": "Opprett en hendelse", - "create_event_desc": "Klikk på denne knappen for å opprette en ny kalenderhendelse. Du kan angi en tittel, dato, klokkeslett og legge til deltakere.", - "event_modal_title": "Hendelsesdetaljer", - "event_modal_desc": "Her er hendelsesskjemaet. Fyll inn tittelen, velg dato og klokkeslett, og legg til sted eller deltakere. Trykk på «Lagre» når du er ferdig – eller lukk skjemaet og gå videre.", - "contacts_list_title": "Dine kontakter", - "contacts_list_desc": "Her er kontaktene dine. Klikk på en kontakt for å se all informasjonen til høyre. Du kan også opprette nye kontakter, importere vCard-filer eller organisere kontakter i grupper.", - "settings_tabs_title": "Innstillingsmenyen", - "settings_tabs_desc": "Her er alle innstillingskategoriene. Tilpass utseendet, administrer identiteter, sett opp e-postfiltre, velg kalenderinnstillinger og mye mer.", - "files_title": "Fillagring", - "files_desc": "Filutforskeren lar deg laste opp, organisere og dele filer – som et personlig skylager innebygd i e-posten din.", - "demo_banner_title": "Demokontroller", - "demo_banner_desc": "Du er i demomodus – alt forblir i nettleseren. Trykk på «Tilbakestill demo» når som helst for å begynne på nytt med nye eksempeldata.", - "quota_title": "Lagringsbruk", - "quota_desc": "Følg med på størrelsen på postkassen her. Sirkelen fylles opp etter hvert som du bruker mer plass." - }, - "unified_mailbox": { - "search_unavailable": "Søk er ikke tilgjengelig i den samlede visningen" - }, - "quote_header": { - "reply_line": "Den {date} skrev {from}:", - "forwarded_separator": "---------- Videresendt melding ----------", - "from_label": "Fra", - "date_label": "Dato", - "subject_label": "Emne" - }, - "pwa_install": { - "title": "Installer {appName}", - "description": "Installer appen for rask tilgang og støtte for bruk uten nett.", - "not_now": "Ikke nå", - "install": "Installer", - "dont_remind": "Ikke minn meg på det igjen", - "dismiss_aria": "Lukk forespørselen om installering" - }, - "deep_link": { - "message_not_found": "Denne meldingen er ikke lenger tilgjengelig.", - "thread_not_found": "Denne samtalen er ikke lenger tilgjengelig.", - "folder_not_found": "Denne mappen er ikke lenger tilgjengelig.", - "event_not_found": "Denne hendelsen er ikke lenger tilgjengelig.", - "file_not_found": "Denne filen er ikke lenger tilgjengelig.", - "account_unavailable": "Logg på den kontoen for å åpne denne lenken.", - "copy_message": "Kopier lenken til meldingen", - "copy_conversation": "Kopier lenken til samtalen", - "copy_event": "Kopier lenken til hendelsen", - "copied": "Lenken er kopiert", - "copy_failed": "Kunne ikke kopiere lenken" - }, - "plugins": { - "oauth_callback": { - "completing": "Fullfører autorisering…", - "completing_message": "Vennligst vent mens tilkoblingen opprettes.", - "received_title": "Autorisering mottatt", - "received_message": "Du kan gå tilbake til fanen der du startet tilkoblingen.", - "failed_title": "Autorisering mislyktes", - "denied": "Autorisering ble avslått.", - "failed_detail": "Autorisering mislyktes: {error}", - "missing_params": "Mangler autorisasjonskode eller tilstand.", - "delivery_failed": "Autoriseringsresultatet kunne ikke leveres til app-fanen. Lukk dette vinduet og prøv igjen." - } - }, - "global_search": { - "title": "Søk i alt", - "placeholder": "Søk i e-post, kontakter, kalender, filer...", - "scope_label": "Søk i", - "account_label": "Konto", - "scope_all": "Alt", - "scope_mail": "E-post", - "scope_contacts": "Kontakter", - "scope_calendar": "Kalender", - "scope_files": "Filer", - "all_accounts": "Alle kontoer", - "show_all": "Vis alle resultater", - "no_results": "Ingen resultater for “{query}”", - "server_hint": "Hele ord søker på serveren; deler av ord treffer allerede innlastede elementer", - "searching": "Søker...", - "account_error": "Kunne ikke søke i {account}", - "recent_searches": "Nylige søk", - "recurring": "Gjentakende", - "no_subject": "(no subject)", - "empty_hint": "Søk i e-post, kontakter, hendelser og filer på alle kontoer. Prøv from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Flatt etter dato", - "grouped": "Gruppert", - "load_more": "Last inn flere", - "preview_empty": "Velg et resultat for å forhåndsvise det", - "open_full": "Åpne", - "view_label": "Visning" - } -} +{ + "meta_description": "Minimalistisk nettpostklient som bruker JMAP-protokollen", + "login": { + "title": "Nettpost", + "username_label": "E-post", + "username_placeholder": "user@example.com", + "password_label": "Passord", + "password_placeholder": "Skriv inn passordet ditt", + "jmap_endpoint_label": "JMAP-server", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Serveren må tillate CORS-forespørsler fra dette domenet.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Serveren ble valgt ut fra e-postdomenet ditt.", + "sign_in": "Logg på", + "signing_in": "Logger på...", + "loading": "Laster inn...", + "reconnecting": "Tilkoblingen ble brutt. Forsøker å koble til på nytt...", + "error": { + "invalid_credentials": "Ugyldig e-postadresse eller passord. Sjekk påloggingsopplysningene dine og prøv igjen.", + "invalid_token": "Serveren godtok ikke dette tilgangstokenet. Sjekk at det er fullstendig, og at det ikke har utløpt eller blitt tilbakekalt.", + "connection_failed": "Kan ikke nå serveren. Sjekk internettforbindelsen og prøv igjen.", + "cors_blocked": "Serveren er tilgjengelig, men blokkerer forespørsler fra andre opprinnelser. Sjekk JMAP-serverens CORS-innstillinger og tillat dette domenet.", + "server_error": "Serveren er midlertidig utilgjengelig. Prøv igjen senere.", + "generic": "Det oppstod en uventet feil. Kontakt administratoren din hvis feilen vedvarer.", + "totp_required": "Du må skrive inn en tofaktorautentiseringskode nedenfor.", + "totp_invalid": "Ugyldig autentiseringskode. Sjekk autentiseringsappen din og prøv igjen.", + "oauth_discovery_failed": "SSO er aktivert, men identitetsleverandøren kunne ikke nås. Sjekk OAuth-konfigurasjonen.", + "token_exchange_failed": "Passordet og koden ble godtatt, men e-postserveren nektet å starte en økt for denne appen. Be administratoren din om å sjekke OAuth-klientinnstillingene." + }, + "show_password": "Vis passord", + "hide_password": "Skjul passord", + "totp_toggle": "Jeg har en kode for tofaktorautentisering", + "token_toggle": "Logg på med et tilgangstoken", + "password_toggle": "Logg på med passord", + "token_label": "Tilgangstoken", + "token_placeholder": "Lim inn API-tokenet ditt", + "token_hint": "Opprett et API-token i innstillingene hos e-postleverandøren din, og lim det inn her.", + "remember_me": "Husk meg", + "config_error": { + "title": "Konfigurasjonsfeil", + "fetch_failed": "Kan ikke laste inn programoppsettet. Prøv igjen senere.", + "server_not_configured": "E-postserveren er ikke konfigurert. Kontakt administratoren din." + }, + "remove_from_history": "Fjern fra historien", + "totp_label": "Autentiseringskode", + "totp_placeholder": "000 000", + "session_expired": "Økten din er utløpt. Logg på igjen.", + "dismiss": "Lukk", + "or": "eller", + "sign_in_sso": "Logg på med SSO", + "add_account_title": "Legg til konto", + "add_account_subtitle": "Logg på med en annen konto", + "cancel": "Avbryt", + "website": "Nettsted", + "imprint": "Juridisk informasjon", + "privacy_policy": "Personvernerklæring", + "try_demo": "Prøv demoen", + "demo_description": "Utforsk med eksempeldata – ingen konto nødvendig", + "demo_launching": "Starter demoen...", + "demo_login_button": "Start demoen", + "demo_tagline": "Prøv en fullverdig e-postklient. Du trenger ingen konto.", + "demo_no_signup": "Ingen registrering nødvendig – utforsk fritt med eksempeldata", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Fullfører pålogging...", + "oauth_error": { + "title": "Autentisering mislyktes", + "invalid_state": "Sikkerhetskontrollen mislyktes. Prøv å logge på igjen.", + "missing_params": "Manglende autorisasjonsdata. Prøv å logge på igjen.", + "token_exchange_failed": "Kunne ikke fullføre autentiseringen. Prøv igjen.", + "access_denied": "Du fikk ikke tilgang. Kontakt administratoren din.", + "back_to_login": "Tilbake til pålogging" + } + }, + "sidebar": { + "close": "Lukk", + "compose": "Skriv", + "compose_hint": "Skriv (c)", + "search_placeholder": "Søk i e-post...", + "search_placeholder_hint": "Søk i e-post... (trykk /)", + "storage": "Lagring", + "storage_used": "Brukt", + "storage_free": "Ledig", + "storage_total": "Totalt", + "sign_out": "Logg ut", + "sign_out_of": "Logg ut av {account}", + "sign_out_all": "Logg ut av alle kontoer", + "add_account": "Legg til konto", + "set_as_default": "Sett som standard", + "switch_account": "Bytt konto", + "contacts": "Kontakter", + "calendar": "Kalender", + "settings": "Innstillinger", + "admin": "Administrasjon", + "files": "Filer", + "loading_mailboxes": "Laster inn postkasser...", + "push_connected": "Sanntidsoppdateringer er aktive", + "push_disconnected": "Sanntidsoppdateringer er inaktive", + "keyboard_shortcuts": "Tastatursnarveier", + "theme": { + "light": "Lys modus", + "dark": "Mørk modus", + "system": "Systemtema" + }, + "language": { + "title": "Språk" + }, + "mailboxes": { + "inbox": "Innboks", + "sent": "Sendt", + "drafts": "Utkast", + "trash": "Papirkurv", + "archive": "Arkiv", + "starred": "Stjernemerket", + "all_mail": "All e-post", + "spam": "Søppelpost", + "important": "Viktig" + }, + "unified_inbox": "Samlet innboks", + "unified_sent": "Alle sendte meldinger", + "unified_drafts": "Alle utkast", + "unified_trash": "Alle papirkurver", + "unified_archive": "Alle arkiver", + "unified_junk": "All søppelpost", + "all_accounts": "Alle kontoer", + "all_accounts_loading": "Laster inn kontoer: {loaded} av {total}", + "unified_mailbox": "Samlet postkasse", + "expand": "Utvid", + "collapse": "Fold sammen", + "expand_tooltip": "Utvid", + "collapse_tooltip": "Fold sammen", + "mobile": { + "search": "Søk", + "compose": "Skriv", + "go_back": "Gå tilbake", + "toggle_menu": "Vis eller skjul menyen" + }, + "clear_search": "Tøm søket", + "vacation_active": "Feriesvar er aktivt", + "demo_banner": "Demomodus", + "demo_reset": "Tilbakestill", + "demo_tour": "Omvisning", + "tags": "Etiketter", + "show_all_tags": "Vis alle ({count})", + "show_fewer_tags": "Vis færre", + "folders": "Mapper", + "your_folders": "Dine mapper", + "create_folder": "Opprett mappe", + "show_more": "Mer", + "show_fewer": "Mindre", + "shared": "Delt", + "shared_account_hidden": "Skjult fra sidepanelet. Vis den igjen under Innstillinger › Oppsett.", + "notifications_muted": "Varsler dempet", + "mail": "E-post", + "nav_label": "Navigasjon", + "add_app": "Apper", + "scheduled": "Planlagt", + "unified_all_unread": "Alle uleste", + "unified_all_starred": "Alle stjernemerkede", + "unified_all_mail": "All e-post", + "remove_account": "Fjern konto", + "remove_account_confirm": "Vil du fjerne {account} fra denne enheten? Du kan legge den til igjen senere.", + "labels": "Etiketter", + "create_label": "Opprett ny etikett", + "mv_tasks": "Oppgaver", + "mv_side_panel": "Sidepanel", + "mv_open_full": "Åpne i full visning", + "mv_previous_day": "Forrige dag", + "mv_next_day": "Neste dag", + "mv_add_another_account": "Legg til en annen konto", + "mv_manage_account": "Administrer kontoen", + "mv_storage_used": "{percent} % av {total} brukt" + }, + "protocol_handlers": { + "title": "Standardapper", + "description": "Velg om e-post- og kalenderlenker skal åpnes i Bulwark. Teknisk sett registrerer Bulwark seg som protokollbehandler for mailto:- og webcal:-lenker.", + "unsupported": "Denne nettleseren eller tilkoblingen støtter ikke manuell registrering av protokollbehandlere. Det kan hende du likevel kan velge den installerte PWA-en i innstillingene for nettleseren eller operativsystemet.", + "mailto_label": "E-postlenker", + "mailto_description": "Åpne mailto:-lenker i Bulwark med et forhåndsutfylt meldingsvindu.", + "protocol_open_mode_label": "Når mailto:- og webcal:-lenker åpnes", + "protocol_open_mode_description": "Velg om Bulwark skal åpne mailto:- og webcal:-lenker i en ny fane eller bruke en økt som allerede er åpen. Alternativet for aktiv økt krever tillatelse til varsler. Hvis nettleseren hindrer Bulwark i å få fokus, kan du klikke på et varsel for å vise vinduet.", + "protocol_open_mode_active_session": "Åpne i aktiv økt hvis mulig", + "protocol_open_mode_new_tab": "Åpne alltid en ny fane", + "focus_notification_title": "Åpne Bulwark", + "focus_notification_body": "Lenken ble åpnet i Bulwark. Klikk for å vise vinduet.", + "webcal_label": "Kalenderlenker", + "webcal_description": "Åpne webcal:-lenker i Bulwark med en forhåndsutfylt dialog for kalenderabonnement.", + "register_mailto": "Registrer e-postprogram", + "register_webcal": "Registrer kalenderprogram", + "mailto_registered": "Forespørsel om registrering av e-postprogram er sendt", + "webcal_registered": "Forespørsel om registrering av kalenderprogram er sendt", + "registration_failed": "Registrering av protokollbehandler mislyktes", + "opening_mailto": "Åpner meldingsvinduet...", + "opening_webcal": "Åpner kalenderen...", + "browser_note": "Nettleseren eller operativsystemet kan be deg bekrefte dette. Det kan også hende at Bulwark må installeres før programmet kan velges som standardapp.", + "select_account_title": "Velg konto", + "select_mailto_account": "Velg hvilken konto som skal åpne denne e-postlenken.", + "select_webcal_account": "Velg hvilken konto som skal åpne denne kalenderlenken.", + "select_account_note": "Dette gjelder bare denne lenken.", + "detail_to": "Til", + "detail_subject": "Emne", + "detail_no_subject": "Uten emne", + "detail_calendar": "Kalender", + "detail_source": "Kilde", + "active_account": "Aktiv", + "switching_account": "Bytter konto..." + }, + "sidebar_apps": { + "modal_title": "Apper i sidefeltet", + "add_new": "Legg til app", + "edit_app": "Rediger app", + "name_label": "Navn", + "name_placeholder": "Min app", + "name_required": "Navn må fylles ut", + "url_label": "URL", + "url_required": "URL må fylles ut", + "url_invalid": "Skriv inn en gyldig HTTP- eller HTTPS-URL", + "icon_label": "Ikon", + "icon_required": "Du må velge et ikon", + "open_mode_label": "Åpningsmodus", + "open_new_tab": "Ny fane", + "open_inline": "I sidefeltet", + "cancel": "Avbryt", + "add": "Legg til", + "update": "Oppdater", + "delete": "Slett", + "delete_confirm_title": "Slett appen", + "delete_confirm": "Er du sikker på at du vil slette «{name}»?", + "no_apps": "Ingen apper er lagt til ennå", + "no_apps_hint": "Legg til egendefinerte apper og lenker i sidefeltet", + "managed_title": "Levert av administratoren din", + "managed_description": "Disse appene er satt opp for alle og kan ikke endres.", + "managed_badge": "Administrert", + "search_icons": "Søk etter ikoner...", + "show_popular": "Populære", + "show_all": "Alle", + "no_icons_found": "Ingen ikoner funnet", + "inline_badge": "Sidefelt", + "tab_badge": "Fane", + "show_on_mobile": "Vis på mobil" + }, + "email_list": { + "columns": { + "from": "Fra", + "to": "Til", + "subject": "Emne", + "date": "Dato", + "size": "Størrelse" + }, + "no_emails": "Ingen meldinger funnet", + "no_emails_description": "Denne postkassen er tom", + "no_search_results": "Ingen resultater funnet", + "no_search_results_description": "Prøv å justere søket eller filtrene", + "loading": "Laster inn e-poster...", + "unread": "ulest", + "to_me": "Til meg", + "to_recipients": "Til {count} mottakere", + "and_others": "og {count} andre", + "draft": "Utkast", + "starred": "Stjernemerket", + "pinned": "Festet", + "replied": "Besvart", + "forwarded": "Videresendt", + "has_attachment": "Har vedlegg", + "conversations_range": "{from}–{to, number} av {total, number}", + "conversations_count": "{count} av {total} samtaler", + "conversations_count_plus": "{count}+ samtaler", + "conversations_count_simple": "{count} samtaler", + "no_conversations": "Ingen samtaler", + "loading_more": "Laster inn flere e-poster...", + "no_more_emails": "Ingen flere e-poster å laste inn", + "batch_actions": { + "select": "Velg e-poster", + "select_all": "Velg alle", + "selected_messages": "{count, plural, one {1 e-post} other {# e-poster}} valgt", + "mark_read": "Merk som lest", + "mark_unread": "Merk som ulest", + "delete": "Slett", + "delete_confirm_title": "Slett e-poster", + "delete_confirm_message": "Er du sikker på at du vil slette {count, plural, one {1 e-post} other {# e-poster}}?", + "clear_selection": "Fjern valg" + }, + "permanent_delete": "Slett permanent", + "permanent_delete_confirm_title": "Slett permanent", + "permanent_delete_confirm_message": "Denne e-posten slettes permanent. Denne handlingen kan ikke angres.", + "permanent_delete_confirm_batch_message": "{count, plural, one {Denne e-posten} other {Disse # e-postene}} blir slettet permanent. Denne handlingen kan ikke angres.", + "empty_folder": { + "button": "Tøm mappe", + "confirm_title": "Tøm mappe", + "confirm_message": "Alle e-postene i denne mappen slettes permanent. Denne handlingen kan ikke angres.", + "confirm_message_trash": "Alle e-poster i denne mappen flyttes til papirkurven.", + "confirm_button": "Tøm mappe", + "junk_hint": "Du kan tømme søppelpostmappen for å fjerne alle meldinger permanent.", + "trash_hint": "Du kan tømme papirkurven for å fjerne alle meldinger permanent." + }, + "no_scheduled_emails": "Ingen planlagte e-poster", + "no_scheduled_emails_description": "Meldinger som er planlagt til senere, vises her.", + "scheduled_count": "{count, plural, one {1 planlagt e-post} other {# planlagte e-poster}}", + "scheduled_actions_hint": "Bruk handlingene for planlagte meldinger til å avbryte, planlegge på nytt eller redigere dem.", + "cancel_scheduled_send": "Avbryt sending", + "reschedule_send": "Planlegg på nytt", + "cancel_and_edit": "Avbryt og rediger", + "cancel_and_compose_again": "Avbryt og skriv meldingen på nytt", + "reschedule_prompt": "Skriv inn ny dato og klokkeslett, for eksempel 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Avsenderen ba om å bli varslet når du åpner denne meldingen.", + "send": "Send lesebekreftelse", + "ignore": "Ignorer", + "sent": "Lesebekreftelsen er sendt.", + "send_failed": "Lesebekreftelsen kunne ikke sendes", + "mdn_subject": "Lest: {subject}", + "mdn_body": "Dette er en lesebekreftelse for meldingen du sendte til {recipient}.\n\nMerk: Denne bekreftelsen viser bare at meldingen ble vist på mottakerens datamaskin. Det er ingen garanti for at mottakeren har lest eller forstått meldingsinnholdet." + }, + "no_email_selected": "Ingen e-post er valgt", + "no_email_description": "Velg en e-post fra listen for å se den her", + "no_conversation_selected": "Ingen samtale er valgt", + "no_conversation_description": "Velg en samtale fra listen for å lese den her", + "compose": "Skriv", + "compose_hint": "Skriv ny melding", + "no_subject": "(Uten emne)", + "no_body_content": "(Meldingen har ikke noe innhold)", + "no_preview_available": "Ingen forhåndsvisning er tilgjengelig", + "show_quoted_text": "Vis sitert tekst", + "hide_quoted_text": "Skjul sitert tekst", + "loading_email": "Laster inn e-post...", + "loading": "Laster inn...", + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "delete": "Slett", + "archive": "Arkiver", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Stjernemerk", + "unstar": "Fjern stjerne", + "mark_unread": "Merk som ulest", + "mark_read": "Merk som lest", + "unread": "Ulest", + "read": "Lest", + "spam_short": "Søppelpost", + "not_spam_short": "Ikke søppelpost", + "move": "Flytt", + "print": "Skriv ut", + "view_source": "Vis meldingskilde", + "fullscreen": "Fullskjerm", + "exit_fullscreen": "Avslutt fullskjerm", + "view_in_dark_mode": "Vis i mørk modus", + "view_in_light_mode": "Vis i lys modus", + "export_email": "Eksporter som .eml", + "forward_as_attachment": "Videresend som vedlegg", + "import_email": "Importer .eml eller .zip", + "keyboard_shortcuts": "Tastatursnarveier (?)", + "email_source": "Meldingskilde", + "draft_banner": "Denne meldingen er et utkast", + "edit_draft": "Rediger", + "copy_source": "Kopier til utklippstavlen", + "source_copied": "Meldingskilden er kopiert til utklippstavlen", + "attachments": "Vedlegg", + "important": "Viktig", + "download": "Last ned", + "download_all": "Last ned alle", + "from": "Fra", + "to": "Til", + "cc": "CC", + "bcc": "BCC", + "date": "Dato", + "subject": "Emne", + "show_details": "Vis detaljer", + "hide_details": "Skjul detaljer", + "external_content_warning": "Bilder og eksternt innhold er blokkert", + "load_external_content": "Last inn bilder", + "trust_sender": "Stol alltid på denne avsenderen", + "sender_check": { + "unverified_label": "Ubekreftet avsender", + "failed_label": "Avsenderkontrollen mislyktes", + "unverified": "Denne meldingen utgir seg for å være fra {domain}, men ingenting bekrefter det. Den har ingen gyldig signatur fra {domain} og kom ikke fra en server som {domain} har godkjent.", + "unverified_sent_from": "Denne meldingen utgir seg for å være fra {domain}, men den ble sendt fra {host} og har ingen gyldig signatur fra {domain}.", + "failed": "Denne meldingen utgir seg for å være fra {domain}, men den besto ikke domenets avsenderkontroller. Den kan være forfalsket.", + "failed_sent_from": "Denne meldingen utgir seg for å være fra {domain}, men den ble sendt fra {host} og besto ikke avsenderkontrollene til {domain}. Den kan være forfalsket.", + "caution": "Vær forsiktig med lenker, vedlegg og forespørsler om passord eller betalingsopplysninger." + }, + "back_to_list": "Tilbake til listen", + "view_contact": "Se kontakt", + "message_details": "Meldingsdetaljer", + "more_reply_options": "Flere svaralternativer", + "set_tag": "Legg til etikett", + "tag": "Etikett", + "more_actions": "Flere handlinger", + "previous": "Forrige", + "next": "Neste", + "move_to": "Flytt til...", + "remove_tag": "Fjern etiketten", + "tag_filter_placeholder": "Filtrer etiketter", + "tag_no_matches": "Ingen etiketter passer med søket", + "more_count": "+{count} til", + "characters_count": "{count} tegn", + "quick_reply_placeholder": "Skriv et kort svar...", + "more_options": "Flere alternativer", + "sending": "Sender...", + "security_authentication": "Sikkerhet og autentisering", + "technical_details": "Tekniske detaljer", + "message_id_label": "Meldings-ID:", + "reply_to_label": "Svar til:", + "delivery_time_label": "Leveringstid:", + "conversation_part_label": "En del av samtalen:", + "previous_messages": "{count} tidligere melding", + "previous_messages_plural": "{count} tidligere meldinger", + "time": { + "day": "dag", + "days": "dager", + "hour": "time", + "hours": "timer", + "minute": "minutt", + "minutes": "minutter" + }, + "unknown_sender": "Ukjent", + "recipient_me": "meg", + "recipient_and_others": "{name} og {count} andre", + "recipient_to_prefix": "Til:", + "authentication": { + "title": "Autentisering", + "status": { + "verified": "Verifisert", + "warning": "Advarsel", + "none": "Ikke autentisert" + }, + "spf": { + "pass": "SPF godkjent", + "fail": "SPF ikke godkjent", + "none": "Ingen SPF" + }, + "dkim": { + "pass": "DKIM gyldig", + "fail": "DKIM ugyldig", + "none": "Ingen DKIM" + }, + "dmarc": { + "pass": "DMARC godkjent", + "fail": "DMARC ikke godkjent", + "none": "Ingen DMARC" + }, + "spam_score": "Poengsum for søppelpost", + "tooltip_spf": "Sender Policy Framework: Verifiserer at avsenderserveren er autorisert til å sende e-post på vegne av domenet", + "tooltip_dkim": "DomainKeys Identified Mail: Bekrefter at e-posten ikke ble endret under overføringen ved hjelp av en kryptografisk signatur", + "tooltip_dmarc": "Domenebasert meldingsautentisering, rapportering og samsvar: Kontrollerer at SPF og DKIM samsvarer med avsenderens domene, og fastsetter hvordan meldinger som ikke godkjennes, skal behandles", + "policy": "Retningslinje", + "result": { + "pass": "Godkjent", + "fail": "Mislykket", + "softfail": "Myk feil", + "neutral": "Nøytral", + "permerror": "Permanent feil", + "temperror": "Midlertidig feil", + "none": "Ingen" + } + }, + "details": { + "recipients_routing": "Mottakere og ruting", + "authentication_security": "Autentisering og sikkerhet", + "identifiers_threading": "Identifikatorer og tråder", + "mailing_list": "Postliste", + "message_properties": "Meldingsegenskaper", + "sent": "Sendt", + "received": "Mottatt", + "delivery_time": "Leveringstid", + "in_reply_to": "In-Reply-To", + "references": "Referanser", + "thread_id": "Tråd-ID", + "size": "Størrelse", + "mime_type": "MIME-type", + "attachments_summary": "{count} filer · {size}", + "list_id": "Liste-ID", + "list_help": "Listehjelp", + "list_post": "Listepost", + "list_unsubscribe": "Avslutt abonnementet", + "iprev": "Omvendt DNS", + "spam_status": "Søppelpoststatus", + "ai_verdict": "KI-vurdering", + "account": "Konto", + "no_subject": "(uten emne)" + }, + "headers": { + "routing": "Ruting", + "received": "Mottatt", + "message_id": "Meldings-ID", + "list_info": "Listeinformasjon" + }, + "tooltips": { + "reply": "Svar (r)", + "reply_all": "Svar alle (a)", + "forward": "Videresend (f)", + "archive": "Arkiver (e)", + "delete": "Slett (# eller Del)", + "star": "Stjernemerk (s)", + "unstar": "Fjern stjerne (s)", + "compose": "Skriv (c)", + "previous": "Forrige e-post", + "next": "Neste e-post", + "edit_draft": "Rediger utkast" + }, + "spam": { + "button_title": "Rapporter som søppelpost", + "not_spam_title": "Merk som ikke søppelpost", + "toast_success": "Flyttet til søppelpost", + "toast_batch": "{count} e-poster flyttet til søppelpost", + "toast_undo": "Angre", + "toast_not_spam_success": "Flyttet til innboksen", + "toast_not_spam_batch": "{count} e-poster flyttet til innboksen", + "error": "Kunne ikke rapportere søppelpost", + "error_not_spam": "Kunne ikke gjenopprette e-post" + }, + "unsubscribe_banner": { + "label": "Nyhetsbrev", + "button": "Avslutt abonnementet", + "confirm_title": "Vil du melde deg av denne avsenderens e-postliste?", + "confirm_button": "Bekreft", + "cancel": "Avbryt", + "success_http": "Avmeldingssiden er åpnet i en ny fane", + "success_mailto": "Forespørselen om avmelding er sendt", + "confirm_message_http": "Avmeldingssiden åpnes i en ny fane.", + "confirm_message_mailto": "En forespørsel om avmelding sendes til avsenderen.", + "error": "Kan ikke avslutte abonnementet", + "dismiss": "Lukk" + }, + "calendar_invitation": { + "loading": "Laster inn hendelsesdetaljer...", + "title": "Kalenderinvitasjon", + "published_title": "Publisert hendelse", + "response_title": "Svar på hendelse", + "update_title": "Hendelsesoppdatering", + "counter_title": "Motforslag", + "refresh_title": "Forespørsel om oppdatering", + "declined_counter_title": "Motforslag avvist", + "cancelled_title": "Hendelsen er avlyst", + "organizer": "Arrangør: {name}", + "organizer_label": "Arrangør", + "attendees": "{count, plural, one {# deltaker} other {# deltakere}}", + "accept": "Godta", + "maybe": "Kanskje", + "decline": "Avslå", + "add_to_calendar": "Legg til i kalenderen", + "added": "Lagt til i kalenderen", + "rsvp_sent": "Svar sendt", + "parse_error": "Kunne ikke lese invitasjonen", + "action_failed": "Kunne ikke fullføre handlingen i kalenderen.", + "no_calendar": "Kalenderen er ikke tilgjengelig", + "published_info": "Denne hendelsen ble delt til informasjon.", + "response_info": "Denne meldingen inneholder et svar fra en deltaker.", + "response_info_organizer": "Dette svaret fra deltakeren oppdaterer hendelsen din.", + "update_info": "Denne meldingen oppdaterer en eksisterende hendelse.", + "counter_info": "Denne meldingen foreslår endringer i en hendelse.", + "counter_info_organizer": "Denne deltakeren foreslo endringer i hendelsen din.", + "refresh_info": "Denne meldingen ber om de siste hendelsesdetaljene.", + "refresh_info_organizer": "En deltaker ba om de siste hendelsesdetaljene.", + "declined_counter_info": "Arrangøren avslo et motforslag.", + "authentication_failed_info": "Kontrollen av e-postautentisering for denne invitasjonen mislyktes. Vær forsiktig med handlingene i kalenderinvitasjonen.", + "authentication_missing_info": "Denne invitasjonen har ingen bekreftet e-postautentisering. Avklar detaljene med arrangøren hvis noe ser uvanlig ut.", + "sender_mismatch_info": "Denne invitasjonen ble sendt fra {sender}, mens arrangøren som er oppført i kalenderdataene er {organizer}.", + "sender_mismatch_unverified_info": "Denne invitasjonen ble sendt fra {sender}, mens arrangøren som er oppført i kalenderdataene er {organizer}, og meldingen kunne ikke bekreftes.", + "organizer_role": "Du er arrangør for denne hendelsen", + "your_response": "Ditt svar: {status}", + "response_needed": "Svar kreves", + "response_accepted": "Godtatt", + "response_tentative": "Kanskje", + "response_declined": "Avslått", + "response_delegated": "Delegert", + "actor_sent_info": "Sendt av {name}.", + "actor_response_info": "{name} svarte: {status}.", + "actor_counter_info": "{name} foreslo endringer i denne hendelsen.", + "actor_refresh_info": "{name} ba om de siste hendelsesdetaljene.", + "actor_declined_counter_info": "{name} avslo motforslaget.", + "actor_note": "Merk: {comment}", + "actor_unknown": "Noen", + "proposed_changes": "Foreslåtte endringer", + "change_title": "Tittel", + "change_time": "Tid", + "change_location": "Sted", + "change_description": "Beskrivelse", + "change_empty": "Ingen", + "change_from_to": "{before} → {after}", + "apply_proposal": "Bruk foreslåtte endringer", + "proposal_applied": "De foreslåtte endringene er tatt i bruk.", + "review_proposal": "Gjennomgå forslaget", + "review_request": "Gjennomgå forespørselen", + "view_in_calendar": "Se i kalenderen", + "select_calendar": "Velg kalender", + "already_in_calendar": "Allerede i kalenderen din", + "request_info": "Du er invitert til denne hendelsen. Svar for å gi arrangøren beskjed om du kan delta.", + "cancel_info": "Arrangøren har avlyst denne hendelsen.", + "event_updated": "Oppdatering #{sequence}", + "event_status_tentative": "Foreløpig", + "event_status_cancelled": "Avlyst", + "expand": "Vis detaljer", + "collapse": "Skjul detaljer" + }, + "send": "Send", + "more": "mer", + "scheduled_banner": "Planlagt sending: {date}", + "scheduled_send_created": "Sendingen er planlagt", + "sending_in": "Sendes om {seconds} s", + "undo_send_failed": "Kunne ikke angres. Meldingen kan allerede være sendt.", + "cancel_scheduled_send": "Avbryt sending", + "reschedule_send": "Planlegg på nytt", + "cancel_and_edit": "Avbryt og rediger", + "cancel_and_compose_again": "Avbryt og skriv meldingen på nytt", + "reschedule_prompt": "Skriv inn ny dato og klokkeslett, for eksempel 2026-05-04T15:30", + "scheduled_actions_only": "I denne visningen kan du bare bruke handlinger for planlagte meldinger", + "undo_send_scheduled": "Meldingen er planlagt for sending", + "undo_send": "Angre sending", + "contact_sidebar": { + "title": "Kontakt", + "close": "Lukk sidefeltet", + "action_email": "E-post", + "action_email_title": "Send e-post", + "action_copy": "Kopier", + "action_copy_title": "Kopier e-postadresse", + "action_edit_title": "Rediger kontakt", + "section_emails": "E-postadresser", + "section_phones": "Telefonnumre", + "section_organizations": "Organisasjoner", + "section_addresses": "Adresser", + "section_notes": "Notater", + "not_in_contacts": "Ikke i kontaktene dine", + "add_to_contacts": "Legg til som kontakt", + "copied": "Kopiert!", + "copy_failed": "Kunne ikke kopiere", + "view_contact": "Vis kontakt", + "view_details": "Vis detaljer" + }, + "send_now": "Send nå", + "verification_code": { + "copy": "Kopier kode {code}", + "copied": "Kode kopiert", + "copy_failed": "Kunne ikke kopiere koden" + }, + "mv_position": "{index} av {total}", + "plugin_panel_show": "Vis tilleggspanel", + "plugin_panel_hide": "Skjul tilleggspanel" + }, + "email_composer": { + "select_from_directory": "Velg fra katalog", + "minimize": "Minimer", + "maximize": "Fullskjerm", + "restore": "Gjenopprett", + "new_message": "Ny melding", + "signature_edit_hint": "Dobbeltklikk for å redigere signaturen", + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "reply_to": "Svar", + "reply_all_to": "Svar alle", + "forward_message": "Videresend", + "from": "Fra", + "to": "Til", + "cc": "CC", + "bcc": "BCC", + "subject": "Emne", + "body_placeholder": "Skriv meldingen din...", + "send": "Send", + "cancel": "Avbryt", + "attach": "Legg ved", + "attach_photos": "Bilder og videoer", + "attach_files": "Filer", + "attach_from_files": "Legg ved fra Filer", + "files_picker": { + "attach": "{count, plural, =0 {Legg ved} one {Legg ved 1 fil} other {Legg ved # filer}}", + "empty_folder": "Denne mappen er tom", + "load_failed": "Filene dine kunne ikke lastes inn" + }, + "discard": "Forkast", + "discard_draft_title": "Vil du forkaste utkastet?", + "discard_draft_confirm": "Du har ulagrede endringer. Vil du forkaste dette utkastet?", + "discard_draft_message": "Utkastet blir slettet.", + "saving": "Lagrer...", + "sending": "Sender...", + "add_link": "Legg til lenke", + "link_url_prompt": "Skriv inn nettadressen", + "draft_saved": "Utkastet er lagret", + "save_failed": "Kunne ikke lagre utkastet", + "to_placeholder": "E-postadresser til mottakere", + "cc_placeholder": "Kopimottakere", + "bcc_placeholder": "Blindkopimottakere", + "subject_placeholder": "Emne", + "cc_label": "Kopi:", + "bcc_label": "Blindkopi:", + "subject_label": "Emne:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 e-post} other {# e-poster}}", + "no_subject": "(Uten emne)", + "unknown_sender": "Ukjent", + "quote": { + "reply_header": "Den {date} skrev {sender}:", + "forward_header": "---------- Videresendt melding ----------", + "from": "Fra: {sender}", + "date": "Dato: {date}", + "subject": "Emne: {subject}", + "to": "Til: {recipients}" + }, + "remove_sub_address": "Fjern underadressen", + "from_override": { + "toggle_off": "Endre avsender", + "toggle_on": "Avbryt endringen", + "toggle_tooltip": "Rediger Fra-navnet og -adressen fritt. E-posten sendes fortsatt gjennom identiteten din.", + "name_label": "Navn i Fra-feltet", + "name_placeholder": "Navn", + "email_label": "E-postadresse i Fra-feltet", + "email_placeholder": "alias@example.com", + "envelope_notice": "Hvis serveren din ikke godtar denne adressen som konvoluttavsender, brukes {identity} i stedet, og mottakerne kan se den i Return-Path-overskriften." + }, + "save_as_template": "Lagre som mal", + "attach_from_device": "Last opp fra denne enheten", + "templates_menu": "Maler", + "insert_template": "Sett inn mal", + "options": "Alternativer", + "option_plain_text": "Ren tekst", + "option_plain_text_hint": "Send uten formatering", + "option_read_receipt": "Be om lesebekreftelse", + "option_read_receipt_hint": "Be mottakeren bekrefte at den er åpnet", + "option_dsn": "Leveringsvarsel", + "option_dsn_hint": "Få en rapport når serveren leverer den", + "option_require_tls": "Krev kryptert levering", + "option_require_tls_hint": "Lever bare over TLS, ellers avvis", + "option_priority": "Prioritet", + "option_priority_high": "Høy", + "option_priority_normal": "Normal", + "option_priority_low": "Lav", + "validation": { + "recipient_required": "Legg til minst én mottaker før du sender", + "body_required": "Skriv en melding eller legg ved en fil", + "invalid_recipients": "Ikke sendt - sjekk disse adressene: {addresses}", + "attachments_uploading": "Vedlegg lastes fortsatt opp – meldingen sendes så snart opplastingen er ferdig", + "attachment_upload_failed": "Ikke sendt – et vedlegg kunne ikke lastes opp. Fjern det og prøv igjen." + }, + "upload_progress": "Laster opp {uploaded} / {total}", + "upload_cancel": "Avbryt opplasting", + "upload_failed": "Kunne ikke laste opp {filename}", + "attachment_too_large": "«{name}» er større enn serveren tillater ({max} per fil)", + "attachments_total_too_large": "Vedleggene ville overskride serverens grense på {max} per melding", + "drop_files": "Slipp filer for å legge ved", + "show_less": "Vis færre", + "send_failed": "Kunne ikke sende e-post", + "send_timeout": "Serveren sluttet å svare. Sjekk Sendt-mappen før du sender på nytt – meldingen kan ha blitt sendt.", + "send_offline": "Du ser ut til å være frakoblet. Meldingen er her fortsatt, send den på nytt når du er tilkoblet igjen.", + "formatting": "Formatering", + "send_recipients_rejected": "Ikke sendt – serveren avviste alle mottakerne.", + "send_plugin_failed": "Ikke sendt - et tillegg som står for sendingen, mislyktes eller brukte for lang tid. Sjekk Sendt-mappen før du sender på nytt.", + "send_some_recipients_rejected": "Sendt, men ikke til disse mottakerne – serveren avviste dem.", + "continue_draft": "Fortsett å skrive", + "close_draft_title": "Lagre eller forkast utkast?", + "close_draft_message": "Du har ulagrede endringer. Vil du lagre dette som et utkast eller forkaste det?", + "smime_sign_on": "S/MIME-signering aktivert", + "smime_sign_off": "Aktiver S/MIME-signering", + "smime_encrypt_on": "S/MIME-kryptering aktivert", + "smime_encrypt_off": "Aktiver S/MIME-kryptering", + "smime_encrypt_unavailable": "S/MIME-kryptering er utilgjengelig – mottakersertifikater mangler", + "smime_unlock_title": "Lås opp S/MIME-nøkkel", + "smime_unlock_message": "Skriv inn passordfrasen for å låse opp S/MIME-signeringsnøkkelen.", + "smime_unlock_button": "Lås opp", + "smime_passphrase_placeholder": "Passordfrase", + "forgot_attachment": { + "title": "Har du glemt et vedlegg?", + "message": "Meldingen din nevner «{keyword}», men ingen fil er vedlagt. Sende likevel?", + "send_anyway": "Send likevel", + "back": "Tilbake til redigering" + }, + "empty_subject": { + "title": "Sende uten emne?", + "message": "Denne meldingen har ikke noe emne. Sende den likevel?", + "dont_ask_again": "Ikke spør igjen", + "send_anyway": "Send likevel", + "back": "Tilbake til redigering" + }, + "schedule_send": "Planlegg sending", + "schedule_send_description": "Velg når serveren skal sende denne meldingen.", + "schedule_preset": { + "tomorrow_morning": "I morgen tidlig", + "tomorrow_afternoon": "I morgen ettermiddag", + "monday_morning": "Mandag morgen" + }, + "schedule_send_required": "Velg dato og klokkeslett.", + "schedule_send_invalid": "Skriv inn en gyldig dato og klokkeslett.", + "schedule_send_future": "Velg et tidspunkt i fremtiden.", + "schedule_send_too_late": "Dette tidspunktet er senere enn serveren tillater.", + "schedule_send_unsupported": "Planlagt sending støttes ikke for denne kontoen.", + "schedule_send_cleanup_warning": "Sendingen ble planlagt, men oppryddingen av utkastet mislyktes.", + "send_delay_unsupported": "Sendeforsinkelse støttes ikke for denne kontoen.", + "send_delay_unsupported_confirm": "Denne kontoen støtter ikke sendeforsinkelse. Send umiddelbart i stedet?", + "recipient_edit_email": "Rediger e-postadresse", + "recipient_edit_name": "Rediger visningsnavn", + "recipient_email_placeholder": "E-postadresse", + "recipient_name_placeholder": "Visningsnavn", + "invalid_recipient": "Ikke en gyldig e-postadresse", + "autocomplete_search_server": "Søk på serveren", + "autocomplete_searching": "Søker...", + "mention_recipients": "Mottakere", + "mention_card_not_recipient": "Ikke i Til eller Kopi, så de får ikke denne meldingen.", + "mention_card_add": "Legg til i {field}", + "mention_card_remove": "Fjern omtale", + "toolbar": { + "bold": "Fet skrift", + "italic": "Kursiv", + "underline": "Understreking", + "strikethrough": "Gjennomstreking", + "text_color": "Tekstfarge", + "remove_color": "Fjern farge", + "heading_1": "Overskrift 1", + "heading_2": "Overskrift 2", + "bullet_list": "Punktliste", + "ordered_list": "Nummerert liste", + "quote": "Sitat", + "code_block": "Kodeblokk", + "align_left": "Venstrejuster", + "align_center": "Midtstill", + "align_right": "Høyrejuster", + "text_direction": "Tekstretning (RTL/LTR)", + "link": "Lenke", + "table": "Tabell", + "clear_formatting": "Fjern formatering", + "undo": "Angre", + "redo": "Gjenta", + "add_row_above": "Legg til rad over", + "add_row_below": "Legg til rad nedenfor", + "add_column_before": "Legg til kolonne før", + "add_column_after": "Legg til kolonne etter", + "delete_row": "Slett rad", + "delete_column": "Slett kolonne", + "toggle_header_row": "Slå overskriftsraden av eller på", + "delete_table": "Slett tabell", + "pick_size": "Velg størrelse", + "font_size": "Skriftstørrelse", + "font_size_default": "Standard", + "background_color": "Bakgrunnsfarge", + "remove_background_color": "Fjern bakgrunnsfarge" + }, + "send_filing_warning": "Meldingen ble sendt, men oppryddingen etterpå mislyktes. Et gammelt utkast kan bli liggende.", + "pop_out": "Åpne svaret i et eget vindu", + "send_guard_failed": "Ikke sendt - et tillegg som sjekker meldinger før sending, feilet eller svarte ikke." + }, + "confirm_dialog": { + "confirm": "Bekreft", + "cancel": "Avbryt" + }, + "common": { + "loading": "Laster inn...", + "error": "Feil", + "success": "Vellykket", + "cancel": "Avbryt", + "save": "Lagre", + "delete": "Slett", + "edit": "Rediger", + "close": "Lukk", + "search": "Søk", + "refresh": "Oppdater", + "settings": "Innstillinger", + "help": "Hjelp", + "logout": "Logg ut", + "yes": "Ja", + "no": "Nei", + "unknown": "Ukjent", + "app_title": "Nettpost", + "reconnecting": "Tilkoblingen ble brutt. Forsøker å koble til på nytt...", + "rate_limited_title": "Serveren begrenser midlertidig antall autentiseringsforsøk.", + "rate_limited_detail": "Bulwark har satt bakgrunnsforespørsler på pause for å unngå at kontoen blir låst. Prøver på nytt om {seconds} sekunder.", + "rate_limited_action_title": "Forespørselen er satt på pause for å unngå at kontoen blir låst.", + "rate_limited_action_detail": "Bulwark venter til sperreperioden på serveren er over før flere autentiserte forespørsler sendes. Prøv igjen om {seconds} sekunder.", + "pull_to_refresh": "Dra ned for å oppdatere", + "release_to_refresh": "Slipp for å oppdatere", + "refreshing": "Oppdaterer..." + }, + "notifications": { + "email_sent": "E-posten er sendt", + "email_deleted": "E-posten er slettet", + "email_archived": "E-posten er arkivert", + "emails_deleted": "{count, plural, one {# e-post slettet} other {# e-poster slettet}}", + "emails_archived": "{count, plural, one {# e-post arkivert} other {# e-poster arkivert}}", + "error_archiving": "Kunne ikke arkivere e-posten", + "error_updating": "Kunne ikke oppdatere e-posten", + "email_starred": "E-posten er stjernemerket", + "email_unstarred": "E-posten er ikke lenger stjernemerket", + "email_marked_read": "E-posten er merket som lest", + "email_marked_unread": "E-posten er merket som ulest", + "copied_to_clipboard": "Kopiert til utklippstavlen", + "source_copied": "Meldingskilden er kopiert til utklippstavlen", + "error_sending": "Kunne ikke sende e-post", + "error_deleting": "Kunne ikke slette e-post", + "error_loading": "Kunne ikke laste inn e-poster", + "new_email": "Ny e-post", + "new_email_from": "Fra {sender}", + "click_to_view": "Klikk for å se", + "email_moved": "E-posten er flyttet", + "emails_moved": "{count} e-poster er flyttet", + "moved_to_mailbox": "Flyttet til {mailbox}", + "move_failed": "Kunne ikke flytte e-posten", + "removed_from_folder": "Fjernet fra {mailbox}", + "remove_from_folder_failed": "Kunne ikke fjerne fra denne mappen", + "emails_copied": "{count} e-poster er kopiert", + "copy_failed": "Kunne ikke kopiere", + "move_error": "Kunne ikke flytte e-poster til den valgte mappen", + "email_tagged": "E-posten er merket med en etikett", + "emails_tagged": "{count} e-poster er merket med en etikett", + "tag_failed": "Kunne ikke legge til etiketten", + "identity_created": "Identiteten er opprettet", + "identity_updated": "Identiteten er oppdatert", + "identity_deleted": "Identiteten er slettet", + "identity_set_primary": "Hovedidentiteten er oppdatert", + "identity_create_failed": "Kunne ikke opprette identitet: {error}", + "identity_update_failed": "Kunne ikke oppdatere identitet: {error}", + "identity_delete_failed": "Kunne ikke slette identitet: {error}", + "identity_unauthorized": "Du er ikke autorisert til å sende fra denne e-postadressen", + "identity_not_found": "Finner ikke identiteten", + "vacation_saved": "Fraværsinnstillinger er lagret", + "vacation_save_failed": "Kunne ikke lagre fraværsinnstillingene", + "filters_saved": "Filtrene er lagret", + "filters_save_failed": "Kunne ikke lagre filtrene", + "filters_deleted": "Filterregelen er slettet", + "templates_exported": "Malene er eksportert", + "templates_imported": "{count, plural, one {# mal} other {# maler}} importert", + "templates_import_errors": "Noen maler kunne ikke importeres", + "templates_import_empty": "Fant ingen maler i filen", + "export_email_error": "Kunne ikke eksportere e-post", + "import_email_success": "E-posten er importert", + "import_email_error": "Kunne ikke importere e-post", + "rule_created": "Regel opprettet", + "rule_merged": "Lagt til i regelen «{name}»", + "rule_already_covered": "Dekkes allerede av regelen «{name}»", + "rule_apply_existing": "{count, plural, one {Bruk på # eksisterende melding} other {Bruk på # eksisterende meldinger}}", + "rule_applied": "{count, plural, =0 {Ingen meldinger måtte endres} one {Regelen brukt på # melding} other {Regelen brukt på # meldinger}}", + "rule_apply_failed": "Kunne ikke bruke regelen på eksisterende meldinger", + "rule_edit": "Rediger regel", + "rule_undo": "Angre", + "rule_undone": "Regelendringen er angret", + "rule_undo_failed": "Kunne ikke angre regelendringen", + "rule_undo_conflict": "Filtrene dine er endret siden. Angre endringen i filterinnstillingene.", + "rule_folder_failed": "Kunne ikke opprette mappen", + "vacation_filters_save_failed": "Fraværsinnstillinger er lagret, men videresending og svarmottakere kunne ikke lagres" + }, + "date": { + "today": "I dag", + "yesterday": "I går", + "this_week": "Denne uken", + "last_week": "Forrige uke", + "this_month": "Denne måneden", + "older": "Eldre", + "just_now": "Akkurat nå", + "minutes_ago": "{count} minutt siden", + "minutes_ago_plural": "{count} minutter siden", + "hours_ago": "{count} time siden", + "hours_ago_plural": "{count} timer siden", + "days_ago": "{count} dag siden", + "days_ago_plural": "{count} dager siden" + }, + "language": { + "title": "Språk", + "english": "engelsk", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Velg språk", + "switch_to_english": "Bytt til engelsk", + "switch_to_french": "Bytt til fransk", + "switch_to_japanese": "Bytt til japansk", + "switch_to_spanish": "Bytt til spansk", + "switch_to_italian": "Bytt til italiensk", + "switch_to_german": "Bytt til tysk", + "switch_to_dutch": "Bytt til nederlandsk", + "switch_to_portuguese": "Bytt til portugisisk", + "switch_to_russian": "Bytt til russisk", + "switching": "Bytter språk...", + "switch": "Bytt språk", + "current": "Gjeldende språk", + "polish": "Polski", + "switch_to_polish": "Bytt til polsk", + "en": "engelsk", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Bytt til farsi", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Bytt til mongolsk" + }, + "settings": { + "title": "Innstillinger", + "back_to_mail": "Tilbake til e-post", + "save_success": "Innstillingene er lagret", + "import_success": "Innstillingene er importert", + "import_error": "Kunne ikke importere innstillinger", + "reset_confirm": "Vil du tilbakestille alle innstillingene?", + "unsaved_changes": "Du har ulagrede endringer", + "discard_changes": "Vil du forkaste ulagrede endringer?", + "discard": "Forkast", + "keep_editing": "Fortsett å redigere", + "search_placeholder": "Søk i innstillinger", + "search_clear": "Tøm søket", + "search_no_results": "Ingen innstillinger passer med søket", + "tabs": { + "appearance": "Utseende", + "language": "Språk, region og tid", + "email": "E-posthåndtering", + "composer": "Skriving", + "privacy": "Personvern og sikkerhet", + "account": "Konto", + "identities": "Identiteter", + "vacation": "Fravær", + "advanced": "Avansert", + "calendar": "Kalender", + "filters": "Filtre", + "templates": "Maler", + "folders": "Mapper", + "keywords": "Etiketter", + "security": "Sikkerhet", + "files": "Filer", + "contacts": "Kontakter", + "encryption": "Kryptering", + "protocol_handlers": "Standardapper", + "sidebar_apps": "Apper i sidefeltet", + "notifications": "Varsler", + "layout": "Oppsett", + "themes": "Temaer", + "reading": "Lesing", + "composing": "Skriving", + "downloads": "Nedlastinger", + "content_senders": "Innhold og avsendere", + "about_data": "Om programmet og data", + "debug": "Feilsøking" + }, + "themes": { + "title": "Temaer", + "description": "Velg blant temaer som administratoren din har gjort tilgjengelige, og innebygde forhåndsinnstillinger.", + "locked": "Temavalget er låst av en administrator.", + "forced": "Temaet «{name}» er påtvunget av administratoren og kan ikke endres", + "admin_theme": "Administratortema", + "activated": "Tema aktivert", + "default_restored": "Standardtema gjenopprettet", + "default_name": "Standard", + "default_badge": "Standard", + "admin_enforced": "Påtvunget av administrator" + }, + "tab_groups": { + "general": "Generelt", + "account": "Konto og identitet", + "organization": "Organisering av e-post", + "apps": "Apper", + "system": "System", + "appearance": "Utseende", + "mail": "E-post", + "privacy": "Personvern og sikkerhet", + "advanced": "Avansert" + }, + "appearance": { + "message_list_order": { + "label": "Rekkefølge i meldingslisten", + "description": "Hvordan meldingene sorteres i listen. Sorteringen utføres av serveren og gjelder derfor hele mappen, ikke bare meldingene som er lastet inn.", + "preset": { + "chronological": "Kronologisk (nyeste først)", + "unread_first": "Uleste først", + "starred_first": "Stjernemerkede først", + "tagged_first": "Meldinger med etikett først", + "custom": "Egendefinert" + }, + "tag_label": "Etikett", + "advanced_show": "Avansert: Rediger sorteringsnivåer", + "advanced_hide": "Skjul avanserte innstillinger", + "criterion_label": "Sorteringskriterium på nivå {n}", + "direction_label": "Sorteringsretning på nivå {n}", + "criteria": { + "unread": "Ulest", + "starred": "Stjernemerket", + "tag": "Etikett", + "receivedAt": "Mottaksdato", + "sentAt": "Sendingsdato", + "from": "Avsender", + "subject": "Emne", + "size": "Størrelse" + }, + "direction": { + "unread_first": "Uleste først", + "read_first": "Leste først", + "starred_first": "Stjernemerkede først", + "unstarred_first": "Uten stjerne først", + "tagged_first": "Med etikett først", + "untagged_first": "Uten etikett først", + "newest_first": "Nyeste først", + "oldest_first": "Eldste først", + "a_to_z": "A → Å", + "z_to_a": "Å → A", + "largest_first": "Største først", + "smallest_first": "Minste først" + }, + "add_level": "Legg til nivå", + "remove_level": "Fjern nivå", + "move_up": "Flytt opp", + "move_down": "Flytt ned", + "tiebreak_note": "Opptil tre nivåer. Senere nivåer avgjør rekkefølgen ved likhet. Nyeste først brukes alltid til slutt.", + "scope": { + "label": "Bruk på alle mapper", + "description": "Når dette er slått av, gjelder rekkefølgen bare for innboksen. Andre mapper forblir kronologiske." + }, + "unsupported_note": "E-postserveren din støtter ikke sortering etter lesestatus, stjerner eller etiketter. Disse nivåene ignoreres, og listen forblir kronologisk." + }, + "title": "Utseende", + "description": "Tilpass utseendet til nettposten", + "theme": { + "label": "Tema", + "description": "Velg fargevalget du foretrekker", + "light": "Lyst", + "dark": "Mørkt", + "system": "System" + }, + "language": { + "label": "Språk", + "description": "Velg språket du foretrekker" + }, + "font_size": { + "label": "Skriftstørrelse", + "description": "Juster tekststørrelsen for bedre lesbarhet", + "small": "Liten", + "medium": "Middels", + "large": "Stor" + }, + "list_density": { + "label": "Tetthet", + "description": "Juster avstand og luft i brukergrensesnittet", + "extra_compact": "Ekstra kompakt", + "compact": "Kompakt", + "regular": "Vanlig", + "comfortable": "Luftig" + }, + "interface_layout": { + "label": "Grensesnittoppsett", + "description": "Hvor søk, navigasjon og verktøylinjer ligger", + "default": "Standard", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Aktiver animasjoner", + "description": "Vis jevne overganger og effekter" + }, + "toolbar_position": { + "label": "Plassering av verktøylinjen", + "description": "Velg hvor handlingsknapper for e-post skal vises (Svar, Arkiver, Slett osv.)", + "top": "Topp", + "below_subject": "Under emnet" + }, + "toolbar_labels": { + "label": "Vis tekst ved ikonene", + "description": "Vis tekst ved siden av ikonene på verktøylinjen. Deaktiver dette for å spare plass når du har blitt kjent med ikonene." + }, + "hide_account_switcher": { + "label": "Skjul kontobytter i sidefeltet", + "description": "Skjul kontovelgeren øverst i mappesidefeltet. Du kan fortsatt bytte konto fra navigasjonsfeltet nederst." + }, + "show_rail_account_list": { + "label": "Vis kontobilder i navigasjonsfeltet", + "description": "Vis et kontobilde for hver konto nederst i navigasjonsfeltet, slik at du raskt kan bytte konto. En utloggingsknapp vises nedenfor." + }, + "unified_mailbox": { + "label": "Samlet postkasse", + "description": "Vis kombinerte mapper (innboks, sendt osv.) for den aktive kontoen og dens delte mapper.", + "cross_account": { + "label": "På tvers av alle kontoer", + "description": "Slå sammen den samlede postkassen på tvers av alle tilkoblede kontoer, i stedet for å begrense den til den aktive kontoen." + }, + "include_group": { + "label": "Ta med gruppeinnbokser", + "description": "Ta også med delte innbokser og gruppeinnbokser i den samlede visningen." + } + }, + "all_mail": { + "folders_label": "Mapper i de samlede listene", + "folders_description": "Velg hvilke mapper fra denne kontoen som skal tas med i listene All e-post, Ulest og Stjernemerket.", + "account_hint": "Gjelder {account}.", + "no_folders": "Ingen mapper er tilgjengelige." + }, + "colorful_sidebar_icons": { + "label": "Fargede ikoner i sidefeltet", + "description": "Fargelegg mappe- og etikettikoner etter type (blått ikon for Innboks, rødt for Søppelpost, grønt for Sendt osv.). Deaktiver dette for et ensfarget sidefelt." + }, + "tint_list_rows": { + "label": "Fargelegg meldingsrader etter etikett", + "description": "Gi hver meldingsrad en svak bakgrunnsfarge basert på den første etiketten. Deaktiver dette for nøytrale rader; etikettmarkørene viser fortsatt fargen." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Vis totalt antall meldinger", + "description": "Vis totalt antall meldinger ved siden av mapper og etiketter, sammen med antall uleste. Deaktiver for å vise bare antall uleste." + }, + "favicon_unread_badge": { + "label": "Antall uleste på faneikonet", + "description": "Vis antall uleste meldinger i innboksen som et merke på nettleserfaneikonet, slik at ny e-post er synlig når fanen ikke er fokusert. Deaktiver for å beholde ikonet uten merke." + }, + "show_shared_mail_accounts": { + "label": "Vis delte e-postkontoer", + "description": "Vis mappene fra postbokser som er delt med deg under Delt i sidepanelet. Slå av for å skjule alle, eller skjul enkeltkontoer nedenfor. Kalendere og kontakter blir værende." + }, + "pro_interface": { + "label": "Pro-grensesnitt (eksperimentelt)", + "description": "Skrivebordsoppsett for erfarne brukere, med meldinger i flere faner og arbeidsflyter på tvers av kontoer. Standardgrensesnittet påvirkes ikke, og du kan bytte tilbake når som helst.", + "open_label": "Åpne Pro-grensesnittet", + "back_to_standard": "Tilbake til standardgrensesnittet" + }, + "cross_unread": { + "label": "Ulest", + "description": "Vis Ulest i den samlede postkassen, med ulest e-post fra de valgte mappene." + }, + "cross_starred": { + "label": "Stjernemerket", + "description": "Vis Stjernemerket i den samlede postkassen, med stjernemerket e-post fra de valgte mappene." + }, + "cross_all": { + "label": "All e-post", + "description": "Vis All e-post i den samlede postkassen, med all e-post fra de valgte mappene." + } + }, + "keywords": { + "title": "E-postetiketter", + "description": "Definer etiketter for å organisere e-postene dine. De lagres som JMAP-nøkkelord på serveren.", + "add_keyword": "Legg til etikett", + "label_field": "Visningsnavn", + "label_placeholder": "f.eks. arbeid, personlig, haster", + "id_field": "Etikett-ID", + "id_placeholder": "f.eks. jobb, personlig", + "color_field": "Farge", + "id_exists": "Denne etikett-ID-en finnes allerede", + "edit": "Rediger etikett", + "delete": "Slett etikett", + "save": "Lagre", + "add": "Legg til", + "cancel": "Avbryt", + "migrating": "Oppdaterer etiketten på eksisterende e-poster...", + "migration_error": "Kunne ikke oppdatere etiketten på eksisterende e-poster", + "filters_migration_error": "Noen filterregler bruker fortsatt den gamle etiketten – oppdater dem manuelt", + "nesting": { + "label": "Nestede etiketter", + "description": "Plasser etiketter under hverandre og vis dem som et hierarki i sidefeltet." + }, + "parent_field": "Overordnet etikett", + "no_parent": "Ingen overordnet", + "too_long": "Denne etikettstien er for lang (høyst {max} tegn)", + "has_children_locked": "Andre etiketter er nestet under denne, så navnet og den overordnede etiketten er låst. Flytt eller fjern dem først.", + "has_children_delete": "Fjern etikettene som er nestet under denne først", + "visibility_field": "Synlighet i sidefeltet", + "visibility": { + "show": "Vis", + "unread": "Vis hvis ulest", + "hide": "Skjul" + }, + "unrecognized": { + "title": "Gjenopprett etiketter fra serveren", + "description": "Navn og farger på etiketter lagres i denne nettleseren, men selve etikettene finnes på meldingene som JMAP-nøkkelord. Søk gjennom meldingene etter nøkkelord som ikke er definert her, og legg dem til igjen.", + "scan": "Søk etter etiketter", + "rescan": "Søk på nytt", + "scanning": "Søker... {scanned} av {total} meldinger", + "none": "Alle etikettene på meldingene dine er allerede definert her.", + "messages": "{count, plural, one {# melding} other {# meldinger}}", + "add_all": "Legg til alle {count}", + "partial": "Bare de {scanned} siste av {total} meldingene ble skannet. Etiketter som bare brukes på eldre e-poster, kan mangle.", + "error": "Kunne ikke søke gjennom meldingene etter etiketter." + } + }, + "notifications": { + "test_sound": "Test varslingslyden", + "sounds": { + "default": "Standard (pip)", + "cheerful": "Munter", + "involved": "Engasjert", + "swift": "Rask", + "relax": "Rolig" + }, + "push": { + "title": "Bakgrunnsvarsler", + "description": "Motta systemvarsler om ny e-post selv når denne siden ikke er åpen. Varslene leveres via Bulwarks push-relé, som aldri ser innholdet i e-posten.", + "relay_label": "Push-relé", + "relay_desc": "Reléet som driftes av Bulwark, brukes som standard. Endre dette bare hvis du drifter et eget relé.", + "relay_locked": "Angitt av administrator", + "relay_locked_desc": "URL-en til push-reléet er angitt av administratoren og kan ikke endres.", + "status_active": "Aktiv på denne enheten", + "status_inactive": "Ikke aktivert på denne enheten", + "status_unsupported": "Denne nettleseren støtter ikke Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "Behandler...", + "enable": "Aktiver", + "reenable": "Registrer på nytt", + "disable": "Deaktiver", + "confirm_disable_title": "Vil du deaktivere bakgrunnsvarsler?", + "confirm_disable_message": "Denne enheten slutter å motta varsler når Bulwark ikke er åpent.", + "dismiss_aria": "Lukk forespørselen om varsler", + "ios_hint": "På iOS må du først installere nettstedet på startskjermen – Safari leverer bare Web Push til installerte PWA-er.", + "devices_title": "Registrerte enheter", + "devices_desc": "Alle enheter som er registrert for å motta bakgrunnsvarsler for denne kontoen. Hvis du tilbakekaller tilgangen for en enhet, slutter den umiddelbart å motta varsler.", + "devices_loading": "Laster inn registrerte enheter...", + "devices_empty": "Ingen enheter er registrert for bakgrunnsvarsler.", + "devices_error": "Kunne ikke laste inn registrerte enheter.", + "devices_refresh": "Oppdater", + "device_this": "Denne enheten", + "device_other": "Annen enhet", + "device_status_active": "Mottar varsler", + "device_status_inactive": "Har aldri mottatt et varsel", + "device_status_unknown": "Ukjent for dette reléet", + "device_expires": "Utløper {date}", + "revoke": "Tilbakekall", + "confirm_revoke_title": "Vil du tilbakekalle tilgangen for denne enheten?", + "confirm_revoke_message": "Enheten slutter umiddelbart å motta bakgrunnsvarsler. Hvis pushvarsler aktiveres på nytt på enheten, registreres den på nytt.", + "confirm_revoke_message_this": "Denne enheten slutter umiddelbart å motta bakgrunnsvarsler." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Varslingslyd", + "description": "Velg hvilken lyd som skal spilles av for varsler", + "choose": "Lyd", + "choose_desc": "Velg en varseltone og klikk på høyttalerikonet for å forhåndsvise den" + }, + "email": { + "title": "E-postvarsler", + "description": "Velg innstillinger for varsler om innkommende e-post", + "enabled": "E-postvarsler", + "enabled_desc": "Vis varsler når nye e-poster kommer", + "sound": "Varslingslyd", + "sound_desc": "Spill av et lydvarsel når nye e-poster kommer", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Kalendervarsler", + "description": "Velg innstillinger for varsler om kalenderhendelser", + "enabled": "Hendelsesvarsler", + "enabled_desc": "Vis varsler for kommende kalenderhendelser", + "sound": "Varslingslyd", + "sound_desc": "Spill av et lydvarsel for kalenderpåminnelser", + "invitation_parsing": "Les kalenderinvitasjoner i e-post", + "invitation_parsing_desc": "Finn kalenderinvitasjoner i e-postvedlegg og vis tilgjengelige kalenderhandlinger" + } + }, + "language_region": { + "title": "Språk, region og tid", + "description": "Språk, datoformat, tidsformat og andre regionale preferanser", + "language": { + "label": "Språk", + "description": "Velg språket du foretrekker", + "english": "engelsk", + "french": "Français" + }, + "date_format": { + "label": "Datoformat", + "description": "Hvordan datoer vises i e-postlisten", + "smart": "Automatisk", + "relative": "Relativ", + "full": "Alltid fullstendig dato", + "preview_today": "I dag:", + "preview_this_week": "Denne uken:", + "preview_older": "Eldre:" + }, + "date_locale": { + "label": "Regionalt datoformat", + "description": "Rekkefølgen i numeriske datoer (dag, måned og år)", + "auto": "Automatisk", + "iso": "ISO 8601", + "dmy": "Dag/måned/år", + "mdy": "Måned/dag/år" + }, + "time_format": { + "label": "Tidsformat", + "description": "Velg mellom 12- og 24-timersklokke", + "12h": "12-timersformat", + "24h": "24-timersformat" + }, + "first_day": { + "label": "Første dag i uken", + "description": "Start uken på søndag eller mandag", + "sunday": "søndag", + "monday": "mandag" + }, + "time_zone": { + "label": "Tidssone", + "description": "Vis klokkeslett for e-post og kalender i denne tidssonen i stedet for tidssonen som nettleseren oppgir", + "auto": "Automatisk ({zone})", + "preview_now": "Nå:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Tøm søket ved bytte av mappe", + "description": "Å klikke på en mappe (Innboks, Sendt …) tømmer det aktive søket og viser mappen. Når det er av, beholdes søket og kjøres på nytt i mappen du bytter til." + }, + "request_read_receipt": { + "label": "Be om lesebekreftelser som standard", + "description": "Be automatisk om lesebekreftelse når du skriver en ny melding." + }, + "read_receipt_response": { + "label": "Svar på forespørsler om lesebekreftelse", + "description": "Velg hva som skal skje når en innkommende melding ber om lesebekreftelse.", + "ask": "Spør hver gang", + "always": "Send alltid", + "never": "Send aldri" + }, + "title": "E-posthåndtering", + "description": "Velg hvordan e-post skal håndteres", + "mark_read": { + "label": "Merk som lest", + "description": "Velg når e-poster skal merkes som lest etter at de er åpnet", + "instant": "Øyeblikkelig", + "delay_3s": "Etter 3 sekunder", + "delay_5s": "Etter 5 sekunder", + "never": "Aldri" + }, + "delete_action": { + "label": "Handling ved sletting", + "description": "Velg hva som skal skje når du sletter en e-post", + "trash": "Flytt til papirkurven", + "trash_and_read": "Flytt til papirkurven og merk som lest", + "permanent": "Slett permanent", + "warning": "E-poster slettes permanent og kan ikke gjenopprettes. Handlingen kan ikke angres." + }, + "message_spacing": { + "label": "Luft rundt meldingen", + "description": "Luft rundt meldingsteksten i leseren", + "auto": "Automatisk (anbefalt)", + "always": "Legg alltid til luft", + "edge": "Kant til kant" + }, + "plain_text_font": { + "label": "Skrifttype for ren tekst", + "description": "Skrifttypen som brukes i e-poster med ren tekst", + "mono": "Fast tegnbredde", + "sans": "Normal (samme som programmet)" + }, + "archive_mode": { + "label": "Arkiver i", + "description": "Hvordan organisere e-poster ved arkivering", + "single": "En enkelt mappe", + "year": "En mappe per år", + "month": "En mappe per måned", + "reorganize": "Omorganiser eksisterende arkiv", + "reorganize_success": "{count, plural, =0 {Ingen e-poster å omorganisere} =1 {1 e-post omorganisert} other {# e-poster omorganisert}}", + "reorganize_error": "Kunne ikke omorganisere arkivet" + }, + "permanently_delete_junk": { + "label": "Slett søppelpost permanent", + "description": "Slett e-poster permanent fra søppelpostmappen i stedet for å flytte dem til papirkurven" + }, + "confirm_bulk_delete": { + "label": "Bekreft før sletting av flere meldinger", + "description": "Spør før flere valgte meldinger flyttes til papirkurven. Permanent sletting spør alltid." + }, + "mail_layout": { + "label": "E-postoppsett", + "description": "Velg mellom den klassiske delte leseruten, en fokusert leseflyt i Gmail-stil eller en bunnleserute i Zimbra-stil.", + "split": "Delt rute", + "split_description": "Hold meldingslisten og leseruten synlige side ved side.", + "focus": "Fokusert liste", + "focus_description": "Vis én linje per melding, og åpne meldingen i full bredde samtidig som mappesidefeltet forblir synlig.", + "horizontal": "Leseruten nederst", + "horizontal_description": "Vis meldingslisten øverst og åpne den valgte meldingen i en leserute under den." + }, + "list_columns": { + "label": "Vis listen som kolonner", + "description": "Vis enlinjesrader som en tabell med kolonnene Fra, Emne, Dato og Størrelse under en overskrift." + }, + "show_preview": { + "label": "Vis forhåndsvisningstekst", + "description": "Vis forhåndsvisning av e-post i listen", + "focus_description": "Vis innebygd forhåndsvisningstekst i den fokuserte meldingslisten på én linje" + }, + "show_verification_codes": { + "label": "Vis bekreftelseskoder", + "description": "Tilby koden fra innloggings- og bekreftelses-e-poster for kopiering med ett klikk, i listen i ett døgn og i meldingen" + }, + "disable_threading": { + "label": "Deaktiver samtalegruppering", + "description": "Vis e-poster som individuelle meldinger i stedet for gruppert etter samtale" + }, + "plain_text_mode": { + "label": "Kun ren tekst", + "description": "Deaktiver redigering av formatert tekst, og send all e-post som ren tekst, også svar og videresendinger" + }, + "auto_select_reply_identity": { + "label": "Svar fra mottatt adresse", + "description": "Når du svarer, bruker Bulwark adressen meldingen opprinnelig ble sendt til. Programmet ser først etter en samsvarende identitet. For meldinger til et oppsamlingsdomene brukes aliaset i Fra-feltet, mens sendingen går gjennom hovedidentiteten din." + }, + "reply_identity_match": { + "label": "Samsvar for mottaksadresse", + "description": "Hvilke mottaksadresser som regnes som dine. «Kun nøyaktig adresse» velger bare en av dine oppsatte identiteter; «samme domene» behandler i tillegg enhver annen adresse på identitetsdomenene dine som et catch-all-alias og skriver om Fra-headeren til den. Velg nøyaktig adresse hvis disse adressene er distribusjonslister.", + "exact": "Kun nøyaktig adresse", + "domain": "Enhver adresse på mine domener" + }, + "signature_position": { + "label": "Signaturposisjon", + "description": "Velg hvor signaturen skal settes inn i svar og videresendinger. Over den siterte teksten fungerer den som en naturlig avslutning på svaret. Under den siterte teksten holdes den opprinnelige meldingen samlet.", + "above_quote": "Før sitert tekst", + "below_quote": "Etter sitert tekst" + }, + "signature_separator": { + "label": "Signaturavgrensning", + "description": "Sett standardavgrensningen «-- » på en egen linje foran signaturen (RFC 3676). Slå av hvis meldingen skal gå rett over i signaturen." + }, + "sub_address_delimiter": { + "label": "Skilletegn for underadresser", + "description": "Tegnet som skiller brukernavnet fra en underadressekode. Bruk samme skilletegn som e-postserveren (f.eks. user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "Egendefinert...", + "custom_input_label": "Egendefinert skilletegn" + }, + "attachment_click_action": { + "label": "Handling ved klikk på vedlegg", + "description": "Velg om et klikk på et filvedlegg skal forhåndsvise det eller laste det ned umiddelbart", + "preview": "Forhåndsvis når det er mulig", + "download": "Last ned umiddelbart" + }, + "attachment_position": { + "label": "Plassering av vedlegg", + "description": "Velg hvor vedlegg skal vises i e-postoverskriften", + "beside-sender": "Ved siden av avsender", + "below-header": "Under overskriften" + }, + "emails_per_page": { + "10": "10 e-poster", + "25": "25 e-poster", + "50": "50 e-poster", + "100": "100 e-poster", + "label": "E-poster per side", + "description": "Antall e-poster som skal lastes inn samtidig" + }, + "always_light_mode": { + "label": "Vis alltid e-poster i lysmodus", + "description": "Vis e-postinnhold i lys modus selv når programmet bruker mørk modus. Dette unngår problemer ved konvertering til mørk modus." + }, + "external_content": { + "label": "Eksternt innhold", + "description": "Hvordan håndtere bilder og eksternt innhold", + "ask": "Spør alltid", + "block": "Blokker alltid", + "allow": "Tillat alltid" + }, + "trusted_senders": { + "label": "Pålitelige avsendere", + "description": "Administrer avsendere du alltid vil laste inn bilder fra", + "count_zero": "Ingen", + "count_one": "1 avsender", + "count_other": "{count} avsendere", + "modal_title": "Pålitelige avsendere", + "empty_title": "Ingen pålitelige avsendere ennå", + "empty_description": "Når du ser på en e-post med blokkerte bilder, klikker du «Stol alltid på denne avsenderen» for å legge til avsenderen her.", + "add_manually": "Legg til avsender manuelt", + "add_button": "Legg til", + "add_placeholder": "Skriv inn e-postadresse", + "search_placeholder": "Søk etter avsendere...", + "no_results": "Ingen avsendere passer med søket ditt", + "remove": "Fjern", + "close": "Lukk", + "invalid_email": "Skriv inn en gyldig e-postadresse", + "already_added": "Denne avsenderen står allerede på listen over pålitelige avsendere", + "save_error": "Kunne ikke lagre – se feilsøkingsloggen for kontakter for mer informasjon", + "use_address_book_label": "Synkroniser med adressebok", + "use_address_book_description": "Lagre pålitelige avsendere i en egen «Trusted Senders»-adressebok, slik at de synkroniseres på tvers av alle enhetene dine" + }, + "hover_actions": { + "label": "Hurtighandlinger når du peker", + "description": "Velg hvilke hurtighandlinger som skal vises når du holder musepekeren over en e-post i listen", + "delete": "Slett", + "star": "Legg til eller fjern stjerne", + "mark_read": "Merk som lest eller ulest", + "archive": "Arkiver", + "tag": "Etikett", + "spam": "Merk som søppelpost", + "not_spam": "Ikke søppelpost", + "none_selected": "Ingen handlinger er valgt", + "mode_label": "Visningsmodus", + "mode_inline": "På raden", + "mode_floating": "Flytende", + "corner_label": "Plassering av flytende handlinger", + "corner_top-left": "Øverst til venstre", + "corner_top-right": "Øverst til høyre", + "corner_bottom-left": "Nederst til venstre", + "corner_bottom-right": "Nederst til høyre" + }, + "default_mail_program": { + "label": "Standard e-postprogram", + "description": "Registrer {appName} som standard e-postprogram for mailto:-lenker", + "button": "Sett som standard", + "success": "Nettleseren ba deg angi programmet som standard", + "error": "Nettleseren din støtter ikke denne funksjonen" + }, + "attachment_reminder": { + "label": "Vedleggspåminnelse", + "description": "Advar før sending når meldingen nevner vedlegg, men ingen er vedlagt", + "keywords_label": "Ord som utløser påminnelsen", + "keywords_description": "Ord eller uttrykk som utløser påminnelsen når de finnes i meldingen", + "add_placeholder": "Legg til søkeord...", + "add": "Legg til", + "remove": "Fjern" + }, + "empty_subject_warning": { + "label": "Advarsel om tomt emne", + "description": "Be om bekreftelse før du sender en melding uten emne." + }, + "recipient_mentions": { + "label": "Nevn mottakere med @", + "description": "Når du skriver @ i meldingen, foreslås mottakerne, og fornavnet til den valgte settes inn. Enter eller Tab velger det uthevede navnet." + }, + "mention_adds_recipient": { + "label": "Kontakter i @-omtaler", + "description": "Når du skriver @, foreslås også kontaktene dine og personer fra katalogen. Velger du noen som ennå ikke er mottaker, legges de til i meldingen.", + "to": "Legg til i Til", + "cc": "Legg til i Kopi", + "off": "Bare foreslå mottakere" + }, + "hide_inline_image_attachments": { + "label": "Skjul innebygde bilder i vedleggslisten", + "description": "Bilder som er innebygd i meldingsteksten, er ikke oppført som separate vedlegg" + }, + "attachment_image_previews": { + "label": "Vis forhåndsvisninger av bilder i vedlegg", + "description": "Vis bildevedlegg som miniatyrer i stedet for vanlige filikoner" + }, + "send_delay": { + "label": "Angre sending (sendeforsinkelse)", + "description": "Forsink vanlige sendinger i et kort tidsrom på serveren.", + "off": "Av", + "seconds": "{seconds} sekunder", + "unsupported": "Denne kontoen støtter ikke forsinket sending. Innstillingen lagres likevel for andre kontoer." + }, + "return_to_list_after_action": { + "label": "Gå tilbake til listen etter sletting eller merking som ulest", + "description": "Gå tilbake til meldingslisten i stedet for å åpne neste melding etter at du har slettet den åpne meldingen eller merket den som ulest." + }, + "swipe_right_action": { + "label": "Handling ved sveip mot høyre (mobil)", + "description": "Handlingen som utføres når du sveiper en melding mot høyre i listen på en berøringsenhet." + }, + "swipe_left_action": { + "label": "Handling ved sveip mot venstre (mobil)", + "description": "Handlingen som utføres når du sveiper en melding mot venstre i listen på en berøringsenhet." + }, + "swipe_actions": { + "none": "Ingen", + "archive": "Arkiver", + "delete": "Slett", + "mark_read": "Veksle mellom lest og ulest", + "star": "Legg til eller fjern stjerne", + "spam": "Merk som søppelpost" + }, + "rtl_editing": { + "label": "Støtte for redigering fra høyre mot venstre", + "description": "Legger til en retningsknapp på verktøylinjen for å angi avsnitt fra venstre mot høyre eller høyre mot venstre" + } + }, + "composer": { + "title": "Skriving", + "description": "Velg innstillinger for skriving av e-post", + "autosave": { + "label": "Intervall for automatisk lagring", + "description": "Hvor ofte utkast skal lagres automatisk", + "30s": "Hvert 30. sekund", + "1m": "Hvert minutt", + "2m": "Hvert 2. minutt", + "5m": "Hvert 5. minutt" + }, + "send_confirmation": { + "label": "Bekreft før sending", + "description": "Be om bekreftelse før du sender e-post" + }, + "default_reply": { + "label": "Standardhandling for svar", + "description": "Handlingen som brukes når du klikker på «Svar»", + "reply": "Svar", + "reply_all": "Svar alle" + } + }, + "privacy": { + "title": "Personvern og sikkerhet", + "description": "Velg innstillinger for personvern og sikkerhet", + "external_images": { + "label": "Blokker eksterne bilder", + "description": "Forhindre sporing gjennom eksterne bilder" + }, + "session_timeout": { + "label": "Tidsavbrudd for økten", + "description": "Logg ut automatisk etter inaktivitet", + "never": "Aldri", + "30m": "30 minutter", + "1h": "1 time", + "4h": "4 timer" + }, + "clear_cache": { + "label": "Tøm hurtigbuffer", + "description": "Fjern data i hurtigbufferen og midlertidige filer", + "button": "Tøm hurtigbuffer", + "confirm": "Er du sikker på at du vil tømme hurtigbufferen?", + "success": "Hurtigbufferen er tømt" + } + }, + "account": { + "title": "Konto", + "description": "Se kontoinformasjonen din", + "name_label": "Visningsnavn", + "username_label": "Brukernavn", + "account_type_label": "Kontotype", + "auth_method_label": "Autentisering", + "auth_method_oauth": "Enkel pålogging (OAuth/OIDC)", + "auth_method_basic": "Passord", + "auth_method_token": "Tilgangstoken", + "demo_account": "Demokonto", + "email": { + "label": "E-postadresse", + "value": "{email}" + }, + "server": { + "label": "JMAP-server", + "value": "{server}" + }, + "storage": { + "label": "Lagringsbruk", + "used": "{used} av {total} brukt", + "percentage": "{percent}% brukt" + }, + "last_sync": { + "label": "Siste synkronisering", + "value": "{time}" + }, + "accounts": { + "title": "Innloggede kontoer", + "description": "Dra for å endre rekkefølgen på kontoene i kontomenyen", + "active": "Aktiv konto", + "default_badge": "Standardkonto", + "set_default": "Sett som standard", + "switch_to": "Bytt til denne kontoen", + "move_up": "Flytt opp", + "move_down": "Flytt ned", + "drag_handle": "Dra for å omorganisere", + "add": "Legg til konto" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Delt med meg", + "description": "Gruppekontoer og delte kontoer du kan administrere. Velg en konto for å redigere filtre, feriesvar, kalendere og kontakter.", + "shared_label": "Delt konto" + } + }, + "scoped": { + "back": "Tilbake til kontoen min", + "managing": "Innstillinger for: {name}" + }, + "security": { + "title": "Kontosikkerhet", + "description": "Administrer passordet ditt, tofaktorautentisering og sikkerhetsinnstillinger", + "detecting": "Kontrollerer hvilke funksjoner serveren støtter...", + "not_available": "Administrasjon av kontosikkerhet er ikke tilgjengelig på denne e-postserveren. Nødvendige tillatelser kan være deaktivert. Se dokumentasjonen for mer informasjon.", + "password": { + "title": "Endre passord", + "current": "Gjeldende passord", + "new": "Nytt passord", + "confirm": "Bekreft nytt passord", + "submit": "Endre passord", + "success": "Passordet ble endret", + "error_title": "Passordendring mislyktes", + "error_mismatch": "De nye passordene er ikke like", + "error_min_length": "Passordet må inneholde minst 8 tegn", + "error_generic": "Kunne ikke endre passord" + }, + "display_name": { + "label": "Visningsnavn", + "description": "Navnet ditt slik det vises på serveren", + "placeholder": "Skriv inn visningsnavnet ditt", + "save": "Lagre", + "success": "Visningsnavnet er oppdatert", + "error": "Kunne ikke oppdatere visningsnavnet" + }, + "totp": { + "section_title": "Tofaktorautentisering", + "label": "TOTP-autentisering", + "description": "Legg til et ekstra lag med sikkerhet med et tidsbasert engangspassord", + "active": "Aktivert", + "inactive": "Deaktivert", + "enabled": "Tofaktorautentisering aktivert", + "disabled": "Tofaktorautentisering deaktivert", + "enable_error": "Kunne ikke aktivere tofaktorautentisering", + "disable_error": "Kunne ikke deaktivere tofaktorautentisering", + "setup_instructions": "Kopier denne URL-en til autentiseringsappen din (Google Authenticator, Authy osv.):", + "verification_code": "Bekreftelseskode", + "confirm": "Bekreft", + "disable": "Deaktiver", + "disable_confirm_prompt": "Skriv inn passordet ditt for å deaktivere tofaktorautentisering.", + "password_required": "Passord må fylles ut", + "code_required": "Bekreftelseskode må fylles ut", + "code_invalid": "Ugyldig bekreftelseskode. Sjekk autentiseringsappen din og prøv igjen." + }, + "app_passwords": { + "title": "App-passord", + "description": "Lag passord for apper som ikke støtter tofaktorautentisering", + "add": "Legg til", + "create": "Opprett", + "cancel": "Avbryt", + "done": "Ferdig", + "generate": "Generer", + "name_label": "Appnavn", + "name_placeholder": "f.eks. Thunderbird, iPhone Mail", + "expires_label": "Utløper (valgfritt)", + "allowed_ips_label": "Tillatte IP-er (valgfritt)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Skill adressene med komma eller mellomrom. La feltet stå tomt for å tillate alle IP-adresser.", + "password_label": "Passord (la feltet stå tomt for å generere automatisk)", + "password_placeholder": "Genereres automatisk hvis feltet er tomt", + "copy_now_warning": "Kopier dette passordet nå – det vises ikke igjen.", + "added": "App-passordet er opprettet", + "removed": "App-passordet er fjernet", + "add_error": "Kunne ikke opprette app-passord", + "remove_error": "Kunne ikke fjerne app-passordet", + "none": "Ingen app-passord er opprettet" + }, + "api_keys": { + "title": "API-nøkler", + "description": "Lag API-nøkler for skript og integrasjoner som kommuniserer direkte med serveren", + "name_label": "Nøkkelnavn", + "name_placeholder": "f.eks. sikkerhetskopieringsskript, CI-løper", + "copy_now_warning": "Kopier denne API-nøkkelen nå – den vises ikke igjen.", + "added": "API-nøkkelen er opprettet", + "removed": "API-nøkkelen er fjernet", + "add_error": "Kunne ikke opprette API-nøkkel", + "remove_error": "Kunne ikke fjerne API-nøkkelen", + "none": "Ingen API-nøkler er opprettet" + }, + "encryption": { + "section_title": "Kryptering av lagrede data", + "label": "E-postkryptering", + "description": "Krypter lagrede e-poster på serveren for ekstra personvern", + "active": "{type}-kryptering er aktivert", + "inactive": "Deaktivert", + "enabled": "Kryptering av lagrede data er aktivert", + "disabled_success": "Kryptering av lagrede data er deaktivert", + "error": "Kunne ikke oppdatere krypteringsinnstillingene", + "algorithm_label": "Krypteringsalgoritme", + "encrypt_on_append": "Krypter nye e-poster ved opplasting", + "allow_spam_training": "Tillat opplæring av søppelpostfilteret før e-post krypteres", + "enable_encryption": "Aktiver kryptering", + "enable_tooltip": "Aktiver kryptering med denne nøkkelen", + "configure_title": "Aktiver kryptering med denne nøkkelen", + "enable_button": "Aktiver", + "disable_tooltip": "Deaktiver kryptering", + "enabled_success": "Kryptering av lagrede data er aktivert" + }, + "public_keys": { + "name_label": "Navn på offentlig nøkkel", + "name_placeholder": "f.eks. Min nøkkel", + "key_label": "Offentlig nøkkel (S/MIME eller ASCII-armert PGP)", + "key_placeholder": "Lim inn den offentlige nøkkelen din her", + "none": "Ingen offentlige nøkler er lagt til" + }, + "email_client": { + "title": "Oppsett av e-postklient", + "description": "Bruk denne påloggingsinformasjonen til å sette opp en e-postklient på datamaskinen eller mobilen (Thunderbird, Apple Mail, Outlook osv.)", + "jmap_username_label": "JMAP-brukernavn", + "copy": "Kopier", + "copied": "Kopiert", + "password_instructions": "Bruk JMAP-brukernavnet ovenfor sammen med et app-passord for å logge på e-postklienten. Opprett et app-passord i delen ovenfor hvis du ikke allerede har gjort det." + }, + "link_device": { + "title": "Koble til mobilappen", + "description": "Logg på Bulwark Mail-appen på telefonen uten å skrive inn passordet der. Telefonen får sin egen pålogging, så den forblir pålogget når du logger av her.", + "generate": "Vis QR-kode", + "regenerate": "Vis en ny kode", + "instructions": "Åpne Bulwark Mail-appen og trykk på «Scan a sign-in code» på påloggingsskjermen (eller «Add account» → «Scan a sign-in code» hvis du allerede er pålogget), og rett deretter kameraet hit.", + "expires_in": "Denne koden utløper om {seconds} sekunder. Den kan bare brukes én gang.", + "expired": "Denne koden er utløpt.", + "error": "Kunne ikke opprette en sammenkoblingskode. Prøv igjen.", + "password_prompt": "Skriv inn passordet ditt for å bekrefte at det er deg. Telefonen får sin egen pålogging; passordet ditt sendes ikke til telefonen.", + "show_password": "Vis passord", + "hide_password": "Skjul passord", + "continue": "Fortsett", + "error_wrong_password": "Feil passord. Prøv igjen.", + "totp_prompt": "Skriv inn koden fra autentiseringsappen din.", + "error_password_or_code": "Passordet eller koden er feil.", + "error_not_signed_in": "Økten din er utløpt. Logg på igjen for å koble til en enhet.", + "error_impersonation": "Enheter kan ikke kobles til mens du ser på en annen brukers postkasse.", + "error_too_many_attempts": "For mange forsøk. Vent litt og prøv igjen.", + "error_session_secret": "Tilkobling av enheter er ikke satt opp på denne serveren (SESSION_SECRET mangler). Kontakt administratoren din.", + "error_server_unreachable": "Fikk ikke kontakt med e-postserveren din. Prøv igjen.", + "error_pairing_unavailable": "E-postserveren din tillater ikke å koble til enheter på denne måten. Kontakt administratoren din.", + "qr_alt": "QR-kode for pålogging", + "link_label": "Påloggingslenke", + "copy_link": "Kopier påloggingslenke", + "copied": "Kopiert", + "copy_hint": "Ingen kamera? Kopier lenken og lim den inn i appen med «Paste sign-in link».", + "success_title": "Pålogget på telefonen", + "success_body": "Du kan lukke denne siden. Vis en ny kode for å koble til en annen enhet.", + "link_another": "Koble til en annen enhet", + "use_sso": "Bruk enkel pålogging i stedet", + "error_insecure_server": "Mobilappen kobler bare til e-postservere over https, og denne bruker http." + } + }, + "identities": { + "title": "Avsenderidentiteter", + "description": "Administrer e-postadresser du kan sende fra", + "identities_count": { + "label": "Dine identiteter", + "description": "E-postadresser som er satt opp for sending", + "count_zero": "Ingen identiteter", + "count_one": "1 identitet", + "count_other": "{count} identiteter" + }, + "manage": "Administrer identiteter", + "sub_addressing": { + "label": "Underadressering", + "description": "Bruk koder som user+tag@domain.com for å organisere innkommende e-post", + "learn_more": "Lær mer" + } + }, + "vacation": { + "title": "Fravær", + "description": "Håndter innkommende e-poster automatisk mens du er borte", + "loading": "Laster inn innstillinger for feriesvar...", + "not_supported": "E-postserveren din støtter ikke feriesvar.", + "fetch_error": "Kunne ikke laste inn innstillingene for feriesvar. Prøv igjen.", + "status": { + "label": "Svar automatisk", + "description": "Send et automatisk svar til folk som sender deg e-post", + "active": "Aktiv", + "inactive": "Inaktiv" + }, + "date_range": { + "start": "Startdato", + "start_description": "La feltet stå tomt hvis det ikke skal være noen startdato", + "end": "Sluttdato", + "end_description": "La feltet stå tomt hvis det ikke skal være noen sluttdato" + }, + "message": { + "subject_label": "Emne", + "subject_description": "Emnelinje for autosvaret", + "subject_placeholder": "Ikke på kontoret", + "body_label": "Meldingstekst", + "body_description": "Meldingsinnhold i ren tekst", + "body_placeholder": "Takk for e-posten. Jeg er ikke på kontoret for øyeblikket og svarer når jeg er tilbake.", + "html_label": "Formatert melding (HTML)", + "html_description": "Legg til en versjon med rik formatering, lenker og stil. Mottakere med e-postklienter som ikke kan vise den, får versjonen i ren tekst ovenfor.", + "html_placeholder": "Skriv et formatert fraværssvar..." + }, + "preview": { + "title": "Forhåndsvisning", + "show": "Vis forhåndsvisning", + "hide": "Skjul forhåndsvisning" + }, + "save": "Lagre endringer", + "saving": "Lagrer...", + "warnings": { + "end_before_start": "Sluttdatoen må være etter startdatoen", + "invalid_date": "Angi en gyldig start- og sluttdato med klokkeslett, eller la feltet stå tomt", + "start_in_past": "Startdatoen er i fortiden", + "empty_body": "Meldingsteksten er tom – mottakerne får et tomt svar", + "subject_too_long": "Emnet er for langt for serveren (maks. {max} byte).", + "body_too_long": "Meldingen er for lang for serveren (maks. {max} byte inkludert formatering).", + "forward_address": "Angi en gyldig adresse å videresende til", + "not_running": "Videresending eller svarmottakere er lagret, men er ikke aktive akkurat nå. Lagre for å slå dem på igjen." + }, + "forward": { + "title": "Videresending", + "description": "Videresender innkommende meldinger til en annen adresse, med eller uten feriesvar, i perioden ovenfor (uten sluttdato, til den slås av). Søppelpost videresendes ikke.", + "enabled_label": "Videresend meldinger", + "to_label": "Videresend til", + "to_placeholder": "email@example.com", + "keep_label": "Behold i innboksen", + "keep_description": "Ellers blir meldingen bare videresendt: Den blir ikke liggende i denne postkassen, og ingen filterregel kjøres på den." + }, + "audience": { + "label": "Svar til", + "description": "Interne avsendere: {domains}", + "all": "Alle avsendere", + "internal": "Bare interne avsendere", + "external": "Bare eksterne avsendere" + }, + "auto_reply": { + "title": "Automatisk svar" + } + }, + "folders": { + "role_memos": "Notater", + "title": "Mapper", + "description": "Administrer e-postmappene dine og tildel standardroller", + "folder_list": "Dine mapper", + "folder_list_description": "Klikk på et mappeikon for å tilpasse det", + "standard_roles": "Standardroller for mapper", + "standard_roles_description": "Velg hvilke mapper som skal ha standardroller som innboks, sendt, papirkurv osv.", + "role_inbox": "Innboks", + "role_drafts": "Utkast", + "role_sent": "Sendt", + "role_trash": "Papirkurv", + "role_junk": "Søppelpost", + "role_archive": "Arkiv", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Ingen", + "create_folder": "Opprett mappe", + "create_subfolder": "Opprett undermappe", + "reorder": "Dra for å endre rekkefølgen", + "reorder_error": "Kunne ikke endre rekkefølgen på mappene", + "move_error": "Kunne ikke flytte mappen", + "select_folder": "Velg {name}", + "select_hint": "Velg. Shift+klikk velger alle mapper frem til den siste du klikket på.", + "selected_count": "{count, plural, one {# mappe} other {# mapper}} valgt", + "move_to": "Flytt til...", + "move_to_top_level": "Øverste nivå", + "move": "Flytt", + "clear_selection": "Fjern valg", + "folders_moved": "{count, plural, one {# mappe flyttet} other {# mapper flyttet}}", + "move_partial_error": "{count, plural, one {# mappe} other {# mapper}} kunne ikke flyttes", + "subfolder_of": "I {name}", + "subfolder_name": "Navn på undermappe", + "new_folder_name": "Mappenavn", + "rename": "Gi nytt navn", + "change_icon": "Endre ikon", + "count_mode": "Teller for {name}", + "count_mode_default": "Teller: standard", + "count_mode_unread": "Bare uleste", + "count_mode_all": "Uleste og totalt", + "count_mode_none": "Ingen teller", + "delete": "Slett", + "confirm_delete": "Er du sikker på at du vil slette «{name}»? E-postene i denne mappen flyttes til papirkurven.", + "create": "Opprett", + "cancel": "Avbryt", + "no_folders": "Ingen egendefinerte mapper", + "cannot_delete_role": "Kan ikke slette en mappe med en standardrolle. Fjern rollen først.", + "folder_created": "Mappen er opprettet", + "folder_renamed": "Mappen har fått nytt navn", + "folder_deleted": "Mappen er slettet", + "role_updated": "Mapperollen er oppdatert", + "error_create": "Kunne ikke opprette mappe", + "error_rename": "Kunne ikke gi nytt navn til mappen", + "error_delete": "Kunne ikke slette mappen", + "error_delete_has_children": "Kan ikke slette mappen: den inneholder fortsatt undermapper. Slett eller flytt dem først.", + "error_delete_has_email": "Kan ikke slette mappen: den inneholder fortsatt e-poster. Flytt eller slett dem først.", + "error_role": "Kunne ikke oppdatere mapperollen" + }, + "advanced": { + "title": "Avansert", + "description": "Avanserte alternativer og utviklerinnstillinger", + "debug_mode": { + "label": "Feilsøkingsmodus", + "description": "Aktiver detaljert logging for feilsøking" + }, + "debug_categories": { + "description": "Velg hvilke kategorier som skal logges. Deaktiver kategorier du ikke trenger, for å redusere antallet meldinger i konsollen.", + "jmap": "JMAP-klient", + "jmap_description": "Postkasseoperasjoner, e-posthenting og JMAP-protokollforespørsler", + "calendar": "Kalender", + "calendar_description": "Kalenderhendelser, import og planleggingsmeldinger", + "tasks": "Oppgaver", + "tasks_description": "Oppretting, henting og oppdatering av kalenderoppgaver", + "auth": "Autentisering", + "auth_description": "Pålogging, TOTP, tokenutveksling og øktadministrasjon", + "filters": "Filtre", + "filters_description": "Sieve-filterregler og ferieskript", + "email": "E-postvisning", + "email_description": "Visning av e-post, TNEF-behandling og merking som lest", + "push": "Pushvarsler", + "push_description": "Oppsett og levering av pushvarsler", + "contacts": "Kontakter og adressebøker", + "contacts_description": "Kontaktsynkronisering, adressebokoperasjoner og pålitelige avsendere" + }, + "settings_sync": { + "label": "Synkronisering av innstillinger", + "description": "Synkroniser innstillingene dine på tvers av nettlesere og enheter" + }, + "sender_favicons": { + "label": "Nettstedsikoner for avsendere", + "description": "Vis nettstedsikoner som profilbilder for bedriftsavsendere", + "refresh_label": "Oppdater avsenderikoner", + "refresh_description": "Hent nettstedikonene på nytt, for eksempel når en avsender har byttet ikon", + "refresh_button": "Oppdater", + "refresh_done": "Avsenderikonene hentes på nytt" + }, + "show_avatars_in_junk": { + "label": "Vis profilbilder i søppelpostmappen", + "description": "Vis profilbilder og nettstedsikoner for avsendere i søppelpostmappen. Dette er deaktivert som standard for å unngå at svindelforsøk virker mer troverdige." + }, + "keyboard_shortcuts": { + "label": "Tastatursnarveier", + "description": "Se tilgjengelige tastatursnarveier", + "button": "Vis snarveier" + }, + "refresh_cache": { + "label": "Oppdater bufrede data", + "description": "Last inn kontakter, kalendere og mapper på nytt fra serveren. Kontoene og øktene beholdes, slik at du kan rette en utdatert eller feil visning uten å logge av.", + "button": "Oppdater" + }, + "reset_settings": { + "label": "Tilbakestill innstillinger", + "description": "Gjenopprett standardinnstillingene", + "button": "Tilbakestill til standard" + }, + "export_settings": { + "label": "Eksporter innstillinger", + "description": "Last ned innstillingene dine som JSON", + "button": "Eksporter" + }, + "import_settings": { + "label": "Importer innstillinger", + "description": "Last opp innstillinger fra en JSON-fil", + "button": "Importer" + }, + "about": { + "title": "Bulwark nettpost", + "source_code": "Kildekode" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Klikk på søppelpost og phishing før de når innboksen din. Slipp ekte e-post gjennom. Mister du alle tre skjoldene, faller innboksen.", + "kinds": { + "spam": "Søppelpost", + "phishing": "Phishing", + "legit": "Ekte e-post" + }, + "click_to_block": "Klikk for å blokkere", + "let_through": "Slipp gjennom", + "start": "Start", + "play_again": "Spill igjen", + "score": "Poeng", + "combo": "Rekkebonus", + "shields_left": "{count} av {total} skjold igjen", + "seconds": "{seconds} s", + "best": "Rekord: {score}", + "new_best": "Ny rekord", + "held": "Innboksen holdt stand", + "overrun": "Innboksen ble overkjørt", + "stats": { + "caught": "Blokkert", + "delivered": "Levert", + "mistakes": "Feil" + } + } + }, + "sidebar_apps": { + "title": "Apper i sidefeltet", + "description": "Administrer egendefinerte apper og lenker i sidefeltet", + "keep_loaded": "Hold apper åpne i bakgrunnen", + "keep_loaded_description": "La apper i sidefeltet fortsette å kjøre i bakgrunnen når du bytter mellom dem, slik at de ikke må lastes inn på nytt", + "managed_title": "Levert av administratoren din", + "managed_description": "Disse appene er satt opp for alle og kan ikke endres.", + "manage_title": "Egendefinerte apper", + "manage_description": "Legg til, rediger eller fjern egendefinerte apper fra sidefeltet" + }, + "contacts": { + "title": "Kontakter", + "description": "Importer og eksporter kontaktene dine", + "group_by_letter_label": "Grupper etter første bokstav", + "sort_by_last_name_label": "Sorter etter etternavn", + "sort_by_last_name_description": "Sorter kontaktlisten etter etternavn slik at familiemedlemmer står samlet", + "group_by_letter_description": "Vis alfabetiske overskrifter i kontaktlisten", + "import_label": "Importer kontakter", + "import_description": "Importer kontakter fra en vCard-fil (.vcf).", + "export_label": "Eksporter kontakter", + "export_description": "Eksporter alle kontakter som en vCard-fil (.vcf).", + "manage_title": "Adressebøker", + "manage_description": "Gi nytt navn til adressebøkene dine", + "no_address_books": "Ingen adressebøker funnet", + "categories_title": "Kategorier", + "categories_description": "Gi nytt navn til kontaktkategorier", + "no_categories": "Ingen kategorier funnet" + }, + "downloads": { + "title": "Nedlastinger", + "description": "Tilpass hvordan nedlastede e-poster og vedlegg navngis.", + "reset": "Gjenopprett standardinnstillinger", + "preview": "Forhåndsvisning:", + "email_template": { + "label": "Filnavn for e-post (.eml)", + "description": "Malen som brukes når du eksporterer en e-post eller drar den ut til filsystemet. Filtypen .eml legges til automatisk." + }, + "attachment_template": { + "label": "Filnavn for vedlegg", + "description": "Malen som brukes når du laster ned eller drar ut et vedlegg. Hvis du utelater «{filename}» og «{ext}», beholdes den opprinnelige filtypen." + }, + "bundle_template": { + "label": "ZIP-filnavn for flere e-poster", + "description": "Malen som brukes når du drar ut eller laster ned flere valgte e-poster som ett .zip-arkiv. Filtypen .zip legges til automatisk." + }, + "spaces": { + "label": "Mellomrom", + "description": "Erstatt mellomrom i det ferdige filnavnet med et annet tegn.", + "keep": "Behold mellomrom", + "underscore": "Erstatt med _", + "dash": "Erstatt med -" + }, + "lowercase": { + "label": "Små bokstaver", + "description": "Gjør hele filnavnet om til små bokstaver." + }, + "strip_diacritics": { + "label": "Fjern diakritiske tegn", + "description": "Konverter bokstaver med diakritiske tegn til tilsvarende ASCII-tegn (ä → a, é → e). Nyttig for verktøy som ikke håndterer Unicode-filnavn riktig." + }, + "collapse_separators": { + "label": "Slå sammen gjentatte skilletegn", + "description": "Erstatt sammenhengende mellomrom, understreker eller bindestreker med ett tegn." + }, + "after_export": { + "label": "Etter eksport", + "description": "Flytt om ønskelig e-posten etter at den er eksportert som .eml.", + "keep": "Behold i postkassen", + "archive": "Flytt til arkiv", + "trash": "Flytt til papirkurven" + } + }, + "filters": { + "title": "E-postfiltre", + "description": "Lag regler for automatisk å sortere, merke og administrere innkommende e-poster", + "add_rule": "Legg til regel", + "no_rules": "Ingen filterregler", + "no_rules_description": "Lag regler for automatisk å organisere innkommende e-poster", + "vacation_active": "Feriesvar er aktivt", + "vacation_active_description": "Autosvar er aktivert for innkommende meldinger", + "vacation_configure": "Sett opp", + "edit_rule": "Rediger regel", + "new_rule": "Ny regel", + "delete_rule": "Slett regel", + "delete_confirm": "Er du sikker på at du vil slette denne regelen?", + "enable": "Aktiver", + "disable": "Deaktiver", + "raw_editor": "Rediger rå Sieve-kode", + "raw_editor_warning": "Redigering av rå Sieve-kode kan gjøre at reglene ikke lenger kan redigeres visuelt. Endringer som gjøres her, overstyrer den visuelle regelbyggeren.", + "validate": "Valider", + "validation_success": "Skriptet er gyldig", + "validation_error": "Skriptet har feil", + "save": "Lagre regler", + "saving": "Lagrer...", + "saved": "Filtrene er lagret", + "save_failed": "Kunne ikke lagre filtrene", + "loading": "Laster inn filtre...", + "not_supported": "E-postserveren din støtter ikke e-postfiltre.", + "rule_name": "Regelnavn", + "rule_name_placeholder": "f.eks. sorter nyhetsbrev", + "match_all": "ALLE vilkår må samsvare", + "match_any": "Minst ETT vilkår må samsvare", + "conditions": "Vilkår", + "add_condition": "Legg til vilkår", + "actions": "Handlinger", + "add_action": "Legg til handling", + "stop_processing": "Ikke behandle flere regler", + "include_spam": "Flytt også meldinger som er merket som søppelpost", + "attachment_type_placeholder": "f.eks. pdf, doc, jpg", + "value_placeholder_multi": "Verdi (skill flere verdier med komma)", + "condition_fields": { + "attachment": "Vedlegg", + "from": "Fra", + "to": "Til", + "cc": "Kopi", + "subject": "Emne", + "header": "Egendefinert meldingshode", + "size": "Størrelse", + "body": "Meldingstekst", + "all": "Alle meldinger" + }, + "comparators": { + "has_any": "finnes", + "has_type": "av typen", + "contains": "inneholder", + "not_contains": "inneholder ikke", + "is": "er lik", + "not_is": "er ikke", + "starts_with": "starter med", + "ends_with": "slutter med", + "matches": "samsvarer med mønsteret", + "greater_than": "er større enn", + "less_than": "er mindre enn", + "address_is": "er adressen", + "domain_is": "har domenet" + }, + "action_types": { + "move": "Flytt til mappe", + "copy": "Kopier til mappe", + "forward": "Videresend til", + "mark_read": "Merk som lest", + "star": "Stjernemerk melding", + "add_label": "Legg til etikett", + "discard": "Forkast (slett uten varsel)", + "reject": "Avvis med melding", + "keep": "Behold i innboksen", + "stop": "Stopp behandlingen" + }, + "move_to_folder": "Velg mappe", + "copy_to_folder": "Velg mappe", + "forward_to": "Videresend til e-postadresse", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Behold en kopi", + "forward_limit": "Grense for videresendinger per melding på denne serveren: {count}. Flere videresendinger hoppes over.", + "forward_unavailable": "Denne serveren tillater ikke videresending. Videresendingshandlinger i filterregler har ingen effekt.", + "reject_message": "Avvisningsmelding", + "reject_placeholder": "Meldingen din er avvist", + "label_name": "Etikettnavn", + "label_placeholder": "Velg etikett", + "header_name": "Navn på meldingshode", + "header_placeholder": "f.eks. X-Mailing-List", + "size_bytes": "Størrelse i byte", + "size_placeholder": "f.eks. 1000000", + "system_managed": "Regel som styres av systemet", + "opaque_warning": "Dette skriptet ble redigert utenfor den visuelle regelbyggeren. Bare redigering av rå Sieve-kode er tilgjengelig.", + "open_sieve_editor": "Åpne redigereren for Sieve-kode", + "fetch_error": "Kunne ikke laste inn filtre", + "expanded_view": "Utvidet visning", + "expanded_view_description": "Vis filterregler med detaljerte blokker for vilkår og handlinger", + "if": "Hvis", + "then": "Gjør følgende", + "match_all_conditions": "alle samsvarer", + "match_any_condition": "minst ett samsvarer", + "and": "og", + "or": "eller", + "cancel": "Avbryt", + "confirm_delete": "Slett", + "rule_list": "Filterregler", + "drag_to_reorder": "Dra for å endre rekkefølgen", + "match_type": "Samsvar", + "reset_to_visual": "Gå tilbake til visuell redigering", + "reset_warning": "Dette forkaster det gjeldende skriptet og lar deg begynne på nytt.", + "confirm_reset": "Tilbakestill", + "validation_empty_name": "Regelnavn må fylles ut", + "validation_empty_conditions": "Minst ett vilkår må ha en verdi", + "validation_empty_actions": "Du må legge til minst én handling", + "templates_section": "Start med en mal", + "template_newsletters": "Flytt nyhetsbrev til mappe", + "template_receipts": "Arkiver kvitteringer automatisk", + "template_important": "Stjernemerk viktige e-poster", + "template_notifications": "Filtrer varsler", + "sieve_editor": { + "title": "Rediger Sieve-skript", + "warning": "Redigering av rå Sieve-kode kan gjøre at reglene ikke lenger kan redigeres visuelt. Endringer som gjøres her, overstyrer den visuelle regelbyggeren.", + "script_content": "Sieve-skript", + "valid": "Skriptet er gyldig", + "invalid": "Skriptet har feil", + "save_warning": "Lagring overskriver eventuelle visuelle regler. Dette kan ikke angres. Klikk på «Lagre» igjen for å bekrefte.", + "validating": "Validerer...", + "validate": "Valider", + "cancel": "Avbryt", + "save": "Lagre", + "confirm_save": "Bekreft lagring", + "validation_failed": "Kunne ikke validere skriptet" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# vilkår} other {# vilkår}}", + "actions_count": "{count, plural, one {# handling} other {# handlinger}}" + }, + "origin_external": "Ekstern", + "managed_by_tooltip": "Denne regelen administreres av {source}. Rediger den der, eller bruk redigereren for rå Sieve-kode.", + "quick_rule_names": { + "move_sender": "Flytt fra {sender} til {folder}", + "move_domain": "Flytt fra @{domain} til {folder}", + "move_list": "Flytt listen {list} til {folder}", + "mark_read": "Merk fra {sender} som lest", + "tag": "Merk fra {sender} med {tag}", + "block": "Blokker {sender}", + "custom": "Meldinger fra {sender}", + "custom_plain": "Ny regel", + "senders": "{count, plural, one {# avsender} other {# avsendere}}" + }, + "suggestions": "Forslag", + "suggest_subject": "Emne «{subject}»", + "suggest_to": "Til {address}", + "suggest_cc": "Kopi {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Bruk også på meldinger som allerede ligger i denne mappen", + "apply_existing_unsupported": "Bare regler som sjekker avsender, mottakere, emne eller hoder, og som flytter, kopierer, merker som lest, stjernemerker eller setter etikett, kan brukes på eksisterende meldinger.", + "period_title": "Datoperiode", + "period_start": "Startdato", + "period_end": "Sluttdato", + "validation_period_order": "Sluttdatoen må være etter startdatoen", + "period_toggle": "Bare aktiv i en periode", + "period_hint": "Gjelder meldinger som kommer i denne perioden. La et felt stå tomt for å holde den enden åpen.", + "validation_period_empty": "Angi en start- eller sluttdato for perioden", + "validation_period_invalid": "Angi en gyldig dato med klokkeslett for perioden, eller la feltet stå tomt", + "apply_existing_period": "En regel med periode virker bare på meldinger som kommer i perioden, og kan derfor ikke brukes på eksisterende meldinger.", + "period_status_scheduled": "planlagt", + "period_status_active": "aktiv", + "period_status_expired": "utløpt" + }, + "templates": { + "title": "E-postmaler", + "description": "Lag gjenbrukbare e-postmaler med variabler som plassholdere", + "add": "Ny mal", + "edit": "Rediger mal", + "name": "Malnavn", + "name_placeholder": "f.eks. oppfølgingsmelding", + "category": "Kategori", + "category_placeholder": "f.eks. jobb, personlig", + "subject": "Emne", + "subject_placeholder": "Emne i e-posten", + "body": "Meldingstekst", + "body_placeholder": "Innhold i e-posten...", + "recipients_placeholder": "email@example.com", + "identity": "Send som", + "default_identity": "Standardidentitet", + "favorite": "Favoritt", + "cancel": "Avbryt", + "create": "Opprett", + "update": "Oppdater", + "confirm_delete": "Slett", + "no_templates": "Ingen maler ennå", + "manage": "Administrer maler", + "count": "{count, plural, one {# mal} other {# maler}}", + "export_import": "Eksport og import", + "export_import_description": "Sikkerhetskopier malene dine eller overfør dem til en annen enhet", + "export": "Eksporter", + "import": "Importer", + "validation": { + "empty": "Malnavn må fylles ut", + "too_long": "Malnavnet kan inneholde høyst 200 tegn" + } + }, + "files": { + "display": { + "title": "Visning", + "description": "Velg hvordan filer og mapper skal vises" + }, + "default_view": { + "label": "Standardvisning", + "description": "Velg mellom rutenett og listeoppsett", + "list": "Liste", + "grid": "Rutenett" + }, + "default_sort": { + "label": "Standardsortering", + "description": "Velg standardsortering for filer", + "name": "Navn", + "size": "Størrelse", + "modified": "Endret" + }, + "sort_direction": { + "label": "Sorteringsretning", + "description": "Velg stigende eller synkende rekkefølge", + "ascending": "Stigende", + "descending": "Synkende" + }, + "icons": { + "title": "Ikoner", + "description": "Velg hvordan filikonene skal se ut" + }, + "show_icons": { + "label": "Vis filikoner", + "description": "Vis ikoner ved siden av filer og mapper" + }, + "colored_icons": { + "label": "Fargede ikoner", + "description": "Bruk fargede ikoner i stedet for ensfargede" + }, + "show_thumbnails": { + "label": "Vis miniatyrbilder", + "description": "Vis bildeforhåndsvisninger i stedet for ikoner for bildefiler" + }, + "behavior": { + "title": "Virkemåte", + "description": "Velg hvordan filutforskeren skal fungere" + }, + "show_hidden": { + "label": "Vis skjulte filer", + "description": "Vis filer og mapper som starter med en prikk" + }, + "preview": { + "label": "Forhåndsvisning" + } + } + }, + "errors": { + "page_error_title": "Noe gikk galt", + "page_error_description": "Det oppstod en uventet feil. Prøv igjen eller gå tilbake til innboksen.", + "sidebar_error": "Kan ikke laste inn postkasser", + "email_list_error": "Kan ikke laste inn e-poster", + "viewer_error_title": "Kan ikke vise e-post", + "viewer_error_description": "Det oppstod et problem med å vise denne e-posten. Den kan inneholde innhold som ikke støttes.", + "composer_error": "Kan ikke laste inn meldingsvinduet", + "settings_error_title": "Innstillinger utilgjengelige", + "settings_error_description": "Kan ikke laste inn innstillinger. Det kan hende at innstillingene dine ikke lagres.", + "try_again": "Prøv igjen", + "reload": "Last inn på nytt", + "reload_emails": "Last inn e-poster på nytt", + "reload_settings": "Last inn innstillinger på nytt", + "retry": "Prøv på nytt", + "go_home": "Gå til innboksen" + }, + "context_menu": { + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "mark_read": "Merk som lest", + "mark_unread": "Merk som ulest", + "star": "Stjernemerk", + "unstar": "Fjern stjerne", + "pin": "Fest", + "unpin": "Løsne", + "move_to": "Flytt til...", + "copy_to_account": "Kopier til konto...", + "remove_from_folder": "Fjern fra denne mappen", + "archive": "Arkiver", + "delete": "Slett", + "mark_as_spam": "Rapporter som søppelpost", + "not_spam": "Ikke søppelpost", + "tag": "Etikett", + "items_selected": "Valgt: {count}", + "forward_as_attachments": "Videresend som vedlegg", + "edit_draft": "Rediger utkast", + "cancel_scheduled_send": "Avbryt sending", + "reschedule_send": "Planlegg på nytt", + "cancel_and_edit": "Avbryt og rediger", + "cancel_and_compose_again": "Avbryt og skriv meldingen på nytt", + "rules": { + "title": "Regler", + "move_from": "Flytt alltid meldinger fra {sender}", + "move_from_domain": "Flytt alltid meldinger fra @{domain}", + "move_from_list": "Flytt alltid meldinger fra denne listen", + "mark_read_from": "Merk alltid meldinger fra {sender} som lest", + "tag_with": "Merk alltid med etiketten", + "block": "{count, plural, one {Blokker avsender} other {Blokker # avsendere}}", + "no_junk": "Denne kontoen har ingen søppelpostmappe", + "create": "Lag regel…", + "manage": "Administrer regler", + "senders_count": "{count, plural, one {# avsender} other {# avsendere}}", + "cross_account": "Velg meldinger fra én konto", + "opaque_hint": "Filtrene dine er redigert for hånd. Åpne filterinnstillingene" , + "loading": "Laster inn regler…", + "load_failed": "Kunne ikke laste inn reglene", + "none": "Ingen regler ennå", + "new_mail_only": "Bare for nye meldinger", + "running": "Kjører «{name}» på {folder}…" + }, + "find_emails_from": "Finn e-poster fra {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Merk mappen som lest", + "mark_folder_tree_read": "Merk mappe og undermapper som lest", + "mark_all_folders_read": "Merk alle mapper som lest", + "new_subfolder": "Ny undermappe...", + "new_folder": "Ny mappe...", + "rename": "Gi nytt navn...", + "share": "Del...", + "import_email": "Importer .eml eller .zip...", + "empty_folder": "Tøm mappe", + "empty_folder_generic": "Tøm mappe", + "delete_folder": "Slett mappe", + "refresh": "Oppdater", + "hide_shared_account": "Skjul fra sidepanelet", + "mute_notifications": "Demp varsler", + "unmute_notifications": "Slå på varsler", + "mute_account_notifications": "Demp varsler for denne kontoen", + "unmute_account_notifications": "Slå på varsler for denne kontoen", + "reset_folder_notifications": "Tilbakestill varsler for mapper", + "mark_all_confirm_title": "Merk alle mapper som lest", + "mark_all_confirm_message": "Vil du merke alle uleste meldinger på den personlige kontoen din som lest?", + "delete_confirm_title": "Slett mappe", + "delete_confirm_message": "Vil du slette mappen «{name}» permanent? Denne handlingen kan ikke angres.", + "prompt_new_subfolder": "Skriv inn et navn for den nye undermappen.", + "prompt_new_folder": "Skriv inn et navn for den nye mappen.", + "prompt_rename": "Skriv inn et nytt navn for denne mappen.", + "placeholder_folder_name": "Mappenavn", + "create": "Opprett", + "rename_confirm": "Gi nytt navn", + "toast_marked_read": "Mappen er merket som lest", + "toast_marked_read_count": "Merket {count, plural, one {1 melding} other {# meldinger}} som lest", + "toast_already_read": "Ingen uleste meldinger", + "toast_marked_all_read": "Alle mapper er merket som lest", + "toast_emptied": "Mappen er tømt", + "toast_folder_created": "Mappen er opprettet", + "toast_folder_renamed": "Mappen har fått nytt navn", + "toast_folder_deleted": "Mappen er slettet", + "toast_error_mark_read": "Kunne ikke merke som lest", + "toast_error_empty": "Kunne ikke tømme mappen", + "toast_error_create": "Kunne ikke opprette mappe", + "toast_error_rename": "Kunne ikke gi nytt navn til mappen", + "toast_error_delete": "Kunne ikke slette mappen", + "toast_error_delete_has_children": "Mappen har undermapper. Fjern dem først.", + "toast_error_delete_has_email": "Mappen er ikke tom. Tøm den først.", + "delete_with_contents_title": "Mappen er ikke tom", + "delete_with_contents_message": "Mappen «{name}» inneholder fortsatt {count, plural, one {1 melding} other {# meldinger}}. Slette mappen og alle meldingene i den? Dette kan ikke angres.", + "delete_with_contents_confirm": "Slett mappe og meldinger" + }, + "shortcuts": { + "title": "Tastatursnarveier", + "tip": "Trykk på ? når som helst for å vise denne hjelpen", + "sections": { + "navigation": "Navigasjon", + "actions": "E-posthandlinger", + "global": "Generelt", + "threads": "Tråder", + "composer": "Skriving" + }, + "navigation": { + "next_email": "Neste e-post", + "previous_email": "Forrige e-post", + "open_email": "Åpne e-post", + "close_email": "Lukk / fjern valget", + "go_to_folder": "Gå til mappe", + "next_unread": "Neste uleste e-post", + "no_more_unread": "Det er ingen flere uleste e-poster lenger ned i listen" + }, + "actions": { + "reply": "Svar", + "reply_all": "Svar alle", + "forward": "Videresend", + "star": "Slå stjernemerking av eller på", + "archive": "Arkiver", + "delete": "Slett", + "mark_unread": "Merk som ulest", + "mark_read": "Merk som lest", + "toggle_spam": "Rapporter som søppelpost / Ikke søppelpost" + }, + "global": { + "compose": "Skriv ny e-post", + "search": "Aktiver søkefeltet", + "help": "Vis tastatursnarveier", + "refresh": "Oppdater e-poster", + "select_all": "Velg alle", + "global_search": "Søk i alt", + "extend_selection": "Utvid markeringen ned / opp" + }, + "threads": { + "expand_collapse": "Utvid eller fold sammen tråden", + "select_mountain_view": "Velg samtalen" + }, + "composer": { + "send": "Send e-post", + "schedule_send": "Planlegg sending", + "template_picker": "Åpne malvelgeren" + } + }, + "threads": { + "messages_one": "{count} melding", + "messages_other": "{count} meldinger", + "messages_tooltip": "{count, plural, one {# melding i denne samtalen} other {# meldinger i denne samtalen}}", + "expand": "Utvid samtalen", + "collapse": "Fold sammen samtalen", + "loading": "Laster inn samtale...", + "mark_read": "Merk samtalen som lest", + "mark_unread": "Merk samtalen som ulest", + "archive": "Arkiver samtale", + "delete": "Slett samtalen", + "star": "Stjernemerk samtalen", + "unstar": "Fjern stjernemerking fra samtalen", + "toggle_thread": "Utvid eller fold sammen tråden" + }, + "identities": { + "modal_title": "Administrer avsenderidentiteter", + "create_new": "Opprett ny identitet", + "edit_identity": "Rediger identitet", + "delete_confirm": "Vil du slette denne identiteten? Dette kan ikke angres.", + "cannot_delete": "Denne identiteten kan ikke slettes", + "primary_identity": "Hovedidentitet", + "set_as_primary": "Angi som hovedidentitet", + "no_identities": "Ingen identiteter", + "display": { + "reply_to": "Svar til:", + "bcc": "BCC:", + "signature": "Signatur:", + "preview": "Forhåndsvisning:" + }, + "validation_errors": { + "invalid_emails": "Ugyldige e-postadresser: {emails}", + "unknown_error": "Ukjent feil" + }, + "form": { + "name_label": "Visningsnavn", + "name_placeholder": "f.eks. jobb, privat", + "name_required": "Navn må fylles ut", + "email_label": "E-postadresse", + "email_placeholder": "your.email@example.com", + "email_required": "E-postadresse må fylles ut", + "email_invalid": "Skriv inn en gyldig e-postadresse", + "email_immutable": "E-postadressen kan ikke endres etter at identiteten er opprettet", + "reply_to_label": "Svar til (valgfritt)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "Automatisk blindkopi (valgfritt)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Signatur i ren tekst", + "html_signature_label": "HTML-signatur", + "signature_byte_counter": "{bytes} / {max} byte", + "signature_byte_limit_reached": "Serverens grense er nådd", + "save": "Lagre identitet", + "cancel": "Avbryt", + "creating": "Oppretter...", + "updating": "Oppdaterer..." + }, + "sub_address": { + "button_tooltip": "Bruk underadresse", + "popover_title": "Legg til underadressekode", + "tag_input_placeholder": "Skriv inn kode (for eksempel shopping)", + "preview_label": "Forhåndsvisning:", + "recent_tags": "Nylige koder", + "suggested_tags": "Foreslåtte koder", + "use_address": "Bruk denne adressen", + "invalid_tag": "Koden kan bare inneholde bokstaver, tall og bindestreker", + "tag_too_long": "Koden kan inneholde opptil 30 tegn", + "help_text": "E-poster som sendes til user{delimiter}tag@domain.com, havner i innboksen din", + "validation": { + "empty": "Koden kan ikke være tom", + "too_long": "Koden kan inneholde opptil {max} tegn", + "invalid_chars": "Koden kan bare inneholde bokstaver, tall og bindestreker" + } + }, + "badge": { + "sent_via": "sendt via", + "sub_address_tag": "Sendt med underadresse: {tag}", + "identity_name": "Sendt med identitet: {name}", + "identity_short": "via {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Slett", + "delete_confirm_title": "Slett identitet" + }, + "templates": { + "picker_title": "Velg en mal", + "search_placeholder": "Søk etter maler...", + "section_favorites": "Favoritter", + "section_recent": "Nylige", + "section_uncategorized": "Annet", + "no_templates": "Ingen maler ennå", + "no_results": "Ingen maler funnet", + "fill_placeholders": "Fyll inn verdier for plassholdere", + "enter_value": "Skriv inn en verdi...", + "preview": "Forhåndsvisning", + "insert_with_values": "Sett inn ferdig utfylt", + "insert_raw": "Sett inn uten å fylle ut", + "copy_suffix": "(kopi)", + "placeholder": "Variabel", + "placeholders": { + "recipient_name": "Mottakerens navn", + "company": "Selskapsnavn", + "date": "Gjeldende dato", + "day_of_week": "Ukedag", + "sender_name": "Navnet ditt" + } + }, + "contacts": { + "directory": { + "title": "Katalog", + "search_placeholder": "Søk etter navn eller e-postadresse", + "refresh": "Oppdater katalog", + "retry": "Prøv igjen", + "loading": "Laster katalog…", + "unavailable_title": "Ingen katalog", + "unavailable_hint": "E-postserveren din tilbyr ikke en katalog over kontoer.", + "forbidden_title": "Katalog ikke tilgjengelig", + "forbidden_hint": "E-postserveren din lar ikke denne kontoen bla gjennom katalogen. Spør administratoren.", + "error_title": "Kunne ikke laste katalogen", + "error_hint": "Sjekk tilkoblingen og prøv igjen.", + "empty_title": "Katalogen er tom", + "empty_hint": "E-postserveren din viser ingen kontoer med e-postadresse.", + "no_match": "Ingen i katalogen samsvarer med søket", + "refresh_failed": "Kunne ikke oppdatere katalogen. Viser sist lastede liste.", + "select_page": "Velg alle på denne siden", + "deselect_page": "Fjern valg på denne siden", + "select_entry": "Velg {name}", + "range": "{from}–{to} av {total}", + "page": "Side {page} av {count}", + "previous_page": "Forrige side", + "next_page": "Neste side", + "picker_title": "Velg fra katalog", + "cancel": "Avbryt", + "selected_count": "{count} valgt", + "add_to": "Legg til i Til", + "add_cc": "Legg til i Kopi", + "add_bcc": "Legg til i Blindkopi", + "select_hint": "Velg en person for å se detaljer", + "write_email": "Skriv e-post", + "add_to_contacts": "Legg til i kontakter", + "email": "E-post", + "read_only_note": "Oppføringene kommer fra e-postserveren din og kan ikke redigeres her.", + "type": { + "individual": "Person", + "group": "Gruppe", + "resource": "Ressurs", + "location": "Sted", + "other": "Annet" + } + }, + "title": "Kontakter", + "search_placeholder": "Søk etter kontakter...", + "create_new": "Ny kontakt", + "no_category": "Ingen kategori", + "rename_category": "Gi nytt navn til kategori", + "category_name_label": "Kategorinavn", + "category_renamed": "Kategorien har fått nytt navn", + "category_rename_failed": "Kunne ikke gi kategorien nytt navn", + "category_added": "Kontakt lagt til i {name}", + "category_added_plural": "{count} kontakter lagt til i {name}", + "empty_state": "Ingen kontakter ennå", + "empty_state_title": "Ingen kontakter ennå", + "empty_state_subtitle": "Opprett din første kontakt eller importer fra en vCard-fil", + "empty_search": "Ingen kontakter passer med søket ditt", + "empty_search_hint": "Prøv et annet søkeord", + "empty_filtered": "Ingen kontakter passer med filtrene dine", + "empty_filtered_hint": "Prøv å endre eller fjerne filtrene", + "clear_search": "Tøm søket", + "import_vcard": "Importer vCard", + "delete_confirm_title": "Slett kontakt", + "delete_confirm": "Er du sikker på at du vil slette denne kontakten?", + "local_mode": "Kontakter lagres lokalt (serveren støtter ikke JMAP-kontakter)", + "back_to_contacts": "Tilbake til kontaktene", + "back_to_email": "Tilbake til e-post", + "open_categories": "Åpne kategorier", + "tabs": { + "all": "Alle", + "groups": "Grupper" + }, + "shared": { + "title": "Delt" + }, + "address_books": { + "create": "Ny adressebok", + "created": "Adresseboken er opprettet", + "create_failed": "Kunne ikke opprette adresseboken", + "title": "Mine adressebøker", + "shared_prefix": "Delt: {name}", + "moved": "Kontakten er flyttet til {name}", + "moved_plural": "{count} kontakter er flyttet til {name}", + "move_failed": "Kunne ikke flytte kontakten", + "address_book": "Adressebok", + "rename": "Gi nytt navn til adresseboken", + "name_label": "Adresseboknavn", + "renamed": "Adresseboken har fått nytt navn", + "rename_failed": "Kunne ikke gi adresseboken nytt navn", + "default": "Standard", + "set_default": "Sett som standard", + "default_updated": "Standardadresseboken er oppdatert", + "set_default_failed": "Kunne ikke angi standardadressebok", + "manage": "Administrer adressebøker", + "share": "Del adressebok", + "new_contact_in_book": "Ny kontakt i denne adresseboken", + "delete": "Slett adressebok", + "confirm_delete": "Vil du slette «{name}»? Alle kontaktene i denne adresseboken fjernes.", + "deleted": "Adresseboken er slettet", + "delete_failed": "Kunne ikke slette adresseboken" + }, + "detail": { + "emails": "E-postadresser", + "phones": "Telefonnumre", + "organizations": "Organisasjoner", + "addresses": "Adresser", + "notes": "Notater", + "titles": "Titler og roller", + "online_services": "Nettjenester", + "anniversaries": "Merkedager", + "personal_info": "Personlig informasjon", + "languages": "Språk", + "categories": "Kategorier", + "related_contacts": "Tilknyttede kontakter", + "crypto_keys": "Krypteringsnøkler", + "cert_issuer": "Utsteder", + "cert_expires": "Utløper", + "cert_expired": "Utløpt", + "cert_fingerprint": "Fingeravtrykk", + "cert_algorithm": "Algoritme", + "import_to_smime": "Importer til S/MIME", + "cert_already_imported": "Allerede importert til S/MIME", + "cert_imported": "Sertifikatet er importert til S/MIME-lageret", + "cert_import_failed": "Kunne ikke importere sertifikatet", + "no_contact_selected": "Velg en kontakt for å se detaljer", + "compose_email": "Skriv en e-post", + "copy_email": "Kopier e-postadresse", + "copy_phone": "Kopier telefonnummer", + "copy_url": "Kopier nettadresse", + "copied": "Kopiert til utklippstavlen", + "copy_failed": "Kunne ikke kopiere til utklippstavlen", + "created": "Opprettet", + "updated": "Sist oppdatert", + "timezone": "Tidssone", + "anniversary_birth": "Bursdag", + "anniversary_death": "Dødsdag", + "anniversary_wedding": "Bryllupsdag", + "anniversary_other": "Annet", + "personal_expertise": "Ekspertise", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Annet", + "gender": "Kjønn", + "gender_masculine": "Mann", + "gender_feminine": "Kvinne", + "gender_other": "Annet", + "gender_none": "Ikke aktuelt", + "gender_unknown": "Ukjent", + "calendar": "Kalender", + "calendar_uri": "Kalender-URL", + "scheduling_uri": "Planleggings-URL", + "freebusy_uri": "Ledig/opptatt-URL", + "section_contact": "Kontaktinformasjon", + "section_work": "Arbeid", + "section_personal": "Personlig", + "email_default_label": "E-post", + "phone_default_label": "Telefon", + "address_default_label": "Adresse", + "online_service_default_label": "Nettjeneste", + "organization_label": "Organisasjon", + "title_label": "Tittel", + "role_label": "Rolle", + "language_label": "Språk", + "related_default_label": "Tilknyttet", + "more_actions": "Flere handlinger", + "age_years": "{count, plural, one {1 år gammel} other {# år gammel}}", + "years_since": "{count, plural, one {1 år} other {# år}}" + }, + "activity": { + "recent_emails": "Nylige e-poster", + "upcoming_events": "Kommende hendelser", + "no_emails": "Ingen nylige e-poster", + "no_events": "Ingen kommende hendelser", + "no_subject": "(Uten emne)", + "no_title": "(Ingen tittel)", + "load_failed": "Kunne ikke laste inn", + "unknown_sender": "Ukjent avsender", + "all_day": "Hele dagen" + }, + "form": { + "create_title": "Ny kontakt", + "edit_title": "Rediger kontakt", + "section_address_book": "Adressebok", + "select_address_book": "Velg en adressebok...", + "section_identity": "Navn og identitet", + "contact_type": "Kontakttype", + "type_person": "Person", + "type_organization": "Organisasjon", + "section_work": "Arbeid og organisasjon", + "prefix": "Tittel", + "prefix_placeholder": "dr., herr, fru", + "given_name": "Fornavn", + "middle_name": "Mellomnavn", + "surname": "Etternavn", + "suffix": "Navnetillegg", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Kallenavn", + "nickname_placeholder": "Kallenavn", + "email": "E-post", + "email_placeholder": "email@example.com", + "phone": "Telefon", + "phone_placeholder": "+1 234 567 890", + "phone_type": "Type", + "phone_voice": "Taleanrop", + "phone_cell": "Mobil", + "phone_fax": "Faks", + "phone_pager": "Personsøker", + "phone_video": "Video", + "phone_text": "SMS", + "organization": "Organisasjon", + "organization_placeholder": "Selskapsnavn", + "department": "Avdeling", + "department_placeholder": "Avdeling", + "job_title": "Stillingstittel", + "job_title_placeholder": "f.eks. programvareutvikler", + "role": "Rolle", + "role_placeholder": "f.eks. teamleder", + "addresses": "Adresser", + "add_address": "Legg til adresse", + "street": "Gateadresse", + "city": "By", + "region": "Fylke/region", + "postcode": "Postnummer", + "country": "Land", + "online_services": "Nettjenester", + "add_online_service": "Legg til nettjeneste", + "url_placeholder": "https://...", + "service_placeholder": "Tjeneste", + "anniversaries": "Merkedager", + "add_anniversary": "Legg til merkedag", + "anniversary_birth": "Bursdag", + "anniversary_wedding": "Bryllupsdag", + "anniversary_death": "Dødsdag", + "anniversary_other": "Annet", + "personal_info": "Personlig informasjon", + "add_personal_info": "Legg til oppføring", + "personal_info_placeholder": "f.eks. fotografi", + "personal_expertise": "Ekspertise", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Annet", + "level": "Nivå", + "level_high": "Høyt", + "level_medium": "Middels", + "level_low": "Lavt", + "categories": "Kategorier", + "categories_placeholder": "f.eks. familie, venner, kolleger", + "categories_hint": "Skriv for å søke eller legge til kategorier", + "category_add": "Legg til", + "note": "Notater", + "note_placeholder": "Legg til et notat...", + "gender": "Kjønn", + "gender_sex": "Biologisk kjønn", + "gender_male": "Mann", + "gender_female": "Kvinne", + "gender_other": "Annet", + "gender_none": "Ikke aktuelt", + "gender_unknown": "Ukjent", + "gender_identity": "Kjønnsidentitet", + "gender_identity_placeholder": "Kjønnsidentitet...", + "calendar": "Kalender", + "calendar_uri": "Kalender-URL", + "scheduling_uri": "Planleggings-URL", + "freebusy_uri": "Ledig/opptatt-URL", + "context_work": "Arbeid", + "context_private": "Privat", + "add_email": "Legg til e-postadresse", + "add_phone": "Legg til telefonnummer", + "save": "Lagre", + "cancel": "Avbryt", + "creating": "Oppretter...", + "updating": "Oppdaterer...", + "name_required": "Skriv inn et fornavn, etternavn eller organisasjon", + "email_invalid": "Skriv inn en gyldig e-postadresse", + "email_error_inline": "Ugyldig e-postformat", + "save_failed": "Kunne ikke lagre kontakten", + "delete": "Slett", + "upload_photo": "Last opp bilde", + "remove_photo": "Fjern bildet", + "photo_hint": "JPG eller PNG, opptil 10 MB. Bildet skaleres automatisk.", + "photo_too_large": "Bildet er for stort (maks. 10 MB)", + "photo_invalid": "Ugyldig bildefil", + "change_photo": "Endre" + }, + "groups": { + "create": "Ny gruppe", + "edit": "Rediger gruppe", + "empty": "Ingen grupper ennå", + "delete_confirm_title": "Slett gruppe", + "delete_confirm": "Er du sikker på at du vil slette denne gruppen?", + "name_label": "Gruppenavn", + "name_placeholder": "f.eks. team, familie", + "name_required": "Gruppenavn må fylles ut", + "save_failed": "Kunne ikke lagre gruppen", + "members_label": "Medlemmer", + "search_members": "Søk etter kontakter som skal legges til...", + "no_members": "Ingen medlemmer i denne gruppen", + "member_count": "{count, plural, =0 {Ingen medlemmer} one {1 medlem} other {# medlemmer}}", + "send_email": "Send e-post til gruppen", + "send_email_to": "Til", + "send_email_cc": "Kopi", + "send_email_bcc": "Blindkopi", + "no_member_emails": "Denne gruppen har ingen medlemmer med en e-postadresse." + }, + "import": { + "title": "Importer kontakter", + "drop_hint": "Klikk for å velge en vCard-fil", + "file_types": ".vcf- eller .vcard-filer", + "no_contacts": "Ingen kontakter funnet i filen", + "parse_error": "Kunne ikke analysere vCard-filen", + "found": "{count, plural, one {1 kontakt funnet} other {# kontakter funnet}}", + "duplicate": "Dobbeltoppføring", + "select_all": "Velg alle", + "deselect_all": "Fjern alle markeringer", + "selected": "{count, plural, one {1 valgt} other {# valgte}}", + "import_button": "Importer", + "importing": "Importerer...", + "success": "{count, plural, one {1 kontakt importert} other {# kontakter importert}}", + "failed": "Importen mislyktes", + "close": "Lukk", + "file_too_large": "Filen er for stor (maks. 5 MB)" + }, + "export": { + "title": "Eksporter kontakter", + "success": "{count, plural, one {1 kontakt eksportert} other {# kontakter eksportert}}" + }, + "bulk": { + "selected": "{count, plural, one {1 valgt} other {# valgte}}", + "select_all": "Velg alle", + "delete": "Slett", + "delete_confirm_title": "Slett kontakter", + "delete_confirm": "Vil du slette {count, plural, one {1 kontakt} other {# kontakter}}?", + "deleted": "{count, plural, one {1 kontakt slettet} other {# kontakter slettet}}", + "add_to_group": "Legg til i gruppe", + "choose_group": "Velg en gruppe", + "adding_contacts": "Legger til {count, plural, one {1 kontakt} other {# kontakter}}", + "added_to_group": "Kontaktene er lagt til i gruppen", + "export": "Eksporter", + "clear": "Fjern valg" + }, + "toast": { + "created": "Kontakten er opprettet", + "updated": "Kontakten er oppdatert", + "deleted": "Kontakten er slettet", + "error_create": "Kunne ikke opprette kontakt", + "error_update": "Kunne ikke oppdatere kontakten", + "error_delete": "Kunne ikke slette kontakten" + }, + "context_menu": { + "open": "Åpne", + "edit": "Rediger", + "send_email": "Send e-post", + "add_to_group": "Legg til i gruppe", + "export_vcard": "Eksporter som vCard", + "delete": "Slett", + "call": "Ring", + "duplicate": "Dupliser", + "print": "Skriv ut" + }, + "filters": { + "toggle": "Filtre", + "select": "Velg", + "clear": "Tøm", + "close": "Lukk", + "title": "Avanserte filtre", + "organization": "Selskap", + "organization_placeholder": "f.eks. Acme Corp", + "job_title": "Stillingstittel", + "job_title_placeholder": "f.eks. designer", + "location": "Sted", + "location_placeholder": "By eller land", + "email_domain": "E-postdomene", + "email_domain_placeholder": "eksempel.com", + "birthday_month": "Bursdag i", + "any_month": "Alle måneder", + "has_email": "Har e-postadresse", + "has_phone": "Har telefonnummer", + "has_photo": "Har bilde", + "sort_by": "Sorter etter", + "sort_first_name": "Fornavn", + "sort_last_name": "Etternavn" + } + }, + "calendar": { + "title": "Kalender", + "back_to_email": "Tilbake til e-post", + "back_to_month": "Tilbake til måneden", + "my_calendars": "Kalendere", + "birthday_calendar": "Bursdager", + "mini_calendar_change": "Klikk for å endre måned", + "views": { + "month": "Måned", + "week": "Uke", + "day": "Dag", + "agenda": "Agenda", + "today": "I dag", + "month_hint": "Måned (m)", + "week_hint": "Uke (w)", + "day_hint": "Dag (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Oppgaver", + "tasks_hint": "Oppgaver (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# tidligere hendelse} other {# tidligere hendelser}}", + "hidden_after": "{count, plural, one {# senere hendelse} other {# senere hendelser}}", + "show_all_hours": "Vis alle timer", + "show_display_hours": "Vis bare {start} – {end}", + "create": "Opprett hendelse", + "edit": "Rediger hendelse", + "delete": "Slett hendelse", + "details": "Hendelsesdetaljer", + "no_events": "Ingen hendelser", + "all_day": "Hele dagen", + "more": "+{count} til", + "show_more": "Vis flere", + "show_less": "Vis færre", + "no_title": "(Ingen tittel)", + "resize": "Endre størrelsen på hendelsen", + "duplicate": "Dupliser", + "today_header": "I dag", + "tomorrow_header": "I morgen", + "agenda_show_earlier": "Vis tidligere hendelser", + "agenda_loading": "Laster…", + "agenda_range_start": "Hendelser fra {date}", + "agenda_range_end": "Hendelser til {date}", + "export_ics": "Eksporter som .ics", + "copy_title": "Kopier tittel", + "copy_link": "Kopier møtelenken", + "new_event": "Ny hendelse", + "new_all_day_event": "Ny heldagshendelse", + "new_task": "Ny oppgave", + "go_to_today": "Gå til i dag" + }, + "detail": { + "add_note": "Legg til et notat...", + "save_note": "Lagre", + "note_saved": "Notatet er lagret", + "open_link": "Åpne lenken", + "copy_location": "Kopier sted", + "meeting_link": "Møtelenke", + "tentative": "Foreløpig", + "cancelled": "Avlyst", + "delete_confirm": "Vil du slette denne hendelsen?" + }, + "form": { + "title": "Tittel", + "description": "Beskrivelse", + "location": "Sted", + "meeting_link": "Møtelenke", + "start_date": "Startdato", + "end_date": "Sluttdato", + "start_time": "Starttid", + "end_time": "Sluttid", + "all_day_event": "Heldagshendelse", + "calendar_select": "Kalender", + "save": "Lagre", + "cancel": "Avbryt", + "delete_confirm": "Er du sikker på at du vil slette denne hendelsen?", + "color": "Farge" + }, + "participants": { + "availability": { + "title": "Tilgjengelighet", + "free": "Ledig", + "busy": "Opptatt", + "tentative": "Foreløpig", + "unknown": "Ikke en bruker på denne serveren", + "checking": "Sjekker…" + }, + "title": "Deltakere", + "add": "Legg til deltaker", + "organizer": "Arrangør", + "attendee": "Deltaker", + "accepted": "Godtatt", + "declined": "Avslått", + "tentative": "Kanskje", + "needs_action": "Har ikke svart", + "remove": "Fjern", + "edit": "Rediger", + "email_placeholder": "Legg til e-postadresse eller søk etter kontakter", + "send_invitations": "Send invitasjoner til deltakerne", + "status_summary": "{accepted} har godtatt, {pending} har ikke svart", + "invited_by": "Invitasjon fra {name}", + "respond_below": "Bruk knappene nedenfor til å svare", + "rsvp_label": "Ditt svar", + "cancel_notification": "Deltakerne blir varslet om avlysningen", + "you_organizer": "Du er arrangøren", + "you_attendee": "Du er deltaker", + "no_participants": "Ingen deltakere", + "count": "{count, plural, one {# deltaker} other {# deltakere}}" + }, + "recurrence": { + "title": "Gjentakelse", + "none": "Gjentas ikke", + "daily": "Daglig", + "weekly": "Ukentlig", + "monthly": "Månedlig", + "yearly": "Årlig", + "every_n_days": "Hver {count}. dag", + "every_n_weeks": "Hver {count}. uke", + "every_n_months": "Hver {count}. måned", + "until": "senest", + "occurrences": "{count} forekomster", + "custom": "Egendefinert...", + "edit_custom": "Rediger egendefinert gjentakelse", + "every_n_years": "Hvert {count}. år", + "on_days": "på {days}", + "on_day_n": "på dag {day}", + "on_the_nth": "på {nth} {day}", + "in_month": "i {month}", + "nth_1": "første", + "nth_2": "andre", + "nth_3": "tredje", + "nth_4": "fjerde", + "nth_last": "siste", + "editor_freq_day": "Dag", + "editor_freq_week": "Uke", + "editor_freq_month": "Måned", + "editor_freq_year": "År", + "editor_repeats_on": "Gjentas på", + "editor_every": "Hver", + "editor_unit_days": "dag(er)", + "editor_unit_weeks": "uke(r)", + "editor_unit_months": "måned(er)", + "editor_unit_years": "år", + "editor_on_day": "på dag", + "editor_on_the": "på", + "editor_in": "i", + "editor_ends": "Slutter", + "editor_never": "Aldri", + "editor_ends_on": "På", + "editor_ends_after": "Etter", + "editor_occurrences": "forekomster" + }, + "recurrence_scope": { + "edit_title": "Rediger gjentakende hendelse", + "delete_title": "Slett gjentakende hendelse", + "description": "Dette er en gjentakende hendelse. Hvilke hendelser vil du endre?", + "this_event": "Bare denne hendelsen", + "this_and_future": "Dette og følgende hendelser", + "all_events": "Alle hendelser", + "cancel": "Avbryt", + "save": "Lagre", + "delete": "Slett", + "rsvp_title": "Svar på gjentakende hendelse", + "rsvp_description": "Dette er en gjentakende hendelse. Hvilke hendelser gjelder svaret ditt for?", + "respond": "Svar" + }, + "alerts": { + "title": "Påminnelser", + "none": "Ingen påminnelser", + "at_time": "Når hendelsen starter", + "minutes_before": "{count, plural, one {# minutt før} other {# minutter før}}", + "hours_before": "{count, plural, one {# time før} other {# timer før}}", + "days_before": "{count, plural, one {# dag før} other {# dager før}}", + "weeks_before": "{count, plural, one {# uke før} other {# uker før}}", + "unit_minutes_before": "minutter før", + "unit_hours_before": "timer før", + "unit_days_before": "dager før", + "unit_weeks_before": "uker før", + "add": "Legg til påminnelse", + "remove": "Fjern påminnelsen", + "amount": "Antall", + "unit": "Tidsenhet", + "action": "Påminnelsestype", + "action_display": "Varsel", + "action_email": "E-post", + "by_email": "{label} via e-post", + "kept": "{count, plural, one {# påminnelse til som ikke kan redigeres her, beholdes} other {# påminnelser til som ikke kan redigeres her, beholdes}}", + "other": "{count, plural, one {# annen påminnelse} other {# andre påminnelser}}", + "calendar_defaults": "Kalenderens standardpåminnelser", + "customize": "Tilpass", + "use_defaults": "Bruk kalenderens standardpåminnelser" + }, + "settings": { + "limit_hours": "Begrens synlige timer", + "limit_hours_desc": "Vis bare disse timene i dag- og ukevisningen. Hendelser utenfor dem kan fortsatt nås via pilene øverst og nederst på hver dag.", + "visible_hours": "Synlige timer", + "visible_hours_start": "Start på synlige timer", + "visible_hours_end": "Slutt på synlige timer", + "hide_non_working_days": "Skjul fridager", + "hide_non_working_days_desc": "Utelat dagene du ikke jobber, fra ukevisningen. Hendelsene deres vises fortsatt i måneds-, dag- og agendavisningen.", + "working_days": "Arbeidsdager", + "title": "Kalenderinnstillinger", + "default_view": "Standardvisning", + "week_starts_on": "Uken starter på", + "time_format": "Tidsformat", + "default_calendar": "Standardkalender", + "default_reminder": "Standardpåminnelse", + "time_format_12h": "12-timersformat", + "time_format_24h": "24-timersformat", + "notifications_enabled": "Hendelsesvarsler", + "notifications_enabled_desc": "Vis varsler for kommende kalenderhendelser", + "notification_sound": "Varslingslyd", + "notification_sound_desc": "Spill av en lyd for kalendervarsler", + "invitation_parsing": "Les kalenderinvitasjoner i e-post", + "invitation_parsing_desc": "Finn kalenderinvitasjoner i e-postvedlegg og vis tilgjengelige kalenderhandlinger", + "show_time_in_month_view": "Vis klokkeslett i månedsvisning", + "show_time_in_month_view_desc": "Vis klokkeslettet for hendelser i månedsvisningen. På små skjermer vises hele hendelsesoppføringen i stedet for en prikk.", + "show_week_numbers": "Vis ukenummer", + "show_week_numbers_desc": "Vis ukenummer i minikalenderen", + "calendar_free_scroll": "Fri rulling", + "calendar_free_scroll_desc": "Rull fortløpende gjennom måneder, uker og dager i stedet for én periode om gangen", + "enable_tasks": "Aktiver oppgaver", + "enable_tasks_desc": "Vis en oppgavevisning i kalenderen for å administrere gjøremål", + "show_tasks_on_calendar": "Vis oppgaver i kalenderen", + "show_tasks_on_calendar_desc": "Vis oppgaver i måneds-, uke- og dagsvisningen", + "hover_preview": "Forhåndsvis hendelser når du peker", + "hover_preview_desc": "Vis et detaljvindu når du holder markøren over kalenderhendelser", + "hover_preview_instant": "Øyeblikkelig", + "hover_preview_delay_500ms": "Forsinkelse på 0,5 sekunder", + "hover_preview_delay_1s": "Forsinkelse på 1 sekund", + "hover_preview_delay_2s": "Forsinkelse på 2 sekunder", + "hover_preview_off": "Deaktivert", + "organizer_identity": "Organiser invitasjoner som", + "organizer_identity_desc": "Kalenderadressen som brukes som arrangør når du inviterer folk til hendelser.", + "organizer_identity_failed": "Kunne ikke endre arrangøradressen", + "show_birthday_calendar": "Fødselsdager fra kontakter", + "show_birthday_calendar_desc": "Vis en virtuell kalender med bursdager fra kontaktene dine" + }, + "days": { + "monday": "mandag", + "tuesday": "tirsdag", + "wednesday": "onsdag", + "thursday": "torsdag", + "friday": "fredag", + "saturday": "lørdag", + "sunday": "søndag", + "mon": "man", + "tue": "tir", + "wed": "ons", + "thu": "tor", + "fri": "fre", + "sat": "lør", + "sun": "søn" + }, + "months": { + "jan": "jan.", + "feb": "feb.", + "mar": "mars", + "apr": "apr.", + "may": "mai", + "jun": "juni", + "jul": "juli", + "aug": "aug.", + "sep": "sep.", + "oct": "okt.", + "nov": "nov.", + "dec": "des.", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Hendelsen er opprettet", + "event_updated": "Hendelsen er oppdatert", + "event_deleted": "Hendelsen er slettet", + "calendar_created": "Kalenderen er opprettet", + "calendar_deleted": "Kalenderen er slettet", + "event_move_error": "Kunne ikke flytte hendelsen", + "event_resize_error": "Kunne ikke endre størrelsen på hendelsen", + "alert_title": "Kommende hendelse", + "alert_now": "Starter nå", + "alert_in_minutes": "Om {count} min", + "invitation_sent": "Invitasjonene er sendt", + "invitations_denied_title": "Invitasjonene kan ikke sendes", + "invitations_denied": "Serveren nektet å sende invitasjonene: {reason}. Lagre hendelsen uten å sende dem?", + "save_without_invitations": "Lagre uten invitasjoner", + "rsvp_updated": "Svaret er oppdatert", + "rsvp_error": "Kunne ikke oppdatere svaret", + "event_duplicated": "Hendelsen er duplisert", + "event_error": "Kunne ikke lagre hendelsen", + "task_due": "Oppgaven forfaller", + "event_exported": "Hendelsen er eksportert", + "title_copied": "Tittelen er kopiert", + "link_copied": "Lenken er kopiert" + }, + "status": { + "loading_calendars": "Laster inn kalendere...", + "loading_events": "Laster inn hendelser..." + }, + "quick_create": { + "placeholder": "Tittel på ny hendelse", + "aria_label": "Opprett en hendelse raskt", + "add_title": "Legg til tittel", + "more_options": "Flere alternativer" + }, + "nav_prev": "Forrige", + "nav_next": "Neste", + "nav_open_menu": "Åpne menyen", + "import": { + "title": "Importer kalender", + "tab_file": "Fil", + "tab_url": "URL", + "select_file": "Velg .ics-fil", + "drop_file": "eller slipp filen her", + "supported_formats": "Støtter iCalendar (.ics)-filer", + "url_description": "Skriv inn URL-en til en ekstern iCalendar-strøm (.ics) for å importere hendelser.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Støtter iCalendar-URL-er (.ics)", + "fetch": "Hent", + "invalid_url": "Skriv inn en gyldig URL", + "url_fetch_failed": "Kunne ikke hente kalenderen fra nettadressen", + "parsing": "Leser kalenderfilen...", + "parsed_events": "Hendelser funnet: {count}", + "no_events": "Ingen hendelser funnet i filen", + "select_all": "Velg alle", + "deselect_all": "Fjern alle markeringer", + "target_calendar": "Importer til kalender", + "import_button": "Importer valgte hendelser", + "importing": "Importerer hendelser...", + "success": "Importerte hendelser: {count}", + "error": "Kunne ikke importere kalenderen", + "file_too_large": "Filen er større enn grensen på 10 MB", + "invalid_format": "Ugyldig kalenderfilformat" + }, + "webcal_action": { + "title": "Åpne kalenderlenken", + "description": "Hvordan vil du bruke «{name}»?", + "import_title": "Importer én gang", + "import_description": "Hent hendelsene nå og kopier dem inn i en av kalenderne dine.", + "subscribe_title": "Abonner", + "subscribe_description": "Kalenderen synkroniseres automatisk som en egen kalender.", + "cancel": "Avbryt" + }, + "management": { + "title": "Administrer kalendere", + "description": "Opprett, gi nytt navn og tilpass kalenderne dine. Høyreklikk på en kalender i sidefeltet for raskt å endre fargen.", + "name": "Navn", + "name_placeholder": "Kalendernavn", + "color": "Farge", + "change_color": "Endre farge", + "random_color": "Ny tilfeldig farge", + "add_calendar": "Legg til kalender", + "edit": "Rediger", + "delete": "Slett", + "save": "Lagre", + "create": "Opprett", + "cancel": "Avbryt", + "default": "Standard", + "confirm_delete": "Vil du slette «{name}»? Alle hendelsene i kalenderen fjernes.", + "confirm_clear": "Vil du fjerne alle hendelser fra «{name}»? Dette kan ikke angres.", + "set_default": "Sett som standard", + "default_updated": "Standardkalenderen er oppdatert", + "error_default": "Kunne ikke angi standardkalender", + "clear_events": "Fjern alle hendelser", + "events_cleared": "Hendelser fjernet: {count}", + "error_clear": "Kunne ikke fjerne kalenderhendelsene", + "calendar_created": "Kalenderen er opprettet", + "calendar_updated": "Kalenderen er oppdatert", + "calendar_deleted": "Kalenderen er slettet", + "color_updated": "Kalenderfargen er oppdatert", + "error_create": "Kunne ikke opprette kalender", + "error_update": "Kunne ikke oppdatere kalenderen", + "error_delete": "Kunne ikke slette kalenderen", + "caldav_url": "CalDAV-URL", + "copy_url": "Kopier CalDAV-URL", + "url_copied": "CalDAV-URL kopiert til utklippstavlen", + "share": "Del kalender", + "new_event_in_calendar": "Ny hendelse i denne kalenderen", + "shared_with": "Delt med {names}", + "shared_with_count": "{count, plural, one {Delt med 1 person} other {Delt med # personer}}", + "move_up": "Flytt opp", + "move_down": "Flytt ned", + "kind": "Innhold", + "kind_events": "Hendelser", + "kind_tasks": "Oppgaver", + "kind_both": "Hendelser og oppgaver", + "kind_hint": "Bestemmer hvilke apper som ser denne kalenderen når den synkroniseres via CalDAV. Dette kan ikke endres senere.", + "default_alerts": "Standardpåminnelser", + "default_alerts_title": "Standardpåminnelser for «{name}»", + "default_alerts_hint": "Nye hendelser i denne kalenderen får disse påminnelsene, med mindre de endres i hendelsen. Endringer her påvirker ikke eksisterende hendelser. Påminnelser på e-post sendes av e-postserveren.", + "default_alerts_timed": "Hendelser med klokkeslett", + "default_alerts_all_day": "Heldagshendelser", + "default_alerts_updated": "Standardpåminnelser oppdatert" + }, + "subscription": { + "title": "iCal-abonnement", + "section_title": "iCal-abonnementer", + "description": "Abonner på en ekstern iCalendar-strøm. Hendelser synkroniseres automatisk til en egen kalender. Du kan bruke URL-er med https:// og webcal://.", + "url_label": "Kalender-URL", + "url_placeholder": "https://example.com/calendar.ics eller webcal://...", + "name_label": "Kalendernavn", + "name_placeholder": "f.eks. Helligdager", + "color_label": "Farge", + "refresh_interval": "Oppdateringsintervall", + "interval_15": "Hvert 15. minutt", + "interval_30": "Hvert 30. minutt", + "interval_60": "Hver time", + "interval_360": "Hver 6. time", + "interval_1440": "Hver dag", + "subscribe": "Abonner", + "subscribing": "Abonnerer...", + "save": "Lagre endringer", + "saving": "Lagrer...", + "edit": "Rediger", + "edit_title": "Rediger abonnement", + "updated": "«{name}» er oppdatert", + "update_error": "Kunne ikke oppdatere abonnementet", + "invalid_url": "Skriv inn en gyldig URL", + "success": "Abonnementet på «{name}» er lagt til", + "error": "Kunne ikke legge til abonnement", + "refresh": "Oppdater nå", + "refresh_success": "Abonnementet er oppdatert", + "refresh_error": "Kunne ikke oppdatere abonnementet", + "unsubscribe": "Fjern abonnement", + "confirm_delete": "Vil du fjerne abonnementet på «{name}»? Kalenderen og alle hendelsene i den fjernes.", + "deleted": "Abonnementet er fjernet", + "delete_error": "Kunne ikke fjerne abonnementet", + "last_refreshed": "Sist oppdatert: {time}" + }, + "tasks": { + "label": "Oppgaver", + "no_tasks": "Ingen oppgaver", + "no_title": "(Ingen tittel)", + "mark_complete": "Merk som fullført", + "mark_incomplete": "Merk som ikke fullført", + "filter_all": "Alle", + "filter_pending": "Ventende", + "filter_completed": "Fullførte", + "filter_overdue": "Forfalte", + "show_completed": "Vis fullførte oppgaver", + "create": "Ny oppgave", + "edit": "Rediger oppgave", + "title_placeholder": "Oppgavetittel", + "description_placeholder": "Legg til en beskrivelse...", + "due_date": "Forfallsdato", + "include_time": "Ta med klokkeslett", + "priority": "Prioritet", + "priority_none": "Ingen", + "priority_high": "Høy", + "priority_medium": "Middels", + "priority_low": "Lav", + "progress": "Status", + "progress_needs_action": "Ikke startet", + "progress_in_process": "Pågår", + "progress_completed": "Fullført", + "progress_cancelled": "Avbrutt", + "calendar": "Kalender", + "alert": "Påminnelse", + "alert_none": "Ingen", + "alert_at_time": "På forfallstidspunktet", + "alert_5min": "5 minutter før", + "alert_15min": "15 minutter før", + "alert_30min": "30 minutter før", + "alert_1hr": "1 time før", + "alert_1day": "1 dag før", + "delete": "Slett", + "cancel": "Avbryt", + "save": "Lagre", + "quick_add_placeholder": "Legg til en oppgave...", + "due_today": "I dag", + "due_tomorrow": "I morgen", + "overdue": "Forfalt" + }, + "toolbar": { + "more": "Flere handlinger", + "select_view": "Bytt visning, nå {view}" + }, + "other_calendars": "Andre kalendere", + "calendar_options": "Alternativer for {name}" + }, + "calendar_event_notifications": { + "someone": "Noen", + "invited": "{name} har invitert deg til «{title}»", + "updated": "{name} har oppdatert «{title}»", + "cancelled": "{name} har avlyst «{title}»", + "untitled": "Hendelse uten tittel" + }, + "share_notifications": { + "someone": "Noen", + "shared": "{name} har delt {kind} «{object}» med deg", + "changed": "{name} har endret tilgangen din til {kind} «{object}»", + "revoked": "{name} har fjernet tilgangen din til {kind} «{object}»", + "object": { + "folder": "mappen", + "calendar": "kalenderen", + "address_book": "adresseboken", + "files": "filmappen" + } + }, + "sharing": { + "title": "Del «{name}»", + "description": "Gi andre brukere eller grupper på denne serveren tilgang. Endringer trer i kraft umiddelbart.", + "no_shares": "Ikke delt med noen ennå.", + "add_person": "Legg til person eller gruppe", + "search_placeholder": "Søk etter navn eller e-postadresse...", + "loading_principals": "Laster inn brukere...", + "no_principals": "Ingen andre brukere eller grupper funnet.", + "no_match": "Ingen treff.", + "remove": "Fjern tilgang", + "group": "Gruppe", + "share_added": "Tilgang er gitt", + "share_updated": "Tilgangen er oppdatert", + "share_removed": "Tilgangen er fjernet", + "share_failed": "Kunne ikke oppdatere deling", + "preset": { + "freeBusy": "Bare ledig/opptatt", + "read": "Kan lese", + "readWrite": "Kan lese og skrive", + "manager": "Kan administrere", + "custom": "Egendefinert" + } + }, + "advanced_search": { + "title": "Avansert søk", + "from": "Fra", + "from_placeholder": "Avsenderens e-postadresse eller navn", + "to": "Til", + "to_placeholder": "Mottakerens e-postadresse eller navn", + "subject": "Emne", + "subject_placeholder": "Emnet inneholder...", + "body": "Meldingstekst", + "body_placeholder": "Meldingsteksten inneholder...", + "folder": "Mappe", + "all_folders": "Alle mapper", + "all_folders_except_spam_trash": "Alle mapper unntatt søppelpost og papirkurv", + "has_attachment": "Vedlegg", + "date_after": "Etter", + "date_before": "Før", + "size_min": "Større enn (KB)", + "size_max": "Mindre enn (KB)", + "starred": "Stjernemerket", + "unread": "Ulest", + "read": "Lest", + "yes": "Ja", + "no": "Nei", + "clear": "Tøm", + "clear_all": "Fjern alle", + "filters_active": "{count} filter", + "filters_active_plural": "{count} filtre", + "toggle_filters": "Flere", + "search_hint": "Bruk avanserte filtre for mer presise søk", + "advanced_filters_tooltip": "Avanserte søkefiltre", + "results_found": "{count, plural, =0 {Ingen treff} one {# treff} other {# treff}}", + "results_found_more": "{count}+ treff", + "suggestions_recent": "Nylige søk", + "suggestions_people": "Personer", + "suggestions_from": "Søk etter e-post fra {address}", + "suggestions_to": "Søk etter e-post til {address}", + "suggestions_remove_recent": "Fjern fra nylige søk", + "has_words": "Inneholder ordene", + "size": "Størrelse", + "date_within": "Dato", + "search_in": "Søk i", + "has_attachment_check": "Har vedlegg", + "search": "Søk" + }, + "welcome": { + "title": "Velkommen til postkassen din", + "tip_compose": "Trykk c for å skrive en ny e-post", + "tip_shortcuts": "Trykk på ? for å se alle tastatursnarveier", + "tip_sidebar": "Finn kalender, kontakter og innstillinger i menyen i sidefeltet", + "tip_settings": "Tilpass nettposten under Innstillinger", + "tip_compose_touch": "Trykk på blyantknappen for å skrive en ny e-post", + "tip_sidebar_touch": "Kalender, Kontakter og Innstillinger finner du i linjen nederst", + "got_it": "Skjønner", + "settings": "Innstillinger", + "dismiss": "Lukk", + "start_tour": "Start omvisningen" + }, + "demo_welcome": { + "title": "Velkommen til Bulwark Mail", + "description": "Utforsk en fullverdig nettpostklient – rett i nettleseren. Alle data forblir på enheten din, så du kan gjerne prøve alt.", + "feature_email": "Les og skriv e-post", + "feature_organize": "Etiketter, stjerner og mapper", + "feature_shortcuts": "Tastatursnarveier", + "feature_privacy": "100 % privat demo", + "hint": "Klikk på en av e-postene til venstre for å komme i gang, eller start omvisningen nedenfor." + }, + "files": { + "office_edit": "Rediger dokument", + "office_loading": "Åpner dokumentredigering…", + "office_error": "Dokumentredigeringen kunne ikke åpnes.", + "office_close": "Lukk redigering", + "open_in_new_tab": "Åpne i ny fane", + "title": "Filer", + "search_placeholder": "Søk etter filer...", + "empty_state_title": "Ingen filer ennå", + "upload_files": "Last opp filer", + "new_folder": "Ny mappe", + "new_folder_name": "Mappenavn", + "rename": "Gi nytt navn", + "rename_title": "Gi nytt navn", + "new_name": "Nytt navn", + "name_forbidden_chars": "Serveren tillater ikke disse tegnene i navn: {chars}", + "name_reserved": "Dette navnet er reservert på serveren.", + "delete": "Slett", + "delete_confirm_title": "Slett fil eller mappe", + "delete_confirm_message": "Er du sikker på at du vil slette «{name}»? Dette kan ikke angres.", + "download": "Last ned", + "name": "Navn", + "size": "Størrelse", + "modified": "Endret", + "type": "Type", + "folder": "Mappe", + "file": "Fil", + "no_accounts": "Ingen tilkoblede kontoer.", + "uploading": "Laster opp...", + "upload_success": "{count, plural, one {1 fil lastet opp} other {# filer lastet opp}}", + "upload_canceled": "Opplastingen ble avbrutt", + "upload_canceled_partial": "{count, plural, one {1 fil ble} other {# filer ble}} lastet opp før du avbrøt", + "upload_error": "Kunne ikke laste opp filen", + "create_folder_success": "Mappen er opprettet", + "create_folder_error": "Kunne ikke opprette mappe", + "delete_success": "Elementet er slettet", + "delete_error": "Kunne ikke slette elementet", + "rename_success": "Navnet er endret", + "rename_error": "Kunne ikke endre navnet", + "download_error": "Kunne ikke laste ned", + "not_available": "Fillagring er ikke tilgjengelig på denne serveren", + "cancel": "Avbryt", + "create": "Opprett", + "save": "Lagre", + "no_results": "Ingen filer passer med søket ditt", + "batch_delete_confirm_message": "Er du sikker på at du vil slette {count, plural, one {1 element} other {# elementer}}? Dette kan ikke angres.", + "batch_delete_success": "{count, plural, one {1 element slettet} other {# elementer slettet}}", + "grid_view": "Rutenettvisning", + "list_view": "Listevisning", + "details": "Detaljer", + "path": "Sti", + "preview": "Forhåndsvisning", + "preview_error": "Kunne ikke laste inn forhåndsvisningen", + "cut": "Klipp ut", + "copy": "Kopier", + "paste": "Lim inn", + "move_success": "{count, plural, one {1 element flyttet} other {# elementer flyttet}}", + "move_error": "Kunne ikke flytte elementet", + "paste_success": "Limt inn", + "paste_error": "Kunne ikke lime inn", + "new_text_file": "Ny tekstfil", + "file_name": "Filnavn", + "retry": "Prøv på nytt", + "refresh": "Oppdater", + "duplicate": "Dupliser", + "duplicate_success": "Duplisert", + "duplicate_error": "Kunne ikke duplisere", + "share": "Del", + "shared": "Delt", + "shared_with_me": "Delt med meg", + "shared_by": "Delt av {name}", + "create_file_success": "Filen er opprettet", + "create_file_error": "Kunne ikke opprette filen", + "favorites": "Favoritter", + "recent": "Nylige", + "show_in_folder": "Vis i mappe", + "remove_from_recent": "Fjern fra nylige", + "upload_folder": "Last opp mappe", + "file_too_large": "«{name}» overskrider den maksimale filstørrelsen på {max}", + "undo": "Angre", + "undo_success": "Handlingen er angret", + "undo_error": "Kunne ikke angre", + "toolbar": "Filhandlinger", + "open_folder_tree": "Åpne mappetreet", + "file_list": "Filer og mapper", + "context_menu": "Handlinger", + "settings_ascending": "Stigende", + "settings_descending": "Synkende", + "disabled_title": "Administratoren har deaktivert filfunksjonen", + "disabled_description": "Store filopplastinger via WebDAV kan føre til ustabilitet i Stalwart/RocksDB, blant annet krasj på grunn av for lite minne og diskplass som ikke blir frigjort. Slettede filer kan ikke fjernes umiddelbart fra blob-lagringen. Denne funksjonen anbefales ikke for produksjonsmiljøer.", + "migration_title": "Oppdaterer filene dine...", + "migration_description": "Mapper og filer organiseres i riktig struktur. Dette skjer bare én gang.", + "new": "Ny", + "my_files": "Mine filer", + "folders": "Mapper", + "files_heading": "Filer", + "open": "Åpne", + "star": "Stjernemerk", + "unstar": "Fjern stjerne", + "expand_folder": "Utvid {name}", + "collapse_folder": "Skjul {name}", + "sidebar_hint": "Stjernemerk mapper og åpne filer for å finne dem her.", + "breadcrumbs": "Mappesti", + "show_path": "Vis skjulte mapper i stien", + "selection_actions": "Handlinger for valgte elementer", + "selected_count": "{count, plural, one {1 valgt} other {# valgt}}", + "clear_selection": "Fjern valg", + "select_all": "Velg alle", + "select_item": "Velg {name}", + "more_actions": "Flere handlinger for {name}", + "paste_here": "Lim inn i denne mappen", + "paste_count": "{count, plural, one {Lim inn 1 element} other {Lim inn # elementer}}", + "sort_by": "Sorter etter", + "view_mode": "Oppsett", + "clear_search": "Tøm søk", + "no_results_for": "Ingenting i denne mappen samsvarer med «{query}»", + "download_count": "{count, plural, one {Last ned 1 fil} other {Last ned # filer}}", + "delete_count": "{count, plural, one {Slett 1 element} other {Slett # elementer}}", + "close_details": "Lukk detaljer", + "file_kind": "{ext}-fil", + "folder_summary": "{folders, plural, =0 {Ingen mapper} one {1 mappe} other {# mapper}}, {files, plural, =0 {ingen filer} one {1 fil} other {# filer}}", + "details_hint": "Velg en fil eller mappe for å se detaljene.", + "sharing": "Deling", + "shared_with_count": "{count, plural, one {Delt med 1 person} other {Delt med # personer}}", + "only_you": "Bare deg", + "empty_folder_title": "Denne mappen er tom", + "empty_folder_description": "Slipp filer her, eller last dem opp fra enheten din.", + "empty_folder_read_only": "Du kan ikke legge til filer i denne mappen.", + "choose_account": "Velg en konto", + "drop_to_upload": "Slipp for å laste opp til {folder}", + "uploading_count": "Laster opp {current} av {total}", + "dismiss": "Lukk", + "zoom_in": "Zoom inn", + "zoom_out": "Zoom ut", + "zoom_reset": "Tilpass til skjermen", + "rotate": "Roter", + "close_preview": "Lukk" + }, + "smime": { + "your_certificates": "Dine sertifikater", + "your_certificates_desc": "Importer og administrer S/MIME-sertifikatene dine for signering og kryptering av e-post", + "recipient_certificates": "Mottakersertifikater", + "recipient_certificates_desc": "Offentlige sertifikater for kryptering av e-post til mottakere", + "identity_bindings": "Sertifikater for avsenderidentiteter", + "identity_bindings_desc": "Knytt S/MIME-sertifikater til avsenderidentitetene dine", + "defaults_title": "Standardinnstillinger", + "defaults_desc": "Velg standardinnstillinger for signering og kryptering", + "import_pkcs12": "Importer PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importer sertifikat", + "no_certificates": "Ingen sertifikater er importert ennå", + "no_recipient_certs": "Ingen mottakersertifikater", + "expires": "Utløper", + "expired": "Utløpt", + "bound_to": "Knyttet til", + "no_key_bound": "Ingen", + "lock": "Lås nøkkelen", + "unlock": "Lås opp nøkkelen", + "details": "Se detaljer", + "delete": "Slett", + "encrypt_by_default": "Krypter som standard", + "encrypt_by_default_desc": "Krypter e-post automatisk når alle mottakere har sertifikater", + "remember_unlocked": "Husk ulåste nøkler", + "remember_unlocked_desc": "Hold nøklene ulåste så lenge denne nettleserøkten varer", + "sign_default_for": "Signer som standard for", + "enter_p12_passphrase": "Skriv inn passordfrasen til PKCS#12-filen", + "p12_passphrase_desc": "Skriv inn passordfrasen som beskytter denne sertifikatfilen", + "enter_storage_passphrase": "Angi passordfrase for lagring", + "storage_passphrase_desc": "Velg en passordfrase som beskytter nøkkelen når den er lagret i nettleseren", + "next": "Neste", + "import": "Importer", + "unlock_key": "Lås opp nøkkelen", + "unlock_key_desc": "Skriv inn passordfrasen for lagring for å låse opp nøkkelen til signering eller dekryptering", + "passphrase_placeholder": "Skriv inn passordfrase", + "confirm_passphrase_placeholder": "Bekreft passordfrase", + "passphrase_mismatch": "Passordfrasene er ikke like", + "cancel": "Avbryt", + "processing": "Behandler...", + "close": "Lukk", + "certificate_details": "Sertifikatdetaljer", + "cert_subject": "Innehaver", + "cert_issuer": "Utsteder", + "cert_email": "E-post", + "cert_serial": "Serienummer", + "cert_validity": "Gyldighet", + "cert_fingerprint": "Fingeravtrykk (SHA-256)", + "cert_algorithm": "Algoritme", + "cert_capabilities": "Bruksområder", + "cert_source": "Kilde", + "cert_expired": "Dette sertifikatet har utløpt", + "cert_not_yet_valid": "Dette sertifikatet er ikke gyldig ennå", + "cap_sign": "Signering", + "cap_encrypt": "Kryptering", + "cap_none": "Ingen", + "show_passphrase": "Vis passordfrase", + "hide_passphrase": "Skjul passordfrase", + "sign_toggle": "Signer", + "encrypt_toggle": "Krypter", + "missing_recipient_certs": "Manglende sertifikater for: {emails}", + "missing_sender_cert": "Det er ikke knyttet noe sertifikat til denne identiteten", + "status_encrypted_ok": "Denne meldingen ble kryptert", + "status_encrypted_no_key": "Denne meldingen er kryptert, men det ble ikke funnet noen nøkkel som passer", + "status_encrypted_failed": "Kunne ikke dekryptere denne meldingen", + "status_signed_valid": "Signaturen er bekreftet", + "status_signed_invalid": "Kunne ikke bekrefte signaturen", + "status_signed_expired_cert": "Signert med et utløpt sertifikat", + "status_signed_self_signed": "Signaturen er gyldig, men sertifikatet er selvsignert og regnes ikke som pålitelig", + "status_signed_mismatch": "Signaturen er gyldig, men den som signerte, samsvarer ikke med avsenderen", + "status_unsupported": "S/MIME-formatet støttes ikke", + "auto_import_signer_certs": "Importer signeringssertifikater automatisk", + "auto_import_signer_certs_desc": "Lagre automatisk sertifikater fra e-poster med bekreftet signatur for fremtidig kryptering", + "export": "Eksporter", + "enter_export_passphrase": "Angi passordfrase for eksport", + "export_passphrase_desc": "Velg en passordfrase for å beskytte den eksporterte PKCS#12-filen", + "export_storage_desc": "Skriv inn passordfrasen for lagring for å dekryptere nøkkelen før eksport", + "incorrect_passphrase": "Feil passordfrase" + }, + "tour": { + "step_counter": "Trinn {current} av {total}", + "skip": "Hopp over omvisningen", + "back": "Tilbake", + "next": "Neste", + "finish": "Fullfør", + "take_a_tour": "Ta en omvisning i grensesnittet", + "restart_title": "Introduksjonsomvisning", + "restart_desc": "Vis den veiledede omvisningen i grensesnittet på nytt", + "restart_button": "Start omvisningen på nytt", + "show_on_new_devices_title": "Vis på nye enheter", + "show_on_new_devices_desc": "Vis velkomstbanneret og omvisningen første gang du logger på en ny enhet, selv om du allerede har fullført dem andre steder", + "sidebar_title": "Dine postkasser", + "sidebar_desc": "Dette er mappesidefeltet. Klikk på en postkasse for å se e-postene. Du kan opprette mapper, dra e-poster mellom dem og se antall uleste meldinger med et øyekast.", + "compose_title": "Skriv en e-post", + "compose_desc": "Klikk her for å skrive en ny e-post. Du kan legge til mottakere og vedlegg og bruke formatert tekst.", + "search_title": "Søk i e-posten din", + "search_desc": "Søk etter avsender, emne eller innhold. Klikk på filterikonet for avanserte alternativer som datoperiode, vedlegg og stjernemerkede meldinger.", + "email_list_title": "Din e-postliste", + "email_list_desc": "E-poster vises her. Klikk på én for å lese den til høyre. Bruk avmerkingsboksen til å velge flere, og flytt, slett eller merk dem samlet.", + "email_viewer_title": "Leseruten", + "email_viewer_desc": "Den valgte e-posten åpnes her. Svar, videresend, arkiver eller slett med knappene på verktøylinjen. Du kan også stjernemerke e-poster eller legge til fargeetiketter.", + "keywords_title": "Fargeetiketter", + "keywords_desc": "Organiser e-posten med fargekodede etiketter. Dra en e-post over på en etikett for å merke den, eller høyreklikk på e-posten for å velge etiketter.", + "calendar_title": "Kalender", + "calendar_desc": "Bytt til kalenderen for å administrere hendelsene dine. Opprett hendelser, angi påminnelser og velg dags-, ukes- eller månedsvisning.", + "contacts_title": "Kontakter", + "contacts_desc": "Her finner du adresseboken din. Importer kontakter, opprett grupper og klikk på en kontakt for å se alle detaljene.", + "settings_title": "Innstillinger", + "settings_desc": "Tilpass alt: tema, tetthet, signaturer, filtre, tastatursnarveier, standardinnstillinger for kalenderen og mer.", + "shortcuts_title": "Tastatursnarveier", + "shortcuts_desc": "Erfarne brukere vil like dette. Trykk på ? når som helst for å se alle tilgjengelige tastatursnarveier. Du kan navigere, skrive og administrere e-post uten å bruke musen.", + "compose_open_title": "Meldingsvinduet", + "compose_open_desc": "Dette er meldingsvinduet. Legg til mottakere, skriv meldingen, legg ved filer og bruk formatert tekst. Du kan også lagre utkast og bruke maler.", + "calendar_view_title": "Kalenderen din", + "calendar_view_desc": "Her er kalenderen din med eksempler på hendelser. Du kan bytte mellom dag, uke, måned og agendavisning ved å bruke verktøylinjen.", + "create_event_title": "Opprett en hendelse", + "create_event_desc": "Klikk på denne knappen for å opprette en ny kalenderhendelse. Du kan angi en tittel, dato, klokkeslett og legge til deltakere.", + "event_modal_title": "Hendelsesdetaljer", + "event_modal_desc": "Her er hendelsesskjemaet. Fyll inn tittelen, velg dato og klokkeslett, og legg til sted eller deltakere. Trykk på «Lagre» når du er ferdig – eller lukk skjemaet og gå videre.", + "contacts_list_title": "Dine kontakter", + "contacts_list_desc": "Her er kontaktene dine. Klikk på en kontakt for å se all informasjonen til høyre. Du kan også opprette nye kontakter, importere vCard-filer eller organisere kontakter i grupper.", + "settings_tabs_title": "Innstillingsmenyen", + "settings_tabs_desc": "Her er alle innstillingskategoriene. Tilpass utseendet, administrer identiteter, sett opp e-postfiltre, velg kalenderinnstillinger og mye mer.", + "files_title": "Fillagring", + "files_desc": "Filutforskeren lar deg laste opp, organisere og dele filer – som et personlig skylager innebygd i e-posten din.", + "demo_banner_title": "Demokontroller", + "demo_banner_desc": "Du er i demomodus – alt forblir i nettleseren. Trykk på «Tilbakestill demo» når som helst for å begynne på nytt med nye eksempeldata.", + "quota_title": "Lagringsbruk", + "quota_desc": "Følg med på størrelsen på postkassen her. Sirkelen fylles opp etter hvert som du bruker mer plass." + }, + "unified_mailbox": { + "search_unavailable": "Søk er ikke tilgjengelig i den samlede visningen" + }, + "quote_header": { + "reply_line": "Den {date} skrev {from}:", + "forwarded_separator": "---------- Videresendt melding ----------", + "from_label": "Fra", + "date_label": "Dato", + "subject_label": "Emne" + }, + "pwa_install": { + "title": "Installer {appName}", + "description": "Installer appen for rask tilgang og støtte for bruk uten nett.", + "not_now": "Ikke nå", + "install": "Installer", + "dont_remind": "Ikke minn meg på det igjen", + "dismiss_aria": "Lukk forespørselen om installering" + }, + "deep_link": { + "message_not_found": "Denne meldingen er ikke lenger tilgjengelig.", + "thread_not_found": "Denne samtalen er ikke lenger tilgjengelig.", + "folder_not_found": "Denne mappen er ikke lenger tilgjengelig.", + "event_not_found": "Denne hendelsen er ikke lenger tilgjengelig.", + "file_not_found": "Denne filen er ikke lenger tilgjengelig.", + "account_unavailable": "Logg på den kontoen for å åpne denne lenken.", + "copy_message": "Kopier lenken til meldingen", + "copy_conversation": "Kopier lenken til samtalen", + "copy_event": "Kopier lenken til hendelsen", + "copied": "Lenken er kopiert", + "copy_failed": "Kunne ikke kopiere lenken" + }, + "plugins": { + "oauth_callback": { + "completing": "Fullfører autorisering…", + "completing_message": "Vennligst vent mens tilkoblingen opprettes.", + "received_title": "Autorisering mottatt", + "received_message": "Du kan gå tilbake til fanen der du startet tilkoblingen.", + "failed_title": "Autorisering mislyktes", + "denied": "Autorisering ble avslått.", + "failed_detail": "Autorisering mislyktes: {error}", + "missing_params": "Mangler autorisasjonskode eller tilstand.", + "delivery_failed": "Autoriseringsresultatet kunne ikke leveres til app-fanen. Lukk dette vinduet og prøv igjen." + }, + "settings": { + "title": "Tillegg", + "description": "Tillegg som administratoren din har lagt til. Slå dem på eller av for kontoen din.", + "description_uploads": "Tillegg gir webmailen flere funksjoner. Slå dem på eller av for kontoen din, eller installer dine egne.", + "description_demo": "Tillegg er ikke tilgjengelige i demoen.", + "install_button": "Installer tillegg", + "install_hint": "Velg en ZIP-fil med et tillegg.", + "install_hint_approval": "Velg en ZIP-fil med et tillegg. En administrator må godkjenne det før du kan slå det på.", + "install_uploads_off": "Administratoren din tillater ikke at du installerer tillegg.", + "install_managed_id": "Administratoren din tilbyr allerede et tillegg med dette navnet, så det kan ikke erstattes.", + "install_invalid": "Denne filen er ikke et tillegg som kan installeres: {details}", + "toast_installed": "Tillegget er installert. Slå det på nedenfor.", + "toast_installed_pending": "Tillegget er installert og sendt til administratoren din for godkjenning.", + "toast_enabled": "{name} er på", + "toast_disabled": "{name} er av", + "toast_removed": "{name} er fjernet", + "toast_forced": "Administratoren din holder {name} slått på", + "toast_approval_requested": "{name} venter på godkjenning fra administratoren din", + "empty_title": "Ingen tillegg ennå", + "empty_admin": "Administratoren din har ikke lagt til noen tillegg.", + "empty_uploads": "Installer et tillegg fra en ZIP-fil for å komme i gang.", + "empty_demo": "Tillegg krever en ekte konto, så demoen har ingen.", + "syncing": "Sjekker tilleggene mot serveren…", + "status_installed": "Av", + "status_enabled": "Starter", + "status_running": "På", + "status_disabled": "Av", + "status_error": "Problem", + "badge_forced": "Påkrevd", + "badge_managed": "Fra administratoren din", + "badge_yours": "Installert av deg", + "badge_awaiting": "Venter på godkjenning", + "toggle_label": "Slå {name} på eller av", + "forced_note": "Administratoren din krever dette tillegget, så det kan ikke slås av.", + "permissions": "Hva det kan gjøre", + "permissions_managed": "Hva det kan gjøre (godkjent av administratoren din)", + "settings": "Innstillinger", + "remove": "Fjern", + "remove_title": "Vil du fjerne {name}?", + "remove_message": "Tillegget og alt det har lagret i denne nettleseren, blir slettet. Du kan installere det på nytt senere.", + "remove_confirm": "Fjern", + "error_user_plugins_off": "Administratoren din har slått av tillegg som brukere har installert, så dette tillegget kjører ikke.", + "error_approval_unavailable": "Dette tillegget ble installert før godkjenning ble påkrevd. Fjern det og installer det på nytt for å sende det til godkjenning.", + "error_approval_denied": "Administratoren din godkjente ikke dette tillegget.", + "error_approval_pending": "Venter på at administratoren din skal godkjenne dette tillegget.", + "error_auto_disabled": "Slått av fordi det feilet gjentatte ganger. Slå det på igjen for å prøve på nytt.", + "accounts_heading": "Tilkoblede kontoer", + "account_on": "{label} på {host}", + "account_waiting": "{label} på {host} (pålogging ikke fullført)", + "disconnect": "Koble fra", + "disconnect_failed": "Kunne ikke koble fra {label}" + }, + "consent": { + "title": "Vil du la {name} gjøre dette?", + "intro": "Tillegget ber om tillatelsene nedenfor. Det får dem bare hvis du tillater det.", + "high_risk_note": "Merkede punkter gir tillegget tilgang til innholdet i e-posten din eller lar det sende data andre steder. Tillat dem bare for tillegg du stoler på.", + "revoke_hint": "Du kan når som helst slå av eller fjerne tillegget under Innstillinger.", + "deny": "Ikke tillat", + "allow": "Tillat" + }, + "permissions": { + "none": "Det trenger ingen spesielle tillatelser.", + "high_risk": "Krever varsomhet", + "email_read": { + "title": "Lese e-posten din", + "body": "Se avsendere, mottakere, emner og hele teksten i alle meldinger på kontoen din, og mappene dine." + }, + "email_write": { + "title": "Endre og organisere e-posten din", + "body": "Legge til eller fjerne etiketter, flagg og andre nøkkelord på meldingene dine, flytte dem mellom mappene dine og til arkivet, og lagre utkast." + }, + "email_send": { + "title": "Endre e-post før den sendes", + "body": "Lese og endre mottakere, emne, tekst og vedlegg i meldingene du skriver, og stoppe en melding fra å bli sendt." + }, + "crypto_full": { + "title": "Full tilgang til denne appen", + "body": "Kjører med de samme rettighetene som selve webmailen. Det kan lese all e-posten din og lagrede nøkler og handle på dine vegne. Tillat dette bare for tillegg du stoler fullt på." + }, + "email_raw_send": { + "title": "Sende meldinger det har laget selv", + "body": "Sende ferdige meldinger, for eksempel signerte eller krypterte, fra kontoen din." + }, + "email_blob_read": { + "title": "Lese råinnholdet i meldinger", + "body": "Lese de opprinnelige dataene i meldingene og vedleggene dine, og i hver fil du legger ved før den sendes." + }, + "email_blob_write": { + "title": "Erstatte vedlegg før sending", + "body": "Laste opp filer til e-postserveren din og erstatte et vedlegg med en annen fil før meldingen sendes." + }, + "email_render_takeover": { + "title": "Endre hvordan meldinger vises", + "body": "Erstatte teksten i meldingene du åpner og i meldingslisten, for eksempel for å vise dekryptert innhold." + }, + "calendar_read": { + "title": "Lese kalenderen din", + "body": "Se kalenderne dine og hendelsene i dem." + }, + "calendar_write": { + "title": "Endre kalenderen din", + "body": "Opprette, endre og slette hendelser." + }, + "contacts_read": { + "title": "Lese kontaktene dine", + "body": "Søke i og lese adressebøkene dine." + }, + "contacts_write": { + "title": "Endre kontaktene dine", + "body": "Opprette, endre og slette kontakter og adressebøker." + }, + "files_read": { + "title": "Lese filene dine", + "body": "Bla gjennom og åpne filene som er lagret på kontoen din." + }, + "files_write": { + "title": "Endre filene dine", + "body": "Laste opp, gi nytt navn til, flytte og slette filer på kontoen din." + }, + "identity_read": { + "title": "Se avsenderidentitetene dine", + "body": "Se adressene du sender fra, og signaturene deres." + }, + "identity_write": { + "title": "Endre avsenderidentitetene dine", + "body": "Opprette, endre og slette avsenderidentiteter og signaturer." + }, + "filters_read": { + "title": "Lese e-postfiltrene dine", + "body": "Se reglene som sorterer innkommende e-post." + }, + "filters_write": { + "title": "Endre e-postfiltrene dine", + "body": "Legge til regler i filtrene dine på serveren. Slike regler kan flytte, videresende eller slette innkommende e-post." + }, + "tasks_read": { + "title": "Lese oppgavene dine", + "body": "Se oppgavelistene og oppgavene dine." + }, + "tasks_write": { + "title": "Endre oppgavene dine", + "body": "Opprette, endre, fullføre og slette oppgaver." + }, + "templates_read": { + "title": "Lese malene dine", + "body": "Se de lagrede meldingsmalene dine." + }, + "templates_write": { + "title": "Endre malene dine", + "body": "Opprette, endre og slette meldingsmaler." + }, + "smime_read": { + "title": "Se S/MIME-statusen din", + "body": "Se om du har S/MIME-nøkler og -sertifikater, uten tilgang til nøklene." + }, + "vacation_read": { + "title": "Se fraværssvaret ditt", + "body": "Se om det automatiske svaret ditt er på, og hva det sier." + }, + "vacation_write": { + "title": "Endre fraværssvaret ditt", + "body": "Slå det automatiske svaret på eller av og endre teksten og datoene." + }, + "settings_read": { + "title": "Lese innstillingene dine", + "body": "Se innstillingene og etikettdefinisjonene dine." + }, + "settings_write": { + "title": "Endre innstillingene dine", + "body": "Legge til og endre rekkefølgen på etiketter og endre innstillingene dine." + }, + "security_read": { + "title": "Se kontosikkerheten din", + "body": "Se om tofaktorautentisering og kryptering er slått på, uten tilgang til hemmeligheter." + }, + "auth_observe": { + "title": "Vite når du logger på", + "body": "Få beskjed når du logger på, bytter konto eller økten din fornyes." + }, + "auth_emit": { + "title": "Logge deg ut", + "body": "Logge deg ut av webmailen." + }, + "account_read": { + "title": "Se kontoene dine", + "body": "Se kontoene du har lagt til, med adressene og serverne deres." + }, + "http_post": { + "title": "Bruke webmailserveren for deg", + "body": "Kalle disse serverendepunktene med påloggingen din:" + }, + "http_fetch": { + "title": "Koble til andre nettsteder", + "body": "Sende forespørsler til disse nettstedene. Alt tillegget kan lese, kan bli sendt dit:" + }, + "admin_config": { + "title": "Lese innstillingene sine fra administratoren din", + "body": "Lese innstillingene administratoren din har lagret for dette tillegget." + }, + "ui_observe": { + "title": "Følge med på hvor du er i appen", + "body": "Vite hvilken side som er åpen, og reagere på lenker du klikker på." + }, + "ui_toolbar": { + "title": "Legge til knapper i verktøylinjer", + "body": "Vise egne knapper i meldingsverktøylinjen, på en åpen melding og i topplinjen." + }, + "ui_app_top_banner": { + "title": "Vise et banner øverst", + "body": "Vise et varsel over hele toppen av appen." + }, + "ui_email_banner": { + "title": "Vise varsler på meldinger", + "body": "Vise egne varsler og sammendrag over, i og mellom delene av en åpen melding eller samtale." + }, + "ui_email_footer": { + "title": "Legge til innhold under meldinger", + "body": "Vise eget innhold under en åpen melding." + }, + "ui_contact_cryptokeys": { + "title": "Vise krypteringsnøklene til kontakter", + "body": "Vise og administrere krypteringsnøklene på en kontakt." + }, + "ui_email_details": { + "title": "Legge til paneler i meldingsdetaljene", + "body": "Vise et eget panel ved siden av eller under en åpen melding." + }, + "ui_download_file": { + "title": "Lagre filer på enheten din", + "body": "Tilby filer det har laget, for nedlasting." + }, + "ui_message_list_tabs": { + "title": "Legge til faner i innboksen din", + "body": "Sortere innboksen din i faner som Oppdateringer eller Kampanjer." + }, + "ui_composer_toolbar": { + "title": "Legge til i meldingsvinduet", + "body": "Vise egne knapper, menyvalg og seksjoner mens du skriver en melding." + }, + "ui_composer_sidebar": { + "title": "Legge til et panel i meldingsvinduet", + "body": "Vise et eget panel ved siden av meldingen du skriver." + }, + "ui_sidebar_widget": { + "title": "Legge til i sidefeltet", + "body": "Vise egne bokser og seksjoner i sidefeltet for e-post." + }, + "ui_settings_section": { + "title": "Legge til en innstillingsside", + "body": "Vise en egen side under Innstillinger." + }, + "ui_context_menu": { + "title": "Legge til i meldingsmenyen", + "body": "Legge til valg i menyen som åpnes når du høyreklikker på en melding." + }, + "ui_navigation_rail": { + "title": "Legge til i navigasjonsfeltet", + "body": "Vise en egen knapp i navigasjonsfeltet." + }, + "ui_keyboard": { + "title": "Legge til tastatursnarveier", + "body": "Reagere på tastatursnarveier det definerer selv." + }, + "ui_calendar_action": { + "title": "Legge til i kalendervisningen", + "body": "Vise egne knapper og seksjoner på hendelser, og korte etiketter og prikker på kalenderdager." + }, + "ui_admin_page": { + "title": "Legge til en administrasjonsside", + "body": "Vise en egen side i administrasjonsinnstillingene." + }, + "app_lifecycle": { + "title": "Vite når appen starter og stopper", + "body": "Få beskjed når appen starter, går frakoblet eller du logger ut." + }, + "compose_open": { + "title": "Åpne meldingsvinduet for deg", + "body": "Åpne en ny melding med mottakere, tekst eller vedlegg fylt ut. Du bestemmer fortsatt om den skal sendes." + }, + "compose_read": { + "title": "Lese meldingen du skriver", + "body": "Lese teksten i en melding mens du skriver den, når du bruker tillegget i redigeringsvinduet. Ikke signaturen eller den siterte meldingen." + }, + "compose_write": { + "title": "Endre meldingen du skriver", + "body": "Erstatte eller legge til tekst i en melding mens du skriver den, når du bruker tillegget i redigeringsvinduet. Du kan angre hver endring, og du bestemmer fortsatt om den skal sendes." + }, + "ui_notifications": { + "title": "Vise varsler", + "body": "Vise varsler på enheten din, eller i appen hvis du ikke har tillatt dem. Hvert varsel viser navnet på tillegget." + }, + "ui_message_list": { + "title": "Legge til i meldingslisten", + "body": "Vise eget innhold over meldingslisten, og knapper på meldinger og i verktøylinjen for valgte meldinger." + }, + "ui_contacts": { + "title": "Legge til i kontaktvisningen", + "body": "Vise egne seksjoner i detaljene for en kontakt og knapper i kontaktlisten." + }, + "ui_tasks": { + "title": "Legge til i oppgavevisningen", + "body": "Vise egne seksjoner i detaljene for en oppgave og knapper i oppgavelisten." + }, + "ui_files": { + "title": "Legge til i filvisningen", + "body": "Vise egne seksjoner i forhåndsvisningen av filer og valg i menyen for en fil." + }, + "ui_commands": { + "title": "Legge til kommandoer", + "body": "Legge til egne valg i kommandopaletten (Ctrl+K)." + }, + "ui_app_page": { + "title": "Legge til sider", + "body": "Legge til egne sider i appen, hver med en knapp i navigasjonsfeltet." + }, + "http_proxy": { + "title": "Bruke kontoer du kobler til", + "body": "Sende forespørsler via Bulwarks server med en konto eller nøkkel som du eller administratoren din har satt opp. Tillegget ser aldri passordet, men det kan gjøre alt den kontoen kan gjøre på sin egen server." + } + }, + "dialog": { + "from_plugin": "Fra tillegget {name}", + "ok": "OK", + "submit": "Send", + "confirm": "Bekreft", + "cancel": "Avbryt", + "close": "Lukk", + "turn_off": "Slå av {name}", + "plugin_menu": "Alternativer for {name}", + "password_note": "Dette skjemaet kommer fra tillegget {name}, ikke fra webmailen. Skriv bare inn et passord hvis du stoler på dette tillegget.", + "toast_auto_disabled": "{name} ble slått av fordi det feilet gjentatte ganger." + }, + "connect": { + "title": "Koble til {label}", + "intro_password": "{name} vil bruke kontoen din hos {label}. Bulwark lagrer passordet på sin server og sender det bare til {host}. Tillegget ser det aldri.", + "intro_password_any": "{name} vil bruke kontoen din hos {label}. Bulwark lagrer passordet på sin server og sender det bare til serveren du skriver inn her. Tillegget ser det aldri.", + "intro_signin": "{name} vil logge på kontoen din hos {label}. Skriv inn serveren her, og logg deretter på via serverens egen side. Bulwark lagrer tilgangen det får, på sin server; tillegget ser den aldri.", + "server": "Serveradresse", + "server_hint": "cloud.example.com", + "username": "Brukernavn", + "secret": "Passord eller nøkkel", + "connect": "Koble til", + "continue": "Fortsett", + "cancel": "Avbryt", + "saving": "Lagrer…", + "error_server": "Skriv inn adressen til serveren, for eksempel https://cloud.example.com", + "error_save": "Kunne ikke lagre: {error}" + } + }, + "global_search": { + "title": "Søk i alt", + "placeholder": "Søk i e-post, kontakter, kalender, filer...", + "scope_label": "Søk i", + "account_label": "Konto", + "scope_all": "Alt", + "scope_mail": "E-post", + "scope_contacts": "Kontakter", + "scope_calendar": "Kalender", + "scope_files": "Filer", + "all_accounts": "Alle kontoer", + "show_all": "Vis alle resultater", + "no_results": "Ingen resultater for “{query}”", + "server_hint": "Hele ord søker på serveren; deler av ord treffer allerede innlastede elementer", + "searching": "Søker...", + "account_error": "Kunne ikke søke i {account}", + "recent_searches": "Nylige søk", + "recurring": "Gjentakende", + "no_subject": "(no subject)", + "empty_hint": "Søk i e-post, kontakter, hendelser og filer på alle kontoer. Prøv from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Flatt etter dato", + "grouped": "Gruppert", + "load_more": "Last inn flere", + "preview_empty": "Velg et resultat for å forhåndsvise det", + "open_full": "Åpne", + "view_label": "Visning" + }, + "top_bar": { + "more": "Flere alternativer" + }, + "plugin_pages": { + "loading": "Laster inn siden", + "not_available_title": "Denne siden er ikke tilgjengelig", + "not_available_description": "Tillegget som la den til, er slått av eller ikke lenger installert.", + "back_to_mail": "Tilbake til e-post", + "settings_group": "Tillegg" + }, + "command_palette": { + "title": "Kommandopalett", + "open": "Åpne kommandopaletten", + "placeholder": "Skriv en kommando", + "no_results": "Ingen samsvarende kommandoer", + "go_to": "Gå til {name}", + "compose": "Ny melding", + "search": "Søk i e-post", + "theme_dark": "Bytt til mørkt tema", + "theme_light": "Bytt til lyst tema", + "shortcuts": "Vis tastatursnarveier", + "group_navigation": "Gå til", + "group_actions": "Handlinger", + "group_plugins": "Tillegg" + }, + "notification_center": { + "title": "Varsler", + "mark_all_read": "Merk alle som lest", + "clear": "Tøm", + "empty": "Ingen varsler", + "close": "Lukk", + "unread": "Ulest", + "unread_count": "{count} uleste", + "level_error": "Feil", + "level_warning": "Advarsel" + } +} diff --git a/locales/nl/common.json b/locales/nl/common.json index 47d6b736..b2c6c434 100644 --- a/locales/nl/common.json +++ b/locales/nl/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimalistische webmailclient die het JMAP-protocol gebruikt", - "login": { - "title": "Webmail", - "username_label": "E-mail", - "username_placeholder": "gebruiker@voorbeeld.nl", - "password_label": "Wachtwoord", - "password_placeholder": "Voer je wachtwoord in", - "jmap_endpoint_label": "JMAP-server", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "De server moet CORS-verzoeken van dit domein toestaan.", - "jmap_server_label": "Server", - "jmap_server_auto_picked": "Server geselecteerd op basis van je e-maildomein.", - "sign_in": "Aanmelden", - "signing_in": "Aanmelden...", - "loading": "Laden...", - "reconnecting": "Verbinding verloren. Opnieuw verbinden…", - "error": { - "invalid_credentials": "Ongeldig e-mailadres of wachtwoord", - "connection_failed": "Kan geen verbinding maken met de server", - "cors_blocked": "De server is bereikbaar maar blokkeert cross-origin verzoeken. Controleer de CORS-instellingen van uw JMAP-server en sta dit domein toe.", - "server_error": "De server is tijdelijk niet beschikbaar. Probeer het later opnieuw.", - "generic": "Er is een fout opgetreden. Probeer het opnieuw.", - "totp_required": "Een tweefactorauthenticatiecode is vereist. Voer uw code hieronder in.", - "totp_invalid": "Ongeldige authenticatiecode. Controleer uw authenticator-app.", - "oauth_discovery_failed": "SSO is ingeschakeld maar de identiteitsprovider is niet bereikbaar. Controleer uw OAuth-configuratie." - }, - "show_password": "Wachtwoord tonen", - "hide_password": "Wachtwoord verbergen", - "totp_toggle": "Ik heb een 2FA-code", - "remember_me": "Onthoud mij", - "config_error": { - "title": "Configuratiefout", - "fetch_failed": "Kan de applicatieconfiguratie niet laden. Probeer het later opnieuw.", - "server_not_configured": "De mailserver is niet geconfigureerd. Neem contact op met je beheerder." - }, - "remove_from_history": "Verwijder uit geschiedenis", - "totp_label": "Authenticatiecode", - "totp_placeholder": "000000", - "session_expired": "Uw sessie is verlopen. Meld u opnieuw aan.", - "dismiss": "Sluiten", - "or": "of", - "sign_in_sso": "Inloggen met SSO", - "add_account_title": "Account toevoegen", - "add_account_subtitle": "Inloggen met een ander account", - "cancel": "Annuleren", - "website": "Website", - "imprint": "Colofon", - "privacy_policy": "Privacybeleid", - "try_demo": "Demo proberen", - "demo_description": "Verken met voorbeeldgegevens - geen account nodig", - "demo_launching": "Demo starten...", - "demo_login_button": "Demo starten", - "demo_tagline": "Ervaar een complete e-mailclient. Geen account nodig.", - "demo_no_signup": "Geen registratie nodig - verken vrij met voorbeeldgegevens", - "oauth_completing": "Aanmelding voltooien...", - "oauth_error": { - "title": "Authenticatie mislukt", - "invalid_state": "Beveiligingsvalidatie mislukt. Probeer opnieuw in te loggen.", - "missing_params": "Ontbrekende autorisatiegegevens. Probeer opnieuw in te loggen.", - "token_exchange_failed": "Authenticatie kon niet worden voltooid. Probeer het opnieuw.", - "access_denied": "Toegang geweigerd. Neem contact op met uw beheerder.", - "back_to_login": "Terug naar inloggen" - } - }, - "sidebar": { - "close": "Sluiten", - "compose": "Nieuw bericht", - "compose_hint": "Opstellen (c)", - "search_placeholder": "Zoeken in e-mail...", - "search_placeholder_hint": "E-mail zoeken... (druk /)", - "storage": "Opslag", - "storage_used": "Gebruikt", - "storage_free": "Vrij", - "storage_total": "Totaal", - "sign_out": "Afmelden", - "sign_out_of": "Uitloggen van {account}", - "sign_out_all": "Uitloggen van alle accounts", - "add_account": "Account toevoegen", - "set_as_default": "Als standaard instellen", - "switch_account": "Account wisselen", - "contacts": "Contacten", - "calendar": "Agenda", - "settings": "Instellingen", - "admin": "Admin", - "files": "Bestanden", - "loading_mailboxes": "Mappen laden...", - "push_connected": "Real-time updates actief", - "push_disconnected": "Real-time updates inactief", - "keyboard_shortcuts": "Sneltoetsen", - "theme": { - "light": "Lichte modus", - "dark": "Donkere modus", - "system": "Systeemthema" - }, - "language": { - "title": "Taal" - }, - "mailboxes": { - "inbox": "Postvak IN", - "sent": "Verzonden", - "drafts": "Concepten", - "trash": "Prullenbak", - "archive": "Archief", - "starred": "Met ster", - "all_mail": "Alle e-mail", - "spam": "Spam", - "important": "Belangrijk" - }, - "unified_inbox": "Gecombineerd postvak IN", - "unified_sent": "Alle verzonden", - "unified_drafts": "Alle concepten", - "unified_trash": "Alle prullenbakken", - "unified_archive": "Alle archieven", - "unified_junk": "Alle spam", - "all_accounts": "Alle accounts", - "unified_mailbox": "Gecombineerd postvak", - "expand": "Uitklappen", - "collapse": "Inklappen", - "expand_tooltip": "Uitklappen", - "collapse_tooltip": "Inklappen", - "mobile": { - "search": "Zoeken", - "compose": "Opstellen", - "go_back": "Terug", - "toggle_menu": "Menu wisselen" - }, - "clear_search": "Zoekopdracht wissen", - "vacation_active": "Afwezigheidsmelder is actief", - "demo_banner": "Demomodus", - "demo_reset": "Resetten", - "demo_tour": "Rondleiding", - "tags": "Labels", - "show_all_tags": "Alles tonen ({count})", - "show_fewer_tags": "Minder tonen", - "folders": "Mappen", - "mail": "E-mail", - "nav_label": "Navigatie", - "add_app": "Apps", - "shared": "Gedeeld", - "scheduled": "Gepland", - "unified_all_unread": "Alle ongelezen", - "unified_all_starred": "Alle met ster", - "unified_all_mail": "Alle e-mail", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Standaardapps", - "description": "Kies of e-mail- en kalenderlinks in Bulwark worden geopend. Technisch registreert Bulwark zich als protocolhandler voor mailto:- en webcal:-links.", - "unsupported": "Deze browser of verbinding ondersteunt geen handmatige registratie van protocolhandlers. Mogelijk kun je de geïnstalleerde PWA nog gebruiken via de browser- of systeeminstellingen.", - "mailto_label": "E-maillinks", - "mailto_description": "Opent mailto:-links in Bulwark met een vooraf ingevulde opsteller.", - "protocol_open_mode_label": "Bij het openen van protocollinks", - "protocol_open_mode_description": "Kies of Bulwark mailto:- en webcal:-links in een nieuw tabblad opent of een geopende sessie hergebruikt. Voor de optie actieve sessie is toestemming voor meldingen nodig, zodat je op een fallbackmelding kunt klikken om Bulwark naar voren te halen als de browser focus blokkeert.", - "protocol_open_mode_active_session": "Indien mogelijk openen in actieve sessie", - "protocol_open_mode_new_tab": "Altijd een nieuw tabblad openen", - "focus_notification_title": "Bulwark openen", - "focus_notification_body": "De link is geopend in Bulwark. Klik om het venster naar voren te halen.", - "webcal_label": "Kalenderlinks", - "webcal_description": "Opent webcal:-links in Bulwark met een vooraf ingevuld dialoogvenster voor kalenderabonnementen.", - "register_mailto": "E-mailapp registreren", - "register_webcal": "Kalenderapp registreren", - "mailto_registered": "Registratie van e-mailhandler aangevraagd", - "webcal_registered": "Registratie van kalenderhandler aangevraagd", - "registration_failed": "Registratie van protocolhandler mislukt", - "opening_mailto": "Opsteller wordt geopend...", - "opening_webcal": "Kalender wordt geopend...", - "browser_note": "Je browser of besturingssysteem kan om bevestiging vragen en kan vereisen dat Bulwark is geïnstalleerd voordat het als standaardapp kan worden geselecteerd.", - "select_account_title": "Account kiezen", - "select_mailto_account": "Kies welk account deze e-maillink moet openen.", - "select_webcal_account": "Kies welk account deze kalenderlink moet openen.", - "select_account_note": "Deze keuze geldt alleen voor deze protocol-link.", - "detail_to": "Aan", - "detail_subject": "Onderwerp", - "detail_no_subject": "Geen onderwerp", - "detail_calendar": "Agenda", - "detail_source": "Bron", - "active_account": "Actief", - "switching_account": "Account wisselen..." - }, - "sidebar_apps": { - "modal_title": "Zijbalk-apps", - "add_new": "App toevoegen", - "edit_app": "App bewerken", - "name_label": "Naam", - "name_placeholder": "Mijn app", - "name_required": "Naam is vereist", - "url_label": "URL", - "url_required": "URL is vereist", - "url_invalid": "Voer een geldige http- of https-URL in", - "icon_label": "Pictogram", - "icon_required": "Pictogram is vereist", - "open_mode_label": "Openingsmodus", - "open_new_tab": "Nieuw tabblad", - "open_inline": "Ingesloten", - "cancel": "Annuleren", - "add": "Toevoegen", - "update": "Bijwerken", - "delete": "Verwijderen", - "delete_confirm_title": "App verwijderen", - "delete_confirm": "Weet je zeker dat je \"{name}\" wilt verwijderen?", - "no_apps": "Nog geen apps toegevoegd", - "no_apps_hint": "Voeg aangepaste apps en links toe aan je zijbalk", - "managed_title": "Aangeboden door uw beheerder", - "managed_description": "Deze apps zijn voor iedereen ingesteld en kunnen niet worden gewijzigd.", - "managed_badge": "Beheerd", - "search_icons": "Pictogrammen zoeken...", - "show_popular": "Populair", - "show_all": "Alle", - "no_icons_found": "Geen pictogrammen gevonden", - "inline_badge": "Ingesloten", - "tab_badge": "Tabblad", - "show_on_mobile": "Weergeven op mobiel" - }, - "email_list": { - "no_emails": "Geen berichten gevonden", - "no_emails_description": "Deze mailbox is leeg", - "no_search_results": "Geen resultaten gevonden", - "no_search_results_description": "Pas je zoekopdracht of filters aan", - "loading": "E-mails laden...", - "unread": "ongelezen", - "to_me": "Aan mij", - "to_recipients": "Aan {count} ontvangers", - "and_others": "en {count} anderen", - "draft": "Concept", - "starred": "Met ster", - "pinned": "Vastgezet", - "replied": "Beantwoord", - "forwarded": "Doorgestuurd", - "has_attachment": "Met bijlage", - "conversations_count": "{count} van {total} gesprekken", - "conversations_count_plus": "{count}+ gesprekken", - "conversations_count_simple": "{count} gesprekken", - "no_conversations": "Geen gesprekken", - "loading_more": "Meer e-mails laden...", - "no_more_emails": "Geen e-mails meer om te laden", - "batch_actions": { - "select": "E-mails selecteren", - "select_all": "Alles selecteren", - "mark_read": "Markeren als gelezen", - "selected_messages": "{count, plural, one {1 e-mail} other {# e-mails}} geselecteerd", - "mark_unread": "Markeren als ongelezen", - "delete": "Verwijderen", - "delete_confirm_title": "E-mails verwijderen", - "delete_confirm_message": "Weet u zeker dat u {count, plural, one {1 e-mail} other {# e-mails}} wilt verwijderen?", - "clear_selection": "Selectie wissen" - }, - "permanent_delete": "Definitief verwijderen", - "permanent_delete_confirm_title": "Definitief verwijderen", - "permanent_delete_confirm_message": "Deze e-mail wordt definitief verwijderd. Deze actie kan niet ongedaan worden gemaakt.", - "permanent_delete_confirm_batch_message": "Deze {count, plural, one {1 e-mail} other {# e-mails}} worden definitief verwijderd. Deze actie kan niet ongedaan worden gemaakt.", - "empty_folder": { - "button": "Map legen", - "confirm_title": "Map legen", - "confirm_message": "Alle e-mails in deze map worden permanent verwijderd. Deze actie kan niet ongedaan worden gemaakt.", - "confirm_button": "Map legen", - "junk_hint": "U kunt de map Spam legen om alle berichten permanent te verwijderen.", - "trash_hint": "U kunt de prullenbak legen om alle berichten permanent te verwijderen." - }, - "no_scheduled_emails": "Geen geplande e-mails", - "no_scheduled_emails_description": "Berichten die voor later zijn gepland verschijnen hier.", - "scheduled_count": "{count, plural, one {1 geplande e-mail} other {# geplande e-mails}}", - "scheduled_actions_hint": "Geplande berichten kunnen worden geannuleerd, opnieuw gepland of bewerkt via hun geplande acties.", - "cancel_scheduled_send": "Verzenden annuleren", - "reschedule_send": "Opnieuw plannen", - "cancel_and_edit": "Annuleren en bewerken", - "cancel_and_compose_again": "Annuleren en opnieuw opstellen", - "reschedule_prompt": "Voer een nieuwe datum/tijd in, bijv. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "De afzender vraagt om een leesbevestiging:", - "send": "Bevestiging verzenden", - "ignore": "Negeren", - "sent": "Leesbevestiging verzonden.", - "send_failed": "Leesbevestiging kon niet worden verzonden", - "mdn_subject": "Gelezen: {subject}", - "mdn_body": "Dit is een leesbevestiging voor het bericht dat u hebt verzonden naar {recipient}.\n\nLet op: deze bevestiging geeft alleen aan dat het bericht is weergegeven op de computer van de ontvanger. Er is geen garantie dat de ontvanger de inhoud heeft gelezen of begrepen." - }, - "no_email_selected": "Geen e-mail geselecteerd", - "no_email_description": "Selecteer een e-mail uit de lijst om deze hier te bekijken", - "no_conversation_selected": "Geen gesprek geselecteerd", - "no_conversation_description": "Kies een gesprek uit de lijst om het hier te lezen", - "compose": "Opstellen", - "compose_hint": "Nieuw bericht opstellen", - "no_subject": "(Geen onderwerp)", - "no_body_content": "(Geen inhoud beschikbaar)", - "no_preview_available": "Geen voorbeeld beschikbaar", - "show_quoted_text": "Geciteerde tekst tonen", - "hide_quoted_text": "Geciteerde tekst verbergen", - "loading_email": "E-mail laden...", - "loading": "Laden...", - "reply": "Beantwoorden", - "reply_all": "Allen beantwoorden", - "forward": "Doorsturen", - "delete": "Verwijderen", - "archive": "Archiveren", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Ster toevoegen", - "unstar": "Ster verwijderen", - "mark_unread": "Markeren als ongelezen", - "mark_read": "Markeren als gelezen", - "unread": "Ongelezen", - "read": "Gelezen", - "spam_short": "Spam", - "not_spam_short": "Geen spam", - "move": "Verplaatsen", - "print": "Afdrukken", - "view_source": "Bron bekijken", - "fullscreen": "Volledig scherm", - "exit_fullscreen": "Volledig scherm sluiten", - "export_email": "Exporteren als .eml", - "forward_as_attachment": "Doorsturen als bijlage", - "import_email": ".eml of .zip importeren", - "keyboard_shortcuts": "Sneltoetsen (?)", - "email_source": "E-mailbron", - "draft_banner": "Dit bericht is een concept", - "edit_draft": "Bewerken", - "copy_source": "Kopiëren naar klembord", - "source_copied": "Bron gekopieerd naar klembord", - "attachments": "Bijlagen", - "important": "Belangrijk", - "download": "Downloaden", - "download_all": "Alles downloaden", - "from": "Van", - "to": "Aan", - "cc": "CC", - "bcc": "BCC", - "date": "Datum", - "subject": "Onderwerp", - "show_details": "Details tonen", - "hide_details": "Details verbergen", - "external_content_warning": "Afbeeldingen en externe inhoud zijn geblokkeerd", - "load_external_content": "Afbeeldingen laden", - "trust_sender": "Deze afzender altijd vertrouwen", - "back_to_list": "Terug naar lijst", - "view_contact": "Contact bekijken", - "message_details": "Berichtdetails", - "more_reply_options": "Meer antwoordopties", - "set_tag": "Label instellen", - "tag": "Label", - "more_actions": "Meer acties", - "move_to": "Verplaatsen naar...", - "remove_tag": "Label verwijderen", - "tag_filter_placeholder": "Labels filteren", - "tag_no_matches": "Geen overeenkomende labels", - "more_count": "+{count} meer", - "characters_count": "{count} tekens", - "quick_reply_placeholder": "Schrijf een snel antwoord...", - "more_options": "Meer opties", - "sending": "Verzenden...", - "security_authentication": "Beveiliging & Authenticatie", - "technical_details": "Technische details", - "message_id_label": "Bericht-ID:", - "reply_to_label": "Antwoord naar:", - "delivery_time_label": "Bezorgtijd:", - "conversation_part_label": "Onderdeel van gesprek:", - "previous_messages": "{count} vorig bericht", - "previous_messages_plural": "{count} vorige berichten", - "time": { - "day": "dag", - "days": "dagen", - "hour": "uur", - "hours": "uur", - "minute": "minuut", - "minutes": "minuten" - }, - "unknown_sender": "Onbekend", - "recipient_me": "mij", - "recipient_and_others": "{name} en {count} anderen", - "recipient_to_prefix": "Aan:", - "authentication": { - "title": "Authenticatie", - "status": { - "verified": "Geverifieerd", - "warning": "Waarschuwing", - "none": "Niet geauthenticeerd" - }, - "spf": { - "pass": "SPF Geslaagd", - "fail": "SPF Mislukt", - "none": "Geen SPF" - }, - "dkim": { - "pass": "DKIM Geldig", - "fail": "DKIM Ongeldig", - "none": "Geen DKIM" - }, - "dmarc": { - "pass": "DMARC Geslaagd", - "fail": "DMARC Mislukt", - "none": "Geen DMARC" - }, - "spam_score": "Spamscore", - "tooltip_spf": "Sender Policy Framework: controleert of de verzendende server gemachtigd is om e-mail namens het domein te verzenden", - "tooltip_dkim": "DomainKeys Identified Mail: bevestigt met een cryptografische handtekening dat de e-mail tijdens het transport niet is gewijzigd", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zorgt ervoor dat SPF en DKIM overeenkomen met het domein van de afzender en stelt een beleid in voor fouten", - "policy": "Beleid", - "result": { - "pass": "Geslaagd", - "fail": "Mislukt", - "softfail": "Zachte mislukking", - "neutral": "Neutraal", - "permerror": "Permanente fout", - "temperror": "Tijdelijke fout", - "none": "Geen" - } - }, - "headers": { - "routing": "Routering", - "received": "Ontvangen", - "message_id": "Bericht-ID", - "list_info": "Lijstinformatie" - }, - "tooltips": { - "reply": "Beantwoorden", - "reply_all": "Allen beantwoorden (a)", - "forward": "Doorsturen (f)", - "archive": "Archiveren", - "delete": "Verwijderen", - "star": "Ster toevoegen (s)", - "unstar": "Ster verwijderen (s)", - "compose": "Opstellen (c)", - "previous": "Vorige e-mail", - "next": "Volgende e-mail", - "edit_draft": "Concept bewerken" - }, - "spam": { - "button_title": "Spam melden", - "not_spam_title": "Markeren als legitiem", - "toast_success": "Verplaatst naar Ongewenst", - "toast_batch": "{count} e-mails verplaatst naar Ongewenst", - "toast_undo": "Ongedaan maken", - "toast_not_spam_success": "Verplaatst naar Postvak IN", - "toast_not_spam_batch": "{count} e-mails verplaatst naar Postvak IN", - "error": "Kan spam niet melden", - "error_not_spam": "Kan e-mail niet herstellen" - }, - "unsubscribe_banner": { - "label": "Nieuwsbrief", - "button": "Uitschrijven", - "confirm_title": "Uitschrijven van deze afzender?", - "confirm_button": "Bevestigen", - "cancel": "Annuleren", - "success_http": "Uitschrijfpagina geopend in nieuw tabblad", - "success_mailto": "Afmeldingsmail verzonden", - "confirm_message_http": "De afmeldpagina wordt in een nieuw tabblad geopend.", - "confirm_message_mailto": "Er wordt een afmeldingsmail naar de afzender gestuurd.", - "error": "Kan niet uitschrijven", - "dismiss": "Sluiten" - }, - "calendar_invitation": { - "loading": "Evenementdetails laden…", - "title": "Agenda-uitnodiging", - "published_title": "Gepubliceerd evenement", - "response_title": "Reactie op evenement", - "update_title": "Evenementupdate", - "counter_title": "Tegenvoorstel", - "refresh_title": "Vernieuwingsverzoek", - "declined_counter_title": "Tegenvoorstel afgewezen", - "cancelled_title": "Evenement geannuleerd", - "organizer": "Georganiseerd door {name}", - "organizer_label": "Georganiseerd door", - "attendees": "{count, plural, one {# deelnemer} other {# deelnemers}}", - "accept": "Accepteren", - "maybe": "Misschien", - "decline": "Weigeren", - "add_to_calendar": "Toevoegen aan agenda", - "added": "Toegevoegd aan agenda", - "rsvp_sent": "Reactie verzonden", - "parse_error": "Kan uitnodiging niet lezen", - "action_failed": "Die agenda-actie kon niet worden voltooid.", - "no_calendar": "Agenda niet beschikbaar", - "published_info": "Dit evenement is gedeeld ter referentie.", - "response_info": "Dit bericht bevat een reactie van een deelnemer.", - "response_info_organizer": "Deze reactie werkt uw evenement bij.", - "update_info": "Dit bericht werkt een bestaand evenement bij.", - "counter_info": "Dit bericht stelt wijzigingen aan een evenement voor.", - "counter_info_organizer": "Deze deelnemer stelde wijzigingen aan uw evenement voor.", - "refresh_info": "Dit bericht vraagt om de nieuwste evenementdetails.", - "refresh_info_organizer": "Een deelnemer vroeg om de nieuwste evenementdetails.", - "declined_counter_info": "De organisator heeft een tegenvoorstel afgewezen.", - "authentication_failed_info": "De mailauthenticatiecontroles voor deze uitnodiging zijn mislukt. Wees voorzichtig met agenda-acties.", - "authentication_missing_info": "Deze uitnodiging bevat geen geverifieerde mailauthenticatie. Controleer de details bij de organisator als iets ongewoon lijkt.", - "sender_mismatch_info": "Deze uitnodiging is verzonden vanaf {sender}, terwijl de organisator in de kalendergegevens {organizer} is.", - "sender_mismatch_unverified_info": "Deze uitnodiging is verzonden vanaf {sender}, terwijl de organisator in de kalendergegevens {organizer} is, en het bericht kon niet worden geverifieerd.", - "organizer_role": "U organiseert dit evenement", - "your_response": "Uw reactie: {status}", - "response_needed": "Reactie vereist", - "response_accepted": "Geaccepteerd", - "response_tentative": "Voorlopig", - "response_declined": "Geweigerd", - "response_delegated": "Gedelegeerd", - "actor_sent_info": "Verzonden door {name}.", - "actor_response_info": "{name} reageerde {status}.", - "actor_counter_info": "{name} stelde wijzigingen voor dit evenement voor.", - "actor_refresh_info": "{name} vroeg om de nieuwste evenementdetails.", - "actor_declined_counter_info": "{name} wees het tegenvoorstel af.", - "actor_note": "Notitie: {comment}", - "actor_unknown": "Iemand", - "proposed_changes": "Voorgestelde wijzigingen", - "change_title": "Titel", - "change_time": "Tijd", - "change_location": "Locatie", - "change_description": "Beschrijving", - "change_empty": "Geen", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Voorgestelde wijzigingen toepassen", - "proposal_applied": "De voorgestelde wijzigingen zijn toegepast.", - "review_proposal": "Voorstel bekijken", - "review_request": "Verzoek bekijken", - "view_in_calendar": "In agenda bekijken", - "select_calendar": "Agenda selecteren", - "already_in_calendar": "Staat al in je agenda", - "request_info": "Je bent uitgenodigd voor dit evenement. Reageer om de organisator te laten weten of je kunt deelnemen.", - "cancel_info": "De organisator heeft dit evenement geannuleerd.", - "event_updated": "Update #{sequence}", - "event_status_tentative": "Voorlopig", - "event_status_cancelled": "Geannuleerd", - "collapse": "Details verbergen", - "expand": "Details tonen" - }, - "previous": "Vorige", - "next": "Volgende", - "send": "Verzenden", - "more": "meer", - "details": { - "recipients_routing": "Ontvangers & routing", - "authentication_security": "Authenticatie & beveiliging", - "identifiers_threading": "Identificatoren & threading", - "mailing_list": "Mailinglijst", - "message_properties": "Berichteigenschappen", - "sent": "Verzonden", - "received": "Ontvangen", - "delivery_time": "Bezorgtijd", - "in_reply_to": "In-Reply-To", - "references": "Verwijzingen", - "thread_id": "Thread-ID", - "size": "Grootte", - "mime_type": "MIME-type", - "attachments_summary": "{count} bestanden · {size}", - "list_id": "Lijst-ID", - "list_help": "Lijsthulp", - "list_post": "Lijstbericht", - "list_unsubscribe": "Afmelden", - "iprev": "Reverse DNS", - "spam_status": "Spamstatus", - "ai_verdict": "AI-oordeel", - "account": "Account", - "no_subject": "(geen onderwerp)" - }, - "scheduled_banner": "Gepland om te verzenden op {date}", - "scheduled_send_created": "E-mail gepland voor verzending", - "cancel_scheduled_send": "Verzenden annuleren", - "reschedule_send": "Opnieuw plannen", - "cancel_and_edit": "Annuleren en bewerken", - "cancel_and_compose_again": "Annuleren en opnieuw opstellen", - "reschedule_prompt": "Voer een nieuwe datum/tijd in, bijv. 2026-05-04T15:30", - "scheduled_actions_only": "De geplande weergave ondersteunt alleen geplande acties", - "undo_send_scheduled": "Bericht gepland voor verzending", - "undo_send": "Verzenden ongedaan maken", - "contact_sidebar": { - "title": "Contact", - "close": "Zijbalk sluiten", - "action_email": "E-mail", - "action_email_title": "E-mail verzenden", - "action_copy": "Kopiëren", - "action_copy_title": "E-mailadres kopiëren", - "action_edit_title": "Contact bewerken", - "section_emails": "E-mailadressen", - "section_phones": "Telefoonnummers", - "section_organizations": "Organisaties", - "section_addresses": "Adressen", - "section_notes": "Notities", - "not_in_contacts": "Niet in uw contacten", - "add_to_contacts": "Aan contacten toevoegen", - "copied": "Gekopieerd!", - "copy_failed": "Kopiëren mislukt" - }, - "send_now": "Nu verzenden" - }, - "email_composer": { - "read_receipt_on": "Leesbevestiging aangevraagd (klik om uit te schakelen)", - "read_receipt_off": "Leesbevestiging aanvragen", - "dsn_on": "Bezorgmelding aangevraagd (klik om uit te schakelen)", - "dsn_off": "Bezorgmelding aanvragen", - "format_plain_text": "Overschakelen naar platte tekst", - "format_rich_text": "Overschakelen naar opgemaakte tekst (HTML)", - "require_tls_on": "Versleutelde bezorging vereist (klik om uit te schakelen)", - "require_tls_off": "Versleutelde bezorging vereisen (TLS)", - "new_message": "Nieuw bericht", - "signature_edit_hint": "Dubbelklik om de handtekening te bewerken", - "reply": "Beantwoorden", - "reply_all": "Allen beantwoorden", - "forward": "Doorsturen", - "reply_to": "Beantwoorden", - "reply_all_to": "Allen beantwoorden", - "forward_message": "Doorsturen", - "from": "Van", - "to": "Aan", - "cc": "CC", - "bcc": "BCC", - "subject": "Onderwerp", - "body_placeholder": "Schrijf je bericht...", - "send": "Verzenden", - "cancel": "Annuleren", - "attach": "Bijlage toevoegen", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Verwijderen", - "discard_draft_title": "Concept verwijderen?", - "discard_draft_confirm": "Je hebt niet-opgeslagen wijzigingen. Wil je dit concept verwijderen?", - "saving": "Opslaan...", - "draft_saved": "Concept opgeslagen", - "save_failed": "Opslaan mislukt", - "to_placeholder": "E-mailadressen van ontvangers", - "cc_placeholder": "CC-ontvangers", - "bcc_placeholder": "BCC-ontvangers", - "subject_placeholder": "Onderwerp", - "cc_label": "CC:", - "bcc_label": "BCC:", - "subject_label": "Onderwerp:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(Geen onderwerp)", - "unknown_sender": "Onbekend", - "quote": { - "reply_header": "Op {date} schreef {sender}:", - "forward_header": "---------- Doorgestuurd bericht ----------", - "from": "Van: {sender}", - "date": "Datum: {date}", - "subject": "Onderwerp: {subject}", - "to": "Aan: {recipients}" - }, - "remove_sub_address": "Sub-adres verwijderen", - "from_override": { - "toggle_off": "Overschrijven", - "toggle_on": "Overschrijven annuleren", - "toggle_tooltip": "Bewerk de naam en het adres van de afzender vrij. E-mail wordt nog steeds via je identiteit verzonden.", - "name_label": "Afzendernaam", - "name_placeholder": "Naam", - "email_label": "E-mailadres afzender", - "email_placeholder": "alias@example.com", - "envelope_notice": "Als je server dit adres niet accepteert als envelopafzender, wordt daar {identity} gebruikt, en ontvangers kunnen dat zien in de Return-Path-header." - }, - "use_template": "Sjabloon", - "save_as_template": "Opslaan als sjabloon", - "validation": { - "recipient_required": "Voeg een ontvanger toe om te verzenden", - "body_required": "Schrijf een bericht of voeg een bestand toe", - "attachments_uploading": "Bijlagen worden nog geüpload — wordt verzonden zodra dit klaar is", - "attachment_upload_failed": "Niet verzonden - een bijlage kon niet worden geüpload. Verwijder deze en probeer het opnieuw." - }, - "upload_progress": "Uploaden {uploaded} / {total}", - "upload_cancel": "Upload annuleren", - "upload_failed": "Uploaden van {filename} mislukt", - "attachment_too_large": "\"{name}\" is groter dan de server toestaat ({max} per bestand)", - "attachments_total_too_large": "De bijlagen zouden de serverlimiet van {max} per bericht overschrijden", - "send_failed": "E-mail verzenden mislukt", - "smime_sign_on": "S/MIME-ondertekening ingeschakeld", - "smime_sign_off": "S/MIME-ondertekening uitgeschakeld", - "smime_encrypt_on": "S/MIME-versleuteling ingeschakeld", - "smime_encrypt_off": "S/MIME-versleuteling uitgeschakeld", - "smime_encrypt_unavailable": "S/MIME-versleuteling niet beschikbaar – ontbrekende ontvangercertificaten", - "smime_unlock_title": "S/MIME-sleutel ontgrendelen", - "smime_unlock_message": "Voer de wachtwoordzin in om uw S/MIME-sleutel te ontgrendelen.", - "smime_unlock_button": "Ontgrendelen", - "smime_passphrase_placeholder": "S/MIME-wachtwoordzin", - "send_timeout": "De server reageert niet meer. Controleer de map Verzonden voordat u opnieuw verstuurt — het bericht is mogelijk al verzonden.", - "continue_draft": "Concept voortzetten", - "close_draft_title": "Concept opslaan of verwijderen?", - "close_draft_message": "U heeft niet-opgeslagen wijzigingen. Wilt u dit als concept opslaan of verwijderen?", - "drop_files": "Sleep bestanden om bij te voegen", - "show_less": "Minder tonen", - "forgot_attachment": { - "title": "Bent u een bijlage vergeten?", - "message": "Uw bericht vermeldt \"{keyword}\" maar er is geen bestand bijgevoegd. Toch verzenden?", - "send_anyway": "Toch verzenden", - "back": "Terug naar bewerken" - }, - "empty_subject": { - "title": "Verzenden zonder onderwerp?", - "message": "Dit bericht heeft geen onderwerp. Toch verzenden?", - "dont_ask_again": "Niet meer vragen", - "send_anyway": "Toch verzenden", - "back": "Terug naar bewerken" - }, - "add_link": "Link toevoegen", - "link_url_prompt": "Voer de URL in", - "sending": "Bezig met verzenden...", - "schedule_send": "Verzenden plannen", - "schedule_send_description": "Kies wanneer de server dit bericht moet vrijgeven.", - "schedule_send_required": "Kies een datum en tijd.", - "schedule_send_invalid": "Voer een geldige datum en tijd in.", - "schedule_send_future": "Kies een toekomstige datum en tijd.", - "schedule_send_too_late": "Dit tijdstip is later dan de server toestaat.", - "schedule_send_unsupported": "Gepland verzenden wordt niet ondersteund voor dit account.", - "schedule_send_cleanup_warning": "Gepland verzenden is aangemaakt, maar het opschonen van de conceptversie is mislukt.", - "send_delay_unsupported": "Verzendvertraging wordt niet ondersteund voor dit account.", - "send_delay_unsupported_confirm": "Dit account ondersteunt geen verzendvertraging. Meteen verzenden?", - "recipient_edit_email": "E-mailadres bewerken", - "recipient_edit_name": "Weergavenaam bewerken", - "recipient_email_placeholder": "E-mailadres", - "recipient_name_placeholder": "Weergavenaam", - "autocomplete_search_server": "Op de server zoeken", - "autocomplete_searching": "Bezig met zoeken...", - "toolbar": { - "bold": "Vet", - "italic": "Cursief", - "underline": "Onderstrepen", - "strikethrough": "Doorhalen", - "text_color": "Tekstkleur", - "remove_color": "Kleur verwijderen", - "heading_1": "Kop 1", - "heading_2": "Kop 2", - "bullet_list": "Opsommingslijst", - "ordered_list": "Genummerde lijst", - "quote": "Citaat", - "code_block": "Codeblok", - "align_left": "Links uitlijnen", - "align_center": "Centreren", - "align_right": "Rechts uitlijnen", - "text_direction": "Tekstrichting (RTL/LTR)", - "link": "Link", - "table": "Tabel", - "clear_formatting": "Opmaak wissen", - "undo": "Ongedaan maken", - "redo": "Opnieuw", - "add_row_above": "Rij erboven toevoegen", - "add_row_below": "Rij eronder toevoegen", - "add_column_before": "Kolom ervoor toevoegen", - "add_column_after": "Kolom erna toevoegen", - "delete_row": "Rij verwijderen", - "delete_column": "Kolom verwijderen", - "toggle_header_row": "Koprij aan/uit", - "delete_table": "Tabel verwijderen", - "pick_size": "Grootte kiezen", - "font_size": "Lettergrootte", - "font_size_default": "Standaard", - "background_color": "Achtergrondkleur", - "remove_background_color": "Achtergrondkleur verwijderen" - }, - "send_filing_warning": "Verzonden - maar het opruimen daarna is mislukt, mogelijk blijft een oud concept staan." - }, - "confirm_dialog": { - "confirm": "Bevestigen", - "cancel": "Annuleren" - }, - "common": { - "loading": "Laden...", - "error": "Fout", - "success": "Geslaagd", - "cancel": "Annuleren", - "save": "Opslaan", - "delete": "Verwijderen", - "edit": "Bewerken", - "close": "Sluiten", - "search": "Zoeken", - "refresh": "Vernieuwen", - "settings": "Instellingen", - "help": "Help", - "logout": "Uitloggen", - "yes": "Ja", - "no": "Nee", - "unknown": "Onbekend", - "app_title": "Webmail", - "reconnecting": "Verbinding verloren. Opnieuw verbinden…", - "rate_limited_title": "Serverauthenticatie is tijdelijk beperkt.", - "rate_limited_detail": "Bulwark heeft achtergrondverzoeken gepauzeerd om een blokkade te voorkomen. Nieuwe poging over {seconds}s.", - "rate_limited_action_title": "Verzoek gepauzeerd om blokkade te voorkomen.", - "rate_limited_action_detail": "Bulwark wacht tot de servercooldown voorbij is voordat er nieuwe geauthenticeerde verzoeken worden verzonden. Probeer het over {seconds}s opnieuw.", - "pull_to_refresh": "Trek om te vernieuwen", - "release_to_refresh": "Laat los om te vernieuwen", - "refreshing": "Vernieuwen…" - }, - "notifications": { - "email_sent": "E-mail succesvol verzonden", - "email_deleted": "E-mail verwijderd", - "email_archived": "E-mail gearchiveerd", - "emails_deleted": "{count, plural, one {# e-mail verwijderd} other {# e-mails verwijderd}}", - "emails_archived": "{count, plural, one {# e-mail gearchiveerd} other {# e-mails gearchiveerd}}", - "error_archiving": "E-mail kon niet worden gearchiveerd", - "error_updating": "E-mail kon niet worden bijgewerkt", - "email_starred": "Ster toegevoegd aan e-mail", - "email_unstarred": "Ster verwijderd van e-mail", - "email_marked_read": "E-mail gemarkeerd als gelezen", - "email_marked_unread": "E-mail gemarkeerd als ongelezen", - "copied_to_clipboard": "Gekopieerd naar klembord", - "source_copied": "Bron gekopieerd naar klembord", - "error_sending": "Kan e-mail niet verzenden", - "error_deleting": "Kan e-mail niet verwijderen", - "error_loading": "Kan e-mails niet laden", - "new_email": "Nieuwe e-mail", - "new_email_from": "Van {sender}", - "click_to_view": "Klik om te bekijken", - "email_moved": "E-mail verplaatst", - "emails_moved": "{count} e-mails verplaatst", - "moved_to_mailbox": "Verplaatst naar {mailbox}", - "move_failed": "Verplaatsen mislukt", - "move_error": "Kan e-mails niet verplaatsen naar de geselecteerde map", - "email_tagged": "E-mail gelabeld", - "emails_tagged": "{count} e-mails gelabeld", - "tag_failed": "Labelen mislukt", - "identity_created": "Identiteit succesvol aangemaakt", - "identity_updated": "Identiteit succesvol bijgewerkt", - "identity_deleted": "Identiteit verwijderd", - "identity_set_primary": "Primaire identiteit bijgewerkt", - "identity_create_failed": "Kan identiteit niet aanmaken: {error}", - "identity_update_failed": "Kan identiteit niet bijwerken: {error}", - "identity_delete_failed": "Kan identiteit niet verwijderen: {error}", - "identity_unauthorized": "Je bent niet geautoriseerd om vanaf dit e-mailadres te verzenden", - "identity_not_found": "Identiteit niet gevonden", - "vacation_saved": "Afwezigheidsinstellingen opgeslagen", - "vacation_save_failed": "Kan afwezigheidsinstellingen niet opslaan", - "filters_saved": "Filters succesvol opgeslagen", - "filters_save_failed": "Filters opslaan mislukt", - "filters_deleted": "Filterregel verwijderd", - "templates_exported": "Sjablonen succesvol geëxporteerd", - "templates_imported": "{count, plural, one {# sjabloon geïmporteerd} other {# sjablonen geïmporteerd}}", - "templates_import_errors": "Sommige sjablonen konden niet worden geïmporteerd", - "templates_import_empty": "Geen sjablonen gevonden in het bestand", - "export_email_error": "Kan e-mail niet exporteren", - "import_email_success": "E-mail succesvol geïmporteerd", - "import_email_error": "Kan e-mail niet importeren" - }, - "date": { - "today": "Vandaag", - "yesterday": "Gisteren", - "this_week": "Deze week", - "last_week": "Vorige week", - "this_month": "Deze maand", - "older": "Ouder", - "just_now": "Zojuist", - "minutes_ago": "{count} minuut geleden", - "minutes_ago_plural": "{count} minuten geleden", - "hours_ago": "{count} uur geleden", - "hours_ago_plural": "{count} uur geleden", - "days_ago": "{count} dag geleden", - "days_ago_plural": "{count} dagen geleden" - }, - "language": { - "title": "Taal", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Selecteer taal", - "switch_to_english": "Overschakelen naar Engels", - "switch_to_french": "Overschakelen naar Frans", - "switch_to_japanese": "Overschakelen naar Japans", - "switch_to_spanish": "Overschakelen naar Spaans", - "switch_to_italian": "Overschakelen naar Italiaans", - "switch_to_german": "Overschakelen naar Duits", - "switch_to_dutch": "Overschakelen naar Nederlands", - "switch_to_portuguese": "Overschakelen naar Portugees", - "switch_to_russian": "Overschakelen naar Russisch", - "switching": "Taal wijzigen...", - "switch": "Taal wijzigen", - "current": "Huidige taal", - "polish": "Polski", - "switch_to_polish": "Overschakelen naar Pools", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Overschakelen naar Farsi", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Overschakelen naar Mongools" - }, - "settings": { - "title": "Instellingen", - "back_to_mail": "Terug naar Mail", - "save_success": "Instellingen succesvol opgeslagen", - "import_success": "Instellingen succesvol geïmporteerd", - "import_error": "Kan instellingen niet importeren", - "reset_confirm": "Weet je zeker dat je alle instellingen wilt resetten naar de standaardwaarden?", - "unsaved_changes": "U heeft niet-opgeslagen wijzigingen", - "discard_changes": "Wijzigingen verwerpen?", - "discard": "Verwerpen", - "keep_editing": "Doorgaan met bewerken", - "tabs": { - "appearance": "Uiterlijk", - "language": "Taal, Regio & Tijd", - "email": "E-mailgedrag", - "composer": "Opstellen", - "privacy": "Privacy & Beveiliging", - "account": "Account", - "identities": "Identiteiten", - "vacation": "Afwezigheidsmelder", - "advanced": "Geavanceerd", - "calendar": "Agenda", - "filters": "Filters", - "templates": "Sjablonen", - "folders": "Mappen", - "keywords": "Labels", - "security": "Beveiliging", - "encryption": "Versleuteling", - "files": "Bestanden", - "contacts": "Contacten", - "protocol_handlers": "Standaardapps", - "sidebar_apps": "Zijbalk-apps", - "notifications": "Meldingen", - "layout": "Indeling", - "reading": "Lezen", - "composing": "Opstellen", - "downloads": "Downloads", - "content_senders": "Inhoud en afzenders", - "about_data": "Over en gegevens", - "debug": "Debuggen" - }, - "tab_groups": { - "general": "Algemeen", - "account": "Account & identiteit", - "organization": "E-mailorganisatie", - "apps": "Apps", - "system": "Systeem", - "appearance": "Weergave", - "mail": "E-mail", - "privacy": "Privacy en beveiliging", - "advanced": "Geavanceerd" - }, - "appearance": { - "message_list_order": { - "label": "Volgorde van de berichtenlijst", - "description": "Hoe berichten in de lijst worden gesorteerd. De server sorteert, dus het geldt voor de hele map en niet alleen voor de geladen berichten.", - "preset": { - "chronological": "Chronologisch (nieuwste eerst)", - "unread_first": "Ongelezen eerst", - "starred_first": "Met ster eerst", - "tagged_first": "Met label eerst", - "custom": "Aangepast" - }, - "tag_label": "Label", - "advanced_show": "Geavanceerd: sorteerniveaus bewerken", - "advanced_hide": "Geavanceerd verbergen", - "criterion_label": "Criterium van niveau {n}", - "direction_label": "Richting van niveau {n}", - "criteria": { - "unread": "Ongelezen", - "starred": "Met ster", - "tag": "Label", - "receivedAt": "Ontvangstdatum", - "sentAt": "Verzenddatum", - "from": "Afzender", - "subject": "Onderwerp", - "size": "Grootte" - }, - "direction": { - "unread_first": "Ongelezen eerst", - "read_first": "Gelezen eerst", - "starred_first": "Met ster eerst", - "unstarred_first": "Zonder ster eerst", - "tagged_first": "Met label eerst", - "untagged_first": "Zonder label eerst", - "newest_first": "Nieuwste eerst", - "oldest_first": "Oudste eerst", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Grootste eerst", - "smallest_first": "Kleinste eerst" - }, - "add_level": "Niveau toevoegen", - "remove_level": "Niveau verwijderen", - "move_up": "Omhoog", - "move_down": "Omlaag", - "tiebreak_note": "Maximaal drie niveaus; latere niveaus beslissen bij gelijke stand. 'Nieuwste eerst' wordt altijd als laatste toegepast.", - "scope": { - "label": "Op alle mappen toepassen", - "description": "Uitgeschakeld geldt de volgorde alleen voor het Postvak IN; andere mappen blijven chronologisch." - }, - "unsupported_note": "Je mailserver ondersteunt geen sortering op leesstatus, ster of label. Die niveaus worden genegeerd en de lijst blijft chronologisch." - }, - "title": "Uiterlijk", - "description": "Pas het uiterlijk van je webmail aan", - "theme": { - "label": "Thema", - "description": "Kies je voorkeurkleurenschema", - "light": "Licht", - "dark": "Donker", - "system": "Systeem" - }, - "language": { - "label": "Taal", - "description": "Kies je voorkeurstaal" - }, - "font_size": { - "label": "Lettergrootte", - "description": "Pas de tekstgrootte aan voor betere leesbaarheid", - "small": "Klein", - "medium": "Middel", - "large": "Groot" - }, - "list_density": { - "label": "Dichtheid", - "description": "Regeleer de ruimte in de gehele interface", - "extra_compact": "Extra compact", - "compact": "Compact", - "regular": "Normaal", - "comfortable": "Comfortabel" - }, - "animations": { - "label": "Animaties inschakelen", - "description": "Vloeiende overgangen en effecten tonen" - }, - "toolbar_position": { - "label": "Werkbalkpositie", - "description": "Waar de e-mailactieknoppen worden weergegeven (Beantwoorden, Archiveren, Verwijderen, enz.)", - "top": "Bovenaan", - "below_subject": "Onder het onderwerp" - }, - "toolbar_labels": { - "label": "Werkbalklabels tonen", - "description": "Tekstlabels naast werkbalkpictogrammen weergeven. Uitschakelen om ruimte te besparen als u de pictogrammen kent." - }, - "hide_account_switcher": { - "label": "Accountwisselaar in zijbalk verbergen", - "description": "Verberg de accountselector bovenaan de mappenzijbalk. U kunt nog steeds van account wisselen via de onderste navigatiebalk." - }, - "show_rail_account_list": { - "label": "Accountavatars tonen op navigatiebalk", - "description": "Toon individuele accountcirkels onderaan de navigatiebalk voor snel wisselen, met een afmeldknop eronder." - }, - "unified_mailbox": { - "label": "Gecombineerd postvak", - "description": "Gecombineerde mappen (Postvak IN, Verzonden, enz.) van alle verbonden accounts weergeven", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Groepspostvakken meenemen", - "description": "Gedeelde/groepspostvakken ook in de gecombineerde weergave opnemen." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Gekleurde zijbalkpictogrammen", - "description": "Kleur map- en tagpictogrammen op type (blauw Postvak IN, rood Spam, groen Verzonden, enz.). Schakel uit voor een monochrome zijbalk." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Totaal aantal berichten tonen", - "description": "Toont het totale aantal berichten naast mappen en labels, naast het aantal ongelezen berichten. Schakel uit om alleen ongelezen aantallen te tonen." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro-interface (experimenteel)", - "description": "Power user-indeling alleen voor desktop met meertabs berichtweergave en accountoverschrijdende workflows. De standaardinterface blijft onveranderd; u kunt op elk moment terugkeren.", - "open_label": "Pro-interface openen", - "back_to_standard": "Terug naar standaard" - }, - "cross_unread": { - "label": "Alle accounts: Ongelezen", - "description": "Toont een item in de sectie Alle accounts met ongelezen e-mail van alle accounts, in alle mappen behalve ongewenst, verzonden, archief, prullenbak en concepten." - }, - "cross_starred": { - "label": "Alle accounts: Met ster", - "description": "Toont een item in de sectie Alle accounts met e-mail met ster van alle accounts, in alle mappen behalve ongewenst, verzonden, archief, prullenbak en concepten." - }, - "cross_all": { - "label": "Alle accounts: Alle e-mail", - "description": "Toont een item in de sectie Alle accounts met alle e-mail van alle accounts, in alle mappen behalve ongewenst, verzonden, archief, prullenbak en concepten." - } - }, - "keywords": { - "title": "E-maillabels", - "description": "Definieer labels om uw e-mails te organiseren. Deze worden opgeslagen als JMAP-trefwoorden op de server.", - "add_keyword": "Label toevoegen", - "label_field": "Weergavenaam", - "label_placeholder": "bijv. Werk, Persoonlijk, Urgent", - "id_field": "Label-ID", - "id_placeholder": "bijv. werk, persoonlijk", - "color_field": "Kleur", - "id_exists": "Deze label-ID bestaat al", - "edit": "Label bewerken", - "delete": "Label verwijderen", - "save": "Opslaan", - "add": "Toevoegen", - "cancel": "Annuleren", - "migrating": "Label bijwerken op bestaande e-mails…", - "migration_error": "Label bijwerken op bestaande e-mails mislukt", - "nesting": { - "label": "Geneste labels", - "description": "Nest labels onder andere labels en toon ze als een boomstructuur in de zijbalk." - }, - "parent_field": "Bovenliggend label", - "no_parent": "Geen bovenliggend label", - "too_long": "Dit labelpad is te lang (maximaal {max} tekens)", - "has_children_locked": "Er vallen andere labels onder dit label, dus de naam en het bovenliggende label liggen vast. Verplaats of verwijder ze eerst.", - "has_children_delete": "Verwijder eerst de labels die hieronder vallen", - "visibility_field": "Zichtbaarheid in de zijbalk", - "visibility": { - "show": "Tonen", - "unread": "Tonen bij ongelezen", - "hide": "Verbergen" - }, - "unrecognized": { - "title": "Labels van de server herstellen", - "description": "Labelnamen en -kleuren worden in deze browser bewaard, maar de labels zelf staan als JMAP-trefwoorden op uw e-mails. Doorzoek uw e-mail op trefwoorden waarvoor hier geen label bestaat en voeg ze weer toe.", - "scan": "Zoeken naar labels", - "rescan": "Opnieuw zoeken", - "scanning": "Bezig met zoeken… {scanned} van {total} e-mails", - "none": "Alle labels op uw e-mails zijn hier al gedefinieerd.", - "messages": "{count, plural, one {# e-mail} other {# e-mails}}", - "add_all": "Alle {count} toevoegen", - "partial": "Alleen de {scanned} meest recente van {total} e-mails zijn doorzocht. Labels die alleen op oudere e-mails staan, ontbreken mogelijk.", - "error": "Kan uw e-mail niet doorzoeken op labels." - } - }, - "notifications": { - "test_sound": "Meldingsgeluid testen", - "sounds": { - "default": "Standaard (Pieptoon)", - "cheerful": "Vrolijk", - "involved": "Uitgebreid", - "swift": "Snel gebaar", - "relax": "Ontspannen" - }, - "sound_selection": { - "title": "Meldingsgeluid", - "description": "Kies welk geluid wordt afgespeeld voor meldingen", - "choose": "Geluid", - "choose_desc": "Selecteer een meldingstoon en klik op het luidsprekerpictogram voor een voorbeeld" - }, - "email": { - "title": "E-mailmeldingen", - "description": "Meldingen voor inkomende e-mails configureren", - "enabled": "E-mailmeldingen", - "enabled_desc": "Meldingen tonen wanneer nieuwe e-mails binnenkomen", - "sound": "Meldingsgeluid", - "sound_desc": "Een geluid afspelen wanneer nieuwe e-mails binnenkomen", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Agendameldingen", - "description": "Meldingen voor agenda-evenementen configureren", - "enabled": "Evenementmeldingen", - "enabled_desc": "Waarschuwingen tonen voor aankomende agenda-evenementen", - "sound": "Meldingsgeluid", - "sound_desc": "Een geluid afspelen voor agendaherinneringen", - "invitation_parsing": "E-mailuitnodigingen herkennen", - "invitation_parsing_desc": "Agenda-uitnodigingen in e-mailbijlagen detecteren en agendaacties tonen" - }, - "push": { - "confirm_disable_message": "Dit apparaat ontvangt geen meldingen meer als de site is gesloten.", - "confirm_disable_title": "Achtergrondmeldingen uitschakelen?", - "description": "Ontvang systeemmeldingen voor nieuwe e-mail wanneer deze site gesloten is. Geleverd via de Bulwark-pushrelay; de relay ziet nooit e-mailinhoud.", - "disable": "Uitschakelen", - "enable": "Inschakelen", - "dismiss_aria": "Meldingsvenster sluiten", - "ios_hint": "Installeer de site op iOS eerst op het beginscherm – Safari levert Web Push alleen aan geïnstalleerde PWA's.", - "reenable": "Opnieuw registreren", - "relay_desc": "Gebruikt standaard de gehoste Bulwark-relay. Wijzig alleen als je zelf hostt.", - "relay_label": "Push-relay", - "relay_locked": "Ingesteld door beheerder", - "relay_locked_desc": "De push-relay-URL is ingesteld door je beheerder en kan niet worden gewijzigd.", - "status_active": "Actief op dit apparaat", - "status_busy": "Bezig…", - "status_inactive": "Niet ingeschakeld op dit apparaat", - "status_unsupported": "Deze browser ondersteunt geen Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Achtergrondmeldingen", - "devices_title": "Geregistreerde apparaten", - "devices_desc": "Alle apparaten die achtergrondmeldingen voor dit account ontvangen. Trek je er een in, dan stopt de ontvangst meteen.", - "devices_loading": "Geregistreerde apparaten laden…", - "devices_empty": "Er zijn geen apparaten geregistreerd voor achtergrondmeldingen.", - "devices_error": "Kan de geregistreerde apparaten niet laden.", - "devices_refresh": "Vernieuwen", - "device_this": "Dit apparaat", - "device_other": "Ander apparaat", - "device_status_active": "Levert meldingen af", - "device_status_inactive": "Heeft nooit een melding afgeleverd", - "device_status_unknown": "Niet bekend bij deze relay", - "device_expires": "Verloopt op {date}", - "revoke": "Intrekken", - "confirm_revoke_title": "Dit apparaat intrekken?", - "confirm_revoke_message": "Het ontvangt meteen geen achtergrondmeldingen meer. Push opnieuw inschakelen op dat apparaat registreert het opnieuw.", - "confirm_revoke_message_this": "Dit apparaat ontvangt meteen geen achtergrondmeldingen meer." - } - }, - "language_region": { - "title": "Taal, Regio & Tijd", - "description": "Taal, datumnotatie, tijdnotatie en andere regionale voorkeuren", - "language": { - "label": "Taal", - "description": "Kies je voorkeurstaal", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Datumnotatie", - "description": "Hoe datums worden weergegeven in de e-maillijst", - "smart": "Slim (regionaal)", - "relative": "Relatief (1 u geleden, 2 d geleden)", - "full": "Altijd volledige datum", - "preview_today": "Vandaag:", - "preview_this_week": "Deze week:", - "preview_older": "Ouder:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Tijdnotatie", - "description": "Kies tussen 12-uurs of 24-uurs klok", - "12h": "12-uurs", - "24h": "24-uurs" - }, - "first_day": { - "label": "Eerste dag van de week", - "description": "Begin de week op zondag of maandag", - "sunday": "Zondag", - "monday": "Maandag" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Zoekopdracht wissen bij wisselen van map", - "description": "Op een map klikken (Postvak IN, Verzonden…) wist de actieve zoekopdracht en toont die map. Uitgeschakeld blijft de zoekopdracht staan en wordt opnieuw uitgevoerd in de gekozen map." - }, - "request_read_receipt": { - "label": "Standaard om leesbevestiging vragen", - "description": "De aanvraag voor een leesbevestiging vooraf inschakelen bij het opstellen van een nieuw bericht." - }, - "read_receipt_response": { - "label": "Reageren op verzoeken om leesbevestiging", - "description": "Wat te doen wanneer een inkomend bericht om een leesbevestiging vraagt.", - "ask": "Elke keer vragen", - "always": "Altijd verzenden", - "never": "Nooit verzenden" - }, - "title": "E-mailgedrag", - "description": "Configureer hoe e-mails worden verwerkt", - "mark_read": { - "label": "Markeren als gelezen", - "description": "Wanneer e-mails als gelezen worden gemarkeerd bij openen", - "instant": "Direct", - "delay_3s": "Na 3 seconden", - "delay_5s": "Na 5 seconden", - "never": "Nooit" - }, - "delete_action": { - "label": "Verwijderactie", - "description": "Wat er gebeurt wanneer je een e-mail verwijdert", - "trash": "Verplaatsen naar prullenbak", - "trash_and_read": "Verplaatsen naar prullenbak en als gelezen markeren", - "permanent": "Permanent verwijderen", - "warning": "E-mails worden permanent verwijderd en kunnen niet worden hersteld. Deze actie is onomkeerbaar." - }, - "message_spacing": { - "label": "Berichtmarge", - "description": "Ruimte rond de berichtinhoud in de leesweergave", - "auto": "Automatisch (aanbevolen)", - "always": "Altijd ruimte toevoegen", - "edge": "Rand tot rand" - }, - "plain_text_font": { - "label": "Lettertype voor platte tekst", - "description": "Lettertype voor e-mails in platte tekst", - "mono": "Monospace", - "sans": "Normaal (zoals de app)" - }, - "archive_mode": { - "label": "Archiveren in", - "description": "Hoe e-mails bij archivering worden georganiseerd", - "single": "Eén enkele map", - "year": "Een map per jaar", - "month": "Een map per maand", - "reorganize": "Bestaand archief herorganiseren", - "reorganize_success": "{count, plural, =0 {Geen e-mails om te herorganiseren} =1 {1 e-mail geherorganiseerd} other {# e-mails geherorganiseerd}}", - "reorganize_error": "Archief herorganiseren mislukt" - }, - "permanently_delete_junk": { - "label": "Spam permanent verwijderen", - "description": "E-mails uit de map Spam permanent verwijderen in plaats van naar de prullenbak te verplaatsen" - }, - "mail_layout": { - "label": "E-mailindeling", - "description": "Kies tussen het klassieke gesplitste leesvenster en een gefocuste leesstroom in Gmail-stijl.", - "split": "Gesplitst venster", - "split_description": "Houd de berichtenlijst en het leesvenster naast elkaar zichtbaar.", - "focus": "Gefocuste lijst", - "focus_description": "Toon één regel per bericht en open e-mail op volledige breedte terwijl de mappenzijbalk zichtbaar blijft.", - "horizontal": "Leesvenster onderaan", - "horizontal_description": "Toon de berichtenlijst bovenaan en open het geselecteerde bericht in een leesvenster eronder." - }, - "disable_threading": { - "label": "Conversatiegroepering uitschakelen", - "description": "Toon e-mails als afzonderlijke berichten in plaats van gegroepeerd op gesprek" - }, - "plain_text_mode": { - "label": "Alleen platte tekst", - "description": "Schakel de rich text-editor uit en verzend alle e-mails alleen als platte tekst, inclusief antwoorden en doorgestuurde berichten" - }, - "auto_select_reply_identity": { - "label": "Antwoordadres automatisch selecteren", - "description": "Schakel bij het beantwoorden automatisch het Van-adres om naar de identiteit die het oorspronkelijke bericht ontving" - }, - "reply_identity_match": { - "label": "Overeenkomst van ontvangstadres", - "description": "Welke ontvangstadressen als de uwe gelden. „Alleen exact adres“ kiest uitsluitend een van uw ingestelde identiteiten; „zelfde domein“ behandelt ook elk ander adres op uw identiteitsdomeinen als catch-all-alias en herschrijft de Van-header ernaar. Kies exact adres als die adressen distributielijsten zijn.", - "exact": "Alleen exact adres", - "domain": "Elk adres op mijn domeinen" - }, - "signature_position": { - "label": "Positie van handtekening", - "description": "Waar je handtekening in antwoorden en doorgestuurde berichten moet worden ingevoegd. Boven de geciteerde tekst leest natuurlijk als afsluiting van het antwoord; eronder houdt het originele bericht aaneengesloten.", - "above_quote": "Voor geciteerde tekst", - "below_quote": "Na geciteerde tekst" - }, - "signature_separator": { - "label": "Handtekening-scheider", - "description": "De handtekening voorafgaan met de standaard scheidingsregel \"-- \" (RFC 3676). Zet uit als je liever direct van het bericht in de handtekening overgaat." - }, - "sub_address_delimiter": { - "label": "Sub-adres scheidingsteken", - "description": "Teken dat je gebruikersnaam scheidt van het sub-adres-label. Gebruik het scheidingsteken dat je mailserver gebruikt (bv. gebruiker{delimiter}tag@domein.nl).", - "option": "{delimiter} (gebruiker{delimiter}tag@domein.nl)", - "custom": "Aangepast…", - "custom_input_label": "Aangepast scheidingsteken" - }, - "show_preview": { - "label": "Voorbeeldtekst tonen", - "description": "E-mailvoorbeeld weergeven in de lijst", - "focus_description": "Inline voorbeeldtekst weergeven in de gefocuste berichtenlijst met één regel" - }, - "attachment_click_action": { - "label": "Actie bij klikken op bijlagen", - "description": "Kies of een klik op een bijlage een voorbeeld opent of direct downloadt", - "preview": "Voorbeeld tonen indien mogelijk", - "download": "Direct downloaden" - }, - "attachment_position": { - "label": "Positie van bijlagen", - "description": "Waar bijlagen in de e-mailkop worden weergegeven", - "beside-sender": "Naast de afzender", - "below-header": "Onder de kop" - }, - "emails_per_page": { - "10": "10 e-mails", - "25": "25 e-mails", - "50": "50 e-mails", - "100": "100 e-mails", - "label": "E-mails per pagina", - "description": "Aantal e-mails dat in één keer wordt geladen" - }, - "always_light_mode": { - "label": "E-mails altijd in lichte modus weergeven", - "description": "E-mailinhoud in lichte modus weergeven, zelfs wanneer de app in donkere modus staat, om problemen met donkere modus-conversie te voorkomen" - }, - "external_content": { - "label": "Externe inhoud", - "description": "Hoe afbeeldingen en externe inhoud moeten worden verwerkt", - "ask": "Altijd vragen", - "block": "Altijd blokkeren", - "allow": "Altijd toestaan" - }, - "trusted_senders": { - "label": "Vertrouwde afzenders", - "description": "Beheer afzenders van wie afbeeldingen automatisch worden geladen", - "count_zero": "Geen", - "count_one": "1 afzender", - "count_other": "{count} afzenders", - "modal_title": "Vertrouwde afzenders", - "empty_title": "Nog geen vertrouwde afzenders", - "empty_description": "Wanneer je een e-mail bekijkt met geblokkeerde afbeeldingen, klik op \"Deze afzender altijd vertrouwen\" om ze hier toe te voegen.", - "add_manually": "Afzender handmatig toevoegen", - "add_button": "Toevoegen", - "add_placeholder": "Voer e-mailadres in", - "search_placeholder": "Zoek afzenders...", - "no_results": "Geen afzenders komen overeen met je zoekopdracht", - "remove": "Verwijderen", - "close": "Sluiten", - "invalid_email": "Voer een geldig e-mailadres in", - "already_added": "Deze afzender wordt al vertrouwd", - "save_error": "Opslaan mislukt - controleer het Contacten-debuglog voor details", - "use_address_book_label": "Synchroniseren met adresboek", - "use_address_book_description": "Sla vertrouwde afzenders op in een apart \"Vertrouwde afzenders\"-adresboek zodat ze op al je apparaten synchroniseren" - }, - "hover_actions": { - "label": "Snelle hover-acties", - "description": "Kies welke snelle acties verschijnen wanneer u over een e-mail in de lijst beweegt", - "delete": "Verwijderen", - "star": "Ster aan / uit", - "mark_read": "Markeer gelezen / ongelezen", - "archive": "Archiveren", - "tag": "Label", - "spam": "Markeer als spam", - "not_spam": "Geen spam", - "none_selected": "Geen acties geselecteerd", - "mode_label": "Weergavemodus", - "mode_inline": "Inline", - "mode_floating": "Zwevend", - "corner_label": "Zwevende positie", - "corner_top-left": "Linksboven", - "corner_top-right": "Rechtsboven", - "corner_bottom-left": "Linksonder", - "corner_bottom-right": "Rechtsonder" - }, - "default_mail_program": { - "label": "Standaard e-mailprogramma", - "description": "Registreer {appName} als uw standaard e-mailprogramma voor mailto:-links", - "button": "Instellen als standaard", - "success": "Browser gevraagd om als standaard in te stellen", - "error": "Uw browser ondersteunt deze functie niet" - }, - "attachment_reminder": { - "label": "Bijlageherinnering", - "description": "Waarschuwen vóór verzending wanneer uw bericht bijlagen vermeldt maar er geen zijn bijgevoegd", - "keywords_label": "Triggerwoorden", - "keywords_description": "Woorden of zinnen die de herinnering activeren wanneer ze in uw bericht worden gevonden", - "add_placeholder": "Trefwoord toevoegen...", - "add": "Add", - "remove": "Verwijderen" - }, - "empty_subject_warning": { - "label": "Waarschuwing bij leeg onderwerp", - "description": "Vraag om bevestiging voordat een bericht zonder onderwerp wordt verzonden." - }, - "hide_inline_image_attachments": { - "label": "Inline-afbeeldingen verbergen uit bijlagenlijst", - "description": "Afbeeldingen die in de berichttekst zijn ingesloten worden niet als aparte bijlagen weergegeven" - }, - "attachment_image_previews": { - "label": "Voorbeelden van afbeeldingen tonen in bijlagen", - "description": "Toon afbeeldingsbijlagen als miniatuurkaarten in plaats van generieke bestandspictogrammen" - }, - "send_delay": { - "label": "Verzenden ongedaan maken / verzendvertraging", - "description": "Vertraag normale verzending met een korte server-side periode.", - "off": "Uit", - "seconds": "{seconds} seconden", - "unsupported": "Het huidige account meldt geen ondersteuning voor vertraagd verzenden. De instelling blijft opgeslagen voor andere accounts." - }, - "return_to_list_after_action": { - "label": "Terug naar lijst na verwijderen of markeren als ongelezen", - "description": "Na het verwijderen of als ongelezen markeren van het geopende bericht terugkeren naar de berichtenlijst in plaats van het volgende bericht te openen." - }, - "swipe_right_action": { - "label": "Actie bij naar rechts vegen (mobiel)", - "description": "Op aanraakapparaten de actie die wordt uitgevoerd wanneer je een bericht in de lijst naar rechts veegt." - }, - "swipe_left_action": { - "label": "Actie bij naar links vegen (mobiel)", - "description": "Op aanraakapparaten de actie die wordt uitgevoerd wanneer je een bericht in de lijst naar links veegt." - }, - "swipe_actions": { - "none": "Geen", - "archive": "Archiveren", - "delete": "Verwijderen", - "mark_read": "Gelezen omschakelen", - "star": "Ster omschakelen", - "spam": "Markeren als spam" - }, - "rtl_editing": { - "label": "Ondersteuning voor rechts-naar-links bewerken", - "description": "Voegt een richtingsknop toe aan de werkbalk van de editor waarmee alinea's links-naar-rechts of rechts-naar-links kunnen worden ingesteld" - } - }, - "composer": { - "title": "Opstellen", - "description": "Configureer instellingen voor het opstellen van e-mails", - "autosave": { - "label": "Automatisch opslaan interval", - "description": "Hoe vaak concepten automatisch worden opgeslagen", - "30s": "Elke 30 seconden", - "1m": "Elke minuut", - "2m": "Elke 2 minuten", - "5m": "Elke 5 minuten" - }, - "send_confirmation": { - "label": "Verzendbevestiging", - "description": "Vraag om bevestiging voordat e-mails worden verzonden" - }, - "default_reply": { - "label": "Standaard antwoordmodus", - "description": "Standaardactie bij klikken op beantwoorden", - "reply": "Beantwoorden", - "reply_all": "Allen beantwoorden" - } - }, - "privacy": { - "title": "Privacy & Beveiliging", - "description": "Beheer je privacy- en beveiligingsinstellingen", - "external_images": { - "label": "Externe afbeeldingen blokkeren", - "description": "Voorkom tracking via externe afbeeldingen" - }, - "session_timeout": { - "label": "Sessie time-out", - "description": "Automatisch uitloggen na inactiviteit", - "never": "Nooit", - "30m": "30 minuten", - "1h": "1 uur", - "4h": "4 uur" - }, - "clear_cache": { - "label": "Cache wissen", - "description": "Verwijder gecachte gegevens en tijdelijke bestanden", - "button": "Cache wissen", - "confirm": "Weet je zeker dat je de cache wilt wissen?", - "success": "Cache succesvol gewist" - } - }, - "account": { - "title": "Account", - "description": "Bekijk je accountinformatie", - "name_label": "Weergavenaam", - "username_label": "Gebruikersnaam", - "account_type_label": "Accounttype", - "auth_method_label": "Authenticatie", - "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", - "auth_method_basic": "Wachtwoord", - "demo_account": "Demoaccount", - "email": { - "label": "E-mailadres", - "value": "{email}" - }, - "server": { - "label": "JMAP-server", - "value": "{server}" - }, - "storage": { - "label": "Opslaggebruik", - "used": "{used} van {total} gebruikt", - "percentage": "{percent}% gebruikt" - }, - "last_sync": { - "label": "Laatste synchronisatie", - "value": "{time}" - }, - "accounts": { - "title": "Aangemelde accounts", - "description": "Sleep om de volgorde aan te passen waarin accounts in het accountmenu verschijnen", - "active": "Momenteel actief account", - "default_badge": "Standaardaccount", - "set_default": "Als standaard instellen", - "switch_to": "Overschakelen naar dit account", - "move_up": "Omhoog verplaatsen", - "move_down": "Omlaag verplaatsen", - "drag_handle": "Sleep om volgorde aan te passen", - "add": "Account toevoegen" - }, - "shared_accounts": { - "title": "Gedeeld met mij", - "description": "Groeps- en gedeelde accounts die u kunt beheren. Selecteer er een om de filters, afwezigheidsassistent, agenda's en contacten te bewerken.", - "shared_label": "Gedeeld account" - } - }, - "security": { - "title": "Accountbeveiliging", - "description": "Beheer uw wachtwoord, tweefactorauthenticatie en beveiligingsinstellingen", - "detecting": "Servermogelijkheden worden gedetecteerd...", - "not_available": "Accountbeveiligingsbeheer is niet beschikbaar voor deze mailserver. Vereiste machtigingen kunnen zijn uitgeschakeld. Zie de documentatie voor details.", - "password": { - "title": "Wachtwoord wijzigen", - "current": "Huidig wachtwoord", - "new": "Nieuw wachtwoord", - "confirm": "Bevestig nieuw wachtwoord", - "submit": "Wachtwoord wijzigen", - "success": "Wachtwoord succesvol gewijzigd", - "error_title": "Wachtwoord wijzigen mislukt", - "error_mismatch": "Nieuwe wachtwoorden komen niet overeen", - "error_min_length": "Wachtwoord moet minimaal 8 tekens bevatten", - "error_generic": "Kan wachtwoord niet wijzigen" - }, - "display_name": { - "label": "Weergavenaam", - "description": "Uw naam zoals weergegeven op de server", - "placeholder": "Voer uw weergavenaam in", - "save": "Opslaan", - "success": "Weergavenaam bijgewerkt", - "error": "Kan weergavenaam niet bijwerken" - }, - "totp": { - "section_title": "Tweefactorauthenticatie", - "label": "TOTP-authenticatie", - "description": "Voeg een extra beveiligingslaag toe met een op tijd gebaseerd eenmalig wachtwoord", - "active": "Ingeschakeld", - "inactive": "Uitgeschakeld", - "enabled": "Tweefactorauthenticatie ingeschakeld", - "disabled": "Tweefactorauthenticatie uitgeschakeld", - "enable_error": "Kan 2FA niet inschakelen", - "disable_error": "Kan 2FA niet uitschakelen", - "setup_instructions": "Kopieer deze URL naar uw authenticator-app (Google Authenticator, Authy, etc.):", - "verification_code": "Verificatiecode", - "confirm": "Bevestigen", - "disable": "Uitschakelen", - "disable_confirm_prompt": "Voer uw wachtwoord in om tweefactorauthenticatie uit te schakelen.", - "password_required": "Wachtwoord is vereist", - "code_required": "Verificatiecode is vereist", - "code_invalid": "Ongeldige verificatiecode. Controleer uw authenticator-app en probeer het opnieuw." - }, - "app_passwords": { - "title": "App-wachtwoorden", - "description": "Maak wachtwoorden aan voor apps die geen tweefactorauthenticatie ondersteunen", - "add": "Toevoegen", - "create": "Aanmaken", - "cancel": "Annuleren", - "generate": "Genereren", - "name_label": "App-naam", - "name_placeholder": "bijv. Thunderbird, iPhone Mail", - "password_label": "Wachtwoord (leeg laten voor automatisch genereren)", - "password_placeholder": "Automatisch gegenereerd als leeg", - "added": "App-wachtwoord aangemaakt", - "removed": "App-wachtwoord verwijderd", - "add_error": "Kan app-wachtwoord niet aanmaken", - "remove_error": "Kan app-wachtwoord niet verwijderen", - "none": "Geen app-wachtwoorden geconfigureerd", - "done": "Klaar", - "expires_label": "Verloopt (optioneel)", - "copy_now_warning": "Kopieer dit wachtwoord nu - het wordt niet opnieuw weergegeven.", - "allowed_ips_label": "Toegestane IP's (optioneel)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Gescheiden door komma of spatie. Laat leeg om elk IP toe te staan." - }, - "api_keys": { - "title": "API-sleutels", - "description": "Maak API-sleutels voor scripts en integraties die rechtstreeks met de server praten", - "name_label": "Sleutelnaam", - "name_placeholder": "bijv. Back-upscript, CI-runner", - "copy_now_warning": "Kopieer deze API-sleutel nu - hij wordt niet opnieuw getoond.", - "added": "API-sleutel aangemaakt", - "removed": "API-sleutel verwijderd", - "add_error": "API-sleutel aanmaken mislukt", - "remove_error": "API-sleutel verwijderen mislukt", - "none": "Geen API-sleutels geconfigureerd" - }, - "encryption": { - "section_title": "Versleuteling in rust", - "label": "E-mailversleuteling", - "description": "Versleutel opgeslagen e-mails op de server voor extra privacy", - "active": "{type}-versleuteling ingeschakeld", - "inactive": "Uitgeschakeld", - "enabled": "Versleuteling in rust ingeschakeld", - "disabled_success": "Versleuteling in rust uitgeschakeld", - "error": "Kan versleutelingsinstellingen niet bijwerken", - "algorithm_label": "Versleutelingsalgoritme", - "encrypt_on_append": "Nieuwe e-mails versleutelen bij uploaden", - "allow_spam_training": "Spamtraining toestaan voordat e-mails worden versleuteld", - "enable_encryption": "Versleuteling inschakelen", - "enable_tooltip": "Versleuteling met deze sleutel inschakelen", - "configure_title": "Versleuteling met deze sleutel inschakelen", - "enable_button": "Inschakelen", - "disable_tooltip": "Versleuteling uitschakelen", - "enabled_success": "Versleuteling in rust ingeschakeld" - }, - "email_client": { - "title": "E-mailclient instellen", - "description": "Gebruik deze inloggegevens om uw desktop- of mobiele e-mailclient te configureren (Thunderbird, Apple Mail, Outlook, enz.)", - "jmap_username_label": "JMAP-gebruikersnaam", - "copy": "Kopiëren", - "copied": "Gekopieerd", - "password_instructions": "Gebruik uw hierboven vermelde JMAP-gebruikersnaam samen met een app-wachtwoord om in te loggen bij uw e-mailclient. Maak een app-wachtwoord aan in het bovenstaande gedeelte als u dat nog niet heeft gedaan." - }, - "link_device": { - "title": "Mobiele app koppelen", - "description": "Log in bij de Bulwark Mail mobiele app zonder iets te typen. Genereer hier een QR-code en scan deze vanaf het inlogscherm van de app.", - "generate": "QR-code tonen", - "regenerate": "Nieuwe code tonen", - "instructions": "Open de Bulwark Mail app, tik op \"QR-code scannen\" op het inlogscherm en richt uw camera hierop.", - "expires_in": "Deze code verloopt over {seconds} seconden. Hij kan maar één keer worden gebruikt.", - "expired": "Deze code is verlopen.", - "generating": "Genereren…", - "error": "Kon geen koppelingscode aanmaken. Probeer het opnieuw." - }, - "public_keys": { - "name_label": "Naam van openbare sleutel", - "name_placeholder": "bijv. Mijn sleutel", - "key_label": "Openbare sleutel (S/MIME of PGP in ASCII-formaat)", - "key_placeholder": "Plak hier uw openbare sleutel", - "none": "Geen openbare sleutels geconfigureerd" - } - }, - "identities": { - "title": "Verzendidentiteiten", - "description": "Beheer e-mailadressen van waaruit je kunt verzenden", - "identities_count": { - "label": "Je identiteiten", - "description": "E-mailadressen geconfigureerd voor verzenden", - "count_zero": "Geen identiteiten", - "count_one": "1 identiteit", - "count_other": "{count} identiteiten" - }, - "manage": "Identiteiten beheren", - "sub_addressing": { - "label": "Sub-adressering", - "description": "Gebruik tags zoals gebruiker+tag@domein.nl om inkomende mail te organiseren", - "learn_more": "Meer informatie" - } - }, - "vacation": { - "title": "Afwezigheidsmelder", - "description": "Automatisch antwoorden op inkomende e-mails terwijl je afwezig bent", - "loading": "Afwezigheidsinstellingen laden...", - "not_supported": "Je mailserver ondersteunt geen afwezigheidsantwoorden.", - "fetch_error": "Kan afwezigheidsinstellingen niet laden. Probeer het opnieuw.", - "status": { - "label": "Afwezigheidsmelder", - "description": "Stuur een automatisch antwoord naar mensen die je mailen", - "active": "Actief", - "inactive": "Inactief" - }, - "date_range": { - "title": "Periode", - "description": "Beperk optioneel het automatische antwoord tot een specifieke periode", - "start": "Startdatum", - "start_description": "Leeg laten voor geen startlimiet", - "end": "Einddatum", - "end_description": "Leeg laten voor geen eindlimiet" - }, - "message": { - "title": "Automatisch antwoordbericht", - "description": "Het bericht dat als antwoord wordt verzonden", - "subject_label": "Onderwerp", - "subject_description": "Onderwerpregel van het automatische antwoord", - "subject_placeholder": "Afwezigheidsbericht", - "body_label": "Berichttekst", - "body_description": "Inhoud van het bericht in platte tekst", - "body_placeholder": "Bedankt voor je e-mail. Ik ben momenteel niet aanwezig en zal reageren bij terugkomst.", - "html_label": "Opgemaakt bericht (HTML)", - "html_description": "Voeg een opgemaakte versie met links en stijl toe. Ontvangers van wie het e-mailprogramma deze niet kan weergeven, zien de platte tekst hierboven.", - "html_placeholder": "Schrijf een opgemaakt afwezigheidsantwoord…" - }, - "preview": { - "title": "Voorbeeld", - "show": "Voorbeeld tonen", - "hide": "Voorbeeld verbergen" - }, - "save": "Wijzigingen opslaan", - "saving": "Opslaan...", - "warnings": { - "end_before_start": "De einddatum moet na de startdatum liggen", - "start_in_past": "De startdatum ligt in het verleden", - "empty_body": "De berichttekst is leeg - ontvangers krijgen een leeg antwoord", - "subject_too_long": "Het onderwerp is te lang voor de server (maximaal {max} bytes).", - "body_too_long": "Het bericht is te lang voor de server (maximaal {max} bytes, inclusief opmaak)." - } - }, - "folders": { - "role_memos": "Notities", - "title": "Mappen", - "description": "Beheer uw e-mailmappen en wijs standaardrollen toe", - "folder_list": "Uw mappen", - "folder_list_description": "Klik op een mappictogram om het aan te passen", - "standard_roles": "Standaard maprollen", - "standard_roles_description": "Wijs toe welke mappen worden gebruikt voor standaardrollen zoals Postvak IN, Verzonden, Prullenbak, enz.", - "role_inbox": "Postvak IN", - "role_drafts": "Concepten", - "role_sent": "Verzonden", - "role_trash": "Prullenbak", - "role_junk": "Spam / Ongewenst", - "role_archive": "Archief", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Geen", - "create_folder": "Map maken", - "create_subfolder": "Submap maken", - "reorder": "Sleep om te herschikken", - "reorder_error": "Herschikken van mappen is mislukt", - "move_error": "Verplaatsen van map is mislukt", - "subfolder_of": "In {name}", - "subfolder_name": "Naam van submap", - "new_folder_name": "Mapnaam", - "rename": "Hernoemen", - "change_icon": "Pictogram wijzigen", - "delete": "Verwijderen", - "confirm_delete": "Weet u zeker dat u \"{name}\" wilt verwijderen? E-mails in deze map worden naar de Prullenbak verplaatst.", - "create": "Maken", - "cancel": "Annuleren", - "no_folders": "Geen aangepaste mappen", - "cannot_delete_role": "Kan een map met een standaardrol niet verwijderen. Verwijder eerst de rol.", - "folder_created": "Map aangemaakt", - "folder_renamed": "Map hernoemd", - "folder_deleted": "Map verwijderd", - "role_updated": "Maprol bijgewerkt", - "error_create": "Map maken mislukt", - "error_rename": "Map hernoemen mislukt", - "error_delete": "Map verwijderen mislukt", - "error_delete_has_children": "Map kan niet worden verwijderd: deze bevat nog submappen. Verwijder of verplaats deze eerst.", - "error_delete_has_email": "Map kan niet worden verwijderd: deze bevat nog e-mails. Verplaats of verwijder deze eerst.", - "error_role": "Maprol bijwerken mislukt" - }, - "advanced": { - "title": "Geavanceerd", - "description": "Geavanceerde opties en ontwikkelaarsinstellingen", - "debug_mode": { - "label": "Debugmodus", - "description": "Schakel gedetailleerde logging in voor probleemoplossing" - }, - "debug_categories": { - "description": "Selecteer welke categorieën moeten worden gelogd. Schakel categorieën die u niet nodig hebt uit om ruis in de console te verminderen.", - "jmap": "JMAP-client", - "jmap_description": "Mailboxbewerkingen, e-mail ophalen en JMAP-protocolverzoeken", - "calendar": "Agenda", - "calendar_description": "Agenda-afspraken, importen en planningsberichten", - "tasks": "Taken", - "tasks_description": "Aanmaken, ophalen en bijwerken van agendataken", - "auth": "Authenticatie", - "auth_description": "Inloggen, TOTP, tokenuitwisseling en sessiebeheer", - "filters": "Filters", - "filters_description": "Sieve-filterregels en vakantiescripts", - "email": "E-mailweergave", - "email_description": "E-mailrendering, TNEF-verwerking en markeren als gelezen", - "push": "Pushmeldingen", - "push_description": "Instellen en afleveren van pushmeldingen", - "contacts": "Contacten en adresboeken", - "contacts_description": "Contactsynchronisatie, adresboekacties en vertrouwde afzenders" - }, - "settings_sync": { - "label": "Instellingen synchroniseren", - "description": "Synchroniseer uw instellingen tussen browsers en apparaten" - }, - "sender_favicons": { - "label": "Afzender-favicons", - "description": "Toon websitepictogrammen als profielfoto's voor zakelijke afzenders" - }, - "show_avatars_in_junk": { - "label": "Avatars tonen in de map ongewenst", - "description": "Toon profielfoto's en favicons van afzenders in de map ongewenst. Standaard uitgeschakeld zodat phishingpogingen geen vertrouwd uiterlijk krijgen." - }, - "keyboard_shortcuts": { - "label": "Sneltoetsen", - "description": "Bekijk beschikbare sneltoetsen", - "button": "Sneltoetsen bekijken" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Instellingen resetten", - "description": "Herstel alle instellingen naar standaardwaarden", - "button": "Resetten naar standaard" - }, - "export_settings": { - "label": "Instellingen exporteren", - "description": "Download je instellingen als JSON", - "button": "Exporteren" - }, - "import_settings": { - "label": "Instellingen importeren", - "description": "Upload instellingen vanuit JSON-bestand", - "button": "Importeren" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Zijbalk-apps", - "description": "Aangepaste apps en links in de zijbalk beheren", - "keep_loaded": "Apps geladen houden", - "keep_loaded_description": "Inline-apps op de achtergrond laten draaien bij het wisselen om herladen te voorkomen", - "managed_title": "Aangeboden door uw beheerder", - "managed_description": "Deze apps zijn voor iedereen ingesteld en kunnen niet worden gewijzigd.", - "manage_title": "Aangepaste apps", - "manage_description": "Aangepaste apps toevoegen, bewerken of verwijderen uit de zijbalk" - }, - "contacts": { - "title": "Contacten", - "description": "Importeer en exporteer uw contacten", - "import_label": "Contacten importeren", - "import_description": "Contacten importeren uit een vCard-bestand (.vcf)", - "export_label": "Contacten exporteren", - "export_description": "Alle contacten exporteren als vCard-bestand (.vcf)", - "manage_title": "Adresboeken", - "manage_description": "Hernoem uw adresboeken", - "no_address_books": "Geen adresboeken gevonden", - "categories_title": "Categorieën", - "categories_description": "Contactcategorieën hernoemen", - "no_categories": "Geen categorieën gevonden", - "group_by_letter_description": "Toon alfabetische sectiekoppen in de contactenlijst", - "group_by_letter_label": "Groeperen op eerste letter", - "sort_by_last_name_label": "Sorteren op achternaam", - "sort_by_last_name_description": "Sorteer de contactenlijst op achternaam zodat gezinsleden bij elkaar staan" - }, - "downloads": { - "title": "Downloads", - "description": "Pas aan hoe gedownloade e-mails en bijlagen worden genoemd.", - "reset": "Standaard herstellen", - "preview": "Voorbeeld:", - "email_template": { - "label": "E-mailbestandsnaam (.eml)", - "description": "Sjabloon dat wordt gebruikt bij het exporteren of slepen van een e-mail naar het bestandssysteem. De extensie .eml wordt automatisch toegevoegd." - }, - "attachment_template": { - "label": "Bestandsnaam van bijlage", - "description": "Sjabloon bij het downloaden of slepen van een bijlage. Als je '{filename}' en '{ext}' weglaat, blijft de oorspronkelijke extensie behouden." - }, - "bundle_template": { - "label": "Bestandsnaam .zip met meerdere e-mails", - "description": "Sjabloon dat wordt gebruikt wanneer je meerdere geselecteerde e-mails als één .zip-archief sleept of downloadt. De extensie .zip wordt automatisch toegevoegd." - }, - "spaces": { - "label": "Spaties", - "description": "Vervang spaties in de uiteindelijke bestandsnaam door een ander teken.", - "keep": "Spaties behouden", - "underscore": "Vervangen door _", - "dash": "Vervangen door -" - }, - "lowercase": { - "label": "Kleine letters", - "description": "Forceer de hele bestandsnaam in kleine letters." - }, - "strip_diacritics": { - "label": "Diakritische tekens verwijderen", - "description": "Zet letters met accenten om naar hun ASCII-equivalent (ä → a, é → e). Handig voor tools die Unicode-namen niet goed verwerken." - }, - "collapse_separators": { - "label": "Herhaalde scheidingstekens samenvoegen", - "description": "Voeg reeksen spaties, underscores of streepjes samen tot één teken." - }, - "after_export": { - "label": "Na exporteren", - "description": "Verplaats de e-mail eventueel nadat deze als .eml is geëxporteerd.", - "keep": "In postvak houden", - "archive": "Naar archief verplaatsen", - "trash": "Naar prullenbak verplaatsen" - } - }, - "filters": { - "title": "E-mailfilters", - "description": "Maak regels om inkomende e-mails automatisch te sorteren, labelen en beheren", - "add_rule": "Regel toevoegen", - "no_rules": "Geen filterregels", - "vacation_active": "Afwezigheidsantwoord is actief", - "vacation_active_description": "Automatisch antwoord is ingeschakeld voor inkomende berichten", - "vacation_configure": "Configureren", - "no_rules_description": "Maak regels om uw inkomende e-mails automatisch te organiseren", - "edit_rule": "Regel bewerken", - "new_rule": "Nieuwe regel", - "delete_rule": "Regel verwijderen", - "delete_confirm": "Weet u zeker dat u deze regel wilt verwijderen?", - "enable": "Inschakelen", - "disable": "Uitschakelen", - "raw_editor": "Sieve-scripteditor", - "raw_editor_warning": "Het bewerken van het Sieve-script kan de visuele regelbewerking verstoren. Wijzigingen hier overschrijven de visuele builder.", - "validate": "Valideren", - "validation_success": "Het script is geldig", - "validation_error": "Het script bevat fouten", - "save": "Regels opslaan", - "saving": "Opslaan...", - "saved": "Filters succesvol opgeslagen", - "save_failed": "Filters opslaan mislukt", - "loading": "Filters laden...", - "not_supported": "Uw mailserver ondersteunt geen e-mailfilters.", - "rule_name": "Regelnaam", - "rule_name_placeholder": "bijv. Nieuwsbrieven sorteren", - "match_all": "Aan ALLE voorwaarden voldoen", - "match_any": "Aan EEN willekeurige voorwaarde voldoen", - "conditions": "Voorwaarden", - "add_condition": "Voorwaarde toevoegen", - "actions": "Acties", - "add_action": "Actie toevoegen", - "stop_processing": "Verwerking van volgende regels stoppen", - "include_spam": "Ook berichten verplaatsen die als spam zijn gemarkeerd", - "attachment_type_placeholder": "bijv. pdf, doc, jpg", - "value_placeholder_multi": "Waarde (meerdere met komma's gescheiden)", - "condition_fields": { - "attachment": "Bijlage", - "from": "Van", - "to": "Aan", - "cc": "Cc", - "subject": "Onderwerp", - "header": "Aangepaste header", - "size": "Grootte", - "body": "Inhoud" - }, - "comparators": { - "has_any": "aanwezig", - "has_type": "van type", - "contains": "bevat", - "not_contains": "bevat niet", - "is": "is precies", - "not_is": "is niet", - "starts_with": "begint met", - "ends_with": "eindigt met", - "matches": "komt overeen met patroon", - "greater_than": "is groter dan", - "less_than": "is kleiner dan" - }, - "action_types": { - "move": "Verplaatsen naar map", - "copy": "Kopiëren naar map", - "forward": "Doorsturen naar", - "mark_read": "Markeren als gelezen", - "star": "Ster toevoegen", - "add_label": "Label toevoegen", - "discard": "Stilletjes verwijderen", - "reject": "Afwijzen met bericht", - "keep": "In postvak IN houden", - "stop": "Verwerking stoppen" - }, - "move_to_folder": "Map selecteren", - "copy_to_folder": "Map selecteren", - "forward_to": "Doorsturen naar e-mailadres", - "forward_placeholder": "email@voorbeeld.nl", - "forward_keep_copy": "Kopie bewaren", - "forward_limit": "Maximaal aantal doorsturingen per bericht op deze server: {count}. Extra doorsturingen worden overgeslagen.", - "reject_message": "Afwijzingsbericht", - "reject_placeholder": "Uw e-mail is afgewezen", - "label_name": "Labelnaam", - "label_placeholder": "Label kiezen", - "header_name": "Headernaam", - "header_placeholder": "bijv. X-Mailing-List", - "size_bytes": "Grootte in bytes", - "size_placeholder": "bijv. 1000000", - "system_managed": "Systeembeheerde regel", - "opaque_warning": "Dit script is buiten de visuele builder bewerkt. Alleen Sieve-scriptbewerking is beschikbaar.", - "open_sieve_editor": "Sieve-scripteditor openen", - "fetch_error": "Filters konden niet worden geladen", - "expanded_view": "Uitgebreide weergave", - "expanded_view_description": "Filterregels weergeven met gedetailleerde voorwaarde- en actieblokken", - "if": "Als", - "then": "Dan", - "match_all_conditions": "alle overeenkomen", - "match_any_condition": "een overeenkomt", - "and": "en", - "or": "of", - "cancel": "Annuleren", - "confirm_delete": "Verwijderen", - "rule_list": "Filterregels", - "drag_to_reorder": "Slepen om te herschikken", - "match_type": "Overeenkomsttype", - "reset_to_visual": "Terugzetten naar visuele builder", - "reset_warning": "Dit verwijdert het huidige script en begint opnieuw.", - "confirm_reset": "Terugzetten", - "validation_empty_name": "Regelnaam is verplicht", - "validation_empty_conditions": "Minstens één voorwaarde met een waarde is vereist", - "validation_empty_actions": "Minstens één actie is vereist", - "templates_section": "Starten vanuit sjabloon", - "template_newsletters": "Nieuwsbrieven naar map verplaatsen", - "template_receipts": "Bonnen automatisch archiveren", - "template_important": "Belangrijke e-mails markeren", - "template_notifications": "Meldingen filteren", - "sieve_editor": { - "title": "Sieve-scripteditor", - "warning": "Het bewerken van het Sieve-script kan de visuele regelbewerking verbreken. Wijzigingen hier overschrijven de visuele builder.", - "script_content": "Sieve-script", - "valid": "Script is geldig", - "invalid": "Script bevat fouten", - "save_warning": "Opslaan overschrijft alle visuele regels. Dit kan niet ongedaan worden gemaakt. Klik nogmaals op Opslaan om te bevestigen.", - "validating": "Valideren...", - "validate": "Valideren", - "cancel": "Annuleren", - "save": "Opslaan", - "confirm_save": "Opslaan bevestigen", - "validation_failed": "Validatieverzoek mislukt" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# voorwaarde} other {# voorwaarden}}", - "actions_count": "{count, plural, one {# actie} other {# acties}}" - }, - "origin_external": "Extern", - "managed_by_tooltip": "Beheerd door {source}. Bewerk het in die app of gebruik de ruwe Sieve-editor." - }, - "templates": { - "title": "E-mailsjablonen", - "description": "Maak herbruikbare e-mailsjablonen met plaatshoudervariabelen", - "add": "Nieuw sjabloon", - "edit": "Sjabloon bewerken", - "name": "Sjabloonnaam", - "name_placeholder": "bijv.: Opvolgmail", - "category": "Categorie", - "category_placeholder": "bijv.: Werk, Persoonlijk", - "subject": "Onderwerp", - "subject_placeholder": "E-mailonderwerp", - "body": "Inhoud", - "body_placeholder": "E-mailinhoud...", - "recipients_placeholder": "email@example.com", - "identity": "Verzenden als", - "default_identity": "Standaard identiteit", - "favorite": "Favoriet", - "cancel": "Annuleren", - "create": "Aanmaken", - "update": "Bijwerken", - "confirm_delete": "Verwijderen", - "no_templates": "Geen sjablonen", - "manage": "Sjablonen beheren", - "count": "{count, plural, one {# sjabloon} other {# sjablonen}}", - "export_import": "Exporteren en importeren", - "export_import_description": "Maak een back-up van uw sjablonen of breng ze over naar een ander apparaat", - "export": "Exporteren", - "import": "Importeren", - "validation": { - "empty": "Sjabloonnaam is verplicht", - "too_long": "Sjabloonnaam mag maximaal 200 tekens zijn" - } - }, - "files": { - "display": { - "title": "Weergave", - "description": "Configureer hoe bestanden en mappen worden weergegeven" - }, - "folder_layout": { - "label": "Mapnavigatie", - "description": "Kies hoe mappen worden weergegeven: inline bij bestanden of in een boomstructuur in de zijbalk", - "inline": "Inline", - "sidebar": "Zijbalk" - }, - "default_view": { - "label": "Standaardweergave", - "description": "Kies tussen raster- en lijstweergave", - "list": "Lijst", - "grid": "Raster" - }, - "default_sort": { - "label": "Standaardsortering", - "description": "Kies de standaardsortering voor bestanden", - "name": "Naam", - "size": "Grootte", - "modified": "Gewijzigd" - }, - "sort_direction": { - "label": "Sorteerrichting", - "description": "Kies oplopende of aflopende volgorde", - "ascending": "Oplopend", - "descending": "Aflopend" - }, - "icons": { - "title": "Pictogrammen", - "description": "Configureer de weergave van bestandspictogrammen" - }, - "show_icons": { - "label": "Bestandspictogrammen tonen", - "description": "Pictogrammen naast bestanden en mappen weergeven" - }, - "colored_icons": { - "label": "Gekleurde pictogrammen", - "description": "Gebruik gekleurde pictogrammen in plaats van monochroom" - }, - "show_thumbnails": { - "label": "Miniaturen weergeven", - "description": "Toon afbeeldingsvoorbeelden in plaats van pictogrammen" - }, - "behavior": { - "title": "Gedrag", - "description": "Configureer het gedrag van de bestandsbrowser" - }, - "show_hidden": { - "label": "Verborgen bestanden tonen", - "description": "Bestanden en mappen weergeven die beginnen met een punt" - }, - "preview": { - "label": "Voorbeeld" - } - }, - "search_placeholder": "Instellingen zoeken", - "search_clear": "Zoekopdracht wissen", - "search_no_results": "Geen overeenkomende instellingen", - "scoped": { - "back": "Terug naar mijn account", - "managing": "Beheren: {name}" - } - }, - "errors": { - "page_error_title": "Er is iets misgegaan", - "page_error_description": "We hebben een onverwachte fout ondervonden. Probeer het opnieuw of keer terug naar de startpagina.", - "sidebar_error": "Kan mappen niet laden", - "email_list_error": "Kan e-mails niet laden", - "viewer_error_title": "Kan e-mail niet weergeven", - "viewer_error_description": "Er was een probleem met het renderen van deze e-mail. Het kan niet-ondersteunde inhoud bevatten.", - "composer_error": "Kan opsteller niet laden", - "settings_error_title": "Instellingen niet beschikbaar", - "settings_error_description": "Kan instellingen niet laden. Je voorkeuren worden mogelijk niet opgeslagen.", - "try_again": "Opnieuw proberen", - "reload": "Herladen", - "reload_emails": "E-mails herladen", - "reload_settings": "Instellingen herladen", - "retry": "Opnieuw proberen", - "go_home": "Ga naar postvak IN" - }, - "context_menu": { - "reply": "Beantwoorden", - "reply_all": "Allen beantwoorden", - "forward": "Doorsturen", - "mark_read": "Markeren als gelezen", - "mark_unread": "Markeren als ongelezen", - "star": "Ster toevoegen", - "unstar": "Ster verwijderen", - "pin": "Vastmaken", - "unpin": "Losmaken", - "move_to": "Verplaatsen naar...", - "archive": "Archiveren", - "delete": "Verwijderen", - "mark_as_spam": "Spam melden", - "not_spam": "Geen spam", - "tag": "Label", - "items_selected": "{count} e-mails geselecteerd", - "edit_draft": "Concept bewerken", - "cancel_scheduled_send": "Verzenden annuleren", - "reschedule_send": "Opnieuw plannen", - "cancel_and_edit": "Annuleren en bewerken", - "cancel_and_compose_again": "Annuleren en opnieuw opstellen" - }, - "mailbox_context_menu": { - "mark_folder_read": "Map markeren als gelezen", - "mark_folder_tree_read": "Map en submappen markeren als gelezen", - "mark_all_folders_read": "Alle mappen markeren als gelezen", - "new_subfolder": "Nieuwe submap...", - "new_folder": "Nieuwe map...", - "rename": "Hernoemen...", - "share": "Delen...", - "import_email": ".eml of .zip importeren...", - "empty_folder": "Map leegmaken", - "empty_folder_generic": "Map leegmaken", - "delete_folder": "Map verwijderen", - "refresh": "Vernieuwen", - "mark_all_confirm_title": "Alle mappen markeren als gelezen", - "mark_all_confirm_message": "Alle ongelezen berichten in uw persoonlijke account als gelezen markeren?", - "delete_confirm_title": "Map verwijderen", - "delete_confirm_message": "Map \"{name}\" definitief verwijderen? Deze actie kan niet ongedaan worden gemaakt.", - "prompt_new_subfolder": "Voer een naam in voor de nieuwe submap.", - "prompt_new_folder": "Voer een naam in voor de nieuwe map.", - "prompt_rename": "Voer een nieuwe naam in voor deze map.", - "toast_marked_read": "Map gemarkeerd als gelezen", - "toast_marked_read_count": "{count, plural, one {1 bericht} other {# berichten}} gemarkeerd als gelezen", - "toast_already_read": "Geen ongelezen berichten", - "toast_marked_all_read": "Alle mappen gemarkeerd als gelezen", - "toast_emptied": "Map leeggemaakt", - "toast_folder_created": "Map aangemaakt", - "toast_folder_renamed": "Map hernoemd", - "toast_folder_deleted": "Map verwijderd", - "toast_error_mark_read": "Markeren als gelezen mislukt", - "toast_error_empty": "Map leegmaken mislukt", - "toast_error_create": "Map aanmaken mislukt", - "toast_error_rename": "Map hernoemen mislukt", - "toast_error_delete": "Map verwijderen mislukt", - "toast_error_delete_has_children": "Map bevat submappen. Verwijder deze eerst.", - "toast_error_delete_has_email": "Map is niet leeg. Maak deze eerst leeg.", - "delete_with_contents_title": "Map is niet leeg", - "delete_with_contents_message": "De map \"{name}\" bevat nog {count, plural, one {1 bericht} other {# berichten}}. De map en al haar berichten verwijderen? Dit kan niet ongedaan worden gemaakt.", - "delete_with_contents_confirm": "Map en berichten verwijderen", - "placeholder_folder_name": "Mapnaam", - "create": "Aanmaken", - "rename_confirm": "Hernoemen" - }, - "shortcuts": { - "title": "Sneltoetsen", - "tip": "Druk op ? om deze hulp te tonen", - "sections": { - "navigation": "Navigatie", - "actions": "E-mailacties", - "global": "Globaal", - "threads": "Gesprekken", - "composer": "Opstellen" - }, - "navigation": { - "next_email": "Volgende e-mail", - "previous_email": "Vorige e-mail", - "open_email": "E-mail openen", - "close_email": "Sluiten / Deselecteren" - }, - "actions": { - "reply": "Beantwoorden", - "reply_all": "Allen beantwoorden", - "forward": "Doorsturen", - "star": "Ster aan/uit", - "archive": "Archiveren", - "delete": "Verwijderen", - "mark_unread": "Markeren als ongelezen", - "mark_read": "Markeren als gelezen", - "toggle_spam": "Spam melden / Geen spam" - }, - "global": { - "compose": "Nieuw bericht opstellen", - "search": "Focus op zoeken", - "help": "Sneltoetsen tonen", - "refresh": "E-mails vernieuwen", - "select_all": "Alles selecteren", - "global_search": "Alles doorzoeken" - }, - "threads": { - "expand_collapse": "Gesprek uitklappen/inklappen" - }, - "composer": { - "send": "E-mail verzenden", - "schedule_send": "Verzenden plannen", - "template_picker": "Sjabloonkiezer openen" - } - }, - "threads": { - "messages_one": "{count} bericht", - "messages_other": "{count} berichten", - "messages_tooltip": "{count, plural, one {# bericht in dit gesprek} other {# berichten in dit gesprek}}", - "expand": "Gesprek uitklappen", - "collapse": "Gesprek inklappen", - "loading": "Gesprek laden...", - "mark_read": "Gesprek markeren als gelezen", - "mark_unread": "Gesprek markeren als ongelezen", - "archive": "Gesprek archiveren", - "delete": "Gesprek verwijderen", - "star": "Ster toevoegen aan gesprek", - "unstar": "Ster verwijderen van gesprek", - "toggle_thread": "Gesprek in-/uitvouwen" - }, - "identities": { - "modal_title": "Verzendidentiteiten beheren", - "create_new": "Nieuwe identiteit aanmaken", - "edit_identity": "Identiteit bewerken", - "delete_confirm": "Deze identiteit verwijderen? Dit kan niet ongedaan worden gemaakt.", - "cannot_delete": "Deze identiteit kan niet worden verwijderd", - "primary_identity": "Primair", - "set_as_primary": "Instellen als primair", - "no_identities": "Geen identiteiten gevonden", - "display": { - "reply_to": "Antwoord naar:", - "bcc": "BCC:", - "signature": "Handtekening:", - "preview": "Voorbeeld:" - }, - "validation_errors": { - "invalid_emails": "Ongeldige e-mails: {emails}", - "unknown_error": "Onbekende fout" - }, - "form": { - "name_label": "Weergavenaam", - "name_placeholder": "bijv. Werk-e-mail, Persoonlijk", - "name_required": "Naam is vereist", - "email_label": "E-mailadres", - "email_placeholder": "jouw.email@voorbeeld.nl", - "email_required": "E-mail is vereist", - "email_invalid": "Voer een geldig e-mailadres in", - "email_immutable": "E-mailadres kan niet worden gewijzigd na aanmaak", - "reply_to_label": "Antwoord naar (optioneel)", - "reply_to_placeholder": "ander@email.nl", - "bcc_label": "Automatische BCC (optioneel)", - "bcc_placeholder": "archief@email.nl", - "text_signature_label": "Teksthandtekening", - "html_signature_label": "HTML-handtekening", - "signature_byte_counter": "{bytes} / {max} bytes", - "signature_byte_limit_reached": "Serverlimiet bereikt", - "save": "Identiteit opslaan", - "cancel": "Annuleren", - "creating": "Aanmaken...", - "updating": "Bijwerken..." - }, - "sub_address": { - "button_tooltip": "Sub-adres gebruiken", - "popover_title": "Sub-adrestag toevoegen", - "tag_input_placeholder": "Voer tag in (bijv. winkelen)", - "preview_label": "Voorbeeld:", - "recent_tags": "Recente tags", - "suggested_tags": "Voorgesteld", - "use_address": "Dit adres gebruiken", - "invalid_tag": "Tag mag alleen letters, cijfers en streepjes bevatten", - "tag_too_long": "Tag mag maximaal 30 tekens bevatten", - "help_text": "E-mails verzonden naar gebruiker{delimiter}tag@domein.nl komen in je postvak IN aan", - "validation": { - "empty": "Tag mag niet leeg zijn", - "too_long": "Tag mag maximaal {max} tekens bevatten", - "invalid_chars": "Tag mag alleen letters, cijfers en streepjes bevatten" - } - }, - "badge": { - "sent_via": "via", - "sub_address_tag": "Verzonden met sub-adres: {tag}", - "identity_name": "Verzonden met identiteit: {name}", - "identity_short": "via {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Verwijderen", - "delete_confirm_title": "Identiteit verwijderen" - }, - "templates": { - "picker_title": "Kies een sjabloon", - "search_placeholder": "Sjablonen zoeken...", - "section_favorites": "Favorieten", - "section_recent": "Recent", - "section_uncategorized": "Overig", - "no_templates": "Geen sjablonen", - "no_results": "Geen sjablonen gevonden", - "fill_placeholders": "Plaatshouderwaarden invullen", - "enter_value": "Voer een waarde in...", - "preview": "Voorbeeld", - "insert_with_values": "Invoegen met waarden", - "insert_raw": "Onbewerkt invoegen", - "copy_suffix": "(kopie)", - "placeholder": "Variabele", - "placeholders": { - "recipient_name": "Naam ontvanger", - "company": "Bedrijfsnaam", - "date": "Huidige datum", - "day_of_week": "Dag van de week", - "sender_name": "Uw naam" - } - }, - "contacts": { - "title": "Contacten", - "search_placeholder": "Contacten zoeken...", - "create_new": "Nieuw contact", - "no_category": "Geen categorie", - "rename_category": "Categorie hernoemen", - "category_name_label": "Categorienaam", - "category_renamed": "Categorie hernoemd", - "category_rename_failed": "Categorie hernoemen mislukt", - "category_added": "Contact toegevoegd aan {name}", - "category_added_plural": "{count} contacten toegevoegd aan {name}", - "empty_state": "Geen contacten", - "empty_state_title": "Geen contacten", - "empty_state_subtitle": "Maak uw eerste contact aan of importeer vanuit een vCard-bestand", - "empty_search": "Geen contacten gevonden", - "empty_search_hint": "Probeer een andere zoekterm", - "empty_filtered": "Geen contacten voldoen aan uw filters", - "empty_filtered_hint": "Pas filters aan of wis ze", - "clear_search": "Zoekopdracht wissen", - "import_vcard": "vCard importeren", - "delete_confirm_title": "Contact verwijderen", - "delete_confirm": "Weet u zeker dat u dit contact wilt verwijderen?", - "local_mode": "Contacten worden lokaal opgeslagen (server ondersteunt geen JMAP Contacts)", - "back_to_contacts": "Terug naar contacten", - "back_to_email": "Terug naar e-mail", - "tabs": { - "all": "Alle", - "groups": "Groepen" - }, - "shared": { - "title": "Gedeeld" - }, - "address_books": { - "create": "Nieuw adresboek", - "created": "Adresboek aangemaakt", - "create_failed": "Kan adresboek niet aanmaken", - "title": "Mijn Adresboeken", - "shared_prefix": "Gedeeld: {name}", - "moved": "Contact verplaatst naar {name}", - "moved_plural": "{count} contacten verplaatst naar {name}", - "move_failed": "Verplaatsen van contact mislukt", - "address_book": "Adresboek", - "rename": "Adresboek hernoemen", - "name_label": "Naam van adresboek", - "renamed": "Adresboek hernoemd", - "rename_failed": "Adresboek hernoemen mislukt", - "default": "Standaard", - "set_default": "Als standaard instellen", - "default_updated": "Standaardadresboek bijgewerkt", - "set_default_failed": "Standaardadresboek instellen mislukt", - "manage": "Adresboeken beheren", - "share": "Adresboek delen", - "new_contact_in_book": "Nieuw contact in dit adresboek", - "delete": "Adresboek verwijderen", - "confirm_delete": "\"{name}\" verwijderen? Alle contacten in dit adresboek worden verwijderd.", - "deleted": "Adresboek verwijderd", - "delete_failed": "Adresboek kon niet worden verwijderd" - }, - "detail": { - "emails": "E-mailadressen", - "phones": "Telefoonnummers", - "organizations": "Organisaties", - "addresses": "Adressen", - "notes": "Notities", - "titles": "Titels en rollen", - "online_services": "Online diensten", - "anniversaries": "Jubilea", - "personal_info": "Persoonlijke informatie", - "languages": "Talen", - "categories": "Categorieën", - "related_contacts": "Gerelateerde contacten", - "crypto_keys": "Cryptografische sleutels", - "no_contact_selected": "Selecteer een contact om details te bekijken", - "compose_email": "E-mail schrijven", - "copy_email": "E-mail kopiëren", - "copy_phone": "Telefoonnummer kopiëren", - "copy_url": "URL kopiëren", - "copied": "Gekopieerd naar klembord", - "copy_failed": "Kopiëren naar klembord mislukt", - "created": "Aangemaakt", - "updated": "Laatst bijgewerkt", - "timezone": "Tijdzone", - "anniversary_birth": "Verjaardag", - "anniversary_death": "Overleden", - "anniversary_wedding": "Trouwdag", - "anniversary_other": "Overig", - "personal_expertise": "Expertise", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Overig", - "gender": "Geslacht", - "gender_masculine": "Man", - "gender_feminine": "Vrouw", - "gender_other": "Anders", - "gender_none": "Niet van toepassing", - "gender_unknown": "Onbekend", - "calendar": "Kalender", - "calendar_uri": "Kalender-URL", - "scheduling_uri": "Planning-URL", - "freebusy_uri": "Beschikbaarheid-URL", - "cert_issuer": "Uitgever", - "cert_expires": "Verloopt", - "cert_expired": "Verlopen", - "cert_fingerprint": "Vingerafdruk", - "cert_algorithm": "Algoritme", - "import_to_smime": "Importeren naar S/MIME", - "cert_already_imported": "Certificaat is al geïmporteerd", - "cert_imported": "Certificaat geïmporteerd", - "cert_import_failed": "Importeren van certificaat mislukt", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Meer acties", - "age_years": "{count, plural, one {1 jaar} other {# jaar}}", - "years_since": "{count, plural, one {1 jaar geleden} other {# jaar geleden}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Nieuw contact", - "edit_title": "Contact bewerken", - "section_address_book": "Adresboek", - "select_address_book": "Selecteer een adresboek...", - "section_identity": "Naam en identiteit", - "contact_type": "Contacttype", - "type_person": "Persoon", - "type_organization": "Organisatie", - "section_work": "Werk en organisatie", - "prefix": "Voorvoegsel", - "prefix_placeholder": "Dr., Dhr., Mevr.", - "given_name": "Voornaam", - "middle_name": "Tweede naam", - "surname": "Achternaam", - "suffix": "Achtervoegsel", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Bijnaam", - "nickname_placeholder": "Bijnaam", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Telefoon", - "phone_placeholder": "+31 20 123 4567", - "phone_type": "Type", - "phone_voice": "Spraak", - "phone_cell": "Mobiel", - "phone_fax": "Fax", - "phone_pager": "Pieper", - "phone_video": "Video", - "phone_text": "SMS", - "organization": "Organisatie", - "organization_placeholder": "Bedrijfsnaam", - "department": "Afdeling", - "department_placeholder": "Afdeling", - "job_title": "Functietitel", - "job_title_placeholder": "bijv. Software-engineer", - "role": "Rol", - "role_placeholder": "bijv. Teamleider", - "addresses": "Adressen", - "add_address": "Adres toevoegen", - "street": "Straat", - "city": "Stad", - "region": "Provincie / Regio", - "postcode": "Postcode", - "country": "Land", - "online_services": "Online diensten", - "add_online_service": "Online dienst toevoegen", - "url_placeholder": "https://...", - "service_placeholder": "Dienst", - "anniversaries": "Jubilea", - "add_anniversary": "Datum toevoegen", - "anniversary_birth": "Verjaardag", - "anniversary_wedding": "Trouwdag", - "anniversary_death": "Herdenking", - "anniversary_other": "Overig", - "personal_info": "Persoonlijke informatie", - "add_personal_info": "Item toevoegen", - "personal_info_placeholder": "bijv. Fotografie", - "personal_expertise": "Expertise", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Overig", - "level": "Niveau", - "level_high": "Hoog", - "level_medium": "Gemiddeld", - "level_low": "Laag", - "categories": "Categorieën", - "categories_placeholder": "bijv. Familie, Vrienden, Collega's", - "categories_hint": "Typ om te zoeken of toe te voegen", - "category_add": "Toevoegen", - "note": "Notities", - "note_placeholder": "Notitie toevoegen...", - "gender": "Geslacht", - "gender_sex": "Geslacht", - "gender_male": "Man", - "gender_female": "Vrouw", - "gender_other": "Anders", - "gender_none": "Niet van toepassing", - "gender_unknown": "Onbekend", - "gender_identity": "Genderidentiteit", - "gender_identity_placeholder": "Genderidentiteit...", - "calendar": "Kalender", - "calendar_uri": "Kalender-URL", - "scheduling_uri": "Planning-URL", - "freebusy_uri": "Beschikbaarheid-URL", - "context_work": "Werk", - "context_private": "Privé", - "add_email": "E-mail toevoegen", - "add_phone": "Telefoon toevoegen", - "save": "Opslaan", - "cancel": "Annuleren", - "creating": "Aanmaken...", - "updating": "Bijwerken...", - "name_required": "Voer een voornaam, achternaam of organisatie in", - "email_invalid": "Voer een geldig e-mailadres in", - "email_error_inline": "Ongeldig e-mailformaat", - "save_failed": "Kon contact niet opslaan", - "delete": "Verwijderen", - "upload_photo": "Foto uploaden", - "remove_photo": "Foto verwijderen", - "photo_hint": "JPG of PNG, tot 10 MB. Wordt verkleind.", - "photo_too_large": "Afbeelding is te groot (max 10 MB)", - "photo_invalid": "Ongeldig afbeeldingsbestand", - "change_photo": "Wijzigen" - }, - "groups": { - "create": "Nieuwe groep", - "edit": "Groep bewerken", - "empty": "Geen groepen", - "delete_confirm_title": "Groep verwijderen", - "delete_confirm": "Weet u zeker dat u deze groep wilt verwijderen?", - "name_label": "Groepsnaam", - "name_placeholder": "bijv. Team, Familie", - "name_required": "Groepsnaam is vereist", - "save_failed": "Kon groep niet opslaan", - "members_label": "Leden", - "search_members": "Contacten zoeken om toe te voegen...", - "no_members": "Geen leden in deze groep", - "member_count": "{count, plural, =0 {Geen leden} one {1 lid} other {# leden}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Contacten importeren", - "drop_hint": "Klik om een vCard-bestand te selecteren", - "file_types": ".vcf- of .vcard-bestanden", - "no_contacts": "Geen contacten gevonden in bestand", - "parse_error": "Kon vCard-bestand niet lezen", - "found": "{count, plural, one {1 contact gevonden} other {# contacten gevonden}}", - "duplicate": "Duplicaat", - "select_all": "Alles selecteren", - "deselect_all": "Alles deselecteren", - "selected": "{count, plural, one {1 geselecteerd} other {# geselecteerd}}", - "import_button": "Importeren", - "importing": "Importeren...", - "success": "{count, plural, one {1 contact geïmporteerd} other {# contacten geïmporteerd}}", - "failed": "Import mislukt", - "close": "Sluiten", - "file_too_large": "Bestand is te groot (max 5 MB)" - }, - "export": { - "title": "Contacten exporteren", - "success": "{count, plural, one {1 contact geëxporteerd} other {# contacten geëxporteerd}}" - }, - "bulk": { - "selected": "{count, plural, one {1 geselecteerd} other {# geselecteerd}}", - "select_all": "Alles selecteren", - "delete": "Verwijderen", - "delete_confirm_title": "Contacten verwijderen", - "delete_confirm": "{count, plural, one {1 contact} other {# contacten}} verwijderen?", - "deleted": "{count, plural, one {1 contact verwijderd} other {# contacten verwijderd}}", - "add_to_group": "Aan groep toevoegen", - "choose_group": "Kies een groep", - "adding_contacts": "{count, plural, one {1 contact} other {# contacten}} toevoegen", - "added_to_group": "Contacten aan groep toegevoegd", - "export": "Exporteren", - "clear": "Selectie wissen" - }, - "toast": { - "created": "Contact aangemaakt", - "updated": "Contact bijgewerkt", - "deleted": "Contact verwijderd", - "error_create": "Kon contact niet aanmaken", - "error_update": "Kon contact niet bijwerken", - "error_delete": "Kon contact niet verwijderen" - }, - "context_menu": { - "open": "Openen", - "edit": "Bewerken", - "send_email": "E-mail verzenden", - "add_to_group": "Aan groep toevoegen", - "export_vcard": "Exporteren als vCard", - "delete": "Verwijderen", - "call": "Bellen", - "duplicate": "Dupliceren", - "print": "Afdrukken" - }, - "filters": { - "toggle": "Filters", - "select": "Selecteren", - "clear": "Wissen", - "close": "Sluiten", - "title": "Geavanceerde filters", - "organization": "Bedrijf", - "organization_placeholder": "bijv. Acme", - "job_title": "Functie", - "job_title_placeholder": "bijv. Ontwerper", - "location": "Locatie", - "location_placeholder": "Stad of land", - "email_domain": "E-maildomein", - "email_domain_placeholder": "voorbeeld.nl", - "birthday_month": "Verjaardag in", - "any_month": "Elke maand", - "has_email": "Met e-mail", - "has_phone": "Met telefoon", - "has_photo": "Met foto", - "sort_by": "Sorteren op", - "sort_first_name": "Voornaam", - "sort_last_name": "Achternaam" - }, - "open_categories": "Categorieën openen" - }, - "calendar": { - "title": "Agenda", - "back_to_email": "Terug naar e-mail", - "back_to_month": "Terug naar maand", - "my_calendars": "Mijn agenda's", - "birthday_calendar": "Verjaardagen", - "mini_calendar_change": "Klik om van maand te wisselen", - "views": { - "month": "Maand", - "week": "Week", - "day": "Dag", - "agenda": "Agenda", - "today": "Vandaag", - "month_hint": "Maand (m)", - "week_hint": "Week (w)", - "day_hint": "Dag (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Taken", - "tasks_hint": "Taken (k)" - }, - "events": { - "create": "Evenement aanmaken", - "edit": "Evenement bewerken", - "delete": "Evenement verwijderen", - "details": "Evenementdetails", - "no_events": "Geen evenementen", - "all_day": "Hele dag", - "more": "+{count} meer", - "no_title": "(Geen titel)", - "resize": "Evenement formaat wijzigen", - "duplicate": "Dupliceren", - "today_header": "Vandaag", - "tomorrow_header": "Morgen", - "agenda_show_earlier": "Eerdere afspraken tonen", - "agenda_loading": "Laden…", - "agenda_range_start": "Afspraken vanaf {date}", - "agenda_range_end": "Afspraken tot {date}", - "export_ics": "Exporteren als .ics", - "copy_title": "Titel kopiëren", - "copy_link": "Vergaderlink kopiëren", - "go_to_today": "Ga naar vandaag", - "new_all_day_event": "Nieuwe dagvullende afspraak", - "new_event": "Nieuwe afspraak", - "new_task": "Nieuwe taak" - }, - "detail": { - "add_note": "Notitie toevoegen...", - "save_note": "Opslaan", - "note_saved": "Notitie toegevoegd", - "open_link": "Link openen", - "meeting_link": "Vergaderlink", - "tentative": "Voorlopig", - "cancelled": "Geannuleerd", - "delete_confirm": "Dit evenement verwijderen?" - }, - "form": { - "title": "Titel", - "description": "Beschrijving", - "location": "Locatie", - "meeting_link": "Vergaderlink", - "start_date": "Startdatum", - "end_date": "Einddatum", - "start_time": "Starttijd", - "end_time": "Eindtijd", - "all_day_event": "Hele dag evenement", - "calendar_select": "Agenda", - "save": "Opslaan", - "cancel": "Annuleren", - "delete_confirm": "Weet je zeker dat je dit evenement wilt verwijderen?", - "color": "Kleur" - }, - "participants": { - "availability": { - "title": "Beschikbaarheid", - "free": "Beschikbaar", - "busy": "Bezet", - "tentative": "Voorlopig", - "unknown": "Geen gebruiker op deze server", - "checking": "Controleren…" - }, - "title": "Deelnemers", - "add": "Deelnemer toevoegen", - "organizer": "Organisator", - "attendee": "Deelnemer", - "accepted": "Geaccepteerd", - "declined": "Geweigerd", - "tentative": "Voorlopig", - "needs_action": "Reactie vereist", - "remove": "Verwijderen", - "edit": "Bewerken", - "email_placeholder": "E-mailadres toevoegen of contacten zoeken", - "send_invitations": "Uitnodigingen sturen naar deelnemers", - "status_summary": "{accepted} geaccepteerd, {pending} in afwachting", - "invited_by": "Uitnodiging van {name}", - "respond_below": "Reageer met de knoppen hieronder", - "rsvp_label": "Uw reactie", - "cancel_notification": "Deelnemers worden op de hoogte gebracht van de annulering", - "you_organizer": "Je bent de organisator", - "you_attendee": "Je bent een deelnemer", - "no_participants": "Geen deelnemers", - "count": "{count, plural, one {# deelnemer} other {# deelnemers}}" - }, - "recurrence": { - "title": "Herhaling", - "none": "Wordt niet herhaald", - "daily": "Dagelijks", - "weekly": "Wekelijks", - "monthly": "Maandelijks", - "yearly": "Jaarlijks", - "every_n_days": "Elke {count} dagen", - "every_n_weeks": "Elke {count} weken", - "every_n_months": "Elke {count} maanden", - "until": "Tot", - "occurrences": "{count} herhalingen", - "custom": "Aangepast…", - "edit_custom": "Aangepaste herhaling bewerken", - "every_n_years": "Elke {count} jaar", - "on_days": "op {days}", - "on_day_n": "op dag {day}", - "on_the_nth": "op de {nth} {day}", - "in_month": "in {month}", - "nth_1": "eerste", - "nth_2": "tweede", - "nth_3": "derde", - "nth_4": "vierde", - "nth_last": "laatste", - "editor_freq_day": "Dag", - "editor_freq_week": "Week", - "editor_freq_month": "Maand", - "editor_freq_year": "Jaar", - "editor_repeats_on": "Herhaalt zich", - "editor_every": "Elke", - "editor_unit_days": "dag(en)", - "editor_unit_weeks": "week/weken", - "editor_unit_months": "maand(en)", - "editor_unit_years": "jaar/jaren", - "editor_on_day": "op dag", - "editor_on_the": "op de", - "editor_in": "in", - "editor_ends": "Eindigt", - "editor_never": "Nooit", - "editor_ends_on": "Op", - "editor_ends_after": "Na", - "editor_occurrences": "herhalingen" - }, - "recurrence_scope": { - "edit_title": "Terugkerend evenement bewerken", - "delete_title": "Terugkerend evenement verwijderen", - "description": "Dit is een terugkerend evenement. Welke evenementen wilt u wijzigen?", - "this_event": "Alleen dit evenement", - "this_and_future": "Dit en volgende evenementen", - "all_events": "Alle evenementen", - "cancel": "Annuleren", - "save": "Opslaan", - "delete": "Verwijderen", - "rsvp_title": "Reageren op terugkerend evenement", - "rsvp_description": "Dit is een terugkerend evenement. Voor welke evenementen geldt uw reactie?", - "respond": "Reageren" - }, - "alerts": { - "title": "Herinnering", - "none": "Geen herinnering", - "at_time": "Op het tijdstip van het evenement", - "minutes_before": "{count, plural, one {# minuut van tevoren} other {# minuten van tevoren}}", - "hours_before": "{count, plural, one {# uur van tevoren} other {# uur van tevoren}}", - "days_before": "{count, plural, one {# dag van tevoren} other {# dagen van tevoren}}", - "weeks_before": "{count, plural, one {# week van tevoren} other {# weken van tevoren}}", - "unit_minutes_before": "minuten van tevoren", - "unit_hours_before": "uur van tevoren", - "unit_days_before": "dagen van tevoren", - "unit_weeks_before": "weken van tevoren", - "add": "Herinnering toevoegen", - "remove": "Herinnering verwijderen", - "amount": "Herinneringshoeveelheid", - "unit": "Herinneringseenheid" - }, - "settings": { - "title": "Agenda-instellingen", - "default_view": "Standaardweergave", - "week_starts_on": "Week begint op", - "time_format": "Tijdnotatie", - "default_calendar": "Standaardagenda", - "default_reminder": "Standaardherinnering", - "time_format_12h": "12-uurs", - "time_format_24h": "24-uurs", - "notifications_enabled": "Evenementmeldingen", - "notifications_enabled_desc": "Meldingen weergeven voor aankomende evenementen", - "notification_sound": "Meldingsgeluid", - "notification_sound_desc": "Geluid afspelen voor agendameldingen", - "invitation_parsing": "E-mailuitnodigingen verwerken", - "invitation_parsing_desc": "Kalenderuitnodigingen in e-mailbijlagen detecteren en kalenderacties tonen", - "show_time_in_month_view": "Tijd weergeven in maandweergave", - "show_time_in_month_view_desc": "Evenementtijden weergeven in de maandkalenderweergave", - "show_week_numbers": "Weeknummers weergeven", - "show_week_numbers_desc": "Weeknummers weergeven in de minikalender", - "calendar_free_scroll": "Vrij scrollen", - "calendar_free_scroll_desc": "Doorlopend door maanden, weken en dagen scrollen in plaats van één periode tegelijk", - "enable_tasks": "Taken inschakelen", - "enable_tasks_desc": "Een takenweergave in de agenda tonen om to-do's te beheren", - "show_tasks_on_calendar": "Taken in agenda tonen", - "show_tasks_on_calendar_desc": "Taaklabels tonen in dag- en weekweergave van de agenda", - "hover_preview": "Voorvertoning bij hover", - "hover_preview_desc": "Toon een detailpopover bij het hoveren over agenda-evenementen", - "hover_preview_instant": "Direct", - "hover_preview_delay_500ms": "0,5 seconde vertraging", - "hover_preview_delay_1s": "1 seconde vertraging", - "hover_preview_delay_2s": "2 seconden vertraging", - "hover_preview_off": "Uitgeschakeld", - "organizer_identity": "Uitnodigingen organiseren als", - "organizer_identity_desc": "Het agenda-adres dat als organisator wordt gebruikt wanneer je mensen uitnodigt voor afspraken.", - "organizer_identity_failed": "Kon het organisatoradres niet wijzigen", - "show_birthday_calendar": "Verjaardagskalender", - "show_birthday_calendar_desc": "Toon een virtuele kalender met verjaardagen van je contacten" - }, - "days": { - "monday": "Maandag", - "tuesday": "Dinsdag", - "wednesday": "Woensdag", - "thursday": "Donderdag", - "friday": "Vrijdag", - "saturday": "Zaterdag", - "sunday": "Zondag", - "mon": "Ma", - "tue": "Di", - "wed": "Wo", - "thu": "Do", - "fri": "Vr", - "sat": "Za", - "sun": "Zo" - }, - "notifications": { - "event_created": "Evenement aangemaakt", - "event_updated": "Evenement bijgewerkt", - "event_deleted": "Evenement verwijderd", - "calendar_created": "Agenda aangemaakt", - "calendar_deleted": "Agenda verwijderd", - "event_move_error": "Evenement verplaatsen mislukt", - "event_resize_error": "Evenement formaat wijzigen mislukt", - "alert_title": "Aankomend evenement", - "alert_now": "Begint nu", - "alert_in_minutes": "Over {count} min", - "invitation_sent": "Uitnodigingen verzonden", - "invitations_denied_title": "Uitnodigingen kunnen niet worden verzonden", - "invitations_denied": "De server weigerde de uitnodigingen te verzenden: {reason}. Het evenement opslaan zonder ze te verzenden?", - "save_without_invitations": "Opslaan zonder uitnodigingen", - "rsvp_updated": "Reactie bijgewerkt", - "rsvp_error": "Reactie kon niet worden bijgewerkt", - "event_duplicated": "Evenement gedupliceerd", - "event_error": "Evenement opslaan mislukt", - "task_due": "Taak vervalt", - "event_exported": "Afspraak geëxporteerd", - "title_copied": "Titel gekopieerd", - "link_copied": "Link gekopieerd" - }, - "status": { - "loading_calendars": "Agenda's laden...", - "loading_events": "Evenementen laden..." - }, - "quick_create": { - "placeholder": "Titel nieuw evenement", - "aria_label": "Snel evenement aanmaken" - }, - "nav_prev": "Vorige", - "nav_next": "Volgende", - "import": { - "title": "Agenda importeren", - "tab_file": "Bestand", - "tab_url": "URL", - "select_file": "Selecteer .ics-bestand", - "drop_file": "of sleep het bestand hierheen", - "supported_formats": "iCalendar (.ics) bestanden worden ondersteund", - "url_description": "Voer de URL in van een externe iCalendar (.ics) feed om evenementen te importeren.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Ondersteunt iCalendar (.ics) URLs", - "fetch": "Ophalen", - "invalid_url": "Voer een geldige URL in", - "url_fetch_failed": "Kan agenda niet ophalen van URL", - "parsing": "Agendabestand wordt verwerkt...", - "parsed_events": "{count} evenementen gevonden", - "no_events": "Geen evenementen gevonden in bestand", - "select_all": "Alles selecteren", - "deselect_all": "Alles deselecteren", - "target_calendar": "Importeren in agenda", - "import_button": "Selectie importeren", - "importing": "Evenementen importeren...", - "success": "{count} evenementen succesvol geïmporteerd", - "error": "Agenda importeren mislukt", - "file_too_large": "Bestand overschrijdt de limiet van 10 MB", - "invalid_format": "Ongeldig agendabestandsformaat" - }, - "webcal_action": { - "title": "Agendalink openen", - "description": "Hoe wilt u \"{name}\" gebruiken?", - "import_title": "Eenmalig importeren", - "import_description": "Haal de afspraken nu op en kopieer ze naar een van uw agenda's.", - "subscribe_title": "Abonneren", - "subscribe_description": "Houd deze agenda automatisch gesynchroniseerd als aparte agenda.", - "cancel": "Annuleren" - }, - "management": { - "title": "Agendabeheer", - "description": "Maak, hernoem en pas uw agenda's aan. Klik met de rechtermuisknop op een agenda in de zijbalk om snel de kleur te wijzigen.", - "name": "Naam", - "name_placeholder": "Agendanaam", - "color": "Kleur", - "change_color": "Kleur wijzigen", - "random_color": "Nieuwe willekeurige kleur", - "add_calendar": "Agenda toevoegen", - "edit": "Bewerken", - "delete": "Verwijderen", - "save": "Opslaan", - "create": "Aanmaken", - "cancel": "Annuleren", - "default": "Standaard", - "confirm_delete": "\"{name}\" verwijderen? Alle afspraken in deze agenda worden verwijderd.", - "calendar_created": "Agenda aangemaakt", - "calendar_updated": "Agenda bijgewerkt", - "calendar_deleted": "Agenda verwijderd", - "color_updated": "Agendakleur bijgewerkt", - "error_create": "Agenda aanmaken mislukt", - "error_update": "Agenda bijwerken mislukt", - "error_delete": "Agenda verwijderen mislukt", - "caldav_url": "CalDAV-URL", - "copy_url": "CalDAV-URL kopiëren", - "url_copied": "CalDAV-URL gekopieerd naar klembord", - "confirm_clear": "Alle afspraken uit \"{name}\" verwijderen? Dit kan niet ongedaan worden gemaakt.", - "set_default": "Als standaard instellen", - "default_updated": "Standaardagenda bijgewerkt", - "error_default": "Standaardagenda instellen mislukt", - "clear_events": "Afspraken verwijderen", - "events_cleared": "{count} afspraken verwijderd", - "error_clear": "Kan agendagebeurtenissen niet verwijderen", - "share": "Agenda delen", - "new_event_in_calendar": "Nieuwe afspraak in deze agenda", - "kind": "Inhoud", - "kind_events": "Afspraken", - "kind_tasks": "Taken", - "kind_both": "Afspraken en taken", - "kind_hint": "Bepaalt welke apps deze agenda zien wanneer die via CalDAV wordt gesynchroniseerd. Dit kan later niet meer worden gewijzigd." - }, - "subscription": { - "title": "iCal-abonnement", - "section_title": "iCal-abonnementen", - "description": "Abonneer op een externe iCalendar-feed. Afspraken worden automatisch gesynchroniseerd in een eigen agenda. Ondersteunt https://- en webcal://-URL's.", - "url_label": "Agenda-URL", - "url_placeholder": "https://example.com/calendar.ics of webcal://...", - "name_label": "Agendanaam", - "name_placeholder": "bijv. Feestdagen", - "color_label": "Kleur", - "refresh_interval": "Verversingsinterval", - "interval_15": "Elke 15 minuten", - "interval_30": "Elke 30 minuten", - "interval_60": "Elk uur", - "interval_360": "Elke 6 uur", - "interval_1440": "Elke dag", - "subscribe": "Abonneren", - "subscribing": "Bezig met abonneren...", - "save": "Wijzigingen opslaan", - "saving": "Opslaan...", - "edit": "Bewerken", - "edit_title": "Abonnement bewerken", - "updated": "\"{name}\" bijgewerkt", - "update_error": "Abonnement kon niet worden bijgewerkt", - "invalid_url": "Voer een geldige URL in", - "success": "Geabonneerd op \"{name}\"", - "error": "Kan abonnement niet toevoegen", - "refresh": "Nu vernieuwen", - "refresh_success": "Abonnement vernieuwd", - "refresh_error": "Kan abonnement niet vernieuwen", - "unsubscribe": "Afmelden", - "confirm_delete": "Afmelden van \"{name}\"? De agenda en alle afspraken worden verwijderd.", - "deleted": "Abonnement verwijderd", - "delete_error": "Kan abonnement niet verwijderen", - "last_refreshed": "Laatst bijgewerkt: {time}" - }, - "tasks": { - "label": "Taken", - "no_tasks": "Geen taken", - "no_title": "(Geen titel)", - "mark_complete": "Markeren als voltooid", - "mark_incomplete": "Markeren als onvoltooid", - "filter_all": "Alle", - "filter_pending": "Openstaand", - "filter_completed": "Voltooid", - "filter_overdue": "Achterstallig", - "show_completed": "Voltooide tonen", - "create": "Nieuwe taak", - "edit": "Taak bewerken", - "title_placeholder": "Taaktitel", - "description_placeholder": "Voeg een beschrijving toe...", - "due_date": "Vervaldatum", - "include_time": "Tijd opnemen", - "priority": "Prioriteit", - "priority_none": "Geen", - "priority_high": "Hoog", - "priority_medium": "Middel", - "priority_low": "Laag", - "progress": "Status", - "progress_needs_action": "Actie vereist", - "progress_in_process": "In uitvoering", - "progress_completed": "Voltooid", - "progress_cancelled": "Geannuleerd", - "calendar": "Agenda", - "alert": "Herinnering", - "alert_none": "Geen", - "alert_at_time": "Op vervaldatum en -tijd", - "alert_5min": "5 minuten van tevoren", - "alert_15min": "15 minuten van tevoren", - "alert_30min": "30 minuten van tevoren", - "alert_1hr": "1 uur van tevoren", - "alert_1day": "1 dag van tevoren", - "delete": "Verwijderen", - "cancel": "Annuleren", - "save": "Opslaan", - "quick_add_placeholder": "Taak toevoegen...", - "due_today": "Vervalt vandaag", - "due_tomorrow": "Vervalt morgen", - "overdue": "Achterstallig" - }, - "months": { - "jan": "jan", - "feb": "feb", - "mar": "mrt", - "apr": "apr", - "may": "mei", - "jun": "jun", - "jul": "jul", - "aug": "aug", - "sep": "sep", - "oct": "okt", - "nov": "nov", - "dec": "dec", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Menu openen" - }, - "advanced_search": { - "title": "Geavanceerd zoeken", - "from": "Van", - "from_placeholder": "E-mail of naam afzender", - "to": "Aan", - "to_placeholder": "E-mail of naam ontvanger", - "subject": "Onderwerp", - "subject_placeholder": "Onderwerp bevat...", - "body": "Inhoud", - "body_placeholder": "Inhoud bevat...", - "folder": "Map", - "all_folders": "Alle mappen", - "has_attachment": "Bijlagen", - "date_after": "Na", - "date_before": "Voor", - "size_min": "Groter dan (KB)", - "size_max": "Kleiner dan (KB)", - "starred": "Met ster", - "unread": "Ongelezen", - "read": "Gelezen", - "yes": "Ja", - "no": "Nee", - "clear": "Wissen", - "clear_all": "Alles wissen", - "filters_active": "{count} filter", - "filters_active_plural": "{count} filters", - "toggle_filters": "Filters", - "search_hint": "Gebruik geavanceerde filters voor een nauwkeurige zoekopdracht", - "advanced_filters_tooltip": "Geavanceerde zoekfilters", - "results_found": "{count, plural, =0 {Geen resultaten gevonden} one {# resultaat gevonden} other {# resultaten gevonden}}", - "results_found_more": "{count}+ resultaten gevonden", - "suggestions_recent": "Recente zoekopdrachten", - "suggestions_people": "Personen", - "suggestions_from": "E-mails van {address} zoeken", - "suggestions_to": "E-mails aan {address} zoeken", - "suggestions_remove_recent": "Verwijderen uit recente zoekopdrachten" - }, - "welcome": { - "title": "Welkom bij uw mailbox", - "tip_compose": "Druk op c om een nieuwe e-mail te schrijven", - "tip_shortcuts": "Druk op ? om alle sneltoetsen te bekijken", - "tip_sidebar": "Vind Agenda, Contacten en Instellingen in het zijmenu", - "tip_settings": "Pas uw ervaring aan in Instellingen", - "got_it": "Begrepen", - "settings": "Instellingen", - "dismiss": "Sluiten", - "start_tour": "Tour starten" - }, - "demo_welcome": { - "title": "Welkom bij Bulwark Mail", - "description": "Ontdek een volwaardige webmail-client - rechtstreeks in je browser. Alle gegevens blijven op je apparaat, dus test gerust alles.", - "feature_email": "E-mails lezen en schrijven", - "feature_organize": "Tags, sterren en mappen", - "feature_shortcuts": "Sneltoetsen", - "feature_privacy": "100% privédemo", - "hint": "Klik links op een e-mail om te beginnen, of start de tour." - }, - "files": { - "office_edit": "Document bewerken", - "office_loading": "Documenteditor wordt geopend…", - "office_error": "De documenteditor kon niet worden geopend.", - "office_close": "Editor sluiten", - "open_in_new_tab": "Openen in nieuw tabblad", - "title": "Bestanden", - "search_placeholder": "Bestanden zoeken...", - "empty_state_title": "Nog geen bestanden", - "empty_state_description": "Upload bestanden of maak mappen aan om te beginnen", - "upload": "Uploaden", - "upload_files": "Bestanden uploaden", - "new_folder": "Nieuwe map", - "new_folder_name": "Mapnaam", - "rename": "Hernoemen", - "rename_title": "Hernoemen", - "new_name": "Nieuwe naam", - "name_forbidden_chars": "De server staat deze tekens niet toe in namen: {chars}", - "name_reserved": "Deze naam is gereserveerd op de server.", - "delete": "Verwijderen", - "delete_confirm_title": "Bron verwijderen", - "delete_confirm_message": "Weet u zeker dat u \"{name}\" wilt verwijderen? Dit kan niet ongedaan worden gemaakt.", - "download": "Downloaden", - "name": "Naam", - "size": "Grootte", - "modified": "Gewijzigd", - "type": "Type", - "folder": "Map", - "file": "Bestand", - "parent_directory": "Bovenliggende map", - "breadcrumb_root": "Start", - "drop_files_here": "Sleep bestanden of mappen hierheen om te uploaden", - "uploading": "Uploaden...", - "upload_success": "{count, plural, one {1 bestand geüpload} other {# bestanden geüpload}}", - "upload_error": "Bestand uploaden mislukt", - "create_folder_success": "Map aangemaakt", - "create_folder_error": "Map aanmaken mislukt", - "delete_success": "Succesvol verwijderd", - "delete_error": "Verwijderen mislukt", - "rename_success": "Succesvol hernoemd", - "rename_error": "Hernoemen mislukt", - "download_error": "Downloaden mislukt", - "not_available": "Bestandsopslag is niet beschikbaar op deze server", - "cancel": "Annuleren", - "create": "Aanmaken", - "save": "Opslaan", - "no_results": "Geen bestanden komen overeen met uw zoekopdracht", - "batch_delete_confirm_message": "Weet u zeker dat u {count, plural, one {1 item} other {# items}} wilt verwijderen? Dit kan niet ongedaan worden gemaakt.", - "batch_delete_success": "{count, plural, one {1 item verwijderd} other {# items verwijderd}}", - "grid_view": "Rasterweergave", - "list_view": "Lijstweergave", - "details": "Details", - "path": "Pad", - "preview": "Voorbeeld", - "preview_error": "Voorbeeld laden mislukt", - "cut": "Knippen", - "copy": "Kopiëren", - "paste": "Plakken", - "move_success": "{count, plural, one {1 item verplaatst} other {# items verplaatst}}", - "move_error": "Verplaatsen mislukt", - "paste_success": "Succesvol geplakt", - "paste_error": "Plakken mislukt", - "new_text_file": "Nieuw tekstbestand", - "file_name": "Bestandsnaam", - "retry": "Opnieuw proberen", - "refresh": "Vernieuwen", - "toggle_favorite": "Favoriet aan/uit", - "duplicate": "Dupliceren", - "duplicate_success": "Succesvol gedupliceerd", - "duplicate_error": "Dupliceren mislukt", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Bestand aangemaakt", - "create_file_error": "Bestand aanmaken mislukt", - "favorites": "Favorieten", - "recent": "Recent", - "properties": "Eigenschappen", - "open_folder": "Map openen", - "upload_folder": "Map uploaden", - "file_too_large": "\"{name}\" overschrijdt de maximale bestandsgrootte ({max})", - "undo": "Ongedaan maken", - "undo_success": "Actie ongedaan gemaakt", - "undo_error": "Ongedaan maken mislukt", - "toolbar": "Bestandsacties", - "file_list": "Bestanden en mappen", - "context_menu": "Acties", - "settings_title": "Bestandsinstellingen", - "settings_display": "Weergave", - "settings_default_view": "Standaardweergave", - "settings_default_view_desc": "Kies tussen raster- en lijstweergave", - "settings_default_sort": "Standaardsortering", - "settings_default_sort_desc": "Kies de standaardsortering voor bestanden", - "settings_sort_direction": "Sorteerrichting", - "settings_sort_direction_desc": "Kies oplopende of aflopende volgorde", - "settings_ascending": "Oplopend", - "settings_descending": "Aflopend", - "settings_icons": "Pictogrammen", - "settings_show_icons": "Bestandspictogrammen tonen", - "settings_show_icons_desc": "Pictogrammen naast bestanden en mappen weergeven", - "settings_colored_icons": "Gekleurde pictogrammen", - "settings_colored_icons_desc": "Gebruik gekleurde pictogrammen in plaats van monochroom", - "settings_show_thumbnails": "Miniaturen weergeven", - "settings_show_thumbnails_desc": "Toon afbeeldingsvoorbeelden in plaats van pictogrammen", - "settings_behavior": "Gedrag", - "settings_show_hidden": "Verborgen bestanden tonen", - "settings_show_hidden_desc": "Bestanden en mappen weergeven die beginnen met een punt", - "settings_folder_layout": "Mapnavigatie", - "settings_folder_layout_desc": "Kies hoe mappen worden weergegeven: inline bij bestanden of in een boomstructuur in de zijbalk", - "settings_folder_layout_inline": "Inline", - "settings_folder_layout_sidebar": "Zijbalk", - "disabled_title": "De bestandsfunctie is uitgeschakeld door uw beheerder", - "disabled_description": "Grote bestandsuploads via WebDAV kunnen Stalwart/RocksDB-instabiliteit veroorzaken, waaronder geheugenfouten en onherstelbaar schijfgebruik. Verwijderde bestanden worden mogelijk niet onmiddellijk uit de opslag verwijderd. Deze functie wordt niet aanbevolen voor productieomgevingen.", - "stability_warning": "Grote bestandsuploads kunnen serverinstabiliteit veroorzaken. Verwijderde bestanden worden mogelijk niet onmiddellijk uit de opslag verwijderd. Gebruik met voorzichtigheid.", - "no_accounts": "Geen gekoppelde accounts.", - "open_folder_tree": "Mappenstructuur openen", - "other_accounts": "Andere accounts", - "migration_title": "Je bestanden worden bijgewerkt…", - "migration_description": "Mappen en bestanden worden in de juiste structuur georganiseerd. Dit gebeurt slechts één keer." - }, - "smime": { - "your_certificates": "Uw certificaten", - "your_certificates_desc": "Importeer en beheer uw S/MIME-certificaten voor het ondertekenen en versleutelen van e-mails", - "recipient_certificates": "Certificaten van ontvangers", - "recipient_certificates_desc": "Openbare certificaten om e-mails naar ontvangers te versleutelen", - "identity_bindings": "Koppelingen tussen identiteit en sleutel", - "identity_bindings_desc": "Koppel S/MIME-certificaten aan uw e-mailidentiteiten", - "defaults_title": "Standaardinstellingen", - "defaults_desc": "Stel standaardgedrag voor ondertekenen en versleutelen in", - "import_pkcs12": "PKCS#12 importeren (.p12/.pfx)", - "import_public_cert": "Certificaat importeren", - "no_certificates": "Nog geen certificaten geïmporteerd", - "no_recipient_certs": "Geen ontvangerscertificaten", - "expires": "Verloopt", - "expired": "Verlopen", - "bound_to": "Gekoppeld aan", - "no_key_bound": "Geen", - "lock": "Sleutel vergrendelen", - "unlock": "Sleutel ontgrendelen", - "details": "Details bekijken", - "delete": "Verwijderen", - "encrypt_by_default": "Standaard versleutelen", - "encrypt_by_default_desc": "E-mails automatisch versleutelen wanneer alle ontvangers certificaten hebben", - "remember_unlocked": "Ontgrendelde sleutels onthouden", - "remember_unlocked_desc": "Sleutels ontgrendeld houden gedurende deze browsersessie", - "sign_default_for": "Standaard ondertekenen voor", - "enter_p12_passphrase": "PKCS#12-wachtwoordzin invoeren", - "p12_passphrase_desc": "Voer de wachtwoordzin in die dit certificaatbestand beschermt", - "enter_storage_passphrase": "Opslagwachtwoordzin instellen", - "storage_passphrase_desc": "Kies een wachtwoordzin om deze sleutel in uw browser te beschermen", - "next": "Volgende", - "import": "Importeren", - "unlock_key": "Sleutel ontgrendelen", - "unlock_key_desc": "Voer de opslagwachtwoordzin in om deze sleutel te ontgrendelen voor ondertekenen of ontsleutelen", - "passphrase_placeholder": "Voer wachtwoordzin in", - "confirm_passphrase_placeholder": "Bevestig wachtwoordzin", - "passphrase_mismatch": "Wachtwoordzinnen komen niet overeen", - "cancel": "Annuleren", - "processing": "Bezig met verwerken…", - "close": "Sluiten", - "certificate_details": "Certificaatdetails", - "cert_subject": "Onderwerp", - "cert_issuer": "Uitgever", - "cert_email": "E-mail", - "cert_serial": "Serienummer", - "cert_validity": "Geldigheid", - "cert_fingerprint": "Vingerafdruk (SHA-256)", - "cert_algorithm": "Algoritme", - "cert_capabilities": "Mogelijkheden", - "cert_source": "Bron", - "cert_expired": "Dit certificaat is verlopen", - "cert_not_yet_valid": "Dit certificaat is nog niet geldig", - "cap_sign": "Ondertekenen", - "cap_encrypt": "Versleuteling", - "cap_none": "Geen", - "show_passphrase": "Wachtwoordzin tonen", - "hide_passphrase": "Wachtwoordzin verbergen", - "sign_toggle": "Ondertekenen", - "encrypt_toggle": "Versleutelen", - "missing_recipient_certs": "Ontbrekende certificaten voor: {emails}", - "missing_sender_cert": "Er is geen certificaat gekoppeld aan deze identiteit", - "status_encrypted_ok": "Dit bericht is versleuteld", - "status_encrypted_no_key": "Dit bericht is versleuteld, maar er is geen passende sleutel gevonden", - "status_encrypted_failed": "Dit bericht kon niet worden ontsleuteld", - "status_signed_valid": "Handtekening geverifieerd", - "status_signed_invalid": "Verificatie van de handtekening is mislukt", - "status_signed_expired_cert": "Ondertekend met een verlopen certificaat", - "status_signed_self_signed": "Handtekening geldig, maar het certificaat is zelfondertekend (niet vertrouwd)", - "status_signed_mismatch": "Handtekening is geldig, maar de ondertekenaar komt niet overeen met de afzender", - "status_unsupported": "Niet-ondersteund S/MIME-formaat", - "auto_import_signer_certs": "Ondertekenaarcertificaten automatisch importeren", - "auto_import_signer_certs_desc": "Certificaten van geverifieerde ondertekende e-mails automatisch opslaan voor toekomstige versleuteling", - "export": "Exporteren", - "enter_export_passphrase": "Exportwachtwoordzin instellen", - "export_passphrase_desc": "Kies een wachtwoordzin om het geëxporteerde PKCS#12-bestand te beschermen", - "export_storage_desc": "Voer de opslagwachtwoordzin in om de sleutel voor export te ontsleutelen", - "incorrect_passphrase": "Onjuiste wachtwoordzin" - }, - "tour": { - "step_counter": "Stap {current} van {total}", - "skip": "Tour overslaan", - "back": "Terug", - "next": "Volgende", - "finish": "Voltooien", - "take_a_tour": "Maak een rondleiding door de interface", - "restart_title": "Introductietour", - "restart_desc": "Bekijk de rondleiding door de interface opnieuw", - "restart_button": "Tour herstarten", - "show_on_new_devices_title": "Tonen op nieuwe apparaten", - "show_on_new_devices_desc": "Herhaal de welkomstbanner en de rondleiding wanneer je voor het eerst inlogt op een nieuw apparaat, zelfs als je ze elders al hebt voltooid", - "sidebar_title": "Uw mailboxen", - "sidebar_desc": "Dit is uw mappenbalk. Klik op een mailbox om de e-mails te bekijken. U kunt mappen maken, e-mails tussen mappen slepen en ongelezen aantallen zien.", - "compose_title": "E-mail schrijven", - "compose_desc": "Klik hier om een nieuwe e-mail te schrijven. U kunt ontvangers, bijlagen toevoegen en rijke tekstopmaak gebruiken.", - "search_title": "Zoek in uw mail", - "search_desc": "Zoek op afzender, onderwerp of inhoud. Klik op het filtericoon voor geavanceerde opties zoals datumbereik, bijlagen en favoriete berichten.", - "email_list_title": "Uw e-maillijst", - "email_list_desc": "E-mails verschijnen hier. Klik op een e-mail om deze rechts te lezen. Gebruik het selectievakje om meerdere te selecteren, verplaats, verwijder of label ze vervolgens.", - "email_viewer_title": "Leesvenster", - "email_viewer_desc": "De geselecteerde e-mail opent hier. Beantwoord, doorstuur, archiveer of verwijder met de werkbalkknopen. U kunt ook e-mails markeren of kleurlabels toevoegen.", - "keywords_title": "Kleurlabels", - "keywords_desc": "Organiseer uw e-mail met kleurgecodeerde labels. Sleep een e-mail naar een label om het te markeren, of klik met de rechtermuisknop om labels toe te wijzen.", - "calendar_title": "Agenda", - "calendar_desc": "Schakel naar de agenda om uw evenementen te beheren. Maak evenementen aan, stel herinneringen in en bekijk dag-, week- of maandweergaven.", - "contacts_title": "Contacten", - "contacts_desc": "Uw adresboek bevindt zich hier. Importeer contacten, maak groepen aan en klik op een contact om de volledige details te bekijken.", - "settings_title": "Instellingen", - "settings_desc": "Pas alles aan: thema, dichtheid, handtekeningen, filters, sneltoetsen, agendainstellingen en meer.", - "shortcuts_title": "Sneltoetsen", - "shortcuts_desc": "Ervaren gebruikers zijn hier dol op. Druk op ? om alle beschikbare sneltoetsen te bekijken. Navigeer, schrijf en beheer e-mails zonder de muis aan te raken.", - "compose_open_title": "De e-maileditor", - "compose_open_desc": "Dit is de e-maileditor. Voeg ontvangers toe, schrijf uw bericht, voeg bestanden bij en gebruik rijke tekstopmaak. U kunt ook concepten opslaan en sjablonen gebruiken.", - "calendar_view_title": "Uw agenda", - "calendar_view_desc": "Hier is uw agenda met voorbeeldevenementen. Schakel tussen dag-, week-, maand- en agendaweergave via de werkbalk.", - "create_event_title": "Evenement aanmaken", - "create_event_desc": "Klik op deze knop om een nieuw agenda-evenement aan te maken. U kunt een titel, datum, tijd en deelnemers instellen.", - "event_modal_title": "Evenementdetails", - "event_modal_desc": "Hier is het evenementformulier. Vul de titel in, kies een datum en tijd, voeg een locatie of deelnemers toe. Klik op opslaan als u klaar bent - of sluit het en ga verder.", - "contacts_list_title": "Uw contacten", - "contacts_list_desc": "Hier zijn uw contacten. Klik op een contact om de details rechts te bekijken. U kunt ook nieuwe contacten aanmaken, vCards importeren of contacten in groepen organiseren.", - "files_title": "Bestandsopslag", - "settings_tabs_title": "Instellingenmenu", - "settings_tabs_desc": "Hier vindt u alle instellingscategorieën. Pas het uiterlijk aan, beheer identiteiten, stel e-mailfilters in, configureer uw agenda en nog veel meer.", - "files_desc": "Uw bestandsbrowser laat u bestanden uploaden, organiseren en delen - als een persoonlijke cloud geïntegreerd in uw mail.", - "demo_banner_title": "Demo-bediening", - "demo_banner_desc": "U bent in demomodus - alles blijft in uw browser. Klik op 'Demo resetten' om opnieuw te beginnen met schone voorbeeldgegevens.", - "quota_title": "Opslaggebruik", - "quota_desc": "Volg de grootte van uw mailbox hier. De cirkel vult zich naarmate u meer ruimte gebruikt." - }, - "unified_mailbox": { - "search_unavailable": "Zoeken is niet beschikbaar in de gecombineerde weergave" - }, - "calendar_event_notifications": { - "someone": "Iemand", - "invited": "{name} heeft je uitgenodigd voor \"{title}\"", - "updated": "{name} heeft \"{title}\" bijgewerkt", - "cancelled": "{name} heeft \"{title}\" geannuleerd", - "untitled": "Afspraak zonder titel" - }, - "share_notifications": { - "someone": "Iemand", - "shared": "{name} heeft {kind} \"{object}\" met je gedeeld", - "changed": "{name} heeft je toegang tot {kind} \"{object}\" gewijzigd", - "revoked": "{name} heeft je toegang tot {kind} \"{object}\" ingetrokken", - "object": { - "folder": "de map", - "calendar": "de agenda", - "address_book": "het adresboek", - "files": "de bestandsmap" - } - }, - "sharing": { - "title": "\"{name}\" delen", - "description": "Geef andere gebruikers of groepen op deze server toegang. Wijzigingen zijn direct van kracht.", - "no_shares": "Nog niet gedeeld.", - "add_person": "Persoon of groep toevoegen", - "search_placeholder": "Zoeken op naam of e-mail…", - "loading_principals": "Gebruikers laden…", - "no_principals": "Geen andere gebruikers of groepen gevonden.", - "no_match": "Geen overeenkomsten.", - "remove": "Toegang intrekken", - "group": "Groep", - "share_added": "Toegang verleend", - "share_updated": "Toegang bijgewerkt", - "share_removed": "Toegang ingetrokken", - "share_failed": "Delen kon niet worden bijgewerkt", - "preset": { - "freeBusy": "Alleen vrij/bezet", - "read": "Alleen lezen", - "readWrite": "Lezen en schrijven", - "manager": "Beheerder", - "custom": "Aangepast" - } - }, - "quote_header": { - "reply_line": "Op {date} schreef {from}:", - "forwarded_separator": "---------- Doorgestuurd bericht ----------", - "from_label": "Van", - "date_label": "Datum", - "subject_label": "Onderwerp" - }, - "pwa_install": { - "title": "{appName} installeren", - "description": "Installeer onze app voor snelle toegang en offline-ondersteuning.", - "not_now": "Niet nu", - "install": "Installeren", - "dont_remind": "Niet meer herinneren", - "dismiss_aria": "Installatiemelding sluiten" - }, - "deep_link": { - "message_not_found": "Dit bericht is niet meer beschikbaar.", - "thread_not_found": "Dit gesprek is niet meer beschikbaar.", - "folder_not_found": "Deze map is niet meer beschikbaar.", - "event_not_found": "Deze afspraak is niet meer beschikbaar.", - "file_not_found": "Dit bestand is niet meer beschikbaar.", - "account_unavailable": "Log in op dat account om deze link te openen.", - "copy_message": "Link naar bericht kopiëren", - "copy_conversation": "Link naar gesprek kopiëren", - "copy_event": "Link naar afspraak kopiëren", - "copied": "Link gekopieerd", - "copy_failed": "Kon de link niet kopiëren" - }, - "plugins": { - "oauth_callback": { - "completing": "Autorisatie voltooien…", - "completing_message": "Even geduld terwijl de verbinding tot stand wordt gebracht.", - "received_title": "Autorisatie ontvangen", - "received_message": "Je kunt terugkeren naar het tabblad waar je de verbinding bent gestart.", - "failed_title": "Autorisatie mislukt", - "denied": "Autorisatie geweigerd.", - "failed_detail": "Autorisatie mislukt: {error}", - "missing_params": "Autorisatiecode of status ontbreekt.", - "delivery_failed": "Het autorisatieresultaat kon niet worden afgeleverd bij het app-tabblad. Sluit dit venster en probeer het opnieuw." - } - }, - "global_search": { - "title": "Alles doorzoeken", - "placeholder": "Zoek in mail, contacten, agenda, bestanden...", - "scope_label": "Zoeken in", - "account_label": "Account", - "scope_all": "Alles", - "scope_mail": "E-mail", - "scope_contacts": "Contacten", - "scope_calendar": "Agenda", - "scope_files": "Bestanden", - "all_accounts": "Alle accounts", - "show_all": "Alle resultaten tonen", - "no_results": "Geen resultaten voor “{query}”", - "server_hint": "Volledige woorden doorzoeken de server; woorddelen vinden al geladen items", - "searching": "Zoeken...", - "account_error": "Kon niet zoeken in {account}", - "recent_searches": "Recente zoekopdrachten", - "recurring": "Terugkerend", - "no_subject": "(no subject)", - "empty_hint": "Doorzoek mail, contacten, afspraken en bestanden van alle accounts. Probeer from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Plat op datum", - "grouped": "Gegroepeerd", - "load_more": "Meer laden", - "preview_empty": "Selecteer een resultaat voor een voorbeeld", - "open_full": "Openen", - "view_label": "Weergave" - } -} +{ + "meta_description": "Minimalistische webmailclient die het JMAP-protocol gebruikt", + "login": { + "title": "Webmail", + "username_label": "E-mail", + "username_placeholder": "gebruiker@voorbeeld.nl", + "password_label": "Wachtwoord", + "password_placeholder": "Voer je wachtwoord in", + "jmap_endpoint_label": "JMAP-server", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "De server moet CORS-verzoeken van dit domein toestaan.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Server geselecteerd op basis van je e-maildomein.", + "sign_in": "Aanmelden", + "signing_in": "Aanmelden...", + "loading": "Laden...", + "reconnecting": "Verbinding verloren. Opnieuw verbinden…", + "error": { + "invalid_credentials": "Ongeldig e-mailadres of wachtwoord", + "invalid_token": "De server heeft dit toegangstoken niet geaccepteerd. Controleer of het volledig is en niet is verlopen of ingetrokken.", + "connection_failed": "Kan geen verbinding maken met de server", + "cors_blocked": "De server is bereikbaar maar blokkeert cross-origin verzoeken. Controleer de CORS-instellingen van uw JMAP-server en sta dit domein toe.", + "server_error": "De server is tijdelijk niet beschikbaar. Probeer het later opnieuw.", + "generic": "Er is een fout opgetreden. Probeer het opnieuw.", + "totp_required": "Een tweefactorauthenticatiecode is vereist. Voer uw code hieronder in.", + "totp_invalid": "Ongeldige authenticatiecode. Controleer uw authenticator-app.", + "oauth_discovery_failed": "SSO is ingeschakeld maar de identiteitsprovider is niet bereikbaar. Controleer uw OAuth-configuratie.", + "token_exchange_failed": "Uw wachtwoord en code zijn geaccepteerd, maar de mailserver weigerde een sessie voor deze app te starten. Vraag uw beheerder de OAuth-clientinstellingen te controleren." + }, + "show_password": "Wachtwoord tonen", + "hide_password": "Wachtwoord verbergen", + "totp_toggle": "Ik heb een 2FA-code", + "token_toggle": "Aanmelden met een toegangstoken", + "password_toggle": "Aanmelden met een wachtwoord", + "token_label": "Toegangstoken", + "token_placeholder": "Plak uw API-token", + "token_hint": "Maak een API-token aan in de instellingen van uw e-mailprovider en plak het hier.", + "remember_me": "Onthoud mij", + "config_error": { + "title": "Configuratiefout", + "fetch_failed": "Kan de applicatieconfiguratie niet laden. Probeer het later opnieuw.", + "server_not_configured": "De mailserver is niet geconfigureerd. Neem contact op met je beheerder." + }, + "remove_from_history": "Verwijder uit geschiedenis", + "totp_label": "Authenticatiecode", + "totp_placeholder": "000000", + "session_expired": "Uw sessie is verlopen. Meld u opnieuw aan.", + "dismiss": "Sluiten", + "or": "of", + "sign_in_sso": "Inloggen met SSO", + "add_account_title": "Account toevoegen", + "add_account_subtitle": "Inloggen met een ander account", + "cancel": "Annuleren", + "website": "Website", + "imprint": "Colofon", + "privacy_policy": "Privacybeleid", + "try_demo": "Demo proberen", + "demo_description": "Verken met voorbeeldgegevens - geen account nodig", + "demo_launching": "Demo starten...", + "demo_login_button": "Demo starten", + "demo_tagline": "Ervaar een complete e-mailclient. Geen account nodig.", + "demo_no_signup": "Geen registratie nodig - verken vrij met voorbeeldgegevens", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Aanmelding voltooien...", + "oauth_error": { + "title": "Authenticatie mislukt", + "invalid_state": "Beveiligingsvalidatie mislukt. Probeer opnieuw in te loggen.", + "missing_params": "Ontbrekende autorisatiegegevens. Probeer opnieuw in te loggen.", + "token_exchange_failed": "Authenticatie kon niet worden voltooid. Probeer het opnieuw.", + "access_denied": "Toegang geweigerd. Neem contact op met uw beheerder.", + "back_to_login": "Terug naar inloggen" + } + }, + "sidebar": { + "close": "Sluiten", + "compose": "Nieuw bericht", + "compose_hint": "Opstellen (c)", + "search_placeholder": "Zoeken in e-mail...", + "search_placeholder_hint": "E-mail zoeken... (druk /)", + "storage": "Opslag", + "storage_used": "Gebruikt", + "storage_free": "Vrij", + "storage_total": "Totaal", + "sign_out": "Afmelden", + "sign_out_of": "Uitloggen van {account}", + "sign_out_all": "Uitloggen van alle accounts", + "add_account": "Account toevoegen", + "set_as_default": "Als standaard instellen", + "switch_account": "Account wisselen", + "contacts": "Contacten", + "calendar": "Agenda", + "settings": "Instellingen", + "admin": "Admin", + "files": "Bestanden", + "loading_mailboxes": "Mappen laden...", + "push_connected": "Real-time updates actief", + "push_disconnected": "Real-time updates inactief", + "keyboard_shortcuts": "Sneltoetsen", + "theme": { + "light": "Lichte modus", + "dark": "Donkere modus", + "system": "Systeemthema" + }, + "language": { + "title": "Taal" + }, + "mailboxes": { + "inbox": "Postvak IN", + "sent": "Verzonden", + "drafts": "Concepten", + "trash": "Prullenbak", + "archive": "Archief", + "starred": "Met ster", + "all_mail": "Alle e-mail", + "spam": "Spam", + "important": "Belangrijk" + }, + "unified_inbox": "Gecombineerd postvak IN", + "unified_sent": "Alle verzonden", + "unified_drafts": "Alle concepten", + "unified_trash": "Alle prullenbakken", + "unified_archive": "Alle archieven", + "unified_junk": "Alle spam", + "all_accounts": "Alle accounts", + "all_accounts_loading": "Accounts laden: {loaded} van {total}", + "unified_mailbox": "Gecombineerd postvak", + "expand": "Uitklappen", + "collapse": "Inklappen", + "expand_tooltip": "Uitklappen", + "collapse_tooltip": "Inklappen", + "mobile": { + "search": "Zoeken", + "compose": "Opstellen", + "go_back": "Terug", + "toggle_menu": "Menu wisselen" + }, + "clear_search": "Zoekopdracht wissen", + "vacation_active": "Afwezigheidsmelder is actief", + "demo_banner": "Demomodus", + "demo_reset": "Resetten", + "demo_tour": "Rondleiding", + "tags": "Labels", + "show_all_tags": "Alles tonen ({count})", + "show_fewer_tags": "Minder tonen", + "folders": "Mappen", + "your_folders": "Je mappen", + "create_folder": "Map maken", + "show_more": "Meer", + "show_fewer": "Minder", + "mail": "E-mail", + "nav_label": "Navigatie", + "add_app": "Apps", + "shared": "Gedeeld", + "shared_account_hidden": "Verborgen in de zijbalk. Zet het weer aan via Instellingen › Indeling.", + "notifications_muted": "Meldingen gedempt", + "scheduled": "Gepland", + "unified_all_unread": "Alle ongelezen", + "unified_all_starred": "Alle met ster", + "unified_all_mail": "Alle e-mail", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Labels", + "create_label": "Nieuw label maken", + "mv_tasks": "Taken", + "mv_side_panel": "Zijpaneel", + "mv_open_full": "Openen in volledige weergave", + "mv_previous_day": "Vorige dag", + "mv_next_day": "Volgende dag", + "mv_add_another_account": "Nog een account toevoegen", + "mv_manage_account": "Account beheren", + "mv_storage_used": "{percent}% van {total} gebruikt" + }, + "protocol_handlers": { + "title": "Standaardapps", + "description": "Kies of e-mail- en kalenderlinks in Bulwark worden geopend. Technisch registreert Bulwark zich als protocolhandler voor mailto:- en webcal:-links.", + "unsupported": "Deze browser of verbinding ondersteunt geen handmatige registratie van protocolhandlers. Mogelijk kun je de geïnstalleerde PWA nog gebruiken via de browser- of systeeminstellingen.", + "mailto_label": "E-maillinks", + "mailto_description": "Opent mailto:-links in Bulwark met een vooraf ingevulde opsteller.", + "protocol_open_mode_label": "Bij het openen van protocollinks", + "protocol_open_mode_description": "Kies of Bulwark mailto:- en webcal:-links in een nieuw tabblad opent of een geopende sessie hergebruikt. Voor de optie actieve sessie is toestemming voor meldingen nodig, zodat je op een fallbackmelding kunt klikken om Bulwark naar voren te halen als de browser focus blokkeert.", + "protocol_open_mode_active_session": "Indien mogelijk openen in actieve sessie", + "protocol_open_mode_new_tab": "Altijd een nieuw tabblad openen", + "focus_notification_title": "Bulwark openen", + "focus_notification_body": "De link is geopend in Bulwark. Klik om het venster naar voren te halen.", + "webcal_label": "Kalenderlinks", + "webcal_description": "Opent webcal:-links in Bulwark met een vooraf ingevuld dialoogvenster voor kalenderabonnementen.", + "register_mailto": "E-mailapp registreren", + "register_webcal": "Kalenderapp registreren", + "mailto_registered": "Registratie van e-mailhandler aangevraagd", + "webcal_registered": "Registratie van kalenderhandler aangevraagd", + "registration_failed": "Registratie van protocolhandler mislukt", + "opening_mailto": "Opsteller wordt geopend...", + "opening_webcal": "Kalender wordt geopend...", + "browser_note": "Je browser of besturingssysteem kan om bevestiging vragen en kan vereisen dat Bulwark is geïnstalleerd voordat het als standaardapp kan worden geselecteerd.", + "select_account_title": "Account kiezen", + "select_mailto_account": "Kies welk account deze e-maillink moet openen.", + "select_webcal_account": "Kies welk account deze kalenderlink moet openen.", + "select_account_note": "Deze keuze geldt alleen voor deze protocol-link.", + "detail_to": "Aan", + "detail_subject": "Onderwerp", + "detail_no_subject": "Geen onderwerp", + "detail_calendar": "Agenda", + "detail_source": "Bron", + "active_account": "Actief", + "switching_account": "Account wisselen..." + }, + "sidebar_apps": { + "modal_title": "Zijbalk-apps", + "add_new": "App toevoegen", + "edit_app": "App bewerken", + "name_label": "Naam", + "name_placeholder": "Mijn app", + "name_required": "Naam is vereist", + "url_label": "URL", + "url_required": "URL is vereist", + "url_invalid": "Voer een geldige http- of https-URL in", + "icon_label": "Pictogram", + "icon_required": "Pictogram is vereist", + "open_mode_label": "Openingsmodus", + "open_new_tab": "Nieuw tabblad", + "open_inline": "Ingesloten", + "cancel": "Annuleren", + "add": "Toevoegen", + "update": "Bijwerken", + "delete": "Verwijderen", + "delete_confirm_title": "App verwijderen", + "delete_confirm": "Weet je zeker dat je \"{name}\" wilt verwijderen?", + "no_apps": "Nog geen apps toegevoegd", + "no_apps_hint": "Voeg aangepaste apps en links toe aan je zijbalk", + "managed_title": "Aangeboden door uw beheerder", + "managed_description": "Deze apps zijn voor iedereen ingesteld en kunnen niet worden gewijzigd.", + "managed_badge": "Beheerd", + "search_icons": "Pictogrammen zoeken...", + "show_popular": "Populair", + "show_all": "Alle", + "no_icons_found": "Geen pictogrammen gevonden", + "inline_badge": "Ingesloten", + "tab_badge": "Tabblad", + "show_on_mobile": "Weergeven op mobiel" + }, + "email_list": { + "columns": { + "from": "Van", + "to": "Aan", + "subject": "Onderwerp", + "date": "Datum", + "size": "Grootte" + }, + "no_emails": "Geen berichten gevonden", + "no_emails_description": "Deze mailbox is leeg", + "no_search_results": "Geen resultaten gevonden", + "no_search_results_description": "Pas je zoekopdracht of filters aan", + "loading": "E-mails laden...", + "unread": "ongelezen", + "to_me": "Aan mij", + "to_recipients": "Aan {count} ontvangers", + "and_others": "en {count} anderen", + "draft": "Concept", + "starred": "Met ster", + "pinned": "Vastgezet", + "replied": "Beantwoord", + "forwarded": "Doorgestuurd", + "has_attachment": "Met bijlage", + "conversations_range": "{from}–{to, number} van {total, number}", + "conversations_count": "{count} van {total} gesprekken", + "conversations_count_plus": "{count}+ gesprekken", + "conversations_count_simple": "{count} gesprekken", + "no_conversations": "Geen gesprekken", + "loading_more": "Meer e-mails laden...", + "no_more_emails": "Geen e-mails meer om te laden", + "batch_actions": { + "select": "E-mails selecteren", + "select_all": "Alles selecteren", + "mark_read": "Markeren als gelezen", + "selected_messages": "{count, plural, one {1 e-mail} other {# e-mails}} geselecteerd", + "mark_unread": "Markeren als ongelezen", + "delete": "Verwijderen", + "delete_confirm_title": "E-mails verwijderen", + "delete_confirm_message": "Weet u zeker dat u {count, plural, one {1 e-mail} other {# e-mails}} wilt verwijderen?", + "clear_selection": "Selectie wissen" + }, + "permanent_delete": "Definitief verwijderen", + "permanent_delete_confirm_title": "Definitief verwijderen", + "permanent_delete_confirm_message": "Deze e-mail wordt definitief verwijderd. Deze actie kan niet ongedaan worden gemaakt.", + "permanent_delete_confirm_batch_message": "Deze {count, plural, one {1 e-mail} other {# e-mails}} worden definitief verwijderd. Deze actie kan niet ongedaan worden gemaakt.", + "empty_folder": { + "button": "Map legen", + "confirm_title": "Map legen", + "confirm_message": "Alle e-mails in deze map worden permanent verwijderd. Deze actie kan niet ongedaan worden gemaakt.", + "confirm_message_trash": "Alle e-mails in deze map worden naar de prullenbak verplaatst.", + "confirm_button": "Map legen", + "junk_hint": "U kunt de map Spam legen om alle berichten permanent te verwijderen.", + "trash_hint": "U kunt de prullenbak legen om alle berichten permanent te verwijderen." + }, + "no_scheduled_emails": "Geen geplande e-mails", + "no_scheduled_emails_description": "Berichten die voor later zijn gepland verschijnen hier.", + "scheduled_count": "{count, plural, one {1 geplande e-mail} other {# geplande e-mails}}", + "scheduled_actions_hint": "Geplande berichten kunnen worden geannuleerd, opnieuw gepland of bewerkt via hun geplande acties.", + "cancel_scheduled_send": "Verzenden annuleren", + "reschedule_send": "Opnieuw plannen", + "cancel_and_edit": "Annuleren en bewerken", + "cancel_and_compose_again": "Annuleren en opnieuw opstellen", + "reschedule_prompt": "Voer een nieuwe datum/tijd in, bijv. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "De afzender vraagt om een leesbevestiging:", + "send": "Bevestiging verzenden", + "ignore": "Negeren", + "sent": "Leesbevestiging verzonden.", + "send_failed": "Leesbevestiging kon niet worden verzonden", + "mdn_subject": "Gelezen: {subject}", + "mdn_body": "Dit is een leesbevestiging voor het bericht dat u hebt verzonden naar {recipient}.\n\nLet op: deze bevestiging geeft alleen aan dat het bericht is weergegeven op de computer van de ontvanger. Er is geen garantie dat de ontvanger de inhoud heeft gelezen of begrepen." + }, + "no_email_selected": "Geen e-mail geselecteerd", + "no_email_description": "Selecteer een e-mail uit de lijst om deze hier te bekijken", + "no_conversation_selected": "Geen gesprek geselecteerd", + "no_conversation_description": "Kies een gesprek uit de lijst om het hier te lezen", + "compose": "Opstellen", + "compose_hint": "Nieuw bericht opstellen", + "no_subject": "(Geen onderwerp)", + "no_body_content": "(Geen inhoud beschikbaar)", + "no_preview_available": "Geen voorbeeld beschikbaar", + "show_quoted_text": "Geciteerde tekst tonen", + "hide_quoted_text": "Geciteerde tekst verbergen", + "loading_email": "E-mail laden...", + "loading": "Laden...", + "reply": "Beantwoorden", + "reply_all": "Allen beantwoorden", + "forward": "Doorsturen", + "delete": "Verwijderen", + "archive": "Archiveren", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Ster toevoegen", + "unstar": "Ster verwijderen", + "mark_unread": "Markeren als ongelezen", + "mark_read": "Markeren als gelezen", + "unread": "Ongelezen", + "read": "Gelezen", + "spam_short": "Spam", + "not_spam_short": "Geen spam", + "move": "Verplaatsen", + "print": "Afdrukken", + "view_source": "Bron bekijken", + "fullscreen": "Volledig scherm", + "exit_fullscreen": "Volledig scherm sluiten", + "view_in_dark_mode": "Weergeven in donkere modus", + "view_in_light_mode": "Weergeven in lichte modus", + "export_email": "Exporteren als .eml", + "forward_as_attachment": "Doorsturen als bijlage", + "import_email": ".eml of .zip importeren", + "keyboard_shortcuts": "Sneltoetsen (?)", + "email_source": "E-mailbron", + "draft_banner": "Dit bericht is een concept", + "edit_draft": "Bewerken", + "copy_source": "Kopiëren naar klembord", + "source_copied": "Bron gekopieerd naar klembord", + "attachments": "Bijlagen", + "important": "Belangrijk", + "download": "Downloaden", + "download_all": "Alles downloaden", + "from": "Van", + "to": "Aan", + "cc": "CC", + "bcc": "BCC", + "date": "Datum", + "subject": "Onderwerp", + "show_details": "Details tonen", + "hide_details": "Details verbergen", + "external_content_warning": "Afbeeldingen en externe inhoud zijn geblokkeerd", + "load_external_content": "Afbeeldingen laden", + "trust_sender": "Deze afzender altijd vertrouwen", + "sender_check": { + "unverified_label": "Niet-geverifieerde afzender", + "failed_label": "Afzendercontrole mislukt", + "unverified": "Dit bericht beweert van {domain} te komen, maar niets bevestigt dat. Het heeft geen geldige handtekening van {domain} en kwam niet van een server die {domain} toestaat.", + "unverified_sent_from": "Dit bericht beweert van {domain} te komen, maar is verzonden vanaf {host} en heeft geen geldige handtekening van {domain}.", + "failed": "Dit bericht beweert van {domain} te komen, maar is niet door de afzendercontroles van dat domein gekomen. Het kan vervalst zijn.", + "failed_sent_from": "Dit bericht beweert van {domain} te komen, maar is verzonden vanaf {host} en is niet door de afzendercontroles van {domain} gekomen. Het kan vervalst zijn.", + "caution": "Wees voorzichtig met links, bijlagen en verzoeken om wachtwoorden of betaalgegevens." + }, + "back_to_list": "Terug naar lijst", + "view_contact": "Contact bekijken", + "message_details": "Berichtdetails", + "more_reply_options": "Meer antwoordopties", + "set_tag": "Label instellen", + "tag": "Label", + "more_actions": "Meer acties", + "move_to": "Verplaatsen naar...", + "remove_tag": "Label verwijderen", + "tag_filter_placeholder": "Labels filteren", + "tag_no_matches": "Geen overeenkomende labels", + "more_count": "+{count} meer", + "characters_count": "{count} tekens", + "quick_reply_placeholder": "Schrijf een snel antwoord...", + "more_options": "Meer opties", + "sending": "Verzenden...", + "security_authentication": "Beveiliging & Authenticatie", + "technical_details": "Technische details", + "message_id_label": "Bericht-ID:", + "reply_to_label": "Antwoord naar:", + "delivery_time_label": "Bezorgtijd:", + "conversation_part_label": "Onderdeel van gesprek:", + "previous_messages": "{count} vorig bericht", + "previous_messages_plural": "{count} vorige berichten", + "time": { + "day": "dag", + "days": "dagen", + "hour": "uur", + "hours": "uur", + "minute": "minuut", + "minutes": "minuten" + }, + "unknown_sender": "Onbekend", + "recipient_me": "mij", + "recipient_and_others": "{name} en {count} anderen", + "recipient_to_prefix": "Aan:", + "authentication": { + "title": "Authenticatie", + "status": { + "verified": "Geverifieerd", + "warning": "Waarschuwing", + "none": "Niet geauthenticeerd" + }, + "spf": { + "pass": "SPF Geslaagd", + "fail": "SPF Mislukt", + "none": "Geen SPF" + }, + "dkim": { + "pass": "DKIM Geldig", + "fail": "DKIM Ongeldig", + "none": "Geen DKIM" + }, + "dmarc": { + "pass": "DMARC Geslaagd", + "fail": "DMARC Mislukt", + "none": "Geen DMARC" + }, + "spam_score": "Spamscore", + "tooltip_spf": "Sender Policy Framework: controleert of de verzendende server gemachtigd is om e-mail namens het domein te verzenden", + "tooltip_dkim": "DomainKeys Identified Mail: bevestigt met een cryptografische handtekening dat de e-mail tijdens het transport niet is gewijzigd", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zorgt ervoor dat SPF en DKIM overeenkomen met het domein van de afzender en stelt een beleid in voor fouten", + "policy": "Beleid", + "result": { + "pass": "Geslaagd", + "fail": "Mislukt", + "softfail": "Zachte mislukking", + "neutral": "Neutraal", + "permerror": "Permanente fout", + "temperror": "Tijdelijke fout", + "none": "Geen" + } + }, + "headers": { + "routing": "Routering", + "received": "Ontvangen", + "message_id": "Bericht-ID", + "list_info": "Lijstinformatie" + }, + "tooltips": { + "reply": "Beantwoorden", + "reply_all": "Allen beantwoorden (a)", + "forward": "Doorsturen (f)", + "archive": "Archiveren", + "delete": "Verwijderen", + "star": "Ster toevoegen (s)", + "unstar": "Ster verwijderen (s)", + "compose": "Opstellen (c)", + "previous": "Vorige e-mail", + "next": "Volgende e-mail", + "edit_draft": "Concept bewerken" + }, + "spam": { + "button_title": "Spam melden", + "not_spam_title": "Markeren als legitiem", + "toast_success": "Verplaatst naar Ongewenst", + "toast_batch": "{count} e-mails verplaatst naar Ongewenst", + "toast_undo": "Ongedaan maken", + "toast_not_spam_success": "Verplaatst naar Postvak IN", + "toast_not_spam_batch": "{count} e-mails verplaatst naar Postvak IN", + "error": "Kan spam niet melden", + "error_not_spam": "Kan e-mail niet herstellen" + }, + "unsubscribe_banner": { + "label": "Nieuwsbrief", + "button": "Uitschrijven", + "confirm_title": "Uitschrijven van deze afzender?", + "confirm_button": "Bevestigen", + "cancel": "Annuleren", + "success_http": "Uitschrijfpagina geopend in nieuw tabblad", + "success_mailto": "Afmeldingsmail verzonden", + "confirm_message_http": "De afmeldpagina wordt in een nieuw tabblad geopend.", + "confirm_message_mailto": "Er wordt een afmeldingsmail naar de afzender gestuurd.", + "error": "Kan niet uitschrijven", + "dismiss": "Sluiten" + }, + "calendar_invitation": { + "loading": "Evenementdetails laden…", + "title": "Agenda-uitnodiging", + "published_title": "Gepubliceerd evenement", + "response_title": "Reactie op evenement", + "update_title": "Evenementupdate", + "counter_title": "Tegenvoorstel", + "refresh_title": "Vernieuwingsverzoek", + "declined_counter_title": "Tegenvoorstel afgewezen", + "cancelled_title": "Evenement geannuleerd", + "organizer": "Georganiseerd door {name}", + "organizer_label": "Georganiseerd door", + "attendees": "{count, plural, one {# deelnemer} other {# deelnemers}}", + "accept": "Accepteren", + "maybe": "Misschien", + "decline": "Weigeren", + "add_to_calendar": "Toevoegen aan agenda", + "added": "Toegevoegd aan agenda", + "rsvp_sent": "Reactie verzonden", + "parse_error": "Kan uitnodiging niet lezen", + "action_failed": "Die agenda-actie kon niet worden voltooid.", + "no_calendar": "Agenda niet beschikbaar", + "published_info": "Dit evenement is gedeeld ter referentie.", + "response_info": "Dit bericht bevat een reactie van een deelnemer.", + "response_info_organizer": "Deze reactie werkt uw evenement bij.", + "update_info": "Dit bericht werkt een bestaand evenement bij.", + "counter_info": "Dit bericht stelt wijzigingen aan een evenement voor.", + "counter_info_organizer": "Deze deelnemer stelde wijzigingen aan uw evenement voor.", + "refresh_info": "Dit bericht vraagt om de nieuwste evenementdetails.", + "refresh_info_organizer": "Een deelnemer vroeg om de nieuwste evenementdetails.", + "declined_counter_info": "De organisator heeft een tegenvoorstel afgewezen.", + "authentication_failed_info": "De mailauthenticatiecontroles voor deze uitnodiging zijn mislukt. Wees voorzichtig met agenda-acties.", + "authentication_missing_info": "Deze uitnodiging bevat geen geverifieerde mailauthenticatie. Controleer de details bij de organisator als iets ongewoon lijkt.", + "sender_mismatch_info": "Deze uitnodiging is verzonden vanaf {sender}, terwijl de organisator in de kalendergegevens {organizer} is.", + "sender_mismatch_unverified_info": "Deze uitnodiging is verzonden vanaf {sender}, terwijl de organisator in de kalendergegevens {organizer} is, en het bericht kon niet worden geverifieerd.", + "organizer_role": "U organiseert dit evenement", + "your_response": "Uw reactie: {status}", + "response_needed": "Reactie vereist", + "response_accepted": "Geaccepteerd", + "response_tentative": "Voorlopig", + "response_declined": "Geweigerd", + "response_delegated": "Gedelegeerd", + "actor_sent_info": "Verzonden door {name}.", + "actor_response_info": "{name} reageerde {status}.", + "actor_counter_info": "{name} stelde wijzigingen voor dit evenement voor.", + "actor_refresh_info": "{name} vroeg om de nieuwste evenementdetails.", + "actor_declined_counter_info": "{name} wees het tegenvoorstel af.", + "actor_note": "Notitie: {comment}", + "actor_unknown": "Iemand", + "proposed_changes": "Voorgestelde wijzigingen", + "change_title": "Titel", + "change_time": "Tijd", + "change_location": "Locatie", + "change_description": "Beschrijving", + "change_empty": "Geen", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Voorgestelde wijzigingen toepassen", + "proposal_applied": "De voorgestelde wijzigingen zijn toegepast.", + "review_proposal": "Voorstel bekijken", + "review_request": "Verzoek bekijken", + "view_in_calendar": "In agenda bekijken", + "select_calendar": "Agenda selecteren", + "already_in_calendar": "Staat al in je agenda", + "request_info": "Je bent uitgenodigd voor dit evenement. Reageer om de organisator te laten weten of je kunt deelnemen.", + "cancel_info": "De organisator heeft dit evenement geannuleerd.", + "event_updated": "Update #{sequence}", + "event_status_tentative": "Voorlopig", + "event_status_cancelled": "Geannuleerd", + "collapse": "Details verbergen", + "expand": "Details tonen" + }, + "previous": "Vorige", + "next": "Volgende", + "send": "Verzenden", + "more": "meer", + "details": { + "recipients_routing": "Ontvangers & routing", + "authentication_security": "Authenticatie & beveiliging", + "identifiers_threading": "Identificatoren & threading", + "mailing_list": "Mailinglijst", + "message_properties": "Berichteigenschappen", + "sent": "Verzonden", + "received": "Ontvangen", + "delivery_time": "Bezorgtijd", + "in_reply_to": "In-Reply-To", + "references": "Verwijzingen", + "thread_id": "Thread-ID", + "size": "Grootte", + "mime_type": "MIME-type", + "attachments_summary": "{count} bestanden · {size}", + "list_id": "Lijst-ID", + "list_help": "Lijsthulp", + "list_post": "Lijstbericht", + "list_unsubscribe": "Afmelden", + "iprev": "Reverse DNS", + "spam_status": "Spamstatus", + "ai_verdict": "AI-oordeel", + "account": "Account", + "no_subject": "(geen onderwerp)" + }, + "scheduled_banner": "Gepland om te verzenden op {date}", + "scheduled_send_created": "E-mail gepland voor verzending", + "sending_in": "Wordt over {seconds} s verzonden", + "undo_send_failed": "Ongedaan maken lukte niet. Het bericht is misschien al verzonden.", + "cancel_scheduled_send": "Verzenden annuleren", + "reschedule_send": "Opnieuw plannen", + "cancel_and_edit": "Annuleren en bewerken", + "cancel_and_compose_again": "Annuleren en opnieuw opstellen", + "reschedule_prompt": "Voer een nieuwe datum/tijd in, bijv. 2026-05-04T15:30", + "scheduled_actions_only": "De geplande weergave ondersteunt alleen geplande acties", + "undo_send_scheduled": "Bericht gepland voor verzending", + "undo_send": "Verzenden ongedaan maken", + "contact_sidebar": { + "title": "Contact", + "close": "Zijbalk sluiten", + "action_email": "E-mail", + "action_email_title": "E-mail verzenden", + "action_copy": "Kopiëren", + "action_copy_title": "E-mailadres kopiëren", + "action_edit_title": "Contact bewerken", + "section_emails": "E-mailadressen", + "section_phones": "Telefoonnummers", + "section_organizations": "Organisaties", + "section_addresses": "Adressen", + "section_notes": "Notities", + "not_in_contacts": "Niet in uw contacten", + "add_to_contacts": "Aan contacten toevoegen", + "copied": "Gekopieerd!", + "copy_failed": "Kopiëren mislukt", + "view_contact": "Contact bekijken", + "view_details": "Details bekijken" + }, + "send_now": "Nu verzenden", + "verification_code": { + "copy": "Code {code} kopiëren", + "copied": "Code gekopieerd", + "copy_failed": "Kan de code niet kopiëren" + }, + "mv_position": "{index} van {total}", + "plugin_panel_show": "Plug-inpaneel tonen", + "plugin_panel_hide": "Plug-inpaneel verbergen" + }, + "email_composer": { + "select_from_directory": "Kiezen uit adresboek van de organisatie", + "minimize": "Minimaliseren", + "maximize": "Volledig scherm", + "restore": "Herstellen", + "new_message": "Nieuw bericht", + "signature_edit_hint": "Dubbelklik om de handtekening te bewerken", + "reply": "Beantwoorden", + "reply_all": "Allen beantwoorden", + "forward": "Doorsturen", + "reply_to": "Beantwoorden", + "reply_all_to": "Allen beantwoorden", + "forward_message": "Doorsturen", + "from": "Van", + "to": "Aan", + "cc": "CC", + "bcc": "BCC", + "subject": "Onderwerp", + "body_placeholder": "Schrijf je bericht...", + "send": "Verzenden", + "cancel": "Annuleren", + "attach": "Bijlage toevoegen", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Bijvoegen uit Bestanden", + "files_picker": { + "attach": "{count, plural, =0 {Bijvoegen} one {1 bestand bijvoegen} other {# bestanden bijvoegen}}", + "empty_folder": "Deze map is leeg", + "load_failed": "Je bestanden konden niet worden geladen" + }, + "discard": "Verwijderen", + "discard_draft_title": "Concept verwijderen?", + "discard_draft_confirm": "Je hebt niet-opgeslagen wijzigingen. Wil je dit concept verwijderen?", + "discard_draft_message": "Het concept wordt verwijderd.", + "saving": "Opslaan...", + "draft_saved": "Concept opgeslagen", + "save_failed": "Opslaan mislukt", + "to_placeholder": "E-mailadressen van ontvangers", + "cc_placeholder": "CC-ontvangers", + "bcc_placeholder": "BCC-ontvangers", + "subject_placeholder": "Onderwerp", + "cc_label": "CC:", + "bcc_label": "BCC:", + "subject_label": "Onderwerp:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} other {# e-mails}}", + "no_subject": "(Geen onderwerp)", + "unknown_sender": "Onbekend", + "quote": { + "reply_header": "Op {date} schreef {sender}:", + "forward_header": "---------- Doorgestuurd bericht ----------", + "from": "Van: {sender}", + "date": "Datum: {date}", + "subject": "Onderwerp: {subject}", + "to": "Aan: {recipients}" + }, + "remove_sub_address": "Sub-adres verwijderen", + "from_override": { + "toggle_off": "Overschrijven", + "toggle_on": "Overschrijven annuleren", + "toggle_tooltip": "Bewerk de naam en het adres van de afzender vrij. E-mail wordt nog steeds via je identiteit verzonden.", + "name_label": "Afzendernaam", + "name_placeholder": "Naam", + "email_label": "E-mailadres afzender", + "email_placeholder": "alias@example.com", + "envelope_notice": "Als je server dit adres niet accepteert als envelopafzender, wordt daar {identity} gebruikt, en ontvangers kunnen dat zien in de Return-Path-header." + }, + "save_as_template": "Opslaan als sjabloon", + "attach_from_device": "Uploaden vanaf dit apparaat", + "templates_menu": "Sjablonen", + "insert_template": "Sjabloon invoegen", + "options": "Opties", + "option_plain_text": "Platte tekst", + "option_plain_text_hint": "Verzenden zonder opmaak", + "option_read_receipt": "Leesbevestiging vragen", + "option_read_receipt_hint": "De ontvanger vragen te bevestigen dat die het opende", + "option_dsn": "Bezorgmelding", + "option_dsn_hint": "Een rapport krijgen zodra de server het bezorgt", + "option_require_tls": "Versleutelde bezorging vereisen", + "option_require_tls_hint": "Alleen via TLS bezorgen, anders terugsturen", + "option_priority": "Prioriteit", + "option_priority_high": "Hoog", + "option_priority_normal": "Normaal", + "option_priority_low": "Laag", + "validation": { + "recipient_required": "Voeg een ontvanger toe om te verzenden", + "body_required": "Schrijf een bericht of voeg een bestand toe", + "invalid_recipients": "Niet verzonden - controleer deze adressen: {addresses}", + "attachments_uploading": "Bijlagen worden nog geüpload — wordt verzonden zodra dit klaar is", + "attachment_upload_failed": "Niet verzonden - een bijlage kon niet worden geüpload. Verwijder deze en probeer het opnieuw." + }, + "upload_progress": "Uploaden {uploaded} / {total}", + "upload_cancel": "Upload annuleren", + "upload_failed": "Uploaden van {filename} mislukt", + "attachment_too_large": "\"{name}\" is groter dan de server toestaat ({max} per bestand)", + "attachments_total_too_large": "De bijlagen zouden de serverlimiet van {max} per bericht overschrijden", + "send_failed": "E-mail verzenden mislukt", + "smime_sign_on": "S/MIME-ondertekening ingeschakeld", + "smime_sign_off": "S/MIME-ondertekening uitgeschakeld", + "smime_encrypt_on": "S/MIME-versleuteling ingeschakeld", + "smime_encrypt_off": "S/MIME-versleuteling uitgeschakeld", + "smime_encrypt_unavailable": "S/MIME-versleuteling niet beschikbaar – ontbrekende ontvangercertificaten", + "smime_unlock_title": "S/MIME-sleutel ontgrendelen", + "smime_unlock_message": "Voer de wachtwoordzin in om uw S/MIME-sleutel te ontgrendelen.", + "smime_unlock_button": "Ontgrendelen", + "smime_passphrase_placeholder": "S/MIME-wachtwoordzin", + "send_timeout": "De server reageert niet meer. Controleer de map Verzonden voordat u opnieuw verstuurt — het bericht is mogelijk al verzonden.", + "send_offline": "Je lijkt offline te zijn. Het bericht staat er nog; verstuur het opnieuw zodra je weer online bent.", + "formatting": "Opmaak", + "send_recipients_rejected": "Niet verzonden - de server heeft alle ontvangers geweigerd.", + "send_plugin_failed": "Niet verzonden - een plug-in die het verzenden afhandelt, is mislukt of duurde te lang. Controleer je map Verzonden voordat je opnieuw verzendt.", + "send_some_recipients_rejected": "Verzonden, maar niet naar deze ontvangers - de server heeft ze geweigerd.", + "continue_draft": "Concept voortzetten", + "close_draft_title": "Concept opslaan of verwijderen?", + "close_draft_message": "U heeft niet-opgeslagen wijzigingen. Wilt u dit als concept opslaan of verwijderen?", + "drop_files": "Sleep bestanden om bij te voegen", + "show_less": "Minder tonen", + "forgot_attachment": { + "title": "Bent u een bijlage vergeten?", + "message": "Uw bericht vermeldt \"{keyword}\" maar er is geen bestand bijgevoegd. Toch verzenden?", + "send_anyway": "Toch verzenden", + "back": "Terug naar bewerken" + }, + "empty_subject": { + "title": "Verzenden zonder onderwerp?", + "message": "Dit bericht heeft geen onderwerp. Toch verzenden?", + "dont_ask_again": "Niet meer vragen", + "send_anyway": "Toch verzenden", + "back": "Terug naar bewerken" + }, + "add_link": "Link toevoegen", + "link_url_prompt": "Voer de URL in", + "sending": "Bezig met verzenden...", + "schedule_send": "Verzenden plannen", + "schedule_send_description": "Kies wanneer de server dit bericht moet vrijgeven.", + "schedule_preset": { + "tomorrow_morning": "Morgenochtend", + "tomorrow_afternoon": "Morgenmiddag", + "monday_morning": "Maandagochtend" + }, + "schedule_send_required": "Kies een datum en tijd.", + "schedule_send_invalid": "Voer een geldige datum en tijd in.", + "schedule_send_future": "Kies een toekomstige datum en tijd.", + "schedule_send_too_late": "Dit tijdstip is later dan de server toestaat.", + "schedule_send_unsupported": "Gepland verzenden wordt niet ondersteund voor dit account.", + "schedule_send_cleanup_warning": "Gepland verzenden is aangemaakt, maar het opschonen van de conceptversie is mislukt.", + "send_delay_unsupported": "Verzendvertraging wordt niet ondersteund voor dit account.", + "send_delay_unsupported_confirm": "Dit account ondersteunt geen verzendvertraging. Meteen verzenden?", + "recipient_edit_email": "E-mailadres bewerken", + "recipient_edit_name": "Weergavenaam bewerken", + "recipient_email_placeholder": "E-mailadres", + "recipient_name_placeholder": "Weergavenaam", + "invalid_recipient": "Geen geldig e-mailadres", + "autocomplete_search_server": "Op de server zoeken", + "autocomplete_searching": "Bezig met zoeken...", + "mention_recipients": "Ontvangers", + "mention_card_not_recipient": "Staat niet in Aan of CC en krijgt dit bericht dus niet.", + "mention_card_add": "Toevoegen aan {field}", + "mention_card_remove": "Vermelding verwijderen", + "toolbar": { + "bold": "Vet", + "italic": "Cursief", + "underline": "Onderstrepen", + "strikethrough": "Doorhalen", + "text_color": "Tekstkleur", + "remove_color": "Kleur verwijderen", + "heading_1": "Kop 1", + "heading_2": "Kop 2", + "bullet_list": "Opsommingslijst", + "ordered_list": "Genummerde lijst", + "quote": "Citaat", + "code_block": "Codeblok", + "align_left": "Links uitlijnen", + "align_center": "Centreren", + "align_right": "Rechts uitlijnen", + "text_direction": "Tekstrichting (RTL/LTR)", + "link": "Link", + "table": "Tabel", + "clear_formatting": "Opmaak wissen", + "undo": "Ongedaan maken", + "redo": "Opnieuw", + "add_row_above": "Rij erboven toevoegen", + "add_row_below": "Rij eronder toevoegen", + "add_column_before": "Kolom ervoor toevoegen", + "add_column_after": "Kolom erna toevoegen", + "delete_row": "Rij verwijderen", + "delete_column": "Kolom verwijderen", + "toggle_header_row": "Koprij aan/uit", + "delete_table": "Tabel verwijderen", + "pick_size": "Grootte kiezen", + "font_size": "Lettergrootte", + "font_size_default": "Standaard", + "background_color": "Achtergrondkleur", + "remove_background_color": "Achtergrondkleur verwijderen" + }, + "send_filing_warning": "Verzonden - maar het opruimen daarna is mislukt, mogelijk blijft een oud concept staan.", + "pop_out": "Antwoord in een apart venster openen", + "send_guard_failed": "Niet verzonden - een plug-in die berichten controleert voor het verzenden is mislukt of reageerde niet." + }, + "confirm_dialog": { + "confirm": "Bevestigen", + "cancel": "Annuleren" + }, + "common": { + "loading": "Laden...", + "error": "Fout", + "success": "Geslaagd", + "cancel": "Annuleren", + "save": "Opslaan", + "delete": "Verwijderen", + "edit": "Bewerken", + "close": "Sluiten", + "search": "Zoeken", + "refresh": "Vernieuwen", + "settings": "Instellingen", + "help": "Help", + "logout": "Uitloggen", + "yes": "Ja", + "no": "Nee", + "unknown": "Onbekend", + "app_title": "Webmail", + "reconnecting": "Verbinding verloren. Opnieuw verbinden…", + "rate_limited_title": "Serverauthenticatie is tijdelijk beperkt.", + "rate_limited_detail": "Bulwark heeft achtergrondverzoeken gepauzeerd om een blokkade te voorkomen. Nieuwe poging over {seconds}s.", + "rate_limited_action_title": "Verzoek gepauzeerd om blokkade te voorkomen.", + "rate_limited_action_detail": "Bulwark wacht tot de servercooldown voorbij is voordat er nieuwe geauthenticeerde verzoeken worden verzonden. Probeer het over {seconds}s opnieuw.", + "pull_to_refresh": "Trek om te vernieuwen", + "release_to_refresh": "Laat los om te vernieuwen", + "refreshing": "Vernieuwen…" + }, + "notifications": { + "email_sent": "E-mail succesvol verzonden", + "email_deleted": "E-mail verwijderd", + "email_archived": "E-mail gearchiveerd", + "emails_deleted": "{count, plural, one {# e-mail verwijderd} other {# e-mails verwijderd}}", + "emails_archived": "{count, plural, one {# e-mail gearchiveerd} other {# e-mails gearchiveerd}}", + "error_archiving": "E-mail kon niet worden gearchiveerd", + "error_updating": "E-mail kon niet worden bijgewerkt", + "email_starred": "Ster toegevoegd aan e-mail", + "email_unstarred": "Ster verwijderd van e-mail", + "email_marked_read": "E-mail gemarkeerd als gelezen", + "email_marked_unread": "E-mail gemarkeerd als ongelezen", + "copied_to_clipboard": "Gekopieerd naar klembord", + "source_copied": "Bron gekopieerd naar klembord", + "error_sending": "Kan e-mail niet verzenden", + "error_deleting": "Kan e-mail niet verwijderen", + "error_loading": "Kan e-mails niet laden", + "new_email": "Nieuwe e-mail", + "new_email_from": "Van {sender}", + "click_to_view": "Klik om te bekijken", + "email_moved": "E-mail verplaatst", + "emails_moved": "{count} e-mails verplaatst", + "moved_to_mailbox": "Verplaatst naar {mailbox}", + "move_failed": "Verplaatsen mislukt", + "removed_from_folder": "Verwijderd uit {mailbox}", + "remove_from_folder_failed": "Kon niet uit deze map verwijderen", + "emails_copied": "{count} e-mails gekopieerd", + "copy_failed": "Kopiëren mislukt", + "move_error": "Kan e-mails niet verplaatsen naar de geselecteerde map", + "email_tagged": "E-mail gelabeld", + "emails_tagged": "{count} e-mails gelabeld", + "tag_failed": "Labelen mislukt", + "identity_created": "Identiteit succesvol aangemaakt", + "identity_updated": "Identiteit succesvol bijgewerkt", + "identity_deleted": "Identiteit verwijderd", + "identity_set_primary": "Primaire identiteit bijgewerkt", + "identity_create_failed": "Kan identiteit niet aanmaken: {error}", + "identity_update_failed": "Kan identiteit niet bijwerken: {error}", + "identity_delete_failed": "Kan identiteit niet verwijderen: {error}", + "identity_unauthorized": "Je bent niet geautoriseerd om vanaf dit e-mailadres te verzenden", + "identity_not_found": "Identiteit niet gevonden", + "vacation_saved": "Afwezigheidsinstellingen opgeslagen", + "vacation_save_failed": "Kan afwezigheidsinstellingen niet opslaan", + "filters_saved": "Filters succesvol opgeslagen", + "filters_save_failed": "Filters opslaan mislukt", + "filters_deleted": "Filterregel verwijderd", + "templates_exported": "Sjablonen succesvol geëxporteerd", + "templates_imported": "{count, plural, one {# sjabloon geïmporteerd} other {# sjablonen geïmporteerd}}", + "templates_import_errors": "Sommige sjablonen konden niet worden geïmporteerd", + "templates_import_empty": "Geen sjablonen gevonden in het bestand", + "export_email_error": "Kan e-mail niet exporteren", + "import_email_success": "E-mail succesvol geïmporteerd", + "import_email_error": "Kan e-mail niet importeren", + "rule_created": "Regel gemaakt", + "rule_merged": "Toegevoegd aan regel ‘{name}’", + "rule_already_covered": "Valt al onder regel ‘{name}’", + "rule_apply_existing": "{count, plural, one {Toepassen op # bestaand bericht} other {Toepassen op # bestaande berichten}}", + "rule_applied": "{count, plural, =0 {Geen berichten hoefden te worden gewijzigd} one {Regel toegepast op # bericht} other {Regel toegepast op # berichten}}", + "rule_apply_failed": "De regel kon niet op bestaande berichten worden toegepast", + "rule_edit": "Regel bewerken", + "rule_undo": "Ongedaan maken", + "rule_undone": "Regelwijziging ongedaan gemaakt", + "rule_undo_failed": "De regelwijziging kon niet ongedaan worden gemaakt", + "rule_undo_conflict": "Je filters zijn intussen gewijzigd. Maak de wijziging ongedaan in de filterinstellingen.", + "rule_folder_failed": "De map kon niet worden gemaakt", + "vacation_filters_save_failed": "Afwezigheidsinstellingen opgeslagen, maar het doorsturen en de ontvangers van het antwoord konden niet worden opgeslagen" + }, + "date": { + "today": "Vandaag", + "yesterday": "Gisteren", + "this_week": "Deze week", + "last_week": "Vorige week", + "this_month": "Deze maand", + "older": "Ouder", + "just_now": "Zojuist", + "minutes_ago": "{count} minuut geleden", + "minutes_ago_plural": "{count} minuten geleden", + "hours_ago": "{count} uur geleden", + "hours_ago_plural": "{count} uur geleden", + "days_ago": "{count} dag geleden", + "days_ago_plural": "{count} dagen geleden" + }, + "language": { + "title": "Taal", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Selecteer taal", + "switch_to_english": "Overschakelen naar Engels", + "switch_to_french": "Overschakelen naar Frans", + "switch_to_japanese": "Overschakelen naar Japans", + "switch_to_spanish": "Overschakelen naar Spaans", + "switch_to_italian": "Overschakelen naar Italiaans", + "switch_to_german": "Overschakelen naar Duits", + "switch_to_dutch": "Overschakelen naar Nederlands", + "switch_to_portuguese": "Overschakelen naar Portugees", + "switch_to_russian": "Overschakelen naar Russisch", + "switching": "Taal wijzigen...", + "switch": "Taal wijzigen", + "current": "Huidige taal", + "polish": "Polski", + "switch_to_polish": "Overschakelen naar Pools", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Overschakelen naar Farsi", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Overschakelen naar Mongools" + }, + "settings": { + "title": "Instellingen", + "back_to_mail": "Terug naar Mail", + "save_success": "Instellingen succesvol opgeslagen", + "import_success": "Instellingen succesvol geïmporteerd", + "import_error": "Kan instellingen niet importeren", + "reset_confirm": "Weet je zeker dat je alle instellingen wilt resetten naar de standaardwaarden?", + "unsaved_changes": "U heeft niet-opgeslagen wijzigingen", + "discard_changes": "Wijzigingen verwerpen?", + "discard": "Verwerpen", + "keep_editing": "Doorgaan met bewerken", + "tabs": { + "appearance": "Uiterlijk", + "language": "Taal, Regio & Tijd", + "email": "E-mailgedrag", + "composer": "Opstellen", + "privacy": "Privacy & Beveiliging", + "account": "Account", + "identities": "Identiteiten", + "vacation": "Afwezigheid", + "advanced": "Geavanceerd", + "calendar": "Agenda", + "filters": "Filters", + "templates": "Sjablonen", + "folders": "Mappen", + "keywords": "Labels", + "security": "Beveiliging", + "encryption": "Versleuteling", + "files": "Bestanden", + "contacts": "Contacten", + "protocol_handlers": "Standaardapps", + "sidebar_apps": "Zijbalk-apps", + "notifications": "Meldingen", + "layout": "Indeling", + "themes": "Thema's", + "reading": "Lezen", + "composing": "Opstellen", + "downloads": "Downloads", + "content_senders": "Inhoud en afzenders", + "about_data": "Over en gegevens", + "debug": "Debuggen" + }, + "themes": { + "title": "Thema's", + "description": "Kies uit thema's die je beheerder heeft uitgerold en ingebouwde voorinstellingen.", + "locked": "Themakeuze is vergrendeld door een beheerder.", + "forced": "Thema “{name}” is afgedwongen door de beheerder en kan niet worden gewijzigd", + "admin_theme": "Beheerdersthema", + "activated": "Thema geactiveerd", + "default_restored": "Standaardthema hersteld", + "default_name": "Standaard", + "default_badge": "Standaard", + "admin_enforced": "Afgedwongen door beheerder" + }, + "tab_groups": { + "general": "Algemeen", + "account": "Account & identiteit", + "organization": "E-mailorganisatie", + "apps": "Apps", + "system": "Systeem", + "appearance": "Weergave", + "mail": "E-mail", + "privacy": "Privacy en beveiliging", + "advanced": "Geavanceerd" + }, + "appearance": { + "message_list_order": { + "label": "Volgorde van de berichtenlijst", + "description": "Hoe berichten in de lijst worden gesorteerd. De server sorteert, dus het geldt voor de hele map en niet alleen voor de geladen berichten.", + "preset": { + "chronological": "Chronologisch (nieuwste eerst)", + "unread_first": "Ongelezen eerst", + "starred_first": "Met ster eerst", + "tagged_first": "Met label eerst", + "custom": "Aangepast" + }, + "tag_label": "Label", + "advanced_show": "Geavanceerd: sorteerniveaus bewerken", + "advanced_hide": "Geavanceerd verbergen", + "criterion_label": "Criterium van niveau {n}", + "direction_label": "Richting van niveau {n}", + "criteria": { + "unread": "Ongelezen", + "starred": "Met ster", + "tag": "Label", + "receivedAt": "Ontvangstdatum", + "sentAt": "Verzenddatum", + "from": "Afzender", + "subject": "Onderwerp", + "size": "Grootte" + }, + "direction": { + "unread_first": "Ongelezen eerst", + "read_first": "Gelezen eerst", + "starred_first": "Met ster eerst", + "unstarred_first": "Zonder ster eerst", + "tagged_first": "Met label eerst", + "untagged_first": "Zonder label eerst", + "newest_first": "Nieuwste eerst", + "oldest_first": "Oudste eerst", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Grootste eerst", + "smallest_first": "Kleinste eerst" + }, + "add_level": "Niveau toevoegen", + "remove_level": "Niveau verwijderen", + "move_up": "Omhoog", + "move_down": "Omlaag", + "tiebreak_note": "Maximaal drie niveaus; latere niveaus beslissen bij gelijke stand. 'Nieuwste eerst' wordt altijd als laatste toegepast.", + "scope": { + "label": "Op alle mappen toepassen", + "description": "Uitgeschakeld geldt de volgorde alleen voor het Postvak IN; andere mappen blijven chronologisch." + }, + "unsupported_note": "Je mailserver ondersteunt geen sortering op leesstatus, ster of label. Die niveaus worden genegeerd en de lijst blijft chronologisch." + }, + "title": "Uiterlijk", + "description": "Pas het uiterlijk van je webmail aan", + "theme": { + "label": "Thema", + "description": "Kies je voorkeurkleurenschema", + "light": "Licht", + "dark": "Donker", + "system": "Systeem" + }, + "language": { + "label": "Taal", + "description": "Kies je voorkeurstaal" + }, + "font_size": { + "label": "Lettergrootte", + "description": "Pas de tekstgrootte aan voor betere leesbaarheid", + "small": "Klein", + "medium": "Middel", + "large": "Groot" + }, + "list_density": { + "label": "Dichtheid", + "description": "Regeleer de ruimte in de gehele interface", + "extra_compact": "Extra compact", + "compact": "Compact", + "regular": "Normaal", + "comfortable": "Comfortabel" + }, + "interface_layout": { + "label": "Interface-indeling", + "description": "Waar zoeken, navigatie en werkbalken staan", + "default": "Standaard", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Animaties inschakelen", + "description": "Vloeiende overgangen en effecten tonen" + }, + "toolbar_position": { + "label": "Werkbalkpositie", + "description": "Waar de e-mailactieknoppen worden weergegeven (Beantwoorden, Archiveren, Verwijderen, enz.)", + "top": "Bovenaan", + "below_subject": "Onder het onderwerp" + }, + "toolbar_labels": { + "label": "Werkbalklabels tonen", + "description": "Tekstlabels naast werkbalkpictogrammen weergeven. Uitschakelen om ruimte te besparen als u de pictogrammen kent." + }, + "hide_account_switcher": { + "label": "Accountwisselaar in zijbalk verbergen", + "description": "Verberg de accountselector bovenaan de mappenzijbalk. U kunt nog steeds van account wisselen via de onderste navigatiebalk." + }, + "show_rail_account_list": { + "label": "Accountavatars tonen op navigatiebalk", + "description": "Toon individuele accountcirkels onderaan de navigatiebalk voor snel wisselen, met een afmeldknop eronder." + }, + "unified_mailbox": { + "label": "Gecombineerd postvak", + "description": "Gecombineerde mappen (Postvak IN, Verzonden, enz.) van alle verbonden accounts weergeven", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Groepspostvakken meenemen", + "description": "Gedeelde/groepspostvakken ook in de gecombineerde weergave opnemen." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Gekleurde zijbalkpictogrammen", + "description": "Kleur map- en tagpictogrammen op type (blauw Postvak IN, rood Spam, groen Verzonden, enz.). Schakel uit voor een monochrome zijbalk." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Totaal aantal berichten tonen", + "description": "Toont het totale aantal berichten naast mappen en labels, naast het aantal ongelezen berichten. Schakel uit om alleen ongelezen aantallen te tonen." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Gedeelde mailaccounts tonen", + "description": "Toon de mappen van postvakken die met je zijn gedeeld onder Gedeeld in de zijbalk. Zet uit om ze allemaal te verbergen, of verberg hieronder losse accounts. Hun agenda's en contacten blijven zichtbaar." + }, + "pro_interface": { + "label": "Pro-interface (experimenteel)", + "description": "Power user-indeling alleen voor desktop met meertabs berichtweergave en accountoverschrijdende workflows. De standaardinterface blijft onveranderd; u kunt op elk moment terugkeren.", + "open_label": "Pro-interface openen", + "back_to_standard": "Terug naar standaard" + }, + "cross_unread": { + "label": "Alle accounts: Ongelezen", + "description": "Toont een item in de sectie Alle accounts met ongelezen e-mail van alle accounts, in alle mappen behalve ongewenst, verzonden, archief, prullenbak en concepten." + }, + "cross_starred": { + "label": "Alle accounts: Met ster", + "description": "Toont een item in de sectie Alle accounts met e-mail met ster van alle accounts, in alle mappen behalve ongewenst, verzonden, archief, prullenbak en concepten." + }, + "cross_all": { + "label": "Alle accounts: Alle e-mail", + "description": "Toont een item in de sectie Alle accounts met alle e-mail van alle accounts, in alle mappen behalve ongewenst, verzonden, archief, prullenbak en concepten." + } + }, + "keywords": { + "title": "E-maillabels", + "description": "Definieer labels om uw e-mails te organiseren. Deze worden opgeslagen als JMAP-trefwoorden op de server.", + "add_keyword": "Label toevoegen", + "label_field": "Weergavenaam", + "label_placeholder": "bijv. Werk, Persoonlijk, Urgent", + "id_field": "Label-ID", + "id_placeholder": "bijv. werk, persoonlijk", + "color_field": "Kleur", + "id_exists": "Deze label-ID bestaat al", + "edit": "Label bewerken", + "delete": "Label verwijderen", + "save": "Opslaan", + "add": "Toevoegen", + "cancel": "Annuleren", + "migrating": "Label bijwerken op bestaande e-mails…", + "migration_error": "Label bijwerken op bestaande e-mails mislukt", + "filters_migration_error": "Sommige filterregels gebruiken nog het oude label — werk ze handmatig bij", + "nesting": { + "label": "Geneste labels", + "description": "Nest labels onder andere labels en toon ze als een boomstructuur in de zijbalk." + }, + "parent_field": "Bovenliggend label", + "no_parent": "Geen bovenliggend label", + "too_long": "Dit labelpad is te lang (maximaal {max} tekens)", + "has_children_locked": "Er vallen andere labels onder dit label, dus de naam en het bovenliggende label liggen vast. Verplaats of verwijder ze eerst.", + "has_children_delete": "Verwijder eerst de labels die hieronder vallen", + "visibility_field": "Zichtbaarheid in de zijbalk", + "visibility": { + "show": "Tonen", + "unread": "Tonen bij ongelezen", + "hide": "Verbergen" + }, + "unrecognized": { + "title": "Labels van de server herstellen", + "description": "Labelnamen en -kleuren worden in deze browser bewaard, maar de labels zelf staan als JMAP-trefwoorden op uw e-mails. Doorzoek uw e-mail op trefwoorden waarvoor hier geen label bestaat en voeg ze weer toe.", + "scan": "Zoeken naar labels", + "rescan": "Opnieuw zoeken", + "scanning": "Bezig met zoeken… {scanned} van {total} e-mails", + "none": "Alle labels op uw e-mails zijn hier al gedefinieerd.", + "messages": "{count, plural, one {# e-mail} other {# e-mails}}", + "add_all": "Alle {count} toevoegen", + "partial": "Alleen de {scanned} meest recente van {total} e-mails zijn doorzocht. Labels die alleen op oudere e-mails staan, ontbreken mogelijk.", + "error": "Kan uw e-mail niet doorzoeken op labels." + } + }, + "notifications": { + "test_sound": "Meldingsgeluid testen", + "sounds": { + "default": "Standaard (Pieptoon)", + "cheerful": "Vrolijk", + "involved": "Uitgebreid", + "swift": "Snel gebaar", + "relax": "Ontspannen" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Meldingsgeluid", + "description": "Kies welk geluid wordt afgespeeld voor meldingen", + "choose": "Geluid", + "choose_desc": "Selecteer een meldingstoon en klik op het luidsprekerpictogram voor een voorbeeld" + }, + "email": { + "title": "E-mailmeldingen", + "description": "Meldingen voor inkomende e-mails configureren", + "enabled": "E-mailmeldingen", + "enabled_desc": "Meldingen tonen wanneer nieuwe e-mails binnenkomen", + "sound": "Meldingsgeluid", + "sound_desc": "Een geluid afspelen wanneer nieuwe e-mails binnenkomen", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Agendameldingen", + "description": "Meldingen voor agenda-evenementen configureren", + "enabled": "Evenementmeldingen", + "enabled_desc": "Waarschuwingen tonen voor aankomende agenda-evenementen", + "sound": "Meldingsgeluid", + "sound_desc": "Een geluid afspelen voor agendaherinneringen", + "invitation_parsing": "E-mailuitnodigingen herkennen", + "invitation_parsing_desc": "Agenda-uitnodigingen in e-mailbijlagen detecteren en agendaacties tonen" + }, + "push": { + "confirm_disable_message": "Dit apparaat ontvangt geen meldingen meer als de site is gesloten.", + "confirm_disable_title": "Achtergrondmeldingen uitschakelen?", + "description": "Ontvang systeemmeldingen voor nieuwe e-mail wanneer deze site gesloten is. Geleverd via de Bulwark-pushrelay; de relay ziet nooit e-mailinhoud.", + "disable": "Uitschakelen", + "enable": "Inschakelen", + "dismiss_aria": "Meldingsvenster sluiten", + "ios_hint": "Installeer de site op iOS eerst op het beginscherm – Safari levert Web Push alleen aan geïnstalleerde PWA's.", + "reenable": "Opnieuw registreren", + "relay_desc": "Gebruikt standaard de gehoste Bulwark-relay. Wijzig alleen als je zelf hostt.", + "relay_label": "Push-relay", + "relay_locked": "Ingesteld door beheerder", + "relay_locked_desc": "De push-relay-URL is ingesteld door je beheerder en kan niet worden gewijzigd.", + "status_active": "Actief op dit apparaat", + "status_busy": "Bezig…", + "status_inactive": "Niet ingeschakeld op dit apparaat", + "status_unsupported": "Deze browser ondersteunt geen Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Achtergrondmeldingen", + "devices_title": "Geregistreerde apparaten", + "devices_desc": "Alle apparaten die achtergrondmeldingen voor dit account ontvangen. Trek je er een in, dan stopt de ontvangst meteen.", + "devices_loading": "Geregistreerde apparaten laden…", + "devices_empty": "Er zijn geen apparaten geregistreerd voor achtergrondmeldingen.", + "devices_error": "Kan de geregistreerde apparaten niet laden.", + "devices_refresh": "Vernieuwen", + "device_this": "Dit apparaat", + "device_other": "Ander apparaat", + "device_status_active": "Levert meldingen af", + "device_status_inactive": "Heeft nooit een melding afgeleverd", + "device_status_unknown": "Niet bekend bij deze relay", + "device_expires": "Verloopt op {date}", + "revoke": "Intrekken", + "confirm_revoke_title": "Dit apparaat intrekken?", + "confirm_revoke_message": "Het ontvangt meteen geen achtergrondmeldingen meer. Push opnieuw inschakelen op dat apparaat registreert het opnieuw.", + "confirm_revoke_message_this": "Dit apparaat ontvangt meteen geen achtergrondmeldingen meer." + } + }, + "language_region": { + "title": "Taal, Regio & Tijd", + "description": "Taal, datumnotatie, tijdnotatie en andere regionale voorkeuren", + "language": { + "label": "Taal", + "description": "Kies je voorkeurstaal", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Datumnotatie", + "description": "Hoe datums worden weergegeven in de e-maillijst", + "smart": "Slim", + "relative": "Relatief", + "full": "Altijd volledige datum", + "preview_today": "Vandaag:", + "preview_this_week": "Deze week:", + "preview_older": "Ouder:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Tijdnotatie", + "description": "Kies tussen 12-uurs of 24-uurs klok", + "12h": "12-uurs", + "24h": "24-uurs" + }, + "first_day": { + "label": "Eerste dag van de week", + "description": "Begin de week op zondag of maandag", + "sunday": "Zondag", + "monday": "Maandag" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Zoekopdracht wissen bij wisselen van map", + "description": "Op een map klikken (Postvak IN, Verzonden…) wist de actieve zoekopdracht en toont die map. Uitgeschakeld blijft de zoekopdracht staan en wordt opnieuw uitgevoerd in de gekozen map." + }, + "request_read_receipt": { + "label": "Standaard om leesbevestiging vragen", + "description": "De aanvraag voor een leesbevestiging vooraf inschakelen bij het opstellen van een nieuw bericht." + }, + "read_receipt_response": { + "label": "Reageren op verzoeken om leesbevestiging", + "description": "Wat te doen wanneer een inkomend bericht om een leesbevestiging vraagt.", + "ask": "Elke keer vragen", + "always": "Altijd verzenden", + "never": "Nooit verzenden" + }, + "title": "E-mailgedrag", + "description": "Configureer hoe e-mails worden verwerkt", + "mark_read": { + "label": "Markeren als gelezen", + "description": "Wanneer e-mails als gelezen worden gemarkeerd bij openen", + "instant": "Direct", + "delay_3s": "Na 3 seconden", + "delay_5s": "Na 5 seconden", + "never": "Nooit" + }, + "delete_action": { + "label": "Verwijderactie", + "description": "Wat er gebeurt wanneer je een e-mail verwijdert", + "trash": "Verplaatsen naar prullenbak", + "trash_and_read": "Verplaatsen naar prullenbak en als gelezen markeren", + "permanent": "Permanent verwijderen", + "warning": "E-mails worden permanent verwijderd en kunnen niet worden hersteld. Deze actie is onomkeerbaar." + }, + "message_spacing": { + "label": "Berichtmarge", + "description": "Ruimte rond de berichtinhoud in de leesweergave", + "auto": "Automatisch (aanbevolen)", + "always": "Altijd ruimte toevoegen", + "edge": "Rand tot rand" + }, + "plain_text_font": { + "label": "Lettertype voor platte tekst", + "description": "Lettertype voor e-mails in platte tekst", + "mono": "Monospace", + "sans": "Normaal (zoals de app)" + }, + "archive_mode": { + "label": "Archiveren in", + "description": "Hoe e-mails bij archivering worden georganiseerd", + "single": "Eén enkele map", + "year": "Een map per jaar", + "month": "Een map per maand", + "reorganize": "Bestaand archief herorganiseren", + "reorganize_success": "{count, plural, =0 {Geen e-mails om te herorganiseren} =1 {1 e-mail geherorganiseerd} other {# e-mails geherorganiseerd}}", + "reorganize_error": "Archief herorganiseren mislukt" + }, + "permanently_delete_junk": { + "label": "Spam permanent verwijderen", + "description": "E-mails uit de map Spam permanent verwijderen in plaats van naar de prullenbak te verplaatsen" + }, + "confirm_bulk_delete": { + "label": "Bevestigen voor het verwijderen van meerdere berichten", + "description": "Vragen voordat meerdere geselecteerde berichten naar de prullenbak gaan. Definitief verwijderen vraagt altijd." + }, + "mail_layout": { + "label": "E-mailindeling", + "description": "Kies tussen het klassieke gesplitste leesvenster en een gefocuste leesstroom in Gmail-stijl.", + "split": "Gesplitst venster", + "split_description": "Houd de berichtenlijst en het leesvenster naast elkaar zichtbaar.", + "focus": "Gefocuste lijst", + "focus_description": "Toon één regel per bericht en open e-mail op volledige breedte terwijl de mappenzijbalk zichtbaar blijft.", + "horizontal": "Leesvenster onderaan", + "horizontal_description": "Toon de berichtenlijst bovenaan en open het geselecteerde bericht in een leesvenster eronder." + }, + "list_columns": { + "label": "Lijst in kolommen weergeven", + "description": "Toont rijen van één regel als tabel met de kolommen Van, Onderwerp, Datum en Grootte onder een kop." + }, + "disable_threading": { + "label": "Conversatiegroepering uitschakelen", + "description": "Toon e-mails als afzonderlijke berichten in plaats van gegroepeerd op gesprek" + }, + "plain_text_mode": { + "label": "Alleen platte tekst", + "description": "Schakel de rich text-editor uit en verzend alle e-mails alleen als platte tekst, inclusief antwoorden en doorgestuurde berichten" + }, + "auto_select_reply_identity": { + "label": "Antwoordadres automatisch selecteren", + "description": "Schakel bij het beantwoorden automatisch het Van-adres om naar de identiteit die het oorspronkelijke bericht ontving" + }, + "reply_identity_match": { + "label": "Overeenkomst van ontvangstadres", + "description": "Welke ontvangstadressen als de uwe gelden. „Alleen exact adres“ kiest uitsluitend een van uw ingestelde identiteiten; „zelfde domein“ behandelt ook elk ander adres op uw identiteitsdomeinen als catch-all-alias en herschrijft de Van-header ernaar. Kies exact adres als die adressen distributielijsten zijn.", + "exact": "Alleen exact adres", + "domain": "Elk adres op mijn domeinen" + }, + "signature_position": { + "label": "Positie van handtekening", + "description": "Waar je handtekening in antwoorden en doorgestuurde berichten moet worden ingevoegd. Boven de geciteerde tekst leest natuurlijk als afsluiting van het antwoord; eronder houdt het originele bericht aaneengesloten.", + "above_quote": "Voor geciteerde tekst", + "below_quote": "Na geciteerde tekst" + }, + "signature_separator": { + "label": "Handtekening-scheider", + "description": "De handtekening voorafgaan met de standaard scheidingsregel \"-- \" (RFC 3676). Zet uit als je liever direct van het bericht in de handtekening overgaat." + }, + "sub_address_delimiter": { + "label": "Sub-adres scheidingsteken", + "description": "Teken dat je gebruikersnaam scheidt van het sub-adres-label. Gebruik het scheidingsteken dat je mailserver gebruikt (bv. gebruiker{delimiter}tag@domein.nl).", + "option": "{delimiter} (gebruiker{delimiter}tag@domein.nl)", + "custom": "Aangepast…", + "custom_input_label": "Aangepast scheidingsteken" + }, + "show_preview": { + "label": "Voorbeeldtekst tonen", + "description": "E-mailvoorbeeld weergeven in de lijst", + "focus_description": "Inline voorbeeldtekst weergeven in de gefocuste berichtenlijst met één regel" + }, + "show_verification_codes": { + "label": "Verificatiecodes tonen", + "description": "De code uit aanmeld- en bevestigingsmails aanbieden om met één klik te kopiëren, een dag lang in de lijst en in het bericht" + }, + "attachment_click_action": { + "label": "Actie bij klikken op bijlagen", + "description": "Kies of een klik op een bijlage een voorbeeld opent of direct downloadt", + "preview": "Voorbeeld tonen indien mogelijk", + "download": "Direct downloaden" + }, + "attachment_position": { + "label": "Positie van bijlagen", + "description": "Waar bijlagen in de e-mailkop worden weergegeven", + "beside-sender": "Naast de afzender", + "below-header": "Onder de kop" + }, + "emails_per_page": { + "10": "10 e-mails", + "25": "25 e-mails", + "50": "50 e-mails", + "100": "100 e-mails", + "label": "E-mails per pagina", + "description": "Aantal e-mails dat in één keer wordt geladen" + }, + "always_light_mode": { + "label": "E-mails altijd in lichte modus weergeven", + "description": "E-mailinhoud in lichte modus weergeven, zelfs wanneer de app in donkere modus staat, om problemen met donkere modus-conversie te voorkomen" + }, + "external_content": { + "label": "Externe inhoud", + "description": "Hoe afbeeldingen en externe inhoud moeten worden verwerkt", + "ask": "Altijd vragen", + "block": "Altijd blokkeren", + "allow": "Altijd toestaan" + }, + "trusted_senders": { + "label": "Vertrouwde afzenders", + "description": "Beheer afzenders van wie afbeeldingen automatisch worden geladen", + "count_zero": "Geen", + "count_one": "1 afzender", + "count_other": "{count} afzenders", + "modal_title": "Vertrouwde afzenders", + "empty_title": "Nog geen vertrouwde afzenders", + "empty_description": "Wanneer je een e-mail bekijkt met geblokkeerde afbeeldingen, klik op \"Deze afzender altijd vertrouwen\" om ze hier toe te voegen.", + "add_manually": "Afzender handmatig toevoegen", + "add_button": "Toevoegen", + "add_placeholder": "Voer e-mailadres in", + "search_placeholder": "Zoek afzenders...", + "no_results": "Geen afzenders komen overeen met je zoekopdracht", + "remove": "Verwijderen", + "close": "Sluiten", + "invalid_email": "Voer een geldig e-mailadres in", + "already_added": "Deze afzender wordt al vertrouwd", + "save_error": "Opslaan mislukt - controleer het Contacten-debuglog voor details", + "use_address_book_label": "Synchroniseren met adresboek", + "use_address_book_description": "Sla vertrouwde afzenders op in een apart \"Vertrouwde afzenders\"-adresboek zodat ze op al je apparaten synchroniseren" + }, + "hover_actions": { + "label": "Snelle hover-acties", + "description": "Kies welke snelle acties verschijnen wanneer u over een e-mail in de lijst beweegt", + "delete": "Verwijderen", + "star": "Ster aan / uit", + "mark_read": "Markeer gelezen / ongelezen", + "archive": "Archiveren", + "tag": "Label", + "spam": "Markeer als spam", + "not_spam": "Geen spam", + "none_selected": "Geen acties geselecteerd", + "mode_label": "Weergavemodus", + "mode_inline": "Inline", + "mode_floating": "Zwevend", + "corner_label": "Zwevende positie", + "corner_top-left": "Linksboven", + "corner_top-right": "Rechtsboven", + "corner_bottom-left": "Linksonder", + "corner_bottom-right": "Rechtsonder" + }, + "default_mail_program": { + "label": "Standaard e-mailprogramma", + "description": "Registreer {appName} als uw standaard e-mailprogramma voor mailto:-links", + "button": "Instellen als standaard", + "success": "Browser gevraagd om als standaard in te stellen", + "error": "Uw browser ondersteunt deze functie niet" + }, + "attachment_reminder": { + "label": "Bijlageherinnering", + "description": "Waarschuwen vóór verzending wanneer uw bericht bijlagen vermeldt maar er geen zijn bijgevoegd", + "keywords_label": "Triggerwoorden", + "keywords_description": "Woorden of zinnen die de herinnering activeren wanneer ze in uw bericht worden gevonden", + "add_placeholder": "Trefwoord toevoegen...", + "add": "Add", + "remove": "Verwijderen" + }, + "empty_subject_warning": { + "label": "Waarschuwing bij leeg onderwerp", + "description": "Vraag om bevestiging voordat een bericht zonder onderwerp wordt verzonden." + }, + "recipient_mentions": { + "label": "Ontvangers noemen met @", + "description": "Als je @ typt in het bericht, worden de ontvangers voorgesteld en wordt de voornaam van de gekozen ontvanger ingevoegd. Enter of Tab kiest de gemarkeerde naam." + }, + "mention_adds_recipient": { + "label": "Contacten bij @-vermeldingen", + "description": "Bij het typen van @ worden ook je contacten en mensen uit de adreslijst voorgesteld. Kies je iemand die nog geen ontvanger is, dan wordt die aan het bericht toegevoegd.", + "to": "Toevoegen aan Aan", + "cc": "Toevoegen aan CC", + "off": "Alleen ontvangers" + }, + "hide_inline_image_attachments": { + "label": "Inline-afbeeldingen verbergen uit bijlagenlijst", + "description": "Afbeeldingen die in de berichttekst zijn ingesloten worden niet als aparte bijlagen weergegeven" + }, + "attachment_image_previews": { + "label": "Voorbeelden van afbeeldingen tonen in bijlagen", + "description": "Toon afbeeldingsbijlagen als miniatuurkaarten in plaats van generieke bestandspictogrammen" + }, + "send_delay": { + "label": "Verzenden ongedaan maken / verzendvertraging", + "description": "Vertraag normale verzending met een korte server-side periode.", + "off": "Uit", + "seconds": "{seconds} seconden", + "unsupported": "Het huidige account meldt geen ondersteuning voor vertraagd verzenden. De instelling blijft opgeslagen voor andere accounts." + }, + "return_to_list_after_action": { + "label": "Terug naar lijst na verwijderen of markeren als ongelezen", + "description": "Na het verwijderen of als ongelezen markeren van het geopende bericht terugkeren naar de berichtenlijst in plaats van het volgende bericht te openen." + }, + "swipe_right_action": { + "label": "Actie bij naar rechts vegen (mobiel)", + "description": "Op aanraakapparaten de actie die wordt uitgevoerd wanneer je een bericht in de lijst naar rechts veegt." + }, + "swipe_left_action": { + "label": "Actie bij naar links vegen (mobiel)", + "description": "Op aanraakapparaten de actie die wordt uitgevoerd wanneer je een bericht in de lijst naar links veegt." + }, + "swipe_actions": { + "none": "Geen", + "archive": "Archiveren", + "delete": "Verwijderen", + "mark_read": "Gelezen omschakelen", + "star": "Ster omschakelen", + "spam": "Markeren als spam" + }, + "rtl_editing": { + "label": "Ondersteuning voor rechts-naar-links bewerken", + "description": "Voegt een richtingsknop toe aan de werkbalk van de editor waarmee alinea's links-naar-rechts of rechts-naar-links kunnen worden ingesteld" + } + }, + "composer": { + "title": "Opstellen", + "description": "Configureer instellingen voor het opstellen van e-mails", + "autosave": { + "label": "Automatisch opslaan interval", + "description": "Hoe vaak concepten automatisch worden opgeslagen", + "30s": "Elke 30 seconden", + "1m": "Elke minuut", + "2m": "Elke 2 minuten", + "5m": "Elke 5 minuten" + }, + "send_confirmation": { + "label": "Verzendbevestiging", + "description": "Vraag om bevestiging voordat e-mails worden verzonden" + }, + "default_reply": { + "label": "Standaard antwoordmodus", + "description": "Standaardactie bij klikken op beantwoorden", + "reply": "Beantwoorden", + "reply_all": "Allen beantwoorden" + } + }, + "privacy": { + "title": "Privacy & Beveiliging", + "description": "Beheer je privacy- en beveiligingsinstellingen", + "external_images": { + "label": "Externe afbeeldingen blokkeren", + "description": "Voorkom tracking via externe afbeeldingen" + }, + "session_timeout": { + "label": "Sessie time-out", + "description": "Automatisch uitloggen na inactiviteit", + "never": "Nooit", + "30m": "30 minuten", + "1h": "1 uur", + "4h": "4 uur" + }, + "clear_cache": { + "label": "Cache wissen", + "description": "Verwijder gecachte gegevens en tijdelijke bestanden", + "button": "Cache wissen", + "confirm": "Weet je zeker dat je de cache wilt wissen?", + "success": "Cache succesvol gewist" + } + }, + "account": { + "title": "Account", + "description": "Bekijk je accountinformatie", + "name_label": "Weergavenaam", + "username_label": "Gebruikersnaam", + "account_type_label": "Accounttype", + "auth_method_label": "Authenticatie", + "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", + "auth_method_basic": "Wachtwoord", + "auth_method_token": "Toegangstoken", + "demo_account": "Demoaccount", + "email": { + "label": "E-mailadres", + "value": "{email}" + }, + "server": { + "label": "JMAP-server", + "value": "{server}" + }, + "storage": { + "label": "Opslaggebruik", + "used": "{used} van {total} gebruikt", + "percentage": "{percent}% gebruikt" + }, + "last_sync": { + "label": "Laatste synchronisatie", + "value": "{time}" + }, + "accounts": { + "title": "Aangemelde accounts", + "description": "Sleep om de volgorde aan te passen waarin accounts in het accountmenu verschijnen", + "active": "Momenteel actief account", + "default_badge": "Standaardaccount", + "set_default": "Als standaard instellen", + "switch_to": "Overschakelen naar dit account", + "move_up": "Omhoog verplaatsen", + "move_down": "Omlaag verplaatsen", + "drag_handle": "Sleep om volgorde aan te passen", + "add": "Account toevoegen" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Gedeeld met mij", + "description": "Groeps- en gedeelde accounts die u kunt beheren. Selecteer er een om de filters, afwezigheidsassistent, agenda's en contacten te bewerken.", + "shared_label": "Gedeeld account" + } + }, + "security": { + "title": "Accountbeveiliging", + "description": "Beheer uw wachtwoord, tweefactorauthenticatie en beveiligingsinstellingen", + "detecting": "Servermogelijkheden worden gedetecteerd...", + "not_available": "Accountbeveiligingsbeheer is niet beschikbaar voor deze mailserver. Vereiste machtigingen kunnen zijn uitgeschakeld. Zie de documentatie voor details.", + "password": { + "title": "Wachtwoord wijzigen", + "current": "Huidig wachtwoord", + "new": "Nieuw wachtwoord", + "confirm": "Bevestig nieuw wachtwoord", + "submit": "Wachtwoord wijzigen", + "success": "Wachtwoord succesvol gewijzigd", + "error_title": "Wachtwoord wijzigen mislukt", + "error_mismatch": "Nieuwe wachtwoorden komen niet overeen", + "error_min_length": "Wachtwoord moet minimaal 8 tekens bevatten", + "error_generic": "Kan wachtwoord niet wijzigen" + }, + "display_name": { + "label": "Weergavenaam", + "description": "Uw naam zoals weergegeven op de server", + "placeholder": "Voer uw weergavenaam in", + "save": "Opslaan", + "success": "Weergavenaam bijgewerkt", + "error": "Kan weergavenaam niet bijwerken" + }, + "totp": { + "section_title": "Tweefactorauthenticatie", + "label": "TOTP-authenticatie", + "description": "Voeg een extra beveiligingslaag toe met een op tijd gebaseerd eenmalig wachtwoord", + "active": "Ingeschakeld", + "inactive": "Uitgeschakeld", + "enabled": "Tweefactorauthenticatie ingeschakeld", + "disabled": "Tweefactorauthenticatie uitgeschakeld", + "enable_error": "Kan 2FA niet inschakelen", + "disable_error": "Kan 2FA niet uitschakelen", + "setup_instructions": "Kopieer deze URL naar uw authenticator-app (Google Authenticator, Authy, etc.):", + "verification_code": "Verificatiecode", + "confirm": "Bevestigen", + "disable": "Uitschakelen", + "disable_confirm_prompt": "Voer uw wachtwoord in om tweefactorauthenticatie uit te schakelen.", + "password_required": "Wachtwoord is vereist", + "code_required": "Verificatiecode is vereist", + "code_invalid": "Ongeldige verificatiecode. Controleer uw authenticator-app en probeer het opnieuw." + }, + "app_passwords": { + "title": "App-wachtwoorden", + "description": "Maak wachtwoorden aan voor apps die geen tweefactorauthenticatie ondersteunen", + "add": "Toevoegen", + "create": "Aanmaken", + "cancel": "Annuleren", + "generate": "Genereren", + "name_label": "App-naam", + "name_placeholder": "bijv. Thunderbird, iPhone Mail", + "password_label": "Wachtwoord (leeg laten voor automatisch genereren)", + "password_placeholder": "Automatisch gegenereerd als leeg", + "added": "App-wachtwoord aangemaakt", + "removed": "App-wachtwoord verwijderd", + "add_error": "Kan app-wachtwoord niet aanmaken", + "remove_error": "Kan app-wachtwoord niet verwijderen", + "none": "Geen app-wachtwoorden geconfigureerd", + "done": "Klaar", + "expires_label": "Verloopt (optioneel)", + "copy_now_warning": "Kopieer dit wachtwoord nu - het wordt niet opnieuw weergegeven.", + "allowed_ips_label": "Toegestane IP's (optioneel)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Gescheiden door komma of spatie. Laat leeg om elk IP toe te staan." + }, + "api_keys": { + "title": "API-sleutels", + "description": "Maak API-sleutels voor scripts en integraties die rechtstreeks met de server praten", + "name_label": "Sleutelnaam", + "name_placeholder": "bijv. Back-upscript, CI-runner", + "copy_now_warning": "Kopieer deze API-sleutel nu - hij wordt niet opnieuw getoond.", + "added": "API-sleutel aangemaakt", + "removed": "API-sleutel verwijderd", + "add_error": "API-sleutel aanmaken mislukt", + "remove_error": "API-sleutel verwijderen mislukt", + "none": "Geen API-sleutels geconfigureerd" + }, + "encryption": { + "section_title": "Versleuteling in rust", + "label": "E-mailversleuteling", + "description": "Versleutel opgeslagen e-mails op de server voor extra privacy", + "active": "{type}-versleuteling ingeschakeld", + "inactive": "Uitgeschakeld", + "enabled": "Versleuteling in rust ingeschakeld", + "disabled_success": "Versleuteling in rust uitgeschakeld", + "error": "Kan versleutelingsinstellingen niet bijwerken", + "algorithm_label": "Versleutelingsalgoritme", + "encrypt_on_append": "Nieuwe e-mails versleutelen bij uploaden", + "allow_spam_training": "Spamtraining toestaan voordat e-mails worden versleuteld", + "enable_encryption": "Versleuteling inschakelen", + "enable_tooltip": "Versleuteling met deze sleutel inschakelen", + "configure_title": "Versleuteling met deze sleutel inschakelen", + "enable_button": "Inschakelen", + "disable_tooltip": "Versleuteling uitschakelen", + "enabled_success": "Versleuteling in rust ingeschakeld" + }, + "email_client": { + "title": "E-mailclient instellen", + "description": "Gebruik deze inloggegevens om uw desktop- of mobiele e-mailclient te configureren (Thunderbird, Apple Mail, Outlook, enz.)", + "jmap_username_label": "JMAP-gebruikersnaam", + "copy": "Kopiëren", + "copied": "Gekopieerd", + "password_instructions": "Gebruik uw hierboven vermelde JMAP-gebruikersnaam samen met een app-wachtwoord om in te loggen bij uw e-mailclient. Maak een app-wachtwoord aan in het bovenstaande gedeelte als u dat nog niet heeft gedaan." + }, + "link_device": { + "title": "Mobiele app koppelen", + "description": "Log in bij de Bulwark Mail-app op uw telefoon zonder daar uw wachtwoord te typen. Uw telefoon krijgt een eigen aanmelding, dus als u hier uitlogt, blijft de telefoon ingelogd.", + "generate": "QR-code tonen", + "regenerate": "Nieuwe code tonen", + "instructions": "Open de Bulwark Mail-app en tik op het inlogscherm op \"Scan a sign-in code\" (of op \"Add account\" → \"Scan a sign-in code\" als u al bent ingelogd) en richt de camera hierop.", + "expires_in": "Deze code verloopt over {seconds} seconden. Hij kan maar één keer worden gebruikt.", + "expired": "Deze code is verlopen.", + "error": "Kon geen koppelingscode aanmaken. Probeer het opnieuw.", + "password_prompt": "Voer uw wachtwoord in om te bevestigen dat u het bent. Uw telefoon krijgt een eigen aanmelding; uw wachtwoord wordt niet naar de telefoon gestuurd.", + "show_password": "Wachtwoord tonen", + "hide_password": "Wachtwoord verbergen", + "continue": "Doorgaan", + "error_wrong_password": "Onjuist wachtwoord. Probeer het opnieuw.", + "totp_prompt": "Voer de code uit uw authenticator-app in.", + "error_password_or_code": "Het wachtwoord of de code is onjuist.", + "error_not_signed_in": "Uw sessie is verlopen. Log opnieuw in om een apparaat te koppelen.", + "error_impersonation": "U kunt geen apparaten koppelen terwijl u de mailbox van een andere gebruiker bekijkt.", + "error_too_many_attempts": "Te veel pogingen. Wacht even en probeer het opnieuw.", + "error_session_secret": "Apparaten koppelen is niet ingesteld op deze server (SESSION_SECRET ontbreekt). Neem contact op met uw beheerder.", + "error_server_unreachable": "Uw mailserver is niet bereikbaar. Probeer het opnieuw.", + "error_pairing_unavailable": "Uw mailserver staat het koppelen van apparaten op deze manier niet toe. Neem contact op met uw beheerder.", + "qr_alt": "QR-code om in te loggen", + "link_label": "Inloglink", + "copy_link": "Inloglink kopiëren", + "copied": "Gekopieerd", + "copy_hint": "Geen camera? Kopieer de link en plak hem in de app via \"Paste sign-in link\".", + "success_title": "Ingelogd op uw telefoon", + "success_body": "U kunt deze pagina sluiten. Toon een nieuwe code om nog een apparaat te koppelen.", + "link_another": "Nog een apparaat koppelen", + "use_sso": "In plaats daarvan eenmalige aanmelding gebruiken", + "error_insecure_server": "De mobiele app maakt alleen via https verbinding met mailservers, en deze gebruikt http." + }, + "public_keys": { + "name_label": "Naam van openbare sleutel", + "name_placeholder": "bijv. Mijn sleutel", + "key_label": "Openbare sleutel (S/MIME of PGP in ASCII-formaat)", + "key_placeholder": "Plak hier uw openbare sleutel", + "none": "Geen openbare sleutels geconfigureerd" + } + }, + "identities": { + "title": "Verzendidentiteiten", + "description": "Beheer e-mailadressen van waaruit je kunt verzenden", + "identities_count": { + "label": "Je identiteiten", + "description": "E-mailadressen geconfigureerd voor verzenden", + "count_zero": "Geen identiteiten", + "count_one": "1 identiteit", + "count_other": "{count} identiteiten" + }, + "manage": "Identiteiten beheren", + "sub_addressing": { + "label": "Sub-adressering", + "description": "Gebruik tags zoals gebruiker+tag@domein.nl om inkomende mail te organiseren", + "learn_more": "Meer informatie" + } + }, + "vacation": { + "title": "Afwezigheid", + "description": "Inkomende e-mails automatisch afhandelen terwijl je afwezig bent", + "loading": "Afwezigheidsinstellingen laden...", + "not_supported": "Je mailserver ondersteunt geen afwezigheidsantwoorden.", + "fetch_error": "Kan afwezigheidsinstellingen niet laden. Probeer het opnieuw.", + "status": { + "label": "Automatisch antwoorden", + "description": "Stuur een automatisch antwoord naar mensen die je mailen", + "active": "Actief", + "inactive": "Inactief" + }, + "date_range": { + "start": "Startdatum", + "start_description": "Leeg laten voor geen startlimiet", + "end": "Einddatum", + "end_description": "Leeg laten voor geen eindlimiet" + }, + "message": { + "subject_label": "Onderwerp", + "subject_description": "Onderwerpregel van het automatische antwoord", + "subject_placeholder": "Afwezigheidsbericht", + "body_label": "Berichttekst", + "body_description": "Inhoud van het bericht in platte tekst", + "body_placeholder": "Bedankt voor je e-mail. Ik ben momenteel niet aanwezig en zal reageren bij terugkomst.", + "html_label": "Opgemaakt bericht (HTML)", + "html_description": "Voeg een opgemaakte versie met links en stijl toe. Ontvangers van wie het e-mailprogramma deze niet kan weergeven, zien de platte tekst hierboven.", + "html_placeholder": "Schrijf een opgemaakt afwezigheidsantwoord…" + }, + "preview": { + "title": "Voorbeeld", + "show": "Voorbeeld tonen", + "hide": "Voorbeeld verbergen" + }, + "save": "Wijzigingen opslaan", + "saving": "Opslaan...", + "warnings": { + "end_before_start": "De einddatum moet na de startdatum liggen", + "invalid_date": "Vul een geldige start- en einddatum met tijd in, of laat het veld leeg", + "start_in_past": "De startdatum ligt in het verleden", + "empty_body": "De berichttekst is leeg - ontvangers krijgen een leeg antwoord", + "subject_too_long": "Het onderwerp is te lang voor de server (maximaal {max} bytes).", + "body_too_long": "Het bericht is te lang voor de server (maximaal {max} bytes, inclusief opmaak).", + "forward_address": "Vul een geldig adres in om naar door te sturen", + "not_running": "Doorsturen of de ontvangers van het antwoord zijn opgeslagen, maar nu niet actief. Sla op om ze weer in te schakelen." + }, + "forward": { + "title": "Doorsturen", + "description": "Stuurt binnenkomende berichten door naar een ander adres, met of zonder automatisch antwoord, binnen de periode hierboven (zonder einddatum, tot het wordt uitgeschakeld). Spam wordt niet doorgestuurd.", + "enabled_label": "Berichten doorsturen", + "to_label": "Doorsturen naar", + "to_placeholder": "email@voorbeeld.nl", + "keep_label": "In postvak IN houden", + "keep_description": "Anders wordt het bericht alleen doorgestuurd: het blijft niet in deze mailbox en er wordt geen filterregel op toegepast." + }, + "audience": { + "label": "Antwoorden aan", + "description": "Interne afzenders: {domains}", + "all": "Alle afzenders", + "internal": "Alleen interne afzenders", + "external": "Alleen externe afzenders" + }, + "auto_reply": { + "title": "Automatisch antwoord" + } + }, + "folders": { + "role_memos": "Notities", + "title": "Mappen", + "description": "Beheer uw e-mailmappen en wijs standaardrollen toe", + "folder_list": "Uw mappen", + "folder_list_description": "Klik op een mappictogram om het aan te passen", + "standard_roles": "Standaard maprollen", + "standard_roles_description": "Wijs toe welke mappen worden gebruikt voor standaardrollen zoals Postvak IN, Verzonden, Prullenbak, enz.", + "role_inbox": "Postvak IN", + "role_drafts": "Concepten", + "role_sent": "Verzonden", + "role_trash": "Prullenbak", + "role_junk": "Spam / Ongewenst", + "role_archive": "Archief", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Geen", + "create_folder": "Map maken", + "create_subfolder": "Submap maken", + "reorder": "Sleep om te herschikken", + "reorder_error": "Herschikken van mappen is mislukt", + "move_error": "Verplaatsen van map is mislukt", + "select_folder": "{name} selecteren", + "select_hint": "Selecteren. Shift+klik selecteert alle mappen tot en met de laatste waarop je hebt geklikt.", + "selected_count": "{count, plural, one {# map} other {# mappen}} geselecteerd", + "move_to": "Verplaatsen naar...", + "move_to_top_level": "Hoogste niveau", + "move": "Verplaatsen", + "clear_selection": "Selectie wissen", + "folders_moved": "{count, plural, one {# map verplaatst} other {# mappen verplaatst}}", + "move_partial_error": "{count, plural, one {# map kon} other {# mappen konden}} niet worden verplaatst", + "subfolder_of": "In {name}", + "subfolder_name": "Naam van submap", + "new_folder_name": "Mapnaam", + "rename": "Hernoemen", + "change_icon": "Pictogram wijzigen", + "count_mode": "Teller voor {name}", + "count_mode_default": "Teller: standaard", + "count_mode_unread": "Alleen ongelezen", + "count_mode_all": "Ongelezen en totaal", + "count_mode_none": "Geen teller", + "delete": "Verwijderen", + "confirm_delete": "Weet u zeker dat u \"{name}\" wilt verwijderen? E-mails in deze map worden naar de Prullenbak verplaatst.", + "create": "Maken", + "cancel": "Annuleren", + "no_folders": "Geen aangepaste mappen", + "cannot_delete_role": "Kan een map met een standaardrol niet verwijderen. Verwijder eerst de rol.", + "folder_created": "Map aangemaakt", + "folder_renamed": "Map hernoemd", + "folder_deleted": "Map verwijderd", + "role_updated": "Maprol bijgewerkt", + "error_create": "Map maken mislukt", + "error_rename": "Map hernoemen mislukt", + "error_delete": "Map verwijderen mislukt", + "error_delete_has_children": "Map kan niet worden verwijderd: deze bevat nog submappen. Verwijder of verplaats deze eerst.", + "error_delete_has_email": "Map kan niet worden verwijderd: deze bevat nog e-mails. Verplaats of verwijder deze eerst.", + "error_role": "Maprol bijwerken mislukt" + }, + "advanced": { + "title": "Geavanceerd", + "description": "Geavanceerde opties en ontwikkelaarsinstellingen", + "debug_mode": { + "label": "Debugmodus", + "description": "Schakel gedetailleerde logging in voor probleemoplossing" + }, + "debug_categories": { + "description": "Selecteer welke categorieën moeten worden gelogd. Schakel categorieën die u niet nodig hebt uit om ruis in de console te verminderen.", + "jmap": "JMAP-client", + "jmap_description": "Mailboxbewerkingen, e-mail ophalen en JMAP-protocolverzoeken", + "calendar": "Agenda", + "calendar_description": "Agenda-afspraken, importen en planningsberichten", + "tasks": "Taken", + "tasks_description": "Aanmaken, ophalen en bijwerken van agendataken", + "auth": "Authenticatie", + "auth_description": "Inloggen, TOTP, tokenuitwisseling en sessiebeheer", + "filters": "Filters", + "filters_description": "Sieve-filterregels en vakantiescripts", + "email": "E-mailweergave", + "email_description": "E-mailrendering, TNEF-verwerking en markeren als gelezen", + "push": "Pushmeldingen", + "push_description": "Instellen en afleveren van pushmeldingen", + "contacts": "Contacten en adresboeken", + "contacts_description": "Contactsynchronisatie, adresboekacties en vertrouwde afzenders" + }, + "settings_sync": { + "label": "Instellingen synchroniseren", + "description": "Synchroniseer uw instellingen tussen browsers en apparaten" + }, + "sender_favicons": { + "label": "Afzender-favicons", + "description": "Toon websitepictogrammen als profielfoto's voor zakelijke afzenders", + "refresh_label": "Afzenderpictogrammen vernieuwen", + "refresh_description": "De websitepictogrammen opnieuw ophalen, bijvoorbeeld als een afzender het zijne heeft gewijzigd", + "refresh_button": "Vernieuwen", + "refresh_done": "Afzenderpictogrammen worden opnieuw opgehaald" + }, + "show_avatars_in_junk": { + "label": "Avatars tonen in de map ongewenst", + "description": "Toon profielfoto's en favicons van afzenders in de map ongewenst. Standaard uitgeschakeld zodat phishingpogingen geen vertrouwd uiterlijk krijgen." + }, + "keyboard_shortcuts": { + "label": "Sneltoetsen", + "description": "Bekijk beschikbare sneltoetsen", + "button": "Sneltoetsen bekijken" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Instellingen resetten", + "description": "Herstel alle instellingen naar standaardwaarden", + "button": "Resetten naar standaard" + }, + "export_settings": { + "label": "Instellingen exporteren", + "description": "Download je instellingen als JSON", + "button": "Exporteren" + }, + "import_settings": { + "label": "Instellingen importeren", + "description": "Upload instellingen vanuit JSON-bestand", + "button": "Importeren" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Broncode" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Klik op spam en phishing voordat ze uw inbox bereiken. Laat echte e-mail door. Verliest u alle drie de schilden, dan valt de inbox.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Echte e-mail" + }, + "click_to_block": "Klik om te blokkeren", + "let_through": "Doorlaten", + "start": "Starten", + "play_again": "Opnieuw spelen", + "score": "Score", + "combo": "Reeksbonus", + "shields_left": "{count} van {total} schilden over", + "seconds": "{seconds} s", + "best": "Record: {score}", + "new_best": "Nieuw record", + "held": "Inbox verdedigd", + "overrun": "Inbox overlopen", + "stats": { + "caught": "Geblokkeerd", + "delivered": "Bezorgd", + "mistakes": "Fouten" + } + } + }, + "sidebar_apps": { + "title": "Zijbalk-apps", + "description": "Aangepaste apps en links in de zijbalk beheren", + "keep_loaded": "Apps geladen houden", + "keep_loaded_description": "Inline-apps op de achtergrond laten draaien bij het wisselen om herladen te voorkomen", + "managed_title": "Aangeboden door uw beheerder", + "managed_description": "Deze apps zijn voor iedereen ingesteld en kunnen niet worden gewijzigd.", + "manage_title": "Aangepaste apps", + "manage_description": "Aangepaste apps toevoegen, bewerken of verwijderen uit de zijbalk" + }, + "contacts": { + "title": "Contacten", + "description": "Importeer en exporteer uw contacten", + "import_label": "Contacten importeren", + "import_description": "Contacten importeren uit een vCard-bestand (.vcf)", + "export_label": "Contacten exporteren", + "export_description": "Alle contacten exporteren als vCard-bestand (.vcf)", + "manage_title": "Adresboeken", + "manage_description": "Hernoem uw adresboeken", + "no_address_books": "Geen adresboeken gevonden", + "categories_title": "Categorieën", + "categories_description": "Contactcategorieën hernoemen", + "no_categories": "Geen categorieën gevonden", + "group_by_letter_description": "Toon alfabetische sectiekoppen in de contactenlijst", + "group_by_letter_label": "Groeperen op eerste letter", + "sort_by_last_name_label": "Sorteren op achternaam", + "sort_by_last_name_description": "Sorteer de contactenlijst op achternaam zodat gezinsleden bij elkaar staan" + }, + "downloads": { + "title": "Downloads", + "description": "Pas aan hoe gedownloade e-mails en bijlagen worden genoemd.", + "reset": "Standaard herstellen", + "preview": "Voorbeeld:", + "email_template": { + "label": "E-mailbestandsnaam (.eml)", + "description": "Sjabloon dat wordt gebruikt bij het exporteren of slepen van een e-mail naar het bestandssysteem. De extensie .eml wordt automatisch toegevoegd." + }, + "attachment_template": { + "label": "Bestandsnaam van bijlage", + "description": "Sjabloon bij het downloaden of slepen van een bijlage. Als je '{filename}' en '{ext}' weglaat, blijft de oorspronkelijke extensie behouden." + }, + "bundle_template": { + "label": "Bestandsnaam .zip met meerdere e-mails", + "description": "Sjabloon dat wordt gebruikt wanneer je meerdere geselecteerde e-mails als één .zip-archief sleept of downloadt. De extensie .zip wordt automatisch toegevoegd." + }, + "spaces": { + "label": "Spaties", + "description": "Vervang spaties in de uiteindelijke bestandsnaam door een ander teken.", + "keep": "Spaties behouden", + "underscore": "Vervangen door _", + "dash": "Vervangen door -" + }, + "lowercase": { + "label": "Kleine letters", + "description": "Forceer de hele bestandsnaam in kleine letters." + }, + "strip_diacritics": { + "label": "Diakritische tekens verwijderen", + "description": "Zet letters met accenten om naar hun ASCII-equivalent (ä → a, é → e). Handig voor tools die Unicode-namen niet goed verwerken." + }, + "collapse_separators": { + "label": "Herhaalde scheidingstekens samenvoegen", + "description": "Voeg reeksen spaties, underscores of streepjes samen tot één teken." + }, + "after_export": { + "label": "Na exporteren", + "description": "Verplaats de e-mail eventueel nadat deze als .eml is geëxporteerd.", + "keep": "In postvak houden", + "archive": "Naar archief verplaatsen", + "trash": "Naar prullenbak verplaatsen" + } + }, + "filters": { + "title": "E-mailfilters", + "description": "Maak regels om inkomende e-mails automatisch te sorteren, labelen en beheren", + "add_rule": "Regel toevoegen", + "no_rules": "Geen filterregels", + "vacation_active": "Afwezigheidsantwoord is actief", + "vacation_active_description": "Automatisch antwoord is ingeschakeld voor inkomende berichten", + "vacation_configure": "Configureren", + "no_rules_description": "Maak regels om uw inkomende e-mails automatisch te organiseren", + "edit_rule": "Regel bewerken", + "new_rule": "Nieuwe regel", + "delete_rule": "Regel verwijderen", + "delete_confirm": "Weet u zeker dat u deze regel wilt verwijderen?", + "enable": "Inschakelen", + "disable": "Uitschakelen", + "raw_editor": "Sieve-scripteditor", + "raw_editor_warning": "Het bewerken van het Sieve-script kan de visuele regelbewerking verstoren. Wijzigingen hier overschrijven de visuele builder.", + "validate": "Valideren", + "validation_success": "Het script is geldig", + "validation_error": "Het script bevat fouten", + "save": "Regels opslaan", + "saving": "Opslaan...", + "saved": "Filters succesvol opgeslagen", + "save_failed": "Filters opslaan mislukt", + "loading": "Filters laden...", + "not_supported": "Uw mailserver ondersteunt geen e-mailfilters.", + "rule_name": "Regelnaam", + "rule_name_placeholder": "bijv. Nieuwsbrieven sorteren", + "match_all": "Aan ALLE voorwaarden voldoen", + "match_any": "Aan EEN willekeurige voorwaarde voldoen", + "conditions": "Voorwaarden", + "add_condition": "Voorwaarde toevoegen", + "actions": "Acties", + "add_action": "Actie toevoegen", + "stop_processing": "Verwerking van volgende regels stoppen", + "include_spam": "Ook berichten verplaatsen die als spam zijn gemarkeerd", + "attachment_type_placeholder": "bijv. pdf, doc, jpg", + "value_placeholder_multi": "Waarde (meerdere met komma's gescheiden)", + "condition_fields": { + "attachment": "Bijlage", + "from": "Van", + "to": "Aan", + "cc": "Cc", + "subject": "Onderwerp", + "header": "Aangepaste header", + "size": "Grootte", + "body": "Inhoud", + "all": "Alle berichten" + }, + "comparators": { + "has_any": "aanwezig", + "has_type": "van type", + "contains": "bevat", + "not_contains": "bevat niet", + "is": "is precies", + "not_is": "is niet", + "starts_with": "begint met", + "ends_with": "eindigt met", + "matches": "komt overeen met patroon", + "greater_than": "is groter dan", + "less_than": "is kleiner dan", + "address_is": "is het adres", + "domain_is": "heeft het domein" + }, + "action_types": { + "move": "Verplaatsen naar map", + "copy": "Kopiëren naar map", + "forward": "Doorsturen naar", + "mark_read": "Markeren als gelezen", + "star": "Ster toevoegen", + "add_label": "Label toevoegen", + "discard": "Stilletjes verwijderen", + "reject": "Afwijzen met bericht", + "keep": "In postvak IN houden", + "stop": "Verwerking stoppen" + }, + "move_to_folder": "Map selecteren", + "copy_to_folder": "Map selecteren", + "forward_to": "Doorsturen naar e-mailadres", + "forward_placeholder": "email@voorbeeld.nl", + "forward_keep_copy": "Kopie bewaren", + "forward_limit": "Maximaal aantal doorsturingen per bericht op deze server: {count}. Extra doorsturingen worden overgeslagen.", + "forward_unavailable": "Deze server staat doorsturen niet toe. Doorstuuracties in filterregels hebben geen effect.", + "reject_message": "Afwijzingsbericht", + "reject_placeholder": "Uw e-mail is afgewezen", + "label_name": "Labelnaam", + "label_placeholder": "Label kiezen", + "header_name": "Headernaam", + "header_placeholder": "bijv. X-Mailing-List", + "size_bytes": "Grootte in bytes", + "size_placeholder": "bijv. 1000000", + "system_managed": "Systeembeheerde regel", + "opaque_warning": "Dit script is buiten de visuele builder bewerkt. Alleen Sieve-scriptbewerking is beschikbaar.", + "open_sieve_editor": "Sieve-scripteditor openen", + "fetch_error": "Filters konden niet worden geladen", + "expanded_view": "Uitgebreide weergave", + "expanded_view_description": "Filterregels weergeven met gedetailleerde voorwaarde- en actieblokken", + "if": "Als", + "then": "Dan", + "match_all_conditions": "alle overeenkomen", + "match_any_condition": "een overeenkomt", + "and": "en", + "or": "of", + "cancel": "Annuleren", + "confirm_delete": "Verwijderen", + "rule_list": "Filterregels", + "drag_to_reorder": "Slepen om te herschikken", + "match_type": "Overeenkomsttype", + "reset_to_visual": "Terugzetten naar visuele builder", + "reset_warning": "Dit verwijdert het huidige script en begint opnieuw.", + "confirm_reset": "Terugzetten", + "validation_empty_name": "Regelnaam is verplicht", + "validation_empty_conditions": "Minstens één voorwaarde met een waarde is vereist", + "validation_empty_actions": "Minstens één actie is vereist", + "templates_section": "Starten vanuit sjabloon", + "template_newsletters": "Nieuwsbrieven naar map verplaatsen", + "template_receipts": "Bonnen automatisch archiveren", + "template_important": "Belangrijke e-mails markeren", + "template_notifications": "Meldingen filteren", + "sieve_editor": { + "title": "Sieve-scripteditor", + "warning": "Het bewerken van het Sieve-script kan de visuele regelbewerking verbreken. Wijzigingen hier overschrijven de visuele builder.", + "script_content": "Sieve-script", + "valid": "Script is geldig", + "invalid": "Script bevat fouten", + "save_warning": "Opslaan overschrijft alle visuele regels. Dit kan niet ongedaan worden gemaakt. Klik nogmaals op Opslaan om te bevestigen.", + "validating": "Valideren...", + "validate": "Valideren", + "cancel": "Annuleren", + "save": "Opslaan", + "confirm_save": "Opslaan bevestigen", + "validation_failed": "Validatieverzoek mislukt" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# voorwaarde} other {# voorwaarden}}", + "actions_count": "{count, plural, one {# actie} other {# acties}}" + }, + "origin_external": "Extern", + "managed_by_tooltip": "Beheerd door {source}. Bewerk het in die app of gebruik de ruwe Sieve-editor.", + "quick_rule_names": { + "move_sender": "Van {sender} naar {folder} verplaatsen", + "move_domain": "Van @{domain} naar {folder} verplaatsen", + "move_list": "Lijst {list} naar {folder} verplaatsen", + "mark_read": "Van {sender} als gelezen markeren", + "tag": "Van {sender} labelen als {tag}", + "block": "{sender} blokkeren", + "custom": "Berichten van {sender}", + "custom_plain": "Nieuwe regel", + "senders": "{count, plural, one {# afzender} other {# afzenders}}" + }, + "suggestions": "Suggesties", + "suggest_subject": "Onderwerp ‘{subject}’", + "suggest_to": "Aan {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Ook toepassen op berichten die al in deze map staan", + "apply_existing_unsupported": "Alleen regels die de afzender, ontvangers, het onderwerp of headers controleren, en die verplaatsen, kopiëren, als gelezen markeren, een ster geven of labelen, kunnen op bestaande berichten worden toegepast.", + "period_title": "Periode", + "period_start": "Startdatum", + "period_end": "Einddatum", + "validation_period_order": "De einddatum moet na de startdatum liggen", + "period_toggle": "Alleen actief in een periode", + "period_hint": "Geldt voor berichten die in deze periode binnenkomen. Laat een veld leeg om die kant open te laten.", + "validation_period_empty": "Vul een start- of einddatum voor de periode in", + "validation_period_invalid": "Vul voor de periode een geldige datum met tijd in, of laat het veld leeg", + "apply_existing_period": "Een regel met een periode werkt alleen op berichten die binnen die periode binnenkomen en kan dus niet op bestaande berichten worden toegepast.", + "period_status_scheduled": "gepland", + "period_status_active": "actief", + "period_status_expired": "verlopen" + }, + "templates": { + "title": "E-mailsjablonen", + "description": "Maak herbruikbare e-mailsjablonen met plaatshoudervariabelen", + "add": "Nieuw sjabloon", + "edit": "Sjabloon bewerken", + "name": "Sjabloonnaam", + "name_placeholder": "bijv.: Opvolgmail", + "category": "Categorie", + "category_placeholder": "bijv.: Werk, Persoonlijk", + "subject": "Onderwerp", + "subject_placeholder": "E-mailonderwerp", + "body": "Inhoud", + "body_placeholder": "E-mailinhoud...", + "recipients_placeholder": "email@example.com", + "identity": "Verzenden als", + "default_identity": "Standaard identiteit", + "favorite": "Favoriet", + "cancel": "Annuleren", + "create": "Aanmaken", + "update": "Bijwerken", + "confirm_delete": "Verwijderen", + "no_templates": "Geen sjablonen", + "manage": "Sjablonen beheren", + "count": "{count, plural, one {# sjabloon} other {# sjablonen}}", + "export_import": "Exporteren en importeren", + "export_import_description": "Maak een back-up van uw sjablonen of breng ze over naar een ander apparaat", + "export": "Exporteren", + "import": "Importeren", + "validation": { + "empty": "Sjabloonnaam is verplicht", + "too_long": "Sjabloonnaam mag maximaal 200 tekens zijn" + } + }, + "files": { + "display": { + "title": "Weergave", + "description": "Configureer hoe bestanden en mappen worden weergegeven" + }, + "default_view": { + "label": "Standaardweergave", + "description": "Kies tussen raster- en lijstweergave", + "list": "Lijst", + "grid": "Raster" + }, + "default_sort": { + "label": "Standaardsortering", + "description": "Kies de standaardsortering voor bestanden", + "name": "Naam", + "size": "Grootte", + "modified": "Gewijzigd" + }, + "sort_direction": { + "label": "Sorteerrichting", + "description": "Kies oplopende of aflopende volgorde", + "ascending": "Oplopend", + "descending": "Aflopend" + }, + "icons": { + "title": "Pictogrammen", + "description": "Configureer de weergave van bestandspictogrammen" + }, + "show_icons": { + "label": "Bestandspictogrammen tonen", + "description": "Pictogrammen naast bestanden en mappen weergeven" + }, + "colored_icons": { + "label": "Gekleurde pictogrammen", + "description": "Gebruik gekleurde pictogrammen in plaats van monochroom" + }, + "show_thumbnails": { + "label": "Miniaturen weergeven", + "description": "Toon afbeeldingsvoorbeelden in plaats van pictogrammen" + }, + "behavior": { + "title": "Gedrag", + "description": "Configureer het gedrag van de bestandsbrowser" + }, + "show_hidden": { + "label": "Verborgen bestanden tonen", + "description": "Bestanden en mappen weergeven die beginnen met een punt" + }, + "preview": { + "label": "Voorbeeld" + } + }, + "search_placeholder": "Instellingen zoeken", + "search_clear": "Zoekopdracht wissen", + "search_no_results": "Geen overeenkomende instellingen", + "scoped": { + "back": "Terug naar mijn account", + "managing": "Beheren: {name}" + } + }, + "errors": { + "page_error_title": "Er is iets misgegaan", + "page_error_description": "We hebben een onverwachte fout ondervonden. Probeer het opnieuw of keer terug naar de startpagina.", + "sidebar_error": "Kan mappen niet laden", + "email_list_error": "Kan e-mails niet laden", + "viewer_error_title": "Kan e-mail niet weergeven", + "viewer_error_description": "Er was een probleem met het renderen van deze e-mail. Het kan niet-ondersteunde inhoud bevatten.", + "composer_error": "Kan opsteller niet laden", + "settings_error_title": "Instellingen niet beschikbaar", + "settings_error_description": "Kan instellingen niet laden. Je voorkeuren worden mogelijk niet opgeslagen.", + "try_again": "Opnieuw proberen", + "reload": "Herladen", + "reload_emails": "E-mails herladen", + "reload_settings": "Instellingen herladen", + "retry": "Opnieuw proberen", + "go_home": "Ga naar postvak IN" + }, + "context_menu": { + "reply": "Beantwoorden", + "reply_all": "Allen beantwoorden", + "forward": "Doorsturen", + "mark_read": "Markeren als gelezen", + "mark_unread": "Markeren als ongelezen", + "star": "Ster toevoegen", + "unstar": "Ster verwijderen", + "pin": "Vastmaken", + "unpin": "Losmaken", + "move_to": "Verplaatsen naar...", + "copy_to_account": "Kopiëren naar account...", + "remove_from_folder": "Uit deze map verwijderen", + "archive": "Archiveren", + "delete": "Verwijderen", + "mark_as_spam": "Spam melden", + "not_spam": "Geen spam", + "tag": "Label", + "items_selected": "{count} e-mails geselecteerd", + "forward_as_attachments": "Doorsturen als bijlagen", + "edit_draft": "Concept bewerken", + "cancel_scheduled_send": "Verzenden annuleren", + "reschedule_send": "Opnieuw plannen", + "cancel_and_edit": "Annuleren en bewerken", + "cancel_and_compose_again": "Annuleren en opnieuw opstellen", + "rules": { + "title": "Regels", + "move_from": "Berichten van {sender} altijd verplaatsen", + "move_from_domain": "Berichten van @{domain} altijd verplaatsen", + "move_from_list": "Berichten van deze lijst altijd verplaatsen", + "mark_read_from": "Berichten van {sender} altijd als gelezen markeren", + "tag_with": "Altijd labelen met", + "block": "{count, plural, one {Afzender blokkeren} other {# afzenders blokkeren}}", + "no_junk": "Dit account heeft geen map Ongewenst", + "create": "Regel maken…", + "manage": "Regels beheren", + "senders_count": "{count, plural, one {# afzender} other {# afzenders}}", + "cross_account": "Selecteer berichten uit één account", + "opaque_hint": "Je filters zijn met de hand bewerkt. Open de filterinstellingen" , + "loading": "Regels laden…", + "load_failed": "De regels konden niet worden geladen", + "none": "Nog geen regels", + "new_mail_only": "Alleen voor nieuwe berichten", + "running": "\"{name}\" wordt uitgevoerd op {folder}…" + }, + "find_emails_from": "E-mails van {name} zoeken" + }, + "mailbox_context_menu": { + "mark_folder_read": "Map markeren als gelezen", + "mark_folder_tree_read": "Map en submappen markeren als gelezen", + "mark_all_folders_read": "Alle mappen markeren als gelezen", + "new_subfolder": "Nieuwe submap...", + "new_folder": "Nieuwe map...", + "rename": "Hernoemen...", + "share": "Delen...", + "import_email": ".eml of .zip importeren...", + "empty_folder": "Map leegmaken", + "empty_folder_generic": "Map leegmaken", + "delete_folder": "Map verwijderen", + "refresh": "Vernieuwen", + "hide_shared_account": "Verbergen in zijbalk", + "mute_notifications": "Meldingen dempen", + "unmute_notifications": "Meldingen inschakelen", + "mute_account_notifications": "Meldingen voor dit account dempen", + "unmute_account_notifications": "Meldingen voor dit account inschakelen", + "reset_folder_notifications": "Meldingen van mappen herstellen", + "mark_all_confirm_title": "Alle mappen markeren als gelezen", + "mark_all_confirm_message": "Alle ongelezen berichten in uw persoonlijke account als gelezen markeren?", + "delete_confirm_title": "Map verwijderen", + "delete_confirm_message": "Map \"{name}\" definitief verwijderen? Deze actie kan niet ongedaan worden gemaakt.", + "prompt_new_subfolder": "Voer een naam in voor de nieuwe submap.", + "prompt_new_folder": "Voer een naam in voor de nieuwe map.", + "prompt_rename": "Voer een nieuwe naam in voor deze map.", + "toast_marked_read": "Map gemarkeerd als gelezen", + "toast_marked_read_count": "{count, plural, one {1 bericht} other {# berichten}} gemarkeerd als gelezen", + "toast_already_read": "Geen ongelezen berichten", + "toast_marked_all_read": "Alle mappen gemarkeerd als gelezen", + "toast_emptied": "Map leeggemaakt", + "toast_folder_created": "Map aangemaakt", + "toast_folder_renamed": "Map hernoemd", + "toast_folder_deleted": "Map verwijderd", + "toast_error_mark_read": "Markeren als gelezen mislukt", + "toast_error_empty": "Map leegmaken mislukt", + "toast_error_create": "Map aanmaken mislukt", + "toast_error_rename": "Map hernoemen mislukt", + "toast_error_delete": "Map verwijderen mislukt", + "toast_error_delete_has_children": "Map bevat submappen. Verwijder deze eerst.", + "toast_error_delete_has_email": "Map is niet leeg. Maak deze eerst leeg.", + "delete_with_contents_title": "Map is niet leeg", + "delete_with_contents_message": "De map \"{name}\" bevat nog {count, plural, one {1 bericht} other {# berichten}}. De map en al haar berichten verwijderen? Dit kan niet ongedaan worden gemaakt.", + "delete_with_contents_confirm": "Map en berichten verwijderen", + "placeholder_folder_name": "Mapnaam", + "create": "Aanmaken", + "rename_confirm": "Hernoemen" + }, + "shortcuts": { + "title": "Sneltoetsen", + "tip": "Druk op ? om deze hulp te tonen", + "sections": { + "navigation": "Navigatie", + "actions": "E-mailacties", + "global": "Globaal", + "threads": "Gesprekken", + "composer": "Opstellen" + }, + "navigation": { + "next_email": "Volgende e-mail", + "previous_email": "Vorige e-mail", + "open_email": "E-mail openen", + "close_email": "Sluiten / Deselecteren", + "go_to_folder": "Ga naar map", + "next_unread": "Volgende ongelezen e-mail", + "no_more_unread": "Verderop in de lijst staan geen ongelezen e-mails meer" + }, + "actions": { + "reply": "Beantwoorden", + "reply_all": "Allen beantwoorden", + "forward": "Doorsturen", + "star": "Ster aan/uit", + "archive": "Archiveren", + "delete": "Verwijderen", + "mark_unread": "Markeren als ongelezen", + "mark_read": "Markeren als gelezen", + "toggle_spam": "Spam melden / Geen spam" + }, + "global": { + "compose": "Nieuw bericht opstellen", + "search": "Focus op zoeken", + "help": "Sneltoetsen tonen", + "refresh": "E-mails vernieuwen", + "select_all": "Alles selecteren", + "global_search": "Alles doorzoeken", + "extend_selection": "Selectie omlaag / omhoog uitbreiden" + }, + "threads": { + "expand_collapse": "Gesprek uitklappen/inklappen", + "select_mountain_view": "Gesprek selecteren" + }, + "composer": { + "send": "E-mail verzenden", + "schedule_send": "Verzenden plannen", + "template_picker": "Sjabloonkiezer openen" + } + }, + "threads": { + "messages_one": "{count} bericht", + "messages_other": "{count} berichten", + "messages_tooltip": "{count, plural, one {# bericht in dit gesprek} other {# berichten in dit gesprek}}", + "expand": "Gesprek uitklappen", + "collapse": "Gesprek inklappen", + "loading": "Gesprek laden...", + "mark_read": "Gesprek markeren als gelezen", + "mark_unread": "Gesprek markeren als ongelezen", + "archive": "Gesprek archiveren", + "delete": "Gesprek verwijderen", + "star": "Ster toevoegen aan gesprek", + "unstar": "Ster verwijderen van gesprek", + "toggle_thread": "Gesprek in-/uitvouwen" + }, + "identities": { + "modal_title": "Verzendidentiteiten beheren", + "create_new": "Nieuwe identiteit aanmaken", + "edit_identity": "Identiteit bewerken", + "delete_confirm": "Deze identiteit verwijderen? Dit kan niet ongedaan worden gemaakt.", + "cannot_delete": "Deze identiteit kan niet worden verwijderd", + "primary_identity": "Primair", + "set_as_primary": "Instellen als primair", + "no_identities": "Geen identiteiten gevonden", + "display": { + "reply_to": "Antwoord naar:", + "bcc": "BCC:", + "signature": "Handtekening:", + "preview": "Voorbeeld:" + }, + "validation_errors": { + "invalid_emails": "Ongeldige e-mails: {emails}", + "unknown_error": "Onbekende fout" + }, + "form": { + "name_label": "Weergavenaam", + "name_placeholder": "bijv. Werk-e-mail, Persoonlijk", + "name_required": "Naam is vereist", + "email_label": "E-mailadres", + "email_placeholder": "jouw.email@voorbeeld.nl", + "email_required": "E-mail is vereist", + "email_invalid": "Voer een geldig e-mailadres in", + "email_immutable": "E-mailadres kan niet worden gewijzigd na aanmaak", + "reply_to_label": "Antwoord naar (optioneel)", + "reply_to_placeholder": "ander@email.nl", + "bcc_label": "Automatische BCC (optioneel)", + "bcc_placeholder": "archief@email.nl", + "text_signature_label": "Teksthandtekening", + "html_signature_label": "HTML-handtekening", + "signature_byte_counter": "{bytes} / {max} bytes", + "signature_byte_limit_reached": "Serverlimiet bereikt", + "save": "Identiteit opslaan", + "cancel": "Annuleren", + "creating": "Aanmaken...", + "updating": "Bijwerken..." + }, + "sub_address": { + "button_tooltip": "Sub-adres gebruiken", + "popover_title": "Sub-adrestag toevoegen", + "tag_input_placeholder": "Voer tag in (bijv. winkelen)", + "preview_label": "Voorbeeld:", + "recent_tags": "Recente tags", + "suggested_tags": "Voorgesteld", + "use_address": "Dit adres gebruiken", + "invalid_tag": "Tag mag alleen letters, cijfers en streepjes bevatten", + "tag_too_long": "Tag mag maximaal 30 tekens bevatten", + "help_text": "E-mails verzonden naar gebruiker{delimiter}tag@domein.nl komen in je postvak IN aan", + "validation": { + "empty": "Tag mag niet leeg zijn", + "too_long": "Tag mag maximaal {max} tekens bevatten", + "invalid_chars": "Tag mag alleen letters, cijfers en streepjes bevatten" + } + }, + "badge": { + "sent_via": "via", + "sub_address_tag": "Verzonden met sub-adres: {tag}", + "identity_name": "Verzonden met identiteit: {name}", + "identity_short": "via {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Verwijderen", + "delete_confirm_title": "Identiteit verwijderen" + }, + "templates": { + "picker_title": "Kies een sjabloon", + "search_placeholder": "Sjablonen zoeken...", + "section_favorites": "Favorieten", + "section_recent": "Recent", + "section_uncategorized": "Overig", + "no_templates": "Geen sjablonen", + "no_results": "Geen sjablonen gevonden", + "fill_placeholders": "Plaatshouderwaarden invullen", + "enter_value": "Voer een waarde in...", + "preview": "Voorbeeld", + "insert_with_values": "Invoegen met waarden", + "insert_raw": "Onbewerkt invoegen", + "copy_suffix": "(kopie)", + "placeholder": "Variabele", + "placeholders": { + "recipient_name": "Naam ontvanger", + "company": "Bedrijfsnaam", + "date": "Huidige datum", + "day_of_week": "Dag van de week", + "sender_name": "Uw naam" + } + }, + "contacts": { + "directory": { + "title": "Adresboek van de organisatie", + "search_placeholder": "Zoek naam of e-mailadres", + "refresh": "Adresboek vernieuwen", + "retry": "Opnieuw proberen", + "loading": "Adresboek laden…", + "unavailable_title": "Geen adresboek van de organisatie", + "unavailable_hint": "Je mailserver biedt geen overzicht van accounts.", + "forbidden_title": "Adresboek niet beschikbaar", + "forbidden_hint": "Je mailserver staat dit account niet toe het overzicht te bekijken. Vraag het je beheerder.", + "error_title": "Adresboek kon niet worden geladen", + "error_hint": "Controleer je verbinding en probeer het opnieuw.", + "empty_title": "Het adresboek is leeg", + "empty_hint": "Je mailserver toont geen accounts met een e-mailadres.", + "no_match": "Niemand in het adresboek komt overeen met je zoekopdracht", + "refresh_failed": "Adresboek kon niet worden vernieuwd. De laatst geladen lijst wordt getoond.", + "select_page": "Iedereen op deze pagina selecteren", + "deselect_page": "Iedereen op deze pagina deselecteren", + "select_entry": "{name} selecteren", + "range": "{from}–{to} van {total}", + "page": "Pagina {page} van {count}", + "previous_page": "Vorige pagina", + "next_page": "Volgende pagina", + "picker_title": "Kiezen uit adresboek van de organisatie", + "cancel": "Annuleren", + "selected_count": "{count} geselecteerd", + "add_to": "Toevoegen aan Aan", + "add_cc": "Toevoegen aan Cc", + "add_bcc": "Toevoegen aan Bcc", + "select_hint": "Selecteer iemand om de details te zien", + "write_email": "E-mail schrijven", + "add_to_contacts": "Toevoegen aan contacten", + "email": "E-mail", + "read_only_note": "Deze gegevens komen van je mailserver en kunnen hier niet worden bewerkt.", + "type": { + "individual": "Persoon", + "group": "Groep", + "resource": "Middel", + "location": "Locatie", + "other": "Overig" + } + }, + "title": "Contacten", + "search_placeholder": "Contacten zoeken...", + "create_new": "Nieuw contact", + "no_category": "Geen categorie", + "rename_category": "Categorie hernoemen", + "category_name_label": "Categorienaam", + "category_renamed": "Categorie hernoemd", + "category_rename_failed": "Categorie hernoemen mislukt", + "category_added": "Contact toegevoegd aan {name}", + "category_added_plural": "{count} contacten toegevoegd aan {name}", + "empty_state": "Geen contacten", + "empty_state_title": "Geen contacten", + "empty_state_subtitle": "Maak uw eerste contact aan of importeer vanuit een vCard-bestand", + "empty_search": "Geen contacten gevonden", + "empty_search_hint": "Probeer een andere zoekterm", + "empty_filtered": "Geen contacten voldoen aan uw filters", + "empty_filtered_hint": "Pas filters aan of wis ze", + "clear_search": "Zoekopdracht wissen", + "import_vcard": "vCard importeren", + "delete_confirm_title": "Contact verwijderen", + "delete_confirm": "Weet u zeker dat u dit contact wilt verwijderen?", + "local_mode": "Contacten worden lokaal opgeslagen (server ondersteunt geen JMAP Contacts)", + "back_to_contacts": "Terug naar contacten", + "back_to_email": "Terug naar e-mail", + "tabs": { + "all": "Alle", + "groups": "Groepen" + }, + "shared": { + "title": "Gedeeld" + }, + "address_books": { + "create": "Nieuw adresboek", + "created": "Adresboek aangemaakt", + "create_failed": "Kan adresboek niet aanmaken", + "title": "Mijn Adresboeken", + "shared_prefix": "Gedeeld: {name}", + "moved": "Contact verplaatst naar {name}", + "moved_plural": "{count} contacten verplaatst naar {name}", + "move_failed": "Verplaatsen van contact mislukt", + "address_book": "Adresboek", + "rename": "Adresboek hernoemen", + "name_label": "Naam van adresboek", + "renamed": "Adresboek hernoemd", + "rename_failed": "Adresboek hernoemen mislukt", + "default": "Standaard", + "set_default": "Als standaard instellen", + "default_updated": "Standaardadresboek bijgewerkt", + "set_default_failed": "Standaardadresboek instellen mislukt", + "manage": "Adresboeken beheren", + "share": "Adresboek delen", + "new_contact_in_book": "Nieuw contact in dit adresboek", + "delete": "Adresboek verwijderen", + "confirm_delete": "\"{name}\" verwijderen? Alle contacten in dit adresboek worden verwijderd.", + "deleted": "Adresboek verwijderd", + "delete_failed": "Adresboek kon niet worden verwijderd" + }, + "detail": { + "emails": "E-mailadressen", + "phones": "Telefoonnummers", + "organizations": "Organisaties", + "addresses": "Adressen", + "notes": "Notities", + "titles": "Titels en rollen", + "online_services": "Online diensten", + "anniversaries": "Jubilea", + "personal_info": "Persoonlijke informatie", + "languages": "Talen", + "categories": "Categorieën", + "related_contacts": "Gerelateerde contacten", + "crypto_keys": "Cryptografische sleutels", + "no_contact_selected": "Selecteer een contact om details te bekijken", + "compose_email": "E-mail schrijven", + "copy_email": "E-mail kopiëren", + "copy_phone": "Telefoonnummer kopiëren", + "copy_url": "URL kopiëren", + "copied": "Gekopieerd naar klembord", + "copy_failed": "Kopiëren naar klembord mislukt", + "created": "Aangemaakt", + "updated": "Laatst bijgewerkt", + "timezone": "Tijdzone", + "anniversary_birth": "Verjaardag", + "anniversary_death": "Overleden", + "anniversary_wedding": "Trouwdag", + "anniversary_other": "Overig", + "personal_expertise": "Expertise", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Overig", + "gender": "Geslacht", + "gender_masculine": "Man", + "gender_feminine": "Vrouw", + "gender_other": "Anders", + "gender_none": "Niet van toepassing", + "gender_unknown": "Onbekend", + "calendar": "Kalender", + "calendar_uri": "Kalender-URL", + "scheduling_uri": "Planning-URL", + "freebusy_uri": "Beschikbaarheid-URL", + "cert_issuer": "Uitgever", + "cert_expires": "Verloopt", + "cert_expired": "Verlopen", + "cert_fingerprint": "Vingerafdruk", + "cert_algorithm": "Algoritme", + "import_to_smime": "Importeren naar S/MIME", + "cert_already_imported": "Certificaat is al geïmporteerd", + "cert_imported": "Certificaat geïmporteerd", + "cert_import_failed": "Importeren van certificaat mislukt", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Meer acties", + "age_years": "{count, plural, one {1 jaar} other {# jaar}}", + "years_since": "{count, plural, one {1 jaar geleden} other {# jaar geleden}}" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "Nieuw contact", + "edit_title": "Contact bewerken", + "section_address_book": "Adresboek", + "select_address_book": "Selecteer een adresboek...", + "section_identity": "Naam en identiteit", + "contact_type": "Contacttype", + "type_person": "Persoon", + "type_organization": "Organisatie", + "section_work": "Werk en organisatie", + "prefix": "Voorvoegsel", + "prefix_placeholder": "Dr., Dhr., Mevr.", + "given_name": "Voornaam", + "middle_name": "Tweede naam", + "surname": "Achternaam", + "suffix": "Achtervoegsel", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Bijnaam", + "nickname_placeholder": "Bijnaam", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Telefoon", + "phone_placeholder": "+31 20 123 4567", + "phone_type": "Type", + "phone_voice": "Spraak", + "phone_cell": "Mobiel", + "phone_fax": "Fax", + "phone_pager": "Pieper", + "phone_video": "Video", + "phone_text": "SMS", + "organization": "Organisatie", + "organization_placeholder": "Bedrijfsnaam", + "department": "Afdeling", + "department_placeholder": "Afdeling", + "job_title": "Functietitel", + "job_title_placeholder": "bijv. Software-engineer", + "role": "Rol", + "role_placeholder": "bijv. Teamleider", + "addresses": "Adressen", + "add_address": "Adres toevoegen", + "street": "Straat", + "city": "Stad", + "region": "Provincie / Regio", + "postcode": "Postcode", + "country": "Land", + "online_services": "Online diensten", + "add_online_service": "Online dienst toevoegen", + "url_placeholder": "https://...", + "service_placeholder": "Dienst", + "anniversaries": "Jubilea", + "add_anniversary": "Datum toevoegen", + "anniversary_birth": "Verjaardag", + "anniversary_wedding": "Trouwdag", + "anniversary_death": "Herdenking", + "anniversary_other": "Overig", + "personal_info": "Persoonlijke informatie", + "add_personal_info": "Item toevoegen", + "personal_info_placeholder": "bijv. Fotografie", + "personal_expertise": "Expertise", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Overig", + "level": "Niveau", + "level_high": "Hoog", + "level_medium": "Gemiddeld", + "level_low": "Laag", + "categories": "Categorieën", + "categories_placeholder": "bijv. Familie, Vrienden, Collega's", + "categories_hint": "Typ om te zoeken of toe te voegen", + "category_add": "Toevoegen", + "note": "Notities", + "note_placeholder": "Notitie toevoegen...", + "gender": "Geslacht", + "gender_sex": "Geslacht", + "gender_male": "Man", + "gender_female": "Vrouw", + "gender_other": "Anders", + "gender_none": "Niet van toepassing", + "gender_unknown": "Onbekend", + "gender_identity": "Genderidentiteit", + "gender_identity_placeholder": "Genderidentiteit...", + "calendar": "Kalender", + "calendar_uri": "Kalender-URL", + "scheduling_uri": "Planning-URL", + "freebusy_uri": "Beschikbaarheid-URL", + "context_work": "Werk", + "context_private": "Privé", + "add_email": "E-mail toevoegen", + "add_phone": "Telefoon toevoegen", + "save": "Opslaan", + "cancel": "Annuleren", + "creating": "Aanmaken...", + "updating": "Bijwerken...", + "name_required": "Voer een voornaam, achternaam of organisatie in", + "email_invalid": "Voer een geldig e-mailadres in", + "email_error_inline": "Ongeldig e-mailformaat", + "save_failed": "Kon contact niet opslaan", + "delete": "Verwijderen", + "upload_photo": "Foto uploaden", + "remove_photo": "Foto verwijderen", + "photo_hint": "JPG of PNG, tot 10 MB. Wordt verkleind.", + "photo_too_large": "Afbeelding is te groot (max 10 MB)", + "photo_invalid": "Ongeldig afbeeldingsbestand", + "change_photo": "Wijzigen" + }, + "groups": { + "create": "Nieuwe groep", + "edit": "Groep bewerken", + "empty": "Geen groepen", + "delete_confirm_title": "Groep verwijderen", + "delete_confirm": "Weet u zeker dat u deze groep wilt verwijderen?", + "name_label": "Groepsnaam", + "name_placeholder": "bijv. Team, Familie", + "name_required": "Groepsnaam is vereist", + "save_failed": "Kon groep niet opslaan", + "members_label": "Leden", + "search_members": "Contacten zoeken om toe te voegen...", + "no_members": "Geen leden in deze groep", + "member_count": "{count, plural, =0 {Geen leden} one {1 lid} other {# leden}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Contacten importeren", + "drop_hint": "Klik om een vCard-bestand te selecteren", + "file_types": ".vcf- of .vcard-bestanden", + "no_contacts": "Geen contacten gevonden in bestand", + "parse_error": "Kon vCard-bestand niet lezen", + "found": "{count, plural, one {1 contact gevonden} other {# contacten gevonden}}", + "duplicate": "Duplicaat", + "select_all": "Alles selecteren", + "deselect_all": "Alles deselecteren", + "selected": "{count, plural, one {1 geselecteerd} other {# geselecteerd}}", + "import_button": "Importeren", + "importing": "Importeren...", + "success": "{count, plural, one {1 contact geïmporteerd} other {# contacten geïmporteerd}}", + "failed": "Import mislukt", + "close": "Sluiten", + "file_too_large": "Bestand is te groot (max 5 MB)" + }, + "export": { + "title": "Contacten exporteren", + "success": "{count, plural, one {1 contact geëxporteerd} other {# contacten geëxporteerd}}" + }, + "bulk": { + "selected": "{count, plural, one {1 geselecteerd} other {# geselecteerd}}", + "select_all": "Alles selecteren", + "delete": "Verwijderen", + "delete_confirm_title": "Contacten verwijderen", + "delete_confirm": "{count, plural, one {1 contact} other {# contacten}} verwijderen?", + "deleted": "{count, plural, one {1 contact verwijderd} other {# contacten verwijderd}}", + "add_to_group": "Aan groep toevoegen", + "choose_group": "Kies een groep", + "adding_contacts": "{count, plural, one {1 contact} other {# contacten}} toevoegen", + "added_to_group": "Contacten aan groep toegevoegd", + "export": "Exporteren", + "clear": "Selectie wissen" + }, + "toast": { + "created": "Contact aangemaakt", + "updated": "Contact bijgewerkt", + "deleted": "Contact verwijderd", + "error_create": "Kon contact niet aanmaken", + "error_update": "Kon contact niet bijwerken", + "error_delete": "Kon contact niet verwijderen" + }, + "context_menu": { + "open": "Openen", + "edit": "Bewerken", + "send_email": "E-mail verzenden", + "add_to_group": "Aan groep toevoegen", + "export_vcard": "Exporteren als vCard", + "delete": "Verwijderen", + "call": "Bellen", + "duplicate": "Dupliceren", + "print": "Afdrukken" + }, + "filters": { + "toggle": "Filters", + "select": "Selecteren", + "clear": "Wissen", + "close": "Sluiten", + "title": "Geavanceerde filters", + "organization": "Bedrijf", + "organization_placeholder": "bijv. Acme", + "job_title": "Functie", + "job_title_placeholder": "bijv. Ontwerper", + "location": "Locatie", + "location_placeholder": "Stad of land", + "email_domain": "E-maildomein", + "email_domain_placeholder": "voorbeeld.nl", + "birthday_month": "Verjaardag in", + "any_month": "Elke maand", + "has_email": "Met e-mail", + "has_phone": "Met telefoon", + "has_photo": "Met foto", + "sort_by": "Sorteren op", + "sort_first_name": "Voornaam", + "sort_last_name": "Achternaam" + }, + "open_categories": "Categorieën openen" + }, + "calendar": { + "title": "Agenda", + "back_to_email": "Terug naar e-mail", + "back_to_month": "Terug naar maand", + "my_calendars": "Mijn agenda's", + "birthday_calendar": "Verjaardagen", + "mini_calendar_change": "Klik om van maand te wisselen", + "views": { + "month": "Maand", + "week": "Week", + "day": "Dag", + "agenda": "Agenda", + "today": "Vandaag", + "month_hint": "Maand (m)", + "week_hint": "Week (w)", + "day_hint": "Dag (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Taken", + "tasks_hint": "Taken (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# eerder evenement} other {# eerdere evenementen}}", + "hidden_after": "{count, plural, one {# later evenement} other {# latere evenementen}}", + "show_all_hours": "Alle uren tonen", + "show_display_hours": "Alleen {start} – {end} tonen", + "create": "Evenement aanmaken", + "edit": "Evenement bewerken", + "delete": "Evenement verwijderen", + "details": "Evenementdetails", + "no_events": "Geen evenementen", + "all_day": "Hele dag", + "more": "+{count} meer", + "show_more": "Meer tonen", + "show_less": "Minder tonen", + "no_title": "(Geen titel)", + "resize": "Evenement formaat wijzigen", + "duplicate": "Dupliceren", + "today_header": "Vandaag", + "tomorrow_header": "Morgen", + "agenda_show_earlier": "Eerdere afspraken tonen", + "agenda_loading": "Laden…", + "agenda_range_start": "Afspraken vanaf {date}", + "agenda_range_end": "Afspraken tot {date}", + "export_ics": "Exporteren als .ics", + "copy_title": "Titel kopiëren", + "copy_link": "Vergaderlink kopiëren", + "go_to_today": "Ga naar vandaag", + "new_all_day_event": "Nieuwe dagvullende afspraak", + "new_event": "Nieuwe afspraak", + "new_task": "Nieuwe taak" + }, + "detail": { + "add_note": "Notitie toevoegen...", + "save_note": "Opslaan", + "note_saved": "Notitie toegevoegd", + "open_link": "Link openen", + "copy_location": "Locatie kopiëren", + "meeting_link": "Vergaderlink", + "tentative": "Voorlopig", + "cancelled": "Geannuleerd", + "delete_confirm": "Dit evenement verwijderen?" + }, + "form": { + "title": "Titel", + "description": "Beschrijving", + "location": "Locatie", + "meeting_link": "Vergaderlink", + "start_date": "Startdatum", + "end_date": "Einddatum", + "start_time": "Starttijd", + "end_time": "Eindtijd", + "all_day_event": "Hele dag evenement", + "calendar_select": "Agenda", + "save": "Opslaan", + "cancel": "Annuleren", + "delete_confirm": "Weet je zeker dat je dit evenement wilt verwijderen?", + "color": "Kleur" + }, + "participants": { + "availability": { + "title": "Beschikbaarheid", + "free": "Beschikbaar", + "busy": "Bezet", + "tentative": "Voorlopig", + "unknown": "Geen gebruiker op deze server", + "checking": "Controleren…" + }, + "title": "Deelnemers", + "add": "Deelnemer toevoegen", + "organizer": "Organisator", + "attendee": "Deelnemer", + "accepted": "Geaccepteerd", + "declined": "Geweigerd", + "tentative": "Voorlopig", + "needs_action": "Reactie vereist", + "remove": "Verwijderen", + "edit": "Bewerken", + "email_placeholder": "E-mailadres toevoegen of contacten zoeken", + "send_invitations": "Uitnodigingen sturen naar deelnemers", + "status_summary": "{accepted} geaccepteerd, {pending} in afwachting", + "invited_by": "Uitnodiging van {name}", + "respond_below": "Reageer met de knoppen hieronder", + "rsvp_label": "Uw reactie", + "cancel_notification": "Deelnemers worden op de hoogte gebracht van de annulering", + "you_organizer": "Je bent de organisator", + "you_attendee": "Je bent een deelnemer", + "no_participants": "Geen deelnemers", + "count": "{count, plural, one {# deelnemer} other {# deelnemers}}" + }, + "recurrence": { + "title": "Herhaling", + "none": "Wordt niet herhaald", + "daily": "Dagelijks", + "weekly": "Wekelijks", + "monthly": "Maandelijks", + "yearly": "Jaarlijks", + "every_n_days": "Elke {count} dagen", + "every_n_weeks": "Elke {count} weken", + "every_n_months": "Elke {count} maanden", + "until": "Tot", + "occurrences": "{count} herhalingen", + "custom": "Aangepast…", + "edit_custom": "Aangepaste herhaling bewerken", + "every_n_years": "Elke {count} jaar", + "on_days": "op {days}", + "on_day_n": "op dag {day}", + "on_the_nth": "op de {nth} {day}", + "in_month": "in {month}", + "nth_1": "eerste", + "nth_2": "tweede", + "nth_3": "derde", + "nth_4": "vierde", + "nth_last": "laatste", + "editor_freq_day": "Dag", + "editor_freq_week": "Week", + "editor_freq_month": "Maand", + "editor_freq_year": "Jaar", + "editor_repeats_on": "Herhaalt zich", + "editor_every": "Elke", + "editor_unit_days": "dag(en)", + "editor_unit_weeks": "week/weken", + "editor_unit_months": "maand(en)", + "editor_unit_years": "jaar/jaren", + "editor_on_day": "op dag", + "editor_on_the": "op de", + "editor_in": "in", + "editor_ends": "Eindigt", + "editor_never": "Nooit", + "editor_ends_on": "Op", + "editor_ends_after": "Na", + "editor_occurrences": "herhalingen" + }, + "recurrence_scope": { + "edit_title": "Terugkerend evenement bewerken", + "delete_title": "Terugkerend evenement verwijderen", + "description": "Dit is een terugkerend evenement. Welke evenementen wilt u wijzigen?", + "this_event": "Alleen dit evenement", + "this_and_future": "Dit en volgende evenementen", + "all_events": "Alle evenementen", + "cancel": "Annuleren", + "save": "Opslaan", + "delete": "Verwijderen", + "rsvp_title": "Reageren op terugkerend evenement", + "rsvp_description": "Dit is een terugkerend evenement. Voor welke evenementen geldt uw reactie?", + "respond": "Reageren" + }, + "alerts": { + "title": "Herinnering", + "none": "Geen herinnering", + "at_time": "Op het tijdstip van het evenement", + "minutes_before": "{count, plural, one {# minuut van tevoren} other {# minuten van tevoren}}", + "hours_before": "{count, plural, one {# uur van tevoren} other {# uur van tevoren}}", + "days_before": "{count, plural, one {# dag van tevoren} other {# dagen van tevoren}}", + "weeks_before": "{count, plural, one {# week van tevoren} other {# weken van tevoren}}", + "unit_minutes_before": "minuten van tevoren", + "unit_hours_before": "uur van tevoren", + "unit_days_before": "dagen van tevoren", + "unit_weeks_before": "weken van tevoren", + "add": "Herinnering toevoegen", + "remove": "Herinnering verwijderen", + "amount": "Herinneringshoeveelheid", + "unit": "Herinneringseenheid", + "action": "Soort herinnering", + "action_display": "Melding", + "action_email": "E-mail", + "by_email": "{label} per e-mail", + "kept": "{count, plural, one {# andere herinnering die hier niet kan worden bewerkt, blijft behouden} other {# andere herinneringen die hier niet kunnen worden bewerkt, blijven behouden}}", + "other": "{count, plural, one {# andere herinnering} other {# andere herinneringen}}", + "calendar_defaults": "Standaardherinneringen van de agenda", + "customize": "Aanpassen", + "use_defaults": "Standaardherinneringen van de agenda gebruiken" + }, + "settings": { + "limit_hours": "Zichtbare uren beperken", + "limit_hours_desc": "Toon alleen deze uren in de dag- en weekweergave. Evenementen daarbuiten blijven bereikbaar via de pijlen boven en onder elke dag.", + "visible_hours": "Zichtbare uren", + "visible_hours_start": "Begin van de zichtbare uren", + "visible_hours_end": "Einde van de zichtbare uren", + "hide_non_working_days": "Niet-werkdagen verbergen", + "hide_non_working_days_desc": "Laat de dagen waarop je niet werkt weg uit de weekweergave. Hun evenementen blijven zichtbaar in de maand-, dag- en agendaweergave.", + "working_days": "Werkdagen", + "title": "Agenda-instellingen", + "default_view": "Standaardweergave", + "week_starts_on": "Week begint op", + "time_format": "Tijdnotatie", + "default_calendar": "Standaardagenda", + "default_reminder": "Standaardherinnering", + "time_format_12h": "12-uurs", + "time_format_24h": "24-uurs", + "notifications_enabled": "Evenementmeldingen", + "notifications_enabled_desc": "Meldingen weergeven voor aankomende evenementen", + "notification_sound": "Meldingsgeluid", + "notification_sound_desc": "Geluid afspelen voor agendameldingen", + "invitation_parsing": "E-mailuitnodigingen verwerken", + "invitation_parsing_desc": "Kalenderuitnodigingen in e-mailbijlagen detecteren en kalenderacties tonen", + "show_time_in_month_view": "Tijd weergeven in maandweergave", + "show_time_in_month_view_desc": "Evenementtijden weergeven in de maandkalenderweergave", + "show_week_numbers": "Weeknummers weergeven", + "show_week_numbers_desc": "Weeknummers weergeven in de minikalender", + "calendar_free_scroll": "Vrij scrollen", + "calendar_free_scroll_desc": "Doorlopend door maanden, weken en dagen scrollen in plaats van één periode tegelijk", + "enable_tasks": "Taken inschakelen", + "enable_tasks_desc": "Een takenweergave in de agenda tonen om to-do's te beheren", + "show_tasks_on_calendar": "Taken in agenda tonen", + "show_tasks_on_calendar_desc": "Taken tonen in de maand-, week- en dagweergave van de agenda", + "hover_preview": "Voorvertoning bij hover", + "hover_preview_desc": "Toon een detailpopover bij het hoveren over agenda-evenementen", + "hover_preview_instant": "Direct", + "hover_preview_delay_500ms": "0,5 seconde vertraging", + "hover_preview_delay_1s": "1 seconde vertraging", + "hover_preview_delay_2s": "2 seconden vertraging", + "hover_preview_off": "Uitgeschakeld", + "organizer_identity": "Uitnodigingen organiseren als", + "organizer_identity_desc": "Het agenda-adres dat als organisator wordt gebruikt wanneer je mensen uitnodigt voor afspraken.", + "organizer_identity_failed": "Kon het organisatoradres niet wijzigen", + "show_birthday_calendar": "Verjaardagskalender", + "show_birthday_calendar_desc": "Toon een virtuele kalender met verjaardagen van je contacten" + }, + "days": { + "monday": "Maandag", + "tuesday": "Dinsdag", + "wednesday": "Woensdag", + "thursday": "Donderdag", + "friday": "Vrijdag", + "saturday": "Zaterdag", + "sunday": "Zondag", + "mon": "Ma", + "tue": "Di", + "wed": "Wo", + "thu": "Do", + "fri": "Vr", + "sat": "Za", + "sun": "Zo" + }, + "notifications": { + "event_created": "Evenement aangemaakt", + "event_updated": "Evenement bijgewerkt", + "event_deleted": "Evenement verwijderd", + "calendar_created": "Agenda aangemaakt", + "calendar_deleted": "Agenda verwijderd", + "event_move_error": "Evenement verplaatsen mislukt", + "event_resize_error": "Evenement formaat wijzigen mislukt", + "alert_title": "Aankomend evenement", + "alert_now": "Begint nu", + "alert_in_minutes": "Over {count} min", + "invitation_sent": "Uitnodigingen verzonden", + "invitations_denied_title": "Uitnodigingen kunnen niet worden verzonden", + "invitations_denied": "De server weigerde de uitnodigingen te verzenden: {reason}. Het evenement opslaan zonder ze te verzenden?", + "save_without_invitations": "Opslaan zonder uitnodigingen", + "rsvp_updated": "Reactie bijgewerkt", + "rsvp_error": "Reactie kon niet worden bijgewerkt", + "event_duplicated": "Evenement gedupliceerd", + "event_error": "Evenement opslaan mislukt", + "task_due": "Taak vervalt", + "event_exported": "Afspraak geëxporteerd", + "title_copied": "Titel gekopieerd", + "link_copied": "Link gekopieerd" + }, + "status": { + "loading_calendars": "Agenda's laden...", + "loading_events": "Evenementen laden..." + }, + "quick_create": { + "placeholder": "Titel nieuw evenement", + "aria_label": "Snel evenement aanmaken", + "add_title": "Titel toevoegen", + "more_options": "Meer opties" + }, + "nav_prev": "Vorige", + "nav_next": "Volgende", + "import": { + "title": "Agenda importeren", + "tab_file": "Bestand", + "tab_url": "URL", + "select_file": "Selecteer .ics-bestand", + "drop_file": "of sleep het bestand hierheen", + "supported_formats": "iCalendar (.ics) bestanden worden ondersteund", + "url_description": "Voer de URL in van een externe iCalendar (.ics) feed om evenementen te importeren.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Ondersteunt iCalendar (.ics) URLs", + "fetch": "Ophalen", + "invalid_url": "Voer een geldige URL in", + "url_fetch_failed": "Kan agenda niet ophalen van URL", + "parsing": "Agendabestand wordt verwerkt...", + "parsed_events": "{count} evenementen gevonden", + "no_events": "Geen evenementen gevonden in bestand", + "select_all": "Alles selecteren", + "deselect_all": "Alles deselecteren", + "target_calendar": "Importeren in agenda", + "import_button": "Selectie importeren", + "importing": "Evenementen importeren...", + "success": "{count} evenementen succesvol geïmporteerd", + "error": "Agenda importeren mislukt", + "file_too_large": "Bestand overschrijdt de limiet van 10 MB", + "invalid_format": "Ongeldig agendabestandsformaat" + }, + "webcal_action": { + "title": "Agendalink openen", + "description": "Hoe wilt u \"{name}\" gebruiken?", + "import_title": "Eenmalig importeren", + "import_description": "Haal de afspraken nu op en kopieer ze naar een van uw agenda's.", + "subscribe_title": "Abonneren", + "subscribe_description": "Houd deze agenda automatisch gesynchroniseerd als aparte agenda.", + "cancel": "Annuleren" + }, + "management": { + "title": "Agendabeheer", + "description": "Maak, hernoem en pas uw agenda's aan. Klik met de rechtermuisknop op een agenda in de zijbalk om snel de kleur te wijzigen.", + "name": "Naam", + "name_placeholder": "Agendanaam", + "color": "Kleur", + "change_color": "Kleur wijzigen", + "random_color": "Nieuwe willekeurige kleur", + "add_calendar": "Agenda toevoegen", + "edit": "Bewerken", + "delete": "Verwijderen", + "save": "Opslaan", + "create": "Aanmaken", + "cancel": "Annuleren", + "default": "Standaard", + "confirm_delete": "\"{name}\" verwijderen? Alle afspraken in deze agenda worden verwijderd.", + "calendar_created": "Agenda aangemaakt", + "calendar_updated": "Agenda bijgewerkt", + "calendar_deleted": "Agenda verwijderd", + "color_updated": "Agendakleur bijgewerkt", + "error_create": "Agenda aanmaken mislukt", + "error_update": "Agenda bijwerken mislukt", + "error_delete": "Agenda verwijderen mislukt", + "caldav_url": "CalDAV-URL", + "copy_url": "CalDAV-URL kopiëren", + "url_copied": "CalDAV-URL gekopieerd naar klembord", + "confirm_clear": "Alle afspraken uit \"{name}\" verwijderen? Dit kan niet ongedaan worden gemaakt.", + "set_default": "Als standaard instellen", + "default_updated": "Standaardagenda bijgewerkt", + "error_default": "Standaardagenda instellen mislukt", + "clear_events": "Afspraken verwijderen", + "events_cleared": "{count} afspraken verwijderd", + "error_clear": "Kan agendagebeurtenissen niet verwijderen", + "share": "Agenda delen", + "new_event_in_calendar": "Nieuwe afspraak in deze agenda", + "shared_with": "Gedeeld met {names}", + "shared_with_count": "{count, plural, one {Gedeeld met 1 persoon} other {Gedeeld met # personen}}", + "move_up": "Omhoog verplaatsen", + "move_down": "Omlaag verplaatsen", + "kind": "Inhoud", + "kind_events": "Afspraken", + "kind_tasks": "Taken", + "kind_both": "Afspraken en taken", + "kind_hint": "Bepaalt welke apps deze agenda zien wanneer die via CalDAV wordt gesynchroniseerd. Dit kan later niet meer worden gewijzigd.", + "default_alerts": "Standaardherinneringen", + "default_alerts_title": "Standaardherinneringen voor \"{name}\"", + "default_alerts_hint": "Nieuwe afspraken in deze agenda krijgen deze herinneringen, tenzij ze in de afspraak worden gewijzigd. Wijzigingen hier hebben geen invloed op bestaande afspraken. E-mailherinneringen worden door de mailserver verstuurd.", + "default_alerts_timed": "Afspraken met een tijd", + "default_alerts_all_day": "Afspraken voor de hele dag", + "default_alerts_updated": "Standaardherinneringen bijgewerkt" + }, + "subscription": { + "title": "iCal-abonnement", + "section_title": "iCal-abonnementen", + "description": "Abonneer op een externe iCalendar-feed. Afspraken worden automatisch gesynchroniseerd in een eigen agenda. Ondersteunt https://- en webcal://-URL's.", + "url_label": "Agenda-URL", + "url_placeholder": "https://example.com/calendar.ics of webcal://...", + "name_label": "Agendanaam", + "name_placeholder": "bijv. Feestdagen", + "color_label": "Kleur", + "refresh_interval": "Verversingsinterval", + "interval_15": "Elke 15 minuten", + "interval_30": "Elke 30 minuten", + "interval_60": "Elk uur", + "interval_360": "Elke 6 uur", + "interval_1440": "Elke dag", + "subscribe": "Abonneren", + "subscribing": "Bezig met abonneren...", + "save": "Wijzigingen opslaan", + "saving": "Opslaan...", + "edit": "Bewerken", + "edit_title": "Abonnement bewerken", + "updated": "\"{name}\" bijgewerkt", + "update_error": "Abonnement kon niet worden bijgewerkt", + "invalid_url": "Voer een geldige URL in", + "success": "Geabonneerd op \"{name}\"", + "error": "Kan abonnement niet toevoegen", + "refresh": "Nu vernieuwen", + "refresh_success": "Abonnement vernieuwd", + "refresh_error": "Kan abonnement niet vernieuwen", + "unsubscribe": "Afmelden", + "confirm_delete": "Afmelden van \"{name}\"? De agenda en alle afspraken worden verwijderd.", + "deleted": "Abonnement verwijderd", + "delete_error": "Kan abonnement niet verwijderen", + "last_refreshed": "Laatst bijgewerkt: {time}" + }, + "tasks": { + "label": "Taken", + "no_tasks": "Geen taken", + "no_title": "(Geen titel)", + "mark_complete": "Markeren als voltooid", + "mark_incomplete": "Markeren als onvoltooid", + "filter_all": "Alle", + "filter_pending": "Openstaand", + "filter_completed": "Voltooid", + "filter_overdue": "Achterstallig", + "show_completed": "Voltooide tonen", + "create": "Nieuwe taak", + "edit": "Taak bewerken", + "title_placeholder": "Taaktitel", + "description_placeholder": "Voeg een beschrijving toe...", + "due_date": "Vervaldatum", + "include_time": "Tijd opnemen", + "priority": "Prioriteit", + "priority_none": "Geen", + "priority_high": "Hoog", + "priority_medium": "Middel", + "priority_low": "Laag", + "progress": "Status", + "progress_needs_action": "Actie vereist", + "progress_in_process": "In uitvoering", + "progress_completed": "Voltooid", + "progress_cancelled": "Geannuleerd", + "calendar": "Agenda", + "alert": "Herinnering", + "alert_none": "Geen", + "alert_at_time": "Op vervaldatum en -tijd", + "alert_5min": "5 minuten van tevoren", + "alert_15min": "15 minuten van tevoren", + "alert_30min": "30 minuten van tevoren", + "alert_1hr": "1 uur van tevoren", + "alert_1day": "1 dag van tevoren", + "delete": "Verwijderen", + "cancel": "Annuleren", + "save": "Opslaan", + "quick_add_placeholder": "Taak toevoegen...", + "due_today": "Vervalt vandaag", + "due_tomorrow": "Vervalt morgen", + "overdue": "Achterstallig" + }, + "months": { + "jan": "jan", + "feb": "feb", + "mar": "mrt", + "apr": "apr", + "may": "mei", + "jun": "jun", + "jul": "jul", + "aug": "aug", + "sep": "sep", + "oct": "okt", + "nov": "nov", + "dec": "dec", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Menu openen", + "toolbar": { + "more": "Meer acties", + "select_view": "Weergave wijzigen, nu {view}" + }, + "other_calendars": "Andere agenda's", + "calendar_options": "Opties voor {name}" + }, + "advanced_search": { + "title": "Geavanceerd zoeken", + "from": "Van", + "from_placeholder": "E-mail of naam afzender", + "to": "Aan", + "to_placeholder": "E-mail of naam ontvanger", + "subject": "Onderwerp", + "subject_placeholder": "Onderwerp bevat...", + "body": "Inhoud", + "body_placeholder": "Inhoud bevat...", + "folder": "Map", + "all_folders": "Alle mappen", + "all_folders_except_spam_trash": "Alle mappen behalve Spam en Prullenbak", + "has_attachment": "Bijlagen", + "date_after": "Na", + "date_before": "Voor", + "size_min": "Groter dan (KB)", + "size_max": "Kleiner dan (KB)", + "starred": "Met ster", + "unread": "Ongelezen", + "read": "Gelezen", + "yes": "Ja", + "no": "Nee", + "clear": "Wissen", + "clear_all": "Alles wissen", + "filters_active": "{count} filter", + "filters_active_plural": "{count} filters", + "toggle_filters": "Filters", + "search_hint": "Gebruik geavanceerde filters voor een nauwkeurige zoekopdracht", + "advanced_filters_tooltip": "Geavanceerde zoekfilters", + "results_found": "{count, plural, =0 {Geen resultaten gevonden} one {# resultaat gevonden} other {# resultaten gevonden}}", + "results_found_more": "{count}+ resultaten gevonden", + "suggestions_recent": "Recente zoekopdrachten", + "suggestions_people": "Personen", + "suggestions_from": "E-mails van {address} zoeken", + "suggestions_to": "E-mails aan {address} zoeken", + "suggestions_remove_recent": "Verwijderen uit recente zoekopdrachten", + "has_words": "Bevat de woorden", + "size": "Grootte", + "date_within": "Datum", + "search_in": "Zoeken in", + "has_attachment_check": "Met bijlage", + "search": "Zoeken" + }, + "welcome": { + "title": "Welkom bij uw mailbox", + "tip_compose": "Druk op c om een nieuwe e-mail te schrijven", + "tip_shortcuts": "Druk op ? om alle sneltoetsen te bekijken", + "tip_sidebar": "Vind Agenda, Contacten en Instellingen in het zijmenu", + "tip_settings": "Pas uw ervaring aan in Instellingen", + "tip_compose_touch": "Tik op de potloodknop om een nieuwe e-mail te schrijven", + "tip_sidebar_touch": "Agenda, Contacten en Instellingen vind je in de balk onderaan", + "got_it": "Begrepen", + "settings": "Instellingen", + "dismiss": "Sluiten", + "start_tour": "Tour starten" + }, + "demo_welcome": { + "title": "Welkom bij Bulwark Mail", + "description": "Ontdek een volwaardige webmail-client - rechtstreeks in je browser. Alle gegevens blijven op je apparaat, dus test gerust alles.", + "feature_email": "E-mails lezen en schrijven", + "feature_organize": "Tags, sterren en mappen", + "feature_shortcuts": "Sneltoetsen", + "feature_privacy": "100% privédemo", + "hint": "Klik links op een e-mail om te beginnen, of start de tour." + }, + "files": { + "office_edit": "Document bewerken", + "office_loading": "Documenteditor wordt geopend…", + "office_error": "De documenteditor kon niet worden geopend.", + "office_close": "Editor sluiten", + "open_in_new_tab": "Openen in nieuw tabblad", + "title": "Bestanden", + "search_placeholder": "Bestanden zoeken...", + "empty_state_title": "Nog geen bestanden", + "upload_files": "Bestanden uploaden", + "new_folder": "Nieuwe map", + "new_folder_name": "Mapnaam", + "rename": "Hernoemen", + "rename_title": "Hernoemen", + "new_name": "Nieuwe naam", + "name_forbidden_chars": "De server staat deze tekens niet toe in namen: {chars}", + "name_reserved": "Deze naam is gereserveerd op de server.", + "delete": "Verwijderen", + "delete_confirm_title": "Bron verwijderen", + "delete_confirm_message": "Weet u zeker dat u \"{name}\" wilt verwijderen? Dit kan niet ongedaan worden gemaakt.", + "download": "Downloaden", + "name": "Naam", + "size": "Grootte", + "modified": "Gewijzigd", + "type": "Type", + "folder": "Map", + "file": "Bestand", + "uploading": "Uploaden...", + "upload_success": "{count, plural, one {1 bestand geüpload} other {# bestanden geüpload}}", + "upload_canceled": "Uploaden geannuleerd", + "upload_canceled_partial": "{count, plural, one {1 bestand was} other {# bestanden waren}} al geüpload voordat u annuleerde", + "upload_error": "Bestand uploaden mislukt", + "create_folder_success": "Map aangemaakt", + "create_folder_error": "Map aanmaken mislukt", + "delete_success": "Succesvol verwijderd", + "delete_error": "Verwijderen mislukt", + "rename_success": "Succesvol hernoemd", + "rename_error": "Hernoemen mislukt", + "download_error": "Downloaden mislukt", + "not_available": "Bestandsopslag is niet beschikbaar op deze server", + "cancel": "Annuleren", + "create": "Aanmaken", + "save": "Opslaan", + "no_results": "Geen bestanden komen overeen met uw zoekopdracht", + "batch_delete_confirm_message": "Weet u zeker dat u {count, plural, one {1 item} other {# items}} wilt verwijderen? Dit kan niet ongedaan worden gemaakt.", + "batch_delete_success": "{count, plural, one {1 item verwijderd} other {# items verwijderd}}", + "grid_view": "Rasterweergave", + "list_view": "Lijstweergave", + "details": "Details", + "path": "Pad", + "preview": "Voorbeeld", + "preview_error": "Voorbeeld laden mislukt", + "cut": "Knippen", + "copy": "Kopiëren", + "paste": "Plakken", + "move_success": "{count, plural, one {1 item verplaatst} other {# items verplaatst}}", + "move_error": "Verplaatsen mislukt", + "paste_success": "Succesvol geplakt", + "paste_error": "Plakken mislukt", + "new_text_file": "Nieuw tekstbestand", + "file_name": "Bestandsnaam", + "retry": "Opnieuw proberen", + "refresh": "Vernieuwen", + "duplicate": "Dupliceren", + "duplicate_success": "Succesvol gedupliceerd", + "duplicate_error": "Dupliceren mislukt", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Bestand aangemaakt", + "create_file_error": "Bestand aanmaken mislukt", + "favorites": "Favorieten", + "recent": "Recent", + "show_in_folder": "Weergeven in map", + "remove_from_recent": "Verwijderen uit recent", + "upload_folder": "Map uploaden", + "file_too_large": "\"{name}\" overschrijdt de maximale bestandsgrootte ({max})", + "undo": "Ongedaan maken", + "undo_success": "Actie ongedaan gemaakt", + "undo_error": "Ongedaan maken mislukt", + "toolbar": "Bestandsacties", + "file_list": "Bestanden en mappen", + "context_menu": "Acties", + "settings_ascending": "Oplopend", + "settings_descending": "Aflopend", + "disabled_title": "De bestandsfunctie is uitgeschakeld door uw beheerder", + "disabled_description": "Grote bestandsuploads via WebDAV kunnen Stalwart/RocksDB-instabiliteit veroorzaken, waaronder geheugenfouten en onherstelbaar schijfgebruik. Verwijderde bestanden worden mogelijk niet onmiddellijk uit de opslag verwijderd. Deze functie wordt niet aanbevolen voor productieomgevingen.", + "no_accounts": "Geen gekoppelde accounts.", + "open_folder_tree": "Mappenstructuur openen", + "migration_title": "Je bestanden worden bijgewerkt…", + "migration_description": "Mappen en bestanden worden in de juiste structuur georganiseerd. Dit gebeurt slechts één keer.", + "new": "Nieuw", + "my_files": "Mijn bestanden", + "folders": "Mappen", + "files_heading": "Bestanden", + "open": "Openen", + "star": "Ster toevoegen", + "unstar": "Ster verwijderen", + "expand_folder": "{name} uitvouwen", + "collapse_folder": "{name} samenvouwen", + "sidebar_hint": "Geef mappen een ster en open bestanden om ze hier terug te vinden.", + "breadcrumbs": "Mappad", + "show_path": "Verborgen mappen in het pad tonen", + "selection_actions": "Acties voor geselecteerde items", + "selected_count": "{count, plural, one {1 geselecteerd} other {# geselecteerd}}", + "clear_selection": "Selectie wissen", + "select_all": "Alles selecteren", + "select_item": "{name} selecteren", + "more_actions": "Meer acties voor {name}", + "paste_here": "In deze map plakken", + "paste_count": "{count, plural, one {1 item plakken} other {# items plakken}}", + "sort_by": "Sorteren op", + "view_mode": "Indeling", + "clear_search": "Zoekopdracht wissen", + "no_results_for": "Niets in deze map komt overeen met \"{query}\"", + "download_count": "{count, plural, one {1 bestand downloaden} other {# bestanden downloaden}}", + "delete_count": "{count, plural, one {1 item verwijderen} other {# items verwijderen}}", + "close_details": "Details sluiten", + "file_kind": "{ext}-bestand", + "folder_summary": "{folders, plural, =0 {Geen mappen} one {1 map} other {# mappen}}, {files, plural, =0 {geen bestanden} one {1 bestand} other {# bestanden}}", + "details_hint": "Selecteer een bestand of map om de details te bekijken.", + "sharing": "Delen", + "shared_with_count": "{count, plural, one {Gedeeld met 1 persoon} other {Gedeeld met # personen}}", + "only_you": "Alleen u", + "empty_folder_title": "Deze map is leeg", + "empty_folder_description": "Sleep bestanden hierheen of upload ze vanaf uw apparaat.", + "empty_folder_read_only": "U kunt geen bestanden aan deze map toevoegen.", + "choose_account": "Kies een account", + "drop_to_upload": "Loslaten om te uploaden naar {folder}", + "uploading_count": "{current} van {total} uploaden", + "dismiss": "Sluiten", + "zoom_in": "Inzoomen", + "zoom_out": "Uitzoomen", + "zoom_reset": "Aanpassen aan scherm", + "rotate": "Draaien", + "close_preview": "Sluiten" + }, + "smime": { + "your_certificates": "Uw certificaten", + "your_certificates_desc": "Importeer en beheer uw S/MIME-certificaten voor het ondertekenen en versleutelen van e-mails", + "recipient_certificates": "Certificaten van ontvangers", + "recipient_certificates_desc": "Openbare certificaten om e-mails naar ontvangers te versleutelen", + "identity_bindings": "Koppelingen tussen identiteit en sleutel", + "identity_bindings_desc": "Koppel S/MIME-certificaten aan uw e-mailidentiteiten", + "defaults_title": "Standaardinstellingen", + "defaults_desc": "Stel standaardgedrag voor ondertekenen en versleutelen in", + "import_pkcs12": "PKCS#12 importeren (.p12/.pfx)", + "import_public_cert": "Certificaat importeren", + "no_certificates": "Nog geen certificaten geïmporteerd", + "no_recipient_certs": "Geen ontvangerscertificaten", + "expires": "Verloopt", + "expired": "Verlopen", + "bound_to": "Gekoppeld aan", + "no_key_bound": "Geen", + "lock": "Sleutel vergrendelen", + "unlock": "Sleutel ontgrendelen", + "details": "Details bekijken", + "delete": "Verwijderen", + "encrypt_by_default": "Standaard versleutelen", + "encrypt_by_default_desc": "E-mails automatisch versleutelen wanneer alle ontvangers certificaten hebben", + "remember_unlocked": "Ontgrendelde sleutels onthouden", + "remember_unlocked_desc": "Sleutels ontgrendeld houden gedurende deze browsersessie", + "sign_default_for": "Standaard ondertekenen voor", + "enter_p12_passphrase": "PKCS#12-wachtwoordzin invoeren", + "p12_passphrase_desc": "Voer de wachtwoordzin in die dit certificaatbestand beschermt", + "enter_storage_passphrase": "Opslagwachtwoordzin instellen", + "storage_passphrase_desc": "Kies een wachtwoordzin om deze sleutel in uw browser te beschermen", + "next": "Volgende", + "import": "Importeren", + "unlock_key": "Sleutel ontgrendelen", + "unlock_key_desc": "Voer de opslagwachtwoordzin in om deze sleutel te ontgrendelen voor ondertekenen of ontsleutelen", + "passphrase_placeholder": "Voer wachtwoordzin in", + "confirm_passphrase_placeholder": "Bevestig wachtwoordzin", + "passphrase_mismatch": "Wachtwoordzinnen komen niet overeen", + "cancel": "Annuleren", + "processing": "Bezig met verwerken…", + "close": "Sluiten", + "certificate_details": "Certificaatdetails", + "cert_subject": "Houder", + "cert_issuer": "Uitgever", + "cert_email": "E-mail", + "cert_serial": "Serienummer", + "cert_validity": "Geldigheid", + "cert_fingerprint": "Vingerafdruk (SHA-256)", + "cert_algorithm": "Algoritme", + "cert_capabilities": "Mogelijkheden", + "cert_source": "Bron", + "cert_expired": "Dit certificaat is verlopen", + "cert_not_yet_valid": "Dit certificaat is nog niet geldig", + "cap_sign": "Ondertekenen", + "cap_encrypt": "Versleuteling", + "cap_none": "Geen", + "show_passphrase": "Wachtwoordzin tonen", + "hide_passphrase": "Wachtwoordzin verbergen", + "sign_toggle": "Ondertekenen", + "encrypt_toggle": "Versleutelen", + "missing_recipient_certs": "Ontbrekende certificaten voor: {emails}", + "missing_sender_cert": "Er is geen certificaat gekoppeld aan deze identiteit", + "status_encrypted_ok": "Dit bericht is versleuteld", + "status_encrypted_no_key": "Dit bericht is versleuteld, maar er is geen passende sleutel gevonden", + "status_encrypted_failed": "Dit bericht kon niet worden ontsleuteld", + "status_signed_valid": "Handtekening geverifieerd", + "status_signed_invalid": "Verificatie van de handtekening is mislukt", + "status_signed_expired_cert": "Ondertekend met een verlopen certificaat", + "status_signed_self_signed": "Handtekening geldig, maar het certificaat is zelfondertekend (niet vertrouwd)", + "status_signed_mismatch": "Handtekening is geldig, maar de ondertekenaar komt niet overeen met de afzender", + "status_unsupported": "Niet-ondersteund S/MIME-formaat", + "auto_import_signer_certs": "Ondertekenaarcertificaten automatisch importeren", + "auto_import_signer_certs_desc": "Certificaten van geverifieerde ondertekende e-mails automatisch opslaan voor toekomstige versleuteling", + "export": "Exporteren", + "enter_export_passphrase": "Exportwachtwoordzin instellen", + "export_passphrase_desc": "Kies een wachtwoordzin om het geëxporteerde PKCS#12-bestand te beschermen", + "export_storage_desc": "Voer de opslagwachtwoordzin in om de sleutel voor export te ontsleutelen", + "incorrect_passphrase": "Onjuiste wachtwoordzin" + }, + "tour": { + "step_counter": "Stap {current} van {total}", + "skip": "Tour overslaan", + "back": "Terug", + "next": "Volgende", + "finish": "Voltooien", + "take_a_tour": "Maak een rondleiding door de interface", + "restart_title": "Introductietour", + "restart_desc": "Bekijk de rondleiding door de interface opnieuw", + "restart_button": "Tour herstarten", + "show_on_new_devices_title": "Tonen op nieuwe apparaten", + "show_on_new_devices_desc": "Herhaal de welkomstbanner en de rondleiding wanneer je voor het eerst inlogt op een nieuw apparaat, zelfs als je ze elders al hebt voltooid", + "sidebar_title": "Uw mailboxen", + "sidebar_desc": "Dit is uw mappenbalk. Klik op een mailbox om de e-mails te bekijken. U kunt mappen maken, e-mails tussen mappen slepen en ongelezen aantallen zien.", + "compose_title": "E-mail schrijven", + "compose_desc": "Klik hier om een nieuwe e-mail te schrijven. U kunt ontvangers, bijlagen toevoegen en rijke tekstopmaak gebruiken.", + "search_title": "Zoek in uw mail", + "search_desc": "Zoek op afzender, onderwerp of inhoud. Klik op het filtericoon voor geavanceerde opties zoals datumbereik, bijlagen en favoriete berichten.", + "email_list_title": "Uw e-maillijst", + "email_list_desc": "E-mails verschijnen hier. Klik op een e-mail om deze rechts te lezen. Gebruik het selectievakje om meerdere te selecteren, verplaats, verwijder of label ze vervolgens.", + "email_viewer_title": "Leesvenster", + "email_viewer_desc": "De geselecteerde e-mail opent hier. Beantwoord, doorstuur, archiveer of verwijder met de werkbalkknopen. U kunt ook e-mails markeren of kleurlabels toevoegen.", + "keywords_title": "Kleurlabels", + "keywords_desc": "Organiseer uw e-mail met kleurgecodeerde labels. Sleep een e-mail naar een label om het te markeren, of klik met de rechtermuisknop om labels toe te wijzen.", + "calendar_title": "Agenda", + "calendar_desc": "Schakel naar de agenda om uw evenementen te beheren. Maak evenementen aan, stel herinneringen in en bekijk dag-, week- of maandweergaven.", + "contacts_title": "Contacten", + "contacts_desc": "Uw adresboek bevindt zich hier. Importeer contacten, maak groepen aan en klik op een contact om de volledige details te bekijken.", + "settings_title": "Instellingen", + "settings_desc": "Pas alles aan: thema, dichtheid, handtekeningen, filters, sneltoetsen, agendainstellingen en meer.", + "shortcuts_title": "Sneltoetsen", + "shortcuts_desc": "Ervaren gebruikers zijn hier dol op. Druk op ? om alle beschikbare sneltoetsen te bekijken. Navigeer, schrijf en beheer e-mails zonder de muis aan te raken.", + "compose_open_title": "De e-maileditor", + "compose_open_desc": "Dit is de e-maileditor. Voeg ontvangers toe, schrijf uw bericht, voeg bestanden bij en gebruik rijke tekstopmaak. U kunt ook concepten opslaan en sjablonen gebruiken.", + "calendar_view_title": "Uw agenda", + "calendar_view_desc": "Hier is uw agenda met voorbeeldevenementen. Schakel tussen dag-, week-, maand- en agendaweergave via de werkbalk.", + "create_event_title": "Evenement aanmaken", + "create_event_desc": "Klik op deze knop om een nieuw agenda-evenement aan te maken. U kunt een titel, datum, tijd en deelnemers instellen.", + "event_modal_title": "Evenementdetails", + "event_modal_desc": "Hier is het evenementformulier. Vul de titel in, kies een datum en tijd, voeg een locatie of deelnemers toe. Klik op opslaan als u klaar bent - of sluit het en ga verder.", + "contacts_list_title": "Uw contacten", + "contacts_list_desc": "Hier zijn uw contacten. Klik op een contact om de details rechts te bekijken. U kunt ook nieuwe contacten aanmaken, vCards importeren of contacten in groepen organiseren.", + "files_title": "Bestandsopslag", + "settings_tabs_title": "Instellingenmenu", + "settings_tabs_desc": "Hier vindt u alle instellingscategorieën. Pas het uiterlijk aan, beheer identiteiten, stel e-mailfilters in, configureer uw agenda en nog veel meer.", + "files_desc": "Uw bestandsbrowser laat u bestanden uploaden, organiseren en delen - als een persoonlijke cloud geïntegreerd in uw mail.", + "demo_banner_title": "Demo-bediening", + "demo_banner_desc": "U bent in demomodus - alles blijft in uw browser. Klik op 'Demo resetten' om opnieuw te beginnen met schone voorbeeldgegevens.", + "quota_title": "Opslaggebruik", + "quota_desc": "Volg de grootte van uw mailbox hier. De cirkel vult zich naarmate u meer ruimte gebruikt." + }, + "unified_mailbox": { + "search_unavailable": "Zoeken is niet beschikbaar in de gecombineerde weergave" + }, + "calendar_event_notifications": { + "someone": "Iemand", + "invited": "{name} heeft je uitgenodigd voor \"{title}\"", + "updated": "{name} heeft \"{title}\" bijgewerkt", + "cancelled": "{name} heeft \"{title}\" geannuleerd", + "untitled": "Afspraak zonder titel" + }, + "share_notifications": { + "someone": "Iemand", + "shared": "{name} heeft {kind} \"{object}\" met je gedeeld", + "changed": "{name} heeft je toegang tot {kind} \"{object}\" gewijzigd", + "revoked": "{name} heeft je toegang tot {kind} \"{object}\" ingetrokken", + "object": { + "folder": "de map", + "calendar": "de agenda", + "address_book": "het adresboek", + "files": "de bestandsmap" + } + }, + "sharing": { + "title": "\"{name}\" delen", + "description": "Geef andere gebruikers of groepen op deze server toegang. Wijzigingen zijn direct van kracht.", + "no_shares": "Nog niet gedeeld.", + "add_person": "Persoon of groep toevoegen", + "search_placeholder": "Zoeken op naam of e-mail…", + "loading_principals": "Gebruikers laden…", + "no_principals": "Geen andere gebruikers of groepen gevonden.", + "no_match": "Geen overeenkomsten.", + "remove": "Toegang intrekken", + "group": "Groep", + "share_added": "Toegang verleend", + "share_updated": "Toegang bijgewerkt", + "share_removed": "Toegang ingetrokken", + "share_failed": "Delen kon niet worden bijgewerkt", + "preset": { + "freeBusy": "Alleen vrij/bezet", + "read": "Alleen lezen", + "readWrite": "Lezen en schrijven", + "manager": "Beheerder", + "custom": "Aangepast" + } + }, + "quote_header": { + "reply_line": "Op {date} schreef {from}:", + "forwarded_separator": "---------- Doorgestuurd bericht ----------", + "from_label": "Van", + "date_label": "Datum", + "subject_label": "Onderwerp" + }, + "pwa_install": { + "title": "{appName} installeren", + "description": "Installeer onze app voor snelle toegang en offline-ondersteuning.", + "not_now": "Niet nu", + "install": "Installeren", + "dont_remind": "Niet meer herinneren", + "dismiss_aria": "Installatiemelding sluiten" + }, + "deep_link": { + "message_not_found": "Dit bericht is niet meer beschikbaar.", + "thread_not_found": "Dit gesprek is niet meer beschikbaar.", + "folder_not_found": "Deze map is niet meer beschikbaar.", + "event_not_found": "Deze afspraak is niet meer beschikbaar.", + "file_not_found": "Dit bestand is niet meer beschikbaar.", + "account_unavailable": "Log in op dat account om deze link te openen.", + "copy_message": "Link naar bericht kopiëren", + "copy_conversation": "Link naar gesprek kopiëren", + "copy_event": "Link naar afspraak kopiëren", + "copied": "Link gekopieerd", + "copy_failed": "Kon de link niet kopiëren" + }, + "plugins": { + "oauth_callback": { + "completing": "Autorisatie voltooien…", + "completing_message": "Even geduld terwijl de verbinding tot stand wordt gebracht.", + "received_title": "Autorisatie ontvangen", + "received_message": "Je kunt terugkeren naar het tabblad waar je de verbinding bent gestart.", + "failed_title": "Autorisatie mislukt", + "denied": "Autorisatie geweigerd.", + "failed_detail": "Autorisatie mislukt: {error}", + "missing_params": "Autorisatiecode of status ontbreekt.", + "delivery_failed": "Het autorisatieresultaat kon niet worden afgeleverd bij het app-tabblad. Sluit dit venster en probeer het opnieuw." + }, + "settings": { + "title": "Plug-ins", + "description": "Plug-ins die je beheerder heeft toegevoegd. Zet ze aan of uit voor je account.", + "description_uploads": "Plug-ins voegen functies toe aan de webmail. Zet ze aan of uit voor je account, of installeer je eigen plug-ins.", + "description_demo": "Plug-ins zijn niet beschikbaar in de demo.", + "install_button": "Plug-in installeren", + "install_hint": "Kies een ZIP-bestand met een plug-in.", + "install_hint_approval": "Kies een ZIP-bestand met een plug-in. Een beheerder moet de plug-in goedkeuren voordat je hem kunt aanzetten.", + "install_uploads_off": "Je beheerder staat het installeren van plug-ins niet toe.", + "install_managed_id": "Je beheerder biedt al een plug-in met deze naam aan, dus die kan niet worden vervangen.", + "install_invalid": "Dit bestand is geen plug-in die kan worden geïnstalleerd: {details}", + "toast_installed": "Plug-in geïnstalleerd. Zet hem hieronder aan.", + "toast_installed_pending": "Plug-in geïnstalleerd en ter goedkeuring naar je beheerder gestuurd.", + "toast_enabled": "{name} staat aan", + "toast_disabled": "{name} staat uit", + "toast_removed": "{name} is verwijderd", + "toast_forced": "Je beheerder houdt {name} aan", + "toast_approval_requested": "{name} wacht op goedkeuring door je beheerder", + "empty_title": "Nog geen plug-ins", + "empty_admin": "Je beheerder heeft geen plug-ins toegevoegd.", + "empty_uploads": "Installeer een plug-in vanuit een ZIP-bestand om te beginnen.", + "empty_demo": "Plug-ins hebben een echt account nodig, dus de demo heeft er geen.", + "syncing": "Plug-ins controleren bij de server…", + "status_installed": "Uit", + "status_enabled": "Wordt gestart", + "status_running": "Aan", + "status_disabled": "Uit", + "status_error": "Probleem", + "badge_forced": "Verplicht", + "badge_managed": "Van je beheerder", + "badge_yours": "Door jou geïnstalleerd", + "badge_awaiting": "Wacht op goedkeuring", + "toggle_label": "{name} aan- of uitzetten", + "forced_note": "Je beheerder vereist deze plug-in, dus hij kan niet worden uitgezet.", + "permissions": "Wat hij kan doen", + "permissions_managed": "Wat hij kan doen (goedgekeurd door je beheerder)", + "settings": "Instellingen", + "remove": "Verwijderen", + "remove_title": "{name} verwijderen?", + "remove_message": "De plug-in en alles wat hij in deze browser heeft opgeslagen, worden verwijderd. Je kunt hem later opnieuw installeren.", + "remove_confirm": "Verwijderen", + "error_user_plugins_off": "Je beheerder heeft plug-ins die door gebruikers zijn geïnstalleerd uitgezet, dus deze plug-in draait niet.", + "error_approval_unavailable": "Deze plug-in is geïnstalleerd voordat goedkeuring verplicht was. Verwijder hem en installeer hem opnieuw om hem ter goedkeuring te sturen.", + "error_approval_denied": "Je beheerder heeft deze plug-in niet goedgekeurd.", + "error_approval_pending": "Wacht tot je beheerder deze plug-in goedkeurt.", + "error_auto_disabled": "Uitgezet omdat het steeds misging. Zet het weer aan om het opnieuw te proberen.", + "accounts_heading": "Gekoppelde accounts", + "account_on": "{label} op {host}", + "account_waiting": "{label} op {host} (inloggen niet afgerond)", + "disconnect": "Ontkoppelen", + "disconnect_failed": "Kan {label} niet ontkoppelen" + }, + "consent": { + "title": "{name} dit toestaan?", + "intro": "De plug-in vraagt de onderstaande rechten. Hij krijgt ze alleen als je ze toestaat.", + "high_risk_note": "Gemarkeerde items geven de plug-in toegang tot de inhoud van je e-mail of laten hem gegevens elders naartoe sturen. Sta ze alleen toe voor plug-ins die je vertrouwt.", + "revoke_hint": "Je kunt de plug-in op elk moment uitzetten of verwijderen in Instellingen.", + "deny": "Niet toestaan", + "allow": "Toestaan" + }, + "permissions": { + "none": "Hij heeft geen speciale rechten nodig.", + "high_risk": "Let op", + "email_read": { + "title": "Je e-mail lezen", + "body": "Afzenders, ontvangers, onderwerpen en de volledige tekst zien van elk bericht in je account, en je mappen." + }, + "email_write": { + "title": "Je e-mail wijzigen en ordenen", + "body": "Labels, markeringen en andere trefwoorden aan je berichten toevoegen of ervan verwijderen, berichten tussen je mappen en naar het archief verplaatsen, en concepten opslaan." + }, + "email_send": { + "title": "E-mail wijzigen voordat die wordt verzonden", + "body": "De ontvangers, het onderwerp, de tekst en de bijlagen lezen en wijzigen van berichten die je schrijft, en het verzenden van een bericht tegenhouden." + }, + "crypto_full": { + "title": "Volledige toegang tot deze app", + "body": "Draait met dezelfde rechten als de webmail zelf. Hij kan al je e-mail en opgeslagen sleutels lezen en namens jou handelen. Sta dit alleen toe voor plug-ins die je volledig vertrouwt." + }, + "email_raw_send": { + "title": "Zelf samengestelde berichten verzenden", + "body": "Volledige berichten, bijvoorbeeld ondertekende of versleutelde, vanaf je account verzenden." + }, + "email_blob_read": { + "title": "De ruwe inhoud van berichten lezen", + "body": "De oorspronkelijke gegevens van je berichten en bijlagen lezen, en van elk bestand dat je bijvoegt voordat het wordt verzonden." + }, + "email_blob_write": { + "title": "Bijlagen vervangen vóór het verzenden", + "body": "Bestanden naar je mailserver uploaden en een bijgevoegd bestand door een ander vervangen voordat het bericht wordt verzonden." + }, + "email_render_takeover": { + "title": "Wijzigen hoe berichten worden weergegeven", + "body": "De tekst vervangen van berichten die je opent en van de berichtenlijst, bijvoorbeeld om ontsleutelde inhoud te tonen." + }, + "calendar_read": { + "title": "Je agenda lezen", + "body": "Je agenda's en hun afspraken zien." + }, + "calendar_write": { + "title": "Je agenda wijzigen", + "body": "Afspraken aanmaken, wijzigen en verwijderen." + }, + "contacts_read": { + "title": "Je contacten lezen", + "body": "Je adresboeken doorzoeken en lezen." + }, + "contacts_write": { + "title": "Je contacten wijzigen", + "body": "Contacten en adresboeken aanmaken, wijzigen en verwijderen." + }, + "files_read": { + "title": "Je bestanden lezen", + "body": "De bestanden in je account bekijken en openen." + }, + "files_write": { + "title": "Je bestanden wijzigen", + "body": "Bestanden in je account uploaden, hernoemen, verplaatsen en verwijderen." + }, + "identity_read": { + "title": "Je afzenderadressen zien", + "body": "De adressen waarvandaan je verzendt en hun handtekeningen zien." + }, + "identity_write": { + "title": "Je afzenderadressen wijzigen", + "body": "Afzenderadressen en handtekeningen aanmaken, wijzigen en verwijderen." + }, + "filters_read": { + "title": "Je mailfilters lezen", + "body": "De regels zien die je binnenkomende e-mail sorteren." + }, + "filters_write": { + "title": "Je mailfilters wijzigen", + "body": "Regels toevoegen aan je filters op de server. Zulke regels kunnen binnenkomende e-mail verplaatsen, doorsturen of verwijderen." + }, + "tasks_read": { + "title": "Je taken lezen", + "body": "Je takenlijsten en taken zien." + }, + "tasks_write": { + "title": "Je taken wijzigen", + "body": "Taken aanmaken, wijzigen, afronden en verwijderen." + }, + "templates_read": { + "title": "Je sjablonen lezen", + "body": "Je opgeslagen berichtsjablonen zien." + }, + "templates_write": { + "title": "Je sjablonen wijzigen", + "body": "Berichtsjablonen aanmaken, wijzigen en verwijderen." + }, + "smime_read": { + "title": "Je S/MIME-status zien", + "body": "Zien of je S/MIME-sleutels en -certificaten hebt, zonder toegang tot de sleutels." + }, + "vacation_read": { + "title": "Je afwezigheidsmelding zien", + "body": "Zien of je automatisch antwoord aanstaat en wat erin staat." + }, + "vacation_write": { + "title": "Je afwezigheidsmelding wijzigen", + "body": "Je automatisch antwoord aan- of uitzetten en de tekst en datums ervan wijzigen." + }, + "settings_read": { + "title": "Je instellingen lezen", + "body": "Je voorkeuren en labeldefinities zien." + }, + "settings_write": { + "title": "Je instellingen wijzigen", + "body": "Labels toevoegen en herschikken en je voorkeuren wijzigen." + }, + "security_read": { + "title": "De beveiliging van je account zien", + "body": "Zien of tweefactoraanmelding en versleuteling aanstaan, zonder toegang tot geheimen." + }, + "auth_observe": { + "title": "Weten wanneer je je aanmeldt", + "body": "Een melding krijgen wanneer je je aanmeldt, van account wisselt of je sessie wordt vernieuwd." + }, + "auth_emit": { + "title": "Je afmelden", + "body": "Je afmelden bij de webmail." + }, + "account_read": { + "title": "Je accounts zien", + "body": "De accounts zien die je hebt toegevoegd, met hun adressen en servers." + }, + "http_post": { + "title": "De webmailserver voor je gebruiken", + "body": "Deze servereindpunten aanroepen met je aanmelding:" + }, + "http_fetch": { + "title": "Verbinding maken met andere websites", + "body": "Verzoeken naar deze sites sturen. Alles wat de plug-in kan lezen, kan daarheen worden gestuurd:" + }, + "admin_config": { + "title": "Zijn instellingen van je beheerder lezen", + "body": "De instellingen lezen die je beheerder voor deze plug-in heeft opgeslagen." + }, + "ui_observe": { + "title": "Volgen waar je bent in de app", + "body": "Weten welke pagina open is en reageren op links waarop je klikt." + }, + "ui_toolbar": { + "title": "Knoppen toevoegen aan werkbalken", + "body": "Eigen knoppen tonen in de berichtwerkbalk, bij een geopend bericht en in de bovenbalk." + }, + "ui_app_top_banner": { + "title": "Een banner bovenaan tonen", + "body": "Een melding tonen over de hele bovenkant van de app." + }, + "ui_email_banner": { + "title": "Meldingen bij berichten tonen", + "body": "Eigen meldingen en samenvattingen tonen boven, in en tussen de delen van een geopend bericht of gesprek." + }, + "ui_email_footer": { + "title": "Inhoud onder berichten toevoegen", + "body": "Eigen inhoud tonen onder een geopend bericht." + }, + "ui_contact_cryptokeys": { + "title": "Versleutelingssleutels van contacten tonen", + "body": "De versleutelingssleutels van een contact tonen en beheren." + }, + "ui_email_details": { + "title": "Panelen toevoegen aan berichtdetails", + "body": "Een eigen paneel tonen naast of onder een geopend bericht." + }, + "ui_download_file": { + "title": "Bestanden op je apparaat opslaan", + "body": "Bestanden die hij heeft gemaakt aanbieden om te downloaden." + }, + "ui_message_list_tabs": { + "title": "Tabbladen toevoegen aan je Postvak IN", + "body": "Je Postvak IN in tabbladen indelen, zoals Updates of Promoties." + }, + "ui_composer_toolbar": { + "title": "Toevoegen aan de opsteller", + "body": "Eigen knoppen, menu-items en secties tonen terwijl je een bericht schrijft." + }, + "ui_composer_sidebar": { + "title": "Een paneel toevoegen aan de opsteller", + "body": "Een eigen paneel tonen naast het bericht dat je schrijft." + }, + "ui_sidebar_widget": { + "title": "Toevoegen aan de zijbalk", + "body": "Eigen vakken en secties tonen in de zijbalk van de mail." + }, + "ui_settings_section": { + "title": "Een instellingenpagina toevoegen", + "body": "Een eigen pagina tonen in Instellingen." + }, + "ui_context_menu": { + "title": "Toevoegen aan het berichtmenu", + "body": "Items toevoegen aan het menu dat opent als je met de rechtermuisknop op een bericht klikt." + }, + "ui_navigation_rail": { + "title": "Toevoegen aan de navigatiebalk", + "body": "Een eigen knop tonen in de navigatiebalk." + }, + "ui_keyboard": { + "title": "Sneltoetsen toevoegen", + "body": "Reageren op sneltoetsen die hij zelf vastlegt." + }, + "ui_calendar_action": { + "title": "Toevoegen aan de agendaweergave", + "body": "Eigen knoppen en secties tonen bij afspraken, en korte labels en stippen bij dagen in de agenda." + }, + "ui_admin_page": { + "title": "Een beheerpagina toevoegen", + "body": "Een eigen pagina tonen in de beheerinstellingen." + }, + "app_lifecycle": { + "title": "Weten wanneer de app start en stopt", + "body": "Een melding krijgen wanneer de app start, offline gaat of je je afmeldt." + }, + "compose_open": { + "title": "De opsteller voor je openen", + "body": "Een nieuw bericht openen met ontvangers, tekst of bijlagen al ingevuld. Jij beslist nog steeds of het wordt verzonden." + }, + "compose_read": { + "title": "Het bericht lezen dat je schrijft", + "body": "De tekst van een bericht lezen terwijl je het schrijft, als je de plug-in in de editor gebruikt. Niet de handtekening of het geciteerde bericht." + }, + "compose_write": { + "title": "Het bericht wijzigen dat je schrijft", + "body": "Tekst in een bericht vervangen of toevoegen terwijl je het schrijft, als je de plug-in in de editor gebruikt. Je kunt elke wijziging ongedaan maken en je beslist nog steeds zelf of je het verstuurt." + }, + "ui_notifications": { + "title": "Meldingen tonen", + "body": "Meldingen op je apparaat tonen, of in de app als je ze niet hebt toegestaan. Elke melding noemt de naam van de plug-in." + }, + "ui_message_list": { + "title": "Toevoegen aan de berichtenlijst", + "body": "Eigen inhoud tonen boven de berichtenlijst, en knoppen bij berichten en in de werkbalk voor geselecteerde berichten." + }, + "ui_contacts": { + "title": "Toevoegen aan de contactenweergave", + "body": "Eigen secties tonen in de details van een contact en knoppen in de contactenlijst." + }, + "ui_tasks": { + "title": "Toevoegen aan de takenweergave", + "body": "Eigen secties tonen in de details van een taak en knoppen in de takenlijst." + }, + "ui_files": { + "title": "Toevoegen aan de bestandenweergave", + "body": "Eigen secties tonen in het bestandsvoorbeeld en items in het menu van een bestand." + }, + "ui_commands": { + "title": "Opdrachten toevoegen", + "body": "Eigen items toevoegen aan het opdrachtenpalet (Ctrl+K)." + }, + "ui_app_page": { + "title": "Pagina's toevoegen", + "body": "Eigen pagina's toevoegen aan de app, elk met een knop in de navigatiebalk." + }, + "http_proxy": { + "title": "Accounts gebruiken die je koppelt", + "body": "Verzoeken via de server van Bulwark sturen met een account of sleutel die jij of je beheerder heeft ingesteld. De plug-in ziet het wachtwoord nooit, maar kan alles doen wat dat account op zijn eigen server mag." + } + }, + "dialog": { + "from_plugin": "Van de plug-in {name}", + "ok": "OK", + "submit": "Versturen", + "confirm": "Bevestigen", + "cancel": "Annuleren", + "close": "Sluiten", + "turn_off": "{name} uitzetten", + "plugin_menu": "Opties voor {name}", + "password_note": "Dit formulier komt van de plug-in {name}, niet van de webmail. Vul alleen een wachtwoord in als je deze plug-in vertrouwt.", + "toast_auto_disabled": "{name} is uitgezet omdat het steeds misging." + }, + "connect": { + "title": "{label} koppelen", + "intro_password": "{name} wil je account bij {label} gebruiken. Bulwark bewaart het wachtwoord op zijn eigen server en stuurt het alleen naar {host}. De plug-in ziet het nooit.", + "intro_password_any": "{name} wil je account bij {label} gebruiken. Bulwark bewaart het wachtwoord op zijn eigen server en stuurt het alleen naar de server die je hier invult. De plug-in ziet het nooit.", + "intro_signin": "{name} wil inloggen op je account bij {label}. Vul hier de server in en log daarna in op de eigen pagina van die server. Bulwark bewaart de toegang die het krijgt op zijn eigen server; de plug-in ziet die nooit.", + "server": "Serveradres", + "server_hint": "cloud.example.com", + "username": "Gebruikersnaam", + "secret": "Wachtwoord of sleutel", + "connect": "Koppelen", + "continue": "Doorgaan", + "cancel": "Annuleren", + "saving": "Opslaan…", + "error_server": "Vul het adres van de server in, bijvoorbeeld https://cloud.example.com", + "error_save": "Opslaan mislukt: {error}" + } + }, + "global_search": { + "title": "Alles doorzoeken", + "placeholder": "Zoek in mail, contacten, agenda, bestanden...", + "scope_label": "Zoeken in", + "account_label": "Account", + "scope_all": "Alles", + "scope_mail": "E-mail", + "scope_contacts": "Contacten", + "scope_calendar": "Agenda", + "scope_files": "Bestanden", + "all_accounts": "Alle accounts", + "show_all": "Alle resultaten tonen", + "no_results": "Geen resultaten voor “{query}”", + "server_hint": "Volledige woorden doorzoeken de server; woorddelen vinden al geladen items", + "searching": "Zoeken...", + "account_error": "Kon niet zoeken in {account}", + "recent_searches": "Recente zoekopdrachten", + "recurring": "Terugkerend", + "no_subject": "(no subject)", + "empty_hint": "Doorzoek mail, contacten, afspraken en bestanden van alle accounts. Probeer from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Plat op datum", + "grouped": "Gegroepeerd", + "load_more": "Meer laden", + "preview_empty": "Selecteer een resultaat voor een voorbeeld", + "open_full": "Openen", + "view_label": "Weergave" + }, + "top_bar": { + "more": "Meer opties" + }, + "plugin_pages": { + "loading": "Pagina laden", + "not_available_title": "Deze pagina is niet beschikbaar", + "not_available_description": "De plug-in die hem heeft toegevoegd, staat uit of is niet meer geïnstalleerd.", + "back_to_mail": "Terug naar e-mail", + "settings_group": "Plug-ins" + }, + "command_palette": { + "title": "Opdrachtenpalet", + "open": "Opdrachtenpalet openen", + "placeholder": "Typ een opdracht", + "no_results": "Geen overeenkomende opdrachten", + "go_to": "Naar {name}", + "compose": "Nieuw bericht", + "search": "E-mail doorzoeken", + "theme_dark": "Donker thema gebruiken", + "theme_light": "Licht thema gebruiken", + "shortcuts": "Sneltoetsen tonen", + "group_navigation": "Naar", + "group_actions": "Acties", + "group_plugins": "Plug-ins" + }, + "notification_center": { + "title": "Meldingen", + "mark_all_read": "Alles als gelezen markeren", + "clear": "Wissen", + "empty": "Geen meldingen", + "close": "Sluiten", + "unread": "Ongelezen", + "unread_count": "{count} ongelezen", + "level_error": "Fout", + "level_warning": "Waarschuwing" + } +} diff --git a/locales/pl/common.json b/locales/pl/common.json index 6201a4ae..dcca6570 100644 --- a/locales/pl/common.json +++ b/locales/pl/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimalistyczny klient poczty internetowej wykorzystujący protokół JMAP", - "login": { - "title": "Webmail", - "username_label": "E-mail", - "username_placeholder": "user@example.com", - "password_label": "Hasło", - "password_placeholder": "Wprowadź hasło", - "jmap_endpoint_label": "Serwer JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Serwer musi zezwalać na żądania CORS z tej domeny.", - "jmap_server_label": "Serwer", - "jmap_server_auto_picked": "Serwer wybrany na podstawie domeny Twojej poczty.", - "sign_in": "Zaloguj się", - "signing_in": "Logowanie...", - "loading": "Ładowanie...", - "reconnecting": "Utracono połączenie. Próba ponownego połączenia…", - "error": { - "invalid_credentials": "Nieprawidłowy adres e-mail lub hasło. Sprawdź dane logowania i spróbuj ponownie.", - "connection_failed": "Nie można połączyć się z serwerem. Sprawdź połączenie internetowe i spróbuj ponownie.", - "cors_blocked": "Serwer jest osiągalny, ale blokuje żądania między domenami. Sprawdź ustawienia CORS serwera JMAP i zezwól na tę domenę.", - "server_error": "Serwer jest tymczasowo niedostępny. Spróbuj ponownie później.", - "generic": "Wystąpił nieoczekiwany błąd. Jeśli problem się powtarza, skontaktuj się z administratorem.", - "totp_required": "Wymagany jest kod uwierzytelniania dwuskładnikowego. Wprowadź kod poniżej.", - "totp_invalid": "Nieprawidłowy kod uwierzytelniania. Sprawdź aplikację uwierzytelniającą i spróbuj ponownie.", - "oauth_discovery_failed": "SSO jest włączone, ale nie można połączyć się z dostawcą tożsamości. Sprawdź konfigurację OAuth." - }, - "show_password": "Pokaż hasło", - "hide_password": "Ukryj hasło", - "totp_toggle": "Mam kod 2FA", - "remember_me": "Zapamiętaj mnie", - "config_error": { - "title": "Błąd konfiguracji", - "fetch_failed": "Nie można załadować konfiguracji aplikacji. Spróbuj ponownie później.", - "server_not_configured": "Serwer poczty nie został skonfigurowany. Skontaktuj się z administratorem." - }, - "remove_from_history": "Usuń z historii", - "totp_label": "Kod uwierzytelniania", - "totp_placeholder": "000000", - "session_expired": "Twoja sesja wygasła. Zaloguj się ponownie.", - "dismiss": "Zamknij", - "or": "lub", - "sign_in_sso": "Zaloguj się przez SSO", - "add_account_title": "Dodaj konto", - "add_account_subtitle": "Zaloguj się na inne konto", - "cancel": "Anuluj", - "website": "Strona internetowa", - "imprint": "Impressum", - "privacy_policy": "Polityka prywatności", - "try_demo": "Wypróbuj demo", - "demo_description": "Poznaj aplikację z przykładowymi danymi - bez potrzeby zakładania konta", - "demo_launching": "Uruchamianie demo...", - "demo_login_button": "Uruchom demo", - "demo_tagline": "Wypróbuj w pełni funkcjonalnego klienta poczty. Konto nie jest wymagane.", - "demo_no_signup": "Bez rejestracji - eksploruj swobodnie z przykładowymi danymi", - "oauth_completing": "Kończenie logowania...", - "oauth_error": { - "title": "Uwierzytelnianie nie powiodło się", - "invalid_state": "Walidacja bezpieczeństwa nie powiodła się. Spróbuj zalogować się ponownie.", - "missing_params": "Brakujące dane autoryzacji. Spróbuj zalogować się ponownie.", - "token_exchange_failed": "Nie udało się dokończyć uwierzytelniania. Spróbuj ponownie.", - "access_denied": "Odmowa dostępu. Skontaktuj się z administratorem.", - "back_to_login": "Powrót do logowania" - } - }, - "sidebar": { - "close": "Zamknij", - "compose": "Napisz", - "compose_hint": "Napisz (c)", - "search_placeholder": "Szukaj poczty...", - "search_placeholder_hint": "Szukaj poczty... (naciśnij /)", - "storage": "Przestrzeń dyskowa", - "storage_used": "Zajęte", - "storage_free": "Wolne", - "storage_total": "Łącznie", - "sign_out": "Wyloguj się", - "sign_out_of": "Wyloguj się z {account}", - "sign_out_all": "Wyloguj się ze wszystkich kont", - "add_account": "Dodaj konto", - "set_as_default": "Ustaw jako domyślne", - "switch_account": "Przełącz konto", - "contacts": "Kontakty", - "calendar": "Kalendarz", - "settings": "Ustawienia", - "admin": "Administracja", - "files": "Pliki", - "loading_mailboxes": "Ładowanie skrzynek...", - "push_connected": "Aktualizacje w czasie rzeczywistym aktywne", - "push_disconnected": "Aktualizacje w czasie rzeczywistym nieaktywne", - "keyboard_shortcuts": "Skróty klawiszowe", - "theme": { - "light": "Tryb jasny", - "dark": "Tryb ciemny", - "system": "Motyw systemowy" - }, - "language": { - "title": "Język" - }, - "mailboxes": { - "inbox": "Odebrane", - "sent": "Wysłane", - "drafts": "Szkice", - "trash": "Kosz", - "archive": "Archiwum", - "starred": "Oznaczone gwiazdką", - "all_mail": "Cała poczta", - "spam": "Spam", - "important": "Ważne" - }, - "unified_inbox": "Wspólne odebrane", - "unified_sent": "Wszystkie wysłane", - "unified_drafts": "Wszystkie szkice", - "unified_trash": "Wszystkie kosze", - "unified_archive": "Wszystkie archiwa", - "unified_junk": "Wszystkie spam", - "all_accounts": "Wszystkie konta", - "unified_mailbox": "Wspólna skrzynka", - "expand": "Rozwiń", - "collapse": "Zwiń", - "expand_tooltip": "Rozwiń", - "collapse_tooltip": "Zwiń", - "mobile": { - "search": "Szukaj", - "compose": "Napisz", - "go_back": "Wróć", - "toggle_menu": "Przełącz menu" - }, - "clear_search": "Wyczyść wyszukiwanie", - "vacation_active": "Autoresponder wakacyjny jest aktywny", - "demo_banner": "Tryb demo", - "demo_reset": "Resetuj", - "demo_tour": "Przewodnik", - "tags": "Etykiety", - "show_all_tags": "Pokaż wszystkie ({count})", - "show_fewer_tags": "Pokaż mniej", - "folders": "Foldery", - "mail": "Poczta", - "nav_label": "Nawigacja", - "add_app": "Aplikacje", - "shared": "Udostępnione", - "scheduled": "Zaplanowane", - "unified_all_unread": "Wszystkie nieprzeczytane", - "unified_all_starred": "Wszystkie oznaczone gwiazdką", - "unified_all_mail": "Cała poczta", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Aplikacje domyślne", - "description": "Wybierz, czy linki e-mail i kalendarza mają otwierać się w Bulwark. Technicznie Bulwark rejestruje się jako obsługa protokołu dla linków mailto: i webcal:.", - "unsupported": "Ta przeglądarka lub to połączenie nie obsługuje ręcznej rejestracji obsługi protokołu. Nadal możesz mieć możliwość użycia zainstalowanej aplikacji PWA w ustawieniach przeglądarki lub systemu.", - "mailto_label": "Linki e-mail", - "mailto_description": "Otwiera linki mailto: w Bulwark z wstępnie wypełnionym edytorem wiadomości.", - "protocol_open_mode_label": "Podczas otwierania linków protokołu", - "protocol_open_mode_description": "Wybierz, czy Bulwark ma otwierać linki mailto: i webcal: w nowej karcie, czy ponownie używać otwartej sesji. Opcja aktywnej sesji wymaga uprawnienia do powiadomień, aby można było kliknąć powiadomienie awaryjne i przenieść Bulwark na pierwszy plan, jeśli przeglądarka blokuje fokus.", - "protocol_open_mode_active_session": "Jeśli to możliwe, otwórz w aktywnej sesji", - "protocol_open_mode_new_tab": "Zawsze otwieraj nową kartę", - "focus_notification_title": "Otwórz Bulwark", - "focus_notification_body": "Link został otwarty w Bulwark. Kliknij, aby przenieść okno na pierwszy plan.", - "webcal_label": "Linki kalendarza", - "webcal_description": "Otwiera linki webcal: w Bulwark z wstępnie wypełnionym oknem subskrypcji kalendarza.", - "register_mailto": "Zarejestruj aplikację e-mail", - "register_webcal": "Zarejestruj aplikację kalendarza", - "mailto_registered": "Zażądano rejestracji obsługi e-mail", - "webcal_registered": "Zażądano rejestracji obsługi kalendarza", - "registration_failed": "Rejestracja obsługi protokołu nie powiodła się", - "opening_mailto": "Otwieranie edytora...", - "opening_webcal": "Otwieranie kalendarza...", - "browser_note": "Przeglądarka lub system operacyjny może poprosić o potwierdzenie i może wymagać zainstalowania Bulwark, zanim będzie można wybrać go jako aplikację domyślną.", - "select_account_title": "Wybierz konto", - "select_mailto_account": "Wybierz konto, które ma otworzyć ten link e-mail.", - "select_webcal_account": "Wybierz konto, które ma otworzyć ten link kalendarza.", - "select_account_note": "Ten wybór dotyczy tylko tego linku protokołu.", - "detail_to": "Do", - "detail_subject": "Temat", - "detail_no_subject": "Bez tematu", - "detail_calendar": "Kalendarz", - "detail_source": "Źródło", - "active_account": "Aktywne", - "switching_account": "Przełączanie konta..." - }, - "sidebar_apps": { - "modal_title": "Aplikacje paska bocznego", - "add_new": "Dodaj aplikację", - "edit_app": "Edytuj aplikację", - "name_label": "Nazwa", - "name_placeholder": "Moja aplikacja", - "name_required": "Nazwa jest wymagana", - "url_label": "URL", - "url_required": "URL jest wymagany", - "url_invalid": "Wprowadź prawidłowy adres http lub https", - "icon_label": "Ikona", - "icon_required": "Ikona jest wymagana", - "open_mode_label": "Tryb otwierania", - "open_new_tab": "Nowa karta", - "open_inline": "Wbudowany", - "cancel": "Anuluj", - "add": "Dodaj", - "update": "Zaktualizuj", - "delete": "Usuń", - "delete_confirm_title": "Usuń aplikację", - "delete_confirm": "Czy na pewno chcesz usunąć \"{name}\"?", - "no_apps": "Nie dodano jeszcze żadnych aplikacji", - "no_apps_hint": "Dodaj własne aplikacje i linki do paska bocznego", - "managed_title": "Udostępnione przez administratora", - "managed_description": "Te aplikacje są skonfigurowane dla wszystkich i nie można ich zmienić.", - "managed_badge": "Zarządzana", - "search_icons": "Szukaj ikon...", - "show_popular": "Popularne", - "show_all": "Wszystkie", - "no_icons_found": "Nie znaleziono ikon", - "inline_badge": "Wbudowany", - "tab_badge": "Karta", - "show_on_mobile": "Pokaż na telefonie" - }, - "email_list": { - "no_emails": "Nie znaleziono wiadomości", - "no_emails_description": "Ta skrzynka jest pusta", - "no_search_results": "Nie znaleziono wyników", - "no_search_results_description": "Spróbuj zmienić wyszukiwanie lub filtry", - "loading": "Ładowanie wiadomości...", - "unread": "nieprzeczytane", - "to_me": "Do mnie", - "to_recipients": "Do {count} odbiorców", - "and_others": "i {count} innych", - "draft": "Szkic", - "starred": "Oznaczone gwiazdką", - "pinned": "Przypięte", - "replied": "Odpowiedziano", - "forwarded": "Przekazano", - "has_attachment": "Z załącznikiem", - "conversations_count": "{count} z {total} konwersacji", - "conversations_count_plus": "{count}+ konwersacji", - "conversations_count_simple": "{count} konwersacji", - "no_conversations": "Brak konwersacji", - "loading_more": "Ładowanie kolejnych wiadomości...", - "no_more_emails": "Nie ma więcej wiadomości do załadowania", - "batch_actions": { - "select": "Zaznacz wiadomości", - "select_all": "Zaznacz wszystkie", - "selected_messages": "Wybrano {count, plural, one {1 wiadomość} other {# wiadomości}} e-mail", - "mark_read": "Oznacz jako przeczytane", - "mark_unread": "Oznacz jako nieprzeczytane", - "delete": "Usuń", - "delete_confirm_title": "Usuń wiadomości", - "delete_confirm_message": "Czy na pewno chcesz usunąć {count, plural, one {1 wiadomość} other {# wiadomości}}?", - "clear_selection": "Wyczyść zaznaczenie" - }, - "permanent_delete": "Usuń trwale", - "permanent_delete_confirm_title": "Trwałe usunięcie", - "permanent_delete_confirm_message": "Ta wiadomość zostanie trwale usunięta. Tej operacji nie można cofnąć.", - "permanent_delete_confirm_batch_message": "{count, plural, one {1 wiadomość zostanie trwale usunięta} other {# wiadomości zostanie trwale usuniętych}}. Tej operacji nie można cofnąć.", - "empty_folder": { - "button": "Opróżnij folder", - "confirm_title": "Opróżnij folder", - "confirm_message": "Wszystkie wiadomości w tym folderze zostaną trwale usunięte. Tej operacji nie można cofnąć.", - "confirm_button": "Opróżnij folder", - "junk_hint": "Możesz opróżnić folder Spam, aby trwale usunąć wszystkie wiadomości.", - "trash_hint": "Możesz opróżnić Kosz, aby trwale usunąć wszystkie wiadomości." - }, - "no_scheduled_emails": "Brak zaplanowanych e-maili", - "no_scheduled_emails_description": "Wiadomości zaplanowane na później pojawią się tutaj.", - "scheduled_count": "{count, plural, one {1 zaplanowany e-mail} few {# zaplanowane e-maile} other {# zaplanowanych e-maili}}", - "scheduled_actions_hint": "Zaplanowane wiadomości można anulować, przełożyć lub edytować z poziomu akcji planowania.", - "cancel_scheduled_send": "Anuluj wysyłkę", - "reschedule_send": "Przełóż", - "cancel_and_edit": "Anuluj i edytuj", - "cancel_and_compose_again": "Anuluj i napisz ponownie", - "reschedule_prompt": "Wprowadź nową datę/godzinę, np. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Nadawca prosi o potwierdzenie przeczytania:", - "send": "Wyślij potwierdzenie", - "ignore": "Ignoruj", - "sent": "Wysłano potwierdzenie przeczytania.", - "send_failed": "Nie udało się wysłać potwierdzenia przeczytania", - "mdn_subject": "Przeczytano: {subject}", - "mdn_body": "To jest potwierdzenie przeczytania wiadomości wysłanej do {recipient}.\n\nUwaga: to potwierdzenie oznacza jedynie, że wiadomość została wyświetlona na komputerze odbiorcy. Nie gwarantuje, że odbiorca przeczytał lub zrozumiał treść." - }, - "no_email_selected": "Nie wybrano wiadomości", - "no_email_description": "Wybierz wiadomość z listy, aby ją wyświetlić", - "no_conversation_selected": "Nie wybrano konwersacji", - "no_conversation_description": "Wybierz konwersację z listy, aby ją przeczytać", - "compose": "Napisz", - "compose_hint": "Napisz nową wiadomość", - "no_subject": "(Bez tematu)", - "no_body_content": "(Brak treści)", - "no_preview_available": "Podgląd niedostępny", - "show_quoted_text": "Pokaż cytowany tekst", - "hide_quoted_text": "Ukryj cytowany tekst", - "loading_email": "Ładowanie wiadomości...", - "loading": "Ładowanie...", - "reply": "Odpowiedz", - "reply_all": "Odpowiedz wszystkim", - "forward": "Przekaż dalej", - "delete": "Usuń", - "archive": "Archiwizuj", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Oznacz gwiazdką", - "unstar": "Usuń gwiazdkę", - "mark_unread": "Oznacz jako nieprzeczytane", - "mark_read": "Oznacz jako przeczytane", - "unread": "Nieprzeczytane", - "read": "Przeczytane", - "spam_short": "Spam", - "not_spam_short": "Nie spam", - "move": "Przenieś", - "print": "Drukuj", - "view_source": "Pokaż źródło", - "fullscreen": "Pełny ekran", - "exit_fullscreen": "Zamknij pełny ekran", - "export_email": "Eksportuj jako .eml", - "forward_as_attachment": "Prześlij dalej jako załącznik", - "import_email": "Importuj .eml lub .zip", - "keyboard_shortcuts": "Skróty klawiszowe (?)", - "email_source": "Źródło wiadomości", - "draft_banner": "Ta wiadomość jest szkicem", - "edit_draft": "Edytuj", - "copy_source": "Kopiuj do schowka", - "source_copied": "Źródło skopiowane do schowka", - "attachments": "Załączniki", - "important": "Ważne", - "download": "Pobierz", - "download_all": "Pobierz wszystkie", - "from": "Od", - "to": "Do", - "cc": "DW", - "bcc": "UDW", - "date": "Data", - "subject": "Temat", - "show_details": "Pokaż szczegóły", - "hide_details": "Ukryj szczegóły", - "external_content_warning": "Obrazy i treści zewnętrzne zostały zablokowane", - "load_external_content": "Załaduj obrazy", - "trust_sender": "Zawsze ufaj temu nadawcy", - "back_to_list": "Powrót do listy", - "view_contact": "Pokaż kontakt", - "message_details": "Szczegóły wiadomości", - "more_reply_options": "Więcej opcji odpowiedzi", - "set_tag": "Ustaw etykietę", - "tag": "Etykieta", - "more_actions": "Więcej działań", - "previous": "Poprz.", - "next": "Nast.", - "move_to": "Przenieś do...", - "remove_tag": "Usuń etykietę", - "tag_filter_placeholder": "Filtruj etykiety", - "tag_no_matches": "Brak pasujących etykiet", - "more_count": "+{count} więcej", - "characters_count": "{count} znaków", - "quick_reply_placeholder": "Napisz szybką odpowiedź...", - "more_options": "Więcej opcji", - "sending": "Wysyłanie...", - "security_authentication": "Bezpieczeństwo i uwierzytelnianie", - "technical_details": "Szczegóły techniczne", - "message_id_label": "Message-ID:", - "reply_to_label": "Reply-To:", - "delivery_time_label": "Czas dostarczenia:", - "conversation_part_label": "Część konwersacji:", - "previous_messages": "{count} poprzednia wiadomość", - "previous_messages_plural": "{count} poprzednich wiadomości", - "time": { - "day": "dzień", - "days": "dni", - "hour": "godzina", - "hours": "godzin", - "minute": "minuta", - "minutes": "minut" - }, - "unknown_sender": "Nieznany", - "recipient_me": "ja", - "recipient_and_others": "{name} i {count} innych", - "recipient_to_prefix": "Do:", - "authentication": { - "title": "Uwierzytelnianie", - "status": { - "verified": "Zweryfikowano", - "warning": "Ostrzeżenie", - "none": "Nieuwierzytelnione" - }, - "spf": { - "pass": "SPF zaliczony", - "fail": "SPF niezaliczony", - "none": "Brak SPF" - }, - "dkim": { - "pass": "DKIM prawidłowy", - "fail": "DKIM nieprawidłowy", - "none": "Brak DKIM" - }, - "dmarc": { - "pass": "DMARC zaliczony", - "fail": "DMARC niezaliczony", - "none": "Brak DMARC" - }, - "spam_score": "Ocena spamu", - "tooltip_spf": "Sender Policy Framework: sprawdza, czy serwer wysyłający jest uprawniony do wysyłania wiadomości w imieniu domeny", - "tooltip_dkim": "DomainKeys Identified Mail: potwierdza za pomocą podpisu kryptograficznego, że wiadomość nie została zmieniona w trakcie przesyłania", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zapewnia zgodność SPF i DKIM z domeną nadawcy i określa politykę dla niepowodzeń", - "policy": "Polityka", - "result": { - "pass": "Zaliczony", - "fail": "Niezaliczony", - "softfail": "Łagodny błąd", - "neutral": "Neutralny", - "permerror": "Błąd trwały", - "temperror": "Błąd tymczasowy", - "none": "Brak" - } - }, - "headers": { - "routing": "Routing", - "received": "Otrzymano", - "message_id": "ID wiadomości", - "list_info": "Informacje o liście" - }, - "tooltips": { - "reply": "Odpowiedz (r)", - "reply_all": "Odpowiedz wszystkim (a)", - "forward": "Przekaż dalej (f)", - "archive": "Archiwizuj (e)", - "delete": "Usuń (# lub Del)", - "star": "Oznacz gwiazdką (s)", - "unstar": "Usuń gwiazdkę (s)", - "compose": "Napisz (c)", - "previous": "Poprzednia wiadomość", - "next": "Następna wiadomość", - "edit_draft": "Edytuj szkic" - }, - "spam": { - "button_title": "Zgłoś spam", - "not_spam_title": "Oznacz jako prawidłową", - "toast_success": "Przeniesiono do Spamu", - "toast_batch": "{count} wiadomości przeniesiono do Spamu", - "toast_undo": "Cofnij", - "toast_not_spam_success": "Przeniesiono do Odebranych", - "toast_not_spam_batch": "{count} wiadomości przeniesiono do Odebranych", - "error": "Nie udało się zgłosić spamu", - "error_not_spam": "Nie udało się przywrócić wiadomości" - }, - "unsubscribe_banner": { - "label": "Newsletter", - "button": "Wypisz się", - "confirm_title": "Wypisać się od tego nadawcy?", - "confirm_button": "Potwierdź", - "cancel": "Anuluj", - "success_http": "Strona wypisania otwarta w nowej karcie", - "success_mailto": "Wysłano e-mail rezygnacji z subskrypcji", - "confirm_message_http": "Strona rezygnacji z subskrypcji otworzy się w nowej karcie.", - "confirm_message_mailto": "Do nadawcy zostanie wysłany e-mail rezygnacji z subskrypcji.", - "error": "Nie można się wypisać", - "dismiss": "Zamknij" - }, - "calendar_invitation": { - "loading": "Ładowanie szczegółów wydarzenia…", - "title": "Zaproszenie kalendarza", - "published_title": "Opublikowane wydarzenie", - "response_title": "Odpowiedź na wydarzenie", - "update_title": "Aktualizacja wydarzenia", - "counter_title": "Propozycja zmian", - "refresh_title": "Żądanie odświeżenia", - "declined_counter_title": "Propozycja zmian odrzucona", - "cancelled_title": "Wydarzenie anulowane", - "organizer": "Organizator: {name}", - "organizer_label": "Organizator:", - "attendees": "{count, plural, one {# uczestnik} other {# uczestników}}", - "accept": "Akceptuj", - "maybe": "Może", - "decline": "Odrzuć", - "add_to_calendar": "Dodaj do kalendarza", - "added": "Dodano do kalendarza", - "rsvp_sent": "Odpowiedź wysłana", - "parse_error": "Nie można odczytać zaproszenia", - "action_failed": "Nie udało się wykonać akcji kalendarza.", - "no_calendar": "Kalendarz niedostępny", - "published_info": "To wydarzenie zostało udostępnione w celach informacyjnych.", - "response_info": "Ta wiadomość zawiera odpowiedź uczestnika.", - "response_info_organizer": "Ta odpowiedź uczestnika aktualizuje Twoje wydarzenie.", - "update_info": "Ta wiadomość aktualizuje istniejące wydarzenie.", - "counter_info": "Ta wiadomość proponuje zmiany w wydarzeniu.", - "counter_info_organizer": "Ten uczestnik zaproponował zmiany w Twoim wydarzeniu.", - "refresh_info": "Ta wiadomość żąda najnowszych szczegółów wydarzenia.", - "refresh_info_organizer": "Uczestnik poprosił o najnowsze szczegóły wydarzenia.", - "declined_counter_info": "Organizator odrzucił propozycję zmian.", - "authentication_failed_info": "Uwierzytelnianie poczty dla tego zaproszenia nie powiodło się. Traktuj akcje kalendarza z ostrożnością.", - "authentication_missing_info": "To zaproszenie nie zawiera zweryfikowanego uwierzytelniania poczty. Potwierdź szczegóły z organizatorem, jeśli cokolwiek wygląda nietypowo.", - "sender_mismatch_info": "To zaproszenie zostało wysłane z {sender}, podczas gdy organizator w danych kalendarza to {organizer}.", - "sender_mismatch_unverified_info": "To zaproszenie zostało wysłane z {sender}, podczas gdy organizator w danych kalendarza to {organizer}, a wiadomość nie mogła zostać zweryfikowana.", - "organizer_role": "Jesteś organizatorem tego wydarzenia", - "your_response": "Twoja odpowiedź: {status}", - "response_needed": "Wymaga odpowiedzi", - "response_accepted": "Zaakceptowano", - "response_tentative": "Wstępnie", - "response_declined": "Odrzucono", - "response_delegated": "Delegowano", - "actor_sent_info": "Wysłano przez {name}.", - "actor_response_info": "{name} odpowiedział(a) {status}.", - "actor_counter_info": "{name} zaproponował(a) zmiany w tym wydarzeniu.", - "actor_refresh_info": "{name} poprosił(a) o najnowsze szczegóły wydarzenia.", - "actor_declined_counter_info": "{name} odrzucił(a) propozycję zmian.", - "actor_note": "Notatka: {comment}", - "actor_unknown": "Ktoś", - "proposed_changes": "Proponowane zmiany", - "change_title": "Tytuł", - "change_time": "Czas", - "change_location": "Lokalizacja", - "change_description": "Opis", - "change_empty": "Brak", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Zastosuj proponowane zmiany", - "proposal_applied": "Proponowane zmiany zastosowane.", - "review_proposal": "Przejrzyj propozycję", - "review_request": "Przejrzyj żądanie", - "view_in_calendar": "Pokaż w kalendarzu", - "select_calendar": "Wybierz kalendarz", - "already_in_calendar": "Już w Twoim kalendarzu", - "request_info": "Zostałeś zaproszony na to wydarzenie. Odpowiedz, aby poinformować organizatora o swojej dostępności.", - "cancel_info": "Organizator anulował to wydarzenie.", - "event_updated": "Aktualizacja #{sequence}", - "event_status_tentative": "Wstępne", - "event_status_cancelled": "Anulowane", - "collapse": "Ukryj szczegóły", - "expand": "Pokaż szczegóły" - }, - "send": "Wyślij", - "more": "więcej", - "details": { - "recipients_routing": "Odbiorcy i trasowanie", - "authentication_security": "Uwierzytelnianie i bezpieczeństwo", - "identifiers_threading": "Identyfikatory i wątkowanie", - "mailing_list": "Lista mailingowa", - "message_properties": "Właściwości wiadomości", - "sent": "Wysłano", - "received": "Odebrano", - "delivery_time": "Czas dostarczenia", - "in_reply_to": "W odpowiedzi na", - "references": "Odniesienia", - "thread_id": "ID wątku", - "size": "Rozmiar", - "mime_type": "Typ MIME", - "attachments_summary": "{count} plików · {size}", - "list_id": "ID listy", - "list_help": "Pomoc listy", - "list_post": "Wysyłanie do listy", - "list_unsubscribe": "Wypisz się", - "iprev": "Odwrotny DNS", - "spam_status": "Status spamu", - "ai_verdict": "Werdykt AI", - "account": "Konto", - "no_subject": "(brak tematu)" - }, - "scheduled_banner": "Zaplanowano wysyłkę na {date}", - "scheduled_send_created": "E-mail zaplanowany do wysłania", - "cancel_scheduled_send": "Anuluj wysyłkę", - "reschedule_send": "Przełóż", - "cancel_and_edit": "Anuluj i edytuj", - "cancel_and_compose_again": "Anuluj i napisz ponownie", - "reschedule_prompt": "Wprowadź nową datę/godzinę, np. 2026-05-04T15:30", - "scheduled_actions_only": "Widok Zaplanowane obsługuje tylko akcje planowania", - "undo_send_scheduled": "Wiadomość zaplanowana do wysłania", - "undo_send": "Cofnij wysyłkę", - "contact_sidebar": { - "title": "Kontakt", - "close": "Zamknij panel boczny", - "action_email": "E-mail", - "action_email_title": "Wyślij e-mail", - "action_copy": "Kopiuj", - "action_copy_title": "Kopiuj adres e-mail", - "action_edit_title": "Edytuj kontakt", - "section_emails": "E-maile", - "section_phones": "Telefony", - "section_organizations": "Organizacje", - "section_addresses": "Adresy", - "section_notes": "Notatki", - "not_in_contacts": "Brak w kontaktach", - "add_to_contacts": "Dodaj do kontaktów", - "copied": "Skopiowano!", - "copy_failed": "Nie udało się skopiować" - }, - "send_now": "Wyślij teraz" - }, - "email_composer": { - "read_receipt_on": "Zażądano potwierdzenia przeczytania (kliknij, aby wyłączyć)", - "read_receipt_off": "Zażądaj potwierdzenia przeczytania", - "dsn_on": "Zażądano powiadomienia o dostarczeniu (kliknij, aby wyłączyć)", - "dsn_off": "Zażądaj powiadomienia o dostarczeniu", - "format_plain_text": "Przełącz na zwykły tekst", - "format_rich_text": "Przełącz na tekst sformatowany (HTML)", - "require_tls_on": "Wymagane szyfrowane dostarczenie (kliknij, aby wyłączyć)", - "require_tls_off": "Wymagaj szyfrowanego dostarczenia (TLS)", - "new_message": "Nowa wiadomość", - "signature_edit_hint": "Kliknij dwukrotnie, aby edytować podpis", - "reply": "Odpowiedz", - "reply_all": "Odpowiedz wszystkim", - "forward": "Przekaż dalej", - "reply_to": "Odpowiedz", - "reply_all_to": "Odpowiedz wszystkim", - "forward_message": "Przekaż dalej", - "from": "Od", - "to": "Do", - "cc": "DW", - "bcc": "UDW", - "subject": "Temat", - "body_placeholder": "Napisz wiadomość...", - "send": "Wyślij", - "cancel": "Anuluj", - "attach": "Załącz", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Odrzuć", - "discard_draft_title": "Odrzucić szkic?", - "discard_draft_confirm": "Masz niezapisane zmiany. Czy chcesz odrzucić ten szkic?", - "saving": "Zapisywanie...", - "draft_saved": "Szkic zapisany", - "save_failed": "Nie udało się zapisać", - "to_placeholder": "Adresy e-mail odbiorców", - "cc_placeholder": "Odbiorcy DW", - "bcc_placeholder": "Odbiorcy UDW", - "subject_placeholder": "Temat", - "cc_label": "DW:", - "bcc_label": "UDW:", - "subject_label": "Temat:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(Bez tematu)", - "unknown_sender": "Nieznany", - "quote": { - "reply_header": "Dnia {date}, {sender} napisał(a):", - "forward_header": "---------- Przekazana wiadomość ----------", - "from": "Od: {sender}", - "date": "Data: {date}", - "subject": "Temat: {subject}", - "to": "Do: {recipients}" - }, - "remove_sub_address": "Usuń podadres", - "from_override": { - "toggle_off": "Zastąp", - "toggle_on": "Anuluj zastąpienie", - "toggle_tooltip": "Swobodnie edytuj nazwę i adres nadawcy. Poczta jest nadal wysyłana przez twoją tożsamość.", - "name_label": "Nazwa nadawcy", - "name_placeholder": "Nazwa", - "email_label": "Adres e-mail nadawcy", - "email_placeholder": "alias@example.com", - "envelope_notice": "Jeśli twój serwer nie akceptuje tego adresu jako nadawcy koperty, zostanie użyty {identity}, a odbiorcy zobaczą go w nagłówku Return-Path." - }, - "use_template": "Szablon", - "save_as_template": "Zapisz jako szablon", - "validation": { - "recipient_required": "Dodaj odbiorcę, aby wysłać", - "body_required": "Napisz wiadomość lub załącz plik", - "attachments_uploading": "Załączniki wciąż się przesyłają — wiadomość zostanie wysłana po zakończeniu", - "attachment_upload_failed": "Nie wysłano - nie udało się przesłać załącznika. Usuń go i spróbuj ponownie." - }, - "upload_progress": "Przesyłanie {uploaded} / {total}", - "upload_cancel": "Anuluj przesyłanie", - "upload_failed": "Nie udało się przesłać {filename}", - "attachment_too_large": "„{name}” jest większy, niż pozwala serwer ({max} na plik)", - "attachments_total_too_large": "Załączniki przekroczyłyby limit serwera {max} na wiadomość", - "drop_files": "Upuść pliki, aby załączyć", - "show_less": "Pokaż mniej", - "send_failed": "Nie udało się wysłać wiadomości", - "send_timeout": "Serwer przestał odpowiadać. Przed ponownym wysłaniem sprawdź folder Wysłane — wiadomość mogła już zostać wysłana.", - "continue_draft": "Kontynuuj szkic", - "close_draft_title": "Zapisać czy odrzucić szkic?", - "close_draft_message": "Masz niezapisane zmiany. Czy chcesz zapisać jako szkic czy odrzucić?", - "smime_sign_on": "Podpisywanie S/MIME włączone", - "smime_sign_off": "Włącz podpisywanie S/MIME", - "smime_encrypt_on": "Szyfrowanie S/MIME włączone", - "smime_encrypt_off": "Włącz szyfrowanie S/MIME", - "smime_encrypt_unavailable": "Szyfrowanie S/MIME niedostępne – brak certyfikatów odbiorców", - "smime_unlock_title": "Odblokuj klucz S/MIME", - "smime_unlock_message": "Wprowadź hasło, aby odblokować klucz podpisywania S/MIME.", - "smime_unlock_button": "Odblokuj", - "smime_passphrase_placeholder": "Hasło", - "forgot_attachment": { - "title": "Czy zapomniałeś o załączniku?", - "message": "Twoja wiadomość wspomina o \"{keyword}\", ale nie załączono żadnego pliku. Wysłać mimo to?", - "send_anyway": "Wyślij mimo to", - "back": "Wróć do edycji" - }, - "empty_subject": { - "title": "Wysłać bez tematu?", - "message": "Ta wiadomość nie ma tematu. Wysłać mimo to?", - "dont_ask_again": "Nie pytaj ponownie", - "send_anyway": "Wyślij mimo to", - "back": "Wróć do edycji" - }, - "add_link": "Dodaj link", - "link_url_prompt": "Wprowadź adres URL", - "sending": "Wysyłanie...", - "schedule_send": "Zaplanuj wysyłkę", - "schedule_send_description": "Wybierz, kiedy serwer ma wysłać tę wiadomość.", - "schedule_send_required": "Wybierz datę i godzinę.", - "schedule_send_invalid": "Wprowadź prawidłową datę i godzinę.", - "schedule_send_future": "Wybierz datę i godzinę w przyszłości.", - "schedule_send_too_late": "Ten czas jest późniejszy, niż pozwala serwer.", - "schedule_send_unsupported": "Zaplanowana wysyłka nie jest obsługiwana dla tego konta.", - "schedule_send_cleanup_warning": "Zaplanowana wysyłka została utworzona, ale czyszczenie wersji roboczej nie powiodło się.", - "send_delay_unsupported": "Opóźnienie wysyłki nie jest obsługiwane dla tego konta.", - "send_delay_unsupported_confirm": "To konto nie obsługuje opóźnienia wysyłki. Wysłać natychmiast?", - "recipient_edit_email": "Edytuj adres e-mail", - "recipient_edit_name": "Edytuj wyświetlaną nazwę", - "recipient_email_placeholder": "Adres e-mail", - "recipient_name_placeholder": "Wyświetlana nazwa", - "autocomplete_search_server": "Szukaj na serwerze", - "autocomplete_searching": "Wyszukiwanie...", - "toolbar": { - "bold": "Pogrubienie", - "italic": "Kursywa", - "underline": "Podkreślenie", - "strikethrough": "Przekreślenie", - "text_color": "Kolor tekstu", - "remove_color": "Usuń kolor", - "heading_1": "Nagłówek 1", - "heading_2": "Nagłówek 2", - "bullet_list": "Lista punktowana", - "ordered_list": "Lista numerowana", - "quote": "Cytat", - "code_block": "Blok kodu", - "align_left": "Wyrównaj do lewej", - "align_center": "Wyśrodkuj", - "align_right": "Wyrównaj do prawej", - "text_direction": "Kierunek tekstu (RTL/LTR)", - "link": "Link", - "table": "Tabela", - "clear_formatting": "Wyczyść formatowanie", - "undo": "Cofnij", - "redo": "Ponów", - "add_row_above": "Dodaj wiersz powyżej", - "add_row_below": "Dodaj wiersz poniżej", - "add_column_before": "Dodaj kolumnę przed", - "add_column_after": "Dodaj kolumnę po", - "delete_row": "Usuń wiersz", - "delete_column": "Usuń kolumnę", - "toggle_header_row": "Przełącz wiersz nagłówka", - "delete_table": "Usuń tabelę", - "pick_size": "Wybierz rozmiar", - "font_size": "Rozmiar czcionki", - "font_size_default": "Domyślny", - "background_color": "Kolor tła", - "remove_background_color": "Usuń kolor tła" - }, - "send_filing_warning": "Wysłano - ale późniejsze porządkowanie nie powiodło się, może pozostać nieaktualna wersja robocza." - }, - "confirm_dialog": { - "confirm": "Potwierdź", - "cancel": "Anuluj" - }, - "common": { - "loading": "Ładowanie...", - "error": "Błąd", - "success": "Sukces", - "cancel": "Anuluj", - "save": "Zapisz", - "delete": "Usuń", - "edit": "Edytuj", - "close": "Zamknij", - "search": "Szukaj", - "refresh": "Odśwież", - "settings": "Ustawienia", - "help": "Pomoc", - "logout": "Wyloguj", - "yes": "Tak", - "no": "Nie", - "unknown": "Nieznany", - "app_title": "Webmail", - "reconnecting": "Utracono połączenie. Próba ponownego połączenia…", - "rate_limited_title": "Uwierzytelnianie serwera jest tymczasowo ograniczone.", - "rate_limited_detail": "Bulwark wstrzymał żądania w tle, aby uniknąć blokady. Ponowna próba za {seconds}s.", - "rate_limited_action_title": "Żądanie wstrzymane, aby uniknąć blokady.", - "rate_limited_action_detail": "Bulwark czeka na zakończenie okresu blokady serwera przed wysłaniem kolejnych uwierzytelnionych żądań. Spróbuj ponownie za {seconds}s.", - "pull_to_refresh": "Pociągnij, aby odświeżyć", - "release_to_refresh": "Puść, aby odświeżyć", - "refreshing": "Odświeżanie…" - }, - "notifications": { - "email_sent": "Wiadomość wysłana pomyślnie", - "email_deleted": "Wiadomość usunięta", - "email_archived": "Wiadomość zarchiwizowana", - "emails_deleted": "{count, plural, one {# wiadomość usunięta} few {# wiadomości usunięte} many {# wiadomości usuniętych} other {# wiadomości usuniętych}}", - "emails_archived": "{count, plural, one {# wiadomość zarchiwizowana} few {# wiadomości zarchiwizowane} many {# wiadomości zarchiwizowanych} other {# wiadomości zarchiwizowanych}}", - "error_archiving": "Nie udało się zarchiwizować wiadomości", - "error_updating": "Nie udało się zaktualizować wiadomości", - "email_starred": "Wiadomość oznaczona gwiazdką", - "email_unstarred": "Gwiazdka usunięta", - "email_marked_read": "Wiadomość oznaczona jako przeczytana", - "email_marked_unread": "Wiadomość oznaczona jako nieprzeczytana", - "copied_to_clipboard": "Skopiowano do schowka", - "source_copied": "Źródło skopiowane do schowka", - "error_sending": "Nie udało się wysłać wiadomości", - "error_deleting": "Nie udało się usunąć wiadomości", - "error_loading": "Nie udało się załadować wiadomości", - "new_email": "Nowa wiadomość", - "new_email_from": "Od {sender}", - "click_to_view": "Kliknij, aby wyświetlić", - "email_moved": "Wiadomość przeniesiona", - "emails_moved": "{count} wiadomości przeniesionych", - "moved_to_mailbox": "Przeniesiono do {mailbox}", - "move_failed": "Przenoszenie nie powiodło się", - "move_error": "Nie można przenieść wiadomości do wybranego folderu", - "email_tagged": "Wiadomość oznaczona etykietą", - "emails_tagged": "{count} wiadomości oznaczonych etykietą", - "tag_failed": "Oznaczanie etykietą nie powiodło się", - "identity_created": "Tożsamość utworzona pomyślnie", - "identity_updated": "Tożsamość zaktualizowana pomyślnie", - "identity_deleted": "Tożsamość usunięta", - "identity_set_primary": "Główna tożsamość zaktualizowana", - "identity_create_failed": "Nie udało się utworzyć tożsamości: {error}", - "identity_update_failed": "Nie udało się zaktualizować tożsamości: {error}", - "identity_delete_failed": "Nie udało się usunąć tożsamości: {error}", - "identity_unauthorized": "Nie masz uprawnień do wysyłania z tego adresu e-mail", - "identity_not_found": "Nie znaleziono tożsamości", - "vacation_saved": "Ustawienia autorespondera wakacyjnego zapisane", - "vacation_save_failed": "Nie udało się zapisać ustawień autorespondera wakacyjnego", - "filters_saved": "Filtry zapisane pomyślnie", - "filters_save_failed": "Nie udało się zapisać filtrów", - "filters_deleted": "Reguła filtra usunięta", - "templates_exported": "Szablony wyeksportowane pomyślnie", - "templates_imported": "{count, plural, one {# szablon zaimportowany} other {# szablonów zaimportowanych}}", - "templates_import_errors": "Niektóre szablony nie mogły zostać zaimportowane", - "templates_import_empty": "Nie znaleziono szablonów w pliku", - "export_email_error": "Nie udało się wyeksportować wiadomości", - "import_email_success": "Wiadomość zaimportowana pomyślnie", - "import_email_error": "Nie udało się zaimportować wiadomości" - }, - "date": { - "today": "Dzisiaj", - "yesterday": "Wczoraj", - "this_week": "Ten tydzień", - "last_week": "Poprzedni tydzień", - "this_month": "Ten miesiąc", - "older": "Starsze", - "just_now": "Przed chwilą", - "minutes_ago": "{count} minutę temu", - "minutes_ago_plural": "{count} minut temu", - "hours_ago": "{count} godzinę temu", - "hours_ago_plural": "{count} godzin temu", - "days_ago": "{count} dzień temu", - "days_ago_plural": "{count} dni temu" - }, - "language": { - "title": "Język", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "polish": "Polski", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Wybierz język", - "switch_to_english": "Przełącz na angielski", - "switch_to_french": "Przełącz na francuski", - "switch_to_japanese": "Przełącz na japoński", - "switch_to_spanish": "Przełącz na hiszpański", - "switch_to_italian": "Przełącz na włoski", - "switch_to_german": "Przełącz na niemiecki", - "switch_to_dutch": "Przełącz na niderlandzki", - "switch_to_polish": "Przełącz na polski", - "switch_to_portuguese": "Przełącz na portugalski", - "switch_to_russian": "Przełącz na rosyjski", - "switching": "Zmiana języka...", - "switch": "Zmień język", - "current": "Bieżący język", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Przełącz na perski", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Przełącz na mongolski" - }, - "settings": { - "title": "Ustawienia", - "back_to_mail": "Powrót do poczty", - "save_success": "Ustawienia zostały zapisane", - "import_success": "Ustawienia zostały zaimportowane", - "import_error": "Nie udało się zaimportować ustawień", - "reset_confirm": "Czy na pewno chcesz przywrócić domyślne ustawienia?", - "unsaved_changes": "Masz niezapisane zmiany", - "discard_changes": "Odrzucić niezapisane zmiany?", - "discard": "Odrzuć", - "keep_editing": "Kontynuuj edycję", - "tabs": { - "appearance": "Wygląd", - "language": "Język, region i czas", - "email": "Zachowanie poczty e-mail", - "composer": "Redagowanie", - "privacy": "Prywatność i bezpieczeństwo", - "account": "Konto", - "identities": "Tożsamości", - "vacation": "Autoresponder urlopowy", - "advanced": "Zaawansowane", - "calendar": "Kalendarz", - "filters": "Filtry", - "templates": "Szablony", - "folders": "Foldery", - "keywords": "Etykiety", - "security": "Bezpieczeństwo", - "files": "Pliki", - "contacts": "Kontakty", - "encryption": "Szyfrowanie", - "protocol_handlers": "Aplikacje domyślne", - "sidebar_apps": "Aplikacje paska bocznego", - "notifications": "Powiadomienia", - "layout": "Układ", - "reading": "Czytanie", - "composing": "Tworzenie", - "downloads": "Pobrane", - "content_senders": "Treść i nadawcy", - "about_data": "O programie i dane", - "debug": "Debugowanie" - }, - "tab_groups": { - "general": "Ogólne", - "account": "Konto i tożsamość", - "organization": "Organizacja poczty", - "apps": "Aplikacje", - "system": "System", - "appearance": "Wygląd", - "mail": "Poczta", - "privacy": "Prywatność i bezpieczeństwo", - "advanced": "Zaawansowane" - }, - "appearance": { - "message_list_order": { - "label": "Kolejność listy wiadomości", - "description": "Jak wiadomości są uporządkowane na liście. Sortowanie wykonuje serwer, więc obejmuje cały folder, a nie tylko załadowane wiadomości.", - "preset": { - "chronological": "Chronologicznie (najnowsze najpierw)", - "unread_first": "Nieprzeczytane najpierw", - "starred_first": "Oznaczone gwiazdką najpierw", - "tagged_first": "Z etykietą najpierw", - "custom": "Własna" - }, - "tag_label": "Etykieta", - "advanced_show": "Zaawansowane: edytuj poziomy sortowania", - "advanced_hide": "Ukryj zaawansowane", - "criterion_label": "Kryterium poziomu {n}", - "direction_label": "Kierunek poziomu {n}", - "criteria": { - "unread": "Nieprzeczytane", - "starred": "Z gwiazdką", - "tag": "Etykieta", - "receivedAt": "Data odebrania", - "sentAt": "Data wysłania", - "from": "Nadawca", - "subject": "Temat", - "size": "Rozmiar" - }, - "direction": { - "unread_first": "Nieprzeczytane najpierw", - "read_first": "Przeczytane najpierw", - "starred_first": "Z gwiazdką najpierw", - "unstarred_first": "Bez gwiazdki najpierw", - "tagged_first": "Z etykietą najpierw", - "untagged_first": "Bez etykiety najpierw", - "newest_first": "Najnowsze najpierw", - "oldest_first": "Najstarsze najpierw", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Największe najpierw", - "smallest_first": "Najmniejsze najpierw" - }, - "add_level": "Dodaj poziom", - "remove_level": "Usuń poziom", - "move_up": "Przenieś wyżej", - "move_down": "Przenieś niżej", - "tiebreak_note": "Do trzech poziomów; kolejne poziomy rozstrzygają remisy. „Najnowsze najpierw” jest zawsze stosowane na końcu.", - "scope": { - "label": "Zastosuj do wszystkich folderów", - "description": "Gdy wyłączone, kolejność dotyczy tylko Odebranych; pozostałe foldery pozostają chronologiczne." - }, - "unsupported_note": "Twój serwer poczty nie obsługuje sortowania według stanu przeczytania, gwiazdek ani etykiet. Te poziomy są pomijane, a lista pozostaje chronologiczna." - }, - "title": "Wygląd", - "description": "Dostosuj wygląd i działanie swojej poczty webowej", - "theme": { - "label": "Motyw", - "description": "Wybierz preferowany schemat kolorów", - "light": "Jasny", - "dark": "Ciemny", - "system": "Systemowy" - }, - "language": { - "label": "Język", - "description": "Wybierz preferowany język" - }, - "font_size": { - "label": "Rozmiar czcionki", - "description": "Dostosuj rozmiar tekstu dla lepszej czytelności", - "small": "Mały", - "medium": "Średni", - "large": "Duży" - }, - "list_density": { - "label": "Gęstość", - "description": "Kontroluj odstępy i wypełnienie w całym interfejsie", - "extra_compact": "Bardzo kompaktowy", - "compact": "Kompaktowy", - "regular": "Zwykły", - "comfortable": "Wygodny" - }, - "animations": { - "label": "Włącz animacje", - "description": "Pokazuj płynne przejścia i efekty" - }, - "toolbar_position": { - "label": "Pozycja paska narzędzi", - "description": "Gdzie wyświetlać przyciski akcji wiadomości (Odpowiedz, Archiwizuj, Usuń itp.)", - "top": "U góry", - "below_subject": "Pod tematem" - }, - "toolbar_labels": { - "label": "Pokazuj etykiety paska narzędzi", - "description": "Wyświetlaj etykiety tekstowe obok ikon paska narzędzi. Wyłącz tę opcję, aby oszczędzić miejsce, gdy znasz już ikony." - }, - "hide_account_switcher": { - "label": "Ukryj przełącznik kont na pasku bocznym", - "description": "Ukryj wybór konta u góry paska folderów. Nadal możesz przełączać konta z dolnego paska nawigacyjnego." - }, - "show_rail_account_list": { - "label": "Pokazuj awatary kont na pasku nawigacyjnym", - "description": "Wyświetlaj osobne ikony kont na dole paska nawigacyjnego, aby szybko się przełączać, z przyciskiem wylogowania poniżej." - }, - "unified_mailbox": { - "label": "Wspólna skrzynka", - "description": "Wyświetlaj połączone foldery (Odebrane, Wysłane itp.) ze wszystkich połączonych kont", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Uwzględnij skrzynki grupowe", - "description": "Dodaj również udostępnione/grupowe skrzynki do widoku wspólnego." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Kolorowe ikony paska bocznego", - "description": "Koloruj ikony folderów i tagów według typu (niebieska Skrzynka odbiorcza, czerwona Spam, zielona Wysłane itp.). Wyłącz, aby uzyskać monochromatyczny pasek boczny." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Pokaż łączną liczbę wiadomości", - "description": "Pokazuje łączną liczbę wiadomości obok folderów i etykiet, obok liczby nieprzeczytanych. Wyłącz, aby pokazywać tylko nieprzeczytane." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Interfejs Pro (eksperymentalny)", - "description": "Układ dla zaawansowanych użytkowników (tylko na komputerze) z przeglądaniem wiadomości w wielu kartach i obiegami pracy między kontami. Standardowy interfejs pozostaje nietknięty; możesz wrócić w dowolnej chwili.", - "open_label": "Otwórz interfejs Pro", - "back_to_standard": "Powrót do standardu" - }, - "cross_unread": { - "label": "Wszystkie konta: Nieprzeczytane", - "description": "Pokazuje pozycję w sekcji Wszystkie konta z nieprzeczytaną pocztą ze wszystkich kont, obejmującą wszystkie foldery oprócz spamu, wysłanych, archiwum, kosza i roboczych." - }, - "cross_starred": { - "label": "Wszystkie konta: Oznaczone gwiazdką", - "description": "Pokazuje pozycję w sekcji Wszystkie konta z pocztą oznaczoną gwiazdką ze wszystkich kont, obejmującą wszystkie foldery oprócz spamu, wysłanych, archiwum, kosza i roboczych." - }, - "cross_all": { - "label": "Wszystkie konta: Cała poczta", - "description": "Pokazuje pozycję w sekcji Wszystkie konta z całą pocztą ze wszystkich kont, obejmującą wszystkie foldery oprócz spamu, wysłanych, archiwum, kosza i roboczych." - } - }, - "keywords": { - "title": "Etykiety e-mail", - "description": "Zdefiniuj etykiety do organizowania e-maili za pomocą kolorów. Są one przechowywane jako słowa kluczowe JMAP na serwerze.", - "add_keyword": "Dodaj etykietę", - "label_field": "Nazwa wyświetlana", - "label_placeholder": "np. Praca, Osobiste, Pilne", - "id_field": "ID etykiety", - "id_placeholder": "np. praca, osobiste", - "color_field": "Kolor", - "id_exists": "Ten ID etykiety już istnieje", - "edit": "Edytuj etykietę", - "delete": "Usuń etykietę", - "save": "Zapisz", - "add": "Dodaj", - "cancel": "Anuluj", - "migrating": "Aktualizowanie etykiety w istniejących e-mailach…", - "migration_error": "Nie udało się zaktualizować etykiety w istniejących e-mailach", - "nesting": { - "label": "Zagnieżdżone etykiety", - "description": "Zagnieżdżaj etykiety pod innymi etykietami i wyświetlaj je w panelu bocznym jako drzewo." - }, - "parent_field": "Etykieta nadrzędna", - "no_parent": "Brak etykiety nadrzędnej", - "too_long": "Ta ścieżka etykiety jest za długa (maksymalnie {max} znaków)", - "has_children_locked": "Pod tą etykietą zagnieżdżone są inne etykiety, więc jej nazwa i etykieta nadrzędna są zablokowane. Najpierw je przenieś lub usuń.", - "has_children_delete": "Najpierw usuń etykiety zagnieżdżone pod tą", - "visibility_field": "Widoczność w panelu bocznym", - "visibility": { - "show": "Pokaż", - "unread": "Pokaż przy nieprzeczytanych", - "hide": "Ukryj" - }, - "unrecognized": { - "title": "Odzyskaj etykiety z serwera", - "description": "Nazwy i kolory etykiet są zapisane w tej przeglądarce, ale same etykiety znajdują się w wiadomościach jako słowa kluczowe JMAP. Przeskanuj pocztę w poszukiwaniu słów kluczowych, dla których nie ma tu etykiety, i dodaj je ponownie.", - "scan": "Szukaj etykiet", - "rescan": "Skanuj ponownie", - "scanning": "Skanowanie… {scanned} z {total} wiadomości", - "none": "Wszystkie etykiety w Twoich wiadomościach są już tutaj zdefiniowane.", - "messages": "{count, plural, one {# wiadomość} few {# wiadomości} other {# wiadomości}}", - "add_all": "Dodaj wszystkie ({count})", - "partial": "Przeskanowano tylko {scanned} najnowszych z {total} wiadomości. Etykiety używane wyłącznie w starszej poczcie mogą zostać pominięte.", - "error": "Nie udało się przeskanować poczty w poszukiwaniu etykiet." - } - }, - "notifications": { - "test_sound": "Przetestuj dźwięk powiadomienia", - "sounds": { - "default": "Domyślny (Beep)", - "cheerful": "Pogodny", - "involved": "Dynamiczny", - "swift": "Szybki gest", - "relax": "Relaks" - }, - "sound_selection": { - "title": "Dźwięk powiadomienia", - "description": "Wybierz dźwięk odtwarzany dla powiadomień", - "choose": "Dźwięk", - "choose_desc": "Wybierz ton powiadomienia i kliknij ikonę głośnika, aby go odsłuchać" - }, - "email": { - "title": "Powiadomienia e-mail", - "description": "Skonfiguruj powiadomienia dla przychodzących wiadomości e-mail", - "enabled": "Powiadomienia e-mail", - "enabled_desc": "Pokazuj powiadomienia po nadejściu nowych wiadomości e-mail", - "sound": "Dźwięk powiadomienia", - "sound_desc": "Odtwarzaj sygnał dźwiękowy po nadejściu nowych wiadomości e-mail", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Powiadomienia kalendarza", - "description": "Skonfiguruj powiadomienia dla wydarzeń kalendarza", - "enabled": "Powiadomienia o wydarzeniach", - "enabled_desc": "Pokazuj alerty o nadchodzących wydarzeniach kalendarza", - "sound": "Dźwięk powiadomienia", - "sound_desc": "Odtwarzaj sygnał dźwiękowy dla przypomnień kalendarza", - "invitation_parsing": "Rozpoznawaj zaproszenia e-mail", - "invitation_parsing_desc": "Wykrywaj zaproszenia kalendarzowe w załącznikach wiadomości e-mail i pokazuj akcje kalendarza" - }, - "push": { - "confirm_disable_message": "To urządzenie przestanie otrzymywać powiadomienia, gdy strona jest zamknięta.", - "confirm_disable_title": "Wyłączyć powiadomienia w tle?", - "description": "Otrzymuj powiadomienia systemowe o nowych wiadomościach, gdy ta strona jest zamknięta. Dostarczane przez przekaźnik push Bulwark; przekaźnik nigdy nie widzi treści wiadomości.", - "disable": "Wyłącz", - "enable": "Włącz", - "dismiss_aria": "Zamknij monit powiadomień", - "ios_hint": "W systemie iOS najpierw zainstaluj stronę na ekranie głównym – Safari dostarcza Web Push tylko zainstalowanym PWA.", - "reenable": "Zarejestruj ponownie", - "relay_desc": "Domyślnie używa hostowanego przekaźnika Bulwark. Zmień tylko jeśli hostujesz samodzielnie.", - "relay_label": "Przekaźnik push", - "relay_locked": "Ustawione przez administratora", - "relay_locked_desc": "Adres URL przekaźnika push został ustawiony przez administratora i nie można go zmienić.", - "status_active": "Aktywne na tym urządzeniu", - "status_busy": "Przetwarzanie…", - "status_inactive": "Niewłączone na tym urządzeniu", - "status_unsupported": "Ta przeglądarka nie obsługuje Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Powiadomienia w tle", - "devices_title": "Zarejestrowane urządzenia", - "devices_desc": "Wszystkie urządzenia zarejestrowane do odbioru powiadomień w tle dla tego konta. Unieważnienie natychmiast wstrzymuje ich odbiór.", - "devices_loading": "Wczytywanie zarejestrowanych urządzeń…", - "devices_empty": "Żadne urządzenie nie jest zarejestrowane do powiadomień w tle.", - "devices_error": "Nie udało się wczytać zarejestrowanych urządzeń.", - "devices_refresh": "Odśwież", - "device_this": "To urządzenie", - "device_other": "Inne urządzenie", - "device_status_active": "Dostarcza powiadomienia", - "device_status_inactive": "Nigdy nie dostarczyło powiadomienia", - "device_status_unknown": "Nieznane temu przekaźnikowi", - "device_expires": "Wygasa {date}", - "revoke": "Unieważnij", - "confirm_revoke_title": "Unieważnić to urządzenie?", - "confirm_revoke_message": "Natychmiast przestanie odbierać powiadomienia w tle. Ponowne włączenie push na tym urządzeniu zarejestruje je od nowa.", - "confirm_revoke_message_this": "To urządzenie natychmiast przestanie odbierać powiadomienia w tle." - } - }, - "language_region": { - "title": "Język, region i czas", - "description": "Język, format daty, format godziny i inne preferencje regionalne", - "language": { - "label": "Język", - "description": "Wybierz preferowany język", - "english": "Angielski", - "french": "Francuski" - }, - "date_format": { - "label": "Format daty", - "description": "Jak daty są wyświetlane na liście e-maili", - "smart": "Inteligentny (regionalny)", - "relative": "Względny (1 g temu, 2 d temu)", - "full": "Zawsze pełna data", - "preview_today": "Dzisiaj:", - "preview_this_week": "W tym tygodniu:", - "preview_older": "Starsze:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Format czasu", - "description": "Wybierz zegar 12-godzinny lub 24-godzinny", - "12h": "12-godzinny", - "24h": "24-godzinny" - }, - "first_day": { - "label": "Pierwszy dzień tygodnia", - "description": "Rozpocznij tydzień w niedzielę lub poniedziałek", - "sunday": "Niedziela", - "monday": "Poniedziałek" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Czyść wyszukiwanie przy zmianie folderu", - "description": "Kliknięcie folderu (Odebrane, Wysłane…) czyści aktywne wyszukiwanie i otwiera ten folder. Gdy wyłączone, wyszukiwanie pozostaje i jest ponawiane w wybranym folderze." - }, - "request_read_receipt": { - "label": "Domyślnie żądaj potwierdzeń przeczytania", - "description": "Włącz wcześniej żądanie potwierdzenia przeczytania podczas tworzenia nowej wiadomości." - }, - "read_receipt_response": { - "label": "Odpowiadaj na żądania potwierdzenia przeczytania", - "description": "Co zrobić, gdy przychodząca wiadomość prosi o potwierdzenie przeczytania.", - "ask": "Pytaj za każdym razem", - "always": "Zawsze wysyłaj", - "never": "Nigdy nie wysyłaj" - }, - "title": "Zachowanie poczty e-mail", - "description": "Skonfiguruj sposób obsługi wiadomości e-mail", - "mark_read": { - "label": "Oznacz jako przeczytane", - "description": "Kiedy oznaczać wiadomości e-mail jako przeczytane po otwarciu", - "instant": "Natychmiast", - "delay_3s": "Po 3 sekundach", - "delay_5s": "Po 5 sekundach", - "never": "Nigdy" - }, - "delete_action": { - "label": "Akcja usuwania", - "description": "Co ma się stać po usunięciu wiadomości e-mail", - "trash": "Przenieś do kosza", - "trash_and_read": "Przenieś do kosza i oznacz jako przeczytane", - "permanent": "Usuń trwale", - "warning": "Wiadomości e-mail zostaną trwale usunięte i nie będzie można ich odzyskać. Ta akcja jest nieodwracalna." - }, - "message_spacing": { - "label": "Odstępy wiadomości", - "description": "Margines wokół treści wiadomości w czytniku", - "auto": "Automatycznie (zalecane)", - "always": "Zawsze dodawaj odstępy", - "edge": "Od krawędzi do krawędzi" - }, - "plain_text_font": { - "label": "Czcionka zwykłego tekstu", - "description": "Czcionka używana dla wiadomości w zwykłym tekście", - "mono": "O stałej szerokości", - "sans": "Normalna (jak aplikacja)" - }, - "archive_mode": { - "label": "Archiwizuj w", - "description": "Jak organizować wiadomości e-mail podczas archiwizacji", - "single": "Jeden folder", - "year": "Folder na każdy rok", - "month": "Folder na każdy miesiąc", - "reorganize": "Zreorganizuj istniejące archiwum", - "reorganize_success": "{count, plural, =0 {Brak wiadomości do reorganizacji} =1 {Zreorganizowano 1 wiadomość} other {Zreorganizowano # wiadomości}}", - "reorganize_error": "Nie udało się zreorganizować archiwum" - }, - "permanently_delete_junk": { - "label": "Trwale usuwaj spam", - "description": "Trwale usuwaj wiadomości z folderu Spam/Niechciane zamiast przenosić je do Kosza" - }, - "mail_layout": { - "label": "Układ poczty", - "description": "Wybierz między klasycznym podzielonym panelem czytania a stylem czytania skupionym jak w Gmailu.", - "split": "Widok podzielony", - "split_description": "Lista wiadomości i panel czytania pozostają widoczne obok siebie.", - "focus": "Skupiona lista", - "focus_description": "Pokazuj jeden wiersz na wiadomość i otwieraj pocztę na pełną szerokość, pozostawiając widoczny pasek folderów.", - "horizontal": "Okienko czytania na dole", - "horizontal_description": "Pokaż listę wiadomości u góry i otwórz wybraną wiadomość w okienku czytania pod nią." - }, - "show_preview": { - "label": "Pokaż tekst podglądu", - "description": "Wyświetlaj podgląd wiadomości e-mail na liście", - "focus_description": "Wyświetlaj wbudowany tekst podglądu w jednoliniowej skupionej liście wiadomości" - }, - "disable_threading": { - "label": "Wyłącz grupowanie konwersacji", - "description": "Pokazuj wiadomości e-mail jako pojedyncze wiadomości zamiast grupowania według konwersacji" - }, - "plain_text_mode": { - "label": "Tylko zwykły tekst", - "description": "Wyłącz edytor tekstu sformatowanego i wysyłaj wszystkie wiadomości e-mail tylko jako zwykły tekst, w tym odpowiedzi i przekazywanie dalej" - }, - "auto_select_reply_identity": { - "label": "Automatycznie wybieraj adres odpowiedzi", - "description": "Podczas odpowiadania automatycznie przełączaj adres nadawcy na tożsamość, która pierwotnie otrzymała wiadomość" - }, - "reply_identity_match": { - "label": "Dopasowanie adresu odbioru", - "description": "Które adresy odbioru są uznawane za Twoje. „Tylko dokładny adres” wybiera wyłącznie jedną ze skonfigurowanych tożsamości; „ta sama domena” traktuje dodatkowo każdy inny adres w domenach Twoich tożsamości jako alias catch-all i przepisuje na niego nagłówek Od. Wybierz dokładny adres, jeśli te adresy to listy dystrybucyjne.", - "exact": "Tylko dokładny adres", - "domain": "Dowolny adres w moich domenach" - }, - "signature_position": { - "label": "Pozycja podpisu", - "description": "Gdzie wstawić podpis w odpowiedziach i wiadomościach przekazanych dalej. Nad cytowanym tekstem brzmi naturalnie jako zakończenie odpowiedzi; pod nim zachowuje oryginalną wiadomość w całości.", - "above_quote": "Przed cytowanym tekstem", - "below_quote": "Po cytowanym tekście" - }, - "signature_separator": { - "label": "Separator podpisu", - "description": "Poprzedź podpis standardową linią separatora \"-- \" (RFC 3676). Wyłącz, jeśli chcesz przejść bezpośrednio z treści wiadomości do podpisu." - }, - "sub_address_delimiter": { - "label": "Separator sub-adresu", - "description": "Znak oddzielający Twoją nazwę użytkownika od tagu sub-adresu. Użyj separatora zgodnego z Twoim serwerem pocztowym (np. user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "Niestandardowy…", - "custom_input_label": "Niestandardowy znak separatora" - }, - "attachment_click_action": { - "label": "Akcja po kliknięciu załącznika", - "description": "Wybierz, czy kliknięcie załącznika pliku ma pokazać podgląd, czy od razu go pobrać", - "preview": "Podgląd, gdy to możliwe", - "download": "Pobierz natychmiast" - }, - "attachment_position": { - "label": "Pozycja załączników", - "description": "Gdzie wyświetlać załączniki w nagłówku wiadomości e-mail", - "beside-sender": "Obok nadawcy", - "below-header": "Pod nagłówkiem" - }, - "emails_per_page": { - "10": "10 wiadomości", - "25": "25 wiadomości", - "50": "50 wiadomości", - "100": "100 wiadomości", - "label": "Wiadomości na stronę", - "description": "Liczba wiadomości e-mail ładowanych jednorazowo" - }, - "always_light_mode": { - "label": "Zawsze wyświetlaj wiadomości w trybie jasnym", - "description": "Renderuj treść wiadomości e-mail w trybie jasnym nawet wtedy, gdy aplikacja jest w trybie ciemnym, aby uniknąć problemów z konwersją trybu ciemnego" - }, - "external_content": { - "label": "Treści zewnętrzne", - "description": "Jak obsługiwać obrazy i treści zewnętrzne", - "ask": "Zawsze pytaj", - "block": "Zawsze blokuj", - "allow": "Zawsze zezwalaj" - }, - "trusted_senders": { - "label": "Zaufani nadawcy", - "description": "Zarządzaj nadawcami, których obrazy ładują się automatycznie", - "count_zero": "Brak", - "count_one": "1 nadawca", - "count_other": "{count} nadawców", - "modal_title": "Zaufani nadawcy", - "empty_title": "Brak zaufanych nadawców", - "empty_description": "Podczas wyświetlania wiadomości e-mail z zablokowanymi obrazami kliknij \"Zawsze ufaj temu nadawcy\", aby dodać go tutaj.", - "add_manually": "Dodaj nadawcę ręcznie", - "add_button": "Dodaj", - "add_placeholder": "Wprowadź adres e-mail", - "search_placeholder": "Szukaj nadawców...", - "no_results": "Żaden nadawca nie pasuje do wyszukiwania", - "remove": "Usuń", - "close": "Zamknij", - "invalid_email": "Wprowadź prawidłowy adres e-mail", - "already_added": "Ten nadawca jest już zaufany", - "save_error": "Nie udało się zapisać - sprawdź dziennik debugowania Kontaktów", - "use_address_book_label": "Synchronizuj z książką adresową", - "use_address_book_description": "Zapisuj zaufanych nadawców w osobnej książce „Zaufani nadawcy”, aby synchronizować ich na wszystkich urządzeniach" - }, - "hover_actions": { - "label": "Szybkie akcje po najechaniu", - "description": "Wybierz szybkie akcje widoczne po najechaniu na wiadomość e-mail na liście", - "delete": "Usuń", - "star": "Oznacz gwiazdką / usuń gwiazdkę", - "mark_read": "Oznacz jako przeczytane / nieprzeczytane", - "archive": "Archiwizuj", - "tag": "Etykieta", - "spam": "Oznacz jako spam", - "not_spam": "Nie spam", - "none_selected": "Nie wybrano żadnych akcji", - "mode_label": "Tryb wyświetlania", - "mode_inline": "Wbudowany", - "mode_floating": "Pływający", - "corner_label": "Pozycja pływająca", - "corner_top-left": "Lewy górny róg", - "corner_top-right": "Prawy górny róg", - "corner_bottom-left": "Lewy dolny róg", - "corner_bottom-right": "Prawy dolny róg" - }, - "default_mail_program": { - "label": "Domyślny program pocztowy", - "description": "Zarejestruj {appName} jako domyślny program pocztowy dla linków mailto:", - "button": "Ustaw jako domyślny", - "success": "Przeglądarka poprosiła o ustawienie jako domyślnego", - "error": "Twoja przeglądarka nie obsługuje tej funkcji" - }, - "attachment_reminder": { - "label": "Przypomnienie o załączniku", - "description": "Ostrzegaj przed wysłaniem, gdy wiadomość wspomina o załącznikach, ale żaden nie jest dołączony", - "keywords_label": "Słowa kluczowe wyzwalające", - "keywords_description": "Słowa lub frazy, które wyzwalają przypomnienie po znalezieniu w wiadomości", - "add_placeholder": "Dodaj słowo kluczowe...", - "add": "Add", - "remove": "Usuń" - }, - "empty_subject_warning": { - "label": "Ostrzeżenie o pustym temacie", - "description": "Poproś o potwierdzenie przed wysłaniem wiadomości bez tematu." - }, - "hide_inline_image_attachments": { - "label": "Ukryj obrazy osadzone z listy załączników", - "description": "Obrazy osadzone w treści wiadomości nie są wyświetlane jako osobne załączniki" - }, - "attachment_image_previews": { - "label": "Pokaż podgląd obrazów w załącznikach", - "description": "Wyświetlaj załączone obrazy jako miniatury zamiast ogólnych ikon plików" - }, - "send_delay": { - "label": "Cofnij wysyłkę / opóźnienie wysyłki", - "description": "Opóźnia normalne wysyłanie krótkim oknem po stronie serwera.", - "off": "Wyłączone", - "seconds": "{seconds} sekund", - "unsupported": "Bieżące konto nie zgłasza obsługi opóźnionej wysyłki. Ustawienie pozostanie zapisane dla innych kont." - }, - "return_to_list_after_action": { - "label": "Powrót do listy po usunięciu lub oznaczeniu jako nieprzeczytane", - "description": "Po usunięciu lub oznaczeniu otwartej wiadomości jako nieprzeczytanej wróć do listy wiadomości zamiast otwierać następną." - }, - "swipe_right_action": { - "label": "Akcja przy przesunięciu w prawo (mobilne)", - "description": "Na urządzeniach dotykowych akcja wykonywana po przesunięciu wiadomości w prawo na liście." - }, - "swipe_left_action": { - "label": "Akcja przy przesunięciu w lewo (mobilne)", - "description": "Na urządzeniach dotykowych akcja wykonywana po przesunięciu wiadomości w lewo na liście." - }, - "swipe_actions": { - "none": "Brak", - "archive": "Archiwizuj", - "delete": "Usuń", - "mark_read": "Przełącz przeczytane", - "star": "Przełącz gwiazdkę", - "spam": "Oznacz jako spam" - }, - "rtl_editing": { - "label": "Obsługa edycji od prawej do lewej", - "description": "Dodaje przycisk kierunku do paska narzędzi edytora, pozwalający ustawić akapity od lewej do prawej lub od prawej do lewej" - } - }, - "composer": { - "title": "Redagowanie", - "description": "Skonfiguruj ustawienia tworzenia wiadomości e-mail", - "autosave": { - "label": "Interwał autozapisu", - "description": "Jak często automatycznie zapisywać szkice", - "30s": "Co 30 sekund", - "1m": "Co minutę", - "2m": "Co 2 minuty", - "5m": "Co 5 minut" - }, - "send_confirmation": { - "label": "Potwierdzenie wysyłki", - "description": "Proś o potwierdzenie przed wysłaniem wiadomości e-mail" - }, - "default_reply": { - "label": "Domyślny tryb odpowiedzi", - "description": "Domyślna akcja po kliknięciu odpowiedzi", - "reply": "Odpowiedz", - "reply_all": "Odpowiedz wszystkim" - } - }, - "privacy": { - "title": "Prywatność i bezpieczeństwo", - "description": "Zarządzaj ustawieniami prywatności i bezpieczeństwa", - "external_images": { - "label": "Blokuj obrazy zewnętrzne", - "description": "Zapobiegaj śledzeniu przez zewnętrzne obrazy" - }, - "session_timeout": { - "label": "Limit czasu sesji", - "description": "Automatycznie wyloguj po okresie bezczynności", - "never": "Nigdy", - "30m": "30 minut", - "1h": "1 godzina", - "4h": "4 godziny" - }, - "clear_cache": { - "label": "Wyczyść pamięć podręczną", - "description": "Usuń dane z pamięci podręcznej i pliki tymczasowe", - "button": "Wyczyść pamięć podręczną", - "confirm": "Czy na pewno chcesz wyczyścić pamięć podręczną?", - "success": "Pamięć podręczna została wyczyszczona" - } - }, - "account": { - "title": "Konto", - "description": "Wyświetl informacje o swoim koncie", - "name_label": "Nazwa wyświetlana", - "username_label": "Nazwa użytkownika", - "account_type_label": "Typ konta", - "auth_method_label": "Uwierzytelnianie", - "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", - "auth_method_basic": "Hasło", - "demo_account": "Konto demo", - "email": { - "label": "Adres e-mail", - "value": "{email}" - }, - "server": { - "label": "Serwer JMAP", - "value": "{server}" - }, - "storage": { - "label": "Wykorzystanie przestrzeni", - "used": "{used} z {total} wykorzystano", - "percentage": "Wykorzystano {percent}%" - }, - "last_sync": { - "label": "Ostatnia synchronizacja", - "value": "{time}" - }, - "accounts": { - "title": "Zalogowane konta", - "description": "Przeciągnij, aby zmienić kolejność wyświetlania kont w menu rozwijanym", - "active": "Aktualnie aktywne konto", - "default_badge": "Konto domyślne", - "set_default": "Ustaw jako domyślne", - "switch_to": "Przełącz na to konto", - "move_up": "Przesuń w górę", - "move_down": "Przesuń w dół", - "drag_handle": "Przeciągnij, aby zmienić kolejność", - "add": "Dodaj konto" - }, - "shared_accounts": { - "title": "Udostępnione mnie", - "description": "Konta grupowe i udostępnione, którymi możesz zarządzać. Wybierz jedno, aby edytować jego filtry, odpowiedź wakacyjną, kalendarze i kontakty.", - "shared_label": "Konto udostępnione" - } - }, - "security": { - "title": "Bezpieczeństwo konta", - "description": "Zarządzaj hasłem, uwierzytelnianiem dwuskładnikowym i ustawieniami bezpieczeństwa", - "detecting": "Wykrywanie możliwości serwera...", - "not_available": "Zarządzanie bezpieczeństwem konta nie jest dostępne dla tego serwera pocztowego. Wymagane uprawnienia mogą być wyłączone. Szczegóły znajdziesz w dokumentacji.", - "password": { - "title": "Zmień hasło", - "current": "Aktualne hasło", - "new": "Nowe hasło", - "confirm": "Potwierdź nowe hasło", - "submit": "Zmień hasło", - "success": "Hasło zostało zmienione", - "error_title": "Zmiana hasła nie powiodła się", - "error_mismatch": "Nowe hasła nie są zgodne", - "error_min_length": "Hasło musi mieć co najmniej 8 znaków", - "error_generic": "Nie udało się zmienić hasła" - }, - "display_name": { - "label": "Nazwa wyświetlana", - "description": "Twoja nazwa widoczna na serwerze", - "placeholder": "Wprowadź swoją nazwę wyświetlaną", - "save": "Zapisz", - "success": "Nazwa wyświetlana została zaktualizowana", - "error": "Nie udało się zaktualizować nazwy wyświetlanej" - }, - "totp": { - "section_title": "Uwierzytelnianie dwuskładnikowe", - "label": "Uwierzytelnianie TOTP", - "description": "Dodaj dodatkową warstwę bezpieczeństwa za pomocą jednorazowego hasła opartego na czasie", - "active": "Włączone", - "inactive": "Wyłączone", - "enabled": "Uwierzytelnianie dwuskładnikowe włączone", - "disabled": "Uwierzytelnianie dwuskładnikowe wyłączone", - "enable_error": "Nie udało się włączyć 2FA", - "disable_error": "Nie udało się wyłączyć 2FA", - "setup_instructions": "Skopiuj ten adres URL do swojej aplikacji uwierzytelniającej (Google Authenticator, Authy itp.):", - "verification_code": "Kod weryfikacyjny", - "confirm": "Potwierdź", - "disable": "Wyłącz", - "disable_confirm_prompt": "Wprowadź hasło, aby wyłączyć uwierzytelnianie dwuskładnikowe.", - "password_required": "Hasło jest wymagane", - "code_required": "Kod weryfikacyjny jest wymagany", - "code_invalid": "Nieprawidłowy kod weryfikacyjny. Sprawdź aplikację uwierzytelniającą i spróbuj ponownie." - }, - "app_passwords": { - "title": "Hasła aplikacji", - "description": "Twórz hasła dla aplikacji, które nie obsługują uwierzytelniania dwuskładnikowego", - "add": "Dodaj", - "create": "Utwórz", - "cancel": "Anuluj", - "generate": "Generuj", - "name_label": "Nazwa aplikacji", - "name_placeholder": "np. Thunderbird, iPhone Mail", - "password_label": "Hasło (pozostaw puste, aby wygenerować automatycznie)", - "password_placeholder": "Automatycznie wygenerowane, jeśli puste", - "added": "Hasło aplikacji zostało utworzone", - "removed": "Hasło aplikacji zostało usunięte", - "add_error": "Nie udało się utworzyć hasła aplikacji", - "remove_error": "Nie udało się usunąć hasła aplikacji", - "none": "Brak skonfigurowanych haseł aplikacji", - "done": "Gotowe", - "expires_label": "Wygasa (opcjonalnie)", - "copy_now_warning": "Skopiuj to hasło teraz - nie zostanie ponownie wyświetlone.", - "allowed_ips_label": "Dozwolone adresy IP (opcjonalnie)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Oddzielone przecinkiem lub spacją. Pozostaw puste, aby zezwolić na dowolny IP." - }, - "api_keys": { - "title": "Klucze API", - "description": "Twórz klucze API dla skryptów i integracji komunikujących się bezpośrednio z serwerem", - "name_label": "Nazwa klucza", - "name_placeholder": "np. Skrypt kopii zapasowej, CI runner", - "copy_now_warning": "Skopiuj ten klucz API teraz - nie zostanie pokazany ponownie.", - "added": "Klucz API utworzony", - "removed": "Klucz API usunięty", - "add_error": "Nie udało się utworzyć klucza API", - "remove_error": "Nie udało się usunąć klucza API", - "none": "Brak skonfigurowanych kluczy API" - }, - "encryption": { - "section_title": "Szyfrowanie danych w spoczynku", - "label": "Szyfrowanie wiadomości e-mail", - "description": "Szyfruj wiadomości przechowywane na serwerze dla większej prywatności", - "active": "Szyfrowanie {type} włączone", - "inactive": "Wyłączone", - "enabled": "Szyfrowanie danych w spoczynku włączone", - "disabled_success": "Szyfrowanie danych w spoczynku wyłączone", - "error": "Nie udało się zaktualizować ustawień szyfrowania", - "algorithm_label": "Algorytm szyfrowania", - "encrypt_on_append": "Szyfruj nowe wiadomości podczas przesyłania", - "allow_spam_training": "Zezwalaj na trenowanie filtra spamu przed zaszyfrowaniem wiadomości", - "enable_encryption": "Włącz szyfrowanie", - "enable_tooltip": "Włącz szyfrowanie tym kluczem", - "configure_title": "Włącz szyfrowanie tym kluczem", - "enable_button": "Włącz", - "disable_tooltip": "Wyłącz szyfrowanie", - "enabled_success": "Szyfrowanie danych w spoczynku włączone" - }, - "email_client": { - "title": "Konfiguracja klienta poczty", - "description": "Użyj tych danych uwierzytelniających, aby skonfigurować klienta poczty na komputerze lub urządzeniu mobilnym (Thunderbird, Apple Mail, Outlook itp.)", - "jmap_username_label": "Nazwa użytkownika JMAP", - "copy": "Kopiuj", - "copied": "Skopiowano", - "password_instructions": "Użyj powyższej nazwy użytkownika JMAP wraz z hasłem aplikacji, aby zalogować się w kliencie poczty. Utwórz hasło aplikacji w sekcji powyżej, jeśli jeszcze tego nie zrobiłeś." - }, - "link_device": { - "title": "Połącz aplikację mobilną", - "description": "Zaloguj się do aplikacji mobilnej Bulwark Mail bez wpisywania czegokolwiek. Wygeneruj tutaj kod QR i zeskanuj go na ekranie logowania aplikacji.", - "generate": "Pokaż kod QR", - "regenerate": "Pokaż nowy kod", - "instructions": "Otwórz aplikację Bulwark Mail, na ekranie logowania stuknij \"Skanuj kod QR\" i skieruj aparat tutaj.", - "expires_in": "Ten kod wygaśnie za {seconds} sekund. Można go użyć tylko raz.", - "expired": "Ten kod wygasł.", - "generating": "Generowanie…", - "error": "Nie udało się utworzyć kodu parowania. Spróbuj ponownie." - }, - "public_keys": { - "name_label": "Nazwa klucza publicznego", - "name_placeholder": "np. Mój klucz", - "key_label": "Klucz publiczny (S/MIME lub PGP w formacie ASCII)", - "key_placeholder": "Wklej tutaj swój klucz publiczny", - "none": "Nie skonfigurowano żadnych kluczy publicznych" - } - }, - "identities": { - "title": "Tożsamości nadawcy", - "description": "Zarządzaj adresami e-mail, z których możesz wysyłać wiadomości", - "identities_count": { - "label": "Twoje tożsamości", - "description": "Adresy e-mail skonfigurowane do wysyłania", - "count_zero": "Brak tożsamości", - "count_one": "1 tożsamość", - "count_other": "{count} tożsamości" - }, - "manage": "Zarządzaj tożsamościami", - "sub_addressing": { - "label": "Subadresowanie", - "description": "Używaj tagów takich jak user+tag@domain.com, aby organizować pocztę przychodzącą", - "learn_more": "Dowiedz się więcej" - } - }, - "vacation": { - "title": "Autoresponder urlopowy", - "description": "Automatycznie odpowiadaj na przychodzące wiadomości e-mail podczas nieobecności", - "loading": "Ładowanie ustawień autorespondera urlopowego...", - "not_supported": "Twój serwer pocztowy nie obsługuje odpowiedzi urlopowych.", - "fetch_error": "Nie udało się załadować ustawień urlopowych. Spróbuj ponownie.", - "status": { - "label": "Autoresponder urlopowy", - "description": "Wysyłaj automatyczną odpowiedź do osób, które do Ciebie piszą", - "active": "Aktywny", - "inactive": "Nieaktywny" - }, - "date_range": { - "title": "Zakres dat", - "description": "Opcjonalnie ogranicz automatyczną odpowiedź do określonego okresu", - "start": "Data rozpoczęcia", - "start_description": "Pozostaw puste, aby nie ustawiać daty początkowej", - "end": "Data zakończenia", - "end_description": "Pozostaw puste, aby nie ustawiać daty końcowej" - }, - "message": { - "title": "Wiadomość automatycznej odpowiedzi", - "description": "Wiadomość, która zostanie wysłana jako odpowiedź", - "subject_label": "Temat", - "subject_description": "Wiersz tematu automatycznej odpowiedzi", - "subject_placeholder": "Poza biurem", - "body_label": "Treść wiadomości", - "body_description": "Treść wiadomości w zwykłym tekście", - "body_placeholder": "Dziękuję za wiadomość. Obecnie jestem poza biurem i odpowiem po powrocie.", - "html_label": "Wiadomość sformatowana (HTML)", - "html_description": "Dodaj sformatowaną wersję z odnośnikami i stylami. Odbiorcy, których program pocztowy nie może jej wyświetlić, zobaczą powyższy zwykły tekst.", - "html_placeholder": "Napisz sformatowaną odpowiedź o nieobecności…" - }, - "preview": { - "title": "Podgląd", - "show": "Pokaż podgląd", - "hide": "Ukryj podgląd" - }, - "save": "Zapisz zmiany", - "saving": "Zapisywanie...", - "warnings": { - "end_before_start": "Data zakończenia musi być późniejsza niż data rozpoczęcia", - "start_in_past": "Data rozpoczęcia jest w przeszłości", - "empty_body": "Treść wiadomości jest pusta - odbiorcy otrzymają pustą odpowiedź", - "subject_too_long": "Temat jest za długi dla serwera (maksymalnie {max} bajtów).", - "body_too_long": "Wiadomość jest za długa dla serwera (maksymalnie {max} bajtów wraz z formatowaniem)." - } - }, - "folders": { - "role_memos": "Notatki", - "title": "Foldery", - "description": "Zarządzaj folderami poczty i przypisuj standardowe role", - "folder_list": "Twoje foldery", - "folder_list_description": "Kliknij ikonę folderu, aby go dostosować", - "standard_roles": "Standardowe role folderów", - "standard_roles_description": "Przypisz foldery używane dla standardowych ról skrzynki, takich jak Odebrane, Wysłane, Kosz itp.", - "role_inbox": "Odebrane", - "role_drafts": "Szkice", - "role_sent": "Wysłane", - "role_trash": "Kosz", - "role_junk": "Spam / Niechciane", - "role_archive": "Archiwum", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Brak", - "create_folder": "Utwórz folder", - "create_subfolder": "Utwórz podfolder", - "reorder": "Przeciągnij, aby zmienić kolejność", - "reorder_error": "Nie udało się zmienić kolejności folderów", - "move_error": "Nie udało się przenieść folderu", - "subfolder_of": "W folderze {name}", - "subfolder_name": "Nazwa podfolderu", - "new_folder_name": "Nazwa folderu", - "rename": "Zmień nazwę", - "change_icon": "Zmień ikonę", - "delete": "Usuń", - "confirm_delete": "Czy na pewno chcesz usunąć „{name}”? Wiadomości w tym folderze zostaną przeniesione do Kosza.", - "create": "Utwórz", - "cancel": "Anuluj", - "no_folders": "Brak niestandardowych folderów", - "cannot_delete_role": "Nie można usunąć folderu ze standardową rolą. Najpierw usuń tę rolę.", - "folder_created": "Folder został utworzony", - "folder_renamed": "Nazwa folderu została zmieniona", - "folder_deleted": "Folder został usunięty", - "role_updated": "Rola folderu została zaktualizowana", - "error_create": "Nie udało się utworzyć folderu", - "error_rename": "Nie udało się zmienić nazwy folderu", - "error_delete": "Nie udało się usunąć folderu", - "error_delete_has_children": "Nie można usunąć folderu: nadal zawiera podfoldery. Najpierw usuń je lub przenieś.", - "error_delete_has_email": "Nie można usunąć folderu: nadal zawiera wiadomości e-mail. Najpierw je przenieś lub usuń.", - "error_role": "Nie udało się zaktualizować roli folderu" - }, - "advanced": { - "title": "Zaawansowane", - "description": "Opcje zaawansowane i ustawienia deweloperskie", - "debug_mode": { - "label": "Tryb debugowania", - "description": "Włącz szczegółowe logowanie do rozwiązywania problemów" - }, - "debug_categories": { - "description": "Wybierz kategorie do logowania. Wyłącz kategorie, których nie potrzebujesz, aby zmniejszyć szum w konsoli.", - "jmap": "Klient JMAP", - "jmap_description": "Operacje na skrzynkach, pobieranie wiadomości e-mail i żądania protokołu JMAP", - "calendar": "Kalendarz", - "calendar_description": "Wydarzenia kalendarza, importy i wiadomości planowania", - "tasks": "Zadania", - "tasks_description": "Tworzenie zadań kalendarza, pobieranie i aktualizacje", - "auth": "Uwierzytelnianie", - "auth_description": "Logowanie, TOTP, wymiana tokenów i zarządzanie sesją", - "filters": "Filtry", - "filters_description": "Reguły filtrów Sieve i skrypty urlopowe", - "email": "Wyświetlanie wiadomości", - "email_description": "Renderowanie wiadomości e-mail, przetwarzanie TNEF i oznaczanie jako przeczytane", - "push": "Powiadomienia push", - "push_description": "Konfiguracja i dostarczanie powiadomień push", - "contacts": "Kontakty i książki adresowe", - "contacts_description": "Synchronizacja kontaktów, operacje książki adresowej i zaufani nadawcy" - }, - "settings_sync": { - "label": "Synchronizacja ustawień", - "description": "Synchronizuj ustawienia między przeglądarkami i urządzeniami" - }, - "sender_favicons": { - "label": "Favikony nadawców", - "description": "Pokazuj ikony stron internetowych jako zdjęcia profilowe nadawców firmowych" - }, - "show_avatars_in_junk": { - "label": "Pokazuj awatary w folderze spam", - "description": "Pokazuj zdjęcia profilowe i favikony nadawców w folderze spam. Domyślnie wyłączone, aby próby phishingu nie wyglądały na wiarygodne." - }, - "keyboard_shortcuts": { - "label": "Skróty klawiszowe", - "description": "Wyświetl dostępne skróty klawiszowe", - "button": "Pokaż skróty" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Resetuj ustawienia", - "description": "Przywróć wszystkie ustawienia do wartości domyślnych", - "button": "Przywróć domyślne" - }, - "export_settings": { - "label": "Eksportuj ustawienia", - "description": "Pobierz swoje ustawienia jako JSON", - "button": "Eksportuj" - }, - "import_settings": { - "label": "Importuj ustawienia", - "description": "Prześlij ustawienia z pliku JSON", - "button": "Importuj" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Aplikacje paska bocznego", - "description": "Zarządzaj niestandardowymi aplikacjami i linkami na pasku bocznym", - "keep_loaded": "Pozostaw aplikacje załadowane", - "keep_loaded_description": "Utrzymuj aplikacje wbudowane uruchomione w tle podczas przełączania się między nimi, aby uniknąć ponownego ładowania", - "managed_title": "Udostępnione przez administratora", - "managed_description": "Te aplikacje są skonfigurowane dla wszystkich i nie można ich zmienić.", - "manage_title": "Niestandardowe aplikacje", - "manage_description": "Dodawaj, edytuj lub usuwaj niestandardowe aplikacje z paska bocznego" - }, - "contacts": { - "title": "Kontakty", - "description": "Importuj i eksportuj kontakty", - "import_label": "Importuj kontakty", - "import_description": "Importuj kontakty z pliku vCard (.vcf)", - "export_label": "Eksportuj kontakty", - "export_description": "Eksportuj wszystkie kontakty jako plik vCard (.vcf)", - "manage_title": "Książki adresowe", - "manage_description": "Zmień nazwy swoich książek adresowych", - "no_address_books": "Nie znaleziono książek adresowych", - "categories_title": "Kategorie", - "categories_description": "Zmień nazwy kategorii kontaktów", - "no_categories": "Nie znaleziono kategorii", - "group_by_letter_description": "Pokaż alfabetyczne nagłówki sekcji na liście kontaktów", - "group_by_letter_label": "Grupuj według pierwszej litery", - "sort_by_last_name_label": "Sortuj według nazwiska", - "sort_by_last_name_description": "Uporządkuj listę kontaktów według nazwiska, aby członkowie rodziny byli obok siebie" - }, - "downloads": { - "title": "Pobrane", - "description": "Dostosuj sposób nazywania pobranych wiadomości i załączników.", - "reset": "Przywróć domyślne", - "preview": "Podgląd:", - "email_template": { - "label": "Nazwa pliku wiadomości (.eml)", - "description": "Szablon używany przy eksporcie wiadomości lub jej przeciąganiu do systemu plików. Rozszerzenie .eml jest dodawane automatycznie." - }, - "attachment_template": { - "label": "Nazwa pliku załącznika", - "description": "Szablon używany przy pobieraniu lub przeciąganiu załącznika. Jeśli pominiesz '{filename}' i '{ext}', oryginalne rozszerzenie zostanie zachowane." - }, - "bundle_template": { - "label": "Nazwa pliku .zip wielu wiadomości", - "description": "Szablon używany przy przeciąganiu lub pobieraniu kilku wybranych wiadomości jako jednego archiwum .zip. Rozszerzenie .zip jest dodawane automatycznie." - }, - "spaces": { - "label": "Spacje", - "description": "Zastąp spacje w nazwie pliku innym znakiem.", - "keep": "Zachowaj spacje", - "underscore": "Zamień na _", - "dash": "Zamień na -" - }, - "lowercase": { - "label": "Małe litery", - "description": "Wymuś małe litery w całej nazwie pliku." - }, - "strip_diacritics": { - "label": "Usuń znaki diakrytyczne", - "description": "Zamień litery z akcentami na ich odpowiedniki ASCII (ą → a, é → e). Przydatne dla narzędzi, które źle obsługują nazwy Unicode." - }, - "collapse_separators": { - "label": "Łącz powtórzone separatory", - "description": "Połącz ciągi spacji, podkreśleń lub myślników w jeden znak." - }, - "after_export": { - "label": "Po eksporcie", - "description": "Opcjonalnie przenieś wiadomość po wyeksportowaniu jako .eml.", - "keep": "Zachowaj w skrzynce", - "archive": "Przenieś do archiwum", - "trash": "Przenieś do kosza" - } - }, - "filters": { - "title": "Filtry wiadomości e-mail", - "description": "Twórz reguły, aby automatycznie sortować, etykietować i zarządzać przychodzącymi wiadomościami e-mail", - "add_rule": "Dodaj regułę", - "no_rules": "Brak reguł filtrów", - "no_rules_description": "Twórz reguły, aby automatycznie organizować przychodzące wiadomości e-mail", - "vacation_active": "Autoresponder urlopowy jest aktywny", - "vacation_active_description": "Automatyczna odpowiedź jest włączona dla wiadomości przychodzących", - "vacation_configure": "Konfiguruj", - "edit_rule": "Edytuj regułę", - "new_rule": "Nowa reguła", - "delete_rule": "Usuń regułę", - "delete_confirm": "Czy na pewno chcesz usunąć tę regułę?", - "enable": "Włącz", - "disable": "Wyłącz", - "raw_editor": "Surowy edytor Sieve", - "raw_editor_warning": "Edytowanie surowego skryptu Sieve może zepsuć wizualną edycję reguł. Zmiany wprowadzone tutaj zastępują kreator wizualny.", - "validate": "Waliduj", - "validation_success": "Skrypt jest poprawny", - "validation_error": "Skrypt zawiera błędy", - "save": "Zapisz reguły", - "saving": "Zapisywanie...", - "saved": "Filtry zostały zapisane", - "save_failed": "Nie udało się zapisać filtrów", - "loading": "Ładowanie filtrów...", - "not_supported": "Twój serwer pocztowy nie obsługuje filtrów wiadomości e-mail.", - "rule_name": "Nazwa reguły", - "rule_name_placeholder": "np. Sortuj newslettery", - "match_all": "Dopasuj WSZYSTKIE warunki", - "match_any": "Dopasuj DOWOLNY warunek", - "conditions": "Warunki", - "add_condition": "Dodaj warunek", - "actions": "Akcje", - "add_action": "Dodaj akcję", - "stop_processing": "Zatrzymaj przetwarzanie kolejnych reguł", - "include_spam": "Przenoś również wiadomości oznaczone jako spam", - "attachment_type_placeholder": "np. pdf, doc, jpg", - "value_placeholder_multi": "Wartość (kilka oddzielonych przecinkami)", - "condition_fields": { - "attachment": "Załącznik", - "from": "Od", - "to": "Do", - "cc": "DW", - "subject": "Temat", - "header": "Niestandardowy nagłówek", - "size": "Rozmiar", - "body": "Treść" - }, - "comparators": { - "has_any": "jest obecny", - "has_type": "typu", - "contains": "zawiera", - "not_contains": "nie zawiera", - "is": "jest dokładnie", - "not_is": "nie jest", - "starts_with": "zaczyna się od", - "ends_with": "kończy się na", - "matches": "pasuje do wzorca", - "greater_than": "jest większe niż", - "less_than": "jest mniejsze niż" - }, - "action_types": { - "move": "Przenieś do folderu", - "copy": "Kopiuj do folderu", - "forward": "Prześlij dalej do", - "mark_read": "Oznacz jako przeczytane", - "star": "Oznacz wiadomość gwiazdką", - "add_label": "Dodaj etykietę", - "discard": "Odrzuć (usuń po cichu)", - "reject": "Odrzuć z wiadomością", - "keep": "Pozostaw w Odebranych", - "stop": "Zatrzymaj przetwarzanie" - }, - "move_to_folder": "Wybierz folder", - "copy_to_folder": "Wybierz folder", - "forward_to": "Prześlij dalej na adres e-mail", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Zachowaj kopię", - "forward_limit": "Limit przekazań na wiadomość na tym serwerze: {count}. Dodatkowe przekazania są pomijane.", - "reject_message": "Wiadomość odrzucenia", - "reject_placeholder": "Twoja wiadomość e-mail została odrzucona", - "label_name": "Nazwa etykiety", - "label_placeholder": "Wybierz etykietę", - "header_name": "Nazwa nagłówka", - "header_placeholder": "np. X-Mailing-List", - "size_bytes": "Rozmiar w bajtach", - "size_placeholder": "np. 1000000", - "system_managed": "Reguła zarządzana przez system", - "opaque_warning": "Ten skrypt został edytowany poza kreatorem wizualnym. Dostępna jest tylko surowa edycja Sieve.", - "open_sieve_editor": "Otwórz surowy edytor Sieve", - "fetch_error": "Nie udało się załadować filtrów", - "expanded_view": "Widok rozszerzony", - "expanded_view_description": "Pokazuj reguły filtrów ze szczegółowymi blokami warunków i akcji", - "if": "Jeśli", - "then": "Wtedy", - "match_all_conditions": "wszystkie pasują", - "match_any_condition": "dowolne pasują", - "and": "i", - "or": "lub", - "cancel": "Anuluj", - "confirm_delete": "Usuń", - "rule_list": "Reguły filtrów", - "drag_to_reorder": "Przeciągnij, aby zmienić kolejność", - "match_type": "Typ dopasowania", - "reset_to_visual": "Resetuj do kreatora wizualnego", - "reset_warning": "To odrzuci bieżący skrypt i pozwoli zacząć od nowa.", - "confirm_reset": "Resetuj", - "validation_empty_name": "Nazwa reguły jest wymagana", - "validation_empty_conditions": "Wymagany jest co najmniej jeden warunek z wartością", - "validation_empty_actions": "Wymagana jest co najmniej jedna akcja", - "templates_section": "Rozpocznij od szablonu", - "template_newsletters": "Przenieś newslettery do folderu", - "template_receipts": "Automatycznie archiwizuj rachunki", - "template_important": "Oznacz ważne wiadomości e-mail", - "template_notifications": "Filtruj powiadomienia", - "sieve_editor": { - "title": "Edytor skryptu Sieve", - "warning": "Edytowanie surowego skryptu Sieve może zepsuć wizualną edycję reguł. Zmiany wprowadzone tutaj zastępują kreator wizualny.", - "script_content": "Skrypt Sieve", - "valid": "Skrypt jest poprawny", - "invalid": "Skrypt zawiera błędy", - "save_warning": "Zapisanie nadpisze wszystkie reguły wizualne. Nie można tego cofnąć. Kliknij Zapisz ponownie, aby potwierdzić.", - "validating": "Walidowanie...", - "validate": "Waliduj", - "cancel": "Anuluj", - "save": "Zapisz", - "confirm_save": "Potwierdź zapis", - "validation_failed": "Żądanie walidacji nie powiodło się" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# warunek} other {# warunków}}", - "actions_count": "{count, plural, one {# akcja} other {# akcji}}" - }, - "origin_external": "Zewnętrzny", - "managed_by_tooltip": "Zarządzane przez {source}. Edytuj w tej aplikacji lub użyj surowego edytora Sieve." - }, - "templates": { - "title": "Szablony wiadomości e-mail", - "description": "Twórz wielokrotnego użytku szablony wiadomości e-mail ze zmiennymi zastępczymi", - "add": "Nowy szablon", - "edit": "Edytuj szablon", - "name": "Nazwa szablonu", - "name_placeholder": "np. Wiadomość kontrolna", - "category": "Kategoria", - "category_placeholder": "np. Praca, Osobiste", - "subject": "Temat", - "subject_placeholder": "Temat wiadomości e-mail", - "body": "Treść", - "body_placeholder": "Treść wiadomości e-mail...", - "recipients_placeholder": "email@example.com", - "identity": "Wyślij jako", - "default_identity": "Domyślna tożsamość", - "favorite": "Ulubione", - "cancel": "Anuluj", - "create": "Utwórz", - "update": "Aktualizuj", - "confirm_delete": "Usuń", - "no_templates": "Brak szablonów", - "manage": "Zarządzaj szablonami", - "count": "{count, plural, one {# szablon} other {# szablonów}}", - "export_import": "Eksport i import", - "export_import_description": "Twórz kopie zapasowe szablonów lub przenoś je na inne urządzenie", - "export": "Eksportuj", - "import": "Importuj", - "validation": { - "empty": "Nazwa szablonu jest wymagana", - "too_long": "Nazwa szablonu musi mieć maksymalnie 200 znaków" - } - }, - "files": { - "display": { - "title": "Wyświetlanie", - "description": "Skonfiguruj sposób wyświetlania plików i folderów" - }, - "folder_layout": { - "label": "Nawigacja folderów", - "description": "Wybierz sposób wyświetlania folderów: wbudowane z plikami lub w drzewie paska bocznego", - "inline": "Wbudowana", - "sidebar": "Pasek boczny" - }, - "default_view": { - "label": "Domyślny widok", - "description": "Wybierz układ siatki lub listy", - "list": "Lista", - "grid": "Siatka" - }, - "default_sort": { - "label": "Domyślne sortowanie", - "description": "Wybierz domyślne sortowanie plików", - "name": "Nazwa", - "size": "Rozmiar", - "modified": "Zmodyfikowano" - }, - "sort_direction": { - "label": "Kierunek sortowania", - "description": "Wybierz kolejność rosnącą lub malejącą", - "ascending": "Rosnąco", - "descending": "Malejąco" - }, - "icons": { - "title": "Ikony", - "description": "Skonfiguruj wygląd ikon plików" - }, - "show_icons": { - "label": "Pokazuj ikony plików", - "description": "Wyświetlaj ikony obok plików i folderów" - }, - "colored_icons": { - "label": "Kolorowe ikony", - "description": "Używaj kolorowych ikon zamiast monochromatycznych" - }, - "show_thumbnails": { - "label": "Pokazuj miniatury", - "description": "Wyświetlaj podglądy obrazów zamiast ikon dla plików graficznych" - }, - "behavior": { - "title": "Zachowanie", - "description": "Skonfiguruj działanie przeglądarki plików" - }, - "show_hidden": { - "label": "Pokazuj ukryte pliki", - "description": "Wyświetlaj pliki i foldery zaczynające się od kropki" - }, - "preview": { - "label": "Podgląd" - } - }, - "search_placeholder": "Szukaj ustawień", - "search_clear": "Wyczyść wyszukiwanie", - "search_no_results": "Brak pasujących ustawień", - "scoped": { - "back": "Powrót do mojego konta", - "managing": "Zarządzanie: {name}" - } - }, - "errors": { - "page_error_title": "Coś poszło nie tak", - "page_error_description": "Wystąpił nieoczekiwany błąd. Spróbuj ponownie lub wróć do strony głównej.", - "sidebar_error": "Nie udało się załadować skrzynek pocztowych", - "email_list_error": "Nie udało się załadować wiadomości", - "viewer_error_title": "Nie można wyświetlić wiadomości", - "viewer_error_description": "Wystąpił problem podczas renderowania tej wiadomości. Może zawierać nieobsługiwaną zawartość.", - "composer_error": "Nie udało się załadować edytora wiadomości", - "settings_error_title": "Ustawienia są niedostępne", - "settings_error_description": "Nie udało się załadować ustawień. Twoje preferencje mogą nie zostać zapisane.", - "try_again": "Spróbuj ponownie", - "reload": "Odśwież", - "reload_emails": "Odśwież wiadomości", - "reload_settings": "Odśwież ustawienia", - "retry": "Ponów", - "go_home": "Przejdź do skrzynki odbiorczej" - }, - "context_menu": { - "reply": "Odpowiedz", - "reply_all": "Odpowiedz wszystkim", - "forward": "Prześlij dalej", - "mark_read": "Oznacz jako przeczytane", - "mark_unread": "Oznacz jako nieprzeczytane", - "star": "Oznacz gwiazdką", - "unstar": "Usuń gwiazdkę", - "pin": "Przypnij", - "unpin": "Odepnij", - "move_to": "Przenieś do...", - "archive": "Archiwizuj", - "delete": "Usuń", - "mark_as_spam": "Zgłoś spam", - "not_spam": "To nie spam", - "tag": "Etykieta", - "items_selected": "{count} zaznaczonych wiadomości", - "edit_draft": "Edytuj szkic", - "cancel_scheduled_send": "Anuluj wysyłkę", - "reschedule_send": "Przełóż", - "cancel_and_edit": "Anuluj i edytuj", - "cancel_and_compose_again": "Anuluj i napisz ponownie" - }, - "mailbox_context_menu": { - "mark_folder_read": "Oznacz folder jako przeczytany", - "mark_folder_tree_read": "Oznacz folder i podfoldery jako przeczytane", - "mark_all_folders_read": "Oznacz wszystkie foldery jako przeczytane", - "new_subfolder": "Nowy podfolder...", - "new_folder": "Nowy folder...", - "rename": "Zmień nazwę...", - "share": "Udostępnij...", - "import_email": "Importuj .eml lub .zip...", - "empty_folder": "Opróżnij folder", - "empty_folder_generic": "Opróżnij folder", - "delete_folder": "Usuń folder", - "refresh": "Odśwież", - "mark_all_confirm_title": "Oznacz wszystkie foldery jako przeczytane", - "mark_all_confirm_message": "Oznaczyć wszystkie nieprzeczytane wiadomości na koncie osobistym jako przeczytane?", - "delete_confirm_title": "Usuń folder", - "delete_confirm_message": "Trwale usunąć folder \"{name}\"? Tej operacji nie można cofnąć.", - "prompt_new_subfolder": "Podaj nazwę nowego podfolderu.", - "prompt_new_folder": "Podaj nazwę nowego folderu.", - "prompt_rename": "Podaj nową nazwę tego folderu.", - "toast_marked_read": "Folder oznaczony jako przeczytany", - "toast_marked_read_count": "Oznaczono {count, plural, one {1 wiadomość} other {# wiadomości}} jako przeczytane", - "toast_already_read": "Brak nieprzeczytanych wiadomości", - "toast_marked_all_read": "Wszystkie foldery oznaczone jako przeczytane", - "toast_emptied": "Folder opróżniony", - "toast_folder_created": "Folder utworzony", - "toast_folder_renamed": "Nazwa folderu zmieniona", - "toast_folder_deleted": "Folder usunięty", - "toast_error_mark_read": "Nie udało się oznaczyć jako przeczytane", - "toast_error_empty": "Nie udało się opróżnić folderu", - "toast_error_create": "Nie udało się utworzyć folderu", - "toast_error_rename": "Nie udało się zmienić nazwy folderu", - "toast_error_delete": "Nie udało się usunąć folderu", - "toast_error_delete_has_children": "Folder zawiera podfoldery. Najpierw je usuń.", - "toast_error_delete_has_email": "Folder nie jest pusty. Najpierw go opróżnij.", - "delete_with_contents_title": "Folder nie jest pusty", - "delete_with_contents_message": "Folder „{name}” nadal zawiera {count, plural, one {1 wiadomość} few {# wiadomości} other {# wiadomości}}. Usunąć folder wraz ze wszystkimi wiadomościami? Tej operacji nie można cofnąć.", - "delete_with_contents_confirm": "Usuń folder i wiadomości", - "placeholder_folder_name": "Nazwa folderu", - "create": "Utwórz", - "rename_confirm": "Zmień nazwę" - }, - "shortcuts": { - "title": "Skróty klawiszowe", - "tip": "Naciśnij ? w dowolnym momencie, aby wyświetlić tę pomoc", - "sections": { - "navigation": "Nawigacja", - "actions": "Akcje wiadomości", - "global": "Globalne", - "threads": "Wątki", - "composer": "Edytor wiadomości" - }, - "navigation": { - "next_email": "Następna wiadomość", - "previous_email": "Poprzednia wiadomość", - "open_email": "Otwórz wiadomość", - "close_email": "Zamknij / Odznacz" - }, - "actions": { - "reply": "Odpowiedz", - "reply_all": "Odpowiedz wszystkim", - "forward": "Prześlij dalej", - "star": "Przełącz gwiazdkę", - "archive": "Archiwizuj", - "delete": "Usuń", - "mark_unread": "Oznacz jako nieprzeczytane", - "mark_read": "Oznacz jako przeczytane", - "toggle_spam": "Zgłoś spam / To nie spam" - }, - "global": { - "compose": "Napisz nową wiadomość", - "search": "Przejdź do wyszukiwania", - "help": "Pokaż skróty", - "refresh": "Odśwież wiadomości", - "select_all": "Zaznacz wszystko", - "global_search": "Szukaj wszędzie" - }, - "threads": { - "expand_collapse": "Rozwiń/zwiń wątek" - }, - "composer": { - "send": "Wyślij e-mail", - "schedule_send": "Zaplanuj wysyłkę", - "template_picker": "Otwórz wybór szablonu" - } - }, - "threads": { - "messages_one": "{count} wiadomość", - "messages_other": "{count} wiadomości", - "messages_tooltip": "{count, plural, one {# wiadomość w tej konwersacji} other {# wiadomości w tej konwersacji}}", - "expand": "Rozwiń konwersację", - "collapse": "Zwiń konwersację", - "loading": "Ładowanie konwersacji...", - "mark_read": "Oznacz konwersację jako przeczytaną", - "mark_unread": "Oznacz konwersację jako nieprzeczytaną", - "archive": "Archiwizuj konwersację", - "delete": "Usuń konwersację", - "star": "Oznacz konwersację gwiazdką", - "unstar": "Usuń gwiazdkę z konwersacji", - "toggle_thread": "Przełącz widok wątku" - }, - "identities": { - "modal_title": "Zarządzaj tożsamościami nadawcy", - "create_new": "Utwórz nową tożsamość", - "edit_identity": "Edytuj tożsamość", - "delete_confirm": "Usunąć tę tożsamość? Tej operacji nie można cofnąć.", - "cannot_delete": "Tej tożsamości nie można usunąć", - "primary_identity": "Główna", - "set_as_primary": "Ustaw jako główną", - "no_identities": "Nie znaleziono tożsamości", - "display": { - "reply_to": "Odpowiedz-do:", - "bcc": "UDW:", - "signature": "Podpis:", - "preview": "Podgląd:" - }, - "validation_errors": { - "invalid_emails": "Nieprawidłowe adresy e-mail: {emails}", - "unknown_error": "Nieznany błąd" - }, - "form": { - "name_label": "Nazwa wyświetlana", - "name_placeholder": "np. Służbowy e-mail, Prywatny", - "name_required": "Nazwa jest wymagana", - "email_label": "Adres e-mail", - "email_placeholder": "your.email@example.com", - "email_required": "Adres e-mail jest wymagany", - "email_invalid": "Wprowadź prawidłowy adres e-mail", - "email_immutable": "Adresu e-mail nie można zmienić po utworzeniu", - "reply_to_label": "Odpowiedz-do (opcjonalnie)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "Automatyczne UDW (opcjonalnie)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "Podpis tekstowy", - "html_signature_label": "Podpis HTML", - "signature_byte_counter": "{bytes} / {max} bajtów", - "signature_byte_limit_reached": "Osiągnięto limit serwera", - "save": "Zapisz tożsamość", - "cancel": "Anuluj", - "creating": "Tworzenie...", - "updating": "Aktualizowanie..." - }, - "sub_address": { - "button_tooltip": "Użyj podadresu", - "popover_title": "Dodaj tag podadresu", - "tag_input_placeholder": "Wpisz tag (np. zakupy)", - "preview_label": "Podgląd:", - "recent_tags": "Ostatnie tagi", - "suggested_tags": "Sugerowane", - "use_address": "Użyj tego adresu", - "invalid_tag": "Tag może zawierać tylko litery, cyfry i myślniki", - "tag_too_long": "Tag może mieć maksymalnie 30 znaków", - "help_text": "Wiadomości wysłane na adres user{delimiter}tag@domain.com trafią do Twojej skrzynki odbiorczej", - "validation": { - "empty": "Tag nie może być pusty", - "too_long": "Tag może mieć maksymalnie {max} znaków", - "invalid_chars": "Tag może zawierać tylko litery, cyfry i myślniki" - } - }, - "badge": { - "sent_via": "przez", - "sub_address_tag": "Wysłano przy użyciu podadresu: {tag}", - "identity_name": "Wysłano przy użyciu tożsamości: {name}", - "identity_short": "przez {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Usuń", - "delete_confirm_title": "Usuń tożsamość" - }, - "templates": { - "picker_title": "Wybierz szablon", - "search_placeholder": "Szukaj szablonów...", - "section_favorites": "Ulubione", - "section_recent": "Ostatnie", - "section_uncategorized": "Inne", - "no_templates": "Nie ma jeszcze szablonów", - "no_results": "Nie znaleziono szablonów", - "fill_placeholders": "Uzupełnij wartości zmiennych", - "enter_value": "Wpisz wartość...", - "preview": "Podgląd", - "insert_with_values": "Wstaw z wartościami", - "insert_raw": "Wstaw bez zmian", - "copy_suffix": "(kopia)", - "placeholder": "Zmienna", - "placeholders": { - "recipient_name": "Nazwa odbiorcy", - "company": "Nazwa firmy", - "date": "Bieżąca data", - "day_of_week": "Dzień tygodnia", - "sender_name": "Twoje imię" - } - }, - "contacts": { - "title": "Kontakty", - "search_placeholder": "Szukaj kontaktów...", - "create_new": "Nowy kontakt", - "no_category": "Brak kategorii", - "rename_category": "Zmień nazwę kategorii", - "category_name_label": "Nazwa kategorii", - "category_renamed": "Zmieniono nazwę kategorii", - "category_rename_failed": "Nie udało się zmienić nazwy kategorii", - "category_added": "Kontakt dodany do {name}", - "category_added_plural": "{count} kontaktów dodano do {name}", - "empty_state": "Brak kontaktów", - "empty_state_title": "Brak kontaktów", - "empty_state_subtitle": "Utwórz pierwszy kontakt lub zaimportuj z pliku vCard", - "empty_search": "Żadne kontakty nie pasują do wyszukiwania", - "empty_search_hint": "Spróbuj użyć innego wyszukiwanego hasła", - "empty_filtered": "Żadne kontakty nie pasują do filtrów", - "empty_filtered_hint": "Dostosuj lub wyczyść filtry", - "clear_search": "Wyczyść wyszukiwanie", - "import_vcard": "Importuj vCard", - "delete_confirm_title": "Usuń kontakt", - "delete_confirm": "Czy na pewno chcesz usunąć ten kontakt?", - "local_mode": "Kontakty są przechowywane lokalnie (serwer nie obsługuje JMAP Contacts)", - "back_to_contacts": "Powrót do kontaktów", - "back_to_email": "Powrót do wiadomości", - "tabs": { - "all": "Wszystkie", - "groups": "Grupy" - }, - "shared": { - "title": "Udostępnione" - }, - "address_books": { - "create": "Nowa książka adresowa", - "created": "Utworzono książkę adresową", - "create_failed": "Nie udało się utworzyć książki adresowej", - "title": "Moje książki adresowe", - "shared_prefix": "Udostępnione: {name}", - "moved": "Kontakt przeniesiono do {name}", - "moved_plural": "{count} kontaktów przeniesiono do {name}", - "move_failed": "Nie udało się przenieść kontaktu", - "address_book": "Książka adresowa", - "rename": "Zmień nazwę książki adresowej", - "name_label": "Nazwa książki adresowej", - "renamed": "Zmieniono nazwę książki adresowej", - "rename_failed": "Nie udało się zmienić nazwy książki adresowej", - "default": "Domyślna", - "set_default": "Ustaw jako domyślną", - "default_updated": "Zaktualizowano domyślną książkę adresową", - "set_default_failed": "Nie udało się ustawić domyślnej książki adresowej", - "manage": "Zarządzaj książkami adresowymi", - "share": "Udostępnij książkę adresową", - "new_contact_in_book": "Nowy kontakt w tej książce adresowej", - "delete": "Usuń książkę adresową", - "confirm_delete": "Usunąć „{name}\"? Wszystkie kontakty w tej książce adresowej zostaną usunięte.", - "deleted": "Książka adresowa usunięta", - "delete_failed": "Nie udało się usunąć książki adresowej" - }, - "detail": { - "emails": "Adresy e-mail", - "phones": "Numery telefonów", - "organizations": "Organizacje", - "addresses": "Adresy", - "notes": "Notatki", - "titles": "Stanowiska i role", - "online_services": "Usługi online", - "anniversaries": "Rocznice", - "personal_info": "Informacje osobiste", - "languages": "Języki", - "categories": "Kategorie", - "related_contacts": "Powiązane kontakty", - "crypto_keys": "Klucze kryptograficzne", - "cert_issuer": "Wystawca", - "cert_expires": "Wygasa", - "cert_expired": "Wygasł", - "cert_fingerprint": "Odcisk", - "cert_algorithm": "Algorytm", - "import_to_smime": "Importuj do S/MIME", - "cert_already_imported": "Już zaimportowano do S/MIME", - "cert_imported": "Certyfikat zaimportowano do magazynu S/MIME", - "cert_import_failed": "Nie udało się zaimportować certyfikatu", - "no_contact_selected": "Wybierz kontakt, aby wyświetlić szczegóły", - "compose_email": "Napisz e-mail", - "copy_email": "Kopiuj e-mail", - "copy_phone": "Kopiuj numer telefonu", - "copy_url": "Kopiuj URL", - "copied": "Skopiowano do schowka", - "copy_failed": "Nie udało się skopiować do schowka", - "created": "Utworzono", - "updated": "Ostatnia aktualizacja", - "timezone": "Strefa czasowa", - "anniversary_birth": "Urodziny", - "anniversary_death": "Data śmierci", - "anniversary_wedding": "Rocznica", - "anniversary_other": "Inne", - "personal_expertise": "Specjalizacja", - "personal_hobby": "Hobby", - "personal_interest": "Zainteresowanie", - "personal_other": "Inne", - "gender": "Płeć", - "gender_masculine": "Mężczyzna", - "gender_feminine": "Kobieta", - "gender_other": "Inne", - "gender_none": "Nie dotyczy", - "gender_unknown": "Nieznana", - "calendar": "Kalendarz", - "calendar_uri": "Adres URL kalendarza", - "scheduling_uri": "Adres URL planowania", - "freebusy_uri": "Adres URL wolny/zajęty", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Więcej działań", - "age_years": "{count, plural, one {1 rok} few {# lata} many {# lat} other {# lat}}", - "years_since": "{count, plural, one {1 rok temu} few {# lata temu} many {# lat temu} other {# lat temu}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Nowy kontakt", - "edit_title": "Edytuj kontakt", - "section_address_book": "Katalog", - "select_address_book": "Wybierz katalog...", - "section_identity": "Imię i tożsamość", - "contact_type": "Typ kontaktu", - "type_person": "Osoba", - "type_organization": "Organizacja", - "section_work": "Praca i organizacja", - "prefix": "Tytuł", - "prefix_placeholder": "Dr, Pan, Pani", - "given_name": "Imię", - "middle_name": "Drugie imię", - "surname": "Nazwisko", - "suffix": "Sufiks", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Pseudonim", - "nickname_placeholder": "Pseudonim", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Telefon", - "phone_placeholder": "+1 234 567 890", - "phone_type": "Typ", - "phone_voice": "Głosowy", - "phone_cell": "Komórkowy", - "phone_fax": "Faks", - "phone_pager": "Pager", - "phone_video": "Wideo", - "phone_text": "SMS", - "organization": "Organizacja", - "organization_placeholder": "Nazwa firmy", - "department": "Dział", - "department_placeholder": "Dział", - "job_title": "Stanowisko", - "job_title_placeholder": "np. Inżynier oprogramowania", - "role": "Rola", - "role_placeholder": "np. Lider zespołu", - "addresses": "Adresy", - "add_address": "Dodaj adres", - "street": "Ulica", - "city": "Miasto", - "region": "Stan / region", - "postcode": "Kod pocztowy", - "country": "Kraj", - "online_services": "Usługi online", - "add_online_service": "Dodaj usługę online", - "url_placeholder": "https://...", - "service_placeholder": "Usługa", - "anniversaries": "Rocznice", - "add_anniversary": "Dodaj datę", - "anniversary_birth": "Urodziny", - "anniversary_wedding": "Rocznica", - "anniversary_death": "Upamiętnienie", - "anniversary_other": "Inne", - "personal_info": "Informacje osobiste", - "add_personal_info": "Dodaj wpis", - "personal_info_placeholder": "np. Fotografia", - "personal_expertise": "Specjalizacja", - "personal_hobby": "Hobby", - "personal_interest": "Zainteresowanie", - "personal_other": "Inne", - "level": "Poziom", - "level_high": "Wysoki", - "level_medium": "Średni", - "level_low": "Niski", - "categories": "Kategorie", - "categories_placeholder": "np. Rodzina, Przyjaciele, Współpracownicy", - "categories_hint": "Wpisz, aby wyszukać lub dodać kategorie", - "category_add": "Dodaj", - "note": "Notatki", - "note_placeholder": "Dodaj notatkę...", - "gender": "Płeć", - "gender_sex": "Płeć biologiczna", - "gender_male": "Mężczyzna", - "gender_female": "Kobieta", - "gender_other": "Inne", - "gender_none": "Nie dotyczy", - "gender_unknown": "Nieznana", - "gender_identity": "Tożsamość płciowa", - "gender_identity_placeholder": "Tożsamość płciowa...", - "calendar": "Kalendarz", - "calendar_uri": "Adres URL kalendarza", - "scheduling_uri": "Adres URL planowania", - "freebusy_uri": "Adres URL wolny/zajęty", - "context_work": "Praca", - "context_private": "Prywatny", - "add_email": "Dodaj e-mail", - "add_phone": "Dodaj telefon", - "save": "Zapisz", - "cancel": "Anuluj", - "creating": "Tworzenie...", - "updating": "Aktualizowanie...", - "name_required": "Podaj imię, nazwisko lub organizację", - "email_invalid": "Wprowadź prawidłowy adres e-mail", - "email_error_inline": "Nieprawidłowy format adresu e-mail", - "save_failed": "Nie udało się zapisać kontaktu", - "delete": "Usuń", - "upload_photo": "Prześlij zdjęcie", - "remove_photo": "Usuń zdjęcie", - "photo_hint": "JPG lub PNG, do 10 MB. Zostanie zmniejszone.", - "photo_too_large": "Obraz jest za duży (maks. 10 MB)", - "photo_invalid": "Nieprawidłowy plik obrazu", - "change_photo": "Zmień" - }, - "groups": { - "create": "Nowa grupa", - "edit": "Edytuj grupę", - "empty": "Brak grup", - "delete_confirm_title": "Usuń grupę", - "delete_confirm": "Czy na pewno chcesz usunąć tę grupę?", - "name_label": "Nazwa grupy", - "name_placeholder": "np. Zespół, Rodzina", - "name_required": "Nazwa grupy jest wymagana", - "save_failed": "Nie udało się zapisać grupy", - "members_label": "Członkowie", - "search_members": "Szukaj kontaktów do dodania...", - "no_members": "Brak członków w tej grupie", - "member_count": "{count, plural, =0 {Brak członków} one {1 członek} other {# członków}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Importuj kontakty", - "drop_hint": "Kliknij, aby wybrać plik vCard", - "file_types": "pliki .vcf lub .vcard", - "no_contacts": "Nie znaleziono kontaktów w pliku", - "parse_error": "Nie udało się przetworzyć pliku vCard", - "found": "{count, plural, one {Znaleziono 1 kontakt} other {Znaleziono # kontaktów}}", - "duplicate": "Duplikat", - "select_all": "Zaznacz wszystko", - "deselect_all": "Odznacz wszystko", - "selected": "{count, plural, one {1 zaznaczony} other {# zaznaczonych}}", - "import_button": "Importuj", - "importing": "Importowanie...", - "success": "{count, plural, one {Zaimportowano 1 kontakt} other {Zaimportowano # kontaktów}}", - "failed": "Import nie powiódł się", - "close": "Zamknij", - "file_too_large": "Plik jest za duży (maks. 5 MB)" - }, - "export": { - "title": "Eksportuj kontakty", - "success": "{count, plural, one {Wyeksportowano 1 kontakt} other {Wyeksportowano # kontaktów}}" - }, - "bulk": { - "selected": "{count, plural, one {1 zaznaczony} other {# zaznaczonych}}", - "select_all": "Zaznacz wszystko", - "delete": "Usuń", - "delete_confirm_title": "Usuń kontakty", - "delete_confirm": "Usunąć {count, plural, one {1 kontakt} other {# kontaktów}}?", - "deleted": "{count, plural, one {Usunięto 1 kontakt} other {Usunięto # kontaktów}}", - "add_to_group": "Dodaj do grupy", - "choose_group": "Wybierz grupę", - "adding_contacts": "Dodawanie {count, plural, one {1 kontaktu} other {# kontaktów}}", - "added_to_group": "Kontakty dodano do grupy", - "export": "Eksportuj", - "clear": "Wyczyść zaznaczenie" - }, - "toast": { - "created": "Kontakt utworzono", - "updated": "Kontakt zaktualizowano", - "deleted": "Kontakt usunięto", - "error_create": "Nie udało się utworzyć kontaktu", - "error_update": "Nie udało się zaktualizować kontaktu", - "error_delete": "Nie udało się usunąć kontaktu" - }, - "context_menu": { - "open": "Otwórz", - "edit": "Edytuj", - "send_email": "Wyślij e-mail", - "add_to_group": "Dodaj do grupy", - "export_vcard": "Eksportuj jako vCard", - "delete": "Usuń", - "call": "Zadzwoń", - "duplicate": "Duplikuj", - "print": "Drukuj" - }, - "filters": { - "toggle": "Filtry", - "select": "Zaznacz", - "clear": "Wyczyść", - "close": "Zamknij", - "title": "Filtry zaawansowane", - "organization": "Firma", - "organization_placeholder": "np. Acme", - "job_title": "Stanowisko", - "job_title_placeholder": "np. Projektant", - "location": "Lokalizacja", - "location_placeholder": "Miasto lub kraj", - "email_domain": "Domena e-mail", - "email_domain_placeholder": "przyklad.pl", - "birthday_month": "Urodziny w", - "any_month": "Dowolny miesiąc", - "has_email": "Z e-mailem", - "has_phone": "Z telefonem", - "has_photo": "Ze zdjęciem", - "sort_by": "Sortuj według", - "sort_first_name": "Imię", - "sort_last_name": "Nazwisko" - }, - "open_categories": "Otwórz kategorie" - }, - "calendar": { - "title": "Kalendarz", - "back_to_email": "Powrót do poczty", - "back_to_month": "Powrót do miesiąca", - "my_calendars": "Kalendarze", - "birthday_calendar": "Urodziny", - "mini_calendar_change": "Kliknij, aby zmienić miesiąc", - "views": { - "month": "Miesiąc", - "week": "Tydzień", - "day": "Dzień", - "agenda": "Agenda", - "today": "Dzisiaj", - "month_hint": "Miesiąc (m)", - "week_hint": "Tydzień (w)", - "day_hint": "Dzień (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Zadania", - "tasks_hint": "Zadania (k)" - }, - "events": { - "create": "Utwórz wydarzenie", - "edit": "Edytuj wydarzenie", - "delete": "Usuń wydarzenie", - "details": "Szczegóły wydarzenia", - "no_events": "Brak wydarzeń", - "all_day": "Cały dzień", - "more": "+{count} więcej", - "no_title": "(Bez tytułu)", - "resize": "Zmień rozmiar wydarzenia", - "duplicate": "Duplikuj", - "today_header": "Dzisiaj", - "tomorrow_header": "Jutro", - "agenda_show_earlier": "Pokaż wcześniejsze wydarzenia", - "agenda_loading": "Ładowanie…", - "agenda_range_start": "Wydarzenia od {date}", - "agenda_range_end": "Wydarzenia do {date}", - "export_ics": "Eksportuj jako .ics", - "copy_title": "Kopiuj tytuł", - "copy_link": "Kopiuj link do spotkania", - "go_to_today": "Przejdź do dzisiaj", - "new_all_day_event": "Nowe wydarzenie całodniowe", - "new_event": "Nowe wydarzenie", - "new_task": "Nowe zadanie" - }, - "detail": { - "add_note": "Dodaj notatkę...", - "save_note": "Zapisz", - "note_saved": "Dodano notatkę", - "open_link": "Otwórz link", - "meeting_link": "Link do spotkania", - "tentative": "Wstępnie", - "cancelled": "Odwołane", - "delete_confirm": "Usunąć to wydarzenie?" - }, - "form": { - "title": "Tytuł", - "description": "Opis", - "location": "Lokalizacja", - "meeting_link": "Link do spotkania", - "start_date": "Data rozpoczęcia", - "end_date": "Data zakończenia", - "start_time": "Godzina rozpoczęcia", - "end_time": "Godzina zakończenia", - "all_day_event": "Wydarzenie całodniowe", - "calendar_select": "Kalendarz", - "save": "Zapisz", - "cancel": "Anuluj", - "delete_confirm": "Czy na pewno chcesz usunąć to wydarzenie?", - "color": "Kolor" - }, - "participants": { - "availability": { - "title": "Dostępność", - "free": "Dostępny", - "busy": "Zajęty", - "tentative": "Wstępnie", - "unknown": "Nie jest użytkownikiem tego serwera", - "checking": "Sprawdzanie…" - }, - "title": "Uczestnicy", - "add": "Dodaj uczestnika", - "organizer": "Organizator", - "attendee": "Uczestnik", - "accepted": "Zaakceptowano", - "declined": "Odrzucono", - "tentative": "Wstępnie", - "needs_action": "Wymaga działania", - "remove": "Usuń", - "edit": "Edytuj", - "email_placeholder": "Dodaj adres e-mail lub wyszukaj kontakty", - "send_invitations": "Wyślij zaproszenia do uczestników", - "status_summary": "{accepted} zaakceptowano, {pending} oczekuje", - "invited_by": "Zaproszenie od {name}", - "respond_below": "Odpowiedz za pomocą przycisków poniżej", - "rsvp_label": "Twoja odpowiedź", - "cancel_notification": "Uczestnicy zostaną powiadomieni o odwołaniu", - "you_organizer": "Jesteś organizatorem", - "you_attendee": "Jesteś uczestnikiem", - "no_participants": "Brak uczestników", - "count": "{count, plural, one {# uczestnik} other {# uczestników}}" - }, - "recurrence": { - "title": "Powtarzanie", - "none": "Nie powtarza się", - "daily": "Codziennie", - "weekly": "Co tydzień", - "monthly": "Co miesiąc", - "yearly": "Co rok", - "every_n_days": "Co {count} dni", - "every_n_weeks": "Co {count} tygodni", - "every_n_months": "Co {count} miesięcy", - "until": "Do", - "occurrences": "{count} wystąpień", - "custom": "Niestandardowe…", - "edit_custom": "Edytuj niestandardowe powtarzanie", - "every_n_years": "Co {count} lat", - "on_days": "w {days}", - "on_day_n": "{day}. dnia miesiąca", - "on_the_nth": "w {nth} {day}", - "in_month": "w {month}", - "nth_1": "pierwszy", - "nth_2": "drugi", - "nth_3": "trzeci", - "nth_4": "czwarty", - "nth_last": "ostatni", - "editor_freq_day": "Dzień", - "editor_freq_week": "Tydzień", - "editor_freq_month": "Miesiąc", - "editor_freq_year": "Rok", - "editor_repeats_on": "Powtarza się", - "editor_every": "Co", - "editor_unit_days": "dni", - "editor_unit_weeks": "tyg.", - "editor_unit_months": "mies.", - "editor_unit_years": "lat(a)", - "editor_on_day": "dnia", - "editor_on_the": "w", - "editor_in": "w miesiącu", - "editor_ends": "Kończy się", - "editor_never": "Nigdy", - "editor_ends_on": "Dnia", - "editor_ends_after": "Po", - "editor_occurrences": "wystąpieniach" - }, - "recurrence_scope": { - "edit_title": "Edytuj wydarzenie cykliczne", - "delete_title": "Usuń wydarzenie cykliczne", - "description": "To jest wydarzenie cykliczne. Które wydarzenia chcesz zmodyfikować?", - "this_event": "Tylko to wydarzenie", - "this_and_future": "To i następne wydarzenia", - "all_events": "Wszystkie wydarzenia", - "cancel": "Anuluj", - "save": "Zapisz", - "delete": "Usuń", - "rsvp_title": "Odpowiedz na wydarzenie cykliczne", - "rsvp_description": "To jest wydarzenie cykliczne. Których wydarzeń dotyczy Twoja odpowiedź?", - "respond": "Odpowiedz" - }, - "alerts": { - "title": "Przypomnienie", - "none": "Brak przypomnienia", - "at_time": "W czasie wydarzenia", - "minutes_before": "{count, plural, one {# minutę przed} other {# minut przed}}", - "hours_before": "{count, plural, one {# godzinę przed} other {# godzin przed}}", - "days_before": "{count, plural, one {# dzień przed} other {# dni przed}}", - "weeks_before": "{count, plural, one {# tydzień przed} few {# tygodnie przed} many {# tygodni przed} other {# tygodnia przed}}", - "unit_minutes_before": "minut przed", - "unit_hours_before": "godzin przed", - "unit_days_before": "dni przed", - "unit_weeks_before": "tygodni przed", - "add": "Dodaj przypomnienie", - "remove": "Usuń przypomnienie", - "amount": "Wartość przypomnienia", - "unit": "Jednostka przypomnienia" - }, - "settings": { - "title": "Ustawienia kalendarza", - "default_view": "Domyślny widok", - "week_starts_on": "Tydzień zaczyna się w", - "time_format": "Format czasu", - "default_calendar": "Domyślny kalendarz", - "default_reminder": "Domyślne przypomnienie", - "time_format_12h": "12-godzinny", - "time_format_24h": "24-godzinny", - "notifications_enabled": "Powiadomienia o wydarzeniach", - "notifications_enabled_desc": "Pokazuj alerty o nadchodzących wydarzeniach w kalendarzu", - "notification_sound": "Dźwięk powiadomienia", - "notification_sound_desc": "Odtwarzaj dźwięk dla alertów kalendarza", - "invitation_parsing": "Analizuj zaproszenia e-mail", - "invitation_parsing_desc": "Wykrywaj zaproszenia kalendarza w załącznikach e-mail i pokazuj działania kalendarza", - "show_time_in_month_view": "Pokaż godzinę w widoku miesiąca", - "show_time_in_month_view_desc": "Wyświetlaj godziny wydarzeń w miesięcznym widoku kalendarza", - "show_week_numbers": "Pokaż numery tygodni", - "show_week_numbers_desc": "Wyświetlaj numery tygodni w mini-kalendarzu", - "calendar_free_scroll": "Swobodne przewijanie", - "calendar_free_scroll_desc": "Przewijaj płynnie miesiące, tygodnie i dni zamiast jednego okresu naraz", - "enable_tasks": "Włącz zadania", - "enable_tasks_desc": "Pokaż widok zadań w kalendarzu do zarządzania zadaniami", - "show_tasks_on_calendar": "Pokaż zadania w kalendarzu", - "show_tasks_on_calendar_desc": "Wyświetlaj oznaczenia zadań w dziennym i tygodniowym widoku kalendarza", - "hover_preview": "Podgląd wydarzenia po najechaniu", - "hover_preview_desc": "Pokaż wyskakujące okno szczegółów po najechaniu na wydarzenia w kalendarzu", - "hover_preview_instant": "Natychmiast", - "hover_preview_delay_500ms": "Opóźnienie 0,5 sekundy", - "hover_preview_delay_1s": "Opóźnienie 1 sekundy", - "hover_preview_delay_2s": "Opóźnienie 2 sekund", - "hover_preview_off": "Wyłączono", - "organizer_identity": "Organizuj zaproszenia jako", - "organizer_identity_desc": "Adres kalendarza używany jako organizator, gdy zapraszasz osoby na wydarzenia.", - "organizer_identity_failed": "Nie udało się zmienić adresu organizatora", - "show_birthday_calendar": "Kalendarz urodzin kontaktów", - "show_birthday_calendar_desc": "Pokaż wirtualny kalendarz z urodzinami z kontaktów" - }, - "days": { - "monday": "Poniedziałek", - "tuesday": "Wtorek", - "wednesday": "Środa", - "thursday": "Czwartek", - "friday": "Piątek", - "saturday": "Sobota", - "sunday": "Niedziela", - "mon": "Pon", - "tue": "Wt", - "wed": "Śr", - "thu": "Czw", - "fri": "Pt", - "sat": "Sob", - "sun": "Ndz" - }, - "notifications": { - "event_created": "Wydarzenie utworzono", - "event_updated": "Wydarzenie zaktualizowano", - "event_deleted": "Wydarzenie usunięto", - "calendar_created": "Kalendarz utworzono", - "calendar_deleted": "Kalendarz usunięto", - "event_move_error": "Nie udało się przenieść wydarzenia", - "event_resize_error": "Nie udało się zmienić rozmiaru wydarzenia", - "alert_title": "Nadchodzące wydarzenie", - "alert_now": "Zaczyna się teraz", - "alert_in_minutes": "Za {count} min", - "invitation_sent": "Zaproszenia wysłano", - "invitations_denied_title": "Nie można wysłać zaproszeń", - "invitations_denied": "Serwer odmówił wysłania zaproszeń: {reason}. Zapisać wydarzenie bez ich wysyłania?", - "save_without_invitations": "Zapisz bez zaproszeń", - "rsvp_updated": "Odpowiedź zaktualizowano", - "rsvp_error": "Nie udało się zaktualizować odpowiedzi", - "event_duplicated": "Wydarzenie zduplikowano", - "event_error": "Nie udało się zapisać wydarzenia", - "task_due": "Termin zadania", - "event_exported": "Wydarzenie wyeksportowane", - "title_copied": "Tytuł skopiowany", - "link_copied": "Link skopiowany" - }, - "status": { - "loading_calendars": "Ładowanie kalendarzy...", - "loading_events": "Ładowanie wydarzeń..." - }, - "quick_create": { - "placeholder": "Tytuł nowego wydarzenia", - "aria_label": "Szybkie tworzenie wydarzenia" - }, - "nav_prev": "Poprzedni", - "nav_next": "Następny", - "import": { - "title": "Importuj kalendarz", - "tab_file": "Plik", - "tab_url": "URL", - "select_file": "Wybierz plik .ics", - "drop_file": "lub upuść plik tutaj", - "supported_formats": "Obsługuje pliki iCalendar (.ics)", - "url_description": "Wprowadź adres URL zewnętrznego kanału iCalendar (.ics), aby zaimportować wydarzenia.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Obsługuje adresy URL iCalendar (.ics)", - "fetch": "Pobierz", - "invalid_url": "Wprowadź prawidłowy adres URL", - "url_fetch_failed": "Nie udało się pobrać kalendarza z adresu URL", - "parsing": "Analizowanie pliku kalendarza...", - "parsed_events": "Znaleziono {count} wydarzeń", - "no_events": "Nie znaleziono wydarzeń w pliku", - "select_all": "Zaznacz wszystko", - "deselect_all": "Odznacz wszystko", - "target_calendar": "Importuj do kalendarza", - "import_button": "Importuj zaznaczone", - "importing": "Importowanie wydarzeń...", - "success": "Pomyślnie zaimportowano {count} wydarzeń", - "error": "Nie udało się zaimportować kalendarza", - "file_too_large": "Plik przekracza limit 10 MB", - "invalid_format": "Nieprawidłowy format pliku kalendarza" - }, - "webcal_action": { - "title": "Otwórz link kalendarza", - "description": "Jak chcesz użyć \"{name}\"?", - "import_title": "Importuj jednorazowo", - "import_description": "Pobierz wydarzenia teraz i skopiuj je do jednego ze swoich kalendarzy.", - "subscribe_title": "Subskrybuj", - "subscribe_description": "Automatycznie synchronizuj ten kalendarz jako oddzielny kalendarz.", - "cancel": "Anuluj" - }, - "management": { - "title": "Zarządzanie kalendarzem", - "description": "Twórz, zmieniaj nazwy i dostosowuj swoje kalendarze. Kliknij prawym przyciskiem kalendarz na pasku bocznym, aby szybko zmienić jego kolor.", - "name": "Nazwa", - "name_placeholder": "Nazwa kalendarza", - "color": "Kolor", - "change_color": "Zmień kolor", - "random_color": "Nowy losowy kolor", - "add_calendar": "Dodaj kalendarz", - "edit": "Edytuj", - "delete": "Usuń", - "save": "Zapisz", - "create": "Utwórz", - "cancel": "Anuluj", - "default": "Domyślny", - "confirm_delete": "Usunąć \"{name}\"? Wszystkie wydarzenia w tym kalendarzu zostaną usunięte.", - "confirm_clear": "Wyczyścić wszystkie wydarzenia z \"{name}\"? Tej operacji nie można cofnąć.", - "set_default": "Ustaw jako domyślny", - "default_updated": "Zaktualizowano domyślny kalendarz", - "error_default": "Nie udało się ustawić domyślnego kalendarza", - "clear_events": "Wyczyść wydarzenia", - "events_cleared": "Wyczyszczono {count} wydarzeń", - "error_clear": "Nie udało się wyczyścić wydarzeń kalendarza", - "calendar_created": "Kalendarz utworzono", - "calendar_updated": "Kalendarz zaktualizowano", - "calendar_deleted": "Kalendarz usunięto", - "color_updated": "Kolor kalendarza zaktualizowano", - "error_create": "Nie udało się utworzyć kalendarza", - "error_update": "Nie udało się zaktualizować kalendarza", - "error_delete": "Nie udało się usunąć kalendarza", - "caldav_url": "Adres URL CalDAV", - "copy_url": "Kopiuj adres URL CalDAV", - "url_copied": "Adres URL CalDAV skopiowano do schowka", - "share": "Udostępnij kalendarz", - "new_event_in_calendar": "Nowe wydarzenie w tym kalendarzu", - "kind": "Zawartość", - "kind_events": "Wydarzenia", - "kind_tasks": "Zadania", - "kind_both": "Wydarzenia i zadania", - "kind_hint": "Określa, które aplikacje widzą ten kalendarz podczas synchronizacji przez CalDAV. Nie można tego później zmienić." - }, - "subscription": { - "title": "Subskrypcja iCal", - "section_title": "Subskrypcje iCal", - "description": "Subskrybuj zewnętrzny kanał iCalendar. Wydarzenia będą automatycznie synchronizowane do osobnego kalendarza. Obsługuje adresy URL https:// i webcal://.", - "url_label": "Adres URL kalendarza", - "url_placeholder": "https://example.com/calendar.ics lub webcal://...", - "name_label": "Nazwa kalendarza", - "name_placeholder": "np. Święta publiczne", - "color_label": "Kolor", - "refresh_interval": "Częstotliwość odświeżania", - "interval_15": "Co 15 minut", - "interval_30": "Co 30 minut", - "interval_60": "Co godzinę", - "interval_360": "Co 6 godzin", - "interval_1440": "Codziennie", - "subscribe": "Subskrybuj", - "subscribing": "Subskrybowanie...", - "save": "Zapisz zmiany", - "saving": "Zapisywanie...", - "edit": "Edytuj", - "edit_title": "Edytuj subskrypcję", - "updated": "Zaktualizowano \"{name}\"", - "update_error": "Nie udało się zaktualizować subskrypcji", - "invalid_url": "Wprowadź prawidłowy adres URL", - "success": "Zasubskrybowano \"{name}\"", - "error": "Nie udało się dodać subskrypcji", - "refresh": "Odśwież teraz", - "refresh_success": "Subskrypcję odświeżono", - "refresh_error": "Nie udało się odświeżyć subskrypcji", - "unsubscribe": "Anuluj subskrypcję", - "confirm_delete": "Anulować subskrypcję \"{name}\"? Kalendarz i wszystkie jego wydarzenia zostaną usunięte.", - "deleted": "Subskrypcję usunięto", - "delete_error": "Nie udało się usunąć subskrypcji", - "last_refreshed": "Ostatnia aktualizacja: {time}" - }, - "tasks": { - "label": "Zadania", - "no_tasks": "Brak zadań", - "no_title": "(Bez tytułu)", - "mark_complete": "Oznacz jako ukończone", - "mark_incomplete": "Oznacz jako nieukończone", - "filter_all": "Wszystkie", - "filter_pending": "Oczekujące", - "filter_completed": "Ukończone", - "filter_overdue": "Zaległe", - "show_completed": "Pokaż ukończone", - "create": "Nowe zadanie", - "edit": "Edytuj zadanie", - "title_placeholder": "Tytuł zadania", - "description_placeholder": "Dodaj opis...", - "due_date": "Termin", - "include_time": "Uwzględnij godzinę", - "priority": "Priorytet", - "priority_none": "Brak", - "priority_high": "Wysoki", - "priority_medium": "Średni", - "priority_low": "Niski", - "progress": "Status", - "progress_needs_action": "Wymaga działania", - "progress_in_process": "W trakcie", - "progress_completed": "Ukończone", - "progress_cancelled": "Anulowano", - "calendar": "Kalendarz", - "alert": "Przypomnienie", - "alert_none": "Brak", - "alert_at_time": "W czasie terminu", - "alert_5min": "5 minut przed", - "alert_15min": "15 minut przed", - "alert_30min": "30 minut przed", - "alert_1hr": "1 godzinę przed", - "alert_1day": "1 dzień przed", - "delete": "Usuń", - "cancel": "Anuluj", - "save": "Zapisz", - "quick_add_placeholder": "Dodaj zadanie...", - "due_today": "Dzisiaj", - "due_tomorrow": "Jutro", - "overdue": "Zaległe" - }, - "months": { - "jan": "sty", - "feb": "lut", - "mar": "mar", - "apr": "kwi", - "may": "maj", - "jun": "cze", - "jul": "lip", - "aug": "sie", - "sep": "wrz", - "oct": "paź", - "nov": "lis", - "dec": "gru", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Otwórz menu" - }, - "advanced_search": { - "title": "Wyszukiwanie zaawansowane", - "from": "Od", - "from_placeholder": "Adres e-mail lub nazwa nadawcy", - "to": "Do", - "to_placeholder": "Adres e-mail lub nazwa odbiorcy", - "subject": "Temat", - "subject_placeholder": "Temat zawiera...", - "body": "Treść", - "body_placeholder": "Treść zawiera...", - "folder": "Folder", - "all_folders": "Wszystkie foldery", - "has_attachment": "Załączniki", - "date_after": "Po", - "date_before": "Przed", - "size_min": "Większe niż (KB)", - "size_max": "Mniejsze niż (KB)", - "starred": "Z gwiazdką", - "unread": "Nieprzeczytane", - "read": "Przeczytane", - "yes": "Tak", - "no": "Nie", - "clear": "Wyczyść", - "clear_all": "Wyczyść wszystko", - "filters_active": "{count} filtr", - "filters_active_plural": "{count} filtry", - "toggle_filters": "Więcej", - "search_hint": "Użyj zaawansowanych filtrów, aby precyzyjnie wyszukiwać", - "advanced_filters_tooltip": "Zaawansowane filtry wyszukiwania", - "results_found": "{count, plural, =0 {Nie znaleziono wyników} one {Znaleziono # wynik} other {Znaleziono # wyników}}", - "results_found_more": "Znaleziono {count}+ wyników", - "suggestions_recent": "Ostatnie wyszukiwania", - "suggestions_people": "Osoby", - "suggestions_from": "Szukaj wiadomości od {address}", - "suggestions_to": "Szukaj wiadomości do {address}", - "suggestions_remove_recent": "Usuń z ostatnich wyszukiwań" - }, - "welcome": { - "title": "Witaj w swojej skrzynce pocztowej", - "tip_compose": "Naciśnij c, aby napisać nową wiadomość e-mail", - "tip_shortcuts": "Naciśnij ?, aby zobaczyć wszystkie skróty klawiszowe", - "tip_sidebar": "Kalendarz, Kontakty i Ustawienia znajdziesz w menu bocznym", - "tip_settings": "Dostosuj działanie aplikacji w Ustawieniach", - "got_it": "Rozumiem", - "settings": "Ustawienia", - "dismiss": "Zamknij", - "start_tour": "Rozpocznij przewodnik" - }, - "demo_welcome": { - "title": "Witaj w Bulwark Mail", - "description": "Poznaj w pełni funkcjonalnego klienta poczty webowej - bezpośrednio w przeglądarce. Wszystkie dane pozostają na Twoim urządzeniu, więc możesz śmiało wszystko przetestować.", - "feature_email": "Czytaj i pisz wiadomości e-mail", - "feature_organize": "Etykiety, gwiazdki i foldery", - "feature_shortcuts": "Skróty klawiszowe", - "feature_privacy": "100% prywatna wersja demo", - "hint": "Kliknij dowolną wiadomość po lewej, aby zacząć, albo skorzystaj z przewodnika poniżej." - }, - "files": { - "office_edit": "Edytuj dokument", - "office_loading": "Otwieranie edytora dokumentów…", - "office_error": "Nie udało się otworzyć edytora dokumentów.", - "office_close": "Zamknij edytor", - "open_in_new_tab": "Otwórz w nowej karcie", - "title": "Pliki", - "search_placeholder": "Szukaj plików...", - "empty_state_title": "Brak plików", - "empty_state_description": "Prześlij pliki lub utwórz foldery, aby rozpocząć", - "upload": "Prześlij", - "upload_files": "Prześlij pliki", - "new_folder": "Nowy folder", - "new_folder_name": "Nazwa folderu", - "rename": "Zmień nazwę", - "rename_title": "Zmień nazwę", - "new_name": "Nowa nazwa", - "name_forbidden_chars": "Serwer nie zezwala na te znaki w nazwach: {chars}", - "name_reserved": "Ta nazwa jest zarezerwowana na serwerze.", - "delete": "Usuń", - "delete_confirm_title": "Usuń zasób", - "delete_confirm_message": "Czy na pewno chcesz usunąć \"{name}\"? Tej operacji nie można cofnąć.", - "download": "Pobierz", - "name": "Nazwa", - "size": "Rozmiar", - "modified": "Zmodyfikowano", - "type": "Typ", - "folder": "Folder", - "file": "Plik", - "parent_directory": "Katalog nadrzędny", - "breadcrumb_root": "Strona główna", - "drop_files_here": "Upuść pliki lub foldery tutaj, aby je przesłać", - "uploading": "Przesyłanie...", - "upload_success": "{count, plural, one {Przesłano 1 plik} other {Przesłano # plików}}", - "upload_error": "Nie udało się przesłać pliku", - "create_folder_success": "Folder utworzono", - "create_folder_error": "Nie udało się utworzyć folderu", - "delete_success": "Usunięto pomyślnie", - "delete_error": "Nie udało się usunąć", - "rename_success": "Nazwę zmieniono pomyślnie", - "rename_error": "Nie udało się zmienić nazwy", - "download_error": "Nie udało się pobrać", - "not_available": "Przechowywanie plików nie jest dostępne na tym serwerze", - "cancel": "Anuluj", - "create": "Utwórz", - "save": "Zapisz", - "no_results": "Żadne pliki nie pasują do wyszukiwania", - "batch_delete_confirm_message": "Czy na pewno chcesz usunąć {count, plural, one {1 element} other {# elementów}}? Tej operacji nie można cofnąć.", - "batch_delete_success": "{count, plural, one {Usunięto 1 element} other {Usunięto # elementów}}", - "grid_view": "Widok siatki", - "list_view": "Widok listy", - "details": "Szczegóły", - "path": "Ścieżka", - "preview": "Podgląd", - "preview_error": "Nie udało się załadować podglądu", - "cut": "Wytnij", - "copy": "Kopiuj", - "paste": "Wklej", - "move_success": "{count, plural, one {Przeniesiono 1 element} other {Przeniesiono # elementów}}", - "move_error": "Nie udało się przenieść", - "paste_success": "Wklejono pomyślnie", - "paste_error": "Nie udało się wkleić", - "new_text_file": "Nowy plik tekstowy", - "file_name": "Nazwa pliku", - "retry": "Ponów", - "refresh": "Odśwież", - "toggle_favorite": "Przełącz ulubione", - "duplicate": "Duplikuj", - "duplicate_success": "Zduplikowano pomyślnie", - "duplicate_error": "Nie udało się zduplikować", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Plik utworzono", - "create_file_error": "Nie udało się utworzyć pliku", - "favorites": "Ulubione", - "recent": "Ostatnie", - "properties": "Właściwości", - "open_folder": "Otwórz folder", - "upload_folder": "Prześlij folder", - "file_too_large": "\"{name}\" przekracza maksymalny rozmiar pliku ({max})", - "undo": "Cofnij", - "undo_success": "Cofnięto działanie", - "undo_error": "Nie udało się cofnąć", - "toolbar": "Działania na plikach", - "file_list": "Pliki i foldery", - "context_menu": "Działania", - "settings_title": "Ustawienia plików", - "settings_display": "Wyświetlanie", - "settings_default_view": "Domyślny widok", - "settings_default_view_desc": "Wybierz układ siatki lub listy", - "settings_default_sort": "Domyślne sortowanie", - "settings_default_sort_desc": "Wybierz domyślne sortowanie plików", - "settings_sort_direction": "Kierunek sortowania", - "settings_sort_direction_desc": "Wybierz kolejność rosnącą lub malejącą", - "settings_ascending": "Rosnąco", - "settings_descending": "Malejąco", - "settings_icons": "Ikony", - "settings_show_icons": "Pokaż ikony plików", - "settings_show_icons_desc": "Wyświetlaj ikony obok plików i folderów", - "settings_colored_icons": "Kolorowe ikony", - "settings_colored_icons_desc": "Używaj kolorowych ikon zamiast monochromatycznych", - "settings_show_thumbnails": "Pokaż miniatury", - "settings_show_thumbnails_desc": "Wyświetlaj podglądy obrazów zamiast ikon dla plików graficznych", - "settings_behavior": "Zachowanie", - "settings_show_hidden": "Pokaż ukryte pliki", - "settings_show_hidden_desc": "Wyświetlaj pliki i foldery zaczynające się od kropki", - "settings_folder_layout": "Nawigacja po folderach", - "settings_folder_layout_desc": "Wybierz sposób wyświetlania folderów: razem z plikami lub w drzewie na pasku bocznym", - "settings_folder_layout_inline": "Wbudowany", - "settings_folder_layout_sidebar": "Pasek boczny", - "disabled_title": "Funkcja Pliki jest wyłączona przez administratora", - "disabled_description": "Duże przesyłanie plików przez WebDAV może powodować niestabilność Stalwart/RocksDB, w tym awarie z powodu braku pamięci i nieodwracalne zużycie miejsca na dysku. Usunięte pliki mogą nie zostać natychmiast usunięte z magazynu blob. Ta funkcja nie jest zalecana w środowiskach produkcyjnych.", - "stability_warning": "Duże przesyłanie plików może powodować niestabilność serwera. Usunięte pliki mogą nie zostać natychmiast usunięte z magazynu. Używaj ostrożnie.", - "no_accounts": "Brak połączonych kont.", - "open_folder_tree": "Otwórz drzewo folderów", - "other_accounts": "Inne konta", - "migration_title": "Aktualizowanie plików…", - "migration_description": "Porządkowanie folderów i plików w odpowiedniej strukturze. Dzieje się to tylko raz." - }, - "smime": { - "your_certificates": "Twoje certyfikaty", - "your_certificates_desc": "Importuj i zarządzaj swoimi certyfikatami S/MIME do podpisywania i szyfrowania wiadomości e-mail", - "recipient_certificates": "Certyfikaty odbiorców", - "recipient_certificates_desc": "Certyfikaty publiczne do szyfrowania wiadomości e-mail do odbiorców", - "identity_bindings": "Powiązania kluczy tożsamości", - "identity_bindings_desc": "Powiąż certyfikaty S/MIME z tożsamościami e-mail", - "defaults_title": "Domyślne", - "defaults_desc": "Skonfiguruj domyślne zachowanie podpisywania i szyfrowania", - "import_pkcs12": "Importuj PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importuj certyfikat", - "no_certificates": "Nie zaimportowano jeszcze żadnych certyfikatów", - "no_recipient_certs": "Brak certyfikatów odbiorców", - "expires": "Wygasa", - "expired": "Wygasł", - "bound_to": "Powiązano z", - "no_key_bound": "Brak", - "lock": "Zablokuj klucz", - "unlock": "Odblokuj klucz", - "details": "Pokaż szczegóły", - "delete": "Usuń", - "encrypt_by_default": "Szyfruj domyślnie", - "encrypt_by_default_desc": "Automatycznie szyfruj wiadomości e-mail, gdy wszyscy odbiorcy mają certyfikaty", - "remember_unlocked": "Zapamiętaj odblokowane klucze", - "remember_unlocked_desc": "Pozostaw klucze odblokowane przez czas trwania tej sesji przeglądarki", - "sign_default_for": "Podpisuj domyślnie dla", - "enter_p12_passphrase": "Wprowadź hasło PKCS#12", - "p12_passphrase_desc": "Wprowadź hasło chroniące ten plik certyfikatu", - "enter_storage_passphrase": "Ustaw hasło magazynu", - "storage_passphrase_desc": "Wybierz hasło do ochrony tego klucza podczas przechowywania w przeglądarce", - "next": "Dalej", - "import": "Importuj", - "unlock_key": "Odblokuj klucz", - "unlock_key_desc": "Wprowadź hasło magazynu, aby odblokować ten klucz do podpisywania lub odszyfrowywania", - "passphrase_placeholder": "Wprowadź hasło", - "confirm_passphrase_placeholder": "Potwierdź hasło", - "passphrase_mismatch": "Hasła nie są zgodne", - "cancel": "Anuluj", - "processing": "Przetwarzanie…", - "close": "Zamknij", - "certificate_details": "Szczegóły certyfikatu", - "cert_subject": "Podmiot", - "cert_issuer": "Wystawca", - "cert_email": "E-mail", - "cert_serial": "Numer seryjny", - "cert_validity": "Ważność", - "cert_fingerprint": "Odcisk (SHA-256)", - "cert_algorithm": "Algorytm", - "cert_capabilities": "Możliwości", - "cert_source": "Źródło", - "cert_expired": "Ten certyfikat wygasł", - "cert_not_yet_valid": "Ten certyfikat nie jest jeszcze ważny", - "cap_sign": "Podpisywanie", - "cap_encrypt": "Szyfrowanie", - "cap_none": "Brak", - "show_passphrase": "Pokaż hasło", - "hide_passphrase": "Ukryj hasło", - "sign_toggle": "Podpisz", - "encrypt_toggle": "Szyfruj", - "missing_recipient_certs": "Brak certyfikatów dla: {emails}", - "missing_sender_cert": "Brak certyfikatu powiązanego z tą tożsamością", - "status_encrypted_ok": "Ta wiadomość została zaszyfrowana", - "status_encrypted_no_key": "Ta wiadomość jest zaszyfrowana, ale nie znaleziono pasującego klucza", - "status_encrypted_failed": "Nie udało się odszyfrować tej wiadomości", - "status_signed_valid": "Podpis zweryfikowano", - "status_signed_invalid": "Weryfikacja podpisu nie powiodła się", - "status_signed_expired_cert": "Podpisano wygasłym certyfikatem", - "status_signed_self_signed": "Podpis jest prawidłowy, ale certyfikat jest samopodpisany (niezaufany)", - "status_signed_mismatch": "Podpis jest prawidłowy, ale podpisujący nie zgadza się z nadawcą", - "status_unsupported": "Nieobsługiwany format S/MIME", - "auto_import_signer_certs": "Automatycznie importuj certyfikaty podpisujących", - "auto_import_signer_certs_desc": "Automatycznie zapisuj certyfikaty ze zweryfikowanych podpisanych wiadomości e-mail do przyszłego szyfrowania", - "export": "Eksportuj", - "enter_export_passphrase": "Ustaw hasło eksportu", - "export_passphrase_desc": "Wybierz hasło do ochrony eksportowanego pliku PKCS#12", - "export_storage_desc": "Wprowadź hasło magazynu, aby odszyfrować klucz do eksportu", - "incorrect_passphrase": "Nieprawidłowe hasło" - }, - "tour": { - "step_counter": "Krok {current} z {total}", - "skip": "Pomiń przewodnik", - "back": "Wstecz", - "next": "Dalej", - "finish": "Zakończ", - "take_a_tour": "Poznaj interfejs w krótkim przewodniku", - "restart_title": "Przewodnik wprowadzający", - "restart_desc": "Odtwórz przewodnik po interfejsie krok po kroku", - "restart_button": "Uruchom przewodnik ponownie", - "show_on_new_devices_title": "Pokaż na nowych urządzeniach", - "show_on_new_devices_desc": "Wyświetl ponownie baner powitalny i przewodnik przy pierwszym logowaniu na nowym urządzeniu, nawet jeśli zostały już ukończone w innym miejscu", - "sidebar_title": "Twoje skrzynki pocztowe", - "sidebar_desc": "To jest pasek boczny z folderami. Kliknij dowolną skrzynkę, aby zobaczyć jej wiadomości. Możesz tworzyć foldery, przeciągać między nimi wiadomości i od razu widzieć liczbę nieprzeczytanych.", - "compose_title": "Napisz wiadomość", - "compose_desc": "Kliknij tutaj, aby napisać nową wiadomość. Możesz dodać odbiorców, załączniki i korzystać z formatowania tekstu sformatowanego.", - "search_title": "Przeszukuj pocztę", - "search_desc": "Szukaj według nadawcy, tematu lub treści. Kliknij ikonę filtra, aby użyć zaawansowanych opcji, takich jak zakres dat, załączniki i wiadomości z gwiazdką.", - "email_list_title": "Lista wiadomości", - "email_list_desc": "Tutaj pojawiają się wiadomości. Kliknij jedną z nich, aby przeczytać ją po prawej stronie. Użyj pola wyboru, aby zaznaczyć kilka naraz, a potem zbiorczo je przenieść, usunąć lub oznaczyć etykietą.", - "email_viewer_title": "Panel czytania", - "email_viewer_desc": "Tutaj otwiera się wybrana wiadomość. Za pomocą przycisków na pasku narzędzi możesz odpowiedzieć, przesłać ją dalej, zarchiwizować lub usunąć. Możesz też oznaczać wiadomości gwiazdką albo dodawać kolorowe etykiety.", - "keywords_title": "Kolorowe etykiety", - "keywords_desc": "Porządkuj pocztę za pomocą kolorowych etykiet. Przeciągnij wiadomość na etykietę, aby ją oznaczyć, albo kliknij wiadomość prawym przyciskiem myszy, aby przypisać etykiety.", - "calendar_title": "Kalendarz", - "calendar_desc": "Przełącz się do kalendarza, aby zarządzać wydarzeniami. Możesz tworzyć wydarzenia, ustawiać przypomnienia i przełączać widok dnia, tygodnia lub miesiąca.", - "contacts_title": "Kontakty", - "contacts_desc": "Tutaj znajduje się Twoja książka adresowa. Importuj kontakty, twórz grupy i kliknij dowolny kontakt, aby zobaczyć jego pełne dane.", - "settings_title": "Ustawienia", - "settings_desc": "Dostosuj wszystko: motyw, gęstość widoku, podpisy, filtry, skróty klawiszowe, domyślne ustawienia kalendarza i wiele więcej.", - "shortcuts_title": "Skróty klawiszowe", - "shortcuts_desc": "To ulubiona funkcja zaawansowanych użytkowników. Naciśnij ? w dowolnym momencie, aby zobaczyć wszystkie dostępne skróty. Możesz poruszać się po aplikacji, pisać i zarządzać wiadomościami bez użycia myszy.", - "compose_open_title": "Edytor wiadomości", - "compose_open_desc": "To jest edytor wiadomości. Dodawaj odbiorców, pisz treść, załączaj pliki i korzystaj z formatowania tekstu sformatowanego. Możesz też zapisywać szkice i używać szablonów.", - "calendar_view_title": "Twój kalendarz", - "calendar_view_desc": "Tutaj widzisz kalendarz z przykładowymi wydarzeniami. Za pomocą paska narzędzi możesz przełączać widok dnia, tygodnia, miesiąca i agendy.", - "create_event_title": "Utwórz wydarzenie", - "create_event_desc": "Kliknij ten przycisk, aby utworzyć nowe wydarzenie w kalendarzu. Możesz ustawić tytuł, datę, godzinę i dodać uczestników.", - "event_modal_title": "Szczegóły wydarzenia", - "event_modal_desc": "To jest formularz wydarzenia. Uzupełnij tytuł, wybierz datę i godzinę, dodaj lokalizację albo uczestników. Gdy skończysz, kliknij zapisz - albo zamknij okno i przejdź dalej.", - "contacts_list_title": "Twoje kontakty", - "contacts_list_desc": "Tutaj są Twoje kontakty. Kliknij dowolny kontakt, aby zobaczyć jego pełne dane po prawej stronie. Możesz też tworzyć nowe kontakty, importować vCardy i organizować kontakty w grupy.", - "settings_tabs_title": "Menu ustawień", - "settings_tabs_desc": "Tutaj znajdują się wszystkie kategorie ustawień. Dostosuj wygląd, zarządzaj tożsamościami, skonfiguruj filtry poczty, ustawienia kalendarza i wiele więcej.", - "files_title": "Przechowywanie plików", - "files_desc": "Przeglądarka plików pozwala przesyłać, porządkować i udostępniać pliki - jak osobisty dysk w chmurze wbudowany w Twoją pocztę.", - "demo_banner_title": "Sterowanie wersją demo", - "demo_banner_desc": "Jesteś w trybie demo - wszystko pozostaje w Twojej przeglądarce. W każdej chwili kliknij „Reset Demo”, aby zacząć od nowa z czystymi danymi przykładowymi.", - "quota_title": "Wykorzystanie miejsca", - "quota_desc": "Tutaj możesz śledzić rozmiar swojej skrzynki pocztowej. Okrąg wypełnia się wraz ze wzrostem użycia przestrzeni." - }, - "unified_mailbox": { - "search_unavailable": "Wyszukiwanie jest niedostępne w widoku ujednoliconym" - }, - "calendar_event_notifications": { - "someone": "Ktoś", - "invited": "{name} zaprosił(a) Cię na „{title}”", - "updated": "{name} zaktualizował(a) „{title}”", - "cancelled": "{name} odwołał(a) „{title}”", - "untitled": "Wydarzenie bez tytułu" - }, - "share_notifications": { - "someone": "Ktoś", - "shared": "{name} udostępnił(a) Ci {kind} „{object}”", - "changed": "{name} zmienił(a) Twój dostęp do {kind} „{object}”", - "revoked": "{name} odebrał(a) Ci dostęp do {kind} „{object}”", - "object": { - "folder": "folder", - "calendar": "kalendarz", - "address_book": "książkę adresową", - "files": "folder plików" - } - }, - "sharing": { - "title": "Udostępnij „{name}\"", - "description": "Udziel dostępu innym użytkownikom lub grupom na tym serwerze. Zmiany są natychmiastowe.", - "no_shares": "Jeszcze nie udostępniono.", - "add_person": "Dodaj osobę lub grupę", - "search_placeholder": "Szukaj po imieniu lub e-mailu…", - "loading_principals": "Ładowanie użytkowników…", - "no_principals": "Nie znaleziono innych użytkowników ani grup.", - "no_match": "Brak wyników.", - "remove": "Usuń dostęp", - "group": "Grupa", - "share_added": "Dostęp przyznany", - "share_updated": "Dostęp zaktualizowany", - "share_removed": "Dostęp usunięty", - "share_failed": "Nie udało się zaktualizować udostępniania", - "preset": { - "freeBusy": "Tylko dostępność", - "read": "Tylko do odczytu", - "readWrite": "Odczyt i zapis", - "manager": "Menedżer", - "custom": "Niestandardowe" - } - }, - "quote_header": { - "reply_line": "{date}, {from} napisał(a):", - "forwarded_separator": "---------- Wiadomość przekazana ----------", - "from_label": "Od", - "date_label": "Data", - "subject_label": "Temat" - }, - "pwa_install": { - "title": "Zainstaluj {appName}", - "description": "Zainstaluj naszą aplikację, aby uzyskać szybki dostęp i obsługę offline.", - "not_now": "Nie teraz", - "install": "Zainstaluj", - "dont_remind": "Nie przypominaj mi więcej", - "dismiss_aria": "Zamknij monit instalacji" - }, - "deep_link": { - "message_not_found": "Ta wiadomość nie jest już dostępna.", - "thread_not_found": "Ta rozmowa nie jest już dostępna.", - "folder_not_found": "Ten folder nie jest już dostępny.", - "event_not_found": "To wydarzenie nie jest już dostępne.", - "file_not_found": "Ten plik nie jest już dostępny.", - "account_unavailable": "Zaloguj się na to konto, aby otworzyć ten link.", - "copy_message": "Kopiuj link do wiadomości", - "copy_conversation": "Kopiuj link do rozmowy", - "copy_event": "Kopiuj link do wydarzenia", - "copied": "Link skopiowany", - "copy_failed": "Nie udało się skopiować linku" - }, - "plugins": { - "oauth_callback": { - "completing": "Kończenie autoryzacji…", - "completing_message": "Proszę czekać na nawiązanie połączenia.", - "received_title": "Otrzymano autoryzację", - "received_message": "Możesz wrócić do karty, w której rozpoczęto nawiązywanie połączenia.", - "failed_title": "Autoryzacja nie powiodła się", - "denied": "Autoryzacja została odrzucona.", - "failed_detail": "Autoryzacja nie powiodła się: {error}", - "missing_params": "Brak kodu autoryzacji lub stanu.", - "delivery_failed": "Nie udało się przekazać wyniku autoryzacji do karty aplikacji. Zamknij to okno i spróbuj ponownie." - } - }, - "global_search": { - "title": "Szukaj wszędzie", - "placeholder": "Szukaj w poczcie, kontaktach, kalendarzu, plikach...", - "scope_label": "Szukaj w", - "account_label": "Konto", - "scope_all": "Wszystko", - "scope_mail": "Poczta", - "scope_contacts": "Kontakty", - "scope_calendar": "Kalendarz", - "scope_files": "Pliki", - "all_accounts": "Wszystkie konta", - "show_all": "Pokaż wszystkie wyniki", - "no_results": "Brak wyników dla „{query}”", - "server_hint": "Całe słowa przeszukują serwer; części słów pasują do już wczytanych elementów", - "searching": "Szukanie...", - "account_error": "Nie udało się przeszukać {account}", - "recent_searches": "Ostatnie wyszukiwania", - "recurring": "Cykliczne", - "no_subject": "(no subject)", - "empty_hint": "Szukaj poczty, kontaktów, wydarzeń i plików na wszystkich kontach. Wypróbuj from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Płasko według daty", - "grouped": "Pogrupowane", - "load_more": "Wczytaj więcej", - "preview_empty": "Wybierz wynik, aby zobaczyć podgląd", - "open_full": "Otwórz", - "view_label": "Widok" - } -} +{ + "meta_description": "Minimalistyczny klient poczty internetowej wykorzystujący protokół JMAP", + "login": { + "title": "Webmail", + "username_label": "E-mail", + "username_placeholder": "user@example.com", + "password_label": "Hasło", + "password_placeholder": "Wprowadź hasło", + "jmap_endpoint_label": "Serwer JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Serwer musi zezwalać na żądania CORS z tej domeny.", + "jmap_server_label": "Serwer", + "jmap_server_auto_picked": "Serwer wybrany na podstawie domeny Twojej poczty.", + "sign_in": "Zaloguj się", + "signing_in": "Logowanie...", + "loading": "Ładowanie...", + "reconnecting": "Utracono połączenie. Próba ponownego połączenia…", + "error": { + "invalid_credentials": "Nieprawidłowy adres e-mail lub hasło. Sprawdź dane logowania i spróbuj ponownie.", + "invalid_token": "Serwer nie zaakceptował tego tokenu dostępu. Sprawdź, czy jest kompletny i czy nie wygasł ani nie został unieważniony.", + "connection_failed": "Nie można połączyć się z serwerem. Sprawdź połączenie internetowe i spróbuj ponownie.", + "cors_blocked": "Serwer jest osiągalny, ale blokuje żądania między domenami. Sprawdź ustawienia CORS serwera JMAP i zezwól na tę domenę.", + "server_error": "Serwer jest tymczasowo niedostępny. Spróbuj ponownie później.", + "generic": "Wystąpił nieoczekiwany błąd. Jeśli problem się powtarza, skontaktuj się z administratorem.", + "totp_required": "Wymagany jest kod uwierzytelniania dwuskładnikowego. Wprowadź kod poniżej.", + "totp_invalid": "Nieprawidłowy kod uwierzytelniania. Sprawdź aplikację uwierzytelniającą i spróbuj ponownie.", + "oauth_discovery_failed": "SSO jest włączone, ale nie można połączyć się z dostawcą tożsamości. Sprawdź konfigurację OAuth.", + "token_exchange_failed": "Hasło i kod zostały zaakceptowane, ale serwer poczty odmówił rozpoczęcia sesji dla tej aplikacji. Poproś administratora o sprawdzenie ustawień klienta OAuth." + }, + "show_password": "Pokaż hasło", + "hide_password": "Ukryj hasło", + "totp_toggle": "Mam kod 2FA", + "token_toggle": "Zaloguj się tokenem dostępu", + "password_toggle": "Zaloguj się hasłem", + "token_label": "Token dostępu", + "token_placeholder": "Wklej swój token API", + "token_hint": "Utwórz token API w ustawieniach swojego dostawcy poczty i wklej go tutaj.", + "remember_me": "Zapamiętaj mnie", + "config_error": { + "title": "Błąd konfiguracji", + "fetch_failed": "Nie można załadować konfiguracji aplikacji. Spróbuj ponownie później.", + "server_not_configured": "Serwer poczty nie został skonfigurowany. Skontaktuj się z administratorem." + }, + "remove_from_history": "Usuń z historii", + "totp_label": "Kod uwierzytelniania", + "totp_placeholder": "000000", + "session_expired": "Twoja sesja wygasła. Zaloguj się ponownie.", + "dismiss": "Zamknij", + "or": "lub", + "sign_in_sso": "Zaloguj się przez SSO", + "add_account_title": "Dodaj konto", + "add_account_subtitle": "Zaloguj się na inne konto", + "cancel": "Anuluj", + "website": "Strona internetowa", + "imprint": "Impressum", + "privacy_policy": "Polityka prywatności", + "try_demo": "Wypróbuj demo", + "demo_description": "Poznaj aplikację z przykładowymi danymi - bez potrzeby zakładania konta", + "demo_launching": "Uruchamianie demo...", + "demo_login_button": "Uruchom demo", + "demo_tagline": "Wypróbuj w pełni funkcjonalnego klienta poczty. Konto nie jest wymagane.", + "demo_no_signup": "Bez rejestracji - eksploruj swobodnie z przykładowymi danymi", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Kończenie logowania...", + "oauth_error": { + "title": "Uwierzytelnianie nie powiodło się", + "invalid_state": "Walidacja bezpieczeństwa nie powiodła się. Spróbuj zalogować się ponownie.", + "missing_params": "Brakujące dane autoryzacji. Spróbuj zalogować się ponownie.", + "token_exchange_failed": "Nie udało się dokończyć uwierzytelniania. Spróbuj ponownie.", + "access_denied": "Odmowa dostępu. Skontaktuj się z administratorem.", + "back_to_login": "Powrót do logowania" + } + }, + "sidebar": { + "close": "Zamknij", + "compose": "Napisz", + "compose_hint": "Napisz (c)", + "search_placeholder": "Szukaj poczty...", + "search_placeholder_hint": "Szukaj poczty... (naciśnij /)", + "storage": "Przestrzeń dyskowa", + "storage_used": "Zajęte", + "storage_free": "Wolne", + "storage_total": "Łącznie", + "sign_out": "Wyloguj się", + "sign_out_of": "Wyloguj się z {account}", + "sign_out_all": "Wyloguj się ze wszystkich kont", + "add_account": "Dodaj konto", + "set_as_default": "Ustaw jako domyślne", + "switch_account": "Przełącz konto", + "contacts": "Kontakty", + "calendar": "Kalendarz", + "settings": "Ustawienia", + "admin": "Administracja", + "files": "Pliki", + "loading_mailboxes": "Ładowanie skrzynek...", + "push_connected": "Aktualizacje w czasie rzeczywistym aktywne", + "push_disconnected": "Aktualizacje w czasie rzeczywistym nieaktywne", + "keyboard_shortcuts": "Skróty klawiszowe", + "theme": { + "light": "Tryb jasny", + "dark": "Tryb ciemny", + "system": "Motyw systemowy" + }, + "language": { + "title": "Język" + }, + "mailboxes": { + "inbox": "Odebrane", + "sent": "Wysłane", + "drafts": "Szkice", + "trash": "Kosz", + "archive": "Archiwum", + "starred": "Oznaczone gwiazdką", + "all_mail": "Cała poczta", + "spam": "Spam", + "important": "Ważne" + }, + "unified_inbox": "Wspólne odebrane", + "unified_sent": "Wszystkie wysłane", + "unified_drafts": "Wszystkie szkice", + "unified_trash": "Wszystkie kosze", + "unified_archive": "Wszystkie archiwa", + "unified_junk": "Wszystkie spam", + "all_accounts": "Wszystkie konta", + "all_accounts_loading": "Wczytywanie kont: {loaded} z {total}", + "unified_mailbox": "Wspólna skrzynka", + "expand": "Rozwiń", + "collapse": "Zwiń", + "expand_tooltip": "Rozwiń", + "collapse_tooltip": "Zwiń", + "mobile": { + "search": "Szukaj", + "compose": "Napisz", + "go_back": "Wróć", + "toggle_menu": "Przełącz menu" + }, + "clear_search": "Wyczyść wyszukiwanie", + "vacation_active": "Autoresponder wakacyjny jest aktywny", + "demo_banner": "Tryb demo", + "demo_reset": "Resetuj", + "demo_tour": "Przewodnik", + "tags": "Etykiety", + "show_all_tags": "Pokaż wszystkie ({count})", + "show_fewer_tags": "Pokaż mniej", + "folders": "Foldery", + "your_folders": "Twoje foldery", + "create_folder": "Utwórz folder", + "show_more": "Więcej", + "show_fewer": "Mniej", + "mail": "Poczta", + "nav_label": "Nawigacja", + "add_app": "Aplikacje", + "shared": "Udostępnione", + "shared_account_hidden": "Ukryto na pasku bocznym. Przywrócisz je w Ustawienia › Układ.", + "notifications_muted": "Powiadomienia wyciszone", + "scheduled": "Zaplanowane", + "unified_all_unread": "Wszystkie nieprzeczytane", + "unified_all_starred": "Wszystkie oznaczone gwiazdką", + "unified_all_mail": "Cała poczta", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Etykiety", + "create_label": "Utwórz etykietę", + "mv_tasks": "Zadania", + "mv_side_panel": "Panel boczny", + "mv_open_full": "Otwórz w pełnym widoku", + "mv_previous_day": "Poprzedni dzień", + "mv_next_day": "Następny dzień", + "mv_add_another_account": "Dodaj kolejne konto", + "mv_manage_account": "Zarządzaj kontem", + "mv_storage_used": "Wykorzystano {percent}% z {total}" + }, + "protocol_handlers": { + "title": "Aplikacje domyślne", + "description": "Wybierz, czy linki e-mail i kalendarza mają otwierać się w Bulwark. Technicznie Bulwark rejestruje się jako obsługa protokołu dla linków mailto: i webcal:.", + "unsupported": "Ta przeglądarka lub to połączenie nie obsługuje ręcznej rejestracji obsługi protokołu. Nadal możesz mieć możliwość użycia zainstalowanej aplikacji PWA w ustawieniach przeglądarki lub systemu.", + "mailto_label": "Linki e-mail", + "mailto_description": "Otwiera linki mailto: w Bulwark z wstępnie wypełnionym edytorem wiadomości.", + "protocol_open_mode_label": "Podczas otwierania linków protokołu", + "protocol_open_mode_description": "Wybierz, czy Bulwark ma otwierać linki mailto: i webcal: w nowej karcie, czy ponownie używać otwartej sesji. Opcja aktywnej sesji wymaga uprawnienia do powiadomień, aby można było kliknąć powiadomienie awaryjne i przenieść Bulwark na pierwszy plan, jeśli przeglądarka blokuje fokus.", + "protocol_open_mode_active_session": "Jeśli to możliwe, otwórz w aktywnej sesji", + "protocol_open_mode_new_tab": "Zawsze otwieraj nową kartę", + "focus_notification_title": "Otwórz Bulwark", + "focus_notification_body": "Link został otwarty w Bulwark. Kliknij, aby przenieść okno na pierwszy plan.", + "webcal_label": "Linki kalendarza", + "webcal_description": "Otwiera linki webcal: w Bulwark z wstępnie wypełnionym oknem subskrypcji kalendarza.", + "register_mailto": "Zarejestruj aplikację e-mail", + "register_webcal": "Zarejestruj aplikację kalendarza", + "mailto_registered": "Zażądano rejestracji obsługi e-mail", + "webcal_registered": "Zażądano rejestracji obsługi kalendarza", + "registration_failed": "Rejestracja obsługi protokołu nie powiodła się", + "opening_mailto": "Otwieranie edytora...", + "opening_webcal": "Otwieranie kalendarza...", + "browser_note": "Przeglądarka lub system operacyjny może poprosić o potwierdzenie i może wymagać zainstalowania Bulwark, zanim będzie można wybrać go jako aplikację domyślną.", + "select_account_title": "Wybierz konto", + "select_mailto_account": "Wybierz konto, które ma otworzyć ten link e-mail.", + "select_webcal_account": "Wybierz konto, które ma otworzyć ten link kalendarza.", + "select_account_note": "Ten wybór dotyczy tylko tego linku protokołu.", + "detail_to": "Do", + "detail_subject": "Temat", + "detail_no_subject": "Bez tematu", + "detail_calendar": "Kalendarz", + "detail_source": "Źródło", + "active_account": "Aktywne", + "switching_account": "Przełączanie konta..." + }, + "sidebar_apps": { + "modal_title": "Aplikacje paska bocznego", + "add_new": "Dodaj aplikację", + "edit_app": "Edytuj aplikację", + "name_label": "Nazwa", + "name_placeholder": "Moja aplikacja", + "name_required": "Nazwa jest wymagana", + "url_label": "URL", + "url_required": "URL jest wymagany", + "url_invalid": "Wprowadź prawidłowy adres http lub https", + "icon_label": "Ikona", + "icon_required": "Ikona jest wymagana", + "open_mode_label": "Tryb otwierania", + "open_new_tab": "Nowa karta", + "open_inline": "Wbudowany", + "cancel": "Anuluj", + "add": "Dodaj", + "update": "Zaktualizuj", + "delete": "Usuń", + "delete_confirm_title": "Usuń aplikację", + "delete_confirm": "Czy na pewno chcesz usunąć \"{name}\"?", + "no_apps": "Nie dodano jeszcze żadnych aplikacji", + "no_apps_hint": "Dodaj własne aplikacje i linki do paska bocznego", + "managed_title": "Udostępnione przez administratora", + "managed_description": "Te aplikacje są skonfigurowane dla wszystkich i nie można ich zmienić.", + "managed_badge": "Zarządzana", + "search_icons": "Szukaj ikon...", + "show_popular": "Popularne", + "show_all": "Wszystkie", + "no_icons_found": "Nie znaleziono ikon", + "inline_badge": "Wbudowany", + "tab_badge": "Karta", + "show_on_mobile": "Pokaż na telefonie" + }, + "email_list": { + "columns": { + "from": "Od", + "to": "Do", + "subject": "Temat", + "date": "Data", + "size": "Rozmiar" + }, + "no_emails": "Nie znaleziono wiadomości", + "no_emails_description": "Ta skrzynka jest pusta", + "no_search_results": "Nie znaleziono wyników", + "no_search_results_description": "Spróbuj zmienić wyszukiwanie lub filtry", + "loading": "Ładowanie wiadomości...", + "unread": "nieprzeczytane", + "to_me": "Do mnie", + "to_recipients": "Do {count} odbiorców", + "and_others": "i {count} innych", + "draft": "Szkic", + "starred": "Oznaczone gwiazdką", + "pinned": "Przypięte", + "replied": "Odpowiedziano", + "forwarded": "Przekazano", + "has_attachment": "Z załącznikiem", + "conversations_range": "{from}–{to, number} z {total, number}", + "conversations_count": "{count} z {total} konwersacji", + "conversations_count_plus": "{count}+ konwersacji", + "conversations_count_simple": "{count} konwersacji", + "no_conversations": "Brak konwersacji", + "loading_more": "Ładowanie kolejnych wiadomości...", + "no_more_emails": "Nie ma więcej wiadomości do załadowania", + "batch_actions": { + "select": "Zaznacz wiadomości", + "select_all": "Zaznacz wszystkie", + "selected_messages": "Wybrano {count, plural, one {1 wiadomość} other {# wiadomości}} e-mail", + "mark_read": "Oznacz jako przeczytane", + "mark_unread": "Oznacz jako nieprzeczytane", + "delete": "Usuń", + "delete_confirm_title": "Usuń wiadomości", + "delete_confirm_message": "Czy na pewno chcesz usunąć {count, plural, one {1 wiadomość} other {# wiadomości}}?", + "clear_selection": "Wyczyść zaznaczenie" + }, + "permanent_delete": "Usuń trwale", + "permanent_delete_confirm_title": "Trwałe usunięcie", + "permanent_delete_confirm_message": "Ta wiadomość zostanie trwale usunięta. Tej operacji nie można cofnąć.", + "permanent_delete_confirm_batch_message": "{count, plural, one {1 wiadomość zostanie trwale usunięta} other {# wiadomości zostanie trwale usuniętych}}. Tej operacji nie można cofnąć.", + "empty_folder": { + "button": "Opróżnij folder", + "confirm_title": "Opróżnij folder", + "confirm_message": "Wszystkie wiadomości w tym folderze zostaną trwale usunięte. Tej operacji nie można cofnąć.", + "confirm_message_trash": "Wszystkie wiadomości z tego folderu zostaną przeniesione do kosza.", + "confirm_button": "Opróżnij folder", + "junk_hint": "Możesz opróżnić folder Spam, aby trwale usunąć wszystkie wiadomości.", + "trash_hint": "Możesz opróżnić Kosz, aby trwale usunąć wszystkie wiadomości." + }, + "no_scheduled_emails": "Brak zaplanowanych e-maili", + "no_scheduled_emails_description": "Wiadomości zaplanowane na później pojawią się tutaj.", + "scheduled_count": "{count, plural, one {1 zaplanowany e-mail} few {# zaplanowane e-maile} other {# zaplanowanych e-maili}}", + "scheduled_actions_hint": "Zaplanowane wiadomości można anulować, przełożyć lub edytować z poziomu akcji planowania.", + "cancel_scheduled_send": "Anuluj wysyłkę", + "reschedule_send": "Przełóż", + "cancel_and_edit": "Anuluj i edytuj", + "cancel_and_compose_again": "Anuluj i napisz ponownie", + "reschedule_prompt": "Wprowadź nową datę/godzinę, np. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Nadawca prosi o potwierdzenie przeczytania:", + "send": "Wyślij potwierdzenie", + "ignore": "Ignoruj", + "sent": "Wysłano potwierdzenie przeczytania.", + "send_failed": "Nie udało się wysłać potwierdzenia przeczytania", + "mdn_subject": "Przeczytano: {subject}", + "mdn_body": "To jest potwierdzenie przeczytania wiadomości wysłanej do {recipient}.\n\nUwaga: to potwierdzenie oznacza jedynie, że wiadomość została wyświetlona na komputerze odbiorcy. Nie gwarantuje, że odbiorca przeczytał lub zrozumiał treść." + }, + "no_email_selected": "Nie wybrano wiadomości", + "no_email_description": "Wybierz wiadomość z listy, aby ją wyświetlić", + "no_conversation_selected": "Nie wybrano konwersacji", + "no_conversation_description": "Wybierz konwersację z listy, aby ją przeczytać", + "compose": "Napisz", + "compose_hint": "Napisz nową wiadomość", + "no_subject": "(Bez tematu)", + "no_body_content": "(Brak treści)", + "no_preview_available": "Podgląd niedostępny", + "show_quoted_text": "Pokaż cytowany tekst", + "hide_quoted_text": "Ukryj cytowany tekst", + "loading_email": "Ładowanie wiadomości...", + "loading": "Ładowanie...", + "reply": "Odpowiedz", + "reply_all": "Odpowiedz wszystkim", + "forward": "Przekaż dalej", + "delete": "Usuń", + "archive": "Archiwizuj", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Oznacz gwiazdką", + "unstar": "Usuń gwiazdkę", + "mark_unread": "Oznacz jako nieprzeczytane", + "mark_read": "Oznacz jako przeczytane", + "unread": "Nieprzeczytane", + "read": "Przeczytane", + "spam_short": "Spam", + "not_spam_short": "Nie spam", + "move": "Przenieś", + "print": "Drukuj", + "view_source": "Pokaż źródło", + "fullscreen": "Pełny ekran", + "exit_fullscreen": "Zamknij pełny ekran", + "view_in_dark_mode": "Wyświetl w ciemnym trybie", + "view_in_light_mode": "Wyświetl w jasnym trybie", + "export_email": "Eksportuj jako .eml", + "forward_as_attachment": "Prześlij dalej jako załącznik", + "import_email": "Importuj .eml lub .zip", + "keyboard_shortcuts": "Skróty klawiszowe (?)", + "email_source": "Źródło wiadomości", + "draft_banner": "Ta wiadomość jest szkicem", + "edit_draft": "Edytuj", + "copy_source": "Kopiuj do schowka", + "source_copied": "Źródło skopiowane do schowka", + "attachments": "Załączniki", + "important": "Ważne", + "download": "Pobierz", + "download_all": "Pobierz wszystkie", + "from": "Od", + "to": "Do", + "cc": "DW", + "bcc": "UDW", + "date": "Data", + "subject": "Temat", + "show_details": "Pokaż szczegóły", + "hide_details": "Ukryj szczegóły", + "external_content_warning": "Obrazy i treści zewnętrzne zostały zablokowane", + "load_external_content": "Załaduj obrazy", + "trust_sender": "Zawsze ufaj temu nadawcy", + "sender_check": { + "unverified_label": "Niezweryfikowany nadawca", + "failed_label": "Weryfikacja nadawcy nie powiodła się", + "unverified": "Ta wiadomość twierdzi, że pochodzi z domeny {domain}, ale nic tego nie potwierdza. Nie ma ważnego podpisu domeny {domain} i nie przyszła z serwera upoważnionego przez domenę {domain}.", + "unverified_sent_from": "Ta wiadomość twierdzi, że pochodzi z domeny {domain}, ale została wysłana z domeny {host} i nie ma ważnego podpisu domeny {domain}.", + "failed": "Ta wiadomość twierdzi, że pochodzi z domeny {domain}, ale nie przeszła weryfikacji nadawcy tej domeny. Może być sfałszowana.", + "failed_sent_from": "Ta wiadomość twierdzi, że pochodzi z domeny {domain}, ale została wysłana z domeny {host} i nie przeszła weryfikacji nadawcy domeny {domain}. Może być sfałszowana.", + "caution": "Uważaj na linki, załączniki i prośby o hasła lub dane płatnicze." + }, + "back_to_list": "Powrót do listy", + "view_contact": "Pokaż kontakt", + "message_details": "Szczegóły wiadomości", + "more_reply_options": "Więcej opcji odpowiedzi", + "set_tag": "Ustaw etykietę", + "tag": "Etykieta", + "more_actions": "Więcej działań", + "previous": "Poprz.", + "next": "Nast.", + "move_to": "Przenieś do...", + "remove_tag": "Usuń etykietę", + "tag_filter_placeholder": "Filtruj etykiety", + "tag_no_matches": "Brak pasujących etykiet", + "more_count": "+{count} więcej", + "characters_count": "{count} znaków", + "quick_reply_placeholder": "Napisz szybką odpowiedź...", + "more_options": "Więcej opcji", + "sending": "Wysyłanie...", + "security_authentication": "Bezpieczeństwo i uwierzytelnianie", + "technical_details": "Szczegóły techniczne", + "message_id_label": "Message-ID:", + "reply_to_label": "Reply-To:", + "delivery_time_label": "Czas dostarczenia:", + "conversation_part_label": "Część konwersacji:", + "previous_messages": "{count} poprzednia wiadomość", + "previous_messages_plural": "{count} poprzednich wiadomości", + "time": { + "day": "dzień", + "days": "dni", + "hour": "godzina", + "hours": "godzin", + "minute": "minuta", + "minutes": "minut" + }, + "unknown_sender": "Nieznany", + "recipient_me": "ja", + "recipient_and_others": "{name} i {count} innych", + "recipient_to_prefix": "Do:", + "authentication": { + "title": "Uwierzytelnianie", + "status": { + "verified": "Zweryfikowano", + "warning": "Ostrzeżenie", + "none": "Nieuwierzytelnione" + }, + "spf": { + "pass": "SPF zaliczony", + "fail": "SPF niezaliczony", + "none": "Brak SPF" + }, + "dkim": { + "pass": "DKIM prawidłowy", + "fail": "DKIM nieprawidłowy", + "none": "Brak DKIM" + }, + "dmarc": { + "pass": "DMARC zaliczony", + "fail": "DMARC niezaliczony", + "none": "Brak DMARC" + }, + "spam_score": "Ocena spamu", + "tooltip_spf": "Sender Policy Framework: sprawdza, czy serwer wysyłający jest uprawniony do wysyłania wiadomości w imieniu domeny", + "tooltip_dkim": "DomainKeys Identified Mail: potwierdza za pomocą podpisu kryptograficznego, że wiadomość nie została zmieniona w trakcie przesyłania", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zapewnia zgodność SPF i DKIM z domeną nadawcy i określa politykę dla niepowodzeń", + "policy": "Polityka", + "result": { + "pass": "Zaliczony", + "fail": "Niezaliczony", + "softfail": "Łagodny błąd", + "neutral": "Neutralny", + "permerror": "Błąd trwały", + "temperror": "Błąd tymczasowy", + "none": "Brak" + } + }, + "headers": { + "routing": "Routing", + "received": "Otrzymano", + "message_id": "ID wiadomości", + "list_info": "Informacje o liście" + }, + "tooltips": { + "reply": "Odpowiedz (r)", + "reply_all": "Odpowiedz wszystkim (a)", + "forward": "Przekaż dalej (f)", + "archive": "Archiwizuj (e)", + "delete": "Usuń (# lub Del)", + "star": "Oznacz gwiazdką (s)", + "unstar": "Usuń gwiazdkę (s)", + "compose": "Napisz (c)", + "previous": "Poprzednia wiadomość", + "next": "Następna wiadomość", + "edit_draft": "Edytuj szkic" + }, + "spam": { + "button_title": "Zgłoś spam", + "not_spam_title": "Oznacz jako prawidłową", + "toast_success": "Przeniesiono do Spamu", + "toast_batch": "{count} wiadomości przeniesiono do Spamu", + "toast_undo": "Cofnij", + "toast_not_spam_success": "Przeniesiono do Odebranych", + "toast_not_spam_batch": "{count} wiadomości przeniesiono do Odebranych", + "error": "Nie udało się zgłosić spamu", + "error_not_spam": "Nie udało się przywrócić wiadomości" + }, + "unsubscribe_banner": { + "label": "Newsletter", + "button": "Wypisz się", + "confirm_title": "Wypisać się od tego nadawcy?", + "confirm_button": "Potwierdź", + "cancel": "Anuluj", + "success_http": "Strona wypisania otwarta w nowej karcie", + "success_mailto": "Wysłano e-mail rezygnacji z subskrypcji", + "confirm_message_http": "Strona rezygnacji z subskrypcji otworzy się w nowej karcie.", + "confirm_message_mailto": "Do nadawcy zostanie wysłany e-mail rezygnacji z subskrypcji.", + "error": "Nie można się wypisać", + "dismiss": "Zamknij" + }, + "calendar_invitation": { + "loading": "Ładowanie szczegółów wydarzenia…", + "title": "Zaproszenie kalendarza", + "published_title": "Opublikowane wydarzenie", + "response_title": "Odpowiedź na wydarzenie", + "update_title": "Aktualizacja wydarzenia", + "counter_title": "Propozycja zmian", + "refresh_title": "Żądanie odświeżenia", + "declined_counter_title": "Propozycja zmian odrzucona", + "cancelled_title": "Wydarzenie anulowane", + "organizer": "Organizator: {name}", + "organizer_label": "Organizator:", + "attendees": "{count, plural, one {# uczestnik} other {# uczestników}}", + "accept": "Akceptuj", + "maybe": "Może", + "decline": "Odrzuć", + "add_to_calendar": "Dodaj do kalendarza", + "added": "Dodano do kalendarza", + "rsvp_sent": "Odpowiedź wysłana", + "parse_error": "Nie można odczytać zaproszenia", + "action_failed": "Nie udało się wykonać akcji kalendarza.", + "no_calendar": "Kalendarz niedostępny", + "published_info": "To wydarzenie zostało udostępnione w celach informacyjnych.", + "response_info": "Ta wiadomość zawiera odpowiedź uczestnika.", + "response_info_organizer": "Ta odpowiedź uczestnika aktualizuje Twoje wydarzenie.", + "update_info": "Ta wiadomość aktualizuje istniejące wydarzenie.", + "counter_info": "Ta wiadomość proponuje zmiany w wydarzeniu.", + "counter_info_organizer": "Ten uczestnik zaproponował zmiany w Twoim wydarzeniu.", + "refresh_info": "Ta wiadomość żąda najnowszych szczegółów wydarzenia.", + "refresh_info_organizer": "Uczestnik poprosił o najnowsze szczegóły wydarzenia.", + "declined_counter_info": "Organizator odrzucił propozycję zmian.", + "authentication_failed_info": "Uwierzytelnianie poczty dla tego zaproszenia nie powiodło się. Traktuj akcje kalendarza z ostrożnością.", + "authentication_missing_info": "To zaproszenie nie zawiera zweryfikowanego uwierzytelniania poczty. Potwierdź szczegóły z organizatorem, jeśli cokolwiek wygląda nietypowo.", + "sender_mismatch_info": "To zaproszenie zostało wysłane z {sender}, podczas gdy organizator w danych kalendarza to {organizer}.", + "sender_mismatch_unverified_info": "To zaproszenie zostało wysłane z {sender}, podczas gdy organizator w danych kalendarza to {organizer}, a wiadomość nie mogła zostać zweryfikowana.", + "organizer_role": "Jesteś organizatorem tego wydarzenia", + "your_response": "Twoja odpowiedź: {status}", + "response_needed": "Wymaga odpowiedzi", + "response_accepted": "Zaakceptowano", + "response_tentative": "Wstępnie", + "response_declined": "Odrzucono", + "response_delegated": "Delegowano", + "actor_sent_info": "Wysłano przez {name}.", + "actor_response_info": "{name} odpowiedział(a) {status}.", + "actor_counter_info": "{name} zaproponował(a) zmiany w tym wydarzeniu.", + "actor_refresh_info": "{name} poprosił(a) o najnowsze szczegóły wydarzenia.", + "actor_declined_counter_info": "{name} odrzucił(a) propozycję zmian.", + "actor_note": "Notatka: {comment}", + "actor_unknown": "Ktoś", + "proposed_changes": "Proponowane zmiany", + "change_title": "Tytuł", + "change_time": "Czas", + "change_location": "Lokalizacja", + "change_description": "Opis", + "change_empty": "Brak", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Zastosuj proponowane zmiany", + "proposal_applied": "Proponowane zmiany zastosowane.", + "review_proposal": "Przejrzyj propozycję", + "review_request": "Przejrzyj żądanie", + "view_in_calendar": "Pokaż w kalendarzu", + "select_calendar": "Wybierz kalendarz", + "already_in_calendar": "Już w Twoim kalendarzu", + "request_info": "Zostałeś zaproszony na to wydarzenie. Odpowiedz, aby poinformować organizatora o swojej dostępności.", + "cancel_info": "Organizator anulował to wydarzenie.", + "event_updated": "Aktualizacja #{sequence}", + "event_status_tentative": "Wstępne", + "event_status_cancelled": "Anulowane", + "collapse": "Ukryj szczegóły", + "expand": "Pokaż szczegóły" + }, + "send": "Wyślij", + "more": "więcej", + "details": { + "recipients_routing": "Odbiorcy i trasowanie", + "authentication_security": "Uwierzytelnianie i bezpieczeństwo", + "identifiers_threading": "Identyfikatory i wątkowanie", + "mailing_list": "Lista mailingowa", + "message_properties": "Właściwości wiadomości", + "sent": "Wysłano", + "received": "Odebrano", + "delivery_time": "Czas dostarczenia", + "in_reply_to": "W odpowiedzi na", + "references": "Odniesienia", + "thread_id": "ID wątku", + "size": "Rozmiar", + "mime_type": "Typ MIME", + "attachments_summary": "{count} plików · {size}", + "list_id": "ID listy", + "list_help": "Pomoc listy", + "list_post": "Wysyłanie do listy", + "list_unsubscribe": "Wypisz się", + "iprev": "Odwrotny DNS", + "spam_status": "Status spamu", + "ai_verdict": "Werdykt AI", + "account": "Konto", + "no_subject": "(brak tematu)" + }, + "scheduled_banner": "Zaplanowano wysyłkę na {date}", + "scheduled_send_created": "E-mail zaplanowany do wysłania", + "sending_in": "Wysyłka za {seconds} s", + "undo_send_failed": "Nie udało się cofnąć. Wiadomość mogła już zostać wysłana.", + "cancel_scheduled_send": "Anuluj wysyłkę", + "reschedule_send": "Przełóż", + "cancel_and_edit": "Anuluj i edytuj", + "cancel_and_compose_again": "Anuluj i napisz ponownie", + "reschedule_prompt": "Wprowadź nową datę/godzinę, np. 2026-05-04T15:30", + "scheduled_actions_only": "Widok Zaplanowane obsługuje tylko akcje planowania", + "undo_send_scheduled": "Wiadomość zaplanowana do wysłania", + "undo_send": "Cofnij wysyłkę", + "contact_sidebar": { + "title": "Kontakt", + "close": "Zamknij panel boczny", + "action_email": "E-mail", + "action_email_title": "Wyślij e-mail", + "action_copy": "Kopiuj", + "action_copy_title": "Kopiuj adres e-mail", + "action_edit_title": "Edytuj kontakt", + "section_emails": "E-maile", + "section_phones": "Telefony", + "section_organizations": "Organizacje", + "section_addresses": "Adresy", + "section_notes": "Notatki", + "not_in_contacts": "Brak w kontaktach", + "add_to_contacts": "Dodaj do kontaktów", + "copied": "Skopiowano!", + "copy_failed": "Nie udało się skopiować", + "view_contact": "Pokaż kontakt", + "view_details": "Pokaż szczegóły" + }, + "send_now": "Wyślij teraz", + "verification_code": { + "copy": "Kopiuj kod {code}", + "copied": "Skopiowano kod", + "copy_failed": "Nie udało się skopiować kodu" + }, + "mv_position": "{index} z {total}", + "plugin_panel_show": "Pokaż panel wtyczki", + "plugin_panel_hide": "Ukryj panel wtyczki" + }, + "email_composer": { + "select_from_directory": "Wybierz z katalogu", + "minimize": "Minimalizuj", + "maximize": "Pełny ekran", + "restore": "Przywróć", + "new_message": "Nowa wiadomość", + "signature_edit_hint": "Kliknij dwukrotnie, aby edytować podpis", + "reply": "Odpowiedz", + "reply_all": "Odpowiedz wszystkim", + "forward": "Przekaż dalej", + "reply_to": "Odpowiedz", + "reply_all_to": "Odpowiedz wszystkim", + "forward_message": "Przekaż dalej", + "from": "Od", + "to": "Do", + "cc": "DW", + "bcc": "UDW", + "subject": "Temat", + "body_placeholder": "Napisz wiadomość...", + "send": "Wyślij", + "cancel": "Anuluj", + "attach": "Załącz", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Dołącz z Plików", + "files_picker": { + "attach": "{count, plural, =0 {Dołącz} one {Dołącz 1 plik} few {Dołącz # pliki} many {Dołącz # plików} other {Dołącz # pliku}}", + "empty_folder": "Ten folder jest pusty", + "load_failed": "Nie udało się wczytać plików" + }, + "discard": "Odrzuć", + "discard_draft_title": "Odrzucić szkic?", + "discard_draft_confirm": "Masz niezapisane zmiany. Czy chcesz odrzucić ten szkic?", + "discard_draft_message": "Wersja robocza zostanie usunięta.", + "saving": "Zapisywanie...", + "draft_saved": "Szkic zapisany", + "save_failed": "Nie udało się zapisać", + "to_placeholder": "Adresy e-mail odbiorców", + "cc_placeholder": "Odbiorcy DW", + "bcc_placeholder": "Odbiorcy UDW", + "subject_placeholder": "Temat", + "cc_label": "DW:", + "bcc_label": "UDW:", + "subject_label": "Temat:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} few {# e-maile} many {# e-maili} other {# e-maili}}", + "no_subject": "(Bez tematu)", + "unknown_sender": "Nieznany", + "quote": { + "reply_header": "Dnia {date}, {sender} napisał(a):", + "forward_header": "---------- Przekazana wiadomość ----------", + "from": "Od: {sender}", + "date": "Data: {date}", + "subject": "Temat: {subject}", + "to": "Do: {recipients}" + }, + "remove_sub_address": "Usuń podadres", + "from_override": { + "toggle_off": "Zastąp", + "toggle_on": "Anuluj zastąpienie", + "toggle_tooltip": "Swobodnie edytuj nazwę i adres nadawcy. Poczta jest nadal wysyłana przez twoją tożsamość.", + "name_label": "Nazwa nadawcy", + "name_placeholder": "Nazwa", + "email_label": "Adres e-mail nadawcy", + "email_placeholder": "alias@example.com", + "envelope_notice": "Jeśli twój serwer nie akceptuje tego adresu jako nadawcy koperty, zostanie użyty {identity}, a odbiorcy zobaczą go w nagłówku Return-Path." + }, + "save_as_template": "Zapisz jako szablon", + "attach_from_device": "Prześlij z tego urządzenia", + "templates_menu": "Szablony", + "insert_template": "Wstaw szablon", + "options": "Opcje", + "option_plain_text": "Zwykły tekst", + "option_plain_text_hint": "Wyślij bez formatowania", + "option_read_receipt": "Poproś o potwierdzenie przeczytania", + "option_read_receipt_hint": "Poproś odbiorcę o potwierdzenie otwarcia", + "option_dsn": "Powiadomienie o doręczeniu", + "option_dsn_hint": "Otrzymaj raport, gdy serwer ją doręczy", + "option_require_tls": "Wymagaj szyfrowanego doręczenia", + "option_require_tls_hint": "Doręcz tylko przez TLS, inaczej odrzuć", + "option_priority": "Priorytet", + "option_priority_high": "Wysoki", + "option_priority_normal": "Normalny", + "option_priority_low": "Niski", + "validation": { + "recipient_required": "Dodaj odbiorcę, aby wysłać", + "body_required": "Napisz wiadomość lub załącz plik", + "invalid_recipients": "Nie wysłano - sprawdź te adresy: {addresses}", + "attachments_uploading": "Załączniki wciąż się przesyłają — wiadomość zostanie wysłana po zakończeniu", + "attachment_upload_failed": "Nie wysłano - nie udało się przesłać załącznika. Usuń go i spróbuj ponownie." + }, + "upload_progress": "Przesyłanie {uploaded} / {total}", + "upload_cancel": "Anuluj przesyłanie", + "upload_failed": "Nie udało się przesłać {filename}", + "attachment_too_large": "„{name}” jest większy, niż pozwala serwer ({max} na plik)", + "attachments_total_too_large": "Załączniki przekroczyłyby limit serwera {max} na wiadomość", + "drop_files": "Upuść pliki, aby załączyć", + "show_less": "Pokaż mniej", + "send_failed": "Nie udało się wysłać wiadomości", + "send_timeout": "Serwer przestał odpowiadać. Przed ponownym wysłaniem sprawdź folder Wysłane — wiadomość mogła już zostać wysłana.", + "send_offline": "Wygląda na to, że jesteś offline. Wiadomość nadal tu jest, wyślij ją ponownie po odzyskaniu połączenia.", + "formatting": "Formatowanie", + "send_recipients_rejected": "Nie wysłano – serwer odrzucił wszystkich odbiorców.", + "send_plugin_failed": "Nie wysłano – wtyczka obsługująca wysyłanie zawiodła lub działała zbyt długo. Przed ponownym wysłaniem sprawdź folder Wysłane.", + "send_some_recipients_rejected": "Wysłano, ale nie do tych odbiorców – serwer ich odrzucił.", + "continue_draft": "Kontynuuj szkic", + "close_draft_title": "Zapisać czy odrzucić szkic?", + "close_draft_message": "Masz niezapisane zmiany. Czy chcesz zapisać jako szkic czy odrzucić?", + "smime_sign_on": "Podpisywanie S/MIME włączone", + "smime_sign_off": "Włącz podpisywanie S/MIME", + "smime_encrypt_on": "Szyfrowanie S/MIME włączone", + "smime_encrypt_off": "Włącz szyfrowanie S/MIME", + "smime_encrypt_unavailable": "Szyfrowanie S/MIME niedostępne – brak certyfikatów odbiorców", + "smime_unlock_title": "Odblokuj klucz S/MIME", + "smime_unlock_message": "Wprowadź hasło, aby odblokować klucz podpisywania S/MIME.", + "smime_unlock_button": "Odblokuj", + "smime_passphrase_placeholder": "Hasło", + "forgot_attachment": { + "title": "Czy zapomniałeś o załączniku?", + "message": "Twoja wiadomość wspomina o \"{keyword}\", ale nie załączono żadnego pliku. Wysłać mimo to?", + "send_anyway": "Wyślij mimo to", + "back": "Wróć do edycji" + }, + "empty_subject": { + "title": "Wysłać bez tematu?", + "message": "Ta wiadomość nie ma tematu. Wysłać mimo to?", + "dont_ask_again": "Nie pytaj ponownie", + "send_anyway": "Wyślij mimo to", + "back": "Wróć do edycji" + }, + "add_link": "Dodaj link", + "link_url_prompt": "Wprowadź adres URL", + "sending": "Wysyłanie...", + "schedule_send": "Zaplanuj wysyłkę", + "schedule_send_description": "Wybierz, kiedy serwer ma wysłać tę wiadomość.", + "schedule_preset": { + "tomorrow_morning": "Jutro rano", + "tomorrow_afternoon": "Jutro po południu", + "monday_morning": "W poniedziałek rano" + }, + "schedule_send_required": "Wybierz datę i godzinę.", + "schedule_send_invalid": "Wprowadź prawidłową datę i godzinę.", + "schedule_send_future": "Wybierz datę i godzinę w przyszłości.", + "schedule_send_too_late": "Ten czas jest późniejszy, niż pozwala serwer.", + "schedule_send_unsupported": "Zaplanowana wysyłka nie jest obsługiwana dla tego konta.", + "schedule_send_cleanup_warning": "Zaplanowana wysyłka została utworzona, ale czyszczenie wersji roboczej nie powiodło się.", + "send_delay_unsupported": "Opóźnienie wysyłki nie jest obsługiwane dla tego konta.", + "send_delay_unsupported_confirm": "To konto nie obsługuje opóźnienia wysyłki. Wysłać natychmiast?", + "recipient_edit_email": "Edytuj adres e-mail", + "recipient_edit_name": "Edytuj wyświetlaną nazwę", + "recipient_email_placeholder": "Adres e-mail", + "recipient_name_placeholder": "Wyświetlana nazwa", + "invalid_recipient": "Nieprawidłowy adres e-mail", + "autocomplete_search_server": "Szukaj na serwerze", + "autocomplete_searching": "Wyszukiwanie...", + "mention_recipients": "Odbiorcy", + "mention_card_not_recipient": "Brak w polach Do i DW, więc ta osoba nie dostanie wiadomości.", + "mention_card_add": "Dodaj do {field}", + "mention_card_remove": "Usuń wzmiankę", + "toolbar": { + "bold": "Pogrubienie", + "italic": "Kursywa", + "underline": "Podkreślenie", + "strikethrough": "Przekreślenie", + "text_color": "Kolor tekstu", + "remove_color": "Usuń kolor", + "heading_1": "Nagłówek 1", + "heading_2": "Nagłówek 2", + "bullet_list": "Lista punktowana", + "ordered_list": "Lista numerowana", + "quote": "Cytat", + "code_block": "Blok kodu", + "align_left": "Wyrównaj do lewej", + "align_center": "Wyśrodkuj", + "align_right": "Wyrównaj do prawej", + "text_direction": "Kierunek tekstu (RTL/LTR)", + "link": "Link", + "table": "Tabela", + "clear_formatting": "Wyczyść formatowanie", + "undo": "Cofnij", + "redo": "Ponów", + "add_row_above": "Dodaj wiersz powyżej", + "add_row_below": "Dodaj wiersz poniżej", + "add_column_before": "Dodaj kolumnę przed", + "add_column_after": "Dodaj kolumnę po", + "delete_row": "Usuń wiersz", + "delete_column": "Usuń kolumnę", + "toggle_header_row": "Przełącz wiersz nagłówka", + "delete_table": "Usuń tabelę", + "pick_size": "Wybierz rozmiar", + "font_size": "Rozmiar czcionki", + "font_size_default": "Domyślny", + "background_color": "Kolor tła", + "remove_background_color": "Usuń kolor tła" + }, + "send_filing_warning": "Wysłano - ale późniejsze porządkowanie nie powiodło się, może pozostać nieaktualna wersja robocza.", + "pop_out": "Otwórz odpowiedź w oknie", + "send_guard_failed": "Nie wysłano - wtyczka sprawdzająca wiadomości przed wysłaniem zawiodła lub nie odpowiedziała." + }, + "confirm_dialog": { + "confirm": "Potwierdź", + "cancel": "Anuluj" + }, + "common": { + "loading": "Ładowanie...", + "error": "Błąd", + "success": "Sukces", + "cancel": "Anuluj", + "save": "Zapisz", + "delete": "Usuń", + "edit": "Edytuj", + "close": "Zamknij", + "search": "Szukaj", + "refresh": "Odśwież", + "settings": "Ustawienia", + "help": "Pomoc", + "logout": "Wyloguj", + "yes": "Tak", + "no": "Nie", + "unknown": "Nieznany", + "app_title": "Webmail", + "reconnecting": "Utracono połączenie. Próba ponownego połączenia…", + "rate_limited_title": "Uwierzytelnianie serwera jest tymczasowo ograniczone.", + "rate_limited_detail": "Bulwark wstrzymał żądania w tle, aby uniknąć blokady. Ponowna próba za {seconds}s.", + "rate_limited_action_title": "Żądanie wstrzymane, aby uniknąć blokady.", + "rate_limited_action_detail": "Bulwark czeka na zakończenie okresu blokady serwera przed wysłaniem kolejnych uwierzytelnionych żądań. Spróbuj ponownie za {seconds}s.", + "pull_to_refresh": "Pociągnij, aby odświeżyć", + "release_to_refresh": "Puść, aby odświeżyć", + "refreshing": "Odświeżanie…" + }, + "notifications": { + "email_sent": "Wiadomość wysłana pomyślnie", + "email_deleted": "Wiadomość usunięta", + "email_archived": "Wiadomość zarchiwizowana", + "emails_deleted": "{count, plural, one {# wiadomość usunięta} few {# wiadomości usunięte} many {# wiadomości usuniętych} other {# wiadomości usuniętych}}", + "emails_archived": "{count, plural, one {# wiadomość zarchiwizowana} few {# wiadomości zarchiwizowane} many {# wiadomości zarchiwizowanych} other {# wiadomości zarchiwizowanych}}", + "error_archiving": "Nie udało się zarchiwizować wiadomości", + "error_updating": "Nie udało się zaktualizować wiadomości", + "email_starred": "Wiadomość oznaczona gwiazdką", + "email_unstarred": "Gwiazdka usunięta", + "email_marked_read": "Wiadomość oznaczona jako przeczytana", + "email_marked_unread": "Wiadomość oznaczona jako nieprzeczytana", + "copied_to_clipboard": "Skopiowano do schowka", + "source_copied": "Źródło skopiowane do schowka", + "error_sending": "Nie udało się wysłać wiadomości", + "error_deleting": "Nie udało się usunąć wiadomości", + "error_loading": "Nie udało się załadować wiadomości", + "new_email": "Nowa wiadomość", + "new_email_from": "Od {sender}", + "click_to_view": "Kliknij, aby wyświetlić", + "email_moved": "Wiadomość przeniesiona", + "emails_moved": "{count} wiadomości przeniesionych", + "moved_to_mailbox": "Przeniesiono do {mailbox}", + "move_failed": "Przenoszenie nie powiodło się", + "removed_from_folder": "Usunięto z folderu {mailbox}", + "remove_from_folder_failed": "Nie udało się usunąć z tego folderu", + "emails_copied": "{count} wiadomości skopiowanych", + "copy_failed": "Kopiowanie nie powiodło się", + "move_error": "Nie można przenieść wiadomości do wybranego folderu", + "email_tagged": "Wiadomość oznaczona etykietą", + "emails_tagged": "{count} wiadomości oznaczonych etykietą", + "tag_failed": "Oznaczanie etykietą nie powiodło się", + "identity_created": "Tożsamość utworzona pomyślnie", + "identity_updated": "Tożsamość zaktualizowana pomyślnie", + "identity_deleted": "Tożsamość usunięta", + "identity_set_primary": "Główna tożsamość zaktualizowana", + "identity_create_failed": "Nie udało się utworzyć tożsamości: {error}", + "identity_update_failed": "Nie udało się zaktualizować tożsamości: {error}", + "identity_delete_failed": "Nie udało się usunąć tożsamości: {error}", + "identity_unauthorized": "Nie masz uprawnień do wysyłania z tego adresu e-mail", + "identity_not_found": "Nie znaleziono tożsamości", + "vacation_saved": "Ustawienia nieobecności zapisane", + "vacation_save_failed": "Nie udało się zapisać ustawień nieobecności", + "filters_saved": "Filtry zapisane pomyślnie", + "filters_save_failed": "Nie udało się zapisać filtrów", + "filters_deleted": "Reguła filtra usunięta", + "templates_exported": "Szablony wyeksportowane pomyślnie", + "templates_imported": "{count, plural, one {# szablon zaimportowany} other {# szablonów zaimportowanych}}", + "templates_import_errors": "Niektóre szablony nie mogły zostać zaimportowane", + "templates_import_empty": "Nie znaleziono szablonów w pliku", + "export_email_error": "Nie udało się wyeksportować wiadomości", + "import_email_success": "Wiadomość zaimportowana pomyślnie", + "import_email_error": "Nie udało się zaimportować wiadomości", + "rule_created": "Utworzono regułę", + "rule_merged": "Dodano do reguły „{name}”", + "rule_already_covered": "Już obejmuje to reguła „{name}”", + "rule_apply_existing": "{count, plural, one {Zastosuj do # istniejącej wiadomości} few {Zastosuj do # istniejących wiadomości} many {Zastosuj do # istniejących wiadomości} other {Zastosuj do # istniejącej wiadomości}}", + "rule_applied": "{count, plural, =0 {Żadna wiadomość nie wymagała zmian} one {Zastosowano regułę do # wiadomości} few {Zastosowano regułę do # wiadomości} many {Zastosowano regułę do # wiadomości} other {Zastosowano regułę do # wiadomości}}", + "rule_apply_failed": "Nie udało się zastosować reguły do istniejących wiadomości", + "rule_edit": "Edytuj regułę", + "rule_undo": "Cofnij", + "rule_undone": "Cofnięto zmianę reguły", + "rule_undo_failed": "Nie udało się cofnąć zmiany reguły", + "rule_undo_conflict": "Twoje filtry zmieniły się w międzyczasie. Cofnij zmianę w ustawieniach filtrów.", + "rule_folder_failed": "Nie udało się utworzyć folderu", + "vacation_filters_save_failed": "Ustawienia nieobecności zapisane, ale nie udało się zapisać przekazywania ani odbiorców odpowiedzi" + }, + "date": { + "today": "Dzisiaj", + "yesterday": "Wczoraj", + "this_week": "Ten tydzień", + "last_week": "Poprzedni tydzień", + "this_month": "Ten miesiąc", + "older": "Starsze", + "just_now": "Przed chwilą", + "minutes_ago": "{count} minutę temu", + "minutes_ago_plural": "{count} minut temu", + "hours_ago": "{count} godzinę temu", + "hours_ago_plural": "{count} godzin temu", + "days_ago": "{count} dzień temu", + "days_ago_plural": "{count} dni temu" + }, + "language": { + "title": "Język", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "polish": "Polski", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Wybierz język", + "switch_to_english": "Przełącz na angielski", + "switch_to_french": "Przełącz na francuski", + "switch_to_japanese": "Przełącz na japoński", + "switch_to_spanish": "Przełącz na hiszpański", + "switch_to_italian": "Przełącz na włoski", + "switch_to_german": "Przełącz na niemiecki", + "switch_to_dutch": "Przełącz na niderlandzki", + "switch_to_polish": "Przełącz na polski", + "switch_to_portuguese": "Przełącz na portugalski", + "switch_to_russian": "Przełącz na rosyjski", + "switching": "Zmiana języka...", + "switch": "Zmień język", + "current": "Bieżący język", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Przełącz na perski", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Przełącz na mongolski" + }, + "settings": { + "title": "Ustawienia", + "back_to_mail": "Powrót do poczty", + "save_success": "Ustawienia zostały zapisane", + "import_success": "Ustawienia zostały zaimportowane", + "import_error": "Nie udało się zaimportować ustawień", + "reset_confirm": "Czy na pewno chcesz przywrócić domyślne ustawienia?", + "unsaved_changes": "Masz niezapisane zmiany", + "discard_changes": "Odrzucić niezapisane zmiany?", + "discard": "Odrzuć", + "keep_editing": "Kontynuuj edycję", + "tabs": { + "appearance": "Wygląd", + "language": "Język, region i czas", + "email": "Zachowanie poczty e-mail", + "composer": "Redagowanie", + "privacy": "Prywatność i bezpieczeństwo", + "account": "Konto", + "identities": "Tożsamości", + "vacation": "Nieobecność", + "advanced": "Zaawansowane", + "calendar": "Kalendarz", + "filters": "Filtry", + "templates": "Szablony", + "folders": "Foldery", + "keywords": "Etykiety", + "security": "Bezpieczeństwo", + "files": "Pliki", + "contacts": "Kontakty", + "encryption": "Szyfrowanie", + "protocol_handlers": "Aplikacje domyślne", + "sidebar_apps": "Aplikacje paska bocznego", + "notifications": "Powiadomienia", + "layout": "Układ", + "themes": "Motywy", + "reading": "Czytanie", + "composing": "Tworzenie", + "downloads": "Pobrane", + "content_senders": "Treść i nadawcy", + "about_data": "O programie i dane", + "debug": "Debugowanie" + }, + "themes": { + "title": "Motywy", + "description": "Wybierz jeden z motywów udostępnionych przez administratora lub jeden z wbudowanych.", + "locked": "Wybór motywu został zablokowany przez administratora.", + "forced": "Motyw „{name}” jest wymuszony przez administratora i nie można go zmienić", + "admin_theme": "Motyw administratora", + "activated": "Motyw aktywowany", + "default_restored": "Przywrócono domyślny motyw", + "default_name": "Domyślny", + "default_badge": "Domyślny", + "admin_enforced": "Wymuszony przez administratora" + }, + "tab_groups": { + "general": "Ogólne", + "account": "Konto i tożsamość", + "organization": "Organizacja poczty", + "apps": "Aplikacje", + "system": "System", + "appearance": "Wygląd", + "mail": "Poczta", + "privacy": "Prywatność i bezpieczeństwo", + "advanced": "Zaawansowane" + }, + "appearance": { + "message_list_order": { + "label": "Kolejność listy wiadomości", + "description": "Jak wiadomości są uporządkowane na liście. Sortowanie wykonuje serwer, więc obejmuje cały folder, a nie tylko załadowane wiadomości.", + "preset": { + "chronological": "Chronologicznie (najnowsze najpierw)", + "unread_first": "Nieprzeczytane najpierw", + "starred_first": "Oznaczone gwiazdką najpierw", + "tagged_first": "Z etykietą najpierw", + "custom": "Własna" + }, + "tag_label": "Etykieta", + "advanced_show": "Zaawansowane: edytuj poziomy sortowania", + "advanced_hide": "Ukryj zaawansowane", + "criterion_label": "Kryterium poziomu {n}", + "direction_label": "Kierunek poziomu {n}", + "criteria": { + "unread": "Nieprzeczytane", + "starred": "Z gwiazdką", + "tag": "Etykieta", + "receivedAt": "Data odebrania", + "sentAt": "Data wysłania", + "from": "Nadawca", + "subject": "Temat", + "size": "Rozmiar" + }, + "direction": { + "unread_first": "Nieprzeczytane najpierw", + "read_first": "Przeczytane najpierw", + "starred_first": "Z gwiazdką najpierw", + "unstarred_first": "Bez gwiazdki najpierw", + "tagged_first": "Z etykietą najpierw", + "untagged_first": "Bez etykiety najpierw", + "newest_first": "Najnowsze najpierw", + "oldest_first": "Najstarsze najpierw", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Największe najpierw", + "smallest_first": "Najmniejsze najpierw" + }, + "add_level": "Dodaj poziom", + "remove_level": "Usuń poziom", + "move_up": "Przenieś wyżej", + "move_down": "Przenieś niżej", + "tiebreak_note": "Do trzech poziomów; kolejne poziomy rozstrzygają remisy. „Najnowsze najpierw” jest zawsze stosowane na końcu.", + "scope": { + "label": "Zastosuj do wszystkich folderów", + "description": "Gdy wyłączone, kolejność dotyczy tylko Odebranych; pozostałe foldery pozostają chronologiczne." + }, + "unsupported_note": "Twój serwer poczty nie obsługuje sortowania według stanu przeczytania, gwiazdek ani etykiet. Te poziomy są pomijane, a lista pozostaje chronologiczna." + }, + "title": "Wygląd", + "description": "Dostosuj wygląd i działanie swojej poczty webowej", + "theme": { + "label": "Motyw", + "description": "Wybierz preferowany schemat kolorów", + "light": "Jasny", + "dark": "Ciemny", + "system": "Systemowy" + }, + "language": { + "label": "Język", + "description": "Wybierz preferowany język" + }, + "font_size": { + "label": "Rozmiar czcionki", + "description": "Dostosuj rozmiar tekstu dla lepszej czytelności", + "small": "Mały", + "medium": "Średni", + "large": "Duży" + }, + "list_density": { + "label": "Gęstość", + "description": "Kontroluj odstępy i wypełnienie w całym interfejsie", + "extra_compact": "Bardzo kompaktowy", + "compact": "Kompaktowy", + "regular": "Zwykły", + "comfortable": "Wygodny" + }, + "interface_layout": { + "label": "Układ interfejsu", + "description": "Gdzie znajdują się wyszukiwarka, nawigacja i paski narzędzi", + "default": "Domyślny", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Włącz animacje", + "description": "Pokazuj płynne przejścia i efekty" + }, + "toolbar_position": { + "label": "Pozycja paska narzędzi", + "description": "Gdzie wyświetlać przyciski akcji wiadomości (Odpowiedz, Archiwizuj, Usuń itp.)", + "top": "U góry", + "below_subject": "Pod tematem" + }, + "toolbar_labels": { + "label": "Pokazuj etykiety paska narzędzi", + "description": "Wyświetlaj etykiety tekstowe obok ikon paska narzędzi. Wyłącz tę opcję, aby oszczędzić miejsce, gdy znasz już ikony." + }, + "hide_account_switcher": { + "label": "Ukryj przełącznik kont na pasku bocznym", + "description": "Ukryj wybór konta u góry paska folderów. Nadal możesz przełączać konta z dolnego paska nawigacyjnego." + }, + "show_rail_account_list": { + "label": "Pokazuj awatary kont na pasku nawigacyjnym", + "description": "Wyświetlaj osobne ikony kont na dole paska nawigacyjnego, aby szybko się przełączać, z przyciskiem wylogowania poniżej." + }, + "unified_mailbox": { + "label": "Wspólna skrzynka", + "description": "Wyświetlaj połączone foldery (Odebrane, Wysłane itp.) ze wszystkich połączonych kont", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Uwzględnij skrzynki grupowe", + "description": "Dodaj również udostępnione/grupowe skrzynki do widoku wspólnego." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Kolorowe ikony paska bocznego", + "description": "Koloruj ikony folderów i tagów według typu (niebieska Skrzynka odbiorcza, czerwona Spam, zielona Wysłane itp.). Wyłącz, aby uzyskać monochromatyczny pasek boczny." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Pokaż łączną liczbę wiadomości", + "description": "Pokazuje łączną liczbę wiadomości obok folderów i etykiet, obok liczby nieprzeczytanych. Wyłącz, aby pokazywać tylko nieprzeczytane." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Pokaż udostępnione konta pocztowe", + "description": "Pokazuj foldery skrzynek udostępnionych Ci w sekcji Udostępnione na pasku bocznym. Wyłącz, aby ukryć wszystkie, lub ukryj poszczególne konta poniżej. Ich kalendarze i kontakty pozostaną." + }, + "pro_interface": { + "label": "Interfejs Pro (eksperymentalny)", + "description": "Układ dla zaawansowanych użytkowników (tylko na komputerze) z przeglądaniem wiadomości w wielu kartach i obiegami pracy między kontami. Standardowy interfejs pozostaje nietknięty; możesz wrócić w dowolnej chwili.", + "open_label": "Otwórz interfejs Pro", + "back_to_standard": "Powrót do standardu" + }, + "cross_unread": { + "label": "Wszystkie konta: Nieprzeczytane", + "description": "Pokazuje pozycję w sekcji Wszystkie konta z nieprzeczytaną pocztą ze wszystkich kont, obejmującą wszystkie foldery oprócz spamu, wysłanych, archiwum, kosza i roboczych." + }, + "cross_starred": { + "label": "Wszystkie konta: Oznaczone gwiazdką", + "description": "Pokazuje pozycję w sekcji Wszystkie konta z pocztą oznaczoną gwiazdką ze wszystkich kont, obejmującą wszystkie foldery oprócz spamu, wysłanych, archiwum, kosza i roboczych." + }, + "cross_all": { + "label": "Wszystkie konta: Cała poczta", + "description": "Pokazuje pozycję w sekcji Wszystkie konta z całą pocztą ze wszystkich kont, obejmującą wszystkie foldery oprócz spamu, wysłanych, archiwum, kosza i roboczych." + } + }, + "keywords": { + "title": "Etykiety e-mail", + "description": "Zdefiniuj etykiety do organizowania e-maili za pomocą kolorów. Są one przechowywane jako słowa kluczowe JMAP na serwerze.", + "add_keyword": "Dodaj etykietę", + "label_field": "Nazwa wyświetlana", + "label_placeholder": "np. Praca, Osobiste, Pilne", + "id_field": "ID etykiety", + "id_placeholder": "np. praca, osobiste", + "color_field": "Kolor", + "id_exists": "Ten ID etykiety już istnieje", + "edit": "Edytuj etykietę", + "delete": "Usuń etykietę", + "save": "Zapisz", + "add": "Dodaj", + "cancel": "Anuluj", + "migrating": "Aktualizowanie etykiety w istniejących e-mailach…", + "migration_error": "Nie udało się zaktualizować etykiety w istniejących e-mailach", + "filters_migration_error": "Niektóre reguły filtrów nadal używają starego tagu — zaktualizuj je ręcznie", + "nesting": { + "label": "Zagnieżdżone etykiety", + "description": "Zagnieżdżaj etykiety pod innymi etykietami i wyświetlaj je w panelu bocznym jako drzewo." + }, + "parent_field": "Etykieta nadrzędna", + "no_parent": "Brak etykiety nadrzędnej", + "too_long": "Ta ścieżka etykiety jest za długa (maksymalnie {max} znaków)", + "has_children_locked": "Pod tą etykietą zagnieżdżone są inne etykiety, więc jej nazwa i etykieta nadrzędna są zablokowane. Najpierw je przenieś lub usuń.", + "has_children_delete": "Najpierw usuń etykiety zagnieżdżone pod tą", + "visibility_field": "Widoczność w panelu bocznym", + "visibility": { + "show": "Pokaż", + "unread": "Pokaż przy nieprzeczytanych", + "hide": "Ukryj" + }, + "unrecognized": { + "title": "Odzyskaj etykiety z serwera", + "description": "Nazwy i kolory etykiet są zapisane w tej przeglądarce, ale same etykiety znajdują się w wiadomościach jako słowa kluczowe JMAP. Przeskanuj pocztę w poszukiwaniu słów kluczowych, dla których nie ma tu etykiety, i dodaj je ponownie.", + "scan": "Szukaj etykiet", + "rescan": "Skanuj ponownie", + "scanning": "Skanowanie… {scanned} z {total} wiadomości", + "none": "Wszystkie etykiety w Twoich wiadomościach są już tutaj zdefiniowane.", + "messages": "{count, plural, one {# wiadomość} few {# wiadomości} other {# wiadomości}}", + "add_all": "Dodaj wszystkie ({count})", + "partial": "Przeskanowano tylko {scanned} najnowszych z {total} wiadomości. Etykiety używane wyłącznie w starszej poczcie mogą zostać pominięte.", + "error": "Nie udało się przeskanować poczty w poszukiwaniu etykiet." + } + }, + "notifications": { + "test_sound": "Przetestuj dźwięk powiadomienia", + "sounds": { + "default": "Domyślny (Beep)", + "cheerful": "Pogodny", + "involved": "Dynamiczny", + "swift": "Szybki gest", + "relax": "Relaks" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Dźwięk powiadomienia", + "description": "Wybierz dźwięk odtwarzany dla powiadomień", + "choose": "Dźwięk", + "choose_desc": "Wybierz ton powiadomienia i kliknij ikonę głośnika, aby go odsłuchać" + }, + "email": { + "title": "Powiadomienia e-mail", + "description": "Skonfiguruj powiadomienia dla przychodzących wiadomości e-mail", + "enabled": "Powiadomienia e-mail", + "enabled_desc": "Pokazuj powiadomienia po nadejściu nowych wiadomości e-mail", + "sound": "Dźwięk powiadomienia", + "sound_desc": "Odtwarzaj sygnał dźwiękowy po nadejściu nowych wiadomości e-mail", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Powiadomienia kalendarza", + "description": "Skonfiguruj powiadomienia dla wydarzeń kalendarza", + "enabled": "Powiadomienia o wydarzeniach", + "enabled_desc": "Pokazuj alerty o nadchodzących wydarzeniach kalendarza", + "sound": "Dźwięk powiadomienia", + "sound_desc": "Odtwarzaj sygnał dźwiękowy dla przypomnień kalendarza", + "invitation_parsing": "Rozpoznawaj zaproszenia e-mail", + "invitation_parsing_desc": "Wykrywaj zaproszenia kalendarzowe w załącznikach wiadomości e-mail i pokazuj akcje kalendarza" + }, + "push": { + "confirm_disable_message": "To urządzenie przestanie otrzymywać powiadomienia, gdy strona jest zamknięta.", + "confirm_disable_title": "Wyłączyć powiadomienia w tle?", + "description": "Otrzymuj powiadomienia systemowe o nowych wiadomościach, gdy ta strona jest zamknięta. Dostarczane przez przekaźnik push Bulwark; przekaźnik nigdy nie widzi treści wiadomości.", + "disable": "Wyłącz", + "enable": "Włącz", + "dismiss_aria": "Zamknij monit powiadomień", + "ios_hint": "W systemie iOS najpierw zainstaluj stronę na ekranie głównym – Safari dostarcza Web Push tylko zainstalowanym PWA.", + "reenable": "Zarejestruj ponownie", + "relay_desc": "Domyślnie używa hostowanego przekaźnika Bulwark. Zmień tylko jeśli hostujesz samodzielnie.", + "relay_label": "Przekaźnik push", + "relay_locked": "Ustawione przez administratora", + "relay_locked_desc": "Adres URL przekaźnika push został ustawiony przez administratora i nie można go zmienić.", + "status_active": "Aktywne na tym urządzeniu", + "status_busy": "Przetwarzanie…", + "status_inactive": "Niewłączone na tym urządzeniu", + "status_unsupported": "Ta przeglądarka nie obsługuje Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Powiadomienia w tle", + "devices_title": "Zarejestrowane urządzenia", + "devices_desc": "Wszystkie urządzenia zarejestrowane do odbioru powiadomień w tle dla tego konta. Unieważnienie natychmiast wstrzymuje ich odbiór.", + "devices_loading": "Wczytywanie zarejestrowanych urządzeń…", + "devices_empty": "Żadne urządzenie nie jest zarejestrowane do powiadomień w tle.", + "devices_error": "Nie udało się wczytać zarejestrowanych urządzeń.", + "devices_refresh": "Odśwież", + "device_this": "To urządzenie", + "device_other": "Inne urządzenie", + "device_status_active": "Dostarcza powiadomienia", + "device_status_inactive": "Nigdy nie dostarczyło powiadomienia", + "device_status_unknown": "Nieznane temu przekaźnikowi", + "device_expires": "Wygasa {date}", + "revoke": "Unieważnij", + "confirm_revoke_title": "Unieważnić to urządzenie?", + "confirm_revoke_message": "Natychmiast przestanie odbierać powiadomienia w tle. Ponowne włączenie push na tym urządzeniu zarejestruje je od nowa.", + "confirm_revoke_message_this": "To urządzenie natychmiast przestanie odbierać powiadomienia w tle." + } + }, + "language_region": { + "title": "Język, region i czas", + "description": "Język, format daty, format godziny i inne preferencje regionalne", + "language": { + "label": "Język", + "description": "Wybierz preferowany język", + "english": "Angielski", + "french": "Francuski" + }, + "date_format": { + "label": "Format daty", + "description": "Jak daty są wyświetlane na liście e-maili", + "smart": "Inteligentny", + "relative": "Względny", + "full": "Zawsze pełna data", + "preview_today": "Dzisiaj:", + "preview_this_week": "W tym tygodniu:", + "preview_older": "Starsze:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Format czasu", + "description": "Wybierz zegar 12-godzinny lub 24-godzinny", + "12h": "12-godzinny", + "24h": "24-godzinny" + }, + "first_day": { + "label": "Pierwszy dzień tygodnia", + "description": "Rozpocznij tydzień w niedzielę lub poniedziałek", + "sunday": "Niedziela", + "monday": "Poniedziałek" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Czyść wyszukiwanie przy zmianie folderu", + "description": "Kliknięcie folderu (Odebrane, Wysłane…) czyści aktywne wyszukiwanie i otwiera ten folder. Gdy wyłączone, wyszukiwanie pozostaje i jest ponawiane w wybranym folderze." + }, + "request_read_receipt": { + "label": "Domyślnie żądaj potwierdzeń przeczytania", + "description": "Włącz wcześniej żądanie potwierdzenia przeczytania podczas tworzenia nowej wiadomości." + }, + "read_receipt_response": { + "label": "Odpowiadaj na żądania potwierdzenia przeczytania", + "description": "Co zrobić, gdy przychodząca wiadomość prosi o potwierdzenie przeczytania.", + "ask": "Pytaj za każdym razem", + "always": "Zawsze wysyłaj", + "never": "Nigdy nie wysyłaj" + }, + "title": "Zachowanie poczty e-mail", + "description": "Skonfiguruj sposób obsługi wiadomości e-mail", + "mark_read": { + "label": "Oznacz jako przeczytane", + "description": "Kiedy oznaczać wiadomości e-mail jako przeczytane po otwarciu", + "instant": "Natychmiast", + "delay_3s": "Po 3 sekundach", + "delay_5s": "Po 5 sekundach", + "never": "Nigdy" + }, + "delete_action": { + "label": "Akcja usuwania", + "description": "Co ma się stać po usunięciu wiadomości e-mail", + "trash": "Przenieś do kosza", + "trash_and_read": "Przenieś do kosza i oznacz jako przeczytane", + "permanent": "Usuń trwale", + "warning": "Wiadomości e-mail zostaną trwale usunięte i nie będzie można ich odzyskać. Ta akcja jest nieodwracalna." + }, + "message_spacing": { + "label": "Odstępy wiadomości", + "description": "Margines wokół treści wiadomości w czytniku", + "auto": "Automatycznie (zalecane)", + "always": "Zawsze dodawaj odstępy", + "edge": "Od krawędzi do krawędzi" + }, + "plain_text_font": { + "label": "Czcionka zwykłego tekstu", + "description": "Czcionka używana dla wiadomości w zwykłym tekście", + "mono": "O stałej szerokości", + "sans": "Normalna (jak aplikacja)" + }, + "archive_mode": { + "label": "Archiwizuj w", + "description": "Jak organizować wiadomości e-mail podczas archiwizacji", + "single": "Jeden folder", + "year": "Folder na każdy rok", + "month": "Folder na każdy miesiąc", + "reorganize": "Zreorganizuj istniejące archiwum", + "reorganize_success": "{count, plural, =0 {Brak wiadomości do reorganizacji} =1 {Zreorganizowano 1 wiadomość} other {Zreorganizowano # wiadomości}}", + "reorganize_error": "Nie udało się zreorganizować archiwum" + }, + "permanently_delete_junk": { + "label": "Trwale usuwaj spam", + "description": "Trwale usuwaj wiadomości z folderu Spam/Niechciane zamiast przenosić je do Kosza" + }, + "confirm_bulk_delete": { + "label": "Potwierdzaj przed usunięciem wielu wiadomości", + "description": "Pytaj przed przeniesieniem wielu zaznaczonych wiadomości do kosza. Trwałe usunięcie zawsze pyta." + }, + "mail_layout": { + "label": "Układ poczty", + "description": "Wybierz między klasycznym podzielonym panelem czytania a stylem czytania skupionym jak w Gmailu.", + "split": "Widok podzielony", + "split_description": "Lista wiadomości i panel czytania pozostają widoczne obok siebie.", + "focus": "Skupiona lista", + "focus_description": "Pokazuj jeden wiersz na wiadomość i otwieraj pocztę na pełną szerokość, pozostawiając widoczny pasek folderów.", + "horizontal": "Okienko czytania na dole", + "horizontal_description": "Pokaż listę wiadomości u góry i otwórz wybraną wiadomość w okienku czytania pod nią." + }, + "list_columns": { + "label": "Pokaż listę w kolumnach", + "description": "Wyświetla jednowierszowe wiersze jako tabelę z kolumnami Od, Temat, Data i Rozmiar pod nagłówkiem." + }, + "show_preview": { + "label": "Pokaż tekst podglądu", + "description": "Wyświetlaj podgląd wiadomości e-mail na liście", + "focus_description": "Wyświetlaj wbudowany tekst podglądu w jednoliniowej skupionej liście wiadomości" + }, + "show_verification_codes": { + "label": "Pokazuj kody weryfikacyjne", + "description": "Oferuj kod z e-maili logowania i potwierdzenia do skopiowania jednym kliknięciem, na liście przez jeden dzień i w wiadomości" + }, + "disable_threading": { + "label": "Wyłącz grupowanie konwersacji", + "description": "Pokazuj wiadomości e-mail jako pojedyncze wiadomości zamiast grupowania według konwersacji" + }, + "plain_text_mode": { + "label": "Tylko zwykły tekst", + "description": "Wyłącz edytor tekstu sformatowanego i wysyłaj wszystkie wiadomości e-mail tylko jako zwykły tekst, w tym odpowiedzi i przekazywanie dalej" + }, + "auto_select_reply_identity": { + "label": "Automatycznie wybieraj adres odpowiedzi", + "description": "Podczas odpowiadania automatycznie przełączaj adres nadawcy na tożsamość, która pierwotnie otrzymała wiadomość" + }, + "reply_identity_match": { + "label": "Dopasowanie adresu odbioru", + "description": "Które adresy odbioru są uznawane za Twoje. „Tylko dokładny adres” wybiera wyłącznie jedną ze skonfigurowanych tożsamości; „ta sama domena” traktuje dodatkowo każdy inny adres w domenach Twoich tożsamości jako alias catch-all i przepisuje na niego nagłówek Od. Wybierz dokładny adres, jeśli te adresy to listy dystrybucyjne.", + "exact": "Tylko dokładny adres", + "domain": "Dowolny adres w moich domenach" + }, + "signature_position": { + "label": "Pozycja podpisu", + "description": "Gdzie wstawić podpis w odpowiedziach i wiadomościach przekazanych dalej. Nad cytowanym tekstem brzmi naturalnie jako zakończenie odpowiedzi; pod nim zachowuje oryginalną wiadomość w całości.", + "above_quote": "Przed cytowanym tekstem", + "below_quote": "Po cytowanym tekście" + }, + "signature_separator": { + "label": "Separator podpisu", + "description": "Poprzedź podpis standardową linią separatora \"-- \" (RFC 3676). Wyłącz, jeśli chcesz przejść bezpośrednio z treści wiadomości do podpisu." + }, + "sub_address_delimiter": { + "label": "Separator sub-adresu", + "description": "Znak oddzielający Twoją nazwę użytkownika od tagu sub-adresu. Użyj separatora zgodnego z Twoim serwerem pocztowym (np. user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "Niestandardowy…", + "custom_input_label": "Niestandardowy znak separatora" + }, + "attachment_click_action": { + "label": "Akcja po kliknięciu załącznika", + "description": "Wybierz, czy kliknięcie załącznika pliku ma pokazać podgląd, czy od razu go pobrać", + "preview": "Podgląd, gdy to możliwe", + "download": "Pobierz natychmiast" + }, + "attachment_position": { + "label": "Pozycja załączników", + "description": "Gdzie wyświetlać załączniki w nagłówku wiadomości e-mail", + "beside-sender": "Obok nadawcy", + "below-header": "Pod nagłówkiem" + }, + "emails_per_page": { + "10": "10 wiadomości", + "25": "25 wiadomości", + "50": "50 wiadomości", + "100": "100 wiadomości", + "label": "Wiadomości na stronę", + "description": "Liczba wiadomości e-mail ładowanych jednorazowo" + }, + "always_light_mode": { + "label": "Zawsze wyświetlaj wiadomości w trybie jasnym", + "description": "Renderuj treść wiadomości e-mail w trybie jasnym nawet wtedy, gdy aplikacja jest w trybie ciemnym, aby uniknąć problemów z konwersją trybu ciemnego" + }, + "external_content": { + "label": "Treści zewnętrzne", + "description": "Jak obsługiwać obrazy i treści zewnętrzne", + "ask": "Zawsze pytaj", + "block": "Zawsze blokuj", + "allow": "Zawsze zezwalaj" + }, + "trusted_senders": { + "label": "Zaufani nadawcy", + "description": "Zarządzaj nadawcami, których obrazy ładują się automatycznie", + "count_zero": "Brak", + "count_one": "1 nadawca", + "count_other": "{count} nadawców", + "modal_title": "Zaufani nadawcy", + "empty_title": "Brak zaufanych nadawców", + "empty_description": "Podczas wyświetlania wiadomości e-mail z zablokowanymi obrazami kliknij \"Zawsze ufaj temu nadawcy\", aby dodać go tutaj.", + "add_manually": "Dodaj nadawcę ręcznie", + "add_button": "Dodaj", + "add_placeholder": "Wprowadź adres e-mail", + "search_placeholder": "Szukaj nadawców...", + "no_results": "Żaden nadawca nie pasuje do wyszukiwania", + "remove": "Usuń", + "close": "Zamknij", + "invalid_email": "Wprowadź prawidłowy adres e-mail", + "already_added": "Ten nadawca jest już zaufany", + "save_error": "Nie udało się zapisać - sprawdź dziennik debugowania Kontaktów", + "use_address_book_label": "Synchronizuj z książką adresową", + "use_address_book_description": "Zapisuj zaufanych nadawców w osobnej książce „Zaufani nadawcy”, aby synchronizować ich na wszystkich urządzeniach" + }, + "hover_actions": { + "label": "Szybkie akcje po najechaniu", + "description": "Wybierz szybkie akcje widoczne po najechaniu na wiadomość e-mail na liście", + "delete": "Usuń", + "star": "Oznacz gwiazdką / usuń gwiazdkę", + "mark_read": "Oznacz jako przeczytane / nieprzeczytane", + "archive": "Archiwizuj", + "tag": "Etykieta", + "spam": "Oznacz jako spam", + "not_spam": "Nie spam", + "none_selected": "Nie wybrano żadnych akcji", + "mode_label": "Tryb wyświetlania", + "mode_inline": "Wbudowany", + "mode_floating": "Pływający", + "corner_label": "Pozycja pływająca", + "corner_top-left": "Lewy górny róg", + "corner_top-right": "Prawy górny róg", + "corner_bottom-left": "Lewy dolny róg", + "corner_bottom-right": "Prawy dolny róg" + }, + "default_mail_program": { + "label": "Domyślny program pocztowy", + "description": "Zarejestruj {appName} jako domyślny program pocztowy dla linków mailto:", + "button": "Ustaw jako domyślny", + "success": "Przeglądarka poprosiła o ustawienie jako domyślnego", + "error": "Twoja przeglądarka nie obsługuje tej funkcji" + }, + "attachment_reminder": { + "label": "Przypomnienie o załączniku", + "description": "Ostrzegaj przed wysłaniem, gdy wiadomość wspomina o załącznikach, ale żaden nie jest dołączony", + "keywords_label": "Słowa kluczowe wyzwalające", + "keywords_description": "Słowa lub frazy, które wyzwalają przypomnienie po znalezieniu w wiadomości", + "add_placeholder": "Dodaj słowo kluczowe...", + "add": "Add", + "remove": "Usuń" + }, + "empty_subject_warning": { + "label": "Ostrzeżenie o pustym temacie", + "description": "Poproś o potwierdzenie przed wysłaniem wiadomości bez tematu." + }, + "recipient_mentions": { + "label": "Wspominaj odbiorców za pomocą @", + "description": "Wpisanie @ w wiadomości podpowiada jej odbiorców i wstawia imię wybranego. Enter lub Tab wybiera podświetlone imię." + }, + "mention_adds_recipient": { + "label": "Kontakty we wzmiankach @", + "description": "Po wpisaniu @ podpowiadane są też Twoje kontakty i osoby z katalogu. Wybranie kogoś, kto nie jest jeszcze odbiorcą, dodaje go do wiadomości.", + "to": "Dodaj do Do", + "cc": "Dodaj do DW", + "off": "Tylko odbiorcy" + }, + "hide_inline_image_attachments": { + "label": "Ukryj obrazy osadzone z listy załączników", + "description": "Obrazy osadzone w treści wiadomości nie są wyświetlane jako osobne załączniki" + }, + "attachment_image_previews": { + "label": "Pokaż podgląd obrazów w załącznikach", + "description": "Wyświetlaj załączone obrazy jako miniatury zamiast ogólnych ikon plików" + }, + "send_delay": { + "label": "Cofnij wysyłkę / opóźnienie wysyłki", + "description": "Opóźnia normalne wysyłanie krótkim oknem po stronie serwera.", + "off": "Wyłączone", + "seconds": "{seconds} sekund", + "unsupported": "Bieżące konto nie zgłasza obsługi opóźnionej wysyłki. Ustawienie pozostanie zapisane dla innych kont." + }, + "return_to_list_after_action": { + "label": "Powrót do listy po usunięciu lub oznaczeniu jako nieprzeczytane", + "description": "Po usunięciu lub oznaczeniu otwartej wiadomości jako nieprzeczytanej wróć do listy wiadomości zamiast otwierać następną." + }, + "swipe_right_action": { + "label": "Akcja przy przesunięciu w prawo (mobilne)", + "description": "Na urządzeniach dotykowych akcja wykonywana po przesunięciu wiadomości w prawo na liście." + }, + "swipe_left_action": { + "label": "Akcja przy przesunięciu w lewo (mobilne)", + "description": "Na urządzeniach dotykowych akcja wykonywana po przesunięciu wiadomości w lewo na liście." + }, + "swipe_actions": { + "none": "Brak", + "archive": "Archiwizuj", + "delete": "Usuń", + "mark_read": "Przełącz przeczytane", + "star": "Przełącz gwiazdkę", + "spam": "Oznacz jako spam" + }, + "rtl_editing": { + "label": "Obsługa edycji od prawej do lewej", + "description": "Dodaje przycisk kierunku do paska narzędzi edytora, pozwalający ustawić akapity od lewej do prawej lub od prawej do lewej" + } + }, + "composer": { + "title": "Redagowanie", + "description": "Skonfiguruj ustawienia tworzenia wiadomości e-mail", + "autosave": { + "label": "Interwał autozapisu", + "description": "Jak często automatycznie zapisywać szkice", + "30s": "Co 30 sekund", + "1m": "Co minutę", + "2m": "Co 2 minuty", + "5m": "Co 5 minut" + }, + "send_confirmation": { + "label": "Potwierdzenie wysyłki", + "description": "Proś o potwierdzenie przed wysłaniem wiadomości e-mail" + }, + "default_reply": { + "label": "Domyślny tryb odpowiedzi", + "description": "Domyślna akcja po kliknięciu odpowiedzi", + "reply": "Odpowiedz", + "reply_all": "Odpowiedz wszystkim" + } + }, + "privacy": { + "title": "Prywatność i bezpieczeństwo", + "description": "Zarządzaj ustawieniami prywatności i bezpieczeństwa", + "external_images": { + "label": "Blokuj obrazy zewnętrzne", + "description": "Zapobiegaj śledzeniu przez zewnętrzne obrazy" + }, + "session_timeout": { + "label": "Limit czasu sesji", + "description": "Automatycznie wyloguj po okresie bezczynności", + "never": "Nigdy", + "30m": "30 minut", + "1h": "1 godzina", + "4h": "4 godziny" + }, + "clear_cache": { + "label": "Wyczyść pamięć podręczną", + "description": "Usuń dane z pamięci podręcznej i pliki tymczasowe", + "button": "Wyczyść pamięć podręczną", + "confirm": "Czy na pewno chcesz wyczyścić pamięć podręczną?", + "success": "Pamięć podręczna została wyczyszczona" + } + }, + "account": { + "title": "Konto", + "description": "Wyświetl informacje o swoim koncie", + "name_label": "Nazwa wyświetlana", + "username_label": "Nazwa użytkownika", + "account_type_label": "Typ konta", + "auth_method_label": "Uwierzytelnianie", + "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", + "auth_method_basic": "Hasło", + "auth_method_token": "Token dostępu", + "demo_account": "Konto demo", + "email": { + "label": "Adres e-mail", + "value": "{email}" + }, + "server": { + "label": "Serwer JMAP", + "value": "{server}" + }, + "storage": { + "label": "Wykorzystanie przestrzeni", + "used": "{used} z {total} wykorzystano", + "percentage": "Wykorzystano {percent}%" + }, + "last_sync": { + "label": "Ostatnia synchronizacja", + "value": "{time}" + }, + "accounts": { + "title": "Zalogowane konta", + "description": "Przeciągnij, aby zmienić kolejność wyświetlania kont w menu rozwijanym", + "active": "Aktualnie aktywne konto", + "default_badge": "Konto domyślne", + "set_default": "Ustaw jako domyślne", + "switch_to": "Przełącz na to konto", + "move_up": "Przesuń w górę", + "move_down": "Przesuń w dół", + "drag_handle": "Przeciągnij, aby zmienić kolejność", + "add": "Dodaj konto" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Udostępnione mnie", + "description": "Konta grupowe i udostępnione, którymi możesz zarządzać. Wybierz jedno, aby edytować jego filtry, odpowiedź wakacyjną, kalendarze i kontakty.", + "shared_label": "Konto udostępnione" + } + }, + "security": { + "title": "Bezpieczeństwo konta", + "description": "Zarządzaj hasłem, uwierzytelnianiem dwuskładnikowym i ustawieniami bezpieczeństwa", + "detecting": "Wykrywanie możliwości serwera...", + "not_available": "Zarządzanie bezpieczeństwem konta nie jest dostępne dla tego serwera pocztowego. Wymagane uprawnienia mogą być wyłączone. Szczegóły znajdziesz w dokumentacji.", + "password": { + "title": "Zmień hasło", + "current": "Aktualne hasło", + "new": "Nowe hasło", + "confirm": "Potwierdź nowe hasło", + "submit": "Zmień hasło", + "success": "Hasło zostało zmienione", + "error_title": "Zmiana hasła nie powiodła się", + "error_mismatch": "Nowe hasła nie są zgodne", + "error_min_length": "Hasło musi mieć co najmniej 8 znaków", + "error_generic": "Nie udało się zmienić hasła" + }, + "display_name": { + "label": "Nazwa wyświetlana", + "description": "Twoja nazwa widoczna na serwerze", + "placeholder": "Wprowadź swoją nazwę wyświetlaną", + "save": "Zapisz", + "success": "Nazwa wyświetlana została zaktualizowana", + "error": "Nie udało się zaktualizować nazwy wyświetlanej" + }, + "totp": { + "section_title": "Uwierzytelnianie dwuskładnikowe", + "label": "Uwierzytelnianie TOTP", + "description": "Dodaj dodatkową warstwę bezpieczeństwa za pomocą jednorazowego hasła opartego na czasie", + "active": "Włączone", + "inactive": "Wyłączone", + "enabled": "Uwierzytelnianie dwuskładnikowe włączone", + "disabled": "Uwierzytelnianie dwuskładnikowe wyłączone", + "enable_error": "Nie udało się włączyć 2FA", + "disable_error": "Nie udało się wyłączyć 2FA", + "setup_instructions": "Skopiuj ten adres URL do swojej aplikacji uwierzytelniającej (Google Authenticator, Authy itp.):", + "verification_code": "Kod weryfikacyjny", + "confirm": "Potwierdź", + "disable": "Wyłącz", + "disable_confirm_prompt": "Wprowadź hasło, aby wyłączyć uwierzytelnianie dwuskładnikowe.", + "password_required": "Hasło jest wymagane", + "code_required": "Kod weryfikacyjny jest wymagany", + "code_invalid": "Nieprawidłowy kod weryfikacyjny. Sprawdź aplikację uwierzytelniającą i spróbuj ponownie." + }, + "app_passwords": { + "title": "Hasła aplikacji", + "description": "Twórz hasła dla aplikacji, które nie obsługują uwierzytelniania dwuskładnikowego", + "add": "Dodaj", + "create": "Utwórz", + "cancel": "Anuluj", + "generate": "Generuj", + "name_label": "Nazwa aplikacji", + "name_placeholder": "np. Thunderbird, iPhone Mail", + "password_label": "Hasło (pozostaw puste, aby wygenerować automatycznie)", + "password_placeholder": "Automatycznie wygenerowane, jeśli puste", + "added": "Hasło aplikacji zostało utworzone", + "removed": "Hasło aplikacji zostało usunięte", + "add_error": "Nie udało się utworzyć hasła aplikacji", + "remove_error": "Nie udało się usunąć hasła aplikacji", + "none": "Brak skonfigurowanych haseł aplikacji", + "done": "Gotowe", + "expires_label": "Wygasa (opcjonalnie)", + "copy_now_warning": "Skopiuj to hasło teraz - nie zostanie ponownie wyświetlone.", + "allowed_ips_label": "Dozwolone adresy IP (opcjonalnie)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Oddzielone przecinkiem lub spacją. Pozostaw puste, aby zezwolić na dowolny IP." + }, + "api_keys": { + "title": "Klucze API", + "description": "Twórz klucze API dla skryptów i integracji komunikujących się bezpośrednio z serwerem", + "name_label": "Nazwa klucza", + "name_placeholder": "np. Skrypt kopii zapasowej, CI runner", + "copy_now_warning": "Skopiuj ten klucz API teraz - nie zostanie pokazany ponownie.", + "added": "Klucz API utworzony", + "removed": "Klucz API usunięty", + "add_error": "Nie udało się utworzyć klucza API", + "remove_error": "Nie udało się usunąć klucza API", + "none": "Brak skonfigurowanych kluczy API" + }, + "encryption": { + "section_title": "Szyfrowanie danych w spoczynku", + "label": "Szyfrowanie wiadomości e-mail", + "description": "Szyfruj wiadomości przechowywane na serwerze dla większej prywatności", + "active": "Szyfrowanie {type} włączone", + "inactive": "Wyłączone", + "enabled": "Szyfrowanie danych w spoczynku włączone", + "disabled_success": "Szyfrowanie danych w spoczynku wyłączone", + "error": "Nie udało się zaktualizować ustawień szyfrowania", + "algorithm_label": "Algorytm szyfrowania", + "encrypt_on_append": "Szyfruj nowe wiadomości podczas przesyłania", + "allow_spam_training": "Zezwalaj na trenowanie filtra spamu przed zaszyfrowaniem wiadomości", + "enable_encryption": "Włącz szyfrowanie", + "enable_tooltip": "Włącz szyfrowanie tym kluczem", + "configure_title": "Włącz szyfrowanie tym kluczem", + "enable_button": "Włącz", + "disable_tooltip": "Wyłącz szyfrowanie", + "enabled_success": "Szyfrowanie danych w spoczynku włączone" + }, + "email_client": { + "title": "Konfiguracja klienta poczty", + "description": "Użyj tych danych uwierzytelniających, aby skonfigurować klienta poczty na komputerze lub urządzeniu mobilnym (Thunderbird, Apple Mail, Outlook itp.)", + "jmap_username_label": "Nazwa użytkownika JMAP", + "copy": "Kopiuj", + "copied": "Skopiowano", + "password_instructions": "Użyj powyższej nazwy użytkownika JMAP wraz z hasłem aplikacji, aby zalogować się w kliencie poczty. Utwórz hasło aplikacji w sekcji powyżej, jeśli jeszcze tego nie zrobiłeś." + }, + "link_device": { + "title": "Połącz aplikację mobilną", + "description": "Zaloguj się do aplikacji Bulwark Mail na telefonie bez wpisywania w niej hasła. Telefon otrzymuje własne logowanie, więc wylogowanie się tutaj go nie wyloguje.", + "generate": "Pokaż kod QR", + "regenerate": "Pokaż nowy kod", + "instructions": "Otwórz aplikację Bulwark Mail i na ekranie logowania stuknij \"Scan a sign-in code\" (lub \"Add account\" → \"Scan a sign-in code\", jeśli już jesteś zalogowany), a następnie skieruj aparat tutaj.", + "expires_in": "Ten kod wygaśnie za {seconds} sekund. Można go użyć tylko raz.", + "expired": "Ten kod wygasł.", + "error": "Nie udało się utworzyć kodu parowania. Spróbuj ponownie.", + "password_prompt": "Wpisz hasło, aby potwierdzić swoją tożsamość. Telefon otrzymuje własne logowanie; hasło nie jest wysyłane na telefon.", + "show_password": "Pokaż hasło", + "hide_password": "Ukryj hasło", + "continue": "Dalej", + "error_wrong_password": "Nieprawidłowe hasło. Spróbuj ponownie.", + "totp_prompt": "Wpisz kod z aplikacji uwierzytelniającej.", + "error_password_or_code": "Hasło lub kod jest nieprawidłowy.", + "error_not_signed_in": "Twoja sesja wygasła. Zaloguj się ponownie, aby połączyć urządzenie.", + "error_impersonation": "Nie można łączyć urządzeń podczas przeglądania skrzynki pocztowej innego użytkownika.", + "error_too_many_attempts": "Zbyt wiele prób. Odczekaj chwilę i spróbuj ponownie.", + "error_session_secret": "Łączenie urządzeń nie jest skonfigurowane na tym serwerze (brak SESSION_SECRET). Skontaktuj się z administratorem.", + "error_server_unreachable": "Nie udało się połączyć z serwerem poczty. Spróbuj ponownie.", + "error_pairing_unavailable": "Twój serwer poczty nie pozwala na łączenie urządzeń w ten sposób. Skontaktuj się z administratorem.", + "qr_alt": "Kod QR do logowania", + "link_label": "Link logowania", + "copy_link": "Kopiuj link logowania", + "copied": "Skopiowano", + "copy_hint": "Brak aparatu? Skopiuj link i wklej go w aplikacji za pomocą \"Paste sign-in link\".", + "success_title": "Zalogowano na telefonie", + "success_body": "Możesz zamknąć tę stronę. Aby połączyć kolejne urządzenie, pokaż nowy kod.", + "link_another": "Połącz kolejne urządzenie", + "use_sso": "Zamiast tego użyj logowania jednokrotnego", + "error_insecure_server": "Aplikacja mobilna łączy się z serwerami poczty tylko przez https, a ten używa http." + }, + "public_keys": { + "name_label": "Nazwa klucza publicznego", + "name_placeholder": "np. Mój klucz", + "key_label": "Klucz publiczny (S/MIME lub PGP w formacie ASCII)", + "key_placeholder": "Wklej tutaj swój klucz publiczny", + "none": "Nie skonfigurowano żadnych kluczy publicznych" + } + }, + "identities": { + "title": "Tożsamości nadawcy", + "description": "Zarządzaj adresami e-mail, z których możesz wysyłać wiadomości", + "identities_count": { + "label": "Twoje tożsamości", + "description": "Adresy e-mail skonfigurowane do wysyłania", + "count_zero": "Brak tożsamości", + "count_one": "1 tożsamość", + "count_other": "{count} tożsamości" + }, + "manage": "Zarządzaj tożsamościami", + "sub_addressing": { + "label": "Subadresowanie", + "description": "Używaj tagów takich jak user+tag@domain.com, aby organizować pocztę przychodzącą", + "learn_more": "Dowiedz się więcej" + } + }, + "vacation": { + "title": "Nieobecność", + "description": "Automatycznie obsługuj przychodzące wiadomości e-mail podczas nieobecności", + "loading": "Ładowanie ustawień autorespondera urlopowego...", + "not_supported": "Twój serwer pocztowy nie obsługuje odpowiedzi urlopowych.", + "fetch_error": "Nie udało się załadować ustawień urlopowych. Spróbuj ponownie.", + "status": { + "label": "Odpowiadaj automatycznie", + "description": "Wysyłaj automatyczną odpowiedź do osób, które do Ciebie piszą", + "active": "Aktywny", + "inactive": "Nieaktywny" + }, + "date_range": { + "start": "Data rozpoczęcia", + "start_description": "Pozostaw puste, aby nie ustawiać daty początkowej", + "end": "Data zakończenia", + "end_description": "Pozostaw puste, aby nie ustawiać daty końcowej" + }, + "message": { + "subject_label": "Temat", + "subject_description": "Wiersz tematu automatycznej odpowiedzi", + "subject_placeholder": "Poza biurem", + "body_label": "Treść wiadomości", + "body_description": "Treść wiadomości w zwykłym tekście", + "body_placeholder": "Dziękuję za wiadomość. Obecnie jestem poza biurem i odpowiem po powrocie.", + "html_label": "Wiadomość sformatowana (HTML)", + "html_description": "Dodaj sformatowaną wersję z odnośnikami i stylami. Odbiorcy, których program pocztowy nie może jej wyświetlić, zobaczą powyższy zwykły tekst.", + "html_placeholder": "Napisz sformatowaną odpowiedź o nieobecności…" + }, + "preview": { + "title": "Podgląd", + "show": "Pokaż podgląd", + "hide": "Ukryj podgląd" + }, + "save": "Zapisz zmiany", + "saving": "Zapisywanie...", + "warnings": { + "end_before_start": "Data zakończenia musi być późniejsza niż data rozpoczęcia", + "invalid_date": "Podaj prawidłową datę rozpoczęcia i zakończenia z godziną albo pozostaw pole puste", + "start_in_past": "Data rozpoczęcia jest w przeszłości", + "empty_body": "Treść wiadomości jest pusta - odbiorcy otrzymają pustą odpowiedź", + "subject_too_long": "Temat jest za długi dla serwera (maksymalnie {max} bajtów).", + "body_too_long": "Wiadomość jest za długa dla serwera (maksymalnie {max} bajtów wraz z formatowaniem).", + "forward_address": "Podaj prawidłowy adres do przekazywania", + "not_running": "Przekazywanie lub odbiorcy odpowiedzi są zapisani, ale obecnie nieaktywni. Zapisz, aby ponownie je włączyć." + }, + "forward": { + "title": "Przekazywanie", + "description": "Przekazuje wiadomości przychodzące na inny adres, z automatyczną odpowiedzią lub bez niej, w okresie podanym powyżej (bez daty zakończenia, dopóki nie zostanie wyłączone). Spam nie jest przekazywany.", + "enabled_label": "Przekazuj wiadomości", + "to_label": "Prześlij dalej do", + "to_placeholder": "email@example.com", + "keep_label": "Pozostaw w Odebranych", + "keep_description": "W przeciwnym razie wiadomość jest tylko przekazywana: nie zostaje w tej skrzynce i nie działa na nią żadna reguła filtrowania." + }, + "audience": { + "label": "Odpowiadaj", + "description": "Nadawcy wewnętrzni: {domains}", + "all": "Wszystkim nadawcom", + "internal": "Tylko nadawcom wewnętrznym", + "external": "Tylko nadawcom zewnętrznym" + }, + "auto_reply": { + "title": "Automatyczna odpowiedź" + } + }, + "folders": { + "role_memos": "Notatki", + "title": "Foldery", + "description": "Zarządzaj folderami poczty i przypisuj standardowe role", + "folder_list": "Twoje foldery", + "folder_list_description": "Kliknij ikonę folderu, aby go dostosować", + "standard_roles": "Standardowe role folderów", + "standard_roles_description": "Przypisz foldery używane dla standardowych ról skrzynki, takich jak Odebrane, Wysłane, Kosz itp.", + "role_inbox": "Odebrane", + "role_drafts": "Szkice", + "role_sent": "Wysłane", + "role_trash": "Kosz", + "role_junk": "Spam / Niechciane", + "role_archive": "Archiwum", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Brak", + "create_folder": "Utwórz folder", + "create_subfolder": "Utwórz podfolder", + "reorder": "Przeciągnij, aby zmienić kolejność", + "reorder_error": "Nie udało się zmienić kolejności folderów", + "move_error": "Nie udało się przenieść folderu", + "select_folder": "Zaznacz {name}", + "select_hint": "Zaznacz. Shift+kliknięcie zaznacza wszystkie foldery aż do ostatnio klikniętego.", + "selected_count": "{count, plural, one {Zaznaczono # folder} few {Zaznaczono # foldery} many {Zaznaczono # folderów} other {Zaznaczono # folderu}}", + "move_to": "Przenieś do...", + "move_to_top_level": "Najwyższy poziom", + "move": "Przenieś", + "clear_selection": "Wyczyść zaznaczenie", + "folders_moved": "{count, plural, one {Przeniesiono # folder} few {Przeniesiono # foldery} many {Przeniesiono # folderów} other {Przeniesiono # folderu}}", + "move_partial_error": "{count, plural, one {Nie udało się przenieść # folderu} few {Nie udało się przenieść # folderów} many {Nie udało się przenieść # folderów} other {Nie udało się przenieść # folderu}}", + "subfolder_of": "W folderze {name}", + "subfolder_name": "Nazwa podfolderu", + "new_folder_name": "Nazwa folderu", + "rename": "Zmień nazwę", + "change_icon": "Zmień ikonę", + "count_mode": "Licznik dla {name}", + "count_mode_default": "Licznik: domyślny", + "count_mode_unread": "Tylko nieprzeczytane", + "count_mode_all": "Nieprzeczytane i łącznie", + "count_mode_none": "Bez licznika", + "delete": "Usuń", + "confirm_delete": "Czy na pewno chcesz usunąć „{name}”? Wiadomości w tym folderze zostaną przeniesione do Kosza.", + "create": "Utwórz", + "cancel": "Anuluj", + "no_folders": "Brak niestandardowych folderów", + "cannot_delete_role": "Nie można usunąć folderu ze standardową rolą. Najpierw usuń tę rolę.", + "folder_created": "Folder został utworzony", + "folder_renamed": "Nazwa folderu została zmieniona", + "folder_deleted": "Folder został usunięty", + "role_updated": "Rola folderu została zaktualizowana", + "error_create": "Nie udało się utworzyć folderu", + "error_rename": "Nie udało się zmienić nazwy folderu", + "error_delete": "Nie udało się usunąć folderu", + "error_delete_has_children": "Nie można usunąć folderu: nadal zawiera podfoldery. Najpierw usuń je lub przenieś.", + "error_delete_has_email": "Nie można usunąć folderu: nadal zawiera wiadomości e-mail. Najpierw je przenieś lub usuń.", + "error_role": "Nie udało się zaktualizować roli folderu" + }, + "advanced": { + "title": "Zaawansowane", + "description": "Opcje zaawansowane i ustawienia deweloperskie", + "debug_mode": { + "label": "Tryb debugowania", + "description": "Włącz szczegółowe logowanie do rozwiązywania problemów" + }, + "debug_categories": { + "description": "Wybierz kategorie do logowania. Wyłącz kategorie, których nie potrzebujesz, aby zmniejszyć szum w konsoli.", + "jmap": "Klient JMAP", + "jmap_description": "Operacje na skrzynkach, pobieranie wiadomości e-mail i żądania protokołu JMAP", + "calendar": "Kalendarz", + "calendar_description": "Wydarzenia kalendarza, importy i wiadomości planowania", + "tasks": "Zadania", + "tasks_description": "Tworzenie zadań kalendarza, pobieranie i aktualizacje", + "auth": "Uwierzytelnianie", + "auth_description": "Logowanie, TOTP, wymiana tokenów i zarządzanie sesją", + "filters": "Filtry", + "filters_description": "Reguły filtrów Sieve i skrypty urlopowe", + "email": "Wyświetlanie wiadomości", + "email_description": "Renderowanie wiadomości e-mail, przetwarzanie TNEF i oznaczanie jako przeczytane", + "push": "Powiadomienia push", + "push_description": "Konfiguracja i dostarczanie powiadomień push", + "contacts": "Kontakty i książki adresowe", + "contacts_description": "Synchronizacja kontaktów, operacje książki adresowej i zaufani nadawcy" + }, + "settings_sync": { + "label": "Synchronizacja ustawień", + "description": "Synchronizuj ustawienia między przeglądarkami i urządzeniami" + }, + "sender_favicons": { + "label": "Favikony nadawców", + "description": "Pokazuj ikony stron internetowych jako zdjęcia profilowe nadawców firmowych", + "refresh_label": "Odśwież ikony nadawców", + "refresh_description": "Pobierz ponownie ikony stron, na przykład gdy nadawca zmienił swoją ikonę", + "refresh_button": "Odśwież", + "refresh_done": "Ikony nadawców zostaną pobrane ponownie" + }, + "show_avatars_in_junk": { + "label": "Pokazuj awatary w folderze spam", + "description": "Pokazuj zdjęcia profilowe i favikony nadawców w folderze spam. Domyślnie wyłączone, aby próby phishingu nie wyglądały na wiarygodne." + }, + "keyboard_shortcuts": { + "label": "Skróty klawiszowe", + "description": "Wyświetl dostępne skróty klawiszowe", + "button": "Pokaż skróty" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Resetuj ustawienia", + "description": "Przywróć wszystkie ustawienia do wartości domyślnych", + "button": "Przywróć domyślne" + }, + "export_settings": { + "label": "Eksportuj ustawienia", + "description": "Pobierz swoje ustawienia jako JSON", + "button": "Eksportuj" + }, + "import_settings": { + "label": "Importuj ustawienia", + "description": "Prześlij ustawienia z pliku JSON", + "button": "Importuj" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Kod źródłowy" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Klikaj spam i phishing, zanim dotrą do Twojej skrzynki odbiorczej. Prawdziwą pocztę przepuszczaj. Stracisz wszystkie trzy tarcze i skrzynka padnie.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Prawdziwa poczta" + }, + "click_to_block": "Kliknij, aby zablokować", + "let_through": "Przepuść", + "start": "Start", + "play_again": "Zagraj ponownie", + "score": "Wynik", + "combo": "Bonus za serię", + "shields_left": "Pozostało {count} z {total} tarcz", + "seconds": "{seconds} s", + "best": "Rekord: {score}", + "new_best": "Nowy rekord", + "held": "Skrzynka obroniona", + "overrun": "Skrzynka zalana", + "stats": { + "caught": "Zablokowane", + "delivered": "Dostarczone", + "mistakes": "Błędy" + } + } + }, + "sidebar_apps": { + "title": "Aplikacje paska bocznego", + "description": "Zarządzaj niestandardowymi aplikacjami i linkami na pasku bocznym", + "keep_loaded": "Pozostaw aplikacje załadowane", + "keep_loaded_description": "Utrzymuj aplikacje wbudowane uruchomione w tle podczas przełączania się między nimi, aby uniknąć ponownego ładowania", + "managed_title": "Udostępnione przez administratora", + "managed_description": "Te aplikacje są skonfigurowane dla wszystkich i nie można ich zmienić.", + "manage_title": "Niestandardowe aplikacje", + "manage_description": "Dodawaj, edytuj lub usuwaj niestandardowe aplikacje z paska bocznego" + }, + "contacts": { + "title": "Kontakty", + "description": "Importuj i eksportuj kontakty", + "import_label": "Importuj kontakty", + "import_description": "Importuj kontakty z pliku vCard (.vcf)", + "export_label": "Eksportuj kontakty", + "export_description": "Eksportuj wszystkie kontakty jako plik vCard (.vcf)", + "manage_title": "Książki adresowe", + "manage_description": "Zmień nazwy swoich książek adresowych", + "no_address_books": "Nie znaleziono książek adresowych", + "categories_title": "Kategorie", + "categories_description": "Zmień nazwy kategorii kontaktów", + "no_categories": "Nie znaleziono kategorii", + "group_by_letter_description": "Pokaż alfabetyczne nagłówki sekcji na liście kontaktów", + "group_by_letter_label": "Grupuj według pierwszej litery", + "sort_by_last_name_label": "Sortuj według nazwiska", + "sort_by_last_name_description": "Uporządkuj listę kontaktów według nazwiska, aby członkowie rodziny byli obok siebie" + }, + "downloads": { + "title": "Pobrane", + "description": "Dostosuj sposób nazywania pobranych wiadomości i załączników.", + "reset": "Przywróć domyślne", + "preview": "Podgląd:", + "email_template": { + "label": "Nazwa pliku wiadomości (.eml)", + "description": "Szablon używany przy eksporcie wiadomości lub jej przeciąganiu do systemu plików. Rozszerzenie .eml jest dodawane automatycznie." + }, + "attachment_template": { + "label": "Nazwa pliku załącznika", + "description": "Szablon używany przy pobieraniu lub przeciąganiu załącznika. Jeśli pominiesz '{filename}' i '{ext}', oryginalne rozszerzenie zostanie zachowane." + }, + "bundle_template": { + "label": "Nazwa pliku .zip wielu wiadomości", + "description": "Szablon używany przy przeciąganiu lub pobieraniu kilku wybranych wiadomości jako jednego archiwum .zip. Rozszerzenie .zip jest dodawane automatycznie." + }, + "spaces": { + "label": "Spacje", + "description": "Zastąp spacje w nazwie pliku innym znakiem.", + "keep": "Zachowaj spacje", + "underscore": "Zamień na _", + "dash": "Zamień na -" + }, + "lowercase": { + "label": "Małe litery", + "description": "Wymuś małe litery w całej nazwie pliku." + }, + "strip_diacritics": { + "label": "Usuń znaki diakrytyczne", + "description": "Zamień litery z akcentami na ich odpowiedniki ASCII (ą → a, é → e). Przydatne dla narzędzi, które źle obsługują nazwy Unicode." + }, + "collapse_separators": { + "label": "Łącz powtórzone separatory", + "description": "Połącz ciągi spacji, podkreśleń lub myślników w jeden znak." + }, + "after_export": { + "label": "Po eksporcie", + "description": "Opcjonalnie przenieś wiadomość po wyeksportowaniu jako .eml.", + "keep": "Zachowaj w skrzynce", + "archive": "Przenieś do archiwum", + "trash": "Przenieś do kosza" + } + }, + "filters": { + "title": "Filtry wiadomości e-mail", + "description": "Twórz reguły, aby automatycznie sortować, etykietować i zarządzać przychodzącymi wiadomościami e-mail", + "add_rule": "Dodaj regułę", + "no_rules": "Brak reguł filtrów", + "no_rules_description": "Twórz reguły, aby automatycznie organizować przychodzące wiadomości e-mail", + "vacation_active": "Autoresponder urlopowy jest aktywny", + "vacation_active_description": "Automatyczna odpowiedź jest włączona dla wiadomości przychodzących", + "vacation_configure": "Konfiguruj", + "edit_rule": "Edytuj regułę", + "new_rule": "Nowa reguła", + "delete_rule": "Usuń regułę", + "delete_confirm": "Czy na pewno chcesz usunąć tę regułę?", + "enable": "Włącz", + "disable": "Wyłącz", + "raw_editor": "Surowy edytor Sieve", + "raw_editor_warning": "Edytowanie surowego skryptu Sieve może zepsuć wizualną edycję reguł. Zmiany wprowadzone tutaj zastępują kreator wizualny.", + "validate": "Waliduj", + "validation_success": "Skrypt jest poprawny", + "validation_error": "Skrypt zawiera błędy", + "save": "Zapisz reguły", + "saving": "Zapisywanie...", + "saved": "Filtry zostały zapisane", + "save_failed": "Nie udało się zapisać filtrów", + "loading": "Ładowanie filtrów...", + "not_supported": "Twój serwer pocztowy nie obsługuje filtrów wiadomości e-mail.", + "rule_name": "Nazwa reguły", + "rule_name_placeholder": "np. Sortuj newslettery", + "match_all": "Dopasuj WSZYSTKIE warunki", + "match_any": "Dopasuj DOWOLNY warunek", + "conditions": "Warunki", + "add_condition": "Dodaj warunek", + "actions": "Akcje", + "add_action": "Dodaj akcję", + "stop_processing": "Zatrzymaj przetwarzanie kolejnych reguł", + "include_spam": "Przenoś również wiadomości oznaczone jako spam", + "attachment_type_placeholder": "np. pdf, doc, jpg", + "value_placeholder_multi": "Wartość (kilka oddzielonych przecinkami)", + "condition_fields": { + "attachment": "Załącznik", + "from": "Od", + "to": "Do", + "cc": "DW", + "subject": "Temat", + "header": "Niestandardowy nagłówek", + "size": "Rozmiar", + "body": "Treść", + "all": "Wszystkie wiadomości" + }, + "comparators": { + "has_any": "jest obecny", + "has_type": "typu", + "contains": "zawiera", + "not_contains": "nie zawiera", + "is": "jest dokładnie", + "not_is": "nie jest", + "starts_with": "zaczyna się od", + "ends_with": "kończy się na", + "matches": "pasuje do wzorca", + "greater_than": "jest większe niż", + "less_than": "jest mniejsze niż", + "address_is": "to adres", + "domain_is": "ma domenę" + }, + "action_types": { + "move": "Przenieś do folderu", + "copy": "Kopiuj do folderu", + "forward": "Prześlij dalej do", + "mark_read": "Oznacz jako przeczytane", + "star": "Oznacz wiadomość gwiazdką", + "add_label": "Dodaj etykietę", + "discard": "Odrzuć (usuń po cichu)", + "reject": "Odrzuć z wiadomością", + "keep": "Pozostaw w Odebranych", + "stop": "Zatrzymaj przetwarzanie" + }, + "move_to_folder": "Wybierz folder", + "copy_to_folder": "Wybierz folder", + "forward_to": "Prześlij dalej na adres e-mail", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Zachowaj kopię", + "forward_limit": "Limit przekazań na wiadomość na tym serwerze: {count}. Dodatkowe przekazania są pomijane.", + "forward_unavailable": "Ten serwer nie pozwala na przekazywanie. Akcje przekazania w regułach filtrów nie działają.", + "reject_message": "Wiadomość odrzucenia", + "reject_placeholder": "Twoja wiadomość e-mail została odrzucona", + "label_name": "Nazwa etykiety", + "label_placeholder": "Wybierz etykietę", + "header_name": "Nazwa nagłówka", + "header_placeholder": "np. X-Mailing-List", + "size_bytes": "Rozmiar w bajtach", + "size_placeholder": "np. 1000000", + "system_managed": "Reguła zarządzana przez system", + "opaque_warning": "Ten skrypt został edytowany poza kreatorem wizualnym. Dostępna jest tylko surowa edycja Sieve.", + "open_sieve_editor": "Otwórz surowy edytor Sieve", + "fetch_error": "Nie udało się załadować filtrów", + "expanded_view": "Widok rozszerzony", + "expanded_view_description": "Pokazuj reguły filtrów ze szczegółowymi blokami warunków i akcji", + "if": "Jeśli", + "then": "Wtedy", + "match_all_conditions": "wszystkie pasują", + "match_any_condition": "dowolne pasują", + "and": "i", + "or": "lub", + "cancel": "Anuluj", + "confirm_delete": "Usuń", + "rule_list": "Reguły filtrów", + "drag_to_reorder": "Przeciągnij, aby zmienić kolejność", + "match_type": "Typ dopasowania", + "reset_to_visual": "Resetuj do kreatora wizualnego", + "reset_warning": "To odrzuci bieżący skrypt i pozwoli zacząć od nowa.", + "confirm_reset": "Resetuj", + "validation_empty_name": "Nazwa reguły jest wymagana", + "validation_empty_conditions": "Wymagany jest co najmniej jeden warunek z wartością", + "validation_empty_actions": "Wymagana jest co najmniej jedna akcja", + "templates_section": "Rozpocznij od szablonu", + "template_newsletters": "Przenieś newslettery do folderu", + "template_receipts": "Automatycznie archiwizuj rachunki", + "template_important": "Oznacz ważne wiadomości e-mail", + "template_notifications": "Filtruj powiadomienia", + "sieve_editor": { + "title": "Edytor skryptu Sieve", + "warning": "Edytowanie surowego skryptu Sieve może zepsuć wizualną edycję reguł. Zmiany wprowadzone tutaj zastępują kreator wizualny.", + "script_content": "Skrypt Sieve", + "valid": "Skrypt jest poprawny", + "invalid": "Skrypt zawiera błędy", + "save_warning": "Zapisanie nadpisze wszystkie reguły wizualne. Nie można tego cofnąć. Kliknij Zapisz ponownie, aby potwierdzić.", + "validating": "Walidowanie...", + "validate": "Waliduj", + "cancel": "Anuluj", + "save": "Zapisz", + "confirm_save": "Potwierdź zapis", + "validation_failed": "Żądanie walidacji nie powiodło się" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# warunek} other {# warunków}}", + "actions_count": "{count, plural, one {# akcja} other {# akcji}}" + }, + "origin_external": "Zewnętrzny", + "managed_by_tooltip": "Zarządzane przez {source}. Edytuj w tej aplikacji lub użyj surowego edytora Sieve.", + "quick_rule_names": { + "move_sender": "Przenoś od {sender} do {folder}", + "move_domain": "Przenoś od @{domain} do {folder}", + "move_list": "Przenoś listę {list} do {folder}", + "mark_read": "Oznaczaj od {sender} jako przeczytane", + "tag": "Oznaczaj od {sender} etykietą {tag}", + "block": "Blokuj {sender}", + "custom": "Wiadomości od {sender}", + "custom_plain": "Nowa reguła", + "senders": "{count, plural, one {# nadawcy} few {# nadawców} many {# nadawców} other {# nadawcy}}" + }, + "suggestions": "Sugestie", + "suggest_subject": "Temat „{subject}”", + "suggest_to": "Do {address}", + "suggest_cc": "DW {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Zastosuj też do wiadomości, które już są w tym folderze", + "apply_existing_unsupported": "Do istniejących wiadomości można zastosować tylko reguły, które sprawdzają nadawcę, odbiorców, temat lub nagłówki i które przenoszą, kopiują, oznaczają jako przeczytane, oznaczają gwiazdką lub etykietą.", + "period_title": "Zakres dat", + "period_start": "Data rozpoczęcia", + "period_end": "Data zakończenia", + "validation_period_order": "Data zakończenia musi być późniejsza niż data rozpoczęcia", + "period_toggle": "Aktywna tylko w wybranym okresie", + "period_hint": "Dotyczy wiadomości, które przychodzą w tym okresie. Pozostaw pole puste, aby nie ograniczać tej strony.", + "validation_period_empty": "Podaj datę rozpoczęcia lub zakończenia okresu", + "validation_period_invalid": "Podaj prawidłową datę z godziną dla okresu albo pozostaw pole puste", + "apply_existing_period": "Reguła z okresem działa tylko na wiadomości, które przychodzą w tym okresie, dlatego nie można jej zastosować do istniejących wiadomości.", + "period_status_scheduled": "zaplanowana", + "period_status_active": "aktywna", + "period_status_expired": "wygasła" + }, + "templates": { + "title": "Szablony wiadomości e-mail", + "description": "Twórz wielokrotnego użytku szablony wiadomości e-mail ze zmiennymi zastępczymi", + "add": "Nowy szablon", + "edit": "Edytuj szablon", + "name": "Nazwa szablonu", + "name_placeholder": "np. Wiadomość kontrolna", + "category": "Kategoria", + "category_placeholder": "np. Praca, Osobiste", + "subject": "Temat", + "subject_placeholder": "Temat wiadomości e-mail", + "body": "Treść", + "body_placeholder": "Treść wiadomości e-mail...", + "recipients_placeholder": "email@example.com", + "identity": "Wyślij jako", + "default_identity": "Domyślna tożsamość", + "favorite": "Ulubione", + "cancel": "Anuluj", + "create": "Utwórz", + "update": "Aktualizuj", + "confirm_delete": "Usuń", + "no_templates": "Brak szablonów", + "manage": "Zarządzaj szablonami", + "count": "{count, plural, one {# szablon} other {# szablonów}}", + "export_import": "Eksport i import", + "export_import_description": "Twórz kopie zapasowe szablonów lub przenoś je na inne urządzenie", + "export": "Eksportuj", + "import": "Importuj", + "validation": { + "empty": "Nazwa szablonu jest wymagana", + "too_long": "Nazwa szablonu musi mieć maksymalnie 200 znaków" + } + }, + "files": { + "display": { + "title": "Wyświetlanie", + "description": "Skonfiguruj sposób wyświetlania plików i folderów" + }, + "default_view": { + "label": "Domyślny widok", + "description": "Wybierz układ siatki lub listy", + "list": "Lista", + "grid": "Siatka" + }, + "default_sort": { + "label": "Domyślne sortowanie", + "description": "Wybierz domyślne sortowanie plików", + "name": "Nazwa", + "size": "Rozmiar", + "modified": "Zmodyfikowano" + }, + "sort_direction": { + "label": "Kierunek sortowania", + "description": "Wybierz kolejność rosnącą lub malejącą", + "ascending": "Rosnąco", + "descending": "Malejąco" + }, + "icons": { + "title": "Ikony", + "description": "Skonfiguruj wygląd ikon plików" + }, + "show_icons": { + "label": "Pokazuj ikony plików", + "description": "Wyświetlaj ikony obok plików i folderów" + }, + "colored_icons": { + "label": "Kolorowe ikony", + "description": "Używaj kolorowych ikon zamiast monochromatycznych" + }, + "show_thumbnails": { + "label": "Pokazuj miniatury", + "description": "Wyświetlaj podglądy obrazów zamiast ikon dla plików graficznych" + }, + "behavior": { + "title": "Zachowanie", + "description": "Skonfiguruj działanie przeglądarki plików" + }, + "show_hidden": { + "label": "Pokazuj ukryte pliki", + "description": "Wyświetlaj pliki i foldery zaczynające się od kropki" + }, + "preview": { + "label": "Podgląd" + } + }, + "search_placeholder": "Szukaj ustawień", + "search_clear": "Wyczyść wyszukiwanie", + "search_no_results": "Brak pasujących ustawień", + "scoped": { + "back": "Powrót do mojego konta", + "managing": "Zarządzanie: {name}" + } + }, + "errors": { + "page_error_title": "Coś poszło nie tak", + "page_error_description": "Wystąpił nieoczekiwany błąd. Spróbuj ponownie lub wróć do strony głównej.", + "sidebar_error": "Nie udało się załadować skrzynek pocztowych", + "email_list_error": "Nie udało się załadować wiadomości", + "viewer_error_title": "Nie można wyświetlić wiadomości", + "viewer_error_description": "Wystąpił problem podczas renderowania tej wiadomości. Może zawierać nieobsługiwaną zawartość.", + "composer_error": "Nie udało się załadować edytora wiadomości", + "settings_error_title": "Ustawienia są niedostępne", + "settings_error_description": "Nie udało się załadować ustawień. Twoje preferencje mogą nie zostać zapisane.", + "try_again": "Spróbuj ponownie", + "reload": "Odśwież", + "reload_emails": "Odśwież wiadomości", + "reload_settings": "Odśwież ustawienia", + "retry": "Ponów", + "go_home": "Przejdź do skrzynki odbiorczej" + }, + "context_menu": { + "reply": "Odpowiedz", + "reply_all": "Odpowiedz wszystkim", + "forward": "Prześlij dalej", + "mark_read": "Oznacz jako przeczytane", + "mark_unread": "Oznacz jako nieprzeczytane", + "star": "Oznacz gwiazdką", + "unstar": "Usuń gwiazdkę", + "pin": "Przypnij", + "unpin": "Odepnij", + "move_to": "Przenieś do...", + "copy_to_account": "Kopiuj do konta...", + "remove_from_folder": "Usuń z tego folderu", + "archive": "Archiwizuj", + "delete": "Usuń", + "mark_as_spam": "Zgłoś spam", + "not_spam": "To nie spam", + "tag": "Etykieta", + "items_selected": "{count} zaznaczonych wiadomości", + "forward_as_attachments": "Prześlij dalej jako załączniki", + "edit_draft": "Edytuj szkic", + "cancel_scheduled_send": "Anuluj wysyłkę", + "reschedule_send": "Przełóż", + "cancel_and_edit": "Anuluj i edytuj", + "cancel_and_compose_again": "Anuluj i napisz ponownie", + "rules": { + "title": "Reguły", + "move_from": "Zawsze przenoś wiadomości od {sender}", + "move_from_domain": "Zawsze przenoś wiadomości od @{domain}", + "move_from_list": "Zawsze przenoś wiadomości z tej listy", + "mark_read_from": "Zawsze oznaczaj wiadomości od {sender} jako przeczytane", + "tag_with": "Zawsze oznaczaj etykietą", + "block": "{count, plural, =1 {Zablokuj nadawcę} one {Zablokuj # nadawcę} few {Zablokuj # nadawców} many {Zablokuj # nadawców} other {Zablokuj # nadawcy}}", + "no_junk": "To konto nie ma folderu spamu", + "create": "Utwórz regułę…", + "manage": "Zarządzaj regułami", + "senders_count": "{count, plural, one {# nadawcy} few {# nadawców} many {# nadawców} other {# nadawcy}}", + "cross_account": "Wybierz wiadomości z jednego konta", + "opaque_hint": "Twoje filtry zostały edytowane ręcznie. Otwórz ustawienia filtrów" , + "loading": "Wczytywanie reguł…", + "load_failed": "Nie udało się wczytać reguł", + "none": "Brak reguł", + "new_mail_only": "Tylko dla nowych wiadomości", + "running": "Uruchamianie „{name}” w folderze {folder}…" + }, + "find_emails_from": "Znajdź e-maile od {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Oznacz folder jako przeczytany", + "mark_folder_tree_read": "Oznacz folder i podfoldery jako przeczytane", + "mark_all_folders_read": "Oznacz wszystkie foldery jako przeczytane", + "new_subfolder": "Nowy podfolder...", + "new_folder": "Nowy folder...", + "rename": "Zmień nazwę...", + "share": "Udostępnij...", + "import_email": "Importuj .eml lub .zip...", + "empty_folder": "Opróżnij folder", + "empty_folder_generic": "Opróżnij folder", + "delete_folder": "Usuń folder", + "refresh": "Odśwież", + "hide_shared_account": "Ukryj na pasku bocznym", + "mute_notifications": "Wycisz powiadomienia", + "unmute_notifications": "Włącz powiadomienia", + "mute_account_notifications": "Wycisz powiadomienia tego konta", + "unmute_account_notifications": "Włącz powiadomienia tego konta", + "reset_folder_notifications": "Resetuj powiadomienia folderów", + "mark_all_confirm_title": "Oznacz wszystkie foldery jako przeczytane", + "mark_all_confirm_message": "Oznaczyć wszystkie nieprzeczytane wiadomości na koncie osobistym jako przeczytane?", + "delete_confirm_title": "Usuń folder", + "delete_confirm_message": "Trwale usunąć folder \"{name}\"? Tej operacji nie można cofnąć.", + "prompt_new_subfolder": "Podaj nazwę nowego podfolderu.", + "prompt_new_folder": "Podaj nazwę nowego folderu.", + "prompt_rename": "Podaj nową nazwę tego folderu.", + "toast_marked_read": "Folder oznaczony jako przeczytany", + "toast_marked_read_count": "Oznaczono {count, plural, one {1 wiadomość} other {# wiadomości}} jako przeczytane", + "toast_already_read": "Brak nieprzeczytanych wiadomości", + "toast_marked_all_read": "Wszystkie foldery oznaczone jako przeczytane", + "toast_emptied": "Folder opróżniony", + "toast_folder_created": "Folder utworzony", + "toast_folder_renamed": "Nazwa folderu zmieniona", + "toast_folder_deleted": "Folder usunięty", + "toast_error_mark_read": "Nie udało się oznaczyć jako przeczytane", + "toast_error_empty": "Nie udało się opróżnić folderu", + "toast_error_create": "Nie udało się utworzyć folderu", + "toast_error_rename": "Nie udało się zmienić nazwy folderu", + "toast_error_delete": "Nie udało się usunąć folderu", + "toast_error_delete_has_children": "Folder zawiera podfoldery. Najpierw je usuń.", + "toast_error_delete_has_email": "Folder nie jest pusty. Najpierw go opróżnij.", + "delete_with_contents_title": "Folder nie jest pusty", + "delete_with_contents_message": "Folder „{name}” nadal zawiera {count, plural, one {1 wiadomość} few {# wiadomości} other {# wiadomości}}. Usunąć folder wraz ze wszystkimi wiadomościami? Tej operacji nie można cofnąć.", + "delete_with_contents_confirm": "Usuń folder i wiadomości", + "placeholder_folder_name": "Nazwa folderu", + "create": "Utwórz", + "rename_confirm": "Zmień nazwę" + }, + "shortcuts": { + "title": "Skróty klawiszowe", + "tip": "Naciśnij ? w dowolnym momencie, aby wyświetlić tę pomoc", + "sections": { + "navigation": "Nawigacja", + "actions": "Akcje wiadomości", + "global": "Globalne", + "threads": "Wątki", + "composer": "Edytor wiadomości" + }, + "navigation": { + "next_email": "Następna wiadomość", + "previous_email": "Poprzednia wiadomość", + "open_email": "Otwórz wiadomość", + "close_email": "Zamknij / Odznacz", + "go_to_folder": "Przejdź do folderu", + "next_unread": "Następna nieprzeczytana wiadomość", + "no_more_unread": "Niżej na liście nie ma już nieprzeczytanych wiadomości" + }, + "actions": { + "reply": "Odpowiedz", + "reply_all": "Odpowiedz wszystkim", + "forward": "Prześlij dalej", + "star": "Przełącz gwiazdkę", + "archive": "Archiwizuj", + "delete": "Usuń", + "mark_unread": "Oznacz jako nieprzeczytane", + "mark_read": "Oznacz jako przeczytane", + "toggle_spam": "Zgłoś spam / To nie spam" + }, + "global": { + "compose": "Napisz nową wiadomość", + "search": "Przejdź do wyszukiwania", + "help": "Pokaż skróty", + "refresh": "Odśwież wiadomości", + "select_all": "Zaznacz wszystko", + "global_search": "Szukaj wszędzie", + "extend_selection": "Rozszerz zaznaczenie w dół / w górę" + }, + "threads": { + "expand_collapse": "Rozwiń/zwiń wątek", + "select_mountain_view": "Zaznacz konwersację" + }, + "composer": { + "send": "Wyślij e-mail", + "schedule_send": "Zaplanuj wysyłkę", + "template_picker": "Otwórz wybór szablonu" + } + }, + "threads": { + "messages_one": "{count} wiadomość", + "messages_other": "{count} wiadomości", + "messages_tooltip": "{count, plural, one {# wiadomość w tej konwersacji} other {# wiadomości w tej konwersacji}}", + "expand": "Rozwiń konwersację", + "collapse": "Zwiń konwersację", + "loading": "Ładowanie konwersacji...", + "mark_read": "Oznacz konwersację jako przeczytaną", + "mark_unread": "Oznacz konwersację jako nieprzeczytaną", + "archive": "Archiwizuj konwersację", + "delete": "Usuń konwersację", + "star": "Oznacz konwersację gwiazdką", + "unstar": "Usuń gwiazdkę z konwersacji", + "toggle_thread": "Przełącz widok wątku" + }, + "identities": { + "modal_title": "Zarządzaj tożsamościami nadawcy", + "create_new": "Utwórz nową tożsamość", + "edit_identity": "Edytuj tożsamość", + "delete_confirm": "Usunąć tę tożsamość? Tej operacji nie można cofnąć.", + "cannot_delete": "Tej tożsamości nie można usunąć", + "primary_identity": "Główna", + "set_as_primary": "Ustaw jako główną", + "no_identities": "Nie znaleziono tożsamości", + "display": { + "reply_to": "Odpowiedz-do:", + "bcc": "UDW:", + "signature": "Podpis:", + "preview": "Podgląd:" + }, + "validation_errors": { + "invalid_emails": "Nieprawidłowe adresy e-mail: {emails}", + "unknown_error": "Nieznany błąd" + }, + "form": { + "name_label": "Nazwa wyświetlana", + "name_placeholder": "np. Służbowy e-mail, Prywatny", + "name_required": "Nazwa jest wymagana", + "email_label": "Adres e-mail", + "email_placeholder": "your.email@example.com", + "email_required": "Adres e-mail jest wymagany", + "email_invalid": "Wprowadź prawidłowy adres e-mail", + "email_immutable": "Adresu e-mail nie można zmienić po utworzeniu", + "reply_to_label": "Odpowiedz-do (opcjonalnie)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "Automatyczne UDW (opcjonalnie)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Podpis tekstowy", + "html_signature_label": "Podpis HTML", + "signature_byte_counter": "{bytes} / {max} bajtów", + "signature_byte_limit_reached": "Osiągnięto limit serwera", + "save": "Zapisz tożsamość", + "cancel": "Anuluj", + "creating": "Tworzenie...", + "updating": "Aktualizowanie..." + }, + "sub_address": { + "button_tooltip": "Użyj podadresu", + "popover_title": "Dodaj tag podadresu", + "tag_input_placeholder": "Wpisz tag (np. zakupy)", + "preview_label": "Podgląd:", + "recent_tags": "Ostatnie tagi", + "suggested_tags": "Sugerowane", + "use_address": "Użyj tego adresu", + "invalid_tag": "Tag może zawierać tylko litery, cyfry i myślniki", + "tag_too_long": "Tag może mieć maksymalnie 30 znaków", + "help_text": "Wiadomości wysłane na adres user{delimiter}tag@domain.com trafią do Twojej skrzynki odbiorczej", + "validation": { + "empty": "Tag nie może być pusty", + "too_long": "Tag może mieć maksymalnie {max} znaków", + "invalid_chars": "Tag może zawierać tylko litery, cyfry i myślniki" + } + }, + "badge": { + "sent_via": "przez", + "sub_address_tag": "Wysłano przy użyciu podadresu: {tag}", + "identity_name": "Wysłano przy użyciu tożsamości: {name}", + "identity_short": "przez {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Usuń", + "delete_confirm_title": "Usuń tożsamość" + }, + "templates": { + "picker_title": "Wybierz szablon", + "search_placeholder": "Szukaj szablonów...", + "section_favorites": "Ulubione", + "section_recent": "Ostatnie", + "section_uncategorized": "Inne", + "no_templates": "Nie ma jeszcze szablonów", + "no_results": "Nie znaleziono szablonów", + "fill_placeholders": "Uzupełnij wartości zmiennych", + "enter_value": "Wpisz wartość...", + "preview": "Podgląd", + "insert_with_values": "Wstaw z wartościami", + "insert_raw": "Wstaw bez zmian", + "copy_suffix": "(kopia)", + "placeholder": "Zmienna", + "placeholders": { + "recipient_name": "Nazwa odbiorcy", + "company": "Nazwa firmy", + "date": "Bieżąca data", + "day_of_week": "Dzień tygodnia", + "sender_name": "Twoje imię" + } + }, + "contacts": { + "directory": { + "title": "Katalog", + "search_placeholder": "Szukaj nazwy lub adresu e-mail", + "refresh": "Odśwież katalog", + "retry": "Spróbuj ponownie", + "loading": "Wczytywanie katalogu…", + "unavailable_title": "Brak katalogu", + "unavailable_hint": "Twój serwer poczty nie udostępnia katalogu kont.", + "forbidden_title": "Katalog niedostępny", + "forbidden_hint": "Twój serwer poczty nie pozwala temu kontu przeglądać katalogu. Zapytaj administratora.", + "error_title": "Nie udało się wczytać katalogu", + "error_hint": "Sprawdź połączenie i spróbuj ponownie.", + "empty_title": "Katalog jest pusty", + "empty_hint": "Twój serwer poczty nie wymienia żadnych kont z adresem e-mail.", + "no_match": "Nikt w katalogu nie pasuje do wyszukiwania", + "refresh_failed": "Nie udało się odświeżyć katalogu. Wyświetlana jest ostatnio wczytana lista.", + "select_page": "Zaznacz wszystkich na tej stronie", + "deselect_page": "Odznacz wszystkich na tej stronie", + "select_entry": "Zaznacz {name}", + "range": "{from}–{to} z {total}", + "page": "Strona {page} z {count}", + "previous_page": "Poprzednia strona", + "next_page": "Następna strona", + "picker_title": "Wybierz z katalogu", + "cancel": "Anuluj", + "selected_count": "Zaznaczono: {count}", + "add_to": "Dodaj do Do", + "add_cc": "Dodaj do DW", + "add_bcc": "Dodaj do UDW", + "select_hint": "Wybierz osobę, aby zobaczyć szczegóły", + "write_email": "Napisz e-mail", + "add_to_contacts": "Dodaj do kontaktów", + "email": "E-mail", + "read_only_note": "Wpisy katalogu pochodzą z Twojego serwera poczty i nie można ich tu edytować.", + "type": { + "individual": "Osoba", + "group": "Grupa", + "resource": "Zasób", + "location": "Lokalizacja", + "other": "Inne" + } + }, + "title": "Kontakty", + "search_placeholder": "Szukaj kontaktów...", + "create_new": "Nowy kontakt", + "no_category": "Brak kategorii", + "rename_category": "Zmień nazwę kategorii", + "category_name_label": "Nazwa kategorii", + "category_renamed": "Zmieniono nazwę kategorii", + "category_rename_failed": "Nie udało się zmienić nazwy kategorii", + "category_added": "Kontakt dodany do {name}", + "category_added_plural": "{count} kontaktów dodano do {name}", + "empty_state": "Brak kontaktów", + "empty_state_title": "Brak kontaktów", + "empty_state_subtitle": "Utwórz pierwszy kontakt lub zaimportuj z pliku vCard", + "empty_search": "Żadne kontakty nie pasują do wyszukiwania", + "empty_search_hint": "Spróbuj użyć innego wyszukiwanego hasła", + "empty_filtered": "Żadne kontakty nie pasują do filtrów", + "empty_filtered_hint": "Dostosuj lub wyczyść filtry", + "clear_search": "Wyczyść wyszukiwanie", + "import_vcard": "Importuj vCard", + "delete_confirm_title": "Usuń kontakt", + "delete_confirm": "Czy na pewno chcesz usunąć ten kontakt?", + "local_mode": "Kontakty są przechowywane lokalnie (serwer nie obsługuje JMAP Contacts)", + "back_to_contacts": "Powrót do kontaktów", + "back_to_email": "Powrót do wiadomości", + "tabs": { + "all": "Wszystkie", + "groups": "Grupy" + }, + "shared": { + "title": "Udostępnione" + }, + "address_books": { + "create": "Nowa książka adresowa", + "created": "Utworzono książkę adresową", + "create_failed": "Nie udało się utworzyć książki adresowej", + "title": "Moje książki adresowe", + "shared_prefix": "Udostępnione: {name}", + "moved": "Kontakt przeniesiono do {name}", + "moved_plural": "{count} kontaktów przeniesiono do {name}", + "move_failed": "Nie udało się przenieść kontaktu", + "address_book": "Książka adresowa", + "rename": "Zmień nazwę książki adresowej", + "name_label": "Nazwa książki adresowej", + "renamed": "Zmieniono nazwę książki adresowej", + "rename_failed": "Nie udało się zmienić nazwy książki adresowej", + "default": "Domyślna", + "set_default": "Ustaw jako domyślną", + "default_updated": "Zaktualizowano domyślną książkę adresową", + "set_default_failed": "Nie udało się ustawić domyślnej książki adresowej", + "manage": "Zarządzaj książkami adresowymi", + "share": "Udostępnij książkę adresową", + "new_contact_in_book": "Nowy kontakt w tej książce adresowej", + "delete": "Usuń książkę adresową", + "confirm_delete": "Usunąć „{name}\"? Wszystkie kontakty w tej książce adresowej zostaną usunięte.", + "deleted": "Książka adresowa usunięta", + "delete_failed": "Nie udało się usunąć książki adresowej" + }, + "detail": { + "emails": "Adresy e-mail", + "phones": "Numery telefonów", + "organizations": "Organizacje", + "addresses": "Adresy", + "notes": "Notatki", + "titles": "Stanowiska i role", + "online_services": "Usługi online", + "anniversaries": "Rocznice", + "personal_info": "Informacje osobiste", + "languages": "Języki", + "categories": "Kategorie", + "related_contacts": "Powiązane kontakty", + "crypto_keys": "Klucze kryptograficzne", + "cert_issuer": "Wystawca", + "cert_expires": "Wygasa", + "cert_expired": "Wygasł", + "cert_fingerprint": "Odcisk", + "cert_algorithm": "Algorytm", + "import_to_smime": "Importuj do S/MIME", + "cert_already_imported": "Już zaimportowano do S/MIME", + "cert_imported": "Certyfikat zaimportowano do magazynu S/MIME", + "cert_import_failed": "Nie udało się zaimportować certyfikatu", + "no_contact_selected": "Wybierz kontakt, aby wyświetlić szczegóły", + "compose_email": "Napisz e-mail", + "copy_email": "Kopiuj e-mail", + "copy_phone": "Kopiuj numer telefonu", + "copy_url": "Kopiuj URL", + "copied": "Skopiowano do schowka", + "copy_failed": "Nie udało się skopiować do schowka", + "created": "Utworzono", + "updated": "Ostatnia aktualizacja", + "timezone": "Strefa czasowa", + "anniversary_birth": "Urodziny", + "anniversary_death": "Data śmierci", + "anniversary_wedding": "Rocznica", + "anniversary_other": "Inne", + "personal_expertise": "Specjalizacja", + "personal_hobby": "Hobby", + "personal_interest": "Zainteresowanie", + "personal_other": "Inne", + "gender": "Płeć", + "gender_masculine": "Mężczyzna", + "gender_feminine": "Kobieta", + "gender_other": "Inne", + "gender_none": "Nie dotyczy", + "gender_unknown": "Nieznana", + "calendar": "Kalendarz", + "calendar_uri": "Adres URL kalendarza", + "scheduling_uri": "Adres URL planowania", + "freebusy_uri": "Adres URL wolny/zajęty", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Więcej działań", + "age_years": "{count, plural, one {1 rok} few {# lata} many {# lat} other {# lat}}", + "years_since": "{count, plural, one {1 rok temu} few {# lata temu} many {# lat temu} other {# lat temu}}" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "Nowy kontakt", + "edit_title": "Edytuj kontakt", + "section_address_book": "Katalog", + "select_address_book": "Wybierz katalog...", + "section_identity": "Imię i tożsamość", + "contact_type": "Typ kontaktu", + "type_person": "Osoba", + "type_organization": "Organizacja", + "section_work": "Praca i organizacja", + "prefix": "Tytuł", + "prefix_placeholder": "Dr, Pan, Pani", + "given_name": "Imię", + "middle_name": "Drugie imię", + "surname": "Nazwisko", + "suffix": "Sufiks", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Pseudonim", + "nickname_placeholder": "Pseudonim", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Telefon", + "phone_placeholder": "+1 234 567 890", + "phone_type": "Typ", + "phone_voice": "Głosowy", + "phone_cell": "Komórkowy", + "phone_fax": "Faks", + "phone_pager": "Pager", + "phone_video": "Wideo", + "phone_text": "SMS", + "organization": "Organizacja", + "organization_placeholder": "Nazwa firmy", + "department": "Dział", + "department_placeholder": "Dział", + "job_title": "Stanowisko", + "job_title_placeholder": "np. Inżynier oprogramowania", + "role": "Rola", + "role_placeholder": "np. Lider zespołu", + "addresses": "Adresy", + "add_address": "Dodaj adres", + "street": "Ulica", + "city": "Miasto", + "region": "Stan / region", + "postcode": "Kod pocztowy", + "country": "Kraj", + "online_services": "Usługi online", + "add_online_service": "Dodaj usługę online", + "url_placeholder": "https://...", + "service_placeholder": "Usługa", + "anniversaries": "Rocznice", + "add_anniversary": "Dodaj datę", + "anniversary_birth": "Urodziny", + "anniversary_wedding": "Rocznica", + "anniversary_death": "Upamiętnienie", + "anniversary_other": "Inne", + "personal_info": "Informacje osobiste", + "add_personal_info": "Dodaj wpis", + "personal_info_placeholder": "np. Fotografia", + "personal_expertise": "Specjalizacja", + "personal_hobby": "Hobby", + "personal_interest": "Zainteresowanie", + "personal_other": "Inne", + "level": "Poziom", + "level_high": "Wysoki", + "level_medium": "Średni", + "level_low": "Niski", + "categories": "Kategorie", + "categories_placeholder": "np. Rodzina, Przyjaciele, Współpracownicy", + "categories_hint": "Wpisz, aby wyszukać lub dodać kategorie", + "category_add": "Dodaj", + "note": "Notatki", + "note_placeholder": "Dodaj notatkę...", + "gender": "Płeć", + "gender_sex": "Płeć biologiczna", + "gender_male": "Mężczyzna", + "gender_female": "Kobieta", + "gender_other": "Inne", + "gender_none": "Nie dotyczy", + "gender_unknown": "Nieznana", + "gender_identity": "Tożsamość płciowa", + "gender_identity_placeholder": "Tożsamość płciowa...", + "calendar": "Kalendarz", + "calendar_uri": "Adres URL kalendarza", + "scheduling_uri": "Adres URL planowania", + "freebusy_uri": "Adres URL wolny/zajęty", + "context_work": "Praca", + "context_private": "Prywatny", + "add_email": "Dodaj e-mail", + "add_phone": "Dodaj telefon", + "save": "Zapisz", + "cancel": "Anuluj", + "creating": "Tworzenie...", + "updating": "Aktualizowanie...", + "name_required": "Podaj imię, nazwisko lub organizację", + "email_invalid": "Wprowadź prawidłowy adres e-mail", + "email_error_inline": "Nieprawidłowy format adresu e-mail", + "save_failed": "Nie udało się zapisać kontaktu", + "delete": "Usuń", + "upload_photo": "Prześlij zdjęcie", + "remove_photo": "Usuń zdjęcie", + "photo_hint": "JPG lub PNG, do 10 MB. Zostanie zmniejszone.", + "photo_too_large": "Obraz jest za duży (maks. 10 MB)", + "photo_invalid": "Nieprawidłowy plik obrazu", + "change_photo": "Zmień" + }, + "groups": { + "create": "Nowa grupa", + "edit": "Edytuj grupę", + "empty": "Brak grup", + "delete_confirm_title": "Usuń grupę", + "delete_confirm": "Czy na pewno chcesz usunąć tę grupę?", + "name_label": "Nazwa grupy", + "name_placeholder": "np. Zespół, Rodzina", + "name_required": "Nazwa grupy jest wymagana", + "save_failed": "Nie udało się zapisać grupy", + "members_label": "Członkowie", + "search_members": "Szukaj kontaktów do dodania...", + "no_members": "Brak członków w tej grupie", + "member_count": "{count, plural, =0 {Brak członków} one {1 członek} other {# członków}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Importuj kontakty", + "drop_hint": "Kliknij, aby wybrać plik vCard", + "file_types": "pliki .vcf lub .vcard", + "no_contacts": "Nie znaleziono kontaktów w pliku", + "parse_error": "Nie udało się przetworzyć pliku vCard", + "found": "{count, plural, one {Znaleziono 1 kontakt} other {Znaleziono # kontaktów}}", + "duplicate": "Duplikat", + "select_all": "Zaznacz wszystko", + "deselect_all": "Odznacz wszystko", + "selected": "{count, plural, one {1 zaznaczony} other {# zaznaczonych}}", + "import_button": "Importuj", + "importing": "Importowanie...", + "success": "{count, plural, one {Zaimportowano 1 kontakt} other {Zaimportowano # kontaktów}}", + "failed": "Import nie powiódł się", + "close": "Zamknij", + "file_too_large": "Plik jest za duży (maks. 5 MB)" + }, + "export": { + "title": "Eksportuj kontakty", + "success": "{count, plural, one {Wyeksportowano 1 kontakt} other {Wyeksportowano # kontaktów}}" + }, + "bulk": { + "selected": "{count, plural, one {1 zaznaczony} other {# zaznaczonych}}", + "select_all": "Zaznacz wszystko", + "delete": "Usuń", + "delete_confirm_title": "Usuń kontakty", + "delete_confirm": "Usunąć {count, plural, one {1 kontakt} other {# kontaktów}}?", + "deleted": "{count, plural, one {Usunięto 1 kontakt} other {Usunięto # kontaktów}}", + "add_to_group": "Dodaj do grupy", + "choose_group": "Wybierz grupę", + "adding_contacts": "Dodawanie {count, plural, one {1 kontaktu} other {# kontaktów}}", + "added_to_group": "Kontakty dodano do grupy", + "export": "Eksportuj", + "clear": "Wyczyść zaznaczenie" + }, + "toast": { + "created": "Kontakt utworzono", + "updated": "Kontakt zaktualizowano", + "deleted": "Kontakt usunięto", + "error_create": "Nie udało się utworzyć kontaktu", + "error_update": "Nie udało się zaktualizować kontaktu", + "error_delete": "Nie udało się usunąć kontaktu" + }, + "context_menu": { + "open": "Otwórz", + "edit": "Edytuj", + "send_email": "Wyślij e-mail", + "add_to_group": "Dodaj do grupy", + "export_vcard": "Eksportuj jako vCard", + "delete": "Usuń", + "call": "Zadzwoń", + "duplicate": "Duplikuj", + "print": "Drukuj" + }, + "filters": { + "toggle": "Filtry", + "select": "Zaznacz", + "clear": "Wyczyść", + "close": "Zamknij", + "title": "Filtry zaawansowane", + "organization": "Firma", + "organization_placeholder": "np. Acme", + "job_title": "Stanowisko", + "job_title_placeholder": "np. Projektant", + "location": "Lokalizacja", + "location_placeholder": "Miasto lub kraj", + "email_domain": "Domena e-mail", + "email_domain_placeholder": "przyklad.pl", + "birthday_month": "Urodziny w", + "any_month": "Dowolny miesiąc", + "has_email": "Z e-mailem", + "has_phone": "Z telefonem", + "has_photo": "Ze zdjęciem", + "sort_by": "Sortuj według", + "sort_first_name": "Imię", + "sort_last_name": "Nazwisko" + }, + "open_categories": "Otwórz kategorie" + }, + "calendar": { + "title": "Kalendarz", + "back_to_email": "Powrót do poczty", + "back_to_month": "Powrót do miesiąca", + "my_calendars": "Kalendarze", + "birthday_calendar": "Urodziny", + "mini_calendar_change": "Kliknij, aby zmienić miesiąc", + "views": { + "month": "Miesiąc", + "week": "Tydzień", + "day": "Dzień", + "agenda": "Agenda", + "today": "Dzisiaj", + "month_hint": "Miesiąc (m)", + "week_hint": "Tydzień (w)", + "day_hint": "Dzień (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Zadania", + "tasks_hint": "Zadania (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# wcześniejsze wydarzenie} few {# wcześniejsze wydarzenia} many {# wcześniejszych wydarzeń} other {# wcześniejszego wydarzenia}}", + "hidden_after": "{count, plural, one {# późniejsze wydarzenie} few {# późniejsze wydarzenia} many {# późniejszych wydarzeń} other {# późniejszego wydarzenia}}", + "show_all_hours": "Pokaż wszystkie godziny", + "show_display_hours": "Pokaż tylko {start} – {end}", + "create": "Utwórz wydarzenie", + "edit": "Edytuj wydarzenie", + "delete": "Usuń wydarzenie", + "details": "Szczegóły wydarzenia", + "no_events": "Brak wydarzeń", + "all_day": "Cały dzień", + "more": "+{count} więcej", + "show_more": "Pokaż więcej", + "show_less": "Pokaż mniej", + "no_title": "(Bez tytułu)", + "resize": "Zmień rozmiar wydarzenia", + "duplicate": "Duplikuj", + "today_header": "Dzisiaj", + "tomorrow_header": "Jutro", + "agenda_show_earlier": "Pokaż wcześniejsze wydarzenia", + "agenda_loading": "Ładowanie…", + "agenda_range_start": "Wydarzenia od {date}", + "agenda_range_end": "Wydarzenia do {date}", + "export_ics": "Eksportuj jako .ics", + "copy_title": "Kopiuj tytuł", + "copy_link": "Kopiuj link do spotkania", + "go_to_today": "Przejdź do dzisiaj", + "new_all_day_event": "Nowe wydarzenie całodniowe", + "new_event": "Nowe wydarzenie", + "new_task": "Nowe zadanie" + }, + "detail": { + "add_note": "Dodaj notatkę...", + "save_note": "Zapisz", + "note_saved": "Dodano notatkę", + "open_link": "Otwórz link", + "copy_location": "Kopiuj lokalizację", + "meeting_link": "Link do spotkania", + "tentative": "Wstępnie", + "cancelled": "Odwołane", + "delete_confirm": "Usunąć to wydarzenie?" + }, + "form": { + "title": "Tytuł", + "description": "Opis", + "location": "Lokalizacja", + "meeting_link": "Link do spotkania", + "start_date": "Data rozpoczęcia", + "end_date": "Data zakończenia", + "start_time": "Godzina rozpoczęcia", + "end_time": "Godzina zakończenia", + "all_day_event": "Wydarzenie całodniowe", + "calendar_select": "Kalendarz", + "save": "Zapisz", + "cancel": "Anuluj", + "delete_confirm": "Czy na pewno chcesz usunąć to wydarzenie?", + "color": "Kolor" + }, + "participants": { + "availability": { + "title": "Dostępność", + "free": "Dostępny", + "busy": "Zajęty", + "tentative": "Wstępnie", + "unknown": "Nie jest użytkownikiem tego serwera", + "checking": "Sprawdzanie…" + }, + "title": "Uczestnicy", + "add": "Dodaj uczestnika", + "organizer": "Organizator", + "attendee": "Uczestnik", + "accepted": "Zaakceptowano", + "declined": "Odrzucono", + "tentative": "Wstępnie", + "needs_action": "Wymaga działania", + "remove": "Usuń", + "edit": "Edytuj", + "email_placeholder": "Dodaj adres e-mail lub wyszukaj kontakty", + "send_invitations": "Wyślij zaproszenia do uczestników", + "status_summary": "{accepted} zaakceptowano, {pending} oczekuje", + "invited_by": "Zaproszenie od {name}", + "respond_below": "Odpowiedz za pomocą przycisków poniżej", + "rsvp_label": "Twoja odpowiedź", + "cancel_notification": "Uczestnicy zostaną powiadomieni o odwołaniu", + "you_organizer": "Jesteś organizatorem", + "you_attendee": "Jesteś uczestnikiem", + "no_participants": "Brak uczestników", + "count": "{count, plural, one {# uczestnik} other {# uczestników}}" + }, + "recurrence": { + "title": "Powtarzanie", + "none": "Nie powtarza się", + "daily": "Codziennie", + "weekly": "Co tydzień", + "monthly": "Co miesiąc", + "yearly": "Co rok", + "every_n_days": "Co {count} dni", + "every_n_weeks": "Co {count} tygodni", + "every_n_months": "Co {count} miesięcy", + "until": "Do", + "occurrences": "{count} wystąpień", + "custom": "Niestandardowe…", + "edit_custom": "Edytuj niestandardowe powtarzanie", + "every_n_years": "Co {count} lat", + "on_days": "w {days}", + "on_day_n": "{day}. dnia miesiąca", + "on_the_nth": "w {nth} {day}", + "in_month": "w {month}", + "nth_1": "pierwszy", + "nth_2": "drugi", + "nth_3": "trzeci", + "nth_4": "czwarty", + "nth_last": "ostatni", + "editor_freq_day": "Dzień", + "editor_freq_week": "Tydzień", + "editor_freq_month": "Miesiąc", + "editor_freq_year": "Rok", + "editor_repeats_on": "Powtarza się", + "editor_every": "Co", + "editor_unit_days": "dni", + "editor_unit_weeks": "tyg.", + "editor_unit_months": "mies.", + "editor_unit_years": "lat(a)", + "editor_on_day": "dnia", + "editor_on_the": "w", + "editor_in": "w miesiącu", + "editor_ends": "Kończy się", + "editor_never": "Nigdy", + "editor_ends_on": "Dnia", + "editor_ends_after": "Po", + "editor_occurrences": "wystąpieniach" + }, + "recurrence_scope": { + "edit_title": "Edytuj wydarzenie cykliczne", + "delete_title": "Usuń wydarzenie cykliczne", + "description": "To jest wydarzenie cykliczne. Które wydarzenia chcesz zmodyfikować?", + "this_event": "Tylko to wydarzenie", + "this_and_future": "To i następne wydarzenia", + "all_events": "Wszystkie wydarzenia", + "cancel": "Anuluj", + "save": "Zapisz", + "delete": "Usuń", + "rsvp_title": "Odpowiedz na wydarzenie cykliczne", + "rsvp_description": "To jest wydarzenie cykliczne. Których wydarzeń dotyczy Twoja odpowiedź?", + "respond": "Odpowiedz" + }, + "alerts": { + "title": "Przypomnienie", + "none": "Brak przypomnienia", + "at_time": "W czasie wydarzenia", + "minutes_before": "{count, plural, one {# minutę przed} other {# minut przed}}", + "hours_before": "{count, plural, one {# godzinę przed} other {# godzin przed}}", + "days_before": "{count, plural, one {# dzień przed} other {# dni przed}}", + "weeks_before": "{count, plural, one {# tydzień przed} few {# tygodnie przed} many {# tygodni przed} other {# tygodnia przed}}", + "unit_minutes_before": "minut przed", + "unit_hours_before": "godzin przed", + "unit_days_before": "dni przed", + "unit_weeks_before": "tygodni przed", + "add": "Dodaj przypomnienie", + "remove": "Usuń przypomnienie", + "amount": "Wartość przypomnienia", + "unit": "Jednostka przypomnienia", + "action": "Typ przypomnienia", + "action_display": "Powiadomienie", + "action_email": "E-mail", + "by_email": "{label} e-mailem", + "kept": "{count, plural, one {Zachowano # dodatkowe przypomnienie, którego nie można tu edytować} few {Zachowano # dodatkowe przypomnienia, których nie można tu edytować} many {Zachowano # dodatkowych przypomnień, których nie można tu edytować} other {Zachowano # dodatkowego przypomnienia, których nie można tu edytować}}", + "other": "{count, plural, one {# inne przypomnienie} few {# inne przypomnienia} many {# innych przypomnień} other {# innego przypomnienia}}", + "calendar_defaults": "Domyślne przypomnienia kalendarza", + "customize": "Dostosuj", + "use_defaults": "Użyj domyślnych przypomnień kalendarza" + }, + "settings": { + "limit_hours": "Ogranicz widoczne godziny", + "limit_hours_desc": "Pokazuj w widoku dnia i tygodnia tylko te godziny. Wydarzenia poza nimi są dostępne przez strzałki u góry i u dołu każdego dnia.", + "visible_hours": "Widoczne godziny", + "visible_hours_start": "Początek widocznych godzin", + "visible_hours_end": "Koniec widocznych godzin", + "hide_non_working_days": "Ukryj dni wolne od pracy", + "hide_non_working_days_desc": "Pomijaj w widoku tygodnia dni, w które nie pracujesz. Ich wydarzenia nadal widać w widokach miesiąca, dnia i agendy.", + "working_days": "Dni robocze", + "title": "Ustawienia kalendarza", + "default_view": "Domyślny widok", + "week_starts_on": "Tydzień zaczyna się w", + "time_format": "Format czasu", + "default_calendar": "Domyślny kalendarz", + "default_reminder": "Domyślne przypomnienie", + "time_format_12h": "12-godzinny", + "time_format_24h": "24-godzinny", + "notifications_enabled": "Powiadomienia o wydarzeniach", + "notifications_enabled_desc": "Pokazuj alerty o nadchodzących wydarzeniach w kalendarzu", + "notification_sound": "Dźwięk powiadomienia", + "notification_sound_desc": "Odtwarzaj dźwięk dla alertów kalendarza", + "invitation_parsing": "Analizuj zaproszenia e-mail", + "invitation_parsing_desc": "Wykrywaj zaproszenia kalendarza w załącznikach e-mail i pokazuj działania kalendarza", + "show_time_in_month_view": "Pokaż godzinę w widoku miesiąca", + "show_time_in_month_view_desc": "Wyświetlaj godziny wydarzeń w miesięcznym widoku kalendarza", + "show_week_numbers": "Pokaż numery tygodni", + "show_week_numbers_desc": "Wyświetlaj numery tygodni w mini-kalendarzu", + "calendar_free_scroll": "Swobodne przewijanie", + "calendar_free_scroll_desc": "Przewijaj płynnie miesiące, tygodnie i dni zamiast jednego okresu naraz", + "enable_tasks": "Włącz zadania", + "enable_tasks_desc": "Pokaż widok zadań w kalendarzu do zarządzania zadaniami", + "show_tasks_on_calendar": "Pokaż zadania w kalendarzu", + "show_tasks_on_calendar_desc": "Wyświetlaj zadania w miesięcznym, tygodniowym i dziennym widoku kalendarza", + "hover_preview": "Podgląd wydarzenia po najechaniu", + "hover_preview_desc": "Pokaż wyskakujące okno szczegółów po najechaniu na wydarzenia w kalendarzu", + "hover_preview_instant": "Natychmiast", + "hover_preview_delay_500ms": "Opóźnienie 0,5 sekundy", + "hover_preview_delay_1s": "Opóźnienie 1 sekundy", + "hover_preview_delay_2s": "Opóźnienie 2 sekund", + "hover_preview_off": "Wyłączono", + "organizer_identity": "Organizuj zaproszenia jako", + "organizer_identity_desc": "Adres kalendarza używany jako organizator, gdy zapraszasz osoby na wydarzenia.", + "organizer_identity_failed": "Nie udało się zmienić adresu organizatora", + "show_birthday_calendar": "Kalendarz urodzin kontaktów", + "show_birthday_calendar_desc": "Pokaż wirtualny kalendarz z urodzinami z kontaktów" + }, + "days": { + "monday": "Poniedziałek", + "tuesday": "Wtorek", + "wednesday": "Środa", + "thursday": "Czwartek", + "friday": "Piątek", + "saturday": "Sobota", + "sunday": "Niedziela", + "mon": "Pon", + "tue": "Wt", + "wed": "Śr", + "thu": "Czw", + "fri": "Pt", + "sat": "Sob", + "sun": "Ndz" + }, + "notifications": { + "event_created": "Wydarzenie utworzono", + "event_updated": "Wydarzenie zaktualizowano", + "event_deleted": "Wydarzenie usunięto", + "calendar_created": "Kalendarz utworzono", + "calendar_deleted": "Kalendarz usunięto", + "event_move_error": "Nie udało się przenieść wydarzenia", + "event_resize_error": "Nie udało się zmienić rozmiaru wydarzenia", + "alert_title": "Nadchodzące wydarzenie", + "alert_now": "Zaczyna się teraz", + "alert_in_minutes": "Za {count} min", + "invitation_sent": "Zaproszenia wysłano", + "invitations_denied_title": "Nie można wysłać zaproszeń", + "invitations_denied": "Serwer odmówił wysłania zaproszeń: {reason}. Zapisać wydarzenie bez ich wysyłania?", + "save_without_invitations": "Zapisz bez zaproszeń", + "rsvp_updated": "Odpowiedź zaktualizowano", + "rsvp_error": "Nie udało się zaktualizować odpowiedzi", + "event_duplicated": "Wydarzenie zduplikowano", + "event_error": "Nie udało się zapisać wydarzenia", + "task_due": "Termin zadania", + "event_exported": "Wydarzenie wyeksportowane", + "title_copied": "Tytuł skopiowany", + "link_copied": "Link skopiowany" + }, + "status": { + "loading_calendars": "Ładowanie kalendarzy...", + "loading_events": "Ładowanie wydarzeń..." + }, + "quick_create": { + "placeholder": "Tytuł nowego wydarzenia", + "aria_label": "Szybkie tworzenie wydarzenia", + "add_title": "Dodaj tytuł", + "more_options": "Więcej opcji" + }, + "nav_prev": "Poprzedni", + "nav_next": "Następny", + "import": { + "title": "Importuj kalendarz", + "tab_file": "Plik", + "tab_url": "URL", + "select_file": "Wybierz plik .ics", + "drop_file": "lub upuść plik tutaj", + "supported_formats": "Obsługuje pliki iCalendar (.ics)", + "url_description": "Wprowadź adres URL zewnętrznego kanału iCalendar (.ics), aby zaimportować wydarzenia.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Obsługuje adresy URL iCalendar (.ics)", + "fetch": "Pobierz", + "invalid_url": "Wprowadź prawidłowy adres URL", + "url_fetch_failed": "Nie udało się pobrać kalendarza z adresu URL", + "parsing": "Analizowanie pliku kalendarza...", + "parsed_events": "Znaleziono {count} wydarzeń", + "no_events": "Nie znaleziono wydarzeń w pliku", + "select_all": "Zaznacz wszystko", + "deselect_all": "Odznacz wszystko", + "target_calendar": "Importuj do kalendarza", + "import_button": "Importuj zaznaczone", + "importing": "Importowanie wydarzeń...", + "success": "Pomyślnie zaimportowano {count} wydarzeń", + "error": "Nie udało się zaimportować kalendarza", + "file_too_large": "Plik przekracza limit 10 MB", + "invalid_format": "Nieprawidłowy format pliku kalendarza" + }, + "webcal_action": { + "title": "Otwórz link kalendarza", + "description": "Jak chcesz użyć \"{name}\"?", + "import_title": "Importuj jednorazowo", + "import_description": "Pobierz wydarzenia teraz i skopiuj je do jednego ze swoich kalendarzy.", + "subscribe_title": "Subskrybuj", + "subscribe_description": "Automatycznie synchronizuj ten kalendarz jako oddzielny kalendarz.", + "cancel": "Anuluj" + }, + "management": { + "title": "Zarządzanie kalendarzem", + "description": "Twórz, zmieniaj nazwy i dostosowuj swoje kalendarze. Kliknij prawym przyciskiem kalendarz na pasku bocznym, aby szybko zmienić jego kolor.", + "name": "Nazwa", + "name_placeholder": "Nazwa kalendarza", + "color": "Kolor", + "change_color": "Zmień kolor", + "random_color": "Nowy losowy kolor", + "add_calendar": "Dodaj kalendarz", + "edit": "Edytuj", + "delete": "Usuń", + "save": "Zapisz", + "create": "Utwórz", + "cancel": "Anuluj", + "default": "Domyślny", + "confirm_delete": "Usunąć \"{name}\"? Wszystkie wydarzenia w tym kalendarzu zostaną usunięte.", + "confirm_clear": "Wyczyścić wszystkie wydarzenia z \"{name}\"? Tej operacji nie można cofnąć.", + "set_default": "Ustaw jako domyślny", + "default_updated": "Zaktualizowano domyślny kalendarz", + "error_default": "Nie udało się ustawić domyślnego kalendarza", + "clear_events": "Wyczyść wydarzenia", + "events_cleared": "Wyczyszczono {count} wydarzeń", + "error_clear": "Nie udało się wyczyścić wydarzeń kalendarza", + "calendar_created": "Kalendarz utworzono", + "calendar_updated": "Kalendarz zaktualizowano", + "calendar_deleted": "Kalendarz usunięto", + "color_updated": "Kolor kalendarza zaktualizowano", + "error_create": "Nie udało się utworzyć kalendarza", + "error_update": "Nie udało się zaktualizować kalendarza", + "error_delete": "Nie udało się usunąć kalendarza", + "caldav_url": "Adres URL CalDAV", + "copy_url": "Kopiuj adres URL CalDAV", + "url_copied": "Adres URL CalDAV skopiowano do schowka", + "share": "Udostępnij kalendarz", + "new_event_in_calendar": "Nowe wydarzenie w tym kalendarzu", + "shared_with": "Udostępniono: {names}", + "shared_with_count": "{count, plural, one {Udostępniono 1 osobie} few {Udostępniono # osobom} many {Udostępniono # osobom} other {Udostępniono # osoby}}", + "move_up": "Przesuń w górę", + "move_down": "Przesuń w dół", + "kind": "Zawartość", + "kind_events": "Wydarzenia", + "kind_tasks": "Zadania", + "kind_both": "Wydarzenia i zadania", + "kind_hint": "Określa, które aplikacje widzą ten kalendarz podczas synchronizacji przez CalDAV. Nie można tego później zmienić.", + "default_alerts": "Domyślne przypomnienia", + "default_alerts_title": "Domyślne przypomnienia dla \"{name}\"", + "default_alerts_hint": "Nowe wydarzenia w tym kalendarzu otrzymują te przypomnienia, chyba że zostaną zmienione w wydarzeniu. Zmiana tutaj nie wpływa na istniejące wydarzenia. Przypomnienia e-mail wysyła serwer poczty.", + "default_alerts_timed": "Wydarzenia z godziną", + "default_alerts_all_day": "Wydarzenia całodniowe", + "default_alerts_updated": "Zaktualizowano domyślne przypomnienia" + }, + "subscription": { + "title": "Subskrypcja iCal", + "section_title": "Subskrypcje iCal", + "description": "Subskrybuj zewnętrzny kanał iCalendar. Wydarzenia będą automatycznie synchronizowane do osobnego kalendarza. Obsługuje adresy URL https:// i webcal://.", + "url_label": "Adres URL kalendarza", + "url_placeholder": "https://example.com/calendar.ics lub webcal://...", + "name_label": "Nazwa kalendarza", + "name_placeholder": "np. Święta publiczne", + "color_label": "Kolor", + "refresh_interval": "Częstotliwość odświeżania", + "interval_15": "Co 15 minut", + "interval_30": "Co 30 minut", + "interval_60": "Co godzinę", + "interval_360": "Co 6 godzin", + "interval_1440": "Codziennie", + "subscribe": "Subskrybuj", + "subscribing": "Subskrybowanie...", + "save": "Zapisz zmiany", + "saving": "Zapisywanie...", + "edit": "Edytuj", + "edit_title": "Edytuj subskrypcję", + "updated": "Zaktualizowano \"{name}\"", + "update_error": "Nie udało się zaktualizować subskrypcji", + "invalid_url": "Wprowadź prawidłowy adres URL", + "success": "Zasubskrybowano \"{name}\"", + "error": "Nie udało się dodać subskrypcji", + "refresh": "Odśwież teraz", + "refresh_success": "Subskrypcję odświeżono", + "refresh_error": "Nie udało się odświeżyć subskrypcji", + "unsubscribe": "Anuluj subskrypcję", + "confirm_delete": "Anulować subskrypcję \"{name}\"? Kalendarz i wszystkie jego wydarzenia zostaną usunięte.", + "deleted": "Subskrypcję usunięto", + "delete_error": "Nie udało się usunąć subskrypcji", + "last_refreshed": "Ostatnia aktualizacja: {time}" + }, + "tasks": { + "label": "Zadania", + "no_tasks": "Brak zadań", + "no_title": "(Bez tytułu)", + "mark_complete": "Oznacz jako ukończone", + "mark_incomplete": "Oznacz jako nieukończone", + "filter_all": "Wszystkie", + "filter_pending": "Oczekujące", + "filter_completed": "Ukończone", + "filter_overdue": "Zaległe", + "show_completed": "Pokaż ukończone", + "create": "Nowe zadanie", + "edit": "Edytuj zadanie", + "title_placeholder": "Tytuł zadania", + "description_placeholder": "Dodaj opis...", + "due_date": "Termin", + "include_time": "Uwzględnij godzinę", + "priority": "Priorytet", + "priority_none": "Brak", + "priority_high": "Wysoki", + "priority_medium": "Średni", + "priority_low": "Niski", + "progress": "Status", + "progress_needs_action": "Wymaga działania", + "progress_in_process": "W trakcie", + "progress_completed": "Ukończone", + "progress_cancelled": "Anulowano", + "calendar": "Kalendarz", + "alert": "Przypomnienie", + "alert_none": "Brak", + "alert_at_time": "W czasie terminu", + "alert_5min": "5 minut przed", + "alert_15min": "15 minut przed", + "alert_30min": "30 minut przed", + "alert_1hr": "1 godzinę przed", + "alert_1day": "1 dzień przed", + "delete": "Usuń", + "cancel": "Anuluj", + "save": "Zapisz", + "quick_add_placeholder": "Dodaj zadanie...", + "due_today": "Dzisiaj", + "due_tomorrow": "Jutro", + "overdue": "Zaległe" + }, + "months": { + "jan": "sty", + "feb": "lut", + "mar": "mar", + "apr": "kwi", + "may": "maj", + "jun": "cze", + "jul": "lip", + "aug": "sie", + "sep": "wrz", + "oct": "paź", + "nov": "lis", + "dec": "gru", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Otwórz menu", + "toolbar": { + "more": "Więcej działań", + "select_view": "Zmień widok, obecnie {view}" + }, + "other_calendars": "Inne kalendarze", + "calendar_options": "Opcje dla {name}" + }, + "advanced_search": { + "title": "Wyszukiwanie zaawansowane", + "from": "Od", + "from_placeholder": "Adres e-mail lub nazwa nadawcy", + "to": "Do", + "to_placeholder": "Adres e-mail lub nazwa odbiorcy", + "subject": "Temat", + "subject_placeholder": "Temat zawiera...", + "body": "Treść", + "body_placeholder": "Treść zawiera...", + "folder": "Folder", + "all_folders": "Wszystkie foldery", + "all_folders_except_spam_trash": "Wszystkie foldery oprócz Spamu i Kosza", + "has_attachment": "Załączniki", + "date_after": "Po", + "date_before": "Przed", + "size_min": "Większe niż (KB)", + "size_max": "Mniejsze niż (KB)", + "starred": "Z gwiazdką", + "unread": "Nieprzeczytane", + "read": "Przeczytane", + "yes": "Tak", + "no": "Nie", + "clear": "Wyczyść", + "clear_all": "Wyczyść wszystko", + "filters_active": "{count} filtr", + "filters_active_plural": "{count} filtry", + "toggle_filters": "Więcej", + "search_hint": "Użyj zaawansowanych filtrów, aby precyzyjnie wyszukiwać", + "advanced_filters_tooltip": "Zaawansowane filtry wyszukiwania", + "results_found": "{count, plural, =0 {Nie znaleziono wyników} one {Znaleziono # wynik} other {Znaleziono # wyników}}", + "results_found_more": "Znaleziono {count}+ wyników", + "suggestions_recent": "Ostatnie wyszukiwania", + "suggestions_people": "Osoby", + "suggestions_from": "Szukaj wiadomości od {address}", + "suggestions_to": "Szukaj wiadomości do {address}", + "suggestions_remove_recent": "Usuń z ostatnich wyszukiwań", + "has_words": "Zawiera słowa", + "size": "Rozmiar", + "date_within": "Data", + "search_in": "Szukaj w", + "has_attachment_check": "Z załącznikiem", + "search": "Szukaj" + }, + "welcome": { + "title": "Witaj w swojej skrzynce pocztowej", + "tip_compose": "Naciśnij c, aby napisać nową wiadomość e-mail", + "tip_shortcuts": "Naciśnij ?, aby zobaczyć wszystkie skróty klawiszowe", + "tip_sidebar": "Kalendarz, Kontakty i Ustawienia znajdziesz w menu bocznym", + "tip_settings": "Dostosuj działanie aplikacji w Ustawieniach", + "tip_compose_touch": "Stuknij przycisk z ołówkiem, aby napisać nowy e-mail", + "tip_sidebar_touch": "Kalendarz, Kontakty i Ustawienia znajdziesz na dolnym pasku", + "got_it": "Rozumiem", + "settings": "Ustawienia", + "dismiss": "Zamknij", + "start_tour": "Rozpocznij przewodnik" + }, + "demo_welcome": { + "title": "Witaj w Bulwark Mail", + "description": "Poznaj w pełni funkcjonalnego klienta poczty webowej - bezpośrednio w przeglądarce. Wszystkie dane pozostają na Twoim urządzeniu, więc możesz śmiało wszystko przetestować.", + "feature_email": "Czytaj i pisz wiadomości e-mail", + "feature_organize": "Etykiety, gwiazdki i foldery", + "feature_shortcuts": "Skróty klawiszowe", + "feature_privacy": "100% prywatna wersja demo", + "hint": "Kliknij dowolną wiadomość po lewej, aby zacząć, albo skorzystaj z przewodnika poniżej." + }, + "files": { + "office_edit": "Edytuj dokument", + "office_loading": "Otwieranie edytora dokumentów…", + "office_error": "Nie udało się otworzyć edytora dokumentów.", + "office_close": "Zamknij edytor", + "open_in_new_tab": "Otwórz w nowej karcie", + "title": "Pliki", + "search_placeholder": "Szukaj plików...", + "empty_state_title": "Brak plików", + "upload_files": "Prześlij pliki", + "new_folder": "Nowy folder", + "new_folder_name": "Nazwa folderu", + "rename": "Zmień nazwę", + "rename_title": "Zmień nazwę", + "new_name": "Nowa nazwa", + "name_forbidden_chars": "Serwer nie zezwala na te znaki w nazwach: {chars}", + "name_reserved": "Ta nazwa jest zarezerwowana na serwerze.", + "delete": "Usuń", + "delete_confirm_title": "Usuń zasób", + "delete_confirm_message": "Czy na pewno chcesz usunąć \"{name}\"? Tej operacji nie można cofnąć.", + "download": "Pobierz", + "name": "Nazwa", + "size": "Rozmiar", + "modified": "Zmodyfikowano", + "type": "Typ", + "folder": "Folder", + "file": "Plik", + "uploading": "Przesyłanie...", + "upload_success": "{count, plural, one {Przesłano 1 plik} other {Przesłano # plików}}", + "upload_canceled": "Anulowano przesyłanie", + "upload_canceled_partial": "{count, plural, one {Przed anulowaniem przesłano 1 plik} few {Przed anulowaniem przesłano # pliki} many {Przed anulowaniem przesłano # plików} other {Przed anulowaniem przesłano # pliku}}", + "upload_error": "Nie udało się przesłać pliku", + "create_folder_success": "Folder utworzono", + "create_folder_error": "Nie udało się utworzyć folderu", + "delete_success": "Usunięto pomyślnie", + "delete_error": "Nie udało się usunąć", + "rename_success": "Nazwę zmieniono pomyślnie", + "rename_error": "Nie udało się zmienić nazwy", + "download_error": "Nie udało się pobrać", + "not_available": "Przechowywanie plików nie jest dostępne na tym serwerze", + "cancel": "Anuluj", + "create": "Utwórz", + "save": "Zapisz", + "no_results": "Żadne pliki nie pasują do wyszukiwania", + "batch_delete_confirm_message": "Czy na pewno chcesz usunąć {count, plural, one {1 element} other {# elementów}}? Tej operacji nie można cofnąć.", + "batch_delete_success": "{count, plural, one {Usunięto 1 element} other {Usunięto # elementów}}", + "grid_view": "Widok siatki", + "list_view": "Widok listy", + "details": "Szczegóły", + "path": "Ścieżka", + "preview": "Podgląd", + "preview_error": "Nie udało się załadować podglądu", + "cut": "Wytnij", + "copy": "Kopiuj", + "paste": "Wklej", + "move_success": "{count, plural, one {Przeniesiono 1 element} other {Przeniesiono # elementów}}", + "move_error": "Nie udało się przenieść", + "paste_success": "Wklejono pomyślnie", + "paste_error": "Nie udało się wkleić", + "new_text_file": "Nowy plik tekstowy", + "file_name": "Nazwa pliku", + "retry": "Ponów", + "refresh": "Odśwież", + "duplicate": "Duplikuj", + "duplicate_success": "Zduplikowano pomyślnie", + "duplicate_error": "Nie udało się zduplikować", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Plik utworzono", + "create_file_error": "Nie udało się utworzyć pliku", + "favorites": "Ulubione", + "recent": "Ostatnie", + "show_in_folder": "Pokaż w folderze", + "remove_from_recent": "Usuń z ostatnich", + "upload_folder": "Prześlij folder", + "file_too_large": "\"{name}\" przekracza maksymalny rozmiar pliku ({max})", + "undo": "Cofnij", + "undo_success": "Cofnięto działanie", + "undo_error": "Nie udało się cofnąć", + "toolbar": "Działania na plikach", + "file_list": "Pliki i foldery", + "context_menu": "Działania", + "settings_ascending": "Rosnąco", + "settings_descending": "Malejąco", + "disabled_title": "Funkcja Pliki jest wyłączona przez administratora", + "disabled_description": "Duże przesyłanie plików przez WebDAV może powodować niestabilność Stalwart/RocksDB, w tym awarie z powodu braku pamięci i nieodwracalne zużycie miejsca na dysku. Usunięte pliki mogą nie zostać natychmiast usunięte z magazynu blob. Ta funkcja nie jest zalecana w środowiskach produkcyjnych.", + "no_accounts": "Brak połączonych kont.", + "open_folder_tree": "Otwórz drzewo folderów", + "migration_title": "Aktualizowanie plików…", + "migration_description": "Porządkowanie folderów i plików w odpowiedniej strukturze. Dzieje się to tylko raz.", + "new": "Nowy", + "my_files": "Moje pliki", + "folders": "Foldery", + "files_heading": "Pliki", + "open": "Otwórz", + "star": "Oznacz gwiazdką", + "unstar": "Usuń gwiazdkę", + "expand_folder": "Rozwiń {name}", + "collapse_folder": "Zwiń {name}", + "sidebar_hint": "Oznaczaj foldery gwiazdką i otwieraj pliki, aby znaleźć je tutaj.", + "breadcrumbs": "Ścieżka folderu", + "show_path": "Pokaż ukryte foldery w ścieżce", + "selection_actions": "Działania na zaznaczonych elementach", + "selected_count": "{count, plural, one {Zaznaczono 1} few {Zaznaczono #} many {Zaznaczono #} other {Zaznaczono #}}", + "clear_selection": "Wyczyść zaznaczenie", + "select_all": "Zaznacz wszystkie", + "select_item": "Zaznacz {name}", + "more_actions": "Więcej działań dla {name}", + "paste_here": "Wklej do tego folderu", + "paste_count": "{count, plural, one {Wklej 1 element} few {Wklej # elementy} many {Wklej # elementów} other {Wklej # elementu}}", + "sort_by": "Sortuj według", + "view_mode": "Układ", + "clear_search": "Wyczyść wyszukiwanie", + "no_results_for": "Nic w tym folderze nie pasuje do \"{query}\"", + "download_count": "{count, plural, one {Pobierz 1 plik} few {Pobierz # pliki} many {Pobierz # plików} other {Pobierz # pliku}}", + "delete_count": "{count, plural, one {Usuń 1 element} few {Usuń # elementy} many {Usuń # elementów} other {Usuń # elementu}}", + "close_details": "Zamknij szczegóły", + "file_kind": "Plik {ext}", + "folder_summary": "{folders, plural, =0 {Brak folderów} one {1 folder} few {# foldery} many {# folderów} other {# folderu}}, {files, plural, =0 {brak plików} one {1 plik} few {# pliki} many {# plików} other {# pliku}}", + "details_hint": "Zaznacz plik lub folder, aby zobaczyć szczegóły.", + "sharing": "Udostępnianie", + "shared_with_count": "{count, plural, one {Udostępniono 1 osobie} few {Udostępniono # osobom} many {Udostępniono # osobom} other {Udostępniono # osoby}}", + "only_you": "Tylko Ty", + "empty_folder_title": "Ten folder jest pusty", + "empty_folder_description": "Upuść tutaj pliki lub prześlij je ze swojego urządzenia.", + "empty_folder_read_only": "Nie możesz dodawać plików do tego folderu.", + "choose_account": "Wybierz konto", + "drop_to_upload": "Upuść, aby przesłać do {folder}", + "uploading_count": "Przesyłanie {current} z {total}", + "dismiss": "Zamknij", + "zoom_in": "Powiększ", + "zoom_out": "Pomniejsz", + "zoom_reset": "Dopasuj do ekranu", + "rotate": "Obróć", + "close_preview": "Zamknij" + }, + "smime": { + "your_certificates": "Twoje certyfikaty", + "your_certificates_desc": "Importuj i zarządzaj swoimi certyfikatami S/MIME do podpisywania i szyfrowania wiadomości e-mail", + "recipient_certificates": "Certyfikaty odbiorców", + "recipient_certificates_desc": "Certyfikaty publiczne do szyfrowania wiadomości e-mail do odbiorców", + "identity_bindings": "Powiązania kluczy tożsamości", + "identity_bindings_desc": "Powiąż certyfikaty S/MIME z tożsamościami e-mail", + "defaults_title": "Domyślne", + "defaults_desc": "Skonfiguruj domyślne zachowanie podpisywania i szyfrowania", + "import_pkcs12": "Importuj PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importuj certyfikat", + "no_certificates": "Nie zaimportowano jeszcze żadnych certyfikatów", + "no_recipient_certs": "Brak certyfikatów odbiorców", + "expires": "Wygasa", + "expired": "Wygasł", + "bound_to": "Powiązano z", + "no_key_bound": "Brak", + "lock": "Zablokuj klucz", + "unlock": "Odblokuj klucz", + "details": "Pokaż szczegóły", + "delete": "Usuń", + "encrypt_by_default": "Szyfruj domyślnie", + "encrypt_by_default_desc": "Automatycznie szyfruj wiadomości e-mail, gdy wszyscy odbiorcy mają certyfikaty", + "remember_unlocked": "Zapamiętaj odblokowane klucze", + "remember_unlocked_desc": "Pozostaw klucze odblokowane przez czas trwania tej sesji przeglądarki", + "sign_default_for": "Podpisuj domyślnie dla", + "enter_p12_passphrase": "Wprowadź hasło PKCS#12", + "p12_passphrase_desc": "Wprowadź hasło chroniące ten plik certyfikatu", + "enter_storage_passphrase": "Ustaw hasło magazynu", + "storage_passphrase_desc": "Wybierz hasło do ochrony tego klucza podczas przechowywania w przeglądarce", + "next": "Dalej", + "import": "Importuj", + "unlock_key": "Odblokuj klucz", + "unlock_key_desc": "Wprowadź hasło magazynu, aby odblokować ten klucz do podpisywania lub odszyfrowywania", + "passphrase_placeholder": "Wprowadź hasło", + "confirm_passphrase_placeholder": "Potwierdź hasło", + "passphrase_mismatch": "Hasła nie są zgodne", + "cancel": "Anuluj", + "processing": "Przetwarzanie…", + "close": "Zamknij", + "certificate_details": "Szczegóły certyfikatu", + "cert_subject": "Podmiot", + "cert_issuer": "Wystawca", + "cert_email": "E-mail", + "cert_serial": "Numer seryjny", + "cert_validity": "Ważność", + "cert_fingerprint": "Odcisk (SHA-256)", + "cert_algorithm": "Algorytm", + "cert_capabilities": "Możliwości", + "cert_source": "Źródło", + "cert_expired": "Ten certyfikat wygasł", + "cert_not_yet_valid": "Ten certyfikat nie jest jeszcze ważny", + "cap_sign": "Podpisywanie", + "cap_encrypt": "Szyfrowanie", + "cap_none": "Brak", + "show_passphrase": "Pokaż hasło", + "hide_passphrase": "Ukryj hasło", + "sign_toggle": "Podpisz", + "encrypt_toggle": "Szyfruj", + "missing_recipient_certs": "Brak certyfikatów dla: {emails}", + "missing_sender_cert": "Brak certyfikatu powiązanego z tą tożsamością", + "status_encrypted_ok": "Ta wiadomość została zaszyfrowana", + "status_encrypted_no_key": "Ta wiadomość jest zaszyfrowana, ale nie znaleziono pasującego klucza", + "status_encrypted_failed": "Nie udało się odszyfrować tej wiadomości", + "status_signed_valid": "Podpis zweryfikowano", + "status_signed_invalid": "Weryfikacja podpisu nie powiodła się", + "status_signed_expired_cert": "Podpisano wygasłym certyfikatem", + "status_signed_self_signed": "Podpis jest prawidłowy, ale certyfikat jest samopodpisany (niezaufany)", + "status_signed_mismatch": "Podpis jest prawidłowy, ale podpisujący nie zgadza się z nadawcą", + "status_unsupported": "Nieobsługiwany format S/MIME", + "auto_import_signer_certs": "Automatycznie importuj certyfikaty podpisujących", + "auto_import_signer_certs_desc": "Automatycznie zapisuj certyfikaty ze zweryfikowanych podpisanych wiadomości e-mail do przyszłego szyfrowania", + "export": "Eksportuj", + "enter_export_passphrase": "Ustaw hasło eksportu", + "export_passphrase_desc": "Wybierz hasło do ochrony eksportowanego pliku PKCS#12", + "export_storage_desc": "Wprowadź hasło magazynu, aby odszyfrować klucz do eksportu", + "incorrect_passphrase": "Nieprawidłowe hasło" + }, + "tour": { + "step_counter": "Krok {current} z {total}", + "skip": "Pomiń przewodnik", + "back": "Wstecz", + "next": "Dalej", + "finish": "Zakończ", + "take_a_tour": "Poznaj interfejs w krótkim przewodniku", + "restart_title": "Przewodnik wprowadzający", + "restart_desc": "Odtwórz przewodnik po interfejsie krok po kroku", + "restart_button": "Uruchom przewodnik ponownie", + "show_on_new_devices_title": "Pokaż na nowych urządzeniach", + "show_on_new_devices_desc": "Wyświetl ponownie baner powitalny i przewodnik przy pierwszym logowaniu na nowym urządzeniu, nawet jeśli zostały już ukończone w innym miejscu", + "sidebar_title": "Twoje skrzynki pocztowe", + "sidebar_desc": "To jest pasek boczny z folderami. Kliknij dowolną skrzynkę, aby zobaczyć jej wiadomości. Możesz tworzyć foldery, przeciągać między nimi wiadomości i od razu widzieć liczbę nieprzeczytanych.", + "compose_title": "Napisz wiadomość", + "compose_desc": "Kliknij tutaj, aby napisać nową wiadomość. Możesz dodać odbiorców, załączniki i korzystać z formatowania tekstu sformatowanego.", + "search_title": "Przeszukuj pocztę", + "search_desc": "Szukaj według nadawcy, tematu lub treści. Kliknij ikonę filtra, aby użyć zaawansowanych opcji, takich jak zakres dat, załączniki i wiadomości z gwiazdką.", + "email_list_title": "Lista wiadomości", + "email_list_desc": "Tutaj pojawiają się wiadomości. Kliknij jedną z nich, aby przeczytać ją po prawej stronie. Użyj pola wyboru, aby zaznaczyć kilka naraz, a potem zbiorczo je przenieść, usunąć lub oznaczyć etykietą.", + "email_viewer_title": "Panel czytania", + "email_viewer_desc": "Tutaj otwiera się wybrana wiadomość. Za pomocą przycisków na pasku narzędzi możesz odpowiedzieć, przesłać ją dalej, zarchiwizować lub usunąć. Możesz też oznaczać wiadomości gwiazdką albo dodawać kolorowe etykiety.", + "keywords_title": "Kolorowe etykiety", + "keywords_desc": "Porządkuj pocztę za pomocą kolorowych etykiet. Przeciągnij wiadomość na etykietę, aby ją oznaczyć, albo kliknij wiadomość prawym przyciskiem myszy, aby przypisać etykiety.", + "calendar_title": "Kalendarz", + "calendar_desc": "Przełącz się do kalendarza, aby zarządzać wydarzeniami. Możesz tworzyć wydarzenia, ustawiać przypomnienia i przełączać widok dnia, tygodnia lub miesiąca.", + "contacts_title": "Kontakty", + "contacts_desc": "Tutaj znajduje się Twoja książka adresowa. Importuj kontakty, twórz grupy i kliknij dowolny kontakt, aby zobaczyć jego pełne dane.", + "settings_title": "Ustawienia", + "settings_desc": "Dostosuj wszystko: motyw, gęstość widoku, podpisy, filtry, skróty klawiszowe, domyślne ustawienia kalendarza i wiele więcej.", + "shortcuts_title": "Skróty klawiszowe", + "shortcuts_desc": "To ulubiona funkcja zaawansowanych użytkowników. Naciśnij ? w dowolnym momencie, aby zobaczyć wszystkie dostępne skróty. Możesz poruszać się po aplikacji, pisać i zarządzać wiadomościami bez użycia myszy.", + "compose_open_title": "Edytor wiadomości", + "compose_open_desc": "To jest edytor wiadomości. Dodawaj odbiorców, pisz treść, załączaj pliki i korzystaj z formatowania tekstu sformatowanego. Możesz też zapisywać szkice i używać szablonów.", + "calendar_view_title": "Twój kalendarz", + "calendar_view_desc": "Tutaj widzisz kalendarz z przykładowymi wydarzeniami. Za pomocą paska narzędzi możesz przełączać widok dnia, tygodnia, miesiąca i agendy.", + "create_event_title": "Utwórz wydarzenie", + "create_event_desc": "Kliknij ten przycisk, aby utworzyć nowe wydarzenie w kalendarzu. Możesz ustawić tytuł, datę, godzinę i dodać uczestników.", + "event_modal_title": "Szczegóły wydarzenia", + "event_modal_desc": "To jest formularz wydarzenia. Uzupełnij tytuł, wybierz datę i godzinę, dodaj lokalizację albo uczestników. Gdy skończysz, kliknij zapisz - albo zamknij okno i przejdź dalej.", + "contacts_list_title": "Twoje kontakty", + "contacts_list_desc": "Tutaj są Twoje kontakty. Kliknij dowolny kontakt, aby zobaczyć jego pełne dane po prawej stronie. Możesz też tworzyć nowe kontakty, importować vCardy i organizować kontakty w grupy.", + "settings_tabs_title": "Menu ustawień", + "settings_tabs_desc": "Tutaj znajdują się wszystkie kategorie ustawień. Dostosuj wygląd, zarządzaj tożsamościami, skonfiguruj filtry poczty, ustawienia kalendarza i wiele więcej.", + "files_title": "Przechowywanie plików", + "files_desc": "Przeglądarka plików pozwala przesyłać, porządkować i udostępniać pliki - jak osobisty dysk w chmurze wbudowany w Twoją pocztę.", + "demo_banner_title": "Sterowanie wersją demo", + "demo_banner_desc": "Jesteś w trybie demo - wszystko pozostaje w Twojej przeglądarce. W każdej chwili kliknij „Reset Demo”, aby zacząć od nowa z czystymi danymi przykładowymi.", + "quota_title": "Wykorzystanie miejsca", + "quota_desc": "Tutaj możesz śledzić rozmiar swojej skrzynki pocztowej. Okrąg wypełnia się wraz ze wzrostem użycia przestrzeni." + }, + "unified_mailbox": { + "search_unavailable": "Wyszukiwanie jest niedostępne w widoku ujednoliconym" + }, + "calendar_event_notifications": { + "someone": "Ktoś", + "invited": "{name} zaprosił(a) Cię na „{title}”", + "updated": "{name} zaktualizował(a) „{title}”", + "cancelled": "{name} odwołał(a) „{title}”", + "untitled": "Wydarzenie bez tytułu" + }, + "share_notifications": { + "someone": "Ktoś", + "shared": "{name} udostępnił(a) Ci {kind} „{object}”", + "changed": "{name} zmienił(a) Twój dostęp do {kind} „{object}”", + "revoked": "{name} odebrał(a) Ci dostęp do {kind} „{object}”", + "object": { + "folder": "folder", + "calendar": "kalendarz", + "address_book": "książkę adresową", + "files": "folder plików" + } + }, + "sharing": { + "title": "Udostępnij „{name}\"", + "description": "Udziel dostępu innym użytkownikom lub grupom na tym serwerze. Zmiany są natychmiastowe.", + "no_shares": "Jeszcze nie udostępniono.", + "add_person": "Dodaj osobę lub grupę", + "search_placeholder": "Szukaj po imieniu lub e-mailu…", + "loading_principals": "Ładowanie użytkowników…", + "no_principals": "Nie znaleziono innych użytkowników ani grup.", + "no_match": "Brak wyników.", + "remove": "Usuń dostęp", + "group": "Grupa", + "share_added": "Dostęp przyznany", + "share_updated": "Dostęp zaktualizowany", + "share_removed": "Dostęp usunięty", + "share_failed": "Nie udało się zaktualizować udostępniania", + "preset": { + "freeBusy": "Tylko dostępność", + "read": "Tylko do odczytu", + "readWrite": "Odczyt i zapis", + "manager": "Menedżer", + "custom": "Niestandardowe" + } + }, + "quote_header": { + "reply_line": "{date}, {from} napisał(a):", + "forwarded_separator": "---------- Wiadomość przekazana ----------", + "from_label": "Od", + "date_label": "Data", + "subject_label": "Temat" + }, + "pwa_install": { + "title": "Zainstaluj {appName}", + "description": "Zainstaluj naszą aplikację, aby uzyskać szybki dostęp i obsługę offline.", + "not_now": "Nie teraz", + "install": "Zainstaluj", + "dont_remind": "Nie przypominaj mi więcej", + "dismiss_aria": "Zamknij monit instalacji" + }, + "deep_link": { + "message_not_found": "Ta wiadomość nie jest już dostępna.", + "thread_not_found": "Ta rozmowa nie jest już dostępna.", + "folder_not_found": "Ten folder nie jest już dostępny.", + "event_not_found": "To wydarzenie nie jest już dostępne.", + "file_not_found": "Ten plik nie jest już dostępny.", + "account_unavailable": "Zaloguj się na to konto, aby otworzyć ten link.", + "copy_message": "Kopiuj link do wiadomości", + "copy_conversation": "Kopiuj link do rozmowy", + "copy_event": "Kopiuj link do wydarzenia", + "copied": "Link skopiowany", + "copy_failed": "Nie udało się skopiować linku" + }, + "plugins": { + "oauth_callback": { + "completing": "Kończenie autoryzacji…", + "completing_message": "Proszę czekać na nawiązanie połączenia.", + "received_title": "Otrzymano autoryzację", + "received_message": "Możesz wrócić do karty, w której rozpoczęto nawiązywanie połączenia.", + "failed_title": "Autoryzacja nie powiodła się", + "denied": "Autoryzacja została odrzucona.", + "failed_detail": "Autoryzacja nie powiodła się: {error}", + "missing_params": "Brak kodu autoryzacji lub stanu.", + "delivery_failed": "Nie udało się przekazać wyniku autoryzacji do karty aplikacji. Zamknij to okno i spróbuj ponownie." + }, + "settings": { + "title": "Wtyczki", + "description": "Wtyczki dodane przez administratora. Włączaj je i wyłączaj dla swojego konta.", + "description_uploads": "Wtyczki dodają funkcje do webmaila. Włączaj je i wyłączaj dla swojego konta albo instaluj własne.", + "description_demo": "Wtyczki nie są dostępne w wersji demo.", + "install_button": "Zainstaluj wtyczkę", + "install_hint": "Wybierz plik ZIP z wtyczką.", + "install_hint_approval": "Wybierz plik ZIP z wtyczką. Zanim będzie można ją włączyć, administrator musi ją zatwierdzić.", + "install_uploads_off": "Administrator nie pozwala na instalowanie wtyczek.", + "install_managed_id": "Administrator udostępnia już wtyczkę o tej nazwie, więc nie można jej zastąpić.", + "install_invalid": "Tego pliku nie można zainstalować jako wtyczki: {details}", + "toast_installed": "Wtyczka zainstalowana. Włącz ją poniżej.", + "toast_installed_pending": "Wtyczka zainstalowana i wysłana do zatwierdzenia przez administratora.", + "toast_enabled": "Wtyczka {name} jest włączona", + "toast_disabled": "Wtyczka {name} jest wyłączona", + "toast_removed": "Usunięto wtyczkę {name}", + "toast_forced": "Administrator utrzymuje wtyczkę {name} włączoną", + "toast_approval_requested": "Wtyczka {name} czeka na zatwierdzenie przez administratora", + "empty_title": "Brak wtyczek", + "empty_admin": "Administrator nie dodał jeszcze żadnych wtyczek.", + "empty_uploads": "Na początek zainstaluj wtyczkę z pliku ZIP.", + "empty_demo": "Wtyczki wymagają prawdziwego konta, więc w wersji demo ich nie ma.", + "syncing": "Sprawdzanie wtyczek na serwerze…", + "status_installed": "Wyłączona", + "status_enabled": "Uruchamianie", + "status_running": "Włączona", + "status_disabled": "Wyłączona", + "status_error": "Problem", + "badge_forced": "Wymagana", + "badge_managed": "Od administratora", + "badge_yours": "Zainstalowana przez Ciebie", + "badge_awaiting": "Czeka na zatwierdzenie", + "toggle_label": "Włącz lub wyłącz wtyczkę {name}", + "forced_note": "Administrator wymaga tej wtyczki, więc nie można jej wyłączyć.", + "permissions": "Co może robić", + "permissions_managed": "Co może robić (zatwierdzone przez administratora)", + "settings": "Ustawienia", + "remove": "Usuń", + "remove_title": "Usunąć wtyczkę {name}?", + "remove_message": "Wtyczka i wszystko, co zapisała w tej przeglądarce, zostaną usunięte. Możesz ją później zainstalować ponownie.", + "remove_confirm": "Usuń", + "error_user_plugins_off": "Administrator wyłączył wtyczki instalowane przez użytkowników, więc ta wtyczka nie działa.", + "error_approval_unavailable": "Ta wtyczka została zainstalowana, zanim wprowadzono zatwierdzanie. Usuń ją i zainstaluj ponownie, aby wysłać ją do zatwierdzenia.", + "error_approval_denied": "Administrator nie zatwierdził tej wtyczki.", + "error_approval_pending": "Czeka na zatwierdzenie przez administratora.", + "error_auto_disabled": "Wyłączono, bo wielokrotnie zawodziło. Włącz ponownie, aby spróbować jeszcze raz.", + "accounts_heading": "Połączone konta", + "account_on": "{label} na {host}", + "account_waiting": "{label} na {host} (logowanie niedokończone)", + "disconnect": "Rozłącz", + "disconnect_failed": "Nie udało się rozłączyć {label}" + }, + "consent": { + "title": "Zezwolić wtyczce {name} na te działania?", + "intro": "Wtyczka prosi o poniższe uprawnienia. Otrzyma je tylko wtedy, gdy na to zezwolisz.", + "high_risk_note": "Oznaczone pozycje dają wtyczce dostęp do treści Twojej poczty lub pozwalają jej wysyłać dane gdzie indziej. Zezwalaj na nie tylko zaufanym wtyczkom.", + "revoke_hint": "W każdej chwili możesz wyłączyć lub usunąć wtyczkę w Ustawieniach.", + "deny": "Nie zezwalaj", + "allow": "Zezwól" + }, + "permissions": { + "none": "Nie potrzebuje specjalnych uprawnień.", + "high_risk": "Wymaga ostrożności", + "email_read": { + "title": "Czytanie poczty", + "body": "Widzi nadawców, odbiorców, tematy i pełną treść każdej wiadomości na Twoim koncie oraz Twoje foldery." + }, + "email_write": { + "title": "Zmiana i porządkowanie poczty", + "body": "Dodaje i usuwa etykiety, flagi i inne słowa kluczowe w Twoich wiadomościach, przenosi je między Twoimi folderami i do archiwum oraz zapisuje szkice." + }, + "email_send": { + "title": "Zmiana poczty przed wysłaniem", + "body": "Odczytuje i zmienia odbiorców, temat, treść i załączniki pisanych wiadomości oraz może wstrzymać wysłanie wiadomości." + }, + "crypto_full": { + "title": "Pełny dostęp do aplikacji", + "body": "Działa z tymi samymi uprawnieniami co sam webmail. Może czytać całą Twoją pocztę i zapisane klucze oraz działać w Twoim imieniu. Zezwalaj na to tylko wtyczkom, którym w pełni ufasz." + }, + "email_raw_send": { + "title": "Wysyłanie wiadomości utworzonych przez wtyczkę", + "body": "Wysyła z Twojego konta kompletne wiadomości, na przykład podpisane lub zaszyfrowane." + }, + "email_blob_read": { + "title": "Odczyt surowej treści wiadomości", + "body": "Odczytuje oryginalne dane Twoich wiadomości i załączników oraz każdego pliku, który załączysz przed wysłaniem." + }, + "email_blob_write": { + "title": "Zastępowanie załączników przed wysłaniem", + "body": "Przesyła pliki na Twój serwer poczty i zastępuje załączony plik innym, zanim wiadomość zostanie wysłana." + }, + "email_render_takeover": { + "title": "Zmiana sposobu wyświetlania wiadomości", + "body": "Zastępuje treść otwieranych wiadomości i listy wiadomości, na przykład aby pokazać odszyfrowaną zawartość." + }, + "calendar_read": { + "title": "Odczyt kalendarza", + "body": "Widzi Twoje kalendarze i ich wydarzenia." + }, + "calendar_write": { + "title": "Zmiana kalendarza", + "body": "Tworzy, zmienia i usuwa wydarzenia." + }, + "contacts_read": { + "title": "Odczyt kontaktów", + "body": "Przeszukuje i odczytuje Twoje książki adresowe." + }, + "contacts_write": { + "title": "Zmiana kontaktów", + "body": "Tworzy, zmienia i usuwa kontakty i książki adresowe." + }, + "files_read": { + "title": "Odczyt plików", + "body": "Przegląda i otwiera pliki zapisane na Twoim koncie." + }, + "files_write": { + "title": "Zmiana plików", + "body": "Przesyła, zmienia nazwy, przenosi i usuwa pliki na Twoim koncie." + }, + "identity_read": { + "title": "Podgląd tożsamości nadawcy", + "body": "Widzi adresy, z których wysyłasz wiadomości, i ich podpisy." + }, + "identity_write": { + "title": "Zmiana tożsamości nadawcy", + "body": "Tworzy, zmienia i usuwa tożsamości nadawcy i podpisy." + }, + "filters_read": { + "title": "Odczyt filtrów poczty", + "body": "Widzi reguły, które sortują Twoją przychodzącą pocztę." + }, + "filters_write": { + "title": "Zmiana filtrów poczty", + "body": "Dodaje reguły do filtrów na serwerze. Takie reguły mogą przenosić, przekazywać dalej lub usuwać przychodzącą pocztę." + }, + "tasks_read": { + "title": "Odczyt zadań", + "body": "Widzi Twoje listy zadań i zadania." + }, + "tasks_write": { + "title": "Zmiana zadań", + "body": "Tworzy, zmienia, kończy i usuwa zadania." + }, + "templates_read": { + "title": "Odczyt szablonów", + "body": "Widzi Twoje zapisane szablony wiadomości." + }, + "templates_write": { + "title": "Zmiana szablonów", + "body": "Tworzy, zmienia i usuwa szablony wiadomości." + }, + "smime_read": { + "title": "Podgląd stanu S/MIME", + "body": "Widzi, czy masz klucze i certyfikaty S/MIME, bez dostępu do kluczy." + }, + "vacation_read": { + "title": "Podgląd odpowiedzi o nieobecności", + "body": "Widzi, czy automatyczna odpowiedź jest włączona i jaka jest jej treść." + }, + "vacation_write": { + "title": "Zmiana odpowiedzi o nieobecności", + "body": "Włącza i wyłącza automatyczną odpowiedź oraz zmienia jej treść i daty." + }, + "settings_read": { + "title": "Odczyt ustawień", + "body": "Widzi Twoje preferencje i definicje etykiet." + }, + "settings_write": { + "title": "Zmiana ustawień", + "body": "Dodaje etykiety, zmienia ich kolejność i zmienia Twoje preferencje." + }, + "security_read": { + "title": "Podgląd zabezpieczeń konta", + "body": "Widzi, czy uwierzytelnianie dwuskładnikowe i szyfrowanie są włączone, bez dostępu do sekretów." + }, + "auth_observe": { + "title": "Informacja o logowaniu", + "body": "Otrzymuje informację, gdy się logujesz, przełączasz konta lub Twoja sesja zostaje odnowiona." + }, + "auth_emit": { + "title": "Wylogowywanie", + "body": "Może wylogować Cię z webmaila." + }, + "account_read": { + "title": "Podgląd kont", + "body": "Widzi dodane przez Ciebie konta wraz z ich adresami i serwerami." + }, + "http_post": { + "title": "Korzystanie z serwera webmaila w Twoim imieniu", + "body": "Wywołuje te punkty końcowe serwera z Twoimi danymi logowania:" + }, + "http_fetch": { + "title": "Łączenie z innymi witrynami", + "body": "Wysyła żądania do tych witryn. Wszystko, co wtyczka może odczytać, może tam zostać wysłane:" + }, + "admin_config": { + "title": "Odczyt ustawień od administratora", + "body": "Odczytuje ustawienia, które administrator zapisał dla tej wtyczki." + }, + "ui_observe": { + "title": "Śledzenie, gdzie jesteś w aplikacji", + "body": "Wie, która strona jest otwarta, i reaguje na klikane linki." + }, + "ui_toolbar": { + "title": "Przyciski na paskach narzędzi", + "body": "Pokazuje własne przyciski na pasku narzędzi wiadomości, przy otwartej wiadomości i na górnym pasku." + }, + "ui_app_top_banner": { + "title": "Baner u góry", + "body": "Pokazuje komunikat na górze aplikacji." + }, + "ui_email_banner": { + "title": "Komunikaty przy wiadomościach", + "body": "Pokazuje własne komunikaty i podsumowania nad częściami otwartej wiadomości lub konwersacji, w nich i między nimi." + }, + "ui_email_footer": { + "title": "Treść pod wiadomościami", + "body": "Pokazuje własną treść pod otwartą wiadomością." + }, + "ui_contact_cryptokeys": { + "title": "Klucze szyfrowania kontaktów", + "body": "Pokazuje klucze szyfrowania kontaktu i pozwala nimi zarządzać." + }, + "ui_email_details": { + "title": "Panele w szczegółach wiadomości", + "body": "Pokazuje własny panel obok lub pod otwartą wiadomością." + }, + "ui_download_file": { + "title": "Zapisywanie plików na urządzeniu", + "body": "Udostępnia do pobrania utworzone przez siebie pliki." + }, + "ui_message_list_tabs": { + "title": "Karty w Odebranych", + "body": "Dzieli Odebrane na karty, takie jak Aktualizacje czy Oferty." + }, + "ui_composer_toolbar": { + "title": "Elementy w edytorze wiadomości", + "body": "Pokazuje własne przyciski, pozycje menu i sekcje podczas pisania wiadomości." + }, + "ui_composer_sidebar": { + "title": "Panel w edytorze wiadomości", + "body": "Pokazuje własny panel obok pisanej wiadomości." + }, + "ui_sidebar_widget": { + "title": "Elementy na pasku bocznym", + "body": "Pokazuje własne pola i sekcje na pasku bocznym poczty." + }, + "ui_settings_section": { + "title": "Strona ustawień", + "body": "Pokazuje własną stronę w Ustawieniach." + }, + "ui_context_menu": { + "title": "Pozycje w menu wiadomości", + "body": "Dodaje pozycje do menu otwieranego kliknięciem wiadomości prawym przyciskiem myszy." + }, + "ui_navigation_rail": { + "title": "Przycisk na pasku nawigacyjnym", + "body": "Pokazuje własny przycisk na pasku nawigacyjnym." + }, + "ui_keyboard": { + "title": "Skróty klawiszowe", + "body": "Reaguje na zdefiniowane przez siebie skróty klawiszowe." + }, + "ui_calendar_action": { + "title": "Elementy w widoku kalendarza", + "body": "Pokazuje własne przyciski i sekcje przy wydarzeniach oraz krótkie etykiety i kropki przy dniach w kalendarzu." + }, + "ui_admin_page": { + "title": "Strona administracyjna", + "body": "Pokazuje własną stronę w ustawieniach administracyjnych." + }, + "app_lifecycle": { + "title": "Informacja o uruchomieniu i zamknięciu aplikacji", + "body": "Otrzymuje informację, gdy aplikacja się uruchamia, przechodzi w tryb offline lub gdy się wylogowujesz." + }, + "compose_open": { + "title": "Otwieranie edytora wiadomości", + "body": "Otwiera nową wiadomość z wypełnionymi odbiorcami, treścią lub załącznikami. O wysłaniu nadal decydujesz Ty." + }, + "compose_read": { + "title": "Czytanie pisanej wiadomości", + "body": "Czytanie tekstu wiadomości podczas pisania, gdy używasz wtyczki w edytorze. Bez podpisu i cytowanej wiadomości." + }, + "compose_write": { + "title": "Zmiana pisanej wiadomości", + "body": "Zastępowanie lub dodawanie tekstu w wiadomości podczas pisania, gdy używasz wtyczki w edytorze. Każdą zmianę możesz cofnąć, a o wysłaniu nadal decydujesz Ty." + }, + "ui_notifications": { + "title": "Wyświetlanie powiadomień", + "body": "Wyświetla powiadomienia na urządzeniu lub w aplikacji, jeśli ich nie zezwolono. Każde podaje nazwę wtyczki." + }, + "ui_message_list": { + "title": "Elementy na liście wiadomości", + "body": "Pokazuje własną treść nad listą wiadomości oraz przyciski przy wiadomościach i na pasku narzędzi zaznaczonych wiadomości." + }, + "ui_contacts": { + "title": "Elementy w widoku kontaktów", + "body": "Pokazuje własne sekcje w szczegółach kontaktu i przyciski na liście kontaktów." + }, + "ui_tasks": { + "title": "Elementy w widoku zadań", + "body": "Pokazuje własne sekcje w szczegółach zadania i przyciski na liście zadań." + }, + "ui_files": { + "title": "Elementy w widoku plików", + "body": "Pokazuje własne sekcje w podglądzie pliku i pozycje w menu pliku." + }, + "ui_commands": { + "title": "Własne polecenia", + "body": "Dodaje własne pozycje do palety poleceń (Ctrl+K)." + }, + "ui_app_page": { + "title": "Własne strony", + "body": "Dodaje do aplikacji własne strony, każdą z przyciskiem na pasku nawigacyjnym." + }, + "http_proxy": { + "title": "Korzystanie z połączonych przez Ciebie kont", + "body": "Wysyła żądania przez serwer Bulwark z użyciem konta lub klucza skonfigurowanego przez Ciebie lub Twojego administratora. Wtyczka nigdy nie widzi hasła, ale może zrobić na tym serwerze wszystko, co może to konto." + } + }, + "dialog": { + "from_plugin": "Od wtyczki {name}", + "ok": "OK", + "submit": "Wyślij", + "confirm": "Potwierdź", + "cancel": "Anuluj", + "close": "Zamknij", + "turn_off": "Wyłącz {name}", + "plugin_menu": "Opcje dla {name}", + "password_note": "Ten formularz pochodzi z wtyczki {name}, a nie z poczty. Wpisz hasło tylko wtedy, gdy ufasz tej wtyczce.", + "toast_auto_disabled": "{name} wyłączono, bo wielokrotnie zawodziło." + }, + "connect": { + "title": "Połącz z {label}", + "intro_password": "{name} chce używać Twojego konta {label}. Bulwark przechowuje hasło na swoim serwerze i wysyła je tylko do {host}. Wtyczka nigdy go nie widzi.", + "intro_password_any": "{name} chce używać Twojego konta {label}. Bulwark przechowuje hasło na swoim serwerze i wysyła je tylko do serwera, który tu wpiszesz. Wtyczka nigdy go nie widzi.", + "intro_signin": "{name} chce zalogować się na Twoje konto {label}. Wpisz tu serwer, a potem zaloguj się na jego stronie. Bulwark przechowuje uzyskany dostęp na swoim serwerze; wtyczka nigdy go nie widzi.", + "server": "Adres serwera", + "server_hint": "cloud.example.com", + "username": "Nazwa użytkownika", + "secret": "Hasło lub klucz", + "connect": "Połącz", + "continue": "Dalej", + "cancel": "Anuluj", + "saving": "Zapisywanie…", + "error_server": "Wpisz adres serwera, na przykład https://cloud.example.com", + "error_save": "Nie udało się zapisać: {error}" + } + }, + "global_search": { + "title": "Szukaj wszędzie", + "placeholder": "Szukaj w poczcie, kontaktach, kalendarzu, plikach...", + "scope_label": "Szukaj w", + "account_label": "Konto", + "scope_all": "Wszystko", + "scope_mail": "Poczta", + "scope_contacts": "Kontakty", + "scope_calendar": "Kalendarz", + "scope_files": "Pliki", + "all_accounts": "Wszystkie konta", + "show_all": "Pokaż wszystkie wyniki", + "no_results": "Brak wyników dla „{query}”", + "server_hint": "Całe słowa przeszukują serwer; części słów pasują do już wczytanych elementów", + "searching": "Szukanie...", + "account_error": "Nie udało się przeszukać {account}", + "recent_searches": "Ostatnie wyszukiwania", + "recurring": "Cykliczne", + "no_subject": "(no subject)", + "empty_hint": "Szukaj poczty, kontaktów, wydarzeń i plików na wszystkich kontach. Wypróbuj from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Płasko według daty", + "grouped": "Pogrupowane", + "load_more": "Wczytaj więcej", + "preview_empty": "Wybierz wynik, aby zobaczyć podgląd", + "open_full": "Otwórz", + "view_label": "Widok" + }, + "top_bar": { + "more": "Więcej opcji" + }, + "plugin_pages": { + "loading": "Wczytywanie strony", + "not_available_title": "Ta strona jest niedostępna", + "not_available_description": "Wtyczka, która ją dodała, jest wyłączona lub nie jest już zainstalowana.", + "back_to_mail": "Wróć do poczty", + "settings_group": "Wtyczki" + }, + "command_palette": { + "title": "Paleta poleceń", + "open": "Otwórz paletę poleceń", + "placeholder": "Wpisz polecenie", + "no_results": "Brak pasujących poleceń", + "go_to": "Przejdź do: {name}", + "compose": "Nowa wiadomość", + "search": "Szukaj w poczcie", + "theme_dark": "Przełącz na ciemny motyw", + "theme_light": "Przełącz na jasny motyw", + "shortcuts": "Pokaż skróty klawiszowe", + "group_navigation": "Przejdź do", + "group_actions": "Działania", + "group_plugins": "Wtyczki" + }, + "notification_center": { + "title": "Powiadomienia", + "mark_all_read": "Oznacz wszystkie jako przeczytane", + "clear": "Wyczyść", + "empty": "Brak powiadomień", + "close": "Zamknij", + "unread": "Nieprzeczytane", + "unread_count": "Nieprzeczytane: {count}", + "level_error": "Błąd", + "level_warning": "Ostrzeżenie" + } +} diff --git a/locales/pt/common.json b/locales/pt/common.json index cb364b48..0289a36d 100644 --- a/locales/pt/common.json +++ b/locales/pt/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Cliente de webmail minimalista que usa o protocolo JMAP", - "login": { - "title": "Webmail", - "username_label": "E-mail", - "username_placeholder": "usuario@exemplo.com", - "password_label": "Senha", - "password_placeholder": "Digite sua senha", - "jmap_endpoint_label": "Servidor JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "O servidor deve permitir solicitações CORS deste domínio.", - "jmap_server_label": "Servidor", - "jmap_server_auto_picked": "Servidor selecionado a partir do domínio do seu e-mail.", - "sign_in": "Entrar", - "signing_in": "Entrando...", - "loading": "Carregando...", - "reconnecting": "Conexão perdida. Tentando reconectar…", - "error": { - "invalid_credentials": "E-mail ou senha inválidos", - "connection_failed": "Falha ao conectar com o servidor", - "cors_blocked": "O servidor está acessível mas está bloqueando requisições de origem cruzada. Verifique as configurações de CORS do seu servidor JMAP e permita este domínio.", - "server_error": "O servidor está temporariamente indisponível. Tente novamente mais tarde.", - "generic": "Ocorreu um erro. Por favor, tente novamente.", - "totp_required": "É necessário um código de autenticação de dois fatores. Insira seu código abaixo.", - "totp_invalid": "Código de autenticação inválido. Verifique seu aplicativo de autenticação.", - "oauth_discovery_failed": "SSO está ativado mas o provedor de identidade não pôde ser contatado. Verifique sua configuração OAuth." - }, - "show_password": "Mostrar senha", - "hide_password": "Ocultar senha", - "totp_toggle": "Tenho um código 2FA", - "remember_me": "Lembrar-me", - "config_error": { - "title": "Erro de Configuração", - "fetch_failed": "Não foi possível carregar a configuração do aplicativo. Por favor, tente novamente mais tarde.", - "server_not_configured": "O servidor de e-mail não foi configurado. Por favor, contate seu administrador." - }, - "remove_from_history": "Remover do histórico", - "totp_label": "Código de autenticação", - "totp_placeholder": "000000", - "session_expired": "Sua sessão expirou. Faça login novamente.", - "dismiss": "Fechar", - "or": "ou", - "sign_in_sso": "Entrar com SSO", - "add_account_title": "Adicionar conta", - "add_account_subtitle": "Entrar com outra conta", - "cancel": "Cancelar", - "website": "Site", - "imprint": "Informações legais", - "privacy_policy": "Política de privacidade", - "try_demo": "Experimentar demo", - "demo_description": "Explore com dados de exemplo - sem conta necessária", - "demo_launching": "Iniciando demo...", - "demo_login_button": "Iniciar demo", - "demo_tagline": "Experimente um cliente de e-mail completo. Sem necessidade de conta.", - "demo_no_signup": "Sem registo - explore livremente com dados de exemplo", - "oauth_completing": "Concluindo login...", - "oauth_error": { - "title": "Falha na autenticação", - "invalid_state": "A validação de segurança falhou. Tente entrar novamente.", - "missing_params": "Dados de autorização ausentes. Tente entrar novamente.", - "token_exchange_failed": "Não foi possível concluir a autenticação. Tente novamente.", - "access_denied": "Acesso negado. Entre em contato com seu administrador.", - "back_to_login": "Voltar ao login" - } - }, - "sidebar": { - "close": "Fechar", - "compose": "Escrever", - "compose_hint": "Compor (c)", - "search_placeholder": "Buscar e-mails...", - "search_placeholder_hint": "Pesquisar e-mail... (pressione /)", - "storage": "Armazenamento", - "storage_used": "Usado", - "storage_free": "Livre", - "storage_total": "Total", - "sign_out": "Sair", - "sign_out_of": "Sair de {account}", - "sign_out_all": "Sair de todas as contas", - "add_account": "Adicionar conta", - "set_as_default": "Definir como padrão", - "switch_account": "Trocar conta", - "contacts": "Contatos", - "calendar": "Calendário", - "settings": "Configurações", - "admin": "Admin", - "files": "Arquivos", - "loading_mailboxes": "Carregando caixas de entrada...", - "push_connected": "Atualizações em tempo real ativas", - "push_disconnected": "Atualizações em tempo real inativas", - "keyboard_shortcuts": "Atalhos de teclado", - "theme": { - "light": "Modo claro", - "dark": "Modo escuro", - "system": "Tema do sistema" - }, - "language": { - "title": "Idioma" - }, - "mailboxes": { - "inbox": "Caixa de Entrada", - "sent": "Enviados", - "drafts": "Rascunhos", - "trash": "Lixeira", - "archive": "Arquivo", - "starred": "Com Estrela", - "all_mail": "Todos os E-mails", - "spam": "Spam", - "important": "Importante" - }, - "unified_inbox": "Caixa de entrada unificada", - "unified_sent": "Todos os enviados", - "unified_drafts": "Todos os rascunhos", - "unified_trash": "Todas as lixeiras", - "unified_archive": "Todos os arquivos", - "unified_junk": "Todo o spam", - "all_accounts": "Todas as contas", - "unified_mailbox": "Caixa de correio unificada", - "expand": "Expandir", - "collapse": "Recolher", - "expand_tooltip": "Expandir", - "collapse_tooltip": "Recolher", - "mobile": { - "search": "Buscar", - "compose": "Escrever", - "go_back": "Voltar", - "toggle_menu": "Alternar menu" - }, - "clear_search": "Limpar busca", - "vacation_active": "Resposta automática ativa", - "demo_banner": "Modo de demonstração", - "demo_reset": "Repor", - "demo_tour": "Tour", - "tags": "Etiquetas", - "show_all_tags": "Mostrar tudo ({count})", - "show_fewer_tags": "Mostrar menos", - "folders": "Pastas", - "mail": "E-mail", - "nav_label": "Navegação", - "add_app": "Apps", - "shared": "Compartilhado", - "scheduled": "Agendados", - "unified_all_unread": "Tudo não lido", - "unified_all_starred": "Tudo com estrela", - "unified_all_mail": "Todo o correio", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Aplicativos padrão", - "description": "Escolha se links de e-mail e calendário devem abrir no Bulwark. Tecnicamente, o Bulwark se registra como manipulador de protocolo para links mailto: e webcal:.", - "unsupported": "Este navegador ou esta conexão não oferece suporte ao registro manual de manipuladores de protocolo. Talvez você ainda consiga usar o PWA instalado pelas configurações do navegador ou do sistema.", - "mailto_label": "Links de e-mail", - "mailto_description": "Abre links mailto: no Bulwark com o editor preenchido previamente.", - "protocol_open_mode_label": "Ao abrir links de protocolo", - "protocol_open_mode_description": "Escolha se o Bulwark abre links mailto: e webcal: em uma nova guia ou reutiliza uma sessão aberta. A opção de sessão ativa precisa da permissão de notificações para que você possa clicar em uma notificação alternativa e trazer o Bulwark para a frente se o navegador bloquear o foco.", - "protocol_open_mode_active_session": "Abrir na sessão ativa se possível", - "protocol_open_mode_new_tab": "Sempre abrir uma nova guia", - "focus_notification_title": "Abrir Bulwark", - "focus_notification_body": "O link foi aberto no Bulwark. Clique para trazer a janela para a frente.", - "webcal_label": "Links de calendário", - "webcal_description": "Abre links webcal: no Bulwark com uma janela de assinatura de calendário preenchida previamente.", - "register_mailto": "Registrar aplicativo de e-mail", - "register_webcal": "Registrar aplicativo de calendário", - "mailto_registered": "Registro do manipulador de e-mail solicitado", - "webcal_registered": "Registro do manipulador de calendário solicitado", - "registration_failed": "Falha ao registrar manipulador de protocolo", - "opening_mailto": "Abrindo editor...", - "opening_webcal": "Abrindo calendário...", - "browser_note": "Seu navegador ou sistema operacional pode pedir confirmação e pode exigir que o Bulwark esteja instalado antes de poder ser selecionado como aplicativo padrão.", - "select_account_title": "Escolher conta", - "select_mailto_account": "Escolha qual conta deve abrir este link de e-mail.", - "select_webcal_account": "Escolha qual conta deve abrir este link de calendário.", - "select_account_note": "Esta escolha se aplica apenas a este link de protocolo.", - "detail_to": "Para", - "detail_subject": "Assunto", - "detail_no_subject": "Sem assunto", - "detail_calendar": "Calendário", - "detail_source": "Fonte", - "active_account": "Ativa", - "switching_account": "Alternando conta..." - }, - "sidebar_apps": { - "modal_title": "Apps da barra lateral", - "add_new": "Adicionar app", - "edit_app": "Editar app", - "name_label": "Nome", - "name_placeholder": "Meu app", - "name_required": "Nome é obrigatório", - "url_label": "URL", - "url_required": "URL é obrigatório", - "url_invalid": "Insira uma URL http ou https válida", - "icon_label": "Ícone", - "icon_required": "Ícone é obrigatório", - "open_mode_label": "Modo de abertura", - "open_new_tab": "Nova aba", - "open_inline": "Integrado", - "cancel": "Cancelar", - "add": "Adicionar", - "update": "Atualizar", - "delete": "Excluir", - "delete_confirm_title": "Excluir app", - "delete_confirm": "Tem certeza de que deseja excluir \"{name}\"?", - "no_apps": "Nenhum app adicionado", - "no_apps_hint": "Adicione apps e links personalizados à sua barra lateral", - "managed_title": "Fornecidas pelo seu administrador", - "managed_description": "Estas aplicações estão configuradas para todos e não podem ser alteradas.", - "managed_badge": "Gerida", - "search_icons": "Pesquisar ícones...", - "show_popular": "Populares", - "show_all": "Todos", - "no_icons_found": "Nenhum ícone encontrado", - "inline_badge": "Integrado", - "tab_badge": "Aba", - "show_on_mobile": "Mostrar no celular" - }, - "email_list": { - "no_emails": "Nenhuma mensagem encontrada", - "no_emails_description": "Esta caixa de correio está vazia", - "no_search_results": "Nenhum resultado encontrado", - "no_search_results_description": "Tente ajustar sua pesquisa ou filtros", - "loading": "Carregando e-mails...", - "unread": "não lido", - "to_me": "Para mim", - "to_recipients": "Para {count} destinatários", - "and_others": "e {count} outros", - "draft": "Rascunho", - "starred": "Com Estrela", - "pinned": "Fixado", - "replied": "Respondido", - "forwarded": "Encaminhado", - "has_attachment": "Com anexo", - "conversations_count": "{count} de {total} conversas", - "conversations_count_plus": "{count}+ conversas", - "conversations_count_simple": "{count} conversas", - "no_conversations": "Nenhuma conversa", - "loading_more": "Carregando mais e-mails...", - "no_more_emails": "Não há mais e-mails para carregar", - "batch_actions": { - "select": "Selecionar e-mails", - "select_all": "Selecionar tudo", - "selected_messages": "{count, plural, one {1 e-mail selecionado} other {# e-mails selecionados}}", - "mark_read": "Marcar como lido", - "mark_unread": "Marcar como não lido", - "delete": "Excluir", - "delete_confirm_title": "Excluir e-mails", - "delete_confirm_message": "Tem certeza que deseja excluir {count, plural, one {1 e-mail} other {# e-mails}}?", - "clear_selection": "Limpar seleção" - }, - "permanent_delete": "Excluir permanentemente", - "permanent_delete_confirm_title": "Excluir permanentemente", - "permanent_delete_confirm_message": "Este e-mail será permanentemente excluído. Esta ação não pode ser desfeita.", - "permanent_delete_confirm_batch_message": "Estes {count, plural, one {1 e-mail} other {# e-mails}} serão permanentemente excluídos. Esta ação não pode ser desfeita.", - "empty_folder": { - "button": "Esvaziar pasta", - "confirm_title": "Esvaziar pasta", - "confirm_message": "Todos os e-mails nesta pasta serão permanentemente excluídos. Esta ação não pode ser desfeita.", - "confirm_button": "Esvaziar pasta", - "junk_hint": "Você pode esvaziar a pasta de Spam para remover permanentemente todas as mensagens.", - "trash_hint": "Você pode esvaziar a Lixeira para remover permanentemente todas as mensagens." - }, - "no_scheduled_emails": "Nenhum e-mail agendado", - "no_scheduled_emails_description": "Mensagens agendadas para mais tarde aparecerão aqui.", - "scheduled_count": "{count, plural, one {1 e-mail agendado} other {# e-mails agendados}}", - "scheduled_actions_hint": "Mensagens agendadas podem ser canceladas, reagendadas ou editadas pelas ações de agendamento.", - "cancel_scheduled_send": "Cancelar envio", - "reschedule_send": "Reagendar", - "cancel_and_edit": "Cancelar e editar", - "cancel_and_compose_again": "Cancelar e escrever novamente", - "reschedule_prompt": "Informe uma nova data/hora, ex. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "O remetente solicita uma confirmação de leitura:", - "send": "Enviar confirmação", - "ignore": "Ignorar", - "sent": "Confirmação de leitura enviada.", - "send_failed": "Não foi possível enviar a confirmação de leitura", - "mdn_subject": "Lido: {subject}", - "mdn_body": "Este é um aviso de leitura da mensagem que você enviou para {recipient}.\n\nObservação: este aviso apenas confirma que a mensagem foi exibida no computador do destinatário. Não há garantia de que o destinatário tenha lido ou compreendido o conteúdo." - }, - "no_email_selected": "Nenhum e-mail selecionado", - "no_email_description": "Selecione um e-mail da lista para visualizá-lo aqui", - "no_conversation_selected": "Nenhuma conversa selecionada", - "no_conversation_description": "Escolha uma conversa da lista para lê-la aqui", - "compose": "Redigir", - "compose_hint": "Redigir nova mensagem", - "no_subject": "(Sem Assunto)", - "no_body_content": "(Sem conteúdo disponível)", - "no_preview_available": "Pré-visualização não disponível", - "show_quoted_text": "Mostrar texto citado", - "hide_quoted_text": "Ocultar texto citado", - "loading_email": "Carregando e-mail...", - "loading": "Carregando...", - "reply": "Responder", - "reply_all": "Responder a Todos", - "forward": "Encaminhar", - "delete": "Excluir", - "archive": "Arquivar", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Adicionar Estrela", - "unstar": "Remover Estrela", - "mark_unread": "Marcar como não lido", - "mark_read": "Marcar como lido", - "unread": "Não lido", - "read": "Lido", - "spam_short": "Spam", - "not_spam_short": "Não spam", - "move": "Mover", - "print": "Imprimir", - "view_source": "Ver código-fonte", - "fullscreen": "Tela cheia", - "exit_fullscreen": "Sair da tela cheia", - "export_email": "Exportar como .eml", - "forward_as_attachment": "Encaminhar como anexo", - "import_email": "Importar .eml ou .zip", - "keyboard_shortcuts": "Atalhos de teclado (?)", - "email_source": "Código-fonte do E-mail", - "draft_banner": "Esta mensagem é um rascunho", - "edit_draft": "Editar", - "copy_source": "Copiar para a área de transferência", - "source_copied": "Código-fonte copiado para a área de transferência", - "attachments": "Anexos", - "important": "Importante", - "download": "Baixar", - "download_all": "Baixar tudo", - "from": "De", - "to": "Para", - "cc": "CC", - "bcc": "CCO", - "date": "Data", - "subject": "Assunto", - "show_details": "Mostrar detalhes", - "hide_details": "Ocultar detalhes", - "external_content_warning": "Imagens e conteúdo externo foram bloqueados", - "load_external_content": "Carregar imagens", - "trust_sender": "Sempre confiar neste remetente", - "back_to_list": "Voltar para a lista", - "view_contact": "Ver contato", - "message_details": "Detalhes da Mensagem", - "more_reply_options": "Mais opções de resposta", - "set_tag": "Definir etiqueta", - "tag": "Etiqueta", - "more_actions": "Mais ações", - "move_to": "Mover para...", - "remove_tag": "Remover etiqueta", - "tag_filter_placeholder": "Filtrar etiquetas", - "tag_no_matches": "Nenhuma etiqueta correspondente", - "more_count": "+{count} mais", - "characters_count": "{count} caracteres", - "quick_reply_placeholder": "Escreva uma resposta rápida...", - "more_options": "Mais opções", - "sending": "Enviando...", - "security_authentication": "Segurança & Autenticação", - "technical_details": "Detalhes Técnicos", - "message_id_label": "Message-ID:", - "reply_to_label": "Responder para:", - "delivery_time_label": "Horário de entrega:", - "conversation_part_label": "Parte da conversa:", - "previous_messages": "{count} mensagem anterior", - "previous_messages_plural": "{count} mensagens anteriores", - "time": { - "day": "dia", - "days": "dias", - "hour": "hora", - "hours": "horas", - "minute": "minuto", - "minutes": "minutos" - }, - "unknown_sender": "Desconhecido", - "recipient_me": "eu", - "recipient_and_others": "{name} e {count} outros", - "recipient_to_prefix": "Para:", - "authentication": { - "title": "Autenticação", - "status": { - "verified": "Verificado", - "warning": "Aviso", - "none": "Não autenticado" - }, - "spf": { - "pass": "SPF Aprovado", - "fail": "SPF Falhou", - "none": "Sem SPF" - }, - "dkim": { - "pass": "DKIM Válido", - "fail": "DKIM Inválido", - "none": "Sem DKIM" - }, - "dmarc": { - "pass": "DMARC Aprovado", - "fail": "DMARC Falhou", - "none": "Sem DMARC" - }, - "spam_score": "Pontuação de Spam", - "tooltip_spf": "Sender Policy Framework: verifica se o servidor de envio está autorizado a enviar e-mails em nome do domínio", - "tooltip_dkim": "DomainKeys Identified Mail: confirma, por meio de assinatura criptográfica, que o e-mail não foi alterado durante o transporte", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: garante que SPF e DKIM estejam alinhados com o domínio do remetente e define uma política para falhas", - "policy": "Política", - "result": { - "pass": "Aprovado", - "fail": "Falhou", - "softfail": "Falha parcial", - "neutral": "Neutro", - "permerror": "Erro permanente", - "temperror": "Erro temporário", - "none": "Nenhum" - } - }, - "headers": { - "routing": "Roteamento", - "received": "Recebido", - "message_id": "ID da Mensagem", - "list_info": "Informações da Lista" - }, - "tooltips": { - "reply": "Responder", - "reply_all": "Responder a todos (a)", - "forward": "Encaminhar (f)", - "archive": "Arquivar", - "delete": "Excluir", - "star": "Favoritar (s)", - "unstar": "Remover favorito (s)", - "compose": "Compor (c)", - "previous": "E-mail anterior", - "next": "Próximo e-mail", - "edit_draft": "Editar rascunho" - }, - "spam": { - "button_title": "Reportar spam", - "not_spam_title": "Marcar como legítimo", - "toast_success": "Movido para Spam", - "toast_batch": "{count} e-mails movidos para Spam", - "toast_undo": "Desfazer", - "toast_not_spam_success": "Movido para Caixa de Entrada", - "toast_not_spam_batch": "{count} e-mails movidos para Caixa de Entrada", - "error": "Falha ao reportar spam", - "error_not_spam": "Falha ao restaurar e-mail" - }, - "unsubscribe_banner": { - "label": "Newsletter", - "button": "Cancelar inscrição", - "confirm_title": "Cancelar inscrição deste remetente?", - "confirm_button": "Confirmar", - "cancel": "Cancelar", - "success_http": "Página de cancelamento aberta em nova aba", - "success_mailto": "E-mail de cancelamento enviado", - "confirm_message_http": "A página de cancelamento será aberta em uma nova aba.", - "confirm_message_mailto": "Um e-mail de cancelamento será enviado ao remetente.", - "error": "Não foi possível cancelar a inscrição", - "dismiss": "Dispensar" - }, - "calendar_invitation": { - "loading": "Carregando detalhes do evento…", - "title": "Convite de calendário", - "published_title": "Evento publicado", - "response_title": "Resposta do evento", - "update_title": "Atualização do evento", - "counter_title": "Contraproposta", - "refresh_title": "Solicitação de atualização", - "declined_counter_title": "Contraproposta recusada", - "cancelled_title": "Evento cancelado", - "organizer": "Organizado por {name}", - "organizer_label": "Organizado por", - "attendees": "{count, plural, one {# participante} other {# participantes}}", - "accept": "Aceitar", - "maybe": "Talvez", - "decline": "Recusar", - "add_to_calendar": "Adicionar ao calendário", - "added": "Adicionado ao calendário", - "rsvp_sent": "Resposta enviada", - "parse_error": "Não foi possível ler o convite", - "action_failed": "Não foi possível concluir essa ação do calendário.", - "no_calendar": "Calendário não disponível", - "published_info": "Este evento foi compartilhado como referência.", - "response_info": "Esta mensagem contém a resposta de um participante.", - "response_info_organizer": "Esta resposta de participante atualiza seu evento.", - "update_info": "Esta mensagem atualiza um evento existente.", - "counter_info": "Esta mensagem propõe alterações em um evento.", - "counter_info_organizer": "Este participante propôs alterações ao seu evento.", - "refresh_info": "Esta mensagem solicita os detalhes mais recentes do evento.", - "refresh_info_organizer": "Um participante solicitou os detalhes mais recentes do evento.", - "declined_counter_info": "O organizador recusou uma contraproposta.", - "authentication_failed_info": "As verificações de autenticação deste convite falharam. Trate as ações de calendário com cautela.", - "authentication_missing_info": "Este convite não inclui autenticação de email verificada. Confirme os detalhes com o organizador se algo parecer incomum.", - "sender_mismatch_info": "Este convite foi enviado por {sender}, enquanto o organizador indicado nos dados do calendário é {organizer}.", - "sender_mismatch_unverified_info": "Este convite foi enviado por {sender}, enquanto o organizador indicado nos dados do calendário é {organizer}, e a mensagem não pôde ser verificada.", - "organizer_role": "Você organiza este evento", - "your_response": "Sua resposta: {status}", - "response_needed": "Resposta necessária", - "response_accepted": "Aceita", - "response_tentative": "Provisória", - "response_declined": "Recusada", - "response_delegated": "Delegada", - "actor_sent_info": "Enviado por {name}.", - "actor_response_info": "{name} respondeu {status}.", - "actor_counter_info": "{name} propôs alterações para este evento.", - "actor_refresh_info": "{name} pediu os detalhes mais recentes do evento.", - "actor_declined_counter_info": "{name} recusou a contraproposta.", - "actor_note": "Nota: {comment}", - "actor_unknown": "Alguém", - "proposed_changes": "Alterações propostas", - "change_title": "Título", - "change_time": "Horário", - "change_location": "Local", - "change_description": "Descrição", - "change_empty": "Nenhuma", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Aplicar alterações propostas", - "proposal_applied": "As alterações propostas foram aplicadas.", - "review_proposal": "Revisar proposta", - "review_request": "Revisar solicitação", - "view_in_calendar": "Ver no calendário", - "select_calendar": "Selecionar calendário", - "already_in_calendar": "Já está no seu calendário", - "request_info": "Você foi convidado para este evento. Responda para informar o organizador sobre sua disponibilidade.", - "cancel_info": "O organizador cancelou este evento.", - "event_updated": "Atualização #{sequence}", - "event_status_tentative": "Provisório", - "event_status_cancelled": "Cancelado", - "collapse": "Ocultar detalhes", - "expand": "Mostrar detalhes" - }, - "previous": "Anterior", - "next": "Próximo", - "send": "Enviar", - "more": "mais", - "details": { - "recipients_routing": "Destinatários e roteamento", - "authentication_security": "Autenticação e segurança", - "identifiers_threading": "Identificadores e conversação", - "mailing_list": "Lista de discussão", - "message_properties": "Propriedades da mensagem", - "sent": "Enviado", - "received": "Recebido", - "delivery_time": "Tempo de entrega", - "in_reply_to": "Em resposta a", - "references": "Referências", - "thread_id": "ID da conversa", - "size": "Tamanho", - "mime_type": "Tipo MIME", - "attachments_summary": "{count} arquivos · {size}", - "list_id": "ID da lista", - "list_help": "Ajuda da lista", - "list_post": "Postagem na lista", - "list_unsubscribe": "Cancelar inscrição", - "iprev": "DNS reverso", - "spam_status": "Status de spam", - "ai_verdict": "Veredito da IA", - "account": "Conta", - "no_subject": "(sem assunto)" - }, - "scheduled_banner": "Agendado para enviar em {date}", - "scheduled_send_created": "E-mail agendado para envio", - "cancel_scheduled_send": "Cancelar envio", - "reschedule_send": "Reagendar", - "cancel_and_edit": "Cancelar e editar", - "cancel_and_compose_again": "Cancelar e escrever novamente", - "reschedule_prompt": "Informe uma nova data/hora, ex. 2026-05-04T15:30", - "scheduled_actions_only": "A visualização Agendados permite apenas ações de agendamento", - "undo_send_scheduled": "Mensagem agendada para envio", - "undo_send": "Desfazer envio", - "contact_sidebar": { - "title": "Contato", - "close": "Fechar barra lateral", - "action_email": "E-mail", - "action_email_title": "Enviar e-mail", - "action_copy": "Copiar", - "action_copy_title": "Copiar e-mail", - "action_edit_title": "Editar contato", - "section_emails": "E-mails", - "section_phones": "Telefones", - "section_organizations": "Organizações", - "section_addresses": "Endereços", - "section_notes": "Notas", - "not_in_contacts": "Não está nos seus contatos", - "add_to_contacts": "Adicionar aos contatos", - "copied": "Copiado!", - "copy_failed": "Falha ao copiar" - }, - "send_now": "Enviar agora" - }, - "email_composer": { - "read_receipt_on": "Confirmação de leitura solicitada (clique para desativar)", - "read_receipt_off": "Solicitar confirmação de leitura", - "dsn_on": "Notificação de entrega pedida (clique para desativar)", - "dsn_off": "Pedir notificação de entrega", - "format_plain_text": "Mudar para texto simples", - "format_rich_text": "Mudar para texto formatado (HTML)", - "require_tls_on": "Entrega cifrada obrigatória (clique para desativar)", - "require_tls_off": "Exigir entrega cifrada (TLS)", - "new_message": "Nova Mensagem", - "signature_edit_hint": "Clique duas vezes para editar a assinatura", - "reply": "Responder", - "reply_all": "Responder a Todos", - "forward": "Encaminhar", - "reply_to": "Responder", - "reply_all_to": "Responder a Todos", - "forward_message": "Encaminhar", - "from": "De", - "to": "Para", - "cc": "CC", - "bcc": "CCO", - "subject": "Assunto", - "body_placeholder": "Escreva sua mensagem...", - "send": "Enviar", - "cancel": "Cancelar", - "attach": "Anexar", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Descartar", - "discard_draft_title": "Descartar rascunho?", - "discard_draft_confirm": "Você tem alterações não salvas. Deseja descartar este rascunho?", - "saving": "Salvando...", - "draft_saved": "Rascunho salvo", - "save_failed": "Falha ao salvar", - "to_placeholder": "Endereços de e-mail dos destinatários", - "cc_placeholder": "Destinatários CC", - "bcc_placeholder": "Destinatários CCO", - "subject_placeholder": "Assunto", - "cc_label": "CC:", - "bcc_label": "CCO:", - "subject_label": "Assunto:", - "file_size_kb": "KB", - "prefix": { - "forward": "Enc:", - "reply": "Re:" - }, - "no_subject": "(Sem Assunto)", - "unknown_sender": "Desconhecido", - "quote": { - "reply_header": "Em {date}, {sender} escreveu:", - "forward_header": "---------- Mensagem encaminhada ----------", - "from": "De: {sender}", - "date": "Data: {date}", - "subject": "Assunto: {subject}", - "to": "Para: {recipients}" - }, - "remove_sub_address": "Remover sub-endereço", - "from_override": { - "toggle_off": "Substituir", - "toggle_on": "Cancelar substituição", - "toggle_tooltip": "Edite livremente o nome e o endereço do remetente. O email ainda é enviado através da sua identidade.", - "name_label": "Nome do remetente", - "name_placeholder": "Nome", - "email_label": "Endereço de email do remetente", - "email_placeholder": "alias@example.com", - "envelope_notice": "Se o seu servidor não aceitar este endereço como remetente do envelope, {identity} é usado em seu lugar, e os destinatários podem vê-lo no cabeçalho Return-Path." - }, - "use_template": "Modelo", - "save_as_template": "Salvar como modelo", - "validation": { - "recipient_required": "Adicione um destinatário para enviar", - "body_required": "Escreva uma mensagem ou anexe um arquivo", - "attachments_uploading": "Os anexos ainda estão sendo enviados — será enviado assim que terminar", - "attachment_upload_failed": "Não enviado - falha ao carregar um anexo. Remova-o e tente novamente." - }, - "upload_progress": "Enviando {uploaded} / {total}", - "upload_cancel": "Cancelar envio", - "upload_failed": "Falha ao enviar {filename}", - "attachment_too_large": "\"{name}\" é maior do que o servidor permite ({max} por ficheiro)", - "attachments_total_too_large": "Os anexos excederiam o limite do servidor de {max} por mensagem", - "send_failed": "Falha ao enviar o e-mail", - "smime_sign_on": "Assinatura S/MIME ativada", - "smime_sign_off": "Assinatura S/MIME desativada", - "smime_encrypt_on": "Criptografia S/MIME ativada", - "smime_encrypt_off": "Criptografia S/MIME desativada", - "smime_encrypt_unavailable": "Criptografia S/MIME indisponível – certificados de destinatário ausentes", - "smime_unlock_title": "Desbloquear chave S/MIME", - "smime_unlock_message": "Insira a frase secreta para desbloquear sua chave S/MIME.", - "smime_unlock_button": "Desbloquear", - "smime_passphrase_placeholder": "Frase secreta S/MIME", - "send_timeout": "O servidor deixou de responder. Verifique a pasta Enviados antes de enviar novamente — a mensagem pode já ter saído.", - "continue_draft": "Continuar rascunho", - "close_draft_title": "Salvar ou descartar rascunho?", - "close_draft_message": "Você tem alterações não salvas. Deseja salvar como rascunho ou descartar?", - "drop_files": "Solte arquivos para anexar", - "show_less": "Mostrar menos", - "forgot_attachment": { - "title": "Esqueceu um anexo?", - "message": "A sua mensagem menciona \"{keyword}\" mas nenhum arquivo está anexado. Enviar mesmo assim?", - "send_anyway": "Enviar mesmo assim", - "back": "Voltar à edição" - }, - "empty_subject": { - "title": "Enviar sem assunto?", - "message": "Esta mensagem não tem assunto. Enviar mesmo assim?", - "dont_ask_again": "Não perguntar novamente", - "send_anyway": "Enviar mesmo assim", - "back": "Voltar à edição" - }, - "add_link": "Adicionar link", - "link_url_prompt": "Insira a URL", - "sending": "Enviando...", - "schedule_send": "Agendar envio", - "schedule_send_description": "Escolha quando o servidor deve liberar esta mensagem.", - "schedule_send_required": "Escolha uma data e hora.", - "schedule_send_invalid": "Informe uma data e hora válidas.", - "schedule_send_future": "Escolha uma data e hora no futuro.", - "schedule_send_too_late": "Este horário é posterior ao permitido pelo servidor.", - "schedule_send_unsupported": "Envio agendado não é compatível com esta conta.", - "schedule_send_cleanup_warning": "O envio agendado foi criado, mas a limpeza do rascunho falhou.", - "send_delay_unsupported": "Atraso de envio não é compatível com esta conta.", - "send_delay_unsupported_confirm": "Esta conta não oferece atraso de envio. Enviar imediatamente?", - "recipient_edit_email": "Editar endereço de e-mail", - "recipient_edit_name": "Editar nome de exibição", - "recipient_email_placeholder": "Endereço de e-mail", - "recipient_name_placeholder": "Nome de exibição", - "autocomplete_search_server": "Pesquisar no servidor", - "autocomplete_searching": "Pesquisando...", - "toolbar": { - "bold": "Negrito", - "italic": "Itálico", - "underline": "Sublinhado", - "strikethrough": "Tachado", - "text_color": "Cor do texto", - "remove_color": "Remover cor", - "heading_1": "Título 1", - "heading_2": "Título 2", - "bullet_list": "Lista com marcadores", - "ordered_list": "Lista numerada", - "quote": "Citação", - "code_block": "Bloco de código", - "align_left": "Alinhar à esquerda", - "align_center": "Centralizar", - "align_right": "Alinhar à direita", - "text_direction": "Direção do texto (RTL/LTR)", - "link": "Link", - "table": "Tabela", - "clear_formatting": "Limpar formatação", - "undo": "Desfazer", - "redo": "Refazer", - "add_row_above": "Adicionar linha acima", - "add_row_below": "Adicionar linha abaixo", - "add_column_before": "Adicionar coluna antes", - "add_column_after": "Adicionar coluna depois", - "delete_row": "Excluir linha", - "delete_column": "Excluir coluna", - "toggle_header_row": "Alternar linha de cabeçalho", - "delete_table": "Excluir tabela", - "pick_size": "Escolher tamanho", - "font_size": "Tamanho da fonte", - "font_size_default": "Padrão", - "background_color": "Cor de fundo", - "remove_background_color": "Remover cor de fundo" - }, - "send_filing_warning": "Enviado - mas a limpeza posterior falhou, um rascunho antigo pode permanecer." - }, - "confirm_dialog": { - "confirm": "Confirmar", - "cancel": "Cancelar" - }, - "common": { - "loading": "Carregando...", - "error": "Erro", - "success": "Sucesso", - "cancel": "Cancelar", - "save": "Salvar", - "delete": "Excluir", - "edit": "Editar", - "close": "Fechar", - "search": "Buscar", - "refresh": "Atualizar", - "settings": "Configurações", - "help": "Ajuda", - "logout": "Sair", - "yes": "Sim", - "no": "Não", - "unknown": "Desconhecido", - "app_title": "Webmail", - "reconnecting": "Conexão perdida. Tentando reconectar…", - "rate_limited_title": "A autenticação do servidor está temporariamente limitada.", - "rate_limited_detail": "O Bulwark pausou as solicitações em segundo plano para evitar bloqueio. Nova tentativa em {seconds}s.", - "rate_limited_action_title": "Solicitação pausada para evitar bloqueio.", - "rate_limited_action_detail": "O Bulwark está aguardando o fim do cooldown do servidor antes de enviar mais solicitações autenticadas. Tente novamente em {seconds}s.", - "pull_to_refresh": "Puxe para atualizar", - "release_to_refresh": "Solte para atualizar", - "refreshing": "Atualizando…" - }, - "notifications": { - "email_sent": "E-mail enviado com sucesso", - "email_deleted": "E-mail excluído", - "email_archived": "E-mail arquivado", - "emails_deleted": "{count, plural, one {# e-mail excluído} other {# e-mails excluídos}}", - "emails_archived": "{count, plural, one {# e-mail arquivado} other {# e-mails arquivados}}", - "error_archiving": "Falha ao arquivar o e-mail", - "error_updating": "Falha ao atualizar o e-mail", - "email_starred": "E-mail marcado com estrela", - "email_unstarred": "Estrela removida do e-mail", - "email_marked_read": "E-mail marcado como lido", - "email_marked_unread": "E-mail marcado como não lido", - "copied_to_clipboard": "Copiado para a área de transferência", - "source_copied": "Código-fonte copiado para a área de transferência", - "error_sending": "Falha ao enviar e-mail", - "error_deleting": "Falha ao excluir e-mail", - "error_loading": "Falha ao carregar e-mails", - "new_email": "Novo e-mail", - "new_email_from": "De {sender}", - "click_to_view": "Clique para visualizar", - "email_moved": "E-mail movido", - "emails_moved": "{count} e-mails movidos", - "moved_to_mailbox": "Movido para {mailbox}", - "move_failed": "Falha ao mover", - "move_error": "Não foi possível mover os e-mails para a pasta selecionada", - "email_tagged": "E-mail etiquetado", - "emails_tagged": "{count} e-mails etiquetados", - "tag_failed": "Falha ao etiquetar", - "identity_created": "Identidade criada com sucesso", - "identity_updated": "Identidade atualizada com sucesso", - "identity_deleted": "Identidade excluída", - "identity_set_primary": "Identidade principal atualizada", - "identity_create_failed": "Falha ao criar identidade: {error}", - "identity_update_failed": "Falha ao atualizar identidade: {error}", - "identity_delete_failed": "Falha ao excluir identidade: {error}", - "identity_unauthorized": "Você não está autorizado a enviar deste endereço de e-mail", - "identity_not_found": "Identidade não encontrada", - "vacation_saved": "Configurações de resposta automática salvas", - "vacation_save_failed": "Falha ao salvar configurações de resposta automática", - "filters_saved": "Filtros salvos com sucesso", - "filters_save_failed": "Falha ao salvar os filtros", - "filters_deleted": "Regra de filtragem eliminada", - "templates_exported": "Modelos exportados com sucesso", - "templates_imported": "{count, plural, one {# modelo importado} other {# modelos importados}}", - "templates_import_errors": "Alguns modelos não puderam ser importados", - "templates_import_empty": "Nenhum modelo encontrado no arquivo", - "export_email_error": "Falha ao exportar o e-mail", - "import_email_success": "E-mail importado com sucesso", - "import_email_error": "Falha ao importar o e-mail" - }, - "date": { - "today": "Hoje", - "yesterday": "Ontem", - "this_week": "Esta semana", - "last_week": "Semana passada", - "this_month": "Este mês", - "older": "Mais antigos", - "just_now": "Agora mesmo", - "minutes_ago": "{count} minuto atrás", - "minutes_ago_plural": "{count} minutos atrás", - "hours_ago": "{count} hora atrás", - "hours_ago_plural": "{count} horas atrás", - "days_ago": "{count} dia atrás", - "days_ago_plural": "{count} dias atrás" - }, - "language": { - "title": "Idioma", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Selecionar idioma", - "switch_to_english": "Mudar para Inglês", - "switch_to_french": "Mudar para Francês", - "switch_to_japanese": "Mudar para Japonês", - "switch_to_spanish": "Mudar para Espanhol", - "switch_to_italian": "Mudar para Italiano", - "switch_to_german": "Mudar para Alemão", - "switch_to_dutch": "Mudar para Holandês", - "switch_to_portuguese": "Mudar para Português", - "switch_to_russian": "Mudar para Russo", - "switching": "Alterando idioma...", - "switch": "Alterar idioma", - "current": "Idioma atual", - "polish": "Polski", - "switch_to_polish": "Mudar para polonês", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Mudar para persa", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Mudar para mongol" - }, - "settings": { - "title": "Configurações", - "back_to_mail": "Voltar para E-mails", - "save_success": "Configurações salvas com sucesso", - "import_success": "Configurações importadas com sucesso", - "import_error": "Falha ao importar configurações", - "reset_confirm": "Tem certeza de que deseja redefinir todas as configurações para os padrões?", - "unsaved_changes": "Você tem alterações não salvas", - "discard_changes": "Descartar alterações?", - "discard": "Descartar", - "keep_editing": "Continuar editando", - "tabs": { - "appearance": "Aparência", - "language": "Idioma, Região e Hora", - "email": "Comportamento de E-mail", - "composer": "Editor", - "privacy": "Privacidade e Segurança", - "account": "Conta", - "identities": "Identidades", - "vacation": "Resposta automática", - "advanced": "Avançado", - "calendar": "Calendário", - "filters": "Filtros", - "templates": "Modelos", - "folders": "Pastas", - "keywords": "Etiquetas", - "security": "Segurança", - "encryption": "Criptografia", - "files": "Arquivos", - "contacts": "Contatos", - "protocol_handlers": "Aplicativos padrão", - "sidebar_apps": "Apps da barra lateral", - "notifications": "Notificações", - "layout": "Layout", - "reading": "Leitura", - "composing": "Composição", - "downloads": "Downloads", - "content_senders": "Conteúdo e remetentes", - "about_data": "Sobre e dados", - "debug": "Depuração" - }, - "tab_groups": { - "general": "Geral", - "account": "Conta e identidade", - "organization": "Organização de e-mail", - "apps": "Aplicativos", - "system": "Sistema", - "appearance": "Aparência", - "mail": "Correio", - "privacy": "Privacidade e segurança", - "advanced": "Avançado" - }, - "appearance": { - "message_list_order": { - "label": "Ordem da lista de mensagens", - "description": "Como as mensagens são ordenadas na lista. A ordenação é feita pelo servidor, por isso abrange toda a pasta e não apenas as mensagens carregadas.", - "preset": { - "chronological": "Cronológica (mais recentes primeiro)", - "unread_first": "Não lidas primeiro", - "starred_first": "Com estrela primeiro", - "tagged_first": "Com etiqueta primeiro", - "custom": "Personalizada" - }, - "tag_label": "Etiqueta", - "advanced_show": "Avançado: editar níveis de ordenação", - "advanced_hide": "Ocultar avançado", - "criterion_label": "Critério do nível {n}", - "direction_label": "Direção do nível {n}", - "criteria": { - "unread": "Não lida", - "starred": "Com estrela", - "tag": "Etiqueta", - "receivedAt": "Data de receção", - "sentAt": "Data de envio", - "from": "Remetente", - "subject": "Assunto", - "size": "Tamanho" - }, - "direction": { - "unread_first": "Não lidas primeiro", - "read_first": "Lidas primeiro", - "starred_first": "Com estrela primeiro", - "unstarred_first": "Sem estrela primeiro", - "tagged_first": "Com etiqueta primeiro", - "untagged_first": "Sem etiqueta primeiro", - "newest_first": "Mais recentes primeiro", - "oldest_first": "Mais antigas primeiro", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Maiores primeiro", - "smallest_first": "Menores primeiro" - }, - "add_level": "Adicionar nível", - "remove_level": "Remover nível", - "move_up": "Mover para cima", - "move_down": "Mover para baixo", - "tiebreak_note": "Até três níveis; os níveis seguintes desempatam. «Mais recentes primeiro» é sempre aplicado por último.", - "scope": { - "label": "Aplicar a todas as pastas", - "description": "Desativado, a ordem aplica-se apenas à Caixa de entrada; as outras pastas continuam cronológicas." - }, - "unsupported_note": "O seu servidor de correio não suporta ordenação por estado de leitura, estrelas ou etiquetas. Esses níveis são ignorados e a lista mantém-se cronológica." - }, - "title": "Aparência", - "description": "Personalize a aparência do seu webmail", - "theme": { - "label": "Tema", - "description": "Escolha seu esquema de cores preferido", - "light": "Claro", - "dark": "Escuro", - "system": "Sistema" - }, - "language": { - "label": "Idioma", - "description": "Escolha seu idioma preferido" - }, - "font_size": { - "label": "Tamanho da Fonte", - "description": "Ajuste o tamanho do texto para melhor legibilidade", - "small": "Pequeno", - "medium": "Médio", - "large": "Grande" - }, - "list_density": { - "label": "Densidade", - "description": "Controle o espaçamento em toda a interface", - "extra_compact": "Extra compacto", - "compact": "Compacto", - "regular": "Normal", - "comfortable": "Confortável" - }, - "animations": { - "label": "Habilitar Animações", - "description": "Mostrar transições e efeitos suaves" - }, - "toolbar_position": { - "label": "Posição da barra de ferramentas", - "description": "Onde mostrar os botões de ação do e-mail (Responder, Arquivar, Excluir, etc.)", - "top": "Topo", - "below_subject": "Abaixo do assunto" - }, - "toolbar_labels": { - "label": "Mostrar etiquetas da barra de ferramentas", - "description": "Apresentar etiquetas de texto junto aos ícones da barra de ferramentas. Desativar para poupar espaço quando estiver familiarizado com os ícones." - }, - "hide_account_switcher": { - "label": "Ocultar seletor de conta da barra lateral", - "description": "Ocultar o seletor de conta no topo da barra lateral de pastas. Você ainda pode trocar de conta pela barra de navegação inferior." - }, - "show_rail_account_list": { - "label": "Mostrar avatares de conta na barra de navegação", - "description": "Exibir círculos de contas individuais na parte inferior da barra de navegação para troca rápida, com um botão de sair abaixo." - }, - "unified_mailbox": { - "label": "Caixa de correio unificada", - "description": "Mostrar pastas combinadas (Entrada, Enviados, etc.) de todas as contas conectadas", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Incluir caixas de grupo", - "description": "Incluir também as caixas partilhadas ou de grupo na vista unificada." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Ícones coloridos na barra lateral", - "description": "Colorir ícones de pastas e etiquetas por tipo (Caixa de entrada azul, Spam vermelho, Enviados verde, etc.). Desative para uma barra lateral monocromática." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Mostrar contagem total de mensagens", - "description": "Mostra a contagem total de mensagens ao lado de pastas e etiquetas, além da contagem de não lidas. Desative para mostrar apenas as não lidas." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Interface Pro (experimental)", - "description": "Layout para utilizadores avançados apenas em desktop com navegação de mensagens em múltiplos separadores e fluxos entre contas. A interface padrão não é afetada; pode voltar a qualquer momento.", - "open_label": "Abrir interface Pro", - "back_to_standard": "Voltar ao padrão" - }, - "cross_unread": { - "label": "Todas as contas: Não lido", - "description": "Mostra uma entrada na seção Todas as contas listando o correio não lido de todas as contas, abrangendo todas as pastas exceto spam, enviados, arquivo, lixeira e rascunhos." - }, - "cross_starred": { - "label": "Todas as contas: Com estrela", - "description": "Mostra uma entrada na seção Todas as contas listando o correio com estrela de todas as contas, abrangendo todas as pastas exceto spam, enviados, arquivo, lixeira e rascunhos." - }, - "cross_all": { - "label": "Todas as contas: Todo o correio", - "description": "Mostra uma entrada na seção Todas as contas listando todo o correio de todas as contas, abrangendo todas as pastas exceto spam, enviados, arquivo, lixeira e rascunhos." - } - }, - "keywords": { - "title": "Etiquetas de e-mail", - "description": "Defina etiquetas para organizar os seus e-mails com cores. São armazenadas como palavras-chave JMAP no servidor.", - "add_keyword": "Adicionar etiqueta", - "label_field": "Nome de exibição", - "label_placeholder": "ex. Trabalho, Pessoal, Urgente", - "id_field": "ID da etiqueta", - "id_placeholder": "ex. trabalho, pessoal", - "color_field": "Cor", - "id_exists": "Este ID de etiqueta já existe", - "edit": "Editar etiqueta", - "delete": "Eliminar etiqueta", - "save": "Salvar", - "add": "Adicionar", - "cancel": "Cancelar", - "migrating": "A atualizar etiqueta nos e-mails existentes…", - "migration_error": "Falha ao atualizar etiqueta nos e-mails existentes", - "nesting": { - "label": "Etiquetas aninhadas", - "description": "Aninhe etiquetas sob outras etiquetas e mostre-as como uma árvore na barra lateral." - }, - "parent_field": "Etiqueta principal", - "no_parent": "Sem etiqueta principal", - "too_long": "Este caminho de etiqueta é demasiado longo (no máximo {max} caracteres)", - "has_children_locked": "Existem outras etiquetas aninhadas sob esta, por isso o seu nome e a sua etiqueta principal estão bloqueados. Mova-as ou remova-as primeiro.", - "has_children_delete": "Remova primeiro as etiquetas aninhadas sob esta", - "visibility_field": "Visibilidade na barra lateral", - "visibility": { - "show": "Mostrar", - "unread": "Mostrar se não lidas", - "hide": "Ocultar" - }, - "unrecognized": { - "title": "Recuperar etiquetas do servidor", - "description": "Os nomes e as cores das etiquetas são guardados neste navegador, mas as etiquetas em si estão nos seus e-mails como palavras-chave JMAP. Analise o seu correio à procura de palavras-chave sem etiqueta definida aqui e volte a adicioná-las.", - "scan": "Procurar etiquetas", - "rescan": "Analisar novamente", - "scanning": "A analisar… {scanned} de {total} e-mails", - "none": "Todas as etiquetas dos seus e-mails já estão definidas aqui.", - "messages": "{count, plural, one {# e-mail} other {# e-mails}}", - "add_all": "Adicionar todas ({count})", - "partial": "Apenas os {scanned} e-mails mais recentes de {total} foram analisados. Podem faltar etiquetas usadas apenas em correio mais antigo.", - "error": "Não foi possível analisar o seu correio à procura de etiquetas." - } - }, - "notifications": { - "test_sound": "Testar som de notificação", - "sounds": { - "default": "Padrão (Bipe)", - "cheerful": "Alegre", - "involved": "Elaborado", - "swift": "Gesto rápido", - "relax": "Relaxante" - }, - "sound_selection": { - "title": "Som de notificação", - "description": "Escolha qual som reproduzir para notificações", - "choose": "Som", - "choose_desc": "Selecione um toque de notificação e clique no ícone do alto-falante para pré-visualizar" - }, - "email": { - "title": "Notificações de e-mail", - "description": "Configurar notificações para e-mails recebidos", - "enabled": "Notificações de e-mail", - "enabled_desc": "Mostrar notificações quando novos e-mails chegarem", - "sound": "Som de notificação", - "sound_desc": "Reproduzir um som quando novos e-mails chegarem", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Notificações de calendário", - "description": "Configurar notificações para eventos do calendário", - "enabled": "Notificações de eventos", - "enabled_desc": "Mostrar alertas para próximos eventos do calendário", - "sound": "Som de notificação", - "sound_desc": "Reproduzir um som para lembretes do calendário", - "invitation_parsing": "Analisar convites por e-mail", - "invitation_parsing_desc": "Detectar convites de calendário em anexos de e-mail e mostrar ações de calendário" - }, - "push": { - "confirm_disable_message": "Este dispositivo deixará de receber alertas quando o site estiver fechado.", - "confirm_disable_title": "Desativar notificações em segundo plano?", - "description": "Receba notificações do sistema para novas mensagens quando este site estiver fechado. Entregue através do relay push do Bulwark; o relay nunca vê o conteúdo da mensagem.", - "disable": "Desativar", - "enable": "Ativar", - "dismiss_aria": "Dispensar aviso de notificações", - "ios_hint": "No iOS, instale o site primeiro na tela inicial – o Safari só entrega Web Push para PWAs instalados.", - "reenable": "Registrar novamente", - "relay_desc": "Usa o relay Bulwark hospedado por padrão. Altere apenas se você hospedar.", - "relay_label": "Relay push", - "relay_locked": "Definido pelo administrador", - "relay_locked_desc": "A URL do relay push foi definida pelo seu administrador e não pode ser alterada.", - "status_active": "Ativo neste dispositivo", - "status_busy": "Processando…", - "status_inactive": "Não ativado neste dispositivo", - "status_unsupported": "Este navegador não oferece suporte a Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Notificações em segundo plano", - "devices_title": "Dispositivos registrados", - "devices_desc": "Todos os dispositivos registrados para receber notificações em segundo plano desta conta. Revogar um deles interrompe o recebimento imediatamente.", - "devices_loading": "Carregando dispositivos registrados…", - "devices_empty": "Nenhum dispositivo está registrado para notificações em segundo plano.", - "devices_error": "Não foi possível carregar os dispositivos registrados.", - "devices_refresh": "Atualizar", - "device_this": "Este dispositivo", - "device_other": "Outro dispositivo", - "device_status_active": "Entregando notificações", - "device_status_inactive": "Nunca entregou uma notificação", - "device_status_unknown": "Desconhecido para este relay", - "device_expires": "Expira em {date}", - "revoke": "Revogar", - "confirm_revoke_title": "Revogar este dispositivo?", - "confirm_revoke_message": "Ele deixará de receber notificações em segundo plano imediatamente. Ativar o push novamente nesse dispositivo o registrará de novo.", - "confirm_revoke_message_this": "Este dispositivo deixará de receber notificações em segundo plano imediatamente." - } - }, - "language_region": { - "title": "Idioma, Região e Hora", - "description": "Idioma, formato de data, formato de hora e outras preferências regionais", - "language": { - "label": "Idioma", - "description": "Escolha seu idioma preferido", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Formato de Data", - "description": "Como as datas são mostradas na lista de e-mails", - "smart": "Inteligente (regional)", - "relative": "Relativo (há 1 h, há 2 d)", - "full": "Sempre data completa", - "preview_today": "Hoje:", - "preview_this_week": "Esta semana:", - "preview_older": "Mais antigo:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Formato de Hora", - "description": "Escolha entre relógio de 12 ou 24 horas", - "12h": "12 horas", - "24h": "24 horas" - }, - "first_day": { - "label": "Primeiro Dia da Semana", - "description": "Começar a semana no domingo ou segunda-feira", - "sunday": "Domingo", - "monday": "Segunda-feira" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Limpar a pesquisa ao mudar de pasta", - "description": "Clicar numa pasta (Caixa de entrada, Enviados…) limpa a pesquisa ativa e mostra essa pasta. Se desativado, a pesquisa mantém-se e volta a ser executada na pasta para a qual mudar." - }, - "request_read_receipt": { - "label": "Solicitar confirmações de leitura por padrão", - "description": "Ativar previamente a solicitação de confirmação de leitura ao redigir uma nova mensagem." - }, - "read_receipt_response": { - "label": "Responder a solicitações de confirmação de leitura", - "description": "O que fazer quando uma mensagem recebida solicita uma confirmação de leitura.", - "ask": "Perguntar sempre", - "always": "Enviar sempre", - "never": "Nunca enviar" - }, - "title": "Comportamento de E-mail", - "description": "Configure como os e-mails são manipulados", - "mark_read": { - "label": "Marcar como Lido", - "description": "Quando marcar e-mails como lidos ao abri-los", - "instant": "Instantaneamente", - "delay_3s": "Após 3 segundos", - "delay_5s": "Após 5 segundos", - "never": "Nunca" - }, - "delete_action": { - "label": "Ação de Exclusão", - "description": "O que acontece quando você exclui um e-mail", - "trash": "Mover para Lixeira", - "trash_and_read": "Mover para Lixeira e marcar como lida", - "permanent": "Excluir Permanentemente", - "warning": "Os e-mails serão excluídos permanentemente e não poderão ser recuperados. Esta ação é irreversível." - }, - "message_spacing": { - "label": "Espaçamento da mensagem", - "description": "Margem ao redor do corpo da mensagem no leitor", - "auto": "Automático (recomendado)", - "always": "Sempre adicionar espaçamento", - "edge": "De borda a borda" - }, - "plain_text_font": { - "label": "Fonte de texto simples", - "description": "Fonte usada para e-mails de texto simples", - "mono": "Monoespaçada", - "sans": "Normal (igual ao aplicativo)" - }, - "archive_mode": { - "label": "Arquivar em", - "description": "Como organizar os e-mails ao arquivar", - "single": "Uma única pasta", - "year": "Uma pasta por ano", - "month": "Uma pasta por mês", - "reorganize": "Reorganizar arquivo existente", - "reorganize_success": "{count, plural, =0 {Nenhum e-mail para reorganizar} =1 {1 e-mail reorganizado} other {# e-mails reorganizados}}", - "reorganize_error": "Falha ao reorganizar o arquivo" - }, - "permanently_delete_junk": { - "label": "Excluir spam permanentemente", - "description": "Excluir permanentemente e-mails da pasta Spam em vez de movê-los para a Lixeira" - }, - "mail_layout": { - "label": "Layout do e-mail", - "description": "Escolha entre o painel de leitura dividido clássico e um fluxo de leitura focado estilo Gmail.", - "split": "Painel dividido", - "split_description": "Manter a lista de mensagens e o painel de leitura visíveis lado a lado.", - "focus": "Lista focada", - "focus_description": "Mostrar uma linha por mensagem e abrir o e-mail em largura total mantendo a barra lateral de pastas visível.", - "horizontal": "Painel de leitura em baixo", - "horizontal_description": "Mostre a lista de mensagens em cima e abra a mensagem selecionada num painel de leitura abaixo." - }, - "disable_threading": { - "label": "Desativar agrupamento de conversas", - "description": "Mostrar e-mails como mensagens individuais em vez de agrupados por conversa" - }, - "plain_text_mode": { - "label": "Apenas texto simples", - "description": "Desativar o editor de texto formatado e enviar todos os e-mails apenas como texto simples, incluindo respostas e encaminhamentos" - }, - "auto_select_reply_identity": { - "label": "Selecionar automaticamente o endereço de resposta", - "description": "Ao responder, muda automaticamente o endereço do remetente para a identidade que recebeu a mensagem original" - }, - "reply_identity_match": { - "label": "Correspondência do endereço de receção", - "description": "Quais endereços de receção contam como seus. «Apenas endereço exato» escolhe somente uma das suas identidades configuradas; «mesmo domínio» trata também qualquer outro endereço dos seus domínios de identidade como alias catch-all e reescreve o cabeçalho De com ele. Escolha o endereço exato se esses endereços forem listas de distribuição.", - "exact": "Apenas endereço exato", - "domain": "Qualquer endereço dos meus domínios" - }, - "signature_position": { - "label": "Posição da assinatura", - "description": "Onde inserir a sua assinatura em respostas e encaminhamentos. Acima do texto citado lê-se naturalmente como fecho da resposta; abaixo mantém a mensagem original contígua.", - "above_quote": "Antes do texto citado", - "below_quote": "Depois do texto citado" - }, - "signature_separator": { - "label": "Delimitador de assinatura", - "description": "Anteceder a assinatura com a linha delimitadora padrão \"-- \" (RFC 3676). Desative se preferir passar direto da mensagem para a assinatura." - }, - "sub_address_delimiter": { - "label": "Delimitador de sub-endereço", - "description": "Caractere que separa seu nome de usuário da tag de sub-endereço. Use o delimitador configurado no seu servidor de e-mail (ex.: usuario{delimiter}tag@dominio.com).", - "option": "{delimiter} (usuario{delimiter}tag@dominio.com)", - "custom": "Personalizado…", - "custom_input_label": "Caractere delimitador personalizado" - }, - "show_preview": { - "label": "Mostrar Texto de Visualização", - "description": "Exibir visualização do e-mail na lista", - "focus_description": "Exibir texto de pré-visualização em linha dentro da lista de mensagens focada de uma linha" - }, - "attachment_click_action": { - "label": "Ação ao clicar em anexos", - "description": "Escolha se clicar em um anexo abre uma pré-visualização ou inicia o download imediatamente", - "preview": "Visualizar quando possível", - "download": "Baixar imediatamente" - }, - "attachment_position": { - "label": "Posição dos anexos", - "description": "Onde exibir os anexos no cabeçalho do e-mail", - "beside-sender": "Ao lado do remetente", - "below-header": "Abaixo do cabeçalho" - }, - "emails_per_page": { - "10": "10 e-mails", - "25": "25 e-mails", - "50": "50 e-mails", - "100": "100 e-mails", - "label": "E-mails Por Página", - "description": "Número de e-mails a carregar de uma vez" - }, - "always_light_mode": { - "label": "Mostrar e-mails sempre em modo claro", - "description": "Exibir o conteúdo dos e-mails em modo claro mesmo quando o aplicativo está em modo escuro, evitando problemas de conversão" - }, - "external_content": { - "label": "Conteúdo Externo", - "description": "Como lidar com imagens e conteúdo externo", - "ask": "Sempre perguntar", - "block": "Sempre bloquear", - "allow": "Sempre permitir" - }, - "trusted_senders": { - "label": "Remetentes Confiáveis", - "description": "Gerencie remetentes cujas imagens são carregadas automaticamente", - "count_zero": "Nenhum", - "count_one": "1 remetente", - "count_other": "{count} remetentes", - "modal_title": "Remetentes Confiáveis", - "empty_title": "Nenhum remetente confiável ainda", - "empty_description": "Ao visualizar um e-mail com imagens bloqueadas, clique em \"Sempre confiar neste remetente\" para adicioná-lo aqui.", - "add_manually": "Adicionar remetente manualmente", - "add_button": "Adicionar", - "add_placeholder": "Digite o endereço de e-mail", - "search_placeholder": "Buscar remetentes...", - "no_results": "Nenhum remetente corresponde à sua busca", - "remove": "Remover", - "close": "Fechar", - "invalid_email": "Por favor, digite um endereço de e-mail válido", - "already_added": "Este remetente já é confiável", - "save_error": "Falha ao salvar - verifique o log de depuração de Contatos para detalhes", - "use_address_book_label": "Sincronizar com catálogo de endereços", - "use_address_book_description": "Armazene remetentes confiáveis em um catálogo \"Remetentes confiáveis\" dedicado para sincronizar em todos os seus dispositivos" - }, - "hover_actions": { - "label": "Ações rápidas ao passar o mouse", - "description": "Escolha quais ações rápidas aparecem ao passar o mouse sobre um e-mail na lista", - "delete": "Excluir", - "star": "Favoritar / Desfavoritar", - "mark_read": "Marcar como lido / não lido", - "archive": "Arquivar", - "tag": "Etiqueta", - "spam": "Marcar como spam", - "not_spam": "Não é spam", - "none_selected": "Nenhuma ação selecionada", - "mode_label": "Modo de exibição", - "mode_inline": "Em linha", - "mode_floating": "Flutuante", - "corner_label": "Posição flutuante", - "corner_top-left": "Superior esquerdo", - "corner_top-right": "Superior direito", - "corner_bottom-left": "Inferior esquerdo", - "corner_bottom-right": "Inferior direito" - }, - "default_mail_program": { - "label": "Programa de e-mail padrão", - "description": "Registrar {appName} como seu programa de e-mail padrão para links mailto:", - "button": "Definir como padrão", - "success": "O navegador solicitou definir como padrão", - "error": "Seu navegador não suporta esta funcionalidade" - }, - "attachment_reminder": { - "label": "Lembrete de anexo", - "description": "Avisar antes de enviar quando a mensagem menciona anexos mas nenhum está anexado", - "keywords_label": "Palavras-chave de ativação", - "keywords_description": "Palavras ou frases que ativam o lembrete quando encontradas na sua mensagem", - "add_placeholder": "Adicionar palavra-chave...", - "add": "Add", - "remove": "Remover" - }, - "empty_subject_warning": { - "label": "Aviso de assunto vazio", - "description": "Pedir confirmação antes de enviar uma mensagem sem assunto." - }, - "hide_inline_image_attachments": { - "label": "Ocultar imagens incorporadas dos anexos", - "description": "As imagens incorporadas no corpo da mensagem não são listadas como anexos separados" - }, - "attachment_image_previews": { - "label": "Mostrar prévias de imagens em anexos", - "description": "Renderizar anexos de imagem como miniaturas em vez de ícones genéricos de arquivo" - }, - "send_delay": { - "label": "Desfazer envio / atraso de envio", - "description": "Atrase envios normais com uma breve janela no servidor.", - "off": "Desativado", - "seconds": "{seconds} segundos", - "unsupported": "A conta atual não anuncia suporte a envio atrasado. A configuração continuará salva para outras contas." - }, - "return_to_list_after_action": { - "label": "Voltar à lista após eliminar ou marcar como não lido", - "description": "Depois de eliminar ou marcar como não lida a mensagem aberta, voltar à lista de mensagens em vez de abrir a mensagem seguinte." - }, - "swipe_right_action": { - "label": "Ação ao deslizar para a direita (celular)", - "description": "Em dispositivos de toque, a ação executada ao deslizar uma mensagem para a direita na lista." - }, - "swipe_left_action": { - "label": "Ação ao deslizar para a esquerda (celular)", - "description": "Em dispositivos de toque, a ação executada ao deslizar uma mensagem para a esquerda na lista." - }, - "swipe_actions": { - "none": "Nenhuma", - "archive": "Arquivar", - "delete": "Excluir", - "mark_read": "Alternar lido", - "star": "Alternar estrela", - "spam": "Marcar como spam" - }, - "rtl_editing": { - "label": "Suporte à edição da direita para a esquerda", - "description": "Adiciona um botão de direção à barra de ferramentas do editor para definir parágrafos da esquerda para a direita ou da direita para a esquerda" - } - }, - "composer": { - "title": "Editor", - "description": "Configure as configurações de composição de e-mail", - "autosave": { - "label": "Intervalo de Salvamento Automático", - "description": "Com que frequência salvar rascunhos automaticamente", - "30s": "A cada 30 segundos", - "1m": "A cada minuto", - "2m": "A cada 2 minutos", - "5m": "A cada 5 minutos" - }, - "send_confirmation": { - "label": "Confirmação de Envio", - "description": "Pedir confirmação antes de enviar e-mails" - }, - "default_reply": { - "label": "Modo de Resposta Padrão", - "description": "Ação padrão ao clicar em responder", - "reply": "Responder", - "reply_all": "Responder a Todos" - } - }, - "privacy": { - "title": "Privacidade e Segurança", - "description": "Gerencie suas configurações de privacidade e segurança", - "external_images": { - "label": "Bloquear Imagens Externas", - "description": "Prevenir rastreamento através de imagens externas" - }, - "session_timeout": { - "label": "Tempo Limite de Sessão", - "description": "Sair automaticamente após inatividade", - "never": "Nunca", - "30m": "30 minutos", - "1h": "1 hora", - "4h": "4 horas" - }, - "clear_cache": { - "label": "Limpar Cache", - "description": "Remover dados em cache e arquivos temporários", - "button": "Limpar Cache", - "confirm": "Tem certeza de que deseja limpar o cache?", - "success": "Cache limpo com sucesso" - } - }, - "account": { - "title": "Conta", - "description": "Visualize as informações da sua conta", - "name_label": "Nome de exibição", - "username_label": "Nome de usuário", - "account_type_label": "Tipo de conta", - "auth_method_label": "Autenticação", - "auth_method_oauth": "Login único (OAuth/OIDC)", - "auth_method_basic": "Senha", - "demo_account": "Conta de demonstração", - "email": { - "label": "Endereço de E-mail", - "value": "{email}" - }, - "server": { - "label": "Servidor JMAP", - "value": "{server}" - }, - "storage": { - "label": "Uso de Armazenamento", - "used": "{used} de {total} usado", - "percentage": "{percent}% usado" - }, - "last_sync": { - "label": "Última Sincronização", - "value": "{time}" - }, - "accounts": { - "title": "Contas conectadas", - "description": "Arraste para reordenar como as contas aparecem no menu suspenso", - "active": "Conta atualmente ativa", - "default_badge": "Conta padrão", - "set_default": "Definir como padrão", - "switch_to": "Mudar para esta conta", - "move_up": "Mover para cima", - "move_down": "Mover para baixo", - "drag_handle": "Arraste para reordenar", - "add": "Adicionar conta" - }, - "shared_accounts": { - "title": "Compartilhado comigo", - "description": "Contas de grupo e compartilhadas que você pode gerenciar. Selecione uma para editar seus filtros, resposta de ausência, calendários e contatos.", - "shared_label": "Conta compartilhada" - } - }, - "security": { - "title": "Segurança da conta", - "description": "Gerencie sua senha, autenticação de dois fatores e configurações de segurança", - "detecting": "Detectando recursos do servidor...", - "not_available": "O gerenciamento de segurança da conta não está disponível para este servidor de e-mail. As permissões necessárias podem estar desabilitadas. Consulte a documentação para obter detalhes.", - "password": { - "title": "Alterar senha", - "current": "Senha atual", - "new": "Nova senha", - "confirm": "Confirmar nova senha", - "submit": "Alterar senha", - "success": "Senha alterada com sucesso", - "error_title": "Falha ao alterar a senha", - "error_mismatch": "As novas senhas não coincidem", - "error_min_length": "A senha deve ter pelo menos 8 caracteres", - "error_generic": "Não foi possível alterar a senha" - }, - "display_name": { - "label": "Nome de exibição", - "description": "Seu nome como aparece no servidor", - "placeholder": "Digite seu nome de exibição", - "save": "Salvar", - "success": "Nome de exibição atualizado", - "error": "Não foi possível atualizar o nome de exibição" - }, - "totp": { - "section_title": "Autenticação de dois fatores", - "label": "Autenticação TOTP", - "description": "Adicione uma camada extra de segurança com uma senha única baseada em tempo", - "active": "Habilitado", - "inactive": "Desabilitado", - "enabled": "Autenticação de dois fatores habilitada", - "disabled": "Autenticação de dois fatores desabilitada", - "enable_error": "Não foi possível habilitar a 2FA", - "disable_error": "Não foi possível desabilitar a 2FA", - "setup_instructions": "Copie esta URL para seu aplicativo de autenticação (Google Authenticator, Authy, etc.):", - "verification_code": "Código de verificação", - "confirm": "Confirmar", - "disable": "Desativar", - "disable_confirm_prompt": "Digite sua senha para desativar a autenticação de dois fatores.", - "password_required": "A senha é obrigatória", - "code_required": "O código de verificação é obrigatório", - "code_invalid": "Código de verificação inválido. Verifique seu aplicativo autenticador e tente novamente." - }, - "app_passwords": { - "title": "Senhas de aplicativo", - "description": "Crie senhas para aplicativos que não suportam autenticação de dois fatores", - "add": "Adicionar", - "create": "Criar", - "cancel": "Cancelar", - "generate": "Gerar", - "name_label": "Nome do aplicativo", - "name_placeholder": "ex. Thunderbird, iPhone Mail", - "password_label": "Senha (deixe vazio para auto-gerar)", - "password_placeholder": "Auto-gerada se vazio", - "added": "Senha de aplicativo criada", - "removed": "Senha de aplicativo removida", - "add_error": "Não foi possível criar a senha de aplicativo", - "remove_error": "Não foi possível remover a senha de aplicativo", - "none": "Nenhuma senha de aplicativo configurada", - "done": "Concluído", - "expires_label": "Expira (opcional)", - "copy_now_warning": "Copie esta senha agora - ela não será exibida novamente.", - "allowed_ips_label": "IPs permitidos (opcional)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Separados por vírgula ou espaço. Deixe vazio para permitir qualquer IP." - }, - "api_keys": { - "title": "Chaves de API", - "description": "Crie chaves de API para scripts e integrações que se comunicam diretamente com o servidor", - "name_label": "Nome da chave", - "name_placeholder": "ex. Script de backup, CI runner", - "copy_now_warning": "Copie esta chave de API agora - ela não será mostrada novamente.", - "added": "Chave de API criada", - "removed": "Chave de API removida", - "add_error": "Falha ao criar chave de API", - "remove_error": "Falha ao remover chave de API", - "none": "Nenhuma chave de API configurada" - }, - "encryption": { - "section_title": "Criptografia em repouso", - "label": "Criptografia de e-mail", - "description": "Criptografe e-mails armazenados no servidor para maior privacidade", - "active": "Criptografia {type} habilitada", - "inactive": "Desabilitado", - "enabled": "Criptografia em repouso habilitada", - "disabled_success": "Criptografia em repouso desabilitada", - "error": "Não foi possível atualizar as configurações de criptografia", - "algorithm_label": "Algoritmo de criptografia", - "encrypt_on_append": "Criptografar novos e-mails ao carregar", - "allow_spam_training": "Permitir treino de spam antes de criptografar os e-mails", - "enable_encryption": "Ativar criptografia", - "enable_tooltip": "Ativar criptografia com esta chave", - "configure_title": "Ativar criptografia com esta chave", - "enable_button": "Ativar", - "disable_tooltip": "Desativar criptografia", - "enabled_success": "Criptografia em repouso habilitada" - }, - "email_client": { - "title": "Configuração do Cliente de E-mail", - "description": "Use estas credenciais para configurar seu cliente de e-mail desktop ou móvel (Thunderbird, Apple Mail, Outlook, etc.)", - "jmap_username_label": "Nome de Usuário JMAP", - "copy": "Copiar", - "copied": "Copiado", - "password_instructions": "Use seu nome de usuário JMAP acima junto com uma senha de aplicativo para entrar no seu cliente de e-mail. Crie uma senha de aplicativo na seção acima se ainda não tiver uma." - }, - "link_device": { - "title": "Vincular aplicativo móvel", - "description": "Entre no aplicativo móvel Bulwark Mail sem digitar nada. Gere um código QR aqui e escaneie-o na tela de login do aplicativo.", - "generate": "Mostrar código QR", - "regenerate": "Mostrar um novo código", - "instructions": "Abra o aplicativo Bulwark Mail, toque em \"Escanear código QR\" na tela de login e aponte sua câmera para cá.", - "expires_in": "Este código expira em {seconds} segundos. Ele só pode ser usado uma vez.", - "expired": "Este código expirou.", - "generating": "Gerando…", - "error": "Não foi possível criar um código de pareamento. Tente novamente." - }, - "public_keys": { - "name_label": "Nome da chave pública", - "name_placeholder": "ex. A minha chave", - "key_label": "Chave pública (S/MIME ou PGP em formato ASCII)", - "key_placeholder": "Cole aqui a sua chave pública", - "none": "Nenhuma chave pública configurada" - } - }, - "identities": { - "title": "Identidades de Envio", - "description": "Gerencie endereços de e-mail que você pode usar para enviar", - "identities_count": { - "label": "Suas Identidades", - "description": "Endereços de e-mail configurados para envio", - "count_zero": "Nenhuma identidade", - "count_one": "1 identidade", - "count_other": "{count} identidades" - }, - "manage": "Gerenciar Identidades", - "sub_addressing": { - "label": "Sub-Endereçamento", - "description": "Use tags como usuario+tag@dominio.com para organizar e-mails recebidos", - "learn_more": "Saiba Mais" - } - }, - "vacation": { - "title": "Resposta automática", - "description": "Responder automaticamente a e-mails recebidos enquanto estiver ausente", - "loading": "Carregando configurações de resposta automática...", - "not_supported": "Seu servidor de e-mail não suporta respostas automáticas de ausência.", - "fetch_error": "Falha ao carregar as configurações de resposta automática. Tente novamente.", - "status": { - "label": "Resposta automática", - "description": "Enviar uma resposta automática para quem lhe enviar um e-mail", - "active": "Ativo", - "inactive": "Inativo" - }, - "date_range": { - "title": "Período", - "description": "Opcionalmente limitar a resposta automática a um período específico", - "start": "Data de início", - "start_description": "Deixe vazio para sem limite de início", - "end": "Data de fim", - "end_description": "Deixe vazio para sem limite de fim" - }, - "message": { - "title": "Mensagem de resposta automática", - "description": "A mensagem que será enviada como resposta", - "subject_label": "Assunto", - "subject_description": "Linha de assunto da resposta automática", - "subject_placeholder": "Fora do escritório", - "body_label": "Corpo da mensagem", - "body_description": "Conteúdo da mensagem em texto simples", - "body_placeholder": "Obrigado pelo seu e-mail. Estou atualmente fora do escritório e responderei quando retornar.", - "html_label": "Mensagem formatada (HTML)", - "html_description": "Adicione uma versão formatada, com links e estilo. Os destinatários cujo cliente de e-mail não a exibir verão o texto simples acima.", - "html_placeholder": "Escreva uma resposta de ausência formatada…" - }, - "preview": { - "title": "Pré-visualização", - "show": "Mostrar pré-visualização", - "hide": "Ocultar pré-visualização" - }, - "save": "Salvar alterações", - "saving": "Salvando...", - "warnings": { - "end_before_start": "A data de fim deve ser posterior à data de início", - "start_in_past": "A data de início está no passado", - "empty_body": "O corpo da mensagem está vazio - os destinatários receberão uma resposta em branco", - "subject_too_long": "O assunto é demasiado longo para o servidor (no máximo {max} bytes).", - "body_too_long": "A mensagem é demasiado longa para o servidor (no máximo {max} bytes, incluindo a formatação)." - } - }, - "folders": { - "role_memos": "Notas", - "title": "Pastas", - "description": "Gerencie suas pastas de e-mail e atribua funções padrão", - "folder_list": "Suas pastas", - "folder_list_description": "Clique no ícone de uma pasta para personalizá-la", - "standard_roles": "Funções padrão de pastas", - "standard_roles_description": "Defina quais pastas são utilizadas para funções padrão como Caixa de entrada, Enviados, Lixeira, etc.", - "role_inbox": "Caixa de entrada", - "role_drafts": "Rascunhos", - "role_sent": "Enviados", - "role_trash": "Lixeira", - "role_junk": "Spam / Lixo eletrônico", - "role_archive": "Arquivo", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Nenhum", - "create_folder": "Criar pasta", - "create_subfolder": "Criar subpasta", - "reorder": "Arraste para reordenar", - "reorder_error": "Falha ao reordenar as pastas", - "move_error": "Falha ao mover a pasta", - "subfolder_of": "Dentro de {name}", - "subfolder_name": "Nome da subpasta", - "new_folder_name": "Nome da pasta", - "rename": "Renomear", - "change_icon": "Alterar ícone", - "delete": "Excluir", - "confirm_delete": "Tem certeza de que deseja excluir \"{name}\"? Os e-mails nesta pasta serão movidos para a Lixeira.", - "create": "Criar", - "cancel": "Cancelar", - "no_folders": "Sem pastas personalizadas", - "cannot_delete_role": "Não é possível excluir uma pasta com uma função padrão. Remova a função primeiro.", - "folder_created": "Pasta criada", - "folder_renamed": "Pasta renomeada", - "folder_deleted": "Pasta excluída", - "role_updated": "Função da pasta atualizada", - "error_create": "Falha ao criar a pasta", - "error_rename": "Falha ao renomear a pasta", - "error_delete": "Falha ao excluir a pasta", - "error_delete_has_children": "Não é possível excluir a pasta: ela ainda contém subpastas. Exclua ou mova-as primeiro.", - "error_delete_has_email": "Não é possível excluir a pasta: ela ainda contém e-mails. Mova ou exclua-os primeiro.", - "error_role": "Falha ao atualizar a função da pasta" - }, - "advanced": { - "title": "Avançado", - "description": "Opções avançadas e configurações de desenvolvedor", - "debug_mode": { - "label": "Modo de Depuração", - "description": "Habilitar registro detalhado para solução de problemas" - }, - "debug_categories": { - "description": "Selecione quais categorias registrar. Desative as categorias de que você não precisa para reduzir o ruído no console.", - "jmap": "Cliente JMAP", - "jmap_description": "Operações de caixa de correio, busca de e-mails e solicitações do protocolo JMAP", - "calendar": "Calendário", - "calendar_description": "Eventos do calendário, importações e mensagens de agendamento", - "tasks": "Tarefas", - "tasks_description": "Criação, busca e atualização de tarefas do calendário", - "auth": "Autenticação", - "auth_description": "Login, TOTP, troca de tokens e gerenciamento de sessão", - "filters": "Filtros", - "filters_description": "Regras de filtro Sieve e scripts de férias", - "email": "Visualização de email", - "email_description": "Renderização de emails, processamento TNEF e marcação como lido", - "push": "Notificações push", - "push_description": "Configuração e entrega de notificações push", - "contacts": "Contatos e catálogos de endereços", - "contacts_description": "Sincronização de contatos, operações do catálogo e remetentes confiáveis" - }, - "settings_sync": { - "label": "Sincronização de configurações", - "description": "Sincronize suas configurações entre navegadores e dispositivos" - }, - "sender_favicons": { - "label": "Favicons de remetente", - "description": "Exibir ícones de sites como fotos de perfil para remetentes empresariais" - }, - "show_avatars_in_junk": { - "label": "Mostrar avatares na pasta de spam", - "description": "Exibir imagens de perfil e favicons dos remetentes na pasta de spam. Desativado por padrão para não dar aparência legítima a tentativas de phishing." - }, - "keyboard_shortcuts": { - "label": "Atalhos de Teclado", - "description": "Visualizar atalhos de teclado disponíveis", - "button": "Ver Atalhos" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Redefinir Configurações", - "description": "Restaurar todas as configurações para valores padrão", - "button": "Redefinir para Padrões" - }, - "export_settings": { - "label": "Exportar Configurações", - "description": "Baixar suas configurações como JSON", - "button": "Exportar" - }, - "import_settings": { - "label": "Importar Configurações", - "description": "Fazer upload de configurações de arquivo JSON", - "button": "Importar" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Apps da barra lateral", - "description": "Gerenciar aplicativos e links personalizados na barra lateral", - "keep_loaded": "Manter apps carregados", - "keep_loaded_description": "Manter apps inline em execução em segundo plano ao alternar entre eles para evitar recarregamento", - "managed_title": "Fornecidas pelo seu administrador", - "managed_description": "Estas aplicações estão configuradas para todos e não podem ser alteradas.", - "manage_title": "Apps personalizados", - "manage_description": "Adicionar, editar ou remover apps personalizados da barra lateral" - }, - "contacts": { - "title": "Contatos", - "description": "Importar e exportar os seus contatos", - "import_label": "Importar contatos", - "import_description": "Importar contatos de um arquivo vCard (.vcf)", - "export_label": "Exportar contatos", - "export_description": "Exportar todos os contatos como arquivo vCard (.vcf)", - "manage_title": "Catálogos de endereços", - "manage_description": "Renomeie seus catálogos de endereços", - "no_address_books": "Nenhum catálogo de endereços encontrado", - "categories_title": "Categorias", - "categories_description": "Renomear categorias de contatos", - "no_categories": "Nenhuma categoria encontrada", - "group_by_letter_description": "Mostrar cabeçalhos de seção alfabéticos na lista de contatos", - "group_by_letter_label": "Agrupar pela primeira letra", - "sort_by_last_name_label": "Ordenar por sobrenome", - "sort_by_last_name_description": "Ordenar a lista de contatos por sobrenome para que os membros da família apareçam juntos" - }, - "downloads": { - "title": "Downloads", - "description": "Personalize como os e-mails e anexos baixados são nomeados.", - "reset": "Restaurar padrão", - "preview": "Pré-visualização:", - "email_template": { - "label": "Nome do arquivo de e-mail (.eml)", - "description": "Modelo usado ao exportar um e-mail ou arrastá-lo para o sistema de arquivos. A extensão .eml é adicionada automaticamente." - }, - "attachment_template": { - "label": "Nome do arquivo do anexo", - "description": "Modelo usado ao baixar ou arrastar um anexo. Se você omitir '{filename}' e '{ext}', a extensão original é preservada." - }, - "bundle_template": { - "label": "Nome do arquivo .zip multi-e-mails", - "description": "Modelo usado ao arrastar ou baixar vários e-mails selecionados como um único arquivo .zip. A extensão .zip é adicionada automaticamente." - }, - "spaces": { - "label": "Espaços", - "description": "Substitui espaços no nome de arquivo resultante por outro caractere.", - "keep": "Manter espaços", - "underscore": "Substituir por _", - "dash": "Substituir por -" - }, - "lowercase": { - "label": "Minúsculas", - "description": "Forçar todo o nome do arquivo para minúsculas." - }, - "strip_diacritics": { - "label": "Remover acentos", - "description": "Converte letras acentuadas para o equivalente ASCII (ä → a, é → e). Útil para ferramentas que estragam nomes Unicode." - }, - "collapse_separators": { - "label": "Combinar separadores repetidos", - "description": "Combina sequências de espaços, sublinhados ou hifens em um único caractere." - }, - "after_export": { - "label": "Após exportar", - "description": "Opcionalmente, mover o e-mail depois de exportá-lo como .eml.", - "keep": "Manter na caixa", - "archive": "Mover para o arquivo", - "trash": "Mover para a lixeira" - } - }, - "filters": { - "title": "Filtros de e-mail", - "description": "Crie regras para classificar, rotular e gerenciar automaticamente os e-mails recebidos", - "add_rule": "Adicionar regra", - "no_rules": "Nenhuma regra de filtro", - "vacation_active": "O respondedor de ausência está ativo", - "vacation_active_description": "A resposta automática está ativada para mensagens recebidas", - "vacation_configure": "Configurar", - "no_rules_description": "Crie regras para organizar automaticamente seus e-mails recebidos", - "edit_rule": "Editar regra", - "new_rule": "Nova regra", - "delete_rule": "Excluir regra", - "delete_confirm": "Tem certeza de que deseja excluir esta regra?", - "enable": "Ativar", - "disable": "Desativar", - "raw_editor": "Editor Sieve bruto", - "raw_editor_warning": "Editar o script Sieve bruto pode quebrar a edição visual de regras. As alterações feitas aqui substituem o construtor visual.", - "validate": "Validar", - "validation_success": "O script é válido", - "validation_error": "O script contém erros", - "save": "Salvar regras", - "saving": "Salvando...", - "saved": "Filtros salvos com sucesso", - "save_failed": "Falha ao salvar os filtros", - "loading": "Carregando filtros...", - "not_supported": "Seu servidor de e-mail não suporta filtros de e-mail.", - "rule_name": "Nome da regra", - "rule_name_placeholder": "ex. Classificar newsletters", - "match_all": "Corresponder a TODAS as condições", - "match_any": "Corresponder a QUALQUER condição", - "conditions": "Condições", - "add_condition": "Adicionar condição", - "actions": "Ações", - "add_action": "Adicionar ação", - "stop_processing": "Parar o processamento das regras seguintes", - "include_spam": "Mover também as mensagens marcadas como spam", - "attachment_type_placeholder": "ex.: pdf, doc, jpg", - "value_placeholder_multi": "Valor (vários separados por vírgulas)", - "condition_fields": { - "attachment": "Anexo", - "from": "De", - "to": "Para", - "cc": "Cc", - "subject": "Assunto", - "header": "Cabeçalho personalizado", - "size": "Tamanho", - "body": "Corpo" - }, - "comparators": { - "has_any": "está presente", - "has_type": "do tipo", - "contains": "contém", - "not_contains": "não contém", - "is": "é exatamente", - "not_is": "não é", - "starts_with": "começa com", - "ends_with": "termina com", - "matches": "corresponde ao padrão", - "greater_than": "é maior que", - "less_than": "é menor que" - }, - "action_types": { - "move": "Mover para pasta", - "copy": "Copiar para pasta", - "forward": "Encaminhar para", - "mark_read": "Marcar como lido", - "star": "Destacar mensagem", - "add_label": "Adicionar etiqueta", - "discard": "Descartar (excluir silenciosamente)", - "reject": "Rejeitar com mensagem", - "keep": "Manter na caixa de entrada", - "stop": "Parar processamento" - }, - "move_to_folder": "Selecionar pasta", - "copy_to_folder": "Selecionar pasta", - "forward_to": "Encaminhar para endereço de e-mail", - "forward_placeholder": "email@exemplo.com", - "forward_keep_copy": "Manter uma cópia", - "forward_limit": "Limite de reencaminhamentos por mensagem neste servidor: {count}. Os restantes são ignorados.", - "reject_message": "Mensagem de rejeição", - "reject_placeholder": "Seu e-mail foi rejeitado", - "label_name": "Nome da etiqueta", - "label_placeholder": "Selecionar etiqueta", - "header_name": "Nome do cabeçalho", - "header_placeholder": "ex. X-Mailing-List", - "size_bytes": "Tamanho em bytes", - "size_placeholder": "ex. 1000000", - "system_managed": "Regra gerenciada pelo sistema", - "opaque_warning": "Este script foi editado fora do construtor visual. Apenas a edição Sieve bruta está disponível.", - "open_sieve_editor": "Abrir editor Sieve", - "fetch_error": "Falha ao carregar filtros", - "expanded_view": "Vista expandida", - "expanded_view_description": "Mostrar regras de filtro com blocos detalhados de condições e ações", - "if": "Se", - "then": "Então", - "match_all_conditions": "todas correspondem", - "match_any_condition": "uma corresponde", - "and": "e", - "or": "ou", - "cancel": "Cancelar", - "confirm_delete": "Eliminar", - "rule_list": "Regras de filtragem", - "drag_to_reorder": "Arrastar para reordenar", - "match_type": "Tipo de correspondência", - "reset_to_visual": "Redefinir para o construtor visual", - "reset_warning": "Isto irá descartar o script atual e começar do zero.", - "confirm_reset": "Redefinir", - "validation_empty_name": "O nome da regra é obrigatório", - "validation_empty_conditions": "É necessária pelo menos uma condição com um valor", - "validation_empty_actions": "É necessária pelo menos uma ação", - "templates_section": "Começar com modelo", - "template_newsletters": "Mover newsletters para pasta", - "template_receipts": "Arquivar recibos automaticamente", - "template_important": "Marcar e-mails importantes", - "template_notifications": "Filtrar notificações", - "sieve_editor": { - "title": "Editor de script Sieve", - "warning": "Editar o script Sieve pode interromper a edição visual de regras. As alterações feitas aqui substituem o construtor visual.", - "script_content": "Script Sieve", - "valid": "O script é válido", - "invalid": "O script contém erros", - "save_warning": "Guardar irá substituir todas as regras visuais. Esta ação não pode ser desfeita. Clique em Guardar novamente para confirmar.", - "validating": "A validar...", - "validate": "Validar", - "cancel": "Cancelar", - "save": "Guardar", - "confirm_save": "Confirmar guardar", - "validation_failed": "Falha no pedido de validação" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# condição} other {# condições}}", - "actions_count": "{count, plural, one {# ação} other {# ações}}" - }, - "origin_external": "Externo", - "managed_by_tooltip": "Gerenciado por {source}. Edite nesse aplicativo ou use o editor Sieve bruto." - }, - "templates": { - "title": "Modelos de e-mail", - "description": "Crie modelos de e-mail reutilizáveis com variáveis de marcador", - "add": "Novo modelo", - "edit": "Editar modelo", - "name": "Nome do modelo", - "name_placeholder": "ex.: E-mail de acompanhamento", - "category": "Categoria", - "category_placeholder": "ex.: Trabalho, Pessoal", - "subject": "Assunto", - "subject_placeholder": "Linha de assunto do e-mail", - "body": "Corpo", - "body_placeholder": "Conteúdo do e-mail...", - "recipients_placeholder": "email@example.com", - "identity": "Enviar como", - "default_identity": "Identidade padrão", - "favorite": "Favorito", - "cancel": "Cancelar", - "create": "Criar", - "update": "Atualizar", - "confirm_delete": "Excluir", - "no_templates": "Sem modelos", - "manage": "Gerenciar modelos", - "count": "{count, plural, one {# modelo} other {# modelos}}", - "export_import": "Exportar e importar", - "export_import_description": "Faça backup dos seus modelos ou transfira-os para outro dispositivo", - "export": "Exportar", - "import": "Importar", - "validation": { - "empty": "O nome do modelo é obrigatório", - "too_long": "O nome do modelo não pode exceder 200 caracteres" - } - }, - "files": { - "display": { - "title": "Exibição", - "description": "Configure como arquivos e pastas são exibidos" - }, - "folder_layout": { - "label": "Navegação de pastas", - "description": "Escolha como as pastas são apresentadas: integradas com os arquivos ou numa árvore na barra lateral", - "inline": "Integrado", - "sidebar": "Barra lateral" - }, - "default_view": { - "label": "Visualização padrão", - "description": "Escolha entre layout em grade e lista", - "list": "Lista", - "grid": "Grade" - }, - "default_sort": { - "label": "Ordenação padrão", - "description": "Escolha a ordenação padrão para os arquivos", - "name": "Nome", - "size": "Tamanho", - "modified": "Modificado" - }, - "sort_direction": { - "label": "Direção da ordenação", - "description": "Escolha ordem crescente ou decrescente", - "ascending": "Crescente", - "descending": "Decrescente" - }, - "icons": { - "title": "Ícones", - "description": "Configure a aparência dos ícones de arquivos" - }, - "show_icons": { - "label": "Mostrar ícones de arquivos", - "description": "Exibir ícones ao lado de arquivos e pastas" - }, - "colored_icons": { - "label": "Ícones coloridos", - "description": "Usar ícones coloridos em vez de monocromáticos" - }, - "show_thumbnails": { - "label": "Mostrar miniaturas", - "description": "Exibir pré-visualizações de imagens em vez de ícones" - }, - "behavior": { - "title": "Comportamento", - "description": "Configure o comportamento do gerenciador de arquivos" - }, - "show_hidden": { - "label": "Mostrar arquivos ocultos", - "description": "Exibir arquivos e pastas que começam com um ponto" - }, - "preview": { - "label": "Visualização" - } - }, - "search_placeholder": "Pesquisar configurações", - "search_clear": "Limpar pesquisa", - "search_no_results": "Nenhuma configuração correspondente", - "scoped": { - "back": "Voltar para minha conta", - "managing": "Gerenciando: {name}" - } - }, - "errors": { - "page_error_title": "Algo deu errado", - "page_error_description": "Encontramos um erro inesperado. Por favor, tente novamente ou volte para a página inicial.", - "sidebar_error": "Não foi possível carregar as caixas de entrada", - "email_list_error": "Não foi possível carregar os e-mails", - "viewer_error_title": "Não foi possível exibir o e-mail", - "viewer_error_description": "Houve um problema ao renderizar este e-mail. Ele pode conter conteúdo não suportado.", - "composer_error": "Não foi possível carregar o editor", - "settings_error_title": "Configurações indisponíveis", - "settings_error_description": "Não foi possível carregar as configurações. Suas preferências podem não ser salvas.", - "try_again": "Tentar novamente", - "reload": "Recarregar", - "reload_emails": "Recarregar e-mails", - "reload_settings": "Recarregar configurações", - "retry": "Tentar novamente", - "go_home": "Ir para caixa de entrada" - }, - "context_menu": { - "reply": "Responder", - "reply_all": "Responder a Todos", - "forward": "Encaminhar", - "mark_read": "Marcar como Lido", - "mark_unread": "Marcar como Não Lido", - "star": "Adicionar Estrela", - "unstar": "Remover Estrela", - "pin": "Fixar", - "unpin": "Desafixar", - "move_to": "Mover para...", - "archive": "Arquivar", - "delete": "Excluir", - "mark_as_spam": "Reportar spam", - "not_spam": "Não é spam", - "tag": "Etiqueta", - "items_selected": "{count} e-mails selecionados", - "edit_draft": "Editar rascunho", - "cancel_scheduled_send": "Cancelar envio", - "reschedule_send": "Reagendar", - "cancel_and_edit": "Cancelar e editar", - "cancel_and_compose_again": "Cancelar e escrever novamente" - }, - "mailbox_context_menu": { - "mark_folder_read": "Marcar pasta como lida", - "mark_folder_tree_read": "Marcar pasta e subpastas como lidas", - "mark_all_folders_read": "Marcar todas as pastas como lidas", - "new_subfolder": "Nova subpasta...", - "new_folder": "Nova pasta...", - "rename": "Renomear...", - "share": "Partilhar...", - "import_email": "Importar .eml ou .zip...", - "empty_folder": "Esvaziar pasta", - "empty_folder_generic": "Esvaziar pasta", - "delete_folder": "Excluir pasta", - "refresh": "Atualizar", - "mark_all_confirm_title": "Marcar todas as pastas como lidas", - "mark_all_confirm_message": "Marcar todas as mensagens não lidas da sua conta pessoal como lidas?", - "delete_confirm_title": "Excluir pasta", - "delete_confirm_message": "Excluir permanentemente a pasta \"{name}\"? Esta ação não pode ser desfeita.", - "prompt_new_subfolder": "Digite um nome para a nova subpasta.", - "prompt_new_folder": "Digite um nome para a nova pasta.", - "prompt_rename": "Digite um novo nome para esta pasta.", - "toast_marked_read": "Pasta marcada como lida", - "toast_marked_read_count": "{count, plural, one {1 mensagem marcada como lida} other {# mensagens marcadas como lidas}}", - "toast_already_read": "Nenhuma mensagem não lida", - "toast_marked_all_read": "Todas as pastas marcadas como lidas", - "toast_emptied": "Pasta esvaziada", - "toast_folder_created": "Pasta criada", - "toast_folder_renamed": "Pasta renomeada", - "toast_folder_deleted": "Pasta excluída", - "toast_error_mark_read": "Falha ao marcar como lida", - "toast_error_empty": "Falha ao esvaziar a pasta", - "toast_error_create": "Falha ao criar a pasta", - "toast_error_rename": "Falha ao renomear a pasta", - "toast_error_delete": "Falha ao excluir a pasta", - "toast_error_delete_has_children": "A pasta contém subpastas. Remova-as primeiro.", - "toast_error_delete_has_email": "A pasta não está vazia. Esvazie-a primeiro.", - "delete_with_contents_title": "A pasta não está vazia", - "delete_with_contents_message": "A pasta \"{name}\" ainda contém {count, plural, one {1 mensagem} other {# mensagens}}. Eliminar a pasta e todas as suas mensagens? Esta ação não pode ser anulada.", - "delete_with_contents_confirm": "Eliminar pasta e mensagens", - "placeholder_folder_name": "Nome da pasta", - "create": "Criar", - "rename_confirm": "Renomear" - }, - "shortcuts": { - "title": "Atalhos de Teclado", - "tip": "Pressione ? a qualquer momento para mostrar esta ajuda", - "sections": { - "navigation": "Navegação", - "actions": "Ações de E-mail", - "global": "Global", - "threads": "Conversas", - "composer": "Redação" - }, - "navigation": { - "next_email": "Próximo e-mail", - "previous_email": "E-mail anterior", - "open_email": "Abrir e-mail", - "close_email": "Fechar / Desselecionar" - }, - "actions": { - "reply": "Responder", - "reply_all": "Responder a todos", - "forward": "Encaminhar", - "star": "Alternar estrela", - "archive": "Arquivar", - "delete": "Excluir", - "mark_unread": "Marcar como não lido", - "mark_read": "Marcar como lido", - "toggle_spam": "Reportar spam / Não é spam" - }, - "global": { - "compose": "Escrever novo e-mail", - "search": "Focar busca", - "help": "Mostrar atalhos", - "refresh": "Atualizar e-mails", - "select_all": "Selecionar todos", - "global_search": "Pesquisar em tudo" - }, - "threads": { - "expand_collapse": "Expandir/recolher conversa" - }, - "composer": { - "send": "Enviar e-mail", - "schedule_send": "Agendar envio", - "template_picker": "Abrir seletor de modelos" - } - }, - "threads": { - "messages_one": "{count} mensagem", - "messages_other": "{count} mensagens", - "messages_tooltip": "{count, plural, one {# mensagem nesta conversa} other {# mensagens nesta conversa}}", - "expand": "Expandir conversa", - "collapse": "Recolher conversa", - "loading": "Carregando conversa...", - "mark_read": "Marcar conversa como lida", - "mark_unread": "Marcar conversa como não lida", - "archive": "Arquivar conversa", - "delete": "Excluir conversa", - "star": "Adicionar estrela à conversa", - "unstar": "Remover estrela da conversa", - "toggle_thread": "Expandir/recolher conversa" - }, - "identities": { - "modal_title": "Gerenciar Identidades de Envio", - "create_new": "Criar Nova Identidade", - "edit_identity": "Editar Identidade", - "delete_confirm": "Excluir esta identidade? Isso não pode ser desfeito.", - "cannot_delete": "Esta identidade não pode ser excluída", - "primary_identity": "Principal", - "set_as_primary": "Definir como principal", - "no_identities": "Nenhuma identidade encontrada", - "display": { - "reply_to": "Responder para:", - "bcc": "CCO:", - "signature": "Assinatura:", - "preview": "Visualização:" - }, - "validation_errors": { - "invalid_emails": "E-mails inválidos: {emails}", - "unknown_error": "Erro desconhecido" - }, - "form": { - "name_label": "Nome de Exibição", - "name_placeholder": "ex: E-mail do Trabalho, Pessoal", - "name_required": "Nome é obrigatório", - "email_label": "Endereço de E-mail", - "email_placeholder": "seu.email@exemplo.com", - "email_required": "E-mail é obrigatório", - "email_invalid": "Por favor, digite um endereço de e-mail válido", - "email_immutable": "O endereço de e-mail não pode ser alterado após a criação", - "reply_to_label": "Responder para (opcional)", - "reply_to_placeholder": "diferente@example.com", - "bcc_label": "CCO Automático (opcional)", - "bcc_placeholder": "arquivo@example.com", - "text_signature_label": "Assinatura de Texto", - "html_signature_label": "Assinatura HTML", - "signature_byte_counter": "{bytes} / {max} bytes", - "signature_byte_limit_reached": "Limite do servidor atingido", - "save": "Salvar Identidade", - "cancel": "Cancelar", - "creating": "Criando...", - "updating": "Atualizando..." - }, - "sub_address": { - "button_tooltip": "Usar sub-endereço", - "popover_title": "Adicionar Tag de Sub-Endereço", - "tag_input_placeholder": "Digite a tag (ex: compras)", - "preview_label": "Visualização:", - "recent_tags": "Tags Recentes", - "suggested_tags": "Sugeridas", - "use_address": "Usar Este Endereço", - "invalid_tag": "A tag deve conter apenas caracteres alfanuméricos e hífens", - "tag_too_long": "A tag deve ter no máximo 30 caracteres", - "help_text": "E-mails enviados para usuario{delimiter}tag@dominio.com chegarão na sua caixa de entrada", - "validation": { - "empty": "A tag não pode estar vazia", - "too_long": "A tag deve ter no máximo {max} caracteres", - "invalid_chars": "A tag deve conter apenas letras, números e hífens" - } - }, - "badge": { - "sent_via": "via", - "sub_address_tag": "Enviado usando sub-endereço: {tag}", - "identity_name": "Enviado usando identidade: {name}", - "identity_short": "via {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Excluir", - "delete_confirm_title": "Excluir identidade" - }, - "templates": { - "picker_title": "Escolher um modelo", - "search_placeholder": "Pesquisar modelos...", - "section_favorites": "Favoritos", - "section_recent": "Recentes", - "section_uncategorized": "Outros", - "no_templates": "Sem modelos", - "no_results": "Nenhum modelo encontrado", - "fill_placeholders": "Preencher valores dos marcadores", - "enter_value": "Insira um valor...", - "preview": "Pré-visualização", - "insert_with_values": "Inserir com valores", - "insert_raw": "Inserir bruto", - "copy_suffix": "(cópia)", - "placeholder": "Variável", - "placeholders": { - "recipient_name": "Nome do destinatário", - "company": "Nome da empresa", - "date": "Data atual", - "day_of_week": "Dia da semana", - "sender_name": "Seu nome" - } - }, - "contacts": { - "title": "Contatos", - "search_placeholder": "Pesquisar contatos...", - "create_new": "Novo contato", - "no_category": "Sem categoria", - "rename_category": "Renomear categoria", - "category_name_label": "Nome da categoria", - "category_renamed": "Categoria renomeada", - "category_rename_failed": "Falha ao renomear a categoria", - "category_added": "Contato adicionado a {name}", - "category_added_plural": "{count} contatos adicionados a {name}", - "empty_state": "Nenhum contato", - "empty_state_title": "Sem contatos", - "empty_state_subtitle": "Crie seu primeiro contato ou importe de um arquivo vCard", - "empty_search": "Nenhum contato encontrado", - "empty_search_hint": "Tente outro termo de pesquisa", - "empty_filtered": "Nenhum contato corresponde aos seus filtros", - "empty_filtered_hint": "Ajuste ou limpe os filtros", - "clear_search": "Limpar pesquisa", - "import_vcard": "Importar vCard", - "delete_confirm_title": "Excluir contato", - "delete_confirm": "Tem certeza de que deseja excluir este contato?", - "local_mode": "Os contatos são armazenados localmente (o servidor não suporta JMAP Contacts)", - "back_to_contacts": "Voltar aos contatos", - "back_to_email": "Voltar ao e-mail", - "tabs": { - "all": "Todos", - "groups": "Grupos" - }, - "shared": { - "title": "Compartilhados" - }, - "address_books": { - "create": "Novo livro de endereços", - "created": "Livro de endereços criado", - "create_failed": "Falha ao criar o livro de endereços", - "title": "Meus Catálogos de Endereços", - "shared_prefix": "Compartilhado: {name}", - "moved": "Contato movido para {name}", - "moved_plural": "{count} contatos movidos para {name}", - "move_failed": "Falha ao mover o contato", - "address_book": "Catálogo de endereços", - "rename": "Renomear catálogo de endereços", - "name_label": "Nome do catálogo de endereços", - "renamed": "Catálogo de endereços renomeado", - "rename_failed": "Falha ao renomear o catálogo de endereços", - "default": "Padrão", - "set_default": "Definir como padrão", - "default_updated": "Catálogo de endereços padrão atualizado", - "set_default_failed": "Falha ao definir o catálogo de endereços padrão", - "manage": "Gerenciar catálogos de endereços", - "share": "Compartilhar lista de contatos", - "new_contact_in_book": "Novo contato nesta lista", - "delete": "Excluir lista de contatos", - "confirm_delete": "Excluir \"{name}\"? Todos os contatos desta lista serão removidos.", - "deleted": "Lista de contatos excluída", - "delete_failed": "Falha ao excluir a lista de contatos" - }, - "detail": { - "emails": "Endereços de e-mail", - "phones": "Números de telefone", - "organizations": "Organizações", - "addresses": "Endereços", - "notes": "Notas", - "titles": "Títulos e funções", - "online_services": "Serviços online", - "anniversaries": "Aniversários", - "personal_info": "Informações pessoais", - "languages": "Idiomas", - "categories": "Categorias", - "related_contacts": "Contatos relacionados", - "crypto_keys": "Chaves criptográficas", - "no_contact_selected": "Selecione um contato para ver os detalhes", - "compose_email": "Escrever e-mail", - "copy_email": "Copiar e-mail", - "copy_phone": "Copiar telefone", - "copy_url": "Copiar URL", - "copied": "Copiado para a área de transferência", - "copy_failed": "Falha ao copiar para a área de transferência", - "created": "Criado", - "updated": "Última atualização", - "timezone": "Fuso horário", - "anniversary_birth": "Aniversário", - "anniversary_death": "Falecimento", - "anniversary_wedding": "Aniversário de casamento", - "anniversary_other": "Outro", - "personal_expertise": "Especialidade", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Outro", - "gender": "Gênero", - "gender_masculine": "Masculino", - "gender_feminine": "Feminino", - "gender_other": "Outro", - "gender_none": "Não aplicável", - "gender_unknown": "Desconhecido", - "calendar": "Calendário", - "calendar_uri": "URL do calendário", - "scheduling_uri": "URL de agendamento", - "freebusy_uri": "URL de disponibilidade", - "cert_issuer": "Emissor", - "cert_expires": "Expira em", - "cert_expired": "Expirado", - "cert_fingerprint": "Impressão digital", - "cert_algorithm": "Algoritmo", - "import_to_smime": "Importar para S/MIME", - "cert_already_imported": "Certificado já importado", - "cert_imported": "Certificado importado", - "cert_import_failed": "Falha ao importar o certificado", - "section_contact": "Detalhes do contato", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Mais ações", - "age_years": "{count, plural, one {1 ano} other {# anos}}", - "years_since": "{count, plural, one {há 1 ano} other {há # anos}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Novo contato", - "edit_title": "Editar contato", - "section_address_book": "Diretório", - "select_address_book": "Selecionar um diretório...", - "section_identity": "Nome e identidade", - "contact_type": "Tipo de contacto", - "type_person": "Pessoa", - "type_organization": "Organização", - "section_work": "Trabalho e organização", - "prefix": "Prefixo", - "prefix_placeholder": "Dr., Sr., Sra.", - "given_name": "Nome", - "middle_name": "Nome do meio", - "surname": "Sobrenome", - "suffix": "Sufixo", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Apelido", - "nickname_placeholder": "Apelido", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Telefone", - "phone_placeholder": "+55 11 1234 5678", - "phone_type": "Tipo", - "phone_voice": "Voz", - "phone_cell": "Celular", - "phone_fax": "Fax", - "phone_pager": "Pager", - "phone_video": "Vídeo", - "phone_text": "Texto", - "organization": "Organização", - "organization_placeholder": "Nome da empresa", - "department": "Departamento", - "department_placeholder": "Departamento", - "job_title": "Cargo", - "job_title_placeholder": "ex., Engenheiro de software", - "role": "Função", - "role_placeholder": "ex., Líder de equipe", - "addresses": "Endereços", - "add_address": "Adicionar endereço", - "street": "Rua", - "city": "Cidade", - "region": "Estado / Região", - "postcode": "CEP", - "country": "País", - "online_services": "Serviços online", - "add_online_service": "Adicionar serviço online", - "url_placeholder": "https://...", - "service_placeholder": "Serviço", - "anniversaries": "Aniversários", - "add_anniversary": "Adicionar data", - "anniversary_birth": "Aniversário", - "anniversary_wedding": "Aniversário de casamento", - "anniversary_death": "Falecimento", - "anniversary_other": "Outro", - "personal_info": "Informações pessoais", - "add_personal_info": "Adicionar entrada", - "personal_info_placeholder": "ex., Fotografia", - "personal_expertise": "Especialidade", - "personal_hobby": "Hobby", - "personal_interest": "Interesse", - "personal_other": "Outro", - "level": "Nível", - "level_high": "Alto", - "level_medium": "Médio", - "level_low": "Baixo", - "categories": "Categorias", - "categories_placeholder": "ex., Família, Amigos, Colegas", - "categories_hint": "Digite para pesquisar ou adicionar", - "category_add": "Adicionar", - "note": "Notas", - "note_placeholder": "Adicionar uma nota...", - "gender": "Gênero", - "gender_sex": "Sexo", - "gender_male": "Masculino", - "gender_female": "Feminino", - "gender_other": "Outro", - "gender_none": "Não aplicável", - "gender_unknown": "Desconhecido", - "gender_identity": "Identidade de gênero", - "gender_identity_placeholder": "Identidade de gênero...", - "calendar": "Calendário", - "calendar_uri": "URL do calendário", - "scheduling_uri": "URL de agendamento", - "freebusy_uri": "URL de disponibilidade", - "context_work": "Trabalho", - "context_private": "Pessoal", - "add_email": "Adicionar e-mail", - "add_phone": "Adicionar telefone", - "save": "Salvar", - "cancel": "Cancelar", - "creating": "Criando...", - "updating": "Atualizando...", - "name_required": "Introduza um nome próprio, apelido ou organização", - "email_invalid": "Por favor, insira um endereço de e-mail válido", - "email_error_inline": "Formato de e-mail inválido", - "save_failed": "Falha ao salvar contato", - "delete": "Excluir", - "upload_photo": "Carregar foto", - "remove_photo": "Remover foto", - "photo_hint": "JPG ou PNG, até 10 MB. Será redimensionada.", - "photo_too_large": "A imagem é muito grande (máx. 10 MB)", - "photo_invalid": "Arquivo de imagem inválido", - "change_photo": "Alterar" - }, - "groups": { - "create": "Novo grupo", - "edit": "Editar grupo", - "empty": "Nenhum grupo", - "delete_confirm_title": "Excluir grupo", - "delete_confirm": "Tem certeza de que deseja excluir este grupo?", - "name_label": "Nome do grupo", - "name_placeholder": "ex. Equipe, Família", - "name_required": "O nome do grupo é obrigatório", - "save_failed": "Falha ao salvar grupo", - "members_label": "Membros", - "search_members": "Pesquisar contatos para adicionar...", - "no_members": "Nenhum membro neste grupo", - "member_count": "{count, plural, =0 {Nenhum membro} one {1 membro} other {# membros}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Importar contatos", - "drop_hint": "Clique para selecionar um arquivo vCard", - "file_types": "Arquivos .vcf ou .vcard", - "no_contacts": "Nenhum contato encontrado no arquivo", - "parse_error": "Falha ao analisar o arquivo vCard", - "found": "{count, plural, one {1 contato encontrado} other {# contatos encontrados}}", - "duplicate": "Duplicado", - "select_all": "Selecionar tudo", - "deselect_all": "Desmarcar tudo", - "selected": "{count, plural, one {1 selecionado} other {# selecionados}}", - "import_button": "Importar", - "importing": "Importando...", - "success": "{count, plural, one {1 contato importado} other {# contatos importados}}", - "failed": "Falha na importação", - "close": "Fechar", - "file_too_large": "Arquivo muito grande (máx. 5 MB)" - }, - "export": { - "title": "Exportar contatos", - "success": "{count, plural, one {1 contato exportado} other {# contatos exportados}}" - }, - "bulk": { - "selected": "{count, plural, one {1 selecionado} other {# selecionados}}", - "select_all": "Selecionar tudo", - "delete": "Excluir", - "delete_confirm_title": "Excluir contatos", - "delete_confirm": "Excluir {count, plural, one {1 contato} other {# contatos}}?", - "deleted": "{count, plural, one {1 contato excluído} other {# contatos excluídos}}", - "add_to_group": "Adicionar ao grupo", - "choose_group": "Escolher um grupo", - "adding_contacts": "Adicionando {count, plural, one {1 contato} other {# contatos}}", - "added_to_group": "Contatos adicionados ao grupo", - "export": "Exportar", - "clear": "Limpar seleção" - }, - "toast": { - "created": "Contato criado", - "updated": "Contato atualizado", - "deleted": "Contato excluído", - "error_create": "Falha ao criar contato", - "error_update": "Falha ao atualizar contato", - "error_delete": "Falha ao excluir contato" - }, - "context_menu": { - "open": "Abrir", - "edit": "Editar", - "send_email": "Enviar e-mail", - "add_to_group": "Adicionar ao grupo", - "export_vcard": "Exportar como vCard", - "delete": "Excluir", - "call": "Ligar", - "duplicate": "Duplicar", - "print": "Imprimir" - }, - "filters": { - "toggle": "Filtros", - "select": "Selecionar", - "clear": "Limpar", - "close": "Fechar", - "title": "Filtros avançados", - "organization": "Empresa", - "organization_placeholder": "ex. Acme", - "job_title": "Cargo", - "job_title_placeholder": "ex. Designer", - "location": "Localização", - "location_placeholder": "Cidade ou país", - "email_domain": "Domínio de e-mail", - "email_domain_placeholder": "exemplo.com", - "birthday_month": "Aniversário em", - "any_month": "Qualquer mês", - "has_email": "Com e-mail", - "has_phone": "Com telefone", - "has_photo": "Com foto", - "sort_by": "Ordenar por", - "sort_first_name": "Nome", - "sort_last_name": "Sobrenome" - }, - "open_categories": "Abrir categorias" - }, - "calendar": { - "title": "Calendário", - "back_to_email": "Voltar ao e-mail", - "back_to_month": "Voltar ao mês", - "my_calendars": "Meus calendários", - "birthday_calendar": "Aniversários", - "mini_calendar_change": "Clique para mudar o mês", - "views": { - "month": "Mês", - "week": "Semana", - "day": "Dia", - "agenda": "Agenda", - "today": "Hoje", - "month_hint": "Mês (m)", - "week_hint": "Semana (w)", - "day_hint": "Dia (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Tarefas", - "tasks_hint": "Tarefas (k)" - }, - "events": { - "create": "Criar evento", - "edit": "Editar evento", - "delete": "Excluir evento", - "details": "Detalhes do evento", - "no_events": "Nenhum evento", - "all_day": "Dia inteiro", - "more": "+{count} mais", - "no_title": "(Sem título)", - "resize": "Redimensionar evento", - "duplicate": "Duplicar", - "today_header": "Hoje", - "tomorrow_header": "Amanhã", - "agenda_show_earlier": "Mostrar eventos anteriores", - "agenda_loading": "A carregar…", - "agenda_range_start": "Eventos a partir de {date}", - "agenda_range_end": "Eventos até {date}", - "export_ics": "Exportar como .ics", - "copy_title": "Copiar título", - "copy_link": "Copiar link da reunião", - "go_to_today": "Ir para hoje", - "new_all_day_event": "Novo evento de dia inteiro", - "new_event": "Novo evento", - "new_task": "Nova tarefa" - }, - "detail": { - "add_note": "Adicionar uma nota...", - "save_note": "Salvar", - "note_saved": "Nota adicionada", - "open_link": "Abrir link", - "meeting_link": "Link da reunião", - "tentative": "Provisório", - "cancelled": "Cancelado", - "delete_confirm": "Excluir este evento?" - }, - "form": { - "title": "Título", - "description": "Descrição", - "location": "Local", - "meeting_link": "Link da reunião", - "start_date": "Data de início", - "end_date": "Data de término", - "start_time": "Hora de início", - "end_time": "Hora de término", - "all_day_event": "Evento de dia inteiro", - "calendar_select": "Calendário", - "save": "Salvar", - "cancel": "Cancelar", - "delete_confirm": "Tem certeza de que deseja excluir este evento?", - "color": "Cor" - }, - "participants": { - "availability": { - "title": "Disponibilidade", - "free": "Disponível", - "busy": "Ocupado", - "tentative": "Provisório", - "unknown": "Não é um utilizador deste servidor", - "checking": "A verificar…" - }, - "title": "Participantes", - "add": "Adicionar participante", - "organizer": "Organizador", - "attendee": "Participante", - "accepted": "Aceito", - "declined": "Recusado", - "tentative": "Provisório", - "needs_action": "Aguardando resposta", - "remove": "Remover", - "edit": "Editar", - "email_placeholder": "Adicionar endereço de e-mail ou pesquisar contatos", - "send_invitations": "Enviar convites aos participantes", - "status_summary": "{accepted} aceito(s), {pending} pendente(s)", - "invited_by": "Convite de {name}", - "respond_below": "Responda com os botões abaixo", - "rsvp_label": "Sua resposta", - "cancel_notification": "Os participantes serão notificados do cancelamento", - "you_organizer": "Você é o organizador", - "you_attendee": "Você é um participante", - "no_participants": "Sem participantes", - "count": "{count, plural, one {# participante} other {# participantes}}" - }, - "recurrence": { - "title": "Recorrência", - "none": "Não se repete", - "daily": "Diário", - "weekly": "Semanal", - "monthly": "Mensal", - "yearly": "Anual", - "every_n_days": "A cada {count} dias", - "every_n_weeks": "A cada {count} semanas", - "every_n_months": "A cada {count} meses", - "until": "Até", - "occurrences": "{count} repetições", - "custom": "Personalizado…", - "edit_custom": "Editar recorrência personalizada", - "every_n_years": "A cada {count} anos", - "on_days": "em {days}", - "on_day_n": "no dia {day}", - "on_the_nth": "no {nth} {day}", - "in_month": "em {month}", - "nth_1": "1.º", - "nth_2": "2.º", - "nth_3": "3.º", - "nth_4": "4.º", - "nth_last": "último", - "editor_freq_day": "Dia", - "editor_freq_week": "Semana", - "editor_freq_month": "Mês", - "editor_freq_year": "Ano", - "editor_repeats_on": "Repete-se", - "editor_every": "A cada", - "editor_unit_days": "dia(s)", - "editor_unit_weeks": "semana(s)", - "editor_unit_months": "mês(es)", - "editor_unit_years": "ano(s)", - "editor_on_day": "no dia", - "editor_on_the": "no", - "editor_in": "em", - "editor_ends": "Termina", - "editor_never": "Nunca", - "editor_ends_on": "Em", - "editor_ends_after": "Após", - "editor_occurrences": "repetições" - }, - "recurrence_scope": { - "edit_title": "Editar evento recorrente", - "delete_title": "Excluir evento recorrente", - "description": "Este é um evento recorrente. Quais eventos você deseja modificar?", - "this_event": "Apenas este evento", - "this_and_future": "Este evento e os seguintes", - "all_events": "Todos os eventos", - "cancel": "Cancelar", - "save": "Salvar", - "delete": "Excluir", - "rsvp_title": "Responder a evento recorrente", - "rsvp_description": "Este é um evento recorrente. A quais eventos sua resposta se aplica?", - "respond": "Responder" - }, - "alerts": { - "title": "Lembrete", - "none": "Sem lembrete", - "at_time": "No momento do evento", - "minutes_before": "{count, plural, one {# minuto antes} other {# minutos antes}}", - "hours_before": "{count, plural, one {# hora antes} other {# horas antes}}", - "days_before": "{count, plural, one {# dia antes} other {# dias antes}}", - "weeks_before": "{count, plural, one {# semana antes} other {# semanas antes}}", - "unit_minutes_before": "minutos antes", - "unit_hours_before": "horas antes", - "unit_days_before": "dias antes", - "unit_weeks_before": "semanas antes", - "add": "Adicionar lembrete", - "remove": "Remover lembrete", - "amount": "Quantidade do lembrete", - "unit": "Unidade do lembrete" - }, - "settings": { - "title": "Configurações do calendário", - "default_view": "Visualização padrão", - "week_starts_on": "Semana começa em", - "time_format": "Formato de hora", - "default_calendar": "Calendário padrão", - "default_reminder": "Lembrete padrão", - "time_format_12h": "12 horas", - "time_format_24h": "24 horas", - "notifications_enabled": "Notificações de eventos", - "notifications_enabled_desc": "Mostrar alertas para eventos próximos", - "notification_sound": "Som de notificação", - "notification_sound_desc": "Reproduzir um som para alertas do calendário", - "invitation_parsing": "Analisar convites por e-mail", - "invitation_parsing_desc": "Detectar convites de calendário em anexos de e-mail e mostrar ações do calendário", - "show_time_in_month_view": "Mostrar horário na visualização mensal", - "show_time_in_month_view_desc": "Exibir horários dos eventos na visualização mensal do calendário", - "show_week_numbers": "Mostrar números da semana", - "show_week_numbers_desc": "Exibir números da semana no minicalendário", - "calendar_free_scroll": "Deslocamento livre", - "calendar_free_scroll_desc": "Percorrer meses, semanas e dias de forma contínua em vez de um período de cada vez", - "enable_tasks": "Ativar tarefas", - "enable_tasks_desc": "Mostrar uma visualização de tarefas no calendário para gerenciar pendências", - "show_tasks_on_calendar": "Mostrar tarefas no calendário", - "show_tasks_on_calendar_desc": "Exibir marcadores de tarefas nas visualizações diária e semanal do calendário", - "hover_preview": "Pré-visualização ao passar o rato", - "hover_preview_desc": "Mostrar um popover de detalhes ao passar o rato sobre eventos do calendário", - "hover_preview_instant": "Instantâneo", - "hover_preview_delay_500ms": "Atraso de 0,5 segundos", - "hover_preview_delay_1s": "Atraso de 1 segundo", - "hover_preview_delay_2s": "Atraso de 2 segundos", - "hover_preview_off": "Desativado", - "organizer_identity": "Organizar convites como", - "organizer_identity_desc": "O endereço de calendário usado como organizador quando convida pessoas para eventos.", - "organizer_identity_failed": "Não foi possível alterar o endereço do organizador", - "show_birthday_calendar": "Calendário de aniversários", - "show_birthday_calendar_desc": "Mostrar um calendário virtual com os aniversários dos seus contatos" - }, - "days": { - "monday": "Segunda-feira", - "tuesday": "Terça-feira", - "wednesday": "Quarta-feira", - "thursday": "Quinta-feira", - "friday": "Sexta-feira", - "saturday": "Sábado", - "sunday": "Domingo", - "mon": "Seg", - "tue": "Ter", - "wed": "Qua", - "thu": "Qui", - "fri": "Sex", - "sat": "Sáb", - "sun": "Dom" - }, - "months": { - "jan": "jan", - "feb": "fev", - "mar": "mar", - "apr": "abr", - "may": "mai", - "jun": "jun", - "jul": "jul", - "aug": "ago", - "sep": "set", - "oct": "out", - "nov": "nov", - "dec": "dez", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Evento criado", - "event_updated": "Evento atualizado", - "event_deleted": "Evento excluído", - "calendar_created": "Calendário criado", - "calendar_deleted": "Calendário excluído", - "event_move_error": "Falha ao mover o evento", - "event_resize_error": "Falha ao redimensionar o evento", - "alert_title": "Evento próximo", - "alert_now": "Começa agora", - "alert_in_minutes": "Em {count} min", - "invitation_sent": "Convites enviados", - "invitations_denied_title": "Não é possível enviar os convites", - "invitations_denied": "O servidor recusou enviar os convites: {reason}. Guardar o evento sem os enviar?", - "save_without_invitations": "Guardar sem convites", - "rsvp_updated": "Resposta atualizada", - "rsvp_error": "Falha ao atualizar resposta", - "event_duplicated": "Evento duplicado", - "event_error": "Falha ao salvar o evento", - "task_due": "Tarefa vencendo", - "event_exported": "Evento exportado", - "title_copied": "Título copiado", - "link_copied": "Link copiado" - }, - "status": { - "loading_calendars": "Carregando calendários...", - "loading_events": "Carregando eventos..." - }, - "quick_create": { - "placeholder": "Título do novo evento", - "aria_label": "Criação rápida de evento" - }, - "nav_prev": "Anterior", - "nav_next": "Próximo", - "import": { - "title": "Importar calendário", - "tab_file": "Arquivo", - "tab_url": "URL", - "select_file": "Selecionar arquivo .ics", - "drop_file": "ou arraste o arquivo aqui", - "supported_formats": "Arquivos iCalendar (.ics) suportados", - "url_description": "Insira a URL de um feed iCalendar (.ics) externo para importar eventos.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Suporta URLs iCalendar (.ics)", - "fetch": "Buscar", - "invalid_url": "Insira uma URL válida", - "url_fetch_failed": "Não foi possível buscar o calendário da URL", - "parsing": "Analisando arquivo de calendário...", - "parsed_events": "{count} eventos encontrados", - "no_events": "Nenhum evento encontrado no arquivo", - "select_all": "Selecionar tudo", - "deselect_all": "Desmarcar tudo", - "target_calendar": "Importar para o calendário", - "import_button": "Importar selecionados", - "importing": "Importando eventos...", - "success": "{count} eventos importados com sucesso", - "error": "Falha ao importar calendário", - "file_too_large": "Arquivo excede o limite de 10 MB", - "invalid_format": "Formato de arquivo de calendário inválido" - }, - "webcal_action": { - "title": "Abrir link de calendário", - "description": "Como você gostaria de usar \"{name}\"?", - "import_title": "Importar uma vez", - "import_description": "Busque os eventos agora e copie-os para um dos seus calendários.", - "subscribe_title": "Assinar", - "subscribe_description": "Mantenha este calendário sincronizado automaticamente como um calendário separado.", - "cancel": "Cancelar" - }, - "management": { - "title": "Gerenciamento de calendários", - "description": "Crie, renomeie e personalize seus calendários. Clique com o botão direito em um calendário na barra lateral para alterar rapidamente sua cor.", - "name": "Nome", - "name_placeholder": "Nome do calendário", - "color": "Cor", - "change_color": "Alterar cor", - "random_color": "Nova cor aleatória", - "add_calendar": "Adicionar calendário", - "edit": "Editar", - "delete": "Excluir", - "save": "Salvar", - "create": "Criar", - "cancel": "Cancelar", - "default": "Padrão", - "confirm_delete": "Excluir \"{name}\"? Todos os eventos neste calendário serão removidos.", - "calendar_created": "Calendário criado", - "calendar_updated": "Calendário atualizado", - "calendar_deleted": "Calendário excluído", - "color_updated": "Cor do calendário atualizada", - "error_create": "Falha ao criar calendário", - "error_update": "Falha ao atualizar calendário", - "error_delete": "Falha ao excluir calendário", - "caldav_url": "URL CalDAV", - "copy_url": "Copiar URL CalDAV", - "url_copied": "URL CalDAV copiada para a área de transferência", - "confirm_clear": "Limpar todos os eventos de \"{name}\"? Esta ação não pode ser desfeita.", - "set_default": "Definir como padrão", - "default_updated": "Calendário padrão atualizado", - "error_default": "Falha ao definir calendário padrão", - "clear_events": "Limpar eventos", - "events_cleared": "{count} eventos removidos", - "error_clear": "Falha ao limpar os eventos do calendário", - "share": "Compartilhar calendário", - "new_event_in_calendar": "Novo evento neste calendário", - "kind": "Conteúdo", - "kind_events": "Eventos", - "kind_tasks": "Tarefas", - "kind_both": "Eventos e tarefas", - "kind_hint": "Define quais aplicativos veem este calendário quando ele é sincronizado via CalDAV. Não pode ser alterado depois." - }, - "subscription": { - "title": "Assinatura iCal", - "section_title": "Assinaturas iCal", - "description": "Assine um feed externo do iCalendar. Os eventos serão sincronizados automaticamente em seu próprio calendário. Suporta URLs https:// e webcal://.", - "url_label": "URL do calendário", - "url_placeholder": "https://example.com/calendar.ics ou webcal://...", - "name_label": "Nome do calendário", - "name_placeholder": "ex. Feriados", - "color_label": "Cor", - "refresh_interval": "Intervalo de atualização", - "interval_15": "A cada 15 minutos", - "interval_30": "A cada 30 minutos", - "interval_60": "A cada hora", - "interval_360": "A cada 6 horas", - "interval_1440": "Diariamente", - "subscribe": "Assinar", - "subscribing": "Assinando...", - "save": "Salvar alterações", - "saving": "Salvando...", - "edit": "Editar", - "edit_title": "Editar assinatura", - "updated": "\"{name}\" atualizada", - "update_error": "Falha ao atualizar assinatura", - "invalid_url": "Por favor, insira uma URL válida", - "success": "Assinado \"{name}\"", - "error": "Falha ao adicionar assinatura", - "refresh": "Atualizar agora", - "refresh_success": "Assinatura atualizada", - "refresh_error": "Falha ao atualizar assinatura", - "unsubscribe": "Cancelar assinatura", - "confirm_delete": "Cancelar assinatura de \"{name}\"? O calendário e todos os seus eventos serão removidos.", - "deleted": "Assinatura removida", - "delete_error": "Falha ao remover assinatura", - "last_refreshed": "Última atualização: {time}" - }, - "tasks": { - "label": "Tarefas", - "no_tasks": "Sem tarefas", - "no_title": "(Sem título)", - "mark_complete": "Marcar como concluída", - "mark_incomplete": "Marcar como não concluída", - "filter_all": "Todas", - "filter_pending": "Pendentes", - "filter_completed": "Concluídas", - "filter_overdue": "Atrasadas", - "show_completed": "Mostrar concluídas", - "create": "Nova tarefa", - "edit": "Editar tarefa", - "title_placeholder": "Título da tarefa", - "description_placeholder": "Adicionar uma descrição...", - "due_date": "Data de vencimento", - "include_time": "Incluir horário", - "priority": "Prioridade", - "priority_none": "Nenhuma", - "priority_high": "Alta", - "priority_medium": "Média", - "priority_low": "Baixa", - "progress": "Status", - "progress_needs_action": "Ação necessária", - "progress_in_process": "Em andamento", - "progress_completed": "Concluída", - "progress_cancelled": "Cancelada", - "calendar": "Calendário", - "alert": "Lembrete", - "alert_none": "Nenhum", - "alert_at_time": "No horário de vencimento", - "alert_5min": "5 minutos antes", - "alert_15min": "15 minutos antes", - "alert_30min": "30 minutos antes", - "alert_1hr": "1 hora antes", - "alert_1day": "1 dia antes", - "delete": "Excluir", - "cancel": "Cancelar", - "save": "Salvar", - "quick_add_placeholder": "Adicionar uma tarefa...", - "due_today": "Vence hoje", - "due_tomorrow": "Vence amanhã", - "overdue": "Atrasada" - }, - "nav_open_menu": "Abrir menu" - }, - "advanced_search": { - "title": "Pesquisa avançada", - "from": "De", - "from_placeholder": "E-mail ou nome do remetente", - "to": "Para", - "to_placeholder": "E-mail ou nome do destinatário", - "subject": "Assunto", - "subject_placeholder": "O assunto contém...", - "body": "Corpo", - "body_placeholder": "O corpo contém...", - "folder": "Pasta", - "all_folders": "Todas as pastas", - "has_attachment": "Anexos", - "date_after": "Após", - "date_before": "Antes de", - "size_min": "Maior que (KB)", - "size_max": "Menor que (KB)", - "starred": "Favoritos", - "unread": "Não lidos", - "read": "Lidos", - "yes": "Sim", - "no": "Não", - "clear": "Limpar", - "clear_all": "Limpar tudo", - "filters_active": "{count} filtro", - "filters_active_plural": "{count} filtros", - "toggle_filters": "Filtros", - "search_hint": "Use os filtros avançados para uma pesquisa precisa", - "advanced_filters_tooltip": "Filtros de pesquisa avançados", - "results_found": "{count, plural, =0 {Nenhum resultado encontrado} one {# resultado encontrado} other {# resultados encontrados}}", - "results_found_more": "{count}+ resultados encontrados", - "suggestions_recent": "Pesquisas recentes", - "suggestions_people": "Pessoas", - "suggestions_from": "Pesquisar e-mails de {address}", - "suggestions_to": "Pesquisar e-mails para {address}", - "suggestions_remove_recent": "Remover das pesquisas recentes" - }, - "welcome": { - "title": "Bem-vindo à sua caixa de entrada", - "tip_compose": "Pressione c para escrever um novo e-mail", - "tip_shortcuts": "Pressione ? para ver todos os atalhos de teclado", - "tip_sidebar": "Encontre Calendário, Contatos e Configurações no menu lateral", - "tip_settings": "Personalize sua experiência nas Configurações", - "got_it": "Entendi", - "settings": "Configurações", - "dismiss": "Fechar", - "start_tour": "Iniciar tour" - }, - "demo_welcome": { - "title": "Bem-vindo ao Bulwark Mail", - "description": "Explore um cliente de webmail completo - diretamente no seu navegador. Todos os dados ficam no seu dispositivo, então teste tudo.", - "feature_email": "Ler e escrever e-mails", - "feature_organize": "Tags, estrelas e pastas", - "feature_shortcuts": "Atalhos de teclado", - "feature_privacy": "Demo 100% privada", - "hint": "Clique num e-mail à esquerda para começar, ou inicie o tour." - }, - "files": { - "office_edit": "Editar documento", - "office_loading": "A abrir o editor de documentos…", - "office_error": "Não foi possível abrir o editor de documentos.", - "office_close": "Fechar editor", - "open_in_new_tab": "Abrir num novo separador", - "title": "Arquivos", - "search_placeholder": "Pesquisar arquivos...", - "empty_state_title": "Ainda não há arquivos", - "empty_state_description": "Carregue arquivos ou crie pastas para começar", - "upload": "Carregar", - "upload_files": "Carregar arquivos", - "new_folder": "Nova pasta", - "new_folder_name": "Nome da pasta", - "rename": "Renomear", - "rename_title": "Renomear", - "new_name": "Novo nome", - "name_forbidden_chars": "O servidor não permite estes caracteres nos nomes: {chars}", - "name_reserved": "Este nome está reservado no servidor.", - "delete": "Eliminar", - "delete_confirm_title": "Eliminar recurso", - "delete_confirm_message": "Tem a certeza de que deseja eliminar \"{name}\"? Esta ação não pode ser desfeita.", - "download": "Transferir", - "name": "Nome", - "size": "Tamanho", - "modified": "Modificado", - "type": "Tipo", - "folder": "Pasta", - "file": "Arquivo", - "parent_directory": "Diretório superior", - "breadcrumb_root": "Início", - "drop_files_here": "Largue arquivos ou pastas aqui para carregar", - "uploading": "A carregar...", - "upload_success": "{count, plural, one {1 arquivo carregado} other {# arquivos carregados}}", - "upload_error": "Falha ao carregar o arquivo", - "create_folder_success": "Pasta criada", - "create_folder_error": "Falha ao criar a pasta", - "delete_success": "Eliminado com sucesso", - "delete_error": "Falha ao eliminar", - "rename_success": "Renomeado com sucesso", - "rename_error": "Falha ao renomear", - "download_error": "Falha ao transferir", - "not_available": "O armazenamento de arquivos não está disponível neste servidor", - "cancel": "Cancelar", - "create": "Criar", - "save": "Guardar", - "no_results": "Nenhum arquivo corresponde à sua pesquisa", - "batch_delete_confirm_message": "Tem a certeza de que deseja eliminar {count, plural, one {1 item} other {# itens}}? Esta ação não pode ser desfeita.", - "batch_delete_success": "{count, plural, one {1 item eliminado} other {# itens eliminados}}", - "grid_view": "Vista em grelha", - "list_view": "Vista em lista", - "details": "Detalhes", - "path": "Caminho", - "preview": "Pré-visualização", - "preview_error": "Falha ao carregar a pré-visualização", - "cut": "Cortar", - "copy": "Copiar", - "paste": "Colar", - "move_success": "{count, plural, one {1 item movido} other {# itens movidos}}", - "move_error": "Falha ao mover", - "paste_success": "Colado com sucesso", - "paste_error": "Falha ao colar", - "new_text_file": "Novo arquivo de texto", - "file_name": "Nome do arquivo", - "retry": "Tentar novamente", - "refresh": "Atualizar", - "toggle_favorite": "Alternar favorito", - "duplicate": "Duplicar", - "duplicate_success": "Duplicado com sucesso", - "duplicate_error": "Falha ao duplicar", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Arquivo criado", - "create_file_error": "Falha ao criar arquivo", - "favorites": "Favoritos", - "recent": "Recentes", - "properties": "Propriedades", - "open_folder": "Abrir pasta", - "upload_folder": "Carregar pasta", - "file_too_large": "\"{name}\" excede o tamanho máximo do arquivo ({max})", - "undo": "Desfazer", - "undo_success": "Ação desfeita", - "undo_error": "Falha ao desfazer", - "toolbar": "Ações de arquivo", - "file_list": "Arquivos e pastas", - "context_menu": "Ações", - "settings_title": "Configurações de arquivos", - "settings_display": "Exibição", - "settings_default_view": "Visualização padrão", - "settings_default_view_desc": "Escolha entre layout em grade e lista", - "settings_default_sort": "Ordenação padrão", - "settings_default_sort_desc": "Escolha a ordenação padrão para os arquivos", - "settings_sort_direction": "Direção da ordenação", - "settings_sort_direction_desc": "Escolha ordem crescente ou decrescente", - "settings_ascending": "Crescente", - "settings_descending": "Decrescente", - "settings_icons": "Ícones", - "settings_show_icons": "Mostrar ícones de arquivos", - "settings_show_icons_desc": "Exibir ícones ao lado de arquivos e pastas", - "settings_colored_icons": "Ícones coloridos", - "settings_colored_icons_desc": "Usar ícones coloridos em vez de monocromáticos", - "settings_show_thumbnails": "Mostrar miniaturas", - "settings_show_thumbnails_desc": "Exibir pré-visualizações de imagens em vez de ícones", - "settings_behavior": "Comportamento", - "settings_show_hidden": "Mostrar arquivos ocultos", - "settings_show_hidden_desc": "Exibir arquivos e pastas que começam com um ponto", - "settings_folder_layout": "Navegação de pastas", - "settings_folder_layout_desc": "Escolha como as pastas são apresentadas: integradas com os arquivos ou numa árvore na barra lateral", - "settings_folder_layout_inline": "Integrado", - "settings_folder_layout_sidebar": "Barra lateral", - "disabled_title": "O recurso de arquivos foi desativado pelo seu administrador", - "disabled_description": "Uploads de arquivos grandes via WebDAV podem causar instabilidade no Stalwart/RocksDB, incluindo falhas de memória e uso irrecuperável de disco. Arquivos excluídos podem não ser removidos imediatamente do armazenamento. Este recurso não é recomendado para ambientes de produção.", - "stability_warning": "Uploads de arquivos grandes podem causar instabilidade no servidor. Arquivos excluídos podem não ser removidos imediatamente do armazenamento. Use com cautela.", - "no_accounts": "Nenhuma conta conectada.", - "open_folder_tree": "Abrir árvore de pastas", - "other_accounts": "Outras contas", - "migration_title": "Atualizando seus arquivos…", - "migration_description": "Organizando pastas e arquivos em sua estrutura adequada. Isso acontece apenas uma vez." - }, - "smime": { - "your_certificates": "Seus certificados", - "your_certificates_desc": "Importe e gerencie seus certificados S/MIME para assinar e criptografar e-mails", - "recipient_certificates": "Certificados dos destinatários", - "recipient_certificates_desc": "Certificados públicos para criptografar e-mails para destinatários", - "identity_bindings": "Vínculos de identidade e chave", - "identity_bindings_desc": "Associe certificados S/MIME às suas identidades de e-mail", - "defaults_title": "Padrões", - "defaults_desc": "Configure o comportamento padrão de assinatura e criptografia", - "import_pkcs12": "Importar PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importar certificado", - "no_certificates": "Nenhum certificado importado ainda", - "no_recipient_certs": "Nenhum certificado de destinatário", - "expires": "Expira em", - "expired": "Expirado", - "bound_to": "Vinculado a", - "no_key_bound": "Nenhum", - "lock": "Bloquear chave", - "unlock": "Desbloquear chave", - "details": "Ver detalhes", - "delete": "Excluir", - "encrypt_by_default": "Criptografar por padrão", - "encrypt_by_default_desc": "Criptografar e-mails automaticamente quando todos os destinatários tiverem certificados", - "remember_unlocked": "Lembrar chaves desbloqueadas", - "remember_unlocked_desc": "Manter as chaves desbloqueadas durante esta sessão do navegador", - "sign_default_for": "Assinar por padrão para", - "enter_p12_passphrase": "Inserir a frase secreta do PKCS#12", - "p12_passphrase_desc": "Insira a frase secreta que protege este arquivo de certificado", - "enter_storage_passphrase": "Definir frase secreta de armazenamento", - "storage_passphrase_desc": "Escolha uma frase secreta para proteger esta chave no navegador", - "next": "Próximo", - "import": "Importar", - "unlock_key": "Desbloquear chave", - "unlock_key_desc": "Insira a frase secreta de armazenamento para desbloquear esta chave para assinatura ou descriptografia", - "passphrase_placeholder": "Insira a frase secreta", - "confirm_passphrase_placeholder": "Confirme a frase secreta", - "passphrase_mismatch": "As frases secretas não coincidem", - "cancel": "Cancelar", - "processing": "Processando…", - "close": "Fechar", - "certificate_details": "Detalhes do certificado", - "cert_subject": "Assunto", - "cert_issuer": "Emissor", - "cert_email": "E-mail", - "cert_serial": "Número de série", - "cert_validity": "Validade", - "cert_fingerprint": "Impressão digital (SHA-256)", - "cert_algorithm": "Algoritmo", - "cert_capabilities": "Recursos", - "cert_source": "Origem", - "cert_expired": "Este certificado expirou", - "cert_not_yet_valid": "Este certificado ainda não é válido", - "cap_sign": "Assinatura", - "cap_encrypt": "Criptografia", - "cap_none": "Nenhum", - "show_passphrase": "Mostrar frase secreta", - "hide_passphrase": "Ocultar frase secreta", - "sign_toggle": "Assinar", - "encrypt_toggle": "Criptografar", - "missing_recipient_certs": "Certificados ausentes para: {emails}", - "missing_sender_cert": "Nenhum certificado vinculado a esta identidade", - "status_encrypted_ok": "Esta mensagem foi criptografada", - "status_encrypted_no_key": "Esta mensagem está criptografada, mas nenhuma chave correspondente foi encontrada", - "status_encrypted_failed": "Falha ao descriptografar esta mensagem", - "status_signed_valid": "Assinatura verificada", - "status_signed_invalid": "A verificação da assinatura falhou", - "status_signed_expired_cert": "Assinada com um certificado expirado", - "status_signed_self_signed": "Assinatura válida, mas o certificado é autoassinado (não confiável)", - "status_signed_mismatch": "A assinatura é válida, mas o signatário não corresponde ao remetente", - "status_unsupported": "Formato S/MIME não suportado", - "auto_import_signer_certs": "Importar automaticamente certificados de signatários", - "auto_import_signer_certs_desc": "Salvar automaticamente certificados de e-mails assinados verificados para criptografia futura", - "export": "Exportar", - "enter_export_passphrase": "Definir frase secreta de exportação", - "export_passphrase_desc": "Escolha uma frase secreta para proteger o arquivo PKCS#12 exportado", - "export_storage_desc": "Insira a frase secreta de armazenamento para descriptografar a chave para exportação", - "incorrect_passphrase": "Frase secreta incorreta" - }, - "tour": { - "step_counter": "Passo {current} de {total}", - "skip": "Pular tour", - "back": "Voltar", - "next": "Próximo", - "finish": "Finalizar", - "take_a_tour": "Faça um tour pela interface", - "restart_title": "Tour introdutório", - "restart_desc": "Rever o tour guiado da interface", - "restart_button": "Reiniciar tour", - "show_on_new_devices_title": "Mostrar em novos dispositivos", - "show_on_new_devices_desc": "Reproduzir o banner de boas-vindas e o tour no primeiro login num novo dispositivo, mesmo que já os tenhas concluído noutro lado", - "sidebar_title": "Suas caixas de correio", - "sidebar_desc": "Esta é a barra lateral de pastas. Clique em qualquer caixa para ver seus e-mails. Você pode criar pastas, arrastar e-mails entre elas e ver contadores de não lidos.", - "compose_title": "Escrever um e-mail", - "compose_desc": "Clique aqui para escrever um novo e-mail. Você pode adicionar destinatários, anexos e usar formatação de texto enriquecido.", - "search_title": "Pesquisar sua caixa", - "search_desc": "Pesquise por remetente, assunto ou conteúdo. Clique no ícone de filtro para opções avançadas como intervalo de datas, anexos e mensagens com estrela.", - "email_list_title": "Sua lista de e-mails", - "email_list_desc": "Os e-mails aparecem aqui. Clique em um para lê-lo à direita. Use a caixa de seleção para selecionar vários, depois mova, exclua ou etiquete em massa.", - "email_viewer_title": "Painel de leitura", - "email_viewer_desc": "O e-mail selecionado abre aqui. Responda, encaminhe, arquive ou exclua com os botões da barra de ferramentas. Você também pode marcar e-mails com estrela ou adicionar etiquetas coloridas.", - "keywords_title": "Etiquetas coloridas", - "keywords_desc": "Organize seus e-mails com etiquetas coloridas. Arraste um e-mail para uma etiqueta para marcá-lo, ou clique com o botão direito para atribuir etiquetas.", - "calendar_title": "Calendário", - "calendar_desc": "Mude para o calendário para gerenciar seus eventos. Crie eventos, defina lembretes e visualize os layouts de dia, semana ou mês.", - "contacts_title": "Contatos", - "contacts_desc": "Seu livro de endereços fica aqui. Importe contatos, crie grupos e clique em qualquer contato para ver seus detalhes completos.", - "settings_title": "Configurações", - "settings_desc": "Personalize tudo: tema, densidade, assinaturas, filtros, atalhos de teclado, padrões do calendário e mais.", - "shortcuts_title": "Atalhos de teclado", - "shortcuts_desc": "Usuários avançados adoram isso. Pressione ? a qualquer momento para ver todos os atalhos disponíveis. Navegue, escreva e gerencie e-mails sem tocar no mouse.", - "compose_open_title": "O compositor", - "compose_open_desc": "Este é o compositor de e-mail. Adicione destinatários, escreva sua mensagem, anexe arquivos e use formatação de texto rico. Você também pode salvar rascunhos e usar modelos.", - "calendar_view_title": "Seu calendário", - "calendar_view_desc": "Aqui está seu calendário com eventos de exemplo. Você pode alternar entre as visualizações de dia, semana, mês e agenda usando a barra de ferramentas.", - "create_event_title": "Criar um evento", - "create_event_desc": "Clique neste botão para criar um novo evento no calendário. Você pode definir um título, data, hora e adicionar participantes.", - "event_modal_title": "Detalhes do evento", - "event_modal_desc": "Aqui está o formulário do evento. Preencha o título, escolha uma data e hora, adicione um local ou participantes. Clique em salvar quando terminar - ou feche e siga em frente.", - "contacts_list_title": "Seus contatos", - "contacts_list_desc": "Aqui estão seus contatos. Clique em qualquer contato para ver seus detalhes à direita. Você também pode criar novos contatos, importar vCards ou organizar contatos em grupos.", - "files_title": "Armazenamento de arquivos", - "settings_tabs_title": "Menu de configurações", - "settings_tabs_desc": "Aqui estão todas as categorias de configurações. Personalize a aparência, gerencie identidades, configure filtros de e-mail, ajuste o calendário e muito mais.", - "files_desc": "O navegador de arquivos permite carregar, organizar e compartilhar arquivos - como uma nuvem pessoal integrada no seu e-mail.", - "demo_banner_title": "Controles de demonstração", - "demo_banner_desc": "Está no modo de demonstração - tudo permanece no seu navegador. Clique em 'Repor Demonstração' a qualquer momento para recomeçar com dados limpos.", - "quota_title": "Utilização do armazenamento", - "quota_desc": "Acompanhe o tamanho da sua caixa de correio aqui. O círculo preenche-se à medida que utiliza mais espaço." - }, - "unified_mailbox": { - "search_unavailable": "A pesquisa não está disponível na vista unificada" - }, - "calendar_event_notifications": { - "someone": "Alguém", - "invited": "{name} convidou-o para \"{title}\"", - "updated": "{name} atualizou \"{title}\"", - "cancelled": "{name} cancelou \"{title}\"", - "untitled": "Evento sem título" - }, - "share_notifications": { - "someone": "Alguém", - "shared": "{name} partilhou consigo {kind} \"{object}\"", - "changed": "{name} alterou o seu acesso a {kind} \"{object}\"", - "revoked": "{name} removeu o seu acesso a {kind} \"{object}\"", - "object": { - "folder": "a pasta", - "calendar": "o calendário", - "address_book": "o livro de endereços", - "files": "a pasta de ficheiros" - } - }, - "sharing": { - "title": "Compartilhar \"{name}\"", - "description": "Conceda acesso a outros usuários ou grupos neste servidor. As alterações têm efeito imediato.", - "no_shares": "Ainda não compartilhado.", - "add_person": "Adicionar pessoa ou grupo", - "search_placeholder": "Buscar por nome ou e-mail…", - "loading_principals": "Carregando usuários…", - "no_principals": "Nenhum outro usuário ou grupo encontrado.", - "no_match": "Sem resultados.", - "remove": "Remover acesso", - "group": "Grupo", - "share_added": "Acesso concedido", - "share_updated": "Acesso atualizado", - "share_removed": "Acesso removido", - "share_failed": "Falha ao atualizar o compartilhamento", - "preset": { - "freeBusy": "Apenas disponibilidade", - "read": "Somente leitura", - "readWrite": "Leitura e escrita", - "manager": "Gerente", - "custom": "Personalizado" - } - }, - "quote_header": { - "reply_line": "Em {date}, {from} escreveu:", - "forwarded_separator": "---------- Mensagem encaminhada ----------", - "from_label": "De", - "date_label": "Data", - "subject_label": "Assunto" - }, - "pwa_install": { - "title": "Instalar {appName}", - "description": "Instale o nosso app para acesso rápido e suporte offline.", - "not_now": "Agora não", - "install": "Instalar", - "dont_remind": "Não lembrar novamente", - "dismiss_aria": "Dispensar aviso de instalação" - }, - "deep_link": { - "message_not_found": "Esta mensagem não está mais disponível.", - "thread_not_found": "Esta conversa não está mais disponível.", - "folder_not_found": "Esta pasta não está mais disponível.", - "event_not_found": "Este evento não está mais disponível.", - "file_not_found": "Este arquivo não está mais disponível.", - "account_unavailable": "Entre nessa conta para abrir este link.", - "copy_message": "Copiar link da mensagem", - "copy_conversation": "Copiar link da conversa", - "copy_event": "Copiar link do evento", - "copied": "Link copiado", - "copy_failed": "Não foi possível copiar o link" - }, - "plugins": { - "oauth_callback": { - "completing": "A concluir a autorização…", - "completing_message": "Aguarde enquanto a ligação é estabelecida.", - "received_title": "Autorização recebida", - "received_message": "Pode voltar ao separador onde iniciou a ligação.", - "failed_title": "Falha na autorização", - "denied": "A autorização foi negada.", - "failed_detail": "Falha na autorização: {error}", - "missing_params": "Falta o código de autorização ou o estado.", - "delivery_failed": "Não foi possível entregar o resultado da autorização ao separador da aplicação. Feche esta janela e tente novamente." - } - }, - "global_search": { - "title": "Pesquisar em tudo", - "placeholder": "Pesquisar e-mail, contactos, calendário, ficheiros...", - "scope_label": "Pesquisar em", - "account_label": "Conta", - "scope_all": "Tudo", - "scope_mail": "E-mail", - "scope_contacts": "Contatos", - "scope_calendar": "Calendário", - "scope_files": "Arquivos", - "all_accounts": "Todas as contas", - "show_all": "Mostrar todos os resultados", - "no_results": "Sem resultados para “{query}”", - "server_hint": "Palavras completas pesquisam no servidor; palavras parciais correspondem ao que já está carregado", - "searching": "A pesquisar...", - "account_error": "Não foi possível pesquisar em {account}", - "recent_searches": "Pesquisas recentes", - "recurring": "Recorrente", - "no_subject": "(no subject)", - "empty_hint": "Pesquise e-mail, contactos, eventos e ficheiros em todas as contas. Experimente from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Plano por data", - "grouped": "Agrupado", - "load_more": "Carregar mais", - "preview_empty": "Selecione um resultado para pré-visualizar", - "open_full": "Abrir", - "view_label": "Vista" - } -} +{ + "meta_description": "Cliente de webmail minimalista que usa o protocolo JMAP", + "login": { + "title": "Webmail", + "username_label": "E-mail", + "username_placeholder": "usuario@exemplo.com", + "password_label": "Senha", + "password_placeholder": "Digite sua senha", + "jmap_endpoint_label": "Servidor JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "O servidor deve permitir solicitações CORS deste domínio.", + "jmap_server_label": "Servidor", + "jmap_server_auto_picked": "Servidor selecionado a partir do domínio do seu e-mail.", + "sign_in": "Entrar", + "signing_in": "Entrando...", + "loading": "Carregando...", + "reconnecting": "Conexão perdida. Tentando reconectar…", + "error": { + "invalid_credentials": "E-mail ou senha inválidos", + "invalid_token": "O servidor não aceitou este token de acesso. Verifique se ele está completo e se não expirou nem foi revogado.", + "connection_failed": "Falha ao conectar com o servidor", + "cors_blocked": "O servidor está acessível mas está bloqueando requisições de origem cruzada. Verifique as configurações de CORS do seu servidor JMAP e permita este domínio.", + "server_error": "O servidor está temporariamente indisponível. Tente novamente mais tarde.", + "generic": "Ocorreu um erro. Por favor, tente novamente.", + "totp_required": "É necessário um código de autenticação de dois fatores. Insira seu código abaixo.", + "totp_invalid": "Código de autenticação inválido. Verifique seu aplicativo de autenticação.", + "oauth_discovery_failed": "SSO está ativado mas o provedor de identidade não pôde ser contatado. Verifique sua configuração OAuth.", + "token_exchange_failed": "Sua senha e o código foram aceitos, mas o servidor de e-mail se recusou a iniciar uma sessão para este aplicativo. Peça ao administrador que verifique as configurações do cliente OAuth." + }, + "show_password": "Mostrar senha", + "hide_password": "Ocultar senha", + "totp_toggle": "Tenho um código 2FA", + "token_toggle": "Entrar com um token de acesso", + "password_toggle": "Entrar com uma senha", + "token_label": "Token de acesso", + "token_placeholder": "Cole seu token de API", + "token_hint": "Crie um token de API nas configurações do seu provedor de e-mail e cole-o aqui.", + "remember_me": "Lembrar-me", + "config_error": { + "title": "Erro de Configuração", + "fetch_failed": "Não foi possível carregar a configuração do aplicativo. Por favor, tente novamente mais tarde.", + "server_not_configured": "O servidor de e-mail não foi configurado. Por favor, contate seu administrador." + }, + "remove_from_history": "Remover do histórico", + "totp_label": "Código de autenticação", + "totp_placeholder": "000000", + "session_expired": "Sua sessão expirou. Faça login novamente.", + "dismiss": "Fechar", + "or": "ou", + "sign_in_sso": "Entrar com SSO", + "add_account_title": "Adicionar conta", + "add_account_subtitle": "Entrar com outra conta", + "cancel": "Cancelar", + "website": "Site", + "imprint": "Informações legais", + "privacy_policy": "Política de privacidade", + "try_demo": "Experimentar demo", + "demo_description": "Explore com dados de exemplo - sem conta necessária", + "demo_launching": "Iniciando demo...", + "demo_login_button": "Iniciar demo", + "demo_tagline": "Experimente um cliente de e-mail completo. Sem necessidade de conta.", + "demo_no_signup": "Sem registo - explore livremente com dados de exemplo", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Concluindo login...", + "oauth_error": { + "title": "Falha na autenticação", + "invalid_state": "A validação de segurança falhou. Tente entrar novamente.", + "missing_params": "Dados de autorização ausentes. Tente entrar novamente.", + "token_exchange_failed": "Não foi possível concluir a autenticação. Tente novamente.", + "access_denied": "Acesso negado. Entre em contato com seu administrador.", + "back_to_login": "Voltar ao login" + } + }, + "sidebar": { + "close": "Fechar", + "compose": "Escrever", + "compose_hint": "Compor (c)", + "search_placeholder": "Buscar e-mails...", + "search_placeholder_hint": "Pesquisar e-mail... (pressione /)", + "storage": "Armazenamento", + "storage_used": "Usado", + "storage_free": "Livre", + "storage_total": "Total", + "sign_out": "Sair", + "sign_out_of": "Sair de {account}", + "sign_out_all": "Sair de todas as contas", + "add_account": "Adicionar conta", + "set_as_default": "Definir como padrão", + "switch_account": "Trocar conta", + "contacts": "Contatos", + "calendar": "Calendário", + "settings": "Configurações", + "admin": "Admin", + "files": "Arquivos", + "loading_mailboxes": "Carregando caixas de entrada...", + "push_connected": "Atualizações em tempo real ativas", + "push_disconnected": "Atualizações em tempo real inativas", + "keyboard_shortcuts": "Atalhos de teclado", + "theme": { + "light": "Modo claro", + "dark": "Modo escuro", + "system": "Tema do sistema" + }, + "language": { + "title": "Idioma" + }, + "mailboxes": { + "inbox": "Caixa de Entrada", + "sent": "Enviados", + "drafts": "Rascunhos", + "trash": "Lixeira", + "archive": "Arquivo", + "starred": "Com Estrela", + "all_mail": "Todos os E-mails", + "spam": "Spam", + "important": "Importante" + }, + "unified_inbox": "Caixa de entrada unificada", + "unified_sent": "Todos os enviados", + "unified_drafts": "Todos os rascunhos", + "unified_trash": "Todas as lixeiras", + "unified_archive": "Todos os arquivos", + "unified_junk": "Todo o spam", + "all_accounts": "Todas as contas", + "all_accounts_loading": "A carregar contas: {loaded} de {total}", + "unified_mailbox": "Caixa de correio unificada", + "expand": "Expandir", + "collapse": "Recolher", + "expand_tooltip": "Expandir", + "collapse_tooltip": "Recolher", + "mobile": { + "search": "Buscar", + "compose": "Escrever", + "go_back": "Voltar", + "toggle_menu": "Alternar menu" + }, + "clear_search": "Limpar busca", + "vacation_active": "Resposta automática ativa", + "demo_banner": "Modo de demonstração", + "demo_reset": "Repor", + "demo_tour": "Tour", + "tags": "Etiquetas", + "show_all_tags": "Mostrar tudo ({count})", + "show_fewer_tags": "Mostrar menos", + "folders": "Pastas", + "your_folders": "As suas pastas", + "create_folder": "Criar pasta", + "show_more": "Mais", + "show_fewer": "Menos", + "mail": "E-mail", + "nav_label": "Navegação", + "add_app": "Apps", + "shared": "Compartilhado", + "shared_account_hidden": "Oculto da barra lateral. Mostre de novo em Configurações › Layout.", + "notifications_muted": "Notificações silenciadas", + "scheduled": "Agendados", + "unified_all_unread": "Tudo não lido", + "unified_all_starred": "Tudo com estrela", + "unified_all_mail": "Todo o correio", + "remove_account": "Remover conta", + "remove_account_confirm": "Remover {account} desse dispositivo? Você pode adicioná-la de volta depois.", + "labels": "Etiquetas", + "create_label": "Criar etiqueta", + "mv_tasks": "Tarefas", + "mv_side_panel": "Painel lateral", + "mv_open_full": "Abrir em vista completa", + "mv_previous_day": "Dia anterior", + "mv_next_day": "Dia seguinte", + "mv_add_another_account": "Adicionar outra conta", + "mv_manage_account": "Gerir a sua conta", + "mv_storage_used": "{percent}% de {total} utilizado" + }, + "protocol_handlers": { + "title": "Aplicativos padrão", + "description": "Escolha se links de e-mail e calendário devem abrir no Bulwark. Tecnicamente, o Bulwark se registra como manipulador de protocolo para links mailto: e webcal:.", + "unsupported": "Este navegador ou esta conexão não oferece suporte ao registro manual de manipuladores de protocolo. Talvez você ainda consiga usar o PWA instalado pelas configurações do navegador ou do sistema.", + "mailto_label": "Links de e-mail", + "mailto_description": "Abre links mailto: no Bulwark com o editor preenchido previamente.", + "protocol_open_mode_label": "Ao abrir links de protocolo", + "protocol_open_mode_description": "Escolha se o Bulwark abre links mailto: e webcal: em uma nova guia ou reutiliza uma sessão aberta. A opção de sessão ativa precisa da permissão de notificações para que você possa clicar em uma notificação alternativa e trazer o Bulwark para a frente se o navegador bloquear o foco.", + "protocol_open_mode_active_session": "Abrir na sessão ativa se possível", + "protocol_open_mode_new_tab": "Sempre abrir uma nova guia", + "focus_notification_title": "Abrir Bulwark", + "focus_notification_body": "O link foi aberto no Bulwark. Clique para trazer a janela para a frente.", + "webcal_label": "Links de calendário", + "webcal_description": "Abre links webcal: no Bulwark com uma janela de assinatura de calendário preenchida previamente.", + "register_mailto": "Registrar aplicativo de e-mail", + "register_webcal": "Registrar aplicativo de calendário", + "mailto_registered": "Registro do manipulador de e-mail solicitado", + "webcal_registered": "Registro do manipulador de calendário solicitado", + "registration_failed": "Falha ao registrar manipulador de protocolo", + "opening_mailto": "Abrindo editor...", + "opening_webcal": "Abrindo calendário...", + "browser_note": "Seu navegador ou sistema operacional pode pedir confirmação e pode exigir que o Bulwark esteja instalado antes de poder ser selecionado como aplicativo padrão.", + "select_account_title": "Escolher conta", + "select_mailto_account": "Escolha qual conta deve abrir este link de e-mail.", + "select_webcal_account": "Escolha qual conta deve abrir este link de calendário.", + "select_account_note": "Esta escolha se aplica apenas a este link de protocolo.", + "detail_to": "Para", + "detail_subject": "Assunto", + "detail_no_subject": "Sem assunto", + "detail_calendar": "Calendário", + "detail_source": "Fonte", + "active_account": "Ativa", + "switching_account": "Alternando conta..." + }, + "sidebar_apps": { + "modal_title": "Apps da barra lateral", + "add_new": "Adicionar app", + "edit_app": "Editar app", + "name_label": "Nome", + "name_placeholder": "Meu app", + "name_required": "Nome é obrigatório", + "url_label": "URL", + "url_required": "URL é obrigatório", + "url_invalid": "Insira uma URL http ou https válida", + "icon_label": "Ícone", + "icon_required": "Ícone é obrigatório", + "open_mode_label": "Modo de abertura", + "open_new_tab": "Nova aba", + "open_inline": "Integrado", + "cancel": "Cancelar", + "add": "Adicionar", + "update": "Atualizar", + "delete": "Excluir", + "delete_confirm_title": "Excluir app", + "delete_confirm": "Tem certeza de que deseja excluir \"{name}\"?", + "no_apps": "Nenhum app adicionado", + "no_apps_hint": "Adicione apps e links personalizados à sua barra lateral", + "managed_title": "Fornecidas pelo seu administrador", + "managed_description": "Estas aplicações estão configuradas para todos e não podem ser alteradas.", + "managed_badge": "Gerida", + "search_icons": "Pesquisar ícones...", + "show_popular": "Populares", + "show_all": "Todos", + "no_icons_found": "Nenhum ícone encontrado", + "inline_badge": "Integrado", + "tab_badge": "Aba", + "show_on_mobile": "Mostrar no celular" + }, + "email_list": { + "columns": { + "from": "De", + "to": "Para", + "subject": "Assunto", + "date": "Data", + "size": "Tamanho" + }, + "no_emails": "Nenhuma mensagem encontrada", + "no_emails_description": "Esta caixa de correio está vazia", + "no_search_results": "Nenhum resultado encontrado", + "no_search_results_description": "Tente ajustar sua pesquisa ou filtros", + "loading": "Carregando e-mails...", + "unread": "não lido", + "to_me": "Para mim", + "to_recipients": "Para {count} destinatários", + "and_others": "e {count} outros", + "draft": "Rascunho", + "starred": "Com Estrela", + "pinned": "Fixado", + "replied": "Respondido", + "forwarded": "Encaminhado", + "has_attachment": "Com anexo", + "conversations_range": "{from}–{to, number} de {total, number}", + "conversations_count": "{count} de {total} conversas", + "conversations_count_plus": "{count}+ conversas", + "conversations_count_simple": "{count} conversas", + "no_conversations": "Nenhuma conversa", + "loading_more": "Carregando mais e-mails...", + "no_more_emails": "Não há mais e-mails para carregar", + "batch_actions": { + "select": "Selecionar e-mails", + "select_all": "Selecionar tudo", + "selected_messages": "{count, plural, one {1 e-mail selecionado} other {# e-mails selecionados}}", + "mark_read": "Marcar como lido", + "mark_unread": "Marcar como não lido", + "delete": "Excluir", + "delete_confirm_title": "Excluir e-mails", + "delete_confirm_message": "Tem certeza que deseja excluir {count, plural, one {1 e-mail} other {# e-mails}}?", + "clear_selection": "Limpar seleção" + }, + "permanent_delete": "Excluir permanentemente", + "permanent_delete_confirm_title": "Excluir permanentemente", + "permanent_delete_confirm_message": "Este e-mail será permanentemente excluído. Esta ação não pode ser desfeita.", + "permanent_delete_confirm_batch_message": "Estes {count, plural, one {1 e-mail} other {# e-mails}} serão permanentemente excluídos. Esta ação não pode ser desfeita.", + "empty_folder": { + "button": "Esvaziar pasta", + "confirm_title": "Esvaziar pasta", + "confirm_message": "Todos os e-mails nesta pasta serão permanentemente excluídos. Esta ação não pode ser desfeita.", + "confirm_message_trash": "Todos os e-mails desta pasta serão movidos para a lixeira.", + "confirm_button": "Esvaziar pasta", + "junk_hint": "Você pode esvaziar a pasta de Spam para remover permanentemente todas as mensagens.", + "trash_hint": "Você pode esvaziar a Lixeira para remover permanentemente todas as mensagens." + }, + "no_scheduled_emails": "Nenhum e-mail agendado", + "no_scheduled_emails_description": "Mensagens agendadas para mais tarde aparecerão aqui.", + "scheduled_count": "{count, plural, one {1 e-mail agendado} other {# e-mails agendados}}", + "scheduled_actions_hint": "Mensagens agendadas podem ser canceladas, reagendadas ou editadas pelas ações de agendamento.", + "cancel_scheduled_send": "Cancelar envio", + "reschedule_send": "Reagendar", + "cancel_and_edit": "Cancelar e editar", + "cancel_and_compose_again": "Cancelar e escrever novamente", + "reschedule_prompt": "Informe uma nova data/hora, ex. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "O remetente solicita uma confirmação de leitura:", + "send": "Enviar confirmação", + "ignore": "Ignorar", + "sent": "Confirmação de leitura enviada.", + "send_failed": "Não foi possível enviar a confirmação de leitura", + "mdn_subject": "Lido: {subject}", + "mdn_body": "Este é um aviso de leitura da mensagem que você enviou para {recipient}.\n\nObservação: este aviso apenas confirma que a mensagem foi exibida no computador do destinatário. Não há garantia de que o destinatário tenha lido ou compreendido o conteúdo." + }, + "no_email_selected": "Nenhum e-mail selecionado", + "no_email_description": "Selecione um e-mail da lista para visualizá-lo aqui", + "no_conversation_selected": "Nenhuma conversa selecionada", + "no_conversation_description": "Escolha uma conversa da lista para lê-la aqui", + "compose": "Redigir", + "compose_hint": "Redigir nova mensagem", + "no_subject": "(Sem Assunto)", + "no_body_content": "(Sem conteúdo disponível)", + "no_preview_available": "Pré-visualização não disponível", + "show_quoted_text": "Mostrar texto citado", + "hide_quoted_text": "Ocultar texto citado", + "loading_email": "Carregando e-mail...", + "loading": "Carregando...", + "reply": "Responder", + "reply_all": "Responder a Todos", + "forward": "Encaminhar", + "delete": "Excluir", + "archive": "Arquivar", + "archive_mailbox_not_found": "Caixa de arquivos não encontrada - não é possível arquivar e-mail", + "star": "Adicionar Estrela", + "unstar": "Remover Estrela", + "mark_unread": "Marcar como não lido", + "mark_read": "Marcar como lido", + "unread": "Não lido", + "read": "Lido", + "spam_short": "Spam", + "not_spam_short": "Não spam", + "move": "Mover", + "print": "Imprimir", + "view_source": "Ver código-fonte", + "fullscreen": "Tela cheia", + "exit_fullscreen": "Sair da tela cheia", + "view_in_dark_mode": "Ver em modo escuro", + "view_in_light_mode": "Ver em modo claro", + "export_email": "Exportar como .eml", + "forward_as_attachment": "Encaminhar como anexo", + "import_email": "Importar .eml ou .zip", + "keyboard_shortcuts": "Atalhos de teclado (?)", + "email_source": "Código-fonte do E-mail", + "draft_banner": "Esta mensagem é um rascunho", + "edit_draft": "Editar", + "copy_source": "Copiar para a área de transferência", + "source_copied": "Código-fonte copiado para a área de transferência", + "attachments": "Anexos", + "important": "Importante", + "download": "Baixar", + "download_all": "Baixar tudo", + "from": "De", + "to": "Para", + "cc": "CC", + "bcc": "CCO", + "date": "Data", + "subject": "Assunto", + "show_details": "Mostrar detalhes", + "hide_details": "Ocultar detalhes", + "external_content_warning": "Imagens e conteúdo externo foram bloqueados", + "load_external_content": "Carregar imagens", + "trust_sender": "Sempre confiar neste remetente", + "sender_check": { + "unverified_label": "Remetente não verificado", + "failed_label": "Falha na verificação do remetente", + "unverified": "Esta mensagem afirma ser de {domain}, mas nada confirma isso. Ela não tem uma assinatura válida de {domain} e não veio de um servidor autorizado por {domain}.", + "unverified_sent_from": "Esta mensagem afirma ser de {domain}, mas foi enviada de {host} e não tem uma assinatura válida de {domain}.", + "failed": "Esta mensagem afirma ser de {domain}, mas não passou nas verificações de remetente desse domínio. Pode ter sido falsificada.", + "failed_sent_from": "Esta mensagem afirma ser de {domain}, mas foi enviada de {host} e não passou nas verificações de remetente de {domain}. Pode ter sido falsificada.", + "caution": "Tenha cuidado com links, anexos e pedidos de senhas ou dados de pagamento." + }, + "back_to_list": "Voltar para a lista", + "view_contact": "Ver contato", + "message_details": "Detalhes da Mensagem", + "more_reply_options": "Mais opções de resposta", + "set_tag": "Definir etiqueta", + "tag": "Etiqueta", + "more_actions": "Mais ações", + "move_to": "Mover para...", + "remove_tag": "Remover etiqueta", + "tag_filter_placeholder": "Filtrar etiquetas", + "tag_no_matches": "Nenhuma etiqueta correspondente", + "more_count": "+{count} mais", + "characters_count": "{count} caracteres", + "quick_reply_placeholder": "Escreva uma resposta rápida...", + "more_options": "Mais opções", + "sending": "Enviando...", + "security_authentication": "Segurança & Autenticação", + "technical_details": "Detalhes Técnicos", + "message_id_label": "Message-ID:", + "reply_to_label": "Responder para:", + "delivery_time_label": "Horário de entrega:", + "conversation_part_label": "Parte da conversa:", + "previous_messages": "{count} mensagem anterior", + "previous_messages_plural": "{count} mensagens anteriores", + "time": { + "day": "dia", + "days": "dias", + "hour": "hora", + "hours": "horas", + "minute": "minuto", + "minutes": "minutos" + }, + "unknown_sender": "Desconhecido", + "recipient_me": "eu", + "recipient_and_others": "{name} e {count} outros", + "recipient_to_prefix": "Para:", + "authentication": { + "title": "Autenticação", + "status": { + "verified": "Verificado", + "warning": "Aviso", + "none": "Não autenticado" + }, + "spf": { + "pass": "SPF Aprovado", + "fail": "SPF Falhou", + "none": "Sem SPF" + }, + "dkim": { + "pass": "DKIM Válido", + "fail": "DKIM Inválido", + "none": "Sem DKIM" + }, + "dmarc": { + "pass": "DMARC Aprovado", + "fail": "DMARC Falhou", + "none": "Sem DMARC" + }, + "spam_score": "Pontuação de Spam", + "tooltip_spf": "Sender Policy Framework: verifica se o servidor de envio está autorizado a enviar e-mails em nome do domínio", + "tooltip_dkim": "DomainKeys Identified Mail: confirma, por meio de assinatura criptográfica, que o e-mail não foi alterado durante o transporte", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: garante que SPF e DKIM estejam alinhados com o domínio do remetente e define uma política para falhas", + "policy": "Política", + "result": { + "pass": "Aprovado", + "fail": "Falhou", + "softfail": "Falha parcial", + "neutral": "Neutro", + "permerror": "Erro permanente", + "temperror": "Erro temporário", + "none": "Nenhum" + } + }, + "headers": { + "routing": "Roteamento", + "received": "Recebido", + "message_id": "ID da Mensagem", + "list_info": "Informações da Lista" + }, + "tooltips": { + "reply": "Responder", + "reply_all": "Responder a todos (a)", + "forward": "Encaminhar (f)", + "archive": "Arquivar", + "delete": "Excluir", + "star": "Favoritar (s)", + "unstar": "Remover favorito (s)", + "compose": "Compor (c)", + "previous": "E-mail anterior", + "next": "Próximo e-mail", + "edit_draft": "Editar rascunho" + }, + "spam": { + "button_title": "Reportar spam", + "not_spam_title": "Marcar como legítimo", + "toast_success": "Movido para Spam", + "toast_batch": "{count} e-mails movidos para Spam", + "toast_undo": "Desfazer", + "toast_not_spam_success": "Movido para Caixa de Entrada", + "toast_not_spam_batch": "{count} e-mails movidos para Caixa de Entrada", + "error": "Falha ao reportar spam", + "error_not_spam": "Falha ao restaurar e-mail" + }, + "unsubscribe_banner": { + "label": "Newsletter", + "button": "Cancelar inscrição", + "confirm_title": "Cancelar inscrição deste remetente?", + "confirm_button": "Confirmar", + "cancel": "Cancelar", + "success_http": "Página de cancelamento aberta em nova aba", + "success_mailto": "E-mail de cancelamento enviado", + "confirm_message_http": "A página de cancelamento será aberta em uma nova aba.", + "confirm_message_mailto": "Um e-mail de cancelamento será enviado ao remetente.", + "error": "Não foi possível cancelar a inscrição", + "dismiss": "Dispensar" + }, + "calendar_invitation": { + "loading": "Carregando detalhes do evento…", + "title": "Convite de calendário", + "published_title": "Evento publicado", + "response_title": "Resposta do evento", + "update_title": "Atualização do evento", + "counter_title": "Contraproposta", + "refresh_title": "Solicitação de atualização", + "declined_counter_title": "Contraproposta recusada", + "cancelled_title": "Evento cancelado", + "organizer": "Organizado por {name}", + "organizer_label": "Organizado por", + "attendees": "{count, plural, one {# participante} other {# participantes}}", + "accept": "Aceitar", + "maybe": "Talvez", + "decline": "Recusar", + "add_to_calendar": "Adicionar ao calendário", + "added": "Adicionado ao calendário", + "rsvp_sent": "Resposta enviada", + "parse_error": "Não foi possível ler o convite", + "action_failed": "Não foi possível concluir essa ação do calendário.", + "no_calendar": "Calendário não disponível", + "published_info": "Este evento foi compartilhado como referência.", + "response_info": "Esta mensagem contém a resposta de um participante.", + "response_info_organizer": "Esta resposta de participante atualiza seu evento.", + "update_info": "Esta mensagem atualiza um evento existente.", + "counter_info": "Esta mensagem propõe alterações em um evento.", + "counter_info_organizer": "Este participante propôs alterações ao seu evento.", + "refresh_info": "Esta mensagem solicita os detalhes mais recentes do evento.", + "refresh_info_organizer": "Um participante solicitou os detalhes mais recentes do evento.", + "declined_counter_info": "O organizador recusou uma contraproposta.", + "authentication_failed_info": "As verificações de autenticação deste convite falharam. Trate as ações de calendário com cautela.", + "authentication_missing_info": "Este convite não inclui autenticação de email verificada. Confirme os detalhes com o organizador se algo parecer incomum.", + "sender_mismatch_info": "Este convite foi enviado por {sender}, enquanto o organizador indicado nos dados do calendário é {organizer}.", + "sender_mismatch_unverified_info": "Este convite foi enviado por {sender}, enquanto o organizador indicado nos dados do calendário é {organizer}, e a mensagem não pôde ser verificada.", + "organizer_role": "Você organiza este evento", + "your_response": "Sua resposta: {status}", + "response_needed": "Resposta necessária", + "response_accepted": "Aceita", + "response_tentative": "Provisória", + "response_declined": "Recusada", + "response_delegated": "Delegada", + "actor_sent_info": "Enviado por {name}.", + "actor_response_info": "{name} respondeu {status}.", + "actor_counter_info": "{name} propôs alterações para este evento.", + "actor_refresh_info": "{name} pediu os detalhes mais recentes do evento.", + "actor_declined_counter_info": "{name} recusou a contraproposta.", + "actor_note": "Nota: {comment}", + "actor_unknown": "Alguém", + "proposed_changes": "Alterações propostas", + "change_title": "Título", + "change_time": "Horário", + "change_location": "Local", + "change_description": "Descrição", + "change_empty": "Nenhuma", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Aplicar alterações propostas", + "proposal_applied": "As alterações propostas foram aplicadas.", + "review_proposal": "Revisar proposta", + "review_request": "Revisar solicitação", + "view_in_calendar": "Ver no calendário", + "select_calendar": "Selecionar calendário", + "already_in_calendar": "Já está no seu calendário", + "request_info": "Você foi convidado para este evento. Responda para informar o organizador sobre sua disponibilidade.", + "cancel_info": "O organizador cancelou este evento.", + "event_updated": "Atualização #{sequence}", + "event_status_tentative": "Provisório", + "event_status_cancelled": "Cancelado", + "collapse": "Ocultar detalhes", + "expand": "Mostrar detalhes" + }, + "previous": "Anterior", + "next": "Próximo", + "send": "Enviar", + "more": "mais", + "details": { + "recipients_routing": "Destinatários e roteamento", + "authentication_security": "Autenticação e segurança", + "identifiers_threading": "Identificadores e conversação", + "mailing_list": "Lista de discussão", + "message_properties": "Propriedades da mensagem", + "sent": "Enviado", + "received": "Recebido", + "delivery_time": "Tempo de entrega", + "in_reply_to": "Em resposta a", + "references": "Referências", + "thread_id": "ID da conversa", + "size": "Tamanho", + "mime_type": "Tipo MIME", + "attachments_summary": "{count} arquivos · {size}", + "list_id": "ID da lista", + "list_help": "Ajuda da lista", + "list_post": "Postagem na lista", + "list_unsubscribe": "Cancelar inscrição", + "iprev": "DNS reverso", + "spam_status": "Status de spam", + "ai_verdict": "Veredito da IA", + "account": "Conta", + "no_subject": "(sem assunto)" + }, + "scheduled_banner": "Agendado para enviar em {date}", + "scheduled_send_created": "E-mail agendado para envio", + "sending_in": "Enviando em {seconds} s", + "undo_send_failed": "Não foi possível desfazer. A mensagem pode já ter sido enviada.", + "cancel_scheduled_send": "Cancelar envio", + "reschedule_send": "Reagendar", + "cancel_and_edit": "Cancelar e editar", + "cancel_and_compose_again": "Cancelar e escrever novamente", + "reschedule_prompt": "Informe uma nova data/hora, ex. 2026-05-04T15:30", + "scheduled_actions_only": "A visualização Agendados permite apenas ações de agendamento", + "undo_send_scheduled": "Mensagem agendada para envio", + "undo_send": "Desfazer envio", + "contact_sidebar": { + "title": "Contato", + "close": "Fechar barra lateral", + "action_email": "E-mail", + "action_email_title": "Enviar e-mail", + "action_copy": "Copiar", + "action_copy_title": "Copiar e-mail", + "action_edit_title": "Editar contato", + "section_emails": "E-mails", + "section_phones": "Telefones", + "section_organizations": "Organizações", + "section_addresses": "Endereços", + "section_notes": "Notas", + "not_in_contacts": "Não está nos seus contatos", + "add_to_contacts": "Adicionar aos contatos", + "copied": "Copiado!", + "copy_failed": "Falha ao copiar", + "view_contact": "Ver contacto", + "view_details": "Ver detalhes" + }, + "send_now": "Enviar agora", + "verification_code": { + "copy": "Copiar código {code}", + "copied": "Código copiado", + "copy_failed": "Não foi possível copiar o código" + }, + "mv_position": "{index} de {total}", + "plugin_panel_show": "Mostrar o painel do plugin", + "plugin_panel_hide": "Ocultar o painel do plugin" + }, + "email_composer": { + "select_from_directory": "Selecionar do diretório", + "minimize": "Minimizar", + "maximize": "Ecrã inteiro", + "restore": "Restaurar", + "new_message": "Nova Mensagem", + "signature_edit_hint": "Clique duas vezes para editar a assinatura", + "reply": "Responder", + "reply_all": "Responder a Todos", + "forward": "Encaminhar", + "reply_to": "Responder", + "reply_all_to": "Responder a Todos", + "forward_message": "Encaminhar", + "from": "De", + "to": "Para", + "cc": "CC", + "bcc": "CCO", + "subject": "Assunto", + "body_placeholder": "Escreva sua mensagem...", + "send": "Enviar", + "cancel": "Cancelar", + "attach": "Anexar", + "attach_photos": "Fotos & Vídeos", + "attach_files": "Arquivos", + "attach_from_files": "Anexar de Arquivos", + "files_picker": { + "attach": "{count, plural, =0 {Anexar} one {Anexar 1 arquivo} other {Anexar # arquivos}}", + "empty_folder": "Esta pasta está vazia", + "load_failed": "Não foi possível carregar seus arquivos" + }, + "discard": "Descartar", + "discard_draft_title": "Descartar rascunho?", + "discard_draft_confirm": "Você tem alterações não salvas. Deseja descartar este rascunho?", + "discard_draft_message": "O rascunho será excluído.", + "saving": "Salvando...", + "draft_saved": "Rascunho salvo", + "save_failed": "Falha ao salvar", + "to_placeholder": "Endereços de e-mail dos destinatários", + "cc_placeholder": "Destinatários CC", + "bcc_placeholder": "Destinatários CCO", + "subject_placeholder": "Assunto", + "cc_label": "Cc:", + "bcc_label": "Cco:", + "subject_label": "Assunto:", + "file_size_kb": "KB", + "prefix": { + "forward": "Enc:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} other {# e-mails}}", + "no_subject": "(Sem Assunto)", + "unknown_sender": "Desconhecido", + "quote": { + "reply_header": "Em {date}, {sender} escreveu:", + "forward_header": "---------- Mensagem encaminhada ----------", + "from": "De: {sender}", + "date": "Data: {date}", + "subject": "Assunto: {subject}", + "to": "Para: {recipients}" + }, + "remove_sub_address": "Remover sub-endereço", + "from_override": { + "toggle_off": "Substituir", + "toggle_on": "Cancelar substituição", + "toggle_tooltip": "Edite livremente o nome e o endereço do remetente. O email ainda é enviado através da sua identidade.", + "name_label": "Nome do remetente", + "name_placeholder": "Nome", + "email_label": "Endereço de email do remetente", + "email_placeholder": "alias@example.com", + "envelope_notice": "Se o seu servidor não aceitar este endereço como remetente do envelope, {identity} é usado em seu lugar, e os destinatários podem vê-lo no cabeçalho Return-Path." + }, + "save_as_template": "Salvar como modelo", + "attach_from_device": "Enviar deste dispositivo", + "templates_menu": "Modelos", + "insert_template": "Inserir modelo", + "options": "Opções", + "option_plain_text": "Texto simples", + "option_plain_text_hint": "Enviar sem formatação", + "option_read_receipt": "Pedir confirmação de leitura", + "option_read_receipt_hint": "Pedir ao destinatário que confirme a abertura", + "option_dsn": "Notificação de entrega", + "option_dsn_hint": "Receber um relatório quando o servidor entregar", + "option_require_tls": "Exigir entrega cifrada", + "option_require_tls_hint": "Entregar só via TLS; caso contrário, devolver", + "option_priority": "Prioridade", + "option_priority_high": "Alta", + "option_priority_normal": "Normal", + "option_priority_low": "Baixa", + "validation": { + "recipient_required": "Adicione um destinatário para enviar", + "body_required": "Escreva uma mensagem ou anexe um arquivo", + "invalid_recipients": "Não enviado - verifique estes endereços: {addresses}", + "attachments_uploading": "Os anexos ainda estão sendo enviados — será enviado assim que terminar", + "attachment_upload_failed": "Não enviado - falha ao carregar um anexo. Remova-o e tente novamente." + }, + "upload_progress": "Enviando {uploaded} / {total}", + "upload_cancel": "Cancelar envio", + "upload_failed": "Falha ao enviar {filename}", + "attachment_too_large": "\"{name}\" é maior do que o servidor permite ({max} por ficheiro)", + "attachments_total_too_large": "Os anexos excederiam o limite do servidor de {max} por mensagem", + "send_failed": "Falha ao enviar o e-mail", + "smime_sign_on": "Assinatura S/MIME ativada", + "smime_sign_off": "Assinatura S/MIME desativada", + "smime_encrypt_on": "Criptografia S/MIME ativada", + "smime_encrypt_off": "Criptografia S/MIME desativada", + "smime_encrypt_unavailable": "Criptografia S/MIME indisponível – certificados de destinatário ausentes", + "smime_unlock_title": "Desbloquear chave S/MIME", + "smime_unlock_message": "Insira a frase secreta para desbloquear sua chave S/MIME.", + "smime_unlock_button": "Desbloquear", + "smime_passphrase_placeholder": "Frase secreta S/MIME", + "send_timeout": "O servidor deixou de responder. Verifique a pasta Enviados antes de enviar novamente — a mensagem pode já ter saído.", + "send_offline": "Parece que você está offline. A mensagem continua aqui; envie novamente quando voltar a ficar online.", + "formatting": "Formatação", + "send_recipients_rejected": "Não enviado: o servidor rejeitou todos os destinatários.", + "send_plugin_failed": "Não enviado: um plugin que trata do envio falhou ou demorou demasiado. Verifique a pasta Enviados antes de enviar novamente.", + "send_some_recipients_rejected": "Enviado, mas não para estes destinatários, que foram rejeitados pelo servidor.", + "continue_draft": "Continuar rascunho", + "close_draft_title": "Salvar ou descartar rascunho?", + "close_draft_message": "Você tem alterações não salvas. Deseja salvar como rascunho ou descartar?", + "drop_files": "Solte arquivos para anexar", + "show_less": "Mostrar menos", + "forgot_attachment": { + "title": "Esqueceu um anexo?", + "message": "A sua mensagem menciona \"{keyword}\" mas nenhum arquivo está anexado. Enviar mesmo assim?", + "send_anyway": "Enviar mesmo assim", + "back": "Voltar à edição" + }, + "empty_subject": { + "title": "Enviar sem assunto?", + "message": "Esta mensagem não tem assunto. Enviar mesmo assim?", + "dont_ask_again": "Não perguntar novamente", + "send_anyway": "Enviar mesmo assim", + "back": "Voltar à edição" + }, + "add_link": "Adicionar link", + "link_url_prompt": "Insira a URL", + "sending": "Enviando...", + "schedule_send": "Agendar envio", + "schedule_send_description": "Escolha quando o servidor deve liberar esta mensagem.", + "schedule_preset": { + "tomorrow_morning": "Amanhã de manhã", + "tomorrow_afternoon": "Amanhã à tarde", + "monday_morning": "Segunda de manhã" + }, + "schedule_send_required": "Escolha uma data e hora.", + "schedule_send_invalid": "Informe uma data e hora válidas.", + "schedule_send_future": "Escolha uma data e hora no futuro.", + "schedule_send_too_late": "Este horário é posterior ao permitido pelo servidor.", + "schedule_send_unsupported": "Envio agendado não é compatível com esta conta.", + "schedule_send_cleanup_warning": "O envio agendado foi criado, mas a limpeza do rascunho falhou.", + "send_delay_unsupported": "Atraso de envio não é compatível com esta conta.", + "send_delay_unsupported_confirm": "Esta conta não oferece atraso de envio. Enviar imediatamente?", + "recipient_edit_email": "Editar endereço de e-mail", + "recipient_edit_name": "Editar nome de exibição", + "recipient_email_placeholder": "Endereço de e-mail", + "recipient_name_placeholder": "Nome de exibição", + "invalid_recipient": "Endereço de e-mail inválido", + "autocomplete_search_server": "Pesquisar no servidor", + "autocomplete_searching": "Pesquisando...", + "mention_recipients": "Destinatários", + "mention_card_not_recipient": "Não está em Para nem em CC, por isso não vai receber esta mensagem.", + "mention_card_add": "Adicionar a {field}", + "mention_card_remove": "Remover menção", + "toolbar": { + "bold": "Negrito", + "italic": "Itálico", + "underline": "Sublinhado", + "strikethrough": "Tachado", + "text_color": "Cor do texto", + "remove_color": "Remover cor", + "heading_1": "Título 1", + "heading_2": "Título 2", + "bullet_list": "Lista com marcadores", + "ordered_list": "Lista numerada", + "quote": "Citação", + "code_block": "Bloco de código", + "align_left": "Alinhar à esquerda", + "align_center": "Centralizar", + "align_right": "Alinhar à direita", + "text_direction": "Direção do texto (RTL/LTR)", + "link": "Link", + "table": "Tabela", + "clear_formatting": "Limpar formatação", + "undo": "Desfazer", + "redo": "Refazer", + "add_row_above": "Adicionar linha acima", + "add_row_below": "Adicionar linha abaixo", + "add_column_before": "Adicionar coluna antes", + "add_column_after": "Adicionar coluna depois", + "delete_row": "Excluir linha", + "delete_column": "Excluir coluna", + "toggle_header_row": "Alternar linha de cabeçalho", + "delete_table": "Excluir tabela", + "pick_size": "Escolher tamanho", + "font_size": "Tamanho da fonte", + "font_size_default": "Padrão", + "background_color": "Cor de fundo", + "remove_background_color": "Remover cor de fundo" + }, + "send_filing_warning": "Enviado - mas a limpeza posterior falhou, um rascunho antigo pode permanecer.", + "pop_out": "Abrir a resposta numa janela", + "send_guard_failed": "Não enviado - um plugin que verifica as mensagens antes do envio falhou ou não respondeu." + }, + "confirm_dialog": { + "confirm": "Confirmar", + "cancel": "Cancelar" + }, + "common": { + "loading": "Carregando...", + "error": "Erro", + "success": "Sucesso", + "cancel": "Cancelar", + "save": "Salvar", + "delete": "Excluir", + "edit": "Editar", + "close": "Fechar", + "search": "Buscar", + "refresh": "Atualizar", + "settings": "Configurações", + "help": "Ajuda", + "logout": "Sair", + "yes": "Sim", + "no": "Não", + "unknown": "Desconhecido", + "app_title": "Webmail", + "reconnecting": "Conexão perdida. Tentando reconectar…", + "rate_limited_title": "A autenticação do servidor está temporariamente limitada.", + "rate_limited_detail": "O Bulwark pausou as solicitações em segundo plano para evitar bloqueio. Nova tentativa em {seconds}s.", + "rate_limited_action_title": "Solicitação pausada para evitar bloqueio.", + "rate_limited_action_detail": "O Bulwark está aguardando o fim do cooldown do servidor antes de enviar mais solicitações autenticadas. Tente novamente em {seconds}s.", + "pull_to_refresh": "Puxe para atualizar", + "release_to_refresh": "Solte para atualizar", + "refreshing": "Atualizando…" + }, + "notifications": { + "email_sent": "E-mail enviado com sucesso", + "email_deleted": "E-mail excluído", + "email_archived": "E-mail arquivado", + "emails_deleted": "{count, plural, one {# e-mail excluído} other {# e-mails excluídos}}", + "emails_archived": "{count, plural, one {# e-mail arquivado} other {# e-mails arquivados}}", + "error_archiving": "Falha ao arquivar o e-mail", + "error_updating": "Falha ao atualizar o e-mail", + "email_starred": "E-mail marcado com estrela", + "email_unstarred": "Estrela removida do e-mail", + "email_marked_read": "E-mail marcado como lido", + "email_marked_unread": "E-mail marcado como não lido", + "copied_to_clipboard": "Copiado para a área de transferência", + "source_copied": "Código-fonte copiado para a área de transferência", + "error_sending": "Falha ao enviar e-mail", + "error_deleting": "Falha ao excluir e-mail", + "error_loading": "Falha ao carregar e-mails", + "new_email": "Novo e-mail", + "new_email_from": "De {sender}", + "click_to_view": "Clique para visualizar", + "email_moved": "E-mail movido", + "emails_moved": "{count} e-mails movidos", + "moved_to_mailbox": "Movido para {mailbox}", + "move_failed": "Falha ao mover", + "removed_from_folder": "Removido de {mailbox}", + "remove_from_folder_failed": "Não foi possível remover desta pasta", + "emails_copied": "{count} e-mails copiados", + "copy_failed": "Falha ao copiar", + "move_error": "Não foi possível mover os e-mails para a pasta selecionada", + "email_tagged": "E-mail etiquetado", + "emails_tagged": "{count} e-mails etiquetados", + "tag_failed": "Falha ao etiquetar", + "identity_created": "Identidade criada com sucesso", + "identity_updated": "Identidade atualizada com sucesso", + "identity_deleted": "Identidade excluída", + "identity_set_primary": "Identidade principal atualizada", + "identity_create_failed": "Falha ao criar identidade: {error}", + "identity_update_failed": "Falha ao atualizar identidade: {error}", + "identity_delete_failed": "Falha ao excluir identidade: {error}", + "identity_unauthorized": "Você não está autorizado a enviar deste endereço de e-mail", + "identity_not_found": "Identidade não encontrada", + "vacation_saved": "Configurações de ausência salvas", + "vacation_save_failed": "Falha ao salvar configurações de ausência", + "filters_saved": "Filtros salvos com sucesso", + "filters_save_failed": "Falha ao salvar os filtros", + "filters_deleted": "Regra de filtragem eliminada", + "templates_exported": "Modelos exportados com sucesso", + "templates_imported": "{count, plural, one {# modelo importado} other {# modelos importados}}", + "templates_import_errors": "Alguns modelos não puderam ser importados", + "templates_import_empty": "Nenhum modelo encontrado no arquivo", + "export_email_error": "Falha ao exportar o e-mail", + "import_email_success": "E-mail importado com sucesso", + "import_email_error": "Falha ao importar o e-mail", + "rule_created": "Regra criada", + "rule_merged": "Adicionado à regra “{name}”", + "rule_already_covered": "Já coberto pela regra “{name}”", + "rule_apply_existing": "{count, plural, one {Aplicar a # mensagem existente} other {Aplicar a # mensagens existentes}}", + "rule_applied": "{count, plural, =0 {Nenhuma mensagem precisou de alterações} one {Regra aplicada a # mensagem} other {Regra aplicada a # mensagens}}", + "rule_apply_failed": "Não foi possível aplicar a regra às mensagens existentes", + "rule_edit": "Editar regra", + "rule_undo": "Desfazer", + "rule_undone": "Alteração da regra desfeita", + "rule_undo_failed": "Não foi possível desfazer a alteração da regra", + "rule_undo_conflict": "Seus filtros mudaram desde então. Desfaça a alteração nas configurações de filtros.", + "rule_folder_failed": "Não foi possível criar a pasta", + "vacation_filters_save_failed": "Configurações de ausência salvas, mas não foi possível salvar o encaminhamento e os destinatários da resposta" + }, + "date": { + "today": "Hoje", + "yesterday": "Ontem", + "this_week": "Esta semana", + "last_week": "Semana passada", + "this_month": "Este mês", + "older": "Mais antigos", + "just_now": "Agora mesmo", + "minutes_ago": "{count} minuto atrás", + "minutes_ago_plural": "{count} minutos atrás", + "hours_ago": "{count} hora atrás", + "hours_ago_plural": "{count} horas atrás", + "days_ago": "{count} dia atrás", + "days_ago_plural": "{count} dias atrás" + }, + "language": { + "title": "Idioma", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Selecionar idioma", + "switch_to_english": "Mudar para Inglês", + "switch_to_french": "Mudar para Francês", + "switch_to_japanese": "Mudar para Japonês", + "switch_to_spanish": "Mudar para Espanhol", + "switch_to_italian": "Mudar para Italiano", + "switch_to_german": "Mudar para Alemão", + "switch_to_dutch": "Mudar para Holandês", + "switch_to_portuguese": "Mudar para Português", + "switch_to_russian": "Mudar para Russo", + "switching": "Alterando idioma...", + "switch": "Alterar idioma", + "current": "Idioma atual", + "polish": "Polski", + "switch_to_polish": "Mudar para polonês", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Mudar para persa", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Mudar para mongol" + }, + "settings": { + "title": "Configurações", + "back_to_mail": "Voltar para E-mails", + "save_success": "Configurações salvas com sucesso", + "import_success": "Configurações importadas com sucesso", + "import_error": "Falha ao importar configurações", + "reset_confirm": "Tem certeza de que deseja redefinir todas as configurações para os padrões?", + "unsaved_changes": "Você tem alterações não salvas", + "discard_changes": "Descartar alterações?", + "discard": "Descartar", + "keep_editing": "Continuar editando", + "tabs": { + "appearance": "Aparência", + "language": "Idioma, Região e Hora", + "email": "Comportamento de E-mail", + "composer": "Editor", + "privacy": "Privacidade e Segurança", + "account": "Conta", + "identities": "Identidades", + "vacation": "Ausência", + "advanced": "Avançado", + "calendar": "Calendário", + "filters": "Filtros", + "templates": "Modelos", + "folders": "Pastas", + "keywords": "Etiquetas", + "security": "Segurança", + "encryption": "Criptografia", + "files": "Arquivos", + "contacts": "Contatos", + "protocol_handlers": "Aplicativos padrão", + "sidebar_apps": "Apps da barra lateral", + "notifications": "Notificações", + "layout": "Layout", + "themes": "Temas", + "reading": "Leitura", + "composing": "Composição", + "downloads": "Downloads", + "content_senders": "Conteúdo e remetentes", + "about_data": "Sobre e dados", + "debug": "Depuração" + }, + "themes": { + "title": "Temas", + "description": "Escolha entre os temas disponibilizados pelo seu administrador e as predefinições integradas.", + "locked": "A seleção de tema está bloqueada por um administrador.", + "forced": "O tema “{name}” é imposto pelo administrador e não pode ser alterado", + "admin_theme": "Tema do administrador", + "activated": "Tema ativado", + "default_restored": "Tema padrão restaurado", + "default_name": "Padrão", + "default_badge": "Padrão", + "admin_enforced": "Imposto pelo administrador" + }, + "tab_groups": { + "general": "Geral", + "account": "Conta e identidade", + "organization": "Organização de e-mail", + "apps": "Aplicativos", + "system": "Sistema", + "appearance": "Aparência", + "mail": "Correio", + "privacy": "Privacidade e segurança", + "advanced": "Avançado" + }, + "appearance": { + "message_list_order": { + "label": "Ordem da lista de mensagens", + "description": "Como as mensagens são ordenadas na lista. A ordenação é feita pelo servidor, por isso abrange toda a pasta e não apenas as mensagens carregadas.", + "preset": { + "chronological": "Cronológica (mais recentes primeiro)", + "unread_first": "Não lidas primeiro", + "starred_first": "Com estrela primeiro", + "tagged_first": "Com etiqueta primeiro", + "custom": "Personalizada" + }, + "tag_label": "Etiqueta", + "advanced_show": "Avançado: editar níveis de ordenação", + "advanced_hide": "Ocultar avançado", + "criterion_label": "Critério do nível {n}", + "direction_label": "Direção do nível {n}", + "criteria": { + "unread": "Não lida", + "starred": "Com estrela", + "tag": "Etiqueta", + "receivedAt": "Data de receção", + "sentAt": "Data de envio", + "from": "Remetente", + "subject": "Assunto", + "size": "Tamanho" + }, + "direction": { + "unread_first": "Não lidas primeiro", + "read_first": "Lidas primeiro", + "starred_first": "Com estrela primeiro", + "unstarred_first": "Sem estrela primeiro", + "tagged_first": "Com etiqueta primeiro", + "untagged_first": "Sem etiqueta primeiro", + "newest_first": "Mais recentes primeiro", + "oldest_first": "Mais antigas primeiro", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Maiores primeiro", + "smallest_first": "Menores primeiro" + }, + "add_level": "Adicionar nível", + "remove_level": "Remover nível", + "move_up": "Mover para cima", + "move_down": "Mover para baixo", + "tiebreak_note": "Até três níveis; os níveis seguintes desempatam. «Mais recentes primeiro» é sempre aplicado por último.", + "scope": { + "label": "Aplicar a todas as pastas", + "description": "Desativado, a ordem aplica-se apenas à Caixa de entrada; as outras pastas continuam cronológicas." + }, + "unsupported_note": "O seu servidor de correio não suporta ordenação por estado de leitura, estrelas ou etiquetas. Esses níveis são ignorados e a lista mantém-se cronológica." + }, + "title": "Aparência", + "description": "Personalize a aparência do seu webmail", + "theme": { + "label": "Tema", + "description": "Escolha seu esquema de cores preferido", + "light": "Claro", + "dark": "Escuro", + "system": "Sistema" + }, + "language": { + "label": "Idioma", + "description": "Escolha seu idioma preferido" + }, + "font_size": { + "label": "Tamanho da Fonte", + "description": "Ajuste o tamanho do texto para melhor legibilidade", + "small": "Pequeno", + "medium": "Médio", + "large": "Grande" + }, + "list_density": { + "label": "Densidade", + "description": "Controle o espaçamento em toda a interface", + "extra_compact": "Extra compacto", + "compact": "Compacto", + "regular": "Normal", + "comfortable": "Confortável" + }, + "interface_layout": { + "label": "Esquema da interface", + "description": "Onde ficam a pesquisa, a navegação e as barras de ferramentas", + "default": "Predefinido", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Habilitar Animações", + "description": "Mostrar transições e efeitos suaves" + }, + "toolbar_position": { + "label": "Posição da barra de ferramentas", + "description": "Onde mostrar os botões de ação do e-mail (Responder, Arquivar, Excluir, etc.)", + "top": "Topo", + "below_subject": "Abaixo do assunto" + }, + "toolbar_labels": { + "label": "Mostrar etiquetas da barra de ferramentas", + "description": "Apresentar etiquetas de texto junto aos ícones da barra de ferramentas. Desativar para poupar espaço quando estiver familiarizado com os ícones." + }, + "hide_account_switcher": { + "label": "Ocultar seletor de conta da barra lateral", + "description": "Ocultar o seletor de conta no topo da barra lateral de pastas. Você ainda pode trocar de conta pela barra de navegação inferior." + }, + "show_rail_account_list": { + "label": "Mostrar avatares de conta na barra de navegação", + "description": "Exibir círculos de contas individuais na parte inferior da barra de navegação para troca rápida, com um botão de sair abaixo." + }, + "unified_mailbox": { + "label": "Caixa de correio unificada", + "description": "Mostrar pastas combinadas (Entrada, Enviados, etc.) de todas as contas conectadas", + "cross_account": { + "label": "Todas as contas", + "description": "Unifique a caixa de correio em todas as contas conectadas, em vez de permanecer apenas na conta ativa." + }, + "include_group": { + "label": "Incluir caixas de grupo", + "description": "Incluir também as caixas partilhadas ou de grupo na vista unificada." + } + }, + "all_mail": { + "folders_label": "Pastas em Todos os E-mails", + "folders_description": "Escolha quais pastas serão mescladas na visualização de Todos os E-mails.", + "account_hint": "Aplica-se a {account}.", + "no_folders": "Nenhuma pasta disponível." + }, + "colorful_sidebar_icons": { + "label": "Ícones coloridos na barra lateral", + "description": "Colorir ícones de pastas e etiquetas por tipo (Caixa de entrada azul, Spam vermelho, Enviados verde, etc.). Desative para uma barra lateral monocromática." + }, + "tint_list_rows": { + "label": "Colorir as linhas da lista de acordo com a cor da etiqueta", + "description": "Aplicar sombreado a cada linha de mensagem com a cor da primeira etiqueta. Desative essa opção para manter as linhas sem sombreado; os pontos e os ícones das etiquetas continuarão exibindo a cor." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Mostrar contagem total de mensagens", + "description": "Mostra a contagem total de mensagens ao lado de pastas e etiquetas, além da contagem de não lidas. Desative para mostrar apenas as não lidas." + }, + "favicon_unread_badge": { + "label": "Contagem de não lidos no Ícone da Aba", + "description": "Mostra a contagem de e-mails não lidos na aba do navegador, para que novos e-mails sejam visíveis mesmo quando a aba não estiver em foco. Desative para manter o ícone sem marca." + }, + "show_shared_mail_accounts": { + "label": "Mostrar contas de e-mail compartilhadas", + "description": "Mostra as pastas das caixas compartilhadas com você em Compartilhado, na barra lateral. Desative para ocultar todas, ou oculte contas individuais abaixo. Os calendários e contatos continuam visíveis." + }, + "pro_interface": { + "label": "Interface Pro (experimental)", + "description": "Layout para utilizadores avançados apenas em desktop com navegação de mensagens em múltiplos separadores e fluxos entre contas. A interface padrão não é afetada; pode voltar a qualquer momento.", + "open_label": "Abrir interface Pro", + "back_to_standard": "Voltar ao padrão" + }, + "cross_unread": { + "label": "Todas as contas: Não lido", + "description": "Mostra uma entrada na seção Todas as contas listando o correio não lido de todas as contas, abrangendo todas as pastas exceto spam, enviados, arquivo, lixeira e rascunhos." + }, + "cross_starred": { + "label": "Todas as contas: Com estrela", + "description": "Mostra uma entrada na seção Todas as contas listando o correio com estrela de todas as contas, abrangendo todas as pastas exceto spam, enviados, arquivo, lixeira e rascunhos." + }, + "cross_all": { + "label": "Todas as contas: Todo o correio", + "description": "Mostra uma entrada na seção Todas as contas listando todo o correio de todas as contas, abrangendo todas as pastas exceto spam, enviados, arquivo, lixeira e rascunhos." + } + }, + "keywords": { + "title": "Etiquetas de e-mail", + "description": "Defina etiquetas para organizar os seus e-mails com cores. São armazenadas como palavras-chave JMAP no servidor.", + "add_keyword": "Adicionar etiqueta", + "label_field": "Nome de exibição", + "label_placeholder": "ex. Trabalho, Pessoal, Urgente", + "id_field": "ID da etiqueta", + "id_placeholder": "ex. trabalho, pessoal", + "color_field": "Cor", + "id_exists": "Este ID de etiqueta já existe", + "edit": "Editar etiqueta", + "delete": "Eliminar etiqueta", + "save": "Salvar", + "add": "Adicionar", + "cancel": "Cancelar", + "migrating": "A atualizar etiqueta nos e-mails existentes…", + "migration_error": "Falha ao atualizar etiqueta nos e-mails existentes", + "filters_migration_error": "Algumas regras de filtro ainda usam a etiqueta antiga — atualize-as manualmente", + "nesting": { + "label": "Etiquetas aninhadas", + "description": "Aninhe etiquetas sob outras etiquetas e mostre-as como uma árvore na barra lateral." + }, + "parent_field": "Etiqueta principal", + "no_parent": "Sem etiqueta principal", + "too_long": "Este caminho de etiqueta é demasiado longo (no máximo {max} caracteres)", + "has_children_locked": "Existem outras etiquetas aninhadas sob esta, por isso o seu nome e a sua etiqueta principal estão bloqueados. Mova-as ou remova-as primeiro.", + "has_children_delete": "Remova primeiro as etiquetas aninhadas sob esta", + "visibility_field": "Visibilidade na barra lateral", + "visibility": { + "show": "Mostrar", + "unread": "Mostrar se não lidas", + "hide": "Ocultar" + }, + "unrecognized": { + "title": "Recuperar etiquetas do servidor", + "description": "Os nomes e as cores das etiquetas são guardados neste navegador, mas as etiquetas em si estão nos seus e-mails como palavras-chave JMAP. Analise o seu correio à procura de palavras-chave sem etiqueta definida aqui e volte a adicioná-las.", + "scan": "Procurar etiquetas", + "rescan": "Analisar novamente", + "scanning": "A analisar… {scanned} de {total} e-mails", + "none": "Todas as etiquetas dos seus e-mails já estão definidas aqui.", + "messages": "{count, plural, one {# e-mail} other {# e-mails}}", + "add_all": "Adicionar todas ({count})", + "partial": "Apenas os {scanned} e-mails mais recentes de {total} foram analisados. Podem faltar etiquetas usadas apenas em correio mais antigo.", + "error": "Não foi possível analisar o seu correio à procura de etiquetas." + } + }, + "notifications": { + "test_sound": "Testar som de notificação", + "sounds": { + "default": "Padrão (Bipe)", + "cheerful": "Alegre", + "involved": "Elaborado", + "swift": "Gesto rápido", + "relax": "Relaxante" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Som de notificação", + "description": "Escolha qual som reproduzir para notificações", + "choose": "Som", + "choose_desc": "Selecione um toque de notificação e clique no ícone do alto-falante para pré-visualizar" + }, + "email": { + "title": "Notificações de e-mail", + "description": "Configurar notificações para e-mails recebidos", + "enabled": "Notificações de e-mail", + "enabled_desc": "Mostrar notificações quando novos e-mails chegarem", + "sound": "Som de notificação", + "sound_desc": "Reproduzir um som quando novos e-mails chegarem", + "inbox_only": "Apenas Caixa de Entrada", + "inbox_only_desc": "Notificar apenas pelos e-mails que chegarem à Caixa de Entrada; pular e-mails que forem filtrados para outra pasta" + }, + "calendar": { + "title": "Notificações de calendário", + "description": "Configurar notificações para eventos do calendário", + "enabled": "Notificações de eventos", + "enabled_desc": "Mostrar alertas para próximos eventos do calendário", + "sound": "Som de notificação", + "sound_desc": "Reproduzir um som para lembretes do calendário", + "invitation_parsing": "Analisar convites por e-mail", + "invitation_parsing_desc": "Detectar convites de calendário em anexos de e-mail e mostrar ações de calendário" + }, + "push": { + "confirm_disable_message": "Este dispositivo deixará de receber alertas quando o site estiver fechado.", + "confirm_disable_title": "Desativar notificações em segundo plano?", + "description": "Receba notificações do sistema para novas mensagens quando este site estiver fechado. Entregue através do relay push do Bulwark; o relay nunca vê o conteúdo da mensagem.", + "disable": "Desativar", + "enable": "Ativar", + "dismiss_aria": "Dispensar aviso de notificações", + "ios_hint": "No iOS, instale o site primeiro na tela inicial – o Safari só entrega Web Push para PWAs instalados.", + "reenable": "Registrar novamente", + "relay_desc": "Usa o relay Bulwark hospedado por padrão. Altere apenas se você hospedar.", + "relay_label": "Relay push", + "relay_locked": "Definido pelo administrador", + "relay_locked_desc": "A URL do relay push foi definida pelo seu administrador e não pode ser alterada.", + "status_active": "Ativo neste dispositivo", + "status_busy": "Processando…", + "status_inactive": "Não ativado neste dispositivo", + "status_unsupported": "Este navegador não oferece suporte a Web Push", + "status_lite": "O Web Push não está disponível no Bulwark Lite; ele requer o servidor do Bulwark.", + "title": "Notificações em segundo plano", + "devices_title": "Dispositivos registrados", + "devices_desc": "Todos os dispositivos registrados para receber notificações em segundo plano desta conta. Revogar um deles interrompe o recebimento imediatamente.", + "devices_loading": "Carregando dispositivos registrados…", + "devices_empty": "Nenhum dispositivo está registrado para notificações em segundo plano.", + "devices_error": "Não foi possível carregar os dispositivos registrados.", + "devices_refresh": "Atualizar", + "device_this": "Este dispositivo", + "device_other": "Outro dispositivo", + "device_status_active": "Entregando notificações", + "device_status_inactive": "Nunca entregou uma notificação", + "device_status_unknown": "Desconhecido para este relay", + "device_expires": "Expira em {date}", + "revoke": "Revogar", + "confirm_revoke_title": "Revogar este dispositivo?", + "confirm_revoke_message": "Ele deixará de receber notificações em segundo plano imediatamente. Ativar o push novamente nesse dispositivo o registrará de novo.", + "confirm_revoke_message_this": "Este dispositivo deixará de receber notificações em segundo plano imediatamente." + } + }, + "language_region": { + "title": "Idioma, Região e Hora", + "description": "Idioma, formato de data, formato de hora e outras preferências regionais", + "language": { + "label": "Idioma", + "description": "Escolha seu idioma preferido", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Formato de Data", + "description": "Como as datas são mostradas na lista de e-mails", + "smart": "Inteligente", + "relative": "Relativo", + "full": "Sempre data completa", + "preview_today": "Hoje:", + "preview_this_week": "Esta semana:", + "preview_older": "Mais antigo:" + }, + "date_locale": { + "label": "Formato de data da região", + "description": "Como as datas numéricas são ordenadas (dia, mês, ano)", + "auto": "Automático", + "iso": "ISO 8601", + "dmy": "Dia/Mês/Ano", + "mdy": "Mês/Dia/Ano" + }, + "time_format": { + "label": "Formato de Hora", + "description": "Escolha entre relógio de 12 ou 24 horas", + "12h": "12 horas", + "24h": "24 horas" + }, + "first_day": { + "label": "Primeiro Dia da Semana", + "description": "Começar a semana no domingo ou segunda-feira", + "sunday": "Domingo", + "monday": "Segunda-feira" + }, + "time_zone": { + "label": "Fuso horário", + "description": "Mostrar os horários dos e-mails e da agenda neste fuso horário, em vez daquele informado pelo seu navegador", + "auto": "Automático ({zone})", + "preview_now": "Agora:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Limpar a pesquisa ao mudar de pasta", + "description": "Clicar numa pasta (Caixa de entrada, Enviados…) limpa a pesquisa ativa e mostra essa pasta. Se desativado, a pesquisa mantém-se e volta a ser executada na pasta para a qual mudar." + }, + "request_read_receipt": { + "label": "Solicitar confirmações de leitura por padrão", + "description": "Ativar previamente a solicitação de confirmação de leitura ao redigir uma nova mensagem." + }, + "read_receipt_response": { + "label": "Responder a solicitações de confirmação de leitura", + "description": "O que fazer quando uma mensagem recebida solicita uma confirmação de leitura.", + "ask": "Perguntar sempre", + "always": "Enviar sempre", + "never": "Nunca enviar" + }, + "title": "Comportamento de E-mail", + "description": "Configure como os e-mails são manipulados", + "mark_read": { + "label": "Marcar como Lido", + "description": "Quando marcar e-mails como lidos ao abri-los", + "instant": "Instantaneamente", + "delay_3s": "Após 3 segundos", + "delay_5s": "Após 5 segundos", + "never": "Nunca" + }, + "delete_action": { + "label": "Ação de Exclusão", + "description": "O que acontece quando você exclui um e-mail", + "trash": "Mover para Lixeira", + "trash_and_read": "Mover para Lixeira e marcar como lida", + "permanent": "Excluir Permanentemente", + "warning": "Os e-mails serão excluídos permanentemente e não poderão ser recuperados. Esta ação é irreversível." + }, + "message_spacing": { + "label": "Espaçamento da mensagem", + "description": "Margem ao redor do corpo da mensagem no leitor", + "auto": "Automático (recomendado)", + "always": "Sempre adicionar espaçamento", + "edge": "De borda a borda" + }, + "plain_text_font": { + "label": "Fonte de texto simples", + "description": "Fonte usada para e-mails de texto simples", + "mono": "Monoespaçada", + "sans": "Normal (igual ao aplicativo)" + }, + "archive_mode": { + "label": "Arquivar em", + "description": "Como organizar os e-mails ao arquivar", + "single": "Uma única pasta", + "year": "Uma pasta por ano", + "month": "Uma pasta por mês", + "reorganize": "Reorganizar arquivo existente", + "reorganize_success": "{count, plural, =0 {Nenhum e-mail para reorganizar} =1 {1 e-mail reorganizado} other {# e-mails reorganizados}}", + "reorganize_error": "Falha ao reorganizar o arquivo" + }, + "permanently_delete_junk": { + "label": "Excluir spam permanentemente", + "description": "Excluir permanentemente e-mails da pasta Spam em vez de movê-los para a Lixeira" + }, + "confirm_bulk_delete": { + "label": "Confirmar antes de eliminar várias mensagens", + "description": "Perguntar antes de mover várias mensagens selecionadas para o lixo. A eliminação permanente pergunta sempre." + }, + "mail_layout": { + "label": "Layout do e-mail", + "description": "Escolha entre o painel de leitura dividido clássico e um fluxo de leitura focado estilo Gmail.", + "split": "Painel dividido", + "split_description": "Manter a lista de mensagens e o painel de leitura visíveis lado a lado.", + "focus": "Lista focada", + "focus_description": "Mostrar uma linha por mensagem e abrir o e-mail em largura total mantendo a barra lateral de pastas visível.", + "horizontal": "Painel de leitura em baixo", + "horizontal_description": "Mostre a lista de mensagens em cima e abra a mensagem selecionada num painel de leitura abaixo." + }, + "list_columns": { + "label": "Mostrar a lista em colunas", + "description": "Mostra as linhas de uma linha como uma tabela com as colunas De, Assunto, Data e Tamanho sob um cabeçalho." + }, + "disable_threading": { + "label": "Desativar agrupamento de conversas", + "description": "Mostrar e-mails como mensagens individuais em vez de agrupados por conversa" + }, + "plain_text_mode": { + "label": "Apenas texto simples", + "description": "Desativar o editor de texto formatado e enviar todos os e-mails apenas como texto simples, incluindo respostas e encaminhamentos" + }, + "auto_select_reply_identity": { + "label": "Selecionar automaticamente o endereço de resposta", + "description": "Ao responder, muda automaticamente o endereço do remetente para a identidade que recebeu a mensagem original" + }, + "reply_identity_match": { + "label": "Correspondência do endereço de receção", + "description": "Quais endereços de receção contam como seus. «Apenas endereço exato» escolhe somente uma das suas identidades configuradas; «mesmo domínio» trata também qualquer outro endereço dos seus domínios de identidade como alias catch-all e reescreve o cabeçalho De com ele. Escolha o endereço exato se esses endereços forem listas de distribuição.", + "exact": "Apenas endereço exato", + "domain": "Qualquer endereço dos meus domínios" + }, + "signature_position": { + "label": "Posição da assinatura", + "description": "Onde inserir a sua assinatura em respostas e encaminhamentos. Acima do texto citado lê-se naturalmente como fecho da resposta; abaixo mantém a mensagem original contígua.", + "above_quote": "Antes do texto citado", + "below_quote": "Depois do texto citado" + }, + "signature_separator": { + "label": "Delimitador de assinatura", + "description": "Anteceder a assinatura com a linha delimitadora padrão \"-- \" (RFC 3676). Desative se preferir passar direto da mensagem para a assinatura." + }, + "sub_address_delimiter": { + "label": "Delimitador de sub-endereço", + "description": "Caractere que separa seu nome de usuário da tag de sub-endereço. Use o delimitador configurado no seu servidor de e-mail (ex.: usuario{delimiter}tag@dominio.com).", + "option": "{delimiter} (usuario{delimiter}tag@dominio.com)", + "custom": "Personalizado…", + "custom_input_label": "Caractere delimitador personalizado" + }, + "show_preview": { + "label": "Mostrar Texto de Visualização", + "description": "Exibir visualização do e-mail na lista", + "focus_description": "Exibir texto de pré-visualização em linha dentro da lista de mensagens focada de uma linha" + }, + "show_verification_codes": { + "label": "Mostrar códigos de verificação", + "description": "Oferecer o código dos e-mails de início de sessão e confirmação para copiar com um clique, na lista durante um dia e na mensagem" + }, + "attachment_click_action": { + "label": "Ação ao clicar em anexos", + "description": "Escolha se clicar em um anexo abre uma pré-visualização ou inicia o download imediatamente", + "preview": "Visualizar quando possível", + "download": "Baixar imediatamente" + }, + "attachment_position": { + "label": "Posição dos anexos", + "description": "Onde exibir os anexos no cabeçalho do e-mail", + "beside-sender": "Ao lado do remetente", + "below-header": "Abaixo do cabeçalho" + }, + "emails_per_page": { + "10": "10 e-mails", + "25": "25 e-mails", + "50": "50 e-mails", + "100": "100 e-mails", + "label": "E-mails Por Página", + "description": "Número de e-mails a carregar de uma vez" + }, + "always_light_mode": { + "label": "Mostrar e-mails sempre em modo claro", + "description": "Exibir o conteúdo dos e-mails em modo claro mesmo quando o aplicativo está em modo escuro, evitando problemas de conversão" + }, + "external_content": { + "label": "Conteúdo Externo", + "description": "Como lidar com imagens e conteúdo externo", + "ask": "Sempre perguntar", + "block": "Sempre bloquear", + "allow": "Sempre permitir" + }, + "trusted_senders": { + "label": "Remetentes Confiáveis", + "description": "Gerencie remetentes cujas imagens são carregadas automaticamente", + "count_zero": "Nenhum", + "count_one": "1 remetente", + "count_other": "{count} remetentes", + "modal_title": "Remetentes Confiáveis", + "empty_title": "Nenhum remetente confiável ainda", + "empty_description": "Ao visualizar um e-mail com imagens bloqueadas, clique em \"Sempre confiar neste remetente\" para adicioná-lo aqui.", + "add_manually": "Adicionar remetente manualmente", + "add_button": "Adicionar", + "add_placeholder": "Digite o endereço de e-mail", + "search_placeholder": "Buscar remetentes...", + "no_results": "Nenhum remetente corresponde à sua busca", + "remove": "Remover", + "close": "Fechar", + "invalid_email": "Por favor, digite um endereço de e-mail válido", + "already_added": "Este remetente já é confiável", + "save_error": "Falha ao salvar - verifique o log de depuração de Contatos para detalhes", + "use_address_book_label": "Sincronizar com catálogo de endereços", + "use_address_book_description": "Armazene remetentes confiáveis em um catálogo \"Remetentes confiáveis\" dedicado para sincronizar em todos os seus dispositivos" + }, + "hover_actions": { + "label": "Ações rápidas ao passar o mouse", + "description": "Escolha quais ações rápidas aparecem ao passar o mouse sobre um e-mail na lista", + "delete": "Excluir", + "star": "Favoritar / Desfavoritar", + "mark_read": "Marcar como lido / não lido", + "archive": "Arquivar", + "tag": "Etiqueta", + "spam": "Marcar como spam", + "not_spam": "Não é spam", + "none_selected": "Nenhuma ação selecionada", + "mode_label": "Modo de exibição", + "mode_inline": "Em linha", + "mode_floating": "Flutuante", + "corner_label": "Posição flutuante", + "corner_top-left": "Superior esquerdo", + "corner_top-right": "Superior direito", + "corner_bottom-left": "Inferior esquerdo", + "corner_bottom-right": "Inferior direito" + }, + "default_mail_program": { + "label": "Programa de e-mail padrão", + "description": "Registrar {appName} como seu programa de e-mail padrão para links mailto:", + "button": "Definir como padrão", + "success": "O navegador solicitou definir como padrão", + "error": "Seu navegador não suporta esta funcionalidade" + }, + "attachment_reminder": { + "label": "Lembrete de anexo", + "description": "Avisar antes de enviar quando a mensagem menciona anexos mas nenhum está anexado", + "keywords_label": "Palavras-chave de ativação", + "keywords_description": "Palavras ou frases que ativam o lembrete quando encontradas na sua mensagem", + "add_placeholder": "Adicionar palavra-chave...", + "add": "Adicionar", + "remove": "Remover" + }, + "empty_subject_warning": { + "label": "Aviso de assunto vazio", + "description": "Pedir confirmação antes de enviar uma mensagem sem assunto." + }, + "recipient_mentions": { + "label": "Mencionar destinatários com @", + "description": "Ao digitar @ na mensagem, são sugeridos os destinatários e é inserido o primeiro nome do escolhido. Enter ou Tab escolhe o nome destacado." + }, + "mention_adds_recipient": { + "label": "Contactos nas menções com @", + "description": "Ao escrever @ também são sugeridos os seus contactos e pessoas do diretório. Escolher alguém que ainda não é destinatário adiciona-o à mensagem.", + "to": "Adicionar a Para", + "cc": "Adicionar a CC", + "off": "Só destinatários" + }, + "hide_inline_image_attachments": { + "label": "Ocultar imagens incorporadas dos anexos", + "description": "As imagens incorporadas no corpo da mensagem não são listadas como anexos separados" + }, + "attachment_image_previews": { + "label": "Mostrar prévias de imagens em anexos", + "description": "Renderizar anexos de imagem como miniaturas em vez de ícones genéricos de arquivo" + }, + "send_delay": { + "label": "Desfazer envio / atraso de envio", + "description": "Atrase envios normais com uma breve janela no servidor.", + "off": "Desativado", + "seconds": "{seconds} segundos", + "unsupported": "A conta atual não anuncia suporte a envio atrasado. A configuração continuará salva para outras contas." + }, + "return_to_list_after_action": { + "label": "Voltar à lista após eliminar ou marcar como não lido", + "description": "Depois de eliminar ou marcar como não lida a mensagem aberta, voltar à lista de mensagens em vez de abrir a mensagem seguinte." + }, + "swipe_right_action": { + "label": "Ação ao deslizar para a direita (celular)", + "description": "Em dispositivos de toque, a ação executada ao deslizar uma mensagem para a direita na lista." + }, + "swipe_left_action": { + "label": "Ação ao deslizar para a esquerda (celular)", + "description": "Em dispositivos de toque, a ação executada ao deslizar uma mensagem para a esquerda na lista." + }, + "swipe_actions": { + "none": "Nenhuma", + "archive": "Arquivar", + "delete": "Excluir", + "mark_read": "Alternar lido", + "star": "Alternar estrela", + "spam": "Marcar como spam" + }, + "rtl_editing": { + "label": "Suporte à edição da direita para a esquerda", + "description": "Adiciona um botão de direção à barra de ferramentas do editor para definir parágrafos da esquerda para a direita ou da direita para a esquerda" + } + }, + "composer": { + "title": "Editor", + "description": "Configure as configurações de composição de e-mail", + "autosave": { + "label": "Intervalo de Salvamento Automático", + "description": "Com que frequência salvar rascunhos automaticamente", + "30s": "A cada 30 segundos", + "1m": "A cada minuto", + "2m": "A cada 2 minutos", + "5m": "A cada 5 minutos" + }, + "send_confirmation": { + "label": "Confirmação de Envio", + "description": "Pedir confirmação antes de enviar e-mails" + }, + "default_reply": { + "label": "Modo de Resposta Padrão", + "description": "Ação padrão ao clicar em responder", + "reply": "Responder", + "reply_all": "Responder a Todos" + } + }, + "privacy": { + "title": "Privacidade e Segurança", + "description": "Gerencie suas configurações de privacidade e segurança", + "external_images": { + "label": "Bloquear Imagens Externas", + "description": "Prevenir rastreamento através de imagens externas" + }, + "session_timeout": { + "label": "Tempo Limite de Sessão", + "description": "Sair automaticamente após inatividade", + "never": "Nunca", + "30m": "30 minutos", + "1h": "1 hora", + "4h": "4 horas" + }, + "clear_cache": { + "label": "Limpar Cache", + "description": "Remover dados em cache e arquivos temporários", + "button": "Limpar Cache", + "confirm": "Tem certeza de que deseja limpar o cache?", + "success": "Cache limpo com sucesso" + } + }, + "account": { + "title": "Conta", + "description": "Visualize as informações da sua conta", + "name_label": "Nome de exibição", + "username_label": "Nome de usuário", + "account_type_label": "Tipo de conta", + "auth_method_label": "Autenticação", + "auth_method_oauth": "Login único (OAuth/OIDC)", + "auth_method_basic": "Senha", + "auth_method_token": "Token de acesso", + "demo_account": "Conta de demonstração", + "email": { + "label": "Endereço de E-mail", + "value": "{email}" + }, + "server": { + "label": "Servidor JMAP", + "value": "{server}" + }, + "storage": { + "label": "Uso de Armazenamento", + "used": "{used} de {total} usado", + "percentage": "{percent}% usado" + }, + "last_sync": { + "label": "Última Sincronização", + "value": "{time}" + }, + "accounts": { + "title": "Contas conectadas", + "description": "Arraste para reordenar como as contas aparecem no menu suspenso", + "active": "Conta atualmente ativa", + "default_badge": "Conta padrão", + "set_default": "Definir como padrão", + "switch_to": "Mudar para esta conta", + "move_up": "Mover para cima", + "move_down": "Mover para baixo", + "drag_handle": "Arraste para reordenar", + "add": "Adicionar conta" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Compartilhado comigo", + "description": "Contas de grupo e compartilhadas que você pode gerenciar. Selecione uma para editar seus filtros, resposta de ausência, calendários e contatos.", + "shared_label": "Conta compartilhada" + } + }, + "security": { + "title": "Segurança da conta", + "description": "Gerencie sua senha, autenticação de dois fatores e configurações de segurança", + "detecting": "Detectando recursos do servidor...", + "not_available": "O gerenciamento de segurança da conta não está disponível para este servidor de e-mail. As permissões necessárias podem estar desabilitadas. Consulte a documentação para obter detalhes.", + "password": { + "title": "Alterar senha", + "current": "Senha atual", + "new": "Nova senha", + "confirm": "Confirmar nova senha", + "submit": "Alterar senha", + "success": "Senha alterada com sucesso", + "error_title": "Falha ao alterar a senha", + "error_mismatch": "As novas senhas não coincidem", + "error_min_length": "A senha deve ter pelo menos 8 caracteres", + "error_generic": "Não foi possível alterar a senha" + }, + "display_name": { + "label": "Nome de exibição", + "description": "Seu nome como aparece no servidor", + "placeholder": "Digite seu nome de exibição", + "save": "Salvar", + "success": "Nome de exibição atualizado", + "error": "Não foi possível atualizar o nome de exibição" + }, + "totp": { + "section_title": "Autenticação de dois fatores", + "label": "Autenticação TOTP", + "description": "Adicione uma camada extra de segurança com uma senha única baseada em tempo", + "active": "Habilitado", + "inactive": "Desabilitado", + "enabled": "Autenticação de dois fatores habilitada", + "disabled": "Autenticação de dois fatores desabilitada", + "enable_error": "Não foi possível habilitar a 2FA", + "disable_error": "Não foi possível desabilitar a 2FA", + "setup_instructions": "Copie esta URL para seu aplicativo de autenticação (Google Authenticator, Authy, etc.):", + "verification_code": "Código de verificação", + "confirm": "Confirmar", + "disable": "Desativar", + "disable_confirm_prompt": "Digite sua senha para desativar a autenticação de dois fatores.", + "password_required": "A senha é obrigatória", + "code_required": "O código de verificação é obrigatório", + "code_invalid": "Código de verificação inválido. Verifique seu aplicativo autenticador e tente novamente." + }, + "app_passwords": { + "title": "Senhas de aplicativo", + "description": "Crie senhas para aplicativos que não suportam autenticação de dois fatores", + "add": "Adicionar", + "create": "Criar", + "cancel": "Cancelar", + "generate": "Gerar", + "name_label": "Nome do aplicativo", + "name_placeholder": "ex. Thunderbird, iPhone Mail", + "password_label": "Senha (deixe vazio para auto-gerar)", + "password_placeholder": "Auto-gerada se vazio", + "added": "Senha de aplicativo criada", + "removed": "Senha de aplicativo removida", + "add_error": "Não foi possível criar a senha de aplicativo", + "remove_error": "Não foi possível remover a senha de aplicativo", + "none": "Nenhuma senha de aplicativo configurada", + "done": "Concluído", + "expires_label": "Expira (opcional)", + "copy_now_warning": "Copie esta senha agora - ela não será exibida novamente.", + "allowed_ips_label": "IPs permitidos (opcional)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Separados por vírgula ou espaço. Deixe vazio para permitir qualquer IP." + }, + "api_keys": { + "title": "Chaves de API", + "description": "Crie chaves de API para scripts e integrações que se comunicam diretamente com o servidor", + "name_label": "Nome da chave", + "name_placeholder": "ex. Script de backup, CI runner", + "copy_now_warning": "Copie esta chave de API agora - ela não será mostrada novamente.", + "added": "Chave de API criada", + "removed": "Chave de API removida", + "add_error": "Falha ao criar chave de API", + "remove_error": "Falha ao remover chave de API", + "none": "Nenhuma chave de API configurada" + }, + "encryption": { + "section_title": "Criptografia em repouso", + "label": "Criptografia de e-mail", + "description": "Criptografe e-mails armazenados no servidor para maior privacidade", + "active": "Criptografia {type} habilitada", + "inactive": "Desabilitado", + "enabled": "Criptografia em repouso habilitada", + "disabled_success": "Criptografia em repouso desabilitada", + "error": "Não foi possível atualizar as configurações de criptografia", + "algorithm_label": "Algoritmo de criptografia", + "encrypt_on_append": "Criptografar novos e-mails ao carregar", + "allow_spam_training": "Permitir treino de spam antes de criptografar os e-mails", + "enable_encryption": "Ativar criptografia", + "enable_tooltip": "Ativar criptografia com esta chave", + "configure_title": "Ativar criptografia com esta chave", + "enable_button": "Ativar", + "disable_tooltip": "Desativar criptografia", + "enabled_success": "Criptografia em repouso habilitada" + }, + "email_client": { + "title": "Configuração do Cliente de E-mail", + "description": "Use estas credenciais para configurar seu cliente de e-mail desktop ou móvel (Thunderbird, Apple Mail, Outlook, etc.)", + "jmap_username_label": "Nome de Usuário JMAP", + "copy": "Copiar", + "copied": "Copiado", + "password_instructions": "Use seu nome de usuário JMAP acima junto com uma senha de aplicativo para entrar no seu cliente de e-mail. Crie uma senha de aplicativo na seção acima se ainda não tiver uma." + }, + "link_device": { + "title": "Vincular aplicativo móvel", + "description": "Entre no aplicativo Bulwark Mail no seu celular sem digitar sua senha nele. Seu celular recebe um acesso próprio, então sair aqui não o desconecta.", + "generate": "Mostrar código QR", + "regenerate": "Mostrar um novo código", + "instructions": "Abra o aplicativo Bulwark Mail e toque em \"Scan a sign-in code\" na tela de login (ou em \"Add account\" → \"Scan a sign-in code\" se você já estiver conectado) e aponte a câmera para cá.", + "expires_in": "Este código expira em {seconds} segundos. Ele só pode ser usado uma vez.", + "expired": "Este código expirou.", + "error": "Não foi possível criar um código de pareamento. Tente novamente.", + "password_prompt": "Digite sua senha para confirmar que é você. Seu celular recebe um acesso próprio; sua senha não é enviada ao celular.", + "show_password": "Mostrar senha", + "hide_password": "Ocultar senha", + "continue": "Continuar", + "error_wrong_password": "Senha incorreta. Tente novamente.", + "totp_prompt": "Digite o código do seu aplicativo autenticador.", + "error_password_or_code": "A senha ou o código está incorreto.", + "error_not_signed_in": "Sua sessão expirou. Entre novamente para vincular um dispositivo.", + "error_impersonation": "Não é possível vincular dispositivos enquanto você visualiza a caixa de correio de outro usuário.", + "error_too_many_attempts": "Muitas tentativas. Aguarde um momento e tente novamente.", + "error_session_secret": "A vinculação de dispositivos não está configurada neste servidor (SESSION_SECRET ausente). Fale com o seu administrador.", + "error_server_unreachable": "Não foi possível acessar seu servidor de e-mail. Tente novamente.", + "error_pairing_unavailable": "Seu servidor de e-mail não permite vincular um dispositivo dessa forma. Fale com o seu administrador.", + "qr_alt": "Código QR de login", + "link_label": "Link de login", + "copy_link": "Copiar link de login", + "copied": "Copiado", + "copy_hint": "Sem câmera? Copie o link e cole-o no aplicativo com \"Paste sign-in link\".", + "success_title": "Conectado no seu celular", + "success_body": "Você pode fechar esta página. Para vincular outro dispositivo, mostre um novo código.", + "link_another": "Vincular outro dispositivo", + "use_sso": "Usar o início de sessão único", + "error_insecure_server": "A aplicação móvel só se liga a servidores de correio por https, e este usa http." + }, + "public_keys": { + "name_label": "Nome da chave pública", + "name_placeholder": "ex. A minha chave", + "key_label": "Chave pública (S/MIME ou PGP em formato ASCII)", + "key_placeholder": "Cole aqui a sua chave pública", + "none": "Nenhuma chave pública configurada" + } + }, + "identities": { + "title": "Identidades de Envio", + "description": "Gerencie endereços de e-mail que você pode usar para enviar", + "identities_count": { + "label": "Suas Identidades", + "description": "Endereços de e-mail configurados para envio", + "count_zero": "Nenhuma identidade", + "count_one": "1 identidade", + "count_other": "{count} identidades" + }, + "manage": "Gerenciar Identidades", + "sub_addressing": { + "label": "Sub-Endereçamento", + "description": "Use tags como usuario+tag@dominio.com para organizar e-mails recebidos", + "learn_more": "Saiba Mais" + } + }, + "vacation": { + "title": "Ausência", + "description": "Tratar automaticamente os e-mails recebidos enquanto estiver ausente", + "loading": "Carregando configurações de resposta automática...", + "not_supported": "Seu servidor de e-mail não suporta respostas automáticas de ausência.", + "fetch_error": "Falha ao carregar as configurações de resposta automática. Tente novamente.", + "status": { + "label": "Responder automaticamente", + "description": "Enviar uma resposta automática para quem lhe enviar um e-mail", + "active": "Ativo", + "inactive": "Inativo" + }, + "date_range": { + "start": "Data de início", + "start_description": "Deixe vazio para sem limite de início", + "end": "Data de fim", + "end_description": "Deixe vazio para sem limite de fim" + }, + "message": { + "subject_label": "Assunto", + "subject_description": "Linha de assunto da resposta automática", + "subject_placeholder": "Fora do escritório", + "body_label": "Corpo da mensagem", + "body_description": "Conteúdo da mensagem em texto simples", + "body_placeholder": "Obrigado pelo seu e-mail. Estou atualmente fora do escritório e responderei quando retornar.", + "html_label": "Mensagem formatada (HTML)", + "html_description": "Adicione uma versão formatada, com links e estilo. Os destinatários cujo cliente de e-mail não a exibir verão o texto simples acima.", + "html_placeholder": "Escreva uma resposta de ausência formatada…" + }, + "preview": { + "title": "Pré-visualização", + "show": "Mostrar pré-visualização", + "hide": "Ocultar pré-visualização" + }, + "save": "Salvar alterações", + "saving": "Salvando...", + "warnings": { + "end_before_start": "A data de fim deve ser posterior à data de início", + "invalid_date": "Indique uma data de início e de fim válidas com hora ou deixe o campo vazio", + "start_in_past": "A data de início está no passado", + "empty_body": "O corpo da mensagem está vazio - os destinatários receberão uma resposta em branco", + "subject_too_long": "O assunto é demasiado longo para o servidor (no máximo {max} bytes).", + "body_too_long": "A mensagem é demasiado longa para o servidor (no máximo {max} bytes, incluindo a formatação).", + "forward_address": "Informe um endereço válido para o encaminhamento", + "not_running": "O encaminhamento ou os destinatários da resposta estão salvos, mas não estão ativos agora. Salve para reativá-los." + }, + "forward": { + "title": "Encaminhamento", + "description": "Encaminha as mensagens recebidas para outro endereço, com ou sem resposta automática, dentro do período acima (sem data de término, até ser desativado). Spam não é encaminhado.", + "enabled_label": "Encaminhar mensagens", + "to_label": "Encaminhar para", + "to_placeholder": "email@exemplo.com", + "keep_label": "Manter na caixa de entrada", + "keep_description": "Caso contrário, a mensagem é apenas encaminhada: ela não fica nesta caixa de correio e nenhuma regra de filtro é aplicada a ela." + }, + "audience": { + "label": "Responder a", + "description": "Remetentes internos: {domains}", + "all": "Todos os remetentes", + "internal": "Somente remetentes internos", + "external": "Somente remetentes externos" + }, + "auto_reply": { + "title": "Resposta automática" + } + }, + "folders": { + "role_memos": "Notas", + "title": "Pastas", + "description": "Gerencie suas pastas de e-mail e atribua funções padrão", + "folder_list": "Suas pastas", + "folder_list_description": "Clique no ícone de uma pasta para personalizá-la", + "standard_roles": "Funções padrão de pastas", + "standard_roles_description": "Defina quais pastas são utilizadas para funções padrão como Caixa de entrada, Enviados, Lixeira, etc.", + "role_inbox": "Caixa de entrada", + "role_drafts": "Rascunhos", + "role_sent": "Enviados", + "role_trash": "Lixeira", + "role_junk": "Spam / Lixo eletrônico", + "role_archive": "Arquivo", + "role_scheduled": "Programado", + "role_snoozed": "Adiado", + "role_none": "Nenhum", + "create_folder": "Criar pasta", + "create_subfolder": "Criar subpasta", + "reorder": "Arraste para reordenar", + "reorder_error": "Falha ao reordenar as pastas", + "move_error": "Falha ao mover a pasta", + "select_folder": "Selecionar {name}", + "select_hint": "Selecionar. Shift+clique seleciona todas as pastas até a última em que você clicou.", + "selected_count": "{count, plural, one {# pasta selecionada} other {# pastas selecionadas}}", + "move_to": "Mover para...", + "move_to_top_level": "Nível superior", + "move": "Mover", + "clear_selection": "Limpar seleção", + "folders_moved": "{count, plural, one {# pasta movida} other {# pastas movidas}}", + "move_partial_error": "{count, plural, one {Não foi possível mover # pasta} other {Não foi possível mover # pastas}}", + "subfolder_of": "Dentro de {name}", + "subfolder_name": "Nome da subpasta", + "new_folder_name": "Nome da pasta", + "rename": "Renomear", + "change_icon": "Alterar ícone", + "count_mode": "Contador de {name}", + "count_mode_default": "Contador: padrão", + "count_mode_unread": "Apenas não lidas", + "count_mode_all": "Não lidas e total", + "count_mode_none": "Sem contador", + "delete": "Excluir", + "confirm_delete": "Tem certeza de que deseja excluir \"{name}\"? Os e-mails nesta pasta serão movidos para a Lixeira.", + "create": "Criar", + "cancel": "Cancelar", + "no_folders": "Sem pastas personalizadas", + "cannot_delete_role": "Não é possível excluir uma pasta com uma função padrão. Remova a função primeiro.", + "folder_created": "Pasta criada", + "folder_renamed": "Pasta renomeada", + "folder_deleted": "Pasta excluída", + "role_updated": "Função da pasta atualizada", + "error_create": "Falha ao criar a pasta", + "error_rename": "Falha ao renomear a pasta", + "error_delete": "Falha ao excluir a pasta", + "error_delete_has_children": "Não é possível excluir a pasta: ela ainda contém subpastas. Exclua ou mova-as primeiro.", + "error_delete_has_email": "Não é possível excluir a pasta: ela ainda contém e-mails. Mova ou exclua-os primeiro.", + "error_role": "Falha ao atualizar a função da pasta" + }, + "advanced": { + "title": "Avançado", + "description": "Opções avançadas e configurações de desenvolvedor", + "debug_mode": { + "label": "Modo de Depuração", + "description": "Habilitar registro detalhado para solução de problemas" + }, + "debug_categories": { + "description": "Selecione quais categorias registrar. Desative as categorias de que você não precisa para reduzir o ruído no console.", + "jmap": "Cliente JMAP", + "jmap_description": "Operações de caixa de correio, busca de e-mails e solicitações do protocolo JMAP", + "calendar": "Calendário", + "calendar_description": "Eventos do calendário, importações e mensagens de agendamento", + "tasks": "Tarefas", + "tasks_description": "Criação, busca e atualização de tarefas do calendário", + "auth": "Autenticação", + "auth_description": "Login, TOTP, troca de tokens e gerenciamento de sessão", + "filters": "Filtros", + "filters_description": "Regras de filtro Sieve e scripts de férias", + "email": "Visualização de email", + "email_description": "Renderização de emails, processamento TNEF e marcação como lido", + "push": "Notificações push", + "push_description": "Configuração e entrega de notificações push", + "contacts": "Contatos e catálogos de endereços", + "contacts_description": "Sincronização de contatos, operações do catálogo e remetentes confiáveis" + }, + "settings_sync": { + "label": "Sincronização de configurações", + "description": "Sincronize suas configurações entre navegadores e dispositivos" + }, + "sender_favicons": { + "label": "Favicons de remetente", + "description": "Exibir ícones de sites como fotos de perfil para remetentes empresariais", + "refresh_label": "Atualizar ícones dos remetentes", + "refresh_description": "Obter novamente os ícones dos sites, por exemplo quando um remetente alterou o seu", + "refresh_button": "Atualizar", + "refresh_done": "Os ícones dos remetentes serão obtidos novamente" + }, + "show_avatars_in_junk": { + "label": "Mostrar avatares na pasta de spam", + "description": "Exibir imagens de perfil e favicons dos remetentes na pasta de spam. Desativado por padrão para não dar aparência legítima a tentativas de phishing." + }, + "keyboard_shortcuts": { + "label": "Atalhos de Teclado", + "description": "Visualizar atalhos de teclado disponíveis", + "button": "Ver Atalhos" + }, + "refresh_cache": { + "label": "Atualizar dados armazenados em cache", + "description": "Atualize os contatos, calendários e pastas do servidor. Mantém suas contas e sessões — corrige uma visualização desatualizada ou incorreta sem precisar sair da conta.", + "button": "Recarregar" + }, + "reset_settings": { + "label": "Redefinir Configurações", + "description": "Restaurar todas as configurações para valores padrão", + "button": "Redefinir para Padrões" + }, + "export_settings": { + "label": "Exportar Configurações", + "description": "Baixar suas configurações como JSON", + "button": "Exportar" + }, + "import_settings": { + "label": "Importar Configurações", + "description": "Fazer upload de configurações de arquivo JSON", + "button": "Importar" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Código-fonte" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Clique em spam e phishing antes que cheguem à sua caixa de entrada. Deixe passar os e-mails reais. Se perder os três escudos, a caixa cai.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "E-mail real" + }, + "click_to_block": "Clique para bloquear", + "let_through": "Deixe passar", + "start": "Começar", + "play_again": "Jogar de novo", + "score": "Pontos", + "combo": "Bônus de sequência", + "shields_left": "{count} de {total} escudos restantes", + "seconds": "{seconds} s", + "best": "Recorde: {score}", + "new_best": "Novo recorde", + "held": "Caixa de entrada defendida", + "overrun": "Caixa de entrada invadida", + "stats": { + "caught": "Bloqueados", + "delivered": "Entregues", + "mistakes": "Erros" + } + } + }, + "sidebar_apps": { + "title": "Apps da barra lateral", + "description": "Gerenciar aplicativos e links personalizados na barra lateral", + "keep_loaded": "Manter apps carregados", + "keep_loaded_description": "Manter apps inline em execução em segundo plano ao alternar entre eles para evitar recarregamento", + "managed_title": "Fornecidas pelo seu administrador", + "managed_description": "Estas aplicações estão configuradas para todos e não podem ser alteradas.", + "manage_title": "Apps personalizados", + "manage_description": "Adicionar, editar ou remover apps personalizados da barra lateral" + }, + "contacts": { + "title": "Contatos", + "description": "Importar e exportar os seus contatos", + "import_label": "Importar contatos", + "import_description": "Importar contatos de um arquivo vCard (.vcf)", + "export_label": "Exportar contatos", + "export_description": "Exportar todos os contatos como arquivo vCard (.vcf)", + "manage_title": "Catálogos de endereços", + "manage_description": "Renomeie seus catálogos de endereços", + "no_address_books": "Nenhum catálogo de endereços encontrado", + "categories_title": "Categorias", + "categories_description": "Renomear categorias de contatos", + "no_categories": "Nenhuma categoria encontrada", + "group_by_letter_description": "Mostrar cabeçalhos de seção alfabéticos na lista de contatos", + "group_by_letter_label": "Agrupar pela primeira letra", + "sort_by_last_name_label": "Ordenar por sobrenome", + "sort_by_last_name_description": "Ordenar a lista de contatos por sobrenome para que os membros da família apareçam juntos" + }, + "downloads": { + "title": "Downloads", + "description": "Personalize como os e-mails e anexos baixados são nomeados.", + "reset": "Restaurar padrão", + "preview": "Pré-visualização:", + "email_template": { + "label": "Nome do arquivo de e-mail (.eml)", + "description": "Modelo usado ao exportar um e-mail ou arrastá-lo para o sistema de arquivos. A extensão .eml é adicionada automaticamente." + }, + "attachment_template": { + "label": "Nome do arquivo do anexo", + "description": "Modelo usado ao baixar ou arrastar um anexo. Se você omitir '{filename}' e '{ext}', a extensão original é preservada." + }, + "bundle_template": { + "label": "Nome do arquivo .zip multi-e-mails", + "description": "Modelo usado ao arrastar ou baixar vários e-mails selecionados como um único arquivo .zip. A extensão .zip é adicionada automaticamente." + }, + "spaces": { + "label": "Espaços", + "description": "Substitui espaços no nome de arquivo resultante por outro caractere.", + "keep": "Manter espaços", + "underscore": "Substituir por _", + "dash": "Substituir por -" + }, + "lowercase": { + "label": "Minúsculas", + "description": "Forçar todo o nome do arquivo para minúsculas." + }, + "strip_diacritics": { + "label": "Remover acentos", + "description": "Converte letras acentuadas para o equivalente ASCII (ä → a, é → e). Útil para ferramentas que estragam nomes Unicode." + }, + "collapse_separators": { + "label": "Combinar separadores repetidos", + "description": "Combina sequências de espaços, sublinhados ou hifens em um único caractere." + }, + "after_export": { + "label": "Após exportar", + "description": "Opcionalmente, mover o e-mail depois de exportá-lo como .eml.", + "keep": "Manter na caixa", + "archive": "Mover para o arquivo", + "trash": "Mover para a lixeira" + } + }, + "filters": { + "title": "Filtros de e-mail", + "description": "Crie regras para classificar, rotular e gerenciar automaticamente os e-mails recebidos", + "add_rule": "Adicionar regra", + "no_rules": "Nenhuma regra de filtro", + "vacation_active": "O respondedor de ausência está ativo", + "vacation_active_description": "A resposta automática está ativada para mensagens recebidas", + "vacation_configure": "Configurar", + "no_rules_description": "Crie regras para organizar automaticamente seus e-mails recebidos", + "edit_rule": "Editar regra", + "new_rule": "Nova regra", + "delete_rule": "Excluir regra", + "delete_confirm": "Tem certeza de que deseja excluir esta regra?", + "enable": "Ativar", + "disable": "Desativar", + "raw_editor": "Editor Sieve bruto", + "raw_editor_warning": "Editar o script Sieve bruto pode quebrar a edição visual de regras. As alterações feitas aqui substituem o construtor visual.", + "validate": "Validar", + "validation_success": "O script é válido", + "validation_error": "O script contém erros", + "save": "Salvar regras", + "saving": "Salvando...", + "saved": "Filtros salvos com sucesso", + "save_failed": "Falha ao salvar os filtros", + "loading": "Carregando filtros...", + "not_supported": "Seu servidor de e-mail não suporta filtros de e-mail.", + "rule_name": "Nome da regra", + "rule_name_placeholder": "ex. Classificar newsletters", + "match_all": "Corresponder a TODAS as condições", + "match_any": "Corresponder a QUALQUER condição", + "conditions": "Condições", + "add_condition": "Adicionar condição", + "actions": "Ações", + "add_action": "Adicionar ação", + "stop_processing": "Parar o processamento das regras seguintes", + "include_spam": "Mover também as mensagens marcadas como spam", + "attachment_type_placeholder": "ex.: pdf, doc, jpg", + "value_placeholder_multi": "Valor (vários separados por vírgulas)", + "condition_fields": { + "attachment": "Anexo", + "from": "De", + "to": "Para", + "cc": "Cc", + "subject": "Assunto", + "header": "Cabeçalho personalizado", + "size": "Tamanho", + "body": "Corpo", + "all": "Todas as mensagens" + }, + "comparators": { + "has_any": "está presente", + "has_type": "do tipo", + "contains": "contém", + "not_contains": "não contém", + "is": "é exatamente", + "not_is": "não é", + "starts_with": "começa com", + "ends_with": "termina com", + "matches": "corresponde ao padrão", + "greater_than": "é maior que", + "less_than": "é menor que", + "address_is": "é o endereço", + "domain_is": "tem o domínio" + }, + "action_types": { + "move": "Mover para pasta", + "copy": "Copiar para pasta", + "forward": "Encaminhar para", + "mark_read": "Marcar como lido", + "star": "Destacar mensagem", + "add_label": "Adicionar etiqueta", + "discard": "Descartar (excluir silenciosamente)", + "reject": "Rejeitar com mensagem", + "keep": "Manter na caixa de entrada", + "stop": "Parar processamento" + }, + "move_to_folder": "Selecionar pasta", + "copy_to_folder": "Selecionar pasta", + "forward_to": "Encaminhar para endereço de e-mail", + "forward_placeholder": "email@exemplo.com", + "forward_keep_copy": "Manter uma cópia", + "forward_limit": "Limite de reencaminhamentos por mensagem neste servidor: {count}. Os restantes são ignorados.", + "forward_unavailable": "Este servidor não permite reencaminhar. As ações de reencaminhamento nas regras de filtro não têm efeito.", + "reject_message": "Mensagem de rejeição", + "reject_placeholder": "Seu e-mail foi rejeitado", + "label_name": "Nome da etiqueta", + "label_placeholder": "Selecionar etiqueta", + "header_name": "Nome do cabeçalho", + "header_placeholder": "ex. X-Mailing-List", + "size_bytes": "Tamanho em bytes", + "size_placeholder": "ex. 1000000", + "system_managed": "Regra gerenciada pelo sistema", + "opaque_warning": "Este script foi editado fora do construtor visual. Apenas a edição Sieve bruta está disponível.", + "open_sieve_editor": "Abrir editor Sieve", + "fetch_error": "Falha ao carregar filtros", + "expanded_view": "Vista expandida", + "expanded_view_description": "Mostrar regras de filtro com blocos detalhados de condições e ações", + "if": "Se", + "then": "Então", + "match_all_conditions": "todas correspondem", + "match_any_condition": "uma corresponde", + "and": "e", + "or": "ou", + "cancel": "Cancelar", + "confirm_delete": "Eliminar", + "rule_list": "Regras de filtragem", + "drag_to_reorder": "Arrastar para reordenar", + "match_type": "Tipo de correspondência", + "reset_to_visual": "Redefinir para o construtor visual", + "reset_warning": "Isto irá descartar o script atual e começar do zero.", + "confirm_reset": "Redefinir", + "validation_empty_name": "O nome da regra é obrigatório", + "validation_empty_conditions": "É necessária pelo menos uma condição com um valor", + "validation_empty_actions": "É necessária pelo menos uma ação", + "templates_section": "Começar com modelo", + "template_newsletters": "Mover newsletters para pasta", + "template_receipts": "Arquivar recibos automaticamente", + "template_important": "Marcar e-mails importantes", + "template_notifications": "Filtrar notificações", + "sieve_editor": { + "title": "Editor de script Sieve", + "warning": "Editar o script Sieve pode interromper a edição visual de regras. As alterações feitas aqui substituem o construtor visual.", + "script_content": "Script Sieve", + "valid": "O script é válido", + "invalid": "O script contém erros", + "save_warning": "Guardar irá substituir todas as regras visuais. Esta ação não pode ser desfeita. Clique em Guardar novamente para confirmar.", + "validating": "A validar...", + "validate": "Validar", + "cancel": "Cancelar", + "save": "Guardar", + "confirm_save": "Confirmar guardar", + "validation_failed": "Falha no pedido de validação" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# condição} other {# condições}}", + "actions_count": "{count, plural, one {# ação} other {# ações}}" + }, + "origin_external": "Externo", + "managed_by_tooltip": "Gerenciado por {source}. Edite nesse aplicativo ou use o editor Sieve bruto.", + "quick_rule_names": { + "move_sender": "Mover de {sender} para {folder}", + "move_domain": "Mover de @{domain} para {folder}", + "move_list": "Mover a lista {list} para {folder}", + "mark_read": "Marcar como lidas as de {sender}", + "tag": "Etiquetar como {tag} as de {sender}", + "block": "Bloquear {sender}", + "custom": "Mensagens de {sender}", + "custom_plain": "Nova regra", + "senders": "{count, plural, one {# remetente} other {# remetentes}}" + }, + "suggestions": "Sugestões", + "suggest_subject": "Assunto “{subject}”", + "suggest_to": "Para {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Aplicar também às mensagens que já estão nesta pasta", + "apply_existing_unsupported": "Só regras que verificam o remetente, os destinatários, o assunto ou os cabeçalhos, e que movem, copiam, marcam como lida, marcam com estrela ou etiquetam, podem ser aplicadas a mensagens existentes.", + "period_title": "Período", + "period_start": "Data de início", + "period_end": "Data de fim", + "validation_period_order": "A data de fim deve ser posterior à data de início", + "period_toggle": "Ativa apenas durante um período", + "period_hint": "Aplica-se às mensagens que chegam durante este período. Deixe um campo vazio para não limitar esse extremo.", + "validation_period_empty": "Indique uma data de início ou de fim para o período", + "validation_period_invalid": "Indique uma data válida com hora para o período ou deixe o campo vazio", + "apply_existing_period": "Uma regra com período só atua sobre as mensagens que chegam dentro dele, por isso não pode ser aplicada às mensagens existentes.", + "period_status_scheduled": "agendada", + "period_status_active": "ativa", + "period_status_expired": "expirada" + }, + "templates": { + "title": "Modelos de e-mail", + "description": "Crie modelos de e-mail reutilizáveis com variáveis de marcador", + "add": "Novo modelo", + "edit": "Editar modelo", + "name": "Nome do modelo", + "name_placeholder": "ex.: E-mail de acompanhamento", + "category": "Categoria", + "category_placeholder": "ex.: Trabalho, Pessoal", + "subject": "Assunto", + "subject_placeholder": "Linha de assunto do e-mail", + "body": "Corpo", + "body_placeholder": "Conteúdo do e-mail...", + "recipients_placeholder": "email@example.com", + "identity": "Enviar como", + "default_identity": "Identidade padrão", + "favorite": "Favorito", + "cancel": "Cancelar", + "create": "Criar", + "update": "Atualizar", + "confirm_delete": "Excluir", + "no_templates": "Sem modelos", + "manage": "Gerenciar modelos", + "count": "{count, plural, one {# modelo} other {# modelos}}", + "export_import": "Exportar e importar", + "export_import_description": "Faça backup dos seus modelos ou transfira-os para outro dispositivo", + "export": "Exportar", + "import": "Importar", + "validation": { + "empty": "O nome do modelo é obrigatório", + "too_long": "O nome do modelo não pode exceder 200 caracteres" + } + }, + "files": { + "display": { + "title": "Exibição", + "description": "Configure como arquivos e pastas são exibidos" + }, + "default_view": { + "label": "Visualização padrão", + "description": "Escolha entre layout em grade e lista", + "list": "Lista", + "grid": "Grade" + }, + "default_sort": { + "label": "Ordenação padrão", + "description": "Escolha a ordenação padrão para os arquivos", + "name": "Nome", + "size": "Tamanho", + "modified": "Modificado" + }, + "sort_direction": { + "label": "Direção da ordenação", + "description": "Escolha ordem crescente ou decrescente", + "ascending": "Crescente", + "descending": "Decrescente" + }, + "icons": { + "title": "Ícones", + "description": "Configure a aparência dos ícones de arquivos" + }, + "show_icons": { + "label": "Mostrar ícones de arquivos", + "description": "Exibir ícones ao lado de arquivos e pastas" + }, + "colored_icons": { + "label": "Ícones coloridos", + "description": "Usar ícones coloridos em vez de monocromáticos" + }, + "show_thumbnails": { + "label": "Mostrar miniaturas", + "description": "Exibir pré-visualizações de imagens em vez de ícones" + }, + "behavior": { + "title": "Comportamento", + "description": "Configure o comportamento do gerenciador de arquivos" + }, + "show_hidden": { + "label": "Mostrar arquivos ocultos", + "description": "Exibir arquivos e pastas que começam com um ponto" + }, + "preview": { + "label": "Visualização" + } + }, + "search_placeholder": "Pesquisar configurações", + "search_clear": "Limpar pesquisa", + "search_no_results": "Nenhuma configuração correspondente", + "scoped": { + "back": "Voltar para minha conta", + "managing": "Gerenciando: {name}" + } + }, + "errors": { + "page_error_title": "Algo deu errado", + "page_error_description": "Encontramos um erro inesperado. Por favor, tente novamente ou volte para a página inicial.", + "sidebar_error": "Não foi possível carregar as caixas de entrada", + "email_list_error": "Não foi possível carregar os e-mails", + "viewer_error_title": "Não foi possível exibir o e-mail", + "viewer_error_description": "Houve um problema ao renderizar este e-mail. Ele pode conter conteúdo não suportado.", + "composer_error": "Não foi possível carregar o editor", + "settings_error_title": "Configurações indisponíveis", + "settings_error_description": "Não foi possível carregar as configurações. Suas preferências podem não ser salvas.", + "try_again": "Tentar novamente", + "reload": "Recarregar", + "reload_emails": "Recarregar e-mails", + "reload_settings": "Recarregar configurações", + "retry": "Tentar novamente", + "go_home": "Ir para caixa de entrada" + }, + "context_menu": { + "reply": "Responder", + "reply_all": "Responder a Todos", + "forward": "Encaminhar", + "mark_read": "Marcar como Lido", + "mark_unread": "Marcar como Não Lido", + "star": "Adicionar Estrela", + "unstar": "Remover Estrela", + "pin": "Fixar", + "unpin": "Desafixar", + "move_to": "Mover para...", + "copy_to_account": "Copiar para conta...", + "remove_from_folder": "Remover desta pasta", + "archive": "Arquivar", + "delete": "Excluir", + "mark_as_spam": "Reportar spam", + "not_spam": "Não é spam", + "tag": "Etiqueta", + "items_selected": "{count} e-mails selecionados", + "forward_as_attachments": "Encaminhar como anexos", + "edit_draft": "Editar rascunho", + "cancel_scheduled_send": "Cancelar envio", + "reschedule_send": "Reagendar", + "cancel_and_edit": "Cancelar e editar", + "cancel_and_compose_again": "Cancelar e escrever novamente", + "rules": { + "title": "Regras", + "move_from": "Sempre mover mensagens de {sender}", + "move_from_domain": "Sempre mover mensagens de @{domain}", + "move_from_list": "Sempre mover mensagens desta lista", + "mark_read_from": "Sempre marcar como lidas as mensagens de {sender}", + "tag_with": "Sempre etiquetar com", + "block": "{count, plural, one {Bloquear remetente} other {Bloquear # remetentes}}", + "no_junk": "Esta conta não tem pasta de spam", + "create": "Criar regra…", + "manage": "Gerenciar regras", + "senders_count": "{count, plural, one {# remetente} other {# remetentes}}", + "cross_account": "Selecione mensagens de uma única conta", + "opaque_hint": "Seus filtros foram editados manualmente. Abra as configurações de filtros" , + "loading": "A carregar regras…", + "load_failed": "Não foi possível carregar as regras", + "none": "Ainda não há regras", + "new_mail_only": "Apenas para mensagens novas", + "running": "A executar \"{name}\" em {folder}…" + }, + "find_emails_from": "Encontrar e-mails de {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Marcar pasta como lida", + "mark_folder_tree_read": "Marcar pasta e subpastas como lidas", + "mark_all_folders_read": "Marcar todas as pastas como lidas", + "new_subfolder": "Nova subpasta...", + "new_folder": "Nova pasta...", + "rename": "Renomear...", + "share": "Partilhar...", + "import_email": "Importar .eml ou .zip...", + "empty_folder": "Esvaziar pasta", + "empty_folder_generic": "Esvaziar pasta", + "delete_folder": "Excluir pasta", + "refresh": "Atualizar", + "hide_shared_account": "Ocultar da barra lateral", + "mute_notifications": "Silenciar notificações", + "unmute_notifications": "Reativar notificações", + "mute_account_notifications": "Silenciar notificações desta conta", + "unmute_account_notifications": "Reativar notificações desta conta", + "reset_folder_notifications": "Redefinir notificações das pastas", + "mark_all_confirm_title": "Marcar todas as pastas como lidas", + "mark_all_confirm_message": "Marcar todas as mensagens não lidas da sua conta pessoal como lidas?", + "delete_confirm_title": "Excluir pasta", + "delete_confirm_message": "Excluir permanentemente a pasta \"{name}\"? Esta ação não pode ser desfeita.", + "prompt_new_subfolder": "Digite um nome para a nova subpasta.", + "prompt_new_folder": "Digite um nome para a nova pasta.", + "prompt_rename": "Digite um novo nome para esta pasta.", + "toast_marked_read": "Pasta marcada como lida", + "toast_marked_read_count": "{count, plural, one {1 mensagem marcada como lida} other {# mensagens marcadas como lidas}}", + "toast_already_read": "Nenhuma mensagem não lida", + "toast_marked_all_read": "Todas as pastas marcadas como lidas", + "toast_emptied": "Pasta esvaziada", + "toast_folder_created": "Pasta criada", + "toast_folder_renamed": "Pasta renomeada", + "toast_folder_deleted": "Pasta excluída", + "toast_error_mark_read": "Falha ao marcar como lida", + "toast_error_empty": "Falha ao esvaziar a pasta", + "toast_error_create": "Falha ao criar a pasta", + "toast_error_rename": "Falha ao renomear a pasta", + "toast_error_delete": "Falha ao excluir a pasta", + "toast_error_delete_has_children": "A pasta contém subpastas. Remova-as primeiro.", + "toast_error_delete_has_email": "A pasta não está vazia. Esvazie-a primeiro.", + "delete_with_contents_title": "A pasta não está vazia", + "delete_with_contents_message": "A pasta \"{name}\" ainda contém {count, plural, one {1 mensagem} other {# mensagens}}. Eliminar a pasta e todas as suas mensagens? Esta ação não pode ser anulada.", + "delete_with_contents_confirm": "Eliminar pasta e mensagens", + "placeholder_folder_name": "Nome da pasta", + "create": "Criar", + "rename_confirm": "Renomear" + }, + "shortcuts": { + "title": "Atalhos de Teclado", + "tip": "Pressione ? a qualquer momento para mostrar esta ajuda", + "sections": { + "navigation": "Navegação", + "actions": "Ações de E-mail", + "global": "Global", + "threads": "Conversas", + "composer": "Redação" + }, + "navigation": { + "next_email": "Próximo e-mail", + "previous_email": "E-mail anterior", + "open_email": "Abrir e-mail", + "close_email": "Fechar / Desselecionar", + "go_to_folder": "Ir para a pasta", + "next_unread": "Próximo e-mail não lido", + "no_more_unread": "Não há mais e-mails não lidos mais abaixo na lista" + }, + "actions": { + "reply": "Responder", + "reply_all": "Responder a todos", + "forward": "Encaminhar", + "star": "Alternar estrela", + "archive": "Arquivar", + "delete": "Excluir", + "mark_unread": "Marcar como não lido", + "mark_read": "Marcar como lido", + "toggle_spam": "Reportar spam / Não é spam" + }, + "global": { + "compose": "Escrever novo e-mail", + "search": "Focar busca", + "help": "Mostrar atalhos", + "refresh": "Atualizar e-mails", + "select_all": "Selecionar todos", + "global_search": "Pesquisar em tudo", + "extend_selection": "Expandir a seleção para baixo / cima" + }, + "threads": { + "expand_collapse": "Expandir/recolher conversa", + "select_mountain_view": "Selecionar a conversa" + }, + "composer": { + "send": "Enviar e-mail", + "schedule_send": "Agendar envio", + "template_picker": "Abrir seletor de modelos" + } + }, + "threads": { + "messages_one": "{count} mensagem", + "messages_other": "{count} mensagens", + "messages_tooltip": "{count, plural, one {# mensagem nesta conversa} other {# mensagens nesta conversa}}", + "expand": "Expandir conversa", + "collapse": "Recolher conversa", + "loading": "Carregando conversa...", + "mark_read": "Marcar conversa como lida", + "mark_unread": "Marcar conversa como não lida", + "archive": "Arquivar conversa", + "delete": "Excluir conversa", + "star": "Adicionar estrela à conversa", + "unstar": "Remover estrela da conversa", + "toggle_thread": "Expandir/recolher conversa" + }, + "identities": { + "modal_title": "Gerenciar Identidades de Envio", + "create_new": "Criar Nova Identidade", + "edit_identity": "Editar Identidade", + "delete_confirm": "Excluir esta identidade? Isso não pode ser desfeito.", + "cannot_delete": "Esta identidade não pode ser excluída", + "primary_identity": "Principal", + "set_as_primary": "Definir como principal", + "no_identities": "Nenhuma identidade encontrada", + "display": { + "reply_to": "Responder para:", + "bcc": "CCO:", + "signature": "Assinatura:", + "preview": "Visualização:" + }, + "validation_errors": { + "invalid_emails": "E-mails inválidos: {emails}", + "unknown_error": "Erro desconhecido" + }, + "form": { + "name_label": "Nome de Exibição", + "name_placeholder": "ex: E-mail do Trabalho, Pessoal", + "name_required": "Nome é obrigatório", + "email_label": "Endereço de E-mail", + "email_placeholder": "seu.email@exemplo.com", + "email_required": "E-mail é obrigatório", + "email_invalid": "Por favor, digite um endereço de e-mail válido", + "email_immutable": "O endereço de e-mail não pode ser alterado após a criação", + "reply_to_label": "Responder para (opcional)", + "reply_to_placeholder": "diferente@example.com", + "bcc_label": "CCO Automático (opcional)", + "bcc_placeholder": "arquivo@example.com", + "text_signature_label": "Assinatura de Texto", + "html_signature_label": "Assinatura HTML", + "signature_byte_counter": "{bytes} / {max} bytes", + "signature_byte_limit_reached": "Limite do servidor atingido", + "save": "Salvar Identidade", + "cancel": "Cancelar", + "creating": "Criando...", + "updating": "Atualizando..." + }, + "sub_address": { + "button_tooltip": "Usar sub-endereço", + "popover_title": "Adicionar Tag de Sub-Endereço", + "tag_input_placeholder": "Digite a tag (ex: compras)", + "preview_label": "Visualização:", + "recent_tags": "Tags Recentes", + "suggested_tags": "Sugeridas", + "use_address": "Usar Este Endereço", + "invalid_tag": "A tag deve conter apenas caracteres alfanuméricos e hífens", + "tag_too_long": "A tag deve ter no máximo 30 caracteres", + "help_text": "E-mails enviados para usuario{delimiter}tag@dominio.com chegarão na sua caixa de entrada", + "validation": { + "empty": "A tag não pode estar vazia", + "too_long": "A tag deve ter no máximo {max} caracteres", + "invalid_chars": "A tag deve conter apenas letras, números e hífens" + } + }, + "badge": { + "sent_via": "via", + "sub_address_tag": "Enviado usando sub-endereço: {tag}", + "identity_name": "Enviado usando identidade: {name}", + "identity_short": "via {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Excluir", + "delete_confirm_title": "Excluir identidade" + }, + "templates": { + "picker_title": "Escolher um modelo", + "search_placeholder": "Pesquisar modelos...", + "section_favorites": "Favoritos", + "section_recent": "Recentes", + "section_uncategorized": "Outros", + "no_templates": "Sem modelos", + "no_results": "Nenhum modelo encontrado", + "fill_placeholders": "Preencher valores dos marcadores", + "enter_value": "Insira um valor...", + "preview": "Pré-visualização", + "insert_with_values": "Inserir com valores", + "insert_raw": "Inserir bruto", + "copy_suffix": "(cópia)", + "placeholder": "Variável", + "placeholders": { + "recipient_name": "Nome do destinatário", + "company": "Nome da empresa", + "date": "Data atual", + "day_of_week": "Dia da semana", + "sender_name": "Seu nome" + } + }, + "contacts": { + "directory": { + "title": "Diretório", + "search_placeholder": "Pesquisar nome ou endereço de e-mail", + "refresh": "Atualizar diretório", + "retry": "Tentar novamente", + "loading": "Carregando diretório…", + "unavailable_title": "Sem diretório", + "unavailable_hint": "Seu servidor de e-mail não oferece um diretório de contas.", + "forbidden_title": "Diretório indisponível", + "forbidden_hint": "Seu servidor de e-mail não permite que esta conta consulte o diretório. Fale com o administrador.", + "error_title": "Não foi possível carregar o diretório", + "error_hint": "Verifique sua conexão e tente novamente.", + "empty_title": "O diretório está vazio", + "empty_hint": "Seu servidor de e-mail não lista nenhuma conta com endereço de e-mail.", + "no_match": "Ninguém no diretório corresponde à sua pesquisa", + "refresh_failed": "Não foi possível atualizar o diretório. Exibindo a última lista carregada.", + "select_page": "Selecionar todos nesta página", + "deselect_page": "Desmarcar todos nesta página", + "select_entry": "Selecionar {name}", + "range": "{from}–{to} de {total}", + "page": "Página {page} de {count}", + "previous_page": "Página anterior", + "next_page": "Próxima página", + "picker_title": "Selecionar do diretório", + "cancel": "Cancelar", + "selected_count": "{count} selecionados", + "add_to": "Adicionar em Para", + "add_cc": "Adicionar em Cc", + "add_bcc": "Adicionar em Cco", + "select_hint": "Selecione alguém para ver os detalhes", + "write_email": "Escrever e-mail", + "add_to_contacts": "Adicionar aos contatos", + "email": "E-mail", + "read_only_note": "As entradas do diretório vêm do seu servidor de e-mail e não podem ser editadas aqui.", + "type": { + "individual": "Pessoa", + "group": "Grupo", + "resource": "Recurso", + "location": "Local", + "other": "Outro" + } + }, + "title": "Contatos", + "search_placeholder": "Pesquisar contatos...", + "create_new": "Novo contato", + "no_category": "Sem categoria", + "rename_category": "Renomear categoria", + "category_name_label": "Nome da categoria", + "category_renamed": "Categoria renomeada", + "category_rename_failed": "Falha ao renomear a categoria", + "category_added": "Contato adicionado a {name}", + "category_added_plural": "{count} contatos adicionados a {name}", + "empty_state": "Nenhum contato", + "empty_state_title": "Sem contatos", + "empty_state_subtitle": "Crie seu primeiro contato ou importe de um arquivo vCard", + "empty_search": "Nenhum contato encontrado", + "empty_search_hint": "Tente outro termo de pesquisa", + "empty_filtered": "Nenhum contato corresponde aos seus filtros", + "empty_filtered_hint": "Ajuste ou limpe os filtros", + "clear_search": "Limpar pesquisa", + "import_vcard": "Importar vCard", + "delete_confirm_title": "Excluir contato", + "delete_confirm": "Tem certeza de que deseja excluir este contato?", + "local_mode": "Os contatos são armazenados localmente (o servidor não suporta JMAP Contacts)", + "back_to_contacts": "Voltar aos contatos", + "back_to_email": "Voltar ao e-mail", + "tabs": { + "all": "Todos", + "groups": "Grupos" + }, + "shared": { + "title": "Compartilhados" + }, + "address_books": { + "create": "Novo livro de endereços", + "created": "Livro de endereços criado", + "create_failed": "Falha ao criar o livro de endereços", + "title": "Meus Catálogos de Endereços", + "shared_prefix": "Compartilhado: {name}", + "moved": "Contato movido para {name}", + "moved_plural": "{count} contatos movidos para {name}", + "move_failed": "Falha ao mover o contato", + "address_book": "Catálogo de endereços", + "rename": "Renomear catálogo de endereços", + "name_label": "Nome do catálogo de endereços", + "renamed": "Catálogo de endereços renomeado", + "rename_failed": "Falha ao renomear o catálogo de endereços", + "default": "Padrão", + "set_default": "Definir como padrão", + "default_updated": "Catálogo de endereços padrão atualizado", + "set_default_failed": "Falha ao definir o catálogo de endereços padrão", + "manage": "Gerenciar catálogos de endereços", + "share": "Compartilhar lista de contatos", + "new_contact_in_book": "Novo contato nesta lista", + "delete": "Excluir lista de contatos", + "confirm_delete": "Excluir \"{name}\"? Todos os contatos desta lista serão removidos.", + "deleted": "Lista de contatos excluída", + "delete_failed": "Falha ao excluir a lista de contatos" + }, + "detail": { + "emails": "Endereços de e-mail", + "phones": "Números de telefone", + "organizations": "Organizações", + "addresses": "Endereços", + "notes": "Notas", + "titles": "Títulos e funções", + "online_services": "Serviços online", + "anniversaries": "Aniversários", + "personal_info": "Informações pessoais", + "languages": "Idiomas", + "categories": "Categorias", + "related_contacts": "Contatos relacionados", + "crypto_keys": "Chaves criptográficas", + "no_contact_selected": "Selecione um contato para ver os detalhes", + "compose_email": "Escrever e-mail", + "copy_email": "Copiar e-mail", + "copy_phone": "Copiar telefone", + "copy_url": "Copiar URL", + "copied": "Copiado para a área de transferência", + "copy_failed": "Falha ao copiar para a área de transferência", + "created": "Criado", + "updated": "Última atualização", + "timezone": "Fuso horário", + "anniversary_birth": "Aniversário", + "anniversary_death": "Falecimento", + "anniversary_wedding": "Aniversário de casamento", + "anniversary_other": "Outro", + "personal_expertise": "Especialidade", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Outro", + "gender": "Gênero", + "gender_masculine": "Masculino", + "gender_feminine": "Feminino", + "gender_other": "Outro", + "gender_none": "Não aplicável", + "gender_unknown": "Desconhecido", + "calendar": "Calendário", + "calendar_uri": "URL do calendário", + "scheduling_uri": "URL de agendamento", + "freebusy_uri": "URL de disponibilidade", + "cert_issuer": "Emissor", + "cert_expires": "Expira em", + "cert_expired": "Expirado", + "cert_fingerprint": "Impressão digital", + "cert_algorithm": "Algoritmo", + "import_to_smime": "Importar para S/MIME", + "cert_already_imported": "Certificado já importado", + "cert_imported": "Certificado importado", + "cert_import_failed": "Falha ao importar o certificado", + "section_contact": "Detalhes do contato", + "section_work": "Trabalho", + "section_personal": "Pessoal", + "email_default_label": "E-mail", + "phone_default_label": "Telefone", + "address_default_label": "Endereço", + "online_service_default_label": "Online", + "organization_label": "Organização", + "title_label": "Título", + "role_label": "Função", + "language_label": "Idioma", + "related_default_label": "Relacionado", + "more_actions": "Mais ações", + "age_years": "{count, plural, one {1 ano} other {# anos}}", + "years_since": "{count, plural, one {há 1 ano} other {há # anos}}" + }, + "activity": { + "recent_emails": "E-mails recentes", + "upcoming_events": "Eventos futuros", + "no_emails": "Nenhum e-mail recente", + "no_events": "Nenhum evento futuro", + "no_subject": "(Sem assunto)", + "no_title": "(Sem título)", + "load_failed": "Falha ao carregar", + "unknown_sender": "Remetente desconhecido", + "all_day": "Dia todo" + }, + "form": { + "create_title": "Novo contato", + "edit_title": "Editar contato", + "section_address_book": "Diretório", + "select_address_book": "Selecionar um diretório...", + "section_identity": "Nome e identidade", + "contact_type": "Tipo de contacto", + "type_person": "Pessoa", + "type_organization": "Organização", + "section_work": "Trabalho e organização", + "prefix": "Prefixo", + "prefix_placeholder": "Dr., Sr., Sra.", + "given_name": "Nome", + "middle_name": "Nome do meio", + "surname": "Sobrenome", + "suffix": "Sufixo", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Apelido", + "nickname_placeholder": "Apelido", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Telefone", + "phone_placeholder": "+55 11 1234 5678", + "phone_type": "Tipo", + "phone_voice": "Voz", + "phone_cell": "Celular", + "phone_fax": "Fax", + "phone_pager": "Pager", + "phone_video": "Vídeo", + "phone_text": "Texto", + "organization": "Organização", + "organization_placeholder": "Nome da empresa", + "department": "Departamento", + "department_placeholder": "Departamento", + "job_title": "Cargo", + "job_title_placeholder": "ex., Engenheiro de software", + "role": "Função", + "role_placeholder": "ex., Líder de equipe", + "addresses": "Endereços", + "add_address": "Adicionar endereço", + "street": "Rua", + "city": "Cidade", + "region": "Estado / Região", + "postcode": "CEP", + "country": "País", + "online_services": "Serviços online", + "add_online_service": "Adicionar serviço online", + "url_placeholder": "https://...", + "service_placeholder": "Serviço", + "anniversaries": "Aniversários", + "add_anniversary": "Adicionar data", + "anniversary_birth": "Aniversário", + "anniversary_wedding": "Aniversário de casamento", + "anniversary_death": "Falecimento", + "anniversary_other": "Outro", + "personal_info": "Informações pessoais", + "add_personal_info": "Adicionar entrada", + "personal_info_placeholder": "ex., Fotografia", + "personal_expertise": "Especialidade", + "personal_hobby": "Hobby", + "personal_interest": "Interesse", + "personal_other": "Outro", + "level": "Nível", + "level_high": "Alto", + "level_medium": "Médio", + "level_low": "Baixo", + "categories": "Categorias", + "categories_placeholder": "ex., Família, Amigos, Colegas", + "categories_hint": "Digite para pesquisar ou adicionar", + "category_add": "Adicionar", + "note": "Notas", + "note_placeholder": "Adicionar uma nota...", + "gender": "Gênero", + "gender_sex": "Sexo", + "gender_male": "Masculino", + "gender_female": "Feminino", + "gender_other": "Outro", + "gender_none": "Não aplicável", + "gender_unknown": "Desconhecido", + "gender_identity": "Identidade de gênero", + "gender_identity_placeholder": "Identidade de gênero...", + "calendar": "Calendário", + "calendar_uri": "URL do calendário", + "scheduling_uri": "URL de agendamento", + "freebusy_uri": "URL de disponibilidade", + "context_work": "Trabalho", + "context_private": "Pessoal", + "add_email": "Adicionar e-mail", + "add_phone": "Adicionar telefone", + "save": "Salvar", + "cancel": "Cancelar", + "creating": "Criando...", + "updating": "Atualizando...", + "name_required": "Introduza um nome próprio, apelido ou organização", + "email_invalid": "Por favor, insira um endereço de e-mail válido", + "email_error_inline": "Formato de e-mail inválido", + "save_failed": "Falha ao salvar contato", + "delete": "Excluir", + "upload_photo": "Carregar foto", + "remove_photo": "Remover foto", + "photo_hint": "JPG ou PNG, até 10 MB. Será redimensionada.", + "photo_too_large": "A imagem é muito grande (máx. 10 MB)", + "photo_invalid": "Arquivo de imagem inválido", + "change_photo": "Alterar" + }, + "groups": { + "create": "Novo grupo", + "edit": "Editar grupo", + "empty": "Nenhum grupo", + "delete_confirm_title": "Excluir grupo", + "delete_confirm": "Tem certeza de que deseja excluir este grupo?", + "name_label": "Nome do grupo", + "name_placeholder": "ex. Equipe, Família", + "name_required": "O nome do grupo é obrigatório", + "save_failed": "Falha ao salvar grupo", + "members_label": "Membros", + "search_members": "Pesquisar contatos para adicionar...", + "no_members": "Nenhum membro neste grupo", + "member_count": "{count, plural, =0 {Nenhum membro} one {1 membro} other {# membros}}", + "send_email": "Enviar e-mail para o grupo", + "send_email_to": "Para", + "send_email_cc": "Cc", + "send_email_bcc": "Cco", + "no_member_emails": "Este grupo não tem membros com endereço de e-mail." + }, + "import": { + "title": "Importar contatos", + "drop_hint": "Clique para selecionar um arquivo vCard", + "file_types": "Arquivos .vcf ou .vcard", + "no_contacts": "Nenhum contato encontrado no arquivo", + "parse_error": "Falha ao analisar o arquivo vCard", + "found": "{count, plural, one {1 contato encontrado} other {# contatos encontrados}}", + "duplicate": "Duplicado", + "select_all": "Selecionar tudo", + "deselect_all": "Desmarcar tudo", + "selected": "{count, plural, one {1 selecionado} other {# selecionados}}", + "import_button": "Importar", + "importing": "Importando...", + "success": "{count, plural, one {1 contato importado} other {# contatos importados}}", + "failed": "Falha na importação", + "close": "Fechar", + "file_too_large": "Arquivo muito grande (máx. 5 MB)" + }, + "export": { + "title": "Exportar contatos", + "success": "{count, plural, one {1 contato exportado} other {# contatos exportados}}" + }, + "bulk": { + "selected": "{count, plural, one {1 selecionado} other {# selecionados}}", + "select_all": "Selecionar tudo", + "delete": "Excluir", + "delete_confirm_title": "Excluir contatos", + "delete_confirm": "Excluir {count, plural, one {1 contato} other {# contatos}}?", + "deleted": "{count, plural, one {1 contato excluído} other {# contatos excluídos}}", + "add_to_group": "Adicionar ao grupo", + "choose_group": "Escolher um grupo", + "adding_contacts": "Adicionando {count, plural, one {1 contato} other {# contatos}}", + "added_to_group": "Contatos adicionados ao grupo", + "export": "Exportar", + "clear": "Limpar seleção" + }, + "toast": { + "created": "Contato criado", + "updated": "Contato atualizado", + "deleted": "Contato excluído", + "error_create": "Falha ao criar contato", + "error_update": "Falha ao atualizar contato", + "error_delete": "Falha ao excluir contato" + }, + "context_menu": { + "open": "Abrir", + "edit": "Editar", + "send_email": "Enviar e-mail", + "add_to_group": "Adicionar ao grupo", + "export_vcard": "Exportar como vCard", + "delete": "Excluir", + "call": "Ligar", + "duplicate": "Duplicar", + "print": "Imprimir" + }, + "filters": { + "toggle": "Filtros", + "select": "Selecionar", + "clear": "Limpar", + "close": "Fechar", + "title": "Filtros avançados", + "organization": "Empresa", + "organization_placeholder": "ex. Acme", + "job_title": "Cargo", + "job_title_placeholder": "ex. Designer", + "location": "Localização", + "location_placeholder": "Cidade ou país", + "email_domain": "Domínio de e-mail", + "email_domain_placeholder": "exemplo.com", + "birthday_month": "Aniversário em", + "any_month": "Qualquer mês", + "has_email": "Com e-mail", + "has_phone": "Com telefone", + "has_photo": "Com foto", + "sort_by": "Ordenar por", + "sort_first_name": "Nome", + "sort_last_name": "Sobrenome" + }, + "open_categories": "Abrir categorias" + }, + "calendar": { + "title": "Calendário", + "back_to_email": "Voltar ao e-mail", + "back_to_month": "Voltar ao mês", + "my_calendars": "Meus calendários", + "birthday_calendar": "Aniversários", + "mini_calendar_change": "Clique para mudar o mês", + "views": { + "month": "Mês", + "week": "Semana", + "day": "Dia", + "agenda": "Agenda", + "today": "Hoje", + "month_hint": "Mês (m)", + "week_hint": "Semana (w)", + "day_hint": "Dia (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Tarefas", + "tasks_hint": "Tarefas (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# evento anterior} other {# eventos anteriores}}", + "hidden_after": "{count, plural, one {# evento posterior} other {# eventos posteriores}}", + "show_all_hours": "Mostrar todas as horas", + "show_display_hours": "Mostrar apenas {start} – {end}", + "create": "Criar evento", + "edit": "Editar evento", + "delete": "Excluir evento", + "details": "Detalhes do evento", + "no_events": "Nenhum evento", + "all_day": "Dia inteiro", + "more": "+{count} mais", + "show_more": "Mostrar mais", + "show_less": "Mostrar menos", + "no_title": "(Sem título)", + "resize": "Redimensionar evento", + "duplicate": "Duplicar", + "today_header": "Hoje", + "tomorrow_header": "Amanhã", + "agenda_show_earlier": "Mostrar eventos anteriores", + "agenda_loading": "A carregar…", + "agenda_range_start": "Eventos a partir de {date}", + "agenda_range_end": "Eventos até {date}", + "export_ics": "Exportar como .ics", + "copy_title": "Copiar título", + "copy_link": "Copiar link da reunião", + "go_to_today": "Ir para hoje", + "new_all_day_event": "Novo evento de dia inteiro", + "new_event": "Novo evento", + "new_task": "Nova tarefa" + }, + "detail": { + "add_note": "Adicionar uma nota...", + "save_note": "Salvar", + "note_saved": "Nota adicionada", + "open_link": "Abrir link", + "copy_location": "Copiar local", + "meeting_link": "Link da reunião", + "tentative": "Provisório", + "cancelled": "Cancelado", + "delete_confirm": "Excluir este evento?" + }, + "form": { + "title": "Título", + "description": "Descrição", + "location": "Local", + "meeting_link": "Link da reunião", + "start_date": "Data de início", + "end_date": "Data de término", + "start_time": "Hora de início", + "end_time": "Hora de término", + "all_day_event": "Evento de dia inteiro", + "calendar_select": "Calendário", + "save": "Salvar", + "cancel": "Cancelar", + "delete_confirm": "Tem certeza de que deseja excluir este evento?", + "color": "Cor" + }, + "participants": { + "availability": { + "title": "Disponibilidade", + "free": "Disponível", + "busy": "Ocupado", + "tentative": "Provisório", + "unknown": "Não é um utilizador deste servidor", + "checking": "A verificar…" + }, + "title": "Participantes", + "add": "Adicionar participante", + "organizer": "Organizador", + "attendee": "Participante", + "accepted": "Aceito", + "declined": "Recusado", + "tentative": "Provisório", + "needs_action": "Aguardando resposta", + "remove": "Remover", + "edit": "Editar", + "email_placeholder": "Adicionar endereço de e-mail ou pesquisar contatos", + "send_invitations": "Enviar convites aos participantes", + "status_summary": "{accepted} aceito(s), {pending} pendente(s)", + "invited_by": "Convite de {name}", + "respond_below": "Responda com os botões abaixo", + "rsvp_label": "Sua resposta", + "cancel_notification": "Os participantes serão notificados do cancelamento", + "you_organizer": "Você é o organizador", + "you_attendee": "Você é um participante", + "no_participants": "Sem participantes", + "count": "{count, plural, one {# participante} other {# participantes}}" + }, + "recurrence": { + "title": "Recorrência", + "none": "Não se repete", + "daily": "Diário", + "weekly": "Semanal", + "monthly": "Mensal", + "yearly": "Anual", + "every_n_days": "A cada {count} dias", + "every_n_weeks": "A cada {count} semanas", + "every_n_months": "A cada {count} meses", + "until": "Até", + "occurrences": "{count} repetições", + "custom": "Personalizado…", + "edit_custom": "Editar recorrência personalizada", + "every_n_years": "A cada {count} anos", + "on_days": "em {days}", + "on_day_n": "no dia {day}", + "on_the_nth": "no {nth} {day}", + "in_month": "em {month}", + "nth_1": "1.º", + "nth_2": "2.º", + "nth_3": "3.º", + "nth_4": "4.º", + "nth_last": "último", + "editor_freq_day": "Dia", + "editor_freq_week": "Semana", + "editor_freq_month": "Mês", + "editor_freq_year": "Ano", + "editor_repeats_on": "Repete-se", + "editor_every": "A cada", + "editor_unit_days": "dia(s)", + "editor_unit_weeks": "semana(s)", + "editor_unit_months": "mês(es)", + "editor_unit_years": "ano(s)", + "editor_on_day": "no dia", + "editor_on_the": "no", + "editor_in": "em", + "editor_ends": "Termina", + "editor_never": "Nunca", + "editor_ends_on": "Em", + "editor_ends_after": "Após", + "editor_occurrences": "repetições" + }, + "recurrence_scope": { + "edit_title": "Editar evento recorrente", + "delete_title": "Excluir evento recorrente", + "description": "Este é um evento recorrente. Quais eventos você deseja modificar?", + "this_event": "Apenas este evento", + "this_and_future": "Este evento e os seguintes", + "all_events": "Todos os eventos", + "cancel": "Cancelar", + "save": "Salvar", + "delete": "Excluir", + "rsvp_title": "Responder a evento recorrente", + "rsvp_description": "Este é um evento recorrente. A quais eventos sua resposta se aplica?", + "respond": "Responder" + }, + "alerts": { + "title": "Lembrete", + "none": "Sem lembrete", + "at_time": "No momento do evento", + "minutes_before": "{count, plural, one {# minuto antes} other {# minutos antes}}", + "hours_before": "{count, plural, one {# hora antes} other {# horas antes}}", + "days_before": "{count, plural, one {# dia antes} other {# dias antes}}", + "weeks_before": "{count, plural, one {# semana antes} other {# semanas antes}}", + "unit_minutes_before": "minutos antes", + "unit_hours_before": "horas antes", + "unit_days_before": "dias antes", + "unit_weeks_before": "semanas antes", + "add": "Adicionar lembrete", + "remove": "Remover lembrete", + "amount": "Quantidade do lembrete", + "unit": "Unidade do lembrete", + "action": "Tipo de lembrete", + "action_display": "Notificação", + "action_email": "E-mail", + "by_email": "{label} por e-mail", + "kept": "{count, plural, one {# outro lembrete que não pode ser editado aqui é mantido} other {# outros lembretes que não podem ser editados aqui são mantidos}}", + "other": "{count, plural, one {# outro lembrete} other {# outros lembretes}}", + "calendar_defaults": "Lembretes padrão do calendário", + "customize": "Personalizar", + "use_defaults": "Usar os lembretes padrão do calendário" + }, + "settings": { + "limit_hours": "Limitar as horas visíveis", + "limit_hours_desc": "Mostrar apenas estas horas nas visualizações de dia e semana. Os eventos fora delas continuam acessíveis pelas setas no topo e na base de cada dia.", + "visible_hours": "Horas visíveis", + "visible_hours_start": "Início das horas visíveis", + "visible_hours_end": "Fim das horas visíveis", + "hide_non_working_days": "Ocultar dias não úteis", + "hide_non_working_days_desc": "Remover da visualização semanal os dias em que você não trabalha. Os eventos desses dias continuam aparecendo nas visualizações de mês, dia e agenda.", + "working_days": "Dias úteis", + "title": "Configurações do calendário", + "default_view": "Visualização padrão", + "week_starts_on": "Semana começa em", + "time_format": "Formato de hora", + "default_calendar": "Calendário padrão", + "default_reminder": "Lembrete padrão", + "time_format_12h": "12 horas", + "time_format_24h": "24 horas", + "notifications_enabled": "Notificações de eventos", + "notifications_enabled_desc": "Mostrar alertas para eventos próximos", + "notification_sound": "Som de notificação", + "notification_sound_desc": "Reproduzir um som para alertas do calendário", + "invitation_parsing": "Analisar convites por e-mail", + "invitation_parsing_desc": "Detectar convites de calendário em anexos de e-mail e mostrar ações do calendário", + "show_time_in_month_view": "Mostrar horário na visualização mensal", + "show_time_in_month_view_desc": "Exibir horários dos eventos na visualização mensal do calendário", + "show_week_numbers": "Mostrar números da semana", + "show_week_numbers_desc": "Exibir números da semana no minicalendário", + "calendar_free_scroll": "Deslocamento livre", + "calendar_free_scroll_desc": "Percorrer meses, semanas e dias de forma contínua em vez de um período de cada vez", + "enable_tasks": "Ativar tarefas", + "enable_tasks_desc": "Mostrar uma visualização de tarefas no calendário para gerenciar pendências", + "show_tasks_on_calendar": "Mostrar tarefas no calendário", + "show_tasks_on_calendar_desc": "Exibir tarefas nas visualizações mensal, semanal e diária do calendário", + "hover_preview": "Pré-visualização ao passar o rato", + "hover_preview_desc": "Mostrar um popover de detalhes ao passar o rato sobre eventos do calendário", + "hover_preview_instant": "Instantâneo", + "hover_preview_delay_500ms": "Atraso de 0,5 segundos", + "hover_preview_delay_1s": "Atraso de 1 segundo", + "hover_preview_delay_2s": "Atraso de 2 segundos", + "hover_preview_off": "Desativado", + "organizer_identity": "Organizar convites como", + "organizer_identity_desc": "O endereço de calendário usado como organizador quando convida pessoas para eventos.", + "organizer_identity_failed": "Não foi possível alterar o endereço do organizador", + "show_birthday_calendar": "Calendário de aniversários", + "show_birthday_calendar_desc": "Mostrar um calendário virtual com os aniversários dos seus contatos" + }, + "days": { + "monday": "Segunda-feira", + "tuesday": "Terça-feira", + "wednesday": "Quarta-feira", + "thursday": "Quinta-feira", + "friday": "Sexta-feira", + "saturday": "Sábado", + "sunday": "Domingo", + "mon": "Seg", + "tue": "Ter", + "wed": "Qua", + "thu": "Qui", + "fri": "Sex", + "sat": "Sáb", + "sun": "Dom" + }, + "months": { + "jan": "jan", + "feb": "fev", + "mar": "mar", + "apr": "abr", + "may": "mai", + "jun": "jun", + "jul": "jul", + "aug": "ago", + "sep": "set", + "oct": "out", + "nov": "nov", + "dec": "dez", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Evento criado", + "event_updated": "Evento atualizado", + "event_deleted": "Evento excluído", + "calendar_created": "Calendário criado", + "calendar_deleted": "Calendário excluído", + "event_move_error": "Falha ao mover o evento", + "event_resize_error": "Falha ao redimensionar o evento", + "alert_title": "Evento próximo", + "alert_now": "Começa agora", + "alert_in_minutes": "Em {count} min", + "invitation_sent": "Convites enviados", + "invitations_denied_title": "Não é possível enviar os convites", + "invitations_denied": "O servidor recusou enviar os convites: {reason}. Guardar o evento sem os enviar?", + "save_without_invitations": "Guardar sem convites", + "rsvp_updated": "Resposta atualizada", + "rsvp_error": "Falha ao atualizar resposta", + "event_duplicated": "Evento duplicado", + "event_error": "Falha ao salvar o evento", + "task_due": "Tarefa vencendo", + "event_exported": "Evento exportado", + "title_copied": "Título copiado", + "link_copied": "Link copiado" + }, + "status": { + "loading_calendars": "Carregando calendários...", + "loading_events": "Carregando eventos..." + }, + "quick_create": { + "placeholder": "Título do novo evento", + "aria_label": "Criação rápida de evento", + "add_title": "Adicionar título", + "more_options": "Mais opções" + }, + "nav_prev": "Anterior", + "nav_next": "Próximo", + "import": { + "title": "Importar calendário", + "tab_file": "Arquivo", + "tab_url": "URL", + "select_file": "Selecionar arquivo .ics", + "drop_file": "ou arraste o arquivo aqui", + "supported_formats": "Arquivos iCalendar (.ics) suportados", + "url_description": "Insira a URL de um feed iCalendar (.ics) externo para importar eventos.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Suporta URLs iCalendar (.ics)", + "fetch": "Buscar", + "invalid_url": "Insira uma URL válida", + "url_fetch_failed": "Não foi possível buscar o calendário da URL", + "parsing": "Analisando arquivo de calendário...", + "parsed_events": "{count} eventos encontrados", + "no_events": "Nenhum evento encontrado no arquivo", + "select_all": "Selecionar tudo", + "deselect_all": "Desmarcar tudo", + "target_calendar": "Importar para o calendário", + "import_button": "Importar selecionados", + "importing": "Importando eventos...", + "success": "{count} eventos importados com sucesso", + "error": "Falha ao importar calendário", + "file_too_large": "Arquivo excede o limite de 10 MB", + "invalid_format": "Formato de arquivo de calendário inválido" + }, + "webcal_action": { + "title": "Abrir link de calendário", + "description": "Como você gostaria de usar \"{name}\"?", + "import_title": "Importar uma vez", + "import_description": "Busque os eventos agora e copie-os para um dos seus calendários.", + "subscribe_title": "Assinar", + "subscribe_description": "Mantenha este calendário sincronizado automaticamente como um calendário separado.", + "cancel": "Cancelar" + }, + "management": { + "title": "Gerenciamento de calendários", + "description": "Crie, renomeie e personalize seus calendários. Clique com o botão direito em um calendário na barra lateral para alterar rapidamente sua cor.", + "name": "Nome", + "name_placeholder": "Nome do calendário", + "color": "Cor", + "change_color": "Alterar cor", + "random_color": "Nova cor aleatória", + "add_calendar": "Adicionar calendário", + "edit": "Editar", + "delete": "Excluir", + "save": "Salvar", + "create": "Criar", + "cancel": "Cancelar", + "default": "Padrão", + "confirm_delete": "Excluir \"{name}\"? Todos os eventos neste calendário serão removidos.", + "calendar_created": "Calendário criado", + "calendar_updated": "Calendário atualizado", + "calendar_deleted": "Calendário excluído", + "color_updated": "Cor do calendário atualizada", + "error_create": "Falha ao criar calendário", + "error_update": "Falha ao atualizar calendário", + "error_delete": "Falha ao excluir calendário", + "caldav_url": "URL CalDAV", + "copy_url": "Copiar URL CalDAV", + "url_copied": "URL CalDAV copiada para a área de transferência", + "confirm_clear": "Limpar todos os eventos de \"{name}\"? Esta ação não pode ser desfeita.", + "set_default": "Definir como padrão", + "default_updated": "Calendário padrão atualizado", + "error_default": "Falha ao definir calendário padrão", + "clear_events": "Limpar eventos", + "events_cleared": "{count} eventos removidos", + "error_clear": "Falha ao limpar os eventos do calendário", + "share": "Compartilhar calendário", + "new_event_in_calendar": "Novo evento neste calendário", + "shared_with": "Compartilhado com {names}", + "shared_with_count": "{count, plural, one {Compartilhado com 1 pessoa} other {Compartilhado com # pessoas}}", + "move_up": "Mover para cima", + "move_down": "Mover para baixo", + "kind": "Conteúdo", + "kind_events": "Eventos", + "kind_tasks": "Tarefas", + "kind_both": "Eventos e tarefas", + "kind_hint": "Define quais aplicativos veem este calendário quando ele é sincronizado via CalDAV. Não pode ser alterado depois.", + "default_alerts": "Lembretes padrão", + "default_alerts_title": "Lembretes padrão de \"{name}\"", + "default_alerts_hint": "Novos eventos neste calendário recebem esses lembretes, a menos que sejam alterados no evento. Alterá-los aqui não muda os eventos existentes. Os lembretes por e-mail são enviados pelo servidor de e-mail.", + "default_alerts_timed": "Eventos com horário", + "default_alerts_all_day": "Eventos de dia inteiro", + "default_alerts_updated": "Lembretes padrão atualizados" + }, + "subscription": { + "title": "Assinatura iCal", + "section_title": "Assinaturas iCal", + "description": "Assine um feed externo do iCalendar. Os eventos serão sincronizados automaticamente em seu próprio calendário. Suporta URLs https:// e webcal://.", + "url_label": "URL do calendário", + "url_placeholder": "https://example.com/calendar.ics ou webcal://...", + "name_label": "Nome do calendário", + "name_placeholder": "ex. Feriados", + "color_label": "Cor", + "refresh_interval": "Intervalo de atualização", + "interval_15": "A cada 15 minutos", + "interval_30": "A cada 30 minutos", + "interval_60": "A cada hora", + "interval_360": "A cada 6 horas", + "interval_1440": "Diariamente", + "subscribe": "Assinar", + "subscribing": "Assinando...", + "save": "Salvar alterações", + "saving": "Salvando...", + "edit": "Editar", + "edit_title": "Editar assinatura", + "updated": "\"{name}\" atualizada", + "update_error": "Falha ao atualizar assinatura", + "invalid_url": "Por favor, insira uma URL válida", + "success": "Assinado \"{name}\"", + "error": "Falha ao adicionar assinatura", + "refresh": "Atualizar agora", + "refresh_success": "Assinatura atualizada", + "refresh_error": "Falha ao atualizar assinatura", + "unsubscribe": "Cancelar assinatura", + "confirm_delete": "Cancelar assinatura de \"{name}\"? O calendário e todos os seus eventos serão removidos.", + "deleted": "Assinatura removida", + "delete_error": "Falha ao remover assinatura", + "last_refreshed": "Última atualização: {time}" + }, + "tasks": { + "label": "Tarefas", + "no_tasks": "Sem tarefas", + "no_title": "(Sem título)", + "mark_complete": "Marcar como concluída", + "mark_incomplete": "Marcar como não concluída", + "filter_all": "Todas", + "filter_pending": "Pendentes", + "filter_completed": "Concluídas", + "filter_overdue": "Atrasadas", + "show_completed": "Mostrar concluídas", + "create": "Nova tarefa", + "edit": "Editar tarefa", + "title_placeholder": "Título da tarefa", + "description_placeholder": "Adicionar uma descrição...", + "due_date": "Data de vencimento", + "include_time": "Incluir horário", + "priority": "Prioridade", + "priority_none": "Nenhuma", + "priority_high": "Alta", + "priority_medium": "Média", + "priority_low": "Baixa", + "progress": "Status", + "progress_needs_action": "Ação necessária", + "progress_in_process": "Em andamento", + "progress_completed": "Concluída", + "progress_cancelled": "Cancelada", + "calendar": "Calendário", + "alert": "Lembrete", + "alert_none": "Nenhum", + "alert_at_time": "No horário de vencimento", + "alert_5min": "5 minutos antes", + "alert_15min": "15 minutos antes", + "alert_30min": "30 minutos antes", + "alert_1hr": "1 hora antes", + "alert_1day": "1 dia antes", + "delete": "Excluir", + "cancel": "Cancelar", + "save": "Salvar", + "quick_add_placeholder": "Adicionar uma tarefa...", + "due_today": "Vence hoje", + "due_tomorrow": "Vence amanhã", + "overdue": "Atrasada" + }, + "nav_open_menu": "Abrir menu", + "toolbar": { + "more": "Mais ações", + "select_view": "Mudar vista, atualmente {view}" + }, + "other_calendars": "Outros calendários", + "calendar_options": "Opções de {name}" + }, + "advanced_search": { + "title": "Pesquisa avançada", + "from": "De", + "from_placeholder": "E-mail ou nome do remetente", + "to": "Para", + "to_placeholder": "E-mail ou nome do destinatário", + "subject": "Assunto", + "subject_placeholder": "O assunto contém...", + "body": "Corpo", + "body_placeholder": "O corpo contém...", + "folder": "Pasta", + "all_folders": "Todas as pastas", + "all_folders_except_spam_trash": "Todas as pastas exceto Spam e Lixeira", + "has_attachment": "Anexos", + "date_after": "Após", + "date_before": "Antes de", + "size_min": "Maior que (KB)", + "size_max": "Menor que (KB)", + "starred": "Favoritos", + "unread": "Não lidos", + "read": "Lidos", + "yes": "Sim", + "no": "Não", + "clear": "Limpar", + "clear_all": "Limpar tudo", + "filters_active": "{count} filtro", + "filters_active_plural": "{count} filtros", + "toggle_filters": "Filtros", + "search_hint": "Use os filtros avançados para uma pesquisa precisa", + "advanced_filters_tooltip": "Filtros de pesquisa avançados", + "results_found": "{count, plural, =0 {Nenhum resultado encontrado} one {# resultado encontrado} other {# resultados encontrados}}", + "results_found_more": "{count}+ resultados encontrados", + "suggestions_recent": "Pesquisas recentes", + "suggestions_people": "Pessoas", + "suggestions_from": "Pesquisar e-mails de {address}", + "suggestions_to": "Pesquisar e-mails para {address}", + "suggestions_remove_recent": "Remover das pesquisas recentes", + "has_words": "Contém as palavras", + "size": "Tamanho", + "date_within": "Data", + "search_in": "Pesquisar em", + "has_attachment_check": "Com anexo", + "search": "Pesquisar" + }, + "welcome": { + "title": "Bem-vindo à sua caixa de entrada", + "tip_compose": "Pressione c para escrever um novo e-mail", + "tip_shortcuts": "Pressione ? para ver todos os atalhos de teclado", + "tip_sidebar": "Encontre Calendário, Contatos e Configurações no menu lateral", + "tip_settings": "Personalize sua experiência nas Configurações", + "tip_compose_touch": "Toque no botão de lápis para escrever um novo e-mail", + "tip_sidebar_touch": "Calendário, Contatos e Configurações ficam na barra inferior", + "got_it": "Entendi", + "settings": "Configurações", + "dismiss": "Fechar", + "start_tour": "Iniciar tour" + }, + "demo_welcome": { + "title": "Bem-vindo ao Bulwark Mail", + "description": "Explore um cliente de webmail completo - diretamente no seu navegador. Todos os dados ficam no seu dispositivo, então teste tudo.", + "feature_email": "Ler e escrever e-mails", + "feature_organize": "Tags, estrelas e pastas", + "feature_shortcuts": "Atalhos de teclado", + "feature_privacy": "Demo 100% privada", + "hint": "Clique num e-mail à esquerda para começar, ou inicie o tour." + }, + "files": { + "office_edit": "Editar documento", + "office_loading": "A abrir o editor de documentos…", + "office_error": "Não foi possível abrir o editor de documentos.", + "office_close": "Fechar editor", + "open_in_new_tab": "Abrir num novo separador", + "title": "Arquivos", + "search_placeholder": "Pesquisar arquivos...", + "empty_state_title": "Ainda não há arquivos", + "upload_files": "Carregar arquivos", + "new_folder": "Nova pasta", + "new_folder_name": "Nome da pasta", + "rename": "Renomear", + "rename_title": "Renomear", + "new_name": "Novo nome", + "name_forbidden_chars": "O servidor não permite estes caracteres nos nomes: {chars}", + "name_reserved": "Este nome está reservado no servidor.", + "delete": "Eliminar", + "delete_confirm_title": "Eliminar recurso", + "delete_confirm_message": "Tem a certeza de que deseja eliminar \"{name}\"? Esta ação não pode ser desfeita.", + "download": "Transferir", + "name": "Nome", + "size": "Tamanho", + "modified": "Modificado", + "type": "Tipo", + "folder": "Pasta", + "file": "Arquivo", + "uploading": "A carregar...", + "upload_success": "{count, plural, one {1 arquivo carregado} other {# arquivos carregados}}", + "upload_canceled": "Carregamento cancelado", + "upload_canceled_partial": "{count, plural, one {1 arquivo foi carregado} other {# arquivos foram carregados}} antes de cancelar", + "upload_error": "Falha ao carregar o arquivo", + "create_folder_success": "Pasta criada", + "create_folder_error": "Falha ao criar a pasta", + "delete_success": "Eliminado com sucesso", + "delete_error": "Falha ao eliminar", + "rename_success": "Renomeado com sucesso", + "rename_error": "Falha ao renomear", + "download_error": "Falha ao transferir", + "not_available": "O armazenamento de arquivos não está disponível neste servidor", + "cancel": "Cancelar", + "create": "Criar", + "save": "Guardar", + "no_results": "Nenhum arquivo corresponde à sua pesquisa", + "batch_delete_confirm_message": "Tem a certeza de que deseja eliminar {count, plural, one {1 item} other {# itens}}? Esta ação não pode ser desfeita.", + "batch_delete_success": "{count, plural, one {1 item eliminado} other {# itens eliminados}}", + "grid_view": "Vista em grelha", + "list_view": "Vista em lista", + "details": "Detalhes", + "path": "Caminho", + "preview": "Pré-visualização", + "preview_error": "Falha ao carregar a pré-visualização", + "cut": "Cortar", + "copy": "Copiar", + "paste": "Colar", + "move_success": "{count, plural, one {1 item movido} other {# itens movidos}}", + "move_error": "Falha ao mover", + "paste_success": "Colado com sucesso", + "paste_error": "Falha ao colar", + "new_text_file": "Novo arquivo de texto", + "file_name": "Nome do arquivo", + "retry": "Tentar novamente", + "refresh": "Atualizar", + "duplicate": "Duplicar", + "duplicate_success": "Duplicado com sucesso", + "duplicate_error": "Falha ao duplicar", + "share": "Compartilhar", + "shared": "Compartilhado", + "shared_with_me": "Compartilhado comigo", + "shared_by": "Compartilhado por {name}", + "create_file_success": "Arquivo criado", + "create_file_error": "Falha ao criar arquivo", + "favorites": "Favoritos", + "recent": "Recentes", + "show_in_folder": "Mostrar na pasta", + "remove_from_recent": "Remover dos recentes", + "upload_folder": "Carregar pasta", + "file_too_large": "\"{name}\" excede o tamanho máximo do arquivo ({max})", + "undo": "Desfazer", + "undo_success": "Ação desfeita", + "undo_error": "Falha ao desfazer", + "toolbar": "Ações de arquivo", + "file_list": "Arquivos e pastas", + "context_menu": "Ações", + "settings_ascending": "Crescente", + "settings_descending": "Decrescente", + "disabled_title": "O recurso de arquivos foi desativado pelo seu administrador", + "disabled_description": "Uploads de arquivos grandes via WebDAV podem causar instabilidade no Stalwart/RocksDB, incluindo falhas de memória e uso irrecuperável de disco. Arquivos excluídos podem não ser removidos imediatamente do armazenamento. Este recurso não é recomendado para ambientes de produção.", + "no_accounts": "Nenhuma conta conectada.", + "open_folder_tree": "Abrir árvore de pastas", + "migration_title": "Atualizando seus arquivos…", + "migration_description": "Organizando pastas e arquivos em sua estrutura adequada. Isso acontece apenas uma vez.", + "new": "Novo", + "my_files": "Meus arquivos", + "folders": "Pastas", + "files_heading": "Arquivos", + "open": "Abrir", + "star": "Adicionar estrela", + "unstar": "Remover estrela", + "expand_folder": "Expandir {name}", + "collapse_folder": "Recolher {name}", + "sidebar_hint": "Adicione estrelas a pastas e abra arquivos para encontrá-los aqui.", + "breadcrumbs": "Caminho da pasta", + "show_path": "Mostrar pastas ocultas no caminho", + "selection_actions": "Ações para os itens selecionados", + "selected_count": "{count, plural, one {1 selecionado} other {# selecionados}}", + "clear_selection": "Limpar seleção", + "select_all": "Selecionar tudo", + "select_item": "Selecionar {name}", + "more_actions": "Mais ações para {name}", + "paste_here": "Colar nesta pasta", + "paste_count": "{count, plural, one {Colar 1 item} other {Colar # itens}}", + "sort_by": "Ordenar por", + "view_mode": "Layout", + "clear_search": "Limpar pesquisa", + "no_results_for": "Nada nesta pasta corresponde a \"{query}\"", + "download_count": "{count, plural, one {Transferir 1 arquivo} other {Transferir # arquivos}}", + "delete_count": "{count, plural, one {Eliminar 1 item} other {Eliminar # itens}}", + "close_details": "Fechar detalhes", + "file_kind": "Arquivo {ext}", + "folder_summary": "{folders, plural, =0 {Nenhuma pasta} one {1 pasta} other {# pastas}}, {files, plural, =0 {nenhum arquivo} one {1 arquivo} other {# arquivos}}", + "details_hint": "Selecione um arquivo ou uma pasta para ver os detalhes.", + "sharing": "Compartilhamento", + "shared_with_count": "{count, plural, one {Compartilhado com 1 pessoa} other {Compartilhado com # pessoas}}", + "only_you": "Apenas você", + "empty_folder_title": "Esta pasta está vazia", + "empty_folder_description": "Arraste arquivos para aqui ou carregue-os do seu dispositivo.", + "empty_folder_read_only": "Não pode adicionar arquivos a esta pasta.", + "choose_account": "Escolher uma conta", + "drop_to_upload": "Solte para carregar para {folder}", + "uploading_count": "A carregar {current} de {total}", + "dismiss": "Fechar", + "zoom_in": "Aumentar zoom", + "zoom_out": "Diminuir zoom", + "zoom_reset": "Ajustar ao ecrã", + "rotate": "Rodar", + "close_preview": "Fechar" + }, + "smime": { + "your_certificates": "Seus certificados", + "your_certificates_desc": "Importe e gerencie seus certificados S/MIME para assinar e criptografar e-mails", + "recipient_certificates": "Certificados dos destinatários", + "recipient_certificates_desc": "Certificados públicos para criptografar e-mails para destinatários", + "identity_bindings": "Vínculos de identidade e chave", + "identity_bindings_desc": "Associe certificados S/MIME às suas identidades de e-mail", + "defaults_title": "Padrões", + "defaults_desc": "Configure o comportamento padrão de assinatura e criptografia", + "import_pkcs12": "Importar PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importar certificado", + "no_certificates": "Nenhum certificado importado ainda", + "no_recipient_certs": "Nenhum certificado de destinatário", + "expires": "Expira em", + "expired": "Expirado", + "bound_to": "Vinculado a", + "no_key_bound": "Nenhum", + "lock": "Bloquear chave", + "unlock": "Desbloquear chave", + "details": "Ver detalhes", + "delete": "Excluir", + "encrypt_by_default": "Criptografar por padrão", + "encrypt_by_default_desc": "Criptografar e-mails automaticamente quando todos os destinatários tiverem certificados", + "remember_unlocked": "Lembrar chaves desbloqueadas", + "remember_unlocked_desc": "Manter as chaves desbloqueadas durante esta sessão do navegador", + "sign_default_for": "Assinar por padrão para", + "enter_p12_passphrase": "Inserir a frase secreta do PKCS#12", + "p12_passphrase_desc": "Insira a frase secreta que protege este arquivo de certificado", + "enter_storage_passphrase": "Definir frase secreta de armazenamento", + "storage_passphrase_desc": "Escolha uma frase secreta para proteger esta chave no navegador", + "next": "Próximo", + "import": "Importar", + "unlock_key": "Desbloquear chave", + "unlock_key_desc": "Insira a frase secreta de armazenamento para desbloquear esta chave para assinatura ou descriptografia", + "passphrase_placeholder": "Insira a frase secreta", + "confirm_passphrase_placeholder": "Confirme a frase secreta", + "passphrase_mismatch": "As frases secretas não coincidem", + "cancel": "Cancelar", + "processing": "Processando…", + "close": "Fechar", + "certificate_details": "Detalhes do certificado", + "cert_subject": "Titular", + "cert_issuer": "Emissor", + "cert_email": "E-mail", + "cert_serial": "Número de série", + "cert_validity": "Validade", + "cert_fingerprint": "Impressão digital (SHA-256)", + "cert_algorithm": "Algoritmo", + "cert_capabilities": "Recursos", + "cert_source": "Origem", + "cert_expired": "Este certificado expirou", + "cert_not_yet_valid": "Este certificado ainda não é válido", + "cap_sign": "Assinatura", + "cap_encrypt": "Criptografia", + "cap_none": "Nenhum", + "show_passphrase": "Mostrar frase secreta", + "hide_passphrase": "Ocultar frase secreta", + "sign_toggle": "Assinar", + "encrypt_toggle": "Criptografar", + "missing_recipient_certs": "Certificados ausentes para: {emails}", + "missing_sender_cert": "Nenhum certificado vinculado a esta identidade", + "status_encrypted_ok": "Esta mensagem foi criptografada", + "status_encrypted_no_key": "Esta mensagem está criptografada, mas nenhuma chave correspondente foi encontrada", + "status_encrypted_failed": "Falha ao descriptografar esta mensagem", + "status_signed_valid": "Assinatura verificada", + "status_signed_invalid": "A verificação da assinatura falhou", + "status_signed_expired_cert": "Assinada com um certificado expirado", + "status_signed_self_signed": "Assinatura válida, mas o certificado é autoassinado (não confiável)", + "status_signed_mismatch": "A assinatura é válida, mas o signatário não corresponde ao remetente", + "status_unsupported": "Formato S/MIME não suportado", + "auto_import_signer_certs": "Importar automaticamente certificados de signatários", + "auto_import_signer_certs_desc": "Salvar automaticamente certificados de e-mails assinados verificados para criptografia futura", + "export": "Exportar", + "enter_export_passphrase": "Definir frase secreta de exportação", + "export_passphrase_desc": "Escolha uma frase secreta para proteger o arquivo PKCS#12 exportado", + "export_storage_desc": "Insira a frase secreta de armazenamento para descriptografar a chave para exportação", + "incorrect_passphrase": "Frase secreta incorreta" + }, + "tour": { + "step_counter": "Passo {current} de {total}", + "skip": "Pular tour", + "back": "Voltar", + "next": "Próximo", + "finish": "Finalizar", + "take_a_tour": "Faça um tour pela interface", + "restart_title": "Tour introdutório", + "restart_desc": "Rever o tour guiado da interface", + "restart_button": "Reiniciar tour", + "show_on_new_devices_title": "Mostrar em novos dispositivos", + "show_on_new_devices_desc": "Reproduzir o banner de boas-vindas e o tour no primeiro login num novo dispositivo, mesmo que já os tenhas concluído noutro lado", + "sidebar_title": "Suas caixas de correio", + "sidebar_desc": "Esta é a barra lateral de pastas. Clique em qualquer caixa para ver seus e-mails. Você pode criar pastas, arrastar e-mails entre elas e ver contadores de não lidos.", + "compose_title": "Escrever um e-mail", + "compose_desc": "Clique aqui para escrever um novo e-mail. Você pode adicionar destinatários, anexos e usar formatação de texto enriquecido.", + "search_title": "Pesquisar sua caixa", + "search_desc": "Pesquise por remetente, assunto ou conteúdo. Clique no ícone de filtro para opções avançadas como intervalo de datas, anexos e mensagens com estrela.", + "email_list_title": "Sua lista de e-mails", + "email_list_desc": "Os e-mails aparecem aqui. Clique em um para lê-lo à direita. Use a caixa de seleção para selecionar vários, depois mova, exclua ou etiquete em massa.", + "email_viewer_title": "Painel de leitura", + "email_viewer_desc": "O e-mail selecionado abre aqui. Responda, encaminhe, arquive ou exclua com os botões da barra de ferramentas. Você também pode marcar e-mails com estrela ou adicionar etiquetas coloridas.", + "keywords_title": "Etiquetas coloridas", + "keywords_desc": "Organize seus e-mails com etiquetas coloridas. Arraste um e-mail para uma etiqueta para marcá-lo, ou clique com o botão direito para atribuir etiquetas.", + "calendar_title": "Calendário", + "calendar_desc": "Mude para o calendário para gerenciar seus eventos. Crie eventos, defina lembretes e visualize os layouts de dia, semana ou mês.", + "contacts_title": "Contatos", + "contacts_desc": "Seu livro de endereços fica aqui. Importe contatos, crie grupos e clique em qualquer contato para ver seus detalhes completos.", + "settings_title": "Configurações", + "settings_desc": "Personalize tudo: tema, densidade, assinaturas, filtros, atalhos de teclado, padrões do calendário e mais.", + "shortcuts_title": "Atalhos de teclado", + "shortcuts_desc": "Usuários avançados adoram isso. Pressione ? a qualquer momento para ver todos os atalhos disponíveis. Navegue, escreva e gerencie e-mails sem tocar no mouse.", + "compose_open_title": "O editor", + "compose_open_desc": "Este é o editor de e-mail. Adicione destinatários, escreva sua mensagem, anexe arquivos e use formatação de texto rico. Você também pode salvar rascunhos e usar modelos.", + "calendar_view_title": "Seu calendário", + "calendar_view_desc": "Aqui está seu calendário com eventos de exemplo. Você pode alternar entre as visualizações de dia, semana, mês e agenda usando a barra de ferramentas.", + "create_event_title": "Criar um evento", + "create_event_desc": "Clique neste botão para criar um novo evento no calendário. Você pode definir um título, data, hora e adicionar participantes.", + "event_modal_title": "Detalhes do evento", + "event_modal_desc": "Aqui está o formulário do evento. Preencha o título, escolha uma data e hora, adicione um local ou participantes. Clique em salvar quando terminar - ou feche e siga em frente.", + "contacts_list_title": "Seus contatos", + "contacts_list_desc": "Aqui estão seus contatos. Clique em qualquer contato para ver seus detalhes à direita. Você também pode criar novos contatos, importar vCards ou organizar contatos em grupos.", + "files_title": "Armazenamento de arquivos", + "settings_tabs_title": "Menu de configurações", + "settings_tabs_desc": "Aqui estão todas as categorias de configurações. Personalize a aparência, gerencie identidades, configure filtros de e-mail, ajuste o calendário e muito mais.", + "files_desc": "O navegador de arquivos permite carregar, organizar e compartilhar arquivos - como uma nuvem pessoal integrada no seu e-mail.", + "demo_banner_title": "Controles de demonstração", + "demo_banner_desc": "Está no modo de demonstração - tudo permanece no seu navegador. Clique em 'Repor Demonstração' a qualquer momento para recomeçar com dados limpos.", + "quota_title": "Utilização do armazenamento", + "quota_desc": "Acompanhe o tamanho da sua caixa de correio aqui. O círculo preenche-se à medida que utiliza mais espaço." + }, + "unified_mailbox": { + "search_unavailable": "A pesquisa não está disponível na vista unificada" + }, + "calendar_event_notifications": { + "someone": "Alguém", + "invited": "{name} convidou-o para \"{title}\"", + "updated": "{name} atualizou \"{title}\"", + "cancelled": "{name} cancelou \"{title}\"", + "untitled": "Evento sem título" + }, + "share_notifications": { + "someone": "Alguém", + "shared": "{name} partilhou consigo {kind} \"{object}\"", + "changed": "{name} alterou o seu acesso a {kind} \"{object}\"", + "revoked": "{name} removeu o seu acesso a {kind} \"{object}\"", + "object": { + "folder": "a pasta", + "calendar": "o calendário", + "address_book": "o livro de endereços", + "files": "a pasta de ficheiros" + } + }, + "sharing": { + "title": "Compartilhar \"{name}\"", + "description": "Conceda acesso a outros usuários ou grupos neste servidor. As alterações têm efeito imediato.", + "no_shares": "Ainda não compartilhado.", + "add_person": "Adicionar pessoa ou grupo", + "search_placeholder": "Buscar por nome ou e-mail…", + "loading_principals": "Carregando usuários…", + "no_principals": "Nenhum outro usuário ou grupo encontrado.", + "no_match": "Sem resultados.", + "remove": "Remover acesso", + "group": "Grupo", + "share_added": "Acesso concedido", + "share_updated": "Acesso atualizado", + "share_removed": "Acesso removido", + "share_failed": "Falha ao atualizar o compartilhamento", + "preset": { + "freeBusy": "Apenas disponibilidade", + "read": "Somente leitura", + "readWrite": "Leitura e escrita", + "manager": "Gerente", + "custom": "Personalizado" + } + }, + "quote_header": { + "reply_line": "Em {date}, {from} escreveu:", + "forwarded_separator": "---------- Mensagem encaminhada ----------", + "from_label": "De", + "date_label": "Data", + "subject_label": "Assunto" + }, + "pwa_install": { + "title": "Instalar {appName}", + "description": "Instale o nosso app para acesso rápido e suporte offline.", + "not_now": "Agora não", + "install": "Instalar", + "dont_remind": "Não lembrar novamente", + "dismiss_aria": "Dispensar aviso de instalação" + }, + "deep_link": { + "message_not_found": "Esta mensagem não está mais disponível.", + "thread_not_found": "Esta conversa não está mais disponível.", + "folder_not_found": "Esta pasta não está mais disponível.", + "event_not_found": "Este evento não está mais disponível.", + "file_not_found": "Este arquivo não está mais disponível.", + "account_unavailable": "Entre nessa conta para abrir este link.", + "copy_message": "Copiar link da mensagem", + "copy_conversation": "Copiar link da conversa", + "copy_event": "Copiar link do evento", + "copied": "Link copiado", + "copy_failed": "Não foi possível copiar o link" + }, + "plugins": { + "oauth_callback": { + "completing": "A concluir a autorização…", + "completing_message": "Aguarde enquanto a ligação é estabelecida.", + "received_title": "Autorização recebida", + "received_message": "Pode voltar ao separador onde iniciou a ligação.", + "failed_title": "Falha na autorização", + "denied": "A autorização foi negada.", + "failed_detail": "Falha na autorização: {error}", + "missing_params": "Falta o código de autorização ou o estado.", + "delivery_failed": "Não foi possível entregar o resultado da autorização ao separador da aplicação. Feche esta janela e tente novamente." + }, + "settings": { + "title": "Plugins", + "description": "Plugins adicionados pelo seu administrador. Ative ou desative-os na sua conta.", + "description_uploads": "Os plugins adicionam funções ao webmail. Ative ou desative-os na sua conta, ou instale os seus próprios.", + "description_demo": "Os plugins não estão disponíveis na demo.", + "install_button": "Instalar plugin", + "install_hint": "Escolha um arquivo ZIP de plugin.", + "install_hint_approval": "Escolha um arquivo ZIP de plugin. Um administrador precisa aprová-lo antes que você possa ativá-lo.", + "install_uploads_off": "Seu administrador não permite instalar plugins.", + "install_managed_id": "Seu administrador já fornece um plugin com este nome, por isso ele não pode ser substituído.", + "install_invalid": "Este arquivo não é um plugin que possa ser instalado: {details}", + "toast_installed": "Plugin instalado. Ative-o abaixo.", + "toast_installed_pending": "Plugin instalado e enviado ao seu administrador para aprovação.", + "toast_enabled": "{name} está ativado", + "toast_disabled": "{name} está desativado", + "toast_removed": "{name} foi removido", + "toast_forced": "Seu administrador mantém {name} ativado", + "toast_approval_requested": "{name} está aguardando a aprovação do seu administrador", + "empty_title": "Nenhum plugin ainda", + "empty_admin": "Seu administrador não adicionou nenhum plugin.", + "empty_uploads": "Instale um plugin a partir de um arquivo ZIP para começar.", + "empty_demo": "Os plugins precisam de uma conta real, por isso a demo não tem nenhum.", + "syncing": "Verificando plugins com o servidor…", + "status_installed": "Desativado", + "status_enabled": "Iniciando", + "status_running": "Ativado", + "status_disabled": "Desativado", + "status_error": "Problema", + "badge_forced": "Obrigatório", + "badge_managed": "Do seu administrador", + "badge_yours": "Instalado por você", + "badge_awaiting": "Aguardando aprovação", + "toggle_label": "Ativar ou desativar {name}", + "forced_note": "Seu administrador exige este plugin, por isso ele não pode ser desativado.", + "permissions": "O que ele pode fazer", + "permissions_managed": "O que ele pode fazer (aprovado pelo seu administrador)", + "settings": "Configurações", + "remove": "Remover", + "remove_title": "Remover {name}?", + "remove_message": "O plugin e tudo o que ele armazenou neste navegador serão excluídos. Você pode instalá-lo novamente depois.", + "remove_confirm": "Remover", + "error_user_plugins_off": "Seu administrador desativou os plugins instalados por usuários, por isso este plugin não é executado.", + "error_approval_unavailable": "Este plugin foi instalado antes de as aprovações serem exigidas. Remova-o e instale-o novamente para enviá-lo para aprovação.", + "error_approval_denied": "Seu administrador não aprovou este plugin.", + "error_approval_pending": "Aguardando o seu administrador aprovar este plugin.", + "error_auto_disabled": "Desativado porque falhava repetidamente. Ative-o de novo para tentar outra vez.", + "accounts_heading": "Contas conectadas", + "account_on": "{label} em {host}", + "account_waiting": "{label} em {host} (login não concluído)", + "disconnect": "Desconectar", + "disconnect_failed": "Não foi possível desconectar {label}" + }, + "consent": { + "title": "Permitir que {name} faça isto?", + "intro": "O plugin pede as permissões abaixo. Ele só as recebe se você permitir.", + "high_risk_note": "Os itens marcados dão ao plugin acesso ao conteúdo dos seus e-mails ou permitem que ele envie dados para outro lugar. Só os permita para plugins em que você confia.", + "revoke_hint": "Você pode desativar ou remover o plugin em Configurações a qualquer momento.", + "deny": "Não permitir", + "allow": "Permitir" + }, + "permissions": { + "none": "Não precisa de permissões especiais.", + "high_risk": "Requer atenção", + "email_read": { + "title": "Ler seus e-mails", + "body": "Ver remetentes, destinatários, assuntos e o texto completo de qualquer mensagem da sua conta, e suas pastas." + }, + "email_write": { + "title": "Alterar e organizar seus e-mails", + "body": "Adicionar ou remover etiquetas, marcações e outras palavras-chave nas suas mensagens, movê-las entre suas pastas e para o arquivo, e salvar rascunhos." + }, + "email_send": { + "title": "Alterar e-mails antes do envio", + "body": "Ler e alterar os destinatários, o assunto, o texto e os anexos das mensagens que você escreve, e impedir o envio de uma mensagem." + }, + "crypto_full": { + "title": "Acesso total a este aplicativo", + "body": "É executado com os mesmos direitos do próprio webmail. Pode ler todos os seus e-mails e chaves armazenadas e agir em seu nome. Só permita isto para plugins em que você confia totalmente." + }, + "email_raw_send": { + "title": "Enviar mensagens criadas por ele mesmo", + "body": "Enviar mensagens completas, por exemplo assinadas ou criptografadas, a partir da sua conta." + }, + "email_blob_read": { + "title": "Ler o conteúdo bruto das mensagens", + "body": "Ler os dados originais das suas mensagens e anexos, e de cada arquivo que você anexa antes do envio." + }, + "email_blob_write": { + "title": "Substituir anexos antes do envio", + "body": "Enviar arquivos para o seu servidor de e-mail e substituir um arquivo anexado por outro antes de a mensagem ser enviada." + }, + "email_render_takeover": { + "title": "Alterar como as mensagens são exibidas", + "body": "Substituir o texto das mensagens que você abre e da lista de mensagens, por exemplo para mostrar conteúdo descriptografado." + }, + "calendar_read": { + "title": "Ler seu calendário", + "body": "Ver seus calendários e os eventos deles." + }, + "calendar_write": { + "title": "Alterar seu calendário", + "body": "Criar, alterar e excluir eventos." + }, + "contacts_read": { + "title": "Ler seus contatos", + "body": "Pesquisar e ler seus catálogos de endereços." + }, + "contacts_write": { + "title": "Alterar seus contatos", + "body": "Criar, alterar e excluir contatos e catálogos de endereços." + }, + "files_read": { + "title": "Ler seus arquivos", + "body": "Navegar e abrir os arquivos armazenados na sua conta." + }, + "files_write": { + "title": "Alterar seus arquivos", + "body": "Enviar, renomear, mover e excluir arquivos na sua conta." + }, + "identity_read": { + "title": "Ver suas identidades de envio", + "body": "Ver os endereços dos quais você envia e as assinaturas deles." + }, + "identity_write": { + "title": "Alterar suas identidades de envio", + "body": "Criar, alterar e excluir identidades de envio e assinaturas." + }, + "filters_read": { + "title": "Ler seus filtros de e-mail", + "body": "Ver as regras que organizam seus e-mails recebidos." + }, + "filters_write": { + "title": "Alterar seus filtros de e-mail", + "body": "Adicionar regras aos filtros do servidor. Essas regras podem mover, encaminhar ou excluir e-mails recebidos." + }, + "tasks_read": { + "title": "Ler suas tarefas", + "body": "Ver suas listas de tarefas e tarefas." + }, + "tasks_write": { + "title": "Alterar suas tarefas", + "body": "Criar, alterar, concluir e excluir tarefas." + }, + "templates_read": { + "title": "Ler seus modelos", + "body": "Ver seus modelos de mensagem salvos." + }, + "templates_write": { + "title": "Alterar seus modelos", + "body": "Criar, alterar e excluir modelos de mensagem." + }, + "smime_read": { + "title": "Ver seu status S/MIME", + "body": "Ver se você tem chaves e certificados S/MIME, sem acesso às chaves." + }, + "vacation_read": { + "title": "Ver sua resposta automática", + "body": "Ver se sua resposta automática está ativada e o que ela diz." + }, + "vacation_write": { + "title": "Alterar sua resposta automática", + "body": "Ativar ou desativar sua resposta automática e alterar o texto e as datas." + }, + "settings_read": { + "title": "Ler suas configurações", + "body": "Ver suas preferências e definições de etiquetas." + }, + "settings_write": { + "title": "Alterar suas configurações", + "body": "Adicionar e reordenar etiquetas e alterar suas preferências." + }, + "security_read": { + "title": "Ver a segurança da sua conta", + "body": "Ver se a autenticação de dois fatores e a criptografia estão ativadas, sem acesso a segredos." + }, + "auth_observe": { + "title": "Saber quando você entra", + "body": "Ser avisado quando você entra, troca de conta ou sua sessão é renovada." + }, + "auth_emit": { + "title": "Encerrar sua sessão", + "body": "Encerrar sua sessão no webmail." + }, + "account_read": { + "title": "Ver suas contas", + "body": "Ver as contas que você adicionou, com os endereços e servidores delas." + }, + "http_post": { + "title": "Usar o servidor do webmail por você", + "body": "Chamar estes endpoints do servidor com o seu login:" + }, + "http_fetch": { + "title": "Conectar-se a outros sites", + "body": "Enviar solicitações a estes sites. Tudo o que o plugin pode ler pode ser enviado para lá:" + }, + "admin_config": { + "title": "Ler as configurações do seu administrador", + "body": "Ler as configurações que seu administrador salvou para este plugin." + }, + "ui_observe": { + "title": "Acompanhar onde você está no aplicativo", + "body": "Saber qual página está aberta e reagir aos links em que você clica." + }, + "ui_toolbar": { + "title": "Adicionar botões às barras de ferramentas", + "body": "Mostrar os próprios botões na barra de ferramentas das mensagens, em uma mensagem aberta e na barra superior." + }, + "ui_app_top_banner": { + "title": "Mostrar um banner no topo", + "body": "Mostrar um aviso no topo do aplicativo." + }, + "ui_email_banner": { + "title": "Mostrar avisos nas mensagens", + "body": "Mostrar os próprios avisos e resumos acima, dentro e entre as partes de uma mensagem ou conversa aberta." + }, + "ui_email_footer": { + "title": "Adicionar conteúdo abaixo das mensagens", + "body": "Mostrar o próprio conteúdo abaixo de uma mensagem aberta." + }, + "ui_contact_cryptokeys": { + "title": "Mostrar chaves de criptografia dos contatos", + "body": "Mostrar e gerenciar as chaves de criptografia de um contato." + }, + "ui_email_details": { + "title": "Adicionar painéis aos detalhes da mensagem", + "body": "Mostrar o próprio painel ao lado ou abaixo de uma mensagem aberta." + }, + "ui_download_file": { + "title": "Salvar arquivos no seu dispositivo", + "body": "Oferecer para download arquivos que ele criou." + }, + "ui_message_list_tabs": { + "title": "Adicionar abas à sua caixa de entrada", + "body": "Organizar sua caixa de entrada em abas como Atualizações ou Promoções." + }, + "ui_composer_toolbar": { + "title": "Adicionar ao editor", + "body": "Mostrar os próprios botões, itens de menu e seções enquanto você escreve uma mensagem." + }, + "ui_composer_sidebar": { + "title": "Adicionar um painel ao editor", + "body": "Mostrar o próprio painel ao lado da mensagem que você escreve." + }, + "ui_sidebar_widget": { + "title": "Adicionar à barra lateral", + "body": "Mostrar as próprias caixas e seções na barra lateral do e-mail." + }, + "ui_settings_section": { + "title": "Adicionar uma página de configurações", + "body": "Mostrar a própria página em Configurações." + }, + "ui_context_menu": { + "title": "Adicionar itens ao menu da mensagem", + "body": "Adicionar itens ao menu que abre quando você clica com o botão direito em uma mensagem." + }, + "ui_navigation_rail": { + "title": "Adicionar à barra de navegação", + "body": "Mostrar o próprio botão na barra de navegação." + }, + "ui_keyboard": { + "title": "Adicionar atalhos de teclado", + "body": "Reagir aos atalhos de teclado que ele define." + }, + "ui_calendar_action": { + "title": "Adicionar à visualização do calendário", + "body": "Mostrar os próprios botões e seções nos eventos, e rótulos curtos e pontos nos dias do calendário." + }, + "ui_admin_page": { + "title": "Adicionar uma página de administração", + "body": "Mostrar a própria página nas configurações de administração." + }, + "app_lifecycle": { + "title": "Saber quando o aplicativo inicia e para", + "body": "Ser avisado quando o aplicativo inicia, fica offline ou você sai." + }, + "compose_open": { + "title": "Abrir o editor para você", + "body": "Abrir uma nova mensagem com destinatários, texto ou anexos já preenchidos. Você continua decidindo se ela é enviada." + }, + "compose_read": { + "title": "Ler a mensagem que você está escrevendo", + "body": "Ler o texto de uma mensagem enquanto você a escreve, quando usa o plugin no editor. Não a assinatura nem a mensagem citada." + }, + "compose_write": { + "title": "Alterar a mensagem que você está escrevendo", + "body": "Substituir ou adicionar texto em uma mensagem enquanto você a escreve, quando usa o plugin no editor. Você pode desfazer cada alteração e continua decidindo se envia." + }, + "ui_notifications": { + "title": "Mostrar notificações", + "body": "Mostrar notificações no seu dispositivo, ou dentro do aplicativo se você não as permitiu. Cada uma indica o nome do plugin." + }, + "ui_message_list": { + "title": "Adicionar à lista de mensagens", + "body": "Mostrar o próprio conteúdo acima da lista de mensagens, e botões nas mensagens e na barra das mensagens selecionadas." + }, + "ui_contacts": { + "title": "Adicionar à visualização de contatos", + "body": "Mostrar as próprias seções nos detalhes de um contato e botões na lista de contatos." + }, + "ui_tasks": { + "title": "Adicionar à visualização de tarefas", + "body": "Mostrar as próprias seções nos detalhes de uma tarefa e botões na lista de tarefas." + }, + "ui_files": { + "title": "Adicionar à visualização de arquivos", + "body": "Mostrar as próprias seções na pré-visualização de arquivos e itens no menu de um arquivo." + }, + "ui_commands": { + "title": "Adicionar comandos", + "body": "Adicionar os próprios itens à paleta de comandos (Ctrl+K)." + }, + "ui_app_page": { + "title": "Adicionar páginas", + "body": "Adicionar as próprias páginas ao aplicativo, cada uma com um botão na barra de navegação." + }, + "http_proxy": { + "title": "Usar contas que você conecta", + "body": "Enviar solicitações pelo servidor do Bulwark com uma conta ou chave configurada por você ou pelo seu administrador. O plugin nunca vê a senha, mas pode fazer tudo o que essa conta pode fazer no próprio servidor." + } + }, + "dialog": { + "from_plugin": "Do plugin {name}", + "ok": "OK", + "submit": "Enviar", + "confirm": "Confirmar", + "cancel": "Cancelar", + "close": "Fechar", + "turn_off": "Desativar {name}", + "plugin_menu": "Opções de {name}", + "password_note": "Este formulário vem do plugin {name}, não do webmail. Só introduza uma palavra-passe se confiar neste plugin.", + "toast_auto_disabled": "{name} foi desativado porque falhava repetidamente." + }, + "connect": { + "title": "Conectar {label}", + "intro_password": "{name} quer usar sua conta {label}. O Bulwark guarda a senha no servidor dele e só a envia para {host}. O plugin nunca a vê.", + "intro_password_any": "{name} quer usar sua conta {label}. O Bulwark guarda a senha no servidor dele e só a envia para o servidor que você informar aqui. O plugin nunca a vê.", + "intro_signin": "{name} quer entrar na sua conta {label}. Informe o servidor aqui e depois entre na página dele. O Bulwark guarda no servidor dele o acesso que recebe; o plugin nunca o vê.", + "server": "Endereço do servidor", + "server_hint": "cloud.example.com", + "username": "Nome de usuário", + "secret": "Senha ou chave", + "connect": "Conectar", + "continue": "Continuar", + "cancel": "Cancelar", + "saving": "Salvando…", + "error_server": "Informe o endereço do servidor, por exemplo https://cloud.example.com", + "error_save": "Não foi possível salvar: {error}" + } + }, + "global_search": { + "title": "Pesquisar em tudo", + "placeholder": "Pesquisar e-mail, contactos, calendário, ficheiros...", + "scope_label": "Pesquisar em", + "account_label": "Conta", + "scope_all": "Tudo", + "scope_mail": "E-mail", + "scope_contacts": "Contatos", + "scope_calendar": "Calendário", + "scope_files": "Arquivos", + "all_accounts": "Todas as contas", + "show_all": "Mostrar todos os resultados", + "no_results": "Sem resultados para “{query}”", + "server_hint": "Palavras completas pesquisam no servidor; palavras parciais correspondem ao que já está carregado", + "searching": "A pesquisar...", + "account_error": "Não foi possível pesquisar em {account}", + "recent_searches": "Pesquisas recentes", + "recurring": "Recorrente", + "no_subject": "(no subject)", + "empty_hint": "Pesquise e-mail, contactos, eventos e ficheiros em todas as contas. Experimente from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Plano por data", + "grouped": "Agrupado", + "load_more": "Carregar mais", + "preview_empty": "Selecione um resultado para pré-visualizar", + "open_full": "Abrir", + "view_label": "Vista" + }, + "top_bar": { + "more": "Mais opções" + }, + "plugin_pages": { + "loading": "Carregando a página", + "not_available_title": "Esta página não está disponível", + "not_available_description": "O plugin que a adicionou está desativado ou não está mais instalado.", + "back_to_mail": "Voltar ao e-mail", + "settings_group": "Plugins" + }, + "command_palette": { + "title": "Paleta de comandos", + "open": "Abrir a paleta de comandos", + "placeholder": "Digite um comando", + "no_results": "Nenhum comando correspondente", + "go_to": "Ir para {name}", + "compose": "Nova mensagem", + "search": "Pesquisar no e-mail", + "theme_dark": "Mudar para o tema escuro", + "theme_light": "Mudar para o tema claro", + "shortcuts": "Mostrar atalhos de teclado", + "group_navigation": "Ir para", + "group_actions": "Ações", + "group_plugins": "Plugins" + }, + "notification_center": { + "title": "Notificações", + "mark_all_read": "Marcar todas como lidas", + "clear": "Limpar", + "empty": "Nenhuma notificação", + "close": "Fechar", + "unread": "Não lida", + "unread_count": "{count} não lidas", + "level_error": "Erro", + "level_warning": "Aviso" + } +} diff --git a/locales/rn/ar.json b/locales/rn/ar.json index 4874b1c5..7978eb13 100644 --- a/locales/rn/ar.json +++ b/locales/rn/ar.json @@ -445,7 +445,6 @@ "open_error": "تعذّر فتح الملف", "open_with": "فتح باستخدام…", "preview_too_large": "هذا الملف كبير جدًا بحيث لا يمكن معاينته.", - "selected_count": "{count, plural, zero {تم تحديد #} one {تم تحديد عنصر واحد} two {تم تحديد عنصرين} few {تم تحديد # عناصر} many {تم تحديد # عنصرًا} other {تم تحديد # عنصر}}", "share_add_people": "إضافة أشخاص", "share_empty": "لم تتم المشاركة مع أي شخص بعد", "share_error": "فشل تحديث المشاركة", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "الإضافات", - "themes": "السمات", "updates": "التحديثات" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "لوحات الألوان الفاتحة والداكنة الافتراضية.", - "default_name": "Bulwark", - "mobile_description": "اختر سمة ألوان. تتبع السمات إعداد الوضع الفاتح/الداكن من المظهر؛ وتُدار حزم السمات المخصصة في بريد الويب.", - "title": "السمات" + "mobile_description": "اختر سمة ألوان. تتبع السمات إعداد الوضع الفاتح/الداكن من المظهر؛ وتُدار حزم السمات المخصصة في بريد الويب." }, "vacation": { "warnings": { diff --git a/locales/rn/ca.json b/locales/rn/ca.json index c35057c6..69f69bfb 100644 --- a/locales/rn/ca.json +++ b/locales/rn/ca.json @@ -445,7 +445,6 @@ "open_error": "No s'ha pogut obrir el fitxer", "open_with": "Obre amb…", "preview_too_large": "Aquest fitxer és massa gran per previsualitzar-lo.", - "selected_count": "{count, plural, one {# seleccionat} other {# seleccionats}}", "share_add_people": "Afegeix persones", "share_empty": "Encara no s'ha compartit amb ningú", "share_error": "No s'ha pogut actualitzar la compartició", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Connectors", - "themes": "Temes", "updates": "Actualitzacions" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Les paletes clara i fosca per defecte.", - "default_name": "Bulwark", - "mobile_description": "Trieu un tema de color. Els temes segueixen el paràmetre clar/fosc d'Aparença; els paquets de temes personalitzats es gestionen al webmail.", - "title": "Temes" + "mobile_description": "Trieu un tema de color. Els temes segueixen el paràmetre clar/fosc d'Aparença; els paquets de temes personalitzats es gestionen al webmail." }, "vacation": { "warnings": { diff --git a/locales/rn/cs.json b/locales/rn/cs.json index fb3f8006..91b0ab25 100644 --- a/locales/rn/cs.json +++ b/locales/rn/cs.json @@ -445,7 +445,6 @@ "open_error": "Soubor se nepodařilo otevřít", "open_with": "Otevřít pomocí…", "preview_too_large": "Tento soubor je příliš velký pro náhled.", - "selected_count": "{count, plural, one {# vybraná} few {# vybrané} many {# vybrané} other {# vybraných}}", "share_add_people": "Přidat osoby", "share_empty": "Zatím nikomu nesdíleno", "share_error": "Aktualizace sdílení selhala", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Pluginy", - "themes": "Motivy", "updates": "Aktualizace" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Výchozí světlé a tmavé palety.", - "default_name": "Bulwark", - "mobile_description": "Vyberte barevný motiv. Motivy se řídí nastavením světlý/tmavý ve Vzhledu; vlastní balíčky motivů se spravují ve webmailu.", - "title": "Motivy" + "mobile_description": "Vyberte barevný motiv. Motivy se řídí nastavením světlý/tmavý ve Vzhledu; vlastní balíčky motivů se spravují ve webmailu." }, "vacation": { "warnings": { diff --git a/locales/rn/da.json b/locales/rn/da.json index 6d5c1a79..103d4817 100644 --- a/locales/rn/da.json +++ b/locales/rn/da.json @@ -445,7 +445,6 @@ "open_error": "Kunne ikke åbne filen", "open_with": "Åbn med…", "preview_too_large": "Denne fil er for stor til forhåndsvisning.", - "selected_count": "{count, plural, one {# valgt} other {# valgte}}", "share_add_people": "Tilføj personer", "share_empty": "Ikke delt med nogen endnu", "share_error": "Kunne ikke opdatere deling", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Plugins", - "themes": "Temaer", "updates": "Opdateringer" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Standardpaletterne til lys og mørk.", - "default_name": "Bulwark", - "mobile_description": "Vælg et farvetema. Temaer følger indstillingen for lys/mørk under Udseende; brugerdefinerede temapakker administreres i webmailen.", - "title": "Temaer" + "mobile_description": "Vælg et farvetema. Temaer følger indstillingen for lys/mørk under Udseende; brugerdefinerede temapakker administreres i webmailen." }, "vacation": { "warnings": { diff --git a/locales/rn/de.json b/locales/rn/de.json index df75b691..cbad464d 100644 --- a/locales/rn/de.json +++ b/locales/rn/de.json @@ -451,7 +451,6 @@ "open_error": "Die Datei konnte nicht geöffnet werden", "open_with": "Öffnen mit…", "preview_too_large": "Diese Datei ist zu groß für eine Vorschau.", - "selected_count": "{count, plural, one {# ausgewählt} other {# ausgewählt}}", "share_add_people": "Personen hinzufügen", "share_empty": "Noch mit niemandem geteilt", "share_error": "Freigabe konnte nicht aktualisiert werden", @@ -1120,7 +1119,6 @@ }, "tabs": { "plugins": "Plugins", - "themes": "Designs", "updates": "Updates", "storage": "Speicher & Sicherung", "about": "Über", @@ -1140,9 +1138,7 @@ }, "themes": { "default_description": "Die Standardpaletten für Hell und Dunkel.", - "default_name": "Bulwark", - "mobile_description": "Wählen Sie ein Farbdesign. Designs folgen der Hell/Dunkel-Einstellung unter Darstellung; eigene Designpakete werden im Webmail verwaltet.", - "title": "Designs" + "mobile_description": "Wählen Sie ein Farbdesign. Designs folgen der Hell/Dunkel-Einstellung unter Darstellung; eigene Designpakete werden im Webmail verwaltet." }, "vacation": { "warnings": { diff --git a/locales/rn/en.json b/locales/rn/en.json index 56c33f23..0eb98652 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -284,7 +284,6 @@ "attach_camera": "Camera", "attachment_reminder_body": "Your message mentions \"{keyword}\" but no file is attached. Send anyway?", "attachment_reminder_title": "Forgot an attachment?", - "attachment_too_large": "This file is larger than the server allows ({size} > {max}).", "attachments_too_large_total": "The attachments would exceed the {max} this server allows per message.", "editor_unavailable": "Could not read the message content. Copy your text, then close and reopen the composer.", "format_plain_text": "Switch to plain text", @@ -297,6 +296,7 @@ "no_sent_folder": "No Sent folder found", "permission_camera": "Camera permission is required to take a photo.", "permission_photos": "Photo library permission is required to attach images.", + "read_receipt_off": "Request a read receipt", "recipient_add_contact": "Add to contacts", "recipient_copy": "Copy address", "recipient_move_to": "Move to {field}", @@ -322,15 +322,14 @@ "send_timeout_body": "The message may already have gone out. Check your Sent folder before sending it again.", "send_timeout_title": "No answer from the server", "toolbar": { - "background_color": "Background color", "insert_image": "Insert image", - "remove_background_color": "Remove background color", "remove_link": "Remove link" }, "undo_send_label": "Sending in {seconds}s…", "upload_failed_short": "Upload failed", "uploading": "Uploading...", "uploading_pct": "Uploading {pct}%", + "use_template": "Template", "validation": { "subject_required": "Add a subject" } @@ -422,16 +421,16 @@ "forwarded": "Forwarded", "load_failed": "Failed to load email", "preview": { - "open_external": "Open in app", - "pdf_external": "PDFs open in your PDF viewer on this device.", - "unsupported": "No preview for this file type.", - "media_unsupported": "This file can't be played here.", - "pdf_protected": "This PDF is password-protected. Open it in another app to enter the password.", - "pdf_unreadable": "This PDF can't be shown here.", "archive_files": "{count, plural, one {# file} other {# files}}", "archive_first_files": "The first {count} files", "archive_protected": "This archive is password-protected. Open it in another app to enter the password.", - "archive_unreadable": "This archive can't be opened here." + "archive_unreadable": "This archive can't be opened here.", + "media_unsupported": "This file can't be played here.", + "open_external": "Open in app", + "pdf_external": "PDFs open in your PDF viewer on this device.", + "pdf_protected": "This PDF is password-protected. Open it in another app to enter the password.", + "pdf_unreadable": "This PDF can't be shown here.", + "unsupported": "No preview for this file type." }, "received_via": "to {name}", "remove_color": "Remove tag", @@ -462,7 +461,23 @@ "open_error": "Could not open the file", "open_with": "Open with…", "preview_too_large": "This file is too large to preview.", - "selected_count": "{count, plural, one {# selected} other {# selected}}", + "settings_behavior": "Behavior", + "settings_colored_icons": "Colored Icons", + "settings_colored_icons_desc": "Use colorful icons instead of monochrome", + "settings_default_sort": "Default Sort", + "settings_default_sort_desc": "Choose the default sorting for files", + "settings_default_view": "Default View", + "settings_default_view_desc": "Choose between grid and list layout", + "settings_display": "Display", + "settings_icons": "Icons", + "settings_show_hidden": "Show Hidden Files", + "settings_show_hidden_desc": "Display files and folders that start with a dot", + "settings_show_icons": "Show File Icons", + "settings_show_icons_desc": "Display icons next to files and folders", + "settings_show_thumbnails": "Show Thumbnails", + "settings_show_thumbnails_desc": "Display image previews instead of icons for image files", + "settings_sort_direction": "Sort Direction", + "settings_sort_direction_desc": "Choose ascending or descending order", "share_add_people": "Add people", "share_empty": "Not shared with anyone yet", "share_error": "Failed to update sharing", @@ -477,6 +492,7 @@ "share_unavailable": "Sharing is not available on this device", "shared_with": "Shared with", "sharing_access": "Sharing & access", + "upload": "Upload", "upload_unavailable": "The file picker is not installed in this build. Rebuild the app to enable uploads.", "uploading_progress": "Uploading {current}/{total} · {name}" }, @@ -843,12 +859,6 @@ "plain_text_mode": { "description_mobile": "Compose and read in plain text only." }, - "reply_identity_match": { - "description": "Which received addresses count as yours. Exact address only picks one of your configured identities. Same domain also treats any other address on one of your identity domains as a catch-all alias and rewrites the From header to it. Choose exact address if those addresses are distribution lists rather than aliases.", - "domain": "Any address on my domains", - "exact": "Exact address only", - "label": "Received Address Matching" - }, "send_delay": { "description_mobile": "Hold outgoing mail for a few seconds so you can cancel it. Requires server support." }, @@ -1184,38 +1194,37 @@ "mobile_explainer": "S/MIME is not available in the mobile app yet. Manage keys and certificates in the webmail; signed messages are shown without verification here." }, "summary": { + "composing": "Signature, send delay, drafts", + "contacts": "Address books, sorting", + "content_senders": "Remote images, trusted senders", + "downloads": "File names for saved mail", "events_off": "events off", "events_on": "events on", + "files": "View, sorting, thumbnails", + "filters": "Rules for incoming mail", + "folders": "Create, rename, roles", + "identities": "Addresses you send from", + "keywords": "Tag names and colours", + "layout": "Quick actions, swipes, list order", "mail_off": "Mail off", "mail_on": "Mail on", "offline_off": "Offline mail off", "offline_on": "Offline mail on · {days, plural, one {# day} other {# days}}, {mb} MB", - "version": "Version {version}", - "week_starts": "week starts {day}", - "layout": "Quick actions, swipes, list order", "reading": "Read status, delete, attachments", - "composing": "Signature, send delay, drafts", - "identities": "Addresses you send from", - "vacation": "Automatic reply while away", - "filters": "Rules for incoming mail", - "templates": "Reusable messages", - "folders": "Create, rename, roles", - "keywords": "Tag names and colours", - "downloads": "File names for saved mail", "security": "Password, two-factor, app passwords", - "content_senders": "Remote images, trusted senders", - "contacts": "Address books, sorting", - "files": "View, sorting, thumbnails", "sidebar_apps": "Web apps in the side menu", - "updates": "Install new versions" + "templates": "Reusable messages", + "updates": "Install new versions", + "vacation": "Automatic reply while away", + "version": "Version {version}", + "week_starts": "week starts {day}" }, "tabs": { - "plugins": "Plugins", - "themes": "Themes", - "updates": "Updates", - "storage": "Storage & backup", "about": "About", - "troubleshooting": "Troubleshooting" + "plugins": "Plugins", + "storage": "Storage & backup", + "troubleshooting": "Troubleshooting", + "updates": "Updates" }, "templates": { "delete_confirm": "Permanently delete \"{name}\"?", @@ -1231,9 +1240,7 @@ }, "themes": { "default_description": "The default light and dark palettes.", - "default_name": "Bulwark", - "mobile_description": "Pick a colour theme. Themes follow the light/dark setting from Appearance; custom theme packages are managed in the webmail.", - "title": "Themes" + "mobile_description": "Pick a colour theme. Themes follow the light/dark setting from Appearance; custom theme packages are managed in the webmail." }, "troubleshooting": { "cache_desc": "When a list looks stale or wrong.", @@ -1242,6 +1249,14 @@ "title": "Troubleshooting" }, "vacation": { + "date_range": { + "description": "Optionally limit the auto-reply to a specific period", + "title": "Date Range" + }, + "message": { + "description": "The message that will be sent as a reply", + "title": "Auto-Reply Message" + }, "warnings": { "date_format": "Dates must be \"YYYY-MM-DD\" or \"YYYY-MM-DD HH:MM\"" } diff --git a/locales/rn/es.json b/locales/rn/es.json index ba43ca8f..a910588f 100644 --- a/locales/rn/es.json +++ b/locales/rn/es.json @@ -445,7 +445,6 @@ "open_error": "No se pudo abrir el archivo", "open_with": "Abrir con…", "preview_too_large": "Este archivo es demasiado grande para la vista previa.", - "selected_count": "{count, plural, one {# seleccionado} other {# seleccionados}}", "share_add_people": "Añadir personas", "share_empty": "Aún no se ha compartido con nadie", "share_error": "No se pudo actualizar el uso compartido", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Plugins", - "themes": "Temas", "updates": "Actualizaciones" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Las paletas clara y oscura predeterminadas.", - "default_name": "Bulwark", - "mobile_description": "Elige un tema de color. Los temas siguen el ajuste claro/oscuro de Apariencia; los paquetes de temas personalizados se gestionan en el webmail.", - "title": "Temas" + "mobile_description": "Elige un tema de color. Los temas siguen el ajuste claro/oscuro de Apariencia; los paquetes de temas personalizados se gestionan en el webmail." }, "vacation": { "warnings": { diff --git a/locales/rn/fa.json b/locales/rn/fa.json index e69110c6..b1d46862 100644 --- a/locales/rn/fa.json +++ b/locales/rn/fa.json @@ -445,7 +445,6 @@ "open_error": "باز کردن فایل ممکن نشد", "open_with": "باز کردن با…", "preview_too_large": "این فایل برای پیش‌نمایش بیش از حد بزرگ است.", - "selected_count": "{count, plural, one {# انتخاب شده} other {# انتخاب شده}}", "share_add_people": "افزودن افراد", "share_empty": "هنوز با کسی به اشتراک گذاشته نشده", "share_error": "به‌روزرسانی اشتراک‌گذاری ناموفق بود", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "افزونه‌ها", - "themes": "تم‌ها", "updates": "به‌روزرسانی‌ها" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "پالت‌های روشن و تاریک پیش‌فرض.", - "default_name": "Bulwark", - "mobile_description": "یک تم رنگی انتخاب کنید. تم‌ها از تنظیم روشن/تاریک بخش ظاهر پیروی می‌کنند؛ بسته‌های تم سفارشی در ایمیل تحت وب مدیریت می‌شوند.", - "title": "تم‌ها" + "mobile_description": "یک تم رنگی انتخاب کنید. تم‌ها از تنظیم روشن/تاریک بخش ظاهر پیروی می‌کنند؛ بسته‌های تم سفارشی در ایمیل تحت وب مدیریت می‌شوند." }, "vacation": { "warnings": { diff --git a/locales/rn/fr.json b/locales/rn/fr.json index 286dde78..1773844f 100644 --- a/locales/rn/fr.json +++ b/locales/rn/fr.json @@ -445,7 +445,6 @@ "open_error": "Impossible d'ouvrir le fichier", "open_with": "Ouvrir avec…", "preview_too_large": "Ce fichier est trop volumineux pour être prévisualisé.", - "selected_count": "{count, plural, one {# sélectionné} other {# sélectionnés}}", "share_add_people": "Ajouter des personnes", "share_empty": "Pas encore partagé", "share_error": "Échec de la mise à jour du partage", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Plugins", - "themes": "Thèmes", "updates": "Mises à jour" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Les palettes claire et sombre par défaut.", - "default_name": "Bulwark", - "mobile_description": "Choisissez un thème de couleurs. Les thèmes suivent le réglage clair/sombre de l'Apparence. Les packs de thèmes personnalisés se gèrent dans le webmail.", - "title": "Thèmes" + "mobile_description": "Choisissez un thème de couleurs. Les thèmes suivent le réglage clair/sombre de l'Apparence. Les packs de thèmes personnalisés se gèrent dans le webmail." }, "vacation": { "warnings": { diff --git a/locales/rn/he.json b/locales/rn/he.json index 94985885..b7a09c7e 100644 --- a/locales/rn/he.json +++ b/locales/rn/he.json @@ -445,7 +445,6 @@ "open_error": "לא ניתן היה לפתוח את הקובץ", "open_with": "פתח באמצעות…", "preview_too_large": "הקובץ גדול מכדי להציג תצוגה מקדימה.", - "selected_count": "{count, plural, one {נבחר פריט אחד} two {נבחרו שני פריטים} other {נבחרו # פריטים}}", "share_add_people": "הוסף אנשים", "share_empty": "עדיין לא משותף עם אף אחד", "share_error": "עדכון השיתוף נכשל", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "תוספים", - "themes": "ערכות נושא", "updates": "עדכונים" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "ערכות הצבעים הבהירה והכהה המוגדרות כברירת מחדל.", - "default_name": "Bulwark", - "mobile_description": "בחר ערכת צבעים. ערכות הנושא פועלות לפי הגדרת בהיר/כהה במראה; חבילות ערכות נושא מותאמות מנוהלות בדואר האינטרנט.", - "title": "ערכות נושא" + "mobile_description": "בחר ערכת צבעים. ערכות הנושא פועלות לפי הגדרת בהיר/כהה במראה; חבילות ערכות נושא מותאמות מנוהלות בדואר האינטרנט." }, "vacation": { "warnings": { diff --git a/locales/rn/hu.json b/locales/rn/hu.json index 51401294..fc14a2c9 100644 --- a/locales/rn/hu.json +++ b/locales/rn/hu.json @@ -445,7 +445,6 @@ "open_error": "Nem sikerült megnyitni a fájlt", "open_with": "Megnyitás ezzel…", "preview_too_large": "Ez a fájl túl nagy az előnézethez.", - "selected_count": "{count, plural, one {# kijelölve} other {# kijelölve}}", "share_add_people": "Személyek hozzáadása", "share_empty": "Még nincs megosztva senkivel", "share_error": "Nem sikerült frissíteni a megosztást", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Bővítmények", - "themes": "Témák", "updates": "Frissítések" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Az alapértelmezett világos és sötét paletták.", - "default_name": "Bulwark", - "mobile_description": "Válassz színtémát. A témák a Megjelenés világos/sötét beállítását követik; az egyéni témacsomagokat a webmailben kezelheted.", - "title": "Témák" + "mobile_description": "Válassz színtémát. A témák a Megjelenés világos/sötét beállítását követik; az egyéni témacsomagokat a webmailben kezelheted." }, "vacation": { "warnings": { diff --git a/locales/rn/it.json b/locales/rn/it.json index d960c64b..0d2ca04b 100644 --- a/locales/rn/it.json +++ b/locales/rn/it.json @@ -445,7 +445,6 @@ "open_error": "Impossibile aprire il file", "open_with": "Apri con…", "preview_too_large": "Questo file è troppo grande per l'anteprima.", - "selected_count": "{count, plural, one {# selezionato} other {# selezionati}}", "share_add_people": "Aggiungi persone", "share_empty": "Non ancora condiviso con nessuno", "share_error": "Impossibile aggiornare la condivisione", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Plugin", - "themes": "Temi", "updates": "Aggiornamenti" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Le palette chiara e scura predefinite.", - "default_name": "Bulwark", - "mobile_description": "Scegli un tema di colori. I temi seguono l'impostazione chiaro/scuro di Aspetto; i pacchetti di temi personalizzati si gestiscono nella webmail.", - "title": "Temi" + "mobile_description": "Scegli un tema di colori. I temi seguono l'impostazione chiaro/scuro di Aspetto; i pacchetti di temi personalizzati si gestiscono nella webmail." }, "vacation": { "warnings": { diff --git a/locales/rn/ja.json b/locales/rn/ja.json index 1a48476e..831a7bfe 100644 --- a/locales/rn/ja.json +++ b/locales/rn/ja.json @@ -445,7 +445,6 @@ "open_error": "ファイルを開けませんでした", "open_with": "別のアプリで開く…", "preview_too_large": "このファイルは大きすぎるため、プレビューできません。", - "selected_count": "{count, plural, other {#件選択中}}", "share_add_people": "ユーザーを追加", "share_empty": "まだ誰にも共有されていません", "share_error": "共有の更新に失敗しました", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "プラグイン", - "themes": "テーマ", "updates": "アップデート" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "デフォルトのライト/ダークパレットです。", - "default_name": "Bulwark", - "mobile_description": "カラーテーマを選択します。テーマは「外観」のライト/ダーク設定に従います。カスタムテーマパッケージはウェブメールで管理します。", - "title": "テーマ" + "mobile_description": "カラーテーマを選択します。テーマは「外観」のライト/ダーク設定に従います。カスタムテーマパッケージはウェブメールで管理します。" }, "vacation": { "warnings": { diff --git a/locales/rn/ko.json b/locales/rn/ko.json index b0ffa78e..c02f11de 100644 --- a/locales/rn/ko.json +++ b/locales/rn/ko.json @@ -445,7 +445,6 @@ "open_error": "파일을 열 수 없어요", "open_with": "다른 앱으로 열기…", "preview_too_large": "파일이 너무 커서 미리볼 수 없어요.", - "selected_count": "{count, plural, other {#개 선택됨}}", "share_add_people": "사용자 추가", "share_empty": "아직 아무와도 공유하지 않았어요", "share_error": "공유 업데이트에 실패했어요", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "플러그인", - "themes": "테마", "updates": "업데이트" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "기본 라이트 및 다크 팔레트예요.", - "default_name": "Bulwark", - "mobile_description": "색상 테마를 선택하세요. 테마는 화면 설정의 라이트/다크 설정을 따르며, 사용자 지정 테마 패키지는 웹메일에서 관리해요.", - "title": "테마" + "mobile_description": "색상 테마를 선택하세요. 테마는 화면 설정의 라이트/다크 설정을 따르며, 사용자 지정 테마 패키지는 웹메일에서 관리해요." }, "vacation": { "warnings": { diff --git a/locales/rn/lv.json b/locales/rn/lv.json index 27189a1b..5ba26822 100644 --- a/locales/rn/lv.json +++ b/locales/rn/lv.json @@ -445,7 +445,6 @@ "open_error": "Neizdevās atvērt failu", "open_with": "Atvērt ar…", "preview_too_large": "Šis fails ir pārāk liels priekšskatīšanai.", - "selected_count": "{count, plural, zero {Atlasīti #} one {Atlasīts #} other {Atlasīti #}}", "share_add_people": "Pievienot personas", "share_empty": "Vēl nav kopīgots ne ar vienu", "share_error": "Neizdevās atjaunināt kopīgošanu", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Spraudņi", - "themes": "Tēmas", "updates": "Atjauninājumi" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Noklusējuma gaišās un tumšās paletes.", - "default_name": "Bulwark", - "mobile_description": "Izvēlieties krāsu tēmu. Tēmas seko gaišā/tumšā režīma iestatījumam sadaļā Izskats; pielāgotas tēmu pakotnes tiek pārvaldītas tīmekļa pastā.", - "title": "Tēmas" + "mobile_description": "Izvēlieties krāsu tēmu. Tēmas seko gaišā/tumšā režīma iestatījumam sadaļā Izskats; pielāgotas tēmu pakotnes tiek pārvaldītas tīmekļa pastā." }, "vacation": { "warnings": { diff --git a/locales/rn/mn.json b/locales/rn/mn.json index c808bb5a..0cc569f5 100644 --- a/locales/rn/mn.json +++ b/locales/rn/mn.json @@ -445,7 +445,6 @@ "open_error": "Файлыг нээж чадсангүй", "open_with": "Өөр аппаар нээх…", "preview_too_large": "Энэ файл урьдчилан харахад хэт том байна.", - "selected_count": "{count, plural, one {Сонгосон: #} other {Сонгосон: #}}", "share_add_people": "Хүн нэмэх", "share_empty": "Одоохондоо хэнтэй ч хуваалцаагүй", "share_error": "Хуваалцалтыг шинэчилж чадсангүй", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Плагинууд", - "themes": "Өнгөний загварууд", "updates": "Шинэчлэлт" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Өгөгдмөл цайвар ба харанхуй палитр.", - "default_name": "Bulwark", - "mobile_description": "Өнгөний загвар сонгоно уу. Өнгөний загварууд Гадаад төрх хэсгийн цайвар/харанхуй тохиргоог дагана; өөрийн загварын багцуудыг вэбмэйлд удирдана.", - "title": "Өнгөний загварууд" + "mobile_description": "Өнгөний загвар сонгоно уу. Өнгөний загварууд Гадаад төрх хэсгийн цайвар/харанхуй тохиргоог дагана; өөрийн загварын багцуудыг вэбмэйлд удирдана." }, "vacation": { "warnings": { diff --git a/locales/rn/nb.json b/locales/rn/nb.json index 169a55b6..1269a31f 100644 --- a/locales/rn/nb.json +++ b/locales/rn/nb.json @@ -445,7 +445,6 @@ "open_error": "Kunne ikke åpne filen", "open_with": "Åpne med…", "preview_too_large": "Denne filen er for stor til å forhåndsvises.", - "selected_count": "{count, plural, one {# valgt} other {# valgte}}", "share_add_people": "Legg til personer", "share_empty": "Ikke delt med noen ennå", "share_error": "Kunne ikke oppdatere deling", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Programtillegg", - "themes": "Temaer", "updates": "Oppdateringer" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Standardpalettene for lyst og mørkt.", - "default_name": "Bulwark", - "mobile_description": "Velg et fargetema. Temaene følger innstillingen for lyst/mørkt under Utseende; egendefinerte temapakker administreres i nettposten.", - "title": "Temaer" + "mobile_description": "Velg et fargetema. Temaene følger innstillingen for lyst/mørkt under Utseende; egendefinerte temapakker administreres i nettposten." }, "vacation": { "warnings": { diff --git a/locales/rn/nl.json b/locales/rn/nl.json index 6b468c43..7b160aee 100644 --- a/locales/rn/nl.json +++ b/locales/rn/nl.json @@ -445,7 +445,6 @@ "open_error": "Kon het bestand niet openen", "open_with": "Openen met…", "preview_too_large": "Dit bestand is te groot voor een voorbeeld.", - "selected_count": "{count, plural, one {# geselecteerd} other {# geselecteerd}}", "share_add_people": "Personen toevoegen", "share_empty": "Nog met niemand gedeeld", "share_error": "Delen kon niet worden bijgewerkt", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Plug-ins", - "themes": "Thema's", "updates": "Updates" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "De standaard lichte en donkere paletten.", - "default_name": "Bulwark", - "mobile_description": "Kies een kleurthema. Thema's volgen de instelling licht/donker onder Uiterlijk; aangepaste themapakketten worden beheerd in de webmail.", - "title": "Thema's" + "mobile_description": "Kies een kleurthema. Thema's volgen de instelling licht/donker onder Uiterlijk; aangepaste themapakketten worden beheerd in de webmail." }, "vacation": { "warnings": { diff --git a/locales/rn/pl.json b/locales/rn/pl.json index b57829c4..ac872e03 100644 --- a/locales/rn/pl.json +++ b/locales/rn/pl.json @@ -445,7 +445,6 @@ "open_error": "Nie udało się otworzyć pliku", "open_with": "Otwórz za pomocą…", "preview_too_large": "Ten plik jest zbyt duży, aby wyświetlić podgląd.", - "selected_count": "{count, plural, one {# zaznaczony} few {# zaznaczone} many {# zaznaczonych} other {# zaznaczonego}}", "share_add_people": "Dodaj osoby", "share_empty": "Jeszcze nikomu nie udostępniono", "share_error": "Nie udało się zaktualizować udostępniania", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Wtyczki", - "themes": "Motywy", "updates": "Aktualizacje" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Domyślne jasne i ciemne palety kolorów.", - "default_name": "Bulwark", - "mobile_description": "Wybierz motyw kolorystyczny. Motywy stosują ustawienie jasny/ciemny z sekcji Wygląd; własnymi pakietami motywów zarządza się w webmailu.", - "title": "Motywy" + "mobile_description": "Wybierz motyw kolorystyczny. Motywy stosują ustawienie jasny/ciemny z sekcji Wygląd; własnymi pakietami motywów zarządza się w webmailu." }, "vacation": { "warnings": { diff --git a/locales/rn/pt.json b/locales/rn/pt.json index 19acf556..0410191a 100644 --- a/locales/rn/pt.json +++ b/locales/rn/pt.json @@ -445,7 +445,6 @@ "open_error": "Não foi possível abrir o arquivo", "open_with": "Abrir com…", "preview_too_large": "Este arquivo é grande demais para pré-visualizar.", - "selected_count": "{count, plural, one {# selecionado} other {# selecionados}}", "share_add_people": "Adicionar pessoas", "share_empty": "Ainda não compartilhado com ninguém", "share_error": "Falha ao atualizar o compartilhamento", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Plugins", - "themes": "Temas", "updates": "Atualizações" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "As paletas clara e escura padrão.", - "default_name": "Bulwark", - "mobile_description": "Escolha um tema de cores. Os temas seguem a configuração clara/escura de Aparência; pacotes de temas personalizados são gerenciados no webmail.", - "title": "Temas" + "mobile_description": "Escolha um tema de cores. Os temas seguem a configuração clara/escura de Aparência; pacotes de temas personalizados são gerenciados no webmail." }, "vacation": { "warnings": { diff --git a/locales/rn/ro.json b/locales/rn/ro.json index 618e98d8..bb8df984 100644 --- a/locales/rn/ro.json +++ b/locales/rn/ro.json @@ -445,7 +445,6 @@ "open_error": "Fișierul nu a putut fi deschis", "open_with": "Deschideți cu…", "preview_too_large": "Acest fișier este prea mare pentru previzualizare.", - "selected_count": "{count, plural, one {# element selectat} few {# elemente selectate} other {# de elemente selectate}}", "share_add_people": "Adăugați persoane", "share_empty": "Nu a fost partajat încă cu nimeni", "share_error": "Nu s-a putut actualiza partajarea", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Pluginuri", - "themes": "Teme", "updates": "Actualizări" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Paletele implicite, luminoasă și întunecată.", - "default_name": "Bulwark", - "mobile_description": "Alegeți o temă de culori. Temele urmează setarea luminos/întunecat din Aspect; pachetele de teme personalizate se gestionează în webmail.", - "title": "Teme" + "mobile_description": "Alegeți o temă de culori. Temele urmează setarea luminos/întunecat din Aspect; pachetele de teme personalizate se gestionează în webmail." }, "vacation": { "warnings": { diff --git a/locales/rn/ru.json b/locales/rn/ru.json index 1bbd16d5..2d0f765e 100644 --- a/locales/rn/ru.json +++ b/locales/rn/ru.json @@ -445,7 +445,6 @@ "open_error": "Не удалось открыть файл", "open_with": "Открыть с помощью…", "preview_too_large": "Файл слишком большой для предпросмотра.", - "selected_count": "{count, plural, one {Выбрано: #} few {Выбрано: #} many {Выбрано: #} other {Выбрано: #}}", "share_add_people": "Добавить людей", "share_empty": "Пока никому не предоставлен доступ", "share_error": "Не удалось обновить общий доступ", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Плагины", - "themes": "Темы", "updates": "Обновления" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Стандартные светлая и тёмная палитры.", - "default_name": "Bulwark", - "mobile_description": "Выберите цветовую тему. Темы учитывают светлый или тёмный режим из раздела «Внешний вид»; собственными пакетами тем можно управлять в веб-почте.", - "title": "Темы" + "mobile_description": "Выберите цветовую тему. Темы учитывают светлый или тёмный режим из раздела «Внешний вид»; собственными пакетами тем можно управлять в веб-почте." }, "vacation": { "warnings": { diff --git a/locales/rn/sk.json b/locales/rn/sk.json index 60df6ac0..2e090460 100644 --- a/locales/rn/sk.json +++ b/locales/rn/sk.json @@ -445,7 +445,6 @@ "open_error": "Súbor sa nepodarilo otvoriť", "open_with": "Otvoriť pomocou…", "preview_too_large": "Tento súbor je príliš veľký na náhľad.", - "selected_count": "{count, plural, one {# vybraná} few {# vybrané} many {# vybranej} other {# vybraných}}", "share_add_people": "Pridať osoby", "share_empty": "Zatiaľ nikomu nezdieľané", "share_error": "Aktualizácia zdieľania zlyhala", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Pluginy", - "themes": "Témy", "updates": "Aktualizácie" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Predvolené svetlé a tmavé palety.", - "default_name": "Bulwark", - "mobile_description": "Vyberte farebnú tému. Témy sa riadia nastavením svetlý/tmavý vo Vzhľade; vlastné balíky tém sa spravujú vo webmaile.", - "title": "Témy" + "mobile_description": "Vyberte farebnú tému. Témy sa riadia nastavením svetlý/tmavý vo Vzhľade; vlastné balíky tém sa spravujú vo webmaile." }, "vacation": { "warnings": { diff --git a/locales/rn/tr.json b/locales/rn/tr.json index deae15a7..e067d091 100644 --- a/locales/rn/tr.json +++ b/locales/rn/tr.json @@ -445,7 +445,6 @@ "open_error": "Dosya açılamadı", "open_with": "Birlikte aç…", "preview_too_large": "Bu dosya, önizleme için çok büyük.", - "selected_count": "{count, plural, one {# seçildi} other {# seçildi}}", "share_add_people": "Kişi ekle", "share_empty": "Henüz kimseyle paylaşılmadı", "share_error": "Paylaşım güncellenemedi", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Eklentiler", - "themes": "Temalar", "updates": "Güncellemeler" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Varsayılan açık ve koyu paletler.", - "default_name": "Bulwark", - "mobile_description": "Bir renk teması seçin. Temalar Görünüm'deki açık/koyu ayarını izler; özel tema paketleri webmail'de yönetilir.", - "title": "Temalar" + "mobile_description": "Bir renk teması seçin. Temalar Görünüm'deki açık/koyu ayarını izler; özel tema paketleri webmail'de yönetilir." }, "vacation": { "warnings": { diff --git a/locales/rn/uk.json b/locales/rn/uk.json index e3150d88..1832f100 100644 --- a/locales/rn/uk.json +++ b/locales/rn/uk.json @@ -445,7 +445,6 @@ "open_error": "Не вдалося відкрити файл", "open_with": "Відкрити за допомогою…", "preview_too_large": "Файл завеликий для попереднього перегляду.", - "selected_count": "{count, plural, one {Вибрано: #} few {Вибрано: #} many {Вибрано: #} other {Вибрано: #}}", "share_add_people": "Додати людей", "share_empty": "Поки що ні з ким не поділено", "share_error": "Не вдалося оновити спільний доступ", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "Плагіни", - "themes": "Теми", "updates": "Оновлення" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "Стандартні світла й темна палітри.", - "default_name": "Bulwark", - "mobile_description": "Виберіть кольорову тему. Теми враховують світлий або темний режим із розділу «Зовнішній вигляд»; власними пакетами тем можна керувати у веб-пошті.", - "title": "Теми" + "mobile_description": "Виберіть кольорову тему. Теми враховують світлий або темний режим із розділу «Зовнішній вигляд»; власними пакетами тем можна керувати у веб-пошті." }, "vacation": { "warnings": { diff --git a/locales/rn/zh-TW.json b/locales/rn/zh-TW.json index dea23a13..84371e0e 100644 --- a/locales/rn/zh-TW.json +++ b/locales/rn/zh-TW.json @@ -445,7 +445,6 @@ "open_error": "無法開啟檔案", "open_with": "以其他應用程式開啟…", "preview_too_large": "此檔案太大,無法預覽。", - "selected_count": "{count, plural, other {已選取 # 項}}", "share_add_people": "新增使用者", "share_empty": "尚未與任何人分享", "share_error": "無法更新分享設定", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "外掛程式", - "themes": "主題", "updates": "更新" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "預設的淺色與深色配色。", - "default_name": "Bulwark", - "mobile_description": "選擇色彩主題。主題會依照「外觀」中的淺色/深色設定;自訂主題套件請在網頁郵件中管理。", - "title": "主題" + "mobile_description": "選擇色彩主題。主題會依照「外觀」中的淺色/深色設定;自訂主題套件請在網頁郵件中管理。" }, "vacation": { "warnings": { diff --git a/locales/rn/zh.json b/locales/rn/zh.json index 71758718..79981256 100644 --- a/locales/rn/zh.json +++ b/locales/rn/zh.json @@ -445,7 +445,6 @@ "open_error": "无法打开文件", "open_with": "打开方式…", "preview_too_large": "此文件过大,无法预览。", - "selected_count": "{count, plural, other {已选择 # 项}}", "share_add_people": "添加用户", "share_empty": "尚未与任何人共享", "share_error": "更新共享失败", @@ -1102,7 +1101,6 @@ }, "tabs": { "plugins": "插件", - "themes": "主题", "updates": "更新" }, "templates": { @@ -1119,9 +1117,7 @@ }, "themes": { "default_description": "默认的浅色和深色配色。", - "default_name": "Bulwark", - "mobile_description": "选择颜色主题。主题会跟随“外观”中的浅色/深色设置;自定义主题包需在网页邮箱中管理。", - "title": "主题" + "mobile_description": "选择颜色主题。主题会跟随“外观”中的浅色/深色设置;自定义主题包需在网页邮箱中管理。" }, "vacation": { "warnings": { diff --git a/locales/ro/common.json b/locales/ro/common.json index ced8ef7f..3fed4915 100644 --- a/locales/ro/common.json +++ b/locales/ro/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Client de webmail minimalist care utilizează protocolul JMAP", - "login": { - "title": "Webmail", - "username_label": "E-mail", - "username_placeholder": "user@example.com", - "password_label": "Parolă", - "password_placeholder": "Introduceți parola", - "jmap_endpoint_label": "JMAP Server", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Serverul trebuie să permită cererile CORS provenite de la acest domeniu.", - "jmap_server_label": "Server", - "jmap_server_auto_picked": "Server selectat din domeniul dvs. de e-mail.", - "sign_in": "Autentificare", - "signing_in": "Autentificare în curs...", - "loading": "Se încarcă...", - "reconnecting": "Conexiune pierdută. Se încearcă reconectarea…", - "error": { - "invalid_credentials": "Adresă de e-mail sau parolă invalidă. Vă rugăm să verificați datele de autentificare și să încercați din nou.", - "connection_failed": "Nu se poate stabili conexiunea cu serverul. Verificați conexiunea la internet și încercați din nou.", - "cors_blocked": "Serverul este accesibil, dar blochează cererile de tip „cross-origin”. Verificați setările CORS ale serverului JMAP și permiteți acest domeniu.", - "server_error": "Serverul este temporar indisponibil. Vă rugăm să încercați din nou mai târziu.", - "generic": "A apărut o eroare neașteptată. Dacă problema persistă, contactați administratorul.", - "totp_required": "Este necesar un cod de autentificare în doi pași. Vă rugăm să introduceți codul mai jos.", - "totp_invalid": "Cod de autentificare invalid. Vă rugăm să verificați aplicația de autentificare și să încercați din nou.", - "oauth_discovery_failed": "SSO este activată, dar furnizorul de identitate nu a putut fi contactat. Verificați configurația OAuth." - }, - "show_password": "Afișează parola", - "hide_password": "Ascunde parola", - "totp_toggle": "Am un cod 2FA", - "remember_me": "Reține-mă", - "config_error": { - "title": "Eroare de configurare", - "fetch_failed": "Nu se poate încărca configurația aplicației. Vă rugăm să încercați din nou mai târziu.", - "server_not_configured": "Serverul de e-mail nu a fost configurat. Vă rugăm să contactați administratorul." - }, - "remove_from_history": "Elimină din istoric", - "totp_label": "Cod de autentificare", - "totp_placeholder": "000000", - "session_expired": "Sesiunea dvs. a expirat. Vă rugăm să vă autentificați din nou.", - "dismiss": "Ignoră", - "or": "sau", - "sign_in_sso": "Conectați-vă cu SSO", - "add_account_title": "Adăugare cont", - "add_account_subtitle": "Conectați-vă cu un alt cont", - "cancel": "Anulează", - "website": "Site web", - "imprint": "Mențiuni legale", - "privacy_policy": "Politica de confidențialitate", - "try_demo": "Încercați demo-ul", - "demo_description": "Explorați cu date de exemplu – nu este necesar un cont", - "demo_launching": "Se lansează demonstrația...", - "demo_login_button": "Lansați demonstrația", - "demo_tagline": "Descoperiți un client de e-mail cu funcționalități complete. Nu este necesar un cont.", - "demo_no_signup": "Nu este necesară înregistrarea – explorați liber cu date de exemplu", - "oauth_completing": "Se finalizează autentificarea...", - "oauth_error": { - "title": "Autentificare eșuată", - "invalid_state": "Validarea de securitate a eșuat. Vă rugăm să încercați să vă autentificați din nou.", - "missing_params": "Lipsesc datele de autentificare. Vă rugăm să încercați să vă autentificați din nou.", - "token_exchange_failed": "Autentificarea nu a reușit. Vă rugăm să încercați din nou.", - "access_denied": "Accesul a fost refuzat. Vă rugăm să contactați administratorul.", - "back_to_login": "Înapoi la autentificare" - } - }, - "sidebar": { - "close": "Închide", - "compose": "Compune", - "compose_hint": "Compune (c)", - "search_placeholder": "Căutare în e-mail...", - "search_placeholder_hint": "Căutare în e-mail... (apăsați /)", - "storage": "Stocare", - "storage_used": "Utilizat", - "storage_free": "Gratuit", - "storage_total": "Total", - "sign_out": "Deconectare", - "sign_out_of": "Deconectați-vă de pe {account}", - "sign_out_all": "Deconectați-vă de la toate conturile", - "add_account": "Adăugare cont", - "set_as_default": "Setați ca implicită", - "switch_account": "Schimbă contul", - "contacts": "Contacte", - "calendar": "Calendar", - "settings": "Setări", - "admin": "Admin", - "files": "Fișiere", - "loading_mailboxes": "Se încarcă căsuțele poștale...", - "push_connected": "Actualizări în timp real active", - "push_disconnected": "Actualizări în timp real inactive", - "keyboard_shortcuts": "Comenzi rapide de la tastatură", - "theme": { - "light": "Modul luminos", - "dark": "Modul întunecat", - "system": "Tema sistemului" - }, - "language": { - "title": "Limba" - }, - "mailboxes": { - "inbox": "Primite", - "sent": "Trimise", - "drafts": "Ciorne", - "trash": "Coș de gunoi", - "archive": "Arhivă", - "starred": "Cu stea", - "all_mail": "Toate mesajele", - "spam": "Spam", - "important": "Important" - }, - "unified_inbox": "Căsuță de intrare unificată", - "unified_sent": "Toate mesajele trimise", - "unified_drafts": "Toate ciorne", - "unified_trash": "Toate coșul de gunoi", - "unified_archive": "Arhivă completă", - "unified_junk": "Toate mesajele nedorite", - "all_accounts": "Toate conturile", - "unified_mailbox": "Căsuță poștală unificată", - "expand": "Extindeți", - "collapse": "Reduceți", - "expand_tooltip": "Extindeți", - "collapse_tooltip": "Reduceți", - "mobile": { - "search": "Căutare", - "compose": "Compune", - "go_back": "Înapoi", - "toggle_menu": "Comută meniul" - }, - "clear_search": "Șterge căutarea", - "vacation_active": "Răspunsul automat de vacanță este activ", - "demo_banner": "Modul demo", - "demo_reset": "Resetare", - "demo_tour": "Tur de prezentare", - "tags": "Etichete", - "show_all_tags": "Afișează tot ({count})", - "show_fewer_tags": "Afișează mai puține", - "folders": "Dosare", - "shared": "Partajat", - "mail": "E-mail", - "nav_label": "Navigare", - "add_app": "Aplicații", - "scheduled": "Programat", - "unified_all_unread": "Toate necitite", - "unified_all_starred": "Toate cu stea", - "unified_all_mail": "Toată poșta", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Aplicații implicite", - "description": "Alegeți dacă linkurile către e-mail și calendar se deschid în Bulwark. Din punct de vedere tehnic, Bulwark se înregistrează ca gestionar de protocol pentru linkurile de tip mailto: și webcal:.", - "unsupported": "Acest browser sau această conexiune nu acceptă înregistrarea manuală a gestionarului de protocoale. Este posibil să puteți utiliza totuși aplicația PWA instalată prin setările browserului sau ale sistemului de operare.", - "mailto_label": "Linkuri din e-mail", - "mailto_description": "Deschideți linkurile „mailto:” în Bulwark cu o fereastră de redactare precompletată.", - "protocol_open_mode_label": "La deschiderea linkurilor de protocol", - "protocol_open_mode_description": "Alegeți dacă Bulwark deschide linkurile mailto: și webcal: într-o filă nouă sau reutilizează o sesiune deschisă. Opțiunea „sesiune activă” necesită permisiunea de notificare, astfel încât să puteți face clic pe o notificare de rezervă pentru a aduce Bulwark în prim-plan dacă browserul blochează focalizarea.", - "protocol_open_mode_active_session": "Deschideți într-o sesiune activă, dacă este posibil", - "protocol_open_mode_new_tab": "Deschideți întotdeauna o filă nouă", - "focus_notification_title": "Deschideți Bulwark", - "focus_notification_body": "Linkul a fost deschis în Bulwark. Faceți clic pentru a aduce fereastra în prim-plan.", - "webcal_label": "Linkuri către calendar", - "webcal_description": "Deschideți linkurile webcal: din Bulwark cu o fereastră de dialog de abonare la calendar completată în prealabil.", - "register_mailto": "Înregistrați aplicația de e-mail", - "register_webcal": "Înregistrați aplicația de calendar", - "mailto_registered": "S-a solicitat înregistrarea unui gestionar de e-mail", - "webcal_registered": "S-a solicitat înregistrarea unui handler pentru calendar", - "registration_failed": "Înregistrarea gestionarului de protocol a eșuat", - "opening_mailto": "Se deschide fereastra de redactare...", - "opening_webcal": "Se deschide calendarul...", - "browser_note": "Este posibil ca browserul sau sistemul de operare să vă solicite confirmarea acestei acțiuni și să necesite instalareBulwarkului înainte de a putea fi selectat ca aplicație implicită.", - "select_account_title": "Alegeți contul", - "select_mailto_account": "Alegeți contul în care să se deschidă acest link de e-mail.", - "select_webcal_account": "Alegeți contul care ar trebui să deschidă acest link către calendar.", - "select_account_note": "Aceasta se aplică numai acestui link de protocol.", - "detail_to": "Către", - "detail_subject": "Subiect", - "detail_no_subject": "Fără subiect", - "detail_calendar": "Calendar", - "detail_source": "Sursă", - "active_account": "Activ", - "switching_account": "Se schimbă contul..." - }, - "sidebar_apps": { - "modal_title": "Aplicații din bara laterală", - "add_new": "Adăugați aplicația", - "edit_app": "Editați aplicația", - "name_label": "Nume", - "name_placeholder": "Aplicația mea", - "name_required": "Numele este obligatoriu", - "url_label": "URL", - "url_required": "URL este obligatoriu", - "url_invalid": "Introduceți o adresă URL validă de tip http sau https", - "icon_label": "Pictogramă", - "icon_required": "Pictograma este obligatorie", - "open_mode_label": "Modul Deschis", - "open_new_tab": "Filă nouă", - "open_inline": "În text", - "cancel": "Anulează", - "add": "Adăugați", - "update": "Actualizare", - "delete": "Șterge", - "delete_confirm_title": "Șterge aplicația", - "delete_confirm": "Sunteți sigur că doriți să ștergeți „{name}”?", - "no_apps": "Nu s-au adăugat încă aplicații", - "no_apps_hint": "Adăugați aplicații personalizate și linkuri în bara laterală", - "managed_title": "Furnizate de administrator", - "managed_description": "Aceste aplicații sunt configurate pentru toți și nu pot fi modificate.", - "managed_badge": "Gestionată", - "search_icons": "Pictograme de căutare...", - "show_popular": "Popular", - "show_all": "Toate", - "no_icons_found": "Nu s-au găsit pictograme", - "inline_badge": "În text", - "tab_badge": "Tab", - "show_on_mobile": "Afișare pe mobil" - }, - "email_list": { - "no_emails": "Nu s-au găsit mesaje", - "no_emails_description": "Această căsuță poștală este goală", - "no_search_results": "Nu s-au găsit rezultate", - "no_search_results_description": "Încercați să modificați criteriile de căutare sau filtrele", - "loading": "Se încarcă e-mailurile...", - "unread": "necitit", - "to_me": "Pentru mine", - "to_recipients": "Către destinatarii {count}", - "and_others": "și {count} altele", - "draft": "Ciorne", - "starred": "Cu stea", - "pinned": "Fixat", - "replied": "Răspuns", - "forwarded": "Redirecționat", - "has_attachment": "Cu atașament", - "conversations_count": "{count} din conversațiile de pe {total}", - "conversations_count_plus": "{count}+ conversations", - "conversations_count_simple": "{count} conversații", - "no_conversations": "Fără conversații", - "loading_more": "Se încarcă mai multe e-mailuri...", - "no_more_emails": "Nu mai sunt e-mailuri de încărcat", - "batch_actions": { - "select": "Selectați e-mailurile", - "select_all": "Selectează tot", - "selected_messages": "{count, plural, one {1 e-mail} few {# e-mailuri} other {# de e-mailuri}} selectat", - "mark_read": "Marcați ca citit", - "mark_unread": "Marcați ca necitit", - "delete": "Șterge", - "delete_confirm_title": "Ștergeți e-mailurile", - "delete_confirm_message": "Sunteți sigur că doriți să ștergeți {count, plural, one {1 e-mail} few {# e-mailuri} other {# de e-mailuri}}?", - "clear_selection": "Șterge selecția" - }, - "permanent_delete": "Șterge definitiv", - "permanent_delete_confirm_title": "Șterge definitiv", - "permanent_delete_confirm_message": "Acest e-mail va fi șters definitiv. Această acțiune nu poate fi anulată.", - "permanent_delete_confirm_batch_message": "Acestea {count, plural, one {1 e-mail} few {# e-mailuri} other {# de e-mailuri}} va fi șters definitiv. Această acțiune nu poate fi anulată.", - "empty_folder": { - "button": "Folder gol", - "confirm_title": "Folder gol", - "confirm_message": "Toate e-mailurile din acest dosar vor fi șterse definitiv. Această acțiune nu poate fi anulată.", - "confirm_button": "Folder gol", - "junk_hint": "Puteți goli folderul „Junk” pentru a șterge definitiv toate mesajele.", - "trash_hint": "Puteți goli folderul Coș de gunoi pentru a șterge definitiv toate mesajele." - }, - "no_scheduled_emails": "Fără e-mailuri programate", - "no_scheduled_emails_description": "Mesajele programate pentru mai târziu vor apărea aici.", - "scheduled_count": "{count, plural, one {1 e-mail programat} few {# e-mailuri programate} other {# de e-mailuri programate}}", - "scheduled_actions_hint": "Mesajele programate pot fi anulate, reprogramate sau editate din lista acțiunilor programate.", - "cancel_scheduled_send": "Anulează trimiterea", - "reschedule_send": "Reprogramare", - "cancel_and_edit": "Anulează și editează", - "cancel_and_compose_again": "Anulează și redactează din nou", - "reschedule_prompt": "Introduceți o nouă dată/oră, de exemplu 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Expeditorul a solicitat o confirmare de citire:", - "send": "Trimite confirmare de primire", - "ignore": "Ignoră", - "sent": "Confirmare de citire trimisă.", - "send_failed": "Nu s-a putut trimite confirmarea de citire", - "mdn_subject": "Citiți: {subject}", - "mdn_body": "Aceasta este o confirmare de primire pentru mesajul pe care l-ați trimis la {recipient}.\n\nNotă: Această confirmare de primire atestă doar faptul că mesajul a fost afișat pe computerul destinatarului. Nu există nicio garanție că destinatarul a citit sau a înțeles conținutul mesajului." - }, - "no_email_selected": "Nu a fost selectat niciun e-mail", - "no_email_description": "Selectați un e-mail din listă pentru a-l vizualiza aici", - "no_conversation_selected": "Nu este selectată nicio conversație", - "no_conversation_description": "Alegeți o conversație din listă pentru a o citi aici", - "compose": "Compune", - "compose_hint": "Redactați un mesaj nou", - "no_subject": "(Fără subiect)", - "no_body_content": "(Nu există conținut disponibil)", - "no_preview_available": "Nu este disponibilă previzualizarea", - "show_quoted_text": "Afișează textul citat", - "hide_quoted_text": "Ascunde textul citat", - "loading_email": "Se încarcă e-mailul...", - "loading": "Se încarcă...", - "reply": "Răspunde", - "reply_all": "Răspunde tuturor", - "forward": "Redirecționează", - "delete": "Șterge", - "archive": "Arhivează", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Stea", - "unstar": "Anulează marcarea cu stea", - "mark_unread": "Marcați ca necitit", - "mark_read": "Marcați ca citit", - "unread": "Necitit", - "read": "Citiți", - "spam_short": "Spam", - "not_spam_short": "Nu este spam", - "move": "Mutare", - "print": "Imprimare", - "view_source": "Vizualizați sursa", - "fullscreen": "Ecran complet", - "exit_fullscreen": "Ieșire din ecran complet", - "export_email": "Exportați ca fișier .eml", - "forward_as_attachment": "Redirecționează ca atașament", - "import_email": "Importați fișiere .eml sau .zip", - "keyboard_shortcuts": "Comenzi rapide de la tastatură (?)", - "email_source": "Sursa e-mailului", - "draft_banner": "Acest mesaj este o schiță", - "edit_draft": "Editare", - "copy_source": "Copiați în clipboard", - "source_copied": "Sursă copiată în clipboard", - "attachments": "Anexe", - "important": "Important", - "download": "Descărcați", - "download_all": "Descărcați tot", - "from": "De la", - "to": "Către", - "cc": "CC", - "bcc": "BCC", - "date": "Data", - "subject": "Subiect", - "show_details": "Afișați detalii", - "hide_details": "Ascunde detaliile", - "external_content_warning": "Imaginile și conținutul extern au fost blocate", - "load_external_content": "Încărcați imagini", - "trust_sender": "Aveți întotdeauna încredere în acest expeditor", - "back_to_list": "Înapoi la listă", - "view_contact": "Vizualizare contact", - "message_details": "Detalii mesaj", - "more_reply_options": "Mai multe opțiuni de răspuns", - "set_tag": "Setați eticheta", - "tag": "Etichetă", - "more_actions": "Alte acțiuni", - "previous": "Anterior", - "next": "Următorul", - "move_to": "Mergi la...", - "remove_tag": "Eliminați eticheta", - "tag_filter_placeholder": "Filtrează etichetele", - "tag_no_matches": "Nicio etichetă corespunzătoare", - "more_count": "+{count} mai multe", - "characters_count": "{count} caractere", - "quick_reply_placeholder": "Scrie un răspuns rapid...", - "more_options": "Mai multe opțiuni", - "sending": "Se trimite...", - "security_authentication": "Securitate și autentificare", - "technical_details": "Detalii tehnice", - "message_id_label": "IDul mesajului:", - "reply_to_label": "Răspunde la:", - "delivery_time_label": "Termen de livrare:", - "conversation_part_label": "Parte a conversației:", - "previous_messages": "{count} mesajul anterior", - "previous_messages_plural": "{count} mesaje anterioare", - "time": { - "day": "zi", - "days": "zile", - "hour": "oră", - "hours": "ore", - "minute": "minut", - "minutes": "minute" - }, - "unknown_sender": "Necunoscut", - "recipient_me": "eu", - "recipient_and_others": "{name} și {count} altele", - "recipient_to_prefix": "Către:", - "authentication": { - "title": "Autentificare", - "status": { - "verified": "Verificat", - "warning": "Avertisment", - "none": "Nu sunteți autentificat" - }, - "spf": { - "pass": "SPF Admis", - "fail": "SPF Eșec", - "none": "Fără „SPF”" - }, - "dkim": { - "pass": "DKIM Valid", - "fail": "DKIM Invalid", - "none": "Fără „DKIM”" - }, - "dmarc": { - "pass": "DMARC Admis", - "fail": "DMARC Eșec", - "none": "Fără „DMARC”" - }, - "spam_score": "Scorul de spam", - "tooltip_spf": "Sender Policy Framework: verifică dacă serverul expeditor este autorizat să trimită e-mailuri în numele domeniului", - "tooltip_dkim": "DomainKeys Identified Mail: confirmă faptul că e-mailul nu a fost modificat în timpul transmiterii, folosind o semnătură criptografică", - "tooltip_dmarc": "Autentificarea, raportarea și conformitatea mesajelor bazate pe domeniu (DMARC): Asigură că adresele SPF și DKIM corespund domeniului expeditorului și stabilește o politică pentru eșecuri", - "policy": "Politică", - "result": { - "pass": "Admis", - "fail": "Eșec", - "softfail": "Eșec ușor", - "neutral": "Neutru", - "permerror": "Eroare permanentă", - "temperror": "Eroare temporară", - "none": "Niciunul" - } - }, - "details": { - "recipients_routing": "Destinatari și rutare", - "authentication_security": "Autentificare și securitate", - "identifiers_threading": "Identificatori și organizarea în firuri de discuție", - "mailing_list": "Listă de distribuție", - "message_properties": "Proprietățile mesajului", - "sent": "Trimis", - "received": "Primit", - "delivery_time": "Termen de livrare", - "in_reply_to": "In-Reply-To", - "references": "Referințe", - "thread_id": "ID de discuție", - "size": "Dimensiune", - "mime_type": "MIME tip", - "attachments_summary": "{count} fișiere · {size}", - "list_id": "Listă ID", - "list_help": "Ajutor pentru listă", - "list_post": "Postare în listă", - "list_unsubscribe": "Dezabonare", - "iprev": "DNS inversat", - "spam_status": "Starea spamului", - "ai_verdict": "Verdictul AI", - "account": "Cont", - "no_subject": "(fără subiect)" - }, - "headers": { - "routing": "Rutare", - "received": "Primit", - "message_id": "IDul mesajelor", - "list_info": "Informații despre listă" - }, - "tooltips": { - "reply": "Răspunde (r)", - "reply_all": "Răspunde tuturor (a)", - "forward": "Redirecționează (f)", - "archive": "Arhivare (e)", - "delete": "Ștergere (# sau Del)", - "star": "Stea (stele)", - "unstar": "Dezactivează marcajul (marcajele)", - "compose": "Compune (c)", - "previous": "E-mailul anterior", - "next": "Următorul e-mail", - "edit_draft": "Editează schița" - }, - "spam": { - "button_title": "Raportează spamul", - "not_spam_title": "Marcați ca legitim", - "toast_success": "Mutate în folderul „Junk”", - "toast_batch": "{count} e-mailuri mutate în folderul „Junk”", - "toast_undo": "Anulează", - "toast_not_spam_success": "Mutată în Inbox", - "toast_not_spam_batch": "{count} e-mailuri mutate în Inbox", - "error": "Nu s-a reușit raportarea spamului", - "error_not_spam": "Nu s-a putut restaura e-mailul" - }, - "unsubscribe_banner": { - "label": "Buletin informativ", - "button": "Dezabonare", - "confirm_title": "Doriți să vă dezabonați de la acest expeditor?", - "confirm_button": "Confirmare", - "cancel": "Anulează", - "success_http": "Pagina de dezabonare se deschide într-o filă nouă", - "success_mailto": "E-mailul de dezabonare a fost trimis", - "confirm_message_http": "Pagina de dezabonare se va deschide într-o filă nouă.", - "confirm_message_mailto": "Un e-mail de dezabonare va fi trimis expeditorului.", - "error": "Nu se poate dezabona", - "dismiss": "Ignoră" - }, - "calendar_invitation": { - "loading": "Se încarcă detaliile evenimentului…", - "title": "Invitație în calendar", - "published_title": "Eveniment publicat", - "response_title": "Răspuns la evenimente", - "update_title": "Actualizare eveniment", - "counter_title": "Contra-propunere", - "refresh_title": "Cerere de reîmprospătare", - "declined_counter_title": "Contrapropunere respinsă", - "cancelled_title": "Eveniment anulat", - "organizer": "Organizat de {name}", - "organizer_label": "Organizat după", - "attendees": "{count, plural, one {# participant} few {# participanți} other {# de participanți}}", - "accept": "Accept", - "maybe": "Poate", - "decline": "Declinare", - "add_to_calendar": "Adaugă în calendar", - "added": "Adăugat în calendar", - "rsvp_sent": "Răspuns trimis", - "parse_error": "Nu s-a putut citi invitația", - "action_failed": "Nu s-a putut finaliza acea acțiune din calendar.", - "no_calendar": "Calendarul nu este disponibil", - "published_info": "Acest eveniment a fost distribuit cu titlu informativ.", - "response_info": "Acest mesaj conține răspunsul unui participant.", - "response_info_organizer": "Răspunsul acestui participant actualizează evenimentul dumneavoastră.", - "update_info": "Acest mesaj actualizează un eveniment existent.", - "counter_info": "Acest mesaj propune modificări la un eveniment.", - "counter_info_organizer": "Acest participant a propus modificări la evenimentul dvs.", - "refresh_info": "Acest mesaj solicită detaliile celui mai recent eveniment.", - "refresh_info_organizer": "Un participant a solicitat detaliile cele mai recente despre eveniment.", - "declined_counter_info": "Organizatorul a respins o contrapropunere.", - "authentication_failed_info": "Verificările de autentificare a adresei de e-mail pentru această invitație au eșuat. Tratați cu precauție acțiunile din calendar.", - "authentication_missing_info": "Această invitație nu include autentificarea verificată a adresei de e-mail. Confirmați detaliile cu organizatorul dacă ceva pare neobișnuit.", - "sender_mismatch_info": "Această invitație a fost trimisă de la {sender}, în timp ce organizatorul menționat în datele din calendar este {organizer}.", - "sender_mismatch_unverified_info": "Această invitație a fost trimisă de la adresa {sender}, în timp ce organizatorul menționat în datele din calendar este {organizer}, iar mesajul nu a putut fi verificat.", - "organizer_role": "Tu organizezi acest eveniment", - "your_response": "Răspunsul dvs.: {status}", - "response_needed": "Necesită răspuns", - "response_accepted": "Acceptat", - "response_tentative": "Provizoriu", - "response_declined": "Respins", - "response_delegated": "Delegat", - "actor_sent_info": "Trimis de {name}.", - "actor_response_info": "{name} a răspuns {status}.", - "actor_counter_info": "{name} modificări propuse pentru acest eveniment.", - "actor_refresh_info": "{name} a solicitat detalii despre cel mai recent eveniment.", - "actor_declined_counter_info": "{name} a respins contrapropunerea.", - "actor_note": "Notă: {comment}", - "actor_unknown": "Cineva", - "proposed_changes": "Modificări propuse", - "change_title": "Titlu", - "change_time": "Ora", - "change_location": "Locație", - "change_description": "Descriere", - "change_empty": "Niciunul", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Aplicați modificările propuse", - "proposal_applied": "Modificările propuse au fost aplicate.", - "review_proposal": "Revizuirea propunerii", - "review_request": "Solicitare de revizuire", - "view_in_calendar": "Vizualizare în calendar", - "select_calendar": "Selectați calendarul", - "already_in_calendar": "Deja în calendarul dvs.", - "request_info": "Ați fost invitat la acest eveniment. Răspundeți pentru a-i comunica organizatorului disponibilitatea dumneavoastră.", - "cancel_info": "Organizatorul a anulat acest eveniment.", - "event_updated": "Actualizați # {sequence}", - "event_status_tentative": "Provizoriu", - "event_status_cancelled": "Anulat", - "expand": "Afișați detalii", - "collapse": "Ascunde detaliile" - }, - "send": "Trimite", - "more": "mai mult", - "scheduled_banner": "Programat pentru trimitere la {date}", - "scheduled_send_created": "E-mail programat pentru trimitere", - "cancel_scheduled_send": "Anulează trimiterea", - "reschedule_send": "Reprogramare", - "cancel_and_edit": "Anulează și editează", - "cancel_and_compose_again": "Anulează și redactează din nou", - "reschedule_prompt": "Introduceți o nouă dată/oră, de exemplu 2026-05-04T15:30", - "scheduled_actions_only": "Vizualizarea programată acceptă numai acțiuni programate", - "undo_send_scheduled": "Mesaj programat pentru trimitere", - "undo_send": "Anulează trimiterea", - "contact_sidebar": { - "title": "Contact", - "close": "Închide bara laterală", - "action_email": "E-mail", - "action_email_title": "Trimite e-mail", - "action_copy": "Copiere", - "action_copy_title": "Copiați e-mailul", - "action_edit_title": "Editează contactul", - "section_emails": "E-mailuri", - "section_phones": "Telefoane", - "section_organizations": "Organizații", - "section_addresses": "Adrese", - "section_notes": "Note", - "not_in_contacts": "Nu se află în lista dvs. de contacte", - "add_to_contacts": "Adaugă la contacte", - "copied": "Copiat!", - "copy_failed": "Copierea a eșuat" - }, - "send_now": "Trimite acum" - }, - "email_composer": { - "read_receipt_on": "Solicitare confirmare de citire (faceți clic pentru a dezactiva)", - "read_receipt_off": "Solicitați o confirmare de citire", - "dsn_on": "Notificare de livrare solicitată (clic pentru a dezactiva)", - "dsn_off": "Solicită o notificare de livrare", - "format_plain_text": "Comută la text simplu", - "format_rich_text": "Comută la text formatat (HTML)", - "require_tls_on": "Livrare criptată obligatorie (clic pentru a dezactiva)", - "require_tls_off": "Impune livrare criptată (TLS)", - "new_message": "Mesaj nou", - "signature_edit_hint": "Dublu clic pentru a edita semnătura", - "reply": "Răspunde", - "reply_all": "Răspunde tuturor", - "forward": "Redirecționează", - "reply_to": "Răspunde", - "reply_all_to": "Răspunde tuturor", - "forward_message": "Redirecționează", - "from": "De la", - "to": "Către", - "cc": "CC", - "bcc": "BCC", - "subject": "Subiect", - "body_placeholder": "Scrieți mesajul...", - "send": "Trimite", - "cancel": "Anulează", - "attach": "Atașați", - "attach_photos": "Fotografii și videoclipuri", - "attach_files": "Fișiere", - "discard": "Ignorați", - "discard_draft_title": "Doriți să ștergeți schița?", - "discard_draft_confirm": "Aveți modificări nesalvate. Doriți să renunțați la această schiță?", - "saving": "Se salvează...", - "sending": "Se trimite...", - "add_link": "Adăugați un link", - "link_url_prompt": "Accesați URL", - "draft_saved": "Ciorna a fost salvată", - "save_failed": "Nu s-a putut salva", - "to_placeholder": "Adresele de e-mail ale destinatarilor", - "cc_placeholder": "Destinatari în Cc", - "bcc_placeholder": "Destinatari Bcc", - "subject_placeholder": "Subiect", - "cc_label": "Cc:", - "bcc_label": "Bcc:", - "subject_label": "Subiect:", - "file_size_kb": "KB", - "prefix": { - "forward": "Redirecționare:", - "reply": "Re:" - }, - "no_subject": "(Fără subiect)", - "unknown_sender": "Necunoscut", - "quote": { - "reply_header": "Pe {date}, {sender} a scris:", - "forward_header": "---------- Mesaj redirecționat ----------", - "from": "De la: {sender}", - "date": "Data: {date}", - "subject": "Subiect: {subject}", - "to": "Către: {recipients}" - }, - "remove_sub_address": "Eliminați subadresa", - "from_override": { - "toggle_off": "Suprascrieți", - "toggle_on": "Anulare suprascriere", - "toggle_tooltip": "Puteți modifica liber numele și adresa din câmpul „De la”. E-mailul este trimis în continuare sub identitatea dvs.", - "name_label": "Numele expeditorului", - "name_placeholder": "Nume", - "email_label": "De la adresa de e-mail", - "email_placeholder": "alias@example.com", - "envelope_notice": "Dacă serverul dvs. nu acceptă această adresă ca expeditor al plicului, se folosește în schimb {identity}, iar destinatarii o pot vedea în antetul Return-Path." - }, - "use_template": "Șablon", - "save_as_template": "Salvează ca șablon", - "validation": { - "recipient_required": "Adăugați un destinatar căruia să îi trimiteți mesajul", - "body_required": "Scrieți un mesaj sau atașați un fișier", - "attachments_uploading": "Atașamentele încă se încarcă — se va trimite imediat ce se termină", - "attachment_upload_failed": "Netrimis - încărcarea unui atașament a eșuat. Eliminați-l și încercați din nou." - }, - "upload_progress": "Încărcare {uploaded} / {total}", - "upload_cancel": "Anulează încărcarea", - "upload_failed": "Nu s-a putut încărca {filename}", - "attachment_too_large": "„{name}” depășește dimensiunea permisă de server ({max} per fișier)", - "attachments_total_too_large": "Atașamentele ar depăși limita serverului de {max} per mesaj", - "drop_files": "Trageți fișierele pentru a le atașa", - "show_less": "Afișați mai puțin", - "send_failed": "Nu s-a putut trimite e-mailul", - "send_timeout": "Serverul a încetat să răspundă. Verificați folderul Trimise înainte de a trimite din nou — mesajul poate să fi plecat deja.", - "continue_draft": "Continuă schița", - "close_draft_title": "Să salvați sau să renunțați la schiță?", - "close_draft_message": "Aveți modificări nesalvate. Doriți să salvați acest mesaj ca schiță sau să îl ignorați?", - "smime_sign_on": "S/MIME semnarea activată", - "smime_sign_off": "Activați semnarea cu „S/MIME”", - "smime_encrypt_on": "S/MIME criptare activată", - "smime_encrypt_off": "Activați criptareS/MIME", - "smime_encrypt_unavailable": "S/MIME Criptarea nu este disponibilă – lipsesc certificatele destinatarilor", - "smime_unlock_title": "Deblocați o cheiS/MIME", - "smime_unlock_message": "Introduceți fraza de acces pentru a debloca cheia de semnare S/MIME.", - "smime_unlock_button": "Deblochează", - "smime_passphrase_placeholder": "Frază de acces", - "forgot_attachment": { - "title": "Ați uitat să atașați un fișier?", - "message": "Mesajul dvs. menționează „{keyword}”, dar nu este atașat niciun fișier. Doriți să trimiteți oricum?", - "send_anyway": "Trimite oricum", - "back": "Înapoi la editare" - }, - "empty_subject": { - "title": "Trimiteți fără subiect?", - "message": "Acest mesaj nu are subiect. Îl trimiteți oricum?", - "dont_ask_again": "Nu mai întreba", - "send_anyway": "Trimite oricum", - "back": "Înapoi la editare" - }, - "schedule_send": "Programează trimiterea", - "schedule_send_description": "Alegeți momentul în care serverul ar trebui să transmită acest mesaj.", - "schedule_send_required": "Alegeți o dată și o oră.", - "schedule_send_invalid": "Introduceți o dată și o oră valide.", - "schedule_send_future": "Alegeți o dată și o oră viitoare.", - "schedule_send_too_late": "Ora aceasta este mai târzie decât cea permisă de server.", - "schedule_send_unsupported": "Trimiterea programată nu este acceptată pentru acest cont.", - "schedule_send_cleanup_warning": "Trimiterea programată a fost creată, dar curățarea schițelor a eșuat.", - "send_delay_unsupported": "Întârzierea trimiterii nu este acceptată pentru acest cont.", - "send_delay_unsupported_confirm": "Acest cont nu acceptă amânarea trimiterii. Doriți să trimiteți imediat?", - "recipient_edit_email": "Editați adresa de e-mail", - "recipient_edit_name": "Editați numele afișat", - "recipient_email_placeholder": "Adresă de e-mail", - "recipient_name_placeholder": "Numele afișat", - "autocomplete_search_server": "Caută pe server", - "autocomplete_searching": "Se caută...", - "toolbar": { - "bold": "Aldin", - "italic": "Cursiv", - "underline": "Subliniat", - "strikethrough": "Tăiat", - "text_color": "Culoarea textului", - "remove_color": "Elimină culoarea", - "heading_1": "Titlu 1", - "heading_2": "Titlu 2", - "bullet_list": "Listă cu marcatori", - "ordered_list": "Listă numerotată", - "quote": "Citat", - "code_block": "Bloc de cod", - "align_left": "Aliniere la stânga", - "align_center": "Centrare", - "align_right": "Aliniere la dreapta", - "text_direction": "Direcția textului (RTL/LTR)", - "link": "Link", - "table": "Tabel", - "clear_formatting": "Șterge formatarea", - "undo": "Anulează", - "redo": "Refă", - "add_row_above": "Adaugă rând deasupra", - "add_row_below": "Adaugă rând dedesubt", - "add_column_before": "Adaugă coloană înainte", - "add_column_after": "Adaugă coloană după", - "delete_row": "Șterge rândul", - "delete_column": "Șterge coloana", - "toggle_header_row": "Comută rândul de antet", - "delete_table": "Șterge tabelul", - "pick_size": "Alege dimensiunea", - "font_size": "Dimensiune font", - "font_size_default": "Implicit", - "background_color": "Culoarea fundalului", - "remove_background_color": "Elimină culoarea fundalului" - }, - "send_filing_warning": "Trimis - dar curățarea ulterioară a eșuat, poate rămâne o ciornă veche." - }, - "confirm_dialog": { - "confirm": "Confirmare", - "cancel": "Anulează" - }, - "common": { - "loading": "Se încarcă...", - "error": "Eroare", - "success": "Succes", - "cancel": "Anulează", - "save": "Salvare", - "delete": "Șterge", - "edit": "Editare", - "close": "Închide", - "search": "Căutare", - "refresh": "Reîmprospătează", - "settings": "Setări", - "help": "Ajutor", - "logout": "Deconectare", - "yes": "Da", - "no": "Nu", - "unknown": "Necunoscut", - "app_title": "Webmail", - "reconnecting": "Conexiune pierdută. Se încearcă reconectarea…", - "rate_limited_title": "Autentificarea pe server este temporar limitată ca frecvență.", - "rate_limited_detail": "Bulwark A suspendat solicitările de fundal pentru a evita blocarea. Se va încerca din nou în câteva {seconds} s.", - "rate_limited_action_title": "Solicitarea a fost pusă în așteptare pentru a evita blocarea contului.", - "rate_limited_action_detail": "Bulwark Așteaptă încheierea perioadei de așteptare a serverului înainte de a trimite alte cereri autentificate. Încercați din nou în câteva {seconds}.", - "pull_to_refresh": "Trageți pentru a reîmprospăta", - "release_to_refresh": "Eliberați pentru a reîmprospăta", - "refreshing": "Se reîmprospătează…" - }, - "notifications": { - "email_sent": "E-mailul a fost trimis cu succes", - "email_deleted": "E-mail șters", - "email_archived": "E-mail arhivat", - "emails_deleted": "{count, plural, one {# e-mail șters} few {# e-mailuri șterse} other {# de e-mailuri șterse}}", - "emails_archived": "{count, plural, one {# e-mail arhivat} few {# e-mailuri arhivate} other {# de e-mailuri arhivate}}", - "error_archiving": "Arhivarea e-mailului a eșuat", - "error_updating": "Actualizarea e-mailului a eșuat", - "email_starred": "E-mail marcat cu stea", - "email_unstarred": "E-mail fără stea", - "email_marked_read": "E-mail marcat ca citit", - "email_marked_unread": "E-mail marcat ca necitit", - "copied_to_clipboard": "Copiat în clipboard", - "source_copied": "Sursă copiată în clipboard", - "error_sending": "Nu s-a putut trimite e-mailul", - "error_deleting": "Nu s-a putut șterge e-mailul", - "error_loading": "Nu s-au putut încărca e-mailurile", - "new_email": "E-mail nou", - "new_email_from": "De pe {sender}", - "click_to_view": "Faceți clic pentru a vizualiza", - "email_moved": "E-mail mutat", - "emails_moved": "{count} e-mailuri mutate", - "moved_to_mailbox": "Mutată la {mailbox}", - "move_failed": "Mutarea a eșuat", - "move_error": "Nu s-au putut muta e-mailurile în folderul selectat", - "email_tagged": "E-mail etichetat", - "emails_tagged": "{count} e-mailuri etichetate", - "tag_failed": "Etichetarea a eșuat", - "identity_created": "Identitatea a fost creată cu succes", - "identity_updated": "Identitatea a fost actualizată cu succes", - "identity_deleted": "Identitate ștearsă", - "identity_set_primary": "Identitate principală actualizată", - "identity_create_failed": "Nu s-a putut crea identitatea: {error}", - "identity_update_failed": "Nu s-a putut actualiza identitatea: {error}", - "identity_delete_failed": "Nu s-a putut șterge identitatea: {error}", - "identity_unauthorized": "Nu aveți autorizație să trimiteți mesaje de la această adresă de e-mail", - "identity_not_found": "Identitate neidentificată", - "vacation_saved": "Setările răspunsului automat de vacanță au fost salvate", - "vacation_save_failed": "Nu s-au putut salva setările răspunsului automat de concediu", - "filters_saved": "Filtrele au fost salvate cu succes", - "filters_save_failed": "Nu s-au putut salva filtrele", - "filters_deleted": "Regula de filtrare a fost ștersă", - "templates_exported": "Șabloanele au fost exportate cu succes", - "templates_imported": "{count, plural, one {# șablon} few {# șabloane} other {# de șabloane}} importat", - "templates_import_errors": "Unele șabloane nu au putut fi importate", - "templates_import_empty": "Nu s-au găsit șabloane în fișier", - "export_email_error": "Nu s-a putut exporta e-mailul", - "import_email_success": "E-mailul a fost importat cu succes", - "import_email_error": "Importul e-mailului a eșuat" - }, - "date": { - "today": "Astăzi", - "yesterday": "Ieri", - "this_week": "Săptămâna aceasta", - "last_week": "Săptămâna trecută", - "this_month": "Luna aceasta", - "older": "Mai vechi", - "just_now": "Chiar acum", - "minutes_ago": "{count} acum un minut", - "minutes_ago_plural": "{count} acum câteva minute", - "hours_ago": "{count} acum o oră", - "hours_ago_plural": "{count} acum câteva ore", - "days_ago": "{count} acum o zi", - "days_ago_plural": "{count} zile în urmă" - }, - "language": { - "title": "Limba", - "english": "Engleză", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Portugheză", - "russian": "Русский", - "select_language": "Selectați limba", - "switch_to_english": "Comutați la limba engleză", - "switch_to_french": "Comutați la limba franceză", - "switch_to_japanese": "Comutați la limba japoneză", - "switch_to_spanish": "Comutați la limba spaniolă", - "switch_to_italian": "Comutați la limba italiană", - "switch_to_german": "Comutați la limba germană", - "switch_to_dutch": "Comutați la limba olandeză", - "switch_to_portuguese": "Comutați la portugheză", - "switch_to_russian": "Comutați la limba rusă", - "switching": "Schimbarea limbii...", - "switch": "Schimbă limba", - "current": "Limba curentă", - "polish": "Poloneză", - "switch_to_polish": "Comutați la limba poloneză", - "en": "Engleză", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Poloneză", - "pt": "Portugheză", - "he": "עברית", - "ru": "Русский", - "sk": "Slovacă", - "uk": "Ukrainiană", - "zh": "Chineză simplificată", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Comută la persană", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Comută la mongolă" - }, - "settings": { - "title": "Setări", - "back_to_mail": "Înapoi la Mail", - "save_success": "Setările au fost salvate cu succes", - "import_success": "Setările au fost importate cu succes", - "import_error": "Nu s-au putut importa setările", - "reset_confirm": "Sunteți sigur că doriți să resetați toate setările la valorile implicite?", - "unsaved_changes": "Aveți modificări nesalvate", - "discard_changes": "Doriți să renunțați la modificările nesalvate?", - "discard": "Ignorați", - "keep_editing": "Continuați editarea", - "search_placeholder": "Setări de căutare", - "search_clear": "Șterge căutarea", - "search_no_results": "Nu există setări corespunzătoare", - "tabs": { - "appearance": "Aspect", - "language": "Limba, regiunea și ora", - "email": "Comportamentul e-mailurilor", - "composer": "Compozitor", - "privacy": "Confidențialitate și securitate", - "account": "Cont", - "identities": "Identități", - "vacation": "Răspuns automat de vacanță", - "advanced": "Nivel avansat", - "calendar": "Calendar", - "filters": "Filtre", - "templates": "Șabloane", - "folders": "Dosare", - "keywords": "Etichete", - "security": "Securitate", - "files": "Fișiere", - "contacts": "Contacte", - "encryption": "Criptare", - "protocol_handlers": "Aplicații implicite", - "sidebar_apps": "Aplicații din bara laterală", - "notifications": "Notificări", - "layout": "Aspect", - "reading": "Citire", - "composing": "Redactarea", - "downloads": "Descărcări", - "content_senders": "Conținut și expeditori", - "about_data": "Despre & Date", - "debug": "Depanare" - }, - "tab_groups": { - "general": "Generalități", - "account": "Cont și identitate", - "organization": "Organizarea e-mailurilor", - "apps": "Aplicații", - "system": "Sistem", - "appearance": "Aspect", - "mail": "E-mail", - "privacy": "Confidențialitate și securitate", - "advanced": "Nivel avansat" - }, - "appearance": { - "message_list_order": { - "label": "Ordinea listei de mesaje", - "description": "Cum sunt ordonate mesajele în listă. Sortarea este făcută de server, deci acoperă întregul dosar, nu doar mesajele încărcate.", - "preset": { - "chronological": "Cronologic (cele mai noi primele)", - "unread_first": "Necitite primele", - "starred_first": "Cu stea primele", - "tagged_first": "Etichetate primele", - "custom": "Personalizat" - }, - "tag_label": "Etichetă", - "advanced_show": "Avansat: editează nivelurile de sortare", - "advanced_hide": "Ascunde avansat", - "criterion_label": "Criteriul nivelului {n}", - "direction_label": "Direcția nivelului {n}", - "criteria": { - "unread": "Necitit", - "starred": "Cu stea", - "tag": "Etichetă", - "receivedAt": "Data primirii", - "sentAt": "Data trimiterii", - "from": "Expeditor", - "subject": "Subiect", - "size": "Dimensiune" - }, - "direction": { - "unread_first": "Necitite primele", - "read_first": "Citite primele", - "starred_first": "Cu stea primele", - "unstarred_first": "Fără stea primele", - "tagged_first": "Etichetate primele", - "untagged_first": "Neetichetate primele", - "newest_first": "Cele mai noi primele", - "oldest_first": "Cele mai vechi primele", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Cele mai mari primele", - "smallest_first": "Cele mai mici primele" - }, - "add_level": "Adaugă nivel", - "remove_level": "Elimină nivelul", - "move_up": "Mută în sus", - "move_down": "Mută în jos", - "tiebreak_note": "Până la trei niveluri; nivelurile următoare departajează egalitățile. „Cele mai noi primele” se aplică întotdeauna la final.", - "scope": { - "label": "Aplică la toate dosarele", - "description": "Dezactivat, ordinea se aplică doar la Inbox; celelalte dosare rămân cronologice." - }, - "unsupported_note": "Serverul tău de e-mail nu acceptă sortarea după starea de citire, stele sau etichete. Aceste niveluri sunt ignorate, iar lista rămâne cronologică." - }, - "title": "Aspect", - "description": "Personalizați aspectul și funcționalitatea serviciului dvs. de webmail", - "theme": { - "label": "Tema", - "description": "Alegeți schema de culori preferată", - "light": "Ușor", - "dark": "Întunecat", - "system": "Sistem" - }, - "language": { - "label": "Limba", - "description": "Alegeți limba preferată" - }, - "font_size": { - "label": "Dimensiunea fontului", - "description": "Ajustați dimensiunea textului pentru o mai bună lizibilitate", - "small": "Mic", - "medium": "Nivel mediu", - "large": "Mare" - }, - "list_density": { - "label": "Densitate", - "description": "Controlați spațierea și marginile interioare în întreaga interfață de utilizator", - "extra_compact": "Extra compact", - "compact": "Compact", - "regular": "Regulat", - "comfortable": "Confortabil" - }, - "animations": { - "label": "Activați animațiile", - "description": "Prezentați tranziții și efecte fluide" - }, - "toolbar_position": { - "label": "Poziția barei de instrumente", - "description": "Unde să afișați butoanele de acțiune pentru e-mailuri (Răspunde, Arhivează, Șterge etc.)", - "top": "Sus", - "below_subject": "Sub subiect" - }, - "toolbar_labels": { - "label": "Afișați etichetele barei de instrumente", - "description": "Afișați etichete text lângă pictogramele din bara de instrumente. Dezactivați-le pentru a economisi spațiu odată ce v-ați familiarizat cu pictogramele." - }, - "hide_account_switcher": { - "label": "Ascunde comutatorul de conturi din bara laterală", - "description": "Ascundeți selectorul de conturi din partea de sus a barei laterale a folderelor. Puteți comuta între conturi în continuare din bara de navigare din partea de jos." - }, - "show_rail_account_list": { - "label": "Afișați avatarurile conturilor în bara de navigare", - "description": "Afișați cercurile conturilor individuale în partea de jos a barei de navigare pentru comutarea rapidă, cu un buton de deconectare sub acestea." - }, - "unified_mailbox": { - "label": "Căsuță poștală unificată", - "description": "Afișați folderele combinate (Mesaje primite, Mesaje trimise etc.) pentru toate conturile conectate", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Includeți căsuțele de e-mail de grup", - "description": "De asemenea, integrați căsuțele de e-mail partajate/de grup în vizualizarea unificată." - } - }, - "all_mail": { - "folders_label": "Dosare în „Toate mesajele”", - "folders_description": "Alegeți ce dosare să fie incluse în vizualizarea „Toate mesajele”.", - "account_hint": "Se aplică pentru {account}.", - "no_folders": "Nu sunt disponibile foldere." - }, - "colorful_sidebar_icons": { - "label": "Pictograme colorate în bara laterală", - "description": "Colorați pictogramele folderelor și etichetelor în funcție de tip (albastru pentru „Inbox”, roșu pentru „Junk”, verde pentru „Sent” etc.). Dezactivați această opțiune pentru o bară laterală monocromă." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Afișează numărul total de mesaje", - "description": "Afișează numărul total de mesaje lângă foldere și etichete, pe lângă numărul celor necitite. Dezactivează pentru a afișa doar mesajele necitite." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Interfață Pro (experimentală)", - "description": "Aspect destinat utilizatorilor avansați, disponibil doar pe desktop, cu navigare prin mesaje în mai multe file și fluxuri de lucru între conturi. Interfața standard nu este afectată; puteți reveni la aceasta în orice moment.", - "open_label": "Interfața Open Pro", - "back_to_standard": "Înapoi la standard" - }, - "cross_unread": { - "label": "Toate conturile: Necitite", - "description": "Afișează în secțiunea Toate conturile o intrare cu poșta necitită din toate conturile, în toate folderele cu excepția spam, trimise, arhivă, coș și ciorne." - }, - "cross_starred": { - "label": "Toate conturile: Cu stea", - "description": "Afișează în secțiunea Toate conturile o intrare cu poșta cu stea din toate conturile, în toate folderele cu excepția spam, trimise, arhivă, coș și ciorne." - }, - "cross_all": { - "label": "Toate conturile: Toată poșta", - "description": "Afișează în secțiunea Toate conturile o intrare cu toată poșta din toate conturile, în toate folderele cu excepția spam, trimise, arhivă, coș și ciorne." - } - }, - "keywords": { - "title": "Etichete de e-mail", - "description": "Definiți etichete pentru a vă organiza e-mailurile pe culori. Acestea sunt stocate pe server sub formă de cuvinte-cheie dJMAP.", - "add_keyword": "Adăugați etichetă", - "label_field": "Nume afișat", - "label_placeholder": "de ex. Serviciu, Personal, Urgent", - "id_field": "EtichetăID", - "id_placeholder": "de exemplu: profesional, personal", - "color_field": "Culoare", - "id_exists": "Această etichetă ID există deja", - "edit": "Editați eticheta", - "delete": "Șterge eticheta", - "save": "Salvare", - "add": "Adăugați", - "cancel": "Anulează", - "migrating": "Actualizarea etichetei pentru e-mailurile existente…", - "migration_error": "Nu s-a putut actualiza eticheta pentru e-mailurile existente", - "nesting": { - "label": "Etichete imbricate", - "description": "Imbricați etichete sub alte etichete și afișați-le ca un arbore în bara laterală." - }, - "parent_field": "Etichetă părinte", - "no_parent": "Fără etichetă părinte", - "too_long": "Această cale de etichetă este prea lungă (cel mult {max} caractere)", - "has_children_locked": "Alte etichete sunt imbricate sub aceasta, așa că numele și eticheta părinte sunt blocate. Mutați-le sau eliminați-le mai întâi.", - "has_children_delete": "Eliminați mai întâi etichetele imbricate sub aceasta", - "visibility_field": "Vizibilitate în bara laterală", - "visibility": { - "show": "Afișează", - "unread": "Afișează dacă sunt necitite", - "hide": "Ascunde" - }, - "unrecognized": { - "title": "Recuperați etichetele de pe server", - "description": "Numele și culorile etichetelor sunt stocate în acest browser, dar etichetele în sine se află pe e-mailurile dvs. sub formă de cuvinte-cheie JMAP. Scanați corespondența pentru cuvinte-cheie care nu au o etichetă aici și adăugați-le înapoi.", - "scan": "Căutați etichete", - "rescan": "Scanați din nou", - "scanning": "Se scanează… {scanned} din {total} e-mailuri", - "none": "Toate etichetele de pe e-mailurile dvs. sunt deja definite aici.", - "messages": "{count, plural, one {# e-mail} few {# e-mailuri} other {# de e-mailuri}}", - "add_all": "Adăugați toate cele {count}", - "partial": "Au fost scanate doar cele mai recente {scanned} din {total} e-mailuri. Etichetele folosite doar pe mesaje mai vechi pot lipsi.", - "error": "Corespondența nu a putut fi scanată pentru etichete." - } - }, - "notifications": { - "test_sound": "Testați sunetul de notificare", - "sounds": { - "default": "Implicit (Bip)", - "cheerful": "Vesel", - "involved": "Implicat", - "swift": "Gest rapid", - "relax": "Relaxează-te" - }, - "push": { - "title": "Notificări în fundal", - "description": "Primiți notificări de sistem pentru e-mailuri noi chiar și atunci când acest site este închis. Notificările sunt transmise prin intermediul serverului de push Bulwark; serverul nu vede niciodată conținutul e-mailurilor.", - "relay_label": "Relay push", - "relay_desc": "Implicit este setat serverul de retransmitere găzduit Bulwark. Modificați această setare numai dacă utilizați un server propriu.", - "relay_locked": "Configurat de administrator", - "relay_locked_desc": "Relay-ul push URL a fost configurat de administratorul dvs. și nu poate fi modificat.", - "status_active": "Activ pe acest dispozitiv", - "status_inactive": "Nu este activat pe acest dispozitiv", - "status_unsupported": "Acest browser nu acceptă Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "Se lucrează…", - "enable": "Activați", - "reenable": "Reînregistrează-te", - "disable": "Dezactivare", - "confirm_disable_title": "Dezactivați notificările din fundal?", - "confirm_disable_message": "Acest dispozitiv nu va mai primi alerte atunci când site-ul este închis.", - "dismiss_aria": "Ignorați solicitarea de notificări", - "ios_hint": "Pe iOS, instalați mai întâi site-ul pe ecranul de start – Safari trimite notificări Web Push numai către aplicațiile web progresive (PWA) instalate.", - "devices_title": "Dispozitive înregistrate", - "devices_desc": "Toate dispozitivele înregistrate să primească notificări în fundal pentru acest cont. Revocarea unuia îl oprește imediat din a le primi.", - "devices_loading": "Se încarcă dispozitivele înregistrate…", - "devices_empty": "Niciun dispozitiv nu este înregistrat pentru notificări în fundal.", - "devices_error": "Dispozitivele înregistrate nu au putut fi încărcate.", - "devices_refresh": "Reîmprospătează", - "device_this": "Acest dispozitiv", - "device_other": "Alt dispozitiv", - "device_status_active": "Livrează notificări", - "device_status_inactive": "Nu a livrat niciodată o notificare", - "device_status_unknown": "Necunoscut acestui releu", - "device_expires": "Expiră la {date}", - "revoke": "Revocă", - "confirm_revoke_title": "Revocați acest dispozitiv?", - "confirm_revoke_message": "Va înceta imediat să primească notificări în fundal. Reactivarea push pe acel dispozitiv îl va înregistra din nou.", - "confirm_revoke_message_this": "Acest dispozitiv va înceta imediat să primească notificări în fundal." - }, - "sound_selection": { - "title": "Sunet de notificare", - "description": "Alegeți ce sunet să se redea pentru notificări", - "choose": "Sunet", - "choose_desc": "Selectați un ton de notificare și faceți clic pe pictograma difuzorului pentru a-l asculta" - }, - "email": { - "title": "Notificări prin e-mail", - "description": "Configurați notificările pentru e-mailurile primite", - "enabled": "Notificări prin e-mail", - "enabled_desc": "Afișați notificări la primirea de e-mailuri noi", - "sound": "Sunet de notificare", - "sound_desc": "Redă o alertă audio la sosirea unor e-mailuri noi", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Notificări din calendar", - "description": "Configurați notificările pentru evenimentele din calendar", - "enabled": "Notificări privind evenimentele", - "enabled_desc": "Afișați alerte pentru evenimentele viitoare din calendar", - "sound": "Sunet de notificare", - "sound_desc": "Redă o alertă audio pentru mementourile din calendar", - "invitation_parsing": "Analizați invitațiile prin e-mail", - "invitation_parsing_desc": "Detectați invitațiile din calendar din atașamentele de e-mail și afișați acțiunile din calendar" - } - }, - "language_region": { - "title": "Limba, regiunea și ora", - "description": "Limba, formatul datei, formatul orei și alte preferințe regionale", - "language": { - "label": "Limba", - "description": "Alegeți limba preferată", - "english": "Engleză", - "french": "Français" - }, - "date_format": { - "label": "Formatul datei", - "description": "Modul în care sunt afișate datele în lista de e-mailuri", - "smart": "Inteligent (adaptat la setările locale)", - "relative": "Relativ (acum 1 oră, acum 2 zile)", - "full": "Întotdeauna data completă", - "preview_today": "Astăzi:", - "preview_this_week": "În această săptămână:", - "preview_older": "Versiune anterioară:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Formatul orei", - "description": "Alegeți între formatul de 12 ore sau cel de 24 de ore", - "12h": "12 ore", - "24h": "24 de ore" - }, - "first_day": { - "label": "Prima zi a săptămânii", - "description": "Începeți săptămâna duminică sau luni", - "sunday": "Duminică", - "monday": "Luni" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Șterge căutarea la schimbarea folderului", - "description": "Un clic pe un folder (Inbox, Trimise…) șterge căutarea activă și afișează acel folder. Dezactivat, căutarea rămâne și se reia în folderul ales." - }, - "request_read_receipt": { - "label": "Solicitați confirmări de citire în mod implicit", - "description": "Activați în prealabil solicitarea de confirmare de citire atunci când redactați un mesaj nou." - }, - "read_receipt_response": { - "label": "Răspundeți la solicitările de confirmare de citire", - "description": "Ce trebuie făcut atunci când un mesaj primit solicită o confirmare de citire.", - "ask": "Întrebați de fiecare dată", - "always": "Trimiteți întotdeauna", - "never": "Nu trimiteți niciodată" - }, - "title": "Comportamentul e-mailurilor", - "description": "Configurați modul în care sunt gestionate e-mailurile", - "mark_read": { - "label": "Marcați ca citit", - "description": "Când să marcați e-mailurile ca citite atunci când sunt deschise", - "instant": "Imediat", - "delay_3s": "După 3 secunde", - "delay_5s": "După 5 secunde", - "never": "Niciodată" - }, - "delete_action": { - "label": "Acțiunea Ștergere", - "description": "Ce se întâmplă când ștergeți un e-mail", - "trash": "Mută în Coșul de gunoi", - "trash_and_read": "Mută în Coșul de gunoi și marchează ca citit", - "permanent": "Șterge definitiv", - "warning": "E-mailurile vor fi șterse definitiv și nu pot fi recuperate. Această acțiune este ireversibilă." - }, - "message_spacing": { - "label": "Spațierea mesajului", - "description": "Spațiu în jurul corpului mesajului în cititor", - "auto": "Automat (recomandat)", - "always": "Adaugă întotdeauna spațiere", - "edge": "Din margine în margine" - }, - "plain_text_font": { - "label": "Font pentru text simplu", - "description": "Fontul folosit pentru e-mailurile în text simplu", - "mono": "Monospațiat", - "sans": "Normal (la fel ca aplicația)" - }, - "archive_mode": { - "label": "Arhivați în", - "description": "Cum să organizați e-mailurile atunci când le arhivați", - "single": "Un singur dosar", - "year": "Un dosar pentru fiecare an", - "month": "Un dosar pentru fiecare lună", - "reorganize": "Reorganizați arhiva existentă", - "reorganize_success": "{count, plural, =0 {Nu există e-mailuri de reorganizat} one {1 e-mail reorganizat} few {# e-mailuri reorganizate} other {# de e-mailuri reorganizate}}", - "reorganize_error": "Nu s-a putut reorganiza arhiva" - }, - "permanently_delete_junk": { - "label": "Șterge definitiv mesajele nedorite", - "description": "Ștergeți definitiv e-mailurile din folderul „Junk/Spam” în loc să le mutați în „Coșul de gunoi”" - }, - "mail_layout": { - "label": "Structura e-mailului", - "description": "Alegeți între panoul de citire clasic divizat, un flux de citire concentrat în stilul Gmail sau un panou de citire în partea de jos, în stilul Zimbra.", - "split": "Panou divizat", - "split_description": "Mențineți lista de mesaje și panoul de citire vizibile unul lângă celălalt.", - "focus": "Listă cu elemente evidențiate", - "focus_description": "Afișați câte o linie pe mesaj și deschideți e-mailul pe toată lățimea ecranului, menținând în același timp vizibilă bara laterală a folderelor.", - "horizontal": "Panoul de citire în partea de jos", - "horizontal_description": "Afișați lista de mesaje în partea de sus și deschideți mesajul selectat într-un panou de citire situat sub aceasta." - }, - "show_preview": { - "label": "Afișați textul de previzualizare", - "description": "Afișați previzualizarea e-mailurilor în listă", - "focus_description": "Afișați textul de previzualizare în linie în lista de mesaje cu o singură linie selectată" - }, - "disable_threading": { - "label": "Dezactivați gruparea conversațiilor", - "description": "Afișați e-mailurile ca mesaje individuale, în loc să le grupați pe conversații" - }, - "plain_text_mode": { - "label": "Numai text simplu", - "description": "Dezactivați editorul de text formatat și trimiteți toate e-mailurile doar ca text simplu, inclusiv răspunsurile și redirecționările" - }, - "auto_select_reply_identity": { - "label": "Răspunde de la adresa de la care a fost primit mesajul", - "description": "Când răspundeți, trimiteți mesajul de la adresa la care a fost trimis inițial. Se potrivesc mai întâi identitățile; pentru livrările de tip „catch-all” ale domeniului, antetul „De la” este rescris cu aliasul, în timp ce trimiterea se face prin identitatea dvs. principală." - }, - "reply_identity_match": { - "label": "Potrivirea adresei de primire", - "description": "Care adrese de primire sunt considerate ale tale. „Doar adresa exactă” alege numai una dintre identitățile configurate; „același domeniu” tratează și orice altă adresă de pe domeniile identităților tale ca alias catch-all și rescrie antetul De la cu ea. Alege adresa exactă dacă aceste adrese sunt liste de distribuție.", - "exact": "Doar adresa exactă", - "domain": "Orice adresă de pe domeniile mele" - }, - "signature_position": { - "label": "Poziția semnăturii", - "description": "Unde să inserați semnătura în răspunsuri și redirecționări. Deasupra textului citat, semnătura se citește în mod natural ca o încheiere a răspunsului; sub text, mesajul original rămâne contiguu.", - "above_quote": "Înaintea textului citat", - "below_quote": "După textul între ghilimele" - }, - "signature_separator": { - "label": "Delimitator de semnătură", - "description": "Prefațați semnătura cu linia standard de delimitare „-- ” (RFC 3676). Dezactivați această opțiune dacă preferați ca mesajul să treacă direct în semnătură." - }, - "sub_address_delimiter": { - "label": "Delimitator de subadresă", - "description": "Caracterul care separă numele dvs. de utilizator de eticheta subadresei. Folosiți delimitatorul utilizat de serverul dvs. de e-mail (de exemplu, user {delimiter} tag@domain.com).", - "option": "{delimiter} ({delimiter} tag@domain.com)", - "custom": "Personalizare…", - "custom_input_label": "Caracter delimitator personalizat" - }, - "attachment_click_action": { - "label": "Acțiunea de clic pe atașament", - "description": "Alegeți dacă, la clic pe un fișier atașat, acesta să fie previzualizat sau descărcat imediat", - "preview": "Previzualizați, dacă este posibil", - "download": "Descărcați imediat" - }, - "attachment_position": { - "label": "Poziția atașamentului", - "description": "Unde să afișați atașamentele în antetul e-mailului", - "beside-sender": "Lângă expeditor", - "below-header": "Sub antet" - }, - "emails_per_page": { - "10": "10 e-mailuri", - "25": "25 de e-mailuri", - "50": "50 de e-mailuri", - "100": "100 de e-mailuri", - "label": "E-mailuri pe pagină", - "description": "Numărul de e-mailuri care se încarcă simultan" - }, - "always_light_mode": { - "label": "Afișați întotdeauna e-mailurile în modul luminos", - "description": "Afișați conținutul e-mailurilor în modul luminos chiar și atunci când aplicația se află în modul întunecat, evitând astfel problemele de conversie asociate modului întunecat" - }, - "external_content": { - "label": "Conținut extern", - "description": "Cum se gestionează imaginile și conținutul extern", - "ask": "Întrebați întotdeauna", - "block": "Blocați întotdeauna", - "allow": "Permiteți întotdeauna" - }, - "trusted_senders": { - "label": "Expeditori de încredere", - "description": "Gestionați expeditorii ale căror imagini se încarcă automat", - "count_zero": "Niciunul", - "count_one": "1 expeditor", - "count_other": "{count} expeditori", - "modal_title": "Expeditori de încredere", - "empty_title": "Nu există încă expeditori de încredere", - "empty_description": "Când vizualizați un e-mail cu imagini blocate, faceți clic pe „Aveți întotdeauna încredere în acest expeditor” pentru a le adăuga aici.", - "add_manually": "Adăugați expeditorul manual", - "add_button": "Adăugați", - "add_placeholder": "Introduceți adresa de e-mail", - "search_placeholder": "Căutați expeditori...", - "no_results": "Nu există expeditori care să corespundă căutării dvs.", - "remove": "Eliminați", - "close": "Închide", - "invalid_email": "Vă rugăm să introduceți o adresă de e-mail validă", - "already_added": "Acest expeditor este deja de încredere", - "save_error": "Nu s-a putut salva – verificați jurnalul de depanare al Contacte pentru detalii", - "use_address_book_label": "Sincronizare cu agenda de adrese", - "use_address_book_description": "Salvați expeditorii de încredere într-o agendă dedicată, denumită „Expeditori de încredere”, astfel încât aceștia să se sincronizeze pe toate dispozitivele dvs." - }, - "hover_actions": { - "label": "Acțiuni rapide la trecerea cursorului", - "description": "Alegeți ce acțiuni rapide să apară atunci când treceți cu mouse-ul peste un e-mail din listă", - "delete": "Șterge", - "star": "Marcați cu stea / Dezmarcați", - "mark_read": "Marcați ca citit / necitit", - "archive": "Arhivează", - "tag": "Etichetă", - "spam": "Marcați ca spam", - "not_spam": "Nu este spam", - "none_selected": "Nu sunt selectate acțiuni", - "mode_label": "Modul de afișare", - "mode_inline": "În text", - "mode_floating": "Plutitoare", - "corner_label": "Poziție flotantă", - "corner_top-left": "Stânga sus", - "corner_top-right": "Colțul din dreapta sus", - "corner_bottom-left": "Stânga jos", - "corner_bottom-right": "În partea dreaptă jos" - }, - "default_mail_program": { - "label": "Programul de e-mail implicit", - "description": "Setați {appName} ca program de e-mail implicit pentru linkurile de tip mailto:", - "button": "Setează ca implicit", - "success": "Browserul este solicitat să îl seteze ca implicit", - "error": "Browserul dvs. nu acceptă această funcție" - }, - "attachment_reminder": { - "label": "Memento privind atașamentele", - "description": "Afișați un avertisment înainte de trimitere atunci când mesajul menționează atașamente, dar nu sunt atașate niciunul", - "keywords_label": "Cuvinte-cheie declanșatoare", - "keywords_description": "Cuvinte sau expresii care declanșează memento-ul atunci când sunt găsite în mesajul dvs.", - "add_placeholder": "Adăugați cuvântul cheie...", - "add": "Adăugați", - "remove": "Eliminați" - }, - "empty_subject_warning": { - "label": "Avertisment pentru subiect gol", - "description": "Cere confirmare înainte de a trimite un mesaj fără subiect." - }, - "hide_inline_image_attachments": { - "label": "Ascundeți imaginile încorporate din atașamente", - "description": "Imaginile încorporate în corpul mesajului nu sunt listate ca atașamente separate" - }, - "attachment_image_previews": { - "label": "Afișați previzualizări ale imaginilor din atașamente", - "description": "Afișați atașamentele de tip imagine sub formă de miniaturi, în loc de pictograme generice de fișiere" - }, - "send_delay": { - "label": "Anulare trimitere / amânare trimitere", - "description": "Amânați trimiterile normale pentru o scurtă perioadă de timp pe partea de server.", - "off": "Oprit", - "seconds": "{seconds} secunde", - "unsupported": "Contul curent nu indică faptul că acceptă trimiterea amânată. Setarea este totuși salvată pentru alte conturi." - }, - "return_to_list_after_action": { - "label": "Revenire la listă după ștergere sau marcare ca necitit", - "description": "După ștergerea sau marcarea mesajului deschis ca necitit, reveniți la lista de mesaje în loc să deschideți mesajul următor." - }, - "swipe_right_action": { - "label": "Acțiune la glisare spre dreapta (mobil)", - "description": "Pe dispozitivele tactile, acțiunea efectuată când glisați un mesaj spre dreapta în listă." - }, - "swipe_left_action": { - "label": "Acțiune la glisare spre stânga (mobil)", - "description": "Pe dispozitivele tactile, acțiunea efectuată când glisați un mesaj spre stânga în listă." - }, - "swipe_actions": { - "none": "Niciuna", - "archive": "Arhivează", - "delete": "Șterge", - "mark_read": "Comută citit", - "star": "Comută steaua", - "spam": "Marchează ca spam" - }, - "rtl_editing": { - "label": "Suport pentru editare de la dreapta la stânga", - "description": "Adaugă un buton de direcție în bara de instrumente a editorului, cu care paragrafele pot fi setate de la stânga la dreapta sau de la dreapta la stânga" - } - }, - "composer": { - "title": "Compozitor", - "description": "Configurați setările de redactare a e-mailurilor", - "autosave": { - "label": "Interval de salvare automată", - "description": "Cât de des să se salveze automat schițele", - "30s": "La fiecare 30 de secunde", - "1m": "În fiecare minut", - "2m": "La fiecare 2 minute", - "5m": "La fiecare 5 minute" - }, - "send_confirmation": { - "label": "Confirmare trimitere", - "description": "Solicitați confirmarea înainte de a trimite e-mailurile" - }, - "default_reply": { - "label": "Modul implicit de răspuns", - "description": "Acțiunea implicită la clic pe „Răspunde”", - "reply": "Răspunde", - "reply_all": "Răspunde tuturor" - } - }, - "privacy": { - "title": "Confidențialitate și securitate", - "description": "Gestionați-vă setările de confidențialitate și securitate", - "external_images": { - "label": "Blocați imaginile externe", - "description": "Preveniți urmărirea prin intermediul imaginilor externe" - }, - "session_timeout": { - "label": "Expirarea sesiunii", - "description": "Deconectare automată după o perioadă de inactivitate", - "never": "Niciodată", - "30m": "30 de minute", - "1h": "1 oră", - "4h": "4 ore" - }, - "clear_cache": { - "label": "Golește memoria cache", - "description": "Ștergeți datele stocate în cache și fișierele temporare", - "button": "Golește memoria cache", - "confirm": "Sunteți sigur că doriți să goliți memoria cache?", - "success": "Memoria cache a fost golită cu succes" - } - }, - "account": { - "title": "Cont", - "description": "Vizualizați informațiile contului dvs.", - "name_label": "Nume afișat", - "username_label": "Nume de utilizator", - "account_type_label": "Tipul contului", - "auth_method_label": "Autentificare", - "auth_method_oauth": "Autentificare unică (OAuth /OIDC)", - "auth_method_basic": "Parolă", - "demo_account": "Cont demo", - "email": { - "label": "Adresă de e-mail", - "value": "{email}" - }, - "server": { - "label": "JMAP Server", - "value": "{server}" - }, - "storage": { - "label": "Utilizarea spațiului de stocare", - "used": "{used} {total}-ul utilizat", - "percentage": "{percent} % utilizat" - }, - "last_sync": { - "label": "Ultima sincronizare", - "value": "{time}" - }, - "accounts": { - "title": "Conturi conectate", - "description": "Trageți pentru a reordona modul în care apar conturile în meniul derulant al conturilor", - "active": "Cont activ în prezent", - "default_badge": "Cont implicit", - "set_default": "Setați ca implicită", - "switch_to": "Treceți la acest cont", - "move_up": "Sus", - "move_down": "Derulați în jos", - "drag_handle": "Trageți pentru a reordona", - "add": "Adăugare cont" - }, - "shared_accounts": { - "title": "Partajat cu mine", - "description": "Conturi de grup și conturi partajate pe care le puteți gestiona. Selectați unul pentru a-i edita filtrele, răspunsul automat de vacanță, calendarele și contactele.", - "shared_label": "Cont partajat" - } - }, - "scoped": { - "back": "Înapoi la contul meu", - "managing": "Administrare: {name}" - }, - "security": { - "title": "Securitatea contului", - "description": "Gestionați-vă parola, autentificarea în doi pași și setările de securitate", - "detecting": "Se detectează capacitățile serverului...", - "not_available": "Gestionarea securității contului nu este disponibilă pentru acest server de e-mail. Este posibil ca permisiunile necesare să fie dezactivate. Consultați documentația pentru detalii.", - "password": { - "title": "Schimbă parola", - "current": "Parola curentă", - "new": "Parolă nouă", - "confirm": "Confirmă noua parolă", - "submit": "Schimbă parola", - "success": "Parola a fost modificată cu succes", - "error_title": "Schimbarea parolei a eșuat", - "error_mismatch": "Noile parole nu se potrivesc", - "error_min_length": "Parola trebuie să conțină cel puțin 8 caractere", - "error_generic": "Nu s-a putut schimba parola" - }, - "display_name": { - "label": "Nume afișat", - "description": "Numele dvs. așa cum apare pe server", - "placeholder": "Introduceți numele afișat", - "save": "Salvare", - "success": "Numele afișat a fost actualizat", - "error": "Nu s-a reușit actualizarea numelui afișat" - }, - "totp": { - "section_title": "Autentificare în doi pași", - "label": "Autentificare TOTP", - "description": "Adăugați un nivel suplimentar de securitate cu o parolă unică, valabilă o singură dată, bazată pe timp", - "active": "Activat", - "inactive": "Dezactivat", - "enabled": "Autentificarea în doi pași este activată", - "disabled": "Autentificarea în doi pași este dezactivată", - "enable_error": "Nu s-a reușit activarea autentificării în doi pași (2FA)", - "disable_error": "Nu s-a reușit dezactivarea autentificării în doi pași (2FA)", - "setup_instructions": "Copiați acest cod URL în aplicația dvs. de autentificare (Google Authenticator, Authy etc.):", - "verification_code": "Cod de verificare", - "confirm": "Confirmare", - "disable": "Dezactivare", - "disable_confirm_prompt": "Introduceți parola pentru a dezactiva autentificarea în doi pași.", - "password_required": "Este necesară introducerea parolei", - "code_required": "Este necesar un cod de verificare", - "code_invalid": "Cod de verificare invalid. Verificați aplicația de autentificare și încercați din nou." - }, - "app_passwords": { - "title": "Parole pentru aplicații", - "description": "Creați parole pentru aplicațiile care nu acceptă autentificarea în doi pași", - "add": "Adăugați", - "create": "Creați", - "cancel": "Anulează", - "done": "Gata", - "generate": "Generați", - "name_label": "Numele aplicației", - "name_placeholder": "de exemplu, Thunderbird, Mail pe iPhone", - "expires_label": "Expiră (opțional)", - "allowed_ips_label": "Adresele IP permise (opțional)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Separate prin virgulă sau spațiu. Lăsați câmpul gol pentru a permite orice adresă IP.", - "password_label": "Parolă (lăsați câmpul gol pentru generarea automată)", - "password_placeholder": "Generat automat dacă este gol", - "copy_now_warning": "Copiați această parolă acum – nu va mai fi afișată din nou.", - "added": "Parola aplicației a fost creată", - "removed": "Parola aplicației a fost eliminată", - "add_error": "Nu s-a putut crea parola aplicației", - "remove_error": "Nu s-a reușit eliminarea parolei aplicației", - "none": "Nu sunt configurate parole pentru aplicații" - }, - "api_keys": { - "title": "API Chei", - "description": "Creați chei de tip „API” pentru scripturi și integrări care comunică direct cu serverul", - "name_label": "Numele cheii", - "name_placeholder": "de ex. script de backup, CI runner", - "copy_now_warning": "Copiați acum această cheie „API” – nu va mai fi afișată din nou.", - "added": "API cheie creată", - "removed": "API cheie eliminată", - "add_error": "Nu s-a putut crea cheia dAPI", - "remove_error": "Nu s-a putut elimina cheia „API”", - "none": "Nu sunt configurate tastele „API”" - }, - "encryption": { - "section_title": "Criptarea datelor stocate", - "label": "Criptarea e-mailurilor", - "description": "Criptați e-mailurile stocate pe server pentru un plus de confidențialitate", - "active": "{type} criptare activată", - "inactive": "Dezactivat", - "enabled": "Criptarea datelor stocate este activată", - "disabled_success": "Criptarea datelor stocate este dezactivată", - "error": "Nu s-au putut actualiza setările de criptare", - "algorithm_label": "Algoritm de criptare", - "encrypt_on_append": "Criptați e-mailurile noi la încărcare", - "allow_spam_training": "Permiteți antrenarea filtrului de spam înainte de criptarea e-mailurilor", - "enable_encryption": "Activați criptarea", - "enable_tooltip": "Activați criptarea cu această cheie", - "configure_title": "Activați criptarea cu această cheie", - "enable_button": "Activați", - "disable_tooltip": "Dezactivați criptarea", - "enabled_success": "Criptarea datelor stocate este activată" - }, - "email_client": { - "title": "Configurarea clientului de e-mail", - "description": "Utilizați aceste date de autentificare pentru a configura clientul de e-mail de pe desktop sau de pe dispozitivul mobil (Thunderbird, Apple Mail, Outlook etc.)", - "jmap_username_label": "JMAP Nume de utilizator", - "copy": "Copiere", - "copied": "Copiat", - "password_instructions": "Folosiți numele de utilizator de la JMAP menționat mai sus, împreună cu o parolă de aplicație, pentru a vă autentifica în clientul de e-mail. Creați o parolă de aplicație în secțiunea de mai sus, dacă nu ați făcut-o deja." - }, - "link_device": { - "title": "Aplicație mobilă", - "description": "Conectați-vă la aplicația mobilă „Bulwark” fără a introduce nimic. Generați un cod QR aici și scanați-l din ecranul de conectare al aplicației.", - "generate": "Afișați codul QR", - "regenerate": "Afișați un cod nou", - "instructions": "Deschideți aplicația „BulwarkMail”, atingeți „Scanează codul QR” pe ecranul de autentificare și îndreptați camera în această direcție.", - "expires_in": "Acest cod expiră în {seconds} secunde. Poate fi utilizat o singură dată.", - "expired": "Acest cod a expirat.", - "generating": "Se generează…", - "error": "Nu s-a putut crea un cod de asociere. Vă rugăm să încercați din nou." - }, - "public_keys": { - "name_label": "Numele cheii publice", - "name_placeholder": "de ex. Cheia mea", - "key_label": "Cheie publică (S/MIME sau PGP în format ASCII)", - "key_placeholder": "Lipiți aici cheia dvs. publică", - "none": "Nicio cheie publică configurată" - } - }, - "identities": { - "title": "Identități de expediere", - "description": "Gestionați adresele de e-mail de la care puteți trimite mesaje", - "identities_count": { - "label": "Identitățile tale", - "description": "Adrese de e-mail configurate pentru trimitere", - "count_zero": "Fără identități", - "count_one": "1 identitate", - "count_other": "{count} identități" - }, - "manage": "Gestionarea identităților", - "sub_addressing": { - "label": "Subadresare", - "description": "Folosiți etichete precum user+tag@domain.com pentru a organiza e-mailurile primite", - "learn_more": "Aflați mai multe" - } - }, - "vacation": { - "title": "Răspuns automat de vacanță", - "description": "Răspundeți automat la e-mailurile primite în timp ce sunteți plecat", - "loading": "Se încarcă setările de concediu...", - "not_supported": "Serverul dvs. de e-mail nu acceptă răspunsurile de vacanță.", - "fetch_error": "Nu s-au putut încărca setările de concediu. Vă rugăm să încercați din nou.", - "status": { - "label": "Răspuns automat de vacanță", - "description": "Trimiteți un răspuns automat persoanelor care vă trimit e-mailuri", - "active": "Activ", - "inactive": "Inactiv" - }, - "date_range": { - "title": "Interval de date", - "description": "Opțional, limitați răspunsul automat la o anumită perioadă", - "start": "Data de începere", - "start_description": "Lăsați necompletat dacă nu există o limită inițială", - "end": "Data de încheiere", - "end_description": "Lăsați necompletat dacă nu există o limită de sfârșit" - }, - "message": { - "title": "Mesaj de răspuns automat", - "description": "Mesajul care va fi trimis ca răspuns", - "subject_label": "Subiect", - "subject_description": "Subiectul răspunsului automat", - "subject_placeholder": "Absent de la birou", - "body_label": "Corpul mesajului", - "body_description": "Conținutul mesajelor în text simplu", - "body_placeholder": "Vă mulțumesc pentru e-mail. În prezent nu mă aflu la birou și vă voi răspunde la întoarcere.", - "html_label": "Mesaj formatat (HTML)", - "html_description": "Adăugați o versiune formatată, cu linkuri și stil. Destinatarii al căror client de e-mail nu o poate afișa vor vedea textul simplu de mai sus.", - "html_placeholder": "Scrieți un răspuns de absență formatat…" - }, - "preview": { - "title": "Previzualizare", - "show": "Afișează previzualizarea", - "hide": "Ascunde previzualizarea" - }, - "save": "Salvați modificările", - "saving": "Se salvează...", - "warnings": { - "end_before_start": "Data de încheiere trebuie să fie ulterioară datei de începere", - "start_in_past": "Data de începere este în trecut", - "empty_body": "Corpul mesajului este gol – destinatarii vor primi un răspuns gol", - "subject_too_long": "Subiectul este prea lung pentru server (cel mult {max} octeți).", - "body_too_long": "Mesajul este prea lung pentru server (cel mult {max} octeți, inclusiv formatarea)." - } - }, - "folders": { - "role_memos": "Note", - "title": "Dosare", - "description": "Gestionați-vă folderele de e-mail și atribuiți roluri standard", - "folder_list": "Dosarele dvs.", - "folder_list_description": "Faceți clic pe pictograma unui dosar pentru a-l personaliza", - "standard_roles": "Rolurile standard ale folderelor", - "standard_roles_description": "Stabiliți care sunt folderele utilizate pentru rolurile standard ale căsuței poștale, cum ar fi „Inbox”, „Sent”, „Trash” etc.", - "role_inbox": "Primite", - "role_drafts": "Ciorne", - "role_sent": "Trimise", - "role_trash": "Coș de gunoi", - "role_junk": "Spam / Mesaje nedorite", - "role_archive": "Arhivă", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Niciunul", - "create_folder": "Creați un dosar", - "create_subfolder": "Creați un subfolder", - "reorder": "Trageți pentru a reordona", - "reorder_error": "Reordonarea dosarelor a eșuat", - "move_error": "Mutarea dosarului a eșuat", - "subfolder_of": "În interiorul {name}", - "subfolder_name": "Numele subfolderului", - "new_folder_name": "Numele folderului", - "rename": "Redenumire", - "change_icon": "Schimbă pictograma", - "delete": "Șterge", - "confirm_delete": "Sunteți sigur că doriți să ștergeți „{name}”? E-mailurile din acest folder vor fi mutate în Coșul de gunoi.", - "create": "Creați", - "cancel": "Anulează", - "no_folders": "Fără foldere personalizate", - "cannot_delete_role": "Nu se poate șterge un dosar cu un rol standard. Eliminați mai întâi rolul.", - "folder_created": "Folder creat", - "folder_renamed": "Folder redenumit", - "folder_deleted": "Dosarul a fost șters", - "role_updated": "Rolul folderului a fost actualizat", - "error_create": "Nu s-a putut crea folderul", - "error_rename": "Nu s-a putut redenumi folderul", - "error_delete": "Nu s-a putut șterge folderul", - "error_delete_has_children": "Nu se poate șterge folderul: acesta conține încă subfoldere. Ștergeți-le sau mutați-le mai întâi.", - "error_delete_has_email": "Nu se poate șterge folderul: acesta conține încă e-mailuri. Mutați-le sau ștergeți-le mai întâi.", - "error_role": "Nu s-a putut actualiza rolul folderului" - }, - "advanced": { - "title": "Nivel avansat", - "description": "Opțiuni avansate și setări pentru dezvoltatori", - "debug_mode": { - "label": "Modul de depanare", - "description": "Activați înregistrarea detaliată a evenimentelor pentru depanare" - }, - "debug_categories": { - "description": "Selectați categoriile pe care doriți să le înregistrați. Dezactivați categoriile de care nu aveți nevoie pentru a reduce zgomotul din consolă.", - "jmap": "JMAP Client", - "jmap_description": "Operațiuni cu căsuța poștală, preluarea e-mailurilor și cererile prin protocolul JMAP", - "calendar": "Calendar", - "calendar_description": "Evenimente din calendar, importuri și programarea mesajelor", - "tasks": "Sarcini", - "tasks_description": "Crearea, preluarea și actualizarea sarcinilor din calendar", - "auth": "Autentificare", - "auth_description": "Autentificare, TOTP, schimb de tokenuri și gestionarea sesiunilor", - "filters": "Filtre", - "filters_description": "Sieve reguli de filtrare și scripturi de vacanță", - "email": "Vizualizarea e-mailurilor", - "email_description": "Renderizarea e-mailurilor, procesarea TNEF și marcarea ca citit", - "push": "Notificări push", - "push_description": "Configurarea și livrarea notificărilor push", - "contacts": "Contacte și agende de adrese", - "contacts_description": "Sincronizarea contactelor, operațiunile din agenda de adrese și expeditorii de încredere" - }, - "settings_sync": { - "label": "Sincronizarea setărilor", - "description": "Sincronizați-vă setările între browsere și dispozitive" - }, - "sender_favicons": { - "label": "Favicon-uri ale expeditorului", - "description": "Afișați pictogramele site-urilor web ca imagini de profil pentru expeditorii de afaceri" - }, - "show_avatars_in_junk": { - "label": "Afișați avatarurile din folderul „Junk”", - "description": "Afișați imaginile de profil și faviconurile expeditorilor din folderul de mesaje nedorite. Această opțiune este dezactivată în mod implicit pentru a evita conferirea unei aparențe de legitimitate încercărilor de phishing." - }, - "keyboard_shortcuts": { - "label": "Comenzi rapide de la tastatură", - "description": "Vedeți comenzile rapide de la tastatură disponibile", - "button": "Comenzi rapide de vizualizare" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Resetare setări", - "description": "Restabiliți toate setările la valorile implicite", - "button": "Resezare la setările implicite" - }, - "export_settings": { - "label": "Setări de export", - "description": "Descărcați setările în format JSON", - "button": "Export" - }, - "import_settings": { - "label": "Importați setările", - "description": "Încărcați setările dintr-un fișier JSON", - "button": "Import" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Aplicații din bara laterală", - "description": "Gestionați aplicațiile personalizate și linkurile din bara laterală", - "keep_loaded": "Mențineți aplicațiile încărcate", - "keep_loaded_description": "Mențineți aplicațiile încorporate în execuție în fundal atunci când comutați între ele, pentru a evita reîncărcarea", - "managed_title": "Furnizate de administrator", - "managed_description": "Aceste aplicații sunt configurate pentru toți și nu pot fi modificate.", - "manage_title": "Aplicații personalizate", - "manage_description": "Adăugați, editați sau eliminați aplicații personalizate din bara laterală" - }, - "contacts": { - "title": "Contacte", - "description": "Importați și exportați contactele", - "group_by_letter_label": "Grupați după prima literă", - "sort_by_last_name_label": "Sortare după nume de familie", - "sort_by_last_name_description": "Ordonați lista de contacte după numele de familie, astfel încât membrii familiei să apară împreună", - "group_by_letter_description": "Afișați titlurile secțiunilor în ordine alfabetică în lista de contacte", - "import_label": "Importați contactele", - "import_description": "Importați contactele dintr-un fișier „vCard” (.vcf)", - "export_label": "Exportați contactele", - "export_description": "Exportați toate contactele ca fișier VCF (vCard).", - "manage_title": "Agende de adrese", - "manage_description": "Redenumiți agendele de adrese", - "no_address_books": "Nu s-au găsit agende de adrese", - "categories_title": "Categorii", - "categories_description": "Redenumiți categoriile de contacte", - "no_categories": "Nu s-au găsit categorii" - }, - "downloads": { - "title": "Descărcări", - "description": "Personalizați modul în care sunt denumite e-mailurile și atașamentele descărcate.", - "reset": "Restabilește setările implicite", - "preview": "Previzualizare:", - "email_template": { - "label": "Numele fișierului de e-mail (.eml)", - "description": "Șablon utilizat atunci când exportați un e-mail sau îl trageți în sistemul de fișiere. Extensia .eml se adaugă automat." - }, - "attachment_template": { - "label": "Numele fișierului atașat", - "description": "Șablon utilizat la descărcarea sau glisarea unui atașament. Dacă omiteți „{filename}” și „{ext}”, extensia originală este păstrată." - }, - "bundle_template": { - "label": "Nume de fișier .zip cu mai multe e-mailuri", - "description": "Șablon utilizat atunci când trageți sau descărcați mai multe e-mailuri selectate ca o singură arhivă .zip. Extensia .zip se adaugă automat." - }, - "spaces": { - "label": "Spații", - "description": "Înlocuiți spațiile din numele fișierului rezultat cu un alt caracter.", - "keep": "Păstrați spațiile", - "underscore": "Înlocuiți cu _", - "dash": "Înlocuiți cu -" - }, - "lowercase": { - "label": "Cu litere mici", - "description": "Convertiți întregul nume de fișier în litere mici." - }, - "strip_diacritics": { - "label": "Eliminați semnele diacritice", - "description": "Convertiți literele accentuate în echivalentele lor ASCII (ä → a, é → e). Util pentru instrumentele care denaturează numele de fișiere Unicode." - }, - "collapse_separators": { - "label": "Ascunde separatoarele repetate", - "description": "Reduceți șirurile de spații, linii de subliniere sau liniuțe la un singur caracter." - }, - "after_export": { - "label": "După export", - "description": "Opțional, mutați e-mailul după ce l-ați exportat în format .eml.", - "keep": "Păstrează în căsuța poștală", - "archive": "Mută în arhivă", - "trash": "Mută în coșul de gunoi" - } - }, - "filters": { - "title": "Filtre de e-mail", - "description": "Creați reguli pentru a sorta, eticheta și gestiona automat e-mailurile primite", - "add_rule": "Adăugați regulă", - "no_rules": "Fără reguli de filtrare", - "no_rules_description": "Creați reguli pentru a vă organiza automat e-mailurile primite", - "vacation_active": "Răspunsul automat de vacanță este activ", - "vacation_active_description": "Răspunsul automat este activat pentru mesajele primite", - "vacation_configure": "Configurare", - "edit_rule": "Editați regula", - "new_rule": "Regulă nouă", - "delete_rule": "Șterge regulă", - "delete_confirm": "Sunteți sigur că doriți să ștergeți această regulă?", - "enable": "Activați", - "disable": "Dezactivare", - "raw_editor": "Editorul „Sieve” (fără formatare)", - "raw_editor_warning": "Editarea scriptului brut Sieve poate afecta funcționalitatea editorului vizual de reguli. Modificările efectuate aici au prioritate față de cele din editorul vizual.", - "validate": "Validați", - "validation_success": "Scriptul este valid", - "validation_error": "Scriptul conține erori", - "save": "Salvați regulile", - "saving": "Se salvează...", - "saved": "Filtrele au fost salvate cu succes", - "save_failed": "Nu s-au putut salva filtrele", - "loading": "Se încarcă filtrele...", - "not_supported": "Serverul dvs. de e-mail nu acceptă filtre de e-mail.", - "rule_name": "Numele regulii", - "rule_name_placeholder": "de ex., Sortează buletinele informative", - "match_all": "Îndepliniți TOATE condițiile", - "match_any": "Îndepliniți ORICE condiție", - "conditions": "Condiții", - "add_condition": "Adăugați condiție", - "actions": "Acțiuni", - "add_action": "Adăugare acțiune", - "stop_processing": "Opriți procesarea regulilor următoare", - "include_spam": "Mută și mesajele marcate ca spam", - "attachment_type_placeholder": "de ex. pdf, doc, jpg", - "value_placeholder_multi": "Valoare (mai multe valori separate prin virgule)", - "condition_fields": { - "attachment": "Atașament", - "from": "De la", - "to": "Către", - "cc": "Cc", - "subject": "Subiect", - "header": "Antet personalizat", - "size": "Dimensiune", - "body": "Corpul mesajului" - }, - "comparators": { - "has_any": "este prezent", - "has_type": "de tip", - "contains": "conține", - "not_contains": "nu conține", - "is": "este exact", - "not_is": "nu este", - "starts_with": "începe cu", - "ends_with": "se termină cu", - "matches": "se potrivește cu modelul", - "greater_than": "este mai mare decât", - "less_than": "este mai mic decât" - }, - "action_types": { - "move": "Mută în folder", - "copy": "Copiere în folder", - "forward": "Redirecționează către", - "mark_read": "Marcați ca citit", - "star": "Marcați mesajul cu stea", - "add_label": "Adăugați o etichetă", - "discard": "Ignorare (ștergere fără confirmare)", - "reject": "Respinge cu mesaj", - "keep": "Păstrează în căsuța de primire", - "stop": "Opriți procesarea" - }, - "move_to_folder": "Selectează folderul", - "copy_to_folder": "Selectează folderul", - "forward_to": "Redirecționează către adresa de e-mail", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Păstrează o copie", - "forward_limit": "Limita de redirecționări per mesaj pe acest server: {count}. Redirecționările în plus sunt omise.", - "reject_message": "Mesaj de respingere", - "reject_placeholder": "E-mailul dvs. a fost respins", - "label_name": "Numele etichetei", - "label_placeholder": "Selectați eticheta", - "header_name": "Numele antetului", - "header_placeholder": "de ex., X-Mailing-List", - "size_bytes": "Dimensiune în octeți", - "size_placeholder": "de exemplu, 1000000", - "system_managed": "Regulă gestionată de sistem", - "opaque_warning": "Acest script a fost editat în afara editorului vizual. Este disponibilă doar editarea directă a fișierului „Sieve”.", - "open_sieve_editor": "Deschideți editorul de fișiere Sieve în format brut", - "fetch_error": "Nu s-au putut încărca filtrele", - "expanded_view": "Vizualizare extinsă", - "expanded_view_description": "Afișați regulile de filtrare cu blocuri detaliate de condiții și acțiuni", - "if": "Dacă", - "then": "Apoi", - "match_all_conditions": "toate se potrivesc", - "match_any_condition": "orice potriviri", - "and": "și", - "or": "sau", - "cancel": "Anulează", - "confirm_delete": "Șterge", - "rule_list": "Reguli de filtrare", - "drag_to_reorder": "Trageți pentru a reordona", - "match_type": "Potriviți tipul", - "reset_to_visual": "Reveniți la editorul vizual", - "reset_warning": "Aceasta va anula scriptul curent și va începe de la zero.", - "confirm_reset": "Resetare", - "validation_empty_name": "Numele regulii este obligatoriu", - "validation_empty_conditions": "Este necesară cel puțin o condiție cu o valoare", - "validation_empty_actions": "Este necesară cel puțin o acțiune", - "templates_section": "Începeți de la șablon", - "template_newsletters": "Mutați buletinele informative în dosar", - "template_receipts": "Arhivarea automată a confirmărilor de primire", - "template_important": "Marcați e-mailurile importante", - "template_notifications": "Filtrează notificările", - "sieve_editor": { - "title": "Sieve Editor de scripturi", - "warning": "Editarea scriptului brut Sieve poate afecta funcționalitatea editorului vizual de reguli. Modificările efectuate aici au prioritate față de cele din editorul vizual.", - "script_content": "Sieve script", - "valid": "Scriptul este valid", - "invalid": "Scriptul conține erori", - "save_warning": "Salvarea va suprascrie orice reguli vizuale. Această acțiune nu poate fi anulată. Faceți clic din nou pe Salvare pentru a confirma.", - "validating": "Se validează...", - "validate": "Validați", - "cancel": "Anulează", - "save": "Salvare", - "confirm_save": "Confirmare Salvare", - "validation_failed": "Solicitarea de validare a eșuat" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# condiție} few {# condiții} other {# de condiții}}", - "actions_count": "{count, plural, one {# acțiune} few {# acțiuni} other {# de acțiuni}}" - }, - "origin_external": "Extern", - "managed_by_tooltip": "Gestionat de {source}. Editați-l în acea aplicație sau utilizați editorul brut Sieve." - }, - "templates": { - "title": "Șabloane de e-mail", - "description": "Creați șabloane de e-mail reutilizabile cu variabile de substituție", - "add": "Șablon nou", - "edit": "Editați șablonul", - "name": "Numele șablonului", - "name_placeholder": "de exemplu, e-mail de urmărire", - "category": "Categorie", - "category_placeholder": "de exemplu, Serviciu, Personal", - "subject": "Subiect", - "subject_placeholder": "Subiectul e-mailului", - "body": "Corpul mesajului", - "body_placeholder": "Conținutul corpului e-mailului...", - "recipients_placeholder": "email@example.com", - "identity": "Trimite ca", - "default_identity": "Identitate implicită", - "favorite": "Favorite", - "cancel": "Anulează", - "create": "Creați", - "update": "Actualizare", - "confirm_delete": "Șterge", - "no_templates": "Nu există încă șabloane", - "manage": "Gestionarea șabloanelor", - "count": "{count, plural, one {# șablon} few {# șabloane} other {# de șabloane}}", - "export_import": "Export și import", - "export_import_description": "Faceți o copie de rezervă a șabloanelor sau transferați-le pe un alt dispozitiv", - "export": "Export", - "import": "Import", - "validation": { - "empty": "Numele șablonului este obligatoriu", - "too_long": "Numele șablonului trebuie să aibă cel mult 200 de caractere" - } - }, - "files": { - "display": { - "title": "Afișare", - "description": "Configurați modul de afișare a fișierelor și folderelor" - }, - "folder_layout": { - "label": "Navigarea în dosare", - "description": "Alegeți modul de afișare a folderelor: alături de fișiere sau într-o structură arborescentă din bara laterală", - "inline": "În text", - "sidebar": "Bara laterală" - }, - "default_view": { - "label": "Vizualizare implicită", - "description": "Alegeți între afișarea sub formă de grilă și cea sub formă de listă", - "list": "Listă", - "grid": "Tabel" - }, - "default_sort": { - "label": "Sortare implicită", - "description": "Alegeți sortarea implicită pentru fișiere", - "name": "Nume", - "size": "Dimensiune", - "modified": "Modificat" - }, - "sort_direction": { - "label": "Direcția de sortare", - "description": "Alegeți ordinea crescătoare sau descrescătoare", - "ascending": "În ordine crescătoare", - "descending": "Descendent" - }, - "icons": { - "title": "Pictograme", - "description": "Configurați aspectul pictogramelor fișierelor" - }, - "show_icons": { - "label": "Afișați pictogramele fișierelor", - "description": "Afișați pictograme lângă fișiere și foldere" - }, - "colored_icons": { - "label": "Pictograme colorate", - "description": "Folosiți pictograme colorate în locul celor monocrome" - }, - "show_thumbnails": { - "label": "Afișează miniaturi", - "description": "Afișați previzualizări ale imaginilor în locul pictogramelor pentru fișierele imagine" - }, - "behavior": { - "title": "Comportament", - "description": "Configurați comportamentul browserului de fișiere" - }, - "show_hidden": { - "label": "Afișează fișierele ascunse", - "description": "Afișați fișierele și folderele care încep cu un punct" - }, - "preview": { - "label": "Previzualizare" - } - } - }, - "errors": { - "page_error_title": "A apărut o eroare", - "page_error_description": "A apărut o eroare neașteptată. Vă rugăm să încercați din nou sau să reveniți la pagina principală.", - "sidebar_error": "Nu se pot încărca căsuțele poștale", - "email_list_error": "Nu se pot încărca e-mailurile", - "viewer_error_title": "Nu se poate afișa e-mailul", - "viewer_error_description": "A apărut o problemă la afișarea acestui e-mail. Este posibil ca acesta să conțină conținut neacceptat.", - "composer_error": "Nu se poate încărca editorul", - "settings_error_title": "Setări indisponibile", - "settings_error_description": "Nu se pot încărca setările. Este posibil ca preferințele dvs. să nu fie salvate.", - "try_again": "Încercați din nou", - "reload": "Reîncarcă", - "reload_emails": "Reîncarcă e-mailurile", - "reload_settings": "Reîncarcă setările", - "retry": "Reîncearcă", - "go_home": "Accesați căsuța de intrare" - }, - "context_menu": { - "reply": "Răspunde", - "reply_all": "Răspunde tuturor", - "forward": "Redirecționează", - "mark_read": "Marcați ca citit", - "mark_unread": "Marcați ca necitit", - "star": "Stea", - "unstar": "Anulează marcarea cu stea", - "pin": "Fixează", - "unpin": "Anulează fixarea", - "move_to": "Mergi la...", - "archive": "Arhivează", - "delete": "Șterge", - "mark_as_spam": "Raportează spamul", - "not_spam": "Nu este spam", - "tag": "Etichetă", - "items_selected": "{count} e-mailuri selectate", - "edit_draft": "Editează schița", - "cancel_scheduled_send": "Anulează trimiterea", - "reschedule_send": "Reprogramare", - "cancel_and_edit": "Anulează și editează", - "cancel_and_compose_again": "Anulează și redactează din nou" - }, - "mailbox_context_menu": { - "mark_folder_read": "Marcați folderul ca citit", - "mark_folder_tree_read": "Marcați folderul și subfolderele ca citite", - "mark_all_folders_read": "Marcați toate folderele ca citite", - "new_subfolder": "Subfolder nou...", - "new_folder": "Dosar nou...", - "rename": "Redenumire...", - "share": "Partajează...", - "import_email": "Importați fișiere .eml sau .zip...", - "empty_folder": "Folder gol", - "empty_folder_generic": "Folder gol", - "delete_folder": "Șterge dosarul", - "refresh": "Reîmprospătează", - "mark_all_confirm_title": "Marcați toate folderele ca citite", - "mark_all_confirm_message": "Doriți să marcați toate mesajele necitite din contul dvs. personal ca fiind citite?", - "delete_confirm_title": "Șterge dosarul", - "delete_confirm_message": "Doriți să ștergeți definitiv folderul „{name}”? Această acțiune nu poate fi anulată.", - "prompt_new_subfolder": "Introduceți un nume pentru noul subfolder.", - "prompt_new_folder": "Introduceți un nume pentru noul folder.", - "prompt_rename": "Introduceți un nume nou pentru acest folder.", - "placeholder_folder_name": "Numele folderului", - "create": "Creați", - "rename_confirm": "Redenumire", - "toast_marked_read": "Dosar marcat ca citit", - "toast_marked_read_count": "Marcat {count, plural, one {1 mesaj} few {# mesaje} other {# de mesaje}} ca citit", - "toast_already_read": "Nu există mesaje necitite", - "toast_marked_all_read": "Toate folderele marcate ca citite", - "toast_emptied": "Dosarul a fost golit", - "toast_folder_created": "Folder creat", - "toast_folder_renamed": "Folder redenumit", - "toast_folder_deleted": "Dosarul a fost șters", - "toast_error_mark_read": "Nu s-a reușit marcarea ca citit", - "toast_error_empty": "Nu s-a reușit golirea folderului", - "toast_error_create": "Nu s-a putut crea folderul", - "toast_error_rename": "Nu s-a putut redenumi folderul", - "toast_error_delete": "Nu s-a putut șterge folderul", - "toast_error_delete_has_children": "Dosarul conține subdosare. Ștergeți-le mai întâi.", - "toast_error_delete_has_email": "Dosarul nu este gol. Goliți-l mai întâi.", - "delete_with_contents_title": "Dosarul nu este gol", - "delete_with_contents_message": "Dosarul „{name}” încă mai conține {count, plural, one {1 mesaj} other {# mesaje}}. Ștergi dosarul și toate mesajele sale? Acțiunea nu poate fi anulată.", - "delete_with_contents_confirm": "Șterge dosarul și mesajele" - }, - "shortcuts": { - "title": "Comenzi rapide de la tastatură", - "tip": "Apăsați ? oricând pentru a afișa acest ajutor", - "sections": { - "navigation": "Navigare", - "actions": "Acțiuni de e-mail", - "global": "Global", - "threads": "Subiecte", - "composer": "Compozitor" - }, - "navigation": { - "next_email": "Următorul e-mail", - "previous_email": "E-mailul anterior", - "open_email": "Deschide e-mailul", - "close_email": "Închide / Deselectează" - }, - "actions": { - "reply": "Răspunde", - "reply_all": "Răspunde tuturor", - "forward": "Redirecționează", - "star": "Comută steluța", - "archive": "Arhivează", - "delete": "Șterge", - "mark_unread": "Marcați ca necitit", - "mark_read": "Marcați ca citit", - "toggle_spam": "Raportează spam / Nu este spam" - }, - "global": { - "compose": "Redactați un e-mail nou", - "search": "Căutare focalizată", - "help": "Afișați comenzile rapide", - "refresh": "Reîmprospătați e-mailurile", - "select_all": "Selectează tot", - "global_search": "Caută în tot" - }, - "threads": { - "expand_collapse": "Extindeți/reduceți firul de discuție" - }, - "composer": { - "send": "Trimite e-mail", - "schedule_send": "Programează trimiterea", - "template_picker": "Deschide selectorul de șabloane" - } - }, - "threads": { - "messages_one": "{count} mesaj", - "messages_other": "{count} mesaje", - "messages_tooltip": "{count, plural, one {# mesajul din această conversație} few {# mesaje din această conversație} other {# de mesaje din această conversație}}", - "expand": "Extinde conversația", - "collapse": "Ascunde conversația", - "loading": "Se încarcă conversația...", - "mark_read": "Marcați conversația ca citită", - "mark_unread": "Marcați conversația ca necitită", - "archive": "Arhivează conversația", - "delete": "Șterge conversația", - "star": "Marcați conversația cu stea", - "unstar": "Dezactivează marcajul conversației", - "toggle_thread": "Comutare fir de discuție" - }, - "identities": { - "modal_title": "Gestionarea identităților de expediere", - "create_new": "Creați o identitate nouă", - "edit_identity": "Editați identitatea", - "delete_confirm": "Doriți să ștergeți această identitate? Această acțiune nu poate fi anulată.", - "cannot_delete": "Această identitate nu poate fi ștearsă", - "primary_identity": "Primar", - "set_as_primary": "Setați ca principal", - "no_identities": "Nu s-au găsit identități", - "display": { - "reply_to": "Răspunde la:", - "bcc": "BCC:", - "signature": "Semnătură:", - "preview": "Previzualizare:" - }, - "validation_errors": { - "invalid_emails": "Adrese de e-mail nevalide: {emails}", - "unknown_error": "Eroare necunoscută" - }, - "form": { - "name_label": "Nume afișat", - "name_placeholder": "de exemplu, E-mail de serviciu, Personal", - "name_required": "Numele este obligatoriu", - "email_label": "Adresă de e-mail", - "email_placeholder": "your.email@example.com", - "email_required": "Adresa de e-mail este obligatorie", - "email_invalid": "Vă rugăm să introduceți o adresă de e-mail validă", - "email_immutable": "Adresa de e-mail nu poate fi modificată după creare", - "reply_to_label": "Răspunde la (opțional)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "BCC automat (opțional)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "Semnătură text", - "html_signature_label": "HTML Semnătură", - "signature_byte_counter": "{bytes} / {max} bytes", - "signature_byte_limit_reached": "Limita serverului a fost atinsă", - "save": "Salvați identitatea", - "cancel": "Anulează", - "creating": "Se creează...", - "updating": "Se actualizează..." - }, - "sub_address": { - "button_tooltip": "Utilizați subadrese", - "popover_title": "Adăugați eticheta „Sub-adresă”", - "tag_input_placeholder": "Introduceți o etichetă (de exemplu, cumpărături)", - "preview_label": "Previzualizare:", - "recent_tags": "Etichete recente", - "suggested_tags": "Sugestii", - "use_address": "Utilizați această adresă", - "invalid_tag": "Eticheta trebuie să conțină doar caractere alfanumerice și liniuțe", - "tag_too_long": "Eticheta trebuie să aibă cel mult 30 de caractere", - "help_text": "E-mailurile trimise către adresele {delimiter} și tag@domain.com vor ajunge în căsuța dvs. de e-mail primite", - "validation": { - "empty": "Câmpul „Tag” nu poate fi gol", - "too_long": "Eticheta trebuie să conțină cel mult{max}e", - "invalid_chars": "Eticheta trebuie să conțină numai litere, cifre și liniuțe" - } - }, - "badge": { - "sent_via": "via", - "sub_address_tag": "Trimis folosind subadresa: {tag}", - "identity_name": "Trimis folosind identitatea: {name}", - "identity_short": "via {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Șterge", - "delete_confirm_title": "Ștergeți identitatea" - }, - "templates": { - "picker_title": "Alegeți un șablon", - "search_placeholder": "Căutați șabloane...", - "section_favorites": "Favorite", - "section_recent": "Recent", - "section_uncategorized": "Altele", - "no_templates": "Nu există încă șabloane", - "no_results": "Nu s-au găsit șabloane", - "fill_placeholders": "Completați valorile din câmpurile de substituție", - "enter_value": "Introduceți o valoare...", - "preview": "Previzualizare", - "insert_with_values": "Inserați cu valori", - "insert_raw": "Inserați Raw", - "copy_suffix": "(copiere)", - "placeholder": "Variabilă", - "placeholders": { - "recipient_name": "Numele destinatarului", - "company": "Numele companiei", - "date": "Data curentă", - "day_of_week": "Ziua săptămânii", - "sender_name": "Numele dvs." - } - }, - "contacts": { - "title": "Contacte", - "search_placeholder": "Căutare contacte...", - "create_new": "Contact nou", - "no_category": "Fără categorie", - "rename_category": "Redenumire categorie", - "category_name_label": "Numele categoriei", - "category_renamed": "Categorie redenumită", - "category_rename_failed": "Nu s-a putut redenumi categoria", - "category_added": "Contact adăugat la {name}", - "category_added_plural": "{count} contacte adăugate la {name}", - "empty_state": "Nu există încă contacte", - "empty_state_title": "Nu există încă contacte", - "empty_state_subtitle": "Creați primul dvs. contact sau importați-l dintr-un fișier „vCard”", - "empty_search": "Nu există contacte care să corespundă căutării dvs.", - "empty_search_hint": "Încercați un alt termen de căutare", - "empty_filtered": "Nu există contacte care să corespundă filtrelor dvs.", - "empty_filtered_hint": "Încercați să modificați sau să ștergeți filtrele", - "clear_search": "Șterge căutarea", - "import_vcard": "ImportvCard", - "delete_confirm_title": "Șterge contactul", - "delete_confirm": "Sunteți sigur că doriți să ștergeți acest contact?", - "local_mode": "Contactele sunt stocate local (serverul nu acceptă contactele dJMAP)", - "back_to_contacts": "Înapoi la contacte", - "back_to_email": "Înapoi la e-mail", - "open_categories": "Deschideți categoriile", - "tabs": { - "all": "Toate", - "groups": "Grupuri" - }, - "shared": { - "title": "Partajat" - }, - "address_books": { - "create": "Agendă nouă", - "created": "Agendă creată", - "create_failed": "Nu s-a putut crea agenda", - "title": "Agendele mele de adrese", - "shared_prefix": "Partajat: {name}", - "moved": "Contactul a fost mutat la {name}", - "moved_plural": "{count} contactele au fost mutate la {name}", - "move_failed": "Nu s-a putut muta contactul", - "address_book": "Agendă", - "rename": "Redenumire agendă", - "name_label": "Numele agendei de adrese", - "renamed": "Agenda de adrese a fost redenumită", - "rename_failed": "Nu s-a putut redenumi agenda de adrese", - "default": "Implicit", - "set_default": "Setați ca implicită", - "default_updated": "Agenda de adrese implicită a fost actualizată", - "set_default_failed": "Nu s-a reușit setarea agendei de adrese implicite", - "manage": "Gestionați agendele de adrese", - "share": "Partajați agenda de adrese", - "new_contact_in_book": "Contact nou în această agendă", - "delete": "Șterge agenda de adrese", - "confirm_delete": "Doriți să ștergeți „{name}”? Toate contactele din această agendă vor fi eliminate.", - "deleted": "Agenda de adrese a fost ștersă", - "delete_failed": "Nu s-a putut șterge agenda de adrese" - }, - "detail": { - "emails": "Adrese de e-mail", - "phones": "Numere de telefon", - "organizations": "Organizații", - "addresses": "Adrese", - "notes": "Note", - "titles": "Titluri și roluri", - "online_services": "Servicii online", - "anniversaries": "Aniversări", - "personal_info": "Informații personale", - "languages": "Limbi", - "categories": "Categorii", - "related_contacts": "Contacte asociate", - "crypto_keys": "Chei criptografice", - "cert_issuer": "Emitent", - "cert_expires": "Expiră", - "cert_expired": "Expirat", - "cert_fingerprint": "Amprentă digitală", - "cert_algorithm": "Algoritm", - "import_to_smime": "Importați în S/MIME", - "cert_already_imported": "Deja importat pe S/MIME", - "cert_imported": "Certificat importat în magazinul S/MIME", - "cert_import_failed": "Importul certificatului a eșuat", - "no_contact_selected": "Selectați un contact pentru a vizualiza detaliile", - "compose_email": "Redactați un e-mail", - "copy_email": "Copiați e-mailul", - "copy_phone": "Copiați numărul de telefon", - "copy_url": "Copiați URL", - "copied": "Copiat în clipboard", - "copy_failed": "Nu s-a putut copia în clipboard", - "created": "Creat", - "updated": "Ultima actualizare", - "timezone": "Fus orar", - "anniversary_birth": "Ziua de naștere", - "anniversary_death": "A decedat", - "anniversary_wedding": "Aniversare", - "anniversary_other": "Altele", - "personal_expertise": "Competențe", - "personal_hobby": "Hobby", - "personal_interest": "Interes", - "personal_other": "Altele", - "gender": "Gen", - "gender_masculine": "Bărbat", - "gender_feminine": "Femeie", - "gender_other": "Altele", - "gender_none": "Nu se aplică", - "gender_unknown": "Necunoscut", - "calendar": "Calendar", - "calendar_uri": "URL de calendar", - "scheduling_uri": "Programare URL", - "freebusy_uri": "URL „Liber/Ocupat”", - "section_contact": "Date de contact", - "section_work": "Muncă", - "section_personal": "Personal", - "email_default_label": "E-mail", - "phone_default_label": "Telefon", - "address_default_label": "Adresă", - "online_service_default_label": "Online", - "organization_label": "Organizare", - "title_label": "Titlu", - "role_label": "Rol", - "language_label": "Limba", - "related_default_label": "Legat de", - "more_actions": "Alte acțiuni", - "age_years": "{count, plural, one {1 an} few {# ani} other {# de ani}}", - "years_since": "{count, plural, one {1 an} few {# ani} other {# de ani}}" - }, - "activity": { - "recent_emails": "E-mailuri recente", - "upcoming_events": "Evenimente viitoare", - "no_emails": "Nu există e-mailuri recente", - "no_events": "Nu există evenimente viitoare", - "no_subject": "(Fără subiect)", - "no_title": "(Fără titlu)", - "load_failed": "Nu s-a putut încărca", - "unknown_sender": "Expeditor necunoscut", - "all_day": "Toată ziua" - }, - "form": { - "create_title": "Contact nou", - "edit_title": "Editează contactul", - "section_address_book": "Director", - "select_address_book": "Selectați un director...", - "section_identity": "Nume și identitate", - "contact_type": "Tip de contact", - "type_person": "Persoană", - "type_organization": "Organizare", - "section_work": "Muncă și organizare", - "prefix": "Prefix", - "prefix_placeholder": "Dr., Dl., Dna.", - "given_name": "Prenume", - "middle_name": "Al doilea prenume", - "surname": "Nume de familie", - "suffix": "Sufix", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Pseudonim", - "nickname_placeholder": "Pseudonim", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Telefon", - "phone_placeholder": "+1 234 567 890", - "phone_type": "Tastați", - "phone_voice": "Voce", - "phone_cell": "Mobil", - "phone_fax": "Fax", - "phone_pager": "Pager", - "phone_video": "Video", - "phone_text": "Text", - "organization": "Organizare", - "organization_placeholder": "Numele companiei", - "department": "Departament", - "department_placeholder": "Departament", - "job_title": "Funcția", - "job_title_placeholder": "de ex., inginer software", - "role": "Rol", - "role_placeholder": "de ex., șef de echipă", - "addresses": "Adrese", - "add_address": "Adăugați adresa", - "street": "Stradă", - "city": "Oraș", - "region": "Stat / Regiune", - "postcode": "Cod poștal", - "country": "Țară", - "online_services": "Servicii online", - "add_online_service": "Adăugați un serviciu online", - "url_placeholder": "https://...", - "service_placeholder": "Serviciu", - "anniversaries": "Aniversări", - "add_anniversary": "Adăugați data", - "anniversary_birth": "Ziua de naștere", - "anniversary_wedding": "Aniversare", - "anniversary_death": "Memorial", - "anniversary_other": "Altele", - "personal_info": "Informații personale", - "add_personal_info": "Adăugați o intrare", - "personal_info_placeholder": "de exemplu, Fotografie", - "personal_expertise": "Competențe", - "personal_hobby": "Hobby", - "personal_interest": "Interes", - "personal_other": "Altele", - "level": "Nivel", - "level_high": "Nivel ridicat", - "level_medium": "Nivel mediu", - "level_low": "Nivel scăzut", - "categories": "Categorii", - "categories_placeholder": "de exemplu, Familie, Prieteni, Colegi", - "categories_hint": "Tastați pentru a căuta sau a adăuga categorii", - "category_add": "Adăugați", - "note": "Note", - "note_placeholder": "Adăugați o notă...", - "gender": "Gen", - "gender_sex": "Sex", - "gender_male": "Bărbat", - "gender_female": "Femeie", - "gender_other": "Altele", - "gender_none": "Nu se aplică", - "gender_unknown": "Necunoscut", - "gender_identity": "Identitatea de gen", - "gender_identity_placeholder": "Identitatea de gen...", - "calendar": "Calendar", - "calendar_uri": "URL de calendar", - "scheduling_uri": "Programare URL", - "freebusy_uri": "URL „Liber/Ocupat”", - "context_work": "Muncă", - "context_private": "Privat", - "add_email": "Adăugați un e-mail", - "add_phone": "Adăugați numărul de telefon", - "save": "Salvare", - "cancel": "Anulează", - "creating": "Se creează...", - "updating": "Se actualizează...", - "name_required": "Introduceți un prenume, un nume sau o organizație", - "email_invalid": "Vă rugăm să introduceți o adresă de e-mail validă", - "email_error_inline": "Format de e-mail nevalid", - "save_failed": "Nu s-a putut salva contactul", - "delete": "Șterge", - "upload_photo": "Încărcați o fotografie", - "remove_photo": "Elimină fotografia", - "photo_hint": "JPG sau PNG, până la 10 MB. Dimensiunea imaginii va fi redimensionată.", - "photo_too_large": "Imaginea este prea mare (max. 10 MB)", - "photo_invalid": "Fișier imagine nevalid", - "change_photo": "Modificare" - }, - "groups": { - "create": "Grup nou", - "edit": "Editare grup", - "empty": "Nu există încă grupuri", - "delete_confirm_title": "Șterge grupul", - "delete_confirm": "Sunteți sigur că doriți să ștergeți acest grup?", - "name_label": "Numele grupului", - "name_placeholder": "de exemplu, Echipa, Familia", - "name_required": "Numele grupului este obligatoriu", - "save_failed": "Nu s-a putut salva grupul", - "members_label": "Membri", - "search_members": "Căutați contacte pentru a le adăuga...", - "no_members": "Nu există membri în acest grup", - "member_count": "{count, plural, =0 {Nu există membri} one {1 membru} few {# membri} other {# de membri}}", - "send_email": "Trimite e-mail către grup", - "send_email_to": "Către", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "Acest grup nu are membri cu adresă de e-mail." - }, - "import": { - "title": "Importați contactele", - "drop_hint": "Faceți clic pentru a selecta un fișier de tip „vCard”", - "file_types": "Fișiere .vcf sau .vcard", - "no_contacts": "Nu s-au găsit contacte în fișier", - "parse_error": "Nu s-a putut analiza fișierul „vCard”", - "found": "{count, plural, one {S-a găsit 1 contact} few {# contacte găsite} other {# de contacte găsite}}", - "duplicate": "Duplicare", - "select_all": "Selectează tot", - "deselect_all": "Deselectați tot", - "selected": "{count, plural, one {1 selectat} few {# selectat} other {# de selectat}}", - "import_button": "Import", - "importing": "Se importă...", - "success": "{count, plural, one {1 contact importat} few {# contacte importate} other {# de contacte importate}}", - "failed": "Importul a eșuat", - "close": "Închide", - "file_too_large": "Fișierul este prea mare (max. 5 MB)" - }, - "export": { - "title": "Exportați contactele", - "success": "{count, plural, one {1 contact exportat} few {# contacte exportate} other {# de contacte exportate}}" - }, - "bulk": { - "selected": "{count, plural, one {1 selectat} few {# selectat} other {# de selectat}}", - "select_all": "Selectează tot", - "delete": "Șterge", - "delete_confirm_title": "Ștergeți contactele", - "delete_confirm": "Șterge {count, plural, one {1 contact} few {# contacte} other {# de contacte}}?", - "deleted": "{count, plural, one {1 contact șters} few {# contacte șterse} other {# de contacte șterse}}", - "add_to_group": "Adaugă la grup", - "choose_group": "Alegeți un grup", - "adding_contacts": "Adăugare {count, plural, one {1 contact} few {# contacte} other {# de contacte}}", - "added_to_group": "Contacte adăugate la grup", - "export": "Export", - "clear": "Șterge selecția" - }, - "toast": { - "created": "Contact creat", - "updated": "Date de contact actualizate", - "deleted": "Contact șters", - "error_create": "Nu s-a putut crea contactul", - "error_update": "Nu s-a putut actualiza contactul", - "error_delete": "Nu s-a putut șterge contactul" - }, - "context_menu": { - "open": "Deschide", - "edit": "Editare", - "send_email": "Trimite e-mail", - "add_to_group": "Adaugă la grup", - "export_vcard": "Exportați cvCard", - "delete": "Șterge", - "call": "Apel", - "duplicate": "Duplicare", - "print": "Imprimare" - }, - "filters": { - "toggle": "Filtre", - "select": "Selectați", - "clear": "Ștergeți", - "close": "Închide", - "title": "Filtre avansate", - "organization": "Companie", - "organization_placeholder": "de ex. Acme Corp", - "job_title": "Funcția", - "job_title_placeholder": "de ex. Designer", - "location": "Locație", - "location_placeholder": "Oraș sau țară", - "email_domain": "Domeniul de e-mail", - "email_domain_placeholder": "example.com", - "birthday_month": "Ziua de naștere în", - "any_month": "Orice lună", - "has_email": "Are e-mail", - "has_phone": "Are telefon", - "has_photo": "Are fotografie", - "sort_by": "Sortare după", - "sort_first_name": "Prenume", - "sort_last_name": "Nume de familie" - } - }, - "calendar": { - "title": "Calendar", - "back_to_email": "Înapoi la e-mail", - "back_to_month": "Înapoi la lună", - "my_calendars": "Calendare", - "birthday_calendar": "Zile de naștere", - "mini_calendar_change": "Faceți clic pentru a schimba luna", - "views": { - "month": "Luna", - "week": "Săptămână", - "day": "Zi", - "agenda": "Agendă", - "today": "Astăzi", - "month_hint": "Luna (m)", - "week_hint": "Săptămână (s)", - "day_hint": "Ziua (d)", - "agenda_hint": "Agendă (a)", - "tasks": "Sarcini", - "tasks_hint": "Sarcini (k)" - }, - "events": { - "create": "Creați un eveniment", - "edit": "Editați evenimentul", - "delete": "Ștergere eveniment", - "details": "Detalii despre eveniment", - "no_events": "Fără evenimente", - "all_day": "Toată ziua", - "more": "+{count} mai multe", - "no_title": "(Fără titlu)", - "resize": "Eveniment de redimensionare", - "duplicate": "Duplicare", - "today_header": "Astăzi", - "tomorrow_header": "Mâine", - "agenda_show_earlier": "Afișează evenimentele anterioare", - "agenda_loading": "Se încarcă…", - "agenda_range_start": "Evenimente de la {date}", - "agenda_range_end": "Evenimente până la {date}", - "export_ics": "Exportați ca fișier .ics", - "copy_title": "Copiați titlul", - "copy_link": "Copiați linkul întâlnirii", - "new_event": "Eveniment nou", - "new_all_day_event": "Eveniment nou pe toată durata zilei", - "new_task": "Sarcină nouă", - "go_to_today": "Mergi la astăzi" - }, - "detail": { - "add_note": "Adăugați o notă...", - "save_note": "Salvare", - "note_saved": "Notă adăugată", - "open_link": "Deschideți linkul", - "meeting_link": "Link către întâlnire", - "tentative": "Provizoriu", - "cancelled": "Anulat", - "delete_confirm": "Doriți să ștergeți acest eveniment?" - }, - "form": { - "title": "Titlu", - "description": "Descriere", - "location": "Locație", - "meeting_link": "Link către întâlnire", - "start_date": "Data de începere", - "end_date": "Data de încheiere", - "start_time": "Ora de începere", - "end_time": "Ora de încheiere", - "all_day_event": "Eveniment pe toată durata zilei", - "calendar_select": "Calendar", - "save": "Salvare", - "cancel": "Anulează", - "delete_confirm": "Sunteți sigur că doriți să ștergeți acest eveniment?", - "color": "Culoare" - }, - "participants": { - "availability": { - "title": "Disponibilitate", - "free": "Disponibil", - "busy": "Ocupat", - "tentative": "Provizoriu", - "unknown": "Nu este un utilizator al acestui server", - "checking": "Se verifică…" - }, - "title": "Participanți", - "add": "Adăugați un participant", - "organizer": "Organizator", - "attendee": "Participant", - "accepted": "Acceptat", - "declined": "Respins", - "tentative": "Provizoriu", - "needs_action": "Necesită acțiune", - "remove": "Eliminați", - "edit": "Editare", - "email_placeholder": "Adăugați o adresă de e-mail sau căutați contacte", - "send_invitations": "Trimiteți invitații participanților", - "status_summary": "{accepted} acceptat, {pending} în așteptare", - "invited_by": "Invitație de la {name}", - "respond_below": "Răspundeți folosind butoanele de mai jos", - "rsvp_label": "Răspunsul dvs.", - "cancel_notification": "Participanții vor fi informați cu privire la anulare", - "you_organizer": "Tu ești organizatorul", - "you_attendee": "Sunteți participant", - "no_participants": "Nu există participanți", - "count": "{count, plural, one {# participant} few {# participanți} other {# de participanți}}" - }, - "recurrence": { - "title": "Recurență", - "none": "Nu se repetă", - "daily": "Zilnic", - "weekly": "Săptămânal", - "monthly": "Lunar", - "yearly": "Anual", - "every_n_days": "La fiecare {count} zile", - "every_n_weeks": "La fiecare {count} săptămâni", - "every_n_months": "La fiecare {count} luni", - "until": "Până la", - "occurrences": "{count} ocurențe", - "custom": "Personalizare…", - "edit_custom": "Editați recurența personalizată", - "every_n_years": "La fiecare {count} ani", - "on_days": "pe {days}", - "on_day_n": "în ziua de{day}", - "on_the_nth": "pe {nth} {day}", - "in_month": "în {month}", - "nth_1": "primul", - "nth_2": "al doilea", - "nth_3": "al treilea", - "nth_4": "al patrulea", - "nth_last": "ultimul", - "editor_freq_day": "Zi", - "editor_freq_week": "Săptămână", - "editor_freq_month": "Luna", - "editor_freq_year": "Anul", - "editor_repeats_on": "Se repetă pe", - "editor_every": "Fiecare", - "editor_unit_days": "zi(i)", - "editor_unit_weeks": "săptămână(e)", - "editor_unit_months": "lună(i)", - "editor_unit_years": "an(i)", - "editor_on_day": "în ziua", - "editor_on_the": "pe", - "editor_in": "în", - "editor_ends": "Sfârșit", - "editor_never": "Niciodată", - "editor_ends_on": "Pe", - "editor_ends_after": "După", - "editor_occurrences": "ocurențe" - }, - "recurrence_scope": { - "edit_title": "Editați evenimentul recurent", - "delete_title": "Șterge evenimentul recurent", - "description": "Acesta este un eveniment recurent. Ce evenimente doriți să modificați?", - "this_event": "Numai acest eveniment", - "this_and_future": "Acest eveniment și evenimentele următoare", - "all_events": "Toate evenimentele", - "cancel": "Anulează", - "save": "Salvare", - "delete": "Șterge", - "rsvp_title": "Răspundeți la evenimentul recurent", - "rsvp_description": "Acesta este un eveniment recurent. Pentru ce evenimente se aplică răspunsul dvs.?", - "respond": "Răspundeți" - }, - "alerts": { - "title": "Memento-uri", - "none": "Fără mementouri", - "at_time": "La momentul evenimentului", - "minutes_before": "{count, plural, one {# cu un minut înainte} few {# minute înainte de} other {# de minute înainte de}}", - "hours_before": "{count, plural, one {# cu o oră înainte} few {# cu câteva ore înainte} other {# de cu câteva ore înainte}}", - "days_before": "{count, plural, one {# cu o zi înainte} few {# zile înainte} other {# de zile înainte}}", - "weeks_before": "{count, plural, one {# cu o săptămână înainte} few {# cu câteva săptămâni înainte} other {# de cu câteva săptămâni înainte}}", - "unit_minutes_before": "minute înainte de", - "unit_hours_before": "cu câteva ore înainte", - "unit_days_before": "zile înainte", - "unit_weeks_before": "cu câteva săptămâni înainte", - "add": "Adăugați o notificare", - "remove": "Eliminare memento", - "amount": "Suma de reamintire", - "unit": "Unitatea de reamintire" - }, - "settings": { - "title": "Setări calendar", - "default_view": "Vizualizare implicită", - "week_starts_on": "Săptămâna începe pe", - "time_format": "Formatul orei", - "default_calendar": "Calendarul implicit", - "default_reminder": "Memento implicit", - "time_format_12h": "12 ore", - "time_format_24h": "24 de ore", - "notifications_enabled": "Notificări privind evenimentele", - "notifications_enabled_desc": "Afișați alerte pentru evenimentele viitoare din calendar", - "notification_sound": "Sunet de notificare", - "notification_sound_desc": "Redă un sunet pentru alertele din calendar", - "invitation_parsing": "Analizați invitațiile prin e-mail", - "invitation_parsing_desc": "Detectați invitațiile din calendar din atașamentele de e-mail și afișați acțiunile din calendar", - "show_time_in_month_view": "Afișați ora în vizualizarea lunară", - "show_time_in_month_view_desc": "Afișați orele evenimentelor în vizualizarea calendarului lunar", - "show_week_numbers": "Afișați numerele săptămânilor", - "show_week_numbers_desc": "Afișați numerele săptămânilor în mini-calendar", - "calendar_free_scroll": "Derulare liberă", - "calendar_free_scroll_desc": "Derulează continuu prin luni, săptămâni și zile în loc de o singură perioadă odată", - "enable_tasks": "Activați sarcinile", - "enable_tasks_desc": "Afișați o vizualizare a sarcinilor în calendar pentru gestionarea sarcinilor de îndeplinit", - "show_tasks_on_calendar": "Afișați sarcinile în calendar", - "show_tasks_on_calendar_desc": "Afișați cartonașe cu sarcini în vizualizările calendarului zilnic și săptămânal", - "hover_preview": "Previzualizare la trecerea cursorului peste elemente", - "hover_preview_desc": "Afișați o fereastră pop-up cu detalii atunci când treceți cu mouse-ul peste evenimentele din calendar", - "hover_preview_instant": "Instant", - "hover_preview_delay_500ms": "Întârziere de 0,5 secunde", - "hover_preview_delay_1s": "Întârziere de 1 secundă", - "hover_preview_delay_2s": "Întârziere de 2 secunde", - "hover_preview_off": "Dezactivat", - "organizer_identity": "Organizează invitațiile ca", - "organizer_identity_desc": "Adresa de calendar folosită ca organizator când inviți persoane la evenimente.", - "organizer_identity_failed": "Nu s-a putut schimba adresa organizatorului", - "show_birthday_calendar": "Calendarul zilelor de naștere ale contactelor", - "show_birthday_calendar_desc": "Afișați un calendar virtual cu zilele de naștere ale contactelor dvs." - }, - "days": { - "monday": "Luni", - "tuesday": "Marți", - "wednesday": "Miercuri", - "thursday": "Joi", - "friday": "Vineri", - "saturday": "Sâmbătă", - "sunday": "Duminică", - "mon": "Luni", - "tue": "Marți", - "wed": "Miercuri", - "thu": "Joi", - "fri": "Vineri", - "sat": "Sâmbătă", - "sun": "Duminică" - }, - "months": { - "jan": "ianuarie", - "feb": "feb", - "mar": "Mar", - "apr": "Apr", - "may": "Poate", - "jun": "Iunie", - "jul": "iulie", - "aug": "aug", - "sep": "Sep", - "oct": "Oct", - "nov": "Nov", - "dec": "Dec", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Eveniment creat", - "event_updated": "Eveniment actualizat", - "event_deleted": "Eveniment șters", - "calendar_created": "Calendar creat", - "calendar_deleted": "Calendar șters", - "event_move_error": "Nu s-a putut muta evenimentul", - "event_resize_error": "Eșec la redimensionarea evenimentului", - "alert_title": "Eveniment viitor", - "alert_now": "Începând de acum", - "alert_in_minutes": "În {count} min", - "invitation_sent": "Invitații trimise", - "invitations_denied_title": "Invitațiile nu pot fi trimise", - "invitations_denied": "Serverul a refuzat trimiterea invitațiilor: {reason}. Salvezi evenimentul fără a le trimite?", - "save_without_invitations": "Salvează fără invitații", - "rsvp_updated": "Răspuns actualizat", - "rsvp_error": "Nu s-a putut actualiza răspunsul", - "event_duplicated": "Eveniment duplicat", - "event_error": "Nu s-a putut salva evenimentul", - "task_due": "Termen de finalizare a sarcinii", - "event_exported": "Eveniment exportat", - "title_copied": "Titlu copiat", - "link_copied": "Link copiat" - }, - "status": { - "loading_calendars": "Se încarcă calendarele...", - "loading_events": "Se încarcă evenimente..." - }, - "quick_create": { - "placeholder": "Titlul unui eveniment nou", - "aria_label": "Creare rapidă a unui eveniment" - }, - "nav_prev": "Anterior", - "nav_next": "Următorul", - "nav_open_menu": "Deschide meniul", - "import": { - "title": "Importare calendar", - "tab_file": "Fișier", - "tab_url": "URL", - "select_file": "Selectați fișierul .ics", - "drop_file": "sau trageți fișierul aici", - "supported_formats": "Suportă fișiere de tip „iCalendar” (.ics)", - "url_description": "Introduceți adresa URL a unui flux extern iCalendar (.ics) pentru a importa evenimente.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Suportă adresele URL de tip iCalendar (.ics)", - "fetch": "Preluare", - "invalid_url": "Vă rugăm să introduceți o adresă de e-mail validă URL", - "url_fetch_failed": "Nu s-a putut prelua calendarul de la URL", - "parsing": "Se analizează fișierul de calendar...", - "parsed_events": "{count} evenimente găsite", - "no_events": "Nu s-au găsit evenimente în fișier", - "select_all": "Selectează tot", - "deselect_all": "Deselectați tot", - "target_calendar": "Import în calendar", - "import_button": "Importați elementele selectate", - "importing": "Se importă evenimente...", - "success": "{count} evenimentele au fost importate cu succes", - "error": "Importul calendarului a eșuat", - "file_too_large": "Fișierul depășește limita de 10 MB", - "invalid_format": "Format de fișier calendar invalid" - }, - "webcal_action": { - "title": "Deschideți linkul către calendar", - "description": "Cum ați dori să utilizați „{name}”?", - "import_title": "Importați o singură dată", - "import_description": "Preia evenimentele acum și copiază-le într-unul dintre calendarele tale.", - "subscribe_title": "Abonează-te", - "subscribe_description": "Sincronizați automat acest calendar ca un calendar separat.", - "cancel": "Anulează" - }, - "management": { - "title": "Gestionarea calendarului", - "description": "Creați, redenumiți și personalizați-vă calendarele. Faceți clic dreapta pe un calendar din bara laterală pentru a-i schimba rapid culoarea.", - "name": "Nume", - "name_placeholder": "Numele calendarului", - "color": "Culoare", - "change_color": "Schimbă culoarea", - "random_color": "Culoare aleatorie nouă", - "add_calendar": "Adăugați calendar", - "edit": "Editare", - "delete": "Șterge", - "save": "Salvare", - "create": "Creați", - "cancel": "Anulează", - "default": "Implicit", - "confirm_delete": "Doriți să ștergeți „{name}”? Toate evenimentele din acest calendar vor fi eliminate.", - "confirm_clear": "Ștergeți toate evenimentele din „{name}”? Această acțiune nu poate fi anulată.", - "set_default": "Setați ca implicită", - "default_updated": "Calendarul implicit a fost actualizat", - "error_default": "Nu s-a reușit setarea calendarului implicit", - "clear_events": "Ștergeți evenimentele", - "events_cleared": "{count} evenimente șterse", - "error_clear": "Nu s-au șters evenimentele din calendar", - "calendar_created": "Calendar creat", - "calendar_updated": "Calendar actualizat", - "calendar_deleted": "Calendar șters", - "color_updated": "Culoarea calendarului a fost actualizată", - "error_create": "Nu s-a putut crea calendarul", - "error_update": "Nu s-a putut actualiza calendarul", - "error_delete": "Nu s-a putut șterge calendarul", - "caldav_url": "CalDAV URL", - "copy_url": "Copiați CalDAV URL", - "url_copied": "CalDAV URL copiat în clipboard", - "share": "Partajare calendar", - "new_event_in_calendar": "Eveniment nou în acest calendar", - "kind": "Conținut", - "kind_events": "Evenimente", - "kind_tasks": "Sarcini", - "kind_both": "Evenimente și sarcini", - "kind_hint": "Stabilește ce aplicații văd acest calendar când este sincronizat prin CalDAV. Nu poate fi schimbat ulterior." - }, - "subscription": { - "title": "iCal Abonament", - "section_title": "iCal Abonamente", - "description": "Abonați-vă la un flux extern de tip iCalendar. Evenimentele vor fi sincronizate automat în propriul calendar. Suportă adresele URL de tip https:// și webcal://.", - "url_label": "URL de calendar", - "url_placeholder": "https://example.com/calendar.ics sau webcal://...", - "name_label": "Numele calendarului", - "name_placeholder": "de ex. Zile de sărbătoare legală", - "color_label": "Culoare", - "refresh_interval": "Interval de reîmprospătare", - "interval_15": "La fiecare 15 minute", - "interval_30": "La fiecare 30 de minute", - "interval_60": "La fiecare oră", - "interval_360": "La fiecare 6 ore", - "interval_1440": "În fiecare zi", - "subscribe": "Abonează-te", - "subscribing": "Abonare...", - "save": "Salvați modificările", - "saving": "Se salvează...", - "edit": "Editare", - "edit_title": "Editați abonamentul", - "updated": "Actualizare „{name}”", - "update_error": "Nu s-a putut actualiza abonamentul", - "invalid_url": "Vă rugăm să introduceți o adresă de e-mail validă URL", - "success": "Abonați la „{name}”", - "error": "Nu s-a putut adăuga abonamentul", - "refresh": "Reîmprospătează acum", - "refresh_success": "Abonamentul a fost actualizat", - "refresh_error": "Nu s-a reușit reîmprospătarea abonamentului", - "unsubscribe": "Dezabonare", - "confirm_delete": "Doriți să vă dezabonați de la „{name}”? Calendarul și toate evenimentele sale vor fi eliminate.", - "deleted": "Abonamentul a fost eliminat", - "delete_error": "Nu s-a putut anula abonamentul", - "last_refreshed": "Ultima actualizare: {time}" - }, - "tasks": { - "label": "Sarcini", - "no_tasks": "Nu există sarcini", - "no_title": "(Fără titlu)", - "mark_complete": "Marcați ca finalizat", - "mark_incomplete": "Marcați ca incomplet", - "filter_all": "Toate", - "filter_pending": "În așteptare", - "filter_completed": "Finalizat", - "filter_overdue": "Restant", - "show_completed": "Afișați cele finalizate", - "create": "Sarcină nouă", - "edit": "Editare sarcină", - "title_placeholder": "Titlul sarcinii", - "description_placeholder": "Adăugați o descriere...", - "due_date": "Termenul limită", - "include_time": "Includeți ora", - "priority": "Prioritate", - "priority_none": "Niciunul", - "priority_high": "Nivel ridicat", - "priority_medium": "Nivel mediu", - "priority_low": "Nivel scăzut", - "progress": "Stare", - "progress_needs_action": "Necesită acțiune", - "progress_in_process": "În curs de realizare", - "progress_completed": "Finalizat", - "progress_cancelled": "Anulat", - "calendar": "Calendar", - "alert": "Memento", - "alert_none": "Niciunul", - "alert_at_time": "La data scadenței", - "alert_5min": "Cu 5 minute înainte", - "alert_15min": "Cu 15 minute înainte", - "alert_30min": "Cu 30 de minute înainte", - "alert_1hr": "Cu o oră înainte", - "alert_1day": "Cu o zi înainte", - "delete": "Șterge", - "cancel": "Anulează", - "save": "Salvare", - "quick_add_placeholder": "Adăugați o sarcină...", - "due_today": "Astăzi", - "due_tomorrow": "Mâine", - "overdue": "Restant" - } - }, - "calendar_event_notifications": { - "someone": "Cineva", - "invited": "{name} te-a invitat la „{title}”", - "updated": "{name} a actualizat „{title}”", - "cancelled": "{name} a anulat „{title}”", - "untitled": "Eveniment fără titlu" - }, - "share_notifications": { - "someone": "Cineva", - "shared": "{name} a partajat cu tine {kind} „{object}”", - "changed": "{name} ți-a modificat accesul la {kind} „{object}”", - "revoked": "{name} ți-a retras accesul la {kind} „{object}”", - "object": { - "folder": "dosarul", - "calendar": "calendarul", - "address_book": "agenda de contacte", - "files": "dosarul de fișiere" - } - }, - "sharing": { - "title": "Distribuie „{name}”", - "description": "Acordați acces altor utilizatori sau grupuri de pe acest server. Modificările intră în vigoare imediat.", - "no_shares": "Nu a fost partajat încă cu nimeni.", - "add_person": "Adăugați o persoană sau un grup", - "search_placeholder": "Căutați după nume sau adresă de e-mail…", - "loading_principals": "Se încarcă utilizatorii…", - "no_principals": "Nu s-au găsit alți utilizatori sau grupuri.", - "no_match": "Nu s-au găsit rezultate.", - "remove": "Eliminați accesul", - "group": "Grup", - "share_added": "Acces acordat", - "share_updated": "Acces actualizat", - "share_removed": "Accesul a fost eliminat", - "share_failed": "Nu s-a putut actualiza partajarea", - "preset": { - "freeBusy": "Doar disponibilitate", - "read": "Doar citire", - "readWrite": "Citire și scriere", - "manager": "Manager", - "custom": "Personalizat" - } - }, - "advanced_search": { - "title": "Căutare avansată", - "from": "De la", - "from_placeholder": "Adresa de e-mail sau numele expeditorului", - "to": "Către", - "to_placeholder": "Adresa de e-mail sau numele destinatarului", - "subject": "Subiect", - "subject_placeholder": "Subiectul conține...", - "body": "Corpul mesajului", - "body_placeholder": "Corpul conține...", - "folder": "Dosar", - "all_folders": "Toate folderele", - "has_attachment": "Anexe", - "date_after": "După", - "date_before": "Înainte", - "size_min": "Mai mare de (KB)", - "size_max": "Mai mic de (KB)", - "starred": "Cu stea", - "unread": "Necitit", - "read": "Citiți", - "yes": "Da", - "no": "Nu", - "clear": "Ștergeți", - "clear_all": "Șterge tot", - "filters_active": "{count} filtru", - "filters_active_plural": "{count} filtre", - "toggle_filters": "Mai mult", - "search_hint": "Folosiți filtre avansate pentru o căutare precisă", - "advanced_filters_tooltip": "Filtre de căutare avansate", - "results_found": "{count, plural, =0 {Nu s-au găsit rezultate} one {# rezultat găsit} few {# rezultate găsite} other {# de rezultate găsite}}", - "results_found_more": "{count}+ rezultate găsite", - "suggestions_recent": "Căutări recente", - "suggestions_people": "Persoane", - "suggestions_from": "Caută e-mailuri de la {address}", - "suggestions_to": "Caută e-mailuri către {address}", - "suggestions_remove_recent": "Elimină din căutările recente" - }, - "welcome": { - "title": "Bine ați venit în căsuța dvs. poștală", - "tip_compose": "Apăsați tasta c pentru a redacta un e-mail nou", - "tip_shortcuts": "Apăsați tasta „?” pentru a vedea toate comenzile rapide de la tastatură", - "tip_sidebar": "Găsiți Calendar, Contacte și Setări în meniul din bara laterală", - "tip_settings": "Personalizați-vă experiența în Setări", - "got_it": "Am înțeles", - "settings": "Setări", - "dismiss": "Ignoră", - "start_tour": "Începe turul" - }, - "demo_welcome": { - "title": "Bine ați venit la Bulwark Mail", - "description": "Explorați un client de e-mail web cu funcționalități complete – direct în browserul dvs. Toate datele rămân pe dispozitivul dvs., așa că nu ezitați să testați totul.", - "feature_email": "Citiți și redactați e-mailuri", - "feature_organize": "Etichete, stele și dosare", - "feature_shortcuts": "Comenzi rapide de la tastatură", - "feature_privacy": "Demo 100% privat", - "hint": "Faceți clic pe orice e-mail din partea stângă pentru a începe sau parcurgeți turul de mai jos." - }, - "files": { - "office_edit": "Editează documentul", - "office_loading": "Se deschide editorul de documente…", - "office_error": "Editorul de documente nu a putut fi deschis.", - "office_close": "Închide editorul", - "open_in_new_tab": "Deschide într-o filă nouă", - "title": "Fișiere", - "search_placeholder": "Căutare fișiere...", - "empty_state_title": "Nu există încă fișiere", - "empty_state_description": "Încărcați fișiere sau creați foldere pentru a începe", - "upload": "Încărcare", - "upload_files": "Încărcați fișiere", - "new_folder": "Dosar nou", - "new_folder_name": "Numele folderului", - "rename": "Redenumire", - "rename_title": "Redenumire", - "new_name": "Nume nou", - "name_forbidden_chars": "Serverul nu permite aceste caractere în nume: {chars}", - "name_reserved": "Acest nume este rezervat pe server.", - "delete": "Șterge", - "delete_confirm_title": "Ștergeți resursa", - "delete_confirm_message": "Sunteți sigur că doriți să ștergeți „{name}”? Această acțiune nu poate fi anulată.", - "download": "Descărcați", - "name": "Nume", - "size": "Dimensiune", - "modified": "Modificat", - "type": "Tastați", - "folder": "Dosar", - "file": "Fișier", - "parent_directory": "Directorul părinte", - "breadcrumb_root": "Acasă", - "other_accounts": "Alte conturi", - "no_accounts": "Nu există conturi conectate.", - "drop_files_here": "Trageți fișierele sau folderele aici pentru a le încărca", - "uploading": "Se încarcă...", - "upload_success": "{count, plural, one {1 fișier încărcat} few {# fișiere încărcate} other {# de fișiere încărcate}}", - "upload_error": "Nu s-a reușit încărcarea fișierului", - "create_folder_success": "Folder creat", - "create_folder_error": "Nu s-a putut crea folderul", - "delete_success": "Șters cu succes", - "delete_error": "Nu s-a putut șterge", - "rename_success": "Redenumit cu succes", - "rename_error": "Nu s-a putut redenumi", - "download_error": "Descărcarea a eșuat", - "not_available": "Stocarea fișierelor nu este disponibilă pe acest server", - "cancel": "Anulează", - "create": "Creați", - "save": "Salvare", - "no_results": "Nu există fișiere care să corespundă căutării dvs.", - "batch_delete_confirm_message": "Sunteți sigur că doriți să ștergeți {count, plural, one {1 element} few {# elemente} other {# de elemente}}? Această acțiune nu poate fi anulată.", - "batch_delete_success": "{count, plural, one {1 element șters} few {# elemente șterse} other {# de elemente șterse}}", - "grid_view": "Vizualizare tabelară", - "list_view": "Vizualizare listă", - "details": "Detalii", - "path": "Cale", - "preview": "Previzualizare", - "preview_error": "Nu s-a putut încărca previzualizarea", - "cut": "Tăiere", - "copy": "Copiere", - "paste": "Lipiți", - "move_success": "{count, plural, one {1 element mutat} few {# elemente mutate} other {# de elemente mutate}}", - "move_error": "Nu s-a putut muta", - "paste_success": "S-a lipit cu succes", - "paste_error": "Nu s-a putut insera", - "new_text_file": "Fișier text nou", - "file_name": "Numele fișierului", - "retry": "Reîncearcă", - "refresh": "Reîmprospătează", - "toggle_favorite": "Comută la favorite", - "duplicate": "Duplicare", - "duplicate_success": "Duplicat cu succes", - "duplicate_error": "Nu s-a putut duplica", - "share": "Distribuie", - "shared": "Partajat", - "shared_with_me": "Partajat cu mine", - "shared_by": "Distribuit de {name}", - "create_file_success": "Fișier creat", - "create_file_error": "Nu s-a putut crea fișierul", - "favorites": "Favorite", - "recent": "Recent", - "properties": "Proprietăți", - "open_folder": "Deschide folderul", - "upload_folder": "Încărcare folder", - "file_too_large": "„{name}” depășește dimensiunea maximă a fișierului ({max})", - "undo": "Anulează", - "undo_success": "Acțiune anulată", - "undo_error": "Nu s-a putut anula", - "toolbar": "Acțiuni asupra fișierelor", - "open_folder_tree": "Deschideți arborele de foldere", - "file_list": "Fișiere și foldere", - "context_menu": "Acțiuni", - "settings_title": "Setări fișier", - "settings_display": "Afișare", - "settings_default_view": "Vizualizare implicită", - "settings_default_view_desc": "Alegeți între afișarea sub formă de grilă și cea sub formă de listă", - "settings_default_sort": "Sortare implicită", - "settings_default_sort_desc": "Alegeți sortarea implicită pentru fișiere", - "settings_sort_direction": "Direcția de sortare", - "settings_sort_direction_desc": "Alegeți ordinea crescătoare sau descrescătoare", - "settings_ascending": "În ordine crescătoare", - "settings_descending": "Descendent", - "settings_icons": "Pictograme", - "settings_show_icons": "Afișați pictogramele fișierelor", - "settings_show_icons_desc": "Afișați pictograme lângă fișiere și foldere", - "settings_colored_icons": "Pictograme colorate", - "settings_colored_icons_desc": "Folosiți pictograme colorate în locul celor monocrome", - "settings_show_thumbnails": "Afișează miniaturi", - "settings_show_thumbnails_desc": "Afișați previzualizări ale imaginilor în locul pictogramelor pentru fișierele imagine", - "settings_behavior": "Comportament", - "settings_show_hidden": "Afișează fișierele ascunse", - "settings_show_hidden_desc": "Afișați fișierele și folderele care încep cu un punct", - "settings_folder_layout": "Navigarea în dosare", - "settings_folder_layout_desc": "Alegeți modul de afișare a folderelor: alături de fișiere sau într-o structură arborescentă din bara laterală", - "settings_folder_layout_inline": "În text", - "settings_folder_layout_sidebar": "Bara laterală", - "disabled_title": "Funcția de fișiere este dezactivată de administratorul dvs.", - "disabled_description": "Încărcarea fișierelor de dimensiuni mari prin intermediul WebDAV poate provoca instabilitate în Stalwart /RocksDB, inclusiv blocări din cauza epuizării memoriei și utilizare irecuperabilă a spațiului pe disc. Este posibil ca fișierele șterse să nu fie eliminate imediat din spațiul de stocare blob. Această funcție nu este recomandată pentru mediile de producție.", - "stability_warning": "Încărcarea fișierelor de dimensiuni mari poate provoca instabilitatea serverului. Este posibil ca fișierele șterse să nu fie eliminate imediat din spațiul de stocare. Utilizați cu precauție.", - "migration_title": "Se actualizează fișierele...", - "migration_description": "Organizarea dosarelor și a fișierelor în structura corespunzătoare. Această operațiune se efectuează o singură dată." - }, - "smime": { - "your_certificates": "Certificatele dvs.", - "your_certificates_desc": "Importați și gestionați certificatele S/MIME pentru semnarea și criptarea e-mailurilor", - "recipient_certificates": "Certificate ale destinatarilor", - "recipient_certificates_desc": "Certificate publice pentru criptarea e-mailurilor către destinatari", - "identity_bindings": "Combinații de taste pentru identitate", - "identity_bindings_desc": "Asociați certificatelS/MIMEe identităților dvs. de e-mail", - "defaults_title": "Setări implicite", - "defaults_desc": "Configurați comportamentul implicit de semnare și criptare", - "import_pkcs12": "Importați fișiere PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importați certificatul", - "no_certificates": "Nu s-au importat încă certificate", - "no_recipient_certs": "Fără certificate ale destinatarilor", - "expires": "Expiră", - "expired": "Expirat", - "bound_to": "Legat de", - "no_key_bound": "Niciunul", - "lock": "Tasta de blocare", - "unlock": "Cheia de deblocare", - "details": "Vedeți detalii", - "delete": "Șterge", - "encrypt_by_default": "Criptați în mod implicit", - "encrypt_by_default_desc": "Criptați automat e-mailurile atunci când toți destinatarii dețin certificate", - "remember_unlocked": "Rețineți cheile deblocate", - "remember_unlocked_desc": "Păstrați tastele deblocate pe durata acestei sesiuni de navigare", - "sign_default_for": "Semnați în mod implicit pentru", - "enter_p12_passphrase": "Introduceți fraza de acces PKCS # 12", - "p12_passphrase_desc": "Introduceți fraza de acces care protejează acest fișier de certificat", - "enter_storage_passphrase": "Setați fraza de acces pentru stocare", - "storage_passphrase_desc": "Alegeți o frază de acces pentru a proteja această cheie stocată în browserul dvs.", - "next": "Următorul", - "import": "Import", - "unlock_key": "Cheia de deblocare", - "unlock_key_desc": "Introduceți fraza de acces la spațiul de stocare pentru a debloca această cheie în vederea semnării sau decriptării", - "passphrase_placeholder": "Introduceți fraza de acces", - "confirm_passphrase_placeholder": "Confirmă fraza de acces", - "passphrase_mismatch": "Frazele de acces nu se potrivesc", - "cancel": "Anulează", - "processing": "Se procesează…", - "close": "Închide", - "certificate_details": "Detalii certificat", - "cert_subject": "Subiect", - "cert_issuer": "Emitent", - "cert_email": "E-mail", - "cert_serial": "Număr de serie", - "cert_validity": "Valabilitate", - "cert_fingerprint": "Amprentă digitală (SHA-256)", - "cert_algorithm": "Algoritm", - "cert_capabilities": "Capacități", - "cert_source": "Sursă", - "cert_expired": "Acest certificat a expirat", - "cert_not_yet_valid": "Acest certificat nu este încă valabil", - "cap_sign": "Semnarea", - "cap_encrypt": "Criptare", - "cap_none": "Niciunul", - "show_passphrase": "Afișează fraza de acces", - "hide_passphrase": "Ascunde fraza de acces", - "sign_toggle": "Semn", - "encrypt_toggle": "Criptați", - "missing_recipient_certs": "Certificate lipsă pentru: {emails}", - "missing_sender_cert": "Nu există niciun certificat asociat acestei identități", - "status_encrypted_ok": "Acest mesaj a fost criptat", - "status_encrypted_no_key": "Acest mesaj este criptat, dar nu s-a găsit nicio cheie corespunzătoare", - "status_encrypted_failed": "Nu s-a reușit decriptarea acestui mesaj", - "status_signed_valid": "Semnătură verificată", - "status_signed_invalid": "Verificarea semnăturii a eșuat", - "status_signed_expired_cert": "Semnat cu un certificat expirat", - "status_signed_self_signed": "Semnătura este validă, dar certificatul este autosemnat (nu este de încredere)", - "status_signed_mismatch": "Semnătura este validă, dar semnatarul nu corespunde cu expeditorul", - "status_unsupported": "Format „S/MIME” neacceptat", - "auto_import_signer_certs": "Import automat al certificatelor semnatarilor", - "auto_import_signer_certs_desc": "Salvați automat certificatele din e-mailurile semnate și verificate pentru criptarea ulterioară", - "export": "Export", - "enter_export_passphrase": "Setați fraza de acces pentru export", - "export_passphrase_desc": "Alegeți o frază de acces pentru a proteja fișierul PKCS # 12 exportat", - "export_storage_desc": "Introduceți fraza de acces la spațiul de stocare pentru a decripta cheia în vederea exportului", - "incorrect_passphrase": "Parolă incorectă" - }, - "tour": { - "step_counter": "Pasul {current} din {total}", - "skip": "Sari peste turul de prezentare", - "back": "Înapoi", - "next": "Următorul", - "finish": "Finalizare", - "take_a_tour": "Faceți un tur al interfeței", - "restart_title": "Tur de prezentare", - "restart_desc": "Vizionați din nou prezentarea ghidată a interfeței", - "restart_button": "Reîncepe turul", - "show_on_new_devices_title": "Afișare pe dispozitive noi", - "show_on_new_devices_desc": "Redați din nou bannerul de bun venit și turul de prezentare la prima conectare pe un dispozitiv nou, chiar dacă le-ați parcurs deja în altă parte", - "sidebar_title": "Căsuțele dvs. de e-mail", - "sidebar_desc": "Aceasta este bara laterală cu dosare. Faceți clic pe orice căsuță poștală pentru a vizualiza e-mailurile din aceasta. Puteți crea dosare, muta e-mailuri între ele și vedeți dintr-o privire numărul de mesaje necitite.", - "compose_title": "Redactați un e-mail", - "compose_desc": "Faceți clic aici pentru a scrie un e-mail nou. Puteți adăuga destinatari, atașamente și puteți utiliza formatarea textului îmbogățit.", - "search_title": "Căutați în e-mailurile dvs.", - "search_desc": "Căutați după expeditor, subiect sau conținut. Faceți clic pe pictograma de filtrare pentru opțiuni avansate, cum ar fi intervalul de date, atașamentele și mesajele marcate cu stea.", - "email_list_title": "Lista dvs. de e-mailuri", - "email_list_desc": "Aici apar e-mailurile. Faceți clic pe unul pentru a-l citi în partea dreaptă. Utilizați caseta de selectare pentru a selecta mai multe e-mailuri, apoi mutați-le, ștergeți-le sau etichetați-le în bloc.", - "email_viewer_title": "Panoul de citire", - "email_viewer_desc": "E-mailul selectat se deschide aici. Răspundeți, redirecționați, arhivați sau ștergeți folosind butoanele din bara de instrumente. De asemenea, puteți marca e-mailurile cu stea sau adăuga etichete colorate.", - "keywords_title": "Etichete colorate", - "keywords_desc": "Organizați-vă e-mailurile cu etichete codificate pe culori. Trageți un e-mail peste o etichetă pentru a-l eticheta sau faceți clic dreapta pe un e-mail pentru a-i atribui etichete.", - "calendar_title": "Calendar", - "calendar_desc": "Treceți la calendar pentru a vă gestiona evenimentele. Creați evenimente, setați mementouri și vizualizați vizualizările pe zi, săptămână sau lună.", - "contacts_title": "Contacte", - "contacts_desc": "Aici se află agenda dvs. de adrese. Importați contacte, creați grupuri și faceți clic pe orice contact pentru a vedea detaliile complete ale acestuia.", - "settings_title": "Setări", - "settings_desc": "Personalizați totul: tema, densitatea, semnăturile, filtrele, comenzile rapide de la tastatură, setările implicite ale calendarului și multe altele.", - "shortcuts_title": "Comenzi rapide de la tastatură", - "shortcuts_desc": "Utilizatorii avansați adoră această funcție. Apăsați tasta „?” oricând pentru a vedea toate comenzile rapide disponibile. Puteți naviga, redacta și gestiona e-mailurile fără a atinge mouse-ul.", - "compose_open_title": "Fereastra de redactare", - "compose_open_desc": "Aceasta este fereastra de redactare a e-mailurilor. Adăugați destinatari, scrieți mesajul, atașați fișiere și folosiți formatarea de text îmbogățit. De asemenea, puteți salva ciorne și utiliza șabloane.", - "calendar_view_title": "Calendarul dvs.", - "calendar_view_desc": "Iată calendarul dvs. cu exemple de evenimente. Puteți comuta între vizualizările de zi, săptămână, lună și agendă folosind bara de instrumente.", - "create_event_title": "Creați un eveniment", - "create_event_desc": "Faceți clic pe acest buton pentru a crea un eveniment nou în calendar. Puteți seta un titlu, data, ora și puteți adăuga participanți.", - "event_modal_title": "Detalii despre eveniment", - "event_modal_desc": "Iată formularul evenimentului. Completați titlul, alegeți o dată și o oră, adăugați o locație sau participanți. Apăsați „Salvați” când ați terminat – sau închideți-l și continuați.", - "contacts_list_title": "Contactele tale", - "contacts_list_desc": "Iată contactele dvs. Faceți clic pe orice contact pentru a vedea detaliile complete ale acestuia în partea dreaptă. De asemenea, puteți crea contacte noi, importa vCard-uri sau organiza contactele în grupuri.", - "settings_tabs_title": "Meniul Setări", - "settings_tabs_desc": "Iată toate categoriile de setări. Personalizați aspectul, gestionați identitățile, configurați filtrele de e-mail, configurați calendarul și multe altele.", - "files_title": "Stocarea fișierelor", - "files_desc": "Browserul de fișiere vă permite să încărcați, să organizați și să partajați fișiere – ca un spațiu de stocare personal în cloud integrat în serviciul de e-mail.", - "demo_banner_title": "Controale demo", - "demo_banner_desc": "Vă aflați în modul demo – totul rămâne în browserul dvs. Apăsați „Resetare demo” oricând pentru a începe de la zero cu date de exemplu curate.", - "quota_title": "Utilizarea spațiului de stocare", - "quota_desc": "Urmăriți aici dimensiunea căsuței dvs. de e-mail. Cercul se umple pe măsură ce utilizați mai mult spațiu." - }, - "unified_mailbox": { - "search_unavailable": "Funcția de căutare nu este disponibilă în vizualizarea unificată" - }, - "quote_header": { - "reply_line": "Pe {date}, {from} a scris:", - "forwarded_separator": "---------- Mesaj redirecționat ----------", - "from_label": "De la", - "date_label": "Data", - "subject_label": "Subiect" - }, - "pwa_install": { - "title": "Instalați {appName}", - "description": "Instalați aplicația noastră pentru acces rapid și asistență offline.", - "not_now": "Nu acum", - "install": "Instalați", - "dont_remind": "Nu-mi mai reaminti", - "dismiss_aria": "Ignorați solicitarea de instalare" - }, - "deep_link": { - "message_not_found": "Acest mesaj nu mai este disponibil.", - "thread_not_found": "Această conversație nu mai este disponibilă.", - "folder_not_found": "Acest folder nu mai este disponibil.", - "event_not_found": "Acest eveniment nu mai este disponibil.", - "file_not_found": "Acest fișier nu mai este disponibil.", - "account_unavailable": "Conectați-vă la acel cont pentru a deschide linkul.", - "copy_message": "Copiați linkul mesajului", - "copy_conversation": "Copiați linkul conversației", - "copy_event": "Copiați linkul evenimentului", - "copied": "Link copiat", - "copy_failed": "Linkul nu a putut fi copiat" - }, - "plugins": { - "oauth_callback": { - "completing": "Se finalizează autorizarea…", - "completing_message": "Vă rugăm să așteptați stabilirea conexiunii.", - "received_title": "Autorizare primită", - "received_message": "Vă puteți întoarce la fila în care ați inițiat conexiunea.", - "failed_title": "Autorizarea a eșuat", - "denied": "Autorizarea a fost refuzată.", - "failed_detail": "Autorizarea a eșuat: {error}", - "missing_params": "Lipsește codul de autorizare sau starea.", - "delivery_failed": "Rezultatul autorizării nu a putut fi transmis filei aplicației. Închideți această fereastră și încercați din nou." - } - }, - "global_search": { - "title": "Caută în tot", - "placeholder": "Caută în mail, contacte, calendar, fișiere...", - "scope_label": "Caută în", - "account_label": "Cont", - "scope_all": "Tot", - "scope_mail": "E-mail", - "scope_contacts": "Contacte", - "scope_calendar": "Calendar", - "scope_files": "Fișiere", - "all_accounts": "Toate conturile", - "show_all": "Afișează toate rezultatele", - "no_results": "Niciun rezultat pentru „{query}”", - "server_hint": "Cuvintele complete caută pe server; fragmentele de cuvinte se potrivesc cu elementele deja încărcate", - "searching": "Se caută...", - "account_error": "Nu s-a putut căuta în {account}", - "recent_searches": "Căutări recente", - "recurring": "Recurent", - "no_subject": "(no subject)", - "empty_hint": "Caută mail, contacte, evenimente și fișiere în toate conturile. Încearcă from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Plat după dată", - "grouped": "Grupat", - "load_more": "Încarcă mai multe", - "preview_empty": "Selectează un rezultat pentru previzualizare", - "open_full": "Deschide", - "view_label": "Vizualizare" - } -} +{ + "meta_description": "Client de webmail minimalist care utilizează protocolul JMAP", + "login": { + "title": "Webmail", + "username_label": "E-mail", + "username_placeholder": "user@example.com", + "password_label": "Parolă", + "password_placeholder": "Introduceți parola", + "jmap_endpoint_label": "JMAP Server", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Serverul trebuie să permită cererile CORS provenite de la acest domeniu.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Server selectat din domeniul dvs. de e-mail.", + "sign_in": "Autentificare", + "signing_in": "Autentificare în curs...", + "loading": "Se încarcă...", + "reconnecting": "Conexiune pierdută. Se încearcă reconectarea…", + "error": { + "invalid_credentials": "Adresă de e-mail sau parolă invalidă. Vă rugăm să verificați datele de autentificare și să încercați din nou.", + "invalid_token": "Serverul nu a acceptat acest token de acces. Verificați dacă este complet și dacă nu a expirat sau nu a fost revocat.", + "connection_failed": "Nu se poate stabili conexiunea cu serverul. Verificați conexiunea la internet și încercați din nou.", + "cors_blocked": "Serverul este accesibil, dar blochează cererile de tip „cross-origin”. Verificați setările CORS ale serverului JMAP și permiteți acest domeniu.", + "server_error": "Serverul este temporar indisponibil. Vă rugăm să încercați din nou mai târziu.", + "generic": "A apărut o eroare neașteptată. Dacă problema persistă, contactați administratorul.", + "totp_required": "Este necesar un cod de autentificare în doi pași. Vă rugăm să introduceți codul mai jos.", + "totp_invalid": "Cod de autentificare invalid. Vă rugăm să verificați aplicația de autentificare și să încercați din nou.", + "oauth_discovery_failed": "SSO este activată, dar furnizorul de identitate nu a putut fi contactat. Verificați configurația OAuth.", + "token_exchange_failed": "Parola și codul au fost acceptate, dar serverul de e-mail a refuzat să pornească o sesiune pentru această aplicație. Cereți administratorului să verifice setările clientului OAuth." + }, + "show_password": "Afișează parola", + "hide_password": "Ascunde parola", + "totp_toggle": "Am un cod 2FA", + "token_toggle": "Autentificare cu un token de acces", + "password_toggle": "Autentificare cu o parolă", + "token_label": "Token de acces", + "token_placeholder": "Lipiți tokenul API", + "token_hint": "Creați un token API în setările furnizorului dvs. de e-mail și lipiți-l aici.", + "remember_me": "Reține-mă", + "config_error": { + "title": "Eroare de configurare", + "fetch_failed": "Nu se poate încărca configurația aplicației. Vă rugăm să încercați din nou mai târziu.", + "server_not_configured": "Serverul de e-mail nu a fost configurat. Vă rugăm să contactați administratorul." + }, + "remove_from_history": "Elimină din istoric", + "totp_label": "Cod de autentificare", + "totp_placeholder": "000000", + "session_expired": "Sesiunea dvs. a expirat. Vă rugăm să vă autentificați din nou.", + "dismiss": "Ignoră", + "or": "sau", + "sign_in_sso": "Conectați-vă cu SSO", + "add_account_title": "Adăugare cont", + "add_account_subtitle": "Conectați-vă cu un alt cont", + "cancel": "Anulează", + "website": "Site web", + "imprint": "Mențiuni legale", + "privacy_policy": "Politica de confidențialitate", + "try_demo": "Încercați demo-ul", + "demo_description": "Explorați cu date de exemplu – nu este necesar un cont", + "demo_launching": "Se lansează demonstrația...", + "demo_login_button": "Lansați demonstrația", + "demo_tagline": "Descoperiți un client de e-mail cu funcționalități complete. Nu este necesar un cont.", + "demo_no_signup": "Nu este necesară înregistrarea – explorați liber cu date de exemplu", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Se finalizează autentificarea...", + "oauth_error": { + "title": "Autentificare eșuată", + "invalid_state": "Validarea de securitate a eșuat. Vă rugăm să încercați să vă autentificați din nou.", + "missing_params": "Lipsesc datele de autentificare. Vă rugăm să încercați să vă autentificați din nou.", + "token_exchange_failed": "Autentificarea nu a reușit. Vă rugăm să încercați din nou.", + "access_denied": "Accesul a fost refuzat. Vă rugăm să contactați administratorul.", + "back_to_login": "Înapoi la autentificare" + } + }, + "sidebar": { + "close": "Închide", + "compose": "Compune", + "compose_hint": "Compune (c)", + "search_placeholder": "Căutare în e-mail...", + "search_placeholder_hint": "Căutare în e-mail... (apăsați /)", + "storage": "Stocare", + "storage_used": "Utilizat", + "storage_free": "Gratuit", + "storage_total": "Total", + "sign_out": "Deconectare", + "sign_out_of": "Deconectați-vă de pe {account}", + "sign_out_all": "Deconectați-vă de la toate conturile", + "add_account": "Adăugare cont", + "set_as_default": "Setați ca implicită", + "switch_account": "Schimbă contul", + "contacts": "Contacte", + "calendar": "Calendar", + "settings": "Setări", + "admin": "Admin", + "files": "Fișiere", + "loading_mailboxes": "Se încarcă căsuțele poștale...", + "push_connected": "Actualizări în timp real active", + "push_disconnected": "Actualizări în timp real inactive", + "keyboard_shortcuts": "Comenzi rapide de la tastatură", + "theme": { + "light": "Modul luminos", + "dark": "Modul întunecat", + "system": "Tema sistemului" + }, + "language": { + "title": "Limba" + }, + "mailboxes": { + "inbox": "Primite", + "sent": "Trimise", + "drafts": "Ciorne", + "trash": "Coș de gunoi", + "archive": "Arhivă", + "starred": "Cu stea", + "all_mail": "Toate mesajele", + "spam": "Spam", + "important": "Important" + }, + "unified_inbox": "Căsuță de intrare unificată", + "unified_sent": "Toate mesajele trimise", + "unified_drafts": "Toate ciorne", + "unified_trash": "Toate coșul de gunoi", + "unified_archive": "Arhivă completă", + "unified_junk": "Toate mesajele nedorite", + "all_accounts": "Toate conturile", + "all_accounts_loading": "Se încarcă conturile: {loaded} din {total}", + "unified_mailbox": "Căsuță poștală unificată", + "expand": "Extindeți", + "collapse": "Reduceți", + "expand_tooltip": "Extindeți", + "collapse_tooltip": "Reduceți", + "mobile": { + "search": "Căutare", + "compose": "Compune", + "go_back": "Înapoi", + "toggle_menu": "Comută meniul" + }, + "clear_search": "Șterge căutarea", + "vacation_active": "Răspunsul automat de vacanță este activ", + "demo_banner": "Modul demo", + "demo_reset": "Resetare", + "demo_tour": "Tur de prezentare", + "tags": "Etichete", + "show_all_tags": "Afișează tot ({count})", + "show_fewer_tags": "Afișează mai puține", + "folders": "Dosare", + "your_folders": "Dosarele tale", + "create_folder": "Creează dosar", + "show_more": "Mai multe", + "show_fewer": "Mai puține", + "shared": "Partajat", + "shared_account_hidden": "Ascuns din bara laterală. Îl poți afișa din nou în Setări › Aspect.", + "notifications_muted": "Notificări dezactivate", + "mail": "E-mail", + "nav_label": "Navigare", + "add_app": "Aplicații", + "scheduled": "Programat", + "unified_all_unread": "Toate necitite", + "unified_all_starred": "Toate cu stea", + "unified_all_mail": "Toată poșta", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Etichete", + "create_label": "Creează o etichetă nouă", + "mv_tasks": "Sarcini", + "mv_side_panel": "Panou lateral", + "mv_open_full": "Deschide în vizualizare completă", + "mv_previous_day": "Ziua anterioară", + "mv_next_day": "Ziua următoare", + "mv_add_another_account": "Adaugă alt cont", + "mv_manage_account": "Gestionează contul", + "mv_storage_used": "{percent}% din {total} folosit" + }, + "protocol_handlers": { + "title": "Aplicații implicite", + "description": "Alegeți dacă linkurile către e-mail și calendar se deschid în Bulwark. Din punct de vedere tehnic, Bulwark se înregistrează ca gestionar de protocol pentru linkurile de tip mailto: și webcal:.", + "unsupported": "Acest browser sau această conexiune nu acceptă înregistrarea manuală a gestionarului de protocoale. Este posibil să puteți utiliza totuși aplicația PWA instalată prin setările browserului sau ale sistemului de operare.", + "mailto_label": "Linkuri din e-mail", + "mailto_description": "Deschideți linkurile „mailto:” în Bulwark cu o fereastră de redactare precompletată.", + "protocol_open_mode_label": "La deschiderea linkurilor de protocol", + "protocol_open_mode_description": "Alegeți dacă Bulwark deschide linkurile mailto: și webcal: într-o filă nouă sau reutilizează o sesiune deschisă. Opțiunea „sesiune activă” necesită permisiunea de notificare, astfel încât să puteți face clic pe o notificare de rezervă pentru a aduce Bulwark în prim-plan dacă browserul blochează focalizarea.", + "protocol_open_mode_active_session": "Deschideți într-o sesiune activă, dacă este posibil", + "protocol_open_mode_new_tab": "Deschideți întotdeauna o filă nouă", + "focus_notification_title": "Deschideți Bulwark", + "focus_notification_body": "Linkul a fost deschis în Bulwark. Faceți clic pentru a aduce fereastra în prim-plan.", + "webcal_label": "Linkuri către calendar", + "webcal_description": "Deschideți linkurile webcal: din Bulwark cu o fereastră de dialog de abonare la calendar completată în prealabil.", + "register_mailto": "Înregistrați aplicația de e-mail", + "register_webcal": "Înregistrați aplicația de calendar", + "mailto_registered": "S-a solicitat înregistrarea unui gestionar de e-mail", + "webcal_registered": "S-a solicitat înregistrarea unui handler pentru calendar", + "registration_failed": "Înregistrarea gestionarului de protocol a eșuat", + "opening_mailto": "Se deschide fereastra de redactare...", + "opening_webcal": "Se deschide calendarul...", + "browser_note": "Este posibil ca browserul sau sistemul de operare să vă solicite confirmarea acestei acțiuni și să necesite instalareBulwarkului înainte de a putea fi selectat ca aplicație implicită.", + "select_account_title": "Alegeți contul", + "select_mailto_account": "Alegeți contul în care să se deschidă acest link de e-mail.", + "select_webcal_account": "Alegeți contul care ar trebui să deschidă acest link către calendar.", + "select_account_note": "Aceasta se aplică numai acestui link de protocol.", + "detail_to": "Către", + "detail_subject": "Subiect", + "detail_no_subject": "Fără subiect", + "detail_calendar": "Calendar", + "detail_source": "Sursă", + "active_account": "Activ", + "switching_account": "Se schimbă contul..." + }, + "sidebar_apps": { + "modal_title": "Aplicații din bara laterală", + "add_new": "Adăugați aplicația", + "edit_app": "Editați aplicația", + "name_label": "Nume", + "name_placeholder": "Aplicația mea", + "name_required": "Numele este obligatoriu", + "url_label": "URL", + "url_required": "URL este obligatoriu", + "url_invalid": "Introduceți o adresă URL validă de tip http sau https", + "icon_label": "Pictogramă", + "icon_required": "Pictograma este obligatorie", + "open_mode_label": "Modul Deschis", + "open_new_tab": "Filă nouă", + "open_inline": "În text", + "cancel": "Anulează", + "add": "Adăugați", + "update": "Actualizare", + "delete": "Șterge", + "delete_confirm_title": "Șterge aplicația", + "delete_confirm": "Sunteți sigur că doriți să ștergeți „{name}”?", + "no_apps": "Nu s-au adăugat încă aplicații", + "no_apps_hint": "Adăugați aplicații personalizate și linkuri în bara laterală", + "managed_title": "Furnizate de administrator", + "managed_description": "Aceste aplicații sunt configurate pentru toți și nu pot fi modificate.", + "managed_badge": "Gestionată", + "search_icons": "Pictograme de căutare...", + "show_popular": "Popular", + "show_all": "Toate", + "no_icons_found": "Nu s-au găsit pictograme", + "inline_badge": "În text", + "tab_badge": "Tab", + "show_on_mobile": "Afișare pe mobil" + }, + "email_list": { + "columns": { + "from": "De la", + "to": "Către", + "subject": "Subiect", + "date": "Dată", + "size": "Dimensiune" + }, + "no_emails": "Nu s-au găsit mesaje", + "no_emails_description": "Această căsuță poștală este goală", + "no_search_results": "Nu s-au găsit rezultate", + "no_search_results_description": "Încercați să modificați criteriile de căutare sau filtrele", + "loading": "Se încarcă e-mailurile...", + "unread": "necitit", + "to_me": "Pentru mine", + "to_recipients": "Către destinatarii {count}", + "and_others": "și {count} altele", + "draft": "Ciorne", + "starred": "Cu stea", + "pinned": "Fixat", + "replied": "Răspuns", + "forwarded": "Redirecționat", + "has_attachment": "Cu atașament", + "conversations_range": "{from}–{to, number} din {total, number}", + "conversations_count": "{count} din conversațiile de pe {total}", + "conversations_count_plus": "{count}+ conversations", + "conversations_count_simple": "{count} conversații", + "no_conversations": "Fără conversații", + "loading_more": "Se încarcă mai multe e-mailuri...", + "no_more_emails": "Nu mai sunt e-mailuri de încărcat", + "batch_actions": { + "select": "Selectați e-mailurile", + "select_all": "Selectează tot", + "selected_messages": "{count, plural, one {1 e-mail} few {# e-mailuri} other {# de e-mailuri}} selectat", + "mark_read": "Marcați ca citit", + "mark_unread": "Marcați ca necitit", + "delete": "Șterge", + "delete_confirm_title": "Ștergeți e-mailurile", + "delete_confirm_message": "Sunteți sigur că doriți să ștergeți {count, plural, one {1 e-mail} few {# e-mailuri} other {# de e-mailuri}}?", + "clear_selection": "Șterge selecția" + }, + "permanent_delete": "Șterge definitiv", + "permanent_delete_confirm_title": "Șterge definitiv", + "permanent_delete_confirm_message": "Acest e-mail va fi șters definitiv. Această acțiune nu poate fi anulată.", + "permanent_delete_confirm_batch_message": "Acestea {count, plural, one {1 e-mail} few {# e-mailuri} other {# de e-mailuri}} va fi șters definitiv. Această acțiune nu poate fi anulată.", + "empty_folder": { + "button": "Folder gol", + "confirm_title": "Folder gol", + "confirm_message": "Toate e-mailurile din acest dosar vor fi șterse definitiv. Această acțiune nu poate fi anulată.", + "confirm_message_trash": "Toate e-mailurile din acest dosar vor fi mutate în coșul de gunoi.", + "confirm_button": "Folder gol", + "junk_hint": "Puteți goli folderul „Junk” pentru a șterge definitiv toate mesajele.", + "trash_hint": "Puteți goli folderul Coș de gunoi pentru a șterge definitiv toate mesajele." + }, + "no_scheduled_emails": "Fără e-mailuri programate", + "no_scheduled_emails_description": "Mesajele programate pentru mai târziu vor apărea aici.", + "scheduled_count": "{count, plural, one {1 e-mail programat} few {# e-mailuri programate} other {# de e-mailuri programate}}", + "scheduled_actions_hint": "Mesajele programate pot fi anulate, reprogramate sau editate din lista acțiunilor programate.", + "cancel_scheduled_send": "Anulează trimiterea", + "reschedule_send": "Reprogramare", + "cancel_and_edit": "Anulează și editează", + "cancel_and_compose_again": "Anulează și redactează din nou", + "reschedule_prompt": "Introduceți o nouă dată/oră, de exemplu 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Expeditorul a solicitat o confirmare de citire:", + "send": "Trimite confirmare de primire", + "ignore": "Ignoră", + "sent": "Confirmare de citire trimisă.", + "send_failed": "Nu s-a putut trimite confirmarea de citire", + "mdn_subject": "Citiți: {subject}", + "mdn_body": "Aceasta este o confirmare de primire pentru mesajul pe care l-ați trimis la {recipient}.\n\nNotă: Această confirmare de primire atestă doar faptul că mesajul a fost afișat pe computerul destinatarului. Nu există nicio garanție că destinatarul a citit sau a înțeles conținutul mesajului." + }, + "no_email_selected": "Nu a fost selectat niciun e-mail", + "no_email_description": "Selectați un e-mail din listă pentru a-l vizualiza aici", + "no_conversation_selected": "Nu este selectată nicio conversație", + "no_conversation_description": "Alegeți o conversație din listă pentru a o citi aici", + "compose": "Compune", + "compose_hint": "Redactați un mesaj nou", + "no_subject": "(Fără subiect)", + "no_body_content": "(Nu există conținut disponibil)", + "no_preview_available": "Nu este disponibilă previzualizarea", + "show_quoted_text": "Afișează textul citat", + "hide_quoted_text": "Ascunde textul citat", + "loading_email": "Se încarcă e-mailul...", + "loading": "Se încarcă...", + "reply": "Răspunde", + "reply_all": "Răspunde tuturor", + "forward": "Redirecționează", + "delete": "Șterge", + "archive": "Arhivează", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Stea", + "unstar": "Anulează marcarea cu stea", + "mark_unread": "Marcați ca necitit", + "mark_read": "Marcați ca citit", + "unread": "Necitit", + "read": "Citiți", + "spam_short": "Spam", + "not_spam_short": "Nu este spam", + "move": "Mutare", + "print": "Imprimare", + "view_source": "Vizualizați sursa", + "fullscreen": "Ecran complet", + "exit_fullscreen": "Ieșire din ecran complet", + "view_in_dark_mode": "Vizualizare în modul întunecat", + "view_in_light_mode": "Vizualizare în modul luminos", + "export_email": "Exportați ca fișier .eml", + "forward_as_attachment": "Redirecționează ca atașament", + "import_email": "Importați fișiere .eml sau .zip", + "keyboard_shortcuts": "Comenzi rapide de la tastatură (?)", + "email_source": "Sursa e-mailului", + "draft_banner": "Acest mesaj este o schiță", + "edit_draft": "Editare", + "copy_source": "Copiați în clipboard", + "source_copied": "Sursă copiată în clipboard", + "attachments": "Anexe", + "important": "Important", + "download": "Descărcați", + "download_all": "Descărcați tot", + "from": "De la", + "to": "Către", + "cc": "CC", + "bcc": "BCC", + "date": "Data", + "subject": "Subiect", + "show_details": "Afișați detalii", + "hide_details": "Ascunde detaliile", + "external_content_warning": "Imaginile și conținutul extern au fost blocate", + "load_external_content": "Încărcați imagini", + "trust_sender": "Aveți întotdeauna încredere în acest expeditor", + "sender_check": { + "unverified_label": "Expeditor neverificat", + "failed_label": "Verificarea expeditorului a eșuat", + "unverified": "Acest mesaj pretinde că provine de la {domain}, dar nimic nu confirmă acest lucru. Nu are o semnătură validă de la {domain} și nu a venit de la un server autorizat de {domain}.", + "unverified_sent_from": "Acest mesaj pretinde că provine de la {domain}, dar a fost trimis de pe {host} și nu are o semnătură validă de la {domain}.", + "failed": "Acest mesaj pretinde că provine de la {domain}, dar nu a trecut de verificările de expeditor ale acestui domeniu. Este posibil să fie falsificat.", + "failed_sent_from": "Acest mesaj pretinde că provine de la {domain}, dar a fost trimis de pe {host} și nu a trecut de verificările de expeditor ale domeniului {domain}. Este posibil să fie falsificat.", + "caution": "Fiți atenți la linkuri, atașamente și solicitări de parole sau date de plată." + }, + "back_to_list": "Înapoi la listă", + "view_contact": "Vizualizare contact", + "message_details": "Detalii mesaj", + "more_reply_options": "Mai multe opțiuni de răspuns", + "set_tag": "Setați eticheta", + "tag": "Etichetă", + "more_actions": "Alte acțiuni", + "previous": "Anterior", + "next": "Următorul", + "move_to": "Mergi la...", + "remove_tag": "Eliminați eticheta", + "tag_filter_placeholder": "Filtrează etichetele", + "tag_no_matches": "Nicio etichetă corespunzătoare", + "more_count": "+{count} mai multe", + "characters_count": "{count} caractere", + "quick_reply_placeholder": "Scrie un răspuns rapid...", + "more_options": "Mai multe opțiuni", + "sending": "Se trimite...", + "security_authentication": "Securitate și autentificare", + "technical_details": "Detalii tehnice", + "message_id_label": "IDul mesajului:", + "reply_to_label": "Răspunde la:", + "delivery_time_label": "Termen de livrare:", + "conversation_part_label": "Parte a conversației:", + "previous_messages": "{count} mesajul anterior", + "previous_messages_plural": "{count} mesaje anterioare", + "time": { + "day": "zi", + "days": "zile", + "hour": "oră", + "hours": "ore", + "minute": "minut", + "minutes": "minute" + }, + "unknown_sender": "Necunoscut", + "recipient_me": "eu", + "recipient_and_others": "{name} și {count} altele", + "recipient_to_prefix": "Către:", + "authentication": { + "title": "Autentificare", + "status": { + "verified": "Verificat", + "warning": "Avertisment", + "none": "Nu sunteți autentificat" + }, + "spf": { + "pass": "SPF Admis", + "fail": "SPF Eșec", + "none": "Fără „SPF”" + }, + "dkim": { + "pass": "DKIM Valid", + "fail": "DKIM Invalid", + "none": "Fără „DKIM”" + }, + "dmarc": { + "pass": "DMARC Admis", + "fail": "DMARC Eșec", + "none": "Fără „DMARC”" + }, + "spam_score": "Scorul de spam", + "tooltip_spf": "Sender Policy Framework: verifică dacă serverul expeditor este autorizat să trimită e-mailuri în numele domeniului", + "tooltip_dkim": "DomainKeys Identified Mail: confirmă faptul că e-mailul nu a fost modificat în timpul transmiterii, folosind o semnătură criptografică", + "tooltip_dmarc": "Autentificarea, raportarea și conformitatea mesajelor bazate pe domeniu (DMARC): Asigură că adresele SPF și DKIM corespund domeniului expeditorului și stabilește o politică pentru eșecuri", + "policy": "Politică", + "result": { + "pass": "Admis", + "fail": "Eșec", + "softfail": "Eșec ușor", + "neutral": "Neutru", + "permerror": "Eroare permanentă", + "temperror": "Eroare temporară", + "none": "Niciunul" + } + }, + "details": { + "recipients_routing": "Destinatari și rutare", + "authentication_security": "Autentificare și securitate", + "identifiers_threading": "Identificatori și organizarea în firuri de discuție", + "mailing_list": "Listă de distribuție", + "message_properties": "Proprietățile mesajului", + "sent": "Trimis", + "received": "Primit", + "delivery_time": "Termen de livrare", + "in_reply_to": "In-Reply-To", + "references": "Referințe", + "thread_id": "ID de discuție", + "size": "Dimensiune", + "mime_type": "MIME tip", + "attachments_summary": "{count} fișiere · {size}", + "list_id": "Listă ID", + "list_help": "Ajutor pentru listă", + "list_post": "Postare în listă", + "list_unsubscribe": "Dezabonare", + "iprev": "DNS inversat", + "spam_status": "Starea spamului", + "ai_verdict": "Verdictul AI", + "account": "Cont", + "no_subject": "(fără subiect)" + }, + "headers": { + "routing": "Rutare", + "received": "Primit", + "message_id": "IDul mesajelor", + "list_info": "Informații despre listă" + }, + "tooltips": { + "reply": "Răspunde (r)", + "reply_all": "Răspunde tuturor (a)", + "forward": "Redirecționează (f)", + "archive": "Arhivare (e)", + "delete": "Ștergere (# sau Del)", + "star": "Stea (stele)", + "unstar": "Dezactivează marcajul (marcajele)", + "compose": "Compune (c)", + "previous": "E-mailul anterior", + "next": "Următorul e-mail", + "edit_draft": "Editează schița" + }, + "spam": { + "button_title": "Raportează spamul", + "not_spam_title": "Marcați ca legitim", + "toast_success": "Mutate în folderul „Junk”", + "toast_batch": "{count} e-mailuri mutate în folderul „Junk”", + "toast_undo": "Anulează", + "toast_not_spam_success": "Mutată în Inbox", + "toast_not_spam_batch": "{count} e-mailuri mutate în Inbox", + "error": "Nu s-a reușit raportarea spamului", + "error_not_spam": "Nu s-a putut restaura e-mailul" + }, + "unsubscribe_banner": { + "label": "Buletin informativ", + "button": "Dezabonare", + "confirm_title": "Doriți să vă dezabonați de la acest expeditor?", + "confirm_button": "Confirmare", + "cancel": "Anulează", + "success_http": "Pagina de dezabonare se deschide într-o filă nouă", + "success_mailto": "E-mailul de dezabonare a fost trimis", + "confirm_message_http": "Pagina de dezabonare se va deschide într-o filă nouă.", + "confirm_message_mailto": "Un e-mail de dezabonare va fi trimis expeditorului.", + "error": "Nu se poate dezabona", + "dismiss": "Ignoră" + }, + "calendar_invitation": { + "loading": "Se încarcă detaliile evenimentului…", + "title": "Invitație în calendar", + "published_title": "Eveniment publicat", + "response_title": "Răspuns la evenimente", + "update_title": "Actualizare eveniment", + "counter_title": "Contra-propunere", + "refresh_title": "Cerere de reîmprospătare", + "declined_counter_title": "Contrapropunere respinsă", + "cancelled_title": "Eveniment anulat", + "organizer": "Organizat de {name}", + "organizer_label": "Organizat după", + "attendees": "{count, plural, one {# participant} few {# participanți} other {# de participanți}}", + "accept": "Accept", + "maybe": "Poate", + "decline": "Declinare", + "add_to_calendar": "Adaugă în calendar", + "added": "Adăugat în calendar", + "rsvp_sent": "Răspuns trimis", + "parse_error": "Nu s-a putut citi invitația", + "action_failed": "Nu s-a putut finaliza acea acțiune din calendar.", + "no_calendar": "Calendarul nu este disponibil", + "published_info": "Acest eveniment a fost distribuit cu titlu informativ.", + "response_info": "Acest mesaj conține răspunsul unui participant.", + "response_info_organizer": "Răspunsul acestui participant actualizează evenimentul dumneavoastră.", + "update_info": "Acest mesaj actualizează un eveniment existent.", + "counter_info": "Acest mesaj propune modificări la un eveniment.", + "counter_info_organizer": "Acest participant a propus modificări la evenimentul dvs.", + "refresh_info": "Acest mesaj solicită detaliile celui mai recent eveniment.", + "refresh_info_organizer": "Un participant a solicitat detaliile cele mai recente despre eveniment.", + "declined_counter_info": "Organizatorul a respins o contrapropunere.", + "authentication_failed_info": "Verificările de autentificare a adresei de e-mail pentru această invitație au eșuat. Tratați cu precauție acțiunile din calendar.", + "authentication_missing_info": "Această invitație nu include autentificarea verificată a adresei de e-mail. Confirmați detaliile cu organizatorul dacă ceva pare neobișnuit.", + "sender_mismatch_info": "Această invitație a fost trimisă de la {sender}, în timp ce organizatorul menționat în datele din calendar este {organizer}.", + "sender_mismatch_unverified_info": "Această invitație a fost trimisă de la adresa {sender}, în timp ce organizatorul menționat în datele din calendar este {organizer}, iar mesajul nu a putut fi verificat.", + "organizer_role": "Tu organizezi acest eveniment", + "your_response": "Răspunsul dvs.: {status}", + "response_needed": "Necesită răspuns", + "response_accepted": "Acceptat", + "response_tentative": "Provizoriu", + "response_declined": "Respins", + "response_delegated": "Delegat", + "actor_sent_info": "Trimis de {name}.", + "actor_response_info": "{name} a răspuns {status}.", + "actor_counter_info": "{name} modificări propuse pentru acest eveniment.", + "actor_refresh_info": "{name} a solicitat detalii despre cel mai recent eveniment.", + "actor_declined_counter_info": "{name} a respins contrapropunerea.", + "actor_note": "Notă: {comment}", + "actor_unknown": "Cineva", + "proposed_changes": "Modificări propuse", + "change_title": "Titlu", + "change_time": "Ora", + "change_location": "Locație", + "change_description": "Descriere", + "change_empty": "Niciunul", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Aplicați modificările propuse", + "proposal_applied": "Modificările propuse au fost aplicate.", + "review_proposal": "Revizuirea propunerii", + "review_request": "Solicitare de revizuire", + "view_in_calendar": "Vizualizare în calendar", + "select_calendar": "Selectați calendarul", + "already_in_calendar": "Deja în calendarul dvs.", + "request_info": "Ați fost invitat la acest eveniment. Răspundeți pentru a-i comunica organizatorului disponibilitatea dumneavoastră.", + "cancel_info": "Organizatorul a anulat acest eveniment.", + "event_updated": "Actualizați # {sequence}", + "event_status_tentative": "Provizoriu", + "event_status_cancelled": "Anulat", + "expand": "Afișați detalii", + "collapse": "Ascunde detaliile" + }, + "send": "Trimite", + "more": "mai mult", + "scheduled_banner": "Programat pentru trimitere la {date}", + "scheduled_send_created": "E-mail programat pentru trimitere", + "sending_in": "Se trimite în {seconds} s", + "undo_send_failed": "Anularea nu a reușit. Este posibil ca mesajul să fi fost deja trimis.", + "cancel_scheduled_send": "Anulează trimiterea", + "reschedule_send": "Reprogramare", + "cancel_and_edit": "Anulează și editează", + "cancel_and_compose_again": "Anulează și redactează din nou", + "reschedule_prompt": "Introduceți o nouă dată/oră, de exemplu 2026-05-04T15:30", + "scheduled_actions_only": "Vizualizarea programată acceptă numai acțiuni programate", + "undo_send_scheduled": "Mesaj programat pentru trimitere", + "undo_send": "Anulează trimiterea", + "contact_sidebar": { + "title": "Contact", + "close": "Închide bara laterală", + "action_email": "E-mail", + "action_email_title": "Trimite e-mail", + "action_copy": "Copiere", + "action_copy_title": "Copiați e-mailul", + "action_edit_title": "Editează contactul", + "section_emails": "E-mailuri", + "section_phones": "Telefoane", + "section_organizations": "Organizații", + "section_addresses": "Adrese", + "section_notes": "Note", + "not_in_contacts": "Nu se află în lista dvs. de contacte", + "add_to_contacts": "Adaugă la contacte", + "copied": "Copiat!", + "copy_failed": "Copierea a eșuat", + "view_contact": "Vezi contactul", + "view_details": "Vezi detaliile" + }, + "send_now": "Trimite acum", + "verification_code": { + "copy": "Copiază codul {code}", + "copied": "Cod copiat", + "copy_failed": "Codul nu a putut fi copiat" + }, + "mv_position": "{index} din {total}", + "plugin_panel_show": "Afișează panoul pluginului", + "plugin_panel_hide": "Ascunde panoul pluginului" + }, + "email_composer": { + "select_from_directory": "Selectează din director", + "minimize": "Minimizează", + "maximize": "Ecran complet", + "restore": "Restabilește", + "new_message": "Mesaj nou", + "signature_edit_hint": "Dublu clic pentru a edita semnătura", + "reply": "Răspunde", + "reply_all": "Răspunde tuturor", + "forward": "Redirecționează", + "reply_to": "Răspunde", + "reply_all_to": "Răspunde tuturor", + "forward_message": "Redirecționează", + "from": "De la", + "to": "Către", + "cc": "CC", + "bcc": "BCC", + "subject": "Subiect", + "body_placeholder": "Scrieți mesajul...", + "send": "Trimite", + "cancel": "Anulează", + "attach": "Atașați", + "attach_photos": "Fotografii și videoclipuri", + "attach_files": "Fișiere", + "attach_from_files": "Atașează din Fișiere", + "files_picker": { + "attach": "{count, plural, =0 {Atașează} one {Atașează 1 fișier} few {Atașează # fișiere} other {Atașează # de fișiere}}", + "empty_folder": "Acest dosar este gol", + "load_failed": "Fișierele tale nu au putut fi încărcate" + }, + "discard": "Ignorați", + "discard_draft_title": "Doriți să ștergeți schița?", + "discard_draft_confirm": "Aveți modificări nesalvate. Doriți să renunțați la această schiță?", + "discard_draft_message": "Ciorna va fi ștearsă.", + "saving": "Se salvează...", + "sending": "Se trimite...", + "add_link": "Adăugați un link", + "link_url_prompt": "Accesați URL", + "draft_saved": "Ciorna a fost salvată", + "save_failed": "Nu s-a putut salva", + "to_placeholder": "Adresele de e-mail ale destinatarilor", + "cc_placeholder": "Destinatari în Cc", + "bcc_placeholder": "Destinatari Bcc", + "subject_placeholder": "Subiect", + "cc_label": "Cc:", + "bcc_label": "Bcc:", + "subject_label": "Subiect:", + "file_size_kb": "KB", + "prefix": { + "forward": "Redirecționare:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} few {# e-mailuri} other {# de e-mailuri}}", + "no_subject": "(Fără subiect)", + "unknown_sender": "Necunoscut", + "quote": { + "reply_header": "Pe {date}, {sender} a scris:", + "forward_header": "---------- Mesaj redirecționat ----------", + "from": "De la: {sender}", + "date": "Data: {date}", + "subject": "Subiect: {subject}", + "to": "Către: {recipients}" + }, + "remove_sub_address": "Eliminați subadresa", + "from_override": { + "toggle_off": "Suprascrieți", + "toggle_on": "Anulare suprascriere", + "toggle_tooltip": "Puteți modifica liber numele și adresa din câmpul „De la”. E-mailul este trimis în continuare sub identitatea dvs.", + "name_label": "Numele expeditorului", + "name_placeholder": "Nume", + "email_label": "De la adresa de e-mail", + "email_placeholder": "alias@example.com", + "envelope_notice": "Dacă serverul dvs. nu acceptă această adresă ca expeditor al plicului, se folosește în schimb {identity}, iar destinatarii o pot vedea în antetul Return-Path." + }, + "save_as_template": "Salvează ca șablon", + "attach_from_device": "Încarcă de pe acest dispozitiv", + "templates_menu": "Șabloane", + "insert_template": "Inserează șablon", + "options": "Opțiuni", + "option_plain_text": "Text simplu", + "option_plain_text_hint": "Trimite fără formatare", + "option_read_receipt": "Cere confirmare de citire", + "option_read_receipt_hint": "Cere destinatarului să confirme deschiderea", + "option_dsn": "Notificare de livrare", + "option_dsn_hint": "Primește un raport când serverul îl livrează", + "option_require_tls": "Cere livrare criptată", + "option_require_tls_hint": "Livrează doar prin TLS, altfel respinge", + "option_priority": "Prioritate", + "option_priority_high": "Ridicată", + "option_priority_normal": "Normală", + "option_priority_low": "Scăzută", + "validation": { + "recipient_required": "Adăugați un destinatar căruia să îi trimiteți mesajul", + "body_required": "Scrieți un mesaj sau atașați un fișier", + "invalid_recipients": "Nu s-a trimis - verifică aceste adrese: {addresses}", + "attachments_uploading": "Atașamentele încă se încarcă — se va trimite imediat ce se termină", + "attachment_upload_failed": "Netrimis - încărcarea unui atașament a eșuat. Eliminați-l și încercați din nou." + }, + "upload_progress": "Încărcare {uploaded} / {total}", + "upload_cancel": "Anulează încărcarea", + "upload_failed": "Nu s-a putut încărca {filename}", + "attachment_too_large": "„{name}” depășește dimensiunea permisă de server ({max} per fișier)", + "attachments_total_too_large": "Atașamentele ar depăși limita serverului de {max} per mesaj", + "drop_files": "Trageți fișierele pentru a le atașa", + "show_less": "Afișați mai puțin", + "send_failed": "Nu s-a putut trimite e-mailul", + "send_timeout": "Serverul a încetat să răspundă. Verificați folderul Trimise înainte de a trimite din nou — mesajul poate să fi plecat deja.", + "send_offline": "Se pare că ești offline. Mesajul este încă aici; trimite-l din nou când revii online.", + "formatting": "Formatare", + "send_recipients_rejected": "Netrimis - serverul a respins toți destinatarii.", + "send_plugin_failed": "Nu a fost trimis - un plugin care se ocupă de trimitere a eșuat sau a durat prea mult. Verifică dosarul Trimise înainte de a trimite din nou.", + "send_some_recipients_rejected": "Trimis, dar nu și acestor destinatari - serverul i-a respins.", + "continue_draft": "Continuă schița", + "close_draft_title": "Să salvați sau să renunțați la schiță?", + "close_draft_message": "Aveți modificări nesalvate. Doriți să salvați acest mesaj ca schiță sau să îl ignorați?", + "smime_sign_on": "S/MIME semnarea activată", + "smime_sign_off": "Activați semnarea cu „S/MIME”", + "smime_encrypt_on": "S/MIME criptare activată", + "smime_encrypt_off": "Activați criptareS/MIME", + "smime_encrypt_unavailable": "S/MIME Criptarea nu este disponibilă – lipsesc certificatele destinatarilor", + "smime_unlock_title": "Deblocați o cheiS/MIME", + "smime_unlock_message": "Introduceți fraza de acces pentru a debloca cheia de semnare S/MIME.", + "smime_unlock_button": "Deblochează", + "smime_passphrase_placeholder": "Frază de acces", + "forgot_attachment": { + "title": "Ați uitat să atașați un fișier?", + "message": "Mesajul dvs. menționează „{keyword}”, dar nu este atașat niciun fișier. Doriți să trimiteți oricum?", + "send_anyway": "Trimite oricum", + "back": "Înapoi la editare" + }, + "empty_subject": { + "title": "Trimiteți fără subiect?", + "message": "Acest mesaj nu are subiect. Îl trimiteți oricum?", + "dont_ask_again": "Nu mai întreba", + "send_anyway": "Trimite oricum", + "back": "Înapoi la editare" + }, + "schedule_send": "Programează trimiterea", + "schedule_send_description": "Alegeți momentul în care serverul ar trebui să transmită acest mesaj.", + "schedule_preset": { + "tomorrow_morning": "Mâine dimineață", + "tomorrow_afternoon": "Mâine după-amiază", + "monday_morning": "Luni dimineață" + }, + "schedule_send_required": "Alegeți o dată și o oră.", + "schedule_send_invalid": "Introduceți o dată și o oră valide.", + "schedule_send_future": "Alegeți o dată și o oră viitoare.", + "schedule_send_too_late": "Ora aceasta este mai târzie decât cea permisă de server.", + "schedule_send_unsupported": "Trimiterea programată nu este acceptată pentru acest cont.", + "schedule_send_cleanup_warning": "Trimiterea programată a fost creată, dar curățarea schițelor a eșuat.", + "send_delay_unsupported": "Întârzierea trimiterii nu este acceptată pentru acest cont.", + "send_delay_unsupported_confirm": "Acest cont nu acceptă amânarea trimiterii. Doriți să trimiteți imediat?", + "recipient_edit_email": "Editați adresa de e-mail", + "recipient_edit_name": "Editați numele afișat", + "recipient_email_placeholder": "Adresă de e-mail", + "recipient_name_placeholder": "Numele afișat", + "invalid_recipient": "Adresă de e-mail nevalidă", + "autocomplete_search_server": "Caută pe server", + "autocomplete_searching": "Se caută...", + "mention_recipients": "Destinatari", + "mention_card_not_recipient": "Nu este în Către sau CC, deci nu va primi acest mesaj.", + "mention_card_add": "Adaugă la {field}", + "mention_card_remove": "Elimină mențiunea", + "toolbar": { + "bold": "Aldin", + "italic": "Cursiv", + "underline": "Subliniat", + "strikethrough": "Tăiat", + "text_color": "Culoarea textului", + "remove_color": "Elimină culoarea", + "heading_1": "Titlu 1", + "heading_2": "Titlu 2", + "bullet_list": "Listă cu marcatori", + "ordered_list": "Listă numerotată", + "quote": "Citat", + "code_block": "Bloc de cod", + "align_left": "Aliniere la stânga", + "align_center": "Centrare", + "align_right": "Aliniere la dreapta", + "text_direction": "Direcția textului (RTL/LTR)", + "link": "Link", + "table": "Tabel", + "clear_formatting": "Șterge formatarea", + "undo": "Anulează", + "redo": "Refă", + "add_row_above": "Adaugă rând deasupra", + "add_row_below": "Adaugă rând dedesubt", + "add_column_before": "Adaugă coloană înainte", + "add_column_after": "Adaugă coloană după", + "delete_row": "Șterge rândul", + "delete_column": "Șterge coloana", + "toggle_header_row": "Comută rândul de antet", + "delete_table": "Șterge tabelul", + "pick_size": "Alege dimensiunea", + "font_size": "Dimensiune font", + "font_size_default": "Implicit", + "background_color": "Culoarea fundalului", + "remove_background_color": "Elimină culoarea fundalului" + }, + "send_filing_warning": "Trimis - dar curățarea ulterioară a eșuat, poate rămâne o ciornă veche.", + "pop_out": "Deschide răspunsul într-o fereastră", + "send_guard_failed": "Nu s-a trimis - un plugin care verifică mesajele înainte de trimitere a eșuat sau nu a răspuns." + }, + "confirm_dialog": { + "confirm": "Confirmare", + "cancel": "Anulează" + }, + "common": { + "loading": "Se încarcă...", + "error": "Eroare", + "success": "Succes", + "cancel": "Anulează", + "save": "Salvare", + "delete": "Șterge", + "edit": "Editare", + "close": "Închide", + "search": "Căutare", + "refresh": "Reîmprospătează", + "settings": "Setări", + "help": "Ajutor", + "logout": "Deconectare", + "yes": "Da", + "no": "Nu", + "unknown": "Necunoscut", + "app_title": "Webmail", + "reconnecting": "Conexiune pierdută. Se încearcă reconectarea…", + "rate_limited_title": "Autentificarea pe server este temporar limitată ca frecvență.", + "rate_limited_detail": "Bulwark A suspendat solicitările de fundal pentru a evita blocarea. Se va încerca din nou în câteva {seconds} s.", + "rate_limited_action_title": "Solicitarea a fost pusă în așteptare pentru a evita blocarea contului.", + "rate_limited_action_detail": "Bulwark Așteaptă încheierea perioadei de așteptare a serverului înainte de a trimite alte cereri autentificate. Încercați din nou în câteva {seconds}.", + "pull_to_refresh": "Trageți pentru a reîmprospăta", + "release_to_refresh": "Eliberați pentru a reîmprospăta", + "refreshing": "Se reîmprospătează…" + }, + "notifications": { + "email_sent": "E-mailul a fost trimis cu succes", + "email_deleted": "E-mail șters", + "email_archived": "E-mail arhivat", + "emails_deleted": "{count, plural, one {# e-mail șters} few {# e-mailuri șterse} other {# de e-mailuri șterse}}", + "emails_archived": "{count, plural, one {# e-mail arhivat} few {# e-mailuri arhivate} other {# de e-mailuri arhivate}}", + "error_archiving": "Arhivarea e-mailului a eșuat", + "error_updating": "Actualizarea e-mailului a eșuat", + "email_starred": "E-mail marcat cu stea", + "email_unstarred": "E-mail fără stea", + "email_marked_read": "E-mail marcat ca citit", + "email_marked_unread": "E-mail marcat ca necitit", + "copied_to_clipboard": "Copiat în clipboard", + "source_copied": "Sursă copiată în clipboard", + "error_sending": "Nu s-a putut trimite e-mailul", + "error_deleting": "Nu s-a putut șterge e-mailul", + "error_loading": "Nu s-au putut încărca e-mailurile", + "new_email": "E-mail nou", + "new_email_from": "De pe {sender}", + "click_to_view": "Faceți clic pentru a vizualiza", + "email_moved": "E-mail mutat", + "emails_moved": "{count} e-mailuri mutate", + "moved_to_mailbox": "Mutată la {mailbox}", + "move_failed": "Mutarea a eșuat", + "removed_from_folder": "Eliminat din {mailbox}", + "remove_from_folder_failed": "Nu s-a putut elimina din acest dosar", + "emails_copied": "{count} e-mailuri copiate", + "copy_failed": "Copierea a eșuat", + "move_error": "Nu s-au putut muta e-mailurile în folderul selectat", + "email_tagged": "E-mail etichetat", + "emails_tagged": "{count} e-mailuri etichetate", + "tag_failed": "Etichetarea a eșuat", + "identity_created": "Identitatea a fost creată cu succes", + "identity_updated": "Identitatea a fost actualizată cu succes", + "identity_deleted": "Identitate ștearsă", + "identity_set_primary": "Identitate principală actualizată", + "identity_create_failed": "Nu s-a putut crea identitatea: {error}", + "identity_update_failed": "Nu s-a putut actualiza identitatea: {error}", + "identity_delete_failed": "Nu s-a putut șterge identitatea: {error}", + "identity_unauthorized": "Nu aveți autorizație să trimiteți mesaje de la această adresă de e-mail", + "identity_not_found": "Identitate neidentificată", + "vacation_saved": "Setările de absență au fost salvate", + "vacation_save_failed": "Nu s-au putut salva setările de absență", + "filters_saved": "Filtrele au fost salvate cu succes", + "filters_save_failed": "Nu s-au putut salva filtrele", + "filters_deleted": "Regula de filtrare a fost ștersă", + "templates_exported": "Șabloanele au fost exportate cu succes", + "templates_imported": "{count, plural, one {# șablon} few {# șabloane} other {# de șabloane}} importat", + "templates_import_errors": "Unele șabloane nu au putut fi importate", + "templates_import_empty": "Nu s-au găsit șabloane în fișier", + "export_email_error": "Nu s-a putut exporta e-mailul", + "import_email_success": "E-mailul a fost importat cu succes", + "import_email_error": "Importul e-mailului a eșuat", + "rule_created": "Regula a fost creată", + "rule_merged": "Adăugat la regula „{name}”", + "rule_already_covered": "Deja acoperit de regula „{name}”", + "rule_apply_existing": "{count, plural, one {Aplicați la # mesaj existent} few {Aplicați la # mesaje existente} other {Aplicați la # de mesaje existente}}", + "rule_applied": "{count, plural, =0 {Niciun mesaj nu a trebuit modificat} one {Regula a fost aplicată la # mesaj} few {Regula a fost aplicată la # mesaje} other {Regula a fost aplicată la # de mesaje}}", + "rule_apply_failed": "Regula nu a putut fi aplicată mesajelor existente", + "rule_edit": "Editați regula", + "rule_undo": "Anulează", + "rule_undone": "Modificarea regulii a fost anulată", + "rule_undo_failed": "Modificarea regulii nu a putut fi anulată", + "rule_undo_conflict": "Filtrele dvs. s-au schimbat între timp. Anulați modificarea din setările filtrelor.", + "rule_folder_failed": "Dosarul nu a putut fi creat", + "vacation_filters_save_failed": "Setările de absență au fost salvate, dar redirecționarea și destinatarii răspunsului nu au putut fi salvați" + }, + "date": { + "today": "Astăzi", + "yesterday": "Ieri", + "this_week": "Săptămâna aceasta", + "last_week": "Săptămâna trecută", + "this_month": "Luna aceasta", + "older": "Mai vechi", + "just_now": "Chiar acum", + "minutes_ago": "{count} acum un minut", + "minutes_ago_plural": "{count} acum câteva minute", + "hours_ago": "{count} acum o oră", + "hours_ago_plural": "{count} acum câteva ore", + "days_ago": "{count} acum o zi", + "days_ago_plural": "{count} zile în urmă" + }, + "language": { + "title": "Limba", + "english": "Engleză", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Portugheză", + "russian": "Русский", + "select_language": "Selectați limba", + "switch_to_english": "Comutați la limba engleză", + "switch_to_french": "Comutați la limba franceză", + "switch_to_japanese": "Comutați la limba japoneză", + "switch_to_spanish": "Comutați la limba spaniolă", + "switch_to_italian": "Comutați la limba italiană", + "switch_to_german": "Comutați la limba germană", + "switch_to_dutch": "Comutați la limba olandeză", + "switch_to_portuguese": "Comutați la portugheză", + "switch_to_russian": "Comutați la limba rusă", + "switching": "Schimbarea limbii...", + "switch": "Schimbă limba", + "current": "Limba curentă", + "polish": "Poloneză", + "switch_to_polish": "Comutați la limba poloneză", + "en": "Engleză", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Poloneză", + "pt": "Portugheză", + "he": "עברית", + "ru": "Русский", + "sk": "Slovacă", + "uk": "Ukrainiană", + "zh": "Chineză simplificată", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Comută la persană", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Comută la mongolă" + }, + "settings": { + "title": "Setări", + "back_to_mail": "Înapoi la Mail", + "save_success": "Setările au fost salvate cu succes", + "import_success": "Setările au fost importate cu succes", + "import_error": "Nu s-au putut importa setările", + "reset_confirm": "Sunteți sigur că doriți să resetați toate setările la valorile implicite?", + "unsaved_changes": "Aveți modificări nesalvate", + "discard_changes": "Doriți să renunțați la modificările nesalvate?", + "discard": "Ignorați", + "keep_editing": "Continuați editarea", + "search_placeholder": "Setări de căutare", + "search_clear": "Șterge căutarea", + "search_no_results": "Nu există setări corespunzătoare", + "tabs": { + "appearance": "Aspect", + "language": "Limba, regiunea și ora", + "email": "Comportamentul e-mailurilor", + "composer": "Redactare", + "privacy": "Confidențialitate și securitate", + "account": "Cont", + "identities": "Identități", + "vacation": "Absență", + "advanced": "Nivel avansat", + "calendar": "Calendar", + "filters": "Filtre", + "templates": "Șabloane", + "folders": "Dosare", + "keywords": "Etichete", + "security": "Securitate", + "files": "Fișiere", + "contacts": "Contacte", + "encryption": "Criptare", + "protocol_handlers": "Aplicații implicite", + "sidebar_apps": "Aplicații din bara laterală", + "notifications": "Notificări", + "layout": "Aspect", + "themes": "Teme", + "reading": "Citire", + "composing": "Redactarea", + "downloads": "Descărcări", + "content_senders": "Conținut și expeditori", + "about_data": "Despre & Date", + "debug": "Depanare" + }, + "themes": { + "title": "Teme", + "description": "Alegeți dintre temele implementate de administratorul dvs. și presetările integrate.", + "locked": "Selectarea temei este blocată de un administrator.", + "forced": "Tema „{name}” este impusă de administrator și nu poate fi schimbată", + "admin_theme": "Temă de administrator", + "activated": "Temă activată", + "default_restored": "Tema implicită a fost restabilită", + "default_name": "Implicită", + "default_badge": "Implicită", + "admin_enforced": "Impusă de administrator" + }, + "tab_groups": { + "general": "Generalități", + "account": "Cont și identitate", + "organization": "Organizarea e-mailurilor", + "apps": "Aplicații", + "system": "Sistem", + "appearance": "Aspect", + "mail": "E-mail", + "privacy": "Confidențialitate și securitate", + "advanced": "Nivel avansat" + }, + "appearance": { + "message_list_order": { + "label": "Ordinea listei de mesaje", + "description": "Cum sunt ordonate mesajele în listă. Sortarea este făcută de server, deci acoperă întregul dosar, nu doar mesajele încărcate.", + "preset": { + "chronological": "Cronologic (cele mai noi primele)", + "unread_first": "Necitite primele", + "starred_first": "Cu stea primele", + "tagged_first": "Etichetate primele", + "custom": "Personalizat" + }, + "tag_label": "Etichetă", + "advanced_show": "Avansat: editează nivelurile de sortare", + "advanced_hide": "Ascunde avansat", + "criterion_label": "Criteriul nivelului {n}", + "direction_label": "Direcția nivelului {n}", + "criteria": { + "unread": "Necitit", + "starred": "Cu stea", + "tag": "Etichetă", + "receivedAt": "Data primirii", + "sentAt": "Data trimiterii", + "from": "Expeditor", + "subject": "Subiect", + "size": "Dimensiune" + }, + "direction": { + "unread_first": "Necitite primele", + "read_first": "Citite primele", + "starred_first": "Cu stea primele", + "unstarred_first": "Fără stea primele", + "tagged_first": "Etichetate primele", + "untagged_first": "Neetichetate primele", + "newest_first": "Cele mai noi primele", + "oldest_first": "Cele mai vechi primele", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Cele mai mari primele", + "smallest_first": "Cele mai mici primele" + }, + "add_level": "Adaugă nivel", + "remove_level": "Elimină nivelul", + "move_up": "Mută în sus", + "move_down": "Mută în jos", + "tiebreak_note": "Până la trei niveluri; nivelurile următoare departajează egalitățile. „Cele mai noi primele” se aplică întotdeauna la final.", + "scope": { + "label": "Aplică la toate dosarele", + "description": "Dezactivat, ordinea se aplică doar la Inbox; celelalte dosare rămân cronologice." + }, + "unsupported_note": "Serverul tău de e-mail nu acceptă sortarea după starea de citire, stele sau etichete. Aceste niveluri sunt ignorate, iar lista rămâne cronologică." + }, + "title": "Aspect", + "description": "Personalizați aspectul și funcționalitatea serviciului dvs. de webmail", + "theme": { + "label": "Tema", + "description": "Alegeți schema de culori preferată", + "light": "Ușor", + "dark": "Întunecat", + "system": "Sistem" + }, + "language": { + "label": "Limba", + "description": "Alegeți limba preferată" + }, + "font_size": { + "label": "Dimensiunea fontului", + "description": "Ajustați dimensiunea textului pentru o mai bună lizibilitate", + "small": "Mic", + "medium": "Nivel mediu", + "large": "Mare" + }, + "list_density": { + "label": "Densitate", + "description": "Controlați spațierea și marginile interioare în întreaga interfață de utilizator", + "extra_compact": "Extra compact", + "compact": "Compact", + "regular": "Regulat", + "comfortable": "Confortabil" + }, + "interface_layout": { + "label": "Aspectul interfeței", + "description": "Unde se află căutarea, navigarea și barele de instrumente", + "default": "Implicit", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Activați animațiile", + "description": "Prezentați tranziții și efecte fluide" + }, + "toolbar_position": { + "label": "Poziția barei de instrumente", + "description": "Unde să afișați butoanele de acțiune pentru e-mailuri (Răspunde, Arhivează, Șterge etc.)", + "top": "Sus", + "below_subject": "Sub subiect" + }, + "toolbar_labels": { + "label": "Afișați etichetele barei de instrumente", + "description": "Afișați etichete text lângă pictogramele din bara de instrumente. Dezactivați-le pentru a economisi spațiu odată ce v-ați familiarizat cu pictogramele." + }, + "hide_account_switcher": { + "label": "Ascunde comutatorul de conturi din bara laterală", + "description": "Ascundeți selectorul de conturi din partea de sus a barei laterale a folderelor. Puteți comuta între conturi în continuare din bara de navigare din partea de jos." + }, + "show_rail_account_list": { + "label": "Afișați avatarurile conturilor în bara de navigare", + "description": "Afișați cercurile conturilor individuale în partea de jos a barei de navigare pentru comutarea rapidă, cu un buton de deconectare sub acestea." + }, + "unified_mailbox": { + "label": "Căsuță poștală unificată", + "description": "Afișați folderele combinate (Mesaje primite, Mesaje trimise etc.) pentru toate conturile conectate", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Includeți căsuțele de e-mail de grup", + "description": "De asemenea, integrați căsuțele de e-mail partajate/de grup în vizualizarea unificată." + } + }, + "all_mail": { + "folders_label": "Dosare în „Toate mesajele”", + "folders_description": "Alegeți ce dosare să fie incluse în vizualizarea „Toate mesajele”.", + "account_hint": "Se aplică pentru {account}.", + "no_folders": "Nu sunt disponibile foldere." + }, + "colorful_sidebar_icons": { + "label": "Pictograme colorate în bara laterală", + "description": "Colorați pictogramele folderelor și etichetelor în funcție de tip (albastru pentru „Inbox”, roșu pentru „Junk”, verde pentru „Sent” etc.). Dezactivați această opțiune pentru o bară laterală monocromă." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Afișează numărul total de mesaje", + "description": "Afișează numărul total de mesaje lângă foldere și etichete, pe lângă numărul celor necitite. Dezactivează pentru a afișa doar mesajele necitite." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Afișează conturile de e-mail partajate", + "description": "Afișează folderele cutiilor poștale partajate cu tine la Partajat, în bara laterală. Dezactivează pentru a le ascunde pe toate sau ascunde conturi individuale mai jos. Calendarele și contactele lor rămân." + }, + "pro_interface": { + "label": "Interfață Pro (experimentală)", + "description": "Aspect destinat utilizatorilor avansați, disponibil doar pe desktop, cu navigare prin mesaje în mai multe file și fluxuri de lucru între conturi. Interfața standard nu este afectată; puteți reveni la aceasta în orice moment.", + "open_label": "Interfața Open Pro", + "back_to_standard": "Înapoi la standard" + }, + "cross_unread": { + "label": "Toate conturile: Necitite", + "description": "Afișează în secțiunea Toate conturile o intrare cu poșta necitită din toate conturile, în toate folderele cu excepția spam, trimise, arhivă, coș și ciorne." + }, + "cross_starred": { + "label": "Toate conturile: Cu stea", + "description": "Afișează în secțiunea Toate conturile o intrare cu poșta cu stea din toate conturile, în toate folderele cu excepția spam, trimise, arhivă, coș și ciorne." + }, + "cross_all": { + "label": "Toate conturile: Toată poșta", + "description": "Afișează în secțiunea Toate conturile o intrare cu toată poșta din toate conturile, în toate folderele cu excepția spam, trimise, arhivă, coș și ciorne." + } + }, + "keywords": { + "title": "Etichete de e-mail", + "description": "Definiți etichete pentru a vă organiza e-mailurile pe culori. Acestea sunt stocate pe server sub formă de cuvinte-cheie dJMAP.", + "add_keyword": "Adăugați etichetă", + "label_field": "Nume afișat", + "label_placeholder": "de ex. Serviciu, Personal, Urgent", + "id_field": "EtichetăID", + "id_placeholder": "de exemplu: profesional, personal", + "color_field": "Culoare", + "id_exists": "Această etichetă ID există deja", + "edit": "Editați eticheta", + "delete": "Șterge eticheta", + "save": "Salvare", + "add": "Adăugați", + "cancel": "Anulează", + "migrating": "Actualizarea etichetei pentru e-mailurile existente…", + "migration_error": "Nu s-a putut actualiza eticheta pentru e-mailurile existente", + "filters_migration_error": "Unele reguli de filtrare folosesc încă eticheta veche — actualizează-le manual", + "nesting": { + "label": "Etichete imbricate", + "description": "Imbricați etichete sub alte etichete și afișați-le ca un arbore în bara laterală." + }, + "parent_field": "Etichetă părinte", + "no_parent": "Fără etichetă părinte", + "too_long": "Această cale de etichetă este prea lungă (cel mult {max} caractere)", + "has_children_locked": "Alte etichete sunt imbricate sub aceasta, așa că numele și eticheta părinte sunt blocate. Mutați-le sau eliminați-le mai întâi.", + "has_children_delete": "Eliminați mai întâi etichetele imbricate sub aceasta", + "visibility_field": "Vizibilitate în bara laterală", + "visibility": { + "show": "Afișează", + "unread": "Afișează dacă sunt necitite", + "hide": "Ascunde" + }, + "unrecognized": { + "title": "Recuperați etichetele de pe server", + "description": "Numele și culorile etichetelor sunt stocate în acest browser, dar etichetele în sine se află pe e-mailurile dvs. sub formă de cuvinte-cheie JMAP. Scanați corespondența pentru cuvinte-cheie care nu au o etichetă aici și adăugați-le înapoi.", + "scan": "Căutați etichete", + "rescan": "Scanați din nou", + "scanning": "Se scanează… {scanned} din {total} e-mailuri", + "none": "Toate etichetele de pe e-mailurile dvs. sunt deja definite aici.", + "messages": "{count, plural, one {# e-mail} few {# e-mailuri} other {# de e-mailuri}}", + "add_all": "Adăugați toate cele {count}", + "partial": "Au fost scanate doar cele mai recente {scanned} din {total} e-mailuri. Etichetele folosite doar pe mesaje mai vechi pot lipsi.", + "error": "Corespondența nu a putut fi scanată pentru etichete." + } + }, + "notifications": { + "test_sound": "Testați sunetul de notificare", + "sounds": { + "default": "Implicit (Bip)", + "cheerful": "Vesel", + "involved": "Implicat", + "swift": "Gest rapid", + "relax": "Relaxează-te" + }, + "push": { + "title": "Notificări în fundal", + "description": "Primiți notificări de sistem pentru e-mailuri noi chiar și atunci când acest site este închis. Notificările sunt transmise prin intermediul serverului de push Bulwark; serverul nu vede niciodată conținutul e-mailurilor.", + "relay_label": "Relay push", + "relay_desc": "Implicit este setat serverul de retransmitere găzduit Bulwark. Modificați această setare numai dacă utilizați un server propriu.", + "relay_locked": "Configurat de administrator", + "relay_locked_desc": "Relay-ul push URL a fost configurat de administratorul dvs. și nu poate fi modificat.", + "status_active": "Activ pe acest dispozitiv", + "status_inactive": "Nu este activat pe acest dispozitiv", + "status_unsupported": "Acest browser nu acceptă Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "Se lucrează…", + "enable": "Activați", + "reenable": "Reînregistrează-te", + "disable": "Dezactivare", + "confirm_disable_title": "Dezactivați notificările din fundal?", + "confirm_disable_message": "Acest dispozitiv nu va mai primi alerte atunci când site-ul este închis.", + "dismiss_aria": "Ignorați solicitarea de notificări", + "ios_hint": "Pe iOS, instalați mai întâi site-ul pe ecranul de start – Safari trimite notificări Web Push numai către aplicațiile web progresive (PWA) instalate.", + "devices_title": "Dispozitive înregistrate", + "devices_desc": "Toate dispozitivele înregistrate să primească notificări în fundal pentru acest cont. Revocarea unuia îl oprește imediat din a le primi.", + "devices_loading": "Se încarcă dispozitivele înregistrate…", + "devices_empty": "Niciun dispozitiv nu este înregistrat pentru notificări în fundal.", + "devices_error": "Dispozitivele înregistrate nu au putut fi încărcate.", + "devices_refresh": "Reîmprospătează", + "device_this": "Acest dispozitiv", + "device_other": "Alt dispozitiv", + "device_status_active": "Livrează notificări", + "device_status_inactive": "Nu a livrat niciodată o notificare", + "device_status_unknown": "Necunoscut acestui releu", + "device_expires": "Expiră la {date}", + "revoke": "Revocă", + "confirm_revoke_title": "Revocați acest dispozitiv?", + "confirm_revoke_message": "Va înceta imediat să primească notificări în fundal. Reactivarea push pe acel dispozitiv îl va înregistra din nou.", + "confirm_revoke_message_this": "Acest dispozitiv va înceta imediat să primească notificări în fundal." + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Sunet de notificare", + "description": "Alegeți ce sunet să se redea pentru notificări", + "choose": "Sunet", + "choose_desc": "Selectați un ton de notificare și faceți clic pe pictograma difuzorului pentru a-l asculta" + }, + "email": { + "title": "Notificări prin e-mail", + "description": "Configurați notificările pentru e-mailurile primite", + "enabled": "Notificări prin e-mail", + "enabled_desc": "Afișați notificări la primirea de e-mailuri noi", + "sound": "Sunet de notificare", + "sound_desc": "Redă o alertă audio la sosirea unor e-mailuri noi", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Notificări din calendar", + "description": "Configurați notificările pentru evenimentele din calendar", + "enabled": "Notificări privind evenimentele", + "enabled_desc": "Afișați alerte pentru evenimentele viitoare din calendar", + "sound": "Sunet de notificare", + "sound_desc": "Redă o alertă audio pentru mementourile din calendar", + "invitation_parsing": "Analizați invitațiile prin e-mail", + "invitation_parsing_desc": "Detectați invitațiile din calendar din atașamentele de e-mail și afișați acțiunile din calendar" + } + }, + "language_region": { + "title": "Limba, regiunea și ora", + "description": "Limba, formatul datei, formatul orei și alte preferințe regionale", + "language": { + "label": "Limba", + "description": "Alegeți limba preferată", + "english": "Engleză", + "french": "Français" + }, + "date_format": { + "label": "Formatul datei", + "description": "Modul în care sunt afișate datele în lista de e-mailuri", + "smart": "Inteligent", + "relative": "Relativ", + "full": "Întotdeauna data completă", + "preview_today": "Astăzi:", + "preview_this_week": "În această săptămână:", + "preview_older": "Versiune anterioară:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Formatul orei", + "description": "Alegeți între formatul de 12 ore sau cel de 24 de ore", + "12h": "12 ore", + "24h": "24 de ore" + }, + "first_day": { + "label": "Prima zi a săptămânii", + "description": "Începeți săptămâna duminică sau luni", + "sunday": "Duminică", + "monday": "Luni" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Șterge căutarea la schimbarea folderului", + "description": "Un clic pe un folder (Inbox, Trimise…) șterge căutarea activă și afișează acel folder. Dezactivat, căutarea rămâne și se reia în folderul ales." + }, + "request_read_receipt": { + "label": "Solicitați confirmări de citire în mod implicit", + "description": "Activați în prealabil solicitarea de confirmare de citire atunci când redactați un mesaj nou." + }, + "read_receipt_response": { + "label": "Răspundeți la solicitările de confirmare de citire", + "description": "Ce trebuie făcut atunci când un mesaj primit solicită o confirmare de citire.", + "ask": "Întrebați de fiecare dată", + "always": "Trimiteți întotdeauna", + "never": "Nu trimiteți niciodată" + }, + "title": "Comportamentul e-mailurilor", + "description": "Configurați modul în care sunt gestionate e-mailurile", + "mark_read": { + "label": "Marcați ca citit", + "description": "Când să marcați e-mailurile ca citite atunci când sunt deschise", + "instant": "Imediat", + "delay_3s": "După 3 secunde", + "delay_5s": "După 5 secunde", + "never": "Niciodată" + }, + "delete_action": { + "label": "Acțiunea Ștergere", + "description": "Ce se întâmplă când ștergeți un e-mail", + "trash": "Mută în Coșul de gunoi", + "trash_and_read": "Mută în Coșul de gunoi și marchează ca citit", + "permanent": "Șterge definitiv", + "warning": "E-mailurile vor fi șterse definitiv și nu pot fi recuperate. Această acțiune este ireversibilă." + }, + "message_spacing": { + "label": "Spațierea mesajului", + "description": "Spațiu în jurul corpului mesajului în cititor", + "auto": "Automat (recomandat)", + "always": "Adaugă întotdeauna spațiere", + "edge": "Din margine în margine" + }, + "plain_text_font": { + "label": "Font pentru text simplu", + "description": "Fontul folosit pentru e-mailurile în text simplu", + "mono": "Monospațiat", + "sans": "Normal (la fel ca aplicația)" + }, + "archive_mode": { + "label": "Arhivați în", + "description": "Cum să organizați e-mailurile atunci când le arhivați", + "single": "Un singur dosar", + "year": "Un dosar pentru fiecare an", + "month": "Un dosar pentru fiecare lună", + "reorganize": "Reorganizați arhiva existentă", + "reorganize_success": "{count, plural, =0 {Nu există e-mailuri de reorganizat} one {1 e-mail reorganizat} few {# e-mailuri reorganizate} other {# de e-mailuri reorganizate}}", + "reorganize_error": "Nu s-a putut reorganiza arhiva" + }, + "permanently_delete_junk": { + "label": "Șterge definitiv mesajele nedorite", + "description": "Ștergeți definitiv e-mailurile din folderul „Junk/Spam” în loc să le mutați în „Coșul de gunoi”" + }, + "confirm_bulk_delete": { + "label": "Confirmă înainte de a șterge mai multe mesaje", + "description": "Întreabă înainte de a muta mai multe mesaje selectate în coșul de gunoi. Ștergerea definitivă întreabă întotdeauna." + }, + "mail_layout": { + "label": "Structura e-mailului", + "description": "Alegeți între panoul de citire clasic divizat, un flux de citire concentrat în stilul Gmail sau un panou de citire în partea de jos, în stilul Zimbra.", + "split": "Panou divizat", + "split_description": "Mențineți lista de mesaje și panoul de citire vizibile unul lângă celălalt.", + "focus": "Listă cu elemente evidențiate", + "focus_description": "Afișați câte o linie pe mesaj și deschideți e-mailul pe toată lățimea ecranului, menținând în același timp vizibilă bara laterală a folderelor.", + "horizontal": "Panoul de citire în partea de jos", + "horizontal_description": "Afișați lista de mesaje în partea de sus și deschideți mesajul selectat într-un panou de citire situat sub aceasta." + }, + "list_columns": { + "label": "Afișează lista pe coloane", + "description": "Afișează rândurile pe o singură linie ca tabel cu coloanele De la, Subiect, Dată și Dimensiune sub un antet." + }, + "show_preview": { + "label": "Afișați textul de previzualizare", + "description": "Afișați previzualizarea e-mailurilor în listă", + "focus_description": "Afișați textul de previzualizare în linie în lista de mesaje cu o singură linie selectată" + }, + "show_verification_codes": { + "label": "Afișează codurile de verificare", + "description": "Oferă codul din e-mailurile de autentificare și confirmare pentru copiere cu un clic, în listă timp de o zi și în mesaj" + }, + "disable_threading": { + "label": "Dezactivați gruparea conversațiilor", + "description": "Afișați e-mailurile ca mesaje individuale, în loc să le grupați pe conversații" + }, + "plain_text_mode": { + "label": "Numai text simplu", + "description": "Dezactivați editorul de text formatat și trimiteți toate e-mailurile doar ca text simplu, inclusiv răspunsurile și redirecționările" + }, + "auto_select_reply_identity": { + "label": "Răspunde de la adresa de la care a fost primit mesajul", + "description": "Când răspundeți, trimiteți mesajul de la adresa la care a fost trimis inițial. Se potrivesc mai întâi identitățile; pentru livrările de tip „catch-all” ale domeniului, antetul „De la” este rescris cu aliasul, în timp ce trimiterea se face prin identitatea dvs. principală." + }, + "reply_identity_match": { + "label": "Potrivirea adresei de primire", + "description": "Care adrese de primire sunt considerate ale tale. „Doar adresa exactă” alege numai una dintre identitățile configurate; „același domeniu” tratează și orice altă adresă de pe domeniile identităților tale ca alias catch-all și rescrie antetul De la cu ea. Alege adresa exactă dacă aceste adrese sunt liste de distribuție.", + "exact": "Doar adresa exactă", + "domain": "Orice adresă de pe domeniile mele" + }, + "signature_position": { + "label": "Poziția semnăturii", + "description": "Unde să inserați semnătura în răspunsuri și redirecționări. Deasupra textului citat, semnătura se citește în mod natural ca o încheiere a răspunsului; sub text, mesajul original rămâne contiguu.", + "above_quote": "Înaintea textului citat", + "below_quote": "După textul între ghilimele" + }, + "signature_separator": { + "label": "Delimitator de semnătură", + "description": "Prefațați semnătura cu linia standard de delimitare „-- ” (RFC 3676). Dezactivați această opțiune dacă preferați ca mesajul să treacă direct în semnătură." + }, + "sub_address_delimiter": { + "label": "Delimitator de subadresă", + "description": "Caracterul care separă numele dvs. de utilizator de eticheta subadresei. Folosiți delimitatorul utilizat de serverul dvs. de e-mail (de exemplu, user {delimiter} tag@domain.com).", + "option": "{delimiter} ({delimiter} tag@domain.com)", + "custom": "Personalizare…", + "custom_input_label": "Caracter delimitator personalizat" + }, + "attachment_click_action": { + "label": "Acțiunea de clic pe atașament", + "description": "Alegeți dacă, la clic pe un fișier atașat, acesta să fie previzualizat sau descărcat imediat", + "preview": "Previzualizați, dacă este posibil", + "download": "Descărcați imediat" + }, + "attachment_position": { + "label": "Poziția atașamentului", + "description": "Unde să afișați atașamentele în antetul e-mailului", + "beside-sender": "Lângă expeditor", + "below-header": "Sub antet" + }, + "emails_per_page": { + "10": "10 e-mailuri", + "25": "25 de e-mailuri", + "50": "50 de e-mailuri", + "100": "100 de e-mailuri", + "label": "E-mailuri pe pagină", + "description": "Numărul de e-mailuri care se încarcă simultan" + }, + "always_light_mode": { + "label": "Afișați întotdeauna e-mailurile în modul luminos", + "description": "Afișați conținutul e-mailurilor în modul luminos chiar și atunci când aplicația se află în modul întunecat, evitând astfel problemele de conversie asociate modului întunecat" + }, + "external_content": { + "label": "Conținut extern", + "description": "Cum se gestionează imaginile și conținutul extern", + "ask": "Întrebați întotdeauna", + "block": "Blocați întotdeauna", + "allow": "Permiteți întotdeauna" + }, + "trusted_senders": { + "label": "Expeditori de încredere", + "description": "Gestionați expeditorii ale căror imagini se încarcă automat", + "count_zero": "Niciunul", + "count_one": "1 expeditor", + "count_other": "{count} expeditori", + "modal_title": "Expeditori de încredere", + "empty_title": "Nu există încă expeditori de încredere", + "empty_description": "Când vizualizați un e-mail cu imagini blocate, faceți clic pe „Aveți întotdeauna încredere în acest expeditor” pentru a le adăuga aici.", + "add_manually": "Adăugați expeditorul manual", + "add_button": "Adăugați", + "add_placeholder": "Introduceți adresa de e-mail", + "search_placeholder": "Căutați expeditori...", + "no_results": "Nu există expeditori care să corespundă căutării dvs.", + "remove": "Eliminați", + "close": "Închide", + "invalid_email": "Vă rugăm să introduceți o adresă de e-mail validă", + "already_added": "Acest expeditor este deja de încredere", + "save_error": "Nu s-a putut salva – verificați jurnalul de depanare al Contacte pentru detalii", + "use_address_book_label": "Sincronizare cu agenda de adrese", + "use_address_book_description": "Salvați expeditorii de încredere într-o agendă dedicată, denumită „Expeditori de încredere”, astfel încât aceștia să se sincronizeze pe toate dispozitivele dvs." + }, + "hover_actions": { + "label": "Acțiuni rapide la trecerea cursorului", + "description": "Alegeți ce acțiuni rapide să apară atunci când treceți cu mouse-ul peste un e-mail din listă", + "delete": "Șterge", + "star": "Marcați cu stea / Dezmarcați", + "mark_read": "Marcați ca citit / necitit", + "archive": "Arhivează", + "tag": "Etichetă", + "spam": "Marcați ca spam", + "not_spam": "Nu este spam", + "none_selected": "Nu sunt selectate acțiuni", + "mode_label": "Modul de afișare", + "mode_inline": "În text", + "mode_floating": "Plutitoare", + "corner_label": "Poziție flotantă", + "corner_top-left": "Stânga sus", + "corner_top-right": "Colțul din dreapta sus", + "corner_bottom-left": "Stânga jos", + "corner_bottom-right": "În partea dreaptă jos" + }, + "default_mail_program": { + "label": "Programul de e-mail implicit", + "description": "Setați {appName} ca program de e-mail implicit pentru linkurile de tip mailto:", + "button": "Setează ca implicit", + "success": "Browserul este solicitat să îl seteze ca implicit", + "error": "Browserul dvs. nu acceptă această funcție" + }, + "attachment_reminder": { + "label": "Memento privind atașamentele", + "description": "Afișați un avertisment înainte de trimitere atunci când mesajul menționează atașamente, dar nu sunt atașate niciunul", + "keywords_label": "Cuvinte-cheie declanșatoare", + "keywords_description": "Cuvinte sau expresii care declanșează memento-ul atunci când sunt găsite în mesajul dvs.", + "add_placeholder": "Adăugați cuvântul cheie...", + "add": "Adăugați", + "remove": "Eliminați" + }, + "empty_subject_warning": { + "label": "Avertisment pentru subiect gol", + "description": "Cere confirmare înainte de a trimite un mesaj fără subiect." + }, + "recipient_mentions": { + "label": "Menționează destinatarii cu @", + "description": "Tastarea @ în mesaj propune destinatarii și inserează prenumele celui ales. Enter sau Tab alege numele evidențiat." + }, + "mention_adds_recipient": { + "label": "Contacte în mențiunile cu @", + "description": "Când tastezi @ sunt propuse și contactele tale și persoanele din director. Dacă alegi pe cineva care nu e încă destinatar, este adăugat la mesaj.", + "to": "Adaugă la Către", + "cc": "Adaugă la CC", + "off": "Doar destinatarii" + }, + "hide_inline_image_attachments": { + "label": "Ascundeți imaginile încorporate din atașamente", + "description": "Imaginile încorporate în corpul mesajului nu sunt listate ca atașamente separate" + }, + "attachment_image_previews": { + "label": "Afișați previzualizări ale imaginilor din atașamente", + "description": "Afișați atașamentele de tip imagine sub formă de miniaturi, în loc de pictograme generice de fișiere" + }, + "send_delay": { + "label": "Anulare trimitere / amânare trimitere", + "description": "Amânați trimiterile normale pentru o scurtă perioadă de timp pe partea de server.", + "off": "Oprit", + "seconds": "{seconds} secunde", + "unsupported": "Contul curent nu indică faptul că acceptă trimiterea amânată. Setarea este totuși salvată pentru alte conturi." + }, + "return_to_list_after_action": { + "label": "Revenire la listă după ștergere sau marcare ca necitit", + "description": "După ștergerea sau marcarea mesajului deschis ca necitit, reveniți la lista de mesaje în loc să deschideți mesajul următor." + }, + "swipe_right_action": { + "label": "Acțiune la glisare spre dreapta (mobil)", + "description": "Pe dispozitivele tactile, acțiunea efectuată când glisați un mesaj spre dreapta în listă." + }, + "swipe_left_action": { + "label": "Acțiune la glisare spre stânga (mobil)", + "description": "Pe dispozitivele tactile, acțiunea efectuată când glisați un mesaj spre stânga în listă." + }, + "swipe_actions": { + "none": "Niciuna", + "archive": "Arhivează", + "delete": "Șterge", + "mark_read": "Comută citit", + "star": "Comută steaua", + "spam": "Marchează ca spam" + }, + "rtl_editing": { + "label": "Suport pentru editare de la dreapta la stânga", + "description": "Adaugă un buton de direcție în bara de instrumente a editorului, cu care paragrafele pot fi setate de la stânga la dreapta sau de la dreapta la stânga" + } + }, + "composer": { + "title": "Redactare", + "description": "Configurați setările de redactare a e-mailurilor", + "autosave": { + "label": "Interval de salvare automată", + "description": "Cât de des să se salveze automat schițele", + "30s": "La fiecare 30 de secunde", + "1m": "În fiecare minut", + "2m": "La fiecare 2 minute", + "5m": "La fiecare 5 minute" + }, + "send_confirmation": { + "label": "Confirmare trimitere", + "description": "Solicitați confirmarea înainte de a trimite e-mailurile" + }, + "default_reply": { + "label": "Modul implicit de răspuns", + "description": "Acțiunea implicită la clic pe „Răspunde”", + "reply": "Răspunde", + "reply_all": "Răspunde tuturor" + } + }, + "privacy": { + "title": "Confidențialitate și securitate", + "description": "Gestionați-vă setările de confidențialitate și securitate", + "external_images": { + "label": "Blocați imaginile externe", + "description": "Preveniți urmărirea prin intermediul imaginilor externe" + }, + "session_timeout": { + "label": "Expirarea sesiunii", + "description": "Deconectare automată după o perioadă de inactivitate", + "never": "Niciodată", + "30m": "30 de minute", + "1h": "1 oră", + "4h": "4 ore" + }, + "clear_cache": { + "label": "Golește memoria cache", + "description": "Ștergeți datele stocate în cache și fișierele temporare", + "button": "Golește memoria cache", + "confirm": "Sunteți sigur că doriți să goliți memoria cache?", + "success": "Memoria cache a fost golită cu succes" + } + }, + "account": { + "title": "Cont", + "description": "Vizualizați informațiile contului dvs.", + "name_label": "Nume afișat", + "username_label": "Nume de utilizator", + "account_type_label": "Tipul contului", + "auth_method_label": "Autentificare", + "auth_method_oauth": "Autentificare unică (OAuth /OIDC)", + "auth_method_basic": "Parolă", + "auth_method_token": "Token de acces", + "demo_account": "Cont demo", + "email": { + "label": "Adresă de e-mail", + "value": "{email}" + }, + "server": { + "label": "JMAP Server", + "value": "{server}" + }, + "storage": { + "label": "Utilizarea spațiului de stocare", + "used": "{used} {total}-ul utilizat", + "percentage": "{percent} % utilizat" + }, + "last_sync": { + "label": "Ultima sincronizare", + "value": "{time}" + }, + "accounts": { + "title": "Conturi conectate", + "description": "Trageți pentru a reordona modul în care apar conturile în meniul derulant al conturilor", + "active": "Cont activ în prezent", + "default_badge": "Cont implicit", + "set_default": "Setați ca implicită", + "switch_to": "Treceți la acest cont", + "move_up": "Sus", + "move_down": "Derulați în jos", + "drag_handle": "Trageți pentru a reordona", + "add": "Adăugare cont" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Partajat cu mine", + "description": "Conturi de grup și conturi partajate pe care le puteți gestiona. Selectați unul pentru a-i edita filtrele, răspunsul automat de vacanță, calendarele și contactele.", + "shared_label": "Cont partajat" + } + }, + "scoped": { + "back": "Înapoi la contul meu", + "managing": "Administrare: {name}" + }, + "security": { + "title": "Securitatea contului", + "description": "Gestionați-vă parola, autentificarea în doi pași și setările de securitate", + "detecting": "Se detectează capacitățile serverului...", + "not_available": "Gestionarea securității contului nu este disponibilă pentru acest server de e-mail. Este posibil ca permisiunile necesare să fie dezactivate. Consultați documentația pentru detalii.", + "password": { + "title": "Schimbă parola", + "current": "Parola curentă", + "new": "Parolă nouă", + "confirm": "Confirmă noua parolă", + "submit": "Schimbă parola", + "success": "Parola a fost modificată cu succes", + "error_title": "Schimbarea parolei a eșuat", + "error_mismatch": "Noile parole nu se potrivesc", + "error_min_length": "Parola trebuie să conțină cel puțin 8 caractere", + "error_generic": "Nu s-a putut schimba parola" + }, + "display_name": { + "label": "Nume afișat", + "description": "Numele dvs. așa cum apare pe server", + "placeholder": "Introduceți numele afișat", + "save": "Salvare", + "success": "Numele afișat a fost actualizat", + "error": "Nu s-a reușit actualizarea numelui afișat" + }, + "totp": { + "section_title": "Autentificare în doi pași", + "label": "Autentificare TOTP", + "description": "Adăugați un nivel suplimentar de securitate cu o parolă unică, valabilă o singură dată, bazată pe timp", + "active": "Activat", + "inactive": "Dezactivat", + "enabled": "Autentificarea în doi pași este activată", + "disabled": "Autentificarea în doi pași este dezactivată", + "enable_error": "Nu s-a reușit activarea autentificării în doi pași (2FA)", + "disable_error": "Nu s-a reușit dezactivarea autentificării în doi pași (2FA)", + "setup_instructions": "Copiați acest cod URL în aplicația dvs. de autentificare (Google Authenticator, Authy etc.):", + "verification_code": "Cod de verificare", + "confirm": "Confirmare", + "disable": "Dezactivare", + "disable_confirm_prompt": "Introduceți parola pentru a dezactiva autentificarea în doi pași.", + "password_required": "Este necesară introducerea parolei", + "code_required": "Este necesar un cod de verificare", + "code_invalid": "Cod de verificare invalid. Verificați aplicația de autentificare și încercați din nou." + }, + "app_passwords": { + "title": "Parole pentru aplicații", + "description": "Creați parole pentru aplicațiile care nu acceptă autentificarea în doi pași", + "add": "Adăugați", + "create": "Creați", + "cancel": "Anulează", + "done": "Gata", + "generate": "Generați", + "name_label": "Numele aplicației", + "name_placeholder": "de exemplu, Thunderbird, Mail pe iPhone", + "expires_label": "Expiră (opțional)", + "allowed_ips_label": "Adresele IP permise (opțional)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Separate prin virgulă sau spațiu. Lăsați câmpul gol pentru a permite orice adresă IP.", + "password_label": "Parolă (lăsați câmpul gol pentru generarea automată)", + "password_placeholder": "Generat automat dacă este gol", + "copy_now_warning": "Copiați această parolă acum – nu va mai fi afișată din nou.", + "added": "Parola aplicației a fost creată", + "removed": "Parola aplicației a fost eliminată", + "add_error": "Nu s-a putut crea parola aplicației", + "remove_error": "Nu s-a reușit eliminarea parolei aplicației", + "none": "Nu sunt configurate parole pentru aplicații" + }, + "api_keys": { + "title": "API Chei", + "description": "Creați chei de tip „API” pentru scripturi și integrări care comunică direct cu serverul", + "name_label": "Numele cheii", + "name_placeholder": "de ex. script de backup, CI runner", + "copy_now_warning": "Copiați acum această cheie „API” – nu va mai fi afișată din nou.", + "added": "API cheie creată", + "removed": "API cheie eliminată", + "add_error": "Nu s-a putut crea cheia dAPI", + "remove_error": "Nu s-a putut elimina cheia „API”", + "none": "Nu sunt configurate tastele „API”" + }, + "encryption": { + "section_title": "Criptarea datelor stocate", + "label": "Criptarea e-mailurilor", + "description": "Criptați e-mailurile stocate pe server pentru un plus de confidențialitate", + "active": "{type} criptare activată", + "inactive": "Dezactivat", + "enabled": "Criptarea datelor stocate este activată", + "disabled_success": "Criptarea datelor stocate este dezactivată", + "error": "Nu s-au putut actualiza setările de criptare", + "algorithm_label": "Algoritm de criptare", + "encrypt_on_append": "Criptați e-mailurile noi la încărcare", + "allow_spam_training": "Permiteți antrenarea filtrului de spam înainte de criptarea e-mailurilor", + "enable_encryption": "Activați criptarea", + "enable_tooltip": "Activați criptarea cu această cheie", + "configure_title": "Activați criptarea cu această cheie", + "enable_button": "Activați", + "disable_tooltip": "Dezactivați criptarea", + "enabled_success": "Criptarea datelor stocate este activată" + }, + "email_client": { + "title": "Configurarea clientului de e-mail", + "description": "Utilizați aceste date de autentificare pentru a configura clientul de e-mail de pe desktop sau de pe dispozitivul mobil (Thunderbird, Apple Mail, Outlook etc.)", + "jmap_username_label": "JMAP Nume de utilizator", + "copy": "Copiere", + "copied": "Copiat", + "password_instructions": "Folosiți numele de utilizator de la JMAP menționat mai sus, împreună cu o parolă de aplicație, pentru a vă autentifica în clientul de e-mail. Creați o parolă de aplicație în secțiunea de mai sus, dacă nu ați făcut-o deja." + }, + "link_device": { + "title": "Aplicație mobilă", + "description": "Conectați-vă la aplicația Bulwark Mail de pe telefon fără să introduceți parola acolo. Telefonul primește propria conectare, așa că deconectarea de aici nu îl deconectează.", + "generate": "Afișați codul QR", + "regenerate": "Afișați un cod nou", + "instructions": "Deschideți aplicația Bulwark Mail și atingeți „Scan a sign-in code” pe ecranul de conectare (sau „Add account” → „Scan a sign-in code” dacă sunteți deja conectat), apoi îndreptați camera spre acest ecran.", + "expires_in": "Acest cod expiră în {seconds} secunde. Poate fi utilizat o singură dată.", + "expired": "Acest cod a expirat.", + "error": "Nu s-a putut crea un cod de asociere. Vă rugăm să încercați din nou.", + "password_prompt": "Introduceți parola pentru a confirma că sunteți dvs. Telefonul primește propria conectare; parola nu este trimisă către telefon.", + "show_password": "Afișați parola", + "hide_password": "Ascundeți parola", + "continue": "Continuați", + "error_wrong_password": "Parolă greșită. Încercați din nou.", + "totp_prompt": "Introduceți codul din aplicația de autentificare.", + "error_password_or_code": "Parola sau codul este incorect.", + "error_not_signed_in": "Sesiunea a expirat. Conectați-vă din nou pentru a asocia un dispozitiv.", + "error_impersonation": "Nu puteți asocia dispozitive în timp ce vizualizați căsuța poștală a altui utilizator.", + "error_too_many_attempts": "Prea multe încercări. Așteptați puțin și încercați din nou.", + "error_session_secret": "Asocierea dispozitivelor nu este configurată pe acest server (lipsește SESSION_SECRET). Contactați administratorul.", + "error_server_unreachable": "Serverul de e-mail nu a putut fi contactat. Încercați din nou.", + "error_pairing_unavailable": "Serverul dvs. de e-mail nu permite asocierea dispozitivelor în acest mod. Contactați administratorul.", + "qr_alt": "Cod QR de conectare", + "link_label": "Link de conectare", + "copy_link": "Copiați linkul de conectare", + "copied": "Copiat", + "copy_hint": "Nu aveți cameră? Copiați linkul și lipiți-l în aplicație cu „Paste sign-in link”.", + "success_title": "Conectat pe telefon", + "success_body": "Puteți închide această pagină. Pentru a asocia un alt dispozitiv, afișați un cod nou.", + "link_another": "Asociați alt dispozitiv", + "use_sso": "Folosește autentificarea unică în schimb", + "error_insecure_server": "Aplicația mobilă se conectează la serverele de e-mail doar prin https, iar acesta folosește http." + }, + "public_keys": { + "name_label": "Numele cheii publice", + "name_placeholder": "de ex. Cheia mea", + "key_label": "Cheie publică (S/MIME sau PGP în format ASCII)", + "key_placeholder": "Lipiți aici cheia dvs. publică", + "none": "Nicio cheie publică configurată" + } + }, + "identities": { + "title": "Identități de expediere", + "description": "Gestionați adresele de e-mail de la care puteți trimite mesaje", + "identities_count": { + "label": "Identitățile tale", + "description": "Adrese de e-mail configurate pentru trimitere", + "count_zero": "Fără identități", + "count_one": "1 identitate", + "count_other": "{count} identități" + }, + "manage": "Gestionarea identităților", + "sub_addressing": { + "label": "Subadresare", + "description": "Folosiți etichete precum user+tag@domain.com pentru a organiza e-mailurile primite", + "learn_more": "Aflați mai multe" + } + }, + "vacation": { + "title": "Absență", + "description": "Gestionați automat e-mailurile primite în timp ce sunteți plecat", + "loading": "Se încarcă setările de concediu...", + "not_supported": "Serverul dvs. de e-mail nu acceptă răspunsurile de vacanță.", + "fetch_error": "Nu s-au putut încărca setările de concediu. Vă rugăm să încercați din nou.", + "status": { + "label": "Răspunde automat", + "description": "Trimiteți un răspuns automat persoanelor care vă trimit e-mailuri", + "active": "Activ", + "inactive": "Inactiv" + }, + "date_range": { + "start": "Data de începere", + "start_description": "Lăsați necompletat dacă nu există o limită inițială", + "end": "Data de încheiere", + "end_description": "Lăsați necompletat dacă nu există o limită de sfârșit" + }, + "message": { + "subject_label": "Subiect", + "subject_description": "Subiectul răspunsului automat", + "subject_placeholder": "Absent de la birou", + "body_label": "Corpul mesajului", + "body_description": "Conținutul mesajelor în text simplu", + "body_placeholder": "Vă mulțumesc pentru e-mail. În prezent nu mă aflu la birou și vă voi răspunde la întoarcere.", + "html_label": "Mesaj formatat (HTML)", + "html_description": "Adăugați o versiune formatată, cu linkuri și stil. Destinatarii al căror client de e-mail nu o poate afișa vor vedea textul simplu de mai sus.", + "html_placeholder": "Scrieți un răspuns de absență formatat…" + }, + "preview": { + "title": "Previzualizare", + "show": "Afișează previzualizarea", + "hide": "Ascunde previzualizarea" + }, + "save": "Salvați modificările", + "saving": "Se salvează...", + "warnings": { + "end_before_start": "Data de încheiere trebuie să fie ulterioară datei de începere", + "invalid_date": "Introduceți o dată de început și de sfârșit validă, cu oră, sau lăsați câmpul gol", + "start_in_past": "Data de începere este în trecut", + "empty_body": "Corpul mesajului este gol – destinatarii vor primi un răspuns gol", + "subject_too_long": "Subiectul este prea lung pentru server (cel mult {max} octeți).", + "body_too_long": "Mesajul este prea lung pentru server (cel mult {max} octeți, inclusiv formatarea).", + "forward_address": "Introduceți o adresă validă pentru redirecționare", + "not_running": "Redirecționarea sau destinatarii răspunsului sunt salvați, dar momentan nu sunt activi. Salvați pentru a-i reactiva." + }, + "forward": { + "title": "Redirecționare", + "description": "Redirecționează mesajele primite către o altă adresă, cu sau fără răspuns automat, în perioada de mai sus (fără dată de încheiere, până când este dezactivată). Spamul nu este redirecționat.", + "enabled_label": "Redirecționează mesajele", + "to_label": "Redirecționează către", + "to_placeholder": "email@example.com", + "keep_label": "Păstrează în căsuța de primire", + "keep_description": "Altfel, mesajul este doar redirecționat: nu rămâne în această căsuță poștală și nu i se aplică nicio regulă de filtrare." + }, + "audience": { + "label": "Răspunde la", + "description": "Expeditori interni: {domains}", + "all": "Toți expeditorii", + "internal": "Doar expeditorii interni", + "external": "Doar expeditorii externi" + }, + "auto_reply": { + "title": "Răspuns automat" + } + }, + "folders": { + "role_memos": "Note", + "title": "Dosare", + "description": "Gestionați-vă folderele de e-mail și atribuiți roluri standard", + "folder_list": "Dosarele dvs.", + "folder_list_description": "Faceți clic pe pictograma unui dosar pentru a-l personaliza", + "standard_roles": "Rolurile standard ale folderelor", + "standard_roles_description": "Stabiliți care sunt folderele utilizate pentru rolurile standard ale căsuței poștale, cum ar fi „Inbox”, „Sent”, „Trash” etc.", + "role_inbox": "Primite", + "role_drafts": "Ciorne", + "role_sent": "Trimise", + "role_trash": "Coș de gunoi", + "role_junk": "Spam / Mesaje nedorite", + "role_archive": "Arhivă", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Niciunul", + "create_folder": "Creați un dosar", + "create_subfolder": "Creați un subfolder", + "reorder": "Trageți pentru a reordona", + "reorder_error": "Reordonarea dosarelor a eșuat", + "move_error": "Mutarea dosarului a eșuat", + "select_folder": "Selectează {name}", + "select_hint": "Selectează. Shift+clic selectează toate dosarele până la ultimul pe care ai dat clic.", + "selected_count": "{count, plural, one {# dosar selectat} few {# dosare selectate} other {# de dosare selectate}}", + "move_to": "Mută în...", + "move_to_top_level": "Nivelul superior", + "move": "Mută", + "clear_selection": "Golește selecția", + "folders_moved": "{count, plural, one {# dosar mutat} few {# dosare mutate} other {# de dosare mutate}}", + "move_partial_error": "{count, plural, one {# dosar nu a putut fi mutat} few {# dosare nu au putut fi mutate} other {# de dosare nu au putut fi mutate}}", + "subfolder_of": "În interiorul {name}", + "subfolder_name": "Numele subfolderului", + "new_folder_name": "Numele folderului", + "rename": "Redenumire", + "change_icon": "Schimbă pictograma", + "count_mode": "Contor pentru {name}", + "count_mode_default": "Contor: implicit", + "count_mode_unread": "Doar necitite", + "count_mode_all": "Necitite și total", + "count_mode_none": "Fără contor", + "delete": "Șterge", + "confirm_delete": "Sunteți sigur că doriți să ștergeți „{name}”? E-mailurile din acest folder vor fi mutate în Coșul de gunoi.", + "create": "Creați", + "cancel": "Anulează", + "no_folders": "Fără foldere personalizate", + "cannot_delete_role": "Nu se poate șterge un dosar cu un rol standard. Eliminați mai întâi rolul.", + "folder_created": "Folder creat", + "folder_renamed": "Folder redenumit", + "folder_deleted": "Dosarul a fost șters", + "role_updated": "Rolul folderului a fost actualizat", + "error_create": "Nu s-a putut crea folderul", + "error_rename": "Nu s-a putut redenumi folderul", + "error_delete": "Nu s-a putut șterge folderul", + "error_delete_has_children": "Nu se poate șterge folderul: acesta conține încă subfoldere. Ștergeți-le sau mutați-le mai întâi.", + "error_delete_has_email": "Nu se poate șterge folderul: acesta conține încă e-mailuri. Mutați-le sau ștergeți-le mai întâi.", + "error_role": "Nu s-a putut actualiza rolul folderului" + }, + "advanced": { + "title": "Nivel avansat", + "description": "Opțiuni avansate și setări pentru dezvoltatori", + "debug_mode": { + "label": "Modul de depanare", + "description": "Activați înregistrarea detaliată a evenimentelor pentru depanare" + }, + "debug_categories": { + "description": "Selectați categoriile pe care doriți să le înregistrați. Dezactivați categoriile de care nu aveți nevoie pentru a reduce zgomotul din consolă.", + "jmap": "JMAP Client", + "jmap_description": "Operațiuni cu căsuța poștală, preluarea e-mailurilor și cererile prin protocolul JMAP", + "calendar": "Calendar", + "calendar_description": "Evenimente din calendar, importuri și programarea mesajelor", + "tasks": "Sarcini", + "tasks_description": "Crearea, preluarea și actualizarea sarcinilor din calendar", + "auth": "Autentificare", + "auth_description": "Autentificare, TOTP, schimb de tokenuri și gestionarea sesiunilor", + "filters": "Filtre", + "filters_description": "Sieve reguli de filtrare și scripturi de vacanță", + "email": "Vizualizarea e-mailurilor", + "email_description": "Renderizarea e-mailurilor, procesarea TNEF și marcarea ca citit", + "push": "Notificări push", + "push_description": "Configurarea și livrarea notificărilor push", + "contacts": "Contacte și agende de adrese", + "contacts_description": "Sincronizarea contactelor, operațiunile din agenda de adrese și expeditorii de încredere" + }, + "settings_sync": { + "label": "Sincronizarea setărilor", + "description": "Sincronizați-vă setările între browsere și dispozitive" + }, + "sender_favicons": { + "label": "Favicon-uri ale expeditorului", + "description": "Afișați pictogramele site-urilor web ca imagini de profil pentru expeditorii de afaceri", + "refresh_label": "Reîmprospătează pictogramele expeditorilor", + "refresh_description": "Descarcă din nou pictogramele site-urilor, de exemplu când un expeditor și-a schimbat pictograma", + "refresh_button": "Reîmprospătează", + "refresh_done": "Pictogramele expeditorilor vor fi descărcate din nou" + }, + "show_avatars_in_junk": { + "label": "Afișați avatarurile din folderul „Junk”", + "description": "Afișați imaginile de profil și faviconurile expeditorilor din folderul de mesaje nedorite. Această opțiune este dezactivată în mod implicit pentru a evita conferirea unei aparențe de legitimitate încercărilor de phishing." + }, + "keyboard_shortcuts": { + "label": "Comenzi rapide de la tastatură", + "description": "Vedeți comenzile rapide de la tastatură disponibile", + "button": "Comenzi rapide de vizualizare" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Resetare setări", + "description": "Restabiliți toate setările la valorile implicite", + "button": "Resezare la setările implicite" + }, + "export_settings": { + "label": "Setări de export", + "description": "Descărcați setările în format JSON", + "button": "Export" + }, + "import_settings": { + "label": "Importați setările", + "description": "Încărcați setările dintr-un fișier JSON", + "button": "Import" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Cod sursă" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Faceți clic pe spam și phishing înainte să ajungă în Inbox. Lăsați să treacă e-mailurile reale. Dacă pierdeți toate cele trei scuturi, căsuța cade.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "E-mail real" + }, + "click_to_block": "Faceți clic pentru blocare", + "let_through": "Lăsați să treacă", + "start": "Începeți", + "play_again": "Jucați din nou", + "score": "Scor", + "combo": "Bonus de serie", + "shields_left": "Au rămas {count} din {total} scuturi", + "seconds": "{seconds} s", + "best": "Record: {score}", + "new_best": "Record nou", + "held": "Inbox apărat", + "overrun": "Inbox copleșit", + "stats": { + "caught": "Blocate", + "delivered": "Livrate", + "mistakes": "Greșeli" + } + } + }, + "sidebar_apps": { + "title": "Aplicații din bara laterală", + "description": "Gestionați aplicațiile personalizate și linkurile din bara laterală", + "keep_loaded": "Mențineți aplicațiile încărcate", + "keep_loaded_description": "Mențineți aplicațiile încorporate în execuție în fundal atunci când comutați între ele, pentru a evita reîncărcarea", + "managed_title": "Furnizate de administrator", + "managed_description": "Aceste aplicații sunt configurate pentru toți și nu pot fi modificate.", + "manage_title": "Aplicații personalizate", + "manage_description": "Adăugați, editați sau eliminați aplicații personalizate din bara laterală" + }, + "contacts": { + "title": "Contacte", + "description": "Importați și exportați contactele", + "group_by_letter_label": "Grupați după prima literă", + "sort_by_last_name_label": "Sortare după nume de familie", + "sort_by_last_name_description": "Ordonați lista de contacte după numele de familie, astfel încât membrii familiei să apară împreună", + "group_by_letter_description": "Afișați titlurile secțiunilor în ordine alfabetică în lista de contacte", + "import_label": "Importați contactele", + "import_description": "Importați contactele dintr-un fișier „vCard” (.vcf)", + "export_label": "Exportați contactele", + "export_description": "Exportați toate contactele ca fișier VCF (vCard).", + "manage_title": "Agende de adrese", + "manage_description": "Redenumiți agendele de adrese", + "no_address_books": "Nu s-au găsit agende de adrese", + "categories_title": "Categorii", + "categories_description": "Redenumiți categoriile de contacte", + "no_categories": "Nu s-au găsit categorii" + }, + "downloads": { + "title": "Descărcări", + "description": "Personalizați modul în care sunt denumite e-mailurile și atașamentele descărcate.", + "reset": "Restabilește setările implicite", + "preview": "Previzualizare:", + "email_template": { + "label": "Numele fișierului de e-mail (.eml)", + "description": "Șablon utilizat atunci când exportați un e-mail sau îl trageți în sistemul de fișiere. Extensia .eml se adaugă automat." + }, + "attachment_template": { + "label": "Numele fișierului atașat", + "description": "Șablon utilizat la descărcarea sau glisarea unui atașament. Dacă omiteți „{filename}” și „{ext}”, extensia originală este păstrată." + }, + "bundle_template": { + "label": "Nume de fișier .zip cu mai multe e-mailuri", + "description": "Șablon utilizat atunci când trageți sau descărcați mai multe e-mailuri selectate ca o singură arhivă .zip. Extensia .zip se adaugă automat." + }, + "spaces": { + "label": "Spații", + "description": "Înlocuiți spațiile din numele fișierului rezultat cu un alt caracter.", + "keep": "Păstrați spațiile", + "underscore": "Înlocuiți cu _", + "dash": "Înlocuiți cu -" + }, + "lowercase": { + "label": "Cu litere mici", + "description": "Convertiți întregul nume de fișier în litere mici." + }, + "strip_diacritics": { + "label": "Eliminați semnele diacritice", + "description": "Convertiți literele accentuate în echivalentele lor ASCII (ä → a, é → e). Util pentru instrumentele care denaturează numele de fișiere Unicode." + }, + "collapse_separators": { + "label": "Ascunde separatoarele repetate", + "description": "Reduceți șirurile de spații, linii de subliniere sau liniuțe la un singur caracter." + }, + "after_export": { + "label": "După export", + "description": "Opțional, mutați e-mailul după ce l-ați exportat în format .eml.", + "keep": "Păstrează în căsuța poștală", + "archive": "Mută în arhivă", + "trash": "Mută în coșul de gunoi" + } + }, + "filters": { + "title": "Filtre de e-mail", + "description": "Creați reguli pentru a sorta, eticheta și gestiona automat e-mailurile primite", + "add_rule": "Adăugați regulă", + "no_rules": "Fără reguli de filtrare", + "no_rules_description": "Creați reguli pentru a vă organiza automat e-mailurile primite", + "vacation_active": "Răspunsul automat de vacanță este activ", + "vacation_active_description": "Răspunsul automat este activat pentru mesajele primite", + "vacation_configure": "Configurare", + "edit_rule": "Editați regula", + "new_rule": "Regulă nouă", + "delete_rule": "Șterge regulă", + "delete_confirm": "Sunteți sigur că doriți să ștergeți această regulă?", + "enable": "Activați", + "disable": "Dezactivare", + "raw_editor": "Editorul „Sieve” (fără formatare)", + "raw_editor_warning": "Editarea scriptului brut Sieve poate afecta funcționalitatea editorului vizual de reguli. Modificările efectuate aici au prioritate față de cele din editorul vizual.", + "validate": "Validați", + "validation_success": "Scriptul este valid", + "validation_error": "Scriptul conține erori", + "save": "Salvați regulile", + "saving": "Se salvează...", + "saved": "Filtrele au fost salvate cu succes", + "save_failed": "Nu s-au putut salva filtrele", + "loading": "Se încarcă filtrele...", + "not_supported": "Serverul dvs. de e-mail nu acceptă filtre de e-mail.", + "rule_name": "Numele regulii", + "rule_name_placeholder": "de ex., Sortează buletinele informative", + "match_all": "Îndepliniți TOATE condițiile", + "match_any": "Îndepliniți ORICE condiție", + "conditions": "Condiții", + "add_condition": "Adăugați condiție", + "actions": "Acțiuni", + "add_action": "Adăugare acțiune", + "stop_processing": "Opriți procesarea regulilor următoare", + "include_spam": "Mută și mesajele marcate ca spam", + "attachment_type_placeholder": "de ex. pdf, doc, jpg", + "value_placeholder_multi": "Valoare (mai multe valori separate prin virgule)", + "condition_fields": { + "attachment": "Atașament", + "from": "De la", + "to": "Către", + "cc": "Cc", + "subject": "Subiect", + "header": "Antet personalizat", + "size": "Dimensiune", + "body": "Corpul mesajului", + "all": "Toate mesajele" + }, + "comparators": { + "has_any": "este prezent", + "has_type": "de tip", + "contains": "conține", + "not_contains": "nu conține", + "is": "este exact", + "not_is": "nu este", + "starts_with": "începe cu", + "ends_with": "se termină cu", + "matches": "se potrivește cu modelul", + "greater_than": "este mai mare decât", + "less_than": "este mai mic decât", + "address_is": "este adresa", + "domain_is": "are domeniul" + }, + "action_types": { + "move": "Mută în folder", + "copy": "Copiere în folder", + "forward": "Redirecționează către", + "mark_read": "Marcați ca citit", + "star": "Marcați mesajul cu stea", + "add_label": "Adăugați o etichetă", + "discard": "Ignorare (ștergere fără confirmare)", + "reject": "Respinge cu mesaj", + "keep": "Păstrează în căsuța de primire", + "stop": "Opriți procesarea" + }, + "move_to_folder": "Selectează folderul", + "copy_to_folder": "Selectează folderul", + "forward_to": "Redirecționează către adresa de e-mail", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Păstrează o copie", + "forward_limit": "Limita de redirecționări per mesaj pe acest server: {count}. Redirecționările în plus sunt omise.", + "forward_unavailable": "Acest server nu permite redirecționarea. Acțiunile de redirecționare din regulile de filtrare nu au niciun efect.", + "reject_message": "Mesaj de respingere", + "reject_placeholder": "E-mailul dvs. a fost respins", + "label_name": "Numele etichetei", + "label_placeholder": "Selectați eticheta", + "header_name": "Numele antetului", + "header_placeholder": "de ex., X-Mailing-List", + "size_bytes": "Dimensiune în octeți", + "size_placeholder": "de exemplu, 1000000", + "system_managed": "Regulă gestionată de sistem", + "opaque_warning": "Acest script a fost editat în afara editorului vizual. Este disponibilă doar editarea directă a fișierului „Sieve”.", + "open_sieve_editor": "Deschideți editorul de fișiere Sieve în format brut", + "fetch_error": "Nu s-au putut încărca filtrele", + "expanded_view": "Vizualizare extinsă", + "expanded_view_description": "Afișați regulile de filtrare cu blocuri detaliate de condiții și acțiuni", + "if": "Dacă", + "then": "Apoi", + "match_all_conditions": "toate se potrivesc", + "match_any_condition": "orice potriviri", + "and": "și", + "or": "sau", + "cancel": "Anulează", + "confirm_delete": "Șterge", + "rule_list": "Reguli de filtrare", + "drag_to_reorder": "Trageți pentru a reordona", + "match_type": "Potriviți tipul", + "reset_to_visual": "Reveniți la editorul vizual", + "reset_warning": "Aceasta va anula scriptul curent și va începe de la zero.", + "confirm_reset": "Resetare", + "validation_empty_name": "Numele regulii este obligatoriu", + "validation_empty_conditions": "Este necesară cel puțin o condiție cu o valoare", + "validation_empty_actions": "Este necesară cel puțin o acțiune", + "templates_section": "Începeți de la șablon", + "template_newsletters": "Mutați buletinele informative în dosar", + "template_receipts": "Arhivarea automată a confirmărilor de primire", + "template_important": "Marcați e-mailurile importante", + "template_notifications": "Filtrează notificările", + "sieve_editor": { + "title": "Sieve Editor de scripturi", + "warning": "Editarea scriptului brut Sieve poate afecta funcționalitatea editorului vizual de reguli. Modificările efectuate aici au prioritate față de cele din editorul vizual.", + "script_content": "Sieve script", + "valid": "Scriptul este valid", + "invalid": "Scriptul conține erori", + "save_warning": "Salvarea va suprascrie orice reguli vizuale. Această acțiune nu poate fi anulată. Faceți clic din nou pe Salvare pentru a confirma.", + "validating": "Se validează...", + "validate": "Validați", + "cancel": "Anulează", + "save": "Salvare", + "confirm_save": "Confirmare Salvare", + "validation_failed": "Solicitarea de validare a eșuat" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# condiție} few {# condiții} other {# de condiții}}", + "actions_count": "{count, plural, one {# acțiune} few {# acțiuni} other {# de acțiuni}}" + }, + "origin_external": "Extern", + "managed_by_tooltip": "Gestionat de {source}. Editați-l în acea aplicație sau utilizați editorul brut Sieve.", + "quick_rule_names": { + "move_sender": "Mută de la {sender} în {folder}", + "move_domain": "Mută de la @{domain} în {folder}", + "move_list": "Mută lista {list} în {folder}", + "mark_read": "Marchează ca citite de la {sender}", + "tag": "Etichetează {tag} de la {sender}", + "block": "Blochează {sender}", + "custom": "Mesaje de la {sender}", + "custom_plain": "Regulă nouă", + "senders": "{count, plural, one {# expeditor} few {# expeditori} other {# de expeditori}}" + }, + "suggestions": "Sugestii", + "suggest_subject": "Subiect „{subject}”", + "suggest_to": "Către {address}", + "suggest_cc": "Cc {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Aplicați și mesajelor care sunt deja în acest dosar", + "apply_existing_unsupported": "Doar regulile care verifică expeditorul, destinatarii, subiectul sau antetele și care mută, copiază, marchează ca citit, marchează cu stea sau etichetează pot fi aplicate mesajelor existente.", + "period_title": "Interval de date", + "period_start": "Data de începere", + "period_end": "Data de încheiere", + "validation_period_order": "Data de încheiere trebuie să fie ulterioară datei de începere", + "period_toggle": "Activă doar într-o anumită perioadă", + "period_hint": "Se aplică mesajelor care sosesc în această perioadă. Lăsați un câmp gol pentru a nu limita acel capăt.", + "validation_period_empty": "Introduceți o dată de începere sau de încheiere pentru perioadă", + "validation_period_invalid": "Introduceți o dată validă cu oră pentru perioadă sau lăsați câmpul gol", + "apply_existing_period": "O regulă cu perioadă acționează doar asupra mesajelor care sosesc în acea perioadă, deci nu poate fi aplicată mesajelor existente.", + "period_status_scheduled": "programată", + "period_status_active": "activă", + "period_status_expired": "expirată" + }, + "templates": { + "title": "Șabloane de e-mail", + "description": "Creați șabloane de e-mail reutilizabile cu variabile de substituție", + "add": "Șablon nou", + "edit": "Editați șablonul", + "name": "Numele șablonului", + "name_placeholder": "de exemplu, e-mail de urmărire", + "category": "Categorie", + "category_placeholder": "de exemplu, Serviciu, Personal", + "subject": "Subiect", + "subject_placeholder": "Subiectul e-mailului", + "body": "Corpul mesajului", + "body_placeholder": "Conținutul corpului e-mailului...", + "recipients_placeholder": "email@example.com", + "identity": "Trimite ca", + "default_identity": "Identitate implicită", + "favorite": "Favorite", + "cancel": "Anulează", + "create": "Creați", + "update": "Actualizare", + "confirm_delete": "Șterge", + "no_templates": "Nu există încă șabloane", + "manage": "Gestionarea șabloanelor", + "count": "{count, plural, one {# șablon} few {# șabloane} other {# de șabloane}}", + "export_import": "Export și import", + "export_import_description": "Faceți o copie de rezervă a șabloanelor sau transferați-le pe un alt dispozitiv", + "export": "Export", + "import": "Import", + "validation": { + "empty": "Numele șablonului este obligatoriu", + "too_long": "Numele șablonului trebuie să aibă cel mult 200 de caractere" + } + }, + "files": { + "display": { + "title": "Afișare", + "description": "Configurați modul de afișare a fișierelor și folderelor" + }, + "default_view": { + "label": "Vizualizare implicită", + "description": "Alegeți între afișarea sub formă de grilă și cea sub formă de listă", + "list": "Listă", + "grid": "Tabel" + }, + "default_sort": { + "label": "Sortare implicită", + "description": "Alegeți sortarea implicită pentru fișiere", + "name": "Nume", + "size": "Dimensiune", + "modified": "Modificat" + }, + "sort_direction": { + "label": "Direcția de sortare", + "description": "Alegeți ordinea crescătoare sau descrescătoare", + "ascending": "În ordine crescătoare", + "descending": "Descendent" + }, + "icons": { + "title": "Pictograme", + "description": "Configurați aspectul pictogramelor fișierelor" + }, + "show_icons": { + "label": "Afișați pictogramele fișierelor", + "description": "Afișați pictograme lângă fișiere și foldere" + }, + "colored_icons": { + "label": "Pictograme colorate", + "description": "Folosiți pictograme colorate în locul celor monocrome" + }, + "show_thumbnails": { + "label": "Afișează miniaturi", + "description": "Afișați previzualizări ale imaginilor în locul pictogramelor pentru fișierele imagine" + }, + "behavior": { + "title": "Comportament", + "description": "Configurați comportamentul browserului de fișiere" + }, + "show_hidden": { + "label": "Afișează fișierele ascunse", + "description": "Afișați fișierele și folderele care încep cu un punct" + }, + "preview": { + "label": "Previzualizare" + } + } + }, + "errors": { + "page_error_title": "A apărut o eroare", + "page_error_description": "A apărut o eroare neașteptată. Vă rugăm să încercați din nou sau să reveniți la pagina principală.", + "sidebar_error": "Nu se pot încărca căsuțele poștale", + "email_list_error": "Nu se pot încărca e-mailurile", + "viewer_error_title": "Nu se poate afișa e-mailul", + "viewer_error_description": "A apărut o problemă la afișarea acestui e-mail. Este posibil ca acesta să conțină conținut neacceptat.", + "composer_error": "Nu se poate încărca editorul", + "settings_error_title": "Setări indisponibile", + "settings_error_description": "Nu se pot încărca setările. Este posibil ca preferințele dvs. să nu fie salvate.", + "try_again": "Încercați din nou", + "reload": "Reîncarcă", + "reload_emails": "Reîncarcă e-mailurile", + "reload_settings": "Reîncarcă setările", + "retry": "Reîncearcă", + "go_home": "Accesați căsuța de intrare" + }, + "context_menu": { + "reply": "Răspunde", + "reply_all": "Răspunde tuturor", + "forward": "Redirecționează", + "mark_read": "Marcați ca citit", + "mark_unread": "Marcați ca necitit", + "star": "Stea", + "unstar": "Anulează marcarea cu stea", + "pin": "Fixează", + "unpin": "Anulează fixarea", + "move_to": "Mergi la...", + "copy_to_account": "Copiază în cont...", + "remove_from_folder": "Elimină din acest dosar", + "archive": "Arhivează", + "delete": "Șterge", + "mark_as_spam": "Raportează spamul", + "not_spam": "Nu este spam", + "tag": "Etichetă", + "items_selected": "{count} e-mailuri selectate", + "forward_as_attachments": "Redirecționează ca atașamente", + "edit_draft": "Editează schița", + "cancel_scheduled_send": "Anulează trimiterea", + "reschedule_send": "Reprogramare", + "cancel_and_edit": "Anulează și editează", + "cancel_and_compose_again": "Anulează și redactează din nou", + "rules": { + "title": "Reguli", + "move_from": "Mută întotdeauna mesajele de la {sender}", + "move_from_domain": "Mută întotdeauna mesajele de la @{domain}", + "move_from_list": "Mută întotdeauna mesajele din această listă", + "mark_read_from": "Marchează întotdeauna ca citite mesajele de la {sender}", + "tag_with": "Etichetează întotdeauna cu", + "block": "{count, plural, one {Blochează expeditorul} few {Blochează # expeditori} other {Blochează # de expeditori}}", + "no_junk": "Acest cont nu are un dosar de spam", + "create": "Creați o regulă…", + "manage": "Gestionați regulile", + "senders_count": "{count, plural, one {# expeditor} few {# expeditori} other {# de expeditori}}", + "cross_account": "Selectați mesaje dintr-un singur cont", + "opaque_hint": "Filtrele dvs. au fost editate manual. Deschideți setările filtrelor" , + "loading": "Se încarcă regulile…", + "load_failed": "Regulile nu au putut fi încărcate", + "none": "Nicio regulă încă", + "new_mail_only": "Doar pentru mesajele noi", + "running": "Se rulează „{name}” în {folder}…" + }, + "find_emails_from": "Găsește e-mailurile de la {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Marcați folderul ca citit", + "mark_folder_tree_read": "Marcați folderul și subfolderele ca citite", + "mark_all_folders_read": "Marcați toate folderele ca citite", + "new_subfolder": "Subfolder nou...", + "new_folder": "Dosar nou...", + "rename": "Redenumire...", + "share": "Partajează...", + "import_email": "Importați fișiere .eml sau .zip...", + "empty_folder": "Folder gol", + "empty_folder_generic": "Folder gol", + "delete_folder": "Șterge dosarul", + "refresh": "Reîmprospătează", + "hide_shared_account": "Ascunde din bara laterală", + "mute_notifications": "Dezactivează notificările", + "unmute_notifications": "Activează notificările", + "mute_account_notifications": "Dezactivează notificările pentru acest cont", + "unmute_account_notifications": "Activează notificările pentru acest cont", + "reset_folder_notifications": "Resetează notificările dosarelor", + "mark_all_confirm_title": "Marcați toate folderele ca citite", + "mark_all_confirm_message": "Doriți să marcați toate mesajele necitite din contul dvs. personal ca fiind citite?", + "delete_confirm_title": "Șterge dosarul", + "delete_confirm_message": "Doriți să ștergeți definitiv folderul „{name}”? Această acțiune nu poate fi anulată.", + "prompt_new_subfolder": "Introduceți un nume pentru noul subfolder.", + "prompt_new_folder": "Introduceți un nume pentru noul folder.", + "prompt_rename": "Introduceți un nume nou pentru acest folder.", + "placeholder_folder_name": "Numele folderului", + "create": "Creați", + "rename_confirm": "Redenumire", + "toast_marked_read": "Dosar marcat ca citit", + "toast_marked_read_count": "Marcat {count, plural, one {1 mesaj} few {# mesaje} other {# de mesaje}} ca citit", + "toast_already_read": "Nu există mesaje necitite", + "toast_marked_all_read": "Toate folderele marcate ca citite", + "toast_emptied": "Dosarul a fost golit", + "toast_folder_created": "Folder creat", + "toast_folder_renamed": "Folder redenumit", + "toast_folder_deleted": "Dosarul a fost șters", + "toast_error_mark_read": "Nu s-a reușit marcarea ca citit", + "toast_error_empty": "Nu s-a reușit golirea folderului", + "toast_error_create": "Nu s-a putut crea folderul", + "toast_error_rename": "Nu s-a putut redenumi folderul", + "toast_error_delete": "Nu s-a putut șterge folderul", + "toast_error_delete_has_children": "Dosarul conține subdosare. Ștergeți-le mai întâi.", + "toast_error_delete_has_email": "Dosarul nu este gol. Goliți-l mai întâi.", + "delete_with_contents_title": "Dosarul nu este gol", + "delete_with_contents_message": "Dosarul „{name}” încă mai conține {count, plural, one {1 mesaj} other {# mesaje}}. Ștergi dosarul și toate mesajele sale? Acțiunea nu poate fi anulată.", + "delete_with_contents_confirm": "Șterge dosarul și mesajele" + }, + "shortcuts": { + "title": "Comenzi rapide de la tastatură", + "tip": "Apăsați ? oricând pentru a afișa acest ajutor", + "sections": { + "navigation": "Navigare", + "actions": "Acțiuni de e-mail", + "global": "Global", + "threads": "Subiecte", + "composer": "Redactare" + }, + "navigation": { + "next_email": "Următorul e-mail", + "previous_email": "E-mailul anterior", + "open_email": "Deschide e-mailul", + "close_email": "Închide / Deselectează", + "go_to_folder": "Mergi la dosar", + "next_unread": "Următorul e-mail necitit", + "no_more_unread": "Nu mai sunt e-mailuri necitite mai jos în listă" + }, + "actions": { + "reply": "Răspunde", + "reply_all": "Răspunde tuturor", + "forward": "Redirecționează", + "star": "Comută steluța", + "archive": "Arhivează", + "delete": "Șterge", + "mark_unread": "Marcați ca necitit", + "mark_read": "Marcați ca citit", + "toggle_spam": "Raportează spam / Nu este spam" + }, + "global": { + "compose": "Redactați un e-mail nou", + "search": "Căutare focalizată", + "help": "Afișați comenzile rapide", + "refresh": "Reîmprospătați e-mailurile", + "select_all": "Selectează tot", + "global_search": "Caută în tot", + "extend_selection": "Extinde selecția în jos / în sus" + }, + "threads": { + "expand_collapse": "Extindeți/reduceți firul de discuție", + "select_mountain_view": "Selectează conversația" + }, + "composer": { + "send": "Trimite e-mail", + "schedule_send": "Programează trimiterea", + "template_picker": "Deschide selectorul de șabloane" + } + }, + "threads": { + "messages_one": "{count} mesaj", + "messages_other": "{count} mesaje", + "messages_tooltip": "{count, plural, one {# mesajul din această conversație} few {# mesaje din această conversație} other {# de mesaje din această conversație}}", + "expand": "Extinde conversația", + "collapse": "Ascunde conversația", + "loading": "Se încarcă conversația...", + "mark_read": "Marcați conversația ca citită", + "mark_unread": "Marcați conversația ca necitită", + "archive": "Arhivează conversația", + "delete": "Șterge conversația", + "star": "Marcați conversația cu stea", + "unstar": "Dezactivează marcajul conversației", + "toggle_thread": "Comutare fir de discuție" + }, + "identities": { + "modal_title": "Gestionarea identităților de expediere", + "create_new": "Creați o identitate nouă", + "edit_identity": "Editați identitatea", + "delete_confirm": "Doriți să ștergeți această identitate? Această acțiune nu poate fi anulată.", + "cannot_delete": "Această identitate nu poate fi ștearsă", + "primary_identity": "Primar", + "set_as_primary": "Setați ca principal", + "no_identities": "Nu s-au găsit identități", + "display": { + "reply_to": "Răspunde la:", + "bcc": "BCC:", + "signature": "Semnătură:", + "preview": "Previzualizare:" + }, + "validation_errors": { + "invalid_emails": "Adrese de e-mail nevalide: {emails}", + "unknown_error": "Eroare necunoscută" + }, + "form": { + "name_label": "Nume afișat", + "name_placeholder": "de exemplu, E-mail de serviciu, Personal", + "name_required": "Numele este obligatoriu", + "email_label": "Adresă de e-mail", + "email_placeholder": "your.email@example.com", + "email_required": "Adresa de e-mail este obligatorie", + "email_invalid": "Vă rugăm să introduceți o adresă de e-mail validă", + "email_immutable": "Adresa de e-mail nu poate fi modificată după creare", + "reply_to_label": "Răspunde la (opțional)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "BCC automat (opțional)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Semnătură text", + "html_signature_label": "HTML Semnătură", + "signature_byte_counter": "{bytes} / {max} bytes", + "signature_byte_limit_reached": "Limita serverului a fost atinsă", + "save": "Salvați identitatea", + "cancel": "Anulează", + "creating": "Se creează...", + "updating": "Se actualizează..." + }, + "sub_address": { + "button_tooltip": "Utilizați subadrese", + "popover_title": "Adăugați eticheta „Sub-adresă”", + "tag_input_placeholder": "Introduceți o etichetă (de exemplu, cumpărături)", + "preview_label": "Previzualizare:", + "recent_tags": "Etichete recente", + "suggested_tags": "Sugestii", + "use_address": "Utilizați această adresă", + "invalid_tag": "Eticheta trebuie să conțină doar caractere alfanumerice și liniuțe", + "tag_too_long": "Eticheta trebuie să aibă cel mult 30 de caractere", + "help_text": "E-mailurile trimise către adresele {delimiter} și tag@domain.com vor ajunge în căsuța dvs. de e-mail primite", + "validation": { + "empty": "Câmpul „Tag” nu poate fi gol", + "too_long": "Eticheta trebuie să conțină cel mult{max}e", + "invalid_chars": "Eticheta trebuie să conțină numai litere, cifre și liniuțe" + } + }, + "badge": { + "sent_via": "via", + "sub_address_tag": "Trimis folosind subadresa: {tag}", + "identity_name": "Trimis folosind identitatea: {name}", + "identity_short": "via {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Șterge", + "delete_confirm_title": "Ștergeți identitatea" + }, + "templates": { + "picker_title": "Alegeți un șablon", + "search_placeholder": "Căutați șabloane...", + "section_favorites": "Favorite", + "section_recent": "Recent", + "section_uncategorized": "Altele", + "no_templates": "Nu există încă șabloane", + "no_results": "Nu s-au găsit șabloane", + "fill_placeholders": "Completați valorile din câmpurile de substituție", + "enter_value": "Introduceți o valoare...", + "preview": "Previzualizare", + "insert_with_values": "Inserați cu valori", + "insert_raw": "Inserați Raw", + "copy_suffix": "(copiere)", + "placeholder": "Variabilă", + "placeholders": { + "recipient_name": "Numele destinatarului", + "company": "Numele companiei", + "date": "Data curentă", + "day_of_week": "Ziua săptămânii", + "sender_name": "Numele dvs." + } + }, + "contacts": { + "directory": { + "title": "Director", + "search_placeholder": "Caută nume sau adresă de e-mail", + "refresh": "Reîmprospătează directorul", + "retry": "Încearcă din nou", + "loading": "Se încarcă directorul…", + "unavailable_title": "Niciun director", + "unavailable_hint": "Serverul de e-mail nu oferă un director al conturilor.", + "forbidden_title": "Director indisponibil", + "forbidden_hint": "Serverul de e-mail nu permite acestui cont să răsfoiască directorul. Întreabă administratorul.", + "error_title": "Directorul nu a putut fi încărcat", + "error_hint": "Verifică conexiunea și încearcă din nou.", + "empty_title": "Directorul este gol", + "empty_hint": "Serverul de e-mail nu listează niciun cont cu adresă de e-mail.", + "no_match": "Nimeni din director nu corespunde căutării", + "refresh_failed": "Directorul nu a putut fi reîmprospătat. Se afișează ultima listă încărcată.", + "select_page": "Selectează pe toată lumea de pe această pagină", + "deselect_page": "Deselectează pe toată lumea de pe această pagină", + "select_entry": "Selectează {name}", + "range": "{from}–{to} din {total}", + "page": "Pagina {page} din {count}", + "previous_page": "Pagina anterioară", + "next_page": "Pagina următoare", + "picker_title": "Selectează din director", + "cancel": "Anulează", + "selected_count": "{count} selectați", + "add_to": "Adaugă la Către", + "add_cc": "Adaugă la Cc", + "add_bcc": "Adaugă la Bcc", + "select_hint": "Selectează o persoană pentru detalii", + "write_email": "Scrie e-mail", + "add_to_contacts": "Adaugă la contacte", + "email": "E-mail", + "read_only_note": "Intrările din director provin de la serverul de e-mail și nu pot fi editate aici.", + "type": { + "individual": "Persoană", + "group": "Grup", + "resource": "Resursă", + "location": "Locație", + "other": "Altul" + } + }, + "title": "Contacte", + "search_placeholder": "Căutare contacte...", + "create_new": "Contact nou", + "no_category": "Fără categorie", + "rename_category": "Redenumire categorie", + "category_name_label": "Numele categoriei", + "category_renamed": "Categorie redenumită", + "category_rename_failed": "Nu s-a putut redenumi categoria", + "category_added": "Contact adăugat la {name}", + "category_added_plural": "{count} contacte adăugate la {name}", + "empty_state": "Nu există încă contacte", + "empty_state_title": "Nu există încă contacte", + "empty_state_subtitle": "Creați primul dvs. contact sau importați-l dintr-un fișier „vCard”", + "empty_search": "Nu există contacte care să corespundă căutării dvs.", + "empty_search_hint": "Încercați un alt termen de căutare", + "empty_filtered": "Nu există contacte care să corespundă filtrelor dvs.", + "empty_filtered_hint": "Încercați să modificați sau să ștergeți filtrele", + "clear_search": "Șterge căutarea", + "import_vcard": "ImportvCard", + "delete_confirm_title": "Șterge contactul", + "delete_confirm": "Sunteți sigur că doriți să ștergeți acest contact?", + "local_mode": "Contactele sunt stocate local (serverul nu acceptă contactele dJMAP)", + "back_to_contacts": "Înapoi la contacte", + "back_to_email": "Înapoi la e-mail", + "open_categories": "Deschideți categoriile", + "tabs": { + "all": "Toate", + "groups": "Grupuri" + }, + "shared": { + "title": "Partajat" + }, + "address_books": { + "create": "Agendă nouă", + "created": "Agendă creată", + "create_failed": "Nu s-a putut crea agenda", + "title": "Agendele mele de adrese", + "shared_prefix": "Partajat: {name}", + "moved": "Contactul a fost mutat la {name}", + "moved_plural": "{count} contactele au fost mutate la {name}", + "move_failed": "Nu s-a putut muta contactul", + "address_book": "Agendă", + "rename": "Redenumire agendă", + "name_label": "Numele agendei de adrese", + "renamed": "Agenda de adrese a fost redenumită", + "rename_failed": "Nu s-a putut redenumi agenda de adrese", + "default": "Implicit", + "set_default": "Setați ca implicită", + "default_updated": "Agenda de adrese implicită a fost actualizată", + "set_default_failed": "Nu s-a reușit setarea agendei de adrese implicite", + "manage": "Gestionați agendele de adrese", + "share": "Partajați agenda de adrese", + "new_contact_in_book": "Contact nou în această agendă", + "delete": "Șterge agenda de adrese", + "confirm_delete": "Doriți să ștergeți „{name}”? Toate contactele din această agendă vor fi eliminate.", + "deleted": "Agenda de adrese a fost ștersă", + "delete_failed": "Nu s-a putut șterge agenda de adrese" + }, + "detail": { + "emails": "Adrese de e-mail", + "phones": "Numere de telefon", + "organizations": "Organizații", + "addresses": "Adrese", + "notes": "Note", + "titles": "Titluri și roluri", + "online_services": "Servicii online", + "anniversaries": "Aniversări", + "personal_info": "Informații personale", + "languages": "Limbi", + "categories": "Categorii", + "related_contacts": "Contacte asociate", + "crypto_keys": "Chei criptografice", + "cert_issuer": "Emitent", + "cert_expires": "Expiră", + "cert_expired": "Expirat", + "cert_fingerprint": "Amprentă digitală", + "cert_algorithm": "Algoritm", + "import_to_smime": "Importați în S/MIME", + "cert_already_imported": "Deja importat pe S/MIME", + "cert_imported": "Certificat importat în magazinul S/MIME", + "cert_import_failed": "Importul certificatului a eșuat", + "no_contact_selected": "Selectați un contact pentru a vizualiza detaliile", + "compose_email": "Redactați un e-mail", + "copy_email": "Copiați e-mailul", + "copy_phone": "Copiați numărul de telefon", + "copy_url": "Copiați URL", + "copied": "Copiat în clipboard", + "copy_failed": "Nu s-a putut copia în clipboard", + "created": "Creat", + "updated": "Ultima actualizare", + "timezone": "Fus orar", + "anniversary_birth": "Ziua de naștere", + "anniversary_death": "A decedat", + "anniversary_wedding": "Aniversare", + "anniversary_other": "Altele", + "personal_expertise": "Competențe", + "personal_hobby": "Hobby", + "personal_interest": "Interes", + "personal_other": "Altele", + "gender": "Gen", + "gender_masculine": "Bărbat", + "gender_feminine": "Femeie", + "gender_other": "Altele", + "gender_none": "Nu se aplică", + "gender_unknown": "Necunoscut", + "calendar": "Calendar", + "calendar_uri": "URL de calendar", + "scheduling_uri": "Programare URL", + "freebusy_uri": "URL „Liber/Ocupat”", + "section_contact": "Date de contact", + "section_work": "Muncă", + "section_personal": "Personal", + "email_default_label": "E-mail", + "phone_default_label": "Telefon", + "address_default_label": "Adresă", + "online_service_default_label": "Online", + "organization_label": "Organizare", + "title_label": "Titlu", + "role_label": "Rol", + "language_label": "Limba", + "related_default_label": "Legat de", + "more_actions": "Alte acțiuni", + "age_years": "{count, plural, one {1 an} few {# ani} other {# de ani}}", + "years_since": "{count, plural, one {1 an} few {# ani} other {# de ani}}" + }, + "activity": { + "recent_emails": "E-mailuri recente", + "upcoming_events": "Evenimente viitoare", + "no_emails": "Nu există e-mailuri recente", + "no_events": "Nu există evenimente viitoare", + "no_subject": "(Fără subiect)", + "no_title": "(Fără titlu)", + "load_failed": "Nu s-a putut încărca", + "unknown_sender": "Expeditor necunoscut", + "all_day": "Toată ziua" + }, + "form": { + "create_title": "Contact nou", + "edit_title": "Editează contactul", + "section_address_book": "Director", + "select_address_book": "Selectați un director...", + "section_identity": "Nume și identitate", + "contact_type": "Tip de contact", + "type_person": "Persoană", + "type_organization": "Organizare", + "section_work": "Muncă și organizare", + "prefix": "Prefix", + "prefix_placeholder": "Dr., Dl., Dna.", + "given_name": "Prenume", + "middle_name": "Al doilea prenume", + "surname": "Nume de familie", + "suffix": "Sufix", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Pseudonim", + "nickname_placeholder": "Pseudonim", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Telefon", + "phone_placeholder": "+1 234 567 890", + "phone_type": "Tastați", + "phone_voice": "Voce", + "phone_cell": "Mobil", + "phone_fax": "Fax", + "phone_pager": "Pager", + "phone_video": "Video", + "phone_text": "Text", + "organization": "Organizare", + "organization_placeholder": "Numele companiei", + "department": "Departament", + "department_placeholder": "Departament", + "job_title": "Funcția", + "job_title_placeholder": "de ex., inginer software", + "role": "Rol", + "role_placeholder": "de ex., șef de echipă", + "addresses": "Adrese", + "add_address": "Adăugați adresa", + "street": "Stradă", + "city": "Oraș", + "region": "Stat / Regiune", + "postcode": "Cod poștal", + "country": "Țară", + "online_services": "Servicii online", + "add_online_service": "Adăugați un serviciu online", + "url_placeholder": "https://...", + "service_placeholder": "Serviciu", + "anniversaries": "Aniversări", + "add_anniversary": "Adăugați data", + "anniversary_birth": "Ziua de naștere", + "anniversary_wedding": "Aniversare", + "anniversary_death": "Memorial", + "anniversary_other": "Altele", + "personal_info": "Informații personale", + "add_personal_info": "Adăugați o intrare", + "personal_info_placeholder": "de exemplu, Fotografie", + "personal_expertise": "Competențe", + "personal_hobby": "Hobby", + "personal_interest": "Interes", + "personal_other": "Altele", + "level": "Nivel", + "level_high": "Nivel ridicat", + "level_medium": "Nivel mediu", + "level_low": "Nivel scăzut", + "categories": "Categorii", + "categories_placeholder": "de exemplu, Familie, Prieteni, Colegi", + "categories_hint": "Tastați pentru a căuta sau a adăuga categorii", + "category_add": "Adăugați", + "note": "Note", + "note_placeholder": "Adăugați o notă...", + "gender": "Gen", + "gender_sex": "Sex", + "gender_male": "Bărbat", + "gender_female": "Femeie", + "gender_other": "Altele", + "gender_none": "Nu se aplică", + "gender_unknown": "Necunoscut", + "gender_identity": "Identitatea de gen", + "gender_identity_placeholder": "Identitatea de gen...", + "calendar": "Calendar", + "calendar_uri": "URL de calendar", + "scheduling_uri": "Programare URL", + "freebusy_uri": "URL „Liber/Ocupat”", + "context_work": "Muncă", + "context_private": "Privat", + "add_email": "Adăugați un e-mail", + "add_phone": "Adăugați numărul de telefon", + "save": "Salvare", + "cancel": "Anulează", + "creating": "Se creează...", + "updating": "Se actualizează...", + "name_required": "Introduceți un prenume, un nume sau o organizație", + "email_invalid": "Vă rugăm să introduceți o adresă de e-mail validă", + "email_error_inline": "Format de e-mail nevalid", + "save_failed": "Nu s-a putut salva contactul", + "delete": "Șterge", + "upload_photo": "Încărcați o fotografie", + "remove_photo": "Elimină fotografia", + "photo_hint": "JPG sau PNG, până la 10 MB. Dimensiunea imaginii va fi redimensionată.", + "photo_too_large": "Imaginea este prea mare (max. 10 MB)", + "photo_invalid": "Fișier imagine nevalid", + "change_photo": "Modificare" + }, + "groups": { + "create": "Grup nou", + "edit": "Editare grup", + "empty": "Nu există încă grupuri", + "delete_confirm_title": "Șterge grupul", + "delete_confirm": "Sunteți sigur că doriți să ștergeți acest grup?", + "name_label": "Numele grupului", + "name_placeholder": "de exemplu, Echipa, Familia", + "name_required": "Numele grupului este obligatoriu", + "save_failed": "Nu s-a putut salva grupul", + "members_label": "Membri", + "search_members": "Căutați contacte pentru a le adăuga...", + "no_members": "Nu există membri în acest grup", + "member_count": "{count, plural, =0 {Nu există membri} one {1 membru} few {# membri} other {# de membri}}", + "send_email": "Trimite e-mail către grup", + "send_email_to": "Către", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "Acest grup nu are membri cu adresă de e-mail." + }, + "import": { + "title": "Importați contactele", + "drop_hint": "Faceți clic pentru a selecta un fișier de tip „vCard”", + "file_types": "Fișiere .vcf sau .vcard", + "no_contacts": "Nu s-au găsit contacte în fișier", + "parse_error": "Nu s-a putut analiza fișierul „vCard”", + "found": "{count, plural, one {S-a găsit 1 contact} few {# contacte găsite} other {# de contacte găsite}}", + "duplicate": "Duplicare", + "select_all": "Selectează tot", + "deselect_all": "Deselectați tot", + "selected": "{count, plural, one {1 selectat} few {# selectat} other {# de selectat}}", + "import_button": "Import", + "importing": "Se importă...", + "success": "{count, plural, one {1 contact importat} few {# contacte importate} other {# de contacte importate}}", + "failed": "Importul a eșuat", + "close": "Închide", + "file_too_large": "Fișierul este prea mare (max. 5 MB)" + }, + "export": { + "title": "Exportați contactele", + "success": "{count, plural, one {1 contact exportat} few {# contacte exportate} other {# de contacte exportate}}" + }, + "bulk": { + "selected": "{count, plural, one {1 selectat} few {# selectat} other {# de selectat}}", + "select_all": "Selectează tot", + "delete": "Șterge", + "delete_confirm_title": "Ștergeți contactele", + "delete_confirm": "Șterge {count, plural, one {1 contact} few {# contacte} other {# de contacte}}?", + "deleted": "{count, plural, one {1 contact șters} few {# contacte șterse} other {# de contacte șterse}}", + "add_to_group": "Adaugă la grup", + "choose_group": "Alegeți un grup", + "adding_contacts": "Adăugare {count, plural, one {1 contact} few {# contacte} other {# de contacte}}", + "added_to_group": "Contacte adăugate la grup", + "export": "Export", + "clear": "Șterge selecția" + }, + "toast": { + "created": "Contact creat", + "updated": "Date de contact actualizate", + "deleted": "Contact șters", + "error_create": "Nu s-a putut crea contactul", + "error_update": "Nu s-a putut actualiza contactul", + "error_delete": "Nu s-a putut șterge contactul" + }, + "context_menu": { + "open": "Deschide", + "edit": "Editare", + "send_email": "Trimite e-mail", + "add_to_group": "Adaugă la grup", + "export_vcard": "Exportați cvCard", + "delete": "Șterge", + "call": "Apel", + "duplicate": "Duplicare", + "print": "Imprimare" + }, + "filters": { + "toggle": "Filtre", + "select": "Selectați", + "clear": "Ștergeți", + "close": "Închide", + "title": "Filtre avansate", + "organization": "Companie", + "organization_placeholder": "de ex. Acme Corp", + "job_title": "Funcția", + "job_title_placeholder": "de ex. Designer", + "location": "Locație", + "location_placeholder": "Oraș sau țară", + "email_domain": "Domeniul de e-mail", + "email_domain_placeholder": "example.com", + "birthday_month": "Ziua de naștere în", + "any_month": "Orice lună", + "has_email": "Are e-mail", + "has_phone": "Are telefon", + "has_photo": "Are fotografie", + "sort_by": "Sortare după", + "sort_first_name": "Prenume", + "sort_last_name": "Nume de familie" + } + }, + "calendar": { + "title": "Calendar", + "back_to_email": "Înapoi la e-mail", + "back_to_month": "Înapoi la lună", + "my_calendars": "Calendare", + "birthday_calendar": "Zile de naștere", + "mini_calendar_change": "Faceți clic pentru a schimba luna", + "views": { + "month": "Luna", + "week": "Săptămână", + "day": "Zi", + "agenda": "Agendă", + "today": "Astăzi", + "month_hint": "Luna (m)", + "week_hint": "Săptămână (s)", + "day_hint": "Ziua (d)", + "agenda_hint": "Agendă (a)", + "tasks": "Sarcini", + "tasks_hint": "Sarcini (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# eveniment anterior} few {# evenimente anterioare} other {# de evenimente anterioare}}", + "hidden_after": "{count, plural, one {# eveniment ulterior} few {# evenimente ulterioare} other {# de evenimente ulterioare}}", + "show_all_hours": "Afișează toate orele", + "show_display_hours": "Afișează doar {start} – {end}", + "create": "Creați un eveniment", + "edit": "Editați evenimentul", + "delete": "Ștergere eveniment", + "details": "Detalii despre eveniment", + "no_events": "Fără evenimente", + "all_day": "Toată ziua", + "more": "+{count} mai multe", + "show_more": "Afișați mai mult", + "show_less": "Afișați mai puțin", + "no_title": "(Fără titlu)", + "resize": "Eveniment de redimensionare", + "duplicate": "Duplicare", + "today_header": "Astăzi", + "tomorrow_header": "Mâine", + "agenda_show_earlier": "Afișează evenimentele anterioare", + "agenda_loading": "Se încarcă…", + "agenda_range_start": "Evenimente de la {date}", + "agenda_range_end": "Evenimente până la {date}", + "export_ics": "Exportați ca fișier .ics", + "copy_title": "Copiați titlul", + "copy_link": "Copiați linkul întâlnirii", + "new_event": "Eveniment nou", + "new_all_day_event": "Eveniment nou pe toată durata zilei", + "new_task": "Sarcină nouă", + "go_to_today": "Mergi la astăzi" + }, + "detail": { + "add_note": "Adăugați o notă...", + "save_note": "Salvare", + "note_saved": "Notă adăugată", + "open_link": "Deschideți linkul", + "copy_location": "Copiază locația", + "meeting_link": "Link către întâlnire", + "tentative": "Provizoriu", + "cancelled": "Anulat", + "delete_confirm": "Doriți să ștergeți acest eveniment?" + }, + "form": { + "title": "Titlu", + "description": "Descriere", + "location": "Locație", + "meeting_link": "Link către întâlnire", + "start_date": "Data de începere", + "end_date": "Data de încheiere", + "start_time": "Ora de începere", + "end_time": "Ora de încheiere", + "all_day_event": "Eveniment pe toată durata zilei", + "calendar_select": "Calendar", + "save": "Salvare", + "cancel": "Anulează", + "delete_confirm": "Sunteți sigur că doriți să ștergeți acest eveniment?", + "color": "Culoare" + }, + "participants": { + "availability": { + "title": "Disponibilitate", + "free": "Disponibil", + "busy": "Ocupat", + "tentative": "Provizoriu", + "unknown": "Nu este un utilizator al acestui server", + "checking": "Se verifică…" + }, + "title": "Participanți", + "add": "Adăugați un participant", + "organizer": "Organizator", + "attendee": "Participant", + "accepted": "Acceptat", + "declined": "Respins", + "tentative": "Provizoriu", + "needs_action": "Necesită acțiune", + "remove": "Eliminați", + "edit": "Editare", + "email_placeholder": "Adăugați o adresă de e-mail sau căutați contacte", + "send_invitations": "Trimiteți invitații participanților", + "status_summary": "{accepted} acceptat, {pending} în așteptare", + "invited_by": "Invitație de la {name}", + "respond_below": "Răspundeți folosind butoanele de mai jos", + "rsvp_label": "Răspunsul dvs.", + "cancel_notification": "Participanții vor fi informați cu privire la anulare", + "you_organizer": "Tu ești organizatorul", + "you_attendee": "Sunteți participant", + "no_participants": "Nu există participanți", + "count": "{count, plural, one {# participant} few {# participanți} other {# de participanți}}" + }, + "recurrence": { + "title": "Recurență", + "none": "Nu se repetă", + "daily": "Zilnic", + "weekly": "Săptămânal", + "monthly": "Lunar", + "yearly": "Anual", + "every_n_days": "La fiecare {count} zile", + "every_n_weeks": "La fiecare {count} săptămâni", + "every_n_months": "La fiecare {count} luni", + "until": "Până la", + "occurrences": "{count} ocurențe", + "custom": "Personalizare…", + "edit_custom": "Editați recurența personalizată", + "every_n_years": "La fiecare {count} ani", + "on_days": "pe {days}", + "on_day_n": "în ziua de{day}", + "on_the_nth": "pe {nth} {day}", + "in_month": "în {month}", + "nth_1": "primul", + "nth_2": "al doilea", + "nth_3": "al treilea", + "nth_4": "al patrulea", + "nth_last": "ultimul", + "editor_freq_day": "Zi", + "editor_freq_week": "Săptămână", + "editor_freq_month": "Luna", + "editor_freq_year": "Anul", + "editor_repeats_on": "Se repetă pe", + "editor_every": "Fiecare", + "editor_unit_days": "zi(i)", + "editor_unit_weeks": "săptămână(e)", + "editor_unit_months": "lună(i)", + "editor_unit_years": "an(i)", + "editor_on_day": "în ziua", + "editor_on_the": "pe", + "editor_in": "în", + "editor_ends": "Sfârșit", + "editor_never": "Niciodată", + "editor_ends_on": "Pe", + "editor_ends_after": "După", + "editor_occurrences": "ocurențe" + }, + "recurrence_scope": { + "edit_title": "Editați evenimentul recurent", + "delete_title": "Șterge evenimentul recurent", + "description": "Acesta este un eveniment recurent. Ce evenimente doriți să modificați?", + "this_event": "Numai acest eveniment", + "this_and_future": "Acest eveniment și evenimentele următoare", + "all_events": "Toate evenimentele", + "cancel": "Anulează", + "save": "Salvare", + "delete": "Șterge", + "rsvp_title": "Răspundeți la evenimentul recurent", + "rsvp_description": "Acesta este un eveniment recurent. Pentru ce evenimente se aplică răspunsul dvs.?", + "respond": "Răspundeți" + }, + "alerts": { + "title": "Memento-uri", + "none": "Fără mementouri", + "at_time": "La momentul evenimentului", + "minutes_before": "{count, plural, one {# cu un minut înainte} few {# minute înainte de} other {# de minute înainte de}}", + "hours_before": "{count, plural, one {# cu o oră înainte} few {# cu câteva ore înainte} other {# de cu câteva ore înainte}}", + "days_before": "{count, plural, one {# cu o zi înainte} few {# zile înainte} other {# de zile înainte}}", + "weeks_before": "{count, plural, one {# cu o săptămână înainte} few {# cu câteva săptămâni înainte} other {# de cu câteva săptămâni înainte}}", + "unit_minutes_before": "minute înainte de", + "unit_hours_before": "cu câteva ore înainte", + "unit_days_before": "zile înainte", + "unit_weeks_before": "cu câteva săptămâni înainte", + "add": "Adăugați o notificare", + "remove": "Eliminare memento", + "amount": "Suma de reamintire", + "unit": "Unitatea de reamintire", + "action": "Tip de memento", + "action_display": "Notificare", + "action_email": "E-mail", + "by_email": "{label} prin e-mail", + "kept": "{count, plural, one {Se păstrează încă # memento care nu poate fi editat aici} few {Se păstrează încă # mementouri care nu pot fi editate aici} other {Se păstrează încă # de mementouri care nu pot fi editate aici}}", + "other": "{count, plural, one {Încă # memento} few {Încă # mementouri} other {Încă # de mementouri}}", + "calendar_defaults": "Mementourile implicite ale calendarului", + "customize": "Personalizează", + "use_defaults": "Folosește mementourile implicite ale calendarului" + }, + "settings": { + "limit_hours": "Limitează orele vizibile", + "limit_hours_desc": "Afișează doar aceste ore în vizualizările pe zi și pe săptămână. Evenimentele din afara lor rămân accesibile prin săgețile din partea de sus și de jos a fiecărei zile.", + "visible_hours": "Ore vizibile", + "visible_hours_start": "Începutul orelor vizibile", + "visible_hours_end": "Sfârșitul orelor vizibile", + "hide_non_working_days": "Ascunde zilele nelucrătoare", + "hide_non_working_days_desc": "Omite din vizualizarea pe săptămână zilele în care nu lucrezi. Evenimentele lor apar în continuare în vizualizările pe lună, pe zi și agendă.", + "working_days": "Zile lucrătoare", + "title": "Setări calendar", + "default_view": "Vizualizare implicită", + "week_starts_on": "Săptămâna începe pe", + "time_format": "Formatul orei", + "default_calendar": "Calendarul implicit", + "default_reminder": "Memento implicit", + "time_format_12h": "12 ore", + "time_format_24h": "24 de ore", + "notifications_enabled": "Notificări privind evenimentele", + "notifications_enabled_desc": "Afișați alerte pentru evenimentele viitoare din calendar", + "notification_sound": "Sunet de notificare", + "notification_sound_desc": "Redă un sunet pentru alertele din calendar", + "invitation_parsing": "Analizați invitațiile prin e-mail", + "invitation_parsing_desc": "Detectați invitațiile din calendar din atașamentele de e-mail și afișați acțiunile din calendar", + "show_time_in_month_view": "Afișați ora în vizualizarea lunară", + "show_time_in_month_view_desc": "Afișați orele evenimentelor în vizualizarea calendarului lunar", + "show_week_numbers": "Afișați numerele săptămânilor", + "show_week_numbers_desc": "Afișați numerele săptămânilor în mini-calendar", + "calendar_free_scroll": "Derulare liberă", + "calendar_free_scroll_desc": "Derulează continuu prin luni, săptămâni și zile în loc de o singură perioadă odată", + "enable_tasks": "Activați sarcinile", + "enable_tasks_desc": "Afișați o vizualizare a sarcinilor în calendar pentru gestionarea sarcinilor de îndeplinit", + "show_tasks_on_calendar": "Afișați sarcinile în calendar", + "show_tasks_on_calendar_desc": "Afișați sarcinile în vizualizările lunare, săptămânale și zilnice ale calendarului", + "hover_preview": "Previzualizare la trecerea cursorului peste elemente", + "hover_preview_desc": "Afișați o fereastră pop-up cu detalii atunci când treceți cu mouse-ul peste evenimentele din calendar", + "hover_preview_instant": "Instant", + "hover_preview_delay_500ms": "Întârziere de 0,5 secunde", + "hover_preview_delay_1s": "Întârziere de 1 secundă", + "hover_preview_delay_2s": "Întârziere de 2 secunde", + "hover_preview_off": "Dezactivat", + "organizer_identity": "Organizează invitațiile ca", + "organizer_identity_desc": "Adresa de calendar folosită ca organizator când inviți persoane la evenimente.", + "organizer_identity_failed": "Nu s-a putut schimba adresa organizatorului", + "show_birthday_calendar": "Calendarul zilelor de naștere ale contactelor", + "show_birthday_calendar_desc": "Afișați un calendar virtual cu zilele de naștere ale contactelor dvs." + }, + "days": { + "monday": "Luni", + "tuesday": "Marți", + "wednesday": "Miercuri", + "thursday": "Joi", + "friday": "Vineri", + "saturday": "Sâmbătă", + "sunday": "Duminică", + "mon": "Luni", + "tue": "Marți", + "wed": "Miercuri", + "thu": "Joi", + "fri": "Vineri", + "sat": "Sâmbătă", + "sun": "Duminică" + }, + "months": { + "jan": "ianuarie", + "feb": "feb", + "mar": "Mar", + "apr": "Apr", + "may": "Poate", + "jun": "Iunie", + "jul": "iulie", + "aug": "aug", + "sep": "Sep", + "oct": "Oct", + "nov": "Nov", + "dec": "Dec", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Eveniment creat", + "event_updated": "Eveniment actualizat", + "event_deleted": "Eveniment șters", + "calendar_created": "Calendar creat", + "calendar_deleted": "Calendar șters", + "event_move_error": "Nu s-a putut muta evenimentul", + "event_resize_error": "Eșec la redimensionarea evenimentului", + "alert_title": "Eveniment viitor", + "alert_now": "Începând de acum", + "alert_in_minutes": "În {count} min", + "invitation_sent": "Invitații trimise", + "invitations_denied_title": "Invitațiile nu pot fi trimise", + "invitations_denied": "Serverul a refuzat trimiterea invitațiilor: {reason}. Salvezi evenimentul fără a le trimite?", + "save_without_invitations": "Salvează fără invitații", + "rsvp_updated": "Răspuns actualizat", + "rsvp_error": "Nu s-a putut actualiza răspunsul", + "event_duplicated": "Eveniment duplicat", + "event_error": "Nu s-a putut salva evenimentul", + "task_due": "Termen de finalizare a sarcinii", + "event_exported": "Eveniment exportat", + "title_copied": "Titlu copiat", + "link_copied": "Link copiat" + }, + "status": { + "loading_calendars": "Se încarcă calendarele...", + "loading_events": "Se încarcă evenimente..." + }, + "quick_create": { + "placeholder": "Titlul unui eveniment nou", + "aria_label": "Creare rapidă a unui eveniment", + "add_title": "Adaugă titlu", + "more_options": "Mai multe opțiuni" + }, + "nav_prev": "Anterior", + "nav_next": "Următorul", + "nav_open_menu": "Deschide meniul", + "import": { + "title": "Importare calendar", + "tab_file": "Fișier", + "tab_url": "URL", + "select_file": "Selectați fișierul .ics", + "drop_file": "sau trageți fișierul aici", + "supported_formats": "Suportă fișiere de tip „iCalendar” (.ics)", + "url_description": "Introduceți adresa URL a unui flux extern iCalendar (.ics) pentru a importa evenimente.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Suportă adresele URL de tip iCalendar (.ics)", + "fetch": "Preluare", + "invalid_url": "Vă rugăm să introduceți o adresă de e-mail validă URL", + "url_fetch_failed": "Nu s-a putut prelua calendarul de la URL", + "parsing": "Se analizează fișierul de calendar...", + "parsed_events": "{count} evenimente găsite", + "no_events": "Nu s-au găsit evenimente în fișier", + "select_all": "Selectează tot", + "deselect_all": "Deselectați tot", + "target_calendar": "Import în calendar", + "import_button": "Importați elementele selectate", + "importing": "Se importă evenimente...", + "success": "{count} evenimentele au fost importate cu succes", + "error": "Importul calendarului a eșuat", + "file_too_large": "Fișierul depășește limita de 10 MB", + "invalid_format": "Format de fișier calendar invalid" + }, + "webcal_action": { + "title": "Deschideți linkul către calendar", + "description": "Cum ați dori să utilizați „{name}”?", + "import_title": "Importați o singură dată", + "import_description": "Preia evenimentele acum și copiază-le într-unul dintre calendarele tale.", + "subscribe_title": "Abonează-te", + "subscribe_description": "Sincronizați automat acest calendar ca un calendar separat.", + "cancel": "Anulează" + }, + "management": { + "title": "Gestionarea calendarului", + "description": "Creați, redenumiți și personalizați-vă calendarele. Faceți clic dreapta pe un calendar din bara laterală pentru a-i schimba rapid culoarea.", + "name": "Nume", + "name_placeholder": "Numele calendarului", + "color": "Culoare", + "change_color": "Schimbă culoarea", + "random_color": "Culoare aleatorie nouă", + "add_calendar": "Adăugați calendar", + "edit": "Editare", + "delete": "Șterge", + "save": "Salvare", + "create": "Creați", + "cancel": "Anulează", + "default": "Implicit", + "confirm_delete": "Doriți să ștergeți „{name}”? Toate evenimentele din acest calendar vor fi eliminate.", + "confirm_clear": "Ștergeți toate evenimentele din „{name}”? Această acțiune nu poate fi anulată.", + "set_default": "Setați ca implicită", + "default_updated": "Calendarul implicit a fost actualizat", + "error_default": "Nu s-a reușit setarea calendarului implicit", + "clear_events": "Ștergeți evenimentele", + "events_cleared": "{count} evenimente șterse", + "error_clear": "Nu s-au șters evenimentele din calendar", + "calendar_created": "Calendar creat", + "calendar_updated": "Calendar actualizat", + "calendar_deleted": "Calendar șters", + "color_updated": "Culoarea calendarului a fost actualizată", + "error_create": "Nu s-a putut crea calendarul", + "error_update": "Nu s-a putut actualiza calendarul", + "error_delete": "Nu s-a putut șterge calendarul", + "caldav_url": "CalDAV URL", + "copy_url": "Copiați CalDAV URL", + "url_copied": "CalDAV URL copiat în clipboard", + "share": "Partajare calendar", + "new_event_in_calendar": "Eveniment nou în acest calendar", + "shared_with": "Partajat cu {names}", + "shared_with_count": "{count, plural, one {Partajat cu 1 persoană} few {Partajat cu # persoane} other {Partajat cu # de persoane}}", + "move_up": "Mutați în sus", + "move_down": "Mutați în jos", + "kind": "Conținut", + "kind_events": "Evenimente", + "kind_tasks": "Sarcini", + "kind_both": "Evenimente și sarcini", + "kind_hint": "Stabilește ce aplicații văd acest calendar când este sincronizat prin CalDAV. Nu poate fi schimbat ulterior.", + "default_alerts": "Mementouri implicite", + "default_alerts_title": "Mementouri implicite pentru „{name}”", + "default_alerts_hint": "Evenimentele noi din acest calendar primesc aceste mementouri, dacă nu sunt modificate în eveniment. Modificările de aici nu schimbă evenimentele existente. Mementourile prin e-mail sunt trimise de serverul de e-mail.", + "default_alerts_timed": "Evenimente cu oră", + "default_alerts_all_day": "Evenimente de o zi întreagă", + "default_alerts_updated": "Mementourile implicite au fost actualizate" + }, + "subscription": { + "title": "iCal Abonament", + "section_title": "iCal Abonamente", + "description": "Abonați-vă la un flux extern de tip iCalendar. Evenimentele vor fi sincronizate automat în propriul calendar. Suportă adresele URL de tip https:// și webcal://.", + "url_label": "URL de calendar", + "url_placeholder": "https://example.com/calendar.ics sau webcal://...", + "name_label": "Numele calendarului", + "name_placeholder": "de ex. Zile de sărbătoare legală", + "color_label": "Culoare", + "refresh_interval": "Interval de reîmprospătare", + "interval_15": "La fiecare 15 minute", + "interval_30": "La fiecare 30 de minute", + "interval_60": "La fiecare oră", + "interval_360": "La fiecare 6 ore", + "interval_1440": "În fiecare zi", + "subscribe": "Abonează-te", + "subscribing": "Abonare...", + "save": "Salvați modificările", + "saving": "Se salvează...", + "edit": "Editare", + "edit_title": "Editați abonamentul", + "updated": "Actualizare „{name}”", + "update_error": "Nu s-a putut actualiza abonamentul", + "invalid_url": "Vă rugăm să introduceți o adresă de e-mail validă URL", + "success": "Abonați la „{name}”", + "error": "Nu s-a putut adăuga abonamentul", + "refresh": "Reîmprospătează acum", + "refresh_success": "Abonamentul a fost actualizat", + "refresh_error": "Nu s-a reușit reîmprospătarea abonamentului", + "unsubscribe": "Dezabonare", + "confirm_delete": "Doriți să vă dezabonați de la „{name}”? Calendarul și toate evenimentele sale vor fi eliminate.", + "deleted": "Abonamentul a fost eliminat", + "delete_error": "Nu s-a putut anula abonamentul", + "last_refreshed": "Ultima actualizare: {time}" + }, + "tasks": { + "label": "Sarcini", + "no_tasks": "Nu există sarcini", + "no_title": "(Fără titlu)", + "mark_complete": "Marcați ca finalizat", + "mark_incomplete": "Marcați ca incomplet", + "filter_all": "Toate", + "filter_pending": "În așteptare", + "filter_completed": "Finalizat", + "filter_overdue": "Restant", + "show_completed": "Afișați cele finalizate", + "create": "Sarcină nouă", + "edit": "Editare sarcină", + "title_placeholder": "Titlul sarcinii", + "description_placeholder": "Adăugați o descriere...", + "due_date": "Termenul limită", + "include_time": "Includeți ora", + "priority": "Prioritate", + "priority_none": "Niciunul", + "priority_high": "Nivel ridicat", + "priority_medium": "Nivel mediu", + "priority_low": "Nivel scăzut", + "progress": "Stare", + "progress_needs_action": "Necesită acțiune", + "progress_in_process": "În curs de realizare", + "progress_completed": "Finalizat", + "progress_cancelled": "Anulat", + "calendar": "Calendar", + "alert": "Memento", + "alert_none": "Niciunul", + "alert_at_time": "La data scadenței", + "alert_5min": "Cu 5 minute înainte", + "alert_15min": "Cu 15 minute înainte", + "alert_30min": "Cu 30 de minute înainte", + "alert_1hr": "Cu o oră înainte", + "alert_1day": "Cu o zi înainte", + "delete": "Șterge", + "cancel": "Anulează", + "save": "Salvare", + "quick_add_placeholder": "Adăugați o sarcină...", + "due_today": "Astăzi", + "due_tomorrow": "Mâine", + "overdue": "Restant" + }, + "toolbar": { + "more": "Mai multe acțiuni", + "select_view": "Schimbă vizualizarea, acum {view}" + }, + "other_calendars": "Alte calendare", + "calendar_options": "Opțiuni pentru {name}" + }, + "calendar_event_notifications": { + "someone": "Cineva", + "invited": "{name} te-a invitat la „{title}”", + "updated": "{name} a actualizat „{title}”", + "cancelled": "{name} a anulat „{title}”", + "untitled": "Eveniment fără titlu" + }, + "share_notifications": { + "someone": "Cineva", + "shared": "{name} a partajat cu tine {kind} „{object}”", + "changed": "{name} ți-a modificat accesul la {kind} „{object}”", + "revoked": "{name} ți-a retras accesul la {kind} „{object}”", + "object": { + "folder": "dosarul", + "calendar": "calendarul", + "address_book": "agenda de contacte", + "files": "dosarul de fișiere" + } + }, + "sharing": { + "title": "Distribuie „{name}”", + "description": "Acordați acces altor utilizatori sau grupuri de pe acest server. Modificările intră în vigoare imediat.", + "no_shares": "Nu a fost partajat încă cu nimeni.", + "add_person": "Adăugați o persoană sau un grup", + "search_placeholder": "Căutați după nume sau adresă de e-mail…", + "loading_principals": "Se încarcă utilizatorii…", + "no_principals": "Nu s-au găsit alți utilizatori sau grupuri.", + "no_match": "Nu s-au găsit rezultate.", + "remove": "Eliminați accesul", + "group": "Grup", + "share_added": "Acces acordat", + "share_updated": "Acces actualizat", + "share_removed": "Accesul a fost eliminat", + "share_failed": "Nu s-a putut actualiza partajarea", + "preset": { + "freeBusy": "Doar disponibilitate", + "read": "Doar citire", + "readWrite": "Citire și scriere", + "manager": "Manager", + "custom": "Personalizat" + } + }, + "advanced_search": { + "title": "Căutare avansată", + "from": "De la", + "from_placeholder": "Adresa de e-mail sau numele expeditorului", + "to": "Către", + "to_placeholder": "Adresa de e-mail sau numele destinatarului", + "subject": "Subiect", + "subject_placeholder": "Subiectul conține...", + "body": "Corpul mesajului", + "body_placeholder": "Corpul conține...", + "folder": "Dosar", + "all_folders": "Toate folderele", + "all_folders_except_spam_trash": "Toate folderele, cu excepția Spam și Coș de gunoi", + "has_attachment": "Anexe", + "date_after": "După", + "date_before": "Înainte", + "size_min": "Mai mare de (KB)", + "size_max": "Mai mic de (KB)", + "starred": "Cu stea", + "unread": "Necitit", + "read": "Citiți", + "yes": "Da", + "no": "Nu", + "clear": "Ștergeți", + "clear_all": "Șterge tot", + "filters_active": "{count} filtru", + "filters_active_plural": "{count} filtre", + "toggle_filters": "Mai mult", + "search_hint": "Folosiți filtre avansate pentru o căutare precisă", + "advanced_filters_tooltip": "Filtre de căutare avansate", + "results_found": "{count, plural, =0 {Nu s-au găsit rezultate} one {# rezultat găsit} few {# rezultate găsite} other {# de rezultate găsite}}", + "results_found_more": "{count}+ rezultate găsite", + "suggestions_recent": "Căutări recente", + "suggestions_people": "Persoane", + "suggestions_from": "Caută e-mailuri de la {address}", + "suggestions_to": "Caută e-mailuri către {address}", + "suggestions_remove_recent": "Elimină din căutările recente", + "has_words": "Conține cuvintele", + "size": "Dimensiune", + "date_within": "Dată", + "search_in": "Caută în", + "has_attachment_check": "Are atașament", + "search": "Caută" + }, + "welcome": { + "title": "Bine ați venit în căsuța dvs. poștală", + "tip_compose": "Apăsați tasta c pentru a redacta un e-mail nou", + "tip_shortcuts": "Apăsați tasta „?” pentru a vedea toate comenzile rapide de la tastatură", + "tip_sidebar": "Găsiți Calendar, Contacte și Setări în meniul din bara laterală", + "tip_settings": "Personalizați-vă experiența în Setări", + "tip_compose_touch": "Atinge butonul cu creion pentru a scrie un e-mail nou", + "tip_sidebar_touch": "Calendarul, Contactele și Setările sunt în bara de jos", + "got_it": "Am înțeles", + "settings": "Setări", + "dismiss": "Ignoră", + "start_tour": "Începe turul" + }, + "demo_welcome": { + "title": "Bine ați venit la Bulwark Mail", + "description": "Explorați un client de e-mail web cu funcționalități complete – direct în browserul dvs. Toate datele rămân pe dispozitivul dvs., așa că nu ezitați să testați totul.", + "feature_email": "Citiți și redactați e-mailuri", + "feature_organize": "Etichete, stele și dosare", + "feature_shortcuts": "Comenzi rapide de la tastatură", + "feature_privacy": "Demo 100% privat", + "hint": "Faceți clic pe orice e-mail din partea stângă pentru a începe sau parcurgeți turul de mai jos." + }, + "files": { + "office_edit": "Editează documentul", + "office_loading": "Se deschide editorul de documente…", + "office_error": "Editorul de documente nu a putut fi deschis.", + "office_close": "Închide editorul", + "open_in_new_tab": "Deschide într-o filă nouă", + "title": "Fișiere", + "search_placeholder": "Căutare fișiere...", + "empty_state_title": "Nu există încă fișiere", + "upload_files": "Încărcați fișiere", + "new_folder": "Dosar nou", + "new_folder_name": "Numele folderului", + "rename": "Redenumire", + "rename_title": "Redenumire", + "new_name": "Nume nou", + "name_forbidden_chars": "Serverul nu permite aceste caractere în nume: {chars}", + "name_reserved": "Acest nume este rezervat pe server.", + "delete": "Șterge", + "delete_confirm_title": "Ștergeți resursa", + "delete_confirm_message": "Sunteți sigur că doriți să ștergeți „{name}”? Această acțiune nu poate fi anulată.", + "download": "Descărcați", + "name": "Nume", + "size": "Dimensiune", + "modified": "Modificat", + "type": "Tastați", + "folder": "Dosar", + "file": "Fișier", + "no_accounts": "Nu există conturi conectate.", + "uploading": "Se încarcă...", + "upload_success": "{count, plural, one {1 fișier încărcat} few {# fișiere încărcate} other {# de fișiere încărcate}}", + "upload_canceled": "Încărcare anulată", + "upload_canceled_partial": "{count, plural, one {1 fișier a fost încărcat} few {# fișiere au fost încărcate} other {# de fișiere au fost încărcate}} înainte de anulare", + "upload_error": "Nu s-a reușit încărcarea fișierului", + "create_folder_success": "Folder creat", + "create_folder_error": "Nu s-a putut crea folderul", + "delete_success": "Șters cu succes", + "delete_error": "Nu s-a putut șterge", + "rename_success": "Redenumit cu succes", + "rename_error": "Nu s-a putut redenumi", + "download_error": "Descărcarea a eșuat", + "not_available": "Stocarea fișierelor nu este disponibilă pe acest server", + "cancel": "Anulează", + "create": "Creați", + "save": "Salvare", + "no_results": "Nu există fișiere care să corespundă căutării dvs.", + "batch_delete_confirm_message": "Sunteți sigur că doriți să ștergeți {count, plural, one {1 element} few {# elemente} other {# de elemente}}? Această acțiune nu poate fi anulată.", + "batch_delete_success": "{count, plural, one {1 element șters} few {# elemente șterse} other {# de elemente șterse}}", + "grid_view": "Vizualizare tabelară", + "list_view": "Vizualizare listă", + "details": "Detalii", + "path": "Cale", + "preview": "Previzualizare", + "preview_error": "Nu s-a putut încărca previzualizarea", + "cut": "Tăiere", + "copy": "Copiere", + "paste": "Lipiți", + "move_success": "{count, plural, one {1 element mutat} few {# elemente mutate} other {# de elemente mutate}}", + "move_error": "Nu s-a putut muta", + "paste_success": "S-a lipit cu succes", + "paste_error": "Nu s-a putut insera", + "new_text_file": "Fișier text nou", + "file_name": "Numele fișierului", + "retry": "Reîncearcă", + "refresh": "Reîmprospătează", + "duplicate": "Duplicare", + "duplicate_success": "Duplicat cu succes", + "duplicate_error": "Nu s-a putut duplica", + "share": "Distribuie", + "shared": "Partajat", + "shared_with_me": "Partajat cu mine", + "shared_by": "Distribuit de {name}", + "create_file_success": "Fișier creat", + "create_file_error": "Nu s-a putut crea fișierul", + "favorites": "Favorite", + "recent": "Recent", + "show_in_folder": "Afișează în folder", + "remove_from_recent": "Elimină din recente", + "upload_folder": "Încărcare folder", + "file_too_large": "„{name}” depășește dimensiunea maximă a fișierului ({max})", + "undo": "Anulează", + "undo_success": "Acțiune anulată", + "undo_error": "Nu s-a putut anula", + "toolbar": "Acțiuni asupra fișierelor", + "open_folder_tree": "Deschideți arborele de foldere", + "file_list": "Fișiere și foldere", + "context_menu": "Acțiuni", + "settings_ascending": "În ordine crescătoare", + "settings_descending": "Descendent", + "disabled_title": "Funcția de fișiere este dezactivată de administratorul dvs.", + "disabled_description": "Încărcarea fișierelor de dimensiuni mari prin intermediul WebDAV poate provoca instabilitate în Stalwart /RocksDB, inclusiv blocări din cauza epuizării memoriei și utilizare irecuperabilă a spațiului pe disc. Este posibil ca fișierele șterse să nu fie eliminate imediat din spațiul de stocare blob. Această funcție nu este recomandată pentru mediile de producție.", + "migration_title": "Se actualizează fișierele...", + "migration_description": "Organizarea dosarelor și a fișierelor în structura corespunzătoare. Această operațiune se efectuează o singură dată.", + "new": "Nou", + "my_files": "Fișierele mele", + "folders": "Foldere", + "files_heading": "Fișiere", + "open": "Deschide", + "star": "Marchează cu stea", + "unstar": "Elimină steaua", + "expand_folder": "Extinde {name}", + "collapse_folder": "Restrânge {name}", + "sidebar_hint": "Marchează folderele cu stea și deschide fișiere pentru a le găsi aici.", + "breadcrumbs": "Calea folderului", + "show_path": "Afișează folderele ascunse din cale", + "selection_actions": "Acțiuni pentru elementele selectate", + "selected_count": "{count, plural, one {1 selectat} few {# selectate} other {# selectate}}", + "clear_selection": "Golește selecția", + "select_all": "Selectează tot", + "select_item": "Selectează {name}", + "more_actions": "Mai multe acțiuni pentru {name}", + "paste_here": "Lipește în acest folder", + "paste_count": "{count, plural, one {Lipește 1 element} few {Lipește # elemente} other {Lipește # de elemente}}", + "sort_by": "Sortează după", + "view_mode": "Aspect", + "clear_search": "Golește căutarea", + "no_results_for": "Nimic din acest folder nu corespunde cu „{query}”", + "download_count": "{count, plural, one {Descarcă 1 fișier} few {Descarcă # fișiere} other {Descarcă # de fișiere}}", + "delete_count": "{count, plural, one {Șterge 1 element} few {Șterge # elemente} other {Șterge # de elemente}}", + "close_details": "Închide detaliile", + "file_kind": "Fișier {ext}", + "folder_summary": "{folders, plural, =0 {Niciun folder} one {1 folder} few {# foldere} other {# de foldere}}, {files, plural, =0 {niciun fișier} one {1 fișier} few {# fișiere} other {# de fișiere}}", + "details_hint": "Selectează un fișier sau un folder pentru a vedea detaliile.", + "sharing": "Partajare", + "shared_with_count": "{count, plural, one {Partajat cu 1 persoană} few {Partajat cu # persoane} other {Partajat cu # de persoane}}", + "only_you": "Doar tu", + "empty_folder_title": "Acest folder este gol", + "empty_folder_description": "Trage fișierele aici sau încarcă-le de pe dispozitiv.", + "empty_folder_read_only": "Nu poți adăuga fișiere în acest folder.", + "choose_account": "Alege un cont", + "drop_to_upload": "Plasează pentru a încărca în {folder}", + "uploading_count": "Se încarcă {current} din {total}", + "dismiss": "Închide", + "zoom_in": "Mărește", + "zoom_out": "Micșorează", + "zoom_reset": "Potrivește pe ecran", + "rotate": "Rotește", + "close_preview": "Închide" + }, + "smime": { + "your_certificates": "Certificatele dvs.", + "your_certificates_desc": "Importați și gestionați certificatele S/MIME pentru semnarea și criptarea e-mailurilor", + "recipient_certificates": "Certificate ale destinatarilor", + "recipient_certificates_desc": "Certificate publice pentru criptarea e-mailurilor către destinatari", + "identity_bindings": "Combinații de taste pentru identitate", + "identity_bindings_desc": "Asociați certificatelS/MIMEe identităților dvs. de e-mail", + "defaults_title": "Setări implicite", + "defaults_desc": "Configurați comportamentul implicit de semnare și criptare", + "import_pkcs12": "Importați fișiere PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importați certificatul", + "no_certificates": "Nu s-au importat încă certificate", + "no_recipient_certs": "Fără certificate ale destinatarilor", + "expires": "Expiră", + "expired": "Expirat", + "bound_to": "Legat de", + "no_key_bound": "Niciunul", + "lock": "Tasta de blocare", + "unlock": "Cheia de deblocare", + "details": "Vedeți detalii", + "delete": "Șterge", + "encrypt_by_default": "Criptați în mod implicit", + "encrypt_by_default_desc": "Criptați automat e-mailurile atunci când toți destinatarii dețin certificate", + "remember_unlocked": "Rețineți cheile deblocate", + "remember_unlocked_desc": "Păstrați tastele deblocate pe durata acestei sesiuni de navigare", + "sign_default_for": "Semnați în mod implicit pentru", + "enter_p12_passphrase": "Introduceți fraza de acces PKCS # 12", + "p12_passphrase_desc": "Introduceți fraza de acces care protejează acest fișier de certificat", + "enter_storage_passphrase": "Setați fraza de acces pentru stocare", + "storage_passphrase_desc": "Alegeți o frază de acces pentru a proteja această cheie stocată în browserul dvs.", + "next": "Următorul", + "import": "Import", + "unlock_key": "Cheia de deblocare", + "unlock_key_desc": "Introduceți fraza de acces la spațiul de stocare pentru a debloca această cheie în vederea semnării sau decriptării", + "passphrase_placeholder": "Introduceți fraza de acces", + "confirm_passphrase_placeholder": "Confirmă fraza de acces", + "passphrase_mismatch": "Frazele de acces nu se potrivesc", + "cancel": "Anulează", + "processing": "Se procesează…", + "close": "Închide", + "certificate_details": "Detalii certificat", + "cert_subject": "Titular", + "cert_issuer": "Emitent", + "cert_email": "E-mail", + "cert_serial": "Număr de serie", + "cert_validity": "Valabilitate", + "cert_fingerprint": "Amprentă digitală (SHA-256)", + "cert_algorithm": "Algoritm", + "cert_capabilities": "Capacități", + "cert_source": "Sursă", + "cert_expired": "Acest certificat a expirat", + "cert_not_yet_valid": "Acest certificat nu este încă valabil", + "cap_sign": "Semnarea", + "cap_encrypt": "Criptare", + "cap_none": "Niciunul", + "show_passphrase": "Afișează fraza de acces", + "hide_passphrase": "Ascunde fraza de acces", + "sign_toggle": "Semn", + "encrypt_toggle": "Criptați", + "missing_recipient_certs": "Certificate lipsă pentru: {emails}", + "missing_sender_cert": "Nu există niciun certificat asociat acestei identități", + "status_encrypted_ok": "Acest mesaj a fost criptat", + "status_encrypted_no_key": "Acest mesaj este criptat, dar nu s-a găsit nicio cheie corespunzătoare", + "status_encrypted_failed": "Nu s-a reușit decriptarea acestui mesaj", + "status_signed_valid": "Semnătură verificată", + "status_signed_invalid": "Verificarea semnăturii a eșuat", + "status_signed_expired_cert": "Semnat cu un certificat expirat", + "status_signed_self_signed": "Semnătura este validă, dar certificatul este autosemnat (nu este de încredere)", + "status_signed_mismatch": "Semnătura este validă, dar semnatarul nu corespunde cu expeditorul", + "status_unsupported": "Format „S/MIME” neacceptat", + "auto_import_signer_certs": "Import automat al certificatelor semnatarilor", + "auto_import_signer_certs_desc": "Salvați automat certificatele din e-mailurile semnate și verificate pentru criptarea ulterioară", + "export": "Export", + "enter_export_passphrase": "Setați fraza de acces pentru export", + "export_passphrase_desc": "Alegeți o frază de acces pentru a proteja fișierul PKCS # 12 exportat", + "export_storage_desc": "Introduceți fraza de acces la spațiul de stocare pentru a decripta cheia în vederea exportului", + "incorrect_passphrase": "Parolă incorectă" + }, + "tour": { + "step_counter": "Pasul {current} din {total}", + "skip": "Sari peste turul de prezentare", + "back": "Înapoi", + "next": "Următorul", + "finish": "Finalizare", + "take_a_tour": "Faceți un tur al interfeței", + "restart_title": "Tur de prezentare", + "restart_desc": "Vizionați din nou prezentarea ghidată a interfeței", + "restart_button": "Reîncepe turul", + "show_on_new_devices_title": "Afișare pe dispozitive noi", + "show_on_new_devices_desc": "Redați din nou bannerul de bun venit și turul de prezentare la prima conectare pe un dispozitiv nou, chiar dacă le-ați parcurs deja în altă parte", + "sidebar_title": "Căsuțele dvs. de e-mail", + "sidebar_desc": "Aceasta este bara laterală cu dosare. Faceți clic pe orice căsuță poștală pentru a vizualiza e-mailurile din aceasta. Puteți crea dosare, muta e-mailuri între ele și vedeți dintr-o privire numărul de mesaje necitite.", + "compose_title": "Redactați un e-mail", + "compose_desc": "Faceți clic aici pentru a scrie un e-mail nou. Puteți adăuga destinatari, atașamente și puteți utiliza formatarea textului îmbogățit.", + "search_title": "Căutați în e-mailurile dvs.", + "search_desc": "Căutați după expeditor, subiect sau conținut. Faceți clic pe pictograma de filtrare pentru opțiuni avansate, cum ar fi intervalul de date, atașamentele și mesajele marcate cu stea.", + "email_list_title": "Lista dvs. de e-mailuri", + "email_list_desc": "Aici apar e-mailurile. Faceți clic pe unul pentru a-l citi în partea dreaptă. Utilizați caseta de selectare pentru a selecta mai multe e-mailuri, apoi mutați-le, ștergeți-le sau etichetați-le în bloc.", + "email_viewer_title": "Panoul de citire", + "email_viewer_desc": "E-mailul selectat se deschide aici. Răspundeți, redirecționați, arhivați sau ștergeți folosind butoanele din bara de instrumente. De asemenea, puteți marca e-mailurile cu stea sau adăuga etichete colorate.", + "keywords_title": "Etichete colorate", + "keywords_desc": "Organizați-vă e-mailurile cu etichete codificate pe culori. Trageți un e-mail peste o etichetă pentru a-l eticheta sau faceți clic dreapta pe un e-mail pentru a-i atribui etichete.", + "calendar_title": "Calendar", + "calendar_desc": "Treceți la calendar pentru a vă gestiona evenimentele. Creați evenimente, setați mementouri și vizualizați vizualizările pe zi, săptămână sau lună.", + "contacts_title": "Contacte", + "contacts_desc": "Aici se află agenda dvs. de adrese. Importați contacte, creați grupuri și faceți clic pe orice contact pentru a vedea detaliile complete ale acestuia.", + "settings_title": "Setări", + "settings_desc": "Personalizați totul: tema, densitatea, semnăturile, filtrele, comenzile rapide de la tastatură, setările implicite ale calendarului și multe altele.", + "shortcuts_title": "Comenzi rapide de la tastatură", + "shortcuts_desc": "Utilizatorii avansați adoră această funcție. Apăsați tasta „?” oricând pentru a vedea toate comenzile rapide disponibile. Puteți naviga, redacta și gestiona e-mailurile fără a atinge mouse-ul.", + "compose_open_title": "Fereastra de redactare", + "compose_open_desc": "Aceasta este fereastra de redactare a e-mailurilor. Adăugați destinatari, scrieți mesajul, atașați fișiere și folosiți formatarea de text îmbogățit. De asemenea, puteți salva ciorne și utiliza șabloane.", + "calendar_view_title": "Calendarul dvs.", + "calendar_view_desc": "Iată calendarul dvs. cu exemple de evenimente. Puteți comuta între vizualizările de zi, săptămână, lună și agendă folosind bara de instrumente.", + "create_event_title": "Creați un eveniment", + "create_event_desc": "Faceți clic pe acest buton pentru a crea un eveniment nou în calendar. Puteți seta un titlu, data, ora și puteți adăuga participanți.", + "event_modal_title": "Detalii despre eveniment", + "event_modal_desc": "Iată formularul evenimentului. Completați titlul, alegeți o dată și o oră, adăugați o locație sau participanți. Apăsați „Salvați” când ați terminat – sau închideți-l și continuați.", + "contacts_list_title": "Contactele tale", + "contacts_list_desc": "Iată contactele dvs. Faceți clic pe orice contact pentru a vedea detaliile complete ale acestuia în partea dreaptă. De asemenea, puteți crea contacte noi, importa vCard-uri sau organiza contactele în grupuri.", + "settings_tabs_title": "Meniul Setări", + "settings_tabs_desc": "Iată toate categoriile de setări. Personalizați aspectul, gestionați identitățile, configurați filtrele de e-mail, configurați calendarul și multe altele.", + "files_title": "Stocarea fișierelor", + "files_desc": "Browserul de fișiere vă permite să încărcați, să organizați și să partajați fișiere – ca un spațiu de stocare personal în cloud integrat în serviciul de e-mail.", + "demo_banner_title": "Controale demo", + "demo_banner_desc": "Vă aflați în modul demo – totul rămâne în browserul dvs. Apăsați „Resetare demo” oricând pentru a începe de la zero cu date de exemplu curate.", + "quota_title": "Utilizarea spațiului de stocare", + "quota_desc": "Urmăriți aici dimensiunea căsuței dvs. de e-mail. Cercul se umple pe măsură ce utilizați mai mult spațiu." + }, + "unified_mailbox": { + "search_unavailable": "Funcția de căutare nu este disponibilă în vizualizarea unificată" + }, + "quote_header": { + "reply_line": "Pe {date}, {from} a scris:", + "forwarded_separator": "---------- Mesaj redirecționat ----------", + "from_label": "De la", + "date_label": "Data", + "subject_label": "Subiect" + }, + "pwa_install": { + "title": "Instalați {appName}", + "description": "Instalați aplicația noastră pentru acces rapid și asistență offline.", + "not_now": "Nu acum", + "install": "Instalați", + "dont_remind": "Nu-mi mai reaminti", + "dismiss_aria": "Ignorați solicitarea de instalare" + }, + "deep_link": { + "message_not_found": "Acest mesaj nu mai este disponibil.", + "thread_not_found": "Această conversație nu mai este disponibilă.", + "folder_not_found": "Acest folder nu mai este disponibil.", + "event_not_found": "Acest eveniment nu mai este disponibil.", + "file_not_found": "Acest fișier nu mai este disponibil.", + "account_unavailable": "Conectați-vă la acel cont pentru a deschide linkul.", + "copy_message": "Copiați linkul mesajului", + "copy_conversation": "Copiați linkul conversației", + "copy_event": "Copiați linkul evenimentului", + "copied": "Link copiat", + "copy_failed": "Linkul nu a putut fi copiat" + }, + "plugins": { + "oauth_callback": { + "completing": "Se finalizează autorizarea…", + "completing_message": "Vă rugăm să așteptați stabilirea conexiunii.", + "received_title": "Autorizare primită", + "received_message": "Vă puteți întoarce la fila în care ați inițiat conexiunea.", + "failed_title": "Autorizarea a eșuat", + "denied": "Autorizarea a fost refuzată.", + "failed_detail": "Autorizarea a eșuat: {error}", + "missing_params": "Lipsește codul de autorizare sau starea.", + "delivery_failed": "Rezultatul autorizării nu a putut fi transmis filei aplicației. Închideți această fereastră și încercați din nou." + }, + "settings": { + "title": "Pluginuri", + "description": "Pluginuri adăugate de administrator. Activați-le sau dezactivați-le pentru contul dvs.", + "description_uploads": "Pluginurile adaugă funcții în webmail. Activați-le sau dezactivați-le pentru contul dvs. sau instalați-vă propriile pluginuri.", + "description_demo": "Pluginurile nu sunt disponibile în demo.", + "install_button": "Instalați un plugin", + "install_hint": "Alegeți un fișier ZIP cu un plugin.", + "install_hint_approval": "Alegeți un fișier ZIP cu un plugin. Un administrator trebuie să-l aprobe înainte să-l puteți activa.", + "install_uploads_off": "Administratorul dvs. nu permite instalarea pluginurilor.", + "install_managed_id": "Administratorul dvs. oferă deja un plugin cu acest nume, așa că nu poate fi înlocuit.", + "install_invalid": "Acest fișier nu este un plugin care poate fi instalat: {details}", + "toast_installed": "Pluginul a fost instalat. Activați-l mai jos.", + "toast_installed_pending": "Pluginul a fost instalat și trimis administratorului spre aprobare.", + "toast_enabled": "{name} este activat", + "toast_disabled": "{name} este dezactivat", + "toast_removed": "{name} a fost eliminat", + "toast_forced": "Administratorul dvs. menține {name} activat", + "toast_approval_requested": "{name} așteaptă aprobarea administratorului", + "empty_title": "Niciun plugin încă", + "empty_admin": "Administratorul dvs. nu a adăugat niciun plugin.", + "empty_uploads": "Pentru a începe, instalați un plugin dintr-un fișier ZIP.", + "empty_demo": "Pluginurile au nevoie de un cont real, așa că demo-ul nu are niciunul.", + "syncing": "Se verifică pluginurile cu serverul…", + "status_installed": "Dezactivat", + "status_enabled": "Se pornește", + "status_running": "Activat", + "status_disabled": "Dezactivat", + "status_error": "Problemă", + "badge_forced": "Obligatoriu", + "badge_managed": "De la administrator", + "badge_yours": "Instalat de dvs.", + "badge_awaiting": "Așteaptă aprobarea", + "toggle_label": "Activați sau dezactivați {name}", + "forced_note": "Administratorul dvs. impune acest plugin, așa că nu poate fi dezactivat.", + "permissions": "Ce poate face", + "permissions_managed": "Ce poate face (aprobat de administrator)", + "settings": "Setări", + "remove": "Eliminați", + "remove_title": "Eliminați {name}?", + "remove_message": "Pluginul și tot ce a stocat în acest browser vor fi șterse. Îl puteți instala din nou mai târziu.", + "remove_confirm": "Eliminați", + "error_user_plugins_off": "Administratorul dvs. a dezactivat pluginurile instalate de utilizatori, așa că acest plugin nu rulează.", + "error_approval_unavailable": "Acest plugin a fost instalat înainte ca aprobările să fie obligatorii. Eliminați-l și instalați-l din nou pentru a-l trimite spre aprobare.", + "error_approval_denied": "Administratorul dvs. nu a aprobat acest plugin.", + "error_approval_pending": "Se așteaptă ca administratorul să aprobe acest plugin.", + "error_auto_disabled": "Dezactivat pentru că a eșuat în mod repetat. Activați-l din nou pentru a reîncerca.", + "accounts_heading": "Conturi conectate", + "account_on": "{label} pe {host}", + "account_waiting": "{label} pe {host} (conectare neterminată)", + "disconnect": "Deconectați", + "disconnect_failed": "{label} nu a putut fi deconectat" + }, + "consent": { + "title": "Permiteți ca {name} să facă acest lucru?", + "intro": "Pluginul cere permisiunile de mai jos. Le primește doar dacă le permiteți.", + "high_risk_note": "Elementele marcate oferă pluginului acces la conținutul e-mailurilor dvs. sau îi permit să trimită date în altă parte. Permiteți-le doar pluginurilor în care aveți încredere.", + "revoke_hint": "Puteți dezactiva sau elimina pluginul oricând din Setări.", + "deny": "Nu permiteți", + "allow": "Permiteți" + }, + "permissions": { + "none": "Nu are nevoie de permisiuni speciale.", + "high_risk": "Necesită atenție", + "email_read": { + "title": "Citirea e-mailurilor", + "body": "Vede expeditorii, destinatarii, subiectele și textul complet al oricărui mesaj din contul dvs., precum și dosarele dvs." + }, + "email_write": { + "title": "Modificarea și organizarea e-mailurilor", + "body": "Adaugă sau elimină etichete, marcaje și alte cuvinte cheie din mesajele dvs., le mută între dosarele dvs. și în arhivă și salvează ciorne." + }, + "email_send": { + "title": "Modificarea e-mailurilor înainte de trimitere", + "body": "Citește și modifică destinatarii, subiectul, textul și atașamentele mesajelor pe care le scrieți și poate opri trimiterea unui mesaj." + }, + "crypto_full": { + "title": "Acces complet la această aplicație", + "body": "Rulează cu aceleași drepturi ca webmailul însuși. Poate citi toate e-mailurile și cheile stocate și poate acționa în numele dvs. Permiteți acest lucru doar pluginurilor în care aveți încredere deplină." + }, + "email_raw_send": { + "title": "Trimiterea mesajelor create de el însuși", + "body": "Trimite mesaje complete, de exemplu semnate sau criptate, din contul dvs." + }, + "email_blob_read": { + "title": "Citirea conținutului brut al mesajelor", + "body": "Citește datele originale ale mesajelor și atașamentelor dvs. și ale fiecărui fișier pe care îl atașați înainte de trimitere." + }, + "email_blob_write": { + "title": "Înlocuirea atașamentelor înainte de trimitere", + "body": "Încarcă fișiere pe serverul dvs. de e-mail și înlocuiește un fișier atașat cu altul înainte ca mesajul să fie trimis." + }, + "email_render_takeover": { + "title": "Modificarea modului în care sunt afișate mesajele", + "body": "Înlocuiește textul mesajelor pe care le deschideți și al listei de mesaje, de exemplu pentru a afișa conținut decriptat." + }, + "calendar_read": { + "title": "Citirea calendarului", + "body": "Vede calendarele dvs. și evenimentele lor." + }, + "calendar_write": { + "title": "Modificarea calendarului", + "body": "Creează, modifică și șterge evenimente." + }, + "contacts_read": { + "title": "Citirea contactelor", + "body": "Caută în agendele dvs. și le citește." + }, + "contacts_write": { + "title": "Modificarea contactelor", + "body": "Creează, modifică și șterge contacte și agende." + }, + "files_read": { + "title": "Citirea fișierelor", + "body": "Răsfoiește și deschide fișierele stocate în contul dvs." + }, + "files_write": { + "title": "Modificarea fișierelor", + "body": "Încarcă, redenumește, mută și șterge fișiere din contul dvs." + }, + "identity_read": { + "title": "Vizualizarea identităților de expediere", + "body": "Vede adresele de pe care trimiteți și semnăturile lor." + }, + "identity_write": { + "title": "Modificarea identităților de expediere", + "body": "Creează, modifică și șterge identități de expediere și semnături." + }, + "filters_read": { + "title": "Citirea filtrelor de e-mail", + "body": "Vede regulile care sortează e-mailurile primite." + }, + "filters_write": { + "title": "Modificarea filtrelor de e-mail", + "body": "Adaugă reguli în filtrele de pe server. Aceste reguli pot muta, redirecționa sau șterge e-mailurile primite." + }, + "tasks_read": { + "title": "Citirea sarcinilor", + "body": "Vede listele de sarcini și sarcinile dvs." + }, + "tasks_write": { + "title": "Modificarea sarcinilor", + "body": "Creează, modifică, finalizează și șterge sarcini." + }, + "templates_read": { + "title": "Citirea șabloanelor", + "body": "Vede șabloanele de mesaje salvate." + }, + "templates_write": { + "title": "Modificarea șabloanelor", + "body": "Creează, modifică și șterge șabloane de mesaje." + }, + "smime_read": { + "title": "Vizualizarea stării S/MIME", + "body": "Vede dacă aveți chei și certificate S/MIME, fără acces la chei." + }, + "vacation_read": { + "title": "Vizualizarea răspunsului automat", + "body": "Vede dacă răspunsul automat este activat și ce conține." + }, + "vacation_write": { + "title": "Modificarea răspunsului automat", + "body": "Activează sau dezactivează răspunsul automat și îi modifică textul și datele." + }, + "settings_read": { + "title": "Citirea setărilor", + "body": "Vede preferințele dvs. și definițiile etichetelor." + }, + "settings_write": { + "title": "Modificarea setărilor", + "body": "Adaugă și reordonează etichete și modifică preferințele dvs." + }, + "security_read": { + "title": "Vizualizarea securității contului", + "body": "Vede dacă autentificarea în doi pași și criptarea sunt activate, fără acces la secrete." + }, + "auth_observe": { + "title": "Aflarea momentului autentificării", + "body": "Este anunțat când vă autentificați, schimbați contul sau sesiunea este reînnoită." + }, + "auth_emit": { + "title": "Deconectarea dvs.", + "body": "Vă deconectează din webmail." + }, + "account_read": { + "title": "Vizualizarea conturilor", + "body": "Vede conturile pe care le-ați adăugat, cu adresele și serverele lor." + }, + "http_post": { + "title": "Folosirea serverului webmail în numele dvs.", + "body": "Apelează aceste endpointuri ale serverului cu datele dvs. de autentificare:" + }, + "http_fetch": { + "title": "Conectarea la alte site-uri", + "body": "Trimite cereri către aceste site-uri. Orice poate citi pluginul poate fi trimis acolo:" + }, + "admin_config": { + "title": "Citirea setărilor de la administrator", + "body": "Citește setările pe care administratorul le-a salvat pentru acest plugin." + }, + "ui_observe": { + "title": "Urmărirea locului în care vă aflați în aplicație", + "body": "Știe ce pagină este deschisă și reacționează la linkurile pe care faceți clic." + }, + "ui_toolbar": { + "title": "Adăugarea de butoane în barele de instrumente", + "body": "Afișează butoane proprii în bara de instrumente a mesajelor, pe un mesaj deschis și în bara de sus." + }, + "ui_app_top_banner": { + "title": "Afișarea unui banner în partea de sus", + "body": "Afișează o notificare în partea de sus a aplicației." + }, + "ui_email_banner": { + "title": "Afișarea de notificări pe mesaje", + "body": "Afișează notificări și rezumate proprii deasupra, în interiorul și între părțile unui mesaj sau ale unei conversații deschise." + }, + "ui_email_footer": { + "title": "Adăugarea de conținut sub mesaje", + "body": "Afișează conținut propriu sub un mesaj deschis." + }, + "ui_contact_cryptokeys": { + "title": "Afișarea cheilor de criptare ale contactelor", + "body": "Afișează și gestionează cheile de criptare ale unui contact." + }, + "ui_email_details": { + "title": "Adăugarea de panouri la detaliile mesajului", + "body": "Afișează un panou propriu lângă sau sub un mesaj deschis." + }, + "ui_download_file": { + "title": "Salvarea fișierelor pe dispozitiv", + "body": "Oferă spre descărcare fișierele create de el." + }, + "ui_message_list_tabs": { + "title": "Adăugarea de file în Primite", + "body": "Sortează mesajele primite în file precum Actualizări sau Promoții." + }, + "ui_composer_toolbar": { + "title": "Adăugarea în fereastra de redactare", + "body": "Afișează butoane, opțiuni de meniu și secțiuni proprii în timp ce scrieți un mesaj." + }, + "ui_composer_sidebar": { + "title": "Adăugarea unui panou în fereastra de redactare", + "body": "Afișează un panou propriu lângă mesajul pe care îl scrieți." + }, + "ui_sidebar_widget": { + "title": "Adăugarea în bara laterală", + "body": "Afișează casete și secțiuni proprii în bara laterală a e-mailului." + }, + "ui_settings_section": { + "title": "Adăugarea unei pagini de setări", + "body": "Afișează o pagină proprie în Setări." + }, + "ui_context_menu": { + "title": "Adăugarea de opțiuni în meniul mesajului", + "body": "Adaugă opțiuni în meniul care se deschide când faceți clic dreapta pe un mesaj." + }, + "ui_navigation_rail": { + "title": "Adăugarea în bara de navigare", + "body": "Afișează un buton propriu în bara de navigare." + }, + "ui_keyboard": { + "title": "Adăugarea de comenzi rapide de la tastatură", + "body": "Reacționează la comenzile rapide de la tastatură pe care le definește." + }, + "ui_calendar_action": { + "title": "Adăugarea în vizualizarea calendarului", + "body": "Afișează butoane și secțiuni proprii la evenimente, precum și etichete scurte și puncte la zilele din calendar." + }, + "ui_admin_page": { + "title": "Adăugarea unei pagini de administrare", + "body": "Afișează o pagină proprie în setările de administrare." + }, + "app_lifecycle": { + "title": "Aflarea momentului pornirii și opririi aplicației", + "body": "Este anunțat când aplicația pornește, trece offline sau vă deconectați." + }, + "compose_open": { + "title": "Deschiderea ferestrei de redactare", + "body": "Deschide un mesaj nou cu destinatarii, textul sau atașamentele deja completate. Dvs. decideți în continuare dacă îl trimiteți." + }, + "compose_read": { + "title": "Citirea mesajului pe care îl scrieți", + "body": "Citirea textului unui mesaj în timp ce îl scrieți, când folosiți pluginul în editor. Nu și semnătura sau mesajul citat." + }, + "compose_write": { + "title": "Modificarea mesajului pe care îl scrieți", + "body": "Înlocuirea sau adăugarea de text într-un mesaj în timp ce îl scrieți, când folosiți pluginul în editor. Puteți anula fiecare modificare și decideți în continuare dacă îl trimiteți." + }, + "ui_notifications": { + "title": "Afișarea notificărilor", + "body": "Afișează notificări pe dispozitiv sau în aplicație, dacă nu le-ați permis. Fiecare indică numele pluginului." + }, + "ui_message_list": { + "title": "Adăugarea în lista de mesaje", + "body": "Afișează conținut propriu deasupra listei de mesaje, precum și butoane pe mesaje și în bara mesajelor selectate." + }, + "ui_contacts": { + "title": "Adăugarea în vizualizarea contactelor", + "body": "Afișează secțiuni proprii în detaliile unui contact și butoane în lista de contacte." + }, + "ui_tasks": { + "title": "Adăugarea în vizualizarea sarcinilor", + "body": "Afișează secțiuni proprii în detaliile unei sarcini și butoane în lista de sarcini." + }, + "ui_files": { + "title": "Adăugarea în vizualizarea fișierelor", + "body": "Afișează secțiuni proprii în previzualizarea fișierelor și opțiuni în meniul unui fișier." + }, + "ui_commands": { + "title": "Adăugarea de comenzi", + "body": "Adaugă opțiuni proprii în paleta de comenzi (Ctrl+K)." + }, + "ui_app_page": { + "title": "Adăugarea de pagini", + "body": "Adaugă pagini proprii în aplicație, fiecare cu un buton în bara de navigare." + }, + "http_proxy": { + "title": "Folosirea conturilor pe care le conectați", + "body": "Trimite cereri prin serverul Bulwark cu un cont sau o cheie configurate de dvs. sau de administratorul dvs. Pluginul nu vede niciodată parola, dar poate face tot ce poate face acel cont pe serverul său." + } + }, + "dialog": { + "from_plugin": "De la pluginul {name}", + "ok": "OK", + "submit": "Trimite", + "confirm": "Confirmă", + "cancel": "Anulează", + "close": "Închide", + "turn_off": "Dezactivează {name}", + "plugin_menu": "Opțiuni pentru {name}", + "password_note": "Acest formular vine de la pluginul {name}, nu de la webmail. Introduceți o parolă doar dacă aveți încredere în acest plugin.", + "toast_auto_disabled": "{name} a fost dezactivat pentru că a eșuat în mod repetat." + }, + "connect": { + "title": "Conectați {label}", + "intro_password": "{name} vrea să folosească contul dvs. {label}. Bulwark păstrează parola pe serverul său și o trimite doar către {host}. Pluginul nu o vede niciodată.", + "intro_password_any": "{name} vrea să folosească contul dvs. {label}. Bulwark păstrează parola pe serverul său și o trimite doar către serverul pe care îl introduceți aici. Pluginul nu o vede niciodată.", + "intro_signin": "{name} vrea să se conecteze la contul dvs. {label}. Introduceți aici serverul, apoi conectați-vă pe pagina acestuia. Bulwark păstrează pe serverul său accesul obținut; pluginul nu îl vede niciodată.", + "server": "Adresa serverului", + "server_hint": "cloud.example.com", + "username": "Nume de utilizator", + "secret": "Parolă sau cheie", + "connect": "Conectați", + "continue": "Continuați", + "cancel": "Anulați", + "saving": "Se salvează…", + "error_server": "Introduceți adresa serverului, de exemplu https://cloud.example.com", + "error_save": "Nu s-a putut salva: {error}" + } + }, + "global_search": { + "title": "Caută în tot", + "placeholder": "Caută în mail, contacte, calendar, fișiere...", + "scope_label": "Caută în", + "account_label": "Cont", + "scope_all": "Tot", + "scope_mail": "E-mail", + "scope_contacts": "Contacte", + "scope_calendar": "Calendar", + "scope_files": "Fișiere", + "all_accounts": "Toate conturile", + "show_all": "Afișează toate rezultatele", + "no_results": "Niciun rezultat pentru „{query}”", + "server_hint": "Cuvintele complete caută pe server; fragmentele de cuvinte se potrivesc cu elementele deja încărcate", + "searching": "Se caută...", + "account_error": "Nu s-a putut căuta în {account}", + "recent_searches": "Căutări recente", + "recurring": "Recurent", + "no_subject": "(no subject)", + "empty_hint": "Caută mail, contacte, evenimente și fișiere în toate conturile. Încearcă from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Plat după dată", + "grouped": "Grupat", + "load_more": "Încarcă mai multe", + "preview_empty": "Selectează un rezultat pentru previzualizare", + "open_full": "Deschide", + "view_label": "Vizualizare" + }, + "top_bar": { + "more": "Mai multe opțiuni" + }, + "plugin_pages": { + "loading": "Se încarcă pagina", + "not_available_title": "Această pagină nu este disponibilă", + "not_available_description": "Pluginul care a adăugat-o este dezactivat sau nu mai este instalat.", + "back_to_mail": "Înapoi la e-mail", + "settings_group": "Pluginuri" + }, + "command_palette": { + "title": "Paleta de comenzi", + "open": "Deschide paleta de comenzi", + "placeholder": "Tastați o comandă", + "no_results": "Nicio comandă potrivită", + "go_to": "Mergi la {name}", + "compose": "Mesaj nou", + "search": "Caută în e-mail", + "theme_dark": "Comută la tema întunecată", + "theme_light": "Comută la tema luminoasă", + "shortcuts": "Afișează comenzile rapide de la tastatură", + "group_navigation": "Mergi la", + "group_actions": "Acțiuni", + "group_plugins": "Pluginuri" + }, + "notification_center": { + "title": "Notificări", + "mark_all_read": "Marchează toate ca citite", + "clear": "Golește", + "empty": "Nicio notificare", + "close": "Închide", + "unread": "Necitită", + "unread_count": "Necitite: {count}", + "level_error": "Eroare", + "level_warning": "Avertisment" + } +} diff --git a/locales/ru/common.json b/locales/ru/common.json index 1a1cef83..60e741ca 100644 --- a/locales/ru/common.json +++ b/locales/ru/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Минималистичный веб-клиент электронной почты, использующий протокол JMAP", - "login": { - "title": "Веб-почта", - "username_label": "Электронная почта", - "username_placeholder": "user@пример.рф", - "password_label": "Пароль", - "password_placeholder": "Введите пароль", - "jmap_endpoint_label": "Сервер JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Сервер должен разрешать CORS-запросы с этого домена.", - "jmap_server_label": "Сервер", - "jmap_server_auto_picked": "Сервер выбран по домену вашей электронной почты.", - "sign_in": "Войти", - "signing_in": "Вход...", - "loading": "Загрузка...", - "reconnecting": "Соединение потеряно. Попытка переподключения…", - "error": { - "invalid_credentials": "Неверный email или пароль. Проверьте учётные данные и повторите попытку.", - "connection_failed": "Не удалось подключиться к серверу. Проверьте интернет-соединение и попробуйте снова.", - "cors_blocked": "Сервер доступен, но блокирует запросы от других источников. Проверьте настройки CORS вашего JMAP-сервера и разрешите этот домен.", - "server_error": "Сервер временно недоступен. Повторите попытку позже.", - "generic": "Произошла непредвиденная ошибка. Если она повторяется, обратитесь к администратору.", - "totp_required": "Требуется код двухфакторной аутентификации. Пожалуйста, введите его ниже.", - "totp_invalid": "Неверный код аутентификации. Проверьте приложение аутентификатора и повторите попытку.", - "oauth_discovery_failed": "SSO включён, но поставщик удостоверений недоступен. Проверьте настройки OAuth." - }, - "show_password": "Показать пароль", - "hide_password": "Скрыть пароль", - "totp_toggle": "У меня есть код 2FA", - "remember_me": "Запомнить меня", - "config_error": { - "title": "Ошибка конфигурации", - "fetch_failed": "Не удалось загрузить конфигурацию приложения. Повторите попытку позже.", - "server_not_configured": "Почтовый сервер не настроен. Обратитесь к администратору." - }, - "remove_from_history": "Удалить из истории", - "totp_label": "Код аутентификации", - "totp_placeholder": "000000", - "session_expired": "Срок действия сессии истёк. Войдите снова.", - "dismiss": "Закрыть", - "or": "или", - "sign_in_sso": "Войти через SSO", - "add_account_title": "Добавить аккаунт", - "add_account_subtitle": "Войдите с другим аккаунтом", - "cancel": "Отмена", - "website": "Веб-сайт", - "imprint": "Выходные данные", - "privacy_policy": "Политика конфиденциальности", - "try_demo": "Попробовать демо", - "demo_description": "Исследуйте с примерными данными - аккаунт не нужен", - "demo_launching": "Запуск демо...", - "demo_login_button": "Запустить демо", - "demo_tagline": "Попробуйте полнофункциональный почтовый клиент. Аккаунт не нужен.", - "demo_no_signup": "Регистрация не требуется - исследуйте свободно с примерными данными", - "oauth_completing": "Завершение входа...", - "oauth_error": { - "title": "Ошибка аутентификации", - "invalid_state": "Ошибка проверки безопасности. Попробуйте войти снова.", - "missing_params": "Отсутствуют данные авторизации. Попробуйте войти снова.", - "token_exchange_failed": "Не удалось завершить аутентификацию. Повторите попытку.", - "access_denied": "Доступ запрещён. Обратитесь к администратору.", - "back_to_login": "Вернуться ко входу" - } - }, - "sidebar": { - "close": "Закрыть", - "compose": "Создать", - "compose_hint": "Создать (c)", - "search_placeholder": "Поиск почты...", - "search_placeholder_hint": "Поиск почты... (нажмите /)", - "storage": "Хранилище", - "storage_used": "Использовано", - "storage_free": "Свободно", - "storage_total": "Всего", - "sign_out": "Выйти", - "sign_out_of": "Выйти из {account}", - "sign_out_all": "Выйти из всех аккаунтов", - "add_account": "Добавить аккаунт", - "set_as_default": "Установить по умолчанию", - "switch_account": "Сменить аккаунт", - "contacts": "Контакты", - "calendar": "Календарь", - "settings": "Настройки", - "admin": "Админ", - "files": "Файлы", - "loading_mailboxes": "Загрузка папок...", - "push_connected": "Обновления в реальном времени активны", - "push_disconnected": "Обновления в реальном времени неактивны", - "keyboard_shortcuts": "Сочетания клавиш", - "theme": { - "light": "Светлая тема", - "dark": "Тёмная тема", - "system": "Системная тема" - }, - "language": { - "title": "Язык" - }, - "mailboxes": { - "inbox": "Входящие", - "sent": "Отправленные", - "drafts": "Черновики", - "trash": "Корзина", - "archive": "Архив", - "starred": "Помечённые", - "all_mail": "Вся почта", - "spam": "Спам", - "important": "Важные" - }, - "unified_inbox": "Общие входящие", - "unified_sent": "Все отправленные", - "unified_drafts": "Все черновики", - "unified_trash": "Все корзины", - "unified_archive": "Все архивы", - "unified_junk": "Весь спам", - "all_accounts": "Все аккаунты", - "unified_mailbox": "Общий почтовый ящик", - "expand": "Развернуть", - "collapse": "Свернуть", - "expand_tooltip": "Развернуть", - "collapse_tooltip": "Свернуть", - "mobile": { - "search": "Поиск", - "compose": "Создать", - "go_back": "Назад", - "toggle_menu": "Переключить меню" - }, - "clear_search": "Очистить поиск", - "vacation_active": "Автоответ на отпуск активен", - "demo_banner": "Режим демо", - "demo_reset": "Сбросить", - "demo_tour": "Тур", - "tags": "Теги", - "show_all_tags": "Показать все ({count})", - "show_fewer_tags": "Показать меньше", - "folders": "Папки", - "mail": "Почта", - "nav_label": "Навигация", - "add_app": "Приложения", - "shared": "Общие", - "scheduled": "Запланировано", - "unified_all_unread": "Все непрочитанные", - "unified_all_starred": "Все помеченные", - "unified_all_mail": "Вся почта", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Приложения по умолчанию", - "description": "Выберите, должны ли ссылки электронной почты и календаря открываться в Bulwark. Технически Bulwark регистрируется как обработчик протокола для ссылок mailto: и webcal:.", - "unsupported": "Этот браузер или это соединение не поддерживает ручную регистрацию обработчиков протоколов. Возможно, установленное PWA всё же можно использовать через настройки браузера или системы.", - "mailto_label": "Ссылки электронной почты", - "mailto_description": "Открывает ссылки mailto: в Bulwark с предварительно заполненным редактором письма.", - "protocol_open_mode_label": "При открытии ссылок протоколов", - "protocol_open_mode_description": "Выберите, будет ли Bulwark открывать ссылки mailto: и webcal: в новой вкладке или повторно использовать открытую сессию. Для варианта с активной сессией нужно разрешение на уведомления, чтобы можно было нажать на резервное уведомление и вывести Bulwark на передний план, если браузер блокирует фокус.", - "protocol_open_mode_active_session": "Открывать в активной сессии, если возможно", - "protocol_open_mode_new_tab": "Всегда открывать новую вкладку", - "focus_notification_title": "Открыть Bulwark", - "focus_notification_body": "Ссылка была открыта в Bulwark. Нажмите, чтобы вывести окно на передний план.", - "webcal_label": "Ссылки календаря", - "webcal_description": "Открывает ссылки webcal: в Bulwark с предварительно заполненным диалогом подписки на календарь.", - "register_mailto": "Зарегистрировать почтовое приложение", - "register_webcal": "Зарегистрировать приложение календаря", - "mailto_registered": "Запрошена регистрация обработчика электронной почты", - "webcal_registered": "Запрошена регистрация обработчика календаря", - "registration_failed": "Не удалось зарегистрировать обработчик протокола", - "opening_mailto": "Открытие редактора...", - "opening_webcal": "Открытие календаря...", - "browser_note": "Браузер или операционная система может запросить подтверждение и может потребовать, чтобы Bulwark был установлен, прежде чем его можно будет выбрать приложением по умолчанию.", - "select_account_title": "Выберите аккаунт", - "select_mailto_account": "Выберите, в каком аккаунте открыть эту ссылку электронной почты.", - "select_webcal_account": "Выберите, в каком аккаунте открыть эту ссылку календаря.", - "select_account_note": "Этот выбор применяется только к этой ссылке протокола.", - "detail_to": "Кому", - "detail_subject": "Тема", - "detail_no_subject": "Без темы", - "detail_calendar": "Календарь", - "detail_source": "Источник", - "active_account": "Активен", - "switching_account": "Переключение аккаунта..." - }, - "sidebar_apps": { - "modal_title": "Приложения боковой панели", - "add_new": "Добавить приложение", - "edit_app": "Редактировать приложение", - "name_label": "Название", - "name_placeholder": "Моё приложение", - "name_required": "Название обязательно", - "url_label": "URL-адрес", - "url_required": "URL обязателен", - "url_invalid": "Введите корректный URL http или https", - "icon_label": "Иконка", - "icon_required": "Иконка обязательна", - "open_mode_label": "Режим открытия", - "open_new_tab": "Новая вкладка", - "open_inline": "Встроенный", - "cancel": "Отмена", - "add": "Добавить", - "update": "Обновить", - "delete": "Удалить", - "delete_confirm_title": "Удалить приложение", - "delete_confirm": "Вы уверены, что хотите удалить \"{name}\"?", - "no_apps": "Приложения ещё не добавлены", - "no_apps_hint": "Добавьте пользовательские приложения и ссылки в боковую панель", - "managed_title": "Предоставлено администратором", - "managed_description": "Эти приложения настроены для всех и не могут быть изменены.", - "managed_badge": "Управляемое", - "search_icons": "Поиск иконок...", - "show_popular": "Популярные", - "show_all": "Все", - "no_icons_found": "Иконки не найдены", - "inline_badge": "Встроенный", - "tab_badge": "Вкладка", - "show_on_mobile": "Показывать на мобильном" - }, - "email_list": { - "no_emails": "Сообщений не найдено", - "no_emails_description": "Папка пуста", - "no_search_results": "Результатов не найдено", - "no_search_results_description": "Попробуйте изменить поисковый запрос или фильтры", - "loading": "Загрузка писем...", - "unread": "непрочитанные", - "to_me": "Мне", - "to_recipients": "{count} получателям", - "and_others": "и ещё {count}", - "draft": "Черновик", - "starred": "Помечено", - "pinned": "Закреплено", - "replied": "Отвечено", - "forwarded": "Переслано", - "has_attachment": "С вложением", - "conversations_count": "{count} из {total} бесед", - "conversations_count_plus": "{count}+ бесед", - "conversations_count_simple": "{count} бесед", - "no_conversations": "Нет бесед", - "loading_more": "Загрузка писем...", - "no_more_emails": "Больше писем нет", - "batch_actions": { - "select": "Выбрать письма", - "select_all": "Выбрать все", - "selected_messages": "Выбрано {count, plural, one {1 письмо} other {# письма}}", - "mark_read": "Отметить прочитанными", - "mark_unread": "Отметить непрочитанными", - "delete": "Удалить", - "delete_confirm_title": "Удалить письма", - "delete_confirm_message": "Вы уверены, что хотите удалить {count, plural, one {1 письмо} other {# писем}}?", - "clear_selection": "Снять выделение" - }, - "permanent_delete": "Удалить навсегда", - "permanent_delete_confirm_title": "Удалить навсегда", - "permanent_delete_confirm_message": "Это письмо будет удалено навсегда. Действие нельзя отменить.", - "permanent_delete_confirm_batch_message": "{count, plural, one {1 письмо} other {# писем}} будут удалены навсегда. Действие нельзя отменить.", - "empty_folder": { - "button": "Очистить папку", - "confirm_title": "Очистить папку", - "confirm_message": "Все письма в этой папке будут удалены навсегда. Действие нельзя отменить.", - "confirm_button": "Очистить папку", - "junk_hint": "Вы можете очистить папку Спам для постоянного удаления всех сообщений.", - "trash_hint": "Вы можете очистить Корзину для постоянного удаления всех сообщений." - }, - "no_scheduled_emails": "Нет запланированных писем", - "no_scheduled_emails_description": "Сообщения, запланированные на позже, появятся здесь.", - "scheduled_count": "{count, plural, one {# запланированное письмо} few {# запланированных письма} other {# запланированных писем}}", - "scheduled_actions_hint": "Запланированные сообщения можно отменить, перенести или изменить через действия планирования.", - "cancel_scheduled_send": "Отменить отправку", - "reschedule_send": "Перенести", - "cancel_and_edit": "Отменить и изменить", - "cancel_and_compose_again": "Отменить и написать заново", - "reschedule_prompt": "Введите новую дату/время, например 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Отправитель запрашивает уведомление о прочтении:", - "send": "Отправить уведомление", - "ignore": "Игнорировать", - "sent": "Уведомление о прочтении отправлено.", - "send_failed": "Не удалось отправить уведомление о прочтении", - "mdn_subject": "Прочитано: {subject}", - "mdn_body": "Это уведомление о прочтении сообщения, отправленного вами на адрес {recipient}.\n\nПримечание: это уведомление лишь подтверждает, что сообщение было показано на компьютере получателя. Оно не гарантирует, что получатель прочитал или понял содержимое." - }, - "no_email_selected": "Письмо не выбрано", - "no_email_description": "Выберите письмо из списка, чтобы просмотреть его здесь", - "no_conversation_selected": "Беседа не выбрана", - "no_conversation_description": "Выберите беседу из списка, чтобы прочитать её здесь", - "compose": "Написать", - "compose_hint": "Написать новое сообщение", - "no_subject": "(Без темы)", - "no_body_content": "(Содержимое отсутствует)", - "no_preview_available": "Предварительный просмотр недоступен", - "show_quoted_text": "Показать цитируемый текст", - "hide_quoted_text": "Скрыть цитируемый текст", - "loading_email": "Загрузка письма...", - "loading": "Загрузка...", - "reply": "Ответить", - "reply_all": "Ответить всем", - "forward": "Переслать", - "delete": "Удалить", - "archive": "Архивировать", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Пометить", - "unstar": "Снять пометку", - "mark_unread": "Отметить непрочитанным", - "mark_read": "Отметить прочитанным", - "unread": "Не прочитано", - "read": "Прочитано", - "spam_short": "Спам", - "not_spam_short": "Не спам", - "move": "Переместить", - "print": "Распечатать", - "view_source": "Просмотреть исходный код", - "fullscreen": "Во весь экран", - "exit_fullscreen": "Выйти из полноэкранного режима", - "export_email": "Экспортировать как .eml", - "forward_as_attachment": "Переслать как вложение", - "import_email": "Импортировать .eml или .zip", - "keyboard_shortcuts": "Сочетания клавиш (?)", - "email_source": "Исходный код письма", - "draft_banner": "Это письмо является черновиком", - "edit_draft": "Редактировать", - "copy_source": "Копировать в буфер обмена", - "source_copied": "Исходный код скопирован в буфер обмена", - "attachments": "Вложения", - "important": "Важное", - "download": "Скачать", - "download_all": "Скачать все", - "from": "От", - "to": "Кому", - "cc": "Копия", - "bcc": "Скрытая копия", - "date": "Дата", - "subject": "Тема", - "show_details": "Показать детали", - "hide_details": "Скрыть детали", - "external_content_warning": "Изображения и внешнее содержимое заблокированы", - "load_external_content": "Загрузить изображения", - "trust_sender": "Всегда доверять этому отправителю", - "back_to_list": "Вернуться к списку", - "view_contact": "Просмотреть контакт", - "message_details": "Детали сообщения", - "more_reply_options": "Дополнительные параметры ответа", - "set_tag": "Установить тег", - "tag": "Тег", - "more_actions": "Другие действия", - "previous": "Пред.", - "next": "След.", - "move_to": "Переместить в...", - "remove_tag": "Удалить тег", - "tag_filter_placeholder": "Фильтр тегов", - "tag_no_matches": "Подходящих тегов нет", - "more_count": "+{count} ещё", - "characters_count": "{count} символов", - "quick_reply_placeholder": "Написать быстрый ответ...", - "more_options": "Другие параметры", - "sending": "Отправка...", - "security_authentication": "Безопасность и аутентификация", - "technical_details": "Технические детали", - "message_id_label": "Идентификатор сообщения:", - "reply_to_label": "Адрес для ответа:", - "delivery_time_label": "Время доставки:", - "conversation_part_label": "Часть беседы:", - "previous_messages": "{count} предыдущее сообщение", - "previous_messages_plural": "{count} предыдущих сообщений", - "time": { - "day": "день", - "days": "дней", - "hour": "час", - "hours": "часов", - "minute": "минута", - "minutes": "минут" - }, - "unknown_sender": "Неизвестно", - "recipient_me": "я", - "recipient_and_others": "{name} и ещё {count}", - "recipient_to_prefix": "Кому:", - "authentication": { - "title": "Аутентификация", - "status": { - "verified": "Подтверждено", - "warning": "Предупреждение", - "none": "Не аутентифицировано" - }, - "spf": { - "pass": "SPF прошёл", - "fail": "SPF не прошёл", - "none": "SPF отсутствует" - }, - "dkim": { - "pass": "DKIM действителен", - "fail": "DKIM недействителен", - "none": "DKIM отсутствует" - }, - "dmarc": { - "pass": "DMARC прошёл", - "fail": "DMARC не прошёл", - "none": "DMARC отсутствует" - }, - "spam_score": "Оценка спама", - "tooltip_spf": "Sender Policy Framework: проверяет, имеет ли отправляющий сервер право отправлять почту от имени домена", - "tooltip_dkim": "DomainKeys Identified Mail: подтверждает с помощью криптографической подписи, что письмо не было изменено при передаче", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: обеспечивает согласованность SPF и DKIM с доменом отправителя и устанавливает политику для сбоев", - "policy": "Политика", - "result": { - "pass": "Пройдено", - "fail": "Не пройдено", - "softfail": "Частичный сбой", - "neutral": "Нейтрально", - "permerror": "Постоянная ошибка", - "temperror": "Временная ошибка", - "none": "Нет" - } - }, - "headers": { - "routing": "Маршрутизация", - "received": "Получено", - "message_id": "Идентификатор сообщения", - "list_info": "Информация о рассылке" - }, - "tooltips": { - "reply": "Ответить (r)", - "reply_all": "Ответить всем (a)", - "forward": "Переслать (f)", - "archive": "Архивировать (e)", - "delete": "Удалить (# или Del)", - "star": "Пометить (s)", - "unstar": "Снять пометку (s)", - "compose": "Создать (c)", - "previous": "Предыдущее письмо", - "next": "Следующее письмо", - "edit_draft": "Редактировать черновик" - }, - "spam": { - "button_title": "Пожаловаться на спам", - "not_spam_title": "Отметить как не спам", - "toast_success": "Перемещено в Спам", - "toast_batch": "{count} писем перемещено в Спам", - "toast_undo": "Отменить", - "toast_not_spam_success": "Перемещено во Входящие", - "toast_not_spam_batch": "{count} писем перемещено во Входящие", - "error": "Не удалось пожаловаться на спам", - "error_not_spam": "Не удалось восстановить письмо" - }, - "unsubscribe_banner": { - "label": "Рассылка", - "button": "Отписаться", - "confirm_title": "Отписаться от этого отправителя?", - "confirm_button": "Подтвердить", - "cancel": "Отмена", - "success_http": "Страница отписки открыта в новой вкладке", - "success_mailto": "Письмо для отписки отправлено", - "confirm_message_http": "Страница отписки откроется в новой вкладке.", - "confirm_message_mailto": "Отправителю будет отправлено письмо для отписки.", - "error": "Не удалось отписаться", - "dismiss": "Закрыть" - }, - "calendar_invitation": { - "loading": "Загрузка деталей события…", - "title": "Приглашение в календарь", - "published_title": "Опубликованное событие", - "response_title": "Ответ на событие", - "update_title": "Обновление события", - "counter_title": "Встречное предложение", - "refresh_title": "Запрос обновления", - "declined_counter_title": "Встречное предложение отклонено", - "cancelled_title": "Событие отменено", - "organizer": "Организовано {name}", - "organizer_label": "Организовано", - "attendees": "{count, plural, one {# участник} other {# участников}}", - "accept": "Принять", - "maybe": "Возможно", - "decline": "Отклонить", - "add_to_calendar": "Добавить в календарь", - "added": "Добавлено в календарь", - "rsvp_sent": "Ответ отправлен", - "parse_error": "Не удалось прочитать приглашение", - "action_failed": "Не удалось выполнить действие с календарём.", - "no_calendar": "Календарь недоступен", - "published_info": "Это событие опубликовано для ознакомления.", - "response_info": "Это сообщение содержит ответ участника.", - "response_info_organizer": "Этот ответ участника обновляет ваше событие.", - "update_info": "Это сообщение обновляет существующее событие.", - "counter_info": "Это сообщение предлагает изменения в событии.", - "counter_info_organizer": "Этот участник предложил изменения в вашем событии.", - "refresh_info": "Это сообщение запрашивает последние детали события.", - "refresh_info_organizer": "Участник запросил последние детали события.", - "declined_counter_info": "Организатор отклонил встречное предложение.", - "authentication_failed_info": "Проверка подлинности почты для этого приглашения не пройдена. Будьте осторожны с действиями в календаре.", - "authentication_missing_info": "Это приглашение не содержит подтверждённой аутентификации почты. Если что-то кажется необычным, уточните детали у организатора.", - "sender_mismatch_info": "Это приглашение отправлено от {sender}, тогда как организатором в данных календаря указан {organizer}.", - "sender_mismatch_unverified_info": "Это приглашение отправлено от {sender}, тогда как организатором указан {organizer}, и сообщение не удалось проверить.", - "organizer_role": "Вы являетесь организатором этого события", - "your_response": "Ваш ответ: {status}", - "response_needed": "Требуется ответ", - "response_accepted": "Принято", - "response_tentative": "Под вопросом", - "response_declined": "Отклонено", - "response_delegated": "Делегировано", - "actor_sent_info": "Отправлено {name}.", - "actor_response_info": "{name} ответил {status}.", - "actor_counter_info": "{name} предложил изменения в этом событии.", - "actor_refresh_info": "{name} запросил последние детали события.", - "actor_declined_counter_info": "{name} отклонил встречное предложение.", - "actor_note": "Примечание: {comment}", - "actor_unknown": "Кто-то", - "proposed_changes": "Предложенные изменения", - "change_title": "Заголовок", - "change_time": "Время", - "change_location": "Местоположение", - "change_description": "Описание", - "change_empty": "Нет", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Применить предложенные изменения", - "proposal_applied": "Предложенные изменения применены.", - "review_proposal": "Просмотреть предложение", - "review_request": "Просмотреть запрос", - "view_in_calendar": "Открыть в календаре", - "select_calendar": "Выбрать календарь", - "already_in_calendar": "Уже в вашем календаре", - "request_info": "Вас пригласили на это событие. Ответьте, чтобы организатор знал о вашей доступности.", - "cancel_info": "Организатор отменил это событие.", - "event_updated": "Обновление №{sequence}", - "event_status_tentative": "Под вопросом", - "event_status_cancelled": "Отменено", - "collapse": "Скрыть детали", - "expand": "Показать детали" - }, - "send": "Отправить", - "more": "ещё", - "details": { - "recipients_routing": "Получатели и маршрутизация", - "authentication_security": "Аутентификация и безопасность", - "identifiers_threading": "Идентификаторы и обсуждения", - "mailing_list": "Список рассылки", - "message_properties": "Свойства сообщения", - "sent": "Отправлено", - "received": "Получено", - "delivery_time": "Время доставки", - "in_reply_to": "В ответ на", - "references": "Ссылки", - "thread_id": "ID обсуждения", - "size": "Размер", - "mime_type": "MIME-тип", - "attachments_summary": "{count} файлов · {size}", - "list_id": "ID списка", - "list_help": "Справка списка", - "list_post": "Публикация в список", - "list_unsubscribe": "Отписаться", - "iprev": "Обратный DNS", - "spam_status": "Статус спама", - "ai_verdict": "Вердикт ИИ", - "account": "Аккаунт", - "no_subject": "(без темы)" - }, - "scheduled_banner": "Запланировано к отправке {date}", - "scheduled_send_created": "Письмо запланировано к отправке", - "cancel_scheduled_send": "Отменить отправку", - "reschedule_send": "Перенести", - "cancel_and_edit": "Отменить и изменить", - "cancel_and_compose_again": "Отменить и написать заново", - "reschedule_prompt": "Введите новую дату/время, например 2026-05-04T15:30", - "scheduled_actions_only": "В представлении запланированных доступны только действия планирования", - "undo_send_scheduled": "Сообщение запланировано к отправке", - "undo_send": "Отменить отправку", - "contact_sidebar": { - "title": "Контакт", - "close": "Закрыть боковую панель", - "action_email": "Письмо", - "action_email_title": "Отправить письмо", - "action_copy": "Копировать", - "action_copy_title": "Копировать адрес", - "action_edit_title": "Редактировать контакт", - "section_emails": "Электронная почта", - "section_phones": "Телефоны", - "section_organizations": "Организации", - "section_addresses": "Адреса", - "section_notes": "Заметки", - "not_in_contacts": "Нет в ваших контактах", - "add_to_contacts": "Добавить в контакты", - "copied": "Скопировано!", - "copy_failed": "Не удалось скопировать" - }, - "send_now": "Отправить сейчас" - }, - "email_composer": { - "read_receipt_on": "Запрошено уведомление о прочтении (нажмите, чтобы отключить)", - "read_receipt_off": "Запросить уведомление о прочтении", - "dsn_on": "Запрошено уведомление о доставке (нажмите, чтобы отключить)", - "dsn_off": "Запросить уведомление о доставке", - "format_plain_text": "Переключить на простой текст", - "format_rich_text": "Переключить на форматированный текст (HTML)", - "require_tls_on": "Требуется зашифрованная доставка (нажмите, чтобы отключить)", - "require_tls_off": "Требовать зашифрованную доставку (TLS)", - "new_message": "Новое письмо", - "signature_edit_hint": "Дважды щёлкните, чтобы изменить подпись", - "reply": "Ответить", - "reply_all": "Ответить всем", - "forward": "Переслать", - "reply_to": "Ответить", - "reply_all_to": "Ответить всем", - "forward_message": "Переслать", - "from": "От", - "to": "Кому", - "cc": "Копия", - "bcc": "Скрытая копия", - "subject": "Тема", - "body_placeholder": "Напишите сообщение...", - "send": "Отправить", - "cancel": "Отмена", - "attach": "Прикрепить", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Удалить", - "discard_draft_title": "Удалить черновик?", - "discard_draft_confirm": "Есть несохранённые изменения. Удалить этот черновик?", - "saving": "Сохранение...", - "draft_saved": "Черновик сохранён", - "save_failed": "Не удалось сохранить", - "to_placeholder": "Адреса получателей", - "cc_placeholder": "Получатели копии", - "bcc_placeholder": "Получатели скрытой копии", - "subject_placeholder": "Тема", - "cc_label": "Копия:", - "bcc_label": "Скрытая копия:", - "subject_label": "Тема:", - "file_size_kb": "КБ", - "prefix": { - "forward": "Пересл:", - "reply": "Ответ:" - }, - "no_subject": "(Без темы)", - "unknown_sender": "Неизвестный", - "quote": { - "reply_header": "{date}, {sender} написал(а):", - "forward_header": "---------- Пересланное сообщение ----------", - "from": "От: {sender}", - "date": "Дата: {date}", - "subject": "Тема: {subject}", - "to": "Кому: {recipients}" - }, - "remove_sub_address": "Удалить суб-адрес", - "from_override": { - "toggle_off": "Переопределить", - "toggle_on": "Отменить переопределение", - "toggle_tooltip": "Свободно редактируйте имя и адрес отправителя. Письмо по-прежнему отправляется через вашу учётную запись.", - "name_label": "Имя отправителя", - "name_placeholder": "Имя", - "email_label": "Email отправителя", - "email_placeholder": "alias@example.com", - "envelope_notice": "Если ваш сервер не принимает этот адрес как отправителя конверта, вместо него используется {identity}, и получатели увидят его в заголовке Return-Path." - }, - "use_template": "Шаблон", - "save_as_template": "Сохранить как шаблон", - "validation": { - "recipient_required": "Укажите получателя для отправки", - "body_required": "Напишите сообщение или прикрепите файл", - "attachments_uploading": "Вложения ещё загружаются — отправка начнётся, как только загрузка завершится", - "attachment_upload_failed": "Не отправлено — не удалось загрузить вложение. Удалите его и попробуйте снова." - }, - "upload_progress": "Загрузка {uploaded} / {total}", - "upload_cancel": "Отменить загрузку", - "upload_failed": "Не удалось загрузить {filename}", - "attachment_too_large": "«{name}» больше, чем разрешает сервер ({max} на файл)", - "attachments_total_too_large": "Вложения превысят лимит сервера в {max} на письмо", - "drop_files": "Перетащите файлы для прикрепления", - "show_less": "Свернуть", - "send_failed": "Не удалось отправить письмо", - "send_timeout": "Сервер перестал отвечать. Прежде чем отправлять снова, проверьте папку «Отправленные» — письмо могло уже уйти.", - "continue_draft": "Продолжить черновик", - "close_draft_title": "Сохранить или удалить черновик?", - "close_draft_message": "Есть несохранённые изменения. Сохранить как черновик или удалить?", - "smime_sign_on": "Подпись S/MIME включена", - "smime_sign_off": "Включить подпись S/MIME", - "smime_encrypt_on": "Шифрование S/MIME включено", - "smime_encrypt_off": "Включить шифрование S/MIME", - "smime_encrypt_unavailable": "Шифрование S/MIME недоступно - отсутствует сертификат получателя", - "smime_unlock_title": "Разблокировать ключ S/MIME", - "smime_unlock_message": "Введите парольную фразу для разблокировки вашего ключа подписи S/MIME.", - "smime_unlock_button": "Разблокировать", - "smime_passphrase_placeholder": "Парольная фраза", - "forgot_attachment": { - "title": "Вы забыли прикрепить файл?", - "message": "В вашем сообщении упоминается \"{keyword}\", но файл не прикреплён. Отправить всё равно?", - "send_anyway": "Отправить всё равно", - "back": "Вернуться к редактированию" - }, - "empty_subject": { - "title": "Отправить без темы?", - "message": "У этого письма нет темы. Всё равно отправить?", - "dont_ask_again": "Больше не спрашивать", - "send_anyway": "Отправить всё равно", - "back": "Вернуться к редактированию" - }, - "add_link": "Добавить ссылку", - "link_url_prompt": "Введите URL", - "sending": "Отправка...", - "schedule_send": "Запланировать отправку", - "schedule_send_description": "Выберите, когда сервер должен отправить это сообщение.", - "schedule_send_required": "Выберите дату и время.", - "schedule_send_invalid": "Введите действительные дату и время.", - "schedule_send_future": "Выберите дату и время в будущем.", - "schedule_send_too_late": "Это время позже, чем разрешает сервер.", - "schedule_send_unsupported": "Запланированная отправка не поддерживается для этой учетной записи.", - "schedule_send_cleanup_warning": "Запланированная отправка создана, но очистка черновика не удалась.", - "send_delay_unsupported": "Задержка отправки не поддерживается для этой учетной записи.", - "send_delay_unsupported_confirm": "Эта учетная запись не поддерживает задержку отправки. Отправить сразу?", - "recipient_edit_email": "Изменить адрес эл. почты", - "recipient_edit_name": "Изменить отображаемое имя", - "recipient_email_placeholder": "Адрес эл. почты", - "recipient_name_placeholder": "Отображаемое имя", - "autocomplete_search_server": "Искать на сервере", - "autocomplete_searching": "Поиск...", - "toolbar": { - "bold": "Жирный", - "italic": "Курсив", - "underline": "Подчёркнутый", - "strikethrough": "Зачёркнутый", - "text_color": "Цвет текста", - "remove_color": "Убрать цвет", - "heading_1": "Заголовок 1", - "heading_2": "Заголовок 2", - "bullet_list": "Маркированный список", - "ordered_list": "Нумерованный список", - "quote": "Цитата", - "code_block": "Блок кода", - "align_left": "По левому краю", - "align_center": "По центру", - "align_right": "По правому краю", - "text_direction": "Направление текста (RTL/LTR)", - "link": "Ссылка", - "table": "Таблица", - "clear_formatting": "Очистить форматирование", - "undo": "Отменить", - "redo": "Повторить", - "add_row_above": "Вставить строку выше", - "add_row_below": "Вставить строку ниже", - "add_column_before": "Вставить столбец слева", - "add_column_after": "Вставить столбец справа", - "delete_row": "Удалить строку", - "delete_column": "Удалить столбец", - "toggle_header_row": "Переключить строку заголовка", - "delete_table": "Удалить таблицу", - "pick_size": "Выбрать размер", - "font_size": "Размер шрифта", - "font_size_default": "По умолчанию", - "background_color": "Цвет фона", - "remove_background_color": "Убрать цвет фона" - }, - "send_filing_warning": "Отправлено - но последующая очистка не удалась, может остаться устаревший черновик." - }, - "confirm_dialog": { - "confirm": "Подтвердить", - "cancel": "Отмена" - }, - "common": { - "loading": "Загрузка...", - "error": "Ошибка", - "success": "Успешно", - "cancel": "Отмена", - "save": "Сохранить", - "delete": "Удалить", - "edit": "Редактировать", - "close": "Закрыть", - "search": "Поиск", - "refresh": "Обновить", - "settings": "Настройки", - "help": "Помощь", - "logout": "Выйти", - "yes": "Да", - "no": "Нет", - "unknown": "Неизвестно", - "app_title": "Веб-почта", - "reconnecting": "Соединение потеряно. Попытка переподключения…", - "rate_limited_title": "Аутентификация на сервере временно ограничена.", - "rate_limited_detail": "Bulwark приостановил фоновые запросы, чтобы избежать блокировки. Повтор через {seconds}с.", - "rate_limited_action_title": "Запрос приостановлен, чтобы избежать блокировки.", - "rate_limited_action_detail": "Bulwark ждет окончания серверного тайм-аута перед отправкой новых аутентифицированных запросов. Повторите через {seconds}с.", - "pull_to_refresh": "Потяните, чтобы обновить", - "release_to_refresh": "Отпустите, чтобы обновить", - "refreshing": "Обновление…" - }, - "notifications": { - "email_sent": "Письмо успешно отправлено", - "email_deleted": "Письмо удалено", - "email_archived": "Письмо архивировано", - "emails_deleted": "{count, plural, one {# письмо удалено} few {# письма удалены} many {# писем удалено} other {# письма удалено}}", - "emails_archived": "{count, plural, one {# письмо архивировано} few {# письма архивированы} many {# писем архивировано} other {# письма архивировано}}", - "error_archiving": "Не удалось архивировать письмо", - "error_updating": "Не удалось обновить письмо", - "email_starred": "Письмо помечено", - "email_unstarred": "Пометка снята", - "email_marked_read": "Письмо отмечено прочитанным", - "email_marked_unread": "Письмо отмечено непрочитанным", - "copied_to_clipboard": "Скопировано в буфер обмена", - "source_copied": "Исходный код скопирован в буфер обмена", - "error_sending": "Не удалось отправить письмо", - "error_deleting": "Не удалось удалить письмо", - "error_loading": "Не удалось загрузить письма", - "new_email": "Новое письмо", - "new_email_from": "От {sender}", - "click_to_view": "Нажмите для просмотра", - "email_moved": "Письмо перемещено", - "emails_moved": "{count} писем перемещено", - "moved_to_mailbox": "Перемещено в {mailbox}", - "move_failed": "Ошибка перемещения", - "move_error": "Не удалось переместить письма в выбранную папку", - "email_tagged": "Письмо помечено тегом", - "emails_tagged": "{count} писем помечено тегом", - "tag_failed": "Ошибка тегирования", - "identity_created": "Удостоверение успешно создано", - "identity_updated": "Удостоверение успешно обновлено", - "identity_deleted": "Удостоверение удалено", - "identity_set_primary": "Основное удостоверение обновлено", - "identity_create_failed": "Не удалось создать удостоверение: {error}", - "identity_update_failed": "Не удалось обновить удостоверение: {error}", - "identity_delete_failed": "Не удалось удалить удостоверение: {error}", - "identity_unauthorized": "У вас нет прав для отправки с этого email-адреса", - "identity_not_found": "Удостоверение не найдено", - "vacation_saved": "Настройки автоответа на отпуск сохранены", - "vacation_save_failed": "Не удалось сохранить настройки автоответа на отпуск", - "filters_saved": "Фильтры успешно сохранены", - "filters_save_failed": "Не удалось сохранить фильтры", - "filters_deleted": "Правило фильтра удалено", - "templates_exported": "Шаблоны успешно экспортированы", - "templates_imported": "{count, plural, one {# шаблон} other {# шаблонов}} импортировано", - "templates_import_errors": "Некоторые шаблоны не удалось импортировать", - "templates_import_empty": "В файле не найдено шаблонов", - "export_email_error": "Не удалось экспортировать письмо", - "import_email_success": "Письмо успешно импортировано", - "import_email_error": "Не удалось импортировать письмо" - }, - "date": { - "today": "Сегодня", - "yesterday": "Вчера", - "this_week": "На этой неделе", - "last_week": "На прошлой неделе", - "this_month": "В этом месяце", - "older": "Давно", - "just_now": "Только что", - "minutes_ago": "{count} минуту назад", - "minutes_ago_plural": "{count} минут назад", - "hours_ago": "{count} час назад", - "hours_ago_plural": "{count} часов назад", - "days_ago": "{count} день назад", - "days_ago_plural": "{count} дней назад" - }, - "language": { - "title": "Язык", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Выбрать язык", - "switch_to_english": "Переключить на английский", - "switch_to_french": "Переключить на французский", - "switch_to_japanese": "Переключить на японский", - "switch_to_spanish": "Переключить на испанский", - "switch_to_italian": "Переключить на итальянский", - "switch_to_german": "Переключить на немецкий", - "switch_to_dutch": "Переключить на нидерландский", - "switch_to_portuguese": "Переключить на португальский", - "switch_to_russian": "Переключить на русский", - "switching": "Смена языка...", - "switch": "Сменить язык", - "current": "Текущий язык", - "polish": "Polski", - "switch_to_polish": "Переключиться на польский", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Переключить на фарси", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Переключить на монгольский" - }, - "settings": { - "title": "Настройки", - "back_to_mail": "Вернуться к почте", - "save_success": "Настройки успешно сохранены", - "import_success": "Настройки успешно импортированы", - "import_error": "Не удалось импортировать настройки", - "reset_confirm": "Вы уверены, что хотите сбросить все настройки до значений по умолчанию?", - "unsaved_changes": "У вас есть несохранённые изменения", - "discard_changes": "Отменить несохранённые изменения?", - "discard": "Отменить", - "keep_editing": "Продолжить редактирование", - "tabs": { - "appearance": "Внешний вид", - "language": "Язык, регион и время", - "email": "Поведение почты", - "composer": "Редактор", - "privacy": "Конфиденциальность и безопасность", - "account": "Аккаунт", - "identities": "Удостоверения", - "vacation": "Автоответ на отпуск", - "advanced": "Дополнительно", - "calendar": "Календарь", - "filters": "Фильтры", - "templates": "Шаблоны", - "folders": "Папки", - "keywords": "Теги", - "security": "Безопасность", - "files": "Файлы", - "contacts": "Контакты", - "encryption": "Шифрование", - "protocol_handlers": "Приложения по умолчанию", - "sidebar_apps": "Приложения боковой панели", - "notifications": "Уведомления", - "layout": "Макет", - "reading": "Чтение", - "composing": "Написание", - "downloads": "Загрузки", - "content_senders": "Содержимое и отправители", - "about_data": "О программе и данные", - "debug": "Отладка" - }, - "tab_groups": { - "general": "Общие", - "account": "Аккаунт и удостоверение", - "organization": "Организация почты", - "apps": "Приложения", - "system": "Система", - "appearance": "Внешний вид", - "mail": "Почта", - "privacy": "Конфиденциальность и безопасность", - "advanced": "Дополнительно" - }, - "appearance": { - "message_list_order": { - "label": "Порядок списка сообщений", - "description": "Как упорядочены сообщения в списке. Сортировку выполняет сервер, поэтому она охватывает всю папку, а не только загруженные сообщения.", - "preset": { - "chronological": "Хронологически (сначала новые)", - "unread_first": "Сначала непрочитанные", - "starred_first": "Сначала помеченные", - "tagged_first": "Сначала с меткой", - "custom": "Свой порядок" - }, - "tag_label": "Метка", - "advanced_show": "Дополнительно: настроить уровни сортировки", - "advanced_hide": "Скрыть дополнительно", - "criterion_label": "Критерий уровня {n}", - "direction_label": "Направление уровня {n}", - "criteria": { - "unread": "Непрочитанные", - "starred": "Помеченные", - "tag": "Метка", - "receivedAt": "Дата получения", - "sentAt": "Дата отправки", - "from": "Отправитель", - "subject": "Тема", - "size": "Размер" - }, - "direction": { - "unread_first": "Сначала непрочитанные", - "read_first": "Сначала прочитанные", - "starred_first": "Сначала помеченные", - "unstarred_first": "Сначала непомеченные", - "tagged_first": "Сначала с меткой", - "untagged_first": "Сначала без метки", - "newest_first": "Сначала новые", - "oldest_first": "Сначала старые", - "a_to_z": "А → Я", - "z_to_a": "Я → А", - "largest_first": "Сначала большие", - "smallest_first": "Сначала маленькие" - }, - "add_level": "Добавить уровень", - "remove_level": "Удалить уровень", - "move_up": "Выше", - "move_down": "Ниже", - "tiebreak_note": "До трёх уровней; следующие уровни решают при равенстве. «Сначала новые» всегда применяется последним.", - "scope": { - "label": "Применять ко всем папкам", - "description": "Если выключено, порядок действует только для папки «Входящие»; остальные папки остаются хронологическими." - }, - "unsupported_note": "Ваш почтовый сервер не поддерживает сортировку по статусу прочтения, пометкам или меткам. Эти уровни игнорируются, и список остаётся хронологическим." - }, - "title": "Внешний вид", - "description": "Настройте внешний вид веб-почты", - "theme": { - "label": "Тема", - "description": "Выберите предпочтительную цветовую схему", - "light": "Светлая", - "dark": "Тёмная", - "system": "Системная" - }, - "language": { - "label": "Язык", - "description": "Выберите предпочтительный язык" - }, - "font_size": { - "label": "Размер шрифта", - "description": "Регулируйте размер текста для лучшей читаемости", - "small": "Малый", - "medium": "Средний", - "large": "Большой" - }, - "list_density": { - "label": "Плотность", - "description": "Управляйте расстояниями и отступами в интерфейсе", - "extra_compact": "Максимально компактный", - "compact": "Компактный", - "regular": "Обычный", - "comfortable": "Просторный" - }, - "animations": { - "label": "Включить анимации", - "description": "Показывать плавные переходы и эффекты" - }, - "toolbar_position": { - "label": "Положение панели инструментов", - "description": "Где отображать кнопки действий письма (Ответить, Архивировать, Удалить и др.)", - "top": "Сверху", - "below_subject": "Под темой" - }, - "toolbar_labels": { - "label": "Показывать подписи панели инструментов", - "description": "Отображать текстовые подписи рядом с иконками панели. Отключите для экономии места, когда освоитесь с иконками." - }, - "hide_account_switcher": { - "label": "Скрыть переключатель аккаунтов в боковой панели", - "description": "Скрыть выбор аккаунта в верхней части боковой панели папок. Вы по-прежнему можете переключать аккаунты через нижнюю панель навигации." - }, - "show_rail_account_list": { - "label": "Показать аватары аккаунтов на панели навигации", - "description": "Отображать отдельные круги аккаунтов в нижней части панели навигации для быстрого переключения, с кнопкой выхода ниже." - }, - "unified_mailbox": { - "label": "Общий почтовый ящик", - "description": "Показывать объединённые папки (Входящие, Отправленные и др.) для всех подключённых аккаунтов", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Включать групповые ящики", - "description": "Также объединять общие/групповые ящики в едином представлении." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Цветные значки боковой панели", - "description": "Окрашивать значки папок и тегов по типу (синий «Входящие», красный «Спам», зелёный «Отправленные» и т. д.). Отключите для монохромной боковой панели." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Показывать общее количество сообщений", - "description": "Показывает общее количество сообщений рядом с папками и метками, наряду с количеством непрочитанных. Отключите, чтобы показывать только непрочитанные." - }, - "favicon_unread_badge": { - "label": "Счётчик непрочитанных на иконке вкладки и приложения", - "description": "Показывает количество непрочитанных писем значком на иконке вкладки браузера и, если поддерживается, на иконке установленного приложения — чтобы новая почта была видна без переключения на вкладку. Отключите, чтобы обе иконки оставались без значка." - }, - "pro_interface": { - "label": "Pro-интерфейс (экспериментальный)", - "description": "Макет для опытных пользователей только для настольных устройств с просмотром сообщений в нескольких вкладках и работой между аккаунтами. Стандартный интерфейс не меняется; вы можете вернуться в любое время.", - "open_label": "Открыть Pro-интерфейс", - "back_to_standard": "К стандартному" - }, - "cross_unread": { - "label": "Все аккаунты: Непрочитанные", - "description": "Показывает в разделе «Все аккаунты» запись с непрочитанными письмами из всех аккаунтов, по всем папкам, кроме спама, отправленных, архива, корзины и черновиков." - }, - "cross_starred": { - "label": "Все аккаунты: Помеченные", - "description": "Показывает в разделе «Все аккаунты» запись с помеченными письмами из всех аккаунтов, по всем папкам, кроме спама, отправленных, архива, корзины и черновиков." - }, - "cross_all": { - "label": "Все аккаунты: Вся почта", - "description": "Показывает в разделе «Все аккаунты» запись со всеми письмами из всех аккаунтов, по всем папкам, кроме спама, отправленных, архива, корзины и черновиков." - } - }, - "keywords": { - "title": "Теги электронной почты", - "description": "Определите теги для организации электронных писем с помощью цветов. Они хранятся как ключевые слова JMAP на сервере.", - "add_keyword": "Добавить тег", - "label_field": "Отображаемое название", - "label_placeholder": "напр., Работа, Личное, Срочно", - "id_field": "Идентификатор тега", - "id_placeholder": "напр., work, personal", - "color_field": "Цвет", - "id_exists": "Этот идентификатор тега уже существует", - "edit": "Редактировать тег", - "delete": "Удалить тег", - "save": "Сохранить", - "add": "Добавить", - "cancel": "Отмена", - "migrating": "Обновление тега в существующих письмах…", - "migration_error": "Не удалось обновить тег в существующих письмах", - "nesting": { - "label": "Вложенные теги", - "description": "Вкладывайте теги в другие теги и показывайте их в боковой панели в виде дерева." - }, - "parent_field": "Родительский тег", - "no_parent": "Без родительского тега", - "too_long": "Этот путь тега слишком длинный (не более {max} символов)", - "has_children_locked": "В этот тег вложены другие теги, поэтому его имя и родительский тег заблокированы. Сначала переместите или удалите их.", - "has_children_delete": "Сначала удалите теги, вложенные в этот", - "visibility_field": "Видимость в боковой панели", - "visibility": { - "show": "Показывать", - "unread": "Показывать при непрочитанных", - "hide": "Скрывать" - }, - "unrecognized": { - "title": "Восстановить теги с сервера", - "description": "Названия и цвета тегов хранятся в этом браузере, но сами теги находятся в ваших письмах в виде ключевых слов JMAP. Просканируйте почту на ключевые слова, для которых здесь нет тега, и добавьте их обратно.", - "scan": "Найти теги", - "rescan": "Сканировать снова", - "scanning": "Сканирование… {scanned} из {total} писем", - "none": "Все теги в ваших письмах уже определены здесь.", - "messages": "{count, plural, one {# письмо} few {# письма} other {# писем}}", - "add_all": "Добавить все ({count})", - "partial": "Просканированы только {scanned} последних писем из {total}. Теги, встречающиеся только в более старых письмах, могут отсутствовать.", - "error": "Не удалось просканировать почту на наличие тегов." - } - }, - "notifications": { - "test_sound": "Проверить звук уведомления", - "sounds": { - "default": "По умолчанию (Сигнал)", - "cheerful": "Радостный", - "involved": "Вовлечённый", - "swift": "Быстрый жест", - "relax": "Релакс" - }, - "sound_selection": { - "title": "Звук уведомления", - "description": "Выберите звук для уведомлений", - "choose": "Звук", - "choose_desc": "Выберите звук уведомления и нажмите значок динамика для предпросмотра" - }, - "email": { - "title": "Уведомления о почте", - "description": "Настройте уведомления о входящих письмах", - "enabled": "Уведомления о почте", - "enabled_desc": "Показывать уведомления при получении новых писем", - "sound": "Звук уведомления", - "sound_desc": "Воспроизводить звуковой сигнал при получении новых писем", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Уведомления календаря", - "description": "Настройте уведомления о событиях календаря", - "enabled": "Уведомления о событиях", - "enabled_desc": "Показывать оповещения о предстоящих событиях календаря", - "sound": "Звук уведомления", - "sound_desc": "Воспроизводить звуковой сигнал для напоминаний календаря", - "invitation_parsing": "Распознавать приглашения по почте", - "invitation_parsing_desc": "Обнаруживать приглашения календаря во вложениях писем и показывать действия календаря" - }, - "push": { - "confirm_disable_message": "Это устройство перестанет получать оповещения, когда сайт закрыт.", - "confirm_disable_title": "Отключить фоновые уведомления?", - "description": "Получайте системные уведомления о новых письмах, когда этот сайт закрыт. Доставляется через push-релей Bulwark; релей никогда не видит содержимое писем.", - "disable": "Отключить", - "enable": "Включить", - "dismiss_aria": "Закрыть запрос на уведомления", - "ios_hint": "В iOS сначала установите сайт на главный экран – Safari доставляет Web Push только установленным PWA.", - "reenable": "Перерегистрировать", - "relay_desc": "По умолчанию используется размещённый релей Bulwark. Меняйте только если хостите самостоятельно.", - "relay_label": "Push-релей", - "relay_locked": "Установлено администратором", - "relay_locked_desc": "URL push-релея установлен вашим администратором и не может быть изменён.", - "status_active": "Активно на этом устройстве", - "status_busy": "Выполняется…", - "status_inactive": "Не включено на этом устройстве", - "status_unsupported": "Этот браузер не поддерживает Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Фоновые уведомления", - "devices_title": "Зарегистрированные устройства", - "devices_desc": "Все устройства, зарегистрированные для получения фоновых уведомлений этой учётной записи. После отзыва устройство сразу перестаёт их получать.", - "devices_loading": "Загрузка зарегистрированных устройств…", - "devices_empty": "Для фоновых уведомлений не зарегистрировано ни одного устройства.", - "devices_error": "Не удалось загрузить зарегистрированные устройства.", - "devices_refresh": "Обновить", - "device_this": "Это устройство", - "device_other": "Другое устройство", - "device_status_active": "Доставляет уведомления", - "device_status_inactive": "Ни разу не доставляло уведомлений", - "device_status_unknown": "Неизвестно этому ретранслятору", - "device_expires": "Истекает {date}", - "revoke": "Отозвать", - "confirm_revoke_title": "Отозвать это устройство?", - "confirm_revoke_message": "Оно сразу перестанет получать фоновые уведомления. Если снова включить push на этом устройстве, оно зарегистрируется заново.", - "confirm_revoke_message_this": "Это устройство сразу перестанет получать фоновые уведомления." - } - }, - "language_region": { - "title": "Язык, регион и время", - "description": "Язык, формат даты, формат времени и другие региональные настройки", - "language": { - "label": "Язык", - "description": "Выберите предпочтительный язык", - "english": "Английский", - "french": "Французский" - }, - "date_format": { - "label": "Формат даты", - "description": "Как отображаются даты в списке писем", - "smart": "Умный (по региону)", - "relative": "Относительный (1 ч назад, 2 д назад)", - "full": "Всегда полная дата", - "preview_today": "Сегодня:", - "preview_this_week": "На этой неделе:", - "preview_older": "Старее:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Формат времени", - "description": "Выберите формат 12- или 24-часовой нотации", - "12h": "12-часовой", - "24h": "24-часовой" - }, - "first_day": { - "label": "Первый день недели", - "description": "Начинать неделю с воскресенья или понедельника", - "sunday": "Воскресенье", - "monday": "Понедельник" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Очищать поиск при переходе между папками", - "description": "Щелчок по папке («Входящие», «Отправленные»…) очищает активный поиск и открывает эту папку. Если выключено, поиск сохраняется и выполняется заново в выбранной папке." - }, - "request_read_receipt": { - "label": "Запрашивать уведомления о прочтении по умолчанию", - "description": "Заранее включать запрос уведомления о прочтении при создании нового сообщения." - }, - "read_receipt_response": { - "label": "Отвечать на запросы уведомления о прочтении", - "description": "Что делать, когда входящее сообщение запрашивает уведомление о прочтении.", - "ask": "Спрашивать каждый раз", - "always": "Всегда отправлять", - "never": "Никогда не отправлять" - }, - "title": "Поведение почты", - "description": "Настройте обработку писем", - "mark_read": { - "label": "Отметить прочитанным", - "description": "Когда отмечать письма прочитанными при открытии", - "instant": "Немедленно", - "delay_3s": "Через 3 секунды", - "delay_5s": "Через 5 секунд", - "never": "Никогда" - }, - "delete_action": { - "label": "Действие при удалении", - "description": "Что происходит при удалении письма", - "trash": "Переместить в корзину", - "trash_and_read": "Переместить в корзину и пометить как прочитанное", - "permanent": "Удалить навсегда", - "warning": "Письма будут удалены навсегда и не могут быть восстановлены. Это действие необратимо." - }, - "message_spacing": { - "label": "Отступы сообщения", - "description": "Отступы вокруг текста сообщения при чтении", - "auto": "Автоматически (рекомендуется)", - "always": "Всегда добавлять отступы", - "edge": "От края до края" - }, - "plain_text_font": { - "label": "Шрифт обычного текста", - "description": "Шрифт для писем в обычном тексте", - "mono": "Моноширинный", - "sans": "Обычный (как в приложении)" - }, - "archive_mode": { - "label": "Архивировать в", - "description": "Как организовывать письма при архивировании", - "single": "Одну папку", - "year": "Папку на каждый год", - "month": "Папку на каждый месяц", - "reorganize": "Реорганизовать существующий архив", - "reorganize_success": "{count, plural, =0 {Нет писем для реорганизации} =1 {1 письмо реорганизовано} other {# писем реорганизовано}}", - "reorganize_error": "Не удалось реорганизовать архив" - }, - "permanently_delete_junk": { - "label": "Удалять спам навсегда", - "description": "Удалять письма из папки «Спам» навсегда вместо перемещения в корзину" - }, - "mail_layout": { - "label": "Макет почты", - "description": "Выберите между классической разделённой панелью чтения и сфокусированным потоком чтения в стиле Gmail.", - "split": "Разделённая панель", - "split_description": "Список сообщений и панель чтения отображаются рядом друг с другом.", - "focus": "Сфокусированный список", - "focus_description": "Показывать одну строку на сообщение и открывать письма на всю ширину, сохраняя видимой боковую панель папок.", - "horizontal": "Область чтения снизу", - "horizontal_description": "Показывать список сообщений сверху и открывать выбранное сообщение в области чтения под ним." - }, - "disable_threading": { - "label": "Отключить группировку по беседам", - "description": "Отображать письма как отдельные сообщения, а не сгруппированные по беседам" - }, - "plain_text_mode": { - "label": "Только простой текст", - "description": "Отключить редактор форматированного текста и отправлять все письма только в виде простого текста, включая ответы и пересылки" - }, - "auto_select_reply_identity": { - "label": "Автоматически выбирать адрес для ответа", - "description": "При ответе автоматически переключать адрес отправителя на ту учетную запись, которая получила исходное сообщение" - }, - "reply_identity_match": { - "label": "Сопоставление адреса получения", - "description": "Какие адреса получения считать вашими. «Только точный адрес» выбирает исключительно одну из настроенных личностей; «тот же домен» также считает любой другой адрес на доменах ваших личностей catch-all-псевдонимом и подставляет его в заголовок «От». Выберите точный адрес, если такие адреса являются списками рассылки.", - "exact": "Только точный адрес", - "domain": "Любой адрес на моих доменах" - }, - "signature_position": { - "label": "Положение подписи", - "description": "Куда вставлять подпись в ответах и пересылке. Над цитируемым текстом она читается естественно как завершение ответа; под ним сохраняет целостность исходного сообщения.", - "above_quote": "Перед цитируемым текстом", - "below_quote": "После цитируемого текста" - }, - "signature_separator": { - "label": "Разделитель подписи", - "description": "Добавлять перед подписью стандартную строку-разделитель \"-- \" (RFC 3676). Отключите, если хотите переходить от текста сразу к подписи." - }, - "sub_address_delimiter": { - "label": "Разделитель суб-адресов", - "description": "Символ, отделяющий имя пользователя от тега суб-адреса. Используйте разделитель, настроенный на вашем почтовом сервере (например, user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "Свой…", - "custom_input_label": "Свой символ-разделитель" - }, - "show_preview": { - "label": "Показывать текст предпросмотра", - "description": "Отображать предпросмотр письма в списке", - "focus_description": "Отображать встроенный текст предпросмотра в сфокусированном однострочном списке сообщений" - }, - "attachment_click_action": { - "label": "Действие при нажатии на вложение", - "description": "Выберите, предпросмотр или немедленная загрузка при нажатии на вложение", - "preview": "Предпросмотр, если возможно", - "download": "Скачать немедленно" - }, - "attachment_position": { - "label": "Расположение вложений", - "description": "Где отображать вложения в заголовке письма", - "beside-sender": "Рядом с отправителем", - "below-header": "Под заголовком" - }, - "emails_per_page": { - "10": "10 писем", - "25": "25 писем", - "50": "50 писем", - "100": "100 писем", - "label": "Писем на странице", - "description": "Количество писем для загрузки за раз" - }, - "always_light_mode": { - "label": "Всегда показывать письма в светлом режиме", - "description": "Отображать содержимое писем в светлом режиме даже когда приложение в тёмном режиме, избегая проблем преобразования" - }, - "external_content": { - "label": "Внешнее содержимое", - "description": "Как обрабатывать изображения и внешнее содержимое", - "ask": "Всегда спрашивать", - "block": "Всегда блокировать", - "allow": "Всегда разрешать" - }, - "trusted_senders": { - "label": "Доверенные отправители", - "description": "Управляйте отправителями, чьи изображения загружаются автоматически", - "count_zero": "Нет", - "count_one": "1 отправитель", - "count_other": "{count} отправителей", - "modal_title": "Доверенные отправители", - "empty_title": "Нет доверенных отправителей", - "empty_description": "При просмотре письма с заблокированными изображениями нажмите «Всегда доверять этому отправителю», чтобы добавить его сюда.", - "add_manually": "Добавить отправителя вручную", - "add_button": "Добавить", - "add_placeholder": "Введите email-адрес", - "search_placeholder": "Поиск отправителей...", - "no_results": "Отправители не найдены", - "remove": "Удалить", - "close": "Закрыть", - "invalid_email": "Введите корректный email-адрес", - "already_added": "Этот отправитель уже является доверенным", - "save_error": "Не удалось сохранить - проверьте журнал отладки Контактов для подробностей", - "use_address_book_label": "Синхронизировать с адресной книгой", - "use_address_book_description": "Сохранять доверенных отправителей в отдельной адресной книге \"Доверенные отправители\", чтобы синхронизировать их на всех устройствах" - }, - "hover_actions": { - "label": "Быстрые действия при наведении", - "description": "Выберите быстрые действия, отображаемые при наведении на письмо в списке", - "delete": "Удалить", - "star": "Пометить / Снять пометку", - "mark_read": "Прочитано / Непрочитано", - "archive": "Архивировать", - "tag": "Тег", - "spam": "Отметить как спам", - "not_spam": "Не спам", - "none_selected": "Действия не выбраны", - "mode_label": "Режим отображения", - "mode_inline": "Встроенный", - "mode_floating": "Плавающий", - "corner_label": "Позиция плавающих действий", - "corner_top-left": "Вверху слева", - "corner_top-right": "Вверху справа", - "corner_bottom-left": "Внизу слева", - "corner_bottom-right": "Внизу справа" - }, - "default_mail_program": { - "label": "Почтовая программа по умолчанию", - "description": "Зарегистрировать {appName} как почтовую программу по умолчанию для ссылок mailto:", - "button": "Установить по умолчанию", - "success": "Браузер запрошен для установки по умолчанию", - "error": "Ваш браузер не поддерживает эту функцию" - }, - "attachment_reminder": { - "label": "Напоминание о вложении", - "description": "Предупреждать перед отправкой, если в сообщении упоминаются вложения, но ничего не прикреплено", - "keywords_label": "Ключевые слова-триггеры", - "keywords_description": "Слова или фразы, при обнаружении которых в сообщении появляется напоминание", - "add_placeholder": "Добавить ключевое слово...", - "add": "Add", - "remove": "Удалить" - }, - "empty_subject_warning": { - "label": "Предупреждение о пустой теме", - "description": "Запрашивать подтверждение перед отправкой письма без темы." - }, - "hide_inline_image_attachments": { - "label": "Скрывать встроенные изображения из вложений", - "description": "Изображения, встроенные в тело сообщения, не отображаются как отдельные вложения" - }, - "attachment_image_previews": { - "label": "Показывать миниатюры изображений во вложениях", - "description": "Отображать вложенные изображения в виде миниатюр вместо обычных значков файлов" - }, - "send_delay": { - "label": "Отмена отправки / задержка отправки", - "description": "Задерживает обычную отправку на короткий серверный интервал.", - "off": "Выкл.", - "seconds": "{seconds} сек.", - "unsupported": "Текущая учетная запись не заявляет поддержку отложенной отправки. Настройка сохранится для других учетных записей." - }, - "return_to_list_after_action": { - "label": "Возврат к списку после удаления или отметки как непрочитанного", - "description": "После удаления или отметки открытого письма как непрочитанного возвращаться к списку писем вместо открытия следующего." - }, - "swipe_right_action": { - "label": "Действие при свайпе вправо (мобильные)", - "description": "На сенсорных устройствах — действие, выполняемое при свайпе сообщения вправо в списке." - }, - "swipe_left_action": { - "label": "Действие при свайпе влево (мобильные)", - "description": "На сенсорных устройствах — действие, выполняемое при свайпе сообщения влево в списке." - }, - "swipe_actions": { - "none": "Нет", - "archive": "Архивировать", - "delete": "Удалить", - "mark_read": "Переключить прочитано", - "star": "Переключить пометку", - "spam": "Отметить как спам" - }, - "rtl_editing": { - "label": "Поддержка редактирования справа налево", - "description": "Добавляет кнопку направления текста на панель инструментов редактора, позволяя задавать абзацам направление слева направо или справа налево" - } - }, - "composer": { - "title": "Редактор", - "description": "Настройте параметры написания писем", - "autosave": { - "label": "Интервал автосохранения", - "description": "Как часто автоматически сохранять черновики", - "30s": "Каждые 30 секунд", - "1m": "Каждую минуту", - "2m": "Каждые 2 минуты", - "5m": "Каждые 5 минут" - }, - "send_confirmation": { - "label": "Подтверждение отправки", - "description": "Запрашивать подтверждение перед отправкой писем" - }, - "default_reply": { - "label": "Режим ответа по умолчанию", - "description": "Действие по умолчанию при нажатии «Ответить»", - "reply": "Ответить", - "reply_all": "Ответить всем" - } - }, - "privacy": { - "title": "Конфиденциальность и безопасность", - "description": "Управляйте настройками конфиденциальности и безопасности", - "external_images": { - "label": "Блокировать внешние изображения", - "description": "Предотвращать отслеживание через внешние изображения" - }, - "session_timeout": { - "label": "Тайм-аут сессии", - "description": "Автоматически выходить после бездействия", - "never": "Никогда", - "30m": "30 минут", - "1h": "1 час", - "4h": "4 часа" - }, - "clear_cache": { - "label": "Очистить кэш", - "description": "Удалить кэшированные данные и временные файлы", - "button": "Очистить кэш", - "confirm": "Вы уверены, что хотите очистить кэш?", - "success": "Кэш успешно очищен" - } - }, - "account": { - "title": "Аккаунт", - "description": "Просмотрите данные своего аккаунта", - "name_label": "Отображаемое имя", - "username_label": "Имя пользователя", - "account_type_label": "Тип аккаунта", - "auth_method_label": "Аутентификация", - "auth_method_oauth": "Единый вход (OAuth/OIDC)", - "auth_method_basic": "Пароль", - "demo_account": "Демо-аккаунт", - "email": { - "label": "Email-адрес", - "value": "{email}" - }, - "server": { - "label": "JMAP-сервер", - "value": "{server}" - }, - "storage": { - "label": "Использование хранилища", - "used": "{used} из {total} использовано", - "percentage": "{percent}% использовано" - }, - "last_sync": { - "label": "Последняя синхронизация", - "value": "{time}" - }, - "accounts": { - "title": "Подключённые учётные записи", - "description": "Перетащите, чтобы изменить порядок отображения учётных записей в меню", - "active": "Текущая активная учётная запись", - "default_badge": "По умолчанию", - "set_default": "Сделать основной", - "switch_to": "Переключиться на эту учётную запись", - "move_up": "Переместить вверх", - "move_down": "Переместить вниз", - "drag_handle": "Перетащите, чтобы изменить порядок", - "add": "Добавить учётную запись" - }, - "shared_accounts": { - "title": "Доступные мне", - "description": "Групповые и общие учётные записи, которыми вы можете управлять. Выберите одну, чтобы изменить её фильтры, автоответчик, календари и контакты.", - "shared_label": "Общая учётная запись" - } - }, - "security": { - "title": "Безопасность аккаунта", - "description": "Управляйте паролем, двухфакторной аутентификацией и настройками безопасности", - "detecting": "Определение возможностей сервера...", - "not_available": "Управление безопасностью аккаунта недоступно для этого почтового сервера. Требуемые разрешения могут быть отключены. Подробнее см. в документации.", - "password": { - "title": "Изменить пароль", - "current": "Текущий пароль", - "new": "Новый пароль", - "confirm": "Подтвердите новый пароль", - "submit": "Изменить пароль", - "success": "Пароль успешно изменён", - "error_title": "Не удалось изменить пароль", - "error_mismatch": "Новые пароли не совпадают", - "error_min_length": "Пароль должен содержать не менее 8 символов", - "error_generic": "Не удалось изменить пароль" - }, - "display_name": { - "label": "Отображаемое имя", - "description": "Ваше имя, отображаемое на сервере", - "placeholder": "Введите ваше отображаемое имя", - "save": "Сохранить", - "success": "Отображаемое имя обновлено", - "error": "Не удалось обновить отображаемое имя" - }, - "totp": { - "section_title": "Двухфакторная аутентификация", - "label": "Аутентификация TOTP", - "description": "Добавьте дополнительный уровень безопасности с помощью одноразового пароля", - "active": "Включена", - "inactive": "Отключена", - "enabled": "Двухфакторная аутентификация включена", - "disabled": "Двухфакторная аутентификация отключена", - "enable_error": "Не удалось включить 2FA", - "disable_error": "Не удалось отключить 2FA", - "setup_instructions": "Скопируйте этот URL в приложение-аутентификатор (Google Authenticator, Authy и др.):", - "verification_code": "Код подтверждения", - "confirm": "Подтвердить", - "disable": "Отключить", - "disable_confirm_prompt": "Введите пароль, чтобы отключить двухфакторную аутентификацию.", - "password_required": "Требуется пароль", - "code_required": "Требуется код подтверждения", - "code_invalid": "Неверный код подтверждения. Проверьте приложение-аутентификатор и попробуйте снова." - }, - "app_passwords": { - "title": "Пароли приложений", - "description": "Создавайте пароли для приложений, не поддерживающих двухфакторную аутентификацию", - "add": "Добавить", - "create": "Создать", - "cancel": "Отмена", - "generate": "Сгенерировать", - "name_label": "Название приложения", - "name_placeholder": "напр., Thunderbird, iPhone Mail", - "password_label": "Пароль (оставьте пустым для автогенерации)", - "password_placeholder": "Автогенерация, если пусто", - "added": "Пароль приложения создан", - "removed": "Пароль приложения удалён", - "add_error": "Не удалось создать пароль приложения", - "remove_error": "Не удалось удалить пароль приложения", - "none": "Пароли приложений не настроены", - "done": "Готово", - "expires_label": "Срок действия (необязательно)", - "copy_now_warning": "Скопируйте этот пароль сейчас - он больше не будет показан.", - "allowed_ips_label": "Разрешённые IP (необязательно)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Через запятую или пробел. Оставьте пустым, чтобы разрешить любой IP." - }, - "api_keys": { - "title": "API-ключи", - "description": "Создавайте API-ключи для скриптов и интеграций, обращающихся к серверу напрямую", - "name_label": "Название ключа", - "name_placeholder": "напр. Скрипт резервного копирования, CI runner", - "copy_now_warning": "Скопируйте этот API-ключ сейчас – он больше не будет показан.", - "added": "API-ключ создан", - "removed": "API-ключ удалён", - "add_error": "Не удалось создать API-ключ", - "remove_error": "Не удалось удалить API-ключ", - "none": "API-ключи не настроены" - }, - "encryption": { - "section_title": "Шифрование хранилища", - "label": "Шифрование почты", - "description": "Шифровать хранимые письма на сервере для дополнительной конфиденциальности", - "active": "Шифрование {type} включено", - "inactive": "Отключено", - "enabled": "Шифрование хранилища включено", - "disabled_success": "Шифрование хранилища отключено", - "error": "Не удалось обновить настройки шифрования", - "algorithm_label": "Алгоритм шифрования", - "encrypt_on_append": "Шифровать новые письма при загрузке", - "allow_spam_training": "Разрешить обучение спам-фильтра до шифрования писем", - "enable_encryption": "Включить шифрование", - "enable_tooltip": "Включить шифрование с этим ключом", - "configure_title": "Включить шифрование с этим ключом", - "enable_button": "Включить", - "disable_tooltip": "Отключить шифрование", - "enabled_success": "Шифрование хранилища включено" - }, - "email_client": { - "title": "Настройка почтового клиента", - "description": "Используйте эти учётные данные для настройки вашего почтового клиента (Thunderbird, Apple Mail, Outlook и др.)", - "jmap_username_label": "Имя пользователя JMAP", - "copy": "Копировать", - "copied": "Скопировано", - "password_instructions": "Используйте указанное выше имя пользователя JMAP вместе с паролем приложения для входа в почтовый клиент. Создайте пароль приложения в разделе выше, если вы ещё этого не сделали." - }, - "link_device": { - "title": "Привязать мобильное приложение", - "description": "Войдите в мобильное приложение Bulwark Mail без ввода данных. Создайте здесь QR-код и отсканируйте его на экране входа в приложении.", - "generate": "Показать QR-код", - "regenerate": "Показать новый код", - "instructions": "Откройте приложение Bulwark Mail, нажмите \"Сканировать QR-код\" на экране входа и наведите камеру сюда.", - "expires_in": "Срок действия кода истечёт через {seconds} секунд. Его можно использовать только один раз.", - "expired": "Срок действия кода истёк.", - "generating": "Создание…", - "error": "Не удалось создать код привязки. Пожалуйста, попробуйте ещё раз." - }, - "public_keys": { - "name_label": "Название открытого ключа", - "name_placeholder": "напр., Мой ключ", - "key_label": "Открытый ключ (S/MIME или PGP в формате ASCII)", - "key_placeholder": "Вставьте сюда свой открытый ключ", - "none": "Открытые ключи не настроены" - } - }, - "identities": { - "title": "Удостоверения отправителя", - "description": "Управляйте email-адресами, с которых можно отправлять письма", - "identities_count": { - "label": "Ваши удостоверения", - "description": "Email-адреса, настроенные для отправки", - "count_zero": "Нет удостоверений", - "count_one": "1 удостоверение", - "count_other": "{count} удостоверений" - }, - "manage": "Управление удостоверениями", - "sub_addressing": { - "label": "Суб-адресация", - "description": "Используйте теги вида user+tag@domain.com для организации входящей почты", - "learn_more": "Подробнее" - } - }, - "vacation": { - "title": "Автоответ на отпуск", - "description": "Автоматически отвечать на входящие письма в ваше отсутствие", - "loading": "Загрузка настроек автоответа...", - "not_supported": "Ваш почтовый сервер не поддерживает автоответы на отпуск.", - "fetch_error": "Не удалось загрузить настройки автоответа. Повторите попытку.", - "status": { - "label": "Автоответ на отпуск", - "description": "Отправлять автоматический ответ на входящие письма", - "active": "Активен", - "inactive": "Неактивен" - }, - "date_range": { - "title": "Диапазон дат", - "description": "Ограничьте автоответ определённым периодом (необязательно)", - "start": "Дата начала", - "start_description": "Оставьте пустым для без ограничения", - "end": "Дата окончания", - "end_description": "Оставьте пустым для без ограничения" - }, - "message": { - "title": "Сообщение автоответа", - "description": "Сообщение, которое будет отправляться в качестве ответа", - "subject_label": "Тема", - "subject_description": "Тема автоответа", - "subject_placeholder": "Вне офиса", - "body_label": "Текст сообщения", - "body_description": "Содержимое сообщения в виде обычного текста", - "body_placeholder": "Спасибо за ваше письмо. Я сейчас вне офиса и отвечу, когда вернусь.", - "html_label": "Форматированное сообщение (HTML)", - "html_description": "Добавьте форматированную версию со ссылками и оформлением. Получатели, чей почтовый клиент не может её отобразить, увидят обычный текст выше.", - "html_placeholder": "Напишите форматированный ответ об отсутствии…" - }, - "preview": { - "title": "Предпросмотр", - "show": "Показать предпросмотр", - "hide": "Скрыть предпросмотр" - }, - "save": "Сохранить изменения", - "saving": "Сохранение...", - "warnings": { - "end_before_start": "Дата окончания должна быть позже даты начала", - "start_in_past": "Дата начала в прошлом", - "empty_body": "Текст сообщения пуст - получатели получат пустой ответ", - "subject_too_long": "Тема слишком длинная для сервера (не более {max} байт).", - "body_too_long": "Сообщение слишком длинное для сервера (не более {max} байт с учётом форматирования)." - } - }, - "folders": { - "role_memos": "Заметки", - "title": "Папки", - "description": "Управляйте папками почты и назначайте стандартные роли", - "folder_list": "Ваши папки", - "folder_list_description": "Нажмите на значок папки для настройки", - "standard_roles": "Стандартные роли папок", - "standard_roles_description": "Назначьте папки для стандартных ролей: Входящие, Отправленные, Корзина и др.", - "role_inbox": "Входящие", - "role_drafts": "Черновики", - "role_sent": "Отправленные", - "role_trash": "Корзина", - "role_junk": "Спам", - "role_archive": "Архив", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Нет", - "create_folder": "Создать папку", - "create_subfolder": "Создать подпапку", - "reorder": "Перетащите, чтобы изменить порядок", - "reorder_error": "Не удалось изменить порядок папок", - "move_error": "Не удалось переместить папку", - "subfolder_of": "Внутри {name}", - "subfolder_name": "Название подпапки", - "new_folder_name": "Название папки", - "rename": "Переименовать", - "change_icon": "Изменить значок", - "delete": "Удалить", - "confirm_delete": "Вы уверены, что хотите удалить \"{name}\"? Письма в этой папке будут перемещены в корзину.", - "create": "Создать", - "cancel": "Отмена", - "no_folders": "Нет пользовательских папок", - "cannot_delete_role": "Нельзя удалить папку со стандартной ролью. Сначала снимите роль.", - "folder_created": "Папка создана", - "folder_renamed": "Папка переименована", - "folder_deleted": "Папка удалена", - "role_updated": "Роль папки обновлена", - "error_create": "Не удалось создать папку", - "error_rename": "Не удалось переименовать папку", - "error_delete": "Не удалось удалить папку", - "error_delete_has_children": "Невозможно удалить папку: она содержит подпапки. Сначала удалите или переместите их.", - "error_delete_has_email": "Невозможно удалить папку: она содержит письма. Сначала переместите или удалите их.", - "error_role": "Не удалось обновить роль папки" - }, - "advanced": { - "title": "Дополнительно", - "description": "Расширенные параметры и настройки разработчика", - "debug_mode": { - "label": "Режим отладки", - "description": "Включить подробное журналирование для диагностики" - }, - "debug_categories": { - "description": "Выберите, какие категории журналировать. Отключите ненужные категории, чтобы уменьшить шум в консоли.", - "jmap": "Клиент JMAP", - "jmap_description": "Операции с почтовыми ящиками, получение почты и запросы протокола JMAP", - "calendar": "Календарь", - "calendar_description": "События календаря, импорт и сообщения планирования", - "tasks": "Задачи", - "tasks_description": "Создание, получение и обновление задач календаря", - "auth": "Аутентификация", - "auth_description": "Вход, TOTP, обмен токенами и управление сессией", - "filters": "Фильтры", - "filters_description": "Правила фильтрации Sieve и скрипты отпуска", - "email": "Просмотр почты", - "email_description": "Отрисовка писем, обработка TNEF и пометка как прочитано", - "push": "Push-уведомления", - "push_description": "Настройка и доставка push-уведомлений", - "contacts": "Контакты и адресные книги", - "contacts_description": "Синхронизация контактов, операции адресной книги и доверенные отправители" - }, - "settings_sync": { - "label": "Синхронизация настроек", - "description": "Синхронизировать настройки между браузерами и устройствами" - }, - "sender_favicons": { - "label": "Фавиконы отправителей", - "description": "Показывать иконки сайтов как аватары для корпоративных отправителей" - }, - "show_avatars_in_junk": { - "label": "Показывать аватары в папке «Спам»", - "description": "Показывать аватары и фавиконы отправителей в папке «Спам». По умолчанию отключено, чтобы фишинговые письма не выглядели правдоподобно." - }, - "keyboard_shortcuts": { - "label": "Сочетания клавиш", - "description": "Просмотреть доступные сочетания клавиш", - "button": "Показать сочетания" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Сбросить настройки", - "description": "Восстановить все настройки до значений по умолчанию", - "button": "Сбросить" - }, - "export_settings": { - "label": "Экспортировать настройки", - "description": "Скачать настройки в формате JSON", - "button": "Экспортировать" - }, - "import_settings": { - "label": "Импортировать настройки", - "description": "Загрузить настройки из файла JSON", - "button": "Импортировать" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Приложения боковой панели", - "description": "Управляйте пользовательскими приложениями и ссылками в боковой панели", - "keep_loaded": "Держать приложения загруженными", - "keep_loaded_description": "Держать встроенные приложения в фоне при переключении между ними, чтобы избежать перезагрузки", - "managed_title": "Предоставлено администратором", - "managed_description": "Эти приложения настроены для всех и не могут быть изменены.", - "manage_title": "Пользовательские приложения", - "manage_description": "Добавляйте, редактируйте и удаляйте пользовательские приложения из боковой панели" - }, - "contacts": { - "title": "Контакты", - "description": "Импортируйте и экспортируйте ваши контакты", - "import_label": "Импорт контактов", - "import_description": "Импортировать контакты из файла vCard (.vcf)", - "export_label": "Экспорт контактов", - "export_description": "Экспортировать все контакты в виде файла vCard (.vcf)", - "manage_title": "Адресные книги", - "manage_description": "Переименуйте ваши адресные книги", - "no_address_books": "Адресные книги не найдены", - "categories_title": "Категории", - "categories_description": "Переименование категорий контактов", - "no_categories": "Категории не найдены", - "group_by_letter_description": "Показывать алфавитные заголовки разделов в списке контактов", - "group_by_letter_label": "Группировать по первой букве", - "sort_by_last_name_label": "Сортировать по фамилии", - "sort_by_last_name_description": "Упорядочить список контактов по фамилии, чтобы члены семьи шли подряд" - }, - "downloads": { - "title": "Загрузки", - "description": "Настройте, как именуются загруженные письма и вложения.", - "reset": "Восстановить по умолчанию", - "preview": "Предпросмотр:", - "email_template": { - "label": "Имя файла письма (.eml)", - "description": "Шаблон, используемый при экспорте письма или при перетаскивании его в файловую систему. Расширение .eml добавляется автоматически." - }, - "attachment_template": { - "label": "Имя файла вложения", - "description": "Шаблон, используемый при загрузке или перетаскивании вложения. Если опустить '{filename}' и '{ext}', исходное расширение сохраняется." - }, - "bundle_template": { - "label": "Имя файла .zip с несколькими письмами", - "description": "Шаблон, используемый при перетаскивании или загрузке нескольких выбранных писем одним .zip-архивом. Расширение .zip добавляется автоматически." - }, - "spaces": { - "label": "Пробелы", - "description": "Заменять пробелы в итоговом имени файла другим символом.", - "keep": "Сохранять пробелы", - "underscore": "Заменять на _", - "dash": "Заменять на -" - }, - "lowercase": { - "label": "Нижний регистр", - "description": "Принудительно перевести всё имя файла в нижний регистр." - }, - "strip_diacritics": { - "label": "Удалять диакритику", - "description": "Преобразовать буквы с акцентами в их ASCII-эквиваленты (ä → a, é → e). Полезно для инструментов, плохо работающих с Unicode-именами." - }, - "collapse_separators": { - "label": "Сжимать повторяющиеся разделители", - "description": "Сжимать последовательности пробелов, подчёркиваний или дефисов до одного символа." - }, - "after_export": { - "label": "После экспорта", - "description": "При необходимости переместить письмо после экспорта в .eml.", - "keep": "Оставить в ящике", - "archive": "Переместить в архив", - "trash": "Переместить в корзину" - } - }, - "filters": { - "title": "Фильтры почты", - "description": "Создавайте правила для автоматической сортировки, маркировки и управления входящими письмами", - "add_rule": "Добавить правило", - "no_rules": "Нет правил фильтрации", - "vacation_active": "Автоответ отпуска активен", - "vacation_active_description": "Автоматический ответ включён для входящих сообщений", - "vacation_configure": "Настроить", - "no_rules_description": "Создайте правила для автоматической организации входящей почты", - "edit_rule": "Редактировать правило", - "new_rule": "Новое правило", - "delete_rule": "Удалить правило", - "delete_confirm": "Вы уверены, что хотите удалить это правило?", - "enable": "Включить", - "disable": "Отключить", - "raw_editor": "Редактор Sieve", - "raw_editor_warning": "Редактирование Sieve-скрипта может нарушить визуальное редактирование правил. Изменения здесь переопределяют визуальный конструктор.", - "validate": "Проверить", - "validation_success": "Скрипт корректен", - "validation_error": "Скрипт содержит ошибки", - "save": "Сохранить правила", - "saving": "Сохранение...", - "saved": "Фильтры успешно сохранены", - "save_failed": "Не удалось сохранить фильтры", - "loading": "Загрузка фильтров...", - "not_supported": "Ваш почтовый сервер не поддерживает фильтры.", - "rule_name": "Название правила", - "rule_name_placeholder": "напр., Сортировка рассылок", - "match_all": "Совпадают ВСЕ условия", - "match_any": "Совпадает ЛЮБОЕ условие", - "conditions": "Условия", - "add_condition": "Добавить условие", - "actions": "Действия", - "add_action": "Добавить действие", - "stop_processing": "Прекратить обработку последующих правил", - "include_spam": "Перемещать также письма, помеченные как спам", - "attachment_type_placeholder": "напр. pdf, doc, jpg", - "value_placeholder_multi": "Значение (несколько через запятую)", - "condition_fields": { - "attachment": "Вложение", - "from": "От", - "to": "Кому", - "cc": "Копия", - "subject": "Тема", - "header": "Пользовательский заголовок", - "size": "Размер", - "body": "Тело" - }, - "comparators": { - "has_any": "присутствует", - "has_type": "типа", - "contains": "содержит", - "not_contains": "не содержит", - "is": "точно совпадает", - "not_is": "не равно", - "starts_with": "начинается с", - "ends_with": "заканчивается на", - "matches": "соответствует шаблону", - "greater_than": "больше чем", - "less_than": "меньше чем" - }, - "action_types": { - "move": "Переместить в папку", - "copy": "Копировать в папку", - "forward": "Переслать на", - "mark_read": "Отметить прочитанным", - "star": "Пометить сообщение", - "add_label": "Добавить тег", - "discard": "Удалить (без уведомления)", - "reject": "Отклонить с сообщением", - "keep": "Оставить во входящих", - "stop": "Прекратить обработку" - }, - "move_to_folder": "Выбрать папку", - "copy_to_folder": "Выбрать папку", - "forward_to": "Переслать на адрес электронной почты", - "forward_placeholder": "user@пример.рф", - "forward_keep_copy": "Сохранить копию", - "forward_limit": "Лимит пересылок на одно письмо на этом сервере: {count}. Лишние пересылки пропускаются.", - "reject_message": "Сообщение об отклонении", - "reject_placeholder": "Ваше письмо было отклонено", - "label_name": "Название тега", - "label_placeholder": "Выбрать тег", - "header_name": "Имя заголовка", - "header_placeholder": "напр., X-Mailing-List", - "size_bytes": "Размер в байтах", - "size_placeholder": "напр., 1000000", - "system_managed": "Правило, управляемое системой", - "opaque_warning": "Этот скрипт был отредактирован вне визуального конструктора. Доступно только редактирование необработанного Sieve.", - "open_sieve_editor": "Открыть редактор Sieve", - "fetch_error": "Не удалось загрузить фильтры", - "expanded_view": "Расширенный вид", - "expanded_view_description": "Показывать правила фильтрации с подробными блоками условий и действий", - "if": "Если", - "then": "То", - "match_all_conditions": "все совпадают", - "match_any_condition": "любое совпадает", - "and": "и", - "or": "или", - "cancel": "Отмена", - "confirm_delete": "Удалить", - "rule_list": "Правила фильтрации", - "drag_to_reorder": "Перетащите для изменения порядка", - "match_type": "Тип совпадения", - "reset_to_visual": "Сбросить до визуального конструктора", - "reset_warning": "Это удалит текущий скрипт и начнёт заново.", - "confirm_reset": "Сбросить", - "validation_empty_name": "Требуется название правила", - "validation_empty_conditions": "Требуется хотя бы одно условие со значением", - "validation_empty_actions": "Требуется хотя бы одно действие", - "templates_section": "Начать с шаблона", - "template_newsletters": "Перемещать рассылки в папку", - "template_receipts": "Автоархивировать квитанции", - "template_important": "Помечать важные письма", - "template_notifications": "Фильтровать уведомления", - "sieve_editor": { - "title": "Редактор Sieve-скрипта", - "warning": "Редактирование Sieve-скрипта может нарушить визуальное редактирование правил. Изменения здесь переопределяют визуальный конструктор.", - "script_content": "Sieve-скрипт", - "valid": "Скрипт корректен", - "invalid": "Скрипт содержит ошибки", - "save_warning": "Сохранение перезапишет визуальные правила. Это нельзя отменить. Нажмите «Сохранить» ещё раз для подтверждения.", - "validating": "Проверка...", - "validate": "Проверить", - "cancel": "Отмена", - "save": "Сохранить", - "confirm_save": "Подтвердить сохранение", - "validation_failed": "Ошибка запроса проверки" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# условие} other {# условий}}", - "actions_count": "{count, plural, one {# действие} other {# действий}}" - }, - "origin_external": "Внешнее", - "managed_by_tooltip": "Управляется {source}. Редактируйте в этом приложении или используйте редактор Sieve." - }, - "templates": { - "title": "Шаблоны писем", - "description": "Создавайте многоразовые шаблоны писем с переменными-заполнителями", - "add": "Новый шаблон", - "edit": "Редактировать шаблон", - "name": "Название шаблона", - "name_placeholder": "напр., Письмо-напоминание", - "category": "Категория", - "category_placeholder": "напр., Работа, Личное", - "subject": "Тема", - "subject_placeholder": "Тема письма", - "body": "Текст", - "body_placeholder": "Текст письма...", - "recipients_placeholder": "user@пример.рф", - "identity": "Отправить как", - "default_identity": "Удостоверение по умолчанию", - "favorite": "Избранное", - "cancel": "Отмена", - "create": "Создать", - "update": "Обновить", - "confirm_delete": "Удалить", - "no_templates": "Шаблоны отсутствуют", - "manage": "Управление шаблонами", - "count": "{count, plural, one {# шаблон} other {# шаблонов}}", - "export_import": "Экспорт и импорт", - "export_import_description": "Резервная копия шаблонов или перенос на другое устройство", - "export": "Экспортировать", - "import": "Импортировать", - "validation": { - "empty": "Название шаблона обязательно", - "too_long": "Название шаблона не должно превышать 200 символов" - } - }, - "files": { - "display": { - "title": "Отображение", - "description": "Настройте отображение файлов и папок" - }, - "folder_layout": { - "label": "Навигация по папкам", - "description": "Выберите, как отображать папки: встроенно с файлами или в боковом дереве", - "inline": "Встроенно", - "sidebar": "Боковая панель" - }, - "default_view": { - "label": "Вид по умолчанию", - "description": "Выберите между сеткой и списком", - "list": "Список", - "grid": "Сетка" - }, - "default_sort": { - "label": "Сортировка по умолчанию", - "description": "Выберите способ сортировки файлов по умолчанию", - "name": "Имя", - "size": "Размер", - "modified": "Дата изменения" - }, - "sort_direction": { - "label": "Направление сортировки", - "description": "Выберите порядок: по возрастанию или убыванию", - "ascending": "По возрастанию", - "descending": "По убыванию" - }, - "icons": { - "title": "Иконки", - "description": "Настройте внешний вид иконок файлов" - }, - "show_icons": { - "label": "Показывать иконки файлов", - "description": "Отображать иконки рядом с файлами и папками" - }, - "colored_icons": { - "label": "Цветные иконки", - "description": "Использовать цветные иконки вместо монохромных" - }, - "show_thumbnails": { - "label": "Показывать миниатюры", - "description": "Отображать предпросмотр изображений вместо иконок для изображений" - }, - "behavior": { - "title": "Поведение", - "description": "Настройте поведение файлового менеджера" - }, - "show_hidden": { - "label": "Показывать скрытые файлы", - "description": "Отображать файлы и папки, начинающиеся с точки" - }, - "preview": { - "label": "Предпросмотр" - } - }, - "search_placeholder": "Поиск настроек", - "search_clear": "Очистить поиск", - "search_no_results": "Нет подходящих настроек", - "scoped": { - "back": "Назад к моей учётной записи", - "managing": "Управление: {name}" - } - }, - "errors": { - "page_error_title": "Что-то пошло не так", - "page_error_description": "Произошла непредвиденная ошибка. Пожалуйста, попробуйте снова или вернитесь на главную страницу.", - "sidebar_error": "Не удалось загрузить почтовые ящики", - "email_list_error": "Не удалось загрузить письма", - "viewer_error_title": "Не удалось отобразить письмо", - "viewer_error_description": "Произошла проблема при отображении этого письма. Оно может содержать неподдерживаемый контент.", - "composer_error": "Не удалось загрузить редактор письма", - "settings_error_title": "Настройки недоступны", - "settings_error_description": "Не удалось загрузить настройки. Ваши предпочтения могут не быть сохранены.", - "try_again": "Попробовать снова", - "reload": "Перезагрузить", - "reload_emails": "Перезагрузить письма", - "reload_settings": "Перезагрузить настройки", - "retry": "Повторить", - "go_home": "В папку «Входящие»" - }, - "context_menu": { - "reply": "Ответить", - "reply_all": "Ответить всем", - "forward": "Переслать", - "mark_read": "Отметить как прочитанное", - "mark_unread": "Отметить как непрочитанное", - "star": "Добавить звёздочку", - "unstar": "Убрать звёздочку", - "pin": "Закрепить", - "unpin": "Открепить", - "move_to": "Переместить в...", - "archive": "В архив", - "delete": "Удалить", - "mark_as_spam": "Отметить как спам", - "not_spam": "Не спам", - "tag": "Тег", - "items_selected": "{count} писем выбрано", - "edit_draft": "Редактировать черновик", - "cancel_scheduled_send": "Отменить отправку", - "reschedule_send": "Перенести", - "cancel_and_edit": "Отменить и изменить", - "cancel_and_compose_again": "Отменить и написать заново" - }, - "mailbox_context_menu": { - "mark_folder_read": "Отметить папку как прочитанную", - "mark_folder_tree_read": "Отметить папку и вложенные папки как прочитанные", - "mark_all_folders_read": "Отметить все папки как прочитанные", - "new_subfolder": "Новая вложенная папка...", - "new_folder": "Новая папка...", - "rename": "Переименовать...", - "share": "Поделиться...", - "import_email": "Импортировать .eml или .zip...", - "empty_folder": "Очистить папку", - "empty_folder_generic": "Очистить папку", - "delete_folder": "Удалить папку", - "refresh": "Обновить", - "mark_all_confirm_title": "Отметить все папки как прочитанные", - "mark_all_confirm_message": "Отметить все непрочитанные сообщения в вашем личном аккаунте как прочитанные?", - "delete_confirm_title": "Удалить папку", - "delete_confirm_message": "Безвозвратно удалить папку \"{name}\"? Это действие нельзя отменить.", - "prompt_new_subfolder": "Введите имя для новой вложенной папки.", - "prompt_new_folder": "Введите имя для новой папки.", - "prompt_rename": "Введите новое имя для этой папки.", - "toast_marked_read": "Папка отмечена как прочитанная", - "toast_marked_read_count": "Отмечено {count, plural, one {1 сообщение} few {# сообщения} other {# сообщений}} как прочитанные", - "toast_already_read": "Нет непрочитанных сообщений", - "toast_marked_all_read": "Все папки отмечены как прочитанные", - "toast_emptied": "Папка очищена", - "toast_folder_created": "Папка создана", - "toast_folder_renamed": "Папка переименована", - "toast_folder_deleted": "Папка удалена", - "toast_error_mark_read": "Не удалось отметить как прочитанное", - "toast_error_empty": "Не удалось очистить папку", - "toast_error_create": "Не удалось создать папку", - "toast_error_rename": "Не удалось переименовать папку", - "toast_error_delete": "Не удалось удалить папку", - "toast_error_delete_has_children": "Папка содержит вложенные папки. Сначала удалите их.", - "toast_error_delete_has_email": "Папка не пуста. Сначала очистите её.", - "delete_with_contents_title": "Папка не пуста", - "delete_with_contents_message": "Папка «{name}» всё ещё содержит {count, plural, one {# сообщение} few {# сообщения} other {# сообщений}}. Удалить папку вместе со всеми сообщениями? Это действие нельзя отменить.", - "delete_with_contents_confirm": "Удалить папку и сообщения", - "placeholder_folder_name": "Имя папки", - "create": "Создать", - "rename_confirm": "Переименовать" - }, - "shortcuts": { - "title": "Сочетания клавиш", - "tip": "Нажмите ? в любое время для отображения справки", - "sections": { - "navigation": "Навигация", - "actions": "Действия с письмом", - "global": "Глобальные", - "threads": "Цепочки", - "composer": "Редактор" - }, - "navigation": { - "next_email": "Следующее письмо", - "previous_email": "Предыдущее письмо", - "open_email": "Открыть письмо", - "close_email": "Закрыть / Отменить выбор" - }, - "actions": { - "reply": "Ответить", - "reply_all": "Ответить всем", - "forward": "Переслать", - "star": "Переключить звёздочку", - "archive": "В архив", - "delete": "Удалить", - "mark_unread": "Отметить как непрочитанное", - "mark_read": "Отметить как прочитанное", - "toggle_spam": "Отметить спам / Не спам" - }, - "global": { - "compose": "Написать новое письмо", - "search": "Перейти к поиску", - "help": "Показать сочетания клавиш", - "refresh": "Обновить письма", - "select_all": "Выбрать все", - "global_search": "Искать везде" - }, - "threads": { - "expand_collapse": "Развернуть/свернуть цепочку" - }, - "composer": { - "send": "Отправить письмо", - "schedule_send": "Запланировать отправку", - "template_picker": "Открыть выбор шаблонов" - } - }, - "threads": { - "messages_one": "{count} сообщение", - "messages_other": "{count} сообщений", - "messages_tooltip": "{count, plural, one {# сообщение в этом разговоре} other {# сообщений в этом разговоре}}", - "expand": "Развернуть разговор", - "collapse": "Свернуть разговор", - "loading": "Загрузка разговора...", - "mark_read": "Отметить разговор как прочитанный", - "mark_unread": "Отметить разговор как непрочитанный", - "archive": "Архивировать разговор", - "delete": "Удалить разговор", - "star": "Добавить звёздочку к разговору", - "unstar": "Убрать звёздочку у разговора", - "toggle_thread": "Переключить цепочку" - }, - "identities": { - "modal_title": "Управление идентификациями отправителя", - "create_new": "Создать новую идентификацию", - "edit_identity": "Редактировать идентификацию", - "delete_confirm": "Удалить эту идентификацию? Это действие нельзя отменить.", - "cannot_delete": "Эту идентификацию нельзя удалить", - "primary_identity": "Основная", - "set_as_primary": "Сделать основной", - "no_identities": "Идентификации не найдены", - "display": { - "reply_to": "Ответить на:", - "bcc": "СК:", - "signature": "Подпись:", - "preview": "Предпросмотр:" - }, - "validation_errors": { - "invalid_emails": "Неверные адреса: {emails}", - "unknown_error": "Неизвестная ошибка" - }, - "form": { - "name_label": "Отображаемое имя", - "name_placeholder": "напр., Рабочая почта, Личная", - "name_required": "Имя обязательно", - "email_label": "Адрес электронной почты", - "email_placeholder": "user@пример.рф", - "email_required": "Email обязателен", - "email_invalid": "Введите корректный адрес электронной почты", - "email_immutable": "Адрес электронной почты нельзя изменить после создания", - "reply_to_label": "Ответить на (необязательно)", - "reply_to_placeholder": "other@example.com", - "bcc_label": "Авто СК (необязательно)", - "bcc_placeholder": "archive@пример.рф", - "text_signature_label": "Текстовая подпись", - "html_signature_label": "HTML-подпись", - "signature_byte_counter": "{bytes} / {max} байт", - "signature_byte_limit_reached": "Достигнут лимит сервера", - "save": "Сохранить идентификацию", - "cancel": "Отмена", - "creating": "Создание...", - "updating": "Обновление..." - }, - "sub_address": { - "button_tooltip": "Использовать суб-адрес", - "popover_title": "Добавить тег суб-адреса", - "tag_input_placeholder": "Введите тег (напр., shopping)", - "preview_label": "Предпросмотр:", - "recent_tags": "Недавние теги", - "suggested_tags": "Предложения", - "use_address": "Использовать этот адрес", - "invalid_tag": "Тег должен содержать только буквы, цифры и дефисы", - "tag_too_long": "Тег не должен превышать 30 символов", - "help_text": "Письма на адрес user{delimiter}tag@domain.com будут приходить в ваш ящик", - "validation": { - "empty": "Тег не может быть пустым", - "too_long": "Тег не должен превышать {max} символов", - "invalid_chars": "Тег должен содержать только буквы, цифры и дефисы" - } - }, - "badge": { - "sent_via": "через", - "sub_address_tag": "Отправлено через суб-адрес: {tag}", - "identity_name": "Отправлено через идентификацию: {name}", - "identity_short": "через {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Удалить", - "delete_confirm_title": "Удалить идентификацию" - }, - "templates": { - "picker_title": "Выбрать шаблон", - "search_placeholder": "Поиск шаблонов...", - "section_favorites": "Избранное", - "section_recent": "Недавние", - "section_uncategorized": "Другие", - "no_templates": "Шаблоны ещё не созданы", - "no_results": "Шаблоны не найдены", - "fill_placeholders": "Заполнить значения переменных", - "enter_value": "Введите значение...", - "preview": "Предпросмотр", - "insert_with_values": "Вставить со значениями", - "insert_raw": "Вставить без изменений", - "copy_suffix": "(копия)", - "placeholder": "Переменная", - "placeholders": { - "recipient_name": "Имя получателя", - "company": "Название компании", - "date": "Текущая дата", - "day_of_week": "День недели", - "sender_name": "Ваше имя" - } - }, - "contacts": { - "title": "Контакты", - "search_placeholder": "Поиск контактов...", - "create_new": "Новый контакт", - "no_category": "Без категории", - "rename_category": "Переименовать категорию", - "category_name_label": "Название категории", - "category_renamed": "Категория переименована", - "category_rename_failed": "Не удалось переименовать категорию", - "category_added": "Контакт добавлен в {name}", - "category_added_plural": "{count} контактов добавлено в {name}", - "empty_state": "Контактов пока нет", - "empty_state_title": "Контактов пока нет", - "empty_state_subtitle": "Создайте первый контакт или импортируйте из файла vCard", - "empty_search": "Нет контактов, соответствующих вашему запросу", - "empty_search_hint": "Попробуйте другой поисковый запрос", - "empty_filtered": "Нет контактов, соответствующих фильтрам", - "empty_filtered_hint": "Измените или очистите фильтры", - "clear_search": "Очистить поиск", - "import_vcard": "Импорт vCard", - "delete_confirm_title": "Удалить контакт", - "delete_confirm": "Вы уверены, что хотите удалить этот контакт?", - "local_mode": "Контакты хранятся локально (сервер не поддерживает JMAP Contacts)", - "back_to_contacts": "Вернуться к контактам", - "back_to_email": "Вернуться к письму", - "tabs": { - "all": "Все", - "groups": "Группы" - }, - "shared": { - "title": "Общий" - }, - "address_books": { - "create": "Новая адресная книга", - "created": "Адресная книга создана", - "create_failed": "Не удалось создать адресную книгу", - "title": "Мои адресные книги", - "shared_prefix": "Общая: {name}", - "moved": "Контакт перемещён в {name}", - "moved_plural": "{count} контактов перемещено в {name}", - "move_failed": "Не удалось переместить контакт", - "address_book": "Адресная книга", - "rename": "Переименовать адресную книгу", - "name_label": "Название адресной книги", - "renamed": "Адресная книга переименована", - "rename_failed": "Не удалось переименовать адресную книгу", - "default": "По умолчанию", - "set_default": "Сделать по умолчанию", - "default_updated": "Адресная книга по умолчанию обновлена", - "set_default_failed": "Не удалось установить адресную книгу по умолчанию", - "manage": "Управление адресными книгами", - "share": "Поделиться адресной книгой", - "new_contact_in_book": "Новый контакт в этой адресной книге", - "delete": "Удалить адресную книгу", - "confirm_delete": "Удалить «{name}»? Все контакты в этой адресной книге будут удалены.", - "deleted": "Адресная книга удалена", - "delete_failed": "Не удалось удалить адресную книгу" - }, - "detail": { - "emails": "Адреса электронной почты", - "phones": "Номера телефонов", - "organizations": "Организации", - "addresses": "Адреса", - "notes": "Заметки", - "titles": "Должности и роли", - "online_services": "Онлайн-сервисы", - "anniversaries": "Даты", - "personal_info": "Личная информация", - "languages": "Языки", - "categories": "Категории", - "related_contacts": "Связанные контакты", - "crypto_keys": "Криптографические ключи", - "cert_issuer": "Издатель", - "cert_expires": "Истекает", - "cert_expired": "Истёк", - "cert_fingerprint": "Отпечаток", - "cert_algorithm": "Алгоритм", - "import_to_smime": "Импортировать в S/MIME", - "cert_already_imported": "Уже импортировано в S/MIME", - "cert_imported": "Сертификат импортирован в хранилище S/MIME", - "cert_import_failed": "Не удалось импортировать сертификат", - "no_contact_selected": "Выберите контакт для просмотра деталей", - "compose_email": "Написать письмо", - "copy_email": "Скопировать email", - "copy_phone": "Скопировать номер телефона", - "copy_url": "Скопировать URL", - "copied": "Скопировано в буфер обмена", - "copy_failed": "Не удалось скопировать в буфер обмена", - "created": "Создан", - "updated": "Последнее обновление", - "timezone": "Часовой пояс", - "anniversary_birth": "День рождения", - "anniversary_death": "Дата смерти", - "anniversary_wedding": "Годовщина", - "anniversary_other": "Другое", - "personal_expertise": "Экспертиза", - "personal_hobby": "Хобби", - "personal_interest": "Интерес", - "personal_other": "Другое", - "gender": "Пол", - "gender_masculine": "Мужской", - "gender_feminine": "Женский", - "gender_other": "Другой", - "gender_none": "Не применимо", - "gender_unknown": "Неизвестно", - "calendar": "Календарь", - "calendar_uri": "URL календаря", - "scheduling_uri": "URL планирования", - "freebusy_uri": "URL доступности", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Другие действия", - "age_years": "{count, plural, one {# год} few {# года} many {# лет} other {# лет}}", - "years_since": "{count, plural, one {# год назад} few {# года назад} many {# лет назад} other {# лет назад}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Новый контакт", - "edit_title": "Редактировать контакт", - "section_address_book": "Каталог", - "select_address_book": "Выберите каталог...", - "section_identity": "Имя и личность", - "contact_type": "Тип контакта", - "type_person": "Человек", - "type_organization": "Организация", - "section_work": "Работа и организация", - "prefix": "Префикс", - "prefix_placeholder": "Д-р., Г-н., Г-жа.", - "given_name": "Имя", - "middle_name": "Отчество", - "surname": "Фамилия", - "suffix": "Суффикс", - "suffix_placeholder": "Мл., Ст., III", - "nickname": "Псевдоним", - "nickname_placeholder": "Псевдоним", - "email": "Электронная почта", - "email_placeholder": "user@пример.рф", - "phone": "Телефон", - "phone_placeholder": "+7 234 567 890", - "phone_type": "Тип", - "phone_voice": "Голосовой", - "phone_cell": "Мобильный", - "phone_fax": "Факс", - "phone_pager": "Пейджер", - "phone_video": "Видео", - "phone_text": "Текст", - "organization": "Организация", - "organization_placeholder": "Название компании", - "department": "Отдел", - "department_placeholder": "Отдел", - "job_title": "Должность", - "job_title_placeholder": "напр., инженер-программист", - "role": "Роль", - "role_placeholder": "напр., руководитель группы", - "addresses": "Адреса", - "add_address": "Добавить адрес", - "street": "Улица", - "city": "Город", - "region": "Штат / Регион", - "postcode": "Почтовый индекс", - "country": "Страна", - "online_services": "Онлайн-сервисы", - "add_online_service": "Добавить онлайн-сервис", - "url_placeholder": "https://пример.рф", - "service_placeholder": "Сервис", - "anniversaries": "Даты", - "add_anniversary": "Добавить дату", - "anniversary_birth": "День рождения", - "anniversary_wedding": "Годовщина", - "anniversary_death": "Памятная дата", - "anniversary_other": "Другое", - "personal_info": "Личная информация", - "add_personal_info": "Добавить запись", - "personal_info_placeholder": "напр., Фотография", - "personal_expertise": "Экспертиза", - "personal_hobby": "Хобби", - "personal_interest": "Интерес", - "personal_other": "Другое", - "level": "Уровень", - "level_high": "Высокий", - "level_medium": "Средний", - "level_low": "Низкий", - "categories": "Категории", - "categories_placeholder": "напр., Семья, Друзья, Коллеги", - "categories_hint": "Введите для поиска или добавления категорий", - "category_add": "Добавить", - "note": "Заметки", - "note_placeholder": "Добавьте заметку...", - "gender": "Пол", - "gender_sex": "Биологический пол", - "gender_male": "Мужской", - "gender_female": "Женский", - "gender_other": "Другой", - "gender_none": "Не применимо", - "gender_unknown": "Неизвестно", - "gender_identity": "Гендерная идентичность", - "gender_identity_placeholder": "Гендерная идентичность...", - "calendar": "Календарь", - "calendar_uri": "URL календаря", - "scheduling_uri": "URL планирования", - "freebusy_uri": "URL доступности", - "context_work": "Рабочий", - "context_private": "Личный", - "add_email": "Добавить email", - "add_phone": "Добавить телефон", - "save": "Сохранить", - "cancel": "Отмена", - "creating": "Создание...", - "updating": "Обновление...", - "name_required": "Укажите имя, фамилию или организацию", - "email_invalid": "Введите корректный адрес электронной почты", - "email_error_inline": "Неверный формат email", - "save_failed": "Не удалось сохранить контакт", - "delete": "Удалить", - "upload_photo": "Загрузить фото", - "remove_photo": "Удалить фото", - "photo_hint": "JPG или PNG, до 10 МБ. Будет уменьшено.", - "photo_too_large": "Изображение слишком большое (макс. 10 МБ)", - "photo_invalid": "Недопустимый файл изображения", - "change_photo": "Изменить" - }, - "groups": { - "create": "Новая группа", - "edit": "Редактировать группу", - "empty": "Групп пока нет", - "delete_confirm_title": "Удалить группу", - "delete_confirm": "Вы уверены, что хотите удалить эту группу?", - "name_label": "Название группы", - "name_placeholder": "напр., Команда, Семья", - "name_required": "Название группы обязательно", - "save_failed": "Не удалось сохранить группу", - "members_label": "Участники", - "search_members": "Поиск контактов для добавления...", - "no_members": "В этой группе нет участников", - "member_count": "{count, plural, =0 {Нет участников} one {1 участник} other {# участников}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Импорт контактов", - "drop_hint": "Нажмите для выбора файла vCard", - "file_types": "Файлы .vcf или .vcard", - "no_contacts": "Контакты в файле не найдены", - "parse_error": "Не удалось разобрать файл vCard", - "found": "{count, plural, one {1 контакт найден} other {# контактов найдено}}", - "duplicate": "Дубликат", - "select_all": "Выбрать все", - "deselect_all": "Снять выделение", - "selected": "{count, plural, one {1 выбран} other {# выбрано}}", - "import_button": "Импортировать", - "importing": "Импортирование...", - "success": "{count, plural, one {1 контакт импортирован} other {# контактов импортировано}}", - "failed": "Импорт не выполнен", - "close": "Закрыть", - "file_too_large": "Файл слишком большой (макс. 5 МБ)" - }, - "export": { - "title": "Экспорт контактов", - "success": "{count, plural, one {1 контакт экспортирован} other {# контактов экспортировано}}" - }, - "bulk": { - "selected": "{count, plural, one {1 выбран} other {# выбрано}}", - "select_all": "Выбрать все", - "delete": "Удалить", - "delete_confirm_title": "Удалить контакты", - "delete_confirm": "Удалить {count, plural, one {1 контакт} other {# контактов}}?", - "deleted": "{count, plural, one {1 контакт удалён} other {# контактов удалено}}", - "add_to_group": "Добавить в группу", - "choose_group": "Выбрать группу", - "adding_contacts": "Добавляем {count, plural, one {1 контакт} other {# контактов}}", - "added_to_group": "Контакты добавлены в группу", - "export": "Экспорт", - "clear": "Очистить выбор" - }, - "toast": { - "created": "Контакт создан", - "updated": "Контакт обновлён", - "deleted": "Контакт удалён", - "error_create": "Не удалось создать контакт", - "error_update": "Не удалось обновить контакт", - "error_delete": "Не удалось удалить контакт" - }, - "context_menu": { - "open": "Открыть", - "edit": "Изменить", - "send_email": "Отправить письмо", - "add_to_group": "Добавить в группу", - "export_vcard": "Экспортировать как vCard", - "delete": "Удалить", - "call": "Позвонить", - "duplicate": "Дублировать", - "print": "Печать" - }, - "filters": { - "toggle": "Фильтры", - "select": "Выбрать", - "clear": "Очистить", - "close": "Закрыть", - "title": "Расширенные фильтры", - "organization": "Компания", - "organization_placeholder": "напр. Acme", - "job_title": "Должность", - "job_title_placeholder": "напр. Дизайнер", - "location": "Местоположение", - "location_placeholder": "Город или страна", - "email_domain": "Домен e-mail", - "email_domain_placeholder": "primer.ru", - "birthday_month": "День рождения в", - "any_month": "Любой месяц", - "has_email": "С эл. почтой", - "has_phone": "С телефоном", - "has_photo": "С фото", - "sort_by": "Сортировать по", - "sort_first_name": "Имя", - "sort_last_name": "Фамилия" - }, - "open_categories": "Открыть категории" - }, - "calendar": { - "title": "Календарь", - "back_to_email": "Вернуться к почте", - "back_to_month": "Вернуться к месяцу", - "my_calendars": "Календари", - "birthday_calendar": "Дни рождения", - "mini_calendar_change": "Нажмите для смены месяца", - "views": { - "month": "Месяц", - "week": "Неделя", - "day": "День", - "agenda": "Повестка", - "today": "Сегодня", - "month_hint": "Месяц (m)", - "week_hint": "Неделя (w)", - "day_hint": "День (d)", - "agenda_hint": "Повестка (a)", - "tasks": "Задачи", - "tasks_hint": "Задачи (k)" - }, - "events": { - "create": "Создать событие", - "edit": "Редактировать событие", - "delete": "Удалить событие", - "details": "Детали события", - "no_events": "Нет событий", - "all_day": "Весь день", - "more": "ещё {count}", - "no_title": "(Без названия)", - "resize": "Изменить размер события", - "duplicate": "Дублировать", - "today_header": "Сегодня", - "tomorrow_header": "Завтра", - "agenda_show_earlier": "Показать более ранние события", - "agenda_loading": "Загрузка…", - "agenda_range_start": "События с {date}", - "agenda_range_end": "События до {date}", - "export_ics": "Экспорт в .ics", - "copy_title": "Скопировать название", - "copy_link": "Скопировать ссылку встречи", - "go_to_today": "Перейти к сегодня", - "new_all_day_event": "Новое событие на весь день", - "new_event": "Новое событие", - "new_task": "Новая задача" - }, - "detail": { - "add_note": "Добавить заметку...", - "save_note": "Сохранить", - "note_saved": "Заметка добавлена", - "open_link": "Открыть ссылку", - "meeting_link": "Ссылка на встречу", - "tentative": "Предварительно", - "cancelled": "Отменено", - "delete_confirm": "Удалить это событие?" - }, - "form": { - "title": "Название", - "description": "Описание", - "location": "Место", - "meeting_link": "Ссылка на встречу", - "start_date": "Дата начала", - "end_date": "Дата окончания", - "start_time": "Время начала", - "end_time": "Время окончания", - "all_day_event": "Событие на весь день", - "calendar_select": "Календарь", - "save": "Сохранить", - "cancel": "Отмена", - "delete_confirm": "Вы уверены, что хотите удалить это событие?", - "color": "Цвет" - }, - "participants": { - "availability": { - "title": "Доступность", - "free": "Свободен", - "busy": "Занят", - "tentative": "Предварительно", - "unknown": "Не пользователь этого сервера", - "checking": "Проверка…" - }, - "title": "Участники", - "add": "Добавить участника", - "organizer": "Организатор", - "attendee": "Участник", - "accepted": "Принято", - "declined": "Отклонено", - "tentative": "Предварительно", - "needs_action": "Требует ответа", - "remove": "Удалить", - "edit": "Редактировать", - "email_placeholder": "Добавьте адрес или найдите контакт", - "send_invitations": "Отправить приглашения участникам", - "status_summary": "{accepted} принято, {pending} ожидает", - "invited_by": "Приглашение от {name}", - "respond_below": "Ответьте с помощью кнопок ниже", - "rsvp_label": "Ваш ответ", - "cancel_notification": "Участники будут уведомлены об отмене", - "you_organizer": "Вы являетесь организатором", - "you_attendee": "Вы являетесь участником", - "no_participants": "Нет участников", - "count": "{count, plural, one {# участник} other {# участников}}" - }, - "recurrence": { - "title": "Повторение", - "none": "Не повторяется", - "daily": "Ежедневно", - "weekly": "Еженедельно", - "monthly": "Ежемесячно", - "yearly": "Ежегодно", - "every_n_days": "Каждые {count} дней", - "every_n_weeks": "Каждые {count} недель", - "every_n_months": "Каждые {count} месяцев", - "until": "До", - "occurrences": "{count} повторений", - "custom": "Свой вариант…", - "edit_custom": "Изменить настраиваемое повторение", - "every_n_years": "Каждые {count} лет", - "on_days": "в {days}", - "on_day_n": "{day}-го числа", - "on_the_nth": "в {nth} {day}", - "in_month": "в {month}", - "nth_1": "первый", - "nth_2": "второй", - "nth_3": "третий", - "nth_4": "четвёртый", - "nth_last": "последний", - "editor_freq_day": "День", - "editor_freq_week": "Неделя", - "editor_freq_month": "Месяц", - "editor_freq_year": "Год", - "editor_repeats_on": "Повторяется", - "editor_every": "Каждые", - "editor_unit_days": "дн.", - "editor_unit_weeks": "нед.", - "editor_unit_months": "мес.", - "editor_unit_years": "г.", - "editor_on_day": "в день", - "editor_on_the": "в", - "editor_in": "в месяце", - "editor_ends": "Заканчивается", - "editor_never": "Никогда", - "editor_ends_on": "Дата", - "editor_ends_after": "После", - "editor_occurrences": "повторений" - }, - "recurrence_scope": { - "edit_title": "Редактировать повторяющееся событие", - "delete_title": "Удалить повторяющееся событие", - "description": "Это повторяющееся событие. Какие события вы хотите изменить?", - "this_event": "Только это событие", - "this_and_future": "Это и последующие события", - "all_events": "Все события", - "cancel": "Отмена", - "save": "Сохранить", - "delete": "Удалить", - "rsvp_title": "Ответить на повторяющееся событие", - "rsvp_description": "Это повторяющееся событие. К каким событиям относится ваш ответ?", - "respond": "Ответить" - }, - "alerts": { - "title": "Напоминание", - "none": "Без напоминания", - "at_time": "В момент события", - "minutes_before": "{count, plural, one {За # минуту} other {За # минут}}", - "hours_before": "{count, plural, one {За # час} other {За # часов}}", - "days_before": "{count, plural, one {За # день} other {За # дней}}", - "weeks_before": "{count, plural, one {За # неделю} few {За # недели} many {За # недель} other {За # недели}}", - "unit_minutes_before": "минут до", - "unit_hours_before": "часов до", - "unit_days_before": "дней до", - "unit_weeks_before": "недель до", - "add": "Добавить напоминание", - "remove": "Удалить напоминание", - "amount": "Значение напоминания", - "unit": "Единица напоминания" - }, - "settings": { - "title": "Настройки календаря", - "default_view": "Вид по умолчанию", - "week_starts_on": "Неделя начинается с", - "time_format": "Формат времени", - "default_calendar": "Календарь по умолчанию", - "default_reminder": "Напоминание по умолчанию", - "time_format_12h": "12-часовой", - "time_format_24h": "24-часовой", - "notifications_enabled": "Уведомления о событиях", - "notifications_enabled_desc": "Показывать оповещения о предстоящих событиях", - "notification_sound": "Звук уведомления", - "notification_sound_desc": "Воспроизводить звук для оповещений календаря", - "invitation_parsing": "Разбор приглашений по email", - "invitation_parsing_desc": "Определять приглашения календаря во вложениях email и показывать действия", - "show_time_in_month_view": "Показывать время в виде месяца", - "show_time_in_month_view_desc": "Отображать время события в виде месячного календаря", - "show_week_numbers": "Показывать номера недель", - "show_week_numbers_desc": "Отображать номера недель в мини-календаре", - "calendar_free_scroll": "Свободная прокрутка", - "calendar_free_scroll_desc": "Непрерывно прокручивать месяцы, недели и дни вместо одного периода за раз", - "enable_tasks": "Включить задачи", - "enable_tasks_desc": "Показывать представление задач в календаре для управления делами", - "show_tasks_on_calendar": "Показывать задачи в календаре", - "show_tasks_on_calendar_desc": "Отображать чипы задач в видах «день» и «неделя»", - "hover_preview": "Предпросмотр при наведении", - "hover_preview_desc": "Показывать всплывающее окно с деталями при наведении на события календаря", - "hover_preview_instant": "Мгновенно", - "hover_preview_delay_500ms": "Задержка 0,5 секунды", - "hover_preview_delay_1s": "Задержка 1 секунда", - "hover_preview_delay_2s": "Задержка 2 секунды", - "hover_preview_off": "Отключено", - "organizer_identity": "Организовывать приглашения как", - "organizer_identity_desc": "Адрес календаря, используемый как организатор при приглашении людей на события.", - "organizer_identity_failed": "Не удалось изменить адрес организатора", - "show_birthday_calendar": "Календарь дней рождения", - "show_birthday_calendar_desc": "Показать виртуальный календарь с днями рождения из ваших контактов" - }, - "days": { - "monday": "Понедельник", - "tuesday": "Вторник", - "wednesday": "Среда", - "thursday": "Четверг", - "friday": "Пятница", - "saturday": "Суббота", - "sunday": "Воскресенье", - "mon": "Пн", - "tue": "Вт", - "wed": "Ср", - "thu": "Чт", - "fri": "Пт", - "sat": "Сб", - "sun": "Вс" - }, - "notifications": { - "event_created": "Событие создано", - "event_updated": "Событие обновлено", - "event_deleted": "Событие удалено", - "calendar_created": "Календарь создан", - "calendar_deleted": "Календарь удалён", - "event_move_error": "Не удалось переместить событие", - "event_resize_error": "Не удалось изменить размер события", - "alert_title": "Предстоящее событие", - "alert_now": "Начинается сейчас", - "alert_in_minutes": "Через {count} мин", - "invitation_sent": "Приглашения отправлены", - "invitations_denied_title": "Не удаётся отправить приглашения", - "invitations_denied": "Сервер отказался отправить приглашения: {reason}. Сохранить событие без их отправки?", - "save_without_invitations": "Сохранить без приглашений", - "rsvp_updated": "Ответ обновлён", - "rsvp_error": "Не удалось обновить ответ", - "event_duplicated": "Событие дублировано", - "event_error": "Не удалось сохранить событие", - "task_due": "Срок задачи", - "event_exported": "Событие экспортировано", - "title_copied": "Название скопировано", - "link_copied": "Ссылка скопирована" - }, - "status": { - "loading_calendars": "Загрузка календарей...", - "loading_events": "Загрузка событий..." - }, - "quick_create": { - "placeholder": "Название нового события", - "aria_label": "Быстрое создание события" - }, - "nav_prev": "Предыдущий", - "nav_next": "Следующий", - "import": { - "title": "Импорт календаря", - "tab_file": "Файл", - "tab_url": "URL-адрес", - "select_file": "Выберите файл .ics", - "drop_file": "или перетащите файл сюда", - "supported_formats": "Поддерживает файлы iCalendar (.ics)", - "url_description": "Введите URL внешней ленты iCalendar (.ics) для импорта событий.", - "url_placeholder": "https://пример.рф/календарь.ics", - "url_hint": "Поддерживает URL iCalendar (.ics)", - "fetch": "Получить", - "invalid_url": "Введите корректный URL", - "url_fetch_failed": "Не удалось получить календарь по URL", - "parsing": "Разбор файла календаря...", - "parsed_events": "Найдено {count} событий", - "no_events": "События в файле не найдены", - "select_all": "Выбрать все", - "deselect_all": "Снять выделение", - "target_calendar": "Импортировать в календарь", - "import_button": "Импортировать выбранные", - "importing": "Импортирование событий...", - "success": "{count} событий успешно импортировано", - "error": "Не удалось импортировать календарь", - "file_too_large": "Файл превышает лимит 10 МБ", - "invalid_format": "Неверный формат файла календаря" - }, - "webcal_action": { - "title": "Открыть ссылку календаря", - "description": "Как вы хотите использовать \"{name}\"?", - "import_title": "Импортировать один раз", - "import_description": "Загрузить события сейчас и скопировать их в один из ваших календарей.", - "subscribe_title": "Подписаться", - "subscribe_description": "Автоматически синхронизировать этот календарь как отдельный календарь.", - "cancel": "Отмена" - }, - "management": { - "title": "Управление календарями", - "description": "Создавайте, переименовывайте и настраивайте свои календари. Щёлкните правой кнопкой мыши на календаре в боковой панели для быстрой смены цвета.", - "name": "Имя", - "name_placeholder": "Название календаря", - "color": "Цвет", - "change_color": "Изменить цвет", - "random_color": "Новый случайный цвет", - "add_calendar": "Добавить календарь", - "edit": "Редактировать", - "delete": "Удалить", - "save": "Сохранить", - "create": "Создать", - "cancel": "Отмена", - "default": "По умолчанию", - "confirm_delete": "Удалить «{name}»? Все события в этом календаре будут удалены.", - "confirm_clear": "Очистить все события из «{name}»? Это действие нельзя отменить.", - "set_default": "Сделать по умолчанию", - "default_updated": "Календарь по умолчанию обновлён", - "error_default": "Не удалось установить календарь по умолчанию", - "clear_events": "Очистить события", - "events_cleared": "{count} событий очищено", - "error_clear": "Не удалось очистить события календаря", - "calendar_created": "Календарь создан", - "calendar_updated": "Календарь обновлён", - "calendar_deleted": "Календарь удалён", - "color_updated": "Цвет календаря обновлён", - "error_create": "Не удалось создать календарь", - "error_update": "Не удалось обновить календарь", - "error_delete": "Не удалось удалить календарь", - "caldav_url": "URL CalDAV", - "copy_url": "Скопировать CalDAV URL", - "url_copied": "CalDAV URL скопирован в буфер обмена", - "share": "Поделиться календарём", - "new_event_in_calendar": "Новое событие в этом календаре", - "kind": "Содержимое", - "kind_events": "События", - "kind_tasks": "Задачи", - "kind_both": "События и задачи", - "kind_hint": "Определяет, какие приложения видят этот календарь при синхронизации через CalDAV. Позже изменить нельзя." - }, - "subscription": { - "title": "Подписка iCal", - "section_title": "Подписки iCal", - "description": "Подпишитесь на внешнюю ленту iCalendar. События будут автоматически синхронизированы в отдельном календаре. Поддерживает URLs вида https:// и webcal://.", - "url_label": "URL календаря", - "url_placeholder": "https://example.com/calendar.ics или webcal://...", - "name_label": "Название календаря", - "name_placeholder": "напр. Государственные праздники", - "color_label": "Цвет", - "refresh_interval": "Интервал обновления", - "interval_15": "Каждые 15 минут", - "interval_30": "Каждые 30 минут", - "interval_60": "Каждый час", - "interval_360": "Каждые 6 часов", - "interval_1440": "Каждый день", - "subscribe": "Подписаться", - "subscribing": "Подписка...", - "save": "Сохранить изменения", - "saving": "Сохранение...", - "edit": "Изменить", - "edit_title": "Изменить подписку", - "updated": "«{name}» обновлена", - "update_error": "Не удалось обновить подписку", - "invalid_url": "Введите корректный URL", - "success": "Подписка на «{name}» оформлена", - "error": "Не удалось добавить подписку", - "refresh": "Обновить сейчас", - "refresh_success": "Подписка обновлена", - "refresh_error": "Не удалось обновить подписку", - "unsubscribe": "Отписаться", - "confirm_delete": "Отписаться от «{name}»? Календарь и все его события будут удалены.", - "deleted": "Подписка удалена", - "delete_error": "Не удалось удалить подписку", - "last_refreshed": "Последнее обновление: {time}" - }, - "tasks": { - "label": "Задачи", - "no_tasks": "Нет задач", - "no_title": "(Без названия)", - "mark_complete": "Отметить как выполненное", - "mark_incomplete": "Отметить как невыполненное", - "filter_all": "Все", - "filter_pending": "Ожидающие", - "filter_completed": "Завершённые", - "filter_overdue": "Просроченные", - "show_completed": "Показывать завершённые", - "create": "Новая задача", - "edit": "Редактировать задачу", - "title_placeholder": "Название задачи", - "description_placeholder": "Добавьте описание...", - "due_date": "Срок выполнения", - "include_time": "Включить время", - "priority": "Приоритет", - "priority_none": "Нет", - "priority_high": "Высокий", - "priority_medium": "Средний", - "priority_low": "Низкий", - "progress": "Статус", - "progress_needs_action": "Требует действия", - "progress_in_process": "В процессе", - "progress_completed": "Завершено", - "progress_cancelled": "Отменено", - "calendar": "Календарь", - "alert": "Напоминание", - "alert_none": "Нет", - "alert_at_time": "В момент срока", - "alert_5min": "За 5 минут", - "alert_15min": "За 15 минут", - "alert_30min": "За 30 минут", - "alert_1hr": "За 1 час", - "alert_1day": "За 1 день", - "delete": "Удалить", - "cancel": "Отмена", - "save": "Сохранить", - "quick_add_placeholder": "Добавить задачу...", - "due_today": "Сегодня", - "due_tomorrow": "Завтра", - "overdue": "Просрочено" - }, - "months": { - "jan": "янв.", - "feb": "февр.", - "mar": "март", - "apr": "апр.", - "may": "май", - "jun": "июнь", - "jul": "июль", - "aug": "авг.", - "sep": "сент.", - "oct": "окт.", - "nov": "нояб.", - "dec": "дек.", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Открыть меню" - }, - "advanced_search": { - "title": "Расширенный поиск", - "from": "От", - "from_placeholder": "Email или имя отправителя", - "to": "Кому", - "to_placeholder": "Email или имя получателя", - "subject": "Тема", - "subject_placeholder": "Тема содержит...", - "body": "Текст письма", - "body_placeholder": "Текст содержит...", - "folder": "Папка", - "all_folders": "Все папки", - "has_attachment": "Вложения", - "date_after": "После", - "date_before": "До", - "size_min": "Больше чем (КБ)", - "size_max": "Меньше чем (КБ)", - "starred": "Со звёздочкой", - "unread": "Непрочитанные", - "read": "Прочитанные", - "yes": "Да", - "no": "Нет", - "clear": "Очистить", - "clear_all": "Очистить всё", - "filters_active": "{count} фильтр", - "filters_active_plural": "{count} фильтров", - "toggle_filters": "Ещё", - "search_hint": "Используйте расширенные фильтры для точного поиска", - "advanced_filters_tooltip": "Расширенные фильтры поиска", - "results_found": "{count, plural, =0 {Ничего не найдено} one {# результат} other {# результатов}}", - "results_found_more": "Найдено {count}+ результатов", - "suggestions_recent": "Недавние запросы", - "suggestions_people": "Люди", - "suggestions_from": "Искать письма от {address}", - "suggestions_to": "Искать письма для {address}", - "suggestions_remove_recent": "Удалить из недавних запросов" - }, - "welcome": { - "title": "Добро пожаловать в ваш почтовый ящик", - "tip_compose": "Нажмите c для создания нового письма", - "tip_shortcuts": "Нажмите ? для просмотра всех сочетаний клавиш", - "tip_sidebar": "Найдите Календарь, Контакты и Настройки в боковом меню", - "tip_settings": "Настройте интерфейс в разделе «Настройки»", - "got_it": "Понятно", - "settings": "Настройки", - "dismiss": "Закрыть", - "start_tour": "Начать тур" - }, - "demo_welcome": { - "title": "Добро пожаловать в Bulwark Mail", - "description": "Откройте для себя полнофункциональный веб-клиент электронной почты - прямо в браузере. Все данные хранятся на вашем устройстве, поэтому вы можете смело всё проверить.", - "feature_email": "Чтение и написание писем", - "feature_organize": "Метки, звёздочки и папки", - "feature_shortcuts": "Сочетания клавиш", - "feature_privacy": "100% конфиденциальный демо-режим", - "hint": "Нажмите на любое письмо слева, чтобы начать, или пройдите тур ниже." - }, - "files": { - "office_edit": "Редактировать документ", - "office_loading": "Открытие редактора документов…", - "office_error": "Не удалось открыть редактор документов.", - "office_close": "Закрыть редактор", - "open_in_new_tab": "Открыть в новой вкладке", - "title": "Файлы", - "search_placeholder": "Поиск файлов...", - "empty_state_title": "Файлов пока нет", - "empty_state_description": "Загрузите файлы или создайте папки для начала", - "upload": "Загрузить", - "upload_files": "Загрузить файлы", - "new_folder": "Новая папка", - "new_folder_name": "Название папки", - "rename": "Переименовать", - "rename_title": "Переименовать", - "new_name": "Новое название", - "name_forbidden_chars": "Сервер не допускает эти символы в именах: {chars}", - "name_reserved": "Это имя зарезервировано на сервере.", - "delete": "Удалить", - "delete_confirm_title": "Удалить ресурс", - "delete_confirm_message": "Вы уверены, что хотите удалить «{name}»? Это действие нельзя отменить.", - "download": "Скачать", - "name": "Имя", - "size": "Размер", - "modified": "Дата изменения", - "type": "Тип", - "folder": "Папка", - "file": "Файл", - "parent_directory": "Родительский каталог", - "breadcrumb_root": "Главная", - "drop_files_here": "Перетащите файлы или папки сюда для загрузки", - "uploading": "Загрузка...", - "upload_success": "{count, plural, one {1 файл загружен} other {# файлов загружено}}", - "upload_error": "Не удалось загрузить файл", - "create_folder_success": "Папка создана", - "create_folder_error": "Не удалось создать папку", - "delete_success": "Успешно удалено", - "delete_error": "Не удалось удалить", - "rename_success": "Успешно переименовано", - "rename_error": "Не удалось переименовать", - "download_error": "Не удалось скачать", - "not_available": "Хранилище файлов недоступно на этом сервере", - "cancel": "Отмена", - "create": "Создать", - "save": "Сохранить", - "no_results": "Нет файлов, соответствующих вашему запросу", - "batch_delete_confirm_message": "Вы уверены, что хотите удалить {count, plural, one {1 элемент} other {# элементов}}? Это действие нельзя отменить.", - "batch_delete_success": "{count, plural, one {1 элемент удалён} other {# элементов удалено}}", - "grid_view": "Сеткой", - "list_view": "Списком", - "details": "Подробности", - "path": "Путь", - "preview": "Предпросмотр", - "preview_error": "Не удалось загрузить предпросмотр", - "cut": "Вырезать", - "copy": "Копировать", - "paste": "Вставить", - "move_success": "{count, plural, one {1 элемент перемещён} other {# элементов перемещено}}", - "move_error": "Не удалось переместить", - "paste_success": "Вставлено успешно", - "paste_error": "Не удалось вставить", - "new_text_file": "Новый текстовый файл", - "file_name": "Имя файла", - "retry": "Повторить", - "refresh": "Обновить", - "toggle_favorite": "Переключить избранное", - "duplicate": "Дублировать", - "duplicate_success": "Дублирование выполнено успешно", - "duplicate_error": "Не удалось дублировать", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Файл создан", - "create_file_error": "Не удалось создать файл", - "favorites": "Избранное", - "recent": "Недавние", - "properties": "Свойства", - "open_folder": "Открыть папку", - "upload_folder": "Загрузить папку", - "file_too_large": "«{name}» превышает максимальный размер файла ({max})", - "undo": "Отменить", - "undo_success": "Действие отменено", - "undo_error": "Не удалось отменить", - "toolbar": "Действия с файлами", - "file_list": "Файлы и папки", - "context_menu": "Действия", - "settings_title": "Настройки файлов", - "settings_display": "Отображение", - "settings_default_view": "Вид по умолчанию", - "settings_default_view_desc": "Выберите между сеткой и списком", - "settings_default_sort": "Сортировка по умолчанию", - "settings_default_sort_desc": "Выберите способ сортировки файлов по умолчанию", - "settings_sort_direction": "Направление сортировки", - "settings_sort_direction_desc": "Выберите порядок: по возрастанию или убыванию", - "settings_ascending": "По возрастанию", - "settings_descending": "По убыванию", - "settings_icons": "Иконки", - "settings_show_icons": "Показывать иконки файлов", - "settings_show_icons_desc": "Отображать иконки рядом с файлами и папками", - "settings_colored_icons": "Цветные иконки", - "settings_colored_icons_desc": "Использовать цветные иконки вместо монохромных", - "settings_show_thumbnails": "Показывать миниатюры", - "settings_show_thumbnails_desc": "Отображать предпросмотр изображений вместо иконок", - "settings_behavior": "Поведение", - "settings_show_hidden": "Показывать скрытые файлы", - "settings_show_hidden_desc": "Отображать файлы и папки, начинающиеся с точки", - "settings_folder_layout": "Навигация по папкам", - "settings_folder_layout_desc": "Выберите, как отображать папки: встроенно с файлами или в боковом дереве", - "settings_folder_layout_inline": "Встроенно", - "settings_folder_layout_sidebar": "Боковая панель", - "disabled_title": "Функция файлов отключена вашим администратором", - "disabled_description": "Загрузка больших файлов через WebDAV может вызвать нестабильность Stalwart/RocksDB, включая ошибки нехватки памяти и невосстановимое использование диска. Удалённые файлы могут не быть немедленно удалены из хранилища. Эта функция не рекомендуется для рабочих сред.", - "stability_warning": "Загрузка больших файлов может вызвать нестабильность сервера. Удалённые файлы могут не быть немедленно удалены из хранилища. Используйте с осторожностью.", - "no_accounts": "Нет подключённых учётных записей.", - "open_folder_tree": "Открыть дерево папок", - "other_accounts": "Другие учётные записи", - "migration_title": "Обновление ваших файлов…", - "migration_description": "Папки и файлы упорядочиваются в правильную структуру. Это происходит только один раз." - }, - "smime": { - "your_certificates": "Ваши сертификаты", - "your_certificates_desc": "Импортируйте и управляйте своими сертификатами S/MIME для подписи и шифрования писем", - "recipient_certificates": "Сертификаты получателей", - "recipient_certificates_desc": "Публичные сертификаты для шифрования писем получателям", - "identity_bindings": "Привязки ключей к удостоверениям", - "identity_bindings_desc": "Привяжите сертификаты S/MIME к вашим почтовым удостоверениям", - "defaults_title": "Параметры по умолчанию", - "defaults_desc": "Настройте поведение подписи и шифрования по умолчанию", - "import_pkcs12": "Импорт PKCS#12 (.p12/.pfx)", - "import_public_cert": "Импортировать сертификат", - "no_certificates": "Сертификаты ещё не импортированы", - "no_recipient_certs": "Нет сертификатов получателей", - "expires": "Истекает", - "expired": "Истёк", - "bound_to": "Привязан к", - "no_key_bound": "Нет", - "lock": "Заблокировать ключ", - "unlock": "Разблокировать ключ", - "details": "Показать подробности", - "delete": "Удалить", - "encrypt_by_default": "Шифровать по умолчанию", - "encrypt_by_default_desc": "Автоматически шифровать письма, когда у всех получателей есть сертификаты", - "remember_unlocked": "Запоминать разблокированные ключи", - "remember_unlocked_desc": "Оставлять ключи разблокированными на время этой сессии браузера", - "sign_default_for": "Подписывать по умолчанию для", - "enter_p12_passphrase": "Введите парольную фразу PKCS#12", - "p12_passphrase_desc": "Введите парольную фразу, которая защищает этот файл сертификата", - "enter_storage_passphrase": "Установите парольную фразу хранилища", - "storage_passphrase_desc": "Выберите парольную фразу для защиты этого ключа при хранении в браузере", - "next": "Далее", - "import": "Импортировать", - "unlock_key": "Разблокировать ключ", - "unlock_key_desc": "Введите парольную фразу хранилища, чтобы разблокировать ключ для подписи или расшифрования", - "passphrase_placeholder": "Введите парольную фразу", - "confirm_passphrase_placeholder": "Подтвердите парольную фразу", - "passphrase_mismatch": "Парольные фразы не совпадают", - "cancel": "Отмена", - "processing": "Обработка…", - "close": "Закрыть", - "certificate_details": "Сведения о сертификате", - "cert_subject": "Субъект", - "cert_issuer": "Издатель", - "cert_email": "Электронная почта", - "cert_serial": "Серийный номер", - "cert_validity": "Срок действия", - "cert_fingerprint": "Отпечаток (SHA-256)", - "cert_algorithm": "Алгоритм", - "cert_capabilities": "Возможности", - "cert_source": "Источник", - "cert_expired": "Срок действия этого сертификата истёк", - "cert_not_yet_valid": "Этот сертификат ещё не действует", - "cap_sign": "Подпись", - "cap_encrypt": "Шифрование", - "cap_none": "Нет", - "show_passphrase": "Показать парольную фразу", - "hide_passphrase": "Скрыть парольную фразу", - "sign_toggle": "Подписывать", - "encrypt_toggle": "Шифровать", - "missing_recipient_certs": "Отсутствуют сертификаты для: {emails}", - "missing_sender_cert": "К этому удостоверению не привязан сертификат", - "status_encrypted_ok": "Это сообщение было зашифровано", - "status_encrypted_no_key": "Сообщение зашифровано, но подходящий ключ не найден", - "status_encrypted_failed": "Не удалось расшифровать это сообщение", - "status_signed_valid": "Подпись проверена", - "status_signed_invalid": "Проверка подписи не удалась", - "status_signed_expired_cert": "Подписано просроченным сертификатом", - "status_signed_self_signed": "Подпись действительна, но сертификат самоподписанный (не доверенный)", - "status_signed_mismatch": "Подпись действительна, но подписант не совпадает с отправителем", - "status_unsupported": "Неподдерживаемый формат S/MIME", - "auto_import_signer_certs": "Автоимпорт сертификатов подписантов", - "auto_import_signer_certs_desc": "Автоматически сохранять сертификаты из проверенных подписанных писем для будущего шифрования", - "export": "Экспорт", - "enter_export_passphrase": "Установите парольную фразу для экспорта", - "export_passphrase_desc": "Выберите парольную фразу для защиты экспортируемого файла PKCS#12", - "export_storage_desc": "Введите парольную фразу хранилища для расшифрования ключа перед экспортом", - "incorrect_passphrase": "Неверная парольная фраза" - }, - "tour": { - "step_counter": "Шаг {current} из {total}", - "skip": "Пропустить тур", - "back": "Назад", - "next": "Далее", - "finish": "Завершить", - "take_a_tour": "Пройдите тур по интерфейсу", - "restart_title": "Ознакомительный тур", - "restart_desc": "Повторить пошаговое руководство по интерфейсу", - "restart_button": "Перезапустить тур", - "show_on_new_devices_title": "Показывать на новых устройствах", - "show_on_new_devices_desc": "Повторить приветственный баннер и тур при первом входе на новом устройстве, даже если вы уже завершили их в другом месте", - "sidebar_title": "Ваши почтовые ящики", - "sidebar_desc": "Это боковая панель с папками. Нажмите на любой почтовый ящик для просмотра писем. Вы можете создавать папки, перетаскивать письма между ними и видеть количество непрочитанных.", - "compose_title": "Написать письмо", - "compose_desc": "Нажмите здесь, чтобы написать новое письмо. Вы можете добавить получателей, вложения и использовать форматирование текста.", - "search_title": "Поиск почты", - "search_desc": "Ищите по отправителю, теме или содержимому. Нажмите на значок фильтра для расширенных параметров: диапазон дат, вложения и письма со звёздочкой.", - "email_list_title": "Список писем", - "email_list_desc": "Здесь отображаются письма. Нажмите на письмо для чтения справа. Используйте флажок для выбора нескольких писем, затем массово переместите, удалите или пометьте их.", - "email_viewer_title": "Панель чтения", - "email_viewer_desc": "Выбранное письмо открывается здесь. Ответьте, перешлите, архивируйте или удалите с помощью кнопок панели. Также можно добавить звёздочку или цветную метку.", - "keywords_title": "Цветные метки", - "keywords_desc": "Организуйте почту с помощью цветных меток. Перетащите письмо на метку для маркировки или щёлкните правой кнопкой мыши для назначения меток.", - "calendar_title": "Календарь", - "calendar_desc": "Перейдите в календарь для управления событиями. Создавайте события, устанавливайте напоминания и просматривайте день, неделю или месяц.", - "contacts_title": "Контакты", - "contacts_desc": "Здесь находится ваша адресная книга. Импортируйте контакты, создавайте группы и нажмите на контакт для просмотра подробностей.", - "settings_title": "Настройки", - "settings_desc": "Настройте всё: тему, плотность, подписи, фильтры, сочетания клавиш, параметры календаря и многое другое.", - "shortcuts_title": "Сочетания клавиш", - "shortcuts_desc": "Опытные пользователи оценят это. Нажмите ? в любое время для просмотра доступных сочетаний. Вы можете перемещаться, создавать и управлять письмами без мыши.", - "compose_open_title": "Редактор писем", - "compose_open_desc": "Это редактор писем. Добавьте получателей, напишите сообщение, прикрепите файлы и используйте форматирование. Также можно сохранять черновики и использовать шаблоны.", - "calendar_view_title": "Ваш календарь", - "calendar_view_desc": "Вот ваш календарь с примерными событиями. Вы можете переключаться между видами «день», «неделя», «месяц» и «повестка» с помощью панели инструментов.", - "create_event_title": "Создать событие", - "create_event_desc": "Нажмите эту кнопку для создания нового события. Вы можете указать название, дату, время и добавить участников.", - "event_modal_title": "Детали события", - "event_modal_desc": "Это форма события. Заполните название, выберите дату и время, добавьте место или участников. Нажмите «Сохранить» после завершения - или закройте и продолжайте.", - "contacts_list_title": "Ваши контакты", - "contacts_list_desc": "Здесь ваши контакты. Нажмите на контакт для просмотра подробностей справа. Вы также можете создавать новые контакты, импортировать vCard или организовывать контакты в группы.", - "settings_tabs_title": "Меню настроек", - "settings_tabs_desc": "Здесь все категории настроек. Настройте внешний вид, управляйте идентификациями, настраивайте фильтры, конфигурируйте календарь и многое другое.", - "files_title": "Хранилище файлов", - "files_desc": "Файловый менеджер позволяет загружать, организовывать и делиться файлами - как личное облачное хранилище в вашей почте.", - "demo_banner_title": "Элементы управления демо", - "demo_banner_desc": "Вы в демо-режиме - всё остаётся в вашем браузере. Нажмите «Сбросить демо» в любое время, чтобы начать заново с чистыми данными.", - "quota_title": "Использование хранилища", - "quota_desc": "Отслеживайте размер вашего почтового ящика здесь. Круг заполняется по мере использования пространства." - }, - "unified_mailbox": { - "search_unavailable": "Поиск недоступен в объединённом представлении" - }, - "calendar_event_notifications": { - "someone": "Кто-то", - "invited": "{name} пригласил(а) вас на «{title}»", - "updated": "{name} обновил(а) «{title}»", - "cancelled": "{name} отменил(а) «{title}»", - "untitled": "Событие без названия" - }, - "share_notifications": { - "someone": "Кто-то", - "shared": "{name} поделился(ась) с вами {kind} «{object}»", - "changed": "{name} изменил(а) ваш доступ к {kind} «{object}»", - "revoked": "{name} отозвал(а) ваш доступ к {kind} «{object}»", - "object": { - "folder": "папкой", - "calendar": "календарём", - "address_book": "адресной книгой", - "files": "папкой файлов" - } - }, - "sharing": { - "title": "Поделиться «{name}»", - "description": "Предоставьте доступ другим пользователям или группам на этом сервере. Изменения вступают в силу немедленно.", - "no_shares": "Пока никому не предоставлен доступ.", - "add_person": "Добавить пользователя или группу", - "search_placeholder": "Искать по имени или email…", - "loading_principals": "Загрузка пользователей…", - "no_principals": "Других пользователей или групп не найдено.", - "no_match": "Нет совпадений.", - "remove": "Отозвать доступ", - "group": "Группа", - "share_added": "Доступ предоставлен", - "share_updated": "Доступ обновлён", - "share_removed": "Доступ отозван", - "share_failed": "Не удалось обновить общий доступ", - "preset": { - "freeBusy": "Только занятость", - "read": "Только чтение", - "readWrite": "Чтение и запись", - "manager": "Управляющий", - "custom": "Пользовательский" - } - }, - "quote_header": { - "reply_line": "{date}, {from} написал:", - "forwarded_separator": "---------- Пересланное сообщение ----------", - "from_label": "От", - "date_label": "Дата", - "subject_label": "Тема" - }, - "pwa_install": { - "title": "Установить {appName}", - "description": "Установите наше приложение для быстрого доступа и работы офлайн.", - "not_now": "Не сейчас", - "install": "Установить", - "dont_remind": "Больше не напоминать", - "dismiss_aria": "Закрыть запрос на установку" - }, - "deep_link": { - "message_not_found": "Это письмо больше недоступно.", - "thread_not_found": "Эта переписка больше недоступна.", - "folder_not_found": "Эта папка больше недоступна.", - "event_not_found": "Это событие больше недоступно.", - "file_not_found": "Этот файл больше недоступен.", - "account_unavailable": "Войдите в эту учётную запись, чтобы открыть ссылку.", - "copy_message": "Скопировать ссылку на письмо", - "copy_conversation": "Скопировать ссылку на переписку", - "copy_event": "Скопировать ссылку на событие", - "copied": "Ссылка скопирована", - "copy_failed": "Не удалось скопировать ссылку" - }, - "plugins": { - "oauth_callback": { - "completing": "Завершение авторизации…", - "completing_message": "Пожалуйста, подождите, пока устанавливается соединение.", - "received_title": "Авторизация получена", - "received_message": "Вы можете вернуться на вкладку, где начали подключение.", - "failed_title": "Ошибка авторизации", - "denied": "В авторизации отказано.", - "failed_detail": "Ошибка авторизации: {error}", - "missing_params": "Отсутствует код авторизации или состояние.", - "delivery_failed": "Не удалось передать результат авторизации на вкладку приложения. Закройте это окно и попробуйте снова." - } - }, - "global_search": { - "title": "Искать везде", - "placeholder": "Поиск по почте, контактам, календарю, файлам...", - "scope_label": "Искать в", - "account_label": "Аккаунт", - "scope_all": "Всё", - "scope_mail": "Почта", - "scope_contacts": "Контакты", - "scope_calendar": "Календарь", - "scope_files": "Файлы", - "all_accounts": "Все аккаунты", - "show_all": "Показать все результаты", - "no_results": "Ничего не найдено по запросу «{query}»", - "server_hint": "Целые слова ищутся на сервере; части слов — среди уже загруженного", - "searching": "Поиск...", - "account_error": "Не удалось выполнить поиск в {account}", - "recent_searches": "Недавние запросы", - "recurring": "Повторяющееся", - "no_subject": "(no subject)", - "empty_hint": "Ищите почту, контакты, события и файлы во всех аккаунтах. Попробуйте from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Списком по дате", - "grouped": "По группам", - "load_more": "Загрузить ещё", - "preview_empty": "Выберите результат для предпросмотра", - "open_full": "Открыть", - "view_label": "Вид" - } -} +{ + "meta_description": "Минималистичный веб-клиент электронной почты, использующий протокол JMAP", + "login": { + "title": "Веб-почта", + "username_label": "Электронная почта", + "username_placeholder": "user@пример.рф", + "password_label": "Пароль", + "password_placeholder": "Введите пароль", + "jmap_endpoint_label": "Сервер JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Сервер должен разрешать CORS-запросы с этого домена.", + "jmap_server_label": "Сервер", + "jmap_server_auto_picked": "Сервер выбран по домену вашей электронной почты.", + "sign_in": "Войти", + "signing_in": "Вход...", + "loading": "Загрузка...", + "reconnecting": "Соединение потеряно. Попытка переподключения…", + "error": { + "invalid_credentials": "Неверный email или пароль. Проверьте учётные данные и повторите попытку.", + "invalid_token": "Сервер не принял этот токен доступа. Проверьте, что он скопирован полностью, не истёк и не был отозван.", + "connection_failed": "Не удалось подключиться к серверу. Проверьте интернет-соединение и попробуйте снова.", + "cors_blocked": "Сервер доступен, но блокирует запросы от других источников. Проверьте настройки CORS вашего JMAP-сервера и разрешите этот домен.", + "server_error": "Сервер временно недоступен. Повторите попытку позже.", + "generic": "Произошла непредвиденная ошибка. Если она повторяется, обратитесь к администратору.", + "totp_required": "Требуется код двухфакторной аутентификации. Пожалуйста, введите его ниже.", + "totp_invalid": "Неверный код аутентификации. Проверьте приложение аутентификатора и повторите попытку.", + "oauth_discovery_failed": "SSO включён, но поставщик удостоверений недоступен. Проверьте настройки OAuth.", + "token_exchange_failed": "Пароль и код приняты, но почтовый сервер отказался открыть сеанс для этого приложения. Попросите администратора проверить настройки OAuth-клиента." + }, + "show_password": "Показать пароль", + "hide_password": "Скрыть пароль", + "totp_toggle": "У меня есть код 2FA", + "token_toggle": "Войти с токеном доступа", + "password_toggle": "Войти с паролем", + "token_label": "Токен доступа", + "token_placeholder": "Вставьте ваш API-токен", + "token_hint": "Создайте API-токен в настройках вашего почтового провайдера и вставьте его сюда.", + "remember_me": "Запомнить меня", + "config_error": { + "title": "Ошибка конфигурации", + "fetch_failed": "Не удалось загрузить конфигурацию приложения. Повторите попытку позже.", + "server_not_configured": "Почтовый сервер не настроен. Обратитесь к администратору." + }, + "remove_from_history": "Удалить из истории", + "totp_label": "Код аутентификации", + "totp_placeholder": "000000", + "session_expired": "Срок действия сессии истёк. Войдите снова.", + "dismiss": "Закрыть", + "or": "или", + "sign_in_sso": "Войти через SSO", + "add_account_title": "Добавить аккаунт", + "add_account_subtitle": "Войдите с другим аккаунтом", + "cancel": "Отмена", + "website": "Веб-сайт", + "imprint": "Выходные данные", + "privacy_policy": "Политика конфиденциальности", + "try_demo": "Попробовать демо", + "demo_description": "Исследуйте с примерными данными - аккаунт не нужен", + "demo_launching": "Запуск демо...", + "demo_login_button": "Запустить демо", + "demo_tagline": "Попробуйте полнофункциональный почтовый клиент. Аккаунт не нужен.", + "demo_no_signup": "Регистрация не требуется - исследуйте свободно с примерными данными", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Завершение входа...", + "oauth_error": { + "title": "Ошибка аутентификации", + "invalid_state": "Ошибка проверки безопасности. Попробуйте войти снова.", + "missing_params": "Отсутствуют данные авторизации. Попробуйте войти снова.", + "token_exchange_failed": "Не удалось завершить аутентификацию. Повторите попытку.", + "access_denied": "Доступ запрещён. Обратитесь к администратору.", + "back_to_login": "Вернуться ко входу" + } + }, + "sidebar": { + "close": "Закрыть", + "compose": "Создать", + "compose_hint": "Создать (c)", + "search_placeholder": "Поиск почты...", + "search_placeholder_hint": "Поиск почты... (нажмите /)", + "storage": "Хранилище", + "storage_used": "Использовано", + "storage_free": "Свободно", + "storage_total": "Всего", + "sign_out": "Выйти", + "sign_out_of": "Выйти из {account}", + "sign_out_all": "Выйти из всех аккаунтов", + "add_account": "Добавить аккаунт", + "set_as_default": "Установить по умолчанию", + "switch_account": "Сменить аккаунт", + "contacts": "Контакты", + "calendar": "Календарь", + "settings": "Настройки", + "admin": "Админ", + "files": "Файлы", + "loading_mailboxes": "Загрузка папок...", + "push_connected": "Обновления в реальном времени активны", + "push_disconnected": "Обновления в реальном времени неактивны", + "keyboard_shortcuts": "Сочетания клавиш", + "theme": { + "light": "Светлая тема", + "dark": "Тёмная тема", + "system": "Системная тема" + }, + "language": { + "title": "Язык" + }, + "mailboxes": { + "inbox": "Входящие", + "sent": "Отправленные", + "drafts": "Черновики", + "trash": "Корзина", + "archive": "Архив", + "starred": "Помечённые", + "all_mail": "Вся почта", + "spam": "Спам", + "important": "Важные" + }, + "unified_inbox": "Общие входящие", + "unified_sent": "Все отправленные", + "unified_drafts": "Все черновики", + "unified_trash": "Все корзины", + "unified_archive": "Все архивы", + "unified_junk": "Весь спам", + "all_accounts": "Все аккаунты", + "all_accounts_loading": "Загрузка аккаунтов: {loaded} из {total}", + "unified_mailbox": "Общий почтовый ящик", + "expand": "Развернуть", + "collapse": "Свернуть", + "expand_tooltip": "Развернуть", + "collapse_tooltip": "Свернуть", + "mobile": { + "search": "Поиск", + "compose": "Создать", + "go_back": "Назад", + "toggle_menu": "Переключить меню" + }, + "clear_search": "Очистить поиск", + "vacation_active": "Автоответ на отпуск активен", + "demo_banner": "Режим демо", + "demo_reset": "Сбросить", + "demo_tour": "Тур", + "tags": "Теги", + "show_all_tags": "Показать все ({count})", + "show_fewer_tags": "Показать меньше", + "folders": "Папки", + "your_folders": "Ваши папки", + "create_folder": "Создать папку", + "show_more": "Ещё", + "show_fewer": "Свернуть", + "mail": "Почта", + "nav_label": "Навигация", + "add_app": "Приложения", + "shared": "Общие", + "shared_account_hidden": "Скрыто с боковой панели. Вернуть можно в Настройки › Макет.", + "notifications_muted": "Уведомления отключены", + "scheduled": "Запланировано", + "unified_all_unread": "Все непрочитанные", + "unified_all_starred": "Все помеченные", + "unified_all_mail": "Вся почта", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Ярлыки", + "create_label": "Создать ярлык", + "mv_tasks": "Задачи", + "mv_side_panel": "Боковая панель", + "mv_open_full": "Открыть полностью", + "mv_previous_day": "Предыдущий день", + "mv_next_day": "Следующий день", + "mv_add_another_account": "Добавить другой аккаунт", + "mv_manage_account": "Управление аккаунтом", + "mv_storage_used": "Использовано {percent}% из {total}" + }, + "protocol_handlers": { + "title": "Приложения по умолчанию", + "description": "Выберите, должны ли ссылки электронной почты и календаря открываться в Bulwark. Технически Bulwark регистрируется как обработчик протокола для ссылок mailto: и webcal:.", + "unsupported": "Этот браузер или это соединение не поддерживает ручную регистрацию обработчиков протоколов. Возможно, установленное PWA всё же можно использовать через настройки браузера или системы.", + "mailto_label": "Ссылки электронной почты", + "mailto_description": "Открывает ссылки mailto: в Bulwark с предварительно заполненным редактором письма.", + "protocol_open_mode_label": "При открытии ссылок протоколов", + "protocol_open_mode_description": "Выберите, будет ли Bulwark открывать ссылки mailto: и webcal: в новой вкладке или повторно использовать открытую сессию. Для варианта с активной сессией нужно разрешение на уведомления, чтобы можно было нажать на резервное уведомление и вывести Bulwark на передний план, если браузер блокирует фокус.", + "protocol_open_mode_active_session": "Открывать в активной сессии, если возможно", + "protocol_open_mode_new_tab": "Всегда открывать новую вкладку", + "focus_notification_title": "Открыть Bulwark", + "focus_notification_body": "Ссылка была открыта в Bulwark. Нажмите, чтобы вывести окно на передний план.", + "webcal_label": "Ссылки календаря", + "webcal_description": "Открывает ссылки webcal: в Bulwark с предварительно заполненным диалогом подписки на календарь.", + "register_mailto": "Зарегистрировать почтовое приложение", + "register_webcal": "Зарегистрировать приложение календаря", + "mailto_registered": "Запрошена регистрация обработчика электронной почты", + "webcal_registered": "Запрошена регистрация обработчика календаря", + "registration_failed": "Не удалось зарегистрировать обработчик протокола", + "opening_mailto": "Открытие редактора...", + "opening_webcal": "Открытие календаря...", + "browser_note": "Браузер или операционная система может запросить подтверждение и может потребовать, чтобы Bulwark был установлен, прежде чем его можно будет выбрать приложением по умолчанию.", + "select_account_title": "Выберите аккаунт", + "select_mailto_account": "Выберите, в каком аккаунте открыть эту ссылку электронной почты.", + "select_webcal_account": "Выберите, в каком аккаунте открыть эту ссылку календаря.", + "select_account_note": "Этот выбор применяется только к этой ссылке протокола.", + "detail_to": "Кому", + "detail_subject": "Тема", + "detail_no_subject": "Без темы", + "detail_calendar": "Календарь", + "detail_source": "Источник", + "active_account": "Активен", + "switching_account": "Переключение аккаунта..." + }, + "sidebar_apps": { + "modal_title": "Приложения боковой панели", + "add_new": "Добавить приложение", + "edit_app": "Редактировать приложение", + "name_label": "Название", + "name_placeholder": "Моё приложение", + "name_required": "Название обязательно", + "url_label": "URL-адрес", + "url_required": "URL обязателен", + "url_invalid": "Введите корректный URL http или https", + "icon_label": "Иконка", + "icon_required": "Иконка обязательна", + "open_mode_label": "Режим открытия", + "open_new_tab": "Новая вкладка", + "open_inline": "Встроенный", + "cancel": "Отмена", + "add": "Добавить", + "update": "Обновить", + "delete": "Удалить", + "delete_confirm_title": "Удалить приложение", + "delete_confirm": "Вы уверены, что хотите удалить \"{name}\"?", + "no_apps": "Приложения ещё не добавлены", + "no_apps_hint": "Добавьте пользовательские приложения и ссылки в боковую панель", + "managed_title": "Предоставлено администратором", + "managed_description": "Эти приложения настроены для всех и не могут быть изменены.", + "managed_badge": "Управляемое", + "search_icons": "Поиск иконок...", + "show_popular": "Популярные", + "show_all": "Все", + "no_icons_found": "Иконки не найдены", + "inline_badge": "Встроенный", + "tab_badge": "Вкладка", + "show_on_mobile": "Показывать на мобильном" + }, + "email_list": { + "columns": { + "from": "От", + "to": "Кому", + "subject": "Тема", + "date": "Дата", + "size": "Размер" + }, + "no_emails": "Сообщений не найдено", + "no_emails_description": "Папка пуста", + "no_search_results": "Результатов не найдено", + "no_search_results_description": "Попробуйте изменить поисковый запрос или фильтры", + "loading": "Загрузка писем...", + "unread": "непрочитанные", + "to_me": "Мне", + "to_recipients": "{count} получателям", + "and_others": "и ещё {count}", + "draft": "Черновик", + "starred": "Помечено", + "pinned": "Закреплено", + "replied": "Отвечено", + "forwarded": "Переслано", + "has_attachment": "С вложением", + "conversations_range": "{from}–{to, number} из {total, number}", + "conversations_count": "{count} из {total} бесед", + "conversations_count_plus": "{count}+ бесед", + "conversations_count_simple": "{count} бесед", + "no_conversations": "Нет бесед", + "loading_more": "Загрузка писем...", + "no_more_emails": "Больше писем нет", + "batch_actions": { + "select": "Выбрать письма", + "select_all": "Выбрать все", + "selected_messages": "Выбрано {count, plural, one {1 письмо} other {# письма}}", + "mark_read": "Отметить прочитанными", + "mark_unread": "Отметить непрочитанными", + "delete": "Удалить", + "delete_confirm_title": "Удалить письма", + "delete_confirm_message": "Вы уверены, что хотите удалить {count, plural, one {1 письмо} other {# писем}}?", + "clear_selection": "Снять выделение" + }, + "permanent_delete": "Удалить навсегда", + "permanent_delete_confirm_title": "Удалить навсегда", + "permanent_delete_confirm_message": "Это письмо будет удалено навсегда. Действие нельзя отменить.", + "permanent_delete_confirm_batch_message": "{count, plural, one {1 письмо} other {# писем}} будут удалены навсегда. Действие нельзя отменить.", + "empty_folder": { + "button": "Очистить папку", + "confirm_title": "Очистить папку", + "confirm_message": "Все письма в этой папке будут удалены навсегда. Действие нельзя отменить.", + "confirm_message_trash": "Все письма из этой папки будут перемещены в корзину.", + "confirm_button": "Очистить папку", + "junk_hint": "Вы можете очистить папку Спам для постоянного удаления всех сообщений.", + "trash_hint": "Вы можете очистить Корзину для постоянного удаления всех сообщений." + }, + "no_scheduled_emails": "Нет запланированных писем", + "no_scheduled_emails_description": "Сообщения, запланированные на позже, появятся здесь.", + "scheduled_count": "{count, plural, one {# запланированное письмо} few {# запланированных письма} other {# запланированных писем}}", + "scheduled_actions_hint": "Запланированные сообщения можно отменить, перенести или изменить через действия планирования.", + "cancel_scheduled_send": "Отменить отправку", + "reschedule_send": "Перенести", + "cancel_and_edit": "Отменить и изменить", + "cancel_and_compose_again": "Отменить и написать заново", + "reschedule_prompt": "Введите новую дату/время, например 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Отправитель запрашивает уведомление о прочтении:", + "send": "Отправить уведомление", + "ignore": "Игнорировать", + "sent": "Уведомление о прочтении отправлено.", + "send_failed": "Не удалось отправить уведомление о прочтении", + "mdn_subject": "Прочитано: {subject}", + "mdn_body": "Это уведомление о прочтении сообщения, отправленного вами на адрес {recipient}.\n\nПримечание: это уведомление лишь подтверждает, что сообщение было показано на компьютере получателя. Оно не гарантирует, что получатель прочитал или понял содержимое." + }, + "no_email_selected": "Письмо не выбрано", + "no_email_description": "Выберите письмо из списка, чтобы просмотреть его здесь", + "no_conversation_selected": "Беседа не выбрана", + "no_conversation_description": "Выберите беседу из списка, чтобы прочитать её здесь", + "compose": "Написать", + "compose_hint": "Написать новое сообщение", + "no_subject": "(Без темы)", + "no_body_content": "(Содержимое отсутствует)", + "no_preview_available": "Предварительный просмотр недоступен", + "show_quoted_text": "Показать цитируемый текст", + "hide_quoted_text": "Скрыть цитируемый текст", + "loading_email": "Загрузка письма...", + "loading": "Загрузка...", + "reply": "Ответить", + "reply_all": "Ответить всем", + "forward": "Переслать", + "delete": "Удалить", + "archive": "Архивировать", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Пометить", + "unstar": "Снять пометку", + "mark_unread": "Отметить непрочитанным", + "mark_read": "Отметить прочитанным", + "unread": "Не прочитано", + "read": "Прочитано", + "spam_short": "Спам", + "not_spam_short": "Не спам", + "move": "Переместить", + "print": "Распечатать", + "view_source": "Просмотреть исходный код", + "fullscreen": "Во весь экран", + "exit_fullscreen": "Выйти из полноэкранного режима", + "view_in_dark_mode": "Просмотр в тёмном режиме", + "view_in_light_mode": "Просмотр в светлом режиме", + "export_email": "Экспортировать как .eml", + "forward_as_attachment": "Переслать как вложение", + "import_email": "Импортировать .eml или .zip", + "keyboard_shortcuts": "Сочетания клавиш (?)", + "email_source": "Исходный код письма", + "draft_banner": "Это письмо является черновиком", + "edit_draft": "Редактировать", + "copy_source": "Копировать в буфер обмена", + "source_copied": "Исходный код скопирован в буфер обмена", + "attachments": "Вложения", + "important": "Важное", + "download": "Скачать", + "download_all": "Скачать все", + "from": "От", + "to": "Кому", + "cc": "Копия", + "bcc": "Скрытая копия", + "date": "Дата", + "subject": "Тема", + "show_details": "Показать детали", + "hide_details": "Скрыть детали", + "external_content_warning": "Изображения и внешнее содержимое заблокированы", + "load_external_content": "Загрузить изображения", + "trust_sender": "Всегда доверять этому отправителю", + "sender_check": { + "unverified_label": "Отправитель не подтверждён", + "failed_label": "Проверка отправителя не пройдена", + "unverified": "Это сообщение якобы отправлено с домена {domain}, но ничто этого не подтверждает. У него нет действительной подписи домена {domain}, и оно пришло не с сервера, которому домен {domain} разрешает отправку.", + "unverified_sent_from": "Это сообщение якобы отправлено с домена {domain}, но на самом деле оно отправлено с домена {host} и не имеет действительной подписи домена {domain}.", + "failed": "Это сообщение якобы отправлено с домена {domain}, но оно не прошло проверку отправителя этого домена. Возможно, оно поддельное.", + "failed_sent_from": "Это сообщение якобы отправлено с домена {domain}, но на самом деле оно отправлено с домена {host} и не прошло проверку отправителя домена {domain}. Возможно, оно поддельное.", + "caution": "Будьте осторожны со ссылками, вложениями и просьбами указать пароль или платёжные данные." + }, + "back_to_list": "Вернуться к списку", + "view_contact": "Просмотреть контакт", + "message_details": "Детали сообщения", + "more_reply_options": "Дополнительные параметры ответа", + "set_tag": "Установить тег", + "tag": "Тег", + "more_actions": "Другие действия", + "previous": "Пред.", + "next": "След.", + "move_to": "Переместить в...", + "remove_tag": "Удалить тег", + "tag_filter_placeholder": "Фильтр тегов", + "tag_no_matches": "Подходящих тегов нет", + "more_count": "+{count} ещё", + "characters_count": "{count} символов", + "quick_reply_placeholder": "Написать быстрый ответ...", + "more_options": "Другие параметры", + "sending": "Отправка...", + "security_authentication": "Безопасность и аутентификация", + "technical_details": "Технические детали", + "message_id_label": "Идентификатор сообщения:", + "reply_to_label": "Адрес для ответа:", + "delivery_time_label": "Время доставки:", + "conversation_part_label": "Часть беседы:", + "previous_messages": "{count} предыдущее сообщение", + "previous_messages_plural": "{count} предыдущих сообщений", + "time": { + "day": "день", + "days": "дней", + "hour": "час", + "hours": "часов", + "minute": "минута", + "minutes": "минут" + }, + "unknown_sender": "Неизвестно", + "recipient_me": "я", + "recipient_and_others": "{name} и ещё {count}", + "recipient_to_prefix": "Кому:", + "authentication": { + "title": "Аутентификация", + "status": { + "verified": "Подтверждено", + "warning": "Предупреждение", + "none": "Не аутентифицировано" + }, + "spf": { + "pass": "SPF прошёл", + "fail": "SPF не прошёл", + "none": "SPF отсутствует" + }, + "dkim": { + "pass": "DKIM действителен", + "fail": "DKIM недействителен", + "none": "DKIM отсутствует" + }, + "dmarc": { + "pass": "DMARC прошёл", + "fail": "DMARC не прошёл", + "none": "DMARC отсутствует" + }, + "spam_score": "Оценка спама", + "tooltip_spf": "Sender Policy Framework: проверяет, имеет ли отправляющий сервер право отправлять почту от имени домена", + "tooltip_dkim": "DomainKeys Identified Mail: подтверждает с помощью криптографической подписи, что письмо не было изменено при передаче", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: обеспечивает согласованность SPF и DKIM с доменом отправителя и устанавливает политику для сбоев", + "policy": "Политика", + "result": { + "pass": "Пройдено", + "fail": "Не пройдено", + "softfail": "Частичный сбой", + "neutral": "Нейтрально", + "permerror": "Постоянная ошибка", + "temperror": "Временная ошибка", + "none": "Нет" + } + }, + "headers": { + "routing": "Маршрутизация", + "received": "Получено", + "message_id": "Идентификатор сообщения", + "list_info": "Информация о рассылке" + }, + "tooltips": { + "reply": "Ответить (r)", + "reply_all": "Ответить всем (a)", + "forward": "Переслать (f)", + "archive": "Архивировать (e)", + "delete": "Удалить (# или Del)", + "star": "Пометить (s)", + "unstar": "Снять пометку (s)", + "compose": "Создать (c)", + "previous": "Предыдущее письмо", + "next": "Следующее письмо", + "edit_draft": "Редактировать черновик" + }, + "spam": { + "button_title": "Пожаловаться на спам", + "not_spam_title": "Отметить как не спам", + "toast_success": "Перемещено в Спам", + "toast_batch": "{count} писем перемещено в Спам", + "toast_undo": "Отменить", + "toast_not_spam_success": "Перемещено во Входящие", + "toast_not_spam_batch": "{count} писем перемещено во Входящие", + "error": "Не удалось пожаловаться на спам", + "error_not_spam": "Не удалось восстановить письмо" + }, + "unsubscribe_banner": { + "label": "Рассылка", + "button": "Отписаться", + "confirm_title": "Отписаться от этого отправителя?", + "confirm_button": "Подтвердить", + "cancel": "Отмена", + "success_http": "Страница отписки открыта в новой вкладке", + "success_mailto": "Письмо для отписки отправлено", + "confirm_message_http": "Страница отписки откроется в новой вкладке.", + "confirm_message_mailto": "Отправителю будет отправлено письмо для отписки.", + "error": "Не удалось отписаться", + "dismiss": "Закрыть" + }, + "calendar_invitation": { + "loading": "Загрузка деталей события…", + "title": "Приглашение в календарь", + "published_title": "Опубликованное событие", + "response_title": "Ответ на событие", + "update_title": "Обновление события", + "counter_title": "Встречное предложение", + "refresh_title": "Запрос обновления", + "declined_counter_title": "Встречное предложение отклонено", + "cancelled_title": "Событие отменено", + "organizer": "Организовано {name}", + "organizer_label": "Организовано", + "attendees": "{count, plural, one {# участник} other {# участников}}", + "accept": "Принять", + "maybe": "Возможно", + "decline": "Отклонить", + "add_to_calendar": "Добавить в календарь", + "added": "Добавлено в календарь", + "rsvp_sent": "Ответ отправлен", + "parse_error": "Не удалось прочитать приглашение", + "action_failed": "Не удалось выполнить действие с календарём.", + "no_calendar": "Календарь недоступен", + "published_info": "Это событие опубликовано для ознакомления.", + "response_info": "Это сообщение содержит ответ участника.", + "response_info_organizer": "Этот ответ участника обновляет ваше событие.", + "update_info": "Это сообщение обновляет существующее событие.", + "counter_info": "Это сообщение предлагает изменения в событии.", + "counter_info_organizer": "Этот участник предложил изменения в вашем событии.", + "refresh_info": "Это сообщение запрашивает последние детали события.", + "refresh_info_organizer": "Участник запросил последние детали события.", + "declined_counter_info": "Организатор отклонил встречное предложение.", + "authentication_failed_info": "Проверка подлинности почты для этого приглашения не пройдена. Будьте осторожны с действиями в календаре.", + "authentication_missing_info": "Это приглашение не содержит подтверждённой аутентификации почты. Если что-то кажется необычным, уточните детали у организатора.", + "sender_mismatch_info": "Это приглашение отправлено от {sender}, тогда как организатором в данных календаря указан {organizer}.", + "sender_mismatch_unverified_info": "Это приглашение отправлено от {sender}, тогда как организатором указан {organizer}, и сообщение не удалось проверить.", + "organizer_role": "Вы являетесь организатором этого события", + "your_response": "Ваш ответ: {status}", + "response_needed": "Требуется ответ", + "response_accepted": "Принято", + "response_tentative": "Под вопросом", + "response_declined": "Отклонено", + "response_delegated": "Делегировано", + "actor_sent_info": "Отправлено {name}.", + "actor_response_info": "{name} ответил {status}.", + "actor_counter_info": "{name} предложил изменения в этом событии.", + "actor_refresh_info": "{name} запросил последние детали события.", + "actor_declined_counter_info": "{name} отклонил встречное предложение.", + "actor_note": "Примечание: {comment}", + "actor_unknown": "Кто-то", + "proposed_changes": "Предложенные изменения", + "change_title": "Заголовок", + "change_time": "Время", + "change_location": "Местоположение", + "change_description": "Описание", + "change_empty": "Нет", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Применить предложенные изменения", + "proposal_applied": "Предложенные изменения применены.", + "review_proposal": "Просмотреть предложение", + "review_request": "Просмотреть запрос", + "view_in_calendar": "Открыть в календаре", + "select_calendar": "Выбрать календарь", + "already_in_calendar": "Уже в вашем календаре", + "request_info": "Вас пригласили на это событие. Ответьте, чтобы организатор знал о вашей доступности.", + "cancel_info": "Организатор отменил это событие.", + "event_updated": "Обновление №{sequence}", + "event_status_tentative": "Под вопросом", + "event_status_cancelled": "Отменено", + "collapse": "Скрыть детали", + "expand": "Показать детали" + }, + "send": "Отправить", + "more": "ещё", + "details": { + "recipients_routing": "Получатели и маршрутизация", + "authentication_security": "Аутентификация и безопасность", + "identifiers_threading": "Идентификаторы и обсуждения", + "mailing_list": "Список рассылки", + "message_properties": "Свойства сообщения", + "sent": "Отправлено", + "received": "Получено", + "delivery_time": "Время доставки", + "in_reply_to": "В ответ на", + "references": "Ссылки", + "thread_id": "ID обсуждения", + "size": "Размер", + "mime_type": "MIME-тип", + "attachments_summary": "{count} файлов · {size}", + "list_id": "ID списка", + "list_help": "Справка списка", + "list_post": "Публикация в список", + "list_unsubscribe": "Отписаться", + "iprev": "Обратный DNS", + "spam_status": "Статус спама", + "ai_verdict": "Вердикт ИИ", + "account": "Аккаунт", + "no_subject": "(без темы)" + }, + "scheduled_banner": "Запланировано к отправке {date}", + "scheduled_send_created": "Письмо запланировано к отправке", + "sending_in": "Отправка через {seconds} с", + "undo_send_failed": "Не удалось отменить. Возможно, сообщение уже отправлено.", + "cancel_scheduled_send": "Отменить отправку", + "reschedule_send": "Перенести", + "cancel_and_edit": "Отменить и изменить", + "cancel_and_compose_again": "Отменить и написать заново", + "reschedule_prompt": "Введите новую дату/время, например 2026-05-04T15:30", + "scheduled_actions_only": "В представлении запланированных доступны только действия планирования", + "undo_send_scheduled": "Сообщение запланировано к отправке", + "undo_send": "Отменить отправку", + "contact_sidebar": { + "title": "Контакт", + "close": "Закрыть боковую панель", + "action_email": "Письмо", + "action_email_title": "Отправить письмо", + "action_copy": "Копировать", + "action_copy_title": "Копировать адрес", + "action_edit_title": "Редактировать контакт", + "section_emails": "Электронная почта", + "section_phones": "Телефоны", + "section_organizations": "Организации", + "section_addresses": "Адреса", + "section_notes": "Заметки", + "not_in_contacts": "Нет в ваших контактах", + "add_to_contacts": "Добавить в контакты", + "copied": "Скопировано!", + "copy_failed": "Не удалось скопировать", + "view_contact": "Открыть контакт", + "view_details": "Подробнее" + }, + "send_now": "Отправить сейчас", + "verification_code": { + "copy": "Скопировать код {code}", + "copied": "Код скопирован", + "copy_failed": "Не удалось скопировать код" + }, + "mv_position": "{index} из {total}", + "plugin_panel_show": "Показать панель плагина", + "plugin_panel_hide": "Скрыть панель плагина" + }, + "email_composer": { + "select_from_directory": "Выбрать из справочника", + "minimize": "Свернуть", + "maximize": "Во весь экран", + "restore": "Восстановить", + "new_message": "Новое письмо", + "signature_edit_hint": "Дважды щёлкните, чтобы изменить подпись", + "reply": "Ответить", + "reply_all": "Ответить всем", + "forward": "Переслать", + "reply_to": "Ответить", + "reply_all_to": "Ответить всем", + "forward_message": "Переслать", + "from": "От", + "to": "Кому", + "cc": "Копия", + "bcc": "Скрытая копия", + "subject": "Тема", + "body_placeholder": "Напишите сообщение...", + "send": "Отправить", + "cancel": "Отмена", + "attach": "Прикрепить", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Прикрепить из Файлов", + "files_picker": { + "attach": "{count, plural, =0 {Прикрепить} one {Прикрепить # файл} few {Прикрепить # файла} many {Прикрепить # файлов} other {Прикрепить # файла}}", + "empty_folder": "Эта папка пуста", + "load_failed": "Не удалось загрузить ваши файлы" + }, + "discard": "Удалить", + "discard_draft_title": "Удалить черновик?", + "discard_draft_confirm": "Есть несохранённые изменения. Удалить этот черновик?", + "discard_draft_message": "Черновик будет удалён.", + "saving": "Сохранение...", + "draft_saved": "Черновик сохранён", + "save_failed": "Не удалось сохранить", + "to_placeholder": "Адреса получателей", + "cc_placeholder": "Получатели копии", + "bcc_placeholder": "Получатели скрытой копии", + "subject_placeholder": "Тема", + "cc_label": "Копия:", + "bcc_label": "Скрытая копия:", + "subject_label": "Тема:", + "file_size_kb": "КБ", + "prefix": { + "forward": "Пересл:", + "reply": "Ответ:" + }, + "forward_batch_count": "{count, plural, one {1 письмо} few {# письма} many {# писем} other {# писем}}", + "no_subject": "(Без темы)", + "unknown_sender": "Неизвестный", + "quote": { + "reply_header": "{date}, {sender} написал(а):", + "forward_header": "---------- Пересланное сообщение ----------", + "from": "От: {sender}", + "date": "Дата: {date}", + "subject": "Тема: {subject}", + "to": "Кому: {recipients}" + }, + "remove_sub_address": "Удалить суб-адрес", + "from_override": { + "toggle_off": "Переопределить", + "toggle_on": "Отменить переопределение", + "toggle_tooltip": "Свободно редактируйте имя и адрес отправителя. Письмо по-прежнему отправляется через вашу учётную запись.", + "name_label": "Имя отправителя", + "name_placeholder": "Имя", + "email_label": "Email отправителя", + "email_placeholder": "alias@example.com", + "envelope_notice": "Если ваш сервер не принимает этот адрес как отправителя конверта, вместо него используется {identity}, и получатели увидят его в заголовке Return-Path." + }, + "save_as_template": "Сохранить как шаблон", + "attach_from_device": "Загрузить с этого устройства", + "templates_menu": "Шаблоны", + "insert_template": "Вставить шаблон", + "options": "Параметры", + "option_plain_text": "Обычный текст", + "option_plain_text_hint": "Отправить без форматирования", + "option_read_receipt": "Запросить уведомление о прочтении", + "option_read_receipt_hint": "Попросить получателя подтвердить открытие", + "option_dsn": "Уведомление о доставке", + "option_dsn_hint": "Получить отчёт, когда сервер доставит письмо", + "option_require_tls": "Требовать шифрованную доставку", + "option_require_tls_hint": "Доставлять только через TLS, иначе вернуть", + "option_priority": "Важность", + "option_priority_high": "Высокая", + "option_priority_normal": "Обычная", + "option_priority_low": "Низкая", + "validation": { + "recipient_required": "Укажите получателя для отправки", + "body_required": "Напишите сообщение или прикрепите файл", + "invalid_recipients": "Не отправлено - проверьте эти адреса: {addresses}", + "attachments_uploading": "Вложения ещё загружаются — отправка начнётся, как только загрузка завершится", + "attachment_upload_failed": "Не отправлено — не удалось загрузить вложение. Удалите его и попробуйте снова." + }, + "upload_progress": "Загрузка {uploaded} / {total}", + "upload_cancel": "Отменить загрузку", + "upload_failed": "Не удалось загрузить {filename}", + "attachment_too_large": "«{name}» больше, чем разрешает сервер ({max} на файл)", + "attachments_total_too_large": "Вложения превысят лимит сервера в {max} на письмо", + "drop_files": "Перетащите файлы для прикрепления", + "show_less": "Свернуть", + "send_failed": "Не удалось отправить письмо", + "send_timeout": "Сервер перестал отвечать. Прежде чем отправлять снова, проверьте папку «Отправленные» — письмо могло уже уйти.", + "send_offline": "Похоже, вы не в сети. Сообщение сохранено, отправьте его снова, когда подключение восстановится.", + "formatting": "Форматирование", + "send_recipients_rejected": "Не отправлено — сервер отклонил всех получателей.", + "send_plugin_failed": "Не отправлено: плагин, который отвечает за отправку, завершился с ошибкой или работал слишком долго. Проверьте папку «Отправленные», прежде чем отправлять снова.", + "send_some_recipients_rejected": "Отправлено, но не этим получателям — сервер их отклонил.", + "continue_draft": "Продолжить черновик", + "close_draft_title": "Сохранить или удалить черновик?", + "close_draft_message": "Есть несохранённые изменения. Сохранить как черновик или удалить?", + "smime_sign_on": "Подпись S/MIME включена", + "smime_sign_off": "Включить подпись S/MIME", + "smime_encrypt_on": "Шифрование S/MIME включено", + "smime_encrypt_off": "Включить шифрование S/MIME", + "smime_encrypt_unavailable": "Шифрование S/MIME недоступно - отсутствует сертификат получателя", + "smime_unlock_title": "Разблокировать ключ S/MIME", + "smime_unlock_message": "Введите парольную фразу для разблокировки вашего ключа подписи S/MIME.", + "smime_unlock_button": "Разблокировать", + "smime_passphrase_placeholder": "Парольная фраза", + "forgot_attachment": { + "title": "Вы забыли прикрепить файл?", + "message": "В вашем сообщении упоминается \"{keyword}\", но файл не прикреплён. Отправить всё равно?", + "send_anyway": "Отправить всё равно", + "back": "Вернуться к редактированию" + }, + "empty_subject": { + "title": "Отправить без темы?", + "message": "У этого письма нет темы. Всё равно отправить?", + "dont_ask_again": "Больше не спрашивать", + "send_anyway": "Отправить всё равно", + "back": "Вернуться к редактированию" + }, + "add_link": "Добавить ссылку", + "link_url_prompt": "Введите URL", + "sending": "Отправка...", + "schedule_send": "Запланировать отправку", + "schedule_send_description": "Выберите, когда сервер должен отправить это сообщение.", + "schedule_preset": { + "tomorrow_morning": "Завтра утром", + "tomorrow_afternoon": "Завтра днём", + "monday_morning": "В понедельник утром" + }, + "schedule_send_required": "Выберите дату и время.", + "schedule_send_invalid": "Введите действительные дату и время.", + "schedule_send_future": "Выберите дату и время в будущем.", + "schedule_send_too_late": "Это время позже, чем разрешает сервер.", + "schedule_send_unsupported": "Запланированная отправка не поддерживается для этой учетной записи.", + "schedule_send_cleanup_warning": "Запланированная отправка создана, но очистка черновика не удалась.", + "send_delay_unsupported": "Задержка отправки не поддерживается для этой учетной записи.", + "send_delay_unsupported_confirm": "Эта учетная запись не поддерживает задержку отправки. Отправить сразу?", + "recipient_edit_email": "Изменить адрес эл. почты", + "recipient_edit_name": "Изменить отображаемое имя", + "recipient_email_placeholder": "Адрес эл. почты", + "recipient_name_placeholder": "Отображаемое имя", + "invalid_recipient": "Недействительный адрес электронной почты", + "autocomplete_search_server": "Искать на сервере", + "autocomplete_searching": "Поиск...", + "mention_recipients": "Получатели", + "mention_card_not_recipient": "Нет в полях «Кому» и «Копия», поэтому письмо не получит.", + "mention_card_add": "Добавить в {field}", + "mention_card_remove": "Убрать упоминание", + "toolbar": { + "bold": "Жирный", + "italic": "Курсив", + "underline": "Подчёркнутый", + "strikethrough": "Зачёркнутый", + "text_color": "Цвет текста", + "remove_color": "Убрать цвет", + "heading_1": "Заголовок 1", + "heading_2": "Заголовок 2", + "bullet_list": "Маркированный список", + "ordered_list": "Нумерованный список", + "quote": "Цитата", + "code_block": "Блок кода", + "align_left": "По левому краю", + "align_center": "По центру", + "align_right": "По правому краю", + "text_direction": "Направление текста (RTL/LTR)", + "link": "Ссылка", + "table": "Таблица", + "clear_formatting": "Очистить форматирование", + "undo": "Отменить", + "redo": "Повторить", + "add_row_above": "Вставить строку выше", + "add_row_below": "Вставить строку ниже", + "add_column_before": "Вставить столбец слева", + "add_column_after": "Вставить столбец справа", + "delete_row": "Удалить строку", + "delete_column": "Удалить столбец", + "toggle_header_row": "Переключить строку заголовка", + "delete_table": "Удалить таблицу", + "pick_size": "Выбрать размер", + "font_size": "Размер шрифта", + "font_size_default": "По умолчанию", + "background_color": "Цвет фона", + "remove_background_color": "Убрать цвет фона" + }, + "send_filing_warning": "Отправлено - но последующая очистка не удалась, может остаться устаревший черновик.", + "pop_out": "Открыть ответ в отдельном окне", + "send_guard_failed": "Не отправлено: плагин, проверяющий письма перед отправкой, завершился с ошибкой или не ответил." + }, + "confirm_dialog": { + "confirm": "Подтвердить", + "cancel": "Отмена" + }, + "common": { + "loading": "Загрузка...", + "error": "Ошибка", + "success": "Успешно", + "cancel": "Отмена", + "save": "Сохранить", + "delete": "Удалить", + "edit": "Редактировать", + "close": "Закрыть", + "search": "Поиск", + "refresh": "Обновить", + "settings": "Настройки", + "help": "Помощь", + "logout": "Выйти", + "yes": "Да", + "no": "Нет", + "unknown": "Неизвестно", + "app_title": "Веб-почта", + "reconnecting": "Соединение потеряно. Попытка переподключения…", + "rate_limited_title": "Аутентификация на сервере временно ограничена.", + "rate_limited_detail": "Bulwark приостановил фоновые запросы, чтобы избежать блокировки. Повтор через {seconds}с.", + "rate_limited_action_title": "Запрос приостановлен, чтобы избежать блокировки.", + "rate_limited_action_detail": "Bulwark ждет окончания серверного тайм-аута перед отправкой новых аутентифицированных запросов. Повторите через {seconds}с.", + "pull_to_refresh": "Потяните, чтобы обновить", + "release_to_refresh": "Отпустите, чтобы обновить", + "refreshing": "Обновление…" + }, + "notifications": { + "email_sent": "Письмо успешно отправлено", + "email_deleted": "Письмо удалено", + "email_archived": "Письмо архивировано", + "emails_deleted": "{count, plural, one {# письмо удалено} few {# письма удалены} many {# писем удалено} other {# письма удалено}}", + "emails_archived": "{count, plural, one {# письмо архивировано} few {# письма архивированы} many {# писем архивировано} other {# письма архивировано}}", + "error_archiving": "Не удалось архивировать письмо", + "error_updating": "Не удалось обновить письмо", + "email_starred": "Письмо помечено", + "email_unstarred": "Пометка снята", + "email_marked_read": "Письмо отмечено прочитанным", + "email_marked_unread": "Письмо отмечено непрочитанным", + "copied_to_clipboard": "Скопировано в буфер обмена", + "source_copied": "Исходный код скопирован в буфер обмена", + "error_sending": "Не удалось отправить письмо", + "error_deleting": "Не удалось удалить письмо", + "error_loading": "Не удалось загрузить письма", + "new_email": "Новое письмо", + "new_email_from": "От {sender}", + "click_to_view": "Нажмите для просмотра", + "email_moved": "Письмо перемещено", + "emails_moved": "{count} писем перемещено", + "moved_to_mailbox": "Перемещено в {mailbox}", + "move_failed": "Ошибка перемещения", + "removed_from_folder": "Убрано из папки {mailbox}", + "remove_from_folder_failed": "Не удалось убрать из этой папки", + "emails_copied": "{count} писем скопировано", + "copy_failed": "Ошибка копирования", + "move_error": "Не удалось переместить письма в выбранную папку", + "email_tagged": "Письмо помечено тегом", + "emails_tagged": "{count} писем помечено тегом", + "tag_failed": "Ошибка тегирования", + "identity_created": "Удостоверение успешно создано", + "identity_updated": "Удостоверение успешно обновлено", + "identity_deleted": "Удостоверение удалено", + "identity_set_primary": "Основное удостоверение обновлено", + "identity_create_failed": "Не удалось создать удостоверение: {error}", + "identity_update_failed": "Не удалось обновить удостоверение: {error}", + "identity_delete_failed": "Не удалось удалить удостоверение: {error}", + "identity_unauthorized": "У вас нет прав для отправки с этого email-адреса", + "identity_not_found": "Удостоверение не найдено", + "vacation_saved": "Настройки отсутствия сохранены", + "vacation_save_failed": "Не удалось сохранить настройки отсутствия", + "filters_saved": "Фильтры успешно сохранены", + "filters_save_failed": "Не удалось сохранить фильтры", + "filters_deleted": "Правило фильтра удалено", + "templates_exported": "Шаблоны успешно экспортированы", + "templates_imported": "{count, plural, one {# шаблон} other {# шаблонов}} импортировано", + "templates_import_errors": "Некоторые шаблоны не удалось импортировать", + "templates_import_empty": "В файле не найдено шаблонов", + "export_email_error": "Не удалось экспортировать письмо", + "import_email_success": "Письмо успешно импортировано", + "import_email_error": "Не удалось импортировать письмо", + "rule_created": "Правило создано", + "rule_merged": "Добавлено в правило «{name}»", + "rule_already_covered": "Уже покрывается правилом «{name}»", + "rule_apply_existing": "{count, plural, one {Применить к # имеющемуся письму} few {Применить к # имеющимся письмам} many {Применить к # имеющимся письмам} other {Применить к # имеющегося письма}}", + "rule_applied": "{count, plural, =0 {Ни одно письмо не пришлось менять} one {Правило применено к # письму} few {Правило применено к # письмам} many {Правило применено к # письмам} other {Правило применено к # письма}}", + "rule_apply_failed": "Не удалось применить правило к имеющимся письмам", + "rule_edit": "Изменить правило", + "rule_undo": "Отменить", + "rule_undone": "Изменение правила отменено", + "rule_undo_failed": "Не удалось отменить изменение правила", + "rule_undo_conflict": "Ваши фильтры с тех пор изменились. Отмените изменение в настройках фильтров.", + "rule_folder_failed": "Не удалось создать папку", + "vacation_filters_save_failed": "Настройки отсутствия сохранены, но пересылку и получателей ответа сохранить не удалось" + }, + "date": { + "today": "Сегодня", + "yesterday": "Вчера", + "this_week": "На этой неделе", + "last_week": "На прошлой неделе", + "this_month": "В этом месяце", + "older": "Давно", + "just_now": "Только что", + "minutes_ago": "{count} минуту назад", + "minutes_ago_plural": "{count} минут назад", + "hours_ago": "{count} час назад", + "hours_ago_plural": "{count} часов назад", + "days_ago": "{count} день назад", + "days_ago_plural": "{count} дней назад" + }, + "language": { + "title": "Язык", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Выбрать язык", + "switch_to_english": "Переключить на английский", + "switch_to_french": "Переключить на французский", + "switch_to_japanese": "Переключить на японский", + "switch_to_spanish": "Переключить на испанский", + "switch_to_italian": "Переключить на итальянский", + "switch_to_german": "Переключить на немецкий", + "switch_to_dutch": "Переключить на нидерландский", + "switch_to_portuguese": "Переключить на португальский", + "switch_to_russian": "Переключить на русский", + "switching": "Смена языка...", + "switch": "Сменить язык", + "current": "Текущий язык", + "polish": "Polski", + "switch_to_polish": "Переключиться на польский", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Переключить на фарси", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Переключить на монгольский" + }, + "settings": { + "title": "Настройки", + "back_to_mail": "Вернуться к почте", + "save_success": "Настройки успешно сохранены", + "import_success": "Настройки успешно импортированы", + "import_error": "Не удалось импортировать настройки", + "reset_confirm": "Вы уверены, что хотите сбросить все настройки до значений по умолчанию?", + "unsaved_changes": "У вас есть несохранённые изменения", + "discard_changes": "Отменить несохранённые изменения?", + "discard": "Отменить", + "keep_editing": "Продолжить редактирование", + "tabs": { + "appearance": "Внешний вид", + "language": "Язык, регион и время", + "email": "Поведение почты", + "composer": "Редактор", + "privacy": "Конфиденциальность и безопасность", + "account": "Аккаунт", + "identities": "Удостоверения", + "vacation": "Отсутствие", + "advanced": "Дополнительно", + "calendar": "Календарь", + "filters": "Фильтры", + "templates": "Шаблоны", + "folders": "Папки", + "keywords": "Теги", + "security": "Безопасность", + "files": "Файлы", + "contacts": "Контакты", + "encryption": "Шифрование", + "protocol_handlers": "Приложения по умолчанию", + "sidebar_apps": "Приложения боковой панели", + "notifications": "Уведомления", + "layout": "Макет", + "themes": "Темы", + "reading": "Чтение", + "composing": "Написание", + "downloads": "Загрузки", + "content_senders": "Содержимое и отправители", + "about_data": "О программе и данные", + "debug": "Отладка" + }, + "themes": { + "title": "Темы", + "description": "Выберите тему, развёрнутую вашим администратором, или одну из встроенных.", + "locked": "Выбор темы заблокирован администратором.", + "forced": "Тема «{name}» задана администратором и не может быть изменена", + "admin_theme": "Тема администратора", + "activated": "Тема активирована", + "default_restored": "Тема по умолчанию восстановлена", + "default_name": "По умолчанию", + "default_badge": "По умолчанию", + "admin_enforced": "Задано администратором" + }, + "tab_groups": { + "general": "Общие", + "account": "Аккаунт и удостоверение", + "organization": "Организация почты", + "apps": "Приложения", + "system": "Система", + "appearance": "Внешний вид", + "mail": "Почта", + "privacy": "Конфиденциальность и безопасность", + "advanced": "Дополнительно" + }, + "appearance": { + "message_list_order": { + "label": "Порядок списка сообщений", + "description": "Как упорядочены сообщения в списке. Сортировку выполняет сервер, поэтому она охватывает всю папку, а не только загруженные сообщения.", + "preset": { + "chronological": "Хронологически (сначала новые)", + "unread_first": "Сначала непрочитанные", + "starred_first": "Сначала помеченные", + "tagged_first": "Сначала с меткой", + "custom": "Свой порядок" + }, + "tag_label": "Метка", + "advanced_show": "Дополнительно: настроить уровни сортировки", + "advanced_hide": "Скрыть дополнительно", + "criterion_label": "Критерий уровня {n}", + "direction_label": "Направление уровня {n}", + "criteria": { + "unread": "Непрочитанные", + "starred": "Помеченные", + "tag": "Метка", + "receivedAt": "Дата получения", + "sentAt": "Дата отправки", + "from": "Отправитель", + "subject": "Тема", + "size": "Размер" + }, + "direction": { + "unread_first": "Сначала непрочитанные", + "read_first": "Сначала прочитанные", + "starred_first": "Сначала помеченные", + "unstarred_first": "Сначала непомеченные", + "tagged_first": "Сначала с меткой", + "untagged_first": "Сначала без метки", + "newest_first": "Сначала новые", + "oldest_first": "Сначала старые", + "a_to_z": "А → Я", + "z_to_a": "Я → А", + "largest_first": "Сначала большие", + "smallest_first": "Сначала маленькие" + }, + "add_level": "Добавить уровень", + "remove_level": "Удалить уровень", + "move_up": "Выше", + "move_down": "Ниже", + "tiebreak_note": "До трёх уровней; следующие уровни решают при равенстве. «Сначала новые» всегда применяется последним.", + "scope": { + "label": "Применять ко всем папкам", + "description": "Если выключено, порядок действует только для папки «Входящие»; остальные папки остаются хронологическими." + }, + "unsupported_note": "Ваш почтовый сервер не поддерживает сортировку по статусу прочтения, пометкам или меткам. Эти уровни игнорируются, и список остаётся хронологическим." + }, + "title": "Внешний вид", + "description": "Настройте внешний вид веб-почты", + "theme": { + "label": "Тема", + "description": "Выберите предпочтительную цветовую схему", + "light": "Светлая", + "dark": "Тёмная", + "system": "Системная" + }, + "language": { + "label": "Язык", + "description": "Выберите предпочтительный язык" + }, + "font_size": { + "label": "Размер шрифта", + "description": "Регулируйте размер текста для лучшей читаемости", + "small": "Малый", + "medium": "Средний", + "large": "Большой" + }, + "list_density": { + "label": "Плотность", + "description": "Управляйте расстояниями и отступами в интерфейсе", + "extra_compact": "Максимально компактный", + "compact": "Компактный", + "regular": "Обычный", + "comfortable": "Просторный" + }, + "interface_layout": { + "label": "Макет интерфейса", + "description": "Где находятся поиск, навигация и панели инструментов", + "default": "По умолчанию", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Включить анимации", + "description": "Показывать плавные переходы и эффекты" + }, + "toolbar_position": { + "label": "Положение панели инструментов", + "description": "Где отображать кнопки действий письма (Ответить, Архивировать, Удалить и др.)", + "top": "Сверху", + "below_subject": "Под темой" + }, + "toolbar_labels": { + "label": "Показывать подписи панели инструментов", + "description": "Отображать текстовые подписи рядом с иконками панели. Отключите для экономии места, когда освоитесь с иконками." + }, + "hide_account_switcher": { + "label": "Скрыть переключатель аккаунтов в боковой панели", + "description": "Скрыть выбор аккаунта в верхней части боковой панели папок. Вы по-прежнему можете переключать аккаунты через нижнюю панель навигации." + }, + "show_rail_account_list": { + "label": "Показать аватары аккаунтов на панели навигации", + "description": "Отображать отдельные круги аккаунтов в нижней части панели навигации для быстрого переключения, с кнопкой выхода ниже." + }, + "unified_mailbox": { + "label": "Общий почтовый ящик", + "description": "Показывать объединённые папки (Входящие, Отправленные и др.) для всех подключённых аккаунтов", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Включать групповые ящики", + "description": "Также объединять общие/групповые ящики в едином представлении." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Цветные значки боковой панели", + "description": "Окрашивать значки папок и тегов по типу (синий «Входящие», красный «Спам», зелёный «Отправленные» и т. д.). Отключите для монохромной боковой панели." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Показывать общее количество сообщений", + "description": "Показывает общее количество сообщений рядом с папками и метками, наряду с количеством непрочитанных. Отключите, чтобы показывать только непрочитанные." + }, + "favicon_unread_badge": { + "label": "Счётчик непрочитанных на иконке вкладки и приложения", + "description": "Показывает количество непрочитанных писем значком на иконке вкладки браузера и, если поддерживается, на иконке установленного приложения — чтобы новая почта была видна без переключения на вкладку. Отключите, чтобы обе иконки оставались без значка." + }, + "show_shared_mail_accounts": { + "label": "Показывать общие почтовые аккаунты", + "description": "Показывать папки ящиков, к которым вам открыт доступ, в разделе «Общие» на боковой панели. Выключите, чтобы скрыть все, или скройте отдельные аккаунты ниже. Их календари и контакты останутся." + }, + "pro_interface": { + "label": "Pro-интерфейс (экспериментальный)", + "description": "Макет для опытных пользователей только для настольных устройств с просмотром сообщений в нескольких вкладках и работой между аккаунтами. Стандартный интерфейс не меняется; вы можете вернуться в любое время.", + "open_label": "Открыть Pro-интерфейс", + "back_to_standard": "К стандартному" + }, + "cross_unread": { + "label": "Все аккаунты: Непрочитанные", + "description": "Показывает в разделе «Все аккаунты» запись с непрочитанными письмами из всех аккаунтов, по всем папкам, кроме спама, отправленных, архива, корзины и черновиков." + }, + "cross_starred": { + "label": "Все аккаунты: Помеченные", + "description": "Показывает в разделе «Все аккаунты» запись с помеченными письмами из всех аккаунтов, по всем папкам, кроме спама, отправленных, архива, корзины и черновиков." + }, + "cross_all": { + "label": "Все аккаунты: Вся почта", + "description": "Показывает в разделе «Все аккаунты» запись со всеми письмами из всех аккаунтов, по всем папкам, кроме спама, отправленных, архива, корзины и черновиков." + } + }, + "keywords": { + "title": "Теги электронной почты", + "description": "Определите теги для организации электронных писем с помощью цветов. Они хранятся как ключевые слова JMAP на сервере.", + "add_keyword": "Добавить тег", + "label_field": "Отображаемое название", + "label_placeholder": "напр., Работа, Личное, Срочно", + "id_field": "Идентификатор тега", + "id_placeholder": "напр., work, personal", + "color_field": "Цвет", + "id_exists": "Этот идентификатор тега уже существует", + "edit": "Редактировать тег", + "delete": "Удалить тег", + "save": "Сохранить", + "add": "Добавить", + "cancel": "Отмена", + "migrating": "Обновление тега в существующих письмах…", + "migration_error": "Не удалось обновить тег в существующих письмах", + "filters_migration_error": "Некоторые правила фильтров всё ещё используют старый тег — обновите их вручную", + "nesting": { + "label": "Вложенные теги", + "description": "Вкладывайте теги в другие теги и показывайте их в боковой панели в виде дерева." + }, + "parent_field": "Родительский тег", + "no_parent": "Без родительского тега", + "too_long": "Этот путь тега слишком длинный (не более {max} символов)", + "has_children_locked": "В этот тег вложены другие теги, поэтому его имя и родительский тег заблокированы. Сначала переместите или удалите их.", + "has_children_delete": "Сначала удалите теги, вложенные в этот", + "visibility_field": "Видимость в боковой панели", + "visibility": { + "show": "Показывать", + "unread": "Показывать при непрочитанных", + "hide": "Скрывать" + }, + "unrecognized": { + "title": "Восстановить теги с сервера", + "description": "Названия и цвета тегов хранятся в этом браузере, но сами теги находятся в ваших письмах в виде ключевых слов JMAP. Просканируйте почту на ключевые слова, для которых здесь нет тега, и добавьте их обратно.", + "scan": "Найти теги", + "rescan": "Сканировать снова", + "scanning": "Сканирование… {scanned} из {total} писем", + "none": "Все теги в ваших письмах уже определены здесь.", + "messages": "{count, plural, one {# письмо} few {# письма} other {# писем}}", + "add_all": "Добавить все ({count})", + "partial": "Просканированы только {scanned} последних писем из {total}. Теги, встречающиеся только в более старых письмах, могут отсутствовать.", + "error": "Не удалось просканировать почту на наличие тегов." + } + }, + "notifications": { + "test_sound": "Проверить звук уведомления", + "sounds": { + "default": "По умолчанию (Сигнал)", + "cheerful": "Радостный", + "involved": "Вовлечённый", + "swift": "Быстрый жест", + "relax": "Релакс" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Звук уведомления", + "description": "Выберите звук для уведомлений", + "choose": "Звук", + "choose_desc": "Выберите звук уведомления и нажмите значок динамика для предпросмотра" + }, + "email": { + "title": "Уведомления о почте", + "description": "Настройте уведомления о входящих письмах", + "enabled": "Уведомления о почте", + "enabled_desc": "Показывать уведомления при получении новых писем", + "sound": "Звук уведомления", + "sound_desc": "Воспроизводить звуковой сигнал при получении новых писем", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Уведомления календаря", + "description": "Настройте уведомления о событиях календаря", + "enabled": "Уведомления о событиях", + "enabled_desc": "Показывать оповещения о предстоящих событиях календаря", + "sound": "Звук уведомления", + "sound_desc": "Воспроизводить звуковой сигнал для напоминаний календаря", + "invitation_parsing": "Распознавать приглашения по почте", + "invitation_parsing_desc": "Обнаруживать приглашения календаря во вложениях писем и показывать действия календаря" + }, + "push": { + "confirm_disable_message": "Это устройство перестанет получать оповещения, когда сайт закрыт.", + "confirm_disable_title": "Отключить фоновые уведомления?", + "description": "Получайте системные уведомления о новых письмах, когда этот сайт закрыт. Доставляется через push-релей Bulwark; релей никогда не видит содержимое писем.", + "disable": "Отключить", + "enable": "Включить", + "dismiss_aria": "Закрыть запрос на уведомления", + "ios_hint": "В iOS сначала установите сайт на главный экран – Safari доставляет Web Push только установленным PWA.", + "reenable": "Перерегистрировать", + "relay_desc": "По умолчанию используется размещённый релей Bulwark. Меняйте только если хостите самостоятельно.", + "relay_label": "Push-релей", + "relay_locked": "Установлено администратором", + "relay_locked_desc": "URL push-релея установлен вашим администратором и не может быть изменён.", + "status_active": "Активно на этом устройстве", + "status_busy": "Выполняется…", + "status_inactive": "Не включено на этом устройстве", + "status_unsupported": "Этот браузер не поддерживает Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Фоновые уведомления", + "devices_title": "Зарегистрированные устройства", + "devices_desc": "Все устройства, зарегистрированные для получения фоновых уведомлений этой учётной записи. После отзыва устройство сразу перестаёт их получать.", + "devices_loading": "Загрузка зарегистрированных устройств…", + "devices_empty": "Для фоновых уведомлений не зарегистрировано ни одного устройства.", + "devices_error": "Не удалось загрузить зарегистрированные устройства.", + "devices_refresh": "Обновить", + "device_this": "Это устройство", + "device_other": "Другое устройство", + "device_status_active": "Доставляет уведомления", + "device_status_inactive": "Ни разу не доставляло уведомлений", + "device_status_unknown": "Неизвестно этому ретранслятору", + "device_expires": "Истекает {date}", + "revoke": "Отозвать", + "confirm_revoke_title": "Отозвать это устройство?", + "confirm_revoke_message": "Оно сразу перестанет получать фоновые уведомления. Если снова включить push на этом устройстве, оно зарегистрируется заново.", + "confirm_revoke_message_this": "Это устройство сразу перестанет получать фоновые уведомления." + } + }, + "language_region": { + "title": "Язык, регион и время", + "description": "Язык, формат даты, формат времени и другие региональные настройки", + "language": { + "label": "Язык", + "description": "Выберите предпочтительный язык", + "english": "Английский", + "french": "Французский" + }, + "date_format": { + "label": "Формат даты", + "description": "Как отображаются даты в списке писем", + "smart": "Умный", + "relative": "Относительный", + "full": "Всегда полная дата", + "preview_today": "Сегодня:", + "preview_this_week": "На этой неделе:", + "preview_older": "Старее:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Формат времени", + "description": "Выберите формат 12- или 24-часовой нотации", + "12h": "12-часовой", + "24h": "24-часовой" + }, + "first_day": { + "label": "Первый день недели", + "description": "Начинать неделю с воскресенья или понедельника", + "sunday": "Воскресенье", + "monday": "Понедельник" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Очищать поиск при переходе между папками", + "description": "Щелчок по папке («Входящие», «Отправленные»…) очищает активный поиск и открывает эту папку. Если выключено, поиск сохраняется и выполняется заново в выбранной папке." + }, + "request_read_receipt": { + "label": "Запрашивать уведомления о прочтении по умолчанию", + "description": "Заранее включать запрос уведомления о прочтении при создании нового сообщения." + }, + "read_receipt_response": { + "label": "Отвечать на запросы уведомления о прочтении", + "description": "Что делать, когда входящее сообщение запрашивает уведомление о прочтении.", + "ask": "Спрашивать каждый раз", + "always": "Всегда отправлять", + "never": "Никогда не отправлять" + }, + "title": "Поведение почты", + "description": "Настройте обработку писем", + "mark_read": { + "label": "Отметить прочитанным", + "description": "Когда отмечать письма прочитанными при открытии", + "instant": "Немедленно", + "delay_3s": "Через 3 секунды", + "delay_5s": "Через 5 секунд", + "never": "Никогда" + }, + "delete_action": { + "label": "Действие при удалении", + "description": "Что происходит при удалении письма", + "trash": "Переместить в корзину", + "trash_and_read": "Переместить в корзину и пометить как прочитанное", + "permanent": "Удалить навсегда", + "warning": "Письма будут удалены навсегда и не могут быть восстановлены. Это действие необратимо." + }, + "message_spacing": { + "label": "Отступы сообщения", + "description": "Отступы вокруг текста сообщения при чтении", + "auto": "Автоматически (рекомендуется)", + "always": "Всегда добавлять отступы", + "edge": "От края до края" + }, + "plain_text_font": { + "label": "Шрифт обычного текста", + "description": "Шрифт для писем в обычном тексте", + "mono": "Моноширинный", + "sans": "Обычный (как в приложении)" + }, + "archive_mode": { + "label": "Архивировать в", + "description": "Как организовывать письма при архивировании", + "single": "Одну папку", + "year": "Папку на каждый год", + "month": "Папку на каждый месяц", + "reorganize": "Реорганизовать существующий архив", + "reorganize_success": "{count, plural, =0 {Нет писем для реорганизации} =1 {1 письмо реорганизовано} other {# писем реорганизовано}}", + "reorganize_error": "Не удалось реорганизовать архив" + }, + "permanently_delete_junk": { + "label": "Удалять спам навсегда", + "description": "Удалять письма из папки «Спам» навсегда вместо перемещения в корзину" + }, + "confirm_bulk_delete": { + "label": "Подтверждать удаление нескольких писем", + "description": "Спрашивать перед перемещением нескольких выбранных писем в корзину. При безвозвратном удалении подтверждение запрашивается всегда." + }, + "mail_layout": { + "label": "Макет почты", + "description": "Выберите между классической разделённой панелью чтения и сфокусированным потоком чтения в стиле Gmail.", + "split": "Разделённая панель", + "split_description": "Список сообщений и панель чтения отображаются рядом друг с другом.", + "focus": "Сфокусированный список", + "focus_description": "Показывать одну строку на сообщение и открывать письма на всю ширину, сохраняя видимой боковую панель папок.", + "horizontal": "Область чтения снизу", + "horizontal_description": "Показывать список сообщений сверху и открывать выбранное сообщение в области чтения под ним." + }, + "list_columns": { + "label": "Показывать список в виде столбцов", + "description": "Показывает однострочные строки в виде таблицы со столбцами «От», «Тема», «Дата» и «Размер» под заголовком." + }, + "disable_threading": { + "label": "Отключить группировку по беседам", + "description": "Отображать письма как отдельные сообщения, а не сгруппированные по беседам" + }, + "plain_text_mode": { + "label": "Только простой текст", + "description": "Отключить редактор форматированного текста и отправлять все письма только в виде простого текста, включая ответы и пересылки" + }, + "auto_select_reply_identity": { + "label": "Автоматически выбирать адрес для ответа", + "description": "При ответе автоматически переключать адрес отправителя на ту учетную запись, которая получила исходное сообщение" + }, + "reply_identity_match": { + "label": "Сопоставление адреса получения", + "description": "Какие адреса получения считать вашими. «Только точный адрес» выбирает исключительно одну из настроенных личностей; «тот же домен» также считает любой другой адрес на доменах ваших личностей catch-all-псевдонимом и подставляет его в заголовок «От». Выберите точный адрес, если такие адреса являются списками рассылки.", + "exact": "Только точный адрес", + "domain": "Любой адрес на моих доменах" + }, + "signature_position": { + "label": "Положение подписи", + "description": "Куда вставлять подпись в ответах и пересылке. Над цитируемым текстом она читается естественно как завершение ответа; под ним сохраняет целостность исходного сообщения.", + "above_quote": "Перед цитируемым текстом", + "below_quote": "После цитируемого текста" + }, + "signature_separator": { + "label": "Разделитель подписи", + "description": "Добавлять перед подписью стандартную строку-разделитель \"-- \" (RFC 3676). Отключите, если хотите переходить от текста сразу к подписи." + }, + "sub_address_delimiter": { + "label": "Разделитель суб-адресов", + "description": "Символ, отделяющий имя пользователя от тега суб-адреса. Используйте разделитель, настроенный на вашем почтовом сервере (например, user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "Свой…", + "custom_input_label": "Свой символ-разделитель" + }, + "show_preview": { + "label": "Показывать текст предпросмотра", + "description": "Отображать предпросмотр письма в списке", + "focus_description": "Отображать встроенный текст предпросмотра в сфокусированном однострочном списке сообщений" + }, + "show_verification_codes": { + "label": "Показывать коды подтверждения", + "description": "Предлагать код из писем для входа и подтверждения для копирования в один клик: в списке в течение суток и в самом письме" + }, + "attachment_click_action": { + "label": "Действие при нажатии на вложение", + "description": "Выберите, предпросмотр или немедленная загрузка при нажатии на вложение", + "preview": "Предпросмотр, если возможно", + "download": "Скачать немедленно" + }, + "attachment_position": { + "label": "Расположение вложений", + "description": "Где отображать вложения в заголовке письма", + "beside-sender": "Рядом с отправителем", + "below-header": "Под заголовком" + }, + "emails_per_page": { + "10": "10 писем", + "25": "25 писем", + "50": "50 писем", + "100": "100 писем", + "label": "Писем на странице", + "description": "Количество писем для загрузки за раз" + }, + "always_light_mode": { + "label": "Всегда показывать письма в светлом режиме", + "description": "Отображать содержимое писем в светлом режиме даже когда приложение в тёмном режиме, избегая проблем преобразования" + }, + "external_content": { + "label": "Внешнее содержимое", + "description": "Как обрабатывать изображения и внешнее содержимое", + "ask": "Всегда спрашивать", + "block": "Всегда блокировать", + "allow": "Всегда разрешать" + }, + "trusted_senders": { + "label": "Доверенные отправители", + "description": "Управляйте отправителями, чьи изображения загружаются автоматически", + "count_zero": "Нет", + "count_one": "1 отправитель", + "count_other": "{count} отправителей", + "modal_title": "Доверенные отправители", + "empty_title": "Нет доверенных отправителей", + "empty_description": "При просмотре письма с заблокированными изображениями нажмите «Всегда доверять этому отправителю», чтобы добавить его сюда.", + "add_manually": "Добавить отправителя вручную", + "add_button": "Добавить", + "add_placeholder": "Введите email-адрес", + "search_placeholder": "Поиск отправителей...", + "no_results": "Отправители не найдены", + "remove": "Удалить", + "close": "Закрыть", + "invalid_email": "Введите корректный email-адрес", + "already_added": "Этот отправитель уже является доверенным", + "save_error": "Не удалось сохранить - проверьте журнал отладки Контактов для подробностей", + "use_address_book_label": "Синхронизировать с адресной книгой", + "use_address_book_description": "Сохранять доверенных отправителей в отдельной адресной книге \"Доверенные отправители\", чтобы синхронизировать их на всех устройствах" + }, + "hover_actions": { + "label": "Быстрые действия при наведении", + "description": "Выберите быстрые действия, отображаемые при наведении на письмо в списке", + "delete": "Удалить", + "star": "Пометить / Снять пометку", + "mark_read": "Прочитано / Непрочитано", + "archive": "Архивировать", + "tag": "Тег", + "spam": "Отметить как спам", + "not_spam": "Не спам", + "none_selected": "Действия не выбраны", + "mode_label": "Режим отображения", + "mode_inline": "Встроенный", + "mode_floating": "Плавающий", + "corner_label": "Позиция плавающих действий", + "corner_top-left": "Вверху слева", + "corner_top-right": "Вверху справа", + "corner_bottom-left": "Внизу слева", + "corner_bottom-right": "Внизу справа" + }, + "default_mail_program": { + "label": "Почтовая программа по умолчанию", + "description": "Зарегистрировать {appName} как почтовую программу по умолчанию для ссылок mailto:", + "button": "Установить по умолчанию", + "success": "Браузер запрошен для установки по умолчанию", + "error": "Ваш браузер не поддерживает эту функцию" + }, + "attachment_reminder": { + "label": "Напоминание о вложении", + "description": "Предупреждать перед отправкой, если в сообщении упоминаются вложения, но ничего не прикреплено", + "keywords_label": "Ключевые слова-триггеры", + "keywords_description": "Слова или фразы, при обнаружении которых в сообщении появляется напоминание", + "add_placeholder": "Добавить ключевое слово...", + "add": "Add", + "remove": "Удалить" + }, + "empty_subject_warning": { + "label": "Предупреждение о пустой теме", + "description": "Запрашивать подтверждение перед отправкой письма без темы." + }, + "recipient_mentions": { + "label": "Упоминать получателей через @", + "description": "При вводе @ в тексте письма предлагаются его получатели, и вставляется имя выбранного. Enter или Tab выбирает выделенное имя." + }, + "mention_adds_recipient": { + "label": "Контакты в упоминаниях через @", + "description": "При вводе @ также предлагаются ваши контакты и люди из каталога. Если выбрать того, кого ещё нет среди получателей, он будет добавлен в письмо.", + "to": "Добавить в «Кому»", + "cc": "Добавить в «Копия»", + "off": "Только получатели" + }, + "hide_inline_image_attachments": { + "label": "Скрывать встроенные изображения из вложений", + "description": "Изображения, встроенные в тело сообщения, не отображаются как отдельные вложения" + }, + "attachment_image_previews": { + "label": "Показывать миниатюры изображений во вложениях", + "description": "Отображать вложенные изображения в виде миниатюр вместо обычных значков файлов" + }, + "send_delay": { + "label": "Отмена отправки / задержка отправки", + "description": "Задерживает обычную отправку на короткий серверный интервал.", + "off": "Выкл.", + "seconds": "{seconds} сек.", + "unsupported": "Текущая учетная запись не заявляет поддержку отложенной отправки. Настройка сохранится для других учетных записей." + }, + "return_to_list_after_action": { + "label": "Возврат к списку после удаления или отметки как непрочитанного", + "description": "После удаления или отметки открытого письма как непрочитанного возвращаться к списку писем вместо открытия следующего." + }, + "swipe_right_action": { + "label": "Действие при свайпе вправо (мобильные)", + "description": "На сенсорных устройствах — действие, выполняемое при свайпе сообщения вправо в списке." + }, + "swipe_left_action": { + "label": "Действие при свайпе влево (мобильные)", + "description": "На сенсорных устройствах — действие, выполняемое при свайпе сообщения влево в списке." + }, + "swipe_actions": { + "none": "Нет", + "archive": "Архивировать", + "delete": "Удалить", + "mark_read": "Переключить прочитано", + "star": "Переключить пометку", + "spam": "Отметить как спам" + }, + "rtl_editing": { + "label": "Поддержка редактирования справа налево", + "description": "Добавляет кнопку направления текста на панель инструментов редактора, позволяя задавать абзацам направление слева направо или справа налево" + } + }, + "composer": { + "title": "Редактор", + "description": "Настройте параметры написания писем", + "autosave": { + "label": "Интервал автосохранения", + "description": "Как часто автоматически сохранять черновики", + "30s": "Каждые 30 секунд", + "1m": "Каждую минуту", + "2m": "Каждые 2 минуты", + "5m": "Каждые 5 минут" + }, + "send_confirmation": { + "label": "Подтверждение отправки", + "description": "Запрашивать подтверждение перед отправкой писем" + }, + "default_reply": { + "label": "Режим ответа по умолчанию", + "description": "Действие по умолчанию при нажатии «Ответить»", + "reply": "Ответить", + "reply_all": "Ответить всем" + } + }, + "privacy": { + "title": "Конфиденциальность и безопасность", + "description": "Управляйте настройками конфиденциальности и безопасности", + "external_images": { + "label": "Блокировать внешние изображения", + "description": "Предотвращать отслеживание через внешние изображения" + }, + "session_timeout": { + "label": "Тайм-аут сессии", + "description": "Автоматически выходить после бездействия", + "never": "Никогда", + "30m": "30 минут", + "1h": "1 час", + "4h": "4 часа" + }, + "clear_cache": { + "label": "Очистить кэш", + "description": "Удалить кэшированные данные и временные файлы", + "button": "Очистить кэш", + "confirm": "Вы уверены, что хотите очистить кэш?", + "success": "Кэш успешно очищен" + } + }, + "account": { + "title": "Аккаунт", + "description": "Просмотрите данные своего аккаунта", + "name_label": "Отображаемое имя", + "username_label": "Имя пользователя", + "account_type_label": "Тип аккаунта", + "auth_method_label": "Аутентификация", + "auth_method_oauth": "Единый вход (OAuth/OIDC)", + "auth_method_basic": "Пароль", + "auth_method_token": "Токен доступа", + "demo_account": "Демо-аккаунт", + "email": { + "label": "Email-адрес", + "value": "{email}" + }, + "server": { + "label": "JMAP-сервер", + "value": "{server}" + }, + "storage": { + "label": "Использование хранилища", + "used": "{used} из {total} использовано", + "percentage": "{percent}% использовано" + }, + "last_sync": { + "label": "Последняя синхронизация", + "value": "{time}" + }, + "accounts": { + "title": "Подключённые учётные записи", + "description": "Перетащите, чтобы изменить порядок отображения учётных записей в меню", + "active": "Текущая активная учётная запись", + "default_badge": "По умолчанию", + "set_default": "Сделать основной", + "switch_to": "Переключиться на эту учётную запись", + "move_up": "Переместить вверх", + "move_down": "Переместить вниз", + "drag_handle": "Перетащите, чтобы изменить порядок", + "add": "Добавить учётную запись" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Доступные мне", + "description": "Групповые и общие учётные записи, которыми вы можете управлять. Выберите одну, чтобы изменить её фильтры, автоответчик, календари и контакты.", + "shared_label": "Общая учётная запись" + } + }, + "security": { + "title": "Безопасность аккаунта", + "description": "Управляйте паролем, двухфакторной аутентификацией и настройками безопасности", + "detecting": "Определение возможностей сервера...", + "not_available": "Управление безопасностью аккаунта недоступно для этого почтового сервера. Требуемые разрешения могут быть отключены. Подробнее см. в документации.", + "password": { + "title": "Изменить пароль", + "current": "Текущий пароль", + "new": "Новый пароль", + "confirm": "Подтвердите новый пароль", + "submit": "Изменить пароль", + "success": "Пароль успешно изменён", + "error_title": "Не удалось изменить пароль", + "error_mismatch": "Новые пароли не совпадают", + "error_min_length": "Пароль должен содержать не менее 8 символов", + "error_generic": "Не удалось изменить пароль" + }, + "display_name": { + "label": "Отображаемое имя", + "description": "Ваше имя, отображаемое на сервере", + "placeholder": "Введите ваше отображаемое имя", + "save": "Сохранить", + "success": "Отображаемое имя обновлено", + "error": "Не удалось обновить отображаемое имя" + }, + "totp": { + "section_title": "Двухфакторная аутентификация", + "label": "Аутентификация TOTP", + "description": "Добавьте дополнительный уровень безопасности с помощью одноразового пароля", + "active": "Включена", + "inactive": "Отключена", + "enabled": "Двухфакторная аутентификация включена", + "disabled": "Двухфакторная аутентификация отключена", + "enable_error": "Не удалось включить 2FA", + "disable_error": "Не удалось отключить 2FA", + "setup_instructions": "Скопируйте этот URL в приложение-аутентификатор (Google Authenticator, Authy и др.):", + "verification_code": "Код подтверждения", + "confirm": "Подтвердить", + "disable": "Отключить", + "disable_confirm_prompt": "Введите пароль, чтобы отключить двухфакторную аутентификацию.", + "password_required": "Требуется пароль", + "code_required": "Требуется код подтверждения", + "code_invalid": "Неверный код подтверждения. Проверьте приложение-аутентификатор и попробуйте снова." + }, + "app_passwords": { + "title": "Пароли приложений", + "description": "Создавайте пароли для приложений, не поддерживающих двухфакторную аутентификацию", + "add": "Добавить", + "create": "Создать", + "cancel": "Отмена", + "generate": "Сгенерировать", + "name_label": "Название приложения", + "name_placeholder": "напр., Thunderbird, iPhone Mail", + "password_label": "Пароль (оставьте пустым для автогенерации)", + "password_placeholder": "Автогенерация, если пусто", + "added": "Пароль приложения создан", + "removed": "Пароль приложения удалён", + "add_error": "Не удалось создать пароль приложения", + "remove_error": "Не удалось удалить пароль приложения", + "none": "Пароли приложений не настроены", + "done": "Готово", + "expires_label": "Срок действия (необязательно)", + "copy_now_warning": "Скопируйте этот пароль сейчас - он больше не будет показан.", + "allowed_ips_label": "Разрешённые IP (необязательно)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Через запятую или пробел. Оставьте пустым, чтобы разрешить любой IP." + }, + "api_keys": { + "title": "API-ключи", + "description": "Создавайте API-ключи для скриптов и интеграций, обращающихся к серверу напрямую", + "name_label": "Название ключа", + "name_placeholder": "напр. Скрипт резервного копирования, CI runner", + "copy_now_warning": "Скопируйте этот API-ключ сейчас – он больше не будет показан.", + "added": "API-ключ создан", + "removed": "API-ключ удалён", + "add_error": "Не удалось создать API-ключ", + "remove_error": "Не удалось удалить API-ключ", + "none": "API-ключи не настроены" + }, + "encryption": { + "section_title": "Шифрование хранилища", + "label": "Шифрование почты", + "description": "Шифровать хранимые письма на сервере для дополнительной конфиденциальности", + "active": "Шифрование {type} включено", + "inactive": "Отключено", + "enabled": "Шифрование хранилища включено", + "disabled_success": "Шифрование хранилища отключено", + "error": "Не удалось обновить настройки шифрования", + "algorithm_label": "Алгоритм шифрования", + "encrypt_on_append": "Шифровать новые письма при загрузке", + "allow_spam_training": "Разрешить обучение спам-фильтра до шифрования писем", + "enable_encryption": "Включить шифрование", + "enable_tooltip": "Включить шифрование с этим ключом", + "configure_title": "Включить шифрование с этим ключом", + "enable_button": "Включить", + "disable_tooltip": "Отключить шифрование", + "enabled_success": "Шифрование хранилища включено" + }, + "email_client": { + "title": "Настройка почтового клиента", + "description": "Используйте эти учётные данные для настройки вашего почтового клиента (Thunderbird, Apple Mail, Outlook и др.)", + "jmap_username_label": "Имя пользователя JMAP", + "copy": "Копировать", + "copied": "Скопировано", + "password_instructions": "Используйте указанное выше имя пользователя JMAP вместе с паролем приложения для входа в почтовый клиент. Создайте пароль приложения в разделе выше, если вы ещё этого не сделали." + }, + "link_device": { + "title": "Привязать мобильное приложение", + "description": "Войдите в приложение Bulwark Mail на телефоне, не вводя там пароль. Телефон получает собственный вход, поэтому выход здесь не завершает сеанс на нём.", + "generate": "Показать QR-код", + "regenerate": "Показать новый код", + "instructions": "Откройте приложение Bulwark Mail и нажмите \"Scan a sign-in code\" на экране входа (или \"Add account\" → \"Scan a sign-in code\", если вы уже вошли), затем наведите камеру сюда.", + "expires_in": "Срок действия кода истечёт через {seconds} секунд. Его можно использовать только один раз.", + "expired": "Срок действия кода истёк.", + "error": "Не удалось создать код привязки. Пожалуйста, попробуйте ещё раз.", + "password_prompt": "Введите пароль, чтобы подтвердить, что это вы. Телефон получает собственный вход; ваш пароль на телефон не передаётся.", + "show_password": "Показать пароль", + "hide_password": "Скрыть пароль", + "continue": "Продолжить", + "error_wrong_password": "Неверный пароль. Попробуйте ещё раз.", + "totp_prompt": "Введите код из приложения-аутентификатора.", + "error_password_or_code": "Неверный пароль или код.", + "error_not_signed_in": "Ваш сеанс истёк. Войдите снова, чтобы привязать устройство.", + "error_impersonation": "Нельзя привязывать устройства, пока вы просматриваете почтовый ящик другого пользователя.", + "error_too_many_attempts": "Слишком много попыток. Подождите немного и попробуйте снова.", + "error_session_secret": "Привязка устройств на этом сервере не настроена (отсутствует SESSION_SECRET). Обратитесь к администратору.", + "error_server_unreachable": "Не удалось связаться с вашим почтовым сервером. Попробуйте ещё раз.", + "error_pairing_unavailable": "Ваш почтовый сервер не позволяет привязывать устройства таким способом. Обратитесь к администратору.", + "qr_alt": "QR-код для входа", + "link_label": "Ссылка для входа", + "copy_link": "Скопировать ссылку для входа", + "copied": "Скопировано", + "copy_hint": "Нет камеры? Скопируйте ссылку и вставьте её в приложении через \"Paste sign-in link\".", + "success_title": "Вход на телефоне выполнен", + "success_body": "Эту страницу можно закрыть. Чтобы привязать ещё одно устройство, покажите новый код.", + "link_another": "Привязать ещё одно устройство", + "use_sso": "Использовать единый вход", + "error_insecure_server": "Мобильное приложение подключается к почтовым серверам только по https, а этот сервер использует http." + }, + "public_keys": { + "name_label": "Название открытого ключа", + "name_placeholder": "напр., Мой ключ", + "key_label": "Открытый ключ (S/MIME или PGP в формате ASCII)", + "key_placeholder": "Вставьте сюда свой открытый ключ", + "none": "Открытые ключи не настроены" + } + }, + "identities": { + "title": "Удостоверения отправителя", + "description": "Управляйте email-адресами, с которых можно отправлять письма", + "identities_count": { + "label": "Ваши удостоверения", + "description": "Email-адреса, настроенные для отправки", + "count_zero": "Нет удостоверений", + "count_one": "1 удостоверение", + "count_other": "{count} удостоверений" + }, + "manage": "Управление удостоверениями", + "sub_addressing": { + "label": "Суб-адресация", + "description": "Используйте теги вида user+tag@domain.com для организации входящей почты", + "learn_more": "Подробнее" + } + }, + "vacation": { + "title": "Отсутствие", + "description": "Автоматически обрабатывать входящие письма в ваше отсутствие", + "loading": "Загрузка настроек автоответа...", + "not_supported": "Ваш почтовый сервер не поддерживает автоответы на отпуск.", + "fetch_error": "Не удалось загрузить настройки автоответа. Повторите попытку.", + "status": { + "label": "Отвечать автоматически", + "description": "Отправлять автоматический ответ на входящие письма", + "active": "Активен", + "inactive": "Неактивен" + }, + "date_range": { + "start": "Дата начала", + "start_description": "Оставьте пустым для без ограничения", + "end": "Дата окончания", + "end_description": "Оставьте пустым для без ограничения" + }, + "message": { + "subject_label": "Тема", + "subject_description": "Тема автоответа", + "subject_placeholder": "Вне офиса", + "body_label": "Текст сообщения", + "body_description": "Содержимое сообщения в виде обычного текста", + "body_placeholder": "Спасибо за ваше письмо. Я сейчас вне офиса и отвечу, когда вернусь.", + "html_label": "Форматированное сообщение (HTML)", + "html_description": "Добавьте форматированную версию со ссылками и оформлением. Получатели, чей почтовый клиент не может её отобразить, увидят обычный текст выше.", + "html_placeholder": "Напишите форматированный ответ об отсутствии…" + }, + "preview": { + "title": "Предпросмотр", + "show": "Показать предпросмотр", + "hide": "Скрыть предпросмотр" + }, + "save": "Сохранить изменения", + "saving": "Сохранение...", + "warnings": { + "end_before_start": "Дата окончания должна быть позже даты начала", + "invalid_date": "Укажите корректные даты начала и окончания со временем или оставьте поле пустым", + "start_in_past": "Дата начала в прошлом", + "empty_body": "Текст сообщения пуст - получатели получат пустой ответ", + "subject_too_long": "Тема слишком длинная для сервера (не более {max} байт).", + "body_too_long": "Сообщение слишком длинное для сервера (не более {max} байт с учётом форматирования).", + "forward_address": "Укажите действительный адрес для пересылки", + "not_running": "Пересылка или получатели ответа сохранены, но сейчас не действуют. Сохраните, чтобы снова их включить." + }, + "forward": { + "title": "Пересылка", + "description": "Пересылает входящие письма на другой адрес, с автоответом или без него, в указанный выше период (без даты окончания, пока пересылку не выключат). Спам не пересылается.", + "enabled_label": "Пересылать письма", + "to_label": "Переслать на", + "to_placeholder": "user@пример.рф", + "keep_label": "Оставить во входящих", + "keep_description": "Иначе письмо только пересылается: оно не остаётся в этом почтовом ящике, и к нему не применяется ни одно правило фильтрации." + }, + "audience": { + "label": "Отвечать", + "description": "Внутренние отправители: {domains}", + "all": "Всем отправителям", + "internal": "Только внутренним отправителям", + "external": "Только внешним отправителям" + }, + "auto_reply": { + "title": "Автоответ" + } + }, + "folders": { + "role_memos": "Заметки", + "title": "Папки", + "description": "Управляйте папками почты и назначайте стандартные роли", + "folder_list": "Ваши папки", + "folder_list_description": "Нажмите на значок папки для настройки", + "standard_roles": "Стандартные роли папок", + "standard_roles_description": "Назначьте папки для стандартных ролей: Входящие, Отправленные, Корзина и др.", + "role_inbox": "Входящие", + "role_drafts": "Черновики", + "role_sent": "Отправленные", + "role_trash": "Корзина", + "role_junk": "Спам", + "role_archive": "Архив", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Нет", + "create_folder": "Создать папку", + "create_subfolder": "Создать подпапку", + "reorder": "Перетащите, чтобы изменить порядок", + "reorder_error": "Не удалось изменить порядок папок", + "move_error": "Не удалось переместить папку", + "select_folder": "Выбрать {name}", + "select_hint": "Выбрать. Shift+щелчок выбирает все папки до последней, по которой вы щёлкнули.", + "selected_count": "{count, plural, one {Выбрана # папка} few {Выбрано # папки} many {Выбрано # папок} other {Выбрано # папки}}", + "move_to": "Переместить в...", + "move_to_top_level": "Верхний уровень", + "move": "Переместить", + "clear_selection": "Снять выделение", + "folders_moved": "{count, plural, one {Перемещена # папка} few {Перемещено # папки} many {Перемещено # папок} other {Перемещено # папки}}", + "move_partial_error": "{count, plural, one {Не удалось переместить # папку} few {Не удалось переместить # папки} many {Не удалось переместить # папок} other {Не удалось переместить # папки}}", + "subfolder_of": "Внутри {name}", + "subfolder_name": "Название подпапки", + "new_folder_name": "Название папки", + "rename": "Переименовать", + "change_icon": "Изменить значок", + "count_mode": "Счётчик для {name}", + "count_mode_default": "Счётчик: по умолчанию", + "count_mode_unread": "Только непрочитанные", + "count_mode_all": "Непрочитанные и всего", + "count_mode_none": "Без счётчика", + "delete": "Удалить", + "confirm_delete": "Вы уверены, что хотите удалить \"{name}\"? Письма в этой папке будут перемещены в корзину.", + "create": "Создать", + "cancel": "Отмена", + "no_folders": "Нет пользовательских папок", + "cannot_delete_role": "Нельзя удалить папку со стандартной ролью. Сначала снимите роль.", + "folder_created": "Папка создана", + "folder_renamed": "Папка переименована", + "folder_deleted": "Папка удалена", + "role_updated": "Роль папки обновлена", + "error_create": "Не удалось создать папку", + "error_rename": "Не удалось переименовать папку", + "error_delete": "Не удалось удалить папку", + "error_delete_has_children": "Невозможно удалить папку: она содержит подпапки. Сначала удалите или переместите их.", + "error_delete_has_email": "Невозможно удалить папку: она содержит письма. Сначала переместите или удалите их.", + "error_role": "Не удалось обновить роль папки" + }, + "advanced": { + "title": "Дополнительно", + "description": "Расширенные параметры и настройки разработчика", + "debug_mode": { + "label": "Режим отладки", + "description": "Включить подробное журналирование для диагностики" + }, + "debug_categories": { + "description": "Выберите, какие категории журналировать. Отключите ненужные категории, чтобы уменьшить шум в консоли.", + "jmap": "Клиент JMAP", + "jmap_description": "Операции с почтовыми ящиками, получение почты и запросы протокола JMAP", + "calendar": "Календарь", + "calendar_description": "События календаря, импорт и сообщения планирования", + "tasks": "Задачи", + "tasks_description": "Создание, получение и обновление задач календаря", + "auth": "Аутентификация", + "auth_description": "Вход, TOTP, обмен токенами и управление сессией", + "filters": "Фильтры", + "filters_description": "Правила фильтрации Sieve и скрипты отпуска", + "email": "Просмотр почты", + "email_description": "Отрисовка писем, обработка TNEF и пометка как прочитано", + "push": "Push-уведомления", + "push_description": "Настройка и доставка push-уведомлений", + "contacts": "Контакты и адресные книги", + "contacts_description": "Синхронизация контактов, операции адресной книги и доверенные отправители" + }, + "settings_sync": { + "label": "Синхронизация настроек", + "description": "Синхронизировать настройки между браузерами и устройствами" + }, + "sender_favicons": { + "label": "Фавиконы отправителей", + "description": "Показывать иконки сайтов как аватары для корпоративных отправителей", + "refresh_label": "Обновить значки отправителей", + "refresh_description": "Заново загрузить значки сайтов, например если отправитель сменил свой", + "refresh_button": "Обновить", + "refresh_done": "Значки отправителей будут загружены заново" + }, + "show_avatars_in_junk": { + "label": "Показывать аватары в папке «Спам»", + "description": "Показывать аватары и фавиконы отправителей в папке «Спам». По умолчанию отключено, чтобы фишинговые письма не выглядели правдоподобно." + }, + "keyboard_shortcuts": { + "label": "Сочетания клавиш", + "description": "Просмотреть доступные сочетания клавиш", + "button": "Показать сочетания" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Сбросить настройки", + "description": "Восстановить все настройки до значений по умолчанию", + "button": "Сбросить" + }, + "export_settings": { + "label": "Экспортировать настройки", + "description": "Скачать настройки в формате JSON", + "button": "Экспортировать" + }, + "import_settings": { + "label": "Импортировать настройки", + "description": "Загрузить настройки из файла JSON", + "button": "Импортировать" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Исходный код" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Нажимайте на спам и фишинг, пока они не попали во входящие. Настоящие письма пропускайте. Потеряете все три щита — входящие падут.", + "kinds": { + "spam": "Спам", + "phishing": "Фишинг", + "legit": "Настоящее письмо" + }, + "click_to_block": "Нажмите, чтобы заблокировать", + "let_through": "Пропустить", + "start": "Начать", + "play_again": "Сыграть ещё", + "score": "Очки", + "combo": "Бонус за серию", + "shields_left": "Осталось щитов: {count} из {total}", + "seconds": "{seconds} с", + "best": "Рекорд: {score}", + "new_best": "Новый рекорд", + "held": "Входящие отстояли", + "overrun": "Входящие захвачены", + "stats": { + "caught": "Заблокировано", + "delivered": "Доставлено", + "mistakes": "Ошибки" + } + } + }, + "sidebar_apps": { + "title": "Приложения боковой панели", + "description": "Управляйте пользовательскими приложениями и ссылками в боковой панели", + "keep_loaded": "Держать приложения загруженными", + "keep_loaded_description": "Держать встроенные приложения в фоне при переключении между ними, чтобы избежать перезагрузки", + "managed_title": "Предоставлено администратором", + "managed_description": "Эти приложения настроены для всех и не могут быть изменены.", + "manage_title": "Пользовательские приложения", + "manage_description": "Добавляйте, редактируйте и удаляйте пользовательские приложения из боковой панели" + }, + "contacts": { + "title": "Контакты", + "description": "Импортируйте и экспортируйте ваши контакты", + "import_label": "Импорт контактов", + "import_description": "Импортировать контакты из файла vCard (.vcf)", + "export_label": "Экспорт контактов", + "export_description": "Экспортировать все контакты в виде файла vCard (.vcf)", + "manage_title": "Адресные книги", + "manage_description": "Переименуйте ваши адресные книги", + "no_address_books": "Адресные книги не найдены", + "categories_title": "Категории", + "categories_description": "Переименование категорий контактов", + "no_categories": "Категории не найдены", + "group_by_letter_description": "Показывать алфавитные заголовки разделов в списке контактов", + "group_by_letter_label": "Группировать по первой букве", + "sort_by_last_name_label": "Сортировать по фамилии", + "sort_by_last_name_description": "Упорядочить список контактов по фамилии, чтобы члены семьи шли подряд" + }, + "downloads": { + "title": "Загрузки", + "description": "Настройте, как именуются загруженные письма и вложения.", + "reset": "Восстановить по умолчанию", + "preview": "Предпросмотр:", + "email_template": { + "label": "Имя файла письма (.eml)", + "description": "Шаблон, используемый при экспорте письма или при перетаскивании его в файловую систему. Расширение .eml добавляется автоматически." + }, + "attachment_template": { + "label": "Имя файла вложения", + "description": "Шаблон, используемый при загрузке или перетаскивании вложения. Если опустить '{filename}' и '{ext}', исходное расширение сохраняется." + }, + "bundle_template": { + "label": "Имя файла .zip с несколькими письмами", + "description": "Шаблон, используемый при перетаскивании или загрузке нескольких выбранных писем одним .zip-архивом. Расширение .zip добавляется автоматически." + }, + "spaces": { + "label": "Пробелы", + "description": "Заменять пробелы в итоговом имени файла другим символом.", + "keep": "Сохранять пробелы", + "underscore": "Заменять на _", + "dash": "Заменять на -" + }, + "lowercase": { + "label": "Нижний регистр", + "description": "Принудительно перевести всё имя файла в нижний регистр." + }, + "strip_diacritics": { + "label": "Удалять диакритику", + "description": "Преобразовать буквы с акцентами в их ASCII-эквиваленты (ä → a, é → e). Полезно для инструментов, плохо работающих с Unicode-именами." + }, + "collapse_separators": { + "label": "Сжимать повторяющиеся разделители", + "description": "Сжимать последовательности пробелов, подчёркиваний или дефисов до одного символа." + }, + "after_export": { + "label": "После экспорта", + "description": "При необходимости переместить письмо после экспорта в .eml.", + "keep": "Оставить в ящике", + "archive": "Переместить в архив", + "trash": "Переместить в корзину" + } + }, + "filters": { + "title": "Фильтры почты", + "description": "Создавайте правила для автоматической сортировки, маркировки и управления входящими письмами", + "add_rule": "Добавить правило", + "no_rules": "Нет правил фильтрации", + "vacation_active": "Автоответ отпуска активен", + "vacation_active_description": "Автоматический ответ включён для входящих сообщений", + "vacation_configure": "Настроить", + "no_rules_description": "Создайте правила для автоматической организации входящей почты", + "edit_rule": "Редактировать правило", + "new_rule": "Новое правило", + "delete_rule": "Удалить правило", + "delete_confirm": "Вы уверены, что хотите удалить это правило?", + "enable": "Включить", + "disable": "Отключить", + "raw_editor": "Редактор Sieve", + "raw_editor_warning": "Редактирование Sieve-скрипта может нарушить визуальное редактирование правил. Изменения здесь переопределяют визуальный конструктор.", + "validate": "Проверить", + "validation_success": "Скрипт корректен", + "validation_error": "Скрипт содержит ошибки", + "save": "Сохранить правила", + "saving": "Сохранение...", + "saved": "Фильтры успешно сохранены", + "save_failed": "Не удалось сохранить фильтры", + "loading": "Загрузка фильтров...", + "not_supported": "Ваш почтовый сервер не поддерживает фильтры.", + "rule_name": "Название правила", + "rule_name_placeholder": "напр., Сортировка рассылок", + "match_all": "Совпадают ВСЕ условия", + "match_any": "Совпадает ЛЮБОЕ условие", + "conditions": "Условия", + "add_condition": "Добавить условие", + "actions": "Действия", + "add_action": "Добавить действие", + "stop_processing": "Прекратить обработку последующих правил", + "include_spam": "Перемещать также письма, помеченные как спам", + "attachment_type_placeholder": "напр. pdf, doc, jpg", + "value_placeholder_multi": "Значение (несколько через запятую)", + "condition_fields": { + "attachment": "Вложение", + "from": "От", + "to": "Кому", + "cc": "Копия", + "subject": "Тема", + "header": "Пользовательский заголовок", + "size": "Размер", + "body": "Тело", + "all": "Все письма" + }, + "comparators": { + "has_any": "присутствует", + "has_type": "типа", + "contains": "содержит", + "not_contains": "не содержит", + "is": "точно совпадает", + "not_is": "не равно", + "starts_with": "начинается с", + "ends_with": "заканчивается на", + "matches": "соответствует шаблону", + "greater_than": "больше чем", + "less_than": "меньше чем", + "address_is": "— это адрес", + "domain_is": "имеет домен" + }, + "action_types": { + "move": "Переместить в папку", + "copy": "Копировать в папку", + "forward": "Переслать на", + "mark_read": "Отметить прочитанным", + "star": "Пометить сообщение", + "add_label": "Добавить тег", + "discard": "Удалить (без уведомления)", + "reject": "Отклонить с сообщением", + "keep": "Оставить во входящих", + "stop": "Прекратить обработку" + }, + "move_to_folder": "Выбрать папку", + "copy_to_folder": "Выбрать папку", + "forward_to": "Переслать на адрес электронной почты", + "forward_placeholder": "user@пример.рф", + "forward_keep_copy": "Сохранить копию", + "forward_limit": "Лимит пересылок на одно письмо на этом сервере: {count}. Лишние пересылки пропускаются.", + "forward_unavailable": "Этот сервер не разрешает пересылку. Действия пересылки в правилах фильтров не сработают.", + "reject_message": "Сообщение об отклонении", + "reject_placeholder": "Ваше письмо было отклонено", + "label_name": "Название тега", + "label_placeholder": "Выбрать тег", + "header_name": "Имя заголовка", + "header_placeholder": "напр., X-Mailing-List", + "size_bytes": "Размер в байтах", + "size_placeholder": "напр., 1000000", + "system_managed": "Правило, управляемое системой", + "opaque_warning": "Этот скрипт был отредактирован вне визуального конструктора. Доступно только редактирование необработанного Sieve.", + "open_sieve_editor": "Открыть редактор Sieve", + "fetch_error": "Не удалось загрузить фильтры", + "expanded_view": "Расширенный вид", + "expanded_view_description": "Показывать правила фильтрации с подробными блоками условий и действий", + "if": "Если", + "then": "То", + "match_all_conditions": "все совпадают", + "match_any_condition": "любое совпадает", + "and": "и", + "or": "или", + "cancel": "Отмена", + "confirm_delete": "Удалить", + "rule_list": "Правила фильтрации", + "drag_to_reorder": "Перетащите для изменения порядка", + "match_type": "Тип совпадения", + "reset_to_visual": "Сбросить до визуального конструктора", + "reset_warning": "Это удалит текущий скрипт и начнёт заново.", + "confirm_reset": "Сбросить", + "validation_empty_name": "Требуется название правила", + "validation_empty_conditions": "Требуется хотя бы одно условие со значением", + "validation_empty_actions": "Требуется хотя бы одно действие", + "templates_section": "Начать с шаблона", + "template_newsletters": "Перемещать рассылки в папку", + "template_receipts": "Автоархивировать квитанции", + "template_important": "Помечать важные письма", + "template_notifications": "Фильтровать уведомления", + "sieve_editor": { + "title": "Редактор Sieve-скрипта", + "warning": "Редактирование Sieve-скрипта может нарушить визуальное редактирование правил. Изменения здесь переопределяют визуальный конструктор.", + "script_content": "Sieve-скрипт", + "valid": "Скрипт корректен", + "invalid": "Скрипт содержит ошибки", + "save_warning": "Сохранение перезапишет визуальные правила. Это нельзя отменить. Нажмите «Сохранить» ещё раз для подтверждения.", + "validating": "Проверка...", + "validate": "Проверить", + "cancel": "Отмена", + "save": "Сохранить", + "confirm_save": "Подтвердить сохранение", + "validation_failed": "Ошибка запроса проверки" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# условие} other {# условий}}", + "actions_count": "{count, plural, one {# действие} other {# действий}}" + }, + "origin_external": "Внешнее", + "managed_by_tooltip": "Управляется {source}. Редактируйте в этом приложении или используйте редактор Sieve.", + "quick_rule_names": { + "move_sender": "Перемещать от {sender} в {folder}", + "move_domain": "Перемещать от @{domain} в {folder}", + "move_list": "Перемещать рассылку {list} в {folder}", + "mark_read": "Отмечать прочитанными от {sender}", + "tag": "Добавлять тег {tag} для {sender}", + "block": "Блокировать {sender}", + "custom": "Письма от {sender}", + "custom_plain": "Новое правило", + "senders": "{count, plural, one {# отправителя} few {# отправителей} many {# отправителей} other {# отправителя}}" + }, + "suggestions": "Подсказки", + "suggest_subject": "Тема «{subject}»", + "suggest_to": "Кому {address}", + "suggest_cc": "Копия {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Применить и к письмам, которые уже есть в этой папке", + "apply_existing_unsupported": "К имеющимся письмам можно применить только правила, которые проверяют отправителя, получателей, тему или заголовки и которые перемещают, копируют, отмечают прочитанными, помечают звёздочкой или тегом.", + "period_title": "Диапазон дат", + "period_start": "Дата начала", + "period_end": "Дата окончания", + "validation_period_order": "Дата окончания должна быть позже даты начала", + "period_toggle": "Активно только в указанный период", + "period_hint": "Действует для писем, которые приходят в этот период. Оставьте поле пустым, чтобы не ограничивать эту границу.", + "validation_period_empty": "Укажите дату начала или окончания периода", + "validation_period_invalid": "Укажите для периода корректную дату со временем или оставьте поле пустым", + "apply_existing_period": "Правило с периодом действует только на письма, пришедшие в этот период, поэтому его нельзя применить к уже имеющимся письмам.", + "period_status_scheduled": "запланировано", + "period_status_active": "активно", + "period_status_expired": "истекло" + }, + "templates": { + "title": "Шаблоны писем", + "description": "Создавайте многоразовые шаблоны писем с переменными-заполнителями", + "add": "Новый шаблон", + "edit": "Редактировать шаблон", + "name": "Название шаблона", + "name_placeholder": "напр., Письмо-напоминание", + "category": "Категория", + "category_placeholder": "напр., Работа, Личное", + "subject": "Тема", + "subject_placeholder": "Тема письма", + "body": "Текст", + "body_placeholder": "Текст письма...", + "recipients_placeholder": "user@пример.рф", + "identity": "Отправить как", + "default_identity": "Удостоверение по умолчанию", + "favorite": "Избранное", + "cancel": "Отмена", + "create": "Создать", + "update": "Обновить", + "confirm_delete": "Удалить", + "no_templates": "Шаблоны отсутствуют", + "manage": "Управление шаблонами", + "count": "{count, plural, one {# шаблон} other {# шаблонов}}", + "export_import": "Экспорт и импорт", + "export_import_description": "Резервная копия шаблонов или перенос на другое устройство", + "export": "Экспортировать", + "import": "Импортировать", + "validation": { + "empty": "Название шаблона обязательно", + "too_long": "Название шаблона не должно превышать 200 символов" + } + }, + "files": { + "display": { + "title": "Отображение", + "description": "Настройте отображение файлов и папок" + }, + "default_view": { + "label": "Вид по умолчанию", + "description": "Выберите между сеткой и списком", + "list": "Список", + "grid": "Сетка" + }, + "default_sort": { + "label": "Сортировка по умолчанию", + "description": "Выберите способ сортировки файлов по умолчанию", + "name": "Имя", + "size": "Размер", + "modified": "Дата изменения" + }, + "sort_direction": { + "label": "Направление сортировки", + "description": "Выберите порядок: по возрастанию или убыванию", + "ascending": "По возрастанию", + "descending": "По убыванию" + }, + "icons": { + "title": "Иконки", + "description": "Настройте внешний вид иконок файлов" + }, + "show_icons": { + "label": "Показывать иконки файлов", + "description": "Отображать иконки рядом с файлами и папками" + }, + "colored_icons": { + "label": "Цветные иконки", + "description": "Использовать цветные иконки вместо монохромных" + }, + "show_thumbnails": { + "label": "Показывать миниатюры", + "description": "Отображать предпросмотр изображений вместо иконок для изображений" + }, + "behavior": { + "title": "Поведение", + "description": "Настройте поведение файлового менеджера" + }, + "show_hidden": { + "label": "Показывать скрытые файлы", + "description": "Отображать файлы и папки, начинающиеся с точки" + }, + "preview": { + "label": "Предпросмотр" + } + }, + "search_placeholder": "Поиск настроек", + "search_clear": "Очистить поиск", + "search_no_results": "Нет подходящих настроек", + "scoped": { + "back": "Назад к моей учётной записи", + "managing": "Управление: {name}" + } + }, + "errors": { + "page_error_title": "Что-то пошло не так", + "page_error_description": "Произошла непредвиденная ошибка. Пожалуйста, попробуйте снова или вернитесь на главную страницу.", + "sidebar_error": "Не удалось загрузить почтовые ящики", + "email_list_error": "Не удалось загрузить письма", + "viewer_error_title": "Не удалось отобразить письмо", + "viewer_error_description": "Произошла проблема при отображении этого письма. Оно может содержать неподдерживаемый контент.", + "composer_error": "Не удалось загрузить редактор письма", + "settings_error_title": "Настройки недоступны", + "settings_error_description": "Не удалось загрузить настройки. Ваши предпочтения могут не быть сохранены.", + "try_again": "Попробовать снова", + "reload": "Перезагрузить", + "reload_emails": "Перезагрузить письма", + "reload_settings": "Перезагрузить настройки", + "retry": "Повторить", + "go_home": "В папку «Входящие»" + }, + "context_menu": { + "reply": "Ответить", + "reply_all": "Ответить всем", + "forward": "Переслать", + "mark_read": "Отметить как прочитанное", + "mark_unread": "Отметить как непрочитанное", + "star": "Добавить звёздочку", + "unstar": "Убрать звёздочку", + "pin": "Закрепить", + "unpin": "Открепить", + "move_to": "Переместить в...", + "copy_to_account": "Копировать в аккаунт...", + "remove_from_folder": "Убрать из этой папки", + "archive": "В архив", + "delete": "Удалить", + "mark_as_spam": "Отметить как спам", + "not_spam": "Не спам", + "tag": "Тег", + "items_selected": "{count} писем выбрано", + "forward_as_attachments": "Переслать как вложения", + "edit_draft": "Редактировать черновик", + "cancel_scheduled_send": "Отменить отправку", + "reschedule_send": "Перенести", + "cancel_and_edit": "Отменить и изменить", + "cancel_and_compose_again": "Отменить и написать заново", + "rules": { + "title": "Правила", + "move_from": "Всегда перемещать письма от {sender}", + "move_from_domain": "Всегда перемещать письма от @{domain}", + "move_from_list": "Всегда перемещать письма из этой рассылки", + "mark_read_from": "Всегда отмечать письма от {sender} прочитанными", + "tag_with": "Всегда добавлять тег", + "block": "{count, plural, =1 {Заблокировать отправителя} one {Заблокировать # отправителя} few {Заблокировать # отправителей} many {Заблокировать # отправителей} other {Заблокировать # отправителя}}", + "no_junk": "В этой учётной записи нет папки «Спам»", + "create": "Создать правило…", + "manage": "Управление правилами", + "senders_count": "{count, plural, one {# отправителя} few {# отправителей} many {# отправителей} other {# отправителя}}", + "cross_account": "Выберите письма из одной учётной записи", + "opaque_hint": "Ваши фильтры отредактированы вручную. Откройте настройки фильтров" , + "loading": "Загрузка правил…", + "load_failed": "Не удалось загрузить правила", + "none": "Правил пока нет", + "new_mail_only": "Только для новых писем", + "running": "Выполняется «{name}» в папке {folder}…" + }, + "find_emails_from": "Найти письма от {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Отметить папку как прочитанную", + "mark_folder_tree_read": "Отметить папку и вложенные папки как прочитанные", + "mark_all_folders_read": "Отметить все папки как прочитанные", + "new_subfolder": "Новая вложенная папка...", + "new_folder": "Новая папка...", + "rename": "Переименовать...", + "share": "Поделиться...", + "import_email": "Импортировать .eml или .zip...", + "empty_folder": "Очистить папку", + "empty_folder_generic": "Очистить папку", + "delete_folder": "Удалить папку", + "refresh": "Обновить", + "hide_shared_account": "Скрыть с боковой панели", + "mute_notifications": "Отключить уведомления", + "unmute_notifications": "Включить уведомления", + "mute_account_notifications": "Отключить уведомления для этого аккаунта", + "unmute_account_notifications": "Включить уведомления для этого аккаунта", + "reset_folder_notifications": "Сбросить уведомления папок", + "mark_all_confirm_title": "Отметить все папки как прочитанные", + "mark_all_confirm_message": "Отметить все непрочитанные сообщения в вашем личном аккаунте как прочитанные?", + "delete_confirm_title": "Удалить папку", + "delete_confirm_message": "Безвозвратно удалить папку \"{name}\"? Это действие нельзя отменить.", + "prompt_new_subfolder": "Введите имя для новой вложенной папки.", + "prompt_new_folder": "Введите имя для новой папки.", + "prompt_rename": "Введите новое имя для этой папки.", + "toast_marked_read": "Папка отмечена как прочитанная", + "toast_marked_read_count": "Отмечено {count, plural, one {1 сообщение} few {# сообщения} other {# сообщений}} как прочитанные", + "toast_already_read": "Нет непрочитанных сообщений", + "toast_marked_all_read": "Все папки отмечены как прочитанные", + "toast_emptied": "Папка очищена", + "toast_folder_created": "Папка создана", + "toast_folder_renamed": "Папка переименована", + "toast_folder_deleted": "Папка удалена", + "toast_error_mark_read": "Не удалось отметить как прочитанное", + "toast_error_empty": "Не удалось очистить папку", + "toast_error_create": "Не удалось создать папку", + "toast_error_rename": "Не удалось переименовать папку", + "toast_error_delete": "Не удалось удалить папку", + "toast_error_delete_has_children": "Папка содержит вложенные папки. Сначала удалите их.", + "toast_error_delete_has_email": "Папка не пуста. Сначала очистите её.", + "delete_with_contents_title": "Папка не пуста", + "delete_with_contents_message": "Папка «{name}» всё ещё содержит {count, plural, one {# сообщение} few {# сообщения} other {# сообщений}}. Удалить папку вместе со всеми сообщениями? Это действие нельзя отменить.", + "delete_with_contents_confirm": "Удалить папку и сообщения", + "placeholder_folder_name": "Имя папки", + "create": "Создать", + "rename_confirm": "Переименовать" + }, + "shortcuts": { + "title": "Сочетания клавиш", + "tip": "Нажмите ? в любое время для отображения справки", + "sections": { + "navigation": "Навигация", + "actions": "Действия с письмом", + "global": "Глобальные", + "threads": "Цепочки", + "composer": "Редактор" + }, + "navigation": { + "next_email": "Следующее письмо", + "previous_email": "Предыдущее письмо", + "open_email": "Открыть письмо", + "close_email": "Закрыть / Отменить выбор", + "go_to_folder": "Перейти к папке", + "next_unread": "Следующее непрочитанное письмо", + "no_more_unread": "Ниже в списке больше нет непрочитанных писем" + }, + "actions": { + "reply": "Ответить", + "reply_all": "Ответить всем", + "forward": "Переслать", + "star": "Переключить звёздочку", + "archive": "В архив", + "delete": "Удалить", + "mark_unread": "Отметить как непрочитанное", + "mark_read": "Отметить как прочитанное", + "toggle_spam": "Отметить спам / Не спам" + }, + "global": { + "compose": "Написать новое письмо", + "search": "Перейти к поиску", + "help": "Показать сочетания клавиш", + "refresh": "Обновить письма", + "select_all": "Выбрать все", + "global_search": "Искать везде", + "extend_selection": "Расширить выделение вниз / вверх" + }, + "threads": { + "expand_collapse": "Развернуть/свернуть цепочку", + "select_mountain_view": "Выбрать переписку" + }, + "composer": { + "send": "Отправить письмо", + "schedule_send": "Запланировать отправку", + "template_picker": "Открыть выбор шаблонов" + } + }, + "threads": { + "messages_one": "{count} сообщение", + "messages_other": "{count} сообщений", + "messages_tooltip": "{count, plural, one {# сообщение в этом разговоре} other {# сообщений в этом разговоре}}", + "expand": "Развернуть разговор", + "collapse": "Свернуть разговор", + "loading": "Загрузка разговора...", + "mark_read": "Отметить разговор как прочитанный", + "mark_unread": "Отметить разговор как непрочитанный", + "archive": "Архивировать разговор", + "delete": "Удалить разговор", + "star": "Добавить звёздочку к разговору", + "unstar": "Убрать звёздочку у разговора", + "toggle_thread": "Переключить цепочку" + }, + "identities": { + "modal_title": "Управление идентификациями отправителя", + "create_new": "Создать новую идентификацию", + "edit_identity": "Редактировать идентификацию", + "delete_confirm": "Удалить эту идентификацию? Это действие нельзя отменить.", + "cannot_delete": "Эту идентификацию нельзя удалить", + "primary_identity": "Основная", + "set_as_primary": "Сделать основной", + "no_identities": "Идентификации не найдены", + "display": { + "reply_to": "Ответить на:", + "bcc": "СК:", + "signature": "Подпись:", + "preview": "Предпросмотр:" + }, + "validation_errors": { + "invalid_emails": "Неверные адреса: {emails}", + "unknown_error": "Неизвестная ошибка" + }, + "form": { + "name_label": "Отображаемое имя", + "name_placeholder": "напр., Рабочая почта, Личная", + "name_required": "Имя обязательно", + "email_label": "Адрес электронной почты", + "email_placeholder": "user@пример.рф", + "email_required": "Email обязателен", + "email_invalid": "Введите корректный адрес электронной почты", + "email_immutable": "Адрес электронной почты нельзя изменить после создания", + "reply_to_label": "Ответить на (необязательно)", + "reply_to_placeholder": "other@example.com", + "bcc_label": "Авто СК (необязательно)", + "bcc_placeholder": "archive@пример.рф", + "text_signature_label": "Текстовая подпись", + "html_signature_label": "HTML-подпись", + "signature_byte_counter": "{bytes} / {max} байт", + "signature_byte_limit_reached": "Достигнут лимит сервера", + "save": "Сохранить идентификацию", + "cancel": "Отмена", + "creating": "Создание...", + "updating": "Обновление..." + }, + "sub_address": { + "button_tooltip": "Использовать суб-адрес", + "popover_title": "Добавить тег суб-адреса", + "tag_input_placeholder": "Введите тег (напр., shopping)", + "preview_label": "Предпросмотр:", + "recent_tags": "Недавние теги", + "suggested_tags": "Предложения", + "use_address": "Использовать этот адрес", + "invalid_tag": "Тег должен содержать только буквы, цифры и дефисы", + "tag_too_long": "Тег не должен превышать 30 символов", + "help_text": "Письма на адрес user{delimiter}tag@domain.com будут приходить в ваш ящик", + "validation": { + "empty": "Тег не может быть пустым", + "too_long": "Тег не должен превышать {max} символов", + "invalid_chars": "Тег должен содержать только буквы, цифры и дефисы" + } + }, + "badge": { + "sent_via": "через", + "sub_address_tag": "Отправлено через суб-адрес: {tag}", + "identity_name": "Отправлено через идентификацию: {name}", + "identity_short": "через {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Удалить", + "delete_confirm_title": "Удалить идентификацию" + }, + "templates": { + "picker_title": "Выбрать шаблон", + "search_placeholder": "Поиск шаблонов...", + "section_favorites": "Избранное", + "section_recent": "Недавние", + "section_uncategorized": "Другие", + "no_templates": "Шаблоны ещё не созданы", + "no_results": "Шаблоны не найдены", + "fill_placeholders": "Заполнить значения переменных", + "enter_value": "Введите значение...", + "preview": "Предпросмотр", + "insert_with_values": "Вставить со значениями", + "insert_raw": "Вставить без изменений", + "copy_suffix": "(копия)", + "placeholder": "Переменная", + "placeholders": { + "recipient_name": "Имя получателя", + "company": "Название компании", + "date": "Текущая дата", + "day_of_week": "День недели", + "sender_name": "Ваше имя" + } + }, + "contacts": { + "directory": { + "title": "Справочник", + "search_placeholder": "Поиск по имени или адресу", + "refresh": "Обновить справочник", + "retry": "Повторить", + "loading": "Загрузка справочника…", + "unavailable_title": "Нет справочника", + "unavailable_hint": "Ваш почтовый сервер не предоставляет справочник учётных записей.", + "forbidden_title": "Справочник недоступен", + "forbidden_hint": "Ваш почтовый сервер не разрешает этой учётной записи просматривать справочник. Обратитесь к администратору.", + "error_title": "Не удалось загрузить справочник", + "error_hint": "Проверьте подключение и повторите попытку.", + "empty_title": "Справочник пуст", + "empty_hint": "Ваш почтовый сервер не возвращает учётных записей с адресом электронной почты.", + "no_match": "В справочнике никто не соответствует запросу", + "refresh_failed": "Не удалось обновить справочник. Показан последний загруженный список.", + "select_page": "Выбрать всех на этой странице", + "deselect_page": "Снять выбор на этой странице", + "select_entry": "Выбрать {name}", + "range": "{from}–{to} из {total}", + "page": "Страница {page} из {count}", + "previous_page": "Предыдущая страница", + "next_page": "Следующая страница", + "picker_title": "Выбрать из справочника", + "cancel": "Отмена", + "selected_count": "Выбрано: {count}", + "add_to": "Добавить в «Кому»", + "add_cc": "Добавить в «Копия»", + "add_bcc": "Добавить в «Скрытая копия»", + "select_hint": "Выберите человека, чтобы увидеть подробности", + "write_email": "Написать письмо", + "add_to_contacts": "Добавить в контакты", + "email": "Эл. почта", + "read_only_note": "Записи справочника приходят с вашего почтового сервера, здесь их нельзя изменить.", + "type": { + "individual": "Человек", + "group": "Группа", + "resource": "Ресурс", + "location": "Место", + "other": "Другое" + } + }, + "title": "Контакты", + "search_placeholder": "Поиск контактов...", + "create_new": "Новый контакт", + "no_category": "Без категории", + "rename_category": "Переименовать категорию", + "category_name_label": "Название категории", + "category_renamed": "Категория переименована", + "category_rename_failed": "Не удалось переименовать категорию", + "category_added": "Контакт добавлен в {name}", + "category_added_plural": "{count} контактов добавлено в {name}", + "empty_state": "Контактов пока нет", + "empty_state_title": "Контактов пока нет", + "empty_state_subtitle": "Создайте первый контакт или импортируйте из файла vCard", + "empty_search": "Нет контактов, соответствующих вашему запросу", + "empty_search_hint": "Попробуйте другой поисковый запрос", + "empty_filtered": "Нет контактов, соответствующих фильтрам", + "empty_filtered_hint": "Измените или очистите фильтры", + "clear_search": "Очистить поиск", + "import_vcard": "Импорт vCard", + "delete_confirm_title": "Удалить контакт", + "delete_confirm": "Вы уверены, что хотите удалить этот контакт?", + "local_mode": "Контакты хранятся локально (сервер не поддерживает JMAP Contacts)", + "back_to_contacts": "Вернуться к контактам", + "back_to_email": "Вернуться к письму", + "tabs": { + "all": "Все", + "groups": "Группы" + }, + "shared": { + "title": "Общий" + }, + "address_books": { + "create": "Новая адресная книга", + "created": "Адресная книга создана", + "create_failed": "Не удалось создать адресную книгу", + "title": "Мои адресные книги", + "shared_prefix": "Общая: {name}", + "moved": "Контакт перемещён в {name}", + "moved_plural": "{count} контактов перемещено в {name}", + "move_failed": "Не удалось переместить контакт", + "address_book": "Адресная книга", + "rename": "Переименовать адресную книгу", + "name_label": "Название адресной книги", + "renamed": "Адресная книга переименована", + "rename_failed": "Не удалось переименовать адресную книгу", + "default": "По умолчанию", + "set_default": "Сделать по умолчанию", + "default_updated": "Адресная книга по умолчанию обновлена", + "set_default_failed": "Не удалось установить адресную книгу по умолчанию", + "manage": "Управление адресными книгами", + "share": "Поделиться адресной книгой", + "new_contact_in_book": "Новый контакт в этой адресной книге", + "delete": "Удалить адресную книгу", + "confirm_delete": "Удалить «{name}»? Все контакты в этой адресной книге будут удалены.", + "deleted": "Адресная книга удалена", + "delete_failed": "Не удалось удалить адресную книгу" + }, + "detail": { + "emails": "Адреса электронной почты", + "phones": "Номера телефонов", + "organizations": "Организации", + "addresses": "Адреса", + "notes": "Заметки", + "titles": "Должности и роли", + "online_services": "Онлайн-сервисы", + "anniversaries": "Даты", + "personal_info": "Личная информация", + "languages": "Языки", + "categories": "Категории", + "related_contacts": "Связанные контакты", + "crypto_keys": "Криптографические ключи", + "cert_issuer": "Издатель", + "cert_expires": "Истекает", + "cert_expired": "Истёк", + "cert_fingerprint": "Отпечаток", + "cert_algorithm": "Алгоритм", + "import_to_smime": "Импортировать в S/MIME", + "cert_already_imported": "Уже импортировано в S/MIME", + "cert_imported": "Сертификат импортирован в хранилище S/MIME", + "cert_import_failed": "Не удалось импортировать сертификат", + "no_contact_selected": "Выберите контакт для просмотра деталей", + "compose_email": "Написать письмо", + "copy_email": "Скопировать email", + "copy_phone": "Скопировать номер телефона", + "copy_url": "Скопировать URL", + "copied": "Скопировано в буфер обмена", + "copy_failed": "Не удалось скопировать в буфер обмена", + "created": "Создан", + "updated": "Последнее обновление", + "timezone": "Часовой пояс", + "anniversary_birth": "День рождения", + "anniversary_death": "Дата смерти", + "anniversary_wedding": "Годовщина", + "anniversary_other": "Другое", + "personal_expertise": "Экспертиза", + "personal_hobby": "Хобби", + "personal_interest": "Интерес", + "personal_other": "Другое", + "gender": "Пол", + "gender_masculine": "Мужской", + "gender_feminine": "Женский", + "gender_other": "Другой", + "gender_none": "Не применимо", + "gender_unknown": "Неизвестно", + "calendar": "Календарь", + "calendar_uri": "URL календаря", + "scheduling_uri": "URL планирования", + "freebusy_uri": "URL доступности", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Другие действия", + "age_years": "{count, plural, one {# год} few {# года} many {# лет} other {# лет}}", + "years_since": "{count, plural, one {# год назад} few {# года назад} many {# лет назад} other {# лет назад}}" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "Новый контакт", + "edit_title": "Редактировать контакт", + "section_address_book": "Каталог", + "select_address_book": "Выберите каталог...", + "section_identity": "Имя и личность", + "contact_type": "Тип контакта", + "type_person": "Человек", + "type_organization": "Организация", + "section_work": "Работа и организация", + "prefix": "Префикс", + "prefix_placeholder": "Д-р., Г-н., Г-жа.", + "given_name": "Имя", + "middle_name": "Отчество", + "surname": "Фамилия", + "suffix": "Суффикс", + "suffix_placeholder": "Мл., Ст., III", + "nickname": "Псевдоним", + "nickname_placeholder": "Псевдоним", + "email": "Электронная почта", + "email_placeholder": "user@пример.рф", + "phone": "Телефон", + "phone_placeholder": "+7 234 567 890", + "phone_type": "Тип", + "phone_voice": "Голосовой", + "phone_cell": "Мобильный", + "phone_fax": "Факс", + "phone_pager": "Пейджер", + "phone_video": "Видео", + "phone_text": "Текст", + "organization": "Организация", + "organization_placeholder": "Название компании", + "department": "Отдел", + "department_placeholder": "Отдел", + "job_title": "Должность", + "job_title_placeholder": "напр., инженер-программист", + "role": "Роль", + "role_placeholder": "напр., руководитель группы", + "addresses": "Адреса", + "add_address": "Добавить адрес", + "street": "Улица", + "city": "Город", + "region": "Штат / Регион", + "postcode": "Почтовый индекс", + "country": "Страна", + "online_services": "Онлайн-сервисы", + "add_online_service": "Добавить онлайн-сервис", + "url_placeholder": "https://пример.рф", + "service_placeholder": "Сервис", + "anniversaries": "Даты", + "add_anniversary": "Добавить дату", + "anniversary_birth": "День рождения", + "anniversary_wedding": "Годовщина", + "anniversary_death": "Памятная дата", + "anniversary_other": "Другое", + "personal_info": "Личная информация", + "add_personal_info": "Добавить запись", + "personal_info_placeholder": "напр., Фотография", + "personal_expertise": "Экспертиза", + "personal_hobby": "Хобби", + "personal_interest": "Интерес", + "personal_other": "Другое", + "level": "Уровень", + "level_high": "Высокий", + "level_medium": "Средний", + "level_low": "Низкий", + "categories": "Категории", + "categories_placeholder": "напр., Семья, Друзья, Коллеги", + "categories_hint": "Введите для поиска или добавления категорий", + "category_add": "Добавить", + "note": "Заметки", + "note_placeholder": "Добавьте заметку...", + "gender": "Пол", + "gender_sex": "Биологический пол", + "gender_male": "Мужской", + "gender_female": "Женский", + "gender_other": "Другой", + "gender_none": "Не применимо", + "gender_unknown": "Неизвестно", + "gender_identity": "Гендерная идентичность", + "gender_identity_placeholder": "Гендерная идентичность...", + "calendar": "Календарь", + "calendar_uri": "URL календаря", + "scheduling_uri": "URL планирования", + "freebusy_uri": "URL доступности", + "context_work": "Рабочий", + "context_private": "Личный", + "add_email": "Добавить email", + "add_phone": "Добавить телефон", + "save": "Сохранить", + "cancel": "Отмена", + "creating": "Создание...", + "updating": "Обновление...", + "name_required": "Укажите имя, фамилию или организацию", + "email_invalid": "Введите корректный адрес электронной почты", + "email_error_inline": "Неверный формат email", + "save_failed": "Не удалось сохранить контакт", + "delete": "Удалить", + "upload_photo": "Загрузить фото", + "remove_photo": "Удалить фото", + "photo_hint": "JPG или PNG, до 10 МБ. Будет уменьшено.", + "photo_too_large": "Изображение слишком большое (макс. 10 МБ)", + "photo_invalid": "Недопустимый файл изображения", + "change_photo": "Изменить" + }, + "groups": { + "create": "Новая группа", + "edit": "Редактировать группу", + "empty": "Групп пока нет", + "delete_confirm_title": "Удалить группу", + "delete_confirm": "Вы уверены, что хотите удалить эту группу?", + "name_label": "Название группы", + "name_placeholder": "напр., Команда, Семья", + "name_required": "Название группы обязательно", + "save_failed": "Не удалось сохранить группу", + "members_label": "Участники", + "search_members": "Поиск контактов для добавления...", + "no_members": "В этой группе нет участников", + "member_count": "{count, plural, =0 {Нет участников} one {1 участник} other {# участников}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Импорт контактов", + "drop_hint": "Нажмите для выбора файла vCard", + "file_types": "Файлы .vcf или .vcard", + "no_contacts": "Контакты в файле не найдены", + "parse_error": "Не удалось разобрать файл vCard", + "found": "{count, plural, one {1 контакт найден} other {# контактов найдено}}", + "duplicate": "Дубликат", + "select_all": "Выбрать все", + "deselect_all": "Снять выделение", + "selected": "{count, plural, one {1 выбран} other {# выбрано}}", + "import_button": "Импортировать", + "importing": "Импортирование...", + "success": "{count, plural, one {1 контакт импортирован} other {# контактов импортировано}}", + "failed": "Импорт не выполнен", + "close": "Закрыть", + "file_too_large": "Файл слишком большой (макс. 5 МБ)" + }, + "export": { + "title": "Экспорт контактов", + "success": "{count, plural, one {1 контакт экспортирован} other {# контактов экспортировано}}" + }, + "bulk": { + "selected": "{count, plural, one {1 выбран} other {# выбрано}}", + "select_all": "Выбрать все", + "delete": "Удалить", + "delete_confirm_title": "Удалить контакты", + "delete_confirm": "Удалить {count, plural, one {1 контакт} other {# контактов}}?", + "deleted": "{count, plural, one {1 контакт удалён} other {# контактов удалено}}", + "add_to_group": "Добавить в группу", + "choose_group": "Выбрать группу", + "adding_contacts": "Добавляем {count, plural, one {1 контакт} other {# контактов}}", + "added_to_group": "Контакты добавлены в группу", + "export": "Экспорт", + "clear": "Очистить выбор" + }, + "toast": { + "created": "Контакт создан", + "updated": "Контакт обновлён", + "deleted": "Контакт удалён", + "error_create": "Не удалось создать контакт", + "error_update": "Не удалось обновить контакт", + "error_delete": "Не удалось удалить контакт" + }, + "context_menu": { + "open": "Открыть", + "edit": "Изменить", + "send_email": "Отправить письмо", + "add_to_group": "Добавить в группу", + "export_vcard": "Экспортировать как vCard", + "delete": "Удалить", + "call": "Позвонить", + "duplicate": "Дублировать", + "print": "Печать" + }, + "filters": { + "toggle": "Фильтры", + "select": "Выбрать", + "clear": "Очистить", + "close": "Закрыть", + "title": "Расширенные фильтры", + "organization": "Компания", + "organization_placeholder": "напр. Acme", + "job_title": "Должность", + "job_title_placeholder": "напр. Дизайнер", + "location": "Местоположение", + "location_placeholder": "Город или страна", + "email_domain": "Домен e-mail", + "email_domain_placeholder": "primer.ru", + "birthday_month": "День рождения в", + "any_month": "Любой месяц", + "has_email": "С эл. почтой", + "has_phone": "С телефоном", + "has_photo": "С фото", + "sort_by": "Сортировать по", + "sort_first_name": "Имя", + "sort_last_name": "Фамилия" + }, + "open_categories": "Открыть категории" + }, + "calendar": { + "title": "Календарь", + "back_to_email": "Вернуться к почте", + "back_to_month": "Вернуться к месяцу", + "my_calendars": "Календари", + "birthday_calendar": "Дни рождения", + "mini_calendar_change": "Нажмите для смены месяца", + "views": { + "month": "Месяц", + "week": "Неделя", + "day": "День", + "agenda": "Повестка", + "today": "Сегодня", + "month_hint": "Месяц (m)", + "week_hint": "Неделя (w)", + "day_hint": "День (d)", + "agenda_hint": "Повестка (a)", + "tasks": "Задачи", + "tasks_hint": "Задачи (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# более раннее событие} few {# более ранних события} many {# более ранних событий} other {# более ранних события}}", + "hidden_after": "{count, plural, one {# более позднее событие} few {# более поздних события} many {# более поздних событий} other {# более поздних события}}", + "show_all_hours": "Показать все часы", + "show_display_hours": "Показать только {start} – {end}", + "create": "Создать событие", + "edit": "Редактировать событие", + "delete": "Удалить событие", + "details": "Детали события", + "no_events": "Нет событий", + "all_day": "Весь день", + "more": "ещё {count}", + "show_more": "Развернуть", + "show_less": "Свернуть", + "no_title": "(Без названия)", + "resize": "Изменить размер события", + "duplicate": "Дублировать", + "today_header": "Сегодня", + "tomorrow_header": "Завтра", + "agenda_show_earlier": "Показать более ранние события", + "agenda_loading": "Загрузка…", + "agenda_range_start": "События с {date}", + "agenda_range_end": "События до {date}", + "export_ics": "Экспорт в .ics", + "copy_title": "Скопировать название", + "copy_link": "Скопировать ссылку встречи", + "go_to_today": "Перейти к сегодня", + "new_all_day_event": "Новое событие на весь день", + "new_event": "Новое событие", + "new_task": "Новая задача" + }, + "detail": { + "add_note": "Добавить заметку...", + "save_note": "Сохранить", + "note_saved": "Заметка добавлена", + "open_link": "Открыть ссылку", + "copy_location": "Копировать место", + "meeting_link": "Ссылка на встречу", + "tentative": "Предварительно", + "cancelled": "Отменено", + "delete_confirm": "Удалить это событие?" + }, + "form": { + "title": "Название", + "description": "Описание", + "location": "Место", + "meeting_link": "Ссылка на встречу", + "start_date": "Дата начала", + "end_date": "Дата окончания", + "start_time": "Время начала", + "end_time": "Время окончания", + "all_day_event": "Событие на весь день", + "calendar_select": "Календарь", + "save": "Сохранить", + "cancel": "Отмена", + "delete_confirm": "Вы уверены, что хотите удалить это событие?", + "color": "Цвет" + }, + "participants": { + "availability": { + "title": "Доступность", + "free": "Свободен", + "busy": "Занят", + "tentative": "Предварительно", + "unknown": "Не пользователь этого сервера", + "checking": "Проверка…" + }, + "title": "Участники", + "add": "Добавить участника", + "organizer": "Организатор", + "attendee": "Участник", + "accepted": "Принято", + "declined": "Отклонено", + "tentative": "Предварительно", + "needs_action": "Требует ответа", + "remove": "Удалить", + "edit": "Редактировать", + "email_placeholder": "Добавьте адрес или найдите контакт", + "send_invitations": "Отправить приглашения участникам", + "status_summary": "{accepted} принято, {pending} ожидает", + "invited_by": "Приглашение от {name}", + "respond_below": "Ответьте с помощью кнопок ниже", + "rsvp_label": "Ваш ответ", + "cancel_notification": "Участники будут уведомлены об отмене", + "you_organizer": "Вы являетесь организатором", + "you_attendee": "Вы являетесь участником", + "no_participants": "Нет участников", + "count": "{count, plural, one {# участник} other {# участников}}" + }, + "recurrence": { + "title": "Повторение", + "none": "Не повторяется", + "daily": "Ежедневно", + "weekly": "Еженедельно", + "monthly": "Ежемесячно", + "yearly": "Ежегодно", + "every_n_days": "Каждые {count} дней", + "every_n_weeks": "Каждые {count} недель", + "every_n_months": "Каждые {count} месяцев", + "until": "До", + "occurrences": "{count} повторений", + "custom": "Свой вариант…", + "edit_custom": "Изменить настраиваемое повторение", + "every_n_years": "Каждые {count} лет", + "on_days": "в {days}", + "on_day_n": "{day}-го числа", + "on_the_nth": "в {nth} {day}", + "in_month": "в {month}", + "nth_1": "первый", + "nth_2": "второй", + "nth_3": "третий", + "nth_4": "четвёртый", + "nth_last": "последний", + "editor_freq_day": "День", + "editor_freq_week": "Неделя", + "editor_freq_month": "Месяц", + "editor_freq_year": "Год", + "editor_repeats_on": "Повторяется", + "editor_every": "Каждые", + "editor_unit_days": "дн.", + "editor_unit_weeks": "нед.", + "editor_unit_months": "мес.", + "editor_unit_years": "г.", + "editor_on_day": "в день", + "editor_on_the": "в", + "editor_in": "в месяце", + "editor_ends": "Заканчивается", + "editor_never": "Никогда", + "editor_ends_on": "Дата", + "editor_ends_after": "После", + "editor_occurrences": "повторений" + }, + "recurrence_scope": { + "edit_title": "Редактировать повторяющееся событие", + "delete_title": "Удалить повторяющееся событие", + "description": "Это повторяющееся событие. Какие события вы хотите изменить?", + "this_event": "Только это событие", + "this_and_future": "Это и последующие события", + "all_events": "Все события", + "cancel": "Отмена", + "save": "Сохранить", + "delete": "Удалить", + "rsvp_title": "Ответить на повторяющееся событие", + "rsvp_description": "Это повторяющееся событие. К каким событиям относится ваш ответ?", + "respond": "Ответить" + }, + "alerts": { + "title": "Напоминание", + "none": "Без напоминания", + "at_time": "В момент события", + "minutes_before": "{count, plural, one {За # минуту} other {За # минут}}", + "hours_before": "{count, plural, one {За # час} other {За # часов}}", + "days_before": "{count, plural, one {За # день} other {За # дней}}", + "weeks_before": "{count, plural, one {За # неделю} few {За # недели} many {За # недель} other {За # недели}}", + "unit_minutes_before": "минут до", + "unit_hours_before": "часов до", + "unit_days_before": "дней до", + "unit_weeks_before": "недель до", + "add": "Добавить напоминание", + "remove": "Удалить напоминание", + "amount": "Значение напоминания", + "unit": "Единица напоминания", + "action": "Тип напоминания", + "action_display": "Уведомление", + "action_email": "Эл. почта", + "by_email": "{label} по эл. почте", + "kept": "{count, plural, one {Сохраняется ещё # напоминание, которое нельзя изменить здесь} few {Сохраняются ещё # напоминания, которые нельзя изменить здесь} many {Сохраняются ещё # напоминаний, которые нельзя изменить здесь} other {Сохраняются ещё # напоминания, которые нельзя изменить здесь}}", + "other": "{count, plural, one {Ещё # напоминание} few {Ещё # напоминания} many {Ещё # напоминаний} other {Ещё # напоминания}}", + "calendar_defaults": "Напоминания календаря по умолчанию", + "customize": "Настроить", + "use_defaults": "Использовать напоминания календаря по умолчанию" + }, + "settings": { + "limit_hours": "Ограничить видимые часы", + "limit_hours_desc": "Показывать в представлениях дня и недели только эти часы. События за их пределами доступны через стрелки вверху и внизу каждого дня.", + "visible_hours": "Видимые часы", + "visible_hours_start": "Начало видимых часов", + "visible_hours_end": "Конец видимых часов", + "hide_non_working_days": "Скрывать нерабочие дни", + "hide_non_working_days_desc": "Не показывать в представлении недели дни, в которые вы не работаете. Их события по-прежнему видны в представлениях месяца, дня и повестки.", + "working_days": "Рабочие дни", + "title": "Настройки календаря", + "default_view": "Вид по умолчанию", + "week_starts_on": "Неделя начинается с", + "time_format": "Формат времени", + "default_calendar": "Календарь по умолчанию", + "default_reminder": "Напоминание по умолчанию", + "time_format_12h": "12-часовой", + "time_format_24h": "24-часовой", + "notifications_enabled": "Уведомления о событиях", + "notifications_enabled_desc": "Показывать оповещения о предстоящих событиях", + "notification_sound": "Звук уведомления", + "notification_sound_desc": "Воспроизводить звук для оповещений календаря", + "invitation_parsing": "Разбор приглашений по email", + "invitation_parsing_desc": "Определять приглашения календаря во вложениях email и показывать действия", + "show_time_in_month_view": "Показывать время в виде месяца", + "show_time_in_month_view_desc": "Отображать время события в виде месячного календаря", + "show_week_numbers": "Показывать номера недель", + "show_week_numbers_desc": "Отображать номера недель в мини-календаре", + "calendar_free_scroll": "Свободная прокрутка", + "calendar_free_scroll_desc": "Непрерывно прокручивать месяцы, недели и дни вместо одного периода за раз", + "enable_tasks": "Включить задачи", + "enable_tasks_desc": "Показывать представление задач в календаре для управления делами", + "show_tasks_on_calendar": "Показывать задачи в календаре", + "show_tasks_on_calendar_desc": "Отображать задачи в видах «месяц», «неделя» и «день»", + "hover_preview": "Предпросмотр при наведении", + "hover_preview_desc": "Показывать всплывающее окно с деталями при наведении на события календаря", + "hover_preview_instant": "Мгновенно", + "hover_preview_delay_500ms": "Задержка 0,5 секунды", + "hover_preview_delay_1s": "Задержка 1 секунда", + "hover_preview_delay_2s": "Задержка 2 секунды", + "hover_preview_off": "Отключено", + "organizer_identity": "Организовывать приглашения как", + "organizer_identity_desc": "Адрес календаря, используемый как организатор при приглашении людей на события.", + "organizer_identity_failed": "Не удалось изменить адрес организатора", + "show_birthday_calendar": "Календарь дней рождения", + "show_birthday_calendar_desc": "Показать виртуальный календарь с днями рождения из ваших контактов" + }, + "days": { + "monday": "Понедельник", + "tuesday": "Вторник", + "wednesday": "Среда", + "thursday": "Четверг", + "friday": "Пятница", + "saturday": "Суббота", + "sunday": "Воскресенье", + "mon": "Пн", + "tue": "Вт", + "wed": "Ср", + "thu": "Чт", + "fri": "Пт", + "sat": "Сб", + "sun": "Вс" + }, + "notifications": { + "event_created": "Событие создано", + "event_updated": "Событие обновлено", + "event_deleted": "Событие удалено", + "calendar_created": "Календарь создан", + "calendar_deleted": "Календарь удалён", + "event_move_error": "Не удалось переместить событие", + "event_resize_error": "Не удалось изменить размер события", + "alert_title": "Предстоящее событие", + "alert_now": "Начинается сейчас", + "alert_in_minutes": "Через {count} мин", + "invitation_sent": "Приглашения отправлены", + "invitations_denied_title": "Не удаётся отправить приглашения", + "invitations_denied": "Сервер отказался отправить приглашения: {reason}. Сохранить событие без их отправки?", + "save_without_invitations": "Сохранить без приглашений", + "rsvp_updated": "Ответ обновлён", + "rsvp_error": "Не удалось обновить ответ", + "event_duplicated": "Событие дублировано", + "event_error": "Не удалось сохранить событие", + "task_due": "Срок задачи", + "event_exported": "Событие экспортировано", + "title_copied": "Название скопировано", + "link_copied": "Ссылка скопирована" + }, + "status": { + "loading_calendars": "Загрузка календарей...", + "loading_events": "Загрузка событий..." + }, + "quick_create": { + "placeholder": "Название нового события", + "aria_label": "Быстрое создание события", + "add_title": "Добавьте название", + "more_options": "Другие параметры" + }, + "nav_prev": "Предыдущий", + "nav_next": "Следующий", + "import": { + "title": "Импорт календаря", + "tab_file": "Файл", + "tab_url": "URL-адрес", + "select_file": "Выберите файл .ics", + "drop_file": "или перетащите файл сюда", + "supported_formats": "Поддерживает файлы iCalendar (.ics)", + "url_description": "Введите URL внешней ленты iCalendar (.ics) для импорта событий.", + "url_placeholder": "https://пример.рф/календарь.ics", + "url_hint": "Поддерживает URL iCalendar (.ics)", + "fetch": "Получить", + "invalid_url": "Введите корректный URL", + "url_fetch_failed": "Не удалось получить календарь по URL", + "parsing": "Разбор файла календаря...", + "parsed_events": "Найдено {count} событий", + "no_events": "События в файле не найдены", + "select_all": "Выбрать все", + "deselect_all": "Снять выделение", + "target_calendar": "Импортировать в календарь", + "import_button": "Импортировать выбранные", + "importing": "Импортирование событий...", + "success": "{count} событий успешно импортировано", + "error": "Не удалось импортировать календарь", + "file_too_large": "Файл превышает лимит 10 МБ", + "invalid_format": "Неверный формат файла календаря" + }, + "webcal_action": { + "title": "Открыть ссылку календаря", + "description": "Как вы хотите использовать \"{name}\"?", + "import_title": "Импортировать один раз", + "import_description": "Загрузить события сейчас и скопировать их в один из ваших календарей.", + "subscribe_title": "Подписаться", + "subscribe_description": "Автоматически синхронизировать этот календарь как отдельный календарь.", + "cancel": "Отмена" + }, + "management": { + "title": "Управление календарями", + "description": "Создавайте, переименовывайте и настраивайте свои календари. Щёлкните правой кнопкой мыши на календаре в боковой панели для быстрой смены цвета.", + "name": "Имя", + "name_placeholder": "Название календаря", + "color": "Цвет", + "change_color": "Изменить цвет", + "random_color": "Новый случайный цвет", + "add_calendar": "Добавить календарь", + "edit": "Редактировать", + "delete": "Удалить", + "save": "Сохранить", + "create": "Создать", + "cancel": "Отмена", + "default": "По умолчанию", + "confirm_delete": "Удалить «{name}»? Все события в этом календаре будут удалены.", + "confirm_clear": "Очистить все события из «{name}»? Это действие нельзя отменить.", + "set_default": "Сделать по умолчанию", + "default_updated": "Календарь по умолчанию обновлён", + "error_default": "Не удалось установить календарь по умолчанию", + "clear_events": "Очистить события", + "events_cleared": "{count} событий очищено", + "error_clear": "Не удалось очистить события календаря", + "calendar_created": "Календарь создан", + "calendar_updated": "Календарь обновлён", + "calendar_deleted": "Календарь удалён", + "color_updated": "Цвет календаря обновлён", + "error_create": "Не удалось создать календарь", + "error_update": "Не удалось обновить календарь", + "error_delete": "Не удалось удалить календарь", + "caldav_url": "URL CalDAV", + "copy_url": "Скопировать CalDAV URL", + "url_copied": "CalDAV URL скопирован в буфер обмена", + "share": "Поделиться календарём", + "new_event_in_calendar": "Новое событие в этом календаре", + "shared_with": "Доступ открыт: {names}", + "shared_with_count": "{count, plural, one {Доступ открыт # человеку} few {Доступ открыт # людям} many {Доступ открыт # людям} other {Доступ открыт # человека}}", + "move_up": "Переместить вверх", + "move_down": "Переместить вниз", + "kind": "Содержимое", + "kind_events": "События", + "kind_tasks": "Задачи", + "kind_both": "События и задачи", + "kind_hint": "Определяет, какие приложения видят этот календарь при синхронизации через CalDAV. Позже изменить нельзя.", + "default_alerts": "Напоминания по умолчанию", + "default_alerts_title": "Напоминания по умолчанию для «{name}»", + "default_alerts_hint": "Новые события в этом календаре получают эти напоминания, если их не изменить в самом событии. Изменения здесь не затрагивают существующие события. Напоминания по эл. почте отправляет почтовый сервер.", + "default_alerts_timed": "События со временем", + "default_alerts_all_day": "События на весь день", + "default_alerts_updated": "Напоминания по умолчанию обновлены" + }, + "subscription": { + "title": "Подписка iCal", + "section_title": "Подписки iCal", + "description": "Подпишитесь на внешнюю ленту iCalendar. События будут автоматически синхронизированы в отдельном календаре. Поддерживает URLs вида https:// и webcal://.", + "url_label": "URL календаря", + "url_placeholder": "https://example.com/calendar.ics или webcal://...", + "name_label": "Название календаря", + "name_placeholder": "напр. Государственные праздники", + "color_label": "Цвет", + "refresh_interval": "Интервал обновления", + "interval_15": "Каждые 15 минут", + "interval_30": "Каждые 30 минут", + "interval_60": "Каждый час", + "interval_360": "Каждые 6 часов", + "interval_1440": "Каждый день", + "subscribe": "Подписаться", + "subscribing": "Подписка...", + "save": "Сохранить изменения", + "saving": "Сохранение...", + "edit": "Изменить", + "edit_title": "Изменить подписку", + "updated": "«{name}» обновлена", + "update_error": "Не удалось обновить подписку", + "invalid_url": "Введите корректный URL", + "success": "Подписка на «{name}» оформлена", + "error": "Не удалось добавить подписку", + "refresh": "Обновить сейчас", + "refresh_success": "Подписка обновлена", + "refresh_error": "Не удалось обновить подписку", + "unsubscribe": "Отписаться", + "confirm_delete": "Отписаться от «{name}»? Календарь и все его события будут удалены.", + "deleted": "Подписка удалена", + "delete_error": "Не удалось удалить подписку", + "last_refreshed": "Последнее обновление: {time}" + }, + "tasks": { + "label": "Задачи", + "no_tasks": "Нет задач", + "no_title": "(Без названия)", + "mark_complete": "Отметить как выполненное", + "mark_incomplete": "Отметить как невыполненное", + "filter_all": "Все", + "filter_pending": "Ожидающие", + "filter_completed": "Завершённые", + "filter_overdue": "Просроченные", + "show_completed": "Показывать завершённые", + "create": "Новая задача", + "edit": "Редактировать задачу", + "title_placeholder": "Название задачи", + "description_placeholder": "Добавьте описание...", + "due_date": "Срок выполнения", + "include_time": "Включить время", + "priority": "Приоритет", + "priority_none": "Нет", + "priority_high": "Высокий", + "priority_medium": "Средний", + "priority_low": "Низкий", + "progress": "Статус", + "progress_needs_action": "Требует действия", + "progress_in_process": "В процессе", + "progress_completed": "Завершено", + "progress_cancelled": "Отменено", + "calendar": "Календарь", + "alert": "Напоминание", + "alert_none": "Нет", + "alert_at_time": "В момент срока", + "alert_5min": "За 5 минут", + "alert_15min": "За 15 минут", + "alert_30min": "За 30 минут", + "alert_1hr": "За 1 час", + "alert_1day": "За 1 день", + "delete": "Удалить", + "cancel": "Отмена", + "save": "Сохранить", + "quick_add_placeholder": "Добавить задачу...", + "due_today": "Сегодня", + "due_tomorrow": "Завтра", + "overdue": "Просрочено" + }, + "months": { + "jan": "янв.", + "feb": "февр.", + "mar": "март", + "apr": "апр.", + "may": "май", + "jun": "июнь", + "jul": "июль", + "aug": "авг.", + "sep": "сент.", + "oct": "окт.", + "nov": "нояб.", + "dec": "дек.", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Открыть меню", + "toolbar": { + "more": "Другие действия", + "select_view": "Сменить вид, сейчас {view}" + }, + "other_calendars": "Другие календари", + "calendar_options": "Параметры: {name}" + }, + "advanced_search": { + "title": "Расширенный поиск", + "from": "От", + "from_placeholder": "Email или имя отправителя", + "to": "Кому", + "to_placeholder": "Email или имя получателя", + "subject": "Тема", + "subject_placeholder": "Тема содержит...", + "body": "Текст письма", + "body_placeholder": "Текст содержит...", + "folder": "Папка", + "all_folders": "Все папки", + "all_folders_except_spam_trash": "Все папки, кроме спама и корзины", + "has_attachment": "Вложения", + "date_after": "После", + "date_before": "До", + "size_min": "Больше чем (КБ)", + "size_max": "Меньше чем (КБ)", + "starred": "Со звёздочкой", + "unread": "Непрочитанные", + "read": "Прочитанные", + "yes": "Да", + "no": "Нет", + "clear": "Очистить", + "clear_all": "Очистить всё", + "filters_active": "{count} фильтр", + "filters_active_plural": "{count} фильтров", + "toggle_filters": "Ещё", + "search_hint": "Используйте расширенные фильтры для точного поиска", + "advanced_filters_tooltip": "Расширенные фильтры поиска", + "results_found": "{count, plural, =0 {Ничего не найдено} one {# результат} other {# результатов}}", + "results_found_more": "Найдено {count}+ результатов", + "suggestions_recent": "Недавние запросы", + "suggestions_people": "Люди", + "suggestions_from": "Искать письма от {address}", + "suggestions_to": "Искать письма для {address}", + "suggestions_remove_recent": "Удалить из недавних запросов", + "has_words": "Содержит слова", + "size": "Размер", + "date_within": "Дата", + "search_in": "Искать в", + "has_attachment_check": "Есть вложения", + "search": "Найти" + }, + "welcome": { + "title": "Добро пожаловать в ваш почтовый ящик", + "tip_compose": "Нажмите c для создания нового письма", + "tip_shortcuts": "Нажмите ? для просмотра всех сочетаний клавиш", + "tip_sidebar": "Найдите Календарь, Контакты и Настройки в боковом меню", + "tip_settings": "Настройте интерфейс в разделе «Настройки»", + "tip_compose_touch": "Нажмите кнопку с карандашом, чтобы написать новое письмо", + "tip_sidebar_touch": "Календарь, Контакты и Настройки находятся на нижней панели", + "got_it": "Понятно", + "settings": "Настройки", + "dismiss": "Закрыть", + "start_tour": "Начать тур" + }, + "demo_welcome": { + "title": "Добро пожаловать в Bulwark Mail", + "description": "Откройте для себя полнофункциональный веб-клиент электронной почты - прямо в браузере. Все данные хранятся на вашем устройстве, поэтому вы можете смело всё проверить.", + "feature_email": "Чтение и написание писем", + "feature_organize": "Метки, звёздочки и папки", + "feature_shortcuts": "Сочетания клавиш", + "feature_privacy": "100% конфиденциальный демо-режим", + "hint": "Нажмите на любое письмо слева, чтобы начать, или пройдите тур ниже." + }, + "files": { + "office_edit": "Редактировать документ", + "office_loading": "Открытие редактора документов…", + "office_error": "Не удалось открыть редактор документов.", + "office_close": "Закрыть редактор", + "open_in_new_tab": "Открыть в новой вкладке", + "title": "Файлы", + "search_placeholder": "Поиск файлов...", + "empty_state_title": "Файлов пока нет", + "upload_files": "Загрузить файлы", + "new_folder": "Новая папка", + "new_folder_name": "Название папки", + "rename": "Переименовать", + "rename_title": "Переименовать", + "new_name": "Новое название", + "name_forbidden_chars": "Сервер не допускает эти символы в именах: {chars}", + "name_reserved": "Это имя зарезервировано на сервере.", + "delete": "Удалить", + "delete_confirm_title": "Удалить ресурс", + "delete_confirm_message": "Вы уверены, что хотите удалить «{name}»? Это действие нельзя отменить.", + "download": "Скачать", + "name": "Имя", + "size": "Размер", + "modified": "Дата изменения", + "type": "Тип", + "folder": "Папка", + "file": "Файл", + "uploading": "Загрузка...", + "upload_success": "{count, plural, one {1 файл загружен} other {# файлов загружено}}", + "upload_canceled": "Загрузка отменена", + "upload_canceled_partial": "{count, plural, one {# файл был загружен} few {# файла было загружено} many {# файлов было загружено} other {# файла было загружено}} до отмены", + "upload_error": "Не удалось загрузить файл", + "create_folder_success": "Папка создана", + "create_folder_error": "Не удалось создать папку", + "delete_success": "Успешно удалено", + "delete_error": "Не удалось удалить", + "rename_success": "Успешно переименовано", + "rename_error": "Не удалось переименовать", + "download_error": "Не удалось скачать", + "not_available": "Хранилище файлов недоступно на этом сервере", + "cancel": "Отмена", + "create": "Создать", + "save": "Сохранить", + "no_results": "Нет файлов, соответствующих вашему запросу", + "batch_delete_confirm_message": "Вы уверены, что хотите удалить {count, plural, one {1 элемент} other {# элементов}}? Это действие нельзя отменить.", + "batch_delete_success": "{count, plural, one {1 элемент удалён} other {# элементов удалено}}", + "grid_view": "Сеткой", + "list_view": "Списком", + "details": "Подробности", + "path": "Путь", + "preview": "Предпросмотр", + "preview_error": "Не удалось загрузить предпросмотр", + "cut": "Вырезать", + "copy": "Копировать", + "paste": "Вставить", + "move_success": "{count, plural, one {1 элемент перемещён} other {# элементов перемещено}}", + "move_error": "Не удалось переместить", + "paste_success": "Вставлено успешно", + "paste_error": "Не удалось вставить", + "new_text_file": "Новый текстовый файл", + "file_name": "Имя файла", + "retry": "Повторить", + "refresh": "Обновить", + "duplicate": "Дублировать", + "duplicate_success": "Дублирование выполнено успешно", + "duplicate_error": "Не удалось дублировать", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Файл создан", + "create_file_error": "Не удалось создать файл", + "favorites": "Избранное", + "recent": "Недавние", + "show_in_folder": "Показать в папке", + "remove_from_recent": "Убрать из недавних", + "upload_folder": "Загрузить папку", + "file_too_large": "«{name}» превышает максимальный размер файла ({max})", + "undo": "Отменить", + "undo_success": "Действие отменено", + "undo_error": "Не удалось отменить", + "toolbar": "Действия с файлами", + "file_list": "Файлы и папки", + "context_menu": "Действия", + "settings_ascending": "По возрастанию", + "settings_descending": "По убыванию", + "disabled_title": "Функция файлов отключена вашим администратором", + "disabled_description": "Загрузка больших файлов через WebDAV может вызвать нестабильность Stalwart/RocksDB, включая ошибки нехватки памяти и невосстановимое использование диска. Удалённые файлы могут не быть немедленно удалены из хранилища. Эта функция не рекомендуется для рабочих сред.", + "no_accounts": "Нет подключённых учётных записей.", + "open_folder_tree": "Открыть дерево папок", + "migration_title": "Обновление ваших файлов…", + "migration_description": "Папки и файлы упорядочиваются в правильную структуру. Это происходит только один раз.", + "new": "Создать", + "my_files": "Мои файлы", + "folders": "Папки", + "files_heading": "Файлы", + "open": "Открыть", + "star": "Добавить в избранное", + "unstar": "Убрать из избранного", + "expand_folder": "Развернуть {name}", + "collapse_folder": "Свернуть {name}", + "sidebar_hint": "Добавляйте папки в избранное и открывайте файлы, чтобы они появлялись здесь.", + "breadcrumbs": "Путь к папке", + "show_path": "Показать скрытые папки пути", + "selection_actions": "Действия с выбранными элементами", + "selected_count": "{count, plural, one {Выбран # элемент} few {Выбрано # элемента} many {Выбрано # элементов} other {Выбрано # элемента}}", + "clear_selection": "Снять выделение", + "select_all": "Выбрать все", + "select_item": "Выбрать {name}", + "more_actions": "Другие действия для {name}", + "paste_here": "Вставить в эту папку", + "paste_count": "{count, plural, one {Вставить # элемент} few {Вставить # элемента} many {Вставить # элементов} other {Вставить # элемента}}", + "sort_by": "Сортировка", + "view_mode": "Вид", + "clear_search": "Очистить поиск", + "no_results_for": "В этой папке ничего не найдено по запросу «{query}»", + "download_count": "{count, plural, one {Скачать # файл} few {Скачать # файла} many {Скачать # файлов} other {Скачать # файла}}", + "delete_count": "{count, plural, one {Удалить # элемент} few {Удалить # элемента} many {Удалить # элементов} other {Удалить # элемента}}", + "close_details": "Закрыть подробности", + "file_kind": "Файл {ext}", + "folder_summary": "{folders, plural, =0 {Нет папок} one {# папка} few {# папки} many {# папок} other {# папки}}, {files, plural, =0 {нет файлов} one {# файл} few {# файла} many {# файлов} other {# файла}}", + "details_hint": "Выберите файл или папку, чтобы увидеть подробности.", + "sharing": "Общий доступ", + "shared_with_count": "{count, plural, one {Доступ открыт # человеку} few {Доступ открыт # людям} many {Доступ открыт # людям} other {Доступ открыт # человека}}", + "only_you": "Только вы", + "empty_folder_title": "Эта папка пуста", + "empty_folder_description": "Перетащите файлы сюда или загрузите их с устройства.", + "empty_folder_read_only": "Вы не можете добавлять файлы в эту папку.", + "choose_account": "Выберите учётную запись", + "drop_to_upload": "Отпустите, чтобы загрузить в {folder}", + "uploading_count": "Загрузка {current} из {total}", + "dismiss": "Закрыть", + "zoom_in": "Увеличить", + "zoom_out": "Уменьшить", + "zoom_reset": "По размеру экрана", + "rotate": "Повернуть", + "close_preview": "Закрыть" + }, + "smime": { + "your_certificates": "Ваши сертификаты", + "your_certificates_desc": "Импортируйте и управляйте своими сертификатами S/MIME для подписи и шифрования писем", + "recipient_certificates": "Сертификаты получателей", + "recipient_certificates_desc": "Публичные сертификаты для шифрования писем получателям", + "identity_bindings": "Привязки ключей к удостоверениям", + "identity_bindings_desc": "Привяжите сертификаты S/MIME к вашим почтовым удостоверениям", + "defaults_title": "Параметры по умолчанию", + "defaults_desc": "Настройте поведение подписи и шифрования по умолчанию", + "import_pkcs12": "Импорт PKCS#12 (.p12/.pfx)", + "import_public_cert": "Импортировать сертификат", + "no_certificates": "Сертификаты ещё не импортированы", + "no_recipient_certs": "Нет сертификатов получателей", + "expires": "Истекает", + "expired": "Истёк", + "bound_to": "Привязан к", + "no_key_bound": "Нет", + "lock": "Заблокировать ключ", + "unlock": "Разблокировать ключ", + "details": "Показать подробности", + "delete": "Удалить", + "encrypt_by_default": "Шифровать по умолчанию", + "encrypt_by_default_desc": "Автоматически шифровать письма, когда у всех получателей есть сертификаты", + "remember_unlocked": "Запоминать разблокированные ключи", + "remember_unlocked_desc": "Оставлять ключи разблокированными на время этой сессии браузера", + "sign_default_for": "Подписывать по умолчанию для", + "enter_p12_passphrase": "Введите парольную фразу PKCS#12", + "p12_passphrase_desc": "Введите парольную фразу, которая защищает этот файл сертификата", + "enter_storage_passphrase": "Установите парольную фразу хранилища", + "storage_passphrase_desc": "Выберите парольную фразу для защиты этого ключа при хранении в браузере", + "next": "Далее", + "import": "Импортировать", + "unlock_key": "Разблокировать ключ", + "unlock_key_desc": "Введите парольную фразу хранилища, чтобы разблокировать ключ для подписи или расшифрования", + "passphrase_placeholder": "Введите парольную фразу", + "confirm_passphrase_placeholder": "Подтвердите парольную фразу", + "passphrase_mismatch": "Парольные фразы не совпадают", + "cancel": "Отмена", + "processing": "Обработка…", + "close": "Закрыть", + "certificate_details": "Сведения о сертификате", + "cert_subject": "Субъект", + "cert_issuer": "Издатель", + "cert_email": "Электронная почта", + "cert_serial": "Серийный номер", + "cert_validity": "Срок действия", + "cert_fingerprint": "Отпечаток (SHA-256)", + "cert_algorithm": "Алгоритм", + "cert_capabilities": "Возможности", + "cert_source": "Источник", + "cert_expired": "Срок действия этого сертификата истёк", + "cert_not_yet_valid": "Этот сертификат ещё не действует", + "cap_sign": "Подпись", + "cap_encrypt": "Шифрование", + "cap_none": "Нет", + "show_passphrase": "Показать парольную фразу", + "hide_passphrase": "Скрыть парольную фразу", + "sign_toggle": "Подписывать", + "encrypt_toggle": "Шифровать", + "missing_recipient_certs": "Отсутствуют сертификаты для: {emails}", + "missing_sender_cert": "К этому удостоверению не привязан сертификат", + "status_encrypted_ok": "Это сообщение было зашифровано", + "status_encrypted_no_key": "Сообщение зашифровано, но подходящий ключ не найден", + "status_encrypted_failed": "Не удалось расшифровать это сообщение", + "status_signed_valid": "Подпись проверена", + "status_signed_invalid": "Проверка подписи не удалась", + "status_signed_expired_cert": "Подписано просроченным сертификатом", + "status_signed_self_signed": "Подпись действительна, но сертификат самоподписанный (не доверенный)", + "status_signed_mismatch": "Подпись действительна, но подписант не совпадает с отправителем", + "status_unsupported": "Неподдерживаемый формат S/MIME", + "auto_import_signer_certs": "Автоимпорт сертификатов подписантов", + "auto_import_signer_certs_desc": "Автоматически сохранять сертификаты из проверенных подписанных писем для будущего шифрования", + "export": "Экспорт", + "enter_export_passphrase": "Установите парольную фразу для экспорта", + "export_passphrase_desc": "Выберите парольную фразу для защиты экспортируемого файла PKCS#12", + "export_storage_desc": "Введите парольную фразу хранилища для расшифрования ключа перед экспортом", + "incorrect_passphrase": "Неверная парольная фраза" + }, + "tour": { + "step_counter": "Шаг {current} из {total}", + "skip": "Пропустить тур", + "back": "Назад", + "next": "Далее", + "finish": "Завершить", + "take_a_tour": "Пройдите тур по интерфейсу", + "restart_title": "Ознакомительный тур", + "restart_desc": "Повторить пошаговое руководство по интерфейсу", + "restart_button": "Перезапустить тур", + "show_on_new_devices_title": "Показывать на новых устройствах", + "show_on_new_devices_desc": "Повторить приветственный баннер и тур при первом входе на новом устройстве, даже если вы уже завершили их в другом месте", + "sidebar_title": "Ваши почтовые ящики", + "sidebar_desc": "Это боковая панель с папками. Нажмите на любой почтовый ящик для просмотра писем. Вы можете создавать папки, перетаскивать письма между ними и видеть количество непрочитанных.", + "compose_title": "Написать письмо", + "compose_desc": "Нажмите здесь, чтобы написать новое письмо. Вы можете добавить получателей, вложения и использовать форматирование текста.", + "search_title": "Поиск почты", + "search_desc": "Ищите по отправителю, теме или содержимому. Нажмите на значок фильтра для расширенных параметров: диапазон дат, вложения и письма со звёздочкой.", + "email_list_title": "Список писем", + "email_list_desc": "Здесь отображаются письма. Нажмите на письмо для чтения справа. Используйте флажок для выбора нескольких писем, затем массово переместите, удалите или пометьте их.", + "email_viewer_title": "Панель чтения", + "email_viewer_desc": "Выбранное письмо открывается здесь. Ответьте, перешлите, архивируйте или удалите с помощью кнопок панели. Также можно добавить звёздочку или цветную метку.", + "keywords_title": "Цветные метки", + "keywords_desc": "Организуйте почту с помощью цветных меток. Перетащите письмо на метку для маркировки или щёлкните правой кнопкой мыши для назначения меток.", + "calendar_title": "Календарь", + "calendar_desc": "Перейдите в календарь для управления событиями. Создавайте события, устанавливайте напоминания и просматривайте день, неделю или месяц.", + "contacts_title": "Контакты", + "contacts_desc": "Здесь находится ваша адресная книга. Импортируйте контакты, создавайте группы и нажмите на контакт для просмотра подробностей.", + "settings_title": "Настройки", + "settings_desc": "Настройте всё: тему, плотность, подписи, фильтры, сочетания клавиш, параметры календаря и многое другое.", + "shortcuts_title": "Сочетания клавиш", + "shortcuts_desc": "Опытные пользователи оценят это. Нажмите ? в любое время для просмотра доступных сочетаний. Вы можете перемещаться, создавать и управлять письмами без мыши.", + "compose_open_title": "Редактор писем", + "compose_open_desc": "Это редактор писем. Добавьте получателей, напишите сообщение, прикрепите файлы и используйте форматирование. Также можно сохранять черновики и использовать шаблоны.", + "calendar_view_title": "Ваш календарь", + "calendar_view_desc": "Вот ваш календарь с примерными событиями. Вы можете переключаться между видами «день», «неделя», «месяц» и «повестка» с помощью панели инструментов.", + "create_event_title": "Создать событие", + "create_event_desc": "Нажмите эту кнопку для создания нового события. Вы можете указать название, дату, время и добавить участников.", + "event_modal_title": "Детали события", + "event_modal_desc": "Это форма события. Заполните название, выберите дату и время, добавьте место или участников. Нажмите «Сохранить» после завершения - или закройте и продолжайте.", + "contacts_list_title": "Ваши контакты", + "contacts_list_desc": "Здесь ваши контакты. Нажмите на контакт для просмотра подробностей справа. Вы также можете создавать новые контакты, импортировать vCard или организовывать контакты в группы.", + "settings_tabs_title": "Меню настроек", + "settings_tabs_desc": "Здесь все категории настроек. Настройте внешний вид, управляйте идентификациями, настраивайте фильтры, конфигурируйте календарь и многое другое.", + "files_title": "Хранилище файлов", + "files_desc": "Файловый менеджер позволяет загружать, организовывать и делиться файлами - как личное облачное хранилище в вашей почте.", + "demo_banner_title": "Элементы управления демо", + "demo_banner_desc": "Вы в демо-режиме - всё остаётся в вашем браузере. Нажмите «Сбросить демо» в любое время, чтобы начать заново с чистыми данными.", + "quota_title": "Использование хранилища", + "quota_desc": "Отслеживайте размер вашего почтового ящика здесь. Круг заполняется по мере использования пространства." + }, + "unified_mailbox": { + "search_unavailable": "Поиск недоступен в объединённом представлении" + }, + "calendar_event_notifications": { + "someone": "Кто-то", + "invited": "{name} пригласил(а) вас на «{title}»", + "updated": "{name} обновил(а) «{title}»", + "cancelled": "{name} отменил(а) «{title}»", + "untitled": "Событие без названия" + }, + "share_notifications": { + "someone": "Кто-то", + "shared": "{name} поделился(ась) с вами {kind} «{object}»", + "changed": "{name} изменил(а) ваш доступ к {kind} «{object}»", + "revoked": "{name} отозвал(а) ваш доступ к {kind} «{object}»", + "object": { + "folder": "папкой", + "calendar": "календарём", + "address_book": "адресной книгой", + "files": "папкой файлов" + } + }, + "sharing": { + "title": "Поделиться «{name}»", + "description": "Предоставьте доступ другим пользователям или группам на этом сервере. Изменения вступают в силу немедленно.", + "no_shares": "Пока никому не предоставлен доступ.", + "add_person": "Добавить пользователя или группу", + "search_placeholder": "Искать по имени или email…", + "loading_principals": "Загрузка пользователей…", + "no_principals": "Других пользователей или групп не найдено.", + "no_match": "Нет совпадений.", + "remove": "Отозвать доступ", + "group": "Группа", + "share_added": "Доступ предоставлен", + "share_updated": "Доступ обновлён", + "share_removed": "Доступ отозван", + "share_failed": "Не удалось обновить общий доступ", + "preset": { + "freeBusy": "Только занятость", + "read": "Только чтение", + "readWrite": "Чтение и запись", + "manager": "Управляющий", + "custom": "Пользовательский" + } + }, + "quote_header": { + "reply_line": "{date}, {from} написал:", + "forwarded_separator": "---------- Пересланное сообщение ----------", + "from_label": "От", + "date_label": "Дата", + "subject_label": "Тема" + }, + "pwa_install": { + "title": "Установить {appName}", + "description": "Установите наше приложение для быстрого доступа и работы офлайн.", + "not_now": "Не сейчас", + "install": "Установить", + "dont_remind": "Больше не напоминать", + "dismiss_aria": "Закрыть запрос на установку" + }, + "deep_link": { + "message_not_found": "Это письмо больше недоступно.", + "thread_not_found": "Эта переписка больше недоступна.", + "folder_not_found": "Эта папка больше недоступна.", + "event_not_found": "Это событие больше недоступно.", + "file_not_found": "Этот файл больше недоступен.", + "account_unavailable": "Войдите в эту учётную запись, чтобы открыть ссылку.", + "copy_message": "Скопировать ссылку на письмо", + "copy_conversation": "Скопировать ссылку на переписку", + "copy_event": "Скопировать ссылку на событие", + "copied": "Ссылка скопирована", + "copy_failed": "Не удалось скопировать ссылку" + }, + "plugins": { + "oauth_callback": { + "completing": "Завершение авторизации…", + "completing_message": "Пожалуйста, подождите, пока устанавливается соединение.", + "received_title": "Авторизация получена", + "received_message": "Вы можете вернуться на вкладку, где начали подключение.", + "failed_title": "Ошибка авторизации", + "denied": "В авторизации отказано.", + "failed_detail": "Ошибка авторизации: {error}", + "missing_params": "Отсутствует код авторизации или состояние.", + "delivery_failed": "Не удалось передать результат авторизации на вкладку приложения. Закройте это окно и попробуйте снова." + }, + "settings": { + "title": "Плагины", + "description": "Плагины, добавленные администратором. Включайте и выключайте их для своего аккаунта.", + "description_uploads": "Плагины добавляют в webmail новые функции. Включайте и выключайте их для своего аккаунта или устанавливайте свои.", + "description_demo": "В демо-режиме плагины недоступны.", + "install_button": "Установить плагин", + "install_hint": "Выберите ZIP-файл плагина.", + "install_hint_approval": "Выберите ZIP-файл плагина. Прежде чем вы сможете его включить, его должен одобрить администратор.", + "install_uploads_off": "Администратор не разрешает устанавливать плагины.", + "install_managed_id": "Администратор уже предоставляет плагин с таким именем, поэтому его нельзя заменить.", + "install_invalid": "Этот файл не является плагином, который можно установить: {details}", + "toast_installed": "Плагин установлен. Включите его ниже.", + "toast_installed_pending": "Плагин установлен и отправлен администратору на одобрение.", + "toast_enabled": "Плагин {name} включён", + "toast_disabled": "Плагин {name} выключен", + "toast_removed": "Плагин {name} удалён", + "toast_forced": "Администратор требует, чтобы плагин {name} оставался включённым", + "toast_approval_requested": "Плагин {name} ожидает одобрения администратором", + "empty_title": "Плагинов пока нет", + "empty_admin": "Администратор не добавил ни одного плагина.", + "empty_uploads": "Чтобы начать, установите плагин из ZIP-файла.", + "empty_demo": "Плагинам нужен настоящий аккаунт, поэтому в демо-режиме их нет.", + "syncing": "Проверка плагинов на сервере…", + "status_installed": "Выключен", + "status_enabled": "Запускается", + "status_running": "Включён", + "status_disabled": "Выключен", + "status_error": "Ошибка", + "badge_forced": "Обязательный", + "badge_managed": "От администратора", + "badge_yours": "Установлен вами", + "badge_awaiting": "Ожидает одобрения", + "toggle_label": "Включить или выключить плагин {name}", + "forced_note": "Администратор требует этот плагин, поэтому его нельзя выключить.", + "permissions": "Что он может делать", + "permissions_managed": "Что он может делать (одобрено администратором)", + "settings": "Настройки", + "remove": "Удалить", + "remove_title": "Удалить плагин {name}?", + "remove_message": "Плагин и все данные, которые он сохранил в этом браузере, будут удалены. Позже его можно установить снова.", + "remove_confirm": "Удалить", + "error_user_plugins_off": "Администратор отключил плагины, установленные пользователями, поэтому этот плагин не работает.", + "error_approval_unavailable": "Этот плагин был установлен до того, как появилось одобрение. Удалите и установите его снова, чтобы отправить на одобрение.", + "error_approval_denied": "Администратор не одобрил этот плагин.", + "error_approval_pending": "Ожидается одобрение этого плагина администратором.", + "error_auto_disabled": "Отключён, потому что постоянно выдавал ошибки. Включите его снова, чтобы попробовать ещё раз.", + "accounts_heading": "Подключённые аккаунты", + "account_on": "{label} на {host}", + "account_waiting": "{label} на {host} (вход не завершён)", + "disconnect": "Отключить", + "disconnect_failed": "Не удалось отключить {label}" + }, + "consent": { + "title": "Разрешить плагину {name} это?", + "intro": "Плагин запрашивает разрешения, перечисленные ниже. Он получит их, только если вы разрешите.", + "high_risk_note": "Отмеченные пункты дают плагину доступ к содержимому вашей почты или позволяют ему передавать данные на сторону. Разрешайте их только плагинам, которым доверяете.", + "revoke_hint": "Вы в любой момент можете выключить или удалить плагин в настройках.", + "deny": "Не разрешать", + "allow": "Разрешить" + }, + "permissions": { + "none": "Ему не нужны особые разрешения.", + "high_risk": "Требует осторожности", + "email_read": { + "title": "Читать вашу почту", + "body": "Видеть отправителей, получателей, темы и полный текст любого письма в вашей учётной записи, а также ваши папки." + }, + "email_write": { + "title": "Менять и упорядочивать вашу почту", + "body": "Добавлять и удалять теги, флажки и другие ключевые слова у ваших писем, перемещать их между вашими папками и в архив, а также сохранять черновики." + }, + "email_send": { + "title": "Менять письма перед отправкой", + "body": "Читать и менять получателей, тему, текст и вложения писем, которые вы пишете, а также останавливать отправку письма." + }, + "crypto_full": { + "title": "Полный доступ к приложению", + "body": "Работает с теми же правами, что и сам webmail. Может читать всю вашу почту и сохранённые ключи и действовать от вашего имени. Разрешайте это только плагинам, которым полностью доверяете." + }, + "email_raw_send": { + "title": "Отправлять собственные письма", + "body": "Отправлять с вашего аккаунта готовые письма, например подписанные или зашифрованные." + }, + "email_blob_read": { + "title": "Читать исходные данные писем", + "body": "Читать исходные данные ваших писем и вложений, а также каждого файла, который вы прикрепляете, до отправки." + }, + "email_blob_write": { + "title": "Заменять вложения перед отправкой", + "body": "Загружать файлы на ваш почтовый сервер и заменять прикреплённый файл другим до отправки письма." + }, + "email_render_takeover": { + "title": "Менять отображение писем", + "body": "Заменять текст открытых писем и писем в списке сообщений, например чтобы показать расшифрованное содержимое." + }, + "calendar_read": { + "title": "Читать ваш календарь", + "body": "Видеть ваши календари и их события." + }, + "calendar_write": { + "title": "Менять ваш календарь", + "body": "Создавать, менять и удалять события." + }, + "contacts_read": { + "title": "Читать ваши контакты", + "body": "Искать в ваших адресных книгах и читать их." + }, + "contacts_write": { + "title": "Менять ваши контакты", + "body": "Создавать, менять и удалять контакты и адресные книги." + }, + "files_read": { + "title": "Читать ваши файлы", + "body": "Просматривать и открывать файлы, хранящиеся в вашем аккаунте." + }, + "files_write": { + "title": "Менять ваши файлы", + "body": "Загружать, переименовывать, перемещать и удалять файлы в вашем аккаунте." + }, + "identity_read": { + "title": "Видеть ваши адреса отправителя", + "body": "Видеть адреса, с которых вы отправляете письма, и их подписи." + }, + "identity_write": { + "title": "Менять ваши адреса отправителя", + "body": "Создавать, менять и удалять адреса отправителя и подписи." + }, + "filters_read": { + "title": "Читать ваши фильтры почты", + "body": "Видеть правила, которые сортируют входящую почту." + }, + "filters_write": { + "title": "Менять ваши фильтры почты", + "body": "Добавлять правила в фильтры на сервере. Такие правила могут перемещать, пересылать или удалять входящую почту." + }, + "tasks_read": { + "title": "Читать ваши задачи", + "body": "Видеть ваши списки задач и задачи." + }, + "tasks_write": { + "title": "Менять ваши задачи", + "body": "Создавать, менять, завершать и удалять задачи." + }, + "templates_read": { + "title": "Читать ваши шаблоны", + "body": "Видеть сохранённые шаблоны писем." + }, + "templates_write": { + "title": "Менять ваши шаблоны", + "body": "Создавать, менять и удалять шаблоны писем." + }, + "smime_read": { + "title": "Видеть ваш статус S/MIME", + "body": "Видеть, есть ли у вас ключи и сертификаты S/MIME, без доступа к самим ключам." + }, + "vacation_read": { + "title": "Видеть ваш автоответ", + "body": "Видеть, включён ли автоответ и что в нём написано." + }, + "vacation_write": { + "title": "Менять ваш автоответ", + "body": "Включать и выключать автоответ, менять его текст и даты." + }, + "settings_read": { + "title": "Читать ваши настройки", + "body": "Видеть ваши параметры и определения тегов." + }, + "settings_write": { + "title": "Менять ваши настройки", + "body": "Добавлять теги, менять их порядок и менять ваши параметры." + }, + "security_read": { + "title": "Видеть безопасность аккаунта", + "body": "Видеть, включены ли двухфакторная аутентификация и шифрование, без доступа к секретам." + }, + "auth_observe": { + "title": "Узнавать о вашем входе", + "body": "Получать уведомления, когда вы входите, переключаете аккаунт или ваш сеанс продлевается." + }, + "auth_emit": { + "title": "Выполнять выход", + "body": "Выполнять ваш выход из webmail." + }, + "account_read": { + "title": "Видеть ваши аккаунты", + "body": "Видеть добавленные вами аккаунты, их адреса и серверы." + }, + "http_post": { + "title": "Обращаться к серверу webmail от вашего имени", + "body": "Вызывать эти адреса сервера с вашими учётными данными:" + }, + "http_fetch": { + "title": "Подключаться к другим сайтам", + "body": "Отправлять запросы на эти сайты. Туда может быть отправлено всё, что плагин может прочитать:" + }, + "admin_config": { + "title": "Читать свои настройки от администратора", + "body": "Читать настройки, которые администратор сохранил для этого плагина." + }, + "ui_observe": { + "title": "Следить за тем, где вы в приложении", + "body": "Знать, какая страница открыта, и реагировать на ссылки, по которым вы нажимаете." + }, + "ui_toolbar": { + "title": "Добавлять кнопки на панели инструментов", + "body": "Показывать свои кнопки на панели инструментов писем, в открытом письме и на верхней панели." + }, + "ui_app_top_banner": { + "title": "Показывать баннер сверху", + "body": "Показывать уведомление в верхней части приложения." + }, + "ui_email_banner": { + "title": "Показывать уведомления в письмах", + "body": "Показывать свои уведомления и сводки над частями открытого письма или беседы, внутри них и между ними." + }, + "ui_email_footer": { + "title": "Добавлять содержимое под письмами", + "body": "Показывать своё содержимое под открытым письмом." + }, + "ui_contact_cryptokeys": { + "title": "Показывать ключи шифрования контактов", + "body": "Показывать ключи шифрования контакта и управлять ими." + }, + "ui_email_details": { + "title": "Добавлять панели к письму", + "body": "Показывать свою панель рядом с открытым письмом или под ним." + }, + "ui_download_file": { + "title": "Сохранять файлы на ваше устройство", + "body": "Предлагать для скачивания созданные им файлы." + }, + "ui_message_list_tabs": { + "title": "Добавлять вкладки во «Входящие»", + "body": "Распределять входящие письма по вкладкам, например «Обновления» или «Промоакции»." + }, + "ui_composer_toolbar": { + "title": "Добавлять элементы в редактор", + "body": "Показывать свои кнопки, пункты меню и разделы, пока вы пишете письмо." + }, + "ui_composer_sidebar": { + "title": "Добавлять панель в редактор", + "body": "Показывать свою панель рядом с письмом, которое вы пишете." + }, + "ui_sidebar_widget": { + "title": "Добавлять элементы на боковую панель", + "body": "Показывать свои блоки и разделы на боковой панели почты." + }, + "ui_settings_section": { + "title": "Добавлять страницу настроек", + "body": "Показывать свою страницу в настройках." + }, + "ui_context_menu": { + "title": "Добавлять пункты в меню письма", + "body": "Добавлять пункты в меню, которое открывается по правому щелчку на письме." + }, + "ui_navigation_rail": { + "title": "Добавлять кнопку на панель навигации", + "body": "Показывать свою кнопку на панели навигации." + }, + "ui_keyboard": { + "title": "Добавлять сочетания клавиш", + "body": "Реагировать на заданные им сочетания клавиш." + }, + "ui_calendar_action": { + "title": "Добавлять элементы в интерфейс календаря", + "body": "Показывать свои кнопки и разделы в событиях, а также короткие метки и точки на днях календаря." + }, + "ui_admin_page": { + "title": "Добавлять страницу администрирования", + "body": "Показывать свою страницу в настройках администратора." + }, + "app_lifecycle": { + "title": "Узнавать о запуске и остановке приложения", + "body": "Получать уведомления, когда приложение запускается, теряет связь или вы выходите." + }, + "compose_open": { + "title": "Открывать редактор письма", + "body": "Открывать новое письмо с уже заполненными получателями, текстом или вложениями. Отправлять ли его, по-прежнему решаете вы." + }, + "compose_read": { + "title": "Читать письмо, которое вы пишете", + "body": "Читать текст письма, пока вы его пишете, когда вы используете плагин в редакторе. Без подписи и цитируемого письма." + }, + "compose_write": { + "title": "Изменять письмо, которое вы пишете", + "body": "Заменять или добавлять текст в письме, пока вы его пишете, когда вы используете плагин в редакторе. Каждое изменение можно отменить, а отправлять ли письмо, решаете вы." + }, + "ui_notifications": { + "title": "Показывать уведомления", + "body": "Показывать уведомления на вашем устройстве или в приложении, если вы их не разрешили. В каждом указано название плагина." + }, + "ui_message_list": { + "title": "Добавлять элементы в список сообщений", + "body": "Показывать своё содержимое над списком сообщений, а также кнопки на письмах и на панели выбранных писем." + }, + "ui_contacts": { + "title": "Добавлять элементы в интерфейс контактов", + "body": "Показывать свои разделы в карточке контакта и кнопки в списке контактов." + }, + "ui_tasks": { + "title": "Добавлять элементы в интерфейс задач", + "body": "Показывать свои разделы в сведениях о задаче и кнопки в списке задач." + }, + "ui_files": { + "title": "Добавлять элементы в интерфейс файлов", + "body": "Показывать свои разделы в предпросмотре файла и пункты в меню файла." + }, + "ui_commands": { + "title": "Добавлять команды", + "body": "Добавлять свои пункты в палитру команд (Ctrl+K)." + }, + "ui_app_page": { + "title": "Добавлять страницы", + "body": "Добавлять в приложение свои страницы, каждую с кнопкой на панели навигации." + }, + "http_proxy": { + "title": "Использовать подключённые вами аккаунты", + "body": "Отправлять запросы через сервер Bulwark с аккаунтом или ключом, который настроили вы или ваш администратор. Плагин никогда не видит пароль, но может делать на этом сервере всё, что может этот аккаунт." + } + }, + "dialog": { + "from_plugin": "От плагина {name}", + "ok": "ОК", + "submit": "Отправить", + "confirm": "Подтвердить", + "cancel": "Отмена", + "close": "Закрыть", + "turn_off": "Отключить {name}", + "plugin_menu": "Параметры {name}", + "password_note": "Эта форма от плагина {name}, а не от веб-почты. Вводите пароль, только если доверяете этому плагину.", + "toast_auto_disabled": "{name} отключён, потому что постоянно выдавал ошибки." + }, + "connect": { + "title": "Подключить {label}", + "intro_password": "{name} хочет использовать ваш аккаунт {label}. Bulwark хранит пароль на своём сервере и отправляет его только на {host}. Плагин его никогда не видит.", + "intro_password_any": "{name} хочет использовать ваш аккаунт {label}. Bulwark хранит пароль на своём сервере и отправляет его только на сервер, который вы укажете здесь. Плагин его никогда не видит.", + "intro_signin": "{name} хочет войти в ваш аккаунт {label}. Укажите здесь сервер, затем войдите на его собственной странице. Bulwark хранит полученный доступ на своём сервере; плагин его никогда не видит.", + "server": "Адрес сервера", + "server_hint": "cloud.example.com", + "username": "Имя пользователя", + "secret": "Пароль или ключ", + "connect": "Подключить", + "continue": "Продолжить", + "cancel": "Отмена", + "saving": "Сохранение…", + "error_server": "Укажите адрес сервера, например https://cloud.example.com", + "error_save": "Не удалось сохранить: {error}" + } + }, + "global_search": { + "title": "Искать везде", + "placeholder": "Поиск по почте, контактам, календарю, файлам...", + "scope_label": "Искать в", + "account_label": "Аккаунт", + "scope_all": "Всё", + "scope_mail": "Почта", + "scope_contacts": "Контакты", + "scope_calendar": "Календарь", + "scope_files": "Файлы", + "all_accounts": "Все аккаунты", + "show_all": "Показать все результаты", + "no_results": "Ничего не найдено по запросу «{query}»", + "server_hint": "Целые слова ищутся на сервере; части слов — среди уже загруженного", + "searching": "Поиск...", + "account_error": "Не удалось выполнить поиск в {account}", + "recent_searches": "Недавние запросы", + "recurring": "Повторяющееся", + "no_subject": "(no subject)", + "empty_hint": "Ищите почту, контакты, события и файлы во всех аккаунтах. Попробуйте from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Списком по дате", + "grouped": "По группам", + "load_more": "Загрузить ещё", + "preview_empty": "Выберите результат для предпросмотра", + "open_full": "Открыть", + "view_label": "Вид" + }, + "top_bar": { + "more": "Другие действия" + }, + "plugin_pages": { + "loading": "Загрузка страницы", + "not_available_title": "Эта страница недоступна", + "not_available_description": "Плагин, который её добавил, выключен или больше не установлен.", + "back_to_mail": "Вернуться к почте", + "settings_group": "Плагины" + }, + "command_palette": { + "title": "Палитра команд", + "open": "Открыть палитру команд", + "placeholder": "Введите команду", + "no_results": "Нет подходящих команд", + "go_to": "Перейти: {name}", + "compose": "Новое письмо", + "search": "Искать в почте", + "theme_dark": "Включить тёмную тему", + "theme_light": "Включить светлую тему", + "shortcuts": "Показать сочетания клавиш", + "group_navigation": "Перейти", + "group_actions": "Действия", + "group_plugins": "Плагины" + }, + "notification_center": { + "title": "Уведомления", + "mark_all_read": "Отметить все как прочитанные", + "clear": "Очистить", + "empty": "Нет уведомлений", + "close": "Закрыть", + "unread": "Непрочитанное", + "unread_count": "Непрочитанных: {count}", + "level_error": "Ошибка", + "level_warning": "Предупреждение" + } +} diff --git a/locales/sk/common.json b/locales/sk/common.json index 152ccc83..0de128b4 100644 --- a/locales/sk/common.json +++ b/locales/sk/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Minimalistický webmailový klient využívajúci protokol JMAP", - "login": { - "title": "Webmail", - "username_label": "E-mail", - "username_placeholder": "pouzivatel@example.com", - "password_label": "Heslo", - "password_placeholder": "Zadajte heslo", - "jmap_endpoint_label": "JMAP server", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Server musí povoliť požiadavky CORS z tejto domény.", - "jmap_server_label": "Server", - "jmap_server_auto_picked": "Server bol vybratý podľa domény vášho e-mailu.", - "sign_in": "Prihlásiť sa", - "signing_in": "Prihlasovanie...", - "loading": "Načítavanie...", - "reconnecting": "Spojenie prerušené. Pokus o opätovné pripojenie…", - "error": { - "invalid_credentials": "Neplatný e-mail alebo heslo. Skontrolujte prihlasovacie údaje a skúste znova.", - "connection_failed": "Nedá sa pripojiť k serveru. Skontrolujte internetové pripojenie a skúste znova.", - "cors_blocked": "Server je dostupný, ale blokuje požiadavky medzi doménami. Skontrolujte nastavenia CORS servera JMAP a povoľte túto doménu.", - "server_error": "Server je dočasne nedostupný. Skúste to neskôr.", - "generic": "Vyskytla sa neočakávaná chyba. Ak problém pretrváva, kontaktujte správcu.", - "totp_required": "Je vyžadovaný kód dvojfaktorového overenia. Zadajte kód nižšie.", - "totp_invalid": "Neplatný overovací kód. Skontrolujte svoju overovaciu aplikáciu a skúste znova.", - "oauth_discovery_failed": "SSO je povolené, ale poskytovateľa identity sa nepodarilo dosiahnuť. Skontrolujte konfiguráciu OAuth." - }, - "show_password": "Zobraziť heslo", - "hide_password": "Skryť heslo", - "totp_toggle": "Mám kód 2FA", - "remember_me": "Zapamätať si ma", - "config_error": { - "title": "Chyba konfigurácie", - "fetch_failed": "Konfiguráciu aplikácie sa nepodarilo načítať. Skúste to neskôr.", - "server_not_configured": "Poštový server nebol nakonfigurovaný. Kontaktujte správcu." - }, - "remove_from_history": "Odstrániť z histórie", - "totp_label": "Overovací kód", - "totp_placeholder": "000000", - "session_expired": "Platnosť relácie vypršala. Prihláste sa znova.", - "dismiss": "Zavrieť", - "or": "alebo", - "sign_in_sso": "Prihlásiť sa cez SSO", - "add_account_title": "Pridať účet", - "add_account_subtitle": "Prihlásiť sa iným účtom", - "cancel": "Zrušiť", - "website": "Webová stránka", - "imprint": "Impressum", - "privacy_policy": "Zásady ochrany osobných údajov", - "try_demo": "Vyskúšať demo", - "demo_description": "Preskúmajte s ukážkovými dátami – bez potreby účtu", - "demo_launching": "Spúšťanie dema...", - "demo_login_button": "Spustiť demo", - "demo_tagline": "Vyskúšajte plnohodnotného e-mailového klienta. Účet nie je potrebný.", - "demo_no_signup": "Bez registrácie – voľne preskúmajte s ukážkovými dátami", - "oauth_completing": "Dokončovanie prihlásenia...", - "oauth_error": { - "title": "Overenie zlyhalo", - "invalid_state": "Bezpečnostné overenie zlyhalo. Skúste sa prihlásiť znova.", - "missing_params": "Chýbajú autorizačné dáta. Skúste sa prihlásiť znova.", - "token_exchange_failed": "Overenie sa nepodarilo dokončiť. Skúste to znova.", - "access_denied": "Prístup bol zamietnutý. Kontaktujte správcu.", - "back_to_login": "Späť na prihlásenie" - } - }, - "sidebar": { - "unified_mailbox": "Zjednotená schránka", - "close": "Zavrieť", - "compose": "Napísať", - "compose_hint": "Napísať (c)", - "search_placeholder": "Hľadať poštu...", - "search_placeholder_hint": "Hľadať poštu... (stlačte /)", - "storage": "Úložisko", - "storage_used": "Použité", - "storage_free": "Voľné", - "storage_total": "Celkom", - "sign_out": "Odhlásiť sa", - "sign_out_of": "Odhlásiť sa z {account}", - "sign_out_all": "Odhlásiť sa zo všetkých účtov", - "add_account": "Pridať účet", - "set_as_default": "Nastaviť ako predvolený", - "switch_account": "Prepnúť účet", - "contacts": "Kontakty", - "calendar": "Kalendár", - "settings": "Nastavenia", - "admin": "Administrácia", - "files": "Súbory", - "loading_mailboxes": "Načítavanie schránok...", - "push_connected": "Aktualizácie v reálnom čase sú aktívne", - "push_disconnected": "Aktualizácie v reálnom čase sú neaktívne", - "keyboard_shortcuts": "Klávesové skratky", - "theme": { - "light": "Svetlý režim", - "dark": "Tmavý režim", - "system": "Systémová téma" - }, - "language": { - "title": "Jazyk" - }, - "mailboxes": { - "inbox": "Doručené", - "sent": "Odoslané", - "drafts": "Koncepty", - "trash": "Koš", - "archive": "Archív", - "starred": "Označené hviezdičkou", - "all_mail": "Všetka pošta", - "spam": "Spam", - "important": "Dôležité" - }, - "unified_inbox": "Zjednotená doručená pošta", - "unified_sent": "Všetky odoslané", - "unified_drafts": "Všetky koncepty", - "unified_trash": "Všetky koše", - "unified_archive": "Všetky archívy", - "unified_junk": "Všetok spam", - "all_accounts": "Všetky účty", - "expand": "Rozbaliť", - "collapse": "Zbaliť", - "expand_tooltip": "Rozbaliť", - "collapse_tooltip": "Zbaliť", - "mobile": { - "search": "Hľadať", - "compose": "Napísať", - "go_back": "Späť", - "toggle_menu": "Prepnúť ponuku" - }, - "clear_search": "Vymazať hľadanie", - "vacation_active": "Automatická odpoveď je aktívna", - "demo_banner": "Demo režim", - "demo_reset": "Resetovať", - "demo_tour": "Sprievodca", - "tags": "Štítky", - "show_all_tags": "Zobraziť všetko ({count})", - "show_fewer_tags": "Zobraziť menej", - "folders": "Priečinky", - "shared": "Zdieľané", - "mail": "Pošta", - "nav_label": "Navigácia", - "add_app": "Aplikácie", - "scheduled": "Naplánované", - "unified_all_unread": "Všetky neprečítané", - "unified_all_starred": "Všetky s hviezdičkou", - "unified_all_mail": "Všetka pošta", - "remove_account": "Odstrániť účet", - "remove_account_confirm": "Odstrániť {account} z tohto zariadenia? Neskôr ho môžete znova pridať." - }, - "protocol_handlers": { - "title": "Predvolené aplikácie", - "description": "Vyberte, či sa e-mailové a kalendárové odkazy otvárajú v Bulwarku. Bulwark sa registruje ako obslužná aplikácia protokolu pre odkazy mailto: a webcal:.", - "unsupported": "Tento prehľadávač alebo pripojenie nepodporuje manuálnu registráciu obslužnej aplikácie protokolu.", - "mailto_label": "E-mailové odkazy", - "mailto_description": "Otvoriť odkazy mailto: v Bulwarku s predvyplneným editorom správy.", - "protocol_open_mode_label": "Pri otváraní odkazov protokolov", - "protocol_open_mode_description": "Vyberte, či má Bulwark otvárať odkazy mailto: a webcal: v novej karte alebo znovu použiť otvorenú reláciu.", - "protocol_open_mode_active_session": "Otvoriť v aktívnej relácii, ak je to možné", - "protocol_open_mode_new_tab": "Vždy otvoriť novú kartu", - "focus_notification_title": "Otvoriť Bulwark", - "focus_notification_body": "Odkaz bol otvorený v Bulwarku. Kliknutím prenesú okno do popredia.", - "webcal_label": "Kalendárové odkazy", - "webcal_description": "Otvoriť odkazy webcal: v Bulwarku s predvyplneným dialógom pre odber kalendára.", - "register_mailto": "Zaregistrovať e-mailovú aplikáciu", - "register_webcal": "Zaregistrovať kalendárovú aplikáciu", - "mailto_registered": "Registrácia obsluhy e-mailových odkazov bola požiadaná", - "webcal_registered": "Registrácia obsluhy kalendárových odkazov bola požiadaná", - "registration_failed": "Registrácia obslužnej aplikácie protokolu zlyhala", - "opening_mailto": "Otvára sa editor...", - "opening_webcal": "Otvára sa kalendár...", - "browser_note": "Prehľadávač alebo operáčný systém vás môže požiadať o potvrdenie.", - "select_account_title": "Vybrať účet", - "select_mailto_account": "Vyberte účet, v ktorom sa má tento e-mailový odkaz otvoriť.", - "select_webcal_account": "Vyberte účet, v ktorom sa má tento kalendárový odkaz otvoriť.", - "select_account_note": "Toto platí iba pre tento odkaz protokolu.", - "detail_to": "Komu", - "detail_subject": "Predmet", - "detail_no_subject": "Bez predmetu", - "detail_calendar": "Kalendár", - "detail_source": "Zdroj", - "active_account": "Aktívny", - "switching_account": "Prepínanie účtu..." - }, - "sidebar_apps": { - "modal_title": "Aplikácie postranného panela", - "add_new": "Pridať aplikáciu", - "edit_app": "Upraviť aplikáciu", - "name_label": "Názov", - "name_placeholder": "Moja aplikácia", - "name_required": "Názov je povinný", - "url_label": "URL", - "url_required": "URL je povinné", - "url_invalid": "Zadajte platnú adresu http alebo https", - "icon_label": "Ikona", - "icon_required": "Ikona je povinná", - "open_mode_label": "Režim otvárania", - "open_new_tab": "Nová karta", - "open_inline": "Vložené", - "cancel": "Zrušiť", - "add": "Pridať", - "update": "Aktualizovať", - "delete": "Odstrániť", - "delete_confirm_title": "Odstrániť aplikáciu", - "delete_confirm": "Naozaj chcete odstrániť \"{name}\"?", - "no_apps": "Zatiaľ neboli pridané žiadne aplikácie", - "no_apps_hint": "Pridajte vlastné aplikácie a odkazy do postranného panela", - "managed_title": "Poskytnuté správcom", - "managed_description": "Tieto aplikácie sú nastavené pre všetkých a nedajú sa zmeniť.", - "managed_badge": "Spravované", - "search_icons": "Hľadať ikony...", - "show_popular": "Obľúbené", - "show_all": "Všetky", - "no_icons_found": "Ikony sa nenašli", - "inline_badge": "Vložené", - "tab_badge": "Karta", - "show_on_mobile": "Zobraziť na mobile" - }, - "email_list": { - "no_emails": "Neboli nájdené žiadne správy", - "no_emails_description": "Táto schránka je prázdna", - "no_search_results": "Neboli nájdené žiadne výsledky", - "no_search_results_description": "Skúste zmeniť hľadanie alebo filtre", - "loading": "Načítavanie e-mailov...", - "unread": "neprečítané", - "to_me": "Pre mňa", - "to_recipients": "Pre {count} príjemcov", - "and_others": "a {count} ďalších", - "draft": "Koncept", - "starred": "S hviezdičkou", - "pinned": "Pripnuté", - "replied": "Zodpovedané", - "forwarded": "Preposlané", - "has_attachment": "Obsahuje prílohu", - "conversations_count": "{count} z {total} konverzácií", - "conversations_count_plus": "{count}+ konverzácií", - "conversations_count_simple": "{count} konverzácií", - "no_conversations": "Žiadne konverzácie", - "loading_more": "Načítavanie ďalších e-mailov...", - "no_more_emails": "Žiadne ďalšie e-maily na načítanie", - "batch_actions": { - "select": "Vybrať e-maily", - "select_all": "Vybrať všetko", - "selected_messages": "{count, plural, one {1 e-mail} other {# e-mailov}} vybraných", - "mark_read": "Označiť ako prečítané", - "mark_unread": "Označiť ako neprečítané", - "delete": "Odstrániť", - "delete_confirm_title": "Odstrániť e-maily", - "delete_confirm_message": "Naozaj chcete odstrániť {count, plural, one {1 e-mail} other {# e-mailov}}?", - "clear_selection": "Zrušiť výber" - }, - "permanent_delete": "Trvalo odstrániť", - "permanent_delete_confirm_title": "Trvalo odstrániť", - "permanent_delete_confirm_message": "Tento e-mail bude trvalo odstránený. Túto akciu nie je možné vrátiť.", - "permanent_delete_confirm_batch_message": "Týchto {count, plural, one {1 e-mail} other {# e-mailov}} bude trvalo odstránených. Túto akciu nie je možné vrátiť.", - "empty_folder": { - "button": "Vyprázdniť priečinku", - "confirm_title": "Vyprázdniť priečinku", - "confirm_message": "Všetky e-maily v tejto priečinke budú trvalo odstránené. Túto akciu nie je možné vrátiť.", - "confirm_button": "Vyprázdniť priečinku", - "junk_hint": "Môžete vyprázdniť priečinku Spam a trvalo odstrániť všetky správy.", - "trash_hint": "Môžete vyprázdniť Koš a trvalo odstrániť všetky správy." - }, - "no_scheduled_emails": "Žiadne naplánované e-maily", - "no_scheduled_emails_description": "Správy naplánované na neskôr sa zobrazia tu.", - "scheduled_count": "{count, plural, one {1 naplánovaný e-mail} other {# naplánovaných e-mailov}}", - "scheduled_actions_hint": "Naplánované správy môžete zrušiť, prenaplánovať alebo upraviť.", - "cancel_scheduled_send": "Zrušiť odoslanie", - "reschedule_send": "Prenaplánovať", - "cancel_and_edit": "Zrušiť a upraviť", - "cancel_and_compose_again": "Zrušiť a napísať znova", - "reschedule_prompt": "Zadajte nový dátum/čas, napr. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Odosielateľ požiadal o potvrdenie o prečítaní:", - "send": "Odoslať potvrdenie", - "ignore": "Ignorovať", - "sent": "Potvrdenie o prečítaní bolo odoslané.", - "send_failed": "Potvrdenie o prečítaní sa nepodarilo odoslať", - "mdn_subject": "Prečítané: {subject}", - "mdn_body": "Toto je potvrdenie o prečítaní správy, ktorú ste odoslali na adresu {recipient}.\n\nPoznámka: Toto potvrdenie iba potvrdzuje, že správa bola zobrazená na počítači príjemcu. Nezaručuje, že príjemca obsah prečítal alebo mu porozumel." - }, - "no_email_selected": "Nie je vybraný žiadny e-mail", - "no_email_description": "Vyberte e-mail zo zoznamu na jeho zobrazenie", - "no_conversation_selected": "Nie je vybraná žiadna konverzácia", - "no_conversation_description": "Vyberte konverzáciu zo zoznamu na jej zobrazenie", - "compose": "Napísať", - "compose_hint": "Napísať novú správu", - "no_subject": "(Bez predmetu)", - "no_body_content": "(Obsah správy nie je dostupný)", - "no_preview_available": "Náhľad nie je dostupný", - "show_quoted_text": "Zobraziť citovaný text", - "hide_quoted_text": "Skryť citovaný text", - "loading_email": "Načítavanie e-mailu...", - "loading": "Načítavanie...", - "reply": "Odpovedať", - "reply_all": "Odpovedať všetkým", - "forward": "Preposlať", - "delete": "Odstrániť", - "archive": "Archivovať", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Označiť hviezdičkou", - "unstar": "Odstrániť hviezdičku", - "mark_unread": "Označiť ako neprečítané", - "mark_read": "Označiť ako prečítané", - "unread": "Neprečítané", - "read": "Prečítané", - "spam_short": "Spam", - "not_spam_short": "Nie je spam", - "move": "Presunúť", - "print": "Tlačiť", - "view_source": "Zobraziť zdrojový kód", - "fullscreen": "Celá obrazovka", - "exit_fullscreen": "Ukončiť režim celej obrazovky", - "export_email": "Exportovať ako .eml", - "forward_as_attachment": "Preposlať ako prílohu", - "import_email": "Importovať .eml alebo .zip", - "keyboard_shortcuts": "Klávesové skratky (?)", - "email_source": "Zdrojový kód e-mailu", - "draft_banner": "Táto správa je koncept", - "edit_draft": "Upraviť", - "copy_source": "Kopírovať do schránky", - "source_copied": "Zdrojový kód bol skopírovaný do schránky", - "attachments": "Prílohy", - "important": "Dôležité", - "download": "Stiahnuť", - "download_all": "Stiahnuť všetko", - "from": "Od", - "to": "Komu", - "cc": "Kópia", - "bcc": "Skrytá kópia", - "date": "Dátum", - "subject": "Predmet", - "show_details": "Zobraziť podrobnosti", - "hide_details": "Skryť podrobnosti", - "external_content_warning": "Obrázky a externý obsah boli zablokované", - "load_external_content": "Načítať obrázky", - "trust_sender": "Vždy dôverovať tomuto odosielateľovi", - "back_to_list": "Späť na zoznam", - "view_contact": "Zobraziť kontakt", - "message_details": "Podrobnosti správy", - "more_reply_options": "Viac možností odpovede", - "set_tag": "Nastaviť štítok", - "tag": "Štítok", - "more_actions": "Viac akcií", - "previous": "Predchádzajúci", - "next": "Ďalší", - "move_to": "Presunúť do...", - "remove_tag": "Odstrániť štítok", - "tag_filter_placeholder": "Filtrovať štítky", - "tag_no_matches": "Žiadne zodpovedajúce štítky", - "more_count": "+{count} ďalších", - "characters_count": "{count} znakov", - "quick_reply_placeholder": "Napísať rýchlu odpoveď...", - "more_options": "Viac možností", - "sending": "Odosielanie...", - "security_authentication": "Zabezpečenie a overenie", - "technical_details": "Technické podrobnosti", - "message_id_label": "Message-ID:", - "reply_to_label": "Reply-To:", - "delivery_time_label": "Čas doručenia:", - "conversation_part_label": "Súčasť konverzácie:", - "previous_messages": "{count} predchádzajúca správa", - "previous_messages_plural": "{count} predchádzajúcich správ", - "time": { - "day": "deň", - "days": "dní", - "hour": "hodina", - "hours": "hodín", - "minute": "minúta", - "minutes": "minút" - }, - "unknown_sender": "Neznámy", - "recipient_me": "ja", - "recipient_and_others": "{name} a {count} ďalších", - "recipient_to_prefix": "Komu:", - "authentication": { - "title": "Overenie", - "status": { - "verified": "Overené", - "warning": "Upozornenie", - "none": "Neoverené" - }, - "spf": { - "pass": "SPF úspešné", - "fail": "SPF zlyhalo", - "none": "Bez SPF" - }, - "dkim": { - "pass": "DKIM platný", - "fail": "DKIM neplatný", - "none": "Bez DKIM" - }, - "dmarc": { - "pass": "DMARC úspešné", - "fail": "DMARC zlyhalo", - "none": "Bez DMARC" - }, - "spam_score": "Skóre spamu", - "tooltip_spf": "Sender Policy Framework: overuje, či je odosielajúci server oprávnený odosielať e-maily v mene domény", - "tooltip_dkim": "DomainKeys Identified Mail: kryptografickým podpisom potvrdzuje, že e-mail nebol počas prenosu zmenený", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zabezpečuje súlad SPF a DKIM s doménou odosielateľa", - "policy": "Politika", - "result": { - "pass": "Úspešné", - "fail": "Zlyhalo", - "softfail": "Mierne zlyhanie", - "neutral": "Neutrálne", - "permerror": "Trvalá chyba", - "temperror": "Dočasná chyba", - "none": "Žiadne" - } - }, - "details": { - "recipients_routing": "Príjemcovia a smerovanie", - "authentication_security": "Overenie a zabezpečenie", - "identifiers_threading": "Identifikátory a vlákna", - "mailing_list": "Mailing list", - "message_properties": "Vlastnosti správy", - "sent": "Odoslané", - "received": "Prijaté", - "delivery_time": "Čas doručenia", - "in_reply_to": "V odpovedi na", - "references": "Odkazy", - "thread_id": "ID vlákna", - "size": "Veľkosť", - "mime_type": "Typ MIME", - "attachments_summary": "{count} súbory · {size}", - "list_id": "ID zoznamu", - "list_help": "Nápoveda zoznamu", - "list_post": "Príspevok do zoznamu", - "list_unsubscribe": "Odhlásiť odber", - "iprev": "Reverzné DNS", - "spam_status": "Stav spamu", - "ai_verdict": "Verdikt AI", - "account": "Účet", - "no_subject": "(bez predmetu)" - }, - "headers": { - "routing": "Smerovanie", - "received": "Prijaté", - "message_id": "ID správy", - "list_info": "Informácie o zozname" - }, - "tooltips": { - "reply": "Odpovedať (r)", - "reply_all": "Odpovedať všetkým (a)", - "forward": "Preposlať (f)", - "archive": "Archivovať (e)", - "delete": "Odstrániť (# alebo Del)", - "star": "Hviezdička (s)", - "unstar": "Odstrániť hviezdičku (s)", - "compose": "Napísať (c)", - "previous": "Predchádzajúci e-mail", - "next": "Ďalší e-mail", - "edit_draft": "Upraviť koncept" - }, - "spam": { - "button_title": "Nahlaš spam", - "not_spam_title": "Označiť ako legitimívne", - "toast_success": "Presunuté do Spamu", - "toast_batch": "{count} e-mailov presunutých do Spamu", - "toast_undo": "Späť", - "toast_not_spam_success": "Presunuté do Doručených", - "toast_not_spam_batch": "{count} e-mailov presunutých do Doručených", - "error": "Nahlasovanie spamu zlyhalo", - "error_not_spam": "Obnovenie e-mailu zlyhalo" - }, - "unsubscribe_banner": { - "label": "Newsletter", - "button": "Odhlásiť odber", - "confirm_title": "Odhlásiť odber od tohto odosielateľa?", - "confirm_button": "Potvrdiť", - "cancel": "Zrušiť", - "success_http": "Stránka pre odhlásenie bola otvorená v novej karte", - "success_mailto": "E-mail na odhlásenie bol odoslaný", - "confirm_message_http": "Stránka odhlásenia sa otvorí na novej karte.", - "confirm_message_mailto": "Odosielateľovi bude odoslaný e-mail na odhlásenie.", - "error": "Odber sa nedá odhlásiť", - "dismiss": "Zavrieť" - }, - "calendar_invitation": { - "loading": "Načítavanie podrobností udalosti…", - "title": "Pozvánka do kalendára", - "published_title": "Zverejnená udalosť", - "response_title": "Odpoveď na udalosť", - "update_title": "Aktualizácia udalosti", - "counter_title": "Protinavárh", - "refresh_title": "Požiadavka na obnovenie", - "declined_counter_title": "Protinavárh zamietnutý", - "cancelled_title": "Udalosť zrušená", - "organizer": "Organizátor: {name}", - "organizer_label": "Organizátor:", - "attendees": "{count, plural, one {# účastník} other {# účastníkov}}", - "accept": "Prijаť", - "maybe": "Možno", - "decline": "Odmietnuť", - "add_to_calendar": "Pridať do kalendára", - "added": "Pridané do kalendára", - "rsvp_sent": "Odpoveď odoslaná", - "parse_error": "Nedá sa prečítať pozvánka", - "action_failed": "Akciu kalendára sa nepodarilo dokončiť.", - "no_calendar": "Kalendár nie je dostupný", - "published_info": "Táto udalosť bola zdielená pre vašu informáciu.", - "response_info": "Táto správa obsahuje odpoveď účastníka.", - "response_info_organizer": "Táto odpoveď účastníka aktualizuje vašu udalosť.", - "update_info": "Táto správa aktualizuje existujúcu udalosť.", - "counter_info": "Táto správa navrhuje zmeny udalosti.", - "counter_info_organizer": "Tento účastník navrhol zmeny vašej udalosti.", - "refresh_info": "Táto správa vyžaduje najnovšie podrobnosti o udalosti.", - "refresh_info_organizer": "Účastník požiadal o najnovšie podrobnosti o udalosti.", - "declined_counter_info": "Organizátor zamietol protinavárh.", - "authentication_failed_info": "Overenie správ pre túto pozvánku zlyhalo. Zaobchádzajte s akciami kalendára opatrne.", - "authentication_missing_info": "Táto pozvánka neobsahuje overené overenie správ. Potvrdte podrobnosti s organizátorom.", - "sender_mismatch_info": "Táto pozvánka bola odoslaná od {sender}, zatiaľčo organizátor v dátach kalendára je {organizer}.", - "sender_mismatch_unverified_info": "Táto pozvánka bola odoslaná od {sender}, zatiaľčo organizátor v dátach kalendára je {organizer}, a správu sa nepodarilo overiť.", - "organizer_role": "Ste organizátorom tejto udalosti", - "your_response": "Vaša odpoveď: {status}", - "response_needed": "Vyžaduje odpoveď", - "response_accepted": "Prijaté", - "response_tentative": "Nezaväzne", - "response_declined": "Odmietnuté", - "response_delegated": "Delegované", - "actor_sent_info": "Odoslané od {name}.", - "actor_response_info": "{name} odpovedal(a) {status}.", - "actor_counter_info": "{name} navrhol(a) zmeny tejto udalosti.", - "actor_refresh_info": "{name} požiadal(a) o najnovšie podrobnosti o udalosti.", - "actor_declined_counter_info": "{name} zamietol(a) protinavárh.", - "actor_note": "Poznámka: {comment}", - "actor_unknown": "Niekto", - "proposed_changes": "Navrhované zmeny", - "change_title": "Názov", - "change_time": "Čas", - "change_location": "Miesto", - "change_description": "Popis", - "change_empty": "Žiadne", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Použiť navrhované zmeny", - "proposal_applied": "Navrhované zmeny boli použité.", - "review_proposal": "Skontrolovať návrh", - "review_request": "Skontrolovať požiadavku", - "view_in_calendar": "Zobraziť v kalendári", - "select_calendar": "Vybrať kalendár", - "already_in_calendar": "Už je vo vašom kalendári", - "request_info": "Boli ste pozvaní na túto udalosť. Odpovedzte, aby organizátor vedel o vašej dostupnosti.", - "cancel_info": "Organizátor zrušil túto udalosť.", - "event_updated": "Aktualizácia #{sequence}", - "event_status_tentative": "Nezaväzne", - "event_status_cancelled": "Zrušené", - "expand": "Zobraziť podrobnosti", - "collapse": "Skryť podrobnosti" - }, - "send": "Odoslať", - "more": "viac", - "scheduled_banner": "Naplánované na odoslanie {date}", - "scheduled_send_created": "E-mail bol naplánovaný na odoslanie", - "cancel_scheduled_send": "Zrušiť odoslanie", - "reschedule_send": "Prenaplánovať", - "cancel_and_edit": "Zrušiť a upraviť", - "cancel_and_compose_again": "Zrušiť a napísať znova", - "reschedule_prompt": "Zadajte nový dátum/čas, napr. 2026-05-04T15:30", - "scheduled_actions_only": "Zobrazenie Naplánované podporuje iba akcie plánovania", - "undo_send_scheduled": "Správa je naplánovaná na odoslanie", - "undo_send": "Vrátiť odoslanie", - "contact_sidebar": { - "title": "Kontakt", - "close": "Zavrieť postranný panel", - "action_email": "E-mail", - "action_email_title": "Odoslať e-mail", - "action_copy": "Kopírovať", - "action_copy_title": "Kopírovať e-mail", - "action_edit_title": "Upraviť kontakt", - "section_emails": "E-maily", - "section_phones": "Telefóny", - "section_organizations": "Organizácie", - "section_addresses": "Adresy", - "section_notes": "Poznámky", - "not_in_contacts": "Nie je vo vašich kontaktoch", - "add_to_contacts": "Pridať do kontaktov", - "copied": "Skopírované!", - "copy_failed": "Kopírovanie zlyhalo" - }, - "send_now": "Odoslať teraz" - }, - "email_composer": { - "read_receipt_on": "Potvrdenie o prečítaní požiadané (kliknutím vypnete)", - "read_receipt_off": "Požiadať o potvrdenie o prečítaní", - "dsn_on": "Oznámenie o doručení vyžiadané (kliknutím vypnete)", - "dsn_off": "Vyžiadať oznámenie o doručení", - "format_plain_text": "Prepnúť na obyčajný text", - "format_rich_text": "Prepnúť na formátovaný text (HTML)", - "require_tls_on": "Šifrované doručenie vyžadované (kliknutím vypnete)", - "require_tls_off": "Vyžadovať šifrované doručenie (TLS)", - "new_message": "Nová správa", - "signature_edit_hint": "Dvojklikom upravíte podpis", - "reply": "Odpovedať", - "reply_all": "Odpovedať všetkým", - "forward": "Preposlať", - "reply_to": "Odpovedať", - "reply_all_to": "Odpovedať všetkým", - "forward_message": "Preposlať", - "from": "Od", - "to": "Komu", - "cc": "Kópia", - "bcc": "Skrytá kópia", - "subject": "Predmet", - "body_placeholder": "Napíšte správu...", - "send": "Odoslať", - "cancel": "Zrušiť", - "attach": "Priložiť", - "attach_photos": "Fotky a videá", - "attach_files": "Súbory", - "discard": "Zahoditť", - "discard_draft_title": "Zahoditť koncept?", - "discard_draft_confirm": "Máte neuložené zmeny. Chcete zahoditť tento koncept?", - "saving": "Ukladánie...", - "sending": "Odosielanie...", - "add_link": "Pridať odkaz", - "link_url_prompt": "Zadajte URL", - "draft_saved": "Koncept bol uložený", - "save_failed": "Uloženie zlyhalo", - "to_placeholder": "E-mailové adresy príjemcov", - "cc_placeholder": "Príjemcovia kópie", - "bcc_placeholder": "Príjemcovia skrytej kópie", - "subject_placeholder": "Predmet", - "cc_label": "Kópia:", - "bcc_label": "Skrytá kópia:", - "subject_label": "Predmet:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(Bez predmetu)", - "unknown_sender": "Neznámy", - "quote": { - "reply_header": "Dňa {date}, {sender} napísal(a):", - "forward_header": "---------- Preposlaná správa ----------", - "from": "Od: {sender}", - "date": "Dátum: {date}", - "subject": "Predmet: {subject}", - "to": "Komu: {recipients}" - }, - "remove_sub_address": "Odstrániť podadresu", - "from_override": { - "toggle_off": "Prepísať", - "toggle_on": "Zrušiť prepísanie", - "toggle_tooltip": "Upravte meno a adresu odosielateľa. Pošta sa stále odosiela cez vašu identitu.", - "name_label": "Meno odosielateľa", - "name_placeholder": "Meno", - "email_label": "E-mailová adresa odosielateľa", - "email_placeholder": "alias@example.com", - "envelope_notice": "Ak váš server túto adresu neprijme ako odosielateľa obálky, použije sa namiesto nej {identity} a príjemcovia ju uvidia v hlavičke Return-Path." - }, - "use_template": "Šablóna", - "save_as_template": "Uložiť ako šablónu", - "validation": { - "recipient_required": "Pridajte príjemcu na odoslanie", - "body_required": "Napíšte správu alebo priložte súbor", - "attachments_uploading": "Prílohy sa stále nahrávajú - odošle sa hneď po ich dokončení", - "attachment_upload_failed": "Neodoslané - prílohu sa nepodarilo nahrať. Odstráňte ju a skúste to znova." - }, - "upload_progress": "Nahrávanie {uploaded} / {total}", - "upload_cancel": "Zrušiť nahrávanie", - "upload_failed": "Súbor {filename} sa nepodarilo nahrať", - "attachment_too_large": "„{name}“ je väčší, než server povoľuje ({max} na súbor)", - "attachments_total_too_large": "Prílohy by prekročili limit servera {max} na správu", - "drop_files": "Presuňte súbory sem na priloženie", - "show_less": "Zobraziť menej", - "send_failed": "Odoslanie e-mailu zlyhalo", - "send_timeout": "Server prestal odpovedať. Pred opätovným odoslaním skontrolujte priečinok Odoslané — správa už mohla odísť.", - "continue_draft": "Pokračovať v koncepte", - "close_draft_title": "Uložiť alebo zahoditť koncept?", - "close_draft_message": "Máte neuložené zmeny. Chcete ich uložiť ako koncept alebo zahoditť?", - "smime_sign_on": "Podpisovanie S/MIME zapnuté", - "smime_sign_off": "Zapnúť podpisovanie S/MIME", - "smime_encrypt_on": "Šifrovanie S/MIME zapnuté", - "smime_encrypt_off": "Zapnúť šifrovanie S/MIME", - "smime_encrypt_unavailable": "Šifrovanie S/MIME nie je dostupné – chýbajú certifikáty príjemcov", - "smime_unlock_title": "Odomknúť kľúč S/MIME", - "smime_unlock_message": "Zadajte heslo na odomknutie kľúča podpisovania S/MIME.", - "smime_unlock_button": "Odomknúť", - "smime_passphrase_placeholder": "Heslo", - "forgot_attachment": { - "title": "Nezabudli ste na prílohu?", - "message": "Vaša správa spíaňuje \"{keyword}\", ale nie je priložený žiadny súbor. Odoslať aj tak?", - "send_anyway": "Odoslať aj tak", - "back": "Späť na úpravu" - }, - "empty_subject": { - "title": "Odoslať bez predmetu?", - "message": "Táto správa nemá predmet. Odoslať aj tak?", - "dont_ask_again": "Znova sa nepýtať", - "send_anyway": "Odoslať aj tak", - "back": "Späť na úpravu" - }, - "schedule_send": "Naplánovať odoslanie", - "schedule_send_description": "Vyberte, kedy má server túto správu odoslať.", - "schedule_send_required": "Vyberte dátum a čas.", - "schedule_send_invalid": "Zadajte platný dátum a čas.", - "schedule_send_future": "Vyberte budúci dátum a čas.", - "schedule_send_too_late": "Tento čas je neskôr, ako server povoľuje.", - "schedule_send_unsupported": "Naplánované odoslanie nie je pre tento účet podporované.", - "schedule_send_cleanup_warning": "Naplánované odoslanie bolo vytvorené, ale vyčistenie konceptu zlyhalo.", - "send_delay_unsupported": "Oneskorenie odoslania nie je pre tento účet podporované.", - "send_delay_unsupported_confirm": "Tento účet nepodporuje oneskorenie odoslania. Odoslať okamžite?", - "recipient_edit_email": "Upraviť e-mailovú adresu", - "recipient_edit_name": "Upraviť zobrazené meno", - "recipient_email_placeholder": "E-mailová adresa", - "recipient_name_placeholder": "Zobrazené meno", - "autocomplete_search_server": "Hľadať na serveri", - "autocomplete_searching": "Hľadanie...", - "toolbar": { - "bold": "Tučné", - "italic": "Kurzíva", - "underline": "Podčiarknuté", - "strikethrough": "Prečiarknuté", - "text_color": "Farba textu", - "remove_color": "Odstrániť farbu", - "heading_1": "Nadpis 1", - "heading_2": "Nadpis 2", - "bullet_list": "Odrážkový zoznam", - "ordered_list": "Číslovaný zoznam", - "quote": "Citát", - "code_block": "Blok kódu", - "align_left": "Zarovnať doľava", - "align_center": "Na stred", - "align_right": "Zarovnať doprava", - "text_direction": "Smer textu (RTL/LTR)", - "link": "Odkaz", - "table": "Tabuľka", - "clear_formatting": "Vymazať formátovanie", - "undo": "Späť", - "redo": "Znova", - "add_row_above": "Pridať riadok nad", - "add_row_below": "Pridať riadok pod", - "add_column_before": "Pridať stĺpec pred", - "add_column_after": "Pridať stĺpec za", - "delete_row": "Odstrániť riadok", - "delete_column": "Odstrániť stĺpec", - "toggle_header_row": "Prepnúť riadok záhlavia", - "delete_table": "Odstrániť tabuľku", - "pick_size": "Vybrať veľkosť", - "font_size": "Veľkosť písma", - "font_size_default": "Predvolené", - "background_color": "Farba pozadia", - "remove_background_color": "Odstrániť farbu pozadia" - }, - "send_filing_warning": "Odoslané - ale následné upratovanie zlyhalo, môže zostať zastaraný koncept." - }, - "confirm_dialog": { - "confirm": "Potvrdiť", - "cancel": "Zrušiť" - }, - "common": { - "loading": "Načítavanie...", - "error": "Chyba", - "success": "Úspešne", - "cancel": "Zrušiť", - "save": "Uložiť", - "delete": "Odstrániť", - "edit": "Upraviť", - "close": "Zavrieť", - "search": "Hľadať", - "refresh": "Obnoviť", - "settings": "Nastavenia", - "help": "Nápoveda", - "logout": "Odhlásiť sa", - "yes": "Áno", - "no": "Nie", - "unknown": "Neznámy", - "app_title": "Webmail", - "reconnecting": "Spojenie prerušené. Pokus o opätovné pripojenie…", - "rate_limited_title": "Overenie servera je dočasne obmedzené.", - "rate_limited_detail": "Bulwark pozastavil požiadavky na pozadí, aby zabránil zablokovaniu. Opätovný pokus za {seconds} s.", - "rate_limited_action_title": "Požiadavka pozastavená, aby sa zabránilo zablokovaniu.", - "rate_limited_action_detail": "Bulwark čaká na ukončenie doby blokovania servera. Skúste to znova za {seconds} s.", - "pull_to_refresh": "Potiahnutím obnovíte", - "release_to_refresh": "Uvoľnením obnovíte", - "refreshing": "Obnovuje sa…" - }, - "notifications": { - "email_sent": "E-mail bol úspešne odoslaný", - "email_deleted": "E-mail bol odstránený", - "email_archived": "E-mail bol archivovaný", - "emails_deleted": "{count, plural, one {# e-mail odstránený} few {# e-maily odstránené} other {# e-mailov odstránených}}", - "emails_archived": "{count, plural, one {# e-mail archivovaný} few {# e-maily archivované} other {# e-mailov archivovaných}}", - "error_archiving": "E-mail sa nepodarilo archivovať", - "error_updating": "E-mail sa nepodarilo aktualizovať", - "email_starred": "E-mail bol označený hviezdičkou", - "email_unstarred": "Hviezdička bola odstránená", - "email_marked_read": "E-mail bol označený ako prečítaný", - "email_marked_unread": "E-mail bol označený ako neprečítaný", - "copied_to_clipboard": "Skopírované do schránky", - "source_copied": "Zdrojový kód skopírovaný do schránky", - "error_sending": "Odoslanie e-mailu zlyhalo", - "error_deleting": "Odstránenie e-mailu zlyhalo", - "error_loading": "Načítanie e-mailov zlyhalo", - "new_email": "Nový e-mail", - "new_email_from": "Od {sender}", - "click_to_view": "Kliknutím zobrazíte", - "email_moved": "E-mail bol presunutý", - "emails_moved": "{count} e-mailov bolo presunutých", - "moved_to_mailbox": "Presunuté do {mailbox}", - "move_failed": "Presun zlyhal", - "move_error": "E-maily sa nedá presunúť do vybratej priečinky", - "email_tagged": "E-mail bol označený štítkom", - "emails_tagged": "{count} e-mailov bolo označených štítkom", - "tag_failed": "Označovanie štítkom zlyhalo", - "identity_created": "Identita bola úspešne vytvorená", - "identity_updated": "Identita bola úspešne aktualizovaná", - "identity_deleted": "Identita bola odstránená", - "identity_set_primary": "Hlavná identita bola aktualizovaná", - "identity_create_failed": "Vytvorenie identity zlyhalo: {error}", - "identity_update_failed": "Aktualizácia identity zlyhala: {error}", - "identity_delete_failed": "Odstránenie identity zlyhalo: {error}", - "identity_unauthorized": "Nemáte oprávnenie odosielať z tejto e-mailovej adresy", - "identity_not_found": "Identita sa nenašla", - "vacation_saved": "Nastavenia automatickej odpovede boli uložené", - "vacation_save_failed": "Uloženie nastavenia automatickej odpovede zlyhalo", - "filters_saved": "Filtre boli úspešne uložené", - "filters_save_failed": "Uloženie filtrov zlyhalo", - "filters_deleted": "Pravidlo filtra bolo odstránené", - "templates_exported": "Šablóny boli úspešne exportované", - "templates_imported": "{count, plural, one {# šablóna} other {# šablón}} importovaných", - "templates_import_errors": "Niektoré šablóny sa nepodarilo importovať", - "templates_import_empty": "V súbore neboli nájdené žiadne šablóny", - "export_email_error": "Export e-mailu zlyhal", - "import_email_success": "E-mail bol úspešne importovaný", - "import_email_error": "Import e-mailu zlyhal" - }, - "date": { - "today": "Dnes", - "yesterday": "Včera", - "this_week": "Tento týždeň", - "last_week": "Minulý týždeň", - "this_month": "Tento mesiac", - "older": "Staršie", - "just_now": "Práve teraz", - "minutes_ago": "pred {count} minútou", - "minutes_ago_plural": "pred {count} minútami", - "hours_ago": "pred {count} hodinou", - "hours_ago_plural": "pred {count} hodinami", - "days_ago": "pred {count} dňom", - "days_ago_plural": "pred {count} dňami" - }, - "language": { - "title": "Jazyk", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Vybrať jazyk", - "switch_to_english": "Prepnúť na angličtinu", - "switch_to_french": "Prepnúť na frančuzštinu", - "switch_to_japanese": "Prepnúť na japončinu", - "switch_to_spanish": "Prepnúť na španielčinu", - "switch_to_italian": "Prepnúť na taliancinu", - "switch_to_german": "Prepnúť na nemčinu", - "switch_to_dutch": "Prepnúť na holštinu", - "switch_to_portuguese": "Prepnúť na portuguálčinu", - "switch_to_russian": "Prepnúť na ruštinu", - "switching": "Zmena jazyka...", - "switch": "Zmeniť jazyk", - "current": "Aktuálny jazyk", - "polish": "Polski", - "switch_to_polish": "Prepnúť na polštinu", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Prepnúť na perštinu", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Prepnúť na mongolčinu" - }, - "settings": { - "title": "Nastavenia", - "back_to_mail": "Späť na poštu", - "save_success": "Nastavenia boli úspešne uložené", - "import_success": "Nastavenia boli úspešne importované", - "import_error": "Import nastavení zlyhal", - "reset_confirm": "Naozaj chcete obnoviť všetky nastavenia na predvolené?", - "unsaved_changes": "Máte neuložené zmeny", - "discard_changes": "Zahoditť neuložené zmeny?", - "discard": "Zahoditť", - "keep_editing": "Pokračovať v úprave", - "search_placeholder": "Hľadať nastavenia", - "search_clear": "Vymazať hľadanie", - "search_no_results": "Žiadne zodpovedajúce nastavenia", - "tabs": { - "appearance": "Vzhľad", - "language": "Jazyk, región a čas", - "email": "Správanie e-mailov", - "composer": "Skladanie správ", - "privacy": "Súkromie a zabezpečenie", - "account": "Účet", - "identities": "Identity", - "vacation": "Automatická odpoveď", - "advanced": "Pokročilé", - "calendar": "Kalendár", - "filters": "Filtre", - "templates": "Šablóny", - "folders": "Priečinky", - "keywords": "Štítky", - "security": "Zabezpečenie", - "files": "Súbory", - "contacts": "Kontakty", - "encryption": "Šifrovanie", - "protocol_handlers": "Predvolené aplikácie", - "sidebar_apps": "Aplikácie postranného panela", - "notifications": "Oznámenia", - "layout": "Rozloženie", - "reading": "Čítanie", - "composing": "Pisanie", - "downloads": "Stiahnuté", - "content_senders": "Obsah a odosielatelia", - "about_data": "Info a dáta", - "debug": "Ladenie" - }, - "tab_groups": { - "general": "Všeobecné", - "account": "Účet a identita", - "organization": "Organizácia pošty", - "apps": "Aplikácie", - "system": "Systém", - "appearance": "Vzhľad", - "mail": "Pošta", - "privacy": "Súkromie a zabezpečenie", - "advanced": "Pokročilé" - }, - "appearance": { - "message_list_order": { - "label": "Poradie zoznamu správ", - "description": "Ako sú správy v zozname zoradené. Zoradenie vykonáva server, takže platí pre celý priečinok, nie len pre načítané správy.", - "preset": { - "chronological": "Chronologicky (najnovšie prvé)", - "unread_first": "Neprečítané prvé", - "starred_first": "S hviezdičkou prvé", - "tagged_first": "So štítkom prvé", - "custom": "Vlastné" - }, - "tag_label": "Štítok", - "advanced_show": "Rozšírené: upraviť úrovne zoradenia", - "advanced_hide": "Skryť rozšírené", - "criterion_label": "Kritérium úrovne {n}", - "direction_label": "Smer úrovne {n}", - "criteria": { - "unread": "Neprečítané", - "starred": "S hviezdičkou", - "tag": "Štítok", - "receivedAt": "Dátum prijatia", - "sentAt": "Dátum odoslania", - "from": "Odosielateľ", - "subject": "Predmet", - "size": "Veľkosť" - }, - "direction": { - "unread_first": "Neprečítané prvé", - "read_first": "Prečítané prvé", - "starred_first": "S hviezdičkou prvé", - "unstarred_first": "Bez hviezdičky prvé", - "tagged_first": "So štítkom prvé", - "untagged_first": "Bez štítku prvé", - "newest_first": "Najnovšie prvé", - "oldest_first": "Najstaršie prvé", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "Najväčšie prvé", - "smallest_first": "Najmenšie prvé" - }, - "add_level": "Pridať úroveň", - "remove_level": "Odstrániť úroveň", - "move_up": "Posunúť nahor", - "move_down": "Posunúť nadol", - "tiebreak_note": "Až tri úrovne; ďalšie úrovne rozhodujú pri zhode. „Najnovšie prvé“ sa vždy uplatní ako posledné.", - "scope": { - "label": "Použiť na všetky priečinky", - "description": "Ak je vypnuté, poradie platí len pre Doručenú poštu; ostatné priečinky zostávajú chronologické." - }, - "unsupported_note": "Váš poštový server nepodporuje zoradenie podľa stavu prečítania, hviezdičiek ani štítkov. Tieto úrovne sa ignorujú a zoznam zostáva chronologický." - }, - "title": "Vzhľad", - "description": "Prispôsobte si vzhľad a správanie webmailu", - "theme": { - "label": "Téma", - "description": "Vyberte preferovanú farebnú schému", - "light": "Svetlá", - "dark": "Tmavá", - "system": "Systémová" - }, - "language": { - "label": "Jazyk", - "description": "Vyberte preferovaný jazyk" - }, - "font_size": { - "label": "Veľkosť písma", - "description": "Upravte veľkosť textu pre lepšiu čitateľnosť", - "small": "Malé", - "medium": "Stredné", - "large": "Veľké" - }, - "list_density": { - "label": "Hustota zobrazenia", - "description": "Ovládanie medzier v celom rozhraní", - "extra_compact": "Veľmi kompaktné", - "compact": "Kompaktné", - "regular": "Bežné", - "comfortable": "Pohodlné" - }, - "animations": { - "label": "Povoliť animácie", - "description": "Zobrazovať plynulé prechody a efekty" - }, - "toolbar_position": { - "label": "Pozícia panela nástrojov", - "description": "Kde zobraziť tlačidlá akcií správy (Odpovedať, Archivovať, Odstrániť atd.)", - "top": "Hore", - "below_subject": "Pod predmetom" - }, - "toolbar_labels": { - "label": "Zobraziť popisky panela nástrojov", - "description": "Zobrazovať textové popisky vedľa ikon na paneli nástrojov." - }, - "hide_account_switcher": { - "label": "Skryť prepínač účtov v postrannom paneli", - "description": "Skryje výber účtu v hornej časti postranného panela." - }, - "show_rail_account_list": { - "label": "Zobraziť avatáry účtov na navigačnej lište", - "description": "Zobrazovať jednotlivé ikony účtov v dolnej časti navigačnej lišty pre rýchle prepínanie." - }, - "unified_mailbox": { - "label": "Zjednotená schránka", - "description": "Zobrazovať zlúčené priečinky (Doručené, Odoslané atd.) zo všetkých pripojených účtov", - "cross_account": { - "label": "Naprieč všetkými účtami", - "description": "Zlúčiť zjednotenú schránku naprieč všetkými pripojenými účtami namiesto zotrvania v aktívnom účte." - }, - "include_group": { - "label": "Zahrnúť skupinové schránky", - "description": "Zahrnúť do zjednoteného zobrazenia aj zdieľané/skupinové schránky." - } - }, - "all_mail": { - "folders_label": "Priečinky vo Všetkej pošte", - "folders_description": "Vyberte, ktoré priečinky sa spoja do zobrazenia Všetka pošta.", - "account_hint": "Platí pre {account}.", - "no_folders": "Žiadne priečinky nie sú dostupné." - }, - "colorful_sidebar_icons": { - "label": "Farebné ikony postranného panela", - "description": "Ofarbí ikony priečinkov a štítkov podľa typu. Vypnite pre jednofarebný postranný panel." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Zobraziť celkový počet správ", - "description": "Zobraziť celkový počet správ vedľa priečinkov a štítkov spolu s počtom neprečítaných." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro rozhranie (experimentálne)", - "description": "Rozloženie pre pokročilých používateľov iba pre stolné počítače.", - "open_label": "Otvoriť Pro rozhranie", - "back_to_standard": "Späť na štandardné" - }, - "cross_unread": { - "label": "Všetky účty: Neprečítané", - "description": "Zobraziť položku v sekcii Všetky účty so zoznamom neprečítanej pošty zo všetkých účtov." - }, - "cross_starred": { - "label": "Všetky účty: S hviezdičkou", - "description": "Zobraziť položku v sekcii Všetky účty so zoznamom pošty s hviezdičkou zo všetkých účtov." - }, - "cross_all": { - "label": "Všetky účty: Všetka pošta", - "description": "Zobraziť položku v sekcii Všetky účty so všetkou poštou zo všetkých účtov." - } - }, - "keywords": { - "title": "E-mailové štítky", - "description": "Definujte štítky na organizáciu e-mailov s farbami.", - "add_keyword": "Pridať štítok", - "label_field": "Zobrazovaný názov", - "label_placeholder": "napr. Práca, Osobné, Naliehavé", - "id_field": "ID štítku", - "id_placeholder": "napr. praca, osobne", - "color_field": "Farba", - "id_exists": "Toto ID štítku už existuje", - "edit": "Upraviť štítok", - "delete": "Odstrániť štítok", - "save": "Uložiť", - "add": "Pridať", - "cancel": "Zrušiť", - "migrating": "Aktualizácia štítku v existujúcich e-mailoch…", - "migration_error": "Nepodarilo sa aktualizovať štítok v existujúcich e-mailoch", - "nesting": { - "label": "Vnorené štítky", - "description": "Vnorujte štítky pod iné štítky a zobrazujte ich v bočnom paneli ako strom." - }, - "parent_field": "Nadradený štítok", - "no_parent": "Bez nadradeného štítku", - "too_long": "Táto cesta štítku je príliš dlhá (najviac {max} znakov)", - "has_children_locked": "Pod týmto štítkom sú vnorené ďalšie štítky, preto sú jeho názov a nadradený štítok uzamknuté. Najprv ich presuňte alebo odstráňte.", - "has_children_delete": "Najprv odstráňte štítky vnorené pod týmto", - "visibility_field": "Viditeľnosť v bočnom paneli", - "visibility": { - "show": "Zobraziť", - "unread": "Zobraziť pri neprečítaných", - "hide": "Skryť" - }, - "unrecognized": { - "title": "Obnoviť štítky zo servera", - "description": "Názvy a farby štítkov sú uložené v tomto prehliadači, ale samotné štítky sú na vašich správach ako kľúčové slová JMAP. Prehľadajte poštu na kľúčové slová, ku ktorým tu nie je štítok, a pridajte ich späť.", - "scan": "Vyhľadať štítky", - "rescan": "Hľadať znova", - "scanning": "Prehľadávanie… {scanned} z {total} správ", - "none": "Všetky štítky na vašich správach sú tu už definované.", - "messages": "{count, plural, one {# správa} few {# správy} other {# správ}}", - "add_all": "Pridať všetkých {count}", - "partial": "Prehľadaných bolo iba {scanned} najnovších správ z {total}. Štítky použité len na staršej pošte môžu chýbať.", - "error": "Poštu sa nepodarilo prehľadať na štítky." - } - }, - "notifications": { - "test_sound": "Otestovať zvuk oznámenia", - "sounds": { - "default": "Predvolený (Pípnutie)", - "cheerful": "Veselý", - "involved": "Dynamický", - "swift": "Rýchly", - "relax": "Relaxačný" - }, - "sound_selection": { - "title": "Zvuk oznámenia", - "description": "Vyberte zvuk, ktorý sa prehrá pri oznámení", - "choose": "Zvuk", - "choose_desc": "Vyberte tón oznámenia a kliknutím na ikonu reproduktora si ho vypočujte" - }, - "email": { - "title": "E-mailové oznámenia", - "description": "Nakonfigurujte oznámenia pre prichádzajúce e-maily", - "enabled": "E-mailové oznámenia", - "enabled_desc": "Zobrazovať oznámenia pri príchode nových e-mailov", - "sound": "Zvuk oznámenia", - "sound_desc": "Prehrať zvukové upozornenie pri príchode nových e-mailov", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Oznámenia kalendára", - "description": "Nakonfigurujte oznámenia pre udalosti kalendára", - "enabled": "Oznámenia o udalostiach", - "enabled_desc": "Zobrazovať upozornenia na nadchádzajúce udalosti v kalendári", - "sound": "Zvuk oznámenia", - "sound_desc": "Prehrať zvukové upozornenie pre pripomenutia kalendára", - "invitation_parsing": "Rozpoznávať e-mailové pozvánky", - "invitation_parsing_desc": "Rozpoznávať pozvánky kalendára v prílohách e-mailov a zobrazovať akcie kalendára" - }, - "push": { - "title": "Oznámenia na pozadí", - "description": "Prijímať systémové oznámenia o novej pošte, keď je táto stránka zatvorená. Doručované cez push relay Bulwark.", - "relay_label": "Push relay", - "relay_desc": "Predvolený je hostovaný relay Bulwark. Zmeňte iba ak používate vlastný hosting.", - "relay_locked": "Nastavené administrátorom", - "relay_locked_desc": "URL push relay bola nastavená administrátorom a nedá sa zmeniť.", - "status_active": "Aktívne na tomto zariadení", - "status_inactive": "Nie je povolené na tomto zariadení", - "status_unsupported": "Tento prehliadač nepodporuje Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "Pracujem…", - "enable": "Povoliť", - "reenable": "Znovu zaregistrovať", - "disable": "Zakázať", - "confirm_disable_title": "Zakázať oznámenia na pozadí?", - "confirm_disable_message": "Toto zariadenie prestane prijímať upozornenia, keď je stránka zatvorená.", - "dismiss_aria": "Zavrieť výzvu na oznámenia", - "ios_hint": "Na iOS najprv nainštalujte stránku na domovskú obrazovku – Safari doručuje Web Push iba nainštalovaným PWA.", - "devices_title": "Registrované zariadenia", - "devices_desc": "Všetky zariadenia registrované na príjem oznámení na pozadí pre tento účet. Odvolanie okamžite zastaví ich príjem.", - "devices_loading": "Načítavajú sa registrované zariadenia…", - "devices_empty": "Pre oznámenia na pozadí nie je registrované žiadne zariadenie.", - "devices_error": "Registrované zariadenia sa nepodarilo načítať.", - "devices_refresh": "Obnoviť", - "device_this": "Toto zariadenie", - "device_other": "Iné zariadenie", - "device_status_active": "Doručuje oznámenia", - "device_status_inactive": "Nikdy nedoručilo oznámenie", - "device_status_unknown": "Tomuto relay nie je známe", - "device_expires": "Vyprší {date}", - "revoke": "Odvolať", - "confirm_revoke_title": "Odvolať toto zariadenie?", - "confirm_revoke_message": "Okamžite prestane prijímať oznámenia na pozadí. Opätovné zapnutie push na tom zariadení ho zaregistruje nanovo.", - "confirm_revoke_message_this": "Toto zariadenie okamžite prestane prijímať oznámenia na pozadí." - } - }, - "language_region": { - "title": "Jazyk, región a čas", - "description": "Jazyk, formát dátumu, formát času a ďalšie regionálne predvoľby", - "language": { - "label": "Jazyk", - "description": "Vyberte preferovaný jazyk", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Formát dátumu", - "description": "Ako sa zobrazujú dátumy v zozname e-mailov", - "smart": "Inteligentný (regionálny)", - "relative": "Relatívny (pred 1 h, pred 2 d)", - "full": "Vždy úplný dátum", - "preview_today": "Dnes:", - "preview_this_week": "Tento týždeň:", - "preview_older": "Staršie:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Formát času", - "description": "Vyberte 12-hodinový alebo 24-hodinový formát času", - "12h": "12-hodinový", - "24h": "24-hodinový" - }, - "first_day": { - "label": "Prvý deň v týždni", - "description": "Začiatok týždňa v nedeľu alebo v pondelok", - "sunday": "Nedeľa", - "monday": "Pondelok" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Vymazať hľadanie pri prepnutí priečinka", - "description": "Kliknutie na priečinok (Doručená pošta, Odoslaná…) vymaže aktívne hľadanie a zobrazí priečinok. Keď je vypnuté, hľadanie zostáva a spustí sa znova vo zvolenom priečinku." - }, - "title": "Správanie e-mailov", - "description": "Nakonfigurujte spôsob spracovania e-mailov", - "request_read_receipt": { - "label": "Predvolene žiadať potvrdenie o prečítaní", - "description": "Pred-povoliť žiadosť o potvrdenie o prečítaní pri písaní novej správy." - }, - "read_receipt_response": { - "label": "Reagovať na žiadosti o potvrdenie o prečítaní", - "description": "Čo robiť, keď prichádzajúca správa žiada o potvrdenie o prečítaní.", - "ask": "Vždy sa opýtať", - "always": "Vždy odoslať", - "never": "Nikdy neodosielať" - }, - "mark_read": { - "label": "Označiť ako prečítané", - "description": "Kedy označiť e-mail ako prečítaný po jeho otvorení", - "instant": "Okamžite", - "delay_3s": "Po 3 sekundách", - "delay_5s": "Po 5 sekundách", - "never": "Nikdy" - }, - "delete_action": { - "label": "Akcia odstránenia", - "description": "Čo sa stane po odstránení e-mailu", - "trash": "Presunúť do Koša", - "trash_and_read": "Presunúť do Koša a označiť ako prečítané", - "permanent": "Trvalo odstrániť", - "warning": "E-maily budú trvalo odstránené a nedajú sa obnoviť." - }, - "message_spacing": { - "label": "Odsadenie správy", - "description": "Odsadenie okolo tela správy pri čítaní", - "auto": "Automaticky (odporúčané)", - "always": "Vždy pridať odsadenie", - "edge": "Od okraja po okraj" - }, - "plain_text_font": { - "label": "Písmo obyčajného textu", - "description": "Písmo použité pre e-maily v obyčajnom texte", - "mono": "Neproporcionálne", - "sans": "Normálne (rovnaké ako aplikácia)" - }, - "archive_mode": { - "label": "Archivovať do", - "description": "Ako organizovať e-maily pri archivácii", - "single": "Jeden priečinok", - "year": "Priečinok pre každý rok", - "month": "Priečinok pre každý mesiac", - "reorganize": "Reorganizovať existujúci archív", - "reorganize_success": "{count, plural, =0 {Žiadne e-maily na reorganizáciu} =1 {1 e-mail reorganizovaný} other {# e-mailov reorganizovaných}}", - "reorganize_error": "Nepodarilo sa reorganizovať archív" - }, - "permanently_delete_junk": { - "label": "Trvalo odstraňovať spam", - "description": "Trvalo odstraňovať správy zo priečinka Spam namiesto presunutia do Koša" - }, - "mail_layout": { - "label": "Rozloženie pošty", - "description": "Vyberte si medzi klasickým rozdeleným zobrazením a sústredením zoznamom.", - "split": "Rozdelené zobrazenie", - "split_description": "Zoznam správ a panel na čítanie zostávajú viditeľné vedľa seba.", - "focus": "Sústredený zoznam", - "focus_description": "Zobraziť jeden riadok na správu a otvoriť poštu na plnú šírku.", - "horizontal": "Panel na čítanie dole", - "horizontal_description": "Zobraziť zoznam správ hore a otvoriť vybranú správu v paneli na čítanie pod ním." - }, - "show_preview": { - "label": "Zobraziť náhľadový text", - "description": "Zobrazovať náhľady e-mailov v zozname", - "focus_description": "Zobrazovať vložený náhľadový text v jednoriadkovom sústredenom zozname" - }, - "disable_threading": { - "label": "Zakázať zoskupovanie konverzácií", - "description": "Zobrazovať e-maily ako jednotlivé správy namiesto zoskupovania do vlákien" - }, - "plain_text_mode": { - "label": "Iba prostý text", - "description": "Zakázať editor formátovaného textu a odosielať všetky e-maily iba ako prostý text" - }, - "auto_select_reply_identity": { - "label": "Odpovedať z prijatej adresy", - "description": "Pri odpovedaní automaticky prepnúť adresu odosielateľa na identitu, ktorá pôvodne prijala správu" - }, - "reply_identity_match": { - "label": "Porovnávanie prijímacej adresy", - "description": "Ktoré prijímacie adresy sa považujú za vaše. „Iba presná adresa“ vyberie len jednu z vašich nastavených identít; „rovnaká doména“ navyše považuje akúkoľvek inú adresu na doménach vašich identít za catch-all alias a prepíše na ňu hlavičku Od. Ak sú tieto adresy distribučné zoznamy, zvoľte presnú adresu.", - "exact": "Iba presná adresa", - "domain": "Akákoľvek adresa na mojich doménach" - }, - "signature_position": { - "label": "Pozícia podpisu", - "description": "Kam vložiť podpis v odpovediach a preposlaniach.", - "above_quote": "Pred citovaným textom", - "below_quote": "Za citovaným textom" - }, - "signature_separator": { - "label": "Oddeľovač podpisu", - "description": "Pred podpis pridať štandardný oddeľovací riadok \"-- \" (RFC 3676)." - }, - "sub_address_delimiter": { - "label": "Oddeľovač podadresy", - "description": "Znak oddeľujúci používateľské meno od podadresy.", - "option": "{delimiter} (pouzivatel{delimiter}stitok@domena.sk)", - "custom": "Vlastný…", - "custom_input_label": "Vlastný znak oddeľovača" - }, - "attachment_click_action": { - "label": "Akcia po kliknutí na prílohu", - "description": "Vyberte, či kliknutie na prílohu zobrazí náhľad alebo ju okamžite stiahne", - "preview": "Náhľad, ak je to možné", - "download": "Okamžite stiahnuť" - }, - "attachment_position": { - "label": "Pozícia príloh", - "description": "Kde zobrazovať prílohy v hlavičke e-mailu", - "beside-sender": "Vedľa odosielateľa", - "below-header": "Pod hlavičkou" - }, - "emails_per_page": { - "10": "10 e-mailov", - "25": "25 e-mailov", - "50": "50 e-mailov", - "100": "100 e-mailov", - "label": "E-mailov na stránku", - "description": "Počet e-mailov načítaných naraz" - }, - "always_light_mode": { - "label": "Vždy zobrazovať e-maily v svetlom režime", - "description": "Vykresľovať obsah e-mailu v svetlom režime aj keď je aplikácia v tmavom režime" - }, - "external_content": { - "label": "Externý obsah", - "description": "Ako zaobchádzať s obrázkami a externým obsahom", - "ask": "Vždy sa opýtať", - "block": "Vždy blokovať", - "allow": "Vždy povoliť" - }, - "trusted_senders": { - "label": "Dôveryhodní odosielatelia", - "description": "Spravujte odosielateľov, ktorých obrázky sa načítavajú automaticky", - "count_zero": "Žiadni", - "count_one": "1 odosielateľ", - "count_other": "{count} odosielateľov", - "modal_title": "Dôveryhodní odosielatelia", - "empty_title": "Žiadni dôveryhodní odosielatelia", - "empty_description": "Pri prezeraní e-mailu so zablokovanými obrázkami kliknite na \"Vždy dôverovať tomuto odosielateľovi\".", - "add_manually": "Pridať odosielateľa ručne", - "add_button": "Pridať", - "add_placeholder": "Zadajte e-mailovú adresu", - "search_placeholder": "Hľadať odosielateľov...", - "no_results": "Hľadaniu nezodpovedá žiadny odosielateľ", - "remove": "Odstrániť", - "close": "Zavrieť", - "invalid_email": "Zadajte platnú e-mailovú adresu", - "already_added": "Tento odosielateľ je už dôveryhodný", - "save_error": "Uloženie zlyhalo", - "use_address_book_label": "Synchronizovať s adresárom", - "use_address_book_description": "Ukladať dôveryhodných odosielateľov vo vyhradenom adresári" - }, - "hover_actions": { - "label": "Rýchle akcie po prejdení myšou", - "description": "Vyberte rýchle akcie viditeľné po prejdení cez e-mail v zozname", - "delete": "Odstrániť", - "star": "Hviezdička / Odstrániť hviezdičku", - "mark_read": "Označiť ako prečítané / neprečítané", - "archive": "Archivovať", - "tag": "Štítok", - "spam": "Označiť ako spam", - "not_spam": "Nie je spam", - "none_selected": "Neboli vybrané žiadne akcie", - "mode_label": "Režim zobrazenia", - "mode_inline": "Vložené", - "mode_floating": "Plávajúce", - "corner_label": "Plávajúca pozícia", - "corner_top-left": "Ľavý horný roh", - "corner_top-right": "Pravý horný roh", - "corner_bottom-left": "Ľavý dolný roh", - "corner_bottom-right": "Pravý dolný roh" - }, - "default_mail_program": { - "label": "Predvolený poštový program", - "description": "Zaregistrovať aplikáciu {appName} ako predvolený poštový program pre odkazy mailto:", - "button": "Nastaviť ako predvolený", - "success": "Prehliadač bol požiadaný o nastavenie ako predvoleného", - "error": "Váš prehliadač túto funkciu nepodporuje" - }, - "attachment_reminder": { - "label": "Pripomenutie prílohy", - "description": "Upozorniť pred odoslaním, keď správa spomína prílohy, ale žiadne nie sú priložené", - "keywords_label": "Spúšťacie kľúčové slová", - "keywords_description": "Slová alebo frázy, ktoré spustia pripomenutie", - "add_placeholder": "Pridať kľúčové slovo...", - "add": "Pridať", - "remove": "Odstrániť" - }, - "empty_subject_warning": { - "label": "Upozornenie na prázdny predmet", - "description": "Pred odoslaním správy bez predmetu požiadať o potvrdenie." - }, - "hide_inline_image_attachments": { - "label": "Skryť vložené obrázky zo zoznamu príloh", - "description": "Obrázky vložené priamo do textu správy sa nezobrazia ako samostatné prílohy" - }, - "attachment_image_previews": { - "label": "Zobraziť náhľady obrázkov v prílohách", - "description": "Zobrazovať obrázkové prílohy ako miniatúry namiesto všeobecných ikon súborov" - }, - "send_delay": { - "label": "Vrátiť odoslanie / oneskorenie odoslania", - "description": "Odložiť bežné odoslanie krátkym oknom na serveri.", - "off": "Vypnuté", - "seconds": "{seconds} sekúnd", - "unsupported": "Aktuálny účet neoznamuje podporu oneskoreného odoslania." - }, - "return_to_list_after_action": { - "label": "Vrátiť sa na zoznam po odstránení alebo označení ako neprečítané", - "description": "Po odstránení alebo označení otvorenej správy ako neprečítanej sa vrátiť na zoznam správ." - }, - "swipe_right_action": { - "label": "Akcia pri potiahnutí doprava (mobil)", - "description": "Na dotykových zariadeniach akcia, ktorá sa vykoná pri potiahnutí správy v zozname doprava." - }, - "swipe_left_action": { - "label": "Akcia pri potiahnutí doľava (mobil)", - "description": "Na dotykových zariadeniach akcia, ktorá sa vykoná pri potiahnutí správy v zozname doľava." - }, - "swipe_actions": { - "none": "Žiadna", - "archive": "Archivovať", - "delete": "Odstrániť", - "mark_read": "Prepnúť prečítané", - "star": "Prepnúť hviezdičku", - "spam": "Označiť ako spam" - }, - "rtl_editing": { - "label": "Podpora písania sprava doľava", - "description": "Pridá do panela nástrojov editora tlačidlo smeru textu, ktorým možno odseky nastaviť zľava doprava alebo sprava doľava" - } - }, - "composer": { - "title": "Skladanie správ", - "description": "Nakonfigurujte nastavenia pre písanie e-mailov", - "autosave": { - "label": "Interval automatického ukladania", - "description": "Ako často sa majú koncepty automaticky ukladať", - "30s": "Každých 30 sekúnd", - "1m": "Každú minútu", - "2m": "Každé 2 minúty", - "5m": "Každých 5 minút" - }, - "send_confirmation": { - "label": "Potvrdenie odoslania", - "description": "Pred odoslaním správy požiadať o potvrdenie" - }, - "default_reply": { - "label": "Predvolený režim odpovede", - "description": "Predvolená akcia po kliknutí na Odpovedať", - "reply": "Odpovedať", - "reply_all": "Odpovedať všetkým" - } - }, - "privacy": { - "title": "Súkromie a zabezpečenie", - "description": "Správa nastavení súkromia a zabezpečenia", - "external_images": { - "label": "Blokovať externé obrázky", - "description": "Zabrániť sledovaniu prostredníctvom externých obrázkov" - }, - "session_timeout": { - "label": "Časový limit relácie", - "description": "Automaticky odhlásiť po určitej dobe nečinnosti", - "never": "Nikdy", - "30m": "30 minút", - "1h": "1 hodina", - "4h": "4 hodiny" - }, - "clear_cache": { - "label": "Vymazať vyrovnávaciu pamäť", - "description": "Odstrániť dáta z vyrovnávacej pamäte a dočasné súbory", - "button": "Vymazať vyrovnávaciu pamäť", - "confirm": "Naozaj chcete vymazať vyrovnávaciu pamäť?", - "success": "Vyrovnávacia pamäť bola vymazaná" - } - }, - "account": { - "title": "Účet", - "description": "Zobraziť informácie o vašom účte", - "name_label": "Zobrazované meno", - "username_label": "Používateľské meno", - "account_type_label": "Typ účtu", - "auth_method_label": "Overenie", - "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", - "auth_method_basic": "Heslo", - "demo_account": "Demo účet", - "email": { - "label": "E-mailová adresa", - "value": "{email}" - }, - "server": { - "label": "JMAP server", - "value": "{server}" - }, - "storage": { - "label": "Využitie úložiska", - "used": "{used} z {total} použitých", - "percentage": "{percent} % použitých" - }, - "last_sync": { - "label": "Posledná synchronizácia", - "value": "{time}" - }, - "accounts": { - "title": "Prihlásené účty", - "description": "Presuňte na zmenu poradia účtov v rozbaľovacom zozname", - "active": "Aktuálne aktívny účet", - "default_badge": "Predvolený účet", - "set_default": "Nastaviť ako predvolený", - "switch_to": "Prepnúť na tento účet", - "move_up": "Posunúť nahor", - "move_down": "Posunúť nadol", - "drag_handle": "Presuňte na zmenu poradia", - "add": "Pridať účet" - }, - "shared_accounts": { - "title": "Zdieľané so mnou", - "description": "Skupinové a zdieľané účty, ktoré môžete spravovať.", - "shared_label": "Zdieľaný účet" - } - }, - "scoped": { - "back": "Späť na môj účet", - "managing": "Správa: {name}" - }, - "security": { - "title": "Zabezpečenie účtu", - "description": "Správa hesla, dvojfaktorového overenia a nastavení zabezpečenia", - "detecting": "Zisťovanie možností servera...", - "not_available": "Správa zabezpečenia účtu nie je pre tento poštový server k dispozícii.", - "password": { - "title": "Zmeniť heslo", - "current": "Aktuálne heslo", - "new": "Nové heslo", - "confirm": "Potvrdiť nové heslo", - "submit": "Zmeniť heslo", - "success": "Heslo bolo zmenené", - "error_title": "Zmena hesla zlyhala", - "error_mismatch": "Nové heslá sa nezhodujú", - "error_min_length": "Heslo musí mať aspoň 8 znakov", - "error_generic": "Nepodarilo sa zmeniť heslo" - }, - "display_name": { - "label": "Zobrazované meno", - "description": "Vaše meno zobrazené na serveri", - "placeholder": "Zadajte svoje zobrazované meno", - "save": "Uložiť", - "success": "Zobrazované meno bolo aktualizované", - "error": "Nepodarilo sa aktualizovať zobrazované meno" - }, - "totp": { - "section_title": "Dvojfaktorové overenie", - "label": "Overenie TOTP", - "description": "Pridajte ďalšiu vrstvu zabezpečenia pomocou jednorazového hesla na báze času", - "active": "Zapnuté", - "inactive": "Vypnuté", - "enabled": "Dvojfaktorové overenie zapnuté", - "disabled": "Dvojfaktorové overenie vypnuté", - "enable_error": "Nepodarilo sa zapnúť 2FA", - "disable_error": "Nepodarilo sa vypnúť 2FA", - "setup_instructions": "Skopírujte túto URL adresu do svojej overovacej aplikácie (Google Authenticator, Authy atd.):", - "verification_code": "Overovací kód", - "confirm": "Potvrdiť", - "disable": "Vypnúť", - "disable_confirm_prompt": "Na vypnutie dvojfaktorového overenia zadajte heslo.", - "password_required": "Heslo je povinné", - "code_required": "Overovací kód je povinný", - "code_invalid": "Neplatný kód. Skontrolujte overovaciu aplikáciu a skúste znova." - }, - "app_passwords": { - "title": "Heslá aplikácií", - "description": "Vytvorte heslá pre aplikácie, ktoré nepodporujú dvojfaktorové overenie", - "add": "Pridať", - "create": "Vytvoriť", - "cancel": "Zrušiť", - "done": "Hotovo", - "generate": "Vygenerovať", - "name_label": "Názov aplikácie", - "name_placeholder": "napr. Thunderbird, iPhone Mail", - "expires_label": "Platí do (voliteľné)", - "allowed_ips_label": "Povolené IP (voliteľné)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Oddeľte čiarkou alebo medzerou. Pre povolenie všetkých IP nechajte prázdne.", - "password_label": "Heslo (nechajte prázdne pre automatické generovanie)", - "password_placeholder": "Automaticky vygenerované", - "copy_now_warning": "Skopírujte toto heslo hneď – už sa znova nezobrazí.", - "added": "Heslo aplikácie vytvorené", - "removed": "Heslo aplikácie odstránené", - "add_error": "Chyba pri vytváraní hesla aplikácie", - "remove_error": "Chyba pri odstraňovaní hesla aplikácie", - "none": "Žiadne heslá aplikácií" - }, - "api_keys": { - "title": "API kľúče", - "description": "Vytvorte API kľúče pre skripty a integrácie.", - "name_label": "Názov kľúča", - "name_placeholder": "napr. Zálohovací skript, CI runner", - "copy_now_warning": "Skopírujte tento API kľúč hneď – už sa znova nezobrazí.", - "added": "API kľúč vytvorený", - "removed": "API kľúč odstránený", - "add_error": "Chyba pri vytváraní API kľúča", - "remove_error": "Chyba pri odstraňovaní API kľúča", - "none": "Žiadne API kľúče" - }, - "encryption": { - "section_title": "Šifrovanie dát v pokoji", - "label": "Šifrovanie e-mailov", - "description": "Šifrovať správy uložené na serveri pre väčšie súkromie", - "active": "Šifrovanie {type} zapnuté", - "inactive": "Vypnuté", - "enabled": "Šifrovanie dát v pokoji zapnuté", - "disabled_success": "Šifrovanie dát v pokoji vypnuté", - "error": "Nepodarilo sa aktualizovať nastavenia šifrovania", - "algorithm_label": "Šifrovací algoritmus", - "encrypt_on_append": "Šifrovať nové e-maily pri nahrávaní", - "allow_spam_training": "Povoliť trénovanie spamového filtra pred zašifrovaním e-mailov", - "enable_encryption": "Zapnúť šifrovanie", - "enable_tooltip": "Zapnúť šifrovanie týmto kľúčom", - "configure_title": "Zapnúť šifrovanie týmto kľúčom", - "enable_button": "Zapnúť", - "disable_tooltip": "Vypnúť šifrovanie", - "enabled_success": "Šifrovanie dát v pokoji zapnuté" - }, - "email_client": { - "title": "Konfigurácia poštového klienta", - "description": "Použite tieto prihlasovacie údaje na konfiguráciu e-mailového klienta (Thunderbird, Apple Mail, Outlook atd.)", - "jmap_username_label": "Používateľské meno JMAP", - "copy": "Kopírovať", - "copied": "Skopírované", - "password_instructions": "Použite vyššie uvedené používateľské meno JMAP spolu s heslom aplikácie na prihlásenie v poštovom klientovi." - }, - "link_device": { - "title": "Prepojiť mobilnú aplikáciu", - "description": "Prihláste sa do mobilnej aplikácie Bulwark Mail bez písania. Vygenerujte tu QR kód a naskenujte ho z prihlasovacej obrazovky aplikácie.", - "generate": "Zobraziť QR kód", - "regenerate": "Zobraziť nový kód", - "instructions": "Otvorte aplikáciu Bulwark Mail, klepnite na \"Skenovať QR kód\" na prihlasovacej obrazovke a namieste fotoaparát sem.", - "expires_in": "Tento kód vyprší za {seconds} sekúnd. Dá sa použiť iba raz.", - "expired": "Platnosť tohto kódu vypršala.", - "generating": "Generovanie…", - "error": "Nepodarilo sa vytvoriť párovací kód. Skúste to znova." - }, - "public_keys": { - "name_label": "Názov verejného kľúča", - "name_placeholder": "napr. Môj kľúč", - "key_label": "Verejný kľúč (S/MIME alebo PGP vo formáte ASCII)", - "key_placeholder": "Sem vložte svoj verejný kľúč", - "none": "Nie sú nastavené žiadne verejné kľúče" - } - }, - "identities": { - "title": "Identity odosielateľa", - "description": "Správa e-mailových adries, z ktorých môžete odosielať správy", - "identities_count": { - "label": "Vaše identity", - "description": "E-mailové adresy nakonfigurované na odosielanie", - "count_zero": "Žiadne identity", - "count_one": "1 identita", - "count_other": "{count} identít" - }, - "manage": "Spravovať identity", - "sub_addressing": { - "label": "Podadresovanie", - "description": "Používajte štítky ako pouzivatel+stitok@domena.sk na organizáciu prichádzajúcej pošty", - "learn_more": "Zistiť viac" - } - }, - "vacation": { - "title": "Automatická odpoveď", - "description": "Automaticky odpovedať na prichádzajúce e-maily počas vašej neprítomnosti", - "loading": "Načítavanie nastavení automatickej odpovede...", - "not_supported": "Váš poštový server nepodporuje automatické odpovede v neprítomnosti.", - "fetch_error": "Nepodarilo sa načítať nastavenia automatickej odpovede. Skúste to znova.", - "status": { - "label": "Automatická odpoveď", - "description": "Odosielať automatickú odpoveď ľuďom, ktorí vám napíšu", - "active": "Aktívna", - "inactive": "Neaktívna" - }, - "date_range": { - "title": "Časové obdobie", - "description": "Voliteľne obmedzte automatickú odpoveď na určité obdobie", - "start": "Dátum začiatku", - "start_description": "Nechajte prázdne pre okamžité spustenie", - "end": "Dátum ukončenia", - "end_description": "Nechajte prázdne pre beh bez ukončenia" - }, - "message": { - "title": "Správa automatickej odpovede", - "description": "Správa, ktorá bude odoslaná ako odpoveď", - "subject_label": "Predmet", - "subject_description": "Predmet automatickej odpovede", - "subject_placeholder": "Mimo kancelárie", - "body_label": "Telo správy", - "body_description": "Obsah správy v prostom texte", - "body_placeholder": "Ďakujem za vašu správu. Momentálne som mimo kancelárie a odpoviem po svojom návrate.", - "html_label": "Formátovaná správa (HTML)", - "html_description": "Pridajte formátovanú verziu s odkazmi a štýlom. Príjemcovia, ktorých poštový klient ju nedokáže zobraziť, uvidia obyčajný text vyššie.", - "html_placeholder": "Napíšte formátovanú odpoveď o neprítomnosti…" - }, - "preview": { - "title": "Náhľad", - "show": "Zobraziť náhľad", - "hide": "Skryť náhľad" - }, - "save": "Uložiť zmeny", - "saving": "Ukladanie...", - "warnings": { - "end_before_start": "Dátum ukončenia musí byť po dátume začiatku", - "start_in_past": "Dátum začiatku je v minulosti", - "empty_body": "Telo správy je prázdne – príjemcovia dostanú prázdnu odpoveď", - "subject_too_long": "Predmet je pre server príliš dlhý (najviac {max} bajtov).", - "body_too_long": "Správa je pre server príliš dlhá (najviac {max} bajtov vrátane formátovania)." - } - }, - "folders": { - "role_memos": "Poznámky", - "title": "Priečinky", - "description": "Správa priečinkov pošty a priradenie štandardných rolí", - "folder_list": "Vaše priečinky", - "folder_list_description": "Kliknutím na ikonu priečinka ho upravíte", - "standard_roles": "Štandardné roly priečinkov", - "standard_roles_description": "Priraďte priečinky používané pre štandardné roly poštovej schránky.", - "role_inbox": "Doručené", - "role_drafts": "Koncepty", - "role_sent": "Odoslané", - "role_trash": "Kôš", - "role_junk": "Spam / Nevyžiadaná pošta", - "role_archive": "Archív", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Žiadna", - "create_folder": "Vytvoriť priečinok", - "create_subfolder": "Vytvoriť podpriečinok", - "reorder": "Potiahnutím zmeníte poradie", - "reorder_error": "Nepodarilo sa zmeniť poradie priečinkov", - "move_error": "Nepodarilo sa presunúť priečinok", - "subfolder_of": "V priečinku {name}", - "subfolder_name": "Názov podpriečinka", - "new_folder_name": "Názov priečinka", - "rename": "Premenovať", - "change_icon": "Zmeniť ikonu", - "delete": "Odstrániť", - "confirm_delete": "Naozaj chcete odstrániť priečinok \"{name}\"? E-maily v tomto priečinku budú presunuté do Koša.", - "create": "Vytvoriť", - "cancel": "Zrušiť", - "no_folders": "Žiadne vlastné priečinky", - "cannot_delete_role": "Priečinok so štandardnou rolou sa nedá odstrániť. Najprv túto rolu odstráňte.", - "folder_created": "Priečinok bol vytvorený", - "folder_renamed": "Priečinok bol premenovaný", - "folder_deleted": "Priečinok bol odstránený", - "role_updated": "Rola priečinka bola aktualizovaná", - "error_create": "Priečinok sa nepodarilo vytvoriť", - "error_rename": "Priečinok sa nepodarilo premenovať", - "error_delete": "Priečinok sa nepodarilo odstrániť", - "error_delete_has_children": "Priečinok sa nedá odstrániť: stále obsahuje podpriečinky. Najprv ich odstráňte alebo presuňte.", - "error_delete_has_email": "Priečinok sa nedá odstrániť: stále obsahuje e-maily. Najprv ich presuňte alebo odstráňte.", - "error_role": "Rolu priečinka sa nepodarilo aktualizovať" - }, - "advanced": { - "title": "Pokročilé", - "description": "Pokročilé možnosti a nastavenia pre vývojárov", - "debug_mode": { - "label": "Režim ladenia", - "description": "Povoliť podrobné protokolovanie na riešenie problémov" - }, - "debug_categories": { - "description": "Vyberte kategórie na protokolovanie.", - "jmap": "Klient JMAP", - "jmap_description": "Operácie so schránkami, načítavanie správ a požiadavky protokolu JMAP", - "calendar": "Kalendár", - "calendar_description": "Udalosti kalendára, importy a plánovanie správ", - "tasks": "Úlohy", - "tasks_description": "Vytváranie úloh kalendára, načítavanie a aktualizácie", - "auth": "Overenie", - "auth_description": "Prihlasovanie, TOTP, výmena tokenov a správa relácií", - "filters": "Filtre", - "filters_description": "Pravidlá filtrov Sieve a skripty pre automatickú odpoveď", - "email": "Zobrazenie e-mailu", - "email_description": "Vykresľovanie e-mailov, spracovanie TNEF a označovanie ako prečítané", - "push": "Push oznámenia", - "push_description": "Konfigurácia a doručovanie push oznámení", - "contacts": "Kontakty a adresáre", - "contacts_description": "Synchronizácia kontaktov, operácie s adresárom a dôveryhodní odosielatelia" - }, - "settings_sync": { - "label": "Synchronizácia nastavení", - "description": "Synchronizovať nastavenia medzi prehliadačmi a zariadeniami" - }, - "sender_favicons": { - "label": "Favikony odosielateľov", - "description": "Zobrazovať ikony webových stránok ako profilové obrázky firemných odosielateľov" - }, - "show_avatars_in_junk": { - "label": "Zobrazovať avatáry v spame", - "description": "Zobrazovať profilové fotky a ikony odosielateľov v spame. Predvolene vypnuté." - }, - "keyboard_shortcuts": { - "label": "Klávesové skratky", - "description": "Zobraziť dostupné klávesové skratky", - "button": "Zobraziť skratky" - }, - "refresh_cache": { - "label": "Obnoviť uložené údaje", - "description": "Znova načíta kontakty, kalendáre a priečinky zo servera. Zachová vaše účty a prihlásenia — opraví zastarané alebo nesprávne zobrazenie bez odhlásenia.", - "button": "Obnoviť" - }, - "reset_settings": { - "label": "Resetovať nastavenia", - "description": "Obnoviť všetky nastavenia na predvolené hodnoty", - "button": "Obnoviť predvolené" - }, - "export_settings": { - "label": "Exportovať nastavenia", - "description": "Stiahnuť svoje nastavenia ako súbor JSON", - "button": "Exportovať" - }, - "import_settings": { - "label": "Importovať nastavenia", - "description": "Nahrať nastavenia zo súboru JSON", - "button": "Importovať" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Aplikácie postranného panela", - "description": "Spravujte vlastné aplikácie a odkazy v postrannom paneli", - "keep_loaded": "Ponechať aplikácie načítané", - "keep_loaded_description": "Udržiavať vložené aplikácie spustené na pozadí počas prepínania", - "managed_title": "Poskytnuté správcom", - "managed_description": "Tieto aplikácie sú nastavené pre všetkých a nedajú sa zmeniť.", - "manage_title": "Vlastné aplikácie", - "manage_description": "Pridávať, upravovať alebo odstraňovať vlastné aplikácie z postranného panela" - }, - "contacts": { - "title": "Kontakty", - "description": "Import a export kontaktov", - "group_by_letter_label": "Zoskupiť podľa prvého písmena", - "sort_by_last_name_label": "Zoradiť podľa priezviska", - "sort_by_last_name_description": "Zoradiť zoznam kontaktov podľa priezviska, aby členovia rodiny boli pri sebe", - "group_by_letter_description": "Zobraziť abecedné záhlavia sekcií v zozname kontaktov", - "import_label": "Importovať kontakty", - "import_description": "Importovať kontakty zo súboru vCard (.vcf)", - "export_label": "Exportovať kontakty", - "export_description": "Exportovať všetky kontakty ako súbor vCard (.vcf)", - "manage_title": "Adresáre", - "manage_description": "Premenovať svoje adresáre", - "no_address_books": "Neboli nájdené žiadne adresáre", - "categories_title": "Kategórie", - "categories_description": "Premenovať kategórie kontaktov", - "no_categories": "Neboli nájdené žiadne kategórie" - }, - "downloads": { - "title": "Stiahnuté", - "description": "Prispôsobte si, ako sa pomenúvajú stiahnuté e-maily a prílohy.", - "reset": "Obnoviť predvolené", - "preview": "Náhľad:", - "email_template": { - "label": "Názov súboru e-mailu (.eml)", - "description": "Šablóna použitá pri exporte e-mailu alebo jeho presunutí do súborového systému. Prípona .eml sa pridáva automaticky." - }, - "attachment_template": { - "label": "Názov súboru prílohy", - "description": "Šablóna použitá pri sťahovaní alebo presunutí prílohy." - }, - "bundle_template": { - "label": "Názov .zip súboru s viacerými e-mailmi", - "description": "Šablóna použitá pri presunutí alebo stiahnutí viacerých vybraných e-mailov ako jedného .zip archívu." - }, - "spaces": { - "label": "Medzery", - "description": "Nahradiť medzery vo výslednom názve súboru iným znakom.", - "keep": "Zachovať medzery", - "underscore": "Nahradiť za _", - "dash": "Nahradiť za -" - }, - "lowercase": { - "label": "Malé písmená", - "description": "Vynútiť celý názov súboru malými písmenami." - }, - "strip_diacritics": { - "label": "Odstrániť diakritiku", - "description": "Previesť písmená s diakritikou na ASCII ekvivalenty (á → a, é → e)." - }, - "collapse_separators": { - "label": "Zlúčiť opakované oddeľovače", - "description": "Zlúčiť postupnosti medzier, podčiarkovníkov alebo pomlčiek do jedného znaku." - }, - "after_export": { - "label": "Po exporte", - "description": "Po exporte ako .eml možno e-mail voliteľne presunúť.", - "keep": "Ponechať v schránke", - "archive": "Presunúť do archívu", - "trash": "Presunúť do koša" - } - }, - "filters": { - "title": "E-mailové filtre", - "description": "Vytvárajte pravidlá na automatické triedenie, štítkovanie a správu prichádzajúcich e-mailov", - "add_rule": "Pridať pravidlo", - "no_rules": "Žiadne pravidlá filtrov", - "no_rules_description": "Vytvárajte pravidlá na automatickú organizáciu prichádzajúcich e-mailov", - "vacation_active": "Automatická odpoveď je aktívna", - "vacation_active_description": "Automatická odpoveď je zapnutá pre prichádzajúce správy", - "vacation_configure": "Konfigurovať", - "edit_rule": "Upraviť pravidlo", - "new_rule": "Nové pravidlo", - "delete_rule": "Odstrániť pravidlo", - "delete_confirm": "Naozaj chcete odstrániť toto pravidlo?", - "enable": "Povoliť", - "disable": "Zakázať", - "raw_editor": "Textový editor Sieve", - "raw_editor_warning": "Úprava samotného skriptu Sieve môže narušiť vizuálnu úpravu pravidiel.", - "validate": "Overiť", - "validation_success": "Skript je platný", - "validation_error": "Skript obsahuje chyby", - "save": "Uložiť pravidlá", - "saving": "Ukladanie...", - "saved": "Filtre boli uložené", - "save_failed": "Uloženie filtrov zlyhalo", - "loading": "Načítavanie filtrov...", - "not_supported": "Váš poštový server nepodporuje e-mailové filtre.", - "rule_name": "Názov pravidla", - "rule_name_placeholder": "napr. Triediť newslettery", - "match_all": "Splňuje VŠETKY podmienky", - "match_any": "Splňuje AKÚKOĽVEK podmienku", - "conditions": "Podmienky", - "add_condition": "Pridať podmienku", - "actions": "Akcie", - "add_action": "Pridať akciu", - "stop_processing": "Zastaviť spracovanie ďalších pravidiel", - "include_spam": "Presúvať aj správy označené ako spam", - "attachment_type_placeholder": "napr. pdf, doc, jpg", - "value_placeholder_multi": "Hodnota (viac oddelených čiarkami)", - "condition_fields": { - "attachment": "Príloha", - "from": "Od", - "to": "Komu", - "cc": "Kópia", - "subject": "Predmet", - "header": "Vlastná hlavička", - "size": "Veľkosť", - "body": "Telo správy" - }, - "comparators": { - "has_any": "je prítomná", - "has_type": "typu", - "contains": "obsahuje", - "not_contains": "neobsahuje", - "is": "je presne", - "not_is": "nie je", - "starts_with": "začína na", - "ends_with": "končí na", - "matches": "zodpovedá vzoru", - "greater_than": "je väčšia ako", - "less_than": "je menšia ako" - }, - "action_types": { - "move": "Presunúť do priečinka", - "copy": "Kopírovať do priečinka", - "forward": "Preposlať na", - "mark_read": "Označiť ako prečítané", - "star": "Označiť správu hviezdičkou", - "add_label": "Pridať štítok", - "discard": "Zahodiť (ticho odstrániť)", - "reject": "Odmietnuť so správou", - "keep": "Ponechať v Doručených", - "stop": "Zastaviť spracovanie" - }, - "move_to_folder": "Vybrať priečinok", - "copy_to_folder": "Vybrať priečinok", - "forward_to": "Preposlať na e-mailovú adresu", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Ponechať kópiu", - "forward_limit": "Limit preposlaní na správu na tomto serveri: {count}. Ďalšie preposlania sa preskočia.", - "reject_message": "Správa o odmietnutí", - "reject_placeholder": "Váš e-mail bol odmietnutý", - "label_name": "Názov štítku", - "label_placeholder": "Vybrať štítok", - "header_name": "Názov hlavičky", - "header_placeholder": "napr. X-Mailing-List", - "size_bytes": "Veľkosť v bajtoch", - "size_placeholder": "napr. 1000000", - "system_managed": "Pravidlo spravované systémom", - "opaque_warning": "Tento skript bol upravený mimo vizuálneho sprievodcu. K dispozícii je iba priama úprava Sieve.", - "open_sieve_editor": "Otvoriť textový editor Sieve", - "fetch_error": "Nepodarilo sa načítať filtre", - "expanded_view": "Rozšírené zobrazenie", - "expanded_view_description": "Zobrazovať pravidlá filtrov s podrobnými blokmi podmienok a akcií", - "if": "Ak", - "then": "Potom", - "match_all_conditions": "všetky splnené", - "match_any_condition": "akékoľvek splnené", - "and": "a", - "or": "alebo", - "cancel": "Zrušiť", - "confirm_delete": "Odstrániť", - "rule_list": "Pravidlá filtrov", - "drag_to_reorder": "Presuňte na zmenu poradia", - "match_type": "Typ zhody", - "reset_to_visual": "Resetovať na vizuálneho sprievodcu", - "reset_warning": "Tým zahodíte aktuálny skript a začnete odznova.", - "confirm_reset": "Resetovať", - "validation_empty_name": "Názov pravidla je povinný", - "validation_empty_conditions": "Je potrebná aspoň jedna podmienka s hodnotou", - "validation_empty_actions": "Je potrebná aspoň jedna akcia", - "templates_section": "Začať zo šablóny", - "template_newsletters": "Presunúť newslettery do priečinka", - "template_receipts": "Automaticky archivovať účtenky", - "template_important": "Označiť dôležité e-maily", - "template_notifications": "Filtrovať oznámenia", - "sieve_editor": { - "title": "Editor skriptov Sieve", - "warning": "Úprava samotného skriptu Sieve môže narušiť vizuálnu úpravu pravidiel.", - "script_content": "Skript Sieve", - "valid": "Skript je platný", - "invalid": "Skript obsahuje chyby", - "save_warning": "Uloženie prepíše všetky vizuálne pravidlá. Túto akciu nie je možné vrátiť. Potvrďte opätovným kliknutím na Uložiť.", - "validating": "Overovanie...", - "validate": "Overiť", - "cancel": "Zrušiť", - "save": "Uložiť", - "confirm_save": "Potvrdiť uloženie", - "validation_failed": "Požiadavka na overenie zlyhala" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# podmienka} other {# podmienok}}", - "actions_count": "{count, plural, one {# akcia} other {# akcií}}" - }, - "origin_external": "Externé", - "managed_by_tooltip": "Spravované aplikáciou {source}. Upravte to v danej aplikácii alebo použite editor zdrojového kódu Sieve." - }, - "templates": { - "title": "E-mailové šablóny", - "description": "Vytvárajte opakovane použiteľné e-mailové šablóny s premennými zástupnými symbolmi", - "add": "Nová šablóna", - "edit": "Upraviť šablónu", - "name": "Názov šablóny", - "name_placeholder": "napr. Kontrolná správa", - "category": "Kategória", - "category_placeholder": "napr. Práca, Osobné", - "subject": "Predmet", - "subject_placeholder": "Predmet e-mailu", - "body": "Telo správy", - "body_placeholder": "Text e-mailu...", - "recipients_placeholder": "email@example.com", - "identity": "Odoslať ako", - "default_identity": "Predvolená identita", - "favorite": "Obľúbené", - "cancel": "Zrušiť", - "create": "Vytvoriť", - "update": "Aktualizovať", - "confirm_delete": "Odstrániť", - "no_templates": "Žiadne šablóny", - "manage": "Spravovať šablóny", - "count": "{count, plural, one {# šablóna} other {# šablón}}", - "export_import": "Export a import", - "export_import_description": "Vytvorte zálohu šablón alebo ich preneste na iné zariadenie", - "export": "Exportovať", - "import": "Importovať", - "validation": { - "empty": "Názov šablóny je povinný", - "too_long": "Názov šablóny môže mať maximálne 200 znakov" - } - }, - "files": { - "display": { - "title": "Zobrazenie", - "description": "Nakonfigurujte spôsob zobrazenia súborov a priečinkov" - }, - "folder_layout": { - "label": "Navigácia priečinkov", - "description": "Vyberte spôsob zobrazenia priečinkov: vložene so súbormi alebo v stromovej štruktúre postranného panela", - "inline": "Vložené", - "sidebar": "Postranný panel" - }, - "default_view": { - "label": "Predvolené zobrazenie", - "description": "Vyberte rozloženie do mriežky alebo zoznamu", - "list": "Zoznam", - "grid": "Mriežka" - }, - "default_sort": { - "label": "Predvolené triedenie", - "description": "Vyberte predvolené triedenie súborov", - "name": "Názov", - "size": "Veľkosť", - "modified": "Zmenené" - }, - "sort_direction": { - "label": "Smer triedenia", - "description": "Vyberte vzostupné alebo zostupné poradie", - "ascending": "Vzostupne", - "descending": "Zostupne" - }, - "icons": { - "title": "Ikony", - "description": "Nakonfigurujte vzhľad ikon súborov" - }, - "show_icons": { - "label": "Zobrazovať ikony súborov", - "description": "Zobrazovať ikony vedľa súborov a priečinkov" - }, - "colored_icons": { - "label": "Farebné ikony", - "description": "Používať farebné ikony namiesto monochromatických" - }, - "show_thumbnails": { - "label": "Zobrazovať miniatúry", - "description": "Zobrazovať náhľady obrázkov namiesto ikon pre obrázkové súbory" - }, - "behavior": { - "title": "Správanie", - "description": "Nakonfigurujte správanie prehliadača súborov" - }, - "show_hidden": { - "label": "Zobrazovať skryté súbory", - "description": "Zobrazovať súbory a priečinky začínajúce bodkou" - }, - "preview": { - "label": "Náhľad" - } - } - }, - "errors": { - "page_error_title": "Niečo sa pokazilo", - "page_error_description": "Vyskytla sa neočakávaná chyba. Skúste to znova alebo sa vráťte na domovskú stránku.", - "sidebar_error": "Nepodarilo sa načítať poštové schránky", - "email_list_error": "Nepodarilo sa načítať e-maily", - "viewer_error_title": "E-mail sa nedá zobraziť", - "viewer_error_description": "Pri vykresľovaní tohto e-mailu sa vyskytol problém. Môže obsahovať nepodporovaný obsah.", - "composer_error": "Nepodarilo sa načítať editor správ", - "settings_error_title": "Nastavenia nie sú dostupné", - "settings_error_description": "Nepodarilo sa načítať nastavenia. Vaše predvoľby sa možno neuložia.", - "try_again": "Skúsiť znova", - "reload": "Obnoviť", - "reload_emails": "Obnoviť e-maily", - "reload_settings": "Obnoviť nastavenia", - "retry": "Opakovať", - "go_home": "Prejsť do doručenej pošty" - }, - "context_menu": { - "reply": "Odpovedať", - "reply_all": "Odpovedať všetkým", - "forward": "Preposlať", - "mark_read": "Označiť ako prečítané", - "mark_unread": "Označiť ako neprečítané", - "star": "Hviezdička", - "unstar": "Odstrániť hviezdičku", - "pin": "Pripnúť", - "unpin": "Odopnúť", - "move_to": "Presunúť do...", - "archive": "Archivovať", - "delete": "Odstrániť", - "mark_as_spam": "Nahlásiť spam", - "not_spam": "Nie je spam", - "tag": "Štítok", - "items_selected": "{count} vybraných e-mailov", - "edit_draft": "Upraviť koncept", - "cancel_scheduled_send": "Zrušiť odoslanie", - "reschedule_send": "Prenaplánovať", - "cancel_and_edit": "Zrušiť a upraviť", - "cancel_and_compose_again": "Zrušiť a napísať znova" - }, - "mailbox_context_menu": { - "mark_folder_read": "Označiť priečinok ako prečítaný", - "mark_folder_tree_read": "Označiť priečinok a podpriečinky ako prečítané", - "mark_all_folders_read": "Označiť všetky priečinky ako prečítané", - "new_subfolder": "Nový podpriečinok...", - "new_folder": "Nový priečinok...", - "rename": "Premenovať...", - "share": "Zdieľať...", - "import_email": "Importovať .eml alebo .zip...", - "empty_folder": "Vyprázdniť priečinok", - "empty_folder_generic": "Vyprázdniť priečinok", - "delete_folder": "Zmazať priečinok", - "refresh": "Obnoviť", - "mark_all_confirm_title": "Označiť všetky priečinky ako prečítané", - "mark_all_confirm_message": "Označiť každú neprečítanú správu vo vašom osobnom účte ako prečítanú?", - "delete_confirm_title": "Zmazať priečinok", - "delete_confirm_message": "Trvalo zmazať priečinok \"{name}\"? Túto akciu nie je možné vrátiť.", - "prompt_new_subfolder": "Zadajte názov nového podpriečinka.", - "prompt_new_folder": "Zadajte názov nového priečinka.", - "prompt_rename": "Zadajte nový názov tohto priečinka.", - "placeholder_folder_name": "Názov priečinka", - "create": "Vytvoriť", - "rename_confirm": "Premenovať", - "toast_marked_read": "Priečinok označený ako prečítaný", - "toast_marked_read_count": "Označených {count, plural, one {1 správa} other {# správ}} ako prečítaných", - "toast_already_read": "Žiadne neprečítané správy", - "toast_marked_all_read": "Všetky priečinky označené ako prečítané", - "toast_emptied": "Priečinok vyprázdnený", - "toast_folder_created": "Priečinok vytvorený", - "toast_folder_renamed": "Priečinok premenovaný", - "toast_folder_deleted": "Priečinok zmazaný", - "toast_error_mark_read": "Nepodarilo sa označiť ako prečítané", - "toast_error_empty": "Nepodarilo sa vyprázdniť priečinok", - "toast_error_create": "Nepodarilo sa vytvoriť priečinok", - "toast_error_rename": "Nepodarilo sa premenovať priečinok", - "toast_error_delete": "Nepodarilo sa zmazať priečinok", - "toast_error_delete_has_children": "Priečinok obsahuje podpriečinky. Najprv ich odstráňte.", - "toast_error_delete_has_email": "Priečinok nie je prázdny. Najprv ho vyprázdnite.", - "delete_with_contents_title": "Priečinok nie je prázdny", - "delete_with_contents_message": "Priečinok „{name}“ stále obsahuje {count, plural, one {1 správu} few {# správy} other {# správ}}. Odstrániť priečinok aj všetky jeho správy? Túto akciu nie je možné vrátiť späť.", - "delete_with_contents_confirm": "Odstrániť priečinok a správy" - }, - "shortcuts": { - "title": "Klávesové skratky", - "tip": "Kedykoľvek stlačte ? na zobrazenie tejto nápovedy", - "sections": { - "navigation": "Navigácia", - "actions": "Akcie správy", - "global": "Globálne", - "threads": "Vlákna", - "composer": "Editor správ" - }, - "navigation": { - "next_email": "Ďalší e-mail", - "previous_email": "Predchádzajúci e-mail", - "open_email": "Otvoriť e-mail", - "close_email": "Zavrieť / Zrušiť výber" - }, - "actions": { - "reply": "Odpovedať", - "reply_all": "Odpovedať všetkým", - "forward": "Preposlať", - "star": "Prepnúť hviezdičku", - "archive": "Archivovať", - "delete": "Odstrániť", - "mark_unread": "Označiť ako neprečítané", - "mark_read": "Označiť ako prečítané", - "toggle_spam": "Nahlásiť spam / Nie je spam" - }, - "global": { - "compose": "Napísať nový e-mail", - "search": "Prejsť na hľadanie", - "help": "Zobraziť skratky", - "refresh": "Obnoviť e-maily", - "select_all": "Vybrať všetko", - "global_search": "Hľadať všade" - }, - "threads": { - "expand_collapse": "Rozbaliť/zbaliť vlákno" - }, - "composer": { - "send": "Odoslať e-mail", - "schedule_send": "Naplánovať odoslanie", - "template_picker": "Otvoriť výber šablón" - } - }, - "threads": { - "messages_one": "{count} správa", - "messages_other": "{count} správ", - "messages_tooltip": "{count, plural, one {# správa v tejto konverzácii} other {# správ v tejto konverzácii}}", - "expand": "Rozbaliť konverzáciu", - "collapse": "Zbaliť konverzáciu", - "loading": "Načítavanie konverzácie...", - "mark_read": "Označiť konverzáciu ako prečítanú", - "mark_unread": "Označiť konverzáciu ako neprečítanú", - "archive": "Archivovať konverzáciu", - "delete": "Odstrániť konverzáciu", - "star": "Označiť konverzáciu hviezdičkou", - "unstar": "Odstrániť hviezdičku z konverzácie", - "toggle_thread": "Prepnúť zobrazenie vlákna" - }, - "identities": { - "modal_title": "Správa identít odosielateľa", - "create_new": "Vytvoriť novú identitu", - "edit_identity": "Upraviť identitu", - "delete_confirm": "Odstrániť túto identitu? Túto akciu nie je možné vrátiť.", - "cannot_delete": "Túto identitu nie je možné odstrániť", - "primary_identity": "Hlavná", - "set_as_primary": "Nastaviť ako hlavnú", - "no_identities": "Neboli nájdené žiadne identity", - "display": { - "reply_to": "Odpovedať na:", - "bcc": "Skrytá kópia:", - "signature": "Podpis:", - "preview": "Náhľad:" - }, - "validation_errors": { - "invalid_emails": "Neplatné e-mailové adresy: {emails}", - "unknown_error": "Neznáma chyba" - }, - "form": { - "name_label": "Zobrazované meno", - "name_placeholder": "napr. Pracovný e-mail, Súkromný", - "name_required": "Meno je povinné", - "email_label": "E-mailová adresa", - "email_placeholder": "vas.email@example.com", - "email_required": "E-mailová adresa je povinná", - "email_invalid": "Zadajte platnú e-mailovú adresu", - "email_immutable": "E-mailovú adresu nie je možné po vytvorení zmeniť", - "reply_to_label": "Odpovedať na (voliteľné)", - "reply_to_placeholder": "iny@example.com", - "bcc_label": "Automatická skrytá kópia (voliteľné)", - "bcc_placeholder": "archiv@example.com", - "text_signature_label": "Textový podpis", - "html_signature_label": "HTML podpis", - "signature_byte_counter": "{bytes} / {max} bajtov", - "signature_byte_limit_reached": "Dosiahnutý limit servera", - "save": "Uložiť identitu", - "cancel": "Zrušiť", - "creating": "Vytváranie...", - "updating": "Aktualizovanie..." - }, - "sub_address": { - "button_tooltip": "Použiť podadresu", - "popover_title": "Pridať štítok podadresy", - "tag_input_placeholder": "Zadajte štítok (napr. nakupy)", - "preview_label": "Náhľad:", - "recent_tags": "Nedávne štítky", - "suggested_tags": "Odporúčané", - "use_address": "Použiť túto adresu", - "invalid_tag": "Štítok môže obsahovať iba písmená, číslice a pomlčky", - "tag_too_long": "Štítok môže mať maximálne 30 znakov", - "help_text": "Správy odoslané na adresu pouzivatel{delimiter}stitok@domena.sk budú doručené do vašej doručenej pošty", - "validation": { - "empty": "Štítok nesmie byť prázdny", - "too_long": "Štítok môže mať maximálne {max} znakov", - "invalid_chars": "Štítok môže obsahovať iba písmená, číslice a pomlčky" - } - }, - "badge": { - "sent_via": "cez", - "sub_address_tag": "Odoslané pomocou podadresy: {tag}", - "identity_name": "Odoslané pomocou identity: {name}", - "identity_short": "cez {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Odstrániť", - "delete_confirm_title": "Odstrániť identitu" - }, - "templates": { - "picker_title": "Vyberte šablónu", - "search_placeholder": "Hľadať šablóny...", - "section_favorites": "Obľúbené", - "section_recent": "Nedávne", - "section_uncategorized": "Ostatné", - "no_templates": "Zatiaľ žiadne šablóny", - "no_results": "Neboli nájdené žiadne šablóny", - "fill_placeholders": "Vyplniť hodnoty premenných", - "enter_value": "Zadajte hodnotu...", - "preview": "Náhľad", - "insert_with_values": "Vložiť s hodnotami", - "insert_raw": "Vložiť bez zmien", - "copy_suffix": "(kópia)", - "placeholder": "Premenná", - "placeholders": { - "recipient_name": "Meno príjemcu", - "company": "Názov spoločnosti", - "date": "Aktuálny dátum", - "day_of_week": "Deň v týždni", - "sender_name": "Vaše meno" - } - }, - "contacts": { - "title": "Kontakty", - "search_placeholder": "Hľadať kontakty...", - "create_new": "Nový kontakt", - "no_category": "Bez kategórie", - "rename_category": "Premenovať kategóriu", - "category_name_label": "Názov kategórie", - "category_renamed": "Kategória bola premenovaná", - "category_rename_failed": "Premenovanie kategórie zlyhalo", - "category_added": "Kontakt pridaný do {name}", - "category_added_plural": "{count} kontaktov bolo pridaných do {name}", - "empty_state": "Žiadne kontakty", - "empty_state_title": "Žiadne kontakty", - "empty_state_subtitle": "Vytvorte prvý kontakt alebo importujte zo súboru vCard", - "empty_search": "Hľadaniu nezodpovedajú žiadne kontakty", - "empty_search_hint": "Skúste iný hľadaný výraz", - "empty_filtered": "Žiadne kontakty nezodpovedajú filtrom", - "empty_filtered_hint": "Skúste filtre upraviť alebo zrušiť", - "clear_search": "Vymazať hľadanie", - "import_vcard": "Importovať vCard", - "delete_confirm_title": "Odstrániť kontakt", - "delete_confirm": "Naozaj chcete odstrániť tento kontakt?", - "local_mode": "Kontakty sú uložené lokálne (server nepodporuje JMAP Contacts)", - "back_to_contacts": "Späť na kontakty", - "back_to_email": "Späť na e-mail", - "open_categories": "Otvoriť kategórie", - "tabs": { - "all": "Všetky", - "groups": "Skupiny" - }, - "shared": { - "title": "Zdieľané" - }, - "address_books": { - "create": "Nový adresár", - "created": "Adresár vytvorený", - "create_failed": "Nepodarilo sa vytvoriť adresár", - "title": "Moje adresáre", - "shared_prefix": "Zdieľané: {name}", - "moved": "Kontakt presunutý do {name}", - "moved_plural": "{count} kontaktov bolo presunutých do {name}", - "move_failed": "Presun kontaktu zlyhal", - "address_book": "Adresár", - "rename": "Premenovať adresár", - "name_label": "Názov adresára", - "renamed": "Adresár bol premenovaný", - "rename_failed": "Premenovanie adresára zlyhalo", - "default": "Predvolený", - "set_default": "Nastaviť ako predvolený", - "default_updated": "Predvolený adresár aktualizovaný", - "set_default_failed": "Nepodarilo sa nastaviť predvolený adresár", - "manage": "Spravovať adresáre", - "share": "Zdieľať adresár", - "new_contact_in_book": "Nový kontakt v tomto adresári", - "delete": "Zmazať adresár", - "confirm_delete": "Zmazať \"{name}\"? Všetky kontakty v tomto adresári budú odstránené.", - "deleted": "Adresár zmazaný", - "delete_failed": "Adresár sa nepodarilo zmazať" - }, - "detail": { - "emails": "E-mailové adresy", - "phones": "Telefónne čísla", - "organizations": "Organizácie", - "addresses": "Adresy", - "notes": "Poznámky", - "titles": "Tituly a roly", - "online_services": "Online služby", - "anniversaries": "Výročia", - "personal_info": "Osobné informácie", - "languages": "Jazyky", - "categories": "Kategórie", - "related_contacts": "Súvisiace kontakty", - "crypto_keys": "Kryptografické kľúče", - "cert_issuer": "Vydavateľ", - "cert_expires": "Vyprší", - "cert_expired": "Vypršala platnosť", - "cert_fingerprint": "Odtlačok", - "cert_algorithm": "Algoritmus", - "import_to_smime": "Importovať do S/MIME", - "cert_already_imported": "Už importované do S/MIME", - "cert_imported": "Certifikát bol importovaný do úložiska S/MIME", - "cert_import_failed": "Import certifikátu zlyhal", - "no_contact_selected": "Vyberte kontakt na zobrazenie podrobností", - "compose_email": "Napísať e-mail", - "copy_email": "Kopírovať e-mail", - "copy_phone": "Kopírovať telefón", - "copy_url": "Kopírovať URL", - "copied": "Skopírované do schránky", - "copy_failed": "Kopírovanie do schránky zlyhalo", - "created": "Vytvorené", - "updated": "Posledná aktualizácia", - "timezone": "Časové pásmo", - "anniversary_birth": "Narodeniny", - "anniversary_death": "Úmrtie", - "anniversary_wedding": "Výročie svadby", - "anniversary_other": "Ostatné", - "personal_expertise": "Odbornosť", - "personal_hobby": "Koníčky", - "personal_interest": "Záujmy", - "personal_other": "Ostatné", - "gender": "Pohlavie", - "gender_masculine": "Muž", - "gender_feminine": "Žena", - "gender_other": "Iné", - "gender_none": "Neuplatňuje sa", - "gender_unknown": "Neznáme", - "calendar": "Kalendár", - "calendar_uri": "URL kalendára", - "scheduling_uri": "URL plánovania", - "freebusy_uri": "URL voľný/obsadený", - "section_contact": "Kontaktné údaje", - "section_work": "Práca", - "section_personal": "Osobné", - "email_default_label": "E-mail", - "phone_default_label": "Telefón", - "address_default_label": "Adresa", - "online_service_default_label": "Online", - "organization_label": "Organizácia", - "title_label": "Titul", - "role_label": "Rola", - "language_label": "Jazyk", - "related_default_label": "Súvisiace", - "more_actions": "Viac akcií", - "age_years": "{count, plural, one {1 rok} other {# rokov}}", - "years_since": "{count, plural, one {1 rok} other {# rokov}}" - }, - "activity": { - "recent_emails": "Nedávne e-maily", - "upcoming_events": "Nadchádzajúce udalosti", - "no_emails": "Žiadne nedávne e-maily", - "no_events": "Žiadne nadchádzajúce udalosti", - "no_subject": "(Bez predmetu)", - "no_title": "(Bez názvu)", - "load_failed": "Nepodarilo sa načítať", - "unknown_sender": "Neznámy odosielateľ", - "all_day": "Celý deň" - }, - "form": { - "create_title": "Nový kontakt", - "edit_title": "Upraviť kontakt", - "section_address_book": "Adresár", - "select_address_book": "Vyberte adresár...", - "section_identity": "Meno a identita", - "contact_type": "Typ kontaktu", - "type_person": "Osoba", - "type_organization": "Organizácia", - "section_work": "Práca a organizácia", - "prefix": "Titul", - "prefix_placeholder": "Dr., Pán, Pani", - "given_name": "Meno", - "middle_name": "Stredné meno", - "surname": "Priezvisko", - "suffix": "Prípona", - "suffix_placeholder": "Jr., Sr., Ph.D.", - "nickname": "Prezývka", - "nickname_placeholder": "Prezývka", - "email": "E-mail", - "email_placeholder": "email@example.com", - "phone": "Telefón", - "phone_placeholder": "+421 123 456 789", - "phone_type": "Typ", - "phone_voice": "Hlasový", - "phone_cell": "Mobilný", - "phone_fax": "Fax", - "phone_pager": "Pager", - "phone_video": "Video", - "phone_text": "SMS", - "organization": "Organizácia", - "organization_placeholder": "Názov spoločnosti", - "department": "Oddelenie", - "department_placeholder": "Oddelenie", - "job_title": "Pozícia", - "job_title_placeholder": "napr. Softvérový inžinier", - "role": "Rola", - "role_placeholder": "napr. Vedúci tímu", - "addresses": "Adresy", - "add_address": "Pridať adresu", - "street": "Ulica", - "city": "Mesto", - "region": "Štát / Kraj", - "postcode": "PSČ", - "country": "Krajina", - "online_services": "Online služby", - "add_online_service": "Pridať online službu", - "url_placeholder": "https://...", - "service_placeholder": "Služba", - "anniversaries": "Výročia", - "add_anniversary": "Pridať dátum", - "anniversary_birth": "Narodeniny", - "anniversary_wedding": "Výročie svadby", - "anniversary_death": "Úmrtie", - "anniversary_other": "Ostatné", - "personal_info": "Osobné informácie", - "add_personal_info": "Pridať záznam", - "personal_info_placeholder": "napr. Fotografia", - "personal_expertise": "Odbornosť", - "personal_hobby": "Koníčky", - "personal_interest": "Záujmy", - "personal_other": "Ostatné", - "level": "Úroveň", - "level_high": "Vysoká", - "level_medium": "Stredná", - "level_low": "Nízka", - "categories": "Kategórie", - "categories_placeholder": "napr. Rodina, Priatelia, Kolegovia", - "categories_hint": "Píšte na vyhľadanie alebo pridanie kategórií", - "category_add": "Pridať", - "note": "Poznámky", - "note_placeholder": "Pridať poznámku...", - "gender": "Pohlavie", - "gender_sex": "Biologické pohlavie", - "gender_male": "Muž", - "gender_female": "Žena", - "gender_other": "Iné", - "gender_none": "Neuplatňuje sa", - "gender_unknown": "Neznáme", - "gender_identity": "Rodová identita", - "gender_identity_placeholder": "Rodová identita...", - "calendar": "Kalendár", - "calendar_uri": "URL kalendára", - "scheduling_uri": "URL plánovania", - "freebusy_uri": "URL voľný/obsadený", - "context_work": "Pracovné", - "context_private": "Súkromné", - "add_email": "Pridať e-mail", - "add_phone": "Pridať telefón", - "save": "Uložiť", - "cancel": "Zrušiť", - "creating": "Vytváranie...", - "updating": "Aktualizovanie...", - "name_required": "Zadajte meno, priezvisko alebo organizáciu", - "email_invalid": "Zadajte platnú e-mailovú adresu", - "email_error_inline": "Neplatný formát e-mailovej adresy", - "save_failed": "Uloženie kontaktu zlyhalo", - "delete": "Odstrániť", - "upload_photo": "Nahrať fotku", - "remove_photo": "Odstrániť fotku", - "photo_hint": "JPG alebo PNG, max. 10 MB. Veľkosť sa upraví.", - "photo_too_large": "Obrázok je príliš veľký (max. 10 MB)", - "photo_invalid": "Neplatný formát obrázka", - "change_photo": "Zmeniť" - }, - "groups": { - "create": "Nová skupina", - "edit": "Upraviť skupinu", - "empty": "Žiadne skupiny", - "delete_confirm_title": "Odstrániť skupinu", - "delete_confirm": "Naozaj chcete odstrániť túto skupinu?", - "name_label": "Názov skupiny", - "name_placeholder": "napr. Tím, Rodina", - "name_required": "Názov skupiny je povinný", - "save_failed": "Uloženie skupiny zlyhalo", - "members_label": "Členovia", - "search_members": "Hľadať kontakty na pridanie...", - "no_members": "Táto skupina nemá žiadnych členov", - "member_count": "{count, plural, =0 {Žiadni členovia} one {1 člen} other {# členov}}", - "send_email": "Odoslať e-mail skupine", - "send_email_to": "Komu", - "send_email_cc": "Kópia", - "send_email_bcc": "Skrytá kópia", - "no_member_emails": "Táto skupina nemá žiadnych členov s e-mailovou adresou." - }, - "import": { - "title": "Importovať kontakty", - "drop_hint": "Kliknite na výber súboru vCard", - "file_types": "súbory .vcf alebo .vcard", - "no_contacts": "V súbore neboli nájdené žiadne kontakty", - "parse_error": "Spracovanie súboru vCard zlyhalo", - "found": "{count, plural, one {Nájdený 1 kontakt} other {Nájdených # kontaktov}}", - "duplicate": "Duplikát", - "select_all": "Vybrať všetko", - "deselect_all": "Zrušiť výber všetkého", - "selected": "{count, plural, one {1 vybraný} other {# vybraných}}", - "import_button": "Importovať", - "importing": "Importovanie...", - "success": "{count, plural, one {Importovaný 1 kontakt} other {Importovaných # kontaktov}}", - "failed": "Import zlyhal", - "close": "Zavrieť", - "file_too_large": "Súbor je príliš veľký (max. 5 MB)" - }, - "export": { - "title": "Exportovať kontakty", - "success": "{count, plural, one {Exportovaný 1 kontakt} other {Exportovaných # kontaktov}}" - }, - "bulk": { - "selected": "{count, plural, one {1 vybraný} other {# vybraných}}", - "select_all": "Vybrať všetko", - "delete": "Odstrániť", - "delete_confirm_title": "Odstrániť kontakty", - "delete_confirm": "Odstrániť {count, plural, one {1 kontakt} other {# kontaktov}}?", - "deleted": "{count, plural, one {Odstránený 1 kontakt} other {Odstránených # kontaktov}}", - "add_to_group": "Pridať do skupiny", - "choose_group": "Vyberte skupinu", - "adding_contacts": "Pridávanie {count, plural, one {1 kontaktu} other {# kontaktov}}", - "added_to_group": "Kontakty boli pridané do skupiny", - "export": "Exportovať", - "clear": "Zrušiť výber" - }, - "toast": { - "created": "Kontakt bol vytvorený", - "updated": "Kontakt bol aktualizovaný", - "deleted": "Kontakt bol odstránený", - "error_create": "Vytvorenie kontaktu zlyhalo", - "error_update": "Aktualizácia kontaktu zlyhala", - "error_delete": "Odstránenie kontaktu zlyhalo" - }, - "context_menu": { - "open": "Otvoriť", - "edit": "Upraviť", - "send_email": "Poslať e-mail", - "add_to_group": "Pridať do skupiny", - "export_vcard": "Exportovať ako vCard", - "delete": "Zmazať", - "call": "Zavolať", - "duplicate": "Duplikovať", - "print": "Vytlačiť" - }, - "filters": { - "toggle": "Filtre", - "select": "Vybrať", - "clear": "Vymazať", - "close": "Zavrieť", - "title": "Pokročilé filtre", - "organization": "Firma", - "organization_placeholder": "napr. Acme Corp", - "job_title": "Pozícia", - "job_title_placeholder": "napr. Dizajnér", - "location": "Lokalita", - "location_placeholder": "Mesto alebo krajina", - "email_domain": "E-mailová doména", - "email_domain_placeholder": "example.com", - "birthday_month": "Narodeniny v", - "any_month": "Ktorýkoľvek mesiac", - "has_email": "Má e-mail", - "has_phone": "Má telefón", - "has_photo": "Má fotku", - "sort_by": "Zoradiť podľa", - "sort_first_name": "Meno", - "sort_last_name": "Priezvisko" - } - }, - "calendar": { - "title": "Kalendár", - "back_to_email": "Späť na poštu", - "back_to_month": "Späť na mesiac", - "my_calendars": "Kalendáre", - "birthday_calendar": "Narodeniny", - "mini_calendar_change": "Kliknutím zmeníte mesiac", - "views": { - "month": "Mesiac", - "week": "Týždeň", - "day": "Deň", - "agenda": "Agenda", - "today": "Dnes", - "month_hint": "Mesiac (m)", - "week_hint": "Týždeň (w)", - "day_hint": "Deň (d)", - "agenda_hint": "Agenda (a)", - "tasks": "Úlohy", - "tasks_hint": "Úlohy (k)" - }, - "events": { - "create": "Vytvoriť udalosť", - "edit": "Upraviť udalosť", - "delete": "Odstrániť udalosť", - "details": "Podrobnosti udalosti", - "no_events": "Žiadne udalosti", - "all_day": "Celý deň", - "more": "+{count} ďalších", - "no_title": "(Bez názvu)", - "resize": "Zmeniť veľkosť udalosti", - "duplicate": "Duplikovať", - "today_header": "Dnes", - "tomorrow_header": "Zajtra", - "agenda_show_earlier": "Zobraziť skoršie udalosti", - "agenda_loading": "Načítava sa…", - "agenda_range_start": "Udalosti od {date}", - "agenda_range_end": "Udalosti do {date}", - "export_ics": "Exportovať ako .ics", - "copy_title": "Kopírovať názov", - "copy_link": "Kopírovať odkaz na stretnutie", - "new_event": "Nová udalosť", - "new_all_day_event": "Nová celodenná udalosť", - "new_task": "Nová úloha", - "go_to_today": "Prejsť na dnešok" - }, - "detail": { - "add_note": "Pridať poznámku...", - "save_note": "Uložiť", - "note_saved": "Poznámka pridaná", - "open_link": "Otvoriť odkaz", - "meeting_link": "Odkaz na stretnutie", - "tentative": "Nezáväzne", - "cancelled": "Zrušené", - "delete_confirm": "Odstrániť túto udalosť?" - }, - "form": { - "title": "Názov", - "description": "Popis", - "location": "Miesto", - "meeting_link": "Odkaz na stretnutie", - "start_date": "Dátum začiatku", - "end_date": "Dátum ukončenia", - "start_time": "Čas začiatku", - "end_time": "Čas ukončenia", - "all_day_event": "Celodenná udalosť", - "calendar_select": "Kalendár", - "save": "Uložiť", - "cancel": "Zrušiť", - "delete_confirm": "Naozaj chcete odstrániť túto udalosť?", - "color": "Farba" - }, - "participants": { - "availability": { - "title": "Dostupnosť", - "free": "Voľný", - "busy": "Zaneprázdnený", - "tentative": "Predbežne", - "unknown": "Nie je používateľom tohto servera", - "checking": "Overuje sa…" - }, - "title": "Účastníci", - "add": "Pridať účastníka", - "organizer": "Organizátor", - "attendee": "Účastník", - "accepted": "Prijaté", - "declined": "Odmietnuté", - "tentative": "Nezáväzne", - "needs_action": "Vyžaduje akciu", - "remove": "Odstrániť", - "edit": "Upraviť", - "email_placeholder": "Pridajte e-mailovú adresu alebo vyhľadajte kontakty", - "send_invitations": "Odoslať pozvánky účastníkom", - "status_summary": "{accepted} prijatých, {pending} čaká", - "invited_by": "Pozvánka od {name}", - "respond_below": "Odpovedzte pomocou tlačidiel nižšie", - "rsvp_label": "Vaša odpoveď", - "cancel_notification": "Účastníci budú upozornení na zrušenie", - "you_organizer": "Ste organizátorom", - "you_attendee": "Ste účastníkom", - "no_participants": "Žiadni účastníci", - "count": "{count, plural, one {# účastník} other {# účastníkov}}" - }, - "recurrence": { - "title": "Opakovanie", - "none": "Neopakuje sa", - "daily": "Denne", - "weekly": "Týždenne", - "monthly": "Mesačne", - "yearly": "Ročne", - "every_n_days": "Každých {count} dní", - "every_n_weeks": "Každých {count} týždňov", - "every_n_months": "Každých {count} mesiacov", - "until": "Do", - "occurrences": "{count} opakovaní", - "custom": "Vlastné…", - "edit_custom": "Upraviť vlastné opakovanie", - "every_n_years": "Každých {count} rokov", - "on_days": "v {days}", - "on_day_n": "v deň {day}", - "on_the_nth": "{nth} {day}", - "in_month": "v mesiaci {month}", - "nth_1": "prvý", - "nth_2": "druhý", - "nth_3": "tretí", - "nth_4": "štvrtý", - "nth_last": "posledný", - "editor_freq_day": "Deň", - "editor_freq_week": "Týždeň", - "editor_freq_month": "Mesiac", - "editor_freq_year": "Rok", - "editor_repeats_on": "Opakuje sa", - "editor_every": "Každých", - "editor_unit_days": "dní", - "editor_unit_weeks": "týždňov", - "editor_unit_months": "mesiacov", - "editor_unit_years": "rokov", - "editor_on_day": "v deň", - "editor_on_the": "v", - "editor_in": "v mesiaci", - "editor_ends": "Končí", - "editor_never": "Nikdy", - "editor_ends_on": "Dňa", - "editor_ends_after": "Po", - "editor_occurrences": "opakovaniach" - }, - "recurrence_scope": { - "edit_title": "Upraviť opakujúcu sa udalosť", - "delete_title": "Odstrániť opakujúcu sa udalosť", - "description": "Toto je opakujúca sa udalosť. Ktoré udalosti chcete upraviť?", - "this_event": "Iba túto udalosť", - "this_and_future": "Túto a všetky nasledujúce udalosti", - "all_events": "Všetky udalosti", - "cancel": "Zrušiť", - "save": "Uložiť", - "delete": "Odstrániť", - "rsvp_title": "Odpovedať na opakujúcu sa udalosť", - "rsvp_description": "Toto je opakujúca sa udalosť. Na ktoré udalosti sa vzťahuje vaša odpoveď?", - "respond": "Odpovedať" - }, - "alerts": { - "title": "Pripomenutia", - "none": "Žiadne pripomenutia", - "at_time": "V čase udalosti", - "minutes_before": "{count, plural, one {# minútu pred} other {# minút pred}}", - "hours_before": "{count, plural, one {# hodinu pred} other {# hodín pred}}", - "days_before": "{count, plural, one {# deň pred} other {# dní pred}}", - "weeks_before": "{count, plural, one {# týždeň pred} other {# týždňov pred}}", - "unit_minutes_before": "minút pred", - "unit_hours_before": "hodín pred", - "unit_days_before": "dní pred", - "unit_weeks_before": "týždňov pred", - "add": "Pridať pripomenutie", - "remove": "Odstrániť pripomenutie", - "amount": "Množstvo pripomenutia", - "unit": "Jednotka pripomenutia" - }, - "settings": { - "title": "Nastavenia kalendára", - "default_view": "Predvolené zobrazenie", - "week_starts_on": "Týždeň začína v", - "time_format": "Formát času", - "default_calendar": "Predvolený kalendár", - "default_reminder": "Predvolené pripomenutie", - "time_format_12h": "12-hodinový", - "time_format_24h": "24-hodinový", - "notifications_enabled": "Oznámenia o udalostiach", - "notifications_enabled_desc": "Zobrazovať upozornenia na nadchádzajúce udalosti v kalendári", - "notification_sound": "Zvuk oznámenia", - "notification_sound_desc": "Prehrať zvuk pre upozornenia kalendára", - "invitation_parsing": "Analyzovať e-mailové pozvánky", - "invitation_parsing_desc": "Rozpoznávať pozvánky kalendára v prílohách e-mailov a zobrazovať akcie kalendára", - "show_time_in_month_view": "Zobraziť čas v mesačnom zobrazení", - "show_time_in_month_view_desc": "Zobrazovať časy udalostí v mesačnom zobrazení kalendára", - "show_week_numbers": "Zobraziť čísla týždňov", - "show_week_numbers_desc": "Zobrazovať čísla týždňov v minikalendári", - "calendar_free_scroll": "Voľné posúvanie", - "calendar_free_scroll_desc": "Plynulo prechádzať mesiace, týždne a dni namiesto jedného obdobia naraz", - "enable_tasks": "Povoliť úlohy", - "enable_tasks_desc": "Zobrazovať zobrazenie úloh v kalendári", - "show_tasks_on_calendar": "Zobraziť úlohy v kalendári", - "show_tasks_on_calendar_desc": "Zobrazovať značky úloh v dennom a týždennom zobrazení kalendára", - "hover_preview": "Náhľad udalosti po prejdení", - "hover_preview_desc": "Zobrazovať vyskakovacie okno s podrobnosťami pri najetí myšou na udalosti v kalendári", - "hover_preview_instant": "Okamžite", - "hover_preview_delay_500ms": "Oneskorenie 0,5 sekundy", - "hover_preview_delay_1s": "Oneskorenie 1 sekunda", - "hover_preview_delay_2s": "Oneskorenie 2 sekundy", - "hover_preview_off": "Vypnuté", - "organizer_identity": "Organizovať pozvánky ako", - "organizer_identity_desc": "Adresa kalendára použitá ako organizátor, keď pozývate ľudí na udalosti.", - "organizer_identity_failed": "Nepodarilo sa zmeniť adresu organizátora", - "show_birthday_calendar": "Kalendár narodenín", - "show_birthday_calendar_desc": "Zobrazovať virtuálny kalendár s narodeninami z vašich kontaktov" - }, - "days": { - "monday": "Pondelok", - "tuesday": "Utorok", - "wednesday": "Streda", - "thursday": "Štvrtok", - "friday": "Piatok", - "saturday": "Sobota", - "sunday": "Nedeľa", - "mon": "Po", - "tue": "Ut", - "wed": "St", - "thu": "Št", - "fri": "Pi", - "sat": "So", - "sun": "Ne" - }, - "months": { - "jan": "jan", - "feb": "feb", - "mar": "mar", - "apr": "apr", - "may": "máj", - "jun": "jún", - "jul": "júl", - "aug": "aug", - "sep": "sep", - "oct": "okt", - "nov": "nov", - "dec": "dec", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Udalosť bola vytvorená", - "event_updated": "Udalosť bola aktualizovaná", - "event_deleted": "Udalosť bola odstránená", - "calendar_created": "Kalendár bol vytvorený", - "calendar_deleted": "Kalendár bol odstránený", - "event_move_error": "Presun udalosti zlyhal", - "event_resize_error": "Zmena veľkosti udalosti zlyhala", - "alert_title": "Nadchádzajúca udalosť", - "alert_now": "Začína teraz", - "alert_in_minutes": "Za {count} min", - "invitation_sent": "Pozvánky boli odoslané", - "invitations_denied_title": "Pozvánky nie je možné odoslať", - "invitations_denied": "Server odmietol odoslať pozvánky: {reason}. Uložiť udalosť bez ich odoslania?", - "save_without_invitations": "Uložiť bez pozvánok", - "rsvp_updated": "Odpoveď bola aktualizovaná", - "rsvp_error": "Aktualizácia odpovede zlyhala", - "event_duplicated": "Udalosť bola duplikovaná", - "event_error": "Uloženie udalosti zlyhalo", - "task_due": "Termín úlohy", - "event_exported": "Udalosť exportovaná", - "title_copied": "Názov skopírovaný", - "link_copied": "Odkaz skopírovaný" - }, - "status": { - "loading_calendars": "Načítavanie kalendárov...", - "loading_events": "Načítavanie udalostí..." - }, - "quick_create": { - "placeholder": "Názov novej udalosti", - "aria_label": "Rýchle vytvorenie udalosti" - }, - "nav_prev": "Predchádzajúci", - "nav_next": "Ďalší", - "nav_open_menu": "Otvoriť ponuku", - "import": { - "title": "Importovať kalendár", - "tab_file": "Súbor", - "tab_url": "URL", - "select_file": "Vyberte súbor .ics", - "drop_file": "alebo sem presuňte súbor", - "supported_formats": "Podporuje súbory iCalendar (.ics)", - "url_description": "Zadajte URL adresu externého zdroja iCalendar (.ics) na import udalostí.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Podporuje URL adresy iCalendar (.ics)", - "fetch": "Načítať", - "invalid_url": "Zadajte platnú URL adresu", - "url_fetch_failed": "Načítanie kalendára z URL adresy zlyhalo", - "parsing": "Analyzovanie súboru kalendára...", - "parsed_events": "Nájdených {count} udalostí", - "no_events": "V súbore neboli nájdené žiadne udalosti", - "select_all": "Vybrať všetko", - "deselect_all": "Zrušiť výber všetkého", - "target_calendar": "Importovať do kalendára", - "import_button": "Importovať vybrané", - "importing": "Importovanie udalostí...", - "success": "Úspešne importovaných {count} udalostí", - "error": "Import kalendára zlyhal", - "file_too_large": "Súbor prekračuje limit 10 MB", - "invalid_format": "Neplatný formát súboru kalendára" - }, - "webcal_action": { - "title": "Otvoriť odkaz kalendára", - "description": "Ako chcete použiť \"{name}\"?", - "import_title": "Jednorazovo importovať", - "import_description": "Načítať udalosti teraz a skopírovať ich do jedného z vašich kalendárov.", - "subscribe_title": "Odoberať", - "subscribe_description": "Automaticky synchronizovať tento kalendár ako samostatný kalendár.", - "cancel": "Zrušiť" - }, - "management": { - "title": "Správa kalendára", - "description": "Vytvárajte, premenúvajte a prispôsobujte si svoje kalendáre.", - "name": "Názov", - "name_placeholder": "Názov kalendára", - "color": "Farba", - "change_color": "Zmeniť farbu", - "random_color": "Nová náhodná farba", - "add_calendar": "Pridať kalendár", - "edit": "Upraviť", - "delete": "Odstrániť", - "save": "Uložiť", - "create": "Vytvoriť", - "cancel": "Zrušiť", - "default": "Predvolený", - "confirm_delete": "Odstrániť \"{name}\"? Všetky udalosti v tomto kalendári budú odstránené.", - "confirm_clear": "Vymazať všetky udalosti z \"{name}\"? Túto akciu nie je možné vrátiť.", - "set_default": "Nastaviť ako predvolený", - "default_updated": "Predvolený kalendár aktualizovaný", - "error_default": "Nepodarilo sa nastaviť predvolený kalendár", - "clear_events": "Vymazať udalosti", - "events_cleared": "Vymazaných {count} udalostí", - "error_clear": "Vymazanie udalostí kalendára zlyhalo", - "calendar_created": "Kalendár bol vytvorený", - "calendar_updated": "Kalendár bol aktualizovaný", - "calendar_deleted": "Kalendár bol odstránený", - "color_updated": "Farba kalendára bola aktualizovaná", - "error_create": "Vytvorenie kalendára zlyhalo", - "error_update": "Aktualizácia kalendára zlyhala", - "error_delete": "Odstránenie kalendára zlyhalo", - "caldav_url": "URL CalDAV", - "copy_url": "Kopírovať URL CalDAV", - "url_copied": "URL CalDAV skopírovaná do schránky", - "share": "Zdieľať kalendár", - "new_event_in_calendar": "Nová udalosť v tomto kalendári", - "kind": "Obsah", - "kind_events": "Udalosti", - "kind_tasks": "Úlohy", - "kind_both": "Udalosti a úlohy", - "kind_hint": "Určuje, ktoré aplikácie tento kalendár uvidia pri synchronizácii cez CalDAV. Neskôr to nemožno zmeniť." - }, - "subscription": { - "title": "Odber iCal", - "section_title": "Odbery iCal", - "description": "Odoberajte externý zdroj iCalendar. Udalosti budú automaticky synchronizované do samostatného kalendára.", - "url_label": "URL kalendára", - "url_placeholder": "https://example.com/calendar.ics alebo webcal://...", - "name_label": "Názov kalendára", - "name_placeholder": "napr. Štátne sviatky", - "color_label": "Farba", - "refresh_interval": "Interval obnovy", - "interval_15": "Každých 15 minút", - "interval_30": "Každých 30 minút", - "interval_60": "Každú hodinu", - "interval_360": "Každých 6 hodín", - "interval_1440": "Denne", - "subscribe": "Odoberať", - "subscribing": "Prihlasovanie na odber...", - "save": "Uložiť zmeny", - "saving": "Ukladanie...", - "edit": "Upraviť", - "edit_title": "Upraviť odber", - "updated": "Aktualizované \"{name}\"", - "update_error": "Aktualizácia odberu zlyhala", - "invalid_url": "Zadajte platnú URL adresu", - "success": "Odoberáte \"{name}\"", - "error": "Pridanie odberu zlyhalo", - "refresh": "Obnoviť teraz", - "refresh_success": "Odber bol obnovený", - "refresh_error": "Obnovenie odberu zlyhalo", - "unsubscribe": "Zrušiť odber", - "confirm_delete": "Zrušiť odber \"{name}\"? Kalendár a všetky jeho udalosti budú odstránené.", - "deleted": "Odber bol odstránený", - "delete_error": "Odstránenie odberu zlyhalo", - "last_refreshed": "Posledná aktualizácia: {time}" - }, - "tasks": { - "label": "Úlohy", - "no_tasks": "Žiadne úlohy", - "no_title": "(Bez názvu)", - "mark_complete": "Označiť ako dokončené", - "mark_incomplete": "Označiť ako nedokončené", - "filter_all": "Všetky", - "filter_pending": "Čakajúce", - "filter_completed": "Dokončené", - "filter_overdue": "Po termíne", - "show_completed": "Zobraziť dokončené", - "create": "Nová úloha", - "edit": "Upraviť úlohu", - "title_placeholder": "Názov úlohy", - "description_placeholder": "Pridať popis...", - "due_date": "Termín", - "include_time": "Zahrnúť čas", - "priority": "Priorita", - "priority_none": "Žiadna", - "priority_high": "Vysoká", - "priority_medium": "Stredná", - "priority_low": "Nízka", - "progress": "Stav", - "progress_needs_action": "Vyžaduje akciu", - "progress_in_process": "Prebieha", - "progress_completed": "Dokončené", - "progress_cancelled": "Zrušené", - "calendar": "Kalendár", - "alert": "Pripomenutie", - "alert_none": "Žiadne", - "alert_at_time": "V čase termínu", - "alert_5min": "5 minút pred", - "alert_15min": "15 minút pred", - "alert_30min": "30 minút pred", - "alert_1hr": "1 hodinu pred", - "alert_1day": "1 deň pred", - "delete": "Odstrániť", - "cancel": "Zrušiť", - "save": "Uložiť", - "quick_add_placeholder": "Pridať úlohu...", - "due_today": "Dnes", - "due_tomorrow": "Zajtra", - "overdue": "Po termíne" - } - }, - "calendar_event_notifications": { - "someone": "Niekto", - "invited": "{name} vás pozval(a) na „{title}“", - "updated": "{name} aktualizoval(a) „{title}“", - "cancelled": "{name} zrušil(a) „{title}“", - "untitled": "Udalosť bez názvu" - }, - "share_notifications": { - "someone": "Niekto", - "shared": "{name} s vami zdieľa {kind} „{object}“", - "changed": "{name} zmenil(a) váš prístup k {kind} „{object}“", - "revoked": "{name} odobral(a) váš prístup k {kind} „{object}“", - "object": { - "folder": "priečinok", - "calendar": "kalendár", - "address_book": "adresár", - "files": "priečinok súborov" - } - }, - "sharing": { - "title": "Zdieľať \"{name}\"", - "description": "Udeľte prístup ďalším používateľom alebo skupinám na tomto serveri. Zmeny sa prejavia okamžite.", - "no_shares": "Zatiaľ nikomu nezdieľané.", - "add_person": "Pridať osobu alebo skupinu", - "search_placeholder": "Hľadať podľa mena alebo e-mailu…", - "loading_principals": "Načítavanie používateľov…", - "no_principals": "Neboli nájdení žiadni ďalší používatelia ani skupiny.", - "no_match": "Žiadne výsledky.", - "remove": "Odstrániť prístup", - "group": "Skupina", - "share_added": "Prístup udelený", - "share_updated": "Prístup aktualizovaný", - "share_removed": "Prístup odstránený", - "share_failed": "Aktualizácia zdieľania zlyhala", - "preset": { - "freeBusy": "Iba voľný/obsadený", - "read": "Iba čítanie", - "readWrite": "Čítanie a zápis", - "manager": "Správca", - "custom": "Vlastné" - } - }, - "advanced_search": { - "title": "Pokročilé hľadanie", - "from": "Od", - "from_placeholder": "E-mail alebo meno odosielateľa", - "to": "Komu", - "to_placeholder": "E-mail alebo meno príjemcu", - "subject": "Predmet", - "subject_placeholder": "Predmet obsahuje...", - "body": "Telo správy", - "body_placeholder": "Telo správy obsahuje...", - "folder": "Priečinok", - "all_folders": "Všetky priečinky", - "has_attachment": "Prílohy", - "date_after": "Po", - "date_before": "Pred", - "size_min": "Väčšie ako (KB)", - "size_max": "Menšie ako (KB)", - "starred": "S hviezdičkou", - "unread": "Neprečítané", - "read": "Prečítané", - "yes": "Áno", - "no": "Nie", - "clear": "Vymazať", - "clear_all": "Vymazať všetko", - "filters_active": "{count} filter", - "filters_active_plural": "{count} filtre", - "toggle_filters": "Viac", - "search_hint": "Použite pokročilé filtre na presné vyhľadávanie", - "advanced_filters_tooltip": "Pokročilé filtre hľadania", - "results_found": "{count, plural, =0 {Neboli nájdené žiadne výsledky} one {# výsledok nájdený} other {# výsledkov nájdených}}", - "results_found_more": "{count}+ výsledkov nájdených", - "suggestions_recent": "Nedávne vyhľadávania", - "suggestions_people": "Ľudia", - "suggestions_from": "Hľadať e-maily od {address}", - "suggestions_to": "Hľadať e-maily pre {address}", - "suggestions_remove_recent": "Odstrániť z nedávnych vyhľadávaní" - }, - "welcome": { - "title": "Vitajte vo svojej poštovej schránke", - "tip_compose": "Stlačte c na napísanie nového e-mailu", - "tip_shortcuts": "Stlačte ? na zobrazenie všetkých klávesových skratiek", - "tip_sidebar": "Kalendár, Kontakty a Nastavenia nájdete v postrannom paneli", - "tip_settings": "Prispôsobte si aplikáciu v Nastaveniach", - "got_it": "Rozumiem", - "settings": "Nastavenia", - "dismiss": "Zavrieť", - "start_tour": "Spustiť sprievodcu" - }, - "demo_welcome": { - "title": "Vitajte v Bulwark Mail", - "description": "Preskúmajte plnohodnotného webového e-mailového klienta – priamo v prehliadači. Všetky dáta zostávajú na vašom zariadení.", - "feature_email": "Čítajte a píšte e-maily", - "feature_organize": "Štítky, hviezdičky a priečinky", - "feature_shortcuts": "Klávesové skratky", - "feature_privacy": "100% súkromné demo", - "hint": "Kliknite na ľubovoľný e-mail vľavo na začiatok alebo využite sprievodcu nižšie." - }, - "files": { - "office_edit": "Upraviť dokument", - "office_loading": "Otvára sa editor dokumentov…", - "office_error": "Editor dokumentov sa nepodarilo otvoriť.", - "office_close": "Zavrieť editor", - "open_in_new_tab": "Otvoriť v novej karte", - "title": "Súbory", - "search_placeholder": "Hľadať súbory...", - "empty_state_title": "Žiadne súbory", - "empty_state_description": "Nahrajte súbory alebo vytvorte priečinky na začiatok", - "upload": "Nahrať", - "upload_files": "Nahrať súbory", - "new_folder": "Nový priečinok", - "new_folder_name": "Názov priečinka", - "rename": "Premenovať", - "rename_title": "Premenovať", - "new_name": "Nový názov", - "name_forbidden_chars": "Server nepovoľuje v názvoch tieto znaky: {chars}", - "name_reserved": "Tento názov je na serveri vyhradený.", - "delete": "Odstrániť", - "delete_confirm_title": "Odstrániť položku", - "delete_confirm_message": "Naozaj chcete odstrániť \"{name}\"? Túto akciu nie je možné vrátiť.", - "download": "Stiahnuť", - "name": "Názov", - "size": "Veľkosť", - "modified": "Zmenené", - "type": "Typ", - "folder": "Priečinok", - "file": "Súbor", - "parent_directory": "Nadradený adresár", - "breadcrumb_root": "Domov", - "other_accounts": "Ostatné účty", - "no_accounts": "Žiadne pripojené účty.", - "drop_files_here": "Presuňte sem súbory alebo priečinky na ich nahranie", - "uploading": "Nahrávanie...", - "upload_success": "{count, plural, one {1 súbor nahraný} other {# súborov nahraných}}", - "upload_error": "Nahranie súboru zlyhalo", - "create_folder_success": "Priečinok bol vytvorený", - "create_folder_error": "Vytvorenie priečinka zlyhalo", - "delete_success": "Úspešne odstránené", - "delete_error": "Odstránenie zlyhalo", - "rename_success": "Úspešne premenované", - "rename_error": "Premenovanie zlyhalo", - "download_error": "Sťahovanie zlyhalo", - "not_available": "Úložisko súborov nie je na tomto serveri k dispozícii", - "cancel": "Zrušiť", - "create": "Vytvoriť", - "save": "Uložiť", - "no_results": "Hľadaniu nezodpovedajú žiadne súbory", - "batch_delete_confirm_message": "Naozaj chcete odstrániť {count, plural, one {1 položku} other {# položiek}}? Túto akciu nie je možné vrátiť.", - "batch_delete_success": "{count, plural, one {Odstránená 1 položka} other {Odstránených # položiek}}", - "grid_view": "Zobrazenie mriežky", - "list_view": "Zobrazenie zoznamu", - "details": "Podrobnosti", - "path": "Cesta", - "preview": "Náhľad", - "preview_error": "Náhľad sa nepodarilo načítať", - "cut": "Vystrihnúť", - "copy": "Kopírovať", - "paste": "Vložiť", - "move_success": "{count, plural, one {1 položka presunutá} other {# položiek presunutých}}", - "move_error": "Presun zlyhal", - "paste_success": "Úspešne vložené", - "paste_error": "Vloženie zlyhalo", - "new_text_file": "Nový textový súbor", - "file_name": "Názov súboru", - "retry": "Opakovať", - "refresh": "Obnoviť", - "toggle_favorite": "Prepnúť obľúbené", - "duplicate": "Duplikovať", - "duplicate_success": "Úspešne duplikované", - "duplicate_error": "Duplikovanie zlyhalo", - "share": "Zdieľať", - "shared": "Zdieľané", - "shared_with_me": "Zdieľané so mnou", - "shared_by": "Zdieľané od {name}", - "create_file_success": "Súbor bol vytvorený", - "create_file_error": "Vytvorenie súboru zlyhalo", - "favorites": "Obľúbené", - "recent": "Nedávne", - "properties": "Vlastnosti", - "open_folder": "Otvoriť priečinok", - "upload_folder": "Nahrať priečinok", - "file_too_large": "\"{name}\" prekračuje maximálnu veľkosť súboru ({max})", - "undo": "Späť", - "undo_success": "Akcia vrátená späť", - "undo_error": "Akciu nie je možné vrátiť", - "toolbar": "Akcie so súbormi", - "open_folder_tree": "Otvoriť strom priečinkov", - "file_list": "Súbory a priečinky", - "context_menu": "Akcie", - "settings_title": "Nastavenia súborov", - "settings_display": "Zobrazenie", - "settings_default_view": "Predvolené zobrazenie", - "settings_default_view_desc": "Vyberte rozloženie mriežky alebo zoznamu", - "settings_default_sort": "Predvolené triedenie", - "settings_default_sort_desc": "Vyberte predvolené triedenie súborov", - "settings_sort_direction": "Smer triedenia", - "settings_sort_direction_desc": "Vyberte vzostupné alebo zostupné poradie", - "settings_ascending": "Vzostupne", - "settings_descending": "Zostupne", - "settings_icons": "Ikony", - "settings_show_icons": "Zobrazovať ikony súborov", - "settings_show_icons_desc": "Zobrazovať ikony vedľa súborov a priečinkov", - "settings_colored_icons": "Farebné ikony", - "settings_colored_icons_desc": "Používať farebné ikony namiesto monochromatických", - "settings_show_thumbnails": "Zobrazovať miniatúry", - "settings_show_thumbnails_desc": "Zobrazovať náhľady obrázkov namiesto ikon pre obrázkové súbory", - "settings_behavior": "Správanie", - "settings_show_hidden": "Zobrazovať skryté súbory", - "settings_show_hidden_desc": "Zobrazovať súbory a priečinky začínajúce bodkou", - "settings_folder_layout": "Navigácia priečinkov", - "settings_folder_layout_desc": "Vyberte spôsob zobrazenia priečinkov: vložene so súbormi alebo v stromovej štruktúre v postrannom paneli", - "settings_folder_layout_inline": "Vložené", - "settings_folder_layout_sidebar": "Postranný panel", - "disabled_title": "Funkcia Súbory je zakázaná správcom", - "disabled_description": "Nahrávanie veľkých súborov cez WebDAV môže spôsobiť nestabilitu Stalwart/RocksDB. Táto funkcia sa neodporúča v produkčnom prostredí.", - "stability_warning": "Nahrávanie veľkých súborov môže spôsobiť nestabilitu servera. Používajte s opatrnosťou.", - "migration_title": "Aktualizácia vašich súborov…", - "migration_description": "Usporiadanie priečinkov a súborov do správnej štruktúry. Toto prebehne iba raz." - }, - "smime": { - "your_certificates": "Vaše certifikáty", - "your_certificates_desc": "Importujte a spravujte svoje certifikáty S/MIME na podpisovanie a šifrovanie e-mailov", - "recipient_certificates": "Certifikáty príjemcov", - "recipient_certificates_desc": "Verejné certifikáty na šifrovanie e-mailov odosielaných príjemcom", - "identity_bindings": "Väzby kľúčov na identity", - "identity_bindings_desc": "Viažte certifikáty S/MIME k e-mailovým identitám", - "defaults_title": "Predvolené", - "defaults_desc": "Nakonfigurujte predvolené správanie pre podpisovanie a šifrovanie", - "import_pkcs12": "Importovať PKCS#12 (.p12/.pfx)", - "import_public_cert": "Importovať certifikát", - "no_certificates": "Zatiaľ neboli importované žiadne certifikáty", - "no_recipient_certs": "Žiadne certifikáty príjemcov", - "expires": "Vyprší", - "expired": "Vypršala platnosť", - "bound_to": "Viazané k", - "no_key_bound": "Žiadne", - "lock": "Zamknúť kľúč", - "unlock": "Odomknúť kľúč", - "details": "Zobraziť podrobnosti", - "delete": "Odstrániť", - "encrypt_by_default": "Predvolene šifrovať", - "encrypt_by_default_desc": "Automaticky šifrovať e-maily, keď majú všetci príjemcovia certifikáty", - "remember_unlocked": "Pamätať si odomknuté kľúče", - "remember_unlocked_desc": "Ponechať kľúče odomknuté počas trvania tejto relácie prehliadača", - "sign_default_for": "Predvolene podpisovať pre", - "enter_p12_passphrase": "Zadajte heslo PKCS#12", - "p12_passphrase_desc": "Zadajte heslo chrániacie tento súbor certifikátu", - "enter_storage_passphrase": "Nastavte heslo úložiska", - "storage_passphrase_desc": "Vyberte heslo na ochranu tohto kľúča pri uložení v prehliadači", - "next": "Ďalší", - "import": "Importovať", - "unlock_key": "Odomknúť kľúč", - "unlock_key_desc": "Zadajte heslo úložiska na odomknutie tohto kľúča na podpisovanie alebo dešifrovanie", - "passphrase_placeholder": "Zadajte heslo", - "confirm_passphrase_placeholder": "Potvrďte heslo", - "passphrase_mismatch": "Heslá sa nezhodujú", - "cancel": "Zrušiť", - "processing": "Spracovanie…", - "close": "Zavrieť", - "certificate_details": "Podrobnosti o certifikáte", - "cert_subject": "Subjekt", - "cert_issuer": "Vydavateľ", - "cert_email": "E-mail", - "cert_serial": "Sériové číslo", - "cert_validity": "Platnosť", - "cert_fingerprint": "Odtlačok (SHA-256)", - "cert_algorithm": "Algoritmus", - "cert_capabilities": "Možnosti", - "cert_source": "Zdroj", - "cert_expired": "Platnosť tohto certifikátu vypršala", - "cert_not_yet_valid": "Tento certifikát ešte nie je platný", - "cap_sign": "Podpisovanie", - "cap_encrypt": "Šifrovanie", - "cap_none": "Žiadne", - "show_passphrase": "Zobraziť heslo", - "hide_passphrase": "Skryť heslo", - "sign_toggle": "Podpísať", - "encrypt_toggle": "Šifrovať", - "missing_recipient_certs": "Chýbajú certifikáty pre: {emails}", - "missing_sender_cert": "Žiadny certifikát viazaný k tejto identite", - "status_encrypted_ok": "Táto správa bola zašifrovaná", - "status_encrypted_no_key": "Táto správa je zašifrovaná, ale nebol nájdený zodpovedajúci kľúč", - "status_encrypted_failed": "Nepodarilo sa dešifrovať túto správu", - "status_signed_valid": "Podpis bol overený", - "status_signed_invalid": "Overenie podpisu zlyhalo", - "status_signed_expired_cert": "Podpísané certifikátom, ktorého platnosť vypršala", - "status_signed_self_signed": "Podpis je platný, ale certifikát je vydaný sám sebe (nedôveryhodný)", - "status_signed_mismatch": "Podpis je platný, ale podpisujúci sa nezhoduje s odosielateľom", - "status_unsupported": "Nepodporovaný formát S/MIME", - "auto_import_signer_certs": "Automaticky importovať certifikáty podpisujúcich", - "auto_import_signer_certs_desc": "Automaticky ukladať certifikáty z overených podpísaných e-mailov na budúce šifrovanie", - "export": "Exportovať", - "enter_export_passphrase": "Nastavte heslo pre export", - "export_passphrase_desc": "Vyberte heslo na ochranu exportovaného súboru PKCS#12", - "export_storage_desc": "Zadajte heslo úložiska na dešifrovanie kľúča na export", - "incorrect_passphrase": "Nesprávne heslo" - }, - "tour": { - "step_counter": "Krok {current} z {total}", - "skip": "Preskočiť sprievodcu", - "back": "Späť", - "next": "Ďalší", - "finish": "Dokončiť", - "take_a_tour": "Preskúmajte rozhranie v krátkom sprievodcovi", - "restart_title": "Úvodný sprievodca", - "restart_desc": "Prehrať sprievodcu rozhraním krok za krokom", - "restart_button": "Spustiť sprievodcu znova", - "show_on_new_devices_title": "Zobraziť na nových zariadeniach", - "show_on_new_devices_desc": "Prehrať uvítací banner a sprievodcu pri prvom prihlásení na novom zariadení", - "sidebar_title": "Vaše poštové schránky", - "sidebar_desc": "Toto je postranný panel so priečinkami. Kliknutím na ľubovoľnú schránku zobrazíte jej e-maily.", - "compose_title": "Napísať e-mail", - "compose_desc": "Kliknutím sem napíšete nový e-mail. Môžete pridávať príjemcov, prílohy a používať formátovaný text.", - "search_title": "Prehľadávať poštu", - "search_desc": "Vyhľadávajte podľa odosielateľa, predmetu alebo obsahu.", - "email_list_title": "Zoznam e-mailov", - "email_list_desc": "Tu sa zobrazujú e-maily. Kliknutím na jeden z nich si ho prečítate napravo.", - "email_viewer_title": "Panel čítania", - "email_viewer_desc": "Tu sa otvára vybraný e-mail. Pomocou tlačidiel na paneli nástrojov môžete odpovedať, preposlať, archivovať alebo odstrániť.", - "keywords_title": "Farebné štítky", - "keywords_desc": "Organizujte poštu pomocou farebných štítkov.", - "calendar_title": "Kalendár", - "calendar_desc": "Prepnite sa do kalendára na správu udalostí.", - "contacts_title": "Kontakty", - "contacts_desc": "Tu sa nachádza váš adresár. Importujte kontakty, vytvárajte skupiny.", - "settings_title": "Nastavenia", - "settings_desc": "Prispôsobte si všetko: tému, hustotu, podpisy, filtre, klávesové skratky a oveľa viac.", - "shortcuts_title": "Klávesové skratky", - "shortcuts_desc": "Kedykoľvek stlačte ? na zobrazenie všetkých dostupných skratiek.", - "compose_open_title": "Editor správ", - "compose_open_desc": "Toto je editor správ. Pridávajte príjemcov, píšte obsah, prikladajte súbory a formátujte text.", - "calendar_view_title": "Váš kalendár", - "calendar_view_desc": "Tu vidíte kalendár s ukážkovými udalosťami.", - "create_event_title": "Vytvoriť udalosť", - "create_event_desc": "Kliknutím na toto tlačidlo vytvoríte novú udalosť v kalendári.", - "event_modal_title": "Podrobnosti udalosti", - "event_modal_desc": "Toto je formulár udalosti. Vyplňte názov, vyberte dátum a čas, pridajte miesto alebo účastníkov.", - "contacts_list_title": "Vaše kontakty", - "contacts_list_desc": "Tu sú vaše kontakty. Kliknutím na ľubovoľný kontakt zobrazíte jeho úplné údaje na pravej strane.", - "settings_tabs_title": "Menu nastavení", - "settings_tabs_desc": "Tu nájdete všetky kategórie nastavení.", - "files_title": "Úložisko súborov", - "files_desc": "Prehliadač súborov umožňuje nahrávať, organizovať a zdieľať súbory.", - "demo_banner_title": "Ovládanie dema", - "demo_banner_desc": "Nachádzate sa v demo režime – všetko zostáva vo vašom prehliadači.", - "quota_title": "Využitie úložiska", - "quota_desc": "Tu môžete sledovať veľkosť svojej poštovej schránky." - }, - "unified_mailbox": { - "search_unavailable": "Vyhľadávanie nie je v zjednotenom zobrazení k dispozícii" - }, - "quote_header": { - "reply_line": "Dňa {date}, {from} napísal(a):", - "forwarded_separator": "---------- Preposlaná správa ----------", - "from_label": "Od", - "date_label": "Dátum", - "subject_label": "Predmet" - }, - "pwa_install": { - "title": "Nainštalovať {appName}", - "description": "Nainštalujte si našu aplikáciu pre rýchly prístup a podporu offline.", - "not_now": "Teraz nie", - "install": "Nainštalovať", - "dont_remind": "Viac mi to nepripomínať", - "dismiss_aria": "Zavrieť výzvu na inštaláciu" - }, - "deep_link": { - "message_not_found": "Táto správa už nie je dostupná.", - "thread_not_found": "Táto konverzácia už nie je dostupná.", - "folder_not_found": "Tento priečinok už nie je dostupný.", - "event_not_found": "Táto udalosť už nie je dostupná.", - "file_not_found": "Tento súbor už nie je dostupný.", - "account_unavailable": "Prihláste sa do tohto účtu, aby ste odkaz otvorili.", - "copy_message": "Kopírovať odkaz na správu", - "copy_conversation": "Kopírovať odkaz na konverzáciu", - "copy_event": "Kopírovať odkaz na udalosť", - "copied": "Odkaz skopírovaný", - "copy_failed": "Odkaz sa nepodarilo skopírovať" - }, - "plugins": { - "oauth_callback": { - "completing": "Dokončovanie autorizácie…", - "completing_message": "Počkajte, kým sa vytvorí spojenie.", - "received_title": "Autorizácia prijatá", - "received_message": "Môžete sa vrátiť na kartu, kde ste začali pripájanie.", - "failed_title": "Autorizácia zlyhala", - "denied": "Autorizácia bola zamietnutá.", - "failed_detail": "Autorizácia zlyhala: {error}", - "missing_params": "Chýba autorizačný kód alebo stav.", - "delivery_failed": "Výsledok autorizácie sa nepodarilo odovzdať karte aplikácie. Zavrite toto okno a skúste to znova." - } - }, - "global_search": { - "title": "Hľadať všade", - "placeholder": "Hľadať v pošte, kontaktoch, kalendári, súboroch...", - "scope_label": "Hľadať v", - "account_label": "Účet", - "scope_all": "Všetko", - "scope_mail": "Pošta", - "scope_contacts": "Kontakty", - "scope_calendar": "Kalendár", - "scope_files": "Súbory", - "all_accounts": "Všetky účty", - "show_all": "Zobraziť všetky výsledky", - "no_results": "Žiadne výsledky pre „{query}“", - "server_hint": "Celé slová prehľadávajú server; časti slov zodpovedajú už načítaným položkám", - "searching": "Hľadá sa...", - "account_error": "Nepodarilo sa prehľadať {account}", - "recent_searches": "Nedávne hľadania", - "recurring": "Opakovaná", - "no_subject": "(no subject)", - "empty_hint": "Hľadajte poštu, kontakty, udalosti a súbory vo všetkých účtoch. Skúste from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Plocho podľa dátumu", - "grouped": "Zoskupené", - "load_more": "Načítať ďalšie", - "preview_empty": "Vyberte výsledok pre náhľad", - "open_full": "Otvoriť", - "view_label": "Zobrazenie" - } -} +{ + "meta_description": "Minimalistický webmailový klient využívajúci protokol JMAP", + "login": { + "title": "Webmail", + "username_label": "E-mail", + "username_placeholder": "pouzivatel@example.com", + "password_label": "Heslo", + "password_placeholder": "Zadajte heslo", + "jmap_endpoint_label": "JMAP server", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Server musí povoliť požiadavky CORS z tejto domény.", + "jmap_server_label": "Server", + "jmap_server_auto_picked": "Server bol vybratý podľa domény vášho e-mailu.", + "sign_in": "Prihlásiť sa", + "signing_in": "Prihlasovanie...", + "loading": "Načítavanie...", + "reconnecting": "Spojenie prerušené. Pokus o opätovné pripojenie…", + "error": { + "invalid_credentials": "Neplatný e-mail alebo heslo. Skontrolujte prihlasovacie údaje a skúste znova.", + "invalid_token": "Server tento prístupový token neprijal. Skontrolujte, či je úplný a či nevypršal alebo nebol odvolaný.", + "connection_failed": "Nedá sa pripojiť k serveru. Skontrolujte internetové pripojenie a skúste znova.", + "cors_blocked": "Server je dostupný, ale blokuje požiadavky medzi doménami. Skontrolujte nastavenia CORS servera JMAP a povoľte túto doménu.", + "server_error": "Server je dočasne nedostupný. Skúste to neskôr.", + "generic": "Vyskytla sa neočakávaná chyba. Ak problém pretrváva, kontaktujte správcu.", + "totp_required": "Je vyžadovaný kód dvojfaktorového overenia. Zadajte kód nižšie.", + "totp_invalid": "Neplatný overovací kód. Skontrolujte svoju overovaciu aplikáciu a skúste znova.", + "oauth_discovery_failed": "SSO je povolené, ale poskytovateľa identity sa nepodarilo dosiahnuť. Skontrolujte konfiguráciu OAuth.", + "token_exchange_failed": "Heslo a kód boli prijaté, ale poštový server odmietol pre túto aplikáciu spustiť reláciu. Požiadajte správcu o kontrolu nastavení klienta OAuth." + }, + "show_password": "Zobraziť heslo", + "hide_password": "Skryť heslo", + "totp_toggle": "Mám kód 2FA", + "token_toggle": "Prihlásiť sa prístupovým tokenom", + "password_toggle": "Prihlásiť sa heslom", + "token_label": "Prístupový token", + "token_placeholder": "Vložte svoj API token", + "token_hint": "Vytvorte API token v nastaveniach svojho poskytovateľa e-mailu a vložte ho sem.", + "remember_me": "Zapamätať si ma", + "config_error": { + "title": "Chyba konfigurácie", + "fetch_failed": "Konfiguráciu aplikácie sa nepodarilo načítať. Skúste to neskôr.", + "server_not_configured": "Poštový server nebol nakonfigurovaný. Kontaktujte správcu." + }, + "remove_from_history": "Odstrániť z histórie", + "totp_label": "Overovací kód", + "totp_placeholder": "000000", + "session_expired": "Platnosť relácie vypršala. Prihláste sa znova.", + "dismiss": "Zavrieť", + "or": "alebo", + "sign_in_sso": "Prihlásiť sa cez SSO", + "add_account_title": "Pridať účet", + "add_account_subtitle": "Prihlásiť sa iným účtom", + "cancel": "Zrušiť", + "website": "Webová stránka", + "imprint": "Impressum", + "privacy_policy": "Zásady ochrany osobných údajov", + "try_demo": "Vyskúšať demo", + "demo_description": "Preskúmajte s ukážkovými dátami – bez potreby účtu", + "demo_launching": "Spúšťanie dema...", + "demo_login_button": "Spustiť demo", + "demo_tagline": "Vyskúšajte plnohodnotného e-mailového klienta. Účet nie je potrebný.", + "demo_no_signup": "Bez registrácie – voľne preskúmajte s ukážkovými dátami", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Dokončovanie prihlásenia...", + "oauth_error": { + "title": "Overenie zlyhalo", + "invalid_state": "Bezpečnostné overenie zlyhalo. Skúste sa prihlásiť znova.", + "missing_params": "Chýbajú autorizačné dáta. Skúste sa prihlásiť znova.", + "token_exchange_failed": "Overenie sa nepodarilo dokončiť. Skúste to znova.", + "access_denied": "Prístup bol zamietnutý. Kontaktujte správcu.", + "back_to_login": "Späť na prihlásenie" + } + }, + "sidebar": { + "unified_mailbox": "Zjednotená schránka", + "close": "Zavrieť", + "compose": "Napísať", + "compose_hint": "Napísať (c)", + "search_placeholder": "Hľadať poštu...", + "search_placeholder_hint": "Hľadať poštu... (stlačte /)", + "storage": "Úložisko", + "storage_used": "Použité", + "storage_free": "Voľné", + "storage_total": "Celkom", + "sign_out": "Odhlásiť sa", + "sign_out_of": "Odhlásiť sa z {account}", + "sign_out_all": "Odhlásiť sa zo všetkých účtov", + "add_account": "Pridať účet", + "set_as_default": "Nastaviť ako predvolený", + "switch_account": "Prepnúť účet", + "contacts": "Kontakty", + "calendar": "Kalendár", + "settings": "Nastavenia", + "admin": "Administrácia", + "files": "Súbory", + "loading_mailboxes": "Načítavanie schránok...", + "push_connected": "Aktualizácie v reálnom čase sú aktívne", + "push_disconnected": "Aktualizácie v reálnom čase sú neaktívne", + "keyboard_shortcuts": "Klávesové skratky", + "theme": { + "light": "Svetlý režim", + "dark": "Tmavý režim", + "system": "Systémová téma" + }, + "language": { + "title": "Jazyk" + }, + "mailboxes": { + "inbox": "Doručené", + "sent": "Odoslané", + "drafts": "Koncepty", + "trash": "Koš", + "archive": "Archív", + "starred": "Označené hviezdičkou", + "all_mail": "Všetka pošta", + "spam": "Spam", + "important": "Dôležité" + }, + "unified_inbox": "Zjednotená doručená pošta", + "unified_sent": "Všetky odoslané", + "unified_drafts": "Všetky koncepty", + "unified_trash": "Všetky koše", + "unified_archive": "Všetky archívy", + "unified_junk": "Všetok spam", + "all_accounts": "Všetky účty", + "all_accounts_loading": "Načítavajú sa účty: {loaded} z {total}", + "expand": "Rozbaliť", + "collapse": "Zbaliť", + "expand_tooltip": "Rozbaliť", + "collapse_tooltip": "Zbaliť", + "mobile": { + "search": "Hľadať", + "compose": "Napísať", + "go_back": "Späť", + "toggle_menu": "Prepnúť ponuku" + }, + "clear_search": "Vymazať hľadanie", + "vacation_active": "Automatická odpoveď je aktívna", + "demo_banner": "Demo režim", + "demo_reset": "Resetovať", + "demo_tour": "Sprievodca", + "tags": "Štítky", + "show_all_tags": "Zobraziť všetko ({count})", + "show_fewer_tags": "Zobraziť menej", + "folders": "Priečinky", + "your_folders": "Vaše priečinky", + "create_folder": "Vytvoriť priečinok", + "show_more": "Viac", + "show_fewer": "Menej", + "shared": "Zdieľané", + "shared_account_hidden": "Skryté z bočného panela. Znova zobrazíte v Nastavenia › Rozloženie.", + "notifications_muted": "Upozornenia stlmené", + "mail": "Pošta", + "nav_label": "Navigácia", + "add_app": "Aplikácie", + "scheduled": "Naplánované", + "unified_all_unread": "Všetky neprečítané", + "unified_all_starred": "Všetky s hviezdičkou", + "unified_all_mail": "Všetka pošta", + "remove_account": "Odstrániť účet", + "remove_account_confirm": "Odstrániť {account} z tohto zariadenia? Neskôr ho môžete znova pridať.", + "labels": "Štítky", + "create_label": "Vytvoriť nový štítok", + "mv_tasks": "Úlohy", + "mv_side_panel": "Bočný panel", + "mv_open_full": "Otvoriť v plnom zobrazení", + "mv_previous_day": "Predchádzajúci deň", + "mv_next_day": "Nasledujúci deň", + "mv_add_another_account": "Pridať ďalší účet", + "mv_manage_account": "Spravovať účet", + "mv_storage_used": "Využité {percent} % z {total}" + }, + "protocol_handlers": { + "title": "Predvolené aplikácie", + "description": "Vyberte, či sa e-mailové a kalendárové odkazy otvárajú v Bulwarku. Bulwark sa registruje ako obslužná aplikácia protokolu pre odkazy mailto: a webcal:.", + "unsupported": "Tento prehľadávač alebo pripojenie nepodporuje manuálnu registráciu obslužnej aplikácie protokolu.", + "mailto_label": "E-mailové odkazy", + "mailto_description": "Otvoriť odkazy mailto: v Bulwarku s predvyplneným editorom správy.", + "protocol_open_mode_label": "Pri otváraní odkazov protokolov", + "protocol_open_mode_description": "Vyberte, či má Bulwark otvárať odkazy mailto: a webcal: v novej karte alebo znovu použiť otvorenú reláciu.", + "protocol_open_mode_active_session": "Otvoriť v aktívnej relácii, ak je to možné", + "protocol_open_mode_new_tab": "Vždy otvoriť novú kartu", + "focus_notification_title": "Otvoriť Bulwark", + "focus_notification_body": "Odkaz bol otvorený v Bulwarku. Kliknutím prenesú okno do popredia.", + "webcal_label": "Kalendárové odkazy", + "webcal_description": "Otvoriť odkazy webcal: v Bulwarku s predvyplneným dialógom pre odber kalendára.", + "register_mailto": "Zaregistrovať e-mailovú aplikáciu", + "register_webcal": "Zaregistrovať kalendárovú aplikáciu", + "mailto_registered": "Registrácia obsluhy e-mailových odkazov bola požiadaná", + "webcal_registered": "Registrácia obsluhy kalendárových odkazov bola požiadaná", + "registration_failed": "Registrácia obslužnej aplikácie protokolu zlyhala", + "opening_mailto": "Otvára sa editor...", + "opening_webcal": "Otvára sa kalendár...", + "browser_note": "Prehľadávač alebo operáčný systém vás môže požiadať o potvrdenie.", + "select_account_title": "Vybrať účet", + "select_mailto_account": "Vyberte účet, v ktorom sa má tento e-mailový odkaz otvoriť.", + "select_webcal_account": "Vyberte účet, v ktorom sa má tento kalendárový odkaz otvoriť.", + "select_account_note": "Toto platí iba pre tento odkaz protokolu.", + "detail_to": "Komu", + "detail_subject": "Predmet", + "detail_no_subject": "Bez predmetu", + "detail_calendar": "Kalendár", + "detail_source": "Zdroj", + "active_account": "Aktívny", + "switching_account": "Prepínanie účtu..." + }, + "sidebar_apps": { + "modal_title": "Aplikácie postranného panela", + "add_new": "Pridať aplikáciu", + "edit_app": "Upraviť aplikáciu", + "name_label": "Názov", + "name_placeholder": "Moja aplikácia", + "name_required": "Názov je povinný", + "url_label": "URL", + "url_required": "URL je povinné", + "url_invalid": "Zadajte platnú adresu http alebo https", + "icon_label": "Ikona", + "icon_required": "Ikona je povinná", + "open_mode_label": "Režim otvárania", + "open_new_tab": "Nová karta", + "open_inline": "Vložené", + "cancel": "Zrušiť", + "add": "Pridať", + "update": "Aktualizovať", + "delete": "Odstrániť", + "delete_confirm_title": "Odstrániť aplikáciu", + "delete_confirm": "Naozaj chcete odstrániť \"{name}\"?", + "no_apps": "Zatiaľ neboli pridané žiadne aplikácie", + "no_apps_hint": "Pridajte vlastné aplikácie a odkazy do postranného panela", + "managed_title": "Poskytnuté správcom", + "managed_description": "Tieto aplikácie sú nastavené pre všetkých a nedajú sa zmeniť.", + "managed_badge": "Spravované", + "search_icons": "Hľadať ikony...", + "show_popular": "Obľúbené", + "show_all": "Všetky", + "no_icons_found": "Ikony sa nenašli", + "inline_badge": "Vložené", + "tab_badge": "Karta", + "show_on_mobile": "Zobraziť na mobile" + }, + "email_list": { + "columns": { + "from": "Od", + "to": "Komu", + "subject": "Predmet", + "date": "Dátum", + "size": "Veľkosť" + }, + "no_emails": "Neboli nájdené žiadne správy", + "no_emails_description": "Táto schránka je prázdna", + "no_search_results": "Neboli nájdené žiadne výsledky", + "no_search_results_description": "Skúste zmeniť hľadanie alebo filtre", + "loading": "Načítavanie e-mailov...", + "unread": "neprečítané", + "to_me": "Pre mňa", + "to_recipients": "Pre {count} príjemcov", + "and_others": "a {count} ďalších", + "draft": "Koncept", + "starred": "S hviezdičkou", + "pinned": "Pripnuté", + "replied": "Zodpovedané", + "forwarded": "Preposlané", + "has_attachment": "Obsahuje prílohu", + "conversations_range": "{from}–{to, number} z {total, number}", + "conversations_count": "{count} z {total} konverzácií", + "conversations_count_plus": "{count}+ konverzácií", + "conversations_count_simple": "{count} konverzácií", + "no_conversations": "Žiadne konverzácie", + "loading_more": "Načítavanie ďalších e-mailov...", + "no_more_emails": "Žiadne ďalšie e-maily na načítanie", + "batch_actions": { + "select": "Vybrať e-maily", + "select_all": "Vybrať všetko", + "selected_messages": "{count, plural, one {1 e-mail} other {# e-mailov}} vybraných", + "mark_read": "Označiť ako prečítané", + "mark_unread": "Označiť ako neprečítané", + "delete": "Odstrániť", + "delete_confirm_title": "Odstrániť e-maily", + "delete_confirm_message": "Naozaj chcete odstrániť {count, plural, one {1 e-mail} other {# e-mailov}}?", + "clear_selection": "Zrušiť výber" + }, + "permanent_delete": "Trvalo odstrániť", + "permanent_delete_confirm_title": "Trvalo odstrániť", + "permanent_delete_confirm_message": "Tento e-mail bude trvalo odstránený. Túto akciu nie je možné vrátiť.", + "permanent_delete_confirm_batch_message": "Týchto {count, plural, one {1 e-mail} other {# e-mailov}} bude trvalo odstránených. Túto akciu nie je možné vrátiť.", + "empty_folder": { + "button": "Vyprázdniť priečinku", + "confirm_title": "Vyprázdniť priečinku", + "confirm_message": "Všetky e-maily v tejto priečinke budú trvalo odstránené. Túto akciu nie je možné vrátiť.", + "confirm_message_trash": "Všetky e-maily v tomto priečinku budú presunuté do koša.", + "confirm_button": "Vyprázdniť priečinku", + "junk_hint": "Môžete vyprázdniť priečinku Spam a trvalo odstrániť všetky správy.", + "trash_hint": "Môžete vyprázdniť Koš a trvalo odstrániť všetky správy." + }, + "no_scheduled_emails": "Žiadne naplánované e-maily", + "no_scheduled_emails_description": "Správy naplánované na neskôr sa zobrazia tu.", + "scheduled_count": "{count, plural, one {1 naplánovaný e-mail} other {# naplánovaných e-mailov}}", + "scheduled_actions_hint": "Naplánované správy môžete zrušiť, prenaplánovať alebo upraviť.", + "cancel_scheduled_send": "Zrušiť odoslanie", + "reschedule_send": "Prenaplánovať", + "cancel_and_edit": "Zrušiť a upraviť", + "cancel_and_compose_again": "Zrušiť a napísať znova", + "reschedule_prompt": "Zadajte nový dátum/čas, napr. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Odosielateľ požiadal o potvrdenie o prečítaní:", + "send": "Odoslať potvrdenie", + "ignore": "Ignorovať", + "sent": "Potvrdenie o prečítaní bolo odoslané.", + "send_failed": "Potvrdenie o prečítaní sa nepodarilo odoslať", + "mdn_subject": "Prečítané: {subject}", + "mdn_body": "Toto je potvrdenie o prečítaní správy, ktorú ste odoslali na adresu {recipient}.\n\nPoznámka: Toto potvrdenie iba potvrdzuje, že správa bola zobrazená na počítači príjemcu. Nezaručuje, že príjemca obsah prečítal alebo mu porozumel." + }, + "no_email_selected": "Nie je vybraný žiadny e-mail", + "no_email_description": "Vyberte e-mail zo zoznamu na jeho zobrazenie", + "no_conversation_selected": "Nie je vybraná žiadna konverzácia", + "no_conversation_description": "Vyberte konverzáciu zo zoznamu na jej zobrazenie", + "compose": "Napísať", + "compose_hint": "Napísať novú správu", + "no_subject": "(Bez predmetu)", + "no_body_content": "(Obsah správy nie je dostupný)", + "no_preview_available": "Náhľad nie je dostupný", + "show_quoted_text": "Zobraziť citovaný text", + "hide_quoted_text": "Skryť citovaný text", + "loading_email": "Načítavanie e-mailu...", + "loading": "Načítavanie...", + "reply": "Odpovedať", + "reply_all": "Odpovedať všetkým", + "forward": "Preposlať", + "delete": "Odstrániť", + "archive": "Archivovať", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Označiť hviezdičkou", + "unstar": "Odstrániť hviezdičku", + "mark_unread": "Označiť ako neprečítané", + "mark_read": "Označiť ako prečítané", + "unread": "Neprečítané", + "read": "Prečítané", + "spam_short": "Spam", + "not_spam_short": "Nie je spam", + "move": "Presunúť", + "print": "Tlačiť", + "view_source": "Zobraziť zdrojový kód", + "fullscreen": "Celá obrazovka", + "exit_fullscreen": "Ukončiť režim celej obrazovky", + "view_in_dark_mode": "Zobraziť v tmavom režime", + "view_in_light_mode": "Zobraziť v svetlom režime", + "export_email": "Exportovať ako .eml", + "forward_as_attachment": "Preposlať ako prílohu", + "import_email": "Importovať .eml alebo .zip", + "keyboard_shortcuts": "Klávesové skratky (?)", + "email_source": "Zdrojový kód e-mailu", + "draft_banner": "Táto správa je koncept", + "edit_draft": "Upraviť", + "copy_source": "Kopírovať do schránky", + "source_copied": "Zdrojový kód bol skopírovaný do schránky", + "attachments": "Prílohy", + "important": "Dôležité", + "download": "Stiahnuť", + "download_all": "Stiahnuť všetko", + "from": "Od", + "to": "Komu", + "cc": "Kópia", + "bcc": "Skrytá kópia", + "date": "Dátum", + "subject": "Predmet", + "show_details": "Zobraziť podrobnosti", + "hide_details": "Skryť podrobnosti", + "external_content_warning": "Obrázky a externý obsah boli zablokované", + "load_external_content": "Načítať obrázky", + "trust_sender": "Vždy dôverovať tomuto odosielateľovi", + "sender_check": { + "unverified_label": "Neoverený odosielateľ", + "failed_label": "Kontrola odosielateľa zlyhala", + "unverified": "Táto správa tvrdí, že pochádza z domény {domain}, ale nič to nepotvrdzuje. Nemá platný podpis domény {domain} a neprišla zo servera, ktorý doména {domain} povoľuje.", + "unverified_sent_from": "Táto správa tvrdí, že pochádza z domény {domain}, ale bola odoslaná z domény {host} a nemá platný podpis domény {domain}.", + "failed": "Táto správa tvrdí, že pochádza z domény {domain}, ale neprešla kontrolami odosielateľa tejto domény. Môže byť podvrhnutá.", + "failed_sent_from": "Táto správa tvrdí, že pochádza z domény {domain}, ale bola odoslaná z domény {host} a neprešla kontrolami odosielateľa domény {domain}. Môže byť podvrhnutá.", + "caution": "Buďte opatrní pri odkazoch, prílohách a žiadostiach o heslá alebo platobné údaje." + }, + "back_to_list": "Späť na zoznam", + "view_contact": "Zobraziť kontakt", + "message_details": "Podrobnosti správy", + "more_reply_options": "Viac možností odpovede", + "set_tag": "Nastaviť štítok", + "tag": "Štítok", + "more_actions": "Viac akcií", + "previous": "Predchádzajúci", + "next": "Ďalší", + "move_to": "Presunúť do...", + "remove_tag": "Odstrániť štítok", + "tag_filter_placeholder": "Filtrovať štítky", + "tag_no_matches": "Žiadne zodpovedajúce štítky", + "more_count": "+{count} ďalších", + "characters_count": "{count} znakov", + "quick_reply_placeholder": "Napísať rýchlu odpoveď...", + "more_options": "Viac možností", + "sending": "Odosielanie...", + "security_authentication": "Zabezpečenie a overenie", + "technical_details": "Technické podrobnosti", + "message_id_label": "Message-ID:", + "reply_to_label": "Reply-To:", + "delivery_time_label": "Čas doručenia:", + "conversation_part_label": "Súčasť konverzácie:", + "previous_messages": "{count} predchádzajúca správa", + "previous_messages_plural": "{count} predchádzajúcich správ", + "time": { + "day": "deň", + "days": "dní", + "hour": "hodina", + "hours": "hodín", + "minute": "minúta", + "minutes": "minút" + }, + "unknown_sender": "Neznámy", + "recipient_me": "ja", + "recipient_and_others": "{name} a {count} ďalších", + "recipient_to_prefix": "Komu:", + "authentication": { + "title": "Overenie", + "status": { + "verified": "Overené", + "warning": "Upozornenie", + "none": "Neoverené" + }, + "spf": { + "pass": "SPF úspešné", + "fail": "SPF zlyhalo", + "none": "Bez SPF" + }, + "dkim": { + "pass": "DKIM platný", + "fail": "DKIM neplatný", + "none": "Bez DKIM" + }, + "dmarc": { + "pass": "DMARC úspešné", + "fail": "DMARC zlyhalo", + "none": "Bez DMARC" + }, + "spam_score": "Skóre spamu", + "tooltip_spf": "Sender Policy Framework: overuje, či je odosielajúci server oprávnený odosielať e-maily v mene domény", + "tooltip_dkim": "DomainKeys Identified Mail: kryptografickým podpisom potvrdzuje, že e-mail nebol počas prenosu zmenený", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: zabezpečuje súlad SPF a DKIM s doménou odosielateľa", + "policy": "Politika", + "result": { + "pass": "Úspešné", + "fail": "Zlyhalo", + "softfail": "Mierne zlyhanie", + "neutral": "Neutrálne", + "permerror": "Trvalá chyba", + "temperror": "Dočasná chyba", + "none": "Žiadne" + } + }, + "details": { + "recipients_routing": "Príjemcovia a smerovanie", + "authentication_security": "Overenie a zabezpečenie", + "identifiers_threading": "Identifikátory a vlákna", + "mailing_list": "Mailing list", + "message_properties": "Vlastnosti správy", + "sent": "Odoslané", + "received": "Prijaté", + "delivery_time": "Čas doručenia", + "in_reply_to": "V odpovedi na", + "references": "Odkazy", + "thread_id": "ID vlákna", + "size": "Veľkosť", + "mime_type": "Typ MIME", + "attachments_summary": "{count} súbory · {size}", + "list_id": "ID zoznamu", + "list_help": "Nápoveda zoznamu", + "list_post": "Príspevok do zoznamu", + "list_unsubscribe": "Odhlásiť odber", + "iprev": "Reverzné DNS", + "spam_status": "Stav spamu", + "ai_verdict": "Verdikt AI", + "account": "Účet", + "no_subject": "(bez predmetu)" + }, + "headers": { + "routing": "Smerovanie", + "received": "Prijaté", + "message_id": "ID správy", + "list_info": "Informácie o zozname" + }, + "tooltips": { + "reply": "Odpovedať (r)", + "reply_all": "Odpovedať všetkým (a)", + "forward": "Preposlať (f)", + "archive": "Archivovať (e)", + "delete": "Odstrániť (# alebo Del)", + "star": "Hviezdička (s)", + "unstar": "Odstrániť hviezdičku (s)", + "compose": "Napísať (c)", + "previous": "Predchádzajúci e-mail", + "next": "Ďalší e-mail", + "edit_draft": "Upraviť koncept" + }, + "spam": { + "button_title": "Nahlaš spam", + "not_spam_title": "Označiť ako legitimívne", + "toast_success": "Presunuté do Spamu", + "toast_batch": "{count} e-mailov presunutých do Spamu", + "toast_undo": "Späť", + "toast_not_spam_success": "Presunuté do Doručených", + "toast_not_spam_batch": "{count} e-mailov presunutých do Doručených", + "error": "Nahlasovanie spamu zlyhalo", + "error_not_spam": "Obnovenie e-mailu zlyhalo" + }, + "unsubscribe_banner": { + "label": "Newsletter", + "button": "Odhlásiť odber", + "confirm_title": "Odhlásiť odber od tohto odosielateľa?", + "confirm_button": "Potvrdiť", + "cancel": "Zrušiť", + "success_http": "Stránka pre odhlásenie bola otvorená v novej karte", + "success_mailto": "E-mail na odhlásenie bol odoslaný", + "confirm_message_http": "Stránka odhlásenia sa otvorí na novej karte.", + "confirm_message_mailto": "Odosielateľovi bude odoslaný e-mail na odhlásenie.", + "error": "Odber sa nedá odhlásiť", + "dismiss": "Zavrieť" + }, + "calendar_invitation": { + "loading": "Načítavanie podrobností udalosti…", + "title": "Pozvánka do kalendára", + "published_title": "Zverejnená udalosť", + "response_title": "Odpoveď na udalosť", + "update_title": "Aktualizácia udalosti", + "counter_title": "Protinavárh", + "refresh_title": "Požiadavka na obnovenie", + "declined_counter_title": "Protinavárh zamietnutý", + "cancelled_title": "Udalosť zrušená", + "organizer": "Organizátor: {name}", + "organizer_label": "Organizátor:", + "attendees": "{count, plural, one {# účastník} other {# účastníkov}}", + "accept": "Prijаť", + "maybe": "Možno", + "decline": "Odmietnuť", + "add_to_calendar": "Pridať do kalendára", + "added": "Pridané do kalendára", + "rsvp_sent": "Odpoveď odoslaná", + "parse_error": "Nedá sa prečítať pozvánka", + "action_failed": "Akciu kalendára sa nepodarilo dokončiť.", + "no_calendar": "Kalendár nie je dostupný", + "published_info": "Táto udalosť bola zdielená pre vašu informáciu.", + "response_info": "Táto správa obsahuje odpoveď účastníka.", + "response_info_organizer": "Táto odpoveď účastníka aktualizuje vašu udalosť.", + "update_info": "Táto správa aktualizuje existujúcu udalosť.", + "counter_info": "Táto správa navrhuje zmeny udalosti.", + "counter_info_organizer": "Tento účastník navrhol zmeny vašej udalosti.", + "refresh_info": "Táto správa vyžaduje najnovšie podrobnosti o udalosti.", + "refresh_info_organizer": "Účastník požiadal o najnovšie podrobnosti o udalosti.", + "declined_counter_info": "Organizátor zamietol protinavárh.", + "authentication_failed_info": "Overenie správ pre túto pozvánku zlyhalo. Zaobchádzajte s akciami kalendára opatrne.", + "authentication_missing_info": "Táto pozvánka neobsahuje overené overenie správ. Potvrdte podrobnosti s organizátorom.", + "sender_mismatch_info": "Táto pozvánka bola odoslaná od {sender}, zatiaľčo organizátor v dátach kalendára je {organizer}.", + "sender_mismatch_unverified_info": "Táto pozvánka bola odoslaná od {sender}, zatiaľčo organizátor v dátach kalendára je {organizer}, a správu sa nepodarilo overiť.", + "organizer_role": "Ste organizátorom tejto udalosti", + "your_response": "Vaša odpoveď: {status}", + "response_needed": "Vyžaduje odpoveď", + "response_accepted": "Prijaté", + "response_tentative": "Nezaväzne", + "response_declined": "Odmietnuté", + "response_delegated": "Delegované", + "actor_sent_info": "Odoslané od {name}.", + "actor_response_info": "{name} odpovedal(a) {status}.", + "actor_counter_info": "{name} navrhol(a) zmeny tejto udalosti.", + "actor_refresh_info": "{name} požiadal(a) o najnovšie podrobnosti o udalosti.", + "actor_declined_counter_info": "{name} zamietol(a) protinavárh.", + "actor_note": "Poznámka: {comment}", + "actor_unknown": "Niekto", + "proposed_changes": "Navrhované zmeny", + "change_title": "Názov", + "change_time": "Čas", + "change_location": "Miesto", + "change_description": "Popis", + "change_empty": "Žiadne", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Použiť navrhované zmeny", + "proposal_applied": "Navrhované zmeny boli použité.", + "review_proposal": "Skontrolovať návrh", + "review_request": "Skontrolovať požiadavku", + "view_in_calendar": "Zobraziť v kalendári", + "select_calendar": "Vybrať kalendár", + "already_in_calendar": "Už je vo vašom kalendári", + "request_info": "Boli ste pozvaní na túto udalosť. Odpovedzte, aby organizátor vedel o vašej dostupnosti.", + "cancel_info": "Organizátor zrušil túto udalosť.", + "event_updated": "Aktualizácia #{sequence}", + "event_status_tentative": "Nezaväzne", + "event_status_cancelled": "Zrušené", + "expand": "Zobraziť podrobnosti", + "collapse": "Skryť podrobnosti" + }, + "send": "Odoslať", + "more": "viac", + "scheduled_banner": "Naplánované na odoslanie {date}", + "scheduled_send_created": "E-mail bol naplánovaný na odoslanie", + "sending_in": "Odoslanie o {seconds} s", + "undo_send_failed": "Odoslanie sa nedá vrátiť. Správa už možno bola odoslaná.", + "cancel_scheduled_send": "Zrušiť odoslanie", + "reschedule_send": "Prenaplánovať", + "cancel_and_edit": "Zrušiť a upraviť", + "cancel_and_compose_again": "Zrušiť a napísať znova", + "reschedule_prompt": "Zadajte nový dátum/čas, napr. 2026-05-04T15:30", + "scheduled_actions_only": "Zobrazenie Naplánované podporuje iba akcie plánovania", + "undo_send_scheduled": "Správa je naplánovaná na odoslanie", + "undo_send": "Vrátiť odoslanie", + "contact_sidebar": { + "title": "Kontakt", + "close": "Zavrieť postranný panel", + "action_email": "E-mail", + "action_email_title": "Odoslať e-mail", + "action_copy": "Kopírovať", + "action_copy_title": "Kopírovať e-mail", + "action_edit_title": "Upraviť kontakt", + "section_emails": "E-maily", + "section_phones": "Telefóny", + "section_organizations": "Organizácie", + "section_addresses": "Adresy", + "section_notes": "Poznámky", + "not_in_contacts": "Nie je vo vašich kontaktoch", + "add_to_contacts": "Pridať do kontaktov", + "copied": "Skopírované!", + "copy_failed": "Kopírovanie zlyhalo", + "view_contact": "Zobraziť kontakt", + "view_details": "Zobraziť podrobnosti" + }, + "send_now": "Odoslať teraz", + "verification_code": { + "copy": "Kopírovať kód {code}", + "copied": "Kód skopírovaný", + "copy_failed": "Kód sa nepodarilo skopírovať" + }, + "mv_position": "{index} z {total}", + "plugin_panel_show": "Zobraziť panel doplnku", + "plugin_panel_hide": "Skryť panel doplnku" + }, + "email_composer": { + "select_from_directory": "Vybrať z adresára", + "minimize": "Minimalizovať", + "maximize": "Celá obrazovka", + "restore": "Obnoviť", + "new_message": "Nová správa", + "signature_edit_hint": "Dvojklikom upravíte podpis", + "reply": "Odpovedať", + "reply_all": "Odpovedať všetkým", + "forward": "Preposlať", + "reply_to": "Odpovedať", + "reply_all_to": "Odpovedať všetkým", + "forward_message": "Preposlať", + "from": "Od", + "to": "Komu", + "cc": "Kópia", + "bcc": "Skrytá kópia", + "subject": "Predmet", + "body_placeholder": "Napíšte správu...", + "send": "Odoslať", + "cancel": "Zrušiť", + "attach": "Priložiť", + "attach_photos": "Fotky a videá", + "attach_files": "Súbory", + "attach_from_files": "Priložiť zo Súborov", + "files_picker": { + "attach": "{count, plural, =0 {Priložiť} one {Priložiť 1 súbor} few {Priložiť # súbory} other {Priložiť # súborov}}", + "empty_folder": "Tento priečinok je prázdny", + "load_failed": "Vaše súbory sa nepodarilo načítať" + }, + "discard": "Zahoditť", + "discard_draft_title": "Zahoditť koncept?", + "discard_draft_confirm": "Máte neuložené zmeny. Chcete zahoditť tento koncept?", + "discard_draft_message": "Koncept sa odstráni.", + "saving": "Ukladánie...", + "sending": "Odosielanie...", + "add_link": "Pridať odkaz", + "link_url_prompt": "Zadajte URL", + "draft_saved": "Koncept bol uložený", + "save_failed": "Uloženie zlyhalo", + "to_placeholder": "E-mailové adresy príjemcov", + "cc_placeholder": "Príjemcovia kópie", + "bcc_placeholder": "Príjemcovia skrytej kópie", + "subject_placeholder": "Predmet", + "cc_label": "Kópia:", + "bcc_label": "Skrytá kópia:", + "subject_label": "Predmet:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 e-mail} few {# e-maily} other {# e-mailov}}", + "no_subject": "(Bez predmetu)", + "unknown_sender": "Neznámy", + "quote": { + "reply_header": "Dňa {date}, {sender} napísal(a):", + "forward_header": "---------- Preposlaná správa ----------", + "from": "Od: {sender}", + "date": "Dátum: {date}", + "subject": "Predmet: {subject}", + "to": "Komu: {recipients}" + }, + "remove_sub_address": "Odstrániť podadresu", + "from_override": { + "toggle_off": "Prepísať", + "toggle_on": "Zrušiť prepísanie", + "toggle_tooltip": "Upravte meno a adresu odosielateľa. Pošta sa stále odosiela cez vašu identitu.", + "name_label": "Meno odosielateľa", + "name_placeholder": "Meno", + "email_label": "E-mailová adresa odosielateľa", + "email_placeholder": "alias@example.com", + "envelope_notice": "Ak váš server túto adresu neprijme ako odosielateľa obálky, použije sa namiesto nej {identity} a príjemcovia ju uvidia v hlavičke Return-Path." + }, + "save_as_template": "Uložiť ako šablónu", + "attach_from_device": "Nahrať z tohto zariadenia", + "templates_menu": "Šablóny", + "insert_template": "Vložiť šablónu", + "options": "Možnosti", + "option_plain_text": "Obyčajný text", + "option_plain_text_hint": "Odoslať bez formátovania", + "option_read_receipt": "Vyžiadať potvrdenie o prečítaní", + "option_read_receipt_hint": "Požiadať príjemcu o potvrdenie otvorenia", + "option_dsn": "Oznámenie o doručení", + "option_dsn_hint": "Dostať správu, keď ju server doručí", + "option_require_tls": "Vyžadovať šifrované doručenie", + "option_require_tls_hint": "Doručiť len cez TLS, inak vrátiť", + "option_priority": "Priorita", + "option_priority_high": "Vysoká", + "option_priority_normal": "Normálna", + "option_priority_low": "Nízka", + "validation": { + "recipient_required": "Pridajte príjemcu na odoslanie", + "body_required": "Napíšte správu alebo priložte súbor", + "invalid_recipients": "Neodoslané - skontrolujte tieto adresy: {addresses}", + "attachments_uploading": "Prílohy sa stále nahrávajú - odošle sa hneď po ich dokončení", + "attachment_upload_failed": "Neodoslané - prílohu sa nepodarilo nahrať. Odstráňte ju a skúste to znova." + }, + "upload_progress": "Nahrávanie {uploaded} / {total}", + "upload_cancel": "Zrušiť nahrávanie", + "upload_failed": "Súbor {filename} sa nepodarilo nahrať", + "attachment_too_large": "„{name}“ je väčší, než server povoľuje ({max} na súbor)", + "attachments_total_too_large": "Prílohy by prekročili limit servera {max} na správu", + "drop_files": "Presuňte súbory sem na priloženie", + "show_less": "Zobraziť menej", + "send_failed": "Odoslanie e-mailu zlyhalo", + "send_timeout": "Server prestal odpovedať. Pred opätovným odoslaním skontrolujte priečinok Odoslané — správa už mohla odísť.", + "send_offline": "Zdá sa, že ste offline. Správa tu zostáva, odošlite ju znova, keď budete online.", + "formatting": "Formátovanie", + "send_recipients_rejected": "Neodoslané – server odmietol všetkých príjemcov.", + "send_plugin_failed": "Neodoslané – doplnok, ktorý zabezpečuje odoslanie, zlyhal alebo trval príliš dlho. Pred opätovným odoslaním skontrolujte priečinok Odoslané.", + "send_some_recipients_rejected": "Odoslané, ale nie týmto príjemcom – server ich odmietol.", + "continue_draft": "Pokračovať v koncepte", + "close_draft_title": "Uložiť alebo zahoditť koncept?", + "close_draft_message": "Máte neuložené zmeny. Chcete ich uložiť ako koncept alebo zahoditť?", + "smime_sign_on": "Podpisovanie S/MIME zapnuté", + "smime_sign_off": "Zapnúť podpisovanie S/MIME", + "smime_encrypt_on": "Šifrovanie S/MIME zapnuté", + "smime_encrypt_off": "Zapnúť šifrovanie S/MIME", + "smime_encrypt_unavailable": "Šifrovanie S/MIME nie je dostupné – chýbajú certifikáty príjemcov", + "smime_unlock_title": "Odomknúť kľúč S/MIME", + "smime_unlock_message": "Zadajte heslo na odomknutie kľúča podpisovania S/MIME.", + "smime_unlock_button": "Odomknúť", + "smime_passphrase_placeholder": "Heslo", + "forgot_attachment": { + "title": "Nezabudli ste na prílohu?", + "message": "Vaša správa spíaňuje \"{keyword}\", ale nie je priložený žiadny súbor. Odoslať aj tak?", + "send_anyway": "Odoslať aj tak", + "back": "Späť na úpravu" + }, + "empty_subject": { + "title": "Odoslať bez predmetu?", + "message": "Táto správa nemá predmet. Odoslať aj tak?", + "dont_ask_again": "Znova sa nepýtať", + "send_anyway": "Odoslať aj tak", + "back": "Späť na úpravu" + }, + "schedule_send": "Naplánovať odoslanie", + "schedule_send_description": "Vyberte, kedy má server túto správu odoslať.", + "schedule_preset": { + "tomorrow_morning": "Zajtra ráno", + "tomorrow_afternoon": "Zajtra popoludní", + "monday_morning": "V pondelok ráno" + }, + "schedule_send_required": "Vyberte dátum a čas.", + "schedule_send_invalid": "Zadajte platný dátum a čas.", + "schedule_send_future": "Vyberte budúci dátum a čas.", + "schedule_send_too_late": "Tento čas je neskôr, ako server povoľuje.", + "schedule_send_unsupported": "Naplánované odoslanie nie je pre tento účet podporované.", + "schedule_send_cleanup_warning": "Naplánované odoslanie bolo vytvorené, ale vyčistenie konceptu zlyhalo.", + "send_delay_unsupported": "Oneskorenie odoslania nie je pre tento účet podporované.", + "send_delay_unsupported_confirm": "Tento účet nepodporuje oneskorenie odoslania. Odoslať okamžite?", + "recipient_edit_email": "Upraviť e-mailovú adresu", + "recipient_edit_name": "Upraviť zobrazené meno", + "recipient_email_placeholder": "E-mailová adresa", + "recipient_name_placeholder": "Zobrazené meno", + "invalid_recipient": "Neplatná e-mailová adresa", + "autocomplete_search_server": "Hľadať na serveri", + "autocomplete_searching": "Hľadanie...", + "mention_recipients": "Príjemcovia", + "mention_card_not_recipient": "Nie je v poli Komu ani Kópia, takže túto správu nedostane.", + "mention_card_add": "Pridať do {field}", + "mention_card_remove": "Odstrániť zmienku", + "toolbar": { + "bold": "Tučné", + "italic": "Kurzíva", + "underline": "Podčiarknuté", + "strikethrough": "Prečiarknuté", + "text_color": "Farba textu", + "remove_color": "Odstrániť farbu", + "heading_1": "Nadpis 1", + "heading_2": "Nadpis 2", + "bullet_list": "Odrážkový zoznam", + "ordered_list": "Číslovaný zoznam", + "quote": "Citát", + "code_block": "Blok kódu", + "align_left": "Zarovnať doľava", + "align_center": "Na stred", + "align_right": "Zarovnať doprava", + "text_direction": "Smer textu (RTL/LTR)", + "link": "Odkaz", + "table": "Tabuľka", + "clear_formatting": "Vymazať formátovanie", + "undo": "Späť", + "redo": "Znova", + "add_row_above": "Pridať riadok nad", + "add_row_below": "Pridať riadok pod", + "add_column_before": "Pridať stĺpec pred", + "add_column_after": "Pridať stĺpec za", + "delete_row": "Odstrániť riadok", + "delete_column": "Odstrániť stĺpec", + "toggle_header_row": "Prepnúť riadok záhlavia", + "delete_table": "Odstrániť tabuľku", + "pick_size": "Vybrať veľkosť", + "font_size": "Veľkosť písma", + "font_size_default": "Predvolené", + "background_color": "Farba pozadia", + "remove_background_color": "Odstrániť farbu pozadia" + }, + "send_filing_warning": "Odoslané - ale následné upratovanie zlyhalo, môže zostať zastaraný koncept.", + "pop_out": "Otvoriť odpoveď v okne", + "send_guard_failed": "Neodoslané - doplnok, ktorý kontroluje správy pred odoslaním, zlyhal alebo neodpovedal." + }, + "confirm_dialog": { + "confirm": "Potvrdiť", + "cancel": "Zrušiť" + }, + "common": { + "loading": "Načítavanie...", + "error": "Chyba", + "success": "Úspešne", + "cancel": "Zrušiť", + "save": "Uložiť", + "delete": "Odstrániť", + "edit": "Upraviť", + "close": "Zavrieť", + "search": "Hľadať", + "refresh": "Obnoviť", + "settings": "Nastavenia", + "help": "Nápoveda", + "logout": "Odhlásiť sa", + "yes": "Áno", + "no": "Nie", + "unknown": "Neznámy", + "app_title": "Webmail", + "reconnecting": "Spojenie prerušené. Pokus o opätovné pripojenie…", + "rate_limited_title": "Overenie servera je dočasne obmedzené.", + "rate_limited_detail": "Bulwark pozastavil požiadavky na pozadí, aby zabránil zablokovaniu. Opätovný pokus za {seconds} s.", + "rate_limited_action_title": "Požiadavka pozastavená, aby sa zabránilo zablokovaniu.", + "rate_limited_action_detail": "Bulwark čaká na ukončenie doby blokovania servera. Skúste to znova za {seconds} s.", + "pull_to_refresh": "Potiahnutím obnovíte", + "release_to_refresh": "Uvoľnením obnovíte", + "refreshing": "Obnovuje sa…" + }, + "notifications": { + "email_sent": "E-mail bol úspešne odoslaný", + "email_deleted": "E-mail bol odstránený", + "email_archived": "E-mail bol archivovaný", + "emails_deleted": "{count, plural, one {# e-mail odstránený} few {# e-maily odstránené} other {# e-mailov odstránených}}", + "emails_archived": "{count, plural, one {# e-mail archivovaný} few {# e-maily archivované} other {# e-mailov archivovaných}}", + "error_archiving": "E-mail sa nepodarilo archivovať", + "error_updating": "E-mail sa nepodarilo aktualizovať", + "email_starred": "E-mail bol označený hviezdičkou", + "email_unstarred": "Hviezdička bola odstránená", + "email_marked_read": "E-mail bol označený ako prečítaný", + "email_marked_unread": "E-mail bol označený ako neprečítaný", + "copied_to_clipboard": "Skopírované do schránky", + "source_copied": "Zdrojový kód skopírovaný do schránky", + "error_sending": "Odoslanie e-mailu zlyhalo", + "error_deleting": "Odstránenie e-mailu zlyhalo", + "error_loading": "Načítanie e-mailov zlyhalo", + "new_email": "Nový e-mail", + "new_email_from": "Od {sender}", + "click_to_view": "Kliknutím zobrazíte", + "email_moved": "E-mail bol presunutý", + "emails_moved": "{count} e-mailov bolo presunutých", + "moved_to_mailbox": "Presunuté do {mailbox}", + "move_failed": "Presun zlyhal", + "removed_from_folder": "Odstránené z priečinka {mailbox}", + "remove_from_folder_failed": "Odstránenie z tohto priečinka zlyhalo", + "emails_copied": "{count} e-mailov bolo skopírovaných", + "copy_failed": "Kopírovanie zlyhalo", + "move_error": "E-maily sa nedá presunúť do vybratej priečinky", + "email_tagged": "E-mail bol označený štítkom", + "emails_tagged": "{count} e-mailov bolo označených štítkom", + "tag_failed": "Označovanie štítkom zlyhalo", + "identity_created": "Identita bola úspešne vytvorená", + "identity_updated": "Identita bola úspešne aktualizovaná", + "identity_deleted": "Identita bola odstránená", + "identity_set_primary": "Hlavná identita bola aktualizovaná", + "identity_create_failed": "Vytvorenie identity zlyhalo: {error}", + "identity_update_failed": "Aktualizácia identity zlyhala: {error}", + "identity_delete_failed": "Odstránenie identity zlyhalo: {error}", + "identity_unauthorized": "Nemáte oprávnenie odosielať z tejto e-mailovej adresy", + "identity_not_found": "Identita sa nenašla", + "vacation_saved": "Nastavenia neprítomnosti boli uložené", + "vacation_save_failed": "Uloženie nastavení neprítomnosti zlyhalo", + "filters_saved": "Filtre boli úspešne uložené", + "filters_save_failed": "Uloženie filtrov zlyhalo", + "filters_deleted": "Pravidlo filtra bolo odstránené", + "templates_exported": "Šablóny boli úspešne exportované", + "templates_imported": "{count, plural, one {# šablóna} other {# šablón}} importovaných", + "templates_import_errors": "Niektoré šablóny sa nepodarilo importovať", + "templates_import_empty": "V súbore neboli nájdené žiadne šablóny", + "export_email_error": "Export e-mailu zlyhal", + "import_email_success": "E-mail bol úspešne importovaný", + "import_email_error": "Import e-mailu zlyhal", + "rule_created": "Pravidlo bolo vytvorené", + "rule_merged": "Pridané do pravidla „{name}“", + "rule_already_covered": "Už to pokrýva pravidlo „{name}“", + "rule_apply_existing": "{count, plural, one {Použiť na # existujúcu správu} few {Použiť na # existujúce správy} many {Použiť na # existujúcej správy} other {Použiť na # existujúcich správ}}", + "rule_applied": "{count, plural, =0 {Žiadnu správu nebolo treba meniť} one {Pravidlo použité na # správu} few {Pravidlo použité na # správy} many {Pravidlo použité na # správy} other {Pravidlo použité na # správ}}", + "rule_apply_failed": "Pravidlo sa nepodarilo použiť na existujúce správy", + "rule_edit": "Upraviť pravidlo", + "rule_undo": "Späť", + "rule_undone": "Zmena pravidla bola vrátená", + "rule_undo_failed": "Zmenu pravidla sa nepodarilo vrátiť", + "rule_undo_conflict": "Vaše filtre sa medzitým zmenili. Vráťte zmenu v nastaveniach filtrov.", + "rule_folder_failed": "Priečinok sa nepodarilo vytvoriť", + "vacation_filters_save_failed": "Nastavenia neprítomnosti boli uložené, ale preposielanie a príjemcov odpovede sa nepodarilo uložiť" + }, + "date": { + "today": "Dnes", + "yesterday": "Včera", + "this_week": "Tento týždeň", + "last_week": "Minulý týždeň", + "this_month": "Tento mesiac", + "older": "Staršie", + "just_now": "Práve teraz", + "minutes_ago": "pred {count} minútou", + "minutes_ago_plural": "pred {count} minútami", + "hours_ago": "pred {count} hodinou", + "hours_ago_plural": "pred {count} hodinami", + "days_ago": "pred {count} dňom", + "days_ago_plural": "pred {count} dňami" + }, + "language": { + "title": "Jazyk", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Vybrať jazyk", + "switch_to_english": "Prepnúť na angličtinu", + "switch_to_french": "Prepnúť na frančuzštinu", + "switch_to_japanese": "Prepnúť na japončinu", + "switch_to_spanish": "Prepnúť na španielčinu", + "switch_to_italian": "Prepnúť na taliancinu", + "switch_to_german": "Prepnúť na nemčinu", + "switch_to_dutch": "Prepnúť na holštinu", + "switch_to_portuguese": "Prepnúť na portuguálčinu", + "switch_to_russian": "Prepnúť na ruštinu", + "switching": "Zmena jazyka...", + "switch": "Zmeniť jazyk", + "current": "Aktuálny jazyk", + "polish": "Polski", + "switch_to_polish": "Prepnúť na polštinu", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Prepnúť na perštinu", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Prepnúť na mongolčinu" + }, + "settings": { + "title": "Nastavenia", + "back_to_mail": "Späť na poštu", + "save_success": "Nastavenia boli úspešne uložené", + "import_success": "Nastavenia boli úspešne importované", + "import_error": "Import nastavení zlyhal", + "reset_confirm": "Naozaj chcete obnoviť všetky nastavenia na predvolené?", + "unsaved_changes": "Máte neuložené zmeny", + "discard_changes": "Zahoditť neuložené zmeny?", + "discard": "Zahoditť", + "keep_editing": "Pokračovať v úprave", + "search_placeholder": "Hľadať nastavenia", + "search_clear": "Vymazať hľadanie", + "search_no_results": "Žiadne zodpovedajúce nastavenia", + "tabs": { + "appearance": "Vzhľad", + "language": "Jazyk, región a čas", + "email": "Správanie e-mailov", + "composer": "Skladanie správ", + "privacy": "Súkromie a zabezpečenie", + "account": "Účet", + "identities": "Identity", + "vacation": "Neprítomnosť", + "advanced": "Pokročilé", + "calendar": "Kalendár", + "filters": "Filtre", + "templates": "Šablóny", + "folders": "Priečinky", + "keywords": "Štítky", + "security": "Zabezpečenie", + "files": "Súbory", + "contacts": "Kontakty", + "encryption": "Šifrovanie", + "protocol_handlers": "Predvolené aplikácie", + "sidebar_apps": "Aplikácie postranného panela", + "notifications": "Oznámenia", + "layout": "Rozloženie", + "themes": "Motívy", + "reading": "Čítanie", + "composing": "Pisanie", + "downloads": "Stiahnuté", + "content_senders": "Obsah a odosielatelia", + "about_data": "Info a dáta", + "debug": "Ladenie" + }, + "themes": { + "title": "Motívy", + "description": "Vyberte si z motívov nasadených vaším správcom a zo vstavaných predvolieb.", + "locked": "Výber motívu je uzamknutý správcom.", + "forced": "Motív „{name}“ je vynútený správcom a nemožno ho zmeniť", + "admin_theme": "Motív správcu", + "activated": "Motív aktivovaný", + "default_restored": "Predvolený motív bol obnovený", + "default_name": "Predvolený", + "default_badge": "Predvolený", + "admin_enforced": "Vynútené správcom" + }, + "tab_groups": { + "general": "Všeobecné", + "account": "Účet a identita", + "organization": "Organizácia pošty", + "apps": "Aplikácie", + "system": "Systém", + "appearance": "Vzhľad", + "mail": "Pošta", + "privacy": "Súkromie a zabezpečenie", + "advanced": "Pokročilé" + }, + "appearance": { + "message_list_order": { + "label": "Poradie zoznamu správ", + "description": "Ako sú správy v zozname zoradené. Zoradenie vykonáva server, takže platí pre celý priečinok, nie len pre načítané správy.", + "preset": { + "chronological": "Chronologicky (najnovšie prvé)", + "unread_first": "Neprečítané prvé", + "starred_first": "S hviezdičkou prvé", + "tagged_first": "So štítkom prvé", + "custom": "Vlastné" + }, + "tag_label": "Štítok", + "advanced_show": "Rozšírené: upraviť úrovne zoradenia", + "advanced_hide": "Skryť rozšírené", + "criterion_label": "Kritérium úrovne {n}", + "direction_label": "Smer úrovne {n}", + "criteria": { + "unread": "Neprečítané", + "starred": "S hviezdičkou", + "tag": "Štítok", + "receivedAt": "Dátum prijatia", + "sentAt": "Dátum odoslania", + "from": "Odosielateľ", + "subject": "Predmet", + "size": "Veľkosť" + }, + "direction": { + "unread_first": "Neprečítané prvé", + "read_first": "Prečítané prvé", + "starred_first": "S hviezdičkou prvé", + "unstarred_first": "Bez hviezdičky prvé", + "tagged_first": "So štítkom prvé", + "untagged_first": "Bez štítku prvé", + "newest_first": "Najnovšie prvé", + "oldest_first": "Najstaršie prvé", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "Najväčšie prvé", + "smallest_first": "Najmenšie prvé" + }, + "add_level": "Pridať úroveň", + "remove_level": "Odstrániť úroveň", + "move_up": "Posunúť nahor", + "move_down": "Posunúť nadol", + "tiebreak_note": "Až tri úrovne; ďalšie úrovne rozhodujú pri zhode. „Najnovšie prvé“ sa vždy uplatní ako posledné.", + "scope": { + "label": "Použiť na všetky priečinky", + "description": "Ak je vypnuté, poradie platí len pre Doručenú poštu; ostatné priečinky zostávajú chronologické." + }, + "unsupported_note": "Váš poštový server nepodporuje zoradenie podľa stavu prečítania, hviezdičiek ani štítkov. Tieto úrovne sa ignorujú a zoznam zostáva chronologický." + }, + "title": "Vzhľad", + "description": "Prispôsobte si vzhľad a správanie webmailu", + "theme": { + "label": "Téma", + "description": "Vyberte preferovanú farebnú schému", + "light": "Svetlá", + "dark": "Tmavá", + "system": "Systémová" + }, + "language": { + "label": "Jazyk", + "description": "Vyberte preferovaný jazyk" + }, + "font_size": { + "label": "Veľkosť písma", + "description": "Upravte veľkosť textu pre lepšiu čitateľnosť", + "small": "Malé", + "medium": "Stredné", + "large": "Veľké" + }, + "list_density": { + "label": "Hustota zobrazenia", + "description": "Ovládanie medzier v celom rozhraní", + "extra_compact": "Veľmi kompaktné", + "compact": "Kompaktné", + "regular": "Bežné", + "comfortable": "Pohodlné" + }, + "interface_layout": { + "label": "Rozloženie rozhrania", + "description": "Kde sa nachádza hľadanie, navigácia a panely nástrojov", + "default": "Predvolené", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Povoliť animácie", + "description": "Zobrazovať plynulé prechody a efekty" + }, + "toolbar_position": { + "label": "Pozícia panela nástrojov", + "description": "Kde zobraziť tlačidlá akcií správy (Odpovedať, Archivovať, Odstrániť atd.)", + "top": "Hore", + "below_subject": "Pod predmetom" + }, + "toolbar_labels": { + "label": "Zobraziť popisky panela nástrojov", + "description": "Zobrazovať textové popisky vedľa ikon na paneli nástrojov." + }, + "hide_account_switcher": { + "label": "Skryť prepínač účtov v postrannom paneli", + "description": "Skryje výber účtu v hornej časti postranného panela." + }, + "show_rail_account_list": { + "label": "Zobraziť avatáry účtov na navigačnej lište", + "description": "Zobrazovať jednotlivé ikony účtov v dolnej časti navigačnej lišty pre rýchle prepínanie." + }, + "unified_mailbox": { + "label": "Zjednotená schránka", + "description": "Zobrazovať zlúčené priečinky (Doručené, Odoslané atd.) zo všetkých pripojených účtov", + "cross_account": { + "label": "Naprieč všetkými účtami", + "description": "Zlúčiť zjednotenú schránku naprieč všetkými pripojenými účtami namiesto zotrvania v aktívnom účte." + }, + "include_group": { + "label": "Zahrnúť skupinové schránky", + "description": "Zahrnúť do zjednoteného zobrazenia aj zdieľané/skupinové schránky." + } + }, + "all_mail": { + "folders_label": "Priečinky vo Všetkej pošte", + "folders_description": "Vyberte, ktoré priečinky sa spoja do zobrazenia Všetka pošta.", + "account_hint": "Platí pre {account}.", + "no_folders": "Žiadne priečinky nie sú dostupné." + }, + "colorful_sidebar_icons": { + "label": "Farebné ikony postranného panela", + "description": "Ofarbí ikony priečinkov a štítkov podľa typu. Vypnite pre jednofarebný postranný panel." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Zobraziť celkový počet správ", + "description": "Zobraziť celkový počet správ vedľa priečinkov a štítkov spolu s počtom neprečítaných." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Zobraziť zdieľané poštové účty", + "description": "Zobraziť priečinky schránok zdieľaných s vami v bočnom paneli pod Zdieľané. Vypnutím skryjete všetky, alebo nižšie skryte jednotlivé účty. Ich kalendáre a kontakty zostanú." + }, + "pro_interface": { + "label": "Pro rozhranie (experimentálne)", + "description": "Rozloženie pre pokročilých používateľov iba pre stolné počítače.", + "open_label": "Otvoriť Pro rozhranie", + "back_to_standard": "Späť na štandardné" + }, + "cross_unread": { + "label": "Všetky účty: Neprečítané", + "description": "Zobraziť položku v sekcii Všetky účty so zoznamom neprečítanej pošty zo všetkých účtov." + }, + "cross_starred": { + "label": "Všetky účty: S hviezdičkou", + "description": "Zobraziť položku v sekcii Všetky účty so zoznamom pošty s hviezdičkou zo všetkých účtov." + }, + "cross_all": { + "label": "Všetky účty: Všetka pošta", + "description": "Zobraziť položku v sekcii Všetky účty so všetkou poštou zo všetkých účtov." + } + }, + "keywords": { + "title": "E-mailové štítky", + "description": "Definujte štítky na organizáciu e-mailov s farbami.", + "add_keyword": "Pridať štítok", + "label_field": "Zobrazovaný názov", + "label_placeholder": "napr. Práca, Osobné, Naliehavé", + "id_field": "ID štítku", + "id_placeholder": "napr. praca, osobne", + "color_field": "Farba", + "id_exists": "Toto ID štítku už existuje", + "edit": "Upraviť štítok", + "delete": "Odstrániť štítok", + "save": "Uložiť", + "add": "Pridať", + "cancel": "Zrušiť", + "migrating": "Aktualizácia štítku v existujúcich e-mailoch…", + "migration_error": "Nepodarilo sa aktualizovať štítok v existujúcich e-mailoch", + "filters_migration_error": "Niektoré pravidlá filtrov stále používajú starý štítok – upravte ich ručne", + "nesting": { + "label": "Vnorené štítky", + "description": "Vnorujte štítky pod iné štítky a zobrazujte ich v bočnom paneli ako strom." + }, + "parent_field": "Nadradený štítok", + "no_parent": "Bez nadradeného štítku", + "too_long": "Táto cesta štítku je príliš dlhá (najviac {max} znakov)", + "has_children_locked": "Pod týmto štítkom sú vnorené ďalšie štítky, preto sú jeho názov a nadradený štítok uzamknuté. Najprv ich presuňte alebo odstráňte.", + "has_children_delete": "Najprv odstráňte štítky vnorené pod týmto", + "visibility_field": "Viditeľnosť v bočnom paneli", + "visibility": { + "show": "Zobraziť", + "unread": "Zobraziť pri neprečítaných", + "hide": "Skryť" + }, + "unrecognized": { + "title": "Obnoviť štítky zo servera", + "description": "Názvy a farby štítkov sú uložené v tomto prehliadači, ale samotné štítky sú na vašich správach ako kľúčové slová JMAP. Prehľadajte poštu na kľúčové slová, ku ktorým tu nie je štítok, a pridajte ich späť.", + "scan": "Vyhľadať štítky", + "rescan": "Hľadať znova", + "scanning": "Prehľadávanie… {scanned} z {total} správ", + "none": "Všetky štítky na vašich správach sú tu už definované.", + "messages": "{count, plural, one {# správa} few {# správy} other {# správ}}", + "add_all": "Pridať všetkých {count}", + "partial": "Prehľadaných bolo iba {scanned} najnovších správ z {total}. Štítky použité len na staršej pošte môžu chýbať.", + "error": "Poštu sa nepodarilo prehľadať na štítky." + } + }, + "notifications": { + "test_sound": "Otestovať zvuk oznámenia", + "sounds": { + "default": "Predvolený (Pípnutie)", + "cheerful": "Veselý", + "involved": "Dynamický", + "swift": "Rýchly", + "relax": "Relaxačný" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Zvuk oznámenia", + "description": "Vyberte zvuk, ktorý sa prehrá pri oznámení", + "choose": "Zvuk", + "choose_desc": "Vyberte tón oznámenia a kliknutím na ikonu reproduktora si ho vypočujte" + }, + "email": { + "title": "E-mailové oznámenia", + "description": "Nakonfigurujte oznámenia pre prichádzajúce e-maily", + "enabled": "E-mailové oznámenia", + "enabled_desc": "Zobrazovať oznámenia pri príchode nových e-mailov", + "sound": "Zvuk oznámenia", + "sound_desc": "Prehrať zvukové upozornenie pri príchode nových e-mailov", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Oznámenia kalendára", + "description": "Nakonfigurujte oznámenia pre udalosti kalendára", + "enabled": "Oznámenia o udalostiach", + "enabled_desc": "Zobrazovať upozornenia na nadchádzajúce udalosti v kalendári", + "sound": "Zvuk oznámenia", + "sound_desc": "Prehrať zvukové upozornenie pre pripomenutia kalendára", + "invitation_parsing": "Rozpoznávať e-mailové pozvánky", + "invitation_parsing_desc": "Rozpoznávať pozvánky kalendára v prílohách e-mailov a zobrazovať akcie kalendára" + }, + "push": { + "title": "Oznámenia na pozadí", + "description": "Prijímať systémové oznámenia o novej pošte, keď je táto stránka zatvorená. Doručované cez push relay Bulwark.", + "relay_label": "Push relay", + "relay_desc": "Predvolený je hostovaný relay Bulwark. Zmeňte iba ak používate vlastný hosting.", + "relay_locked": "Nastavené administrátorom", + "relay_locked_desc": "URL push relay bola nastavená administrátorom a nedá sa zmeniť.", + "status_active": "Aktívne na tomto zariadení", + "status_inactive": "Nie je povolené na tomto zariadení", + "status_unsupported": "Tento prehliadač nepodporuje Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "Pracujem…", + "enable": "Povoliť", + "reenable": "Znovu zaregistrovať", + "disable": "Zakázať", + "confirm_disable_title": "Zakázať oznámenia na pozadí?", + "confirm_disable_message": "Toto zariadenie prestane prijímať upozornenia, keď je stránka zatvorená.", + "dismiss_aria": "Zavrieť výzvu na oznámenia", + "ios_hint": "Na iOS najprv nainštalujte stránku na domovskú obrazovku – Safari doručuje Web Push iba nainštalovaným PWA.", + "devices_title": "Registrované zariadenia", + "devices_desc": "Všetky zariadenia registrované na príjem oznámení na pozadí pre tento účet. Odvolanie okamžite zastaví ich príjem.", + "devices_loading": "Načítavajú sa registrované zariadenia…", + "devices_empty": "Pre oznámenia na pozadí nie je registrované žiadne zariadenie.", + "devices_error": "Registrované zariadenia sa nepodarilo načítať.", + "devices_refresh": "Obnoviť", + "device_this": "Toto zariadenie", + "device_other": "Iné zariadenie", + "device_status_active": "Doručuje oznámenia", + "device_status_inactive": "Nikdy nedoručilo oznámenie", + "device_status_unknown": "Tomuto relay nie je známe", + "device_expires": "Vyprší {date}", + "revoke": "Odvolať", + "confirm_revoke_title": "Odvolať toto zariadenie?", + "confirm_revoke_message": "Okamžite prestane prijímať oznámenia na pozadí. Opätovné zapnutie push na tom zariadení ho zaregistruje nanovo.", + "confirm_revoke_message_this": "Toto zariadenie okamžite prestane prijímať oznámenia na pozadí." + } + }, + "language_region": { + "title": "Jazyk, región a čas", + "description": "Jazyk, formát dátumu, formát času a ďalšie regionálne predvoľby", + "language": { + "label": "Jazyk", + "description": "Vyberte preferovaný jazyk", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Formát dátumu", + "description": "Ako sa zobrazujú dátumy v zozname e-mailov", + "smart": "Inteligentný", + "relative": "Relatívny", + "full": "Vždy úplný dátum", + "preview_today": "Dnes:", + "preview_this_week": "Tento týždeň:", + "preview_older": "Staršie:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Formát času", + "description": "Vyberte 12-hodinový alebo 24-hodinový formát času", + "12h": "12-hodinový", + "24h": "24-hodinový" + }, + "first_day": { + "label": "Prvý deň v týždni", + "description": "Začiatok týždňa v nedeľu alebo v pondelok", + "sunday": "Nedeľa", + "monday": "Pondelok" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Vymazať hľadanie pri prepnutí priečinka", + "description": "Kliknutie na priečinok (Doručená pošta, Odoslaná…) vymaže aktívne hľadanie a zobrazí priečinok. Keď je vypnuté, hľadanie zostáva a spustí sa znova vo zvolenom priečinku." + }, + "title": "Správanie e-mailov", + "description": "Nakonfigurujte spôsob spracovania e-mailov", + "request_read_receipt": { + "label": "Predvolene žiadať potvrdenie o prečítaní", + "description": "Pred-povoliť žiadosť o potvrdenie o prečítaní pri písaní novej správy." + }, + "read_receipt_response": { + "label": "Reagovať na žiadosti o potvrdenie o prečítaní", + "description": "Čo robiť, keď prichádzajúca správa žiada o potvrdenie o prečítaní.", + "ask": "Vždy sa opýtať", + "always": "Vždy odoslať", + "never": "Nikdy neodosielať" + }, + "mark_read": { + "label": "Označiť ako prečítané", + "description": "Kedy označiť e-mail ako prečítaný po jeho otvorení", + "instant": "Okamžite", + "delay_3s": "Po 3 sekundách", + "delay_5s": "Po 5 sekundách", + "never": "Nikdy" + }, + "delete_action": { + "label": "Akcia odstránenia", + "description": "Čo sa stane po odstránení e-mailu", + "trash": "Presunúť do Koša", + "trash_and_read": "Presunúť do Koša a označiť ako prečítané", + "permanent": "Trvalo odstrániť", + "warning": "E-maily budú trvalo odstránené a nedajú sa obnoviť." + }, + "message_spacing": { + "label": "Odsadenie správy", + "description": "Odsadenie okolo tela správy pri čítaní", + "auto": "Automaticky (odporúčané)", + "always": "Vždy pridať odsadenie", + "edge": "Od okraja po okraj" + }, + "plain_text_font": { + "label": "Písmo obyčajného textu", + "description": "Písmo použité pre e-maily v obyčajnom texte", + "mono": "Neproporcionálne", + "sans": "Normálne (rovnaké ako aplikácia)" + }, + "archive_mode": { + "label": "Archivovať do", + "description": "Ako organizovať e-maily pri archivácii", + "single": "Jeden priečinok", + "year": "Priečinok pre každý rok", + "month": "Priečinok pre každý mesiac", + "reorganize": "Reorganizovať existujúci archív", + "reorganize_success": "{count, plural, =0 {Žiadne e-maily na reorganizáciu} =1 {1 e-mail reorganizovaný} other {# e-mailov reorganizovaných}}", + "reorganize_error": "Nepodarilo sa reorganizovať archív" + }, + "permanently_delete_junk": { + "label": "Trvalo odstraňovať spam", + "description": "Trvalo odstraňovať správy zo priečinka Spam namiesto presunutia do Koša" + }, + "confirm_bulk_delete": { + "label": "Potvrdiť pred odstránením viacerých správ", + "description": "Opýtať sa pred presunutím viacerých vybraných správ do koša. Trvalé odstránenie sa pýta vždy." + }, + "mail_layout": { + "label": "Rozloženie pošty", + "description": "Vyberte si medzi klasickým rozdeleným zobrazením a sústredením zoznamom.", + "split": "Rozdelené zobrazenie", + "split_description": "Zoznam správ a panel na čítanie zostávajú viditeľné vedľa seba.", + "focus": "Sústredený zoznam", + "focus_description": "Zobraziť jeden riadok na správu a otvoriť poštu na plnú šírku.", + "horizontal": "Panel na čítanie dole", + "horizontal_description": "Zobraziť zoznam správ hore a otvoriť vybranú správu v paneli na čítanie pod ním." + }, + "list_columns": { + "label": "Zobraziť zoznam v stĺpcoch", + "description": "Zobrazí jednoriadkové riadky ako tabuľku so stĺpcami Od, Predmet, Dátum a Veľkosť pod hlavičkou." + }, + "show_preview": { + "label": "Zobraziť náhľadový text", + "description": "Zobrazovať náhľady e-mailov v zozname", + "focus_description": "Zobrazovať vložený náhľadový text v jednoriadkovom sústredenom zozname" + }, + "show_verification_codes": { + "label": "Zobrazovať overovacie kódy", + "description": "Ponúkať kód z prihlasovacích a potvrdzovacích e-mailov na skopírovanie jedným kliknutím, v zozname počas jedného dňa a v správe" + }, + "disable_threading": { + "label": "Zakázať zoskupovanie konverzácií", + "description": "Zobrazovať e-maily ako jednotlivé správy namiesto zoskupovania do vlákien" + }, + "plain_text_mode": { + "label": "Iba prostý text", + "description": "Zakázať editor formátovaného textu a odosielať všetky e-maily iba ako prostý text" + }, + "auto_select_reply_identity": { + "label": "Odpovedať z prijatej adresy", + "description": "Pri odpovedaní automaticky prepnúť adresu odosielateľa na identitu, ktorá pôvodne prijala správu" + }, + "reply_identity_match": { + "label": "Porovnávanie prijímacej adresy", + "description": "Ktoré prijímacie adresy sa považujú za vaše. „Iba presná adresa“ vyberie len jednu z vašich nastavených identít; „rovnaká doména“ navyše považuje akúkoľvek inú adresu na doménach vašich identít za catch-all alias a prepíše na ňu hlavičku Od. Ak sú tieto adresy distribučné zoznamy, zvoľte presnú adresu.", + "exact": "Iba presná adresa", + "domain": "Akákoľvek adresa na mojich doménach" + }, + "signature_position": { + "label": "Pozícia podpisu", + "description": "Kam vložiť podpis v odpovediach a preposlaniach.", + "above_quote": "Pred citovaným textom", + "below_quote": "Za citovaným textom" + }, + "signature_separator": { + "label": "Oddeľovač podpisu", + "description": "Pred podpis pridať štandardný oddeľovací riadok \"-- \" (RFC 3676)." + }, + "sub_address_delimiter": { + "label": "Oddeľovač podadresy", + "description": "Znak oddeľujúci používateľské meno od podadresy. Použite rovnaký oddeľovač, aký používa váš poštový server (napr. user{delimiter}tag@domain.com).", + "option": "{delimiter} (pouzivatel{delimiter}stitok@domena.sk)", + "custom": "Vlastný…", + "custom_input_label": "Vlastný znak oddeľovača" + }, + "attachment_click_action": { + "label": "Akcia po kliknutí na prílohu", + "description": "Vyberte, či kliknutie na prílohu zobrazí náhľad alebo ju okamžite stiahne", + "preview": "Náhľad, ak je to možné", + "download": "Okamžite stiahnuť" + }, + "attachment_position": { + "label": "Pozícia príloh", + "description": "Kde zobrazovať prílohy v hlavičke e-mailu", + "beside-sender": "Vedľa odosielateľa", + "below-header": "Pod hlavičkou" + }, + "emails_per_page": { + "10": "10 e-mailov", + "25": "25 e-mailov", + "50": "50 e-mailov", + "100": "100 e-mailov", + "label": "E-mailov na stránku", + "description": "Počet e-mailov načítaných naraz" + }, + "always_light_mode": { + "label": "Vždy zobrazovať e-maily v svetlom režime", + "description": "Vykresľovať obsah e-mailu v svetlom režime aj keď je aplikácia v tmavom režime" + }, + "external_content": { + "label": "Externý obsah", + "description": "Ako zaobchádzať s obrázkami a externým obsahom", + "ask": "Vždy sa opýtať", + "block": "Vždy blokovať", + "allow": "Vždy povoliť" + }, + "trusted_senders": { + "label": "Dôveryhodní odosielatelia", + "description": "Spravujte odosielateľov, ktorých obrázky sa načítavajú automaticky", + "count_zero": "Žiadni", + "count_one": "1 odosielateľ", + "count_other": "{count} odosielateľov", + "modal_title": "Dôveryhodní odosielatelia", + "empty_title": "Žiadni dôveryhodní odosielatelia", + "empty_description": "Pri prezeraní e-mailu so zablokovanými obrázkami kliknite na \"Vždy dôverovať tomuto odosielateľovi\".", + "add_manually": "Pridať odosielateľa ručne", + "add_button": "Pridať", + "add_placeholder": "Zadajte e-mailovú adresu", + "search_placeholder": "Hľadať odosielateľov...", + "no_results": "Hľadaniu nezodpovedá žiadny odosielateľ", + "remove": "Odstrániť", + "close": "Zavrieť", + "invalid_email": "Zadajte platnú e-mailovú adresu", + "already_added": "Tento odosielateľ je už dôveryhodný", + "save_error": "Uloženie zlyhalo", + "use_address_book_label": "Synchronizovať s adresárom", + "use_address_book_description": "Ukladať dôveryhodných odosielateľov vo vyhradenom adresári" + }, + "hover_actions": { + "label": "Rýchle akcie po prejdení myšou", + "description": "Vyberte rýchle akcie viditeľné po prejdení cez e-mail v zozname", + "delete": "Odstrániť", + "star": "Hviezdička / Odstrániť hviezdičku", + "mark_read": "Označiť ako prečítané / neprečítané", + "archive": "Archivovať", + "tag": "Štítok", + "spam": "Označiť ako spam", + "not_spam": "Nie je spam", + "none_selected": "Neboli vybrané žiadne akcie", + "mode_label": "Režim zobrazenia", + "mode_inline": "Vložené", + "mode_floating": "Plávajúce", + "corner_label": "Plávajúca pozícia", + "corner_top-left": "Ľavý horný roh", + "corner_top-right": "Pravý horný roh", + "corner_bottom-left": "Ľavý dolný roh", + "corner_bottom-right": "Pravý dolný roh" + }, + "default_mail_program": { + "label": "Predvolený poštový program", + "description": "Zaregistrovať aplikáciu {appName} ako predvolený poštový program pre odkazy mailto:", + "button": "Nastaviť ako predvolený", + "success": "Prehliadač bol požiadaný o nastavenie ako predvoleného", + "error": "Váš prehliadač túto funkciu nepodporuje" + }, + "attachment_reminder": { + "label": "Pripomenutie prílohy", + "description": "Upozorniť pred odoslaním, keď správa spomína prílohy, ale žiadne nie sú priložené", + "keywords_label": "Spúšťacie kľúčové slová", + "keywords_description": "Slová alebo frázy, ktoré spustia pripomenutie", + "add_placeholder": "Pridať kľúčové slovo...", + "add": "Pridať", + "remove": "Odstrániť" + }, + "empty_subject_warning": { + "label": "Upozornenie na prázdny predmet", + "description": "Pred odoslaním správy bez predmetu požiadať o potvrdenie." + }, + "recipient_mentions": { + "label": "Spomínať príjemcov pomocou @", + "description": "Napísaním @ v správe sa ponúknu jej príjemcovia a vloží sa krstné meno vybraného. Enter alebo Tab vyberie zvýraznené meno." + }, + "mention_adds_recipient": { + "label": "Kontakty pri zmienkach cez @", + "description": "Po napísaní @ sa ponúknu aj vaše kontakty a ľudia z adresára. Kto ešte nie je príjemcom, bude po výbere do správy pridaný.", + "to": "Pridať do Komu", + "cc": "Pridať do Kópia", + "off": "Ponúkať iba príjemcov" + }, + "hide_inline_image_attachments": { + "label": "Skryť vložené obrázky zo zoznamu príloh", + "description": "Obrázky vložené priamo do textu správy sa nezobrazia ako samostatné prílohy" + }, + "attachment_image_previews": { + "label": "Zobraziť náhľady obrázkov v prílohách", + "description": "Zobrazovať obrázkové prílohy ako miniatúry namiesto všeobecných ikon súborov" + }, + "send_delay": { + "label": "Vrátiť odoslanie / oneskorenie odoslania", + "description": "Odložiť bežné odoslanie krátkym oknom na serveri.", + "off": "Vypnuté", + "seconds": "{seconds} sekúnd", + "unsupported": "Aktuálny účet neoznamuje podporu oneskoreného odoslania." + }, + "return_to_list_after_action": { + "label": "Vrátiť sa na zoznam po odstránení alebo označení ako neprečítané", + "description": "Po odstránení alebo označení otvorenej správy ako neprečítanej sa vrátiť na zoznam správ." + }, + "swipe_right_action": { + "label": "Akcia pri potiahnutí doprava (mobil)", + "description": "Na dotykových zariadeniach akcia, ktorá sa vykoná pri potiahnutí správy v zozname doprava." + }, + "swipe_left_action": { + "label": "Akcia pri potiahnutí doľava (mobil)", + "description": "Na dotykových zariadeniach akcia, ktorá sa vykoná pri potiahnutí správy v zozname doľava." + }, + "swipe_actions": { + "none": "Žiadna", + "archive": "Archivovať", + "delete": "Odstrániť", + "mark_read": "Prepnúť prečítané", + "star": "Prepnúť hviezdičku", + "spam": "Označiť ako spam" + }, + "rtl_editing": { + "label": "Podpora písania sprava doľava", + "description": "Pridá do panela nástrojov editora tlačidlo smeru textu, ktorým možno odseky nastaviť zľava doprava alebo sprava doľava" + } + }, + "composer": { + "title": "Skladanie správ", + "description": "Nakonfigurujte nastavenia pre písanie e-mailov", + "autosave": { + "label": "Interval automatického ukladania", + "description": "Ako často sa majú koncepty automaticky ukladať", + "30s": "Každých 30 sekúnd", + "1m": "Každú minútu", + "2m": "Každé 2 minúty", + "5m": "Každých 5 minút" + }, + "send_confirmation": { + "label": "Potvrdenie odoslania", + "description": "Pred odoslaním správy požiadať o potvrdenie" + }, + "default_reply": { + "label": "Predvolený režim odpovede", + "description": "Predvolená akcia po kliknutí na Odpovedať", + "reply": "Odpovedať", + "reply_all": "Odpovedať všetkým" + } + }, + "privacy": { + "title": "Súkromie a zabezpečenie", + "description": "Správa nastavení súkromia a zabezpečenia", + "external_images": { + "label": "Blokovať externé obrázky", + "description": "Zabrániť sledovaniu prostredníctvom externých obrázkov" + }, + "session_timeout": { + "label": "Časový limit relácie", + "description": "Automaticky odhlásiť po určitej dobe nečinnosti", + "never": "Nikdy", + "30m": "30 minút", + "1h": "1 hodina", + "4h": "4 hodiny" + }, + "clear_cache": { + "label": "Vymazať vyrovnávaciu pamäť", + "description": "Odstrániť dáta z vyrovnávacej pamäte a dočasné súbory", + "button": "Vymazať vyrovnávaciu pamäť", + "confirm": "Naozaj chcete vymazať vyrovnávaciu pamäť?", + "success": "Vyrovnávacia pamäť bola vymazaná" + } + }, + "account": { + "title": "Účet", + "description": "Zobraziť informácie o vašom účte", + "name_label": "Zobrazované meno", + "username_label": "Používateľské meno", + "account_type_label": "Typ účtu", + "auth_method_label": "Overenie", + "auth_method_oauth": "Single Sign-On (OAuth/OIDC)", + "auth_method_basic": "Heslo", + "auth_method_token": "Prístupový token", + "demo_account": "Demo účet", + "email": { + "label": "E-mailová adresa", + "value": "{email}" + }, + "server": { + "label": "JMAP server", + "value": "{server}" + }, + "storage": { + "label": "Využitie úložiska", + "used": "{used} z {total} použitých", + "percentage": "{percent} % použitých" + }, + "last_sync": { + "label": "Posledná synchronizácia", + "value": "{time}" + }, + "accounts": { + "title": "Prihlásené účty", + "description": "Presuňte na zmenu poradia účtov v rozbaľovacom zozname", + "active": "Aktuálne aktívny účet", + "default_badge": "Predvolený účet", + "set_default": "Nastaviť ako predvolený", + "switch_to": "Prepnúť na tento účet", + "move_up": "Posunúť nahor", + "move_down": "Posunúť nadol", + "drag_handle": "Presuňte na zmenu poradia", + "add": "Pridať účet" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Zdieľané so mnou", + "description": "Skupinové a zdieľané účty, ktoré môžete spravovať.", + "shared_label": "Zdieľaný účet" + } + }, + "scoped": { + "back": "Späť na môj účet", + "managing": "Správa: {name}" + }, + "security": { + "title": "Zabezpečenie účtu", + "description": "Správa hesla, dvojfaktorového overenia a nastavení zabezpečenia", + "detecting": "Zisťovanie možností servera...", + "not_available": "Správa zabezpečenia účtu nie je pre tento poštový server k dispozícii.", + "password": { + "title": "Zmeniť heslo", + "current": "Aktuálne heslo", + "new": "Nové heslo", + "confirm": "Potvrdiť nové heslo", + "submit": "Zmeniť heslo", + "success": "Heslo bolo zmenené", + "error_title": "Zmena hesla zlyhala", + "error_mismatch": "Nové heslá sa nezhodujú", + "error_min_length": "Heslo musí mať aspoň 8 znakov", + "error_generic": "Nepodarilo sa zmeniť heslo" + }, + "display_name": { + "label": "Zobrazované meno", + "description": "Vaše meno zobrazené na serveri", + "placeholder": "Zadajte svoje zobrazované meno", + "save": "Uložiť", + "success": "Zobrazované meno bolo aktualizované", + "error": "Nepodarilo sa aktualizovať zobrazované meno" + }, + "totp": { + "section_title": "Dvojfaktorové overenie", + "label": "Overenie TOTP", + "description": "Pridajte ďalšiu vrstvu zabezpečenia pomocou jednorazového hesla na báze času", + "active": "Zapnuté", + "inactive": "Vypnuté", + "enabled": "Dvojfaktorové overenie zapnuté", + "disabled": "Dvojfaktorové overenie vypnuté", + "enable_error": "Nepodarilo sa zapnúť 2FA", + "disable_error": "Nepodarilo sa vypnúť 2FA", + "setup_instructions": "Skopírujte túto URL adresu do svojej overovacej aplikácie (Google Authenticator, Authy atd.):", + "verification_code": "Overovací kód", + "confirm": "Potvrdiť", + "disable": "Vypnúť", + "disable_confirm_prompt": "Na vypnutie dvojfaktorového overenia zadajte heslo.", + "password_required": "Heslo je povinné", + "code_required": "Overovací kód je povinný", + "code_invalid": "Neplatný kód. Skontrolujte overovaciu aplikáciu a skúste znova." + }, + "app_passwords": { + "title": "Heslá aplikácií", + "description": "Vytvorte heslá pre aplikácie, ktoré nepodporujú dvojfaktorové overenie", + "add": "Pridať", + "create": "Vytvoriť", + "cancel": "Zrušiť", + "done": "Hotovo", + "generate": "Vygenerovať", + "name_label": "Názov aplikácie", + "name_placeholder": "napr. Thunderbird, iPhone Mail", + "expires_label": "Platí do (voliteľné)", + "allowed_ips_label": "Povolené IP (voliteľné)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Oddeľte čiarkou alebo medzerou. Pre povolenie všetkých IP nechajte prázdne.", + "password_label": "Heslo (nechajte prázdne pre automatické generovanie)", + "password_placeholder": "Automaticky vygenerované", + "copy_now_warning": "Skopírujte toto heslo hneď – už sa znova nezobrazí.", + "added": "Heslo aplikácie vytvorené", + "removed": "Heslo aplikácie odstránené", + "add_error": "Chyba pri vytváraní hesla aplikácie", + "remove_error": "Chyba pri odstraňovaní hesla aplikácie", + "none": "Žiadne heslá aplikácií" + }, + "api_keys": { + "title": "API kľúče", + "description": "Vytvorte API kľúče pre skripty a integrácie.", + "name_label": "Názov kľúča", + "name_placeholder": "napr. Zálohovací skript, CI runner", + "copy_now_warning": "Skopírujte tento API kľúč hneď – už sa znova nezobrazí.", + "added": "API kľúč vytvorený", + "removed": "API kľúč odstránený", + "add_error": "Chyba pri vytváraní API kľúča", + "remove_error": "Chyba pri odstraňovaní API kľúča", + "none": "Žiadne API kľúče" + }, + "encryption": { + "section_title": "Šifrovanie dát v pokoji", + "label": "Šifrovanie e-mailov", + "description": "Šifrovať správy uložené na serveri pre väčšie súkromie", + "active": "Šifrovanie {type} zapnuté", + "inactive": "Vypnuté", + "enabled": "Šifrovanie dát v pokoji zapnuté", + "disabled_success": "Šifrovanie dát v pokoji vypnuté", + "error": "Nepodarilo sa aktualizovať nastavenia šifrovania", + "algorithm_label": "Šifrovací algoritmus", + "encrypt_on_append": "Šifrovať nové e-maily pri nahrávaní", + "allow_spam_training": "Povoliť trénovanie spamového filtra pred zašifrovaním e-mailov", + "enable_encryption": "Zapnúť šifrovanie", + "enable_tooltip": "Zapnúť šifrovanie týmto kľúčom", + "configure_title": "Zapnúť šifrovanie týmto kľúčom", + "enable_button": "Zapnúť", + "disable_tooltip": "Vypnúť šifrovanie", + "enabled_success": "Šifrovanie dát v pokoji zapnuté" + }, + "email_client": { + "title": "Konfigurácia poštového klienta", + "description": "Použite tieto prihlasovacie údaje na konfiguráciu e-mailového klienta (Thunderbird, Apple Mail, Outlook atd.)", + "jmap_username_label": "Používateľské meno JMAP", + "copy": "Kopírovať", + "copied": "Skopírované", + "password_instructions": "Použite vyššie uvedené používateľské meno JMAP spolu s heslom aplikácie na prihlásenie v poštovom klientovi." + }, + "link_device": { + "title": "Prepojiť mobilnú aplikáciu", + "description": "Prihláste sa do aplikácie Bulwark Mail v telefóne bez toho, aby ste tam zadávali heslo. Telefón dostane vlastné prihlásenie, takže odhlásenie tu ho neodhlási.", + "generate": "Zobraziť QR kód", + "regenerate": "Zobraziť nový kód", + "instructions": "Otvorte aplikáciu Bulwark Mail a na prihlasovacej obrazovke klepnite na \"Scan a sign-in code\" (alebo na \"Add account\" → \"Scan a sign-in code\", ak ste už prihlásení) a potom namierte fotoaparát sem.", + "expires_in": "Tento kód vyprší za {seconds} sekúnd. Dá sa použiť iba raz.", + "expired": "Platnosť tohto kódu vypršala.", + "error": "Nepodarilo sa vytvoriť párovací kód. Skúste to znova.", + "password_prompt": "Zadajte heslo a potvrďte, že ste to vy. Telefón dostane vlastné prihlásenie; vaše heslo sa do telefónu neposiela.", + "show_password": "Zobraziť heslo", + "hide_password": "Skryť heslo", + "continue": "Pokračovať", + "error_wrong_password": "Nesprávne heslo. Skúste to znova.", + "totp_prompt": "Zadajte kód z overovacej aplikácie.", + "error_password_or_code": "Heslo alebo kód je nesprávny.", + "error_not_signed_in": "Vaša relácia vypršala. Ak chcete prepojiť zariadenie, znova sa prihláste.", + "error_impersonation": "Zariadenia nie je možné prepojiť, kým si prezeráte schránku iného používateľa.", + "error_too_many_attempts": "Príliš veľa pokusov. Chvíľu počkajte a skúste to znova.", + "error_session_secret": "Prepájanie zariadení nie je na tomto serveri nastavené (chýba SESSION_SECRET). Obráťte sa na správcu.", + "error_server_unreachable": "Nepodarilo sa spojiť s vaším poštovým serverom. Skúste to znova.", + "error_pairing_unavailable": "Váš poštový server neumožňuje prepojiť zariadenie týmto spôsobom. Obráťte sa na správcu.", + "qr_alt": "QR kód na prihlásenie", + "link_label": "Prihlasovací odkaz", + "copy_link": "Kopírovať prihlasovací odkaz", + "copied": "Skopírované", + "copy_hint": "Nemáte fotoaparát? Skopírujte odkaz a vložte ho do aplikácie pomocou \"Paste sign-in link\".", + "success_title": "Prihlásené v telefóne", + "success_body": "Túto stránku môžete zavrieť. Ak chcete prepojiť ďalšie zariadenie, zobrazte nový kód.", + "link_another": "Prepojiť ďalšie zariadenie", + "use_sso": "Použiť namiesto toho jednotné prihlásenie", + "error_insecure_server": "Mobilná aplikácia sa k poštovým serverom pripája len cez https a tento server používa http." + }, + "public_keys": { + "name_label": "Názov verejného kľúča", + "name_placeholder": "napr. Môj kľúč", + "key_label": "Verejný kľúč (S/MIME alebo PGP vo formáte ASCII)", + "key_placeholder": "Sem vložte svoj verejný kľúč", + "none": "Nie sú nastavené žiadne verejné kľúče" + } + }, + "identities": { + "title": "Identity odosielateľa", + "description": "Správa e-mailových adries, z ktorých môžete odosielať správy", + "identities_count": { + "label": "Vaše identity", + "description": "E-mailové adresy nakonfigurované na odosielanie", + "count_zero": "Žiadne identity", + "count_one": "1 identita", + "count_other": "{count} identít" + }, + "manage": "Spravovať identity", + "sub_addressing": { + "label": "Podadresovanie", + "description": "Používajte štítky ako pouzivatel+stitok@domena.sk na organizáciu prichádzajúcej pošty", + "learn_more": "Zistiť viac" + } + }, + "vacation": { + "title": "Neprítomnosť", + "description": "Automaticky spracovať prichádzajúce e-maily počas vašej neprítomnosti", + "loading": "Načítavanie nastavení automatickej odpovede...", + "not_supported": "Váš poštový server nepodporuje automatické odpovede v neprítomnosti.", + "fetch_error": "Nepodarilo sa načítať nastavenia automatickej odpovede. Skúste to znova.", + "status": { + "label": "Odpovedať automaticky", + "description": "Odosielať automatickú odpoveď ľuďom, ktorí vám napíšu", + "active": "Aktívna", + "inactive": "Neaktívna" + }, + "date_range": { + "start": "Dátum začiatku", + "start_description": "Nechajte prázdne pre okamžité spustenie", + "end": "Dátum ukončenia", + "end_description": "Nechajte prázdne pre beh bez ukončenia" + }, + "message": { + "subject_label": "Predmet", + "subject_description": "Predmet automatickej odpovede", + "subject_placeholder": "Mimo kancelárie", + "body_label": "Telo správy", + "body_description": "Obsah správy v prostom texte", + "body_placeholder": "Ďakujem za vašu správu. Momentálne som mimo kancelárie a odpoviem po svojom návrate.", + "html_label": "Formátovaná správa (HTML)", + "html_description": "Pridajte formátovanú verziu s odkazmi a štýlom. Príjemcovia, ktorých poštový klient ju nedokáže zobraziť, uvidia obyčajný text vyššie.", + "html_placeholder": "Napíšte formátovanú odpoveď o neprítomnosti…" + }, + "preview": { + "title": "Náhľad", + "show": "Zobraziť náhľad", + "hide": "Skryť náhľad" + }, + "save": "Uložiť zmeny", + "saving": "Ukladanie...", + "warnings": { + "end_before_start": "Dátum ukončenia musí byť po dátume začiatku", + "invalid_date": "Zadajte platný dátum začiatku a konca aj s časom alebo pole nechajte prázdne", + "start_in_past": "Dátum začiatku je v minulosti", + "empty_body": "Telo správy je prázdne – príjemcovia dostanú prázdnu odpoveď", + "subject_too_long": "Predmet je pre server príliš dlhý (najviac {max} bajtov).", + "body_too_long": "Správa je pre server príliš dlhá (najviac {max} bajtov vrátane formátovania).", + "forward_address": "Zadajte platnú adresu na preposielanie", + "not_running": "Preposielanie alebo príjemcovia odpovede sú uložení, ale momentálne nie sú aktívni. Uložením ich znova zapnete." + }, + "forward": { + "title": "Preposielanie", + "description": "Preposiela prichádzajúce správy na inú adresu, s automatickou odpoveďou alebo bez nej, v období uvedenom vyššie (bez dátumu ukončenia, kým ho nevypnete). Spam sa nepreposiela.", + "enabled_label": "Preposielať správy", + "to_label": "Preposlať na", + "to_placeholder": "email@example.com", + "keep_label": "Ponechať v Doručených", + "keep_description": "Inak sa správa iba prepošle: nezostane v tejto schránke a nepoužije sa na ňu žiadne pravidlo filtra." + }, + "audience": { + "label": "Odpovedať", + "description": "Interní odosielatelia: {domains}", + "all": "Všetkým odosielateľom", + "internal": "Iba interným odosielateľom", + "external": "Iba externým odosielateľom" + }, + "auto_reply": { + "title": "Automatická odpoveď" + } + }, + "folders": { + "role_memos": "Poznámky", + "title": "Priečinky", + "description": "Správa priečinkov pošty a priradenie štandardných rolí", + "folder_list": "Vaše priečinky", + "folder_list_description": "Kliknutím na ikonu priečinka ho upravíte", + "standard_roles": "Štandardné roly priečinkov", + "standard_roles_description": "Priraďte priečinky používané pre štandardné roly poštovej schránky.", + "role_inbox": "Doručené", + "role_drafts": "Koncepty", + "role_sent": "Odoslané", + "role_trash": "Kôš", + "role_junk": "Spam / Nevyžiadaná pošta", + "role_archive": "Archív", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Žiadna", + "create_folder": "Vytvoriť priečinok", + "create_subfolder": "Vytvoriť podpriečinok", + "reorder": "Potiahnutím zmeníte poradie", + "reorder_error": "Nepodarilo sa zmeniť poradie priečinkov", + "move_error": "Nepodarilo sa presunúť priečinok", + "select_folder": "Vybrať {name}", + "select_hint": "Vybrať. Shift+kliknutie vyberie všetky priečinky až po naposledy kliknutý.", + "selected_count": "{count, plural, one {Vybraný # priečinok} few {Vybrané # priečinky} other {Vybraných # priečinkov}}", + "move_to": "Presunúť do...", + "move_to_top_level": "Najvyššia úroveň", + "move": "Presunúť", + "clear_selection": "Zrušiť výber", + "folders_moved": "{count, plural, one {Presunutý # priečinok} few {Presunuté # priečinky} other {Presunutých # priečinkov}}", + "move_partial_error": "{count, plural, one {# priečinok sa nepodarilo presunúť} few {# priečinky sa nepodarilo presunúť} other {# priečinkov sa nepodarilo presunúť}}", + "subfolder_of": "V priečinku {name}", + "subfolder_name": "Názov podpriečinka", + "new_folder_name": "Názov priečinka", + "rename": "Premenovať", + "change_icon": "Zmeniť ikonu", + "count_mode": "Počítadlo pre {name}", + "count_mode_default": "Počítadlo: predvolené", + "count_mode_unread": "Len neprečítané", + "count_mode_all": "Neprečítané a celkom", + "count_mode_none": "Bez počítadla", + "delete": "Odstrániť", + "confirm_delete": "Naozaj chcete odstrániť priečinok \"{name}\"? E-maily v tomto priečinku budú presunuté do Koša.", + "create": "Vytvoriť", + "cancel": "Zrušiť", + "no_folders": "Žiadne vlastné priečinky", + "cannot_delete_role": "Priečinok so štandardnou rolou sa nedá odstrániť. Najprv túto rolu odstráňte.", + "folder_created": "Priečinok bol vytvorený", + "folder_renamed": "Priečinok bol premenovaný", + "folder_deleted": "Priečinok bol odstránený", + "role_updated": "Rola priečinka bola aktualizovaná", + "error_create": "Priečinok sa nepodarilo vytvoriť", + "error_rename": "Priečinok sa nepodarilo premenovať", + "error_delete": "Priečinok sa nepodarilo odstrániť", + "error_delete_has_children": "Priečinok sa nedá odstrániť: stále obsahuje podpriečinky. Najprv ich odstráňte alebo presuňte.", + "error_delete_has_email": "Priečinok sa nedá odstrániť: stále obsahuje e-maily. Najprv ich presuňte alebo odstráňte.", + "error_role": "Rolu priečinka sa nepodarilo aktualizovať" + }, + "advanced": { + "title": "Pokročilé", + "description": "Pokročilé možnosti a nastavenia pre vývojárov", + "debug_mode": { + "label": "Režim ladenia", + "description": "Povoliť podrobné protokolovanie na riešenie problémov" + }, + "debug_categories": { + "description": "Vyberte kategórie na protokolovanie.", + "jmap": "Klient JMAP", + "jmap_description": "Operácie so schránkami, načítavanie správ a požiadavky protokolu JMAP", + "calendar": "Kalendár", + "calendar_description": "Udalosti kalendára, importy a plánovanie správ", + "tasks": "Úlohy", + "tasks_description": "Vytváranie úloh kalendára, načítavanie a aktualizácie", + "auth": "Overenie", + "auth_description": "Prihlasovanie, TOTP, výmena tokenov a správa relácií", + "filters": "Filtre", + "filters_description": "Pravidlá filtrov Sieve a skripty pre automatickú odpoveď", + "email": "Zobrazenie e-mailu", + "email_description": "Vykresľovanie e-mailov, spracovanie TNEF a označovanie ako prečítané", + "push": "Push oznámenia", + "push_description": "Konfigurácia a doručovanie push oznámení", + "contacts": "Kontakty a adresáre", + "contacts_description": "Synchronizácia kontaktov, operácie s adresárom a dôveryhodní odosielatelia" + }, + "settings_sync": { + "label": "Synchronizácia nastavení", + "description": "Synchronizovať nastavenia medzi prehliadačmi a zariadeniami" + }, + "sender_favicons": { + "label": "Favikony odosielateľov", + "description": "Zobrazovať ikony webových stránok ako profilové obrázky firemných odosielateľov", + "refresh_label": "Obnoviť ikony odosielateľov", + "refresh_description": "Znova načítať ikony webov, napríklad keď odosielateľ zmenil svoju ikonu", + "refresh_button": "Obnoviť", + "refresh_done": "Ikony odosielateľov sa načítajú znova" + }, + "show_avatars_in_junk": { + "label": "Zobrazovať avatáry v spame", + "description": "Zobrazovať profilové fotky a ikony odosielateľov v spame. Predvolene vypnuté." + }, + "keyboard_shortcuts": { + "label": "Klávesové skratky", + "description": "Zobraziť dostupné klávesové skratky", + "button": "Zobraziť skratky" + }, + "refresh_cache": { + "label": "Obnoviť uložené údaje", + "description": "Znova načíta kontakty, kalendáre a priečinky zo servera. Zachová vaše účty a prihlásenia — opraví zastarané alebo nesprávne zobrazenie bez odhlásenia.", + "button": "Obnoviť" + }, + "reset_settings": { + "label": "Resetovať nastavenia", + "description": "Obnoviť všetky nastavenia na predvolené hodnoty", + "button": "Obnoviť predvolené" + }, + "export_settings": { + "label": "Exportovať nastavenia", + "description": "Stiahnuť svoje nastavenia ako súbor JSON", + "button": "Exportovať" + }, + "import_settings": { + "label": "Importovať nastavenia", + "description": "Nahrať nastavenia zo súboru JSON", + "button": "Importovať" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Zdrojový kód" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Kliknite na spam a phishing skôr, než dorazia do vašej doručenej pošty. Skutočnú poštu nechajte prejsť. Keď stratíte všetky tri štíty, schránka padne.", + "kinds": { + "spam": "Spam", + "phishing": "Phishing", + "legit": "Skutočná pošta" + }, + "click_to_block": "Kliknite na zablokovanie", + "let_through": "Nechajte prejsť", + "start": "Začať", + "play_again": "Hrať znova", + "score": "Skóre", + "combo": "Bonus za sériu", + "shields_left": "Zostáva {count} z {total} štítov", + "seconds": "{seconds} s", + "best": "Rekord: {score}", + "new_best": "Nový rekord", + "held": "Schránka ubránená", + "overrun": "Schránka padla", + "stats": { + "caught": "Zablokované", + "delivered": "Doručené", + "mistakes": "Chyby" + } + } + }, + "sidebar_apps": { + "title": "Aplikácie postranného panela", + "description": "Spravujte vlastné aplikácie a odkazy v postrannom paneli", + "keep_loaded": "Ponechať aplikácie načítané", + "keep_loaded_description": "Udržiavať vložené aplikácie spustené na pozadí počas prepínania", + "managed_title": "Poskytnuté správcom", + "managed_description": "Tieto aplikácie sú nastavené pre všetkých a nedajú sa zmeniť.", + "manage_title": "Vlastné aplikácie", + "manage_description": "Pridávať, upravovať alebo odstraňovať vlastné aplikácie z postranného panela" + }, + "contacts": { + "title": "Kontakty", + "description": "Import a export kontaktov", + "group_by_letter_label": "Zoskupiť podľa prvého písmena", + "sort_by_last_name_label": "Zoradiť podľa priezviska", + "sort_by_last_name_description": "Zoradiť zoznam kontaktov podľa priezviska, aby členovia rodiny boli pri sebe", + "group_by_letter_description": "Zobraziť abecedné záhlavia sekcií v zozname kontaktov", + "import_label": "Importovať kontakty", + "import_description": "Importovať kontakty zo súboru vCard (.vcf)", + "export_label": "Exportovať kontakty", + "export_description": "Exportovať všetky kontakty ako súbor vCard (.vcf)", + "manage_title": "Adresáre", + "manage_description": "Premenovať svoje adresáre", + "no_address_books": "Neboli nájdené žiadne adresáre", + "categories_title": "Kategórie", + "categories_description": "Premenovať kategórie kontaktov", + "no_categories": "Neboli nájdené žiadne kategórie" + }, + "downloads": { + "title": "Stiahnuté", + "description": "Prispôsobte si, ako sa pomenúvajú stiahnuté e-maily a prílohy.", + "reset": "Obnoviť predvolené", + "preview": "Náhľad:", + "email_template": { + "label": "Názov súboru e-mailu (.eml)", + "description": "Šablóna použitá pri exporte e-mailu alebo jeho presunutí do súborového systému. Prípona .eml sa pridáva automaticky." + }, + "attachment_template": { + "label": "Názov súboru prílohy", + "description": "Šablóna použitá pri sťahovaní alebo presunutí prílohy. Ak vynecháte '{filename}' a '{ext}', zachová sa pôvodná prípona." + }, + "bundle_template": { + "label": "Názov .zip súboru s viacerými e-mailmi", + "description": "Šablóna použitá pri presunutí alebo stiahnutí viacerých vybraných e-mailov ako jedného .zip archívu." + }, + "spaces": { + "label": "Medzery", + "description": "Nahradiť medzery vo výslednom názve súboru iným znakom.", + "keep": "Zachovať medzery", + "underscore": "Nahradiť za _", + "dash": "Nahradiť za -" + }, + "lowercase": { + "label": "Malé písmená", + "description": "Vynútiť celý názov súboru malými písmenami." + }, + "strip_diacritics": { + "label": "Odstrániť diakritiku", + "description": "Previesť písmená s diakritikou na ASCII ekvivalenty (á → a, é → e)." + }, + "collapse_separators": { + "label": "Zlúčiť opakované oddeľovače", + "description": "Zlúčiť postupnosti medzier, podčiarkovníkov alebo pomlčiek do jedného znaku." + }, + "after_export": { + "label": "Po exporte", + "description": "Po exporte ako .eml možno e-mail voliteľne presunúť.", + "keep": "Ponechať v schránke", + "archive": "Presunúť do archívu", + "trash": "Presunúť do koša" + } + }, + "filters": { + "title": "E-mailové filtre", + "description": "Vytvárajte pravidlá na automatické triedenie, štítkovanie a správu prichádzajúcich e-mailov", + "add_rule": "Pridať pravidlo", + "no_rules": "Žiadne pravidlá filtrov", + "no_rules_description": "Vytvárajte pravidlá na automatickú organizáciu prichádzajúcich e-mailov", + "vacation_active": "Automatická odpoveď je aktívna", + "vacation_active_description": "Automatická odpoveď je zapnutá pre prichádzajúce správy", + "vacation_configure": "Konfigurovať", + "edit_rule": "Upraviť pravidlo", + "new_rule": "Nové pravidlo", + "delete_rule": "Odstrániť pravidlo", + "delete_confirm": "Naozaj chcete odstrániť toto pravidlo?", + "enable": "Povoliť", + "disable": "Zakázať", + "raw_editor": "Textový editor Sieve", + "raw_editor_warning": "Úprava samotného skriptu Sieve môže narušiť vizuálnu úpravu pravidiel.", + "validate": "Overiť", + "validation_success": "Skript je platný", + "validation_error": "Skript obsahuje chyby", + "save": "Uložiť pravidlá", + "saving": "Ukladanie...", + "saved": "Filtre boli uložené", + "save_failed": "Uloženie filtrov zlyhalo", + "loading": "Načítavanie filtrov...", + "not_supported": "Váš poštový server nepodporuje e-mailové filtre.", + "rule_name": "Názov pravidla", + "rule_name_placeholder": "napr. Triediť newslettery", + "match_all": "Splňuje VŠETKY podmienky", + "match_any": "Splňuje AKÚKOĽVEK podmienku", + "conditions": "Podmienky", + "add_condition": "Pridať podmienku", + "actions": "Akcie", + "add_action": "Pridať akciu", + "stop_processing": "Zastaviť spracovanie ďalších pravidiel", + "include_spam": "Presúvať aj správy označené ako spam", + "attachment_type_placeholder": "napr. pdf, doc, jpg", + "value_placeholder_multi": "Hodnota (viac oddelených čiarkami)", + "condition_fields": { + "attachment": "Príloha", + "from": "Od", + "to": "Komu", + "cc": "Kópia", + "subject": "Predmet", + "header": "Vlastná hlavička", + "size": "Veľkosť", + "body": "Telo správy", + "all": "Všetky správy" + }, + "comparators": { + "has_any": "je prítomná", + "has_type": "typu", + "contains": "obsahuje", + "not_contains": "neobsahuje", + "is": "je presne", + "not_is": "nie je", + "starts_with": "začína na", + "ends_with": "končí na", + "matches": "zodpovedá vzoru", + "greater_than": "je väčšia ako", + "less_than": "je menšia ako", + "address_is": "je adresa", + "domain_is": "má doménu" + }, + "action_types": { + "move": "Presunúť do priečinka", + "copy": "Kopírovať do priečinka", + "forward": "Preposlať na", + "mark_read": "Označiť ako prečítané", + "star": "Označiť správu hviezdičkou", + "add_label": "Pridať štítok", + "discard": "Zahodiť (ticho odstrániť)", + "reject": "Odmietnuť so správou", + "keep": "Ponechať v Doručených", + "stop": "Zastaviť spracovanie" + }, + "move_to_folder": "Vybrať priečinok", + "copy_to_folder": "Vybrať priečinok", + "forward_to": "Preposlať na e-mailovú adresu", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Ponechať kópiu", + "forward_limit": "Limit preposlaní na správu na tomto serveri: {count}. Ďalšie preposlania sa preskočia.", + "forward_unavailable": "Tento server nepovoľuje preposielanie. Akcie preposlania v pravidlách filtrov nemajú žiadny účinok.", + "reject_message": "Správa o odmietnutí", + "reject_placeholder": "Váš e-mail bol odmietnutý", + "label_name": "Názov štítku", + "label_placeholder": "Vybrať štítok", + "header_name": "Názov hlavičky", + "header_placeholder": "napr. X-Mailing-List", + "size_bytes": "Veľkosť v bajtoch", + "size_placeholder": "napr. 1000000", + "system_managed": "Pravidlo spravované systémom", + "opaque_warning": "Tento skript bol upravený mimo vizuálneho sprievodcu. K dispozícii je iba priama úprava Sieve.", + "open_sieve_editor": "Otvoriť textový editor Sieve", + "fetch_error": "Nepodarilo sa načítať filtre", + "expanded_view": "Rozšírené zobrazenie", + "expanded_view_description": "Zobrazovať pravidlá filtrov s podrobnými blokmi podmienok a akcií", + "if": "Ak", + "then": "Potom", + "match_all_conditions": "všetky splnené", + "match_any_condition": "akékoľvek splnené", + "and": "a", + "or": "alebo", + "cancel": "Zrušiť", + "confirm_delete": "Odstrániť", + "rule_list": "Pravidlá filtrov", + "drag_to_reorder": "Presuňte na zmenu poradia", + "match_type": "Typ zhody", + "reset_to_visual": "Resetovať na vizuálneho sprievodcu", + "reset_warning": "Tým zahodíte aktuálny skript a začnete odznova.", + "confirm_reset": "Resetovať", + "validation_empty_name": "Názov pravidla je povinný", + "validation_empty_conditions": "Je potrebná aspoň jedna podmienka s hodnotou", + "validation_empty_actions": "Je potrebná aspoň jedna akcia", + "templates_section": "Začať zo šablóny", + "template_newsletters": "Presunúť newslettery do priečinka", + "template_receipts": "Automaticky archivovať účtenky", + "template_important": "Označiť dôležité e-maily", + "template_notifications": "Filtrovať oznámenia", + "sieve_editor": { + "title": "Editor skriptov Sieve", + "warning": "Úprava samotného skriptu Sieve môže narušiť vizuálnu úpravu pravidiel.", + "script_content": "Skript Sieve", + "valid": "Skript je platný", + "invalid": "Skript obsahuje chyby", + "save_warning": "Uloženie prepíše všetky vizuálne pravidlá. Túto akciu nie je možné vrátiť. Potvrďte opätovným kliknutím na Uložiť.", + "validating": "Overovanie...", + "validate": "Overiť", + "cancel": "Zrušiť", + "save": "Uložiť", + "confirm_save": "Potvrdiť uloženie", + "validation_failed": "Požiadavka na overenie zlyhala" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# podmienka} other {# podmienok}}", + "actions_count": "{count, plural, one {# akcia} other {# akcií}}" + }, + "origin_external": "Externé", + "managed_by_tooltip": "Spravované aplikáciou {source}. Upravte to v danej aplikácii alebo použite editor zdrojového kódu Sieve.", + "quick_rule_names": { + "move_sender": "Presunúť od {sender} do {folder}", + "move_domain": "Presunúť od @{domain} do {folder}", + "move_list": "Presunúť zoznam {list} do {folder}", + "mark_read": "Označiť od {sender} ako prečítané", + "tag": "Označiť od {sender} štítkom {tag}", + "block": "Blokovať {sender}", + "custom": "Správy od {sender}", + "custom_plain": "Nové pravidlo", + "senders": "{count, plural, one {# odosielateľa} few {# odosielateľov} many {# odosielateľa} other {# odosielateľov}}" + }, + "suggestions": "Návrhy", + "suggest_subject": "Predmet „{subject}“", + "suggest_to": "Komu {address}", + "suggest_cc": "Kópia {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Použiť aj na správy, ktoré už sú v tomto priečinku", + "apply_existing_unsupported": "Na existujúce správy možno použiť len pravidlá, ktoré kontrolujú odosielateľa, príjemcov, predmet alebo hlavičky a ktoré presúvajú, kopírujú, označujú ako prečítané, hviezdičkou alebo štítkom.", + "period_title": "Časové obdobie", + "period_start": "Dátum začiatku", + "period_end": "Dátum ukončenia", + "validation_period_order": "Dátum ukončenia musí byť po dátume začiatku", + "period_toggle": "Aktívne iba v určitom období", + "period_hint": "Platí pre správy, ktoré prídu v tomto období. Prázdne pole ponechá daný koniec otvorený.", + "validation_period_empty": "Zadajte dátum začiatku alebo ukončenia obdobia", + "validation_period_invalid": "Zadajte pre obdobie platný dátum aj s časom alebo pole nechajte prázdne", + "apply_existing_period": "Pravidlo s obdobím pôsobí len na správy, ktoré v ňom prídu, preto ho nemožno použiť na existujúce správy.", + "period_status_scheduled": "naplánované", + "period_status_active": "aktívne", + "period_status_expired": "uplynulo" + }, + "templates": { + "title": "E-mailové šablóny", + "description": "Vytvárajte opakovane použiteľné e-mailové šablóny s premennými zástupnými symbolmi", + "add": "Nová šablóna", + "edit": "Upraviť šablónu", + "name": "Názov šablóny", + "name_placeholder": "napr. Kontrolná správa", + "category": "Kategória", + "category_placeholder": "napr. Práca, Osobné", + "subject": "Predmet", + "subject_placeholder": "Predmet e-mailu", + "body": "Telo správy", + "body_placeholder": "Text e-mailu...", + "recipients_placeholder": "email@example.com", + "identity": "Odoslať ako", + "default_identity": "Predvolená identita", + "favorite": "Obľúbené", + "cancel": "Zrušiť", + "create": "Vytvoriť", + "update": "Aktualizovať", + "confirm_delete": "Odstrániť", + "no_templates": "Žiadne šablóny", + "manage": "Spravovať šablóny", + "count": "{count, plural, one {# šablóna} other {# šablón}}", + "export_import": "Export a import", + "export_import_description": "Vytvorte zálohu šablón alebo ich preneste na iné zariadenie", + "export": "Exportovať", + "import": "Importovať", + "validation": { + "empty": "Názov šablóny je povinný", + "too_long": "Názov šablóny môže mať maximálne 200 znakov" + } + }, + "files": { + "display": { + "title": "Zobrazenie", + "description": "Nakonfigurujte spôsob zobrazenia súborov a priečinkov" + }, + "default_view": { + "label": "Predvolené zobrazenie", + "description": "Vyberte rozloženie do mriežky alebo zoznamu", + "list": "Zoznam", + "grid": "Mriežka" + }, + "default_sort": { + "label": "Predvolené triedenie", + "description": "Vyberte predvolené triedenie súborov", + "name": "Názov", + "size": "Veľkosť", + "modified": "Zmenené" + }, + "sort_direction": { + "label": "Smer triedenia", + "description": "Vyberte vzostupné alebo zostupné poradie", + "ascending": "Vzostupne", + "descending": "Zostupne" + }, + "icons": { + "title": "Ikony", + "description": "Nakonfigurujte vzhľad ikon súborov" + }, + "show_icons": { + "label": "Zobrazovať ikony súborov", + "description": "Zobrazovať ikony vedľa súborov a priečinkov" + }, + "colored_icons": { + "label": "Farebné ikony", + "description": "Používať farebné ikony namiesto monochromatických" + }, + "show_thumbnails": { + "label": "Zobrazovať miniatúry", + "description": "Zobrazovať náhľady obrázkov namiesto ikon pre obrázkové súbory" + }, + "behavior": { + "title": "Správanie", + "description": "Nakonfigurujte správanie prehliadača súborov" + }, + "show_hidden": { + "label": "Zobrazovať skryté súbory", + "description": "Zobrazovať súbory a priečinky začínajúce bodkou" + }, + "preview": { + "label": "Náhľad" + } + } + }, + "errors": { + "page_error_title": "Niečo sa pokazilo", + "page_error_description": "Vyskytla sa neočakávaná chyba. Skúste to znova alebo sa vráťte na domovskú stránku.", + "sidebar_error": "Nepodarilo sa načítať poštové schránky", + "email_list_error": "Nepodarilo sa načítať e-maily", + "viewer_error_title": "E-mail sa nedá zobraziť", + "viewer_error_description": "Pri vykresľovaní tohto e-mailu sa vyskytol problém. Môže obsahovať nepodporovaný obsah.", + "composer_error": "Nepodarilo sa načítať editor správ", + "settings_error_title": "Nastavenia nie sú dostupné", + "settings_error_description": "Nepodarilo sa načítať nastavenia. Vaše predvoľby sa možno neuložia.", + "try_again": "Skúsiť znova", + "reload": "Obnoviť", + "reload_emails": "Obnoviť e-maily", + "reload_settings": "Obnoviť nastavenia", + "retry": "Opakovať", + "go_home": "Prejsť do doručenej pošty" + }, + "context_menu": { + "reply": "Odpovedať", + "reply_all": "Odpovedať všetkým", + "forward": "Preposlať", + "mark_read": "Označiť ako prečítané", + "mark_unread": "Označiť ako neprečítané", + "star": "Hviezdička", + "unstar": "Odstrániť hviezdičku", + "pin": "Pripnúť", + "unpin": "Odopnúť", + "move_to": "Presunúť do...", + "copy_to_account": "Kopírovať do účtu...", + "remove_from_folder": "Odstrániť z tohto priečinka", + "archive": "Archivovať", + "delete": "Odstrániť", + "mark_as_spam": "Nahlásiť spam", + "not_spam": "Nie je spam", + "tag": "Štítok", + "items_selected": "{count} vybraných e-mailov", + "forward_as_attachments": "Preposlať ako prílohy", + "edit_draft": "Upraviť koncept", + "cancel_scheduled_send": "Zrušiť odoslanie", + "reschedule_send": "Prenaplánovať", + "cancel_and_edit": "Zrušiť a upraviť", + "cancel_and_compose_again": "Zrušiť a napísať znova", + "rules": { + "title": "Pravidlá", + "move_from": "Vždy presúvať správy od {sender}", + "move_from_domain": "Vždy presúvať správy od @{domain}", + "move_from_list": "Vždy presúvať správy z tohto zoznamu", + "mark_read_from": "Vždy označiť správy od {sender} ako prečítané", + "tag_with": "Vždy označiť štítkom", + "block": "{count, plural, one {Blokovať odosielateľa} few {Blokovať # odosielateľov} many {Blokovať # odosielateľa} other {Blokovať # odosielateľov}}", + "no_junk": "Tento účet nemá priečinok nevyžiadanej pošty", + "create": "Vytvoriť pravidlo…", + "manage": "Spravovať pravidlá", + "senders_count": "{count, plural, one {# odosielateľa} few {# odosielateľov} many {# odosielateľa} other {# odosielateľov}}", + "cross_account": "Vyberte správy z jedného účtu", + "opaque_hint": "Vaše filtre boli upravené ručne. Otvorte nastavenia filtrov" , + "loading": "Načítavajú sa pravidlá…", + "load_failed": "Pravidlá sa nepodarilo načítať", + "none": "Zatiaľ žiadne pravidlá", + "new_mail_only": "Len pre nové správy", + "running": "Spúšťa sa „{name}“ v priečinku {folder}…" + }, + "find_emails_from": "Nájsť e-maily od {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Označiť priečinok ako prečítaný", + "mark_folder_tree_read": "Označiť priečinok a podpriečinky ako prečítané", + "mark_all_folders_read": "Označiť všetky priečinky ako prečítané", + "new_subfolder": "Nový podpriečinok...", + "new_folder": "Nový priečinok...", + "rename": "Premenovať...", + "share": "Zdieľať...", + "import_email": "Importovať .eml alebo .zip...", + "empty_folder": "Vyprázdniť priečinok", + "empty_folder_generic": "Vyprázdniť priečinok", + "delete_folder": "Zmazať priečinok", + "refresh": "Obnoviť", + "hide_shared_account": "Skryť z bočného panela", + "mute_notifications": "Stlmiť upozornenia", + "unmute_notifications": "Zapnúť upozornenia", + "mute_account_notifications": "Stlmiť upozornenia tohto účtu", + "unmute_account_notifications": "Zapnúť upozornenia tohto účtu", + "reset_folder_notifications": "Obnoviť upozornenia priečinkov", + "mark_all_confirm_title": "Označiť všetky priečinky ako prečítané", + "mark_all_confirm_message": "Označiť každú neprečítanú správu vo vašom osobnom účte ako prečítanú?", + "delete_confirm_title": "Zmazať priečinok", + "delete_confirm_message": "Trvalo zmazať priečinok \"{name}\"? Túto akciu nie je možné vrátiť.", + "prompt_new_subfolder": "Zadajte názov nového podpriečinka.", + "prompt_new_folder": "Zadajte názov nového priečinka.", + "prompt_rename": "Zadajte nový názov tohto priečinka.", + "placeholder_folder_name": "Názov priečinka", + "create": "Vytvoriť", + "rename_confirm": "Premenovať", + "toast_marked_read": "Priečinok označený ako prečítaný", + "toast_marked_read_count": "Označených {count, plural, one {1 správa} other {# správ}} ako prečítaných", + "toast_already_read": "Žiadne neprečítané správy", + "toast_marked_all_read": "Všetky priečinky označené ako prečítané", + "toast_emptied": "Priečinok vyprázdnený", + "toast_folder_created": "Priečinok vytvorený", + "toast_folder_renamed": "Priečinok premenovaný", + "toast_folder_deleted": "Priečinok zmazaný", + "toast_error_mark_read": "Nepodarilo sa označiť ako prečítané", + "toast_error_empty": "Nepodarilo sa vyprázdniť priečinok", + "toast_error_create": "Nepodarilo sa vytvoriť priečinok", + "toast_error_rename": "Nepodarilo sa premenovať priečinok", + "toast_error_delete": "Nepodarilo sa zmazať priečinok", + "toast_error_delete_has_children": "Priečinok obsahuje podpriečinky. Najprv ich odstráňte.", + "toast_error_delete_has_email": "Priečinok nie je prázdny. Najprv ho vyprázdnite.", + "delete_with_contents_title": "Priečinok nie je prázdny", + "delete_with_contents_message": "Priečinok „{name}“ stále obsahuje {count, plural, one {1 správu} few {# správy} other {# správ}}. Odstrániť priečinok aj všetky jeho správy? Túto akciu nie je možné vrátiť späť.", + "delete_with_contents_confirm": "Odstrániť priečinok a správy" + }, + "shortcuts": { + "title": "Klávesové skratky", + "tip": "Kedykoľvek stlačte ? na zobrazenie tejto nápovedy", + "sections": { + "navigation": "Navigácia", + "actions": "Akcie správy", + "global": "Globálne", + "threads": "Vlákna", + "composer": "Editor správ" + }, + "navigation": { + "next_email": "Ďalší e-mail", + "previous_email": "Predchádzajúci e-mail", + "open_email": "Otvoriť e-mail", + "close_email": "Zavrieť / Zrušiť výber", + "go_to_folder": "Prejsť do priečinka", + "next_unread": "Ďalší neprečítaný e-mail", + "no_more_unread": "Nižšie v zozname už nie sú žiadne neprečítané e-maily" + }, + "actions": { + "reply": "Odpovedať", + "reply_all": "Odpovedať všetkým", + "forward": "Preposlať", + "star": "Prepnúť hviezdičku", + "archive": "Archivovať", + "delete": "Odstrániť", + "mark_unread": "Označiť ako neprečítané", + "mark_read": "Označiť ako prečítané", + "toggle_spam": "Nahlásiť spam / Nie je spam" + }, + "global": { + "compose": "Napísať nový e-mail", + "search": "Prejsť na hľadanie", + "help": "Zobraziť skratky", + "refresh": "Obnoviť e-maily", + "select_all": "Vybrať všetko", + "global_search": "Hľadať všade", + "extend_selection": "Rozšíriť výber nadol / nahor" + }, + "threads": { + "expand_collapse": "Rozbaliť/zbaliť vlákno", + "select_mountain_view": "Vybrať konverzáciu" + }, + "composer": { + "send": "Odoslať e-mail", + "schedule_send": "Naplánovať odoslanie", + "template_picker": "Otvoriť výber šablón" + } + }, + "threads": { + "messages_one": "{count} správa", + "messages_other": "{count} správ", + "messages_tooltip": "{count, plural, one {# správa v tejto konverzácii} other {# správ v tejto konverzácii}}", + "expand": "Rozbaliť konverzáciu", + "collapse": "Zbaliť konverzáciu", + "loading": "Načítavanie konverzácie...", + "mark_read": "Označiť konverzáciu ako prečítanú", + "mark_unread": "Označiť konverzáciu ako neprečítanú", + "archive": "Archivovať konverzáciu", + "delete": "Odstrániť konverzáciu", + "star": "Označiť konverzáciu hviezdičkou", + "unstar": "Odstrániť hviezdičku z konverzácie", + "toggle_thread": "Prepnúť zobrazenie vlákna" + }, + "identities": { + "modal_title": "Správa identít odosielateľa", + "create_new": "Vytvoriť novú identitu", + "edit_identity": "Upraviť identitu", + "delete_confirm": "Odstrániť túto identitu? Túto akciu nie je možné vrátiť.", + "cannot_delete": "Túto identitu nie je možné odstrániť", + "primary_identity": "Hlavná", + "set_as_primary": "Nastaviť ako hlavnú", + "no_identities": "Neboli nájdené žiadne identity", + "display": { + "reply_to": "Odpovedať na:", + "bcc": "Skrytá kópia:", + "signature": "Podpis:", + "preview": "Náhľad:" + }, + "validation_errors": { + "invalid_emails": "Neplatné e-mailové adresy: {emails}", + "unknown_error": "Neznáma chyba" + }, + "form": { + "name_label": "Zobrazované meno", + "name_placeholder": "napr. Pracovný e-mail, Súkromný", + "name_required": "Meno je povinné", + "email_label": "E-mailová adresa", + "email_placeholder": "vas.email@example.com", + "email_required": "E-mailová adresa je povinná", + "email_invalid": "Zadajte platnú e-mailovú adresu", + "email_immutable": "E-mailovú adresu nie je možné po vytvorení zmeniť", + "reply_to_label": "Odpovedať na (voliteľné)", + "reply_to_placeholder": "iny@example.com", + "bcc_label": "Automatická skrytá kópia (voliteľné)", + "bcc_placeholder": "archiv@example.com", + "text_signature_label": "Textový podpis", + "html_signature_label": "HTML podpis", + "signature_byte_counter": "{bytes} / {max} bajtov", + "signature_byte_limit_reached": "Dosiahnutý limit servera", + "save": "Uložiť identitu", + "cancel": "Zrušiť", + "creating": "Vytváranie...", + "updating": "Aktualizovanie..." + }, + "sub_address": { + "button_tooltip": "Použiť podadresu", + "popover_title": "Pridať štítok podadresy", + "tag_input_placeholder": "Zadajte štítok (napr. nakupy)", + "preview_label": "Náhľad:", + "recent_tags": "Nedávne štítky", + "suggested_tags": "Odporúčané", + "use_address": "Použiť túto adresu", + "invalid_tag": "Štítok môže obsahovať iba písmená, číslice a pomlčky", + "tag_too_long": "Štítok môže mať maximálne 30 znakov", + "help_text": "Správy odoslané na adresu pouzivatel{delimiter}stitok@domena.sk budú doručené do vašej doručenej pošty", + "validation": { + "empty": "Štítok nesmie byť prázdny", + "too_long": "Štítok môže mať maximálne {max} znakov", + "invalid_chars": "Štítok môže obsahovať iba písmená, číslice a pomlčky" + } + }, + "badge": { + "sent_via": "cez", + "sub_address_tag": "Odoslané pomocou podadresy: {tag}", + "identity_name": "Odoslané pomocou identity: {name}", + "identity_short": "cez {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Odstrániť", + "delete_confirm_title": "Odstrániť identitu" + }, + "templates": { + "picker_title": "Vyberte šablónu", + "search_placeholder": "Hľadať šablóny...", + "section_favorites": "Obľúbené", + "section_recent": "Nedávne", + "section_uncategorized": "Ostatné", + "no_templates": "Zatiaľ žiadne šablóny", + "no_results": "Neboli nájdené žiadne šablóny", + "fill_placeholders": "Vyplniť hodnoty premenných", + "enter_value": "Zadajte hodnotu...", + "preview": "Náhľad", + "insert_with_values": "Vložiť s hodnotami", + "insert_raw": "Vložiť bez zmien", + "copy_suffix": "(kópia)", + "placeholder": "Premenná", + "placeholders": { + "recipient_name": "Meno príjemcu", + "company": "Názov spoločnosti", + "date": "Aktuálny dátum", + "day_of_week": "Deň v týždni", + "sender_name": "Vaše meno" + } + }, + "contacts": { + "directory": { + "title": "Adresár organizácie", + "search_placeholder": "Hľadať meno alebo e-mailovú adresu", + "refresh": "Obnoviť adresár", + "retry": "Skúsiť znova", + "loading": "Načítava sa adresár…", + "unavailable_title": "Žiadny adresár", + "unavailable_hint": "Váš poštový server neponúka adresár účtov.", + "forbidden_title": "Adresár nie je dostupný", + "forbidden_hint": "Váš poštový server tomuto účtu nepovoľuje prehliadať adresár. Obráťte sa na správcu.", + "error_title": "Adresár sa nepodarilo načítať", + "error_hint": "Skontrolujte pripojenie a skúste to znova.", + "empty_title": "Adresár je prázdny", + "empty_hint": "Váš poštový server neuvádza žiadne účty s e-mailovou adresou.", + "no_match": "Hľadaniu nezodpovedá nikto v adresári", + "refresh_failed": "Adresár sa nepodarilo obnoviť. Zobrazuje sa naposledy načítaný zoznam.", + "select_page": "Vybrať všetkých na tejto stránke", + "deselect_page": "Zrušiť výber na tejto stránke", + "select_entry": "Vybrať {name}", + "range": "{from}–{to} z {total}", + "page": "Strana {page} z {count}", + "previous_page": "Predchádzajúca strana", + "next_page": "Ďalšia strana", + "picker_title": "Vybrať z adresára", + "cancel": "Zrušiť", + "selected_count": "Vybrané: {count}", + "add_to": "Pridať do Komu", + "add_cc": "Pridať do Kópia", + "add_bcc": "Pridať do Skrytá kópia", + "select_hint": "Vyberte osobu a zobrazte podrobnosti", + "write_email": "Napísať e-mail", + "add_to_contacts": "Pridať do kontaktov", + "email": "E-mail", + "read_only_note": "Položky adresára pochádzajú z vášho poštového servera a nedajú sa tu upraviť.", + "type": { + "individual": "Osoba", + "group": "Skupina", + "resource": "Prostriedok", + "location": "Miesto", + "other": "Iné" + } + }, + "title": "Kontakty", + "search_placeholder": "Hľadať kontakty...", + "create_new": "Nový kontakt", + "no_category": "Bez kategórie", + "rename_category": "Premenovať kategóriu", + "category_name_label": "Názov kategórie", + "category_renamed": "Kategória bola premenovaná", + "category_rename_failed": "Premenovanie kategórie zlyhalo", + "category_added": "Kontakt pridaný do {name}", + "category_added_plural": "{count} kontaktov bolo pridaných do {name}", + "empty_state": "Žiadne kontakty", + "empty_state_title": "Žiadne kontakty", + "empty_state_subtitle": "Vytvorte prvý kontakt alebo importujte zo súboru vCard", + "empty_search": "Hľadaniu nezodpovedajú žiadne kontakty", + "empty_search_hint": "Skúste iný hľadaný výraz", + "empty_filtered": "Žiadne kontakty nezodpovedajú filtrom", + "empty_filtered_hint": "Skúste filtre upraviť alebo zrušiť", + "clear_search": "Vymazať hľadanie", + "import_vcard": "Importovať vCard", + "delete_confirm_title": "Odstrániť kontakt", + "delete_confirm": "Naozaj chcete odstrániť tento kontakt?", + "local_mode": "Kontakty sú uložené lokálne (server nepodporuje JMAP Contacts)", + "back_to_contacts": "Späť na kontakty", + "back_to_email": "Späť na e-mail", + "open_categories": "Otvoriť kategórie", + "tabs": { + "all": "Všetky", + "groups": "Skupiny" + }, + "shared": { + "title": "Zdieľané" + }, + "address_books": { + "create": "Nový adresár", + "created": "Adresár vytvorený", + "create_failed": "Nepodarilo sa vytvoriť adresár", + "title": "Moje adresáre", + "shared_prefix": "Zdieľané: {name}", + "moved": "Kontakt presunutý do {name}", + "moved_plural": "{count} kontaktov bolo presunutých do {name}", + "move_failed": "Presun kontaktu zlyhal", + "address_book": "Adresár", + "rename": "Premenovať adresár", + "name_label": "Názov adresára", + "renamed": "Adresár bol premenovaný", + "rename_failed": "Premenovanie adresára zlyhalo", + "default": "Predvolený", + "set_default": "Nastaviť ako predvolený", + "default_updated": "Predvolený adresár aktualizovaný", + "set_default_failed": "Nepodarilo sa nastaviť predvolený adresár", + "manage": "Spravovať adresáre", + "share": "Zdieľať adresár", + "new_contact_in_book": "Nový kontakt v tomto adresári", + "delete": "Zmazať adresár", + "confirm_delete": "Zmazať \"{name}\"? Všetky kontakty v tomto adresári budú odstránené.", + "deleted": "Adresár zmazaný", + "delete_failed": "Adresár sa nepodarilo zmazať" + }, + "detail": { + "emails": "E-mailové adresy", + "phones": "Telefónne čísla", + "organizations": "Organizácie", + "addresses": "Adresy", + "notes": "Poznámky", + "titles": "Tituly a roly", + "online_services": "Online služby", + "anniversaries": "Výročia", + "personal_info": "Osobné informácie", + "languages": "Jazyky", + "categories": "Kategórie", + "related_contacts": "Súvisiace kontakty", + "crypto_keys": "Kryptografické kľúče", + "cert_issuer": "Vydavateľ", + "cert_expires": "Vyprší", + "cert_expired": "Vypršala platnosť", + "cert_fingerprint": "Odtlačok", + "cert_algorithm": "Algoritmus", + "import_to_smime": "Importovať do S/MIME", + "cert_already_imported": "Už importované do S/MIME", + "cert_imported": "Certifikát bol importovaný do úložiska S/MIME", + "cert_import_failed": "Import certifikátu zlyhal", + "no_contact_selected": "Vyberte kontakt na zobrazenie podrobností", + "compose_email": "Napísať e-mail", + "copy_email": "Kopírovať e-mail", + "copy_phone": "Kopírovať telefón", + "copy_url": "Kopírovať URL", + "copied": "Skopírované do schránky", + "copy_failed": "Kopírovanie do schránky zlyhalo", + "created": "Vytvorené", + "updated": "Posledná aktualizácia", + "timezone": "Časové pásmo", + "anniversary_birth": "Narodeniny", + "anniversary_death": "Úmrtie", + "anniversary_wedding": "Výročie svadby", + "anniversary_other": "Ostatné", + "personal_expertise": "Odbornosť", + "personal_hobby": "Koníčky", + "personal_interest": "Záujmy", + "personal_other": "Ostatné", + "gender": "Pohlavie", + "gender_masculine": "Muž", + "gender_feminine": "Žena", + "gender_other": "Iné", + "gender_none": "Neuplatňuje sa", + "gender_unknown": "Neznáme", + "calendar": "Kalendár", + "calendar_uri": "URL kalendára", + "scheduling_uri": "URL plánovania", + "freebusy_uri": "URL voľný/obsadený", + "section_contact": "Kontaktné údaje", + "section_work": "Práca", + "section_personal": "Osobné", + "email_default_label": "E-mail", + "phone_default_label": "Telefón", + "address_default_label": "Adresa", + "online_service_default_label": "Online", + "organization_label": "Organizácia", + "title_label": "Titul", + "role_label": "Rola", + "language_label": "Jazyk", + "related_default_label": "Súvisiace", + "more_actions": "Viac akcií", + "age_years": "{count, plural, one {1 rok} other {# rokov}}", + "years_since": "{count, plural, one {1 rok} other {# rokov}}" + }, + "activity": { + "recent_emails": "Nedávne e-maily", + "upcoming_events": "Nadchádzajúce udalosti", + "no_emails": "Žiadne nedávne e-maily", + "no_events": "Žiadne nadchádzajúce udalosti", + "no_subject": "(Bez predmetu)", + "no_title": "(Bez názvu)", + "load_failed": "Nepodarilo sa načítať", + "unknown_sender": "Neznámy odosielateľ", + "all_day": "Celý deň" + }, + "form": { + "create_title": "Nový kontakt", + "edit_title": "Upraviť kontakt", + "section_address_book": "Adresár", + "select_address_book": "Vyberte adresár...", + "section_identity": "Meno a identita", + "contact_type": "Typ kontaktu", + "type_person": "Osoba", + "type_organization": "Organizácia", + "section_work": "Práca a organizácia", + "prefix": "Titul", + "prefix_placeholder": "Dr., Pán, Pani", + "given_name": "Meno", + "middle_name": "Stredné meno", + "surname": "Priezvisko", + "suffix": "Prípona", + "suffix_placeholder": "Jr., Sr., Ph.D.", + "nickname": "Prezývka", + "nickname_placeholder": "Prezývka", + "email": "E-mail", + "email_placeholder": "email@example.com", + "phone": "Telefón", + "phone_placeholder": "+421 123 456 789", + "phone_type": "Typ", + "phone_voice": "Hlasový", + "phone_cell": "Mobilný", + "phone_fax": "Fax", + "phone_pager": "Pager", + "phone_video": "Video", + "phone_text": "SMS", + "organization": "Organizácia", + "organization_placeholder": "Názov spoločnosti", + "department": "Oddelenie", + "department_placeholder": "Oddelenie", + "job_title": "Pozícia", + "job_title_placeholder": "napr. Softvérový inžinier", + "role": "Rola", + "role_placeholder": "napr. Vedúci tímu", + "addresses": "Adresy", + "add_address": "Pridať adresu", + "street": "Ulica", + "city": "Mesto", + "region": "Štát / Kraj", + "postcode": "PSČ", + "country": "Krajina", + "online_services": "Online služby", + "add_online_service": "Pridať online službu", + "url_placeholder": "https://...", + "service_placeholder": "Služba", + "anniversaries": "Výročia", + "add_anniversary": "Pridať dátum", + "anniversary_birth": "Narodeniny", + "anniversary_wedding": "Výročie svadby", + "anniversary_death": "Úmrtie", + "anniversary_other": "Ostatné", + "personal_info": "Osobné informácie", + "add_personal_info": "Pridať záznam", + "personal_info_placeholder": "napr. Fotografia", + "personal_expertise": "Odbornosť", + "personal_hobby": "Koníčky", + "personal_interest": "Záujmy", + "personal_other": "Ostatné", + "level": "Úroveň", + "level_high": "Vysoká", + "level_medium": "Stredná", + "level_low": "Nízka", + "categories": "Kategórie", + "categories_placeholder": "napr. Rodina, Priatelia, Kolegovia", + "categories_hint": "Píšte na vyhľadanie alebo pridanie kategórií", + "category_add": "Pridať", + "note": "Poznámky", + "note_placeholder": "Pridať poznámku...", + "gender": "Pohlavie", + "gender_sex": "Biologické pohlavie", + "gender_male": "Muž", + "gender_female": "Žena", + "gender_other": "Iné", + "gender_none": "Neuplatňuje sa", + "gender_unknown": "Neznáme", + "gender_identity": "Rodová identita", + "gender_identity_placeholder": "Rodová identita...", + "calendar": "Kalendár", + "calendar_uri": "URL kalendára", + "scheduling_uri": "URL plánovania", + "freebusy_uri": "URL voľný/obsadený", + "context_work": "Pracovné", + "context_private": "Súkromné", + "add_email": "Pridať e-mail", + "add_phone": "Pridať telefón", + "save": "Uložiť", + "cancel": "Zrušiť", + "creating": "Vytváranie...", + "updating": "Aktualizovanie...", + "name_required": "Zadajte meno, priezvisko alebo organizáciu", + "email_invalid": "Zadajte platnú e-mailovú adresu", + "email_error_inline": "Neplatný formát e-mailovej adresy", + "save_failed": "Uloženie kontaktu zlyhalo", + "delete": "Odstrániť", + "upload_photo": "Nahrať fotku", + "remove_photo": "Odstrániť fotku", + "photo_hint": "JPG alebo PNG, max. 10 MB. Veľkosť sa upraví.", + "photo_too_large": "Obrázok je príliš veľký (max. 10 MB)", + "photo_invalid": "Neplatný formát obrázka", + "change_photo": "Zmeniť" + }, + "groups": { + "create": "Nová skupina", + "edit": "Upraviť skupinu", + "empty": "Žiadne skupiny", + "delete_confirm_title": "Odstrániť skupinu", + "delete_confirm": "Naozaj chcete odstrániť túto skupinu?", + "name_label": "Názov skupiny", + "name_placeholder": "napr. Tím, Rodina", + "name_required": "Názov skupiny je povinný", + "save_failed": "Uloženie skupiny zlyhalo", + "members_label": "Členovia", + "search_members": "Hľadať kontakty na pridanie...", + "no_members": "Táto skupina nemá žiadnych členov", + "member_count": "{count, plural, =0 {Žiadni členovia} one {1 člen} other {# členov}}", + "send_email": "Odoslať e-mail skupine", + "send_email_to": "Komu", + "send_email_cc": "Kópia", + "send_email_bcc": "Skrytá kópia", + "no_member_emails": "Táto skupina nemá žiadnych členov s e-mailovou adresou." + }, + "import": { + "title": "Importovať kontakty", + "drop_hint": "Kliknite na výber súboru vCard", + "file_types": "súbory .vcf alebo .vcard", + "no_contacts": "V súbore neboli nájdené žiadne kontakty", + "parse_error": "Spracovanie súboru vCard zlyhalo", + "found": "{count, plural, one {Nájdený 1 kontakt} other {Nájdených # kontaktov}}", + "duplicate": "Duplikát", + "select_all": "Vybrať všetko", + "deselect_all": "Zrušiť výber všetkého", + "selected": "{count, plural, one {1 vybraný} other {# vybraných}}", + "import_button": "Importovať", + "importing": "Importovanie...", + "success": "{count, plural, one {Importovaný 1 kontakt} other {Importovaných # kontaktov}}", + "failed": "Import zlyhal", + "close": "Zavrieť", + "file_too_large": "Súbor je príliš veľký (max. 5 MB)" + }, + "export": { + "title": "Exportovať kontakty", + "success": "{count, plural, one {Exportovaný 1 kontakt} other {Exportovaných # kontaktov}}" + }, + "bulk": { + "selected": "{count, plural, one {1 vybraný} other {# vybraných}}", + "select_all": "Vybrať všetko", + "delete": "Odstrániť", + "delete_confirm_title": "Odstrániť kontakty", + "delete_confirm": "Odstrániť {count, plural, one {1 kontakt} other {# kontaktov}}?", + "deleted": "{count, plural, one {Odstránený 1 kontakt} other {Odstránených # kontaktov}}", + "add_to_group": "Pridať do skupiny", + "choose_group": "Vyberte skupinu", + "adding_contacts": "Pridávanie {count, plural, one {1 kontaktu} other {# kontaktov}}", + "added_to_group": "Kontakty boli pridané do skupiny", + "export": "Exportovať", + "clear": "Zrušiť výber" + }, + "toast": { + "created": "Kontakt bol vytvorený", + "updated": "Kontakt bol aktualizovaný", + "deleted": "Kontakt bol odstránený", + "error_create": "Vytvorenie kontaktu zlyhalo", + "error_update": "Aktualizácia kontaktu zlyhala", + "error_delete": "Odstránenie kontaktu zlyhalo" + }, + "context_menu": { + "open": "Otvoriť", + "edit": "Upraviť", + "send_email": "Poslať e-mail", + "add_to_group": "Pridať do skupiny", + "export_vcard": "Exportovať ako vCard", + "delete": "Zmazať", + "call": "Zavolať", + "duplicate": "Duplikovať", + "print": "Vytlačiť" + }, + "filters": { + "toggle": "Filtre", + "select": "Vybrať", + "clear": "Vymazať", + "close": "Zavrieť", + "title": "Pokročilé filtre", + "organization": "Firma", + "organization_placeholder": "napr. Acme Corp", + "job_title": "Pozícia", + "job_title_placeholder": "napr. Dizajnér", + "location": "Lokalita", + "location_placeholder": "Mesto alebo krajina", + "email_domain": "E-mailová doména", + "email_domain_placeholder": "example.com", + "birthday_month": "Narodeniny v", + "any_month": "Ktorýkoľvek mesiac", + "has_email": "Má e-mail", + "has_phone": "Má telefón", + "has_photo": "Má fotku", + "sort_by": "Zoradiť podľa", + "sort_first_name": "Meno", + "sort_last_name": "Priezvisko" + } + }, + "calendar": { + "title": "Kalendár", + "back_to_email": "Späť na poštu", + "back_to_month": "Späť na mesiac", + "my_calendars": "Kalendáre", + "birthday_calendar": "Narodeniny", + "mini_calendar_change": "Kliknutím zmeníte mesiac", + "views": { + "month": "Mesiac", + "week": "Týždeň", + "day": "Deň", + "agenda": "Agenda", + "today": "Dnes", + "month_hint": "Mesiac (m)", + "week_hint": "Týždeň (w)", + "day_hint": "Deň (d)", + "agenda_hint": "Agenda (a)", + "tasks": "Úlohy", + "tasks_hint": "Úlohy (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# skoršia udalosť} few {# skoršie udalosti} many {# skoršej udalosti} other {# skorších udalostí}}", + "hidden_after": "{count, plural, one {# neskoršia udalosť} few {# neskoršie udalosti} many {# neskoršej udalosti} other {# neskorších udalostí}}", + "show_all_hours": "Zobraziť všetky hodiny", + "show_display_hours": "Zobraziť len {start} – {end}", + "create": "Vytvoriť udalosť", + "edit": "Upraviť udalosť", + "delete": "Odstrániť udalosť", + "details": "Podrobnosti udalosti", + "no_events": "Žiadne udalosti", + "all_day": "Celý deň", + "more": "+{count} ďalších", + "show_more": "Zobraziť viac", + "show_less": "Zobraziť menej", + "no_title": "(Bez názvu)", + "resize": "Zmeniť veľkosť udalosti", + "duplicate": "Duplikovať", + "today_header": "Dnes", + "tomorrow_header": "Zajtra", + "agenda_show_earlier": "Zobraziť skoršie udalosti", + "agenda_loading": "Načítava sa…", + "agenda_range_start": "Udalosti od {date}", + "agenda_range_end": "Udalosti do {date}", + "export_ics": "Exportovať ako .ics", + "copy_title": "Kopírovať názov", + "copy_link": "Kopírovať odkaz na stretnutie", + "new_event": "Nová udalosť", + "new_all_day_event": "Nová celodenná udalosť", + "new_task": "Nová úloha", + "go_to_today": "Prejsť na dnešok" + }, + "detail": { + "add_note": "Pridať poznámku...", + "save_note": "Uložiť", + "note_saved": "Poznámka pridaná", + "open_link": "Otvoriť odkaz", + "copy_location": "Kopírovať miesto", + "meeting_link": "Odkaz na stretnutie", + "tentative": "Nezáväzne", + "cancelled": "Zrušené", + "delete_confirm": "Odstrániť túto udalosť?" + }, + "form": { + "title": "Názov", + "description": "Popis", + "location": "Miesto", + "meeting_link": "Odkaz na stretnutie", + "start_date": "Dátum začiatku", + "end_date": "Dátum ukončenia", + "start_time": "Čas začiatku", + "end_time": "Čas ukončenia", + "all_day_event": "Celodenná udalosť", + "calendar_select": "Kalendár", + "save": "Uložiť", + "cancel": "Zrušiť", + "delete_confirm": "Naozaj chcete odstrániť túto udalosť?", + "color": "Farba" + }, + "participants": { + "availability": { + "title": "Dostupnosť", + "free": "Voľný", + "busy": "Zaneprázdnený", + "tentative": "Predbežne", + "unknown": "Nie je používateľom tohto servera", + "checking": "Overuje sa…" + }, + "title": "Účastníci", + "add": "Pridať účastníka", + "organizer": "Organizátor", + "attendee": "Účastník", + "accepted": "Prijaté", + "declined": "Odmietnuté", + "tentative": "Nezáväzne", + "needs_action": "Vyžaduje akciu", + "remove": "Odstrániť", + "edit": "Upraviť", + "email_placeholder": "Pridajte e-mailovú adresu alebo vyhľadajte kontakty", + "send_invitations": "Odoslať pozvánky účastníkom", + "status_summary": "{accepted} prijatých, {pending} čaká", + "invited_by": "Pozvánka od {name}", + "respond_below": "Odpovedzte pomocou tlačidiel nižšie", + "rsvp_label": "Vaša odpoveď", + "cancel_notification": "Účastníci budú upozornení na zrušenie", + "you_organizer": "Ste organizátorom", + "you_attendee": "Ste účastníkom", + "no_participants": "Žiadni účastníci", + "count": "{count, plural, one {# účastník} other {# účastníkov}}" + }, + "recurrence": { + "title": "Opakovanie", + "none": "Neopakuje sa", + "daily": "Denne", + "weekly": "Týždenne", + "monthly": "Mesačne", + "yearly": "Ročne", + "every_n_days": "Každých {count} dní", + "every_n_weeks": "Každých {count} týždňov", + "every_n_months": "Každých {count} mesiacov", + "until": "Do", + "occurrences": "{count} opakovaní", + "custom": "Vlastné…", + "edit_custom": "Upraviť vlastné opakovanie", + "every_n_years": "Každých {count} rokov", + "on_days": "v {days}", + "on_day_n": "v deň {day}", + "on_the_nth": "{nth} {day}", + "in_month": "v mesiaci {month}", + "nth_1": "prvý", + "nth_2": "druhý", + "nth_3": "tretí", + "nth_4": "štvrtý", + "nth_last": "posledný", + "editor_freq_day": "Deň", + "editor_freq_week": "Týždeň", + "editor_freq_month": "Mesiac", + "editor_freq_year": "Rok", + "editor_repeats_on": "Opakuje sa", + "editor_every": "Každých", + "editor_unit_days": "dní", + "editor_unit_weeks": "týždňov", + "editor_unit_months": "mesiacov", + "editor_unit_years": "rokov", + "editor_on_day": "v deň", + "editor_on_the": "v", + "editor_in": "v mesiaci", + "editor_ends": "Končí", + "editor_never": "Nikdy", + "editor_ends_on": "Dňa", + "editor_ends_after": "Po", + "editor_occurrences": "opakovaniach" + }, + "recurrence_scope": { + "edit_title": "Upraviť opakujúcu sa udalosť", + "delete_title": "Odstrániť opakujúcu sa udalosť", + "description": "Toto je opakujúca sa udalosť. Ktoré udalosti chcete upraviť?", + "this_event": "Iba túto udalosť", + "this_and_future": "Túto a všetky nasledujúce udalosti", + "all_events": "Všetky udalosti", + "cancel": "Zrušiť", + "save": "Uložiť", + "delete": "Odstrániť", + "rsvp_title": "Odpovedať na opakujúcu sa udalosť", + "rsvp_description": "Toto je opakujúca sa udalosť. Na ktoré udalosti sa vzťahuje vaša odpoveď?", + "respond": "Odpovedať" + }, + "alerts": { + "title": "Pripomenutia", + "none": "Žiadne pripomenutia", + "at_time": "V čase udalosti", + "minutes_before": "{count, plural, one {# minútu pred} other {# minút pred}}", + "hours_before": "{count, plural, one {# hodinu pred} other {# hodín pred}}", + "days_before": "{count, plural, one {# deň pred} other {# dní pred}}", + "weeks_before": "{count, plural, one {# týždeň pred} other {# týždňov pred}}", + "unit_minutes_before": "minút pred", + "unit_hours_before": "hodín pred", + "unit_days_before": "dní pred", + "unit_weeks_before": "týždňov pred", + "add": "Pridať pripomenutie", + "remove": "Odstrániť pripomenutie", + "amount": "Množstvo pripomenutia", + "unit": "Jednotka pripomenutia", + "action": "Typ pripomenutia", + "action_display": "Upozornenie", + "action_email": "E-mail", + "by_email": "{label} e-mailom", + "kept": "{count, plural, one {Zachová sa # ďalšie pripomenutie, ktoré tu nemožno upraviť} few {Zachovajú sa # ďalšie pripomenutia, ktoré tu nemožno upraviť} other {Zachová sa # ďalších pripomenutí, ktoré tu nemožno upraviť}}", + "other": "{count, plural, one {# ďalšie pripomenutie} few {# ďalšie pripomenutia} other {# ďalších pripomenutí}}", + "calendar_defaults": "Predvolené pripomenutia kalendára", + "customize": "Prispôsobiť", + "use_defaults": "Použiť predvolené pripomenutia kalendára" + }, + "settings": { + "limit_hours": "Obmedziť zobrazené hodiny", + "limit_hours_desc": "V dennom a týždennom zobrazení ukazovať len tieto hodiny. Udalosti mimo nich sú dostupné cez šípky hore a dole pri každom dni.", + "visible_hours": "Zobrazené hodiny", + "visible_hours_start": "Začiatok zobrazených hodín", + "visible_hours_end": "Koniec zobrazených hodín", + "hide_non_working_days": "Skryť nepracovné dni", + "hide_non_working_days_desc": "Vynechať z týždenného zobrazenia dni, keď nepracujete. Ich udalosti sa naďalej zobrazujú v mesačnom, dennom a agendovom zobrazení.", + "working_days": "Pracovné dni", + "title": "Nastavenia kalendára", + "default_view": "Predvolené zobrazenie", + "week_starts_on": "Týždeň začína v", + "time_format": "Formát času", + "default_calendar": "Predvolený kalendár", + "default_reminder": "Predvolené pripomenutie", + "time_format_12h": "12-hodinový", + "time_format_24h": "24-hodinový", + "notifications_enabled": "Oznámenia o udalostiach", + "notifications_enabled_desc": "Zobrazovať upozornenia na nadchádzajúce udalosti v kalendári", + "notification_sound": "Zvuk oznámenia", + "notification_sound_desc": "Prehrať zvuk pre upozornenia kalendára", + "invitation_parsing": "Analyzovať e-mailové pozvánky", + "invitation_parsing_desc": "Rozpoznávať pozvánky kalendára v prílohách e-mailov a zobrazovať akcie kalendára", + "show_time_in_month_view": "Zobraziť čas v mesačnom zobrazení", + "show_time_in_month_view_desc": "Zobrazovať časy udalostí v mesačnom zobrazení kalendára", + "show_week_numbers": "Zobraziť čísla týždňov", + "show_week_numbers_desc": "Zobrazovať čísla týždňov v minikalendári", + "calendar_free_scroll": "Voľné posúvanie", + "calendar_free_scroll_desc": "Plynulo prechádzať mesiace, týždne a dni namiesto jedného obdobia naraz", + "enable_tasks": "Povoliť úlohy", + "enable_tasks_desc": "Zobrazovať zobrazenie úloh v kalendári", + "show_tasks_on_calendar": "Zobraziť úlohy v kalendári", + "show_tasks_on_calendar_desc": "Zobrazovať úlohy v mesačnom, týždennom a dennom zobrazení kalendára", + "hover_preview": "Náhľad udalosti po prejdení", + "hover_preview_desc": "Zobrazovať vyskakovacie okno s podrobnosťami pri najetí myšou na udalosti v kalendári", + "hover_preview_instant": "Okamžite", + "hover_preview_delay_500ms": "Oneskorenie 0,5 sekundy", + "hover_preview_delay_1s": "Oneskorenie 1 sekunda", + "hover_preview_delay_2s": "Oneskorenie 2 sekundy", + "hover_preview_off": "Vypnuté", + "organizer_identity": "Organizovať pozvánky ako", + "organizer_identity_desc": "Adresa kalendára použitá ako organizátor, keď pozývate ľudí na udalosti.", + "organizer_identity_failed": "Nepodarilo sa zmeniť adresu organizátora", + "show_birthday_calendar": "Kalendár narodenín", + "show_birthday_calendar_desc": "Zobrazovať virtuálny kalendár s narodeninami z vašich kontaktov" + }, + "days": { + "monday": "Pondelok", + "tuesday": "Utorok", + "wednesday": "Streda", + "thursday": "Štvrtok", + "friday": "Piatok", + "saturday": "Sobota", + "sunday": "Nedeľa", + "mon": "Po", + "tue": "Ut", + "wed": "St", + "thu": "Št", + "fri": "Pi", + "sat": "So", + "sun": "Ne" + }, + "months": { + "jan": "jan", + "feb": "feb", + "mar": "mar", + "apr": "apr", + "may": "máj", + "jun": "jún", + "jul": "júl", + "aug": "aug", + "sep": "sep", + "oct": "okt", + "nov": "nov", + "dec": "dec", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Udalosť bola vytvorená", + "event_updated": "Udalosť bola aktualizovaná", + "event_deleted": "Udalosť bola odstránená", + "calendar_created": "Kalendár bol vytvorený", + "calendar_deleted": "Kalendár bol odstránený", + "event_move_error": "Presun udalosti zlyhal", + "event_resize_error": "Zmena veľkosti udalosti zlyhala", + "alert_title": "Nadchádzajúca udalosť", + "alert_now": "Začína teraz", + "alert_in_minutes": "Za {count} min", + "invitation_sent": "Pozvánky boli odoslané", + "invitations_denied_title": "Pozvánky nie je možné odoslať", + "invitations_denied": "Server odmietol odoslať pozvánky: {reason}. Uložiť udalosť bez ich odoslania?", + "save_without_invitations": "Uložiť bez pozvánok", + "rsvp_updated": "Odpoveď bola aktualizovaná", + "rsvp_error": "Aktualizácia odpovede zlyhala", + "event_duplicated": "Udalosť bola duplikovaná", + "event_error": "Uloženie udalosti zlyhalo", + "task_due": "Termín úlohy", + "event_exported": "Udalosť exportovaná", + "title_copied": "Názov skopírovaný", + "link_copied": "Odkaz skopírovaný" + }, + "status": { + "loading_calendars": "Načítavanie kalendárov...", + "loading_events": "Načítavanie udalostí..." + }, + "quick_create": { + "placeholder": "Názov novej udalosti", + "aria_label": "Rýchle vytvorenie udalosti", + "add_title": "Pridať názov", + "more_options": "Ďalšie možnosti" + }, + "nav_prev": "Predchádzajúci", + "nav_next": "Ďalší", + "nav_open_menu": "Otvoriť ponuku", + "import": { + "title": "Importovať kalendár", + "tab_file": "Súbor", + "tab_url": "URL", + "select_file": "Vyberte súbor .ics", + "drop_file": "alebo sem presuňte súbor", + "supported_formats": "Podporuje súbory iCalendar (.ics)", + "url_description": "Zadajte URL adresu externého zdroja iCalendar (.ics) na import udalostí.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Podporuje URL adresy iCalendar (.ics)", + "fetch": "Načítať", + "invalid_url": "Zadajte platnú URL adresu", + "url_fetch_failed": "Načítanie kalendára z URL adresy zlyhalo", + "parsing": "Analyzovanie súboru kalendára...", + "parsed_events": "Nájdených {count} udalostí", + "no_events": "V súbore neboli nájdené žiadne udalosti", + "select_all": "Vybrať všetko", + "deselect_all": "Zrušiť výber všetkého", + "target_calendar": "Importovať do kalendára", + "import_button": "Importovať vybrané", + "importing": "Importovanie udalostí...", + "success": "Úspešne importovaných {count} udalostí", + "error": "Import kalendára zlyhal", + "file_too_large": "Súbor prekračuje limit 10 MB", + "invalid_format": "Neplatný formát súboru kalendára" + }, + "webcal_action": { + "title": "Otvoriť odkaz kalendára", + "description": "Ako chcete použiť \"{name}\"?", + "import_title": "Jednorazovo importovať", + "import_description": "Načítať udalosti teraz a skopírovať ich do jedného z vašich kalendárov.", + "subscribe_title": "Odoberať", + "subscribe_description": "Automaticky synchronizovať tento kalendár ako samostatný kalendár.", + "cancel": "Zrušiť" + }, + "management": { + "title": "Správa kalendára", + "description": "Vytvárajte, premenúvajte a prispôsobujte si svoje kalendáre.", + "name": "Názov", + "name_placeholder": "Názov kalendára", + "color": "Farba", + "change_color": "Zmeniť farbu", + "random_color": "Nová náhodná farba", + "add_calendar": "Pridať kalendár", + "edit": "Upraviť", + "delete": "Odstrániť", + "save": "Uložiť", + "create": "Vytvoriť", + "cancel": "Zrušiť", + "default": "Predvolený", + "confirm_delete": "Odstrániť \"{name}\"? Všetky udalosti v tomto kalendári budú odstránené.", + "confirm_clear": "Vymazať všetky udalosti z \"{name}\"? Túto akciu nie je možné vrátiť.", + "set_default": "Nastaviť ako predvolený", + "default_updated": "Predvolený kalendár aktualizovaný", + "error_default": "Nepodarilo sa nastaviť predvolený kalendár", + "clear_events": "Vymazať udalosti", + "events_cleared": "Vymazaných {count} udalostí", + "error_clear": "Vymazanie udalostí kalendára zlyhalo", + "calendar_created": "Kalendár bol vytvorený", + "calendar_updated": "Kalendár bol aktualizovaný", + "calendar_deleted": "Kalendár bol odstránený", + "color_updated": "Farba kalendára bola aktualizovaná", + "error_create": "Vytvorenie kalendára zlyhalo", + "error_update": "Aktualizácia kalendára zlyhala", + "error_delete": "Odstránenie kalendára zlyhalo", + "caldav_url": "URL CalDAV", + "copy_url": "Kopírovať URL CalDAV", + "url_copied": "URL CalDAV skopírovaná do schránky", + "share": "Zdieľať kalendár", + "new_event_in_calendar": "Nová udalosť v tomto kalendári", + "shared_with": "Zdieľané s: {names}", + "shared_with_count": "{count, plural, one {Zdieľané s 1 osobou} few {Zdieľané s # osobami} many {Zdieľané s # osoby} other {Zdieľané s # osobami}}", + "move_up": "Posunúť nahor", + "move_down": "Posunúť nadol", + "kind": "Obsah", + "kind_events": "Udalosti", + "kind_tasks": "Úlohy", + "kind_both": "Udalosti a úlohy", + "kind_hint": "Určuje, ktoré aplikácie tento kalendár uvidia pri synchronizácii cez CalDAV. Neskôr to nemožno zmeniť.", + "default_alerts": "Predvolené pripomenutia", + "default_alerts_title": "Predvolené pripomenutia pre \"{name}\"", + "default_alerts_hint": "Nové udalosti v tomto kalendári dostanú tieto pripomenutia, ak ich v udalosti nezmeníte. Zmena tu neovplyvní existujúce udalosti. E-mailové pripomenutia odosiela poštový server.", + "default_alerts_timed": "Udalosti s časom", + "default_alerts_all_day": "Celodenné udalosti", + "default_alerts_updated": "Predvolené pripomenutia boli aktualizované" + }, + "subscription": { + "title": "Odber iCal", + "section_title": "Odbery iCal", + "description": "Odoberajte externý zdroj iCalendar. Udalosti budú automaticky synchronizované do samostatného kalendára.", + "url_label": "URL kalendára", + "url_placeholder": "https://example.com/calendar.ics alebo webcal://...", + "name_label": "Názov kalendára", + "name_placeholder": "napr. Štátne sviatky", + "color_label": "Farba", + "refresh_interval": "Interval obnovy", + "interval_15": "Každých 15 minút", + "interval_30": "Každých 30 minút", + "interval_60": "Každú hodinu", + "interval_360": "Každých 6 hodín", + "interval_1440": "Denne", + "subscribe": "Odoberať", + "subscribing": "Prihlasovanie na odber...", + "save": "Uložiť zmeny", + "saving": "Ukladanie...", + "edit": "Upraviť", + "edit_title": "Upraviť odber", + "updated": "Aktualizované \"{name}\"", + "update_error": "Aktualizácia odberu zlyhala", + "invalid_url": "Zadajte platnú URL adresu", + "success": "Odoberáte \"{name}\"", + "error": "Pridanie odberu zlyhalo", + "refresh": "Obnoviť teraz", + "refresh_success": "Odber bol obnovený", + "refresh_error": "Obnovenie odberu zlyhalo", + "unsubscribe": "Zrušiť odber", + "confirm_delete": "Zrušiť odber \"{name}\"? Kalendár a všetky jeho udalosti budú odstránené.", + "deleted": "Odber bol odstránený", + "delete_error": "Odstránenie odberu zlyhalo", + "last_refreshed": "Posledná aktualizácia: {time}" + }, + "tasks": { + "label": "Úlohy", + "no_tasks": "Žiadne úlohy", + "no_title": "(Bez názvu)", + "mark_complete": "Označiť ako dokončené", + "mark_incomplete": "Označiť ako nedokončené", + "filter_all": "Všetky", + "filter_pending": "Čakajúce", + "filter_completed": "Dokončené", + "filter_overdue": "Po termíne", + "show_completed": "Zobraziť dokončené", + "create": "Nová úloha", + "edit": "Upraviť úlohu", + "title_placeholder": "Názov úlohy", + "description_placeholder": "Pridať popis...", + "due_date": "Termín", + "include_time": "Zahrnúť čas", + "priority": "Priorita", + "priority_none": "Žiadna", + "priority_high": "Vysoká", + "priority_medium": "Stredná", + "priority_low": "Nízka", + "progress": "Stav", + "progress_needs_action": "Vyžaduje akciu", + "progress_in_process": "Prebieha", + "progress_completed": "Dokončené", + "progress_cancelled": "Zrušené", + "calendar": "Kalendár", + "alert": "Pripomenutie", + "alert_none": "Žiadne", + "alert_at_time": "V čase termínu", + "alert_5min": "5 minút pred", + "alert_15min": "15 minút pred", + "alert_30min": "30 minút pred", + "alert_1hr": "1 hodinu pred", + "alert_1day": "1 deň pred", + "delete": "Odstrániť", + "cancel": "Zrušiť", + "save": "Uložiť", + "quick_add_placeholder": "Pridať úlohu...", + "due_today": "Dnes", + "due_tomorrow": "Zajtra", + "overdue": "Po termíne" + }, + "toolbar": { + "more": "Ďalšie akcie", + "select_view": "Zmeniť zobrazenie, teraz {view}" + }, + "other_calendars": "Ďalšie kalendáre", + "calendar_options": "Možnosti pre {name}" + }, + "calendar_event_notifications": { + "someone": "Niekto", + "invited": "{name} vás pozval(a) na „{title}“", + "updated": "{name} aktualizoval(a) „{title}“", + "cancelled": "{name} zrušil(a) „{title}“", + "untitled": "Udalosť bez názvu" + }, + "share_notifications": { + "someone": "Niekto", + "shared": "{name} s vami zdieľa {kind} „{object}“", + "changed": "{name} zmenil(a) váš prístup k {kind} „{object}“", + "revoked": "{name} odobral(a) váš prístup k {kind} „{object}“", + "object": { + "folder": "priečinok", + "calendar": "kalendár", + "address_book": "adresár", + "files": "priečinok súborov" + } + }, + "sharing": { + "title": "Zdieľať \"{name}\"", + "description": "Udeľte prístup ďalším používateľom alebo skupinám na tomto serveri. Zmeny sa prejavia okamžite.", + "no_shares": "Zatiaľ nikomu nezdieľané.", + "add_person": "Pridať osobu alebo skupinu", + "search_placeholder": "Hľadať podľa mena alebo e-mailu…", + "loading_principals": "Načítavanie používateľov…", + "no_principals": "Neboli nájdení žiadni ďalší používatelia ani skupiny.", + "no_match": "Žiadne výsledky.", + "remove": "Odstrániť prístup", + "group": "Skupina", + "share_added": "Prístup udelený", + "share_updated": "Prístup aktualizovaný", + "share_removed": "Prístup odstránený", + "share_failed": "Aktualizácia zdieľania zlyhala", + "preset": { + "freeBusy": "Iba voľný/obsadený", + "read": "Iba čítanie", + "readWrite": "Čítanie a zápis", + "manager": "Správca", + "custom": "Vlastné" + } + }, + "advanced_search": { + "title": "Pokročilé hľadanie", + "from": "Od", + "from_placeholder": "E-mail alebo meno odosielateľa", + "to": "Komu", + "to_placeholder": "E-mail alebo meno príjemcu", + "subject": "Predmet", + "subject_placeholder": "Predmet obsahuje...", + "body": "Telo správy", + "body_placeholder": "Telo správy obsahuje...", + "folder": "Priečinok", + "all_folders": "Všetky priečinky", + "all_folders_except_spam_trash": "Všetky priečinky okrem spamu a koša", + "has_attachment": "Prílohy", + "date_after": "Po", + "date_before": "Pred", + "size_min": "Väčšie ako (KB)", + "size_max": "Menšie ako (KB)", + "starred": "S hviezdičkou", + "unread": "Neprečítané", + "read": "Prečítané", + "yes": "Áno", + "no": "Nie", + "clear": "Vymazať", + "clear_all": "Vymazať všetko", + "filters_active": "{count} filter", + "filters_active_plural": "{count} filtre", + "toggle_filters": "Viac", + "search_hint": "Použite pokročilé filtre na presné vyhľadávanie", + "advanced_filters_tooltip": "Pokročilé filtre hľadania", + "results_found": "{count, plural, =0 {Neboli nájdené žiadne výsledky} one {# výsledok nájdený} other {# výsledkov nájdených}}", + "results_found_more": "{count}+ výsledkov nájdených", + "suggestions_recent": "Nedávne vyhľadávania", + "suggestions_people": "Ľudia", + "suggestions_from": "Hľadať e-maily od {address}", + "suggestions_to": "Hľadať e-maily pre {address}", + "suggestions_remove_recent": "Odstrániť z nedávnych vyhľadávaní", + "has_words": "Obsahuje slová", + "size": "Veľkosť", + "date_within": "Dátum", + "search_in": "Hľadať v", + "has_attachment_check": "Má prílohu", + "search": "Hľadať" + }, + "welcome": { + "title": "Vitajte vo svojej poštovej schránke", + "tip_compose": "Stlačte c na napísanie nového e-mailu", + "tip_shortcuts": "Stlačte ? na zobrazenie všetkých klávesových skratiek", + "tip_sidebar": "Kalendár, Kontakty a Nastavenia nájdete v postrannom paneli", + "tip_settings": "Prispôsobte si aplikáciu v Nastaveniach", + "tip_compose_touch": "Ťuknutím na tlačidlo s ceruzkou napíšete nový e-mail", + "tip_sidebar_touch": "Kalendár, Kontakty a Nastavenia nájdete na lište dole", + "got_it": "Rozumiem", + "settings": "Nastavenia", + "dismiss": "Zavrieť", + "start_tour": "Spustiť sprievodcu" + }, + "demo_welcome": { + "title": "Vitajte v Bulwark Mail", + "description": "Preskúmajte plnohodnotného webového e-mailového klienta – priamo v prehliadači. Všetky dáta zostávajú na vašom zariadení.", + "feature_email": "Čítajte a píšte e-maily", + "feature_organize": "Štítky, hviezdičky a priečinky", + "feature_shortcuts": "Klávesové skratky", + "feature_privacy": "100% súkromné demo", + "hint": "Kliknite na ľubovoľný e-mail vľavo na začiatok alebo využite sprievodcu nižšie." + }, + "files": { + "office_edit": "Upraviť dokument", + "office_loading": "Otvára sa editor dokumentov…", + "office_error": "Editor dokumentov sa nepodarilo otvoriť.", + "office_close": "Zavrieť editor", + "open_in_new_tab": "Otvoriť v novej karte", + "title": "Súbory", + "search_placeholder": "Hľadať súbory...", + "empty_state_title": "Žiadne súbory", + "upload_files": "Nahrať súbory", + "new_folder": "Nový priečinok", + "new_folder_name": "Názov priečinka", + "rename": "Premenovať", + "rename_title": "Premenovať", + "new_name": "Nový názov", + "name_forbidden_chars": "Server nepovoľuje v názvoch tieto znaky: {chars}", + "name_reserved": "Tento názov je na serveri vyhradený.", + "delete": "Odstrániť", + "delete_confirm_title": "Odstrániť položku", + "delete_confirm_message": "Naozaj chcete odstrániť \"{name}\"? Túto akciu nie je možné vrátiť.", + "download": "Stiahnuť", + "name": "Názov", + "size": "Veľkosť", + "modified": "Zmenené", + "type": "Typ", + "folder": "Priečinok", + "file": "Súbor", + "no_accounts": "Žiadne pripojené účty.", + "uploading": "Nahrávanie...", + "upload_success": "{count, plural, one {1 súbor nahraný} other {# súborov nahraných}}", + "upload_canceled": "Nahrávanie zrušené", + "upload_canceled_partial": "Pred zrušením {count, plural, one {sa nahral 1 súbor} few {sa nahrali # súbory} many {sa nahralo # súboru} other {sa nahralo # súborov}}", + "upload_error": "Nahranie súboru zlyhalo", + "create_folder_success": "Priečinok bol vytvorený", + "create_folder_error": "Vytvorenie priečinka zlyhalo", + "delete_success": "Úspešne odstránené", + "delete_error": "Odstránenie zlyhalo", + "rename_success": "Úspešne premenované", + "rename_error": "Premenovanie zlyhalo", + "download_error": "Sťahovanie zlyhalo", + "not_available": "Úložisko súborov nie je na tomto serveri k dispozícii", + "cancel": "Zrušiť", + "create": "Vytvoriť", + "save": "Uložiť", + "no_results": "Hľadaniu nezodpovedajú žiadne súbory", + "batch_delete_confirm_message": "Naozaj chcete odstrániť {count, plural, one {1 položku} other {# položiek}}? Túto akciu nie je možné vrátiť.", + "batch_delete_success": "{count, plural, one {Odstránená 1 položka} other {Odstránených # položiek}}", + "grid_view": "Zobrazenie mriežky", + "list_view": "Zobrazenie zoznamu", + "details": "Podrobnosti", + "path": "Cesta", + "preview": "Náhľad", + "preview_error": "Náhľad sa nepodarilo načítať", + "cut": "Vystrihnúť", + "copy": "Kopírovať", + "paste": "Vložiť", + "move_success": "{count, plural, one {1 položka presunutá} other {# položiek presunutých}}", + "move_error": "Presun zlyhal", + "paste_success": "Úspešne vložené", + "paste_error": "Vloženie zlyhalo", + "new_text_file": "Nový textový súbor", + "file_name": "Názov súboru", + "retry": "Opakovať", + "refresh": "Obnoviť", + "duplicate": "Duplikovať", + "duplicate_success": "Úspešne duplikované", + "duplicate_error": "Duplikovanie zlyhalo", + "share": "Zdieľať", + "shared": "Zdieľané", + "shared_with_me": "Zdieľané so mnou", + "shared_by": "Zdieľané od {name}", + "create_file_success": "Súbor bol vytvorený", + "create_file_error": "Vytvorenie súboru zlyhalo", + "favorites": "Obľúbené", + "recent": "Nedávne", + "show_in_folder": "Zobraziť v priečinku", + "remove_from_recent": "Odstrániť z nedávnych", + "upload_folder": "Nahrať priečinok", + "file_too_large": "\"{name}\" prekračuje maximálnu veľkosť súboru ({max})", + "undo": "Späť", + "undo_success": "Akcia vrátená späť", + "undo_error": "Akciu nie je možné vrátiť", + "toolbar": "Akcie so súbormi", + "open_folder_tree": "Otvoriť strom priečinkov", + "file_list": "Súbory a priečinky", + "context_menu": "Akcie", + "settings_ascending": "Vzostupne", + "settings_descending": "Zostupne", + "disabled_title": "Funkcia Súbory je zakázaná správcom", + "disabled_description": "Nahrávanie veľkých súborov cez WebDAV môže spôsobiť nestabilitu Stalwart/RocksDB. Táto funkcia sa neodporúča v produkčnom prostredí.", + "migration_title": "Aktualizácia vašich súborov…", + "migration_description": "Usporiadanie priečinkov a súborov do správnej štruktúry. Toto prebehne iba raz.", + "new": "Nové", + "my_files": "Moje súbory", + "folders": "Priečinky", + "files_heading": "Súbory", + "open": "Otvoriť", + "star": "Pridať do obľúbených", + "unstar": "Odstrániť z obľúbených", + "expand_folder": "Rozbaliť {name}", + "collapse_folder": "Zbaliť {name}", + "sidebar_hint": "Pridajte priečinky do obľúbených a otvárajte súbory, aby ste ich našli tu.", + "breadcrumbs": "Cesta k priečinku", + "show_path": "Zobraziť skryté priečinky v ceste", + "selection_actions": "Akcie pre vybrané položky", + "selected_count": "{count, plural, one {Vybraná 1 položka} few {Vybrané # položky} many {Vybranej # položky} other {Vybraných # položiek}}", + "clear_selection": "Zrušiť výber", + "select_all": "Vybrať všetko", + "select_item": "Vybrať {name}", + "more_actions": "Ďalšie akcie pre {name}", + "paste_here": "Vložiť do tohto priečinka", + "paste_count": "{count, plural, one {Vložiť 1 položku} few {Vložiť # položky} many {Vložiť # položky} other {Vložiť # položiek}}", + "sort_by": "Zoradiť podľa", + "view_mode": "Zobrazenie", + "clear_search": "Vymazať hľadanie", + "no_results_for": "V tomto priečinku nič nezodpovedá výrazu „{query}“", + "download_count": "{count, plural, one {Stiahnuť 1 súbor} few {Stiahnuť # súbory} many {Stiahnuť # súboru} other {Stiahnuť # súborov}}", + "delete_count": "{count, plural, one {Odstrániť 1 položku} few {Odstrániť # položky} many {Odstrániť # položky} other {Odstrániť # položiek}}", + "close_details": "Zavrieť podrobnosti", + "file_kind": "Súbor {ext}", + "folder_summary": "{folders, plural, =0 {Žiadne priečinky} one {1 priečinok} few {# priečinky} many {# priečinka} other {# priečinkov}}, {files, plural, =0 {žiadne súbory} one {1 súbor} few {# súbory} many {# súboru} other {# súborov}}", + "details_hint": "Vyberte súbor alebo priečinok a zobrazia sa jeho podrobnosti.", + "sharing": "Zdieľanie", + "shared_with_count": "{count, plural, one {Zdieľané s 1 osobou} few {Zdieľané s # osobami} many {Zdieľané s # osoby} other {Zdieľané s # osobami}}", + "only_you": "Iba vy", + "empty_folder_title": "Tento priečinok je prázdny", + "empty_folder_description": "Presuňte sem súbory alebo ich nahrajte zo zariadenia.", + "empty_folder_read_only": "Do tohto priečinka nemôžete pridávať súbory.", + "choose_account": "Vyberte účet", + "drop_to_upload": "Pustite a nahrajte do {folder}", + "uploading_count": "Nahráva sa {current} z {total}", + "dismiss": "Zavrieť", + "zoom_in": "Priblížiť", + "zoom_out": "Oddialiť", + "zoom_reset": "Prispôsobiť obrazovke", + "rotate": "Otočiť", + "close_preview": "Zavrieť" + }, + "smime": { + "your_certificates": "Vaše certifikáty", + "your_certificates_desc": "Importujte a spravujte svoje certifikáty S/MIME na podpisovanie a šifrovanie e-mailov", + "recipient_certificates": "Certifikáty príjemcov", + "recipient_certificates_desc": "Verejné certifikáty na šifrovanie e-mailov odosielaných príjemcom", + "identity_bindings": "Väzby kľúčov na identity", + "identity_bindings_desc": "Viažte certifikáty S/MIME k e-mailovým identitám", + "defaults_title": "Predvolené", + "defaults_desc": "Nakonfigurujte predvolené správanie pre podpisovanie a šifrovanie", + "import_pkcs12": "Importovať PKCS#12 (.p12/.pfx)", + "import_public_cert": "Importovať certifikát", + "no_certificates": "Zatiaľ neboli importované žiadne certifikáty", + "no_recipient_certs": "Žiadne certifikáty príjemcov", + "expires": "Vyprší", + "expired": "Vypršala platnosť", + "bound_to": "Viazané k", + "no_key_bound": "Žiadne", + "lock": "Zamknúť kľúč", + "unlock": "Odomknúť kľúč", + "details": "Zobraziť podrobnosti", + "delete": "Odstrániť", + "encrypt_by_default": "Predvolene šifrovať", + "encrypt_by_default_desc": "Automaticky šifrovať e-maily, keď majú všetci príjemcovia certifikáty", + "remember_unlocked": "Pamätať si odomknuté kľúče", + "remember_unlocked_desc": "Ponechať kľúče odomknuté počas trvania tejto relácie prehliadača", + "sign_default_for": "Predvolene podpisovať pre", + "enter_p12_passphrase": "Zadajte heslo PKCS#12", + "p12_passphrase_desc": "Zadajte heslo chrániacie tento súbor certifikátu", + "enter_storage_passphrase": "Nastavte heslo úložiska", + "storage_passphrase_desc": "Vyberte heslo na ochranu tohto kľúča pri uložení v prehliadači", + "next": "Ďalší", + "import": "Importovať", + "unlock_key": "Odomknúť kľúč", + "unlock_key_desc": "Zadajte heslo úložiska na odomknutie tohto kľúča na podpisovanie alebo dešifrovanie", + "passphrase_placeholder": "Zadajte heslo", + "confirm_passphrase_placeholder": "Potvrďte heslo", + "passphrase_mismatch": "Heslá sa nezhodujú", + "cancel": "Zrušiť", + "processing": "Spracovanie…", + "close": "Zavrieť", + "certificate_details": "Podrobnosti o certifikáte", + "cert_subject": "Subjekt", + "cert_issuer": "Vydavateľ", + "cert_email": "E-mail", + "cert_serial": "Sériové číslo", + "cert_validity": "Platnosť", + "cert_fingerprint": "Odtlačok (SHA-256)", + "cert_algorithm": "Algoritmus", + "cert_capabilities": "Možnosti", + "cert_source": "Zdroj", + "cert_expired": "Platnosť tohto certifikátu vypršala", + "cert_not_yet_valid": "Tento certifikát ešte nie je platný", + "cap_sign": "Podpisovanie", + "cap_encrypt": "Šifrovanie", + "cap_none": "Žiadne", + "show_passphrase": "Zobraziť heslo", + "hide_passphrase": "Skryť heslo", + "sign_toggle": "Podpísať", + "encrypt_toggle": "Šifrovať", + "missing_recipient_certs": "Chýbajú certifikáty pre: {emails}", + "missing_sender_cert": "Žiadny certifikát viazaný k tejto identite", + "status_encrypted_ok": "Táto správa bola zašifrovaná", + "status_encrypted_no_key": "Táto správa je zašifrovaná, ale nebol nájdený zodpovedajúci kľúč", + "status_encrypted_failed": "Nepodarilo sa dešifrovať túto správu", + "status_signed_valid": "Podpis bol overený", + "status_signed_invalid": "Overenie podpisu zlyhalo", + "status_signed_expired_cert": "Podpísané certifikátom, ktorého platnosť vypršala", + "status_signed_self_signed": "Podpis je platný, ale certifikát je vydaný sám sebe (nedôveryhodný)", + "status_signed_mismatch": "Podpis je platný, ale podpisujúci sa nezhoduje s odosielateľom", + "status_unsupported": "Nepodporovaný formát S/MIME", + "auto_import_signer_certs": "Automaticky importovať certifikáty podpisujúcich", + "auto_import_signer_certs_desc": "Automaticky ukladať certifikáty z overených podpísaných e-mailov na budúce šifrovanie", + "export": "Exportovať", + "enter_export_passphrase": "Nastavte heslo pre export", + "export_passphrase_desc": "Vyberte heslo na ochranu exportovaného súboru PKCS#12", + "export_storage_desc": "Zadajte heslo úložiska na dešifrovanie kľúča na export", + "incorrect_passphrase": "Nesprávne heslo" + }, + "tour": { + "step_counter": "Krok {current} z {total}", + "skip": "Preskočiť sprievodcu", + "back": "Späť", + "next": "Ďalší", + "finish": "Dokončiť", + "take_a_tour": "Preskúmajte rozhranie v krátkom sprievodcovi", + "restart_title": "Úvodný sprievodca", + "restart_desc": "Prehrať sprievodcu rozhraním krok za krokom", + "restart_button": "Spustiť sprievodcu znova", + "show_on_new_devices_title": "Zobraziť na nových zariadeniach", + "show_on_new_devices_desc": "Prehrať uvítací banner a sprievodcu pri prvom prihlásení na novom zariadení", + "sidebar_title": "Vaše poštové schránky", + "sidebar_desc": "Toto je postranný panel so priečinkami. Kliknutím na ľubovoľnú schránku zobrazíte jej e-maily.", + "compose_title": "Napísať e-mail", + "compose_desc": "Kliknutím sem napíšete nový e-mail. Môžete pridávať príjemcov, prílohy a používať formátovaný text.", + "search_title": "Prehľadávať poštu", + "search_desc": "Vyhľadávajte podľa odosielateľa, predmetu alebo obsahu.", + "email_list_title": "Zoznam e-mailov", + "email_list_desc": "Tu sa zobrazujú e-maily. Kliknutím na jeden z nich si ho prečítate napravo.", + "email_viewer_title": "Panel čítania", + "email_viewer_desc": "Tu sa otvára vybraný e-mail. Pomocou tlačidiel na paneli nástrojov môžete odpovedať, preposlať, archivovať alebo odstrániť.", + "keywords_title": "Farebné štítky", + "keywords_desc": "Organizujte poštu pomocou farebných štítkov.", + "calendar_title": "Kalendár", + "calendar_desc": "Prepnite sa do kalendára na správu udalostí.", + "contacts_title": "Kontakty", + "contacts_desc": "Tu sa nachádza váš adresár. Importujte kontakty, vytvárajte skupiny.", + "settings_title": "Nastavenia", + "settings_desc": "Prispôsobte si všetko: tému, hustotu, podpisy, filtre, klávesové skratky a oveľa viac.", + "shortcuts_title": "Klávesové skratky", + "shortcuts_desc": "Kedykoľvek stlačte ? na zobrazenie všetkých dostupných skratiek.", + "compose_open_title": "Editor správ", + "compose_open_desc": "Toto je editor správ. Pridávajte príjemcov, píšte obsah, prikladajte súbory a formátujte text.", + "calendar_view_title": "Váš kalendár", + "calendar_view_desc": "Tu vidíte kalendár s ukážkovými udalosťami.", + "create_event_title": "Vytvoriť udalosť", + "create_event_desc": "Kliknutím na toto tlačidlo vytvoríte novú udalosť v kalendári.", + "event_modal_title": "Podrobnosti udalosti", + "event_modal_desc": "Toto je formulár udalosti. Vyplňte názov, vyberte dátum a čas, pridajte miesto alebo účastníkov.", + "contacts_list_title": "Vaše kontakty", + "contacts_list_desc": "Tu sú vaše kontakty. Kliknutím na ľubovoľný kontakt zobrazíte jeho úplné údaje na pravej strane.", + "settings_tabs_title": "Menu nastavení", + "settings_tabs_desc": "Tu nájdete všetky kategórie nastavení.", + "files_title": "Úložisko súborov", + "files_desc": "Prehliadač súborov umožňuje nahrávať, organizovať a zdieľať súbory.", + "demo_banner_title": "Ovládanie dema", + "demo_banner_desc": "Nachádzate sa v demo režime – všetko zostáva vo vašom prehliadači.", + "quota_title": "Využitie úložiska", + "quota_desc": "Tu môžete sledovať veľkosť svojej poštovej schránky." + }, + "unified_mailbox": { + "search_unavailable": "Vyhľadávanie nie je v zjednotenom zobrazení k dispozícii" + }, + "quote_header": { + "reply_line": "Dňa {date}, {from} napísal(a):", + "forwarded_separator": "---------- Preposlaná správa ----------", + "from_label": "Od", + "date_label": "Dátum", + "subject_label": "Predmet" + }, + "pwa_install": { + "title": "Nainštalovať {appName}", + "description": "Nainštalujte si našu aplikáciu pre rýchly prístup a podporu offline.", + "not_now": "Teraz nie", + "install": "Nainštalovať", + "dont_remind": "Viac mi to nepripomínať", + "dismiss_aria": "Zavrieť výzvu na inštaláciu" + }, + "deep_link": { + "message_not_found": "Táto správa už nie je dostupná.", + "thread_not_found": "Táto konverzácia už nie je dostupná.", + "folder_not_found": "Tento priečinok už nie je dostupný.", + "event_not_found": "Táto udalosť už nie je dostupná.", + "file_not_found": "Tento súbor už nie je dostupný.", + "account_unavailable": "Prihláste sa do tohto účtu, aby ste odkaz otvorili.", + "copy_message": "Kopírovať odkaz na správu", + "copy_conversation": "Kopírovať odkaz na konverzáciu", + "copy_event": "Kopírovať odkaz na udalosť", + "copied": "Odkaz skopírovaný", + "copy_failed": "Odkaz sa nepodarilo skopírovať" + }, + "plugins": { + "oauth_callback": { + "completing": "Dokončovanie autorizácie…", + "completing_message": "Počkajte, kým sa vytvorí spojenie.", + "received_title": "Autorizácia prijatá", + "received_message": "Môžete sa vrátiť na kartu, kde ste začali pripájanie.", + "failed_title": "Autorizácia zlyhala", + "denied": "Autorizácia bola zamietnutá.", + "failed_detail": "Autorizácia zlyhala: {error}", + "missing_params": "Chýba autorizačný kód alebo stav.", + "delivery_failed": "Výsledok autorizácie sa nepodarilo odovzdať karte aplikácie. Zavrite toto okno a skúste to znova." + }, + "settings": { + "title": "Doplnky", + "description": "Doplnky pridané vaším správcom. Pre svoj účet ich môžete zapnúť alebo vypnúť.", + "description_uploads": "Doplnky pridávajú do webmailu ďalšie funkcie. Pre svoj účet ich môžete zapnúť alebo vypnúť, prípadne nainštalovať vlastné.", + "description_demo": "Doplnky nie sú v ukážkovej verzii k dispozícii.", + "install_button": "Nainštalovať doplnok", + "install_hint": "Vyberte súbor ZIP s doplnkom.", + "install_hint_approval": "Vyberte súbor ZIP s doplnkom. Skôr než ho budete môcť zapnúť, musí ho schváliť správca.", + "install_uploads_off": "Váš správca nepovoľuje inštaláciu doplnkov.", + "install_managed_id": "Váš správca už poskytuje doplnok s týmto názvom, preto ho nemožno nahradiť.", + "install_invalid": "Tento súbor nie je doplnok, ktorý možno nainštalovať: {details}", + "toast_installed": "Doplnok je nainštalovaný. Zapnite ho nižšie.", + "toast_installed_pending": "Doplnok je nainštalovaný a odoslaný správcovi na schválenie.", + "toast_enabled": "Doplnok {name} je zapnutý", + "toast_disabled": "Doplnok {name} je vypnutý", + "toast_removed": "Doplnok {name} bol odstránený", + "toast_forced": "Váš správca ponecháva doplnok {name} zapnutý", + "toast_approval_requested": "Doplnok {name} čaká na schválenie správcom", + "empty_title": "Zatiaľ žiadne doplnky", + "empty_admin": "Váš správca zatiaľ nepridal žiadne doplnky.", + "empty_uploads": "Začnite inštaláciou doplnku zo súboru ZIP.", + "empty_demo": "Doplnky vyžadujú skutočný účet, preto ich ukážková verzia nemá.", + "syncing": "Kontrola doplnkov na serveri…", + "status_installed": "Vypnuté", + "status_enabled": "Spúšťa sa", + "status_running": "Zapnuté", + "status_disabled": "Vypnuté", + "status_error": "Problém", + "badge_forced": "Povinný", + "badge_managed": "Od správcu", + "badge_yours": "Nainštalované vami", + "badge_awaiting": "Čaká na schválenie", + "toggle_label": "Zapnúť alebo vypnúť doplnok {name}", + "forced_note": "Váš správca tento doplnok vyžaduje, preto ho nemožno vypnúť.", + "permissions": "Čo môže robiť", + "permissions_managed": "Čo môže robiť (schválené správcom)", + "settings": "Nastavenia", + "remove": "Odstrániť", + "remove_title": "Odstrániť doplnok {name}?", + "remove_message": "Doplnok aj všetko, čo uložil v tomto prehliadači, sa vymaže. Neskôr ho môžete nainštalovať znova.", + "remove_confirm": "Odstrániť", + "error_user_plugins_off": "Váš správca vypol doplnky inštalované používateľmi, preto tento doplnok nebeží.", + "error_approval_unavailable": "Tento doplnok bol nainštalovaný skôr, než sa vyžadovalo schválenie. Odstráňte ho a nainštalujte znova, aby sa odoslal na schválenie.", + "error_approval_denied": "Váš správca tento doplnok neschválil.", + "error_approval_pending": "Čaká sa, kým správca tento doplnok schváli.", + "error_auto_disabled": "Vypnuté, pretože opakovane zlyhávalo. Zapnite ho znova a skúste to znova.", + "accounts_heading": "Pripojené účty", + "account_on": "{label} na {host}", + "account_waiting": "{label} na {host} (prihlásenie nedokončené)", + "disconnect": "Odpojiť", + "disconnect_failed": "{label} sa nepodarilo odpojiť" + }, + "consent": { + "title": "Povoliť doplnku {name} tieto akcie?", + "intro": "Doplnok žiada o nižšie uvedené oprávnenia. Dostane ich, len ak ich povolíte.", + "high_risk_note": "Označené položky dávajú doplnku prístup k obsahu vašej pošty alebo mu umožňujú posielať údaje inam. Povoľte ich len doplnkom, ktorým dôverujete.", + "revoke_hint": "Doplnok môžete kedykoľvek vypnúť alebo odstrániť v Nastaveniach.", + "deny": "Nepovoliť", + "allow": "Povoliť" + }, + "permissions": { + "none": "Nepotrebuje žiadne zvláštne oprávnenia.", + "high_risk": "Vyžaduje opatrnosť", + "email_read": { + "title": "Čítať vašu poštu", + "body": "Vidí odosielateľov, príjemcov, predmety a celý text ktorejkoľvek správy vo vašom účte aj vaše priečinky." + }, + "email_write": { + "title": "Meniť a triediť vašu poštu", + "body": "Pridáva alebo odstraňuje štítky, príznaky a ďalšie kľúčové slová pri vašich správach, presúva ich medzi vašimi priečinkami a do archívu a ukladá koncepty." + }, + "email_send": { + "title": "Meniť poštu pred odoslaním", + "body": "Číta a mení príjemcov, predmet, text a prílohy správ, ktoré píšete, a môže zastaviť odoslanie správy." + }, + "crypto_full": { + "title": "Úplný prístup k tejto aplikácii", + "body": "Beží s rovnakými právami ako samotný webmail. Môže čítať všetku vašu poštu a uložené kľúče a konať vo vašom mene. Povoľte to len doplnkom, ktorým úplne dôverujete." + }, + "email_raw_send": { + "title": "Odosielať správy, ktoré sám vytvoril", + "body": "Odosiela z vášho účtu kompletné správy, napríklad podpísané alebo šifrované." + }, + "email_blob_read": { + "title": "Čítať nespracovaný obsah správ", + "body": "Číta pôvodné údaje vašich správ a príloh aj každého súboru, ktorý priložíte pred odoslaním." + }, + "email_blob_write": { + "title": "Nahrádzať prílohy pred odoslaním", + "body": "Nahráva súbory na váš poštový server a pred odoslaním správy nahradí priložený súbor iným." + }, + "email_render_takeover": { + "title": "Meniť zobrazenie správ", + "body": "Nahrádza text správ, ktoré otvoríte, aj zoznamu správ, napríklad aby zobrazil dešifrovaný obsah." + }, + "calendar_read": { + "title": "Čítať váš kalendár", + "body": "Vidí vaše kalendáre a ich udalosti." + }, + "calendar_write": { + "title": "Meniť váš kalendár", + "body": "Vytvára, mení a odstraňuje udalosti." + }, + "contacts_read": { + "title": "Čítať vaše kontakty", + "body": "Prehľadáva a číta vaše adresáre." + }, + "contacts_write": { + "title": "Meniť vaše kontakty", + "body": "Vytvára, mení a odstraňuje kontakty a adresáre." + }, + "files_read": { + "title": "Čítať vaše súbory", + "body": "Prechádza a otvára súbory uložené vo vašom účte." + }, + "files_write": { + "title": "Meniť vaše súbory", + "body": "Nahráva, premenúva, presúva a odstraňuje súbory vo vašom účte." + }, + "identity_read": { + "title": "Vidieť vaše identity odosielateľa", + "body": "Vidí adresy, z ktorých odosielate, a ich podpisy." + }, + "identity_write": { + "title": "Meniť vaše identity odosielateľa", + "body": "Vytvára, mení a odstraňuje identity odosielateľa a podpisy." + }, + "filters_read": { + "title": "Čítať vaše filtre pošty", + "body": "Vidí pravidlá, ktoré triedia vašu prichádzajúcu poštu." + }, + "filters_write": { + "title": "Meniť vaše filtre pošty", + "body": "Pridáva pravidlá do filtrov na serveri. Takéto pravidlá môžu prichádzajúcu poštu presúvať, preposielať alebo odstraňovať." + }, + "tasks_read": { + "title": "Čítať vaše úlohy", + "body": "Vidí vaše zoznamy úloh a úlohy." + }, + "tasks_write": { + "title": "Meniť vaše úlohy", + "body": "Vytvára, mení, dokončuje a odstraňuje úlohy." + }, + "templates_read": { + "title": "Čítať vaše šablóny", + "body": "Vidí vaše uložené šablóny správ." + }, + "templates_write": { + "title": "Meniť vaše šablóny", + "body": "Vytvára, mení a odstraňuje šablóny správ." + }, + "smime_read": { + "title": "Vidieť stav S/MIME", + "body": "Vidí, či máte kľúče a certifikáty S/MIME, bez prístupu ku kľúčom." + }, + "vacation_read": { + "title": "Vidieť automatickú odpoveď v neprítomnosti", + "body": "Vidí, či je automatická odpoveď zapnutá a čo obsahuje." + }, + "vacation_write": { + "title": "Meniť automatickú odpoveď v neprítomnosti", + "body": "Zapína a vypína automatickú odpoveď a mení jej text a dátumy." + }, + "settings_read": { + "title": "Čítať vaše nastavenia", + "body": "Vidí vaše predvoľby a definície štítkov." + }, + "settings_write": { + "title": "Meniť vaše nastavenia", + "body": "Pridáva štítky, mení ich poradie a mení vaše predvoľby." + }, + "security_read": { + "title": "Vidieť zabezpečenie účtu", + "body": "Vidí, či je zapnuté dvojfaktorové overenie a šifrovanie, bez prístupu k tajným údajom." + }, + "auth_observe": { + "title": "Vedieť, kedy sa prihlásite", + "body": "Dostáva správu, keď sa prihlásite, prepnete účet alebo sa obnoví vaša relácia." + }, + "auth_emit": { + "title": "Odhlásiť vás", + "body": "Môže vás odhlásiť z webmailu." + }, + "account_read": { + "title": "Vidieť vaše účty", + "body": "Vidí účty, ktoré ste pridali, vrátane ich adries a serverov." + }, + "http_post": { + "title": "Používať server webmailu vo vašom mene", + "body": "Volá tieto koncové body servera s vaším prihlásením:" + }, + "http_fetch": { + "title": "Pripájať sa k iným webom", + "body": "Odosiela požiadavky na tieto weby. Čokoľvek, čo doplnok môže čítať, tam môže byť odoslané:" + }, + "admin_config": { + "title": "Čítať nastavenia od správcu", + "body": "Číta nastavenia, ktoré pre tento doplnok uložil váš správca." + }, + "ui_observe": { + "title": "Sledovať, kde sa v aplikácii nachádzate", + "body": "Vie, ktorá stránka je otvorená, a reaguje na odkazy, na ktoré kliknete." + }, + "ui_toolbar": { + "title": "Pridávať tlačidlá na panely nástrojov", + "body": "Zobrazuje vlastné tlačidlá na paneli nástrojov správ, pri otvorenej správe a v hornej lište." + }, + "ui_app_top_banner": { + "title": "Zobrazovať banner hore", + "body": "Zobrazuje oznámenie cez hornú časť aplikácie." + }, + "ui_email_banner": { + "title": "Zobrazovať oznámenia pri správach", + "body": "Zobrazuje vlastné oznámenia a zhrnutia nad časťami otvorenej správy alebo konverzácie, v nich a medzi nimi." + }, + "ui_email_footer": { + "title": "Pridávať obsah pod správy", + "body": "Zobrazuje vlastný obsah pod otvorenou správou." + }, + "ui_contact_cryptokeys": { + "title": "Zobrazovať šifrovacie kľúče kontaktov", + "body": "Zobrazuje a spravuje šifrovacie kľúče pri kontakte." + }, + "ui_email_details": { + "title": "Pridávať panely do podrobností správy", + "body": "Zobrazuje vlastný panel vedľa otvorenej správy alebo pod ňou." + }, + "ui_download_file": { + "title": "Ukladať súbory do zariadenia", + "body": "Ponúka na stiahnutie súbory, ktoré vytvoril." + }, + "ui_message_list_tabs": { + "title": "Pridávať karty do Doručených", + "body": "Rozdeľuje Doručené do kariet, napríklad Aktualizácie alebo Akcie." + }, + "ui_composer_toolbar": { + "title": "Pridávať obsah do editora správy", + "body": "Zobrazuje vlastné tlačidlá, položky ponuky a sekcie, keď píšete správu." + }, + "ui_composer_sidebar": { + "title": "Pridávať panel do editora správy", + "body": "Zobrazuje vlastný panel vedľa správy, ktorú píšete." + }, + "ui_sidebar_widget": { + "title": "Pridávať obsah do postranného panela", + "body": "Zobrazuje vlastné polia a sekcie v postrannom paneli pošty." + }, + "ui_settings_section": { + "title": "Pridávať stránku nastavení", + "body": "Zobrazuje vlastnú stránku v Nastaveniach." + }, + "ui_context_menu": { + "title": "Pridávať položky do ponuky správy", + "body": "Pridáva položky do ponuky, ktorá sa otvorí po kliknutí pravým tlačidlom na správu." + }, + "ui_navigation_rail": { + "title": "Pridávať tlačidlo na navigačnú lištu", + "body": "Zobrazuje vlastné tlačidlo na navigačnej lište." + }, + "ui_keyboard": { + "title": "Pridávať klávesové skratky", + "body": "Reaguje na klávesové skratky, ktoré sám definuje." + }, + "ui_calendar_action": { + "title": "Pridávať obsah do sekcie Kalendár", + "body": "Zobrazuje vlastné tlačidlá a sekcie pri udalostiach a krátke štítky a bodky pri dňoch v kalendári." + }, + "ui_admin_page": { + "title": "Pridávať stránku administrácie", + "body": "Zobrazuje vlastnú stránku v nastaveniach administrácie." + }, + "app_lifecycle": { + "title": "Vedieť, kedy sa aplikácia spúšťa a ukončuje", + "body": "Dostáva správu, keď sa aplikácia spustí, prejde do režimu offline alebo keď sa odhlásite." + }, + "compose_open": { + "title": "Otvárať editor správy", + "body": "Otvorí novú správu s vyplnenými príjemcami, textom alebo prílohami. O odoslaní stále rozhodujete vy." + }, + "compose_read": { + "title": "Čítať správu, ktorú píšete", + "body": "Čítať text správy počas písania, keď doplnok použijete v editore. Nie podpis ani citovanú správu." + }, + "compose_write": { + "title": "Meniť správu, ktorú píšete", + "body": "Nahrádzať alebo pridávať text v správe počas písania, keď doplnok použijete v editore. Každú zmenu môžete vrátiť a o odoslaní stále rozhodujete vy." + }, + "ui_notifications": { + "title": "Zobrazovať upozornenia", + "body": "Zobrazuje upozornenia vo vašom zariadení alebo v aplikácii, ak ste ich nepovolili. Každé uvádza názov doplnku." + }, + "ui_message_list": { + "title": "Pridávať obsah do zoznamu správ", + "body": "Zobrazuje vlastný obsah nad zoznamom správ a tlačidlá pri správach a na paneli nástrojov pre vybrané správy." + }, + "ui_contacts": { + "title": "Pridávať obsah do sekcie Kontakty", + "body": "Zobrazuje vlastné sekcie v podrobnostiach kontaktu a tlačidlá v zozname kontaktov." + }, + "ui_tasks": { + "title": "Pridávať obsah do sekcie Úlohy", + "body": "Zobrazuje vlastné sekcie v podrobnostiach úlohy a tlačidlá v zozname úloh." + }, + "ui_files": { + "title": "Pridávať obsah do sekcie Súbory", + "body": "Zobrazuje vlastné sekcie v náhľade súboru a položky v ponuke súboru." + }, + "ui_commands": { + "title": "Pridávať príkazy", + "body": "Pridáva vlastné položky do palety príkazov (Ctrl+K)." + }, + "ui_app_page": { + "title": "Pridávať stránky", + "body": "Pridáva do aplikácie vlastné stránky, každú s tlačidlom na navigačnej lište." + }, + "http_proxy": { + "title": "Používať účty, ktoré pripojíte", + "body": "Odosiela požiadavky cez server Bulwarku pomocou účtu alebo kľúča, ktorý ste nastavili vy alebo váš správca. Doplnok nikdy nevidí heslo, ale môže na príslušnom serveri robiť čokoľvek, čo môže tento účet." + } + }, + "dialog": { + "from_plugin": "Od doplnku {name}", + "ok": "OK", + "submit": "Odoslať", + "confirm": "Potvrdiť", + "cancel": "Zrušiť", + "close": "Zavrieť", + "turn_off": "Vypnúť {name}", + "plugin_menu": "Možnosti pre {name}", + "password_note": "Tento formulár pochádza z doplnku {name}, nie z webmailu. Heslo zadajte, len ak tomuto doplnku dôverujete.", + "toast_auto_disabled": "{name} bol vypnutý, pretože opakovane zlyhával." + }, + "connect": { + "title": "Pripojiť {label}", + "intro_password": "{name} chce používať váš účet {label}. Bulwark uchováva heslo na svojom serveri a posiela ho len na {host}. Doplnok ho nikdy nevidí.", + "intro_password_any": "{name} chce používať váš účet {label}. Bulwark uchováva heslo na svojom serveri a posiela ho len na server, ktorý tu zadáte. Doplnok ho nikdy nevidí.", + "intro_signin": "{name} sa chce prihlásiť do vášho účtu {label}. Zadajte tu server a potom sa prihláste na jeho vlastnej stránke. Bulwark uchováva získaný prístup na svojom serveri; doplnok ho nikdy nevidí.", + "server": "Adresa servera", + "server_hint": "cloud.example.com", + "username": "Používateľské meno", + "secret": "Heslo alebo kľúč", + "connect": "Pripojiť", + "continue": "Pokračovať", + "cancel": "Zrušiť", + "saving": "Ukladá sa…", + "error_server": "Zadajte adresu servera, napríklad https://cloud.example.com", + "error_save": "Uloženie zlyhalo: {error}" + } + }, + "global_search": { + "title": "Hľadať všade", + "placeholder": "Hľadať v pošte, kontaktoch, kalendári, súboroch...", + "scope_label": "Hľadať v", + "account_label": "Účet", + "scope_all": "Všetko", + "scope_mail": "Pošta", + "scope_contacts": "Kontakty", + "scope_calendar": "Kalendár", + "scope_files": "Súbory", + "all_accounts": "Všetky účty", + "show_all": "Zobraziť všetky výsledky", + "no_results": "Žiadne výsledky pre „{query}“", + "server_hint": "Celé slová prehľadávajú server; časti slov zodpovedajú už načítaným položkám", + "searching": "Hľadá sa...", + "account_error": "Nepodarilo sa prehľadať {account}", + "recent_searches": "Nedávne hľadania", + "recurring": "Opakovaná", + "no_subject": "(no subject)", + "empty_hint": "Hľadajte poštu, kontakty, udalosti a súbory vo všetkých účtoch. Skúste from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Plocho podľa dátumu", + "grouped": "Zoskupené", + "load_more": "Načítať ďalšie", + "preview_empty": "Vyberte výsledok pre náhľad", + "open_full": "Otvoriť", + "view_label": "Zobrazenie" + }, + "top_bar": { + "more": "Ďalšie možnosti" + }, + "plugin_pages": { + "loading": "Načítava sa stránka", + "not_available_title": "Táto stránka nie je k dispozícii", + "not_available_description": "Doplnok, ktorý ju pridal, je vypnutý alebo už nie je nainštalovaný.", + "back_to_mail": "Späť na poštu", + "settings_group": "Doplnky" + }, + "command_palette": { + "title": "Paleta príkazov", + "open": "Otvoriť paletu príkazov", + "placeholder": "Zadajte príkaz", + "no_results": "Žiadne zodpovedajúce príkazy", + "go_to": "Prejsť na {name}", + "compose": "Nová správa", + "search": "Hľadať v pošte", + "theme_dark": "Prepnúť na tmavý motív", + "theme_light": "Prepnúť na svetlý motív", + "shortcuts": "Zobraziť klávesové skratky", + "group_navigation": "Prejsť na", + "group_actions": "Akcie", + "group_plugins": "Doplnky" + }, + "notification_center": { + "title": "Oznámenia", + "mark_all_read": "Označiť všetko ako prečítané", + "clear": "Vymazať", + "empty": "Žiadne oznámenia", + "close": "Zavrieť", + "unread": "Neprečítané", + "unread_count": "Neprečítané: {count}", + "level_error": "Chyba", + "level_warning": "Varovanie" + } +} diff --git a/locales/tr/common.json b/locales/tr/common.json index adb1a3ab..48de33e0 100644 --- a/locales/tr/common.json +++ b/locales/tr/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "JMAP protokolünü kullanan minimalist web posta istemcisi", - "login": { - "title": "Webmail", - "username_label": "E-posta", - "username_placeholder": "kullanici@ornek.com", - "password_label": "Şifre", - "password_placeholder": "Şifrenizi girin", - "jmap_endpoint_label": "JMAP Sunucusu", - "jmap_endpoint_placeholder": "https://mail.ornek.com", - "jmap_endpoint_cors_hint": "Sunucu, bu etki alanından gelen CORS isteklerine izin vermelidir.", - "jmap_server_label": "Sunucu", - "jmap_server_auto_picked": "Sunucu, e-posta etki alanınıza göre seçildi.", - "sign_in": "Giriş yap", - "signing_in": "Giriş yapılıyor...", - "loading": "Yükleniyor...", - "reconnecting": "Bağlantı kesildi. Yeniden bağlanılmaya çalışılıyor…", - "error": { - "invalid_credentials": "Geçersiz e-posta veya şifre. Lütfen bilgilerinizi kontrol edip tekrar deneyin.", - "connection_failed": "Sunucuya ulaşılamıyor. İnternet bağlantınızı kontrol edip tekrar deneyin.", - "cors_blocked": "Sunucuya ulaşılıyor ancak çapraz kaynak istekleri engelleniyor. JMAP sunucunuzun CORS ayarlarını kontrol edip bu etki alanına izin verin.", - "server_error": "Sunucu geçici olarak kullanılamıyor. Lütfen daha sonra tekrar deneyin.", - "generic": "Beklenmedik bir hata oluştu. Bu sorun devam ederse yöneticinizle iletişime geçin.", - "totp_required": "İki faktörlü doğrulama kodu gereklidir. Lütfen kodunuzu aşağıya girin.", - "totp_invalid": "Geçersiz doğrulama kodu. Lütfen doğrulama uygulamanızı kontrol edip tekrar deneyin.", - "oauth_discovery_failed": "SSO etkin ancak kimlik sağlayıcısına ulaşılamıyor. OAuth yapılandırmanızı kontrol edin." - }, - "show_password": "Şifreyi göster", - "hide_password": "Şifreyi gizle", - "totp_toggle": "2FA kodum var", - "remember_me": "Beni hatırla", - "config_error": { - "title": "Yapılandırma Hatası", - "fetch_failed": "Uygulama yapılandırması yüklenemiyor. Lütfen daha sonra tekrar deneyin.", - "server_not_configured": "Posta sunucusu yapılandırılmamış. Lütfen yöneticinizle iletişime geçin." - }, - "remove_from_history": "Geçmişten kaldır", - "totp_label": "Doğrulama kodu", - "totp_placeholder": "000000", - "session_expired": "Oturumunuzun süresi doldu. Lütfen tekrar giriş yapın.", - "dismiss": "Kapat", - "or": "veya", - "sign_in_sso": "SSO ile giriş yap", - "add_account_title": "Hesap Ekle", - "add_account_subtitle": "Başka bir hesapla giriş yap", - "cancel": "İptal", - "website": "Web Sitesi", - "imprint": "Yasal Bilgiler", - "privacy_policy": "Gizlilik Politikası", - "try_demo": "Demoyu Dene", - "demo_description": "Örnek verilerle keşfedin - hesap gerekmez", - "demo_launching": "Demo başlatılıyor...", - "demo_login_button": "Demoyu Başlat", - "demo_tagline": "Tam özellikli bir e-posta istemcisini deneyimleyin. Hesap gerekmez.", - "demo_no_signup": "Kayıt gerekmez - örnek verilerle özgürce keşfedin", - "oauth_completing": "Giriş tamamlanıyor...", - "oauth_error": { - "title": "Kimlik Doğrulama Başarısız", - "invalid_state": "Güvenlik doğrulaması başarısız. Lütfen tekrar giriş yapmayı deneyin.", - "missing_params": "Yetkilendirme verisi eksik. Lütfen tekrar giriş yapmayı deneyin.", - "token_exchange_failed": "Kimlik doğrulama tamamlanamadı. Lütfen tekrar deneyin.", - "access_denied": "Erişim reddedildi. Lütfen yöneticinizle iletişime geçin.", - "back_to_login": "Giriş sayfasına dön" - } - }, - "sidebar": { - "close": "Kapat", - "compose": "Yeni E-posta", - "compose_hint": "Yeni E-posta (c)", - "search_placeholder": "Posta ara...", - "search_placeholder_hint": "Posta ara... (/ tuşuna basın)", - "storage": "Depolama", - "storage_used": "Kullanılan", - "storage_free": "Boş", - "storage_total": "Toplam", - "sign_out": "Çıkış yap", - "sign_out_of": "{account} hesabından çıkış yap", - "sign_out_all": "Tüm hesaplardan çıkış yap", - "add_account": "Hesap ekle", - "set_as_default": "Varsayılan olarak ayarla", - "switch_account": "Hesap değiştir", - "contacts": "Kişiler", - "calendar": "Takvim", - "settings": "Ayarlar", - "admin": "Yönetim", - "files": "Dosyalar", - "loading_mailboxes": "Posta kutuları yükleniyor...", - "push_connected": "Anlık güncellemeler aktif", - "push_disconnected": "Anlık güncellemeler devre dışı", - "keyboard_shortcuts": "Klavye Kısayolları", - "theme": { - "light": "Açık tema", - "dark": "Koyu tema", - "system": "Sistem teması" - }, - "language": { - "title": "Dil" - }, - "mailboxes": { - "inbox": "Gelen Kutusu", - "sent": "Gönderilenler", - "drafts": "Taslaklar", - "trash": "Çöp Kutusu", - "archive": "Arşiv", - "starred": "Yıldızlı", - "all_mail": "Tüm Postalar", - "spam": "Spam", - "important": "Önemli" - }, - "unified_inbox": "Birleşik Gelen Kutusu", - "unified_sent": "Tüm Gönderilenler", - "unified_drafts": "Tüm Taslaklar", - "unified_trash": "Tüm Çöp Kutusu", - "unified_archive": "Tüm Arşiv", - "unified_junk": "Tüm Önemsiz", - "all_accounts": "Tüm Hesaplar", - "unified_mailbox": "Birleşik Posta Kutusu", - "expand": "Genişlet", - "collapse": "Daralt", - "expand_tooltip": "Genişlet", - "collapse_tooltip": "Daralt", - "mobile": { - "search": "Ara", - "compose": "Yeni E-posta", - "go_back": "Geri git", - "toggle_menu": "Menüyü aç/kapat" - }, - "clear_search": "Aramayı temizle", - "vacation_active": "Tatil otomatik yanıtı aktif", - "demo_banner": "Demo Modu", - "demo_reset": "Sıfırla", - "demo_tour": "Tur", - "tags": "Etiketler", - "show_all_tags": "Tümünü göster ({count})", - "show_fewer_tags": "Daha az göster", - "folders": "Klasörler", - "shared": "Paylaşılan", - "mail": "Posta", - "nav_label": "Gezinme", - "add_app": "Uygulamalar", - "scheduled": "Zamanlandı", - "unified_all_unread": "Tüm okunmamışlar", - "unified_all_starred": "Tüm yıldızlılar", - "unified_all_mail": "Tüm postalar", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Varsayılan uygulamalar", - "description": "E-posta ve takvim bağlantılarının Bulwark'ta açılıp açılmayacağını seçin. Teknik olarak Bulwark, mailto: ve webcal: bağlantıları için protokol işleyicisi olarak kaydolur.", - "unsupported": "Bu tarayıcı veya bağlantı manuel protokol işleyicisi kaydını desteklemiyor. Yüklü PWA'yı yine de tarayıcı veya işletim sistemi ayarlarından kullanabilirsiniz.", - "mailto_label": "E-posta bağlantıları", - "mailto_description": "mailto: bağlantılarını Bulwark'ta önceden doldurulmuş düzenleyiciyle açın.", - "protocol_open_mode_label": "Protokol bağlantıları açılırken", - "protocol_open_mode_description": "Bulwark'ın mailto: ve webcal: bağlantılarını yeni bir sekmede açmasını mı yoksa açık bir oturumu yeniden kullanmasını mı istediğinizi seçin. Etkin oturum seçeneği, tarayıcı odağı engellerse Bulwark'ı öne getirmek için yedek bildirime tıklayabilmeniz amacıyla bildirim izni gerektirir.", - "protocol_open_mode_active_session": "Mümkünse etkin oturumda aç", - "protocol_open_mode_new_tab": "Her zaman yeni sekme aç", - "focus_notification_title": "Bulwark'ı aç", - "focus_notification_body": "Bağlantı Bulwark'ta açıldı. Pencereyi öne getirmek için tıklayın.", - "webcal_label": "Takvim bağlantıları", - "webcal_description": "webcal: bağlantılarını Bulwark'ta önceden doldurulmuş takvim aboneliği penceresiyle açın.", - "register_mailto": "E-posta uygulaması olarak kaydet", - "register_webcal": "Takvim uygulaması olarak kaydet", - "mailto_registered": "E-posta işleyicisi kaydı istendi", - "webcal_registered": "Takvim işleyicisi kaydı istendi", - "registration_failed": "Protokol işleyicisi kaydı başarısız oldu", - "opening_mailto": "Düzenleyici açılıyor...", - "opening_webcal": "Takvim açılıyor...", - "browser_note": "Tarayıcınız veya işletim sisteminiz bunu onaylamanızı isteyebilir ve Bulwark'ın varsayılan uygulama olarak seçilebilmesi için yüklenmiş olmasını gerektirebilir.", - "select_account_title": "Hesap seç", - "select_mailto_account": "Bu e-posta bağlantısını hangi hesabın açacağını seçin.", - "select_webcal_account": "Bu takvim bağlantısını hangi hesabın açacağını seçin.", - "select_account_note": "Bu seçim yalnızca bu protokol bağlantısı için geçerlidir.", - "detail_to": "Kime", - "detail_subject": "Konu", - "detail_no_subject": "Konu yok", - "detail_calendar": "Takvim", - "detail_source": "Kaynak", - "active_account": "Etkin", - "switching_account": "Hesap değiştiriliyor..." - }, - "sidebar_apps": { - "modal_title": "Kenar Çubuğu Uygulamaları", - "add_new": "Uygulama Ekle", - "edit_app": "Uygulamayı Düzenle", - "name_label": "Ad", - "name_placeholder": "Uygulamam", - "name_required": "Ad zorunludur", - "url_label": "URL", - "url_required": "URL zorunludur", - "url_invalid": "Geçerli bir http veya https URL girin", - "icon_label": "Simge", - "icon_required": "Simge zorunludur", - "open_mode_label": "Açılış Modu", - "open_new_tab": "Yeni Sekme", - "open_inline": "Yerleşik", - "cancel": "İptal", - "add": "Ekle", - "update": "Güncelle", - "delete": "Sil", - "delete_confirm_title": "Uygulamayı Sil", - "delete_confirm": "\"{name}\" uygulamasını silmek istediğinizden emin misiniz?", - "no_apps": "Henüz uygulama eklenmedi", - "no_apps_hint": "Kenar çubuğunuza özel uygulamalar ve bağlantılar ekleyin", - "managed_title": "Yöneticiniz tarafından sağlandı", - "managed_description": "Bu uygulamalar herkes için ayarlanmıştır ve değiştirilemez.", - "managed_badge": "Yönetilen", - "search_icons": "Simge ara...", - "show_popular": "Popüler", - "show_all": "Tümü", - "no_icons_found": "Simge bulunamadı", - "inline_badge": "Yerleşik", - "tab_badge": "Sekme", - "show_on_mobile": "Mobilde Göster" - }, - "email_list": { - "no_emails": "Mesaj bulunamadı", - "no_emails_description": "Bu posta kutusu boş", - "no_search_results": "Sonuç bulunamadı", - "no_search_results_description": "Arama teriminizi veya filtrelerinizi değiştirmeyi deneyin", - "loading": "E-postalar yükleniyor...", - "unread": "okunmamış", - "to_me": "Bana", - "to_recipients": "{count} alıcıya", - "and_others": "ve {count} diğer", - "draft": "Taslak", - "starred": "Yıldızlı", - "pinned": "Sabitlendi", - "replied": "Yanıtlandı", - "forwarded": "İletildi", - "has_attachment": "Ek içeriyor", - "conversations_count": "{total} konuşmadan {count} tanesi", - "conversations_count_plus": "{count}+ konuşma", - "conversations_count_simple": "{count} konuşma", - "no_conversations": "Konuşma yok", - "loading_more": "Daha fazla e-posta yükleniyor...", - "no_more_emails": "Yüklenecek başka e-posta yok", - "batch_actions": { - "select": "E-postaları seç", - "select_all": "Tümünü seç", - "selected_messages": "{count, plural, one {1 e-posta} other {# e-posta}} seçildi", - "mark_read": "Okundu olarak işaretle", - "mark_unread": "Okunmadı olarak işaretle", - "delete": "Sil", - "delete_confirm_title": "E-postaları sil", - "delete_confirm_message": "{count, plural, one {1 e-postayı} other {# e-postayı}} silmek istediğinizden emin misiniz?", - "clear_selection": "Seçimi temizle" - }, - "permanent_delete": "Kalıcı olarak sil", - "permanent_delete_confirm_title": "Kalıcı olarak sil", - "permanent_delete_confirm_message": "Bu e-posta kalıcı olarak silinecek. Bu işlem geri alınamaz.", - "permanent_delete_confirm_batch_message": "Bu {count, plural, one {1 e-posta} other {# e-posta}} kalıcı olarak silinecek. Bu işlem geri alınamaz.", - "empty_folder": { - "button": "Klasörü boşalt", - "confirm_title": "Klasörü boşalt", - "confirm_message": "Bu klasördeki tüm e-postalar kalıcı olarak silinecek. Bu işlem geri alınamaz.", - "confirm_button": "Klasörü boşalt", - "junk_hint": "Tüm iletileri kalıcı olarak kaldırmak için Önemsiz klasörünü boşaltabilirsiniz.", - "trash_hint": "Tüm iletileri kalıcı olarak kaldırmak için Çöp Kutusu klasörünü boşaltabilirsiniz." - }, - "no_scheduled_emails": "Zamanlanmış e-posta yok", - "no_scheduled_emails_description": "Daha sonra gönderilecek iletiler burada görünür.", - "scheduled_count": "{count, plural, one {1 zamanlanmış e-posta} other {# zamanlanmış e-posta}}", - "scheduled_actions_hint": "Zamanlanmış iletiler zamanlama işlemlerinden iptal edilebilir, yeniden zamanlanabilir veya düzenlenebilir.", - "cancel_scheduled_send": "Göndermeyi iptal et", - "reschedule_send": "Yeniden zamanla", - "cancel_and_edit": "İptal et ve düzenle", - "cancel_and_compose_again": "İptal et ve yeniden yaz", - "reschedule_prompt": "Yeni tarih/saat girin, ör. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Gönderen bir okundu bilgisi istiyor:", - "send": "Bilgi gönder", - "ignore": "Yoksay", - "sent": "Okundu bilgisi gönderildi.", - "send_failed": "Okundu bilgisi gönderilemedi", - "mdn_subject": "Okundu: {subject}", - "mdn_body": "Bu, {recipient} adresine gönderdiğiniz iletinin okundu bilgisidir.\n\nNot: Bu bilgi yalnızca iletinin alıcının bilgisayarında görüntülendiğini belirtir. Alıcının içeriği okuduğunu veya anladığını garanti etmez." - }, - "no_email_selected": "E-posta seçilmedi", - "no_email_description": "Burada görüntülemek için listeden bir e-posta seçin", - "no_conversation_selected": "Konuşma seçilmedi", - "no_conversation_description": "Okumak için listeden bir konuşma seçin", - "compose": "Yeni E-posta", - "compose_hint": "Yeni e-posta oluştur", - "no_subject": "(Konu Yok)", - "no_body_content": "(İçerik yok)", - "no_preview_available": "Önizleme kullanılamıyor", - "show_quoted_text": "Alıntılanan metni göster", - "hide_quoted_text": "Alıntılanan metni gizle", - "loading_email": "E-posta yükleniyor...", - "loading": "Yükleniyor...", - "reply": "Yanıtla", - "reply_all": "Tümünü Yanıtla", - "forward": "İlet", - "delete": "Sil", - "archive": "Arşivle", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "Yıldızla", - "unstar": "Yıldızı Kaldır", - "mark_unread": "Okunmadı olarak işaretle", - "mark_read": "Okundu olarak işaretle", - "unread": "Okunmadı", - "read": "Okundu", - "spam_short": "Spam", - "not_spam_short": "Spam değil", - "move": "Taşı", - "print": "Yazdır", - "view_source": "Kaynağı görüntüle", - "fullscreen": "Tam ekran", - "exit_fullscreen": "Tam ekrandan çık", - "export_email": ".eml olarak dışa aktar", - "forward_as_attachment": "Ek olarak ilet", - "import_email": ".eml veya .zip içe aktar", - "keyboard_shortcuts": "Klavye kısayolları (?)", - "email_source": "E-posta Kaynağı", - "draft_banner": "Bu ileti bir taslaktır", - "edit_draft": "Düzenle", - "copy_source": "Panoya kopyala", - "source_copied": "Kaynak panoya kopyalandı", - "attachments": "Ekler", - "important": "Önemli", - "download": "İndir", - "download_all": "Tümünü indir", - "from": "Kimden", - "to": "Kime", - "cc": "CC", - "bcc": "BCC", - "date": "Tarih", - "subject": "Konu", - "show_details": "Ayrıntıları göster", - "hide_details": "Ayrıntıları gizle", - "external_content_warning": "Görseller ve harici içerik engellendi", - "load_external_content": "Görselleri yükle", - "trust_sender": "Bu gönderene her zaman güven", - "back_to_list": "Listeye dön", - "view_contact": "Kişiyi görüntüle", - "message_details": "İleti Ayrıntıları", - "more_reply_options": "Daha fazla yanıt seçeneği", - "set_tag": "Etiket ayarla", - "tag": "Etiket", - "more_actions": "Diğer işlemler", - "previous": "Önceki", - "next": "Sonraki", - "move_to": "Şuraya taşı...", - "remove_tag": "Etiketi kaldır", - "tag_filter_placeholder": "Etiketleri filtrele", - "tag_no_matches": "Eşleşen etiket yok", - "more_count": "+{count} daha", - "characters_count": "{count} karakter", - "quick_reply_placeholder": "Hızlı yanıt yazın...", - "more_options": "Daha fazla seçenek", - "sending": "Gönderiliyor...", - "security_authentication": "Güvenlik ve Kimlik Doğrulama", - "technical_details": "Teknik Ayrıntılar", - "message_id_label": "İleti Kimliği:", - "reply_to_label": "Yanıt Adresi:", - "delivery_time_label": "Teslim süresi:", - "conversation_part_label": "Konuşmanın parçası:", - "previous_messages": "{count} önceki ileti", - "previous_messages_plural": "{count} önceki ileti", - "time": { - "day": "gün", - "days": "gün", - "hour": "saat", - "hours": "saat", - "minute": "dakika", - "minutes": "dakika" - }, - "unknown_sender": "Bilinmeyen", - "recipient_me": "ben", - "recipient_and_others": "{name} ve {count} diğer", - "recipient_to_prefix": "Kime:", - "authentication": { - "title": "Kimlik Doğrulama", - "status": { - "verified": "Doğrulandı", - "warning": "Uyarı", - "none": "Doğrulanmadı" - }, - "spf": { - "pass": "SPF Geçti", - "fail": "SPF Başarısız", - "none": "SPF Yok" - }, - "dkim": { - "pass": "DKIM Geçerli", - "fail": "DKIM Geçersiz", - "none": "DKIM Yok" - }, - "dmarc": { - "pass": "DMARC Geçti", - "fail": "DMARC Başarısız", - "none": "DMARC Yok" - }, - "spam_score": "Spam Puanı", - "tooltip_spf": "Sender Policy Framework: gönderen sunucunun, etki alanı adına e-posta gönderme yetkisine sahip olduğunu doğrular", - "tooltip_dkim": "DomainKeys Identified Mail: kriptografik bir imza kullanarak e-postanın iletim sırasında değiştirilmediğini doğrular", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: SPF ve DKIM'in göndericinin etki alanıyla uyumlu olmasını sağlar ve hatalar için bir ilke belirler", - "policy": "İlke", - "result": { - "pass": "Başarılı", - "fail": "Başarısız", - "softfail": "Hafif başarısızlık", - "neutral": "Nötr", - "permerror": "Kalıcı hata", - "temperror": "Geçici hata", - "none": "Yok" - } - }, - "headers": { - "routing": "Yönlendirme", - "received": "Alındı", - "message_id": "İleti Kimliği", - "list_info": "Liste Bilgisi" - }, - "tooltips": { - "reply": "Yanıtla (r)", - "reply_all": "Tümünü Yanıtla (a)", - "forward": "İlet (f)", - "archive": "Arşivle (e)", - "delete": "Sil (# veya Del)", - "star": "Yıldızla (s)", - "unstar": "Yıldızı Kaldır (s)", - "compose": "Yeni E-posta (c)", - "previous": "Önceki e-posta", - "next": "Sonraki e-posta", - "edit_draft": "Taslağı düzenle" - }, - "spam": { - "button_title": "Spam olarak bildir", - "not_spam_title": "Meşru olarak işaretle", - "toast_success": "Önemsiz'e taşındı", - "toast_batch": "{count} e-posta Önemsiz'e taşındı", - "toast_undo": "Geri al", - "toast_not_spam_success": "Gelen Kutusu'na taşındı", - "toast_not_spam_batch": "{count} e-posta Gelen Kutusu'na taşındı", - "error": "Spam bildirimi başarısız", - "error_not_spam": "E-posta geri yüklenemedi" - }, - "unsubscribe_banner": { - "label": "Bülten", - "button": "Aboneliği iptal et", - "confirm_title": "Bu göndericinin aboneliği iptal edilsin mi?", - "confirm_button": "Onayla", - "cancel": "İptal", - "success_http": "Abonelik iptali sayfası yeni sekmede açıldı", - "success_mailto": "Abonelikten çıkma e-postası gönderildi", - "confirm_message_http": "Abonelikten çıkma sayfası yeni sekmede açılacak.", - "confirm_message_mailto": "Gönderene abonelikten çıkma e-postası gönderilecek.", - "error": "Abonelik iptal edilemedi", - "dismiss": "Kapat" - }, - "calendar_invitation": { - "loading": "Etkinlik ayrıntıları yükleniyor…", - "title": "Takvim Daveti", - "published_title": "Yayımlanmış Etkinlik", - "response_title": "Etkinlik Yanıtı", - "update_title": "Etkinlik Güncellemesi", - "counter_title": "Karşı Teklif", - "refresh_title": "Yenileme İsteği", - "declined_counter_title": "Karşı Teklif Reddedildi", - "cancelled_title": "Etkinlik İptal Edildi", - "organizer": "{name} tarafından düzenleniyor", - "organizer_label": "Düzenleyen:", - "attendees": "{count, plural, one {# katılımcı} other {# katılımcı}}", - "accept": "Kabul et", - "maybe": "Belki", - "decline": "Reddet", - "add_to_calendar": "Takvime ekle", - "added": "Takvime eklendi", - "rsvp_sent": "Yanıt gönderildi", - "parse_error": "Davet okunamadı", - "action_failed": "Takvim işlemi tamamlanamadı.", - "no_calendar": "Takvim mevcut değil", - "published_info": "Bu etkinlik referans olarak paylaşıldı.", - "response_info": "Bu ileti bir katılımcı yanıtı içeriyor.", - "response_info_organizer": "Bu katılımcı yanıtı etkinliğinizi güncelliyor.", - "update_info": "Bu ileti mevcut bir etkinliği güncelliyor.", - "counter_info": "Bu ileti bir etkinlikte değişiklik öneriyor.", - "counter_info_organizer": "Bu katılımcı etkinliğinizde değişiklik önerdi.", - "refresh_info": "Bu ileti en güncel etkinlik ayrıntılarını istiyor.", - "refresh_info_organizer": "Bir katılımcı en güncel etkinlik ayrıntılarını talep etti.", - "declined_counter_info": "Organizatör karşı teklifi reddetti.", - "authentication_failed_info": "Bu davet için posta kimlik doğrulama denetimleri başarısız oldu. Takvim işlemlerini dikkatli gerçekleştirin.", - "authentication_missing_info": "Bu davet doğrulanmış posta kimlik doğrulaması içermiyor. Bir şey olağandışı görünüyorsa ayrıntıları organizatörle teyit edin.", - "sender_mismatch_info": "Bu davet {sender} tarafından gönderildi; ancak takvim verisinde organizatör olarak {organizer} listeleniyor.", - "sender_mismatch_unverified_info": "Bu davet {sender} tarafından gönderildi; ancak takvim verisinde organizatör olarak {organizer} listeleniyor ve ileti doğrulanamadı.", - "organizer_role": "Bu etkinliği siz düzenliyorsunuz", - "your_response": "Yanıtınız: {status}", - "response_needed": "Yanıt gerekiyor", - "response_accepted": "Kabul edildi", - "response_tentative": "Belirsiz", - "response_declined": "Reddedildi", - "response_delegated": "Devredildi", - "actor_sent_info": "{name} tarafından gönderildi.", - "actor_response_info": "{name}, {status} yanıtını verdi.", - "actor_counter_info": "{name} bu etkinlikte değişiklik önerdi.", - "actor_refresh_info": "{name} en güncel etkinlik ayrıntılarını istedi.", - "actor_declined_counter_info": "{name} karşı teklifi reddetti.", - "actor_note": "Not: {comment}", - "actor_unknown": "Biri", - "proposed_changes": "Önerilen değişiklikler", - "change_title": "Başlık", - "change_time": "Saat", - "change_location": "Konum", - "change_description": "Açıklama", - "change_empty": "Yok", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Önerilen değişiklikleri uygula", - "proposal_applied": "Önerilen değişiklikler uygulandı.", - "review_proposal": "Teklifi incele", - "review_request": "İsteği incele", - "view_in_calendar": "Takvimde görüntüle", - "select_calendar": "Takvim seç", - "already_in_calendar": "Zaten takviminizde", - "request_info": "Bu etkinliğe davet edildiniz. Müsaitliğinizi bildirmek için yanıt verin.", - "cancel_info": "Organizatör bu etkinliği iptal etti.", - "event_updated": "Güncelleme #{sequence}", - "event_status_tentative": "Belirsiz", - "event_status_cancelled": "İptal edildi", - "expand": "Ayrıntıları göster", - "collapse": "Ayrıntıları gizle" - }, - "send": "Gönder", - "more": "daha fazla", - "details": { - "recipients_routing": "Alıcılar ve yönlendirme", - "authentication_security": "Kimlik doğrulama ve güvenlik", - "identifiers_threading": "Tanımlayıcılar ve konu zinciri", - "mailing_list": "Posta listesi", - "message_properties": "İleti özellikleri", - "sent": "Gönderildi", - "received": "Alındı", - "delivery_time": "Teslim süresi", - "in_reply_to": "Yanıtlanan", - "references": "Referanslar", - "thread_id": "Konu zinciri kimliği", - "size": "Boyut", - "mime_type": "MIME türü", - "attachments_summary": "{count} dosya · {size}", - "list_id": "Liste kimliği", - "list_help": "Liste yardımı", - "list_post": "Liste gönderimi", - "list_unsubscribe": "Abonelikten çık", - "iprev": "Ters DNS", - "spam_status": "Spam durumu", - "ai_verdict": "Yapay zeka kararı", - "account": "Hesap", - "no_subject": "(konu yok)" - }, - "scheduled_banner": "{date} tarihinde gönderilmek üzere zamanlandı", - "scheduled_send_created": "E-posta gönderim için zamanlandı", - "cancel_scheduled_send": "Göndermeyi iptal et", - "reschedule_send": "Yeniden zamanla", - "cancel_and_edit": "İptal et ve düzenle", - "cancel_and_compose_again": "İptal et ve yeniden yaz", - "reschedule_prompt": "Yeni tarih/saat girin, ör. 2026-05-04T15:30", - "scheduled_actions_only": "Zamanlandı görünümü yalnızca zamanlama işlemlerini destekler", - "undo_send_scheduled": "İleti gönderim için zamanlandı", - "undo_send": "Göndermeyi geri al", - "contact_sidebar": { - "title": "Kişi", - "close": "Kenar çubuğunu kapat", - "action_email": "E-posta", - "action_email_title": "E-posta gönder", - "action_copy": "Kopyala", - "action_copy_title": "E-postayı kopyala", - "action_edit_title": "Kişiyi düzenle", - "section_emails": "E-postalar", - "section_phones": "Telefonlar", - "section_organizations": "Kuruluşlar", - "section_addresses": "Adresler", - "section_notes": "Notlar", - "not_in_contacts": "Kişilerinizde yok", - "add_to_contacts": "Kişilere ekle", - "copied": "Kopyalandı!", - "copy_failed": "Kopyalanamadı" - }, - "send_now": "Şimdi gönder" - }, - "email_composer": { - "read_receipt_on": "Okundu bilgisi istendi (devre dışı bırakmak için tıklayın)", - "read_receipt_off": "Okundu bilgisi iste", - "dsn_on": "Teslimat bildirimi istendi (kapatmak için tıklayın)", - "dsn_off": "Teslimat bildirimi iste", - "format_plain_text": "Düz metne geç", - "format_rich_text": "Zengin metne (HTML) geç", - "require_tls_on": "Şifreli teslimat zorunlu (kapatmak için tıklayın)", - "require_tls_off": "Şifreli teslimat zorunlu kıl (TLS)", - "new_message": "Yeni İleti", - "signature_edit_hint": "İmzayı düzenlemek için çift tıklayın", - "reply": "Yanıtla", - "reply_all": "Tümünü Yanıtla", - "forward": "İlet", - "reply_to": "Yanıtla", - "reply_all_to": "Tümünü Yanıtla", - "forward_message": "İlet", - "from": "Kimden", - "to": "Kime", - "cc": "CC", - "bcc": "BCC", - "subject": "Konu", - "body_placeholder": "İletinizi yazın...", - "send": "Gönder", - "cancel": "İptal", - "attach": "Ekle", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Vazgeç", - "discard_draft_title": "Taslaktan vazgeçilsin mi?", - "discard_draft_confirm": "Kaydedilmemiş değişiklikleriniz var. Bu taslaktan vazgeçmek istiyor musunuz?", - "saving": "Kaydediliyor...", - "sending": "Gönderiliyor...", - "add_link": "Bağlantı ekle", - "link_url_prompt": "URL'yi girin", - "draft_saved": "Taslak kaydedildi", - "save_failed": "Kaydedilemedi", - "to_placeholder": "Alıcı e-posta adresleri", - "cc_placeholder": "Cc alıcıları", - "bcc_placeholder": "Bcc alıcıları", - "subject_placeholder": "Konu", - "cc_label": "Cc:", - "bcc_label": "Bcc:", - "subject_label": "Konu:", - "file_size_kb": "KB", - "prefix": { - "forward": "İlt:", - "reply": "Yn:" - }, - "no_subject": "(Konu Yok)", - "unknown_sender": "Bilinmeyen", - "quote": { - "reply_header": "{date} tarihinde {sender} yazdı:", - "forward_header": "---------- İletilen ileti ----------", - "from": "Kimden: {sender}", - "date": "Tarih: {date}", - "subject": "Konu: {subject}", - "to": "Kime: {recipients}" - }, - "remove_sub_address": "Alt adresi kaldır", - "from_override": { - "toggle_off": "Geçersiz kıl", - "toggle_on": "Geçersiz kılmayı iptal et", - "toggle_tooltip": "Gönderen adını ve adresini serbestçe düzenleyin. Posta hâlâ kimliğiniz üzerinden gönderilir.", - "name_label": "Gönderen adı", - "name_placeholder": "Ad", - "email_label": "Gönderen e-posta adresi", - "email_placeholder": "alias@example.com", - "envelope_notice": "Sunucunuz bu adresi zarf göndereni olarak kabul etmezse yerine {identity} kullanılır ve alıcılar bunu Return-Path başlığında görebilir." - }, - "use_template": "Şablon", - "save_as_template": "Şablon Olarak Kaydet", - "validation": { - "recipient_required": "Göndermek için bir alıcı ekleyin", - "body_required": "Bir ileti yazın veya dosya ekleyin", - "attachments_uploading": "Ekler hâlâ yükleniyor — yükleme bitince gönderilecek", - "attachment_upload_failed": "Gönderilmedi - bir ek yüklenemedi. Eki kaldırıp tekrar deneyin." - }, - "upload_progress": "{uploaded} / {total} yükleniyor", - "upload_cancel": "Yüklemeyi iptal et", - "upload_failed": "{filename} yüklenemedi", - "attachment_too_large": "\"{name}\" sunucunun izin verdiğinden büyük (dosya başına {max})", - "attachments_total_too_large": "Ekler sunucunun ileti başına {max} sınırını aşar", - "drop_files": "Eklemek için dosyaları buraya bırakın", - "show_less": "Daha az göster", - "send_failed": "E-posta gönderilemedi", - "send_timeout": "Sunucu yanıt vermeyi bıraktı. Yeniden göndermeden önce Gönderilenler klasörünü kontrol edin — ileti gönderilmiş olabilir.", - "continue_draft": "Taslağa devam et", - "close_draft_title": "Taslak kaydedilsin mi yoksa silinsin mi?", - "close_draft_message": "Kaydedilmemiş değişiklikleriniz var. Bunu taslak olarak kaydetmek ister misiniz yoksa vazgeçmek mi?", - "smime_sign_on": "S/MIME imzalama etkin", - "smime_sign_off": "S/MIME imzalamayı etkinleştir", - "smime_encrypt_on": "S/MIME şifreleme etkin", - "smime_encrypt_off": "S/MIME şifrelemeyi etkinleştir", - "smime_encrypt_unavailable": "S/MIME şifreleme kullanılamıyor – alıcı sertifikaları eksik", - "smime_unlock_title": "S/MIME Anahtarının Kilidini Aç", - "smime_unlock_message": "S/MIME imzalama anahtarınızın kilidini açmak için parolayı girin.", - "smime_unlock_button": "Kilidi Aç", - "smime_passphrase_placeholder": "Parola", - "forgot_attachment": { - "title": "Ek eklemeyi unuttunuz mu?", - "message": "İletinizde \"{keyword}\" geçiyor ancak hiçbir dosya eklenmemiş. Yine de gönderilsin mi?", - "send_anyway": "Yine de gönder", - "back": "Düzenlemeye dön" - }, - "empty_subject": { - "title": "Konu olmadan gönderilsin mi?", - "message": "Bu iletinin konusu yok. Yine de gönderilsin mi?", - "dont_ask_again": "Bir daha sorma", - "send_anyway": "Yine de gönder", - "back": "Düzenlemeye dön" - }, - "schedule_send": "Göndermeyi zamanla", - "schedule_send_description": "Sunucunun bu iletiyi ne zaman göndereceğini seçin.", - "schedule_send_required": "Bir tarih ve saat seçin.", - "schedule_send_invalid": "Geçerli bir tarih ve saat girin.", - "schedule_send_future": "Gelecekte bir tarih ve saat seçin.", - "schedule_send_too_late": "Bu zaman sunucunun izin verdiğinden daha geç.", - "schedule_send_unsupported": "Bu hesap için zamanlanmış gönderim desteklenmiyor.", - "schedule_send_cleanup_warning": "Zamanlanmış gönderim oluşturuldu, ancak taslak temizliği başarısız oldu.", - "send_delay_unsupported": "Bu hesap için gönderim gecikmesi desteklenmiyor.", - "send_delay_unsupported_confirm": "Bu hesap gönderim gecikmesini desteklemiyor. Hemen gönderilsin mi?", - "recipient_edit_email": "E-posta adresini düzenle", - "recipient_edit_name": "Görünen adı düzenle", - "recipient_email_placeholder": "E-posta adresi", - "recipient_name_placeholder": "Görünen ad", - "autocomplete_search_server": "Sunucuda ara", - "autocomplete_searching": "Aranıyor...", - "toolbar": { - "bold": "Kalın", - "italic": "İtalik", - "underline": "Altı çizili", - "strikethrough": "Üstü çizili", - "text_color": "Metin rengi", - "remove_color": "Rengi kaldır", - "heading_1": "Başlık 1", - "heading_2": "Başlık 2", - "bullet_list": "Madde işaretli liste", - "ordered_list": "Numaralı liste", - "quote": "Alıntı", - "code_block": "Kod bloğu", - "align_left": "Sola hizala", - "align_center": "Ortala", - "align_right": "Sağa hizala", - "text_direction": "Metin yönü (RTL/LTR)", - "link": "Bağlantı", - "table": "Tablo", - "clear_formatting": "Biçimlendirmeyi temizle", - "undo": "Geri al", - "redo": "Yinele", - "add_row_above": "Üste satır ekle", - "add_row_below": "Alta satır ekle", - "add_column_before": "Öncesine sütun ekle", - "add_column_after": "Sonrasına sütun ekle", - "delete_row": "Satırı sil", - "delete_column": "Sütunu sil", - "toggle_header_row": "Başlık satırını aç/kapat", - "delete_table": "Tabloyu sil", - "pick_size": "Boyut seç", - "font_size": "Yazı boyutu", - "font_size_default": "Varsayılan", - "background_color": "Arka plan rengi", - "remove_background_color": "Arka plan rengini kaldır" - }, - "send_filing_warning": "Gönderildi - ancak sonrasındaki temizleme başarısız oldu, eski bir taslak kalabilir." - }, - "confirm_dialog": { - "confirm": "Onayla", - "cancel": "İptal" - }, - "common": { - "loading": "Yükleniyor...", - "error": "Hata", - "success": "Başarılı", - "cancel": "İptal", - "save": "Kaydet", - "delete": "Sil", - "edit": "Düzenle", - "close": "Kapat", - "search": "Ara", - "refresh": "Yenile", - "settings": "Ayarlar", - "help": "Yardım", - "logout": "Çıkış yap", - "yes": "Evet", - "no": "Hayır", - "unknown": "Bilinmeyen", - "app_title": "Webmail", - "reconnecting": "Bağlantı kesildi. Yeniden bağlanılmaya çalışılıyor…", - "rate_limited_title": "Sunucu kimlik doğrulaması geçici olarak hız sınırlamasına tabi.", - "rate_limited_detail": "Hesap kilitlenmesini önlemek için Bulwark arka plan isteklerini duraklattı. {seconds} saniye içinde yeniden deneniyor.", - "rate_limited_action_title": "Kilitlenmeyi önlemek için istek duraklatıldı.", - "rate_limited_action_detail": "Bulwark, daha fazla kimlik doğrulama isteği göndermeden önce sunucunun soğuma süresinin bitmesini bekliyor. {seconds} saniye içinde tekrar deneyin.", - "pull_to_refresh": "Yenilemek için çekin", - "release_to_refresh": "Yenilemek için bırakın", - "refreshing": "Yenileniyor…" - }, - "notifications": { - "email_sent": "E-posta başarıyla gönderildi", - "email_deleted": "E-posta silindi", - "email_archived": "E-posta arşivlendi", - "emails_deleted": "{count, plural, other {# e-posta silindi}}", - "emails_archived": "{count, plural, other {# e-posta arşivlendi}}", - "error_archiving": "E-posta arşivlenemedi", - "error_updating": "E-posta güncellenemedi", - "email_starred": "E-posta yıldızlandı", - "email_unstarred": "E-postanın yıldızı kaldırıldı", - "email_marked_read": "E-posta okundu olarak işaretlendi", - "email_marked_unread": "E-posta okunmadı olarak işaretlendi", - "copied_to_clipboard": "Panoya kopyalandı", - "source_copied": "Kaynak panoya kopyalandı", - "error_sending": "E-posta gönderilemedi", - "error_deleting": "E-posta silinemedi", - "error_loading": "E-postalar yüklenemedi", - "new_email": "Yeni e-posta", - "new_email_from": "{sender} tarafından", - "click_to_view": "Görüntülemek için tıklayın", - "email_moved": "E-posta taşındı", - "emails_moved": "{count} e-posta taşındı", - "moved_to_mailbox": "{mailbox} klasörüne taşındı", - "move_failed": "Taşıma başarısız", - "move_error": "E-postalar seçilen klasöre taşınamadı", - "email_tagged": "E-posta etiketlendi", - "emails_tagged": "{count} e-posta etiketlendi", - "tag_failed": "Etiketleme başarısız", - "identity_created": "Kimlik başarıyla oluşturuldu", - "identity_updated": "Kimlik başarıyla güncellendi", - "identity_deleted": "Kimlik silindi", - "identity_set_primary": "Birincil kimlik güncellendi", - "identity_create_failed": "Kimlik oluşturulamadı: {error}", - "identity_update_failed": "Kimlik güncellenemedi: {error}", - "identity_delete_failed": "Kimlik silinemedi: {error}", - "identity_unauthorized": "Bu e-posta adresinden gönderme yetkiniz yok", - "identity_not_found": "Kimlik bulunamadı", - "vacation_saved": "Tatil otomatik yanıtı ayarları kaydedildi", - "vacation_save_failed": "Tatil otomatik yanıtı ayarları kaydedilemedi", - "filters_saved": "Filtreler başarıyla kaydedildi", - "filters_save_failed": "Filtreler kaydedilemedi", - "filters_deleted": "Filtre kuralı silindi", - "templates_exported": "Şablonlar başarıyla dışa aktarıldı", - "templates_imported": "{count, plural, one {# şablon} other {# şablon}} içe aktarıldı", - "templates_import_errors": "Bazı şablonlar içe aktarılamadı", - "templates_import_empty": "Dosyada şablon bulunamadı", - "export_email_error": "E-posta dışa aktarılamadı", - "import_email_success": "E-posta başarıyla içe aktarıldı", - "import_email_error": "E-posta içe aktarılamadı" - }, - "date": { - "today": "Bugün", - "yesterday": "Dün", - "this_week": "Bu hafta", - "last_week": "Geçen hafta", - "this_month": "Bu ay", - "older": "Daha eski", - "just_now": "Az önce", - "minutes_ago": "{count} dakika önce", - "minutes_ago_plural": "{count} dakika önce", - "hours_ago": "{count} saat önce", - "hours_ago_plural": "{count} saat önce", - "days_ago": "{count} gün önce", - "days_ago_plural": "{count} gün önce" - }, - "language": { - "title": "Dil", - "english": "English", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Nederlands", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Dil seçin", - "switch_to_english": "İngilizce'ye geç", - "switch_to_french": "Fransızca'ya geç", - "switch_to_japanese": "Japonca'ya geç", - "switch_to_spanish": "İspanyolca'ya geç", - "switch_to_italian": "İtalyanca'ya geç", - "switch_to_german": "Almanca'ya geç", - "switch_to_dutch": "Hollandaca'ya geç", - "switch_to_portuguese": "Portekizce'ye geç", - "switch_to_russian": "Rusça'ya geç", - "switching": "Dil değiştiriliyor...", - "switch": "Dili değiştir", - "current": "Mevcut dil", - "polish": "Polski", - "switch_to_polish": "Lehçe'ye geç", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Farsçaya geç", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Moğolcaya geç" - }, - "settings": { - "title": "Ayarlar", - "back_to_mail": "Postaya Dön", - "save_success": "Ayarlar başarıyla kaydedildi", - "import_success": "Ayarlar başarıyla içe aktarıldı", - "import_error": "Ayarlar içe aktarılamadı", - "reset_confirm": "Tüm ayarları varsayılanlara sıfırlamak istediğinizden emin misiniz?", - "unsaved_changes": "Kaydedilmemiş değişiklikleriniz var", - "discard_changes": "Kaydedilmemiş değişiklikler atılsın mı?", - "discard": "Atla", - "keep_editing": "Düzenlemeye devam et", - "tabs": { - "appearance": "Görünüm", - "language": "Dil, Bölge ve Saat", - "email": "E-posta Davranışı", - "composer": "Yazıcı", - "privacy": "Gizlilik ve Güvenlik", - "account": "Hesap", - "identities": "Kimlikler", - "vacation": "Tatil Yanıtlayıcı", - "advanced": "Gelişmiş", - "calendar": "Takvim", - "filters": "Filtreler", - "templates": "Şablonlar", - "folders": "Klasörler", - "keywords": "Etiketler", - "security": "Güvenlik", - "files": "Dosyalar", - "contacts": "Kişiler", - "encryption": "Şifreleme", - "sidebar_apps": "Kenar Çubuğu Uygulamaları", - "protocol_handlers": "Varsayılan uygulamalar", - "notifications": "Bildirimler", - "layout": "Düzen", - "reading": "Okuma", - "composing": "Yazma", - "downloads": "İndirilenler", - "content_senders": "İçerik ve Göndericiler", - "about_data": "Hakkında ve Veriler", - "debug": "Hata Ayıklama" - }, - "tab_groups": { - "general": "Genel", - "account": "Hesap ve Kimlik", - "organization": "Posta Düzeni", - "apps": "Uygulamalar", - "system": "Sistem", - "appearance": "Görünüm", - "mail": "Posta", - "privacy": "Gizlilik ve Güvenlik", - "advanced": "Gelişmiş" - }, - "appearance": { - "message_list_order": { - "label": "İleti listesi sıralaması", - "description": "İletilerin listede nasıl sıralandığı. Sıralamayı sunucu yapar; bu yüzden yalnızca yüklenen iletiler değil, klasörün tamamı sıralanır.", - "preset": { - "chronological": "Kronolojik (en yeni önce)", - "unread_first": "Okunmamışlar önce", - "starred_first": "Yıldızlılar önce", - "tagged_first": "Etiketliler önce", - "custom": "Özel" - }, - "tag_label": "Etiket", - "advanced_show": "Gelişmiş: sıralama düzeylerini düzenle", - "advanced_hide": "Gelişmişi gizle", - "criterion_label": "{n}. düzey ölçütü", - "direction_label": "{n}. düzey yönü", - "criteria": { - "unread": "Okunmamış", - "starred": "Yıldızlı", - "tag": "Etiket", - "receivedAt": "Alınma tarihi", - "sentAt": "Gönderilme tarihi", - "from": "Gönderen", - "subject": "Konu", - "size": "Boyut" - }, - "direction": { - "unread_first": "Okunmamışlar önce", - "read_first": "Okunmuşlar önce", - "starred_first": "Yıldızlılar önce", - "unstarred_first": "Yıldızsızlar önce", - "tagged_first": "Etiketliler önce", - "untagged_first": "Etiketsizler önce", - "newest_first": "En yeni önce", - "oldest_first": "En eski önce", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "En büyük önce", - "smallest_first": "En küçük önce" - }, - "add_level": "Düzey ekle", - "remove_level": "Düzeyi kaldır", - "move_up": "Yukarı taşı", - "move_down": "Aşağı taşı", - "tiebreak_note": "En fazla üç düzey; sonraki düzeyler eşitlikleri çözer. \"En yeni önce\" her zaman en son uygulanır.", - "scope": { - "label": "Tüm klasörlere uygula", - "description": "Kapalıyken sıralama yalnızca Gelen Kutusu için geçerlidir; diğer klasörler kronolojik kalır." - }, - "unsupported_note": "Posta sunucunuz okunma durumu, yıldız veya etikete göre sıralamayı desteklemiyor. Bu düzeyler yok sayılır ve liste kronolojik kalır." - }, - "title": "Görünüm", - "description": "Web postanızın görünümünü özelleştirin", - "theme": { - "label": "Tema", - "description": "Tercih ettiğiniz renk şemasını seçin", - "light": "Açık", - "dark": "Koyu", - "system": "Sistem" - }, - "language": { - "label": "Dil", - "description": "Tercih ettiğiniz dili seçin" - }, - "font_size": { - "label": "Yazı Tipi Boyutu", - "description": "Daha iyi okunabilirlik için metin boyutunu ayarlayın", - "small": "Küçük", - "medium": "Orta", - "large": "Büyük" - }, - "list_density": { - "label": "Yoğunluk", - "description": "Arayüz genelinde boşluk ve doldurmayı kontrol edin", - "extra_compact": "Çok Sıkışık", - "compact": "Sıkışık", - "regular": "Normal", - "comfortable": "Rahat" - }, - "animations": { - "label": "Animasyonları Etkinleştir", - "description": "Akıcı geçişler ve efektler göster" - }, - "toolbar_position": { - "label": "Araç Çubuğu Konumu", - "description": "E-posta işlem düğmelerinin (Yanıtla, Arşivle, Sil vb.) nerede gösterileceği", - "top": "Üst", - "below_subject": "Konunun altı" - }, - "toolbar_labels": { - "label": "Araç Çubuğu Etiketlerini Göster", - "description": "Araç çubuğu simgelerinin yanında metin etiketleri görüntüleyin. Simgelere alışınca yer kazanmak için kapatabilirsiniz." - }, - "hide_account_switcher": { - "label": "Kenar Çubuğu Hesap Değiştiriciyi Gizle", - "description": "Klasör kenar çubuğunun üstündeki hesap seçiciyi gizleyin. Yine de alt gezinme çubuğundan hesap değiştirebilirsiniz." - }, - "show_rail_account_list": { - "label": "Gezinme Çubuğunda Hesap Avatarlarını Göster", - "description": "Hızlı geçiş için gezinme çubuğunun altında ayrı hesap dairelerini ve altında oturumu kapat düğmesini görüntüleyin." - }, - "unified_mailbox": { - "label": "Birleşik Posta Kutusu", - "description": "Bağlı tüm hesaplardaki birleşik klasörleri (Gelen Kutusu, Gönderilenler vb.) göster", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Grup gelen kutularını dahil et", - "description": "Paylaşılan/grup gelen kutularını da birleşik görünüme dahil et." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Renkli Kenar Çubuğu Simgeleri", - "description": "Klasör ve etiket simgelerini türe göre renklendir (mavi Gelen Kutusu, kırmızı İstem Dışı vb.). Tek renkli kenar çubuğu için kapatın." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Toplam mesaj sayısını göster", - "description": "Klasörlerin ve etiketlerin yanında, okunmamış sayısının yanı sıra toplam mesaj sayısını gösterir. Yalnızca okunmamışları göstermek için devre dışı bırakın." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro Arayüz (Deneysel)", - "description": "Yalnızca masaüstü için güçlü kullanıcı düzeni: çoklu sekmede ileti gezme ve hesaplar arası iş akışları. Standart arayüz etkilenmez; istediğiniz zaman geri dönebilirsiniz.", - "open_label": "Pro Arayüzü Aç", - "back_to_standard": "Standarda dön" - }, - "cross_unread": { - "label": "Tüm hesaplar: Okunmamış", - "description": "Tüm hesaplar bölümünde, tüm hesaplardaki okunmamış postaları listeleyen bir giriş gösterir; önemsiz, gönderilmiş, arşiv, çöp ve taslaklar dışındaki tüm klasörleri kapsar." - }, - "cross_starred": { - "label": "Tüm hesaplar: Yıldızlı", - "description": "Tüm hesaplar bölümünde, tüm hesaplardaki yıldızlı postaları listeleyen bir giriş gösterir; önemsiz, gönderilmiş, arşiv, çöp ve taslaklar dışındaki tüm klasörleri kapsar." - }, - "cross_all": { - "label": "Tüm hesaplar: Tüm postalar", - "description": "Tüm hesaplar bölümünde, tüm hesaplardaki tüm postaları listeleyen bir giriş gösterir; önemsiz, gönderilmiş, arşiv, çöp ve taslaklar dışındaki tüm klasörleri kapsar." - } - }, - "keywords": { - "title": "E-posta Etiketleri", - "description": "E-postalarınızı renklerle düzenlemek için etiketler tanımlayın. Bunlar sunucuda JMAP anahtar sözcükleri olarak saklanır.", - "add_keyword": "Etiket Ekle", - "label_field": "Görünen Ad", - "label_placeholder": "ör. İş, Kişisel, Acil", - "id_field": "Etiket Kimliği", - "id_placeholder": "ör. is, kisisel", - "color_field": "Renk", - "id_exists": "Bu etiket kimliği zaten mevcut", - "edit": "Etiketi düzenle", - "delete": "Etiketi sil", - "save": "Kaydet", - "add": "Ekle", - "cancel": "İptal", - "migrating": "Mevcut e-postalardaki etiket güncelleniyor…", - "migration_error": "Mevcut e-postalardaki etiket güncellenemedi", - "nesting": { - "label": "İç içe etiketler", - "description": "Etiketleri başka etiketlerin altına yerleştirin ve kenar çubuğunda ağaç olarak gösterin." - }, - "parent_field": "Üst etiket", - "no_parent": "Üst etiket yok", - "too_long": "Bu etiket yolu çok uzun (en fazla {max} karakter)", - "has_children_locked": "Bunun altında başka etiketler var, bu nedenle adı ve üst etiketi kilitli. Önce onları taşıyın veya kaldırın.", - "has_children_delete": "Önce bunun altındaki etiketleri kaldırın", - "visibility_field": "Kenar çubuğunda görünürlük", - "visibility": { - "show": "Göster", - "unread": "Okunmamış varsa göster", - "hide": "Gizle" - }, - "unrecognized": { - "title": "Etiketleri sunucudan kurtar", - "description": "Etiket adları ve renkleri bu tarayıcıda saklanır, ancak etiketlerin kendisi JMAP anahtar sözcükleri olarak e-postalarınızda bulunur. Burada karşılığı olmayan anahtar sözcükler için postanızı tarayın ve bunları geri ekleyin.", - "scan": "Etiketleri tara", - "rescan": "Yeniden tara", - "scanning": "Taranıyor… {total} e-postadan {scanned} tanesi", - "none": "E-postalarınızdaki tüm etiketler burada zaten tanımlı.", - "messages": "{count, plural, one {# e-posta} other {# e-posta}}", - "add_all": "{count} etiketin tümünü ekle", - "partial": "{total} e-postadan yalnızca en yeni {scanned} tanesi tarandı. Yalnızca eski postalarda kullanılan etiketler eksik olabilir.", - "error": "Postanız etiketler için taranamadı." - } - }, - "notifications": { - "test_sound": "Bildirim sesini test et", - "sounds": { - "default": "Varsayılan (Bip)", - "cheerful": "Neşeli", - "involved": "Odaklı", - "swift": "Hızlı Jest", - "relax": "Rahatla" - }, - "sound_selection": { - "title": "Bildirim Sesi", - "description": "Bildirimler için hangi sesin çalınacağını seçin", - "choose": "Ses", - "choose_desc": "Bir bildirim tonu seçin ve önizlemek için hoparlör simgesine tıklayın" - }, - "email": { - "title": "E-posta Bildirimleri", - "description": "Gelen e-postalar için bildirimleri yapılandırın", - "enabled": "E-posta bildirimleri", - "enabled_desc": "Yeni e-postalar geldiğinde bildirim göster", - "sound": "Bildirim sesi", - "sound_desc": "Yeni e-postalar geldiğinde sesli uyarı çal", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Takvim Bildirimleri", - "description": "Takvim etkinlikleri için bildirimleri yapılandırın", - "enabled": "Etkinlik bildirimleri", - "enabled_desc": "Yaklaşan takvim etkinlikleri için uyarılar göster", - "sound": "Bildirim sesi", - "sound_desc": "Takvim hatırlatıcıları için sesli uyarı çal", - "invitation_parsing": "E-posta davetlerini ayrıştır", - "invitation_parsing_desc": "E-posta eklerindeki takvim davetlerini algıla ve takvim işlemlerini göster" - }, - "push": { - "confirm_disable_message": "Bu cihaz, site kapalıyken uyarı almayı durduracak.", - "confirm_disable_title": "Arka plan bildirimleri devre dışı bırakılsın mı?", - "description": "Bu site kapalıyken yeni postalar için sistem bildirimleri alın. Bulwark push röle aracılığıyla iletilir; röle posta içeriğini asla görmez.", - "disable": "Devre dışı bırak", - "enable": "Etkinleştir", - "dismiss_aria": "Bildirim istemini kapat", - "ios_hint": "iOS'ta önce siteyi ana ekranınıza yükleyin – Safari Web Push'u yalnızca yüklü PWA'lara teslim eder.", - "reenable": "Yeniden kaydet", - "relay_desc": "Varsayılan olarak barındırılan Bulwark rölesini kullanır. Yalnızca kendiniz barındırıyorsanız değiştirin.", - "relay_label": "Push röle", - "relay_locked": "Yönetici tarafından ayarlandı", - "relay_locked_desc": "Push röle URL'si yöneticiniz tarafından ayarlandı ve değiştirilemez.", - "status_active": "Bu cihazda etkin", - "status_busy": "Çalışıyor…", - "status_inactive": "Bu cihazda etkinleştirilmedi", - "status_unsupported": "Bu tarayıcı Web Push'u desteklemiyor", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Arka plan bildirimleri", - "devices_title": "Kayıtlı cihazlar", - "devices_desc": "Bu hesap için arka plan bildirimleri almak üzere kayıtlı tüm cihazlar. Birini iptal ettiğinizde o cihaz bildirimleri hemen almayı bırakır.", - "devices_loading": "Kayıtlı cihazlar yükleniyor…", - "devices_empty": "Arka plan bildirimleri için kayıtlı cihaz yok.", - "devices_error": "Kayıtlı cihazlar yüklenemedi.", - "devices_refresh": "Yenile", - "device_this": "Bu cihaz", - "device_other": "Diğer cihaz", - "device_status_active": "Bildirim iletiyor", - "device_status_inactive": "Hiç bildirim iletmedi", - "device_status_unknown": "Bu aktarıcı tanımıyor", - "device_expires": "Bitiş: {date}", - "revoke": "İptal et", - "confirm_revoke_title": "Bu cihaz iptal edilsin mi?", - "confirm_revoke_message": "Arka plan bildirimlerini almayı hemen bırakacak. O cihazda push’u yeniden açarsanız yeniden kaydedilir.", - "confirm_revoke_message_this": "Bu cihaz arka plan bildirimlerini almayı hemen bırakacak." - } - }, - "language_region": { - "title": "Dil, Bölge ve Saat", - "description": "Dil, tarih biçimi, saat biçimi ve diğer bölgesel tercihler", - "language": { - "label": "Dil", - "description": "Tercih ettiğiniz dili seçin", - "english": "English", - "french": "Français" - }, - "date_format": { - "label": "Tarih Biçimi", - "description": "Tarihlerin e-posta listesinde nasıl gösterileceği", - "smart": "Akıllı (bölgeye duyarlı)", - "relative": "Göreceli (1 sa önce, 2 g önce)", - "full": "Her zaman tam tarih", - "preview_today": "Bugün:", - "preview_this_week": "Bu hafta:", - "preview_older": "Daha eski:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Saat Biçimi", - "description": "12 saatlik veya 24 saatlik saat arasında seçin", - "12h": "12 saatlik", - "24h": "24 saatlik" - }, - "first_day": { - "label": "Haftanın İlk Günü", - "description": "Haftayı Pazar veya Pazartesi başlat", - "sunday": "Pazar", - "monday": "Pazartesi" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Klasör değiştirirken aramayı temizle", - "description": "Bir klasöre tıklamak (Gelen Kutusu, Gönderilenler…) etkin aramayı temizler ve o klasörü gösterir. Kapalıyken arama korunur ve geçtiğiniz klasörde yeniden çalıştırılır." - }, - "request_read_receipt": { - "label": "Varsayılan olarak okundu bilgisi iste", - "description": "Yeni bir ileti yazarken okundu bilgisi isteğini önceden etkinleştir." - }, - "read_receipt_response": { - "label": "Okundu bilgisi isteklerine yanıt ver", - "description": "Gelen bir ileti okundu bilgisi istediğinde ne yapılacağı.", - "ask": "Her seferinde sor", - "always": "Her zaman gönder", - "never": "Asla gönderme" - }, - "title": "E-posta Davranışı", - "description": "E-postaların nasıl işleneceğini yapılandırın", - "mark_read": { - "label": "Okundu Olarak İşaretle", - "description": "Açıldığında e-postaların ne zaman okundu sayılacağı", - "instant": "Anında", - "delay_3s": "3 saniye sonra", - "delay_5s": "5 saniye sonra", - "never": "Hiçbir zaman" - }, - "delete_action": { - "label": "Silme İşlemi", - "description": "Bir e-postayı sildiğinizde ne olacağı", - "trash": "Çöp Kutusuna Taşı", - "trash_and_read": "Çöp Kutusuna Taşı ve okundu olarak işaretle", - "permanent": "Kalıcı Olarak Sil", - "warning": "E-postalar kalıcı olarak silinecek ve kurtarılamayacak. Bu işlem geri alınamaz." - }, - "message_spacing": { - "label": "İleti boşluğu", - "description": "Okuyucuda ileti gövdesinin çevresindeki boşluk", - "auto": "Otomatik (önerilen)", - "always": "Her zaman boşluk ekle", - "edge": "Kenardan kenara" - }, - "plain_text_font": { - "label": "Düz metin yazı tipi", - "description": "Düz metin e-postalar için kullanılan yazı tipi", - "mono": "Eş aralıklı", - "sans": "Normal (uygulamayla aynı)" - }, - "archive_mode": { - "label": "Arşivle", - "description": "Arşivlerken e-postaların nasıl düzenleneceği", - "single": "Tek bir klasör", - "year": "Yıl başına bir klasör", - "month": "Ay başına bir klasör", - "reorganize": "Mevcut arşivi yeniden düzenle", - "reorganize_success": "{count, plural, =0 {Yeniden düzenlenecek e-posta yok} =1 {1 e-posta yeniden düzenlendi} other {# e-posta yeniden düzenlendi}}", - "reorganize_error": "Arşiv yeniden düzenlenemedi" - }, - "permanently_delete_junk": { - "label": "İstem Dışı Postayı Kalıcı Sil", - "description": "İstem Dışı/Spam klasöründeki e-postaları Çöp Kutusu'na taşımak yerine kalıcı olarak sil" - }, - "mail_layout": { - "label": "Posta Düzeni", - "description": "Klasik bölünmüş okuma bölmesi ile Gmail tarzı odaklı okuma akışı arasında seçin.", - "split": "Bölünmüş bölme", - "split_description": "İleti listesi ve okuma bölmesini yan yana görünür tutun.", - "focus": "Odaklı liste", - "focus_description": "İleti başına bir satır gösterin ve klasör kenar çubuğu görünürken postayı tam genişlikte açın.", - "horizontal": "Okuma bölmesi altta", - "horizontal_description": "Mesaj listesini üstte gösterin ve seçilen mesajı altındaki okuma bölmesinde açın." - }, - "show_preview": { - "label": "Önizleme Metnini Göster", - "description": "Listede e-posta önizlemesini görüntüle", - "focus_description": "Odaklı tek satırlı ileti listesinde satır içi önizleme metnini görüntüle" - }, - "disable_threading": { - "label": "Konuşma Gruplamamayı Devre Dışı Bırak", - "description": "E-postaları konuşmaya göre gruplamak yerine ayrı iletiler olarak göster" - }, - "plain_text_mode": { - "label": "Yalnızca Düz Metin", - "description": "Zengin metin düzenleyicisini devre dışı bırak ve yanıtlar ve iletmeler dahil tüm e-postaları yalnızca düz metin olarak gönder" - }, - "auto_select_reply_identity": { - "label": "Yanıt Adresini Otomatik Seç", - "description": "Yanıtlarken, Kimden adresini iletiyi başlangıçta alan kimliğe otomatik olarak değiştir" - }, - "reply_identity_match": { - "label": "Alıcı adresi eşleştirme", - "description": "Hangi alıcı adreslerinin sizin sayılacağı. „Yalnızca tam adres“ sadece yapılandırılmış kimliklerinizden birini seçer; „aynı alan adı“ ayrıca kimlik alan adlarınızdaki diğer tüm adresleri catch-all takma adı olarak değerlendirir ve Kimden başlığını buna göre yeniden yazar. Bu adresler dağıtım listesiyse tam adresi seçin.", - "exact": "Yalnızca tam adres", - "domain": "Alan adlarımdaki herhangi bir adres" - }, - "signature_position": { - "label": "İmza konumu", - "description": "Yanıtlarda ve iletmelerde imzanızın nereye ekleneceği. Alıntılanan metnin üzerinde, yanıt için doğal bir kapanış olarak okunur; altında ise orijinal mesajı bir bütün hâlinde tutar.", - "above_quote": "Alıntılanan metinden önce", - "below_quote": "Alıntılanan metinden sonra" - }, - "signature_separator": { - "label": "İmza ayırıcı", - "description": "İmzayı standart \"-- \" ayırıcı satırıyla başlat (RFC 3676). Mesajdan doğrudan imzaya geçmek istiyorsanız kapatın." - }, - "sub_address_delimiter": { - "label": "Alt Adres Ayırıcı", - "description": "Kullanıcı adını alt adres etiketinden ayıran karakter. Posta sunucunuzun kullandığı ayırıcıyı seçin (ör. kullanici{delimiter}etiket@domain.com).", - "option": "{delimiter} (kullanici{delimiter}etiket@domain.com)", - "custom": "Özel…", - "custom_input_label": "Özel ayırıcı karakter" - }, - "attachment_click_action": { - "label": "Ek Tıklama İşlemi", - "description": "Bir dosya ekine tıklandığında önizleme mi yoksa anında indirme mi yapılacağını seçin", - "preview": "Mümkünse önizle", - "download": "Anında indir" - }, - "attachment_position": { - "label": "Ek Konumu", - "description": "E-posta başlığında eklerin nerede görüntüleneceği", - "beside-sender": "Göndericinin yanı", - "below-header": "Başlığın altı" - }, - "emails_per_page": { - "10": "10 e-posta", - "25": "25 e-posta", - "50": "50 e-posta", - "100": "100 e-posta", - "label": "Sayfa Başına E-posta", - "description": "Bir seferde yüklenecek e-posta sayısı" - }, - "always_light_mode": { - "label": "E-postaları Her Zaman Açık Modda Göster", - "description": "Uygulama koyu moddayken bile e-posta içeriğini açık modda göster; koyu mod dönüşüm sorunlarını önler" - }, - "external_content": { - "label": "Harici İçerik", - "description": "Görsel ve harici içerikle nasıl başa çıkılacağı", - "ask": "Her zaman sor", - "block": "Her zaman engelle", - "allow": "Her zaman izin ver" - }, - "trusted_senders": { - "label": "Güvenilir Göndericiler", - "description": "Görsellerinin otomatik yükleneceği göndericileri yönetin", - "count_zero": "Yok", - "count_one": "1 gönderici", - "count_other": "{count} gönderici", - "modal_title": "Güvenilir Göndericiler", - "empty_title": "Henüz güvenilir gönderici yok", - "empty_description": "Engellenen görseller içeren bir e-postayı görüntülerken \"Bu göndericiyi her zaman güven\" seçeneğine tıklayın.", - "add_manually": "Gönderici manuel ekle", - "add_button": "Ekle", - "add_placeholder": "E-posta adresi girin", - "search_placeholder": "Göndericileri ara...", - "no_results": "Aramanızla eşleşen gönderici yok", - "remove": "Kaldır", - "close": "Kapat", - "invalid_email": "Lütfen geçerli bir e-posta adresi girin", - "already_added": "Bu gönderici zaten güvenilir", - "save_error": "Kaydedilemedi - ayrıntılar için Kişiler hata ayıklama günlüğünü kontrol edin", - "use_address_book_label": "Adres defteriyle senkronize et", - "use_address_book_description": "Tüm cihazlarınızda senkronize olmaları için güvenilir göndericileri özel bir \"Güvenilir Göndericiler\" adres defterinde saklayın" - }, - "hover_actions": { - "label": "Hızlı Üzerine Gelme İşlemleri", - "description": "Listedeki bir e-postanın üzerine gelindiğinde hangi hızlı işlemlerin görüneceğini seçin", - "delete": "Sil", - "star": "Yıldız Ekle / Kaldır", - "mark_read": "Okundu / Okunmadı İşaretle", - "archive": "Arşivle", - "tag": "Etiketle", - "spam": "Spam Olarak İşaretle", - "not_spam": "Önemsiz değil", - "none_selected": "Hiç işlem seçilmedi", - "mode_label": "Görüntüleme Modu", - "mode_inline": "Satır içi", - "mode_floating": "Kayan", - "corner_label": "Kayan Konum", - "corner_top-left": "Sol Üst", - "corner_top-right": "Sağ Üst", - "corner_bottom-left": "Sol Alt", - "corner_bottom-right": "Sağ Alt" - }, - "default_mail_program": { - "label": "Varsayılan Posta Programı", - "description": "{appName} uygulamasını mailto: bağlantıları için varsayılan posta programı olarak kaydet", - "button": "Varsayılan Olarak Ayarla", - "success": "Tarayıcı varsayılan olarak ayarlamak için istendi", - "error": "Tarayıcınız bu özelliği desteklemiyor" - }, - "attachment_reminder": { - "label": "Ek Hatırlatıcı", - "description": "İletinizde eklerden söz ettiğinizde ancak hiçbir ek eklenmemişken göndermeden önce uyar", - "keywords_label": "Tetikleyici anahtar sözcükler", - "keywords_description": "İletinizde bulunduğunda hatırlatıcıyı tetikleyen kelime veya ifadeler", - "add_placeholder": "Anahtar sözcük ekle...", - "add": "Ekle", - "remove": "Kaldır" - }, - "empty_subject_warning": { - "label": "Boş konu uyarısı", - "description": "Konusu olmayan bir ileti gönderilmeden önce onay iste." - }, - "hide_inline_image_attachments": { - "label": "Satır içi görüntüleri eklerden gizle", - "description": "İleti gövdesine gömülü görseller ayrı ek olarak listelenmez" - }, - "attachment_image_previews": { - "label": "Eklerde resim önizlemelerini göster", - "description": "Resim eklerini, genel dosya simgeleri yerine küçük resim kartları olarak göster" - }, - "send_delay": { - "label": "Göndermeyi geri al / gönderim gecikmesi", - "description": "Normal gönderimleri sunucu tarafında kısa bir süre geciktirin.", - "off": "Kapalı", - "seconds": "{seconds} saniye", - "unsupported": "Geçerli hesap gecikmeli gönderim desteği bildirmiyor. Ayar diğer hesaplar için kaydedilmeye devam eder." - }, - "return_to_list_after_action": { - "label": "Silme veya okunmadı olarak işaretlemeden sonra listeye dön", - "description": "Açık iletiyi sildikten veya okunmadı olarak işaretledikten sonra, sonraki iletiyi açmak yerine ileti listesine dön." - }, - "swipe_right_action": { - "label": "Sağa kaydırma eylemi (mobil)", - "description": "Dokunmatik cihazlarda, listede bir iletiyi sağa kaydırdığınızda gerçekleştirilen eylem." - }, - "swipe_left_action": { - "label": "Sola kaydırma eylemi (mobil)", - "description": "Dokunmatik cihazlarda, listede bir iletiyi sola kaydırdığınızda gerçekleştirilen eylem." - }, - "swipe_actions": { - "none": "Yok", - "archive": "Arşivle", - "delete": "Sil", - "mark_read": "Okundu durumunu değiştir", - "star": "Yıldızı değiştir", - "spam": "Spam olarak işaretle" - }, - "rtl_editing": { - "label": "Sağdan sola düzenleme desteği", - "description": "Düzenleyici araç çubuğuna paragrafları soldan sağa veya sağdan sola ayarlayabileceğiniz bir yön düğmesi ekler" - } - }, - "composer": { - "title": "Yazıcı", - "description": "E-posta yazma ayarlarını yapılandırın", - "autosave": { - "label": "Otomatik Kaydetme Aralığı", - "description": "Taslakların otomatik kaydedilme sıklığı", - "30s": "Her 30 saniyede bir", - "1m": "Her dakika", - "2m": "Her 2 dakikada bir", - "5m": "Her 5 dakikada bir" - }, - "send_confirmation": { - "label": "Gönderme Onayı", - "description": "E-posta göndermeden önce onay isteyin" - }, - "default_reply": { - "label": "Varsayılan Yanıt Modu", - "description": "Yanıtla'ya tıklandığında varsayılan işlem", - "reply": "Yanıtla", - "reply_all": "Tümünü Yanıtla" - } - }, - "privacy": { - "title": "Gizlilik ve Güvenlik", - "description": "Gizlilik ve güvenlik ayarlarınızı yönetin", - "external_images": { - "label": "Harici Görselleri Engelle", - "description": "Harici görseller aracılığıyla izlemeyi önle" - }, - "session_timeout": { - "label": "Oturum Zaman Aşımı", - "description": "Hareketsizlik sonrası otomatik oturumu kapat", - "never": "Hiçbir zaman", - "30m": "30 dakika", - "1h": "1 saat", - "4h": "4 saat" - }, - "clear_cache": { - "label": "Önbelleği Temizle", - "description": "Önbelleğe alınmış verileri ve geçici dosyaları kaldır", - "button": "Önbelleği Temizle", - "confirm": "Önbelleği temizlemek istediğinizden emin misiniz?", - "success": "Önbellek başarıyla temizlendi" - } - }, - "account": { - "title": "Hesap", - "description": "Hesap bilgilerinizi görüntüleyin", - "name_label": "Görünen Ad", - "username_label": "Kullanıcı Adı", - "account_type_label": "Hesap Türü", - "auth_method_label": "Kimlik Doğrulama", - "auth_method_oauth": "Tekil Oturum Açma (OAuth/OIDC)", - "auth_method_basic": "Parola", - "demo_account": "Demo Hesabı", - "email": { - "label": "E-posta Adresi", - "value": "{email}" - }, - "server": { - "label": "JMAP Sunucusu", - "value": "{server}" - }, - "storage": { - "label": "Depolama Kullanımı", - "used": "{total} alanın {used} kullanıldı", - "percentage": "%{percent} kullanıldı" - }, - "last_sync": { - "label": "Son Senkronizasyon", - "value": "{time}" - }, - "accounts": { - "title": "Oturum açılmış hesaplar", - "description": "Hesapların hesap menüsünde görüntülenme sırasını değiştirmek için sürükleyin", - "active": "Şu anda etkin hesap", - "default_badge": "Varsayılan hesap", - "set_default": "Varsayılan olarak ayarla", - "switch_to": "Bu hesaba geç", - "move_up": "Yukarı taşı", - "move_down": "Aşağı taşı", - "drag_handle": "Sıralamak için sürükleyin", - "add": "Hesap ekle" - }, - "shared_accounts": { - "title": "Benimle paylaşılanlar", - "description": "Yönetebileceğiniz grup ve paylaşılan hesaplar. Filtrelerini, tatil yanıtlayıcısını, takvimlerini ve kişilerini düzenlemek için birini seçin.", - "shared_label": "Paylaşılan hesap" - } - }, - "security": { - "title": "Hesap Güvenliği", - "description": "Parolanızı, iki faktörlü kimlik doğrulamayı ve güvenlik ayarlarını yönetin", - "detecting": "Sunucu özellikleri algılanıyor...", - "not_available": "Bu posta sunucusu için hesap güvenliği yönetimi kullanılamıyor. Gerekli izinler devre dışı bırakılmış olabilir. Ayrıntılar için belgelere bakın.", - "password": { - "title": "Parolayı Değiştir", - "current": "Mevcut Parola", - "new": "Yeni Parola", - "confirm": "Yeni Parolayı Onayla", - "submit": "Parolayı Değiştir", - "success": "Parola başarıyla değiştirildi", - "error_title": "Parola değiştirme başarısız", - "error_mismatch": "Yeni parolalar eşleşmiyor", - "error_min_length": "Parola en az 8 karakter olmalıdır", - "error_generic": "Parola değiştirilemedi" - }, - "display_name": { - "label": "Görünen Ad", - "description": "Sunucuda görünen adınız", - "placeholder": "Görünen adınızı girin", - "save": "Kaydet", - "success": "Görünen ad güncellendi", - "error": "Görünen ad güncellenemedi" - }, - "totp": { - "section_title": "İki Faktörlü Kimlik Doğrulama", - "label": "TOTP Kimlik Doğrulama", - "description": "Zamana dayalı tek kullanımlık parola ile ekstra bir güvenlik katmanı ekleyin", - "active": "Etkin", - "inactive": "Devre dışı", - "enabled": "İki faktörlü kimlik doğrulama etkinleştirildi", - "disabled": "İki faktörlü kimlik doğrulama devre dışı bırakıldı", - "enable_error": "2FA etkinleştirilemedi", - "disable_error": "2FA devre dışı bırakılamadı", - "setup_instructions": "Bu URL'yi kimlik doğrulayıcı uygulamanıza (Google Authenticator, Authy vb.) kopyalayın:", - "verification_code": "Doğrulama kodu", - "confirm": "Onayla", - "disable": "Devre Dışı Bırak", - "disable_confirm_prompt": "İki faktörlü kimlik doğrulamayı devre dışı bırakmak için parolanızı girin.", - "password_required": "Parola gerekli", - "code_required": "Doğrulama kodu gerekli", - "code_invalid": "Geçersiz doğrulama kodu. Kimlik doğrulayıcı uygulamanızı kontrol edin ve tekrar deneyin." - }, - "app_passwords": { - "title": "Uygulama Parolaları", - "description": "İki faktörlü kimlik doğrulamayı desteklemeyen uygulamalar için parola oluşturun", - "add": "Ekle", - "create": "Oluştur", - "cancel": "İptal", - "done": "Tamam", - "generate": "Oluştur", - "name_label": "Uygulama Adı", - "name_placeholder": "ör. Thunderbird, iPhone Mail", - "expires_label": "Son Kullanma (isteğe bağlı)", - "allowed_ips_label": "İzin Verilen IP'ler (isteğe bağlı)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Virgül veya boşlukla ayrılmış. Herhangi bir IP'ye izin vermek için boş bırakın.", - "password_label": "Parola (otomatik oluşturmak için boş bırakın)", - "password_placeholder": "Boş bırakılırsa otomatik oluşturulur", - "copy_now_warning": "Bu parolayı şimdi kopyalayın - bir daha gösterilmeyecek.", - "added": "Uygulama parolası oluşturuldu", - "removed": "Uygulama parolası kaldırıldı", - "add_error": "Uygulama parolası oluşturulamadı", - "remove_error": "Uygulama parolası kaldırılamadı", - "none": "Yapılandırılmış uygulama parolası yok" - }, - "api_keys": { - "title": "API Anahtarları", - "description": "Sunucuyla doğrudan iletişim kuran betikler ve entegrasyonlar için API anahtarları oluşturun", - "name_label": "Anahtar Adı", - "name_placeholder": "ör. Yedek betiği, CI koşucusu", - "copy_now_warning": "Bu API anahtarını şimdi kopyalayın - bir daha gösterilmeyecek.", - "added": "API anahtarı oluşturuldu", - "removed": "API anahtarı kaldırıldı", - "add_error": "API anahtarı oluşturulamadı", - "remove_error": "API anahtarı kaldırılamadı", - "none": "Yapılandırılmış API anahtarı yok" - }, - "encryption": { - "section_title": "Bekleme Süresi Şifrelemesi", - "label": "E-posta Şifrelemesi", - "description": "Ek gizlilik için sunucudaki saklanan e-postaları şifreleyin", - "active": "{type} şifrelemesi etkin", - "inactive": "Devre dışı", - "enabled": "Bekleme süresi şifrelemesi etkinleştirildi", - "disabled_success": "Bekleme süresi şifrelemesi devre dışı bırakıldı", - "error": "Şifreleme ayarları güncellenemedi", - "algorithm_label": "Şifreleme algoritması", - "encrypt_on_append": "Yeni e-postaları yüklenirken şifrele", - "allow_spam_training": "E-postalar şifrelenmeden önce spam eğitimine izin ver", - "enable_encryption": "Şifrelemeyi etkinleştir", - "enable_tooltip": "Bu anahtarla şifrelemeyi etkinleştir", - "configure_title": "Bu anahtarla şifrelemeyi etkinleştir", - "enable_button": "Etkinleştir", - "disable_tooltip": "Şifrelemeyi devre dışı bırak", - "enabled_success": "Bekleme süresi şifrelemesi etkinleştirildi" - }, - "email_client": { - "title": "E-posta İstemcisi Kurulumu", - "description": "Masaüstü veya mobil e-posta istemcinizi (Thunderbird, Apple Mail, Outlook vb.) yapılandırmak için bu kimlik bilgilerini kullanın", - "jmap_username_label": "JMAP Kullanıcı Adı", - "copy": "Kopyala", - "copied": "Kopyalandı", - "password_instructions": "E-posta istemcinizde oturum açmak için yukarıdaki JMAP kullanıcı adı ile bir uygulama parolasını birlikte kullanın. Henüz oluşturmadıysanız yukarıdaki bölümden bir uygulama parolası oluşturun." - }, - "link_device": { - "title": "Mobil uygulamayı bağla", - "description": "Hiçbir şey yazmadan Bulwark Mail mobil uygulamasında oturum açın. Burada bir QR kodu oluşturun ve uygulamanın giriş ekranından tarayın.", - "generate": "QR kodunu göster", - "regenerate": "Yeni kod göster", - "instructions": "Bulwark Mail uygulamasını açın, giriş ekranında \"QR kodu tara\" seçeneğine dokunun ve kameranızı buraya doğrultun.", - "expires_in": "Bu kodun süresi {seconds} saniye içinde dolacak. Yalnızca bir kez kullanılabilir.", - "expired": "Bu kodun süresi doldu.", - "generating": "Oluşturuluyor…", - "error": "Eşleştirme kodu oluşturulamadı. Lütfen tekrar deneyin." - }, - "public_keys": { - "name_label": "Ortak anahtar adı", - "name_placeholder": "ör. Anahtarım", - "key_label": "Ortak anahtar (S/MIME veya PGP ASCII biçiminde)", - "key_placeholder": "Ortak anahtarınızı buraya yapıştırın", - "none": "Yapılandırılmış ortak anahtar yok" - } - }, - "identities": { - "title": "Gönderme Kimlikleri", - "description": "Gönderebildiğiniz e-posta adreslerini yönetin", - "identities_count": { - "label": "Kimlikleriniz", - "description": "Gönderme için yapılandırılmış e-posta adresleri", - "count_zero": "Kimlik yok", - "count_one": "1 kimlik", - "count_other": "{count} kimlik" - }, - "manage": "Kimlikleri Yönet", - "sub_addressing": { - "label": "Alt Adresleme", - "description": "Gelen postayı düzenlemek için user+etiket@domain.com gibi etiketler kullanın", - "learn_more": "Daha Fazla Bilgi" - } - }, - "vacation": { - "title": "Tatil Yanıtlayıcı", - "description": "Yokken gelen e-postalara otomatik yanıt gönderin", - "loading": "Tatil ayarları yükleniyor...", - "not_supported": "Posta sunucunuz tatil yanıtlarını desteklemiyor.", - "fetch_error": "Tatil ayarları yüklenemedi. Lütfen tekrar deneyin.", - "status": { - "label": "Tatil Yanıtlayıcı", - "description": "Size e-posta gönderenlere otomatik yanıt gönderin", - "active": "Etkin", - "inactive": "Devre dışı" - }, - "date_range": { - "title": "Tarih Aralığı", - "description": "Otomatik yanıtı isteğe bağlı olarak belirli bir dönemle sınırlayın", - "start": "Başlangıç Tarihi", - "start_description": "Başlangıç sınırı olmaması için boş bırakın", - "end": "Bitiş Tarihi", - "end_description": "Bitiş sınırı olmaması için boş bırakın" - }, - "message": { - "title": "Otomatik Yanıt İletisi", - "description": "Yanıt olarak gönderilecek ileti", - "subject_label": "Konu", - "subject_description": "Otomatik yanıtın konu satırı", - "subject_placeholder": "Ofis Dışındayım", - "body_label": "İleti Gövdesi", - "body_description": "Düz metin ileti içeriği", - "body_placeholder": "E-postanız için teşekkür ederim. Şu anda ofis dışındayım ve döndüğümde yanıt vereceğim.", - "html_label": "Biçimlendirilmiş mesaj (HTML)", - "html_description": "Bağlantılar ve stil içeren biçimlendirilmiş bir sürüm ekleyin. E-posta istemcisi bunu görüntüleyemeyen alıcılar yukarıdaki düz metni görür.", - "html_placeholder": "Biçimlendirilmiş bir ofis dışı yanıtı yazın…" - }, - "preview": { - "title": "Önizleme", - "show": "Önizlemeyi göster", - "hide": "Önizlemeyi gizle" - }, - "save": "Değişiklikleri Kaydet", - "saving": "Kaydediliyor...", - "warnings": { - "end_before_start": "Bitiş tarihi başlangıç tarihinden sonra olmalıdır", - "start_in_past": "Başlangıç tarihi geçmişte", - "empty_body": "İleti gövdesi boş - alıcılar boş bir yanıt alacak", - "subject_too_long": "Konu sunucu için çok uzun (en fazla {max} bayt).", - "body_too_long": "İleti sunucu için çok uzun (biçimlendirme dahil en fazla {max} bayt)." - } - }, - "folders": { - "role_memos": "Notlar", - "title": "Klasörler", - "description": "E-posta klasörlerinizi yönetin ve standart roller atayın", - "folder_list": "Klasörleriniz", - "folder_list_description": "Özelleştirmek için klasör simgesine tıklayın", - "standard_roles": "Standart Klasör Rolleri", - "standard_roles_description": "Gelen Kutusu, Gönderilenler, Çöp Kutusu vb. standart posta kutusu rolleri için hangi klasörlerin kullanılacağını atayın", - "role_inbox": "Gelen Kutusu", - "role_drafts": "Taslaklar", - "role_sent": "Gönderilenler", - "role_trash": "Çöp Kutusu", - "role_junk": "Spam / İstem Dışı", - "role_archive": "Arşiv", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Yok", - "create_folder": "Klasör Oluştur", - "create_subfolder": "Alt Klasör Oluştur", - "reorder": "Yeniden sıralamak için sürükleyin", - "reorder_error": "Klasörler yeniden sıralanamadı", - "move_error": "Klasör taşınamadı", - "subfolder_of": "{name} içinde", - "subfolder_name": "Alt klasör adı", - "new_folder_name": "Klasör adı", - "rename": "Yeniden Adlandır", - "change_icon": "Simgeyi değiştir", - "delete": "Sil", - "confirm_delete": "\"{name}\" klasörünü silmek istediğinizden emin misiniz? Bu klasördeki e-postalar Çöp Kutusu'na taşınacak.", - "create": "Oluştur", - "cancel": "İptal", - "no_folders": "Özel klasör yok", - "cannot_delete_role": "Standart rolü olan klasör silinemiyor. Önce rolü kaldırın.", - "folder_created": "Klasör oluşturuldu", - "folder_renamed": "Klasör yeniden adlandırıldı", - "folder_deleted": "Klasör silindi", - "role_updated": "Klasör rolü güncellendi", - "error_create": "Klasör oluşturulamadı", - "error_rename": "Klasör yeniden adlandırılamadı", - "error_delete": "Klasör silinemedi", - "error_delete_has_children": "Klasör silinemiyor: hâlâ alt klasörler içeriyor. Önce onları silin veya taşıyın.", - "error_delete_has_email": "Klasör silinemiyor: hâlâ e-postalar içeriyor. Önce onları taşıyın veya silin.", - "error_role": "Klasör rolü güncellenemedi" - }, - "advanced": { - "title": "Gelişmiş", - "description": "Gelişmiş seçenekler ve geliştirici ayarları", - "debug_mode": { - "label": "Hata Ayıklama Modu", - "description": "Sorun giderme için ayrıntılı günlük kaydını etkinleştir" - }, - "debug_categories": { - "description": "Hangi kategorilerin günlüğe kaydedileceğini seçin. Konsol gürültüsünü azaltmak için ihtiyaç duymadığınız kategorileri devre dışı bırakın.", - "jmap": "JMAP İstemcisi", - "jmap_description": "Posta kutusu işlemleri, e-posta getirme ve JMAP protokol istekleri", - "calendar": "Takvim", - "calendar_description": "Takvim etkinlikleri, içe aktarmalar ve zamanlama iletileri", - "tasks": "Görevler", - "tasks_description": "Takvim görevi oluşturma, getirme ve güncellemeler", - "auth": "Kimlik Doğrulama", - "auth_description": "Giriş, TOTP, belirteç değişimi ve oturum yönetimi", - "filters": "Filtreler", - "filters_description": "Sieve filtre kuralları ve tatil betikleri", - "email": "E-posta Görüntüleme", - "email_description": "E-posta oluşturma, TNEF işleme ve okundu olarak işaretleme", - "push": "Anlık Bildirimler", - "push_description": "Anlık bildirim kurulumu ve teslimi", - "contacts": "Kişiler ve Adres Defterleri", - "contacts_description": "Kişi senkronizasyonu, adres defteri işlemleri ve güvenilir göndericiler" - }, - "settings_sync": { - "label": "Ayar Senkronizasyonu", - "description": "Ayarlarınızı tarayıcılar ve cihazlar arasında senkronize edin" - }, - "sender_favicons": { - "label": "Gönderici Favorikonları", - "description": "İş göndericileri için profil resmi olarak web sitesi simgelerini göster" - }, - "show_avatars_in_junk": { - "label": "İstem Dışı Klasöründe Avatarları Göster", - "description": "İstem dışı klasöründeki göndericiler için profil resimleri ve favorikonları göster. Kimlik avı girişimlerine görsel meşruiyet kazandırmamak için varsayılan olarak kapalıdır." - }, - "keyboard_shortcuts": { - "label": "Klavye Kısayolları", - "description": "Mevcut klavye kısayollarını görüntüleyin", - "button": "Kısayolları Görüntüle" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Ayarları Sıfırla", - "description": "Tüm ayarları varsayılan değerlerine geri yükle", - "button": "Varsayılanlara Sıfırla" - }, - "export_settings": { - "label": "Ayarları Dışa Aktar", - "description": "Ayarlarınızı JSON olarak indirin", - "button": "Dışa Aktar" - }, - "import_settings": { - "label": "Ayarları İçe Aktar", - "description": "JSON dosyasından ayarları yükle", - "button": "İçe Aktar" - }, - "about": { - "title": "Bulwark Webmail" - } - }, - "sidebar_apps": { - "title": "Kenar Çubuğu Uygulamaları", - "description": "Kenar çubuğunuzdaki özel uygulamaları ve bağlantıları yönetin", - "keep_loaded": "Uygulamaları Yüklü Tut", - "keep_loaded_description": "Yeniden yüklemeyi önlemek için aralarında geçiş yaparken satır içi uygulamaları arka planda çalışır halde tutun", - "managed_title": "Yöneticiniz tarafından sağlandı", - "managed_description": "Bu uygulamalar herkes için ayarlanmıştır ve değiştirilemez.", - "manage_title": "Özel Uygulamalar", - "manage_description": "Kenar çubuğunuzdan özel uygulamalar ekleyin, düzenleyin veya kaldırın" - }, - "contacts": { - "title": "Kişiler", - "description": "Kişilerinizi içe ve dışa aktarın", - "group_by_letter_label": "İlk harfe göre grupla", - "sort_by_last_name_label": "Soyada göre sırala", - "sort_by_last_name_description": "Aile üyeleri bir arada görünsün diye kişi listesini soyada göre sıralar", - "group_by_letter_description": "Kişi listesinde alfabetik bölüm başlıkları göster", - "import_label": "Kişileri İçe Aktar", - "import_description": "Kişileri bir vCard (.vcf) dosyasından içe aktarın", - "export_label": "Kişileri Dışa Aktar", - "export_description": "Tüm kişileri vCard (.vcf) dosyası olarak dışa aktarın", - "manage_title": "Adres Defterleri", - "manage_description": "Adres defterlerinizi yeniden adlandırın", - "no_address_books": "Adres defteri bulunamadı", - "categories_title": "Kategoriler", - "categories_description": "Kişi kategorilerini yeniden adlandırın", - "no_categories": "Kategori bulunamadı" - }, - "downloads": { - "title": "İndirilenler", - "description": "İndirilen e-postaların ve eklerin nasıl adlandırılacağını özelleştirin.", - "reset": "Varsayılana sıfırla", - "preview": "Önizleme:", - "email_template": { - "label": "E-posta dosya adı (.eml)", - "description": "Bir e-postayı dışa aktarırken veya dosya sistemine sürüklediğinizde kullanılan şablon. .eml uzantısı otomatik olarak eklenir." - }, - "attachment_template": { - "label": "Ek dosyası adı", - "description": "Bir eki indirirken veya sürüklerken kullanılan şablon. '{filename}' ve '{ext}' yazılmazsa orijinal uzantı korunur." - }, - "bundle_template": { - "label": "Çoklu e-posta .zip dosya adı", - "description": "Birden çok seçili e-postayı tek bir .zip arşivi olarak sürüklediğinizde veya indirdiğinizde kullanılan şablon. .zip uzantısı otomatik olarak eklenir." - }, - "spaces": { - "label": "Boşluklar", - "description": "Sonuçtaki dosya adındaki boşlukları başka bir karakterle değiştirin.", - "keep": "Boşlukları koru", - "underscore": "_ ile değiştir", - "dash": "- ile değiştir" - }, - "lowercase": { - "label": "Küçük harf", - "description": "Tüm dosya adını küçük harfe zorla." - }, - "strip_diacritics": { - "label": "Aksanları kaldır", - "description": "Aksanlı harfleri ASCII karşılıklarına dönüştür (ä → a, é → e). Unicode dosya adlarıyla sorun yaşayan araçlar için yararlıdır." - }, - "collapse_separators": { - "label": "Tekrarlanan ayırıcıları birleştir", - "description": "Art arda gelen boşluk, alt çizgi veya tireleri tek bir karaktere indir." - }, - "after_export": { - "label": "Dışa aktarmadan sonra", - "description": "E-postayı .eml olarak dışa aktardıktan sonra isteğe bağlı olarak taşı.", - "keep": "Posta kutusunda tut", - "archive": "Arşive taşı", - "trash": "Çöp kutusuna taşı" - } - }, - "filters": { - "title": "E-posta Filtreleri", - "description": "Gelen e-postaları otomatik olarak sıralamak, etiketlemek ve yönetmek için kurallar oluşturun", - "add_rule": "Kural Ekle", - "no_rules": "Filtre kuralı yok", - "no_rules_description": "Gelen e-postalarınızı otomatik düzenlemek için kurallar oluşturun", - "vacation_active": "Tatil Yanıtlayıcı etkin", - "vacation_active_description": "Gelen iletiler için otomatik yanıt etkinleştirildi", - "vacation_configure": "Yapılandır", - "edit_rule": "Kuralı Düzenle", - "new_rule": "Yeni Kural", - "delete_rule": "Kuralı Sil", - "delete_confirm": "Bu kuralı silmek istediğinizden emin misiniz?", - "enable": "Etkinleştir", - "disable": "Devre Dışı Bırak", - "raw_editor": "Ham Sieve Düzenleyicisi", - "raw_editor_warning": "Ham Sieve betiğini düzenlemek görsel kural düzenlemeyi bozabilir. Burada yapılan değişiklikler görsel oluşturucunun üzerine yazar.", - "validate": "Doğrula", - "validation_success": "Betik geçerli", - "validation_error": "Betikte hatalar var", - "save": "Kuralları Kaydet", - "saving": "Kaydediliyor...", - "saved": "Filtreler başarıyla kaydedildi", - "save_failed": "Filtreler kaydedilemedi", - "loading": "Filtreler yükleniyor...", - "not_supported": "Posta sunucunuz e-posta filtrelerini desteklemiyor.", - "rule_name": "Kural Adı", - "rule_name_placeholder": "ör. Bültenleri sırala", - "match_all": "TÜM koşullarla eşleş", - "match_any": "HERHANGİ bir koşulla eşleş", - "conditions": "Koşullar", - "add_condition": "Koşul Ekle", - "actions": "İşlemler", - "add_action": "İşlem Ekle", - "stop_processing": "Sonraki kuralları işlemeyi durdur", - "include_spam": "İstenmeyen olarak işaretlenen iletileri de taşı", - "attachment_type_placeholder": "örn. pdf, doc, jpg", - "value_placeholder_multi": "Değer (birden fazla virgülle ayrılır)", - "condition_fields": { - "attachment": "Ek", - "from": "Kimden", - "to": "Kime", - "cc": "Bilgi", - "subject": "Konu", - "header": "Özel Başlık", - "size": "Boyut", - "body": "Gövde" - }, - "comparators": { - "has_any": "mevcut", - "has_type": "türünde", - "contains": "içerir", - "not_contains": "içermez", - "is": "tam olarak", - "not_is": "değil", - "starts_with": "ile başlar", - "ends_with": "ile biter", - "matches": "desenle eşleşir", - "greater_than": "büyüktür", - "less_than": "küçüktür" - }, - "action_types": { - "move": "Klasöre taşı", - "copy": "Klasöre kopyala", - "forward": "İlet", - "mark_read": "Okundu olarak işaretle", - "star": "İletiye yıldız ekle", - "add_label": "Etiket ekle", - "discard": "At (sessizce sil)", - "reject": "İletiyle reddet", - "keep": "Gelen kutusunda tut", - "stop": "İşlemeyi durdur" - }, - "move_to_folder": "Klasör seçin", - "copy_to_folder": "Klasör seçin", - "forward_to": "E-posta adresine ilet", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Bir kopyasını sakla", - "forward_limit": "Bu sunucuda ileti başına yönlendirme sınırı: {count}. Fazla yönlendirmeler atlanır.", - "reject_message": "Reddetme iletisi", - "reject_placeholder": "E-postanız reddedildi", - "label_name": "Etiket adı", - "label_placeholder": "Etiket seçin", - "header_name": "Başlık adı", - "header_placeholder": "ör. X-Mailing-List", - "size_bytes": "Bayt cinsinden boyut", - "size_placeholder": "ör. 1000000", - "system_managed": "Sistem tarafından yönetilen kural", - "opaque_warning": "Bu betik görsel oluşturucunun dışında düzenlendi. Yalnızca ham Sieve düzenleme kullanılabilir.", - "open_sieve_editor": "Ham Sieve düzenleyicisini aç", - "fetch_error": "Filtreler yüklenemedi", - "expanded_view": "Genişletilmiş görünüm", - "expanded_view_description": "Filtre kurallarını ayrıntılı koşul ve işlem bloklarıyla göster", - "if": "Eğer", - "then": "O zaman", - "match_all_conditions": "hepsi eşleşir", - "match_any_condition": "herhangi biri eşleşir", - "and": "ve", - "or": "veya", - "cancel": "İptal", - "confirm_delete": "Sil", - "rule_list": "Filtre kuralları", - "drag_to_reorder": "Yeniden sıralamak için sürükleyin", - "match_type": "Eşleşme türü", - "reset_to_visual": "Görsel oluşturucuya sıfırla", - "reset_warning": "Bu işlem mevcut betiği atacak ve yeniden başlatacak.", - "confirm_reset": "Sıfırla", - "validation_empty_name": "Kural adı gerekli", - "validation_empty_conditions": "En az bir değeri olan koşul gerekli", - "validation_empty_actions": "En az bir işlem gerekli", - "templates_section": "Şablondan başla", - "template_newsletters": "Bültenleri klasöre taşı", - "template_receipts": "Makbuzları otomatik arşivle", - "template_important": "Önemli e-postaları işaretle", - "template_notifications": "Bildirimleri filtrele", - "sieve_editor": { - "title": "Sieve Betik Düzenleyicisi", - "warning": "Ham Sieve betiğini düzenlemek görsel kural düzenlemeyi bozabilir. Burada yapılan değişiklikler görsel oluşturucunun üzerine yazar.", - "script_content": "Sieve betiği", - "valid": "Betik geçerli", - "invalid": "Betikte hatalar var", - "save_warning": "Kaydetme işlemi tüm görsel kuralların üzerine yazacak. Bu geri alınamaz. Onaylamak için Kaydet'e tekrar tıklayın.", - "validating": "Doğrulanıyor...", - "validate": "Doğrula", - "cancel": "İptal", - "save": "Kaydet", - "confirm_save": "Kaydetmeyi Onayla", - "validation_failed": "Doğrulama isteği başarısız" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# koşul} other {# koşul}}", - "actions_count": "{count, plural, one {# işlem} other {# işlem}}" - }, - "origin_external": "Harici", - "managed_by_tooltip": "{source} tarafından yönetilir. Bu uygulamada düzenleyin veya ham Sieve düzenleyicisini kullanın." - }, - "templates": { - "title": "E-posta Şablonları", - "description": "Yer tutucu değişkenlerle yeniden kullanılabilir e-posta şablonları oluşturun", - "add": "Yeni Şablon", - "edit": "Şablonu Düzenle", - "name": "Şablon Adı", - "name_placeholder": "ör. Takip e-postası", - "category": "Kategori", - "category_placeholder": "ör. İş, Kişisel", - "subject": "Konu", - "subject_placeholder": "E-posta konu satırı", - "body": "Gövde", - "body_placeholder": "E-posta gövde içeriği...", - "recipients_placeholder": "email@example.com", - "identity": "Gönderen Olarak", - "default_identity": "Varsayılan kimlik", - "favorite": "Favori", - "cancel": "İptal", - "create": "Oluştur", - "update": "Güncelle", - "confirm_delete": "Sil", - "no_templates": "Henüz şablon yok", - "manage": "Şablonları Yönet", - "count": "{count, plural, one {# şablon} other {# şablon}}", - "export_import": "Dışa ve İçe Aktar", - "export_import_description": "Şablonlarınızı yedekleyin veya başka bir cihaza aktarın", - "export": "Dışa Aktar", - "import": "İçe Aktar", - "validation": { - "empty": "Şablon adı gerekli", - "too_long": "Şablon adı en fazla 200 karakter olmalıdır" - } - }, - "files": { - "display": { - "title": "Görüntüleme", - "description": "Dosya ve klasörlerin nasıl görüntüleneceğini yapılandırın" - }, - "folder_layout": { - "label": "Klasör Gezinmesi", - "description": "Klasörlerin nasıl görüntüleneceğini seçin: dosyalarla birlikte satır içi veya kenar çubuğu ağacında", - "inline": "Satır içi", - "sidebar": "Kenar Çubuğu" - }, - "default_view": { - "label": "Varsayılan Görünüm", - "description": "Izgara ve liste düzeni arasında seçin", - "list": "Liste", - "grid": "Izgara" - }, - "default_sort": { - "label": "Varsayılan Sıralama", - "description": "Dosyalar için varsayılan sıralamayı seçin", - "name": "Ad", - "size": "Boyut", - "modified": "Değiştirilme" - }, - "sort_direction": { - "label": "Sıralama Yönü", - "description": "Artan veya azalan düzen seçin", - "ascending": "Artan", - "descending": "Azalan" - }, - "icons": { - "title": "Simgeler", - "description": "Dosya simgesi görünümünü yapılandırın" - }, - "show_icons": { - "label": "Dosya Simgelerini Göster", - "description": "Dosyaların ve klasörlerin yanında simgeleri görüntüle" - }, - "colored_icons": { - "label": "Renkli Simgeler", - "description": "Tek renk yerine renkli simgeler kullan" - }, - "show_thumbnails": { - "label": "Küçük Resimleri Göster", - "description": "Resim dosyaları için simgeler yerine görüntü önizlemeleri göster" - }, - "behavior": { - "title": "Davranış", - "description": "Dosya tarayıcı davranışını yapılandırın" - }, - "show_hidden": { - "label": "Gizli Dosyaları Göster", - "description": "Nokta ile başlayan dosya ve klasörleri görüntüle" - }, - "preview": { - "label": "Önizleme" - } - }, - "search_placeholder": "Ayarlarda ara", - "search_clear": "Aramayı temizle", - "search_no_results": "Eşleşen ayar yok", - "scoped": { - "back": "Hesabıma geri dön", - "managing": "Yönetiliyor: {name}" - } - }, - "errors": { - "page_error_title": "Bir şeyler ters gitti", - "page_error_description": "Beklenmeyen bir hatayla karşılaştık. Lütfen tekrar deneyin veya ana sayfaya dönün.", - "sidebar_error": "Posta kutuları yüklenemedi", - "email_list_error": "E-postalar yüklenemedi", - "viewer_error_title": "E-posta görüntülenemiyor", - "viewer_error_description": "Bu e-posta oluşturulurken sorun oluştu. Desteklenmeyen içerik bulunuyor olabilir.", - "composer_error": "Yazıcı yüklenemedi", - "settings_error_title": "Ayarlar kullanılamıyor", - "settings_error_description": "Ayarlar yüklenemedi. Tercihleriniz kaydedilmemiş olabilir.", - "try_again": "Tekrar dene", - "reload": "Yenile", - "reload_emails": "E-postaları yenile", - "reload_settings": "Ayarları yenile", - "retry": "Yeniden dene", - "go_home": "Gelen kutusuna git" - }, - "context_menu": { - "reply": "Yanıtla", - "reply_all": "Tümünü Yanıtla", - "forward": "İlet", - "mark_read": "Okundu Olarak İşaretle", - "mark_unread": "Okunmadı Olarak İşaretle", - "star": "Yıldız Ekle", - "unstar": "Yıldızı Kaldır", - "pin": "Sabitle", - "unpin": "Sabitlemeyi kaldır", - "move_to": "Şuraya taşı...", - "archive": "Arşivle", - "delete": "Sil", - "mark_as_spam": "Spam bildir", - "not_spam": "Spam değil", - "tag": "Etiket", - "items_selected": "{count} e-posta seçildi", - "edit_draft": "Taslağı Düzenle", - "cancel_scheduled_send": "Göndermeyi iptal et", - "reschedule_send": "Yeniden zamanla", - "cancel_and_edit": "İptal et ve düzenle", - "cancel_and_compose_again": "İptal et ve yeniden yaz" - }, - "mailbox_context_menu": { - "mark_folder_read": "Klasörü okundu olarak işaretle", - "mark_folder_tree_read": "Klasörü ve alt klasörleri okundu olarak işaretle", - "mark_all_folders_read": "Tüm klasörleri okundu olarak işaretle", - "new_subfolder": "Yeni alt klasör...", - "new_folder": "Yeni klasör...", - "rename": "Yeniden adlandır...", - "share": "Paylaş...", - "import_email": ".eml veya .zip içe aktar...", - "empty_folder": "Klasörü boşalt", - "empty_folder_generic": "Klasörü boşalt", - "delete_folder": "Klasörü sil", - "refresh": "Yenile", - "mark_all_confirm_title": "Tüm klasörleri okundu olarak işaretle", - "mark_all_confirm_message": "Kişisel hesabınızdaki tüm okunmamış iletiler okundu olarak işaretlensin mi?", - "delete_confirm_title": "Klasörü sil", - "delete_confirm_message": "\"{name}\" klasörü kalıcı olarak silinsin mi? Bu işlem geri alınamaz.", - "prompt_new_subfolder": "Yeni alt klasör için bir ad girin.", - "prompt_new_folder": "Yeni klasör için bir ad girin.", - "prompt_rename": "Bu klasör için yeni bir ad girin.", - "placeholder_folder_name": "Klasör adı", - "create": "Oluştur", - "rename_confirm": "Yeniden Adlandır", - "toast_marked_read": "Klasör okundu olarak işaretlendi", - "toast_marked_read_count": "{count, plural, one {1 ileti} other {# ileti}} okundu olarak işaretlendi", - "toast_already_read": "Okunmamış ileti yok", - "toast_marked_all_read": "Tüm klasörler okundu olarak işaretlendi", - "toast_emptied": "Klasör boşaltıldı", - "toast_folder_created": "Klasör oluşturuldu", - "toast_folder_renamed": "Klasör yeniden adlandırıldı", - "toast_folder_deleted": "Klasör silindi", - "toast_error_mark_read": "Okundu olarak işaretlenemedi", - "toast_error_empty": "Klasör boşaltılamadı", - "toast_error_create": "Klasör oluşturulamadı", - "toast_error_rename": "Klasör yeniden adlandırılamadı", - "toast_error_delete": "Klasör silinemedi", - "toast_error_delete_has_children": "Klasörde alt klasörler var. Önce onları kaldırın.", - "toast_error_delete_has_email": "Klasör boş değil. Önce boşaltın.", - "delete_with_contents_title": "Klasör boş değil", - "delete_with_contents_message": "\"{name}\" klasöründe hâlâ {count} ileti var. Klasör ve tüm iletileri silinsin mi? Bu işlem geri alınamaz.", - "delete_with_contents_confirm": "Klasörü ve iletileri sil" - }, - "shortcuts": { - "title": "Klavye Kısayolları", - "tip": "Bu yardımı görmek için istediğiniz zaman ? tuşuna basın", - "sections": { - "navigation": "Gezinme", - "actions": "E-posta İşlemleri", - "global": "Genel", - "threads": "İleti Dizileri", - "composer": "Yazıcı" - }, - "navigation": { - "next_email": "Sonraki e-posta", - "previous_email": "Önceki e-posta", - "open_email": "E-postayı aç", - "close_email": "Kapat / Seçimi kaldır" - }, - "actions": { - "reply": "Yanıtla", - "reply_all": "Tümünü yanıtla", - "forward": "İlet", - "star": "Yıldızı aç/kapat", - "archive": "Arşivle", - "delete": "Sil", - "mark_unread": "Okunmadı olarak işaretle", - "mark_read": "Okundu olarak işaretle", - "toggle_spam": "Spam bildir / Spam değil" - }, - "global": { - "compose": "Yeni e-posta oluştur", - "search": "Aramaya odaklan", - "help": "Kısayolları göster", - "refresh": "E-postaları yenile", - "select_all": "Tümünü seç", - "global_search": "Her yerde ara" - }, - "threads": { - "expand_collapse": "İleti dizisini genişlet/daralt" - }, - "composer": { - "send": "E-posta gönder", - "schedule_send": "Göndermeyi planla", - "template_picker": "Şablon seçiciyi aç" - } - }, - "threads": { - "messages_one": "{count} ileti", - "messages_other": "{count} ileti", - "messages_tooltip": "{count, plural, one {Bu konuşmada # ileti} other {Bu konuşmada # ileti}}", - "expand": "Konuşmayı genişlet", - "collapse": "Konuşmayı daralt", - "loading": "Konuşma yükleniyor...", - "mark_read": "Konuşmayı okundu olarak işaretle", - "mark_unread": "Konuşmayı okunmadı olarak işaretle", - "archive": "Konuşmayı arşivle", - "delete": "Konuşmayı sil", - "star": "Konuşmaya yıldız ekle", - "unstar": "Konuşmanın yıldızını kaldır", - "toggle_thread": "İleti dizisini aç/kapat" - }, - "identities": { - "modal_title": "Gönderme Kimliklerini Yönet", - "create_new": "Yeni Kimlik Oluştur", - "edit_identity": "Kimliği Düzenle", - "delete_confirm": "Bu kimlik silinsin mi? Bu geri alınamaz.", - "cannot_delete": "Bu kimlik silinemez", - "primary_identity": "Birincil", - "set_as_primary": "Birincil olarak ayarla", - "no_identities": "Kimlik bulunamadı", - "display": { - "reply_to": "Yanıt Adresi:", - "bcc": "GGK:", - "signature": "İmza:", - "preview": "Önizleme:" - }, - "validation_errors": { - "invalid_emails": "Geçersiz e-postalar: {emails}", - "unknown_error": "Bilinmeyen hata" - }, - "form": { - "name_label": "Görünen Ad", - "name_placeholder": "ör. İş E-postası, Kişisel", - "name_required": "Ad gerekli", - "email_label": "E-posta Adresi", - "email_placeholder": "sizin.adresiniz@example.com", - "email_required": "E-posta gerekli", - "email_invalid": "Lütfen geçerli bir e-posta adresi girin", - "email_immutable": "E-posta adresi oluşturulduktan sonra değiştirilemez", - "reply_to_label": "Yanıt Adresi (isteğe bağlı)", - "reply_to_placeholder": "baska@example.com", - "bcc_label": "Otomatik GGK (isteğe bağlı)", - "bcc_placeholder": "arsiv@example.com", - "text_signature_label": "Metin İmzası", - "html_signature_label": "HTML İmzası", - "signature_byte_counter": "{bytes} / {max} bayt", - "signature_byte_limit_reached": "Sunucu sınırına ulaşıldı", - "save": "Kimliği Kaydet", - "cancel": "İptal", - "creating": "Oluşturuluyor...", - "updating": "Güncelleniyor..." - }, - "sub_address": { - "button_tooltip": "Alt adres kullan", - "popover_title": "Alt Adres Etiketi Ekle", - "tag_input_placeholder": "Etiket girin (ör. alisveris)", - "preview_label": "Önizleme:", - "recent_tags": "Son Etiketler", - "suggested_tags": "Önerilen", - "use_address": "Bu Adresi Kullan", - "invalid_tag": "Etiket yalnızca harf, rakam ve tire içermelidir", - "tag_too_long": "Etiket en fazla 30 karakter olmalıdır", - "help_text": "kullanici{delimiter}etiket@domain.com adresine gönderilen e-postalar gelen kutunuza ulaşır", - "validation": { - "empty": "Etiket boş olamaz", - "too_long": "Etiket en fazla {max} karakter olmalıdır", - "invalid_chars": "Etiket yalnızca harf, rakam ve tire içermelidir" - } - }, - "badge": { - "sent_via": "üzerinden", - "sub_address_tag": "Alt adres kullanılarak gönderildi: {tag}", - "identity_name": "Kimlik kullanılarak gönderildi: {name}", - "identity_short": "{name} üzerinden", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Sil", - "delete_confirm_title": "Kimliği Sil" - }, - "templates": { - "picker_title": "Şablon Seçin", - "search_placeholder": "Şablonlarda ara...", - "section_favorites": "Favoriler", - "section_recent": "Son Kullanılan", - "section_uncategorized": "Diğer", - "no_templates": "Henüz şablon yok", - "no_results": "Şablon bulunamadı", - "fill_placeholders": "Yer Tutucu Değerleri Doldurun", - "enter_value": "Değer girin...", - "preview": "Önizleme", - "insert_with_values": "Değerlerle Ekle", - "insert_raw": "Ham Ekle", - "copy_suffix": "(kopya)", - "placeholder": "Değişken", - "placeholders": { - "recipient_name": "Alıcı adı", - "company": "Şirket adı", - "date": "Bugünün tarihi", - "day_of_week": "Haftanın günü", - "sender_name": "Adınız" - } - }, - "contacts": { - "title": "Kişiler", - "search_placeholder": "Kişilerde ara...", - "create_new": "Yeni Kişi", - "no_category": "Kategori Yok", - "rename_category": "Kategoriyi yeniden adlandır", - "category_name_label": "Kategori adı", - "category_renamed": "Kategori yeniden adlandırıldı", - "category_rename_failed": "Kategori yeniden adlandırılamadı", - "category_added": "{name} kategorisine kişi eklendi", - "category_added_plural": "{name} kategorisine {count} kişi eklendi", - "empty_state": "Henüz kişi yok", - "empty_state_title": "Henüz kişi yok", - "empty_state_subtitle": "İlk kişinizi oluşturun veya vCard dosyasından içe aktarın", - "empty_search": "Aramanızla eşleşen kişi yok", - "empty_search_hint": "Farklı bir arama terimi deneyin", - "empty_filtered": "Filtrelerinizle eşleşen kişi yok", - "empty_filtered_hint": "Filtreleri ayarlamayı veya temizlemeyi deneyin", - "clear_search": "Aramayı temizle", - "import_vcard": "vCard İçe Aktar", - "delete_confirm_title": "Kişiyi sil", - "delete_confirm": "Bu kişiyi silmek istediğinizden emin misiniz?", - "local_mode": "Kişiler yerel olarak saklanıyor (sunucu JMAP Kişilerini desteklemiyor)", - "back_to_contacts": "Kişilere geri dön", - "back_to_email": "E-postaya geri dön", - "tabs": { - "all": "Tümü", - "groups": "Gruplar" - }, - "shared": { - "title": "Paylaşılan" - }, - "address_books": { - "create": "Yeni adres defteri", - "created": "Adres defteri oluşturuldu", - "create_failed": "Adres defteri oluşturulamadı", - "title": "Adres Defterlerim", - "shared_prefix": "Paylaşılan: {name}", - "moved": "Kişi {name} konumuna taşındı", - "moved_plural": "{count} kişi {name} konumuna taşındı", - "move_failed": "Kişi taşınamadı", - "address_book": "Adres Defteri", - "rename": "Adres defterini yeniden adlandır", - "name_label": "Adres defteri adı", - "renamed": "Adres defteri yeniden adlandırıldı", - "rename_failed": "Adres defteri yeniden adlandırılamadı", - "default": "Varsayılan", - "set_default": "Varsayılan olarak ayarla", - "default_updated": "Varsayılan adres defteri güncellendi", - "set_default_failed": "Varsayılan adres defteri ayarlanamadı", - "manage": "Adres defterlerini yönet", - "share": "Adres defterini paylaş", - "new_contact_in_book": "Bu adres defterinde yeni kişi", - "delete": "Adres defterini sil", - "confirm_delete": "\"{name}\" silinsin mi? Bu adres defterindeki tüm kişiler kaldırılacak.", - "deleted": "Adres defteri silindi", - "delete_failed": "Adres defteri silinemedi" - }, - "detail": { - "emails": "E-posta Adresleri", - "phones": "Telefon Numaraları", - "organizations": "Kuruluşlar", - "addresses": "Adresler", - "notes": "Notlar", - "titles": "Unvanlar ve Roller", - "online_services": "Çevrimiçi Hizmetler", - "anniversaries": "Yıl Dönümleri", - "personal_info": "Kişisel Bilgi", - "languages": "Diller", - "categories": "Kategoriler", - "related_contacts": "İlgili Kişiler", - "crypto_keys": "Şifreleme Anahtarları", - "cert_issuer": "Veren", - "cert_expires": "Son Kullanma", - "cert_expired": "Süresi Doldu", - "cert_fingerprint": "Parmak İzi", - "cert_algorithm": "Algoritma", - "import_to_smime": "S/MIME'ye İçe Aktar", - "cert_already_imported": "S/MIME'ye zaten aktarıldı", - "cert_imported": "Sertifika S/MIME deposuna aktarıldı", - "cert_import_failed": "Sertifika içe aktarılamadı", - "no_contact_selected": "Ayrıntıları görüntülemek için bir kişi seçin", - "compose_email": "E-posta oluştur", - "copy_email": "E-postayı kopyala", - "copy_phone": "Telefon numarasını kopyala", - "copy_url": "URL'yi kopyala", - "copied": "Panoya kopyalandı", - "copy_failed": "Panoya kopyalanamadı", - "created": "Oluşturuldu", - "updated": "Son güncelleme", - "timezone": "Saat Dilimi", - "anniversary_birth": "Doğum Günü", - "anniversary_death": "Vefat", - "anniversary_wedding": "Yıl Dönümü", - "anniversary_other": "Diğer", - "personal_expertise": "Uzmanlık", - "personal_hobby": "Hobi", - "personal_interest": "İlgi Alanı", - "personal_other": "Diğer", - "gender": "Cinsiyet", - "gender_masculine": "Erkek", - "gender_feminine": "Kadın", - "gender_other": "Diğer", - "gender_none": "Geçerli değil", - "gender_unknown": "Bilinmiyor", - "calendar": "Takvim", - "calendar_uri": "Takvim URL'si", - "scheduling_uri": "Zamanlama URL'si", - "freebusy_uri": "Müsait/Meşgul URL'si", - "section_contact": "İletişim bilgileri", - "section_work": "İş", - "section_personal": "Kişisel", - "email_default_label": "E-posta", - "phone_default_label": "Telefon", - "address_default_label": "Adres", - "online_service_default_label": "Çevrimiçi", - "organization_label": "Kuruluş", - "title_label": "Unvan", - "role_label": "Rol", - "language_label": "Dil", - "related_default_label": "İlgili", - "more_actions": "Diğer işlemler", - "age_years": "{count, plural, one {1 yaşında} other {# yaşında}}", - "years_since": "{count, plural, one {1 yıl} other {# yıl}}" - }, - "activity": { - "recent_emails": "Son E-postalar", - "upcoming_events": "Yaklaşan Etkinlikler", - "no_emails": "Son e-posta yok", - "no_events": "Yaklaşan etkinlik yok", - "no_subject": "(Konu yok)", - "no_title": "(Başlık yok)", - "load_failed": "Yüklenemedi", - "unknown_sender": "Bilinmeyen gönderici", - "all_day": "Tüm gün" - }, - "form": { - "create_title": "Yeni Kişi", - "edit_title": "Kişiyi Düzenle", - "section_address_book": "Dizin", - "select_address_book": "Bir dizin seçin...", - "section_identity": "Ad ve Kimlik", - "contact_type": "Kişi türü", - "type_person": "Kişi", - "type_organization": "Kuruluş", - "section_work": "İş ve Kuruluş", - "prefix": "Ön Ek", - "prefix_placeholder": "Dr., Bay, Bayan", - "given_name": "Ad", - "middle_name": "Orta ad", - "surname": "Soyadı", - "suffix": "Son Ek", - "suffix_placeholder": "Jr., Sr., III", - "nickname": "Takma Ad", - "nickname_placeholder": "Takma ad", - "email": "E-posta", - "email_placeholder": "email@example.com", - "phone": "Telefon", - "phone_placeholder": "+90 555 123 45 67", - "phone_type": "Tür", - "phone_voice": "Sesli", - "phone_cell": "Mobil", - "phone_fax": "Faks", - "phone_pager": "Çağrı Cihazı", - "phone_video": "Video", - "phone_text": "Metin", - "organization": "Kuruluş", - "organization_placeholder": "Şirket adı", - "department": "Departman", - "department_placeholder": "Departman", - "job_title": "İş unvanı", - "job_title_placeholder": "ör. Yazılım Mühendisi", - "role": "Rol", - "role_placeholder": "ör. Takım Lideri", - "addresses": "Adresler", - "add_address": "Adres ekle", - "street": "Sokak", - "city": "Şehir", - "region": "İl / Bölge", - "postcode": "Posta kodu", - "country": "Ülke", - "online_services": "Çevrimiçi Hizmetler", - "add_online_service": "Çevrimiçi hizmet ekle", - "url_placeholder": "https://...", - "service_placeholder": "Hizmet", - "anniversaries": "Yıl Dönümleri", - "add_anniversary": "Tarih ekle", - "anniversary_birth": "Doğum Günü", - "anniversary_wedding": "Yıl Dönümü", - "anniversary_death": "Anma Günü", - "anniversary_other": "Diğer", - "personal_info": "Kişisel Bilgi", - "add_personal_info": "Giriş ekle", - "personal_info_placeholder": "ör. Fotoğrafçılık", - "personal_expertise": "Uzmanlık", - "personal_hobby": "Hobi", - "personal_interest": "İlgi Alanı", - "personal_other": "Diğer", - "level": "Seviye", - "level_high": "Yüksek", - "level_medium": "Orta", - "level_low": "Düşük", - "categories": "Kategoriler", - "categories_placeholder": "ör. Aile, Arkadaşlar, Meslektaşlar", - "categories_hint": "Kategori aramak veya eklemek için yazın", - "category_add": "Ekle", - "note": "Notlar", - "note_placeholder": "Not ekle...", - "gender": "Cinsiyet", - "gender_sex": "Biyolojik Cinsiyet", - "gender_male": "Erkek", - "gender_female": "Kadın", - "gender_other": "Diğer", - "gender_none": "Geçerli değil", - "gender_unknown": "Bilinmiyor", - "gender_identity": "Cinsiyet kimliği", - "gender_identity_placeholder": "Cinsiyet kimliği...", - "calendar": "Takvim", - "calendar_uri": "Takvim URL'si", - "scheduling_uri": "Zamanlama URL'si", - "freebusy_uri": "Müsait/Meşgul URL'si", - "context_work": "İş", - "context_private": "Özel", - "add_email": "E-posta ekle", - "add_phone": "Telefon ekle", - "save": "Kaydet", - "cancel": "İptal", - "creating": "Oluşturuluyor...", - "updating": "Güncelleniyor...", - "name_required": "Bir ad, soyad veya kuruluş girin", - "email_invalid": "Lütfen geçerli bir e-posta adresi girin", - "email_error_inline": "Geçersiz e-posta biçimi", - "save_failed": "Kişi kaydedilemedi", - "delete": "Sil", - "upload_photo": "Fotoğraf yükle", - "remove_photo": "Fotoğrafı kaldır", - "photo_hint": "JPG veya PNG, en fazla 10 MB. Yeniden boyutlandırılacak.", - "photo_too_large": "Görüntü çok büyük (maks. 10 MB)", - "photo_invalid": "Geçersiz görüntü dosyası", - "change_photo": "Değiştir" - }, - "groups": { - "create": "Yeni Grup", - "edit": "Grubu Düzenle", - "empty": "Henüz grup yok", - "delete_confirm_title": "Grubu sil", - "delete_confirm": "Bu grubu silmek istediğinizden emin misiniz?", - "name_label": "Grup Adı", - "name_placeholder": "ör. Takım, Aile", - "name_required": "Grup adı gerekli", - "save_failed": "Grup kaydedilemedi", - "members_label": "Üyeler", - "search_members": "Eklemek için kişileri arayın...", - "no_members": "Bu grupta üye yok", - "member_count": "{count, plural, =0 {Üye yok} one {1 üye} other {# üye}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Kişileri İçe Aktar", - "drop_hint": "vCard dosyası seçmek için tıklayın", - "file_types": ".vcf veya .vcard dosyaları", - "no_contacts": "Dosyada kişi bulunamadı", - "parse_error": "vCard dosyası ayrıştırılamadı", - "found": "{count, plural, one {1 kişi bulundu} other {# kişi bulundu}}", - "duplicate": "Yinelenen", - "select_all": "Tümünü seç", - "deselect_all": "Tüm seçimi kaldır", - "selected": "{count, plural, one {1 seçildi} other {# seçildi}}", - "import_button": "İçe Aktar", - "importing": "İçe aktarılıyor...", - "success": "{count, plural, one {1 kişi içe aktarıldı} other {# kişi içe aktarıldı}}", - "failed": "İçe aktarma başarısız", - "close": "Kapat", - "file_too_large": "Dosya çok büyük (maks. 5 MB)" - }, - "export": { - "title": "Kişileri Dışa Aktar", - "success": "{count, plural, one {1 kişi dışa aktarıldı} other {# kişi dışa aktarıldı}}" - }, - "bulk": { - "selected": "{count, plural, one {1 seçildi} other {# seçildi}}", - "select_all": "Tümünü seç", - "delete": "Sil", - "delete_confirm_title": "Kişileri sil", - "delete_confirm": "{count, plural, one {1 kişi} other {# kişi}} silinsin mi?", - "deleted": "{count, plural, one {1 kişi silindi} other {# kişi silindi}}", - "add_to_group": "Gruba ekle", - "choose_group": "Grup seçin", - "adding_contacts": "{count, plural, one {1 kişi} other {# kişi}} ekleniyor", - "added_to_group": "Kişiler gruba eklendi", - "export": "Dışa Aktar", - "clear": "Seçimi temizle" - }, - "toast": { - "created": "Kişi oluşturuldu", - "updated": "Kişi güncellendi", - "deleted": "Kişi silindi", - "error_create": "Kişi oluşturulamadı", - "error_update": "Kişi güncellenemedi", - "error_delete": "Kişi silinemedi" - }, - "context_menu": { - "open": "Aç", - "edit": "Düzenle", - "send_email": "E-posta gönder", - "add_to_group": "Gruba ekle", - "export_vcard": "vCard olarak dışa aktar", - "delete": "Sil", - "call": "Ara", - "duplicate": "Çoğalt", - "print": "Yazdır" - }, - "filters": { - "toggle": "Filtreler", - "select": "Seçin", - "clear": "Temizle", - "close": "Kapat", - "title": "Gelişmiş filtreler", - "organization": "Şirket", - "organization_placeholder": "ör. Örnek A.Ş.", - "job_title": "İş unvanı", - "job_title_placeholder": "ör. Tasarımcı", - "location": "Konum", - "location_placeholder": "Şehir veya ülke", - "email_domain": "E-posta alan adı", - "email_domain_placeholder": "example.com", - "birthday_month": "Doğum günü", - "any_month": "Herhangi bir ay", - "has_email": "E-postası var", - "has_phone": "Telefonu var", - "has_photo": "Fotoğrafı var", - "sort_by": "Sıralama", - "sort_first_name": "Ad", - "sort_last_name": "Soyad" - }, - "open_categories": "Kategorileri aç" - }, - "calendar": { - "title": "Takvim", - "back_to_email": "E-postaya geri dön", - "back_to_month": "Aya geri dön", - "my_calendars": "Takvimler", - "birthday_calendar": "Doğum Günleri", - "mini_calendar_change": "Ayı değiştirmek için tıklayın", - "views": { - "month": "Ay", - "week": "Hafta", - "day": "Gün", - "agenda": "Gündem", - "today": "Bugün", - "month_hint": "Ay (m)", - "week_hint": "Hafta (w)", - "day_hint": "Gün (d)", - "agenda_hint": "Gündem (a)", - "tasks": "Görevler", - "tasks_hint": "Görevler (k)" - }, - "events": { - "create": "Etkinlik oluştur", - "edit": "Etkinliği düzenle", - "delete": "Etkinliği sil", - "details": "Etkinlik ayrıntıları", - "no_events": "Etkinlik yok", - "all_day": "Tüm gün", - "more": "+{count} daha", - "no_title": "(Başlık yok)", - "resize": "Etkinliği yeniden boyutlandır", - "duplicate": "Çoğalt", - "today_header": "Bugün", - "tomorrow_header": "Yarın", - "agenda_show_earlier": "Önceki etkinlikleri göster", - "agenda_loading": "Yükleniyor…", - "agenda_range_start": "{date} tarihinden itibaren etkinlikler", - "agenda_range_end": "{date} tarihine kadar etkinlikler", - "export_ics": ".ics olarak dışa aktar", - "copy_title": "Başlığı kopyala", - "copy_link": "Toplantı bağlantısını kopyala", - "new_event": "Yeni etkinlik", - "new_all_day_event": "Yeni tüm gün etkinlik", - "new_task": "Yeni görev", - "go_to_today": "Bugüne git" - }, - "detail": { - "add_note": "Not ekle...", - "save_note": "Kaydet", - "note_saved": "Not eklendi", - "open_link": "Bağlantıyı aç", - "meeting_link": "Toplantı bağlantısı", - "tentative": "Geçici", - "cancelled": "İptal edildi", - "delete_confirm": "Bu etkinlik silinsin mi?" - }, - "form": { - "title": "Başlık", - "description": "Açıklama", - "location": "Konum", - "meeting_link": "Toplantı bağlantısı", - "start_date": "Başlangıç tarihi", - "end_date": "Bitiş tarihi", - "start_time": "Başlangıç saati", - "end_time": "Bitiş saati", - "all_day_event": "Tüm gün etkinliği", - "calendar_select": "Takvim", - "save": "Kaydet", - "cancel": "İptal", - "delete_confirm": "Bu etkinliği silmek istediğinizden emin misiniz?", - "color": "Renk" - }, - "participants": { - "availability": { - "title": "Uygunluk", - "free": "Uygun", - "busy": "Meşgul", - "tentative": "Geçici", - "unknown": "Bu sunucuda kullanıcı değil", - "checking": "Kontrol ediliyor…" - }, - "title": "Katılımcılar", - "add": "Katılımcı ekle", - "organizer": "Organizatör", - "attendee": "Katılımcı", - "accepted": "Kabul edildi", - "declined": "Reddedildi", - "tentative": "Geçici", - "needs_action": "İşlem gerekiyor", - "remove": "Kaldır", - "edit": "Düzenle", - "email_placeholder": "E-posta adresi ekleyin veya kişileri arayın", - "send_invitations": "Katılımcılara davet gönder", - "status_summary": "{accepted} kabul etti, {pending} bekliyor", - "invited_by": "{name} tarafından davet", - "respond_below": "Aşağıdaki düğmeleri kullanarak yanıt verin", - "rsvp_label": "Yanıtınız", - "cancel_notification": "Katılımcılar iptal hakkında bilgilendirilecek", - "you_organizer": "Siz organizatörsünüz", - "you_attendee": "Siz bir katılımcısınız", - "no_participants": "Katılımcı yok", - "count": "{count, plural, one {# katılımcı} other {# katılımcı}}" - }, - "recurrence": { - "title": "Yineleme", - "none": "Tekrarlamaz", - "daily": "Günlük", - "weekly": "Haftalık", - "monthly": "Aylık", - "yearly": "Yıllık", - "every_n_days": "Her {count} günde bir", - "every_n_weeks": "Her {count} haftada bir", - "every_n_months": "Her {count} ayda bir", - "until": "Şu tarihe kadar", - "occurrences": "{count} tekrar", - "custom": "Özel…", - "edit_custom": "Özel yinelemeyi düzenle", - "every_n_years": "Her {count} yılda bir", - "on_days": "{days} günleri", - "on_day_n": "ayın {day}. günü", - "on_the_nth": "{nth} {day} günü", - "in_month": "{month} ayında", - "nth_1": "ilk", - "nth_2": "ikinci", - "nth_3": "üçüncü", - "nth_4": "dördüncü", - "nth_last": "son", - "editor_freq_day": "Gün", - "editor_freq_week": "Hafta", - "editor_freq_month": "Ay", - "editor_freq_year": "Yıl", - "editor_repeats_on": "Yinelenme", - "editor_every": "Her", - "editor_unit_days": "günde bir", - "editor_unit_weeks": "haftada bir", - "editor_unit_months": "ayda bir", - "editor_unit_years": "yılda bir", - "editor_on_day": "ayın şu günü:", - "editor_on_the": "şu gün:", - "editor_in": "ay:", - "editor_ends": "Bitiş", - "editor_never": "Asla", - "editor_ends_on": "Tarihinde", - "editor_ends_after": "Sonra:", - "editor_occurrences": "tekrar" - }, - "recurrence_scope": { - "edit_title": "Yinelenen etkinliği düzenle", - "delete_title": "Yinelenen etkinliği sil", - "description": "Bu yinelenen bir etkinlik. Hangi etkinlikleri değiştirmek istersiniz?", - "this_event": "Yalnızca bu etkinlik", - "this_and_future": "Bu ve sonraki etkinlikler", - "all_events": "Tüm etkinlikler", - "cancel": "İptal", - "save": "Kaydet", - "delete": "Sil", - "rsvp_title": "Yinelenen etkinliğe yanıt ver", - "rsvp_description": "Bu yinelenen bir etkinlik. Yanıtınız hangi etkinlikler için geçerli?", - "respond": "Yanıtla" - }, - "alerts": { - "title": "Hatırlatıcı", - "none": "Hatırlatıcı yok", - "at_time": "Etkinlik saatinde", - "minutes_before": "{count, plural, one {# dakika önce} other {# dakika önce}}", - "hours_before": "{count, plural, one {# saat önce} other {# saat önce}}", - "days_before": "{count, plural, one {# gün önce} other {# gün önce}}", - "weeks_before": "{count, plural, one {# hafta önce} other {# hafta önce}}", - "unit_minutes_before": "dakika önce", - "unit_hours_before": "saat önce", - "unit_days_before": "gün önce", - "unit_weeks_before": "hafta önce", - "add": "Hatırlatıcı ekle", - "remove": "Hatırlatıcıyı kaldır", - "amount": "Hatırlatıcı miktarı", - "unit": "Hatırlatıcı birimi" - }, - "settings": { - "title": "Takvim ayarları", - "default_view": "Varsayılan görünüm", - "week_starts_on": "Hafta başlangıcı", - "time_format": "Saat biçimi", - "default_calendar": "Varsayılan takvim", - "default_reminder": "Varsayılan hatırlatıcı", - "time_format_12h": "12 saatlik", - "time_format_24h": "24 saatlik", - "notifications_enabled": "Etkinlik bildirimleri", - "notifications_enabled_desc": "Yaklaşan takvim etkinlikleri için uyarılar göster", - "notification_sound": "Bildirim sesi", - "notification_sound_desc": "Takvim uyarıları için ses çal", - "invitation_parsing": "E-posta davetlerini ayrıştır", - "invitation_parsing_desc": "E-posta eklerindeki takvim davetlerini algıla ve takvim işlemlerini göster", - "show_time_in_month_view": "Ay görünümünde saati göster", - "show_time_in_month_view_desc": "Aylık takvim görünümünde etkinlik saatlerini görüntüle", - "show_week_numbers": "Hafta numaralarını göster", - "show_week_numbers_desc": "Mini takvimde hafta numaralarını görüntüle", - "calendar_free_scroll": "Serbest kaydırma", - "calendar_free_scroll_desc": "Tek seferde bir dönem yerine aylar, haftalar ve günler arasında kesintisiz kaydırın", - "enable_tasks": "Görevleri etkinleştir", - "enable_tasks_desc": "Yapılacaklar listesini yönetmek için takvimde görev görünümü göster", - "show_tasks_on_calendar": "Görevleri takvimde göster", - "show_tasks_on_calendar_desc": "Gün ve hafta takvim görünümlerinde görev parçacıklarını görüntüle", - "hover_preview": "Etkinlik üzerine gelme önizlemesi", - "hover_preview_desc": "Takvim etkinliklerinin üzerine gelindiğinde ayrıntı açılır penceresi göster", - "hover_preview_instant": "Anında", - "hover_preview_delay_500ms": "0,5 saniyelik gecikme", - "hover_preview_delay_1s": "1 saniyelik gecikme", - "hover_preview_delay_2s": "2 saniyelik gecikme", - "hover_preview_off": "Devre dışı", - "organizer_identity": "Davetleri şu kimlikle düzenle", - "organizer_identity_desc": "Kişileri etkinliklere davet ederken düzenleyici olarak kullanılan takvim adresi.", - "organizer_identity_failed": "Düzenleyici adresi değiştirilemedi", - "show_birthday_calendar": "Kişi doğum günü takvimi", - "show_birthday_calendar_desc": "Kişilerinizdeki doğum günlerini gösteren sanal takvim göster" - }, - "days": { - "monday": "Pazartesi", - "tuesday": "Salı", - "wednesday": "Çarşamba", - "thursday": "Perşembe", - "friday": "Cuma", - "saturday": "Cumartesi", - "sunday": "Pazar", - "mon": "Pzt", - "tue": "Sal", - "wed": "Çar", - "thu": "Per", - "fri": "Cum", - "sat": "Cmt", - "sun": "Paz" - }, - "months": { - "jan": "Oca", - "feb": "Şub", - "mar": "Mar", - "apr": "Nis", - "may": "May", - "jun": "Haz", - "jul": "Tem", - "aug": "Ağu", - "sep": "Eyl", - "oct": "Eki", - "nov": "Kas", - "dec": "Ara", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "Etkinlik oluşturuldu", - "event_updated": "Etkinlik güncellendi", - "event_deleted": "Etkinlik silindi", - "calendar_created": "Takvim oluşturuldu", - "calendar_deleted": "Takvim silindi", - "event_move_error": "Etkinlik taşınamadı", - "event_resize_error": "Etkinlik yeniden boyutlandırılamadı", - "alert_title": "Yaklaşan etkinlik", - "alert_now": "Şimdi başlıyor", - "alert_in_minutes": "{count} dk içinde", - "invitation_sent": "Davetler gönderildi", - "invitations_denied_title": "Davetler gönderilemiyor", - "invitations_denied": "Sunucu davetleri göndermeyi reddetti: {reason}. Etkinlik davet gönderilmeden kaydedilsin mi?", - "save_without_invitations": "Davetsiz kaydet", - "rsvp_updated": "Yanıt güncellendi", - "rsvp_error": "Yanıt güncellenemedi", - "event_duplicated": "Etkinlik çoğaltıldı", - "event_error": "Etkinlik kaydedilemedi", - "task_due": "Görev süresi doldu", - "event_exported": "Etkinlik dışa aktarıldı", - "title_copied": "Başlık kopyalandı", - "link_copied": "Bağlantı kopyalandı" - }, - "status": { - "loading_calendars": "Takvimler yükleniyor...", - "loading_events": "Etkinlikler yükleniyor..." - }, - "quick_create": { - "placeholder": "Yeni etkinlik başlığı", - "aria_label": "Hızlı etkinlik oluştur" - }, - "nav_prev": "Önceki", - "nav_next": "Sonraki", - "import": { - "title": "Takvim İçe Aktar", - "tab_file": "Dosya", - "tab_url": "URL", - "select_file": ".ics dosyası seçin", - "drop_file": "veya dosyayı buraya bırakın", - "supported_formats": "iCalendar (.ics) dosyalarını destekler", - "url_description": "Etkinlikleri içe aktarmak için harici iCalendar (.ics) akışının URL'sini girin.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "iCalendar (.ics) URL'lerini destekler", - "fetch": "Getir", - "invalid_url": "Lütfen geçerli bir URL girin", - "url_fetch_failed": "URL'den takvim getirilemedi", - "parsing": "Takvim dosyası ayrıştırılıyor...", - "parsed_events": "{count} etkinlik bulundu", - "no_events": "Dosyada etkinlik bulunamadı", - "select_all": "Tümünü seç", - "deselect_all": "Tüm seçimi kaldır", - "target_calendar": "Takvime içe aktar", - "import_button": "Seçilenleri içe aktar", - "importing": "Etkinlikler içe aktarılıyor...", - "success": "{count} etkinlik başarıyla içe aktarıldı", - "error": "Takvim içe aktarılamadı", - "file_too_large": "Dosya 10 MB sınırını aşıyor", - "invalid_format": "Geçersiz takvim dosyası biçimi" - }, - "webcal_action": { - "title": "Takvim bağlantısını aç", - "description": "\"{name}\" öğesini nasıl kullanmak istersiniz?", - "import_title": "Bir kez içe aktar", - "import_description": "Etkinlikleri şimdi alıp takvimlerinizden birine kopyalayın.", - "subscribe_title": "Abone ol", - "subscribe_description": "Bu takvimi ayrı bir takvim olarak otomatik eşitlenmiş tutun.", - "cancel": "İptal" - }, - "management": { - "title": "Takvim Yönetimi", - "description": "Takvimlerinizi oluşturun, yeniden adlandırın ve özelleştirin. Rengini hızlıca değiştirmek için kenar çubuğundaki bir takvime sağ tıklayın.", - "name": "Ad", - "name_placeholder": "Takvim adı", - "color": "Renk", - "change_color": "Rengi değiştir", - "random_color": "Yeni rastgele renk", - "add_calendar": "Takvim ekle", - "edit": "Düzenle", - "delete": "Sil", - "save": "Kaydet", - "create": "Oluştur", - "cancel": "İptal", - "default": "Varsayılan", - "confirm_delete": "\"{name}\" silinsin mi? Bu takvimdeki tüm etkinlikler kaldırılacak.", - "confirm_clear": "\"{name}\" takvimdeki tüm etkinlikler temizlensin mi? Bu geri alınamaz.", - "set_default": "Varsayılan olarak ayarla", - "default_updated": "Varsayılan takvim güncellendi", - "error_default": "Varsayılan takvim ayarlanamadı", - "clear_events": "Etkinlikleri temizle", - "events_cleared": "{count} etkinlik temizlendi", - "error_clear": "Takvim etkinlikleri temizlenemedi", - "calendar_created": "Takvim oluşturuldu", - "calendar_updated": "Takvim güncellendi", - "calendar_deleted": "Takvim silindi", - "color_updated": "Takvim rengi güncellendi", - "error_create": "Takvim oluşturulamadı", - "error_update": "Takvim güncellenemedi", - "error_delete": "Takvim silinemedi", - "caldav_url": "CalDAV URL'si", - "copy_url": "CalDAV URL'sini kopyala", - "url_copied": "CalDAV URL'si panoya kopyalandı", - "share": "Takvimi paylaş", - "new_event_in_calendar": "Bu takvimde yeni etkinlik", - "kind": "İçerik", - "kind_events": "Etkinlikler", - "kind_tasks": "Görevler", - "kind_both": "Etkinlikler ve görevler", - "kind_hint": "Bu takvimin CalDAV üzerinden eşitlendiğinde hangi uygulamalar tarafından görüleceğini belirler. Daha sonra değiştirilemez." - }, - "subscription": { - "title": "iCal Aboneliği", - "section_title": "iCal Abonelikleri", - "description": "Harici iCalendar akışına abone olun. Etkinlikler kendi takvimlerine otomatik olarak senkronize edilecek. https:// ve webcal:// URL'lerini destekler.", - "url_label": "Takvim URL'si", - "url_placeholder": "https://example.com/calendar.ics veya webcal://...", - "name_label": "Takvim adı", - "name_placeholder": "ör. Resmi Tatiller", - "color_label": "Renk", - "refresh_interval": "Yenileme aralığı", - "interval_15": "Her 15 dakikada bir", - "interval_30": "Her 30 dakikada bir", - "interval_60": "Her saat", - "interval_360": "Her 6 saatte bir", - "interval_1440": "Her gün", - "subscribe": "Abone Ol", - "subscribing": "Abone olunuyor...", - "save": "Değişiklikleri kaydet", - "saving": "Kaydediliyor...", - "edit": "Düzenle", - "edit_title": "Aboneliği Düzenle", - "updated": "\"{name}\" güncellendi", - "update_error": "Abonelik güncellenemedi", - "invalid_url": "Lütfen geçerli bir URL girin", - "success": "\"{name}\" aboneliğine girildi", - "error": "Abonelik eklenemedi", - "refresh": "Şimdi yenile", - "refresh_success": "Abonelik yenilendi", - "refresh_error": "Abonelik yenilenemedi", - "unsubscribe": "Aboneliği İptal Et", - "confirm_delete": "\"{name}\" aboneliği iptal edilsin mi? Takvim ve tüm etkinlikleri kaldırılacak.", - "deleted": "Abonelik kaldırıldı", - "delete_error": "Abonelik kaldırılamadı", - "last_refreshed": "Son güncelleme: {time}" - }, - "tasks": { - "label": "Görevler", - "no_tasks": "Görev yok", - "no_title": "(Başlık yok)", - "mark_complete": "Tamamlandı olarak işaretle", - "mark_incomplete": "Tamamlanmadı olarak işaretle", - "filter_all": "Tümü", - "filter_pending": "Bekleyen", - "filter_completed": "Tamamlanan", - "filter_overdue": "Geciken", - "show_completed": "Tamamlananları göster", - "create": "Yeni Görev", - "edit": "Görevi Düzenle", - "title_placeholder": "Görev başlığı", - "description_placeholder": "Açıklama ekle...", - "due_date": "Son tarih", - "include_time": "Saati dahil et", - "priority": "Öncelik", - "priority_none": "Yok", - "priority_high": "Yüksek", - "priority_medium": "Orta", - "priority_low": "Düşük", - "progress": "Durum", - "progress_needs_action": "İşlem gerekiyor", - "progress_in_process": "Devam ediyor", - "progress_completed": "Tamamlandı", - "progress_cancelled": "İptal edildi", - "calendar": "Takvim", - "alert": "Hatırlatıcı", - "alert_none": "Yok", - "alert_at_time": "Son tarih saatinde", - "alert_5min": "5 dakika önce", - "alert_15min": "15 dakika önce", - "alert_30min": "30 dakika önce", - "alert_1hr": "1 saat önce", - "alert_1day": "1 gün önce", - "delete": "Sil", - "cancel": "İptal", - "save": "Kaydet", - "quick_add_placeholder": "Görev ekle...", - "due_today": "Bugün", - "due_tomorrow": "Yarın", - "overdue": "Gecikmiş" - }, - "nav_open_menu": "Menüyü aç" - }, - "calendar_event_notifications": { - "someone": "Birisi", - "invited": "{name} sizi \"{title}\" etkinliğine davet etti", - "updated": "{name} \"{title}\" etkinliğini güncelledi", - "cancelled": "{name} \"{title}\" etkinliğini iptal etti", - "untitled": "Başlıksız etkinlik" - }, - "share_notifications": { - "someone": "Birisi", - "shared": "{name}, \"{object}\" {kind} sizinle paylaştı", - "changed": "{name}, \"{object}\" {kind} erişiminizi değiştirdi", - "revoked": "{name}, \"{object}\" {kind} erişiminizi kaldırdı", - "object": { - "folder": "klasörünü", - "calendar": "takvimini", - "address_book": "adres defterini", - "files": "dosya klasörünü" - } - }, - "sharing": { - "title": "\"{name}\" paylaş", - "description": "Bu sunucudaki diğer kullanıcılara veya gruplara erişim verin. Değişiklikler hemen geçerli olur.", - "no_shares": "Henüz kimseyle paylaşılmadı.", - "add_person": "Kişi veya grup ekle", - "search_placeholder": "Ad veya e-posta ile arayın…", - "loading_principals": "Kullanıcılar yükleniyor…", - "no_principals": "Başka kullanıcı veya grup bulunamadı.", - "no_match": "Eşleşme yok.", - "remove": "Erişimi kaldır", - "group": "Grup", - "share_added": "Erişim verildi", - "share_updated": "Erişim güncellendi", - "share_removed": "Erişim kaldırıldı", - "share_failed": "Paylaşım güncellenemedi", - "preset": { - "freeBusy": "Yalnızca müsait/meşgul", - "read": "Yalnızca okuma", - "readWrite": "Okuma ve yazma", - "manager": "Yönetici", - "custom": "Özel" - } - }, - "advanced_search": { - "title": "Gelişmiş Arama", - "from": "Kimden", - "from_placeholder": "Gönderici e-posta veya adı", - "to": "Kime", - "to_placeholder": "Alıcı e-posta veya adı", - "subject": "Konu", - "subject_placeholder": "Konu içerir...", - "body": "Gövde", - "body_placeholder": "Gövde içerir...", - "folder": "Klasör", - "all_folders": "Tüm klasörler", - "has_attachment": "Ekler", - "date_after": "Sonra", - "date_before": "Önce", - "size_min": "Şundan büyük (KB)", - "size_max": "Şundan küçük (KB)", - "starred": "Yıldızlı", - "unread": "Okunmamış", - "read": "Okunmuş", - "yes": "Evet", - "no": "Hayır", - "clear": "Temizle", - "clear_all": "Tümünü temizle", - "filters_active": "{count} filtre", - "filters_active_plural": "{count} filtre", - "toggle_filters": "Daha fazla", - "search_hint": "Kesin arama için gelişmiş filtreleri kullanın", - "advanced_filters_tooltip": "Gelişmiş arama filtreleri", - "results_found": "{count, plural, =0 {Sonuç bulunamadı} one {# sonuç bulundu} other {# sonuç bulundu}}", - "results_found_more": "{count}+ sonuç bulundu", - "suggestions_recent": "Son aramalar", - "suggestions_people": "Kişiler", - "suggestions_from": "{address} adresinden gelen e-postaları ara", - "suggestions_to": "{address} adresine gönderilen e-postaları ara", - "suggestions_remove_recent": "Son aramalardan kaldır" - }, - "welcome": { - "title": "Posta kutunuza hoş geldiniz", - "tip_compose": "Yeni e-posta oluşturmak için c tuşuna basın", - "tip_shortcuts": "Tüm klavye kısayollarını görmek için ? tuşuna basın", - "tip_sidebar": "Takvim, Kişiler ve Ayarlar'ı kenar çubuğu menüsünde bulabilirsiniz", - "tip_settings": "Deneyiminizi Ayarlar'dan özelleştirin", - "got_it": "Anladım", - "settings": "Ayarlar", - "dismiss": "Kapat", - "start_tour": "Turu Başlat" - }, - "demo_welcome": { - "title": "Bulwark Mail'e Hoş Geldiniz", - "description": "Tam özellikli bir web posta istemcisini doğrudan tarayıcınızda keşfedin. Tüm veriler cihazınızda kalır, her şeyi özgürce test edin.", - "feature_email": "E-posta oku ve oluştur", - "feature_organize": "Etiketler, yıldızlar ve klasörler", - "feature_shortcuts": "Klavye kısayolları", - "feature_privacy": "%100 gizli demo", - "hint": "Başlamak için soldaki herhangi bir e-postaya tıklayın veya aşağıdaki turu yapın." - }, - "files": { - "office_edit": "Belgeyi düzenle", - "office_loading": "Belge düzenleyici açılıyor…", - "office_error": "Belge düzenleyici açılamadı.", - "office_close": "Düzenleyiciyi kapat", - "open_in_new_tab": "Yeni sekmede aç", - "title": "Dosyalar", - "search_placeholder": "Dosyalarda ara...", - "empty_state_title": "Henüz dosya yok", - "empty_state_description": "Başlamak için dosya yükleyin veya klasör oluşturun", - "upload": "Yükle", - "upload_files": "Dosya Yükle", - "new_folder": "Yeni Klasör", - "new_folder_name": "Klasör adı", - "rename": "Yeniden Adlandır", - "rename_title": "Yeniden Adlandır", - "new_name": "Yeni ad", - "name_forbidden_chars": "Sunucu adlarda şu karakterlere izin vermiyor: {chars}", - "name_reserved": "Bu ad sunucuda ayrılmış.", - "delete": "Sil", - "delete_confirm_title": "Kaynağı sil", - "delete_confirm_message": "\"{name}\" silinsin mi? Bu geri alınamaz.", - "download": "İndir", - "name": "Ad", - "size": "Boyut", - "modified": "Değiştirildi", - "type": "Tür", - "folder": "Klasör", - "file": "Dosya", - "parent_directory": "Üst dizin", - "breadcrumb_root": "Ana Sayfa", - "drop_files_here": "Yüklemek için dosya veya klasörleri buraya bırakın", - "uploading": "Yükleniyor...", - "upload_success": "{count, plural, one {1 dosya yüklendi} other {# dosya yüklendi}}", - "upload_error": "Dosya yüklenemedi", - "create_folder_success": "Klasör oluşturuldu", - "create_folder_error": "Klasör oluşturulamadı", - "delete_success": "Başarıyla silindi", - "delete_error": "Silinemedi", - "rename_success": "Başarıyla yeniden adlandırıldı", - "rename_error": "Yeniden adlandırılamadı", - "download_error": "İndirilemedi", - "not_available": "Bu sunucuda dosya depolama kullanılamıyor", - "cancel": "İptal", - "create": "Oluştur", - "save": "Kaydet", - "no_results": "Aramanızla eşleşen dosya yok", - "batch_delete_confirm_message": "{count, plural, one {1 öge} other {# öge}} silinsin mi? Bu geri alınamaz.", - "batch_delete_success": "{count, plural, one {1 öge silindi} other {# öge silindi}}", - "grid_view": "Izgara görünümü", - "list_view": "Liste görünümü", - "details": "Ayrıntılar", - "path": "Yol", - "preview": "Önizleme", - "preview_error": "Önizleme yüklenemedi", - "cut": "Kes", - "copy": "Kopyala", - "paste": "Yapıştır", - "move_success": "{count, plural, one {1 öge taşındı} other {# öge taşındı}}", - "move_error": "Taşınamadı", - "paste_success": "Başarıyla yapıştırıldı", - "paste_error": "Yapıştırılamadı", - "new_text_file": "Yeni Metin Dosyası", - "file_name": "Dosya adı", - "retry": "Yeniden dene", - "refresh": "Yenile", - "toggle_favorite": "Favoriye ekle/çıkar", - "duplicate": "Çoğalt", - "duplicate_success": "Başarıyla çoğaltıldı", - "duplicate_error": "Çoğaltılamadı", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Dosya oluşturuldu", - "create_file_error": "Dosya oluşturulamadı", - "favorites": "Favoriler", - "recent": "Son Kullanılan", - "properties": "Özellikler", - "open_folder": "Klasörü aç", - "upload_folder": "Klasör Yükle", - "file_too_large": "\"{name}\" maksimum dosya boyutunu ({max}) aşıyor", - "undo": "Geri Al", - "undo_success": "İşlem geri alındı", - "undo_error": "Geri alınamadı", - "toolbar": "Dosya işlemleri", - "file_list": "Dosyalar ve klasörler", - "context_menu": "İşlemler", - "settings_title": "Dosya Ayarları", - "settings_display": "Görüntüleme", - "settings_default_view": "Varsayılan Görünüm", - "settings_default_view_desc": "Izgara ve liste düzeni arasında seçin", - "settings_default_sort": "Varsayılan Sıralama", - "settings_default_sort_desc": "Dosyalar için varsayılan sıralamayı seçin", - "settings_sort_direction": "Sıralama Yönü", - "settings_sort_direction_desc": "Artan veya azalan düzen seçin", - "settings_ascending": "Artan", - "settings_descending": "Azalan", - "settings_icons": "Simgeler", - "settings_show_icons": "Dosya Simgelerini Göster", - "settings_show_icons_desc": "Dosyaların ve klasörlerin yanında simgeleri görüntüle", - "settings_colored_icons": "Renkli Simgeler", - "settings_colored_icons_desc": "Tek renk yerine renkli simgeler kullan", - "settings_show_thumbnails": "Küçük Resimleri Göster", - "settings_show_thumbnails_desc": "Resim dosyaları için simgeler yerine görüntü önizlemeleri göster", - "settings_behavior": "Davranış", - "settings_show_hidden": "Gizli Dosyaları Göster", - "settings_show_hidden_desc": "Nokta ile başlayan dosya ve klasörleri görüntüle", - "settings_folder_layout": "Klasör Gezinmesi", - "settings_folder_layout_desc": "Klasörlerin nasıl görüntüleneceğini seçin: dosyalarla birlikte satır içi veya kenar çubuğu ağacında", - "settings_folder_layout_inline": "Satır içi", - "settings_folder_layout_sidebar": "Kenar Çubuğu", - "disabled_title": "Dosyalar özelliği yöneticiniz tarafından devre dışı bırakıldı", - "disabled_description": "WebDAV üzerinden büyük dosya yüklemeleri Stalwart/RocksDB kararsızlığına yol açabilir; bellek yetersizliği çöküşleri ve kurtarılamaz disk kullanımı dahil. Silinen dosyalar blob depolamadan hemen temizlenmeyebilir. Bu özellik üretim ortamları için önerilmez.", - "stability_warning": "Büyük dosya yüklemeleri sunucu kararsızlığına neden olabilir. Silinen dosyalar depolamadan hemen temizlenmeyebilir. Dikkatli kullanın.", - "no_accounts": "Bağlı hesap yok.", - "open_folder_tree": "Klasör ağacını aç", - "other_accounts": "Diğer hesaplar", - "migration_title": "Dosyalarınız güncelleniyor…", - "migration_description": "Klasörler ve dosyalar doğru yapıya göre düzenleniyor. Bu yalnızca bir kez gerçekleşir." - }, - "smime": { - "your_certificates": "Sertifikalarınız", - "your_certificates_desc": "E-postaları imzalamak ve şifrelemek için S/MIME sertifikalarınızı içe aktarın ve yönetin", - "recipient_certificates": "Alıcı Sertifikaları", - "recipient_certificates_desc": "Alıcılara e-posta şifrelemek için genel sertifikalar", - "identity_bindings": "Kimlik Anahtar Bağlamaları", - "identity_bindings_desc": "S/MIME sertifikalarını e-posta kimliklerinize bağlayın", - "defaults_title": "Varsayılanlar", - "defaults_desc": "Varsayılan imzalama ve şifreleme davranışını yapılandırın", - "import_pkcs12": "PKCS#12 İçe Aktar (.p12/.pfx)", - "import_public_cert": "Sertifika İçe Aktar", - "no_certificates": "Henüz sertifika içe aktarılmadı", - "no_recipient_certs": "Alıcı sertifikası yok", - "expires": "Son Kullanma", - "expired": "Süresi Doldu", - "bound_to": "Bağlı", - "no_key_bound": "Yok", - "lock": "Anahtarı kilitle", - "unlock": "Anahtarın kilidini aç", - "details": "Ayrıntıları görüntüle", - "delete": "Sil", - "encrypt_by_default": "Varsayılan olarak şifrele", - "encrypt_by_default_desc": "Tüm alıcıların sertifikası olduğunda e-postaları otomatik şifrele", - "remember_unlocked": "Kilidi açık anahtarları hatırla", - "remember_unlocked_desc": "Bu tarayıcı oturumu süresince anahtarların kilidini açık tut", - "sign_default_for": "Varsayılan imzala", - "enter_p12_passphrase": "PKCS#12 Parolası Girin", - "p12_passphrase_desc": "Bu sertifika dosyasını koruyan parolayı girin", - "enter_storage_passphrase": "Depolama Parolası Ayarla", - "storage_passphrase_desc": "Tarayıcınızda bu anahtarı beklerken korumak için bir parola seçin", - "next": "İleri", - "import": "İçe Aktar", - "unlock_key": "Anahtarın Kilidini Aç", - "unlock_key_desc": "İmzalama veya şifre çözme için bu anahtarın kilidini açmak üzere depolama parolasını girin", - "passphrase_placeholder": "Parola girin", - "confirm_passphrase_placeholder": "Parolayı onaylayın", - "passphrase_mismatch": "Parolalar eşleşmiyor", - "cancel": "İptal", - "processing": "İşleniyor…", - "close": "Kapat", - "certificate_details": "Sertifika Ayrıntıları", - "cert_subject": "Konu", - "cert_issuer": "Veren", - "cert_email": "E-posta", - "cert_serial": "Seri Numarası", - "cert_validity": "Geçerlilik", - "cert_fingerprint": "Parmak İzi (SHA-256)", - "cert_algorithm": "Algoritma", - "cert_capabilities": "Yetenekler", - "cert_source": "Kaynak", - "cert_expired": "Bu sertifikanın süresi dolmuş", - "cert_not_yet_valid": "Bu sertifika henüz geçerli değil", - "cap_sign": "İmzalama", - "cap_encrypt": "Şifreleme", - "cap_none": "Yok", - "show_passphrase": "Parolayı göster", - "hide_passphrase": "Parolayı gizle", - "sign_toggle": "İmzala", - "encrypt_toggle": "Şifrele", - "missing_recipient_certs": "Eksik sertifikalar: {emails}", - "missing_sender_cert": "Bu kimliğe bağlı sertifika yok", - "status_encrypted_ok": "Bu ileti şifrelendi", - "status_encrypted_no_key": "Bu ileti şifreli ama eşleşen anahtar bulunamadı", - "status_encrypted_failed": "Bu ileti şifresi çözülemedi", - "status_signed_valid": "İmza doğrulandı", - "status_signed_invalid": "İmza doğrulama başarısız", - "status_signed_expired_cert": "Süresi dolmuş sertifika ile imzalandı", - "status_signed_self_signed": "İmza geçerli, ancak sertifika kendi kendine imzalanmış (güvenilir değil)", - "status_signed_mismatch": "İmza geçerli, ancak imzalayan göndericiyle eşleşmiyor", - "status_unsupported": "Desteklenmeyen S/MIME biçimi", - "auto_import_signer_certs": "İmzalayan sertifikaları otomatik içe aktar", - "auto_import_signer_certs_desc": "Gelecekteki şifreleme için doğrulanmış imzalı e-postalardan sertifikaları otomatik kaydet", - "export": "Dışa Aktar", - "enter_export_passphrase": "Dışa Aktarma Parolası Ayarla", - "export_passphrase_desc": "Dışa aktarılan PKCS#12 dosyasını korumak için bir parola seçin", - "export_storage_desc": "Dışa aktarma için anahtarın şifresini çözmek üzere depolama parolasını girin", - "incorrect_passphrase": "Yanlış parola" - }, - "tour": { - "step_counter": "{total} adımdan {current}.", - "skip": "Turu atla", - "back": "Geri", - "next": "İleri", - "finish": "Bitir", - "take_a_tour": "Arayüz turu yap", - "restart_title": "Tanıtım turu", - "restart_desc": "Arayüzün rehberli gezintisini tekrar oynat", - "restart_button": "Turu yeniden başlat", - "show_on_new_devices_title": "Yeni cihazlarda göster", - "show_on_new_devices_desc": "Başka bir yerde tamamlamış olsanız bile, yeni bir cihazda ilk oturum açtığınızda karşılama afişini ve turu yeniden gösterin", - "sidebar_title": "Posta kutularınız", - "sidebar_desc": "Bu sizin klasör kenar çubuğunuzdur. E-postalarını görüntülemek için herhangi bir posta kutusuna tıklayın. Klasörler oluşturabilir, e-postaları aralarında sürükleyebilir ve okunmamış sayılarını bir bakışta görebilirsiniz.", - "compose_title": "E-posta oluştur", - "compose_desc": "Yeni e-posta yazmak için buraya tıklayın. Alıcılar ekleyebilir, ek ekleyebilir ve zengin metin biçimlendirmesi kullanabilirsiniz.", - "search_title": "Postanızda arayın", - "search_desc": "Gönderici, konu veya içeriğe göre arayın. Tarih aralığı, ekler ve yıldızlı iletiler gibi gelişmiş seçenekler için filtre simgesine tıklayın.", - "email_list_title": "E-posta listeniz", - "email_list_desc": "E-postalar burada görünür. Okumak için bir tanesine tıklayın. Birden fazla seçmek için onay kutusunu kullanın, ardından toplu taşıma, silme veya etiketleme yapın.", - "email_viewer_title": "Okuma bölmesi", - "email_viewer_desc": "Seçilen e-posta burada açılır. Araç çubuğu düğmeleriyle yanıtlayın, iletin, arşivleyin veya silin. E-postalara yıldız da ekleyebilir veya renk etiketi atayabilirsiniz.", - "keywords_title": "Renk etiketleri", - "keywords_desc": "E-postalarınızı renk kodlu etiketlerle düzenleyin. Etiketlemek için bir e-postayı etikete sürükleyin ya da etiket atamak için sağ tıklayın.", - "calendar_title": "Takvim", - "calendar_desc": "Etkinliklerinizi yönetmek için takvime geçin. Etkinlikler oluşturun, hatırlatıcılar ayarlayın ve gün, hafta veya aylık görünümlerde izleyin.", - "contacts_title": "Kişiler", - "contacts_desc": "Adres defteriniz burada. Kişileri içe aktarın, gruplar oluşturun ve tam ayrıntılarını görmek için herhangi bir kişiye tıklayın.", - "settings_title": "Ayarlar", - "settings_desc": "Her şeyi özelleştirin: tema, yoğunluk, imzalar, filtreler, klavye kısayolları, takvim varsayılanları ve daha fazlası.", - "shortcuts_title": "Klavye kısayolları", - "shortcuts_desc": "Deneyimli kullanıcılar bunu sever. Mevcut tüm kısayolları görmek için istediğiniz zaman ? tuşuna basın. Fareye dokunmadan gezinebilir, e-posta oluşturabilir ve yönetebilirsiniz.", - "compose_open_title": "Yazıcı", - "compose_open_desc": "Bu e-posta yazıcısıdır. Alıcılar ekleyin, iletinizi yazın, dosya ekleyin ve zengin metin biçimlendirmesi kullanın. Ayrıca taslak kaydedebilir ve şablonları kullanabilirsiniz.", - "calendar_view_title": "Takviminiz", - "calendar_view_desc": "İşte örnek etkinliklerin bulunduğu takviminiz. Araç çubuğunu kullanarak gün, hafta, ay ve gündem görünümleri arasında geçiş yapabilirsiniz.", - "create_event_title": "Etkinlik oluştur", - "create_event_desc": "Yeni takvim etkinliği oluşturmak için bu düğmeye tıklayın. Başlık, tarih, saat ayarlayabilir ve katılımcı ekleyebilirsiniz.", - "event_modal_title": "Etkinlik ayrıntıları", - "event_modal_desc": "İşte etkinlik formu. Başlığı doldurun, tarih ve saat seçin, konum veya katılımcı ekleyin. Hazır olduğunuzda kaydedin - ya da kapatıp devam edin.", - "contacts_list_title": "Kişileriniz", - "contacts_list_desc": "İşte kişileriniz. Herhangi bir kişiye tıklayarak sağ taraftaki tam ayrıntılarını görebilirsiniz. Ayrıca yeni kişiler oluşturabilir, vCard içe aktarabilir veya kişileri gruplara düzenleyebilirsiniz.", - "settings_tabs_title": "Ayarlar menüsü", - "settings_tabs_desc": "İşte tüm ayar kategorileri. Görünümünüzü özelleştirin, kimlikleri yönetin, e-posta filtreleri kurun, takviminizi yapılandırın ve çok daha fazlasını yapın.", - "files_title": "Dosya depolama", - "files_desc": "Dosya tarayıcınız dosya yüklemenize, düzenlemenize ve paylaşmanıza olanak tanır - postanıza yerleşik kişisel bulut sürücüsü gibi.", - "demo_banner_title": "Demo kontrolleri", - "demo_banner_desc": "Demo modundasınız - her şey tarayıcınızda kalır. Temiz örnek verilerle yeniden başlamak için istediğiniz zaman 'Demoyu Sıfırla'ya basın.", - "quota_title": "Depolama kullanımı", - "quota_desc": "Posta kutusu boyutunuzu buradan takip edin. Daha fazla alan kullandıkça daire dolmaya başlar." - }, - "unified_mailbox": { - "search_unavailable": "Birleşik görünümde arama kullanılamaz" - }, - "quote_header": { - "reply_line": "{date} tarihinde {from} şunu yazdı:", - "forwarded_separator": "---------- İletilen mesaj ----------", - "from_label": "Kimden", - "date_label": "Tarih", - "subject_label": "Konu" - }, - "pwa_install": { - "title": "{appName} uygulamasını yükle", - "description": "Hızlı erişim ve çevrimdışı destek için uygulamamızı yükleyin.", - "not_now": "Şimdi değil", - "install": "Yükle", - "dont_remind": "Bir daha hatırlatma", - "dismiss_aria": "Yükleme istemini kapat" - }, - "deep_link": { - "message_not_found": "Bu ileti artık kullanılamıyor.", - "thread_not_found": "Bu sohbet artık kullanılamıyor.", - "folder_not_found": "Bu klasör artık kullanılamıyor.", - "event_not_found": "Bu etkinlik artık kullanılamıyor.", - "file_not_found": "Bu dosya artık kullanılamıyor.", - "account_unavailable": "Bu bağlantıyı açmak için o hesapta oturum açın.", - "copy_message": "İleti bağlantısını kopyala", - "copy_conversation": "Sohbet bağlantısını kopyala", - "copy_event": "Etkinlik bağlantısını kopyala", - "copied": "Bağlantı kopyalandı", - "copy_failed": "Bağlantı kopyalanamadı" - }, - "plugins": { - "oauth_callback": { - "completing": "Yetkilendirme tamamlanıyor…", - "completing_message": "Bağlantı kurulurken lütfen bekleyin.", - "received_title": "Yetkilendirme alındı", - "received_message": "Bağlantıyı başlattığınız sekmeye dönebilirsiniz.", - "failed_title": "Yetkilendirme başarısız oldu", - "denied": "Yetkilendirme reddedildi.", - "failed_detail": "Yetkilendirme başarısız oldu: {error}", - "missing_params": "Yetkilendirme kodu veya durum eksik.", - "delivery_failed": "Yetkilendirme sonucu uygulama sekmesine iletilemedi. Lütfen bu pencereyi kapatıp tekrar deneyin." - } - }, - "global_search": { - "title": "Her yerde ara", - "placeholder": "Posta, kişiler, takvim ve dosyalarda ara...", - "scope_label": "Şuralarda ara", - "account_label": "Hesap", - "scope_all": "Tümü", - "scope_mail": "Posta", - "scope_contacts": "Kişiler", - "scope_calendar": "Takvim", - "scope_files": "Dosyalar", - "all_accounts": "Tüm hesaplar", - "show_all": "Tüm sonuçları göster", - "no_results": "“{query}” için sonuç yok", - "server_hint": "Tam kelimeler sunucuda arar; kelime parçaları yüklenmiş öğelerle eşleşir", - "searching": "Aranıyor...", - "account_error": "{account} içinde arama yapılamadı", - "recent_searches": "Son aramalar", - "recurring": "Yinelenen", - "no_subject": "(no subject)", - "empty_hint": "Tüm hesaplarda posta, kişi, etkinlik ve dosya arayın. Şunları deneyin: from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Tarihe göre düz", - "grouped": "Gruplandırılmış", - "load_more": "Daha fazla yükle", - "preview_empty": "Önizlemek için bir sonuç seçin", - "open_full": "Aç", - "view_label": "Görünüm" - } -} +{ + "meta_description": "JMAP protokolünü kullanan minimalist web posta istemcisi", + "login": { + "title": "Webmail", + "username_label": "E-posta", + "username_placeholder": "kullanici@ornek.com", + "password_label": "Şifre", + "password_placeholder": "Şifrenizi girin", + "jmap_endpoint_label": "JMAP Sunucusu", + "jmap_endpoint_placeholder": "https://mail.ornek.com", + "jmap_endpoint_cors_hint": "Sunucu, bu etki alanından gelen CORS isteklerine izin vermelidir.", + "jmap_server_label": "Sunucu", + "jmap_server_auto_picked": "Sunucu, e-posta etki alanınıza göre seçildi.", + "sign_in": "Giriş yap", + "signing_in": "Giriş yapılıyor...", + "loading": "Yükleniyor...", + "reconnecting": "Bağlantı kesildi. Yeniden bağlanılmaya çalışılıyor…", + "error": { + "invalid_credentials": "Geçersiz e-posta veya şifre. Lütfen bilgilerinizi kontrol edip tekrar deneyin.", + "invalid_token": "Sunucu bu erişim belirtecini kabul etmedi. Belirtecin eksiksiz olduğunu, süresinin dolmadığını ve iptal edilmediğini kontrol edin.", + "connection_failed": "Sunucuya ulaşılamıyor. İnternet bağlantınızı kontrol edip tekrar deneyin.", + "cors_blocked": "Sunucuya ulaşılıyor ancak çapraz kaynak istekleri engelleniyor. JMAP sunucunuzun CORS ayarlarını kontrol edip bu etki alanına izin verin.", + "server_error": "Sunucu geçici olarak kullanılamıyor. Lütfen daha sonra tekrar deneyin.", + "generic": "Beklenmedik bir hata oluştu. Bu sorun devam ederse yöneticinizle iletişime geçin.", + "totp_required": "İki faktörlü doğrulama kodu gereklidir. Lütfen kodunuzu aşağıya girin.", + "totp_invalid": "Geçersiz doğrulama kodu. Lütfen doğrulama uygulamanızı kontrol edip tekrar deneyin.", + "oauth_discovery_failed": "SSO etkin ancak kimlik sağlayıcısına ulaşılamıyor. OAuth yapılandırmanızı kontrol edin.", + "token_exchange_failed": "Parolanız ve kodunuz kabul edildi ancak posta sunucusu bu uygulama için oturum başlatmayı reddetti. Yöneticinizden OAuth istemci ayarlarını kontrol etmesini isteyin." + }, + "show_password": "Şifreyi göster", + "hide_password": "Şifreyi gizle", + "totp_toggle": "2FA kodum var", + "token_toggle": "Erişim belirteciyle giriş yap", + "password_toggle": "Şifreyle giriş yap", + "token_label": "Erişim belirteci", + "token_placeholder": "API belirtecinizi yapıştırın", + "token_hint": "E-posta sağlayıcınızın ayarlarında bir API belirteci oluşturun ve buraya yapıştırın.", + "remember_me": "Beni hatırla", + "config_error": { + "title": "Yapılandırma Hatası", + "fetch_failed": "Uygulama yapılandırması yüklenemiyor. Lütfen daha sonra tekrar deneyin.", + "server_not_configured": "Posta sunucusu yapılandırılmamış. Lütfen yöneticinizle iletişime geçin." + }, + "remove_from_history": "Geçmişten kaldır", + "totp_label": "Doğrulama kodu", + "totp_placeholder": "000000", + "session_expired": "Oturumunuzun süresi doldu. Lütfen tekrar giriş yapın.", + "dismiss": "Kapat", + "or": "veya", + "sign_in_sso": "SSO ile giriş yap", + "add_account_title": "Hesap Ekle", + "add_account_subtitle": "Başka bir hesapla giriş yap", + "cancel": "İptal", + "website": "Web Sitesi", + "imprint": "Yasal Bilgiler", + "privacy_policy": "Gizlilik Politikası", + "try_demo": "Demoyu Dene", + "demo_description": "Örnek verilerle keşfedin - hesap gerekmez", + "demo_launching": "Demo başlatılıyor...", + "demo_login_button": "Demoyu Başlat", + "demo_tagline": "Tam özellikli bir e-posta istemcisini deneyimleyin. Hesap gerekmez.", + "demo_no_signup": "Kayıt gerekmez - örnek verilerle özgürce keşfedin", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Giriş tamamlanıyor...", + "oauth_error": { + "title": "Kimlik Doğrulama Başarısız", + "invalid_state": "Güvenlik doğrulaması başarısız. Lütfen tekrar giriş yapmayı deneyin.", + "missing_params": "Yetkilendirme verisi eksik. Lütfen tekrar giriş yapmayı deneyin.", + "token_exchange_failed": "Kimlik doğrulama tamamlanamadı. Lütfen tekrar deneyin.", + "access_denied": "Erişim reddedildi. Lütfen yöneticinizle iletişime geçin.", + "back_to_login": "Giriş sayfasına dön" + } + }, + "sidebar": { + "close": "Kapat", + "compose": "Yeni E-posta", + "compose_hint": "Yeni E-posta (c)", + "search_placeholder": "Posta ara...", + "search_placeholder_hint": "Posta ara... (/ tuşuna basın)", + "storage": "Depolama", + "storage_used": "Kullanılan", + "storage_free": "Boş", + "storage_total": "Toplam", + "sign_out": "Çıkış yap", + "sign_out_of": "{account} hesabından çıkış yap", + "sign_out_all": "Tüm hesaplardan çıkış yap", + "add_account": "Hesap ekle", + "set_as_default": "Varsayılan olarak ayarla", + "switch_account": "Hesap değiştir", + "contacts": "Kişiler", + "calendar": "Takvim", + "settings": "Ayarlar", + "admin": "Yönetim", + "files": "Dosyalar", + "loading_mailboxes": "Posta kutuları yükleniyor...", + "push_connected": "Anlık güncellemeler aktif", + "push_disconnected": "Anlık güncellemeler devre dışı", + "keyboard_shortcuts": "Klavye Kısayolları", + "theme": { + "light": "Açık tema", + "dark": "Koyu tema", + "system": "Sistem teması" + }, + "language": { + "title": "Dil" + }, + "mailboxes": { + "inbox": "Gelen Kutusu", + "sent": "Gönderilenler", + "drafts": "Taslaklar", + "trash": "Çöp Kutusu", + "archive": "Arşiv", + "starred": "Yıldızlı", + "all_mail": "Tüm Postalar", + "spam": "Spam", + "important": "Önemli" + }, + "unified_inbox": "Birleşik Gelen Kutusu", + "unified_sent": "Tüm Gönderilenler", + "unified_drafts": "Tüm Taslaklar", + "unified_trash": "Tüm Çöp Kutusu", + "unified_archive": "Tüm Arşiv", + "unified_junk": "Tüm Önemsiz", + "all_accounts": "Tüm Hesaplar", + "all_accounts_loading": "Hesaplar yükleniyor: {loaded}/{total}", + "unified_mailbox": "Birleşik Posta Kutusu", + "expand": "Genişlet", + "collapse": "Daralt", + "expand_tooltip": "Genişlet", + "collapse_tooltip": "Daralt", + "mobile": { + "search": "Ara", + "compose": "Yeni E-posta", + "go_back": "Geri git", + "toggle_menu": "Menüyü aç/kapat" + }, + "clear_search": "Aramayı temizle", + "vacation_active": "Tatil otomatik yanıtı aktif", + "demo_banner": "Demo Modu", + "demo_reset": "Sıfırla", + "demo_tour": "Tur", + "tags": "Etiketler", + "show_all_tags": "Tümünü göster ({count})", + "show_fewer_tags": "Daha az göster", + "folders": "Klasörler", + "your_folders": "Klasörleriniz", + "create_folder": "Klasör oluştur", + "show_more": "Diğer", + "show_fewer": "Daha az", + "shared": "Paylaşılan", + "shared_account_hidden": "Kenar çubuğundan gizlendi. Ayarlar › Düzen bölümünden yeniden gösterebilirsiniz.", + "notifications_muted": "Bildirimler sessizde", + "mail": "Posta", + "nav_label": "Gezinme", + "add_app": "Uygulamalar", + "scheduled": "Zamanlandı", + "unified_all_unread": "Tüm okunmamışlar", + "unified_all_starred": "Tüm yıldızlılar", + "unified_all_mail": "Tüm postalar", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Etiketler", + "create_label": "Yeni etiket oluştur", + "mv_tasks": "Görevler", + "mv_side_panel": "Yan panel", + "mv_open_full": "Tam görünümde aç", + "mv_previous_day": "Önceki gün", + "mv_next_day": "Sonraki gün", + "mv_add_another_account": "Başka bir hesap ekle", + "mv_manage_account": "Hesabınızı yönetin", + "mv_storage_used": "{total} alanın %{percent} kadarı kullanıldı" + }, + "protocol_handlers": { + "title": "Varsayılan uygulamalar", + "description": "E-posta ve takvim bağlantılarının Bulwark'ta açılıp açılmayacağını seçin. Teknik olarak Bulwark, mailto: ve webcal: bağlantıları için protokol işleyicisi olarak kaydolur.", + "unsupported": "Bu tarayıcı veya bağlantı manuel protokol işleyicisi kaydını desteklemiyor. Yüklü PWA'yı yine de tarayıcı veya işletim sistemi ayarlarından kullanabilirsiniz.", + "mailto_label": "E-posta bağlantıları", + "mailto_description": "mailto: bağlantılarını Bulwark'ta önceden doldurulmuş düzenleyiciyle açın.", + "protocol_open_mode_label": "Protokol bağlantıları açılırken", + "protocol_open_mode_description": "Bulwark'ın mailto: ve webcal: bağlantılarını yeni bir sekmede açmasını mı yoksa açık bir oturumu yeniden kullanmasını mı istediğinizi seçin. Etkin oturum seçeneği, tarayıcı odağı engellerse Bulwark'ı öne getirmek için yedek bildirime tıklayabilmeniz amacıyla bildirim izni gerektirir.", + "protocol_open_mode_active_session": "Mümkünse etkin oturumda aç", + "protocol_open_mode_new_tab": "Her zaman yeni sekme aç", + "focus_notification_title": "Bulwark'ı aç", + "focus_notification_body": "Bağlantı Bulwark'ta açıldı. Pencereyi öne getirmek için tıklayın.", + "webcal_label": "Takvim bağlantıları", + "webcal_description": "webcal: bağlantılarını Bulwark'ta önceden doldurulmuş takvim aboneliği penceresiyle açın.", + "register_mailto": "E-posta uygulaması olarak kaydet", + "register_webcal": "Takvim uygulaması olarak kaydet", + "mailto_registered": "E-posta işleyicisi kaydı istendi", + "webcal_registered": "Takvim işleyicisi kaydı istendi", + "registration_failed": "Protokol işleyicisi kaydı başarısız oldu", + "opening_mailto": "Düzenleyici açılıyor...", + "opening_webcal": "Takvim açılıyor...", + "browser_note": "Tarayıcınız veya işletim sisteminiz bunu onaylamanızı isteyebilir ve Bulwark'ın varsayılan uygulama olarak seçilebilmesi için yüklenmiş olmasını gerektirebilir.", + "select_account_title": "Hesap seç", + "select_mailto_account": "Bu e-posta bağlantısını hangi hesabın açacağını seçin.", + "select_webcal_account": "Bu takvim bağlantısını hangi hesabın açacağını seçin.", + "select_account_note": "Bu seçim yalnızca bu protokol bağlantısı için geçerlidir.", + "detail_to": "Kime", + "detail_subject": "Konu", + "detail_no_subject": "Konu yok", + "detail_calendar": "Takvim", + "detail_source": "Kaynak", + "active_account": "Etkin", + "switching_account": "Hesap değiştiriliyor..." + }, + "sidebar_apps": { + "modal_title": "Kenar Çubuğu Uygulamaları", + "add_new": "Uygulama Ekle", + "edit_app": "Uygulamayı Düzenle", + "name_label": "Ad", + "name_placeholder": "Uygulamam", + "name_required": "Ad zorunludur", + "url_label": "URL", + "url_required": "URL zorunludur", + "url_invalid": "Geçerli bir http veya https URL girin", + "icon_label": "Simge", + "icon_required": "Simge zorunludur", + "open_mode_label": "Açılış Modu", + "open_new_tab": "Yeni Sekme", + "open_inline": "Yerleşik", + "cancel": "İptal", + "add": "Ekle", + "update": "Güncelle", + "delete": "Sil", + "delete_confirm_title": "Uygulamayı Sil", + "delete_confirm": "\"{name}\" uygulamasını silmek istediğinizden emin misiniz?", + "no_apps": "Henüz uygulama eklenmedi", + "no_apps_hint": "Kenar çubuğunuza özel uygulamalar ve bağlantılar ekleyin", + "managed_title": "Yöneticiniz tarafından sağlandı", + "managed_description": "Bu uygulamalar herkes için ayarlanmıştır ve değiştirilemez.", + "managed_badge": "Yönetilen", + "search_icons": "Simge ara...", + "show_popular": "Popüler", + "show_all": "Tümü", + "no_icons_found": "Simge bulunamadı", + "inline_badge": "Yerleşik", + "tab_badge": "Sekme", + "show_on_mobile": "Mobilde Göster" + }, + "email_list": { + "columns": { + "from": "Kimden", + "to": "Kime", + "subject": "Konu", + "date": "Tarih", + "size": "Boyut" + }, + "no_emails": "Mesaj bulunamadı", + "no_emails_description": "Bu posta kutusu boş", + "no_search_results": "Sonuç bulunamadı", + "no_search_results_description": "Arama teriminizi veya filtrelerinizi değiştirmeyi deneyin", + "loading": "E-postalar yükleniyor...", + "unread": "okunmamış", + "to_me": "Bana", + "to_recipients": "{count} alıcıya", + "and_others": "ve {count} diğer", + "draft": "Taslak", + "starred": "Yıldızlı", + "pinned": "Sabitlendi", + "replied": "Yanıtlandı", + "forwarded": "İletildi", + "has_attachment": "Ek içeriyor", + "conversations_range": "{total, number} içinde {from}–{to, number}", + "conversations_count": "{total} konuşmadan {count} tanesi", + "conversations_count_plus": "{count}+ konuşma", + "conversations_count_simple": "{count} konuşma", + "no_conversations": "Konuşma yok", + "loading_more": "Daha fazla e-posta yükleniyor...", + "no_more_emails": "Yüklenecek başka e-posta yok", + "batch_actions": { + "select": "E-postaları seç", + "select_all": "Tümünü seç", + "selected_messages": "{count, plural, one {1 e-posta} other {# e-posta}} seçildi", + "mark_read": "Okundu olarak işaretle", + "mark_unread": "Okunmadı olarak işaretle", + "delete": "Sil", + "delete_confirm_title": "E-postaları sil", + "delete_confirm_message": "{count, plural, one {1 e-postayı} other {# e-postayı}} silmek istediğinizden emin misiniz?", + "clear_selection": "Seçimi temizle" + }, + "permanent_delete": "Kalıcı olarak sil", + "permanent_delete_confirm_title": "Kalıcı olarak sil", + "permanent_delete_confirm_message": "Bu e-posta kalıcı olarak silinecek. Bu işlem geri alınamaz.", + "permanent_delete_confirm_batch_message": "Bu {count, plural, one {1 e-posta} other {# e-posta}} kalıcı olarak silinecek. Bu işlem geri alınamaz.", + "empty_folder": { + "button": "Klasörü boşalt", + "confirm_title": "Klasörü boşalt", + "confirm_message": "Bu klasördeki tüm e-postalar kalıcı olarak silinecek. Bu işlem geri alınamaz.", + "confirm_message_trash": "Bu klasördeki tüm e-postalar çöp kutusuna taşınacak.", + "confirm_button": "Klasörü boşalt", + "junk_hint": "Tüm iletileri kalıcı olarak kaldırmak için Önemsiz klasörünü boşaltabilirsiniz.", + "trash_hint": "Tüm iletileri kalıcı olarak kaldırmak için Çöp Kutusu klasörünü boşaltabilirsiniz." + }, + "no_scheduled_emails": "Zamanlanmış e-posta yok", + "no_scheduled_emails_description": "Daha sonra gönderilecek iletiler burada görünür.", + "scheduled_count": "{count, plural, one {1 zamanlanmış e-posta} other {# zamanlanmış e-posta}}", + "scheduled_actions_hint": "Zamanlanmış iletiler zamanlama işlemlerinden iptal edilebilir, yeniden zamanlanabilir veya düzenlenebilir.", + "cancel_scheduled_send": "Göndermeyi iptal et", + "reschedule_send": "Yeniden zamanla", + "cancel_and_edit": "İptal et ve düzenle", + "cancel_and_compose_again": "İptal et ve yeniden yaz", + "reschedule_prompt": "Yeni tarih/saat girin, ör. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Gönderen bir okundu bilgisi istiyor:", + "send": "Bilgi gönder", + "ignore": "Yoksay", + "sent": "Okundu bilgisi gönderildi.", + "send_failed": "Okundu bilgisi gönderilemedi", + "mdn_subject": "Okundu: {subject}", + "mdn_body": "Bu, {recipient} adresine gönderdiğiniz iletinin okundu bilgisidir.\n\nNot: Bu bilgi yalnızca iletinin alıcının bilgisayarında görüntülendiğini belirtir. Alıcının içeriği okuduğunu veya anladığını garanti etmez." + }, + "no_email_selected": "E-posta seçilmedi", + "no_email_description": "Burada görüntülemek için listeden bir e-posta seçin", + "no_conversation_selected": "Konuşma seçilmedi", + "no_conversation_description": "Okumak için listeden bir konuşma seçin", + "compose": "Yeni E-posta", + "compose_hint": "Yeni e-posta oluştur", + "no_subject": "(Konu Yok)", + "no_body_content": "(İçerik yok)", + "no_preview_available": "Önizleme kullanılamıyor", + "show_quoted_text": "Alıntılanan metni göster", + "hide_quoted_text": "Alıntılanan metni gizle", + "loading_email": "E-posta yükleniyor...", + "loading": "Yükleniyor...", + "reply": "Yanıtla", + "reply_all": "Tümünü Yanıtla", + "forward": "İlet", + "delete": "Sil", + "archive": "Arşivle", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "Yıldızla", + "unstar": "Yıldızı Kaldır", + "mark_unread": "Okunmadı olarak işaretle", + "mark_read": "Okundu olarak işaretle", + "unread": "Okunmadı", + "read": "Okundu", + "spam_short": "Spam", + "not_spam_short": "Spam değil", + "move": "Taşı", + "print": "Yazdır", + "view_source": "Kaynağı görüntüle", + "fullscreen": "Tam ekran", + "exit_fullscreen": "Tam ekrandan çık", + "view_in_dark_mode": "Koyu modda görüntüle", + "view_in_light_mode": "Açık modda görüntüle", + "export_email": ".eml olarak dışa aktar", + "forward_as_attachment": "Ek olarak ilet", + "import_email": ".eml veya .zip içe aktar", + "keyboard_shortcuts": "Klavye kısayolları (?)", + "email_source": "E-posta Kaynağı", + "draft_banner": "Bu ileti bir taslaktır", + "edit_draft": "Düzenle", + "copy_source": "Panoya kopyala", + "source_copied": "Kaynak panoya kopyalandı", + "attachments": "Ekler", + "important": "Önemli", + "download": "İndir", + "download_all": "Tümünü indir", + "from": "Kimden", + "to": "Kime", + "cc": "CC", + "bcc": "BCC", + "date": "Tarih", + "subject": "Konu", + "show_details": "Ayrıntıları göster", + "hide_details": "Ayrıntıları gizle", + "external_content_warning": "Görseller ve harici içerik engellendi", + "load_external_content": "Görselleri yükle", + "trust_sender": "Bu gönderene her zaman güven", + "sender_check": { + "unverified_label": "Doğrulanmamış gönderen", + "failed_label": "Gönderen denetimi başarısız", + "unverified": "Bu ileti {domain} etki alanından geldiğini iddia ediyor, ancak bunu doğrulayan hiçbir şey yok. {domain} etki alanından geçerli bir imzası yok ve {domain} etki alanının yetkilendirdiği bir sunucudan gelmedi.", + "unverified_sent_from": "Bu ileti {domain} etki alanından geldiğini iddia ediyor, ancak {host} etki alanından gönderildi ve {domain} etki alanından geçerli bir imzası yok.", + "failed": "Bu ileti {domain} etki alanından geldiğini iddia ediyor, ancak bu etki alanının gönderen denetimlerinden geçemedi. Sahte olabilir.", + "failed_sent_from": "Bu ileti {domain} etki alanından geldiğini iddia ediyor, ancak {host} etki alanından gönderildi ve {domain} etki alanının gönderen denetimlerinden geçemedi. Sahte olabilir.", + "caution": "Bağlantılara, eklere ve parola ya da ödeme bilgisi isteklerine karşı dikkatli olun." + }, + "back_to_list": "Listeye dön", + "view_contact": "Kişiyi görüntüle", + "message_details": "İleti Ayrıntıları", + "more_reply_options": "Daha fazla yanıt seçeneği", + "set_tag": "Etiket ayarla", + "tag": "Etiket", + "more_actions": "Diğer işlemler", + "previous": "Önceki", + "next": "Sonraki", + "move_to": "Şuraya taşı...", + "remove_tag": "Etiketi kaldır", + "tag_filter_placeholder": "Etiketleri filtrele", + "tag_no_matches": "Eşleşen etiket yok", + "more_count": "+{count} daha", + "characters_count": "{count} karakter", + "quick_reply_placeholder": "Hızlı yanıt yazın...", + "more_options": "Daha fazla seçenek", + "sending": "Gönderiliyor...", + "security_authentication": "Güvenlik ve Kimlik Doğrulama", + "technical_details": "Teknik Ayrıntılar", + "message_id_label": "İleti Kimliği:", + "reply_to_label": "Yanıt Adresi:", + "delivery_time_label": "Teslim süresi:", + "conversation_part_label": "Konuşmanın parçası:", + "previous_messages": "{count} önceki ileti", + "previous_messages_plural": "{count} önceki ileti", + "time": { + "day": "gün", + "days": "gün", + "hour": "saat", + "hours": "saat", + "minute": "dakika", + "minutes": "dakika" + }, + "unknown_sender": "Bilinmeyen", + "recipient_me": "ben", + "recipient_and_others": "{name} ve {count} diğer", + "recipient_to_prefix": "Kime:", + "authentication": { + "title": "Kimlik Doğrulama", + "status": { + "verified": "Doğrulandı", + "warning": "Uyarı", + "none": "Doğrulanmadı" + }, + "spf": { + "pass": "SPF Geçti", + "fail": "SPF Başarısız", + "none": "SPF Yok" + }, + "dkim": { + "pass": "DKIM Geçerli", + "fail": "DKIM Geçersiz", + "none": "DKIM Yok" + }, + "dmarc": { + "pass": "DMARC Geçti", + "fail": "DMARC Başarısız", + "none": "DMARC Yok" + }, + "spam_score": "Spam Puanı", + "tooltip_spf": "Sender Policy Framework: gönderen sunucunun, etki alanı adına e-posta gönderme yetkisine sahip olduğunu doğrular", + "tooltip_dkim": "DomainKeys Identified Mail: kriptografik bir imza kullanarak e-postanın iletim sırasında değiştirilmediğini doğrular", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: SPF ve DKIM'in göndericinin etki alanıyla uyumlu olmasını sağlar ve hatalar için bir ilke belirler", + "policy": "İlke", + "result": { + "pass": "Başarılı", + "fail": "Başarısız", + "softfail": "Hafif başarısızlık", + "neutral": "Nötr", + "permerror": "Kalıcı hata", + "temperror": "Geçici hata", + "none": "Yok" + } + }, + "headers": { + "routing": "Yönlendirme", + "received": "Alındı", + "message_id": "İleti Kimliği", + "list_info": "Liste Bilgisi" + }, + "tooltips": { + "reply": "Yanıtla (r)", + "reply_all": "Tümünü Yanıtla (a)", + "forward": "İlet (f)", + "archive": "Arşivle (e)", + "delete": "Sil (# veya Del)", + "star": "Yıldızla (s)", + "unstar": "Yıldızı Kaldır (s)", + "compose": "Yeni E-posta (c)", + "previous": "Önceki e-posta", + "next": "Sonraki e-posta", + "edit_draft": "Taslağı düzenle" + }, + "spam": { + "button_title": "Spam olarak bildir", + "not_spam_title": "Meşru olarak işaretle", + "toast_success": "Önemsiz'e taşındı", + "toast_batch": "{count} e-posta Önemsiz'e taşındı", + "toast_undo": "Geri al", + "toast_not_spam_success": "Gelen Kutusu'na taşındı", + "toast_not_spam_batch": "{count} e-posta Gelen Kutusu'na taşındı", + "error": "Spam bildirimi başarısız", + "error_not_spam": "E-posta geri yüklenemedi" + }, + "unsubscribe_banner": { + "label": "Bülten", + "button": "Aboneliği iptal et", + "confirm_title": "Bu göndericinin aboneliği iptal edilsin mi?", + "confirm_button": "Onayla", + "cancel": "İptal", + "success_http": "Abonelik iptali sayfası yeni sekmede açıldı", + "success_mailto": "Abonelikten çıkma e-postası gönderildi", + "confirm_message_http": "Abonelikten çıkma sayfası yeni sekmede açılacak.", + "confirm_message_mailto": "Gönderene abonelikten çıkma e-postası gönderilecek.", + "error": "Abonelik iptal edilemedi", + "dismiss": "Kapat" + }, + "calendar_invitation": { + "loading": "Etkinlik ayrıntıları yükleniyor…", + "title": "Takvim Daveti", + "published_title": "Yayımlanmış Etkinlik", + "response_title": "Etkinlik Yanıtı", + "update_title": "Etkinlik Güncellemesi", + "counter_title": "Karşı Teklif", + "refresh_title": "Yenileme İsteği", + "declined_counter_title": "Karşı Teklif Reddedildi", + "cancelled_title": "Etkinlik İptal Edildi", + "organizer": "{name} tarafından düzenleniyor", + "organizer_label": "Düzenleyen:", + "attendees": "{count, plural, one {# katılımcı} other {# katılımcı}}", + "accept": "Kabul et", + "maybe": "Belki", + "decline": "Reddet", + "add_to_calendar": "Takvime ekle", + "added": "Takvime eklendi", + "rsvp_sent": "Yanıt gönderildi", + "parse_error": "Davet okunamadı", + "action_failed": "Takvim işlemi tamamlanamadı.", + "no_calendar": "Takvim mevcut değil", + "published_info": "Bu etkinlik referans olarak paylaşıldı.", + "response_info": "Bu ileti bir katılımcı yanıtı içeriyor.", + "response_info_organizer": "Bu katılımcı yanıtı etkinliğinizi güncelliyor.", + "update_info": "Bu ileti mevcut bir etkinliği güncelliyor.", + "counter_info": "Bu ileti bir etkinlikte değişiklik öneriyor.", + "counter_info_organizer": "Bu katılımcı etkinliğinizde değişiklik önerdi.", + "refresh_info": "Bu ileti en güncel etkinlik ayrıntılarını istiyor.", + "refresh_info_organizer": "Bir katılımcı en güncel etkinlik ayrıntılarını talep etti.", + "declined_counter_info": "Organizatör karşı teklifi reddetti.", + "authentication_failed_info": "Bu davet için posta kimlik doğrulama denetimleri başarısız oldu. Takvim işlemlerini dikkatli gerçekleştirin.", + "authentication_missing_info": "Bu davet doğrulanmış posta kimlik doğrulaması içermiyor. Bir şey olağandışı görünüyorsa ayrıntıları organizatörle teyit edin.", + "sender_mismatch_info": "Bu davet {sender} tarafından gönderildi; ancak takvim verisinde organizatör olarak {organizer} listeleniyor.", + "sender_mismatch_unverified_info": "Bu davet {sender} tarafından gönderildi; ancak takvim verisinde organizatör olarak {organizer} listeleniyor ve ileti doğrulanamadı.", + "organizer_role": "Bu etkinliği siz düzenliyorsunuz", + "your_response": "Yanıtınız: {status}", + "response_needed": "Yanıt gerekiyor", + "response_accepted": "Kabul edildi", + "response_tentative": "Belirsiz", + "response_declined": "Reddedildi", + "response_delegated": "Devredildi", + "actor_sent_info": "{name} tarafından gönderildi.", + "actor_response_info": "{name}, {status} yanıtını verdi.", + "actor_counter_info": "{name} bu etkinlikte değişiklik önerdi.", + "actor_refresh_info": "{name} en güncel etkinlik ayrıntılarını istedi.", + "actor_declined_counter_info": "{name} karşı teklifi reddetti.", + "actor_note": "Not: {comment}", + "actor_unknown": "Biri", + "proposed_changes": "Önerilen değişiklikler", + "change_title": "Başlık", + "change_time": "Saat", + "change_location": "Konum", + "change_description": "Açıklama", + "change_empty": "Yok", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Önerilen değişiklikleri uygula", + "proposal_applied": "Önerilen değişiklikler uygulandı.", + "review_proposal": "Teklifi incele", + "review_request": "İsteği incele", + "view_in_calendar": "Takvimde görüntüle", + "select_calendar": "Takvim seç", + "already_in_calendar": "Zaten takviminizde", + "request_info": "Bu etkinliğe davet edildiniz. Müsaitliğinizi bildirmek için yanıt verin.", + "cancel_info": "Organizatör bu etkinliği iptal etti.", + "event_updated": "Güncelleme #{sequence}", + "event_status_tentative": "Belirsiz", + "event_status_cancelled": "İptal edildi", + "expand": "Ayrıntıları göster", + "collapse": "Ayrıntıları gizle" + }, + "send": "Gönder", + "more": "daha fazla", + "details": { + "recipients_routing": "Alıcılar ve yönlendirme", + "authentication_security": "Kimlik doğrulama ve güvenlik", + "identifiers_threading": "Tanımlayıcılar ve konu zinciri", + "mailing_list": "Posta listesi", + "message_properties": "İleti özellikleri", + "sent": "Gönderildi", + "received": "Alındı", + "delivery_time": "Teslim süresi", + "in_reply_to": "Yanıtlanan", + "references": "Referanslar", + "thread_id": "Konu zinciri kimliği", + "size": "Boyut", + "mime_type": "MIME türü", + "attachments_summary": "{count} dosya · {size}", + "list_id": "Liste kimliği", + "list_help": "Liste yardımı", + "list_post": "Liste gönderimi", + "list_unsubscribe": "Abonelikten çık", + "iprev": "Ters DNS", + "spam_status": "Spam durumu", + "ai_verdict": "Yapay zeka kararı", + "account": "Hesap", + "no_subject": "(konu yok)" + }, + "scheduled_banner": "{date} tarihinde gönderilmek üzere zamanlandı", + "scheduled_send_created": "E-posta gönderim için zamanlandı", + "sending_in": "{seconds} sn içinde gönderilecek", + "undo_send_failed": "Geri alınamadı. İleti zaten gönderilmiş olabilir.", + "cancel_scheduled_send": "Göndermeyi iptal et", + "reschedule_send": "Yeniden zamanla", + "cancel_and_edit": "İptal et ve düzenle", + "cancel_and_compose_again": "İptal et ve yeniden yaz", + "reschedule_prompt": "Yeni tarih/saat girin, ör. 2026-05-04T15:30", + "scheduled_actions_only": "Zamanlandı görünümü yalnızca zamanlama işlemlerini destekler", + "undo_send_scheduled": "İleti gönderim için zamanlandı", + "undo_send": "Göndermeyi geri al", + "contact_sidebar": { + "title": "Kişi", + "close": "Kenar çubuğunu kapat", + "action_email": "E-posta", + "action_email_title": "E-posta gönder", + "action_copy": "Kopyala", + "action_copy_title": "E-postayı kopyala", + "action_edit_title": "Kişiyi düzenle", + "section_emails": "E-postalar", + "section_phones": "Telefonlar", + "section_organizations": "Kuruluşlar", + "section_addresses": "Adresler", + "section_notes": "Notlar", + "not_in_contacts": "Kişilerinizde yok", + "add_to_contacts": "Kişilere ekle", + "copied": "Kopyalandı!", + "copy_failed": "Kopyalanamadı", + "view_contact": "Kişiyi görüntüle", + "view_details": "Ayrıntıları görüntüle" + }, + "send_now": "Şimdi gönder", + "verification_code": { + "copy": "{code} kodunu kopyala", + "copied": "Kod kopyalandı", + "copy_failed": "Kod kopyalanamadı" + }, + "mv_position": "{index} / {total}", + "plugin_panel_show": "Eklenti panelini göster", + "plugin_panel_hide": "Eklenti panelini gizle" + }, + "email_composer": { + "select_from_directory": "Dizinden seç", + "minimize": "Simge durumuna küçült", + "maximize": "Tam ekran", + "restore": "Geri yükle", + "new_message": "Yeni İleti", + "signature_edit_hint": "İmzayı düzenlemek için çift tıklayın", + "reply": "Yanıtla", + "reply_all": "Tümünü Yanıtla", + "forward": "İlet", + "reply_to": "Yanıtla", + "reply_all_to": "Tümünü Yanıtla", + "forward_message": "İlet", + "from": "Kimden", + "to": "Kime", + "cc": "CC", + "bcc": "BCC", + "subject": "Konu", + "body_placeholder": "İletinizi yazın...", + "send": "Gönder", + "cancel": "İptal", + "attach": "Ekle", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Dosyalar'dan ekle", + "files_picker": { + "attach": "{count, plural, =0 {Ekle} one {1 dosya ekle} other {# dosya ekle}}", + "empty_folder": "Bu klasör boş", + "load_failed": "Dosyalarınız yüklenemedi" + }, + "discard": "Vazgeç", + "discard_draft_title": "Taslaktan vazgeçilsin mi?", + "discard_draft_confirm": "Kaydedilmemiş değişiklikleriniz var. Bu taslaktan vazgeçmek istiyor musunuz?", + "discard_draft_message": "Taslak silinecek.", + "saving": "Kaydediliyor...", + "sending": "Gönderiliyor...", + "add_link": "Bağlantı ekle", + "link_url_prompt": "URL'yi girin", + "draft_saved": "Taslak kaydedildi", + "save_failed": "Kaydedilemedi", + "to_placeholder": "Alıcı e-posta adresleri", + "cc_placeholder": "Cc alıcıları", + "bcc_placeholder": "Bcc alıcıları", + "subject_placeholder": "Konu", + "cc_label": "Cc:", + "bcc_label": "Bcc:", + "subject_label": "Konu:", + "file_size_kb": "KB", + "prefix": { + "forward": "İlt:", + "reply": "Yn:" + }, + "forward_batch_count": "{count, plural, one {1 e-posta} other {# e-posta}}", + "no_subject": "(Konu Yok)", + "unknown_sender": "Bilinmeyen", + "quote": { + "reply_header": "{date} tarihinde {sender} yazdı:", + "forward_header": "---------- İletilen ileti ----------", + "from": "Kimden: {sender}", + "date": "Tarih: {date}", + "subject": "Konu: {subject}", + "to": "Kime: {recipients}" + }, + "remove_sub_address": "Alt adresi kaldır", + "from_override": { + "toggle_off": "Geçersiz kıl", + "toggle_on": "Geçersiz kılmayı iptal et", + "toggle_tooltip": "Gönderen adını ve adresini serbestçe düzenleyin. Posta hâlâ kimliğiniz üzerinden gönderilir.", + "name_label": "Gönderen adı", + "name_placeholder": "Ad", + "email_label": "Gönderen e-posta adresi", + "email_placeholder": "alias@example.com", + "envelope_notice": "Sunucunuz bu adresi zarf göndereni olarak kabul etmezse yerine {identity} kullanılır ve alıcılar bunu Return-Path başlığında görebilir." + }, + "save_as_template": "Şablon Olarak Kaydet", + "attach_from_device": "Bu cihazdan yükle", + "templates_menu": "Şablonlar", + "insert_template": "Şablon ekle", + "options": "Seçenekler", + "option_plain_text": "Düz metin", + "option_plain_text_hint": "Biçimlendirmeden gönder", + "option_read_receipt": "Okundu bilgisi iste", + "option_read_receipt_hint": "Alıcıdan açtığını onaylamasını iste", + "option_dsn": "Teslim bildirimi", + "option_dsn_hint": "Sunucu teslim ettiğinde rapor al", + "option_require_tls": "Şifreli teslimat zorunlu", + "option_require_tls_hint": "Yalnızca TLS ile teslim et, yoksa geri çevir", + "option_priority": "Öncelik", + "option_priority_high": "Yüksek", + "option_priority_normal": "Normal", + "option_priority_low": "Düşük", + "validation": { + "recipient_required": "Göndermek için bir alıcı ekleyin", + "body_required": "Bir ileti yazın veya dosya ekleyin", + "invalid_recipients": "Gönderilmedi - şu adresleri kontrol edin: {addresses}", + "attachments_uploading": "Ekler hâlâ yükleniyor — yükleme bitince gönderilecek", + "attachment_upload_failed": "Gönderilmedi - bir ek yüklenemedi. Eki kaldırıp tekrar deneyin." + }, + "upload_progress": "{uploaded} / {total} yükleniyor", + "upload_cancel": "Yüklemeyi iptal et", + "upload_failed": "{filename} yüklenemedi", + "attachment_too_large": "\"{name}\" sunucunun izin verdiğinden büyük (dosya başına {max})", + "attachments_total_too_large": "Ekler sunucunun ileti başına {max} sınırını aşar", + "drop_files": "Eklemek için dosyaları buraya bırakın", + "show_less": "Daha az göster", + "send_failed": "E-posta gönderilemedi", + "send_timeout": "Sunucu yanıt vermeyi bıraktı. Yeniden göndermeden önce Gönderilenler klasörünü kontrol edin — ileti gönderilmiş olabilir.", + "send_offline": "Çevrimdışı görünüyorsunuz. İleti hâlâ burada; yeniden çevrimiçi olduğunuzda tekrar gönderin.", + "formatting": "Biçimlendirme", + "send_recipients_rejected": "Gönderilmedi - sunucu tüm alıcıları reddetti.", + "send_plugin_failed": "Gönderilmedi - gönderimi üstlenen bir eklenti başarısız oldu ya da çok uzun sürdü. Yeniden göndermeden önce Gönderilenler klasörünü kontrol edin.", + "send_some_recipients_rejected": "Gönderildi, ancak bu alıcılara değil - sunucu onları reddetti.", + "continue_draft": "Taslağa devam et", + "close_draft_title": "Taslak kaydedilsin mi yoksa silinsin mi?", + "close_draft_message": "Kaydedilmemiş değişiklikleriniz var. Bunu taslak olarak kaydetmek ister misiniz yoksa vazgeçmek mi?", + "smime_sign_on": "S/MIME imzalama etkin", + "smime_sign_off": "S/MIME imzalamayı etkinleştir", + "smime_encrypt_on": "S/MIME şifreleme etkin", + "smime_encrypt_off": "S/MIME şifrelemeyi etkinleştir", + "smime_encrypt_unavailable": "S/MIME şifreleme kullanılamıyor – alıcı sertifikaları eksik", + "smime_unlock_title": "S/MIME Anahtarının Kilidini Aç", + "smime_unlock_message": "S/MIME imzalama anahtarınızın kilidini açmak için parolayı girin.", + "smime_unlock_button": "Kilidi Aç", + "smime_passphrase_placeholder": "Parola", + "forgot_attachment": { + "title": "Ek eklemeyi unuttunuz mu?", + "message": "İletinizde \"{keyword}\" geçiyor ancak hiçbir dosya eklenmemiş. Yine de gönderilsin mi?", + "send_anyway": "Yine de gönder", + "back": "Düzenlemeye dön" + }, + "empty_subject": { + "title": "Konu olmadan gönderilsin mi?", + "message": "Bu iletinin konusu yok. Yine de gönderilsin mi?", + "dont_ask_again": "Bir daha sorma", + "send_anyway": "Yine de gönder", + "back": "Düzenlemeye dön" + }, + "schedule_send": "Göndermeyi zamanla", + "schedule_send_description": "Sunucunun bu iletiyi ne zaman göndereceğini seçin.", + "schedule_preset": { + "tomorrow_morning": "Yarın sabah", + "tomorrow_afternoon": "Yarın öğleden sonra", + "monday_morning": "Pazartesi sabah" + }, + "schedule_send_required": "Bir tarih ve saat seçin.", + "schedule_send_invalid": "Geçerli bir tarih ve saat girin.", + "schedule_send_future": "Gelecekte bir tarih ve saat seçin.", + "schedule_send_too_late": "Bu zaman sunucunun izin verdiğinden daha geç.", + "schedule_send_unsupported": "Bu hesap için zamanlanmış gönderim desteklenmiyor.", + "schedule_send_cleanup_warning": "Zamanlanmış gönderim oluşturuldu, ancak taslak temizliği başarısız oldu.", + "send_delay_unsupported": "Bu hesap için gönderim gecikmesi desteklenmiyor.", + "send_delay_unsupported_confirm": "Bu hesap gönderim gecikmesini desteklemiyor. Hemen gönderilsin mi?", + "recipient_edit_email": "E-posta adresini düzenle", + "recipient_edit_name": "Görünen adı düzenle", + "recipient_email_placeholder": "E-posta adresi", + "recipient_name_placeholder": "Görünen ad", + "invalid_recipient": "Geçerli bir e-posta adresi değil", + "autocomplete_search_server": "Sunucuda ara", + "autocomplete_searching": "Aranıyor...", + "mention_recipients": "Alıcılar", + "mention_card_not_recipient": "Kime veya CC alanında değil, bu yüzden iletiyi almayacak.", + "mention_card_add": "{field} alanına ekle", + "mention_card_remove": "Bahsetmeyi kaldır", + "toolbar": { + "bold": "Kalın", + "italic": "İtalik", + "underline": "Altı çizili", + "strikethrough": "Üstü çizili", + "text_color": "Metin rengi", + "remove_color": "Rengi kaldır", + "heading_1": "Başlık 1", + "heading_2": "Başlık 2", + "bullet_list": "Madde işaretli liste", + "ordered_list": "Numaralı liste", + "quote": "Alıntı", + "code_block": "Kod bloğu", + "align_left": "Sola hizala", + "align_center": "Ortala", + "align_right": "Sağa hizala", + "text_direction": "Metin yönü (RTL/LTR)", + "link": "Bağlantı", + "table": "Tablo", + "clear_formatting": "Biçimlendirmeyi temizle", + "undo": "Geri al", + "redo": "Yinele", + "add_row_above": "Üste satır ekle", + "add_row_below": "Alta satır ekle", + "add_column_before": "Öncesine sütun ekle", + "add_column_after": "Sonrasına sütun ekle", + "delete_row": "Satırı sil", + "delete_column": "Sütunu sil", + "toggle_header_row": "Başlık satırını aç/kapat", + "delete_table": "Tabloyu sil", + "pick_size": "Boyut seç", + "font_size": "Yazı boyutu", + "font_size_default": "Varsayılan", + "background_color": "Arka plan rengi", + "remove_background_color": "Arka plan rengini kaldır" + }, + "send_filing_warning": "Gönderildi - ancak sonrasındaki temizleme başarısız oldu, eski bir taslak kalabilir.", + "pop_out": "Yanıtı ayrı pencerede aç", + "send_guard_failed": "Gönderilmedi - iletileri göndermeden önce denetleyen bir eklenti başarısız oldu veya yanıt vermedi." + }, + "confirm_dialog": { + "confirm": "Onayla", + "cancel": "İptal" + }, + "common": { + "loading": "Yükleniyor...", + "error": "Hata", + "success": "Başarılı", + "cancel": "İptal", + "save": "Kaydet", + "delete": "Sil", + "edit": "Düzenle", + "close": "Kapat", + "search": "Ara", + "refresh": "Yenile", + "settings": "Ayarlar", + "help": "Yardım", + "logout": "Çıkış yap", + "yes": "Evet", + "no": "Hayır", + "unknown": "Bilinmeyen", + "app_title": "Webmail", + "reconnecting": "Bağlantı kesildi. Yeniden bağlanılmaya çalışılıyor…", + "rate_limited_title": "Sunucu kimlik doğrulaması geçici olarak hız sınırlamasına tabi.", + "rate_limited_detail": "Hesap kilitlenmesini önlemek için Bulwark arka plan isteklerini duraklattı. {seconds} saniye içinde yeniden deneniyor.", + "rate_limited_action_title": "Kilitlenmeyi önlemek için istek duraklatıldı.", + "rate_limited_action_detail": "Bulwark, daha fazla kimlik doğrulama isteği göndermeden önce sunucunun soğuma süresinin bitmesini bekliyor. {seconds} saniye içinde tekrar deneyin.", + "pull_to_refresh": "Yenilemek için çekin", + "release_to_refresh": "Yenilemek için bırakın", + "refreshing": "Yenileniyor…" + }, + "notifications": { + "email_sent": "E-posta başarıyla gönderildi", + "email_deleted": "E-posta silindi", + "email_archived": "E-posta arşivlendi", + "emails_deleted": "{count, plural, other {# e-posta silindi}}", + "emails_archived": "{count, plural, other {# e-posta arşivlendi}}", + "error_archiving": "E-posta arşivlenemedi", + "error_updating": "E-posta güncellenemedi", + "email_starred": "E-posta yıldızlandı", + "email_unstarred": "E-postanın yıldızı kaldırıldı", + "email_marked_read": "E-posta okundu olarak işaretlendi", + "email_marked_unread": "E-posta okunmadı olarak işaretlendi", + "copied_to_clipboard": "Panoya kopyalandı", + "source_copied": "Kaynak panoya kopyalandı", + "error_sending": "E-posta gönderilemedi", + "error_deleting": "E-posta silinemedi", + "error_loading": "E-postalar yüklenemedi", + "new_email": "Yeni e-posta", + "new_email_from": "{sender} tarafından", + "click_to_view": "Görüntülemek için tıklayın", + "email_moved": "E-posta taşındı", + "emails_moved": "{count} e-posta taşındı", + "moved_to_mailbox": "{mailbox} klasörüne taşındı", + "move_failed": "Taşıma başarısız", + "removed_from_folder": "{mailbox} klasöründen kaldırıldı", + "remove_from_folder_failed": "Bu klasörden kaldırılamadı", + "emails_copied": "{count} e-posta kopyalandı", + "copy_failed": "Kopyalama başarısız", + "move_error": "E-postalar seçilen klasöre taşınamadı", + "email_tagged": "E-posta etiketlendi", + "emails_tagged": "{count} e-posta etiketlendi", + "tag_failed": "Etiketleme başarısız", + "identity_created": "Kimlik başarıyla oluşturuldu", + "identity_updated": "Kimlik başarıyla güncellendi", + "identity_deleted": "Kimlik silindi", + "identity_set_primary": "Birincil kimlik güncellendi", + "identity_create_failed": "Kimlik oluşturulamadı: {error}", + "identity_update_failed": "Kimlik güncellenemedi: {error}", + "identity_delete_failed": "Kimlik silinemedi: {error}", + "identity_unauthorized": "Bu e-posta adresinden gönderme yetkiniz yok", + "identity_not_found": "Kimlik bulunamadı", + "vacation_saved": "Ofis dışında ayarları kaydedildi", + "vacation_save_failed": "Ofis dışında ayarları kaydedilemedi", + "filters_saved": "Filtreler başarıyla kaydedildi", + "filters_save_failed": "Filtreler kaydedilemedi", + "filters_deleted": "Filtre kuralı silindi", + "templates_exported": "Şablonlar başarıyla dışa aktarıldı", + "templates_imported": "{count, plural, one {# şablon} other {# şablon}} içe aktarıldı", + "templates_import_errors": "Bazı şablonlar içe aktarılamadı", + "templates_import_empty": "Dosyada şablon bulunamadı", + "export_email_error": "E-posta dışa aktarılamadı", + "import_email_success": "E-posta başarıyla içe aktarıldı", + "import_email_error": "E-posta içe aktarılamadı", + "rule_created": "Kural oluşturuldu", + "rule_merged": "“{name}” kuralına eklendi", + "rule_already_covered": "Zaten “{name}” kuralı kapsıyor", + "rule_apply_existing": "{count, plural, one {# mevcut iletiye uygula} other {# mevcut iletiye uygula}}", + "rule_applied": "{count, plural, =0 {Hiçbir iletinin değişmesi gerekmedi} one {Kural # iletiye uygulandı} other {Kural # iletiye uygulandı}}", + "rule_apply_failed": "Kural mevcut iletilere uygulanamadı", + "rule_edit": "Kuralı düzenle", + "rule_undo": "Geri Al", + "rule_undone": "Kural değişikliği geri alındı", + "rule_undo_failed": "Kural değişikliği geri alınamadı", + "rule_undo_conflict": "Filtreleriniz o zamandan beri değişti. Değişikliği filtre ayarlarından geri alın.", + "rule_folder_failed": "Klasör oluşturulamadı", + "vacation_filters_save_failed": "Ofis dışında ayarları kaydedildi, ancak iletme ve yanıt alıcıları kaydedilemedi" + }, + "date": { + "today": "Bugün", + "yesterday": "Dün", + "this_week": "Bu hafta", + "last_week": "Geçen hafta", + "this_month": "Bu ay", + "older": "Daha eski", + "just_now": "Az önce", + "minutes_ago": "{count} dakika önce", + "minutes_ago_plural": "{count} dakika önce", + "hours_ago": "{count} saat önce", + "hours_ago_plural": "{count} saat önce", + "days_ago": "{count} gün önce", + "days_ago_plural": "{count} gün önce" + }, + "language": { + "title": "Dil", + "english": "English", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Nederlands", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Dil seçin", + "switch_to_english": "İngilizce'ye geç", + "switch_to_french": "Fransızca'ya geç", + "switch_to_japanese": "Japonca'ya geç", + "switch_to_spanish": "İspanyolca'ya geç", + "switch_to_italian": "İtalyanca'ya geç", + "switch_to_german": "Almanca'ya geç", + "switch_to_dutch": "Hollandaca'ya geç", + "switch_to_portuguese": "Portekizce'ye geç", + "switch_to_russian": "Rusça'ya geç", + "switching": "Dil değiştiriliyor...", + "switch": "Dili değiştir", + "current": "Mevcut dil", + "polish": "Polski", + "switch_to_polish": "Lehçe'ye geç", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Farsçaya geç", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Moğolcaya geç" + }, + "settings": { + "title": "Ayarlar", + "back_to_mail": "Postaya Dön", + "save_success": "Ayarlar başarıyla kaydedildi", + "import_success": "Ayarlar başarıyla içe aktarıldı", + "import_error": "Ayarlar içe aktarılamadı", + "reset_confirm": "Tüm ayarları varsayılanlara sıfırlamak istediğinizden emin misiniz?", + "unsaved_changes": "Kaydedilmemiş değişiklikleriniz var", + "discard_changes": "Kaydedilmemiş değişiklikler atılsın mı?", + "discard": "Atla", + "keep_editing": "Düzenlemeye devam et", + "tabs": { + "appearance": "Görünüm", + "language": "Dil, Bölge ve Saat", + "email": "E-posta Davranışı", + "composer": "İleti düzenleyici", + "privacy": "Gizlilik ve Güvenlik", + "account": "Hesap", + "identities": "Kimlikler", + "vacation": "Ofis Dışında", + "advanced": "Gelişmiş", + "calendar": "Takvim", + "filters": "Filtreler", + "templates": "Şablonlar", + "folders": "Klasörler", + "keywords": "Etiketler", + "security": "Güvenlik", + "files": "Dosyalar", + "contacts": "Kişiler", + "encryption": "Şifreleme", + "sidebar_apps": "Kenar Çubuğu Uygulamaları", + "protocol_handlers": "Varsayılan uygulamalar", + "notifications": "Bildirimler", + "layout": "Düzen", + "themes": "Temalar", + "reading": "Okuma", + "composing": "Yazma", + "downloads": "İndirilenler", + "content_senders": "İçerik ve Göndericiler", + "about_data": "Hakkında ve Veriler", + "debug": "Hata Ayıklama" + }, + "themes": { + "title": "Temalar", + "description": "Yöneticinizin dağıttığı temalar ve yerleşik hazır ayarlar arasından seçim yapın.", + "locked": "Tema seçimi bir yönetici tarafından kilitlendi.", + "forced": "“{name}” teması yönetici tarafından zorunlu kılınmıştır ve değiştirilemez", + "admin_theme": "Yönetici teması", + "activated": "Tema etkinleştirildi", + "default_restored": "Varsayılan tema geri yüklendi", + "default_name": "Varsayılan", + "default_badge": "Varsayılan", + "admin_enforced": "Yönetici tarafından zorunlu" + }, + "tab_groups": { + "general": "Genel", + "account": "Hesap ve Kimlik", + "organization": "Posta Düzeni", + "apps": "Uygulamalar", + "system": "Sistem", + "appearance": "Görünüm", + "mail": "Posta", + "privacy": "Gizlilik ve Güvenlik", + "advanced": "Gelişmiş" + }, + "appearance": { + "message_list_order": { + "label": "İleti listesi sıralaması", + "description": "İletilerin listede nasıl sıralandığı. Sıralamayı sunucu yapar; bu yüzden yalnızca yüklenen iletiler değil, klasörün tamamı sıralanır.", + "preset": { + "chronological": "Kronolojik (en yeni önce)", + "unread_first": "Okunmamışlar önce", + "starred_first": "Yıldızlılar önce", + "tagged_first": "Etiketliler önce", + "custom": "Özel" + }, + "tag_label": "Etiket", + "advanced_show": "Gelişmiş: sıralama düzeylerini düzenle", + "advanced_hide": "Gelişmişi gizle", + "criterion_label": "{n}. düzey ölçütü", + "direction_label": "{n}. düzey yönü", + "criteria": { + "unread": "Okunmamış", + "starred": "Yıldızlı", + "tag": "Etiket", + "receivedAt": "Alınma tarihi", + "sentAt": "Gönderilme tarihi", + "from": "Gönderen", + "subject": "Konu", + "size": "Boyut" + }, + "direction": { + "unread_first": "Okunmamışlar önce", + "read_first": "Okunmuşlar önce", + "starred_first": "Yıldızlılar önce", + "unstarred_first": "Yıldızsızlar önce", + "tagged_first": "Etiketliler önce", + "untagged_first": "Etiketsizler önce", + "newest_first": "En yeni önce", + "oldest_first": "En eski önce", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "En büyük önce", + "smallest_first": "En küçük önce" + }, + "add_level": "Düzey ekle", + "remove_level": "Düzeyi kaldır", + "move_up": "Yukarı taşı", + "move_down": "Aşağı taşı", + "tiebreak_note": "En fazla üç düzey; sonraki düzeyler eşitlikleri çözer. \"En yeni önce\" her zaman en son uygulanır.", + "scope": { + "label": "Tüm klasörlere uygula", + "description": "Kapalıyken sıralama yalnızca Gelen Kutusu için geçerlidir; diğer klasörler kronolojik kalır." + }, + "unsupported_note": "Posta sunucunuz okunma durumu, yıldız veya etikete göre sıralamayı desteklemiyor. Bu düzeyler yok sayılır ve liste kronolojik kalır." + }, + "title": "Görünüm", + "description": "Web postanızın görünümünü özelleştirin", + "theme": { + "label": "Tema", + "description": "Tercih ettiğiniz renk şemasını seçin", + "light": "Açık", + "dark": "Koyu", + "system": "Sistem" + }, + "language": { + "label": "Dil", + "description": "Tercih ettiğiniz dili seçin" + }, + "font_size": { + "label": "Yazı Tipi Boyutu", + "description": "Daha iyi okunabilirlik için metin boyutunu ayarlayın", + "small": "Küçük", + "medium": "Orta", + "large": "Büyük" + }, + "list_density": { + "label": "Yoğunluk", + "description": "Arayüz genelinde boşluk ve doldurmayı kontrol edin", + "extra_compact": "Çok Sıkışık", + "compact": "Sıkışık", + "regular": "Normal", + "comfortable": "Rahat" + }, + "interface_layout": { + "label": "Arayüz düzeni", + "description": "Arama, gezinme ve araç çubuklarının konumu", + "default": "Varsayılan", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Animasyonları Etkinleştir", + "description": "Akıcı geçişler ve efektler göster" + }, + "toolbar_position": { + "label": "Araç Çubuğu Konumu", + "description": "E-posta işlem düğmelerinin (Yanıtla, Arşivle, Sil vb.) nerede gösterileceği", + "top": "Üst", + "below_subject": "Konunun altı" + }, + "toolbar_labels": { + "label": "Araç Çubuğu Etiketlerini Göster", + "description": "Araç çubuğu simgelerinin yanında metin etiketleri görüntüleyin. Simgelere alışınca yer kazanmak için kapatabilirsiniz." + }, + "hide_account_switcher": { + "label": "Kenar Çubuğu Hesap Değiştiriciyi Gizle", + "description": "Klasör kenar çubuğunun üstündeki hesap seçiciyi gizleyin. Yine de alt gezinme çubuğundan hesap değiştirebilirsiniz." + }, + "show_rail_account_list": { + "label": "Gezinme Çubuğunda Hesap Avatarlarını Göster", + "description": "Hızlı geçiş için gezinme çubuğunun altında ayrı hesap dairelerini ve altında oturumu kapat düğmesini görüntüleyin." + }, + "unified_mailbox": { + "label": "Birleşik Posta Kutusu", + "description": "Bağlı tüm hesaplardaki birleşik klasörleri (Gelen Kutusu, Gönderilenler vb.) göster", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Grup gelen kutularını dahil et", + "description": "Paylaşılan/grup gelen kutularını da birleşik görünüme dahil et." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Renkli Kenar Çubuğu Simgeleri", + "description": "Klasör ve etiket simgelerini türe göre renklendir (mavi Gelen Kutusu, kırmızı İstem Dışı vb.). Tek renkli kenar çubuğu için kapatın." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Toplam mesaj sayısını göster", + "description": "Klasörlerin ve etiketlerin yanında, okunmamış sayısının yanı sıra toplam mesaj sayısını gösterir. Yalnızca okunmamışları göstermek için devre dışı bırakın." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Paylaşılan posta hesaplarını göster", + "description": "Sizinle paylaşılan posta kutularının klasörlerini kenar çubuğunda Paylaşılan altında göster. Hepsini gizlemek için kapatın veya aşağıdan tek tek hesapları gizleyin. Takvimleri ve kişileri görünür kalır." + }, + "pro_interface": { + "label": "Pro Arayüz (Deneysel)", + "description": "Yalnızca masaüstü için güçlü kullanıcı düzeni: çoklu sekmede ileti gezme ve hesaplar arası iş akışları. Standart arayüz etkilenmez; istediğiniz zaman geri dönebilirsiniz.", + "open_label": "Pro Arayüzü Aç", + "back_to_standard": "Standarda dön" + }, + "cross_unread": { + "label": "Tüm hesaplar: Okunmamış", + "description": "Tüm hesaplar bölümünde, tüm hesaplardaki okunmamış postaları listeleyen bir giriş gösterir; önemsiz, gönderilmiş, arşiv, çöp ve taslaklar dışındaki tüm klasörleri kapsar." + }, + "cross_starred": { + "label": "Tüm hesaplar: Yıldızlı", + "description": "Tüm hesaplar bölümünde, tüm hesaplardaki yıldızlı postaları listeleyen bir giriş gösterir; önemsiz, gönderilmiş, arşiv, çöp ve taslaklar dışındaki tüm klasörleri kapsar." + }, + "cross_all": { + "label": "Tüm hesaplar: Tüm postalar", + "description": "Tüm hesaplar bölümünde, tüm hesaplardaki tüm postaları listeleyen bir giriş gösterir; önemsiz, gönderilmiş, arşiv, çöp ve taslaklar dışındaki tüm klasörleri kapsar." + } + }, + "keywords": { + "title": "E-posta Etiketleri", + "description": "E-postalarınızı renklerle düzenlemek için etiketler tanımlayın. Bunlar sunucuda JMAP anahtar sözcükleri olarak saklanır.", + "add_keyword": "Etiket Ekle", + "label_field": "Görünen Ad", + "label_placeholder": "ör. İş, Kişisel, Acil", + "id_field": "Etiket Kimliği", + "id_placeholder": "ör. is, kisisel", + "color_field": "Renk", + "id_exists": "Bu etiket kimliği zaten mevcut", + "edit": "Etiketi düzenle", + "delete": "Etiketi sil", + "save": "Kaydet", + "add": "Ekle", + "cancel": "İptal", + "migrating": "Mevcut e-postalardaki etiket güncelleniyor…", + "migration_error": "Mevcut e-postalardaki etiket güncellenemedi", + "filters_migration_error": "Bazı filtre kuralları hâlâ eski etiketi kullanıyor — bunları elle güncelleyin", + "nesting": { + "label": "İç içe etiketler", + "description": "Etiketleri başka etiketlerin altına yerleştirin ve kenar çubuğunda ağaç olarak gösterin." + }, + "parent_field": "Üst etiket", + "no_parent": "Üst etiket yok", + "too_long": "Bu etiket yolu çok uzun (en fazla {max} karakter)", + "has_children_locked": "Bunun altında başka etiketler var, bu nedenle adı ve üst etiketi kilitli. Önce onları taşıyın veya kaldırın.", + "has_children_delete": "Önce bunun altındaki etiketleri kaldırın", + "visibility_field": "Kenar çubuğunda görünürlük", + "visibility": { + "show": "Göster", + "unread": "Okunmamış varsa göster", + "hide": "Gizle" + }, + "unrecognized": { + "title": "Etiketleri sunucudan kurtar", + "description": "Etiket adları ve renkleri bu tarayıcıda saklanır, ancak etiketlerin kendisi JMAP anahtar sözcükleri olarak e-postalarınızda bulunur. Burada karşılığı olmayan anahtar sözcükler için postanızı tarayın ve bunları geri ekleyin.", + "scan": "Etiketleri tara", + "rescan": "Yeniden tara", + "scanning": "Taranıyor… {total} e-postadan {scanned} tanesi", + "none": "E-postalarınızdaki tüm etiketler burada zaten tanımlı.", + "messages": "{count, plural, one {# e-posta} other {# e-posta}}", + "add_all": "{count} etiketin tümünü ekle", + "partial": "{total} e-postadan yalnızca en yeni {scanned} tanesi tarandı. Yalnızca eski postalarda kullanılan etiketler eksik olabilir.", + "error": "Postanız etiketler için taranamadı." + } + }, + "notifications": { + "test_sound": "Bildirim sesini test et", + "sounds": { + "default": "Varsayılan (Bip)", + "cheerful": "Neşeli", + "involved": "Odaklı", + "swift": "Hızlı Jest", + "relax": "Rahatla" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Bildirim Sesi", + "description": "Bildirimler için hangi sesin çalınacağını seçin", + "choose": "Ses", + "choose_desc": "Bir bildirim tonu seçin ve önizlemek için hoparlör simgesine tıklayın" + }, + "email": { + "title": "E-posta Bildirimleri", + "description": "Gelen e-postalar için bildirimleri yapılandırın", + "enabled": "E-posta bildirimleri", + "enabled_desc": "Yeni e-postalar geldiğinde bildirim göster", + "sound": "Bildirim sesi", + "sound_desc": "Yeni e-postalar geldiğinde sesli uyarı çal", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Takvim Bildirimleri", + "description": "Takvim etkinlikleri için bildirimleri yapılandırın", + "enabled": "Etkinlik bildirimleri", + "enabled_desc": "Yaklaşan takvim etkinlikleri için uyarılar göster", + "sound": "Bildirim sesi", + "sound_desc": "Takvim hatırlatıcıları için sesli uyarı çal", + "invitation_parsing": "E-posta davetlerini ayrıştır", + "invitation_parsing_desc": "E-posta eklerindeki takvim davetlerini algıla ve takvim işlemlerini göster" + }, + "push": { + "confirm_disable_message": "Bu cihaz, site kapalıyken uyarı almayı durduracak.", + "confirm_disable_title": "Arka plan bildirimleri devre dışı bırakılsın mı?", + "description": "Bu site kapalıyken yeni postalar için sistem bildirimleri alın. Bulwark push röle aracılığıyla iletilir; röle posta içeriğini asla görmez.", + "disable": "Devre dışı bırak", + "enable": "Etkinleştir", + "dismiss_aria": "Bildirim istemini kapat", + "ios_hint": "iOS'ta önce siteyi ana ekranınıza yükleyin – Safari Web Push'u yalnızca yüklü PWA'lara teslim eder.", + "reenable": "Yeniden kaydet", + "relay_desc": "Varsayılan olarak barındırılan Bulwark rölesini kullanır. Yalnızca kendiniz barındırıyorsanız değiştirin.", + "relay_label": "Push röle", + "relay_locked": "Yönetici tarafından ayarlandı", + "relay_locked_desc": "Push röle URL'si yöneticiniz tarafından ayarlandı ve değiştirilemez.", + "status_active": "Bu cihazda etkin", + "status_busy": "Çalışıyor…", + "status_inactive": "Bu cihazda etkinleştirilmedi", + "status_unsupported": "Bu tarayıcı Web Push'u desteklemiyor", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Arka plan bildirimleri", + "devices_title": "Kayıtlı cihazlar", + "devices_desc": "Bu hesap için arka plan bildirimleri almak üzere kayıtlı tüm cihazlar. Birini iptal ettiğinizde o cihaz bildirimleri hemen almayı bırakır.", + "devices_loading": "Kayıtlı cihazlar yükleniyor…", + "devices_empty": "Arka plan bildirimleri için kayıtlı cihaz yok.", + "devices_error": "Kayıtlı cihazlar yüklenemedi.", + "devices_refresh": "Yenile", + "device_this": "Bu cihaz", + "device_other": "Diğer cihaz", + "device_status_active": "Bildirim iletiyor", + "device_status_inactive": "Hiç bildirim iletmedi", + "device_status_unknown": "Bu aktarıcı tanımıyor", + "device_expires": "Bitiş: {date}", + "revoke": "İptal et", + "confirm_revoke_title": "Bu cihaz iptal edilsin mi?", + "confirm_revoke_message": "Arka plan bildirimlerini almayı hemen bırakacak. O cihazda push’u yeniden açarsanız yeniden kaydedilir.", + "confirm_revoke_message_this": "Bu cihaz arka plan bildirimlerini almayı hemen bırakacak." + } + }, + "language_region": { + "title": "Dil, Bölge ve Saat", + "description": "Dil, tarih biçimi, saat biçimi ve diğer bölgesel tercihler", + "language": { + "label": "Dil", + "description": "Tercih ettiğiniz dili seçin", + "english": "English", + "french": "Français" + }, + "date_format": { + "label": "Tarih Biçimi", + "description": "Tarihlerin e-posta listesinde nasıl gösterileceği", + "smart": "Akıllı", + "relative": "Göreceli", + "full": "Her zaman tam tarih", + "preview_today": "Bugün:", + "preview_this_week": "Bu hafta:", + "preview_older": "Daha eski:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Saat Biçimi", + "description": "12 saatlik veya 24 saatlik saat arasında seçin", + "12h": "12 saatlik", + "24h": "24 saatlik" + }, + "first_day": { + "label": "Haftanın İlk Günü", + "description": "Haftayı Pazar veya Pazartesi başlat", + "sunday": "Pazar", + "monday": "Pazartesi" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Klasör değiştirirken aramayı temizle", + "description": "Bir klasöre tıklamak (Gelen Kutusu, Gönderilenler…) etkin aramayı temizler ve o klasörü gösterir. Kapalıyken arama korunur ve geçtiğiniz klasörde yeniden çalıştırılır." + }, + "request_read_receipt": { + "label": "Varsayılan olarak okundu bilgisi iste", + "description": "Yeni bir ileti yazarken okundu bilgisi isteğini önceden etkinleştir." + }, + "read_receipt_response": { + "label": "Okundu bilgisi isteklerine yanıt ver", + "description": "Gelen bir ileti okundu bilgisi istediğinde ne yapılacağı.", + "ask": "Her seferinde sor", + "always": "Her zaman gönder", + "never": "Asla gönderme" + }, + "title": "E-posta Davranışı", + "description": "E-postaların nasıl işleneceğini yapılandırın", + "mark_read": { + "label": "Okundu Olarak İşaretle", + "description": "Açıldığında e-postaların ne zaman okundu sayılacağı", + "instant": "Anında", + "delay_3s": "3 saniye sonra", + "delay_5s": "5 saniye sonra", + "never": "Hiçbir zaman" + }, + "delete_action": { + "label": "Silme İşlemi", + "description": "Bir e-postayı sildiğinizde ne olacağı", + "trash": "Çöp Kutusuna Taşı", + "trash_and_read": "Çöp Kutusuna Taşı ve okundu olarak işaretle", + "permanent": "Kalıcı Olarak Sil", + "warning": "E-postalar kalıcı olarak silinecek ve kurtarılamayacak. Bu işlem geri alınamaz." + }, + "message_spacing": { + "label": "İleti boşluğu", + "description": "Okuyucuda ileti gövdesinin çevresindeki boşluk", + "auto": "Otomatik (önerilen)", + "always": "Her zaman boşluk ekle", + "edge": "Kenardan kenara" + }, + "plain_text_font": { + "label": "Düz metin yazı tipi", + "description": "Düz metin e-postalar için kullanılan yazı tipi", + "mono": "Eş aralıklı", + "sans": "Normal (uygulamayla aynı)" + }, + "archive_mode": { + "label": "Arşivle", + "description": "Arşivlerken e-postaların nasıl düzenleneceği", + "single": "Tek bir klasör", + "year": "Yıl başına bir klasör", + "month": "Ay başına bir klasör", + "reorganize": "Mevcut arşivi yeniden düzenle", + "reorganize_success": "{count, plural, =0 {Yeniden düzenlenecek e-posta yok} =1 {1 e-posta yeniden düzenlendi} other {# e-posta yeniden düzenlendi}}", + "reorganize_error": "Arşiv yeniden düzenlenemedi" + }, + "permanently_delete_junk": { + "label": "İstem Dışı Postayı Kalıcı Sil", + "description": "İstem Dışı/Spam klasöründeki e-postaları Çöp Kutusu'na taşımak yerine kalıcı olarak sil" + }, + "confirm_bulk_delete": { + "label": "Birden fazla iletiyi silmeden önce onayla", + "description": "Seçili birden fazla iletiyi Çöp Kutusu'na taşımadan önce sor. Kalıcı silme her zaman sorar." + }, + "mail_layout": { + "label": "Posta Düzeni", + "description": "Klasik bölünmüş okuma bölmesi ile Gmail tarzı odaklı okuma akışı arasında seçin.", + "split": "Bölünmüş bölme", + "split_description": "İleti listesi ve okuma bölmesini yan yana görünür tutun.", + "focus": "Odaklı liste", + "focus_description": "İleti başına bir satır gösterin ve klasör kenar çubuğu görünürken postayı tam genişlikte açın.", + "horizontal": "Okuma bölmesi altta", + "horizontal_description": "Mesaj listesini üstte gösterin ve seçilen mesajı altındaki okuma bölmesinde açın." + }, + "list_columns": { + "label": "Listeyi sütunlar halinde göster", + "description": "Tek satırlık satırları bir başlık altında Kimden, Konu, Tarih ve Boyut sütunlarıyla tablo olarak gösterir." + }, + "show_preview": { + "label": "Önizleme Metnini Göster", + "description": "Listede e-posta önizlemesini görüntüle", + "focus_description": "Odaklı tek satırlı ileti listesinde satır içi önizleme metnini görüntüle" + }, + "show_verification_codes": { + "label": "Doğrulama kodlarını göster", + "description": "Giriş ve onay e-postalarındaki kodu tek tıkla kopyalamak için sun; listede bir gün boyunca ve iletide" + }, + "disable_threading": { + "label": "Konuşma Gruplamamayı Devre Dışı Bırak", + "description": "E-postaları konuşmaya göre gruplamak yerine ayrı iletiler olarak göster" + }, + "plain_text_mode": { + "label": "Yalnızca Düz Metin", + "description": "Zengin metin düzenleyicisini devre dışı bırak ve yanıtlar ve iletmeler dahil tüm e-postaları yalnızca düz metin olarak gönder" + }, + "auto_select_reply_identity": { + "label": "Yanıt Adresini Otomatik Seç", + "description": "Yanıtlarken, Kimden adresini iletiyi başlangıçta alan kimliğe otomatik olarak değiştir" + }, + "reply_identity_match": { + "label": "Alıcı adresi eşleştirme", + "description": "Hangi alıcı adreslerinin sizin sayılacağı. „Yalnızca tam adres“ sadece yapılandırılmış kimliklerinizden birini seçer; „aynı alan adı“ ayrıca kimlik alan adlarınızdaki diğer tüm adresleri catch-all takma adı olarak değerlendirir ve Kimden başlığını buna göre yeniden yazar. Bu adresler dağıtım listesiyse tam adresi seçin.", + "exact": "Yalnızca tam adres", + "domain": "Alan adlarımdaki herhangi bir adres" + }, + "signature_position": { + "label": "İmza konumu", + "description": "Yanıtlarda ve iletmelerde imzanızın nereye ekleneceği. Alıntılanan metnin üzerinde, yanıt için doğal bir kapanış olarak okunur; altında ise orijinal mesajı bir bütün hâlinde tutar.", + "above_quote": "Alıntılanan metinden önce", + "below_quote": "Alıntılanan metinden sonra" + }, + "signature_separator": { + "label": "İmza ayırıcı", + "description": "İmzayı standart \"-- \" ayırıcı satırıyla başlat (RFC 3676). Mesajdan doğrudan imzaya geçmek istiyorsanız kapatın." + }, + "sub_address_delimiter": { + "label": "Alt Adres Ayırıcı", + "description": "Kullanıcı adını alt adres etiketinden ayıran karakter. Posta sunucunuzun kullandığı ayırıcıyı seçin (ör. kullanici{delimiter}etiket@domain.com).", + "option": "{delimiter} (kullanici{delimiter}etiket@domain.com)", + "custom": "Özel…", + "custom_input_label": "Özel ayırıcı karakter" + }, + "attachment_click_action": { + "label": "Ek Tıklama İşlemi", + "description": "Bir dosya ekine tıklandığında önizleme mi yoksa anında indirme mi yapılacağını seçin", + "preview": "Mümkünse önizle", + "download": "Anında indir" + }, + "attachment_position": { + "label": "Ek Konumu", + "description": "E-posta başlığında eklerin nerede görüntüleneceği", + "beside-sender": "Göndericinin yanı", + "below-header": "Başlığın altı" + }, + "emails_per_page": { + "10": "10 e-posta", + "25": "25 e-posta", + "50": "50 e-posta", + "100": "100 e-posta", + "label": "Sayfa Başına E-posta", + "description": "Bir seferde yüklenecek e-posta sayısı" + }, + "always_light_mode": { + "label": "E-postaları Her Zaman Açık Modda Göster", + "description": "Uygulama koyu moddayken bile e-posta içeriğini açık modda göster; koyu mod dönüşüm sorunlarını önler" + }, + "external_content": { + "label": "Harici İçerik", + "description": "Görsel ve harici içerikle nasıl başa çıkılacağı", + "ask": "Her zaman sor", + "block": "Her zaman engelle", + "allow": "Her zaman izin ver" + }, + "trusted_senders": { + "label": "Güvenilir Göndericiler", + "description": "Görsellerinin otomatik yükleneceği göndericileri yönetin", + "count_zero": "Yok", + "count_one": "1 gönderici", + "count_other": "{count} gönderici", + "modal_title": "Güvenilir Göndericiler", + "empty_title": "Henüz güvenilir gönderici yok", + "empty_description": "Engellenen görseller içeren bir e-postayı görüntülerken \"Bu göndericiyi her zaman güven\" seçeneğine tıklayın.", + "add_manually": "Gönderici manuel ekle", + "add_button": "Ekle", + "add_placeholder": "E-posta adresi girin", + "search_placeholder": "Göndericileri ara...", + "no_results": "Aramanızla eşleşen gönderici yok", + "remove": "Kaldır", + "close": "Kapat", + "invalid_email": "Lütfen geçerli bir e-posta adresi girin", + "already_added": "Bu gönderici zaten güvenilir", + "save_error": "Kaydedilemedi - ayrıntılar için Kişiler hata ayıklama günlüğünü kontrol edin", + "use_address_book_label": "Adres defteriyle senkronize et", + "use_address_book_description": "Tüm cihazlarınızda senkronize olmaları için güvenilir göndericileri özel bir \"Güvenilir Göndericiler\" adres defterinde saklayın" + }, + "hover_actions": { + "label": "Hızlı Üzerine Gelme İşlemleri", + "description": "Listedeki bir e-postanın üzerine gelindiğinde hangi hızlı işlemlerin görüneceğini seçin", + "delete": "Sil", + "star": "Yıldız Ekle / Kaldır", + "mark_read": "Okundu / Okunmadı İşaretle", + "archive": "Arşivle", + "tag": "Etiketle", + "spam": "Spam Olarak İşaretle", + "not_spam": "Önemsiz değil", + "none_selected": "Hiç işlem seçilmedi", + "mode_label": "Görüntüleme Modu", + "mode_inline": "Satır içi", + "mode_floating": "Kayan", + "corner_label": "Kayan Konum", + "corner_top-left": "Sol Üst", + "corner_top-right": "Sağ Üst", + "corner_bottom-left": "Sol Alt", + "corner_bottom-right": "Sağ Alt" + }, + "default_mail_program": { + "label": "Varsayılan Posta Programı", + "description": "{appName} uygulamasını mailto: bağlantıları için varsayılan posta programı olarak kaydet", + "button": "Varsayılan Olarak Ayarla", + "success": "Tarayıcı varsayılan olarak ayarlamak için istendi", + "error": "Tarayıcınız bu özelliği desteklemiyor" + }, + "attachment_reminder": { + "label": "Ek Hatırlatıcı", + "description": "İletinizde eklerden söz ettiğinizde ancak hiçbir ek eklenmemişken göndermeden önce uyar", + "keywords_label": "Tetikleyici anahtar sözcükler", + "keywords_description": "İletinizde bulunduğunda hatırlatıcıyı tetikleyen kelime veya ifadeler", + "add_placeholder": "Anahtar sözcük ekle...", + "add": "Ekle", + "remove": "Kaldır" + }, + "empty_subject_warning": { + "label": "Boş konu uyarısı", + "description": "Konusu olmayan bir ileti gönderilmeden önce onay iste." + }, + "recipient_mentions": { + "label": "Alıcılardan @ ile bahset", + "description": "Mesajda @ yazınca alıcılar önerilir ve seçilenin adı eklenir. Enter veya Tab vurgulanan adı seçer." + }, + "mention_adds_recipient": { + "label": "@ bahsetmelerinde kişiler", + "description": "@ yazdığınızda kişileriniz ve dizindeki kişiler de önerilir. Henüz alıcı olmayan birini seçerseniz iletiye eklenir.", + "to": "Kime alanına ekle", + "cc": "CC alanına ekle", + "off": "Yalnızca alıcılar" + }, + "hide_inline_image_attachments": { + "label": "Satır içi görüntüleri eklerden gizle", + "description": "İleti gövdesine gömülü görseller ayrı ek olarak listelenmez" + }, + "attachment_image_previews": { + "label": "Eklerde resim önizlemelerini göster", + "description": "Resim eklerini, genel dosya simgeleri yerine küçük resim kartları olarak göster" + }, + "send_delay": { + "label": "Göndermeyi geri al / gönderim gecikmesi", + "description": "Normal gönderimleri sunucu tarafında kısa bir süre geciktirin.", + "off": "Kapalı", + "seconds": "{seconds} saniye", + "unsupported": "Geçerli hesap gecikmeli gönderim desteği bildirmiyor. Ayar diğer hesaplar için kaydedilmeye devam eder." + }, + "return_to_list_after_action": { + "label": "Silme veya okunmadı olarak işaretlemeden sonra listeye dön", + "description": "Açık iletiyi sildikten veya okunmadı olarak işaretledikten sonra, sonraki iletiyi açmak yerine ileti listesine dön." + }, + "swipe_right_action": { + "label": "Sağa kaydırma eylemi (mobil)", + "description": "Dokunmatik cihazlarda, listede bir iletiyi sağa kaydırdığınızda gerçekleştirilen eylem." + }, + "swipe_left_action": { + "label": "Sola kaydırma eylemi (mobil)", + "description": "Dokunmatik cihazlarda, listede bir iletiyi sola kaydırdığınızda gerçekleştirilen eylem." + }, + "swipe_actions": { + "none": "Yok", + "archive": "Arşivle", + "delete": "Sil", + "mark_read": "Okundu durumunu değiştir", + "star": "Yıldızı değiştir", + "spam": "Spam olarak işaretle" + }, + "rtl_editing": { + "label": "Sağdan sola düzenleme desteği", + "description": "Düzenleyici araç çubuğuna paragrafları soldan sağa veya sağdan sola ayarlayabileceğiniz bir yön düğmesi ekler" + } + }, + "composer": { + "title": "İleti düzenleyici", + "description": "E-posta yazma ayarlarını yapılandırın", + "autosave": { + "label": "Otomatik Kaydetme Aralığı", + "description": "Taslakların otomatik kaydedilme sıklığı", + "30s": "Her 30 saniyede bir", + "1m": "Her dakika", + "2m": "Her 2 dakikada bir", + "5m": "Her 5 dakikada bir" + }, + "send_confirmation": { + "label": "Gönderme Onayı", + "description": "E-posta göndermeden önce onay isteyin" + }, + "default_reply": { + "label": "Varsayılan Yanıt Modu", + "description": "Yanıtla'ya tıklandığında varsayılan işlem", + "reply": "Yanıtla", + "reply_all": "Tümünü Yanıtla" + } + }, + "privacy": { + "title": "Gizlilik ve Güvenlik", + "description": "Gizlilik ve güvenlik ayarlarınızı yönetin", + "external_images": { + "label": "Harici Görselleri Engelle", + "description": "Harici görseller aracılığıyla izlemeyi önle" + }, + "session_timeout": { + "label": "Oturum Zaman Aşımı", + "description": "Hareketsizlik sonrası otomatik oturumu kapat", + "never": "Hiçbir zaman", + "30m": "30 dakika", + "1h": "1 saat", + "4h": "4 saat" + }, + "clear_cache": { + "label": "Önbelleği Temizle", + "description": "Önbelleğe alınmış verileri ve geçici dosyaları kaldır", + "button": "Önbelleği Temizle", + "confirm": "Önbelleği temizlemek istediğinizden emin misiniz?", + "success": "Önbellek başarıyla temizlendi" + } + }, + "account": { + "title": "Hesap", + "description": "Hesap bilgilerinizi görüntüleyin", + "name_label": "Görünen Ad", + "username_label": "Kullanıcı Adı", + "account_type_label": "Hesap Türü", + "auth_method_label": "Kimlik Doğrulama", + "auth_method_oauth": "Tekil Oturum Açma (OAuth/OIDC)", + "auth_method_basic": "Parola", + "auth_method_token": "Erişim belirteci", + "demo_account": "Demo Hesabı", + "email": { + "label": "E-posta Adresi", + "value": "{email}" + }, + "server": { + "label": "JMAP Sunucusu", + "value": "{server}" + }, + "storage": { + "label": "Depolama Kullanımı", + "used": "{total} alanın {used} kullanıldı", + "percentage": "%{percent} kullanıldı" + }, + "last_sync": { + "label": "Son Senkronizasyon", + "value": "{time}" + }, + "accounts": { + "title": "Oturum açılmış hesaplar", + "description": "Hesapların hesap menüsünde görüntülenme sırasını değiştirmek için sürükleyin", + "active": "Şu anda etkin hesap", + "default_badge": "Varsayılan hesap", + "set_default": "Varsayılan olarak ayarla", + "switch_to": "Bu hesaba geç", + "move_up": "Yukarı taşı", + "move_down": "Aşağı taşı", + "drag_handle": "Sıralamak için sürükleyin", + "add": "Hesap ekle" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Benimle paylaşılanlar", + "description": "Yönetebileceğiniz grup ve paylaşılan hesaplar. Filtrelerini, tatil yanıtlayıcısını, takvimlerini ve kişilerini düzenlemek için birini seçin.", + "shared_label": "Paylaşılan hesap" + } + }, + "security": { + "title": "Hesap Güvenliği", + "description": "Parolanızı, iki faktörlü kimlik doğrulamayı ve güvenlik ayarlarını yönetin", + "detecting": "Sunucu özellikleri algılanıyor...", + "not_available": "Bu posta sunucusu için hesap güvenliği yönetimi kullanılamıyor. Gerekli izinler devre dışı bırakılmış olabilir. Ayrıntılar için belgelere bakın.", + "password": { + "title": "Parolayı Değiştir", + "current": "Mevcut Parola", + "new": "Yeni Parola", + "confirm": "Yeni Parolayı Onayla", + "submit": "Parolayı Değiştir", + "success": "Parola başarıyla değiştirildi", + "error_title": "Parola değiştirme başarısız", + "error_mismatch": "Yeni parolalar eşleşmiyor", + "error_min_length": "Parola en az 8 karakter olmalıdır", + "error_generic": "Parola değiştirilemedi" + }, + "display_name": { + "label": "Görünen Ad", + "description": "Sunucuda görünen adınız", + "placeholder": "Görünen adınızı girin", + "save": "Kaydet", + "success": "Görünen ad güncellendi", + "error": "Görünen ad güncellenemedi" + }, + "totp": { + "section_title": "İki Faktörlü Kimlik Doğrulama", + "label": "TOTP Kimlik Doğrulama", + "description": "Zamana dayalı tek kullanımlık parola ile ekstra bir güvenlik katmanı ekleyin", + "active": "Etkin", + "inactive": "Devre dışı", + "enabled": "İki faktörlü kimlik doğrulama etkinleştirildi", + "disabled": "İki faktörlü kimlik doğrulama devre dışı bırakıldı", + "enable_error": "2FA etkinleştirilemedi", + "disable_error": "2FA devre dışı bırakılamadı", + "setup_instructions": "Bu URL'yi kimlik doğrulayıcı uygulamanıza (Google Authenticator, Authy vb.) kopyalayın:", + "verification_code": "Doğrulama kodu", + "confirm": "Onayla", + "disable": "Devre Dışı Bırak", + "disable_confirm_prompt": "İki faktörlü kimlik doğrulamayı devre dışı bırakmak için parolanızı girin.", + "password_required": "Parola gerekli", + "code_required": "Doğrulama kodu gerekli", + "code_invalid": "Geçersiz doğrulama kodu. Kimlik doğrulayıcı uygulamanızı kontrol edin ve tekrar deneyin." + }, + "app_passwords": { + "title": "Uygulama Parolaları", + "description": "İki faktörlü kimlik doğrulamayı desteklemeyen uygulamalar için parola oluşturun", + "add": "Ekle", + "create": "Oluştur", + "cancel": "İptal", + "done": "Tamam", + "generate": "Oluştur", + "name_label": "Uygulama Adı", + "name_placeholder": "ör. Thunderbird, iPhone Mail", + "expires_label": "Son Kullanma (isteğe bağlı)", + "allowed_ips_label": "İzin Verilen IP'ler (isteğe bağlı)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Virgül veya boşlukla ayrılmış. Herhangi bir IP'ye izin vermek için boş bırakın.", + "password_label": "Parola (otomatik oluşturmak için boş bırakın)", + "password_placeholder": "Boş bırakılırsa otomatik oluşturulur", + "copy_now_warning": "Bu parolayı şimdi kopyalayın - bir daha gösterilmeyecek.", + "added": "Uygulama parolası oluşturuldu", + "removed": "Uygulama parolası kaldırıldı", + "add_error": "Uygulama parolası oluşturulamadı", + "remove_error": "Uygulama parolası kaldırılamadı", + "none": "Yapılandırılmış uygulama parolası yok" + }, + "api_keys": { + "title": "API Anahtarları", + "description": "Sunucuyla doğrudan iletişim kuran betikler ve entegrasyonlar için API anahtarları oluşturun", + "name_label": "Anahtar Adı", + "name_placeholder": "ör. Yedek betiği, CI koşucusu", + "copy_now_warning": "Bu API anahtarını şimdi kopyalayın - bir daha gösterilmeyecek.", + "added": "API anahtarı oluşturuldu", + "removed": "API anahtarı kaldırıldı", + "add_error": "API anahtarı oluşturulamadı", + "remove_error": "API anahtarı kaldırılamadı", + "none": "Yapılandırılmış API anahtarı yok" + }, + "encryption": { + "section_title": "Bekleme Süresi Şifrelemesi", + "label": "E-posta Şifrelemesi", + "description": "Ek gizlilik için sunucudaki saklanan e-postaları şifreleyin", + "active": "{type} şifrelemesi etkin", + "inactive": "Devre dışı", + "enabled": "Bekleme süresi şifrelemesi etkinleştirildi", + "disabled_success": "Bekleme süresi şifrelemesi devre dışı bırakıldı", + "error": "Şifreleme ayarları güncellenemedi", + "algorithm_label": "Şifreleme algoritması", + "encrypt_on_append": "Yeni e-postaları yüklenirken şifrele", + "allow_spam_training": "E-postalar şifrelenmeden önce spam eğitimine izin ver", + "enable_encryption": "Şifrelemeyi etkinleştir", + "enable_tooltip": "Bu anahtarla şifrelemeyi etkinleştir", + "configure_title": "Bu anahtarla şifrelemeyi etkinleştir", + "enable_button": "Etkinleştir", + "disable_tooltip": "Şifrelemeyi devre dışı bırak", + "enabled_success": "Bekleme süresi şifrelemesi etkinleştirildi" + }, + "email_client": { + "title": "E-posta İstemcisi Kurulumu", + "description": "Masaüstü veya mobil e-posta istemcinizi (Thunderbird, Apple Mail, Outlook vb.) yapılandırmak için bu kimlik bilgilerini kullanın", + "jmap_username_label": "JMAP Kullanıcı Adı", + "copy": "Kopyala", + "copied": "Kopyalandı", + "password_instructions": "E-posta istemcinizde oturum açmak için yukarıdaki JMAP kullanıcı adı ile bir uygulama parolasını birlikte kullanın. Henüz oluşturmadıysanız yukarıdaki bölümden bir uygulama parolası oluşturun." + }, + "link_device": { + "title": "Mobil uygulamayı bağla", + "description": "Telefonunuzdaki Bulwark Mail uygulamasında, orada parolanızı yazmadan oturum açın. Telefonunuz kendi oturumunu alır; bu nedenle burada oturumu kapatmanız telefondaki oturumu kapatmaz.", + "generate": "QR kodunu göster", + "regenerate": "Yeni kod göster", + "instructions": "Bulwark Mail uygulamasını açın ve giriş ekranında \"Scan a sign-in code\" seçeneğine dokunun (zaten oturum açtıysanız \"Add account\" → \"Scan a sign-in code\"), ardından kamerayı buraya doğrultun.", + "expires_in": "Bu kodun süresi {seconds} saniye içinde dolacak. Yalnızca bir kez kullanılabilir.", + "expired": "Bu kodun süresi doldu.", + "error": "Eşleştirme kodu oluşturulamadı. Lütfen tekrar deneyin.", + "password_prompt": "Siz olduğunuzu doğrulamak için parolanızı girin. Telefonunuz kendi oturumunu alır; parolanız telefona gönderilmez.", + "show_password": "Parolayı göster", + "hide_password": "Parolayı gizle", + "continue": "Devam", + "error_wrong_password": "Parola yanlış. Tekrar deneyin.", + "totp_prompt": "Kimlik doğrulama uygulamanızdaki kodu girin.", + "error_password_or_code": "Parola veya kod yanlış.", + "error_not_signed_in": "Oturumunuzun süresi doldu. Cihaz bağlamak için yeniden oturum açın.", + "error_impersonation": "Başka bir kullanıcının posta kutusunu görüntülerken cihaz bağlanamaz.", + "error_too_many_attempts": "Çok fazla deneme. Biraz bekleyip tekrar deneyin.", + "error_session_secret": "Bu sunucuda cihaz bağlama ayarlanmamış (SESSION_SECRET eksik). Yöneticinize başvurun.", + "error_server_unreachable": "Posta sunucunuza ulaşılamadı. Tekrar deneyin.", + "error_pairing_unavailable": "Posta sunucunuz bu şekilde cihaz bağlanmasına izin vermiyor. Yöneticinize başvurun.", + "qr_alt": "Giriş QR kodu", + "link_label": "Giriş bağlantısı", + "copy_link": "Giriş bağlantısını kopyala", + "copied": "Kopyalandı", + "copy_hint": "Kamera yok mu? Bağlantıyı kopyalayın ve uygulamada \"Paste sign-in link\" ile yapıştırın.", + "success_title": "Telefonunuzda oturum açıldı", + "success_body": "Bu sayfayı kapatabilirsiniz. Başka bir cihaz bağlamak için yeni bir kod gösterin.", + "link_another": "Başka bir cihaz bağla", + "use_sso": "Bunun yerine çoklu oturum açmayı kullan", + "error_insecure_server": "Mobil uygulama posta sunucularına yalnızca https üzerinden bağlanır, bu sunucu ise http kullanıyor." + }, + "public_keys": { + "name_label": "Ortak anahtar adı", + "name_placeholder": "ör. Anahtarım", + "key_label": "Ortak anahtar (S/MIME veya PGP ASCII biçiminde)", + "key_placeholder": "Ortak anahtarınızı buraya yapıştırın", + "none": "Yapılandırılmış ortak anahtar yok" + } + }, + "identities": { + "title": "Gönderme Kimlikleri", + "description": "Gönderebildiğiniz e-posta adreslerini yönetin", + "identities_count": { + "label": "Kimlikleriniz", + "description": "Gönderme için yapılandırılmış e-posta adresleri", + "count_zero": "Kimlik yok", + "count_one": "1 kimlik", + "count_other": "{count} kimlik" + }, + "manage": "Kimlikleri Yönet", + "sub_addressing": { + "label": "Alt Adresleme", + "description": "Gelen postayı düzenlemek için user+etiket@domain.com gibi etiketler kullanın", + "learn_more": "Daha Fazla Bilgi" + } + }, + "vacation": { + "title": "Ofis Dışında", + "description": "Yokken gelen e-postaları otomatik olarak yönetin", + "loading": "Tatil ayarları yükleniyor...", + "not_supported": "Posta sunucunuz tatil yanıtlarını desteklemiyor.", + "fetch_error": "Tatil ayarları yüklenemedi. Lütfen tekrar deneyin.", + "status": { + "label": "Otomatik yanıt gönder", + "description": "Size e-posta gönderenlere otomatik yanıt gönderin", + "active": "Etkin", + "inactive": "Devre dışı" + }, + "date_range": { + "start": "Başlangıç Tarihi", + "start_description": "Başlangıç sınırı olmaması için boş bırakın", + "end": "Bitiş Tarihi", + "end_description": "Bitiş sınırı olmaması için boş bırakın" + }, + "message": { + "subject_label": "Konu", + "subject_description": "Otomatik yanıtın konu satırı", + "subject_placeholder": "Ofis Dışındayım", + "body_label": "İleti Gövdesi", + "body_description": "Düz metin ileti içeriği", + "body_placeholder": "E-postanız için teşekkür ederim. Şu anda ofis dışındayım ve döndüğümde yanıt vereceğim.", + "html_label": "Biçimlendirilmiş mesaj (HTML)", + "html_description": "Bağlantılar ve stil içeren biçimlendirilmiş bir sürüm ekleyin. E-posta istemcisi bunu görüntüleyemeyen alıcılar yukarıdaki düz metni görür.", + "html_placeholder": "Biçimlendirilmiş bir ofis dışı yanıtı yazın…" + }, + "preview": { + "title": "Önizleme", + "show": "Önizlemeyi göster", + "hide": "Önizlemeyi gizle" + }, + "save": "Değişiklikleri Kaydet", + "saving": "Kaydediliyor...", + "warnings": { + "end_before_start": "Bitiş tarihi başlangıç tarihinden sonra olmalıdır", + "invalid_date": "Saatiyle birlikte geçerli bir başlangıç ve bitiş tarihi girin veya alanı boş bırakın", + "start_in_past": "Başlangıç tarihi geçmişte", + "empty_body": "İleti gövdesi boş - alıcılar boş bir yanıt alacak", + "subject_too_long": "Konu sunucu için çok uzun (en fazla {max} bayt).", + "body_too_long": "İleti sunucu için çok uzun (biçimlendirme dahil en fazla {max} bayt).", + "forward_address": "İletilecek geçerli bir adres girin", + "not_running": "İletme veya yanıt alıcıları kaydedildi, ancak şu anda etkin değil. Yeniden etkinleştirmek için kaydedin." + }, + "forward": { + "title": "İletme", + "description": "Gelen iletileri, otomatik yanıtla ya da yanıtsız, yukarıdaki dönem içinde başka bir adrese iletir (bitiş tarihi yoksa kapatılana kadar). İstenmeyen e-postalar iletilmez.", + "enabled_label": "İletileri ilet", + "to_label": "İlet", + "to_placeholder": "email@example.com", + "keep_label": "Gelen kutusunda tut", + "keep_description": "Aksi halde ileti yalnızca iletilir: bu posta kutusunda kalmaz ve ona hiçbir filtre kuralı uygulanmaz." + }, + "audience": { + "label": "Yanıt gönderilecek kişiler", + "description": "Dahili gönderenler: {domains}", + "all": "Tüm gönderenler", + "internal": "Yalnızca dahili gönderenler", + "external": "Yalnızca harici gönderenler" + }, + "auto_reply": { + "title": "Otomatik Yanıt" + } + }, + "folders": { + "role_memos": "Notlar", + "title": "Klasörler", + "description": "E-posta klasörlerinizi yönetin ve standart roller atayın", + "folder_list": "Klasörleriniz", + "folder_list_description": "Özelleştirmek için klasör simgesine tıklayın", + "standard_roles": "Standart Klasör Rolleri", + "standard_roles_description": "Gelen Kutusu, Gönderilenler, Çöp Kutusu vb. standart posta kutusu rolleri için hangi klasörlerin kullanılacağını atayın", + "role_inbox": "Gelen Kutusu", + "role_drafts": "Taslaklar", + "role_sent": "Gönderilenler", + "role_trash": "Çöp Kutusu", + "role_junk": "Spam / İstem Dışı", + "role_archive": "Arşiv", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Yok", + "create_folder": "Klasör Oluştur", + "create_subfolder": "Alt Klasör Oluştur", + "reorder": "Yeniden sıralamak için sürükleyin", + "reorder_error": "Klasörler yeniden sıralanamadı", + "move_error": "Klasör taşınamadı", + "select_folder": "{name} öğesini seç", + "select_hint": "Seç. Shift+tıklama, en son tıkladığınız klasöre kadar tüm klasörleri seçer.", + "selected_count": "{count, plural, one {# klasör} other {# klasör}} seçildi", + "move_to": "Taşı...", + "move_to_top_level": "En üst düzey", + "move": "Taşı", + "clear_selection": "Seçimi temizle", + "folders_moved": "{count, plural, one {# klasör taşındı} other {# klasör taşındı}}", + "move_partial_error": "{count, plural, one {# klasör} other {# klasör}} taşınamadı", + "subfolder_of": "{name} içinde", + "subfolder_name": "Alt klasör adı", + "new_folder_name": "Klasör adı", + "rename": "Yeniden Adlandır", + "change_icon": "Simgeyi değiştir", + "count_mode": "{name} sayacı", + "count_mode_default": "Sayaç: varsayılan", + "count_mode_unread": "Yalnızca okunmamış", + "count_mode_all": "Okunmamış ve toplam", + "count_mode_none": "Sayaç yok", + "delete": "Sil", + "confirm_delete": "\"{name}\" klasörünü silmek istediğinizden emin misiniz? Bu klasördeki e-postalar Çöp Kutusu'na taşınacak.", + "create": "Oluştur", + "cancel": "İptal", + "no_folders": "Özel klasör yok", + "cannot_delete_role": "Standart rolü olan klasör silinemiyor. Önce rolü kaldırın.", + "folder_created": "Klasör oluşturuldu", + "folder_renamed": "Klasör yeniden adlandırıldı", + "folder_deleted": "Klasör silindi", + "role_updated": "Klasör rolü güncellendi", + "error_create": "Klasör oluşturulamadı", + "error_rename": "Klasör yeniden adlandırılamadı", + "error_delete": "Klasör silinemedi", + "error_delete_has_children": "Klasör silinemiyor: hâlâ alt klasörler içeriyor. Önce onları silin veya taşıyın.", + "error_delete_has_email": "Klasör silinemiyor: hâlâ e-postalar içeriyor. Önce onları taşıyın veya silin.", + "error_role": "Klasör rolü güncellenemedi" + }, + "advanced": { + "title": "Gelişmiş", + "description": "Gelişmiş seçenekler ve geliştirici ayarları", + "debug_mode": { + "label": "Hata Ayıklama Modu", + "description": "Sorun giderme için ayrıntılı günlük kaydını etkinleştir" + }, + "debug_categories": { + "description": "Hangi kategorilerin günlüğe kaydedileceğini seçin. Konsol gürültüsünü azaltmak için ihtiyaç duymadığınız kategorileri devre dışı bırakın.", + "jmap": "JMAP İstemcisi", + "jmap_description": "Posta kutusu işlemleri, e-posta getirme ve JMAP protokol istekleri", + "calendar": "Takvim", + "calendar_description": "Takvim etkinlikleri, içe aktarmalar ve zamanlama iletileri", + "tasks": "Görevler", + "tasks_description": "Takvim görevi oluşturma, getirme ve güncellemeler", + "auth": "Kimlik Doğrulama", + "auth_description": "Giriş, TOTP, belirteç değişimi ve oturum yönetimi", + "filters": "Filtreler", + "filters_description": "Sieve filtre kuralları ve tatil betikleri", + "email": "E-posta Görüntüleme", + "email_description": "E-posta oluşturma, TNEF işleme ve okundu olarak işaretleme", + "push": "Anlık Bildirimler", + "push_description": "Anlık bildirim kurulumu ve teslimi", + "contacts": "Kişiler ve Adres Defterleri", + "contacts_description": "Kişi senkronizasyonu, adres defteri işlemleri ve güvenilir göndericiler" + }, + "settings_sync": { + "label": "Ayar Senkronizasyonu", + "description": "Ayarlarınızı tarayıcılar ve cihazlar arasında senkronize edin" + }, + "sender_favicons": { + "label": "Gönderici Favorikonları", + "description": "İş göndericileri için profil resmi olarak web sitesi simgelerini göster", + "refresh_label": "Gönderen simgelerini yenile", + "refresh_description": "Web sitesi simgelerini yeniden al, örneğin bir gönderen simgesini değiştirdiğinde", + "refresh_button": "Yenile", + "refresh_done": "Gönderen simgeleri yeniden alınacak" + }, + "show_avatars_in_junk": { + "label": "İstem Dışı Klasöründe Avatarları Göster", + "description": "İstem dışı klasöründeki göndericiler için profil resimleri ve favorikonları göster. Kimlik avı girişimlerine görsel meşruiyet kazandırmamak için varsayılan olarak kapalıdır." + }, + "keyboard_shortcuts": { + "label": "Klavye Kısayolları", + "description": "Mevcut klavye kısayollarını görüntüleyin", + "button": "Kısayolları Görüntüle" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Ayarları Sıfırla", + "description": "Tüm ayarları varsayılan değerlerine geri yükle", + "button": "Varsayılanlara Sıfırla" + }, + "export_settings": { + "label": "Ayarları Dışa Aktar", + "description": "Ayarlarınızı JSON olarak indirin", + "button": "Dışa Aktar" + }, + "import_settings": { + "label": "Ayarları İçe Aktar", + "description": "JSON dosyasından ayarları yükle", + "button": "İçe Aktar" + }, + "about": { + "title": "Bulwark Webmail", + "source_code": "Kaynak kodu" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Spam ve oltalama e-postaları gelen kutunuza ulaşmadan önce üzerlerine tıklayın. Gerçek e-postaların geçmesine izin verin. Üç kalkanın hepsini kaybederseniz gelen kutusu düşer.", + "kinds": { + "spam": "Spam", + "phishing": "Oltalama", + "legit": "Gerçek e-posta" + }, + "click_to_block": "Engellemek için tıklayın", + "let_through": "Geçmesine izin verin", + "start": "Başla", + "play_again": "Tekrar oyna", + "score": "Puan", + "combo": "Seri bonusu", + "shields_left": "{total} kalkandan {count} kaldı", + "seconds": "{seconds} sn", + "best": "En iyi: {score}", + "new_best": "Yeni rekor", + "held": "Gelen kutusu korundu", + "overrun": "Gelen kutusu düştü", + "stats": { + "caught": "Engellenen", + "delivered": "Teslim edilen", + "mistakes": "Hatalar" + } + } + }, + "sidebar_apps": { + "title": "Kenar Çubuğu Uygulamaları", + "description": "Kenar çubuğunuzdaki özel uygulamaları ve bağlantıları yönetin", + "keep_loaded": "Uygulamaları Yüklü Tut", + "keep_loaded_description": "Yeniden yüklemeyi önlemek için aralarında geçiş yaparken satır içi uygulamaları arka planda çalışır halde tutun", + "managed_title": "Yöneticiniz tarafından sağlandı", + "managed_description": "Bu uygulamalar herkes için ayarlanmıştır ve değiştirilemez.", + "manage_title": "Özel Uygulamalar", + "manage_description": "Kenar çubuğunuzdan özel uygulamalar ekleyin, düzenleyin veya kaldırın" + }, + "contacts": { + "title": "Kişiler", + "description": "Kişilerinizi içe ve dışa aktarın", + "group_by_letter_label": "İlk harfe göre grupla", + "sort_by_last_name_label": "Soyada göre sırala", + "sort_by_last_name_description": "Aile üyeleri bir arada görünsün diye kişi listesini soyada göre sıralar", + "group_by_letter_description": "Kişi listesinde alfabetik bölüm başlıkları göster", + "import_label": "Kişileri İçe Aktar", + "import_description": "Kişileri bir vCard (.vcf) dosyasından içe aktarın", + "export_label": "Kişileri Dışa Aktar", + "export_description": "Tüm kişileri vCard (.vcf) dosyası olarak dışa aktarın", + "manage_title": "Adres Defterleri", + "manage_description": "Adres defterlerinizi yeniden adlandırın", + "no_address_books": "Adres defteri bulunamadı", + "categories_title": "Kategoriler", + "categories_description": "Kişi kategorilerini yeniden adlandırın", + "no_categories": "Kategori bulunamadı" + }, + "downloads": { + "title": "İndirilenler", + "description": "İndirilen e-postaların ve eklerin nasıl adlandırılacağını özelleştirin.", + "reset": "Varsayılana sıfırla", + "preview": "Önizleme:", + "email_template": { + "label": "E-posta dosya adı (.eml)", + "description": "Bir e-postayı dışa aktarırken veya dosya sistemine sürüklediğinizde kullanılan şablon. .eml uzantısı otomatik olarak eklenir." + }, + "attachment_template": { + "label": "Ek dosyası adı", + "description": "Bir eki indirirken veya sürüklerken kullanılan şablon. '{filename}' ve '{ext}' yazılmazsa orijinal uzantı korunur." + }, + "bundle_template": { + "label": "Çoklu e-posta .zip dosya adı", + "description": "Birden çok seçili e-postayı tek bir .zip arşivi olarak sürüklediğinizde veya indirdiğinizde kullanılan şablon. .zip uzantısı otomatik olarak eklenir." + }, + "spaces": { + "label": "Boşluklar", + "description": "Sonuçtaki dosya adındaki boşlukları başka bir karakterle değiştirin.", + "keep": "Boşlukları koru", + "underscore": "_ ile değiştir", + "dash": "- ile değiştir" + }, + "lowercase": { + "label": "Küçük harf", + "description": "Tüm dosya adını küçük harfe zorla." + }, + "strip_diacritics": { + "label": "Aksanları kaldır", + "description": "Aksanlı harfleri ASCII karşılıklarına dönüştür (ä → a, é → e). Unicode dosya adlarıyla sorun yaşayan araçlar için yararlıdır." + }, + "collapse_separators": { + "label": "Tekrarlanan ayırıcıları birleştir", + "description": "Art arda gelen boşluk, alt çizgi veya tireleri tek bir karaktere indir." + }, + "after_export": { + "label": "Dışa aktarmadan sonra", + "description": "E-postayı .eml olarak dışa aktardıktan sonra isteğe bağlı olarak taşı.", + "keep": "Posta kutusunda tut", + "archive": "Arşive taşı", + "trash": "Çöp kutusuna taşı" + } + }, + "filters": { + "title": "E-posta Filtreleri", + "description": "Gelen e-postaları otomatik olarak sıralamak, etiketlemek ve yönetmek için kurallar oluşturun", + "add_rule": "Kural Ekle", + "no_rules": "Filtre kuralı yok", + "no_rules_description": "Gelen e-postalarınızı otomatik düzenlemek için kurallar oluşturun", + "vacation_active": "Tatil Yanıtlayıcı etkin", + "vacation_active_description": "Gelen iletiler için otomatik yanıt etkinleştirildi", + "vacation_configure": "Yapılandır", + "edit_rule": "Kuralı Düzenle", + "new_rule": "Yeni Kural", + "delete_rule": "Kuralı Sil", + "delete_confirm": "Bu kuralı silmek istediğinizden emin misiniz?", + "enable": "Etkinleştir", + "disable": "Devre Dışı Bırak", + "raw_editor": "Ham Sieve Düzenleyicisi", + "raw_editor_warning": "Ham Sieve betiğini düzenlemek görsel kural düzenlemeyi bozabilir. Burada yapılan değişiklikler görsel oluşturucunun üzerine yazar.", + "validate": "Doğrula", + "validation_success": "Betik geçerli", + "validation_error": "Betikte hatalar var", + "save": "Kuralları Kaydet", + "saving": "Kaydediliyor...", + "saved": "Filtreler başarıyla kaydedildi", + "save_failed": "Filtreler kaydedilemedi", + "loading": "Filtreler yükleniyor...", + "not_supported": "Posta sunucunuz e-posta filtrelerini desteklemiyor.", + "rule_name": "Kural Adı", + "rule_name_placeholder": "ör. Bültenleri sırala", + "match_all": "TÜM koşullarla eşleş", + "match_any": "HERHANGİ bir koşulla eşleş", + "conditions": "Koşullar", + "add_condition": "Koşul Ekle", + "actions": "İşlemler", + "add_action": "İşlem Ekle", + "stop_processing": "Sonraki kuralları işlemeyi durdur", + "include_spam": "İstenmeyen olarak işaretlenen iletileri de taşı", + "attachment_type_placeholder": "örn. pdf, doc, jpg", + "value_placeholder_multi": "Değer (birden fazla virgülle ayrılır)", + "condition_fields": { + "attachment": "Ek", + "from": "Kimden", + "to": "Kime", + "cc": "Bilgi", + "subject": "Konu", + "header": "Özel Başlık", + "size": "Boyut", + "body": "Gövde", + "all": "Tüm iletiler" + }, + "comparators": { + "has_any": "mevcut", + "has_type": "türünde", + "contains": "içerir", + "not_contains": "içermez", + "is": "tam olarak", + "not_is": "değil", + "starts_with": "ile başlar", + "ends_with": "ile biter", + "matches": "desenle eşleşir", + "greater_than": "büyüktür", + "less_than": "küçüktür", + "address_is": "adresi şudur", + "domain_is": "alan adı şudur" + }, + "action_types": { + "move": "Klasöre taşı", + "copy": "Klasöre kopyala", + "forward": "İlet", + "mark_read": "Okundu olarak işaretle", + "star": "İletiye yıldız ekle", + "add_label": "Etiket ekle", + "discard": "At (sessizce sil)", + "reject": "İletiyle reddet", + "keep": "Gelen kutusunda tut", + "stop": "İşlemeyi durdur" + }, + "move_to_folder": "Klasör seçin", + "copy_to_folder": "Klasör seçin", + "forward_to": "E-posta adresine ilet", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Bir kopyasını sakla", + "forward_limit": "Bu sunucuda ileti başına yönlendirme sınırı: {count}. Fazla yönlendirmeler atlanır.", + "forward_unavailable": "Bu sunucu yönlendirmeye izin vermiyor. Filtre kurallarındaki yönlendirme eylemlerinin hiçbir etkisi olmaz.", + "reject_message": "Reddetme iletisi", + "reject_placeholder": "E-postanız reddedildi", + "label_name": "Etiket adı", + "label_placeholder": "Etiket seçin", + "header_name": "Başlık adı", + "header_placeholder": "ör. X-Mailing-List", + "size_bytes": "Bayt cinsinden boyut", + "size_placeholder": "ör. 1000000", + "system_managed": "Sistem tarafından yönetilen kural", + "opaque_warning": "Bu betik görsel oluşturucunun dışında düzenlendi. Yalnızca ham Sieve düzenleme kullanılabilir.", + "open_sieve_editor": "Ham Sieve düzenleyicisini aç", + "fetch_error": "Filtreler yüklenemedi", + "expanded_view": "Genişletilmiş görünüm", + "expanded_view_description": "Filtre kurallarını ayrıntılı koşul ve işlem bloklarıyla göster", + "if": "Eğer", + "then": "O zaman", + "match_all_conditions": "hepsi eşleşir", + "match_any_condition": "herhangi biri eşleşir", + "and": "ve", + "or": "veya", + "cancel": "İptal", + "confirm_delete": "Sil", + "rule_list": "Filtre kuralları", + "drag_to_reorder": "Yeniden sıralamak için sürükleyin", + "match_type": "Eşleşme türü", + "reset_to_visual": "Görsel oluşturucuya sıfırla", + "reset_warning": "Bu işlem mevcut betiği atacak ve yeniden başlatacak.", + "confirm_reset": "Sıfırla", + "validation_empty_name": "Kural adı gerekli", + "validation_empty_conditions": "En az bir değeri olan koşul gerekli", + "validation_empty_actions": "En az bir işlem gerekli", + "templates_section": "Şablondan başla", + "template_newsletters": "Bültenleri klasöre taşı", + "template_receipts": "Makbuzları otomatik arşivle", + "template_important": "Önemli e-postaları işaretle", + "template_notifications": "Bildirimleri filtrele", + "sieve_editor": { + "title": "Sieve Betik Düzenleyicisi", + "warning": "Ham Sieve betiğini düzenlemek görsel kural düzenlemeyi bozabilir. Burada yapılan değişiklikler görsel oluşturucunun üzerine yazar.", + "script_content": "Sieve betiği", + "valid": "Betik geçerli", + "invalid": "Betikte hatalar var", + "save_warning": "Kaydetme işlemi tüm görsel kuralların üzerine yazacak. Bu geri alınamaz. Onaylamak için Kaydet'e tekrar tıklayın.", + "validating": "Doğrulanıyor...", + "validate": "Doğrula", + "cancel": "İptal", + "save": "Kaydet", + "confirm_save": "Kaydetmeyi Onayla", + "validation_failed": "Doğrulama isteği başarısız" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# koşul} other {# koşul}}", + "actions_count": "{count, plural, one {# işlem} other {# işlem}}" + }, + "origin_external": "Harici", + "managed_by_tooltip": "{source} tarafından yönetilir. Bu uygulamada düzenleyin veya ham Sieve düzenleyicisini kullanın.", + "quick_rule_names": { + "move_sender": "{sender} iletilerini {folder} klasörüne taşı", + "move_domain": "@{domain} iletilerini {folder} klasörüne taşı", + "move_list": "{list} listesini {folder} klasörüne taşı", + "mark_read": "{sender} iletilerini okundu işaretle", + "tag": "{sender} iletilerine {tag} etiketi ekle", + "block": "{sender} engelle", + "custom": "{sender} iletileri", + "custom_plain": "Yeni kural", + "senders": "{count, plural, one {# gönderen} other {# gönderen}}" + }, + "suggestions": "Öneriler", + "suggest_subject": "Konu “{subject}”", + "suggest_to": "Alıcı {address}", + "suggest_cc": "Bilgi {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Bu klasörde zaten bulunan iletilere de uygula", + "apply_existing_unsupported": "Mevcut iletilere yalnızca göndereni, alıcıları, konuyu veya üstbilgileri denetleyen ve taşıyan, kopyalayan, okundu işaretleyen, yıldızlayan veya etiketleyen kurallar uygulanabilir.", + "period_title": "Tarih Aralığı", + "period_start": "Başlangıç Tarihi", + "period_end": "Bitiş Tarihi", + "validation_period_order": "Bitiş tarihi başlangıç tarihinden sonra olmalıdır", + "period_toggle": "Yalnızca belirli bir dönemde etkin", + "period_hint": "Bu dönemde gelen iletilere uygulanır. O ucu sınırlamamak için alanı boş bırakın.", + "validation_period_empty": "Dönem için bir başlangıç veya bitiş tarihi girin", + "validation_period_invalid": "Dönem için saatiyle birlikte geçerli bir tarih girin veya alanı boş bırakın", + "apply_existing_period": "Dönemi olan bir kural yalnızca o dönemde gelen iletilere uygulanır, bu nedenle mevcut iletilere uygulanamaz.", + "period_status_scheduled": "planlandı", + "period_status_active": "etkin", + "period_status_expired": "süresi doldu" + }, + "templates": { + "title": "E-posta Şablonları", + "description": "Yer tutucu değişkenlerle yeniden kullanılabilir e-posta şablonları oluşturun", + "add": "Yeni Şablon", + "edit": "Şablonu Düzenle", + "name": "Şablon Adı", + "name_placeholder": "ör. Takip e-postası", + "category": "Kategori", + "category_placeholder": "ör. İş, Kişisel", + "subject": "Konu", + "subject_placeholder": "E-posta konu satırı", + "body": "Gövde", + "body_placeholder": "E-posta gövde içeriği...", + "recipients_placeholder": "email@example.com", + "identity": "Gönderen Olarak", + "default_identity": "Varsayılan kimlik", + "favorite": "Favori", + "cancel": "İptal", + "create": "Oluştur", + "update": "Güncelle", + "confirm_delete": "Sil", + "no_templates": "Henüz şablon yok", + "manage": "Şablonları Yönet", + "count": "{count, plural, one {# şablon} other {# şablon}}", + "export_import": "Dışa ve İçe Aktar", + "export_import_description": "Şablonlarınızı yedekleyin veya başka bir cihaza aktarın", + "export": "Dışa Aktar", + "import": "İçe Aktar", + "validation": { + "empty": "Şablon adı gerekli", + "too_long": "Şablon adı en fazla 200 karakter olmalıdır" + } + }, + "files": { + "display": { + "title": "Görüntüleme", + "description": "Dosya ve klasörlerin nasıl görüntüleneceğini yapılandırın" + }, + "default_view": { + "label": "Varsayılan Görünüm", + "description": "Izgara ve liste düzeni arasında seçin", + "list": "Liste", + "grid": "Izgara" + }, + "default_sort": { + "label": "Varsayılan Sıralama", + "description": "Dosyalar için varsayılan sıralamayı seçin", + "name": "Ad", + "size": "Boyut", + "modified": "Değiştirilme" + }, + "sort_direction": { + "label": "Sıralama Yönü", + "description": "Artan veya azalan düzen seçin", + "ascending": "Artan", + "descending": "Azalan" + }, + "icons": { + "title": "Simgeler", + "description": "Dosya simgesi görünümünü yapılandırın" + }, + "show_icons": { + "label": "Dosya Simgelerini Göster", + "description": "Dosyaların ve klasörlerin yanında simgeleri görüntüle" + }, + "colored_icons": { + "label": "Renkli Simgeler", + "description": "Tek renk yerine renkli simgeler kullan" + }, + "show_thumbnails": { + "label": "Küçük Resimleri Göster", + "description": "Resim dosyaları için simgeler yerine görüntü önizlemeleri göster" + }, + "behavior": { + "title": "Davranış", + "description": "Dosya tarayıcı davranışını yapılandırın" + }, + "show_hidden": { + "label": "Gizli Dosyaları Göster", + "description": "Nokta ile başlayan dosya ve klasörleri görüntüle" + }, + "preview": { + "label": "Önizleme" + } + }, + "search_placeholder": "Ayarlarda ara", + "search_clear": "Aramayı temizle", + "search_no_results": "Eşleşen ayar yok", + "scoped": { + "back": "Hesabıma geri dön", + "managing": "Yönetiliyor: {name}" + } + }, + "errors": { + "page_error_title": "Bir şeyler ters gitti", + "page_error_description": "Beklenmeyen bir hatayla karşılaştık. Lütfen tekrar deneyin veya ana sayfaya dönün.", + "sidebar_error": "Posta kutuları yüklenemedi", + "email_list_error": "E-postalar yüklenemedi", + "viewer_error_title": "E-posta görüntülenemiyor", + "viewer_error_description": "Bu e-posta oluşturulurken sorun oluştu. Desteklenmeyen içerik bulunuyor olabilir.", + "composer_error": "İleti düzenleyici yüklenemedi", + "settings_error_title": "Ayarlar kullanılamıyor", + "settings_error_description": "Ayarlar yüklenemedi. Tercihleriniz kaydedilmemiş olabilir.", + "try_again": "Tekrar dene", + "reload": "Yenile", + "reload_emails": "E-postaları yenile", + "reload_settings": "Ayarları yenile", + "retry": "Yeniden dene", + "go_home": "Gelen kutusuna git" + }, + "context_menu": { + "reply": "Yanıtla", + "reply_all": "Tümünü Yanıtla", + "forward": "İlet", + "mark_read": "Okundu Olarak İşaretle", + "mark_unread": "Okunmadı Olarak İşaretle", + "star": "Yıldız Ekle", + "unstar": "Yıldızı Kaldır", + "pin": "Sabitle", + "unpin": "Sabitlemeyi kaldır", + "move_to": "Şuraya taşı...", + "copy_to_account": "Hesaba kopyala...", + "remove_from_folder": "Bu klasörden kaldır", + "archive": "Arşivle", + "delete": "Sil", + "mark_as_spam": "Spam bildir", + "not_spam": "Spam değil", + "tag": "Etiket", + "items_selected": "{count} e-posta seçildi", + "forward_as_attachments": "Ek olarak ilet", + "edit_draft": "Taslağı Düzenle", + "cancel_scheduled_send": "Göndermeyi iptal et", + "reschedule_send": "Yeniden zamanla", + "cancel_and_edit": "İptal et ve düzenle", + "cancel_and_compose_again": "İptal et ve yeniden yaz", + "rules": { + "title": "Kurallar", + "move_from": "{sender} iletilerini her zaman taşı", + "move_from_domain": "@{domain} iletilerini her zaman taşı", + "move_from_list": "Bu listenin iletilerini her zaman taşı", + "mark_read_from": "{sender} iletilerini her zaman okundu olarak işaretle", + "tag_with": "Her zaman şu etiketi ekle", + "block": "{count, plural, one {Göndereni engelle} other {# göndereni engelle}}", + "no_junk": "Bu hesapta istenmeyen posta klasörü yok", + "create": "Kural oluştur…", + "manage": "Kuralları yönet", + "senders_count": "{count, plural, one {# gönderen} other {# gönderen}}", + "cross_account": "Tek bir hesaptan iletiler seçin", + "opaque_hint": "Filtreleriniz elle düzenlenmiş. Filtre ayarlarını açın" , + "loading": "Kurallar yükleniyor…", + "load_failed": "Kurallar yüklenemedi", + "none": "Henüz kural yok", + "new_mail_only": "Yalnızca yeni iletiler için", + "running": "\"{name}\" {folder} klasöründe çalıştırılıyor…" + }, + "find_emails_from": "{name} adlı kişiden gelen e-postaları bul" + }, + "mailbox_context_menu": { + "mark_folder_read": "Klasörü okundu olarak işaretle", + "mark_folder_tree_read": "Klasörü ve alt klasörleri okundu olarak işaretle", + "mark_all_folders_read": "Tüm klasörleri okundu olarak işaretle", + "new_subfolder": "Yeni alt klasör...", + "new_folder": "Yeni klasör...", + "rename": "Yeniden adlandır...", + "share": "Paylaş...", + "import_email": ".eml veya .zip içe aktar...", + "empty_folder": "Klasörü boşalt", + "empty_folder_generic": "Klasörü boşalt", + "delete_folder": "Klasörü sil", + "refresh": "Yenile", + "hide_shared_account": "Kenar çubuğundan gizle", + "mute_notifications": "Bildirimleri sessize al", + "unmute_notifications": "Bildirimleri aç", + "mute_account_notifications": "Bu hesabın bildirimlerini sessize al", + "unmute_account_notifications": "Bu hesabın bildirimlerini aç", + "reset_folder_notifications": "Klasör bildirim ayarlarını sıfırla", + "mark_all_confirm_title": "Tüm klasörleri okundu olarak işaretle", + "mark_all_confirm_message": "Kişisel hesabınızdaki tüm okunmamış iletiler okundu olarak işaretlensin mi?", + "delete_confirm_title": "Klasörü sil", + "delete_confirm_message": "\"{name}\" klasörü kalıcı olarak silinsin mi? Bu işlem geri alınamaz.", + "prompt_new_subfolder": "Yeni alt klasör için bir ad girin.", + "prompt_new_folder": "Yeni klasör için bir ad girin.", + "prompt_rename": "Bu klasör için yeni bir ad girin.", + "placeholder_folder_name": "Klasör adı", + "create": "Oluştur", + "rename_confirm": "Yeniden Adlandır", + "toast_marked_read": "Klasör okundu olarak işaretlendi", + "toast_marked_read_count": "{count, plural, one {1 ileti} other {# ileti}} okundu olarak işaretlendi", + "toast_already_read": "Okunmamış ileti yok", + "toast_marked_all_read": "Tüm klasörler okundu olarak işaretlendi", + "toast_emptied": "Klasör boşaltıldı", + "toast_folder_created": "Klasör oluşturuldu", + "toast_folder_renamed": "Klasör yeniden adlandırıldı", + "toast_folder_deleted": "Klasör silindi", + "toast_error_mark_read": "Okundu olarak işaretlenemedi", + "toast_error_empty": "Klasör boşaltılamadı", + "toast_error_create": "Klasör oluşturulamadı", + "toast_error_rename": "Klasör yeniden adlandırılamadı", + "toast_error_delete": "Klasör silinemedi", + "toast_error_delete_has_children": "Klasörde alt klasörler var. Önce onları kaldırın.", + "toast_error_delete_has_email": "Klasör boş değil. Önce boşaltın.", + "delete_with_contents_title": "Klasör boş değil", + "delete_with_contents_message": "\"{name}\" klasöründe hâlâ {count} ileti var. Klasör ve tüm iletileri silinsin mi? Bu işlem geri alınamaz.", + "delete_with_contents_confirm": "Klasörü ve iletileri sil" + }, + "shortcuts": { + "title": "Klavye Kısayolları", + "tip": "Bu yardımı görmek için istediğiniz zaman ? tuşuna basın", + "sections": { + "navigation": "Gezinme", + "actions": "E-posta İşlemleri", + "global": "Genel", + "threads": "İleti Dizileri", + "composer": "İleti düzenleyici" + }, + "navigation": { + "next_email": "Sonraki e-posta", + "previous_email": "Önceki e-posta", + "open_email": "E-postayı aç", + "close_email": "Kapat / Seçimi kaldır", + "go_to_folder": "Klasöre git", + "next_unread": "Sonraki okunmamış e-posta", + "no_more_unread": "Listenin aşağısında okunmamış e-posta yok" + }, + "actions": { + "reply": "Yanıtla", + "reply_all": "Tümünü yanıtla", + "forward": "İlet", + "star": "Yıldızı aç/kapat", + "archive": "Arşivle", + "delete": "Sil", + "mark_unread": "Okunmadı olarak işaretle", + "mark_read": "Okundu olarak işaretle", + "toggle_spam": "Spam bildir / Spam değil" + }, + "global": { + "compose": "Yeni e-posta oluştur", + "search": "Aramaya odaklan", + "help": "Kısayolları göster", + "refresh": "E-postaları yenile", + "select_all": "Tümünü seç", + "global_search": "Her yerde ara", + "extend_selection": "Seçimi aşağı / yukarı genişlet" + }, + "threads": { + "expand_collapse": "İleti dizisini genişlet/daralt", + "select_mountain_view": "Konuşmayı seç" + }, + "composer": { + "send": "E-posta gönder", + "schedule_send": "Göndermeyi planla", + "template_picker": "Şablon seçiciyi aç" + } + }, + "threads": { + "messages_one": "{count} ileti", + "messages_other": "{count} ileti", + "messages_tooltip": "{count, plural, one {Bu konuşmada # ileti} other {Bu konuşmada # ileti}}", + "expand": "Konuşmayı genişlet", + "collapse": "Konuşmayı daralt", + "loading": "Konuşma yükleniyor...", + "mark_read": "Konuşmayı okundu olarak işaretle", + "mark_unread": "Konuşmayı okunmadı olarak işaretle", + "archive": "Konuşmayı arşivle", + "delete": "Konuşmayı sil", + "star": "Konuşmaya yıldız ekle", + "unstar": "Konuşmanın yıldızını kaldır", + "toggle_thread": "İleti dizisini aç/kapat" + }, + "identities": { + "modal_title": "Gönderme Kimliklerini Yönet", + "create_new": "Yeni Kimlik Oluştur", + "edit_identity": "Kimliği Düzenle", + "delete_confirm": "Bu kimlik silinsin mi? Bu geri alınamaz.", + "cannot_delete": "Bu kimlik silinemez", + "primary_identity": "Birincil", + "set_as_primary": "Birincil olarak ayarla", + "no_identities": "Kimlik bulunamadı", + "display": { + "reply_to": "Yanıt Adresi:", + "bcc": "GGK:", + "signature": "İmza:", + "preview": "Önizleme:" + }, + "validation_errors": { + "invalid_emails": "Geçersiz e-postalar: {emails}", + "unknown_error": "Bilinmeyen hata" + }, + "form": { + "name_label": "Görünen Ad", + "name_placeholder": "ör. İş E-postası, Kişisel", + "name_required": "Ad gerekli", + "email_label": "E-posta Adresi", + "email_placeholder": "sizin.adresiniz@example.com", + "email_required": "E-posta gerekli", + "email_invalid": "Lütfen geçerli bir e-posta adresi girin", + "email_immutable": "E-posta adresi oluşturulduktan sonra değiştirilemez", + "reply_to_label": "Yanıt Adresi (isteğe bağlı)", + "reply_to_placeholder": "baska@example.com", + "bcc_label": "Otomatik GGK (isteğe bağlı)", + "bcc_placeholder": "arsiv@example.com", + "text_signature_label": "Metin İmzası", + "html_signature_label": "HTML İmzası", + "signature_byte_counter": "{bytes} / {max} bayt", + "signature_byte_limit_reached": "Sunucu sınırına ulaşıldı", + "save": "Kimliği Kaydet", + "cancel": "İptal", + "creating": "Oluşturuluyor...", + "updating": "Güncelleniyor..." + }, + "sub_address": { + "button_tooltip": "Alt adres kullan", + "popover_title": "Alt Adres Etiketi Ekle", + "tag_input_placeholder": "Etiket girin (ör. alisveris)", + "preview_label": "Önizleme:", + "recent_tags": "Son Etiketler", + "suggested_tags": "Önerilen", + "use_address": "Bu Adresi Kullan", + "invalid_tag": "Etiket yalnızca harf, rakam ve tire içermelidir", + "tag_too_long": "Etiket en fazla 30 karakter olmalıdır", + "help_text": "kullanici{delimiter}etiket@domain.com adresine gönderilen e-postalar gelen kutunuza ulaşır", + "validation": { + "empty": "Etiket boş olamaz", + "too_long": "Etiket en fazla {max} karakter olmalıdır", + "invalid_chars": "Etiket yalnızca harf, rakam ve tire içermelidir" + } + }, + "badge": { + "sent_via": "üzerinden", + "sub_address_tag": "Alt adres kullanılarak gönderildi: {tag}", + "identity_name": "Kimlik kullanılarak gönderildi: {name}", + "identity_short": "{name} üzerinden", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Sil", + "delete_confirm_title": "Kimliği Sil" + }, + "templates": { + "picker_title": "Şablon Seçin", + "search_placeholder": "Şablonlarda ara...", + "section_favorites": "Favoriler", + "section_recent": "Son Kullanılan", + "section_uncategorized": "Diğer", + "no_templates": "Henüz şablon yok", + "no_results": "Şablon bulunamadı", + "fill_placeholders": "Yer Tutucu Değerleri Doldurun", + "enter_value": "Değer girin...", + "preview": "Önizleme", + "insert_with_values": "Değerlerle Ekle", + "insert_raw": "Ham Ekle", + "copy_suffix": "(kopya)", + "placeholder": "Değişken", + "placeholders": { + "recipient_name": "Alıcı adı", + "company": "Şirket adı", + "date": "Bugünün tarihi", + "day_of_week": "Haftanın günü", + "sender_name": "Adınız" + } + }, + "contacts": { + "directory": { + "title": "Dizin", + "search_placeholder": "Ad veya e-posta adresi ara", + "refresh": "Dizini yenile", + "retry": "Tekrar dene", + "loading": "Dizin yükleniyor…", + "unavailable_title": "Dizin yok", + "unavailable_hint": "Posta sunucunuz hesap dizini sunmuyor.", + "forbidden_title": "Dizin kullanılamıyor", + "forbidden_hint": "Posta sunucunuz bu hesabın dizine göz atmasına izin vermiyor. Yöneticinize danışın.", + "error_title": "Dizin yüklenemedi", + "error_hint": "Bağlantınızı kontrol edip tekrar deneyin.", + "empty_title": "Dizin boş", + "empty_hint": "Posta sunucunuz e-posta adresi olan hiçbir hesap listelemiyor.", + "no_match": "Dizinde aramanızla eşleşen kimse yok", + "refresh_failed": "Dizin yenilenemedi. Son yüklenen liste gösteriliyor.", + "select_page": "Bu sayfadaki herkesi seç", + "deselect_page": "Bu sayfadaki seçimi kaldır", + "select_entry": "{name} kişisini seç", + "range": "{from}–{to} / {total}", + "page": "Sayfa {page} / {count}", + "previous_page": "Önceki sayfa", + "next_page": "Sonraki sayfa", + "picker_title": "Dizinden seç", + "cancel": "İptal", + "selected_count": "{count} seçildi", + "add_to": "Kime alanına ekle", + "add_cc": "Bilgi alanına ekle", + "add_bcc": "Gizli alanına ekle", + "select_hint": "Ayrıntıları görmek için birini seçin", + "write_email": "E-posta yaz", + "add_to_contacts": "Kişilere ekle", + "email": "E-posta", + "read_only_note": "Dizin girdileri posta sunucunuzdan gelir ve burada düzenlenemez.", + "type": { + "individual": "Kişi", + "group": "Grup", + "resource": "Kaynak", + "location": "Konum", + "other": "Diğer" + } + }, + "title": "Kişiler", + "search_placeholder": "Kişilerde ara...", + "create_new": "Yeni Kişi", + "no_category": "Kategori Yok", + "rename_category": "Kategoriyi yeniden adlandır", + "category_name_label": "Kategori adı", + "category_renamed": "Kategori yeniden adlandırıldı", + "category_rename_failed": "Kategori yeniden adlandırılamadı", + "category_added": "{name} kategorisine kişi eklendi", + "category_added_plural": "{name} kategorisine {count} kişi eklendi", + "empty_state": "Henüz kişi yok", + "empty_state_title": "Henüz kişi yok", + "empty_state_subtitle": "İlk kişinizi oluşturun veya vCard dosyasından içe aktarın", + "empty_search": "Aramanızla eşleşen kişi yok", + "empty_search_hint": "Farklı bir arama terimi deneyin", + "empty_filtered": "Filtrelerinizle eşleşen kişi yok", + "empty_filtered_hint": "Filtreleri ayarlamayı veya temizlemeyi deneyin", + "clear_search": "Aramayı temizle", + "import_vcard": "vCard İçe Aktar", + "delete_confirm_title": "Kişiyi sil", + "delete_confirm": "Bu kişiyi silmek istediğinizden emin misiniz?", + "local_mode": "Kişiler yerel olarak saklanıyor (sunucu JMAP Kişilerini desteklemiyor)", + "back_to_contacts": "Kişilere geri dön", + "back_to_email": "E-postaya geri dön", + "tabs": { + "all": "Tümü", + "groups": "Gruplar" + }, + "shared": { + "title": "Paylaşılan" + }, + "address_books": { + "create": "Yeni adres defteri", + "created": "Adres defteri oluşturuldu", + "create_failed": "Adres defteri oluşturulamadı", + "title": "Adres Defterlerim", + "shared_prefix": "Paylaşılan: {name}", + "moved": "Kişi {name} konumuna taşındı", + "moved_plural": "{count} kişi {name} konumuna taşındı", + "move_failed": "Kişi taşınamadı", + "address_book": "Adres Defteri", + "rename": "Adres defterini yeniden adlandır", + "name_label": "Adres defteri adı", + "renamed": "Adres defteri yeniden adlandırıldı", + "rename_failed": "Adres defteri yeniden adlandırılamadı", + "default": "Varsayılan", + "set_default": "Varsayılan olarak ayarla", + "default_updated": "Varsayılan adres defteri güncellendi", + "set_default_failed": "Varsayılan adres defteri ayarlanamadı", + "manage": "Adres defterlerini yönet", + "share": "Adres defterini paylaş", + "new_contact_in_book": "Bu adres defterinde yeni kişi", + "delete": "Adres defterini sil", + "confirm_delete": "\"{name}\" silinsin mi? Bu adres defterindeki tüm kişiler kaldırılacak.", + "deleted": "Adres defteri silindi", + "delete_failed": "Adres defteri silinemedi" + }, + "detail": { + "emails": "E-posta Adresleri", + "phones": "Telefon Numaraları", + "organizations": "Kuruluşlar", + "addresses": "Adresler", + "notes": "Notlar", + "titles": "Unvanlar ve Roller", + "online_services": "Çevrimiçi Hizmetler", + "anniversaries": "Yıl Dönümleri", + "personal_info": "Kişisel Bilgi", + "languages": "Diller", + "categories": "Kategoriler", + "related_contacts": "İlgili Kişiler", + "crypto_keys": "Şifreleme Anahtarları", + "cert_issuer": "Veren", + "cert_expires": "Son Kullanma", + "cert_expired": "Süresi Doldu", + "cert_fingerprint": "Parmak İzi", + "cert_algorithm": "Algoritma", + "import_to_smime": "S/MIME'ye İçe Aktar", + "cert_already_imported": "S/MIME'ye zaten aktarıldı", + "cert_imported": "Sertifika S/MIME deposuna aktarıldı", + "cert_import_failed": "Sertifika içe aktarılamadı", + "no_contact_selected": "Ayrıntıları görüntülemek için bir kişi seçin", + "compose_email": "E-posta oluştur", + "copy_email": "E-postayı kopyala", + "copy_phone": "Telefon numarasını kopyala", + "copy_url": "URL'yi kopyala", + "copied": "Panoya kopyalandı", + "copy_failed": "Panoya kopyalanamadı", + "created": "Oluşturuldu", + "updated": "Son güncelleme", + "timezone": "Saat Dilimi", + "anniversary_birth": "Doğum Günü", + "anniversary_death": "Vefat", + "anniversary_wedding": "Yıl Dönümü", + "anniversary_other": "Diğer", + "personal_expertise": "Uzmanlık", + "personal_hobby": "Hobi", + "personal_interest": "İlgi Alanı", + "personal_other": "Diğer", + "gender": "Cinsiyet", + "gender_masculine": "Erkek", + "gender_feminine": "Kadın", + "gender_other": "Diğer", + "gender_none": "Geçerli değil", + "gender_unknown": "Bilinmiyor", + "calendar": "Takvim", + "calendar_uri": "Takvim URL'si", + "scheduling_uri": "Zamanlama URL'si", + "freebusy_uri": "Müsait/Meşgul URL'si", + "section_contact": "İletişim bilgileri", + "section_work": "İş", + "section_personal": "Kişisel", + "email_default_label": "E-posta", + "phone_default_label": "Telefon", + "address_default_label": "Adres", + "online_service_default_label": "Çevrimiçi", + "organization_label": "Kuruluş", + "title_label": "Unvan", + "role_label": "Rol", + "language_label": "Dil", + "related_default_label": "İlgili", + "more_actions": "Diğer işlemler", + "age_years": "{count, plural, one {1 yaşında} other {# yaşında}}", + "years_since": "{count, plural, one {1 yıl} other {# yıl}}" + }, + "activity": { + "recent_emails": "Son E-postalar", + "upcoming_events": "Yaklaşan Etkinlikler", + "no_emails": "Son e-posta yok", + "no_events": "Yaklaşan etkinlik yok", + "no_subject": "(Konu yok)", + "no_title": "(Başlık yok)", + "load_failed": "Yüklenemedi", + "unknown_sender": "Bilinmeyen gönderici", + "all_day": "Tüm gün" + }, + "form": { + "create_title": "Yeni Kişi", + "edit_title": "Kişiyi Düzenle", + "section_address_book": "Dizin", + "select_address_book": "Bir dizin seçin...", + "section_identity": "Ad ve Kimlik", + "contact_type": "Kişi türü", + "type_person": "Kişi", + "type_organization": "Kuruluş", + "section_work": "İş ve Kuruluş", + "prefix": "Ön Ek", + "prefix_placeholder": "Dr., Bay, Bayan", + "given_name": "Ad", + "middle_name": "Orta ad", + "surname": "Soyadı", + "suffix": "Son Ek", + "suffix_placeholder": "Jr., Sr., III", + "nickname": "Takma Ad", + "nickname_placeholder": "Takma ad", + "email": "E-posta", + "email_placeholder": "email@example.com", + "phone": "Telefon", + "phone_placeholder": "+90 555 123 45 67", + "phone_type": "Tür", + "phone_voice": "Sesli", + "phone_cell": "Mobil", + "phone_fax": "Faks", + "phone_pager": "Çağrı Cihazı", + "phone_video": "Video", + "phone_text": "Metin", + "organization": "Kuruluş", + "organization_placeholder": "Şirket adı", + "department": "Departman", + "department_placeholder": "Departman", + "job_title": "İş unvanı", + "job_title_placeholder": "ör. Yazılım Mühendisi", + "role": "Rol", + "role_placeholder": "ör. Takım Lideri", + "addresses": "Adresler", + "add_address": "Adres ekle", + "street": "Sokak", + "city": "Şehir", + "region": "İl / Bölge", + "postcode": "Posta kodu", + "country": "Ülke", + "online_services": "Çevrimiçi Hizmetler", + "add_online_service": "Çevrimiçi hizmet ekle", + "url_placeholder": "https://...", + "service_placeholder": "Hizmet", + "anniversaries": "Yıl Dönümleri", + "add_anniversary": "Tarih ekle", + "anniversary_birth": "Doğum Günü", + "anniversary_wedding": "Yıl Dönümü", + "anniversary_death": "Anma Günü", + "anniversary_other": "Diğer", + "personal_info": "Kişisel Bilgi", + "add_personal_info": "Giriş ekle", + "personal_info_placeholder": "ör. Fotoğrafçılık", + "personal_expertise": "Uzmanlık", + "personal_hobby": "Hobi", + "personal_interest": "İlgi Alanı", + "personal_other": "Diğer", + "level": "Seviye", + "level_high": "Yüksek", + "level_medium": "Orta", + "level_low": "Düşük", + "categories": "Kategoriler", + "categories_placeholder": "ör. Aile, Arkadaşlar, Meslektaşlar", + "categories_hint": "Kategori aramak veya eklemek için yazın", + "category_add": "Ekle", + "note": "Notlar", + "note_placeholder": "Not ekle...", + "gender": "Cinsiyet", + "gender_sex": "Biyolojik Cinsiyet", + "gender_male": "Erkek", + "gender_female": "Kadın", + "gender_other": "Diğer", + "gender_none": "Geçerli değil", + "gender_unknown": "Bilinmiyor", + "gender_identity": "Cinsiyet kimliği", + "gender_identity_placeholder": "Cinsiyet kimliği...", + "calendar": "Takvim", + "calendar_uri": "Takvim URL'si", + "scheduling_uri": "Zamanlama URL'si", + "freebusy_uri": "Müsait/Meşgul URL'si", + "context_work": "İş", + "context_private": "Özel", + "add_email": "E-posta ekle", + "add_phone": "Telefon ekle", + "save": "Kaydet", + "cancel": "İptal", + "creating": "Oluşturuluyor...", + "updating": "Güncelleniyor...", + "name_required": "Bir ad, soyad veya kuruluş girin", + "email_invalid": "Lütfen geçerli bir e-posta adresi girin", + "email_error_inline": "Geçersiz e-posta biçimi", + "save_failed": "Kişi kaydedilemedi", + "delete": "Sil", + "upload_photo": "Fotoğraf yükle", + "remove_photo": "Fotoğrafı kaldır", + "photo_hint": "JPG veya PNG, en fazla 10 MB. Yeniden boyutlandırılacak.", + "photo_too_large": "Görüntü çok büyük (maks. 10 MB)", + "photo_invalid": "Geçersiz görüntü dosyası", + "change_photo": "Değiştir" + }, + "groups": { + "create": "Yeni Grup", + "edit": "Grubu Düzenle", + "empty": "Henüz grup yok", + "delete_confirm_title": "Grubu sil", + "delete_confirm": "Bu grubu silmek istediğinizden emin misiniz?", + "name_label": "Grup Adı", + "name_placeholder": "ör. Takım, Aile", + "name_required": "Grup adı gerekli", + "save_failed": "Grup kaydedilemedi", + "members_label": "Üyeler", + "search_members": "Eklemek için kişileri arayın...", + "no_members": "Bu grupta üye yok", + "member_count": "{count, plural, =0 {Üye yok} one {1 üye} other {# üye}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Kişileri İçe Aktar", + "drop_hint": "vCard dosyası seçmek için tıklayın", + "file_types": ".vcf veya .vcard dosyaları", + "no_contacts": "Dosyada kişi bulunamadı", + "parse_error": "vCard dosyası ayrıştırılamadı", + "found": "{count, plural, one {1 kişi bulundu} other {# kişi bulundu}}", + "duplicate": "Yinelenen", + "select_all": "Tümünü seç", + "deselect_all": "Tüm seçimi kaldır", + "selected": "{count, plural, one {1 seçildi} other {# seçildi}}", + "import_button": "İçe Aktar", + "importing": "İçe aktarılıyor...", + "success": "{count, plural, one {1 kişi içe aktarıldı} other {# kişi içe aktarıldı}}", + "failed": "İçe aktarma başarısız", + "close": "Kapat", + "file_too_large": "Dosya çok büyük (maks. 5 MB)" + }, + "export": { + "title": "Kişileri Dışa Aktar", + "success": "{count, plural, one {1 kişi dışa aktarıldı} other {# kişi dışa aktarıldı}}" + }, + "bulk": { + "selected": "{count, plural, one {1 seçildi} other {# seçildi}}", + "select_all": "Tümünü seç", + "delete": "Sil", + "delete_confirm_title": "Kişileri sil", + "delete_confirm": "{count, plural, one {1 kişi} other {# kişi}} silinsin mi?", + "deleted": "{count, plural, one {1 kişi silindi} other {# kişi silindi}}", + "add_to_group": "Gruba ekle", + "choose_group": "Grup seçin", + "adding_contacts": "{count, plural, one {1 kişi} other {# kişi}} ekleniyor", + "added_to_group": "Kişiler gruba eklendi", + "export": "Dışa Aktar", + "clear": "Seçimi temizle" + }, + "toast": { + "created": "Kişi oluşturuldu", + "updated": "Kişi güncellendi", + "deleted": "Kişi silindi", + "error_create": "Kişi oluşturulamadı", + "error_update": "Kişi güncellenemedi", + "error_delete": "Kişi silinemedi" + }, + "context_menu": { + "open": "Aç", + "edit": "Düzenle", + "send_email": "E-posta gönder", + "add_to_group": "Gruba ekle", + "export_vcard": "vCard olarak dışa aktar", + "delete": "Sil", + "call": "Ara", + "duplicate": "Çoğalt", + "print": "Yazdır" + }, + "filters": { + "toggle": "Filtreler", + "select": "Seçin", + "clear": "Temizle", + "close": "Kapat", + "title": "Gelişmiş filtreler", + "organization": "Şirket", + "organization_placeholder": "ör. Örnek A.Ş.", + "job_title": "İş unvanı", + "job_title_placeholder": "ör. Tasarımcı", + "location": "Konum", + "location_placeholder": "Şehir veya ülke", + "email_domain": "E-posta alan adı", + "email_domain_placeholder": "example.com", + "birthday_month": "Doğum günü", + "any_month": "Herhangi bir ay", + "has_email": "E-postası var", + "has_phone": "Telefonu var", + "has_photo": "Fotoğrafı var", + "sort_by": "Sıralama", + "sort_first_name": "Ad", + "sort_last_name": "Soyad" + }, + "open_categories": "Kategorileri aç" + }, + "calendar": { + "title": "Takvim", + "back_to_email": "E-postaya geri dön", + "back_to_month": "Aya geri dön", + "my_calendars": "Takvimler", + "birthday_calendar": "Doğum Günleri", + "mini_calendar_change": "Ayı değiştirmek için tıklayın", + "views": { + "month": "Ay", + "week": "Hafta", + "day": "Gün", + "agenda": "Gündem", + "today": "Bugün", + "month_hint": "Ay (m)", + "week_hint": "Hafta (w)", + "day_hint": "Gün (d)", + "agenda_hint": "Gündem (a)", + "tasks": "Görevler", + "tasks_hint": "Görevler (k)" + }, + "events": { + "hidden_before": "{count, plural, other {# önceki etkinlik}}", + "hidden_after": "{count, plural, other {# sonraki etkinlik}}", + "show_all_hours": "Tüm saatleri göster", + "show_display_hours": "Yalnızca {start} – {end} göster", + "create": "Etkinlik oluştur", + "edit": "Etkinliği düzenle", + "delete": "Etkinliği sil", + "details": "Etkinlik ayrıntıları", + "no_events": "Etkinlik yok", + "all_day": "Tüm gün", + "more": "+{count} daha", + "show_more": "Daha fazla göster", + "show_less": "Daha az göster", + "no_title": "(Başlık yok)", + "resize": "Etkinliği yeniden boyutlandır", + "duplicate": "Çoğalt", + "today_header": "Bugün", + "tomorrow_header": "Yarın", + "agenda_show_earlier": "Önceki etkinlikleri göster", + "agenda_loading": "Yükleniyor…", + "agenda_range_start": "{date} tarihinden itibaren etkinlikler", + "agenda_range_end": "{date} tarihine kadar etkinlikler", + "export_ics": ".ics olarak dışa aktar", + "copy_title": "Başlığı kopyala", + "copy_link": "Toplantı bağlantısını kopyala", + "new_event": "Yeni etkinlik", + "new_all_day_event": "Yeni tüm gün etkinlik", + "new_task": "Yeni görev", + "go_to_today": "Bugüne git" + }, + "detail": { + "add_note": "Not ekle...", + "save_note": "Kaydet", + "note_saved": "Not eklendi", + "open_link": "Bağlantıyı aç", + "copy_location": "Konumu kopyala", + "meeting_link": "Toplantı bağlantısı", + "tentative": "Geçici", + "cancelled": "İptal edildi", + "delete_confirm": "Bu etkinlik silinsin mi?" + }, + "form": { + "title": "Başlık", + "description": "Açıklama", + "location": "Konum", + "meeting_link": "Toplantı bağlantısı", + "start_date": "Başlangıç tarihi", + "end_date": "Bitiş tarihi", + "start_time": "Başlangıç saati", + "end_time": "Bitiş saati", + "all_day_event": "Tüm gün etkinliği", + "calendar_select": "Takvim", + "save": "Kaydet", + "cancel": "İptal", + "delete_confirm": "Bu etkinliği silmek istediğinizden emin misiniz?", + "color": "Renk" + }, + "participants": { + "availability": { + "title": "Uygunluk", + "free": "Uygun", + "busy": "Meşgul", + "tentative": "Geçici", + "unknown": "Bu sunucuda kullanıcı değil", + "checking": "Kontrol ediliyor…" + }, + "title": "Katılımcılar", + "add": "Katılımcı ekle", + "organizer": "Organizatör", + "attendee": "Katılımcı", + "accepted": "Kabul edildi", + "declined": "Reddedildi", + "tentative": "Geçici", + "needs_action": "İşlem gerekiyor", + "remove": "Kaldır", + "edit": "Düzenle", + "email_placeholder": "E-posta adresi ekleyin veya kişileri arayın", + "send_invitations": "Katılımcılara davet gönder", + "status_summary": "{accepted} kabul etti, {pending} bekliyor", + "invited_by": "{name} tarafından davet", + "respond_below": "Aşağıdaki düğmeleri kullanarak yanıt verin", + "rsvp_label": "Yanıtınız", + "cancel_notification": "Katılımcılar iptal hakkında bilgilendirilecek", + "you_organizer": "Siz organizatörsünüz", + "you_attendee": "Siz bir katılımcısınız", + "no_participants": "Katılımcı yok", + "count": "{count, plural, one {# katılımcı} other {# katılımcı}}" + }, + "recurrence": { + "title": "Yineleme", + "none": "Tekrarlamaz", + "daily": "Günlük", + "weekly": "Haftalık", + "monthly": "Aylık", + "yearly": "Yıllık", + "every_n_days": "Her {count} günde bir", + "every_n_weeks": "Her {count} haftada bir", + "every_n_months": "Her {count} ayda bir", + "until": "Şu tarihe kadar", + "occurrences": "{count} tekrar", + "custom": "Özel…", + "edit_custom": "Özel yinelemeyi düzenle", + "every_n_years": "Her {count} yılda bir", + "on_days": "{days} günleri", + "on_day_n": "ayın {day}. günü", + "on_the_nth": "{nth} {day} günü", + "in_month": "{month} ayında", + "nth_1": "ilk", + "nth_2": "ikinci", + "nth_3": "üçüncü", + "nth_4": "dördüncü", + "nth_last": "son", + "editor_freq_day": "Gün", + "editor_freq_week": "Hafta", + "editor_freq_month": "Ay", + "editor_freq_year": "Yıl", + "editor_repeats_on": "Yinelenme", + "editor_every": "Her", + "editor_unit_days": "günde bir", + "editor_unit_weeks": "haftada bir", + "editor_unit_months": "ayda bir", + "editor_unit_years": "yılda bir", + "editor_on_day": "ayın şu günü:", + "editor_on_the": "şu gün:", + "editor_in": "ay:", + "editor_ends": "Bitiş", + "editor_never": "Asla", + "editor_ends_on": "Tarihinde", + "editor_ends_after": "Sonra:", + "editor_occurrences": "tekrar" + }, + "recurrence_scope": { + "edit_title": "Yinelenen etkinliği düzenle", + "delete_title": "Yinelenen etkinliği sil", + "description": "Bu yinelenen bir etkinlik. Hangi etkinlikleri değiştirmek istersiniz?", + "this_event": "Yalnızca bu etkinlik", + "this_and_future": "Bu ve sonraki etkinlikler", + "all_events": "Tüm etkinlikler", + "cancel": "İptal", + "save": "Kaydet", + "delete": "Sil", + "rsvp_title": "Yinelenen etkinliğe yanıt ver", + "rsvp_description": "Bu yinelenen bir etkinlik. Yanıtınız hangi etkinlikler için geçerli?", + "respond": "Yanıtla" + }, + "alerts": { + "title": "Hatırlatıcı", + "none": "Hatırlatıcı yok", + "at_time": "Etkinlik saatinde", + "minutes_before": "{count, plural, one {# dakika önce} other {# dakika önce}}", + "hours_before": "{count, plural, one {# saat önce} other {# saat önce}}", + "days_before": "{count, plural, one {# gün önce} other {# gün önce}}", + "weeks_before": "{count, plural, one {# hafta önce} other {# hafta önce}}", + "unit_minutes_before": "dakika önce", + "unit_hours_before": "saat önce", + "unit_days_before": "gün önce", + "unit_weeks_before": "hafta önce", + "add": "Hatırlatıcı ekle", + "remove": "Hatırlatıcıyı kaldır", + "amount": "Hatırlatıcı miktarı", + "unit": "Hatırlatıcı birimi", + "action": "Hatırlatıcı türü", + "action_display": "Bildirim", + "action_email": "E-posta", + "by_email": "E-postayla {label}", + "kept": "{count, plural, one {Burada düzenlenemeyen # hatırlatıcı daha korunuyor} other {Burada düzenlenemeyen # hatırlatıcı daha korunuyor}}", + "other": "{count, plural, one {# hatırlatıcı daha} other {# hatırlatıcı daha}}", + "calendar_defaults": "Takvim varsayılanları", + "customize": "Özelleştir", + "use_defaults": "Takvim varsayılanlarını kullan" + }, + "settings": { + "limit_hours": "Görünür saatleri sınırla", + "limit_hours_desc": "Gün ve hafta görünümlerinde yalnızca bu saatleri göster. Bunların dışındaki etkinliklere her günün üstündeki ve altındaki oklarla ulaşılabilir.", + "visible_hours": "Görünür saatler", + "visible_hours_start": "Görünür saatlerin başlangıcı", + "visible_hours_end": "Görünür saatlerin bitişi", + "hide_non_working_days": "Çalışılmayan günleri gizle", + "hide_non_working_days_desc": "Çalışmadığınız günleri hafta görünümünden çıkar. Bu günlerin etkinlikleri ay, gün ve ajanda görünümlerinde görünmeye devam eder.", + "working_days": "Çalışma günleri", + "title": "Takvim ayarları", + "default_view": "Varsayılan görünüm", + "week_starts_on": "Hafta başlangıcı", + "time_format": "Saat biçimi", + "default_calendar": "Varsayılan takvim", + "default_reminder": "Varsayılan hatırlatıcı", + "time_format_12h": "12 saatlik", + "time_format_24h": "24 saatlik", + "notifications_enabled": "Etkinlik bildirimleri", + "notifications_enabled_desc": "Yaklaşan takvim etkinlikleri için uyarılar göster", + "notification_sound": "Bildirim sesi", + "notification_sound_desc": "Takvim uyarıları için ses çal", + "invitation_parsing": "E-posta davetlerini ayrıştır", + "invitation_parsing_desc": "E-posta eklerindeki takvim davetlerini algıla ve takvim işlemlerini göster", + "show_time_in_month_view": "Ay görünümünde saati göster", + "show_time_in_month_view_desc": "Aylık takvim görünümünde etkinlik saatlerini görüntüle", + "show_week_numbers": "Hafta numaralarını göster", + "show_week_numbers_desc": "Mini takvimde hafta numaralarını görüntüle", + "calendar_free_scroll": "Serbest kaydırma", + "calendar_free_scroll_desc": "Tek seferde bir dönem yerine aylar, haftalar ve günler arasında kesintisiz kaydırın", + "enable_tasks": "Görevleri etkinleştir", + "enable_tasks_desc": "Yapılacaklar listesini yönetmek için takvimde görev görünümü göster", + "show_tasks_on_calendar": "Görevleri takvimde göster", + "show_tasks_on_calendar_desc": "Görevleri ay, hafta ve gün takvim görünümlerinde görüntüle", + "hover_preview": "Etkinlik üzerine gelme önizlemesi", + "hover_preview_desc": "Takvim etkinliklerinin üzerine gelindiğinde ayrıntı açılır penceresi göster", + "hover_preview_instant": "Anında", + "hover_preview_delay_500ms": "0,5 saniyelik gecikme", + "hover_preview_delay_1s": "1 saniyelik gecikme", + "hover_preview_delay_2s": "2 saniyelik gecikme", + "hover_preview_off": "Devre dışı", + "organizer_identity": "Davetleri şu kimlikle düzenle", + "organizer_identity_desc": "Kişileri etkinliklere davet ederken düzenleyici olarak kullanılan takvim adresi.", + "organizer_identity_failed": "Düzenleyici adresi değiştirilemedi", + "show_birthday_calendar": "Kişi doğum günü takvimi", + "show_birthday_calendar_desc": "Kişilerinizdeki doğum günlerini gösteren sanal takvim göster" + }, + "days": { + "monday": "Pazartesi", + "tuesday": "Salı", + "wednesday": "Çarşamba", + "thursday": "Perşembe", + "friday": "Cuma", + "saturday": "Cumartesi", + "sunday": "Pazar", + "mon": "Pzt", + "tue": "Sal", + "wed": "Çar", + "thu": "Per", + "fri": "Cum", + "sat": "Cmt", + "sun": "Paz" + }, + "months": { + "jan": "Oca", + "feb": "Şub", + "mar": "Mar", + "apr": "Nis", + "may": "May", + "jun": "Haz", + "jul": "Tem", + "aug": "Ağu", + "sep": "Eyl", + "oct": "Eki", + "nov": "Kas", + "dec": "Ara", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "Etkinlik oluşturuldu", + "event_updated": "Etkinlik güncellendi", + "event_deleted": "Etkinlik silindi", + "calendar_created": "Takvim oluşturuldu", + "calendar_deleted": "Takvim silindi", + "event_move_error": "Etkinlik taşınamadı", + "event_resize_error": "Etkinlik yeniden boyutlandırılamadı", + "alert_title": "Yaklaşan etkinlik", + "alert_now": "Şimdi başlıyor", + "alert_in_minutes": "{count} dk içinde", + "invitation_sent": "Davetler gönderildi", + "invitations_denied_title": "Davetler gönderilemiyor", + "invitations_denied": "Sunucu davetleri göndermeyi reddetti: {reason}. Etkinlik davet gönderilmeden kaydedilsin mi?", + "save_without_invitations": "Davetsiz kaydet", + "rsvp_updated": "Yanıt güncellendi", + "rsvp_error": "Yanıt güncellenemedi", + "event_duplicated": "Etkinlik çoğaltıldı", + "event_error": "Etkinlik kaydedilemedi", + "task_due": "Görev süresi doldu", + "event_exported": "Etkinlik dışa aktarıldı", + "title_copied": "Başlık kopyalandı", + "link_copied": "Bağlantı kopyalandı" + }, + "status": { + "loading_calendars": "Takvimler yükleniyor...", + "loading_events": "Etkinlikler yükleniyor..." + }, + "quick_create": { + "placeholder": "Yeni etkinlik başlığı", + "aria_label": "Hızlı etkinlik oluştur", + "add_title": "Başlık ekle", + "more_options": "Diğer seçenekler" + }, + "nav_prev": "Önceki", + "nav_next": "Sonraki", + "import": { + "title": "Takvim İçe Aktar", + "tab_file": "Dosya", + "tab_url": "URL", + "select_file": ".ics dosyası seçin", + "drop_file": "veya dosyayı buraya bırakın", + "supported_formats": "iCalendar (.ics) dosyalarını destekler", + "url_description": "Etkinlikleri içe aktarmak için harici iCalendar (.ics) akışının URL'sini girin.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "iCalendar (.ics) URL'lerini destekler", + "fetch": "Getir", + "invalid_url": "Lütfen geçerli bir URL girin", + "url_fetch_failed": "URL'den takvim getirilemedi", + "parsing": "Takvim dosyası ayrıştırılıyor...", + "parsed_events": "{count} etkinlik bulundu", + "no_events": "Dosyada etkinlik bulunamadı", + "select_all": "Tümünü seç", + "deselect_all": "Tüm seçimi kaldır", + "target_calendar": "Takvime içe aktar", + "import_button": "Seçilenleri içe aktar", + "importing": "Etkinlikler içe aktarılıyor...", + "success": "{count} etkinlik başarıyla içe aktarıldı", + "error": "Takvim içe aktarılamadı", + "file_too_large": "Dosya 10 MB sınırını aşıyor", + "invalid_format": "Geçersiz takvim dosyası biçimi" + }, + "webcal_action": { + "title": "Takvim bağlantısını aç", + "description": "\"{name}\" öğesini nasıl kullanmak istersiniz?", + "import_title": "Bir kez içe aktar", + "import_description": "Etkinlikleri şimdi alıp takvimlerinizden birine kopyalayın.", + "subscribe_title": "Abone ol", + "subscribe_description": "Bu takvimi ayrı bir takvim olarak otomatik eşitlenmiş tutun.", + "cancel": "İptal" + }, + "management": { + "title": "Takvim Yönetimi", + "description": "Takvimlerinizi oluşturun, yeniden adlandırın ve özelleştirin. Rengini hızlıca değiştirmek için kenar çubuğundaki bir takvime sağ tıklayın.", + "name": "Ad", + "name_placeholder": "Takvim adı", + "color": "Renk", + "change_color": "Rengi değiştir", + "random_color": "Yeni rastgele renk", + "add_calendar": "Takvim ekle", + "edit": "Düzenle", + "delete": "Sil", + "save": "Kaydet", + "create": "Oluştur", + "cancel": "İptal", + "default": "Varsayılan", + "confirm_delete": "\"{name}\" silinsin mi? Bu takvimdeki tüm etkinlikler kaldırılacak.", + "confirm_clear": "\"{name}\" takvimdeki tüm etkinlikler temizlensin mi? Bu geri alınamaz.", + "set_default": "Varsayılan olarak ayarla", + "default_updated": "Varsayılan takvim güncellendi", + "error_default": "Varsayılan takvim ayarlanamadı", + "clear_events": "Etkinlikleri temizle", + "events_cleared": "{count} etkinlik temizlendi", + "error_clear": "Takvim etkinlikleri temizlenemedi", + "calendar_created": "Takvim oluşturuldu", + "calendar_updated": "Takvim güncellendi", + "calendar_deleted": "Takvim silindi", + "color_updated": "Takvim rengi güncellendi", + "error_create": "Takvim oluşturulamadı", + "error_update": "Takvim güncellenemedi", + "error_delete": "Takvim silinemedi", + "caldav_url": "CalDAV URL'si", + "copy_url": "CalDAV URL'sini kopyala", + "url_copied": "CalDAV URL'si panoya kopyalandı", + "share": "Takvimi paylaş", + "new_event_in_calendar": "Bu takvimde yeni etkinlik", + "shared_with": "Paylaşılan kişiler: {names}", + "shared_with_count": "{count, plural, one {1 kişiyle paylaşıldı} other {# kişiyle paylaşıldı}}", + "move_up": "Yukarı taşı", + "move_down": "Aşağı taşı", + "kind": "İçerik", + "kind_events": "Etkinlikler", + "kind_tasks": "Görevler", + "kind_both": "Etkinlikler ve görevler", + "kind_hint": "Bu takvimin CalDAV üzerinden eşitlendiğinde hangi uygulamalar tarafından görüleceğini belirler. Daha sonra değiştirilemez.", + "default_alerts": "Varsayılan hatırlatıcılar", + "default_alerts_title": "\"{name}\" için varsayılan hatırlatıcılar", + "default_alerts_hint": "Bu takvimdeki yeni etkinlikler, etkinlikte değiştirilmedikçe bu hatırlatıcıları alır. Buradaki değişiklikler mevcut etkinlikleri etkilemez. E-posta hatırlatıcıları posta sunucusu tarafından gönderilir.", + "default_alerts_timed": "Saatli etkinlikler", + "default_alerts_all_day": "Tüm gün süren etkinlikler", + "default_alerts_updated": "Varsayılan hatırlatıcılar güncellendi" + }, + "subscription": { + "title": "iCal Aboneliği", + "section_title": "iCal Abonelikleri", + "description": "Harici iCalendar akışına abone olun. Etkinlikler kendi takvimlerine otomatik olarak senkronize edilecek. https:// ve webcal:// URL'lerini destekler.", + "url_label": "Takvim URL'si", + "url_placeholder": "https://example.com/calendar.ics veya webcal://...", + "name_label": "Takvim adı", + "name_placeholder": "ör. Resmi Tatiller", + "color_label": "Renk", + "refresh_interval": "Yenileme aralığı", + "interval_15": "Her 15 dakikada bir", + "interval_30": "Her 30 dakikada bir", + "interval_60": "Her saat", + "interval_360": "Her 6 saatte bir", + "interval_1440": "Her gün", + "subscribe": "Abone Ol", + "subscribing": "Abone olunuyor...", + "save": "Değişiklikleri kaydet", + "saving": "Kaydediliyor...", + "edit": "Düzenle", + "edit_title": "Aboneliği Düzenle", + "updated": "\"{name}\" güncellendi", + "update_error": "Abonelik güncellenemedi", + "invalid_url": "Lütfen geçerli bir URL girin", + "success": "\"{name}\" aboneliğine girildi", + "error": "Abonelik eklenemedi", + "refresh": "Şimdi yenile", + "refresh_success": "Abonelik yenilendi", + "refresh_error": "Abonelik yenilenemedi", + "unsubscribe": "Aboneliği İptal Et", + "confirm_delete": "\"{name}\" aboneliği iptal edilsin mi? Takvim ve tüm etkinlikleri kaldırılacak.", + "deleted": "Abonelik kaldırıldı", + "delete_error": "Abonelik kaldırılamadı", + "last_refreshed": "Son güncelleme: {time}" + }, + "tasks": { + "label": "Görevler", + "no_tasks": "Görev yok", + "no_title": "(Başlık yok)", + "mark_complete": "Tamamlandı olarak işaretle", + "mark_incomplete": "Tamamlanmadı olarak işaretle", + "filter_all": "Tümü", + "filter_pending": "Bekleyen", + "filter_completed": "Tamamlanan", + "filter_overdue": "Geciken", + "show_completed": "Tamamlananları göster", + "create": "Yeni Görev", + "edit": "Görevi Düzenle", + "title_placeholder": "Görev başlığı", + "description_placeholder": "Açıklama ekle...", + "due_date": "Son tarih", + "include_time": "Saati dahil et", + "priority": "Öncelik", + "priority_none": "Yok", + "priority_high": "Yüksek", + "priority_medium": "Orta", + "priority_low": "Düşük", + "progress": "Durum", + "progress_needs_action": "İşlem gerekiyor", + "progress_in_process": "Devam ediyor", + "progress_completed": "Tamamlandı", + "progress_cancelled": "İptal edildi", + "calendar": "Takvim", + "alert": "Hatırlatıcı", + "alert_none": "Yok", + "alert_at_time": "Son tarih saatinde", + "alert_5min": "5 dakika önce", + "alert_15min": "15 dakika önce", + "alert_30min": "30 dakika önce", + "alert_1hr": "1 saat önce", + "alert_1day": "1 gün önce", + "delete": "Sil", + "cancel": "İptal", + "save": "Kaydet", + "quick_add_placeholder": "Görev ekle...", + "due_today": "Bugün", + "due_tomorrow": "Yarın", + "overdue": "Gecikmiş" + }, + "nav_open_menu": "Menüyü aç", + "toolbar": { + "more": "Diğer işlemler", + "select_view": "Görünümü değiştir, şu an {view}" + }, + "other_calendars": "Diğer takvimler", + "calendar_options": "{name} seçenekleri" + }, + "calendar_event_notifications": { + "someone": "Birisi", + "invited": "{name} sizi \"{title}\" etkinliğine davet etti", + "updated": "{name} \"{title}\" etkinliğini güncelledi", + "cancelled": "{name} \"{title}\" etkinliğini iptal etti", + "untitled": "Başlıksız etkinlik" + }, + "share_notifications": { + "someone": "Birisi", + "shared": "{name}, \"{object}\" {kind} sizinle paylaştı", + "changed": "{name}, \"{object}\" {kind} erişiminizi değiştirdi", + "revoked": "{name}, \"{object}\" {kind} erişiminizi kaldırdı", + "object": { + "folder": "klasörünü", + "calendar": "takvimini", + "address_book": "adres defterini", + "files": "dosya klasörünü" + } + }, + "sharing": { + "title": "\"{name}\" paylaş", + "description": "Bu sunucudaki diğer kullanıcılara veya gruplara erişim verin. Değişiklikler hemen geçerli olur.", + "no_shares": "Henüz kimseyle paylaşılmadı.", + "add_person": "Kişi veya grup ekle", + "search_placeholder": "Ad veya e-posta ile arayın…", + "loading_principals": "Kullanıcılar yükleniyor…", + "no_principals": "Başka kullanıcı veya grup bulunamadı.", + "no_match": "Eşleşme yok.", + "remove": "Erişimi kaldır", + "group": "Grup", + "share_added": "Erişim verildi", + "share_updated": "Erişim güncellendi", + "share_removed": "Erişim kaldırıldı", + "share_failed": "Paylaşım güncellenemedi", + "preset": { + "freeBusy": "Yalnızca müsait/meşgul", + "read": "Yalnızca okuma", + "readWrite": "Okuma ve yazma", + "manager": "Yönetici", + "custom": "Özel" + } + }, + "advanced_search": { + "title": "Gelişmiş Arama", + "from": "Kimden", + "from_placeholder": "Gönderici e-posta veya adı", + "to": "Kime", + "to_placeholder": "Alıcı e-posta veya adı", + "subject": "Konu", + "subject_placeholder": "Konu içerir...", + "body": "Gövde", + "body_placeholder": "Gövde içerir...", + "folder": "Klasör", + "all_folders": "Tüm klasörler", + "all_folders_except_spam_trash": "Spam ve Çöp Kutusu dışındaki tüm klasörler", + "has_attachment": "Ekler", + "date_after": "Sonra", + "date_before": "Önce", + "size_min": "Şundan büyük (KB)", + "size_max": "Şundan küçük (KB)", + "starred": "Yıldızlı", + "unread": "Okunmamış", + "read": "Okunmuş", + "yes": "Evet", + "no": "Hayır", + "clear": "Temizle", + "clear_all": "Tümünü temizle", + "filters_active": "{count} filtre", + "filters_active_plural": "{count} filtre", + "toggle_filters": "Daha fazla", + "search_hint": "Kesin arama için gelişmiş filtreleri kullanın", + "advanced_filters_tooltip": "Gelişmiş arama filtreleri", + "results_found": "{count, plural, =0 {Sonuç bulunamadı} one {# sonuç bulundu} other {# sonuç bulundu}}", + "results_found_more": "{count}+ sonuç bulundu", + "suggestions_recent": "Son aramalar", + "suggestions_people": "Kişiler", + "suggestions_from": "{address} adresinden gelen e-postaları ara", + "suggestions_to": "{address} adresine gönderilen e-postaları ara", + "suggestions_remove_recent": "Son aramalardan kaldır", + "has_words": "Şu kelimeleri içeren", + "size": "Boyut", + "date_within": "Tarih", + "search_in": "Şurada ara", + "has_attachment_check": "Eki var", + "search": "Ara" + }, + "welcome": { + "title": "Posta kutunuza hoş geldiniz", + "tip_compose": "Yeni e-posta oluşturmak için c tuşuna basın", + "tip_shortcuts": "Tüm klavye kısayollarını görmek için ? tuşuna basın", + "tip_sidebar": "Takvim, Kişiler ve Ayarlar'ı kenar çubuğu menüsünde bulabilirsiniz", + "tip_settings": "Deneyiminizi Ayarlar'dan özelleştirin", + "tip_compose_touch": "Yeni bir e-posta yazmak için kalem düğmesine dokunun", + "tip_sidebar_touch": "Takvim, Kişiler ve Ayarlar alttaki çubukta", + "got_it": "Anladım", + "settings": "Ayarlar", + "dismiss": "Kapat", + "start_tour": "Turu Başlat" + }, + "demo_welcome": { + "title": "Bulwark Mail'e Hoş Geldiniz", + "description": "Tam özellikli bir web posta istemcisini doğrudan tarayıcınızda keşfedin. Tüm veriler cihazınızda kalır, her şeyi özgürce test edin.", + "feature_email": "E-posta oku ve oluştur", + "feature_organize": "Etiketler, yıldızlar ve klasörler", + "feature_shortcuts": "Klavye kısayolları", + "feature_privacy": "%100 gizli demo", + "hint": "Başlamak için soldaki herhangi bir e-postaya tıklayın veya aşağıdaki turu yapın." + }, + "files": { + "office_edit": "Belgeyi düzenle", + "office_loading": "Belge düzenleyici açılıyor…", + "office_error": "Belge düzenleyici açılamadı.", + "office_close": "Düzenleyiciyi kapat", + "open_in_new_tab": "Yeni sekmede aç", + "title": "Dosyalar", + "search_placeholder": "Dosyalarda ara...", + "empty_state_title": "Henüz dosya yok", + "upload_files": "Dosya Yükle", + "new_folder": "Yeni Klasör", + "new_folder_name": "Klasör adı", + "rename": "Yeniden Adlandır", + "rename_title": "Yeniden Adlandır", + "new_name": "Yeni ad", + "name_forbidden_chars": "Sunucu adlarda şu karakterlere izin vermiyor: {chars}", + "name_reserved": "Bu ad sunucuda ayrılmış.", + "delete": "Sil", + "delete_confirm_title": "Kaynağı sil", + "delete_confirm_message": "\"{name}\" silinsin mi? Bu geri alınamaz.", + "download": "İndir", + "name": "Ad", + "size": "Boyut", + "modified": "Değiştirildi", + "type": "Tür", + "folder": "Klasör", + "file": "Dosya", + "uploading": "Yükleniyor...", + "upload_success": "{count, plural, one {1 dosya yüklendi} other {# dosya yüklendi}}", + "upload_canceled": "Yükleme iptal edildi", + "upload_canceled_partial": "İptal etmeden önce {count, plural, one {1 dosya} other {# dosya}} yüklendi", + "upload_error": "Dosya yüklenemedi", + "create_folder_success": "Klasör oluşturuldu", + "create_folder_error": "Klasör oluşturulamadı", + "delete_success": "Başarıyla silindi", + "delete_error": "Silinemedi", + "rename_success": "Başarıyla yeniden adlandırıldı", + "rename_error": "Yeniden adlandırılamadı", + "download_error": "İndirilemedi", + "not_available": "Bu sunucuda dosya depolama kullanılamıyor", + "cancel": "İptal", + "create": "Oluştur", + "save": "Kaydet", + "no_results": "Aramanızla eşleşen dosya yok", + "batch_delete_confirm_message": "{count, plural, one {1 öge} other {# öge}} silinsin mi? Bu geri alınamaz.", + "batch_delete_success": "{count, plural, one {1 öge silindi} other {# öge silindi}}", + "grid_view": "Izgara görünümü", + "list_view": "Liste görünümü", + "details": "Ayrıntılar", + "path": "Yol", + "preview": "Önizleme", + "preview_error": "Önizleme yüklenemedi", + "cut": "Kes", + "copy": "Kopyala", + "paste": "Yapıştır", + "move_success": "{count, plural, one {1 öge taşındı} other {# öge taşındı}}", + "move_error": "Taşınamadı", + "paste_success": "Başarıyla yapıştırıldı", + "paste_error": "Yapıştırılamadı", + "new_text_file": "Yeni Metin Dosyası", + "file_name": "Dosya adı", + "retry": "Yeniden dene", + "refresh": "Yenile", + "duplicate": "Çoğalt", + "duplicate_success": "Başarıyla çoğaltıldı", + "duplicate_error": "Çoğaltılamadı", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Dosya oluşturuldu", + "create_file_error": "Dosya oluşturulamadı", + "favorites": "Favoriler", + "recent": "Son Kullanılan", + "show_in_folder": "Klasörde göster", + "remove_from_recent": "Son kullanılanlardan kaldır", + "upload_folder": "Klasör Yükle", + "file_too_large": "\"{name}\" maksimum dosya boyutunu ({max}) aşıyor", + "undo": "Geri Al", + "undo_success": "İşlem geri alındı", + "undo_error": "Geri alınamadı", + "toolbar": "Dosya işlemleri", + "file_list": "Dosyalar ve klasörler", + "context_menu": "İşlemler", + "settings_ascending": "Artan", + "settings_descending": "Azalan", + "disabled_title": "Dosyalar özelliği yöneticiniz tarafından devre dışı bırakıldı", + "disabled_description": "WebDAV üzerinden büyük dosya yüklemeleri Stalwart/RocksDB kararsızlığına yol açabilir; bellek yetersizliği çöküşleri ve kurtarılamaz disk kullanımı dahil. Silinen dosyalar blob depolamadan hemen temizlenmeyebilir. Bu özellik üretim ortamları için önerilmez.", + "no_accounts": "Bağlı hesap yok.", + "open_folder_tree": "Klasör ağacını aç", + "migration_title": "Dosyalarınız güncelleniyor…", + "migration_description": "Klasörler ve dosyalar doğru yapıya göre düzenleniyor. Bu yalnızca bir kez gerçekleşir.", + "new": "Yeni", + "my_files": "Dosyalarım", + "folders": "Klasörler", + "files_heading": "Dosyalar", + "open": "Aç", + "star": "Favorilere ekle", + "unstar": "Favorilerden kaldır", + "expand_folder": "Genişlet: {name}", + "collapse_folder": "Daralt: {name}", + "sidebar_hint": "Klasörleri favorilere ekleyin ve dosyaları açın, burada bulabilirsiniz.", + "breadcrumbs": "Klasör yolu", + "show_path": "Yoldaki gizli klasörleri göster", + "selection_actions": "Seçili ögeler için işlemler", + "selected_count": "{count, plural, one {1 seçili} other {# seçili}}", + "clear_selection": "Seçimi temizle", + "select_all": "Tümünü seç", + "select_item": "Seç: {name}", + "more_actions": "Diğer işlemler: {name}", + "paste_here": "Bu klasöre yapıştır", + "paste_count": "{count, plural, one {1 ögeyi yapıştır} other {# ögeyi yapıştır}}", + "sort_by": "Sırala", + "view_mode": "Görünüm", + "clear_search": "Aramayı temizle", + "no_results_for": "Bu klasörde \"{query}\" ile eşleşen bir şey yok", + "download_count": "{count, plural, one {1 dosyayı indir} other {# dosyayı indir}}", + "delete_count": "{count, plural, one {1 ögeyi sil} other {# ögeyi sil}}", + "close_details": "Ayrıntıları kapat", + "file_kind": "{ext} dosyası", + "folder_summary": "{folders, plural, =0 {Klasör yok} one {1 klasör} other {# klasör}}, {files, plural, =0 {dosya yok} one {1 dosya} other {# dosya}}", + "details_hint": "Ayrıntılarını görmek için bir dosya veya klasör seçin.", + "sharing": "Paylaşım", + "shared_with_count": "{count, plural, one {1 kişiyle paylaşıldı} other {# kişiyle paylaşıldı}}", + "only_you": "Yalnızca siz", + "empty_folder_title": "Bu klasör boş", + "empty_folder_description": "Dosyaları buraya bırakın veya cihazınızdan yükleyin.", + "empty_folder_read_only": "Bu klasöre dosya ekleyemezsiniz.", + "choose_account": "Bir hesap seçin", + "drop_to_upload": "Yüklemek için bırakın: {folder}", + "uploading_count": "{current}/{total} yükleniyor", + "dismiss": "Kapat", + "zoom_in": "Yakınlaştır", + "zoom_out": "Uzaklaştır", + "zoom_reset": "Ekrana sığdır", + "rotate": "Döndür", + "close_preview": "Kapat" + }, + "smime": { + "your_certificates": "Sertifikalarınız", + "your_certificates_desc": "E-postaları imzalamak ve şifrelemek için S/MIME sertifikalarınızı içe aktarın ve yönetin", + "recipient_certificates": "Alıcı Sertifikaları", + "recipient_certificates_desc": "Alıcılara e-posta şifrelemek için genel sertifikalar", + "identity_bindings": "Kimlik Anahtar Bağlamaları", + "identity_bindings_desc": "S/MIME sertifikalarını e-posta kimliklerinize bağlayın", + "defaults_title": "Varsayılanlar", + "defaults_desc": "Varsayılan imzalama ve şifreleme davranışını yapılandırın", + "import_pkcs12": "PKCS#12 İçe Aktar (.p12/.pfx)", + "import_public_cert": "Sertifika İçe Aktar", + "no_certificates": "Henüz sertifika içe aktarılmadı", + "no_recipient_certs": "Alıcı sertifikası yok", + "expires": "Son Kullanma", + "expired": "Süresi Doldu", + "bound_to": "Bağlı", + "no_key_bound": "Yok", + "lock": "Anahtarı kilitle", + "unlock": "Anahtarın kilidini aç", + "details": "Ayrıntıları görüntüle", + "delete": "Sil", + "encrypt_by_default": "Varsayılan olarak şifrele", + "encrypt_by_default_desc": "Tüm alıcıların sertifikası olduğunda e-postaları otomatik şifrele", + "remember_unlocked": "Kilidi açık anahtarları hatırla", + "remember_unlocked_desc": "Bu tarayıcı oturumu süresince anahtarların kilidini açık tut", + "sign_default_for": "Varsayılan imzala", + "enter_p12_passphrase": "PKCS#12 Parolası Girin", + "p12_passphrase_desc": "Bu sertifika dosyasını koruyan parolayı girin", + "enter_storage_passphrase": "Depolama Parolası Ayarla", + "storage_passphrase_desc": "Tarayıcınızda bu anahtarı beklerken korumak için bir parola seçin", + "next": "İleri", + "import": "İçe Aktar", + "unlock_key": "Anahtarın Kilidini Aç", + "unlock_key_desc": "İmzalama veya şifre çözme için bu anahtarın kilidini açmak üzere depolama parolasını girin", + "passphrase_placeholder": "Parola girin", + "confirm_passphrase_placeholder": "Parolayı onaylayın", + "passphrase_mismatch": "Parolalar eşleşmiyor", + "cancel": "İptal", + "processing": "İşleniyor…", + "close": "Kapat", + "certificate_details": "Sertifika Ayrıntıları", + "cert_subject": "Konu", + "cert_issuer": "Veren", + "cert_email": "E-posta", + "cert_serial": "Seri Numarası", + "cert_validity": "Geçerlilik", + "cert_fingerprint": "Parmak İzi (SHA-256)", + "cert_algorithm": "Algoritma", + "cert_capabilities": "Yetenekler", + "cert_source": "Kaynak", + "cert_expired": "Bu sertifikanın süresi dolmuş", + "cert_not_yet_valid": "Bu sertifika henüz geçerli değil", + "cap_sign": "İmzalama", + "cap_encrypt": "Şifreleme", + "cap_none": "Yok", + "show_passphrase": "Parolayı göster", + "hide_passphrase": "Parolayı gizle", + "sign_toggle": "İmzala", + "encrypt_toggle": "Şifrele", + "missing_recipient_certs": "Eksik sertifikalar: {emails}", + "missing_sender_cert": "Bu kimliğe bağlı sertifika yok", + "status_encrypted_ok": "Bu ileti şifrelendi", + "status_encrypted_no_key": "Bu ileti şifreli ama eşleşen anahtar bulunamadı", + "status_encrypted_failed": "Bu ileti şifresi çözülemedi", + "status_signed_valid": "İmza doğrulandı", + "status_signed_invalid": "İmza doğrulama başarısız", + "status_signed_expired_cert": "Süresi dolmuş sertifika ile imzalandı", + "status_signed_self_signed": "İmza geçerli, ancak sertifika kendi kendine imzalanmış (güvenilir değil)", + "status_signed_mismatch": "İmza geçerli, ancak imzalayan göndericiyle eşleşmiyor", + "status_unsupported": "Desteklenmeyen S/MIME biçimi", + "auto_import_signer_certs": "İmzalayan sertifikaları otomatik içe aktar", + "auto_import_signer_certs_desc": "Gelecekteki şifreleme için doğrulanmış imzalı e-postalardan sertifikaları otomatik kaydet", + "export": "Dışa Aktar", + "enter_export_passphrase": "Dışa Aktarma Parolası Ayarla", + "export_passphrase_desc": "Dışa aktarılan PKCS#12 dosyasını korumak için bir parola seçin", + "export_storage_desc": "Dışa aktarma için anahtarın şifresini çözmek üzere depolama parolasını girin", + "incorrect_passphrase": "Yanlış parola" + }, + "tour": { + "step_counter": "{total} adımdan {current}.", + "skip": "Turu atla", + "back": "Geri", + "next": "İleri", + "finish": "Bitir", + "take_a_tour": "Arayüz turu yap", + "restart_title": "Tanıtım turu", + "restart_desc": "Arayüzün rehberli gezintisini tekrar oynat", + "restart_button": "Turu yeniden başlat", + "show_on_new_devices_title": "Yeni cihazlarda göster", + "show_on_new_devices_desc": "Başka bir yerde tamamlamış olsanız bile, yeni bir cihazda ilk oturum açtığınızda karşılama afişini ve turu yeniden gösterin", + "sidebar_title": "Posta kutularınız", + "sidebar_desc": "Bu sizin klasör kenar çubuğunuzdur. E-postalarını görüntülemek için herhangi bir posta kutusuna tıklayın. Klasörler oluşturabilir, e-postaları aralarında sürükleyebilir ve okunmamış sayılarını bir bakışta görebilirsiniz.", + "compose_title": "E-posta oluştur", + "compose_desc": "Yeni e-posta yazmak için buraya tıklayın. Alıcılar ekleyebilir, ek ekleyebilir ve zengin metin biçimlendirmesi kullanabilirsiniz.", + "search_title": "Postanızda arayın", + "search_desc": "Gönderici, konu veya içeriğe göre arayın. Tarih aralığı, ekler ve yıldızlı iletiler gibi gelişmiş seçenekler için filtre simgesine tıklayın.", + "email_list_title": "E-posta listeniz", + "email_list_desc": "E-postalar burada görünür. Okumak için bir tanesine tıklayın. Birden fazla seçmek için onay kutusunu kullanın, ardından toplu taşıma, silme veya etiketleme yapın.", + "email_viewer_title": "Okuma bölmesi", + "email_viewer_desc": "Seçilen e-posta burada açılır. Araç çubuğu düğmeleriyle yanıtlayın, iletin, arşivleyin veya silin. E-postalara yıldız da ekleyebilir veya renk etiketi atayabilirsiniz.", + "keywords_title": "Renk etiketleri", + "keywords_desc": "E-postalarınızı renk kodlu etiketlerle düzenleyin. Etiketlemek için bir e-postayı etikete sürükleyin ya da etiket atamak için sağ tıklayın.", + "calendar_title": "Takvim", + "calendar_desc": "Etkinliklerinizi yönetmek için takvime geçin. Etkinlikler oluşturun, hatırlatıcılar ayarlayın ve gün, hafta veya aylık görünümlerde izleyin.", + "contacts_title": "Kişiler", + "contacts_desc": "Adres defteriniz burada. Kişileri içe aktarın, gruplar oluşturun ve tam ayrıntılarını görmek için herhangi bir kişiye tıklayın.", + "settings_title": "Ayarlar", + "settings_desc": "Her şeyi özelleştirin: tema, yoğunluk, imzalar, filtreler, klavye kısayolları, takvim varsayılanları ve daha fazlası.", + "shortcuts_title": "Klavye kısayolları", + "shortcuts_desc": "Deneyimli kullanıcılar bunu sever. Mevcut tüm kısayolları görmek için istediğiniz zaman ? tuşuna basın. Fareye dokunmadan gezinebilir, e-posta oluşturabilir ve yönetebilirsiniz.", + "compose_open_title": "İleti düzenleyici", + "compose_open_desc": "Bu, e-posta düzenleyicisidir. Alıcılar ekleyin, iletinizi yazın, dosya ekleyin ve zengin metin biçimlendirmesi kullanın. Ayrıca taslak kaydedebilir ve şablonları kullanabilirsiniz.", + "calendar_view_title": "Takviminiz", + "calendar_view_desc": "İşte örnek etkinliklerin bulunduğu takviminiz. Araç çubuğunu kullanarak gün, hafta, ay ve gündem görünümleri arasında geçiş yapabilirsiniz.", + "create_event_title": "Etkinlik oluştur", + "create_event_desc": "Yeni takvim etkinliği oluşturmak için bu düğmeye tıklayın. Başlık, tarih, saat ayarlayabilir ve katılımcı ekleyebilirsiniz.", + "event_modal_title": "Etkinlik ayrıntıları", + "event_modal_desc": "İşte etkinlik formu. Başlığı doldurun, tarih ve saat seçin, konum veya katılımcı ekleyin. Hazır olduğunuzda kaydedin - ya da kapatıp devam edin.", + "contacts_list_title": "Kişileriniz", + "contacts_list_desc": "İşte kişileriniz. Herhangi bir kişiye tıklayarak sağ taraftaki tam ayrıntılarını görebilirsiniz. Ayrıca yeni kişiler oluşturabilir, vCard içe aktarabilir veya kişileri gruplara düzenleyebilirsiniz.", + "settings_tabs_title": "Ayarlar menüsü", + "settings_tabs_desc": "İşte tüm ayar kategorileri. Görünümünüzü özelleştirin, kimlikleri yönetin, e-posta filtreleri kurun, takviminizi yapılandırın ve çok daha fazlasını yapın.", + "files_title": "Dosya depolama", + "files_desc": "Dosya tarayıcınız dosya yüklemenize, düzenlemenize ve paylaşmanıza olanak tanır - postanıza yerleşik kişisel bulut sürücüsü gibi.", + "demo_banner_title": "Demo kontrolleri", + "demo_banner_desc": "Demo modundasınız - her şey tarayıcınızda kalır. Temiz örnek verilerle yeniden başlamak için istediğiniz zaman 'Demoyu Sıfırla'ya basın.", + "quota_title": "Depolama kullanımı", + "quota_desc": "Posta kutusu boyutunuzu buradan takip edin. Daha fazla alan kullandıkça daire dolmaya başlar." + }, + "unified_mailbox": { + "search_unavailable": "Birleşik görünümde arama kullanılamaz" + }, + "quote_header": { + "reply_line": "{date} tarihinde {from} şunu yazdı:", + "forwarded_separator": "---------- İletilen mesaj ----------", + "from_label": "Kimden", + "date_label": "Tarih", + "subject_label": "Konu" + }, + "pwa_install": { + "title": "{appName} uygulamasını yükle", + "description": "Hızlı erişim ve çevrimdışı destek için uygulamamızı yükleyin.", + "not_now": "Şimdi değil", + "install": "Yükle", + "dont_remind": "Bir daha hatırlatma", + "dismiss_aria": "Yükleme istemini kapat" + }, + "deep_link": { + "message_not_found": "Bu ileti artık kullanılamıyor.", + "thread_not_found": "Bu sohbet artık kullanılamıyor.", + "folder_not_found": "Bu klasör artık kullanılamıyor.", + "event_not_found": "Bu etkinlik artık kullanılamıyor.", + "file_not_found": "Bu dosya artık kullanılamıyor.", + "account_unavailable": "Bu bağlantıyı açmak için o hesapta oturum açın.", + "copy_message": "İleti bağlantısını kopyala", + "copy_conversation": "Sohbet bağlantısını kopyala", + "copy_event": "Etkinlik bağlantısını kopyala", + "copied": "Bağlantı kopyalandı", + "copy_failed": "Bağlantı kopyalanamadı" + }, + "plugins": { + "oauth_callback": { + "completing": "Yetkilendirme tamamlanıyor…", + "completing_message": "Bağlantı kurulurken lütfen bekleyin.", + "received_title": "Yetkilendirme alındı", + "received_message": "Bağlantıyı başlattığınız sekmeye dönebilirsiniz.", + "failed_title": "Yetkilendirme başarısız oldu", + "denied": "Yetkilendirme reddedildi.", + "failed_detail": "Yetkilendirme başarısız oldu: {error}", + "missing_params": "Yetkilendirme kodu veya durum eksik.", + "delivery_failed": "Yetkilendirme sonucu uygulama sekmesine iletilemedi. Lütfen bu pencereyi kapatıp tekrar deneyin." + }, + "settings": { + "title": "Eklentiler", + "description": "Yöneticinizin eklediği eklentiler. Hesabınız için açıp kapatabilirsiniz.", + "description_uploads": "Eklentiler webmail'e yeni özellikler katar. Hesabınız için açıp kapatabilir veya kendi eklentilerinizi yükleyebilirsiniz.", + "description_demo": "Eklentiler demoda kullanılamaz.", + "install_button": "Eklenti yükle", + "install_hint": "Bir eklenti ZIP dosyası seçin.", + "install_hint_approval": "Bir eklenti ZIP dosyası seçin. Açabilmeniz için önce bir yöneticinin onaylaması gerekir.", + "install_uploads_off": "Yöneticiniz eklenti yüklemeye izin vermiyor.", + "install_managed_id": "Yöneticiniz bu adda bir eklentiyi zaten sağlıyor, bu yüzden değiştirilemez.", + "install_invalid": "Bu dosya yüklenebilir bir eklenti değil: {details}", + "toast_installed": "Eklenti yüklendi. Aşağıdan açabilirsiniz.", + "toast_installed_pending": "Eklenti yüklendi ve onay için yöneticinize gönderildi.", + "toast_enabled": "{name} eklentisi açıldı", + "toast_disabled": "{name} eklentisi kapatıldı", + "toast_removed": "{name} eklentisi kaldırıldı", + "toast_forced": "Yöneticiniz {name} eklentisini açık tutuyor", + "toast_approval_requested": "{name} eklentisi yöneticinizin onayını bekliyor", + "empty_title": "Henüz eklenti yok", + "empty_admin": "Yöneticiniz henüz eklenti eklemedi.", + "empty_uploads": "Başlamak için bir ZIP dosyasından eklenti yükleyin.", + "empty_demo": "Eklentiler gerçek bir hesap gerektirir, bu yüzden demoda eklenti yok.", + "syncing": "Eklentiler sunucuyla denetleniyor…", + "status_installed": "Kapalı", + "status_enabled": "Başlatılıyor", + "status_running": "Açık", + "status_disabled": "Kapalı", + "status_error": "Sorun var", + "badge_forced": "Zorunlu", + "badge_managed": "Yöneticinizden", + "badge_yours": "Sizin yüklediğiniz", + "badge_awaiting": "Onay bekliyor", + "toggle_label": "{name} eklentisini aç veya kapat", + "forced_note": "Yöneticiniz bu eklentiyi zorunlu tuttuğu için kapatılamaz.", + "permissions": "Neler yapabilir", + "permissions_managed": "Neler yapabilir (yöneticiniz onayladı)", + "settings": "Ayarlar", + "remove": "Kaldır", + "remove_title": "{name} eklentisi kaldırılsın mı?", + "remove_message": "Eklenti ve bu tarayıcıda kaydettiği her şey silinir. Daha sonra yeniden yükleyebilirsiniz.", + "remove_confirm": "Kaldır", + "error_user_plugins_off": "Yöneticiniz kullanıcıların yüklediği eklentileri kapattı, bu yüzden bu eklenti çalışmıyor.", + "error_approval_unavailable": "Bu eklenti, onay zorunlu olmadan önce yüklendi. Onaya göndermek için kaldırıp yeniden yükleyin.", + "error_approval_denied": "Yöneticiniz bu eklentiyi onaylamadı.", + "error_approval_pending": "Bu eklentinin yöneticiniz tarafından onaylanması bekleniyor.", + "error_auto_disabled": "Sürekli hata verdiği için kapatıldı. Yeniden denemek için tekrar açın.", + "accounts_heading": "Bağlı hesaplar", + "account_on": "{host} üzerinde {label}", + "account_waiting": "{host} üzerinde {label} (giriş tamamlanmadı)", + "disconnect": "Bağlantıyı kes", + "disconnect_failed": "{label} bağlantısı kesilemedi" + }, + "consent": { + "title": "{name} eklentisine bu izin verilsin mi?", + "intro": "Eklenti aşağıdaki izinleri istiyor. Bu izinleri yalnızca siz izin verirseniz alır.", + "high_risk_note": "İşaretli öğeler eklentiye e-postalarınızın içeriğine erişim verir veya verileri başka yerlere göndermesine izin verir. Bunlara yalnızca güvendiğiniz eklentiler için izin verin.", + "revoke_hint": "Eklentiyi istediğiniz zaman Ayarlar'dan kapatabilir veya kaldırabilirsiniz.", + "deny": "İzin verme", + "allow": "İzin ver" + }, + "permissions": { + "none": "Özel bir izne ihtiyacı yok.", + "high_risk": "Dikkat gerektirir", + "email_read": { + "title": "E-postalarınızı okuma", + "body": "Hesabınızdaki herhangi bir iletinin gönderenlerini, alıcılarını, konusunu ve tam metnini, ayrıca klasörlerinizi görme." + }, + "email_write": { + "title": "E-postalarınızı değiştirme ve düzenleme", + "body": "İletilerinize etiket, bayrak ve diğer anahtar sözcükleri ekleme veya kaldırma, iletileri klasörleriniz arasında ve arşive taşıma ve taslak kaydetme." + }, + "email_send": { + "title": "Gönderilmeden önce iletileri değiştirme", + "body": "Yazdığınız iletilerin alıcılarını, konusunu, metnini ve eklerini okuma ve değiştirme, bir iletinin gönderilmesini durdurma." + }, + "crypto_full": { + "title": "Bu uygulamaya tam erişim", + "body": "Webmail'in kendisiyle aynı haklarla çalışır. Tüm e-postalarınızı ve kayıtlı anahtarlarınızı okuyabilir, sizin adınıza işlem yapabilir. Buna yalnızca tamamen güvendiğiniz eklentiler için izin verin." + }, + "email_raw_send": { + "title": "Kendi oluşturduğu iletileri gönderme", + "body": "Hesabınızdan imzalı veya şifreli iletiler gibi eksiksiz iletiler gönderme." + }, + "email_blob_read": { + "title": "İletilerin ham içeriğini okuma", + "body": "İletilerinizin ve eklerinizin özgün verilerini ve eklediğiniz her dosyayı gönderilmeden önce okuma." + }, + "email_blob_write": { + "title": "Göndermeden önce ekleri değiştirme", + "body": "Posta sunucunuza dosya yükleme ve ileti gönderilmeden önce eklediğiniz bir dosyayı başkasıyla değiştirme." + }, + "email_render_takeover": { + "title": "İletilerin gösterilme biçimini değiştirme", + "body": "Açtığınız iletilerin ve ileti listesindekilerin metnini değiştirme, örneğin şifresi çözülmüş içeriği göstermek için." + }, + "calendar_read": { + "title": "Takviminizi okuma", + "body": "Takvimlerinizi ve etkinliklerini görme." + }, + "calendar_write": { + "title": "Takviminizi değiştirme", + "body": "Etkinlik oluşturma, değiştirme ve silme." + }, + "contacts_read": { + "title": "Kişilerinizi okuma", + "body": "Adres defterlerinizde arama yapma ve onları okuma." + }, + "contacts_write": { + "title": "Kişilerinizi değiştirme", + "body": "Kişi ve adres defteri oluşturma, değiştirme ve silme." + }, + "files_read": { + "title": "Dosyalarınızı okuma", + "body": "Hesabınızda saklanan dosyalara göz atma ve onları açma." + }, + "files_write": { + "title": "Dosyalarınızı değiştirme", + "body": "Hesabınızdaki dosyaları yükleme, yeniden adlandırma, taşıma ve silme." + }, + "identity_read": { + "title": "Gönderen adreslerinizi görme", + "body": "Gönderim yaptığınız adresleri ve imzalarını görme." + }, + "identity_write": { + "title": "Gönderen adreslerinizi değiştirme", + "body": "Gönderen adresi ve imza oluşturma, değiştirme ve silme." + }, + "filters_read": { + "title": "Posta filtrelerinizi okuma", + "body": "Gelen postalarınızı sıralayan kuralları görme." + }, + "filters_write": { + "title": "Posta filtrelerinizi değiştirme", + "body": "Sunucudaki filtrelerinize kural ekleme. Bu kurallar gelen postaları taşıyabilir, iletebilir veya silebilir." + }, + "tasks_read": { + "title": "Görevlerinizi okuma", + "body": "Görev listelerinizi ve görevlerinizi görme." + }, + "tasks_write": { + "title": "Görevlerinizi değiştirme", + "body": "Görev oluşturma, değiştirme, tamamlama ve silme." + }, + "templates_read": { + "title": "Şablonlarınızı okuma", + "body": "Kayıtlı ileti şablonlarınızı görme." + }, + "templates_write": { + "title": "Şablonlarınızı değiştirme", + "body": "İleti şablonu oluşturma, değiştirme ve silme." + }, + "smime_read": { + "title": "S/MIME durumunuzu görme", + "body": "Anahtarlara erişmeden S/MIME anahtarlarınızın ve sertifikalarınızın olup olmadığını görme." + }, + "vacation_read": { + "title": "Ofis dışı yanıtınızı görme", + "body": "Otomatik yanıtınızın açık olup olmadığını ve ne yazdığını görme." + }, + "vacation_write": { + "title": "Ofis dışı yanıtınızı değiştirme", + "body": "Otomatik yanıtınızı açıp kapatma, metnini ve tarihlerini değiştirme." + }, + "settings_read": { + "title": "Ayarlarınızı okuma", + "body": "Tercihlerinizi ve etiket tanımlarınızı görme." + }, + "settings_write": { + "title": "Ayarlarınızı değiştirme", + "body": "Etiket ekleme, sıralarını değiştirme ve tercihlerinizi değiştirme." + }, + "security_read": { + "title": "Hesap güvenliğinizi görme", + "body": "Gizli bilgilere erişmeden iki faktörlü kimlik doğrulamanın ve şifrelemenin açık olup olmadığını görme." + }, + "auth_observe": { + "title": "Giriş yaptığınızı bilme", + "body": "Giriş yaptığınızda, hesap değiştirdiğinizde veya oturumunuz yenilendiğinde haber alma." + }, + "auth_emit": { + "title": "Oturumunuzu kapatma", + "body": "Webmail'den çıkış yapmanızı sağlama." + }, + "account_read": { + "title": "Hesaplarınızı görme", + "body": "Eklediğiniz hesapları adresleri ve sunucularıyla birlikte görme." + }, + "http_post": { + "title": "Sizin adınıza webmail sunucusunu kullanma", + "body": "Şu sunucu uç noktalarını oturum bilgilerinizle çağırma:" + }, + "http_fetch": { + "title": "Başka web sitelerine bağlanma", + "body": "Şu sitelere istek gönderme. Eklentinin okuyabildiği her şey oraya gönderilebilir:" + }, + "admin_config": { + "title": "Yöneticinizin ayarlarını okuma", + "body": "Yöneticinizin bu eklenti için kaydettiği ayarları okuma." + }, + "ui_observe": { + "title": "Uygulamada nerede olduğunuzu izleme", + "body": "Hangi sayfanın açık olduğunu bilme ve tıkladığınız bağlantılara tepki verme." + }, + "ui_toolbar": { + "title": "Araç çubuklarına düğme ekleme", + "body": "İleti araç çubuğunda, açık bir iletide ve üst çubukta kendi düğmelerini gösterme." + }, + "ui_app_top_banner": { + "title": "Üstte bir başlık bandı gösterme", + "body": "Uygulamanın üst kısmında bir bildirim gösterme." + }, + "ui_email_banner": { + "title": "İletilerde bildirim gösterme", + "body": "Açık bir iletinin veya konuşmanın bölümlerinin üstünde, içinde ve arasında kendi bildirimlerini ve özetlerini gösterme." + }, + "ui_email_footer": { + "title": "İletilerin altına içerik ekleme", + "body": "Açık bir iletinin altında kendi içeriğini gösterme." + }, + "ui_contact_cryptokeys": { + "title": "Kişilerin şifreleme anahtarlarını gösterme", + "body": "Bir kişinin şifreleme anahtarlarını gösterme ve yönetme." + }, + "ui_email_details": { + "title": "İleti ayrıntılarına panel ekleme", + "body": "Açık bir iletinin yanında veya altında kendi panelini gösterme." + }, + "ui_download_file": { + "title": "Dosyaları cihazınıza kaydetme", + "body": "Oluşturduğu dosyaları indirmeniz için sunma." + }, + "ui_message_list_tabs": { + "title": "Gelen Kutusu'na sekme ekleme", + "body": "Gelen kutunuzu Güncellemeler veya Promosyonlar gibi sekmelere ayırma." + }, + "ui_composer_toolbar": { + "title": "İleti düzenleyiciye öğe ekleme", + "body": "Siz ileti yazarken kendi düğmelerini, menü öğelerini ve bölümlerini gösterme." + }, + "ui_composer_sidebar": { + "title": "İleti düzenleyiciye panel ekleme", + "body": "Yazdığınız iletinin yanında kendi panelini gösterme." + }, + "ui_sidebar_widget": { + "title": "Kenar çubuğuna öğe ekleme", + "body": "Posta kenar çubuğunda kendi kutularını ve bölümlerini gösterme." + }, + "ui_settings_section": { + "title": "Ayarlar sayfası ekleme", + "body": "Ayarlar'da kendi sayfasını gösterme." + }, + "ui_context_menu": { + "title": "İleti menüsüne öğe ekleme", + "body": "Bir iletiye sağ tıkladığınızda açılan menüye öğe ekleme." + }, + "ui_navigation_rail": { + "title": "Gezinme çubuğuna öğe ekleme", + "body": "Gezinme çubuğunda kendi düğmesini gösterme." + }, + "ui_keyboard": { + "title": "Klavye kısayolları ekleme", + "body": "Tanımladığı klavye kısayollarına tepki verme." + }, + "ui_calendar_action": { + "title": "Takvim görünümüne öğe ekleme", + "body": "Etkinliklerde kendi düğmelerini ve bölümlerini, takvim günlerinde kısa etiketler ve noktalar gösterme." + }, + "ui_admin_page": { + "title": "Yönetim sayfası ekleme", + "body": "Yönetim ayarlarında kendi sayfasını gösterme." + }, + "app_lifecycle": { + "title": "Uygulamanın başladığını ve durduğunu bilme", + "body": "Uygulama başladığında, çevrimdışı olduğunda veya çıkış yaptığınızda haber alma." + }, + "compose_open": { + "title": "İleti düzenleyiciyi açma", + "body": "Alıcıları, metni veya ekleri doldurulmuş yeni bir ileti açma. Gönderip göndermemeye yine siz karar verirsiniz." + }, + "compose_read": { + "title": "Yazdığınız iletiyi okuma", + "body": "Eklentiyi düzenleyicide kullandığınızda, yazarken iletinin metnini okur. İmzayı ve alıntılanan iletiyi okumaz." + }, + "compose_write": { + "title": "Yazdığınız iletiyi değiştirme", + "body": "Eklentiyi düzenleyicide kullandığınızda, yazarken iletide metni değiştirir veya ekler. Her değişikliği geri alabilirsiniz ve göndermeye yine siz karar verirsiniz." + }, + "ui_notifications": { + "title": "Bildirim gösterme", + "body": "Cihazınızda bildirim gösterme ya da bildirimlere izin vermediyseniz uygulama içinde gösterme. Her bildirimde eklentinin adı yer alır." + }, + "ui_message_list": { + "title": "İleti listesine öğe ekleme", + "body": "İleti listesinin üstünde kendi içeriğini, iletilerde ve seçili iletilerin araç çubuğunda düğmeler gösterme." + }, + "ui_contacts": { + "title": "Kişiler görünümüne öğe ekleme", + "body": "Bir kişinin ayrıntılarında kendi bölümlerini, kişi listesinde düğmeler gösterme." + }, + "ui_tasks": { + "title": "Görevler görünümüne öğe ekleme", + "body": "Bir görevin ayrıntılarında kendi bölümlerini, görev listesinde düğmeler gösterme." + }, + "ui_files": { + "title": "Dosyalar görünümüne öğe ekleme", + "body": "Dosya önizlemesinde kendi bölümlerini, bir dosyanın menüsünde öğeler gösterme." + }, + "ui_commands": { + "title": "Komut ekleme", + "body": "Komut paletine (Ctrl+K) kendi öğelerini ekleme." + }, + "ui_app_page": { + "title": "Sayfa ekleme", + "body": "Uygulamaya kendi sayfalarını ekleme, her biri için gezinme çubuğunda bir düğmeyle." + }, + "http_proxy": { + "title": "Bağladığınız hesapları kullanma", + "body": "Sizin veya yöneticinizin ayarladığı bir hesap ya da anahtarla Bulwark sunucusu üzerinden istek gönderme. Eklenti parolayı hiçbir zaman görmez, ancak o hesabın kendi sunucusunda yapabildiği her şeyi yapabilir." + } + }, + "dialog": { + "from_plugin": "{name} eklentisinden", + "ok": "Tamam", + "submit": "Gönder", + "confirm": "Onayla", + "cancel": "İptal", + "close": "Kapat", + "turn_off": "{name} eklentisini kapat", + "plugin_menu": "{name} seçenekleri", + "password_note": "Bu form web postasından değil, {name} eklentisinden geliyor. Yalnızca bu eklentiye güveniyorsanız parola girin.", + "toast_auto_disabled": "{name} sürekli hata verdiği için kapatıldı." + }, + "connect": { + "title": "{label} hesabını bağla", + "intro_password": "{name}, {label} hesabınızı kullanmak istiyor. Bulwark parolayı kendi sunucusunda saklar ve yalnızca {host} adresine gönderir. Eklenti parolayı hiçbir zaman görmez.", + "intro_password_any": "{name}, {label} hesabınızı kullanmak istiyor. Bulwark parolayı kendi sunucusunda saklar ve yalnızca burada girdiğiniz sunucuya gönderir. Eklenti parolayı hiçbir zaman görmez.", + "intro_signin": "{name}, {label} hesabınıza giriş yapmak istiyor. Sunucuyu buraya girin, ardından sunucunun kendi sayfasında giriş yapın. Bulwark aldığı erişimi kendi sunucusunda saklar; eklenti bunu hiçbir zaman görmez.", + "server": "Sunucu adresi", + "server_hint": "cloud.example.com", + "username": "Kullanıcı adı", + "secret": "Parola veya anahtar", + "connect": "Bağla", + "continue": "Devam", + "cancel": "İptal", + "saving": "Kaydediliyor…", + "error_server": "Sunucunun adresini girin, örneğin https://cloud.example.com", + "error_save": "Kaydedilemedi: {error}" + } + }, + "global_search": { + "title": "Her yerde ara", + "placeholder": "Posta, kişiler, takvim ve dosyalarda ara...", + "scope_label": "Şuralarda ara", + "account_label": "Hesap", + "scope_all": "Tümü", + "scope_mail": "Posta", + "scope_contacts": "Kişiler", + "scope_calendar": "Takvim", + "scope_files": "Dosyalar", + "all_accounts": "Tüm hesaplar", + "show_all": "Tüm sonuçları göster", + "no_results": "“{query}” için sonuç yok", + "server_hint": "Tam kelimeler sunucuda arar; kelime parçaları yüklenmiş öğelerle eşleşir", + "searching": "Aranıyor...", + "account_error": "{account} içinde arama yapılamadı", + "recent_searches": "Son aramalar", + "recurring": "Yinelenen", + "no_subject": "(no subject)", + "empty_hint": "Tüm hesaplarda posta, kişi, etkinlik ve dosya arayın. Şunları deneyin: from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Tarihe göre düz", + "grouped": "Gruplandırılmış", + "load_more": "Daha fazla yükle", + "preview_empty": "Önizlemek için bir sonuç seçin", + "open_full": "Aç", + "view_label": "Görünüm" + }, + "top_bar": { + "more": "Diğer seçenekler" + }, + "plugin_pages": { + "loading": "Sayfa yükleniyor", + "not_available_title": "Bu sayfa kullanılamıyor", + "not_available_description": "Bu sayfayı ekleyen eklenti kapalı veya artık yüklü değil.", + "back_to_mail": "Postaya dön", + "settings_group": "Eklentiler" + }, + "command_palette": { + "title": "Komut paleti", + "open": "Komut paletini aç", + "placeholder": "Bir komut yazın", + "no_results": "Eşleşen komut yok", + "go_to": "{name} bölümüne git", + "compose": "Yeni ileti", + "search": "Postada ara", + "theme_dark": "Koyu temaya geç", + "theme_light": "Açık temaya geç", + "shortcuts": "Klavye kısayollarını göster", + "group_navigation": "Git", + "group_actions": "Eylemler", + "group_plugins": "Eklentiler" + }, + "notification_center": { + "title": "Bildirimler", + "mark_all_read": "Tümünü okundu işaretle", + "clear": "Temizle", + "empty": "Bildirim yok", + "close": "Kapat", + "unread": "Okunmamış", + "unread_count": "{count} okunmamış", + "level_error": "Hata", + "level_warning": "Uyarı" + } +} diff --git a/locales/uk/common.json b/locales/uk/common.json index e670c40d..2eef54e2 100644 --- a/locales/uk/common.json +++ b/locales/uk/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "Мінімалістичний вебклієнт електронної пошти, що використовує протокол JMAP", - "login": { - "title": "Веб-пошта", - "username_label": "Електронна пошта", - "username_placeholder": "user@example.com", - "password_label": "Пароль", - "password_placeholder": "Введіть свій пароль", - "jmap_endpoint_label": "Сервер JMAP", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "Сервер має дозволити запити CORS із цього домену.", - "jmap_server_label": "Сервер", - "jmap_server_auto_picked": "Сервер обрано з домену вашої електронної пошти.", - "sign_in": "Увійдіть", - "signing_in": "Вхід...", - "loading": "Завантаження...", - "reconnecting": "З'єднання втрачено. Спроба повторного підключення…", - "error": { - "invalid_credentials": "Неправильна адреса електронної пошти або пароль. Перевірте свої облікові дані та повторіть спробу.", - "connection_failed": "Не вдалося підключитися до сервера. Перевірте підключення до Інтернету та повторіть спробу.", - "cors_blocked": "Сервер доступний, але він блокує перехресні запити. Перевірте налаштування CORS свого сервера JMAP і дозвольте цей домен.", - "server_error": "Сервер тимчасово недоступний. Спробуйте пізніше.", - "generic": "Сталася неочікувана помилка. Якщо це не зникне, зверніться до адміністратора.", - "totp_required": "Потрібен код двофакторної автентифікації. Будь ласка, введіть свій код нижче.", - "totp_invalid": "Недійсний код автентифікації. Перевірте свою програму автентифікації та повторіть спробу.", - "oauth_discovery_failed": "Система єдиного входу ввімкнена, але зв’язатися з постачальником ідентифікаційної інформації не вдалося. Перевірте конфігурацію OAuth." - }, - "show_password": "Показати пароль", - "hide_password": "Приховати пароль", - "totp_toggle": "У мене є код 2FA", - "remember_me": "пам'ятай мене", - "config_error": { - "title": "Помилка конфігурації", - "fetch_failed": "Не вдалося завантажити конфігурацію програми. Спробуйте пізніше.", - "server_not_configured": "Поштовий сервер не налаштовано. Будь ласка, зверніться до свого адміністратора." - }, - "remove_from_history": "Видалити з історії", - "totp_label": "Код автентифікації", - "totp_placeholder": "000000", - "session_expired": "Ваш сеанс закінчився. Увійдіть знову.", - "dismiss": "Відхилити", - "or": "або", - "sign_in_sso": "Увійдіть за допомогою SSO", - "add_account_title": "Додати обліковий запис", - "add_account_subtitle": "Увійдіть за допомогою іншого облікового запису", - "cancel": "Скасувати", - "website": "Веб-сайт", - "imprint": "Вихідні дані", - "privacy_policy": "Політика конфіденційності", - "try_demo": "Спробуйте демо", - "demo_description": "Досліджуйте за зразками даних - обліковий запис не потрібен", - "demo_launching": "Запуск демо...", - "demo_login_button": "Запустити демонстрацію", - "demo_tagline": "Спробуйте повнофункціональний поштовий клієнт. Обліковий запис не потрібен.", - "demo_no_signup": "Реєстрація не потрібна - вільно досліджуйте з зразками даних", - "oauth_completing": "Завершення входу...", - "oauth_error": { - "title": "Помилка автентифікації", - "invalid_state": "Помилка перевірки безпеки. Спробуйте ввійти знову.", - "missing_params": "Відсутні дані авторизації. Спробуйте ввійти знову.", - "token_exchange_failed": "Не вдалося завершити автентифікацію. Спробуйте ще раз.", - "access_denied": "У доступі відмовлено. Будь ласка, зверніться до свого адміністратора.", - "back_to_login": "Назад до входу" - } - }, - "sidebar": { - "close": "Закрити", - "compose": "Скласти", - "compose_hint": "Скласти (с)", - "search_placeholder": "Шукати пошту...", - "search_placeholder_hint": "Пошук пошти... (натисніть /)", - "storage": "Зберігання", - "storage_used": "б/в", - "storage_free": "безкоштовно", - "storage_total": "Всього", - "sign_out": "Вийти", - "sign_out_of": "Вийти з {account}", - "sign_out_all": "Вийти з усіх облікових записів", - "add_account": "Додати акаунт", - "set_as_default": "Встановити за умовчанням", - "switch_account": "Змінити обліковий запис", - "contacts": "Контакти", - "calendar": "Календар", - "settings": "Налаштування", - "admin": "адмін", - "files": "Файли", - "loading_mailboxes": "Завантаження поштових скриньок...", - "push_connected": "Активні оновлення в реальному часі", - "push_disconnected": "Оновлення в реальному часі неактивні", - "keyboard_shortcuts": "Комбінації клавіш", - "theme": { - "light": "Світловий режим", - "dark": "Темний режим", - "system": "Тема системи" - }, - "language": { - "title": "Мова" - }, - "mailboxes": { - "inbox": "Вхідні", - "sent": "Надіслано", - "drafts": "Чернетки", - "trash": "сміття", - "archive": "Архів", - "starred": "Позначено зірочкою", - "all_mail": "Вся пошта", - "spam": "Спам", - "important": "важливо" - }, - "unified_inbox": "Спільні вхідні", - "unified_sent": "Усі надіслані", - "unified_drafts": "Усі чернетки", - "unified_trash": "Усі кошики", - "unified_archive": "Усі архіви", - "unified_junk": "Весь спам", - "all_accounts": "Усі облікові записи", - "unified_mailbox": "Спільна поштова скринька", - "expand": "Розгорнути", - "collapse": "Згорнути", - "expand_tooltip": "Розгорнути", - "collapse_tooltip": "Згорнути", - "mobile": { - "search": "Пошук", - "compose": "Скласти", - "go_back": "Іди назад", - "toggle_menu": "Перемкнути меню" - }, - "clear_search": "Очистити пошук", - "vacation_active": "Автовідповідач активний", - "demo_banner": "Демонстраційний режим", - "demo_reset": "Скинути", - "demo_tour": "Тур", - "tags": "Теги", - "show_all_tags": "Показати всі ({count})", - "show_fewer_tags": "Показати менше", - "folders": "Папки", - "mail": "Пошта", - "nav_label": "Навігація", - "add_app": "програми", - "shared": "Спільні", - "scheduled": "Заплановано", - "unified_all_unread": "Усі непрочитані", - "unified_all_starred": "Усі позначені", - "unified_all_mail": "Уся пошта", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "Програми за замовчуванням", - "description": "Виберіть, чи відкривати посилання електронної пошти та календаря в Bulwark. Технічно Bulwark реєструється як обробник протоколу для посилань mailto: і webcal:.", - "unsupported": "Цей браузер або це з'єднання не підтримує ручну реєстрацію обробників протоколів. Можливо, встановлену PWA все одно можна використати через налаштування браузера або системи.", - "mailto_label": "Посилання електронної пошти", - "mailto_description": "Відкриває посилання mailto: у Bulwark із попередньо заповненим редактором листа.", - "protocol_open_mode_label": "Під час відкриття посилань протоколів", - "protocol_open_mode_description": "Виберіть, чи Bulwark має відкривати посилання mailto: і webcal: у новій вкладці, чи повторно використовувати відкритий сеанс. Для варіанта активного сеансу потрібен дозвіл на сповіщення, щоб ви могли натиснути резервне сповіщення й вивести Bulwark на передній план, якщо браузер блокує фокус.", - "protocol_open_mode_active_session": "Якщо можливо, відкривати в активному сеансі", - "protocol_open_mode_new_tab": "Завжди відкривати нову вкладку", - "focus_notification_title": "Відкрити Bulwark", - "focus_notification_body": "Посилання було відкрито в Bulwark. Натисніть, щоб вивести вікно на передній план.", - "webcal_label": "Посилання календаря", - "webcal_description": "Відкриває посилання webcal: у Bulwark із попередньо заповненим діалогом підписки на календар.", - "register_mailto": "Зареєструвати поштову програму", - "register_webcal": "Зареєструвати програму календаря", - "mailto_registered": "Реєстрацію обробника електронної пошти запитано", - "webcal_registered": "Реєстрацію обробника календаря запитано", - "registration_failed": "Не вдалося зареєструвати обробник протоколу", - "opening_mailto": "Відкриття редактора...", - "opening_webcal": "Відкриття календаря...", - "browser_note": "Браузер або операційна система може попросити підтвердження та може вимагати, щоб Bulwark був встановлений, перш ніж його можна буде вибрати програмою за замовчуванням.", - "select_account_title": "Виберіть акаунт", - "select_mailto_account": "Виберіть акаунт, у якому слід відкрити це посилання електронної пошти.", - "select_webcal_account": "Виберіть акаунт, у якому слід відкрити це посилання календаря.", - "select_account_note": "Цей вибір застосовується лише до цього посилання протоколу.", - "detail_to": "Кому", - "detail_subject": "Тема", - "detail_no_subject": "Без теми", - "detail_calendar": "Календар", - "detail_source": "Джерело", - "active_account": "Активний", - "switching_account": "Перемикання акаунта..." - }, - "sidebar_apps": { - "modal_title": "Програми бічної панелі", - "add_new": "Додати додаток", - "edit_app": "Редагувати додаток", - "name_label": "Ім'я", - "name_placeholder": "Мій додаток", - "name_required": "Необхідно вказати ім'я", - "url_label": "URL", - "url_required": "Необхідна URL-адреса", - "url_invalid": "Введіть дійсну URL-адресу http або https", - "icon_label": "значок", - "icon_required": "Потрібен значок", - "open_mode_label": "Відкритий режим", - "open_new_tab": "Нова вкладка", - "open_inline": "Вбудований", - "cancel": "Скасувати", - "add": "додати", - "update": "оновлення", - "delete": "Видалити", - "delete_confirm_title": "Видалити додаток", - "delete_confirm": "Ви впевнені, що хочете видалити \"{name}\"?", - "no_apps": "Ще не додано жодної програми", - "no_apps_hint": "Додайте власні програми та посилання на бічну панель", - "managed_title": "Надано вашим адміністратором", - "managed_description": "Ці застосунки налаштовано для всіх, і їх не можна змінити.", - "managed_badge": "Кероване", - "search_icons": "Значки пошуку...", - "show_popular": "Популярний", - "show_all": "все", - "no_icons_found": "Значок не знайдено", - "inline_badge": "Вбудований", - "tab_badge": "вкладка", - "show_on_mobile": "Показати на мобільному" - }, - "email_list": { - "no_emails": "Повідомлень не знайдено", - "no_emails_description": "Ця поштова скринька порожня", - "no_search_results": "Результатів не знайдено", - "no_search_results_description": "Спробуйте налаштувати пошук або фільтри", - "loading": "Завантаження електронних листів...", - "unread": "непрочитаний", - "to_me": "мені", - "to_recipients": "Для {count} одержувачів", - "and_others": "та {count} інші", - "draft": "Чернетка", - "starred": "Позначено зірочкою", - "pinned": "Закріплено", - "replied": "Відповіли", - "forwarded": "Переслано", - "has_attachment": "З вкладенням", - "conversations_count": "{count} з {total} розмов", - "conversations_count_plus": "{count}+ розмов", - "conversations_count_simple": "{count} розмов", - "no_conversations": "Жодних розмов", - "loading_more": "Завантаження інших електронних листів...", - "no_more_emails": "Більше не потрібно завантажувати електронні листи", - "batch_actions": { - "select": "Виберіть електронні листи", - "select_all": "Вибрати все", - "selected_messages": "Вибрано {count, plural, one {1 електронний лист} other {# електронні листи}}", - "mark_read": "Позначити як прочитане", - "mark_unread": "Позначити як непрочитане", - "delete": "Видалити", - "delete_confirm_title": "Видалити електронні листи", - "delete_confirm_message": "Ви впевнені, що хочете видалити {count, plural, one {1 email} other {# emails}}?", - "clear_selection": "Очистити вибір" - }, - "permanent_delete": "Видалити остаточно", - "permanent_delete_confirm_title": "Видалити назавжди", - "permanent_delete_confirm_message": "Цей електронний лист буде видалено назавжди. Цю дію не можна скасувати.", - "permanent_delete_confirm_batch_message": "Ці {count, plural, one {1 email} other {# emails}} буде остаточно видалено. Цю дію не можна скасувати.", - "empty_folder": { - "button": "Порожня папка", - "confirm_title": "Порожня папка", - "confirm_message": "Усі електронні листи в цій папці буде остаточно видалено. Цю дію не можна скасувати.", - "confirm_button": "Порожня папка", - "junk_hint": "Ви можете очистити папку «Сміття», щоб остаточно видалити всі повідомлення.", - "trash_hint": "Ви можете очистити папку «Кошик», щоб остаточно видалити всі повідомлення." - }, - "no_scheduled_emails": "Немає запланованих листів", - "no_scheduled_emails_description": "Повідомлення, заплановані на пізніше, з’являться тут.", - "scheduled_count": "{count, plural, one {# запланований лист} few {# заплановані листи} other {# запланованих листів}}", - "scheduled_actions_hint": "Заплановані повідомлення можна скасувати, перенести або редагувати через дії планування.", - "cancel_scheduled_send": "Скасувати надсилання", - "reschedule_send": "Перенести", - "cancel_and_edit": "Скасувати й редагувати", - "cancel_and_compose_again": "Скасувати й написати знову", - "reschedule_prompt": "Введіть нову дату/час, напр. 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "Відправник запитує сповіщення про прочитання:", - "send": "Надіслати сповіщення", - "ignore": "Ігнорувати", - "sent": "Сповіщення про прочитання надіслано.", - "send_failed": "Не вдалося надіслати сповіщення про прочитання", - "mdn_subject": "Прочитано: {subject}", - "mdn_body": "Це сповіщення про прочитання повідомлення, яке ви надіслали на адресу {recipient}.\n\nПримітка: це сповіщення лише підтверджує, що повідомлення було показано на комп''ютері отримувача. Воно не гарантує, що отримувач прочитав або зрозумів вміст." - }, - "no_email_selected": "Електронна адреса не вибрана", - "no_email_description": "Виберіть електронний лист зі списку, щоб переглянути його тут", - "no_conversation_selected": "Розмова не вибрана", - "no_conversation_description": "Виберіть розмову зі списку, щоб прочитати її тут", - "compose": "Скласти", - "compose_hint": "Написати нове повідомлення", - "no_subject": "(без теми)", - "no_body_content": "(вміст відсутній)", - "no_preview_available": "Попередній перегляд недоступний", - "show_quoted_text": "Показати цитований текст", - "hide_quoted_text": "Приховати цитований текст", - "loading_email": "Завантаження електронної пошти...", - "loading": "Завантаження...", - "reply": "Відповісти", - "reply_all": "Відповісти всім", - "forward": "вперед", - "delete": "Видалити", - "archive": "Архів", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "зірка", - "unstar": "Зняти зірочку", - "mark_unread": "Позначити як непрочитане", - "mark_read": "Позначити як прочитане", - "unread": "Непрочитане", - "read": "Прочитане", - "spam_short": "Спам", - "not_spam_short": "Не спам", - "move": "Перенести", - "print": "Роздрукувати", - "view_source": "Переглянути джерело", - "fullscreen": "На весь екран", - "exit_fullscreen": "Вийти з повноекранного режиму", - "export_email": "Експортувати як .eml", - "forward_as_attachment": "Переслати як вкладення", - "import_email": "Імпорт .eml або .zip", - "keyboard_shortcuts": "Комбінації клавіш (?)", - "email_source": "Джерело електронної пошти", - "draft_banner": "Це повідомлення є чернеткою", - "edit_draft": "Редагувати", - "copy_source": "Копіювати в буфер обміну", - "source_copied": "Джерело скопійовано в буфер обміну", - "attachments": "Додатки", - "important": "важливо", - "download": "Завантажити", - "download_all": "Завантажити все", - "from": "Від", - "to": "до", - "cc": "CC", - "bcc": "BCC", - "date": "Дата", - "subject": "Тема", - "show_details": "Показати деталі", - "hide_details": "Приховати деталі", - "external_content_warning": "Зображення та зовнішній вміст заблоковано", - "load_external_content": "Завантажити зображення", - "trust_sender": "Завжди довіряйте цьому відправнику", - "back_to_list": "Повернутися до списку", - "view_contact": "Переглянути контакт", - "message_details": "Деталі повідомлення", - "more_reply_options": "Більше варіантів відповіді", - "set_tag": "Встановити тег", - "tag": "Тег", - "more_actions": "Більше дій", - "previous": "попередня", - "next": "Далі", - "move_to": "Перейти до...", - "remove_tag": "Видалити тег", - "tag_filter_placeholder": "Фільтр тегів", - "tag_no_matches": "Немає відповідних тегів", - "more_count": "+ ще {count}", - "characters_count": "{count} символів", - "quick_reply_placeholder": "Напишіть швидку відповідь...", - "more_options": "Більше варіантів", - "sending": "Надсилання...", - "security_authentication": "Безпека та автентифікація", - "technical_details": "Технічні деталі", - "message_id_label": "ID повідомлення:", - "reply_to_label": "Відповідь:", - "delivery_time_label": "Термін доставки:", - "conversation_part_label": "Частина розмови:", - "previous_messages": "{count} попереднє повідомлення", - "previous_messages_plural": "{count} попередніх повідомлень", - "time": { - "day": "день", - "days": "днів", - "hour": "година", - "hours": "години", - "minute": "хвилина", - "minutes": "хвилин" - }, - "unknown_sender": "Невідомий", - "recipient_me": "мене", - "recipient_and_others": "{name} та {count} інші", - "recipient_to_prefix": "до:", - "authentication": { - "title": "Аутентифікація", - "status": { - "verified": "Перевірено", - "warning": "УВАГА", - "none": "Не автентифіковано" - }, - "spf": { - "pass": "SPF пройдено", - "fail": "Збій SPF", - "none": "Без SPF" - }, - "dkim": { - "pass": "DKIM дійсний", - "fail": "Недійсний DKIM", - "none": "Без DKIM" - }, - "dmarc": { - "pass": "Перепустка DMARC", - "fail": "Помилка DMARC", - "none": "Без DMARC" - }, - "spam_score": "Оцінка спаму", - "tooltip_spf": "Sender Policy Framework: перевіряє, чи має сервер-відправник право надсилати пошту від імені домену", - "tooltip_dkim": "DomainKeys Identified Mail: підтверджує криптографічним підписом, що лист не було змінено під час пересилання", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: забезпечує узгодженість SPF і DKIM із доменом відправника та встановлює політику для збоїв", - "policy": "Політика", - "result": { - "pass": "Пройдено", - "fail": "Не пройдено", - "softfail": "М'який збій", - "neutral": "Нейтрально", - "permerror": "Постійна помилка", - "temperror": "Тимчасова помилка", - "none": "Немає" - } - }, - "headers": { - "routing": "Маршрутизація", - "received": "Отримано", - "message_id": "ID повідомлення", - "list_info": "Інформація про список" - }, - "tooltips": { - "reply": "Відповісти (р)", - "reply_all": "Відповісти всім (а)", - "forward": "Вперед (f)", - "archive": "Архів (e)", - "delete": "Видалити (# або Del)", - "star": "зірка (и)", - "unstar": "Зняти зірочки", - "compose": "Скласти (с)", - "previous": "Попередній електронний лист", - "next": "Наступний електронний лист", - "edit_draft": "Редагувати чернетку" - }, - "spam": { - "button_title": "Повідомити про спам", - "not_spam_title": "Позначити як законне", - "toast_success": "Перенесено в сміття", - "toast_batch": "{count} електронних листів переміщено до спаму", - "toast_undo": "Скасувати", - "toast_not_spam_success": "Переміщено до папки \"Вхідні\".", - "toast_not_spam_batch": "{count} електронних листів переміщено до папки \"Вхідні\".", - "error": "Не вдалося повідомити про спам", - "error_not_spam": "Не вдалося відновити електронну пошту" - }, - "unsubscribe_banner": { - "label": "Інформаційний бюлетень", - "button": "Відписатися", - "confirm_title": "Скасувати підписку на цього відправника?", - "confirm_button": "Підтвердити", - "cancel": "Скасувати", - "success_http": "Сторінка скасування підписки відкрилася в новій вкладці", - "success_mailto": "Лист для відписки надіслано", - "confirm_message_http": "Сторінка відписки відкриється в новій вкладці.", - "confirm_message_mailto": "Відправнику буде надіслано лист для відписки.", - "error": "Неможливо скасувати підписку", - "dismiss": "Відхилити" - }, - "calendar_invitation": { - "loading": "Завантаження деталей події…", - "title": "Календар запрошення", - "published_title": "Опублікована подія", - "response_title": "Реакція на подію", - "update_title": "Оновлення події", - "counter_title": "Зустрічна пропозиція", - "refresh_title": "Запит на оновлення", - "declined_counter_title": "Зустрічна пропозиція відхилена", - "cancelled_title": "Подію скасовано", - "organizer": "Організовано {name}", - "organizer_label": "Організовано", - "attendees": "{count, plural, one {# учасник} few {# учасники} many {# учасників} other {# учасників}}", - "accept": "прийняти", - "maybe": "можливо", - "decline": "відхилити", - "add_to_calendar": "Додати в календар", - "added": "Додано до календаря", - "rsvp_sent": "Відповідь надіслано", - "parse_error": "Не вдалося прочитати запрошення", - "action_failed": "Не вдалося виконати цю дію календаря.", - "no_calendar": "Календар недоступний", - "published_info": "Ця подія була опублікована для ознайомлення.", - "response_info": "Це повідомлення містить відповідь учасника.", - "response_info_organizer": "Ця відповідь учасника оновлює вашу подію.", - "update_info": "Це повідомлення оновлює наявну подію.", - "counter_info": "Це повідомлення пропонує зміни до події.", - "counter_info_organizer": "Цей учасник запропонував зміни до вашої події.", - "refresh_info": "У цьому повідомленні запитуються останні деталі події.", - "refresh_info_organizer": "Учасник попросив останні подробиці події.", - "declined_counter_info": "Організатор відхилив зустрічну пропозицію.", - "authentication_failed_info": "Не вдалося перевірити автентифікацію пошти для цього запрошення. Ставтеся до дій календаря з обережністю.", - "authentication_missing_info": "Це запрошення не включає перевірену автентифікацію електронної пошти. Уточнюйте деталі в організатора, якщо щось виглядає незвичним.", - "sender_mismatch_info": "Це запрошення надіслано з {sender}, а організатор, зазначений у даних календаря, {organizer}.", - "sender_mismatch_unverified_info": "Це запрошення було надіслано з {sender}, а організатором, указаним у даних календаря, є {organizer}, і повідомлення не вдалося перевірити.", - "organizer_role": "Ви організовуєте цю подію", - "your_response": "Ваша відповідь: {status}", - "response_needed": "Потребує відповіді", - "response_accepted": "прийнято", - "response_tentative": "Орієнтовний", - "response_declined": "Відхилено", - "response_delegated": "Делегований", - "actor_sent_info": "Надіслано {name}.", - "actor_response_info": "{name} відповів {status}.", - "actor_counter_info": "{name} запропонував зміни до цієї події.", - "actor_refresh_info": "{name} запитав останні деталі події.", - "actor_declined_counter_info": "{name} відхилив зустрічну пропозицію.", - "actor_note": "Примітка: {comment}", - "actor_unknown": "Хтось", - "proposed_changes": "Пропоновані зміни", - "change_title": "Назва", - "change_time": "час", - "change_location": "Розташування", - "change_description": "опис", - "change_empty": "Жодного", - "change_from_to": "{before} -> {after}", - "apply_proposal": "Застосувати запропоновані зміни", - "proposal_applied": "Запропоновані зміни застосовано.", - "review_proposal": "Перегляньте пропозицію", - "review_request": "Запит на перегляд", - "view_in_calendar": "Переглянути в календарі", - "select_calendar": "Виберіть календар", - "already_in_calendar": "Уже у вашому календарі", - "request_info": "Вас запросили на цю подію. Дайте відповідь, щоб повідомити організатора про вашу доступність.", - "cancel_info": "Організатор скасував цю подію.", - "event_updated": "Оновлення №{sequence}", - "event_status_tentative": "Орієнтовний", - "event_status_cancelled": "Скасовано", - "collapse": "Сховати деталі", - "expand": "Показати деталі" - }, - "send": "Надіслати", - "more": "більше", - "details": { - "recipients_routing": "Одержувачі та маршрутизація", - "authentication_security": "Аутентифікація та безпека", - "identifiers_threading": "Ідентифікатори та обговорення", - "mailing_list": "Список розсилки", - "message_properties": "Властивості повідомлення", - "sent": "Надіслано", - "received": "Отримано", - "delivery_time": "Час доставки", - "in_reply_to": "У відповідь на", - "references": "Посилання", - "thread_id": "ID обговорення", - "size": "Розмір", - "mime_type": "Тип MIME", - "attachments_summary": "{count} файлів · {size}", - "list_id": "ID списку", - "list_help": "Довідка списку", - "list_post": "Публікація у список", - "list_unsubscribe": "Відписатися", - "iprev": "Зворотний DNS", - "spam_status": "Статус спаму", - "ai_verdict": "Висновок ШІ", - "account": "Обліковий запис", - "no_subject": "(без теми)" - }, - "scheduled_banner": "Заплановано на надсилання {date}", - "scheduled_send_created": "Лист заплановано до надсилання", - "cancel_scheduled_send": "Скасувати надсилання", - "reschedule_send": "Перенести", - "cancel_and_edit": "Скасувати й редагувати", - "cancel_and_compose_again": "Скасувати й написати знову", - "reschedule_prompt": "Введіть нову дату/час, напр. 2026-05-04T15:30", - "scheduled_actions_only": "У поданні Заплановано доступні лише дії планування", - "undo_send_scheduled": "Повідомлення заплановано до надсилання", - "undo_send": "Скасувати надсилання", - "contact_sidebar": { - "title": "Контакт", - "close": "Закрити бічну панель", - "action_email": "Лист", - "action_email_title": "Надіслати лист", - "action_copy": "Копіювати", - "action_copy_title": "Копіювати адресу", - "action_edit_title": "Редагувати контакт", - "section_emails": "Електронні адреси", - "section_phones": "Телефони", - "section_organizations": "Організації", - "section_addresses": "Адреси", - "section_notes": "Нотатки", - "not_in_contacts": "Немає у ваших контактах", - "add_to_contacts": "Додати до контактів", - "copied": "Скопійовано!", - "copy_failed": "Не вдалося скопіювати" - }, - "send_now": "Надіслати зараз" - }, - "email_composer": { - "read_receipt_on": "Запитано сповіщення про прочитання (натисніть, щоб вимкнути)", - "read_receipt_off": "Запитати сповіщення про прочитання", - "dsn_on": "Запитано сповіщення про доставку (натисніть, щоб вимкнути)", - "dsn_off": "Запитати сповіщення про доставку", - "format_plain_text": "Перемкнути на простий текст", - "format_rich_text": "Перемкнути на форматований текст (HTML)", - "require_tls_on": "Потрібна зашифрована доставка (натисніть, щоб вимкнути)", - "require_tls_off": "Вимагати зашифровану доставку (TLS)", - "new_message": "Нове повідомлення", - "signature_edit_hint": "Двічі клацніть, щоб редагувати підпис", - "reply": "Відповісти", - "reply_all": "Відповісти всім", - "forward": "вперед", - "reply_to": "Відповісти", - "reply_all_to": "Відповісти всім", - "forward_message": "вперед", - "from": "Від", - "to": "до", - "cc": "CC", - "bcc": "BCC", - "subject": "Тема", - "body_placeholder": "Напишіть своє повідомлення...", - "send": "Надіслати", - "cancel": "Скасувати", - "attach": "Прикріпити", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "Відкинути", - "discard_draft_title": "Відхилити чернетку?", - "discard_draft_confirm": "У вас є незбережені зміни. Ви хочете відхилити цю чернетку?", - "saving": "Збереження...", - "draft_saved": "Чернетку збережено", - "save_failed": "Не вдалося зберегти", - "to_placeholder": "Адреси електронної пошти одержувачів", - "cc_placeholder": "Одержувачі копії", - "bcc_placeholder": "Одержувачі прихованої копії", - "subject_placeholder": "Тема", - "cc_label": "Копія:", - "bcc_label": "Прихована копія:", - "subject_label": "Тема:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(без теми)", - "unknown_sender": "Невідомий", - "quote": { - "reply_header": "{date}, {sender} написав:", - "forward_header": "---------- Переслане повідомлення ----------", - "from": "Від: {sender}", - "date": "Дата: {date}", - "subject": "Тема: {subject}", - "to": "Кому: {recipients}" - }, - "remove_sub_address": "Видалити підадресу", - "from_override": { - "toggle_off": "Замінити", - "toggle_on": "Скасувати заміну", - "toggle_tooltip": "Вільно редагуйте ім'я та адресу відправника. Пошта все ще надсилається через вашу ідентичність.", - "name_label": "Ім'я відправника", - "name_placeholder": "Ім'я", - "email_label": "Електронна адреса відправника", - "email_placeholder": "alias@example.com", - "envelope_notice": "Якщо ваш сервер не приймає цю адресу як відправника конверта, замість неї використовується {identity}, і одержувачі бачитимуть її в заголовку Return-Path." - }, - "use_template": "Шаблон", - "save_as_template": "Зберегти як шаблон", - "validation": { - "recipient_required": "Додайте одержувача для надсилання", - "body_required": "Напишіть повідомлення або прикріпіть файл", - "attachments_uploading": "Вкладення ще завантажуються — надсилання почнеться після завершення", - "attachment_upload_failed": "Не надіслано — не вдалося завантажити вкладення. Видаліть його та спробуйте ще раз." - }, - "upload_progress": "Завантаження {uploaded} / {total}", - "upload_cancel": "Скасувати завантаження", - "upload_failed": "Не вдалося завантажити {filename}", - "attachment_too_large": "«{name}» більший, ніж дозволяє сервер ({max} на файл)", - "attachments_total_too_large": "Вкладення перевищать ліміт сервера {max} на лист", - "drop_files": "Перетягніть файли, щоб прикріпити", - "show_less": "Показувати менше", - "send_failed": "Не вдалося надіслати електронний лист", - "send_timeout": "Сервер перестав відповідати. Перш ніж надсилати знову, перевірте теку «Надіслані» — лист міг уже піти.", - "continue_draft": "Продовжити чернетку", - "close_draft_title": "Зберегти чи видалити чернетку?", - "close_draft_message": "У вас є незбережені зміни. Зберегти це як чернетку чи видалити?", - "smime_sign_on": "Підпис S/MIME увімкнено", - "smime_sign_off": "Увімкнути підпис S/MIME", - "smime_encrypt_on": "Шифрування S/MIME увімкнено", - "smime_encrypt_off": "Увімкніть шифрування S/MIME", - "smime_encrypt_unavailable": "Шифрування S/MIME недоступне – відсутні сертифікати одержувача", - "smime_unlock_title": "Розблокувати ключ S/MIME", - "smime_unlock_message": "Введіть парольну фразу, щоб розблокувати ключ підпису S/MIME.", - "smime_unlock_button": "Розблокувати", - "smime_passphrase_placeholder": "Парольна фраза", - "forgot_attachment": { - "title": "Ви забули вкладення?", - "message": "У вашому повідомленні згадується \"{keyword}\", але файл не вкладено. Все одно надіслати?", - "send_anyway": "Все одно надішліть", - "back": "Назад до редагування" - }, - "empty_subject": { - "title": "Надіслати без теми?", - "message": "Цей лист не має теми. Надіслати попри це?", - "dont_ask_again": "Більше не запитувати", - "send_anyway": "Все одно надішліть", - "back": "Назад до редагування" - }, - "add_link": "Додати посилання", - "link_url_prompt": "Введіть URL", - "sending": "Надсилання...", - "schedule_send": "Запланувати надсилання", - "schedule_send_description": "Виберіть, коли сервер має надіслати це повідомлення.", - "schedule_send_required": "Виберіть дату й час.", - "schedule_send_invalid": "Введіть дійсні дату й час.", - "schedule_send_future": "Виберіть дату й час у майбутньому.", - "schedule_send_too_late": "Цей час пізніший, ніж дозволяє сервер.", - "schedule_send_unsupported": "Заплановане надсилання не підтримується для цього облікового запису.", - "schedule_send_cleanup_warning": "Заплановане надсилання створено, але очищення чернетки не вдалося.", - "send_delay_unsupported": "Затримка надсилання не підтримується для цього облікового запису.", - "send_delay_unsupported_confirm": "Цей обліковий запис не підтримує затримку надсилання. Надіслати негайно?", - "recipient_edit_email": "Змінити адресу ел. пошти", - "recipient_edit_name": "Змінити відображуване ім'я", - "recipient_email_placeholder": "Адреса ел. пошти", - "recipient_name_placeholder": "Відображуване ім'я", - "autocomplete_search_server": "Шукати на сервері", - "autocomplete_searching": "Пошук...", - "toolbar": { - "bold": "Жирний", - "italic": "Курсив", - "underline": "Підкреслений", - "strikethrough": "Закреслений", - "text_color": "Колір тексту", - "remove_color": "Прибрати колір", - "heading_1": "Заголовок 1", - "heading_2": "Заголовок 2", - "bullet_list": "Маркований список", - "ordered_list": "Нумерований список", - "quote": "Цитата", - "code_block": "Блок коду", - "align_left": "По лівому краю", - "align_center": "По центру", - "align_right": "По правому краю", - "text_direction": "Напрямок тексту (RTL/LTR)", - "link": "Посилання", - "table": "Таблиця", - "clear_formatting": "Очистити форматування", - "undo": "Скасувати", - "redo": "Повторити", - "add_row_above": "Вставити рядок вище", - "add_row_below": "Вставити рядок нижче", - "add_column_before": "Вставити стовпець ліворуч", - "add_column_after": "Вставити стовпець праворуч", - "delete_row": "Видалити рядок", - "delete_column": "Видалити стовпець", - "toggle_header_row": "Переключити рядок заголовка", - "delete_table": "Видалити таблицю", - "pick_size": "Вибрати розмір", - "font_size": "Розмір шрифту", - "font_size_default": "За замовчуванням", - "background_color": "Колір фону", - "remove_background_color": "Прибрати колір фону" - }, - "send_filing_warning": "Надіслано - але подальше очищення не вдалося, може залишитися застаріла чернетка." - }, - "confirm_dialog": { - "confirm": "Підтвердити", - "cancel": "Скасувати" - }, - "common": { - "loading": "Завантаження...", - "error": "Помилка", - "success": "Успіх", - "cancel": "Скасувати", - "save": "зберегти", - "delete": "Видалити", - "edit": "Редагувати", - "close": "Закрити", - "search": "Пошук", - "refresh": "Оновити", - "settings": "Налаштування", - "help": "Довідка", - "logout": "Вийти", - "yes": "так", - "no": "немає", - "unknown": "Невідомий", - "app_title": "Веб-пошта", - "reconnecting": "З'єднання втрачено. Спроба повторного підключення…", - "rate_limited_title": "Швидкість автентифікації сервера тимчасово обмежена.", - "rate_limited_detail": "Bulwark призупинив фонові запити, щоб уникнути блокування. Повторна спроба через {seconds} с.", - "rate_limited_action_title": "Запит призупинено, щоб уникнути блокування.", - "rate_limited_action_detail": "Bulwark чекає закінчення часу відновлення сервера, перш ніж надсилати додаткові автентифіковані запити. Повторіть спробу через {seconds} с.", - "pull_to_refresh": "Потягніть, щоб оновити", - "release_to_refresh": "Відпустіть, щоб оновити", - "refreshing": "Оновлення…" - }, - "notifications": { - "email_sent": "Електронна пошта успішно надіслана", - "email_deleted": "Електронну пошту видалено", - "email_archived": "Електронний лист заархівовано", - "emails_deleted": "{count, plural, one {# лист видалено} few {# листи видалено} many {# листів видалено} other {# листа видалено}}", - "emails_archived": "{count, plural, one {# лист архівовано} few {# листи архівовано} many {# листів архівовано} other {# листа архівовано}}", - "error_archiving": "Не вдалося архівувати лист", - "error_updating": "Не вдалося оновити лист", - "email_starred": "Електронна пошта позначена зірочкою", - "email_unstarred": "З електронної пошти знято зірочку", - "email_marked_read": "Електронна пошта позначена як прочитана", - "email_marked_unread": "Електронна пошта позначена як непрочитана", - "copied_to_clipboard": "Скопійовано в буфер обміну", - "source_copied": "Джерело скопійовано в буфер обміну", - "error_sending": "Не вдалося надіслати електронний лист", - "error_deleting": "Не вдалося видалити електронну пошту", - "error_loading": "Не вдалося завантажити електронні листи", - "new_email": "Нова електронна пошта", - "new_email_from": "Від {sender}", - "click_to_view": "Натисніть, щоб переглянути", - "email_moved": "Електронну адресу переміщено", - "emails_moved": "{count} електронних листів переміщено", - "moved_to_mailbox": "Переміщено до {mailbox}", - "move_failed": "Не вдалося перемістити", - "move_error": "Не вдалося перемістити електронні листи до вибраної папки", - "email_tagged": "Електронна пошта позначена тегами", - "emails_tagged": "{count} електронних листів позначено тегами", - "tag_failed": "Помилка тегування", - "identity_created": "Ідентичність створено успішно", - "identity_updated": "Ідентичність успішно оновлено", - "identity_deleted": "Ідентичність видалено", - "identity_set_primary": "Основну особу оновлено", - "identity_create_failed": "Не вдалося створити ідентифікатор: {error}", - "identity_update_failed": "Не вдалося оновити ідентифікатор: {error}", - "identity_delete_failed": "Не вдалося видалити особу: {error}", - "identity_unauthorized": "Ви не авторизовані надсилати з цієї електронної адреси", - "identity_not_found": "Особа не знайдена", - "vacation_saved": "Налаштування автовідповідача збережено", - "vacation_save_failed": "Не вдалося зберегти налаштування автовідповідача", - "filters_saved": "Фільтри успішно збережено", - "filters_save_failed": "Не вдалося зберегти фільтри", - "filters_deleted": "Правило фільтра видалено", - "templates_exported": "Шаблони успішно експортовано", - "templates_imported": "{count, plural, one {# template} other {# templates}} імпортовано", - "templates_import_errors": "Деякі шаблони не вдалося імпортувати", - "templates_import_empty": "У файлі не знайдено шаблонів", - "export_email_error": "Не вдалося експортувати електронну пошту", - "import_email_success": "Електронну пошту успішно імпортовано", - "import_email_error": "Не вдалося імпортувати електронну пошту" - }, - "date": { - "today": "Сьогодні", - "yesterday": "вчора", - "this_week": "Цього тижня", - "last_week": "Минулого тижня", - "this_month": "Цей місяць", - "older": "Старший", - "just_now": "Просто зараз", - "minutes_ago": "{count} хвилин тому", - "minutes_ago_plural": "{count} хвилин тому", - "hours_ago": "{count} годин тому", - "hours_ago_plural": "{count} годин тому", - "days_ago": "{count} день тому", - "days_ago_plural": "{count} днів тому" - }, - "language": { - "title": "мова", - "english": "англійська", - "french": "Français", - "japanese": "日本語", - "spanish": "Español", - "italian": "Italiano", - "german": "Deutsch", - "dutch": "Нідерланди", - "portuguese": "Português", - "russian": "Русский", - "select_language": "Виберіть мову", - "switch_to_english": "Переключитися на англійську", - "switch_to_french": "Перейти на французьку", - "switch_to_japanese": "Перейти на японську", - "switch_to_spanish": "Перейти на іспанську", - "switch_to_italian": "Перейти на італійську", - "switch_to_german": "Перейти на німецьку", - "switch_to_dutch": "Перейти на голландську", - "switch_to_portuguese": "Переключитися на португальську", - "switch_to_russian": "Перейти на російську", - "switching": "Зміна мови...", - "switch": "Змінити мову", - "current": "Поточна мова", - "en": "англійська", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Нідерланди", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "polish": "Polski", - "switch_to_polish": "Перемкнути на польську", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "Перейти на фарсі", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "Перейти на монгольську" - }, - "settings": { - "title": "Налаштування", - "back_to_mail": "Назад до пошти", - "save_success": "Налаштування успішно збережено", - "import_success": "Налаштування успішно імпортовано", - "import_error": "Не вдалося імпортувати налаштування", - "reset_confirm": "Ви впевнені, що бажаєте скинути всі налаштування до стандартних?", - "unsaved_changes": "У вас є незбережені зміни", - "discard_changes": "Скасувати незбережені зміни?", - "discard": "Відкинути", - "keep_editing": "Продовжуйте редагувати", - "tabs": { - "appearance": "Зовнішній вигляд", - "language": "Мова, регіон і час", - "email": "Поведінка електронної пошти", - "composer": "Композитор", - "privacy": "Конфіденційність і безпека", - "account": "Обліковий запис", - "identities": "Ідентичності", - "vacation": "Відповідач у відпустці", - "advanced": "Просунутий", - "calendar": "Календар", - "filters": "Фільтри", - "templates": "Шаблони", - "folders": "Папки", - "keywords": "Ключові слова", - "security": "Безпека", - "files": "Файли", - "contacts": "Контакти", - "encryption": "Шифрування", - "protocol_handlers": "Програми за замовчуванням", - "sidebar_apps": "Програми бічної панелі", - "notifications": "Сповіщення", - "layout": "Макет", - "reading": "Читання", - "composing": "Написання", - "downloads": "Завантаження", - "content_senders": "Вміст і відправники", - "about_data": "Про програму та дані", - "debug": "Налагодження" - }, - "tab_groups": { - "general": "Загальний", - "account": "Обліковий запис і ідентифікатор", - "organization": "Організація пошти", - "apps": "програми", - "system": "система", - "appearance": "Вигляд", - "mail": "Пошта", - "privacy": "Конфіденційність і безпека", - "advanced": "Додатково" - }, - "appearance": { - "message_list_order": { - "label": "Порядок списку повідомлень", - "description": "Як упорядковано повідомлення в списку. Сортування виконує сервер, тому воно охоплює всю теку, а не лише завантажені повідомлення.", - "preset": { - "chronological": "Хронологічно (спочатку нові)", - "unread_first": "Спочатку непрочитані", - "starred_first": "Спочатку із зірочкою", - "tagged_first": "Спочатку з міткою", - "custom": "Власний" - }, - "tag_label": "Мітка", - "advanced_show": "Додатково: змінити рівні сортування", - "advanced_hide": "Сховати додаткові", - "criterion_label": "Критерій рівня {n}", - "direction_label": "Напрямок рівня {n}", - "criteria": { - "unread": "Непрочитані", - "starred": "Із зірочкою", - "tag": "Мітка", - "receivedAt": "Дата отримання", - "sentAt": "Дата надсилання", - "from": "Відправник", - "subject": "Тема", - "size": "Розмір" - }, - "direction": { - "unread_first": "Спочатку непрочитані", - "read_first": "Спочатку прочитані", - "starred_first": "Спочатку із зірочкою", - "unstarred_first": "Спочатку без зірочки", - "tagged_first": "Спочатку з міткою", - "untagged_first": "Спочатку без мітки", - "newest_first": "Спочатку нові", - "oldest_first": "Спочатку старі", - "a_to_z": "А → Я", - "z_to_a": "Я → А", - "largest_first": "Спочатку великі", - "smallest_first": "Спочатку малі" - }, - "add_level": "Додати рівень", - "remove_level": "Вилучити рівень", - "move_up": "Вище", - "move_down": "Нижче", - "tiebreak_note": "До трьох рівнів; наступні рівні вирішують за рівності. «Спочатку нові» завжди застосовується останнім.", - "scope": { - "label": "Застосувати до всіх тек", - "description": "Якщо вимкнено, порядок діє лише для теки «Вхідні»; інші теки лишаються хронологічними." - }, - "unsupported_note": "Ваш поштовий сервер не підтримує сортування за станом прочитання, зірочками чи мітками. Ці рівні ігноруються, і список лишається хронологічним." - }, - "title": "Зовнішній вигляд", - "description": "Налаштуйте зовнішній вигляд вашої веб-пошти", - "theme": { - "label": "Тема", - "description": "Виберіть бажану колірну схему", - "light": "світло", - "dark": "Темний", - "system": "система" - }, - "language": { - "label": "Мова", - "description": "Виберіть бажану мову" - }, - "font_size": { - "label": "Розмір шрифту", - "description": "Налаштуйте розмір тексту для кращої читабельності", - "small": "Маленький", - "medium": "Середній", - "large": "Великий" - }, - "list_density": { - "label": "Щільність", - "description": "Контролюйте інтервали та відступи в інтерфейсі", - "extra_compact": "Дуже компактний", - "compact": "Компактний", - "regular": "Регулярний", - "comfortable": "Комфортно" - }, - "animations": { - "label": "Увімкнути анімацію", - "description": "Показувати плавні переходи та ефекти" - }, - "toolbar_position": { - "label": "Положення панелі інструментів", - "description": "Де відображати кнопки дій електронної пошти (Відповісти, Архівувати, Видалити тощо)", - "top": "Топ", - "below_subject": "Тема нижче" - }, - "toolbar_labels": { - "label": "Показати мітки панелі інструментів", - "description": "Відображати текстові мітки поруч із піктограмами панелі інструментів. Вимкніть, щоб заощадити місце, коли ви ознайомитеся з піктограмами." - }, - "hide_account_switcher": { - "label": "Приховати перемикач облікових записів на бічній панелі", - "description": "Сховайте селектор облікових записів у верхній частині бічної панелі папок. Ви все ще можете перемикати облікові записи з нижньої панелі навігації." - }, - "show_rail_account_list": { - "label": "Показувати аватари облікових записів на панелі навігації", - "description": "Відображати кола окремих облікових записів у нижній частині панелі навігації для швидкого перемикання з кнопкою виходу внизу." - }, - "unified_mailbox": { - "label": "Спільна поштова скринька", - "description": "Показувати об'єднані папки (Вхідні, Надіслані тощо) для всіх підключених облікових записів", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "Включати групові скриньки", - "description": "Також об'єднувати спільні/групові скриньки у спільному перегляді." - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "Кольорові значки бічної панелі", - "description": "Забарвлюйте значки папок і тегів за типом (синя «Вхідні», червоний «Спам», зелена «Надіслані» тощо). Вимкніть для монохромної бічної панелі." - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "Показувати загальну кількість повідомлень", - "description": "Показує загальну кількість повідомлень поруч із теками та мітками, разом із кількістю непрочитаних. Вимкніть, щоб показувати лише непрочитані." - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro-інтерфейс (експериментальний)", - "description": "Розкладка для досвідчених користувачів лише для настільного комп'ютера з переглядом повідомлень у кількох вкладках і робочими процесами між обліковими записами. Стандартний інтерфейс не змінюється; ви можете повернутися будь-коли.", - "open_label": "Відкрити Pro-інтерфейс", - "back_to_standard": "Назад до стандарту" - }, - "cross_unread": { - "label": "Усі облікові записи: Непрочитані", - "description": "Показує у розділі «Усі облікові записи» запис із непрочитаними листами з усіх облікових записів, по всіх теках, окрім спаму, надісланих, архіву, кошика та чернеток." - }, - "cross_starred": { - "label": "Усі облікові записи: Позначені", - "description": "Показує у розділі «Усі облікові записи» запис із позначеними листами з усіх облікових записів, по всіх теках, окрім спаму, надісланих, архіву, кошика та чернеток." - }, - "cross_all": { - "label": "Усі облікові записи: Уся пошта", - "description": "Показує у розділі «Усі облікові записи» запис з усіма листами з усіх облікових записів, по всіх теках, окрім спаму, надісланих, архіву, кошика та чернеток." - } - }, - "keywords": { - "title": "Ключові слова електронної пошти", - "description": "Визначте ключові слова (мітки/теги), щоб упорядкувати свої листи за кольорами. Вони зберігаються як ключові слова JMAP на сервері.", - "add_keyword": "Додати ключове слово", - "label_field": "Відображуване ім'я", - "label_placeholder": "напр. Робота, Особиста, Терміново", - "id_field": "ID ключового слова", - "id_placeholder": "напр. робота, особисте", - "color_field": "колір", - "id_exists": "Цей ідентифікатор ключового слова вже існує", - "edit": "Редагувати ключове слово", - "delete": "Видалити ключове слово", - "save": "зберегти", - "add": "додати", - "cancel": "Скасувати", - "migrating": "Оновлення ключового слова в наявних електронних листах…", - "migration_error": "Не вдалося оновити ключове слово в існуючих електронних листах", - "nesting": { - "label": "Вкладені теги", - "description": "Вкладайте теги в інші теги та показуйте їх на бічній панелі у вигляді дерева." - }, - "parent_field": "Батьківський тег", - "no_parent": "Без батьківського тега", - "too_long": "Цей шлях тега задовгий (щонайбільше {max} символів)", - "has_children_locked": "У цей тег вкладено інші теги, тому його назву та батьківський тег заблоковано. Спочатку перемістіть або видаліть їх.", - "has_children_delete": "Спочатку видаліть теги, вкладені в цей", - "visibility_field": "Видимість на бічній панелі", - "visibility": { - "show": "Показувати", - "unread": "Показувати за непрочитаних", - "hide": "Приховувати" - }, - "unrecognized": { - "title": "Відновити теги із сервера", - "description": "Назви та кольори тегів зберігаються в цьому браузері, але самі теги перебувають у ваших листах як ключові слова JMAP. Проскануйте пошту на ключові слова, для яких тут немає тега, і додайте їх назад.", - "scan": "Знайти теги", - "rescan": "Сканувати ще раз", - "scanning": "Сканування… {scanned} з {total} листів", - "none": "Усі теги у ваших листах уже визначені тут.", - "messages": "{count, plural, one {# лист} few {# листи} other {# листів}}", - "add_all": "Додати всі ({count})", - "partial": "Проскановано лише {scanned} найновіших листів із {total}. Теги, використані лише в старіших листах, можуть бути відсутні.", - "error": "Не вдалося просканувати пошту на наявність тегів." - } - }, - "notifications": { - "test_sound": "Тестовий звук сповіщення", - "sounds": { - "default": "За замовчуванням (звуковий сигнал)", - "cheerful": "Веселий", - "involved": "Залучений", - "swift": "Швидкий жест", - "relax": "Розслабтеся" - }, - "sound_selection": { - "title": "Звук сповіщення", - "description": "Виберіть звук для сповіщень", - "choose": "Звук", - "choose_desc": "Виберіть сигнал сповіщення та натисніть значок динаміка, щоб переглянути його" - }, - "email": { - "title": "Сповіщення електронною поштою", - "description": "Налаштувати сповіщення для вхідних електронних листів", - "enabled": "Сповіщення електронною поштою", - "enabled_desc": "Показувати сповіщення про надходження нових електронних листів", - "sound": "Звук сповіщення", - "sound_desc": "Відтворення звукового сповіщення про надходження нових електронних листів", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "Сповіщення календаря", - "description": "Налаштувати сповіщення про події календаря", - "enabled": "Сповіщення про події", - "enabled_desc": "Показувати сповіщення про майбутні події календаря", - "sound": "Звук сповіщення", - "sound_desc": "Відтворення звукового сповіщення для нагадувань календаря", - "invitation_parsing": "Проаналізуйте запрошення електронною поштою", - "invitation_parsing_desc": "Виявляти запрошення календаря у вкладеннях електронної пошти та показувати дії календаря" - }, - "push": { - "confirm_disable_message": "Цей пристрій перестане отримувати сповіщення, коли сайт закритий.", - "confirm_disable_title": "Вимкнути фонові сповіщення?", - "description": "Отримуйте системні сповіщення про нові листи, коли цей сайт закритий. Доставляється через push-реле Bulwark; реле ніколи не бачить вміст листів.", - "disable": "Вимкнути", - "enable": "Увімкнути", - "dismiss_aria": "Закрити запит на сповіщення", - "ios_hint": "На iOS спочатку встановіть сайт на головний екран – Safari доставляє Web Push лише встановленим PWA.", - "reenable": "Перереєструвати", - "relay_desc": "Типово використовується розміщене реле Bulwark. Змінюйте лише, якщо хостите самостійно.", - "relay_label": "Push-реле", - "relay_locked": "Встановлено адміністратором", - "relay_locked_desc": "URL push-реле встановлено вашим адміністратором і його не можна змінити.", - "status_active": "Активно на цьому пристрої", - "status_busy": "Виконується…", - "status_inactive": "Не ввімкнено на цьому пристрої", - "status_unsupported": "Цей браузер не підтримує Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "Фонові сповіщення", - "devices_title": "Зареєстровані пристрої", - "devices_desc": "Усі пристрої, зареєстровані для отримання фонових сповіщень цього облікового запису. Після відкликання пристрій одразу перестає їх отримувати.", - "devices_loading": "Завантаження зареєстрованих пристроїв…", - "devices_empty": "Для фонових сповіщень не зареєстровано жодного пристрою.", - "devices_error": "Не вдалося завантажити зареєстровані пристрої.", - "devices_refresh": "Оновити", - "device_this": "Цей пристрій", - "device_other": "Інший пристрій", - "device_status_active": "Доставляє сповіщення", - "device_status_inactive": "Жодного разу не доставляв сповіщень", - "device_status_unknown": "Невідомий цьому ретранслятору", - "device_expires": "Спливає {date}", - "revoke": "Відкликати", - "confirm_revoke_title": "Відкликати цей пристрій?", - "confirm_revoke_message": "Він одразу перестане отримувати фонові сповіщення. Якщо знову увімкнути push на цьому пристрої, він зареєструється заново.", - "confirm_revoke_message_this": "Цей пристрій одразу перестане отримувати фонові сповіщення." - } - }, - "language_region": { - "title": "Мова, регіон і час", - "description": "Мова, формат дати, формат часу та інші регіональні налаштування", - "language": { - "label": "Мова", - "description": "Виберіть бажану мову", - "english": "англійська", - "french": "Français" - }, - "date_format": { - "label": "Формат дати", - "description": "Як відображаються дати у списку листів", - "smart": "Розумний (за регіоном)", - "relative": "Відносний (1 год тому, 2 дн тому)", - "full": "Завжди повна дата", - "preview_today": "Сьогодні:", - "preview_this_week": "Цього тижня:", - "preview_older": "Старіше:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "Формат часу", - "description": "Виберіть 12-годинний або 24-годинний формат годинника", - "12h": "12-годинний", - "24h": "24 години" - }, - "first_day": { - "label": "Перший день тижня", - "description": "Почніть тиждень у неділю чи понеділок", - "sunday": "неділя", - "monday": "понеділок" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "Очищати пошук під час перемикання тек", - "description": "Клацання теки («Вхідні», «Надіслані»…) очищає активний пошук і відкриває цю теку. Якщо вимкнено, пошук лишається й повторюється у вибраній теці." - }, - "request_read_receipt": { - "label": "Запитувати сповіщення про прочитання за замовчуванням", - "description": "Заздалегідь вмикати запит сповіщення про прочитання під час створення нового повідомлення." - }, - "read_receipt_response": { - "label": "Відповідати на запити сповіщення про прочитання", - "description": "Що робити, коли вхідне повідомлення запитує сповіщення про прочитання.", - "ask": "Запитувати щоразу", - "always": "Завжди надсилати", - "never": "Ніколи не надсилати" - }, - "title": "Поведінка електронної пошти", - "description": "Налаштувати спосіб обробки електронних листів", - "mark_read": { - "label": "Позначити як прочитане", - "description": "Коли позначати листи як прочитані під час відкриття", - "instant": "Миттєво", - "delay_3s": "Через 3 секунди", - "delay_5s": "Через 5 секунд", - "never": "Ніколи" - }, - "delete_action": { - "label": "Видалити дію", - "description": "Що відбувається, коли ви видаляєте електронний лист", - "trash": "Перемістити в кошик", - "trash_and_read": "Перемістити в кошик і позначити як прочитане", - "permanent": "Видалити назавжди", - "warning": "Електронні листи буде видалено назавжди, і їх неможливо буде відновити. Ця дія незворотна." - }, - "message_spacing": { - "label": "Відступи повідомлення", - "description": "Відступи навколо тексту повідомлення під час читання", - "auto": "Автоматично (рекомендовано)", - "always": "Завжди додавати відступи", - "edge": "Від краю до краю" - }, - "plain_text_font": { - "label": "Шрифт звичайного тексту", - "description": "Шрифт для листів у звичайному тексті", - "mono": "Моноширинний", - "sans": "Звичайний (як у застосунку)" - }, - "archive_mode": { - "label": "Архівувати в", - "description": "Як упорядкувати листи під час архівування", - "single": "Одна папка", - "year": "Папка на рік", - "month": "Папка на місяць", - "reorganize": "Реорганізувати існуючий архів", - "reorganize_success": "{count, plural, =0 {Немає листів для реорганізації} =1 {1 лист реорганізовано} other {# листів реорганізовано}}", - "reorganize_error": "Не вдалося реорганізувати архів" - }, - "permanently_delete_junk": { - "label": "Видалити непотріб назавжди", - "description": "Назавжди видаляйте електронні листи з папки «Сміття» замість того, щоб переміщувати їх у кошик" - }, - "mail_layout": { - "label": "Макет пошти", - "description": "Вибирайте між класичною розділеною панеллю читання та зосередженим потоком читання у стилі Gmail.", - "split": "Розділена панель", - "split_description": "Тримайте список повідомлень і панель читання видимими поруч.", - "focus": "Сфокусований список", - "focus_description": "Показувати один рядок на повідомлення та відкривати пошту на всю ширину, зберігаючи бічну панель папки видимою.", - "horizontal": "Область читання знизу", - "horizontal_description": "Показуйте список повідомлень угорі та відкривайте вибране повідомлення в області читання під ним." - }, - "show_preview": { - "label": "Показати попередній перегляд тексту", - "description": "Відобразити попередній перегляд електронної пошти в списку", - "focus_description": "Відображати вбудований текст попереднього перегляду всередині однорядкового списку повідомлень у фокусі" - }, - "disable_threading": { - "label": "Вимкнути групування розмов", - "description": "Показувати електронні листи як окремі повідомлення, а не згруповані за розмовами" - }, - "plain_text_mode": { - "label": "Тільки звичайний текст", - "description": "Вимкніть редактор форматованого тексту та надсилайте всі електронні листи лише як звичайний текст, включаючи відповіді та пересилання" - }, - "auto_select_reply_identity": { - "label": "Автоматичний вибір адреси для відповіді", - "description": "Під час відповіді автоматично змінюйте адресу відправника на особу, яка спочатку отримала повідомлення" - }, - "reply_identity_match": { - "label": "Зіставлення адреси отримання", - "description": "Які адреси отримання вважати вашими. «Лише точна адреса» вибирає виключно одну з налаштованих ідентичностей; «той самий домен» також вважає будь-яку іншу адресу на доменах ваших ідентичностей catch-all-псевдонімом і підставляє її в заголовок «Від». Виберіть точну адресу, якщо такі адреси є списками розсилки.", - "exact": "Лише точна адреса", - "domain": "Будь-яка адреса на моїх доменах" - }, - "signature_position": { - "label": "Розташування підпису", - "description": "Куди вставляти підпис у відповідях і пересиланнях. Над цитованим текстом читається природно як завершення відповіді; під ним зберігає цілісність оригінального повідомлення.", - "above_quote": "Перед цитованим текстом", - "below_quote": "Після цитованого тексту" - }, - "signature_separator": { - "label": "Розділювач підпису", - "description": "Додавати перед підписом стандартний рядок-розділювач \"-- \" (RFC 3676). Вимкніть, якщо хочете переходити з повідомлення відразу до підпису." - }, - "sub_address_delimiter": { - "label": "Розділювач під-адреси", - "description": "Символ, який відокремлює ім'я користувача від мітки під-адреси. Використовуйте розділювач, налаштований на вашому поштовому сервері (напр. user{delimiter}tag@domain.com).", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "Власний…", - "custom_input_label": "Власний символ-розділювач" - }, - "attachment_click_action": { - "label": "Вкладення Натисніть Дія", - "description": "Виберіть, чи клацання вкладеного файлу попередньо переглядає його чи негайно завантажує", - "preview": "Попередній перегляд, коли це можливо", - "download": "Завантажте негайно" - }, - "attachment_position": { - "label": "Позиція прикріплення", - "description": "Де відображати вкладення в заголовку електронної пошти", - "beside-sender": "Поруч із відправником", - "below-header": "Під заголовком" - }, - "emails_per_page": { - "10": "10 електронних листів", - "25": "25 електронних листів", - "50": "50 електронних листів", - "100": "100 електронних листів", - "label": "Кількість електронних листів на сторінку", - "description": "Кількість електронних листів для одночасного завантаження" - }, - "always_light_mode": { - "label": "Завжди показувати електронні листи в світлому режимі", - "description": "Візуалізуйте вміст електронної пошти в світлому режимі, навіть якщо програма працює в темному режимі, уникаючи проблем з перетворенням у темний режим" - }, - "external_content": { - "label": "Зовнішній вміст", - "description": "Як працювати із зображеннями та зовнішнім вмістом", - "ask": "Завжди запитуйте", - "block": "Завжди блокувати", - "allow": "Завжди дозволяти" - }, - "trusted_senders": { - "label": "Довірені відправники", - "description": "Керуйте відправниками, зображення яких завантажуються автоматично", - "count_zero": "Жодного", - "count_one": "1 відправник", - "count_other": "{count} відправників", - "modal_title": "Довірені відправники", - "empty_title": "Ще немає надійних відправників", - "empty_description": "Переглядаючи електронний лист із заблокованими зображеннями, натисніть «Завжди довіряти цьому відправнику», щоб додати їх сюди.", - "add_manually": "Додайте відправника вручну", - "add_button": "додати", - "add_placeholder": "Введіть електронну адресу", - "search_placeholder": "Пошук відправників...", - "no_results": "Немає відправників, які відповідають вашому запиту", - "remove": "видалити", - "close": "Закрити", - "invalid_email": "Введіть дійсну електронну адресу", - "already_added": "Цей відправник уже є надійним", - "save_error": "Не вдалося зберегти - подробиці перевірте в журналі налагодження Контактів", - "use_address_book_label": "Синхронізація з адресною книгою", - "use_address_book_description": "Зберігайте довірених відправників у спеціальній адресній книзі «Надійні відправники», щоб вони синхронізувалися на всіх ваших пристроях" - }, - "hover_actions": { - "label": "Дії швидкого наведення", - "description": "Виберіть, які швидкі дії відображатимуться під час наведення курсора на електронний лист у списку", - "delete": "Видалити", - "star": "Зірочка / Зняти зірочку", - "mark_read": "Позначте прочитане/непрочитане", - "archive": "Архів", - "tag": "Тег", - "spam": "Позначити як спам", - "not_spam": "Не спам", - "none_selected": "Дії не вибрано", - "mode_label": "Режим відображення", - "mode_inline": "Вбудований", - "mode_floating": "Плаваючий", - "corner_label": "Плаваюча позиція", - "corner_top-left": "Зліва вгорі", - "corner_top-right": "Верхній правий", - "corner_bottom-left": "Внизу зліва", - "corner_bottom-right": "Внизу праворуч" - }, - "default_mail_program": { - "label": "Поштова програма за замовчуванням", - "description": "Зареєструйте {appName} як поштову програму за умовчанням для посилань mailto:", - "button": "Встановити за замовчуванням", - "success": "Браузеру запропоновано встановити за умовчанням", - "error": "Ваш браузер не підтримує цю функцію" - }, - "attachment_reminder": { - "label": "Нагадування про вкладення", - "description": "Попереджати перед надсиланням, якщо у вашому повідомленні згадуються вкладення, але вони не вкладені", - "keywords_label": "Тригерні ключові слова", - "keywords_description": "Слова або фрази, які ініціюють нагадування, коли вони знайдені у вашому повідомленні", - "add_placeholder": "Додати ключове слово...", - "add": "додати", - "remove": "видалити" - }, - "empty_subject_warning": { - "label": "Попередження про порожню тему", - "description": "Запитувати підтвердження перед надсиланням листа без теми." - }, - "hide_inline_image_attachments": { - "label": "Приховувати вбудовані зображення з вкладень", - "description": "Зображення, вбудовані в тіло повідомлення, не відображаються як окремі вкладення" - }, - "attachment_image_previews": { - "label": "Показувати попередній перегляд зображень у вкладеннях", - "description": "Відображати вкладені зображення як ескізи замість загальних піктограм файлів" - }, - "send_delay": { - "label": "Скасувати надсилання / затримка надсилання", - "description": "Затримує звичайне надсилання на короткий серверний інтервал.", - "off": "Вимкнено", - "seconds": "{seconds} с", - "unsupported": "Поточний обліковий запис не повідомляє про підтримку відкладеного надсилання. Налаштування залишиться збереженим для інших облікових записів." - }, - "return_to_list_after_action": { - "label": "Повернення до списку після видалення або позначення як непрочитане", - "description": "Після видалення або позначення відкритого листа як непрочитаного повертатися до списку листів замість відкриття наступного." - }, - "swipe_right_action": { - "label": "Дія при свайпі вправо (мобільні)", - "description": "На сенсорних пристроях — дія, що виконується при свайпі повідомлення вправо у списку." - }, - "swipe_left_action": { - "label": "Дія при свайпі вліво (мобільні)", - "description": "На сенсорних пристроях — дія, що виконується при свайпі повідомлення вліво у списку." - }, - "swipe_actions": { - "none": "Немає", - "archive": "Архівувати", - "delete": "Видалити", - "mark_read": "Перемкнути прочитане", - "star": "Перемкнути зірку", - "spam": "Позначити як спам" - }, - "rtl_editing": { - "label": "Підтримка редагування справа наліво", - "description": "Додає кнопку напрямку тексту на панель інструментів редактора, що дозволяє задавати абзацам напрямок зліва направо або справа наліво" - } - }, - "composer": { - "title": "Композитор", - "description": "Налаштування параметрів композиції електронної пошти", - "autosave": { - "label": "Інтервал автозбереження", - "description": "Як часто автоматично зберігати чернетки", - "30s": "Кожні 30 секунд", - "1m": "Кожну хвилину", - "2m": "Кожні 2 хвилини", - "5m": "Кожні 5 хвилин" - }, - "send_confirmation": { - "label": "Надіслати підтвердження", - "description": "Попросіть підтвердження перед надсиланням електронних листів" - }, - "default_reply": { - "label": "Типовий режим відповіді", - "description": "Дія за замовчуванням під час натискання відповіді", - "reply": "Відповісти", - "reply_all": "Відповісти всім" - } - }, - "privacy": { - "title": "Конфіденційність і безпека", - "description": "Керуйте налаштуваннями конфіденційності та безпеки", - "external_images": { - "label": "Блокувати зовнішні зображення", - "description": "Запобігання відстеження через зовнішні зображення" - }, - "session_timeout": { - "label": "Час очікування сеансу", - "description": "Автоматично виходити з системи після бездіяльності", - "never": "Ніколи", - "30m": "30 хвилин", - "1h": "1 година", - "4h": "4 години" - }, - "clear_cache": { - "label": "Очистити кеш", - "description": "Видаліть кешовані дані та тимчасові файли", - "button": "Очистити кеш", - "confirm": "Ви впевнені, що бажаєте очистити кеш?", - "success": "Кеш успішно очищено" - } - }, - "account": { - "title": "Обліковий запис", - "description": "Перегляньте інформацію свого облікового запису", - "name_label": "Відображуване ім'я", - "username_label": "Ім'я користувача", - "account_type_label": "Тип облікового запису", - "auth_method_label": "Аутентифікація", - "auth_method_oauth": "Єдиний вхід (OAuth/OIDC)", - "auth_method_basic": "Пароль", - "demo_account": "Демо-рахунок", - "email": { - "label": "Адреса електронної пошти", - "value": "{email}" - }, - "server": { - "label": "Сервер JMAP", - "value": "{server}" - }, - "storage": { - "label": "Використання сховища", - "used": "Використано {used} з {total}", - "percentage": "Використано {percent}%." - }, - "last_sync": { - "label": "Остання синхронізація", - "value": "{time}" - }, - "accounts": { - "title": "Підключені облікові записи", - "description": "Перетягніть, щоб змінити порядок відображення облікових записів у меню", - "active": "Поточний активний обліковий запис", - "default_badge": "Обліковий запис за замовчуванням", - "set_default": "Зробити основним", - "switch_to": "Перемкнутися на цей обліковий запис", - "move_up": "Перемістити вгору", - "move_down": "Перемістити вниз", - "drag_handle": "Перетягніть, щоб змінити порядок", - "add": "Додати обліковий запис" - }, - "shared_accounts": { - "title": "Доступні мені", - "description": "Групові та спільні облікові записи, якими ви можете керувати. Виберіть один, щоб змінити його фільтри, автовідповідач, календарі та контакти.", - "shared_label": "Спільний обліковий запис" - } - }, - "security": { - "title": "Безпека облікового запису", - "description": "Керуйте своїм паролем, двофакторною автентифікацією та налаштуваннями безпеки", - "detecting": "Виявлення можливостей сервера...", - "not_available": "Керування безпекою облікового запису недоступне для цього поштового сервера. Необхідні дозволи можуть бути вимкнені. Перегляньте документацію для отримання додаткової інформації.", - "password": { - "title": "Змінити пароль", - "current": "Поточний пароль", - "new": "Новий пароль", - "confirm": "Підтвердьте новий пароль", - "submit": "Змінити пароль", - "success": "Пароль успішно змінено", - "error_title": "Не вдалося змінити пароль", - "error_mismatch": "Нові паролі не збігаються", - "error_min_length": "Пароль має бути не менше 8 символів", - "error_generic": "Не вдалося змінити пароль" - }, - "display_name": { - "label": "Відображуване ім'я", - "description": "Ваше ім'я, як воно відображається на сервері", - "placeholder": "Введіть своє відображуване ім'я", - "save": "зберегти", - "success": "Відображуване ім'я оновлено", - "error": "Не вдалося оновити відображуване ім’я" - }, - "totp": { - "section_title": "Двофакторна автентифікація", - "label": "Автентифікація TOTP", - "description": "Додайте додатковий рівень безпеки за допомогою одноразового пароля на основі часу", - "active": "Увімкнено", - "inactive": "Вимкнено", - "enabled": "Увімкнено двофакторну аутентифікацію", - "disabled": "Двофакторну автентифікацію вимкнено", - "enable_error": "Не вдалося ввімкнути 2FA", - "disable_error": "Не вдалося вимкнути 2FA", - "setup_instructions": "Скопіюйте цю URL-адресу в програму автентифікації (Google Authenticator, Authy тощо):", - "verification_code": "Код підтвердження", - "confirm": "Підтвердити", - "disable": "Вимкнути", - "disable_confirm_prompt": "Введіть пароль, щоб вимкнути двофакторну автентифікацію.", - "password_required": "Потрібен пароль", - "code_required": "Потрібен код підтвердження", - "code_invalid": "Недійсний код підтвердження. Перевірте додаток автентифікації та спробуйте ще раз." - }, - "app_passwords": { - "title": "Паролі програм", - "description": "Створюйте паролі для програм, які не підтримують двофакторну автентифікацію", - "add": "додати", - "create": "Створити", - "cancel": "Скасувати", - "generate": "Генерувати", - "name_label": "Назва програми", - "name_placeholder": "напр. Thunderbird, iPhone Mail", - "password_label": "Пароль (залиште порожнім для автоматичного створення)", - "password_placeholder": "Автоматично генерується, якщо порожній", - "added": "Пароль програми створено", - "removed": "Пароль програми видалено", - "add_error": "Не вдалося створити пароль програми", - "remove_error": "Не вдалося видалити пароль програми", - "none": "Паролі програм не налаштовано", - "done": "Готово", - "expires_label": "Термін дії (необов’язково)", - "copy_now_warning": "Скопіюйте цей пароль зараз - він більше не буде показаний.", - "allowed_ips_label": "Дозволені IP (необов’язково)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "Через кому або пробіл. Залиште порожнім, щоб дозволити будь-який IP." - }, - "api_keys": { - "title": "API-ключі", - "description": "Створюйте API-ключі для скриптів та інтеграцій, які звертаються до сервера напряму", - "name_label": "Назва ключа", - "name_placeholder": "напр. Скрипт резервної копії, CI runner", - "copy_now_warning": "Скопіюйте цей API-ключ зараз – він більше не буде показаний.", - "added": "API-ключ створено", - "removed": "API-ключ видалено", - "add_error": "Не вдалося створити API-ключ", - "remove_error": "Не вдалося видалити API-ключ", - "none": "API-ключі не налаштовано" - }, - "encryption": { - "section_title": "Шифрування в спокої", - "label": "Шифрування електронної пошти", - "description": "Шифруйте збережені електронні листи на сервері для додаткової конфіденційності", - "active": "{type} шифрування ввімкнено", - "inactive": "Вимкнено", - "enabled": "Шифрування в стані спокою ввімкнено", - "disabled_success": "Шифрування в стані спокою вимкнено", - "error": "Не вдалося оновити налаштування шифрування", - "algorithm_label": "Алгоритм шифрування", - "encrypt_on_append": "Шифрувати нові листи під час завантаження", - "allow_spam_training": "Дозволити навчання спам-фільтра перед шифруванням листів", - "enable_encryption": "Увімкнути шифрування", - "enable_tooltip": "Увімкнути шифрування цим ключем", - "configure_title": "Увімкнути шифрування цим ключем", - "enable_button": "Увімкнути", - "disable_tooltip": "Вимкнути шифрування", - "enabled_success": "Шифрування в стані спокою ввімкнено" - }, - "email_client": { - "title": "Налаштування поштового клієнта", - "description": "Використовуйте ці облікові дані, щоб налаштувати клієнт електронної пошти для настільного або мобільного пристрою (Thunderbird, Apple Mail, Outlook тощо)", - "jmap_username_label": "Ім'я користувача JMAP", - "copy": "Копія", - "copied": "Скопійовано", - "password_instructions": "Використовуйте своє ім’я користувача JMAP вище разом із паролем програми, щоб увійти у свій поштовий клієнт. Створіть пароль програми в розділі вище, якщо ви ще цього не зробили." - }, - "link_device": { - "title": "Прив'язати мобільний застосунок", - "description": "Увійдіть у мобільний застосунок Bulwark Mail без введення даних. Створіть тут QR-код і відскануйте його на екрані входу в застосунку.", - "generate": "Показати QR-код", - "regenerate": "Показати новий код", - "instructions": "Відкрийте застосунок Bulwark Mail, натисніть \"Сканувати QR-код\" на екрані входу та наведіть камеру сюди.", - "expires_in": "Термін дії коду закінчиться через {seconds} секунд. Його можна використати лише один раз.", - "expired": "Термін дії цього коду закінчився.", - "generating": "Створення…", - "error": "Не вдалося створити код прив'язки. Будь ласка, спробуйте ще раз." - }, - "public_keys": { - "name_label": "Назва відкритого ключа", - "name_placeholder": "напр. Мій ключ", - "key_label": "Відкритий ключ (S/MIME або PGP у форматі ASCII)", - "key_placeholder": "Вставте сюди свій відкритий ключ", - "none": "Відкриті ключі не налаштовано" - } - }, - "identities": { - "title": "Надсилання ідентифікаторів", - "description": "Керуйте електронними адресами, з яких можна надсилати", - "identities_count": { - "label": "Ваші особи", - "description": "Адреси електронної пошти, налаштовані для надсилання", - "count_zero": "Жодної ідентифікації", - "count_one": "1 ідентичність", - "count_other": "{count} особи" - }, - "manage": "Керування ідентифікаторами", - "sub_addressing": { - "label": "Підадресація", - "description": "Використовуйте такі теги, як user+tag@domain.com, щоб упорядкувати вхідну пошту", - "learn_more": "Дізнайтеся більше" - } - }, - "vacation": { - "title": "Відповідач у відпустці", - "description": "Автоматично відповідайте на вхідні листи, поки вас немає", - "loading": "Завантаження налаштувань відпустки...", - "not_supported": "Ваш поштовий сервер не підтримує відповіді про відпустку.", - "fetch_error": "Не вдалося завантажити налаштування відпустки. Спробуйте ще раз.", - "status": { - "label": "Відповідач у відпустці", - "description": "Надсилайте автоматичну відповідь людям, які надсилають вам електронні листи", - "active": "Активний", - "inactive": "Неактивний" - }, - "date_range": { - "title": "Діапазон дат", - "description": "За бажанням можна обмежити автоматичну відповідь певним періодом", - "start": "Дата початку", - "start_description": "Залиште пустим, щоб не було обмеження на початок", - "end": "Дата закінчення", - "end_description": "Залиште пустим, щоб не було кінцевого обмеження" - }, - "message": { - "title": "Повідомлення автоматичної відповіді", - "description": "Повідомлення, яке буде надіслано у відповідь", - "subject_label": "Тема", - "subject_description": "Тема автоматичної відповіді", - "subject_placeholder": "Поза офісом", - "body_label": "Тіло повідомлення", - "body_description": "Вміст звичайного текстового повідомлення", - "body_placeholder": "Дякуємо за ваш електронний лист. Мене зараз немає в офісі, я відповім, коли повернуся.", - "html_label": "Форматоване повідомлення (HTML)", - "html_description": "Додайте форматовану версію з посиланнями та оформленням. Одержувачі, чий поштовий клієнт не може її показати, побачать звичайний текст вище.", - "html_placeholder": "Напишіть форматовану відповідь про відсутність…" - }, - "preview": { - "title": "Попередній перегляд", - "show": "Показати попередній перегляд", - "hide": "Приховати попередній перегляд" - }, - "save": "Зберегти зміни", - "saving": "Збереження...", - "warnings": { - "end_before_start": "Дата завершення має бути після дати початку", - "start_in_past": "Дата початку в минулому", - "empty_body": "Тіло повідомлення порожнє - одержувачі отримають порожню відповідь", - "subject_too_long": "Тема задовга для сервера (не більше {max} байтів).", - "body_too_long": "Повідомлення задовге для сервера (не більше {max} байтів разом із форматуванням)." - } - }, - "folders": { - "role_memos": "Нотатки", - "title": "Папки", - "description": "Керуйте своїми папками електронної пошти та призначайте стандартні ролі", - "folder_list": "Ваші папки", - "folder_list_description": "Натисніть піктограму папки, щоб налаштувати її", - "standard_roles": "Стандартні ролі папок", - "standard_roles_description": "Призначте, які папки використовуються для стандартних ролей поштової скриньки, як-от «Вхідні», «Надіслані», «Кошик» тощо.", - "role_inbox": "Вхідні", - "role_drafts": "Чернетки", - "role_sent": "Надіслано", - "role_trash": "сміття", - "role_junk": "Спам/сміття", - "role_archive": "Архів", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "Жодного", - "create_folder": "Створити папку", - "create_subfolder": "Створити вкладену папку", - "reorder": "Перетягніть, щоб змінити порядок", - "reorder_error": "Не вдалося змінити порядок папок", - "move_error": "Не вдалося перемістити папку", - "subfolder_of": "Всередині {name}", - "subfolder_name": "Назва вкладеної папки", - "new_folder_name": "Назва папки", - "rename": "Перейменувати", - "change_icon": "Змінити значок", - "delete": "Видалити", - "confirm_delete": "Ви впевнені, що хочете видалити \"{name}\"? Електронні листи в цій папці буде переміщено в кошик.", - "create": "Створити", - "cancel": "Скасувати", - "no_folders": "Немає спеціальних папок", - "cannot_delete_role": "Неможливо видалити папку зі стандартною роллю. Спочатку зніміть роль.", - "folder_created": "Папка створена", - "folder_renamed": "Папку перейменовано", - "folder_deleted": "Папку видалено", - "role_updated": "Роль папки оновлено", - "error_create": "Не вдалося створити папку", - "error_rename": "Не вдалося перейменувати папку", - "error_delete": "Не вдалося видалити папку", - "error_delete_has_children": "Неможливо видалити папку: вона все ще містить вкладені папки. Спочатку видаліть або перемістіть їх.", - "error_delete_has_email": "Неможливо видалити папку: вона все ще містить електронні листи. Спочатку перемістіть або видаліть їх.", - "error_role": "Не вдалося оновити роль папки" - }, - "advanced": { - "title": "Просунутий", - "description": "Розширені параметри та налаштування розробника", - "debug_mode": { - "label": "Режим налагодження", - "description": "Увімкніть детальне ведення журналу для усунення несправностей" - }, - "debug_categories": { - "description": "Виберіть категорії для реєстрації. Вимкніть категорії, які вам не потрібні, щоб зменшити шум консолі.", - "jmap": "Клієнт JMAP", - "jmap_description": "Операції з поштовою скринькою, отримання електронної пошти та запити протоколу JMAP", - "calendar": "Календар", - "calendar_description": "Події календаря, імпорт і планування повідомлень", - "tasks": "завдання", - "tasks_description": "Створення, отримання та оновлення завдань календаря", - "auth": "Аутентифікація", - "auth_description": "Логін, TOTP, обмін токеном і керування сесіями", - "filters": "Фільтри", - "filters_description": "Правила фільтрації сита та сценарії відпустки", - "email": "Перегляд електронної пошти", - "email_description": "Візуалізація електронної пошти, обробка TNEF і позначення як прочитане", - "push": "Push-сповіщення", - "push_description": "Налаштування та доставка push-повідомлень", - "contacts": "Контакти та адресні книги", - "contacts_description": "Синхронізація контактів, операції з адресною книгою та довірені відправники" - }, - "settings_sync": { - "label": "Синхронізація налаштувань", - "description": "Синхронізуйте налаштування між браузерами та пристроями" - }, - "sender_favicons": { - "label": "Favicons відправника", - "description": "Показувати піктограми веб-сайтів як зображення профілю для бізнес-відправників" - }, - "show_avatars_in_junk": { - "label": "Показувати аватари в папці «Спам»", - "description": "Показувати зображення профілю та фавікони відправників у папці «Спам». Типово вимкнено, щоб фішингові листи не виглядали достовірно." - }, - "keyboard_shortcuts": { - "label": "Комбінації клавіш", - "description": "Переглянути доступні комбінації клавіш", - "button": "Перегляд ярликів" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "Скинути налаштування", - "description": "Відновити всі налаштування до значень за замовчуванням", - "button": "Скинути до значень за замовчуванням" - }, - "export_settings": { - "label": "Налаштування експорту", - "description": "Завантажте налаштування як JSON", - "button": "Експорт" - }, - "import_settings": { - "label": "Налаштування імпорту", - "description": "Завантажте налаштування з файлу JSON", - "button": "Імпорт" - }, - "about": { - "title": "Оплот веб-пошти" - } - }, - "sidebar_apps": { - "title": "Програми бічної панелі", - "description": "Керуйте спеціальними програмами та посиланнями на бічній панелі", - "keep_loaded": "Тримайте програми завантаженими", - "keep_loaded_description": "Під час перемикання між вбудованими програмами продовжуйте працювати у фоновому режимі, щоб уникнути перезавантаження", - "managed_title": "Надано вашим адміністратором", - "managed_description": "Ці застосунки налаштовано для всіх, і їх не можна змінити.", - "manage_title": "Спеціальні програми", - "manage_description": "Додавайте, редагуйте або видаляйте власні програми з бічної панелі" - }, - "contacts": { - "title": "Контакти", - "description": "Імпортуйте та експортуйте свої контакти", - "import_label": "Імпортувати контакти", - "import_description": "Імпортуйте контакти з файлу vCard (.vcf).", - "export_label": "Експортувати контакти", - "export_description": "Експортуйте всі контакти як файл vCard (.vcf).", - "manage_title": "Адресні книги", - "manage_description": "Перейменуйте свої адресні книги", - "no_address_books": "Адресних книг не знайдено", - "categories_title": "Категорії", - "categories_description": "Перейменувати категорії контактів", - "no_categories": "Категорії не знайдено", - "group_by_letter_description": "Показувати алфавітні заголовки розділів у списку контактів", - "group_by_letter_label": "Групувати за першою літерою", - "sort_by_last_name_label": "Сортувати за прізвищем", - "sort_by_last_name_description": "Упорядкувати список контактів за прізвищем, щоб члени родини були поруч" - }, - "downloads": { - "title": "Завантаження", - "description": "Налаштуйте, як називаються завантажені листи та вкладення.", - "reset": "Відновити стандартне", - "preview": "Попередній перегляд:", - "email_template": { - "label": "Ім'я файлу листа (.eml)", - "description": "Шаблон, що використовується при експорті листа або перетягуванні його у файлову систему. Розширення .eml додається автоматично." - }, - "attachment_template": { - "label": "Ім'я файлу вкладення", - "description": "Шаблон, що використовується при завантаженні або перетягуванні вкладення. Якщо пропустити '{filename}' та '{ext}', оригінальне розширення зберігається." - }, - "bundle_template": { - "label": "Ім'я файлу .zip з кількома листами", - "description": "Шаблон, що використовується при перетягуванні або завантаженні кількох вибраних листів одним .zip-архівом. Розширення .zip додається автоматично." - }, - "spaces": { - "label": "Пробіли", - "description": "Замінити пробіли в підсумковому імені файлу іншим символом.", - "keep": "Залишити пробіли", - "underscore": "Замінити на _", - "dash": "Замінити на -" - }, - "lowercase": { - "label": "Нижній регістр", - "description": "Примусово перетворити все ім'я файлу на нижній регістр." - }, - "strip_diacritics": { - "label": "Видалити діакритику", - "description": "Перетворити літери з акцентами на ASCII-еквіваленти (ä → a, é → e). Корисно для інструментів, що погано обробляють Unicode." - }, - "collapse_separators": { - "label": "Об'єднати повторювані роздільники", - "description": "Об'єднати послідовності пробілів, підкреслень або дефісів в один символ." - }, - "after_export": { - "label": "Після експорту", - "description": "За потреби перемістити лист після експорту як .eml.", - "keep": "Залишити у скриньці", - "archive": "Перемістити в архів", - "trash": "Перемістити у кошик" - } - }, - "filters": { - "title": "Фільтри електронної пошти", - "description": "Створіть правила для автоматичного сортування, позначення та керування вхідними електронними листами", - "add_rule": "Додати правило", - "no_rules": "Без правил фільтрації", - "no_rules_description": "Створіть правила для автоматичного впорядкування вхідних електронних листів", - "vacation_active": "Автовідповідач активний", - "vacation_active_description": "Для вхідних повідомлень увімкнено автовідповідь", - "vacation_configure": "Налаштувати", - "edit_rule": "Редагувати правило", - "new_rule": "Нове правило", - "delete_rule": "Видалити правило", - "delete_confirm": "Ви впевнені, що хочете видалити це правило?", - "enable": "Увімкнути", - "disable": "Вимкнути", - "raw_editor": "Редактор Raw Sieve", - "raw_editor_warning": "Редагування необробленого сценарію Sieve може порушити редагування візуальних правил. Зміни, зроблені тут, замінюють візуальний конструктор.", - "validate": "Перевірити", - "validation_success": "Сценарій дійсний", - "validation_error": "Скрипт містить помилки", - "save": "Зберегти правила", - "saving": "Збереження...", - "saved": "Фільтри успішно збережено", - "save_failed": "Не вдалося зберегти фільтри", - "loading": "Завантаження фільтрів...", - "not_supported": "Ваш поштовий сервер не підтримує фільтри електронної пошти.", - "rule_name": "Назва правила", - "rule_name_placeholder": "наприклад, Сортувати інформаційні бюлетені", - "match_all": "Відповідають УСІМ умовам", - "match_any": "Відповідає БУДЬ-ЯКІЙ умові", - "conditions": "Умови", - "add_condition": "Додати умову", - "actions": "Дії", - "add_action": "Додати дію", - "stop_processing": "Зупинити обробку наступних правил", - "include_spam": "Переміщувати також листи, позначені як спам", - "attachment_type_placeholder": "напр. pdf, doc, jpg", - "value_placeholder_multi": "Значення (декілька через кому)", - "condition_fields": { - "attachment": "Вкладення", - "from": "Від", - "to": "до", - "cc": "Cc", - "subject": "Тема", - "header": "Спеціальний заголовок", - "size": "Розмір", - "body": "Тіло" - }, - "comparators": { - "has_any": "присутнє", - "has_type": "типу", - "contains": "містить", - "not_contains": "не містить", - "is": "точно", - "not_is": "не є", - "starts_with": "починається з", - "ends_with": "закінчується на", - "matches": "відповідає шаблону", - "greater_than": "більше ніж", - "less_than": "менше ніж" - }, - "action_types": { - "move": "Перемістити в папку", - "copy": "Копіювати в папку", - "forward": "Переслати до", - "mark_read": "Позначити як прочитане", - "star": "Повідомлення зірочкою", - "add_label": "Додати мітку", - "discard": "Відкинути (видалити мовчки)", - "reject": "Відхилити повідомленням", - "keep": "Зберігати у вхідних", - "stop": "Зупинити обробку" - }, - "move_to_folder": "Виберіть папку", - "copy_to_folder": "Виберіть папку", - "forward_to": "Переслати на електронну адресу", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "Зберегти копію", - "forward_limit": "Ліміт пересилань на один лист на цьому сервері: {count}. Зайві пересилання пропускаються.", - "reject_message": "Повідомлення про відмову", - "reject_placeholder": "Ваш лист було відхилено", - "label_name": "Назва мітки", - "label_placeholder": "наприклад, важливий", - "header_name": "Назва заголовка", - "header_placeholder": "напр., X-Mailing-List", - "size_bytes": "Розмір у байтах", - "size_placeholder": "напр., 1000000", - "system_managed": "Правило, кероване системою", - "opaque_warning": "Цей сценарій було відредаговано поза візуальним конструктором. Доступне лише редагування Raw Sieve.", - "open_sieve_editor": "Відкрийте редактор Raw Sieve", - "fetch_error": "Не вдалося завантажити фільтри", - "expanded_view": "Розгорнутий вигляд", - "expanded_view_description": "Показати правила фільтрації з детальними умовами та блоками дій", - "if": "Якщо", - "then": "Потім", - "match_all_conditions": "всі збігаються", - "match_any_condition": "будь-які збіги", - "and": "і", - "or": "або", - "cancel": "Скасувати", - "confirm_delete": "Видалити", - "rule_list": "Правила фільтрації", - "drag_to_reorder": "Перетягніть, щоб змінити порядок", - "match_type": "Тип відповідності", - "reset_to_visual": "Скинути до Visual Builder", - "reset_warning": "Це призведе до скасування поточного сценарію та запуску заново.", - "confirm_reset": "Скинути", - "validation_empty_name": "Потрібно вказати назву правила", - "validation_empty_conditions": "Потрібна принаймні одна умова зі значенням", - "validation_empty_actions": "Необхідна принаймні одна дія", - "templates_section": "Почніть із шаблону", - "template_newsletters": "Перемістити інформаційні бюлетені в папку", - "template_receipts": "Автоматичне архівування квитанцій", - "template_important": "Позначайте важливі електронні листи", - "template_notifications": "Фільтр сповіщень", - "sieve_editor": { - "title": "Редактор сценаріїв Sieve", - "warning": "Редагування необробленого сценарію Sieve може порушити редагування візуальних правил. Зміни, зроблені тут, замінюють візуальний конструктор.", - "script_content": "Сито сценарій", - "valid": "Сценарій дійсний", - "invalid": "Скрипт містить помилки", - "save_warning": "Збереження перезапише будь-які візуальні правила. Це неможливо скасувати. Натисніть кнопку Зберегти ще раз для підтвердження.", - "validating": "Перевірка...", - "validate": "Перевірити", - "cancel": "Скасувати", - "save": "зберегти", - "confirm_save": "Підтвердити збереження", - "validation_failed": "Помилка запиту перевірки" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# умова} few {# умови} many {# умов} other {# умов}}", - "actions_count": "{count, plural, one {# дія} few {# дії} many {# дій} other {# дій}}" - }, - "origin_external": "Зовнішнє", - "managed_by_tooltip": "Керується {source}. Редагуйте в тому додатку або використовуйте редактор Sieve." - }, - "templates": { - "title": "Шаблони електронної пошти", - "description": "Створюйте багаторазові шаблони електронної пошти зі змінними-заповнювачами", - "add": "Новий шаблон", - "edit": "Редагувати шаблон", - "name": "Назва шаблону", - "name_placeholder": "наприклад, повторний електронний лист", - "category": "Категорія", - "category_placeholder": "наприклад, Робота, Особисте", - "subject": "Тема", - "subject_placeholder": "Тема листа", - "body": "Тіло", - "body_placeholder": "Основний вміст електронного листа...", - "recipients_placeholder": "email@example.com", - "identity": "Надіслати як", - "default_identity": "Ідентифікація за замовчуванням", - "favorite": "улюблений", - "cancel": "Скасувати", - "create": "Створити", - "update": "оновлення", - "confirm_delete": "Видалити", - "no_templates": "Шаблонів ще немає", - "manage": "Керування шаблонами", - "count": "{count, plural, one {# шаблон} few {# шаблони} many {# шаблонів} other {# шаблонів}}", - "export_import": "Експорт та імпорт", - "export_import_description": "Зробіть резервну копію своїх шаблонів або перенесіть їх на інший пристрій", - "export": "Експорт", - "import": "Імпорт", - "validation": { - "empty": "Потрібно вказати назву шаблону", - "too_long": "Назва шаблону має містити не більше 200 символів" - } - }, - "files": { - "display": { - "title": "Дисплей", - "description": "Налаштувати спосіб відображення файлів і папок" - }, - "folder_layout": { - "label": "Навігація по папках", - "description": "Виберіть спосіб відображення папок: у рядку з файлами чи в дереві бічної панелі", - "inline": "Вбудований", - "sidebar": "Бічна панель" - }, - "default_view": { - "label": "Перегляд за умовчанням", - "description": "Вибір між сіткою та списком", - "list": "Список", - "grid": "Сітка" - }, - "default_sort": { - "label": "Типове сортування", - "description": "Виберіть стандартне сортування для файлів", - "name": "Ім'я", - "size": "Розмір", - "modified": "Змінено" - }, - "sort_direction": { - "label": "Напрямок сортування", - "description": "Виберіть порядок зростання або спадання", - "ascending": "За зростанням", - "descending": "Спускається" - }, - "icons": { - "title": "Ікони", - "description": "Налаштувати зовнішній вигляд значка файлу" - }, - "show_icons": { - "label": "Показати значки файлів", - "description": "Відображення піктограм біля файлів і папок" - }, - "colored_icons": { - "label": "Кольорові значки", - "description": "Використовуйте кольорові значки замість монохромних" - }, - "show_thumbnails": { - "label": "Показати мініатюри", - "description": "Відображати попередній перегляд зображень замість піктограм для файлів зображень" - }, - "behavior": { - "title": "Поведінка", - "description": "Налаштувати поведінку файлового браузера" - }, - "show_hidden": { - "label": "Показати приховані файли", - "description": "Відображення файлів і папок, які починаються з крапки" - }, - "preview": { - "label": "Попередній перегляд" - } - }, - "search_placeholder": "Пошук у налаштуваннях", - "search_clear": "Очистити пошук", - "search_no_results": "Немає відповідних налаштувань", - "scoped": { - "back": "Назад до мого облікового запису", - "managing": "Керування: {name}" - } - }, - "errors": { - "page_error_title": "Щось пішло не так", - "page_error_description": "Ми зіткнулися з неочікуваною помилкою. Спробуйте ще раз або поверніться на головну сторінку.", - "sidebar_error": "Неможливо завантажити поштові скриньки", - "email_list_error": "Неможливо завантажити електронні листи", - "viewer_error_title": "Неможливо відобразити електронну пошту", - "viewer_error_description": "Під час відтворення цього електронного листа виникла проблема. Він може містити непідтримуваний вміст.", - "composer_error": "Не вдалося завантажити композитор", - "settings_error_title": "Налаштування недоступні", - "settings_error_description": "Не вдалося завантажити налаштування. Ваші налаштування можуть не зберегтися.", - "try_again": "Спробуйте знову", - "reload": "Перезавантажити", - "reload_emails": "Перезавантажте електронні листи", - "reload_settings": "Перезавантажити налаштування", - "retry": "Повторіть спробу", - "go_home": "Перейти до папки \"Вхідні\"." - }, - "context_menu": { - "reply": "Відповісти", - "reply_all": "Відповісти всім", - "forward": "вперед", - "mark_read": "Позначити як прочитане", - "mark_unread": "Позначити як непрочитане", - "star": "зірка", - "unstar": "Зняти зірочку", - "pin": "Закріпити", - "unpin": "Відкріпити", - "move_to": "Перейти до...", - "archive": "Архів", - "delete": "Видалити", - "mark_as_spam": "Повідомити про спам", - "not_spam": "Не спам", - "tag": "Мітка", - "items_selected": "Вибрано електронних листів: {count}", - "edit_draft": "Редагувати чернетку", - "cancel_scheduled_send": "Скасувати надсилання", - "reschedule_send": "Перенести", - "cancel_and_edit": "Скасувати й редагувати", - "cancel_and_compose_again": "Скасувати й написати знову" - }, - "mailbox_context_menu": { - "mark_folder_read": "Позначити папку як прочитану", - "mark_folder_tree_read": "Позначити папку та вкладені папки як прочитані", - "mark_all_folders_read": "Позначити всі папки як прочитані", - "new_subfolder": "Нова вкладена папка...", - "new_folder": "Нова папка...", - "rename": "Перейменувати...", - "share": "Поділитися...", - "import_email": "Імпортувати .eml або .zip...", - "empty_folder": "Очистити папку", - "empty_folder_generic": "Очистити папку", - "delete_folder": "Видалити папку", - "refresh": "Оновити", - "mark_all_confirm_title": "Позначити всі папки як прочитані", - "mark_all_confirm_message": "Позначити всі непрочитані повідомлення у вашому особистому акаунті як прочитані?", - "delete_confirm_title": "Видалити папку", - "delete_confirm_message": "Остаточно видалити папку \"{name}\"? Цю дію неможливо скасувати.", - "prompt_new_subfolder": "Введіть ім'я для нової вкладеної папки.", - "prompt_new_folder": "Введіть ім'я для нової папки.", - "prompt_rename": "Введіть нове ім'я для цієї папки.", - "toast_marked_read": "Папку позначено як прочитану", - "toast_marked_read_count": "Позначено {count, plural, one {1 повідомлення} few {# повідомлення} other {# повідомлень}} як прочитані", - "toast_already_read": "Немає непрочитаних повідомлень", - "toast_marked_all_read": "Всі папки позначені як прочитані", - "toast_emptied": "Папку очищено", - "toast_folder_created": "Папку створено", - "toast_folder_renamed": "Папку перейменовано", - "toast_folder_deleted": "Папку видалено", - "toast_error_mark_read": "Не вдалося позначити як прочитане", - "toast_error_empty": "Не вдалося очистити папку", - "toast_error_create": "Не вдалося створити папку", - "toast_error_rename": "Не вдалося перейменувати папку", - "toast_error_delete": "Не вдалося видалити папку", - "toast_error_delete_has_children": "Папка містить вкладені папки. Спочатку видаліть їх.", - "toast_error_delete_has_email": "Папка не порожня. Спочатку очистіть її.", - "delete_with_contents_title": "Тека не порожня", - "delete_with_contents_message": "Тека «{name}» досі містить {count, plural, one {# повідомлення} few {# повідомлення} other {# повідомлень}}. Видалити теку разом з усіма повідомленнями? Цю дію не можна скасувати.", - "delete_with_contents_confirm": "Видалити теку та повідомлення", - "placeholder_folder_name": "Ім'я папки", - "create": "Створити", - "rename_confirm": "Перейменувати" - }, - "shortcuts": { - "title": "Комбінації клавіш", - "tip": "Натисніть ? у будь-який час, щоб показати цю допомогу", - "sections": { - "navigation": "Навігація", - "actions": "Дії електронної пошти", - "global": "Глобальний", - "threads": "Нитки", - "composer": "Композитор" - }, - "navigation": { - "next_email": "Наступний електронний лист", - "previous_email": "Попередній електронний лист", - "open_email": "Відкрийте електронну пошту", - "close_email": "Закрити / Скасувати вибір" - }, - "actions": { - "reply": "Відповісти", - "reply_all": "Відповісти всім", - "forward": "вперед", - "star": "Перемкнути зірку", - "archive": "Архів", - "delete": "Видалити", - "mark_unread": "Позначити як непрочитане", - "mark_read": "Позначити як прочитане", - "toggle_spam": "Повідомити про спам / Не спам" - }, - "global": { - "compose": "Створіть новий електронний лист", - "search": "Пошук по фокусу", - "help": "Показати ярлики", - "refresh": "Оновити електронні листи", - "select_all": "Вибрати все", - "global_search": "Шукати скрізь" - }, - "threads": { - "expand_collapse": "Розгорнути/згорнути ланцюжок" - }, - "composer": { - "send": "Надіслати лист", - "schedule_send": "Запланувати надсилання", - "template_picker": "Відкрити засіб вибору шаблону" - } - }, - "threads": { - "messages_one": "{count} повідомлення", - "messages_other": "{count} повідомлень", - "messages_tooltip": "{count, plural, one {# повідомлення в цій розмові} few {# повідомлення в цій розмові} many {# повідомлень у цій розмові} other {# повідомлень у цій розмові}}", - "expand": "Розгорнути бесіду", - "collapse": "Згорнути бесіду", - "loading": "Завантаження бесіди...", - "mark_read": "Позначити бесіду як прочитану", - "mark_unread": "Позначити розмову як непрочитану", - "archive": "Архів бесіди", - "delete": "Видалити бесіду", - "star": "Зоряна розмова", - "unstar": "Зняти зірочку з розмови", - "toggle_thread": "Перемкнути потік" - }, - "identities": { - "modal_title": "Керування ідентифікаторами надсилання", - "create_new": "Створіть нову ідентичність", - "edit_identity": "Редагувати ідентифікатор", - "delete_confirm": "Видалити цю особу? Це неможливо скасувати.", - "cannot_delete": "Цю особу неможливо видалити", - "primary_identity": "Первинний", - "set_as_primary": "Встановити як основний", - "no_identities": "Особи не знайдено", - "display": { - "reply_to": "Відповідь:", - "bcc": "BCC:", - "signature": "Підпис:", - "preview": "Попередній перегляд:" - }, - "validation_errors": { - "invalid_emails": "Недійсні електронні адреси: {emails}", - "unknown_error": "Невідома помилка" - }, - "form": { - "name_label": "Відображуване ім'я", - "name_placeholder": "наприклад, робоча електронна пошта, особиста", - "name_required": "Необхідно вказати ім'я", - "email_label": "Адреса електронної пошти", - "email_placeholder": "your.email@example.com", - "email_required": "Необхідно вказати адресу електронної пошти", - "email_invalid": "Введіть дійсну електронну адресу", - "email_immutable": "Адресу електронної пошти не можна змінити після створення", - "reply_to_label": "Відповідь (необов'язково)", - "reply_to_placeholder": "different@example.com", - "bcc_label": "Auto BCC (опціонально)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "Текстовий підпис", - "html_signature_label": "Підпис HTML", - "signature_byte_counter": "{bytes} / {max} байтів", - "signature_byte_limit_reached": "Досягнуто обмеження сервера", - "save": "Зберегти ідентифікатор", - "cancel": "Скасувати", - "creating": "Створення...", - "updating": "Оновлення..." - }, - "sub_address": { - "button_tooltip": "Використовуйте допоміжну адресу", - "popover_title": "Додайте тег підадреси", - "tag_input_placeholder": "Введіть тег (наприклад, шопінг)", - "preview_label": "Попередній перегляд:", - "recent_tags": "Останні теги", - "suggested_tags": "Запропоновано", - "use_address": "Використовуйте цю адресу", - "invalid_tag": "Тег має бути лише буквено-цифровим і тире", - "tag_too_long": "Тег має містити 30 символів або менше", - "help_text": "Електронні листи, надіслані на user{delimiter}tag@domain.com, надходитимуть до вашої скриньки", - "validation": { - "empty": "Тег не може бути порожнім", - "too_long": "Тег має містити не більше {max} символів", - "invalid_chars": "Тег повинен містити лише літери, цифри та тире" - } - }, - "badge": { - "sent_via": "через", - "sub_address_tag": "Надіслано з використанням додаткової адреси: {tag}", - "identity_name": "Надіслано з ідентифікатором: {name}", - "identity_short": "через {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "Видалити", - "delete_confirm_title": "Видалити особу" - }, - "templates": { - "picker_title": "Виберіть шаблон", - "search_placeholder": "Пошук шаблонів...", - "section_favorites": "Вибране", - "section_recent": "Останні", - "section_uncategorized": "інше", - "no_templates": "Шаблонів ще немає", - "no_results": "Шаблони не знайдено", - "fill_placeholders": "Заповнити значення заповнювачів", - "enter_value": "Введіть значення...", - "preview": "Попередній перегляд", - "insert_with_values": "Вставити зі значеннями", - "insert_raw": "Вставити Raw", - "copy_suffix": "(копія)", - "placeholder": "змінна", - "placeholders": { - "recipient_name": "Ім'я одержувача", - "company": "Назва компанії", - "date": "Поточна дата", - "day_of_week": "День тижня", - "sender_name": "Ваше ім'я" - } - }, - "contacts": { - "title": "Контакти", - "search_placeholder": "Пошук контактів...", - "create_new": "Новий контакт", - "no_category": "Без категорії", - "rename_category": "Перейменувати категорію", - "category_name_label": "Назва категорії", - "category_renamed": "Категорію перейменовано", - "category_rename_failed": "Не вдалося перейменувати категорію", - "category_added": "Контакт додано до {name}", - "category_added_plural": "{count} контактів додано до {name}", - "empty_state": "Контактів ще немає", - "empty_state_title": "Контактів ще немає", - "empty_state_subtitle": "Створіть свій перший контакт або імпортуйте його з файлу vCard", - "empty_search": "Жоден контакт не відповідає вашому пошуку", - "empty_search_hint": "Спробуйте інший термін пошуку", - "empty_filtered": "Жоден контакт не відповідає фільтрам", - "empty_filtered_hint": "Змініть або очистіть фільтри", - "clear_search": "Очистити пошук", - "import_vcard": "Імпорт vCard", - "delete_confirm_title": "Видалити контакт", - "delete_confirm": "Ви впевнені, що хочете видалити цей контакт?", - "local_mode": "Контакти зберігаються локально (сервер не підтримує контакти JMAP)", - "back_to_contacts": "Назад до контактів", - "back_to_email": "Назад до листа", - "tabs": { - "all": "все", - "groups": "Групи" - }, - "shared": { - "title": "Спільний доступ" - }, - "address_books": { - "create": "Нова адресна книга", - "created": "Адресну книгу створено", - "create_failed": "Не вдалося створити адресну книгу", - "title": "Мої адресні книги", - "shared_prefix": "Спільно: {name}", - "moved": "Контакт переміщено до {name}", - "moved_plural": "{count} контактів переміщено до {name}", - "move_failed": "Не вдалося перемістити контакт", - "address_book": "Адресна книга", - "rename": "Перейменувати адресну книгу", - "name_label": "Назва адресної книги", - "renamed": "Адресну книгу перейменовано", - "rename_failed": "Не вдалося перейменувати адресну книгу", - "default": "За замовчуванням", - "set_default": "Зробити типовою", - "default_updated": "Типову адресну книгу оновлено", - "set_default_failed": "Не вдалося встановити типову адресну книгу", - "manage": "Керуйте адресними книгами", - "share": "Поділитися адресною книгою", - "new_contact_in_book": "Новий контакт у цій адресній книзі", - "delete": "Видалити адресну книгу", - "confirm_delete": "Видалити «{name}»? Усі контакти в цій адресній книзі будуть видалені.", - "deleted": "Адресну книгу видалено", - "delete_failed": "Не вдалося видалити адресну книгу" - }, - "detail": { - "emails": "Адреси електронної пошти", - "phones": "Номери телефонів", - "organizations": "організації", - "addresses": "Адреси", - "notes": "Примітки", - "titles": "Звання та ролі", - "online_services": "Онлайн-сервіси", - "anniversaries": "Ювілеї", - "personal_info": "Особиста інформація", - "languages": "Мови", - "categories": "Категорії", - "related_contacts": "Пов'язані контакти", - "crypto_keys": "Крипто ключі", - "cert_issuer": "Емітент", - "cert_expires": "Термін дії закінчується", - "cert_expired": "Термін дії минув", - "cert_fingerprint": "Відбиток пальця", - "cert_algorithm": "Алгоритм", - "import_to_smime": "Імпортувати в S/MIME", - "cert_already_imported": "Уже імпортовано в S/MIME", - "cert_imported": "Сертифікат імпортовано до магазину S/MIME", - "cert_import_failed": "Не вдалося імпортувати сертифікат", - "no_contact_selected": "Виберіть контакт, щоб переглянути деталі", - "compose_email": "Написати електронний лист", - "copy_email": "Копіювати електронну пошту", - "copy_phone": "Копіювати номер телефону", - "copy_url": "Копіювати URL", - "copied": "Скопійовано в буфер обміну", - "copy_failed": "Не вдалося скопіювати в буфер обміну", - "created": "Створено", - "updated": "Останнє оновлення", - "timezone": "Часовий пояс", - "anniversary_birth": "день народження", - "anniversary_death": "Померла", - "anniversary_wedding": "Ювілей", - "anniversary_other": "інше", - "personal_expertise": "Експертиза", - "personal_hobby": "хобі", - "personal_interest": "Інтерес", - "personal_other": "інше", - "gender": "Стать", - "gender_masculine": "Чоловік", - "gender_feminine": "Жінка", - "gender_other": "інше", - "gender_none": "Не застосовується", - "gender_unknown": "Невідомий", - "calendar": "Календар", - "calendar_uri": "URL-адреса календаря", - "scheduling_uri": "URL-адреса планування", - "freebusy_uri": "Вільний/зайнятий URL", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "Більше дій", - "age_years": "{count, plural, one {# рік} few {# роки} many {# років} other {# років}}", - "years_since": "{count, plural, one {# рік тому} few {# роки тому} many {# років тому} other {# років тому}}" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "Новий контакт", - "edit_title": "Редагувати контакт", - "section_address_book": "Довідник", - "select_address_book": "Виберіть каталог...", - "section_identity": "Ім'я та ідентифікація", - "contact_type": "Тип контакту", - "type_person": "Людина", - "type_organization": "організація", - "section_work": "Робота та організація", - "prefix": "Префікс", - "prefix_placeholder": "доктор, пан, місіс", - "given_name": "ім'я", - "middle_name": "По батькові", - "surname": "Прізвище", - "suffix": "Суфікс", - "suffix_placeholder": "мол., ст., ІІІ ст", - "nickname": "псевдонім", - "nickname_placeholder": "псевдонім", - "email": "Електронна пошта", - "email_placeholder": "email@example.com", - "phone": "Телефон", - "phone_placeholder": "+1 234 567 890", - "phone_type": "Тип", - "phone_voice": "Голос", - "phone_cell": "мобільний", - "phone_fax": "Факс", - "phone_pager": "Пейджер", - "phone_video": "відео", - "phone_text": "текст", - "organization": "організація", - "organization_placeholder": "Назва компанії", - "department": "відділ", - "department_placeholder": "відділ", - "job_title": "Назва посади", - "job_title_placeholder": "наприклад, інженер-програміст", - "role": "Роль", - "role_placeholder": "наприклад, Керівник групи", - "addresses": "Адреси", - "add_address": "Додайте адресу", - "street": "вул", - "city": "Місто", - "region": "Штат / Регіон", - "postcode": "Поштовий індекс", - "country": "Країна", - "online_services": "Онлайн-сервіси", - "add_online_service": "Додати онлайн-сервіс", - "url_placeholder": "https://...", - "service_placeholder": "Сервіс", - "anniversaries": "Ювілеї", - "add_anniversary": "Додайте дату", - "anniversary_birth": "день народження", - "anniversary_wedding": "Ювілей", - "anniversary_death": "Меморіал", - "anniversary_other": "інше", - "personal_info": "Особиста інформація", - "add_personal_info": "Додати запис", - "personal_info_placeholder": "наприклад, фотографія", - "personal_expertise": "Експертиза", - "personal_hobby": "хобі", - "personal_interest": "Інтерес", - "personal_other": "інше", - "level": "Рівень", - "level_high": "Високий", - "level_medium": "Середній", - "level_low": "Низький", - "categories": "Категорії", - "categories_placeholder": "наприклад, родина, друзі, колеги", - "categories_hint": "Введіть для пошуку або додайте категорії", - "category_add": "додати", - "note": "Примітки", - "note_placeholder": "Додати примітку...", - "gender": "Стать", - "gender_sex": "секс", - "gender_male": "Чоловік", - "gender_female": "Жінка", - "gender_other": "інше", - "gender_none": "Не застосовується", - "gender_unknown": "Невідомий", - "gender_identity": "Гендерна ідентичність", - "gender_identity_placeholder": "Гендерна ідентичність...", - "calendar": "Календар", - "calendar_uri": "URL-адреса календаря", - "scheduling_uri": "URL-адреса планування", - "freebusy_uri": "Вільний/зайнятий URL", - "context_work": "Робота", - "context_private": "Приватний", - "add_email": "Додайте адресу електронної пошти", - "add_phone": "Додати телефон", - "save": "зберегти", - "cancel": "Скасувати", - "creating": "Створення...", - "updating": "Оновлення...", - "name_required": "Вкажіть ім'я, прізвище або організацію", - "email_invalid": "Введіть дійсну електронну адресу", - "email_error_inline": "Недійсний формат електронної пошти", - "save_failed": "Не вдалося зберегти контакт", - "delete": "Видалити", - "upload_photo": "Завантажити фото", - "remove_photo": "Видалити фото", - "photo_hint": "JPG або PNG, до 10 МБ. Буде зменшено.", - "photo_too_large": "Зображення завелике (макс. 10 МБ)", - "photo_invalid": "Недійсний файл зображення", - "change_photo": "Змінити" - }, - "groups": { - "create": "Нова група", - "edit": "Редагувати групу", - "empty": "Груп ще немає", - "delete_confirm_title": "Видалити групу", - "delete_confirm": "Ви впевнені, що хочете видалити цю групу?", - "name_label": "Назва групи", - "name_placeholder": "наприклад, Команда, Сім’я", - "name_required": "Потрібно вказати назву групи", - "save_failed": "Не вдалося зберегти групу", - "members_label": "Члени", - "search_members": "Пошук контактів для додавання...", - "no_members": "У цій групі немає учасників", - "member_count": "{count, plural, =0 {Немає учасників} one {1 учасник} few {# учасники} many {# учасників} other {# учасників}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "Імпортувати контакти", - "drop_hint": "Натисніть, щоб вибрати файл vCard", - "file_types": "файли .vcf або .vcard", - "no_contacts": "У файлі не знайдено контактів", - "parse_error": "Не вдалося проаналізувати файл vCard", - "found": "{count, plural, one {1 контакт знайдено} few {# контакти знайдено} many {# контактів знайдено} other {# контактів знайдено}}", - "duplicate": "дублікат", - "select_all": "Вибрати все", - "deselect_all": "Зняти вибір із усіх", - "selected": "{count, plural, one {1 вибрано} few {# вибрано} many {# вибрано} other {# вибрано}}", - "import_button": "Імпорт", - "importing": "Імпорт...", - "success": "{count, plural, one {1 контакт імпортовано} few {# контакти імпортовано} many {# контактів імпортовано} other {# контактів імпортовано}}", - "failed": "Помилка імпорту", - "close": "Закрити", - "file_too_large": "Файл завеликий (макс. 5 МБ)" - }, - "export": { - "title": "Експортувати контакти", - "success": "{count, plural, one {1 контакт експортовано} few {# контакти експортовано} many {# контактів експортовано} other {# контактів експортовано}}" - }, - "bulk": { - "selected": "{count, plural, one {1 вибрано} few {# вибрано} many {# вибрано} other {# вибрано}}", - "select_all": "Вибрати все", - "delete": "Видалити", - "delete_confirm_title": "Видалити контакти", - "delete_confirm": "Видалити {count, plural, one {1 contact} other {# contacts}}?", - "deleted": "{count, plural, one {1 контакт видалено} few {# контакти видалено} many {# контактів видалено} other {# контактів видалено}}", - "add_to_group": "Додати до групи", - "choose_group": "Виберіть групу", - "adding_contacts": "Додавання {count, plural, one {1 contact} other {# contacts}}", - "added_to_group": "Контакти додано до групи", - "export": "Експорт", - "clear": "Очистити вибір" - }, - "toast": { - "created": "Контакт створено", - "updated": "Контакт оновлено", - "deleted": "Контакт видалено", - "error_create": "Не вдалося створити контакт", - "error_update": "Не вдалося оновити контакт", - "error_delete": "Не вдалося видалити контакт" - }, - "context_menu": { - "open": "Відкрити", - "edit": "Редагувати", - "send_email": "Надіслати лист", - "add_to_group": "Додати до групи", - "export_vcard": "Експортувати як vCard", - "delete": "Видалити", - "call": "Зателефонувати", - "duplicate": "Дублювати", - "print": "Друк" - }, - "filters": { - "toggle": "Фільтри", - "select": "Вибрати", - "clear": "Очистити", - "close": "Закрити", - "title": "Розширені фільтри", - "organization": "Компанія", - "organization_placeholder": "напр. Acme", - "job_title": "Посада", - "job_title_placeholder": "напр. Дизайнер", - "location": "Місцезнаходження", - "location_placeholder": "Місто або країна", - "email_domain": "Домен e-mail", - "email_domain_placeholder": "priklad.ua", - "birthday_month": "День народження в", - "any_month": "Будь-який місяць", - "has_email": "З ел. поштою", - "has_phone": "З телефоном", - "has_photo": "З фото", - "sort_by": "Сортувати за", - "sort_first_name": "Ім'я", - "sort_last_name": "Прізвище" - }, - "open_categories": "Відкрити категорії" - }, - "calendar": { - "title": "Календар", - "back_to_email": "Повернутися до електронної пошти", - "back_to_month": "Назад до місяця", - "my_calendars": "Календарі", - "birthday_calendar": "Дні народження", - "mini_calendar_change": "Натисніть, щоб змінити місяць", - "views": { - "month": "місяць", - "week": "тиждень", - "day": "День", - "agenda": "Порядок денний", - "today": "Сьогодні", - "month_hint": "місяць (м)", - "week_hint": "Тиждень (ж)", - "day_hint": "День (d)", - "agenda_hint": "Порядок денний (а)", - "tasks": "завдання", - "tasks_hint": "Завдання (k)" - }, - "events": { - "create": "Створити подію", - "edit": "Редагувати подію", - "delete": "Видалити подію", - "details": "Деталі події", - "no_events": "Подій немає", - "all_day": "Весь день", - "more": "+ ще {count}", - "no_title": "(Без назви)", - "resize": "Змінити розмір події", - "duplicate": "дублікат", - "today_header": "Сьогодні", - "tomorrow_header": "завтра", - "agenda_show_earlier": "Показати попередні події", - "agenda_loading": "Завантаження…", - "agenda_range_start": "Події з {date}", - "agenda_range_end": "Події до {date}", - "export_ics": "Експортувати як .ics", - "copy_title": "Скопіювати назву", - "copy_link": "Скопіювати посилання зустрічі", - "go_to_today": "Перейти до сьогодні", - "new_all_day_event": "Нова подія на весь день", - "new_event": "Нова подія", - "new_task": "Нове завдання" - }, - "detail": { - "add_note": "Додати примітку...", - "save_note": "зберегти", - "note_saved": "Примітка додана", - "open_link": "Відкрити посилання", - "meeting_link": "Посилання на зустріч", - "tentative": "Орієнтовний", - "cancelled": "Скасовано", - "delete_confirm": "Видалити цю подію?" - }, - "form": { - "title": "Назва", - "description": "опис", - "location": "Розташування", - "meeting_link": "Посилання на зустріч", - "start_date": "Дата початку", - "end_date": "Кінцева дата", - "start_time": "Час початку", - "end_time": "Час закінчення", - "all_day_event": "Подія на весь день", - "calendar_select": "Календар", - "save": "зберегти", - "cancel": "Скасувати", - "delete_confirm": "Ви впевнені, що хочете видалити цю подію?", - "color": "колір" - }, - "participants": { - "availability": { - "title": "Доступність", - "free": "Вільний", - "busy": "Зайнятий", - "tentative": "Попередньо", - "unknown": "Не користувач цього сервера", - "checking": "Перевірка…" - }, - "title": "Учасники", - "add": "Додати учасника", - "organizer": "Організатор", - "attendee": "Присутній", - "accepted": "прийнято", - "declined": "Відхилено", - "tentative": "Орієнтовний", - "needs_action": "Потребує дії", - "remove": "видалити", - "edit": "Редагувати", - "email_placeholder": "Додайте електронну адресу або знайдіть контакти", - "send_invitations": "Надішліть запрошення учасникам", - "status_summary": "{accepted} прийнято, {pending} очікує на розгляд", - "invited_by": "Запрошення від {name}", - "respond_below": "Відповісти за допомогою кнопок нижче", - "rsvp_label": "Ваша відповідь", - "cancel_notification": "Учасники будуть повідомлені про скасування", - "you_organizer": "Ви організатор", - "you_attendee": "Ви учасник", - "no_participants": "Немає учасників", - "count": "{count, plural, one {# учасник} few {# учасники} many {# учасників} other {# учасників}}" - }, - "recurrence": { - "title": "Повторюваність", - "none": "Не повторюється", - "daily": "Щодня", - "weekly": "Щотижня", - "monthly": "Щомісяця", - "yearly": "Щорічно", - "every_n_days": "Кожні {count} днів", - "every_n_weeks": "Кожні {count} тижнів", - "every_n_months": "Кожні {count} місяців", - "until": "Поки", - "occurrences": "{count} випадків", - "custom": "Власний…", - "edit_custom": "Змінити власне повторення", - "every_n_years": "Кожні {count} років", - "on_days": "у {days}", - "on_day_n": "{day}-го числа", - "on_the_nth": "у {nth} {day}", - "in_month": "у {month}", - "nth_1": "перший", - "nth_2": "другий", - "nth_3": "третій", - "nth_4": "четвертий", - "nth_last": "останній", - "editor_freq_day": "День", - "editor_freq_week": "Тиждень", - "editor_freq_month": "Місяць", - "editor_freq_year": "Рік", - "editor_repeats_on": "Повторюється", - "editor_every": "Кожні", - "editor_unit_days": "дн.", - "editor_unit_weeks": "тиж.", - "editor_unit_months": "міс.", - "editor_unit_years": "р.", - "editor_on_day": "у день", - "editor_on_the": "у", - "editor_in": "у місяці", - "editor_ends": "Закінчується", - "editor_never": "Ніколи", - "editor_ends_on": "Дата", - "editor_ends_after": "Після", - "editor_occurrences": "повторень" - }, - "recurrence_scope": { - "edit_title": "Редагувати повторювану подію", - "delete_title": "Видалити повторювану подію", - "description": "Це повторювана подія. Які події ви б хотіли змінити?", - "this_event": "Тільки ця подія", - "this_and_future": "Ця та наступні події", - "all_events": "Всі події", - "cancel": "Скасувати", - "save": "зберегти", - "delete": "Видалити", - "rsvp_title": "Відповісти на повторювану подію", - "rsvp_description": "Це повторювана подія. До яких подій стосується ваша відповідь?", - "respond": "Відповісти" - }, - "alerts": { - "title": "Нагадування", - "none": "Жодного нагадування", - "at_time": "На момент події", - "minutes_before": "{count, plural, one {# хвилина до} few {# хвилини до} many {# хвилин до} other {# хвилин до}}", - "hours_before": "{count, plural, one {# година до} few {# години до} many {# годин до} other {# годин до}}", - "days_before": "{count, plural, one {# день до} few {# дні до} many {# днів до} other {# днів до}}", - "weeks_before": "{count, plural, one {# тиждень до} few {# тижні до} many {# тижнів до} other {# тижнів до}}", - "unit_minutes_before": "хвилин до", - "unit_hours_before": "годин до", - "unit_days_before": "днів до", - "unit_weeks_before": "тижнів до", - "add": "Додати нагадування", - "remove": "Видалити нагадування", - "amount": "Кількість нагадування", - "unit": "Одиниця нагадування" - }, - "settings": { - "title": "Налаштування календаря", - "default_view": "Перегляд за замовчуванням", - "week_starts_on": "Тиждень починається о", - "time_format": "Формат часу", - "default_calendar": "Календар за замовчуванням", - "default_reminder": "Нагадування за умовчанням", - "time_format_12h": "12-годинний", - "time_format_24h": "24 години", - "notifications_enabled": "Сповіщення про події", - "notifications_enabled_desc": "Показувати сповіщення про майбутні події календаря", - "notification_sound": "Звук сповіщення", - "notification_sound_desc": "Відтворення звуку для сповіщень календаря", - "invitation_parsing": "Проаналізуйте запрошення електронною поштою", - "invitation_parsing_desc": "Виявляти запрошення календаря у вкладеннях електронної пошти та показувати дії календаря", - "show_time_in_month_view": "Показати час у режимі перегляду місяця", - "show_time_in_month_view_desc": "Відображення часу подій у місячному календарі", - "show_week_numbers": "Показати номери тижнів", - "show_week_numbers_desc": "Відображення номерів тижнів у міні-календарі", - "calendar_free_scroll": "Вільне прокручування", - "calendar_free_scroll_desc": "Безперервно прокручувати місяці, тижні та дні замість одного періоду за раз", - "enable_tasks": "Увімкнути завдання", - "enable_tasks_desc": "Показати перегляд завдань у календарі для керування справами", - "show_tasks_on_calendar": "Показати завдання в календарі", - "show_tasks_on_calendar_desc": "Відображення фішок завдань у режимі перегляду календаря дня та тижня", - "hover_preview": "Попередній перегляд подій", - "hover_preview_desc": "Показувати спливаюче вікно з деталями під час наведення курсора на події календаря", - "hover_preview_instant": "Миттєво", - "hover_preview_delay_500ms": "0,5-секундна затримка", - "hover_preview_delay_1s": "1-секундна затримка", - "hover_preview_delay_2s": "2-секундна затримка", - "hover_preview_off": "Вимкнено", - "organizer_identity": "Організовувати запрошення як", - "organizer_identity_desc": "Адреса календаря, що використовується як організатор, коли ви запрошуєте людей на події.", - "organizer_identity_failed": "Не вдалося змінити адресу організатора", - "show_birthday_calendar": "Контактний календар днів народження", - "show_birthday_calendar_desc": "Показати віртуальний календар із днями народження ваших контактів" - }, - "days": { - "monday": "понеділок", - "tuesday": "вівторок", - "wednesday": "Середа", - "thursday": "четвер", - "friday": "П'ятниця", - "saturday": "Субота", - "sunday": "неділя", - "mon": "Пн", - "tue": "Вт", - "wed": "ср", - "thu": "чт", - "fri": "пт", - "sat": "сб", - "sun": "сонце" - }, - "notifications": { - "event_created": "Подія створена", - "event_updated": "Подію оновлено", - "event_deleted": "Подію видалено", - "calendar_created": "Календар створено", - "calendar_deleted": "Календар видалено", - "event_move_error": "Не вдалося перемістити подію", - "event_resize_error": "Не вдалося змінити розмір події", - "alert_title": "Майбутня подія", - "alert_now": "Починаючи зараз", - "alert_in_minutes": "Через {count} хв", - "invitation_sent": "Запрошення надіслано", - "invitations_denied_title": "Не вдається надіслати запрошення", - "invitations_denied": "Сервер відмовився надіслати запрошення: {reason}. Зберегти подію без їх надсилання?", - "save_without_invitations": "Зберегти без запрошень", - "rsvp_updated": "Відповідь оновлено", - "rsvp_error": "Не вдалося оновити відповідь", - "event_duplicated": "Подія дублюється", - "event_error": "Не вдалося зберегти подію", - "task_due": "Термін виконання завдання", - "event_exported": "Подію експортовано", - "title_copied": "Назву скопійовано", - "link_copied": "Посилання скопійовано" - }, - "status": { - "loading_calendars": "Завантаження календарів...", - "loading_events": "Завантаження подій..." - }, - "quick_create": { - "placeholder": "Нова назва події", - "aria_label": "Швидке створення події" - }, - "nav_prev": "Попередній", - "nav_next": "Далі", - "import": { - "title": "Імпорт календаря", - "tab_file": "Файл", - "tab_url": "URL", - "select_file": "Виберіть файл .ics", - "drop_file": "або перетягніть файл сюди", - "supported_formats": "Підтримує файли iCalendar (.ics).", - "url_description": "Введіть URL-адресу зовнішнього каналу iCalendar (.ics), щоб імпортувати події.", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "Підтримує URL-адреси iCalendar (.ics).", - "fetch": "Принести", - "invalid_url": "Введіть дійсну URL-адресу", - "url_fetch_failed": "Не вдалося отримати календар з URL-адреси", - "parsing": "Розбір файлу календаря...", - "parsed_events": "Знайдено подій: {count}", - "no_events": "У файлі не знайдено подій", - "select_all": "Вибрати все", - "deselect_all": "Зняти вибір із усіх", - "target_calendar": "Імпорт до календаря", - "import_button": "Вибраний імпорт", - "importing": "Імпорт подій...", - "success": "{count} подій успішно імпортовано", - "error": "Не вдалося імпортувати календар", - "file_too_large": "Файл перевищує обмеження в 10 Мб", - "invalid_format": "Недійсний формат файлу календаря" - }, - "webcal_action": { - "title": "Відкрити посилання календаря", - "description": "Як ви хочете використати \"{name}\"?", - "import_title": "Імпортувати один раз", - "import_description": "Завантажити події зараз і скопіювати їх до одного з ваших календарів.", - "subscribe_title": "Підписатися", - "subscribe_description": "Автоматично синхронізувати цей календар як окремий календар.", - "cancel": "Скасувати" - }, - "management": { - "title": "Управління календарем", - "description": "Створюйте, перейменовуйте та налаштовуйте свої календарі. Клацніть правою кнопкою миші календар на бічній панелі, щоб швидко змінити його колір.", - "name": "Ім'я", - "name_placeholder": "Назва календаря", - "color": "колір", - "change_color": "Змінити колір", - "random_color": "Новий випадковий колір", - "add_calendar": "Додати календар", - "edit": "Редагувати", - "delete": "Видалити", - "save": "зберегти", - "create": "Створити", - "cancel": "Скасувати", - "default": "За замовчуванням", - "confirm_delete": "Видалити \"{name}\"? Усі події в цьому календарі буде видалено.", - "confirm_clear": "Очистити всі події з \"{name}\"? Це неможливо скасувати.", - "set_default": "Зробити типовим", - "default_updated": "Типовий календар оновлено", - "error_default": "Не вдалося встановити типовий календар", - "clear_events": "Ясні події", - "events_cleared": "{count} подій видалено", - "error_clear": "Не вдалося очистити події календаря", - "calendar_created": "Календар створено", - "calendar_updated": "Календар оновлено", - "calendar_deleted": "Календар видалено", - "color_updated": "Оновлено колір календаря", - "error_create": "Не вдалося створити календар", - "error_update": "Не вдалося оновити календар", - "error_delete": "Не вдалося видалити календар", - "caldav_url": "URL-адреса CalDAV", - "copy_url": "Скопіюйте URL-адресу CalDAV", - "url_copied": "URL-адресу CalDAV скопійовано в буфер обміну", - "share": "Поділитися календарем", - "new_event_in_calendar": "Нова подія в цьому календарі", - "kind": "Вміст", - "kind_events": "Події", - "kind_tasks": "Завдання", - "kind_both": "Події та завдання", - "kind_hint": "Визначає, які застосунки бачать цей календар під час синхронізації через CalDAV. Пізніше змінити не можна." - }, - "subscription": { - "title": "Підписка iCal", - "section_title": "Підписки iCal", - "description": "Підпишіться на зовнішній канал iCalendar. Події будуть автоматично синхронізовані з власним календарем. Підтримує URL-адреси https:// і webcal://.", - "url_label": "URL-адреса календаря", - "url_placeholder": "https://example.com/calendar.ics або webcal://...", - "name_label": "Назва календаря", - "name_placeholder": "напр. Державні свята", - "color_label": "колір", - "refresh_interval": "Інтервал оновлення", - "interval_15": "Кожні 15 хвилин", - "interval_30": "Кожні 30 хв", - "interval_60": "Щогодини", - "interval_360": "Кожні 6 годин", - "interval_1440": "Кожен день", - "subscribe": "Підпишіться", - "subscribing": "Підписка...", - "save": "Зберегти зміни", - "saving": "Збереження...", - "edit": "Редагувати", - "edit_title": "Редагувати підписку", - "updated": "Оновлено \"{name}\"", - "update_error": "Не вдалося оновити підписку", - "invalid_url": "Введіть дійсну URL-адресу", - "success": "Підписався на \"{name}\"", - "error": "Не вдалося додати підписку", - "refresh": "Оновіть зараз", - "refresh_success": "Підписку оновлено", - "refresh_error": "Не вдалося оновити підписку", - "unsubscribe": "Відписатися", - "confirm_delete": "Скасувати підписку на \"{name}\"? Календар і всі його події буде видалено.", - "deleted": "Підписку видалено", - "delete_error": "Не вдалося видалити підписку", - "last_refreshed": "Останнє оновлення: {time}" - }, - "tasks": { - "label": "завдання", - "no_tasks": "Завдань немає", - "no_title": "(Без назви)", - "mark_complete": "Позначити як завершене", - "mark_incomplete": "Позначити як незавершене", - "filter_all": "все", - "filter_pending": "В очікуванні", - "filter_completed": "Виконано", - "filter_overdue": "Прострочена", - "show_completed": "Показ завершено", - "create": "Нове завдання", - "edit": "Редагувати завдання", - "title_placeholder": "Назва завдання", - "description_placeholder": "Додайте опис...", - "due_date": "Термін виконання", - "include_time": "Включіть час", - "priority": "Пріоритет", - "priority_none": "Жодного", - "priority_high": "Високий", - "priority_medium": "Середній", - "priority_low": "Низький", - "progress": "Статус", - "progress_needs_action": "Потребує дії", - "progress_in_process": "В процесі", - "progress_completed": "Виконано", - "progress_cancelled": "Скасовано", - "calendar": "Календар", - "alert": "Нагадування", - "alert_none": "Жодного", - "alert_at_time": "У встановлений термін", - "alert_5min": "5 хвилин до", - "alert_15min": "за 15 хвилин до", - "alert_30min": "за 30 хвилин до", - "alert_1hr": "за 1 годину до", - "alert_1day": "за 1 день до", - "delete": "Видалити", - "cancel": "Скасувати", - "save": "зберегти", - "quick_add_placeholder": "Додати завдання...", - "due_today": "Сьогодні", - "due_tomorrow": "завтра", - "overdue": "Прострочена" - }, - "months": { - "jan": "січ.", - "feb": "лют.", - "mar": "бер.", - "apr": "квіт.", - "may": "трав.", - "jun": "черв.", - "jul": "лип.", - "aug": "серп.", - "sep": "верес.", - "oct": "жовт.", - "nov": "лист.", - "dec": "груд.", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "Відкрити меню" - }, - "advanced_search": { - "title": "Розширений пошук", - "from": "Від", - "from_placeholder": "Електронна пошта або ім’я відправника", - "to": "до", - "to_placeholder": "Електронна адреса або ім’я одержувача", - "subject": "Тема", - "subject_placeholder": "Предмет містить...", - "body": "Тіло", - "body_placeholder": "Тіло містить...", - "folder": "Папка", - "all_folders": "Всі папки", - "has_attachment": "Додатки", - "date_after": "Після", - "date_before": "Раніше", - "size_min": "Більше ніж (КБ)", - "size_max": "Менше ніж (КБ)", - "starred": "Позначено зірочкою", - "unread": "Непрочитаний", - "read": "Прочитайте", - "yes": "так", - "no": "немає", - "clear": "ясно", - "clear_all": "Очистити все", - "filters_active": "фільтр {count}", - "filters_active_plural": "{count} фільтрів", - "toggle_filters": "більше", - "search_hint": "Використовуйте розширені фільтри для точного пошуку", - "advanced_filters_tooltip": "Розширені фільтри пошуку", - "results_found": "{count, plural, =0 {Результатів не знайдено} one {# результат знайдено} few {# результати знайдено} many {# результатів знайдено} other {# результатів знайдено}}", - "results_found_more": "Знайдено {count}+ результатів", - "suggestions_recent": "Останні пошуки", - "suggestions_people": "Люди", - "suggestions_from": "Шукати листи від {address}", - "suggestions_to": "Шукати листи для {address}", - "suggestions_remove_recent": "Видалити з останніх пошуків" - }, - "welcome": { - "title": "Ласкаво просимо до вашої поштової скриньки", - "tip_compose": "Натисніть c, щоб створити новий електронний лист", - "tip_shortcuts": "Натисніть ? щоб побачити всі комбінації клавіш", - "tip_sidebar": "Знайдіть Календар, Контакти та Налаштування в меню бічної панелі", - "tip_settings": "Налаштуйте свій досвід у налаштуваннях", - "got_it": "зрозумів", - "settings": "Налаштування", - "dismiss": "Відхилити", - "start_tour": "Розпочати тур" - }, - "demo_welcome": { - "title": "Ласкаво просимо до Bulwark Mail", - "description": "Відкрийте для себе повнофункціональний клієнт веб-пошти - прямо у вашому браузері. Усі дані залишаються на вашому пристрої, тому сміливо тестуйте все.", - "feature_email": "Читайте та створюйте електронний лист", - "feature_organize": "Теги, зірочки та папки", - "feature_shortcuts": "Комбінації клавіш", - "feature_privacy": "100% приватна демонстрація", - "hint": "Натисніть будь-яку електронну пошту ліворуч, щоб розпочати, або перегляньте огляд нижче." - }, - "files": { - "office_edit": "Редагувати документ", - "office_loading": "Відкриття редактора документів…", - "office_error": "Не вдалося відкрити редактор документів.", - "office_close": "Закрити редактор", - "open_in_new_tab": "Відкрити в новій вкладці", - "title": "Файли", - "search_placeholder": "Пошук файлів...", - "empty_state_title": "Файлів ще немає", - "empty_state_description": "Щоб почати, завантажте файли або створіть папки", - "upload": "Завантажити", - "upload_files": "Завантажити файли", - "new_folder": "Нова папка", - "new_folder_name": "Назва папки", - "rename": "Перейменувати", - "rename_title": "Перейменувати", - "new_name": "Нова назва", - "name_forbidden_chars": "Сервер не дозволяє ці символи в назвах: {chars}", - "name_reserved": "Ця назва зарезервована на сервері.", - "delete": "Видалити", - "delete_confirm_title": "Видалити ресурс", - "delete_confirm_message": "Ви впевнені, що хочете видалити \"{name}\"? Це неможливо скасувати.", - "download": "Завантажити", - "name": "Ім'я", - "size": "Розмір", - "modified": "Змінено", - "type": "Тип", - "folder": "Папка", - "file": "Файл", - "parent_directory": "Батьківський каталог", - "breadcrumb_root": "додому", - "drop_files_here": "Перетягніть файли або папки сюди, щоб завантажити", - "uploading": "Завантаження...", - "upload_success": "{count, plural, one {1 файл завантажено} few {# файли завантажено} many {# файлів завантажено} other {# файлів завантажено}}", - "upload_error": "Не вдалося завантажити файл", - "create_folder_success": "Папка створена", - "create_folder_error": "Не вдалося створити папку", - "delete_success": "Успішно видалено", - "delete_error": "Не вдалося видалити", - "rename_success": "Успішно перейменовано", - "rename_error": "Не вдалося перейменувати", - "download_error": "Не вдалося завантажити", - "not_available": "Зберігання файлів недоступне на цьому сервері", - "cancel": "Скасувати", - "create": "Створити", - "save": "зберегти", - "no_results": "Немає файлів, що відповідають вашому пошуку", - "batch_delete_confirm_message": "Ви впевнені, що хочете видалити {count, plural, one {1 item} other {# items}}? Це неможливо скасувати.", - "batch_delete_success": "{count, plural, one {1 елемент видалено} few {# елементи видалено} many {# елементів видалено} other {# елементів видалено}}", - "grid_view": "Вигляд сітки", - "list_view": "Перегляд списку", - "details": "Подробиці", - "path": "шлях", - "preview": "Попередній перегляд", - "preview_error": "Не вдалося завантажити попередній перегляд", - "cut": "Вирізати", - "copy": "Копія", - "paste": "Вставити", - "move_success": "{count, plural, one {1 елемент переміщено} few {# елементи переміщено} many {# елементів переміщено} other {# елементів переміщено}}", - "move_error": "Не вдалося переміститися", - "paste_success": "Вставлено успішно", - "paste_error": "Не вдалося вставити", - "new_text_file": "Новий текстовий файл", - "file_name": "Ім'я файлу", - "retry": "Повторіть спробу", - "refresh": "Оновити", - "toggle_favorite": "Перемкнути улюблене", - "duplicate": "дублікат", - "duplicate_success": "Продубльовано успішно", - "duplicate_error": "Не вдалося скопіювати", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "Файл створено", - "create_file_error": "Не вдалося створити файл", - "favorites": "Вибране", - "recent": "Останні", - "properties": "Властивості", - "open_folder": "Відкрити папку", - "upload_folder": "Завантажити папку", - "file_too_large": "\"{name}\" перевищує максимальний розмір файлу ({max})", - "undo": "Скасувати", - "undo_success": "Дія скасована", - "undo_error": "Не вдалося скасувати", - "toolbar": "Дії з файлами", - "file_list": "Файли та папки", - "context_menu": "Дії", - "settings_title": "Параметри файлу", - "settings_display": "Дисплей", - "settings_default_view": "Перегляд за умовчанням", - "settings_default_view_desc": "Вибір між сіткою та списком", - "settings_default_sort": "Типове сортування", - "settings_default_sort_desc": "Виберіть стандартне сортування для файлів", - "settings_sort_direction": "Напрямок сортування", - "settings_sort_direction_desc": "Виберіть порядок зростання або спадання", - "settings_ascending": "Висхідний", - "settings_descending": "Спускається", - "settings_icons": "Ікони", - "settings_show_icons": "Показати значки файлів", - "settings_show_icons_desc": "Відображення піктограм біля файлів і папок", - "settings_colored_icons": "Кольорові значки", - "settings_colored_icons_desc": "Використовуйте кольорові значки замість монохромних", - "settings_show_thumbnails": "Показати мініатюри", - "settings_show_thumbnails_desc": "Відображати попередній перегляд зображень замість піктограм для файлів зображень", - "settings_behavior": "Поведінка", - "settings_show_hidden": "Показати приховані файли", - "settings_show_hidden_desc": "Відображення файлів і папок, які починаються з крапки", - "settings_folder_layout": "Навігація по папках", - "settings_folder_layout_desc": "Виберіть спосіб відображення папок: у рядку з файлами чи в дереві бічної панелі", - "settings_folder_layout_inline": "Вбудований", - "settings_folder_layout_sidebar": "Бічна панель", - "disabled_title": "Функцію файлів вимкнено вашим адміністратором", - "disabled_description": "Завантаження великих файлів через WebDAV може спричинити нестабільність Stalwart/RocksDB, зокрема збої через брак пам’яті та невідновне використання диска. Видалені файли не можуть бути негайно очищені зі сховища BLOB-об’єктів. Ця функція не рекомендована для робочих середовищ.", - "stability_warning": "Завантаження великих файлів може спричинити нестабільність сервера. Видалені файли не можуть бути негайно видалені зі сховища. Використовуйте з обережністю.", - "no_accounts": "Немає підключених облікових записів.", - "open_folder_tree": "Відкрити дерево тек", - "other_accounts": "Інші облікові записи", - "migration_title": "Оновлення ваших файлів…", - "migration_description": "Папки та файли впорядковуються у правильну структуру. Це відбувається лише один раз." - }, - "smime": { - "your_certificates": "Ваші сертифікати", - "your_certificates_desc": "Імпортуйте та керуйте своїми сертифікатами S/MIME для підпису та шифрування електронних листів", - "recipient_certificates": "Сертифікати отримувача", - "recipient_certificates_desc": "Публічні сертифікати для шифрування електронних листів одержувачам", - "identity_bindings": "Ідентифікаційні ключові прив’язки", - "identity_bindings_desc": "Прив’яжіть сертифікати S/MIME до вашої електронної пошти", - "defaults_title": "За замовчуванням", - "defaults_desc": "Налаштувати підписання та шифрування за замовчуванням", - "import_pkcs12": "Імпорт PKCS#12 (.p12/.pfx)", - "import_public_cert": "Імпортний сертифікат", - "no_certificates": "Сертифікати ще не імпортовано", - "no_recipient_certs": "Немає сертифікатів отримувача", - "expires": "Термін дії закінчується", - "expired": "Термін дії минув", - "bound_to": "Прив’язаний до", - "no_key_bound": "Жодного", - "lock": "Ключ від замка", - "unlock": "Ключ розблокування", - "details": "Переглянути деталі", - "delete": "Видалити", - "encrypt_by_default": "Шифрувати за замовчуванням", - "encrypt_by_default_desc": "Автоматично шифрувати електронні листи, коли всі одержувачі мають сертифікати", - "remember_unlocked": "Запам'ятайте незаблоковані ключі", - "remember_unlocked_desc": "Тримайте ключі розблокованими протягом цього сеансу браузера", - "sign_default_for": "Підписати за замовчуванням для", - "enter_p12_passphrase": "Введіть парольну фразу PKCS#12", - "p12_passphrase_desc": "Введіть парольну фразу, яка захищає цей файл сертифіката", - "enter_storage_passphrase": "Установіть парольну фразу для зберігання", - "storage_passphrase_desc": "Виберіть парольну фразу, щоб захистити цей ключ у вашому браузері", - "next": "Далі", - "import": "Імпорт", - "unlock_key": "Розблокувати ключ", - "unlock_key_desc": "Введіть парольну фразу для зберігання, щоб розблокувати цей ключ для підпису чи розшифровки", - "passphrase_placeholder": "Введіть парольну фразу", - "confirm_passphrase_placeholder": "Підтвердьте парольну фразу", - "passphrase_mismatch": "Парольні фрази не збігаються", - "cancel": "Скасувати", - "processing": "Обробка…", - "close": "Закрити", - "certificate_details": "Деталі сертифіката", - "cert_subject": "Тема", - "cert_issuer": "Емітент", - "cert_email": "Електронна пошта", - "cert_serial": "Серійний номер", - "cert_validity": "Термін дії", - "cert_fingerprint": "Відбиток пальця (SHA-256)", - "cert_algorithm": "Алгоритм", - "cert_capabilities": "Можливості", - "cert_source": "Джерело", - "cert_expired": "Термін дії цього сертифіката закінчився", - "cert_not_yet_valid": "Цей сертифікат ще не дійсний", - "cap_sign": "Підписання", - "cap_encrypt": "Шифрування", - "cap_none": "Жодного", - "show_passphrase": "Показати парольну фразу", - "hide_passphrase": "Приховати парольну фразу", - "sign_toggle": "Знак", - "encrypt_toggle": "Зашифрувати", - "missing_recipient_certs": "Відсутні сертифікати для: {emails}", - "missing_sender_cert": "Жоден сертифікат не прив’язаний до цієї особи", - "status_encrypted_ok": "Це повідомлення було зашифровано", - "status_encrypted_no_key": "Це повідомлення зашифровано, але відповідного ключа не знайдено", - "status_encrypted_failed": "Не вдалося розшифрувати це повідомлення", - "status_signed_valid": "Підпис перевірено", - "status_signed_invalid": "Не вдалося перевірити підпис", - "status_signed_expired_cert": "Підписано простроченим сертифікатом", - "status_signed_self_signed": "Підпис дійсний, але сертифікат самопідписаний (ненадійний)", - "status_signed_mismatch": "Підпис дійсний, але підписувач не відповідає відправнику", - "status_unsupported": "Непідтримуваний формат S/MIME", - "auto_import_signer_certs": "Автоматичний імпорт сертифікатів підписувача", - "auto_import_signer_certs_desc": "Автоматично зберігайте сертифікати з перевірених підписаних електронних листів для майбутнього шифрування", - "export": "Експорт", - "enter_export_passphrase": "Установіть парольну фразу експорту", - "export_passphrase_desc": "Виберіть парольну фразу для захисту експортованого файлу PKCS#12", - "export_storage_desc": "Введіть парольну фразу для зберігання, щоб розшифрувати ключ для експорту", - "incorrect_passphrase": "Неправильна парольна фраза" - }, - "tour": { - "step_counter": "Крок {current} з {total}", - "skip": "Пропустити тур", - "back": "Назад", - "next": "Далі", - "finish": "Закінчити", - "take_a_tour": "Ознайомтеся з інтерфейсом", - "restart_title": "Ознайомчий тур", - "restart_desc": "Повторіть покрокове керівництво по інтерфейсу", - "restart_button": "Перезапустити тур", - "show_on_new_devices_title": "Показувати на нових пристроях", - "show_on_new_devices_desc": "Повторіть привітальний банер і тур під час першого входу на новому пристрої, навіть якщо ви вже завершили їх в іншому місці", - "sidebar_title": "Ваші поштові скриньки", - "sidebar_desc": "Це бічна панель вашої папки. Натисніть будь-яку поштову скриньку, щоб переглянути її електронні листи. Ви можете створювати папки, перетягувати електронні листи між ними та миттєво переглядати кількість непрочитаних.", - "compose_title": "Створіть електронний лист", - "compose_desc": "Натисніть тут, щоб написати новий електронний лист. Ви можете додавати одержувачів, вкладення та використовувати форматований текст.", - "search_title": "Шукайте свою пошту", - "search_desc": "Пошук за відправником, темою чи вмістом. Клацніть піктограму фільтра, щоб переглянути додаткові параметри, як-от діапазон дат, вкладення та повідомлення із зірочкою.", - "email_list_title": "Ваш список адрес електронної пошти", - "email_list_desc": "Тут відображаються електронні листи. Натисніть один, щоб прочитати його праворуч. Використовуйте прапорець, щоб вибрати кілька, а потім масово перемістити, видалити або позначити їх.", - "email_viewer_title": "Панель читання", - "email_viewer_desc": "Вибраний електронний лист відкривається тут. Відповідайте, пересилайте, архівуйте або видаляйте за допомогою кнопок панелі інструментів. Ви також можете зірчати електронні листи або додавати кольорові теги.", - "keywords_title": "Кольорові теги", - "keywords_desc": "Упорядкуйте свою електронну пошту за допомогою кольорових тегів. Перетягніть електронний лист на тег, щоб позначити його, або клацніть електронний лист правою кнопкою миші, щоб призначити теги.", - "calendar_title": "Календар", - "calendar_desc": "Перейдіть до календаря, щоб керувати подіями. Створюйте події, встановлюйте нагадування та переглядайте макети днів, тижнів або місяців.", - "contacts_title": "Контакти", - "contacts_desc": "Ваша адресна книга живе тут. Імпортуйте контакти, створюйте групи та клацніть будь-який контакт, щоб переглянути його повну інформацію.", - "settings_title": "Налаштування", - "settings_desc": "Налаштуйте все: тему, щільність, підписи, фільтри, комбінації клавіш, стандартні параметри календаря тощо.", - "shortcuts_title": "Комбінації клавіш", - "shortcuts_desc": "Це подобається досвідченим користувачам. Натисніть ? у будь-який час, щоб переглянути всі доступні ярлики. Ви можете переходити, створювати та керувати електронними листами, не торкаючись миші.", - "compose_open_title": "Композитор", - "compose_open_desc": "Це редактор електронних листів. Додайте одержувачів, напишіть повідомлення, вкладіть файли та використовуйте форматування розширеного тексту. Ви також можете зберігати чернетки та використовувати шаблони.", - "calendar_view_title": "Ваш календар", - "calendar_view_desc": "Ось ваш календар із зразками подій. За допомогою панелі інструментів можна переключатися між переглядами дня, тижня, місяця та порядку денного.", - "create_event_title": "Створіть подію", - "create_event_desc": "Натисніть цю кнопку, щоб створити нову подію календаря. Ви можете встановити назву, дату, час і додати учасників.", - "event_modal_title": "Деталі події", - "event_modal_desc": "Ось форма події. Введіть назву, виберіть дату та час, додайте місце чи учасників. Натисніть «Зберегти», коли закінчите - або закрийте його та йдіть далі.", - "contacts_list_title": "Ваші контакти", - "contacts_list_desc": "Ось ваші контакти. Клацніть будь-який контакт, щоб побачити його повну інформацію праворуч. Ви також можете створювати нові контакти, імпортувати vCards або організовувати контакти в групи.", - "settings_tabs_title": "Меню налаштувань", - "settings_tabs_desc": "Ось усі категорії налаштувань. Налаштуйте свій зовнішній вигляд, керуйте ідентифікаторами, налаштуйте фільтри електронної пошти, налаштуйте свій календар і багато іншого.", - "files_title": "Зберігання файлів", - "files_desc": "Ваш файловий браузер дозволяє завантажувати, упорядковувати та ділитися файлами, як особистий хмарний диск, вбудований у вашу пошту.", - "demo_banner_title": "Демонстраційні елементи керування", - "demo_banner_desc": "Ви в демонстраційному режимі - все залишається у вашому браузері. Будь-коли натисніть «Скинути демонстрацію», щоб почати заново з чистими зразками даних.", - "quota_title": "Використання сховища", - "quota_desc": "Відстежуйте розмір своєї поштової скриньки тут. Коло заповнюється, коли ви використовуєте більше місця." - }, - "unified_mailbox": { - "search_unavailable": "Пошук недоступний в об'єднаному перегляді" - }, - "calendar_event_notifications": { - "someone": "Хтось", - "invited": "{name} запросив(ла) вас на «{title}»", - "updated": "{name} оновив(ла) «{title}»", - "cancelled": "{name} скасував(ла) «{title}»", - "untitled": "Подія без назви" - }, - "share_notifications": { - "someone": "Хтось", - "shared": "{name} поділився(лася) з вами {kind} «{object}»", - "changed": "{name} змінив(ла) ваш доступ до {kind} «{object}»", - "revoked": "{name} скасував(ла) ваш доступ до {kind} «{object}»", - "object": { - "folder": "текою", - "calendar": "календарем", - "address_book": "адресною книгою", - "files": "текою файлів" - } - }, - "sharing": { - "title": "Поділитися «{name}»", - "description": "Надайте доступ іншим користувачам або групам на цьому сервері. Зміни набувають чинності негайно.", - "no_shares": "Поки що ні з ким не поділено.", - "add_person": "Додати людину або групу", - "search_placeholder": "Шукати за іменем або email…", - "loading_principals": "Завантаження користувачів…", - "no_principals": "Інших користувачів або груп не знайдено.", - "no_match": "Збігів немає.", - "remove": "Видалити доступ", - "group": "Група", - "share_added": "Доступ надано", - "share_updated": "Доступ оновлено", - "share_removed": "Доступ видалено", - "share_failed": "Не вдалося оновити спільний доступ", - "preset": { - "freeBusy": "Лише зайнятість", - "read": "Лише читання", - "readWrite": "Читання та запис", - "manager": "Керівник", - "custom": "Власне" - } - }, - "quote_header": { - "reply_line": "{date}, {from} написав:", - "forwarded_separator": "---------- Переслане повідомлення ----------", - "from_label": "Від", - "date_label": "Дата", - "subject_label": "Тема" - }, - "pwa_install": { - "title": "Встановити {appName}", - "description": "Встановіть наш застосунок для швидкого доступу та офлайн-підтримки.", - "not_now": "Не зараз", - "install": "Встановити", - "dont_remind": "Більше не нагадувати", - "dismiss_aria": "Закрити запит на встановлення" - }, - "deep_link": { - "message_not_found": "Цей лист більше недоступний.", - "thread_not_found": "Ця розмова більше недоступна.", - "folder_not_found": "Ця тека більше недоступна.", - "event_not_found": "Ця подія більше недоступна.", - "file_not_found": "Цей файл більше недоступний.", - "account_unavailable": "Увійдіть у цей обліковий запис, щоб відкрити посилання.", - "copy_message": "Скопіювати посилання на лист", - "copy_conversation": "Скопіювати посилання на розмову", - "copy_event": "Скопіювати посилання на подію", - "copied": "Посилання скопійовано", - "copy_failed": "Не вдалося скопіювати посилання" - }, - "plugins": { - "oauth_callback": { - "completing": "Завершення авторизації…", - "completing_message": "Зачекайте, поки встановлюється з'єднання.", - "received_title": "Авторизацію отримано", - "received_message": "Ви можете повернутися на вкладку, де розпочали підключення.", - "failed_title": "Не вдалося виконати авторизацію", - "denied": "В авторизації відмовлено.", - "failed_detail": "Не вдалося виконати авторизацію: {error}", - "missing_params": "Відсутній код авторизації або стан.", - "delivery_failed": "Не вдалося передати результат авторизації на вкладку програми. Закрийте це вікно та спробуйте ще раз." - } - }, - "global_search": { - "title": "Шукати скрізь", - "placeholder": "Пошук у пошті, контактах, календарі, файлах...", - "scope_label": "Шукати в", - "account_label": "Обліковий запис", - "scope_all": "Усе", - "scope_mail": "Пошта", - "scope_contacts": "Контакти", - "scope_calendar": "Календар", - "scope_files": "Файли", - "all_accounts": "Усі облікові записи", - "show_all": "Показати всі результати", - "no_results": "Нічого не знайдено за запитом «{query}»", - "server_hint": "Цілі слова шукаються на сервері; частини слів — серед уже завантаженого", - "searching": "Пошук...", - "account_error": "Не вдалося виконати пошук у {account}", - "recent_searches": "Останні запити", - "recurring": "Повторювана", - "no_subject": "(no subject)", - "empty_hint": "Шукайте пошту, контакти, події та файли в усіх облікових записах. Спробуйте from:, subject:, has:attachment, after:2026-01-01.", - "flat_by_date": "Списком за датою", - "grouped": "За групами", - "load_more": "Завантажити ще", - "preview_empty": "Виберіть результат для перегляду", - "open_full": "Відкрити", - "view_label": "Вигляд" - } -} +{ + "meta_description": "Мінімалістичний вебклієнт електронної пошти, що використовує протокол JMAP", + "login": { + "title": "Веб-пошта", + "username_label": "Електронна пошта", + "username_placeholder": "user@example.com", + "password_label": "Пароль", + "password_placeholder": "Введіть свій пароль", + "jmap_endpoint_label": "Сервер JMAP", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "Сервер має дозволити запити CORS із цього домену.", + "jmap_server_label": "Сервер", + "jmap_server_auto_picked": "Сервер обрано з домену вашої електронної пошти.", + "sign_in": "Увійдіть", + "signing_in": "Вхід...", + "loading": "Завантаження...", + "reconnecting": "З'єднання втрачено. Спроба повторного підключення…", + "error": { + "invalid_credentials": "Неправильна адреса електронної пошти або пароль. Перевірте свої облікові дані та повторіть спробу.", + "invalid_token": "Сервер не прийняв цей токен доступу. Перевірте, чи його скопійовано повністю, чи не минув термін його дії і чи його не відкликано.", + "connection_failed": "Не вдалося підключитися до сервера. Перевірте підключення до Інтернету та повторіть спробу.", + "cors_blocked": "Сервер доступний, але він блокує перехресні запити. Перевірте налаштування CORS свого сервера JMAP і дозвольте цей домен.", + "server_error": "Сервер тимчасово недоступний. Спробуйте пізніше.", + "generic": "Сталася неочікувана помилка. Якщо це не зникне, зверніться до адміністратора.", + "totp_required": "Потрібен код двофакторної автентифікації. Будь ласка, введіть свій код нижче.", + "totp_invalid": "Недійсний код автентифікації. Перевірте свою програму автентифікації та повторіть спробу.", + "oauth_discovery_failed": "Система єдиного входу ввімкнена, але зв’язатися з постачальником ідентифікаційної інформації не вдалося. Перевірте конфігурацію OAuth.", + "token_exchange_failed": "Пароль і код прийнято, але поштовий сервер відмовився відкрити сеанс для цього застосунку. Попросіть адміністратора перевірити налаштування OAuth-клієнта." + }, + "show_password": "Показати пароль", + "hide_password": "Приховати пароль", + "totp_toggle": "У мене є код 2FA", + "token_toggle": "Увійти за допомогою токена доступу", + "password_toggle": "Увійти за допомогою пароля", + "token_label": "Токен доступу", + "token_placeholder": "Вставте свій API-токен", + "token_hint": "Створіть API-токен у налаштуваннях свого поштового провайдера та вставте його сюди.", + "remember_me": "пам'ятай мене", + "config_error": { + "title": "Помилка конфігурації", + "fetch_failed": "Не вдалося завантажити конфігурацію програми. Спробуйте пізніше.", + "server_not_configured": "Поштовий сервер не налаштовано. Будь ласка, зверніться до свого адміністратора." + }, + "remove_from_history": "Видалити з історії", + "totp_label": "Код автентифікації", + "totp_placeholder": "000000", + "session_expired": "Ваш сеанс закінчився. Увійдіть знову.", + "dismiss": "Відхилити", + "or": "або", + "sign_in_sso": "Увійдіть за допомогою SSO", + "add_account_title": "Додати обліковий запис", + "add_account_subtitle": "Увійдіть за допомогою іншого облікового запису", + "cancel": "Скасувати", + "website": "Веб-сайт", + "imprint": "Вихідні дані", + "privacy_policy": "Політика конфіденційності", + "try_demo": "Спробуйте демо", + "demo_description": "Досліджуйте за зразками даних - обліковий запис не потрібен", + "demo_launching": "Запуск демо...", + "demo_login_button": "Запустити демонстрацію", + "demo_tagline": "Спробуйте повнофункціональний поштовий клієнт. Обліковий запис не потрібен.", + "demo_no_signup": "Реєстрація не потрібна - вільно досліджуйте з зразками даних", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "Завершення входу...", + "oauth_error": { + "title": "Помилка автентифікації", + "invalid_state": "Помилка перевірки безпеки. Спробуйте ввійти знову.", + "missing_params": "Відсутні дані авторизації. Спробуйте ввійти знову.", + "token_exchange_failed": "Не вдалося завершити автентифікацію. Спробуйте ще раз.", + "access_denied": "У доступі відмовлено. Будь ласка, зверніться до свого адміністратора.", + "back_to_login": "Назад до входу" + } + }, + "sidebar": { + "close": "Закрити", + "compose": "Скласти", + "compose_hint": "Скласти (с)", + "search_placeholder": "Шукати пошту...", + "search_placeholder_hint": "Пошук пошти... (натисніть /)", + "storage": "Зберігання", + "storage_used": "б/в", + "storage_free": "безкоштовно", + "storage_total": "Всього", + "sign_out": "Вийти", + "sign_out_of": "Вийти з {account}", + "sign_out_all": "Вийти з усіх облікових записів", + "add_account": "Додати акаунт", + "set_as_default": "Встановити за умовчанням", + "switch_account": "Змінити обліковий запис", + "contacts": "Контакти", + "calendar": "Календар", + "settings": "Налаштування", + "admin": "адмін", + "files": "Файли", + "loading_mailboxes": "Завантаження поштових скриньок...", + "push_connected": "Активні оновлення в реальному часі", + "push_disconnected": "Оновлення в реальному часі неактивні", + "keyboard_shortcuts": "Комбінації клавіш", + "theme": { + "light": "Світловий режим", + "dark": "Темний режим", + "system": "Тема системи" + }, + "language": { + "title": "Мова" + }, + "mailboxes": { + "inbox": "Вхідні", + "sent": "Надіслано", + "drafts": "Чернетки", + "trash": "сміття", + "archive": "Архів", + "starred": "Позначено зірочкою", + "all_mail": "Вся пошта", + "spam": "Спам", + "important": "важливо" + }, + "unified_inbox": "Спільні вхідні", + "unified_sent": "Усі надіслані", + "unified_drafts": "Усі чернетки", + "unified_trash": "Усі кошики", + "unified_archive": "Усі архіви", + "unified_junk": "Весь спам", + "all_accounts": "Усі облікові записи", + "all_accounts_loading": "Завантаження облікових записів: {loaded} з {total}", + "unified_mailbox": "Спільна поштова скринька", + "expand": "Розгорнути", + "collapse": "Згорнути", + "expand_tooltip": "Розгорнути", + "collapse_tooltip": "Згорнути", + "mobile": { + "search": "Пошук", + "compose": "Скласти", + "go_back": "Іди назад", + "toggle_menu": "Перемкнути меню" + }, + "clear_search": "Очистити пошук", + "vacation_active": "Автовідповідач активний", + "demo_banner": "Демонстраційний режим", + "demo_reset": "Скинути", + "demo_tour": "Тур", + "tags": "Теги", + "show_all_tags": "Показати всі ({count})", + "show_fewer_tags": "Показати менше", + "folders": "Папки", + "your_folders": "Ваші теки", + "create_folder": "Створити теку", + "show_more": "Більше", + "show_fewer": "Менше", + "mail": "Пошта", + "nav_label": "Навігація", + "add_app": "програми", + "shared": "Спільні", + "shared_account_hidden": "Приховано з бічної панелі. Повернути можна в Налаштування › Макет.", + "notifications_muted": "Сповіщення вимкнено", + "scheduled": "Заплановано", + "unified_all_unread": "Усі непрочитані", + "unified_all_starred": "Усі позначені", + "unified_all_mail": "Уся пошта", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "Мітки", + "create_label": "Створити мітку", + "mv_tasks": "Завдання", + "mv_side_panel": "Бічна панель", + "mv_open_full": "Відкрити повністю", + "mv_previous_day": "Попередній день", + "mv_next_day": "Наступний день", + "mv_add_another_account": "Додати інший обліковий запис", + "mv_manage_account": "Керувати обліковим записом", + "mv_storage_used": "Використано {percent}% з {total}" + }, + "protocol_handlers": { + "title": "Програми за замовчуванням", + "description": "Виберіть, чи відкривати посилання електронної пошти та календаря в Bulwark. Технічно Bulwark реєструється як обробник протоколу для посилань mailto: і webcal:.", + "unsupported": "Цей браузер або це з'єднання не підтримує ручну реєстрацію обробників протоколів. Можливо, встановлену PWA все одно можна використати через налаштування браузера або системи.", + "mailto_label": "Посилання електронної пошти", + "mailto_description": "Відкриває посилання mailto: у Bulwark із попередньо заповненим редактором листа.", + "protocol_open_mode_label": "Під час відкриття посилань протоколів", + "protocol_open_mode_description": "Виберіть, чи Bulwark має відкривати посилання mailto: і webcal: у новій вкладці, чи повторно використовувати відкритий сеанс. Для варіанта активного сеансу потрібен дозвіл на сповіщення, щоб ви могли натиснути резервне сповіщення й вивести Bulwark на передній план, якщо браузер блокує фокус.", + "protocol_open_mode_active_session": "Якщо можливо, відкривати в активному сеансі", + "protocol_open_mode_new_tab": "Завжди відкривати нову вкладку", + "focus_notification_title": "Відкрити Bulwark", + "focus_notification_body": "Посилання було відкрито в Bulwark. Натисніть, щоб вивести вікно на передній план.", + "webcal_label": "Посилання календаря", + "webcal_description": "Відкриває посилання webcal: у Bulwark із попередньо заповненим діалогом підписки на календар.", + "register_mailto": "Зареєструвати поштову програму", + "register_webcal": "Зареєструвати програму календаря", + "mailto_registered": "Реєстрацію обробника електронної пошти запитано", + "webcal_registered": "Реєстрацію обробника календаря запитано", + "registration_failed": "Не вдалося зареєструвати обробник протоколу", + "opening_mailto": "Відкриття редактора...", + "opening_webcal": "Відкриття календаря...", + "browser_note": "Браузер або операційна система може попросити підтвердження та може вимагати, щоб Bulwark був встановлений, перш ніж його можна буде вибрати програмою за замовчуванням.", + "select_account_title": "Виберіть акаунт", + "select_mailto_account": "Виберіть акаунт, у якому слід відкрити це посилання електронної пошти.", + "select_webcal_account": "Виберіть акаунт, у якому слід відкрити це посилання календаря.", + "select_account_note": "Цей вибір застосовується лише до цього посилання протоколу.", + "detail_to": "Кому", + "detail_subject": "Тема", + "detail_no_subject": "Без теми", + "detail_calendar": "Календар", + "detail_source": "Джерело", + "active_account": "Активний", + "switching_account": "Перемикання акаунта..." + }, + "sidebar_apps": { + "modal_title": "Програми бічної панелі", + "add_new": "Додати додаток", + "edit_app": "Редагувати додаток", + "name_label": "Ім'я", + "name_placeholder": "Мій додаток", + "name_required": "Необхідно вказати ім'я", + "url_label": "URL", + "url_required": "Необхідна URL-адреса", + "url_invalid": "Введіть дійсну URL-адресу http або https", + "icon_label": "значок", + "icon_required": "Потрібен значок", + "open_mode_label": "Відкритий режим", + "open_new_tab": "Нова вкладка", + "open_inline": "Вбудований", + "cancel": "Скасувати", + "add": "додати", + "update": "оновлення", + "delete": "Видалити", + "delete_confirm_title": "Видалити додаток", + "delete_confirm": "Ви впевнені, що хочете видалити \"{name}\"?", + "no_apps": "Ще не додано жодної програми", + "no_apps_hint": "Додайте власні програми та посилання на бічну панель", + "managed_title": "Надано вашим адміністратором", + "managed_description": "Ці застосунки налаштовано для всіх, і їх не можна змінити.", + "managed_badge": "Кероване", + "search_icons": "Значки пошуку...", + "show_popular": "Популярний", + "show_all": "все", + "no_icons_found": "Значок не знайдено", + "inline_badge": "Вбудований", + "tab_badge": "вкладка", + "show_on_mobile": "Показати на мобільному" + }, + "email_list": { + "columns": { + "from": "Від", + "to": "Кому", + "subject": "Тема", + "date": "Дата", + "size": "Розмір" + }, + "no_emails": "Повідомлень не знайдено", + "no_emails_description": "Ця поштова скринька порожня", + "no_search_results": "Результатів не знайдено", + "no_search_results_description": "Спробуйте налаштувати пошук або фільтри", + "loading": "Завантаження електронних листів...", + "unread": "непрочитаний", + "to_me": "мені", + "to_recipients": "Для {count} одержувачів", + "and_others": "та {count} інші", + "draft": "Чернетка", + "starred": "Позначено зірочкою", + "pinned": "Закріплено", + "replied": "Відповіли", + "forwarded": "Переслано", + "has_attachment": "З вкладенням", + "conversations_range": "{from}–{to, number} з {total, number}", + "conversations_count": "{count} з {total} розмов", + "conversations_count_plus": "{count}+ розмов", + "conversations_count_simple": "{count} розмов", + "no_conversations": "Жодних розмов", + "loading_more": "Завантаження інших електронних листів...", + "no_more_emails": "Більше не потрібно завантажувати електронні листи", + "batch_actions": { + "select": "Виберіть електронні листи", + "select_all": "Вибрати все", + "selected_messages": "Вибрано {count, plural, one {1 електронний лист} other {# електронні листи}}", + "mark_read": "Позначити як прочитане", + "mark_unread": "Позначити як непрочитане", + "delete": "Видалити", + "delete_confirm_title": "Видалити електронні листи", + "delete_confirm_message": "Ви впевнені, що хочете видалити {count, plural, one {1 email} other {# emails}}?", + "clear_selection": "Очистити вибір" + }, + "permanent_delete": "Видалити остаточно", + "permanent_delete_confirm_title": "Видалити назавжди", + "permanent_delete_confirm_message": "Цей електронний лист буде видалено назавжди. Цю дію не можна скасувати.", + "permanent_delete_confirm_batch_message": "Ці {count, plural, one {1 email} other {# emails}} буде остаточно видалено. Цю дію не можна скасувати.", + "empty_folder": { + "button": "Порожня папка", + "confirm_title": "Порожня папка", + "confirm_message": "Усі електронні листи в цій папці буде остаточно видалено. Цю дію не можна скасувати.", + "confirm_message_trash": "Усі листи з цієї папки буде переміщено в кошик.", + "confirm_button": "Порожня папка", + "junk_hint": "Ви можете очистити папку «Сміття», щоб остаточно видалити всі повідомлення.", + "trash_hint": "Ви можете очистити папку «Кошик», щоб остаточно видалити всі повідомлення." + }, + "no_scheduled_emails": "Немає запланованих листів", + "no_scheduled_emails_description": "Повідомлення, заплановані на пізніше, з’являться тут.", + "scheduled_count": "{count, plural, one {# запланований лист} few {# заплановані листи} other {# запланованих листів}}", + "scheduled_actions_hint": "Заплановані повідомлення можна скасувати, перенести або редагувати через дії планування.", + "cancel_scheduled_send": "Скасувати надсилання", + "reschedule_send": "Перенести", + "cancel_and_edit": "Скасувати й редагувати", + "cancel_and_compose_again": "Скасувати й написати знову", + "reschedule_prompt": "Введіть нову дату/час, напр. 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "Відправник запитує сповіщення про прочитання:", + "send": "Надіслати сповіщення", + "ignore": "Ігнорувати", + "sent": "Сповіщення про прочитання надіслано.", + "send_failed": "Не вдалося надіслати сповіщення про прочитання", + "mdn_subject": "Прочитано: {subject}", + "mdn_body": "Це сповіщення про прочитання повідомлення, яке ви надіслали на адресу {recipient}.\n\nПримітка: це сповіщення лише підтверджує, що повідомлення було показано на комп''ютері отримувача. Воно не гарантує, що отримувач прочитав або зрозумів вміст." + }, + "no_email_selected": "Електронна адреса не вибрана", + "no_email_description": "Виберіть електронний лист зі списку, щоб переглянути його тут", + "no_conversation_selected": "Розмова не вибрана", + "no_conversation_description": "Виберіть розмову зі списку, щоб прочитати її тут", + "compose": "Скласти", + "compose_hint": "Написати нове повідомлення", + "no_subject": "(без теми)", + "no_body_content": "(вміст відсутній)", + "no_preview_available": "Попередній перегляд недоступний", + "show_quoted_text": "Показати цитований текст", + "hide_quoted_text": "Приховати цитований текст", + "loading_email": "Завантаження електронної пошти...", + "loading": "Завантаження...", + "reply": "Відповісти", + "reply_all": "Відповісти всім", + "forward": "вперед", + "delete": "Видалити", + "archive": "Архів", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "зірка", + "unstar": "Зняти зірочку", + "mark_unread": "Позначити як непрочитане", + "mark_read": "Позначити як прочитане", + "unread": "Непрочитане", + "read": "Прочитане", + "spam_short": "Спам", + "not_spam_short": "Не спам", + "move": "Перенести", + "print": "Роздрукувати", + "view_source": "Переглянути джерело", + "fullscreen": "На весь екран", + "exit_fullscreen": "Вийти з повноекранного режиму", + "view_in_dark_mode": "Перегляд у темному режимі", + "view_in_light_mode": "Перегляд у світлому режимі", + "export_email": "Експортувати як .eml", + "forward_as_attachment": "Переслати як вкладення", + "import_email": "Імпорт .eml або .zip", + "keyboard_shortcuts": "Комбінації клавіш (?)", + "email_source": "Джерело електронної пошти", + "draft_banner": "Це повідомлення є чернеткою", + "edit_draft": "Редагувати", + "copy_source": "Копіювати в буфер обміну", + "source_copied": "Джерело скопійовано в буфер обміну", + "attachments": "Додатки", + "important": "важливо", + "download": "Завантажити", + "download_all": "Завантажити все", + "from": "Від", + "to": "до", + "cc": "CC", + "bcc": "BCC", + "date": "Дата", + "subject": "Тема", + "show_details": "Показати деталі", + "hide_details": "Приховати деталі", + "external_content_warning": "Зображення та зовнішній вміст заблоковано", + "load_external_content": "Завантажити зображення", + "trust_sender": "Завжди довіряйте цьому відправнику", + "sender_check": { + "unverified_label": "Неперевірений відправник", + "failed_label": "Перевірку відправника не пройдено", + "unverified": "Це повідомлення нібито надіслано з домену {domain}, але ніщо цього не підтверджує. Воно не має дійсного підпису домену {domain} і надійшло не з сервера, якому домен {domain} дозволяє надсилання.", + "unverified_sent_from": "Це повідомлення нібито надіслано з домену {domain}, але насправді його надіслано з домену {host}, і воно не має дійсного підпису домену {domain}.", + "failed": "Це повідомлення нібито надіслано з домену {domain}, але воно не пройшло перевірку відправника цього домену. Можливо, воно підроблене.", + "failed_sent_from": "Це повідомлення нібито надіслано з домену {domain}, але насправді його надіслано з домену {host}, і воно не пройшло перевірку відправника домену {domain}. Можливо, воно підроблене.", + "caution": "Будьте обережні з посиланнями, вкладеннями та запитами паролів чи платіжних даних." + }, + "back_to_list": "Повернутися до списку", + "view_contact": "Переглянути контакт", + "message_details": "Деталі повідомлення", + "more_reply_options": "Більше варіантів відповіді", + "set_tag": "Встановити тег", + "tag": "Тег", + "more_actions": "Більше дій", + "previous": "попередня", + "next": "Далі", + "move_to": "Перейти до...", + "remove_tag": "Видалити тег", + "tag_filter_placeholder": "Фільтр тегів", + "tag_no_matches": "Немає відповідних тегів", + "more_count": "+ ще {count}", + "characters_count": "{count} символів", + "quick_reply_placeholder": "Напишіть швидку відповідь...", + "more_options": "Більше варіантів", + "sending": "Надсилання...", + "security_authentication": "Безпека та автентифікація", + "technical_details": "Технічні деталі", + "message_id_label": "ID повідомлення:", + "reply_to_label": "Відповідь:", + "delivery_time_label": "Термін доставки:", + "conversation_part_label": "Частина розмови:", + "previous_messages": "{count} попереднє повідомлення", + "previous_messages_plural": "{count} попередніх повідомлень", + "time": { + "day": "день", + "days": "днів", + "hour": "година", + "hours": "години", + "minute": "хвилина", + "minutes": "хвилин" + }, + "unknown_sender": "Невідомий", + "recipient_me": "мене", + "recipient_and_others": "{name} та {count} інші", + "recipient_to_prefix": "до:", + "authentication": { + "title": "Аутентифікація", + "status": { + "verified": "Перевірено", + "warning": "УВАГА", + "none": "Не автентифіковано" + }, + "spf": { + "pass": "SPF пройдено", + "fail": "Збій SPF", + "none": "Без SPF" + }, + "dkim": { + "pass": "DKIM дійсний", + "fail": "Недійсний DKIM", + "none": "Без DKIM" + }, + "dmarc": { + "pass": "Перепустка DMARC", + "fail": "Помилка DMARC", + "none": "Без DMARC" + }, + "spam_score": "Оцінка спаму", + "tooltip_spf": "Sender Policy Framework: перевіряє, чи має сервер-відправник право надсилати пошту від імені домену", + "tooltip_dkim": "DomainKeys Identified Mail: підтверджує криптографічним підписом, що лист не було змінено під час пересилання", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance: забезпечує узгодженість SPF і DKIM із доменом відправника та встановлює політику для збоїв", + "policy": "Політика", + "result": { + "pass": "Пройдено", + "fail": "Не пройдено", + "softfail": "М'який збій", + "neutral": "Нейтрально", + "permerror": "Постійна помилка", + "temperror": "Тимчасова помилка", + "none": "Немає" + } + }, + "headers": { + "routing": "Маршрутизація", + "received": "Отримано", + "message_id": "ID повідомлення", + "list_info": "Інформація про список" + }, + "tooltips": { + "reply": "Відповісти (р)", + "reply_all": "Відповісти всім (а)", + "forward": "Вперед (f)", + "archive": "Архів (e)", + "delete": "Видалити (# або Del)", + "star": "зірка (и)", + "unstar": "Зняти зірочки", + "compose": "Скласти (с)", + "previous": "Попередній електронний лист", + "next": "Наступний електронний лист", + "edit_draft": "Редагувати чернетку" + }, + "spam": { + "button_title": "Повідомити про спам", + "not_spam_title": "Позначити як законне", + "toast_success": "Перенесено в сміття", + "toast_batch": "{count} електронних листів переміщено до спаму", + "toast_undo": "Скасувати", + "toast_not_spam_success": "Переміщено до папки \"Вхідні\".", + "toast_not_spam_batch": "{count} електронних листів переміщено до папки \"Вхідні\".", + "error": "Не вдалося повідомити про спам", + "error_not_spam": "Не вдалося відновити електронну пошту" + }, + "unsubscribe_banner": { + "label": "Інформаційний бюлетень", + "button": "Відписатися", + "confirm_title": "Скасувати підписку на цього відправника?", + "confirm_button": "Підтвердити", + "cancel": "Скасувати", + "success_http": "Сторінка скасування підписки відкрилася в новій вкладці", + "success_mailto": "Лист для відписки надіслано", + "confirm_message_http": "Сторінка відписки відкриється в новій вкладці.", + "confirm_message_mailto": "Відправнику буде надіслано лист для відписки.", + "error": "Неможливо скасувати підписку", + "dismiss": "Відхилити" + }, + "calendar_invitation": { + "loading": "Завантаження деталей події…", + "title": "Календар запрошення", + "published_title": "Опублікована подія", + "response_title": "Реакція на подію", + "update_title": "Оновлення події", + "counter_title": "Зустрічна пропозиція", + "refresh_title": "Запит на оновлення", + "declined_counter_title": "Зустрічна пропозиція відхилена", + "cancelled_title": "Подію скасовано", + "organizer": "Організовано {name}", + "organizer_label": "Організовано", + "attendees": "{count, plural, one {# учасник} few {# учасники} many {# учасників} other {# учасників}}", + "accept": "прийняти", + "maybe": "можливо", + "decline": "відхилити", + "add_to_calendar": "Додати в календар", + "added": "Додано до календаря", + "rsvp_sent": "Відповідь надіслано", + "parse_error": "Не вдалося прочитати запрошення", + "action_failed": "Не вдалося виконати цю дію календаря.", + "no_calendar": "Календар недоступний", + "published_info": "Ця подія була опублікована для ознайомлення.", + "response_info": "Це повідомлення містить відповідь учасника.", + "response_info_organizer": "Ця відповідь учасника оновлює вашу подію.", + "update_info": "Це повідомлення оновлює наявну подію.", + "counter_info": "Це повідомлення пропонує зміни до події.", + "counter_info_organizer": "Цей учасник запропонував зміни до вашої події.", + "refresh_info": "У цьому повідомленні запитуються останні деталі події.", + "refresh_info_organizer": "Учасник попросив останні подробиці події.", + "declined_counter_info": "Організатор відхилив зустрічну пропозицію.", + "authentication_failed_info": "Не вдалося перевірити автентифікацію пошти для цього запрошення. Ставтеся до дій календаря з обережністю.", + "authentication_missing_info": "Це запрошення не включає перевірену автентифікацію електронної пошти. Уточнюйте деталі в організатора, якщо щось виглядає незвичним.", + "sender_mismatch_info": "Це запрошення надіслано з {sender}, а організатор, зазначений у даних календаря, {organizer}.", + "sender_mismatch_unverified_info": "Це запрошення було надіслано з {sender}, а організатором, указаним у даних календаря, є {organizer}, і повідомлення не вдалося перевірити.", + "organizer_role": "Ви організовуєте цю подію", + "your_response": "Ваша відповідь: {status}", + "response_needed": "Потребує відповіді", + "response_accepted": "прийнято", + "response_tentative": "Орієнтовний", + "response_declined": "Відхилено", + "response_delegated": "Делегований", + "actor_sent_info": "Надіслано {name}.", + "actor_response_info": "{name} відповів {status}.", + "actor_counter_info": "{name} запропонував зміни до цієї події.", + "actor_refresh_info": "{name} запитав останні деталі події.", + "actor_declined_counter_info": "{name} відхилив зустрічну пропозицію.", + "actor_note": "Примітка: {comment}", + "actor_unknown": "Хтось", + "proposed_changes": "Пропоновані зміни", + "change_title": "Назва", + "change_time": "час", + "change_location": "Розташування", + "change_description": "опис", + "change_empty": "Жодного", + "change_from_to": "{before} -> {after}", + "apply_proposal": "Застосувати запропоновані зміни", + "proposal_applied": "Запропоновані зміни застосовано.", + "review_proposal": "Перегляньте пропозицію", + "review_request": "Запит на перегляд", + "view_in_calendar": "Переглянути в календарі", + "select_calendar": "Виберіть календар", + "already_in_calendar": "Уже у вашому календарі", + "request_info": "Вас запросили на цю подію. Дайте відповідь, щоб повідомити організатора про вашу доступність.", + "cancel_info": "Організатор скасував цю подію.", + "event_updated": "Оновлення №{sequence}", + "event_status_tentative": "Орієнтовний", + "event_status_cancelled": "Скасовано", + "collapse": "Сховати деталі", + "expand": "Показати деталі" + }, + "send": "Надіслати", + "more": "більше", + "details": { + "recipients_routing": "Одержувачі та маршрутизація", + "authentication_security": "Аутентифікація та безпека", + "identifiers_threading": "Ідентифікатори та обговорення", + "mailing_list": "Список розсилки", + "message_properties": "Властивості повідомлення", + "sent": "Надіслано", + "received": "Отримано", + "delivery_time": "Час доставки", + "in_reply_to": "У відповідь на", + "references": "Посилання", + "thread_id": "ID обговорення", + "size": "Розмір", + "mime_type": "Тип MIME", + "attachments_summary": "{count} файлів · {size}", + "list_id": "ID списку", + "list_help": "Довідка списку", + "list_post": "Публікація у список", + "list_unsubscribe": "Відписатися", + "iprev": "Зворотний DNS", + "spam_status": "Статус спаму", + "ai_verdict": "Висновок ШІ", + "account": "Обліковий запис", + "no_subject": "(без теми)" + }, + "scheduled_banner": "Заплановано на надсилання {date}", + "scheduled_send_created": "Лист заплановано до надсилання", + "sending_in": "Надсилання через {seconds} с", + "undo_send_failed": "Не вдалося скасувати. Можливо, повідомлення вже надіслано.", + "cancel_scheduled_send": "Скасувати надсилання", + "reschedule_send": "Перенести", + "cancel_and_edit": "Скасувати й редагувати", + "cancel_and_compose_again": "Скасувати й написати знову", + "reschedule_prompt": "Введіть нову дату/час, напр. 2026-05-04T15:30", + "scheduled_actions_only": "У поданні Заплановано доступні лише дії планування", + "undo_send_scheduled": "Повідомлення заплановано до надсилання", + "undo_send": "Скасувати надсилання", + "contact_sidebar": { + "title": "Контакт", + "close": "Закрити бічну панель", + "action_email": "Лист", + "action_email_title": "Надіслати лист", + "action_copy": "Копіювати", + "action_copy_title": "Копіювати адресу", + "action_edit_title": "Редагувати контакт", + "section_emails": "Електронні адреси", + "section_phones": "Телефони", + "section_organizations": "Організації", + "section_addresses": "Адреси", + "section_notes": "Нотатки", + "not_in_contacts": "Немає у ваших контактах", + "add_to_contacts": "Додати до контактів", + "copied": "Скопійовано!", + "copy_failed": "Не вдалося скопіювати", + "view_contact": "Переглянути контакт", + "view_details": "Докладніше" + }, + "send_now": "Надіслати зараз", + "verification_code": { + "copy": "Скопіювати код {code}", + "copied": "Код скопійовано", + "copy_failed": "Не вдалося скопіювати код" + }, + "mv_position": "{index} з {total}", + "plugin_panel_show": "Показати панель плагіна", + "plugin_panel_hide": "Сховати панель плагіна" + }, + "email_composer": { + "select_from_directory": "Вибрати з довідника", + "minimize": "Згорнути", + "maximize": "На весь екран", + "restore": "Відновити", + "new_message": "Нове повідомлення", + "signature_edit_hint": "Двічі клацніть, щоб редагувати підпис", + "reply": "Відповісти", + "reply_all": "Відповісти всім", + "forward": "вперед", + "reply_to": "Відповісти", + "reply_all_to": "Відповісти всім", + "forward_message": "вперед", + "from": "Від", + "to": "до", + "cc": "CC", + "bcc": "BCC", + "subject": "Тема", + "body_placeholder": "Напишіть своє повідомлення...", + "send": "Надіслати", + "cancel": "Скасувати", + "attach": "Прикріпити", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "Прикріпити з Файлів", + "files_picker": { + "attach": "{count, plural, =0 {Прикріпити} one {Прикріпити # файл} few {Прикріпити # файли} many {Прикріпити # файлів} other {Прикріпити # файлу}}", + "empty_folder": "Ця папка порожня", + "load_failed": "Не вдалося завантажити ваші файли" + }, + "discard": "Відкинути", + "discard_draft_title": "Відхилити чернетку?", + "discard_draft_confirm": "У вас є незбережені зміни. Ви хочете відхилити цю чернетку?", + "discard_draft_message": "Чернетку буде видалено.", + "saving": "Збереження...", + "draft_saved": "Чернетку збережено", + "save_failed": "Не вдалося зберегти", + "to_placeholder": "Адреси електронної пошти одержувачів", + "cc_placeholder": "Одержувачі копії", + "bcc_placeholder": "Одержувачі прихованої копії", + "subject_placeholder": "Тема", + "cc_label": "Копія:", + "bcc_label": "Прихована копія:", + "subject_label": "Тема:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count, plural, one {1 лист} few {# листи} many {# листів} other {# листів}}", + "no_subject": "(без теми)", + "unknown_sender": "Невідомий", + "quote": { + "reply_header": "{date}, {sender} написав:", + "forward_header": "---------- Переслане повідомлення ----------", + "from": "Від: {sender}", + "date": "Дата: {date}", + "subject": "Тема: {subject}", + "to": "Кому: {recipients}" + }, + "remove_sub_address": "Видалити підадресу", + "from_override": { + "toggle_off": "Замінити", + "toggle_on": "Скасувати заміну", + "toggle_tooltip": "Вільно редагуйте ім'я та адресу відправника. Пошта все ще надсилається через вашу ідентичність.", + "name_label": "Ім'я відправника", + "name_placeholder": "Ім'я", + "email_label": "Електронна адреса відправника", + "email_placeholder": "alias@example.com", + "envelope_notice": "Якщо ваш сервер не приймає цю адресу як відправника конверта, замість неї використовується {identity}, і одержувачі бачитимуть її в заголовку Return-Path." + }, + "save_as_template": "Зберегти як шаблон", + "attach_from_device": "Завантажити з цього пристрою", + "templates_menu": "Шаблони", + "insert_template": "Вставити шаблон", + "options": "Параметри", + "option_plain_text": "Звичайний текст", + "option_plain_text_hint": "Надіслати без форматування", + "option_read_receipt": "Запросити сповіщення про прочитання", + "option_read_receipt_hint": "Попросити одержувача підтвердити відкриття", + "option_dsn": "Сповіщення про доставку", + "option_dsn_hint": "Отримати звіт, коли сервер доставить лист", + "option_require_tls": "Вимагати шифровану доставку", + "option_require_tls_hint": "Доставляти лише через TLS, інакше повернути", + "option_priority": "Важливість", + "option_priority_high": "Висока", + "option_priority_normal": "Звичайна", + "option_priority_low": "Низька", + "validation": { + "recipient_required": "Додайте одержувача для надсилання", + "body_required": "Напишіть повідомлення або прикріпіть файл", + "invalid_recipients": "Не надіслано - перевірте ці адреси: {addresses}", + "attachments_uploading": "Вкладення ще завантажуються — надсилання почнеться після завершення", + "attachment_upload_failed": "Не надіслано — не вдалося завантажити вкладення. Видаліть його та спробуйте ще раз." + }, + "upload_progress": "Завантаження {uploaded} / {total}", + "upload_cancel": "Скасувати завантаження", + "upload_failed": "Не вдалося завантажити {filename}", + "attachment_too_large": "«{name}» більший, ніж дозволяє сервер ({max} на файл)", + "attachments_total_too_large": "Вкладення перевищать ліміт сервера {max} на лист", + "drop_files": "Перетягніть файли, щоб прикріпити", + "show_less": "Показувати менше", + "send_failed": "Не вдалося надіслати електронний лист", + "send_timeout": "Сервер перестав відповідати. Перш ніж надсилати знову, перевірте теку «Надіслані» — лист міг уже піти.", + "send_offline": "Схоже, ви не в мережі. Повідомлення збережено, надішліть його знову, коли з'явиться зв'язок.", + "formatting": "Форматування", + "send_recipients_rejected": "Не надіслано — сервер відхилив усіх одержувачів.", + "send_plugin_failed": "Не надіслано: плагін, який відповідає за надсилання, завершився з помилкою або працював задовго. Перевірте папку «Надіслані», перш ніж надсилати знову.", + "send_some_recipients_rejected": "Надіслано, але не цим одержувачам — сервер їх відхилив.", + "continue_draft": "Продовжити чернетку", + "close_draft_title": "Зберегти чи видалити чернетку?", + "close_draft_message": "У вас є незбережені зміни. Зберегти це як чернетку чи видалити?", + "smime_sign_on": "Підпис S/MIME увімкнено", + "smime_sign_off": "Увімкнути підпис S/MIME", + "smime_encrypt_on": "Шифрування S/MIME увімкнено", + "smime_encrypt_off": "Увімкніть шифрування S/MIME", + "smime_encrypt_unavailable": "Шифрування S/MIME недоступне – відсутні сертифікати одержувача", + "smime_unlock_title": "Розблокувати ключ S/MIME", + "smime_unlock_message": "Введіть парольну фразу, щоб розблокувати ключ підпису S/MIME.", + "smime_unlock_button": "Розблокувати", + "smime_passphrase_placeholder": "Парольна фраза", + "forgot_attachment": { + "title": "Ви забули вкладення?", + "message": "У вашому повідомленні згадується \"{keyword}\", але файл не вкладено. Все одно надіслати?", + "send_anyway": "Все одно надішліть", + "back": "Назад до редагування" + }, + "empty_subject": { + "title": "Надіслати без теми?", + "message": "Цей лист не має теми. Надіслати попри це?", + "dont_ask_again": "Більше не запитувати", + "send_anyway": "Все одно надішліть", + "back": "Назад до редагування" + }, + "add_link": "Додати посилання", + "link_url_prompt": "Введіть URL", + "sending": "Надсилання...", + "schedule_send": "Запланувати надсилання", + "schedule_send_description": "Виберіть, коли сервер має надіслати це повідомлення.", + "schedule_preset": { + "tomorrow_morning": "Завтра вранці", + "tomorrow_afternoon": "Завтра вдень", + "monday_morning": "У понеділок вранці" + }, + "schedule_send_required": "Виберіть дату й час.", + "schedule_send_invalid": "Введіть дійсні дату й час.", + "schedule_send_future": "Виберіть дату й час у майбутньому.", + "schedule_send_too_late": "Цей час пізніший, ніж дозволяє сервер.", + "schedule_send_unsupported": "Заплановане надсилання не підтримується для цього облікового запису.", + "schedule_send_cleanup_warning": "Заплановане надсилання створено, але очищення чернетки не вдалося.", + "send_delay_unsupported": "Затримка надсилання не підтримується для цього облікового запису.", + "send_delay_unsupported_confirm": "Цей обліковий запис не підтримує затримку надсилання. Надіслати негайно?", + "recipient_edit_email": "Змінити адресу ел. пошти", + "recipient_edit_name": "Змінити відображуване ім'я", + "recipient_email_placeholder": "Адреса ел. пошти", + "recipient_name_placeholder": "Відображуване ім'я", + "invalid_recipient": "Недійсна адреса електронної пошти", + "autocomplete_search_server": "Шукати на сервері", + "autocomplete_searching": "Пошук...", + "mention_recipients": "Одержувачі", + "mention_card_not_recipient": "Немає в полях «Кому» та «Копія», тож лист не отримає.", + "mention_card_add": "Додати в {field}", + "mention_card_remove": "Прибрати згадку", + "toolbar": { + "bold": "Жирний", + "italic": "Курсив", + "underline": "Підкреслений", + "strikethrough": "Закреслений", + "text_color": "Колір тексту", + "remove_color": "Прибрати колір", + "heading_1": "Заголовок 1", + "heading_2": "Заголовок 2", + "bullet_list": "Маркований список", + "ordered_list": "Нумерований список", + "quote": "Цитата", + "code_block": "Блок коду", + "align_left": "По лівому краю", + "align_center": "По центру", + "align_right": "По правому краю", + "text_direction": "Напрямок тексту (RTL/LTR)", + "link": "Посилання", + "table": "Таблиця", + "clear_formatting": "Очистити форматування", + "undo": "Скасувати", + "redo": "Повторити", + "add_row_above": "Вставити рядок вище", + "add_row_below": "Вставити рядок нижче", + "add_column_before": "Вставити стовпець ліворуч", + "add_column_after": "Вставити стовпець праворуч", + "delete_row": "Видалити рядок", + "delete_column": "Видалити стовпець", + "toggle_header_row": "Переключити рядок заголовка", + "delete_table": "Видалити таблицю", + "pick_size": "Вибрати розмір", + "font_size": "Розмір шрифту", + "font_size_default": "За замовчуванням", + "background_color": "Колір фону", + "remove_background_color": "Прибрати колір фону" + }, + "send_filing_warning": "Надіслано - але подальше очищення не вдалося, може залишитися застаріла чернетка.", + "pop_out": "Відкрити відповідь в окремому вікні", + "send_guard_failed": "Не надіслано: плагін, що перевіряє листи перед надсиланням, завершився з помилкою або не відповів." + }, + "confirm_dialog": { + "confirm": "Підтвердити", + "cancel": "Скасувати" + }, + "common": { + "loading": "Завантаження...", + "error": "Помилка", + "success": "Успіх", + "cancel": "Скасувати", + "save": "зберегти", + "delete": "Видалити", + "edit": "Редагувати", + "close": "Закрити", + "search": "Пошук", + "refresh": "Оновити", + "settings": "Налаштування", + "help": "Довідка", + "logout": "Вийти", + "yes": "так", + "no": "немає", + "unknown": "Невідомий", + "app_title": "Веб-пошта", + "reconnecting": "З'єднання втрачено. Спроба повторного підключення…", + "rate_limited_title": "Швидкість автентифікації сервера тимчасово обмежена.", + "rate_limited_detail": "Bulwark призупинив фонові запити, щоб уникнути блокування. Повторна спроба через {seconds} с.", + "rate_limited_action_title": "Запит призупинено, щоб уникнути блокування.", + "rate_limited_action_detail": "Bulwark чекає закінчення часу відновлення сервера, перш ніж надсилати додаткові автентифіковані запити. Повторіть спробу через {seconds} с.", + "pull_to_refresh": "Потягніть, щоб оновити", + "release_to_refresh": "Відпустіть, щоб оновити", + "refreshing": "Оновлення…" + }, + "notifications": { + "email_sent": "Електронна пошта успішно надіслана", + "email_deleted": "Електронну пошту видалено", + "email_archived": "Електронний лист заархівовано", + "emails_deleted": "{count, plural, one {# лист видалено} few {# листи видалено} many {# листів видалено} other {# листа видалено}}", + "emails_archived": "{count, plural, one {# лист архівовано} few {# листи архівовано} many {# листів архівовано} other {# листа архівовано}}", + "error_archiving": "Не вдалося архівувати лист", + "error_updating": "Не вдалося оновити лист", + "email_starred": "Електронна пошта позначена зірочкою", + "email_unstarred": "З електронної пошти знято зірочку", + "email_marked_read": "Електронна пошта позначена як прочитана", + "email_marked_unread": "Електронна пошта позначена як непрочитана", + "copied_to_clipboard": "Скопійовано в буфер обміну", + "source_copied": "Джерело скопійовано в буфер обміну", + "error_sending": "Не вдалося надіслати електронний лист", + "error_deleting": "Не вдалося видалити електронну пошту", + "error_loading": "Не вдалося завантажити електронні листи", + "new_email": "Нова електронна пошта", + "new_email_from": "Від {sender}", + "click_to_view": "Натисніть, щоб переглянути", + "email_moved": "Електронну адресу переміщено", + "emails_moved": "{count} електронних листів переміщено", + "moved_to_mailbox": "Переміщено до {mailbox}", + "move_failed": "Не вдалося перемістити", + "removed_from_folder": "Прибрано з папки {mailbox}", + "remove_from_folder_failed": "Не вдалося прибрати з цієї папки", + "emails_copied": "{count} електронних листів скопійовано", + "copy_failed": "Не вдалося скопіювати", + "move_error": "Не вдалося перемістити електронні листи до вибраної папки", + "email_tagged": "Електронна пошта позначена тегами", + "emails_tagged": "{count} електронних листів позначено тегами", + "tag_failed": "Помилка тегування", + "identity_created": "Ідентичність створено успішно", + "identity_updated": "Ідентичність успішно оновлено", + "identity_deleted": "Ідентичність видалено", + "identity_set_primary": "Основну особу оновлено", + "identity_create_failed": "Не вдалося створити ідентифікатор: {error}", + "identity_update_failed": "Не вдалося оновити ідентифікатор: {error}", + "identity_delete_failed": "Не вдалося видалити особу: {error}", + "identity_unauthorized": "Ви не авторизовані надсилати з цієї електронної адреси", + "identity_not_found": "Особа не знайдена", + "vacation_saved": "Налаштування відсутності збережено", + "vacation_save_failed": "Не вдалося зберегти налаштування відсутності", + "filters_saved": "Фільтри успішно збережено", + "filters_save_failed": "Не вдалося зберегти фільтри", + "filters_deleted": "Правило фільтра видалено", + "templates_exported": "Шаблони успішно експортовано", + "templates_imported": "{count, plural, one {# template} other {# templates}} імпортовано", + "templates_import_errors": "Деякі шаблони не вдалося імпортувати", + "templates_import_empty": "У файлі не знайдено шаблонів", + "export_email_error": "Не вдалося експортувати електронну пошту", + "import_email_success": "Електронну пошту успішно імпортовано", + "import_email_error": "Не вдалося імпортувати електронну пошту", + "rule_created": "Правило створено", + "rule_merged": "Додано до правила «{name}»", + "rule_already_covered": "Уже охоплено правилом «{name}»", + "rule_apply_existing": "{count, plural, one {Застосувати до # наявного листа} few {Застосувати до # наявних листів} many {Застосувати до # наявних листів} other {Застосувати до # наявного листа}}", + "rule_applied": "{count, plural, =0 {Жоден лист не потребував змін} one {Правило застосовано до # листа} few {Правило застосовано до # листів} many {Правило застосовано до # листів} other {Правило застосовано до # листа}}", + "rule_apply_failed": "Не вдалося застосувати правило до наявних листів", + "rule_edit": "Редагувати правило", + "rule_undo": "Скасувати", + "rule_undone": "Зміну правила скасовано", + "rule_undo_failed": "Не вдалося скасувати зміну правила", + "rule_undo_conflict": "Ваші фільтри відтоді змінилися. Скасуйте зміну в налаштуваннях фільтрів.", + "rule_folder_failed": "Не вдалося створити папку", + "vacation_filters_save_failed": "Налаштування відсутності збережено, але пересилання та отримувачів відповіді зберегти не вдалося" + }, + "date": { + "today": "Сьогодні", + "yesterday": "вчора", + "this_week": "Цього тижня", + "last_week": "Минулого тижня", + "this_month": "Цей місяць", + "older": "Старший", + "just_now": "Просто зараз", + "minutes_ago": "{count} хвилин тому", + "minutes_ago_plural": "{count} хвилин тому", + "hours_ago": "{count} годин тому", + "hours_ago_plural": "{count} годин тому", + "days_ago": "{count} день тому", + "days_ago_plural": "{count} днів тому" + }, + "language": { + "title": "мова", + "english": "англійська", + "french": "Français", + "japanese": "日本語", + "spanish": "Español", + "italian": "Italiano", + "german": "Deutsch", + "dutch": "Нідерланди", + "portuguese": "Português", + "russian": "Русский", + "select_language": "Виберіть мову", + "switch_to_english": "Переключитися на англійську", + "switch_to_french": "Перейти на французьку", + "switch_to_japanese": "Перейти на японську", + "switch_to_spanish": "Перейти на іспанську", + "switch_to_italian": "Перейти на італійську", + "switch_to_german": "Перейти на німецьку", + "switch_to_dutch": "Перейти на голландську", + "switch_to_portuguese": "Переключитися на португальську", + "switch_to_russian": "Перейти на російську", + "switching": "Зміна мови...", + "switch": "Змінити мову", + "current": "Поточна мова", + "en": "англійська", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Нідерланди", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "polish": "Polski", + "switch_to_polish": "Перемкнути на польську", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "Перейти на фарсі", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "Перейти на монгольську" + }, + "settings": { + "title": "Налаштування", + "back_to_mail": "Назад до пошти", + "save_success": "Налаштування успішно збережено", + "import_success": "Налаштування успішно імпортовано", + "import_error": "Не вдалося імпортувати налаштування", + "reset_confirm": "Ви впевнені, що бажаєте скинути всі налаштування до стандартних?", + "unsaved_changes": "У вас є незбережені зміни", + "discard_changes": "Скасувати незбережені зміни?", + "discard": "Відкинути", + "keep_editing": "Продовжуйте редагувати", + "tabs": { + "appearance": "Зовнішній вигляд", + "language": "Мова, регіон і час", + "email": "Поведінка електронної пошти", + "composer": "Редактор", + "privacy": "Конфіденційність і безпека", + "account": "Обліковий запис", + "identities": "Ідентичності", + "vacation": "Відсутність", + "advanced": "Просунутий", + "calendar": "Календар", + "filters": "Фільтри", + "templates": "Шаблони", + "folders": "Папки", + "keywords": "Ключові слова", + "security": "Безпека", + "files": "Файли", + "contacts": "Контакти", + "encryption": "Шифрування", + "protocol_handlers": "Програми за замовчуванням", + "sidebar_apps": "Програми бічної панелі", + "notifications": "Сповіщення", + "layout": "Макет", + "themes": "Теми", + "reading": "Читання", + "composing": "Написання", + "downloads": "Завантаження", + "content_senders": "Вміст і відправники", + "about_data": "Про програму та дані", + "debug": "Налагодження" + }, + "themes": { + "title": "Теми", + "description": "Виберіть тему, розгорнуту вашим адміністратором, або одну з вбудованих.", + "locked": "Вибір теми заблоковано адміністратором.", + "forced": "Тему «{name}» встановлено адміністратором, і її не можна змінити", + "admin_theme": "Тема адміністратора", + "activated": "Тему активовано", + "default_restored": "Тему за замовчуванням відновлено", + "default_name": "За замовчуванням", + "default_badge": "За замовчуванням", + "admin_enforced": "Встановлено адміністратором" + }, + "tab_groups": { + "general": "Загальний", + "account": "Обліковий запис і ідентифікатор", + "organization": "Організація пошти", + "apps": "програми", + "system": "система", + "appearance": "Вигляд", + "mail": "Пошта", + "privacy": "Конфіденційність і безпека", + "advanced": "Додатково" + }, + "appearance": { + "message_list_order": { + "label": "Порядок списку повідомлень", + "description": "Як упорядковано повідомлення в списку. Сортування виконує сервер, тому воно охоплює всю теку, а не лише завантажені повідомлення.", + "preset": { + "chronological": "Хронологічно (спочатку нові)", + "unread_first": "Спочатку непрочитані", + "starred_first": "Спочатку із зірочкою", + "tagged_first": "Спочатку з міткою", + "custom": "Власний" + }, + "tag_label": "Мітка", + "advanced_show": "Додатково: змінити рівні сортування", + "advanced_hide": "Сховати додаткові", + "criterion_label": "Критерій рівня {n}", + "direction_label": "Напрямок рівня {n}", + "criteria": { + "unread": "Непрочитані", + "starred": "Із зірочкою", + "tag": "Мітка", + "receivedAt": "Дата отримання", + "sentAt": "Дата надсилання", + "from": "Відправник", + "subject": "Тема", + "size": "Розмір" + }, + "direction": { + "unread_first": "Спочатку непрочитані", + "read_first": "Спочатку прочитані", + "starred_first": "Спочатку із зірочкою", + "unstarred_first": "Спочатку без зірочки", + "tagged_first": "Спочатку з міткою", + "untagged_first": "Спочатку без мітки", + "newest_first": "Спочатку нові", + "oldest_first": "Спочатку старі", + "a_to_z": "А → Я", + "z_to_a": "Я → А", + "largest_first": "Спочатку великі", + "smallest_first": "Спочатку малі" + }, + "add_level": "Додати рівень", + "remove_level": "Вилучити рівень", + "move_up": "Вище", + "move_down": "Нижче", + "tiebreak_note": "До трьох рівнів; наступні рівні вирішують за рівності. «Спочатку нові» завжди застосовується останнім.", + "scope": { + "label": "Застосувати до всіх тек", + "description": "Якщо вимкнено, порядок діє лише для теки «Вхідні»; інші теки лишаються хронологічними." + }, + "unsupported_note": "Ваш поштовий сервер не підтримує сортування за станом прочитання, зірочками чи мітками. Ці рівні ігноруються, і список лишається хронологічним." + }, + "title": "Зовнішній вигляд", + "description": "Налаштуйте зовнішній вигляд вашої веб-пошти", + "theme": { + "label": "Тема", + "description": "Виберіть бажану колірну схему", + "light": "світло", + "dark": "Темний", + "system": "система" + }, + "language": { + "label": "Мова", + "description": "Виберіть бажану мову" + }, + "font_size": { + "label": "Розмір шрифту", + "description": "Налаштуйте розмір тексту для кращої читабельності", + "small": "Маленький", + "medium": "Середній", + "large": "Великий" + }, + "list_density": { + "label": "Щільність", + "description": "Контролюйте інтервали та відступи в інтерфейсі", + "extra_compact": "Дуже компактний", + "compact": "Компактний", + "regular": "Регулярний", + "comfortable": "Комфортно" + }, + "interface_layout": { + "label": "Макет інтерфейсу", + "description": "Де розташовані пошук, навігація та панелі інструментів", + "default": "За замовчуванням", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "Увімкнути анімацію", + "description": "Показувати плавні переходи та ефекти" + }, + "toolbar_position": { + "label": "Положення панелі інструментів", + "description": "Де відображати кнопки дій електронної пошти (Відповісти, Архівувати, Видалити тощо)", + "top": "Топ", + "below_subject": "Тема нижче" + }, + "toolbar_labels": { + "label": "Показати мітки панелі інструментів", + "description": "Відображати текстові мітки поруч із піктограмами панелі інструментів. Вимкніть, щоб заощадити місце, коли ви ознайомитеся з піктограмами." + }, + "hide_account_switcher": { + "label": "Приховати перемикач облікових записів на бічній панелі", + "description": "Сховайте селектор облікових записів у верхній частині бічної панелі папок. Ви все ще можете перемикати облікові записи з нижньої панелі навігації." + }, + "show_rail_account_list": { + "label": "Показувати аватари облікових записів на панелі навігації", + "description": "Відображати кола окремих облікових записів у нижній частині панелі навігації для швидкого перемикання з кнопкою виходу внизу." + }, + "unified_mailbox": { + "label": "Спільна поштова скринька", + "description": "Показувати об'єднані папки (Вхідні, Надіслані тощо) для всіх підключених облікових записів", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "Включати групові скриньки", + "description": "Також об'єднувати спільні/групові скриньки у спільному перегляді." + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "Кольорові значки бічної панелі", + "description": "Забарвлюйте значки папок і тегів за типом (синя «Вхідні», червоний «Спам», зелена «Надіслані» тощо). Вимкніть для монохромної бічної панелі." + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "Показувати загальну кількість повідомлень", + "description": "Показує загальну кількість повідомлень поруч із теками та мітками, разом із кількістю непрочитаних. Вимкніть, щоб показувати лише непрочитані." + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "Показувати спільні поштові акаунти", + "description": "Показувати папки скриньок, до яких вам надано доступ, у розділі «Спільні» на бічній панелі. Вимкніть, щоб приховати всі, або приховайте окремі акаунти нижче. Їхні календарі та контакти залишаться." + }, + "pro_interface": { + "label": "Pro-інтерфейс (експериментальний)", + "description": "Розкладка для досвідчених користувачів лише для настільного комп'ютера з переглядом повідомлень у кількох вкладках і робочими процесами між обліковими записами. Стандартний інтерфейс не змінюється; ви можете повернутися будь-коли.", + "open_label": "Відкрити Pro-інтерфейс", + "back_to_standard": "Назад до стандарту" + }, + "cross_unread": { + "label": "Усі облікові записи: Непрочитані", + "description": "Показує у розділі «Усі облікові записи» запис із непрочитаними листами з усіх облікових записів, по всіх теках, окрім спаму, надісланих, архіву, кошика та чернеток." + }, + "cross_starred": { + "label": "Усі облікові записи: Позначені", + "description": "Показує у розділі «Усі облікові записи» запис із позначеними листами з усіх облікових записів, по всіх теках, окрім спаму, надісланих, архіву, кошика та чернеток." + }, + "cross_all": { + "label": "Усі облікові записи: Уся пошта", + "description": "Показує у розділі «Усі облікові записи» запис з усіма листами з усіх облікових записів, по всіх теках, окрім спаму, надісланих, архіву, кошика та чернеток." + } + }, + "keywords": { + "title": "Ключові слова електронної пошти", + "description": "Визначте ключові слова (мітки/теги), щоб упорядкувати свої листи за кольорами. Вони зберігаються як ключові слова JMAP на сервері.", + "add_keyword": "Додати ключове слово", + "label_field": "Відображуване ім'я", + "label_placeholder": "напр. Робота, Особиста, Терміново", + "id_field": "ID ключового слова", + "id_placeholder": "напр. робота, особисте", + "color_field": "колір", + "id_exists": "Цей ідентифікатор ключового слова вже існує", + "edit": "Редагувати ключове слово", + "delete": "Видалити ключове слово", + "save": "зберегти", + "add": "додати", + "cancel": "Скасувати", + "migrating": "Оновлення ключового слова в наявних електронних листах…", + "migration_error": "Не вдалося оновити ключове слово в існуючих електронних листах", + "filters_migration_error": "Деякі правила фільтрів досі використовують старий тег — оновіть їх вручну", + "nesting": { + "label": "Вкладені теги", + "description": "Вкладайте теги в інші теги та показуйте їх на бічній панелі у вигляді дерева." + }, + "parent_field": "Батьківський тег", + "no_parent": "Без батьківського тега", + "too_long": "Цей шлях тега задовгий (щонайбільше {max} символів)", + "has_children_locked": "У цей тег вкладено інші теги, тому його назву та батьківський тег заблоковано. Спочатку перемістіть або видаліть їх.", + "has_children_delete": "Спочатку видаліть теги, вкладені в цей", + "visibility_field": "Видимість на бічній панелі", + "visibility": { + "show": "Показувати", + "unread": "Показувати за непрочитаних", + "hide": "Приховувати" + }, + "unrecognized": { + "title": "Відновити теги із сервера", + "description": "Назви та кольори тегів зберігаються в цьому браузері, але самі теги перебувають у ваших листах як ключові слова JMAP. Проскануйте пошту на ключові слова, для яких тут немає тега, і додайте їх назад.", + "scan": "Знайти теги", + "rescan": "Сканувати ще раз", + "scanning": "Сканування… {scanned} з {total} листів", + "none": "Усі теги у ваших листах уже визначені тут.", + "messages": "{count, plural, one {# лист} few {# листи} other {# листів}}", + "add_all": "Додати всі ({count})", + "partial": "Проскановано лише {scanned} найновіших листів із {total}. Теги, використані лише в старіших листах, можуть бути відсутні.", + "error": "Не вдалося просканувати пошту на наявність тегів." + } + }, + "notifications": { + "test_sound": "Тестовий звук сповіщення", + "sounds": { + "default": "За замовчуванням (звуковий сигнал)", + "cheerful": "Веселий", + "involved": "Залучений", + "swift": "Швидкий жест", + "relax": "Розслабтеся" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "Звук сповіщення", + "description": "Виберіть звук для сповіщень", + "choose": "Звук", + "choose_desc": "Виберіть сигнал сповіщення та натисніть значок динаміка, щоб переглянути його" + }, + "email": { + "title": "Сповіщення електронною поштою", + "description": "Налаштувати сповіщення для вхідних електронних листів", + "enabled": "Сповіщення електронною поштою", + "enabled_desc": "Показувати сповіщення про надходження нових електронних листів", + "sound": "Звук сповіщення", + "sound_desc": "Відтворення звукового сповіщення про надходження нових електронних листів", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "Сповіщення календаря", + "description": "Налаштувати сповіщення про події календаря", + "enabled": "Сповіщення про події", + "enabled_desc": "Показувати сповіщення про майбутні події календаря", + "sound": "Звук сповіщення", + "sound_desc": "Відтворення звукового сповіщення для нагадувань календаря", + "invitation_parsing": "Проаналізуйте запрошення електронною поштою", + "invitation_parsing_desc": "Виявляти запрошення календаря у вкладеннях електронної пошти та показувати дії календаря" + }, + "push": { + "confirm_disable_message": "Цей пристрій перестане отримувати сповіщення, коли сайт закритий.", + "confirm_disable_title": "Вимкнути фонові сповіщення?", + "description": "Отримуйте системні сповіщення про нові листи, коли цей сайт закритий. Доставляється через push-реле Bulwark; реле ніколи не бачить вміст листів.", + "disable": "Вимкнути", + "enable": "Увімкнути", + "dismiss_aria": "Закрити запит на сповіщення", + "ios_hint": "На iOS спочатку встановіть сайт на головний екран – Safari доставляє Web Push лише встановленим PWA.", + "reenable": "Перереєструвати", + "relay_desc": "Типово використовується розміщене реле Bulwark. Змінюйте лише, якщо хостите самостійно.", + "relay_label": "Push-реле", + "relay_locked": "Встановлено адміністратором", + "relay_locked_desc": "URL push-реле встановлено вашим адміністратором і його не можна змінити.", + "status_active": "Активно на цьому пристрої", + "status_busy": "Виконується…", + "status_inactive": "Не ввімкнено на цьому пристрої", + "status_unsupported": "Цей браузер не підтримує Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "Фонові сповіщення", + "devices_title": "Зареєстровані пристрої", + "devices_desc": "Усі пристрої, зареєстровані для отримання фонових сповіщень цього облікового запису. Після відкликання пристрій одразу перестає їх отримувати.", + "devices_loading": "Завантаження зареєстрованих пристроїв…", + "devices_empty": "Для фонових сповіщень не зареєстровано жодного пристрою.", + "devices_error": "Не вдалося завантажити зареєстровані пристрої.", + "devices_refresh": "Оновити", + "device_this": "Цей пристрій", + "device_other": "Інший пристрій", + "device_status_active": "Доставляє сповіщення", + "device_status_inactive": "Жодного разу не доставляв сповіщень", + "device_status_unknown": "Невідомий цьому ретранслятору", + "device_expires": "Спливає {date}", + "revoke": "Відкликати", + "confirm_revoke_title": "Відкликати цей пристрій?", + "confirm_revoke_message": "Він одразу перестане отримувати фонові сповіщення. Якщо знову увімкнути push на цьому пристрої, він зареєструється заново.", + "confirm_revoke_message_this": "Цей пристрій одразу перестане отримувати фонові сповіщення." + } + }, + "language_region": { + "title": "Мова, регіон і час", + "description": "Мова, формат дати, формат часу та інші регіональні налаштування", + "language": { + "label": "Мова", + "description": "Виберіть бажану мову", + "english": "англійська", + "french": "Français" + }, + "date_format": { + "label": "Формат дати", + "description": "Як відображаються дати у списку листів", + "smart": "Розумний", + "relative": "Відносний", + "full": "Завжди повна дата", + "preview_today": "Сьогодні:", + "preview_this_week": "Цього тижня:", + "preview_older": "Старіше:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "Формат часу", + "description": "Виберіть 12-годинний або 24-годинний формат годинника", + "12h": "12-годинний", + "24h": "24 години" + }, + "first_day": { + "label": "Перший день тижня", + "description": "Почніть тиждень у неділю чи понеділок", + "sunday": "неділя", + "monday": "понеділок" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "Очищати пошук під час перемикання тек", + "description": "Клацання теки («Вхідні», «Надіслані»…) очищає активний пошук і відкриває цю теку. Якщо вимкнено, пошук лишається й повторюється у вибраній теці." + }, + "request_read_receipt": { + "label": "Запитувати сповіщення про прочитання за замовчуванням", + "description": "Заздалегідь вмикати запит сповіщення про прочитання під час створення нового повідомлення." + }, + "read_receipt_response": { + "label": "Відповідати на запити сповіщення про прочитання", + "description": "Що робити, коли вхідне повідомлення запитує сповіщення про прочитання.", + "ask": "Запитувати щоразу", + "always": "Завжди надсилати", + "never": "Ніколи не надсилати" + }, + "title": "Поведінка електронної пошти", + "description": "Налаштувати спосіб обробки електронних листів", + "mark_read": { + "label": "Позначити як прочитане", + "description": "Коли позначати листи як прочитані під час відкриття", + "instant": "Миттєво", + "delay_3s": "Через 3 секунди", + "delay_5s": "Через 5 секунд", + "never": "Ніколи" + }, + "delete_action": { + "label": "Видалити дію", + "description": "Що відбувається, коли ви видаляєте електронний лист", + "trash": "Перемістити в кошик", + "trash_and_read": "Перемістити в кошик і позначити як прочитане", + "permanent": "Видалити назавжди", + "warning": "Електронні листи буде видалено назавжди, і їх неможливо буде відновити. Ця дія незворотна." + }, + "message_spacing": { + "label": "Відступи повідомлення", + "description": "Відступи навколо тексту повідомлення під час читання", + "auto": "Автоматично (рекомендовано)", + "always": "Завжди додавати відступи", + "edge": "Від краю до краю" + }, + "plain_text_font": { + "label": "Шрифт звичайного тексту", + "description": "Шрифт для листів у звичайному тексті", + "mono": "Моноширинний", + "sans": "Звичайний (як у застосунку)" + }, + "archive_mode": { + "label": "Архівувати в", + "description": "Як упорядкувати листи під час архівування", + "single": "Одна папка", + "year": "Папка на рік", + "month": "Папка на місяць", + "reorganize": "Реорганізувати існуючий архів", + "reorganize_success": "{count, plural, =0 {Немає листів для реорганізації} =1 {1 лист реорганізовано} other {# листів реорганізовано}}", + "reorganize_error": "Не вдалося реорганізувати архів" + }, + "permanently_delete_junk": { + "label": "Видалити непотріб назавжди", + "description": "Назавжди видаляйте електронні листи з папки «Сміття» замість того, щоб переміщувати їх у кошик" + }, + "confirm_bulk_delete": { + "label": "Підтверджувати видалення кількох листів", + "description": "Питати перед переміщенням кількох вибраних листів до кошика. Остаточне видалення завжди питає." + }, + "mail_layout": { + "label": "Макет пошти", + "description": "Вибирайте між класичною розділеною панеллю читання та зосередженим потоком читання у стилі Gmail.", + "split": "Розділена панель", + "split_description": "Тримайте список повідомлень і панель читання видимими поруч.", + "focus": "Сфокусований список", + "focus_description": "Показувати один рядок на повідомлення та відкривати пошту на всю ширину, зберігаючи бічну панель папки видимою.", + "horizontal": "Область читання знизу", + "horizontal_description": "Показуйте список повідомлень угорі та відкривайте вибране повідомлення в області читання під ним." + }, + "list_columns": { + "label": "Показувати список стовпцями", + "description": "Показує однорядкові рядки як таблицю зі стовпцями «Від», «Тема», «Дата» і «Розмір» під заголовком." + }, + "show_preview": { + "label": "Показати попередній перегляд тексту", + "description": "Відобразити попередній перегляд електронної пошти в списку", + "focus_description": "Відображати вбудований текст попереднього перегляду всередині однорядкового списку повідомлень у фокусі" + }, + "show_verification_codes": { + "label": "Показувати коди підтвердження", + "description": "Пропонувати код із листів для входу та підтвердження для копіювання одним кліком: у списку протягом доби та в самому листі" + }, + "disable_threading": { + "label": "Вимкнути групування розмов", + "description": "Показувати електронні листи як окремі повідомлення, а не згруповані за розмовами" + }, + "plain_text_mode": { + "label": "Тільки звичайний текст", + "description": "Вимкніть редактор форматованого тексту та надсилайте всі електронні листи лише як звичайний текст, включаючи відповіді та пересилання" + }, + "auto_select_reply_identity": { + "label": "Автоматичний вибір адреси для відповіді", + "description": "Під час відповіді автоматично змінюйте адресу відправника на особу, яка спочатку отримала повідомлення" + }, + "reply_identity_match": { + "label": "Зіставлення адреси отримання", + "description": "Які адреси отримання вважати вашими. «Лише точна адреса» вибирає виключно одну з налаштованих ідентичностей; «той самий домен» також вважає будь-яку іншу адресу на доменах ваших ідентичностей catch-all-псевдонімом і підставляє її в заголовок «Від». Виберіть точну адресу, якщо такі адреси є списками розсилки.", + "exact": "Лише точна адреса", + "domain": "Будь-яка адреса на моїх доменах" + }, + "signature_position": { + "label": "Розташування підпису", + "description": "Куди вставляти підпис у відповідях і пересиланнях. Над цитованим текстом читається природно як завершення відповіді; під ним зберігає цілісність оригінального повідомлення.", + "above_quote": "Перед цитованим текстом", + "below_quote": "Після цитованого тексту" + }, + "signature_separator": { + "label": "Розділювач підпису", + "description": "Додавати перед підписом стандартний рядок-розділювач \"-- \" (RFC 3676). Вимкніть, якщо хочете переходити з повідомлення відразу до підпису." + }, + "sub_address_delimiter": { + "label": "Розділювач під-адреси", + "description": "Символ, який відокремлює ім'я користувача від мітки під-адреси. Використовуйте розділювач, налаштований на вашому поштовому сервері (напр. user{delimiter}tag@domain.com).", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "Власний…", + "custom_input_label": "Власний символ-розділювач" + }, + "attachment_click_action": { + "label": "Вкладення Натисніть Дія", + "description": "Виберіть, чи клацання вкладеного файлу попередньо переглядає його чи негайно завантажує", + "preview": "Попередній перегляд, коли це можливо", + "download": "Завантажте негайно" + }, + "attachment_position": { + "label": "Позиція прикріплення", + "description": "Де відображати вкладення в заголовку електронної пошти", + "beside-sender": "Поруч із відправником", + "below-header": "Під заголовком" + }, + "emails_per_page": { + "10": "10 електронних листів", + "25": "25 електронних листів", + "50": "50 електронних листів", + "100": "100 електронних листів", + "label": "Кількість електронних листів на сторінку", + "description": "Кількість електронних листів для одночасного завантаження" + }, + "always_light_mode": { + "label": "Завжди показувати електронні листи в світлому режимі", + "description": "Візуалізуйте вміст електронної пошти в світлому режимі, навіть якщо програма працює в темному режимі, уникаючи проблем з перетворенням у темний режим" + }, + "external_content": { + "label": "Зовнішній вміст", + "description": "Як працювати із зображеннями та зовнішнім вмістом", + "ask": "Завжди запитуйте", + "block": "Завжди блокувати", + "allow": "Завжди дозволяти" + }, + "trusted_senders": { + "label": "Довірені відправники", + "description": "Керуйте відправниками, зображення яких завантажуються автоматично", + "count_zero": "Жодного", + "count_one": "1 відправник", + "count_other": "{count} відправників", + "modal_title": "Довірені відправники", + "empty_title": "Ще немає надійних відправників", + "empty_description": "Переглядаючи електронний лист із заблокованими зображеннями, натисніть «Завжди довіряти цьому відправнику», щоб додати їх сюди.", + "add_manually": "Додайте відправника вручну", + "add_button": "додати", + "add_placeholder": "Введіть електронну адресу", + "search_placeholder": "Пошук відправників...", + "no_results": "Немає відправників, які відповідають вашому запиту", + "remove": "видалити", + "close": "Закрити", + "invalid_email": "Введіть дійсну електронну адресу", + "already_added": "Цей відправник уже є надійним", + "save_error": "Не вдалося зберегти - подробиці перевірте в журналі налагодження Контактів", + "use_address_book_label": "Синхронізація з адресною книгою", + "use_address_book_description": "Зберігайте довірених відправників у спеціальній адресній книзі «Надійні відправники», щоб вони синхронізувалися на всіх ваших пристроях" + }, + "hover_actions": { + "label": "Дії швидкого наведення", + "description": "Виберіть, які швидкі дії відображатимуться під час наведення курсора на електронний лист у списку", + "delete": "Видалити", + "star": "Зірочка / Зняти зірочку", + "mark_read": "Позначте прочитане/непрочитане", + "archive": "Архів", + "tag": "Тег", + "spam": "Позначити як спам", + "not_spam": "Не спам", + "none_selected": "Дії не вибрано", + "mode_label": "Режим відображення", + "mode_inline": "Вбудований", + "mode_floating": "Плаваючий", + "corner_label": "Плаваюча позиція", + "corner_top-left": "Зліва вгорі", + "corner_top-right": "Верхній правий", + "corner_bottom-left": "Внизу зліва", + "corner_bottom-right": "Внизу праворуч" + }, + "default_mail_program": { + "label": "Поштова програма за замовчуванням", + "description": "Зареєструйте {appName} як поштову програму за умовчанням для посилань mailto:", + "button": "Встановити за замовчуванням", + "success": "Браузеру запропоновано встановити за умовчанням", + "error": "Ваш браузер не підтримує цю функцію" + }, + "attachment_reminder": { + "label": "Нагадування про вкладення", + "description": "Попереджати перед надсиланням, якщо у вашому повідомленні згадуються вкладення, але вони не вкладені", + "keywords_label": "Тригерні ключові слова", + "keywords_description": "Слова або фрази, які ініціюють нагадування, коли вони знайдені у вашому повідомленні", + "add_placeholder": "Додати ключове слово...", + "add": "додати", + "remove": "видалити" + }, + "empty_subject_warning": { + "label": "Попередження про порожню тему", + "description": "Запитувати підтвердження перед надсиланням листа без теми." + }, + "recipient_mentions": { + "label": "Згадувати одержувачів через @", + "description": "Під час введення @ у тексті листа пропонуються його одержувачі, і вставляється ім'я вибраного. Enter або Tab вибирає виділене ім'я." + }, + "mention_adds_recipient": { + "label": "Контакти в згадках через @", + "description": "Під час введення @ також пропонуються ваші контакти та люди з каталогу. Якщо вибрати того, кого ще немає серед одержувачів, його буде додано до листа.", + "to": "Додати в «Кому»", + "cc": "Додати в «Копія»", + "off": "Лише одержувачі" + }, + "hide_inline_image_attachments": { + "label": "Приховувати вбудовані зображення з вкладень", + "description": "Зображення, вбудовані в тіло повідомлення, не відображаються як окремі вкладення" + }, + "attachment_image_previews": { + "label": "Показувати попередній перегляд зображень у вкладеннях", + "description": "Відображати вкладені зображення як ескізи замість загальних піктограм файлів" + }, + "send_delay": { + "label": "Скасувати надсилання / затримка надсилання", + "description": "Затримує звичайне надсилання на короткий серверний інтервал.", + "off": "Вимкнено", + "seconds": "{seconds} с", + "unsupported": "Поточний обліковий запис не повідомляє про підтримку відкладеного надсилання. Налаштування залишиться збереженим для інших облікових записів." + }, + "return_to_list_after_action": { + "label": "Повернення до списку після видалення або позначення як непрочитане", + "description": "Після видалення або позначення відкритого листа як непрочитаного повертатися до списку листів замість відкриття наступного." + }, + "swipe_right_action": { + "label": "Дія при свайпі вправо (мобільні)", + "description": "На сенсорних пристроях — дія, що виконується при свайпі повідомлення вправо у списку." + }, + "swipe_left_action": { + "label": "Дія при свайпі вліво (мобільні)", + "description": "На сенсорних пристроях — дія, що виконується при свайпі повідомлення вліво у списку." + }, + "swipe_actions": { + "none": "Немає", + "archive": "Архівувати", + "delete": "Видалити", + "mark_read": "Перемкнути прочитане", + "star": "Перемкнути зірку", + "spam": "Позначити як спам" + }, + "rtl_editing": { + "label": "Підтримка редагування справа наліво", + "description": "Додає кнопку напрямку тексту на панель інструментів редактора, що дозволяє задавати абзацам напрямок зліва направо або справа наліво" + } + }, + "composer": { + "title": "Редактор", + "description": "Налаштування параметрів композиції електронної пошти", + "autosave": { + "label": "Інтервал автозбереження", + "description": "Як часто автоматично зберігати чернетки", + "30s": "Кожні 30 секунд", + "1m": "Кожну хвилину", + "2m": "Кожні 2 хвилини", + "5m": "Кожні 5 хвилин" + }, + "send_confirmation": { + "label": "Надіслати підтвердження", + "description": "Попросіть підтвердження перед надсиланням електронних листів" + }, + "default_reply": { + "label": "Типовий режим відповіді", + "description": "Дія за замовчуванням під час натискання відповіді", + "reply": "Відповісти", + "reply_all": "Відповісти всім" + } + }, + "privacy": { + "title": "Конфіденційність і безпека", + "description": "Керуйте налаштуваннями конфіденційності та безпеки", + "external_images": { + "label": "Блокувати зовнішні зображення", + "description": "Запобігання відстеження через зовнішні зображення" + }, + "session_timeout": { + "label": "Час очікування сеансу", + "description": "Автоматично виходити з системи після бездіяльності", + "never": "Ніколи", + "30m": "30 хвилин", + "1h": "1 година", + "4h": "4 години" + }, + "clear_cache": { + "label": "Очистити кеш", + "description": "Видаліть кешовані дані та тимчасові файли", + "button": "Очистити кеш", + "confirm": "Ви впевнені, що бажаєте очистити кеш?", + "success": "Кеш успішно очищено" + } + }, + "account": { + "title": "Обліковий запис", + "description": "Перегляньте інформацію свого облікового запису", + "name_label": "Відображуване ім'я", + "username_label": "Ім'я користувача", + "account_type_label": "Тип облікового запису", + "auth_method_label": "Аутентифікація", + "auth_method_oauth": "Єдиний вхід (OAuth/OIDC)", + "auth_method_basic": "Пароль", + "auth_method_token": "Токен доступу", + "demo_account": "Демо-рахунок", + "email": { + "label": "Адреса електронної пошти", + "value": "{email}" + }, + "server": { + "label": "Сервер JMAP", + "value": "{server}" + }, + "storage": { + "label": "Використання сховища", + "used": "Використано {used} з {total}", + "percentage": "Використано {percent}%." + }, + "last_sync": { + "label": "Остання синхронізація", + "value": "{time}" + }, + "accounts": { + "title": "Підключені облікові записи", + "description": "Перетягніть, щоб змінити порядок відображення облікових записів у меню", + "active": "Поточний активний обліковий запис", + "default_badge": "Обліковий запис за замовчуванням", + "set_default": "Зробити основним", + "switch_to": "Перемкнутися на цей обліковий запис", + "move_up": "Перемістити вгору", + "move_down": "Перемістити вниз", + "drag_handle": "Перетягніть, щоб змінити порядок", + "add": "Додати обліковий запис" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "Доступні мені", + "description": "Групові та спільні облікові записи, якими ви можете керувати. Виберіть один, щоб змінити його фільтри, автовідповідач, календарі та контакти.", + "shared_label": "Спільний обліковий запис" + } + }, + "security": { + "title": "Безпека облікового запису", + "description": "Керуйте своїм паролем, двофакторною автентифікацією та налаштуваннями безпеки", + "detecting": "Виявлення можливостей сервера...", + "not_available": "Керування безпекою облікового запису недоступне для цього поштового сервера. Необхідні дозволи можуть бути вимкнені. Перегляньте документацію для отримання додаткової інформації.", + "password": { + "title": "Змінити пароль", + "current": "Поточний пароль", + "new": "Новий пароль", + "confirm": "Підтвердьте новий пароль", + "submit": "Змінити пароль", + "success": "Пароль успішно змінено", + "error_title": "Не вдалося змінити пароль", + "error_mismatch": "Нові паролі не збігаються", + "error_min_length": "Пароль має бути не менше 8 символів", + "error_generic": "Не вдалося змінити пароль" + }, + "display_name": { + "label": "Відображуване ім'я", + "description": "Ваше ім'я, як воно відображається на сервері", + "placeholder": "Введіть своє відображуване ім'я", + "save": "зберегти", + "success": "Відображуване ім'я оновлено", + "error": "Не вдалося оновити відображуване ім’я" + }, + "totp": { + "section_title": "Двофакторна автентифікація", + "label": "Автентифікація TOTP", + "description": "Додайте додатковий рівень безпеки за допомогою одноразового пароля на основі часу", + "active": "Увімкнено", + "inactive": "Вимкнено", + "enabled": "Увімкнено двофакторну аутентифікацію", + "disabled": "Двофакторну автентифікацію вимкнено", + "enable_error": "Не вдалося ввімкнути 2FA", + "disable_error": "Не вдалося вимкнути 2FA", + "setup_instructions": "Скопіюйте цю URL-адресу в програму автентифікації (Google Authenticator, Authy тощо):", + "verification_code": "Код підтвердження", + "confirm": "Підтвердити", + "disable": "Вимкнути", + "disable_confirm_prompt": "Введіть пароль, щоб вимкнути двофакторну автентифікацію.", + "password_required": "Потрібен пароль", + "code_required": "Потрібен код підтвердження", + "code_invalid": "Недійсний код підтвердження. Перевірте додаток автентифікації та спробуйте ще раз." + }, + "app_passwords": { + "title": "Паролі програм", + "description": "Створюйте паролі для програм, які не підтримують двофакторну автентифікацію", + "add": "додати", + "create": "Створити", + "cancel": "Скасувати", + "generate": "Генерувати", + "name_label": "Назва програми", + "name_placeholder": "напр. Thunderbird, iPhone Mail", + "password_label": "Пароль (залиште порожнім для автоматичного створення)", + "password_placeholder": "Автоматично генерується, якщо порожній", + "added": "Пароль програми створено", + "removed": "Пароль програми видалено", + "add_error": "Не вдалося створити пароль програми", + "remove_error": "Не вдалося видалити пароль програми", + "none": "Паролі програм не налаштовано", + "done": "Готово", + "expires_label": "Термін дії (необов’язково)", + "copy_now_warning": "Скопіюйте цей пароль зараз - він більше не буде показаний.", + "allowed_ips_label": "Дозволені IP (необов’язково)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "Через кому або пробіл. Залиште порожнім, щоб дозволити будь-який IP." + }, + "api_keys": { + "title": "API-ключі", + "description": "Створюйте API-ключі для скриптів та інтеграцій, які звертаються до сервера напряму", + "name_label": "Назва ключа", + "name_placeholder": "напр. Скрипт резервної копії, CI runner", + "copy_now_warning": "Скопіюйте цей API-ключ зараз – він більше не буде показаний.", + "added": "API-ключ створено", + "removed": "API-ключ видалено", + "add_error": "Не вдалося створити API-ключ", + "remove_error": "Не вдалося видалити API-ключ", + "none": "API-ключі не налаштовано" + }, + "encryption": { + "section_title": "Шифрування в спокої", + "label": "Шифрування електронної пошти", + "description": "Шифруйте збережені електронні листи на сервері для додаткової конфіденційності", + "active": "{type} шифрування ввімкнено", + "inactive": "Вимкнено", + "enabled": "Шифрування в стані спокою ввімкнено", + "disabled_success": "Шифрування в стані спокою вимкнено", + "error": "Не вдалося оновити налаштування шифрування", + "algorithm_label": "Алгоритм шифрування", + "encrypt_on_append": "Шифрувати нові листи під час завантаження", + "allow_spam_training": "Дозволити навчання спам-фільтра перед шифруванням листів", + "enable_encryption": "Увімкнути шифрування", + "enable_tooltip": "Увімкнути шифрування цим ключем", + "configure_title": "Увімкнути шифрування цим ключем", + "enable_button": "Увімкнути", + "disable_tooltip": "Вимкнути шифрування", + "enabled_success": "Шифрування в стані спокою ввімкнено" + }, + "email_client": { + "title": "Налаштування поштового клієнта", + "description": "Використовуйте ці облікові дані, щоб налаштувати клієнт електронної пошти для настільного або мобільного пристрою (Thunderbird, Apple Mail, Outlook тощо)", + "jmap_username_label": "Ім'я користувача JMAP", + "copy": "Копія", + "copied": "Скопійовано", + "password_instructions": "Використовуйте своє ім’я користувача JMAP вище разом із паролем програми, щоб увійти у свій поштовий клієнт. Створіть пароль програми в розділі вище, якщо ви ще цього не зробили." + }, + "link_device": { + "title": "Прив'язати мобільний застосунок", + "description": "Увійдіть у застосунок Bulwark Mail на телефоні, не вводячи там пароль. Телефон отримує власний вхід, тож вихід тут не завершує сеанс на ньому.", + "generate": "Показати QR-код", + "regenerate": "Показати новий код", + "instructions": "Відкрийте застосунок Bulwark Mail і натисніть \"Scan a sign-in code\" на екрані входу (або \"Add account\" → \"Scan a sign-in code\", якщо ви вже увійшли), потім наведіть камеру сюди.", + "expires_in": "Термін дії коду закінчиться через {seconds} секунд. Його можна використати лише один раз.", + "expired": "Термін дії цього коду закінчився.", + "error": "Не вдалося створити код прив'язки. Будь ласка, спробуйте ще раз.", + "password_prompt": "Введіть пароль, щоб підтвердити, що це ви. Телефон отримує власний вхід; ваш пароль на телефон не передається.", + "show_password": "Показати пароль", + "hide_password": "Приховати пароль", + "continue": "Продовжити", + "error_wrong_password": "Неправильний пароль. Спробуйте ще раз.", + "totp_prompt": "Введіть код із застосунку-автентифікатора.", + "error_password_or_code": "Неправильний пароль або код.", + "error_not_signed_in": "Ваш сеанс завершився. Увійдіть знову, щоб прив'язати пристрій.", + "error_impersonation": "Не можна прив'язувати пристрої, поки ви переглядаєте поштову скриньку іншого користувача.", + "error_too_many_attempts": "Забагато спроб. Зачекайте трохи й спробуйте знову.", + "error_session_secret": "Прив'язку пристроїв на цьому сервері не налаштовано (відсутній SESSION_SECRET). Зверніться до адміністратора.", + "error_server_unreachable": "Не вдалося зв'язатися з вашим поштовим сервером. Спробуйте ще раз.", + "error_pairing_unavailable": "Ваш поштовий сервер не дозволяє прив'язувати пристрої таким способом. Зверніться до адміністратора.", + "qr_alt": "QR-код для входу", + "link_label": "Посилання для входу", + "copy_link": "Скопіювати посилання для входу", + "copied": "Скопійовано", + "copy_hint": "Немає камери? Скопіюйте посилання та вставте його в застосунку через \"Paste sign-in link\".", + "success_title": "Вхід на телефоні виконано", + "success_body": "Цю сторінку можна закрити. Щоб прив'язати ще один пристрій, покажіть новий код.", + "link_another": "Прив'язати ще один пристрій", + "use_sso": "Натомість використати єдиний вхід", + "error_insecure_server": "Мобільний застосунок підключається до поштових серверів лише через https, а цей сервер використовує http." + }, + "public_keys": { + "name_label": "Назва відкритого ключа", + "name_placeholder": "напр. Мій ключ", + "key_label": "Відкритий ключ (S/MIME або PGP у форматі ASCII)", + "key_placeholder": "Вставте сюди свій відкритий ключ", + "none": "Відкриті ключі не налаштовано" + } + }, + "identities": { + "title": "Надсилання ідентифікаторів", + "description": "Керуйте електронними адресами, з яких можна надсилати", + "identities_count": { + "label": "Ваші особи", + "description": "Адреси електронної пошти, налаштовані для надсилання", + "count_zero": "Жодної ідентифікації", + "count_one": "1 ідентичність", + "count_other": "{count} особи" + }, + "manage": "Керування ідентифікаторами", + "sub_addressing": { + "label": "Підадресація", + "description": "Використовуйте такі теги, як user+tag@domain.com, щоб упорядкувати вхідну пошту", + "learn_more": "Дізнайтеся більше" + } + }, + "vacation": { + "title": "Відсутність", + "description": "Автоматично обробляйте вхідні листи, поки вас немає", + "loading": "Завантаження налаштувань відпустки...", + "not_supported": "Ваш поштовий сервер не підтримує відповіді про відпустку.", + "fetch_error": "Не вдалося завантажити налаштування відпустки. Спробуйте ще раз.", + "status": { + "label": "Відповідати автоматично", + "description": "Надсилайте автоматичну відповідь людям, які надсилають вам електронні листи", + "active": "Активний", + "inactive": "Неактивний" + }, + "date_range": { + "start": "Дата початку", + "start_description": "Залиште пустим, щоб не було обмеження на початок", + "end": "Дата закінчення", + "end_description": "Залиште пустим, щоб не було кінцевого обмеження" + }, + "message": { + "subject_label": "Тема", + "subject_description": "Тема автоматичної відповіді", + "subject_placeholder": "Поза офісом", + "body_label": "Тіло повідомлення", + "body_description": "Вміст звичайного текстового повідомлення", + "body_placeholder": "Дякуємо за ваш електронний лист. Мене зараз немає в офісі, я відповім, коли повернуся.", + "html_label": "Форматоване повідомлення (HTML)", + "html_description": "Додайте форматовану версію з посиланнями та оформленням. Одержувачі, чий поштовий клієнт не може її показати, побачать звичайний текст вище.", + "html_placeholder": "Напишіть форматовану відповідь про відсутність…" + }, + "preview": { + "title": "Попередній перегляд", + "show": "Показати попередній перегляд", + "hide": "Приховати попередній перегляд" + }, + "save": "Зберегти зміни", + "saving": "Збереження...", + "warnings": { + "end_before_start": "Дата завершення має бути після дати початку", + "invalid_date": "Вкажіть дійсні дати початку й закінчення з часом або залиште поле порожнім", + "start_in_past": "Дата початку в минулому", + "empty_body": "Тіло повідомлення порожнє - одержувачі отримають порожню відповідь", + "subject_too_long": "Тема задовга для сервера (не більше {max} байтів).", + "body_too_long": "Повідомлення задовге для сервера (не більше {max} байтів разом із форматуванням).", + "forward_address": "Вкажіть дійсну адресу для пересилання", + "not_running": "Пересилання або отримувачів відповіді збережено, але зараз вони не діють. Збережіть, щоб знову їх увімкнути." + }, + "forward": { + "title": "Пересилання", + "description": "Пересилає вхідні листи на іншу адресу, з автовідповіддю або без неї, у зазначений вище період (без дати завершення, доки пересилання не вимкнуть). Спам не пересилається.", + "enabled_label": "Пересилати листи", + "to_label": "Переслати до", + "to_placeholder": "email@example.com", + "keep_label": "Зберігати у вхідних", + "keep_description": "Інакше лист лише пересилається: він не залишається в цій поштовій скриньці, і до нього не застосовується жодне правило фільтра." + }, + "audience": { + "label": "Відповідати", + "description": "Внутрішні відправники: {domains}", + "all": "Усім відправникам", + "internal": "Лише внутрішнім відправникам", + "external": "Лише зовнішнім відправникам" + }, + "auto_reply": { + "title": "Автовідповідь" + } + }, + "folders": { + "role_memos": "Нотатки", + "title": "Папки", + "description": "Керуйте своїми папками електронної пошти та призначайте стандартні ролі", + "folder_list": "Ваші папки", + "folder_list_description": "Натисніть піктограму папки, щоб налаштувати її", + "standard_roles": "Стандартні ролі папок", + "standard_roles_description": "Призначте, які папки використовуються для стандартних ролей поштової скриньки, як-от «Вхідні», «Надіслані», «Кошик» тощо.", + "role_inbox": "Вхідні", + "role_drafts": "Чернетки", + "role_sent": "Надіслано", + "role_trash": "сміття", + "role_junk": "Спам/сміття", + "role_archive": "Архів", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "Жодного", + "create_folder": "Створити папку", + "create_subfolder": "Створити вкладену папку", + "reorder": "Перетягніть, щоб змінити порядок", + "reorder_error": "Не вдалося змінити порядок папок", + "move_error": "Не вдалося перемістити папку", + "select_folder": "Вибрати {name}", + "select_hint": "Вибрати. Shift+клацання вибирає всі папки до останньої, яку ви клацнули.", + "selected_count": "{count, plural, one {Вибрано # папку} few {Вибрано # папки} many {Вибрано # папок} other {Вибрано # папки}}", + "move_to": "Перемістити до...", + "move_to_top_level": "Верхній рівень", + "move": "Перемістити", + "clear_selection": "Зняти виділення", + "folders_moved": "{count, plural, one {Переміщено # папку} few {Переміщено # папки} many {Переміщено # папок} other {Переміщено # папки}}", + "move_partial_error": "{count, plural, one {Не вдалося перемістити # папку} few {Не вдалося перемістити # папки} many {Не вдалося перемістити # папок} other {Не вдалося перемістити # папки}}", + "subfolder_of": "Всередині {name}", + "subfolder_name": "Назва вкладеної папки", + "new_folder_name": "Назва папки", + "rename": "Перейменувати", + "change_icon": "Змінити значок", + "count_mode": "Лічильник для {name}", + "count_mode_default": "Лічильник: типово", + "count_mode_unread": "Лише непрочитані", + "count_mode_all": "Непрочитані й усього", + "count_mode_none": "Без лічильника", + "delete": "Видалити", + "confirm_delete": "Ви впевнені, що хочете видалити \"{name}\"? Електронні листи в цій папці буде переміщено в кошик.", + "create": "Створити", + "cancel": "Скасувати", + "no_folders": "Немає спеціальних папок", + "cannot_delete_role": "Неможливо видалити папку зі стандартною роллю. Спочатку зніміть роль.", + "folder_created": "Папка створена", + "folder_renamed": "Папку перейменовано", + "folder_deleted": "Папку видалено", + "role_updated": "Роль папки оновлено", + "error_create": "Не вдалося створити папку", + "error_rename": "Не вдалося перейменувати папку", + "error_delete": "Не вдалося видалити папку", + "error_delete_has_children": "Неможливо видалити папку: вона все ще містить вкладені папки. Спочатку видаліть або перемістіть їх.", + "error_delete_has_email": "Неможливо видалити папку: вона все ще містить електронні листи. Спочатку перемістіть або видаліть їх.", + "error_role": "Не вдалося оновити роль папки" + }, + "advanced": { + "title": "Просунутий", + "description": "Розширені параметри та налаштування розробника", + "debug_mode": { + "label": "Режим налагодження", + "description": "Увімкніть детальне ведення журналу для усунення несправностей" + }, + "debug_categories": { + "description": "Виберіть категорії для реєстрації. Вимкніть категорії, які вам не потрібні, щоб зменшити шум консолі.", + "jmap": "Клієнт JMAP", + "jmap_description": "Операції з поштовою скринькою, отримання електронної пошти та запити протоколу JMAP", + "calendar": "Календар", + "calendar_description": "Події календаря, імпорт і планування повідомлень", + "tasks": "завдання", + "tasks_description": "Створення, отримання та оновлення завдань календаря", + "auth": "Аутентифікація", + "auth_description": "Логін, TOTP, обмін токеном і керування сесіями", + "filters": "Фільтри", + "filters_description": "Правила фільтрації сита та сценарії відпустки", + "email": "Перегляд електронної пошти", + "email_description": "Візуалізація електронної пошти, обробка TNEF і позначення як прочитане", + "push": "Push-сповіщення", + "push_description": "Налаштування та доставка push-повідомлень", + "contacts": "Контакти та адресні книги", + "contacts_description": "Синхронізація контактів, операції з адресною книгою та довірені відправники" + }, + "settings_sync": { + "label": "Синхронізація налаштувань", + "description": "Синхронізуйте налаштування між браузерами та пристроями" + }, + "sender_favicons": { + "label": "Favicons відправника", + "description": "Показувати піктограми веб-сайтів як зображення профілю для бізнес-відправників", + "refresh_label": "Оновити значки відправників", + "refresh_description": "Заново завантажити значки сайтів, наприклад якщо відправник змінив свій", + "refresh_button": "Оновити", + "refresh_done": "Значки відправників буде завантажено заново" + }, + "show_avatars_in_junk": { + "label": "Показувати аватари в папці «Спам»", + "description": "Показувати зображення профілю та фавікони відправників у папці «Спам». Типово вимкнено, щоб фішингові листи не виглядали достовірно." + }, + "keyboard_shortcuts": { + "label": "Комбінації клавіш", + "description": "Переглянути доступні комбінації клавіш", + "button": "Перегляд ярликів" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "Скинути налаштування", + "description": "Відновити всі налаштування до значень за замовчуванням", + "button": "Скинути до значень за замовчуванням" + }, + "export_settings": { + "label": "Налаштування експорту", + "description": "Завантажте налаштування як JSON", + "button": "Експорт" + }, + "import_settings": { + "label": "Налаштування імпорту", + "description": "Завантажте налаштування з файлу JSON", + "button": "Імпорт" + }, + "about": { + "title": "Оплот веб-пошти", + "source_code": "Вихідний код" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "Натискайте на спам і фішинг, доки вони не потрапили у вхідні. Справжні листи пропускайте. Втратите всі три щити — вхідні впадуть.", + "kinds": { + "spam": "Спам", + "phishing": "Фішинг", + "legit": "Справжній лист" + }, + "click_to_block": "Натисніть, щоб заблокувати", + "let_through": "Пропустити", + "start": "Почати", + "play_again": "Грати ще", + "score": "Очки", + "combo": "Бонус за серію", + "shields_left": "Залишилось щитів: {count} з {total}", + "seconds": "{seconds} с", + "best": "Рекорд: {score}", + "new_best": "Новий рекорд", + "held": "Вхідні встояли", + "overrun": "Вхідні захоплено", + "stats": { + "caught": "Заблоковано", + "delivered": "Доставлено", + "mistakes": "Помилки" + } + } + }, + "sidebar_apps": { + "title": "Програми бічної панелі", + "description": "Керуйте спеціальними програмами та посиланнями на бічній панелі", + "keep_loaded": "Тримайте програми завантаженими", + "keep_loaded_description": "Під час перемикання між вбудованими програмами продовжуйте працювати у фоновому режимі, щоб уникнути перезавантаження", + "managed_title": "Надано вашим адміністратором", + "managed_description": "Ці застосунки налаштовано для всіх, і їх не можна змінити.", + "manage_title": "Спеціальні програми", + "manage_description": "Додавайте, редагуйте або видаляйте власні програми з бічної панелі" + }, + "contacts": { + "title": "Контакти", + "description": "Імпортуйте та експортуйте свої контакти", + "import_label": "Імпортувати контакти", + "import_description": "Імпортуйте контакти з файлу vCard (.vcf).", + "export_label": "Експортувати контакти", + "export_description": "Експортуйте всі контакти як файл vCard (.vcf).", + "manage_title": "Адресні книги", + "manage_description": "Перейменуйте свої адресні книги", + "no_address_books": "Адресних книг не знайдено", + "categories_title": "Категорії", + "categories_description": "Перейменувати категорії контактів", + "no_categories": "Категорії не знайдено", + "group_by_letter_description": "Показувати алфавітні заголовки розділів у списку контактів", + "group_by_letter_label": "Групувати за першою літерою", + "sort_by_last_name_label": "Сортувати за прізвищем", + "sort_by_last_name_description": "Упорядкувати список контактів за прізвищем, щоб члени родини були поруч" + }, + "downloads": { + "title": "Завантаження", + "description": "Налаштуйте, як називаються завантажені листи та вкладення.", + "reset": "Відновити стандартне", + "preview": "Попередній перегляд:", + "email_template": { + "label": "Ім'я файлу листа (.eml)", + "description": "Шаблон, що використовується при експорті листа або перетягуванні його у файлову систему. Розширення .eml додається автоматично." + }, + "attachment_template": { + "label": "Ім'я файлу вкладення", + "description": "Шаблон, що використовується при завантаженні або перетягуванні вкладення. Якщо пропустити '{filename}' та '{ext}', оригінальне розширення зберігається." + }, + "bundle_template": { + "label": "Ім'я файлу .zip з кількома листами", + "description": "Шаблон, що використовується при перетягуванні або завантаженні кількох вибраних листів одним .zip-архівом. Розширення .zip додається автоматично." + }, + "spaces": { + "label": "Пробіли", + "description": "Замінити пробіли в підсумковому імені файлу іншим символом.", + "keep": "Залишити пробіли", + "underscore": "Замінити на _", + "dash": "Замінити на -" + }, + "lowercase": { + "label": "Нижній регістр", + "description": "Примусово перетворити все ім'я файлу на нижній регістр." + }, + "strip_diacritics": { + "label": "Видалити діакритику", + "description": "Перетворити літери з акцентами на ASCII-еквіваленти (ä → a, é → e). Корисно для інструментів, що погано обробляють Unicode." + }, + "collapse_separators": { + "label": "Об'єднати повторювані роздільники", + "description": "Об'єднати послідовності пробілів, підкреслень або дефісів в один символ." + }, + "after_export": { + "label": "Після експорту", + "description": "За потреби перемістити лист після експорту як .eml.", + "keep": "Залишити у скриньці", + "archive": "Перемістити в архів", + "trash": "Перемістити у кошик" + } + }, + "filters": { + "title": "Фільтри електронної пошти", + "description": "Створіть правила для автоматичного сортування, позначення та керування вхідними електронними листами", + "add_rule": "Додати правило", + "no_rules": "Без правил фільтрації", + "no_rules_description": "Створіть правила для автоматичного впорядкування вхідних електронних листів", + "vacation_active": "Автовідповідач активний", + "vacation_active_description": "Для вхідних повідомлень увімкнено автовідповідь", + "vacation_configure": "Налаштувати", + "edit_rule": "Редагувати правило", + "new_rule": "Нове правило", + "delete_rule": "Видалити правило", + "delete_confirm": "Ви впевнені, що хочете видалити це правило?", + "enable": "Увімкнути", + "disable": "Вимкнути", + "raw_editor": "Редактор Raw Sieve", + "raw_editor_warning": "Редагування необробленого сценарію Sieve може порушити редагування візуальних правил. Зміни, зроблені тут, замінюють візуальний конструктор.", + "validate": "Перевірити", + "validation_success": "Сценарій дійсний", + "validation_error": "Скрипт містить помилки", + "save": "Зберегти правила", + "saving": "Збереження...", + "saved": "Фільтри успішно збережено", + "save_failed": "Не вдалося зберегти фільтри", + "loading": "Завантаження фільтрів...", + "not_supported": "Ваш поштовий сервер не підтримує фільтри електронної пошти.", + "rule_name": "Назва правила", + "rule_name_placeholder": "наприклад, Сортувати інформаційні бюлетені", + "match_all": "Відповідають УСІМ умовам", + "match_any": "Відповідає БУДЬ-ЯКІЙ умові", + "conditions": "Умови", + "add_condition": "Додати умову", + "actions": "Дії", + "add_action": "Додати дію", + "stop_processing": "Зупинити обробку наступних правил", + "include_spam": "Переміщувати також листи, позначені як спам", + "attachment_type_placeholder": "напр. pdf, doc, jpg", + "value_placeholder_multi": "Значення (декілька через кому)", + "condition_fields": { + "attachment": "Вкладення", + "from": "Від", + "to": "до", + "cc": "Cc", + "subject": "Тема", + "header": "Спеціальний заголовок", + "size": "Розмір", + "body": "Тіло", + "all": "Усі листи" + }, + "comparators": { + "has_any": "присутнє", + "has_type": "типу", + "contains": "містить", + "not_contains": "не містить", + "is": "точно", + "not_is": "не є", + "starts_with": "починається з", + "ends_with": "закінчується на", + "matches": "відповідає шаблону", + "greater_than": "більше ніж", + "less_than": "менше ніж", + "address_is": "— це адреса", + "domain_is": "має домен" + }, + "action_types": { + "move": "Перемістити в папку", + "copy": "Копіювати в папку", + "forward": "Переслати до", + "mark_read": "Позначити як прочитане", + "star": "Повідомлення зірочкою", + "add_label": "Додати мітку", + "discard": "Відкинути (видалити мовчки)", + "reject": "Відхилити повідомленням", + "keep": "Зберігати у вхідних", + "stop": "Зупинити обробку" + }, + "move_to_folder": "Виберіть папку", + "copy_to_folder": "Виберіть папку", + "forward_to": "Переслати на електронну адресу", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "Зберегти копію", + "forward_limit": "Ліміт пересилань на один лист на цьому сервері: {count}. Зайві пересилання пропускаються.", + "forward_unavailable": "Цей сервер не дозволяє пересилання. Дії пересилання в правилах фільтрів не спрацюють.", + "reject_message": "Повідомлення про відмову", + "reject_placeholder": "Ваш лист було відхилено", + "label_name": "Назва мітки", + "label_placeholder": "наприклад, важливий", + "header_name": "Назва заголовка", + "header_placeholder": "напр., X-Mailing-List", + "size_bytes": "Розмір у байтах", + "size_placeholder": "напр., 1000000", + "system_managed": "Правило, кероване системою", + "opaque_warning": "Цей сценарій було відредаговано поза візуальним конструктором. Доступне лише редагування Raw Sieve.", + "open_sieve_editor": "Відкрийте редактор Raw Sieve", + "fetch_error": "Не вдалося завантажити фільтри", + "expanded_view": "Розгорнутий вигляд", + "expanded_view_description": "Показати правила фільтрації з детальними умовами та блоками дій", + "if": "Якщо", + "then": "Потім", + "match_all_conditions": "всі збігаються", + "match_any_condition": "будь-які збіги", + "and": "і", + "or": "або", + "cancel": "Скасувати", + "confirm_delete": "Видалити", + "rule_list": "Правила фільтрації", + "drag_to_reorder": "Перетягніть, щоб змінити порядок", + "match_type": "Тип відповідності", + "reset_to_visual": "Скинути до Visual Builder", + "reset_warning": "Це призведе до скасування поточного сценарію та запуску заново.", + "confirm_reset": "Скинути", + "validation_empty_name": "Потрібно вказати назву правила", + "validation_empty_conditions": "Потрібна принаймні одна умова зі значенням", + "validation_empty_actions": "Необхідна принаймні одна дія", + "templates_section": "Почніть із шаблону", + "template_newsletters": "Перемістити інформаційні бюлетені в папку", + "template_receipts": "Автоматичне архівування квитанцій", + "template_important": "Позначайте важливі електронні листи", + "template_notifications": "Фільтр сповіщень", + "sieve_editor": { + "title": "Редактор сценаріїв Sieve", + "warning": "Редагування необробленого сценарію Sieve може порушити редагування візуальних правил. Зміни, зроблені тут, замінюють візуальний конструктор.", + "script_content": "Сито сценарій", + "valid": "Сценарій дійсний", + "invalid": "Скрипт містить помилки", + "save_warning": "Збереження перезапише будь-які візуальні правила. Це неможливо скасувати. Натисніть кнопку Зберегти ще раз для підтвердження.", + "validating": "Перевірка...", + "validate": "Перевірити", + "cancel": "Скасувати", + "save": "зберегти", + "confirm_save": "Підтвердити збереження", + "validation_failed": "Помилка запиту перевірки" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# умова} few {# умови} many {# умов} other {# умов}}", + "actions_count": "{count, plural, one {# дія} few {# дії} many {# дій} other {# дій}}" + }, + "origin_external": "Зовнішнє", + "managed_by_tooltip": "Керується {source}. Редагуйте в тому додатку або використовуйте редактор Sieve.", + "quick_rule_names": { + "move_sender": "Переміщувати від {sender} до {folder}", + "move_domain": "Переміщувати від @{domain} до {folder}", + "move_list": "Переміщувати розсилку {list} до {folder}", + "mark_read": "Позначати прочитаними від {sender}", + "tag": "Додавати мітку {tag} для {sender}", + "block": "Блокувати {sender}", + "custom": "Листи від {sender}", + "custom_plain": "Нове правило", + "senders": "{count, plural, one {# відправника} few {# відправників} many {# відправників} other {# відправника}}" + }, + "suggestions": "Підказки", + "suggest_subject": "Тема «{subject}»", + "suggest_to": "Кому {address}", + "suggest_cc": "Копія {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "Застосувати й до листів, які вже є в цій папці", + "apply_existing_unsupported": "До наявних листів можна застосувати лише правила, що перевіряють відправника, одержувачів, тему або заголовки й переміщують, копіюють, позначають прочитаними, зірочкою чи міткою.", + "period_title": "Діапазон дат", + "period_start": "Дата початку", + "period_end": "Дата закінчення", + "validation_period_order": "Дата завершення має бути після дати початку", + "period_toggle": "Активне лише в певний період", + "period_hint": "Діє для листів, що надходять у цей період. Залиште поле порожнім, щоб не обмежувати цю межу.", + "validation_period_empty": "Вкажіть дату початку або закінчення періоду", + "validation_period_invalid": "Вкажіть для періоду дійсну дату з часом або залиште поле порожнім", + "apply_existing_period": "Правило з періодом діє лише на листи, що надходять у цей період, тому його не можна застосувати до наявних листів.", + "period_status_scheduled": "заплановано", + "period_status_active": "активне", + "period_status_expired": "завершено" + }, + "templates": { + "title": "Шаблони електронної пошти", + "description": "Створюйте багаторазові шаблони електронної пошти зі змінними-заповнювачами", + "add": "Новий шаблон", + "edit": "Редагувати шаблон", + "name": "Назва шаблону", + "name_placeholder": "наприклад, повторний електронний лист", + "category": "Категорія", + "category_placeholder": "наприклад, Робота, Особисте", + "subject": "Тема", + "subject_placeholder": "Тема листа", + "body": "Тіло", + "body_placeholder": "Основний вміст електронного листа...", + "recipients_placeholder": "email@example.com", + "identity": "Надіслати як", + "default_identity": "Ідентифікація за замовчуванням", + "favorite": "улюблений", + "cancel": "Скасувати", + "create": "Створити", + "update": "оновлення", + "confirm_delete": "Видалити", + "no_templates": "Шаблонів ще немає", + "manage": "Керування шаблонами", + "count": "{count, plural, one {# шаблон} few {# шаблони} many {# шаблонів} other {# шаблонів}}", + "export_import": "Експорт та імпорт", + "export_import_description": "Зробіть резервну копію своїх шаблонів або перенесіть їх на інший пристрій", + "export": "Експорт", + "import": "Імпорт", + "validation": { + "empty": "Потрібно вказати назву шаблону", + "too_long": "Назва шаблону має містити не більше 200 символів" + } + }, + "files": { + "display": { + "title": "Дисплей", + "description": "Налаштувати спосіб відображення файлів і папок" + }, + "default_view": { + "label": "Перегляд за умовчанням", + "description": "Вибір між сіткою та списком", + "list": "Список", + "grid": "Сітка" + }, + "default_sort": { + "label": "Типове сортування", + "description": "Виберіть стандартне сортування для файлів", + "name": "Ім'я", + "size": "Розмір", + "modified": "Змінено" + }, + "sort_direction": { + "label": "Напрямок сортування", + "description": "Виберіть порядок зростання або спадання", + "ascending": "За зростанням", + "descending": "Спускається" + }, + "icons": { + "title": "Ікони", + "description": "Налаштувати зовнішній вигляд значка файлу" + }, + "show_icons": { + "label": "Показати значки файлів", + "description": "Відображення піктограм біля файлів і папок" + }, + "colored_icons": { + "label": "Кольорові значки", + "description": "Використовуйте кольорові значки замість монохромних" + }, + "show_thumbnails": { + "label": "Показати мініатюри", + "description": "Відображати попередній перегляд зображень замість піктограм для файлів зображень" + }, + "behavior": { + "title": "Поведінка", + "description": "Налаштувати поведінку файлового браузера" + }, + "show_hidden": { + "label": "Показати приховані файли", + "description": "Відображення файлів і папок, які починаються з крапки" + }, + "preview": { + "label": "Попередній перегляд" + } + }, + "search_placeholder": "Пошук у налаштуваннях", + "search_clear": "Очистити пошук", + "search_no_results": "Немає відповідних налаштувань", + "scoped": { + "back": "Назад до мого облікового запису", + "managing": "Керування: {name}" + } + }, + "errors": { + "page_error_title": "Щось пішло не так", + "page_error_description": "Ми зіткнулися з неочікуваною помилкою. Спробуйте ще раз або поверніться на головну сторінку.", + "sidebar_error": "Неможливо завантажити поштові скриньки", + "email_list_error": "Неможливо завантажити електронні листи", + "viewer_error_title": "Неможливо відобразити електронну пошту", + "viewer_error_description": "Під час відтворення цього електронного листа виникла проблема. Він може містити непідтримуваний вміст.", + "composer_error": "Не вдалося завантажити редактор листа", + "settings_error_title": "Налаштування недоступні", + "settings_error_description": "Не вдалося завантажити налаштування. Ваші налаштування можуть не зберегтися.", + "try_again": "Спробуйте знову", + "reload": "Перезавантажити", + "reload_emails": "Перезавантажте електронні листи", + "reload_settings": "Перезавантажити налаштування", + "retry": "Повторіть спробу", + "go_home": "Перейти до папки \"Вхідні\"." + }, + "context_menu": { + "reply": "Відповісти", + "reply_all": "Відповісти всім", + "forward": "вперед", + "mark_read": "Позначити як прочитане", + "mark_unread": "Позначити як непрочитане", + "star": "зірка", + "unstar": "Зняти зірочку", + "pin": "Закріпити", + "unpin": "Відкріпити", + "move_to": "Перейти до...", + "copy_to_account": "Копіювати в обліковий запис...", + "remove_from_folder": "Прибрати з цієї папки", + "archive": "Архів", + "delete": "Видалити", + "mark_as_spam": "Повідомити про спам", + "not_spam": "Не спам", + "tag": "Мітка", + "items_selected": "Вибрано електронних листів: {count}", + "forward_as_attachments": "Переслати як вкладення", + "edit_draft": "Редагувати чернетку", + "cancel_scheduled_send": "Скасувати надсилання", + "reschedule_send": "Перенести", + "cancel_and_edit": "Скасувати й редагувати", + "cancel_and_compose_again": "Скасувати й написати знову", + "rules": { + "title": "Правила", + "move_from": "Завжди переміщувати листи від {sender}", + "move_from_domain": "Завжди переміщувати листи від @{domain}", + "move_from_list": "Завжди переміщувати листи з цієї розсилки", + "mark_read_from": "Завжди позначати листи від {sender} прочитаними", + "tag_with": "Завжди додавати мітку", + "block": "{count, plural, =1 {Заблокувати відправника} one {Заблокувати # відправника} few {Заблокувати # відправників} many {Заблокувати # відправників} other {Заблокувати # відправника}}", + "no_junk": "У цьому обліковому записі немає папки «Спам»", + "create": "Створити правило…", + "manage": "Керувати правилами", + "senders_count": "{count, plural, one {# відправника} few {# відправників} many {# відправників} other {# відправника}}", + "cross_account": "Виберіть листи з одного облікового запису", + "opaque_hint": "Ваші фільтри відредаговано вручну. Відкрийте налаштування фільтрів" , + "loading": "Завантаження правил…", + "load_failed": "Не вдалося завантажити правила", + "none": "Правил поки немає", + "new_mail_only": "Лише для нових листів", + "running": "Виконується «{name}» у теці {folder}…" + }, + "find_emails_from": "Знайти листи від {name}" + }, + "mailbox_context_menu": { + "mark_folder_read": "Позначити папку як прочитану", + "mark_folder_tree_read": "Позначити папку та вкладені папки як прочитані", + "mark_all_folders_read": "Позначити всі папки як прочитані", + "new_subfolder": "Нова вкладена папка...", + "new_folder": "Нова папка...", + "rename": "Перейменувати...", + "share": "Поділитися...", + "import_email": "Імпортувати .eml або .zip...", + "empty_folder": "Очистити папку", + "empty_folder_generic": "Очистити папку", + "delete_folder": "Видалити папку", + "refresh": "Оновити", + "hide_shared_account": "Приховати з бічної панелі", + "mute_notifications": "Вимкнути сповіщення", + "unmute_notifications": "Увімкнути сповіщення", + "mute_account_notifications": "Вимкнути сповіщення для цього облікового запису", + "unmute_account_notifications": "Увімкнути сповіщення для цього облікового запису", + "reset_folder_notifications": "Скинути сповіщення папок", + "mark_all_confirm_title": "Позначити всі папки як прочитані", + "mark_all_confirm_message": "Позначити всі непрочитані повідомлення у вашому особистому акаунті як прочитані?", + "delete_confirm_title": "Видалити папку", + "delete_confirm_message": "Остаточно видалити папку \"{name}\"? Цю дію неможливо скасувати.", + "prompt_new_subfolder": "Введіть ім'я для нової вкладеної папки.", + "prompt_new_folder": "Введіть ім'я для нової папки.", + "prompt_rename": "Введіть нове ім'я для цієї папки.", + "toast_marked_read": "Папку позначено як прочитану", + "toast_marked_read_count": "Позначено {count, plural, one {1 повідомлення} few {# повідомлення} other {# повідомлень}} як прочитані", + "toast_already_read": "Немає непрочитаних повідомлень", + "toast_marked_all_read": "Всі папки позначені як прочитані", + "toast_emptied": "Папку очищено", + "toast_folder_created": "Папку створено", + "toast_folder_renamed": "Папку перейменовано", + "toast_folder_deleted": "Папку видалено", + "toast_error_mark_read": "Не вдалося позначити як прочитане", + "toast_error_empty": "Не вдалося очистити папку", + "toast_error_create": "Не вдалося створити папку", + "toast_error_rename": "Не вдалося перейменувати папку", + "toast_error_delete": "Не вдалося видалити папку", + "toast_error_delete_has_children": "Папка містить вкладені папки. Спочатку видаліть їх.", + "toast_error_delete_has_email": "Папка не порожня. Спочатку очистіть її.", + "delete_with_contents_title": "Тека не порожня", + "delete_with_contents_message": "Тека «{name}» досі містить {count, plural, one {# повідомлення} few {# повідомлення} other {# повідомлень}}. Видалити теку разом з усіма повідомленнями? Цю дію не можна скасувати.", + "delete_with_contents_confirm": "Видалити теку та повідомлення", + "placeholder_folder_name": "Ім'я папки", + "create": "Створити", + "rename_confirm": "Перейменувати" + }, + "shortcuts": { + "title": "Комбінації клавіш", + "tip": "Натисніть ? у будь-який час, щоб показати цю допомогу", + "sections": { + "navigation": "Навігація", + "actions": "Дії електронної пошти", + "global": "Глобальний", + "threads": "Нитки", + "composer": "Редактор" + }, + "navigation": { + "next_email": "Наступний електронний лист", + "previous_email": "Попередній електронний лист", + "open_email": "Відкрийте електронну пошту", + "close_email": "Закрити / Скасувати вибір", + "go_to_folder": "Перейти до теки", + "next_unread": "Наступний непрочитаний лист", + "no_more_unread": "Нижче в списку більше немає непрочитаних листів" + }, + "actions": { + "reply": "Відповісти", + "reply_all": "Відповісти всім", + "forward": "вперед", + "star": "Перемкнути зірку", + "archive": "Архів", + "delete": "Видалити", + "mark_unread": "Позначити як непрочитане", + "mark_read": "Позначити як прочитане", + "toggle_spam": "Повідомити про спам / Не спам" + }, + "global": { + "compose": "Створіть новий електронний лист", + "search": "Пошук по фокусу", + "help": "Показати ярлики", + "refresh": "Оновити електронні листи", + "select_all": "Вибрати все", + "global_search": "Шукати скрізь", + "extend_selection": "Розширити виділення вниз / вгору" + }, + "threads": { + "expand_collapse": "Розгорнути/згорнути ланцюжок", + "select_mountain_view": "Вибрати листування" + }, + "composer": { + "send": "Надіслати лист", + "schedule_send": "Запланувати надсилання", + "template_picker": "Відкрити засіб вибору шаблону" + } + }, + "threads": { + "messages_one": "{count} повідомлення", + "messages_other": "{count} повідомлень", + "messages_tooltip": "{count, plural, one {# повідомлення в цій розмові} few {# повідомлення в цій розмові} many {# повідомлень у цій розмові} other {# повідомлень у цій розмові}}", + "expand": "Розгорнути бесіду", + "collapse": "Згорнути бесіду", + "loading": "Завантаження бесіди...", + "mark_read": "Позначити бесіду як прочитану", + "mark_unread": "Позначити розмову як непрочитану", + "archive": "Архів бесіди", + "delete": "Видалити бесіду", + "star": "Зоряна розмова", + "unstar": "Зняти зірочку з розмови", + "toggle_thread": "Перемкнути потік" + }, + "identities": { + "modal_title": "Керування ідентифікаторами надсилання", + "create_new": "Створіть нову ідентичність", + "edit_identity": "Редагувати ідентифікатор", + "delete_confirm": "Видалити цю особу? Це неможливо скасувати.", + "cannot_delete": "Цю особу неможливо видалити", + "primary_identity": "Первинний", + "set_as_primary": "Встановити як основний", + "no_identities": "Особи не знайдено", + "display": { + "reply_to": "Відповідь:", + "bcc": "BCC:", + "signature": "Підпис:", + "preview": "Попередній перегляд:" + }, + "validation_errors": { + "invalid_emails": "Недійсні електронні адреси: {emails}", + "unknown_error": "Невідома помилка" + }, + "form": { + "name_label": "Відображуване ім'я", + "name_placeholder": "наприклад, робоча електронна пошта, особиста", + "name_required": "Необхідно вказати ім'я", + "email_label": "Адреса електронної пошти", + "email_placeholder": "your.email@example.com", + "email_required": "Необхідно вказати адресу електронної пошти", + "email_invalid": "Введіть дійсну електронну адресу", + "email_immutable": "Адресу електронної пошти не можна змінити після створення", + "reply_to_label": "Відповідь (необов'язково)", + "reply_to_placeholder": "different@example.com", + "bcc_label": "Auto BCC (опціонально)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "Текстовий підпис", + "html_signature_label": "Підпис HTML", + "signature_byte_counter": "{bytes} / {max} байтів", + "signature_byte_limit_reached": "Досягнуто обмеження сервера", + "save": "Зберегти ідентифікатор", + "cancel": "Скасувати", + "creating": "Створення...", + "updating": "Оновлення..." + }, + "sub_address": { + "button_tooltip": "Використовуйте допоміжну адресу", + "popover_title": "Додайте тег підадреси", + "tag_input_placeholder": "Введіть тег (наприклад, шопінг)", + "preview_label": "Попередній перегляд:", + "recent_tags": "Останні теги", + "suggested_tags": "Запропоновано", + "use_address": "Використовуйте цю адресу", + "invalid_tag": "Тег має бути лише буквено-цифровим і тире", + "tag_too_long": "Тег має містити 30 символів або менше", + "help_text": "Електронні листи, надіслані на user{delimiter}tag@domain.com, надходитимуть до вашої скриньки", + "validation": { + "empty": "Тег не може бути порожнім", + "too_long": "Тег має містити не більше {max} символів", + "invalid_chars": "Тег повинен містити лише літери, цифри та тире" + } + }, + "badge": { + "sent_via": "через", + "sub_address_tag": "Надіслано з використанням додаткової адреси: {tag}", + "identity_name": "Надіслано з ідентифікатором: {name}", + "identity_short": "через {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "Видалити", + "delete_confirm_title": "Видалити особу" + }, + "templates": { + "picker_title": "Виберіть шаблон", + "search_placeholder": "Пошук шаблонів...", + "section_favorites": "Вибране", + "section_recent": "Останні", + "section_uncategorized": "інше", + "no_templates": "Шаблонів ще немає", + "no_results": "Шаблони не знайдено", + "fill_placeholders": "Заповнити значення заповнювачів", + "enter_value": "Введіть значення...", + "preview": "Попередній перегляд", + "insert_with_values": "Вставити зі значеннями", + "insert_raw": "Вставити Raw", + "copy_suffix": "(копія)", + "placeholder": "змінна", + "placeholders": { + "recipient_name": "Ім'я одержувача", + "company": "Назва компанії", + "date": "Поточна дата", + "day_of_week": "День тижня", + "sender_name": "Ваше ім'я" + } + }, + "contacts": { + "directory": { + "title": "Довідник", + "search_placeholder": "Пошук за ім'ям або адресою", + "refresh": "Оновити довідник", + "retry": "Повторити", + "loading": "Завантаження довідника…", + "unavailable_title": "Немає довідника", + "unavailable_hint": "Ваш поштовий сервер не надає довідник облікових записів.", + "forbidden_title": "Довідник недоступний", + "forbidden_hint": "Ваш поштовий сервер не дозволяє цьому обліковому запису переглядати довідник. Зверніться до адміністратора.", + "error_title": "Не вдалося завантажити довідник", + "error_hint": "Перевірте з'єднання та спробуйте ще раз.", + "empty_title": "Довідник порожній", + "empty_hint": "Ваш поштовий сервер не повертає облікових записів з адресою електронної пошти.", + "no_match": "У довіднику ніхто не відповідає запиту", + "refresh_failed": "Не вдалося оновити довідник. Показано останній завантажений список.", + "select_page": "Вибрати всіх на цій сторінці", + "deselect_page": "Зняти вибір на цій сторінці", + "select_entry": "Вибрати {name}", + "range": "{from}–{to} з {total}", + "page": "Сторінка {page} з {count}", + "previous_page": "Попередня сторінка", + "next_page": "Наступна сторінка", + "picker_title": "Вибрати з довідника", + "cancel": "Скасувати", + "selected_count": "Вибрано: {count}", + "add_to": "Додати в «Кому»", + "add_cc": "Додати в «Копія»", + "add_bcc": "Додати в «Прихована копія»", + "select_hint": "Виберіть людину, щоб побачити подробиці", + "write_email": "Написати листа", + "add_to_contacts": "Додати до контактів", + "email": "Ел. пошта", + "read_only_note": "Записи довідника надходять з вашого поштового сервера, тут їх не можна змінити.", + "type": { + "individual": "Людина", + "group": "Група", + "resource": "Ресурс", + "location": "Місце", + "other": "Інше" + } + }, + "title": "Контакти", + "search_placeholder": "Пошук контактів...", + "create_new": "Новий контакт", + "no_category": "Без категорії", + "rename_category": "Перейменувати категорію", + "category_name_label": "Назва категорії", + "category_renamed": "Категорію перейменовано", + "category_rename_failed": "Не вдалося перейменувати категорію", + "category_added": "Контакт додано до {name}", + "category_added_plural": "{count} контактів додано до {name}", + "empty_state": "Контактів ще немає", + "empty_state_title": "Контактів ще немає", + "empty_state_subtitle": "Створіть свій перший контакт або імпортуйте його з файлу vCard", + "empty_search": "Жоден контакт не відповідає вашому пошуку", + "empty_search_hint": "Спробуйте інший термін пошуку", + "empty_filtered": "Жоден контакт не відповідає фільтрам", + "empty_filtered_hint": "Змініть або очистіть фільтри", + "clear_search": "Очистити пошук", + "import_vcard": "Імпорт vCard", + "delete_confirm_title": "Видалити контакт", + "delete_confirm": "Ви впевнені, що хочете видалити цей контакт?", + "local_mode": "Контакти зберігаються локально (сервер не підтримує контакти JMAP)", + "back_to_contacts": "Назад до контактів", + "back_to_email": "Назад до листа", + "tabs": { + "all": "все", + "groups": "Групи" + }, + "shared": { + "title": "Спільний доступ" + }, + "address_books": { + "create": "Нова адресна книга", + "created": "Адресну книгу створено", + "create_failed": "Не вдалося створити адресну книгу", + "title": "Мої адресні книги", + "shared_prefix": "Спільно: {name}", + "moved": "Контакт переміщено до {name}", + "moved_plural": "{count} контактів переміщено до {name}", + "move_failed": "Не вдалося перемістити контакт", + "address_book": "Адресна книга", + "rename": "Перейменувати адресну книгу", + "name_label": "Назва адресної книги", + "renamed": "Адресну книгу перейменовано", + "rename_failed": "Не вдалося перейменувати адресну книгу", + "default": "За замовчуванням", + "set_default": "Зробити типовою", + "default_updated": "Типову адресну книгу оновлено", + "set_default_failed": "Не вдалося встановити типову адресну книгу", + "manage": "Керуйте адресними книгами", + "share": "Поділитися адресною книгою", + "new_contact_in_book": "Новий контакт у цій адресній книзі", + "delete": "Видалити адресну книгу", + "confirm_delete": "Видалити «{name}»? Усі контакти в цій адресній книзі будуть видалені.", + "deleted": "Адресну книгу видалено", + "delete_failed": "Не вдалося видалити адресну книгу" + }, + "detail": { + "emails": "Адреси електронної пошти", + "phones": "Номери телефонів", + "organizations": "організації", + "addresses": "Адреси", + "notes": "Примітки", + "titles": "Звання та ролі", + "online_services": "Онлайн-сервіси", + "anniversaries": "Ювілеї", + "personal_info": "Особиста інформація", + "languages": "Мови", + "categories": "Категорії", + "related_contacts": "Пов'язані контакти", + "crypto_keys": "Крипто ключі", + "cert_issuer": "Видавець", + "cert_expires": "Термін дії закінчується", + "cert_expired": "Термін дії минув", + "cert_fingerprint": "Відбиток пальця", + "cert_algorithm": "Алгоритм", + "import_to_smime": "Імпортувати в S/MIME", + "cert_already_imported": "Уже імпортовано в S/MIME", + "cert_imported": "Сертифікат імпортовано до магазину S/MIME", + "cert_import_failed": "Не вдалося імпортувати сертифікат", + "no_contact_selected": "Виберіть контакт, щоб переглянути деталі", + "compose_email": "Написати електронний лист", + "copy_email": "Копіювати електронну пошту", + "copy_phone": "Копіювати номер телефону", + "copy_url": "Копіювати URL", + "copied": "Скопійовано в буфер обміну", + "copy_failed": "Не вдалося скопіювати в буфер обміну", + "created": "Створено", + "updated": "Останнє оновлення", + "timezone": "Часовий пояс", + "anniversary_birth": "день народження", + "anniversary_death": "Померла", + "anniversary_wedding": "Ювілей", + "anniversary_other": "інше", + "personal_expertise": "Експертиза", + "personal_hobby": "хобі", + "personal_interest": "Інтерес", + "personal_other": "інше", + "gender": "Стать", + "gender_masculine": "Чоловік", + "gender_feminine": "Жінка", + "gender_other": "інше", + "gender_none": "Не застосовується", + "gender_unknown": "Невідомий", + "calendar": "Календар", + "calendar_uri": "URL-адреса календаря", + "scheduling_uri": "URL-адреса планування", + "freebusy_uri": "Вільний/зайнятий URL", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "Більше дій", + "age_years": "{count, plural, one {# рік} few {# роки} many {# років} other {# років}}", + "years_since": "{count, plural, one {# рік тому} few {# роки тому} many {# років тому} other {# років тому}}" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "Новий контакт", + "edit_title": "Редагувати контакт", + "section_address_book": "Довідник", + "select_address_book": "Виберіть каталог...", + "section_identity": "Ім'я та ідентифікація", + "contact_type": "Тип контакту", + "type_person": "Людина", + "type_organization": "організація", + "section_work": "Робота та організація", + "prefix": "Префікс", + "prefix_placeholder": "доктор, пан, місіс", + "given_name": "ім'я", + "middle_name": "По батькові", + "surname": "Прізвище", + "suffix": "Суфікс", + "suffix_placeholder": "мол., ст., ІІІ ст", + "nickname": "псевдонім", + "nickname_placeholder": "псевдонім", + "email": "Електронна пошта", + "email_placeholder": "email@example.com", + "phone": "Телефон", + "phone_placeholder": "+1 234 567 890", + "phone_type": "Тип", + "phone_voice": "Голос", + "phone_cell": "мобільний", + "phone_fax": "Факс", + "phone_pager": "Пейджер", + "phone_video": "відео", + "phone_text": "текст", + "organization": "організація", + "organization_placeholder": "Назва компанії", + "department": "відділ", + "department_placeholder": "відділ", + "job_title": "Назва посади", + "job_title_placeholder": "наприклад, інженер-програміст", + "role": "Роль", + "role_placeholder": "наприклад, Керівник групи", + "addresses": "Адреси", + "add_address": "Додайте адресу", + "street": "вул", + "city": "Місто", + "region": "Штат / Регіон", + "postcode": "Поштовий індекс", + "country": "Країна", + "online_services": "Онлайн-сервіси", + "add_online_service": "Додати онлайн-сервіс", + "url_placeholder": "https://...", + "service_placeholder": "Сервіс", + "anniversaries": "Ювілеї", + "add_anniversary": "Додайте дату", + "anniversary_birth": "день народження", + "anniversary_wedding": "Ювілей", + "anniversary_death": "Меморіал", + "anniversary_other": "інше", + "personal_info": "Особиста інформація", + "add_personal_info": "Додати запис", + "personal_info_placeholder": "наприклад, фотографія", + "personal_expertise": "Експертиза", + "personal_hobby": "хобі", + "personal_interest": "Інтерес", + "personal_other": "інше", + "level": "Рівень", + "level_high": "Високий", + "level_medium": "Середній", + "level_low": "Низький", + "categories": "Категорії", + "categories_placeholder": "наприклад, родина, друзі, колеги", + "categories_hint": "Введіть для пошуку або додайте категорії", + "category_add": "додати", + "note": "Примітки", + "note_placeholder": "Додати примітку...", + "gender": "Стать", + "gender_sex": "секс", + "gender_male": "Чоловік", + "gender_female": "Жінка", + "gender_other": "інше", + "gender_none": "Не застосовується", + "gender_unknown": "Невідомий", + "gender_identity": "Гендерна ідентичність", + "gender_identity_placeholder": "Гендерна ідентичність...", + "calendar": "Календар", + "calendar_uri": "URL-адреса календаря", + "scheduling_uri": "URL-адреса планування", + "freebusy_uri": "Вільний/зайнятий URL", + "context_work": "Робота", + "context_private": "Приватний", + "add_email": "Додайте адресу електронної пошти", + "add_phone": "Додати телефон", + "save": "зберегти", + "cancel": "Скасувати", + "creating": "Створення...", + "updating": "Оновлення...", + "name_required": "Вкажіть ім'я, прізвище або організацію", + "email_invalid": "Введіть дійсну електронну адресу", + "email_error_inline": "Недійсний формат електронної пошти", + "save_failed": "Не вдалося зберегти контакт", + "delete": "Видалити", + "upload_photo": "Завантажити фото", + "remove_photo": "Видалити фото", + "photo_hint": "JPG або PNG, до 10 МБ. Буде зменшено.", + "photo_too_large": "Зображення завелике (макс. 10 МБ)", + "photo_invalid": "Недійсний файл зображення", + "change_photo": "Змінити" + }, + "groups": { + "create": "Нова група", + "edit": "Редагувати групу", + "empty": "Груп ще немає", + "delete_confirm_title": "Видалити групу", + "delete_confirm": "Ви впевнені, що хочете видалити цю групу?", + "name_label": "Назва групи", + "name_placeholder": "наприклад, Команда, Сім’я", + "name_required": "Потрібно вказати назву групи", + "save_failed": "Не вдалося зберегти групу", + "members_label": "Члени", + "search_members": "Пошук контактів для додавання...", + "no_members": "У цій групі немає учасників", + "member_count": "{count, plural, =0 {Немає учасників} one {1 учасник} few {# учасники} many {# учасників} other {# учасників}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "Імпортувати контакти", + "drop_hint": "Натисніть, щоб вибрати файл vCard", + "file_types": "файли .vcf або .vcard", + "no_contacts": "У файлі не знайдено контактів", + "parse_error": "Не вдалося проаналізувати файл vCard", + "found": "{count, plural, one {1 контакт знайдено} few {# контакти знайдено} many {# контактів знайдено} other {# контактів знайдено}}", + "duplicate": "дублікат", + "select_all": "Вибрати все", + "deselect_all": "Зняти вибір із усіх", + "selected": "{count, plural, one {1 вибрано} few {# вибрано} many {# вибрано} other {# вибрано}}", + "import_button": "Імпорт", + "importing": "Імпорт...", + "success": "{count, plural, one {1 контакт імпортовано} few {# контакти імпортовано} many {# контактів імпортовано} other {# контактів імпортовано}}", + "failed": "Помилка імпорту", + "close": "Закрити", + "file_too_large": "Файл завеликий (макс. 5 МБ)" + }, + "export": { + "title": "Експортувати контакти", + "success": "{count, plural, one {1 контакт експортовано} few {# контакти експортовано} many {# контактів експортовано} other {# контактів експортовано}}" + }, + "bulk": { + "selected": "{count, plural, one {1 вибрано} few {# вибрано} many {# вибрано} other {# вибрано}}", + "select_all": "Вибрати все", + "delete": "Видалити", + "delete_confirm_title": "Видалити контакти", + "delete_confirm": "Видалити {count, plural, one {1 contact} other {# contacts}}?", + "deleted": "{count, plural, one {1 контакт видалено} few {# контакти видалено} many {# контактів видалено} other {# контактів видалено}}", + "add_to_group": "Додати до групи", + "choose_group": "Виберіть групу", + "adding_contacts": "Додавання {count, plural, one {1 contact} other {# contacts}}", + "added_to_group": "Контакти додано до групи", + "export": "Експорт", + "clear": "Очистити вибір" + }, + "toast": { + "created": "Контакт створено", + "updated": "Контакт оновлено", + "deleted": "Контакт видалено", + "error_create": "Не вдалося створити контакт", + "error_update": "Не вдалося оновити контакт", + "error_delete": "Не вдалося видалити контакт" + }, + "context_menu": { + "open": "Відкрити", + "edit": "Редагувати", + "send_email": "Надіслати лист", + "add_to_group": "Додати до групи", + "export_vcard": "Експортувати як vCard", + "delete": "Видалити", + "call": "Зателефонувати", + "duplicate": "Дублювати", + "print": "Друк" + }, + "filters": { + "toggle": "Фільтри", + "select": "Вибрати", + "clear": "Очистити", + "close": "Закрити", + "title": "Розширені фільтри", + "organization": "Компанія", + "organization_placeholder": "напр. Acme", + "job_title": "Посада", + "job_title_placeholder": "напр. Дизайнер", + "location": "Місцезнаходження", + "location_placeholder": "Місто або країна", + "email_domain": "Домен e-mail", + "email_domain_placeholder": "priklad.ua", + "birthday_month": "День народження в", + "any_month": "Будь-який місяць", + "has_email": "З ел. поштою", + "has_phone": "З телефоном", + "has_photo": "З фото", + "sort_by": "Сортувати за", + "sort_first_name": "Ім'я", + "sort_last_name": "Прізвище" + }, + "open_categories": "Відкрити категорії" + }, + "calendar": { + "title": "Календар", + "back_to_email": "Повернутися до електронної пошти", + "back_to_month": "Назад до місяця", + "my_calendars": "Календарі", + "birthday_calendar": "Дні народження", + "mini_calendar_change": "Натисніть, щоб змінити місяць", + "views": { + "month": "місяць", + "week": "тиждень", + "day": "День", + "agenda": "Порядок денний", + "today": "Сьогодні", + "month_hint": "місяць (м)", + "week_hint": "Тиждень (ж)", + "day_hint": "День (d)", + "agenda_hint": "Порядок денний (а)", + "tasks": "завдання", + "tasks_hint": "Завдання (k)" + }, + "events": { + "hidden_before": "{count, plural, one {# раніша подія} few {# раніші події} many {# раніших подій} other {# раніших події}}", + "hidden_after": "{count, plural, one {# пізніша подія} few {# пізніші події} many {# пізніших подій} other {# пізніших події}}", + "show_all_hours": "Показати всі години", + "show_display_hours": "Показати лише {start} – {end}", + "create": "Створити подію", + "edit": "Редагувати подію", + "delete": "Видалити подію", + "details": "Деталі події", + "no_events": "Подій немає", + "all_day": "Весь день", + "more": "+ ще {count}", + "show_more": "Показувати більше", + "show_less": "Показувати менше", + "no_title": "(Без назви)", + "resize": "Змінити розмір події", + "duplicate": "дублікат", + "today_header": "Сьогодні", + "tomorrow_header": "завтра", + "agenda_show_earlier": "Показати попередні події", + "agenda_loading": "Завантаження…", + "agenda_range_start": "Події з {date}", + "agenda_range_end": "Події до {date}", + "export_ics": "Експортувати як .ics", + "copy_title": "Скопіювати назву", + "copy_link": "Скопіювати посилання зустрічі", + "go_to_today": "Перейти до сьогодні", + "new_all_day_event": "Нова подія на весь день", + "new_event": "Нова подія", + "new_task": "Нове завдання" + }, + "detail": { + "add_note": "Додати примітку...", + "save_note": "зберегти", + "note_saved": "Примітка додана", + "open_link": "Відкрити посилання", + "copy_location": "Копіювати місце", + "meeting_link": "Посилання на зустріч", + "tentative": "Орієнтовний", + "cancelled": "Скасовано", + "delete_confirm": "Видалити цю подію?" + }, + "form": { + "title": "Назва", + "description": "опис", + "location": "Розташування", + "meeting_link": "Посилання на зустріч", + "start_date": "Дата початку", + "end_date": "Кінцева дата", + "start_time": "Час початку", + "end_time": "Час закінчення", + "all_day_event": "Подія на весь день", + "calendar_select": "Календар", + "save": "зберегти", + "cancel": "Скасувати", + "delete_confirm": "Ви впевнені, що хочете видалити цю подію?", + "color": "колір" + }, + "participants": { + "availability": { + "title": "Доступність", + "free": "Вільний", + "busy": "Зайнятий", + "tentative": "Попередньо", + "unknown": "Не користувач цього сервера", + "checking": "Перевірка…" + }, + "title": "Учасники", + "add": "Додати учасника", + "organizer": "Організатор", + "attendee": "Присутній", + "accepted": "прийнято", + "declined": "Відхилено", + "tentative": "Орієнтовний", + "needs_action": "Потребує дії", + "remove": "видалити", + "edit": "Редагувати", + "email_placeholder": "Додайте електронну адресу або знайдіть контакти", + "send_invitations": "Надішліть запрошення учасникам", + "status_summary": "{accepted} прийнято, {pending} очікує на розгляд", + "invited_by": "Запрошення від {name}", + "respond_below": "Відповісти за допомогою кнопок нижче", + "rsvp_label": "Ваша відповідь", + "cancel_notification": "Учасники будуть повідомлені про скасування", + "you_organizer": "Ви організатор", + "you_attendee": "Ви учасник", + "no_participants": "Немає учасників", + "count": "{count, plural, one {# учасник} few {# учасники} many {# учасників} other {# учасників}}" + }, + "recurrence": { + "title": "Повторюваність", + "none": "Не повторюється", + "daily": "Щодня", + "weekly": "Щотижня", + "monthly": "Щомісяця", + "yearly": "Щорічно", + "every_n_days": "Кожні {count} днів", + "every_n_weeks": "Кожні {count} тижнів", + "every_n_months": "Кожні {count} місяців", + "until": "Поки", + "occurrences": "{count} випадків", + "custom": "Власний…", + "edit_custom": "Змінити власне повторення", + "every_n_years": "Кожні {count} років", + "on_days": "у {days}", + "on_day_n": "{day}-го числа", + "on_the_nth": "у {nth} {day}", + "in_month": "у {month}", + "nth_1": "перший", + "nth_2": "другий", + "nth_3": "третій", + "nth_4": "четвертий", + "nth_last": "останній", + "editor_freq_day": "День", + "editor_freq_week": "Тиждень", + "editor_freq_month": "Місяць", + "editor_freq_year": "Рік", + "editor_repeats_on": "Повторюється", + "editor_every": "Кожні", + "editor_unit_days": "дн.", + "editor_unit_weeks": "тиж.", + "editor_unit_months": "міс.", + "editor_unit_years": "р.", + "editor_on_day": "у день", + "editor_on_the": "у", + "editor_in": "у місяці", + "editor_ends": "Закінчується", + "editor_never": "Ніколи", + "editor_ends_on": "Дата", + "editor_ends_after": "Після", + "editor_occurrences": "повторень" + }, + "recurrence_scope": { + "edit_title": "Редагувати повторювану подію", + "delete_title": "Видалити повторювану подію", + "description": "Це повторювана подія. Які події ви б хотіли змінити?", + "this_event": "Тільки ця подія", + "this_and_future": "Ця та наступні події", + "all_events": "Всі події", + "cancel": "Скасувати", + "save": "зберегти", + "delete": "Видалити", + "rsvp_title": "Відповісти на повторювану подію", + "rsvp_description": "Це повторювана подія. До яких подій стосується ваша відповідь?", + "respond": "Відповісти" + }, + "alerts": { + "title": "Нагадування", + "none": "Жодного нагадування", + "at_time": "На момент події", + "minutes_before": "{count, plural, one {# хвилина до} few {# хвилини до} many {# хвилин до} other {# хвилин до}}", + "hours_before": "{count, plural, one {# година до} few {# години до} many {# годин до} other {# годин до}}", + "days_before": "{count, plural, one {# день до} few {# дні до} many {# днів до} other {# днів до}}", + "weeks_before": "{count, plural, one {# тиждень до} few {# тижні до} many {# тижнів до} other {# тижнів до}}", + "unit_minutes_before": "хвилин до", + "unit_hours_before": "годин до", + "unit_days_before": "днів до", + "unit_weeks_before": "тижнів до", + "add": "Додати нагадування", + "remove": "Видалити нагадування", + "amount": "Кількість нагадування", + "unit": "Одиниця нагадування", + "action": "Тип нагадування", + "action_display": "Сповіщення", + "action_email": "Ел. пошта", + "by_email": "{label} електронною поштою", + "kept": "{count, plural, one {Зберігається ще # нагадування, яке не можна змінити тут} few {Зберігаються ще # нагадування, які не можна змінити тут} many {Зберігаються ще # нагадувань, які не можна змінити тут} other {Зберігаються ще # нагадування, які не можна змінити тут}}", + "other": "{count, plural, one {Ще # нагадування} few {Ще # нагадування} many {Ще # нагадувань} other {Ще # нагадування}}", + "calendar_defaults": "Типові нагадування календаря", + "customize": "Налаштувати", + "use_defaults": "Використовувати типові нагадування календаря" + }, + "settings": { + "limit_hours": "Обмежити видимі години", + "limit_hours_desc": "Показувати в поданнях дня і тижня лише ці години. Події поза ними доступні через стрілки вгорі та внизу кожного дня.", + "visible_hours": "Видимі години", + "visible_hours_start": "Початок видимих годин", + "visible_hours_end": "Кінець видимих годин", + "hide_non_working_days": "Приховувати неробочі дні", + "hide_non_working_days_desc": "Не показувати в поданні тижня дні, коли ви не працюєте. Їхні події й далі видно в поданнях місяця, дня та порядку денного.", + "working_days": "Робочі дні", + "title": "Налаштування календаря", + "default_view": "Перегляд за замовчуванням", + "week_starts_on": "Тиждень починається о", + "time_format": "Формат часу", + "default_calendar": "Календар за замовчуванням", + "default_reminder": "Нагадування за умовчанням", + "time_format_12h": "12-годинний", + "time_format_24h": "24 години", + "notifications_enabled": "Сповіщення про події", + "notifications_enabled_desc": "Показувати сповіщення про майбутні події календаря", + "notification_sound": "Звук сповіщення", + "notification_sound_desc": "Відтворення звуку для сповіщень календаря", + "invitation_parsing": "Проаналізуйте запрошення електронною поштою", + "invitation_parsing_desc": "Виявляти запрошення календаря у вкладеннях електронної пошти та показувати дії календаря", + "show_time_in_month_view": "Показати час у режимі перегляду місяця", + "show_time_in_month_view_desc": "Відображення часу подій у місячному календарі", + "show_week_numbers": "Показати номери тижнів", + "show_week_numbers_desc": "Відображення номерів тижнів у міні-календарі", + "calendar_free_scroll": "Вільне прокручування", + "calendar_free_scroll_desc": "Безперервно прокручувати місяці, тижні та дні замість одного періоду за раз", + "enable_tasks": "Увімкнути завдання", + "enable_tasks_desc": "Показати перегляд завдань у календарі для керування справами", + "show_tasks_on_calendar": "Показати завдання в календарі", + "show_tasks_on_calendar_desc": "Відображати завдання в режимах перегляду календаря місяця, тижня та дня", + "hover_preview": "Попередній перегляд подій", + "hover_preview_desc": "Показувати спливаюче вікно з деталями під час наведення курсора на події календаря", + "hover_preview_instant": "Миттєво", + "hover_preview_delay_500ms": "0,5-секундна затримка", + "hover_preview_delay_1s": "1-секундна затримка", + "hover_preview_delay_2s": "2-секундна затримка", + "hover_preview_off": "Вимкнено", + "organizer_identity": "Організовувати запрошення як", + "organizer_identity_desc": "Адреса календаря, що використовується як організатор, коли ви запрошуєте людей на події.", + "organizer_identity_failed": "Не вдалося змінити адресу організатора", + "show_birthday_calendar": "Контактний календар днів народження", + "show_birthday_calendar_desc": "Показати віртуальний календар із днями народження ваших контактів" + }, + "days": { + "monday": "понеділок", + "tuesday": "вівторок", + "wednesday": "Середа", + "thursday": "четвер", + "friday": "П'ятниця", + "saturday": "Субота", + "sunday": "неділя", + "mon": "Пн", + "tue": "Вт", + "wed": "ср", + "thu": "чт", + "fri": "пт", + "sat": "сб", + "sun": "сонце" + }, + "notifications": { + "event_created": "Подія створена", + "event_updated": "Подію оновлено", + "event_deleted": "Подію видалено", + "calendar_created": "Календар створено", + "calendar_deleted": "Календар видалено", + "event_move_error": "Не вдалося перемістити подію", + "event_resize_error": "Не вдалося змінити розмір події", + "alert_title": "Майбутня подія", + "alert_now": "Починаючи зараз", + "alert_in_minutes": "Через {count} хв", + "invitation_sent": "Запрошення надіслано", + "invitations_denied_title": "Не вдається надіслати запрошення", + "invitations_denied": "Сервер відмовився надіслати запрошення: {reason}. Зберегти подію без їх надсилання?", + "save_without_invitations": "Зберегти без запрошень", + "rsvp_updated": "Відповідь оновлено", + "rsvp_error": "Не вдалося оновити відповідь", + "event_duplicated": "Подія дублюється", + "event_error": "Не вдалося зберегти подію", + "task_due": "Термін виконання завдання", + "event_exported": "Подію експортовано", + "title_copied": "Назву скопійовано", + "link_copied": "Посилання скопійовано" + }, + "status": { + "loading_calendars": "Завантаження календарів...", + "loading_events": "Завантаження подій..." + }, + "quick_create": { + "placeholder": "Нова назва події", + "aria_label": "Швидке створення події", + "add_title": "Додайте назву", + "more_options": "Більше параметрів" + }, + "nav_prev": "Попередній", + "nav_next": "Далі", + "import": { + "title": "Імпорт календаря", + "tab_file": "Файл", + "tab_url": "URL", + "select_file": "Виберіть файл .ics", + "drop_file": "або перетягніть файл сюди", + "supported_formats": "Підтримує файли iCalendar (.ics).", + "url_description": "Введіть URL-адресу зовнішнього каналу iCalendar (.ics), щоб імпортувати події.", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "Підтримує URL-адреси iCalendar (.ics).", + "fetch": "Принести", + "invalid_url": "Введіть дійсну URL-адресу", + "url_fetch_failed": "Не вдалося отримати календар з URL-адреси", + "parsing": "Розбір файлу календаря...", + "parsed_events": "Знайдено подій: {count}", + "no_events": "У файлі не знайдено подій", + "select_all": "Вибрати все", + "deselect_all": "Зняти вибір із усіх", + "target_calendar": "Імпорт до календаря", + "import_button": "Вибраний імпорт", + "importing": "Імпорт подій...", + "success": "{count} подій успішно імпортовано", + "error": "Не вдалося імпортувати календар", + "file_too_large": "Файл перевищує обмеження в 10 Мб", + "invalid_format": "Недійсний формат файлу календаря" + }, + "webcal_action": { + "title": "Відкрити посилання календаря", + "description": "Як ви хочете використати \"{name}\"?", + "import_title": "Імпортувати один раз", + "import_description": "Завантажити події зараз і скопіювати їх до одного з ваших календарів.", + "subscribe_title": "Підписатися", + "subscribe_description": "Автоматично синхронізувати цей календар як окремий календар.", + "cancel": "Скасувати" + }, + "management": { + "title": "Управління календарем", + "description": "Створюйте, перейменовуйте та налаштовуйте свої календарі. Клацніть правою кнопкою миші календар на бічній панелі, щоб швидко змінити його колір.", + "name": "Ім'я", + "name_placeholder": "Назва календаря", + "color": "колір", + "change_color": "Змінити колір", + "random_color": "Новий випадковий колір", + "add_calendar": "Додати календар", + "edit": "Редагувати", + "delete": "Видалити", + "save": "зберегти", + "create": "Створити", + "cancel": "Скасувати", + "default": "За замовчуванням", + "confirm_delete": "Видалити \"{name}\"? Усі події в цьому календарі буде видалено.", + "confirm_clear": "Очистити всі події з \"{name}\"? Це неможливо скасувати.", + "set_default": "Зробити типовим", + "default_updated": "Типовий календар оновлено", + "error_default": "Не вдалося встановити типовий календар", + "clear_events": "Ясні події", + "events_cleared": "{count} подій видалено", + "error_clear": "Не вдалося очистити події календаря", + "calendar_created": "Календар створено", + "calendar_updated": "Календар оновлено", + "calendar_deleted": "Календар видалено", + "color_updated": "Оновлено колір календаря", + "error_create": "Не вдалося створити календар", + "error_update": "Не вдалося оновити календар", + "error_delete": "Не вдалося видалити календар", + "caldav_url": "URL-адреса CalDAV", + "copy_url": "Скопіюйте URL-адресу CalDAV", + "url_copied": "URL-адресу CalDAV скопійовано в буфер обміну", + "share": "Поділитися календарем", + "new_event_in_calendar": "Нова подія в цьому календарі", + "shared_with": "Доступ надано: {names}", + "shared_with_count": "{count, plural, one {Доступ надано # особі} few {Доступ надано # особам} many {Доступ надано # особам} other {Доступ надано # особи}}", + "move_up": "Перемістити вгору", + "move_down": "Перемістити вниз", + "kind": "Вміст", + "kind_events": "Події", + "kind_tasks": "Завдання", + "kind_both": "Події та завдання", + "kind_hint": "Визначає, які застосунки бачать цей календар під час синхронізації через CalDAV. Пізніше змінити не можна.", + "default_alerts": "Типові нагадування", + "default_alerts_title": "Типові нагадування для «{name}»", + "default_alerts_hint": "Нові події в цьому календарі отримують ці нагадування, якщо їх не змінити в самій події. Зміни тут не впливають на наявні події. Нагадування електронною поштою надсилає поштовий сервер.", + "default_alerts_timed": "Події з часом", + "default_alerts_all_day": "Події на весь день", + "default_alerts_updated": "Типові нагадування оновлено" + }, + "subscription": { + "title": "Підписка iCal", + "section_title": "Підписки iCal", + "description": "Підпишіться на зовнішній канал iCalendar. Події будуть автоматично синхронізовані з власним календарем. Підтримує URL-адреси https:// і webcal://.", + "url_label": "URL-адреса календаря", + "url_placeholder": "https://example.com/calendar.ics або webcal://...", + "name_label": "Назва календаря", + "name_placeholder": "напр. Державні свята", + "color_label": "колір", + "refresh_interval": "Інтервал оновлення", + "interval_15": "Кожні 15 хвилин", + "interval_30": "Кожні 30 хв", + "interval_60": "Щогодини", + "interval_360": "Кожні 6 годин", + "interval_1440": "Кожен день", + "subscribe": "Підпишіться", + "subscribing": "Підписка...", + "save": "Зберегти зміни", + "saving": "Збереження...", + "edit": "Редагувати", + "edit_title": "Редагувати підписку", + "updated": "Оновлено \"{name}\"", + "update_error": "Не вдалося оновити підписку", + "invalid_url": "Введіть дійсну URL-адресу", + "success": "Підписався на \"{name}\"", + "error": "Не вдалося додати підписку", + "refresh": "Оновіть зараз", + "refresh_success": "Підписку оновлено", + "refresh_error": "Не вдалося оновити підписку", + "unsubscribe": "Відписатися", + "confirm_delete": "Скасувати підписку на \"{name}\"? Календар і всі його події буде видалено.", + "deleted": "Підписку видалено", + "delete_error": "Не вдалося видалити підписку", + "last_refreshed": "Останнє оновлення: {time}" + }, + "tasks": { + "label": "завдання", + "no_tasks": "Завдань немає", + "no_title": "(Без назви)", + "mark_complete": "Позначити як завершене", + "mark_incomplete": "Позначити як незавершене", + "filter_all": "все", + "filter_pending": "В очікуванні", + "filter_completed": "Виконано", + "filter_overdue": "Прострочена", + "show_completed": "Показ завершено", + "create": "Нове завдання", + "edit": "Редагувати завдання", + "title_placeholder": "Назва завдання", + "description_placeholder": "Додайте опис...", + "due_date": "Термін виконання", + "include_time": "Включіть час", + "priority": "Пріоритет", + "priority_none": "Жодного", + "priority_high": "Високий", + "priority_medium": "Середній", + "priority_low": "Низький", + "progress": "Статус", + "progress_needs_action": "Потребує дії", + "progress_in_process": "В процесі", + "progress_completed": "Виконано", + "progress_cancelled": "Скасовано", + "calendar": "Календар", + "alert": "Нагадування", + "alert_none": "Жодного", + "alert_at_time": "У встановлений термін", + "alert_5min": "5 хвилин до", + "alert_15min": "за 15 хвилин до", + "alert_30min": "за 30 хвилин до", + "alert_1hr": "за 1 годину до", + "alert_1day": "за 1 день до", + "delete": "Видалити", + "cancel": "Скасувати", + "save": "зберегти", + "quick_add_placeholder": "Додати завдання...", + "due_today": "Сьогодні", + "due_tomorrow": "завтра", + "overdue": "Прострочена" + }, + "months": { + "jan": "січ.", + "feb": "лют.", + "mar": "бер.", + "apr": "квіт.", + "may": "трав.", + "jun": "черв.", + "jul": "лип.", + "aug": "серп.", + "sep": "верес.", + "oct": "жовт.", + "nov": "лист.", + "dec": "груд.", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "Відкрити меню", + "toolbar": { + "more": "Інші дії", + "select_view": "Змінити вигляд, зараз {view}" + }, + "other_calendars": "Інші календарі", + "calendar_options": "Параметри: {name}" + }, + "advanced_search": { + "title": "Розширений пошук", + "from": "Від", + "from_placeholder": "Електронна пошта або ім’я відправника", + "to": "до", + "to_placeholder": "Електронна адреса або ім’я одержувача", + "subject": "Тема", + "subject_placeholder": "Предмет містить...", + "body": "Тіло", + "body_placeholder": "Тіло містить...", + "folder": "Папка", + "all_folders": "Всі папки", + "all_folders_except_spam_trash": "Всі папки, крім спаму та кошика", + "has_attachment": "Додатки", + "date_after": "Після", + "date_before": "Раніше", + "size_min": "Більше ніж (КБ)", + "size_max": "Менше ніж (КБ)", + "starred": "Позначено зірочкою", + "unread": "Непрочитаний", + "read": "Прочитайте", + "yes": "так", + "no": "немає", + "clear": "ясно", + "clear_all": "Очистити все", + "filters_active": "фільтр {count}", + "filters_active_plural": "{count} фільтрів", + "toggle_filters": "більше", + "search_hint": "Використовуйте розширені фільтри для точного пошуку", + "advanced_filters_tooltip": "Розширені фільтри пошуку", + "results_found": "{count, plural, =0 {Результатів не знайдено} one {# результат знайдено} few {# результати знайдено} many {# результатів знайдено} other {# результатів знайдено}}", + "results_found_more": "Знайдено {count}+ результатів", + "suggestions_recent": "Останні пошуки", + "suggestions_people": "Люди", + "suggestions_from": "Шукати листи від {address}", + "suggestions_to": "Шукати листи для {address}", + "suggestions_remove_recent": "Видалити з останніх пошуків", + "has_words": "Містить слова", + "size": "Розмір", + "date_within": "Дата", + "search_in": "Шукати в", + "has_attachment_check": "Є вкладення", + "search": "Шукати" + }, + "welcome": { + "title": "Ласкаво просимо до вашої поштової скриньки", + "tip_compose": "Натисніть c, щоб створити новий електронний лист", + "tip_shortcuts": "Натисніть ? щоб побачити всі комбінації клавіш", + "tip_sidebar": "Знайдіть Календар, Контакти та Налаштування в меню бічної панелі", + "tip_settings": "Налаштуйте свій досвід у налаштуваннях", + "tip_compose_touch": "Торкніться кнопки з олівцем, щоб написати новий лист", + "tip_sidebar_touch": "Календар, Контакти й Налаштування є на нижній панелі", + "got_it": "зрозумів", + "settings": "Налаштування", + "dismiss": "Відхилити", + "start_tour": "Розпочати тур" + }, + "demo_welcome": { + "title": "Ласкаво просимо до Bulwark Mail", + "description": "Відкрийте для себе повнофункціональний клієнт веб-пошти - прямо у вашому браузері. Усі дані залишаються на вашому пристрої, тому сміливо тестуйте все.", + "feature_email": "Читайте та створюйте електронний лист", + "feature_organize": "Теги, зірочки та папки", + "feature_shortcuts": "Комбінації клавіш", + "feature_privacy": "100% приватна демонстрація", + "hint": "Натисніть будь-яку електронну пошту ліворуч, щоб розпочати, або перегляньте огляд нижче." + }, + "files": { + "office_edit": "Редагувати документ", + "office_loading": "Відкриття редактора документів…", + "office_error": "Не вдалося відкрити редактор документів.", + "office_close": "Закрити редактор", + "open_in_new_tab": "Відкрити в новій вкладці", + "title": "Файли", + "search_placeholder": "Пошук файлів...", + "empty_state_title": "Файлів ще немає", + "upload_files": "Завантажити файли", + "new_folder": "Нова папка", + "new_folder_name": "Назва папки", + "rename": "Перейменувати", + "rename_title": "Перейменувати", + "new_name": "Нова назва", + "name_forbidden_chars": "Сервер не дозволяє ці символи в назвах: {chars}", + "name_reserved": "Ця назва зарезервована на сервері.", + "delete": "Видалити", + "delete_confirm_title": "Видалити ресурс", + "delete_confirm_message": "Ви впевнені, що хочете видалити \"{name}\"? Це неможливо скасувати.", + "download": "Завантажити", + "name": "Ім'я", + "size": "Розмір", + "modified": "Змінено", + "type": "Тип", + "folder": "Папка", + "file": "Файл", + "uploading": "Завантаження...", + "upload_success": "{count, plural, one {1 файл завантажено} few {# файли завантажено} many {# файлів завантажено} other {# файлів завантажено}}", + "upload_canceled": "Завантаження скасовано", + "upload_canceled_partial": "{count, plural, one {# файл було завантажено} few {# файли було завантажено} many {# файлів було завантажено} other {# файлу було завантажено}} до скасування", + "upload_error": "Не вдалося завантажити файл", + "create_folder_success": "Папка створена", + "create_folder_error": "Не вдалося створити папку", + "delete_success": "Успішно видалено", + "delete_error": "Не вдалося видалити", + "rename_success": "Успішно перейменовано", + "rename_error": "Не вдалося перейменувати", + "download_error": "Не вдалося завантажити", + "not_available": "Зберігання файлів недоступне на цьому сервері", + "cancel": "Скасувати", + "create": "Створити", + "save": "зберегти", + "no_results": "Немає файлів, що відповідають вашому пошуку", + "batch_delete_confirm_message": "Ви впевнені, що хочете видалити {count, plural, one {1 item} other {# items}}? Це неможливо скасувати.", + "batch_delete_success": "{count, plural, one {1 елемент видалено} few {# елементи видалено} many {# елементів видалено} other {# елементів видалено}}", + "grid_view": "Вигляд сітки", + "list_view": "Перегляд списку", + "details": "Подробиці", + "path": "шлях", + "preview": "Попередній перегляд", + "preview_error": "Не вдалося завантажити попередній перегляд", + "cut": "Вирізати", + "copy": "Копія", + "paste": "Вставити", + "move_success": "{count, plural, one {1 елемент переміщено} few {# елементи переміщено} many {# елементів переміщено} other {# елементів переміщено}}", + "move_error": "Не вдалося переміститися", + "paste_success": "Вставлено успішно", + "paste_error": "Не вдалося вставити", + "new_text_file": "Новий текстовий файл", + "file_name": "Ім'я файлу", + "retry": "Повторіть спробу", + "refresh": "Оновити", + "duplicate": "дублікат", + "duplicate_success": "Продубльовано успішно", + "duplicate_error": "Не вдалося скопіювати", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "Файл створено", + "create_file_error": "Не вдалося створити файл", + "favorites": "Вибране", + "recent": "Останні", + "show_in_folder": "Показати в папці", + "remove_from_recent": "Прибрати з останніх", + "upload_folder": "Завантажити папку", + "file_too_large": "\"{name}\" перевищує максимальний розмір файлу ({max})", + "undo": "Скасувати", + "undo_success": "Дія скасована", + "undo_error": "Не вдалося скасувати", + "toolbar": "Дії з файлами", + "file_list": "Файли та папки", + "context_menu": "Дії", + "settings_ascending": "Висхідний", + "settings_descending": "Спускається", + "disabled_title": "Функцію файлів вимкнено вашим адміністратором", + "disabled_description": "Завантаження великих файлів через WebDAV може спричинити нестабільність Stalwart/RocksDB, зокрема збої через брак пам’яті та невідновне використання диска. Видалені файли не можуть бути негайно очищені зі сховища BLOB-об’єктів. Ця функція не рекомендована для робочих середовищ.", + "no_accounts": "Немає підключених облікових записів.", + "open_folder_tree": "Відкрити дерево тек", + "migration_title": "Оновлення ваших файлів…", + "migration_description": "Папки та файли впорядковуються у правильну структуру. Це відбувається лише один раз.", + "new": "Створити", + "my_files": "Мої файли", + "folders": "Папки", + "files_heading": "Файли", + "open": "Відкрити", + "star": "Додати до вибраного", + "unstar": "Прибрати з вибраного", + "expand_folder": "Розгорнути {name}", + "collapse_folder": "Згорнути {name}", + "sidebar_hint": "Додавайте папки до вибраного й відкривайте файли, щоб знаходити їх тут.", + "breadcrumbs": "Шлях до папки", + "show_path": "Показати приховані папки в шляху", + "selection_actions": "Дії з вибраними елементами", + "selected_count": "{count, plural, one {Вибрано # елемент} few {Вибрано # елементи} many {Вибрано # елементів} other {Вибрано # елемента}}", + "clear_selection": "Скасувати вибір", + "select_all": "Вибрати все", + "select_item": "Вибрати {name}", + "more_actions": "Інші дії для {name}", + "paste_here": "Вставити в цю папку", + "paste_count": "{count, plural, one {Вставити # елемент} few {Вставити # елементи} many {Вставити # елементів} other {Вставити # елемента}}", + "sort_by": "Сортування", + "view_mode": "Вигляд", + "clear_search": "Очистити пошук", + "no_results_for": "У цій папці нічого не знайдено за запитом «{query}»", + "download_count": "{count, plural, one {Завантажити # файл} few {Завантажити # файли} many {Завантажити # файлів} other {Завантажити # файлу}}", + "delete_count": "{count, plural, one {Видалити # елемент} few {Видалити # елементи} many {Видалити # елементів} other {Видалити # елемента}}", + "close_details": "Закрити подробиці", + "file_kind": "Файл {ext}", + "folder_summary": "{folders, plural, =0 {Немає папок} one {# папка} few {# папки} many {# папок} other {# папки}}, {files, plural, =0 {немає файлів} one {# файл} few {# файли} many {# файлів} other {# файлу}}", + "details_hint": "Виберіть файл або папку, щоб переглянути подробиці.", + "sharing": "Спільний доступ", + "shared_with_count": "{count, plural, one {Доступ надано # особі} few {Доступ надано # особам} many {Доступ надано # особам} other {Доступ надано # особи}}", + "only_you": "Лише ви", + "empty_folder_title": "Ця папка порожня", + "empty_folder_description": "Перетягніть файли сюди або завантажте їх зі свого пристрою.", + "empty_folder_read_only": "Ви не можете додавати файли до цієї папки.", + "choose_account": "Виберіть обліковий запис", + "drop_to_upload": "Відпустіть, щоб завантажити в {folder}", + "uploading_count": "Завантаження {current} з {total}", + "dismiss": "Закрити", + "zoom_in": "Збільшити", + "zoom_out": "Зменшити", + "zoom_reset": "За розміром екрана", + "rotate": "Повернути", + "close_preview": "Закрити" + }, + "smime": { + "your_certificates": "Ваші сертифікати", + "your_certificates_desc": "Імпортуйте та керуйте своїми сертифікатами S/MIME для підпису та шифрування електронних листів", + "recipient_certificates": "Сертифікати отримувача", + "recipient_certificates_desc": "Публічні сертифікати для шифрування електронних листів одержувачам", + "identity_bindings": "Ідентифікаційні ключові прив’язки", + "identity_bindings_desc": "Прив’яжіть сертифікати S/MIME до вашої електронної пошти", + "defaults_title": "За замовчуванням", + "defaults_desc": "Налаштувати підписання та шифрування за замовчуванням", + "import_pkcs12": "Імпорт PKCS#12 (.p12/.pfx)", + "import_public_cert": "Імпортний сертифікат", + "no_certificates": "Сертифікати ще не імпортовано", + "no_recipient_certs": "Немає сертифікатів отримувача", + "expires": "Термін дії закінчується", + "expired": "Термін дії минув", + "bound_to": "Прив’язаний до", + "no_key_bound": "Жодного", + "lock": "Ключ від замка", + "unlock": "Ключ розблокування", + "details": "Переглянути деталі", + "delete": "Видалити", + "encrypt_by_default": "Шифрувати за замовчуванням", + "encrypt_by_default_desc": "Автоматично шифрувати електронні листи, коли всі одержувачі мають сертифікати", + "remember_unlocked": "Запам'ятайте незаблоковані ключі", + "remember_unlocked_desc": "Тримайте ключі розблокованими протягом цього сеансу браузера", + "sign_default_for": "Підписати за замовчуванням для", + "enter_p12_passphrase": "Введіть парольну фразу PKCS#12", + "p12_passphrase_desc": "Введіть парольну фразу, яка захищає цей файл сертифіката", + "enter_storage_passphrase": "Установіть парольну фразу для зберігання", + "storage_passphrase_desc": "Виберіть парольну фразу, щоб захистити цей ключ у вашому браузері", + "next": "Далі", + "import": "Імпорт", + "unlock_key": "Розблокувати ключ", + "unlock_key_desc": "Введіть парольну фразу для зберігання, щоб розблокувати цей ключ для підпису чи розшифровки", + "passphrase_placeholder": "Введіть парольну фразу", + "confirm_passphrase_placeholder": "Підтвердьте парольну фразу", + "passphrase_mismatch": "Парольні фрази не збігаються", + "cancel": "Скасувати", + "processing": "Обробка…", + "close": "Закрити", + "certificate_details": "Деталі сертифіката", + "cert_subject": "Суб'єкт", + "cert_issuer": "Видавець", + "cert_email": "Електронна пошта", + "cert_serial": "Серійний номер", + "cert_validity": "Термін дії", + "cert_fingerprint": "Відбиток пальця (SHA-256)", + "cert_algorithm": "Алгоритм", + "cert_capabilities": "Можливості", + "cert_source": "Джерело", + "cert_expired": "Термін дії цього сертифіката закінчився", + "cert_not_yet_valid": "Цей сертифікат ще не дійсний", + "cap_sign": "Підписання", + "cap_encrypt": "Шифрування", + "cap_none": "Жодного", + "show_passphrase": "Показати парольну фразу", + "hide_passphrase": "Приховати парольну фразу", + "sign_toggle": "Знак", + "encrypt_toggle": "Зашифрувати", + "missing_recipient_certs": "Відсутні сертифікати для: {emails}", + "missing_sender_cert": "Жоден сертифікат не прив’язаний до цієї особи", + "status_encrypted_ok": "Це повідомлення було зашифровано", + "status_encrypted_no_key": "Це повідомлення зашифровано, але відповідного ключа не знайдено", + "status_encrypted_failed": "Не вдалося розшифрувати це повідомлення", + "status_signed_valid": "Підпис перевірено", + "status_signed_invalid": "Не вдалося перевірити підпис", + "status_signed_expired_cert": "Підписано простроченим сертифікатом", + "status_signed_self_signed": "Підпис дійсний, але сертифікат самопідписаний (ненадійний)", + "status_signed_mismatch": "Підпис дійсний, але підписувач не відповідає відправнику", + "status_unsupported": "Непідтримуваний формат S/MIME", + "auto_import_signer_certs": "Автоматичний імпорт сертифікатів підписувача", + "auto_import_signer_certs_desc": "Автоматично зберігайте сертифікати з перевірених підписаних електронних листів для майбутнього шифрування", + "export": "Експорт", + "enter_export_passphrase": "Установіть парольну фразу експорту", + "export_passphrase_desc": "Виберіть парольну фразу для захисту експортованого файлу PKCS#12", + "export_storage_desc": "Введіть парольну фразу для зберігання, щоб розшифрувати ключ для експорту", + "incorrect_passphrase": "Неправильна парольна фраза" + }, + "tour": { + "step_counter": "Крок {current} з {total}", + "skip": "Пропустити тур", + "back": "Назад", + "next": "Далі", + "finish": "Закінчити", + "take_a_tour": "Ознайомтеся з інтерфейсом", + "restart_title": "Ознайомчий тур", + "restart_desc": "Повторіть покрокове керівництво по інтерфейсу", + "restart_button": "Перезапустити тур", + "show_on_new_devices_title": "Показувати на нових пристроях", + "show_on_new_devices_desc": "Повторіть привітальний банер і тур під час першого входу на новому пристрої, навіть якщо ви вже завершили їх в іншому місці", + "sidebar_title": "Ваші поштові скриньки", + "sidebar_desc": "Це бічна панель вашої папки. Натисніть будь-яку поштову скриньку, щоб переглянути її електронні листи. Ви можете створювати папки, перетягувати електронні листи між ними та миттєво переглядати кількість непрочитаних.", + "compose_title": "Створіть електронний лист", + "compose_desc": "Натисніть тут, щоб написати новий електронний лист. Ви можете додавати одержувачів, вкладення та використовувати форматований текст.", + "search_title": "Шукайте свою пошту", + "search_desc": "Пошук за відправником, темою чи вмістом. Клацніть піктограму фільтра, щоб переглянути додаткові параметри, як-от діапазон дат, вкладення та повідомлення із зірочкою.", + "email_list_title": "Ваш список адрес електронної пошти", + "email_list_desc": "Тут відображаються електронні листи. Натисніть один, щоб прочитати його праворуч. Використовуйте прапорець, щоб вибрати кілька, а потім масово перемістити, видалити або позначити їх.", + "email_viewer_title": "Панель читання", + "email_viewer_desc": "Вибраний електронний лист відкривається тут. Відповідайте, пересилайте, архівуйте або видаляйте за допомогою кнопок панелі інструментів. Ви також можете зірчати електронні листи або додавати кольорові теги.", + "keywords_title": "Кольорові теги", + "keywords_desc": "Упорядкуйте свою електронну пошту за допомогою кольорових тегів. Перетягніть електронний лист на тег, щоб позначити його, або клацніть електронний лист правою кнопкою миші, щоб призначити теги.", + "calendar_title": "Календар", + "calendar_desc": "Перейдіть до календаря, щоб керувати подіями. Створюйте події, встановлюйте нагадування та переглядайте макети днів, тижнів або місяців.", + "contacts_title": "Контакти", + "contacts_desc": "Ваша адресна книга живе тут. Імпортуйте контакти, створюйте групи та клацніть будь-який контакт, щоб переглянути його повну інформацію.", + "settings_title": "Налаштування", + "settings_desc": "Налаштуйте все: тему, щільність, підписи, фільтри, комбінації клавіш, стандартні параметри календаря тощо.", + "shortcuts_title": "Комбінації клавіш", + "shortcuts_desc": "Це подобається досвідченим користувачам. Натисніть ? у будь-який час, щоб переглянути всі доступні ярлики. Ви можете переходити, створювати та керувати електронними листами, не торкаючись миші.", + "compose_open_title": "Редактор листів", + "compose_open_desc": "Це редактор електронних листів. Додайте одержувачів, напишіть повідомлення, вкладіть файли та використовуйте форматування розширеного тексту. Ви також можете зберігати чернетки та використовувати шаблони.", + "calendar_view_title": "Ваш календар", + "calendar_view_desc": "Ось ваш календар із зразками подій. За допомогою панелі інструментів можна переключатися між переглядами дня, тижня, місяця та порядку денного.", + "create_event_title": "Створіть подію", + "create_event_desc": "Натисніть цю кнопку, щоб створити нову подію календаря. Ви можете встановити назву, дату, час і додати учасників.", + "event_modal_title": "Деталі події", + "event_modal_desc": "Ось форма події. Введіть назву, виберіть дату та час, додайте місце чи учасників. Натисніть «Зберегти», коли закінчите - або закрийте його та йдіть далі.", + "contacts_list_title": "Ваші контакти", + "contacts_list_desc": "Ось ваші контакти. Клацніть будь-який контакт, щоб побачити його повну інформацію праворуч. Ви також можете створювати нові контакти, імпортувати vCards або організовувати контакти в групи.", + "settings_tabs_title": "Меню налаштувань", + "settings_tabs_desc": "Ось усі категорії налаштувань. Налаштуйте свій зовнішній вигляд, керуйте ідентифікаторами, налаштуйте фільтри електронної пошти, налаштуйте свій календар і багато іншого.", + "files_title": "Зберігання файлів", + "files_desc": "Ваш файловий браузер дозволяє завантажувати, упорядковувати та ділитися файлами, як особистий хмарний диск, вбудований у вашу пошту.", + "demo_banner_title": "Демонстраційні елементи керування", + "demo_banner_desc": "Ви в демонстраційному режимі - все залишається у вашому браузері. Будь-коли натисніть «Скинути демонстрацію», щоб почати заново з чистими зразками даних.", + "quota_title": "Використання сховища", + "quota_desc": "Відстежуйте розмір своєї поштової скриньки тут. Коло заповнюється, коли ви використовуєте більше місця." + }, + "unified_mailbox": { + "search_unavailable": "Пошук недоступний в об'єднаному перегляді" + }, + "calendar_event_notifications": { + "someone": "Хтось", + "invited": "{name} запросив(ла) вас на «{title}»", + "updated": "{name} оновив(ла) «{title}»", + "cancelled": "{name} скасував(ла) «{title}»", + "untitled": "Подія без назви" + }, + "share_notifications": { + "someone": "Хтось", + "shared": "{name} поділився(лася) з вами {kind} «{object}»", + "changed": "{name} змінив(ла) ваш доступ до {kind} «{object}»", + "revoked": "{name} скасував(ла) ваш доступ до {kind} «{object}»", + "object": { + "folder": "текою", + "calendar": "календарем", + "address_book": "адресною книгою", + "files": "текою файлів" + } + }, + "sharing": { + "title": "Поділитися «{name}»", + "description": "Надайте доступ іншим користувачам або групам на цьому сервері. Зміни набувають чинності негайно.", + "no_shares": "Поки що ні з ким не поділено.", + "add_person": "Додати людину або групу", + "search_placeholder": "Шукати за іменем або email…", + "loading_principals": "Завантаження користувачів…", + "no_principals": "Інших користувачів або груп не знайдено.", + "no_match": "Збігів немає.", + "remove": "Видалити доступ", + "group": "Група", + "share_added": "Доступ надано", + "share_updated": "Доступ оновлено", + "share_removed": "Доступ видалено", + "share_failed": "Не вдалося оновити спільний доступ", + "preset": { + "freeBusy": "Лише зайнятість", + "read": "Лише читання", + "readWrite": "Читання та запис", + "manager": "Керівник", + "custom": "Власне" + } + }, + "quote_header": { + "reply_line": "{date}, {from} написав:", + "forwarded_separator": "---------- Переслане повідомлення ----------", + "from_label": "Від", + "date_label": "Дата", + "subject_label": "Тема" + }, + "pwa_install": { + "title": "Встановити {appName}", + "description": "Встановіть наш застосунок для швидкого доступу та офлайн-підтримки.", + "not_now": "Не зараз", + "install": "Встановити", + "dont_remind": "Більше не нагадувати", + "dismiss_aria": "Закрити запит на встановлення" + }, + "deep_link": { + "message_not_found": "Цей лист більше недоступний.", + "thread_not_found": "Ця розмова більше недоступна.", + "folder_not_found": "Ця тека більше недоступна.", + "event_not_found": "Ця подія більше недоступна.", + "file_not_found": "Цей файл більше недоступний.", + "account_unavailable": "Увійдіть у цей обліковий запис, щоб відкрити посилання.", + "copy_message": "Скопіювати посилання на лист", + "copy_conversation": "Скопіювати посилання на розмову", + "copy_event": "Скопіювати посилання на подію", + "copied": "Посилання скопійовано", + "copy_failed": "Не вдалося скопіювати посилання" + }, + "plugins": { + "oauth_callback": { + "completing": "Завершення авторизації…", + "completing_message": "Зачекайте, поки встановлюється з'єднання.", + "received_title": "Авторизацію отримано", + "received_message": "Ви можете повернутися на вкладку, де розпочали підключення.", + "failed_title": "Не вдалося виконати авторизацію", + "denied": "В авторизації відмовлено.", + "failed_detail": "Не вдалося виконати авторизацію: {error}", + "missing_params": "Відсутній код авторизації або стан.", + "delivery_failed": "Не вдалося передати результат авторизації на вкладку програми. Закрийте це вікно та спробуйте ще раз." + }, + "settings": { + "title": "Плагіни", + "description": "Плагіни, які додав ваш адміністратор. Вмикайте й вимикайте їх для свого облікового запису.", + "description_uploads": "Плагіни додають у webmail нові функції. Вмикайте й вимикайте їх для свого облікового запису або встановлюйте власні.", + "description_demo": "У демо-режимі плагіни недоступні.", + "install_button": "Встановити плагін", + "install_hint": "Виберіть ZIP-файл плагіна.", + "install_hint_approval": "Виберіть ZIP-файл плагіна. Перш ніж ви зможете його ввімкнути, його має схвалити адміністратор.", + "install_uploads_off": "Ваш адміністратор не дозволяє встановлювати плагіни.", + "install_managed_id": "Ваш адміністратор уже надає плагін із такою назвою, тому його не можна замінити.", + "install_invalid": "Цей файл не є плагіном, який можна встановити: {details}", + "toast_installed": "Плагін встановлено. Увімкніть його нижче.", + "toast_installed_pending": "Плагін встановлено й надіслано вашому адміністратору на схвалення.", + "toast_enabled": "Плагін {name} увімкнено", + "toast_disabled": "Плагін {name} вимкнено", + "toast_removed": "Плагін {name} видалено", + "toast_forced": "Ваш адміністратор вимагає, щоб плагін {name} залишався ввімкненим", + "toast_approval_requested": "Плагін {name} чекає на схвалення вашим адміністратором", + "empty_title": "Плагінів поки немає", + "empty_admin": "Ваш адміністратор не додав жодного плагіна.", + "empty_uploads": "Щоб почати, встановіть плагін із ZIP-файлу.", + "empty_demo": "Плагінам потрібен справжній обліковий запис, тому в демо-режимі їх немає.", + "syncing": "Перевірка плагінів на сервері…", + "status_installed": "Вимкнено", + "status_enabled": "Запускається", + "status_running": "Увімкнено", + "status_disabled": "Вимкнено", + "status_error": "Помилка", + "badge_forced": "Обов'язковий", + "badge_managed": "Від вашого адміністратора", + "badge_yours": "Встановлено вами", + "badge_awaiting": "Чекає на схвалення", + "toggle_label": "Увімкнути або вимкнути плагін {name}", + "forced_note": "Ваш адміністратор вимагає цей плагін, тому його не можна вимкнути.", + "permissions": "Що він може робити", + "permissions_managed": "Що він може робити (схвалено вашим адміністратором)", + "settings": "Налаштування", + "remove": "Видалити", + "remove_title": "Видалити плагін {name}?", + "remove_message": "Плагін і все, що він зберіг у цьому браузері, буде видалено. Пізніше його можна встановити знову.", + "remove_confirm": "Видалити", + "error_user_plugins_off": "Ваш адміністратор вимкнув плагіни, встановлені користувачами, тому цей плагін не працює.", + "error_approval_unavailable": "Цей плагін встановлено ще до того, як з'явилося схвалення. Видаліть і встановіть його знову, щоб надіслати на схвалення.", + "error_approval_denied": "Ваш адміністратор не схвалив цей плагін.", + "error_approval_pending": "Очікується, що ваш адміністратор схвалить цей плагін.", + "error_auto_disabled": "Вимкнено, бо постійно виникали помилки. Увімкніть його знову, щоб спробувати ще раз.", + "accounts_heading": "Підключені облікові записи", + "account_on": "{label} на {host}", + "account_waiting": "{label} на {host} (вхід не завершено)", + "disconnect": "Відключити", + "disconnect_failed": "Не вдалося відключити {label}" + }, + "consent": { + "title": "Дозволити плагіну {name} це?", + "intro": "Плагін запитує дозволи, наведені нижче. Він отримає їх, лише якщо ви дозволите.", + "high_risk_note": "Позначені пункти дають плагіну доступ до вмісту вашої пошти або дозволяють йому передавати дані деінде. Дозволяйте їх лише плагінам, яким довіряєте.", + "revoke_hint": "Ви будь-коли можете вимкнути або видалити плагін у налаштуваннях.", + "deny": "Не дозволяти", + "allow": "Дозволити" + }, + "permissions": { + "none": "Йому не потрібні особливі дозволи.", + "high_risk": "Потребує обережності", + "email_read": { + "title": "Читати вашу пошту", + "body": "Бачити відправників, одержувачів, теми й повний текст будь-якого листа у вашому обліковому записі, а також ваші папки." + }, + "email_write": { + "title": "Змінювати й упорядковувати вашу пошту", + "body": "Додавати й прибирати теги, позначки та інші ключові слова у ваших листах, переміщувати їх між вашими папками та в архів, а також зберігати чернетки." + }, + "email_send": { + "title": "Змінювати листи перед надсиланням", + "body": "Читати й змінювати одержувачів, тему, текст і вкладення листів, які ви пишете, а також зупиняти надсилання листа." + }, + "crypto_full": { + "title": "Повний доступ до програми", + "body": "Працює з тими самими правами, що й сам webmail. Може читати всю вашу пошту та збережені ключі й діяти від вашого імені. Дозволяйте це лише плагінам, яким повністю довіряєте." + }, + "email_raw_send": { + "title": "Надсилати власні листи", + "body": "Надсилати з вашого облікового запису готові листи, наприклад підписані або зашифровані." + }, + "email_blob_read": { + "title": "Читати вихідні дані листів", + "body": "Читати вихідні дані ваших листів і вкладень, а також кожного файлу, який ви прикріплюєте, до надсилання." + }, + "email_blob_write": { + "title": "Замінювати вкладення перед надсиланням", + "body": "Завантажувати файли на ваш поштовий сервер і замінювати прикріплений файл іншим до надсилання листа." + }, + "email_render_takeover": { + "title": "Змінювати показ листів", + "body": "Замінювати текст відкритих листів і листів у списку повідомлень, наприклад щоб показати розшифрований вміст." + }, + "calendar_read": { + "title": "Читати ваш календар", + "body": "Бачити ваші календарі та їхні події." + }, + "calendar_write": { + "title": "Змінювати ваш календар", + "body": "Створювати, змінювати й видаляти події." + }, + "contacts_read": { + "title": "Читати ваші контакти", + "body": "Шукати у ваших адресних книгах і читати їх." + }, + "contacts_write": { + "title": "Змінювати ваші контакти", + "body": "Створювати, змінювати й видаляти контакти та адресні книги." + }, + "files_read": { + "title": "Читати ваші файли", + "body": "Переглядати й відкривати файли, що зберігаються у вашому обліковому записі." + }, + "files_write": { + "title": "Змінювати ваші файли", + "body": "Завантажувати, перейменовувати, переміщувати й видаляти файли у вашому обліковому записі." + }, + "identity_read": { + "title": "Бачити ваші адреси відправника", + "body": "Бачити адреси, з яких ви надсилаєте листи, та їхні підписи." + }, + "identity_write": { + "title": "Змінювати ваші адреси відправника", + "body": "Створювати, змінювати й видаляти адреси відправника та підписи." + }, + "filters_read": { + "title": "Читати ваші фільтри пошти", + "body": "Бачити правила, які сортують вхідну пошту." + }, + "filters_write": { + "title": "Змінювати ваші фільтри пошти", + "body": "Додавати правила до фільтрів на сервері. Такі правила можуть переміщувати, пересилати або видаляти вхідну пошту." + }, + "tasks_read": { + "title": "Читати ваші завдання", + "body": "Бачити ваші списки завдань і завдання." + }, + "tasks_write": { + "title": "Змінювати ваші завдання", + "body": "Створювати, змінювати, завершувати й видаляти завдання." + }, + "templates_read": { + "title": "Читати ваші шаблони", + "body": "Бачити збережені шаблони листів." + }, + "templates_write": { + "title": "Змінювати ваші шаблони", + "body": "Створювати, змінювати й видаляти шаблони листів." + }, + "smime_read": { + "title": "Бачити ваш стан S/MIME", + "body": "Бачити, чи маєте ви ключі та сертифікати S/MIME, без доступу до самих ключів." + }, + "vacation_read": { + "title": "Бачити вашу автовідповідь", + "body": "Бачити, чи ввімкнено автовідповідь і що в ній написано." + }, + "vacation_write": { + "title": "Змінювати вашу автовідповідь", + "body": "Вмикати й вимикати автовідповідь, змінювати її текст і дати." + }, + "settings_read": { + "title": "Читати ваші налаштування", + "body": "Бачити ваші параметри та визначення тегів." + }, + "settings_write": { + "title": "Змінювати ваші налаштування", + "body": "Додавати теги, змінювати їхній порядок і змінювати ваші параметри." + }, + "security_read": { + "title": "Бачити безпеку облікового запису", + "body": "Бачити, чи ввімкнено двофакторну автентифікацію та шифрування, без доступу до секретів." + }, + "auth_observe": { + "title": "Знати, коли ви входите", + "body": "Отримувати сповіщення, коли ви входите, перемикаєте обліковий запис або ваш сеанс подовжується." + }, + "auth_emit": { + "title": "Виконувати вихід", + "body": "Виконувати ваш вихід із webmail." + }, + "account_read": { + "title": "Бачити ваші облікові записи", + "body": "Бачити додані вами облікові записи, їхні адреси та сервери." + }, + "http_post": { + "title": "Звертатися до сервера webmail від вашого імені", + "body": "Викликати ці адреси сервера з вашими обліковими даними:" + }, + "http_fetch": { + "title": "Підключатися до інших сайтів", + "body": "Надсилати запити на ці сайти. Туди може потрапити все, що плагін може прочитати:" + }, + "admin_config": { + "title": "Читати свої налаштування від адміністратора", + "body": "Читати налаштування, які ваш адміністратор зберіг для цього плагіна." + }, + "ui_observe": { + "title": "Стежити, де ви в програмі", + "body": "Знати, яку сторінку відкрито, і реагувати на посилання, які ви натискаєте." + }, + "ui_toolbar": { + "title": "Додавати кнопки на панелі інструментів", + "body": "Показувати власні кнопки на панелі інструментів листів, у відкритому листі та на верхній панелі." + }, + "ui_app_top_banner": { + "title": "Показувати банер угорі", + "body": "Показувати сповіщення у верхній частині програми." + }, + "ui_email_banner": { + "title": "Показувати сповіщення в листах", + "body": "Показувати власні сповіщення та підсумки над частинами відкритого листа або розмови, усередині них і між ними." + }, + "ui_email_footer": { + "title": "Додавати вміст під листами", + "body": "Показувати власний вміст під відкритим листом." + }, + "ui_contact_cryptokeys": { + "title": "Показувати ключі шифрування контактів", + "body": "Показувати ключі шифрування контакту й керувати ними." + }, + "ui_email_details": { + "title": "Додавати панелі до листа", + "body": "Показувати власну панель поруч із відкритим листом або під ним." + }, + "ui_download_file": { + "title": "Зберігати файли на ваш пристрій", + "body": "Пропонувати для завантаження створені ним файли." + }, + "ui_message_list_tabs": { + "title": "Додавати вкладки у «Вхідні»", + "body": "Розподіляти вхідні листи за вкладками, наприклад «Оновлення» або «Акції»." + }, + "ui_composer_toolbar": { + "title": "Додавати елементи в редактор листа", + "body": "Показувати власні кнопки, пункти меню й розділи, поки ви пишете лист." + }, + "ui_composer_sidebar": { + "title": "Додавати панель у редактор листа", + "body": "Показувати власну панель поруч із листом, який ви пишете." + }, + "ui_sidebar_widget": { + "title": "Додавати елементи на бічну панель", + "body": "Показувати власні блоки й розділи на бічній панелі пошти." + }, + "ui_settings_section": { + "title": "Додавати сторінку налаштувань", + "body": "Показувати власну сторінку в налаштуваннях." + }, + "ui_context_menu": { + "title": "Додавати пункти в меню листа", + "body": "Додавати пункти в меню, яке відкривається, коли ви клацаєте лист правою кнопкою." + }, + "ui_navigation_rail": { + "title": "Додавати кнопку на панель навігації", + "body": "Показувати власну кнопку на панелі навігації." + }, + "ui_keyboard": { + "title": "Додавати сполучення клавіш", + "body": "Реагувати на сполучення клавіш, які він визначає." + }, + "ui_calendar_action": { + "title": "Додавати елементи в інтерфейс календаря", + "body": "Показувати власні кнопки й розділи в подіях, а також короткі мітки й крапки на днях календаря." + }, + "ui_admin_page": { + "title": "Додавати сторінку адміністрування", + "body": "Показувати власну сторінку в налаштуваннях адміністратора." + }, + "app_lifecycle": { + "title": "Знати, коли програма запускається й зупиняється", + "body": "Отримувати сповіщення, коли програма запускається, втрачає зв'язок або ви виходите." + }, + "compose_open": { + "title": "Відкривати редактор листа", + "body": "Відкривати новий лист із уже заповненими одержувачами, текстом або вкладеннями. Надсилати його чи ні, як і раніше, вирішуєте ви." + }, + "compose_read": { + "title": "Читати лист, який ви пишете", + "body": "Читати текст листа, поки ви його пишете, коли ви користуєтеся плагіном у редакторі. Без підпису та цитованого листа." + }, + "compose_write": { + "title": "Змінювати лист, який ви пишете", + "body": "Замінювати або додавати текст у листі, поки ви його пишете, коли ви користуєтеся плагіном у редакторі. Кожну зміну можна скасувати, а надсилати чи ні, вирішуєте ви." + }, + "ui_notifications": { + "title": "Показувати сповіщення", + "body": "Показувати сповіщення на вашому пристрої або в програмі, якщо ви їх не дозволили. У кожному вказано назву плагіна." + }, + "ui_message_list": { + "title": "Додавати елементи до списку повідомлень", + "body": "Показувати власний вміст над списком повідомлень, а також кнопки на листах і на панелі вибраних листів." + }, + "ui_contacts": { + "title": "Додавати елементи в інтерфейс контактів", + "body": "Показувати власні розділи в картці контакту й кнопки в списку контактів." + }, + "ui_tasks": { + "title": "Додавати елементи в інтерфейс завдань", + "body": "Показувати власні розділи в подробицях завдання й кнопки в списку завдань." + }, + "ui_files": { + "title": "Додавати елементи в інтерфейс файлів", + "body": "Показувати власні розділи в попередньому перегляді файлу й пункти в меню файлу." + }, + "ui_commands": { + "title": "Додавати команди", + "body": "Додавати власні пункти до палітри команд (Ctrl+K)." + }, + "ui_app_page": { + "title": "Додавати сторінки", + "body": "Додавати до програми власні сторінки, кожну з кнопкою на панелі навігації." + }, + "http_proxy": { + "title": "Використовувати підключені вами облікові записи", + "body": "Надсилати запити через сервер Bulwark з обліковим записом або ключем, який налаштували ви або ваш адміністратор. Плагін ніколи не бачить пароль, але може робити на цьому сервері все, що може цей обліковий запис." + } + }, + "dialog": { + "from_plugin": "Від плагіна {name}", + "ok": "OK", + "submit": "Надіслати", + "confirm": "Підтвердити", + "cancel": "Скасувати", + "close": "Закрити", + "turn_off": "Вимкнути {name}", + "plugin_menu": "Параметри {name}", + "password_note": "Ця форма від плагіна {name}, а не від вебпошти. Вводьте пароль, лише якщо довіряєте цьому плагіну.", + "toast_auto_disabled": "{name} вимкнено, бо постійно виникали помилки." + }, + "connect": { + "title": "Підключити {label}", + "intro_password": "{name} хоче використовувати ваш обліковий запис {label}. Bulwark зберігає пароль на своєму сервері й надсилає його лише на {host}. Плагін ніколи його не бачить.", + "intro_password_any": "{name} хоче використовувати ваш обліковий запис {label}. Bulwark зберігає пароль на своєму сервері й надсилає його лише на сервер, який ви вкажете тут. Плагін ніколи його не бачить.", + "intro_signin": "{name} хоче увійти до вашого облікового запису {label}. Вкажіть тут сервер, а потім увійдіть на його власній сторінці. Bulwark зберігає отриманий доступ на своєму сервері; плагін ніколи його не бачить.", + "server": "Адреса сервера", + "server_hint": "cloud.example.com", + "username": "Ім'я користувача", + "secret": "Пароль або ключ", + "connect": "Підключити", + "continue": "Продовжити", + "cancel": "Скасувати", + "saving": "Збереження…", + "error_server": "Вкажіть адресу сервера, наприклад https://cloud.example.com", + "error_save": "Не вдалося зберегти: {error}" + } + }, + "global_search": { + "title": "Шукати скрізь", + "placeholder": "Пошук у пошті, контактах, календарі, файлах...", + "scope_label": "Шукати в", + "account_label": "Обліковий запис", + "scope_all": "Усе", + "scope_mail": "Пошта", + "scope_contacts": "Контакти", + "scope_calendar": "Календар", + "scope_files": "Файли", + "all_accounts": "Усі облікові записи", + "show_all": "Показати всі результати", + "no_results": "Нічого не знайдено за запитом «{query}»", + "server_hint": "Цілі слова шукаються на сервері; частини слів — серед уже завантаженого", + "searching": "Пошук...", + "account_error": "Не вдалося виконати пошук у {account}", + "recent_searches": "Останні запити", + "recurring": "Повторювана", + "no_subject": "(no subject)", + "empty_hint": "Шукайте пошту, контакти, події та файли в усіх облікових записах. Спробуйте from:, subject:, has:attachment, after:2026-01-01.", + "flat_by_date": "Списком за датою", + "grouped": "За групами", + "load_more": "Завантажити ще", + "preview_empty": "Виберіть результат для перегляду", + "open_full": "Відкрити", + "view_label": "Вигляд" + }, + "top_bar": { + "more": "Інші дії" + }, + "plugin_pages": { + "loading": "Завантаження сторінки", + "not_available_title": "Ця сторінка недоступна", + "not_available_description": "Плагін, який її додав, вимкнено або більше не встановлено.", + "back_to_mail": "Повернутися до пошти", + "settings_group": "Плагіни" + }, + "command_palette": { + "title": "Палітра команд", + "open": "Відкрити палітру команд", + "placeholder": "Введіть команду", + "no_results": "Немає відповідних команд", + "go_to": "Перейти: {name}", + "compose": "Новий лист", + "search": "Шукати в пошті", + "theme_dark": "Увімкнути темну тему", + "theme_light": "Увімкнути світлу тему", + "shortcuts": "Показати комбінації клавіш", + "group_navigation": "Перейти", + "group_actions": "Дії", + "group_plugins": "Плагіни" + }, + "notification_center": { + "title": "Сповіщення", + "mark_all_read": "Позначити всі як прочитані", + "clear": "Очистити", + "empty": "Немає сповіщень", + "close": "Закрити", + "unread": "Непрочитане", + "unread_count": "Непрочитаних: {count}", + "level_error": "Помилка", + "level_warning": "Попередження" + } +} diff --git a/locales/zh-TW/common.json b/locales/zh-TW/common.json index 0760512f..9b283a04 100644 --- a/locales/zh-TW/common.json +++ b/locales/zh-TW/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "使用 JMAP 通訊協定的極簡網頁郵件用戶端", - "login": { - "title": "網頁郵件", - "username_label": "電子郵件地址", - "username_placeholder": "user@example.com", - "password_label": "密碼", - "password_placeholder": "請輸入密碼", - "jmap_endpoint_label": "JMAP 伺服器", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "伺服器必須允許來自此網域的 CORS 請求。", - "jmap_server_label": "伺服器", - "jmap_server_auto_picked": "已根據您的電子郵件網域選擇伺服器。", - "sign_in": "登入", - "signing_in": "正在登入…", - "loading": "正在載入…", - "reconnecting": "連線中斷,正在嘗試重新連線…", - "error": { - "invalid_credentials": "電子郵件地址或密碼不正確。請檢查您的登入資訊後再試一次。", - "connection_failed": "無法連線至伺服器。請檢查網路連線後再試一次。", - "cors_blocked": "可連線至伺服器,但跨來源請求遭到封鎖。請檢查 JMAP 伺服器的 CORS 設定,並允許此網域。", - "server_error": "伺服器目前無法使用,請稍後再試。", - "generic": "發生意外錯誤。如果問題持續存在,請聯絡管理員。", - "totp_required": "需要雙因素驗證碼。請在下方輸入驗證碼。", - "totp_invalid": "驗證碼無效。請檢查驗證器應用程式後再試一次。", - "oauth_discovery_failed": "SSO 已啟用,但無法連線至身分提供者。請檢查 OAuth 設定。" - }, - "show_password": "顯示密碼", - "hide_password": "隱藏密碼", - "totp_toggle": "我有 2FA 驗證碼", - "remember_me": "記住我", - "config_error": { - "title": "設定錯誤", - "fetch_failed": "無法載入應用程式設定。請稍後再試。", - "server_not_configured": "郵件伺服器尚未設定。請聯絡您的管理員。" - }, - "remove_from_history": "從歷史記錄中移除", - "totp_label": "驗證碼", - "totp_placeholder": "000000", - "session_expired": "工作階段已過期,請重新登入。", - "dismiss": "關閉", - "or": "或", - "sign_in_sso": "使用 SSO 登入", - "add_account_title": "新增帳號", - "add_account_subtitle": "使用其他帳號登入", - "cancel": "取消", - "website": "網站", - "imprint": "法律聲明", - "privacy_policy": "隱私權政策", - "try_demo": "試用示範版", - "demo_description": "使用範例資料自由探索,不需要帳號", - "demo_launching": "正在啟動示範版…", - "demo_login_button": "啟動示範版", - "demo_tagline": "體驗功能完整的電子郵件用戶端,不需要帳號。", - "demo_no_signup": "不需要註冊,使用範例資料自由探索", - "oauth_completing": "正在完成登入…", - "oauth_error": { - "title": "驗證失敗", - "invalid_state": "安全驗證失敗。請嘗試重新登入。", - "missing_params": "缺少授權資料。請嘗試重新登入。", - "token_exchange_failed": "無法完成身分驗證。請再試一次。", - "access_denied": "存取遭拒。請聯絡管理員。", - "back_to_login": "返回登入頁面" - } - }, - "sidebar": { - "close": "關閉", - "compose": "撰寫郵件", - "compose_hint": "撰寫郵件 (c)", - "search_placeholder": "搜尋郵件…", - "search_placeholder_hint": "搜尋郵件…(按 /)", - "storage": "儲存空間", - "storage_used": "已使用", - "storage_free": "剩餘", - "storage_total": "總計", - "sign_out": "登出", - "sign_out_of": "登出 {account}", - "sign_out_all": "登出所有帳號", - "add_account": "新增帳號", - "set_as_default": "設為預設", - "switch_account": "切換帳號", - "contacts": "聯絡人", - "calendar": "行事曆", - "settings": "設定", - "admin": "管理", - "files": "檔案", - "loading_mailboxes": "正在載入信件匣…", - "push_connected": "即時更新已啟用", - "push_disconnected": "即時更新未啟用", - "keyboard_shortcuts": "鍵盤快速鍵", - "theme": { - "light": "淺色模式", - "dark": "深色模式", - "system": "系統主題" - }, - "language": { - "title": "語言" - }, - "mailboxes": { - "inbox": "收件匣", - "sent": "寄件備份", - "drafts": "草稿", - "trash": "垃圾桶", - "archive": "封存", - "starred": "已加星號", - "all_mail": "所有郵件", - "spam": "垃圾郵件", - "important": "重要" - }, - "unified_inbox": "整合收件匣", - "unified_sent": "所有寄件備份", - "unified_drafts": "所有草稿", - "unified_trash": "所有垃圾桶", - "unified_archive": "所有封存郵件", - "unified_junk": "所有垃圾郵件", - "all_accounts": "所有帳號", - "unified_mailbox": "整合信箱", - "expand": "展開", - "collapse": "收合", - "expand_tooltip": "展開", - "collapse_tooltip": "收合", - "mobile": { - "search": "搜尋", - "compose": "撰寫郵件", - "go_back": "返回", - "toggle_menu": "切換選單" - }, - "clear_search": "清除搜尋", - "vacation_active": "休假自動回覆已開啟", - "demo_banner": "示範模式", - "demo_reset": "重設", - "demo_tour": "功能導覽", - "tags": "標籤", - "show_all_tags": "顯示全部({count})", - "show_fewer_tags": "顯示較少", - "folders": "信件匣", - "shared": "共用", - "mail": "郵件", - "nav_label": "導覽", - "add_app": "應用程式", - "scheduled": "排程寄送", - "unified_all_unread": "全部未讀", - "unified_all_starred": "全部已加星號", - "unified_all_mail": "全部郵件", - "remove_account": "移除帳號", - "remove_account_confirm": "要從這部裝置移除 {account} 嗎?您稍後仍可重新新增。" - }, - "protocol_handlers": { - "title": "預設應用程式", - "description": "選擇電子郵件與行事曆連結是否要在 Bulwark 開啟。Bulwark 會註冊為 mailto: 與 webcal: 連結的通訊協定處理常式。", - "unsupported": "此瀏覽器或連線不支援手動註冊通訊協定處理常式。您仍可嘗試透過瀏覽器或作業系統設定,使用已安裝的 PWA。", - "mailto_label": "電子郵件連結", - "mailto_description": "使用預先填入的撰寫視窗,在 Bulwark 開啟 mailto: 連結。", - "protocol_open_mode_label": "開啟通訊協定連結時", - "protocol_open_mode_description": "選擇 Bulwark 要在新分頁開啟 mailto: 與 webcal: 連結,或使用現有的工作階段。現有工作階段選項需要通知權限;若瀏覽器阻止視窗取得焦點,您可以點擊備用通知,將 Bulwark 移到最上層。", - "protocol_open_mode_active_session": "盡可能在現有工作階段開啟", - "protocol_open_mode_new_tab": "一律開啟新分頁", - "focus_notification_title": "開啟 Bulwark", - "focus_notification_body": "連結已在 Bulwark 開啟。點擊即可將視窗移到最上層。", - "webcal_label": "行事曆連結", - "webcal_description": "使用預先填入的行事曆訂閱對話方塊,在 Bulwark 開啟 webcal: 連結。", - "register_mailto": "註冊電子郵件應用程式", - "register_webcal": "註冊行事曆應用程式", - "mailto_registered": "已要求註冊電子郵件處理常式", - "webcal_registered": "已要求註冊行事曆處理常式", - "registration_failed": "通訊協定處理常式註冊失敗", - "opening_mailto": "正在開啟撰寫視窗…", - "opening_webcal": "正在開啟行事曆…", - "browser_note": "您的瀏覽器或作業系統可能會要求您確認,而且可能必須先安裝 Bulwark,才能選為預設應用程式。", - "select_account_title": "選擇帳號", - "select_mailto_account": "選擇用於開啟此電子郵件連結的帳號。", - "select_webcal_account": "選擇用於開啟此行事曆連結的帳號。", - "select_account_note": "此選擇僅適用於此協議連結。", - "detail_to": "收件者", - "detail_subject": "主題", - "detail_no_subject": "無主題", - "detail_calendar": "行事曆", - "detail_source": "來源", - "active_account": "使用中", - "switching_account": "正在切換帳號…" - }, - "sidebar_apps": { - "modal_title": "側邊欄應用程式", - "add_new": "新增應用程式", - "edit_app": "編輯應用程式", - "name_label": "名稱", - "name_placeholder": "我的應用程式", - "name_required": "名稱為必填欄位", - "url_label": "URL", - "url_required": "URL 為必填欄位", - "url_invalid": "請輸入有效的 http 或 https URL", - "icon_label": "圖示", - "icon_required": "圖示為必填欄位", - "open_mode_label": "開啟方式", - "open_new_tab": "新分頁", - "open_inline": "內嵌", - "cancel": "取消", - "add": "新增", - "update": "更新", - "delete": "刪除", - "delete_confirm_title": "刪除應用程式", - "delete_confirm": "您確定要刪除「{name}」嗎?", - "no_apps": "尚未新增任何應用程式", - "no_apps_hint": "在側邊欄新增自訂應用程式與連結", - "managed_title": "由您的管理員提供", - "managed_description": "這些應用程式為所有人統一設定,無法變更。", - "managed_badge": "受管理", - "search_icons": "搜尋圖示…", - "show_popular": "熱門", - "show_all": "全部", - "no_icons_found": "找不到圖示", - "inline_badge": "內嵌", - "tab_badge": "新標籤頁", - "show_on_mobile": "在行動裝置上顯示" - }, - "email_list": { - "no_emails": "找不到郵件", - "no_emails_description": "此信件匣中沒有郵件", - "no_search_results": "找不到結果", - "no_search_results_description": "請調整搜尋條件或篩選器", - "loading": "正在載入郵件…", - "unread": "未讀", - "to_me": "寄給我", - "to_recipients": "致 {count} 收件者", - "and_others": "另有 {count} 人", - "draft": "草稿", - "starred": "已加星號", - "pinned": "已釘選", - "replied": "已回覆", - "forwarded": "已轉寄", - "has_attachment": "有附件", - "conversations_count": "顯示 {count} 個對話串,共 {total} 個", - "conversations_count_plus": "{count}+ 個對話串", - "conversations_count_simple": "{count} 個對話串", - "no_conversations": "目前沒有對話串", - "loading_more": "正在載入更多郵件…", - "no_more_emails": "沒有更多郵件可載入", - "batch_actions": { - "select": "選取郵件", - "select_all": "全部選取", - "selected_messages": "已選取 {count, plural, one {1} other {#}} 封郵件", - "mark_read": "標示為已讀", - "mark_unread": "標示為未讀", - "delete": "刪除", - "delete_confirm_title": "刪除郵件", - "delete_confirm_message": "您確定要刪除{count, plural, one {1 封郵件} other {# 封郵件}}嗎?", - "clear_selection": "清除選取" - }, - "permanent_delete": "永久刪除", - "permanent_delete_confirm_title": "永久刪除", - "permanent_delete_confirm_message": "這封郵件將永久刪除。此動作無法復原。", - "permanent_delete_confirm_batch_message": "將永久刪除{count, plural, one {1 封郵件} other {# 封郵件}}。此動作無法復原。", - "empty_folder": { - "button": "清空信件匣", - "confirm_title": "清空信件匣", - "confirm_message": "此信件匣中的所有郵件都將永久刪除。此動作無法復原。", - "confirm_button": "清空信件匣", - "junk_hint": "您可以清空垃圾郵件信件匣,永久刪除其中的所有郵件。", - "trash_hint": "您可以清空垃圾桶,永久刪除其中的所有郵件。" - }, - "no_scheduled_emails": "沒有排程寄送的郵件", - "no_scheduled_emails_description": "排定稍後寄送的郵件會顯示在這裡。", - "scheduled_count": "{count, plural, one {1 封排程寄送郵件} other {# 封排程寄送郵件}}", - "scheduled_actions_hint": "排程郵件可從排程動作中取消、重新排程或編輯。", - "cancel_scheduled_send": "取消傳送", - "reschedule_send": "重新排程", - "cancel_and_edit": "取消並編輯", - "cancel_and_compose_again": "取消並重新撰寫", - "reschedule_prompt": "輸入新的日期與時間,例如 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "寄件者要求在您開啟這封郵件時收到通知。", - "send": "傳送回執", - "ignore": "忽略", - "sent": "已讀回執已傳送。", - "send_failed": "無法傳送已讀回執", - "mdn_subject": "已讀:{subject}", - "mdn_body": "這是您傳送給 {recipient} 的郵件已讀回執。\n\n注意:此回執僅表示郵件已顯示在收件者的電腦上,並不保證收件者已閱讀或理解郵件內容。" - }, - "no_email_selected": "未選取郵件", - "no_email_description": "請從清單選取一封郵件,即可在這裡檢視", - "no_conversation_selected": "未選取對話串", - "no_conversation_description": "請從清單選取一個對話串,即可在這裡閱讀", - "compose": "撰寫郵件", - "compose_hint": "撰寫新郵件", - "no_subject": "(無主題)", - "no_body_content": "(無內文內容)", - "no_preview_available": "無可用預覽", - "show_quoted_text": "顯示引用文字", - "hide_quoted_text": "隱藏引用文字", - "loading_email": "正在載入郵件…", - "loading": "正在載入…", - "reply": "回覆", - "reply_all": "全部回覆", - "forward": "轉寄", - "delete": "刪除", - "archive": "封存", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "加上星號", - "unstar": "移除星號", - "mark_unread": "標示為未讀", - "mark_read": "標示為已讀", - "unread": "未讀", - "read": "已讀", - "spam_short": "垃圾郵件", - "not_spam_short": "非垃圾郵件", - "move": "移動", - "print": "列印", - "view_source": "檢視原始碼", - "fullscreen": "全螢幕", - "exit_fullscreen": "離開全螢幕", - "export_email": "匯出為 .eml", - "forward_as_attachment": "以附件形式轉寄", - "import_email": "匯入 .eml 或 .zip", - "keyboard_shortcuts": "鍵盤快速鍵 (?)", - "email_source": "郵件原始碼", - "draft_banner": "這是一封草稿郵件", - "edit_draft": "編輯", - "copy_source": "複製到剪貼簿", - "source_copied": "原始碼已複製到剪貼簿", - "attachments": "附件", - "important": "重要", - "download": "下載", - "download_all": "全部下載", - "from": "寄件者", - "to": "收件者", - "cc": "副本", - "bcc": "密件副本", - "date": "日期", - "subject": "主題", - "show_details": "顯示詳細資料", - "hide_details": "隱藏詳細資料", - "external_content_warning": "圖片與外部內容已遭封鎖", - "load_external_content": "載入圖片", - "trust_sender": "一律信任此寄件者", - "back_to_list": "回到清單", - "view_contact": "檢視聯絡人", - "message_details": "郵件詳細資料", - "more_reply_options": "更多回覆選項", - "set_tag": "設定標籤", - "tag": "標籤", - "more_actions": "更多動作", - "previous": "上一封", - "next": "下一封", - "move_to": "移至…", - "remove_tag": "移除標籤", - "tag_filter_placeholder": "篩選標籤", - "tag_no_matches": "沒有相符的標籤", - "more_count": "另有 {count} 個", - "characters_count": "{count} 個字元", - "quick_reply_placeholder": "撰寫快速回覆…", - "more_options": "更多選項", - "sending": "傳送中…", - "security_authentication": "安全性與驗證", - "technical_details": "技術詳細資料", - "message_id_label": "郵件 ID:", - "reply_to_label": "回覆至:", - "delivery_time_label": "送達時間:", - "conversation_part_label": "對話串中的部分:", - "previous_messages": "先前的 {count} 封郵件", - "previous_messages_plural": "先前的 {count} 封郵件", - "time": { - "day": "天", - "days": "天", - "hour": "小時", - "hours": "小時", - "minute": "分鐘", - "minutes": "分鐘" - }, - "unknown_sender": "未知", - "recipient_me": "我", - "recipient_and_others": "{name} 及其他 {count} 人", - "recipient_to_prefix": "收件者:", - "authentication": { - "title": "郵件身分驗證", - "status": { - "verified": "已驗證", - "warning": "警告", - "none": "未驗證" - }, - "spf": { - "pass": "SPF 通過", - "fail": "SPF 失敗", - "none": "無 SPF" - }, - "dkim": { - "pass": "DKIM 有效", - "fail": "DKIM 無效", - "none": "無 DKIM" - }, - "dmarc": { - "pass": "DMARC 通過", - "fail": "DMARC 失敗", - "none": "無 DMARC" - }, - "spam_score": "垃圾郵件評分", - "tooltip_spf": "Sender Policy Framework:驗證傳送伺服器是否有權代表該網域傳送郵件", - "tooltip_dkim": "DomainKeys Identified Mail:使用密碼編譯簽章,確認郵件在傳送過程中未遭竄改", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance:確保 SPF 與 DKIM 和寄件者網域一致,並設定驗證失敗時的處理原則", - "policy": "策略", - "result": { - "pass": "通過", - "fail": "失敗", - "softfail": "軟失敗", - "neutral": "中立", - "permerror": "永久錯誤", - "temperror": "暫時性錯誤", - "none": "無" - } - }, - "details": { - "recipients_routing": "收件者與路由", - "authentication_security": "身分驗證與安全", - "identifiers_threading": "識別碼與對話串", - "mailing_list": "郵件清單", - "message_properties": "郵件屬性", - "sent": "傳送時間", - "received": "接收時間", - "delivery_time": "送達時間", - "in_reply_to": "回覆來源", - "references": "引用", - "thread_id": "對話串 ID", - "size": "大小", - "mime_type": "MIME 型別", - "attachments_summary": "{count} 個檔案 · {size}", - "list_id": "清單 ID", - "list_help": "郵件清單說明", - "list_post": "傳送至清單", - "list_unsubscribe": "取消訂閱", - "iprev": "反向 DNS", - "spam_status": "垃圾郵件狀態", - "ai_verdict": "AI 判斷", - "account": "帳號", - "no_subject": "(無主題)" - }, - "headers": { - "routing": "路由", - "received": "接收時間", - "message_id": "訊息 ID", - "list_info": "郵件清單資訊" - }, - "tooltips": { - "reply": "回覆 (r)", - "reply_all": "全部回覆 (a)", - "forward": "轉寄 (f)", - "archive": "封存 (e)", - "delete": "刪除 (# 或 Del)", - "star": "加上星號 (s)", - "unstar": "移除星號 (s)", - "compose": "撰寫郵件 (c)", - "previous": "上一封郵件", - "next": "下一封郵件", - "edit_draft": "編輯草稿" - }, - "spam": { - "button_title": "回報垃圾郵件", - "not_spam_title": "標示為非垃圾郵件", - "toast_success": "已移至垃圾郵件", - "toast_batch": "{count} 封郵件已移至垃圾郵件", - "toast_undo": "撤銷", - "toast_not_spam_success": "已移至收件匣", - "toast_not_spam_batch": "{count} 封郵件已移至收件匣", - "error": "無法回報垃圾郵件", - "error_not_spam": "無法還原郵件" - }, - "unsubscribe_banner": { - "label": "訂閱郵件", - "button": "取消訂閱", - "confirm_title": "要取消訂閱此寄件者嗎?", - "confirm_button": "確認", - "cancel": "取消", - "success_http": "取消訂閱頁面已在新分頁開啟", - "success_mailto": "取消訂閱郵件已傳送", - "confirm_message_http": "取消訂閱頁面將在新分頁開啟。", - "confirm_message_mailto": "將傳送取消訂閱郵件給寄件者。", - "error": "無法取消訂閱", - "dismiss": "關閉" - }, - "calendar_invitation": { - "loading": "正在載入活動詳細資料…", - "title": "行事曆邀請", - "published_title": "已發布的活動", - "response_title": "活動回覆", - "update_title": "活動更新", - "counter_title": "改期建議", - "refresh_title": "重新整理請求", - "declined_counter_title": "改期建議已被拒絕", - "cancelled_title": "活動已取消", - "organizer": "主辦人:{name}", - "organizer_label": "主辦人:", - "attendees": "{count, plural, one {# 位參與者} other {# 位參與者}}", - "accept": "接受", - "maybe": "暫定", - "decline": "拒絕", - "add_to_calendar": "新增到行事曆", - "added": "已新增到行事曆", - "rsvp_sent": "回覆已傳送", - "parse_error": "無法讀取邀請", - "action_failed": "無法完成此行事曆動作。", - "no_calendar": "行事曆無法使用", - "published_info": "此活動已分享,僅供參考。", - "response_info": "這封郵件包含參與者的回覆。", - "response_info_organizer": "此參與者的回覆會更新您的活動。", - "update_info": "這封郵件會更新現有活動。", - "counter_info": "這封郵件建議變更活動。", - "counter_info_organizer": "此參與者建議變更您的活動。", - "refresh_info": "這封郵件要求提供最新的活動詳細資料。", - "refresh_info_organizer": "參與者要求提供最新的活動詳細資料。", - "declined_counter_info": "主辦人拒絕了改期建議。", - "authentication_failed_info": "這封邀請的郵件驗證檢查失敗。請謹慎執行行事曆動作。", - "authentication_missing_info": "這封邀請未包含已驗證的郵件身分驗證資訊。若有任何可疑之處,請向主辦人確認詳細資料。", - "sender_mismatch_info": "這封邀請是由 {sender} 傳送,但行事曆資料中列出的主辦人是 {organizer}。", - "sender_mismatch_unverified_info": "這封邀請是由 {sender} 傳送,但行事曆資料中列出的主辦人是 {organizer},而且無法驗證這封郵件。", - "organizer_role": "您是此活動的主辦人", - "your_response": "您的回覆:{status}", - "response_needed": "待回覆", - "response_accepted": "已接受", - "response_tentative": "暫定", - "response_declined": "已拒絕", - "response_delegated": "已委派", - "actor_sent_info": "由 {name} 傳送。", - "actor_response_info": "{name} 的回覆為{status}。", - "actor_counter_info": "{name} 建議變更此活動。", - "actor_refresh_info": "{name} 要求提供最新的活動詳細資料。", - "actor_declined_counter_info": "{name} 拒絕了改期建議。", - "actor_note": "備註:{comment}", - "actor_unknown": "有人", - "proposed_changes": "建議的更改", - "change_title": "標題", - "change_time": "時間", - "change_location": "地點", - "change_description": "描述", - "change_empty": "無", - "change_from_to": "{before} → {after}", - "apply_proposal": "套用建議的變更", - "proposal_applied": "已套用建議的變更。", - "review_proposal": "檢視建議", - "review_request": "檢視要求", - "view_in_calendar": "在行事曆中檢視", - "select_calendar": "選取行事曆", - "already_in_calendar": "已在您的行事曆中", - "request_info": "您已受邀參加此活動。請回覆,讓主辦人知道您是否能參加。", - "cancel_info": "主辦人已取消此活動。", - "event_updated": "更新 #{sequence}", - "event_status_tentative": "暫定", - "event_status_cancelled": "已取消", - "expand": "顯示詳細資料", - "collapse": "隱藏詳細資料" - }, - "send": "傳送", - "more": "更多", - "scheduled_banner": "排定於 {date} 傳送", - "scheduled_send_created": "郵件已排程寄送", - "cancel_scheduled_send": "取消傳送", - "reschedule_send": "重新排程", - "cancel_and_edit": "取消並編輯", - "cancel_and_compose_again": "取消並重新撰寫", - "reschedule_prompt": "輸入新的日期與時間,例如 2026-05-04T15:30", - "scheduled_actions_only": "此檢視僅支援排程寄送動作", - "undo_send_scheduled": "郵件已排程寄送", - "undo_send": "取消傳送", - "contact_sidebar": { - "title": "聯絡人", - "close": "關閉側邊欄", - "action_email": "郵件", - "action_email_title": "傳送郵件", - "action_copy": "複製", - "action_copy_title": "複製電子郵件地址", - "action_edit_title": "編輯聯絡人", - "section_emails": "電子郵件地址", - "section_phones": "電話", - "section_organizations": "組織", - "section_addresses": "地址", - "section_notes": "備註", - "not_in_contacts": "不在您的聯絡人清單中", - "add_to_contacts": "新增至聯絡人", - "copied": "已複製!", - "copy_failed": "複製失敗" - }, - "send_now": "立即傳送" - }, - "email_composer": { - "read_receipt_on": "已要求傳送已讀回執(點擊以停用)", - "read_receipt_off": "請求已讀回執", - "dsn_on": "已要求遞送通知(點擊停用)", - "dsn_off": "要求遞送通知", - "format_plain_text": "切換為純文字", - "format_rich_text": "切換為 HTML 格式文字", - "require_tls_on": "已要求加密遞送(點擊停用)", - "require_tls_off": "要求加密遞送 (TLS)", - "new_message": "新郵件", - "signature_edit_hint": "點兩下即可編輯簽名檔", - "reply": "回覆", - "reply_all": "全部回覆", - "forward": "轉寄", - "reply_to": "回覆", - "reply_all_to": "全部回覆", - "forward_message": "轉寄", - "from": "寄件者", - "to": "收件者", - "cc": "副本", - "bcc": "密件副本", - "subject": "主題", - "body_placeholder": "撰寫郵件內容…", - "send": "傳送", - "cancel": "取消", - "attach": "附加", - "attach_photos": "照片與影片", - "attach_files": "檔案", - "discard": "捨棄", - "discard_draft_title": "要捨棄草稿嗎?", - "discard_draft_confirm": "您有尚未儲存的變更。要捨棄這封草稿嗎?", - "saving": "正在儲存…", - "sending": "正在傳送…", - "add_link": "新增連結", - "link_url_prompt": "輸入 URL", - "draft_saved": "草稿已儲存", - "save_failed": "儲存失敗", - "to_placeholder": "收件者電子郵件地址", - "cc_placeholder": "副本收件者", - "bcc_placeholder": "密件副本收件者", - "subject_placeholder": "主題", - "cc_label": "副本:", - "bcc_label": "密件副本:", - "subject_label": "主題:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(無主題)", - "unknown_sender": "未知", - "quote": { - "reply_header": "在 {date},{sender} 寫道:", - "forward_header": "---------- 轉寄的郵件 ----------", - "from": "寄件者:{sender}", - "date": "日期:{date}", - "subject": "主題:{subject}", - "to": "收件者:{recipients}" - }, - "remove_sub_address": "刪除子地址", - "from_override": { - "toggle_off": "覆寫", - "toggle_on": "取消覆寫", - "toggle_tooltip": "自由編輯寄件者名稱與電子郵件地址。郵件仍會透過您的寄件身分傳送。", - "name_label": "寄件者姓名", - "name_placeholder": "姓名", - "email_label": "寄件者電子郵件地址", - "email_placeholder": "alias@example.com", - "envelope_notice": "如果您的伺服器不接受此地址作為信封寄件者,將改用 {identity},收件者可以在 Return-Path 標頭中看到它。" - }, - "use_template": "範本", - "save_as_template": "儲存為範本", - "validation": { - "recipient_required": "請新增收件者", - "body_required": "請輸入郵件內容或新增附件", - "attachments_uploading": "附件仍在上傳中,上傳完成後將自動傳送", - "attachment_upload_failed": "未傳送 — 附件上傳失敗。請移除該附件後再試一次。" - }, - "upload_progress": "正在上傳 {uploaded} / {total}", - "upload_cancel": "取消上傳", - "upload_failed": "無法上傳 {filename}", - "attachment_too_large": "「{name}」超過伺服器允許的大小(每個檔案 {max})", - "attachments_total_too_large": "附件將超過伺服器每封郵件 {max} 的上限", - "drop_files": "拖放要附加的檔案", - "show_less": "顯示較少", - "send_failed": "傳送郵件失敗", - "send_timeout": "伺服器停止回應。再次傳送前,請先檢查「寄件備份」信件匣,因為郵件可能已經寄出。", - "continue_draft": "繼續編輯草稿", - "close_draft_title": "要儲存還是捨棄草稿?", - "close_draft_message": "您有尚未儲存的變更。要將這封郵件儲存為草稿,還是捨棄?", - "smime_sign_on": "S/MIME 簽名已啟用", - "smime_sign_off": "啟用 S/MIME 簽名", - "smime_encrypt_on": "S/MIME 加密已啟用", - "smime_encrypt_off": "啟用 S/MIME 加密", - "smime_encrypt_unavailable": "S/MIME 加密無法使用 — 缺少收件者憑證", - "smime_unlock_title": "解鎖 S/MIME 金鑰", - "smime_unlock_message": "請輸入密碼片語,以解鎖您的 S/MIME 簽章金鑰。", - "smime_unlock_button": "解鎖", - "smime_passphrase_placeholder": "密碼片語", - "forgot_attachment": { - "title": "忘記新增附件了嗎?", - "message": "您的郵件提到「{keyword}」,但沒有附加檔案。仍要傳送嗎?", - "send_anyway": "仍然傳送", - "back": "繼續編輯" - }, - "empty_subject": { - "title": "沒有主題就傳送嗎?", - "message": "此郵件沒有主題。仍要傳送嗎?", - "dont_ask_again": "不再提示", - "send_anyway": "仍然傳送", - "back": "繼續編輯" - }, - "schedule_send": "排程寄送", - "schedule_send_description": "選擇伺服器要在何時送出這封郵件。", - "schedule_send_required": "請選擇日期與時間。", - "schedule_send_invalid": "請輸入有效的日期與時間。", - "schedule_send_future": "請選擇未來的日期與時間。", - "schedule_send_too_late": "此時間超出伺服器允許的範圍。", - "schedule_send_unsupported": "此帳號不支援排程寄送。", - "schedule_send_cleanup_warning": "已建立排程寄送,但清理草稿失敗。", - "send_delay_unsupported": "此帳號不支援傳送延遲。", - "send_delay_unsupported_confirm": "此帳號不支援傳送延遲。是否立即傳送?", - "recipient_edit_email": "編輯電子郵件地址", - "recipient_edit_name": "編輯顯示名稱", - "recipient_email_placeholder": "電子郵件地址", - "recipient_name_placeholder": "顯示名稱", - "autocomplete_search_server": "在伺服器上搜尋", - "autocomplete_searching": "正在搜尋…", - "toolbar": { - "bold": "粗體", - "italic": "斜體", - "underline": "底線", - "strikethrough": "刪除線", - "text_color": "文字顏色", - "remove_color": "移除顏色", - "heading_1": "標題 1", - "heading_2": "標題 2", - "bullet_list": "項目符號清單", - "ordered_list": "編號清單", - "quote": "引用", - "code_block": "程式碼區塊", - "align_left": "左對齊", - "align_center": "置中對齊", - "align_right": "右對齊", - "text_direction": "文字方向 (RTL/LTR)", - "link": "連結", - "table": "表格", - "clear_formatting": "清除格式", - "undo": "撤銷", - "redo": "重做", - "add_row_above": "在上方新增一列", - "add_row_below": "在下方新增一列", - "add_column_before": "在前方新增一欄", - "add_column_after": "在後方新增一欄", - "delete_row": "刪除列", - "delete_column": "刪除欄", - "toggle_header_row": "切換標題列", - "delete_table": "刪除表格", - "pick_size": "選取大小", - "font_size": "字型大小", - "font_size_default": "預設", - "background_color": "背景顏色", - "remove_background_color": "移除背景顏色" - }, - "send_filing_warning": "郵件已傳送,但寄送後清理失敗,可能仍保留舊草稿。" - }, - "confirm_dialog": { - "confirm": "確認", - "cancel": "取消" - }, - "common": { - "loading": "正在載入…", - "error": "錯誤", - "success": "成功", - "cancel": "取消", - "save": "儲存", - "delete": "刪除", - "edit": "編輯", - "close": "關閉", - "search": "搜尋", - "refresh": "重新整理", - "settings": "設定", - "help": "說明", - "logout": "登出", - "yes": "是", - "no": "否", - "unknown": "未知", - "app_title": "網頁郵件", - "reconnecting": "連線中斷,正在嘗試重新連線…", - "rate_limited_title": "伺服器驗證目前受到速率限制。", - "rate_limited_detail": "Bulwark 已暫停背景請求,以免帳號遭鎖定。將在 {seconds} 秒後重試。", - "rate_limited_action_title": "已暫停請求,以免帳號遭鎖定。", - "rate_limited_action_detail": "Bulwark 正在等待伺服器的冷卻時間結束,再傳送更多需要驗證的請求。請在 {seconds} 秒後重試。", - "pull_to_refresh": "下拉重新整理", - "release_to_refresh": "鬆開重新整理", - "refreshing": "正在重新整理…" - }, - "notifications": { - "email_sent": "郵件傳送成功", - "email_deleted": "郵件已刪除", - "email_archived": "郵件已封存", - "emails_deleted": "{count, plural, other {已刪除 # 封郵件}}", - "emails_archived": "{count, plural, other {已封存 # 封郵件}}", - "error_archiving": "封存郵件失敗", - "error_updating": "更新郵件失敗", - "email_starred": "郵件已加上星號", - "email_unstarred": "郵件已移除星號", - "email_marked_read": "郵件已標示為已讀", - "email_marked_unread": "郵件已標示為未讀", - "copied_to_clipboard": "已複製到剪貼簿", - "source_copied": "原始碼已複製到剪貼簿", - "error_sending": "傳送郵件失敗", - "error_deleting": "刪除郵件失敗", - "error_loading": "無法載入郵件", - "new_email": "新郵件", - "new_email_from": "來自 {sender}", - "click_to_view": "點選檢視", - "email_moved": "郵件已移動", - "emails_moved": "{count} 封郵件已移動", - "moved_to_mailbox": "已移至 {mailbox}", - "move_failed": "移動郵件失敗", - "move_error": "無法將郵件移至選取的信件匣", - "email_tagged": "郵件已加標籤", - "emails_tagged": "{count} 封郵件已加標籤", - "tag_failed": "加上標籤失敗", - "identity_created": "已成功建立寄件身分", - "identity_updated": "已成功更新寄件身分", - "identity_deleted": "已刪除寄件身分", - "identity_set_primary": "已更新主要寄件身分", - "identity_create_failed": "無法建立寄件身分:{error}", - "identity_update_failed": "無法更新寄件身分:{error}", - "identity_delete_failed": "無法刪除寄件身分:{error}", - "identity_unauthorized": "您沒有使用此電子郵件地址寄信的權限", - "identity_not_found": "找不到寄件身分", - "vacation_saved": "休假自動回覆設定已儲存", - "vacation_save_failed": "無法儲存休假自動回覆設定", - "filters_saved": "篩選規則已成功儲存", - "filters_save_failed": "無法儲存篩選規則", - "filters_deleted": "篩選規則已刪除", - "templates_exported": "範本匯出成功", - "templates_imported": "已匯入{count, plural, one {# 個範本} other {# 個範本}}", - "templates_import_errors": "部分範本無法匯入", - "templates_import_empty": "檔案中找不到範本", - "export_email_error": "無法匯出郵件", - "import_email_success": "郵件已成功匯入", - "import_email_error": "無法匯入郵件" - }, - "date": { - "today": "今天", - "yesterday": "昨天", - "this_week": "本週", - "last_week": "上週", - "this_month": "本月", - "older": "更早", - "just_now": "剛剛", - "minutes_ago": "{count} 分鐘前", - "minutes_ago_plural": "{count} 分鐘前", - "hours_ago": "{count} 小時前", - "hours_ago_plural": "{count} 小時前", - "days_ago": "{count} 天前", - "days_ago_plural": "{count} 天前" - }, - "language": { - "title": "語言", - "english": "英文", - "french": "法文", - "japanese": "日文", - "spanish": "西班牙文", - "italian": "義大利文", - "german": "德文", - "dutch": "荷蘭文", - "portuguese": "葡萄牙文", - "russian": "俄文", - "select_language": "選擇語言", - "switch_to_english": "切換成英文", - "switch_to_french": "切換成法文", - "switch_to_japanese": "切換成日文", - "switch_to_spanish": "切換成西班牙文", - "switch_to_italian": "切換成義大利文", - "switch_to_german": "切換成德文", - "switch_to_dutch": "切換成荷蘭文", - "switch_to_portuguese": "切換成葡萄牙文", - "switch_to_russian": "切換成俄文", - "switching": "正在切換語言…", - "switch": "切換語言", - "current": "目前語言", - "polish": "波蘭文", - "switch_to_polish": "切換成波蘭文", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "簡體中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "切換成波斯文", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "切換成蒙古文" - }, - "settings": { - "title": "設定", - "back_to_mail": "回到郵件", - "save_success": "設定已成功儲存", - "import_success": "已成功匯入設定", - "import_error": "無法匯入設定", - "reset_confirm": "您確定要將所有設定重設為預設值嗎?", - "unsaved_changes": "您有尚未儲存的變更", - "discard_changes": "要捨棄尚未儲存的變更嗎?", - "discard": "捨棄", - "keep_editing": "繼續編輯", - "search_placeholder": "搜尋設定", - "search_clear": "清除搜尋", - "search_no_results": "找不到相符的設定", - "tabs": { - "appearance": "外觀", - "language": "語言、地區與時間", - "email": "郵件行為", - "composer": "郵件撰寫", - "privacy": "隱私權與安全", - "account": "帳號", - "identities": "寄件身分", - "vacation": "休假自動回覆", - "advanced": "進階", - "calendar": "行事曆", - "filters": "篩選規則", - "templates": "範本", - "folders": "信件匣", - "keywords": "標籤", - "security": "安全", - "files": "檔案", - "contacts": "聯絡人", - "encryption": "加密", - "protocol_handlers": "預設應用程式", - "sidebar_apps": "側邊欄應用程式", - "notifications": "通知", - "layout": "版面配置", - "reading": "閱讀", - "composing": "撰寫", - "downloads": "下載", - "content_senders": "內容與寄件者", - "about_data": "關於與資料", - "debug": "除錯" - }, - "tab_groups": { - "general": "一般", - "account": "帳號與寄件身分", - "organization": "郵件整理", - "apps": "應用程式", - "system": "系統", - "appearance": "外觀", - "mail": "郵件", - "privacy": "隱私權與安全", - "advanced": "進階" - }, - "appearance": { - "message_list_order": { - "label": "郵件清單排序", - "description": "清單中的郵件排序方式。排序由伺服器執行,因此會套用至整個信件匣,而不只是已載入的郵件。", - "preset": { - "chronological": "依時間順序(最新優先)", - "unread_first": "未讀優先", - "starred_first": "已加星號優先", - "tagged_first": "有標籤優先", - "custom": "自訂" - }, - "tag_label": "標籤", - "advanced_show": "進階:編輯排序層級", - "advanced_hide": "隱藏進階設定", - "criterion_label": "第 {n} 級條件", - "direction_label": "第 {n} 級方向", - "criteria": { - "unread": "未讀", - "starred": "已加星號", - "tag": "標籤", - "receivedAt": "接收日期", - "sentAt": "傳送日期", - "from": "寄件者", - "subject": "主題", - "size": "大小" - }, - "direction": { - "unread_first": "未讀在前", - "read_first": "已讀在前", - "starred_first": "已加星號優先", - "unstarred_first": "未加星號優先", - "tagged_first": "有標籤在前", - "untagged_first": "無標籤在前", - "newest_first": "最新在前", - "oldest_first": "最早在前", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "最大在前", - "smallest_first": "最小在前" - }, - "add_level": "新增層級", - "remove_level": "移除層級", - "move_up": "上移", - "move_down": "下移", - "tiebreak_note": "最多可設定三個層級;後面的層級用來判定同順位的項目。「最新優先」一律最後套用。", - "scope": { - "label": "套用至所有信件匣", - "description": "關閉時,排序只套用至收件匣;其他信件匣仍依時間順序排列。" - }, - "unsupported_note": "您的郵件伺服器不支援依已讀狀態、星號或標籤排序。系統會忽略這些層級,清單仍依時間順序排列。" - }, - "title": "外觀", - "description": "自訂網頁郵件的外觀與使用感受", - "theme": { - "label": "主題", - "description": "選擇您喜歡的配色方案", - "light": "淺色", - "dark": "深色", - "system": "系統" - }, - "language": { - "label": "語言", - "description": "選擇您偏好的語言" - }, - "font_size": { - "label": "字型大小", - "description": "調整文字大小,提升易讀性", - "small": "小", - "medium": "中", - "large": "大" - }, - "list_density": { - "label": "介面密度", - "description": "調整介面元素之間的間距", - "extra_compact": "超緊湊", - "compact": "緊湊", - "regular": "預設", - "comfortable": "寬鬆" - }, - "animations": { - "label": "啟用動畫", - "description": "顯示平順的轉場與效果" - }, - "toolbar_position": { - "label": "工具列位置", - "description": "選擇顯示郵件動作按鈕的位置(回覆、封存、刪除等)", - "top": "頂端", - "below_subject": "主題下方" - }, - "toolbar_labels": { - "label": "顯示工具列標籤", - "description": "在工具列圖示旁顯示文字標籤。熟悉圖示後可停用,以節省空間。" - }, - "hide_account_switcher": { - "label": "隱藏側邊欄帳號切換器", - "description": "隱藏信件匣側邊欄頂端的帳號選取器。您仍可從底部導覽列切換帳號。" - }, - "show_rail_account_list": { - "label": "在導覽列顯示帳號頭像", - "description": "在導覽列底部顯示各帳號的圓形頭像,方便快速切換,並在下方顯示登出按鈕。" - }, - "unified_mailbox": { - "label": "整合信箱", - "description": "顯示使用中帳號及其共用信件匣的整合信件匣(收件匣、寄件備份等)。", - "cross_account": { - "label": "跨所有帳號", - "description": "將整合信箱合併到所有已連線的帳號,而不只限於使用中的帳號。" - }, - "include_group": { - "label": "包含群組收件匣", - "description": "也將共用/群組收件匣合併到整合檢視中。" - } - }, - "all_mail": { - "folders_label": "整合清單中的信件匣", - "folders_description": "選擇要將此帳號的哪些信件匣合併到「所有郵件/未讀/已加星號」清單。", - "account_hint": "套用至 {account}。", - "no_folders": "沒有可用的信件匣。" - }, - "colorful_sidebar_icons": { - "label": "彩色側邊欄圖示", - "description": "依類型為信件匣與標籤圖示上色(藍色收件匣、紅色垃圾郵件、綠色寄件備份等)。停用後側邊欄會以單色顯示。" - }, - "tint_list_rows": { - "label": "依標籤顏色為清單列上色", - "description": "以第一個標籤的顏色為每封郵件列加上底色。停用後郵件列保持無底色,但標籤圓點與標籤仍會顯示該顏色。" - }, - "show_folder_total_count": { - "label": "顯示郵件總數", - "description": "在信件匣與標籤旁顯示郵件總數及未讀數量。停用後只顯示未讀數量。" - }, - "favicon_unread_badge": { - "label": "在分頁圖示顯示未讀數量", - "description": "在瀏覽器分頁圖示顯示收件匣未讀數量徽章,即使分頁不在前景也能看到新郵件。停用後不顯示徽章。" - }, - "pro_interface": { - "label": "Pro 介面(實驗性)", - "description": "適合進階使用者的桌面版配置,支援多分頁郵件瀏覽與跨帳號工作流程。標準介面不受影響,您可隨時切換回來。", - "open_label": "開啟 Pro 介面", - "back_to_standard": "返回標準介面" - }, - "cross_unread": { - "label": "未讀", - "description": "在整合信箱顯示「未讀」項目,列出所選信件匣中的未讀郵件。" - }, - "cross_starred": { - "label": "已加星號", - "description": "在整合信箱顯示「已加星號」項目,列出所選信件匣中已加星號的郵件。" - }, - "cross_all": { - "label": "所有郵件", - "description": "在整合信箱顯示「所有郵件」項目,列出所選信件匣中的所有郵件。" - } - }, - "keywords": { - "title": "電子郵件標籤", - "description": "定義用來整理郵件的標籤。這些標籤會以 JMAP 關鍵字儲存在伺服器上。", - "add_keyword": "新增標籤", - "label_field": "顯示名稱", - "label_placeholder": "例如工作、個人、緊急", - "id_field": "標籤 ID", - "id_placeholder": "例如工作、個人", - "color_field": "顏色", - "id_exists": "此標籤 ID 已存在", - "edit": "編輯標籤", - "delete": "刪除標籤", - "save": "儲存", - "add": "新增", - "cancel": "取消", - "migrating": "正在更新現有郵件的標籤…", - "migration_error": "更新現有郵件的標籤失敗", - "nesting": { - "label": "巢狀標籤", - "description": "將標籤巢狀放在其他標籤下,並在側邊欄以樹狀結構顯示。" - }, - "parent_field": "上層標籤", - "no_parent": "無上層標籤", - "too_long": "此標籤路徑過長(最多 {max} 個字元)", - "has_children_locked": "此標籤下有其他巢狀標籤,因此無法變更名稱與上層標籤。請先移動或移除這些子標籤。", - "has_children_delete": "請先移除此標籤下的巢狀標籤", - "visibility_field": "側邊欄顯示", - "visibility": { - "show": "顯示", - "unread": "有未讀時顯示", - "hide": "隱藏" - }, - "unrecognized": { - "title": "從伺服器復原標籤", - "description": "標籤名稱與顏色儲存在此瀏覽器中,標籤本身則以 JMAP 關鍵字附加在郵件上。掃描郵件以尋找這裡尚未定義的關鍵字,再將它們加回來。", - "scan": "掃描標籤", - "rescan": "重新掃描", - "scanning": "正在掃描… {total} 封郵件中的 {scanned} 封", - "none": "郵件上的所有標籤均已在此定義。", - "messages": "{count, plural, one {# 封郵件} other {# 封郵件}}", - "add_all": "全部新增({count})", - "partial": "僅掃描 {total} 封郵件中最新的 {scanned} 封。只出現在較舊郵件的標籤可能遺漏。", - "error": "無法掃描郵件中的標籤。" - } - }, - "notifications": { - "test_sound": "測試通知聲音", - "sounds": { - "default": "預設(蜂鳴聲)", - "cheerful": "快樂", - "involved": "沉浸", - "swift": "輕快手勢", - "relax": "放鬆" - }, - "push": { - "title": "背景通知", - "description": "網站關閉時仍可接收新郵件的系統通知。通知會透過 Bulwark 推送中繼傳送;中繼不會看到郵件內容。", - "relay_label": "推送中繼", - "relay_desc": "用來傳送通知的伺服器。請從管理員提供的中繼中選擇。", - "relay_locked": "由管理員設定", - "relay_locked_desc": "推送中繼已由管理員設定,無法變更。", - "status_active": "已在此裝置上啟用", - "status_inactive": "在此裝置上未啟用", - "status_unsupported": "此瀏覽器不支援 Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "status_busy": "工作中…", - "enable": "啟用", - "reenable": "重新註冊", - "disable": "停用", - "confirm_disable_title": "要停用背景通知嗎?", - "confirm_disable_message": "當網站關閉時,此裝置將停止接收提醒。", - "dismiss_aria": "關閉通知提示", - "ios_hint": "在 iOS 上,請先將網站安裝到主畫面;Safari 只會將 Web Push 傳送至已安裝的 PWA。", - "devices_title": "已註冊的裝置", - "devices_desc": "這裡列出為此帳號註冊背景通知的所有裝置。撤銷其中一部裝置的註冊後,該裝置會立即停止接收通知。", - "devices_loading": "正在載入已註冊的裝置…", - "devices_empty": "沒有裝置註冊接收背景通知。", - "devices_error": "無法載入已註冊的裝置。", - "devices_refresh": "重新整理", - "device_this": "此裝置", - "device_other": "其他裝置", - "device_status_active": "正在投遞通知", - "device_status_inactive": "從未投遞過通知", - "device_status_unknown": "此中繼沒有這部裝置的資訊", - "device_expires": "於 {date} 到期", - "revoke": "撤銷", - "confirm_revoke_title": "要撤銷此裝置的註冊嗎?", - "confirm_revoke_message": "此裝置會立即停止接收背景通知。若在該裝置上再次啟用推送,將重新註冊。", - "confirm_revoke_message_this": "此裝置會立即停止接收背景通知。" - }, - "sound_selection": { - "title": "通知聲音", - "description": "選擇通知時播放的聲音", - "choose": "聲音", - "choose_desc": "選擇通知音並點擊揚聲器圖示進行預覽" - }, - "email": { - "title": "郵件通知", - "description": "設定傳入郵件的通知", - "enabled": "郵件通知", - "enabled_desc": "新郵件到達時顯示通知", - "sound": "通知聲音", - "sound_desc": "新郵件到達時播放音訊提醒", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "行事曆通知", - "description": "設定行事曆活動的通知", - "enabled": "活動通知", - "enabled_desc": "顯示即將開始的行事曆活動提醒", - "sound": "通知聲音", - "sound_desc": "播放行事曆提醒的音訊提醒", - "invitation_parsing": "解析郵件邀請", - "invitation_parsing_desc": "偵測郵件附件中的行事曆邀請,並顯示行事曆動作" - } - }, - "language_region": { - "title": "語言、地區與時間", - "description": "語言、日期格式、時間格式以及其他區域設定", - "language": { - "label": "語言", - "description": "選擇您偏好的語言", - "english": "英文", - "french": "法文" - }, - "date_format": { - "label": "日期格式", - "description": "電子郵件清單中日期的顯示方式", - "smart": "智慧(依地區設定)", - "relative": "相對時間(1 小時前、2 天前)", - "full": "一律顯示完整日期", - "preview_today": "今天:", - "preview_this_week": "本週:", - "preview_older": "更早:" - }, - "date_locale": { - "label": "日期格式地區", - "description": "數字日期的排列順序(日、月、年)", - "auto": "自動(與語言相符)", - "iso": "ISO 8601(YYYY-MM-DD)", - "dmy": "日/月/年", - "mdy": "月/日/年" - }, - "time_format": { - "label": "時間格式", - "description": "選擇 12 小時制或 24 小時制", - "12h": "12 小時制", - "24h": "24 小時制" - }, - "first_day": { - "label": "一週的第一天", - "description": "從週日或週一開始一週", - "sunday": "週日", - "monday": "週一" - }, - "time_zone": { - "label": "時區", - "description": "使用此時區顯示郵件與行事曆時間,而非瀏覽器回報的時區", - "auto": "自動({zone})", - "preview_now": "現在:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "切換資料夾時清除搜尋", - "description": "點擊資料夾(收件匣、寄件備份等)會清除目前的搜尋並瀏覽該資料夾。關閉時,搜尋會保留並在切換到的資料夾中重新執行。" - }, - "request_read_receipt": { - "label": "預設請求已讀回執", - "description": "撰寫新郵件時預先啟用已讀回執請求。" - }, - "read_receipt_response": { - "label": "回應已讀回執請求", - "description": "當收到的郵件請求已讀回執時的處理方式。", - "ask": "每次詢問", - "always": "一律傳送", - "never": "永不傳送" - }, - "title": "郵件行為", - "description": "設定郵件的處理方式", - "mark_read": { - "label": "標示為已讀", - "description": "開啟郵件後,何時將其標示為已讀", - "instant": "立即", - "delay_3s": "3 秒後", - "delay_5s": "5 秒後", - "never": "從不" - }, - "delete_action": { - "label": "刪除動作", - "description": "刪除郵件後的行為", - "trash": "移至垃圾桶", - "trash_and_read": "移至垃圾桶並標示為已讀", - "permanent": "永久刪除", - "warning": "郵件將永久刪除且無法復原。此動作無法復原。" - }, - "message_spacing": { - "label": "郵件內距", - "description": "閱讀器中郵件內文周圍的留白", - "auto": "自動(建議)", - "always": "一律加入留白", - "edge": "無邊距" - }, - "plain_text_font": { - "label": "純文字字型", - "description": "用於純文字郵件的字型", - "mono": "等寬", - "sans": "一般(與應用程式相同)" - }, - "archive_mode": { - "label": "封存方式", - "description": "封存時如何組織郵件", - "single": "單一信件匣", - "year": "每年一個信件匣", - "month": "每月一個信件匣", - "reorganize": "重新整理現有封存", - "reorganize_success": "{count, plural, =0 {沒有需要整理的郵件} =1 {已整理 1 封郵件} other {已整理 # 封郵件}}", - "reorganize_error": "無法重新整理封存" - }, - "permanently_delete_junk": { - "label": "永久刪除垃圾郵件", - "description": "直接永久刪除垃圾郵件信件匣中的郵件,而不移至垃圾桶" - }, - "mail_layout": { - "label": "郵件版面配置", - "description": "選擇經典分割閱讀窗格、Gmail 風格的專注閱讀流程,或 Zimbra 風格的底部閱讀窗格。", - "split": "分割窗格", - "split_description": "郵件清單與閱讀窗格並排顯示。", - "focus": "專注清單", - "focus_description": "每封郵件顯示一列,開啟郵件時以全寬顯示,同時保留信件匣側邊欄。", - "horizontal": "底部閱讀窗格", - "horizontal_description": "在頂端顯示郵件清單,並在下方閱讀窗格開啟選取的郵件。" - }, - "show_preview": { - "label": "顯示預覽文字", - "description": "在清單中顯示郵件預覽", - "focus_description": "在專注模式的單列郵件清單中顯示行內預覽文字" - }, - "disable_threading": { - "label": "停用對話串群組", - "description": "將郵件個別顯示,而不是依對話串分組" - }, - "plain_text_mode": { - "label": "僅純文字", - "description": "停用富文字編輯器,並將所有郵件(包括回覆和轉寄)以純文字傳送" - }, - "auto_select_reply_identity": { - "label": "使用收件地址回覆", - "description": "回覆時,使用原始郵件的收件地址寄送。系統會先比對寄件身分;若是網域 catch-all 收件,則透過主要寄件身分傳送,但將 From 標頭改寫為別名地址。" - }, - "reply_identity_match": { - "label": "接收地址比對", - "description": "哪些接收地址算作您的地址。「僅精確地址」只從已設定的身分中選擇;「相同網域」還會將身分網域下的其他任何地址視為 catch-all 別名,並將寄件者標頭改寫為該地址。如果這些地址是通訊群組清單,請選擇精確地址。", - "exact": "僅精確地址", - "domain": "我的網域下的任意地址" - }, - "signature_position": { - "label": "簽名檔位置", - "description": "在回覆與轉寄中插入簽名檔的位置。放在引用文字上方可自然作為回覆結尾;放在下方則能保持原始郵件連續。", - "above_quote": "引用文字之前", - "below_quote": "引用文字之後" - }, - "signature_separator": { - "label": "簽名檔分隔線", - "description": "在簽名檔前加入標準的「-- 」分隔線(RFC 3676)。若希望內文直接接續簽名檔,請停用此選項。" - }, - "sub_address_delimiter": { - "label": "子地址分隔符", - "description": "用於分隔使用者名稱和子地址標籤的字元。請選擇與您的郵件伺服器一致的分隔符(例如 user{delimiter}tag@domain.com)。", - "option": "{delimiter}(user{delimiter}tag@domain.com)", - "custom": "自訂…", - "custom_input_label": "自訂分隔字元" - }, - "attachment_click_action": { - "label": "附件點擊動作", - "description": "選擇點選附件時是預覽還是直接下載", - "preview": "儘可能預覽", - "download": "立即下載" - }, - "attachment_position": { - "label": "附件位置", - "description": "選擇在郵件標頭的哪個位置顯示附件", - "beside-sender": "寄件者旁", - "below-header": "標頭下方" - }, - "emails_per_page": { - "10": "10 封郵件", - "25": "25 封郵件", - "50": "50 封郵件", - "100": "100 封郵件", - "label": "每頁郵件數", - "description": "一次載入的郵件數量" - }, - "always_light_mode": { - "label": "一律以淺色模式顯示郵件", - "description": "即使應用程式使用深色模式,也以淺色模式呈現郵件內容,避免深色模式轉換問題" - }, - "external_content": { - "label": "外部內容", - "description": "選擇如何處理圖片與外部內容", - "ask": "每次詢問", - "block": "一律封鎖", - "allow": "一律允許" - }, - "trusted_senders": { - "label": "受信任寄件者", - "description": "管理可自動載入圖片的寄件者", - "count_zero": "無", - "count_one": "1 位寄件者", - "count_other": "{count} 位寄件者", - "modal_title": "受信任寄件者", - "empty_title": "尚無受信任寄件者", - "empty_description": "檢視包含遭封鎖圖片的郵件時,點擊「一律信任此寄件者」即可新增到這裡。", - "add_manually": "手動新增", - "add_button": "新增", - "add_placeholder": "輸入電子郵件地址", - "search_placeholder": "搜尋寄件者…", - "no_results": "找不到相符的寄件者", - "remove": "移除", - "close": "關閉", - "invalid_email": "請輸入有效的電子郵件地址", - "already_added": "該寄件者已被信任", - "save_error": "儲存失敗 — 請檢視聯絡人除錯記錄以瞭解詳細資料", - "use_address_book_label": "與通訊錄同步", - "use_address_book_description": "將受信任寄件者儲存在專用的「受信任寄件者」通訊錄,以便在所有裝置間同步" - }, - "hover_actions": { - "label": "游標暫留快速動作", - "description": "選擇游標移至清單中的郵件上方時要顯示的快速動作", - "delete": "刪除", - "star": "加上/移除星號", - "mark_read": "標示為已讀/未讀", - "archive": "封存", - "tag": "標籤", - "spam": "標示為垃圾郵件", - "not_spam": "非垃圾郵件", - "none_selected": "未選取任何動作", - "mode_label": "顯示模式", - "mode_inline": "內嵌", - "mode_floating": "浮動", - "corner_label": "浮動位置", - "corner_top-left": "左上", - "corner_top-right": "右上", - "corner_bottom-left": "左下", - "corner_bottom-right": "右下" - }, - "default_mail_program": { - "label": "預設郵件應用程式", - "description": "將 {appName} 註冊為 mailto: 連結的預設郵件應用程式", - "button": "設為預設", - "success": "瀏覽器已提示設定為預設", - "error": "您的瀏覽器不支援此功能" - }, - "attachment_reminder": { - "label": "附件提醒", - "description": "郵件提到附件但沒有附加檔案時,在傳送前發出警告", - "keywords_label": "觸發關鍵字", - "keywords_description": "郵件中出現時會觸發提醒的詞彙或片語", - "add_placeholder": "新增關鍵字…", - "add": "新增", - "remove": "移除" - }, - "empty_subject_warning": { - "label": "空主題提醒", - "description": "傳送沒有主題的郵件前請求確認。" - }, - "hide_inline_image_attachments": { - "label": "在附件清單中隱藏內嵌圖片", - "description": "嵌入到郵件內文中的圖片不會作為單獨的附件顯示" - }, - "attachment_image_previews": { - "label": "在附件中顯示圖片預覽", - "description": "將圖片附件顯示為縮圖卡片,而不是通用檔案圖示" - }, - "send_delay": { - "label": "取消傳送/延遲傳送", - "description": "在伺服器端短暫延遲一般傳送。", - "off": "關閉", - "seconds": "{seconds} 秒", - "unsupported": "目前帳號未宣告支援延遲傳送。該設定仍會為其他帳號儲存。" - }, - "return_to_list_after_action": { - "label": "刪除或標示為未讀後回到清單", - "description": "刪除開啟的郵件或標示為未讀後,回到郵件清單,而不是開啟下一封郵件。" - }, - "swipe_right_action": { - "label": "向右滑動動作(行動裝置)", - "description": "在觸控裝置上,於清單中向右滑動郵件時執行的動作。" - }, - "swipe_left_action": { - "label": "向左滑動動作(行動裝置)", - "description": "在觸控裝置上,於清單中向左滑動郵件時執行的動作。" - }, - "swipe_actions": { - "none": "無", - "archive": "封存", - "delete": "刪除", - "mark_read": "切換已讀/未讀", - "star": "加上/移除星號", - "spam": "標示為垃圾郵件" - }, - "rtl_editing": { - "label": "支援從右至左編輯", - "description": "在編輯器工具列中新增方向按鈕,可將段落設定為從左到右或從右到左" - } - }, - "composer": { - "title": "郵件撰寫", - "description": "設定郵件撰寫偏好", - "autosave": { - "label": "自動儲存間隔", - "description": "自動儲存草稿的頻率", - "30s": "每 30 秒", - "1m": "每分鐘", - "2m": "每 2 分鐘", - "5m": "每 5 分鐘" - }, - "send_confirmation": { - "label": "傳送前確認", - "description": "傳送郵件前要求確認" - }, - "default_reply": { - "label": "預設回覆模式", - "description": "點擊回覆時的預設動作", - "reply": "回覆", - "reply_all": "全部回覆" - } - }, - "privacy": { - "title": "隱私權與安全", - "description": "管理您的隱私權與安全設定", - "external_images": { - "label": "封鎖外部圖片", - "description": "防止透過外部圖片進行追蹤" - }, - "session_timeout": { - "label": "工作階段逾時", - "description": "一段時間未操作後自動登出", - "never": "永不", - "30m": "30 分鐘", - "1h": "1 小時", - "4h": "4 小時" - }, - "clear_cache": { - "label": "清除快取", - "description": "移除快取資料與暫存檔案", - "button": "清除快取", - "confirm": "您確定要清除快取嗎?", - "success": "已成功清除快取" - } - }, - "account": { - "title": "帳號", - "description": "檢視您的帳號資訊", - "name_label": "顯示名稱", - "username_label": "使用者名稱", - "account_type_label": "帳號類型", - "auth_method_label": "驗證方式", - "auth_method_oauth": "單一登入(OAuth/OIDC)", - "auth_method_basic": "密碼", - "demo_account": "示範帳號", - "email": { - "label": "電子郵件地址", - "value": "{email}" - }, - "server": { - "label": "JMAP 伺服器", - "value": "{server}" - }, - "storage": { - "label": "儲存空間使用量", - "used": "已使用 {used} / {total}", - "percentage": "已使用 {percent}%" - }, - "last_sync": { - "label": "上次同步", - "value": "{time}" - }, - "accounts": { - "title": "已登入帳號", - "description": "拖曳以重新排序帳號在帳號下拉選單中的顯示順序", - "active": "使用中的帳號", - "default_badge": "預設帳號", - "set_default": "設為預設", - "switch_to": "切換到此帳號", - "move_up": "上移", - "move_down": "下移", - "drag_handle": "拖曳以重新排序", - "add": "新增帳號" - }, - "shared_accounts": { - "title": "與我共用", - "description": "您可以管理的群組與共用帳號。選取一個帳號,即可編輯其篩選規則、休假自動回覆、行事曆與聯絡人。", - "shared_label": "共用帳號" - } - }, - "scoped": { - "back": "回到我的帳號", - "managing": "正在管理:{name}" - }, - "security": { - "title": "帳號安全", - "description": "管理您的密碼、雙因素驗證與安全設定", - "detecting": "正在偵測伺服器功能…", - "not_available": "此郵件伺服器無法管理帳號安全性。可能已停用必要權限。如需詳細資料,請參閱說明文件。", - "password": { - "title": "變更密碼", - "current": "目前密碼", - "new": "新密碼", - "confirm": "確認新密碼", - "submit": "變更密碼", - "success": "已成功變更密碼", - "error_title": "變更密碼失敗", - "error_mismatch": "兩次輸入的新密碼不相符", - "error_min_length": "密碼必須至少為 8 個字元", - "error_generic": "無法變更密碼" - }, - "display_name": { - "label": "顯示名稱", - "description": "顯示在伺服器上的名稱", - "placeholder": "輸入您的顯示名稱", - "save": "儲存", - "success": "顯示名稱已更新", - "error": "無法更新顯示名稱" - }, - "totp": { - "section_title": "雙因素驗證", - "label": "TOTP 驗證", - "description": "透過基於時間的一次性密碼,增加一層安全保護", - "active": "已啟用", - "inactive": "未啟用", - "enabled": "已啟用雙因素驗證", - "disabled": "已停用雙因素驗證", - "enable_error": "無法啟用 2FA", - "disable_error": "無法停用 2FA", - "setup_instructions": "請將此 URL 複製到驗證器應用程式(Google Authenticator、Authy 等):", - "verification_code": "驗證碼", - "confirm": "確認", - "disable": "停用", - "disable_confirm_prompt": "請輸入密碼以停用雙因素驗證。", - "password_required": "需要密碼", - "code_required": "需要驗證碼", - "code_invalid": "驗證碼無效。請檢查驗證器應用程式後再試一次。" - }, - "app_passwords": { - "title": "應用程式密碼", - "description": "為不支援雙因素驗證的應用程式建立密碼", - "add": "新增", - "create": "建立", - "cancel": "取消", - "done": "完成", - "generate": "產生", - "name_label": "應用程式名稱", - "name_placeholder": "例如 Thunderbird、iPhone 郵件", - "expires_label": "到期日(選填)", - "allowed_ips_label": "允許的 IP(選填)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "用逗號或空格分隔。留空則允許任意 IP。", - "password_label": "密碼(留空時自動產生)", - "password_placeholder": "留空時自動產生", - "copy_now_warning": "請立即複製此密碼 — 之後將不再顯示。", - "added": "已建立應用程式密碼", - "removed": "已移除應用程式密碼", - "add_error": "無法建立應用程式密碼", - "remove_error": "無法移除應用程式密碼", - "none": "尚未設定應用程式密碼" - }, - "api_keys": { - "title": "API 金鑰", - "description": "為直接與伺服器通訊的指令碼和整合建立 API 金鑰", - "name_label": "金鑰名稱", - "name_placeholder": "例如:備份指令碼、CI 執行器", - "copy_now_warning": "請立即複製此 API 金鑰 — 之後將不再顯示。", - "added": "API 金鑰已建立", - "removed": "API 金鑰已刪除", - "add_error": "建立 API 金鑰失敗", - "remove_error": "刪除 API 金鑰失敗", - "none": "未設定 API 金鑰" - }, - "encryption": { - "section_title": "靜態資料加密", - "label": "郵件加密", - "description": "加密儲存在伺服器上的郵件,提供額外的隱私保障", - "active": "{type} 加密已啟用", - "inactive": "未啟用", - "enabled": "已啟用靜態資料加密", - "disabled_success": "已停用靜態資料加密", - "error": "無法更新加密設定", - "algorithm_label": "加密演算法", - "encrypt_on_append": "上傳時加密新郵件", - "allow_spam_training": "在加密郵件前允許垃圾郵件訓練", - "enable_encryption": "啟用加密", - "enable_tooltip": "使用此金鑰啟用加密", - "configure_title": "使用此金鑰啟用加密", - "enable_button": "啟用", - "disable_tooltip": "停用加密", - "enabled_success": "已啟用靜態資料加密" - }, - "public_keys": { - "name_label": "公鑰名稱", - "name_placeholder": "例如:我的金鑰", - "key_label": "公開金鑰(S/MIME 或 PGP ASCII 裝甲格式)", - "key_placeholder": "在此貼上您的公鑰", - "none": "未設定公鑰" - }, - "email_client": { - "title": "郵件用戶端設定", - "description": "使用以下登入資訊設定桌面版或行動裝置電子郵件用戶端(Thunderbird、Apple Mail、Outlook 等)", - "jmap_username_label": "JMAP 使用者名稱", - "copy": "複製", - "copied": "已複製", - "password_instructions": "使用上方的 JMAP 使用者名稱與應用程式密碼登入電子郵件用戶端。如果尚未建立應用程式密碼,請在上方區段建立一組。" - }, - "link_device": { - "title": "連結行動應用程式", - "description": "不需輸入即可登入 Bulwark Mail 行動應用程式。在這裡產生 QR Code,再從應用程式的登入畫面掃描。", - "generate": "顯示 QR Code", - "regenerate": "顯示新的 QR Code", - "instructions": "開啟 Bulwark Mail 應用程式,在登入畫面點選「掃描 QR Code」,再將相機對準這裡。", - "expires_in": "此 QR Code 將在 {seconds} 秒後到期,且只能使用一次。", - "expired": "此 QR Code 已到期。", - "generating": "正在產生…", - "error": "無法建立配對碼。請再試一次。" - } - }, - "identities": { - "title": "寄件身分", - "description": "管理可用來寄信的電子郵件地址", - "identities_count": { - "label": "您的寄件身分", - "description": "已設定用來寄信的電子郵件地址", - "count_zero": "尚無寄件身分", - "count_one": "1 個寄件身分", - "count_other": "{count} 個寄件身分" - }, - "manage": "管理寄件身分", - "sub_addressing": { - "label": "子地址功能", - "description": "使用 user+tag@domain.com 這類標籤來整理收到的郵件", - "learn_more": "瞭解更多" - } - }, - "vacation": { - "title": "休假自動回覆", - "description": "您不在時,自動回覆收到的郵件", - "loading": "正在載入自動回覆設定…", - "not_supported": "您的郵件伺服器不支援自動回覆功能。", - "fetch_error": "無法載入自動回覆設定。請再試一次。", - "status": { - "label": "休假自動回覆", - "description": "自動回覆寄信給您的人", - "active": "已啟用", - "inactive": "已停用" - }, - "date_range": { - "title": "日期範圍", - "description": "可選擇將自動回覆限制在特定期間", - "start": "開始日期", - "start_description": "留空表示不限制開始時間", - "end": "結束日期", - "end_description": "留空表示不限制結束時間" - }, - "message": { - "title": "自動回覆郵件", - "description": "作為回覆傳送的郵件內容", - "subject_label": "主題", - "subject_description": "自動回覆郵件的主題", - "subject_placeholder": "不在辦公室", - "body_label": "郵件內文", - "body_description": "純文字郵件內容", - "body_placeholder": "感謝您的來信。我目前不在辦公室,返回後會儘快回覆您的郵件。", - "html_label": "格式化郵件(HTML)", - "html_description": "新增包含連結與樣式的富文字版本。若收件者的郵件用戶端無法顯示,則改用上方純文字版本。", - "html_placeholder": "撰寫格式化的不在辦公室回覆…" - }, - "preview": { - "title": "預覽", - "show": "顯示預覽", - "hide": "隱藏預覽" - }, - "save": "儲存變更", - "saving": "正在儲存…", - "warnings": { - "end_before_start": "結束日期必須晚於開始日期", - "start_in_past": "開始日期已在過去", - "empty_body": "郵件內文為空 — 收件者將收到空白回覆", - "subject_too_long": "主旨對伺服器來說太長(最多 {max} 位元組)。", - "body_too_long": "訊息對伺服器來說太長(含格式最多 {max} 位元組)。" - } - }, - "folders": { - "role_memos": "備忘錄", - "title": "信件匣", - "description": "管理郵件信件匣並指派標準角色", - "folder_list": "您的信件匣", - "folder_list_description": "點擊信件匣圖示即可自訂", - "standard_roles": "標準信件匣角色", - "standard_roles_description": "指派哪些信件匣作為收件匣、寄件備份、垃圾桶等標準角色。", - "role_inbox": "收件匣", - "role_drafts": "草稿", - "role_sent": "寄件備份", - "role_trash": "垃圾桶", - "role_junk": "垃圾郵件", - "role_archive": "封存", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "無", - "create_folder": "建立信件匣", - "create_subfolder": "建立子信件匣", - "reorder": "上下拖曳可調整順序,左右拖曳可變更上層信件匣", - "reorder_error": "無法重新排序信件匣", - "move_error": "無法移動信件匣", - "subfolder_of": "位於 {name} 內", - "subfolder_name": "子信件匣名稱", - "new_folder_name": "信件匣名稱", - "rename": "重新命名", - "change_icon": "變更圖示", - "delete": "刪除", - "confirm_delete": "您確定要刪除「{name}」嗎?此信件匣中的郵件將移至垃圾桶。", - "create": "建立", - "cancel": "取消", - "no_folders": "尚無自訂信件匣", - "cannot_delete_role": "無法刪除具有標準角色的信件匣。請先移除該角色。", - "folder_created": "已建立信件匣", - "folder_renamed": "已重新命名信件匣", - "folder_deleted": "已刪除信件匣", - "role_updated": "已更新信件匣角色", - "error_create": "無法建立信件匣", - "error_rename": "無法重新命名信件匣", - "error_delete": "無法刪除信件匣", - "error_delete_has_children": "無法刪除信件匣:其中仍有子信件匣。請先刪除或移動它們。", - "error_delete_has_email": "無法刪除信件匣:其中仍有郵件。請先移動或刪除這些郵件。", - "error_role": "無法更新信件匣角色" - }, - "advanced": { - "title": "進階", - "description": "進階選項與開發者設定", - "debug_mode": { - "label": "除錯模式", - "description": "啟用詳細記錄以進行疑難排解" - }, - "debug_categories": { - "description": "選取要記錄的類別。停用不需要的類別,可減少主控台雜訊。", - "jmap": "JMAP 用戶端", - "jmap_description": "信件匣操作、郵件取得與 JMAP 通訊協定請求", - "calendar": "行事曆", - "calendar_description": "行事曆活動、匯入與排程訊息", - "tasks": "任務", - "tasks_description": "行事曆任務建立、取得和更新", - "auth": "驗證", - "auth_description": "登入、TOTP、權杖交換與工作階段管理", - "filters": "篩選規則", - "filters_description": "Sieve 篩選規則和休假自動回覆指令碼", - "email": "郵件檢視", - "email_description": "郵件呈現、TNEF 處理與標示為已讀", - "push": "推送通知", - "push_description": "推送通知設定和傳送", - "contacts": "聯絡人與通訊錄", - "contacts_description": "聯絡人同步、通訊錄操作與受信任寄件者" - }, - "settings_sync": { - "label": "設定同步", - "description": "跨瀏覽器和裝置同步您的設定" - }, - "sender_favicons": { - "label": "寄件者圖示", - "description": "使用企業寄件者的網站圖示作為頭像" - }, - "show_avatars_in_junk": { - "label": "在垃圾郵件信件匣顯示頭像", - "description": "在垃圾郵件信件匣中顯示寄件者的頭像與網站圖示。預設停用,以免釣魚郵件因熟悉的圖示而顯得可信。" - }, - "keyboard_shortcuts": { - "label": "鍵盤快速鍵", - "description": "檢視可用快速鍵", - "button": "檢視快速鍵" - }, - "refresh_cache": { - "label": "重新整理快取資料", - "description": "從伺服器重新載入聯絡人、行事曆與信件匣。保留帳號與工作階段,不需登出即可修正過時或錯誤的畫面。", - "button": "重新整理" - }, - "reset_settings": { - "label": "重設設定", - "description": "將所有設定還原為預設值", - "button": "重設為預設值" - }, - "export_settings": { - "label": "匯出設定", - "description": "將設定匯出為 JSON 檔案", - "button": "匯出" - }, - "import_settings": { - "label": "匯入設定", - "description": "從 JSON 檔案匯入設定", - "button": "匯入" - }, - "about": { - "title": "Bulwark 網頁郵件" - } - }, - "sidebar_apps": { - "title": "側邊欄應用程式", - "description": "管理側邊欄中的自訂應用程式與連結", - "keep_loaded": "保持應用程式載入", - "keep_loaded_description": "切換內嵌應用程式時,讓其在背景繼續執行,以避免重新載入", - "managed_title": "由您的管理員提供", - "managed_description": "這些應用程式為所有人統一設定,無法變更。", - "manage_title": "自訂應用程式", - "manage_description": "在側邊欄新增、編輯或移除自訂應用程式" - }, - "contacts": { - "title": "聯絡人", - "description": "匯入與匯出聯絡人", - "group_by_letter_label": "依首字分組", - "sort_by_last_name_label": "依姓氏排序", - "sort_by_last_name_description": "依姓氏排列聯絡人清單,讓家庭成員顯示在一起", - "group_by_letter_description": "在聯絡人清單中顯示依字母排列的分節標題", - "import_label": "匯入聯絡人", - "import_description": "從 vCard(.vcf)檔案匯入聯絡人", - "export_label": "匯出聯絡人", - "export_description": "將所有聯絡人匯出為 vCard(.vcf)檔案", - "manage_title": "通訊錄", - "manage_description": "重新命名您的通訊錄", - "no_address_books": "找不到通訊錄", - "categories_title": "類別", - "categories_description": "重新命名聯絡人類別", - "no_categories": "找不到類別" - }, - "downloads": { - "title": "下載", - "description": "自訂下載的郵件和附件的命名方式。", - "reset": "還原預設值", - "preview": "預覽:", - "email_template": { - "label": "電子郵件(.eml)檔名", - "description": "匯出郵件或將其拖到檔案系統時使用的範本。副檔名 .eml 會自動新增。" - }, - "attachment_template": { - "label": "附件檔名", - "description": "下載或拖出附件時使用的範本。如果省略 '{filename}' 和 '{ext}',將保留原始副檔名。" - }, - "bundle_template": { - "label": "多郵件 .zip 檔名", - "description": "將多封選取的郵件拖出或下載為單一 .zip 壓縮檔時使用的範本。系統會自動加上 .zip 副檔名。" - }, - "spaces": { - "label": "空格", - "description": "將產生的檔名中的空格取代為其他字元。", - "keep": "保留空格", - "underscore": "取代為 _", - "dash": "取代為 -" - }, - "lowercase": { - "label": "小寫", - "description": "將整個檔名轉為小寫。" - }, - "strip_diacritics": { - "label": "去除變音符號", - "description": "將帶重音的字母轉換為對應的 ASCII 字元(ä → a、é → e)。適用於無法正確處理 Unicode 檔名的工具。" - }, - "collapse_separators": { - "label": "縮減重複的分隔字元", - "description": "將連續的空格、底線或連字號縮減為單一字元。" - }, - "after_export": { - "label": "匯出後", - "description": "匯出為 .eml 後,可選擇移動郵件。", - "keep": "保留在信件匣", - "archive": "移至封存", - "trash": "移至垃圾桶" - } - }, - "filters": { - "title": "郵件篩選規則", - "description": "建立規則,自動整理、標記和管理收到的郵件", - "add_rule": "新增規則", - "no_rules": "尚無篩選規則", - "no_rules_description": "建立規則以自動整理收到的郵件", - "vacation_active": "休假自動回覆已開啟", - "vacation_active_description": "已啟用收到郵件時的自動回覆", - "vacation_configure": "設定", - "edit_rule": "編輯規則", - "new_rule": "新規則", - "delete_rule": "刪除規則", - "delete_confirm": "您確定要刪除此規則嗎?", - "enable": "啟用", - "disable": "停用", - "raw_editor": "原始 Sieve 編輯器", - "raw_editor_warning": "編輯原始 Sieve 指令碼可能導致視覺化規則編輯失效。這裡的變更會覆寫視覺化建構工具。", - "validate": "驗證", - "validation_success": "指令碼有效", - "validation_error": "指令碼有錯誤", - "save": "儲存規則", - "saving": "正在儲存…", - "saved": "篩選規則已成功儲存", - "save_failed": "無法儲存篩選規則", - "loading": "正在載入篩選規則…", - "not_supported": "您的郵件伺服器不支援郵件篩選規則。", - "rule_name": "規則名稱", - "rule_name_placeholder": "例如,整理訂閱郵件", - "match_all": "符合所有條件", - "match_any": "符合任一條件", - "conditions": "條件", - "add_condition": "新增條件", - "actions": "動作", - "add_action": "新增動作", - "stop_processing": "停止處理後續規則", - "include_spam": "同時移動被標記為垃圾郵件的郵件", - "attachment_type_placeholder": "例如:pdf、doc、jpg", - "value_placeholder_multi": "值(多個用逗號分隔)", - "condition_fields": { - "attachment": "附件", - "from": "寄件者", - "to": "收件者", - "cc": "副本", - "subject": "主題", - "header": "自訂標頭", - "size": "大小", - "body": "內文" - }, - "comparators": { - "has_any": "存在", - "has_type": "型別為", - "contains": "包含", - "not_contains": "不包含", - "is": "等於", - "not_is": "不是", - "starts_with": "以…開頭", - "ends_with": "以…結尾", - "matches": "符合模式", - "greater_than": "大於", - "less_than": "小於" - }, - "action_types": { - "move": "移至信件匣", - "copy": "複製到信件匣", - "forward": "轉寄至", - "mark_read": "標示為已讀", - "star": "為郵件加上星號", - "add_label": "新增標籤", - "discard": "捨棄(直接刪除)", - "reject": "附上訊息拒絕", - "keep": "保留在收件匣中", - "stop": "停止處理" - }, - "move_to_folder": "選取信件匣", - "copy_to_folder": "選取信件匣", - "forward_to": "轉寄至電子郵件地址", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "保留副本", - "forward_limit": "此伺服器每封郵件的轉寄上限:{count}。多餘的轉寄將被略過。", - "reject_message": "拒絕留言", - "reject_placeholder": "您的郵件已被拒絕", - "label_name": "標籤名稱", - "label_placeholder": "選取標籤", - "header_name": "標頭名稱", - "header_placeholder": "例如,X-Mailing-List", - "size_bytes": "大小(以位元組為單位)", - "size_placeholder": "例如,1000000", - "system_managed": "系統管理的規則", - "opaque_warning": "此指令碼是在視覺化建構工具之外編輯的。目前只能使用原始 Sieve 編輯器。", - "open_sieve_editor": "開啟原始 Sieve 編輯器", - "fetch_error": "無法載入篩選規則", - "expanded_view": "展開檢視", - "expanded_view_description": "顯示包含詳細條件與動作區塊的篩選規則", - "if": "如果", - "then": "然後", - "match_all_conditions": "全部符合", - "match_any_condition": "任一符合", - "and": "且", - "or": "或", - "cancel": "取消", - "confirm_delete": "刪除", - "rule_list": "篩選規則", - "drag_to_reorder": "拖曳以重新排序", - "match_type": "比對方式", - "reset_to_visual": "重設為視覺化建構工具", - "reset_warning": "這會捨棄目前的指令碼並重新開始。", - "confirm_reset": "重設", - "validation_empty_name": "規則名稱為必填欄位", - "validation_empty_conditions": "至少需要一個具有值的條件", - "validation_empty_actions": "至少需要一個動作", - "templates_section": "從範本開始", - "template_newsletters": "將訂閱郵件移至信件匣", - "template_receipts": "自動封存收據", - "template_important": "標示重要郵件", - "template_notifications": "篩選通知", - "sieve_editor": { - "title": "Sieve 指令碼編輯器", - "warning": "編輯原始 Sieve 指令碼可能導致視覺化規則編輯失效。這裡的變更會覆寫視覺化建構工具。", - "script_content": "Sieve 指令碼", - "valid": "指令碼有效", - "invalid": "指令碼有錯誤", - "save_warning": "儲存會覆寫所有視覺化規則,且無法復原。請再次點擊「儲存」以確認。", - "validating": "正在驗證…", - "validate": "驗證", - "cancel": "取消", - "save": "儲存", - "confirm_save": "確認儲存", - "validation_failed": "驗證請求失敗" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# 個條件} other {# 個條件}}", - "actions_count": "{count, plural, one {# 個動作} other {# 個動作}}" - }, - "origin_external": "外部", - "managed_by_tooltip": "由 {source} 管理。請在該應用程式中編輯,或使用原始 Sieve 編輯器。" - }, - "templates": { - "title": "郵件範本", - "description": "使用預留位置變數建立可重用的郵件範本", - "add": "新增範本", - "edit": "編輯範本", - "name": "範本名稱", - "name_placeholder": "例如,後續郵件", - "category": "類別", - "category_placeholder": "例如,工作、個人", - "subject": "主題", - "subject_placeholder": "郵件主題行", - "body": "內文", - "body_placeholder": "郵件內文內容…", - "recipients_placeholder": "email@example.com", - "identity": "寄件身分", - "default_identity": "預設寄件身分", - "favorite": "我的最愛", - "cancel": "取消", - "create": "建立", - "update": "更新", - "confirm_delete": "刪除", - "no_templates": "尚無範本", - "manage": "管理範本", - "count": "{count, plural, one {# 個範本} other {# 個範本}}", - "export_import": "匯出與匯入", - "export_import_description": "備份您的範本,或移轉到其他裝置", - "export": "匯出", - "import": "匯入", - "validation": { - "empty": "範本名稱為必填欄位", - "too_long": "範本名稱不得超過 200 個字元" - } - }, - "files": { - "display": { - "title": "顯示", - "description": "設定檔案和資料夾的顯示方式" - }, - "folder_layout": { - "label": "資料夾導覽", - "description": "選擇資料夾與檔案並列顯示,或在側邊欄以樹狀顯示", - "inline": "內嵌", - "sidebar": "側邊欄" - }, - "default_view": { - "label": "預設檢視", - "description": "選擇網格或清單版面配置", - "list": "清單", - "grid": "網格" - }, - "default_sort": { - "label": "預設排序", - "description": "選擇檔案的預設排序規則", - "name": "名稱", - "size": "大小", - "modified": "修改日期" - }, - "sort_direction": { - "label": "排序方式", - "description": "選擇升序或降序", - "ascending": "遞增", - "descending": "遞減" - }, - "icons": { - "title": "圖示", - "description": "設定檔案圖示外觀" - }, - "show_icons": { - "label": "顯示檔案圖示", - "description": "在檔案和資料夾旁邊顯示圖示" - }, - "colored_icons": { - "label": "彩色圖示", - "description": "使用彩色圖示取代單色圖示" - }, - "show_thumbnails": { - "label": "顯示縮圖", - "description": "圖片檔案顯示預覽,而不是圖示" - }, - "behavior": { - "title": "行為", - "description": "設定檔案瀏覽器行為" - }, - "show_hidden": { - "label": "顯示隱藏檔案", - "description": "顯示名稱以句點開頭的檔案與資料夾" - }, - "preview": { - "label": "預覽" - } - } - }, - "errors": { - "page_error_title": "出了點問題", - "page_error_description": "發生未預期的錯誤。請再試一次或返回首頁。", - "sidebar_error": "無法載入信件匣", - "email_list_error": "無法載入郵件", - "viewer_error_title": "無法顯示郵件", - "viewer_error_description": "呈現此郵件時出現問題。它可能包含不受支援的內容。", - "composer_error": "無法載入郵件撰寫", - "settings_error_title": "無法使用設定", - "settings_error_description": "無法載入設定。您的偏好設定可能不會被儲存。", - "try_again": "再試一次", - "reload": "重新載入", - "reload_emails": "重新載入郵件", - "reload_settings": "重新載入設定", - "retry": "重試", - "go_home": "前往收件匣" - }, - "context_menu": { - "reply": "回覆", - "reply_all": "全部回覆", - "forward": "轉寄", - "mark_read": "標示為已讀", - "mark_unread": "標示為未讀", - "star": "加上星號", - "unstar": "移除星號", - "pin": "固定", - "unpin": "取消固定", - "move_to": "移至…", - "archive": "封存", - "delete": "刪除", - "mark_as_spam": "回報垃圾郵件", - "not_spam": "非垃圾郵件", - "tag": "標籤", - "items_selected": "已選取 {count} 封郵件", - "edit_draft": "編輯草稿", - "cancel_scheduled_send": "取消傳送", - "reschedule_send": "重新排程", - "cancel_and_edit": "取消並編輯", - "cancel_and_compose_again": "取消並重新撰寫" - }, - "mailbox_context_menu": { - "mark_folder_read": "將信件匣標示為已讀", - "mark_folder_tree_read": "將信件匣與子信件匣標示為已讀", - "mark_all_folders_read": "將所有信件匣標示為已讀", - "new_subfolder": "新增子信件匣…", - "new_folder": "新增信件匣…", - "rename": "重新命名…", - "share": "共用...", - "import_email": "匯入 .eml 或 .zip…", - "empty_folder": "清空信件匣", - "empty_folder_generic": "清空信件匣", - "delete_folder": "刪除信件匣", - "refresh": "重新整理", - "mark_all_confirm_title": "將所有信件匣標示為已讀", - "mark_all_confirm_message": "要將您個人帳號中的所有未讀郵件標示為已讀嗎?", - "delete_confirm_title": "刪除信件匣", - "delete_confirm_message": "要永久刪除信件匣「{name}」嗎?此動作無法復原。", - "prompt_new_subfolder": "請輸入新子信件匣的名稱。", - "prompt_new_folder": "請輸入新信件匣的名稱。", - "prompt_rename": "請輸入此信件匣的新名稱。", - "placeholder_folder_name": "信件匣名稱", - "create": "建立", - "rename_confirm": "重新命名", - "toast_marked_read": "信件匣已標示為已讀", - "toast_marked_read_count": "已將 {count, plural, one {1 封郵件} other {# 封郵件}} 標示為已讀", - "toast_already_read": "沒有未讀郵件", - "toast_marked_all_read": "所有信件匣已標示為已讀", - "toast_emptied": "信件匣已清空", - "toast_folder_created": "已建立信件匣", - "toast_folder_renamed": "已重新命名信件匣", - "toast_folder_deleted": "已刪除信件匣", - "toast_error_mark_read": "無法標示為已讀", - "toast_error_empty": "無法清空信件匣", - "toast_error_create": "無法建立信件匣", - "toast_error_rename": "無法重新命名信件匣", - "toast_error_delete": "無法刪除信件匣", - "toast_error_delete_has_children": "信件匣內有子信件匣,請先移除。", - "toast_error_delete_has_email": "信件匣不是空的,請先清空。", - "delete_with_contents_title": "資料夾不是空的", - "delete_with_contents_message": "資料夾「{name}」中仍有 {count} 封郵件。要刪除該資料夾及其所有郵件嗎?此操作無法復原。", - "delete_with_contents_confirm": "刪除資料夾和郵件" - }, - "shortcuts": { - "title": "鍵盤快速鍵", - "tip": "隨時按下 ? 即可顯示此說明", - "sections": { - "navigation": "導覽", - "actions": "郵件動作", - "global": "全域", - "threads": "對話串", - "composer": "郵件撰寫" - }, - "navigation": { - "next_email": "下一封郵件", - "previous_email": "上一封郵件", - "open_email": "開啟郵件", - "close_email": "關閉/取消選取" - }, - "actions": { - "reply": "回覆", - "reply_all": "全部回覆", - "forward": "轉寄", - "star": "加上/移除星號", - "archive": "封存", - "delete": "刪除", - "mark_unread": "標示為未讀", - "mark_read": "標示為已讀", - "toggle_spam": "回報垃圾郵件/非垃圾郵件" - }, - "global": { - "compose": "撰寫新郵件", - "search": "將焦點移至搜尋框", - "help": "顯示快速鍵", - "refresh": "重新整理郵件", - "select_all": "全部選取", - "global_search": "全域搜尋" - }, - "threads": { - "expand_collapse": "展開/收合對話串" - }, - "composer": { - "send": "傳送郵件", - "schedule_send": "排程寄送", - "template_picker": "開啟範本選取器" - } - }, - "threads": { - "messages_one": "{count} 封郵件", - "messages_other": "{count} 封郵件", - "messages_tooltip": "{count, plural, one {此對話串有 # 封郵件} other {此對話串有 # 封郵件}}", - "expand": "展開對話串", - "collapse": "收合對話串", - "loading": "正在載入對話串…", - "mark_read": "將對話串標示為已讀", - "mark_unread": "將對話串標示為未讀", - "archive": "封存對話串", - "delete": "刪除對話串", - "star": "為對話串加上星號", - "unstar": "移除對話串星號", - "toggle_thread": "展開/收合對話串" - }, - "identities": { - "modal_title": "管理寄件身分", - "create_new": "建立新的寄件身分", - "edit_identity": "編輯寄件身分", - "delete_confirm": "要刪除此寄件身分嗎?此動作無法復原。", - "cannot_delete": "無法刪除此寄件身分", - "primary_identity": "主要", - "set_as_primary": "設為主要", - "no_identities": "找不到寄件身分", - "display": { - "reply_to": "回覆地址:", - "bcc": "密件副本:", - "signature": "簽名檔:", - "preview": "預覽:" - }, - "validation_errors": { - "invalid_emails": "無效電子郵件地址:{emails}", - "unknown_error": "未知錯誤" - }, - "form": { - "name_label": "顯示名稱", - "name_placeholder": "例如,工作信箱、個人信箱", - "name_required": "名稱為必填欄位", - "email_label": "電子郵件地址", - "email_placeholder": "your.email@example.com", - "email_required": "電子郵件地址為必填欄位", - "email_invalid": "請輸入有效的電子郵件地址", - "email_immutable": "寄件身分建立後,無法變更電子郵件地址", - "reply_to_label": "回覆地址(選填)", - "reply_to_placeholder": "reply@example.com", - "bcc_label": "自動密件副本(選填)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "純文字簽名檔", - "html_signature_label": "HTML 簽名檔", - "signature_byte_counter": "{bytes} / {max} 位元組", - "signature_byte_limit_reached": "已達伺服器限制", - "save": "儲存寄件身分", - "cancel": "取消", - "creating": "正在建立…", - "updating": "正在更新…" - }, - "sub_address": { - "button_tooltip": "使用子地址", - "popover_title": "新增子地址標籤", - "tag_input_placeholder": "輸入標籤(例如購物)", - "preview_label": "預覽:", - "recent_tags": "最近的標籤", - "suggested_tags": "建議", - "use_address": "使用此電子郵件地址", - "invalid_tag": "標籤只能包含英文字母、數字與連字號", - "tag_too_long": "標籤不得超過 30 個字元", - "help_text": "傳送到 user{delimiter}tag@domain.com 的郵件將送達您的收件匣", - "validation": { - "empty": "標籤不能為空", - "too_long": "標籤不得超過 {max} 個字元", - "invalid_chars": "標籤只能包含英文字母、數字與連字號" - } - }, - "badge": { - "sent_via": "透過", - "sub_address_tag": "使用子地址傳送:{tag}", - "identity_name": "使用寄件身分傳送:{name}", - "identity_short": "透過 {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "刪除", - "delete_confirm_title": "刪除寄件身分" - }, - "templates": { - "picker_title": "選擇範本", - "search_placeholder": "搜尋範本…", - "section_favorites": "我的最愛", - "section_recent": "最近使用", - "section_uncategorized": "其他", - "no_templates": "尚無範本", - "no_results": "找不到範本", - "fill_placeholders": "填寫預留位置值", - "enter_value": "輸入值…", - "preview": "預覽", - "insert_with_values": "填入值後插入", - "insert_raw": "直接插入範本", - "copy_suffix": "(複製)", - "placeholder": "變數", - "placeholders": { - "recipient_name": "收件者姓名", - "company": "公司名稱", - "date": "目前日期", - "day_of_week": "星期幾", - "sender_name": "您的姓名" - } - }, - "contacts": { - "title": "聯絡人", - "search_placeholder": "搜尋聯絡人…", - "create_new": "新增聯絡人", - "no_category": "無類別", - "rename_category": "重新命名類別", - "category_name_label": "類別名稱", - "category_renamed": "類別已重新命名", - "category_rename_failed": "重新命名類別失敗", - "category_added": "聯絡人已新增至 {name}", - "category_added_plural": "{count} 位聯絡人已新增至 {name}", - "empty_state": "尚無聯絡人", - "empty_state_title": "尚無聯絡人", - "empty_state_subtitle": "建立第一位聯絡人,或從 vCard 檔案匯入", - "empty_search": "沒有符合您搜尋條件的聯絡人", - "empty_search_hint": "嘗試其他搜尋字詞", - "empty_filtered": "沒有符合您篩選條件的聯絡人", - "empty_filtered_hint": "請調整或清除篩選條件", - "clear_search": "清除搜尋", - "import_vcard": "匯入 vCard", - "delete_confirm_title": "刪除聯絡人", - "delete_confirm": "您確定要刪除此聯絡人嗎?", - "local_mode": "聯絡人儲存在本機(伺服器不支援 JMAP 聯絡人)", - "back_to_contacts": "回到聯絡人", - "back_to_email": "回到郵件", - "open_categories": "開啟類別", - "tabs": { - "all": "全部", - "groups": "群組" - }, - "shared": { - "title": "共用" - }, - "address_books": { - "create": "新增通訊錄", - "created": "通訊錄已建立", - "create_failed": "建立通訊錄失敗", - "title": "我的通訊錄", - "shared_prefix": "共用:{name}", - "moved": "聯絡人已移至 {name}", - "moved_plural": "{count} 聯絡人已移至 {name}", - "move_failed": "無法移動聯絡人", - "address_book": "通訊錄", - "rename": "重新命名通訊錄", - "name_label": "通訊錄名稱", - "renamed": "已重新命名通訊錄", - "rename_failed": "無法重新命名通訊錄", - "default": "預設", - "set_default": "設為預設", - "default_updated": "預設通訊錄已更新", - "set_default_failed": "無法設定預設通訊錄", - "manage": "管理通訊錄", - "share": "分享通訊錄", - "new_contact_in_book": "在此通訊錄中新增聯絡人", - "delete": "刪除通訊錄", - "confirm_delete": "要刪除「{name}」嗎?此通訊錄中的所有聯絡人都會移除。", - "deleted": "通訊錄已刪除", - "delete_failed": "刪除通訊錄失敗" - }, - "detail": { - "emails": "電子郵件地址", - "phones": "電話號碼", - "organizations": "組織", - "addresses": "地址", - "notes": "備註", - "titles": "職稱與角色", - "online_services": "線上服務", - "anniversaries": "週年紀念日", - "personal_info": "個人資訊", - "languages": "語言", - "categories": "類別", - "related_contacts": "相關聯絡人", - "crypto_keys": "加密金鑰", - "cert_issuer": "簽發者", - "cert_expires": "到期日", - "cert_expired": "已到期", - "cert_fingerprint": "指紋", - "cert_algorithm": "演算法", - "import_to_smime": "匯入到 S/MIME", - "cert_already_imported": "已匯入至 S/MIME", - "cert_imported": "憑證已匯入 S/MIME 憑證庫", - "cert_import_failed": "匯入憑證失敗", - "no_contact_selected": "選取一位聯絡人以檢視詳細資料", - "compose_email": "撰寫郵件", - "copy_email": "複製電子郵件地址", - "copy_phone": "複製電話號碼", - "copy_url": "複製 URL", - "copied": "已複製到剪貼簿", - "copy_failed": "無法複製到剪貼簿", - "created": "已建立", - "updated": "上次更新", - "timezone": "時區", - "anniversary_birth": "生日", - "anniversary_death": "已故", - "anniversary_wedding": "週年紀念日", - "anniversary_other": "其他", - "personal_expertise": "專業知識", - "personal_hobby": "嗜好", - "personal_interest": "興趣", - "personal_other": "其他", - "gender": "性別", - "gender_masculine": "男性", - "gender_feminine": "女性", - "gender_other": "其他", - "gender_none": "不適用", - "gender_unknown": "未知", - "calendar": "行事曆", - "calendar_uri": "行事曆 URL", - "scheduling_uri": "排程 URL", - "freebusy_uri": "空閒/忙碌 URL", - "section_contact": "聯絡資訊", - "section_work": "工作", - "section_personal": "個人", - "email_default_label": "電子郵件", - "phone_default_label": "電話", - "address_default_label": "地址", - "online_service_default_label": "線上", - "organization_label": "組織", - "title_label": "職稱", - "role_label": "角色", - "language_label": "語言", - "related_default_label": "相關聯絡人", - "more_actions": "更多動作", - "age_years": "{count} 歲", - "years_since": "{count} 年" - }, - "activity": { - "recent_emails": "最近的郵件", - "upcoming_events": "即將開始的活動", - "no_emails": "最近沒有郵件", - "no_events": "沒有即將開始的活動", - "no_subject": "(無主題)", - "no_title": "(無標題)", - "load_failed": "無法載入", - "unknown_sender": "未知寄件者", - "all_day": "全天" - }, - "form": { - "create_title": "新聯絡人", - "edit_title": "編輯聯絡人", - "section_address_book": "通訊錄", - "select_address_book": "選擇通訊錄…", - "section_identity": "姓名與身分", - "contact_type": "聯絡人類型", - "type_person": "個人", - "type_organization": "組織", - "section_work": "工作與組織", - "prefix": "稱謂", - "prefix_placeholder": "博士、先生、女士", - "given_name": "名", - "middle_name": "中間名", - "surname": "姓", - "suffix": "後綴", - "suffix_placeholder": "Jr.、Sr.、III", - "nickname": "暱稱", - "nickname_placeholder": "暱稱", - "email": "電子郵件地址", - "email_placeholder": "email@example.com", - "phone": "電話", - "phone_placeholder": "+1 234 567 890", - "phone_type": "類型", - "phone_voice": "語音電話", - "phone_cell": "行動電話", - "phone_fax": "傳真", - "phone_pager": "呼叫器", - "phone_video": "視訊", - "phone_text": "簡訊", - "organization": "組織", - "organization_placeholder": "公司名稱", - "department": "部門", - "department_placeholder": "部門", - "job_title": "職稱", - "job_title_placeholder": "例如,軟體工程師", - "role": "角色", - "role_placeholder": "例如,團隊負責人", - "addresses": "地址", - "add_address": "新增地址", - "street": "街道", - "city": "城市", - "region": "州/地區", - "postcode": "郵遞區號", - "country": "國家", - "online_services": "線上服務", - "add_online_service": "新增線上服務", - "url_placeholder": "https://...", - "service_placeholder": "服務", - "anniversaries": "週年紀念日", - "add_anniversary": "新增日期", - "anniversary_birth": "生日", - "anniversary_wedding": "週年紀念日", - "anniversary_death": "忌日", - "anniversary_other": "其他", - "personal_info": "個人資訊", - "add_personal_info": "新增項目", - "personal_info_placeholder": "例如,攝影", - "personal_expertise": "專業知識", - "personal_hobby": "嗜好", - "personal_interest": "興趣", - "personal_other": "其他", - "level": "等級", - "level_high": "高", - "level_medium": "中", - "level_low": "低", - "categories": "類別", - "categories_placeholder": "例如,家人、朋友、同事", - "categories_hint": "輸入搜尋或新增類別", - "category_add": "新增", - "note": "備註", - "note_placeholder": "新增備註…", - "gender": "性別", - "gender_sex": "性別", - "gender_male": "男性", - "gender_female": "女性", - "gender_other": "其他", - "gender_none": "不適用", - "gender_unknown": "未知", - "gender_identity": "性別認同", - "gender_identity_placeholder": "性別認同…", - "calendar": "行事曆", - "calendar_uri": "行事曆 URL", - "scheduling_uri": "排程 URL", - "freebusy_uri": "空閒/忙碌 URL", - "context_work": "工作", - "context_private": "個人", - "add_email": "新增電子郵件地址", - "add_phone": "新增電話", - "save": "儲存", - "cancel": "取消", - "creating": "正在建立…", - "updating": "正在更新…", - "name_required": "請輸入名、姓或組織名稱", - "email_invalid": "請輸入有效的電子郵件地址", - "email_error_inline": "電子郵件地址格式無效", - "save_failed": "儲存聯絡人失敗", - "delete": "刪除", - "upload_photo": "上傳照片", - "remove_photo": "移除照片", - "photo_hint": "JPG 或 PNG,最大 10 MB。圖片會自動調整大小。", - "photo_too_large": "圖片太大(上限 10 MB)", - "photo_invalid": "圖片檔案無效", - "change_photo": "變更" - }, - "groups": { - "create": "新增群組", - "edit": "編輯群組", - "empty": "尚無群組", - "delete_confirm_title": "刪除群組", - "delete_confirm": "您確定要刪除該群組嗎?", - "name_label": "群組名稱", - "name_placeholder": "例如,團隊、家庭", - "name_required": "群組名稱為必填欄位", - "save_failed": "儲存群組失敗", - "members_label": "成員", - "search_members": "搜尋要新增的聯絡人…", - "no_members": "該群組中沒有成員", - "member_count": "{count, plural, =0 {無成員} one {1 位成員} other {# 位成員}}", - "send_email": "傳送郵件給群組", - "send_email_to": "收件者", - "send_email_cc": "副本", - "send_email_bcc": "密件副本", - "no_member_emails": "此群組沒有具有電子郵件地址的成員。" - }, - "import": { - "title": "匯入聯絡人", - "drop_hint": "點擊以選取 vCard 檔案", - "file_types": ".vcf 或 .vcard 檔案", - "no_contacts": "檔案中找不到聯絡人", - "parse_error": "無法解析 vCard 檔案", - "found": "{count, plural, one {找到 1 位聯絡人} other {找到 # 位聯絡人}}", - "duplicate": "重複", - "select_all": "全部選取", - "deselect_all": "全部取消選取", - "selected": "{count, plural, one {已選取 1 項} other {已選取 # 項}}", - "import_button": "匯入", - "importing": "正在匯入…", - "success": "{count, plural, one {已匯入 1 位聯絡人} other {已匯入 # 位聯絡人}}", - "failed": "匯入失敗", - "close": "關閉", - "file_too_large": "檔案太大(最大 5 MB)" - }, - "export": { - "title": "匯出聯絡人", - "success": "{count, plural, one {已匯出 1 位聯絡人} other {已匯出 # 位聯絡人}}" - }, - "bulk": { - "selected": "{count, plural, one {已選取 1 項} other {已選取 # 項}}", - "select_all": "全部選取", - "delete": "刪除", - "delete_confirm_title": "刪除聯絡人", - "delete_confirm": "刪除{count, plural, one {1 位聯絡人} other {# 位聯絡人}}?", - "deleted": "{count, plural, one {已刪除 1 位聯絡人} other {已刪除 # 位聯絡人}}", - "add_to_group": "新增至群組", - "choose_group": "選擇群組", - "adding_contacts": "正在新增{count, plural, one {1 位聯絡人} other {# 位聯絡人}}", - "added_to_group": "聯絡人已新增至群組", - "export": "匯出", - "clear": "清除選取" - }, - "toast": { - "created": "已建立聯絡人", - "updated": "聯絡人已更新", - "deleted": "聯絡人已刪除", - "error_create": "建立聯絡人失敗", - "error_update": "無法更新聯絡人", - "error_delete": "刪除聯絡人失敗" - }, - "context_menu": { - "open": "開啟", - "edit": "編輯", - "send_email": "傳送郵件", - "add_to_group": "新增至群組", - "export_vcard": "匯出為 vCard", - "delete": "刪除", - "call": "撥打電話", - "duplicate": "建立副本", - "print": "列印" - }, - "filters": { - "toggle": "篩選", - "select": "選取", - "clear": "清除", - "close": "關閉", - "title": "進階篩選", - "organization": "公司", - "organization_placeholder": "例如 Acme", - "job_title": "職稱", - "job_title_placeholder": "例如 設計師", - "location": "所在地", - "location_placeholder": "城市或國家", - "email_domain": "電子郵件網域", - "email_domain_placeholder": "example.com", - "birthday_month": "生日月份", - "any_month": "任何月份", - "has_email": "有電子郵件地址", - "has_phone": "有電話", - "has_photo": "有照片", - "sort_by": "排序方式", - "sort_first_name": "名", - "sort_last_name": "姓" - } - }, - "calendar": { - "title": "行事曆", - "back_to_email": "回到郵件", - "back_to_month": "回到月檢視", - "my_calendars": "行事曆", - "birthday_calendar": "生日", - "mini_calendar_change": "點選更改月份", - "views": { - "month": "月", - "week": "週", - "day": "天", - "agenda": "議程", - "today": "今天", - "month_hint": "月 (m)", - "week_hint": "週 (w)", - "day_hint": "天 (d)", - "agenda_hint": "議程 (a)", - "tasks": "任務", - "tasks_hint": "任務 (k)" - }, - "events": { - "create": "建立活動", - "edit": "編輯活動", - "delete": "刪除活動", - "details": "活動詳細資料", - "no_events": "沒有活動", - "all_day": "全天", - "more": "另有 {count} 個", - "no_title": "(無標題)", - "resize": "調整活動大小", - "duplicate": "建立副本", - "today_header": "今天", - "tomorrow_header": "明天", - "agenda_show_earlier": "顯示更早的行程", - "agenda_loading": "載入中…", - "agenda_range_start": "顯示 {date} 起的行程", - "agenda_range_end": "顯示至 {date} 的行程", - "export_ics": "匯出為 .ics", - "copy_title": "複製標題", - "copy_link": "複製會議連結", - "new_event": "新增活動", - "new_all_day_event": "新增全天活動", - "new_task": "新增任務", - "go_to_today": "前往今天" - }, - "detail": { - "add_note": "新增備註…", - "save_note": "儲存", - "note_saved": "已新增備註", - "open_link": "開啟連結", - "meeting_link": "會議連結", - "tentative": "暫定", - "cancelled": "已取消", - "delete_confirm": "刪除這個活動嗎?" - }, - "form": { - "title": "標題", - "description": "描述", - "location": "地點", - "meeting_link": "會議連結", - "start_date": "開始日期", - "end_date": "結束日期", - "start_time": "開始時間", - "end_time": "結束時間", - "all_day_event": "全天活動", - "calendar_select": "行事曆", - "save": "儲存", - "cancel": "取消", - "delete_confirm": "您確定要刪除此活動嗎?", - "color": "顏色" - }, - "participants": { - "availability": { - "title": "空閒狀態", - "free": "有空", - "busy": "忙碌", - "tentative": "暫定", - "unknown": "不是此伺服器上的使用者", - "checking": "檢查中…" - }, - "title": "參與者", - "add": "新增參與者", - "organizer": "主辦人", - "attendee": "與會者", - "accepted": "已接受", - "declined": "已拒絕", - "tentative": "暫定", - "needs_action": "待處理", - "remove": "移除", - "edit": "編輯", - "email_placeholder": "新增電子郵件地址或搜尋聯絡人", - "send_invitations": "向參與者傳送邀請", - "status_summary": "{accepted} 已接受,{pending} 待處理", - "invited_by": "來自 {name} 的邀請", - "respond_below": "使用下方按鈕回覆", - "rsvp_label": "您的回覆", - "cancel_notification": "參與者將收到取消通知", - "you_organizer": "您是主辦人", - "you_attendee": "您是與會者", - "no_participants": "沒有參與者", - "count": "{count, plural, one {# 位參與者} other {# 位參與者}}" - }, - "recurrence": { - "title": "重複", - "none": "不重複", - "daily": "每天", - "weekly": "每週", - "monthly": "每月", - "yearly": "每年", - "every_n_days": "每 {count} 天", - "every_n_weeks": "每 {count} 週", - "every_n_months": "每 {count} 個月", - "until": "直到", - "occurrences": "{count} 次", - "custom": "自訂…", - "edit_custom": "編輯自訂重複規則", - "every_n_years": "每 {count} 年", - "on_days": "{days}", - "on_day_n": "{day}日", - "on_the_nth": "{nth}{day}", - "in_month": "{month}", - "nth_1": "第一個", - "nth_2": "第二個", - "nth_3": "第三個", - "nth_4": "第四個", - "nth_last": "最後一個", - "editor_freq_day": "天", - "editor_freq_week": "週", - "editor_freq_month": "月", - "editor_freq_year": "年", - "editor_repeats_on": "重複於", - "editor_every": "每", - "editor_unit_days": "天", - "editor_unit_weeks": "週", - "editor_unit_months": "個月", - "editor_unit_years": "年", - "editor_on_day": "指定日期", - "editor_on_the": "指定星期", - "editor_in": "月份", - "editor_ends": "結束", - "editor_never": "永不", - "editor_ends_on": "於", - "editor_ends_after": "經過", - "editor_occurrences": "次後" - }, - "recurrence_scope": { - "edit_title": "編輯重複活動", - "delete_title": "刪除重複活動", - "description": "這是重複活動。您要修改哪些活動?", - "this_event": "僅限本次活動", - "this_and_future": "本次及後續活動", - "all_events": "所有活動", - "cancel": "取消", - "save": "儲存", - "delete": "刪除", - "rsvp_title": "回覆重複活動", - "rsvp_description": "這是重複活動。您的回覆適用於哪些活動?", - "respond": "回覆" - }, - "alerts": { - "title": "提醒", - "none": "無提醒", - "at_time": "活動舉行時", - "minutes_before": "{count, plural, one {提前 # 分鐘} other {提前 # 分鐘}}", - "hours_before": "{count, plural, one {提前 # 小時} other {提前 # 小時}}", - "days_before": "{count, plural, one {提前 # 天} other {提前 # 天}}", - "weeks_before": "{count, plural, one {提前 # 週} other {提前 # 週}}", - "unit_minutes_before": "分鐘前", - "unit_hours_before": "小時前", - "unit_days_before": "天前", - "unit_weeks_before": "週前", - "add": "新增提醒", - "remove": "移除提醒", - "amount": "提醒數量", - "unit": "提醒單位" - }, - "settings": { - "title": "行事曆設定", - "default_view": "預設檢視", - "week_starts_on": "一週開始於", - "time_format": "時間格式", - "default_calendar": "預設行事曆", - "default_reminder": "預設提醒", - "time_format_12h": "12 小時制", - "time_format_24h": "24 小時制", - "notifications_enabled": "活動通知", - "notifications_enabled_desc": "顯示即將開始的行事曆活動提醒", - "notification_sound": "通知聲音", - "notification_sound_desc": "播放行事曆提醒聲音", - "invitation_parsing": "解析郵件邀請", - "invitation_parsing_desc": "偵測郵件附件中的行事曆邀請,並顯示行事曆動作", - "show_time_in_month_view": "在月檢視中顯示時間", - "show_time_in_month_view_desc": "在月檢視中顯示活動時間。小螢幕會顯示完整活動項目,而不只是圓點。", - "show_week_numbers": "顯示週數", - "show_week_numbers_desc": "在迷你行事曆中顯示週數", - "calendar_free_scroll": "自由捲動", - "calendar_free_scroll_desc": "連續捲動瀏覽月、週和日,而不是一次只顯示一個時段", - "enable_tasks": "啟用任務", - "enable_tasks_desc": "在行事曆中顯示任務檢視以管理待辦事項", - "show_tasks_on_calendar": "在行事曆上顯示任務", - "show_tasks_on_calendar_desc": "在行事曆的日/週檢視中顯示任務標籤", - "hover_preview": "活動游標暫留預覽", - "hover_preview_desc": "游標移至行事曆活動上方時顯示詳細資料浮動視窗", - "hover_preview_instant": "立即", - "hover_preview_delay_500ms": "延遲 0.5 秒", - "hover_preview_delay_1s": "延遲 1 秒", - "hover_preview_delay_2s": "延遲 2 秒", - "hover_preview_off": "停用", - "organizer_identity": "以此身分發送邀請", - "organizer_identity_desc": "邀請他人參加活動時用作主辦者的行事曆地址。", - "organizer_identity_failed": "無法變更主辦者地址", - "show_birthday_calendar": "聯絡人生日行事曆", - "show_birthday_calendar_desc": "顯示包含聯絡人生日的虛擬行事曆" - }, - "days": { - "monday": "週一", - "tuesday": "週二", - "wednesday": "週三", - "thursday": "週四", - "friday": "週五", - "saturday": "週六", - "sunday": "週日", - "mon": "週一", - "tue": "週二", - "wed": "週三", - "thu": "週四", - "fri": "週五", - "sat": "週六", - "sun": "週日" - }, - "months": { - "jan": "1月", - "feb": "2月", - "mar": "3月", - "apr": "4月", - "may": "5月", - "jun": "6月", - "jul": "7月", - "aug": "8月", - "sep": "9月", - "oct": "10月", - "nov": "11月", - "dec": "12月", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "notifications": { - "event_created": "活動已建立", - "event_updated": "活動已更新", - "event_deleted": "活動已刪除", - "calendar_created": "已建立行事曆", - "calendar_deleted": "行事曆已刪除", - "event_move_error": "無法移動活動", - "event_resize_error": "無法調整活動大小", - "alert_title": "即將舉行的活動", - "alert_now": "現在開始", - "alert_in_minutes": "{count} 分鐘後", - "invitation_sent": "已傳送邀請", - "invitations_denied_title": "無法傳送邀請", - "invitations_denied": "伺服器拒絕傳送邀請:{reason}。是否不傳送邀請直接儲存活動?", - "save_without_invitations": "不傳送邀請並儲存", - "rsvp_updated": "回覆已更新", - "rsvp_error": "無法更新回覆", - "event_duplicated": "已建立活動副本", - "event_error": "無法儲存活動", - "task_due": "任務到期", - "event_exported": "活動已匯出", - "title_copied": "標題已複製", - "link_copied": "連結已複製" - }, - "status": { - "loading_calendars": "正在載入行事曆…", - "loading_events": "正在載入活動…" - }, - "quick_create": { - "placeholder": "新活動標題", - "aria_label": "快速建立活動" - }, - "nav_prev": "上一項", - "nav_next": "下一項", - "nav_open_menu": "開啟選單", - "import": { - "title": "匯入行事曆", - "tab_file": "檔案", - "tab_url": "URL", - "select_file": "選取 .ics 檔案", - "drop_file": "或將檔案拖放到這裡", - "supported_formats": "支援 iCalendar (.ics) 檔案", - "url_description": "輸入外部 iCalendar(.ics)資料來源的 URL,以匯入活動。", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "支援 iCalendar(.ics)URL", - "fetch": "取得", - "invalid_url": "請輸入有效的 URL", - "url_fetch_failed": "無法從 URL 取得行事曆", - "parsing": "正在解析行事曆檔案…", - "parsed_events": "找到 {count} 個活動", - "no_events": "檔案中找不到活動", - "select_all": "全部選取", - "deselect_all": "全部取消選取", - "target_calendar": "匯入到行事曆", - "import_button": "匯入選取項目", - "importing": "正在匯入活動…", - "success": "{count} 個活動已成功匯入", - "error": "匯入行事曆失敗", - "file_too_large": "檔案超過 10 MB 上限", - "invalid_format": "行事曆檔案格式無效" - }, - "webcal_action": { - "title": "開啟行事曆連結", - "description": "您要如何使用「{name}」?", - "import_title": "匯入一次", - "import_description": "立即取得活動,並複製到您的其中一個行事曆。", - "subscribe_title": "訂閱", - "subscribe_description": "將此行事曆保留為獨立行事曆並自動同步。", - "cancel": "取消" - }, - "management": { - "title": "行事曆管理", - "description": "建立、重新命名與自訂您的行事曆。在側邊欄的行事曆上按一下滑鼠右鍵,可快速變更顏色。", - "name": "名稱", - "name_placeholder": "行事曆名稱", - "color": "顏色", - "change_color": "變更顏色", - "random_color": "隨機新顏色", - "add_calendar": "新增行事曆", - "edit": "編輯", - "delete": "刪除", - "save": "儲存", - "create": "建立", - "cancel": "取消", - "default": "預設", - "confirm_delete": "要刪除「{name}」嗎?此行事曆中的所有活動都會移除。", - "confirm_clear": "要清除「{name}」中的所有活動嗎?此動作無法復原。", - "set_default": "設為預設", - "default_updated": "預設行事曆已更新", - "error_default": "無法設定預設行事曆", - "clear_events": "清除活動", - "events_cleared": "{count} 個活動已清除", - "error_clear": "無法清除行事曆活動", - "calendar_created": "已建立行事曆", - "calendar_updated": "行事曆已更新", - "calendar_deleted": "行事曆已刪除", - "color_updated": "行事曆顏色已更新", - "error_create": "建立行事曆失敗", - "error_update": "更新行事曆失敗", - "error_delete": "刪除行事曆失敗", - "caldav_url": "CalDAV URL", - "copy_url": "複製 CalDAV URL", - "url_copied": "CalDAV URL 已複製到剪貼簿", - "share": "分享行事曆", - "new_event_in_calendar": "在此行事曆中新增活動", - "kind": "內容", - "kind_events": "活動", - "kind_tasks": "任務", - "kind_both": "活動與任務", - "kind_hint": "決定透過 CalDAV 同步時,哪些應用程式可看到此行事曆。此設定之後無法變更。" - }, - "subscription": { - "title": "iCal 訂閱", - "section_title": "iCal 訂閱", - "description": "訂閱外部 iCalendar 資料來源。活動會自動同步到獨立的行事曆。支援 https:// 與 webcal:// URL。", - "url_label": "行事曆 URL", - "url_placeholder": "https://example.com/calendar.ics 或 webcal://...", - "name_label": "行事曆名稱", - "name_placeholder": "例如國定假日", - "color_label": "顏色", - "refresh_interval": "重新整理間隔", - "interval_15": "每 15 分鐘", - "interval_30": "每 30 分鐘", - "interval_60": "每小時", - "interval_360": "每 6 小時", - "interval_1440": "每天", - "subscribe": "訂閱", - "subscribing": "正在訂閱…", - "save": "儲存變更", - "saving": "正在儲存…", - "edit": "編輯", - "edit_title": "編輯訂閱", - "updated": "已更新「{name}」", - "update_error": "更新訂閱失敗", - "invalid_url": "請輸入有效的 URL", - "success": "已訂閱「{name}」", - "error": "新增訂閱失敗", - "refresh": "立即重新整理", - "refresh_success": "訂閱已重新整理", - "refresh_error": "重新整理訂閱失敗", - "unsubscribe": "取消訂閱", - "confirm_delete": "要取消訂閱「{name}」嗎?此行事曆及其所有活動都會移除。", - "deleted": "已移除訂閱", - "delete_error": "無法移除訂閱", - "last_refreshed": "最後更新:{time}" - }, - "tasks": { - "label": "任務", - "no_tasks": "沒有任務", - "no_title": "(無標題)", - "mark_complete": "標示為完成", - "mark_incomplete": "標示為未完成", - "filter_all": "全部", - "filter_pending": "待處理", - "filter_completed": "已完成", - "filter_overdue": "逾期", - "show_completed": "顯示已完成", - "create": "新增任務", - "edit": "編輯任務", - "title_placeholder": "任務標題", - "description_placeholder": "新增描述…", - "due_date": "到期日", - "include_time": "包含時間", - "priority": "優先順序", - "priority_none": "無", - "priority_high": "高", - "priority_medium": "中", - "priority_low": "低", - "progress": "狀態", - "progress_needs_action": "待處理", - "progress_in_process": "進行中", - "progress_completed": "已完成", - "progress_cancelled": "已取消", - "calendar": "行事曆", - "alert": "提醒", - "alert_none": "無", - "alert_at_time": "在到期日時", - "alert_5min": "5 分鐘前", - "alert_15min": "15 分鐘前", - "alert_30min": "30 分鐘前", - "alert_1hr": "1 小時前", - "alert_1day": "1 天前", - "delete": "刪除", - "cancel": "取消", - "save": "儲存", - "quick_add_placeholder": "新增任務…", - "due_today": "今天", - "due_tomorrow": "明天", - "overdue": "逾期" - } - }, - "calendar_event_notifications": { - "someone": "有人", - "invited": "{name} 邀請您參加「{title}」", - "updated": "{name} 更新了「{title}」", - "cancelled": "{name} 取消了「{title}」", - "untitled": "未命名活動" - }, - "share_notifications": { - "someone": "有人", - "shared": "{name} 與您共用了{kind}「{object}」", - "changed": "{name} 變更了您對{kind}「{object}」的存取權限", - "revoked": "{name} 移除了您對{kind}「{object}」的存取權限", - "object": { - "folder": "資料夾", - "calendar": "行事曆", - "address_book": "通訊錄", - "files": "檔案資料夾" - } - }, - "sharing": { - "title": "分享「{name}」", - "description": "授予此伺服器上的其他使用者或群組存取權。變更會立即生效。", - "no_shares": "尚未與任何人分享。", - "add_person": "新增使用者或群組", - "search_placeholder": "依姓名或電子郵件地址搜尋…", - "loading_principals": "正在載入使用者…", - "no_principals": "找不到其他使用者或群組。", - "no_match": "找不到相符項目。", - "remove": "移除存取權", - "group": "群組", - "share_added": "已授予存取權", - "share_updated": "已更新存取權", - "share_removed": "已移除存取權", - "share_failed": "無法更新分享設定", - "preset": { - "freeBusy": "僅限空閒/忙碌", - "read": "唯讀", - "readWrite": "讀取與寫入", - "manager": "管理員", - "custom": "自訂" - } - }, - "advanced_search": { - "title": "進階搜尋", - "from": "寄件者", - "from_placeholder": "寄件者電子郵件地址或姓名", - "to": "收件者", - "to_placeholder": "收件者電子郵件地址或姓名", - "subject": "主題", - "subject_placeholder": "主題包含…", - "body": "內文", - "body_placeholder": "內文包含…", - "folder": "信件匣", - "all_folders": "所有信件匣", - "has_attachment": "附件", - "date_after": "後", - "date_before": "前", - "size_min": "大於 (KB)", - "size_max": "小於 (KB)", - "starred": "已加星號", - "unread": "未讀", - "read": "已讀", - "yes": "是", - "no": "否", - "clear": "清除", - "clear_all": "全部清除", - "filters_active": "{count} 個篩選條件", - "filters_active_plural": "{count} 個篩選條件", - "toggle_filters": "更多", - "search_hint": "使用進階篩選條件進行精確搜尋", - "advanced_filters_tooltip": "進階搜尋篩選條件", - "results_found": "{count, plural, =0 {找不到結果} one {找到 # 個結果} other {找到 # 個結果}}", - "results_found_more": "找到 {count}+ 個結果", - "suggestions_recent": "最近搜尋", - "suggestions_people": "聯絡人", - "suggestions_from": "搜尋來自 {address} 的郵件", - "suggestions_to": "搜尋寄給 {address} 的郵件", - "suggestions_remove_recent": "從最近搜尋中移除" - }, - "welcome": { - "title": "歡迎使用您的信件匣", - "tip_compose": "按 c 撰寫新郵件", - "tip_shortcuts": "按 ? 檢視所有鍵盤快速鍵", - "tip_sidebar": "可在側邊欄選單找到行事曆、聯絡人與設定", - "tip_settings": "在「設定」中自訂您的使用體驗", - "got_it": "了解", - "settings": "設定", - "dismiss": "關閉", - "start_tour": "開始功能導覽" - }, - "demo_welcome": { - "title": "歡迎使用 Bulwark Mail", - "description": "直接在瀏覽器中探索功能完整的網頁郵件用戶端。所有資料都保留在您的裝置上,請自由測試各項功能。", - "feature_email": "閱讀與撰寫郵件", - "feature_organize": "標籤、星號與信件匣", - "feature_shortcuts": "鍵盤快速鍵", - "feature_privacy": "100% 私密示範", - "hint": "點擊左側任一郵件即可開始,或進行下方的功能導覽。" - }, - "files": { - "office_edit": "編輯文件", - "office_loading": "正在開啟文件編輯器…", - "office_error": "無法開啟文件編輯器。", - "office_close": "關閉編輯器", - "open_in_new_tab": "在新分頁開啟", - "title": "檔案", - "search_placeholder": "搜尋檔案…", - "empty_state_title": "尚無檔案", - "empty_state_description": "上傳檔案或建立資料夾以開始", - "upload": "上傳", - "upload_files": "上傳檔案", - "new_folder": "新增資料夾", - "new_folder_name": "資料夾名稱", - "rename": "重新命名", - "rename_title": "重新命名", - "new_name": "新名稱", - "name_forbidden_chars": "伺服器不允許名稱中包含這些字元:{chars}", - "name_reserved": "此名稱已被伺服器保留。", - "delete": "刪除", - "delete_confirm_title": "刪除項目", - "delete_confirm_message": "您確定要刪除「{name}」嗎?此動作無法復原。", - "download": "下載", - "name": "名稱", - "size": "大小", - "modified": "修改日期", - "type": "類型", - "folder": "資料夾", - "file": "檔案", - "parent_directory": "上層目錄", - "breadcrumb_root": "首頁", - "other_accounts": "其他帳號", - "no_accounts": "沒有已連線的帳號。", - "drop_files_here": "將檔案或資料夾拖放到這裡進行上傳", - "uploading": "正在上傳…", - "upload_success": "{count, plural, one {已上傳 1 個檔案} other {已上傳 # 個檔案}}", - "upload_error": "上傳檔案失敗", - "create_folder_success": "資料夾已建立", - "create_folder_error": "建立資料夾失敗", - "delete_success": "刪除成功", - "delete_error": "刪除失敗", - "rename_success": "重新命名成功", - "rename_error": "重新命名失敗", - "download_error": "下載失敗", - "not_available": "此伺服器無法使用檔案儲存功能", - "cancel": "取消", - "create": "建立", - "save": "儲存", - "no_results": "找不到符合搜尋條件的檔案", - "batch_delete_confirm_message": "您確定要刪除{count, plural, one {1 個項目} other {# 個項目}}嗎?此動作無法復原。", - "batch_delete_success": "{count, plural, one {已刪除 1 個項目} other {已刪除 # 個項目}}", - "grid_view": "網格檢視", - "list_view": "清單檢視", - "details": "詳細資料", - "path": "路徑", - "preview": "預覽", - "preview_error": "載入預覽失敗", - "cut": "剪下", - "copy": "複製", - "paste": "貼上", - "move_success": "{count, plural, one {已移動 1 個項目} other {已移動 # 個項目}}", - "move_error": "移動失敗", - "paste_success": "貼上成功", - "paste_error": "貼上失敗", - "new_text_file": "新增文字檔案", - "file_name": "檔名", - "retry": "重試", - "refresh": "重新整理", - "toggle_favorite": "切換我的最愛", - "duplicate": "建立副本", - "duplicate_success": "已成功建立副本", - "duplicate_error": "無法建立副本", - "share": "分享", - "shared": "已分享", - "shared_with_me": "與我共用", - "shared_by": "由 {name} 分享", - "create_file_success": "檔案已建立", - "create_file_error": "建立檔案失敗", - "favorites": "我的最愛", - "recent": "最近", - "properties": "屬性", - "open_folder": "開啟資料夾", - "upload_folder": "上傳資料夾", - "file_too_large": "「{name}」超過檔案大小上限({max})", - "undo": "復原", - "undo_success": "已復原動作", - "undo_error": "無法復原", - "toolbar": "檔案動作", - "open_folder_tree": "開啟資料夾樹", - "file_list": "檔案與資料夾", - "context_menu": "動作", - "settings_title": "檔案設定", - "settings_display": "顯示", - "settings_default_view": "預設檢視", - "settings_default_view_desc": "選擇網格或清單版面配置", - "settings_default_sort": "預設排序", - "settings_default_sort_desc": "選擇檔案的預設排序規則", - "settings_sort_direction": "排序方式", - "settings_sort_direction_desc": "選擇遞增或遞減順序", - "settings_ascending": "遞增", - "settings_descending": "遞減", - "settings_icons": "圖示", - "settings_show_icons": "顯示檔案圖示", - "settings_show_icons_desc": "在檔案和資料夾旁邊顯示圖示", - "settings_colored_icons": "彩色圖示", - "settings_colored_icons_desc": "使用彩色圖示取代單色圖示", - "settings_show_thumbnails": "顯示縮圖", - "settings_show_thumbnails_desc": "圖片檔案顯示預覽,而不是圖示", - "settings_behavior": "行為", - "settings_show_hidden": "顯示隱藏檔案", - "settings_show_hidden_desc": "顯示名稱以句點開頭的檔案與資料夾", - "settings_folder_layout": "資料夾導覽", - "settings_folder_layout_desc": "選擇資料夾與檔案並列顯示,或在側邊欄以樹狀顯示", - "settings_folder_layout_inline": "內嵌", - "settings_folder_layout_sidebar": "側邊欄", - "disabled_title": "檔案功能已被您的管理員停用", - "disabled_description": "透過 WebDAV 上傳大檔案可能導致 Stalwart/RocksDB 不穩定,包括記憶體不足當機及無法回收的磁碟空間。刪除檔案後,blob 儲存空間可能不會立即清除。本功能不建議用於正式環境。", - "stability_warning": "上傳大檔案可能導致伺服器不穩定。刪除檔案後,儲存空間可能不會立即清除。請謹慎使用。", - "migration_title": "正在更新您的檔案…", - "migration_description": "正在將資料夾與檔案整理成正確結構。這只會執行一次。" - }, - "smime": { - "your_certificates": "您的憑證", - "your_certificates_desc": "匯入與管理用於郵件簽章與加密的 S/MIME 憑證", - "recipient_certificates": "收件者憑證", - "recipient_certificates_desc": "收件者的公開憑證,用於加密寄給他們的郵件", - "identity_bindings": "寄件身分金鑰繫結", - "identity_bindings_desc": "將 S/MIME 憑證繫結至您的寄件身分", - "defaults_title": "預設值", - "defaults_desc": "設定預設的簽章與加密行為", - "import_pkcs12": "匯入 PKCS#12(.p12/.pfx)", - "import_public_cert": "匯入憑證", - "no_certificates": "尚未匯入憑證", - "no_recipient_certs": "無收件者憑證", - "expires": "到期日", - "expired": "已到期", - "bound_to": "繫結至", - "no_key_bound": "無", - "lock": "鎖定金鑰", - "unlock": "解鎖金鑰", - "details": "檢視詳細資料", - "delete": "刪除", - "encrypt_by_default": "預設加密", - "encrypt_by_default_desc": "當所有收件者都有憑證時自動加密郵件", - "remember_unlocked": "記住已解鎖的金鑰", - "remember_unlocked_desc": "在目前的瀏覽器工作階段保持金鑰解鎖", - "sign_default_for": "為以下項目預設簽章", - "enter_p12_passphrase": "輸入 PKCS#12 密碼片語", - "p12_passphrase_desc": "輸入保護此憑證檔案的密碼片語", - "enter_storage_passphrase": "設定儲存密碼片語", - "storage_passphrase_desc": "選擇一組密碼片語,保護此金鑰在瀏覽器中的靜態儲存", - "next": "下一步", - "import": "匯入", - "unlock_key": "解鎖金鑰", - "unlock_key_desc": "輸入儲存密碼片語,以解鎖此金鑰進行簽章或解密", - "passphrase_placeholder": "輸入密碼片語", - "confirm_passphrase_placeholder": "確認密碼片語", - "passphrase_mismatch": "兩次輸入的密碼片語不相符", - "cancel": "取消", - "processing": "處理中…", - "close": "關閉", - "certificate_details": "憑證詳細資料", - "cert_subject": "主體", - "cert_issuer": "簽發者", - "cert_email": "電子郵件地址", - "cert_serial": "序號", - "cert_validity": "有效期限", - "cert_fingerprint": "指紋(SHA-256)", - "cert_algorithm": "演算法", - "cert_capabilities": "功能", - "cert_source": "來源", - "cert_expired": "該憑證已過期", - "cert_not_yet_valid": "該憑證尚未生效", - "cap_sign": "簽章", - "cap_encrypt": "加密", - "cap_none": "無", - "show_passphrase": "顯示密碼片語", - "hide_passphrase": "隱藏密碼片語", - "sign_toggle": "簽章", - "encrypt_toggle": "加密", - "missing_recipient_certs": "缺少以下憑證:{emails}", - "missing_sender_cert": "沒有憑證繫結至此寄件身分", - "status_encrypted_ok": "此郵件已加密", - "status_encrypted_no_key": "此郵件已加密,但找不到相符的金鑰", - "status_encrypted_failed": "無法解密此郵件", - "status_signed_valid": "簽章已驗證", - "status_signed_invalid": "簽章驗證失敗", - "status_signed_expired_cert": "使用已到期的憑證簽章", - "status_signed_self_signed": "簽章有效,但憑證為自我簽署(不受信任)", - "status_signed_mismatch": "簽章有效,但簽署者與寄件者不相符", - "status_unsupported": "不支援的 S/MIME 格式", - "auto_import_signer_certs": "自動匯入簽署者憑證", - "auto_import_signer_certs_desc": "自動儲存已驗證簽章郵件中的憑證,以供日後加密郵件。", - "export": "匯出", - "enter_export_passphrase": "設定匯出密碼片語", - "export_passphrase_desc": "選擇一組密碼片語以保護匯出的 PKCS#12 檔案", - "export_storage_desc": "輸入儲存密碼片語,以解密金鑰並匯出", - "incorrect_passphrase": "密碼片語不正確" - }, - "tour": { - "step_counter": "第 {current} 步,共 {total} 步", - "skip": "跳過導覽", - "back": "上一步", - "next": "下一步", - "finish": "完成", - "take_a_tour": "進行介面功能導覽", - "restart_title": "新手導覽", - "restart_desc": "重新進行介面的逐步功能導覽", - "restart_button": "重新開始導覽", - "show_on_new_devices_title": "在新裝置上顯示", - "show_on_new_devices_desc": "首次在新裝置登入時,重新顯示歡迎橫幅與功能導覽,即使您已在其他裝置完成", - "sidebar_title": "您的信件匣", - "sidebar_desc": "這裡是信件匣側邊欄。點擊任一信件匣即可檢視其中的郵件。您可以建立信件匣、在信件匣之間拖曳郵件,並快速查看未讀數量。", - "compose_title": "撰寫郵件", - "compose_desc": "點擊這裡即可撰寫新郵件。您可以新增收件者、附件,並使用富文字格式。", - "search_title": "搜尋郵件", - "search_desc": "依寄件者、主題或內容搜尋。點擊篩選圖示,可使用日期範圍、附件與已加星號等進階選項。", - "email_list_title": "郵件清單", - "email_list_desc": "郵件會顯示在這裡。點擊一封郵件即可在右側閱讀。使用核取方塊可選取多封郵件,再批次移動、刪除或加上標籤。", - "email_viewer_title": "閱讀區域", - "email_viewer_desc": "選取的郵件會在這裡開啟。使用工具列按鈕回覆、轉寄、封存或刪除。您也可以為郵件加上星號或彩色標籤。", - "keywords_title": "顏色標籤", - "keywords_desc": "使用彩色標籤整理郵件。將郵件拖曳到標籤即可加上標籤,或在郵件上按一下滑鼠右鍵來指派標籤。", - "calendar_title": "行事曆", - "calendar_desc": "切換至行事曆以管理活動。您可以建立活動、設定提醒,並檢視日、週或月版面。", - "contacts_title": "聯絡人", - "contacts_desc": "這裡是您的通訊錄。您可以匯入聯絡人、建立群組,並點擊任一聯絡人以檢視完整詳細資料。", - "settings_title": "設定", - "settings_desc": "在這裡自訂主題、介面密度、簽名檔、篩選規則、鍵盤快速鍵、行事曆預設值等各項設定。", - "shortcuts_title": "鍵盤快速鍵", - "shortcuts_desc": "進階使用者會喜歡這項功能。隨時按下 ? 即可檢視所有可用的快速鍵。您不需使用滑鼠,就能導覽、撰寫與管理郵件。", - "compose_open_title": "郵件編輯器", - "compose_open_desc": "這裡是郵件撰寫器。新增收件者、撰寫內文、附加檔案,並使用富文字格式。您也可以儲存草稿與使用範本。", - "calendar_view_title": "您的行事曆", - "calendar_view_desc": "這裡是包含範例活動的行事曆。您可以使用工具列切換日、週、月或議程檢視。", - "create_event_title": "建立活動", - "create_event_desc": "點擊此按鈕即可建立新的行事曆活動。您可以設定標題、日期、時間並新增參與者。", - "event_modal_title": "活動詳細資料", - "event_modal_desc": "這裡是活動表單。填寫標題、選擇日期與時間,並新增地點或參與者。完成後按下儲存,或關閉並繼續其他操作。", - "contacts_list_title": "聯絡人清單", - "contacts_list_desc": "這裡顯示您的聯絡人。點擊任一聯絡人即可在右側檢視完整詳細資料。您也可以建立聯絡人、匯入 vCard 或將聯絡人整理成群組。", - "settings_tabs_title": "設定選單", - "settings_tabs_desc": "這裡列出所有設定類別。您可以自訂外觀、管理寄件身分、設定郵件篩選規則、設定行事曆等。", - "files_title": "檔案儲存", - "files_desc": "檔案瀏覽器可讓您上傳、整理與分享檔案,就像內建在郵件中的個人雲端硬碟。", - "demo_banner_title": "示範控制", - "demo_banner_desc": "目前處於示範模式,所有資料都保留在瀏覽器中。隨時點擊「重設示範」即可用全新的範例資料重新開始。", - "quota_title": "儲存使用情況", - "quota_desc": "在這裡查看信箱的儲存空間使用量。使用的空間越多,圓形指示器就會填得越滿。" - }, - "unified_mailbox": { - "search_unavailable": "整合檢視中無法使用搜尋" - }, - "quote_header": { - "reply_line": "在 {date},{from} 寫道:", - "forwarded_separator": "---------- 轉寄郵件 ----------", - "from_label": "寄件者", - "date_label": "日期", - "subject_label": "主題" - }, - "pwa_install": { - "title": "安裝 {appName}", - "description": "安裝我們的應用程式,以便快速存取並支援離線使用。", - "not_now": "暫不", - "install": "安裝", - "dont_remind": "不再提醒", - "dismiss_aria": "關閉安裝提示" - }, - "deep_link": { - "message_not_found": "已無法使用此郵件。", - "thread_not_found": "已無法使用此對話串。", - "folder_not_found": "已無法使用此信件匣。", - "event_not_found": "已無法使用此活動。", - "file_not_found": "已無法使用此檔案。", - "account_unavailable": "請登入該帳號以開啟此連結。", - "copy_message": "複製郵件連結", - "copy_conversation": "複製對話串連結", - "copy_event": "複製活動連結", - "copied": "連結已複製", - "copy_failed": "無法複製連結" - }, - "plugins": { - "oauth_callback": { - "completing": "正在完成授權…", - "completing_message": "請稍候,正在建立連線。", - "received_title": "已收到授權", - "received_message": "您可以返回開始連線的分頁。", - "failed_title": "授權失敗", - "denied": "授權被拒絕。", - "failed_detail": "授權失敗:{error}", - "missing_params": "缺少授權碼或狀態。", - "delivery_failed": "無法將授權結果傳遞至應用程式分頁。請關閉此視窗並再試一次。" - } - }, - "global_search": { - "title": "全域搜尋", - "placeholder": "搜尋郵件、聯絡人、行事曆、檔案...", - "scope_label": "搜尋範圍", - "account_label": "帳戶", - "scope_all": "全部", - "scope_mail": "郵件", - "scope_contacts": "聯絡人", - "scope_calendar": "行事曆", - "scope_files": "檔案", - "all_accounts": "所有帳戶", - "show_all": "顯示所有結果", - "no_results": "找不到「{query}」的結果", - "server_hint": "完整單字會搜尋伺服器;部分單字則比對已載入的項目", - "searching": "搜尋中...", - "account_error": "無法搜尋 {account}", - "recent_searches": "最近搜尋", - "recurring": "重複", - "no_subject": "(no subject)", - "empty_hint": "在所有帳戶中搜尋郵件、聯絡人、行事和檔案。可試試 from:、subject:、has:attachment、after:2026-01-01。", - "flat_by_date": "依日期平列", - "grouped": "分組", - "load_more": "載入更多", - "preview_empty": "選擇結果以預覽", - "open_full": "開啟", - "view_label": "檢視" - } -} +{ + "meta_description": "使用 JMAP 通訊協定的極簡網頁郵件用戶端", + "login": { + "title": "網頁郵件", + "username_label": "電子郵件地址", + "username_placeholder": "user@example.com", + "password_label": "密碼", + "password_placeholder": "請輸入密碼", + "jmap_endpoint_label": "JMAP 伺服器", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "伺服器必須允許來自此網域的 CORS 請求。", + "jmap_server_label": "伺服器", + "jmap_server_auto_picked": "已根據您的電子郵件網域選擇伺服器。", + "sign_in": "登入", + "signing_in": "正在登入…", + "loading": "正在載入…", + "reconnecting": "連線中斷,正在嘗試重新連線…", + "error": { + "invalid_credentials": "電子郵件地址或密碼不正確。請檢查您的登入資訊後再試一次。", + "invalid_token": "伺服器不接受此存取權杖。請確認權杖完整,且未過期或遭撤銷。", + "connection_failed": "無法連線至伺服器。請檢查網路連線後再試一次。", + "cors_blocked": "可連線至伺服器,但跨來源請求遭到封鎖。請檢查 JMAP 伺服器的 CORS 設定,並允許此網域。", + "server_error": "伺服器目前無法使用,請稍後再試。", + "generic": "發生意外錯誤。如果問題持續存在,請聯絡管理員。", + "totp_required": "需要雙因素驗證碼。請在下方輸入驗證碼。", + "totp_invalid": "驗證碼無效。請檢查驗證器應用程式後再試一次。", + "oauth_discovery_failed": "SSO 已啟用,但無法連線至身分提供者。請檢查 OAuth 設定。", + "token_exchange_failed": "密碼與驗證碼已通過,但郵件伺服器拒絕為此應用程式建立工作階段。請聯絡管理員檢查其 OAuth 用戶端設定。" + }, + "show_password": "顯示密碼", + "hide_password": "隱藏密碼", + "totp_toggle": "我有 2FA 驗證碼", + "token_toggle": "使用存取權杖登入", + "password_toggle": "使用密碼登入", + "token_label": "存取權杖", + "token_placeholder": "貼上 API 權杖", + "token_hint": "請在郵件服務供應商的設定中建立 API 權杖,然後貼到這裡。", + "remember_me": "記住我", + "config_error": { + "title": "設定錯誤", + "fetch_failed": "無法載入應用程式設定。請稍後再試。", + "server_not_configured": "郵件伺服器尚未設定。請聯絡您的管理員。" + }, + "remove_from_history": "從歷史記錄中移除", + "totp_label": "驗證碼", + "totp_placeholder": "000000", + "session_expired": "工作階段已過期,請重新登入。", + "dismiss": "關閉", + "or": "或", + "sign_in_sso": "使用 SSO 登入", + "add_account_title": "新增帳號", + "add_account_subtitle": "使用其他帳號登入", + "cancel": "取消", + "website": "網站", + "imprint": "法律聲明", + "privacy_policy": "隱私權政策", + "try_demo": "試用示範版", + "demo_description": "使用範例資料自由探索,不需要帳號", + "demo_launching": "正在啟動示範版…", + "demo_login_button": "啟動示範版", + "demo_tagline": "體驗功能完整的電子郵件用戶端,不需要帳號。", + "demo_no_signup": "不需要註冊,使用範例資料自由探索", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "正在完成登入…", + "oauth_error": { + "title": "驗證失敗", + "invalid_state": "安全驗證失敗。請嘗試重新登入。", + "missing_params": "缺少授權資料。請嘗試重新登入。", + "token_exchange_failed": "無法完成身分驗證。請再試一次。", + "access_denied": "存取遭拒。請聯絡管理員。", + "back_to_login": "返回登入頁面" + } + }, + "sidebar": { + "close": "關閉", + "compose": "撰寫郵件", + "compose_hint": "撰寫郵件 (c)", + "search_placeholder": "搜尋郵件…", + "search_placeholder_hint": "搜尋郵件…(按 /)", + "storage": "儲存空間", + "storage_used": "已使用", + "storage_free": "剩餘", + "storage_total": "總計", + "sign_out": "登出", + "sign_out_of": "登出 {account}", + "sign_out_all": "登出所有帳號", + "add_account": "新增帳號", + "set_as_default": "設為預設", + "switch_account": "切換帳號", + "contacts": "聯絡人", + "calendar": "行事曆", + "settings": "設定", + "admin": "管理", + "files": "檔案", + "loading_mailboxes": "正在載入信件匣…", + "push_connected": "即時更新已啟用", + "push_disconnected": "即時更新未啟用", + "keyboard_shortcuts": "鍵盤快速鍵", + "theme": { + "light": "淺色模式", + "dark": "深色模式", + "system": "系統主題" + }, + "language": { + "title": "語言" + }, + "mailboxes": { + "inbox": "收件匣", + "sent": "寄件備份", + "drafts": "草稿", + "trash": "垃圾桶", + "archive": "封存", + "starred": "已加星號", + "all_mail": "所有郵件", + "spam": "垃圾郵件", + "important": "重要" + }, + "unified_inbox": "整合收件匣", + "unified_sent": "所有寄件備份", + "unified_drafts": "所有草稿", + "unified_trash": "所有垃圾桶", + "unified_archive": "所有封存郵件", + "unified_junk": "所有垃圾郵件", + "all_accounts": "所有帳號", + "all_accounts_loading": "正在載入帳號:{loaded}/{total}", + "unified_mailbox": "整合信箱", + "expand": "展開", + "collapse": "收合", + "expand_tooltip": "展開", + "collapse_tooltip": "收合", + "mobile": { + "search": "搜尋", + "compose": "撰寫郵件", + "go_back": "返回", + "toggle_menu": "切換選單" + }, + "clear_search": "清除搜尋", + "vacation_active": "休假自動回覆已開啟", + "demo_banner": "示範模式", + "demo_reset": "重設", + "demo_tour": "功能導覽", + "tags": "標籤", + "show_all_tags": "顯示全部({count})", + "show_fewer_tags": "顯示較少", + "folders": "信件匣", + "your_folders": "我的資料夾", + "create_folder": "建立資料夾", + "show_more": "更多", + "show_fewer": "收合", + "shared": "共用", + "shared_account_hidden": "已從側邊欄隱藏。可在 設定 › 版面配置 中重新顯示。", + "notifications_muted": "通知已靜音", + "mail": "郵件", + "nav_label": "導覽", + "add_app": "應用程式", + "scheduled": "排程寄送", + "unified_all_unread": "全部未讀", + "unified_all_starred": "全部已加星號", + "unified_all_mail": "全部郵件", + "remove_account": "移除帳號", + "remove_account_confirm": "要從這部裝置移除 {account} 嗎?您稍後仍可重新新增。", + "labels": "標籤", + "create_label": "建立新標籤", + "mv_tasks": "工作", + "mv_side_panel": "側邊面板", + "mv_open_full": "以完整檢視開啟", + "mv_previous_day": "前一天", + "mv_next_day": "後一天", + "mv_add_another_account": "新增其他帳戶", + "mv_manage_account": "管理您的帳戶", + "mv_storage_used": "已使用 {total} 中的 {percent}%" + }, + "protocol_handlers": { + "title": "預設應用程式", + "description": "選擇電子郵件與行事曆連結是否要在 Bulwark 開啟。Bulwark 會註冊為 mailto: 與 webcal: 連結的通訊協定處理常式。", + "unsupported": "此瀏覽器或連線不支援手動註冊通訊協定處理常式。您仍可嘗試透過瀏覽器或作業系統設定,使用已安裝的 PWA。", + "mailto_label": "電子郵件連結", + "mailto_description": "使用預先填入的撰寫視窗,在 Bulwark 開啟 mailto: 連結。", + "protocol_open_mode_label": "開啟通訊協定連結時", + "protocol_open_mode_description": "選擇 Bulwark 要在新分頁開啟 mailto: 與 webcal: 連結,或使用現有的工作階段。現有工作階段選項需要通知權限;若瀏覽器阻止視窗取得焦點,您可以點擊備用通知,將 Bulwark 移到最上層。", + "protocol_open_mode_active_session": "盡可能在現有工作階段開啟", + "protocol_open_mode_new_tab": "一律開啟新分頁", + "focus_notification_title": "開啟 Bulwark", + "focus_notification_body": "連結已在 Bulwark 開啟。點擊即可將視窗移到最上層。", + "webcal_label": "行事曆連結", + "webcal_description": "使用預先填入的行事曆訂閱對話方塊,在 Bulwark 開啟 webcal: 連結。", + "register_mailto": "註冊電子郵件應用程式", + "register_webcal": "註冊行事曆應用程式", + "mailto_registered": "已要求註冊電子郵件處理常式", + "webcal_registered": "已要求註冊行事曆處理常式", + "registration_failed": "通訊協定處理常式註冊失敗", + "opening_mailto": "正在開啟撰寫視窗…", + "opening_webcal": "正在開啟行事曆…", + "browser_note": "您的瀏覽器或作業系統可能會要求您確認,而且可能必須先安裝 Bulwark,才能選為預設應用程式。", + "select_account_title": "選擇帳號", + "select_mailto_account": "選擇用於開啟此電子郵件連結的帳號。", + "select_webcal_account": "選擇用於開啟此行事曆連結的帳號。", + "select_account_note": "此選擇僅適用於此協議連結。", + "detail_to": "收件者", + "detail_subject": "主題", + "detail_no_subject": "無主題", + "detail_calendar": "行事曆", + "detail_source": "來源", + "active_account": "使用中", + "switching_account": "正在切換帳號…" + }, + "sidebar_apps": { + "modal_title": "側邊欄應用程式", + "add_new": "新增應用程式", + "edit_app": "編輯應用程式", + "name_label": "名稱", + "name_placeholder": "我的應用程式", + "name_required": "名稱為必填欄位", + "url_label": "URL", + "url_required": "URL 為必填欄位", + "url_invalid": "請輸入有效的 http 或 https URL", + "icon_label": "圖示", + "icon_required": "圖示為必填欄位", + "open_mode_label": "開啟方式", + "open_new_tab": "新分頁", + "open_inline": "內嵌", + "cancel": "取消", + "add": "新增", + "update": "更新", + "delete": "刪除", + "delete_confirm_title": "刪除應用程式", + "delete_confirm": "您確定要刪除「{name}」嗎?", + "no_apps": "尚未新增任何應用程式", + "no_apps_hint": "在側邊欄新增自訂應用程式與連結", + "managed_title": "由您的管理員提供", + "managed_description": "這些應用程式為所有人統一設定,無法變更。", + "managed_badge": "受管理", + "search_icons": "搜尋圖示…", + "show_popular": "熱門", + "show_all": "全部", + "no_icons_found": "找不到圖示", + "inline_badge": "內嵌", + "tab_badge": "新標籤頁", + "show_on_mobile": "在行動裝置上顯示" + }, + "email_list": { + "columns": { + "from": "寄件者", + "to": "收件者", + "subject": "主旨", + "date": "日期", + "size": "大小" + }, + "no_emails": "找不到郵件", + "no_emails_description": "此信件匣中沒有郵件", + "no_search_results": "找不到結果", + "no_search_results_description": "請調整搜尋條件或篩選器", + "loading": "正在載入郵件…", + "unread": "未讀", + "to_me": "寄給我", + "to_recipients": "致 {count} 收件者", + "and_others": "另有 {count} 人", + "draft": "草稿", + "starred": "已加星號", + "pinned": "已釘選", + "replied": "已回覆", + "forwarded": "已轉寄", + "has_attachment": "有附件", + "conversations_range": "第 {from}–{to, number} 封,共 {total, number} 封", + "conversations_count": "顯示 {count} 個對話串,共 {total} 個", + "conversations_count_plus": "{count}+ 個對話串", + "conversations_count_simple": "{count} 個對話串", + "no_conversations": "目前沒有對話串", + "loading_more": "正在載入更多郵件…", + "no_more_emails": "沒有更多郵件可載入", + "batch_actions": { + "select": "選取郵件", + "select_all": "全部選取", + "selected_messages": "已選取 {count, plural, one {1} other {#}} 封郵件", + "mark_read": "標示為已讀", + "mark_unread": "標示為未讀", + "delete": "刪除", + "delete_confirm_title": "刪除郵件", + "delete_confirm_message": "您確定要刪除{count, plural, one {1 封郵件} other {# 封郵件}}嗎?", + "clear_selection": "清除選取" + }, + "permanent_delete": "永久刪除", + "permanent_delete_confirm_title": "永久刪除", + "permanent_delete_confirm_message": "這封郵件將永久刪除。此動作無法復原。", + "permanent_delete_confirm_batch_message": "將永久刪除{count, plural, one {1 封郵件} other {# 封郵件}}。此動作無法復原。", + "empty_folder": { + "button": "清空信件匣", + "confirm_title": "清空信件匣", + "confirm_message": "此信件匣中的所有郵件都將永久刪除。此動作無法復原。", + "confirm_message_trash": "此信件匣中的所有郵件都將移至垃圾桶。", + "confirm_button": "清空信件匣", + "junk_hint": "您可以清空垃圾郵件信件匣,永久刪除其中的所有郵件。", + "trash_hint": "您可以清空垃圾桶,永久刪除其中的所有郵件。" + }, + "no_scheduled_emails": "沒有排程寄送的郵件", + "no_scheduled_emails_description": "排定稍後寄送的郵件會顯示在這裡。", + "scheduled_count": "{count, plural, one {1 封排程寄送郵件} other {# 封排程寄送郵件}}", + "scheduled_actions_hint": "排程郵件可從排程動作中取消、重新排程或編輯。", + "cancel_scheduled_send": "取消傳送", + "reschedule_send": "重新排程", + "cancel_and_edit": "取消並編輯", + "cancel_and_compose_again": "取消並重新撰寫", + "reschedule_prompt": "輸入新的日期與時間,例如 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "寄件者要求在您開啟這封郵件時收到通知。", + "send": "傳送回執", + "ignore": "忽略", + "sent": "已讀回執已傳送。", + "send_failed": "無法傳送已讀回執", + "mdn_subject": "已讀:{subject}", + "mdn_body": "這是您傳送給 {recipient} 的郵件已讀回執。\n\n注意:此回執僅表示郵件已顯示在收件者的電腦上,並不保證收件者已閱讀或理解郵件內容。" + }, + "no_email_selected": "未選取郵件", + "no_email_description": "請從清單選取一封郵件,即可在這裡檢視", + "no_conversation_selected": "未選取對話串", + "no_conversation_description": "請從清單選取一個對話串,即可在這裡閱讀", + "compose": "撰寫郵件", + "compose_hint": "撰寫新郵件", + "no_subject": "(無主題)", + "no_body_content": "(無內文內容)", + "no_preview_available": "無可用預覽", + "show_quoted_text": "顯示引用文字", + "hide_quoted_text": "隱藏引用文字", + "loading_email": "正在載入郵件…", + "loading": "正在載入…", + "reply": "回覆", + "reply_all": "全部回覆", + "forward": "轉寄", + "delete": "刪除", + "archive": "封存", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "加上星號", + "unstar": "移除星號", + "mark_unread": "標示為未讀", + "mark_read": "標示為已讀", + "unread": "未讀", + "read": "已讀", + "spam_short": "垃圾郵件", + "not_spam_short": "非垃圾郵件", + "move": "移動", + "print": "列印", + "view_source": "檢視原始碼", + "fullscreen": "全螢幕", + "exit_fullscreen": "離開全螢幕", + "view_in_dark_mode": "深色模式檢視", + "view_in_light_mode": "淺色模式檢視", + "export_email": "匯出為 .eml", + "forward_as_attachment": "以附件形式轉寄", + "import_email": "匯入 .eml 或 .zip", + "keyboard_shortcuts": "鍵盤快速鍵 (?)", + "email_source": "郵件原始碼", + "draft_banner": "這是一封草稿郵件", + "edit_draft": "編輯", + "copy_source": "複製到剪貼簿", + "source_copied": "原始碼已複製到剪貼簿", + "attachments": "附件", + "important": "重要", + "download": "下載", + "download_all": "全部下載", + "from": "寄件者", + "to": "收件者", + "cc": "副本", + "bcc": "密件副本", + "date": "日期", + "subject": "主題", + "show_details": "顯示詳細資料", + "hide_details": "隱藏詳細資料", + "external_content_warning": "圖片與外部內容已遭封鎖", + "load_external_content": "載入圖片", + "trust_sender": "一律信任此寄件者", + "sender_check": { + "unverified_label": "寄件者未經驗證", + "failed_label": "寄件者驗證失敗", + "unverified": "這封郵件聲稱來自 {domain},但沒有任何證據可以證實。它沒有 {domain} 的有效簽章,也不是從 {domain} 授權的伺服器寄出。", + "unverified_sent_from": "這封郵件聲稱來自 {domain},但它是從 {host} 寄出的,而且沒有 {domain} 的有效簽章。", + "failed": "這封郵件聲稱來自 {domain},但未通過該網域的寄件者驗證,可能是偽造的。", + "failed_sent_from": "這封郵件聲稱來自 {domain},但它是從 {host} 寄出的,而且未通過 {domain} 的寄件者驗證,可能是偽造的。", + "caution": "請小心其中的連結、附件,以及要求提供密碼或付款資訊的請求。" + }, + "back_to_list": "回到清單", + "view_contact": "檢視聯絡人", + "message_details": "郵件詳細資料", + "more_reply_options": "更多回覆選項", + "set_tag": "設定標籤", + "tag": "標籤", + "more_actions": "更多動作", + "previous": "上一封", + "next": "下一封", + "move_to": "移至…", + "remove_tag": "移除標籤", + "tag_filter_placeholder": "篩選標籤", + "tag_no_matches": "沒有相符的標籤", + "more_count": "另有 {count} 個", + "characters_count": "{count} 個字元", + "quick_reply_placeholder": "撰寫快速回覆…", + "more_options": "更多選項", + "sending": "傳送中…", + "security_authentication": "安全性與驗證", + "technical_details": "技術詳細資料", + "message_id_label": "郵件 ID:", + "reply_to_label": "回覆至:", + "delivery_time_label": "送達時間:", + "conversation_part_label": "對話串中的部分:", + "previous_messages": "先前的 {count} 封郵件", + "previous_messages_plural": "先前的 {count} 封郵件", + "time": { + "day": "天", + "days": "天", + "hour": "小時", + "hours": "小時", + "minute": "分鐘", + "minutes": "分鐘" + }, + "unknown_sender": "未知", + "recipient_me": "我", + "recipient_and_others": "{name} 及其他 {count} 人", + "recipient_to_prefix": "收件者:", + "authentication": { + "title": "郵件身分驗證", + "status": { + "verified": "已驗證", + "warning": "警告", + "none": "未驗證" + }, + "spf": { + "pass": "SPF 通過", + "fail": "SPF 失敗", + "none": "無 SPF" + }, + "dkim": { + "pass": "DKIM 有效", + "fail": "DKIM 無效", + "none": "無 DKIM" + }, + "dmarc": { + "pass": "DMARC 通過", + "fail": "DMARC 失敗", + "none": "無 DMARC" + }, + "spam_score": "垃圾郵件評分", + "tooltip_spf": "Sender Policy Framework:驗證傳送伺服器是否有權代表該網域傳送郵件", + "tooltip_dkim": "DomainKeys Identified Mail:使用密碼編譯簽章,確認郵件在傳送過程中未遭竄改", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance:確保 SPF 與 DKIM 和寄件者網域一致,並設定驗證失敗時的處理原則", + "policy": "策略", + "result": { + "pass": "通過", + "fail": "失敗", + "softfail": "軟失敗", + "neutral": "中立", + "permerror": "永久錯誤", + "temperror": "暫時性錯誤", + "none": "無" + } + }, + "details": { + "recipients_routing": "收件者與路由", + "authentication_security": "身分驗證與安全", + "identifiers_threading": "識別碼與對話串", + "mailing_list": "郵件清單", + "message_properties": "郵件屬性", + "sent": "傳送時間", + "received": "接收時間", + "delivery_time": "送達時間", + "in_reply_to": "回覆來源", + "references": "引用", + "thread_id": "對話串 ID", + "size": "大小", + "mime_type": "MIME 型別", + "attachments_summary": "{count} 個檔案 · {size}", + "list_id": "清單 ID", + "list_help": "郵件清單說明", + "list_post": "傳送至清單", + "list_unsubscribe": "取消訂閱", + "iprev": "反向 DNS", + "spam_status": "垃圾郵件狀態", + "ai_verdict": "AI 判斷", + "account": "帳號", + "no_subject": "(無主題)" + }, + "headers": { + "routing": "路由", + "received": "接收時間", + "message_id": "訊息 ID", + "list_info": "郵件清單資訊" + }, + "tooltips": { + "reply": "回覆 (r)", + "reply_all": "全部回覆 (a)", + "forward": "轉寄 (f)", + "archive": "封存 (e)", + "delete": "刪除 (# 或 Del)", + "star": "加上星號 (s)", + "unstar": "移除星號 (s)", + "compose": "撰寫郵件 (c)", + "previous": "上一封郵件", + "next": "下一封郵件", + "edit_draft": "編輯草稿" + }, + "spam": { + "button_title": "回報垃圾郵件", + "not_spam_title": "標示為非垃圾郵件", + "toast_success": "已移至垃圾郵件", + "toast_batch": "{count} 封郵件已移至垃圾郵件", + "toast_undo": "撤銷", + "toast_not_spam_success": "已移至收件匣", + "toast_not_spam_batch": "{count} 封郵件已移至收件匣", + "error": "無法回報垃圾郵件", + "error_not_spam": "無法還原郵件" + }, + "unsubscribe_banner": { + "label": "訂閱郵件", + "button": "取消訂閱", + "confirm_title": "要取消訂閱此寄件者嗎?", + "confirm_button": "確認", + "cancel": "取消", + "success_http": "取消訂閱頁面已在新分頁開啟", + "success_mailto": "取消訂閱郵件已傳送", + "confirm_message_http": "取消訂閱頁面將在新分頁開啟。", + "confirm_message_mailto": "將傳送取消訂閱郵件給寄件者。", + "error": "無法取消訂閱", + "dismiss": "關閉" + }, + "calendar_invitation": { + "loading": "正在載入活動詳細資料…", + "title": "行事曆邀請", + "published_title": "已發布的活動", + "response_title": "活動回覆", + "update_title": "活動更新", + "counter_title": "改期建議", + "refresh_title": "重新整理請求", + "declined_counter_title": "改期建議已被拒絕", + "cancelled_title": "活動已取消", + "organizer": "主辦人:{name}", + "organizer_label": "主辦人:", + "attendees": "{count, plural, one {# 位參與者} other {# 位參與者}}", + "accept": "接受", + "maybe": "暫定", + "decline": "拒絕", + "add_to_calendar": "新增到行事曆", + "added": "已新增到行事曆", + "rsvp_sent": "回覆已傳送", + "parse_error": "無法讀取邀請", + "action_failed": "無法完成此行事曆動作。", + "no_calendar": "行事曆無法使用", + "published_info": "此活動已分享,僅供參考。", + "response_info": "這封郵件包含參與者的回覆。", + "response_info_organizer": "此參與者的回覆會更新您的活動。", + "update_info": "這封郵件會更新現有活動。", + "counter_info": "這封郵件建議變更活動。", + "counter_info_organizer": "此參與者建議變更您的活動。", + "refresh_info": "這封郵件要求提供最新的活動詳細資料。", + "refresh_info_organizer": "參與者要求提供最新的活動詳細資料。", + "declined_counter_info": "主辦人拒絕了改期建議。", + "authentication_failed_info": "這封邀請的郵件驗證檢查失敗。請謹慎執行行事曆動作。", + "authentication_missing_info": "這封邀請未包含已驗證的郵件身分驗證資訊。若有任何可疑之處,請向主辦人確認詳細資料。", + "sender_mismatch_info": "這封邀請是由 {sender} 傳送,但行事曆資料中列出的主辦人是 {organizer}。", + "sender_mismatch_unverified_info": "這封邀請是由 {sender} 傳送,但行事曆資料中列出的主辦人是 {organizer},而且無法驗證這封郵件。", + "organizer_role": "您是此活動的主辦人", + "your_response": "您的回覆:{status}", + "response_needed": "待回覆", + "response_accepted": "已接受", + "response_tentative": "暫定", + "response_declined": "已拒絕", + "response_delegated": "已委派", + "actor_sent_info": "由 {name} 傳送。", + "actor_response_info": "{name} 的回覆為{status}。", + "actor_counter_info": "{name} 建議變更此活動。", + "actor_refresh_info": "{name} 要求提供最新的活動詳細資料。", + "actor_declined_counter_info": "{name} 拒絕了改期建議。", + "actor_note": "備註:{comment}", + "actor_unknown": "有人", + "proposed_changes": "建議的更改", + "change_title": "標題", + "change_time": "時間", + "change_location": "地點", + "change_description": "描述", + "change_empty": "無", + "change_from_to": "{before} → {after}", + "apply_proposal": "套用建議的變更", + "proposal_applied": "已套用建議的變更。", + "review_proposal": "檢視建議", + "review_request": "檢視要求", + "view_in_calendar": "在行事曆中檢視", + "select_calendar": "選取行事曆", + "already_in_calendar": "已在您的行事曆中", + "request_info": "您已受邀參加此活動。請回覆,讓主辦人知道您是否能參加。", + "cancel_info": "主辦人已取消此活動。", + "event_updated": "更新 #{sequence}", + "event_status_tentative": "暫定", + "event_status_cancelled": "已取消", + "expand": "顯示詳細資料", + "collapse": "隱藏詳細資料" + }, + "send": "傳送", + "more": "更多", + "scheduled_banner": "排定於 {date} 傳送", + "scheduled_send_created": "郵件已排程寄送", + "sending_in": "{seconds} 秒後寄出", + "undo_send_failed": "無法復原。郵件可能已經寄出。", + "cancel_scheduled_send": "取消傳送", + "reschedule_send": "重新排程", + "cancel_and_edit": "取消並編輯", + "cancel_and_compose_again": "取消並重新撰寫", + "reschedule_prompt": "輸入新的日期與時間,例如 2026-05-04T15:30", + "scheduled_actions_only": "此檢視僅支援排程寄送動作", + "undo_send_scheduled": "郵件已排程寄送", + "undo_send": "取消傳送", + "contact_sidebar": { + "title": "聯絡人", + "close": "關閉側邊欄", + "action_email": "郵件", + "action_email_title": "傳送郵件", + "action_copy": "複製", + "action_copy_title": "複製電子郵件地址", + "action_edit_title": "編輯聯絡人", + "section_emails": "電子郵件地址", + "section_phones": "電話", + "section_organizations": "組織", + "section_addresses": "地址", + "section_notes": "備註", + "not_in_contacts": "不在您的聯絡人清單中", + "add_to_contacts": "新增至聯絡人", + "copied": "已複製!", + "copy_failed": "複製失敗", + "view_contact": "檢視聯絡人", + "view_details": "檢視詳細資料" + }, + "send_now": "立即傳送", + "verification_code": { + "copy": "複製驗證碼 {code}", + "copied": "驗證碼已複製", + "copy_failed": "無法複製驗證碼" + }, + "mv_position": "第 {index} 封,共 {total} 封", + "plugin_panel_show": "顯示外掛程式面板", + "plugin_panel_hide": "隱藏外掛程式面板" + }, + "email_composer": { + "select_from_directory": "從目錄中選取", + "minimize": "最小化", + "maximize": "全螢幕", + "restore": "還原", + "new_message": "新郵件", + "signature_edit_hint": "點兩下即可編輯簽名檔", + "reply": "回覆", + "reply_all": "全部回覆", + "forward": "轉寄", + "reply_to": "回覆", + "reply_all_to": "全部回覆", + "forward_message": "轉寄", + "from": "寄件者", + "to": "收件者", + "cc": "副本", + "bcc": "密件副本", + "subject": "主題", + "body_placeholder": "撰寫郵件內容…", + "send": "傳送", + "cancel": "取消", + "attach": "附加", + "attach_photos": "照片與影片", + "attach_files": "檔案", + "attach_from_files": "從檔案附加", + "files_picker": { + "attach": "{count, plural, =0 {附加} other {附加 # 個檔案}}", + "empty_folder": "此資料夾是空的", + "load_failed": "無法載入你的檔案" + }, + "discard": "捨棄", + "discard_draft_title": "要捨棄草稿嗎?", + "discard_draft_confirm": "您有尚未儲存的變更。要捨棄這封草稿嗎?", + "discard_draft_message": "草稿將被刪除。", + "saving": "正在儲存…", + "sending": "正在傳送…", + "add_link": "新增連結", + "link_url_prompt": "輸入 URL", + "draft_saved": "草稿已儲存", + "save_failed": "儲存失敗", + "to_placeholder": "收件者電子郵件地址", + "cc_placeholder": "副本收件者", + "bcc_placeholder": "密件副本收件者", + "subject_placeholder": "主題", + "cc_label": "副本:", + "bcc_label": "密件副本:", + "subject_label": "主題:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count} 封郵件", + "no_subject": "(無主題)", + "unknown_sender": "未知", + "quote": { + "reply_header": "在 {date},{sender} 寫道:", + "forward_header": "---------- 轉寄的郵件 ----------", + "from": "寄件者:{sender}", + "date": "日期:{date}", + "subject": "主題:{subject}", + "to": "收件者:{recipients}" + }, + "remove_sub_address": "刪除子地址", + "from_override": { + "toggle_off": "覆寫", + "toggle_on": "取消覆寫", + "toggle_tooltip": "自由編輯寄件者名稱與電子郵件地址。郵件仍會透過您的寄件身分傳送。", + "name_label": "寄件者姓名", + "name_placeholder": "姓名", + "email_label": "寄件者電子郵件地址", + "email_placeholder": "alias@example.com", + "envelope_notice": "如果您的伺服器不接受此地址作為信封寄件者,將改用 {identity},收件者可以在 Return-Path 標頭中看到它。" + }, + "save_as_template": "儲存為範本", + "attach_from_device": "從此裝置上傳", + "templates_menu": "範本", + "insert_template": "插入範本", + "options": "選項", + "option_plain_text": "純文字", + "option_plain_text_hint": "不帶格式傳送", + "option_read_receipt": "要求讀取回條", + "option_read_receipt_hint": "請收件人確認已開啟郵件", + "option_dsn": "投遞通知", + "option_dsn_hint": "伺服器投遞後收到報告", + "option_require_tls": "要求加密投遞", + "option_require_tls_hint": "僅透過 TLS 投遞,否則退回", + "option_priority": "優先順序", + "option_priority_high": "高", + "option_priority_normal": "一般", + "option_priority_low": "低", + "validation": { + "recipient_required": "請新增收件者", + "body_required": "請輸入郵件內容或新增附件", + "invalid_recipients": "未寄出 - 請檢查這些地址:{addresses}", + "attachments_uploading": "附件仍在上傳中,上傳完成後將自動傳送", + "attachment_upload_failed": "未傳送 — 附件上傳失敗。請移除該附件後再試一次。" + }, + "upload_progress": "正在上傳 {uploaded} / {total}", + "upload_cancel": "取消上傳", + "upload_failed": "無法上傳 {filename}", + "attachment_too_large": "「{name}」超過伺服器允許的大小(每個檔案 {max})", + "attachments_total_too_large": "附件將超過伺服器每封郵件 {max} 的上限", + "drop_files": "拖放要附加的檔案", + "show_less": "顯示較少", + "send_failed": "傳送郵件失敗", + "send_timeout": "伺服器停止回應。再次傳送前,請先檢查「寄件備份」信件匣,因為郵件可能已經寄出。", + "send_offline": "你似乎已離線。郵件仍保留在這裡,恢復連線後請再次寄出。", + "formatting": "格式", + "send_recipients_rejected": "未傳送:伺服器拒絕了所有收件者。", + "send_plugin_failed": "未傳送:負責傳送的外掛程式失敗或耗時過久。再次傳送前,請先檢查「寄件備份」資料夾。", + "send_some_recipients_rejected": "已傳送,但未傳送給以下收件者:伺服器拒絕了他們。", + "continue_draft": "繼續編輯草稿", + "close_draft_title": "要儲存還是捨棄草稿?", + "close_draft_message": "您有尚未儲存的變更。要將這封郵件儲存為草稿,還是捨棄?", + "smime_sign_on": "S/MIME 簽名已啟用", + "smime_sign_off": "啟用 S/MIME 簽名", + "smime_encrypt_on": "S/MIME 加密已啟用", + "smime_encrypt_off": "啟用 S/MIME 加密", + "smime_encrypt_unavailable": "S/MIME 加密無法使用 — 缺少收件者憑證", + "smime_unlock_title": "解鎖 S/MIME 金鑰", + "smime_unlock_message": "請輸入密碼片語,以解鎖您的 S/MIME 簽章金鑰。", + "smime_unlock_button": "解鎖", + "smime_passphrase_placeholder": "密碼片語", + "forgot_attachment": { + "title": "忘記新增附件了嗎?", + "message": "您的郵件提到「{keyword}」,但沒有附加檔案。仍要傳送嗎?", + "send_anyway": "仍然傳送", + "back": "繼續編輯" + }, + "empty_subject": { + "title": "沒有主題就傳送嗎?", + "message": "此郵件沒有主題。仍要傳送嗎?", + "dont_ask_again": "不再提示", + "send_anyway": "仍然傳送", + "back": "繼續編輯" + }, + "schedule_send": "排程寄送", + "schedule_send_description": "選擇伺服器要在何時送出這封郵件。", + "schedule_preset": { + "tomorrow_morning": "明天上午", + "tomorrow_afternoon": "明天下午", + "monday_morning": "星期一上午" + }, + "schedule_send_required": "請選擇日期與時間。", + "schedule_send_invalid": "請輸入有效的日期與時間。", + "schedule_send_future": "請選擇未來的日期與時間。", + "schedule_send_too_late": "此時間超出伺服器允許的範圍。", + "schedule_send_unsupported": "此帳號不支援排程寄送。", + "schedule_send_cleanup_warning": "已建立排程寄送,但清理草稿失敗。", + "send_delay_unsupported": "此帳號不支援傳送延遲。", + "send_delay_unsupported_confirm": "此帳號不支援傳送延遲。是否立即傳送?", + "recipient_edit_email": "編輯電子郵件地址", + "recipient_edit_name": "編輯顯示名稱", + "recipient_email_placeholder": "電子郵件地址", + "recipient_name_placeholder": "顯示名稱", + "invalid_recipient": "不是有效的電子郵件地址", + "autocomplete_search_server": "在伺服器上搜尋", + "autocomplete_searching": "正在搜尋…", + "mention_recipients": "收件者", + "mention_card_not_recipient": "不在收件者或副本中,因此不會收到這封郵件。", + "mention_card_add": "加入{field}", + "mention_card_remove": "移除提及", + "toolbar": { + "bold": "粗體", + "italic": "斜體", + "underline": "底線", + "strikethrough": "刪除線", + "text_color": "文字顏色", + "remove_color": "移除顏色", + "heading_1": "標題 1", + "heading_2": "標題 2", + "bullet_list": "項目符號清單", + "ordered_list": "編號清單", + "quote": "引用", + "code_block": "程式碼區塊", + "align_left": "左對齊", + "align_center": "置中對齊", + "align_right": "右對齊", + "text_direction": "文字方向 (RTL/LTR)", + "link": "連結", + "table": "表格", + "clear_formatting": "清除格式", + "undo": "撤銷", + "redo": "重做", + "add_row_above": "在上方新增一列", + "add_row_below": "在下方新增一列", + "add_column_before": "在前方新增一欄", + "add_column_after": "在後方新增一欄", + "delete_row": "刪除列", + "delete_column": "刪除欄", + "toggle_header_row": "切換標題列", + "delete_table": "刪除表格", + "pick_size": "選取大小", + "font_size": "字型大小", + "font_size_default": "預設", + "background_color": "背景顏色", + "remove_background_color": "移除背景顏色" + }, + "send_filing_warning": "郵件已傳送,但寄送後清理失敗,可能仍保留舊草稿。", + "pop_out": "在獨立視窗中開啟回覆", + "send_guard_failed": "未傳送 - 傳送前檢查郵件的外掛程式發生錯誤或沒有回應。" + }, + "confirm_dialog": { + "confirm": "確認", + "cancel": "取消" + }, + "common": { + "loading": "正在載入…", + "error": "錯誤", + "success": "成功", + "cancel": "取消", + "save": "儲存", + "delete": "刪除", + "edit": "編輯", + "close": "關閉", + "search": "搜尋", + "refresh": "重新整理", + "settings": "設定", + "help": "說明", + "logout": "登出", + "yes": "是", + "no": "否", + "unknown": "未知", + "app_title": "網頁郵件", + "reconnecting": "連線中斷,正在嘗試重新連線…", + "rate_limited_title": "伺服器驗證目前受到速率限制。", + "rate_limited_detail": "Bulwark 已暫停背景請求,以免帳號遭鎖定。將在 {seconds} 秒後重試。", + "rate_limited_action_title": "已暫停請求,以免帳號遭鎖定。", + "rate_limited_action_detail": "Bulwark 正在等待伺服器的冷卻時間結束,再傳送更多需要驗證的請求。請在 {seconds} 秒後重試。", + "pull_to_refresh": "下拉重新整理", + "release_to_refresh": "鬆開重新整理", + "refreshing": "正在重新整理…" + }, + "notifications": { + "email_sent": "郵件傳送成功", + "email_deleted": "郵件已刪除", + "email_archived": "郵件已封存", + "emails_deleted": "{count, plural, other {已刪除 # 封郵件}}", + "emails_archived": "{count, plural, other {已封存 # 封郵件}}", + "error_archiving": "封存郵件失敗", + "error_updating": "更新郵件失敗", + "email_starred": "郵件已加上星號", + "email_unstarred": "郵件已移除星號", + "email_marked_read": "郵件已標示為已讀", + "email_marked_unread": "郵件已標示為未讀", + "copied_to_clipboard": "已複製到剪貼簿", + "source_copied": "原始碼已複製到剪貼簿", + "error_sending": "傳送郵件失敗", + "error_deleting": "刪除郵件失敗", + "error_loading": "無法載入郵件", + "new_email": "新郵件", + "new_email_from": "來自 {sender}", + "click_to_view": "點選檢視", + "email_moved": "郵件已移動", + "emails_moved": "{count} 封郵件已移動", + "moved_to_mailbox": "已移至 {mailbox}", + "move_failed": "移動郵件失敗", + "removed_from_folder": "已從 {mailbox} 移除", + "remove_from_folder_failed": "無法從此資料夾移除", + "emails_copied": "{count} 封郵件已複製", + "copy_failed": "複製失敗", + "move_error": "無法將郵件移至選取的信件匣", + "email_tagged": "郵件已加標籤", + "emails_tagged": "{count} 封郵件已加標籤", + "tag_failed": "加上標籤失敗", + "identity_created": "已成功建立寄件身分", + "identity_updated": "已成功更新寄件身分", + "identity_deleted": "已刪除寄件身分", + "identity_set_primary": "已更新主要寄件身分", + "identity_create_failed": "無法建立寄件身分:{error}", + "identity_update_failed": "無法更新寄件身分:{error}", + "identity_delete_failed": "無法刪除寄件身分:{error}", + "identity_unauthorized": "您沒有使用此電子郵件地址寄信的權限", + "identity_not_found": "找不到寄件身分", + "vacation_saved": "外出設定已儲存", + "vacation_save_failed": "無法儲存外出設定", + "filters_saved": "篩選規則已成功儲存", + "filters_save_failed": "無法儲存篩選規則", + "filters_deleted": "篩選規則已刪除", + "templates_exported": "範本匯出成功", + "templates_imported": "已匯入{count, plural, one {# 個範本} other {# 個範本}}", + "templates_import_errors": "部分範本無法匯入", + "templates_import_empty": "檔案中找不到範本", + "export_email_error": "無法匯出郵件", + "import_email_success": "郵件已成功匯入", + "import_email_error": "無法匯入郵件", + "rule_created": "已建立規則", + "rule_merged": "已加入規則「{name}」", + "rule_already_covered": "規則「{name}」已涵蓋此項", + "rule_apply_existing": "{count, plural, other {套用到 # 封既有郵件}}", + "rule_applied": "{count, plural, =0 {沒有需要變更的郵件} other {已將規則套用到 # 封郵件}}", + "rule_apply_failed": "無法將規則套用到既有郵件", + "rule_edit": "編輯規則", + "rule_undo": "復原", + "rule_undone": "已復原規則變更", + "rule_undo_failed": "無法復原規則變更", + "rule_undo_conflict": "篩選規則在那之後已變更。請在篩選規則設定中復原該變更。", + "rule_folder_failed": "無法建立資料夾", + "vacation_filters_save_failed": "外出設定已儲存,但轉寄和回覆對象設定未能儲存" + }, + "date": { + "today": "今天", + "yesterday": "昨天", + "this_week": "本週", + "last_week": "上週", + "this_month": "本月", + "older": "更早", + "just_now": "剛剛", + "minutes_ago": "{count} 分鐘前", + "minutes_ago_plural": "{count} 分鐘前", + "hours_ago": "{count} 小時前", + "hours_ago_plural": "{count} 小時前", + "days_ago": "{count} 天前", + "days_ago_plural": "{count} 天前" + }, + "language": { + "title": "語言", + "english": "英文", + "french": "法文", + "japanese": "日文", + "spanish": "西班牙文", + "italian": "義大利文", + "german": "德文", + "dutch": "荷蘭文", + "portuguese": "葡萄牙文", + "russian": "俄文", + "select_language": "選擇語言", + "switch_to_english": "切換成英文", + "switch_to_french": "切換成法文", + "switch_to_japanese": "切換成日文", + "switch_to_spanish": "切換成西班牙文", + "switch_to_italian": "切換成義大利文", + "switch_to_german": "切換成德文", + "switch_to_dutch": "切換成荷蘭文", + "switch_to_portuguese": "切換成葡萄牙文", + "switch_to_russian": "切換成俄文", + "switching": "正在切換語言…", + "switch": "切換語言", + "current": "目前語言", + "polish": "波蘭文", + "switch_to_polish": "切換成波蘭文", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "簡體中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "切換成波斯文", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "切換成蒙古文" + }, + "settings": { + "title": "設定", + "back_to_mail": "回到郵件", + "save_success": "設定已成功儲存", + "import_success": "已成功匯入設定", + "import_error": "無法匯入設定", + "reset_confirm": "您確定要將所有設定重設為預設值嗎?", + "unsaved_changes": "您有尚未儲存的變更", + "discard_changes": "要捨棄尚未儲存的變更嗎?", + "discard": "捨棄", + "keep_editing": "繼續編輯", + "search_placeholder": "搜尋設定", + "search_clear": "清除搜尋", + "search_no_results": "找不到相符的設定", + "tabs": { + "appearance": "外觀", + "language": "語言、地區與時間", + "email": "郵件行為", + "composer": "郵件撰寫", + "privacy": "隱私權與安全", + "account": "帳號", + "identities": "寄件身分", + "vacation": "外出", + "advanced": "進階", + "calendar": "行事曆", + "filters": "篩選規則", + "templates": "範本", + "folders": "信件匣", + "keywords": "標籤", + "security": "安全", + "files": "檔案", + "contacts": "聯絡人", + "encryption": "加密", + "protocol_handlers": "預設應用程式", + "sidebar_apps": "側邊欄應用程式", + "notifications": "通知", + "layout": "版面配置", + "themes": "主題", + "reading": "閱讀", + "composing": "撰寫", + "downloads": "下載", + "content_senders": "內容與寄件者", + "about_data": "關於與資料", + "debug": "除錯" + }, + "themes": { + "title": "主題", + "description": "從管理員部署的主題和內建預設集中選擇。", + "locked": "主題選擇已被管理員鎖定。", + "forced": "主題「{name}」由管理員強制指定,無法變更", + "admin_theme": "管理員主題", + "activated": "主題已啟用", + "default_restored": "已還原預設主題", + "default_name": "預設", + "default_badge": "預設", + "admin_enforced": "管理員強制" + }, + "tab_groups": { + "general": "一般", + "account": "帳號與寄件身分", + "organization": "郵件整理", + "apps": "應用程式", + "system": "系統", + "appearance": "外觀", + "mail": "郵件", + "privacy": "隱私權與安全", + "advanced": "進階" + }, + "appearance": { + "message_list_order": { + "label": "郵件清單排序", + "description": "清單中的郵件排序方式。排序由伺服器執行,因此會套用至整個信件匣,而不只是已載入的郵件。", + "preset": { + "chronological": "依時間順序(最新優先)", + "unread_first": "未讀優先", + "starred_first": "已加星號優先", + "tagged_first": "有標籤優先", + "custom": "自訂" + }, + "tag_label": "標籤", + "advanced_show": "進階:編輯排序層級", + "advanced_hide": "隱藏進階設定", + "criterion_label": "第 {n} 級條件", + "direction_label": "第 {n} 級方向", + "criteria": { + "unread": "未讀", + "starred": "已加星號", + "tag": "標籤", + "receivedAt": "接收日期", + "sentAt": "傳送日期", + "from": "寄件者", + "subject": "主題", + "size": "大小" + }, + "direction": { + "unread_first": "未讀在前", + "read_first": "已讀在前", + "starred_first": "已加星號優先", + "unstarred_first": "未加星號優先", + "tagged_first": "有標籤在前", + "untagged_first": "無標籤在前", + "newest_first": "最新在前", + "oldest_first": "最早在前", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "最大在前", + "smallest_first": "最小在前" + }, + "add_level": "新增層級", + "remove_level": "移除層級", + "move_up": "上移", + "move_down": "下移", + "tiebreak_note": "最多可設定三個層級;後面的層級用來判定同順位的項目。「最新優先」一律最後套用。", + "scope": { + "label": "套用至所有信件匣", + "description": "關閉時,排序只套用至收件匣;其他信件匣仍依時間順序排列。" + }, + "unsupported_note": "您的郵件伺服器不支援依已讀狀態、星號或標籤排序。系統會忽略這些層級,清單仍依時間順序排列。" + }, + "title": "外觀", + "description": "自訂網頁郵件的外觀與使用感受", + "theme": { + "label": "主題", + "description": "選擇您喜歡的配色方案", + "light": "淺色", + "dark": "深色", + "system": "系統" + }, + "language": { + "label": "語言", + "description": "選擇您偏好的語言" + }, + "font_size": { + "label": "字型大小", + "description": "調整文字大小,提升易讀性", + "small": "小", + "medium": "中", + "large": "大" + }, + "list_density": { + "label": "介面密度", + "description": "調整介面元素之間的間距", + "extra_compact": "超緊湊", + "compact": "緊湊", + "regular": "預設", + "comfortable": "寬鬆" + }, + "interface_layout": { + "label": "介面版面配置", + "description": "搜尋、導覽與工具列的位置", + "default": "預設", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "啟用動畫", + "description": "顯示平順的轉場與效果" + }, + "toolbar_position": { + "label": "工具列位置", + "description": "選擇顯示郵件動作按鈕的位置(回覆、封存、刪除等)", + "top": "頂端", + "below_subject": "主題下方" + }, + "toolbar_labels": { + "label": "顯示工具列標籤", + "description": "在工具列圖示旁顯示文字標籤。熟悉圖示後可停用,以節省空間。" + }, + "hide_account_switcher": { + "label": "隱藏側邊欄帳號切換器", + "description": "隱藏信件匣側邊欄頂端的帳號選取器。您仍可從底部導覽列切換帳號。" + }, + "show_rail_account_list": { + "label": "在導覽列顯示帳號頭像", + "description": "在導覽列底部顯示各帳號的圓形頭像,方便快速切換,並在下方顯示登出按鈕。" + }, + "unified_mailbox": { + "label": "整合信箱", + "description": "顯示使用中帳號及其共用信件匣的整合信件匣(收件匣、寄件備份等)。", + "cross_account": { + "label": "跨所有帳號", + "description": "將整合信箱合併到所有已連線的帳號,而不只限於使用中的帳號。" + }, + "include_group": { + "label": "包含群組收件匣", + "description": "也將共用/群組收件匣合併到整合檢視中。" + } + }, + "all_mail": { + "folders_label": "整合清單中的信件匣", + "folders_description": "選擇要將此帳號的哪些信件匣合併到「所有郵件/未讀/已加星號」清單。", + "account_hint": "套用至 {account}。", + "no_folders": "沒有可用的信件匣。" + }, + "colorful_sidebar_icons": { + "label": "彩色側邊欄圖示", + "description": "依類型為信件匣與標籤圖示上色(藍色收件匣、紅色垃圾郵件、綠色寄件備份等)。停用後側邊欄會以單色顯示。" + }, + "tint_list_rows": { + "label": "依標籤顏色為清單列上色", + "description": "以第一個標籤的顏色為每封郵件列加上底色。停用後郵件列保持無底色,但標籤圓點與標籤仍會顯示該顏色。" + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "顯示郵件總數", + "description": "在信件匣與標籤旁顯示郵件總數及未讀數量。停用後只顯示未讀數量。" + }, + "favicon_unread_badge": { + "label": "在分頁圖示顯示未讀數量", + "description": "在瀏覽器分頁圖示顯示收件匣未讀數量徽章,即使分頁不在前景也能看到新郵件。停用後不顯示徽章。" + }, + "show_shared_mail_accounts": { + "label": "顯示共用郵件帳號", + "description": "在側邊欄的「共用」下顯示與你共用的信箱資料夾。關閉可全部隱藏,也可以在下方逐一隱藏帳號。它們的行事曆和聯絡人仍會顯示。" + }, + "pro_interface": { + "label": "Pro 介面(實驗性)", + "description": "適合進階使用者的桌面版配置,支援多分頁郵件瀏覽與跨帳號工作流程。標準介面不受影響,您可隨時切換回來。", + "open_label": "開啟 Pro 介面", + "back_to_standard": "返回標準介面" + }, + "cross_unread": { + "label": "未讀", + "description": "在整合信箱顯示「未讀」項目,列出所選信件匣中的未讀郵件。" + }, + "cross_starred": { + "label": "已加星號", + "description": "在整合信箱顯示「已加星號」項目,列出所選信件匣中已加星號的郵件。" + }, + "cross_all": { + "label": "所有郵件", + "description": "在整合信箱顯示「所有郵件」項目,列出所選信件匣中的所有郵件。" + } + }, + "keywords": { + "title": "電子郵件標籤", + "description": "定義用來整理郵件的標籤。這些標籤會以 JMAP 關鍵字儲存在伺服器上。", + "add_keyword": "新增標籤", + "label_field": "顯示名稱", + "label_placeholder": "例如工作、個人、緊急", + "id_field": "標籤 ID", + "id_placeholder": "例如工作、個人", + "color_field": "顏色", + "id_exists": "此標籤 ID 已存在", + "edit": "編輯標籤", + "delete": "刪除標籤", + "save": "儲存", + "add": "新增", + "cancel": "取消", + "migrating": "正在更新現有郵件的標籤…", + "migration_error": "更新現有郵件的標籤失敗", + "filters_migration_error": "部分篩選規則仍在使用舊標籤,請手動更新", + "nesting": { + "label": "巢狀標籤", + "description": "將標籤巢狀放在其他標籤下,並在側邊欄以樹狀結構顯示。" + }, + "parent_field": "上層標籤", + "no_parent": "無上層標籤", + "too_long": "此標籤路徑過長(最多 {max} 個字元)", + "has_children_locked": "此標籤下有其他巢狀標籤,因此無法變更名稱與上層標籤。請先移動或移除這些子標籤。", + "has_children_delete": "請先移除此標籤下的巢狀標籤", + "visibility_field": "側邊欄顯示", + "visibility": { + "show": "顯示", + "unread": "有未讀時顯示", + "hide": "隱藏" + }, + "unrecognized": { + "title": "從伺服器復原標籤", + "description": "標籤名稱與顏色儲存在此瀏覽器中,標籤本身則以 JMAP 關鍵字附加在郵件上。掃描郵件以尋找這裡尚未定義的關鍵字,再將它們加回來。", + "scan": "掃描標籤", + "rescan": "重新掃描", + "scanning": "正在掃描… {total} 封郵件中的 {scanned} 封", + "none": "郵件上的所有標籤均已在此定義。", + "messages": "{count, plural, one {# 封郵件} other {# 封郵件}}", + "add_all": "全部新增({count})", + "partial": "僅掃描 {total} 封郵件中最新的 {scanned} 封。只出現在較舊郵件的標籤可能遺漏。", + "error": "無法掃描郵件中的標籤。" + } + }, + "notifications": { + "test_sound": "測試通知聲音", + "sounds": { + "default": "預設(蜂鳴聲)", + "cheerful": "快樂", + "involved": "沉浸", + "swift": "輕快手勢", + "relax": "放鬆" + }, + "push": { + "title": "背景通知", + "description": "網站關閉時仍可接收新郵件的系統通知。通知會透過 Bulwark 推送中繼傳送;中繼不會看到郵件內容。", + "relay_label": "推送中繼", + "relay_desc": "用來傳送通知的伺服器。請從管理員提供的中繼中選擇。", + "relay_locked": "由管理員設定", + "relay_locked_desc": "推送中繼已由管理員設定,無法變更。", + "status_active": "已在此裝置上啟用", + "status_inactive": "在此裝置上未啟用", + "status_unsupported": "此瀏覽器不支援 Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "status_busy": "工作中…", + "enable": "啟用", + "reenable": "重新註冊", + "disable": "停用", + "confirm_disable_title": "要停用背景通知嗎?", + "confirm_disable_message": "當網站關閉時,此裝置將停止接收提醒。", + "dismiss_aria": "關閉通知提示", + "ios_hint": "在 iOS 上,請先將網站安裝到主畫面;Safari 只會將 Web Push 傳送至已安裝的 PWA。", + "devices_title": "已註冊的裝置", + "devices_desc": "這裡列出為此帳號註冊背景通知的所有裝置。撤銷其中一部裝置的註冊後,該裝置會立即停止接收通知。", + "devices_loading": "正在載入已註冊的裝置…", + "devices_empty": "沒有裝置註冊接收背景通知。", + "devices_error": "無法載入已註冊的裝置。", + "devices_refresh": "重新整理", + "device_this": "此裝置", + "device_other": "其他裝置", + "device_status_active": "正在投遞通知", + "device_status_inactive": "從未投遞過通知", + "device_status_unknown": "此中繼沒有這部裝置的資訊", + "device_expires": "於 {date} 到期", + "revoke": "撤銷", + "confirm_revoke_title": "要撤銷此裝置的註冊嗎?", + "confirm_revoke_message": "此裝置會立即停止接收背景通知。若在該裝置上再次啟用推送,將重新註冊。", + "confirm_revoke_message_this": "此裝置會立即停止接收背景通知。" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "通知聲音", + "description": "選擇通知時播放的聲音", + "choose": "聲音", + "choose_desc": "選擇通知音並點擊揚聲器圖示進行預覽" + }, + "email": { + "title": "郵件通知", + "description": "設定傳入郵件的通知", + "enabled": "郵件通知", + "enabled_desc": "新郵件到達時顯示通知", + "sound": "通知聲音", + "sound_desc": "新郵件到達時播放音訊提醒", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "行事曆通知", + "description": "設定行事曆活動的通知", + "enabled": "活動通知", + "enabled_desc": "顯示即將開始的行事曆活動提醒", + "sound": "通知聲音", + "sound_desc": "播放行事曆提醒的音訊提醒", + "invitation_parsing": "解析郵件邀請", + "invitation_parsing_desc": "偵測郵件附件中的行事曆邀請,並顯示行事曆動作" + } + }, + "language_region": { + "title": "語言、地區與時間", + "description": "語言、日期格式、時間格式以及其他區域設定", + "language": { + "label": "語言", + "description": "選擇您偏好的語言", + "english": "英文", + "french": "法文" + }, + "date_format": { + "label": "日期格式", + "description": "電子郵件清單中日期的顯示方式", + "smart": "智慧", + "relative": "相對時間", + "full": "一律顯示完整日期", + "preview_today": "今天:", + "preview_this_week": "本週:", + "preview_older": "更早:" + }, + "date_locale": { + "label": "日期格式地區", + "description": "數字日期的排列順序(日、月、年)", + "auto": "自動", + "iso": "ISO 8601", + "dmy": "日/月/年", + "mdy": "月/日/年" + }, + "time_format": { + "label": "時間格式", + "description": "選擇 12 小時制或 24 小時制", + "12h": "12 小時制", + "24h": "24 小時制" + }, + "first_day": { + "label": "一週的第一天", + "description": "從週日或週一開始一週", + "sunday": "週日", + "monday": "週一" + }, + "time_zone": { + "label": "時區", + "description": "使用此時區顯示郵件與行事曆時間,而非瀏覽器回報的時區", + "auto": "自動({zone})", + "preview_now": "現在:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "切換資料夾時清除搜尋", + "description": "點擊資料夾(收件匣、寄件備份等)會清除目前的搜尋並瀏覽該資料夾。關閉時,搜尋會保留並在切換到的資料夾中重新執行。" + }, + "request_read_receipt": { + "label": "預設請求已讀回執", + "description": "撰寫新郵件時預先啟用已讀回執請求。" + }, + "read_receipt_response": { + "label": "回應已讀回執請求", + "description": "當收到的郵件請求已讀回執時的處理方式。", + "ask": "每次詢問", + "always": "一律傳送", + "never": "永不傳送" + }, + "title": "郵件行為", + "description": "設定郵件的處理方式", + "mark_read": { + "label": "標示為已讀", + "description": "開啟郵件後,何時將其標示為已讀", + "instant": "立即", + "delay_3s": "3 秒後", + "delay_5s": "5 秒後", + "never": "從不" + }, + "delete_action": { + "label": "刪除動作", + "description": "刪除郵件後的行為", + "trash": "移至垃圾桶", + "trash_and_read": "移至垃圾桶並標示為已讀", + "permanent": "永久刪除", + "warning": "郵件將永久刪除且無法復原。此動作無法復原。" + }, + "message_spacing": { + "label": "郵件內距", + "description": "閱讀器中郵件內文周圍的留白", + "auto": "自動(建議)", + "always": "一律加入留白", + "edge": "無邊距" + }, + "plain_text_font": { + "label": "純文字字型", + "description": "用於純文字郵件的字型", + "mono": "等寬", + "sans": "一般(與應用程式相同)" + }, + "archive_mode": { + "label": "封存方式", + "description": "封存時如何組織郵件", + "single": "單一信件匣", + "year": "每年一個信件匣", + "month": "每月一個信件匣", + "reorganize": "重新整理現有封存", + "reorganize_success": "{count, plural, =0 {沒有需要整理的郵件} =1 {已整理 1 封郵件} other {已整理 # 封郵件}}", + "reorganize_error": "無法重新整理封存" + }, + "permanently_delete_junk": { + "label": "永久刪除垃圾郵件", + "description": "直接永久刪除垃圾郵件信件匣中的郵件,而不移至垃圾桶" + }, + "confirm_bulk_delete": { + "label": "刪除多封郵件前確認", + "description": "將多封選取的郵件移至垃圾桶前詢問。永久刪除一律會詢問。" + }, + "mail_layout": { + "label": "郵件版面配置", + "description": "選擇經典分割閱讀窗格、Gmail 風格的專注閱讀流程,或 Zimbra 風格的底部閱讀窗格。", + "split": "分割窗格", + "split_description": "郵件清單與閱讀窗格並排顯示。", + "focus": "專注清單", + "focus_description": "每封郵件顯示一列,開啟郵件時以全寬顯示,同時保留信件匣側邊欄。", + "horizontal": "底部閱讀窗格", + "horizontal_description": "在頂端顯示郵件清單,並在下方閱讀窗格開啟選取的郵件。" + }, + "list_columns": { + "label": "以欄位顯示清單", + "description": "將單行列表顯示為表格,標題下方為寄件者、主旨、日期和大小欄位。" + }, + "show_preview": { + "label": "顯示預覽文字", + "description": "在清單中顯示郵件預覽", + "focus_description": "在專注模式的單列郵件清單中顯示行內預覽文字" + }, + "show_verification_codes": { + "label": "顯示驗證碼", + "description": "提供登入與確認郵件中的驗證碼,一鍵複製(在清單中顯示一天,在郵件中始終顯示)" + }, + "disable_threading": { + "label": "停用對話串群組", + "description": "將郵件個別顯示,而不是依對話串分組" + }, + "plain_text_mode": { + "label": "僅純文字", + "description": "停用富文字編輯器,並將所有郵件(包括回覆和轉寄)以純文字傳送" + }, + "auto_select_reply_identity": { + "label": "使用收件地址回覆", + "description": "回覆時,使用原始郵件的收件地址寄送。系統會先比對寄件身分;若是網域 catch-all 收件,則透過主要寄件身分傳送,但將 From 標頭改寫為別名地址。" + }, + "reply_identity_match": { + "label": "接收地址比對", + "description": "哪些接收地址算作您的地址。「僅精確地址」只從已設定的身分中選擇;「相同網域」還會將身分網域下的其他任何地址視為 catch-all 別名,並將寄件者標頭改寫為該地址。如果這些地址是通訊群組清單,請選擇精確地址。", + "exact": "僅精確地址", + "domain": "我的網域下的任意地址" + }, + "signature_position": { + "label": "簽名檔位置", + "description": "在回覆與轉寄中插入簽名檔的位置。放在引用文字上方可自然作為回覆結尾;放在下方則能保持原始郵件連續。", + "above_quote": "引用文字之前", + "below_quote": "引用文字之後" + }, + "signature_separator": { + "label": "簽名檔分隔線", + "description": "在簽名檔前加入標準的「-- 」分隔線(RFC 3676)。若希望內文直接接續簽名檔,請停用此選項。" + }, + "sub_address_delimiter": { + "label": "子地址分隔符", + "description": "用於分隔使用者名稱和子地址標籤的字元。請選擇與您的郵件伺服器一致的分隔符(例如 user{delimiter}tag@domain.com)。", + "option": "{delimiter}(user{delimiter}tag@domain.com)", + "custom": "自訂…", + "custom_input_label": "自訂分隔字元" + }, + "attachment_click_action": { + "label": "附件點擊動作", + "description": "選擇點選附件時是預覽還是直接下載", + "preview": "儘可能預覽", + "download": "立即下載" + }, + "attachment_position": { + "label": "附件位置", + "description": "選擇在郵件標頭的哪個位置顯示附件", + "beside-sender": "寄件者旁", + "below-header": "標頭下方" + }, + "emails_per_page": { + "10": "10 封郵件", + "25": "25 封郵件", + "50": "50 封郵件", + "100": "100 封郵件", + "label": "每頁郵件數", + "description": "一次載入的郵件數量" + }, + "always_light_mode": { + "label": "一律以淺色模式顯示郵件", + "description": "即使應用程式使用深色模式,也以淺色模式呈現郵件內容,避免深色模式轉換問題" + }, + "external_content": { + "label": "外部內容", + "description": "選擇如何處理圖片與外部內容", + "ask": "每次詢問", + "block": "一律封鎖", + "allow": "一律允許" + }, + "trusted_senders": { + "label": "受信任寄件者", + "description": "管理可自動載入圖片的寄件者", + "count_zero": "無", + "count_one": "1 位寄件者", + "count_other": "{count} 位寄件者", + "modal_title": "受信任寄件者", + "empty_title": "尚無受信任寄件者", + "empty_description": "檢視包含遭封鎖圖片的郵件時,點擊「一律信任此寄件者」即可新增到這裡。", + "add_manually": "手動新增", + "add_button": "新增", + "add_placeholder": "輸入電子郵件地址", + "search_placeholder": "搜尋寄件者…", + "no_results": "找不到相符的寄件者", + "remove": "移除", + "close": "關閉", + "invalid_email": "請輸入有效的電子郵件地址", + "already_added": "該寄件者已被信任", + "save_error": "儲存失敗 — 請檢視聯絡人除錯記錄以瞭解詳細資料", + "use_address_book_label": "與通訊錄同步", + "use_address_book_description": "將受信任寄件者儲存在專用的「受信任寄件者」通訊錄,以便在所有裝置間同步" + }, + "hover_actions": { + "label": "游標暫留快速動作", + "description": "選擇游標移至清單中的郵件上方時要顯示的快速動作", + "delete": "刪除", + "star": "加上/移除星號", + "mark_read": "標示為已讀/未讀", + "archive": "封存", + "tag": "標籤", + "spam": "標示為垃圾郵件", + "not_spam": "非垃圾郵件", + "none_selected": "未選取任何動作", + "mode_label": "顯示模式", + "mode_inline": "內嵌", + "mode_floating": "浮動", + "corner_label": "浮動位置", + "corner_top-left": "左上", + "corner_top-right": "右上", + "corner_bottom-left": "左下", + "corner_bottom-right": "右下" + }, + "default_mail_program": { + "label": "預設郵件應用程式", + "description": "將 {appName} 註冊為 mailto: 連結的預設郵件應用程式", + "button": "設為預設", + "success": "瀏覽器已提示設定為預設", + "error": "您的瀏覽器不支援此功能" + }, + "attachment_reminder": { + "label": "附件提醒", + "description": "郵件提到附件但沒有附加檔案時,在傳送前發出警告", + "keywords_label": "觸發關鍵字", + "keywords_description": "郵件中出現時會觸發提醒的詞彙或片語", + "add_placeholder": "新增關鍵字…", + "add": "新增", + "remove": "移除" + }, + "empty_subject_warning": { + "label": "空主題提醒", + "description": "傳送沒有主題的郵件前請求確認。" + }, + "recipient_mentions": { + "label": "使用 @ 提及收件者", + "description": "在內文輸入 @ 會列出收件者,並插入所選收件者的名字。按 Enter 或 Tab 選擇醒目標示的名字。" + }, + "mention_adds_recipient": { + "label": "在 @ 提及中顯示聯絡人", + "description": "輸入 @ 時也會建議你的聯絡人和目錄中的人員。選擇尚未在收件者中的人會將其加入郵件。", + "to": "加入收件者", + "cc": "加入副本", + "off": "僅顯示收件者" + }, + "hide_inline_image_attachments": { + "label": "在附件清單中隱藏內嵌圖片", + "description": "嵌入到郵件內文中的圖片不會作為單獨的附件顯示" + }, + "attachment_image_previews": { + "label": "在附件中顯示圖片預覽", + "description": "將圖片附件顯示為縮圖卡片,而不是通用檔案圖示" + }, + "send_delay": { + "label": "取消傳送/延遲傳送", + "description": "在伺服器端短暫延遲一般傳送。", + "off": "關閉", + "seconds": "{seconds} 秒", + "unsupported": "目前帳號未宣告支援延遲傳送。該設定仍會為其他帳號儲存。" + }, + "return_to_list_after_action": { + "label": "刪除或標示為未讀後回到清單", + "description": "刪除開啟的郵件或標示為未讀後,回到郵件清單,而不是開啟下一封郵件。" + }, + "swipe_right_action": { + "label": "向右滑動動作(行動裝置)", + "description": "在觸控裝置上,於清單中向右滑動郵件時執行的動作。" + }, + "swipe_left_action": { + "label": "向左滑動動作(行動裝置)", + "description": "在觸控裝置上,於清單中向左滑動郵件時執行的動作。" + }, + "swipe_actions": { + "none": "無", + "archive": "封存", + "delete": "刪除", + "mark_read": "切換已讀/未讀", + "star": "加上/移除星號", + "spam": "標示為垃圾郵件" + }, + "rtl_editing": { + "label": "支援從右至左編輯", + "description": "在編輯器工具列中新增方向按鈕,可將段落設定為從左到右或從右到左" + } + }, + "composer": { + "title": "郵件撰寫", + "description": "設定郵件撰寫偏好", + "autosave": { + "label": "自動儲存間隔", + "description": "自動儲存草稿的頻率", + "30s": "每 30 秒", + "1m": "每分鐘", + "2m": "每 2 分鐘", + "5m": "每 5 分鐘" + }, + "send_confirmation": { + "label": "傳送前確認", + "description": "傳送郵件前要求確認" + }, + "default_reply": { + "label": "預設回覆模式", + "description": "點擊回覆時的預設動作", + "reply": "回覆", + "reply_all": "全部回覆" + } + }, + "privacy": { + "title": "隱私權與安全", + "description": "管理您的隱私權與安全設定", + "external_images": { + "label": "封鎖外部圖片", + "description": "防止透過外部圖片進行追蹤" + }, + "session_timeout": { + "label": "工作階段逾時", + "description": "一段時間未操作後自動登出", + "never": "永不", + "30m": "30 分鐘", + "1h": "1 小時", + "4h": "4 小時" + }, + "clear_cache": { + "label": "清除快取", + "description": "移除快取資料與暫存檔案", + "button": "清除快取", + "confirm": "您確定要清除快取嗎?", + "success": "已成功清除快取" + } + }, + "account": { + "title": "帳號", + "description": "檢視您的帳號資訊", + "name_label": "顯示名稱", + "username_label": "使用者名稱", + "account_type_label": "帳號類型", + "auth_method_label": "驗證方式", + "auth_method_oauth": "單一登入(OAuth/OIDC)", + "auth_method_basic": "密碼", + "auth_method_token": "存取權杖", + "demo_account": "示範帳號", + "email": { + "label": "電子郵件地址", + "value": "{email}" + }, + "server": { + "label": "JMAP 伺服器", + "value": "{server}" + }, + "storage": { + "label": "儲存空間使用量", + "used": "已使用 {used} / {total}", + "percentage": "已使用 {percent}%" + }, + "last_sync": { + "label": "上次同步", + "value": "{time}" + }, + "accounts": { + "title": "已登入帳號", + "description": "拖曳以重新排序帳號在帳號下拉選單中的顯示順序", + "active": "使用中的帳號", + "default_badge": "預設帳號", + "set_default": "設為預設", + "switch_to": "切換到此帳號", + "move_up": "上移", + "move_down": "下移", + "drag_handle": "拖曳以重新排序", + "add": "新增帳號" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "與我共用", + "description": "您可以管理的群組與共用帳號。選取一個帳號,即可編輯其篩選規則、休假自動回覆、行事曆與聯絡人。", + "shared_label": "共用帳號" + } + }, + "scoped": { + "back": "回到我的帳號", + "managing": "正在管理:{name}" + }, + "security": { + "title": "帳號安全", + "description": "管理您的密碼、雙因素驗證與安全設定", + "detecting": "正在偵測伺服器功能…", + "not_available": "此郵件伺服器無法管理帳號安全性。可能已停用必要權限。如需詳細資料,請參閱說明文件。", + "password": { + "title": "變更密碼", + "current": "目前密碼", + "new": "新密碼", + "confirm": "確認新密碼", + "submit": "變更密碼", + "success": "已成功變更密碼", + "error_title": "變更密碼失敗", + "error_mismatch": "兩次輸入的新密碼不相符", + "error_min_length": "密碼必須至少為 8 個字元", + "error_generic": "無法變更密碼" + }, + "display_name": { + "label": "顯示名稱", + "description": "顯示在伺服器上的名稱", + "placeholder": "輸入您的顯示名稱", + "save": "儲存", + "success": "顯示名稱已更新", + "error": "無法更新顯示名稱" + }, + "totp": { + "section_title": "雙因素驗證", + "label": "TOTP 驗證", + "description": "透過基於時間的一次性密碼,增加一層安全保護", + "active": "已啟用", + "inactive": "未啟用", + "enabled": "已啟用雙因素驗證", + "disabled": "已停用雙因素驗證", + "enable_error": "無法啟用 2FA", + "disable_error": "無法停用 2FA", + "setup_instructions": "請將此 URL 複製到驗證器應用程式(Google Authenticator、Authy 等):", + "verification_code": "驗證碼", + "confirm": "確認", + "disable": "停用", + "disable_confirm_prompt": "請輸入密碼以停用雙因素驗證。", + "password_required": "需要密碼", + "code_required": "需要驗證碼", + "code_invalid": "驗證碼無效。請檢查驗證器應用程式後再試一次。" + }, + "app_passwords": { + "title": "應用程式密碼", + "description": "為不支援雙因素驗證的應用程式建立密碼", + "add": "新增", + "create": "建立", + "cancel": "取消", + "done": "完成", + "generate": "產生", + "name_label": "應用程式名稱", + "name_placeholder": "例如 Thunderbird、iPhone 郵件", + "expires_label": "到期日(選填)", + "allowed_ips_label": "允許的 IP(選填)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "用逗號或空格分隔。留空則允許任意 IP。", + "password_label": "密碼(留空時自動產生)", + "password_placeholder": "留空時自動產生", + "copy_now_warning": "請立即複製此密碼 — 之後將不再顯示。", + "added": "已建立應用程式密碼", + "removed": "已移除應用程式密碼", + "add_error": "無法建立應用程式密碼", + "remove_error": "無法移除應用程式密碼", + "none": "尚未設定應用程式密碼" + }, + "api_keys": { + "title": "API 金鑰", + "description": "為直接與伺服器通訊的指令碼和整合建立 API 金鑰", + "name_label": "金鑰名稱", + "name_placeholder": "例如:備份指令碼、CI 執行器", + "copy_now_warning": "請立即複製此 API 金鑰 — 之後將不再顯示。", + "added": "API 金鑰已建立", + "removed": "API 金鑰已刪除", + "add_error": "建立 API 金鑰失敗", + "remove_error": "刪除 API 金鑰失敗", + "none": "未設定 API 金鑰" + }, + "encryption": { + "section_title": "靜態資料加密", + "label": "郵件加密", + "description": "加密儲存在伺服器上的郵件,提供額外的隱私保障", + "active": "{type} 加密已啟用", + "inactive": "未啟用", + "enabled": "已啟用靜態資料加密", + "disabled_success": "已停用靜態資料加密", + "error": "無法更新加密設定", + "algorithm_label": "加密演算法", + "encrypt_on_append": "上傳時加密新郵件", + "allow_spam_training": "在加密郵件前允許垃圾郵件訓練", + "enable_encryption": "啟用加密", + "enable_tooltip": "使用此金鑰啟用加密", + "configure_title": "使用此金鑰啟用加密", + "enable_button": "啟用", + "disable_tooltip": "停用加密", + "enabled_success": "已啟用靜態資料加密" + }, + "public_keys": { + "name_label": "公鑰名稱", + "name_placeholder": "例如:我的金鑰", + "key_label": "公開金鑰(S/MIME 或 PGP ASCII 裝甲格式)", + "key_placeholder": "在此貼上您的公鑰", + "none": "未設定公鑰" + }, + "email_client": { + "title": "郵件用戶端設定", + "description": "使用以下登入資訊設定桌面版或行動裝置電子郵件用戶端(Thunderbird、Apple Mail、Outlook 等)", + "jmap_username_label": "JMAP 使用者名稱", + "copy": "複製", + "copied": "已複製", + "password_instructions": "使用上方的 JMAP 使用者名稱與應用程式密碼登入電子郵件用戶端。如果尚未建立應用程式密碼,請在上方區段建立一組。" + }, + "link_device": { + "title": "連結行動應用程式", + "description": "不需在手機上輸入密碼,即可登入手機上的 Bulwark Mail 應用程式。手機會取得獨立的登入狀態,因此在這裡登出不會讓手機登出。", + "generate": "顯示 QR Code", + "regenerate": "顯示新的 QR Code", + "instructions": "開啟 Bulwark Mail 應用程式,在登入畫面點選「Scan a sign-in code」(如果已經登入,請點選「Add account」→「Scan a sign-in code」),再將相機對準這裡。", + "expires_in": "此 QR Code 將在 {seconds} 秒後到期,且只能使用一次。", + "expired": "此 QR Code 已到期。", + "error": "無法建立配對碼。請再試一次。", + "password_prompt": "請輸入密碼以確認是你本人。手機會取得獨立的登入狀態;你的密碼不會傳送到手機。", + "show_password": "顯示密碼", + "hide_password": "隱藏密碼", + "continue": "繼續", + "error_wrong_password": "密碼錯誤,請再試一次。", + "totp_prompt": "請輸入驗證器應用程式中的驗證碼。", + "error_password_or_code": "密碼或驗證碼不正確。", + "error_not_signed_in": "你的工作階段已過期。請重新登入後再連結裝置。", + "error_impersonation": "檢視其他使用者的信箱時無法連結裝置。", + "error_too_many_attempts": "嘗試次數過多,請稍候再試。", + "error_session_secret": "此伺服器尚未設定裝置連結(缺少 SESSION_SECRET)。請洽詢管理員。", + "error_server_unreachable": "無法連線到你的郵件伺服器,請再試一次。", + "error_pairing_unavailable": "你的郵件伺服器不允許以這種方式連結裝置。請洽詢管理員。", + "qr_alt": "登入 QR Code", + "link_label": "登入連結", + "copy_link": "複製登入連結", + "copied": "已複製", + "copy_hint": "沒有相機?複製連結,然後在應用程式中透過「Paste sign-in link」貼上。", + "success_title": "已在手機上登入", + "success_body": "你可以關閉此頁面。若要連結其他裝置,請顯示新的 QR Code。", + "link_another": "連結其他裝置", + "use_sso": "改用單一登入", + "error_insecure_server": "行動應用程式只透過 https 連線到郵件伺服器,而此伺服器使用的是 http。" + } + }, + "identities": { + "title": "寄件身分", + "description": "管理可用來寄信的電子郵件地址", + "identities_count": { + "label": "您的寄件身分", + "description": "已設定用來寄信的電子郵件地址", + "count_zero": "尚無寄件身分", + "count_one": "1 個寄件身分", + "count_other": "{count} 個寄件身分" + }, + "manage": "管理寄件身分", + "sub_addressing": { + "label": "子地址功能", + "description": "使用 user+tag@domain.com 這類標籤來整理收到的郵件", + "learn_more": "瞭解更多" + } + }, + "vacation": { + "title": "外出", + "description": "您不在時,自動處理收到的郵件", + "loading": "正在載入自動回覆設定…", + "not_supported": "您的郵件伺服器不支援自動回覆功能。", + "fetch_error": "無法載入自動回覆設定。請再試一次。", + "status": { + "label": "傳送自動回覆", + "description": "自動回覆寄信給您的人", + "active": "已啟用", + "inactive": "已停用" + }, + "date_range": { + "start": "開始日期", + "start_description": "留空表示不限制開始時間", + "end": "結束日期", + "end_description": "留空表示不限制結束時間" + }, + "message": { + "subject_label": "主題", + "subject_description": "自動回覆郵件的主題", + "subject_placeholder": "不在辦公室", + "body_label": "郵件內文", + "body_description": "純文字郵件內容", + "body_placeholder": "感謝您的來信。我目前不在辦公室,返回後會儘快回覆您的郵件。", + "html_label": "格式化郵件(HTML)", + "html_description": "新增包含連結與樣式的富文字版本。若收件者的郵件用戶端無法顯示,則改用上方純文字版本。", + "html_placeholder": "撰寫格式化的不在辦公室回覆…" + }, + "preview": { + "title": "預覽", + "show": "顯示預覽", + "hide": "隱藏預覽" + }, + "save": "儲存變更", + "saving": "正在儲存…", + "warnings": { + "end_before_start": "結束日期必須晚於開始日期", + "invalid_date": "請輸入含時間的有效開始與結束日期,或將該欄位留空", + "start_in_past": "開始日期已在過去", + "empty_body": "郵件內文為空 — 收件者將收到空白回覆", + "subject_too_long": "主旨對伺服器來說太長(最多 {max} 位元組)。", + "body_too_long": "訊息對伺服器來說太長(含格式最多 {max} 位元組)。", + "forward_address": "請輸入有效的轉寄地址", + "not_running": "轉寄或回覆對象已儲存,但目前未生效。儲存即可重新啟用。" + }, + "forward": { + "title": "轉寄", + "description": "在上方期間內將收到的郵件轉寄到另一個地址,無論是否啟用自動回覆(未設結束日期時,直到關閉為止)。垃圾郵件不會被轉寄。", + "enabled_label": "轉寄郵件", + "to_label": "轉寄至", + "to_placeholder": "email@example.com", + "keep_label": "保留在收件匣中", + "keep_description": "否則郵件只會被轉寄:不會保留在此信箱中,也不會對其執行任何篩選規則。" + }, + "audience": { + "label": "回覆對象", + "description": "內部寄件者:{domains}", + "all": "所有寄件者", + "internal": "僅內部寄件者", + "external": "僅外部寄件者" + }, + "auto_reply": { + "title": "自動回覆" + } + }, + "folders": { + "role_memos": "備忘錄", + "title": "信件匣", + "description": "管理郵件信件匣並指派標準角色", + "folder_list": "您的信件匣", + "folder_list_description": "點擊信件匣圖示即可自訂", + "standard_roles": "標準信件匣角色", + "standard_roles_description": "指派哪些信件匣作為收件匣、寄件備份、垃圾桶等標準角色。", + "role_inbox": "收件匣", + "role_drafts": "草稿", + "role_sent": "寄件備份", + "role_trash": "垃圾桶", + "role_junk": "垃圾郵件", + "role_archive": "封存", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "無", + "create_folder": "建立信件匣", + "create_subfolder": "建立子信件匣", + "reorder": "上下拖曳可調整順序,左右拖曳可變更上層信件匣", + "reorder_error": "無法重新排序信件匣", + "move_error": "無法移動信件匣", + "select_folder": "選取 {name}", + "select_hint": "選取。按住 Shift 點按可選取直到上次點按的所有資料夾。", + "selected_count": "已選取 {count, plural, other {# 個資料夾}}", + "move_to": "移動到...", + "move_to_top_level": "最上層", + "move": "移動", + "clear_selection": "清除選取", + "folders_moved": "{count, plural, other {已移動 # 個資料夾}}", + "move_partial_error": "{count, plural, other {# 個資料夾}}無法移動", + "subfolder_of": "位於 {name} 內", + "subfolder_name": "子信件匣名稱", + "new_folder_name": "信件匣名稱", + "rename": "重新命名", + "change_icon": "變更圖示", + "count_mode": "{name} 的計數", + "count_mode_default": "計數:預設", + "count_mode_unread": "僅未讀", + "count_mode_all": "未讀與總數", + "count_mode_none": "不顯示計數", + "delete": "刪除", + "confirm_delete": "您確定要刪除「{name}」嗎?此信件匣中的郵件將移至垃圾桶。", + "create": "建立", + "cancel": "取消", + "no_folders": "尚無自訂信件匣", + "cannot_delete_role": "無法刪除具有標準角色的信件匣。請先移除該角色。", + "folder_created": "已建立信件匣", + "folder_renamed": "已重新命名信件匣", + "folder_deleted": "已刪除信件匣", + "role_updated": "已更新信件匣角色", + "error_create": "無法建立信件匣", + "error_rename": "無法重新命名信件匣", + "error_delete": "無法刪除信件匣", + "error_delete_has_children": "無法刪除信件匣:其中仍有子信件匣。請先刪除或移動它們。", + "error_delete_has_email": "無法刪除信件匣:其中仍有郵件。請先移動或刪除這些郵件。", + "error_role": "無法更新信件匣角色" + }, + "advanced": { + "title": "進階", + "description": "進階選項與開發者設定", + "debug_mode": { + "label": "除錯模式", + "description": "啟用詳細記錄以進行疑難排解" + }, + "debug_categories": { + "description": "選取要記錄的類別。停用不需要的類別,可減少主控台雜訊。", + "jmap": "JMAP 用戶端", + "jmap_description": "信件匣操作、郵件取得與 JMAP 通訊協定請求", + "calendar": "行事曆", + "calendar_description": "行事曆活動、匯入與排程訊息", + "tasks": "任務", + "tasks_description": "行事曆任務建立、取得和更新", + "auth": "驗證", + "auth_description": "登入、TOTP、權杖交換與工作階段管理", + "filters": "篩選規則", + "filters_description": "Sieve 篩選規則和休假自動回覆指令碼", + "email": "郵件檢視", + "email_description": "郵件呈現、TNEF 處理與標示為已讀", + "push": "推送通知", + "push_description": "推送通知設定和傳送", + "contacts": "聯絡人與通訊錄", + "contacts_description": "聯絡人同步、通訊錄操作與受信任寄件者" + }, + "settings_sync": { + "label": "設定同步", + "description": "跨瀏覽器和裝置同步您的設定" + }, + "sender_favicons": { + "label": "寄件者圖示", + "description": "使用企業寄件者的網站圖示作為頭像", + "refresh_label": "重新整理寄件者圖示", + "refresh_description": "重新取得網站圖示,例如寄件者更換了圖示時", + "refresh_button": "重新整理", + "refresh_done": "將重新取得寄件者圖示" + }, + "show_avatars_in_junk": { + "label": "在垃圾郵件信件匣顯示頭像", + "description": "在垃圾郵件信件匣中顯示寄件者的頭像與網站圖示。預設停用,以免釣魚郵件因熟悉的圖示而顯得可信。" + }, + "keyboard_shortcuts": { + "label": "鍵盤快速鍵", + "description": "檢視可用快速鍵", + "button": "檢視快速鍵" + }, + "refresh_cache": { + "label": "重新整理快取資料", + "description": "從伺服器重新載入聯絡人、行事曆與信件匣。保留帳號與工作階段,不需登出即可修正過時或錯誤的畫面。", + "button": "重新整理" + }, + "reset_settings": { + "label": "重設設定", + "description": "將所有設定還原為預設值", + "button": "重設為預設值" + }, + "export_settings": { + "label": "匯出設定", + "description": "將設定匯出為 JSON 檔案", + "button": "匯出" + }, + "import_settings": { + "label": "匯入設定", + "description": "從 JSON 檔案匯入設定", + "button": "匯入" + }, + "about": { + "title": "Bulwark 網頁郵件", + "source_code": "原始碼" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "在垃圾郵件和釣魚郵件進入收件匣之前點擊攔截。放行真實郵件。三面盾牌全部失去,收件匣就會失守。", + "kinds": { + "spam": "垃圾郵件", + "phishing": "釣魚郵件", + "legit": "真實郵件" + }, + "click_to_block": "點擊攔截", + "let_through": "放行", + "start": "開始", + "play_again": "再玩一次", + "score": "得分", + "combo": "連擊獎勵", + "shields_left": "剩餘盾牌 {count}/{total}", + "seconds": "{seconds} 秒", + "best": "最高分:{score}", + "new_best": "新紀錄", + "held": "收件匣守住了", + "overrun": "收件匣失守", + "stats": { + "caught": "已攔截", + "delivered": "已送達", + "mistakes": "失誤" + } + } + }, + "sidebar_apps": { + "title": "側邊欄應用程式", + "description": "管理側邊欄中的自訂應用程式與連結", + "keep_loaded": "保持應用程式載入", + "keep_loaded_description": "切換內嵌應用程式時,讓其在背景繼續執行,以避免重新載入", + "managed_title": "由您的管理員提供", + "managed_description": "這些應用程式為所有人統一設定,無法變更。", + "manage_title": "自訂應用程式", + "manage_description": "在側邊欄新增、編輯或移除自訂應用程式" + }, + "contacts": { + "title": "聯絡人", + "description": "匯入與匯出聯絡人", + "group_by_letter_label": "依首字分組", + "sort_by_last_name_label": "依姓氏排序", + "sort_by_last_name_description": "依姓氏排列聯絡人清單,讓家庭成員顯示在一起", + "group_by_letter_description": "在聯絡人清單中顯示依字母排列的分節標題", + "import_label": "匯入聯絡人", + "import_description": "從 vCard(.vcf)檔案匯入聯絡人", + "export_label": "匯出聯絡人", + "export_description": "將所有聯絡人匯出為 vCard(.vcf)檔案", + "manage_title": "通訊錄", + "manage_description": "重新命名您的通訊錄", + "no_address_books": "找不到通訊錄", + "categories_title": "類別", + "categories_description": "重新命名聯絡人類別", + "no_categories": "找不到類別" + }, + "downloads": { + "title": "下載", + "description": "自訂下載的郵件和附件的命名方式。", + "reset": "還原預設值", + "preview": "預覽:", + "email_template": { + "label": "電子郵件(.eml)檔名", + "description": "匯出郵件或將其拖到檔案系統時使用的範本。副檔名 .eml 會自動新增。" + }, + "attachment_template": { + "label": "附件檔名", + "description": "下載或拖出附件時使用的範本。如果省略 '{filename}' 和 '{ext}',將保留原始副檔名。" + }, + "bundle_template": { + "label": "多郵件 .zip 檔名", + "description": "將多封選取的郵件拖出或下載為單一 .zip 壓縮檔時使用的範本。系統會自動加上 .zip 副檔名。" + }, + "spaces": { + "label": "空格", + "description": "將產生的檔名中的空格取代為其他字元。", + "keep": "保留空格", + "underscore": "取代為 _", + "dash": "取代為 -" + }, + "lowercase": { + "label": "小寫", + "description": "將整個檔名轉為小寫。" + }, + "strip_diacritics": { + "label": "去除變音符號", + "description": "將帶重音的字母轉換為對應的 ASCII 字元(ä → a、é → e)。適用於無法正確處理 Unicode 檔名的工具。" + }, + "collapse_separators": { + "label": "縮減重複的分隔字元", + "description": "將連續的空格、底線或連字號縮減為單一字元。" + }, + "after_export": { + "label": "匯出後", + "description": "匯出為 .eml 後,可選擇移動郵件。", + "keep": "保留在信件匣", + "archive": "移至封存", + "trash": "移至垃圾桶" + } + }, + "filters": { + "title": "郵件篩選規則", + "description": "建立規則,自動整理、標記和管理收到的郵件", + "add_rule": "新增規則", + "no_rules": "尚無篩選規則", + "no_rules_description": "建立規則以自動整理收到的郵件", + "vacation_active": "休假自動回覆已開啟", + "vacation_active_description": "已啟用收到郵件時的自動回覆", + "vacation_configure": "設定", + "edit_rule": "編輯規則", + "new_rule": "新規則", + "delete_rule": "刪除規則", + "delete_confirm": "您確定要刪除此規則嗎?", + "enable": "啟用", + "disable": "停用", + "raw_editor": "原始 Sieve 編輯器", + "raw_editor_warning": "編輯原始 Sieve 指令碼可能導致視覺化規則編輯失效。這裡的變更會覆寫視覺化建構工具。", + "validate": "驗證", + "validation_success": "指令碼有效", + "validation_error": "指令碼有錯誤", + "save": "儲存規則", + "saving": "正在儲存…", + "saved": "篩選規則已成功儲存", + "save_failed": "無法儲存篩選規則", + "loading": "正在載入篩選規則…", + "not_supported": "您的郵件伺服器不支援郵件篩選規則。", + "rule_name": "規則名稱", + "rule_name_placeholder": "例如,整理訂閱郵件", + "match_all": "符合所有條件", + "match_any": "符合任一條件", + "conditions": "條件", + "add_condition": "新增條件", + "actions": "動作", + "add_action": "新增動作", + "stop_processing": "停止處理後續規則", + "include_spam": "同時移動被標記為垃圾郵件的郵件", + "attachment_type_placeholder": "例如:pdf、doc、jpg", + "value_placeholder_multi": "值(多個用逗號分隔)", + "condition_fields": { + "attachment": "附件", + "from": "寄件者", + "to": "收件者", + "cc": "副本", + "subject": "主題", + "header": "自訂標頭", + "size": "大小", + "body": "內文", + "all": "所有郵件" + }, + "comparators": { + "has_any": "存在", + "has_type": "型別為", + "contains": "包含", + "not_contains": "不包含", + "is": "等於", + "not_is": "不是", + "starts_with": "以…開頭", + "ends_with": "以…結尾", + "matches": "符合模式", + "greater_than": "大於", + "less_than": "小於", + "address_is": "地址為", + "domain_is": "網域為" + }, + "action_types": { + "move": "移至信件匣", + "copy": "複製到信件匣", + "forward": "轉寄至", + "mark_read": "標示為已讀", + "star": "為郵件加上星號", + "add_label": "新增標籤", + "discard": "捨棄(直接刪除)", + "reject": "附上訊息拒絕", + "keep": "保留在收件匣中", + "stop": "停止處理" + }, + "move_to_folder": "選取信件匣", + "copy_to_folder": "選取信件匣", + "forward_to": "轉寄至電子郵件地址", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "保留副本", + "forward_limit": "此伺服器每封郵件的轉寄上限:{count}。多餘的轉寄將被略過。", + "forward_unavailable": "此伺服器不允許轉寄。篩選規則中的轉寄動作不會生效。", + "reject_message": "拒絕留言", + "reject_placeholder": "您的郵件已被拒絕", + "label_name": "標籤名稱", + "label_placeholder": "選取標籤", + "header_name": "標頭名稱", + "header_placeholder": "例如,X-Mailing-List", + "size_bytes": "大小(以位元組為單位)", + "size_placeholder": "例如,1000000", + "system_managed": "系統管理的規則", + "opaque_warning": "此指令碼是在視覺化建構工具之外編輯的。目前只能使用原始 Sieve 編輯器。", + "open_sieve_editor": "開啟原始 Sieve 編輯器", + "fetch_error": "無法載入篩選規則", + "expanded_view": "展開檢視", + "expanded_view_description": "顯示包含詳細條件與動作區塊的篩選規則", + "if": "如果", + "then": "然後", + "match_all_conditions": "全部符合", + "match_any_condition": "任一符合", + "and": "且", + "or": "或", + "cancel": "取消", + "confirm_delete": "刪除", + "rule_list": "篩選規則", + "drag_to_reorder": "拖曳以重新排序", + "match_type": "比對方式", + "reset_to_visual": "重設為視覺化建構工具", + "reset_warning": "這會捨棄目前的指令碼並重新開始。", + "confirm_reset": "重設", + "validation_empty_name": "規則名稱為必填欄位", + "validation_empty_conditions": "至少需要一個具有值的條件", + "validation_empty_actions": "至少需要一個動作", + "templates_section": "從範本開始", + "template_newsletters": "將訂閱郵件移至信件匣", + "template_receipts": "自動封存收據", + "template_important": "標示重要郵件", + "template_notifications": "篩選通知", + "sieve_editor": { + "title": "Sieve 指令碼編輯器", + "warning": "編輯原始 Sieve 指令碼可能導致視覺化規則編輯失效。這裡的變更會覆寫視覺化建構工具。", + "script_content": "Sieve 指令碼", + "valid": "指令碼有效", + "invalid": "指令碼有錯誤", + "save_warning": "儲存會覆寫所有視覺化規則,且無法復原。請再次點擊「儲存」以確認。", + "validating": "正在驗證…", + "validate": "驗證", + "cancel": "取消", + "save": "儲存", + "confirm_save": "確認儲存", + "validation_failed": "驗證請求失敗" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# 個條件} other {# 個條件}}", + "actions_count": "{count, plural, one {# 個動作} other {# 個動作}}" + }, + "origin_external": "外部", + "managed_by_tooltip": "由 {source} 管理。請在該應用程式中編輯,或使用原始 Sieve 編輯器。", + "quick_rule_names": { + "move_sender": "將 {sender} 的郵件移至 {folder}", + "move_domain": "將 @{domain} 的郵件移至 {folder}", + "move_list": "將清單 {list} 移至 {folder}", + "mark_read": "將 {sender} 的郵件標示為已讀", + "tag": "為 {sender} 的郵件加上標籤 {tag}", + "block": "封鎖 {sender}", + "custom": "來自 {sender} 的郵件", + "custom_plain": "新規則", + "senders": "{count, plural, other {# 位寄件者}}" + }, + "suggestions": "建議", + "suggest_subject": "主旨「{subject}」", + "suggest_to": "收件者 {address}", + "suggest_cc": "副本 {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "也套用到此資料夾中既有的郵件", + "apply_existing_unsupported": "只有檢查寄件者、收件者、主旨或標頭,並執行移動、複製、標示為已讀、加上星號或標籤的規則,才能套用到既有郵件。", + "period_title": "日期範圍", + "period_start": "開始日期", + "period_end": "結束日期", + "validation_period_order": "結束日期必須晚於開始日期", + "period_toggle": "僅在指定期間內生效", + "period_hint": "適用於在此期間內收到的郵件。留空某一欄即表示該端不設限制。", + "validation_period_empty": "請輸入期間的開始日期或結束日期", + "validation_period_invalid": "請為期間輸入含時間的有效日期,或將該欄位留空", + "apply_existing_period": "設定了期間的規則只作用於在該期間內收到的郵件,因此無法套用到既有郵件。", + "period_status_scheduled": "尚未開始", + "period_status_active": "生效中", + "period_status_expired": "已過期" + }, + "templates": { + "title": "郵件範本", + "description": "使用預留位置變數建立可重用的郵件範本", + "add": "新增範本", + "edit": "編輯範本", + "name": "範本名稱", + "name_placeholder": "例如,後續郵件", + "category": "類別", + "category_placeholder": "例如,工作、個人", + "subject": "主題", + "subject_placeholder": "郵件主題行", + "body": "內文", + "body_placeholder": "郵件內文內容…", + "recipients_placeholder": "email@example.com", + "identity": "寄件身分", + "default_identity": "預設寄件身分", + "favorite": "我的最愛", + "cancel": "取消", + "create": "建立", + "update": "更新", + "confirm_delete": "刪除", + "no_templates": "尚無範本", + "manage": "管理範本", + "count": "{count, plural, one {# 個範本} other {# 個範本}}", + "export_import": "匯出與匯入", + "export_import_description": "備份您的範本,或移轉到其他裝置", + "export": "匯出", + "import": "匯入", + "validation": { + "empty": "範本名稱為必填欄位", + "too_long": "範本名稱不得超過 200 個字元" + } + }, + "files": { + "display": { + "title": "顯示", + "description": "設定檔案和資料夾的顯示方式" + }, + "default_view": { + "label": "預設檢視", + "description": "選擇網格或清單版面配置", + "list": "清單", + "grid": "網格" + }, + "default_sort": { + "label": "預設排序", + "description": "選擇檔案的預設排序規則", + "name": "名稱", + "size": "大小", + "modified": "修改日期" + }, + "sort_direction": { + "label": "排序方式", + "description": "選擇升序或降序", + "ascending": "遞增", + "descending": "遞減" + }, + "icons": { + "title": "圖示", + "description": "設定檔案圖示外觀" + }, + "show_icons": { + "label": "顯示檔案圖示", + "description": "在檔案和資料夾旁邊顯示圖示" + }, + "colored_icons": { + "label": "彩色圖示", + "description": "使用彩色圖示取代單色圖示" + }, + "show_thumbnails": { + "label": "顯示縮圖", + "description": "圖片檔案顯示預覽,而不是圖示" + }, + "behavior": { + "title": "行為", + "description": "設定檔案瀏覽器行為" + }, + "show_hidden": { + "label": "顯示隱藏檔案", + "description": "顯示名稱以句點開頭的檔案與資料夾" + }, + "preview": { + "label": "預覽" + } + } + }, + "errors": { + "page_error_title": "出了點問題", + "page_error_description": "發生未預期的錯誤。請再試一次或返回首頁。", + "sidebar_error": "無法載入信件匣", + "email_list_error": "無法載入郵件", + "viewer_error_title": "無法顯示郵件", + "viewer_error_description": "呈現此郵件時出現問題。它可能包含不受支援的內容。", + "composer_error": "無法載入郵件撰寫", + "settings_error_title": "無法使用設定", + "settings_error_description": "無法載入設定。您的偏好設定可能不會被儲存。", + "try_again": "再試一次", + "reload": "重新載入", + "reload_emails": "重新載入郵件", + "reload_settings": "重新載入設定", + "retry": "重試", + "go_home": "前往收件匣" + }, + "context_menu": { + "reply": "回覆", + "reply_all": "全部回覆", + "forward": "轉寄", + "mark_read": "標示為已讀", + "mark_unread": "標示為未讀", + "star": "加上星號", + "unstar": "移除星號", + "pin": "固定", + "unpin": "取消固定", + "move_to": "移至…", + "copy_to_account": "複製到帳戶…", + "remove_from_folder": "從此資料夾移除", + "archive": "封存", + "delete": "刪除", + "mark_as_spam": "回報垃圾郵件", + "not_spam": "非垃圾郵件", + "tag": "標籤", + "items_selected": "已選取 {count} 封郵件", + "forward_as_attachments": "以附件形式轉寄", + "edit_draft": "編輯草稿", + "cancel_scheduled_send": "取消傳送", + "reschedule_send": "重新排程", + "cancel_and_edit": "取消並編輯", + "cancel_and_compose_again": "取消並重新撰寫", + "rules": { + "title": "規則", + "move_from": "一律移動來自 {sender} 的郵件", + "move_from_domain": "一律移動來自 @{domain} 的郵件", + "move_from_list": "一律移動此郵件清單的郵件", + "mark_read_from": "一律將來自 {sender} 的郵件標示為已讀", + "tag_with": "一律加上標籤", + "block": "{count, plural, =1 {封鎖寄件者} other {封鎖 # 位寄件者}}", + "no_junk": "此帳號沒有垃圾郵件資料夾", + "create": "建立規則…", + "manage": "管理規則", + "senders_count": "{count, plural, other {# 位寄件者}}", + "cross_account": "請選取同一個帳號中的郵件", + "opaque_hint": "你的篩選規則曾被手動編輯。請開啟篩選規則設定" , + "loading": "正在載入規則…", + "load_failed": "無法載入規則", + "none": "尚無規則", + "new_mail_only": "僅限新郵件", + "running": "正在 {folder} 中執行「{name}」…" + }, + "find_emails_from": "尋找來自 {name} 的郵件" + }, + "mailbox_context_menu": { + "mark_folder_read": "將信件匣標示為已讀", + "mark_folder_tree_read": "將信件匣與子信件匣標示為已讀", + "mark_all_folders_read": "將所有信件匣標示為已讀", + "new_subfolder": "新增子信件匣…", + "new_folder": "新增信件匣…", + "rename": "重新命名…", + "share": "共用...", + "import_email": "匯入 .eml 或 .zip…", + "empty_folder": "清空信件匣", + "empty_folder_generic": "清空信件匣", + "delete_folder": "刪除信件匣", + "refresh": "重新整理", + "hide_shared_account": "從側邊欄隱藏", + "mute_notifications": "靜音通知", + "unmute_notifications": "取消靜音通知", + "mute_account_notifications": "靜音此帳號的通知", + "unmute_account_notifications": "取消靜音此帳號的通知", + "reset_folder_notifications": "重設資料夾通知設定", + "mark_all_confirm_title": "將所有信件匣標示為已讀", + "mark_all_confirm_message": "要將您個人帳號中的所有未讀郵件標示為已讀嗎?", + "delete_confirm_title": "刪除信件匣", + "delete_confirm_message": "要永久刪除信件匣「{name}」嗎?此動作無法復原。", + "prompt_new_subfolder": "請輸入新子信件匣的名稱。", + "prompt_new_folder": "請輸入新信件匣的名稱。", + "prompt_rename": "請輸入此信件匣的新名稱。", + "placeholder_folder_name": "信件匣名稱", + "create": "建立", + "rename_confirm": "重新命名", + "toast_marked_read": "信件匣已標示為已讀", + "toast_marked_read_count": "已將 {count, plural, one {1 封郵件} other {# 封郵件}} 標示為已讀", + "toast_already_read": "沒有未讀郵件", + "toast_marked_all_read": "所有信件匣已標示為已讀", + "toast_emptied": "信件匣已清空", + "toast_folder_created": "已建立信件匣", + "toast_folder_renamed": "已重新命名信件匣", + "toast_folder_deleted": "已刪除信件匣", + "toast_error_mark_read": "無法標示為已讀", + "toast_error_empty": "無法清空信件匣", + "toast_error_create": "無法建立信件匣", + "toast_error_rename": "無法重新命名信件匣", + "toast_error_delete": "無法刪除信件匣", + "toast_error_delete_has_children": "信件匣內有子信件匣,請先移除。", + "toast_error_delete_has_email": "信件匣不是空的,請先清空。", + "delete_with_contents_title": "資料夾不是空的", + "delete_with_contents_message": "資料夾「{name}」中仍有 {count} 封郵件。要刪除該資料夾及其所有郵件嗎?此操作無法復原。", + "delete_with_contents_confirm": "刪除資料夾和郵件" + }, + "shortcuts": { + "title": "鍵盤快速鍵", + "tip": "隨時按下 ? 即可顯示此說明", + "sections": { + "navigation": "導覽", + "actions": "郵件動作", + "global": "全域", + "threads": "對話串", + "composer": "郵件撰寫" + }, + "navigation": { + "next_email": "下一封郵件", + "previous_email": "上一封郵件", + "open_email": "開啟郵件", + "close_email": "關閉/取消選取", + "go_to_folder": "跳至資料夾", + "next_unread": "下一封未讀郵件", + "no_more_unread": "清單下方沒有更多未讀郵件" + }, + "actions": { + "reply": "回覆", + "reply_all": "全部回覆", + "forward": "轉寄", + "star": "加上/移除星號", + "archive": "封存", + "delete": "刪除", + "mark_unread": "標示為未讀", + "mark_read": "標示為已讀", + "toggle_spam": "回報垃圾郵件/非垃圾郵件" + }, + "global": { + "compose": "撰寫新郵件", + "search": "將焦點移至搜尋框", + "help": "顯示快速鍵", + "refresh": "重新整理郵件", + "select_all": "全部選取", + "global_search": "全域搜尋", + "extend_selection": "向下 / 向上擴展選取" + }, + "threads": { + "expand_collapse": "展開/收合對話串", + "select_mountain_view": "選取會話" + }, + "composer": { + "send": "傳送郵件", + "schedule_send": "排程寄送", + "template_picker": "開啟範本選取器" + } + }, + "threads": { + "messages_one": "{count} 封郵件", + "messages_other": "{count} 封郵件", + "messages_tooltip": "{count, plural, one {此對話串有 # 封郵件} other {此對話串有 # 封郵件}}", + "expand": "展開對話串", + "collapse": "收合對話串", + "loading": "正在載入對話串…", + "mark_read": "將對話串標示為已讀", + "mark_unread": "將對話串標示為未讀", + "archive": "封存對話串", + "delete": "刪除對話串", + "star": "為對話串加上星號", + "unstar": "移除對話串星號", + "toggle_thread": "展開/收合對話串" + }, + "identities": { + "modal_title": "管理寄件身分", + "create_new": "建立新的寄件身分", + "edit_identity": "編輯寄件身分", + "delete_confirm": "要刪除此寄件身分嗎?此動作無法復原。", + "cannot_delete": "無法刪除此寄件身分", + "primary_identity": "主要", + "set_as_primary": "設為主要", + "no_identities": "找不到寄件身分", + "display": { + "reply_to": "回覆地址:", + "bcc": "密件副本:", + "signature": "簽名檔:", + "preview": "預覽:" + }, + "validation_errors": { + "invalid_emails": "無效電子郵件地址:{emails}", + "unknown_error": "未知錯誤" + }, + "form": { + "name_label": "顯示名稱", + "name_placeholder": "例如,工作信箱、個人信箱", + "name_required": "名稱為必填欄位", + "email_label": "電子郵件地址", + "email_placeholder": "your.email@example.com", + "email_required": "電子郵件地址為必填欄位", + "email_invalid": "請輸入有效的電子郵件地址", + "email_immutable": "寄件身分建立後,無法變更電子郵件地址", + "reply_to_label": "回覆地址(選填)", + "reply_to_placeholder": "reply@example.com", + "bcc_label": "自動密件副本(選填)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "純文字簽名檔", + "html_signature_label": "HTML 簽名檔", + "signature_byte_counter": "{bytes} / {max} 位元組", + "signature_byte_limit_reached": "已達伺服器限制", + "save": "儲存寄件身分", + "cancel": "取消", + "creating": "正在建立…", + "updating": "正在更新…" + }, + "sub_address": { + "button_tooltip": "使用子地址", + "popover_title": "新增子地址標籤", + "tag_input_placeholder": "輸入標籤(例如購物)", + "preview_label": "預覽:", + "recent_tags": "最近的標籤", + "suggested_tags": "建議", + "use_address": "使用此電子郵件地址", + "invalid_tag": "標籤只能包含英文字母、數字與連字號", + "tag_too_long": "標籤不得超過 30 個字元", + "help_text": "傳送到 user{delimiter}tag@domain.com 的郵件將送達您的收件匣", + "validation": { + "empty": "標籤不能為空", + "too_long": "標籤不得超過 {max} 個字元", + "invalid_chars": "標籤只能包含英文字母、數字與連字號" + } + }, + "badge": { + "sent_via": "透過", + "sub_address_tag": "使用子地址傳送:{tag}", + "identity_name": "使用寄件身分傳送:{name}", + "identity_short": "透過 {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "刪除", + "delete_confirm_title": "刪除寄件身分" + }, + "templates": { + "picker_title": "選擇範本", + "search_placeholder": "搜尋範本…", + "section_favorites": "我的最愛", + "section_recent": "最近使用", + "section_uncategorized": "其他", + "no_templates": "尚無範本", + "no_results": "找不到範本", + "fill_placeholders": "填寫預留位置值", + "enter_value": "輸入值…", + "preview": "預覽", + "insert_with_values": "填入值後插入", + "insert_raw": "直接插入範本", + "copy_suffix": "(複製)", + "placeholder": "變數", + "placeholders": { + "recipient_name": "收件者姓名", + "company": "公司名稱", + "date": "目前日期", + "day_of_week": "星期幾", + "sender_name": "您的姓名" + } + }, + "contacts": { + "directory": { + "title": "通訊錄目錄", + "search_placeholder": "搜尋姓名或電子郵件地址", + "refresh": "重新整理目錄", + "retry": "重試", + "loading": "正在載入目錄…", + "unavailable_title": "沒有目錄", + "unavailable_hint": "你的郵件伺服器不提供帳戶目錄。", + "forbidden_title": "目錄無法使用", + "forbidden_hint": "你的郵件伺服器不允許此帳戶瀏覽目錄。請聯絡管理員。", + "error_title": "無法載入目錄", + "error_hint": "請檢查網路連線後重試。", + "empty_title": "目錄是空的", + "empty_hint": "你的郵件伺服器沒有列出具有電子郵件地址的帳戶。", + "no_match": "目錄中沒有符合搜尋的人", + "refresh_failed": "無法重新整理目錄,顯示的是上次載入的清單。", + "select_page": "選取本頁所有人", + "deselect_page": "取消選取本頁所有人", + "select_entry": "選取 {name}", + "range": "第 {from}–{to} 項,共 {total} 項", + "page": "第 {page} 頁,共 {count} 頁", + "previous_page": "上一頁", + "next_page": "下一頁", + "picker_title": "從目錄中選取", + "cancel": "取消", + "selected_count": "已選取 {count} 個", + "add_to": "加入收件者", + "add_cc": "加入副本", + "add_bcc": "加入密件副本", + "select_hint": "選取一個人以查看詳細資料", + "write_email": "撰寫郵件", + "add_to_contacts": "加入聯絡人", + "email": "電子郵件", + "read_only_note": "目錄項目來自你的郵件伺服器,無法在此編輯。", + "type": { + "individual": "個人", + "group": "群組", + "resource": "資源", + "location": "地點", + "other": "其他" + } + }, + "title": "聯絡人", + "search_placeholder": "搜尋聯絡人…", + "create_new": "新增聯絡人", + "no_category": "無類別", + "rename_category": "重新命名類別", + "category_name_label": "類別名稱", + "category_renamed": "類別已重新命名", + "category_rename_failed": "重新命名類別失敗", + "category_added": "聯絡人已新增至 {name}", + "category_added_plural": "{count} 位聯絡人已新增至 {name}", + "empty_state": "尚無聯絡人", + "empty_state_title": "尚無聯絡人", + "empty_state_subtitle": "建立第一位聯絡人,或從 vCard 檔案匯入", + "empty_search": "沒有符合您搜尋條件的聯絡人", + "empty_search_hint": "嘗試其他搜尋字詞", + "empty_filtered": "沒有符合您篩選條件的聯絡人", + "empty_filtered_hint": "請調整或清除篩選條件", + "clear_search": "清除搜尋", + "import_vcard": "匯入 vCard", + "delete_confirm_title": "刪除聯絡人", + "delete_confirm": "您確定要刪除此聯絡人嗎?", + "local_mode": "聯絡人儲存在本機(伺服器不支援 JMAP 聯絡人)", + "back_to_contacts": "回到聯絡人", + "back_to_email": "回到郵件", + "open_categories": "開啟類別", + "tabs": { + "all": "全部", + "groups": "群組" + }, + "shared": { + "title": "共用" + }, + "address_books": { + "create": "新增通訊錄", + "created": "通訊錄已建立", + "create_failed": "建立通訊錄失敗", + "title": "我的通訊錄", + "shared_prefix": "共用:{name}", + "moved": "聯絡人已移至 {name}", + "moved_plural": "{count} 聯絡人已移至 {name}", + "move_failed": "無法移動聯絡人", + "address_book": "通訊錄", + "rename": "重新命名通訊錄", + "name_label": "通訊錄名稱", + "renamed": "已重新命名通訊錄", + "rename_failed": "無法重新命名通訊錄", + "default": "預設", + "set_default": "設為預設", + "default_updated": "預設通訊錄已更新", + "set_default_failed": "無法設定預設通訊錄", + "manage": "管理通訊錄", + "share": "分享通訊錄", + "new_contact_in_book": "在此通訊錄中新增聯絡人", + "delete": "刪除通訊錄", + "confirm_delete": "要刪除「{name}」嗎?此通訊錄中的所有聯絡人都會移除。", + "deleted": "通訊錄已刪除", + "delete_failed": "刪除通訊錄失敗" + }, + "detail": { + "emails": "電子郵件地址", + "phones": "電話號碼", + "organizations": "組織", + "addresses": "地址", + "notes": "備註", + "titles": "職稱與角色", + "online_services": "線上服務", + "anniversaries": "週年紀念日", + "personal_info": "個人資訊", + "languages": "語言", + "categories": "類別", + "related_contacts": "相關聯絡人", + "crypto_keys": "加密金鑰", + "cert_issuer": "簽發者", + "cert_expires": "到期日", + "cert_expired": "已到期", + "cert_fingerprint": "指紋", + "cert_algorithm": "演算法", + "import_to_smime": "匯入到 S/MIME", + "cert_already_imported": "已匯入至 S/MIME", + "cert_imported": "憑證已匯入 S/MIME 憑證庫", + "cert_import_failed": "匯入憑證失敗", + "no_contact_selected": "選取一位聯絡人以檢視詳細資料", + "compose_email": "撰寫郵件", + "copy_email": "複製電子郵件地址", + "copy_phone": "複製電話號碼", + "copy_url": "複製 URL", + "copied": "已複製到剪貼簿", + "copy_failed": "無法複製到剪貼簿", + "created": "已建立", + "updated": "上次更新", + "timezone": "時區", + "anniversary_birth": "生日", + "anniversary_death": "已故", + "anniversary_wedding": "週年紀念日", + "anniversary_other": "其他", + "personal_expertise": "專業知識", + "personal_hobby": "嗜好", + "personal_interest": "興趣", + "personal_other": "其他", + "gender": "性別", + "gender_masculine": "男性", + "gender_feminine": "女性", + "gender_other": "其他", + "gender_none": "不適用", + "gender_unknown": "未知", + "calendar": "行事曆", + "calendar_uri": "行事曆 URL", + "scheduling_uri": "排程 URL", + "freebusy_uri": "空閒/忙碌 URL", + "section_contact": "聯絡資訊", + "section_work": "工作", + "section_personal": "個人", + "email_default_label": "電子郵件", + "phone_default_label": "電話", + "address_default_label": "地址", + "online_service_default_label": "線上", + "organization_label": "組織", + "title_label": "職稱", + "role_label": "角色", + "language_label": "語言", + "related_default_label": "相關聯絡人", + "more_actions": "更多動作", + "age_years": "{count} 歲", + "years_since": "{count} 年" + }, + "activity": { + "recent_emails": "最近的郵件", + "upcoming_events": "即將開始的活動", + "no_emails": "最近沒有郵件", + "no_events": "沒有即將開始的活動", + "no_subject": "(無主題)", + "no_title": "(無標題)", + "load_failed": "無法載入", + "unknown_sender": "未知寄件者", + "all_day": "全天" + }, + "form": { + "create_title": "新聯絡人", + "edit_title": "編輯聯絡人", + "section_address_book": "通訊錄", + "select_address_book": "選擇通訊錄…", + "section_identity": "姓名與身分", + "contact_type": "聯絡人類型", + "type_person": "個人", + "type_organization": "組織", + "section_work": "工作與組織", + "prefix": "稱謂", + "prefix_placeholder": "博士、先生、女士", + "given_name": "名", + "middle_name": "中間名", + "surname": "姓", + "suffix": "後綴", + "suffix_placeholder": "Jr.、Sr.、III", + "nickname": "暱稱", + "nickname_placeholder": "暱稱", + "email": "電子郵件地址", + "email_placeholder": "email@example.com", + "phone": "電話", + "phone_placeholder": "+1 234 567 890", + "phone_type": "類型", + "phone_voice": "語音電話", + "phone_cell": "行動電話", + "phone_fax": "傳真", + "phone_pager": "呼叫器", + "phone_video": "視訊", + "phone_text": "簡訊", + "organization": "組織", + "organization_placeholder": "公司名稱", + "department": "部門", + "department_placeholder": "部門", + "job_title": "職稱", + "job_title_placeholder": "例如,軟體工程師", + "role": "角色", + "role_placeholder": "例如,團隊負責人", + "addresses": "地址", + "add_address": "新增地址", + "street": "街道", + "city": "城市", + "region": "州/地區", + "postcode": "郵遞區號", + "country": "國家", + "online_services": "線上服務", + "add_online_service": "新增線上服務", + "url_placeholder": "https://...", + "service_placeholder": "服務", + "anniversaries": "週年紀念日", + "add_anniversary": "新增日期", + "anniversary_birth": "生日", + "anniversary_wedding": "週年紀念日", + "anniversary_death": "忌日", + "anniversary_other": "其他", + "personal_info": "個人資訊", + "add_personal_info": "新增項目", + "personal_info_placeholder": "例如,攝影", + "personal_expertise": "專業知識", + "personal_hobby": "嗜好", + "personal_interest": "興趣", + "personal_other": "其他", + "level": "等級", + "level_high": "高", + "level_medium": "中", + "level_low": "低", + "categories": "類別", + "categories_placeholder": "例如,家人、朋友、同事", + "categories_hint": "輸入搜尋或新增類別", + "category_add": "新增", + "note": "備註", + "note_placeholder": "新增備註…", + "gender": "性別", + "gender_sex": "性別", + "gender_male": "男性", + "gender_female": "女性", + "gender_other": "其他", + "gender_none": "不適用", + "gender_unknown": "未知", + "gender_identity": "性別認同", + "gender_identity_placeholder": "性別認同…", + "calendar": "行事曆", + "calendar_uri": "行事曆 URL", + "scheduling_uri": "排程 URL", + "freebusy_uri": "空閒/忙碌 URL", + "context_work": "工作", + "context_private": "個人", + "add_email": "新增電子郵件地址", + "add_phone": "新增電話", + "save": "儲存", + "cancel": "取消", + "creating": "正在建立…", + "updating": "正在更新…", + "name_required": "請輸入名、姓或組織名稱", + "email_invalid": "請輸入有效的電子郵件地址", + "email_error_inline": "電子郵件地址格式無效", + "save_failed": "儲存聯絡人失敗", + "delete": "刪除", + "upload_photo": "上傳照片", + "remove_photo": "移除照片", + "photo_hint": "JPG 或 PNG,最大 10 MB。圖片會自動調整大小。", + "photo_too_large": "圖片太大(上限 10 MB)", + "photo_invalid": "圖片檔案無效", + "change_photo": "變更" + }, + "groups": { + "create": "新增群組", + "edit": "編輯群組", + "empty": "尚無群組", + "delete_confirm_title": "刪除群組", + "delete_confirm": "您確定要刪除該群組嗎?", + "name_label": "群組名稱", + "name_placeholder": "例如,團隊、家庭", + "name_required": "群組名稱為必填欄位", + "save_failed": "儲存群組失敗", + "members_label": "成員", + "search_members": "搜尋要新增的聯絡人…", + "no_members": "該群組中沒有成員", + "member_count": "{count, plural, =0 {無成員} one {1 位成員} other {# 位成員}}", + "send_email": "傳送郵件給群組", + "send_email_to": "收件者", + "send_email_cc": "副本", + "send_email_bcc": "密件副本", + "no_member_emails": "此群組沒有具有電子郵件地址的成員。" + }, + "import": { + "title": "匯入聯絡人", + "drop_hint": "點擊以選取 vCard 檔案", + "file_types": ".vcf 或 .vcard 檔案", + "no_contacts": "檔案中找不到聯絡人", + "parse_error": "無法解析 vCard 檔案", + "found": "{count, plural, one {找到 1 位聯絡人} other {找到 # 位聯絡人}}", + "duplicate": "重複", + "select_all": "全部選取", + "deselect_all": "全部取消選取", + "selected": "{count, plural, one {已選取 1 項} other {已選取 # 項}}", + "import_button": "匯入", + "importing": "正在匯入…", + "success": "{count, plural, one {已匯入 1 位聯絡人} other {已匯入 # 位聯絡人}}", + "failed": "匯入失敗", + "close": "關閉", + "file_too_large": "檔案太大(最大 5 MB)" + }, + "export": { + "title": "匯出聯絡人", + "success": "{count, plural, one {已匯出 1 位聯絡人} other {已匯出 # 位聯絡人}}" + }, + "bulk": { + "selected": "{count, plural, one {已選取 1 項} other {已選取 # 項}}", + "select_all": "全部選取", + "delete": "刪除", + "delete_confirm_title": "刪除聯絡人", + "delete_confirm": "刪除{count, plural, one {1 位聯絡人} other {# 位聯絡人}}?", + "deleted": "{count, plural, one {已刪除 1 位聯絡人} other {已刪除 # 位聯絡人}}", + "add_to_group": "新增至群組", + "choose_group": "選擇群組", + "adding_contacts": "正在新增{count, plural, one {1 位聯絡人} other {# 位聯絡人}}", + "added_to_group": "聯絡人已新增至群組", + "export": "匯出", + "clear": "清除選取" + }, + "toast": { + "created": "已建立聯絡人", + "updated": "聯絡人已更新", + "deleted": "聯絡人已刪除", + "error_create": "建立聯絡人失敗", + "error_update": "無法更新聯絡人", + "error_delete": "刪除聯絡人失敗" + }, + "context_menu": { + "open": "開啟", + "edit": "編輯", + "send_email": "傳送郵件", + "add_to_group": "新增至群組", + "export_vcard": "匯出為 vCard", + "delete": "刪除", + "call": "撥打電話", + "duplicate": "建立副本", + "print": "列印" + }, + "filters": { + "toggle": "篩選", + "select": "選取", + "clear": "清除", + "close": "關閉", + "title": "進階篩選", + "organization": "公司", + "organization_placeholder": "例如 Acme", + "job_title": "職稱", + "job_title_placeholder": "例如 設計師", + "location": "所在地", + "location_placeholder": "城市或國家", + "email_domain": "電子郵件網域", + "email_domain_placeholder": "example.com", + "birthday_month": "生日月份", + "any_month": "任何月份", + "has_email": "有電子郵件地址", + "has_phone": "有電話", + "has_photo": "有照片", + "sort_by": "排序方式", + "sort_first_name": "名", + "sort_last_name": "姓" + } + }, + "calendar": { + "title": "行事曆", + "back_to_email": "回到郵件", + "back_to_month": "回到月檢視", + "my_calendars": "行事曆", + "birthday_calendar": "生日", + "mini_calendar_change": "點選更改月份", + "views": { + "month": "月", + "week": "週", + "day": "天", + "agenda": "議程", + "today": "今天", + "month_hint": "月 (m)", + "week_hint": "週 (w)", + "day_hint": "天 (d)", + "agenda_hint": "議程 (a)", + "tasks": "任務", + "tasks_hint": "任務 (k)" + }, + "events": { + "hidden_before": "{count, plural, other {較早的活動 # 個}}", + "hidden_after": "{count, plural, other {較晚的活動 # 個}}", + "show_all_hours": "顯示所有時段", + "show_display_hours": "僅顯示 {start} – {end}", + "create": "建立活動", + "edit": "編輯活動", + "delete": "刪除活動", + "details": "活動詳細資料", + "no_events": "沒有活動", + "all_day": "全天", + "more": "另有 {count} 個", + "show_more": "顯示更多", + "show_less": "顯示較少", + "no_title": "(無標題)", + "resize": "調整活動大小", + "duplicate": "建立副本", + "today_header": "今天", + "tomorrow_header": "明天", + "agenda_show_earlier": "顯示更早的行程", + "agenda_loading": "載入中…", + "agenda_range_start": "顯示 {date} 起的行程", + "agenda_range_end": "顯示至 {date} 的行程", + "export_ics": "匯出為 .ics", + "copy_title": "複製標題", + "copy_link": "複製會議連結", + "new_event": "新增活動", + "new_all_day_event": "新增全天活動", + "new_task": "新增任務", + "go_to_today": "前往今天" + }, + "detail": { + "add_note": "新增備註…", + "save_note": "儲存", + "note_saved": "已新增備註", + "open_link": "開啟連結", + "copy_location": "複製地點", + "meeting_link": "會議連結", + "tentative": "暫定", + "cancelled": "已取消", + "delete_confirm": "刪除這個活動嗎?" + }, + "form": { + "title": "標題", + "description": "描述", + "location": "地點", + "meeting_link": "會議連結", + "start_date": "開始日期", + "end_date": "結束日期", + "start_time": "開始時間", + "end_time": "結束時間", + "all_day_event": "全天活動", + "calendar_select": "行事曆", + "save": "儲存", + "cancel": "取消", + "delete_confirm": "您確定要刪除此活動嗎?", + "color": "顏色" + }, + "participants": { + "availability": { + "title": "空閒狀態", + "free": "有空", + "busy": "忙碌", + "tentative": "暫定", + "unknown": "不是此伺服器上的使用者", + "checking": "檢查中…" + }, + "title": "參與者", + "add": "新增參與者", + "organizer": "主辦人", + "attendee": "與會者", + "accepted": "已接受", + "declined": "已拒絕", + "tentative": "暫定", + "needs_action": "待處理", + "remove": "移除", + "edit": "編輯", + "email_placeholder": "新增電子郵件地址或搜尋聯絡人", + "send_invitations": "向參與者傳送邀請", + "status_summary": "{accepted} 已接受,{pending} 待處理", + "invited_by": "來自 {name} 的邀請", + "respond_below": "使用下方按鈕回覆", + "rsvp_label": "您的回覆", + "cancel_notification": "參與者將收到取消通知", + "you_organizer": "您是主辦人", + "you_attendee": "您是與會者", + "no_participants": "沒有參與者", + "count": "{count, plural, one {# 位參與者} other {# 位參與者}}" + }, + "recurrence": { + "title": "重複", + "none": "不重複", + "daily": "每天", + "weekly": "每週", + "monthly": "每月", + "yearly": "每年", + "every_n_days": "每 {count} 天", + "every_n_weeks": "每 {count} 週", + "every_n_months": "每 {count} 個月", + "until": "直到", + "occurrences": "{count} 次", + "custom": "自訂…", + "edit_custom": "編輯自訂重複規則", + "every_n_years": "每 {count} 年", + "on_days": "{days}", + "on_day_n": "{day}日", + "on_the_nth": "{nth}{day}", + "in_month": "{month}", + "nth_1": "第一個", + "nth_2": "第二個", + "nth_3": "第三個", + "nth_4": "第四個", + "nth_last": "最後一個", + "editor_freq_day": "天", + "editor_freq_week": "週", + "editor_freq_month": "月", + "editor_freq_year": "年", + "editor_repeats_on": "重複於", + "editor_every": "每", + "editor_unit_days": "天", + "editor_unit_weeks": "週", + "editor_unit_months": "個月", + "editor_unit_years": "年", + "editor_on_day": "指定日期", + "editor_on_the": "指定星期", + "editor_in": "月份", + "editor_ends": "結束", + "editor_never": "永不", + "editor_ends_on": "於", + "editor_ends_after": "經過", + "editor_occurrences": "次後" + }, + "recurrence_scope": { + "edit_title": "編輯重複活動", + "delete_title": "刪除重複活動", + "description": "這是重複活動。您要修改哪些活動?", + "this_event": "僅限本次活動", + "this_and_future": "本次及後續活動", + "all_events": "所有活動", + "cancel": "取消", + "save": "儲存", + "delete": "刪除", + "rsvp_title": "回覆重複活動", + "rsvp_description": "這是重複活動。您的回覆適用於哪些活動?", + "respond": "回覆" + }, + "alerts": { + "title": "提醒", + "none": "無提醒", + "at_time": "活動舉行時", + "minutes_before": "{count, plural, one {提前 # 分鐘} other {提前 # 分鐘}}", + "hours_before": "{count, plural, one {提前 # 小時} other {提前 # 小時}}", + "days_before": "{count, plural, one {提前 # 天} other {提前 # 天}}", + "weeks_before": "{count, plural, one {提前 # 週} other {提前 # 週}}", + "unit_minutes_before": "分鐘前", + "unit_hours_before": "小時前", + "unit_days_before": "天前", + "unit_weeks_before": "週前", + "add": "新增提醒", + "remove": "移除提醒", + "amount": "提醒數量", + "unit": "提醒單位", + "action": "提醒類型", + "action_display": "通知", + "action_email": "電子郵件", + "by_email": "{label}(電子郵件)", + "kept": "另有 {count} 個無法在此編輯的提醒將會保留", + "other": "另有 {count} 個提醒", + "calendar_defaults": "行事曆預設值", + "customize": "自訂", + "use_defaults": "使用行事曆預設值" + }, + "settings": { + "limit_hours": "限制顯示時段", + "limit_hours_desc": "在日檢視和週檢視中只顯示這些時段。範圍外的活動仍可透過每天頂部和底部的箭頭查看。", + "visible_hours": "顯示時段", + "visible_hours_start": "顯示開始時間", + "visible_hours_end": "顯示結束時間", + "hide_non_working_days": "隱藏非工作日", + "hide_non_working_days_desc": "在週檢視中省略你不工作的日子。這些日子的活動仍會顯示在月、日和行程檢視中。", + "working_days": "工作日", + "title": "行事曆設定", + "default_view": "預設檢視", + "week_starts_on": "一週開始於", + "time_format": "時間格式", + "default_calendar": "預設行事曆", + "default_reminder": "預設提醒", + "time_format_12h": "12 小時制", + "time_format_24h": "24 小時制", + "notifications_enabled": "活動通知", + "notifications_enabled_desc": "顯示即將開始的行事曆活動提醒", + "notification_sound": "通知聲音", + "notification_sound_desc": "播放行事曆提醒聲音", + "invitation_parsing": "解析郵件邀請", + "invitation_parsing_desc": "偵測郵件附件中的行事曆邀請,並顯示行事曆動作", + "show_time_in_month_view": "在月檢視中顯示時間", + "show_time_in_month_view_desc": "在月檢視中顯示活動時間。小螢幕會顯示完整活動項目,而不只是圓點。", + "show_week_numbers": "顯示週數", + "show_week_numbers_desc": "在迷你行事曆中顯示週數", + "calendar_free_scroll": "自由捲動", + "calendar_free_scroll_desc": "連續捲動瀏覽月、週和日,而不是一次只顯示一個時段", + "enable_tasks": "啟用任務", + "enable_tasks_desc": "在行事曆中顯示任務檢視以管理待辦事項", + "show_tasks_on_calendar": "在行事曆上顯示任務", + "show_tasks_on_calendar_desc": "在行事曆的月/週/日檢視中顯示任務", + "hover_preview": "活動游標暫留預覽", + "hover_preview_desc": "游標移至行事曆活動上方時顯示詳細資料浮動視窗", + "hover_preview_instant": "立即", + "hover_preview_delay_500ms": "延遲 0.5 秒", + "hover_preview_delay_1s": "延遲 1 秒", + "hover_preview_delay_2s": "延遲 2 秒", + "hover_preview_off": "停用", + "organizer_identity": "以此身分發送邀請", + "organizer_identity_desc": "邀請他人參加活動時用作主辦者的行事曆地址。", + "organizer_identity_failed": "無法變更主辦者地址", + "show_birthday_calendar": "聯絡人生日行事曆", + "show_birthday_calendar_desc": "顯示包含聯絡人生日的虛擬行事曆" + }, + "days": { + "monday": "週一", + "tuesday": "週二", + "wednesday": "週三", + "thursday": "週四", + "friday": "週五", + "saturday": "週六", + "sunday": "週日", + "mon": "週一", + "tue": "週二", + "wed": "週三", + "thu": "週四", + "fri": "週五", + "sat": "週六", + "sun": "週日" + }, + "months": { + "jan": "1月", + "feb": "2月", + "mar": "3月", + "apr": "4月", + "may": "5月", + "jun": "6月", + "jul": "7月", + "aug": "8月", + "sep": "9月", + "oct": "10月", + "nov": "11月", + "dec": "12月", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "notifications": { + "event_created": "活動已建立", + "event_updated": "活動已更新", + "event_deleted": "活動已刪除", + "calendar_created": "已建立行事曆", + "calendar_deleted": "行事曆已刪除", + "event_move_error": "無法移動活動", + "event_resize_error": "無法調整活動大小", + "alert_title": "即將舉行的活動", + "alert_now": "現在開始", + "alert_in_minutes": "{count} 分鐘後", + "invitation_sent": "已傳送邀請", + "invitations_denied_title": "無法傳送邀請", + "invitations_denied": "伺服器拒絕傳送邀請:{reason}。是否不傳送邀請直接儲存活動?", + "save_without_invitations": "不傳送邀請並儲存", + "rsvp_updated": "回覆已更新", + "rsvp_error": "無法更新回覆", + "event_duplicated": "已建立活動副本", + "event_error": "無法儲存活動", + "task_due": "任務到期", + "event_exported": "活動已匯出", + "title_copied": "標題已複製", + "link_copied": "連結已複製" + }, + "status": { + "loading_calendars": "正在載入行事曆…", + "loading_events": "正在載入活動…" + }, + "quick_create": { + "placeholder": "新活動標題", + "aria_label": "快速建立活動", + "add_title": "新增標題", + "more_options": "更多選項" + }, + "nav_prev": "上一項", + "nav_next": "下一項", + "nav_open_menu": "開啟選單", + "import": { + "title": "匯入行事曆", + "tab_file": "檔案", + "tab_url": "URL", + "select_file": "選取 .ics 檔案", + "drop_file": "或將檔案拖放到這裡", + "supported_formats": "支援 iCalendar (.ics) 檔案", + "url_description": "輸入外部 iCalendar(.ics)資料來源的 URL,以匯入活動。", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "支援 iCalendar(.ics)URL", + "fetch": "取得", + "invalid_url": "請輸入有效的 URL", + "url_fetch_failed": "無法從 URL 取得行事曆", + "parsing": "正在解析行事曆檔案…", + "parsed_events": "找到 {count} 個活動", + "no_events": "檔案中找不到活動", + "select_all": "全部選取", + "deselect_all": "全部取消選取", + "target_calendar": "匯入到行事曆", + "import_button": "匯入選取項目", + "importing": "正在匯入活動…", + "success": "{count} 個活動已成功匯入", + "error": "匯入行事曆失敗", + "file_too_large": "檔案超過 10 MB 上限", + "invalid_format": "行事曆檔案格式無效" + }, + "webcal_action": { + "title": "開啟行事曆連結", + "description": "您要如何使用「{name}」?", + "import_title": "匯入一次", + "import_description": "立即取得活動,並複製到您的其中一個行事曆。", + "subscribe_title": "訂閱", + "subscribe_description": "將此行事曆保留為獨立行事曆並自動同步。", + "cancel": "取消" + }, + "management": { + "title": "行事曆管理", + "description": "建立、重新命名與自訂您的行事曆。在側邊欄的行事曆上按一下滑鼠右鍵,可快速變更顏色。", + "name": "名稱", + "name_placeholder": "行事曆名稱", + "color": "顏色", + "change_color": "變更顏色", + "random_color": "隨機新顏色", + "add_calendar": "新增行事曆", + "edit": "編輯", + "delete": "刪除", + "save": "儲存", + "create": "建立", + "cancel": "取消", + "default": "預設", + "confirm_delete": "要刪除「{name}」嗎?此行事曆中的所有活動都會移除。", + "confirm_clear": "要清除「{name}」中的所有活動嗎?此動作無法復原。", + "set_default": "設為預設", + "default_updated": "預設行事曆已更新", + "error_default": "無法設定預設行事曆", + "clear_events": "清除活動", + "events_cleared": "{count} 個活動已清除", + "error_clear": "無法清除行事曆活動", + "calendar_created": "已建立行事曆", + "calendar_updated": "行事曆已更新", + "calendar_deleted": "行事曆已刪除", + "color_updated": "行事曆顏色已更新", + "error_create": "建立行事曆失敗", + "error_update": "更新行事曆失敗", + "error_delete": "刪除行事曆失敗", + "caldav_url": "CalDAV URL", + "copy_url": "複製 CalDAV URL", + "url_copied": "CalDAV URL 已複製到剪貼簿", + "share": "分享行事曆", + "new_event_in_calendar": "在此行事曆中新增活動", + "shared_with": "已與 {names} 共用", + "shared_with_count": "{count, plural, other {已與 # 人共用}}", + "move_up": "上移", + "move_down": "下移", + "kind": "內容", + "kind_events": "活動", + "kind_tasks": "任務", + "kind_both": "活動與任務", + "kind_hint": "決定透過 CalDAV 同步時,哪些應用程式可看到此行事曆。此設定之後無法變更。", + "default_alerts": "預設提醒", + "default_alerts_title": "「{name}」的預設提醒", + "default_alerts_hint": "此行事曆中的新活動會套用這些提醒,除非在活動中變更。在此變更不會影響現有活動。電子郵件提醒由郵件伺服器寄送。", + "default_alerts_timed": "有指定時間的活動", + "default_alerts_all_day": "全天活動", + "default_alerts_updated": "預設提醒已更新" + }, + "subscription": { + "title": "iCal 訂閱", + "section_title": "iCal 訂閱", + "description": "訂閱外部 iCalendar 資料來源。活動會自動同步到獨立的行事曆。支援 https:// 與 webcal:// URL。", + "url_label": "行事曆 URL", + "url_placeholder": "https://example.com/calendar.ics 或 webcal://...", + "name_label": "行事曆名稱", + "name_placeholder": "例如國定假日", + "color_label": "顏色", + "refresh_interval": "重新整理間隔", + "interval_15": "每 15 分鐘", + "interval_30": "每 30 分鐘", + "interval_60": "每小時", + "interval_360": "每 6 小時", + "interval_1440": "每天", + "subscribe": "訂閱", + "subscribing": "正在訂閱…", + "save": "儲存變更", + "saving": "正在儲存…", + "edit": "編輯", + "edit_title": "編輯訂閱", + "updated": "已更新「{name}」", + "update_error": "更新訂閱失敗", + "invalid_url": "請輸入有效的 URL", + "success": "已訂閱「{name}」", + "error": "新增訂閱失敗", + "refresh": "立即重新整理", + "refresh_success": "訂閱已重新整理", + "refresh_error": "重新整理訂閱失敗", + "unsubscribe": "取消訂閱", + "confirm_delete": "要取消訂閱「{name}」嗎?此行事曆及其所有活動都會移除。", + "deleted": "已移除訂閱", + "delete_error": "無法移除訂閱", + "last_refreshed": "最後更新:{time}" + }, + "tasks": { + "label": "任務", + "no_tasks": "沒有任務", + "no_title": "(無標題)", + "mark_complete": "標示為完成", + "mark_incomplete": "標示為未完成", + "filter_all": "全部", + "filter_pending": "待處理", + "filter_completed": "已完成", + "filter_overdue": "逾期", + "show_completed": "顯示已完成", + "create": "新增任務", + "edit": "編輯任務", + "title_placeholder": "任務標題", + "description_placeholder": "新增描述…", + "due_date": "到期日", + "include_time": "包含時間", + "priority": "優先順序", + "priority_none": "無", + "priority_high": "高", + "priority_medium": "中", + "priority_low": "低", + "progress": "狀態", + "progress_needs_action": "待處理", + "progress_in_process": "進行中", + "progress_completed": "已完成", + "progress_cancelled": "已取消", + "calendar": "行事曆", + "alert": "提醒", + "alert_none": "無", + "alert_at_time": "在到期日時", + "alert_5min": "5 分鐘前", + "alert_15min": "15 分鐘前", + "alert_30min": "30 分鐘前", + "alert_1hr": "1 小時前", + "alert_1day": "1 天前", + "delete": "刪除", + "cancel": "取消", + "save": "儲存", + "quick_add_placeholder": "新增任務…", + "due_today": "今天", + "due_tomorrow": "明天", + "overdue": "逾期" + }, + "toolbar": { + "more": "更多動作", + "select_view": "切換檢視,目前為{view}" + }, + "other_calendars": "其他日曆", + "calendar_options": "{name} 的選項" + }, + "calendar_event_notifications": { + "someone": "有人", + "invited": "{name} 邀請您參加「{title}」", + "updated": "{name} 更新了「{title}」", + "cancelled": "{name} 取消了「{title}」", + "untitled": "未命名活動" + }, + "share_notifications": { + "someone": "有人", + "shared": "{name} 與您共用了{kind}「{object}」", + "changed": "{name} 變更了您對{kind}「{object}」的存取權限", + "revoked": "{name} 移除了您對{kind}「{object}」的存取權限", + "object": { + "folder": "資料夾", + "calendar": "行事曆", + "address_book": "通訊錄", + "files": "檔案資料夾" + } + }, + "sharing": { + "title": "分享「{name}」", + "description": "授予此伺服器上的其他使用者或群組存取權。變更會立即生效。", + "no_shares": "尚未與任何人分享。", + "add_person": "新增使用者或群組", + "search_placeholder": "依姓名或電子郵件地址搜尋…", + "loading_principals": "正在載入使用者…", + "no_principals": "找不到其他使用者或群組。", + "no_match": "找不到相符項目。", + "remove": "移除存取權", + "group": "群組", + "share_added": "已授予存取權", + "share_updated": "已更新存取權", + "share_removed": "已移除存取權", + "share_failed": "無法更新分享設定", + "preset": { + "freeBusy": "僅限空閒/忙碌", + "read": "唯讀", + "readWrite": "讀取與寫入", + "manager": "管理員", + "custom": "自訂" + } + }, + "advanced_search": { + "title": "進階搜尋", + "from": "寄件者", + "from_placeholder": "寄件者電子郵件地址或姓名", + "to": "收件者", + "to_placeholder": "收件者電子郵件地址或姓名", + "subject": "主題", + "subject_placeholder": "主題包含…", + "body": "內文", + "body_placeholder": "內文包含…", + "folder": "信件匣", + "all_folders": "所有信件匣", + "all_folders_except_spam_trash": "除垃圾郵件和垃圾桶外的所有信件匣", + "has_attachment": "附件", + "date_after": "後", + "date_before": "前", + "size_min": "大於 (KB)", + "size_max": "小於 (KB)", + "starred": "已加星號", + "unread": "未讀", + "read": "已讀", + "yes": "是", + "no": "否", + "clear": "清除", + "clear_all": "全部清除", + "filters_active": "{count} 個篩選條件", + "filters_active_plural": "{count} 個篩選條件", + "toggle_filters": "更多", + "search_hint": "使用進階篩選條件進行精確搜尋", + "advanced_filters_tooltip": "進階搜尋篩選條件", + "results_found": "{count, plural, =0 {找不到結果} one {找到 # 個結果} other {找到 # 個結果}}", + "results_found_more": "找到 {count}+ 個結果", + "suggestions_recent": "最近搜尋", + "suggestions_people": "聯絡人", + "suggestions_from": "搜尋來自 {address} 的郵件", + "suggestions_to": "搜尋寄給 {address} 的郵件", + "suggestions_remove_recent": "從最近搜尋中移除", + "has_words": "包含字詞", + "size": "大小", + "date_within": "日期", + "search_in": "搜尋範圍", + "has_attachment_check": "包含附件", + "search": "搜尋" + }, + "welcome": { + "title": "歡迎使用您的信件匣", + "tip_compose": "按 c 撰寫新郵件", + "tip_shortcuts": "按 ? 檢視所有鍵盤快速鍵", + "tip_sidebar": "可在側邊欄選單找到行事曆、聯絡人與設定", + "tip_settings": "在「設定」中自訂您的使用體驗", + "tip_compose_touch": "點按鉛筆按鈕撰寫新郵件", + "tip_sidebar_touch": "行事曆、聯絡人和設定位於底部列", + "got_it": "了解", + "settings": "設定", + "dismiss": "關閉", + "start_tour": "開始功能導覽" + }, + "demo_welcome": { + "title": "歡迎使用 Bulwark Mail", + "description": "直接在瀏覽器中探索功能完整的網頁郵件用戶端。所有資料都保留在您的裝置上,請自由測試各項功能。", + "feature_email": "閱讀與撰寫郵件", + "feature_organize": "標籤、星號與信件匣", + "feature_shortcuts": "鍵盤快速鍵", + "feature_privacy": "100% 私密示範", + "hint": "點擊左側任一郵件即可開始,或進行下方的功能導覽。" + }, + "files": { + "office_edit": "編輯文件", + "office_loading": "正在開啟文件編輯器…", + "office_error": "無法開啟文件編輯器。", + "office_close": "關閉編輯器", + "open_in_new_tab": "在新分頁開啟", + "title": "檔案", + "search_placeholder": "搜尋檔案…", + "empty_state_title": "尚無檔案", + "upload_files": "上傳檔案", + "new_folder": "新增資料夾", + "new_folder_name": "資料夾名稱", + "rename": "重新命名", + "rename_title": "重新命名", + "new_name": "新名稱", + "name_forbidden_chars": "伺服器不允許名稱中包含這些字元:{chars}", + "name_reserved": "此名稱已被伺服器保留。", + "delete": "刪除", + "delete_confirm_title": "刪除項目", + "delete_confirm_message": "您確定要刪除「{name}」嗎?此動作無法復原。", + "download": "下載", + "name": "名稱", + "size": "大小", + "modified": "修改日期", + "type": "類型", + "folder": "資料夾", + "file": "檔案", + "no_accounts": "沒有已連線的帳號。", + "uploading": "正在上傳…", + "upload_success": "{count, plural, one {已上傳 1 個檔案} other {已上傳 # 個檔案}}", + "upload_canceled": "已取消上傳", + "upload_canceled_partial": "取消前已上傳 {count, plural, other {# 個檔案}}", + "upload_error": "上傳檔案失敗", + "create_folder_success": "資料夾已建立", + "create_folder_error": "建立資料夾失敗", + "delete_success": "刪除成功", + "delete_error": "刪除失敗", + "rename_success": "重新命名成功", + "rename_error": "重新命名失敗", + "download_error": "下載失敗", + "not_available": "此伺服器無法使用檔案儲存功能", + "cancel": "取消", + "create": "建立", + "save": "儲存", + "no_results": "找不到符合搜尋條件的檔案", + "batch_delete_confirm_message": "您確定要刪除{count, plural, one {1 個項目} other {# 個項目}}嗎?此動作無法復原。", + "batch_delete_success": "{count, plural, one {已刪除 1 個項目} other {已刪除 # 個項目}}", + "grid_view": "網格檢視", + "list_view": "清單檢視", + "details": "詳細資料", + "path": "路徑", + "preview": "預覽", + "preview_error": "載入預覽失敗", + "cut": "剪下", + "copy": "複製", + "paste": "貼上", + "move_success": "{count, plural, one {已移動 1 個項目} other {已移動 # 個項目}}", + "move_error": "移動失敗", + "paste_success": "貼上成功", + "paste_error": "貼上失敗", + "new_text_file": "新增文字檔案", + "file_name": "檔名", + "retry": "重試", + "refresh": "重新整理", + "duplicate": "建立副本", + "duplicate_success": "已成功建立副本", + "duplicate_error": "無法建立副本", + "share": "分享", + "shared": "已分享", + "shared_with_me": "與我共用", + "shared_by": "由 {name} 分享", + "create_file_success": "檔案已建立", + "create_file_error": "建立檔案失敗", + "favorites": "我的最愛", + "recent": "最近", + "show_in_folder": "在資料夾中顯示", + "remove_from_recent": "從最近項目中移除", + "upload_folder": "上傳資料夾", + "file_too_large": "「{name}」超過檔案大小上限({max})", + "undo": "復原", + "undo_success": "已復原動作", + "undo_error": "無法復原", + "toolbar": "檔案動作", + "open_folder_tree": "開啟資料夾樹", + "file_list": "檔案與資料夾", + "context_menu": "動作", + "settings_ascending": "遞增", + "settings_descending": "遞減", + "disabled_title": "檔案功能已被您的管理員停用", + "disabled_description": "透過 WebDAV 上傳大檔案可能導致 Stalwart/RocksDB 不穩定,包括記憶體不足當機及無法回收的磁碟空間。刪除檔案後,blob 儲存空間可能不會立即清除。本功能不建議用於正式環境。", + "migration_title": "正在更新您的檔案…", + "migration_description": "正在將資料夾與檔案整理成正確結構。這只會執行一次。", + "new": "新增", + "my_files": "我的檔案", + "folders": "資料夾", + "files_heading": "檔案", + "open": "開啟", + "star": "加入我的最愛", + "unstar": "從我的最愛移除", + "expand_folder": "展開 {name}", + "collapse_folder": "收合 {name}", + "sidebar_hint": "將資料夾加入我的最愛或開啟檔案後,就能在這裡找到它們。", + "breadcrumbs": "資料夾路徑", + "show_path": "顯示路徑中隱藏的資料夾", + "selection_actions": "所選項目的動作", + "selected_count": "{count, plural, other {已選取 # 個}}", + "clear_selection": "取消選取", + "select_all": "全選", + "select_item": "選取 {name}", + "more_actions": "{name} 的更多動作", + "paste_here": "貼到此資料夾", + "paste_count": "{count, plural, other {貼上 # 個項目}}", + "sort_by": "排序方式", + "view_mode": "版面配置", + "clear_search": "清除搜尋", + "no_results_for": "此資料夾中沒有符合「{query}」的項目", + "download_count": "{count, plural, other {下載 # 個檔案}}", + "delete_count": "{count, plural, other {刪除 # 個項目}}", + "close_details": "關閉詳細資料", + "file_kind": "{ext} 檔案", + "folder_summary": "{folders, plural, =0 {沒有資料夾} other {# 個資料夾}},{files, plural, =0 {沒有檔案} other {# 個檔案}}", + "details_hint": "選取檔案或資料夾以檢視詳細資料。", + "sharing": "共用", + "shared_with_count": "{count, plural, other {已與 # 人共用}}", + "only_you": "只有您", + "empty_folder_title": "此資料夾是空的", + "empty_folder_description": "將檔案拖放到這裡,或從裝置上傳。", + "empty_folder_read_only": "您無法在此資料夾中新增檔案。", + "choose_account": "選擇帳號", + "drop_to_upload": "放開以上傳到 {folder}", + "uploading_count": "正在上傳 {current}/{total}", + "dismiss": "關閉", + "zoom_in": "放大", + "zoom_out": "縮小", + "zoom_reset": "符合螢幕大小", + "rotate": "旋轉", + "close_preview": "關閉" + }, + "smime": { + "your_certificates": "您的憑證", + "your_certificates_desc": "匯入與管理用於郵件簽章與加密的 S/MIME 憑證", + "recipient_certificates": "收件者憑證", + "recipient_certificates_desc": "收件者的公開憑證,用於加密寄給他們的郵件", + "identity_bindings": "寄件身分金鑰繫結", + "identity_bindings_desc": "將 S/MIME 憑證繫結至您的寄件身分", + "defaults_title": "預設值", + "defaults_desc": "設定預設的簽章與加密行為", + "import_pkcs12": "匯入 PKCS#12(.p12/.pfx)", + "import_public_cert": "匯入憑證", + "no_certificates": "尚未匯入憑證", + "no_recipient_certs": "無收件者憑證", + "expires": "到期日", + "expired": "已到期", + "bound_to": "繫結至", + "no_key_bound": "無", + "lock": "鎖定金鑰", + "unlock": "解鎖金鑰", + "details": "檢視詳細資料", + "delete": "刪除", + "encrypt_by_default": "預設加密", + "encrypt_by_default_desc": "當所有收件者都有憑證時自動加密郵件", + "remember_unlocked": "記住已解鎖的金鑰", + "remember_unlocked_desc": "在目前的瀏覽器工作階段保持金鑰解鎖", + "sign_default_for": "為以下項目預設簽章", + "enter_p12_passphrase": "輸入 PKCS#12 密碼片語", + "p12_passphrase_desc": "輸入保護此憑證檔案的密碼片語", + "enter_storage_passphrase": "設定儲存密碼片語", + "storage_passphrase_desc": "選擇一組密碼片語,保護此金鑰在瀏覽器中的靜態儲存", + "next": "下一步", + "import": "匯入", + "unlock_key": "解鎖金鑰", + "unlock_key_desc": "輸入儲存密碼片語,以解鎖此金鑰進行簽章或解密", + "passphrase_placeholder": "輸入密碼片語", + "confirm_passphrase_placeholder": "確認密碼片語", + "passphrase_mismatch": "兩次輸入的密碼片語不相符", + "cancel": "取消", + "processing": "處理中…", + "close": "關閉", + "certificate_details": "憑證詳細資料", + "cert_subject": "主體", + "cert_issuer": "簽發者", + "cert_email": "電子郵件地址", + "cert_serial": "序號", + "cert_validity": "有效期限", + "cert_fingerprint": "指紋(SHA-256)", + "cert_algorithm": "演算法", + "cert_capabilities": "功能", + "cert_source": "來源", + "cert_expired": "該憑證已過期", + "cert_not_yet_valid": "該憑證尚未生效", + "cap_sign": "簽章", + "cap_encrypt": "加密", + "cap_none": "無", + "show_passphrase": "顯示密碼片語", + "hide_passphrase": "隱藏密碼片語", + "sign_toggle": "簽章", + "encrypt_toggle": "加密", + "missing_recipient_certs": "缺少以下憑證:{emails}", + "missing_sender_cert": "沒有憑證繫結至此寄件身分", + "status_encrypted_ok": "此郵件已加密", + "status_encrypted_no_key": "此郵件已加密,但找不到相符的金鑰", + "status_encrypted_failed": "無法解密此郵件", + "status_signed_valid": "簽章已驗證", + "status_signed_invalid": "簽章驗證失敗", + "status_signed_expired_cert": "使用已到期的憑證簽章", + "status_signed_self_signed": "簽章有效,但憑證為自我簽署(不受信任)", + "status_signed_mismatch": "簽章有效,但簽署者與寄件者不相符", + "status_unsupported": "不支援的 S/MIME 格式", + "auto_import_signer_certs": "自動匯入簽署者憑證", + "auto_import_signer_certs_desc": "自動儲存已驗證簽章郵件中的憑證,以供日後加密郵件。", + "export": "匯出", + "enter_export_passphrase": "設定匯出密碼片語", + "export_passphrase_desc": "選擇一組密碼片語以保護匯出的 PKCS#12 檔案", + "export_storage_desc": "輸入儲存密碼片語,以解密金鑰並匯出", + "incorrect_passphrase": "密碼片語不正確" + }, + "tour": { + "step_counter": "第 {current} 步,共 {total} 步", + "skip": "跳過導覽", + "back": "上一步", + "next": "下一步", + "finish": "完成", + "take_a_tour": "進行介面功能導覽", + "restart_title": "新手導覽", + "restart_desc": "重新進行介面的逐步功能導覽", + "restart_button": "重新開始導覽", + "show_on_new_devices_title": "在新裝置上顯示", + "show_on_new_devices_desc": "首次在新裝置登入時,重新顯示歡迎橫幅與功能導覽,即使您已在其他裝置完成", + "sidebar_title": "您的信件匣", + "sidebar_desc": "這裡是信件匣側邊欄。點擊任一信件匣即可檢視其中的郵件。您可以建立信件匣、在信件匣之間拖曳郵件,並快速查看未讀數量。", + "compose_title": "撰寫郵件", + "compose_desc": "點擊這裡即可撰寫新郵件。您可以新增收件者、附件,並使用富文字格式。", + "search_title": "搜尋郵件", + "search_desc": "依寄件者、主題或內容搜尋。點擊篩選圖示,可使用日期範圍、附件與已加星號等進階選項。", + "email_list_title": "郵件清單", + "email_list_desc": "郵件會顯示在這裡。點擊一封郵件即可在右側閱讀。使用核取方塊可選取多封郵件,再批次移動、刪除或加上標籤。", + "email_viewer_title": "閱讀區域", + "email_viewer_desc": "選取的郵件會在這裡開啟。使用工具列按鈕回覆、轉寄、封存或刪除。您也可以為郵件加上星號或彩色標籤。", + "keywords_title": "顏色標籤", + "keywords_desc": "使用彩色標籤整理郵件。將郵件拖曳到標籤即可加上標籤,或在郵件上按一下滑鼠右鍵來指派標籤。", + "calendar_title": "行事曆", + "calendar_desc": "切換至行事曆以管理活動。您可以建立活動、設定提醒,並檢視日、週或月版面。", + "contacts_title": "聯絡人", + "contacts_desc": "這裡是您的通訊錄。您可以匯入聯絡人、建立群組,並點擊任一聯絡人以檢視完整詳細資料。", + "settings_title": "設定", + "settings_desc": "在這裡自訂主題、介面密度、簽名檔、篩選規則、鍵盤快速鍵、行事曆預設值等各項設定。", + "shortcuts_title": "鍵盤快速鍵", + "shortcuts_desc": "進階使用者會喜歡這項功能。隨時按下 ? 即可檢視所有可用的快速鍵。您不需使用滑鼠,就能導覽、撰寫與管理郵件。", + "compose_open_title": "郵件編輯器", + "compose_open_desc": "這裡是郵件撰寫器。新增收件者、撰寫內文、附加檔案,並使用富文字格式。您也可以儲存草稿與使用範本。", + "calendar_view_title": "您的行事曆", + "calendar_view_desc": "這裡是包含範例活動的行事曆。您可以使用工具列切換日、週、月或議程檢視。", + "create_event_title": "建立活動", + "create_event_desc": "點擊此按鈕即可建立新的行事曆活動。您可以設定標題、日期、時間並新增參與者。", + "event_modal_title": "活動詳細資料", + "event_modal_desc": "這裡是活動表單。填寫標題、選擇日期與時間,並新增地點或參與者。完成後按下儲存,或關閉並繼續其他操作。", + "contacts_list_title": "聯絡人清單", + "contacts_list_desc": "這裡顯示您的聯絡人。點擊任一聯絡人即可在右側檢視完整詳細資料。您也可以建立聯絡人、匯入 vCard 或將聯絡人整理成群組。", + "settings_tabs_title": "設定選單", + "settings_tabs_desc": "這裡列出所有設定類別。您可以自訂外觀、管理寄件身分、設定郵件篩選規則、設定行事曆等。", + "files_title": "檔案儲存", + "files_desc": "檔案瀏覽器可讓您上傳、整理與分享檔案,就像內建在郵件中的個人雲端硬碟。", + "demo_banner_title": "示範控制", + "demo_banner_desc": "目前處於示範模式,所有資料都保留在瀏覽器中。隨時點擊「重設示範」即可用全新的範例資料重新開始。", + "quota_title": "儲存使用情況", + "quota_desc": "在這裡查看信箱的儲存空間使用量。使用的空間越多,圓形指示器就會填得越滿。" + }, + "unified_mailbox": { + "search_unavailable": "整合檢視中無法使用搜尋" + }, + "quote_header": { + "reply_line": "在 {date},{from} 寫道:", + "forwarded_separator": "---------- 轉寄郵件 ----------", + "from_label": "寄件者", + "date_label": "日期", + "subject_label": "主題" + }, + "pwa_install": { + "title": "安裝 {appName}", + "description": "安裝我們的應用程式,以便快速存取並支援離線使用。", + "not_now": "暫不", + "install": "安裝", + "dont_remind": "不再提醒", + "dismiss_aria": "關閉安裝提示" + }, + "deep_link": { + "message_not_found": "已無法使用此郵件。", + "thread_not_found": "已無法使用此對話串。", + "folder_not_found": "已無法使用此信件匣。", + "event_not_found": "已無法使用此活動。", + "file_not_found": "已無法使用此檔案。", + "account_unavailable": "請登入該帳號以開啟此連結。", + "copy_message": "複製郵件連結", + "copy_conversation": "複製對話串連結", + "copy_event": "複製活動連結", + "copied": "連結已複製", + "copy_failed": "無法複製連結" + }, + "plugins": { + "oauth_callback": { + "completing": "正在完成授權…", + "completing_message": "請稍候,正在建立連線。", + "received_title": "已收到授權", + "received_message": "您可以返回開始連線的分頁。", + "failed_title": "授權失敗", + "denied": "授權被拒絕。", + "failed_detail": "授權失敗:{error}", + "missing_params": "缺少授權碼或狀態。", + "delivery_failed": "無法將授權結果傳遞至應用程式分頁。請關閉此視窗並再試一次。" + }, + "settings": { + "title": "外掛程式", + "description": "由管理員新增的外掛程式。您可以為自己的帳號開啟或關閉。", + "description_uploads": "外掛程式可為網頁郵件新增功能。您可以為自己的帳號開啟或關閉,也可以自行安裝。", + "description_demo": "示範模式無法使用外掛程式。", + "install_button": "安裝外掛程式", + "install_hint": "請選擇外掛程式 ZIP 檔案。", + "install_hint_approval": "請選擇外掛程式 ZIP 檔案。需經管理員核准後才能開啟。", + "install_uploads_off": "管理員不允許安裝外掛程式。", + "install_managed_id": "管理員已提供同名的外掛程式,因此無法取代。", + "install_invalid": "此檔案不是可安裝的外掛程式:{details}", + "toast_installed": "外掛程式已安裝。請在下方開啟。", + "toast_installed_pending": "外掛程式已安裝,並已送交管理員核准。", + "toast_enabled": "{name} 已開啟", + "toast_disabled": "{name} 已關閉", + "toast_removed": "{name} 已移除", + "toast_forced": "管理員要求 {name} 保持開啟", + "toast_approval_requested": "{name} 正在等待管理員核准", + "empty_title": "尚無外掛程式", + "empty_admin": "管理員尚未新增任何外掛程式。", + "empty_uploads": "從 ZIP 檔案安裝外掛程式即可開始使用。", + "empty_demo": "外掛程式需要真實帳號,因此示範模式中沒有外掛程式。", + "syncing": "正在與伺服器核對外掛程式…", + "status_installed": "已關閉", + "status_enabled": "啟動中", + "status_running": "已開啟", + "status_disabled": "已關閉", + "status_error": "發生問題", + "badge_forced": "必要", + "badge_managed": "由管理員提供", + "badge_yours": "由您安裝", + "badge_awaiting": "等待核准", + "toggle_label": "開啟或關閉 {name}", + "forced_note": "管理員要求使用此外掛程式,因此無法關閉。", + "permissions": "可執行的動作", + "permissions_managed": "可執行的動作(已由管理員核准)", + "settings": "設定", + "remove": "移除", + "remove_title": "要移除 {name} 嗎?", + "remove_message": "外掛程式及其儲存在此瀏覽器中的所有內容都會被刪除。您之後可以再次安裝。", + "remove_confirm": "移除", + "error_user_plugins_off": "管理員已關閉使用者安裝的外掛程式,因此此外掛程式不會執行。", + "error_approval_unavailable": "此外掛程式是在需要核准之前安裝的。請移除後重新安裝,以送交核准。", + "error_approval_denied": "管理員未核准此外掛程式。", + "error_approval_pending": "正在等待管理員核准此外掛程式。", + "error_auto_disabled": "由於反覆出錯已關閉。重新開啟即可再試一次。", + "accounts_heading": "已連線的帳號", + "account_on": "{host} 上的 {label}", + "account_waiting": "{host} 上的 {label}(尚未完成登入)", + "disconnect": "中斷連線", + "disconnect_failed": "無法中斷 {label} 的連線" + }, + "consent": { + "title": "要允許 {name} 執行這些動作嗎?", + "intro": "此外掛程式要求下列權限。只有在您允許後才會取得。", + "high_risk_note": "標示的項目會讓外掛程式存取您的郵件內容,或將資料傳送到其他地方。請只對您信任的外掛程式允許。", + "revoke_hint": "您隨時可以在設定中關閉或移除此外掛程式。", + "deny": "不允許", + "allow": "允許" + }, + "permissions": { + "none": "不需要特殊權限。", + "high_risk": "需留意", + "email_read": { + "title": "讀取您的郵件", + "body": "查看您帳戶中任何郵件的寄件者、收件者、主旨和完整內文,以及您的信件匣。" + }, + "email_write": { + "title": "變更和整理您的郵件", + "body": "為您的郵件新增或移除標籤、旗標和其他關鍵字,在您的信件匣之間移動郵件或將其移至封存,並儲存草稿。" + }, + "email_send": { + "title": "在傳送前變更郵件", + "body": "讀取並變更您撰寫的郵件的收件者、主旨、內文和附件,並可阻止郵件傳送。" + }, + "crypto_full": { + "title": "完整存取此應用程式", + "body": "以與網頁郵件本身相同的權限執行。它可以讀取您所有的郵件和已儲存的金鑰,並代表您執行動作。請只對您完全信任的外掛程式允許。" + }, + "email_raw_send": { + "title": "傳送它自行建立的郵件", + "body": "從您的帳號傳送完整的郵件,例如已簽署或已加密的郵件。" + }, + "email_blob_read": { + "title": "讀取郵件的原始內容", + "body": "讀取您的郵件和附件的原始資料,以及傳送前您附加的每個檔案。" + }, + "email_blob_write": { + "title": "在傳送前取代附件", + "body": "將檔案上傳到您的郵件伺服器,並在郵件傳送前以另一個檔案取代您附加的檔案。" + }, + "email_render_takeover": { + "title": "變更郵件的顯示方式", + "body": "取代您開啟的郵件和郵件清單中的內文,例如用來顯示解密後的內容。" + }, + "calendar_read": { + "title": "讀取您的行事曆", + "body": "查看您的行事曆及其中的活動。" + }, + "calendar_write": { + "title": "變更您的行事曆", + "body": "建立、變更和刪除活動。" + }, + "contacts_read": { + "title": "讀取您的聯絡人", + "body": "搜尋和讀取您的通訊錄。" + }, + "contacts_write": { + "title": "變更您的聯絡人", + "body": "建立、變更和刪除聯絡人及通訊錄。" + }, + "files_read": { + "title": "讀取您的檔案", + "body": "瀏覽和開啟您帳號中儲存的檔案。" + }, + "files_write": { + "title": "變更您的檔案", + "body": "上傳、重新命名、移動和刪除您帳號中的檔案。" + }, + "identity_read": { + "title": "查看您的寄件身分", + "body": "查看您用來傳送郵件的地址及其簽名檔。" + }, + "identity_write": { + "title": "變更您的寄件身分", + "body": "建立、變更和刪除寄件身分及簽名檔。" + }, + "filters_read": { + "title": "讀取您的郵件篩選規則", + "body": "查看用來整理收到郵件的規則。" + }, + "filters_write": { + "title": "變更您的郵件篩選規則", + "body": "在伺服器端篩選規則中新增規則。這些規則可以移動、轉寄或刪除收到的郵件。" + }, + "tasks_read": { + "title": "讀取您的任務", + "body": "查看您的任務清單和任務。" + }, + "tasks_write": { + "title": "變更您的任務", + "body": "建立、變更、完成和刪除任務。" + }, + "templates_read": { + "title": "讀取您的範本", + "body": "查看您儲存的郵件範本。" + }, + "templates_write": { + "title": "變更您的範本", + "body": "建立、變更和刪除郵件範本。" + }, + "smime_read": { + "title": "查看您的 S/MIME 狀態", + "body": "查看您是否有 S/MIME 金鑰和憑證,但無法存取金鑰。" + }, + "vacation_read": { + "title": "查看您的外出自動回覆", + "body": "查看自動回覆是否已開啟及其內容。" + }, + "vacation_write": { + "title": "變更您的外出自動回覆", + "body": "開啟或關閉自動回覆,並變更其內容和日期。" + }, + "settings_read": { + "title": "讀取您的設定", + "body": "查看您的偏好設定和標籤定義。" + }, + "settings_write": { + "title": "變更您的設定", + "body": "新增標籤、調整標籤順序,並變更您的偏好設定。" + }, + "security_read": { + "title": "查看您的帳號安全性", + "body": "查看雙因素驗證和加密是否已開啟,但無法存取機密資訊。" + }, + "auth_observe": { + "title": "得知您何時登入", + "body": "在您登入、切換帳號或工作階段更新時收到通知。" + }, + "auth_emit": { + "title": "將您登出", + "body": "將您從網頁郵件登出。" + }, + "account_read": { + "title": "查看您的帳號", + "body": "查看您新增的帳號及其地址和伺服器。" + }, + "http_post": { + "title": "代表您使用網頁郵件伺服器", + "body": "使用您的登入資訊呼叫下列伺服器端點:" + }, + "http_fetch": { + "title": "連線到其他網站", + "body": "向下列網站傳送要求。外掛程式能讀取的任何內容都可能被傳送到這些網站:" + }, + "admin_config": { + "title": "讀取管理員為其儲存的設定", + "body": "讀取管理員為此外掛程式儲存的設定。" + }, + "ui_observe": { + "title": "追蹤您在應用程式中的位置", + "body": "得知目前開啟的頁面,並對您點選的連結做出回應。" + }, + "ui_toolbar": { + "title": "在工具列新增按鈕", + "body": "在郵件工具列、開啟的郵件和頂端列顯示它自己的按鈕。" + }, + "ui_app_top_banner": { + "title": "在頂端顯示橫幅", + "body": "在應用程式頂端顯示一則通知。" + }, + "ui_email_banner": { + "title": "在郵件上顯示通知", + "body": "在開啟的郵件或對話串各部分的上方、內部和之間顯示它自己的通知和摘要。" + }, + "ui_email_footer": { + "title": "在郵件下方新增內容", + "body": "在開啟的郵件下方顯示它自己的內容。" + }, + "ui_contact_cryptokeys": { + "title": "顯示聯絡人的加密金鑰", + "body": "顯示並管理聯絡人的加密金鑰。" + }, + "ui_email_details": { + "title": "在郵件詳細資料新增面板", + "body": "在開啟的郵件旁邊或下方顯示它自己的面板。" + }, + "ui_download_file": { + "title": "將檔案儲存到您的裝置", + "body": "提供它建立的檔案供下載。" + }, + "ui_message_list_tabs": { + "title": "為收件匣新增分頁", + "body": "將收件匣分成「最新快訊」或「促銷內容」等分頁。" + }, + "ui_composer_toolbar": { + "title": "新增到撰寫視窗", + "body": "在您撰寫郵件時顯示它自己的按鈕、選單項目和區塊。" + }, + "ui_composer_sidebar": { + "title": "在撰寫視窗新增面板", + "body": "在您撰寫的郵件旁邊顯示它自己的面板。" + }, + "ui_sidebar_widget": { + "title": "新增到側邊欄", + "body": "在郵件側邊欄顯示它自己的方塊和區塊。" + }, + "ui_settings_section": { + "title": "新增設定頁面", + "body": "在設定中顯示它自己的頁面。" + }, + "ui_context_menu": { + "title": "新增到郵件選單", + "body": "在您按右鍵點選郵件時開啟的選單中新增項目。" + }, + "ui_navigation_rail": { + "title": "新增到導覽列", + "body": "在導覽列顯示它自己的按鈕。" + }, + "ui_keyboard": { + "title": "新增鍵盤快速鍵", + "body": "回應它定義的鍵盤快速鍵。" + }, + "ui_calendar_action": { + "title": "新增到行事曆畫面", + "body": "在活動上顯示它自己的按鈕和區塊,並在行事曆日期上顯示簡短標籤和圓點。" + }, + "ui_admin_page": { + "title": "新增管理頁面", + "body": "在管理設定中顯示它自己的頁面。" + }, + "app_lifecycle": { + "title": "得知應用程式何時啟動和停止", + "body": "在應用程式啟動、離線或您登出時收到通知。" + }, + "compose_open": { + "title": "為您開啟撰寫視窗", + "body": "開啟一封已填好收件者、內文或附件的新郵件。是否傳送仍由您決定。" + }, + "compose_read": { + "title": "讀取您正在撰寫的郵件", + "body": "在編輯器中使用外掛程式時,讀取您正在撰寫的郵件內文。不包括簽名和引用的郵件。" + }, + "compose_write": { + "title": "修改您正在撰寫的郵件", + "body": "在編輯器中使用外掛程式時,取代或新增您正在撰寫的郵件中的文字。每次修改都可以復原,是否寄出仍由您決定。" + }, + "ui_notifications": { + "title": "顯示通知", + "body": "在您的裝置上顯示通知;如果您未允許通知,則在應用程式內顯示。每則通知都會註明外掛程式名稱。" + }, + "ui_message_list": { + "title": "新增到郵件清單", + "body": "在郵件清單上方顯示它自己的內容,並在郵件上和所選郵件的工具列顯示按鈕。" + }, + "ui_contacts": { + "title": "新增到聯絡人畫面", + "body": "在聯絡人詳細資料中顯示它自己的區塊,並在聯絡人清單中顯示按鈕。" + }, + "ui_tasks": { + "title": "新增到工作畫面", + "body": "在工作詳細資料中顯示它自己的區塊,並在工作清單中顯示按鈕。" + }, + "ui_files": { + "title": "新增到檔案畫面", + "body": "在檔案預覽中顯示它自己的區塊,並在檔案選單中顯示項目。" + }, + "ui_commands": { + "title": "新增指令", + "body": "在指令面板(Ctrl+K)新增它自己的項目。" + }, + "ui_app_page": { + "title": "新增頁面", + "body": "在應用程式中新增它自己的頁面,每個頁面在導覽列都有一個按鈕。" + }, + "http_proxy": { + "title": "使用您連線的帳號", + "body": "使用您或管理員設定的帳號或金鑰,透過 Bulwark 的伺服器傳送要求。外掛程式永遠看不到密碼,但可以在該帳號所屬的伺服器上執行該帳號能做的任何動作。" + } + }, + "dialog": { + "from_plugin": "來自外掛程式 {name}", + "ok": "確定", + "submit": "送出", + "confirm": "確認", + "cancel": "取消", + "close": "關閉", + "turn_off": "關閉 {name}", + "plugin_menu": "{name} 選項", + "password_note": "此表單來自外掛程式 {name},而不是網頁郵件。只有在您信任此外掛程式時才輸入密碼。", + "toast_auto_disabled": "{name} 因反覆出錯已被關閉。" + }, + "connect": { + "title": "連線至 {label}", + "intro_password": "{name} 想使用您的 {label} 帳號。Bulwark 會將密碼保存在自己的伺服器上,只會傳送給 {host}。外掛程式永遠看不到密碼。", + "intro_password_any": "{name} 想使用您的 {label} 帳號。Bulwark 會將密碼保存在自己的伺服器上,只會傳送給您在此輸入的伺服器。外掛程式永遠看不到密碼。", + "intro_signin": "{name} 想登入您的 {label} 帳號。請在此輸入伺服器,然後在該伺服器自己的頁面上登入。Bulwark 會將取得的存取權保存在自己的伺服器上;外掛程式永遠看不到它。", + "server": "伺服器位址", + "server_hint": "cloud.example.com", + "username": "使用者名稱", + "secret": "密碼或金鑰", + "connect": "連線", + "continue": "繼續", + "cancel": "取消", + "saving": "正在儲存…", + "error_server": "請輸入伺服器位址,例如 https://cloud.example.com", + "error_save": "無法儲存:{error}" + } + }, + "global_search": { + "title": "全域搜尋", + "placeholder": "搜尋郵件、聯絡人、行事曆、檔案...", + "scope_label": "搜尋範圍", + "account_label": "帳戶", + "scope_all": "全部", + "scope_mail": "郵件", + "scope_contacts": "聯絡人", + "scope_calendar": "行事曆", + "scope_files": "檔案", + "all_accounts": "所有帳戶", + "show_all": "顯示所有結果", + "no_results": "找不到「{query}」的結果", + "server_hint": "完整單字會搜尋伺服器;部分單字則比對已載入的項目", + "searching": "搜尋中...", + "account_error": "無法搜尋 {account}", + "recent_searches": "最近搜尋", + "recurring": "重複", + "no_subject": "(no subject)", + "empty_hint": "在所有帳戶中搜尋郵件、聯絡人、行事和檔案。可試試 from:、subject:、has:attachment、after:2026-01-01。", + "flat_by_date": "依日期平列", + "grouped": "分組", + "load_more": "載入更多", + "preview_empty": "選擇結果以預覽", + "open_full": "開啟", + "view_label": "檢視" + }, + "top_bar": { + "more": "更多選項" + }, + "plugin_pages": { + "loading": "正在載入頁面", + "not_available_title": "此頁面無法使用", + "not_available_description": "新增此頁面的外掛程式已關閉或已不再安裝。", + "back_to_mail": "返回郵件", + "settings_group": "外掛程式" + }, + "command_palette": { + "title": "命令面板", + "open": "開啟命令面板", + "placeholder": "輸入命令", + "no_results": "沒有相符的命令", + "go_to": "前往{name}", + "compose": "新郵件", + "search": "搜尋郵件", + "theme_dark": "切換為深色主題", + "theme_light": "切換為淺色主題", + "shortcuts": "顯示鍵盤快速鍵", + "group_navigation": "前往", + "group_actions": "動作", + "group_plugins": "外掛程式" + }, + "notification_center": { + "title": "通知", + "mark_all_read": "全部標示為已讀", + "clear": "清除", + "empty": "沒有通知", + "close": "關閉", + "unread": "未讀", + "unread_count": "{count} 則未讀", + "level_error": "錯誤", + "level_warning": "警告" + } +} diff --git a/locales/zh/common.json b/locales/zh/common.json index bac9ce18..a462f90e 100644 --- a/locales/zh/common.json +++ b/locales/zh/common.json @@ -1,3645 +1,4506 @@ -{ - "meta_description": "使用 JMAP 协议的极简网页邮件客户端", - "login": { - "title": "网页邮箱", - "username_label": "邮箱地址", - "username_placeholder": "user@example.com", - "password_label": "密码", - "password_placeholder": "请输入密码", - "jmap_endpoint_label": "JMAP 服务器", - "jmap_endpoint_placeholder": "https://mail.example.com", - "jmap_endpoint_cors_hint": "服务器必须允许来自此域名的 CORS 请求。", - "jmap_server_label": "服务器", - "jmap_server_auto_picked": "已根据您的电子邮件域名选择服务器。", - "sign_in": "登录", - "signing_in": "正在登录…", - "loading": "正在加载…", - "reconnecting": "连接已断开,正在尝试重连…", - "error": { - "invalid_credentials": "邮箱地址或密码无效。请检查后重试。", - "connection_failed": "无法访问服务器。检查您的网络连接,然后重试。", - "cors_blocked": "服务器可访问,但正在阻止跨源请求。请检查 JMAP 服务器的 CORS 设置,并允许此域名。", - "server_error": "服务器暂时不可用,请稍后重试。", - "generic": "发生意外错误。如果问题持续存在,请联系管理员。", - "totp_required": "需要双重验证代码。请在下方输入验证码。", - "totp_invalid": "验证码无效。请检查您的身份验证器应用,然后重试。", - "oauth_discovery_failed": "SSO 已启用,但无法访问身份提供者。检查您的 OAuth 配置。" - }, - "show_password": "显示密码", - "hide_password": "隐藏密码", - "totp_toggle": "我有双重验证代码", - "remember_me": "记住我", - "config_error": { - "title": "配置错误", - "fetch_failed": "无法加载应用配置。请稍后重试。", - "server_not_configured": "邮件服务器尚未配置。请联系您的管理员。" - }, - "remove_from_history": "从历史记录中移除", - "totp_label": "验证码", - "totp_placeholder": "000000", - "session_expired": "会话已过期,请重新登录。", - "dismiss": "关闭", - "or": "或", - "sign_in_sso": "使用 SSO 登录", - "add_account_title": "添加账户", - "add_account_subtitle": "使用其他账户登录", - "cancel": "取消", - "website": "网站", - "imprint": "法律信息", - "privacy_policy": "隐私政策", - "try_demo": "体验演示版", - "demo_description": "使用示例数据进行探索 - 无需账户", - "demo_launching": "正在启动演示版...", - "demo_login_button": "启动演示", - "demo_tagline": "体验功能完整的邮件客户端,无需账号。", - "demo_no_signup": "无需注册,直接使用示例数据体验", - "oauth_completing": "正在完成登录…", - "oauth_error": { - "title": "认证失败", - "invalid_state": "安全验证失败。请尝试重新登录。", - "missing_params": "缺少授权数据。请尝试重新登录。", - "token_exchange_failed": "无法完成身份验证。请再试一次。", - "access_denied": "访问被拒绝。请联系您的管理员。", - "back_to_login": "返回登录" - } - }, - "sidebar": { - "close": "关闭", - "compose": "撰写邮件", - "compose_hint": "撰写邮件 (c)", - "search_placeholder": "搜索邮件...", - "search_placeholder_hint": "搜索邮件...(按 /)", - "storage": "存储空间", - "storage_used": "已使用", - "storage_free": "剩余", - "storage_total": "总计", - "sign_out": "退出登录", - "sign_out_of": "退出 {account}", - "sign_out_all": "退出所有账户", - "add_account": "添加账户", - "set_as_default": "设为默认", - "switch_account": "切换账户", - "contacts": "联系人", - "calendar": "日历", - "settings": "设置", - "admin": "管理", - "files": "文件", - "loading_mailboxes": "正在加载邮箱...", - "push_connected": "实时同步已启用", - "push_disconnected": "实时同步未启用", - "keyboard_shortcuts": "键盘快捷键", - "theme": { - "light": "浅色模式", - "dark": "深色模式", - "system": "系统主题" - }, - "language": { - "title": "语言" - }, - "mailboxes": { - "inbox": "收件箱", - "sent": "已发送", - "drafts": "草稿", - "trash": "已删除", - "archive": "归档", - "starred": "已加星标", - "all_mail": "所有邮件", - "spam": "垃圾邮件", - "important": "重要" - }, - "unified_inbox": "统一收件箱", - "unified_sent": "所有已发送", - "unified_drafts": "所有草稿", - "unified_trash": "所有已删除", - "unified_archive": "所有归档", - "unified_junk": "所有垃圾邮件", - "all_accounts": "所有账户", - "unified_mailbox": "统一邮箱", - "expand": "展开", - "collapse": "收起", - "expand_tooltip": "展开", - "collapse_tooltip": "收起", - "mobile": { - "search": "搜索", - "compose": "撰写邮件", - "go_back": "返回", - "toggle_menu": "切换菜单" - }, - "clear_search": "清除搜索", - "vacation_active": "假期自动回复已开启", - "demo_banner": "演示模式", - "demo_reset": "重置", - "demo_tour": "引导", - "tags": "标签", - "show_all_tags": "显示全部({count})", - "show_fewer_tags": "收起", - "folders": "文件夹", - "mail": "邮件", - "nav_label": "导航", - "add_app": "应用", - "shared": "共享", - "scheduled": "已计划", - "unified_all_unread": "全部未读", - "unified_all_starred": "全部加星标", - "unified_all_mail": "全部邮件", - "remove_account": "Remove account", - "remove_account_confirm": "Remove {account} from this device? You can add it back later." - }, - "protocol_handlers": { - "title": "默认应用", - "description": "选择是否在 Bulwark 中打开电子邮件和日历链接。从技术上讲,Bulwark 会注册为 mailto: 和 webcal: 链接的协议处理程序。", - "unsupported": "此浏览器或连接不支持手动注册协议处理程序。你仍可尝试通过浏览器或系统设置使用已安装的 PWA。", - "mailto_label": "电子邮件链接", - "mailto_description": "在 Bulwark 中打开 mailto: 链接,并预先填好撰写窗口。", - "protocol_open_mode_label": "打开协议链接时", - "protocol_open_mode_description": "选择 Bulwark 是在新标签页中打开 mailto: 和 webcal: 链接,还是复用已打开的会话。活动会话选项需要通知权限,这样当浏览器阻止聚焦时,你可以点击备用通知将 Bulwark 窗口带到前台。", - "protocol_open_mode_active_session": "尽可能在活动会话中打开", - "protocol_open_mode_new_tab": "始终打开新标签页", - "focus_notification_title": "打开 Bulwark", - "focus_notification_body": "链接已在 Bulwark 中打开。点击可将窗口带到前台。", - "webcal_label": "日历链接", - "webcal_description": "在 Bulwark 中打开 webcal: 链接,并预先填好日历订阅对话框。", - "register_mailto": "注册电子邮件应用", - "register_webcal": "注册日历应用", - "mailto_registered": "已请求注册电子邮件处理程序", - "webcal_registered": "已请求注册日历处理程序", - "registration_failed": "协议处理程序注册失败", - "opening_mailto": "正在打开撰写窗口...", - "opening_webcal": "正在打开日历...", - "browser_note": "你的浏览器或操作系统可能会要求确认,并且可能需要先安装 Bulwark,才能将其选为默认应用。", - "select_account_title": "选择账户", - "select_mailto_account": "选择用于打开此电子邮件链接的账户。", - "select_webcal_account": "选择用于打开此日历链接的账户。", - "select_account_note": "此选择仅适用于此协议链接。", - "detail_to": "收件人", - "detail_subject": "主题", - "detail_no_subject": "无主题", - "detail_calendar": "日历", - "detail_source": "来源", - "active_account": "活动", - "switching_account": "正在切换账户..." - }, - "sidebar_apps": { - "modal_title": "侧边栏应用", - "add_new": "添加应用", - "edit_app": "编辑应用", - "name_label": "名称", - "name_placeholder": "我的应用", - "name_required": "名称为必填项", - "url_label": "URL", - "url_required": "URL 为必填项", - "url_invalid": "请输入有效的 http 或 https URL", - "icon_label": "图标", - "icon_required": "图标为必填项", - "open_mode_label": "打开方式", - "open_new_tab": "在新标签页中打开", - "open_inline": "内嵌打开", - "cancel": "取消", - "add": "添加", - "update": "更新", - "delete": "删除", - "delete_confirm_title": "删除应用", - "delete_confirm": "您确定要删除\"{name}\"吗?", - "no_apps": "尚未添加应用", - "no_apps_hint": "将自定义应用和链接添加到侧边栏", - "managed_title": "由管理员提供", - "managed_description": "这些应用已为所有人配置,无法更改。", - "managed_badge": "受管理", - "search_icons": "搜索图标...", - "show_popular": "热门", - "show_all": "全部", - "no_icons_found": "没有找到图标", - "inline_badge": "内嵌", - "tab_badge": "新标签页", - "show_on_mobile": "在手机上显示" - }, - "email_list": { - "no_emails": "未找到邮件", - "no_emails_description": "此邮箱为空", - "no_search_results": "未找到结果", - "no_search_results_description": "请调整搜索条件或筛选器", - "loading": "正在加载邮件...", - "unread": "未读", - "to_me": "发给我", - "to_recipients": "致 {count} 收件人", - "and_others": "和 {count} 其他人", - "draft": "草稿", - "starred": "加星标", - "pinned": "已置顶", - "replied": "已回复", - "forwarded": "已转发", - "has_attachment": "有附件", - "conversations_count": "共 {total} 个会话,当前显示 {count} 个", - "conversations_count_plus": "{count}+ 个会话", - "conversations_count_simple": "{count} 个会话", - "no_conversations": "暂无会话", - "loading_more": "正在加载更多邮件...", - "no_more_emails": "没有更多邮件了", - "batch_actions": { - "select": "选择邮件", - "select_all": "全选", - "selected_messages": "已选取 {count, plural, one {1} other {#}} 封电子邮件", - "mark_read": "标记为已读", - "mark_unread": "标记为未读", - "delete": "删除", - "delete_confirm_title": "删除邮件", - "delete_confirm_message": "确定要删除{count, plural, one {1 封邮件} other {# 封邮件}}吗?", - "clear_selection": "清除选择" - }, - "permanent_delete": "永久删除", - "permanent_delete_confirm_title": "永久删除", - "permanent_delete_confirm_message": "该邮件将被永久删除。此操作无法撤销。", - "permanent_delete_confirm_batch_message": "这些{count, plural, one {1 封邮件} other {# 封邮件}}将被永久删除。此操作无法撤销。", - "empty_folder": { - "button": "清空文件夹", - "confirm_title": "清空文件夹", - "confirm_message": "此文件夹中的所有邮件都将被永久删除。此操作无法撤销。", - "confirm_button": "清空文件夹", - "junk_hint": "您可以清空垃圾邮件文件夹以永久删除所有邮件。", - "trash_hint": "您可以清空已删除文件夹以永久删除所有邮件。" - }, - "no_scheduled_emails": "没有计划发送的邮件", - "no_scheduled_emails_description": "计划稍后发送的邮件会显示在这里。", - "scheduled_count": "{count, plural, one {1 封计划发送邮件} other {# 封计划发送邮件}}", - "scheduled_actions_hint": "计划发送的邮件可以通过计划操作取消、重新计划或编辑。", - "cancel_scheduled_send": "取消发送", - "reschedule_send": "重新计划", - "cancel_and_edit": "取消并编辑", - "cancel_and_compose_again": "取消并重新撰写", - "reschedule_prompt": "输入新的日期/时间,例如 2026-05-04T15:30" - }, - "email_viewer": { - "read_receipt": { - "prompt": "发件人请求已读回执:", - "send": "发送回执", - "ignore": "忽略", - "sent": "已读回执已发送。", - "send_failed": "无法发送已读回执", - "mdn_subject": "已读:{subject}", - "mdn_body": "这是您发送给 {recipient} 的邮件的已读回执。\n\n注意:此回执仅表示邮件已在收件人的计算机上显示,并不保证收件人已阅读或理解邮件内容。" - }, - "no_email_selected": "未选择邮件", - "no_email_description": "从列表中选择一封邮件以在此查看", - "no_conversation_selected": "未选择会话", - "no_conversation_description": "从列表中选择会话以在此阅读", - "compose": "撰写邮件", - "compose_hint": "撰写新邮件", - "no_subject": "(无主题)", - "no_body_content": "(无正文内容)", - "no_preview_available": "无可用预览", - "show_quoted_text": "显示引用文本", - "hide_quoted_text": "隐藏引用文本", - "loading_email": "正在加载邮件...", - "loading": "正在加载…", - "reply": "回复", - "reply_all": "全部回复", - "forward": "转发", - "delete": "删除", - "archive": "归档", - "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", - "star": "加星标", - "unstar": "取消星标", - "mark_unread": "标记为未读", - "mark_read": "标记为已读", - "unread": "未读", - "read": "已读", - "spam_short": "垃圾邮件", - "not_spam_short": "非垃圾邮件", - "move": "移动", - "print": "打印", - "view_source": "查看源码", - "fullscreen": "全屏", - "exit_fullscreen": "退出全屏", - "export_email": "导出为 .eml", - "forward_as_attachment": "作为附件转发", - "import_email": "导入 .eml 或 .zip", - "keyboard_shortcuts": "键盘快捷键(?)", - "email_source": "邮件源码", - "draft_banner": "这是一封草稿邮件", - "edit_draft": "编辑", - "copy_source": "复制到剪贴板", - "source_copied": "源码已复制到剪贴板", - "attachments": "附件", - "important": "重要", - "download": "下载", - "download_all": "全部下载", - "from": "发件人", - "to": "收件人", - "cc": "抄送", - "bcc": "密送", - "date": "日期", - "subject": "主题", - "show_details": "查看详情", - "hide_details": "隐藏详情", - "external_content_warning": "图片和外部内容已被屏蔽", - "load_external_content": "加载图片", - "trust_sender": "始终信任此发件人", - "back_to_list": "返回列表", - "view_contact": "查看联系人", - "message_details": "邮件详情", - "more_reply_options": "更多回复选项", - "set_tag": "设置颜色标签", - "tag": "标签", - "more_actions": "更多操作", - "previous": "上一封", - "next": "下一封", - "move_to": "移动到…", - "remove_tag": "删除标签", - "tag_filter_placeholder": "筛选标签", - "tag_no_matches": "没有匹配的标签", - "more_count": "+{count} 更多", - "characters_count": "{count} 个字符", - "quick_reply_placeholder": "快速回复...", - "more_options": "更多选项", - "sending": "发送中…", - "security_authentication": "安全与认证", - "technical_details": "技术详情", - "message_id_label": "消息 ID:", - "reply_to_label": "回复至:", - "delivery_time_label": "投递时间:", - "conversation_part_label": "会话片段:", - "previous_messages": "{count} 条之前的消息", - "previous_messages_plural": "{count} 条之前的消息", - "time": { - "day": "天", - "days": "天", - "hour": "小时", - "hours": "小时", - "minute": "分钟", - "minutes": "分钟" - }, - "unknown_sender": "未知", - "recipient_me": "我", - "recipient_and_others": "{name} 和 {count} 其他人", - "recipient_to_prefix": "到:", - "authentication": { - "title": "邮件身份验证", - "status": { - "verified": "已验证", - "warning": "警告", - "none": "未验证" - }, - "spf": { - "pass": "SPF 通过", - "fail": "SPF 失败", - "none": "无 SPF" - }, - "dkim": { - "pass": "DKIM 有效", - "fail": "DKIM 无效", - "none": "无 DKIM" - }, - "dmarc": { - "pass": "DMARC 通过", - "fail": "DMARC 失败", - "none": "无 DMARC" - }, - "spam_score": "垃圾邮件评分", - "tooltip_spf": "Sender Policy Framework:验证发送服务器是否有权代表该域名发送邮件", - "tooltip_dkim": "DomainKeys Identified Mail:通过加密签名确认邮件在传输过程中未被篡改", - "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance:确保 SPF 与 DKIM 与发件人域名一致,并为失败设置处理策略", - "policy": "策略", - "result": { - "pass": "通过", - "fail": "失败", - "softfail": "软失败", - "neutral": "中立", - "permerror": "永久错误", - "temperror": "临时错误", - "none": "无" - } - }, - "headers": { - "routing": "路由", - "received": "接收时间", - "message_id": "消息 ID", - "list_info": "邮件列表信息" - }, - "tooltips": { - "reply": "回复 (r)", - "reply_all": "全部回复 (a)", - "forward": "转发 (f)", - "archive": "归档 (e)", - "delete": "删除 (# 或 Del)", - "star": "加星标 (s)", - "unstar": "取消星标 (s)", - "compose": "撰写邮件 (c)", - "previous": "上一封邮件", - "next": "下一封邮件", - "edit_draft": "编辑草稿" - }, - "spam": { - "button_title": "举报垃圾邮件", - "not_spam_title": "标记为正常邮件", - "toast_success": "已移至垃圾邮件", - "toast_batch": "{count} 封邮件已移至垃圾邮件", - "toast_undo": "撤销", - "toast_not_spam_success": "已移至收件箱", - "toast_not_spam_batch": "{count} 封邮件已移至收件箱", - "error": "举报垃圾邮件失败", - "error_not_spam": "无法恢复邮件" - }, - "unsubscribe_banner": { - "label": "订阅邮件", - "button": "取消订阅", - "confirm_title": "取消订阅该发件人吗?", - "confirm_button": "确认", - "cancel": "取消", - "success_http": "取消订阅页面已在新标签页中打开", - "success_mailto": "退订邮件已发送", - "confirm_message_http": "退订页面将在新标签页中打开。", - "confirm_message_mailto": "将向发件人发送退订邮件。", - "error": "无法取消订阅", - "dismiss": "关闭" - }, - "calendar_invitation": { - "loading": "正在加载活动详情…", - "title": "日历邀请", - "published_title": "已发布活动", - "response_title": "日程回复", - "update_title": "活动更新", - "counter_title": "改期建议", - "refresh_title": "刷新请求", - "declined_counter_title": "改期建议已被拒绝", - "cancelled_title": "活动已取消", - "organizer": "组织者:{name}", - "organizer_label": "组织者:", - "attendees": "{count, plural, one {# 位参与者} other {# 位参与者}}", - "accept": "接受", - "maybe": "暂定", - "decline": "拒绝", - "add_to_calendar": "添加到日历", - "added": "添加到日历", - "rsvp_sent": "回复已发送", - "parse_error": "无法读取邀请", - "action_failed": "无法完成该日历操作。", - "no_calendar": "日历不可用", - "published_info": "此事件仅供参考。", - "response_info": "此消息包含与会者的响应。", - "response_info_organizer": "此与会者回复会更新您的活动。", - "update_info": "此消息更新现有事件。", - "counter_info": "此消息提出了对该活动的改期建议。", - "counter_info_organizer": "该与会者对您的活动提出了改期建议。", - "refresh_info": "此消息请求最新的事件详细信息。", - "refresh_info_organizer": "与会者要求了解最新的活动详细信息。", - "declined_counter_info": "组织者拒绝了该改期建议。", - "authentication_failed_info": "此邀请的邮件身份验证检查失败。谨慎对待日历操作。", - "authentication_missing_info": "此邀请不包括经过验证的邮件身份验证。如果有任何异常情况,请与组织者确认详细信息。", - "sender_mismatch_info": "此邀请是从 {sender} 发送的,而日历数据中列出的组织者是 {organizer}。", - "sender_mismatch_unverified_info": "此邀请是从 {sender} 发送的,而日历数据中列出的组织者是 {organizer},且无法验证该消息。", - "organizer_role": "您是此活动的组织者", - "your_response": "您的回复:{status}", - "response_needed": "待回复", - "response_accepted": "已接受", - "response_tentative": "暂定", - "response_declined": "已拒绝", - "response_delegated": "已委托", - "actor_sent_info": "由 {name} 发送。", - "actor_response_info": "{name} 回复了 {status}。", - "actor_counter_info": "{name} 提议更改此活动的时间。", - "actor_refresh_info": "{name} 请求获取最新的活动详情。", - "actor_declined_counter_info": "{name} 拒绝了改期建议。", - "actor_note": "备注:{comment}", - "actor_unknown": "有人", - "proposed_changes": "建议的更改", - "change_title": "标题", - "change_time": "时间", - "change_location": "地点", - "change_description": "描述", - "change_empty": "无", - "change_from_to": "{before} -> {after}", - "apply_proposal": "应用建议的更改", - "proposal_applied": "已应用建议的更改。", - "review_proposal": "查看建议", - "review_request": "查看请求", - "view_in_calendar": "在日历中打开", - "select_calendar": "选择日历", - "already_in_calendar": "已在你的日历中", - "request_info": "您已被邀请参加此活动。回复以让组织者了解您是否参加。", - "cancel_info": "组织者已取消此活动。", - "event_updated": "更新 #{sequence}", - "event_status_tentative": "暂定", - "event_status_cancelled": "已取消", - "collapse": "隐藏详情", - "expand": "显示详情" - }, - "send": "发送", - "more": "更多", - "details": { - "recipients_routing": "收件人与路由", - "authentication_security": "身份验证与安全", - "identifiers_threading": "标识符与会话", - "mailing_list": "邮件列表", - "message_properties": "邮件属性", - "sent": "发送时间", - "received": "接收时间", - "delivery_time": "送达耗时", - "in_reply_to": "回复对象", - "references": "引用", - "thread_id": "会话 ID", - "size": "大小", - "mime_type": "MIME 类型", - "attachments_summary": "{count} 个文件 · {size}", - "list_id": "列表 ID", - "list_help": "列表帮助", - "list_post": "列表投递", - "list_unsubscribe": "取消订阅", - "iprev": "反向 DNS", - "spam_status": "垃圾邮件状态", - "ai_verdict": "AI 判断", - "account": "账户", - "no_subject": "(无主题)" - }, - "scheduled_banner": "计划于 {date} 发送", - "scheduled_send_created": "邮件已计划发送", - "cancel_scheduled_send": "取消发送", - "reschedule_send": "重新计划", - "cancel_and_edit": "取消并编辑", - "cancel_and_compose_again": "取消并重新撰写", - "reschedule_prompt": "输入新的日期/时间,例如 2026-05-04T15:30", - "scheduled_actions_only": "计划发送视图仅支持计划操作", - "undo_send_scheduled": "邮件已计划发送", - "undo_send": "撤销发送", - "contact_sidebar": { - "title": "联系人", - "close": "关闭侧边栏", - "action_email": "邮件", - "action_email_title": "发送邮件", - "action_copy": "复制", - "action_copy_title": "复制邮箱地址", - "action_edit_title": "编辑联系人", - "section_emails": "邮箱", - "section_phones": "电话", - "section_organizations": "组织", - "section_addresses": "地址", - "section_notes": "备注", - "not_in_contacts": "不在您的联系人中", - "add_to_contacts": "添加到联系人", - "copied": "已复制!", - "copy_failed": "复制失败" - }, - "send_now": "立即发送" - }, - "email_composer": { - "read_receipt_on": "已请求已读回执(点击以关闭)", - "read_receipt_off": "请求已读回执", - "dsn_on": "已请求投递通知(点击禁用)", - "dsn_off": "请求投递通知", - "format_plain_text": "切换为纯文本", - "format_rich_text": "切换为富文本 (HTML)", - "require_tls_on": "已要求加密投递(点击禁用)", - "require_tls_off": "要求加密投递 (TLS)", - "new_message": "新邮件", - "signature_edit_hint": "双击以编辑签名", - "reply": "回复", - "reply_all": "全部回复", - "forward": "转发", - "reply_to": "回复", - "reply_all_to": "全部回复", - "forward_message": "转发", - "from": "发件人", - "to": "收件人", - "cc": "抄送", - "bcc": "密送", - "subject": "主题", - "body_placeholder": "写邮件内容...", - "send": "发送", - "cancel": "取消", - "attach": "附件", - "attach_photos": "Photos & Videos", - "attach_files": "Files", - "discard": "丢弃", - "discard_draft_title": "丢弃草稿?", - "discard_draft_confirm": "您有未保存的更改。确定要丢弃这封草稿吗?", - "saving": "保存中...", - "draft_saved": "草稿已保存", - "save_failed": "保存失败", - "to_placeholder": "收件人", - "cc_placeholder": "抄送", - "bcc_placeholder": "密送", - "subject_placeholder": "主题", - "cc_label": "抄送:", - "bcc_label": "密送:", - "subject_label": "主题:", - "file_size_kb": "KB", - "prefix": { - "forward": "Fwd:", - "reply": "Re:" - }, - "no_subject": "(无主题)", - "unknown_sender": "未知", - "quote": { - "reply_header": "在 {date},{sender} 写道:", - "forward_header": "---------- 转发的邮件 ----------", - "from": "来自:{sender}", - "date": "日期:{date}", - "subject": "主题:{subject}", - "to": "至:{recipients}" - }, - "remove_sub_address": "删除子地址", - "from_override": { - "toggle_off": "覆盖", - "toggle_on": "取消覆盖", - "toggle_tooltip": "自由编辑发件人姓名和地址。邮件仍通过您的身份发送。", - "name_label": "发件人姓名", - "name_placeholder": "姓名", - "email_label": "发件人电子邮件地址", - "email_placeholder": "alias@example.com", - "envelope_notice": "如果您的服务器不接受此地址作为信封发件人,将改用 {identity},收件人可以在 Return-Path 标头中看到它。" - }, - "use_template": "模板", - "save_as_template": "保存为模板", - "validation": { - "recipient_required": "请添加收件人", - "body_required": "请输入邮件内容或添加附件", - "attachments_uploading": "附件仍在上传中,上传完成后将自动发送", - "attachment_upload_failed": "未发送 - 附件上传失败。请移除该附件后重试。" - }, - "upload_progress": "正在上传 {uploaded} / {total}", - "upload_cancel": "取消上传", - "upload_failed": "无法上传 {filename}", - "attachment_too_large": "“{name}”超过了服务器允许的大小(每个文件 {max})", - "attachments_total_too_large": "附件将超过服务器每封邮件 {max} 的上限", - "drop_files": "拖放要附加的文件", - "show_less": "收起", - "send_failed": "发送邮件失败", - "send_timeout": "服务器已停止响应。再次发送前请检查“已发送”文件夹——邮件可能已经发出。", - "continue_draft": "继续编辑草稿", - "close_draft_title": "保存或丢弃草稿?", - "close_draft_message": "您有未保存的更改。要将其保存为草稿还是丢弃?", - "smime_sign_on": "S/MIME 签名已启用", - "smime_sign_off": "启用 S/MIME 签名", - "smime_encrypt_on": "S/MIME 加密已启用", - "smime_encrypt_off": "启用 S/MIME 加密", - "smime_encrypt_unavailable": "S/MIME 加密不可用 – 缺少收件人证书", - "smime_unlock_title": "解锁 S/MIME 密钥", - "smime_unlock_message": "输入密码以解锁您的 S/MIME 签名密钥。", - "smime_unlock_button": "解锁", - "smime_passphrase_placeholder": "输入密码", - "forgot_attachment": { - "title": "忘记添加附件了吗?", - "message": "您的消息中提到了“{keyword}”,但未附加任何文件。仍然发送吗?", - "send_anyway": "仍然发送", - "back": "返回编辑" - }, - "empty_subject": { - "title": "没有主题就发送吗?", - "message": "此邮件没有主题。仍要发送吗?", - "dont_ask_again": "不再提示", - "send_anyway": "仍然发送", - "back": "返回编辑" - }, - "add_link": "添加链接", - "link_url_prompt": "输入 URL", - "sending": "发送中...", - "schedule_send": "计划发送", - "schedule_send_description": "选择服务器应何时发送此邮件。", - "schedule_send_required": "请选择日期和时间。", - "schedule_send_invalid": "请输入有效的日期和时间。", - "schedule_send_future": "请选择未来的日期和时间。", - "schedule_send_too_late": "该时间晚于服务器允许的范围。", - "schedule_send_unsupported": "此账户不支持计划发送。", - "schedule_send_cleanup_warning": "已创建计划发送,但清理草稿失败。", - "send_delay_unsupported": "此账户不支持发送延迟。", - "send_delay_unsupported_confirm": "此账户不支持发送延迟。是否立即发送?", - "recipient_edit_email": "编辑电子邮件地址", - "recipient_edit_name": "编辑显示名称", - "recipient_email_placeholder": "电子邮件地址", - "recipient_name_placeholder": "显示名称", - "autocomplete_search_server": "在服务器上搜索", - "autocomplete_searching": "搜索中...", - "toolbar": { - "bold": "加粗", - "italic": "斜体", - "underline": "下划线", - "strikethrough": "删除线", - "text_color": "文字颜色", - "remove_color": "移除颜色", - "heading_1": "标题 1", - "heading_2": "标题 2", - "bullet_list": "项目符号列表", - "ordered_list": "编号列表", - "quote": "引用", - "code_block": "代码块", - "align_left": "左对齐", - "align_center": "居中对齐", - "align_right": "右对齐", - "text_direction": "文字方向 (RTL/LTR)", - "link": "链接", - "table": "表格", - "clear_formatting": "清除格式", - "undo": "撤销", - "redo": "重做", - "add_row_above": "在上方添加行", - "add_row_below": "在下方添加行", - "add_column_before": "在前面添加列", - "add_column_after": "在后面添加列", - "delete_row": "删除行", - "delete_column": "删除列", - "toggle_header_row": "切换标题行", - "delete_table": "删除表格", - "pick_size": "选择大小", - "font_size": "字号", - "font_size_default": "默认", - "background_color": "背景颜色", - "remove_background_color": "移除背景颜色" - }, - "send_filing_warning": "已发送,但发送后的清理失败,可能会残留旧草稿。" - }, - "confirm_dialog": { - "confirm": "确认", - "cancel": "取消" - }, - "common": { - "loading": "正在加载…", - "error": "错误", - "success": "成功", - "cancel": "取消", - "save": "保存", - "delete": "删除", - "edit": "编辑", - "close": "关闭", - "search": "搜索", - "refresh": "刷新", - "settings": "设置", - "help": "帮助", - "logout": "退出登录", - "yes": "是", - "no": "否", - "unknown": "未知", - "app_title": "网页邮箱", - "reconnecting": "连接已断开,正在尝试重连…", - "rate_limited_title": "服务器身份验证暂时受到速率限制。", - "rate_limited_detail": "Bulwark 已暂停后台请求以避免账户被锁定。将在 {seconds} 秒后重试。", - "rate_limited_action_title": "请求已暂停以避免账户被锁定。", - "rate_limited_action_detail": "Bulwark 正在等待服务器冷却,然后再发送更多请求。请在 {seconds} 秒后重试。", - "pull_to_refresh": "下拉刷新", - "release_to_refresh": "松开刷新", - "refreshing": "正在刷新…" - }, - "notifications": { - "email_sent": "邮件发送成功", - "email_deleted": "邮件已删除", - "email_archived": "邮件已归档", - "emails_deleted": "{count, plural, other {已删除 # 封邮件}}", - "emails_archived": "{count, plural, other {已归档 # 封邮件}}", - "error_archiving": "归档邮件失败", - "error_updating": "更新邮件失败", - "email_starred": "邮件已加星标", - "email_unstarred": "邮件已取消星标", - "email_marked_read": "邮件已标记为已读", - "email_marked_unread": "邮件已标记为未读", - "copied_to_clipboard": "已复制到剪贴板", - "source_copied": "源码已复制到剪贴板", - "error_sending": "发送邮件失败", - "error_deleting": "删除邮件失败", - "error_loading": "无法加载邮件", - "new_email": "新邮件", - "new_email_from": "来自 {sender}", - "click_to_view": "点击查看", - "email_moved": "邮件已移动", - "emails_moved": "{count} 封邮件已移动", - "moved_to_mailbox": "已移至 {mailbox}", - "move_failed": "移动失败", - "move_error": "无法将邮件移至所选文件夹", - "email_tagged": "邮件已加标签", - "emails_tagged": "{count} 封邮件已加标签", - "tag_failed": "标记失败", - "identity_created": "身份创建成功", - "identity_updated": "身份更新成功", - "identity_deleted": "身份已删除", - "identity_set_primary": "主要身份已更新", - "identity_create_failed": "无法创建身份:{error}", - "identity_update_failed": "无法更新身份:{error}", - "identity_delete_failed": "无法删除身份:{error}", - "identity_unauthorized": "您无权从此邮箱地址发送", - "identity_not_found": "未找到身份", - "vacation_saved": "假期自动回复设置已保存", - "vacation_save_failed": "无法保存假期自动回复设置", - "filters_saved": "过滤规则保存成功", - "filters_save_failed": "无法保存过滤器", - "filters_deleted": "过滤规则已删除", - "templates_exported": "模板导出成功", - "templates_imported": "已导入{count, plural, one {# 个模板} other {# 个模板}}", - "templates_import_errors": "部分模板无法导入", - "templates_import_empty": "文件中未找到模板", - "export_email_error": "导出邮件失败", - "import_email_success": "邮件导入成功", - "import_email_error": "导入邮件失败" - }, - "date": { - "today": "今天", - "yesterday": "昨天", - "this_week": "本周", - "last_week": "上周", - "this_month": "本月", - "older": "更早", - "just_now": "刚刚", - "minutes_ago": "{count} 分钟前", - "minutes_ago_plural": "{count} 分钟前", - "hours_ago": "{count} 小时前", - "hours_ago_plural": "{count} 小时前", - "days_ago": "{count} 天前", - "days_ago_plural": "{count} 天前" - }, - "language": { - "title": "语言", - "english": "英语", - "french": "法语", - "japanese": "日语", - "spanish": "西班牙语", - "italian": "意大利语", - "german": "德语", - "dutch": "荷兰语", - "portuguese": "葡萄牙语", - "russian": "俄语", - "select_language": "选择语言", - "switch_to_english": "切换到英语", - "switch_to_french": "切换到法语", - "switch_to_japanese": "切换到日语", - "switch_to_spanish": "切换到西班牙语", - "switch_to_italian": "切换到意大利语", - "switch_to_german": "切换到德语", - "switch_to_dutch": "切换到荷兰语", - "switch_to_portuguese": "切换到葡萄牙语", - "switch_to_russian": "切换到俄语", - "switching": "正在切换语言...", - "switch": "切换语言", - "current": "当前语言", - "polish": "Polski", - "switch_to_polish": "切换到波兰语", - "en": "English", - "fr": "Français", - "de": "Deutsch", - "es": "Español", - "it": "Italiano", - "ja": "日本語", - "ko": "한국어", - "lv": "Latviešu", - "nl": "Nederlands", - "pl": "Polski", - "pt": "Português", - "he": "עברית", - "ru": "Русский", - "sk": "Slovenčina", - "uk": "Українська", - "zh": "简体中文", - "fa": "فارسی", - "farsi": "فارسی", - "switch_to_farsi": "切换到波斯语", - "mn": "Монгол", - "mongolian": "Монгол", - "switch_to_mongolian": "切换到蒙古语" - }, - "settings": { - "title": "设置", - "back_to_mail": "返回邮件", - "save_success": "设置保存成功", - "import_success": "设置导入成功", - "import_error": "导入设置失败", - "reset_confirm": "您确定要将所有设置重置为默认值吗?", - "unsaved_changes": "您有未保存的更改", - "discard_changes": "放弃未保存的更改?", - "discard": "放弃", - "keep_editing": "继续编辑", - "tabs": { - "appearance": "外观", - "language": "语言、地区与时间", - "email": "邮件行为", - "composer": "邮件撰写", - "privacy": "隐私与安全", - "account": "账户", - "identities": "身份", - "vacation": "假期自动回复", - "advanced": "高级", - "calendar": "日历", - "filters": "过滤器", - "templates": "模板", - "folders": "文件夹", - "keywords": "标签", - "security": "安全", - "files": "文件", - "contacts": "联系人", - "encryption": "加密", - "protocol_handlers": "默认应用", - "sidebar_apps": "侧边栏应用", - "notifications": "通知", - "layout": "布局", - "reading": "阅读", - "composing": "撰写", - "downloads": "下载", - "content_senders": "内容和发件人", - "about_data": "关于和数据", - "debug": "调试" - }, - "tab_groups": { - "general": "通用", - "account": "账户与身份", - "organization": "邮件组织", - "apps": "应用", - "system": "系统", - "appearance": "外观", - "mail": "邮件", - "privacy": "隐私和安全", - "advanced": "高级" - }, - "appearance": { - "message_list_order": { - "label": "邮件列表排序", - "description": "列表中邮件的排序方式。排序由服务器完成,因此作用于整个文件夹,而不仅是已加载的邮件。", - "preset": { - "chronological": "按时间(最新在前)", - "unread_first": "未读优先", - "starred_first": "星标优先", - "tagged_first": "标签优先", - "custom": "自定义" - }, - "tag_label": "标签", - "advanced_show": "高级:编辑排序层级", - "advanced_hide": "隐藏高级选项", - "criterion_label": "第 {n} 级条件", - "direction_label": "第 {n} 级方向", - "criteria": { - "unread": "未读", - "starred": "星标", - "tag": "标签", - "receivedAt": "接收日期", - "sentAt": "发送日期", - "from": "发件人", - "subject": "主题", - "size": "大小" - }, - "direction": { - "unread_first": "未读在前", - "read_first": "已读在前", - "starred_first": "星标在前", - "unstarred_first": "无星标在前", - "tagged_first": "有标签在前", - "untagged_first": "无标签在前", - "newest_first": "最新在前", - "oldest_first": "最早在前", - "a_to_z": "A → Z", - "z_to_a": "Z → A", - "largest_first": "最大在前", - "smallest_first": "最小在前" - }, - "add_level": "添加层级", - "remove_level": "移除层级", - "move_up": "上移", - "move_down": "下移", - "tiebreak_note": "最多三个层级;后续层级用于打破平局。“最新在前”始终最后应用。", - "scope": { - "label": "应用于所有文件夹", - "description": "关闭时,排序仅应用于收件箱,其他文件夹保持按时间排序。" - }, - "unsupported_note": "您的邮件服务器不支持按已读状态、星标或标签排序。这些层级将被忽略,列表保持按时间排序。" - }, - "title": "外观", - "description": "自定义邮箱的外观与风格", - "theme": { - "label": "主题", - "description": "选择您喜欢的配色方案", - "light": "浅色", - "dark": "深色", - "system": "系统" - }, - "language": { - "label": "语言", - "description": "选择您的首选语言" - }, - "font_size": { - "label": "字体大小", - "description": "调整文本大小以获得更好的可读性", - "small": "小", - "medium": "中", - "large": "大" - }, - "list_density": { - "label": "界面紧凑度", - "description": "调整界面元素之间的间距", - "extra_compact": "超紧凑", - "compact": "紧凑", - "regular": "默认", - "comfortable": "宽松" - }, - "animations": { - "label": "启用动画", - "description": "显示平滑的过渡和效果" - }, - "toolbar_position": { - "label": "工具栏位置", - "description": "在哪里显示邮件操作按钮(回复、归档、删除等)", - "top": "顶部", - "below_subject": "主题下方" - }, - "toolbar_labels": { - "label": "显示工具栏标签", - "description": "在工具栏图标旁显示文字标签。熟悉图标后可关闭以节省空间。" - }, - "hide_account_switcher": { - "label": "隐藏侧边栏账户切换器", - "description": "隐藏文件夹侧边栏顶部的账户选择器。您仍然可以从底部导航栏切换账户。" - }, - "show_rail_account_list": { - "label": "在导航导轨上显示账户头像", - "description": "在导航栏底部显示账户头像,方便快速切换;下方会保留退出按钮。" - }, - "unified_mailbox": { - "label": "统一邮箱", - "description": "显示所有已连接账户的合并文件夹(收件箱、已发送等)", - "cross_account": { - "label": "Across all accounts", - "description": "Merge the unified mailbox across every connected account instead of staying within the active account." - }, - "include_group": { - "label": "包含群组收件箱", - "description": "在统一视图中也合并共享/群组收件箱。" - } - }, - "all_mail": { - "folders_label": "Folders in All Mail", - "folders_description": "Choose which folders are merged into the All Mail view.", - "account_hint": "Applies to {account}.", - "no_folders": "No folders available." - }, - "colorful_sidebar_icons": { - "label": "彩色侧边栏图标", - "description": "按类型为文件夹和标签图标着色(蓝色收件箱、红色垃圾邮件、绿色已发送等)。禁用以获得单色侧边栏。" - }, - "tint_list_rows": { - "label": "Tint List Rows by Tag Color", - "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." - }, - "show_folder_total_count": { - "label": "显示邮件总数", - "description": "在文件夹和标签旁边显示邮件总数,以及未读数量。停用后仅显示未读数量。" - }, - "favicon_unread_badge": { - "label": "Unread Count on Tab Icon", - "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." - }, - "pro_interface": { - "label": "Pro 界面(实验性)", - "description": "仅限桌面的高级用户布局,支持多标签消息浏览和跨账户工作流。标准界面不受影响,您可以随时切换回来。", - "open_label": "打开 Pro 界面", - "back_to_standard": "返回标准" - }, - "cross_unread": { - "label": "所有账户:未读", - "description": "在“所有账户”部分显示一个条目,列出所有账户的未读邮件,涵盖除垃圾邮件、已发送、归档、废纸篓和草稿之外的所有文件夹。" - }, - "cross_starred": { - "label": "所有账户:加星标", - "description": "在“所有账户”部分显示一个条目,列出所有账户的加星标邮件,涵盖除垃圾邮件、已发送、归档、废纸篓和草稿之外的所有文件夹。" - }, - "cross_all": { - "label": "所有账户:全部邮件", - "description": "在“所有账户”部分显示一个条目,列出所有账户的全部邮件,涵盖除垃圾邮件、已发送、归档、废纸篓和草稿之外的所有文件夹。" - } - }, - "keywords": { - "title": "电子邮件标签", - "description": "定义标签以使用颜色组织您的电子邮件。这些标签作为JMAP关键词存储在服务器上。", - "add_keyword": "添加标签", - "label_field": "显示名称", - "label_placeholder": "例如工作、个人、紧急", - "id_field": "标签ID", - "id_placeholder": "例如工作、个人", - "color_field": "颜色", - "id_exists": "此标签ID已存在", - "edit": "编辑标签", - "delete": "删除标签", - "save": "保存", - "add": "添加", - "cancel": "取消", - "migrating": "正在更新现有邮件的标签…", - "migration_error": "更新现有邮件的标签失败", - "nesting": { - "label": "嵌套标签", - "description": "将标签嵌套在其他标签之下,并在侧边栏中以树形显示。" - }, - "parent_field": "上级标签", - "no_parent": "无上级标签", - "too_long": "此标签路径过长(最多 {max} 个字符)", - "has_children_locked": "此标签下嵌套了其他标签,因此其名称和上级标签已锁定。请先移动或删除它们。", - "has_children_delete": "请先删除嵌套在此标签下的标签", - "visibility_field": "侧边栏显示", - "visibility": { - "show": "显示", - "unread": "有未读时显示", - "hide": "隐藏" - }, - "unrecognized": { - "title": "从服务器恢复标签", - "description": "标签的名称和颜色保存在此浏览器中,但标签本身以 JMAP 关键词的形式存在于邮件上。扫描邮件以查找此处没有对应标签的关键词,然后重新添加它们。", - "scan": "扫描标签", - "rescan": "重新扫描", - "scanning": "正在扫描… {total} 封邮件中的 {scanned} 封", - "none": "邮件上的所有标签均已在此定义。", - "messages": "{count, plural, one {# 封邮件} other {# 封邮件}}", - "add_all": "全部添加({count})", - "partial": "仅扫描了 {total} 封邮件中最新的 {scanned} 封。仅用于较旧邮件的标签可能会遗漏。", - "error": "无法扫描邮件中的标签。" - } - }, - "notifications": { - "test_sound": "测试通知声音", - "sounds": { - "default": "默认(蜂鸣声)", - "cheerful": "快乐", - "involved": "沉浸", - "swift": "轻快手势", - "relax": "放松" - }, - "sound_selection": { - "title": "通知声音", - "description": "选择通知时播放的声音", - "choose": "声音", - "choose_desc": "选择通知音并单击扬声器图标进行预览" - }, - "email": { - "title": "邮件通知", - "description": "配置传入邮件的通知", - "enabled": "邮件通知", - "enabled_desc": "新邮件到达时显示通知", - "sound": "通知声音", - "sound_desc": "新邮件到达时播放音频提醒", - "inbox_only": "Inbox only", - "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" - }, - "calendar": { - "title": "日历通知", - "description": "配置日历事件的通知", - "enabled": "事件通知", - "enabled_desc": "显示即将发生的日历事件的提醒", - "sound": "通知声音", - "sound_desc": "播放日历提醒的音频提醒", - "invitation_parsing": "解析邮件邀请", - "invitation_parsing_desc": "检测邮件附件中的日历邀请并显示日历操作" - }, - "push": { - "confirm_disable_message": "当网站关闭时,此设备将停止接收提醒。", - "confirm_disable_title": "禁用后台通知?", - "description": "在此网站关闭时接收新邮件的系统通知。通过 Bulwark 推送中继传送;中继永远不会看到邮件内容。", - "disable": "禁用", - "enable": "启用", - "dismiss_aria": "关闭通知提示", - "ios_hint": "在 iOS 上,请先将网站安装到主屏幕 – Safari 仅向已安装的 PWA 发送 Web Push。", - "reenable": "重新注册", - "relay_desc": "默认使用托管的 Bulwark 中继。仅当您自托管时才更改。", - "relay_label": "推送中继", - "relay_locked": "由管理员设置", - "relay_locked_desc": "推送中继 URL 已由您的管理员设置,无法更改。", - "status_active": "在此设备上活动", - "status_busy": "工作中…", - "status_inactive": "在此设备上未启用", - "status_unsupported": "此浏览器不支持 Web Push", - "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", - "title": "后台通知", - "devices_title": "已注册的设备", - "devices_desc": "已注册接收此账户后台通知的所有设备。撤销后,该设备会立即停止接收。", - "devices_loading": "正在加载已注册的设备…", - "devices_empty": "没有设备注册接收后台通知。", - "devices_error": "无法加载已注册的设备。", - "devices_refresh": "刷新", - "device_this": "此设备", - "device_other": "其他设备", - "device_status_active": "正在投递通知", - "device_status_inactive": "从未投递过通知", - "device_status_unknown": "此中继不识别", - "device_expires": "{date} 过期", - "revoke": "撤销", - "confirm_revoke_title": "要撤销此设备吗?", - "confirm_revoke_message": "它将立即停止接收后台通知。在该设备上重新启用推送会重新注册。", - "confirm_revoke_message_this": "此设备将立即停止接收后台通知。" - } - }, - "language_region": { - "title": "语言、地区与时间", - "description": "语言、日期格式、时间格式以及其他区域设置", - "language": { - "label": "语言", - "description": "选择您的首选语言", - "english": "英语", - "french": "法语" - }, - "date_format": { - "label": "日期格式", - "description": "电子邮件列表中日期的显示方式", - "smart": "智能(按地区)", - "relative": "相对时间(1 小时前、2 天前)", - "full": "始终完整日期", - "preview_today": "今天:", - "preview_this_week": "本周:", - "preview_older": "更早:" - }, - "date_locale": { - "label": "Date format region", - "description": "How numeric dates are ordered (day, month, year)", - "auto": "Automatic (match language)", - "iso": "ISO 8601 (YYYY-MM-DD)", - "dmy": "Day/Month/Year", - "mdy": "Month/Day/Year" - }, - "time_format": { - "label": "时间格式", - "description": "选择 12 小时制或 24 小时制", - "12h": "12 小时制", - "24h": "24 小时制" - }, - "first_day": { - "label": "一周的第一天", - "description": "从周日或周一开始一周", - "sunday": "周日", - "monday": "周一" - }, - "time_zone": { - "label": "Time zone", - "description": "Show email and calendar times in this time zone instead of the one reported by your browser", - "auto": "Automatic ({zone})", - "preview_now": "Now:" - } - }, - "email_behavior": { - "clear_search_on_folder_change": { - "label": "切换文件夹时清除搜索", - "description": "点击文件夹(收件箱、已发送等)会清除当前搜索并浏览该文件夹。关闭时,搜索会保留并在切换到的文件夹中重新运行。" - }, - "request_read_receipt": { - "label": "默认请求已读回执", - "description": "撰写新邮件时预先启用已读回执请求。" - }, - "read_receipt_response": { - "label": "响应已读回执请求", - "description": "当收到的邮件请求已读回执时的处理方式。", - "ask": "每次询问", - "always": "始终发送", - "never": "从不发送" - }, - "title": "邮件行为", - "description": "配置邮件的处理方式", - "mark_read": { - "label": "标记为已读", - "description": "何时将打开的邮件标记为已读", - "instant": "立即", - "delay_3s": "3 秒后", - "delay_5s": "5 秒后", - "never": "从不" - }, - "delete_action": { - "label": "删除操作", - "description": "删除邮件后的行为", - "trash": "移至已删除", - "trash_and_read": "移至已删除并标记为已读", - "permanent": "永久删除", - "warning": "邮件将被永久删除且无法恢复。此操作是不可逆转的。" - }, - "message_spacing": { - "label": "邮件边距", - "description": "阅读时正文周围的边距", - "auto": "自动(推荐)", - "always": "始终添加边距", - "edge": "无边距" - }, - "plain_text_font": { - "label": "纯文本字体", - "description": "用于纯文本邮件的字体", - "mono": "等宽", - "sans": "普通(与应用相同)" - }, - "archive_mode": { - "label": "归档方式", - "description": "归档时如何组织邮件", - "single": "单个文件夹", - "year": "按年归档", - "month": "按月归档", - "reorganize": "重新整理现有归档", - "reorganize_success": "{count, plural, =0 {没有需要整理的邮件} =1 {已整理 1 封邮件} other {已整理 # 封邮件}}", - "reorganize_error": "无法重新整理归档" - }, - "permanently_delete_junk": { - "label": "永久删除垃圾邮件", - "description": "从\"垃圾邮件\"文件夹中永久删除邮件,而不是将其移至\"已删除\"" - }, - "mail_layout": { - "label": "邮件布局", - "description": "在经典分栏阅读和 Gmail 风格的沉浸阅读之间选择", - "split": "分栏视图", - "split_description": "左侧显示邮件列表,右侧显示阅读窗格。", - "focus": "沉浸模式", - "focus_description": "每封邮件显示一行,打开邮件时使用全宽阅读视图,同时保留文件夹侧边栏。", - "horizontal": "底部阅读窗格", - "horizontal_description": "在顶部显示邮件列表,并在下方的阅读窗格中打开所选邮件。" - }, - "show_preview": { - "label": "显示预览文本", - "description": "在列表中显示邮件预览", - "focus_description": "在沉浸模式的单行邮件列表中显示预览文本" - }, - "disable_threading": { - "label": "禁用对话分组", - "description": "将邮件显示为独立消息,而不是按会话分组" - }, - "plain_text_mode": { - "label": "仅纯文本", - "description": "禁用富文本编辑器,并将所有邮件(包括回复和转发)以纯文本发送" - }, - "auto_select_reply_identity": { - "label": "自动选择回复地址", - "description": "回复时自动将发件人地址切换为最初收到该邮件的身份" - }, - "reply_identity_match": { - "label": "接收地址匹配", - "description": "哪些接收地址算作您的地址。“仅精确地址”只从已配置的身份中选择;“相同域名”还会将身份域名下的其他任何地址视为 catch-all 别名,并将发件人标头改写为该地址。如果这些地址是分发列表,请选择精确地址。", - "exact": "仅精确地址", - "domain": "我的域名下的任意地址" - }, - "signature_position": { - "label": "签名位置", - "description": "在回复和转发中插入签名的位置。位于引用文本上方时,可作为回复的自然结尾;位于下方时,保持原始邮件连贯。", - "above_quote": "引用文本之前", - "below_quote": "引用文本之后" - }, - "signature_separator": { - "label": "签名分隔符", - "description": "在签名前加入标准的 “-- ” 分隔行(RFC 3676)。如果希望正文直接连到签名,请关闭。" - }, - "sub_address_delimiter": { - "label": "子地址分隔符", - "description": "用于分隔用户名和子地址标签的字符。请选择与您的邮件服务器一致的分隔符(例如 user{delimiter}tag@domain.com)。", - "option": "{delimiter} (user{delimiter}tag@domain.com)", - "custom": "自定义…", - "custom_input_label": "自定义分隔字符" - }, - "attachment_click_action": { - "label": "附件单击操作", - "description": "选择点击附件时是预览还是直接下载", - "preview": "尽可能预览", - "download": "立即下载" - }, - "attachment_position": { - "label": "附件位置", - "description": "在邮件标题中的何处显示附件", - "beside-sender": "发件人旁", - "below-header": "标题下方" - }, - "emails_per_page": { - "10": "10 封邮件", - "25": "25 封邮件", - "50": "50 封邮件", - "100": "100 封邮件", - "label": "每页邮件数", - "description": "一次加载的邮件数量" - }, - "always_light_mode": { - "label": "始终以浅色模式显示邮件", - "description": "即使应用处于深色模式,也以浅色模式呈现邮件内容,避免深色模式转换问题" - }, - "external_content": { - "label": "外部内容", - "description": "如何处理图像和外部内容", - "ask": "每次询问", - "block": "始终阻止", - "allow": "始终允许" - }, - "trusted_senders": { - "label": "受信任发件人", - "description": "管理自动加载图像的发件人", - "count_zero": "无", - "count_one": "1 位发件人", - "count_other": "{count} 位发件人", - "modal_title": "受信任发件人", - "empty_title": "尚无受信任发件人", - "empty_description": "查看包含被阻止图像的邮件时,单击\"始终信任此发件人\"将其添加到此处。", - "add_manually": "手动添加", - "add_button": "添加", - "add_placeholder": "输入邮箱地址", - "search_placeholder": "搜索发件人...", - "no_results": "没有匹配的发件人", - "remove": "移除", - "close": "关闭", - "invalid_email": "请输入有效的邮箱地址", - "already_added": "该发件人已被信任", - "save_error": "保存失败 - 请查看联系人调试日志了解详情", - "use_address_book_label": "与通讯录同步", - "use_address_book_description": "将受信任发件人存入专用“受信任发件人”通讯录,以便在所有设备间同步" - }, - "hover_actions": { - "label": "快速悬停操作", - "description": "选择将鼠标悬停在列表中的邮件上时显示的快速操作", - "delete": "删除", - "star": "加星标/取消星标", - "mark_read": "标记已读/未读", - "archive": "归档", - "tag": "标签", - "spam": "标记为垃圾邮件", - "not_spam": "非垃圾邮件", - "none_selected": "未选择任何操作", - "mode_label": "显示模式", - "mode_inline": "内嵌", - "mode_floating": "浮动", - "corner_label": "浮动位置", - "corner_top-left": "左上", - "corner_top-right": "右上", - "corner_bottom-left": "左下", - "corner_bottom-right": "右下" - }, - "default_mail_program": { - "label": "默认邮件应用", - "description": "将 {appName} 注册为 mailto: 链接的默认邮件应用", - "button": "设为默认", - "success": "浏览器已提示设置为默认", - "error": "您的浏览器不支持此功能" - }, - "attachment_reminder": { - "label": "附件提醒", - "description": "当消息提到附件但未附加任何文件时,在发送前发出警告", - "keywords_label": "触发关键词", - "keywords_description": "在消息中发现时触发提醒的词语或短语", - "add_placeholder": "添加关键词...", - "add": "Add", - "remove": "删除" - }, - "empty_subject_warning": { - "label": "空主题提醒", - "description": "发送没有主题的邮件前请求确认。" - }, - "hide_inline_image_attachments": { - "label": "在附件列表中隐藏内嵌图片", - "description": "嵌入到邮件正文中的图片不会作为单独的附件显示" - }, - "attachment_image_previews": { - "label": "在附件中显示图片预览", - "description": "将图片附件显示为缩略图卡片,而不是通用文件图标" - }, - "send_delay": { - "label": "撤销发送 / 发送延迟", - "description": "通过服务器端短暂窗口延迟普通发送。", - "off": "关闭", - "seconds": "{seconds} 秒", - "unsupported": "当前账户未声明支持延迟发送。该设置仍会为其他账户保存。" - }, - "return_to_list_after_action": { - "label": "删除或标记为未读后返回列表", - "description": "删除打开的邮件或将其标记为未读后,返回邮件列表而不是打开下一封邮件。" - }, - "swipe_right_action": { - "label": "右滑操作(移动端)", - "description": "在触屏设备上,在列表中向右滑动邮件时执行的操作。" - }, - "swipe_left_action": { - "label": "左滑操作(移动端)", - "description": "在触屏设备上,在列表中向左滑动邮件时执行的操作。" - }, - "swipe_actions": { - "none": "无", - "archive": "归档", - "delete": "删除", - "mark_read": "切换已读", - "star": "切换星标", - "spam": "标记为垃圾邮件" - }, - "rtl_editing": { - "label": "从右到左编辑支持", - "description": "在编辑器工具栏中添加方向按钮,可将段落设置为从左到右或从右到左" - } - }, - "composer": { - "title": "邮件撰写", - "description": "配置邮件撰写相关设置", - "autosave": { - "label": "自动保存间隔", - "description": "自动保存草稿的频率", - "30s": "每 30 秒", - "1m": "每分钟", - "2m": "每 2 分钟", - "5m": "每 5 分钟" - }, - "send_confirmation": { - "label": "发送前确认", - "description": "发送邮件前要求确认" - }, - "default_reply": { - "label": "默认回复模式", - "description": "点击回复时的默认操作", - "reply": "回复", - "reply_all": "全部回复" - } - }, - "privacy": { - "title": "隐私与安全", - "description": "管理您的隐私和安全设置", - "external_images": { - "label": "阻止外部图像", - "description": "防止通过外部图像进行跟踪" - }, - "session_timeout": { - "label": "会话超时", - "description": "不活动后自动注销", - "never": "从不", - "30m": "30 分钟", - "1h": "1 小时", - "4h": "4 小时" - }, - "clear_cache": { - "label": "清除缓存", - "description": "删除缓存数据和临时文件", - "button": "清除缓存", - "confirm": "您确定要清除缓存吗?", - "success": "缓存清除成功" - } - }, - "account": { - "title": "账户", - "description": "查看您的账户信息", - "name_label": "显示名称", - "username_label": "用户名", - "account_type_label": "账户类型", - "auth_method_label": "认证方式", - "auth_method_oauth": "单点登录 (OAuth/OIDC)", - "auth_method_basic": "密码", - "demo_account": "演示账户", - "email": { - "label": "邮件", - "value": "{email}" - }, - "server": { - "label": "JMAP 服务器", - "value": "{server}" - }, - "storage": { - "label": "存储使用情况", - "used": "已使用 {used} / {total}", - "percentage": "已使用 {percent}%" - }, - "last_sync": { - "label": "上次同步", - "value": "{time}" - }, - "accounts": { - "title": "已登录账户", - "description": "拖动以重新排序账户在账户下拉菜单中的显示顺序", - "active": "当前活动账户", - "default_badge": "默认账户", - "set_default": "设为默认", - "switch_to": "切换到此账户", - "move_up": "上移", - "move_down": "下移", - "drag_handle": "拖动以重新排序", - "add": "添加账户" - }, - "shared_accounts": { - "title": "与我共享", - "description": "您可以管理的群组和共享账户。选择一个以编辑其筛选器、休假自动回复、日历和联系人。", - "shared_label": "共享账户" - } - }, - "security": { - "title": "账户安全", - "description": "管理您的密码、双因素身份验证和安全设置", - "detecting": "正在检测服务器功能...", - "not_available": "此邮件服务器不支持账号安全管理。可能未启用所需权限。详情请参阅文档。", - "password": { - "title": "更改密码", - "current": "当前密码", - "new": "新密码", - "confirm": "确认新密码", - "submit": "更改密码", - "success": "密码修改成功", - "error_title": "密码修改失败", - "error_mismatch": "新密码不匹配", - "error_min_length": "密码必须至少为 8 个字符", - "error_generic": "更改密码失败" - }, - "display_name": { - "label": "显示名称", - "description": "显示在服务器上的名称", - "placeholder": "输入您的显示名称", - "save": "保存", - "success": "显示名称已更新", - "error": "无法更新显示名称" - }, - "totp": { - "section_title": "双因素身份验证", - "label": "TOTP 身份验证", - "description": "使用基于时间的一次性密码添加额外的安全层", - "active": "启用", - "inactive": "未启用", - "enabled": "启用双因素身份验证", - "disabled": "禁用双因素身份验证", - "enable_error": "无法启用 2FA", - "disable_error": "无法禁用 2FA", - "setup_instructions": "请将此 URL 复制到身份验证器应用(Google Authenticator、Authy 等)中:", - "verification_code": "验证码", - "confirm": "确认", - "disable": "禁用", - "disable_confirm_prompt": "输入密码以禁用双重身份验证。", - "password_required": "需要密码", - "code_required": "需要验证码", - "code_invalid": "验证码无效。请检查您的身份验证应用并重试。" - }, - "app_passwords": { - "title": "应用密码", - "description": "为不支持双因素身份验证的应用创建密码", - "add": "添加", - "create": "创建", - "cancel": "取消", - "generate": "生成", - "name_label": "应用名称", - "name_placeholder": "例如雷鸟、iPhone 邮件", - "password_label": "密码(留空自动生成)", - "password_placeholder": "如果为空则自动生成", - "added": "应用密码已创建", - "removed": "应用密码已删除", - "add_error": "创建应用密码失败", - "remove_error": "无法删除应用密码", - "none": "未配置应用密码", - "done": "完成", - "expires_label": "过期时间(可选)", - "copy_now_warning": "立即复制此密码--它将不再显示。", - "allowed_ips_label": "允许的 IP(可选)", - "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", - "allowed_ips_hint": "用逗号或空格分隔。留空则允许任意 IP。" - }, - "api_keys": { - "title": "API 密钥", - "description": "为直接与服务器通信的脚本和集成创建 API 密钥", - "name_label": "密钥名称", - "name_placeholder": "如:备份脚本、CI runner", - "copy_now_warning": "请立即复制此 API 密钥 - 它将不会再次显示。", - "added": "API 密钥已创建", - "removed": "API 密钥已删除", - "add_error": "创建 API 密钥失败", - "remove_error": "删除 API 密钥失败", - "none": "未配置 API 密钥" - }, - "encryption": { - "section_title": "静态加密", - "label": "邮件加密", - "description": "加密服务器上存储的邮件以获得额外的隐私", - "active": "{type} 加密已启用", - "inactive": "未启用", - "enabled": "启用静态加密", - "disabled_success": "静态加密已禁用", - "error": "无法更新加密设置", - "algorithm_label": "加密算法", - "encrypt_on_append": "上传时加密新邮件", - "allow_spam_training": "在加密邮件前允许垃圾邮件训练", - "enable_encryption": "启用加密", - "enable_tooltip": "使用此密钥启用加密", - "configure_title": "使用此密钥启用加密", - "enable_button": "启用", - "disable_tooltip": "禁用加密", - "enabled_success": "静态加密已启用" - }, - "email_client": { - "title": "邮件客户端设置", - "description": "使用这些凭据配置桌面端或移动端邮件客户端(Thunderbird、Apple Mail、Outlook 等)", - "jmap_username_label": "JMAP 用户名", - "copy": "复制", - "copied": "已复制", - "password_instructions": "使用上面的 JMAP 用户名和应用密码登录您的邮件客户端。如果您还没有创建应用密码,请在上面的部分中创建。" - }, - "link_device": { - "title": "关联移动应用", - "description": "无需输入任何内容即可登录 Bulwark Mail 移动应用。在此生成二维码,然后在应用的登录界面扫描。", - "generate": "显示二维码", - "regenerate": "显示新的二维码", - "instructions": "打开 Bulwark Mail 应用,在登录界面点按\"扫描二维码\",然后将相机对准此处。", - "expires_in": "此二维码将在 {seconds} 秒后过期,且只能使用一次。", - "expired": "此二维码已过期。", - "generating": "生成中…", - "error": "无法创建配对码。请重试。" - }, - "public_keys": { - "name_label": "公钥名称", - "name_placeholder": "例如:我的密钥", - "key_label": "公钥(S/MIME 或 PGP ASCII 格式)", - "key_placeholder": "在此粘贴您的公钥", - "none": "未配置公钥" - } - }, - "identities": { - "title": "发送身份", - "description": "管理您可以发送邮件的邮箱地址", - "identities_count": { - "label": "你的身份", - "description": "配置用于发送的邮箱地址", - "count_zero": "暂无身份", - "count_one": "1 个身份", - "count_other": "{count} 个身份" - }, - "manage": "管理身份", - "sub_addressing": { - "label": "子寻址", - "description": "使用 user+tag@domain.com 这类标签来整理收到的邮件", - "learn_more": "了解更多" - } - }, - "vacation": { - "title": "假期自动回复", - "description": "在您不在时自动回复收到的邮件", - "loading": "正在加载自动回复设置…", - "not_supported": "您的邮件服务器不支持自动回复功能。", - "fetch_error": "加载自动回复设置失败,请重试。", - "status": { - "label": "自动回复", - "description": "向给您发送邮件的人自动发送回复", - "active": "已开启", - "inactive": "已关闭" - }, - "date_range": { - "title": "日期范围", - "description": "可选择仅在指定时间段内启用自动回复", - "start": "开始日期", - "start_description": "留空表示不限制开始时间", - "end": "结束日期", - "end_description": "留空表示不限制结束时间" - }, - "message": { - "title": "自动回复正文", - "description": "作为回复发送的邮件内容", - "subject_label": "主题", - "subject_description": "自动回复邮件的主题", - "subject_placeholder": "外出办公", - "body_label": "邮件正文", - "body_description": "纯文本邮件内容", - "body_placeholder": "感谢您的来信。我目前不在办公室,返回后会尽快回复您的邮件。", - "html_label": "带格式的消息(HTML)", - "html_description": "添加带链接和样式的格式化版本。邮件客户端无法显示时,收件人将看到上面的纯文本。", - "html_placeholder": "撰写带格式的外出自动回复…" - }, - "preview": { - "title": "预览", - "show": "显示预览", - "hide": "隐藏预览" - }, - "save": "保存更改", - "saving": "正在保存…", - "warnings": { - "end_before_start": "结束日期必须晚于开始日期", - "start_in_past": "开始日期已在过去", - "empty_body": "邮件正文为空--收件人将收到空白回复", - "subject_too_long": "主题对服务器来说太长(最多 {max} 字节)。", - "body_too_long": "消息对服务器来说太长(含格式最多 {max} 字节)。" - } - }, - "folders": { - "role_memos": "备忘录", - "title": "文件夹", - "description": "管理邮件文件夹并分配邮箱角色", - "folder_list": "您的文件夹", - "folder_list_description": "单击文件夹图标进行自定义", - "standard_roles": "标准文件夹角色", - "standard_roles_description": "指定哪些文件夹用于标准邮箱角色,例如收件箱、已发送、已删除等。", - "role_inbox": "收件箱", - "role_drafts": "草稿", - "role_sent": "已发送", - "role_trash": "已删除", - "role_junk": "垃圾邮件", - "role_archive": "归档", - "role_scheduled": "Scheduled", - "role_snoozed": "Snoozed", - "role_none": "无", - "create_folder": "创建文件夹", - "create_subfolder": "新建子文件夹", - "reorder": "拖动以重新排序", - "reorder_error": "文件夹重新排序失败", - "move_error": "移动文件夹失败", - "subfolder_of": "{name} 内", - "subfolder_name": "子文件夹名称", - "new_folder_name": "文件夹名称", - "rename": "重命名", - "change_icon": "更改图标", - "delete": "删除", - "confirm_delete": "您确定要删除\"{name}\"吗?此文件夹中的邮件将被移至\"已删除\"。", - "create": "创建", - "cancel": "取消", - "no_folders": "没有自定义文件夹", - "cannot_delete_role": "无法删除具有标准角色的文件夹。先删除角色。", - "folder_created": "文件夹已创建", - "folder_renamed": "文件夹已重命名", - "folder_deleted": "文件夹已删除", - "role_updated": "文件夹角色已更新", - "error_create": "创建文件夹失败", - "error_rename": "重命名文件夹失败", - "error_delete": "删除文件夹失败", - "error_delete_has_children": "无法删除文件夹:其中仍包含子文件夹。请先删除或移动它们。", - "error_delete_has_email": "无法删除文件夹:其中仍包含邮件。请先移动或删除这些邮件。", - "error_role": "无法更新文件夹角色" - }, - "advanced": { - "title": "高级", - "description": "高级选项与开发者设置", - "debug_mode": { - "label": "调试模式", - "description": "启用详细日志记录以进行故障排除" - }, - "debug_categories": { - "description": "选择要记录的类别。禁用不需要的类别以减少控制台噪音。", - "jmap": "JMAP 客户端", - "jmap_description": "邮箱操作、邮件获取与 JMAP 协议请求", - "calendar": "日历", - "calendar_description": "日历事件、导入和安排消息", - "tasks": "任务", - "tasks_description": "日历任务创建、获取和更新", - "auth": "认证", - "auth_description": "登录、TOTP、令牌交换和会话管理", - "filters": "过滤器", - "filters_description": "Sieve 过滤规则和假期自动回复脚本", - "email": "邮件查看", - "email_description": "邮件渲染、TNEF 处理与已读标记", - "push": "推送通知", - "push_description": "推送通知设置和发送", - "contacts": "联系人与通讯录", - "contacts_description": "联系人同步、通讯录操作与受信任发件人" - }, - "settings_sync": { - "label": "设置同步", - "description": "跨浏览器和设备同步您的设置" - }, - "sender_favicons": { - "label": "发件人图标", - "description": "使用企业发件人的网站图标作为头像" - }, - "show_avatars_in_junk": { - "label": "在垃圾邮件文件夹中显示头像", - "description": "在垃圾邮件文件夹中显示发件人的头像和网站图标。默认关闭,避免钓鱼邮件因熟悉的图标看起来更可信。" - }, - "keyboard_shortcuts": { - "label": "键盘快捷键", - "description": "查看可用快捷键", - "button": "查看快捷方式" - }, - "refresh_cache": { - "label": "Refresh cached data", - "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", - "button": "Refresh" - }, - "reset_settings": { - "label": "重置设置", - "description": "将所有设置恢复为默认值", - "button": "重置为默认值" - }, - "export_settings": { - "label": "导出设置", - "description": "将设置导出为 JSON 文件", - "button": "导出" - }, - "import_settings": { - "label": "导入设置", - "description": "从 JSON 文件导入设置", - "button": "导入" - }, - "about": { - "title": "Bulwark 网页邮箱" - } - }, - "sidebar_apps": { - "title": "侧边栏应用", - "description": "管理侧边栏中的自定义应用和链接", - "keep_loaded": "保持应用加载", - "keep_loaded_description": "在内联应用之间切换时,保持内联应用在后台运行以避免重新加载", - "managed_title": "由管理员提供", - "managed_description": "这些应用已为所有人配置,无法更改。", - "manage_title": "定制应用", - "manage_description": "从侧边栏添加、编辑或删除自定义应用" - }, - "contacts": { - "title": "联系人", - "description": "导入和导出联系人", - "import_label": "导入联系人", - "import_description": "从 vCard (.vcf) 文件导入联系人", - "export_label": "导出联系人", - "export_description": "将所有联系人导出为 vCard (.vcf) 文件", - "manage_title": "地址簿", - "manage_description": "重命名您的地址簿", - "no_address_books": "未找到地址簿", - "categories_title": "类别", - "categories_description": "重命名联系人类别", - "no_categories": "未找到类别", - "group_by_letter_description": "在联系人列表中显示按字母顺序排列的分节标题", - "group_by_letter_label": "按首字母分组", - "sort_by_last_name_label": "按姓氏排序", - "sort_by_last_name_description": "按姓氏排列联系人列表,使家庭成员显示在一起" - }, - "downloads": { - "title": "下载", - "description": "自定义下载的邮件和附件的命名方式。", - "reset": "恢复默认", - "preview": "预览:", - "email_template": { - "label": "邮件文件名 (.eml)", - "description": "导出邮件或将其拖到文件系统时使用的模板。扩展名 .eml 会自动添加。" - }, - "attachment_template": { - "label": "附件文件名", - "description": "下载或拖出附件时使用的模板。如果省略 '{filename}' 和 '{ext}',将保留原始扩展名。" - }, - "bundle_template": { - "label": "多邮件 .zip 文件名", - "description": "将多个选中的邮件作为单个 .zip 拖出或下载时使用的模板。扩展名 .zip 会自动添加。" - }, - "spaces": { - "label": "空格", - "description": "用其他字符替换最终文件名中的空格。", - "keep": "保留空格", - "underscore": "替换为 _", - "dash": "替换为 -" - }, - "lowercase": { - "label": "小写", - "description": "将整个文件名强制转换为小写。" - }, - "strip_diacritics": { - "label": "去除变音符号", - "description": "将带重音的字母转换为对应的 ASCII 字符(ä → a, é → e)。对处理 Unicode 文件名有问题的工具很有用。" - }, - "collapse_separators": { - "label": "合并重复的分隔符", - "description": "将连续的空格、下划线或短横线合并为单个字符。" - }, - "after_export": { - "label": "导出后", - "description": "将邮件导出为 .eml 后可选择移动到其他位置。", - "keep": "保留在邮箱中", - "archive": "移动到归档", - "trash": "移动到回收站" - } - }, - "filters": { - "title": "邮件过滤器", - "description": "创建规则,自动整理、标记和管理收到的邮件", - "add_rule": "添加规则", - "no_rules": "暂无过滤规则", - "no_rules_description": "创建规则以自动整理收到的邮件", - "vacation_active": "假期自动回复已开启", - "vacation_active_description": "已启用传入消息的自动回复", - "vacation_configure": "配置", - "edit_rule": "编辑规则", - "new_rule": "新规则", - "delete_rule": "删除规则", - "delete_confirm": "您确定要删除此规则吗?", - "enable": "启用", - "disable": "禁用", - "raw_editor": "原始 Sieve 编辑器", - "raw_editor_warning": "编辑原始 Sieve 脚本可能会破坏可视化规则编辑。此处所做的更改会覆盖视觉生成器。", - "validate": "验证", - "validation_success": "脚本有效", - "validation_error": "脚本有错误", - "save": "保存规则", - "saving": "保存中...", - "saved": "过滤器保存成功", - "save_failed": "无法保存过滤器", - "loading": "正在加载过滤器...", - "not_supported": "您的邮件服务器不支持邮件过滤器。", - "rule_name": "规则名称", - "rule_name_placeholder": "例如,整理订阅邮件", - "match_all": "匹配所有条件", - "match_any": "匹配任何条件", - "conditions": "条件", - "add_condition": "添加条件", - "actions": "操作", - "add_action": "添加操作", - "stop_processing": "停止处理后续规则", - "include_spam": "同时移动被标记为垃圾邮件的邮件", - "attachment_type_placeholder": "例如:pdf、doc、jpg", - "value_placeholder_multi": "值(多个用逗号分隔)", - "condition_fields": { - "attachment": "附件", - "from": "发件人", - "to": "收件人", - "cc": "抄送", - "subject": "主题", - "header": "自定义标头", - "size": "大小", - "body": "正文" - }, - "comparators": { - "has_any": "存在", - "has_type": "类型为", - "contains": "包含", - "not_contains": "不包含", - "is": "等于", - "not_is": "不是", - "starts_with": "以…开头", - "ends_with": "以…结尾", - "matches": "匹配模式", - "greater_than": "大于", - "less_than": "小于" - }, - "action_types": { - "move": "移至文件夹", - "copy": "复制到文件夹", - "forward": "转发至", - "mark_read": "标记为已读", - "star": "加星标", - "add_label": "添加标签", - "discard": "丢弃(静默删除)", - "reject": "拒绝并留言", - "keep": "保留在收件箱中", - "stop": "停止处理" - }, - "move_to_folder": "选择文件夹", - "copy_to_folder": "选择文件夹", - "forward_to": "转发至邮箱地址", - "forward_placeholder": "email@example.com", - "forward_keep_copy": "保留副本", - "forward_limit": "此服务器每封邮件的转发上限:{count}。多余的转发将被跳过。", - "reject_message": "拒绝留言", - "reject_placeholder": "您的邮件已被拒绝", - "label_name": "标签名称", - "label_placeholder": "选择标签", - "header_name": "标头名称", - "header_placeholder": "例如,X-Mailing-List", - "size_bytes": "大小(以字节为单位)", - "size_placeholder": "例如,1000000", - "system_managed": "系统管理的规则", - "opaque_warning": "该脚本是在可视化编辑器之外编辑的。仅可使用原始 Sieve 编辑。", - "open_sieve_editor": "打开原始 Sieve 编辑器", - "fetch_error": "加载过滤器失败", - "expanded_view": "展开视图", - "expanded_view_description": "显示包含详细条件和操作的过滤规则", - "if": "如果", - "then": "然后", - "match_all_conditions": "全部匹配", - "match_any_condition": "任一匹配", - "and": "和", - "or": "或", - "cancel": "取消", - "confirm_delete": "删除", - "rule_list": "过滤规则", - "drag_to_reorder": "拖动以重新排序", - "match_type": "匹配类型", - "reset_to_visual": "重置为可视化编辑器", - "reset_warning": "这将丢弃当前脚本并重新开始。", - "confirm_reset": "重置", - "validation_empty_name": "规则名称为必填项", - "validation_empty_conditions": "至少需要一个具有值的条件", - "validation_empty_actions": "至少需要执行一项操作", - "templates_section": "从模板开始", - "template_newsletters": "将订阅邮件移至文件夹", - "template_receipts": "自动归档收据", - "template_important": "标记重要邮件", - "template_notifications": "过滤通知", - "sieve_editor": { - "title": "Sieve 脚本编辑器", - "warning": "编辑原始 Sieve 脚本可能会破坏可视化规则编辑。此处所做的更改会覆盖视觉生成器。", - "script_content": "Sieve 脚本", - "valid": "脚本有效", - "invalid": "脚本有错误", - "save_warning": "保存将覆盖所有可视化规则,此操作无法撤销。再次点击\"保存\"以确认。", - "validating": "正在验证...", - "validate": "验证", - "cancel": "取消", - "save": "保存", - "confirm_save": "确认保存", - "validation_failed": "验证请求失败" - }, - "rule_summary": { - "conditions_count": "{count, plural, one {# 个条件} other {# 个条件}}", - "actions_count": "{count, plural, one {# 个操作} other {# 个操作}}" - }, - "origin_external": "外部", - "managed_by_tooltip": "由 {source} 管理。请在该应用中编辑,或使用原始 Sieve 编辑器。" - }, - "templates": { - "title": "邮件模板", - "description": "使用占位符变量创建可重用的邮件模板", - "add": "新模板", - "edit": "编辑模板", - "name": "模板名称", - "name_placeholder": "例如,后续邮件", - "category": "类别", - "category_placeholder": "例如,工作、个人", - "subject": "主题", - "subject_placeholder": "邮件主题行", - "body": "正文", - "body_placeholder": "邮件正文内容...", - "recipients_placeholder": "email@example.com", - "identity": "发送为", - "default_identity": "默认身份", - "favorite": "收藏", - "cancel": "取消", - "create": "创建", - "update": "更新", - "confirm_delete": "删除", - "no_templates": "还没有模板", - "manage": "管理模板", - "count": "{count, plural, one {# 个模板} other {# 个模板}}", - "export_import": "导出和导入", - "export_import_description": "备份您的模板或将其传输到其他设备", - "export": "导出", - "import": "导入", - "validation": { - "empty": "模板名称为必填项", - "too_long": "模板名称不得超过 200 个字符" - } - }, - "files": { - "display": { - "title": "显示", - "description": "配置文件和文件夹的显示方式" - }, - "folder_layout": { - "label": "文件夹导航", - "description": "选择文件夹的显示方式:与文件列表内嵌显示,或在侧边栏树状显示", - "inline": "内嵌", - "sidebar": "侧边栏" - }, - "default_view": { - "label": "默认视图", - "description": "在网格布局和列表布局之间进行选择", - "list": "列表", - "grid": "网格" - }, - "default_sort": { - "label": "默认排序", - "description": "选择文件的默认排序规则", - "name": "名称", - "size": "大小", - "modified": "修改时间" - }, - "sort_direction": { - "label": "排序方式", - "description": "选择升序或降序", - "ascending": "升序", - "descending": "降序" - }, - "icons": { - "title": "图标", - "description": "配置文件图标外观" - }, - "show_icons": { - "label": "显示文件图标", - "description": "在文件和文件夹旁边显示图标" - }, - "colored_icons": { - "label": "彩色图标", - "description": "使用彩色图标而不是单色图标" - }, - "show_thumbnails": { - "label": "显示缩略图", - "description": "显示图像预览而不是图像文件的图标" - }, - "behavior": { - "title": "行为", - "description": "配置文件浏览器行为" - }, - "show_hidden": { - "label": "显示隐藏文件", - "description": "显示以点开头的文件和文件夹" - }, - "preview": { - "label": "预览" - } - }, - "search_placeholder": "搜索设置", - "search_clear": "清除搜索", - "search_no_results": "没有匹配的设置", - "scoped": { - "back": "返回我的账户", - "managing": "管理:{name}" - } - }, - "errors": { - "page_error_title": "出了点问题", - "page_error_description": "我们遇到了意外错误。请重试或返回主页。", - "sidebar_error": "无法加载邮箱", - "email_list_error": "无法加载邮件", - "viewer_error_title": "无法显示邮件", - "viewer_error_description": "呈现此邮件时出现问题。它可能包含不受支持的内容。", - "composer_error": "无法加载邮件撰写", - "settings_error_title": "设置不可用", - "settings_error_description": "无法加载设置。您的偏好设置可能不会被保存。", - "try_again": "再试一次", - "reload": "重新加载", - "reload_emails": "重新加载邮件", - "reload_settings": "重新加载设置", - "retry": "重试", - "go_home": "前往收件箱" - }, - "context_menu": { - "reply": "回复", - "reply_all": "全部回复", - "forward": "转发", - "mark_read": "标记为已读", - "mark_unread": "标记为未读", - "star": "加星标", - "unstar": "取消星标", - "pin": "固定", - "unpin": "取消固定", - "move_to": "移动到…", - "archive": "归档", - "delete": "删除", - "mark_as_spam": "举报垃圾邮件", - "not_spam": "不是垃圾邮件", - "tag": "标签", - "items_selected": "已选择 {count} 封邮件", - "edit_draft": "编辑草稿", - "cancel_scheduled_send": "取消发送", - "reschedule_send": "重新计划", - "cancel_and_edit": "取消并编辑", - "cancel_and_compose_again": "取消并重新撰写" - }, - "mailbox_context_menu": { - "mark_folder_read": "将文件夹标记为已读", - "mark_folder_tree_read": "将文件夹及子文件夹标记为已读", - "mark_all_folders_read": "将所有文件夹标记为已读", - "new_subfolder": "新建子文件夹...", - "new_folder": "新建文件夹...", - "rename": "重命名...", - "share": "共享...", - "import_email": "导入 .eml 或 .zip...", - "empty_folder": "清空文件夹", - "empty_folder_generic": "清空文件夹", - "delete_folder": "删除文件夹", - "refresh": "刷新", - "mark_all_confirm_title": "将所有文件夹标记为已读", - "mark_all_confirm_message": "将您个人账户中的所有未读邮件标记为已读?", - "delete_confirm_title": "删除文件夹", - "delete_confirm_message": "永久删除文件夹 \"{name}\"?此操作无法撤销。", - "prompt_new_subfolder": "请输入新子文件夹的名称。", - "prompt_new_folder": "请输入新文件夹的名称。", - "prompt_rename": "请输入此文件夹的新名称。", - "toast_marked_read": "文件夹已标记为已读", - "toast_marked_read_count": "已将 {count, plural, one {1 封邮件} other {# 封邮件}} 标记为已读", - "toast_already_read": "没有未读邮件", - "toast_marked_all_read": "所有文件夹已标记为已读", - "toast_emptied": "文件夹已清空", - "toast_folder_created": "文件夹已创建", - "toast_folder_renamed": "文件夹已重命名", - "toast_folder_deleted": "文件夹已删除", - "toast_error_mark_read": "标记为已读失败", - "toast_error_empty": "清空文件夹失败", - "toast_error_create": "创建文件夹失败", - "toast_error_rename": "重命名文件夹失败", - "toast_error_delete": "删除文件夹失败", - "toast_error_delete_has_children": "文件夹包含子文件夹,请先将其删除。", - "toast_error_delete_has_email": "文件夹不为空,请先清空它。", - "delete_with_contents_title": "文件夹不为空", - "delete_with_contents_message": "文件夹“{name}”中仍有 {count} 封邮件。删除该文件夹及其所有邮件?此操作无法撤销。", - "delete_with_contents_confirm": "删除文件夹和邮件", - "placeholder_folder_name": "文件夹名称", - "create": "创建", - "rename_confirm": "重命名" - }, - "shortcuts": { - "title": "键盘快捷键", - "tip": "按?随时显示此帮助", - "sections": { - "navigation": "导航", - "actions": "邮件操作", - "global": "全局", - "threads": "会话", - "composer": "邮件撰写" - }, - "navigation": { - "next_email": "下一封邮件", - "previous_email": "上一封邮件", - "open_email": "打开邮件", - "close_email": "关闭/取消选择" - }, - "actions": { - "reply": "回复", - "reply_all": "全部回复", - "forward": "转发", - "star": "添加或取消星标", - "archive": "归档", - "delete": "删除", - "mark_unread": "标记为未读", - "mark_read": "标记为已读", - "toggle_spam": "举报垃圾邮件/非垃圾邮件" - }, - "global": { - "compose": "撰写新邮件", - "search": "聚焦搜索框", - "help": "显示快捷方式", - "refresh": "刷新邮件", - "select_all": "全选", - "global_search": "全局搜索" - }, - "threads": { - "expand_collapse": "展开/折叠会话" - }, - "composer": { - "send": "发送邮件", - "schedule_send": "定时发送", - "template_picker": "打开模板选择器" - } - }, - "threads": { - "messages_one": "{count} 条消息", - "messages_other": "{count} 条消息", - "messages_tooltip": "{count, plural, one {此会话中有 # 条消息} other {此会话中有 # 条消息}}", - "expand": "展开会话", - "collapse": "折叠会话", - "loading": "正在加载会话...", - "mark_read": "将会话标记为已读", - "mark_unread": "将会话标记为未读", - "archive": "归档会话", - "delete": "删除会话", - "star": "会话加星标", - "unstar": "取消会话星标", - "toggle_thread": "展开/折叠会话" - }, - "identities": { - "modal_title": "管理发送身份", - "create_new": "创建新身份", - "edit_identity": "编辑身份", - "delete_confirm": "删除这个身份?此操作无法撤消。", - "cannot_delete": "该身份无法删除", - "primary_identity": "主要", - "set_as_primary": "设为主要", - "no_identities": "没有找到身份信息", - "display": { - "reply_to": "回复地址:", - "bcc": "密送:", - "signature": "签名:", - "preview": "预览:" - }, - "validation_errors": { - "invalid_emails": "无效邮箱地址:{emails}", - "unknown_error": "未知错误" - }, - "form": { - "name_label": "显示名称", - "name_placeholder": "例如,工作邮箱、个人邮箱", - "name_required": "名称为必填项", - "email_label": "邮箱地址", - "email_placeholder": "your.email@example.com", - "email_required": "邮箱地址为必填项", - "email_invalid": "请输入有效的邮箱地址", - "email_immutable": "邮箱地址创建后无法更改", - "reply_to_label": "回复地址(可选)", - "reply_to_placeholder": "reply@example.com", - "bcc_label": "自动密送(可选)", - "bcc_placeholder": "archive@example.com", - "text_signature_label": "文字签名", - "html_signature_label": "HTML 签名", - "signature_byte_counter": "{bytes} / {max} 字节", - "signature_byte_limit_reached": "已达服务器限制", - "save": "保存身份", - "cancel": "取消", - "creating": "创建中...", - "updating": "更新中..." - }, - "sub_address": { - "button_tooltip": "使用子地址", - "popover_title": "添加子地址标签", - "tag_input_placeholder": "输入标签(例如购物)", - "preview_label": "预览:", - "recent_tags": "最近的标签", - "suggested_tags": "建议", - "use_address": "使用此地址", - "invalid_tag": "标签只能是字母数字和破折号", - "tag_too_long": "标签不得超过 30 个字符", - "help_text": "发送到 user{delimiter}tag@domain.com 的邮件将送达您的收件箱", - "validation": { - "empty": "标签不能为空", - "too_long": "标签不得超过 {max} 个字符", - "invalid_chars": "标签只能包含字母、数字和破折号" - } - }, - "badge": { - "sent_via": "通过", - "sub_address_tag": "使用子地址发送:{tag}", - "identity_name": "使用身份发送:{name}", - "identity_short": "通过 {name}", - "subaddress_tag": "+{tag}" - }, - "delete_button": "删除", - "delete_confirm_title": "删除身份" - }, - "templates": { - "picker_title": "选择一个模板", - "search_placeholder": "搜索模板...", - "section_favorites": "收藏夹", - "section_recent": "最近使用", - "section_uncategorized": "其他", - "no_templates": "还没有模板", - "no_results": "没有找到模板", - "fill_placeholders": "填充占位符值", - "enter_value": "输入一个值...", - "preview": "预览", - "insert_with_values": "插入并填充", - "insert_raw": "插入原始模板", - "copy_suffix": "(复制)", - "placeholder": "变量", - "placeholders": { - "recipient_name": "收件人姓名", - "company": "公司名称", - "date": "当前日期", - "day_of_week": "星期几", - "sender_name": "你的名字" - } - }, - "contacts": { - "title": "联系人", - "search_placeholder": "搜索联系人...", - "create_new": "新联系人", - "no_category": "没有类别", - "rename_category": "重命名类别", - "category_name_label": "类别名称", - "category_renamed": "类别已重命名", - "category_rename_failed": "重命名类别失败", - "category_added": "联系人已添加至 {name}", - "category_added_plural": "{count} 联系人已添加到 {name}", - "empty_state": "还没有联系人", - "empty_state_title": "还没有联系人", - "empty_state_subtitle": "创建您的第一个联系人或从 vCard 文件导入", - "empty_search": "没有符合您搜索条件的联系人", - "empty_search_hint": "尝试不同的搜索词", - "empty_filtered": "没有符合您筛选条件的联系人", - "empty_filtered_hint": "请调整或清除筛选条件", - "clear_search": "清除搜索", - "import_vcard": "导入 vCard", - "delete_confirm_title": "删除联系人", - "delete_confirm": "您确定要删除此联系人吗?", - "local_mode": "联系人存储在本地(服务器不支持 JMAP 联系人)", - "back_to_contacts": "返回联系人", - "back_to_email": "返回邮件", - "tabs": { - "all": "全部", - "groups": "群组" - }, - "shared": { - "title": "共享" - }, - "address_books": { - "create": "新建通讯录", - "created": "通讯录已创建", - "create_failed": "创建通讯录失败", - "title": "我的地址簿", - "shared_prefix": "共享:{name}", - "moved": "联系人已移至 {name}", - "moved_plural": "{count} 联系人已移至 {name}", - "move_failed": "无法移动联系人", - "address_book": "地址簿", - "rename": "重命名地址簿", - "name_label": "地址簿名称", - "renamed": "地址簿已重命名", - "rename_failed": "重命名地址簿失败", - "default": "默认", - "set_default": "设为默认", - "default_updated": "默认地址簿已更新", - "set_default_failed": "无法设置默认地址簿", - "manage": "管理地址簿", - "share": "共享通讯录", - "new_contact_in_book": "在此通讯录中新建联系人", - "delete": "删除通讯录", - "confirm_delete": "删除「{name}」?此通讯录中的所有联系人将被移除。", - "deleted": "通讯录已删除", - "delete_failed": "删除通讯录失败" - }, - "detail": { - "emails": "邮箱地址", - "phones": "电话号码", - "organizations": "组织机构", - "addresses": "地址", - "notes": "笔记", - "titles": "头衔和角色", - "online_services": "在线服务", - "anniversaries": "周年纪念日", - "personal_info": "个人信息", - "languages": "语言", - "categories": "类别", - "related_contacts": "相关联系人", - "crypto_keys": "加密密钥", - "cert_issuer": "颁发者", - "cert_expires": "过期", - "cert_expired": "已到期", - "cert_fingerprint": "指纹", - "cert_algorithm": "算法", - "import_to_smime": "导入到 S/MIME", - "cert_already_imported": "已导入至 S/MIME", - "cert_imported": "证书导入到 S/MIME 存储", - "cert_import_failed": "导入证书失败", - "no_contact_selected": "选择联系人以查看详细信息", - "compose_email": "撰写邮件", - "copy_email": "复制邮箱地址", - "copy_phone": "复制电话号码", - "copy_url": "复制 URL", - "copied": "已复制到剪贴板", - "copy_failed": "无法复制到剪贴板", - "created": "已创建", - "updated": "最后更新", - "timezone": "时区", - "anniversary_birth": "生日", - "anniversary_death": "已故", - "anniversary_wedding": "周年纪念日", - "anniversary_other": "其他", - "personal_expertise": "专业知识", - "personal_hobby": "爱好", - "personal_interest": "兴趣", - "personal_other": "其他", - "gender": "性别", - "gender_masculine": "男性", - "gender_feminine": "女性", - "gender_other": "其他", - "gender_none": "不适用", - "gender_unknown": "未知", - "calendar": "日历", - "calendar_uri": "日历 URL", - "scheduling_uri": "调度 URL", - "freebusy_uri": "空闲/忙碌 URL", - "section_contact": "Contact details", - "section_work": "Work", - "section_personal": "Personal", - "email_default_label": "Email", - "phone_default_label": "Phone", - "address_default_label": "Address", - "online_service_default_label": "Online", - "organization_label": "Organization", - "title_label": "Title", - "role_label": "Role", - "language_label": "Language", - "related_default_label": "Related", - "more_actions": "更多操作", - "age_years": "{count}岁", - "years_since": "{count}年前" - }, - "activity": { - "recent_emails": "Recent Emails", - "upcoming_events": "Upcoming Events", - "no_emails": "No recent emails", - "no_events": "No upcoming events", - "no_subject": "(No subject)", - "no_title": "(No title)", - "load_failed": "Failed to load", - "unknown_sender": "Unknown sender", - "all_day": "All day" - }, - "form": { - "create_title": "新联系人", - "edit_title": "编辑联系人", - "section_address_book": "地址簿", - "select_address_book": "选择地址簿...", - "section_identity": "姓名和身份", - "contact_type": "联系人类型", - "type_person": "个人", - "type_organization": "组织", - "section_work": "工作与组织", - "prefix": "前缀", - "prefix_placeholder": "博士、先生、女士", - "given_name": "名", - "middle_name": "中间名", - "surname": "姓", - "suffix": "后缀", - "suffix_placeholder": "Jr.、Sr.、III", - "nickname": "昵称", - "nickname_placeholder": "昵称", - "email": "邮件", - "email_placeholder": "email@example.com", - "phone": "电话", - "phone_placeholder": "+1 234 567 890", - "phone_type": "类型", - "phone_voice": "语音", - "phone_cell": "手机", - "phone_fax": "传真", - "phone_pager": "寻呼机", - "phone_video": "视频", - "phone_text": "短信", - "organization": "组织", - "organization_placeholder": "公司名称", - "department": "部门", - "department_placeholder": "部门", - "job_title": "职称", - "job_title_placeholder": "例如,软件工程师", - "role": "角色", - "role_placeholder": "例如,团队负责人", - "addresses": "地址", - "add_address": "添加地址", - "street": "街道", - "city": "城市", - "region": "州/地区", - "postcode": "邮政编码", - "country": "国家", - "online_services": "在线服务", - "add_online_service": "添加在线服务", - "url_placeholder": "https://...", - "service_placeholder": "服务", - "anniversaries": "周年纪念日", - "add_anniversary": "添加日期", - "anniversary_birth": "生日", - "anniversary_wedding": "周年纪念日", - "anniversary_death": "忌日", - "anniversary_other": "其他", - "personal_info": "个人信息", - "add_personal_info": "添加条目", - "personal_info_placeholder": "例如,摄影", - "personal_expertise": "专业知识", - "personal_hobby": "爱好", - "personal_interest": "兴趣", - "personal_other": "其他", - "level": "等级", - "level_high": "高", - "level_medium": "中", - "level_low": "低", - "categories": "类别", - "categories_placeholder": "例如,家人、朋友、同事", - "categories_hint": "输入搜索或添加类别", - "category_add": "添加", - "note": "笔记", - "note_placeholder": "添加注释...", - "gender": "性别", - "gender_sex": "性别", - "gender_male": "男性", - "gender_female": "女性", - "gender_other": "其他", - "gender_none": "不适用", - "gender_unknown": "未知", - "gender_identity": "性别认同", - "gender_identity_placeholder": "性别认同...", - "calendar": "日历", - "calendar_uri": "日历 URL", - "scheduling_uri": "调度 URL", - "freebusy_uri": "空闲/忙碌 URL", - "context_work": "工作", - "context_private": "个人", - "add_email": "添加邮箱地址", - "add_phone": "添加电话", - "save": "保存", - "cancel": "取消", - "creating": "创建中...", - "updating": "更新中...", - "name_required": "请输入名字、姓氏或组织", - "email_invalid": "请输入有效的邮箱地址", - "email_error_inline": "邮箱地址格式无效", - "save_failed": "保存联系人失败", - "delete": "删除", - "upload_photo": "上传照片", - "remove_photo": "移除照片", - "photo_hint": "JPG 或 PNG, 最大 10MB。将会调整大小。", - "photo_too_large": "图像过大 (最大 10MB)", - "photo_invalid": "无效的图像文件", - "change_photo": "更改" - }, - "groups": { - "create": "新建群组", - "edit": "编辑群组", - "empty": "还没有群组", - "delete_confirm_title": "删除群组", - "delete_confirm": "您确定要删除该群组吗?", - "name_label": "群组名称", - "name_placeholder": "例如,团队、家庭", - "name_required": "群组名称为必填项", - "save_failed": "保存群组失败", - "members_label": "成员", - "search_members": "搜索联系人以添加...", - "no_members": "该群组中没有成员", - "member_count": "{count, plural, =0 {无成员} one {1 位成员} other {# 位成员}}", - "send_email": "Send email to group", - "send_email_to": "To", - "send_email_cc": "Cc", - "send_email_bcc": "Bcc", - "no_member_emails": "This group has no members with an email address." - }, - "import": { - "title": "导入联系人", - "drop_hint": "单击以选择 vCard 文件", - "file_types": ".vcf 或 .vcard 文件", - "no_contacts": "文件中未找到联系人", - "parse_error": "无法解析 vCard 文件", - "found": "{count, plural, one {找到 1 位联系人} other {找到 # 位联系人}}", - "duplicate": "重复项", - "select_all": "全选", - "deselect_all": "取消全选", - "selected": "{count, plural, one {已选择 1 项} other {已选择 # 项}}", - "import_button": "导入", - "importing": "导入中...", - "success": "{count, plural, one {已导入 1 位联系人} other {已导入 # 位联系人}}", - "failed": "导入失败", - "close": "关闭", - "file_too_large": "文件太大(最大 5 MB)" - }, - "export": { - "title": "导出联系人", - "success": "{count, plural, one {已导出 1 位联系人} other {已导出 # 位联系人}}" - }, - "bulk": { - "selected": "{count, plural, one {已选择 1 项} other {已选择 # 项}}", - "select_all": "全选", - "delete": "删除", - "delete_confirm_title": "删除联系人", - "delete_confirm": "删除{count, plural, one {1 位联系人} other {# 位联系人}}?", - "deleted": "{count, plural, one {已删除 1 位联系人} other {已删除 # 位联系人}}", - "add_to_group": "添加到群组", - "choose_group": "选择一个组", - "adding_contacts": "正在添加{count, plural, one {1 位联系人} other {# 位联系人}}", - "added_to_group": "联系人已添加至群组", - "export": "导出", - "clear": "清除选择" - }, - "toast": { - "created": "已创建联系人", - "updated": "联系人已更新", - "deleted": "联系人已删除", - "error_create": "创建联系人失败", - "error_update": "无法更新联系人", - "error_delete": "删除联系人失败" - }, - "context_menu": { - "open": "打开", - "edit": "编辑", - "send_email": "发送邮件", - "add_to_group": "加入群组", - "export_vcard": "导出为 vCard", - "delete": "删除", - "call": "拨打电话", - "duplicate": "复制", - "print": "打印" - }, - "filters": { - "toggle": "筛选", - "select": "选择", - "clear": "清除", - "close": "关闭", - "title": "高级筛选", - "organization": "公司", - "organization_placeholder": "例如 Acme", - "job_title": "职位", - "job_title_placeholder": "例如 设计师", - "location": "所在地", - "location_placeholder": "城市或国家", - "email_domain": "邮箱域名", - "email_domain_placeholder": "example.com", - "birthday_month": "生日月份", - "any_month": "任意月份", - "has_email": "有邮箱", - "has_phone": "有电话", - "has_photo": "有照片", - "sort_by": "排序方式", - "sort_first_name": "名", - "sort_last_name": "姓" - }, - "open_categories": "打开分类" - }, - "calendar": { - "title": "日历", - "back_to_email": "返回邮件", - "back_to_month": "返回月份", - "my_calendars": "日历", - "birthday_calendar": "生日", - "mini_calendar_change": "点击更改月份", - "views": { - "month": "月", - "week": "周", - "day": "天", - "agenda": "议程", - "today": "今天", - "month_hint": "月 (m)", - "week_hint": "周 (w)", - "day_hint": "天 (d)", - "agenda_hint": "议程 (a)", - "tasks": "任务", - "tasks_hint": "任务 (k)" - }, - "events": { - "create": "创建活动", - "edit": "编辑活动", - "delete": "删除活动", - "details": "活动详情", - "no_events": "没有活动", - "all_day": "全天", - "more": "+{count} 更多", - "no_title": "(无标题)", - "resize": "调整事件大小", - "duplicate": "复制", - "today_header": "今天", - "tomorrow_header": "明天", - "agenda_show_earlier": "显示更早的日程", - "agenda_loading": "加载中…", - "agenda_range_start": "显示 {date} 起的日程", - "agenda_range_end": "显示至 {date} 的日程", - "export_ics": "导出为 .ics", - "copy_title": "复制标题", - "copy_link": "复制会议链接", - "go_to_today": "前往今天", - "new_all_day_event": "新建全天事件", - "new_event": "新建事件", - "new_task": "新建任务" - }, - "detail": { - "add_note": "添加注释...", - "save_note": "保存", - "note_saved": "注释已添加", - "open_link": "打开链接", - "meeting_link": "会议链接", - "tentative": "暂定", - "cancelled": "已取消", - "delete_confirm": "删除这个活动吗?" - }, - "form": { - "title": "标题", - "description": "描述", - "location": "地点", - "meeting_link": "会议链接", - "start_date": "开始日期", - "end_date": "结束日期", - "start_time": "开始时间", - "end_time": "结束时间", - "all_day_event": "全天活动", - "calendar_select": "日历", - "save": "保存", - "cancel": "取消", - "delete_confirm": "您确定要删除此活动吗?", - "color": "颜色" - }, - "participants": { - "availability": { - "title": "空闲状态", - "free": "空闲", - "busy": "忙碌", - "tentative": "暂定", - "unknown": "不是此服务器上的用户", - "checking": "正在检查…" - }, - "title": "参加者", - "add": "添加参与者", - "organizer": "组织者", - "attendee": "出席者", - "accepted": "已接受", - "declined": "已拒绝", - "tentative": "暂定", - "needs_action": "待处理", - "remove": "移除", - "edit": "编辑", - "email_placeholder": "添加邮箱地址或搜索联系人", - "send_invitations": "向参与者发送邀请", - "status_summary": "{accepted} 已接受,{pending} 待处理", - "invited_by": "来自 {name} 的邀请", - "respond_below": "使用下面的按钮回复", - "rsvp_label": "您的回应", - "cancel_notification": "参与者将收到取消通知", - "you_organizer": "您是组织者", - "you_attendee": "您是与会者", - "no_participants": "没有参加者", - "count": "{count, plural, one {# 位参与者} other {# 位参与者}}" - }, - "recurrence": { - "title": "重复", - "none": "不重复", - "daily": "每天", - "weekly": "每周", - "monthly": "每月", - "yearly": "每年", - "every_n_days": "每 {count} 天", - "every_n_weeks": "每 {count} 周", - "every_n_months": "每 {count} 个月", - "until": "直到", - "occurrences": "{count} 次出现", - "custom": "自定义…", - "edit_custom": "编辑自定义重复规则", - "every_n_years": "每 {count} 年", - "on_days": "{days}", - "on_day_n": "{day}日", - "on_the_nth": "{nth}{day}", - "in_month": "{month}", - "nth_1": "第一个", - "nth_2": "第二个", - "nth_3": "第三个", - "nth_4": "第四个", - "nth_last": "最后一个", - "editor_freq_day": "天", - "editor_freq_week": "周", - "editor_freq_month": "月", - "editor_freq_year": "年", - "editor_repeats_on": "重复于", - "editor_every": "每", - "editor_unit_days": "天", - "editor_unit_weeks": "周", - "editor_unit_months": "个月", - "editor_unit_years": "年", - "editor_on_day": "日期:", - "editor_on_the": "在", - "editor_in": "月份:", - "editor_ends": "结束", - "editor_never": "永不", - "editor_ends_on": "于", - "editor_ends_after": "次数:", - "editor_occurrences": "次" - }, - "recurrence_scope": { - "edit_title": "编辑重复事件", - "delete_title": "删除重复事件", - "description": "这是一个重复事件。您想修改哪些事件?", - "this_event": "仅限本次活动", - "this_and_future": "本次及后续活动", - "all_events": "所有活动", - "cancel": "取消", - "save": "保存", - "delete": "删除", - "rsvp_title": "回复重复事件", - "rsvp_description": "这是一个重复事件。您的回复适用于哪些事件?", - "respond": "回复" - }, - "alerts": { - "title": "提醒", - "none": "无提醒", - "at_time": "活动举行时", - "minutes_before": "{count, plural, one {提前 # 分钟} other {提前 # 分钟}}", - "hours_before": "{count, plural, one {提前 # 小时} other {提前 # 小时}}", - "days_before": "{count, plural, one {提前 # 天} other {提前 # 天}}", - "weeks_before": "{count, plural, one {提前 # 周} other {提前 # 周}}", - "unit_minutes_before": "分钟前", - "unit_hours_before": "小时前", - "unit_days_before": "天前", - "unit_weeks_before": "周前", - "add": "添加提醒", - "remove": "移除提醒", - "amount": "提醒数量", - "unit": "提醒单位" - }, - "settings": { - "title": "日历设置", - "default_view": "默认视图", - "week_starts_on": "一周开始于", - "time_format": "时间格式", - "default_calendar": "默认日历", - "default_reminder": "默认提醒", - "time_format_12h": "12小时", - "time_format_24h": "24小时", - "notifications_enabled": "事件通知", - "notifications_enabled_desc": "显示即将发生的日历事件的提醒", - "notification_sound": "通知声音", - "notification_sound_desc": "播放日历提醒声音", - "invitation_parsing": "解析邮件邀请", - "invitation_parsing_desc": "检测邮件附件中的日历邀请并显示日历操作", - "show_time_in_month_view": "在月视图中显示时间", - "show_time_in_month_view_desc": "在月历视图中显示事件时间", - "show_week_numbers": "显示周数", - "show_week_numbers_desc": "在迷你日历中显示周数", - "calendar_free_scroll": "自由滚动", - "calendar_free_scroll_desc": "连续滚动浏览月、周和日,而不是一次只显示一个时段", - "enable_tasks": "启用任务", - "enable_tasks_desc": "在日历中显示任务视图以管理待办事项", - "show_tasks_on_calendar": "在日历上显示任务", - "show_tasks_on_calendar_desc": "在日历的日/周视图中显示任务标签", - "hover_preview": "事件悬停预览", - "hover_preview_desc": "将鼠标悬停在日历事件上时显示详细信息弹出窗口", - "hover_preview_instant": "立即", - "hover_preview_delay_500ms": "0.5秒延迟", - "hover_preview_delay_1s": "1秒延迟", - "hover_preview_delay_2s": "2秒延迟", - "hover_preview_off": "关闭", - "organizer_identity": "以此身份组织邀请", - "organizer_identity_desc": "邀请他人参加活动时用作组织者的日历地址。", - "organizer_identity_failed": "无法更改组织者地址", - "show_birthday_calendar": "联系人生日日历", - "show_birthday_calendar_desc": "显示包含联系人生日的虚拟日历" - }, - "days": { - "monday": "周一", - "tuesday": "周二", - "wednesday": "周三", - "thursday": "周四", - "friday": "周五", - "saturday": "周六", - "sunday": "周日", - "mon": "周一", - "tue": "周二", - "wed": "周三", - "thu": "周四", - "fri": "周五", - "sat": "周六", - "sun": "周日" - }, - "notifications": { - "event_created": "活动已创建", - "event_updated": "活动已更新", - "event_deleted": "活动已删除", - "calendar_created": "已创建日历", - "calendar_deleted": "日历已删除", - "event_move_error": "移动事件失败", - "event_resize_error": "无法调整事件大小", - "alert_title": "即将举行的活动", - "alert_now": "现在开始", - "alert_in_minutes": "{count} 分钟后", - "invitation_sent": "已发送邀请", - "invitations_denied_title": "无法发送邀请", - "invitations_denied": "服务器拒绝发送邀请:{reason}。是否不发送邀请直接保存活动?", - "save_without_invitations": "不发送邀请并保存", - "rsvp_updated": "回复已更新", - "rsvp_error": "无法更新回复", - "event_duplicated": "活动已复制", - "event_error": "无法保存活动", - "task_due": "任务到期", - "event_exported": "日程已导出", - "title_copied": "标题已复制", - "link_copied": "链接已复制" - }, - "status": { - "loading_calendars": "正在加载日历...", - "loading_events": "正在加载事件..." - }, - "quick_create": { - "placeholder": "新活动标题", - "aria_label": "快速创建事件" - }, - "nav_prev": "上一项", - "nav_next": "下一项", - "import": { - "title": "导入日历", - "tab_file": "文件", - "tab_url": "URL", - "select_file": "选择 .ics 文件", - "drop_file": "或将文件拖放到此处", - "supported_formats": "支持 iCalendar (.ics) 文件", - "url_description": "输入外部 iCalendar (.ics) 源的 URL 以导入事件。", - "url_placeholder": "https://example.com/calendar.ics", - "url_hint": "支持 iCalendar (.ics) URL", - "fetch": "获取", - "invalid_url": "请输入有效的 URL", - "url_fetch_failed": "无法从 URL 获取日历", - "parsing": "正在解析日历文件...", - "parsed_events": "找到 {count} 个活动", - "no_events": "文件中未找到活动", - "select_all": "全选", - "deselect_all": "取消全选", - "target_calendar": "导入到日历", - "import_button": "导入所选", - "importing": "正在导入活动...", - "success": "{count} 个活动已成功导入", - "error": "导入日历失败", - "file_too_large": "文件超过 10MB 限制", - "invalid_format": "日历文件格式无效" - }, - "webcal_action": { - "title": "打开日历链接", - "description": "您想如何使用 \"{name}\"?", - "import_title": "导入一次", - "import_description": "立即获取事件并复制到您的某个日历中。", - "subscribe_title": "订阅", - "subscribe_description": "将此日历作为单独的日历自动同步。", - "cancel": "取消" - }, - "management": { - "title": "日历管理", - "description": "创建、重命名和自定义您的日历。右键单击侧栏中的日历可快速更改其颜色。", - "name": "名称", - "name_placeholder": "日历名称", - "color": "颜色", - "change_color": "更改颜色", - "random_color": "随机新颜色", - "add_calendar": "添加日历", - "edit": "编辑", - "delete": "删除", - "save": "保存", - "create": "创建", - "cancel": "取消", - "default": "默认", - "confirm_delete": "删除\"{name}\"?此日历中的所有活动都将被删除。", - "confirm_clear": "清除\"{name}\"中的所有活动吗?此操作无法撤销。", - "set_default": "设为默认", - "default_updated": "默认日历已更新", - "error_default": "无法设置默认日历", - "clear_events": "清除活动", - "events_cleared": "{count} 个活动已清除", - "error_clear": "无法清除日历活动", - "calendar_created": "已创建日历", - "calendar_updated": "日历已更新", - "calendar_deleted": "日历已删除", - "color_updated": "日历颜色已更新", - "error_create": "创建日历失败", - "error_update": "更新日历失败", - "error_delete": "删除日历失败", - "caldav_url": "CalDAV URL", - "copy_url": "复制 CalDAV URL", - "url_copied": "CalDAV URL 已复制到剪贴板", - "share": "共享日历", - "new_event_in_calendar": "在此日历中新建事件", - "kind": "内容", - "kind_events": "日程", - "kind_tasks": "任务", - "kind_both": "日程和任务", - "kind_hint": "决定通过 CalDAV 同步时哪些应用可以看到此日历。之后无法更改。" - }, - "subscription": { - "title": "iCal 订阅", - "section_title": "iCal 订阅", - "description": "订阅外部 iCalendar 源。活动将自动同步到单独的日历中。支持 https:// 和 webcal:// URL。", - "url_label": "日历 URL", - "url_placeholder": "https://example.com/calendar.ics 或 webcal://...", - "name_label": "日历名称", - "name_placeholder": "例如公共假期", - "color_label": "颜色", - "refresh_interval": "刷新间隔", - "interval_15": "每 15 分钟", - "interval_30": "每 30 分钟", - "interval_60": "每小时", - "interval_360": "每 6 小时", - "interval_1440": "每天", - "subscribe": "订阅", - "subscribing": "正在订阅...", - "save": "保存更改", - "saving": "保存中...", - "edit": "编辑", - "edit_title": "编辑订阅", - "updated": "更新了\"{name}\"", - "update_error": "更新订阅失败", - "invalid_url": "请输入有效的 URL", - "success": "已订阅\"{name}\"", - "error": "添加订阅失败", - "refresh": "立即刷新", - "refresh_success": "订阅已刷新", - "refresh_error": "刷新订阅失败", - "unsubscribe": "退订", - "confirm_delete": "取消订阅\"{name}\"?日历及其所有活动都将被删除。", - "deleted": "订阅已删除", - "delete_error": "删除订阅失败", - "last_refreshed": "最后更新:{time}" - }, - "tasks": { - "label": "任务", - "no_tasks": "没有任务", - "no_title": "(无标题)", - "mark_complete": "标记为完成", - "mark_incomplete": "标记为未完成", - "filter_all": "全部", - "filter_pending": "待处理", - "filter_completed": "已完成", - "filter_overdue": "逾期", - "show_completed": "显示已完成", - "create": "新任务", - "edit": "编辑任务", - "title_placeholder": "任务标题", - "description_placeholder": "添加描述...", - "due_date": "到期日", - "include_time": "包括时间", - "priority": "优先级", - "priority_none": "无", - "priority_high": "高", - "priority_medium": "中", - "priority_low": "低", - "progress": "状态", - "progress_needs_action": "待处理", - "progress_in_process": "进行中", - "progress_completed": "已完成", - "progress_cancelled": "已取消", - "calendar": "日历", - "alert": "提醒", - "alert_none": "无", - "alert_at_time": "在到期日时", - "alert_5min": "5分钟前", - "alert_15min": "15分钟前", - "alert_30min": "30分钟前", - "alert_1hr": "1小时前", - "alert_1day": "1天前", - "delete": "删除", - "cancel": "取消", - "save": "保存", - "quick_add_placeholder": "添加任务...", - "due_today": "今天", - "due_tomorrow": "明天", - "overdue": "逾期" - }, - "months": { - "jan": "1月", - "feb": "2月", - "mar": "3月", - "apr": "4月", - "may": "5月", - "jun": "6月", - "jul": "7月", - "aug": "8月", - "sep": "9月", - "oct": "10月", - "nov": "11月", - "dec": "12月", - "far": "Farvardin", - "ord": "Ordibehesht", - "kho": "Khordad", - "tir": "Tir", - "mor": "Mordad", - "sha": "Shahrivar", - "meh": "Mehr", - "aba": "Aban", - "aza": "Azar", - "dey": "Dey", - "bah": "Bahman", - "esf": "Esfand" - }, - "nav_open_menu": "打开菜单" - }, - "advanced_search": { - "title": "高级搜索", - "from": "发件人", - "from_placeholder": "发件人邮箱地址或姓名", - "to": "收件人", - "to_placeholder": "收件人邮箱地址或姓名", - "subject": "主题", - "subject_placeholder": "主题包含...", - "body": "正文", - "body_placeholder": "正文包含...", - "folder": "文件夹", - "all_folders": "所有文件夹", - "has_attachment": "附件", - "date_after": "后", - "date_before": "前", - "size_min": "大于 (KB)", - "size_max": "小于 (KB)", - "starred": "加星标", - "unread": "未读", - "read": "已读", - "yes": "是", - "no": "否", - "clear": "清除", - "clear_all": "全部清除", - "filters_active": "{count} 个筛选条件", - "filters_active_plural": "{count} 个筛选条件", - "toggle_filters": "更多", - "search_hint": "使用高级过滤器进行精确搜索", - "advanced_filters_tooltip": "高级搜索过滤器", - "results_found": "{count, plural, =0 {未找到结果} one {找到 # 条结果} other {找到 # 条结果}}", - "results_found_more": "找到 {count}+ 个结果", - "suggestions_recent": "最近搜索", - "suggestions_people": "联系人", - "suggestions_from": "搜索来自 {address} 的邮件", - "suggestions_to": "搜索发给 {address} 的邮件", - "suggestions_remove_recent": "从最近搜索中移除" - }, - "welcome": { - "title": "欢迎使用邮箱", - "tip_compose": "按 c 撰写新邮件", - "tip_shortcuts": "按?查看所有键盘快捷键", - "tip_sidebar": "在侧边栏菜单中查找日历、联系人和设置", - "tip_settings": "在\"设置\"中自定义您的体验", - "got_it": "知道了", - "settings": "设置", - "dismiss": "关闭", - "start_tour": "开始导览" - }, - "demo_welcome": { - "title": "欢迎使用 Bulwark Mail", - "description": "探索功能齐全的网页邮件客户端--就在您的浏览器中。所有数据都保留在您的设备上,请随意体验所有功能。", - "feature_email": "阅读和撰写邮件", - "feature_organize": "标签、星标和文件夹", - "feature_shortcuts": "键盘快捷键", - "feature_privacy": "100% 隐私演示", - "hint": "点击左侧的任意邮件即可开始,或参加下方的导览。" - }, - "files": { - "office_edit": "编辑文档", - "office_loading": "正在打开文档编辑器…", - "office_error": "无法打开文档编辑器。", - "office_close": "关闭编辑器", - "open_in_new_tab": "在新标签页中打开", - "title": "文件", - "search_placeholder": "搜索文件...", - "empty_state_title": "还没有文件", - "empty_state_description": "上传文件或创建文件夹以开始", - "upload": "上传", - "upload_files": "上传文件", - "new_folder": "新建文件夹", - "new_folder_name": "文件夹名称", - "rename": "重命名", - "rename_title": "重命名", - "new_name": "新名称", - "name_forbidden_chars": "服务器不允许名称中包含这些字符:{chars}", - "name_reserved": "此名称已被服务器保留。", - "delete": "删除", - "delete_confirm_title": "删除资源", - "delete_confirm_message": "您确定要删除\"{name}\"吗?此操作无法撤消。", - "download": "下载", - "name": "名称", - "size": "大小", - "modified": "修改时间", - "type": "类型", - "folder": "文件夹", - "file": "文件", - "parent_directory": "父目录", - "breadcrumb_root": "根目录", - "drop_files_here": "将文件或文件夹拖放到此处进行上传", - "uploading": "正在上传…", - "upload_success": "{count, plural, one {已上传 1 个文件} other {已上传 # 个文件}}", - "upload_error": "上传文件失败", - "create_folder_success": "文件夹已创建", - "create_folder_error": "创建文件夹失败", - "delete_success": "删除成功", - "delete_error": "删除失败", - "rename_success": "重命名成功", - "rename_error": "重命名失败", - "download_error": "下载失败", - "not_available": "文件存储在此服务器上不可用", - "cancel": "取消", - "create": "创建", - "save": "保存", - "no_results": "没有与您的搜索匹配的文件", - "batch_delete_confirm_message": "确定要删除{count, plural, one {1 个项目} other {# 个项目}}吗?此操作无法撤销。", - "batch_delete_success": "{count, plural, one {已删除 1 个项目} other {已删除 # 个项目}}", - "grid_view": "网格视图", - "list_view": "列表视图", - "details": "详情", - "path": "路径", - "preview": "预览", - "preview_error": "加载预览失败", - "cut": "剪切", - "copy": "复制", - "paste": "粘贴", - "move_success": "{count, plural, one {已移动 1 个项目} other {已移动 # 个项目}}", - "move_error": "移动失败", - "paste_success": "粘贴成功", - "paste_error": "粘贴失败", - "new_text_file": "新建文本文件", - "file_name": "文件名", - "retry": "重试", - "refresh": "刷新", - "toggle_favorite": "切换收藏夹", - "duplicate": "创建副本", - "duplicate_success": "复制成功", - "duplicate_error": "复制失败", - "share": "Share", - "shared": "Shared", - "shared_with_me": "Shared with me", - "shared_by": "Shared by {name}", - "create_file_success": "文件已创建", - "create_file_error": "创建文件失败", - "favorites": "收藏夹", - "recent": "最近", - "properties": "属性", - "open_folder": "打开文件夹", - "upload_folder": "上传文件夹", - "file_too_large": "\"{name}\"超过最大文件大小 ({max})", - "undo": "撤销", - "undo_success": "操作已撤消", - "undo_error": "撤销失败", - "toolbar": "文件操作", - "file_list": "文件和文件夹", - "context_menu": "操作", - "settings_title": "文件设置", - "settings_display": "显示", - "settings_default_view": "默认视图", - "settings_default_view_desc": "在网格布局和列表布局之间进行选择", - "settings_default_sort": "默认排序", - "settings_default_sort_desc": "选择文件的默认排序规则", - "settings_sort_direction": "排序方式", - "settings_sort_direction_desc": "选择升序或降序", - "settings_ascending": "升序", - "settings_descending": "降序", - "settings_icons": "图标", - "settings_show_icons": "显示文件图标", - "settings_show_icons_desc": "在文件和文件夹旁边显示图标", - "settings_colored_icons": "彩色图标", - "settings_colored_icons_desc": "使用彩色图标而不是单色图标", - "settings_show_thumbnails": "显示缩略图", - "settings_show_thumbnails_desc": "显示图像预览而不是图像文件的图标", - "settings_behavior": "行为", - "settings_show_hidden": "显示隐藏文件", - "settings_show_hidden_desc": "显示以点开头的文件和文件夹", - "settings_folder_layout": "文件夹导航", - "settings_folder_layout_desc": "选择文件夹的显示方式:与文件列表内嵌显示,或在侧边栏树状显示", - "settings_folder_layout_inline": "内嵌", - "settings_folder_layout_sidebar": "侧边栏", - "disabled_title": "文件功能已被您的管理员禁用", - "disabled_description": "通过 WebDAV 上传大文件可能导致 Stalwart/RocksDB 不稳定,包括内存溢出、崩溃以及难以回收的磁盘占用。已删除文件也可能不会立即从 blob 存储中清除。不建议在生产环境中启用此功能。", - "stability_warning": "大文件上传会导致服务器不稳定。已删除的文件可能不会立即从存储中清除。谨慎使用。", - "no_accounts": "没有已连接的账户。", - "open_folder_tree": "打开文件夹树", - "other_accounts": "其他账户", - "migration_title": "正在更新您的文件…", - "migration_description": "正在将文件夹和文件整理为正确的结构。此操作仅执行一次。" - }, - "smime": { - "your_certificates": "您的证书", - "your_certificates_desc": "导入和管理您的 S/MIME 证书以对邮件进行签名和加密", - "recipient_certificates": "收件人证书", - "recipient_certificates_desc": "用于加密发送给收件人的邮件的公共证书", - "identity_bindings": "身份键绑定", - "identity_bindings_desc": "将 S/MIME 证书绑定到您的邮件身份", - "defaults_title": "默认值", - "defaults_desc": "配置默认签名和加密行为", - "import_pkcs12": "导入 PKCS#12 (.p12/.pfx)", - "import_public_cert": "导入证书", - "no_certificates": "尚未导入证书", - "no_recipient_certs": "无收件人证书", - "expires": "过期", - "expired": "已到期", - "bound_to": "绑定到", - "no_key_bound": "无", - "lock": "锁定密钥", - "unlock": "解锁密钥", - "details": "查看详情", - "delete": "删除", - "encrypt_by_default": "默认加密", - "encrypt_by_default_desc": "当所有收件人都有证书时自动加密邮件", - "remember_unlocked": "记住已解锁的密钥", - "remember_unlocked_desc": "在当前浏览器会话期间保持密钥处于解锁状态", - "sign_default_for": "默认签名", - "enter_p12_passphrase": "输入 PKCS#12 密码", - "p12_passphrase_desc": "输入保护此证书文件的密码", - "enter_storage_passphrase": "设置存储密码", - "storage_passphrase_desc": "选择一个密码来保护浏览器中静态的密钥", - "next": "下一步", - "import": "导入", - "unlock_key": "解锁密钥", - "unlock_key_desc": "输入存储密码以解锁此密钥以进行签名或解密", - "passphrase_placeholder": "输入密码", - "confirm_passphrase_placeholder": "确认密码", - "passphrase_mismatch": "密码不匹配", - "cancel": "取消", - "processing": "处理中…", - "close": "关闭", - "certificate_details": "证书详情", - "cert_subject": "主题", - "cert_issuer": "颁发者", - "cert_email": "邮件", - "cert_serial": "序列号", - "cert_validity": "有效期", - "cert_fingerprint": "指纹 (SHA-256)", - "cert_algorithm": "算法", - "cert_capabilities": "支持能力", - "cert_source": "来源", - "cert_expired": "该证书已过期", - "cert_not_yet_valid": "该证书尚未生效", - "cap_sign": "签名", - "cap_encrypt": "加密", - "cap_none": "无", - "show_passphrase": "显示密码", - "hide_passphrase": "隐藏密码", - "sign_toggle": "签名", - "encrypt_toggle": "加密", - "missing_recipient_certs": "缺少以下证书:{emails}", - "missing_sender_cert": "没有证书与该身份绑定", - "status_encrypted_ok": "此消息已加密", - "status_encrypted_no_key": "此消息已加密,但未找到匹配的密钥", - "status_encrypted_failed": "无法解密此消息", - "status_signed_valid": "签名已验证", - "status_signed_invalid": "签名验证失败", - "status_signed_expired_cert": "使用过期的证书签名", - "status_signed_self_signed": "签名有效,但证书是自签名的(不可信)", - "status_signed_mismatch": "签名有效,但签名者与发件人不匹配", - "status_unsupported": "不支持的 S/MIME 格式", - "auto_import_signer_certs": "自动导入邮件签名者的证书", - "auto_import_signer_certs_desc": "自动保存已验证签名邮件中的证书,以便后续用于邮件加密。", - "export": "导出", - "enter_export_passphrase": "设置导出密码", - "export_passphrase_desc": "选择密码来保护导出的 PKCS#12 文件", - "export_storage_desc": "输入存储密码以解密导出密钥", - "incorrect_passphrase": "密码不正确" - }, - "tour": { - "step_counter": "第 {current} 步,共 {total} 步", - "skip": "跳过导览", - "back": "上一步", - "next": "下一步", - "finish": "完成", - "take_a_tour": "查看界面导览", - "restart_title": "新手导览", - "restart_desc": "重新查看界面功能引导", - "restart_button": "重新开始导览", - "show_on_new_devices_title": "在新设备上显示", - "show_on_new_devices_desc": "首次在新设备登录时重新显示欢迎横幅和导览,即使您已在其他设备完成", - "sidebar_title": "邮箱文件夹", - "sidebar_desc": "这里是邮箱文件夹列表。点击任意文件夹即可查看邮件。你可以创建文件夹、拖动邮件进行整理,并快速查看未读邮件数量。", - "compose_title": "写邮件", - "compose_desc": "点击这里即可新建邮件。你可以添加收件人、附件,并使用富文本格式编辑内容。", - "search_title": "搜索邮件", - "search_desc": "可以按发件人、主题或内容搜索邮件。点击筛选图标还能使用高级搜索,例如日期范围、附件或星标邮件。", - "email_list_title": "邮件列表", - "email_list_desc": "邮件会显示在这里。点击任意邮件即可在右侧阅读。你也可以使用复选框选择多封邮件进行批量移动、删除或添加标签。", - "email_viewer_title": "阅读区域", - "email_viewer_desc": "选中的邮件会在这里打开。你可以使用工具栏进行回复、转发、归档或删除,也可以为邮件添加星标或颜色标签。", - "keywords_title": "颜色标签", - "keywords_desc": "使用颜色标签来整理邮件。可以将邮件拖到标签上进行标记,或右键邮件选择标签。", - "calendar_title": "日历", - "calendar_desc": "切换到日历即可管理你的日程。你可以创建事件、设置提醒,并查看日、周或月视图。", - "contacts_title": "联系人", - "contacts_desc": "这里是你的通讯录。你可以导入联系人、创建联系人分组,点击任意联系人即可查看详细信息。", - "settings_title": "设置", - "settings_desc": "在这里可以自定义各种设置,例如主题、界面紧凑度、签名、邮件过滤规则、快捷键以及日历默认选项等。", - "shortcuts_title": "键盘快捷键", - "shortcuts_desc": "按下 ? 可随时查看所有快捷键。你可以使用键盘完成导航、写邮件和管理邮件等操作。", - "compose_open_title": "邮件编辑器", - "compose_open_desc": "这里是邮件编辑区域。添加收件人、输入正文、上传附件,并使用富文本格式编辑。你还可以保存草稿或使用邮件模板。", - "calendar_view_title": "你的日历", - "calendar_view_desc": "这里是你的日历视图(示例事件)。可以通过工具栏切换日、周、月或日程视图。", - "create_event_title": "创建日程", - "create_event_desc": "点击此按钮创建新的日程。你可以填写标题、日期、时间,并邀请参与者。", - "event_modal_title": "日程详情", - "event_modal_desc": "在这里填写日程信息,例如标题、时间、地点或参与者。完成后点击保存即可。", - "contacts_list_title": "联系人列表", - "contacts_list_desc": "这里显示你的联系人。点击任意联系人即可在右侧查看详情。你也可以新建联系人、导入 vCard 或创建联系人分组。", - "settings_tabs_title": "设置菜单", - "settings_tabs_desc": "这里列出了所有设置分类。你可以自定义界面外观、管理身份、设置邮件规则、配置日历等。", - "files_title": "文件存储", - "files_desc": "这里是你的文件管理器,可以上传、整理和共享文件,就像内置在邮箱中的个人云盘。", - "demo_banner_title": "演示控制", - "demo_banner_desc": "当前为演示模式,所有数据仅保存在浏览器中。随时点击\"重置演示\"即可恢复初始示例数据。", - "quota_title": "存储使用情况", - "quota_desc": "在这里查看邮箱的存储使用情况。随着空间使用增加,进度圆环会逐渐填满。" - }, - "unified_mailbox": { - "search_unavailable": "统一视图中无法使用搜索" - }, - "calendar_event_notifications": { - "someone": "有人", - "invited": "{name} 邀请您参加“{title}”", - "updated": "{name} 更新了“{title}”", - "cancelled": "{name} 取消了“{title}”", - "untitled": "无标题活动" - }, - "share_notifications": { - "someone": "有人", - "shared": "{name} 与您共享了{kind}“{object}”", - "changed": "{name} 更改了您对{kind}“{object}”的访问权限", - "revoked": "{name} 移除了您对{kind}“{object}”的访问权限", - "object": { - "folder": "文件夹", - "calendar": "日历", - "address_book": "通讯录", - "files": "文件目录" - } - }, - "sharing": { - "title": "共享「{name}」", - "description": "向此服务器上的其他用户或群组授予访问权限。更改会立即生效。", - "no_shares": "尚未共享。", - "add_person": "添加用户或群组", - "search_placeholder": "按姓名或邮箱搜索…", - "loading_principals": "正在加载用户…", - "no_principals": "未找到其他用户或群组。", - "no_match": "无匹配项。", - "remove": "取消访问", - "group": "群组", - "share_added": "已授予访问权限", - "share_updated": "已更新访问权限", - "share_removed": "已取消访问权限", - "share_failed": "更新共享失败", - "preset": { - "freeBusy": "仅显示忙/闲", - "read": "只读", - "readWrite": "读写", - "manager": "管理员", - "custom": "自定义" - } - }, - "quote_header": { - "reply_line": "在 {date},{from} 写道:", - "forwarded_separator": "---------- 转发邮件 ----------", - "from_label": "发件人", - "date_label": "日期", - "subject_label": "主题" - }, - "pwa_install": { - "title": "安装 {appName}", - "description": "安装我们的应用,以获得快速访问和离线支持。", - "not_now": "暂不", - "install": "安装", - "dont_remind": "不再提醒", - "dismiss_aria": "关闭安装提示" - }, - "deep_link": { - "message_not_found": "此邮件已不可用。", - "thread_not_found": "此会话已不可用。", - "folder_not_found": "此文件夹已不可用。", - "event_not_found": "此日程已不可用。", - "file_not_found": "此文件已不可用。", - "account_unavailable": "请登录该账户以打开此链接。", - "copy_message": "复制邮件链接", - "copy_conversation": "复制会话链接", - "copy_event": "复制日程链接", - "copied": "链接已复制", - "copy_failed": "无法复制链接" - }, - "plugins": { - "oauth_callback": { - "completing": "正在完成授权…", - "completing_message": "正在建立连接,请稍候。", - "received_title": "已收到授权", - "received_message": "您可以返回发起连接的标签页。", - "failed_title": "授权失败", - "denied": "授权被拒绝。", - "failed_detail": "授权失败:{error}", - "missing_params": "缺少授权码或状态。", - "delivery_failed": "无法将授权结果传递到应用标签页。请关闭此窗口并重试。" - } - }, - "global_search": { - "title": "全局搜索", - "placeholder": "搜索邮件、联系人、日历、文件...", - "scope_label": "搜索范围", - "account_label": "账户", - "scope_all": "全部", - "scope_mail": "邮件", - "scope_contacts": "联系人", - "scope_calendar": "日历", - "scope_files": "文件", - "all_accounts": "所有账户", - "show_all": "显示全部结果", - "no_results": "未找到“{query}”的结果", - "server_hint": "完整单词在服务器上搜索;部分单词匹配已加载的内容", - "searching": "搜索中...", - "account_error": "无法搜索 {account}", - "recent_searches": "最近搜索", - "recurring": "重复", - "no_subject": "(no subject)", - "empty_hint": "在所有账户中搜索邮件、联系人、日程和文件。可尝试 from:、subject:、has:attachment、after:2026-01-01。", - "flat_by_date": "按日期平铺", - "grouped": "分组", - "load_more": "加载更多", - "preview_empty": "选择一个结果以预览", - "open_full": "打开", - "view_label": "视图" - } -} +{ + "meta_description": "使用 JMAP 协议的极简网页邮件客户端", + "login": { + "title": "网页邮箱", + "username_label": "邮箱地址", + "username_placeholder": "user@example.com", + "password_label": "密码", + "password_placeholder": "请输入密码", + "jmap_endpoint_label": "JMAP 服务器", + "jmap_endpoint_placeholder": "https://mail.example.com", + "jmap_endpoint_cors_hint": "服务器必须允许来自此域名的 CORS 请求。", + "jmap_server_label": "服务器", + "jmap_server_auto_picked": "已根据您的电子邮件域名选择服务器。", + "sign_in": "登录", + "signing_in": "正在登录…", + "loading": "正在加载…", + "reconnecting": "连接已断开,正在尝试重连…", + "error": { + "invalid_credentials": "邮箱地址或密码无效。请检查后重试。", + "invalid_token": "服务器未接受此访问令牌。请检查令牌是否完整,以及是否已过期或被撤销。", + "connection_failed": "无法访问服务器。检查您的网络连接,然后重试。", + "cors_blocked": "服务器可访问,但正在阻止跨源请求。请检查 JMAP 服务器的 CORS 设置,并允许此域名。", + "server_error": "服务器暂时不可用,请稍后重试。", + "generic": "发生意外错误。如果问题持续存在,请联系管理员。", + "totp_required": "需要双重验证代码。请在下方输入验证码。", + "totp_invalid": "验证码无效。请检查您的身份验证器应用,然后重试。", + "oauth_discovery_failed": "SSO 已启用,但无法访问身份提供者。检查您的 OAuth 配置。", + "token_exchange_failed": "密码和验证码已通过,但邮件服务器拒绝为此应用建立会话。请联系管理员检查其 OAuth 客户端设置。" + }, + "show_password": "显示密码", + "hide_password": "隐藏密码", + "totp_toggle": "我有双重验证代码", + "token_toggle": "使用访问令牌登录", + "password_toggle": "使用密码登录", + "token_label": "访问令牌", + "token_placeholder": "粘贴 API 令牌", + "token_hint": "请在邮件服务商的设置中创建 API 令牌,然后粘贴到这里。", + "remember_me": "记住我", + "config_error": { + "title": "配置错误", + "fetch_failed": "无法加载应用配置。请稍后重试。", + "server_not_configured": "邮件服务器尚未配置。请联系您的管理员。" + }, + "remove_from_history": "从历史记录中移除", + "totp_label": "验证码", + "totp_placeholder": "000000", + "session_expired": "会话已过期,请重新登录。", + "dismiss": "关闭", + "or": "或", + "sign_in_sso": "使用 SSO 登录", + "add_account_title": "添加账户", + "add_account_subtitle": "使用其他账户登录", + "cancel": "取消", + "website": "网站", + "imprint": "法律信息", + "privacy_policy": "隐私政策", + "try_demo": "体验演示版", + "demo_description": "使用示例数据进行探索 - 无需账户", + "demo_launching": "正在启动演示版...", + "demo_login_button": "启动演示", + "demo_tagline": "体验功能完整的邮件客户端,无需账号。", + "demo_no_signup": "无需注册,直接使用示例数据体验", + "mobile_handoff": { + "title": "Open the Bulwark mobile app?", + "description": "This sign-in page was opened by an app on this device. Your sign-in for {username} will be handed to the app that registered the Bulwark link. Continue only if you started this from the Bulwark mobile app.", + "confirm": "Hand over to the app", + "cancel": "Stay in the webmail" + }, + "oauth_completing": "正在完成登录…", + "oauth_error": { + "title": "认证失败", + "invalid_state": "安全验证失败。请尝试重新登录。", + "missing_params": "缺少授权数据。请尝试重新登录。", + "token_exchange_failed": "无法完成身份验证。请再试一次。", + "access_denied": "访问被拒绝。请联系您的管理员。", + "back_to_login": "返回登录" + } + }, + "sidebar": { + "close": "关闭", + "compose": "撰写邮件", + "compose_hint": "撰写邮件 (c)", + "search_placeholder": "搜索邮件...", + "search_placeholder_hint": "搜索邮件...(按 /)", + "storage": "存储空间", + "storage_used": "已使用", + "storage_free": "剩余", + "storage_total": "总计", + "sign_out": "退出登录", + "sign_out_of": "退出 {account}", + "sign_out_all": "退出所有账户", + "add_account": "添加账户", + "set_as_default": "设为默认", + "switch_account": "切换账户", + "contacts": "联系人", + "calendar": "日历", + "settings": "设置", + "admin": "管理", + "files": "文件", + "loading_mailboxes": "正在加载邮箱...", + "push_connected": "实时同步已启用", + "push_disconnected": "实时同步未启用", + "keyboard_shortcuts": "键盘快捷键", + "theme": { + "light": "浅色模式", + "dark": "深色模式", + "system": "系统主题" + }, + "language": { + "title": "语言" + }, + "mailboxes": { + "inbox": "收件箱", + "sent": "已发送", + "drafts": "草稿", + "trash": "已删除", + "archive": "归档", + "starred": "已加星标", + "all_mail": "所有邮件", + "spam": "垃圾邮件", + "important": "重要" + }, + "unified_inbox": "统一收件箱", + "unified_sent": "所有已发送", + "unified_drafts": "所有草稿", + "unified_trash": "所有已删除", + "unified_archive": "所有归档", + "unified_junk": "所有垃圾邮件", + "all_accounts": "所有账户", + "all_accounts_loading": "正在加载账户:{loaded}/{total}", + "unified_mailbox": "统一邮箱", + "expand": "展开", + "collapse": "收起", + "expand_tooltip": "展开", + "collapse_tooltip": "收起", + "mobile": { + "search": "搜索", + "compose": "撰写邮件", + "go_back": "返回", + "toggle_menu": "切换菜单" + }, + "clear_search": "清除搜索", + "vacation_active": "假期自动回复已开启", + "demo_banner": "演示模式", + "demo_reset": "重置", + "demo_tour": "引导", + "tags": "标签", + "show_all_tags": "显示全部({count})", + "show_fewer_tags": "收起", + "folders": "文件夹", + "your_folders": "我的文件夹", + "create_folder": "新建文件夹", + "show_more": "更多", + "show_fewer": "收起", + "mail": "邮件", + "nav_label": "导航", + "add_app": "应用", + "shared": "共享", + "shared_account_hidden": "已从侧边栏隐藏。可在 设置 › 布局 中重新显示。", + "notifications_muted": "通知已静音", + "scheduled": "已计划", + "unified_all_unread": "全部未读", + "unified_all_starred": "全部加星标", + "unified_all_mail": "全部邮件", + "remove_account": "Remove account", + "remove_account_confirm": "Remove {account} from this device? You can add it back later.", + "labels": "标签", + "create_label": "新建标签", + "mv_tasks": "任务", + "mv_side_panel": "侧边栏", + "mv_open_full": "在完整视图中打开", + "mv_previous_day": "前一天", + "mv_next_day": "后一天", + "mv_add_another_account": "添加其他账户", + "mv_manage_account": "管理您的账户", + "mv_storage_used": "已使用 {total} 中的 {percent}%" + }, + "protocol_handlers": { + "title": "默认应用", + "description": "选择是否在 Bulwark 中打开电子邮件和日历链接。从技术上讲,Bulwark 会注册为 mailto: 和 webcal: 链接的协议处理程序。", + "unsupported": "此浏览器或连接不支持手动注册协议处理程序。你仍可尝试通过浏览器或系统设置使用已安装的 PWA。", + "mailto_label": "电子邮件链接", + "mailto_description": "在 Bulwark 中打开 mailto: 链接,并预先填好撰写窗口。", + "protocol_open_mode_label": "打开协议链接时", + "protocol_open_mode_description": "选择 Bulwark 是在新标签页中打开 mailto: 和 webcal: 链接,还是复用已打开的会话。活动会话选项需要通知权限,这样当浏览器阻止聚焦时,你可以点击备用通知将 Bulwark 窗口带到前台。", + "protocol_open_mode_active_session": "尽可能在活动会话中打开", + "protocol_open_mode_new_tab": "始终打开新标签页", + "focus_notification_title": "打开 Bulwark", + "focus_notification_body": "链接已在 Bulwark 中打开。点击可将窗口带到前台。", + "webcal_label": "日历链接", + "webcal_description": "在 Bulwark 中打开 webcal: 链接,并预先填好日历订阅对话框。", + "register_mailto": "注册电子邮件应用", + "register_webcal": "注册日历应用", + "mailto_registered": "已请求注册电子邮件处理程序", + "webcal_registered": "已请求注册日历处理程序", + "registration_failed": "协议处理程序注册失败", + "opening_mailto": "正在打开撰写窗口...", + "opening_webcal": "正在打开日历...", + "browser_note": "你的浏览器或操作系统可能会要求确认,并且可能需要先安装 Bulwark,才能将其选为默认应用。", + "select_account_title": "选择账户", + "select_mailto_account": "选择用于打开此电子邮件链接的账户。", + "select_webcal_account": "选择用于打开此日历链接的账户。", + "select_account_note": "此选择仅适用于此协议链接。", + "detail_to": "收件人", + "detail_subject": "主题", + "detail_no_subject": "无主题", + "detail_calendar": "日历", + "detail_source": "来源", + "active_account": "活动", + "switching_account": "正在切换账户..." + }, + "sidebar_apps": { + "modal_title": "侧边栏应用", + "add_new": "添加应用", + "edit_app": "编辑应用", + "name_label": "名称", + "name_placeholder": "我的应用", + "name_required": "名称为必填项", + "url_label": "URL", + "url_required": "URL 为必填项", + "url_invalid": "请输入有效的 http 或 https URL", + "icon_label": "图标", + "icon_required": "图标为必填项", + "open_mode_label": "打开方式", + "open_new_tab": "在新标签页中打开", + "open_inline": "内嵌打开", + "cancel": "取消", + "add": "添加", + "update": "更新", + "delete": "删除", + "delete_confirm_title": "删除应用", + "delete_confirm": "您确定要删除\"{name}\"吗?", + "no_apps": "尚未添加应用", + "no_apps_hint": "将自定义应用和链接添加到侧边栏", + "managed_title": "由管理员提供", + "managed_description": "这些应用已为所有人配置,无法更改。", + "managed_badge": "受管理", + "search_icons": "搜索图标...", + "show_popular": "热门", + "show_all": "全部", + "no_icons_found": "没有找到图标", + "inline_badge": "内嵌", + "tab_badge": "新标签页", + "show_on_mobile": "在手机上显示" + }, + "email_list": { + "columns": { + "from": "发件人", + "to": "收件人", + "subject": "主题", + "date": "日期", + "size": "大小" + }, + "no_emails": "未找到邮件", + "no_emails_description": "此邮箱为空", + "no_search_results": "未找到结果", + "no_search_results_description": "请调整搜索条件或筛选器", + "loading": "正在加载邮件...", + "unread": "未读", + "to_me": "发给我", + "to_recipients": "致 {count} 收件人", + "and_others": "和 {count} 其他人", + "draft": "草稿", + "starred": "加星标", + "pinned": "已置顶", + "replied": "已回复", + "forwarded": "已转发", + "has_attachment": "有附件", + "conversations_range": "第 {from}–{to, number} 个,共 {total, number} 个", + "conversations_count": "共 {total} 个会话,当前显示 {count} 个", + "conversations_count_plus": "{count}+ 个会话", + "conversations_count_simple": "{count} 个会话", + "no_conversations": "暂无会话", + "loading_more": "正在加载更多邮件...", + "no_more_emails": "没有更多邮件了", + "batch_actions": { + "select": "选择邮件", + "select_all": "全选", + "selected_messages": "已选取 {count, plural, one {1} other {#}} 封电子邮件", + "mark_read": "标记为已读", + "mark_unread": "标记为未读", + "delete": "删除", + "delete_confirm_title": "删除邮件", + "delete_confirm_message": "确定要删除{count, plural, one {1 封邮件} other {# 封邮件}}吗?", + "clear_selection": "清除选择" + }, + "permanent_delete": "永久删除", + "permanent_delete_confirm_title": "永久删除", + "permanent_delete_confirm_message": "该邮件将被永久删除。此操作无法撤销。", + "permanent_delete_confirm_batch_message": "这些{count, plural, one {1 封邮件} other {# 封邮件}}将被永久删除。此操作无法撤销。", + "empty_folder": { + "button": "清空文件夹", + "confirm_title": "清空文件夹", + "confirm_message": "此文件夹中的所有邮件都将被永久删除。此操作无法撤销。", + "confirm_message_trash": "此文件夹中的所有邮件都将移至已删除文件夹。", + "confirm_button": "清空文件夹", + "junk_hint": "您可以清空垃圾邮件文件夹以永久删除所有邮件。", + "trash_hint": "您可以清空已删除文件夹以永久删除所有邮件。" + }, + "no_scheduled_emails": "没有计划发送的邮件", + "no_scheduled_emails_description": "计划稍后发送的邮件会显示在这里。", + "scheduled_count": "{count, plural, one {1 封计划发送邮件} other {# 封计划发送邮件}}", + "scheduled_actions_hint": "计划发送的邮件可以通过计划操作取消、重新计划或编辑。", + "cancel_scheduled_send": "取消发送", + "reschedule_send": "重新计划", + "cancel_and_edit": "取消并编辑", + "cancel_and_compose_again": "取消并重新撰写", + "reschedule_prompt": "输入新的日期/时间,例如 2026-05-04T15:30" + }, + "email_viewer": { + "read_receipt": { + "prompt": "发件人请求已读回执:", + "send": "发送回执", + "ignore": "忽略", + "sent": "已读回执已发送。", + "send_failed": "无法发送已读回执", + "mdn_subject": "已读:{subject}", + "mdn_body": "这是您发送给 {recipient} 的邮件的已读回执。\n\n注意:此回执仅表示邮件已在收件人的计算机上显示,并不保证收件人已阅读或理解邮件内容。" + }, + "no_email_selected": "未选择邮件", + "no_email_description": "从列表中选择一封邮件以在此查看", + "no_conversation_selected": "未选择会话", + "no_conversation_description": "从列表中选择会话以在此阅读", + "compose": "撰写邮件", + "compose_hint": "撰写新邮件", + "no_subject": "(无主题)", + "no_body_content": "(无正文内容)", + "no_preview_available": "无可用预览", + "show_quoted_text": "显示引用文本", + "hide_quoted_text": "隐藏引用文本", + "loading_email": "正在加载邮件...", + "loading": "正在加载…", + "reply": "回复", + "reply_all": "全部回复", + "forward": "转发", + "delete": "删除", + "archive": "归档", + "archive_mailbox_not_found": "Archive mailbox not found - cannot archive email", + "star": "加星标", + "unstar": "取消星标", + "mark_unread": "标记为未读", + "mark_read": "标记为已读", + "unread": "未读", + "read": "已读", + "spam_short": "垃圾邮件", + "not_spam_short": "非垃圾邮件", + "move": "移动", + "print": "打印", + "view_source": "查看源码", + "fullscreen": "全屏", + "exit_fullscreen": "退出全屏", + "view_in_dark_mode": "深色模式查看", + "view_in_light_mode": "浅色模式查看", + "export_email": "导出为 .eml", + "forward_as_attachment": "作为附件转发", + "import_email": "导入 .eml 或 .zip", + "keyboard_shortcuts": "键盘快捷键(?)", + "email_source": "邮件源码", + "draft_banner": "这是一封草稿邮件", + "edit_draft": "编辑", + "copy_source": "复制到剪贴板", + "source_copied": "源码已复制到剪贴板", + "attachments": "附件", + "important": "重要", + "download": "下载", + "download_all": "全部下载", + "from": "发件人", + "to": "收件人", + "cc": "抄送", + "bcc": "密送", + "date": "日期", + "subject": "主题", + "show_details": "查看详情", + "hide_details": "隐藏详情", + "external_content_warning": "图片和外部内容已被屏蔽", + "load_external_content": "加载图片", + "trust_sender": "始终信任此发件人", + "sender_check": { + "unverified_label": "发件人未经验证", + "failed_label": "发件人验证失败", + "unverified": "此邮件声称来自 {domain},但没有任何证据能证实这一点。它没有 {domain} 的有效签名,也不是从 {domain} 授权的服务器发出的。", + "unverified_sent_from": "此邮件声称来自 {domain},但它是从 {host} 发出的,并且没有 {domain} 的有效签名。", + "failed": "此邮件声称来自 {domain},但未通过该域名的发件人验证。它可能是伪造的。", + "failed_sent_from": "此邮件声称来自 {domain},但它是从 {host} 发出的,并且未通过 {domain} 的发件人验证。它可能是伪造的。", + "caution": "请谨慎对待其中的链接、附件,以及索要密码或付款信息的请求。" + }, + "back_to_list": "返回列表", + "view_contact": "查看联系人", + "message_details": "邮件详情", + "more_reply_options": "更多回复选项", + "set_tag": "设置颜色标签", + "tag": "标签", + "more_actions": "更多操作", + "previous": "上一封", + "next": "下一封", + "move_to": "移动到…", + "remove_tag": "删除标签", + "tag_filter_placeholder": "筛选标签", + "tag_no_matches": "没有匹配的标签", + "more_count": "+{count} 更多", + "characters_count": "{count} 个字符", + "quick_reply_placeholder": "快速回复...", + "more_options": "更多选项", + "sending": "发送中…", + "security_authentication": "安全与认证", + "technical_details": "技术详情", + "message_id_label": "消息 ID:", + "reply_to_label": "回复至:", + "delivery_time_label": "投递时间:", + "conversation_part_label": "会话片段:", + "previous_messages": "{count} 条之前的消息", + "previous_messages_plural": "{count} 条之前的消息", + "time": { + "day": "天", + "days": "天", + "hour": "小时", + "hours": "小时", + "minute": "分钟", + "minutes": "分钟" + }, + "unknown_sender": "未知", + "recipient_me": "我", + "recipient_and_others": "{name} 和 {count} 其他人", + "recipient_to_prefix": "到:", + "authentication": { + "title": "邮件身份验证", + "status": { + "verified": "已验证", + "warning": "警告", + "none": "未验证" + }, + "spf": { + "pass": "SPF 通过", + "fail": "SPF 失败", + "none": "无 SPF" + }, + "dkim": { + "pass": "DKIM 有效", + "fail": "DKIM 无效", + "none": "无 DKIM" + }, + "dmarc": { + "pass": "DMARC 通过", + "fail": "DMARC 失败", + "none": "无 DMARC" + }, + "spam_score": "垃圾邮件评分", + "tooltip_spf": "Sender Policy Framework:验证发送服务器是否有权代表该域名发送邮件", + "tooltip_dkim": "DomainKeys Identified Mail:通过加密签名确认邮件在传输过程中未被篡改", + "tooltip_dmarc": "Domain-based Message Authentication, Reporting & Conformance:确保 SPF 与 DKIM 与发件人域名一致,并为失败设置处理策略", + "policy": "策略", + "result": { + "pass": "通过", + "fail": "失败", + "softfail": "软失败", + "neutral": "中立", + "permerror": "永久错误", + "temperror": "临时错误", + "none": "无" + } + }, + "headers": { + "routing": "路由", + "received": "接收时间", + "message_id": "消息 ID", + "list_info": "邮件列表信息" + }, + "tooltips": { + "reply": "回复 (r)", + "reply_all": "全部回复 (a)", + "forward": "转发 (f)", + "archive": "归档 (e)", + "delete": "删除 (# 或 Del)", + "star": "加星标 (s)", + "unstar": "取消星标 (s)", + "compose": "撰写邮件 (c)", + "previous": "上一封邮件", + "next": "下一封邮件", + "edit_draft": "编辑草稿" + }, + "spam": { + "button_title": "举报垃圾邮件", + "not_spam_title": "标记为正常邮件", + "toast_success": "已移至垃圾邮件", + "toast_batch": "{count} 封邮件已移至垃圾邮件", + "toast_undo": "撤销", + "toast_not_spam_success": "已移至收件箱", + "toast_not_spam_batch": "{count} 封邮件已移至收件箱", + "error": "举报垃圾邮件失败", + "error_not_spam": "无法恢复邮件" + }, + "unsubscribe_banner": { + "label": "订阅邮件", + "button": "取消订阅", + "confirm_title": "取消订阅该发件人吗?", + "confirm_button": "确认", + "cancel": "取消", + "success_http": "取消订阅页面已在新标签页中打开", + "success_mailto": "退订邮件已发送", + "confirm_message_http": "退订页面将在新标签页中打开。", + "confirm_message_mailto": "将向发件人发送退订邮件。", + "error": "无法取消订阅", + "dismiss": "关闭" + }, + "calendar_invitation": { + "loading": "正在加载活动详情…", + "title": "日历邀请", + "published_title": "已发布活动", + "response_title": "日程回复", + "update_title": "活动更新", + "counter_title": "改期建议", + "refresh_title": "刷新请求", + "declined_counter_title": "改期建议已被拒绝", + "cancelled_title": "活动已取消", + "organizer": "组织者:{name}", + "organizer_label": "组织者:", + "attendees": "{count, plural, one {# 位参与者} other {# 位参与者}}", + "accept": "接受", + "maybe": "暂定", + "decline": "拒绝", + "add_to_calendar": "添加到日历", + "added": "添加到日历", + "rsvp_sent": "回复已发送", + "parse_error": "无法读取邀请", + "action_failed": "无法完成该日历操作。", + "no_calendar": "日历不可用", + "published_info": "此事件仅供参考。", + "response_info": "此消息包含与会者的响应。", + "response_info_organizer": "此与会者回复会更新您的活动。", + "update_info": "此消息更新现有事件。", + "counter_info": "此消息提出了对该活动的改期建议。", + "counter_info_organizer": "该与会者对您的活动提出了改期建议。", + "refresh_info": "此消息请求最新的事件详细信息。", + "refresh_info_organizer": "与会者要求了解最新的活动详细信息。", + "declined_counter_info": "组织者拒绝了该改期建议。", + "authentication_failed_info": "此邀请的邮件身份验证检查失败。谨慎对待日历操作。", + "authentication_missing_info": "此邀请不包括经过验证的邮件身份验证。如果有任何异常情况,请与组织者确认详细信息。", + "sender_mismatch_info": "此邀请是从 {sender} 发送的,而日历数据中列出的组织者是 {organizer}。", + "sender_mismatch_unverified_info": "此邀请是从 {sender} 发送的,而日历数据中列出的组织者是 {organizer},且无法验证该消息。", + "organizer_role": "您是此活动的组织者", + "your_response": "您的回复:{status}", + "response_needed": "待回复", + "response_accepted": "已接受", + "response_tentative": "暂定", + "response_declined": "已拒绝", + "response_delegated": "已委托", + "actor_sent_info": "由 {name} 发送。", + "actor_response_info": "{name} 回复了 {status}。", + "actor_counter_info": "{name} 提议更改此活动的时间。", + "actor_refresh_info": "{name} 请求获取最新的活动详情。", + "actor_declined_counter_info": "{name} 拒绝了改期建议。", + "actor_note": "备注:{comment}", + "actor_unknown": "有人", + "proposed_changes": "建议的更改", + "change_title": "标题", + "change_time": "时间", + "change_location": "地点", + "change_description": "描述", + "change_empty": "无", + "change_from_to": "{before} -> {after}", + "apply_proposal": "应用建议的更改", + "proposal_applied": "已应用建议的更改。", + "review_proposal": "查看建议", + "review_request": "查看请求", + "view_in_calendar": "在日历中打开", + "select_calendar": "选择日历", + "already_in_calendar": "已在你的日历中", + "request_info": "您已被邀请参加此活动。回复以让组织者了解您是否参加。", + "cancel_info": "组织者已取消此活动。", + "event_updated": "更新 #{sequence}", + "event_status_tentative": "暂定", + "event_status_cancelled": "已取消", + "collapse": "隐藏详情", + "expand": "显示详情" + }, + "send": "发送", + "more": "更多", + "details": { + "recipients_routing": "收件人与路由", + "authentication_security": "身份验证与安全", + "identifiers_threading": "标识符与会话", + "mailing_list": "邮件列表", + "message_properties": "邮件属性", + "sent": "发送时间", + "received": "接收时间", + "delivery_time": "送达耗时", + "in_reply_to": "回复对象", + "references": "引用", + "thread_id": "会话 ID", + "size": "大小", + "mime_type": "MIME 类型", + "attachments_summary": "{count} 个文件 · {size}", + "list_id": "列表 ID", + "list_help": "列表帮助", + "list_post": "列表投递", + "list_unsubscribe": "取消订阅", + "iprev": "反向 DNS", + "spam_status": "垃圾邮件状态", + "ai_verdict": "AI 判断", + "account": "账户", + "no_subject": "(无主题)" + }, + "scheduled_banner": "计划于 {date} 发送", + "scheduled_send_created": "邮件已计划发送", + "sending_in": "{seconds} 秒后发送", + "undo_send_failed": "无法撤销。邮件可能已经发出。", + "cancel_scheduled_send": "取消发送", + "reschedule_send": "重新计划", + "cancel_and_edit": "取消并编辑", + "cancel_and_compose_again": "取消并重新撰写", + "reschedule_prompt": "输入新的日期/时间,例如 2026-05-04T15:30", + "scheduled_actions_only": "计划发送视图仅支持计划操作", + "undo_send_scheduled": "邮件已计划发送", + "undo_send": "撤销发送", + "contact_sidebar": { + "title": "联系人", + "close": "关闭侧边栏", + "action_email": "邮件", + "action_email_title": "发送邮件", + "action_copy": "复制", + "action_copy_title": "复制邮箱地址", + "action_edit_title": "编辑联系人", + "section_emails": "邮箱", + "section_phones": "电话", + "section_organizations": "组织", + "section_addresses": "地址", + "section_notes": "备注", + "not_in_contacts": "不在您的联系人中", + "add_to_contacts": "添加到联系人", + "copied": "已复制!", + "copy_failed": "复制失败", + "view_contact": "查看联系人", + "view_details": "查看详情" + }, + "send_now": "立即发送", + "verification_code": { + "copy": "复制验证码 {code}", + "copied": "验证码已复制", + "copy_failed": "无法复制验证码" + }, + "mv_position": "第 {index} 封,共 {total} 封", + "plugin_panel_show": "显示插件面板", + "plugin_panel_hide": "隐藏插件面板" + }, + "email_composer": { + "select_from_directory": "从目录中选择", + "minimize": "最小化", + "maximize": "全屏", + "restore": "还原", + "new_message": "新邮件", + "signature_edit_hint": "双击以编辑签名", + "reply": "回复", + "reply_all": "全部回复", + "forward": "转发", + "reply_to": "回复", + "reply_all_to": "全部回复", + "forward_message": "转发", + "from": "发件人", + "to": "收件人", + "cc": "抄送", + "bcc": "密送", + "subject": "主题", + "body_placeholder": "写邮件内容...", + "send": "发送", + "cancel": "取消", + "attach": "附件", + "attach_photos": "Photos & Videos", + "attach_files": "Files", + "attach_from_files": "从文件添加附件", + "files_picker": { + "attach": "{count, plural, =0 {添加附件} other {添加 # 个文件}}", + "empty_folder": "此文件夹为空", + "load_failed": "无法加载你的文件" + }, + "discard": "丢弃", + "discard_draft_title": "丢弃草稿?", + "discard_draft_confirm": "您有未保存的更改。确定要丢弃这封草稿吗?", + "discard_draft_message": "草稿将被删除。", + "saving": "保存中...", + "draft_saved": "草稿已保存", + "save_failed": "保存失败", + "to_placeholder": "收件人", + "cc_placeholder": "抄送", + "bcc_placeholder": "密送", + "subject_placeholder": "主题", + "cc_label": "抄送:", + "bcc_label": "密送:", + "subject_label": "主题:", + "file_size_kb": "KB", + "prefix": { + "forward": "Fwd:", + "reply": "Re:" + }, + "forward_batch_count": "{count} 封邮件", + "no_subject": "(无主题)", + "unknown_sender": "未知", + "quote": { + "reply_header": "在 {date},{sender} 写道:", + "forward_header": "---------- 转发的邮件 ----------", + "from": "来自:{sender}", + "date": "日期:{date}", + "subject": "主题:{subject}", + "to": "至:{recipients}" + }, + "remove_sub_address": "删除子地址", + "from_override": { + "toggle_off": "覆盖", + "toggle_on": "取消覆盖", + "toggle_tooltip": "自由编辑发件人姓名和地址。邮件仍通过您的身份发送。", + "name_label": "发件人姓名", + "name_placeholder": "姓名", + "email_label": "发件人电子邮件地址", + "email_placeholder": "alias@example.com", + "envelope_notice": "如果您的服务器不接受此地址作为信封发件人,将改用 {identity},收件人可以在 Return-Path 标头中看到它。" + }, + "save_as_template": "保存为模板", + "attach_from_device": "从此设备上传", + "templates_menu": "模板", + "insert_template": "插入模板", + "options": "选项", + "option_plain_text": "纯文本", + "option_plain_text_hint": "不带格式发送", + "option_read_receipt": "请求已读回执", + "option_read_receipt_hint": "请收件人确认已打开邮件", + "option_dsn": "投递通知", + "option_dsn_hint": "服务器投递后收到报告", + "option_require_tls": "要求加密投递", + "option_require_tls_hint": "仅通过 TLS 投递,否则退回", + "option_priority": "优先级", + "option_priority_high": "高", + "option_priority_normal": "普通", + "option_priority_low": "低", + "validation": { + "recipient_required": "请添加收件人", + "body_required": "请输入邮件内容或添加附件", + "invalid_recipients": "未发送 - 请检查这些地址:{addresses}", + "attachments_uploading": "附件仍在上传中,上传完成后将自动发送", + "attachment_upload_failed": "未发送 - 附件上传失败。请移除该附件后重试。" + }, + "upload_progress": "正在上传 {uploaded} / {total}", + "upload_cancel": "取消上传", + "upload_failed": "无法上传 {filename}", + "attachment_too_large": "“{name}”超过了服务器允许的大小(每个文件 {max})", + "attachments_total_too_large": "附件将超过服务器每封邮件 {max} 的上限", + "drop_files": "拖放要附加的文件", + "show_less": "收起", + "send_failed": "发送邮件失败", + "send_timeout": "服务器已停止响应。再次发送前请检查“已发送”文件夹——邮件可能已经发出。", + "send_offline": "你似乎已离线。邮件仍保留在这里,恢复联网后请再次发送。", + "formatting": "格式", + "send_recipients_rejected": "未发送:服务器拒绝了所有收件人。", + "send_plugin_failed": "未发送:负责发送的插件失败或耗时过长。再次发送前,请先检查“已发送”文件夹。", + "send_some_recipients_rejected": "已发送,但未发送给以下收件人:服务器拒绝了他们。", + "continue_draft": "继续编辑草稿", + "close_draft_title": "保存或丢弃草稿?", + "close_draft_message": "您有未保存的更改。要将其保存为草稿还是丢弃?", + "smime_sign_on": "S/MIME 签名已启用", + "smime_sign_off": "启用 S/MIME 签名", + "smime_encrypt_on": "S/MIME 加密已启用", + "smime_encrypt_off": "启用 S/MIME 加密", + "smime_encrypt_unavailable": "S/MIME 加密不可用 – 缺少收件人证书", + "smime_unlock_title": "解锁 S/MIME 密钥", + "smime_unlock_message": "输入密码以解锁您的 S/MIME 签名密钥。", + "smime_unlock_button": "解锁", + "smime_passphrase_placeholder": "输入密码", + "forgot_attachment": { + "title": "忘记添加附件了吗?", + "message": "您的消息中提到了“{keyword}”,但未附加任何文件。仍然发送吗?", + "send_anyway": "仍然发送", + "back": "返回编辑" + }, + "empty_subject": { + "title": "没有主题就发送吗?", + "message": "此邮件没有主题。仍要发送吗?", + "dont_ask_again": "不再提示", + "send_anyway": "仍然发送", + "back": "返回编辑" + }, + "add_link": "添加链接", + "link_url_prompt": "输入 URL", + "sending": "发送中...", + "schedule_send": "计划发送", + "schedule_send_description": "选择服务器应何时发送此邮件。", + "schedule_preset": { + "tomorrow_morning": "明天上午", + "tomorrow_afternoon": "明天下午", + "monday_morning": "周一上午" + }, + "schedule_send_required": "请选择日期和时间。", + "schedule_send_invalid": "请输入有效的日期和时间。", + "schedule_send_future": "请选择未来的日期和时间。", + "schedule_send_too_late": "该时间晚于服务器允许的范围。", + "schedule_send_unsupported": "此账户不支持计划发送。", + "schedule_send_cleanup_warning": "已创建计划发送,但清理草稿失败。", + "send_delay_unsupported": "此账户不支持发送延迟。", + "send_delay_unsupported_confirm": "此账户不支持发送延迟。是否立即发送?", + "recipient_edit_email": "编辑电子邮件地址", + "recipient_edit_name": "编辑显示名称", + "recipient_email_placeholder": "电子邮件地址", + "recipient_name_placeholder": "显示名称", + "invalid_recipient": "不是有效的邮箱地址", + "autocomplete_search_server": "在服务器上搜索", + "autocomplete_searching": "搜索中...", + "mention_recipients": "收件人", + "mention_card_not_recipient": "不在收件人或抄送中,因此不会收到这封邮件。", + "mention_card_add": "添加到{field}", + "mention_card_remove": "移除提及", + "toolbar": { + "bold": "加粗", + "italic": "斜体", + "underline": "下划线", + "strikethrough": "删除线", + "text_color": "文字颜色", + "remove_color": "移除颜色", + "heading_1": "标题 1", + "heading_2": "标题 2", + "bullet_list": "项目符号列表", + "ordered_list": "编号列表", + "quote": "引用", + "code_block": "代码块", + "align_left": "左对齐", + "align_center": "居中对齐", + "align_right": "右对齐", + "text_direction": "文字方向 (RTL/LTR)", + "link": "链接", + "table": "表格", + "clear_formatting": "清除格式", + "undo": "撤销", + "redo": "重做", + "add_row_above": "在上方添加行", + "add_row_below": "在下方添加行", + "add_column_before": "在前面添加列", + "add_column_after": "在后面添加列", + "delete_row": "删除行", + "delete_column": "删除列", + "toggle_header_row": "切换标题行", + "delete_table": "删除表格", + "pick_size": "选择大小", + "font_size": "字号", + "font_size_default": "默认", + "background_color": "背景颜色", + "remove_background_color": "移除背景颜色" + }, + "send_filing_warning": "已发送,但发送后的清理失败,可能会残留旧草稿。", + "pop_out": "在单独窗口中打开回复", + "send_guard_failed": "未发送 - 发送前检查邮件的插件出错或没有响应。" + }, + "confirm_dialog": { + "confirm": "确认", + "cancel": "取消" + }, + "common": { + "loading": "正在加载…", + "error": "错误", + "success": "成功", + "cancel": "取消", + "save": "保存", + "delete": "删除", + "edit": "编辑", + "close": "关闭", + "search": "搜索", + "refresh": "刷新", + "settings": "设置", + "help": "帮助", + "logout": "退出登录", + "yes": "是", + "no": "否", + "unknown": "未知", + "app_title": "网页邮箱", + "reconnecting": "连接已断开,正在尝试重连…", + "rate_limited_title": "服务器身份验证暂时受到速率限制。", + "rate_limited_detail": "Bulwark 已暂停后台请求以避免账户被锁定。将在 {seconds} 秒后重试。", + "rate_limited_action_title": "请求已暂停以避免账户被锁定。", + "rate_limited_action_detail": "Bulwark 正在等待服务器冷却,然后再发送更多请求。请在 {seconds} 秒后重试。", + "pull_to_refresh": "下拉刷新", + "release_to_refresh": "松开刷新", + "refreshing": "正在刷新…" + }, + "notifications": { + "email_sent": "邮件发送成功", + "email_deleted": "邮件已删除", + "email_archived": "邮件已归档", + "emails_deleted": "{count, plural, other {已删除 # 封邮件}}", + "emails_archived": "{count, plural, other {已归档 # 封邮件}}", + "error_archiving": "归档邮件失败", + "error_updating": "更新邮件失败", + "email_starred": "邮件已加星标", + "email_unstarred": "邮件已取消星标", + "email_marked_read": "邮件已标记为已读", + "email_marked_unread": "邮件已标记为未读", + "copied_to_clipboard": "已复制到剪贴板", + "source_copied": "源码已复制到剪贴板", + "error_sending": "发送邮件失败", + "error_deleting": "删除邮件失败", + "error_loading": "无法加载邮件", + "new_email": "新邮件", + "new_email_from": "来自 {sender}", + "click_to_view": "点击查看", + "email_moved": "邮件已移动", + "emails_moved": "{count} 封邮件已移动", + "moved_to_mailbox": "已移至 {mailbox}", + "move_failed": "移动失败", + "removed_from_folder": "已从 {mailbox} 中移除", + "remove_from_folder_failed": "无法从此文件夹中移除", + "emails_copied": "{count} 封邮件已复制", + "copy_failed": "复制失败", + "move_error": "无法将邮件移至所选文件夹", + "email_tagged": "邮件已加标签", + "emails_tagged": "{count} 封邮件已加标签", + "tag_failed": "标记失败", + "identity_created": "身份创建成功", + "identity_updated": "身份更新成功", + "identity_deleted": "身份已删除", + "identity_set_primary": "主要身份已更新", + "identity_create_failed": "无法创建身份:{error}", + "identity_update_failed": "无法更新身份:{error}", + "identity_delete_failed": "无法删除身份:{error}", + "identity_unauthorized": "您无权从此邮箱地址发送", + "identity_not_found": "未找到身份", + "vacation_saved": "外出设置已保存", + "vacation_save_failed": "无法保存外出设置", + "filters_saved": "过滤规则保存成功", + "filters_save_failed": "无法保存过滤器", + "filters_deleted": "过滤规则已删除", + "templates_exported": "模板导出成功", + "templates_imported": "已导入{count, plural, one {# 个模板} other {# 个模板}}", + "templates_import_errors": "部分模板无法导入", + "templates_import_empty": "文件中未找到模板", + "export_email_error": "导出邮件失败", + "import_email_success": "邮件导入成功", + "import_email_error": "导入邮件失败", + "rule_created": "规则已创建", + "rule_merged": "已添加到规则“{name}”", + "rule_already_covered": "规则“{name}”已涵盖此项", + "rule_apply_existing": "{count, plural, other {应用于 # 封已有邮件}}", + "rule_applied": "{count, plural, =0 {没有需要更改的邮件} other {规则已应用于 # 封邮件}}", + "rule_apply_failed": "无法将规则应用于已有邮件", + "rule_edit": "编辑规则", + "rule_undo": "撤销", + "rule_undone": "已撤销规则更改", + "rule_undo_failed": "无法撤销规则更改", + "rule_undo_conflict": "过滤器在此之后已更改。请在过滤器设置中撤销该更改。", + "rule_folder_failed": "无法创建文件夹", + "vacation_filters_save_failed": "外出设置已保存,但转发和回复对象设置未能保存" + }, + "date": { + "today": "今天", + "yesterday": "昨天", + "this_week": "本周", + "last_week": "上周", + "this_month": "本月", + "older": "更早", + "just_now": "刚刚", + "minutes_ago": "{count} 分钟前", + "minutes_ago_plural": "{count} 分钟前", + "hours_ago": "{count} 小时前", + "hours_ago_plural": "{count} 小时前", + "days_ago": "{count} 天前", + "days_ago_plural": "{count} 天前" + }, + "language": { + "title": "语言", + "english": "英语", + "french": "法语", + "japanese": "日语", + "spanish": "西班牙语", + "italian": "意大利语", + "german": "德语", + "dutch": "荷兰语", + "portuguese": "葡萄牙语", + "russian": "俄语", + "select_language": "选择语言", + "switch_to_english": "切换到英语", + "switch_to_french": "切换到法语", + "switch_to_japanese": "切换到日语", + "switch_to_spanish": "切换到西班牙语", + "switch_to_italian": "切换到意大利语", + "switch_to_german": "切换到德语", + "switch_to_dutch": "切换到荷兰语", + "switch_to_portuguese": "切换到葡萄牙语", + "switch_to_russian": "切换到俄语", + "switching": "正在切换语言...", + "switch": "切换语言", + "current": "当前语言", + "polish": "Polski", + "switch_to_polish": "切换到波兰语", + "en": "English", + "fr": "Français", + "de": "Deutsch", + "es": "Español", + "it": "Italiano", + "ja": "日本語", + "ko": "한국어", + "lv": "Latviešu", + "nl": "Nederlands", + "pl": "Polski", + "pt": "Português", + "he": "עברית", + "ru": "Русский", + "sk": "Slovenčina", + "uk": "Українська", + "zh": "简体中文", + "fa": "فارسی", + "farsi": "فارسی", + "switch_to_farsi": "切换到波斯语", + "mn": "Монгол", + "mongolian": "Монгол", + "switch_to_mongolian": "切换到蒙古语" + }, + "settings": { + "title": "设置", + "back_to_mail": "返回邮件", + "save_success": "设置保存成功", + "import_success": "设置导入成功", + "import_error": "导入设置失败", + "reset_confirm": "您确定要将所有设置重置为默认值吗?", + "unsaved_changes": "您有未保存的更改", + "discard_changes": "放弃未保存的更改?", + "discard": "放弃", + "keep_editing": "继续编辑", + "tabs": { + "appearance": "外观", + "language": "语言、地区与时间", + "email": "邮件行为", + "composer": "邮件撰写", + "privacy": "隐私与安全", + "account": "账户", + "identities": "身份", + "vacation": "外出", + "advanced": "高级", + "calendar": "日历", + "filters": "过滤器", + "templates": "模板", + "folders": "文件夹", + "keywords": "标签", + "security": "安全", + "files": "文件", + "contacts": "联系人", + "encryption": "加密", + "protocol_handlers": "默认应用", + "sidebar_apps": "侧边栏应用", + "notifications": "通知", + "layout": "布局", + "themes": "主题", + "reading": "阅读", + "composing": "撰写", + "downloads": "下载", + "content_senders": "内容和发件人", + "about_data": "关于和数据", + "debug": "调试" + }, + "themes": { + "title": "主题", + "description": "从管理员部署的主题和内置预设中选择。", + "locked": "主题选择已被管理员锁定。", + "forced": "主题“{name}”由管理员强制指定,无法更改", + "admin_theme": "管理员主题", + "activated": "主题已启用", + "default_restored": "已恢复默认主题", + "default_name": "默认", + "default_badge": "默认", + "admin_enforced": "管理员强制" + }, + "tab_groups": { + "general": "通用", + "account": "账户与身份", + "organization": "邮件组织", + "apps": "应用", + "system": "系统", + "appearance": "外观", + "mail": "邮件", + "privacy": "隐私和安全", + "advanced": "高级" + }, + "appearance": { + "message_list_order": { + "label": "邮件列表排序", + "description": "列表中邮件的排序方式。排序由服务器完成,因此作用于整个文件夹,而不仅是已加载的邮件。", + "preset": { + "chronological": "按时间(最新在前)", + "unread_first": "未读优先", + "starred_first": "星标优先", + "tagged_first": "标签优先", + "custom": "自定义" + }, + "tag_label": "标签", + "advanced_show": "高级:编辑排序层级", + "advanced_hide": "隐藏高级选项", + "criterion_label": "第 {n} 级条件", + "direction_label": "第 {n} 级方向", + "criteria": { + "unread": "未读", + "starred": "星标", + "tag": "标签", + "receivedAt": "接收日期", + "sentAt": "发送日期", + "from": "发件人", + "subject": "主题", + "size": "大小" + }, + "direction": { + "unread_first": "未读在前", + "read_first": "已读在前", + "starred_first": "星标在前", + "unstarred_first": "无星标在前", + "tagged_first": "有标签在前", + "untagged_first": "无标签在前", + "newest_first": "最新在前", + "oldest_first": "最早在前", + "a_to_z": "A → Z", + "z_to_a": "Z → A", + "largest_first": "最大在前", + "smallest_first": "最小在前" + }, + "add_level": "添加层级", + "remove_level": "移除层级", + "move_up": "上移", + "move_down": "下移", + "tiebreak_note": "最多三个层级;后续层级用于打破平局。“最新在前”始终最后应用。", + "scope": { + "label": "应用于所有文件夹", + "description": "关闭时,排序仅应用于收件箱,其他文件夹保持按时间排序。" + }, + "unsupported_note": "您的邮件服务器不支持按已读状态、星标或标签排序。这些层级将被忽略,列表保持按时间排序。" + }, + "title": "外观", + "description": "自定义邮箱的外观与风格", + "theme": { + "label": "主题", + "description": "选择您喜欢的配色方案", + "light": "浅色", + "dark": "深色", + "system": "系统" + }, + "language": { + "label": "语言", + "description": "选择您的首选语言" + }, + "font_size": { + "label": "字体大小", + "description": "调整文本大小以获得更好的可读性", + "small": "小", + "medium": "中", + "large": "大" + }, + "list_density": { + "label": "界面紧凑度", + "description": "调整界面元素之间的间距", + "extra_compact": "超紧凑", + "compact": "紧凑", + "regular": "默认", + "comfortable": "宽松" + }, + "interface_layout": { + "label": "界面布局", + "description": "搜索、导航和工具栏的位置", + "default": "默认", + "mountain_view": "Mountain View" + }, + "animations": { + "label": "启用动画", + "description": "显示平滑的过渡和效果" + }, + "toolbar_position": { + "label": "工具栏位置", + "description": "在哪里显示邮件操作按钮(回复、归档、删除等)", + "top": "顶部", + "below_subject": "主题下方" + }, + "toolbar_labels": { + "label": "显示工具栏标签", + "description": "在工具栏图标旁显示文字标签。熟悉图标后可关闭以节省空间。" + }, + "hide_account_switcher": { + "label": "隐藏侧边栏账户切换器", + "description": "隐藏文件夹侧边栏顶部的账户选择器。您仍然可以从底部导航栏切换账户。" + }, + "show_rail_account_list": { + "label": "在导航导轨上显示账户头像", + "description": "在导航栏底部显示账户头像,方便快速切换;下方会保留退出按钮。" + }, + "unified_mailbox": { + "label": "统一邮箱", + "description": "显示所有已连接账户的合并文件夹(收件箱、已发送等)", + "cross_account": { + "label": "Across all accounts", + "description": "Merge the unified mailbox across every connected account instead of staying within the active account." + }, + "include_group": { + "label": "包含群组收件箱", + "description": "在统一视图中也合并共享/群组收件箱。" + } + }, + "all_mail": { + "folders_label": "Folders in All Mail", + "folders_description": "Choose which folders are merged into the All Mail view.", + "account_hint": "Applies to {account}.", + "no_folders": "No folders available." + }, + "colorful_sidebar_icons": { + "label": "彩色侧边栏图标", + "description": "按类型为文件夹和标签图标着色(蓝色收件箱、红色垃圾邮件、绿色已发送等)。禁用以获得单色侧边栏。" + }, + "tint_list_rows": { + "label": "Tint List Rows by Tag Color", + "description": "Shade each message row with its first tag color. Disable to keep rows plain; tag dots and chips still show the color." + }, + "show_account_dot": { + "label": "Show Account Dot", + "description": "In the unified view, show a dot in the account color next to the sender, so you can tell which account a message came from. Has no effect while rows are tinted by account color." + }, + "tint_list_rows_by_account": { + "label": "Tint List Rows by Account Color", + "description": "In the unified view, shade each row with its account color instead of showing the account dot. Takes precedence over the tag tint; tags keep their color on their chips." + }, + "show_folder_total_count": { + "label": "显示邮件总数", + "description": "在文件夹和标签旁边显示邮件总数,以及未读数量。停用后仅显示未读数量。" + }, + "favicon_unread_badge": { + "label": "Unread Count on Tab Icon", + "description": "Show the inbox unread count as a badge on the browser tab icon, so new mail is visible when the tab is not focused. Disable to keep the icon unbadged." + }, + "show_shared_mail_accounts": { + "label": "显示共享邮件账户", + "description": "在侧边栏的“共享”下显示与你共享的邮箱的文件夹。关闭可全部隐藏,也可以在下方逐个隐藏账户。它们的日历和联系人仍会显示。" + }, + "pro_interface": { + "label": "Pro 界面(实验性)", + "description": "仅限桌面的高级用户布局,支持多标签消息浏览和跨账户工作流。标准界面不受影响,您可以随时切换回来。", + "open_label": "打开 Pro 界面", + "back_to_standard": "返回标准" + }, + "cross_unread": { + "label": "所有账户:未读", + "description": "在“所有账户”部分显示一个条目,列出所有账户的未读邮件,涵盖除垃圾邮件、已发送、归档、废纸篓和草稿之外的所有文件夹。" + }, + "cross_starred": { + "label": "所有账户:加星标", + "description": "在“所有账户”部分显示一个条目,列出所有账户的加星标邮件,涵盖除垃圾邮件、已发送、归档、废纸篓和草稿之外的所有文件夹。" + }, + "cross_all": { + "label": "所有账户:全部邮件", + "description": "在“所有账户”部分显示一个条目,列出所有账户的全部邮件,涵盖除垃圾邮件、已发送、归档、废纸篓和草稿之外的所有文件夹。" + } + }, + "keywords": { + "title": "电子邮件标签", + "description": "定义标签以使用颜色组织您的电子邮件。这些标签作为JMAP关键词存储在服务器上。", + "add_keyword": "添加标签", + "label_field": "显示名称", + "label_placeholder": "例如工作、个人、紧急", + "id_field": "标签ID", + "id_placeholder": "例如工作、个人", + "color_field": "颜色", + "id_exists": "此标签ID已存在", + "edit": "编辑标签", + "delete": "删除标签", + "save": "保存", + "add": "添加", + "cancel": "取消", + "migrating": "正在更新现有邮件的标签…", + "migration_error": "更新现有邮件的标签失败", + "filters_migration_error": "部分过滤规则仍在使用旧标签,请手动更新", + "nesting": { + "label": "嵌套标签", + "description": "将标签嵌套在其他标签之下,并在侧边栏中以树形显示。" + }, + "parent_field": "上级标签", + "no_parent": "无上级标签", + "too_long": "此标签路径过长(最多 {max} 个字符)", + "has_children_locked": "此标签下嵌套了其他标签,因此其名称和上级标签已锁定。请先移动或删除它们。", + "has_children_delete": "请先删除嵌套在此标签下的标签", + "visibility_field": "侧边栏显示", + "visibility": { + "show": "显示", + "unread": "有未读时显示", + "hide": "隐藏" + }, + "unrecognized": { + "title": "从服务器恢复标签", + "description": "标签的名称和颜色保存在此浏览器中,但标签本身以 JMAP 关键词的形式存在于邮件上。扫描邮件以查找此处没有对应标签的关键词,然后重新添加它们。", + "scan": "扫描标签", + "rescan": "重新扫描", + "scanning": "正在扫描… {total} 封邮件中的 {scanned} 封", + "none": "邮件上的所有标签均已在此定义。", + "messages": "{count, plural, one {# 封邮件} other {# 封邮件}}", + "add_all": "全部添加({count})", + "partial": "仅扫描了 {total} 封邮件中最新的 {scanned} 封。仅用于较旧邮件的标签可能会遗漏。", + "error": "无法扫描邮件中的标签。" + } + }, + "notifications": { + "test_sound": "测试通知声音", + "sounds": { + "default": "默认(蜂鸣声)", + "cheerful": "快乐", + "involved": "沉浸", + "swift": "轻快手势", + "relax": "放松" + }, + "push_all": { + "title": "All accounts", + "description": "Turn push on or off for several accounts at once. One browser subscription serves them all.", + "select_all": "Select every connected account", + "state_on": "notifications on", + "state_off": "notifications off", + "enable_selected": "Enable for selected", + "disable_selected": "Disable for selected", + "result_enabled": "Notifications enabled for {count} accounts.", + "result_partial": "Enabled for {enabled} accounts; {failed} could not be enabled.", + "result_disabled": "Notifications disabled for {count} accounts." + }, + "sound_selection": { + "title": "通知声音", + "description": "选择通知时播放的声音", + "choose": "声音", + "choose_desc": "选择通知音并单击扬声器图标进行预览" + }, + "email": { + "title": "邮件通知", + "description": "配置传入邮件的通知", + "enabled": "邮件通知", + "enabled_desc": "新邮件到达时显示通知", + "sound": "通知声音", + "sound_desc": "新邮件到达时播放音频提醒", + "inbox_only": "Inbox only", + "inbox_only_desc": "Only notify for mail that lands in the Inbox; skip mail a filter files into another folder" + }, + "calendar": { + "title": "日历通知", + "description": "配置日历事件的通知", + "enabled": "事件通知", + "enabled_desc": "显示即将发生的日历事件的提醒", + "sound": "通知声音", + "sound_desc": "播放日历提醒的音频提醒", + "invitation_parsing": "解析邮件邀请", + "invitation_parsing_desc": "检测邮件附件中的日历邀请并显示日历操作" + }, + "push": { + "confirm_disable_message": "当网站关闭时,此设备将停止接收提醒。", + "confirm_disable_title": "禁用后台通知?", + "description": "在此网站关闭时接收新邮件的系统通知。通过 Bulwark 推送中继传送;中继永远不会看到邮件内容。", + "disable": "禁用", + "enable": "启用", + "dismiss_aria": "关闭通知提示", + "ios_hint": "在 iOS 上,请先将网站安装到主屏幕 – Safari 仅向已安装的 PWA 发送 Web Push。", + "reenable": "重新注册", + "relay_desc": "默认使用托管的 Bulwark 中继。仅当您自托管时才更改。", + "relay_label": "推送中继", + "relay_locked": "由管理员设置", + "relay_locked_desc": "推送中继 URL 已由您的管理员设置,无法更改。", + "status_active": "在此设备上活动", + "status_busy": "工作中…", + "status_inactive": "在此设备上未启用", + "status_unsupported": "此浏览器不支持 Web Push", + "status_lite": "Web Push is not available in Bulwark Lite; it needs the Bulwark server.", + "title": "后台通知", + "devices_title": "已注册的设备", + "devices_desc": "已注册接收此账户后台通知的所有设备。撤销后,该设备会立即停止接收。", + "devices_loading": "正在加载已注册的设备…", + "devices_empty": "没有设备注册接收后台通知。", + "devices_error": "无法加载已注册的设备。", + "devices_refresh": "刷新", + "device_this": "此设备", + "device_other": "其他设备", + "device_status_active": "正在投递通知", + "device_status_inactive": "从未投递过通知", + "device_status_unknown": "此中继不识别", + "device_expires": "{date} 过期", + "revoke": "撤销", + "confirm_revoke_title": "要撤销此设备吗?", + "confirm_revoke_message": "它将立即停止接收后台通知。在该设备上重新启用推送会重新注册。", + "confirm_revoke_message_this": "此设备将立即停止接收后台通知。" + } + }, + "language_region": { + "title": "语言、地区与时间", + "description": "语言、日期格式、时间格式以及其他区域设置", + "language": { + "label": "语言", + "description": "选择您的首选语言", + "english": "英语", + "french": "法语" + }, + "date_format": { + "label": "日期格式", + "description": "电子邮件列表中日期的显示方式", + "smart": "智能", + "relative": "相对时间", + "full": "始终完整日期", + "preview_today": "今天:", + "preview_this_week": "本周:", + "preview_older": "更早:" + }, + "date_locale": { + "label": "Date format region", + "description": "How numeric dates are ordered (day, month, year)", + "auto": "Automatic", + "iso": "ISO 8601", + "dmy": "Day/Month/Year", + "mdy": "Month/Day/Year" + }, + "time_format": { + "label": "时间格式", + "description": "选择 12 小时制或 24 小时制", + "12h": "12 小时制", + "24h": "24 小时制" + }, + "first_day": { + "label": "一周的第一天", + "description": "从周日或周一开始一周", + "sunday": "周日", + "monday": "周一" + }, + "time_zone": { + "label": "Time zone", + "description": "Show email and calendar times in this time zone instead of the one reported by your browser", + "auto": "Automatic ({zone})", + "preview_now": "Now:" + } + }, + "email_behavior": { + "clear_search_on_folder_change": { + "label": "切换文件夹时清除搜索", + "description": "点击文件夹(收件箱、已发送等)会清除当前搜索并浏览该文件夹。关闭时,搜索会保留并在切换到的文件夹中重新运行。" + }, + "request_read_receipt": { + "label": "默认请求已读回执", + "description": "撰写新邮件时预先启用已读回执请求。" + }, + "read_receipt_response": { + "label": "响应已读回执请求", + "description": "当收到的邮件请求已读回执时的处理方式。", + "ask": "每次询问", + "always": "始终发送", + "never": "从不发送" + }, + "title": "邮件行为", + "description": "配置邮件的处理方式", + "mark_read": { + "label": "标记为已读", + "description": "何时将打开的邮件标记为已读", + "instant": "立即", + "delay_3s": "3 秒后", + "delay_5s": "5 秒后", + "never": "从不" + }, + "delete_action": { + "label": "删除操作", + "description": "删除邮件后的行为", + "trash": "移至已删除", + "trash_and_read": "移至已删除并标记为已读", + "permanent": "永久删除", + "warning": "邮件将被永久删除且无法恢复。此操作是不可逆转的。" + }, + "message_spacing": { + "label": "邮件边距", + "description": "阅读时正文周围的边距", + "auto": "自动(推荐)", + "always": "始终添加边距", + "edge": "无边距" + }, + "plain_text_font": { + "label": "纯文本字体", + "description": "用于纯文本邮件的字体", + "mono": "等宽", + "sans": "普通(与应用相同)" + }, + "archive_mode": { + "label": "归档方式", + "description": "归档时如何组织邮件", + "single": "单个文件夹", + "year": "按年归档", + "month": "按月归档", + "reorganize": "重新整理现有归档", + "reorganize_success": "{count, plural, =0 {没有需要整理的邮件} =1 {已整理 1 封邮件} other {已整理 # 封邮件}}", + "reorganize_error": "无法重新整理归档" + }, + "permanently_delete_junk": { + "label": "永久删除垃圾邮件", + "description": "从\"垃圾邮件\"文件夹中永久删除邮件,而不是将其移至\"已删除\"" + }, + "confirm_bulk_delete": { + "label": "删除多封邮件前确认", + "description": "将多封选中的邮件移到废纸篓前询问。永久删除始终会询问。" + }, + "mail_layout": { + "label": "邮件布局", + "description": "在经典分栏阅读和 Gmail 风格的沉浸阅读之间选择", + "split": "分栏视图", + "split_description": "左侧显示邮件列表,右侧显示阅读窗格。", + "focus": "沉浸模式", + "focus_description": "每封邮件显示一行,打开邮件时使用全宽阅读视图,同时保留文件夹侧边栏。", + "horizontal": "底部阅读窗格", + "horizontal_description": "在顶部显示邮件列表,并在下方的阅读窗格中打开所选邮件。" + }, + "list_columns": { + "label": "以列显示列表", + "description": "将单行列表显示为表格,表头下方为发件人、主题、日期和大小列。" + }, + "show_preview": { + "label": "显示预览文本", + "description": "在列表中显示邮件预览", + "focus_description": "在沉浸模式的单行邮件列表中显示预览文本" + }, + "show_verification_codes": { + "label": "显示验证码", + "description": "提供登录和确认邮件中的验证码,一键复制(在列表中显示一天,在邮件中始终显示)" + }, + "disable_threading": { + "label": "禁用对话分组", + "description": "将邮件显示为独立消息,而不是按会话分组" + }, + "plain_text_mode": { + "label": "仅纯文本", + "description": "禁用富文本编辑器,并将所有邮件(包括回复和转发)以纯文本发送" + }, + "auto_select_reply_identity": { + "label": "自动选择回复地址", + "description": "回复时自动将发件人地址切换为最初收到该邮件的身份" + }, + "reply_identity_match": { + "label": "接收地址匹配", + "description": "哪些接收地址算作您的地址。“仅精确地址”只从已配置的身份中选择;“相同域名”还会将身份域名下的其他任何地址视为 catch-all 别名,并将发件人标头改写为该地址。如果这些地址是分发列表,请选择精确地址。", + "exact": "仅精确地址", + "domain": "我的域名下的任意地址" + }, + "signature_position": { + "label": "签名位置", + "description": "在回复和转发中插入签名的位置。位于引用文本上方时,可作为回复的自然结尾;位于下方时,保持原始邮件连贯。", + "above_quote": "引用文本之前", + "below_quote": "引用文本之后" + }, + "signature_separator": { + "label": "签名分隔符", + "description": "在签名前加入标准的 “-- ” 分隔行(RFC 3676)。如果希望正文直接连到签名,请关闭。" + }, + "sub_address_delimiter": { + "label": "子地址分隔符", + "description": "用于分隔用户名和子地址标签的字符。请选择与您的邮件服务器一致的分隔符(例如 user{delimiter}tag@domain.com)。", + "option": "{delimiter} (user{delimiter}tag@domain.com)", + "custom": "自定义…", + "custom_input_label": "自定义分隔字符" + }, + "attachment_click_action": { + "label": "附件单击操作", + "description": "选择点击附件时是预览还是直接下载", + "preview": "尽可能预览", + "download": "立即下载" + }, + "attachment_position": { + "label": "附件位置", + "description": "在邮件标题中的何处显示附件", + "beside-sender": "发件人旁", + "below-header": "标题下方" + }, + "emails_per_page": { + "10": "10 封邮件", + "25": "25 封邮件", + "50": "50 封邮件", + "100": "100 封邮件", + "label": "每页邮件数", + "description": "一次加载的邮件数量" + }, + "always_light_mode": { + "label": "始终以浅色模式显示邮件", + "description": "即使应用处于深色模式,也以浅色模式呈现邮件内容,避免深色模式转换问题" + }, + "external_content": { + "label": "外部内容", + "description": "如何处理图像和外部内容", + "ask": "每次询问", + "block": "始终阻止", + "allow": "始终允许" + }, + "trusted_senders": { + "label": "受信任发件人", + "description": "管理自动加载图像的发件人", + "count_zero": "无", + "count_one": "1 位发件人", + "count_other": "{count} 位发件人", + "modal_title": "受信任发件人", + "empty_title": "尚无受信任发件人", + "empty_description": "查看包含被阻止图像的邮件时,单击\"始终信任此发件人\"将其添加到此处。", + "add_manually": "手动添加", + "add_button": "添加", + "add_placeholder": "输入邮箱地址", + "search_placeholder": "搜索发件人...", + "no_results": "没有匹配的发件人", + "remove": "移除", + "close": "关闭", + "invalid_email": "请输入有效的邮箱地址", + "already_added": "该发件人已被信任", + "save_error": "保存失败 - 请查看联系人调试日志了解详情", + "use_address_book_label": "与通讯录同步", + "use_address_book_description": "将受信任发件人存入专用“受信任发件人”通讯录,以便在所有设备间同步" + }, + "hover_actions": { + "label": "快速悬停操作", + "description": "选择将鼠标悬停在列表中的邮件上时显示的快速操作", + "delete": "删除", + "star": "加星标/取消星标", + "mark_read": "标记已读/未读", + "archive": "归档", + "tag": "标签", + "spam": "标记为垃圾邮件", + "not_spam": "非垃圾邮件", + "none_selected": "未选择任何操作", + "mode_label": "显示模式", + "mode_inline": "内嵌", + "mode_floating": "浮动", + "corner_label": "浮动位置", + "corner_top-left": "左上", + "corner_top-right": "右上", + "corner_bottom-left": "左下", + "corner_bottom-right": "右下" + }, + "default_mail_program": { + "label": "默认邮件应用", + "description": "将 {appName} 注册为 mailto: 链接的默认邮件应用", + "button": "设为默认", + "success": "浏览器已提示设置为默认", + "error": "您的浏览器不支持此功能" + }, + "attachment_reminder": { + "label": "附件提醒", + "description": "当消息提到附件但未附加任何文件时,在发送前发出警告", + "keywords_label": "触发关键词", + "keywords_description": "在消息中发现时触发提醒的词语或短语", + "add_placeholder": "添加关键词...", + "add": "Add", + "remove": "删除" + }, + "empty_subject_warning": { + "label": "空主题提醒", + "description": "发送没有主题的邮件前请求确认。" + }, + "recipient_mentions": { + "label": "使用 @ 提及收件人", + "description": "在正文中输入 @ 会列出收件人,并插入所选收件人的名字。按 Enter 或 Tab 选择高亮的名字。" + }, + "mention_adds_recipient": { + "label": "在 @ 提及中显示联系人", + "description": "输入 @ 时也会推荐你的联系人和目录中的人员。选择尚未在收件人中的人会将其添加到邮件。", + "to": "添加到收件人", + "cc": "添加到抄送", + "off": "仅显示收件人" + }, + "hide_inline_image_attachments": { + "label": "在附件列表中隐藏内嵌图片", + "description": "嵌入到邮件正文中的图片不会作为单独的附件显示" + }, + "attachment_image_previews": { + "label": "在附件中显示图片预览", + "description": "将图片附件显示为缩略图卡片,而不是通用文件图标" + }, + "send_delay": { + "label": "撤销发送 / 发送延迟", + "description": "通过服务器端短暂窗口延迟普通发送。", + "off": "关闭", + "seconds": "{seconds} 秒", + "unsupported": "当前账户未声明支持延迟发送。该设置仍会为其他账户保存。" + }, + "return_to_list_after_action": { + "label": "删除或标记为未读后返回列表", + "description": "删除打开的邮件或将其标记为未读后,返回邮件列表而不是打开下一封邮件。" + }, + "swipe_right_action": { + "label": "右滑操作(移动端)", + "description": "在触屏设备上,在列表中向右滑动邮件时执行的操作。" + }, + "swipe_left_action": { + "label": "左滑操作(移动端)", + "description": "在触屏设备上,在列表中向左滑动邮件时执行的操作。" + }, + "swipe_actions": { + "none": "无", + "archive": "归档", + "delete": "删除", + "mark_read": "切换已读", + "star": "切换星标", + "spam": "标记为垃圾邮件" + }, + "rtl_editing": { + "label": "从右到左编辑支持", + "description": "在编辑器工具栏中添加方向按钮,可将段落设置为从左到右或从右到左" + } + }, + "composer": { + "title": "邮件撰写", + "description": "配置邮件撰写相关设置", + "autosave": { + "label": "自动保存间隔", + "description": "自动保存草稿的频率", + "30s": "每 30 秒", + "1m": "每分钟", + "2m": "每 2 分钟", + "5m": "每 5 分钟" + }, + "send_confirmation": { + "label": "发送前确认", + "description": "发送邮件前要求确认" + }, + "default_reply": { + "label": "默认回复模式", + "description": "点击回复时的默认操作", + "reply": "回复", + "reply_all": "全部回复" + } + }, + "privacy": { + "title": "隐私与安全", + "description": "管理您的隐私和安全设置", + "external_images": { + "label": "阻止外部图像", + "description": "防止通过外部图像进行跟踪" + }, + "session_timeout": { + "label": "会话超时", + "description": "不活动后自动注销", + "never": "从不", + "30m": "30 分钟", + "1h": "1 小时", + "4h": "4 小时" + }, + "clear_cache": { + "label": "清除缓存", + "description": "删除缓存数据和临时文件", + "button": "清除缓存", + "confirm": "您确定要清除缓存吗?", + "success": "缓存清除成功" + } + }, + "account": { + "title": "账户", + "description": "查看您的账户信息", + "name_label": "显示名称", + "username_label": "用户名", + "account_type_label": "账户类型", + "auth_method_label": "认证方式", + "auth_method_oauth": "单点登录 (OAuth/OIDC)", + "auth_method_basic": "密码", + "auth_method_token": "访问令牌", + "demo_account": "演示账户", + "email": { + "label": "邮件", + "value": "{email}" + }, + "server": { + "label": "JMAP 服务器", + "value": "{server}" + }, + "storage": { + "label": "存储使用情况", + "used": "已使用 {used} / {total}", + "percentage": "已使用 {percent}%" + }, + "last_sync": { + "label": "上次同步", + "value": "{time}" + }, + "accounts": { + "title": "已登录账户", + "description": "拖动以重新排序账户在账户下拉菜单中的显示顺序", + "active": "当前活动账户", + "default_badge": "默认账户", + "set_default": "设为默认", + "switch_to": "切换到此账户", + "move_up": "上移", + "move_down": "下移", + "drag_handle": "拖动以重新排序", + "add": "添加账户" + }, + "backup": { + "title": "Account list backup", + "description": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. Mailbox passwords are not saved: you sign in to each restored account yourself.", + "description_passwords": "Keep the list of accounts in this browser on the server, encrypted with a password only you know, and bring it back on another device. You can include the mailbox passwords, so the restored accounts sign in by themselves.", + "status_none": "No list is saved for {account}.", + "status_saved": "A list is saved for {account}.", + "save": "Save list", + "restore": "Restore list", + "delete": "Delete saved list", + "cancel": "Cancel", + "delete_confirm": "Delete the saved account list from the server?", + "password": "Backup password", + "password_hint": "At least 10 characters. It cannot be recovered: if you forget it, save the list again with a new one.", + "restore_hint": "The password chosen when the list was saved.", + "confirm": "Confirm password", + "include_passwords": "Include mailbox passwords", + "include_passwords_hint": "Accounts signed in here with a password will sign in by themselves after a restore. Anyone who knows the backup password gets into every mailbox in the list.", + "saved": "List saved ({count, plural, one {# account} other {# accounts}}).", + "restored": "Accounts added: {added}. Accounts already here and updated: {updated}. Open each new account from the account list to sign in.", + "restored_signed_in": "Accounts added: {added}. Accounts already here and updated: {updated}.", + "skipped": "{count, plural, one {# account was} other {# accounts were}} not added: the account limit is reached.", + "saved_passwords": "Passwords included for {count, plural, one {# account} other {# accounts}}.", + "signed_in": "Signed in to {count, plural, one {# account} other {# accounts}} with the saved password.", + "sign_in_failed": "{count, plural, one {# saved password was} other {# saved passwords were}} refused: sign in to those accounts from the account list.", + "deleted": "Saved list deleted.", + "errors": { + "mismatch": "The passwords do not match.", + "password_length": "Use at least 10 characters.", + "unlock_failed": "Wrong password, or the list was saved for another account.", + "owner_signin_required": "Sign in to this account again, then retry.", + "https_required": "This needs a secure (HTTPS) connection.", + "storage_failed": "The server could not read or store the list. Try again later." + } + }, + "shared_accounts": { + "title": "与我共享", + "description": "您可以管理的群组和共享账户。选择一个以编辑其筛选器、休假自动回复、日历和联系人。", + "shared_label": "共享账户" + } + }, + "security": { + "title": "账户安全", + "description": "管理您的密码、双因素身份验证和安全设置", + "detecting": "正在检测服务器功能...", + "not_available": "此邮件服务器不支持账号安全管理。可能未启用所需权限。详情请参阅文档。", + "password": { + "title": "更改密码", + "current": "当前密码", + "new": "新密码", + "confirm": "确认新密码", + "submit": "更改密码", + "success": "密码修改成功", + "error_title": "密码修改失败", + "error_mismatch": "新密码不匹配", + "error_min_length": "密码必须至少为 8 个字符", + "error_generic": "更改密码失败" + }, + "display_name": { + "label": "显示名称", + "description": "显示在服务器上的名称", + "placeholder": "输入您的显示名称", + "save": "保存", + "success": "显示名称已更新", + "error": "无法更新显示名称" + }, + "totp": { + "section_title": "双因素身份验证", + "label": "TOTP 身份验证", + "description": "使用基于时间的一次性密码添加额外的安全层", + "active": "启用", + "inactive": "未启用", + "enabled": "启用双因素身份验证", + "disabled": "禁用双因素身份验证", + "enable_error": "无法启用 2FA", + "disable_error": "无法禁用 2FA", + "setup_instructions": "请将此 URL 复制到身份验证器应用(Google Authenticator、Authy 等)中:", + "verification_code": "验证码", + "confirm": "确认", + "disable": "禁用", + "disable_confirm_prompt": "输入密码以禁用双重身份验证。", + "password_required": "需要密码", + "code_required": "需要验证码", + "code_invalid": "验证码无效。请检查您的身份验证应用并重试。" + }, + "app_passwords": { + "title": "应用密码", + "description": "为不支持双因素身份验证的应用创建密码", + "add": "添加", + "create": "创建", + "cancel": "取消", + "generate": "生成", + "name_label": "应用名称", + "name_placeholder": "例如雷鸟、iPhone 邮件", + "password_label": "密码(留空自动生成)", + "password_placeholder": "如果为空则自动生成", + "added": "应用密码已创建", + "removed": "应用密码已删除", + "add_error": "创建应用密码失败", + "remove_error": "无法删除应用密码", + "none": "未配置应用密码", + "done": "完成", + "expires_label": "过期时间(可选)", + "copy_now_warning": "立即复制此密码--它将不再显示。", + "allowed_ips_label": "允许的 IP(可选)", + "allowed_ips_placeholder": "10.0.0.5, 192.168.1.0/24", + "allowed_ips_hint": "用逗号或空格分隔。留空则允许任意 IP。" + }, + "api_keys": { + "title": "API 密钥", + "description": "为直接与服务器通信的脚本和集成创建 API 密钥", + "name_label": "密钥名称", + "name_placeholder": "如:备份脚本、CI runner", + "copy_now_warning": "请立即复制此 API 密钥 - 它将不会再次显示。", + "added": "API 密钥已创建", + "removed": "API 密钥已删除", + "add_error": "创建 API 密钥失败", + "remove_error": "删除 API 密钥失败", + "none": "未配置 API 密钥" + }, + "encryption": { + "section_title": "静态加密", + "label": "邮件加密", + "description": "加密服务器上存储的邮件以获得额外的隐私", + "active": "{type} 加密已启用", + "inactive": "未启用", + "enabled": "启用静态加密", + "disabled_success": "静态加密已禁用", + "error": "无法更新加密设置", + "algorithm_label": "加密算法", + "encrypt_on_append": "上传时加密新邮件", + "allow_spam_training": "在加密邮件前允许垃圾邮件训练", + "enable_encryption": "启用加密", + "enable_tooltip": "使用此密钥启用加密", + "configure_title": "使用此密钥启用加密", + "enable_button": "启用", + "disable_tooltip": "禁用加密", + "enabled_success": "静态加密已启用" + }, + "email_client": { + "title": "邮件客户端设置", + "description": "使用这些凭据配置桌面端或移动端邮件客户端(Thunderbird、Apple Mail、Outlook 等)", + "jmap_username_label": "JMAP 用户名", + "copy": "复制", + "copied": "已复制", + "password_instructions": "使用上面的 JMAP 用户名和应用密码登录您的邮件客户端。如果您还没有创建应用密码,请在上面的部分中创建。" + }, + "link_device": { + "title": "关联移动应用", + "description": "无需在手机上输入密码即可登录手机上的 Bulwark Mail 应用。手机会获得独立的登录状态,因此在此处退出登录不会让手机退出。", + "generate": "显示二维码", + "regenerate": "显示新的二维码", + "instructions": "打开 Bulwark Mail 应用,在登录界面点按“Scan a sign-in code”(如果已登录,则点按“Add account”→“Scan a sign-in code”),然后将相机对准此处。", + "expires_in": "此二维码将在 {seconds} 秒后过期,且只能使用一次。", + "expired": "此二维码已过期。", + "error": "无法创建配对码。请重试。", + "password_prompt": "请输入密码以确认是你本人。手机会获得独立的登录状态;你的密码不会发送到手机。", + "show_password": "显示密码", + "hide_password": "隐藏密码", + "continue": "继续", + "error_wrong_password": "密码错误,请重试。", + "totp_prompt": "请输入身份验证器应用中的验证码。", + "error_password_or_code": "密码或验证码不正确。", + "error_not_signed_in": "你的会话已过期。请重新登录后再关联设备。", + "error_impersonation": "查看其他用户的邮箱时无法关联设备。", + "error_too_many_attempts": "尝试次数过多,请稍后再试。", + "error_session_secret": "此服务器未设置设备关联(缺少 SESSION_SECRET)。请联系管理员。", + "error_server_unreachable": "无法连接到你的邮件服务器,请重试。", + "error_pairing_unavailable": "你的邮件服务器不允许以这种方式关联设备。请联系管理员。", + "qr_alt": "登录二维码", + "link_label": "登录链接", + "copy_link": "复制登录链接", + "copied": "已复制", + "copy_hint": "没有相机?复制链接,然后在应用中通过“Paste sign-in link”粘贴。", + "success_title": "已在手机上登录", + "success_body": "你可以关闭此页面。如需关联其他设备,请显示新的二维码。", + "link_another": "关联其他设备", + "use_sso": "改用单点登录", + "error_insecure_server": "移动应用只通过 https 连接邮件服务器,而此服务器使用的是 http。" + }, + "public_keys": { + "name_label": "公钥名称", + "name_placeholder": "例如:我的密钥", + "key_label": "公钥(S/MIME 或 PGP ASCII 格式)", + "key_placeholder": "在此粘贴您的公钥", + "none": "未配置公钥" + } + }, + "identities": { + "title": "发送身份", + "description": "管理您可以发送邮件的邮箱地址", + "identities_count": { + "label": "你的身份", + "description": "配置用于发送的邮箱地址", + "count_zero": "暂无身份", + "count_one": "1 个身份", + "count_other": "{count} 个身份" + }, + "manage": "管理身份", + "sub_addressing": { + "label": "子寻址", + "description": "使用 user+tag@domain.com 这类标签来整理收到的邮件", + "learn_more": "了解更多" + } + }, + "vacation": { + "title": "外出", + "description": "在您不在时自动处理收到的邮件", + "loading": "正在加载自动回复设置…", + "not_supported": "您的邮件服务器不支持自动回复功能。", + "fetch_error": "加载自动回复设置失败,请重试。", + "status": { + "label": "发送自动回复", + "description": "向给您发送邮件的人自动发送回复", + "active": "已开启", + "inactive": "已关闭" + }, + "date_range": { + "start": "开始日期", + "start_description": "留空表示不限制开始时间", + "end": "结束日期", + "end_description": "留空表示不限制结束时间" + }, + "message": { + "subject_label": "主题", + "subject_description": "自动回复邮件的主题", + "subject_placeholder": "外出办公", + "body_label": "邮件正文", + "body_description": "纯文本邮件内容", + "body_placeholder": "感谢您的来信。我目前不在办公室,返回后会尽快回复您的邮件。", + "html_label": "带格式的消息(HTML)", + "html_description": "添加带链接和样式的格式化版本。邮件客户端无法显示时,收件人将看到上面的纯文本。", + "html_placeholder": "撰写带格式的外出自动回复…" + }, + "preview": { + "title": "预览", + "show": "显示预览", + "hide": "隐藏预览" + }, + "save": "保存更改", + "saving": "正在保存…", + "warnings": { + "end_before_start": "结束日期必须晚于开始日期", + "invalid_date": "请输入带时间的有效开始和结束日期,或将该字段留空", + "start_in_past": "开始日期已在过去", + "empty_body": "邮件正文为空--收件人将收到空白回复", + "subject_too_long": "主题对服务器来说太长(最多 {max} 字节)。", + "body_too_long": "消息对服务器来说太长(含格式最多 {max} 字节)。", + "forward_address": "请输入有效的转发地址", + "not_running": "转发或回复对象已保存,但当前未生效。保存即可重新启用。" + }, + "forward": { + "title": "转发", + "description": "在上方时间段内将收到的邮件转发到另一个地址,无论是否开启自动回复(未设结束日期时,直到关闭为止)。垃圾邮件不会被转发。", + "enabled_label": "转发邮件", + "to_label": "转发至", + "to_placeholder": "email@example.com", + "keep_label": "保留在收件箱中", + "keep_description": "否则邮件只会被转发:不会保留在此邮箱中,也不会对其执行任何过滤规则。" + }, + "audience": { + "label": "回复对象", + "description": "内部发件人:{domains}", + "all": "所有发件人", + "internal": "仅内部发件人", + "external": "仅外部发件人" + }, + "auto_reply": { + "title": "自动回复" + } + }, + "folders": { + "role_memos": "备忘录", + "title": "文件夹", + "description": "管理邮件文件夹并分配邮箱角色", + "folder_list": "您的文件夹", + "folder_list_description": "单击文件夹图标进行自定义", + "standard_roles": "标准文件夹角色", + "standard_roles_description": "指定哪些文件夹用于标准邮箱角色,例如收件箱、已发送、已删除等。", + "role_inbox": "收件箱", + "role_drafts": "草稿", + "role_sent": "已发送", + "role_trash": "已删除", + "role_junk": "垃圾邮件", + "role_archive": "归档", + "role_scheduled": "Scheduled", + "role_snoozed": "Snoozed", + "role_none": "无", + "create_folder": "创建文件夹", + "create_subfolder": "新建子文件夹", + "reorder": "拖动以重新排序", + "reorder_error": "文件夹重新排序失败", + "move_error": "移动文件夹失败", + "select_folder": "选择 {name}", + "select_hint": "选择。按住 Shift 点击可选中直到上次点击的所有文件夹。", + "selected_count": "已选择 {count, plural, other {# 个文件夹}}", + "move_to": "移动到...", + "move_to_top_level": "顶层", + "move": "移动", + "clear_selection": "清除选择", + "folders_moved": "{count, plural, other {已移动 # 个文件夹}}", + "move_partial_error": "{count, plural, other {# 个文件夹}}无法移动", + "subfolder_of": "{name} 内", + "subfolder_name": "子文件夹名称", + "new_folder_name": "文件夹名称", + "rename": "重命名", + "change_icon": "更改图标", + "count_mode": "{name} 的计数", + "count_mode_default": "计数:默认", + "count_mode_unread": "仅未读", + "count_mode_all": "未读和总数", + "count_mode_none": "不显示计数", + "delete": "删除", + "confirm_delete": "您确定要删除\"{name}\"吗?此文件夹中的邮件将被移至\"已删除\"。", + "create": "创建", + "cancel": "取消", + "no_folders": "没有自定义文件夹", + "cannot_delete_role": "无法删除具有标准角色的文件夹。先删除角色。", + "folder_created": "文件夹已创建", + "folder_renamed": "文件夹已重命名", + "folder_deleted": "文件夹已删除", + "role_updated": "文件夹角色已更新", + "error_create": "创建文件夹失败", + "error_rename": "重命名文件夹失败", + "error_delete": "删除文件夹失败", + "error_delete_has_children": "无法删除文件夹:其中仍包含子文件夹。请先删除或移动它们。", + "error_delete_has_email": "无法删除文件夹:其中仍包含邮件。请先移动或删除这些邮件。", + "error_role": "无法更新文件夹角色" + }, + "advanced": { + "title": "高级", + "description": "高级选项与开发者设置", + "debug_mode": { + "label": "调试模式", + "description": "启用详细日志记录以进行故障排除" + }, + "debug_categories": { + "description": "选择要记录的类别。禁用不需要的类别以减少控制台噪音。", + "jmap": "JMAP 客户端", + "jmap_description": "邮箱操作、邮件获取与 JMAP 协议请求", + "calendar": "日历", + "calendar_description": "日历事件、导入和安排消息", + "tasks": "任务", + "tasks_description": "日历任务创建、获取和更新", + "auth": "认证", + "auth_description": "登录、TOTP、令牌交换和会话管理", + "filters": "过滤器", + "filters_description": "Sieve 过滤规则和假期自动回复脚本", + "email": "邮件查看", + "email_description": "邮件渲染、TNEF 处理与已读标记", + "push": "推送通知", + "push_description": "推送通知设置和发送", + "contacts": "联系人与通讯录", + "contacts_description": "联系人同步、通讯录操作与受信任发件人" + }, + "settings_sync": { + "label": "设置同步", + "description": "跨浏览器和设备同步您的设置" + }, + "sender_favicons": { + "label": "发件人图标", + "description": "使用企业发件人的网站图标作为头像", + "refresh_label": "刷新发件人图标", + "refresh_description": "重新获取网站图标,例如发件人更换了图标时", + "refresh_button": "刷新", + "refresh_done": "将重新获取发件人图标" + }, + "show_avatars_in_junk": { + "label": "在垃圾邮件文件夹中显示头像", + "description": "在垃圾邮件文件夹中显示发件人的头像和网站图标。默认关闭,避免钓鱼邮件因熟悉的图标看起来更可信。" + }, + "keyboard_shortcuts": { + "label": "键盘快捷键", + "description": "查看可用快捷键", + "button": "查看快捷方式" + }, + "refresh_cache": { + "label": "Refresh cached data", + "description": "Reload contacts, calendars, and folders from the server. Keeps your accounts and sessions — fixes a stale or wrong view without signing out.", + "button": "Refresh" + }, + "reset_settings": { + "label": "重置设置", + "description": "将所有设置恢复为默认值", + "button": "重置为默认值" + }, + "export_settings": { + "label": "导出设置", + "description": "将设置导出为 JSON 文件", + "button": "导出" + }, + "import_settings": { + "label": "导入设置", + "description": "从 JSON 文件导入设置", + "button": "导入" + }, + "about": { + "title": "Bulwark 网页邮箱", + "source_code": "源代码" + }, + "spam_siege": { + "title": "Spam Siege", + "intro": "在垃圾邮件和钓鱼邮件进入收件箱之前点击拦截。放行真实邮件。三面盾牌全部失去,收件箱就会失守。", + "kinds": { + "spam": "垃圾邮件", + "phishing": "钓鱼邮件", + "legit": "真实邮件" + }, + "click_to_block": "点击拦截", + "let_through": "放行", + "start": "开始", + "play_again": "再玩一次", + "score": "得分", + "combo": "连击奖励", + "shields_left": "剩余盾牌 {count}/{total}", + "seconds": "{seconds} 秒", + "best": "最高分:{score}", + "new_best": "新纪录", + "held": "收件箱守住了", + "overrun": "收件箱失守", + "stats": { + "caught": "已拦截", + "delivered": "已送达", + "mistakes": "失误" + } + } + }, + "sidebar_apps": { + "title": "侧边栏应用", + "description": "管理侧边栏中的自定义应用和链接", + "keep_loaded": "保持应用加载", + "keep_loaded_description": "在内联应用之间切换时,保持内联应用在后台运行以避免重新加载", + "managed_title": "由管理员提供", + "managed_description": "这些应用已为所有人配置,无法更改。", + "manage_title": "定制应用", + "manage_description": "从侧边栏添加、编辑或删除自定义应用" + }, + "contacts": { + "title": "联系人", + "description": "导入和导出联系人", + "import_label": "导入联系人", + "import_description": "从 vCard (.vcf) 文件导入联系人", + "export_label": "导出联系人", + "export_description": "将所有联系人导出为 vCard (.vcf) 文件", + "manage_title": "地址簿", + "manage_description": "重命名您的地址簿", + "no_address_books": "未找到地址簿", + "categories_title": "类别", + "categories_description": "重命名联系人类别", + "no_categories": "未找到类别", + "group_by_letter_description": "在联系人列表中显示按字母顺序排列的分节标题", + "group_by_letter_label": "按首字母分组", + "sort_by_last_name_label": "按姓氏排序", + "sort_by_last_name_description": "按姓氏排列联系人列表,使家庭成员显示在一起" + }, + "downloads": { + "title": "下载", + "description": "自定义下载的邮件和附件的命名方式。", + "reset": "恢复默认", + "preview": "预览:", + "email_template": { + "label": "邮件文件名 (.eml)", + "description": "导出邮件或将其拖到文件系统时使用的模板。扩展名 .eml 会自动添加。" + }, + "attachment_template": { + "label": "附件文件名", + "description": "下载或拖出附件时使用的模板。如果省略 '{filename}' 和 '{ext}',将保留原始扩展名。" + }, + "bundle_template": { + "label": "多邮件 .zip 文件名", + "description": "将多个选中的邮件作为单个 .zip 拖出或下载时使用的模板。扩展名 .zip 会自动添加。" + }, + "spaces": { + "label": "空格", + "description": "用其他字符替换最终文件名中的空格。", + "keep": "保留空格", + "underscore": "替换为 _", + "dash": "替换为 -" + }, + "lowercase": { + "label": "小写", + "description": "将整个文件名强制转换为小写。" + }, + "strip_diacritics": { + "label": "去除变音符号", + "description": "将带重音的字母转换为对应的 ASCII 字符(ä → a, é → e)。对处理 Unicode 文件名有问题的工具很有用。" + }, + "collapse_separators": { + "label": "合并重复的分隔符", + "description": "将连续的空格、下划线或短横线合并为单个字符。" + }, + "after_export": { + "label": "导出后", + "description": "将邮件导出为 .eml 后可选择移动到其他位置。", + "keep": "保留在邮箱中", + "archive": "移动到归档", + "trash": "移动到回收站" + } + }, + "filters": { + "title": "邮件过滤器", + "description": "创建规则,自动整理、标记和管理收到的邮件", + "add_rule": "添加规则", + "no_rules": "暂无过滤规则", + "no_rules_description": "创建规则以自动整理收到的邮件", + "vacation_active": "假期自动回复已开启", + "vacation_active_description": "已启用传入消息的自动回复", + "vacation_configure": "配置", + "edit_rule": "编辑规则", + "new_rule": "新规则", + "delete_rule": "删除规则", + "delete_confirm": "您确定要删除此规则吗?", + "enable": "启用", + "disable": "禁用", + "raw_editor": "原始 Sieve 编辑器", + "raw_editor_warning": "编辑原始 Sieve 脚本可能会破坏可视化规则编辑。此处所做的更改会覆盖视觉生成器。", + "validate": "验证", + "validation_success": "脚本有效", + "validation_error": "脚本有错误", + "save": "保存规则", + "saving": "保存中...", + "saved": "过滤器保存成功", + "save_failed": "无法保存过滤器", + "loading": "正在加载过滤器...", + "not_supported": "您的邮件服务器不支持邮件过滤器。", + "rule_name": "规则名称", + "rule_name_placeholder": "例如,整理订阅邮件", + "match_all": "匹配所有条件", + "match_any": "匹配任何条件", + "conditions": "条件", + "add_condition": "添加条件", + "actions": "操作", + "add_action": "添加操作", + "stop_processing": "停止处理后续规则", + "include_spam": "同时移动被标记为垃圾邮件的邮件", + "attachment_type_placeholder": "例如:pdf、doc、jpg", + "value_placeholder_multi": "值(多个用逗号分隔)", + "condition_fields": { + "attachment": "附件", + "from": "发件人", + "to": "收件人", + "cc": "抄送", + "subject": "主题", + "header": "自定义标头", + "size": "大小", + "body": "正文", + "all": "所有邮件" + }, + "comparators": { + "has_any": "存在", + "has_type": "类型为", + "contains": "包含", + "not_contains": "不包含", + "is": "等于", + "not_is": "不是", + "starts_with": "以…开头", + "ends_with": "以…结尾", + "matches": "匹配模式", + "greater_than": "大于", + "less_than": "小于", + "address_is": "地址为", + "domain_is": "域名为" + }, + "action_types": { + "move": "移至文件夹", + "copy": "复制到文件夹", + "forward": "转发至", + "mark_read": "标记为已读", + "star": "加星标", + "add_label": "添加标签", + "discard": "丢弃(静默删除)", + "reject": "拒绝并留言", + "keep": "保留在收件箱中", + "stop": "停止处理" + }, + "move_to_folder": "选择文件夹", + "copy_to_folder": "选择文件夹", + "forward_to": "转发至邮箱地址", + "forward_placeholder": "email@example.com", + "forward_keep_copy": "保留副本", + "forward_limit": "此服务器每封邮件的转发上限:{count}。多余的转发将被跳过。", + "forward_unavailable": "此服务器不允许转发。过滤规则中的转发操作不会生效。", + "reject_message": "拒绝留言", + "reject_placeholder": "您的邮件已被拒绝", + "label_name": "标签名称", + "label_placeholder": "选择标签", + "header_name": "标头名称", + "header_placeholder": "例如,X-Mailing-List", + "size_bytes": "大小(以字节为单位)", + "size_placeholder": "例如,1000000", + "system_managed": "系统管理的规则", + "opaque_warning": "该脚本是在可视化编辑器之外编辑的。仅可使用原始 Sieve 编辑。", + "open_sieve_editor": "打开原始 Sieve 编辑器", + "fetch_error": "加载过滤器失败", + "expanded_view": "展开视图", + "expanded_view_description": "显示包含详细条件和操作的过滤规则", + "if": "如果", + "then": "然后", + "match_all_conditions": "全部匹配", + "match_any_condition": "任一匹配", + "and": "和", + "or": "或", + "cancel": "取消", + "confirm_delete": "删除", + "rule_list": "过滤规则", + "drag_to_reorder": "拖动以重新排序", + "match_type": "匹配类型", + "reset_to_visual": "重置为可视化编辑器", + "reset_warning": "这将丢弃当前脚本并重新开始。", + "confirm_reset": "重置", + "validation_empty_name": "规则名称为必填项", + "validation_empty_conditions": "至少需要一个具有值的条件", + "validation_empty_actions": "至少需要执行一项操作", + "templates_section": "从模板开始", + "template_newsletters": "将订阅邮件移至文件夹", + "template_receipts": "自动归档收据", + "template_important": "标记重要邮件", + "template_notifications": "过滤通知", + "sieve_editor": { + "title": "Sieve 脚本编辑器", + "warning": "编辑原始 Sieve 脚本可能会破坏可视化规则编辑。此处所做的更改会覆盖视觉生成器。", + "script_content": "Sieve 脚本", + "valid": "脚本有效", + "invalid": "脚本有错误", + "save_warning": "保存将覆盖所有可视化规则,此操作无法撤销。再次点击\"保存\"以确认。", + "validating": "正在验证...", + "validate": "验证", + "cancel": "取消", + "save": "保存", + "confirm_save": "确认保存", + "validation_failed": "验证请求失败" + }, + "rule_summary": { + "conditions_count": "{count, plural, one {# 个条件} other {# 个条件}}", + "actions_count": "{count, plural, one {# 个操作} other {# 个操作}}" + }, + "origin_external": "外部", + "managed_by_tooltip": "由 {source} 管理。请在该应用中编辑,或使用原始 Sieve 编辑器。", + "quick_rule_names": { + "move_sender": "将 {sender} 的邮件移至 {folder}", + "move_domain": "将 @{domain} 的邮件移至 {folder}", + "move_list": "将列表 {list} 移至 {folder}", + "mark_read": "将 {sender} 的邮件标记为已读", + "tag": "为 {sender} 的邮件添加标签 {tag}", + "block": "屏蔽 {sender}", + "custom": "来自 {sender} 的邮件", + "custom_plain": "新规则", + "senders": "{count, plural, other {# 个发件人}}" + }, + "suggestions": "建议", + "suggest_subject": "主题“{subject}”", + "suggest_to": "收件人 {address}", + "suggest_cc": "抄送 {address}", + "suggest_list": "List-Id", + "suggest_domain": "@{domain}", + "apply_existing": "同时应用于此文件夹中已有的邮件", + "apply_existing_unsupported": "只有检查发件人、收件人、主题或邮件头,并执行移动、复制、标记为已读、加星标或添加标签的规则,才能应用于已有邮件。", + "period_title": "日期范围", + "period_start": "开始日期", + "period_end": "结束日期", + "validation_period_order": "结束日期必须晚于开始日期", + "period_toggle": "仅在指定时间段内生效", + "period_hint": "适用于在此时间段内收到的邮件。留空某一项即表示该端不设限制。", + "validation_period_empty": "请输入时间段的开始日期或结束日期", + "validation_period_invalid": "请为时间段输入带时间的有效日期,或将该字段留空", + "apply_existing_period": "设置了时间段的规则只作用于在该时间段内收到的邮件,因此无法应用于已有邮件。", + "period_status_scheduled": "未开始", + "period_status_active": "生效中", + "period_status_expired": "已过期" + }, + "templates": { + "title": "邮件模板", + "description": "使用占位符变量创建可重用的邮件模板", + "add": "新模板", + "edit": "编辑模板", + "name": "模板名称", + "name_placeholder": "例如,后续邮件", + "category": "类别", + "category_placeholder": "例如,工作、个人", + "subject": "主题", + "subject_placeholder": "邮件主题行", + "body": "正文", + "body_placeholder": "邮件正文内容...", + "recipients_placeholder": "email@example.com", + "identity": "发送为", + "default_identity": "默认身份", + "favorite": "收藏", + "cancel": "取消", + "create": "创建", + "update": "更新", + "confirm_delete": "删除", + "no_templates": "还没有模板", + "manage": "管理模板", + "count": "{count, plural, one {# 个模板} other {# 个模板}}", + "export_import": "导出和导入", + "export_import_description": "备份您的模板或将其传输到其他设备", + "export": "导出", + "import": "导入", + "validation": { + "empty": "模板名称为必填项", + "too_long": "模板名称不得超过 200 个字符" + } + }, + "files": { + "display": { + "title": "显示", + "description": "配置文件和文件夹的显示方式" + }, + "default_view": { + "label": "默认视图", + "description": "在网格布局和列表布局之间进行选择", + "list": "列表", + "grid": "网格" + }, + "default_sort": { + "label": "默认排序", + "description": "选择文件的默认排序规则", + "name": "名称", + "size": "大小", + "modified": "修改时间" + }, + "sort_direction": { + "label": "排序方式", + "description": "选择升序或降序", + "ascending": "升序", + "descending": "降序" + }, + "icons": { + "title": "图标", + "description": "配置文件图标外观" + }, + "show_icons": { + "label": "显示文件图标", + "description": "在文件和文件夹旁边显示图标" + }, + "colored_icons": { + "label": "彩色图标", + "description": "使用彩色图标而不是单色图标" + }, + "show_thumbnails": { + "label": "显示缩略图", + "description": "显示图像预览而不是图像文件的图标" + }, + "behavior": { + "title": "行为", + "description": "配置文件浏览器行为" + }, + "show_hidden": { + "label": "显示隐藏文件", + "description": "显示以点开头的文件和文件夹" + }, + "preview": { + "label": "预览" + } + }, + "search_placeholder": "搜索设置", + "search_clear": "清除搜索", + "search_no_results": "没有匹配的设置", + "scoped": { + "back": "返回我的账户", + "managing": "管理:{name}" + } + }, + "errors": { + "page_error_title": "出了点问题", + "page_error_description": "我们遇到了意外错误。请重试或返回主页。", + "sidebar_error": "无法加载邮箱", + "email_list_error": "无法加载邮件", + "viewer_error_title": "无法显示邮件", + "viewer_error_description": "呈现此邮件时出现问题。它可能包含不受支持的内容。", + "composer_error": "无法加载邮件撰写", + "settings_error_title": "设置不可用", + "settings_error_description": "无法加载设置。您的偏好设置可能不会被保存。", + "try_again": "再试一次", + "reload": "重新加载", + "reload_emails": "重新加载邮件", + "reload_settings": "重新加载设置", + "retry": "重试", + "go_home": "前往收件箱" + }, + "context_menu": { + "reply": "回复", + "reply_all": "全部回复", + "forward": "转发", + "mark_read": "标记为已读", + "mark_unread": "标记为未读", + "star": "加星标", + "unstar": "取消星标", + "pin": "固定", + "unpin": "取消固定", + "move_to": "移动到…", + "copy_to_account": "复制到账户…", + "remove_from_folder": "从此文件夹中移除", + "archive": "归档", + "delete": "删除", + "mark_as_spam": "举报垃圾邮件", + "not_spam": "不是垃圾邮件", + "tag": "标签", + "items_selected": "已选择 {count} 封邮件", + "forward_as_attachments": "作为附件转发", + "edit_draft": "编辑草稿", + "cancel_scheduled_send": "取消发送", + "reschedule_send": "重新计划", + "cancel_and_edit": "取消并编辑", + "cancel_and_compose_again": "取消并重新撰写", + "rules": { + "title": "规则", + "move_from": "始终移动来自 {sender} 的邮件", + "move_from_domain": "始终移动来自 @{domain} 的邮件", + "move_from_list": "始终移动此邮件列表的邮件", + "mark_read_from": "始终将来自 {sender} 的邮件标记为已读", + "tag_with": "始终添加标签", + "block": "{count, plural, =1 {屏蔽发件人} other {屏蔽 # 个发件人}}", + "no_junk": "此账户没有垃圾邮件文件夹", + "create": "创建规则…", + "manage": "管理规则", + "senders_count": "{count, plural, other {# 个发件人}}", + "cross_account": "请选择同一账户中的邮件", + "opaque_hint": "你的过滤器已被手动编辑。请打开过滤器设置" , + "loading": "正在加载规则…", + "load_failed": "无法加载规则", + "none": "暂无规则", + "new_mail_only": "仅限新邮件", + "running": "正在 {folder} 中运行“{name}”…" + }, + "find_emails_from": "查找来自 {name} 的邮件" + }, + "mailbox_context_menu": { + "mark_folder_read": "将文件夹标记为已读", + "mark_folder_tree_read": "将文件夹及子文件夹标记为已读", + "mark_all_folders_read": "将所有文件夹标记为已读", + "new_subfolder": "新建子文件夹...", + "new_folder": "新建文件夹...", + "rename": "重命名...", + "share": "共享...", + "import_email": "导入 .eml 或 .zip...", + "empty_folder": "清空文件夹", + "empty_folder_generic": "清空文件夹", + "delete_folder": "删除文件夹", + "refresh": "刷新", + "hide_shared_account": "从侧边栏隐藏", + "mute_notifications": "静音通知", + "unmute_notifications": "取消静音通知", + "mute_account_notifications": "静音此账户的通知", + "unmute_account_notifications": "取消静音此账户的通知", + "reset_folder_notifications": "重置文件夹通知设置", + "mark_all_confirm_title": "将所有文件夹标记为已读", + "mark_all_confirm_message": "将您个人账户中的所有未读邮件标记为已读?", + "delete_confirm_title": "删除文件夹", + "delete_confirm_message": "永久删除文件夹 \"{name}\"?此操作无法撤销。", + "prompt_new_subfolder": "请输入新子文件夹的名称。", + "prompt_new_folder": "请输入新文件夹的名称。", + "prompt_rename": "请输入此文件夹的新名称。", + "toast_marked_read": "文件夹已标记为已读", + "toast_marked_read_count": "已将 {count, plural, one {1 封邮件} other {# 封邮件}} 标记为已读", + "toast_already_read": "没有未读邮件", + "toast_marked_all_read": "所有文件夹已标记为已读", + "toast_emptied": "文件夹已清空", + "toast_folder_created": "文件夹已创建", + "toast_folder_renamed": "文件夹已重命名", + "toast_folder_deleted": "文件夹已删除", + "toast_error_mark_read": "标记为已读失败", + "toast_error_empty": "清空文件夹失败", + "toast_error_create": "创建文件夹失败", + "toast_error_rename": "重命名文件夹失败", + "toast_error_delete": "删除文件夹失败", + "toast_error_delete_has_children": "文件夹包含子文件夹,请先将其删除。", + "toast_error_delete_has_email": "文件夹不为空,请先清空它。", + "delete_with_contents_title": "文件夹不为空", + "delete_with_contents_message": "文件夹“{name}”中仍有 {count} 封邮件。删除该文件夹及其所有邮件?此操作无法撤销。", + "delete_with_contents_confirm": "删除文件夹和邮件", + "placeholder_folder_name": "文件夹名称", + "create": "创建", + "rename_confirm": "重命名" + }, + "shortcuts": { + "title": "键盘快捷键", + "tip": "按?随时显示此帮助", + "sections": { + "navigation": "导航", + "actions": "邮件操作", + "global": "全局", + "threads": "会话", + "composer": "邮件撰写" + }, + "navigation": { + "next_email": "下一封邮件", + "previous_email": "上一封邮件", + "open_email": "打开邮件", + "close_email": "关闭/取消选择", + "go_to_folder": "跳转到文件夹", + "next_unread": "下一封未读邮件", + "no_more_unread": "列表下方没有更多未读邮件" + }, + "actions": { + "reply": "回复", + "reply_all": "全部回复", + "forward": "转发", + "star": "添加或取消星标", + "archive": "归档", + "delete": "删除", + "mark_unread": "标记为未读", + "mark_read": "标记为已读", + "toggle_spam": "举报垃圾邮件/非垃圾邮件" + }, + "global": { + "compose": "撰写新邮件", + "search": "聚焦搜索框", + "help": "显示快捷方式", + "refresh": "刷新邮件", + "select_all": "全选", + "global_search": "全局搜索", + "extend_selection": "向下 / 向上扩展选择" + }, + "threads": { + "expand_collapse": "展开/折叠会话", + "select_mountain_view": "选择会话" + }, + "composer": { + "send": "发送邮件", + "schedule_send": "定时发送", + "template_picker": "打开模板选择器" + } + }, + "threads": { + "messages_one": "{count} 条消息", + "messages_other": "{count} 条消息", + "messages_tooltip": "{count, plural, one {此会话中有 # 条消息} other {此会话中有 # 条消息}}", + "expand": "展开会话", + "collapse": "折叠会话", + "loading": "正在加载会话...", + "mark_read": "将会话标记为已读", + "mark_unread": "将会话标记为未读", + "archive": "归档会话", + "delete": "删除会话", + "star": "会话加星标", + "unstar": "取消会话星标", + "toggle_thread": "展开/折叠会话" + }, + "identities": { + "modal_title": "管理发送身份", + "create_new": "创建新身份", + "edit_identity": "编辑身份", + "delete_confirm": "删除这个身份?此操作无法撤消。", + "cannot_delete": "该身份无法删除", + "primary_identity": "主要", + "set_as_primary": "设为主要", + "no_identities": "没有找到身份信息", + "display": { + "reply_to": "回复地址:", + "bcc": "密送:", + "signature": "签名:", + "preview": "预览:" + }, + "validation_errors": { + "invalid_emails": "无效邮箱地址:{emails}", + "unknown_error": "未知错误" + }, + "form": { + "name_label": "显示名称", + "name_placeholder": "例如,工作邮箱、个人邮箱", + "name_required": "名称为必填项", + "email_label": "邮箱地址", + "email_placeholder": "your.email@example.com", + "email_required": "邮箱地址为必填项", + "email_invalid": "请输入有效的邮箱地址", + "email_immutable": "邮箱地址创建后无法更改", + "reply_to_label": "回复地址(可选)", + "reply_to_placeholder": "reply@example.com", + "bcc_label": "自动密送(可选)", + "bcc_placeholder": "archive@example.com", + "text_signature_label": "文字签名", + "html_signature_label": "HTML 签名", + "signature_byte_counter": "{bytes} / {max} 字节", + "signature_byte_limit_reached": "已达服务器限制", + "save": "保存身份", + "cancel": "取消", + "creating": "创建中...", + "updating": "更新中..." + }, + "sub_address": { + "button_tooltip": "使用子地址", + "popover_title": "添加子地址标签", + "tag_input_placeholder": "输入标签(例如购物)", + "preview_label": "预览:", + "recent_tags": "最近的标签", + "suggested_tags": "建议", + "use_address": "使用此地址", + "invalid_tag": "标签只能是字母数字和破折号", + "tag_too_long": "标签不得超过 30 个字符", + "help_text": "发送到 user{delimiter}tag@domain.com 的邮件将送达您的收件箱", + "validation": { + "empty": "标签不能为空", + "too_long": "标签不得超过 {max} 个字符", + "invalid_chars": "标签只能包含字母、数字和破折号" + } + }, + "badge": { + "sent_via": "通过", + "sub_address_tag": "使用子地址发送:{tag}", + "identity_name": "使用身份发送:{name}", + "identity_short": "通过 {name}", + "subaddress_tag": "+{tag}" + }, + "delete_button": "删除", + "delete_confirm_title": "删除身份" + }, + "templates": { + "picker_title": "选择一个模板", + "search_placeholder": "搜索模板...", + "section_favorites": "收藏夹", + "section_recent": "最近使用", + "section_uncategorized": "其他", + "no_templates": "还没有模板", + "no_results": "没有找到模板", + "fill_placeholders": "填充占位符值", + "enter_value": "输入一个值...", + "preview": "预览", + "insert_with_values": "插入并填充", + "insert_raw": "插入原始模板", + "copy_suffix": "(复制)", + "placeholder": "变量", + "placeholders": { + "recipient_name": "收件人姓名", + "company": "公司名称", + "date": "当前日期", + "day_of_week": "星期几", + "sender_name": "你的名字" + } + }, + "contacts": { + "directory": { + "title": "通讯录目录", + "search_placeholder": "搜索姓名或邮箱地址", + "refresh": "刷新目录", + "retry": "重试", + "loading": "正在加载目录…", + "unavailable_title": "没有目录", + "unavailable_hint": "你的邮件服务器不提供账户目录。", + "forbidden_title": "目录不可用", + "forbidden_hint": "你的邮件服务器不允许此账户浏览目录。请联系管理员。", + "error_title": "无法加载目录", + "error_hint": "请检查网络连接后重试。", + "empty_title": "目录为空", + "empty_hint": "你的邮件服务器没有列出带邮箱地址的账户。", + "no_match": "目录中没有与搜索匹配的人", + "refresh_failed": "无法刷新目录,显示的是上次加载的列表。", + "select_page": "选择本页所有人", + "deselect_page": "取消选择本页所有人", + "select_entry": "选择 {name}", + "range": "第 {from}–{to} 项,共 {total} 项", + "page": "第 {page} 页,共 {count} 页", + "previous_page": "上一页", + "next_page": "下一页", + "picker_title": "从目录中选择", + "cancel": "取消", + "selected_count": "已选择 {count} 个", + "add_to": "添加到收件人", + "add_cc": "添加到抄送", + "add_bcc": "添加到密送", + "select_hint": "选择一个人以查看详情", + "write_email": "写邮件", + "add_to_contacts": "添加到联系人", + "email": "邮箱", + "read_only_note": "目录条目来自你的邮件服务器,无法在此编辑。", + "type": { + "individual": "个人", + "group": "群组", + "resource": "资源", + "location": "地点", + "other": "其他" + } + }, + "title": "联系人", + "search_placeholder": "搜索联系人...", + "create_new": "新联系人", + "no_category": "没有类别", + "rename_category": "重命名类别", + "category_name_label": "类别名称", + "category_renamed": "类别已重命名", + "category_rename_failed": "重命名类别失败", + "category_added": "联系人已添加至 {name}", + "category_added_plural": "{count} 联系人已添加到 {name}", + "empty_state": "还没有联系人", + "empty_state_title": "还没有联系人", + "empty_state_subtitle": "创建您的第一个联系人或从 vCard 文件导入", + "empty_search": "没有符合您搜索条件的联系人", + "empty_search_hint": "尝试不同的搜索词", + "empty_filtered": "没有符合您筛选条件的联系人", + "empty_filtered_hint": "请调整或清除筛选条件", + "clear_search": "清除搜索", + "import_vcard": "导入 vCard", + "delete_confirm_title": "删除联系人", + "delete_confirm": "您确定要删除此联系人吗?", + "local_mode": "联系人存储在本地(服务器不支持 JMAP 联系人)", + "back_to_contacts": "返回联系人", + "back_to_email": "返回邮件", + "tabs": { + "all": "全部", + "groups": "群组" + }, + "shared": { + "title": "共享" + }, + "address_books": { + "create": "新建通讯录", + "created": "通讯录已创建", + "create_failed": "创建通讯录失败", + "title": "我的地址簿", + "shared_prefix": "共享:{name}", + "moved": "联系人已移至 {name}", + "moved_plural": "{count} 联系人已移至 {name}", + "move_failed": "无法移动联系人", + "address_book": "地址簿", + "rename": "重命名地址簿", + "name_label": "地址簿名称", + "renamed": "地址簿已重命名", + "rename_failed": "重命名地址簿失败", + "default": "默认", + "set_default": "设为默认", + "default_updated": "默认地址簿已更新", + "set_default_failed": "无法设置默认地址簿", + "manage": "管理地址簿", + "share": "共享通讯录", + "new_contact_in_book": "在此通讯录中新建联系人", + "delete": "删除通讯录", + "confirm_delete": "删除「{name}」?此通讯录中的所有联系人将被移除。", + "deleted": "通讯录已删除", + "delete_failed": "删除通讯录失败" + }, + "detail": { + "emails": "邮箱地址", + "phones": "电话号码", + "organizations": "组织机构", + "addresses": "地址", + "notes": "笔记", + "titles": "头衔和角色", + "online_services": "在线服务", + "anniversaries": "周年纪念日", + "personal_info": "个人信息", + "languages": "语言", + "categories": "类别", + "related_contacts": "相关联系人", + "crypto_keys": "加密密钥", + "cert_issuer": "颁发者", + "cert_expires": "过期", + "cert_expired": "已到期", + "cert_fingerprint": "指纹", + "cert_algorithm": "算法", + "import_to_smime": "导入到 S/MIME", + "cert_already_imported": "已导入至 S/MIME", + "cert_imported": "证书导入到 S/MIME 存储", + "cert_import_failed": "导入证书失败", + "no_contact_selected": "选择联系人以查看详细信息", + "compose_email": "撰写邮件", + "copy_email": "复制邮箱地址", + "copy_phone": "复制电话号码", + "copy_url": "复制 URL", + "copied": "已复制到剪贴板", + "copy_failed": "无法复制到剪贴板", + "created": "已创建", + "updated": "最后更新", + "timezone": "时区", + "anniversary_birth": "生日", + "anniversary_death": "已故", + "anniversary_wedding": "周年纪念日", + "anniversary_other": "其他", + "personal_expertise": "专业知识", + "personal_hobby": "爱好", + "personal_interest": "兴趣", + "personal_other": "其他", + "gender": "性别", + "gender_masculine": "男性", + "gender_feminine": "女性", + "gender_other": "其他", + "gender_none": "不适用", + "gender_unknown": "未知", + "calendar": "日历", + "calendar_uri": "日历 URL", + "scheduling_uri": "调度 URL", + "freebusy_uri": "空闲/忙碌 URL", + "section_contact": "Contact details", + "section_work": "Work", + "section_personal": "Personal", + "email_default_label": "Email", + "phone_default_label": "Phone", + "address_default_label": "Address", + "online_service_default_label": "Online", + "organization_label": "Organization", + "title_label": "Title", + "role_label": "Role", + "language_label": "Language", + "related_default_label": "Related", + "more_actions": "更多操作", + "age_years": "{count}岁", + "years_since": "{count}年前" + }, + "activity": { + "recent_emails": "Recent Emails", + "upcoming_events": "Upcoming Events", + "no_emails": "No recent emails", + "no_events": "No upcoming events", + "no_subject": "(No subject)", + "no_title": "(No title)", + "load_failed": "Failed to load", + "unknown_sender": "Unknown sender", + "all_day": "All day" + }, + "form": { + "create_title": "新联系人", + "edit_title": "编辑联系人", + "section_address_book": "地址簿", + "select_address_book": "选择地址簿...", + "section_identity": "姓名和身份", + "contact_type": "联系人类型", + "type_person": "个人", + "type_organization": "组织", + "section_work": "工作与组织", + "prefix": "前缀", + "prefix_placeholder": "博士、先生、女士", + "given_name": "名", + "middle_name": "中间名", + "surname": "姓", + "suffix": "后缀", + "suffix_placeholder": "Jr.、Sr.、III", + "nickname": "昵称", + "nickname_placeholder": "昵称", + "email": "邮件", + "email_placeholder": "email@example.com", + "phone": "电话", + "phone_placeholder": "+1 234 567 890", + "phone_type": "类型", + "phone_voice": "语音", + "phone_cell": "手机", + "phone_fax": "传真", + "phone_pager": "寻呼机", + "phone_video": "视频", + "phone_text": "短信", + "organization": "组织", + "organization_placeholder": "公司名称", + "department": "部门", + "department_placeholder": "部门", + "job_title": "职称", + "job_title_placeholder": "例如,软件工程师", + "role": "角色", + "role_placeholder": "例如,团队负责人", + "addresses": "地址", + "add_address": "添加地址", + "street": "街道", + "city": "城市", + "region": "州/地区", + "postcode": "邮政编码", + "country": "国家", + "online_services": "在线服务", + "add_online_service": "添加在线服务", + "url_placeholder": "https://...", + "service_placeholder": "服务", + "anniversaries": "周年纪念日", + "add_anniversary": "添加日期", + "anniversary_birth": "生日", + "anniversary_wedding": "周年纪念日", + "anniversary_death": "忌日", + "anniversary_other": "其他", + "personal_info": "个人信息", + "add_personal_info": "添加条目", + "personal_info_placeholder": "例如,摄影", + "personal_expertise": "专业知识", + "personal_hobby": "爱好", + "personal_interest": "兴趣", + "personal_other": "其他", + "level": "等级", + "level_high": "高", + "level_medium": "中", + "level_low": "低", + "categories": "类别", + "categories_placeholder": "例如,家人、朋友、同事", + "categories_hint": "输入搜索或添加类别", + "category_add": "添加", + "note": "笔记", + "note_placeholder": "添加注释...", + "gender": "性别", + "gender_sex": "性别", + "gender_male": "男性", + "gender_female": "女性", + "gender_other": "其他", + "gender_none": "不适用", + "gender_unknown": "未知", + "gender_identity": "性别认同", + "gender_identity_placeholder": "性别认同...", + "calendar": "日历", + "calendar_uri": "日历 URL", + "scheduling_uri": "调度 URL", + "freebusy_uri": "空闲/忙碌 URL", + "context_work": "工作", + "context_private": "个人", + "add_email": "添加邮箱地址", + "add_phone": "添加电话", + "save": "保存", + "cancel": "取消", + "creating": "创建中...", + "updating": "更新中...", + "name_required": "请输入名字、姓氏或组织", + "email_invalid": "请输入有效的邮箱地址", + "email_error_inline": "邮箱地址格式无效", + "save_failed": "保存联系人失败", + "delete": "删除", + "upload_photo": "上传照片", + "remove_photo": "移除照片", + "photo_hint": "JPG 或 PNG, 最大 10MB。将会调整大小。", + "photo_too_large": "图像过大 (最大 10MB)", + "photo_invalid": "无效的图像文件", + "change_photo": "更改" + }, + "groups": { + "create": "新建群组", + "edit": "编辑群组", + "empty": "还没有群组", + "delete_confirm_title": "删除群组", + "delete_confirm": "您确定要删除该群组吗?", + "name_label": "群组名称", + "name_placeholder": "例如,团队、家庭", + "name_required": "群组名称为必填项", + "save_failed": "保存群组失败", + "members_label": "成员", + "search_members": "搜索联系人以添加...", + "no_members": "该群组中没有成员", + "member_count": "{count, plural, =0 {无成员} one {1 位成员} other {# 位成员}}", + "send_email": "Send email to group", + "send_email_to": "To", + "send_email_cc": "Cc", + "send_email_bcc": "Bcc", + "no_member_emails": "This group has no members with an email address." + }, + "import": { + "title": "导入联系人", + "drop_hint": "单击以选择 vCard 文件", + "file_types": ".vcf 或 .vcard 文件", + "no_contacts": "文件中未找到联系人", + "parse_error": "无法解析 vCard 文件", + "found": "{count, plural, one {找到 1 位联系人} other {找到 # 位联系人}}", + "duplicate": "重复项", + "select_all": "全选", + "deselect_all": "取消全选", + "selected": "{count, plural, one {已选择 1 项} other {已选择 # 项}}", + "import_button": "导入", + "importing": "导入中...", + "success": "{count, plural, one {已导入 1 位联系人} other {已导入 # 位联系人}}", + "failed": "导入失败", + "close": "关闭", + "file_too_large": "文件太大(最大 5 MB)" + }, + "export": { + "title": "导出联系人", + "success": "{count, plural, one {已导出 1 位联系人} other {已导出 # 位联系人}}" + }, + "bulk": { + "selected": "{count, plural, one {已选择 1 项} other {已选择 # 项}}", + "select_all": "全选", + "delete": "删除", + "delete_confirm_title": "删除联系人", + "delete_confirm": "删除{count, plural, one {1 位联系人} other {# 位联系人}}?", + "deleted": "{count, plural, one {已删除 1 位联系人} other {已删除 # 位联系人}}", + "add_to_group": "添加到群组", + "choose_group": "选择一个组", + "adding_contacts": "正在添加{count, plural, one {1 位联系人} other {# 位联系人}}", + "added_to_group": "联系人已添加至群组", + "export": "导出", + "clear": "清除选择" + }, + "toast": { + "created": "已创建联系人", + "updated": "联系人已更新", + "deleted": "联系人已删除", + "error_create": "创建联系人失败", + "error_update": "无法更新联系人", + "error_delete": "删除联系人失败" + }, + "context_menu": { + "open": "打开", + "edit": "编辑", + "send_email": "发送邮件", + "add_to_group": "加入群组", + "export_vcard": "导出为 vCard", + "delete": "删除", + "call": "拨打电话", + "duplicate": "复制", + "print": "打印" + }, + "filters": { + "toggle": "筛选", + "select": "选择", + "clear": "清除", + "close": "关闭", + "title": "高级筛选", + "organization": "公司", + "organization_placeholder": "例如 Acme", + "job_title": "职位", + "job_title_placeholder": "例如 设计师", + "location": "所在地", + "location_placeholder": "城市或国家", + "email_domain": "邮箱域名", + "email_domain_placeholder": "example.com", + "birthday_month": "生日月份", + "any_month": "任意月份", + "has_email": "有邮箱", + "has_phone": "有电话", + "has_photo": "有照片", + "sort_by": "排序方式", + "sort_first_name": "名", + "sort_last_name": "姓" + }, + "open_categories": "打开分类" + }, + "calendar": { + "title": "日历", + "back_to_email": "返回邮件", + "back_to_month": "返回月份", + "my_calendars": "日历", + "birthday_calendar": "生日", + "mini_calendar_change": "点击更改月份", + "views": { + "month": "月", + "week": "周", + "day": "天", + "agenda": "议程", + "today": "今天", + "month_hint": "月 (m)", + "week_hint": "周 (w)", + "day_hint": "天 (d)", + "agenda_hint": "议程 (a)", + "tasks": "任务", + "tasks_hint": "任务 (k)" + }, + "events": { + "hidden_before": "{count, plural, other {更早的日程 # 个}}", + "hidden_after": "{count, plural, other {更晚的日程 # 个}}", + "show_all_hours": "显示全部时段", + "show_display_hours": "仅显示 {start} – {end}", + "create": "创建活动", + "edit": "编辑活动", + "delete": "删除活动", + "details": "活动详情", + "no_events": "没有活动", + "all_day": "全天", + "more": "+{count} 更多", + "show_more": "展开", + "show_less": "收起", + "no_title": "(无标题)", + "resize": "调整事件大小", + "duplicate": "复制", + "today_header": "今天", + "tomorrow_header": "明天", + "agenda_show_earlier": "显示更早的日程", + "agenda_loading": "加载中…", + "agenda_range_start": "显示 {date} 起的日程", + "agenda_range_end": "显示至 {date} 的日程", + "export_ics": "导出为 .ics", + "copy_title": "复制标题", + "copy_link": "复制会议链接", + "go_to_today": "前往今天", + "new_all_day_event": "新建全天事件", + "new_event": "新建事件", + "new_task": "新建任务" + }, + "detail": { + "add_note": "添加注释...", + "save_note": "保存", + "note_saved": "注释已添加", + "open_link": "打开链接", + "copy_location": "复制地点", + "meeting_link": "会议链接", + "tentative": "暂定", + "cancelled": "已取消", + "delete_confirm": "删除这个活动吗?" + }, + "form": { + "title": "标题", + "description": "描述", + "location": "地点", + "meeting_link": "会议链接", + "start_date": "开始日期", + "end_date": "结束日期", + "start_time": "开始时间", + "end_time": "结束时间", + "all_day_event": "全天活动", + "calendar_select": "日历", + "save": "保存", + "cancel": "取消", + "delete_confirm": "您确定要删除此活动吗?", + "color": "颜色" + }, + "participants": { + "availability": { + "title": "空闲状态", + "free": "空闲", + "busy": "忙碌", + "tentative": "暂定", + "unknown": "不是此服务器上的用户", + "checking": "正在检查…" + }, + "title": "参加者", + "add": "添加参与者", + "organizer": "组织者", + "attendee": "出席者", + "accepted": "已接受", + "declined": "已拒绝", + "tentative": "暂定", + "needs_action": "待处理", + "remove": "移除", + "edit": "编辑", + "email_placeholder": "添加邮箱地址或搜索联系人", + "send_invitations": "向参与者发送邀请", + "status_summary": "{accepted} 已接受,{pending} 待处理", + "invited_by": "来自 {name} 的邀请", + "respond_below": "使用下面的按钮回复", + "rsvp_label": "您的回应", + "cancel_notification": "参与者将收到取消通知", + "you_organizer": "您是组织者", + "you_attendee": "您是与会者", + "no_participants": "没有参加者", + "count": "{count, plural, one {# 位参与者} other {# 位参与者}}" + }, + "recurrence": { + "title": "重复", + "none": "不重复", + "daily": "每天", + "weekly": "每周", + "monthly": "每月", + "yearly": "每年", + "every_n_days": "每 {count} 天", + "every_n_weeks": "每 {count} 周", + "every_n_months": "每 {count} 个月", + "until": "直到", + "occurrences": "{count} 次出现", + "custom": "自定义…", + "edit_custom": "编辑自定义重复规则", + "every_n_years": "每 {count} 年", + "on_days": "{days}", + "on_day_n": "{day}日", + "on_the_nth": "{nth}{day}", + "in_month": "{month}", + "nth_1": "第一个", + "nth_2": "第二个", + "nth_3": "第三个", + "nth_4": "第四个", + "nth_last": "最后一个", + "editor_freq_day": "天", + "editor_freq_week": "周", + "editor_freq_month": "月", + "editor_freq_year": "年", + "editor_repeats_on": "重复于", + "editor_every": "每", + "editor_unit_days": "天", + "editor_unit_weeks": "周", + "editor_unit_months": "个月", + "editor_unit_years": "年", + "editor_on_day": "日期:", + "editor_on_the": "在", + "editor_in": "月份:", + "editor_ends": "结束", + "editor_never": "永不", + "editor_ends_on": "于", + "editor_ends_after": "次数:", + "editor_occurrences": "次" + }, + "recurrence_scope": { + "edit_title": "编辑重复事件", + "delete_title": "删除重复事件", + "description": "这是一个重复事件。您想修改哪些事件?", + "this_event": "仅限本次活动", + "this_and_future": "本次及后续活动", + "all_events": "所有活动", + "cancel": "取消", + "save": "保存", + "delete": "删除", + "rsvp_title": "回复重复事件", + "rsvp_description": "这是一个重复事件。您的回复适用于哪些事件?", + "respond": "回复" + }, + "alerts": { + "title": "提醒", + "none": "无提醒", + "at_time": "活动举行时", + "minutes_before": "{count, plural, one {提前 # 分钟} other {提前 # 分钟}}", + "hours_before": "{count, plural, one {提前 # 小时} other {提前 # 小时}}", + "days_before": "{count, plural, one {提前 # 天} other {提前 # 天}}", + "weeks_before": "{count, plural, one {提前 # 周} other {提前 # 周}}", + "unit_minutes_before": "分钟前", + "unit_hours_before": "小时前", + "unit_days_before": "天前", + "unit_weeks_before": "周前", + "add": "添加提醒", + "remove": "移除提醒", + "amount": "提醒数量", + "unit": "提醒单位", + "action": "提醒类型", + "action_display": "通知", + "action_email": "电子邮件", + "by_email": "{label}(电子邮件)", + "kept": "另有 {count} 个无法在此编辑的提醒将被保留", + "other": "另有 {count} 个提醒", + "calendar_defaults": "日历默认设置", + "customize": "自定义", + "use_defaults": "使用日历默认设置" + }, + "settings": { + "limit_hours": "限制显示时段", + "limit_hours_desc": "在日视图和周视图中只显示这些时段。范围外的日程仍可通过每天顶部和底部的箭头查看。", + "visible_hours": "显示时段", + "visible_hours_start": "显示开始时间", + "visible_hours_end": "显示结束时间", + "hide_non_working_days": "隐藏非工作日", + "hide_non_working_days_desc": "在周视图中省略你不工作的日子。这些日子的日程仍会显示在月、日和日程列表视图中。", + "working_days": "工作日", + "title": "日历设置", + "default_view": "默认视图", + "week_starts_on": "一周开始于", + "time_format": "时间格式", + "default_calendar": "默认日历", + "default_reminder": "默认提醒", + "time_format_12h": "12小时", + "time_format_24h": "24小时", + "notifications_enabled": "事件通知", + "notifications_enabled_desc": "显示即将发生的日历事件的提醒", + "notification_sound": "通知声音", + "notification_sound_desc": "播放日历提醒声音", + "invitation_parsing": "解析邮件邀请", + "invitation_parsing_desc": "检测邮件附件中的日历邀请并显示日历操作", + "show_time_in_month_view": "在月视图中显示时间", + "show_time_in_month_view_desc": "在月历视图中显示事件时间", + "show_week_numbers": "显示周数", + "show_week_numbers_desc": "在迷你日历中显示周数", + "calendar_free_scroll": "自由滚动", + "calendar_free_scroll_desc": "连续滚动浏览月、周和日,而不是一次只显示一个时段", + "enable_tasks": "启用任务", + "enable_tasks_desc": "在日历中显示任务视图以管理待办事项", + "show_tasks_on_calendar": "在日历上显示任务", + "show_tasks_on_calendar_desc": "在日历的月/周/日视图中显示任务", + "hover_preview": "事件悬停预览", + "hover_preview_desc": "将鼠标悬停在日历事件上时显示详细信息弹出窗口", + "hover_preview_instant": "立即", + "hover_preview_delay_500ms": "0.5秒延迟", + "hover_preview_delay_1s": "1秒延迟", + "hover_preview_delay_2s": "2秒延迟", + "hover_preview_off": "关闭", + "organizer_identity": "以此身份组织邀请", + "organizer_identity_desc": "邀请他人参加活动时用作组织者的日历地址。", + "organizer_identity_failed": "无法更改组织者地址", + "show_birthday_calendar": "联系人生日日历", + "show_birthday_calendar_desc": "显示包含联系人生日的虚拟日历" + }, + "days": { + "monday": "周一", + "tuesday": "周二", + "wednesday": "周三", + "thursday": "周四", + "friday": "周五", + "saturday": "周六", + "sunday": "周日", + "mon": "周一", + "tue": "周二", + "wed": "周三", + "thu": "周四", + "fri": "周五", + "sat": "周六", + "sun": "周日" + }, + "notifications": { + "event_created": "活动已创建", + "event_updated": "活动已更新", + "event_deleted": "活动已删除", + "calendar_created": "已创建日历", + "calendar_deleted": "日历已删除", + "event_move_error": "移动事件失败", + "event_resize_error": "无法调整事件大小", + "alert_title": "即将举行的活动", + "alert_now": "现在开始", + "alert_in_minutes": "{count} 分钟后", + "invitation_sent": "已发送邀请", + "invitations_denied_title": "无法发送邀请", + "invitations_denied": "服务器拒绝发送邀请:{reason}。是否不发送邀请直接保存活动?", + "save_without_invitations": "不发送邀请并保存", + "rsvp_updated": "回复已更新", + "rsvp_error": "无法更新回复", + "event_duplicated": "活动已复制", + "event_error": "无法保存活动", + "task_due": "任务到期", + "event_exported": "日程已导出", + "title_copied": "标题已复制", + "link_copied": "链接已复制" + }, + "status": { + "loading_calendars": "正在加载日历...", + "loading_events": "正在加载事件..." + }, + "quick_create": { + "placeholder": "新活动标题", + "aria_label": "快速创建事件", + "add_title": "添加标题", + "more_options": "更多选项" + }, + "nav_prev": "上一项", + "nav_next": "下一项", + "import": { + "title": "导入日历", + "tab_file": "文件", + "tab_url": "URL", + "select_file": "选择 .ics 文件", + "drop_file": "或将文件拖放到此处", + "supported_formats": "支持 iCalendar (.ics) 文件", + "url_description": "输入外部 iCalendar (.ics) 源的 URL 以导入事件。", + "url_placeholder": "https://example.com/calendar.ics", + "url_hint": "支持 iCalendar (.ics) URL", + "fetch": "获取", + "invalid_url": "请输入有效的 URL", + "url_fetch_failed": "无法从 URL 获取日历", + "parsing": "正在解析日历文件...", + "parsed_events": "找到 {count} 个活动", + "no_events": "文件中未找到活动", + "select_all": "全选", + "deselect_all": "取消全选", + "target_calendar": "导入到日历", + "import_button": "导入所选", + "importing": "正在导入活动...", + "success": "{count} 个活动已成功导入", + "error": "导入日历失败", + "file_too_large": "文件超过 10MB 限制", + "invalid_format": "日历文件格式无效" + }, + "webcal_action": { + "title": "打开日历链接", + "description": "您想如何使用 \"{name}\"?", + "import_title": "导入一次", + "import_description": "立即获取事件并复制到您的某个日历中。", + "subscribe_title": "订阅", + "subscribe_description": "将此日历作为单独的日历自动同步。", + "cancel": "取消" + }, + "management": { + "title": "日历管理", + "description": "创建、重命名和自定义您的日历。右键单击侧栏中的日历可快速更改其颜色。", + "name": "名称", + "name_placeholder": "日历名称", + "color": "颜色", + "change_color": "更改颜色", + "random_color": "随机新颜色", + "add_calendar": "添加日历", + "edit": "编辑", + "delete": "删除", + "save": "保存", + "create": "创建", + "cancel": "取消", + "default": "默认", + "confirm_delete": "删除\"{name}\"?此日历中的所有活动都将被删除。", + "confirm_clear": "清除\"{name}\"中的所有活动吗?此操作无法撤销。", + "set_default": "设为默认", + "default_updated": "默认日历已更新", + "error_default": "无法设置默认日历", + "clear_events": "清除活动", + "events_cleared": "{count} 个活动已清除", + "error_clear": "无法清除日历活动", + "calendar_created": "已创建日历", + "calendar_updated": "日历已更新", + "calendar_deleted": "日历已删除", + "color_updated": "日历颜色已更新", + "error_create": "创建日历失败", + "error_update": "更新日历失败", + "error_delete": "删除日历失败", + "caldav_url": "CalDAV URL", + "copy_url": "复制 CalDAV URL", + "url_copied": "CalDAV URL 已复制到剪贴板", + "share": "共享日历", + "new_event_in_calendar": "在此日历中新建事件", + "shared_with": "已与 {names} 共享", + "shared_with_count": "{count, plural, other {已与 # 人共享}}", + "move_up": "上移", + "move_down": "下移", + "kind": "内容", + "kind_events": "日程", + "kind_tasks": "任务", + "kind_both": "日程和任务", + "kind_hint": "决定通过 CalDAV 同步时哪些应用可以看到此日历。之后无法更改。", + "default_alerts": "默认提醒", + "default_alerts_title": "“{name}”的默认提醒", + "default_alerts_hint": "此日历中的新活动将使用这些提醒,除非在活动中进行更改。在此处更改不会影响现有活动。电子邮件提醒由邮件服务器发送。", + "default_alerts_timed": "有具体时间的活动", + "default_alerts_all_day": "全天活动", + "default_alerts_updated": "默认提醒已更新" + }, + "subscription": { + "title": "iCal 订阅", + "section_title": "iCal 订阅", + "description": "订阅外部 iCalendar 源。活动将自动同步到单独的日历中。支持 https:// 和 webcal:// URL。", + "url_label": "日历 URL", + "url_placeholder": "https://example.com/calendar.ics 或 webcal://...", + "name_label": "日历名称", + "name_placeholder": "例如公共假期", + "color_label": "颜色", + "refresh_interval": "刷新间隔", + "interval_15": "每 15 分钟", + "interval_30": "每 30 分钟", + "interval_60": "每小时", + "interval_360": "每 6 小时", + "interval_1440": "每天", + "subscribe": "订阅", + "subscribing": "正在订阅...", + "save": "保存更改", + "saving": "保存中...", + "edit": "编辑", + "edit_title": "编辑订阅", + "updated": "更新了\"{name}\"", + "update_error": "更新订阅失败", + "invalid_url": "请输入有效的 URL", + "success": "已订阅\"{name}\"", + "error": "添加订阅失败", + "refresh": "立即刷新", + "refresh_success": "订阅已刷新", + "refresh_error": "刷新订阅失败", + "unsubscribe": "退订", + "confirm_delete": "取消订阅\"{name}\"?日历及其所有活动都将被删除。", + "deleted": "订阅已删除", + "delete_error": "删除订阅失败", + "last_refreshed": "最后更新:{time}" + }, + "tasks": { + "label": "任务", + "no_tasks": "没有任务", + "no_title": "(无标题)", + "mark_complete": "标记为完成", + "mark_incomplete": "标记为未完成", + "filter_all": "全部", + "filter_pending": "待处理", + "filter_completed": "已完成", + "filter_overdue": "逾期", + "show_completed": "显示已完成", + "create": "新任务", + "edit": "编辑任务", + "title_placeholder": "任务标题", + "description_placeholder": "添加描述...", + "due_date": "到期日", + "include_time": "包括时间", + "priority": "优先级", + "priority_none": "无", + "priority_high": "高", + "priority_medium": "中", + "priority_low": "低", + "progress": "状态", + "progress_needs_action": "待处理", + "progress_in_process": "进行中", + "progress_completed": "已完成", + "progress_cancelled": "已取消", + "calendar": "日历", + "alert": "提醒", + "alert_none": "无", + "alert_at_time": "在到期日时", + "alert_5min": "5分钟前", + "alert_15min": "15分钟前", + "alert_30min": "30分钟前", + "alert_1hr": "1小时前", + "alert_1day": "1天前", + "delete": "删除", + "cancel": "取消", + "save": "保存", + "quick_add_placeholder": "添加任务...", + "due_today": "今天", + "due_tomorrow": "明天", + "overdue": "逾期" + }, + "months": { + "jan": "1月", + "feb": "2月", + "mar": "3月", + "apr": "4月", + "may": "5月", + "jun": "6月", + "jul": "7月", + "aug": "8月", + "sep": "9月", + "oct": "10月", + "nov": "11月", + "dec": "12月", + "far": "Farvardin", + "ord": "Ordibehesht", + "kho": "Khordad", + "tir": "Tir", + "mor": "Mordad", + "sha": "Shahrivar", + "meh": "Mehr", + "aba": "Aban", + "aza": "Azar", + "dey": "Dey", + "bah": "Bahman", + "esf": "Esfand" + }, + "nav_open_menu": "打开菜单", + "toolbar": { + "more": "更多操作", + "select_view": "切换视图,当前为{view}" + }, + "other_calendars": "其他日历", + "calendar_options": "{name} 的选项" + }, + "advanced_search": { + "title": "高级搜索", + "from": "发件人", + "from_placeholder": "发件人邮箱地址或姓名", + "to": "收件人", + "to_placeholder": "收件人邮箱地址或姓名", + "subject": "主题", + "subject_placeholder": "主题包含...", + "body": "正文", + "body_placeholder": "正文包含...", + "folder": "文件夹", + "all_folders": "所有文件夹", + "all_folders_except_spam_trash": "除垃圾邮件和已删除外的所有文件夹", + "has_attachment": "附件", + "date_after": "后", + "date_before": "前", + "size_min": "大于 (KB)", + "size_max": "小于 (KB)", + "starred": "加星标", + "unread": "未读", + "read": "已读", + "yes": "是", + "no": "否", + "clear": "清除", + "clear_all": "全部清除", + "filters_active": "{count} 个筛选条件", + "filters_active_plural": "{count} 个筛选条件", + "toggle_filters": "更多", + "search_hint": "使用高级过滤器进行精确搜索", + "advanced_filters_tooltip": "高级搜索过滤器", + "results_found": "{count, plural, =0 {未找到结果} one {找到 # 条结果} other {找到 # 条结果}}", + "results_found_more": "找到 {count}+ 个结果", + "suggestions_recent": "最近搜索", + "suggestions_people": "联系人", + "suggestions_from": "搜索来自 {address} 的邮件", + "suggestions_to": "搜索发给 {address} 的邮件", + "suggestions_remove_recent": "从最近搜索中移除", + "has_words": "包含字词", + "size": "大小", + "date_within": "日期", + "search_in": "搜索范围", + "has_attachment_check": "包含附件", + "search": "搜索" + }, + "welcome": { + "title": "欢迎使用邮箱", + "tip_compose": "按 c 撰写新邮件", + "tip_shortcuts": "按?查看所有键盘快捷键", + "tip_sidebar": "在侧边栏菜单中查找日历、联系人和设置", + "tip_settings": "在\"设置\"中自定义您的体验", + "tip_compose_touch": "点按铅笔按钮写新邮件", + "tip_sidebar_touch": "日历、联系人和设置位于底部栏", + "got_it": "知道了", + "settings": "设置", + "dismiss": "关闭", + "start_tour": "开始导览" + }, + "demo_welcome": { + "title": "欢迎使用 Bulwark Mail", + "description": "探索功能齐全的网页邮件客户端--就在您的浏览器中。所有数据都保留在您的设备上,请随意体验所有功能。", + "feature_email": "阅读和撰写邮件", + "feature_organize": "标签、星标和文件夹", + "feature_shortcuts": "键盘快捷键", + "feature_privacy": "100% 隐私演示", + "hint": "点击左侧的任意邮件即可开始,或参加下方的导览。" + }, + "files": { + "office_edit": "编辑文档", + "office_loading": "正在打开文档编辑器…", + "office_error": "无法打开文档编辑器。", + "office_close": "关闭编辑器", + "open_in_new_tab": "在新标签页中打开", + "title": "文件", + "search_placeholder": "搜索文件...", + "empty_state_title": "还没有文件", + "upload_files": "上传文件", + "new_folder": "新建文件夹", + "new_folder_name": "文件夹名称", + "rename": "重命名", + "rename_title": "重命名", + "new_name": "新名称", + "name_forbidden_chars": "服务器不允许名称中包含这些字符:{chars}", + "name_reserved": "此名称已被服务器保留。", + "delete": "删除", + "delete_confirm_title": "删除资源", + "delete_confirm_message": "您确定要删除\"{name}\"吗?此操作无法撤消。", + "download": "下载", + "name": "名称", + "size": "大小", + "modified": "修改时间", + "type": "类型", + "folder": "文件夹", + "file": "文件", + "uploading": "正在上传…", + "upload_success": "{count, plural, one {已上传 1 个文件} other {已上传 # 个文件}}", + "upload_canceled": "已取消上传", + "upload_canceled_partial": "取消前已上传 {count, plural, other {# 个文件}}", + "upload_error": "上传文件失败", + "create_folder_success": "文件夹已创建", + "create_folder_error": "创建文件夹失败", + "delete_success": "删除成功", + "delete_error": "删除失败", + "rename_success": "重命名成功", + "rename_error": "重命名失败", + "download_error": "下载失败", + "not_available": "文件存储在此服务器上不可用", + "cancel": "取消", + "create": "创建", + "save": "保存", + "no_results": "没有与您的搜索匹配的文件", + "batch_delete_confirm_message": "确定要删除{count, plural, one {1 个项目} other {# 个项目}}吗?此操作无法撤销。", + "batch_delete_success": "{count, plural, one {已删除 1 个项目} other {已删除 # 个项目}}", + "grid_view": "网格视图", + "list_view": "列表视图", + "details": "详情", + "path": "路径", + "preview": "预览", + "preview_error": "加载预览失败", + "cut": "剪切", + "copy": "复制", + "paste": "粘贴", + "move_success": "{count, plural, one {已移动 1 个项目} other {已移动 # 个项目}}", + "move_error": "移动失败", + "paste_success": "粘贴成功", + "paste_error": "粘贴失败", + "new_text_file": "新建文本文件", + "file_name": "文件名", + "retry": "重试", + "refresh": "刷新", + "duplicate": "创建副本", + "duplicate_success": "复制成功", + "duplicate_error": "复制失败", + "share": "Share", + "shared": "Shared", + "shared_with_me": "Shared with me", + "shared_by": "Shared by {name}", + "create_file_success": "文件已创建", + "create_file_error": "创建文件失败", + "favorites": "收藏夹", + "recent": "最近", + "show_in_folder": "在文件夹中显示", + "remove_from_recent": "从最近中移除", + "upload_folder": "上传文件夹", + "file_too_large": "\"{name}\"超过最大文件大小 ({max})", + "undo": "撤销", + "undo_success": "操作已撤消", + "undo_error": "撤销失败", + "toolbar": "文件操作", + "file_list": "文件和文件夹", + "context_menu": "操作", + "settings_ascending": "升序", + "settings_descending": "降序", + "disabled_title": "文件功能已被您的管理员禁用", + "disabled_description": "通过 WebDAV 上传大文件可能导致 Stalwart/RocksDB 不稳定,包括内存溢出、崩溃以及难以回收的磁盘占用。已删除文件也可能不会立即从 blob 存储中清除。不建议在生产环境中启用此功能。", + "no_accounts": "没有已连接的账户。", + "open_folder_tree": "打开文件夹树", + "migration_title": "正在更新您的文件…", + "migration_description": "正在将文件夹和文件整理为正确的结构。此操作仅执行一次。", + "new": "新建", + "my_files": "我的文件", + "folders": "文件夹", + "files_heading": "文件", + "open": "打开", + "star": "收藏", + "unstar": "取消收藏", + "expand_folder": "展开 {name}", + "collapse_folder": "折叠 {name}", + "sidebar_hint": "收藏文件夹或打开文件后,它们会显示在这里。", + "breadcrumbs": "文件夹路径", + "show_path": "显示路径中隐藏的文件夹", + "selection_actions": "所选项目的操作", + "selected_count": "{count, plural, other {已选择 # 项}}", + "clear_selection": "取消选择", + "select_all": "全选", + "select_item": "选择 {name}", + "more_actions": "{name} 的更多操作", + "paste_here": "粘贴到此文件夹", + "paste_count": "{count, plural, other {粘贴 # 个项目}}", + "sort_by": "排序方式", + "view_mode": "布局", + "clear_search": "清除搜索", + "no_results_for": "此文件夹中没有与“{query}”匹配的内容", + "download_count": "{count, plural, other {下载 # 个文件}}", + "delete_count": "{count, plural, other {删除 # 个项目}}", + "close_details": "关闭详情", + "file_kind": "{ext} 文件", + "folder_summary": "{folders, plural, =0 {没有文件夹} other {# 个文件夹}},{files, plural, =0 {没有文件} other {# 个文件}}", + "details_hint": "选择文件或文件夹以查看详情。", + "sharing": "共享", + "shared_with_count": "{count, plural, other {已与 # 人共享}}", + "only_you": "仅您自己", + "empty_folder_title": "此文件夹为空", + "empty_folder_description": "将文件拖放到此处,或从设备上传。", + "empty_folder_read_only": "您无法向此文件夹添加文件。", + "choose_account": "选择账户", + "drop_to_upload": "松开以上传到 {folder}", + "uploading_count": "正在上传 {current}/{total}", + "dismiss": "关闭", + "zoom_in": "放大", + "zoom_out": "缩小", + "zoom_reset": "适应屏幕", + "rotate": "旋转", + "close_preview": "关闭" + }, + "smime": { + "your_certificates": "您的证书", + "your_certificates_desc": "导入和管理您的 S/MIME 证书以对邮件进行签名和加密", + "recipient_certificates": "收件人证书", + "recipient_certificates_desc": "用于加密发送给收件人的邮件的公共证书", + "identity_bindings": "身份键绑定", + "identity_bindings_desc": "将 S/MIME 证书绑定到您的邮件身份", + "defaults_title": "默认值", + "defaults_desc": "配置默认签名和加密行为", + "import_pkcs12": "导入 PKCS#12 (.p12/.pfx)", + "import_public_cert": "导入证书", + "no_certificates": "尚未导入证书", + "no_recipient_certs": "无收件人证书", + "expires": "过期", + "expired": "已到期", + "bound_to": "绑定到", + "no_key_bound": "无", + "lock": "锁定密钥", + "unlock": "解锁密钥", + "details": "查看详情", + "delete": "删除", + "encrypt_by_default": "默认加密", + "encrypt_by_default_desc": "当所有收件人都有证书时自动加密邮件", + "remember_unlocked": "记住已解锁的密钥", + "remember_unlocked_desc": "在当前浏览器会话期间保持密钥处于解锁状态", + "sign_default_for": "默认签名", + "enter_p12_passphrase": "输入 PKCS#12 密码", + "p12_passphrase_desc": "输入保护此证书文件的密码", + "enter_storage_passphrase": "设置存储密码", + "storage_passphrase_desc": "选择一个密码来保护浏览器中静态的密钥", + "next": "下一步", + "import": "导入", + "unlock_key": "解锁密钥", + "unlock_key_desc": "输入存储密码以解锁此密钥以进行签名或解密", + "passphrase_placeholder": "输入密码", + "confirm_passphrase_placeholder": "确认密码", + "passphrase_mismatch": "密码不匹配", + "cancel": "取消", + "processing": "处理中…", + "close": "关闭", + "certificate_details": "证书详情", + "cert_subject": "主体", + "cert_issuer": "颁发者", + "cert_email": "邮件", + "cert_serial": "序列号", + "cert_validity": "有效期", + "cert_fingerprint": "指纹 (SHA-256)", + "cert_algorithm": "算法", + "cert_capabilities": "支持能力", + "cert_source": "来源", + "cert_expired": "该证书已过期", + "cert_not_yet_valid": "该证书尚未生效", + "cap_sign": "签名", + "cap_encrypt": "加密", + "cap_none": "无", + "show_passphrase": "显示密码", + "hide_passphrase": "隐藏密码", + "sign_toggle": "签名", + "encrypt_toggle": "加密", + "missing_recipient_certs": "缺少以下证书:{emails}", + "missing_sender_cert": "没有证书与该身份绑定", + "status_encrypted_ok": "此消息已加密", + "status_encrypted_no_key": "此消息已加密,但未找到匹配的密钥", + "status_encrypted_failed": "无法解密此消息", + "status_signed_valid": "签名已验证", + "status_signed_invalid": "签名验证失败", + "status_signed_expired_cert": "使用过期的证书签名", + "status_signed_self_signed": "签名有效,但证书是自签名的(不可信)", + "status_signed_mismatch": "签名有效,但签名者与发件人不匹配", + "status_unsupported": "不支持的 S/MIME 格式", + "auto_import_signer_certs": "自动导入邮件签名者的证书", + "auto_import_signer_certs_desc": "自动保存已验证签名邮件中的证书,以便后续用于邮件加密。", + "export": "导出", + "enter_export_passphrase": "设置导出密码", + "export_passphrase_desc": "选择密码来保护导出的 PKCS#12 文件", + "export_storage_desc": "输入存储密码以解密导出密钥", + "incorrect_passphrase": "密码不正确" + }, + "tour": { + "step_counter": "第 {current} 步,共 {total} 步", + "skip": "跳过导览", + "back": "上一步", + "next": "下一步", + "finish": "完成", + "take_a_tour": "查看界面导览", + "restart_title": "新手导览", + "restart_desc": "重新查看界面功能引导", + "restart_button": "重新开始导览", + "show_on_new_devices_title": "在新设备上显示", + "show_on_new_devices_desc": "首次在新设备登录时重新显示欢迎横幅和导览,即使您已在其他设备完成", + "sidebar_title": "邮箱文件夹", + "sidebar_desc": "这里是邮箱文件夹列表。点击任意文件夹即可查看邮件。你可以创建文件夹、拖动邮件进行整理,并快速查看未读邮件数量。", + "compose_title": "写邮件", + "compose_desc": "点击这里即可新建邮件。你可以添加收件人、附件,并使用富文本格式编辑内容。", + "search_title": "搜索邮件", + "search_desc": "可以按发件人、主题或内容搜索邮件。点击筛选图标还能使用高级搜索,例如日期范围、附件或星标邮件。", + "email_list_title": "邮件列表", + "email_list_desc": "邮件会显示在这里。点击任意邮件即可在右侧阅读。你也可以使用复选框选择多封邮件进行批量移动、删除或添加标签。", + "email_viewer_title": "阅读区域", + "email_viewer_desc": "选中的邮件会在这里打开。你可以使用工具栏进行回复、转发、归档或删除,也可以为邮件添加星标或颜色标签。", + "keywords_title": "颜色标签", + "keywords_desc": "使用颜色标签来整理邮件。可以将邮件拖到标签上进行标记,或右键邮件选择标签。", + "calendar_title": "日历", + "calendar_desc": "切换到日历即可管理你的日程。你可以创建事件、设置提醒,并查看日、周或月视图。", + "contacts_title": "联系人", + "contacts_desc": "这里是你的通讯录。你可以导入联系人、创建联系人分组,点击任意联系人即可查看详细信息。", + "settings_title": "设置", + "settings_desc": "在这里可以自定义各种设置,例如主题、界面紧凑度、签名、邮件过滤规则、快捷键以及日历默认选项等。", + "shortcuts_title": "键盘快捷键", + "shortcuts_desc": "按下 ? 可随时查看所有快捷键。你可以使用键盘完成导航、写邮件和管理邮件等操作。", + "compose_open_title": "邮件编辑器", + "compose_open_desc": "这里是邮件编辑区域。添加收件人、输入正文、上传附件,并使用富文本格式编辑。你还可以保存草稿或使用邮件模板。", + "calendar_view_title": "你的日历", + "calendar_view_desc": "这里是你的日历视图(示例事件)。可以通过工具栏切换日、周、月或日程视图。", + "create_event_title": "创建日程", + "create_event_desc": "点击此按钮创建新的日程。你可以填写标题、日期、时间,并邀请参与者。", + "event_modal_title": "日程详情", + "event_modal_desc": "在这里填写日程信息,例如标题、时间、地点或参与者。完成后点击保存即可。", + "contacts_list_title": "联系人列表", + "contacts_list_desc": "这里显示你的联系人。点击任意联系人即可在右侧查看详情。你也可以新建联系人、导入 vCard 或创建联系人分组。", + "settings_tabs_title": "设置菜单", + "settings_tabs_desc": "这里列出了所有设置分类。你可以自定义界面外观、管理身份、设置邮件规则、配置日历等。", + "files_title": "文件存储", + "files_desc": "这里是你的文件管理器,可以上传、整理和共享文件,就像内置在邮箱中的个人云盘。", + "demo_banner_title": "演示控制", + "demo_banner_desc": "当前为演示模式,所有数据仅保存在浏览器中。随时点击\"重置演示\"即可恢复初始示例数据。", + "quota_title": "存储使用情况", + "quota_desc": "在这里查看邮箱的存储使用情况。随着空间使用增加,进度圆环会逐渐填满。" + }, + "unified_mailbox": { + "search_unavailable": "统一视图中无法使用搜索" + }, + "calendar_event_notifications": { + "someone": "有人", + "invited": "{name} 邀请您参加“{title}”", + "updated": "{name} 更新了“{title}”", + "cancelled": "{name} 取消了“{title}”", + "untitled": "无标题活动" + }, + "share_notifications": { + "someone": "有人", + "shared": "{name} 与您共享了{kind}“{object}”", + "changed": "{name} 更改了您对{kind}“{object}”的访问权限", + "revoked": "{name} 移除了您对{kind}“{object}”的访问权限", + "object": { + "folder": "文件夹", + "calendar": "日历", + "address_book": "通讯录", + "files": "文件目录" + } + }, + "sharing": { + "title": "共享「{name}」", + "description": "向此服务器上的其他用户或群组授予访问权限。更改会立即生效。", + "no_shares": "尚未共享。", + "add_person": "添加用户或群组", + "search_placeholder": "按姓名或邮箱搜索…", + "loading_principals": "正在加载用户…", + "no_principals": "未找到其他用户或群组。", + "no_match": "无匹配项。", + "remove": "取消访问", + "group": "群组", + "share_added": "已授予访问权限", + "share_updated": "已更新访问权限", + "share_removed": "已取消访问权限", + "share_failed": "更新共享失败", + "preset": { + "freeBusy": "仅显示忙/闲", + "read": "只读", + "readWrite": "读写", + "manager": "管理员", + "custom": "自定义" + } + }, + "quote_header": { + "reply_line": "在 {date},{from} 写道:", + "forwarded_separator": "---------- 转发邮件 ----------", + "from_label": "发件人", + "date_label": "日期", + "subject_label": "主题" + }, + "pwa_install": { + "title": "安装 {appName}", + "description": "安装我们的应用,以获得快速访问和离线支持。", + "not_now": "暂不", + "install": "安装", + "dont_remind": "不再提醒", + "dismiss_aria": "关闭安装提示" + }, + "deep_link": { + "message_not_found": "此邮件已不可用。", + "thread_not_found": "此会话已不可用。", + "folder_not_found": "此文件夹已不可用。", + "event_not_found": "此日程已不可用。", + "file_not_found": "此文件已不可用。", + "account_unavailable": "请登录该账户以打开此链接。", + "copy_message": "复制邮件链接", + "copy_conversation": "复制会话链接", + "copy_event": "复制日程链接", + "copied": "链接已复制", + "copy_failed": "无法复制链接" + }, + "plugins": { + "oauth_callback": { + "completing": "正在完成授权…", + "completing_message": "正在建立连接,请稍候。", + "received_title": "已收到授权", + "received_message": "您可以返回发起连接的标签页。", + "failed_title": "授权失败", + "denied": "授权被拒绝。", + "failed_detail": "授权失败:{error}", + "missing_params": "缺少授权码或状态。", + "delivery_failed": "无法将授权结果传递到应用标签页。请关闭此窗口并重试。" + }, + "settings": { + "title": "插件", + "description": "由管理员添加的插件。可以为您的账户开启或关闭。", + "description_uploads": "插件可为网页邮箱添加功能。可以为您的账户开启或关闭,也可以自行安装。", + "description_demo": "演示模式中无法使用插件。", + "install_button": "安装插件", + "install_hint": "请选择插件 ZIP 文件。", + "install_hint_approval": "请选择插件 ZIP 文件。需要管理员批准后才能开启。", + "install_uploads_off": "管理员不允许安装插件。", + "install_managed_id": "管理员已提供同名插件,因此无法替换。", + "install_invalid": "此文件不是可安装的插件:{details}", + "toast_installed": "插件已安装。请在下方开启。", + "toast_installed_pending": "插件已安装,并已提交给管理员审批。", + "toast_enabled": "{name} 已开启", + "toast_disabled": "{name} 已关闭", + "toast_removed": "{name} 已移除", + "toast_forced": "管理员要求 {name} 保持开启", + "toast_approval_requested": "{name} 正在等待管理员批准", + "empty_title": "暂无插件", + "empty_admin": "管理员尚未添加任何插件。", + "empty_uploads": "从 ZIP 文件安装插件即可开始使用。", + "empty_demo": "插件需要真实账户,因此演示模式中没有插件。", + "syncing": "正在与服务器核对插件…", + "status_installed": "已关闭", + "status_enabled": "正在启动", + "status_running": "已开启", + "status_disabled": "已关闭", + "status_error": "出现问题", + "badge_forced": "必需", + "badge_managed": "由管理员提供", + "badge_yours": "由您安装", + "badge_awaiting": "等待批准", + "toggle_label": "开启或关闭 {name}", + "forced_note": "管理员要求使用此插件,因此无法关闭。", + "permissions": "可执行的操作", + "permissions_managed": "可执行的操作(已由管理员批准)", + "settings": "设置", + "remove": "移除", + "remove_title": "移除 {name}?", + "remove_message": "插件及其在此浏览器中存储的所有内容都将被删除。您以后可以重新安装。", + "remove_confirm": "移除", + "error_user_plugins_off": "管理员已关闭用户安装的插件,因此此插件不会运行。", + "error_approval_unavailable": "此插件是在需要审批之前安装的。请移除后重新安装,以提交审批。", + "error_approval_denied": "管理员未批准此插件。", + "error_approval_pending": "正在等待管理员批准此插件。", + "error_auto_disabled": "由于反复出错已关闭。重新开启即可再试一次。", + "accounts_heading": "已连接的账户", + "account_on": "{host} 上的 {label}", + "account_waiting": "{host} 上的 {label}(尚未完成登录)", + "disconnect": "断开连接", + "disconnect_failed": "无法断开 {label} 的连接" + }, + "consent": { + "title": "允许 {name} 执行这些操作?", + "intro": "此插件请求以下权限。只有在您允许后才会获得。", + "high_risk_note": "带标记的项目会让插件访问您的邮件内容,或将数据发送到其他地方。请只对您信任的插件允许。", + "revoke_hint": "您可以随时在设置中关闭或移除此插件。", + "deny": "不允许", + "allow": "允许" + }, + "permissions": { + "none": "不需要特殊权限。", + "high_risk": "需谨慎", + "email_read": { + "title": "读取您的邮件", + "body": "查看您账户中任何邮件的发件人、收件人、主题和完整正文,以及您的文件夹。" + }, + "email_write": { + "title": "更改和整理您的邮件", + "body": "为您的邮件添加或移除标签、旗标和其他关键词,在您的文件夹之间移动邮件或将其移至归档,并保存草稿。" + }, + "email_send": { + "title": "在发送前更改邮件", + "body": "读取并更改您撰写的邮件的收件人、主题、正文和附件,并可阻止邮件发送。" + }, + "crypto_full": { + "title": "完全访问此应用", + "body": "以与网页邮箱本身相同的权限运行。它可以读取您的所有邮件和已存储的密钥,并代表您执行操作。请只对您完全信任的插件允许。" + }, + "email_raw_send": { + "title": "发送它自行构建的邮件", + "body": "从您的账户发送完整的邮件,例如已签名或已加密的邮件。" + }, + "email_blob_read": { + "title": "读取邮件的原始内容", + "body": "读取您的邮件和附件的原始数据,以及发送前您附加的每个文件。" + }, + "email_blob_write": { + "title": "在发送前替换附件", + "body": "将文件上传到您的邮件服务器,并在邮件发送前用另一个文件替换您附加的文件。" + }, + "email_render_takeover": { + "title": "更改邮件的显示方式", + "body": "替换您打开的邮件和邮件列表中的正文,例如用于显示解密后的内容。" + }, + "calendar_read": { + "title": "读取您的日历", + "body": "查看您的日历及其中的活动。" + }, + "calendar_write": { + "title": "更改您的日历", + "body": "创建、更改和删除活动。" + }, + "contacts_read": { + "title": "读取您的联系人", + "body": "搜索和读取您的通讯录。" + }, + "contacts_write": { + "title": "更改您的联系人", + "body": "创建、更改和删除联系人及通讯录。" + }, + "files_read": { + "title": "读取您的文件", + "body": "浏览和打开您账户中存储的文件。" + }, + "files_write": { + "title": "更改您的文件", + "body": "上传、重命名、移动和删除您账户中的文件。" + }, + "identity_read": { + "title": "查看您的发件身份", + "body": "查看您用于发送邮件的地址及其签名。" + }, + "identity_write": { + "title": "更改您的发件身份", + "body": "创建、更改和删除发件身份及签名。" + }, + "filters_read": { + "title": "读取您的邮件过滤器", + "body": "查看用于整理收到邮件的规则。" + }, + "filters_write": { + "title": "更改您的邮件过滤器", + "body": "向服务器端过滤器添加规则。这些规则可以移动、转发或删除收到的邮件。" + }, + "tasks_read": { + "title": "读取您的任务", + "body": "查看您的任务列表和任务。" + }, + "tasks_write": { + "title": "更改您的任务", + "body": "创建、更改、完成和删除任务。" + }, + "templates_read": { + "title": "读取您的模板", + "body": "查看您保存的邮件模板。" + }, + "templates_write": { + "title": "更改您的模板", + "body": "创建、更改和删除邮件模板。" + }, + "smime_read": { + "title": "查看您的 S/MIME 状态", + "body": "查看您是否拥有 S/MIME 密钥和证书,但无法访问密钥。" + }, + "vacation_read": { + "title": "查看您的外出自动回复", + "body": "查看自动回复是否已开启及其内容。" + }, + "vacation_write": { + "title": "更改您的外出自动回复", + "body": "开启或关闭自动回复,并更改其内容和日期。" + }, + "settings_read": { + "title": "读取您的设置", + "body": "查看您的偏好设置和标签定义。" + }, + "settings_write": { + "title": "更改您的设置", + "body": "添加标签、调整标签顺序,并更改您的偏好设置。" + }, + "security_read": { + "title": "查看您的账户安全", + "body": "查看双因素身份验证和加密是否已开启,但无法访问机密信息。" + }, + "auth_observe": { + "title": "得知您何时登录", + "body": "在您登录、切换账户或会话续期时收到通知。" + }, + "auth_emit": { + "title": "让您退出登录", + "body": "让您退出网页邮箱。" + }, + "account_read": { + "title": "查看您的账户", + "body": "查看您添加的账户及其地址和服务器。" + }, + "http_post": { + "title": "代表您使用网页邮箱服务器", + "body": "使用您的登录信息调用以下服务器端点:" + }, + "http_fetch": { + "title": "连接其他网站", + "body": "向以下网站发送请求。插件能读取的任何内容都可能被发送到这些网站:" + }, + "admin_config": { + "title": "读取管理员为其保存的设置", + "body": "读取管理员为此插件保存的设置。" + }, + "ui_observe": { + "title": "跟踪您在应用中的位置", + "body": "了解当前打开的页面,并对您点击的链接做出响应。" + }, + "ui_toolbar": { + "title": "在工具栏中添加按钮", + "body": "在邮件工具栏、打开的邮件和顶部栏中显示它自己的按钮。" + }, + "ui_app_top_banner": { + "title": "在顶部显示横幅", + "body": "在应用顶部显示一条通知。" + }, + "ui_email_banner": { + "title": "在邮件上显示通知", + "body": "在打开的邮件或会话各部分的上方、内部和之间显示它自己的通知和摘要。" + }, + "ui_email_footer": { + "title": "在邮件下方添加内容", + "body": "在打开的邮件下方显示它自己的内容。" + }, + "ui_contact_cryptokeys": { + "title": "显示联系人的加密密钥", + "body": "显示并管理联系人的加密密钥。" + }, + "ui_email_details": { + "title": "在邮件详情中添加面板", + "body": "在打开的邮件旁边或下方显示它自己的面板。" + }, + "ui_download_file": { + "title": "将文件保存到您的设备", + "body": "提供它创建的文件供下载。" + }, + "ui_message_list_tabs": { + "title": "为收件箱添加标签页", + "body": "将收件箱分为“动态”或“推广”等标签页。" + }, + "ui_composer_toolbar": { + "title": "添加到撰写窗口", + "body": "在您撰写邮件时显示它自己的按钮、菜单项和区块。" + }, + "ui_composer_sidebar": { + "title": "在撰写窗口中添加面板", + "body": "在您撰写的邮件旁边显示它自己的面板。" + }, + "ui_sidebar_widget": { + "title": "添加到侧边栏", + "body": "在邮件侧边栏中显示它自己的方框和区块。" + }, + "ui_settings_section": { + "title": "添加设置页面", + "body": "在设置中显示它自己的页面。" + }, + "ui_context_menu": { + "title": "添加到邮件菜单", + "body": "在右键点击邮件时打开的菜单中添加条目。" + }, + "ui_navigation_rail": { + "title": "添加到导航栏", + "body": "在导航栏中显示它自己的按钮。" + }, + "ui_keyboard": { + "title": "添加键盘快捷键", + "body": "响应它定义的键盘快捷键。" + }, + "ui_calendar_action": { + "title": "添加到日历界面", + "body": "在活动上显示它自己的按钮和区块,并在日历日期上显示简短标签和圆点。" + }, + "ui_admin_page": { + "title": "添加管理页面", + "body": "在管理设置中显示它自己的页面。" + }, + "app_lifecycle": { + "title": "得知应用何时启动和停止", + "body": "在应用启动、离线或您退出登录时收到通知。" + }, + "compose_open": { + "title": "为您打开撰写窗口", + "body": "打开一封已填好收件人、正文或附件的新邮件。是否发送仍由您决定。" + }, + "compose_read": { + "title": "读取您正在写的邮件", + "body": "在编辑器中使用插件时,读取您正在写的邮件正文。不包括签名和引用的邮件。" + }, + "compose_write": { + "title": "修改您正在写的邮件", + "body": "在编辑器中使用插件时,替换或添加您正在写的邮件中的文字。每次修改都可以撤销,是否发送仍由您决定。" + }, + "ui_notifications": { + "title": "显示通知", + "body": "在您的设备上显示通知;如果您未允许通知,则在应用内显示。每条通知都会注明插件名称。" + }, + "ui_message_list": { + "title": "添加到邮件列表", + "body": "在邮件列表上方显示它自己的内容,并在邮件上和所选邮件的工具栏中显示按钮。" + }, + "ui_contacts": { + "title": "添加到联系人界面", + "body": "在联系人详情中显示它自己的区块,并在联系人列表中显示按钮。" + }, + "ui_tasks": { + "title": "添加到任务界面", + "body": "在任务详情中显示它自己的区块,并在任务列表中显示按钮。" + }, + "ui_files": { + "title": "添加到文件界面", + "body": "在文件预览中显示它自己的区块,并在文件菜单中显示条目。" + }, + "ui_commands": { + "title": "添加命令", + "body": "向命令面板(Ctrl+K)添加它自己的条目。" + }, + "ui_app_page": { + "title": "添加页面", + "body": "向应用添加它自己的页面,每个页面在导航栏中都有一个按钮。" + }, + "http_proxy": { + "title": "使用您连接的账户", + "body": "使用您或管理员设置的账户或密钥,通过 Bulwark 的服务器发送请求。插件永远看不到密码,但可以在该账户所属的服务器上执行该账户能做的任何操作。" + } + }, + "dialog": { + "from_plugin": "来自插件 {name}", + "ok": "确定", + "submit": "提交", + "confirm": "确认", + "cancel": "取消", + "close": "关闭", + "turn_off": "关闭 {name}", + "plugin_menu": "{name} 选项", + "password_note": "此表单来自插件 {name},而不是网页邮箱。只有在您信任此插件时才输入密码。", + "toast_auto_disabled": "{name} 因反复出错已被关闭。" + }, + "connect": { + "title": "连接 {label}", + "intro_password": "{name} 想使用您的 {label} 账户。Bulwark 将密码保存在自己的服务器上,只会发送给 {host}。插件永远看不到密码。", + "intro_password_any": "{name} 想使用您的 {label} 账户。Bulwark 将密码保存在自己的服务器上,只会发送给您在此处输入的服务器。插件永远看不到密码。", + "intro_signin": "{name} 想登录您的 {label} 账户。请在此处输入服务器,然后在该服务器自己的页面上登录。Bulwark 将获得的访问权限保存在自己的服务器上;插件永远看不到它。", + "server": "服务器地址", + "server_hint": "cloud.example.com", + "username": "用户名", + "secret": "密码或密钥", + "connect": "连接", + "continue": "继续", + "cancel": "取消", + "saving": "正在保存…", + "error_server": "请输入服务器地址,例如 https://cloud.example.com", + "error_save": "无法保存:{error}" + } + }, + "global_search": { + "title": "全局搜索", + "placeholder": "搜索邮件、联系人、日历、文件...", + "scope_label": "搜索范围", + "account_label": "账户", + "scope_all": "全部", + "scope_mail": "邮件", + "scope_contacts": "联系人", + "scope_calendar": "日历", + "scope_files": "文件", + "all_accounts": "所有账户", + "show_all": "显示全部结果", + "no_results": "未找到“{query}”的结果", + "server_hint": "完整单词在服务器上搜索;部分单词匹配已加载的内容", + "searching": "搜索中...", + "account_error": "无法搜索 {account}", + "recent_searches": "最近搜索", + "recurring": "重复", + "no_subject": "(no subject)", + "empty_hint": "在所有账户中搜索邮件、联系人、日程和文件。可尝试 from:、subject:、has:attachment、after:2026-01-01。", + "flat_by_date": "按日期平铺", + "grouped": "分组", + "load_more": "加载更多", + "preview_empty": "选择一个结果以预览", + "open_full": "打开", + "view_label": "视图" + }, + "top_bar": { + "more": "更多选项" + }, + "plugin_pages": { + "loading": "正在加载页面", + "not_available_title": "此页面不可用", + "not_available_description": "添加此页面的插件已关闭或已不再安装。", + "back_to_mail": "返回邮件", + "settings_group": "插件" + }, + "command_palette": { + "title": "命令面板", + "open": "打开命令面板", + "placeholder": "输入命令", + "no_results": "没有匹配的命令", + "go_to": "前往{name}", + "compose": "新邮件", + "search": "搜索邮件", + "theme_dark": "切换到深色主题", + "theme_light": "切换到浅色主题", + "shortcuts": "显示键盘快捷键", + "group_navigation": "前往", + "group_actions": "操作", + "group_plugins": "插件" + }, + "notification_center": { + "title": "通知", + "mark_all_read": "全部标为已读", + "clear": "清除", + "empty": "没有通知", + "close": "关闭", + "unread": "未读", + "unread_count": "{count} 条未读", + "level_error": "错误", + "level_warning": "警告" + } +} From 5f5ffdaddb07f24b60a235fa46d1ec3e6fb1ee32 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:11:12 +0200 Subject: [PATCH 257/334] feat: mute notifications for single folders or whole accounts #78 --- App.tsx | 38 +++++++ locales/rn/en.json | 2 + scripts/generate-icons.mjs | 1 + src/components/SidebarDrawer.tsx | 79 ++++++++++++- src/components/icons/index.ts | 2 + .../settings/NotificationSettings.tsx | 27 +++++ src/lib/__tests__/notification-mutes.test.ts | 53 +++++++++ .../__tests__/push-config-mailboxes.test.ts | 70 ++++++++++++ src/lib/notification-mutes.ts | 104 ++++++++++++++++++ src/lib/push-notifications.ts | 59 ++++++++-- src/stores/settings-store.ts | 7 ++ 11 files changed, 430 insertions(+), 12 deletions(-) create mode 100644 src/lib/__tests__/notification-mutes.test.ts create mode 100644 src/lib/notification-mutes.ts diff --git a/App.tsx b/App.tsx index 2cb212d9..b1ab1f7a 100644 --- a/App.tsx +++ b/App.tsx @@ -39,6 +39,9 @@ import { type NotificationTapPayload, } from './src/lib/push-notifications'; import { addUnifiedPushEndpointListener } from './src/lib/unified-push'; + +// Pause between a mute change and the push-filter update it sends (#78). +const MUTES_RESYNC_DELAY_MS = 1_000; import type { MainTabsParamList, RootStackParamList } from './src/navigation/types'; import ComposeScreen from './src/screens/ComposeScreen'; import EmailThreadScreen from './src/screens/EmailThreadScreen'; @@ -684,6 +687,41 @@ export default function App() { }; }, [client, isAuthenticated, emailNotificationsEnabled, activeAccountId]); + // Muting a folder or account changes the server-side push filter (#78), + // which the setup above would only send at the next launch. Send it now, + // after a short pause so a few taps in a row make one update. Compared by + // content, since hydration hands back an equal but new object; the value + // seen first after hydration is the baseline, not a change. + const notificationMutes = useSettingsStore((s) => s.notificationMutes); + const settingsHydrated = useSettingsStore((s) => s.hydrated); + const mutesKey = JSON.stringify(notificationMutes); + const lastMutesKey = React.useRef(null); + React.useEffect(() => { + if (!settingsHydrated) return; + if (lastMutesKey.current === null || lastMutesKey.current === mutesKey) { + lastMutesKey.current = mutesKey; + return; + } + lastMutesKey.current = mutesKey; + if (!isAuthenticated || !client || !emailNotificationsEnabled) return; + let cancelled = false; + const timer = setTimeout(async () => { + const relayBaseUrl = await getStoredRelayBaseUrl(); + if (!relayBaseUrl || cancelled) return; + try { + await resyncPushNotifications({ relayBaseUrl, accountLabel: client.username ?? undefined }); + } catch (error) { + console.warn('[push] mute update failed:', error instanceof Error ? error.message : error); + } + }, MUTES_RESYNC_DELAY_MS); + return () => { + cancelled = true; + clearTimeout(timer); + }; + // Only a change of the mutes should send an update; the rest is read live. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mutesKey, settingsHydrated]); + // Live updates (SSE with polling fallback), re-armed on every account // switch and every re-established session — the singleton `client` object // never changes identity, so it cannot be the dependency on its own. diff --git a/locales/rn/en.json b/locales/rn/en.json index 0eb98652..aad32c27 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -996,6 +996,8 @@ "email": { "app_icon_badge": "Unread count on app icon", "app_icon_badge_desc": "Show the inbox unread count as a badge on the app icon.", + "mute_account": "Mute this account", + "mute_account_desc": "No alerts for new mail in this account. Long-press a folder in the sidebar to mute or unmute it on its own.", "sound_channel_hint": "Sound and vibration for mail alerts are controlled by the Android notification channel: long-press a notification or open the system app settings." }, "push": { diff --git a/scripts/generate-icons.mjs b/scripts/generate-icons.mjs index 8be3b870..fe32f5a6 100644 --- a/scripts/generate-icons.mjs +++ b/scripts/generate-icons.mjs @@ -37,6 +37,7 @@ const MAP = { ArrowUpNarrowWide: 'sort-ascending', Ban: 'ban', Bell: 'bell', + BellOff: 'bell-off', BellRing: 'bell-ringing', Bold: 'bold', Book: 'book-2', diff --git a/src/components/SidebarDrawer.tsx b/src/components/SidebarDrawer.tsx index d7342572..f0e929e7 100644 --- a/src/components/SidebarDrawer.tsx +++ b/src/components/SidebarDrawer.tsx @@ -9,7 +9,7 @@ import { Inbox, Send, File as FileIcon, Trash2, Ban, Archive, Star, Folder, FolderOpen, ChevronDown, ChevronRight, X, Settings, LogOut, Check, Plus, Clock, Layers, Users, Tag, Mails, MailOpen, StickyNote, AlarmClock, Flag, - CheckCheck, Eraser, FolderPlus, Pencil, AlertTriangle, UserMinus, + CheckCheck, Eraser, FolderPlus, Pencil, AlertTriangle, UserMinus, Bell, BellOff, RotateCcw, type AppIcon, } from './icons'; import { spacing, radius, typography, type ThemePalette } from '../theme/tokens'; @@ -40,6 +40,11 @@ import type { Mailbox } from '../api/types'; import { MODAL_ORIENTATIONS } from '../lib/modal-orientations'; import { confirmSignOut } from '../lib/sign-out-confirm'; import { setPendingSettingsTab } from '../navigation/pending-settings-tab'; +import { + isAccountMuted, isMailboxMuted, mailboxOverridesForAccount, resetMailboxMutes, setAccountMuted, setMailboxMuted, + type NotificationMutes, +} from '../lib/notification-mutes'; +import { serverSupportsEmailPush } from '../lib/push-notifications'; const CHEVRON_SLOT = 20; const INDENT_STEP = 12; @@ -157,6 +162,8 @@ interface SidebarRowProps { showTotal: boolean; hasChildren: boolean; isExpanded: boolean; + /** New mail here does not notify (#78). */ + muted?: boolean; onPress: () => void; onLongPress?: () => void; onToggleExpand: () => void; @@ -165,7 +172,7 @@ interface SidebarRowProps { function SidebarRow({ icon, label, depth, isSelected, isAccountHeader, unread, total, showTotal, - hasChildren, isExpanded, onPress, onLongPress, onToggleExpand, onPressUnread, + hasChildren, isExpanded, muted, onPress, onLongPress, onToggleExpand, onPressUnread, }: SidebarRowProps) { const c = useColors(); const styles = React.useMemo(() => makeStyles(c), [c]); @@ -215,6 +222,11 @@ function SidebarRow({ > {label} + {muted && ( + + + + )} ); @@ -334,6 +346,8 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side const showFolderTotalCount = useSettingsStore((s) => s.showFolderTotalCount); const includeGroupInUnified = useSettingsStore((s) => s.includeGroupInUnified); const unifiedCrossAccount = useSettingsStore((s) => s.unifiedCrossAccount); + const notificationMutes = useSettingsStore((s) => s.notificationMutes); + const updateSetting = useSettingsStore((s) => s.updateSetting); const keywordDefs = useKeywordsStore((s) => s.keywords); const keywordsHydrated = useKeywordsStore((s) => s.hydrated); const hydrateKeywords = useKeywordsStore((s) => s.hydrate); @@ -459,6 +473,41 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side id: mb.originalId ?? mb.id, accountId: mb.isShared ? mb.accountId : undefined, }); + + // Notification mutes (#78). Muting only takes effect through the server's + // emailPush filter, so the actions show only where that exists. The + // settings are keyed by the server's ids: a folder of the own account may + // carry no accountId, so the client's stands in. + let primaryJmapAccountId: string | null = null; + try { primaryJmapAccountId = jmapClient.accountId; } catch { primaryJmapAccountId = null; } + const canMute = primaryJmapAccountId !== null && serverSupportsEmailPush(); + const muteAccountOf = (accountId?: string | null): string => accountId || primaryJmapAccountId || ''; + const saveMutes = (next: NotificationMutes) => updateSetting('notificationMutes', next); + const muteActionsForAccount = (accountId: string): SheetAction[] => { + const muted = isAccountMuted(notificationMutes, accountId); + const overrides = mailboxOverridesForAccount(notificationMutes, accountId); + const actions: SheetAction[] = [{ + key: 'mute', + label: muted + ? t('mailbox_context_menu.unmute_account_notifications', 'Unmute notifications for this account') + : t('mailbox_context_menu.mute_account_notifications', 'Mute notifications for this account'), + icon: muted ? Bell : BellOff, + onPress: () => saveMutes(setAccountMuted(notificationMutes, accountId, !muted)), + }]; + if (overrides.muted.length + overrides.unmuted.length > 0) { + actions.push({ + key: 'reset-mutes', + label: t('mailbox_context_menu.reset_folder_notifications', 'Reset folder notification settings'), + icon: RotateCcw, + onPress: () => saveMutes(resetMailboxMutes(notificationMutes, accountId)), + }); + } + return actions; + }; + const openOwnAccountSheet = () => { + if (!canMute) return; + setSheet({ title: username ?? t('sidebar.folders', 'Folders'), actions: muteActionsForAccount(muteAccountOf()) }); + }; const runFolderAction = async (label: string, work: () => Promise, affectsCurrent: boolean) => { setBusy(true); try { @@ -499,6 +548,18 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side if (subtree.length > 1) { actions.push({ key: 'tree', label: t('mailbox_context_menu.mark_folder_tree_read', 'Mark folder & subfolders as read'), icon: CheckCheck, onPress: () => markRead(subtree) }); } + if (canMute) { + const accountId = muteAccountOf(mb.accountId); + const muted = isMailboxMuted(notificationMutes, accountId, ref.id); + actions.push({ + key: 'mute', + label: muted + ? t('mailbox_context_menu.unmute_notifications', 'Unmute notifications') + : t('mailbox_context_menu.mute_notifications', 'Mute notifications'), + icon: muted ? Bell : BellOff, + onPress: () => saveMutes(setMailboxMuted(notificationMutes, accountId, ref.id, !muted)), + }); + } if (!mb.isShared) { actions.push({ key: 'all', @@ -622,6 +683,7 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side onPress: () => markRead(mailboxes.filter((m) => m.isShared && m.accountId === accountId).map((m) => m.id)), }, ]; + if (canMute) actions.push(...muteActionsForAccount(accountId)); if (canCreate) { actions.push({ key: 'new', @@ -1036,7 +1098,14 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side )} {/* Folders section */} - toggleSection('foldersExpanded', setFoldersExpanded)}> + toggleSection('foldersExpanded', setFoldersExpanded)} + // The own account's notification mutes (#78), like the webmail's + // menu on this header. + onLongPress={canMute ? openOwnAccountSheet : undefined} + delayLongPress={350} + > {foldersExpanded ? ( ) : ( @@ -1090,6 +1159,9 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side unread={node.unreadEmails} total={node.totalEmails} showTotal={showFolderTotalCount && !node.isAccountNode} + muted={canMute && (node.isAccountNode + ? isAccountMuted(notificationMutes, muteAccountOf(node.accountId)) + : isMailboxMuted(notificationMutes, muteAccountOf(node.accountId), node.originalId ?? node.id))} hasChildren={hasChildren} isExpanded={isExpanded} onPress={ @@ -1458,6 +1530,7 @@ function makeStyles(c: ThemePalette) { ...typography.bodySemibold, color: c.text, }, + rowMuted: { marginLeft: spacing.xs }, rowLabelAccount: { fontSize: 13, lineHeight: 18, diff --git a/src/components/icons/index.ts b/src/components/icons/index.ts index fba15194..c949964f 100644 --- a/src/components/icons/index.ts +++ b/src/components/icons/index.ts @@ -29,6 +29,7 @@ const G = { 'ban': ["M3 12a9 9 0 1 0 18 0a9 9 0 1 0 -18 0","M5.7 5.7l12.6 12.6"], 'beach': ["M17.553 16.75a7.5 7.5 0 0 0 -10.606 0","M18 3.804a6 6 0 0 0 -8.196 2.196l10.392 6a6 6 0 0 0 -2.196 -8.196","M16.732 10c1.658 -2.87 2.225 -5.644 1.268 -6.196c-.957 -.552 -3.075 1.326 -4.732 4.196","M15 9l-3 5.196","M3 19.25a2.4 2.4 0 0 1 1 -.25a2.4 2.4 0 0 1 2 1a2.4 2.4 0 0 0 2 1a2.4 2.4 0 0 0 2 -1a2.4 2.4 0 0 1 2 -1a2.4 2.4 0 0 1 2 1a2.4 2.4 0 0 0 2 1a2.4 2.4 0 0 0 2 -1a2.4 2.4 0 0 1 2 -1a2.4 2.4 0 0 1 1 .25"], 'bell': ["M10 5a2 2 0 1 1 4 0a7 7 0 0 1 4 6v3a4 4 0 0 0 2 3h-16a4 4 0 0 0 2 -3v-3a7 7 0 0 1 4 -6","M9 17v1a3 3 0 0 0 6 0v-1"], + 'bell-off': ["M9.346 5.353c.21 -.129 .428 -.246 .654 -.353a2 2 0 1 1 4 0a7 7 0 0 1 4 6v3m-1 3h-13a4 4 0 0 0 2 -3v-3a6.996 6.996 0 0 1 1.273 -3.707","M9 17v1a3 3 0 0 0 6 0v-1","M3 3l18 18"], 'bell-ringing': ["M10 5a2 2 0 0 1 4 0a7 7 0 0 1 4 6v3a4 4 0 0 0 2 3h-16a4 4 0 0 0 2 -3v-3a7 7 0 0 1 4 -6","M9 17v1a3 3 0 0 0 6 0v-1","M21 6.727a11.05 11.05 0 0 0 -2.794 -3.727","M3 6.727a11.05 11.05 0 0 1 2.792 -3.727"], 'bold': ["M7 5h6a3.5 3.5 0 0 1 0 7h-6l0 -7","M13 12h1a3.5 3.5 0 0 1 0 7h-7v-7"], 'book': ["M3 19a9 9 0 0 1 9 0a9 9 0 0 1 9 0","M3 6a9 9 0 0 1 9 0a9 9 0 0 1 9 0","M3 6l0 13","M12 6l0 13","M21 6l0 13"], @@ -190,6 +191,7 @@ export const ArrowUpDown = makeIcon('ArrowUpDown', G['arrows-sort']); export const ArrowUpNarrowWide = makeIcon('ArrowUpNarrowWide', G['sort-ascending']); export const Ban = makeIcon('Ban', G['ban']); export const Bell = makeIcon('Bell', G['bell']); +export const BellOff = makeIcon('BellOff', G['bell-off']); export const BellRing = makeIcon('BellRing', G['bell-ringing']); export const Bold = makeIcon('Bold', G['bold']); export const Book = makeIcon('Book', G['book-2']); diff --git a/src/components/settings/NotificationSettings.tsx b/src/components/settings/NotificationSettings.tsx index 93c26dd0..aba61598 100644 --- a/src/components/settings/NotificationSettings.tsx +++ b/src/components/settings/NotificationSettings.tsx @@ -27,6 +27,7 @@ import { PushSetupError, readPushAccountIds, revokePushDevice, + serverSupportsEmailPush, setStoredRelayBaseUrl, setupPushNotifications, getStoredPushTransport, @@ -42,6 +43,8 @@ import { saveUnifiedPushDistributor, } from '../../lib/unified-push'; import { canBadgeAppIcon } from '../../lib/app-badge'; +import { isAccountMuted, setAccountMuted } from '../../lib/notification-mutes'; +import { jmapClient } from '../../api/jmap-client'; type PushStatus = | { kind: 'idle' } @@ -78,6 +81,14 @@ export function NotificationSettings() { const hydrate = useSettingsStore((s) => s.hydrate); const update = useSettingsStore((s) => s.updateSetting); const emailEnabled = useSettingsStore((s) => s.emailNotificationsEnabled); + const notificationMutes = useSettingsStore((s) => s.notificationMutes); + // Muting works through the server's emailPush filter (#78): offered only + // where it exists, for the account this screen shows. Single folders are + // muted from the sidebar. + let ownJmapAccountId: string | null = null; + try { ownJmapAccountId = jmapClient.accountId; } catch { ownJmapAccountId = null; } + const canMuteAccount = ownJmapAccountId !== null && serverSupportsEmailPush(); + const accountMuted = ownJmapAccountId !== null && isAccountMuted(notificationMutes, ownJmapAccountId); const calEnabled = useSettingsStore((s) => s.calendarNotificationsEnabled); const invitationParsing = useSettingsStore((s) => s.calendarInvitationParsingEnabled); const appIconBadge = useSettingsStore((s) => s.appIconUnreadBadge); @@ -467,6 +478,22 @@ export function NotificationSettings() { > update('emailNotificationsEnabled', v)} /> + {canMuteAccount && ( + + { + if (ownJmapAccountId) update('notificationMutes', setAccountMuted(notificationMutes, ownJmapAccountId, v)); + }} + /> + + )} {canBadgeAppIcon() && ( { + it('lets a folder follow its account until it has its own setting', () => { + const accountMuted = setAccountMuted(EMPTY_NOTIFICATION_MUTES, 'a', true); + expect(isMailboxMuted(accountMuted, 'a', 'reports')).toBe(true); + expect(isMailboxMuted(accountMuted, 'b', 'reports')).toBe(false); + + const unmutedFolder = setMailboxMuted(accountMuted, 'a', 'inbox', false); + expect(isMailboxMuted(unmutedFolder, 'a', 'inbox')).toBe(false); + expect(isMailboxMuted(unmutedFolder, 'a', 'reports')).toBe(true); + + // The folder's own setting wins over the account in both directions. + const accountBack = setAccountMuted(unmutedFolder, 'a', false); + const mutedFolder = setMailboxMuted(accountBack, 'a', 'renovate', true); + expect(isMailboxMuted(mutedFolder, 'a', 'renovate')).toBe(true); + expect(isMailboxMuted(mutedFolder, 'a', 'reports')).toBe(false); + }); + + it('drops the folder setting when it is set back to what the account says', () => { + const muted = setMailboxMuted(EMPTY_NOTIFICATION_MUTES, 'a', 'renovate', true); + const unmuted = setMailboxMuted(muted, 'a', 'renovate', false); + expect(unmuted.mailboxes).toEqual({}); + + // So muting the account later reaches this folder too. + expect(isMailboxMuted(setAccountMuted(unmuted, 'a', true), 'a', 'renovate')).toBe(true); + }); + + it('resets the folder settings of one account only', () => { + let mutes = setMailboxMuted(EMPTY_NOTIFICATION_MUTES, 'a', 'x', true); + mutes = setMailboxMuted(mutes, 'ab', 'y', true); + mutes = resetMailboxMutes(mutes, 'a'); + expect(mailboxOverridesForAccount(mutes, 'a')).toEqual({ muted: [], unmuted: [] }); + // An account id that merely starts with "a" keeps its settings. + expect(mailboxOverridesForAccount(mutes, 'ab')).toEqual({ muted: ['y'], unmuted: [] }); + }); + + it('repairs malformed stored values', () => { + expect(normalizeNotificationMutes(null)).toEqual(EMPTY_NOTIFICATION_MUTES); + expect(normalizeNotificationMutes(['a'])).toEqual(EMPTY_NOTIFICATION_MUTES); + expect(normalizeNotificationMutes({ accounts: ['a', 3, ''], mailboxes: { 'a/x': true, 'a/y': 'yes' } })) + .toEqual({ accounts: ['a'], mailboxes: { 'a/x': true } }); + }); +}); diff --git a/src/lib/__tests__/push-config-mailboxes.test.ts b/src/lib/__tests__/push-config-mailboxes.test.ts index 93082ce6..51cfc20c 100644 --- a/src/lib/__tests__/push-config-mailboxes.test.ts +++ b/src/lib/__tests__/push-config-mailboxes.test.ts @@ -19,6 +19,7 @@ vi.mock('../../api/email', () => ({ })); import { getMailboxes, getSharedMailboxes } from '../../api/email'; +import { EMPTY_NOTIFICATION_MUTES, type NotificationMutes } from '../notification-mutes'; import { buildEmailPushConfig } from '../push-notifications'; import { provideLoadedMailboxes } from '../mailbox-source'; import { generateAccountId } from '../account-utils'; @@ -65,3 +66,72 @@ describe('push filter folders', () => { }); }); }); + +// Muted accounts and folders (#78) are left out by the server-side filter. +describe('push filter with muted folders and accounts (#78)', () => { + const folders = [ + { id: 'inbox', role: 'inbox', accountId: 'jmap-primary' }, + { id: 'junk', role: 'junk', accountId: 'jmap-primary' }, + { id: 'renovate', accountId: 'jmap-primary' }, + { id: 'team:in', originalId: 'in', role: 'inbox', accountId: 'team', isShared: true }, + { id: 'team:j', originalId: 'j', role: 'junk', accountId: 'team', isShared: true }, + ]; + const mutes = (accounts: string[], mailboxes: Record = {}): NotificationMutes => ({ accounts, mailboxes }); + const junkOnly = (ids: string[]) => ({ operator: 'AND', conditions: [{ notKeyword: '$junk' }, { inMailboxOtherThan: ids }] }); + + beforeEach(() => { + provideLoadedMailboxes(async () => folders as never); + }); + + it('changes nothing without mutes', async () => { + const config = await buildEmailPushConfig(EMPTY_NOTIFICATION_MUTES); + expect(config['jmap-primary'].filter).toEqual(junkOnly(['junk'])); + expect(config.team.filter).toEqual(junkOnly(['j'])); + }); + + it('adds a muted folder to the excluded mailboxes next to Junk', async () => { + const config = await buildEmailPushConfig(mutes([], { 'jmap-primary/renovate': true, 'jmap-primary/gone': true })); + expect(config['jmap-primary'].filter).toEqual(junkOnly(['junk', 'renovate'])); + // Another account's folders are untouched. + expect(config.team.filter).toEqual(junkOnly(['j'])); + }); + + it('silences a muted account entirely', async () => { + const config = await buildEmailPushConfig(mutes(['team'])); + expect(config.team.filter).toEqual({ + operator: 'AND', + conditions: [{ notKeyword: '$junk' }, { hasKeyword: '$junk' }], + }); + expect(config['jmap-primary'].filter).toEqual(junkOnly(['junk'])); + }); + + it('lets a muted account notify for the folders unmuted again', async () => { + const one = await buildEmailPushConfig(mutes(['jmap-primary'], { 'jmap-primary/renovate': false })); + expect(one['jmap-primary'].filter).toEqual({ + operator: 'AND', + conditions: [{ notKeyword: '$junk' }, { inMailbox: 'renovate' }], + }); + + const two = await buildEmailPushConfig(mutes(['jmap-primary'], { + 'jmap-primary/renovate': false, + 'jmap-primary/inbox': false, + // Gone from the server: must not end up in the filter. + 'jmap-primary/deleted': false, + // Unmuting Junk does not bring spam back. + 'jmap-primary/junk': false, + })); + expect(two['jmap-primary'].filter).toEqual({ + operator: 'AND', + conditions: [ + { notKeyword: '$junk' }, + { operator: 'OR', conditions: [{ inMailbox: 'inbox' }, { inMailbox: 'renovate' }] }, + ], + }); + }); + + it('fails instead of dropping the mutes when the folders cannot be read', async () => { + provideLoadedMailboxes(async () => null); + vi.mocked(getMailboxes).mockRejectedValueOnce(new Error('JMAP down')); + await expect(buildEmailPushConfig(mutes(['jmap-primary']))).rejects.toThrow(/JMAP down/); + }); +}); diff --git a/src/lib/notification-mutes.ts b/src/lib/notification-mutes.ts new file mode 100644 index 00000000..90580b56 --- /dev/null +++ b/src/lib/notification-mutes.ts @@ -0,0 +1,104 @@ +/** + * Which accounts and folders stay silent when new mail arrives (#78, + * webmail #1169). Port of the webmail's lib/notification-mutes.ts - keep in sync. + * + * An account is either muted or not. A folder can follow its account or carry + * its own setting, and its own setting always wins: mute one folder in a + * normal account, or mute a whole account and unmute the folders that still + * matter. Ids are the server's JMAP account and mailbox ids, which is what the + * push filter needs; shared-account mailboxes must pass their originalId. + */ + +export interface NotificationMutes { + /** JMAP account ids muted as a whole. */ + accounts: string[]; + /** `accountId/mailboxId` -> true (muted) or false (unmuted). Missing = follow the account. */ + mailboxes: Record; +} + +export const EMPTY_NOTIFICATION_MUTES: NotificationMutes = { accounts: [], mailboxes: {} }; + +function mailboxKey(accountId: string, mailboxId: string): string { + return `${accountId}/${mailboxId}`; +} + +/** Repairs a value from storage or another build into a usable mutes object. */ +export function normalizeNotificationMutes(value: unknown): NotificationMutes { + if (!value || typeof value !== 'object' || Array.isArray(value)) return EMPTY_NOTIFICATION_MUTES; + const raw = value as { accounts?: unknown; mailboxes?: unknown }; + const accounts = Array.isArray(raw.accounts) + ? raw.accounts.filter((id): id is string => typeof id === 'string' && id !== '') + : []; + const mailboxes: Record = {}; + if (raw.mailboxes && typeof raw.mailboxes === 'object' && !Array.isArray(raw.mailboxes)) { + for (const [key, muted] of Object.entries(raw.mailboxes as Record)) { + if (typeof muted === 'boolean') mailboxes[key] = muted; + } + } + return { accounts, mailboxes }; +} + +export function isAccountMuted(mutes: NotificationMutes, accountId: string): boolean { + return mutes.accounts.includes(accountId); +} + +/** The folder's own setting, or undefined when it follows its account. */ +export function mailboxMuteOverride( + mutes: NotificationMutes, + accountId: string, + mailboxId: string, +): boolean | undefined { + return mutes.mailboxes[mailboxKey(accountId, mailboxId)]; +} + +export function isMailboxMuted(mutes: NotificationMutes, accountId: string, mailboxId: string): boolean { + return mailboxMuteOverride(mutes, accountId, mailboxId) ?? isAccountMuted(mutes, accountId); +} + +/** Mailbox ids of one account with their own setting, split by that setting. */ +export function mailboxOverridesForAccount( + mutes: NotificationMutes, + accountId: string, +): { muted: string[]; unmuted: string[] } { + const prefix = mailboxKey(accountId, ''); + const muted: string[] = []; + const unmuted: string[] = []; + for (const [key, value] of Object.entries(mutes.mailboxes)) { + if (!key.startsWith(prefix)) continue; + (value ? muted : unmuted).push(key.slice(prefix.length)); + } + return { muted, unmuted }; +} + +export function setAccountMuted(mutes: NotificationMutes, accountId: string, muted: boolean): NotificationMutes { + const accounts = mutes.accounts.filter((id) => id !== accountId); + if (muted) accounts.push(accountId); + return { ...mutes, accounts }; +} + +/** + * Mutes or unmutes one folder. Setting it back to what the account says drops + * the folder's own setting, so a folder muted and unmuted again follows the + * account later instead of staying pinned to "unmuted". + */ +export function setMailboxMuted( + mutes: NotificationMutes, + accountId: string, + mailboxId: string, + muted: boolean, +): NotificationMutes { + const mailboxes = { ...mutes.mailboxes }; + const key = mailboxKey(accountId, mailboxId); + if (muted === isAccountMuted(mutes, accountId)) delete mailboxes[key]; + else mailboxes[key] = muted; + return { ...mutes, mailboxes }; +} + +/** Drops every folder setting of one account so all its folders follow the account again. */ +export function resetMailboxMutes(mutes: NotificationMutes, accountId: string): NotificationMutes { + const prefix = mailboxKey(accountId, ''); + const mailboxes = Object.fromEntries( + Object.entries(mutes.mailboxes).filter(([key]) => !key.startsWith(prefix)), + ); + return { ...mutes, mailboxes }; +} diff --git a/src/lib/push-notifications.ts b/src/lib/push-notifications.ts index 5ea47bd0..ddc58c04 100644 --- a/src/lib/push-notifications.ts +++ b/src/lib/push-notifications.ts @@ -14,6 +14,13 @@ import { JMAPMethodError, requireMethodResult } from '../api/jmap-result'; import { CAPABILITIES } from '../api/types'; import type { EmailPushConfig, JMAPAccountInfo, JMAPSession, Mailbox } from '../api/types'; import { generateAccountId } from './account-utils'; +import { + EMPTY_NOTIFICATION_MUTES, + isAccountMuted, + mailboxOverridesForAccount, + type NotificationMutes, +} from './notification-mutes'; +import { useSettingsStore } from '../stores/settings-store'; import { t } from '../stores/locale-store'; import { authorityOfType, @@ -296,32 +303,62 @@ export function serverSupportsEmailPush(): boolean { * a Junk-role mailbox (Sieve `fileinto` doesn't set the keyword). The two are * ANDed so a stale mailbox id - the user deleted and recreated Junk - degrades * to keyword-only filtering rather than letting everything through. + * + * Muted accounts and folders (#78) are cut out of the same filter: a muted + * folder joins Junk in the "not only in" list; a muted account only lets + * through mail in the folders the user unmuted again, or nothing at all. */ -export async function buildEmailPushConfig(): Promise> { +export async function buildEmailPushConfig( + mutes: NotificationMutes = EMPTY_NOTIFICATION_MUTES, +): Promise> { const primary = jmapClient.accountId; const junkByAccount = new Map([[primary, []]]); + const mailboxIdsByAccount = new Map>(); // The mail store loads the same folders at the same moment (sign-in, // start): reuse its list rather than a second Mailbox/get of our own. const username = jmapClient.username; const serverUrl = jmapClient.serverUrl; const loaded = username && serverUrl ? await loadedMailboxes(generateAccountId(username, serverUrl)) : null; - const all = loaded ?? [ - ...(await getMailboxes().catch(() => [] as Mailbox[])), - ...(await getSharedMailboxes().catch(() => [] as Mailbox[])), - ]; + // With mutes a swallowed fetch failure would look like "no such folder" + // and unmute everything, so let the real error surface; otherwise the + // filter can still degrade to keyword-only on a transient miss. + const hasMutes = mutes.accounts.length > 0 || Object.keys(mutes.mailboxes).length > 0; + const all = loaded ?? (hasMutes + ? [...(await getMailboxes()), ...(await getSharedMailboxes())] + : [ + ...(await getMailboxes().catch(() => [] as Mailbox[])), + ...(await getSharedMailboxes().catch(() => [] as Mailbox[])), + ]); for (const m of all) { const accountId = m.accountId || primary; - const junk = junkByAccount.get(accountId) ?? []; // Shared-account mailboxes carry a client-side ":" id; // the server only knows the original. - if (m.role === 'junk') junk.push(m.originalId ?? m.id); + const serverId = m.originalId ?? m.id; + const ids = mailboxIdsByAccount.get(accountId) ?? new Set(); + ids.add(serverId); + mailboxIdsByAccount.set(accountId, ids); + const junk = junkByAccount.get(accountId) ?? []; + if (m.role === 'junk') junk.push(serverId); junkByAccount.set(accountId, junk); } const config: Record = {}; for (const [accountId, junkIds] of junkByAccount) { + // Settings for folders that no longer exist are left out. + const existing = mailboxIdsByAccount.get(accountId) ?? new Set(); + const overrides = mailboxOverridesForAccount(mutes, accountId); const conditions: Record[] = [{ notKeyword: '$junk' }]; - if (junkIds.length > 0) conditions.push({ inMailboxOtherThan: [...junkIds].sort() }); + if (isAccountMuted(mutes, accountId)) { + const allowed = overrides.unmuted + .filter((id) => existing.has(id) && !junkIds.includes(id)) + .sort(); + if (allowed.length === 0) conditions.push(NEVER_MATCH); + else if (allowed.length === 1) conditions.push({ inMailbox: allowed[0] }); + else conditions.push({ operator: 'OR', conditions: allowed.map((id) => ({ inMailbox: id })) }); + } else { + const excluded = new Set([...junkIds, ...overrides.muted.filter((id) => existing.has(id))]); + if (excluded.size > 0) conditions.push({ inMailboxOtherThan: [...excluded].sort() }); + } config[accountId] = { // Always the operator form: that's how the server echoes it back, so a // stored config compares equal to a freshly built one. @@ -333,6 +370,10 @@ export async function buildEmailPushConfig(): Promise { if (Array.isArray(v)) return v.map(sortKeys); @@ -980,7 +1021,7 @@ async function setupPushNotificationsInner( if (params.forceRecreate) await AsyncStorage.removeItem(emailPushRefusedKey(accountId)); const refusedBefore = await readRefusedEmailPushAccounts(accountId); const emailPush = serverSupportsEmailPush() - ? withoutAccounts(await buildEmailPushConfig(), refusedBefore) + ? withoutAccounts(await buildEmailPushConfig(useSettingsStore.getState().notificationMutes), refusedBefore) : null; const subKey = subscriptionIdKey(accountId); const storedServerId = await AsyncStorage.getItem(subKey); diff --git a/src/stores/settings-store.ts b/src/stores/settings-store.ts index 1a4e5793..1ed4f379 100644 --- a/src/stores/settings-store.ts +++ b/src/stores/settings-store.ts @@ -4,6 +4,7 @@ import type { Identity } from '../api/types'; import { getIdentities as fetchIdentities } from '../api/identity'; import { jmapClient } from '../api/jmap-client'; import type { SortLevel, MessageListOrderScope } from '../lib/message-list-order'; +import { EMPTY_NOTIFICATION_MUTES, normalizeNotificationMutes, type NotificationMutes } from '../lib/notification-mutes'; export type ExternalContentPolicy = 'allow' | 'block' | 'ask'; export type ThemeMode = 'light' | 'dark' | 'system'; @@ -260,6 +261,9 @@ interface PersistedSettings { // Notifications. Sound/vibration live in the Android notification channel // (the OS owns them after channel creation), so there are no sound keys. emailNotificationsEnabled: boolean; + // Accounts and folders whose new mail does not notify (#78), keyed by the + // server's JMAP ids; see lib/notification-mutes. + notificationMutes: NotificationMutes; calendarNotificationsEnabled: boolean; calendarInvitationParsingEnabled: boolean; // Inbox unread count on the app icon (iOS, see lib/app-badge). Same @@ -413,6 +417,7 @@ const DEFAULT_PERSISTED: PersistedSettings = { filesShowHiddenFiles: false, emailNotificationsEnabled: true, + notificationMutes: EMPTY_NOTIFICATION_MUTES, calendarNotificationsEnabled: true, calendarInvitationParsingEnabled: true, appIconUnreadBadge: true, @@ -605,6 +610,8 @@ export function mergeWithDefaults(parsed: Partial): Persisted if (validator && !validator(v)) continue; if (k === 'bottomQuickActions') { out[k] = normalizeBottomQuickActions(v); + } else if (k === 'notificationMutes') { + out[k] = normalizeNotificationMutes(v); } else if (Array.isArray(def)) { if (Array.isArray(v)) out[k] = v; } else if (typeof def === 'object') { From c7eda4735cc714b9db80f4f5008c49611363f059 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:16:47 +0200 Subject: [PATCH 258/334] feat: trust user-installed CAs and pick the TLS client certificate before the first sign-in #3 --- .../debug/res/xml/network_security_config.xml | 14 +++++ android/app/src/main/AndroidManifest.xml | 2 +- .../main/res/xml/network_security_config.xml | 18 ++++++ locales/rn/en.json | 4 ++ src/screens/LoginScreen.tsx | 55 +++++++++++++++++++ src/screens/login/ServerStep.tsx | 45 ++++++++++++++- 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 android/app/src/debug/res/xml/network_security_config.xml create mode 100644 android/app/src/main/res/xml/network_security_config.xml diff --git a/android/app/src/debug/res/xml/network_security_config.xml b/android/app/src/debug/res/xml/network_security_config.xml new file mode 100644 index 00000000..bea95852 --- /dev/null +++ b/android/app/src/debug/res/xml/network_security_config.xml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 78da5bc3..5d0f74d7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -21,7 +21,7 @@ - + diff --git a/android/app/src/main/res/xml/network_security_config.xml b/android/app/src/main/res/xml/network_security_config.xml new file mode 100644 index 00000000..1d2747a4 --- /dev/null +++ b/android/app/src/main/res/xml/network_security_config.xml @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/locales/rn/en.json b/locales/rn/en.json index aad32c27..fbd4f959 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -606,6 +606,10 @@ "qr_title": "Sign-in code", "scan_desc": "The fastest way in if you're signed in on the web", "scan_title": "Scan a sign-in code", + "server_cert_active": "Signing in with the client certificate \"{alias}\". Tap to choose another.", + "server_cert_clear": "Sign in without the certificate", + "server_cert_failed": "Could not pick a certificate", + "server_cert_hint": "Does your server ask for a client certificate? Pick one installed on this phone.", "server_hint": "Same address you type in the browser. We'll add https:// for you.", "server_label": "Server address", "server_not_found": "We couldn't find a server for {domain}. If you self-host, the address is the one you use for webmail.", diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx index 982d9389..49cb67b1 100644 --- a/src/screens/LoginScreen.tsx +++ b/src/screens/LoginScreen.tsx @@ -20,6 +20,12 @@ import { normalizeServerUrl, } from '../lib/server-discovery'; import { describeLoginError, type LoginErrorCopy } from '../lib/login-errors'; +import { + clearClientCertAlias, + getClientCertAlias, + isClientCertSupported, + pickClientCertAlias, +} from '../lib/client-cert'; import LoginShell from './login/LoginShell'; import ChooseStep from './login/ChooseStep'; import EmailStep from './login/EmailStep'; @@ -109,6 +115,19 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo // Addresses typed before on this device (last 5) plus the registry's, so a // returning user taps instead of retyping. Never contains passwords. const [recentEmails, setRecentEmails] = React.useState([]); + + // TLS client certificate (#3). Every request from discovery on presents + // the picked certificate (lib/client-cert secureFetch), so a server behind + // a proxy that demands one has to get it before the first sign-in - the + // settings screen that also offers it is only reachable afterwards. The + // choice is device-wide, as in the settings. + const certSupported = isClientCertSupported(); + const [certAlias, setCertAlias] = React.useState(null); + const [certBusy, setCertBusy] = React.useState(false); + React.useEffect(() => { + if (!certSupported) return; + void getClientCertAlias().then(setCertAlias).catch(() => undefined); + }, [certSupported]); React.useEffect(() => { void AsyncStorage.getItem(RECENT_EMAILS_KEY).then((raw) => { try { @@ -135,6 +154,36 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo // Servers we already hold credentials for. Discovery trusts these without a // probe, which makes "second account on the same host" instant. const knownServerUrls = React.useMemo(() => accounts.map((a) => a.serverUrl), [accounts]); + + const pickCert = React.useCallback(async () => { + if (certBusy) return; + setCertBusy(true); + setNotice(null); + try { + // The host narrows the system picker to certificates issued for it. + const normalized = normalizeServerUrl(serverInput); + const host = normalized ? new URL(normalized).hostname : null; + const alias = await pickClientCertAlias(host); + if (alias !== null) setCertAlias(alias); + } catch (err) { + setNotice({ + title: t('login.mobile.server_cert_failed', 'Could not pick a certificate'), + detail: err instanceof Error ? err.message : String(err), + }); + } finally { + setCertBusy(false); + } + }, [certBusy, serverInput, t]); + const clearCert = React.useCallback(async () => { + if (certBusy) return; + setCertBusy(true); + try { + await clearClientCertAlias(); + setCertAlias(null); + } finally { + setCertBusy(false); + } + }, [certBusy]); const knownServerUrl = React.useMemo(() => { const active = accounts.find((a) => a.id === activeAccountId); return active?.serverUrl ?? accounts[0]?.serverUrl ?? null; @@ -562,6 +611,12 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo onPaste={() => setPasteVisible(true)} failedDomain={failedDomain} notice={notice} + clientCert={certSupported ? { + alias: certAlias, + busy: certBusy, + onPick: () => { void pickCert(); }, + onClear: () => { void clearCert(); }, + } : undefined} /> ) : null} diff --git a/src/screens/login/ServerStep.tsx b/src/screens/login/ServerStep.tsx index f349e63a..64ba5a76 100644 --- a/src/screens/login/ServerStep.tsx +++ b/src/screens/login/ServerStep.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { View, Text, Pressable, StyleSheet } from 'react-native'; -import { ClipboardPaste, Globe, QrCode } from '../../components/icons'; +import { ClipboardPaste, Globe, QrCode, ShieldCheck, X } from '../../components/icons'; import { spacing, typography, type ThemePalette } from '../../theme/tokens'; import { useColors } from '../../theme/colors'; import { Button, Input } from '../../components'; @@ -16,6 +16,16 @@ interface ServerStepProps { /** Set when we got here because discovery came back empty for this domain. */ failedDomain?: string | null; notice?: { title: string; detail?: string } | null; + /** + * TLS client certificate for a reverse proxy that asks for one (#3). + * Undefined where the platform has no certificate store to pick from. + */ + clientCert?: { + alias: string | null; + busy: boolean; + onPick: () => void; + onClear: () => void; + }; } /** @@ -31,6 +41,7 @@ export default function ServerStep({ onPaste, failedDomain, notice, + clientCert, }: ServerStepProps) { const c = useColors(); const styles = React.useMemo(() => makeStyles(c), [c]); @@ -82,6 +93,38 @@ export default function ServerStep({ {t('login.mobile.server_paste_hint', 'Have a sign-in link instead? Paste it here.')} + {clientCert ? ( + + + + {clientCert.alias + ? t('login.mobile.server_cert_active', 'Signing in with the client certificate "{alias}". Tap to choose another.', { alias: clientCert.alias }) + : t('login.mobile.server_cert_hint', 'Does your server ask for a client certificate? Pick one installed on this phone.')} + + + ) : null} + {clientCert?.alias ? ( + + + + {t('login.mobile.server_cert_clear', 'Sign in without the certificate')} + + + ) : null} ); From 1c9bbcc3831a19afdbe7f52d17b269dcf9e7c2e3 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Mon, 19 Oct 2026 13:16:57 +0200 Subject: [PATCH 259/334] chore: update version number to 0.1.83 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 59e55c1f..660b5ae0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.82 +0.1.83 diff --git a/package-lock.json b/package-lock.json index 3a7f58cd..ef69c7ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.82", + "version": "0.1.83", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.82", + "version": "0.1.83", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 94d88fd7..a4c2a8bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.82", + "version": "0.1.83", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From 341cdb0e61f28b3581752608c01efe723d087e59 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Tue, 20 Oct 2026 12:56:44 +0200 Subject: [PATCH 260/334] feat: build a Google Play bundle alongside the GitHub APK The app now has two product flavors. `github` is the sideloaded APK as before, with the same package name and its in-app updater. `play` is a bundle for Google Play as `org.bulwarkmail.mobile`, without the updater or the installer permission, since Play forbids apps updating themselves. - `release-android.yml` builds the Play bundle in a second job, checks its signer, package name and permissions, and keeps it as an artifact. With `PLAY_SERVICE_ACCOUNT_JSON` set it uploads it to internal testing. - The Play flavor uses the new Firebase project; the GitHub flavor keeps the old one until the app re-registers its token when it changes. - Widget clicks and the device-sync account type follow the package name. - Photos come from the system picker without asking for media access, and the unused microphone permission from expo-camera is removed. - A manual workflow run without a tag no longer replaces the APK of the latest release. --- .github/workflows/release-android.yml | 163 +++++++++++++++++- android/app/build.gradle | 42 ++++- android/app/google-services.json | 16 +- android/app/src/github/AndroidManifest.xml | 6 + android/app/src/github/google-services.json | 48 ++++++ android/app/src/main/AndroidManifest.xml | 72 ++++---- .../bulwarkmobile/BulwarkBuildModule.kt | 18 ++ .../bulwarkmobile/BulwarkFcmPackage.kt | 1 + docs/android-release.md | 44 ++++- .../__tests__/platform-capabilities.test.ts | 35 ++++ src/lib/platform-capabilities.ts | 23 ++- src/screens/ComposeScreen.tsx | 20 +-- src/screens/ContactFormScreen.tsx | 10 +- 13 files changed, 405 insertions(+), 93 deletions(-) create mode 100644 android/app/src/github/AndroidManifest.xml create mode 100644 android/app/src/github/google-services.json create mode 100644 android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkBuildModule.kt create mode 100644 src/lib/__tests__/platform-capabilities.test.ts diff --git a/.github/workflows/release-android.yml b/.github/workflows/release-android.yml index 50348c7f..2e9e6a44 100644 --- a/.github/workflows/release-android.yml +++ b/.github/workflows/release-android.yml @@ -1,4 +1,4 @@ -name: Release Android APK +name: Release Android on: release: @@ -137,7 +137,7 @@ jobs: ORG_GRADLE_PROJECT_BULWARK_RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} run: | chmod +x ./gradlew - ./gradlew assembleRelease --no-daemon + ./gradlew assembleGithubRelease --no-daemon - name: Put apksigner on the PATH run: | @@ -188,7 +188,7 @@ jobs: # Oldest signer first: apksigner uses it for v2, and the rotated key # for v3 from API 28. Without --rotation-min-sdk-version it would # only rotate on Android 13+ (v3.1) and leave 9-12L on the debug key. - APK=android/app/build/outputs/apk/release/app-release.apk + APK=android/app/build/outputs/apk/github/release/app-github-release.apk apksigner sign \ --v4-signing-enabled false \ --ks android/app/debug.keystore --ks-key-alias androiddebugkey \ @@ -210,7 +210,7 @@ jobs: ROTATED: ${{ steps.rotation.outputs.rotated }} run: | set -euo pipefail - APK=android/app/build/outputs/apk/release/app-release.apk + APK=android/app/build/outputs/apk/github/release/app-github-release.apk apksigner verify --verbose --print-certs "$APK" # build-tools 36 prints "Signer #1 certificate SHA-256 digest: …", # 37 prints "V3.0 Signer: certificate …" and "V2 Signer: …". @@ -248,12 +248,12 @@ jobs: run: | set -euo pipefail VERSION=$(cat VERSION | tr -d '[:space:]') - SRC=android/app/build/outputs/apk/release/app-release.apk + SRC=android/app/build/outputs/apk/github/release/app-github-release.apk DEST="bulwark-mobile-${VERSION}-${GITHUB_SHA::7}.apk" cp "$SRC" "$DEST" sha256sum "$DEST" > "$DEST.sha256" - MAPPING_SRC=android/app/build/outputs/mapping/release/mapping.txt + MAPPING_SRC=android/app/build/outputs/mapping/githubRelease/mapping.txt MAPPING="" if [ -f "$MAPPING_SRC" ]; then MAPPING="bulwark-mobile-${VERSION}-${GITHUB_SHA::7}-mapping.txt" @@ -299,12 +299,12 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | + # A manual run with no tag only builds: it must not replace the APK + # of the latest release with one from another commit. if [ "${{ github.event_name }}" = "release" ]; then TAG="${{ github.event.release.tag_name }}" - elif [ -n "${{ github.event.inputs.attach_to_release_tag }}" ]; then - TAG="${{ github.event.inputs.attach_to_release_tag }}" else - TAG=$(gh release list --limit 1 --json tagName --jq '.[0].tagName' || true) + TAG="${{ github.event.inputs.attach_to_release_tag }}" fi echo "tag=$TAG" >> "$GITHUB_OUTPUT" if [ -n "$TAG" ]; then @@ -319,3 +319,148 @@ jobs: with: tag_name: ${{ steps.target_tag.outputs.tag }} files: ${{ steps.artifact.outputs.files }} + + # The Google Play bundle (docs/android-release.md, "Google Play"). It is + # signed with the release key, which Play knows as the upload key; Play + # re-signs what it delivers with its own app signing key. A separate job so + # the GitHub APK isn't held up by the three-ABI native build. + play-bundle: + runs-on: ubuntu-latest + timeout-minutes: 75 + permissions: + contents: read + env: + PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }} + steps: + - name: Require the release signing secrets + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: | + missing="" + [ -n "$KEYSTORE_BASE64" ] || missing="$missing ANDROID_KEYSTORE_BASE64" + [ -n "$KEYSTORE_PASSWORD" ] || missing="$missing ANDROID_KEYSTORE_PASSWORD" + [ -n "$KEY_ALIAS" ] || missing="$missing ANDROID_KEY_ALIAS" + [ -n "$KEY_PASSWORD" ] || missing="$missing ANDROID_KEY_PASSWORD" + if [ -n "$missing" ]; then + echo "::error title=Release signing is not configured::Missing repository secrets:$missing. See docs/android-release.md." + exit 1 + fi + + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + cache: gradle + + - uses: android-actions/setup-android@v3 + with: + packages: platform-tools + + - name: Install JS dependencies + run: npm ci + + - name: Decode and check the upload key + id: key + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + run: | + set -euo pipefail + KEYSTORE="$RUNNER_TEMP/release.keystore" + CERT="$RUNNER_TEMP/release-cert.der" + echo "$KEYSTORE_BASE64" | base64 -d > "$KEYSTORE" + keytool -exportcert -keystore "$KEYSTORE" -alias "$KEY_ALIAS" \ + -storepass:env KEYSTORE_PASSWORD -file "$CERT" + CERT_SHA256=$(sha256sum "$CERT" | cut -d' ' -f1) + if [ "$CERT_SHA256" = "$DEBUG_CERT_SHA256" ]; then + echo "::error title=Upload key is a debug key::ANDROID_KEYSTORE_BASE64 holds the Android debug key." + exit 1 + fi + echo "store_file=$KEYSTORE" >> "$GITHUB_OUTPUT" + echo "cert_sha256=$CERT_SHA256" >> "$GITHUB_OUTPUT" + + # Play splits a bundle per ABI, so the extra architectures cost a phone + # nothing and let 32-bit ARM phones and x86_64 Chromebooks install it. + - name: Build the Play bundle + working-directory: android + env: + ORG_GRADLE_PROJECT_BULWARK_RELEASE_STORE_FILE: ${{ steps.key.outputs.store_file }} + ORG_GRADLE_PROJECT_BULWARK_RELEASE_STORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + ORG_GRADLE_PROJECT_BULWARK_RELEASE_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + ORG_GRADLE_PROJECT_BULWARK_RELEASE_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: | + chmod +x ./gradlew + ./gradlew bundlePlayRelease --no-daemon -PreactNativeArchitectures=armeabi-v7a,arm64-v8a,x86_64 + + # Play rejects a bundle signed with another key than the registered + # upload key, and its policies reject a self-updater and the media + # permission. Catch all three here instead of in the Play Console. + - name: Check the bundle + env: + RELEASE_CERT_SHA256: ${{ steps.key.outputs.cert_sha256 }} + run: | + set -euo pipefail + AAB=android/app/build/outputs/bundle/playRelease/app-play-release.aab + SIGNER=$(keytool -printcert -jarfile "$AAB" | sed -n 's/^[[:space:]]*SHA256: //p' | head -n 1 | tr -d ':' | tr 'A-F' 'a-f') + echo "Bundle signer SHA-256: $SIGNER" + if [ "$SIGNER" != "$RELEASE_CERT_SHA256" ]; then + echo "::error title=Wrong bundle signer::Expected the upload key $RELEASE_CERT_SHA256, got '$SIGNER'." + exit 1 + fi + MANIFEST=$(unzip -p "$AAB" base/manifest/AndroidManifest.xml | tr -c '[:print:]' '\n') + if ! grep -q 'org.bulwarkmail.mobile' <<< "$MANIFEST"; then + echo "::error title=Wrong package::The bundle is not org.bulwarkmail.mobile." + exit 1 + fi + for permission in REQUEST_INSTALL_PACKAGES READ_MEDIA_IMAGES READ_MEDIA_VIDEO RECORD_AUDIO; do + if grep -q "android.permission.$permission" <<< "$MANIFEST"; then + echo "::error title=Permission not allowed on Play::The Play bundle declares $permission." + exit 1 + fi + done + + - name: Name the bundle and R8 mapping with version + id: artifact + run: | + set -euo pipefail + VERSION=$(cat VERSION | tr -d '[:space:]') + AAB="bulwark-mobile-play-${VERSION}-${GITHUB_SHA::7}.aab" + MAPPING="bulwark-mobile-play-${VERSION}-${GITHUB_SHA::7}-mapping.txt" + cp android/app/build/outputs/bundle/playRelease/app-play-release.aab "$AAB" + cp android/app/build/outputs/mapping/playRelease/mapping.txt "$MAPPING" + echo "aab=$AAB" >> "$GITHUB_OUTPUT" + echo "mapping=$MAPPING" >> "$GITHUB_OUTPUT" + + - uses: actions/upload-artifact@v4 + with: + name: ${{ steps.artifact.outputs.aab }} + path: | + ${{ steps.artifact.outputs.aab }} + ${{ steps.artifact.outputs.mapping }} + if-no-files-found: error + + # Runs once PLAY_SERVICE_ACCOUNT_JSON is set. Play only accepts API + # uploads after the first bundle was uploaded by hand in the Console, and + # releases of an app that was never published must be drafts; switch + # `status` to `completed` after the first production release. + - name: Upload to Google Play (internal testing, draft) + if: env.PLAY_SERVICE_ACCOUNT_JSON != '' + uses: r0adkll/upload-google-play@v1 + with: + serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }} + packageName: org.bulwarkmail.mobile + releaseFiles: ${{ steps.artifact.outputs.aab }} + mappingFile: ${{ steps.artifact.outputs.mapping }} + track: internal + status: draft diff --git a/android/app/build.gradle b/android/app/build.gradle index ad476faf..1c101acd 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -90,8 +90,8 @@ react { /* Variants */ // The list of variants to that are debuggable. For those we're going to // skip the bundling of the JS bundle and the assets. By default is just 'debug'. - // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. - // debuggableVariants = ["liteDebug", "prodDebug"] + // The distribution flavors (github, play) need their debug variants listed. + debuggableVariants = ["githubDebug", "playDebug"] /* Bundling */ // A list containing the node command and its flags. Default is just 'node'. @@ -154,13 +154,39 @@ android { versionCode appVersionCode.toInteger() versionName appVersionName - // Android account type of device sync (#34), derived from the package - // name so a later rename needs no other change. The XML in res/xml and - // the Kotlin in sync/ read it as @string/device_sync_account_type. - resValue "string", "device_sync_account_type", "${applicationId}.account" - buildConfigField "String", "REACT_NATIVE_RELEASE_LEVEL", "\"${findProperty('reactNativeReleaseLevel') ?: 'stable'}\"" } + + /** + * Where the build is distributed. The two are separate apps on a phone: + * + * - github: the APK on GitHub Releases, sideloaded, updated by the in-app + * updater. Keeps the original package name so existing installs update. + * - play: the Google Play bundle. Play forbids an app updating itself + * outside Play, so this flavor has no installer permission and the JS + * hides the updater (BuildConfig.DISTRIBUTION, lib/platform-capabilities). + * + * See docs/android-release.md. + */ + flavorDimensions = ["distribution"] + productFlavors { + github { + dimension "distribution" + applicationId 'com.anonymous.bulwarkmobile' + buildConfigField "String", "DISTRIBUTION", '"github"' + } + play { + dimension "distribution" + applicationId 'org.bulwarkmail.mobile' + buildConfigField "String", "DISTRIBUTION", '"play"' + } + } + applicationVariants.all { variant -> + // Android account type of device sync (#34), derived from the package + // name of each flavor. The XML in res/xml and the Kotlin in sync/ read + // it as @string/device_sync_account_type. + variant.resValue "string", "device_sync_account_type", "${variant.applicationId}.account" + } signingConfigs { debug { storeFile file('debug.keystore') @@ -245,7 +271,7 @@ def checkReleaseSigning = tasks.register('checkReleaseSigning') { } } tasks.configureEach { task -> - if (task.name == 'preReleaseBuild') { + if (task.name ==~ /pre\w*ReleaseBuild/) { task.dependsOn(checkReleaseSigning) } } diff --git a/android/app/google-services.json b/android/app/google-services.json index 4517c7e1..63a45bc8 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -1,13 +1,13 @@ { "project_info": { - "project_number": "504486613721", - "project_id": "bulwark-mobile", - "storage_bucket": "bulwark-mobile.firebasestorage.app" + "project_number": "752109678859", + "project_id": "bulwark-20c72", + "storage_bucket": "bulwark-20c72.firebasestorage.app" }, "client": [ { "client_info": { - "mobilesdk_app_id": "1:504486613721:android:69a5d7ba9148fb14cb24c8", + "mobilesdk_app_id": "1:752109678859:android:952e8373ad508e780cf20e", "android_client_info": { "package_name": "com.anonymous.bulwarkmobile" } @@ -15,7 +15,7 @@ "oauth_client": [], "api_key": [ { - "current_key": "AIzaSyD4Zyu1BU4YNYy4SOK5te3EhSRHRGoMZno" + "current_key": "AIzaSyCpmCG1-Mnlgtme3NgtgRbmp7GztQeT4w4" } ], "services": { @@ -26,15 +26,15 @@ }, { "client_info": { - "mobilesdk_app_id": "1:504486613721:android:14c6a69aa49cac3ecb24c8", + "mobilesdk_app_id": "1:752109678859:android:2692d26569e5d9950cf20e", "android_client_info": { - "package_name": "com.bulwark.bulwarkmobile" + "package_name": "org.bulwarkmail.mobile" } }, "oauth_client": [], "api_key": [ { - "current_key": "AIzaSyD4Zyu1BU4YNYy4SOK5te3EhSRHRGoMZno" + "current_key": "AIzaSyCpmCG1-Mnlgtme3NgtgRbmp7GztQeT4w4" } ], "services": { diff --git a/android/app/src/github/AndroidManifest.xml b/android/app/src/github/AndroidManifest.xml new file mode 100644 index 00000000..747e48e7 --- /dev/null +++ b/android/app/src/github/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + diff --git a/android/app/src/github/google-services.json b/android/app/src/github/google-services.json new file mode 100644 index 00000000..4517c7e1 --- /dev/null +++ b/android/app/src/github/google-services.json @@ -0,0 +1,48 @@ +{ + "project_info": { + "project_number": "504486613721", + "project_id": "bulwark-mobile", + "storage_bucket": "bulwark-mobile.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:504486613721:android:69a5d7ba9148fb14cb24c8", + "android_client_info": { + "package_name": "com.anonymous.bulwarkmobile" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyD4Zyu1BU4YNYy4SOK5te3EhSRHRGoMZno" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:504486613721:android:14c6a69aa49cac3ecb24c8", + "android_client_info": { + "package_name": "com.bulwark.bulwarkmobile" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyD4Zyu1BU4YNYy4SOK5te3EhSRHRGoMZno" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5d0f74d7..71324ad2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,9 +2,13 @@ - + - + + + + @@ -110,224 +114,224 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkBuildModule.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkBuildModule.kt new file mode 100644 index 00000000..f6cbde75 --- /dev/null +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkBuildModule.kt @@ -0,0 +1,18 @@ +package com.anonymous.bulwarkmobile + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule + +/** + * Facts about this build that JS can't see on its own. `distribution` is the + * product flavor (app/build.gradle): "github" for the sideloaded APK with its + * own updater, "play" for the Google Play bundle, which must not update + * itself (lib/platform-capabilities). + */ +class BulwarkBuildModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { + override fun getName() = "BulwarkBuild" + + override fun getConstants(): Map = mapOf( + "distribution" to BuildConfig.DISTRIBUTION, + ) +} diff --git a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt index 71cb797a..73b9ded0 100644 --- a/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt +++ b/android/app/src/main/java/com/anonymous/bulwarkmobile/BulwarkFcmPackage.kt @@ -16,6 +16,7 @@ class BulwarkFcmPackage : ReactPackage { add(BulwarkDeviceSyncModule(reactContext)) add(BulwarkWidgetsModule(reactContext)) add(BulwarkPdfModule(reactContext)) + add(BulwarkBuildModule(reactContext)) // The bridge registers React Native's own module under this name. if (ReactNativeNewArchitectureFeatureFlags.enableBridgelessArchitecture()) { add(BulwarkHeadlessJsTaskSupportModule(reactContext)) diff --git a/docs/android-release.md b/docs/android-release.md index 3e5769e3..802e5ae2 100644 --- a/docs/android-release.md +++ b/docs/android-release.md @@ -3,7 +3,8 @@ The release APK is built by [.github/workflows/release-android.yml](../.github/workflows/release-android.yml) on an Ubuntu runner whenever a GitHub release is published, and attached to that -release. Installed apps pick it up through the in-app updater. +release. Installed apps pick it up through the in-app updater. The same workflow +builds the Google Play bundle; see [Google Play](#google-play). Every APK up to and including 0.1.62 was signed with `android/app/debug.keystore`, the debug key that ships with the React Native template. Its password is @@ -199,6 +200,42 @@ Once you delete it, every APK is signed with the release key alone: the update. Those users have to uninstall, reinstall and sign in again, and they lose local data. Afterwards their devices trust only the release key. +## Google Play + +The app has two product flavors (`android/app/build.gradle`). They install +side by side as separate apps: + +| Flavor | Package | Output | Updated by | Firebase project | +|---|---|---|---|---| +| `github` | `com.anonymous.bulwarkmobile` | APK on GitHub Releases | in-app updater | `bulwark-mobile` (`src/github/google-services.json`) | +| `play` | `org.bulwarkmail.mobile` | AAB for Google Play | Google Play | `bulwark-20c72` (`google-services.json`) | + +Play forbids an app updating itself outside Play, so the `play` flavor has no +`REQUEST_INSTALL_PACKAGES` (only `src/github/AndroidManifest.xml` declares it) +and `BuildConfig.DISTRIBUTION` is `"play"`, which the `BulwarkBuild` native +module hands to JS. `supportsSideloadUpdates` in `src/lib/platform-capabilities.ts` +is false there, which hides the updater. Neither flavor declares +`READ_MEDIA_IMAGES`: photos come from the system photo picker, and Play's photo +and video policy rejects the permission for that use. `RECORD_AUDIO`, which +expo-camera adds, is removed as well: QR scanning never records audio. + +Play App Signing holds the key that signs what Play delivers (generated by +Google, certificate SHA-256 `7e:e1:fd:35:…:91:fa:e3`). The workflow signs the +bundle with our release key, which Play registered as the upload key with the +first upload. The two flavors therefore have different signers too. + +The `play-bundle` job builds `bundlePlayRelease` for arm64-v8a, armeabi-v7a and +x86_64 (Play splits the bundle per ABI), checks the signer, the package name and +the permissions, and keeps the AAB and its R8 mapping as a workflow artifact. +With the `PLAY_SERVICE_ACCOUNT_JSON` secret set (vault: "Google Play – service +account bulwark-google-play-api") it also uploads the bundle to the internal +testing track as a draft. Play only accepts API uploads once a first bundle has +been uploaded by hand in the Play Console. + +The `github` flavor stays on the old Firebase project until the app registers +its new FCM token with the relay when the token changes; moving it before that +would silence push for every updated install. + ## Local release builds CI signs releases, so you normally don't need the release key on your machine. @@ -206,7 +243,8 @@ For a local release build, for profiling or checking R8, opt in to the debug key ```bash cd android -./gradlew assembleRelease -PallowDebugSignedRelease=true +./gradlew assembleGithubRelease -PallowDebugSignedRelease=true +./gradlew bundlePlayRelease -PallowDebugSignedRelease=true ``` ```powershell @@ -252,7 +290,7 @@ retrace bulwark-mobile-0.1.63-abc1234-mapping.txt stacktrace.txt `retrace` is in the Android SDK command-line tools (`cmdline-tools/latest/bin`). A local build writes its mapping to -`android/app/build/outputs/mapping/release/mapping.txt`. +`android/app/build/outputs/mapping/Release/mapping.txt`. ## versionCode diff --git a/src/lib/__tests__/platform-capabilities.test.ts b/src/lib/__tests__/platform-capabilities.test.ts new file mode 100644 index 00000000..bc739df4 --- /dev/null +++ b/src/lib/__tests__/platform-capabilities.test.ts @@ -0,0 +1,35 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; + +async function load(os: string, nativeModules: Record) { + vi.resetModules(); + vi.doMock('react-native', () => ({ Platform: { OS: os }, NativeModules: nativeModules })); + return import('../platform-capabilities'); +} + +afterEach(() => { + vi.doUnmock('react-native'); +}); + +describe('supportsSideloadUpdates', () => { + it('is on for the GitHub build', async () => { + const caps = await load('android', { BulwarkBuild: { distribution: 'github' } }); + expect(caps.androidDistribution).toBe('github'); + expect(caps.supportsSideloadUpdates).toBe(true); + }); + + it('is off for the Play build, which Play updates', async () => { + const caps = await load('android', { BulwarkBuild: { distribution: 'play' } }); + expect(caps.androidDistribution).toBe('play'); + expect(caps.supportsSideloadUpdates).toBe(false); + }); + + it('treats a build without the module as the GitHub build', async () => { + const caps = await load('android', {}); + expect(caps.supportsSideloadUpdates).toBe(true); + }); + + it('is off on iOS', async () => { + const caps = await load('ios', {}); + expect(caps.supportsSideloadUpdates).toBe(false); + }); +}); diff --git a/src/lib/platform-capabilities.ts b/src/lib/platform-capabilities.ts index 60489183..5d548809 100644 --- a/src/lib/platform-capabilities.ts +++ b/src/lib/platform-capabilities.ts @@ -1,19 +1,28 @@ // Single source of truth for "this feature only exists on some platforms", so // the UI, the stores and the native bridges all agree on what is reachable. -import { Platform } from 'react-native'; +import { NativeModules, Platform } from 'react-native'; + +/** + * Where this Android build is distributed: the product flavor in + * android/app/build.gradle, read from the BulwarkBuild native module. A build + * without the module (older native code) is the sideloaded GitHub APK. + */ +export const androidDistribution: 'github' | 'play' = + Platform.OS === 'android' && NativeModules.BulwarkBuild?.distribution === 'play' ? 'play' : 'github'; /** * In-app sideload updates: download the release APK from GitHub and hand it to * the system package installer (see `lib/install-update`). * - * Android only. App Store Review guideline 2.5.2 forbids an iOS app from - * downloading and installing executable code, and there is no iOS equivalent of - * the installer intent anyway - iOS builds are updated through TestFlight or - * the App Store. The whole updater surface (banner, settings pane, launch-time - * check) is hidden when this is false. + * Only the GitHub build on Android. App Store Review guideline 2.5.2 forbids an + * iOS app from downloading and installing executable code, and Google Play + * forbids an app updating itself outside Play; the Play build also lacks the + * installer permission. iOS builds are updated through TestFlight or the App + * Store, the Play build through Play. The whole updater surface (banner, + * settings pane, launch-time check) is hidden when this is false. */ -export const supportsSideloadUpdates = Platform.OS === 'android'; +export const supportsSideloadUpdates = Platform.OS === 'android' && androidDistribution === 'github'; /** * Device sync: address books and calendars in Android's Contacts and Calendar diff --git a/src/screens/ComposeScreen.tsx b/src/screens/ComposeScreen.tsx index a122c7f1..51085d7c 100644 --- a/src/screens/ComposeScreen.tsx +++ b/src/screens/ComposeScreen.tsx @@ -1625,14 +1625,8 @@ export default function ComposeScreen({ route, navigation }: Props) { }, []); const pickPhotoAttachments = async () => { - const perm = await ImagePicker.requestMediaLibraryPermissionsAsync(); - if (!perm.granted) { - Alert.alert( - t('email_composer.attach', 'Attach'), - t('email_composer.permission_photos', 'Photo library permission is required to attach images.'), - ); - return; - } + // The system photo picker needs no media permission (and Play rejects + // READ_MEDIA_IMAGES for picking a photo now and then). const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images', 'videos'], allowsMultipleSelection: true, @@ -1729,14 +1723,8 @@ export default function ComposeScreen({ route, navigation }: Props) { }; const insertInlineImage = async () => { - const perm = await ImagePicker.requestMediaLibraryPermissionsAsync(); - if (!perm.granted) { - Alert.alert( - t('email_composer.attach', 'Attach'), - t('email_composer.permission_photos', 'Photo library permission is required to attach images.'), - ); - return; - } + // The system photo picker needs no media permission (and Play rejects + // READ_MEDIA_IMAGES for picking a photo now and then). const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsMultipleSelection: true, diff --git a/src/screens/ContactFormScreen.tsx b/src/screens/ContactFormScreen.tsx index 5e28c384..bd2586b3 100644 --- a/src/screens/ContactFormScreen.tsx +++ b/src/screens/ContactFormScreen.tsx @@ -889,14 +889,8 @@ export default function ContactFormScreen() { : [form.given, form.surname].filter(Boolean).join(' ').trim() || form.full.trim(); const pickPhoto = async () => { - const perm = await ImagePicker.requestMediaLibraryPermissionsAsync(); - if (!perm.granted) { - Alert.alert( - t('contacts.form.photo_permission_title', 'Photo access needed'), - t('contacts.form.photo_permission_message', 'Grant photo library permission to pick a contact photo.'), - ); - return; - } + // The system photo picker needs no media permission (and Play rejects + // READ_MEDIA_IMAGES for picking a photo now and then). const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ['images'], allowsEditing: true, From ae0b76442a7841e91f3db1fa263bdb401b94be6a Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:28:41 +0200 Subject: [PATCH 261/334] test: add stable test ids to sign-in fields, tabs and message rows --- App.tsx | 5 +++++ src/screens/EmailListScreen.tsx | 1 + src/screens/login/PasswordStep.tsx | 2 ++ src/screens/login/ServerStep.tsx | 1 + 4 files changed, 9 insertions(+) diff --git a/App.tsx b/App.tsx index b1ab1f7a..15a8134a 100644 --- a/App.tsx +++ b/App.tsx @@ -297,6 +297,7 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps , tabBarBadge: inboxUnreadCount > 0 ? (inboxUnreadCount > 99 ? '99+' : inboxUnreadCount) : undefined, tabBarBadgeStyle: { @@ -320,6 +321,7 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps , tabBarItemStyle: hasCalendar ? undefined : disabledTabStyle, tabBarAccessibilityLabel: hasCalendar ? tabLabels.calendar : unavailable(tabLabels.calendar), @@ -335,6 +337,7 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps , tabBarItemStyle: hasContacts ? undefined : disabledTabStyle, tabBarAccessibilityLabel: hasContacts ? tabLabels.contacts : unavailable(tabLabels.contacts), @@ -350,6 +353,7 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps , tabBarItemStyle: hasFiles ? undefined : disabledTabStyle, tabBarAccessibilityLabel: hasFiles ? tabLabels.files : unavailable(tabLabels.files), @@ -364,6 +368,7 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps , }} > diff --git a/src/screens/EmailListScreen.tsx b/src/screens/EmailListScreen.tsx index 6389a1fd..e472bf2b 100644 --- a/src/screens/EmailListScreen.tsx +++ b/src/screens/EmailListScreen.tsx @@ -176,6 +176,7 @@ const EmailRow = React.memo(function EmailRow({ onPress={handlePress} onLongPress={handleLongPress} delayLongPress={300} + testID="email-row" > {unread && ( Date: Thu, 22 Oct 2026 00:28:41 +0200 Subject: [PATCH 262/334] ci: run maestro flows on the ios simulator --- .github/workflows/e2e-ios.yml | 167 ++++++++++++++++++++++++++++++++++ .maestro/launch.yaml | 21 +++++ .maestro/signed-in.yaml | 99 ++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 .github/workflows/e2e-ios.yml create mode 100644 .maestro/launch.yaml create mode 100644 .maestro/signed-in.yaml diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml new file mode 100644 index 00000000..e47923e5 --- /dev/null +++ b/.github/workflows/e2e-ios.yml @@ -0,0 +1,167 @@ +name: iOS simulator tests + +# Builds the app for the iOS Simulator (no signing needed), runs the Maestro +# flows in .maestro/ against it and uploads screenshots and a screen recording. +# +# The signed-in flow needs the E2E_SERVER and E2E_USERNAME variables and the +# E2E_PASSWORD secret (a test account on a server the runner can reach). +# Without them only the launch flow runs. + +on: + workflow_dispatch: + release: + types: [published] + +concurrency: + group: e2e-ios-${{ github.ref }} + cancel-in-progress: true + +jobs: + simulator: + runs-on: macos-latest + timeout-minutes: 75 + env: + EXPO_NO_TELEMETRY: "1" + OUT: ${{ github.workspace }}/e2e-output + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 17 + + - name: Cache CocoaPods + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/CocoaPods + ~/.cocoapods/repos + key: pods-${{ runner.os }}-${{ hashFiles('package-lock.json') }} + restore-keys: pods-${{ runner.os }}- + + - name: Install JS dependencies + run: npm ci + + - name: Generate the iOS project + run: npx expo prebuild --platform ios --clean + + - name: Build for the simulator + run: | + WORKSPACE=$(ls -d ios/*.xcworkspace | head -1) + SCHEME=$(basename "$WORKSPACE" .xcworkspace) + # Release bundles the JS into the app, so no Metro server is needed. + # arm64 only: the runner is Apple silicon, and a second simulator + # architecture would double the build time. + mkdir -p "$OUT" + if ! xcodebuild build \ + -workspace "$WORKSPACE" \ + -scheme "$SCHEME" \ + -configuration Release \ + -sdk iphonesimulator \ + -destination 'generic/platform=iOS Simulator' \ + -derivedDataPath build \ + ARCHS=arm64 \ + CODE_SIGNING_ALLOWED=NO \ + > "$OUT/xcodebuild.log" 2>&1; then + grep -E "error:|fatal error" "$OUT/xcodebuild.log" | head -n 50 || true + tail -n 80 "$OUT/xcodebuild.log" + exit 1 + fi + APP=$(ls -d build/Build/Products/Release-iphonesimulator/*.app | head -1) + echo "APP=$APP" >> "$GITHUB_ENV" + echo "Built $APP" + + - name: Upload the simulator app + uses: actions/upload-artifact@v4 + with: + name: bulwark-mobile-simulator-${{ github.sha }} + path: build/Build/Products/Release-iphonesimulator/*.app + if-no-files-found: error + + - name: Boot a simulator + run: | + # The newest iPhone on the newest runtime the image ships. + UDID=$(xcrun simctl list devices available -j | jq -r ' + [.devices | to_entries[] + | select(.key | test("SimRuntime.iOS")) + | .value[] | select(.name | startswith("iPhone")) | .udid] | last') + if [ -z "$UDID" ] || [ "$UDID" = "null" ]; then + xcrun simctl list devices available + echo "::error::No iPhone simulator available" + exit 1 + fi + xcrun simctl boot "$UDID" + xcrun simctl bootstatus "$UDID" -b + xcrun simctl install "$UDID" "$APP" + echo "UDID=$UDID" >> "$GITHUB_ENV" + xcrun simctl list devices | grep "$UDID" + + - name: Install Maestro + run: | + curl -fsSL "https://get.maestro.mobile.dev" | bash + echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" + + - name: Run the flows + env: + SERVER: ${{ vars.E2E_SERVER }} + USERNAME: ${{ vars.E2E_USERNAME }} + PASSWORD: ${{ secrets.E2E_PASSWORD }} + # The XCTest driver takes a while to come up on a cold runner. + MAESTRO_DRIVER_STARTUP_TIMEOUT: "240000" + MAESTRO_CLI_NO_ANALYTICS: "1" + run: | + maestro --version + mkdir -p "$OUT/screenshots" + + xcrun simctl io "$UDID" recordVideo --codec=h264 --force "$OUT/run.mp4" & + RECORDER=$! + + ARGS=() + if [ -n "$SERVER" ] && [ -n "$USERNAME" ] && [ -n "$PASSWORD" ]; then + ARGS+=(-e "SERVER=$SERVER" -e "USERNAME=$USERNAME" -e "PASSWORD=$PASSWORD") + else + echo "::notice::E2E_SERVER, E2E_USERNAME or E2E_PASSWORD is not set - running the launch flow only." + ARGS+=(--exclude-tags signed-in) + fi + + # Screenshots land in the working directory, so run from the output + # folder. Debug output is left out on purpose: it logs typed text, + # including the password, and artifacts here are public. + set +e + (cd "$OUT/screenshots" && maestro --device "$UDID" test \ + --format junit --output "$OUT/report.xml" \ + "${ARGS[@]}" "$GITHUB_WORKSPACE/.maestro") + STATUS=$? + set -e + + kill -INT "$RECORDER" || true + wait "$RECORDER" || true + + # Newer Maestro versions write screenshots (and a screenshot of any + # failed step) under ~/.maestro/tests instead. Copy only the images: + # the JSON next to them holds the typed password. + find "$HOME/.maestro/tests" -name '*.png' -exec cp {} "$OUT/screenshots/" \; 2>/dev/null || true + ls -la "$OUT/screenshots" + exit $STATUS + + - name: Grab the app log on failure + if: failure() && env.UDID != '' + run: | + xcrun simctl spawn "$UDID" log show --last 10m --style compact \ + --predicate 'process CONTAINS "Bulwark"' > "$OUT/app.log" 2>&1 || true + tail -n 100 "$OUT/app.log" || true + + - name: Upload screenshots and recording + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-ios-${{ github.run_number }} + path: e2e-output/ + if-no-files-found: ignore diff --git a/.maestro/launch.yaml b/.maestro/launch.yaml new file mode 100644 index 00000000..857dfd3a --- /dev/null +++ b/.maestro/launch.yaml @@ -0,0 +1,21 @@ +# Fresh install: the app starts and shows the sign-in choices. +# Needs no server, so it also runs when the test account secrets are missing. +appId: org.bulwarkmail.mobile +name: Launch to sign-in +tags: + - smoke +--- +- launchApp: + clearState: true + clearKeychain: true +# A cold start of a release build on a CI simulator can be slow. +- extendedWaitUntil: + visible: "Enter server details manually" + timeout: 120000 +- takeScreenshot: 01-sign-in-choices +- tapOn: "Enter server details manually" +- extendedWaitUntil: + visible: + id: login-server-input + timeout: 10000 +- takeScreenshot: 02-server-step diff --git a/.maestro/signed-in.yaml b/.maestro/signed-in.yaml new file mode 100644 index 00000000..2997457d --- /dev/null +++ b/.maestro/signed-in.yaml @@ -0,0 +1,99 @@ +# Signs in with a password and walks the main screens. +# Run with: maestro test -e SERVER=... -e USERNAME=... -e PASSWORD=... .maestro/signed-in.yaml +appId: org.bulwarkmail.mobile +name: Sign in and browse +tags: + - signed-in +--- +- launchApp: + clearState: true + clearKeychain: true + # The calendar tab asks for notification permission for reminders; + # granting it up front keeps the system alert from covering the screen. + permissions: + notifications: allow +- extendedWaitUntil: + visible: "Enter server details manually" + timeout: 120000 +- tapOn: "Enter server details manually" + +- tapOn: + id: login-server-input +- inputText: ${SERVER} +- pressKey: Enter + +# Servers that offer OAuth land on a confirm step first; switch to the +# password form there. Others go straight to it. +- extendedWaitUntil: + visible: "Sign in with a password instead|Email or username" + timeout: 30000 +- runFlow: + when: + visible: "Sign in with a password instead" + commands: + - tapOn: "Sign in with a password instead" + +- tapOn: + id: login-username-input +- eraseText +- inputText: ${USERNAME} +- tapOn: + id: login-password-input +- inputText: ${PASSWORD} +- pressKey: Enter + +- extendedWaitUntil: + visible: + id: tab-mail + timeout: 60000 +- runFlow: + when: + visible: "Not now" + commands: + - tapOn: "Not now" + +- extendedWaitUntil: + visible: + id: email-row + timeout: 60000 +- takeScreenshot: 10-inbox + +- tapOn: + id: email-row + index: 0 +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: 11-message +- tapOn: "Back" +- extendedWaitUntil: + visible: + id: email-row + timeout: 10000 + +- tapOn: + id: tab-calendar +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: 20-calendar + +- tapOn: + id: tab-contacts +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: 30-contacts + +- tapOn: + id: tab-files +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: 40-files + +- tapOn: + id: tab-settings +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: 50-settings + +# Still alive after the walk: the tab bar is there and the app didn't crash. +- assertVisible: + id: tab-mail From 2078388e6fa0262fdf2c00454724755204e7dd1d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:49:38 +0200 Subject: [PATCH 263/334] ci: log why the maestro ios driver fails to start and allow reusing a built app --- .github/workflows/e2e-ios.yml | 67 +++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index e47923e5..1fc8e006 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -9,6 +9,11 @@ name: iOS simulator tests on: workflow_dispatch: + inputs: + app_run_id: + description: "Reuse the simulator app from this earlier run instead of building (run id)" + type: string + required: false release: types: [published] @@ -20,6 +25,9 @@ jobs: simulator: runs-on: macos-latest timeout-minutes: 75 + permissions: + contents: read + actions: read env: EXPO_NO_TELEMETRY: "1" OUT: ${{ github.workspace }}/e2e-output @@ -28,6 +36,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 + if: inputs.app_run_id == '' with: node-version: 20 cache: npm @@ -38,6 +47,7 @@ jobs: java-version: 17 - name: Cache CocoaPods + if: inputs.app_run_id == '' uses: actions/cache@v4 with: path: | @@ -47,12 +57,15 @@ jobs: restore-keys: pods-${{ runner.os }}- - name: Install JS dependencies + if: inputs.app_run_id == '' run: npm ci - name: Generate the iOS project + if: inputs.app_run_id == '' run: npx expo prebuild --platform ios --clean - name: Build for the simulator + if: inputs.app_run_id == '' run: | WORKSPACE=$(ls -d ios/*.xcworkspace | head -1) SCHEME=$(basename "$WORKSPACE" .xcworkspace) @@ -74,17 +87,33 @@ jobs: tail -n 80 "$OUT/xcodebuild.log" exit 1 fi - APP=$(ls -d build/Build/Products/Release-iphonesimulator/*.app | head -1) - echo "APP=$APP" >> "$GITHUB_ENV" - echo "Built $APP" + # Packed as a tar: artifacts drop the executable bit, which would + # leave a reused app unable to launch. + tar -C build/Build/Products/Release-iphonesimulator -czf "$RUNNER_TEMP/simulator-app.tar.gz" BulwarkMobile.app - name: Upload the simulator app + if: inputs.app_run_id == '' uses: actions/upload-artifact@v4 with: - name: bulwark-mobile-simulator-${{ github.sha }} - path: build/Build/Products/Release-iphonesimulator/*.app + name: simulator-app + path: ${{ runner.temp }}/simulator-app.tar.gz if-no-files-found: error + - name: Download the simulator app from an earlier run + if: inputs.app_run_id != '' + uses: actions/download-artifact@v4 + with: + name: simulator-app + run-id: ${{ inputs.app_run_id }} + github-token: ${{ github.token }} + path: ${{ runner.temp }} + + - name: Unpack the simulator app + run: | + mkdir -p "$RUNNER_TEMP/app" + tar -C "$RUNNER_TEMP/app" -xzf "$RUNNER_TEMP/simulator-app.tar.gz" + echo "APP=$RUNNER_TEMP/app/BulwarkMobile.app" >> "$GITHUB_ENV" + - name: Boot a simulator run: | # The newest iPhone on the newest runtime the image ships. @@ -114,7 +143,7 @@ jobs: USERNAME: ${{ vars.E2E_USERNAME }} PASSWORD: ${{ secrets.E2E_PASSWORD }} # The XCTest driver takes a while to come up on a cold runner. - MAESTRO_DRIVER_STARTUP_TIMEOUT: "240000" + MAESTRO_DRIVER_STARTUP_TIMEOUT: "600000" MAESTRO_CLI_NO_ANALYTICS: "1" run: | maestro --version @@ -131,14 +160,30 @@ jobs: ARGS+=(--exclude-tags signed-in) fi - # Screenshots land in the working directory, so run from the output - # folder. Debug output is left out on purpose: it logs typed text, - # including the password, and artifacts here are public. + # Warm-up with the launch flow alone. It types nothing, so its debug + # log holds no secrets and can be printed when the driver won't start. + # The real run below keeps debug output off: it would log the password, + # and artifacts here are public. set +e + touch "$RUNNER_TEMP/flows-started" (cd "$OUT/screenshots" && maestro --device "$UDID" test \ - --format junit --output "$OUT/report.xml" \ - "${ARGS[@]}" "$GITHUB_WORKSPACE/.maestro") + --debug-output "$RUNNER_TEMP/maestro-warmup" "$GITHUB_WORKSPACE/.maestro/launch.yaml") STATUS=$? + if [ $STATUS -ne 0 ]; then + echo "::group::Maestro warm-up log" + find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec tail -n 300 {} \; + echo "::endgroup::" + echo "::group::xcodebuild logs of the driver" + find "$HOME/Library/Logs" "$HOME/.maestro" "$TMPDIR" -name '*.log' -newer "$RUNNER_TEMP/flows-started" 2>/dev/null \ + | grep -i -E 'xctest|xcodebuild|maestro' | grep -v maestro-warmup | head -5 \ + | while read -r f; do echo "== $f"; tail -n 80 "$f"; done + echo "::endgroup::" + else + (cd "$OUT/screenshots" && maestro --device "$UDID" test \ + --format junit --output "$OUT/report.xml" \ + "${ARGS[@]}" "$GITHUB_WORKSPACE/.maestro") + STATUS=$? + fi set -e kill -INT "$RECORDER" || true From 3748a64b10600462b9247b4bca86bb0e2e8100c0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 01:15:24 +0200 Subject: [PATCH 264/334] ci: turn off the ios slide-to-type tip that swallows typed text in simulator tests --- .github/workflows/e2e-ios.yml | 4 ++++ .maestro/signed-in.yaml | 1 + .maestro/subflows/dismiss-keyboard-tip.yaml | 12 ++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 .maestro/subflows/dismiss-keyboard-tip.yaml diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 1fc8e006..c2ad2070 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -128,6 +128,10 @@ jobs: fi xcrun simctl boot "$UDID" xcrun simctl bootstatus "$UDID" -b + # A fresh simulator shows the slide-to-type tip over the keyboard the + # first time a text field gets focus, and typed text goes nowhere. + xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences DidShowContinuousPathIntroduction -bool true + xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardContinuousPathEnabled -bool false xcrun simctl install "$UDID" "$APP" echo "UDID=$UDID" >> "$GITHUB_ENV" xcrun simctl list devices | grep "$UDID" diff --git a/.maestro/signed-in.yaml b/.maestro/signed-in.yaml index 2997457d..1f8ff7b1 100644 --- a/.maestro/signed-in.yaml +++ b/.maestro/signed-in.yaml @@ -19,6 +19,7 @@ tags: - tapOn: id: login-server-input +- runFlow: subflows/dismiss-keyboard-tip.yaml - inputText: ${SERVER} - pressKey: Enter diff --git a/.maestro/subflows/dismiss-keyboard-tip.yaml b/.maestro/subflows/dismiss-keyboard-tip.yaml new file mode 100644 index 00000000..ae0e82a2 --- /dev/null +++ b/.maestro/subflows/dismiss-keyboard-tip.yaml @@ -0,0 +1,12 @@ +# iOS shows a one-time slide-to-type tip over the keyboard on a fresh +# simulator. The workflow turns it off, but dismiss it if it still shows up. +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + visible: "Speed up your typing by sliding.*" + commands: + # The sign-in screen has its own Continue button; take the tip's. + - tapOn: + text: "Continue" + below: "Speed up your typing by sliding.*" From fa68b2eb53d559d0ff76731c74afe05adeca7dbc Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 01:28:13 +0200 Subject: [PATCH 265/334] ci: type a test value in the ios warm-up flow and log what the driver does --- .github/workflows/e2e-ios.yml | 10 ++++++++++ .maestro/launch.yaml | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index c2ad2070..a1089ca4 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -132,6 +132,12 @@ jobs: # first time a text field gets focus, and typed text goes nowhere. xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences DidShowContinuousPathIntroduction -bool true xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardContinuousPathEnabled -bool false + # Predictions, autocorrect and password AutoFill can swallow or + # rewrite what XCTest types. + xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardPrediction -bool false + xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardAutocorrection -bool false + xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardAutocapitalization -bool false + xcrun simctl spawn "$UDID" defaults write com.apple.WebUI AutoFillPasswords -bool false || true xcrun simctl install "$UDID" "$APP" echo "UDID=$UDID" >> "$GITHUB_ENV" xcrun simctl list devices | grep "$UDID" @@ -173,6 +179,10 @@ jobs: (cd "$OUT/screenshots" && maestro --device "$UDID" test \ --debug-output "$RUNNER_TEMP/maestro-warmup" "$GITHUB_WORKSPACE/.maestro/launch.yaml") STATUS=$? + echo "::group::Maestro warm-up log (typing)" + find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec grep -n -i -E "inputText|typeText|keyboard|input_text|Exception|error" {} \; | tail -n 120 + echo "::endgroup::" + maestro --device "$UDID" hierarchy > "$OUT/hierarchy-warmup.json" 2>/dev/null || true if [ $STATUS -ne 0 ]; then echo "::group::Maestro warm-up log" find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec tail -n 300 {} \; diff --git a/.maestro/launch.yaml b/.maestro/launch.yaml index 857dfd3a..bdef3056 100644 --- a/.maestro/launch.yaml +++ b/.maestro/launch.yaml @@ -1,5 +1,6 @@ # Fresh install: the app starts and shows the sign-in choices. # Needs no server, so it also runs when the test account secrets are missing. +# It types only a made-up address, so its debug log is safe to print. appId: org.bulwarkmail.mobile name: Launch to sign-in tags: @@ -18,4 +19,9 @@ tags: visible: id: login-server-input timeout: 10000 +- tapOn: + id: login-server-input +- runFlow: subflows/dismiss-keyboard-tip.yaml +- inputText: "mail.example.com" - takeScreenshot: 02-server-step +- assertVisible: "mail.example.com" From 273051e5f59369603cdc750801ca6e80b4f2d346 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 01:54:03 +0200 Subject: [PATCH 266/334] ci: run sign-in flows for every method on iphone and ipad simulators --- .github/actions/e2e-ios-leg/action.yml | 92 +++++ .github/workflows/e2e-ios.yml | 295 +++++++------- .maestro/launch.yaml | 20 +- .maestro/scripts/helper.js | 8 + .maestro/signed-in.yaml | 100 ----- .maestro/signin/01-password.yaml | 32 ++ .maestro/signin/02-password-errors.yaml | 48 +++ .maestro/signin/03-totp-password.yaml | 24 ++ .maestro/signin/04-oauth.yaml | 20 + .maestro/signin/05-oauth-cancel.yaml | 39 ++ .maestro/signin/06-webmail.yaml | 20 + .maestro/signin/07-webmail-totp.yaml | 22 + .maestro/signin/08-link-password.yaml | 17 + .maestro/signin/09-paste-password.yaml | 16 + .maestro/signin/10-link-totp.yaml | 15 + .maestro/signin/11-paste-totp.yaml | 15 + .maestro/signin/12-link-expired.yaml | 21 + .maestro/signin/13-link-used.yaml | 44 ++ .maestro/signin/14-scanner-camera.yaml | 32 ++ .maestro/signin/15-email-discovery.yaml | 23 ++ .maestro/signin/16-add-password.yaml | 30 ++ .maestro/signin/17-add-oauth.yaml | 42 ++ .maestro/signin/18-add-webmail.yaml | 43 ++ .maestro/signin/19-add-link.yaml | 30 ++ .maestro/subflows/allow-auth-session.yaml | 11 + .maestro/subflows/check-active-account.yaml | 16 + .maestro/subflows/confirm-link.yaml | 10 + .../subflows/finish-two-factor-if-asked.yaml | 12 + .maestro/subflows/inbox.yaml | 18 + .maestro/subflows/manual-server.yaml | 18 + .maestro/subflows/open-add-account.yaml | 17 + .maestro/subflows/password-form.yaml | 19 + .maestro/subflows/password-sign-in.yaml | 11 + .maestro/subflows/paste-link.yaml | 22 + .maestro/subflows/sign-out.yaml | 16 + .maestro/subflows/stalwart-login-page.yaml | 25 ++ .maestro/subflows/start.yaml | 20 + .maestro/subflows/two-factor.yaml | 21 + .maestro/subflows/type-secret.yaml | 17 + .maestro/subflows/type.yaml | 25 ++ .maestro/subflows/webmail-login-page.yaml | 31 ++ scripts/e2e/boot-sim.sh | 51 +++ scripts/e2e/failed-steps.mjs | 71 ++++ scripts/e2e/helper.mjs | 384 ++++++++++++++++++ scripts/e2e/run-flows.sh | 95 +++++ 45 files changed, 1696 insertions(+), 262 deletions(-) create mode 100644 .github/actions/e2e-ios-leg/action.yml create mode 100644 .maestro/scripts/helper.js delete mode 100644 .maestro/signed-in.yaml create mode 100644 .maestro/signin/01-password.yaml create mode 100644 .maestro/signin/02-password-errors.yaml create mode 100644 .maestro/signin/03-totp-password.yaml create mode 100644 .maestro/signin/04-oauth.yaml create mode 100644 .maestro/signin/05-oauth-cancel.yaml create mode 100644 .maestro/signin/06-webmail.yaml create mode 100644 .maestro/signin/07-webmail-totp.yaml create mode 100644 .maestro/signin/08-link-password.yaml create mode 100644 .maestro/signin/09-paste-password.yaml create mode 100644 .maestro/signin/10-link-totp.yaml create mode 100644 .maestro/signin/11-paste-totp.yaml create mode 100644 .maestro/signin/12-link-expired.yaml create mode 100644 .maestro/signin/13-link-used.yaml create mode 100644 .maestro/signin/14-scanner-camera.yaml create mode 100644 .maestro/signin/15-email-discovery.yaml create mode 100644 .maestro/signin/16-add-password.yaml create mode 100644 .maestro/signin/17-add-oauth.yaml create mode 100644 .maestro/signin/18-add-webmail.yaml create mode 100644 .maestro/signin/19-add-link.yaml create mode 100644 .maestro/subflows/allow-auth-session.yaml create mode 100644 .maestro/subflows/check-active-account.yaml create mode 100644 .maestro/subflows/confirm-link.yaml create mode 100644 .maestro/subflows/finish-two-factor-if-asked.yaml create mode 100644 .maestro/subflows/inbox.yaml create mode 100644 .maestro/subflows/manual-server.yaml create mode 100644 .maestro/subflows/open-add-account.yaml create mode 100644 .maestro/subflows/password-form.yaml create mode 100644 .maestro/subflows/password-sign-in.yaml create mode 100644 .maestro/subflows/paste-link.yaml create mode 100644 .maestro/subflows/sign-out.yaml create mode 100644 .maestro/subflows/stalwart-login-page.yaml create mode 100644 .maestro/subflows/start.yaml create mode 100644 .maestro/subflows/two-factor.yaml create mode 100644 .maestro/subflows/type-secret.yaml create mode 100644 .maestro/subflows/type.yaml create mode 100644 .maestro/subflows/webmail-login-page.yaml create mode 100644 scripts/e2e/boot-sim.sh create mode 100644 scripts/e2e/failed-steps.mjs create mode 100644 scripts/e2e/helper.mjs create mode 100644 scripts/e2e/run-flows.sh diff --git a/.github/actions/e2e-ios-leg/action.yml b/.github/actions/e2e-ios-leg/action.yml new file mode 100644 index 00000000..6be8ec0e --- /dev/null +++ b/.github/actions/e2e-ios-leg/action.yml @@ -0,0 +1,92 @@ +name: iOS simulator test leg +description: Boot a simulator, install the app and run one suite of Maestro flows + +inputs: + suite: + description: Folder under .maestro (signin or features) + required: true + name: + description: Leg name, used for the artifact + required: true + app_run_id: + description: Take the simulator app from this run instead of the current one + required: false + default: "" + flows: + description: Comma list of flow names to run instead of the whole suite + required: false + default: "" + playwright: + description: Install Playwright for the helper's webmail pairing + required: false + default: "false" + +runs: + using: composite + steps: + - name: Download the simulator app (this run) + if: inputs.app_run_id == '' + uses: actions/download-artifact@v4 + with: + name: simulator-app + path: ${{ runner.temp }} + + - name: Download the simulator app (earlier run) + if: inputs.app_run_id != '' + uses: actions/download-artifact@v4 + with: + name: simulator-app + run-id: ${{ inputs.app_run_id }} + github-token: ${{ github.token }} + path: ${{ runner.temp }} + + - name: Unpack the simulator app + shell: bash + run: | + mkdir -p "$RUNNER_TEMP/app" + tar -C "$RUNNER_TEMP/app" -xzf "$RUNNER_TEMP/simulator-app.tar.gz" + echo "APP=$RUNNER_TEMP/app/BulwarkMobile.app" >> "$GITHUB_ENV" + + - name: Boot a simulator + shell: bash + run: bash scripts/e2e/boot-sim.sh "$DEVICE" "$APPEARANCE" "$APP" + + - name: Install Maestro + shell: bash + run: | + curl -fsSL "https://get.maestro.mobile.dev" | bash + echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" + + - name: Install Playwright for the helper + if: inputs.playwright == 'true' + shell: bash + run: | + mkdir -p "$RUNNER_TEMP/pw" + cd "$RUNNER_TEMP/pw" + npm init -y >/dev/null + npm install --no-audit --no-fund playwright@1.59.1 >/dev/null + npx playwright install chromium >/dev/null + echo "PLAYWRIGHT_DIR=$RUNNER_TEMP/pw/node_modules" >> "$GITHUB_ENV" + + - name: Start the test helper + shell: bash + run: | + mkdir -p "$OUT/helper" + HELPER_SHOTS="$OUT/helper" nohup node scripts/e2e/helper.mjs > "$OUT/helper/helper.log" 2>&1 & + for i in $(seq 1 20); do + curl -fs http://127.0.0.1:8787/health >/dev/null && break + sleep 0.5 + done + curl -fs http://127.0.0.1:8787/health + + - name: Run the flows + shell: bash + run: bash scripts/e2e/run-flows.sh "${{ inputs.suite }}" "${{ inputs.flows }}" + + - name: Upload screenshots, recording and logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-ios-${{ inputs.name }} + path: e2e-output/ + if-no-files-found: ignore diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index a1089ca4..0f7c57c9 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -1,11 +1,20 @@ name: iOS simulator tests -# Builds the app for the iOS Simulator (no signing needed), runs the Maestro -# flows in .maestro/ against it and uploads screenshots and a screen recording. +# Builds the app for the iOS Simulator (no signing needed) and runs the +# Maestro flows in .maestro/ on iPhone and iPad simulators: # -# The signed-in flow needs the E2E_SERVER and E2E_USERNAME variables and the -# E2E_PASSWORD secret (a test account on a server the runner can reach). -# Without them only the launch flow runs. +# - signin: every sign-in method (.maestro/signin), on an iPhone and an iPad +# - features: the app feature by feature (.maestro/features), on iPhone and +# iPad in light and dark, plus the iPad in landscape (split layout) +# +# Each leg signs in with its own account on the test server (e2e-* accounts +# on VM 106, password E2E_PASSWORD), so parallel legs never share a mailbox. +# scripts/e2e/helper.mjs runs next to the simulator for what a flow can't do +# itself: TOTP codes, fresh webmail pairing links, mail sent by userb. +# +# Artifacts per leg: screenshots, a screen recording, the app log and a JUnit +# report. They are public; see scripts/e2e/run-flows.sh for how secrets are +# kept out of them. on: workflow_dispatch: @@ -14,6 +23,14 @@ on: description: "Reuse the simulator app from this earlier run instead of building (run id)" type: string required: false + legs: + description: "Legs to run: all, signin, features, or a comma list (signin-phone, features-tablet-dark, ...)" + type: string + default: all + flows: + description: "Only these flows (comma list of file names without .yaml); empty runs all" + type: string + required: false release: types: [published] @@ -21,33 +38,55 @@ concurrency: group: e2e-ios-${{ github.ref }} cancel-in-progress: true -jobs: - simulator: - runs-on: macos-latest - timeout-minutes: 75 - permissions: - contents: read - actions: read - env: - EXPO_NO_TELEMETRY: "1" - OUT: ${{ github.workspace }}/e2e-output +env: + EXPO_NO_TELEMETRY: "1" + MAESTRO_CLI_NO_ANALYTICS: "1" + # The XCTest driver takes minutes to come up on a cold runner. + MAESTRO_DRIVER_STARTUP_TIMEOUT: "600000" +jobs: + plan: + runs-on: ubuntu-latest + outputs: + signin: ${{ steps.plan.outputs.signin }} + features: ${{ steps.plan.outputs.features }} + steps: + - id: plan + env: + LEGS: ${{ inputs.legs || 'all' }} + run: | + node -e ' + const legs = (process.env.LEGS || "all").split(",").map((s) => s.trim()).filter(Boolean); + const want = (name, group) => legs.includes("all") || legs.includes(group) || legs.includes(name); + const signin = ["phone", "tablet"] + .map((device) => ({ name: `signin-${device}`, device })) + .filter((l) => want(l.name, "signin")); + const features = [ + { name: "features-phone-light", device: "phone", appearance: "light", orientation: "portrait", account: "e2e-phone-light" }, + { name: "features-phone-dark", device: "phone", appearance: "dark", orientation: "portrait", account: "e2e-phone-dark" }, + { name: "features-tablet-light", device: "tablet", appearance: "light", orientation: "portrait", account: "e2e-tablet-light" }, + { name: "features-tablet-dark", device: "tablet", appearance: "dark", orientation: "portrait", account: "e2e-tablet-dark" }, + { name: "features-tablet-landscape", device: "tablet", appearance: "light", orientation: "landscape", account: "e2e-tablet-land" }, + ].filter((l) => want(l.name, "features")); + const fs = require("fs"); + fs.appendFileSync(process.env.GITHUB_OUTPUT, + `signin=${JSON.stringify({ include: signin })}\nfeatures=${JSON.stringify({ include: features })}\n`); + console.log({ signin: signin.map((l) => l.name), features: features.map((l) => l.name) }); + ' + + build: + if: inputs.app_run_id == '' + runs-on: macos-latest + timeout-minutes: 60 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 - if: inputs.app_run_id == '' with: node-version: 20 cache: npm - - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - - name: Cache CocoaPods - if: inputs.app_run_id == '' uses: actions/cache@v4 with: path: | @@ -57,22 +96,19 @@ jobs: restore-keys: pods-${{ runner.os }}- - name: Install JS dependencies - if: inputs.app_run_id == '' run: npm ci - name: Generate the iOS project - if: inputs.app_run_id == '' run: npx expo prebuild --platform ios --clean - name: Build for the simulator - if: inputs.app_run_id == '' run: | WORKSPACE=$(ls -d ios/*.xcworkspace | head -1) SCHEME=$(basename "$WORKSPACE" .xcworkspace) + mkdir -p e2e-build # Release bundles the JS into the app, so no Metro server is needed. # arm64 only: the runner is Apple silicon, and a second simulator # architecture would double the build time. - mkdir -p "$OUT" if ! xcodebuild build \ -workspace "$WORKSPACE" \ -scheme "$SCHEME" \ @@ -82,145 +118,98 @@ jobs: -derivedDataPath build \ ARCHS=arm64 \ CODE_SIGNING_ALLOWED=NO \ - > "$OUT/xcodebuild.log" 2>&1; then - grep -E "error:|fatal error" "$OUT/xcodebuild.log" | head -n 50 || true - tail -n 80 "$OUT/xcodebuild.log" + > e2e-build/xcodebuild.log 2>&1; then + grep -E "error:|fatal error" e2e-build/xcodebuild.log | head -n 50 || true + tail -n 80 e2e-build/xcodebuild.log exit 1 fi # Packed as a tar: artifacts drop the executable bit, which would # leave a reused app unable to launch. tar -C build/Build/Products/Release-iphonesimulator -czf "$RUNNER_TEMP/simulator-app.tar.gz" BulwarkMobile.app + ls -la "$RUNNER_TEMP/simulator-app.tar.gz" - name: Upload the simulator app - if: inputs.app_run_id == '' uses: actions/upload-artifact@v4 with: name: simulator-app path: ${{ runner.temp }}/simulator-app.tar.gz if-no-files-found: error - - name: Download the simulator app from an earlier run - if: inputs.app_run_id != '' - uses: actions/download-artifact@v4 - with: - name: simulator-app - run-id: ${{ inputs.app_run_id }} - github-token: ${{ github.token }} - path: ${{ runner.temp }} - - - name: Unpack the simulator app - run: | - mkdir -p "$RUNNER_TEMP/app" - tar -C "$RUNNER_TEMP/app" -xzf "$RUNNER_TEMP/simulator-app.tar.gz" - echo "APP=$RUNNER_TEMP/app/BulwarkMobile.app" >> "$GITHUB_ENV" - - - name: Boot a simulator - run: | - # The newest iPhone on the newest runtime the image ships. - UDID=$(xcrun simctl list devices available -j | jq -r ' - [.devices | to_entries[] - | select(.key | test("SimRuntime.iOS")) - | .value[] | select(.name | startswith("iPhone")) | .udid] | last') - if [ -z "$UDID" ] || [ "$UDID" = "null" ]; then - xcrun simctl list devices available - echo "::error::No iPhone simulator available" - exit 1 - fi - xcrun simctl boot "$UDID" - xcrun simctl bootstatus "$UDID" -b - # A fresh simulator shows the slide-to-type tip over the keyboard the - # first time a text field gets focus, and typed text goes nowhere. - xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences DidShowContinuousPathIntroduction -bool true - xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardContinuousPathEnabled -bool false - # Predictions, autocorrect and password AutoFill can swallow or - # rewrite what XCTest types. - xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardPrediction -bool false - xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardAutocorrection -bool false - xcrun simctl spawn "$UDID" defaults write com.apple.keyboard.preferences KeyboardAutocapitalization -bool false - xcrun simctl spawn "$UDID" defaults write com.apple.WebUI AutoFillPasswords -bool false || true - xcrun simctl install "$UDID" "$APP" - echo "UDID=$UDID" >> "$GITHUB_ENV" - xcrun simctl list devices | grep "$UDID" - - - name: Install Maestro - run: | - curl -fsSL "https://get.maestro.mobile.dev" | bash - echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" - - - name: Run the flows - env: - SERVER: ${{ vars.E2E_SERVER }} - USERNAME: ${{ vars.E2E_USERNAME }} - PASSWORD: ${{ secrets.E2E_PASSWORD }} - # The XCTest driver takes a while to come up on a cold runner. - MAESTRO_DRIVER_STARTUP_TIMEOUT: "600000" - MAESTRO_CLI_NO_ANALYTICS: "1" - run: | - maestro --version - mkdir -p "$OUT/screenshots" - - xcrun simctl io "$UDID" recordVideo --codec=h264 --force "$OUT/run.mp4" & - RECORDER=$! - - ARGS=() - if [ -n "$SERVER" ] && [ -n "$USERNAME" ] && [ -n "$PASSWORD" ]; then - ARGS+=(-e "SERVER=$SERVER" -e "USERNAME=$USERNAME" -e "PASSWORD=$PASSWORD") - else - echo "::notice::E2E_SERVER, E2E_USERNAME or E2E_PASSWORD is not set - running the launch flow only." - ARGS+=(--exclude-tags signed-in) - fi - - # Warm-up with the launch flow alone. It types nothing, so its debug - # log holds no secrets and can be printed when the driver won't start. - # The real run below keeps debug output off: it would log the password, - # and artifacts here are public. - set +e - touch "$RUNNER_TEMP/flows-started" - (cd "$OUT/screenshots" && maestro --device "$UDID" test \ - --debug-output "$RUNNER_TEMP/maestro-warmup" "$GITHUB_WORKSPACE/.maestro/launch.yaml") - STATUS=$? - echo "::group::Maestro warm-up log (typing)" - find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec grep -n -i -E "inputText|typeText|keyboard|input_text|Exception|error" {} \; | tail -n 120 - echo "::endgroup::" - maestro --device "$UDID" hierarchy > "$OUT/hierarchy-warmup.json" 2>/dev/null || true - if [ $STATUS -ne 0 ]; then - echo "::group::Maestro warm-up log" - find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec tail -n 300 {} \; - echo "::endgroup::" - echo "::group::xcodebuild logs of the driver" - find "$HOME/Library/Logs" "$HOME/.maestro" "$TMPDIR" -name '*.log' -newer "$RUNNER_TEMP/flows-started" 2>/dev/null \ - | grep -i -E 'xctest|xcodebuild|maestro' | grep -v maestro-warmup | head -5 \ - | while read -r f; do echo "== $f"; tail -n 80 "$f"; done - echo "::endgroup::" - else - (cd "$OUT/screenshots" && maestro --device "$UDID" test \ - --format junit --output "$OUT/report.xml" \ - "${ARGS[@]}" "$GITHUB_WORKSPACE/.maestro") - STATUS=$? - fi - set -e - - kill -INT "$RECORDER" || true - wait "$RECORDER" || true - - # Newer Maestro versions write screenshots (and a screenshot of any - # failed step) under ~/.maestro/tests instead. Copy only the images: - # the JSON next to them holds the typed password. - find "$HOME/.maestro/tests" -name '*.png' -exec cp {} "$OUT/screenshots/" \; 2>/dev/null || true - ls -la "$OUT/screenshots" - exit $STATUS - - - name: Grab the app log on failure - if: failure() && env.UDID != '' - run: | - xcrun simctl spawn "$UDID" log show --last 10m --style compact \ - --predicate 'process CONTAINS "Bulwark"' > "$OUT/app.log" 2>&1 || true - tail -n 100 "$OUT/app.log" || true - - - name: Upload screenshots and recording - if: always() + - name: Upload the build log + if: failure() uses: actions/upload-artifact@v4 with: - name: e2e-ios-${{ github.run_number }} - path: e2e-output/ - if-no-files-found: ignore + name: xcodebuild-log + path: e2e-build/ + + signin: + needs: [plan, build] + if: >- + always() && !cancelled() && + (needs.build.result == 'success' || needs.build.result == 'skipped') && + fromJson(needs.plan.outputs.signin).include[0] != null + runs-on: macos-latest + timeout-minutes: 90 + permissions: + contents: read + actions: read + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.plan.outputs.signin) }} + env: + OUT: ${{ github.workspace }}/e2e-output + DEVICE: ${{ matrix.device }} + APPEARANCE: light + ORIENTATION: portrait + E2E_SERVER: ${{ vars.E2E_SERVER }} + E2E_WEBMAIL: https://bulwark.internal.bulwarkmail.org + E2E_ACCOUNT: e2e-login-${{ matrix.device }} + E2E_ACCOUNT2: e2e-second-${{ matrix.device }} + E2E_TOTP_ACCOUNT: e2e-totp-${{ matrix.device }} + E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }} + E2E_USERB_PASSWORD: ${{ secrets.E2E_USERB_PASSWORD }} + E2E_TOTP_SECRET: ${{ secrets.E2E_TOTP_SECRET }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/e2e-ios-leg + with: + suite: signin + app_run_id: ${{ inputs.app_run_id }} + flows: ${{ inputs.flows }} + playwright: "true" + name: ${{ matrix.name }} + + features: + needs: [plan, build] + if: >- + always() && !cancelled() && + (needs.build.result == 'success' || needs.build.result == 'skipped') && + fromJson(needs.plan.outputs.features).include[0] != null + runs-on: macos-latest + timeout-minutes: 120 + permissions: + contents: read + actions: read + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.plan.outputs.features) }} + env: + OUT: ${{ github.workspace }}/e2e-output + DEVICE: ${{ matrix.device }} + APPEARANCE: ${{ matrix.appearance }} + ORIENTATION: ${{ matrix.orientation }} + E2E_SERVER: ${{ vars.E2E_SERVER }} + E2E_WEBMAIL: https://bulwark.internal.bulwarkmail.org + E2E_ACCOUNT: ${{ matrix.account }} + E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }} + E2E_USERB_PASSWORD: ${{ secrets.E2E_USERB_PASSWORD }} + E2E_TOTP_SECRET: ${{ secrets.E2E_TOTP_SECRET }} + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/e2e-ios-leg + with: + suite: features + app_run_id: ${{ inputs.app_run_id }} + flows: ${{ inputs.flows }} + playwright: "false" + name: ${{ matrix.name }} diff --git a/.maestro/launch.yaml b/.maestro/launch.yaml index bdef3056..4dfcf872 100644 --- a/.maestro/launch.yaml +++ b/.maestro/launch.yaml @@ -1,6 +1,7 @@ -# Fresh install: the app starts and shows the sign-in choices. -# Needs no server, so it also runs when the test account secrets are missing. -# It types only a made-up address, so its debug log is safe to print. +# Fresh install: the app starts and shows the sign-in choices, and typing +# into a field works. Needs no server, so it runs before every suite as the +# warm-up for the XCTest driver. It types only a made-up address, so its +# debug log is safe to print. appId: org.bulwarkmail.mobile name: Launch to sign-in tags: @@ -13,15 +14,16 @@ tags: - extendedWaitUntil: visible: "Enter server details manually" timeout: 120000 -- takeScreenshot: 01-sign-in-choices +- takeScreenshot: warmup-01-sign-in-choices - tapOn: "Enter server details manually" - extendedWaitUntil: visible: id: login-server-input timeout: 10000 -- tapOn: - id: login-server-input - runFlow: subflows/dismiss-keyboard-tip.yaml -- inputText: "mail.example.com" -- takeScreenshot: 02-server-step -- assertVisible: "mail.example.com" +- runFlow: + file: subflows/type.yaml + env: + FIELD: login-server-input + TEXT: typing-probe.example.net +- takeScreenshot: warmup-02-typed diff --git a/.maestro/scripts/helper.js b/.maestro/scripts/helper.js new file mode 100644 index 00000000..b170a6c4 --- /dev/null +++ b/.maestro/scripts/helper.js @@ -0,0 +1,8 @@ +// Calls the test helper (scripts/e2e/helper.mjs) on the runner. +// env CALL: path and query, e.g. "/totp" or "/pair?user=x&deliver=openurl". +// The answer lands in output.helper. +const response = http.get(HELPER + CALL); +if (!response.ok) { + throw new Error('helper ' + CALL.split('?')[0] + ' failed: ' + response.status + ' ' + response.body); +} +output.helper = json(response.body); diff --git a/.maestro/signed-in.yaml b/.maestro/signed-in.yaml deleted file mode 100644 index 1f8ff7b1..00000000 --- a/.maestro/signed-in.yaml +++ /dev/null @@ -1,100 +0,0 @@ -# Signs in with a password and walks the main screens. -# Run with: maestro test -e SERVER=... -e USERNAME=... -e PASSWORD=... .maestro/signed-in.yaml -appId: org.bulwarkmail.mobile -name: Sign in and browse -tags: - - signed-in ---- -- launchApp: - clearState: true - clearKeychain: true - # The calendar tab asks for notification permission for reminders; - # granting it up front keeps the system alert from covering the screen. - permissions: - notifications: allow -- extendedWaitUntil: - visible: "Enter server details manually" - timeout: 120000 -- tapOn: "Enter server details manually" - -- tapOn: - id: login-server-input -- runFlow: subflows/dismiss-keyboard-tip.yaml -- inputText: ${SERVER} -- pressKey: Enter - -# Servers that offer OAuth land on a confirm step first; switch to the -# password form there. Others go straight to it. -- extendedWaitUntil: - visible: "Sign in with a password instead|Email or username" - timeout: 30000 -- runFlow: - when: - visible: "Sign in with a password instead" - commands: - - tapOn: "Sign in with a password instead" - -- tapOn: - id: login-username-input -- eraseText -- inputText: ${USERNAME} -- tapOn: - id: login-password-input -- inputText: ${PASSWORD} -- pressKey: Enter - -- extendedWaitUntil: - visible: - id: tab-mail - timeout: 60000 -- runFlow: - when: - visible: "Not now" - commands: - - tapOn: "Not now" - -- extendedWaitUntil: - visible: - id: email-row - timeout: 60000 -- takeScreenshot: 10-inbox - -- tapOn: - id: email-row - index: 0 -- waitForAnimationToEnd: - timeout: 5000 -- takeScreenshot: 11-message -- tapOn: "Back" -- extendedWaitUntil: - visible: - id: email-row - timeout: 10000 - -- tapOn: - id: tab-calendar -- waitForAnimationToEnd: - timeout: 5000 -- takeScreenshot: 20-calendar - -- tapOn: - id: tab-contacts -- waitForAnimationToEnd: - timeout: 5000 -- takeScreenshot: 30-contacts - -- tapOn: - id: tab-files -- waitForAnimationToEnd: - timeout: 5000 -- takeScreenshot: 40-files - -- tapOn: - id: tab-settings -- waitForAnimationToEnd: - timeout: 5000 -- takeScreenshot: 50-settings - -# Still alive after the walk: the tab bar is there and the app didn't crash. -- assertVisible: - id: tab-mail diff --git a/.maestro/signin/01-password.yaml b/.maestro/signin/01-password.yaml new file mode 100644 index 00000000..f5aa3656 --- /dev/null +++ b/.maestro/signin/01-password.yaml @@ -0,0 +1,32 @@ +# Password sign-in as the first account, sign out, sign in again. +appId: org.bulwarkmail.mobile +name: Password, sign out, sign in again +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- takeScreenshot: password-01-choices +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${SERVER} +- takeScreenshot: password-02-confirm +- tapOn: "Sign in with a password instead" +- runFlow: + file: ../subflows/password-form.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: password-03-inbox + +- runFlow: ../subflows/sign-out.yaml +- extendedWaitUntil: + visible: "Enter server details manually" + timeout: 30000 +- takeScreenshot: password-04-signed-out + +- runFlow: + file: ../subflows/password-sign-in.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- takeScreenshot: password-05-signed-in-again diff --git a/.maestro/signin/02-password-errors.yaml b/.maestro/signin/02-password-errors.yaml new file mode 100644 index 00000000..5bbbf2b5 --- /dev/null +++ b/.maestro/signin/02-password-errors.yaml @@ -0,0 +1,48 @@ +# A wrong password and an unreachable server: the app says what went wrong +# and stays on the form. +appId: org.bulwarkmail.mobile +name: Password errors +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${SERVER} +- tapOn: "Sign in with a password instead" +- runFlow: + file: ../subflows/password-form.yaml + env: + LOGIN: ${ACCOUNT}@example.org + PASSWORD: wrong-password-123 +- extendedWaitUntil: + visible: "That didn't work" + timeout: 30000 +- takeScreenshot: errors-01-wrong-password +- assertVisible: + id: login-password-input + +# Back to the server step, then a host that doesn't resolve. +- tapOn: "Back" +- tapOn: "Back" +- runFlow: + file: ../subflows/type.yaml + env: + FIELD: login-server-input + TEXT: mail.does-not-exist.invalid +- tapOn: "Continue" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 +- tapOn: "Sign in with a password instead" +- runFlow: + file: ../subflows/password-form.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- extendedWaitUntil: + visible: "Can't reach mail.does-not-exist.invalid|No mail server at mail.does-not-exist.invalid|Sign-in failed" + timeout: 60000 +- takeScreenshot: errors-02-unreachable +- assertVisible: + id: login-password-input diff --git a/.maestro/signin/03-totp-password.yaml b/.maestro/signin/03-totp-password.yaml new file mode 100644 index 00000000..85514778 --- /dev/null +++ b/.maestro/signin/03-totp-password.yaml @@ -0,0 +1,24 @@ +# A TOTP account signs in with a password: Stalwart answers 402 for the right +# password, and the app asks for the code. +appId: org.bulwarkmail.mobile +name: Password with TOTP +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${SERVER} +- tapOn: "Sign in with a password instead" +- runFlow: + file: ../subflows/password-form.yaml + env: + LOGIN: ${TOTP_ACCOUNT}@example.org +- extendedWaitUntil: + visible: "Two-factor code" + timeout: 30000 +- takeScreenshot: totp-01-code-asked +- runFlow: ../subflows/two-factor.yaml +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: totp-02-inbox diff --git a/.maestro/signin/04-oauth.yaml b/.maestro/signin/04-oauth.yaml new file mode 100644 index 00000000..1a4a29eb --- /dev/null +++ b/.maestro/signin/04-oauth.yaml @@ -0,0 +1,20 @@ +# OAuth with Stalwart's own authorization server: the confirm step's +# Continue opens an ASWebAuthenticationSession on Stalwart's sign-in page. +appId: org.bulwarkmail.mobile +name: OAuth (Stalwart) +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${SERVER} +- tapOn: "Continue" +- runFlow: ../subflows/allow-auth-session.yaml +- runFlow: + file: ../subflows/stalwart-login-page.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: oauth-01-inbox diff --git a/.maestro/signin/05-oauth-cancel.yaml b/.maestro/signin/05-oauth-cancel.yaml new file mode 100644 index 00000000..fe757e43 --- /dev/null +++ b/.maestro/signin/05-oauth-cancel.yaml @@ -0,0 +1,39 @@ +# Backing out of OAuth: Cancel on the system alert, and closing the sheet +# halfway. Both leave the user on the confirm step without an error. +appId: org.bulwarkmail.mobile +name: OAuth cancelled +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${SERVER} +- tapOn: "Continue" +- extendedWaitUntil: + visible: ".*Wants to Use.*to Sign In.*" + timeout: 30000 +- tapOn: + text: "Cancel" + below: ".*Wants to Use.*to Sign In.*" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 +- assertNotVisible: "Sign-in failed" +- assertNotVisible: "Sign-in was interrupted" +- takeScreenshot: oauth-cancel-01-alert-cancelled + +# Accept the alert, then close the sheet on Stalwart's page. +- tapOn: "Continue" +- runFlow: ../subflows/allow-auth-session.yaml +- extendedWaitUntil: + visible: "you@example.com" + timeout: 60000 +- tapOn: "Cancel" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 +- assertNotVisible: "Sign-in failed" +- assertNotVisible: "Sign-in was interrupted" +- takeScreenshot: oauth-cancel-02-sheet-closed diff --git a/.maestro/signin/06-webmail.yaml b/.maestro/signin/06-webmail.yaml new file mode 100644 index 00000000..6814d955 --- /dev/null +++ b/.maestro/signin/06-webmail.yaml @@ -0,0 +1,20 @@ +# Sign in via webmail: the confirm step on a Bulwark webmail address hands +# off to the webmail's sign-in page in an auth session. +appId: org.bulwarkmail.mobile +name: Webmail hand-off +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${WEBMAIL} +- tapOn: "Continue" +- runFlow: ../subflows/allow-auth-session.yaml +- runFlow: + file: ../subflows/webmail-login-page.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: webmail-01-inbox diff --git a/.maestro/signin/07-webmail-totp.yaml b/.maestro/signin/07-webmail-totp.yaml new file mode 100644 index 00000000..604bc9f7 --- /dev/null +++ b/.maestro/signin/07-webmail-totp.yaml @@ -0,0 +1,22 @@ +# Webmail hand-off for a TOTP account: the webmail asks for the code; if it +# hands back the password, the app finishes with a code of its own. +appId: org.bulwarkmail.mobile +name: Webmail hand-off with TOTP +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${WEBMAIL} +- tapOn: "Continue" +- runFlow: ../subflows/allow-auth-session.yaml +- runFlow: + file: ../subflows/webmail-login-page.yaml + env: + LOGIN: ${TOTP_ACCOUNT}@example.org + TOTP: "yes" +- runFlow: ../subflows/finish-two-factor-if-asked.yaml +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: webmail-totp-01-inbox diff --git a/.maestro/signin/08-link-password.yaml b/.maestro/signin/08-link-password.yaml new file mode 100644 index 00000000..90615e17 --- /dev/null +++ b/.maestro/signin/08-link-password.yaml @@ -0,0 +1,17 @@ +# Sign-in link from another app (simctl openurl), password account. The +# helper signs in to the test webmail, creates a fresh "Link mobile app" code +# and opens its bulwarkmail://pair link on the simulator. +appId: org.bulwarkmail.mobile +name: Sign-in link from another app +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runScript: + file: ../scripts/helper.js + env: + CALL: /pair?user=${ACCOUNT}&deliver=openurl +- runFlow: ../subflows/confirm-link.yaml +- runFlow: ../subflows/finish-two-factor-if-asked.yaml +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: link-password-01-inbox diff --git a/.maestro/signin/09-paste-password.yaml b/.maestro/signin/09-paste-password.yaml new file mode 100644 index 00000000..d74671f3 --- /dev/null +++ b/.maestro/signin/09-paste-password.yaml @@ -0,0 +1,16 @@ +# "Paste sign-in link" with a fresh pairing link on the clipboard (simctl +# pbcopy), password account. +appId: org.bulwarkmail.mobile +name: Paste sign-in link +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runScript: + file: ../scripts/helper.js + env: + CALL: /pair?user=${ACCOUNT}&deliver=pbcopy +- runFlow: ../subflows/paste-link.yaml +- runFlow: ../subflows/finish-two-factor-if-asked.yaml +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: paste-password-01-inbox diff --git a/.maestro/signin/10-link-totp.yaml b/.maestro/signin/10-link-totp.yaml new file mode 100644 index 00000000..a50bc8ee --- /dev/null +++ b/.maestro/signin/10-link-totp.yaml @@ -0,0 +1,15 @@ +# Sign-in link from another app for a TOTP account. +appId: org.bulwarkmail.mobile +name: Sign-in link, TOTP account +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runScript: + file: ../scripts/helper.js + env: + CALL: /pair?user=${TOTP_ACCOUNT}&deliver=openurl +- runFlow: ../subflows/confirm-link.yaml +- runFlow: ../subflows/finish-two-factor-if-asked.yaml +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: link-totp-01-inbox diff --git a/.maestro/signin/11-paste-totp.yaml b/.maestro/signin/11-paste-totp.yaml new file mode 100644 index 00000000..8fc369e6 --- /dev/null +++ b/.maestro/signin/11-paste-totp.yaml @@ -0,0 +1,15 @@ +# "Paste sign-in link" for a TOTP account. +appId: org.bulwarkmail.mobile +name: Paste sign-in link, TOTP account +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runScript: + file: ../scripts/helper.js + env: + CALL: /pair?user=${TOTP_ACCOUNT}&deliver=pbcopy +- runFlow: ../subflows/paste-link.yaml +- runFlow: ../subflows/finish-two-factor-if-asked.yaml +- runFlow: ../subflows/inbox.yaml +- takeScreenshot: paste-totp-01-inbox diff --git a/.maestro/signin/12-link-expired.yaml b/.maestro/signin/12-link-expired.yaml new file mode 100644 index 00000000..a6ea5299 --- /dev/null +++ b/.maestro/signin/12-link-expired.yaml @@ -0,0 +1,21 @@ +# An expired code (codes live 2 minutes): the helper creates one now and +# opens it after 130 s. The app says so and stays on the sign-in screen. +appId: org.bulwarkmail.mobile +name: Expired sign-in code +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runScript: + file: ../scripts/helper.js + env: + CALL: /pair-later?user=${ACCOUNT}&deliver=openurl&delay=130 +- runFlow: + file: ../subflows/confirm-link.yaml + env: + LINK_WAIT: 200000 +- extendedWaitUntil: + visible: "That code has expired.*" + timeout: 30000 +- takeScreenshot: link-expired-01-notice +- assertVisible: "Enter server details manually" diff --git a/.maestro/signin/13-link-used.yaml b/.maestro/signin/13-link-used.yaml new file mode 100644 index 00000000..798bc0eb --- /dev/null +++ b/.maestro/signin/13-link-used.yaml @@ -0,0 +1,44 @@ +# A code that was already used: sign in with it, sign out, open the same link +# again (same app process, then after a restart, which asks the server). +appId: org.bulwarkmail.mobile +name: Used sign-in code +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runScript: + file: ../scripts/helper.js + env: + CALL: /pair?user=${ACCOUNT}&deliver=openurl +- runFlow: ../subflows/confirm-link.yaml +- runFlow: ../subflows/inbox.yaml +- runFlow: ../subflows/sign-out.yaml +- extendedWaitUntil: + visible: "Enter server details manually" + timeout: 30000 + +- runScript: + file: ../scripts/helper.js + env: + CALL: /redeliver?user=${ACCOUNT}&deliver=openurl +- runFlow: ../subflows/confirm-link.yaml +- extendedWaitUntil: + visible: "That code was already used|That code has expired or was already used" + timeout: 30000 +- takeScreenshot: link-used-01-same-process + +- stopApp +- launchApp +- extendedWaitUntil: + visible: "Enter server details manually" + timeout: 60000 +- runScript: + file: ../scripts/helper.js + env: + CALL: /redeliver?user=${ACCOUNT}&deliver=openurl +- runFlow: ../subflows/confirm-link.yaml +- extendedWaitUntil: + visible: "That code was already used|That code has expired or was already used" + timeout: 30000 +- takeScreenshot: link-used-02-after-restart +- assertVisible: "Enter server details manually" diff --git a/.maestro/signin/14-scanner-camera.yaml b/.maestro/signin/14-scanner-camera.yaml new file mode 100644 index 00000000..6e81d171 --- /dev/null +++ b/.maestro/signin/14-scanner-camera.yaml @@ -0,0 +1,32 @@ +# The QR scanner with camera access denied, then allowed (the simulator has +# no camera, so nothing can be scanned). Neither may crash the app. +appId: org.bulwarkmail.mobile +name: Scanner without camera +tags: + - signin +--- +- runFlow: + file: ../subflows/start.yaml + env: + CAMERA: deny +- tapOn: "Scan a sign-in code.*" +- extendedWaitUntil: + visible: "Camera access is turned off.*|Scanning a sign-in QR code uses the camera.*" + timeout: 15000 +- takeScreenshot: scanner-01-denied +- tapOn: "Close" +- assertVisible: "Enter server details manually" + +- runFlow: + file: ../subflows/start.yaml + env: + CAMERA: allow +- tapOn: "Scan a sign-in code.*" +- extendedWaitUntil: + visible: "Sign-in code" + timeout: 15000 +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: scanner-02-allowed +- tapOn: "Close" +- assertVisible: "Enter server details manually" diff --git a/.maestro/signin/15-email-discovery.yaml b/.maestro/signin/15-email-discovery.yaml new file mode 100644 index 00000000..52170516 --- /dev/null +++ b/.maestro/signin/15-email-discovery.yaml @@ -0,0 +1,23 @@ +# "Use my email address": example.org has no autodiscovery, so the app moves +# on to the server step and names the domain. +appId: org.bulwarkmail.mobile +name: Email address discovery +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- tapOn: "Use my email address.*" +- extendedWaitUntil: + visible: "What's your email address." + timeout: 10000 +- waitForAnimationToEnd: + timeout: 3000 +- inputText: ${ACCOUNT}@example.org +- assertVisible: ${ACCOUNT}@example.org +- tapOn: "Continue" +- extendedWaitUntil: + visible: "We couldn't find a server for example.org.*" + timeout: 60000 +- takeScreenshot: discovery-01-not-found +- assertVisible: + id: login-server-input diff --git a/.maestro/signin/16-add-password.yaml b/.maestro/signin/16-add-password.yaml new file mode 100644 index 00000000..829c1093 --- /dev/null +++ b/.maestro/signin/16-add-password.yaml @@ -0,0 +1,30 @@ +# Second account by password via "Another account on ", then switch +# between the two. +appId: org.bulwarkmail.mobile +name: Add account, password +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/password-sign-in.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runFlow: ../subflows/open-add-account.yaml +- takeScreenshot: add-password-01-choices +- tapOn: "Another account on .*" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 +- tapOn: "Sign in with a password instead" +- runFlow: + file: ../subflows/password-form.yaml + env: + LOGIN: ${ACCOUNT2}@example.org +- runFlow: ../subflows/inbox.yaml +- runFlow: + file: ../subflows/check-active-account.yaml + env: + ACTIVE: ${ACCOUNT2}@example.org +- assertVisible: ${ACCOUNT}@example.org +- takeScreenshot: add-password-02-two-accounts diff --git a/.maestro/signin/17-add-oauth.yaml b/.maestro/signin/17-add-oauth.yaml new file mode 100644 index 00000000..f48ba879 --- /dev/null +++ b/.maestro/signin/17-add-oauth.yaml @@ -0,0 +1,42 @@ +# Second account by OAuth. Adding uses an ephemeral auth session, so the +# first account's Stalwart sign-in must not be reused. +appId: org.bulwarkmail.mobile +name: Add account, OAuth +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${SERVER} +- tapOn: "Continue" +- runFlow: ../subflows/allow-auth-session.yaml +- runFlow: + file: ../subflows/stalwart-login-page.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runFlow: ../subflows/inbox.yaml + +- runFlow: ../subflows/open-add-account.yaml +- tapOn: "Another account on .*" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 +- tapOn: "Continue" +- runFlow: + when: + visible: ".*Wants to Use.*to Sign In.*" + commands: + - runFlow: ../subflows/allow-auth-session.yaml +- runFlow: + file: ../subflows/stalwart-login-page.yaml + env: + LOGIN: ${ACCOUNT2}@example.org +- runFlow: ../subflows/inbox.yaml +- runFlow: + file: ../subflows/check-active-account.yaml + env: + ACTIVE: ${ACCOUNT2}@example.org +- assertVisible: ${ACCOUNT}@example.org +- takeScreenshot: add-oauth-01-two-accounts diff --git a/.maestro/signin/18-add-webmail.yaml b/.maestro/signin/18-add-webmail.yaml new file mode 100644 index 00000000..65b2e934 --- /dev/null +++ b/.maestro/signin/18-add-webmail.yaml @@ -0,0 +1,43 @@ +# Second account via the webmail hand-off, on top of a password account. +appId: org.bulwarkmail.mobile +name: Add account, webmail +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/password-sign-in.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runFlow: ../subflows/open-add-account.yaml +- tapOn: "An account somewhere else.*" +- extendedWaitUntil: + visible: "What's the other address." + timeout: 10000 +- tapOn: "I know my server address" +- runFlow: + file: ../subflows/type.yaml + env: + FIELD: login-server-input + TEXT: ${WEBMAIL} +- tapOn: "Continue" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 +- tapOn: "Continue" +- runFlow: + when: + visible: ".*Wants to Use.*to Sign In.*" + commands: + - runFlow: ../subflows/allow-auth-session.yaml +- runFlow: + file: ../subflows/webmail-login-page.yaml + env: + LOGIN: ${ACCOUNT2}@example.org +- runFlow: ../subflows/inbox.yaml +- runFlow: + file: ../subflows/check-active-account.yaml + env: + ACTIVE: ${ACCOUNT2}@example.org +- assertVisible: ${ACCOUNT}@example.org +- takeScreenshot: add-webmail-01-two-accounts diff --git a/.maestro/signin/19-add-link.yaml b/.maestro/signin/19-add-link.yaml new file mode 100644 index 00000000..a786d251 --- /dev/null +++ b/.maestro/signin/19-add-link.yaml @@ -0,0 +1,30 @@ +# Second account from a sign-in link opened by another app while signed in. +appId: org.bulwarkmail.mobile +name: Add account, sign-in link +tags: + - signin +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/password-sign-in.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runScript: + file: ../scripts/helper.js + env: + CALL: /pair?user=${ACCOUNT2}&deliver=openurl +- extendedWaitUntil: + visible: "Sign in with a link." + timeout: 60000 +- takeScreenshot: add-link-01-prompt +- assertVisible: "This link adds an account.*" +- tapOn: + text: "Continue" + below: "Sign in with a link." +- runFlow: ../subflows/inbox.yaml +- runFlow: + file: ../subflows/check-active-account.yaml + env: + ACTIVE: ${ACCOUNT2}@example.org +- assertVisible: ${ACCOUNT}@example.org +- takeScreenshot: add-link-02-two-accounts diff --git a/.maestro/subflows/allow-auth-session.yaml b/.maestro/subflows/allow-auth-session.yaml new file mode 100644 index 00000000..3cb482df --- /dev/null +++ b/.maestro/subflows/allow-auth-session.yaml @@ -0,0 +1,11 @@ +# iOS asks before an app opens a sign-in session in the system browser: +# "“Bulwark Mobile” Wants to Use “” to Sign In". Accept it. +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: ".*Wants to Use.*to Sign In.*" + timeout: 30000 +- takeScreenshot: auth-session-alert +- tapOn: + text: "Continue" + below: ".*Wants to Use.*to Sign In.*" diff --git a/.maestro/subflows/check-active-account.yaml b/.maestro/subflows/check-active-account.yaml new file mode 100644 index 00000000..d8fd9de3 --- /dev/null +++ b/.maestro/subflows/check-active-account.yaml @@ -0,0 +1,16 @@ +# Settings → Account lists the accounts on this device; the active one is +# ACTIVE. Goes back to Mail afterwards. +appId: org.bulwarkmail.mobile +--- +- tapOn: + id: tab-settings +- extendedWaitUntil: + visible: "Account" + timeout: 10000 +- tapOn: "Account" +- extendedWaitUntil: + visible: ${ACTIVE} + timeout: 20000 +- takeScreenshot: accounts-${ACTIVE} +- tapOn: + id: tab-mail diff --git a/.maestro/subflows/confirm-link.yaml b/.maestro/subflows/confirm-link.yaml new file mode 100644 index 00000000..d0917c13 --- /dev/null +++ b/.maestro/subflows/confirm-link.yaml @@ -0,0 +1,10 @@ +# A sign-in link from another app asks first. Continue. +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: "Sign in with a link.|Open the sign-in page of.*" + timeout: ${LINK_WAIT || 60000} +- takeScreenshot: link-confirm-prompt +- tapOn: + text: "Continue" + below: "Sign in with a link." diff --git a/.maestro/subflows/finish-two-factor-if-asked.yaml b/.maestro/subflows/finish-two-factor-if-asked.yaml new file mode 100644 index 00000000..d6bd8129 --- /dev/null +++ b/.maestro/subflows/finish-two-factor-if-asked.yaml @@ -0,0 +1,12 @@ +# Some routes hand the app a password for a TOTP account; the app then asks +# for a code itself. Answer it when it shows up. +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: "Two-factor code|Inbox" + timeout: 90000 +- runFlow: + when: + visible: "Two-factor code" + commands: + - runFlow: two-factor.yaml diff --git a/.maestro/subflows/inbox.yaml b/.maestro/subflows/inbox.yaml new file mode 100644 index 00000000..0e132000 --- /dev/null +++ b/.maestro/subflows/inbox.yaml @@ -0,0 +1,18 @@ +# Signed in: the tab bar is there and the inbox shows messages. Dismisses the +# one-time prompts on the way. +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: + id: tab-mail + timeout: 90000 +- runFlow: + when: + visible: "Not now" + commands: + - tapOn: "Not now" +- extendedWaitUntil: + visible: + id: email-row + timeout: 60000 +- assertVisible: ${INBOX_TEXT || 'Your verification code'} diff --git a/.maestro/subflows/manual-server.yaml b/.maestro/subflows/manual-server.yaml new file mode 100644 index 00000000..4959e03b --- /dev/null +++ b/.maestro/subflows/manual-server.yaml @@ -0,0 +1,18 @@ +# From the sign-in choices: enter a server address by hand and continue to the +# confirm step. env HOST: the address as a user types it. +appId: org.bulwarkmail.mobile +--- +- tapOn: "Enter server details manually" +- extendedWaitUntil: + visible: + id: login-server-input + timeout: 10000 +- runFlow: + file: type.yaml + env: + FIELD: login-server-input + TEXT: ${HOST} +- tapOn: "Continue" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 diff --git a/.maestro/subflows/open-add-account.yaml b/.maestro/subflows/open-add-account.yaml new file mode 100644 index 00000000..92a22102 --- /dev/null +++ b/.maestro/subflows/open-add-account.yaml @@ -0,0 +1,17 @@ +# Signed in: Settings → Account → Add account, up to the add-mode choices. +appId: org.bulwarkmail.mobile +--- +- tapOn: + id: tab-settings +- extendedWaitUntil: + visible: "Account" + timeout: 10000 +- tapOn: "Account" +- scrollUntilVisible: + element: "Add account" + direction: DOWN + timeout: 20000 +- tapOn: "Add account" +- extendedWaitUntil: + visible: "Add another account" + timeout: 10000 diff --git a/.maestro/subflows/password-form.yaml b/.maestro/subflows/password-form.yaml new file mode 100644 index 00000000..5ba26f5b --- /dev/null +++ b/.maestro/subflows/password-form.yaml @@ -0,0 +1,19 @@ +# On the password step: fill in LOGIN and PASSWORD and sign in. +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: + id: login-username-input + timeout: 20000 +- runFlow: + file: type.yaml + env: + FIELD: login-username-input + TEXT: ${LOGIN} +- runFlow: + file: type-secret.yaml + env: + FIELD: login-password-input + TEXT: ${PASSWORD} +# Return on the password field signs in. +- pressKey: Enter diff --git a/.maestro/subflows/password-sign-in.yaml b/.maestro/subflows/password-sign-in.yaml new file mode 100644 index 00000000..ebd6af1f --- /dev/null +++ b/.maestro/subflows/password-sign-in.yaml @@ -0,0 +1,11 @@ +# From the sign-in choices to the inbox: manual server, password. +# env LOGIN: the account's address. +appId: org.bulwarkmail.mobile +--- +- runFlow: + file: manual-server.yaml + env: + HOST: ${SERVER} +- tapOn: "Sign in with a password instead" +- runFlow: password-form.yaml +- runFlow: inbox.yaml diff --git a/.maestro/subflows/paste-link.yaml b/.maestro/subflows/paste-link.yaml new file mode 100644 index 00000000..ef3c6cfa --- /dev/null +++ b/.maestro/subflows/paste-link.yaml @@ -0,0 +1,22 @@ +# The helper has put a sign-in link on the simulator's clipboard: open +# "Paste sign-in link", paste it and sign in. +appId: org.bulwarkmail.mobile +--- +- tapOn: "Paste sign-in link.*" +- extendedWaitUntil: + visible: "Paste" + timeout: 10000 +- tapOn: "Paste" +# iOS asks before an app reads the clipboard. +- runFlow: + when: + visible: "Allow Paste" + commands: + - tapOn: "Allow Paste" +- extendedWaitUntil: + visible: "bulwarkmail://pair.*" + timeout: 10000 +- takeScreenshot: paste-link-filled +- tapOn: + text: "Sign in" + below: "bulwarkmail://pair.*" diff --git a/.maestro/subflows/sign-out.yaml b/.maestro/subflows/sign-out.yaml new file mode 100644 index 00000000..6f016d4f --- /dev/null +++ b/.maestro/subflows/sign-out.yaml @@ -0,0 +1,16 @@ +# Signed in: sign out of the active account from the end of Settings. +appId: org.bulwarkmail.mobile +--- +- tapOn: + id: tab-settings +- scrollUntilVisible: + element: "Sign Out" + direction: DOWN + timeout: 30000 +- tapOn: "Sign Out" +- extendedWaitUntil: + visible: "The account is removed from this device.*" + timeout: 10000 +- tapOn: + text: "Sign out" + below: "The account is removed from this device.*" diff --git a/.maestro/subflows/stalwart-login-page.yaml b/.maestro/subflows/stalwart-login-page.yaml new file mode 100644 index 00000000..d8db349a --- /dev/null +++ b/.maestro/subflows/stalwart-login-page.yaml @@ -0,0 +1,25 @@ +# Stalwart's own sign-in page inside the auth session. env LOGIN, TOTP (any +# non-empty value: the account needs a one-time code). +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: "you@example.com" + timeout: 60000 +- takeScreenshot: stalwart-login-page +- tapOn: "you@example.com" +- inputText: ${LOGIN} +- tapOn: "••••••••" +- inputText: ${PASSWORD} +- runFlow: + when: + true: ${TOTP == 'yes'} + commands: + - runScript: + file: ../scripts/helper.js + env: + CALL: /totp + - tapOn: "000000" + - inputText: ${output.helper.code} +- tapOn: + text: "Sign in" + index: 1 diff --git a/.maestro/subflows/start.yaml b/.maestro/subflows/start.yaml new file mode 100644 index 00000000..c85d5e1f --- /dev/null +++ b/.maestro/subflows/start.yaml @@ -0,0 +1,20 @@ +# Fresh install state, rotated for the landscape leg, on the sign-in choices. +# env CAMERA: allow (default) or deny. +appId: org.bulwarkmail.mobile +--- +- launchApp: + clearState: true + clearKeychain: true + permissions: + notifications: allow + camera: ${CAMERA || 'allow'} + photos: allow +- runFlow: + when: + true: ${ORIENTATION == 'landscape'} + commands: + - setOrientation: LANDSCAPE_LEFT +# A cold start of a release build on a CI simulator can be slow. +- extendedWaitUntil: + visible: "Enter server details manually" + timeout: 120000 diff --git a/.maestro/subflows/two-factor.yaml b/.maestro/subflows/two-factor.yaml new file mode 100644 index 00000000..6e419252 --- /dev/null +++ b/.maestro/subflows/two-factor.yaml @@ -0,0 +1,21 @@ +# The two-factor field is showing (it focuses itself): type a fresh code from +# the helper and sign in. +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: "Two-factor code" + timeout: 30000 +- runScript: + file: ../scripts/helper.js + env: + CALL: /totp +- waitForAnimationToEnd: + timeout: 3000 +- inputText: ${output.helper.code} +# The number pad has no return key. +- runFlow: + when: + notVisible: "Sign in" + commands: + - hideKeyboard +- tapOn: "Sign in" diff --git a/.maestro/subflows/type-secret.yaml b/.maestro/subflows/type-secret.yaml new file mode 100644 index 00000000..f4bd7a2f --- /dev/null +++ b/.maestro/subflows/type-secret.yaml @@ -0,0 +1,17 @@ +# Type a secret into the field with id FIELD. Same focus handling as +# type.yaml, but the value can't be checked (a secure field shows dots), and +# it must never be shown, so a lost keystroke surfaces as a failed sign-in. +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + notVisible: + id: ${FIELD} + focused: true + commands: + - tapOn: + id: ${FIELD} +- waitForAnimationToEnd: + timeout: 3000 +- eraseText: 80 +- inputText: ${TEXT} diff --git a/.maestro/subflows/type.yaml b/.maestro/subflows/type.yaml new file mode 100644 index 00000000..5ca5d6e8 --- /dev/null +++ b/.maestro/subflows/type.yaml @@ -0,0 +1,25 @@ +# Type TEXT into the field with id FIELD and check it arrived in full. +# +# iOS drops keystrokes while the edit menu (the "AutoFill" bubble) opens, +# which happens when a flow taps a field that already has focus; an +# auto-focused field is left alone. Retyped up to twice if text got lost. +appId: org.bulwarkmail.mobile +--- +- retry: + maxRetries: 2 + commands: + - runFlow: + when: + notVisible: + id: ${FIELD} + focused: true + commands: + - tapOn: + id: ${FIELD} + - waitForAnimationToEnd: + timeout: 3000 + - eraseText: 80 + - inputText: ${TEXT} + - assertVisible: + id: ${FIELD} + text: ${TEXT_PATTERN || TEXT} diff --git a/.maestro/subflows/webmail-login-page.yaml b/.maestro/subflows/webmail-login-page.yaml new file mode 100644 index 00000000..4ef5dddf --- /dev/null +++ b/.maestro/subflows/webmail-login-page.yaml @@ -0,0 +1,31 @@ +# The Bulwark webmail's sign-in page inside the auth session (the app's +# "Sign in via webmail" hand-off). env LOGIN, TOTP=yes for a TOTP account. +appId: org.bulwarkmail.mobile +--- +- extendedWaitUntil: + visible: "user@example.com" + timeout: 60000 +- takeScreenshot: webmail-login-page +- tapOn: "user@example.com" +- inputText: ${LOGIN} +- tapOn: "Enter your password" +- inputText: ${PASSWORD} +- tapOn: + text: "Sign in" + index: 1 +- runFlow: + when: + true: ${TOTP == 'yes'} + commands: + - extendedWaitUntil: + visible: "000000" + timeout: 30000 + - runScript: + file: ../scripts/helper.js + env: + CALL: /totp + - tapOn: "000000" + - inputText: ${output.helper.code} + - tapOn: + text: "Sign in|Verify" + index: 1 diff --git a/scripts/e2e/boot-sim.sh b/scripts/e2e/boot-sim.sh new file mode 100644 index 00000000..0547dd88 --- /dev/null +++ b/scripts/e2e/boot-sim.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Boot a fresh simulator for one test leg and install the app. +# +# scripts/e2e/boot-sim.sh +# +# Writes UDID and SIM_NAME to $GITHUB_ENV. Every simulator gets the keyboard +# set up for XCTest typing: a fresh one shows the slide-to-type tip over the +# keyboard the first time a field gets focus, and predictions, autocorrect and +# password AutoFill can swallow or rewrite typed text. +set -euo pipefail +DEVICE=$1 +APPEARANCE=$2 +APP=$3 + +case "$DEVICE" in + phone) PATTERN='^iPhone' ;; + tablet) PATTERN='^iPad Pro 11|^iPad Air 11|^iPad \(' ;; + *) echo "unknown device $DEVICE" >&2; exit 1 ;; +esac + +# The newest matching device type on the newest iOS runtime the image ships. +RUNTIME=$(xcrun simctl list runtimes available -j | jq -r '[.runtimes[] | select(.platform == "iOS")] | sort_by(.version | split(".") | map(tonumber)) | last | .identifier') +UDID=$(xcrun simctl list devices available -j | jq -r --arg rt "$RUNTIME" --arg re "$PATTERN" ' + [.devices[$rt][] | select(.name | test($re))] | last | .udid // empty') +if [ -z "$UDID" ]; then + xcrun simctl list devices available + echo "::error::No $DEVICE simulator on $RUNTIME" + exit 1 +fi +NAME=$(xcrun simctl list devices -j | jq -r --arg u "$UDID" '.devices[][] | select(.udid == $u) | .name') +echo "Using $NAME ($UDID) on $RUNTIME" + +xcrun simctl boot "$UDID" +xcrun simctl bootstatus "$UDID" -b + +PREFS=com.apple.keyboard.preferences +xcrun simctl spawn "$UDID" defaults write $PREFS DidShowContinuousPathIntroduction -bool true +xcrun simctl spawn "$UDID" defaults write $PREFS KeyboardContinuousPathEnabled -bool false +xcrun simctl spawn "$UDID" defaults write $PREFS KeyboardPrediction -bool false +xcrun simctl spawn "$UDID" defaults write $PREFS KeyboardAutocorrection -bool false +xcrun simctl spawn "$UDID" defaults write $PREFS KeyboardAutocapitalization -bool false +xcrun simctl spawn "$UDID" defaults write $PREFS KeyboardShowPredictionBar -bool false +xcrun simctl spawn "$UDID" defaults write com.apple.WebUI AutoFillPasswords -bool false || true + +xcrun simctl ui "$UDID" appearance "$APPEARANCE" +xcrun simctl install "$UDID" "$APP" + +{ + echo "UDID=$UDID" + echo "SIM_NAME=$NAME" +} >> "${GITHUB_ENV:-/dev/null}" diff --git a/scripts/e2e/failed-steps.mjs b/scripts/e2e/failed-steps.mjs new file mode 100644 index 00000000..f4fdf605 --- /dev/null +++ b/scripts/e2e/failed-steps.mjs @@ -0,0 +1,71 @@ +#!/usr/bin/env node +// Summarise Maestro's debug output (~/.maestro/tests) without its secrets. +// +// The JSON there records every command with its arguments, so typed +// passwords are in it; it is never uploaded. This prints, per flow, the +// command types and their status (no arguments), and for a failed command +// the element tree Maestro saw (labels, ids and bounds), which is what a +// failing selector needs. run-flows.sh scrubs the secret values from the +// output as well. +import fs from 'node:fs'; +import path from 'node:path'; + +const root = process.argv[2]; +const out = []; + +function walkFiles(dir) { + if (!fs.existsSync(dir)) return []; + return fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => { + const p = path.join(dir, e.name); + return e.isDirectory() ? walkFiles(p) : [p]; + }); +} + +function tree(node, depth, lines) { + if (!node || typeof node !== 'object') return; + const a = node.attributes || {}; + const label = [a.accessibilityText, a.text, a.hintText, a['resource-id'] && `#${a['resource-id']}`] + .filter(Boolean) + .filter((v, i, all) => all.indexOf(v) === i) + .join(' | '); + if (label) lines.push(`${' '.repeat(Math.min(depth, 12))}${label} ${a.bounds || ''}${a.focused === 'true' ? ' [focused]' : ''}`); + for (const c of node.children || []) tree(c, depth + 1, lines); +} + +function findHierarchy(obj, seen = new Set()) { + if (!obj || typeof obj !== 'object' || seen.has(obj)) return null; + seen.add(obj); + if (obj.attributes && Array.isArray(obj.children)) return obj; + for (const v of Object.values(obj)) { + const h = findHierarchy(v, seen); + if (h) return h; + } + return null; +} + +for (const file of walkFiles(root).filter((f) => /commands-.*\.json$/.test(f))) { + let data; + try { + data = JSON.parse(fs.readFileSync(file, 'utf8')); + } catch { + continue; + } + out.push(`== ${path.basename(file)}`); + for (const entry of Array.isArray(data) ? data : []) { + const command = entry.command || {}; + const type = Object.keys(command).find((k) => command[k]) || '?'; + const status = entry.metadata?.status || '?'; + out.push(` ${status.padEnd(9)} ${type}`); + if (status === 'FAILED') { + const err = entry.metadata?.error?.message; + if (err) out.push(` error: ${String(err).slice(0, 300)}`); + const h = findHierarchy(entry.metadata); + if (h) { + const lines = []; + tree(h, 0, lines); + out.push(...lines.slice(0, 250).map((l) => ` ${l}`)); + } + } + } +} +process.stdout.write(out.join('\n') + '\n'); diff --git a/scripts/e2e/helper.mjs b/scripts/e2e/helper.mjs new file mode 100644 index 00000000..9c23c068 --- /dev/null +++ b/scripts/e2e/helper.mjs @@ -0,0 +1,384 @@ +#!/usr/bin/env node +// Test helper for the iOS simulator flows (.maestro/). Runs on the CI runner +// next to the simulator; Maestro flows call it from runScript over +// http://127.0.0.1:8787 for things a flow can't do itself: +// +// GET /totp current TOTP code of the e2e-totp-* accounts +// GET /pair?user=&deliver= sign in to the test webmail with Playwright, +// create a fresh "Link mobile app" code and hand +// its bulwarkmail://pair link to the app +// (deliver=openurl: simctl openurl, as a link +// from another app; pbcopy: simctl pbcopy for +// "Paste sign-in link"; none: keep it only) +// GET /redeliver?user=&deliver= hand the last link for that user over again +// GET /pair-later?user=&deliver=&delay= create a code now, deliver it after +// seconds (expired-code test) +// GET /send?to=&subject=&attach=1&html=1 userb sends a message over JMAP +// GET /find?subject=&as=userb&timeout= wait until a message with that subject +// arrives; answers {found, attachments} +// GET /openurl?url= simctl openurl for other links (mailto:, ...) +// GET /clipboard?text= simctl pbcopy of a plain value +// +// Secrets come from the environment and never appear in a response: the +// pairing link is delivered to the simulator directly, not returned. The +// answers are JSON. Logs go to stdout without secrets or links. +// +// Env: E2E_SERVER (JMAP server), E2E_PASSWORD (shared by the e2e accounts), +// E2E_USERB_PASSWORD, E2E_TOTP_SECRET (base32), E2E_WEBMAIL (test webmail base URL), UDID +// (booted simulator), PLAYWRIGHT_DIR (node_modules folder with playwright). + +import http from 'node:http'; +import crypto from 'node:crypto'; +import { execFile } from 'node:child_process'; +import { createRequire } from 'node:module'; +import path from 'node:path'; + +const PORT = Number(process.env.HELPER_PORT || 8787); +const SERVER = (process.env.E2E_SERVER || '').replace(/\/+$/, ''); +const WEBMAIL = (process.env.E2E_WEBMAIL || 'https://bulwark.internal.bulwarkmail.org').replace(/\/+$/, ''); +const PASSWORD = process.env.E2E_PASSWORD || ''; +const TOTP_SECRET = process.env.E2E_TOTP_SECRET || ''; +const UDID = process.env.UDID || 'booted'; +const DOMAIN = 'example.org'; + +const log = (...a) => console.log(new Date().toISOString().slice(11, 19), ...a); + +// ── TOTP (RFC 6238, SHA-1, 6 digits, 30 s) ────────────────────────────────── + +function base32Decode(s) { + const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; + let bits = 0; + let value = 0; + const out = []; + for (const ch of s.replace(/=+$/, '').toUpperCase()) { + const idx = alphabet.indexOf(ch); + if (idx < 0) continue; + value = (value << 5) | idx; + bits += 5; + if (bits >= 8) { + out.push((value >>> (bits - 8)) & 0xff); + bits -= 8; + } + } + return Buffer.from(out); +} + +function totpAt(secret, time) { + const counter = Math.floor(time / 30); + const buf = Buffer.alloc(8); + buf.writeBigUInt64BE(BigInt(counter)); + const mac = crypto.createHmac('sha1', base32Decode(secret)).update(buf).digest(); + const offset = mac[mac.length - 1] & 0xf; + const code = (mac.readUInt32BE(offset) & 0x7fffffff) % 1_000_000; + return String(code).padStart(6, '0'); +} + +// A code with at least `minLeft` seconds to live, waiting for the next window +// when the current one is about to close. +async function freshTotp(minLeft = 10) { + if (!TOTP_SECRET) throw new Error('E2E_TOTP_SECRET is not set'); + const now = Date.now() / 1000; + const left = 30 - (now % 30); + if (left < minLeft) await sleep((left + 0.5) * 1000); + return totpAt(TOTP_SECRET, Date.now() / 1000); +} + +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + +// ── simulator ─────────────────────────────────────────────────────────────── + +function simctl(args, input) { + return new Promise((resolve, reject) => { + const child = execFile('xcrun', ['simctl', ...args], { timeout: 30_000 }, (err, stdout, stderr) => { + if (err) reject(new Error(`simctl ${args[0]} failed: ${stderr || err.message}`)); + else resolve(stdout); + }); + if (input !== undefined) { + child.stdin.end(input); + } + }); +} + +async function deliver(link, how) { + if (how === 'openurl') await simctl(['openurl', UDID, link]); + else if (how === 'pbcopy') await simctl(['pbcopy', UDID], link); + else if (how !== 'none') throw new Error(`unknown deliver=${how}`); +} + +// ── webmail pairing (Playwright) ──────────────────────────────────────────── + +let chromium = null; +function loadPlaywright() { + if (chromium) return chromium; + const dir = process.env.PLAYWRIGHT_DIR; + const req = createRequire(dir ? path.join(dir, 'noop.js') : import.meta.url); + ({ chromium } = req('playwright')); + return chromium; +} + +const lastLinks = new Map(); + +async function createPairingLink(user) { + const browser = await loadPlaywright().launch(); + const started = Date.now(); + let page = null; + try { + const context = await browser.newContext({ locale: 'en-US', viewport: { width: 1280, height: 900 } }); + page = await context.newPage(); + await page.goto(`${WEBMAIL}/en/login`, { waitUntil: 'domcontentloaded' }); + await page.locator('#username').fill(`${user}@${DOMAIN}`); + await page.locator('#password').fill(PASSWORD); + // Keep the session across page loads (a Basic session without "remember + // me" is signed out by the next navigation). + const remember = page.getByRole('checkbox', { name: /remember/i }); + if (await remember.count()) await remember.first().check().catch(() => undefined); + const totpField = page.locator('#totp'); + if (await totpField.isVisible().catch(() => false)) await totpField.fill(await freshTotp()); + await page.locator('button[type=submit]').first().click(); + + // A TOTP account: the webmail asks for the code after the password. + const signedIn = page.waitForURL((u) => !/\/login/.test(u.pathname), { timeout: 30_000 }).then(() => 'in'); + const askedCode = totpField.waitFor({ state: 'visible', timeout: 30_000 }).then(() => 'totp'); + const first = await Promise.race([signedIn, askedCode]); + if (first === 'totp' && /\/login/.test(new URL(page.url()).pathname)) { + await totpField.fill(await freshTotp()); + await page.locator('button[type=submit]').first().click(); + await page.waitForURL((u) => !/\/login/.test(u.pathname), { timeout: 30_000 }); + } + + await page.goto(`${WEBMAIL}/en/settings/security`, { waitUntil: 'domcontentloaded' }); + const show = page.getByRole('button', { name: /Show QR code/i }); + await show.waitFor({ timeout: 30_000 }); + await show.click(); + + // Step-up: the password again (and a code for a TOTP account). + const linkInput = page.locator('input[readonly][value^="bulwarkmail://"]'); + // The Security page has other password forms; use the link-device one. + const form = page.locator('form', { hasText: /confirm it's you/i }); + const pw = form.locator('input[type=password]').first(); + await Promise.race([ + linkInput.waitFor({ timeout: 30_000 }), + pw.waitFor({ timeout: 30_000 }), + ]); + if (!(await linkInput.count())) { + await pw.fill(PASSWORD); + // For a TOTP account the code field is there from the start. + const codeField = form.locator('input:not([type=password])'); + if (await codeField.count()) await codeField.last().fill(await freshTotp()); + await form.getByRole('button', { name: /^Continue$/ }).click(); + const codeAsked = page.getByText(/code from your authenticator app/i); + await Promise.race([ + linkInput.waitFor({ timeout: 30_000 }), + codeAsked.waitFor({ timeout: 30_000 }), + ]); + if (!(await linkInput.count())) { + if (!(await pw.inputValue())) await pw.fill(PASSWORD); + await form.locator('input:not([type=password])').last().fill(await freshTotp()); + await form.getByRole('button', { name: /^Continue$/ }).click(); + await linkInput.waitFor({ timeout: 30_000 }); + } + } + const link = await linkInput.inputValue(); + if (!link.startsWith('bulwarkmail://pair?')) throw new Error('no pairing link on the page'); + log(`pair: code for ${user} after ${Date.now() - started} ms`); + return link; + } catch (err) { + // What the webmail showed, for the run's artifacts (passwords are masked + // on the page; a TOTP code is dead in 30 s). + if (page && process.env.HELPER_SHOTS) { + await page.screenshot({ path: path.join(process.env.HELPER_SHOTS, `pair-${user}-${Date.now()}.png`), fullPage: true }).catch(() => undefined); + } + throw err; + } finally { + await browser.close(); + } +} + +// ── JMAP as userb (or any e2e account) ───────────────────────────────────── + +const sessions = new Map(); +async function jmapSession(user) { + if (sessions.has(user)) return sessions.get(user); + const pass = user === 'userb' && process.env.E2E_USERB_PASSWORD ? process.env.E2E_USERB_PASSWORD : PASSWORD; + const auth = 'Basic ' + Buffer.from(`${user}@${DOMAIN}:${pass}`).toString('base64'); + const res = await fetch(`${SERVER}/.well-known/jmap`, { headers: { Authorization: auth } }); + if (!res.ok) throw new Error(`session for ${user}: HTTP ${res.status}`); + const session = await res.json(); + const s = { + auth, + apiUrl: session.apiUrl, + uploadUrl: session.uploadUrl, + accountId: session.primaryAccounts['urn:ietf:params:jmap:mail'], + }; + sessions.set(user, s); + return s; +} + +async function jmap(user, calls) { + const s = await jmapSession(user); + const res = await fetch(s.apiUrl, { + method: 'POST', + headers: { Authorization: s.auth, 'Content-Type': 'application/json' }, + body: JSON.stringify({ + using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail', 'urn:ietf:params:jmap:submission'], + methodCalls: calls, + }), + }); + if (!res.ok) throw new Error(`JMAP as ${user}: HTTP ${res.status}`); + return (await res.json()).methodResponses; +} + +async function upload(user, data, type) { + const s = await jmapSession(user); + const res = await fetch(s.uploadUrl.replace('{accountId}', s.accountId), { + method: 'POST', + headers: { Authorization: s.auth, 'Content-Type': type }, + body: data, + }); + if (!res.ok) throw new Error(`upload as ${user}: HTTP ${res.status}`); + return (await res.json()).blobId; +} + +async function sendMail({ from = 'userb', to, subject, text, html, attach }) { + const s = await jmapSession(from); + const [[, boxes], [, ids]] = await jmap(from, [ + ['Mailbox/get', { accountId: s.accountId, properties: ['role'] }, '0'], + ['Identity/get', { accountId: s.accountId }, '1'], + ]); + const drafts = boxes.list.find((m) => m.role === 'drafts')?.id; + const sent = boxes.list.find((m) => m.role === 'sent')?.id; + const identity = ids.list.find((i) => i.email === `${from}@${DOMAIN}`) ?? ids.list[0]; + const email = { + mailboxIds: { [drafts]: true }, + keywords: { $draft: true }, + from: [{ name: identity.name || from, email: `${from}@${DOMAIN}` }], + to: [{ email: to.includes('@') ? to : `${to}@${DOMAIN}` }], + subject, + bodyValues: { t: { value: text || `Test message "${subject}" from the iOS simulator tests.\n` } }, + textBody: [{ partId: 't', type: 'text/plain' }], + }; + if (html) { + email.bodyValues.h = { value: `

${subject}

An HTML test message with a link.

cell 1cell 2
` }; + email.htmlBody = [{ partId: 'h', type: 'text/html' }]; + } + if (attach) { + const pdf = Buffer.from( + '%PDF-1.4\n1 0 obj<>endobj 2 0 obj<>endobj ' + + '3 0 obj<>>>>>endobj ' + + '4 0 obj<>stream\nBT /F1 18 Tf 20 70 Td (E2E attachment) Tj ET\nendstream endobj ' + + '5 0 obj<>endobj\ntrailer<>\n%%EOF\n', + ); + const blobId = await upload(from, pdf, 'application/pdf'); + email.attachments = [{ blobId, type: 'application/pdf', name: 'e2e-attachment.pdf', disposition: 'attachment' }]; + } + const resp = await jmap(from, [ + ['Email/set', { accountId: s.accountId, create: { m: email } }, '0'], + ['EmailSubmission/set', { + accountId: s.accountId, + create: { s: { identityId: identity.id, emailId: '#m' } }, + onSuccessUpdateEmail: { '#s': { [`mailboxIds/${drafts}`]: null, [`mailboxIds/${sent}`]: true, 'keywords/$draft': null } }, + }, '1'], + ]); + const created = resp.find(([name, args]) => name === 'EmailSubmission/set' && args.created?.s); + if (!created) throw new Error(`send failed: ${JSON.stringify(resp).slice(0, 400)}`); + log(`send: ${from} -> ${to} "${subject}"`); +} + +async function findMail({ as = 'userb', subject, timeout = 30 }) { + const s = await jmapSession(as); + const deadline = Date.now() + timeout * 1000; + for (;;) { + const [[, q], [, got]] = await jmap(as, [ + ['Email/query', { accountId: s.accountId, filter: { subject }, sort: [{ property: 'receivedAt', isAscending: false }], limit: 5 }, '0'], + ['Email/get', { accountId: s.accountId, '#ids': { resultOf: '0', name: 'Email/query', path: '/ids' }, properties: ['subject', 'attachments', 'mailboxIds', 'from'] }, '1'], + ]); + const hit = got.list.find((m) => m.subject === subject); + if (hit) { + return { + found: true, + attachments: (hit.attachments || []).map((a) => a.name), + from: hit.from?.[0]?.email ?? null, + }; + } + if (Date.now() > deadline) return { found: false, total: q.ids.length }; + await sleep(2000); + } +} + +// ── server ────────────────────────────────────────────────────────────────── + +const routes = { + async '/health'() { + return { ok: true }; + }, + async '/totp'(q) { + return { code: await freshTotp(Number(q.get('minLeft') || 10)) }; + }, + async '/pair'(q) { + const user = q.get('user'); + const how = q.get('deliver') || 'openurl'; + const link = await createPairingLink(user); + lastLinks.set(user, link); + await deliver(link, how); + return { ok: true }; + }, + async '/redeliver'(q) { + const link = lastLinks.get(q.get('user')); + if (!link) throw new Error('no link for that user yet'); + await deliver(link, q.get('deliver') || 'openurl'); + return { ok: true }; + }, + async '/pair-later'(q) { + const user = q.get('user'); + const how = q.get('deliver') || 'openurl'; + const delay = Number(q.get('delay') || 130); + const link = await createPairingLink(user); + lastLinks.set(user, link); + setTimeout(() => { + deliver(link, how).then(() => log(`pair-later: delivered after ${delay}s`), (e) => log('pair-later:', e.message)); + }, delay * 1000); + return { ok: true, deliverIn: delay }; + }, + async '/send'(q) { + await sendMail({ + from: q.get('from') || 'userb', + to: q.get('to'), + subject: q.get('subject'), + text: q.get('text') || undefined, + html: q.get('html') === '1', + attach: q.get('attach') === '1', + }); + return { ok: true }; + }, + async '/find'(q) { + return findMail({ as: q.get('as') || 'userb', subject: q.get('subject'), timeout: Number(q.get('timeout') || 30) }); + }, + async '/openurl'(q) { + await simctl(['openurl', UDID, q.get('url')]); + return { ok: true }; + }, + async '/clipboard'(q) { + await simctl(['pbcopy', UDID], q.get('text') || ''); + return { ok: true }; + }, +}; + +http.createServer(async (req, res) => { + const url = new URL(req.url, 'http://localhost'); + const route = routes[url.pathname]; + res.setHeader('Content-Type', 'application/json'); + if (!route) { + res.statusCode = 404; + res.end(JSON.stringify({ error: 'not found' })); + return; + } + try { + const body = await route(url.searchParams); + log(`${url.pathname} ok`); + res.end(JSON.stringify(body)); + } catch (err) { + log(`${url.pathname} failed: ${err.message}`); + res.statusCode = 500; + res.end(JSON.stringify({ error: err.message })); + } +}).listen(PORT, '127.0.0.1', () => log(`e2e helper on :${PORT}`)); diff --git a/scripts/e2e/run-flows.sh b/scripts/e2e/run-flows.sh new file mode 100644 index 00000000..d11dddad --- /dev/null +++ b/scripts/e2e/run-flows.sh @@ -0,0 +1,95 @@ +#!/usr/bin/env bash +# Run one suite of Maestro flows against the booted simulator ($UDID). +# +# scripts/e2e/run-flows.sh [flow names, comma separated] +# +# Needs in the environment: UDID, OUT (output folder), and for the flows the +# E2E_* values (see .github/workflows/e2e-ios.yml). Output: $OUT/report.xml, +# $OUT/screenshots, $OUT/run.mp4, $OUT/app.log. +# +# Secrets: Maestro's debug output and the JSON under ~/.maestro/tests hold +# every typed text, passwords included, and artifacts of this public repo are +# public. Only the warm-up (which types a made-up address) prints a debug +# log; afterwards only PNGs are copied, and every text file in $OUT is +# scrubbed of the secret values before upload. +set -uo pipefail +SUITE=$1 +ONLY=${2:-} +ROOT=$(cd "$(dirname "$0")/../.." && pwd) +mkdir -p "$OUT/screenshots" + +maestro --version + +xcrun simctl io "$UDID" recordVideo --codec=h264 --force "$OUT/run.mp4" & +RECORDER=$! + +# Warm-up: the XCTest driver takes minutes to start on a cold runner. +touch "$RUNNER_TEMP/flows-started" +(cd "$OUT/screenshots" && maestro --device "$UDID" test \ + --debug-output "$RUNNER_TEMP/maestro-warmup" "$ROOT/.maestro/launch.yaml") +STATUS=$? +if [ $STATUS -ne 0 ]; then + echo "::group::Maestro warm-up log" + find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec tail -n 300 {} \; + echo "::endgroup::" + maestro --device "$UDID" hierarchy > "$OUT/hierarchy-warmup.json" 2>/dev/null || true +fi + +if [ $STATUS -eq 0 ]; then + ARGS=( + -e "SERVER=$E2E_SERVER" + -e "PASSWORD=$E2E_PASSWORD" + -e "ACCOUNT=${E2E_ACCOUNT:-}" + -e "ACCOUNT2=${E2E_ACCOUNT2:-}" + -e "TOTP_ACCOUNT=${E2E_TOTP_ACCOUNT:-}" + -e "WEBMAIL=${E2E_WEBMAIL:-}" + -e "HELPER=http://127.0.0.1:${HELPER_PORT:-8787}" + -e "ORIENTATION=${ORIENTATION:-portrait}" + -e "DEVICE=${DEVICE:-phone}" + -e "APPEARANCE=${APPEARANCE:-light}" + ) + TARGETS=() + if [ -n "$ONLY" ]; then + IFS=',' read -ra NAMES <<< "$ONLY" + for n in "${NAMES[@]}"; do + f="$ROOT/.maestro/$SUITE/${n%.yaml}.yaml" + [ -f "$f" ] && TARGETS+=("$f") || echo "::warning::no flow $SUITE/$n" + done + else + TARGETS=("$ROOT/.maestro/$SUITE") + fi + if [ ${#TARGETS[@]} -gt 0 ]; then + (cd "$OUT/screenshots" && maestro --device "$UDID" test \ + --format junit --output "$OUT/report.xml" \ + "${ARGS[@]}" "${TARGETS[@]}") + STATUS=$? + fi +fi + +kill -INT "$RECORDER" 2>/dev/null || true +wait "$RECORDER" 2>/dev/null || true + +# Screenshots of failed steps land under ~/.maestro/tests. Images only, plus +# a summary of the steps without their arguments (see failed-steps.mjs). +find "$HOME/.maestro/tests" -name '*.png' -exec cp {} "$OUT/screenshots/" \; 2>/dev/null || true +node "$ROOT/scripts/e2e/failed-steps.mjs" "$HOME/.maestro/tests" > "$OUT/steps.txt" 2>&1 || true + +xcrun simctl spawn "$UDID" log show --last 60m --style compact \ + --predicate 'process == "BulwarkMobile"' > "$OUT/app.log" 2>&1 || true + +# Scrub secrets from every text file that gets uploaded. +for f in $(find "$OUT" -type f \( -name '*.xml' -o -name '*.log' -o -name '*.json' -o -name '*.txt' \)); do + node -e ' + const fs = require("fs"); + const f = process.argv[1]; + let s = fs.readFileSync(f, "utf8"); + for (const k of ["E2E_PASSWORD", "E2E_USERB_PASSWORD", "E2E_TOTP_SECRET"]) { + const v = process.env[k]; + if (v && v.length >= 6) s = s.split(v).join("***"); + } + fs.writeFileSync(f, s); + ' "$f" +done + +ls -la "$OUT/screenshots" | tail -n +2 | wc -l | xargs echo "screenshots:" +exit $STATUS From de414f7ed6bd19e98a26a940b1c4e884df53bd28 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:15:35 +0200 Subject: [PATCH 267/334] ci: run each ios flow in its own maestro process and add the feature flows --- .maestro/features/01-folders.yaml | 38 ++++++ .maestro/features/02-read-messages.yaml | 71 ++++++++++++ .maestro/features/03-compose-send.yaml | 62 ++++++++++ .../features/04-incoming-reply-forward.yaml | 69 +++++++++++ .maestro/features/05-search.yaml | 27 +++++ .maestro/features/06-swipe-undo.yaml | 36 ++++++ .maestro/features/07-move-delete-tag.yaml | 61 ++++++++++ .maestro/features/08-scheduled-send.yaml | 57 +++++++++ .maestro/features/09-calendar.yaml | 98 ++++++++++++++++ .maestro/features/10-contacts.yaml | 66 +++++++++++ .maestro/features/11-files.yaml | 58 ++++++++++ .maestro/features/12-settings.yaml | 38 ++++++ .maestro/launch.yaml | 1 + .maestro/signin/02-password-errors.yaml | 2 + .maestro/signin/16-add-password.yaml | 1 - .maestro/signin/17-add-oauth.yaml | 1 - .maestro/signin/18-add-webmail.yaml | 2 +- .maestro/signin/19-add-link.yaml | 1 - .maestro/subflows/assert-delivered.yaml | 18 +++ .maestro/subflows/check-active-account.yaml | 15 +-- .maestro/subflows/close-message.yaml | 8 ++ .maestro/subflows/compose-body.yaml | 19 +++ .maestro/subflows/go-to-folder.yaml | 8 ++ .maestro/subflows/go-to-inbox.yaml | 16 +++ .maestro/subflows/manual-server.yaml | 1 + .maestro/subflows/open-add-account.yaml | 8 +- .maestro/subflows/open-drawer.yaml | 12 ++ .maestro/subflows/open-message.yaml | 13 +++ .maestro/subflows/settings-page.yaml | 24 ++++ .maestro/subflows/sign-out.yaml | 4 +- .maestro/subflows/signed-in.yaml | 28 +++++ .maestro/subflows/type-secret.yaml | 12 +- .maestro/subflows/type.yaml | 51 +++++--- scripts/e2e/boot-sim.sh | 2 + scripts/e2e/run-flows.sh | 109 ++++++++++++------ 35 files changed, 962 insertions(+), 75 deletions(-) create mode 100644 .maestro/features/01-folders.yaml create mode 100644 .maestro/features/02-read-messages.yaml create mode 100644 .maestro/features/03-compose-send.yaml create mode 100644 .maestro/features/04-incoming-reply-forward.yaml create mode 100644 .maestro/features/05-search.yaml create mode 100644 .maestro/features/06-swipe-undo.yaml create mode 100644 .maestro/features/07-move-delete-tag.yaml create mode 100644 .maestro/features/08-scheduled-send.yaml create mode 100644 .maestro/features/09-calendar.yaml create mode 100644 .maestro/features/10-contacts.yaml create mode 100644 .maestro/features/11-files.yaml create mode 100644 .maestro/features/12-settings.yaml create mode 100644 .maestro/subflows/assert-delivered.yaml create mode 100644 .maestro/subflows/close-message.yaml create mode 100644 .maestro/subflows/compose-body.yaml create mode 100644 .maestro/subflows/go-to-folder.yaml create mode 100644 .maestro/subflows/go-to-inbox.yaml create mode 100644 .maestro/subflows/open-drawer.yaml create mode 100644 .maestro/subflows/open-message.yaml create mode 100644 .maestro/subflows/settings-page.yaml create mode 100644 .maestro/subflows/signed-in.yaml diff --git a/.maestro/features/01-folders.yaml b/.maestro/features/01-folders.yaml new file mode 100644 index 00000000..406d27ae --- /dev/null +++ b/.maestro/features/01-folders.yaml @@ -0,0 +1,38 @@ +# Mailbox list and folders: the drawer (or docked sidebar) lists the role +# folders and custom ones; opening a folder shows its messages. +appId: org.bulwarkmail.mobile +name: Folders +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- takeScreenshot: folders-01-inbox +- assertVisible: ".*Your verification code.*" +- runFlow: ../subflows/open-drawer.yaml +- takeScreenshot: folders-02-drawer +- assertVisible: "Inbox.*" +- assertVisible: "Sent.*" +- assertVisible: "Drafts.*" +- assertVisible: "Archive.*" +- assertVisible: "Projects.*" +- tapOn: "Archive.*" +- extendedWaitUntil: + visible: ".*Invoice 2025-12.*" + timeout: 20000 +- takeScreenshot: folders-03-archive +- runFlow: ../subflows/open-drawer.yaml +# Projects/Alpha holds the seeded thread. +- runFlow: + when: + notVisible: "Alpha.*" + commands: + - tapOn: + text: "Expand" + rightOf: "Projects.*" + optional: true +- tapOn: "Alpha.*" +- extendedWaitUntil: + visible: ".*Project Alpha kickoff.*" + timeout: 20000 +- takeScreenshot: folders-04-alpha +- runFlow: ../subflows/go-to-inbox.yaml diff --git a/.maestro/features/02-read-messages.yaml b/.maestro/features/02-read-messages.yaml new file mode 100644 index 00000000..7dd19dfe --- /dev/null +++ b/.maestro/features/02-read-messages.yaml @@ -0,0 +1,71 @@ +# Opening messages: plain text, HTML with blocked remote content, a thread, +# and a PDF attachment in the in-app preview. +appId: org.bulwarkmail.mobile +name: Read messages +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml + +# Plain text +- runFlow: + file: ../subflows/open-message.yaml + env: + SUBJECT: Grüße aus München +- extendedWaitUntil: + visible: ".*Jürgen.*" + timeout: 10000 +- takeScreenshot: read-01-plain +- runFlow: ../subflows/close-message.yaml + +# HTML newsletter +- runFlow: + file: ../subflows/open-message.yaml + env: + SUBJECT: Weekly digest +- extendedWaitUntil: + visible: ".*5 new articles.*|Weekly digest" + timeout: 20000 +- takeScreenshot: read-02-html +- runFlow: + when: + visible: "Load images" + commands: + - tapOn: "Load images" + - takeScreenshot: read-03-html-images +- runFlow: ../subflows/close-message.yaml + +# Attachment and its preview +- runFlow: + file: ../subflows/open-message.yaml + env: + SUBJECT: Q3 report +- extendedWaitUntil: + visible: ".*q3-report.pdf.*" + timeout: 20000 +- takeScreenshot: read-04-attachment +- tapOn: ".*q3-report.pdf.*" +- extendedWaitUntil: + visible: "Share" + timeout: 30000 +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: read-05-attachment-preview +- assertVisible: "q3-report.pdf" +- tapOn: "Close" +- extendedWaitUntil: + visible: "Reply All" + timeout: 10000 +- runFlow: ../subflows/close-message.yaml + +# A thread (Projects/Alpha) +- runFlow: + file: ../subflows/go-to-folder.yaml + env: + FOLDER: "Alpha.*" +- runFlow: + file: ../subflows/open-message.yaml + env: + SUBJECT: Project Alpha kickoff +- takeScreenshot: read-06-thread +- runFlow: ../subflows/go-to-inbox.yaml diff --git a/.maestro/features/03-compose-send.yaml b/.maestro/features/03-compose-send.yaml new file mode 100644 index 00000000..24810e57 --- /dev/null +++ b/.maestro/features/03-compose-send.yaml @@ -0,0 +1,62 @@ +# Compose a new message with a photo attached and send it to userb; userb's +# mailbox (checked over JMAP) must get it with the attachment. +appId: org.bulwarkmail.mobile +name: Compose and send with attachment +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- evalScript: ${output.subject = 'E2E compose ' + DEVICE + '-' + APPEARANCE + '-' + ORIENTATION + ' ' + Date.now()} +- tapOn: "Compose" +- extendedWaitUntil: + visible: "New Message" + timeout: 15000 +- tapOn: "Recipient email addresses" +- inputText: "userb@example.org" +- pressKey: Enter +- tapOn: + text: "Subject" + index: 1 +- inputText: ${output.subject} +- runFlow: + file: ../subflows/compose-body.yaml + env: + BODY: "Hello from the iOS simulator test." +- takeScreenshot: compose-01-filled + +# Attach a photo from the library (boot-sim.sh adds one). +- tapOn: "Attach" +- extendedWaitUntil: + visible: "Photos & Videos" + timeout: 10000 +- tapOn: "Photos & Videos" +- extendedWaitUntil: + visible: "Photos|Cancel" + timeout: 20000 +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: compose-02-photo-picker +- tapOn: + text: "Photo.*" + index: 0 +- runFlow: + when: + visible: "Add|Done" + commands: + - tapOn: "Add|Done" +- extendedWaitUntil: + visible: ".*(png|jpg|jpeg|heic|PNG|JPG|JPEG|HEIC).*" + timeout: 60000 +- takeScreenshot: compose-03-attached + +- tapOn: "Send" +- extendedWaitUntil: + visible: "Email sent successfully" + timeout: 60000 +- runFlow: + file: ../subflows/assert-delivered.yaml + env: + SUBJECT: ${output.subject} +- assertTrue: + condition: ${output.helper.attachments.length > 0} + label: "the sent message carries the photo" diff --git a/.maestro/features/04-incoming-reply-forward.yaml b/.maestro/features/04-incoming-reply-forward.yaml new file mode 100644 index 00000000..252276ae --- /dev/null +++ b/.maestro/features/04-incoming-reply-forward.yaml @@ -0,0 +1,69 @@ +# Incoming mail while the app is open (sent by userb over JMAP), then reply +# and forward it; userb receives both, the forward with the attachment. +appId: org.bulwarkmail.mobile +name: Incoming, reply, forward +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- evalScript: ${output.incoming = 'E2E incoming ' + DEVICE + '-' + APPEARANCE + '-' + ORIENTATION + ' ' + Date.now()} +- runScript: + file: ../scripts/helper.js + env: + CALL: /send?to=${ACCOUNT}&subject=${encodeURIComponent(output.incoming)}&attach=1&html=1 +# Arrives without a pull to refresh (push over EventSource). +- extendedWaitUntil: + visible: ".*${output.incoming}.*" + timeout: 90000 +- takeScreenshot: incoming-01-arrived +- runFlow: + file: ../subflows/open-message.yaml + env: + SUBJECT: ${output.incoming} +- takeScreenshot: incoming-02-open + +# Reply +- tapOn: + text: "Reply" + index: 0 +- extendedWaitUntil: + visible: "Re: ${output.incoming}" + timeout: 15000 +- runFlow: + file: ../subflows/compose-body.yaml + env: + BODY: "Reply from the iOS simulator test." +- takeScreenshot: incoming-03-reply +- tapOn: "Send" +- extendedWaitUntil: + visible: "Email sent successfully" + timeout: 60000 +- runFlow: + file: ../subflows/assert-delivered.yaml + env: + SUBJECT: "Re: ${output.incoming}" + +# Forward with the attachment +- extendedWaitUntil: + visible: "Forward" + timeout: 15000 +- tapOn: + text: "Forward" + index: 0 +- extendedWaitUntil: + visible: "Fwd: ${output.incoming}" + timeout: 15000 +- tapOn: "Recipient email addresses" +- inputText: "userb@example.org" +- pressKey: Enter +- takeScreenshot: incoming-04-forward +- tapOn: "Send" +- extendedWaitUntil: + visible: "Email sent successfully" + timeout: 60000 +- runFlow: + file: ../subflows/assert-delivered.yaml + env: + SUBJECT: "Fwd: ${output.incoming}" + ATTACHMENT: e2e-attachment.pdf +- runFlow: ../subflows/close-message.yaml diff --git a/.maestro/features/05-search.yaml b/.maestro/features/05-search.yaml new file mode 100644 index 00000000..309918c2 --- /dev/null +++ b/.maestro/features/05-search.yaml @@ -0,0 +1,27 @@ +# Search: a hit, a miss, clearing. +appId: org.bulwarkmail.mobile +name: Search +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- tapOn: "Search mail..." +- inputText: "verification" +- pressKey: Enter +- extendedWaitUntil: + visible: ".*Your verification code.*" + timeout: 30000 +- assertNotVisible: ".*Weekly digest.*" +- takeScreenshot: search-01-hit +- tapOn: "Clear search" +- inputText: "zzqx-no-such-mail" +- pressKey: Enter +- extendedWaitUntil: + visible: "No results found" + timeout: 30000 +- takeScreenshot: search-02-miss +- tapOn: "Clear" +- extendedWaitUntil: + visible: ".*Weekly digest.*" + timeout: 30000 +- hideKeyboard diff --git a/.maestro/features/06-swipe-undo.yaml b/.maestro/features/06-swipe-undo.yaml new file mode 100644 index 00000000..126bbd0d --- /dev/null +++ b/.maestro/features/06-swipe-undo.yaml @@ -0,0 +1,36 @@ +# Swipe actions with undo: swipe left archives (undo brings it back), swipe +# right toggles read. +appId: org.bulwarkmail.mobile +name: Swipe actions and undo +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- swipe: + from: ".*Grüße aus München.*" + direction: LEFT + duration: 400 +- extendedWaitUntil: + visible: "Email archived" + timeout: 10000 +- takeScreenshot: swipe-01-archived +- assertNotVisible: ".*Grüße aus München.*" +- tapOn: "UNDO" +- extendedWaitUntil: + visible: ".*Grüße aus München.*" + timeout: 15000 +- takeScreenshot: swipe-02-undone + +- swipe: + from: ".*Grüße aus München.*" + direction: RIGHT + duration: 400 +- waitForAnimationToEnd: + timeout: 3000 +- takeScreenshot: swipe-03-read-toggled +- swipe: + from: ".*Grüße aus München.*" + direction: RIGHT + duration: 400 +- waitForAnimationToEnd: + timeout: 3000 diff --git a/.maestro/features/07-move-delete-tag.yaml b/.maestro/features/07-move-delete-tag.yaml new file mode 100644 index 00000000..b0253bcd --- /dev/null +++ b/.maestro/features/07-move-delete-tag.yaml @@ -0,0 +1,61 @@ +# Message actions with undo: move to a folder, delete to Trash, select and +# star from the list. +appId: org.bulwarkmail.mobile +name: Move, delete, select +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml + +# Move via More → Move to... +- runFlow: + file: ../subflows/open-message.yaml + env: + SUBJECT: Weekly digest +- tapOn: "more" +- extendedWaitUntil: + visible: "More actions" + timeout: 10000 +- takeScreenshot: actions-01-more +- tapOn: "Move to..." +- extendedWaitUntil: + visible: "Move to..." + timeout: 10000 +- tapOn: "Projects" +- extendedWaitUntil: + visible: "Moved to Projects" + timeout: 15000 +- takeScreenshot: actions-02-moved +- tapOn: "UNDO" +- runFlow: ../subflows/close-message.yaml +- extendedWaitUntil: + visible: ".*Weekly digest.*" + timeout: 15000 + +# Delete (to Trash) and undo +- runFlow: + file: ../subflows/open-message.yaml + env: + SUBJECT: Weekly digest +- tapOn: "Delete" +- extendedWaitUntil: + visible: "Email moved to Trash" + timeout: 15000 +- takeScreenshot: actions-03-deleted +- tapOn: "UNDO" +- runFlow: ../subflows/close-message.yaml +- extendedWaitUntil: + visible: ".*Weekly digest.*" + timeout: 15000 + +# Select from the list and star, then unstar +- longPressOn: ".*Grüße aus München.*" +- extendedWaitUntil: + visible: "Clear selection" + timeout: 10000 +- takeScreenshot: actions-04-selected +- tapOn: "Star" +- tapOn: "Clear selection" +- longPressOn: ".*Grüße aus München.*" +- tapOn: "Unstar|Star" +- tapOn: "Clear selection" diff --git a/.maestro/features/08-scheduled-send.yaml b/.maestro/features/08-scheduled-send.yaml new file mode 100644 index 00000000..0f062ecd --- /dev/null +++ b/.maestro/features/08-scheduled-send.yaml @@ -0,0 +1,57 @@ +# Scheduled send: schedule a message for later, find it under Scheduled, +# cancel it. +appId: org.bulwarkmail.mobile +name: Scheduled send +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- evalScript: ${output.subject = 'E2E scheduled ' + DEVICE + '-' + APPEARANCE + '-' + ORIENTATION + ' ' + Date.now()} +- tapOn: "Compose" +- extendedWaitUntil: + visible: "New Message" + timeout: 15000 +- tapOn: "Recipient email addresses" +- inputText: "userb@example.org" +- pressKey: Enter +- tapOn: + text: "Subject" + index: 1 +- inputText: ${output.subject} +- runFlow: + file: ../subflows/compose-body.yaml + env: + BODY: "Scheduled from the iOS simulator test." +- tapOn: "Schedule send" +- extendedWaitUntil: + visible: "Tomorrow morning.*" + timeout: 10000 +- takeScreenshot: scheduled-01-options +- tapOn: "Tomorrow morning.*" +- extendedWaitUntil: + visible: "Scheduled" + timeout: 30000 +- takeScreenshot: scheduled-02-confirmed +- tapOn: "OK" + +- runFlow: + file: ../subflows/go-to-folder.yaml + env: + FOLDER: Scheduled +- extendedWaitUntil: + visible: ".*${output.subject}.*" + timeout: 30000 +- takeScreenshot: scheduled-03-list +- tapOn: ".*${output.subject}.*" +- tapOn: "Cancel send" +- extendedWaitUntil: + visible: "Keep scheduled" + timeout: 10000 +- tapOn: + text: "Cancel send" + below: "Keep scheduled|.*will not be delivered.*" +- extendedWaitUntil: + notVisible: ".*${output.subject}.*" + timeout: 30000 +- takeScreenshot: scheduled-04-cancelled +- runFlow: ../subflows/go-to-inbox.yaml diff --git a/.maestro/features/09-calendar.yaml b/.maestro/features/09-calendar.yaml new file mode 100644 index 00000000..c1c8de93 --- /dev/null +++ b/.maestro/features/09-calendar.yaml @@ -0,0 +1,98 @@ +# Calendar: the views, create an event with a reminder, edit it, delete it. +appId: org.bulwarkmail.mobile +name: Calendar +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- evalScript: ${output.event = 'E2E event ' + DEVICE + ' ' + Date.now() % 100000} +- tapOn: + id: tab-calendar +- extendedWaitUntil: + visible: "Go to today" + timeout: 30000 +- takeScreenshot: calendar-01-month + +- tapOn: "Open menu" +- tapOn: "Schedule" +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: calendar-02-schedule +- tapOn: "Open menu" +- tapOn: "Day" +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: calendar-03-day +- tapOn: "Open menu" +- tapOn: "Week" +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: calendar-04-week + +# Create +- tapOn: "New event" +- extendedWaitUntil: + visible: "Save" + timeout: 10000 +- tapOn: "Title" +- inputText: ${output.event} +- hideKeyboard +- scrollUntilVisible: + element: "Add reminder" + direction: DOWN + timeout: 10000 +- tapOn: "Add reminder" +- tapOn: "15 minutes before" +- takeScreenshot: calendar-05-editor +- tapOn: "Save" +- extendedWaitUntil: + notVisible: "New event" + timeout: 20000 + +# Find it in the schedule and open it +- tapOn: "Open menu" +- tapOn: "Schedule" +- tapOn: "Go to today" +- scrollUntilVisible: + element: ".*${output.event}.*" + direction: DOWN + timeout: 30000 +- tapOn: ".*${output.event}.*" +- extendedWaitUntil: + visible: "Edit" + timeout: 10000 +- takeScreenshot: calendar-06-detail +- assertVisible: ".*15 minutes before.*" + +# Edit the title +- tapOn: "Edit" +- extendedWaitUntil: + visible: "Edit event" + timeout: 10000 +- tapOn: ${output.event} +- inputText: " edited" +- hideKeyboard +- tapOn: "Save" +- extendedWaitUntil: + visible: ".*${output.event} edited.*" + timeout: 20000 +- takeScreenshot: calendar-07-edited + +# Delete +- tapOn: ".*${output.event} edited.*" +- extendedWaitUntil: + visible: "Delete" + timeout: 10000 +- tapOn: "Delete" +- extendedWaitUntil: + visible: "Delete this event.*" + timeout: 10000 +- tapOn: + text: "Delete" + below: "Delete this event.*" +- extendedWaitUntil: + notVisible: ".*${output.event} edited.*" + timeout: 20000 +- takeScreenshot: calendar-08-deleted +- tapOn: + id: tab-mail diff --git a/.maestro/features/10-contacts.yaml b/.maestro/features/10-contacts.yaml new file mode 100644 index 00000000..6d6c4779 --- /dev/null +++ b/.maestro/features/10-contacts.yaml @@ -0,0 +1,66 @@ +# Contacts: the list, a contact's detail, create one and delete it again. +appId: org.bulwarkmail.mobile +name: Contacts +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- evalScript: ${output.last = 'Tester' + Date.now() % 100000} +- tapOn: + id: tab-contacts +- extendedWaitUntil: + visible: "All Contacts" + timeout: 30000 +- takeScreenshot: contacts-01-list +- tapOn: ".*Alice.*" +- extendedWaitUntil: + visible: "Email" + timeout: 10000 +- takeScreenshot: contacts-02-detail +- runFlow: + when: + visible: "Back" + commands: + - tapOn: "Back" + +- tapOn: "New Contact" +- extendedWaitUntil: + visible: "Save" + timeout: 10000 +- tapOn: + text: "First name" + index: 1 +- inputText: "E2E" +- tapOn: + text: "Last name" + index: 1 +- inputText: ${output.last} +- tapOn: "email@example.com" +- inputText: "e2e.contact@example.net" +- hideKeyboard +- takeScreenshot: contacts-03-form +- tapOn: "Save" +- extendedWaitUntil: + visible: ".*${output.last}.*" + timeout: 20000 +- takeScreenshot: contacts-04-created + +- runFlow: + when: + notVisible: "More actions" + commands: + - tapOn: ".*E2E ${output.last}.*" +- tapOn: "More actions" +- tapOn: "Delete" +- extendedWaitUntil: + visible: "Delete contact" + timeout: 10000 +- tapOn: + text: "Delete" + below: "Are you sure.*" +- extendedWaitUntil: + notVisible: ".*E2E ${output.last}.*" + timeout: 20000 +- takeScreenshot: contacts-05-deleted +- tapOn: + id: tab-mail diff --git a/.maestro/features/11-files.yaml b/.maestro/features/11-files.yaml new file mode 100644 index 00000000..b5e4ad49 --- /dev/null +++ b/.maestro/features/11-files.yaml @@ -0,0 +1,58 @@ +# Files: open a folder, preview a text and a PDF file, create and delete a +# folder. +appId: org.bulwarkmail.mobile +name: Files +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- evalScript: ${output.folder = 'E2E folder ' + Date.now() % 100000} +- tapOn: + id: tab-files +- extendedWaitUntil: + visible: ".*Documents.*" + timeout: 30000 +- takeScreenshot: files-01-root +- tapOn: ".*Documents.*" +- extendedWaitUntil: + visible: ".*readme.txt.*" + timeout: 20000 +- tapOn: ".*readme.txt.*" +- extendedWaitUntil: + visible: "readme.txt" + timeout: 20000 +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: files-02-text-preview +- tapOn: "Close" +- tapOn: ".*q3-report.pdf.*" +- extendedWaitUntil: + visible: "q3-report.pdf" + timeout: 20000 +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: files-03-pdf-preview +- tapOn: "Close" +- tapOn: "Back" + +- tapOn: "New Folder" +- tapOn: "Folder name" +- inputText: ${output.folder} +- tapOn: "Create" +- extendedWaitUntil: + visible: ".*${output.folder}.*" + timeout: 20000 +- takeScreenshot: files-04-folder-created +- longPressOn: ".*${output.folder}.*" +- tapOn: "Delete" +- extendedWaitUntil: + visible: "Delete this item.*" + timeout: 10000 +- tapOn: + text: "Delete" + below: "Delete this item.*" +- extendedWaitUntil: + notVisible: ".*${output.folder}.*" + timeout: 20000 +- tapOn: + id: tab-mail diff --git a/.maestro/features/12-settings.yaml b/.maestro/features/12-settings.yaml new file mode 100644 index 00000000..5af6250a --- /dev/null +++ b/.maestro/features/12-settings.yaml @@ -0,0 +1,38 @@ +# Every settings page opens and shows its content. +appId: org.bulwarkmail.mobile +name: Settings pages +tags: + - features +--- +- runFlow: ../subflows/signed-in.yaml +- tapOn: + id: tab-settings +- extendedWaitUntil: + visible: "Search settings" + timeout: 20000 +- takeScreenshot: settings-00-list +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: account, ROW: ".*@example.org, .*", PROOF: "Logged-in accounts|Display Name"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: language, ROW: "Language, Region & Time, .*", PROOF: "Date Format"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: notifications, ROW: "Notifications, .*", PROOF: "Email Notifications"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: appearance, ROW: "Appearance, .*", PROOF: "Choose your preferred color scheme"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: layout, ROW: "Layout, .*", PROOF: "Tune the email list interactions for mobile."}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: reading, ROW: "Reading, .*", PROOF: "Mark as Read"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: composing, ROW: "Composing, .*", PROOF: "Composer"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: identities, ROW: "Identities, .*", PROOF: "Sending Identities"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: vacation, ROW: "Out of Office, .*", PROOF: "Reply automatically"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: filters, ROW: "Filters, .*", PROOF: "Email Filters"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: templates, ROW: "Templates, .*", PROOF: "Email Templates"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: folders, ROW: "Folders, .*", PROOF: ".*Inbox.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: tags, ROW: "Tags, .*", PROOF: "Email Tags"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: downloads, ROW: "Downloads, .*", PROOF: "Email .*eml.* filename"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: security, ROW: "Security, .*", PROOF: "Account Security|Detecting server capabilities.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: content, ROW: "Content & Senders, .*", PROOF: "External Content"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", PROOF: "Default view"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", PROOF: "Group by first letter"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: files, ROW: "Files, .*", PROOF: "Default View"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: sidebar-apps, ROW: "Sidebar Apps, .*", PROOF: "Keep Apps Loaded"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: themes, ROW: "Themes, .*", PROOF: "The default light and dark palettes."}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: storage, ROW: "Storage & backup, .*", PROOF: "Offline mail.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: about, ROW: "About, .*", PROOF: "This device"}} +- tapOn: + id: tab-mail diff --git a/.maestro/launch.yaml b/.maestro/launch.yaml index 4dfcf872..cf402f84 100644 --- a/.maestro/launch.yaml +++ b/.maestro/launch.yaml @@ -25,5 +25,6 @@ tags: file: subflows/type.yaml env: FIELD: login-server-input + TAP: "no" TEXT: typing-probe.example.net - takeScreenshot: warmup-02-typed diff --git a/.maestro/signin/02-password-errors.yaml b/.maestro/signin/02-password-errors.yaml index 5bbbf2b5..bc0182bf 100644 --- a/.maestro/signin/02-password-errors.yaml +++ b/.maestro/signin/02-password-errors.yaml @@ -30,6 +30,8 @@ tags: file: ../subflows/type.yaml env: FIELD: login-server-input + TAP: "no" + ERASE: "yes" TEXT: mail.does-not-exist.invalid - tapOn: "Continue" - extendedWaitUntil: diff --git a/.maestro/signin/16-add-password.yaml b/.maestro/signin/16-add-password.yaml index 829c1093..6f11a60f 100644 --- a/.maestro/signin/16-add-password.yaml +++ b/.maestro/signin/16-add-password.yaml @@ -26,5 +26,4 @@ tags: file: ../subflows/check-active-account.yaml env: ACTIVE: ${ACCOUNT2}@example.org -- assertVisible: ${ACCOUNT}@example.org - takeScreenshot: add-password-02-two-accounts diff --git a/.maestro/signin/17-add-oauth.yaml b/.maestro/signin/17-add-oauth.yaml index f48ba879..024cc747 100644 --- a/.maestro/signin/17-add-oauth.yaml +++ b/.maestro/signin/17-add-oauth.yaml @@ -38,5 +38,4 @@ tags: file: ../subflows/check-active-account.yaml env: ACTIVE: ${ACCOUNT2}@example.org -- assertVisible: ${ACCOUNT}@example.org - takeScreenshot: add-oauth-01-two-accounts diff --git a/.maestro/signin/18-add-webmail.yaml b/.maestro/signin/18-add-webmail.yaml index 65b2e934..5ea8ebc1 100644 --- a/.maestro/signin/18-add-webmail.yaml +++ b/.maestro/signin/18-add-webmail.yaml @@ -19,6 +19,7 @@ tags: file: ../subflows/type.yaml env: FIELD: login-server-input + TAP: "no" TEXT: ${WEBMAIL} - tapOn: "Continue" - extendedWaitUntil: @@ -39,5 +40,4 @@ tags: file: ../subflows/check-active-account.yaml env: ACTIVE: ${ACCOUNT2}@example.org -- assertVisible: ${ACCOUNT}@example.org - takeScreenshot: add-webmail-01-two-accounts diff --git a/.maestro/signin/19-add-link.yaml b/.maestro/signin/19-add-link.yaml index a786d251..fc183e1b 100644 --- a/.maestro/signin/19-add-link.yaml +++ b/.maestro/signin/19-add-link.yaml @@ -26,5 +26,4 @@ tags: file: ../subflows/check-active-account.yaml env: ACTIVE: ${ACCOUNT2}@example.org -- assertVisible: ${ACCOUNT}@example.org - takeScreenshot: add-link-02-two-accounts diff --git a/.maestro/subflows/assert-delivered.yaml b/.maestro/subflows/assert-delivered.yaml new file mode 100644 index 00000000..d2bfc2fe --- /dev/null +++ b/.maestro/subflows/assert-delivered.yaml @@ -0,0 +1,18 @@ +# The helper checks over JMAP that AS (default userb) received SUBJECT; with +# ATTACHMENT set, that the message carries that file. +appId: org.bulwarkmail.mobile +--- +- runScript: + file: ../scripts/helper.js + env: + CALL: /find?as=${AS || 'userb'}&subject=${encodeURIComponent(SUBJECT)}&timeout=60 +- assertTrue: + condition: ${output.helper.found} + label: "${SUBJECT} delivered to ${AS || 'userb'}" +- runFlow: + when: + true: ${!!ATTACHMENT} + commands: + - assertTrue: + condition: ${output.helper.attachments.indexOf(ATTACHMENT) >= 0} + label: "${SUBJECT} carries ${ATTACHMENT}" diff --git a/.maestro/subflows/check-active-account.yaml b/.maestro/subflows/check-active-account.yaml index d8fd9de3..b0a1fabb 100644 --- a/.maestro/subflows/check-active-account.yaml +++ b/.maestro/subflows/check-active-account.yaml @@ -1,15 +1,16 @@ -# Settings → Account lists the accounts on this device; the active one is -# ACTIVE. Goes back to Mail afterwards. +# The Settings account card names the active account ACTIVE, and the Account +# page lists another account to switch to. Goes back to Mail afterwards. appId: org.bulwarkmail.mobile --- - tapOn: id: tab-settings - extendedWaitUntil: - visible: "Account" - timeout: 10000 -- tapOn: "Account" -- extendedWaitUntil: - visible: ${ACTIVE} + visible: ".*${ACTIVE}.*" + timeout: 20000 +- tapOn: ".*${ACTIVE}.*" +- scrollUntilVisible: + element: "Switch to this account" + direction: DOWN timeout: 20000 - takeScreenshot: accounts-${ACTIVE} - tapOn: diff --git a/.maestro/subflows/close-message.yaml b/.maestro/subflows/close-message.yaml new file mode 100644 index 00000000..1bedad8a --- /dev/null +++ b/.maestro/subflows/close-message.yaml @@ -0,0 +1,8 @@ +# Leave the message view: Back on a phone; the split layout keeps the list. +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + visible: "Back" + commands: + - tapOn: "Back" diff --git a/.maestro/subflows/compose-body.yaml b/.maestro/subflows/compose-body.yaml new file mode 100644 index 00000000..6c1a58c6 --- /dev/null +++ b/.maestro/subflows/compose-body.yaml @@ -0,0 +1,19 @@ +# In the composer: type BODY into the editor. The rich editor is a WebView +# whose placeholder is CSS, so it may not be exposed; then tap the middle of +# the screen, which is the body area below Subject (above the keyboard). +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + visible: "Write your message.*" + commands: + - tapOn: "Write your message.*" +- runFlow: + when: + notVisible: "Write your message.*" + commands: + - tapOn: + point: "50%,50%" +- waitForAnimationToEnd: + timeout: 3000 +- inputText: ${BODY} diff --git a/.maestro/subflows/go-to-folder.yaml b/.maestro/subflows/go-to-folder.yaml new file mode 100644 index 00000000..f660cf30 --- /dev/null +++ b/.maestro/subflows/go-to-folder.yaml @@ -0,0 +1,8 @@ +# Open folder FOLDER from the drawer or docked sidebar. +appId: org.bulwarkmail.mobile +--- +- runFlow: open-drawer.yaml +- tapOn: ${FOLDER} +- extendedWaitUntil: + visible: ${FOLDER} + timeout: 15000 diff --git a/.maestro/subflows/go-to-inbox.yaml b/.maestro/subflows/go-to-inbox.yaml new file mode 100644 index 00000000..4b1b84cd --- /dev/null +++ b/.maestro/subflows/go-to-inbox.yaml @@ -0,0 +1,16 @@ +# Back to the inbox list, from wherever in the Mail tab. +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + visible: "Back" + commands: + - tapOn: "Back" +- runFlow: + file: go-to-folder.yaml + env: + FOLDER: Inbox +- extendedWaitUntil: + visible: + id: email-row + timeout: 60000 diff --git a/.maestro/subflows/manual-server.yaml b/.maestro/subflows/manual-server.yaml index 4959e03b..f10e709c 100644 --- a/.maestro/subflows/manual-server.yaml +++ b/.maestro/subflows/manual-server.yaml @@ -11,6 +11,7 @@ appId: org.bulwarkmail.mobile file: type.yaml env: FIELD: login-server-input + TAP: "no" TEXT: ${HOST} - tapOn: "Continue" - extendedWaitUntil: diff --git a/.maestro/subflows/open-add-account.yaml b/.maestro/subflows/open-add-account.yaml index 92a22102..c6d86cd4 100644 --- a/.maestro/subflows/open-add-account.yaml +++ b/.maestro/subflows/open-add-account.yaml @@ -1,12 +1,14 @@ -# Signed in: Settings → Account → Add account, up to the add-mode choices. +# Signed in: Settings → account card → Account page → Add account, up to the +# add-mode choices. (On the iPad split layout the Account page is already +# open; tapping the card again is harmless.) appId: org.bulwarkmail.mobile --- - tapOn: id: tab-settings - extendedWaitUntil: - visible: "Account" + visible: ".*@example.org, .*" timeout: 10000 -- tapOn: "Account" +- tapOn: ".*@example.org, .*" - scrollUntilVisible: element: "Add account" direction: DOWN diff --git a/.maestro/subflows/open-drawer.yaml b/.maestro/subflows/open-drawer.yaml new file mode 100644 index 00000000..853414fc --- /dev/null +++ b/.maestro/subflows/open-drawer.yaml @@ -0,0 +1,12 @@ +# Show the folder list: the modal drawer, or nothing to do where the sidebar +# is docked (iPad landscape). +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + visible: "Toggle menu" + commands: + - tapOn: "Toggle menu" +- extendedWaitUntil: + visible: "Scheduled" + timeout: 10000 diff --git a/.maestro/subflows/open-message.yaml b/.maestro/subflows/open-message.yaml new file mode 100644 index 00000000..d5e27b35 --- /dev/null +++ b/.maestro/subflows/open-message.yaml @@ -0,0 +1,13 @@ +# From a message list: open the message whose row mentions SUBJECT. +appId: org.bulwarkmail.mobile +--- +- scrollUntilVisible: + element: ".*${SUBJECT}.*" + direction: DOWN + timeout: 30000 +- tapOn: ".*${SUBJECT}.*" +- extendedWaitUntil: + visible: "Reply All" + timeout: 20000 +- waitForAnimationToEnd: + timeout: 5000 diff --git a/.maestro/subflows/settings-page.yaml b/.maestro/subflows/settings-page.yaml new file mode 100644 index 00000000..c55a5502 --- /dev/null +++ b/.maestro/subflows/settings-page.yaml @@ -0,0 +1,24 @@ +# Settings list: scroll to the row ROW (regex on "Title, summary"), open it, +# check PROOF shows, screenshot, and go back on a phone. On the iPad the list +# is a left column, so scroll there instead of the middle of the screen. +appId: org.bulwarkmail.mobile +--- +- repeat: + while: + notVisible: ${ROW} + times: 10 + commands: + - swipe: + start: "${DEVICE == 'tablet' ? '18%,80%' : '50%,80%'}" + end: "${DEVICE == 'tablet' ? '18%,40%' : '50%,40%'}" + duration: 600 +- tapOn: ${ROW} +- extendedWaitUntil: + visible: ${PROOF} + timeout: 20000 +- takeScreenshot: settings-${NAME} +- runFlow: + when: + visible: "Back" + commands: + - tapOn: "Back" diff --git a/.maestro/subflows/sign-out.yaml b/.maestro/subflows/sign-out.yaml index 6f016d4f..e41c0aa4 100644 --- a/.maestro/subflows/sign-out.yaml +++ b/.maestro/subflows/sign-out.yaml @@ -4,10 +4,10 @@ appId: org.bulwarkmail.mobile - tapOn: id: tab-settings - scrollUntilVisible: - element: "Sign Out" + element: "Sign out" direction: DOWN timeout: 30000 -- tapOn: "Sign Out" +- tapOn: "Sign out" - extendedWaitUntil: visible: "The account is removed from this device.*" timeout: 10000 diff --git a/.maestro/subflows/signed-in.yaml b/.maestro/subflows/signed-in.yaml new file mode 100644 index 00000000..0d129495 --- /dev/null +++ b/.maestro/subflows/signed-in.yaml @@ -0,0 +1,28 @@ +# Feature flows start here: the app signed in to ACCOUNT, on the inbox. +# Keeps the app's data between flows; signs in only when needed. +appId: org.bulwarkmail.mobile +--- +- launchApp: + permissions: + notifications: allow + photos: allow + camera: allow +- runFlow: + when: + true: ${ORIENTATION == 'landscape'} + commands: + - setOrientation: LANDSCAPE_LEFT +- extendedWaitUntil: + visible: "Enter server details manually|Inbox" + timeout: 120000 +- runFlow: + when: + visible: "Enter server details manually" + commands: + - runFlow: + file: password-sign-in.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- tapOn: + id: tab-mail +- runFlow: go-to-inbox.yaml diff --git a/.maestro/subflows/type-secret.yaml b/.maestro/subflows/type-secret.yaml index f4bd7a2f..bbde88fe 100644 --- a/.maestro/subflows/type-secret.yaml +++ b/.maestro/subflows/type-secret.yaml @@ -1,17 +1,15 @@ -# Type a secret into the field with id FIELD. Same focus handling as -# type.yaml, but the value can't be checked (a secure field shows dots), and -# it must never be shown, so a lost keystroke surfaces as a failed sign-in. +# Type a secret into the field with id FIELD (TAP=no for a field that focuses +# itself, see type.yaml). The value can't be checked (a secure field shows +# dots) and must never be shown, so a lost keystroke surfaces as a failed +# sign-in. appId: org.bulwarkmail.mobile --- - runFlow: when: - notVisible: - id: ${FIELD} - focused: true + true: ${TAP != 'no'} commands: - tapOn: id: ${FIELD} - waitForAnimationToEnd: timeout: 3000 -- eraseText: 80 - inputText: ${TEXT} diff --git a/.maestro/subflows/type.yaml b/.maestro/subflows/type.yaml index 5ca5d6e8..e7c7f5e4 100644 --- a/.maestro/subflows/type.yaml +++ b/.maestro/subflows/type.yaml @@ -1,25 +1,44 @@ # Type TEXT into the field with id FIELD and check it arrived in full. # # iOS drops keystrokes while the edit menu (the "AutoFill" bubble) opens, -# which happens when a flow taps a field that already has focus; an -# auto-focused field is left alone. Retyped up to twice if text got lost. +# which happens when a flow taps a field that already has focus. Maestro +# can't see the focus of a React Native field, so a caller passes TAP=no for +# a field that focuses itself. Retyped up to twice if text got lost (the +# retry taps: by then the field has focus and the menu is harmless). appId: org.bulwarkmail.mobile --- -- retry: - maxRetries: 2 +- runFlow: + when: + true: ${TAP != 'no'} commands: - - runFlow: - when: - notVisible: - id: ${FIELD} - focused: true + - tapOn: + id: ${FIELD} +- waitForAnimationToEnd: + timeout: 3000 +# ERASE=yes: the field already holds text. +- runFlow: + when: + true: ${ERASE == 'yes'} + commands: + - eraseText: 80 +- inputText: ${TEXT} +- runFlow: + when: + notVisible: + id: ${FIELD} + text: ${TEXT_PATTERN || TEXT} + commands: + - retry: + maxRetries: 2 commands: - tapOn: id: ${FIELD} - - waitForAnimationToEnd: - timeout: 3000 - - eraseText: 80 - - inputText: ${TEXT} - - assertVisible: - id: ${FIELD} - text: ${TEXT_PATTERN || TEXT} + - waitForAnimationToEnd: + timeout: 2000 + - eraseText: 80 + - waitForAnimationToEnd: + timeout: 1000 + - inputText: ${TEXT} + - assertVisible: + id: ${FIELD} + text: ${TEXT_PATTERN || TEXT} diff --git a/scripts/e2e/boot-sim.sh b/scripts/e2e/boot-sim.sh index 0547dd88..a96f12ac 100644 --- a/scripts/e2e/boot-sim.sh +++ b/scripts/e2e/boot-sim.sh @@ -44,6 +44,8 @@ xcrun simctl spawn "$UDID" defaults write com.apple.WebUI AutoFillPasswords -boo xcrun simctl ui "$UDID" appearance "$APPEARANCE" xcrun simctl install "$UDID" "$APP" +# A photo for the compose flow's attachment. +xcrun simctl addmedia "$UDID" "$(dirname "$0")/../../assets/icon.png" || true { echo "UDID=$UDID" diff --git a/scripts/e2e/run-flows.sh b/scripts/e2e/run-flows.sh index d11dddad..111d5695 100644 --- a/scripts/e2e/run-flows.sh +++ b/scripts/e2e/run-flows.sh @@ -5,18 +5,29 @@ # # Needs in the environment: UDID, OUT (output folder), and for the flows the # E2E_* values (see .github/workflows/e2e-ios.yml). Output: $OUT/report.xml, -# $OUT/screenshots, $OUT/run.mp4, $OUT/app.log. +# $OUT/screenshots, $OUT/run.mp4, $OUT/app.log, $OUT/steps.txt. # -# Secrets: Maestro's debug output and the JSON under ~/.maestro/tests hold -# every typed text, passwords included, and artifacts of this public repo are -# public. Only the warm-up (which types a made-up address) prints a debug -# log; afterwards only PNGs are copied, and every text file in $OUT is -# scrubbed of the secret values before upload. +# Each flow runs in its own Maestro process with a time cap: when the XCTest +# driver dies (it does, now and then), only that flow fails and the next one +# starts a fresh driver. +# +# Secrets: Maestro's debug output holds every typed text, passwords included, +# and artifacts of this public repo are public. Only the warm-up (which types +# a made-up address) prints a debug log; the debug output of the real flows +# stays on the runner, and only PNGs and an argument-free summary of the +# steps (failed-steps.mjs) are copied. Every text file in $OUT is scrubbed of +# the secret values before upload. set -uo pipefail SUITE=$1 ONLY=${2:-} ROOT=$(cd "$(dirname "$0")/../.." && pwd) -mkdir -p "$OUT/screenshots" +FLOW_TIMEOUT=${FLOW_TIMEOUT:-900} +mkdir -p "$OUT/screenshots" "$OUT/junit" +DEBUG_DIR="$RUNNER_TEMP/maestro-debug" +mkdir -p "$DEBUG_DIR" + +# perl's alarm stands in for coreutils timeout, which macOS lacks. +capped() { perl -e 'alarm shift; exec @ARGV' "$FLOW_TIMEOUT" "$@"; } maestro --version @@ -24,15 +35,15 @@ xcrun simctl io "$UDID" recordVideo --codec=h264 --force "$OUT/run.mp4" & RECORDER=$! # Warm-up: the XCTest driver takes minutes to start on a cold runner. -touch "$RUNNER_TEMP/flows-started" -(cd "$OUT/screenshots" && maestro --device "$UDID" test \ +WARMUP_START=$(date +%s) +(cd "$OUT/screenshots" && capped maestro --device "$UDID" test \ --debug-output "$RUNNER_TEMP/maestro-warmup" "$ROOT/.maestro/launch.yaml") STATUS=$? +echo "warm-up took $(( $(date +%s) - WARMUP_START )) s (status $STATUS)" if [ $STATUS -ne 0 ]; then echo "::group::Maestro warm-up log" find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec tail -n 300 {} \; echo "::endgroup::" - maestro --device "$UDID" hierarchy > "$OUT/hierarchy-warmup.json" 2>/dev/null || true fi if [ $STATUS -eq 0 ]; then @@ -48,48 +59,74 @@ if [ $STATUS -eq 0 ]; then -e "DEVICE=${DEVICE:-phone}" -e "APPEARANCE=${APPEARANCE:-light}" ) - TARGETS=() + FLOWS=() if [ -n "$ONLY" ]; then IFS=',' read -ra NAMES <<< "$ONLY" for n in "${NAMES[@]}"; do f="$ROOT/.maestro/$SUITE/${n%.yaml}.yaml" - [ -f "$f" ] && TARGETS+=("$f") || echo "::warning::no flow $SUITE/$n" + if [ -f "$f" ]; then FLOWS+=("$f"); else echo "::warning::no flow $SUITE/$n"; fi done else - TARGETS=("$ROOT/.maestro/$SUITE") + for f in "$ROOT/.maestro/$SUITE"/*.yaml; do FLOWS+=("$f"); done fi - if [ ${#TARGETS[@]} -gt 0 ]; then - (cd "$OUT/screenshots" && maestro --device "$UDID" test \ - --format junit --output "$OUT/report.xml" \ - "${ARGS[@]}" "${TARGETS[@]}") - STATUS=$? + + FAILED=() + for f in "${FLOWS[@]}"; do + name=$(basename "$f" .yaml) + started=$(date +%s) + echo "::group::$name" + (cd "$OUT/screenshots" && capped maestro --device "$UDID" test \ + --format junit --output "$OUT/junit/$name.xml" \ + --debug-output "$DEBUG_DIR/$name" \ + "${ARGS[@]}" "$f") + rc=$? + echo "::endgroup::" + echo "$name: exit $rc after $(( $(date +%s) - started )) s" + if [ $rc -ne 0 ]; then + FAILED+=("$name") + # What the screen showed when it failed. + xcrun simctl io "$UDID" screenshot "$OUT/screenshots/FAILED-$name.png" >/dev/null 2>&1 || true + fi + done + if [ ${#FAILED[@]} -gt 0 ]; then + echo "::error::Failed flows: ${FAILED[*]}" + STATUS=1 fi + + node -e ' + const fs = require("fs"), path = require("path"); + const dir = process.argv[1]; + const cases = fs.readdirSync(dir).filter((f) => f.endsWith(".xml")).sort() + .map((f) => (fs.readFileSync(path.join(dir, f), "utf8").match(//g) || []).join("\n")); + fs.writeFileSync(process.argv[2], `\n\n\n${cases.join("\n")}\n\n\n`); + ' "$OUT/junit" "$OUT/report.xml" "$SUITE" || true fi kill -INT "$RECORDER" 2>/dev/null || true wait "$RECORDER" 2>/dev/null || true -# Screenshots of failed steps land under ~/.maestro/tests. Images only, plus -# a summary of the steps without their arguments (see failed-steps.mjs). -find "$HOME/.maestro/tests" -name '*.png' -exec cp {} "$OUT/screenshots/" \; 2>/dev/null || true -node "$ROOT/scripts/e2e/failed-steps.mjs" "$HOME/.maestro/tests" > "$OUT/steps.txt" 2>&1 || true +# Images of failed steps, plus a summary of the steps without arguments. +find "$DEBUG_DIR" "$HOME/.maestro/tests" -name '*.png' -exec cp {} "$OUT/screenshots/" \; 2>/dev/null || true +node "$ROOT/scripts/e2e/failed-steps.mjs" "$DEBUG_DIR" > "$OUT/steps.txt" 2>&1 || true +find "$DEBUG_DIR" -type f | sed "s|$DEBUG_DIR/||" > "$OUT/debug-files.txt" 2>/dev/null || true -xcrun simctl spawn "$UDID" log show --last 60m --style compact \ +xcrun simctl spawn "$UDID" log show --last 90m --style compact \ --predicate 'process == "BulwarkMobile"' > "$OUT/app.log" 2>&1 || true # Scrub secrets from every text file that gets uploaded. -for f in $(find "$OUT" -type f \( -name '*.xml' -o -name '*.log' -o -name '*.json' -o -name '*.txt' \)); do - node -e ' - const fs = require("fs"); - const f = process.argv[1]; - let s = fs.readFileSync(f, "utf8"); - for (const k of ["E2E_PASSWORD", "E2E_USERB_PASSWORD", "E2E_TOTP_SECRET"]) { - const v = process.env[k]; - if (v && v.length >= 6) s = s.split(v).join("***"); - } - fs.writeFileSync(f, s); - ' "$f" -done +find "$OUT" -type f \( -name '*.xml' -o -name '*.log' -o -name '*.json' -o -name '*.txt' \) -print0 | + while IFS= read -r -d '' f; do + node -e ' + const fs = require("fs"); + const f = process.argv[1]; + let s = fs.readFileSync(f, "utf8"); + for (const k of ["E2E_PASSWORD", "E2E_USERB_PASSWORD", "E2E_TOTP_SECRET"]) { + const v = process.env[k]; + if (v && v.length >= 6) s = s.split(v).join("***"); + } + fs.writeFileSync(f, s); + ' "$f" + done -ls -la "$OUT/screenshots" | tail -n +2 | wc -l | xargs echo "screenshots:" +echo "screenshots: $(ls "$OUT/screenshots" | wc -l)" exit $STATUS From 08e2f4718776152f783a1db7f3abf79d3b0f8698 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 02:54:00 +0200 Subject: [PATCH 268/334] ci: sign the ios simulator app ad hoc so the keychain works, and fix flow selectors --- .maestro/signin/02-password-errors.yaml | 12 ++++++++-- .maestro/signin/03-totp-password.yaml | 2 +- .maestro/signin/19-add-link.yaml | 10 ++++++++- .maestro/subflows/clear-field.yaml | 13 +++++++++++ .maestro/subflows/confirm-link.yaml | 11 +++++++++- .../subflows/finish-two-factor-if-asked.yaml | 4 ++-- .maestro/subflows/start.yaml | 7 ++++++ .maestro/subflows/two-factor.yaml | 2 +- .maestro/subflows/type.yaml | 9 +++----- .maestro/subflows/webmail-login-page.yaml | 9 +++----- scripts/e2e/boot-sim.sh | 22 +++++++++++++++++++ 11 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 .maestro/subflows/clear-field.yaml diff --git a/.maestro/signin/02-password-errors.yaml b/.maestro/signin/02-password-errors.yaml index bc0182bf..b80936cb 100644 --- a/.maestro/signin/02-password-errors.yaml +++ b/.maestro/signin/02-password-errors.yaml @@ -38,10 +38,18 @@ tags: visible: "Sign in with a password instead" timeout: 20000 - tapOn: "Sign in with a password instead" +- extendedWaitUntil: + visible: + id: login-password-input + timeout: 10000 - runFlow: - file: ../subflows/password-form.yaml + file: ../subflows/type-secret.yaml env: - LOGIN: ${ACCOUNT}@example.org + FIELD: login-password-input + # The email is filled in, so the password field focuses itself. + TAP: "no" + TEXT: ${PASSWORD} +- pressKey: Enter - extendedWaitUntil: visible: "Can't reach mail.does-not-exist.invalid|No mail server at mail.does-not-exist.invalid|Sign-in failed" timeout: 60000 diff --git a/.maestro/signin/03-totp-password.yaml b/.maestro/signin/03-totp-password.yaml index 85514778..b892ee69 100644 --- a/.maestro/signin/03-totp-password.yaml +++ b/.maestro/signin/03-totp-password.yaml @@ -16,7 +16,7 @@ tags: env: LOGIN: ${TOTP_ACCOUNT}@example.org - extendedWaitUntil: - visible: "Two-factor code" + visible: "Authentication code" timeout: 30000 - takeScreenshot: totp-01-code-asked - runFlow: ../subflows/two-factor.yaml diff --git a/.maestro/signin/19-add-link.yaml b/.maestro/signin/19-add-link.yaml index fc183e1b..9ea216ed 100644 --- a/.maestro/signin/19-add-link.yaml +++ b/.maestro/signin/19-add-link.yaml @@ -14,8 +14,16 @@ tags: env: CALL: /pair?user=${ACCOUNT2}&deliver=openurl - extendedWaitUntil: - visible: "Sign in with a link." + visible: "Open in .*Bulwark Mobile.*|Sign in with a link." timeout: 60000 +- runFlow: + when: + visible: "Open in .*Bulwark Mobile.*" + commands: + - tapOn: "Open" +- extendedWaitUntil: + visible: "Sign in with a link." + timeout: 20000 - takeScreenshot: add-link-01-prompt - assertVisible: "This link adds an account.*" - tapOn: diff --git a/.maestro/subflows/clear-field.yaml b/.maestro/subflows/clear-field.yaml new file mode 100644 index 00000000..3fb6022d --- /dev/null +++ b/.maestro/subflows/clear-field.yaml @@ -0,0 +1,13 @@ +# Empty the field with id FIELD. A tap puts the cursor where it lands, and +# eraseText only deletes before the cursor, so select everything first. +appId: org.bulwarkmail.mobile +--- +- longPressOn: + id: ${FIELD} +- runFlow: + when: + visible: "Select All" + commands: + - tapOn: "Select All" + - pressKey: Backspace +- eraseText: 120 diff --git a/.maestro/subflows/confirm-link.yaml b/.maestro/subflows/confirm-link.yaml index d0917c13..452fc8c0 100644 --- a/.maestro/subflows/confirm-link.yaml +++ b/.maestro/subflows/confirm-link.yaml @@ -1,9 +1,18 @@ # A sign-in link from another app asks first. Continue. appId: org.bulwarkmail.mobile --- +# simctl openurl acts like a link from another app: iOS asks first. +- extendedWaitUntil: + visible: "Open in .*Bulwark Mobile.*|Sign in with a link.|Open the sign-in page of.*" + timeout: ${LINK_WAIT || 60000} +- runFlow: + when: + visible: "Open in .*Bulwark Mobile.*" + commands: + - tapOn: "Open" - extendedWaitUntil: visible: "Sign in with a link.|Open the sign-in page of.*" - timeout: ${LINK_WAIT || 60000} + timeout: 20000 - takeScreenshot: link-confirm-prompt - tapOn: text: "Continue" diff --git a/.maestro/subflows/finish-two-factor-if-asked.yaml b/.maestro/subflows/finish-two-factor-if-asked.yaml index d6bd8129..84fa264f 100644 --- a/.maestro/subflows/finish-two-factor-if-asked.yaml +++ b/.maestro/subflows/finish-two-factor-if-asked.yaml @@ -3,10 +3,10 @@ appId: org.bulwarkmail.mobile --- - extendedWaitUntil: - visible: "Two-factor code|Inbox" + visible: "Authentication code|Inbox" timeout: 90000 - runFlow: when: - visible: "Two-factor code" + visible: "Authentication code" commands: - runFlow: two-factor.yaml diff --git a/.maestro/subflows/start.yaml b/.maestro/subflows/start.yaml index c85d5e1f..3dc66523 100644 --- a/.maestro/subflows/start.yaml +++ b/.maestro/subflows/start.yaml @@ -2,6 +2,13 @@ # env CAMERA: allow (default) or deny. appId: org.bulwarkmail.mobile --- +# A system prompt left over from an earlier flow ("Open in …?") stays on +# screen across app launches. +- runFlow: + when: + visible: "Open in .*" + commands: + - tapOn: "Cancel" - launchApp: clearState: true clearKeychain: true diff --git a/.maestro/subflows/two-factor.yaml b/.maestro/subflows/two-factor.yaml index 6e419252..58cf5e64 100644 --- a/.maestro/subflows/two-factor.yaml +++ b/.maestro/subflows/two-factor.yaml @@ -3,7 +3,7 @@ appId: org.bulwarkmail.mobile --- - extendedWaitUntil: - visible: "Two-factor code" + visible: "Authentication code" timeout: 30000 - runScript: file: ../scripts/helper.js diff --git a/.maestro/subflows/type.yaml b/.maestro/subflows/type.yaml index e7c7f5e4..6eed294d 100644 --- a/.maestro/subflows/type.yaml +++ b/.maestro/subflows/type.yaml @@ -20,7 +20,7 @@ appId: org.bulwarkmail.mobile when: true: ${ERASE == 'yes'} commands: - - eraseText: 80 + - runFlow: clear-field.yaml - inputText: ${TEXT} - runFlow: when: @@ -31,11 +31,8 @@ appId: org.bulwarkmail.mobile - retry: maxRetries: 2 commands: - - tapOn: - id: ${FIELD} - - waitForAnimationToEnd: - timeout: 2000 - - eraseText: 80 + - runFlow: + file: clear-field.yaml - waitForAnimationToEnd: timeout: 1000 - inputText: ${TEXT} diff --git a/.maestro/subflows/webmail-login-page.yaml b/.maestro/subflows/webmail-login-page.yaml index 4ef5dddf..cb939892 100644 --- a/.maestro/subflows/webmail-login-page.yaml +++ b/.maestro/subflows/webmail-login-page.yaml @@ -10,9 +10,7 @@ appId: org.bulwarkmail.mobile - inputText: ${LOGIN} - tapOn: "Enter your password" - inputText: ${PASSWORD} -- tapOn: - text: "Sign in" - index: 1 +- tapOn: "Sign in" - runFlow: when: true: ${TOTP == 'yes'} @@ -26,6 +24,5 @@ appId: org.bulwarkmail.mobile CALL: /totp - tapOn: "000000" - inputText: ${output.helper.code} - - tapOn: - text: "Sign in|Verify" - index: 1 + - tapOn: "Sign in|Verify" + diff --git a/scripts/e2e/boot-sim.sh b/scripts/e2e/boot-sim.sh index a96f12ac..b2a98c6c 100644 --- a/scripts/e2e/boot-sim.sh +++ b/scripts/e2e/boot-sim.sh @@ -43,6 +43,28 @@ xcrun simctl spawn "$UDID" defaults write $PREFS KeyboardShowPredictionBar -bool xcrun simctl spawn "$UDID" defaults write com.apple.WebUI AutoFillPasswords -bool false || true xcrun simctl ui "$UDID" appearance "$APPEARANCE" +# The app is built without signing (no certificate on the runner), so it +# carries no entitlements and every keychain call (expo-secure-store, which +# holds the credentials) fails with "A required entitlement isn't present". +# Sign it ad hoc with the keychain entitlement, as Xcode's "Sign to Run +# Locally" does for simulator builds. +ENT="$RUNNER_TEMP/simulator.entitlements" +cat > "$ENT" <<'PLIST' + + + + + application-identifier + 8ZANF9LQ39.org.bulwarkmail.mobile + keychain-access-groups + + 8ZANF9LQ39.org.bulwarkmail.mobile + + + +PLIST +codesign --force --sign - --entitlements "$ENT" --timestamp=none "$APP" +codesign -d --entitlements - "$APP" 2>/dev/null | grep -c keychain-access-groups xcrun simctl install "$UDID" "$APP" # A photo for the compose flow's attachment. xcrun simctl addmedia "$UDID" "$(dirname "$0")/../../assets/icon.png" || true From e57ee3d4469e24e1cbb7e5fd6b7c61921805b191 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 03:05:51 +0200 Subject: [PATCH 269/334] ci: sign the ios simulator build ad hoc so the keychain has its entitlements --- .github/workflows/e2e-ios.yml | 15 ++++++++++++++- scripts/e2e/boot-sim.sh | 22 ---------------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 0f7c57c9..715cc5f5 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -102,6 +102,8 @@ jobs: run: npx expo prebuild --platform ios --clean - name: Build for the simulator + env: + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: | WORKSPACE=$(ls -d ios/*.xcworkspace | head -1) SCHEME=$(basename "$WORKSPACE" .xcworkspace) @@ -117,12 +119,23 @@ jobs: -destination 'generic/platform=iOS Simulator' \ -derivedDataPath build \ ARCHS=arm64 \ - CODE_SIGNING_ALLOWED=NO \ + CODE_SIGN_STYLE=Manual \ + CODE_SIGN_IDENTITY=- \ + DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \ + PROVISIONING_PROFILE_SPECIFIER= \ > e2e-build/xcodebuild.log 2>&1; then grep -E "error:|fatal error" e2e-build/xcodebuild.log | head -n 50 || true tail -n 80 e2e-build/xcodebuild.log exit 1 fi + # Signed ad hoc ("Sign to Run Locally"): no certificate needed, and + # Xcode embeds the simulator entitlements the keychain needs. An + # unsigned build has none, and every expo-secure-store call fails + # with "A required entitlement isn't present". + APP_PATH=build/Build/Products/Release-iphonesimulator/BulwarkMobile.app + echo "Simulated entitlements:" + xcrun otool -X -s __TEXT __entitlements "$APP_PATH/BulwarkMobile" | head -20 || true + codesign -d --entitlements - "$APP_PATH" 2>&1 | head -20 || true # Packed as a tar: artifacts drop the executable bit, which would # leave a reused app unable to launch. tar -C build/Build/Products/Release-iphonesimulator -czf "$RUNNER_TEMP/simulator-app.tar.gz" BulwarkMobile.app diff --git a/scripts/e2e/boot-sim.sh b/scripts/e2e/boot-sim.sh index b2a98c6c..a96f12ac 100644 --- a/scripts/e2e/boot-sim.sh +++ b/scripts/e2e/boot-sim.sh @@ -43,28 +43,6 @@ xcrun simctl spawn "$UDID" defaults write $PREFS KeyboardShowPredictionBar -bool xcrun simctl spawn "$UDID" defaults write com.apple.WebUI AutoFillPasswords -bool false || true xcrun simctl ui "$UDID" appearance "$APPEARANCE" -# The app is built without signing (no certificate on the runner), so it -# carries no entitlements and every keychain call (expo-secure-store, which -# holds the credentials) fails with "A required entitlement isn't present". -# Sign it ad hoc with the keychain entitlement, as Xcode's "Sign to Run -# Locally" does for simulator builds. -ENT="$RUNNER_TEMP/simulator.entitlements" -cat > "$ENT" <<'PLIST' - - - - - application-identifier - 8ZANF9LQ39.org.bulwarkmail.mobile - keychain-access-groups - - 8ZANF9LQ39.org.bulwarkmail.mobile - - - -PLIST -codesign --force --sign - --entitlements "$ENT" --timestamp=none "$APP" -codesign -d --entitlements - "$APP" 2>/dev/null | grep -c keychain-access-groups xcrun simctl install "$UDID" "$APP" # A photo for the compose flow's attachment. xcrun simctl addmedia "$UDID" "$(dirname "$0")/../../assets/icon.png" || true From 02bd714afdc8d49d2f6dc8d400a01f126143ba8c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 03:11:39 +0200 Subject: [PATCH 270/334] ci: add an ios performance leg that compares two simulator builds --- .github/workflows/e2e-ios.yml | 103 ++++++++++++++++- .maestro/perf/interactions.yaml | 96 ++++++++++++++++ .maestro/perf/sign-in.yaml | 11 ++ scripts/e2e/perf.mjs | 188 ++++++++++++++++++++++++++++++++ 4 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 .maestro/perf/interactions.yaml create mode 100644 .maestro/perf/sign-in.yaml create mode 100644 scripts/e2e/perf.mjs diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 715cc5f5..8c269702 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -24,13 +24,22 @@ on: type: string required: false legs: - description: "Legs to run: all, signin, features, or a comma list (signin-phone, features-tablet-dark, ...)" + description: "Legs to run: all, signin, features, perf, or a comma list (signin-phone, features-tablet-dark, ...)" type: string default: all flows: description: "Only these flows (comma list of file names without .yaml); empty runs all" type: string required: false + perf_base_run_id: + description: "perf leg: compare against the simulator app of this run (run id)" + type: string + required: false + perf_rounds: + description: "perf leg: rounds (each: 3 cold starts and the interaction flow per app)" + type: string + default: "5" + required: false release: types: [published] @@ -226,3 +235,95 @@ jobs: flows: ${{ inputs.flows }} playwright: "false" name: ${{ matrix.name }} + + # Part 2 of docs/ios-test-2026-10.md: timing marks from the app + # (src/lib/perf-marks.ts), read by scripts/e2e/perf.mjs. Compares the app + # of this run (or app_run_id) with the app of perf_base_run_id, alternating + # them on one simulator so both sides share the runner. + perf: + needs: [plan, build] + if: >- + always() && !cancelled() && + (needs.build.result == 'success' || needs.build.result == 'skipped') && + contains(inputs.legs, 'perf') + runs-on: macos-latest + timeout-minutes: 120 + permissions: + contents: read + actions: read + env: + OUT: ${{ github.workspace }}/e2e-output + DEVICE: phone + APPEARANCE: light + ORIENTATION: portrait + E2E_SERVER: ${{ vars.E2E_SERVER }} + E2E_ACCOUNT: e2e-bulk + E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }} + steps: + - uses: actions/checkout@v4 + + - name: Download the app under test (this run) + if: inputs.app_run_id == '' + uses: actions/download-artifact@v4 + with: + name: simulator-app + path: ${{ runner.temp }}/cand + - name: Download the app under test (earlier run) + if: inputs.app_run_id != '' + uses: actions/download-artifact@v4 + with: + name: simulator-app + run-id: ${{ inputs.app_run_id }} + github-token: ${{ github.token }} + path: ${{ runner.temp }}/cand + - name: Download the baseline app + if: inputs.perf_base_run_id != '' + uses: actions/download-artifact@v4 + with: + name: simulator-app + run-id: ${{ inputs.perf_base_run_id }} + github-token: ${{ github.token }} + path: ${{ runner.temp }}/base + + - name: Unpack + run: | + for side in cand base; do + [ -f "$RUNNER_TEMP/$side/simulator-app.tar.gz" ] || continue + mkdir -p "$RUNNER_TEMP/$side/app" + tar -C "$RUNNER_TEMP/$side/app" -xzf "$RUNNER_TEMP/$side/simulator-app.tar.gz" + done + echo "APP=$RUNNER_TEMP/cand/app/BulwarkMobile.app" >> "$GITHUB_ENV" + + - name: Boot a simulator + run: bash scripts/e2e/boot-sim.sh "$DEVICE" "$APPEARANCE" "$APP" + + - name: Install Maestro + run: | + curl -fsSL "https://get.maestro.mobile.dev" | bash + echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" + + - name: Sign in once + run: | + mkdir -p "$OUT/screenshots" + cd "$OUT/screenshots" + # Warm-up for the driver, then the sign-in (keychain survives reinstalls). + maestro --device "$UDID" test "$GITHUB_WORKSPACE/.maestro/launch.yaml" + maestro --device "$UDID" test -e "SERVER=$E2E_SERVER" -e "PASSWORD=$E2E_PASSWORD" \ + -e "ACCOUNT=$E2E_ACCOUNT" -e "ORIENTATION=portrait" "$GITHUB_WORKSPACE/.maestro/perf/sign-in.yaml" + + - name: Measure + run: | + APPS=(--app "cand=$RUNNER_TEMP/cand/app/BulwarkMobile.app") + if [ -d "$RUNNER_TEMP/base/app/BulwarkMobile.app" ]; then + APPS=(--app "base=$RUNNER_TEMP/base/app/BulwarkMobile.app" "${APPS[@]}") + fi + node scripts/e2e/perf.mjs "${APPS[@]}" --rounds "${{ inputs.perf_rounds || 5 }}" --cold 3 \ + --flow "$GITHUB_WORKSPACE/.maestro/perf/interactions.yaml" --out "$OUT" + + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: e2e-ios-perf + path: e2e-output/ + if-no-files-found: ignore diff --git a/.maestro/perf/interactions.yaml b/.maestro/perf/interactions.yaml new file mode 100644 index 00000000..eb7cf957 --- /dev/null +++ b/.maestro/perf/interactions.yaml @@ -0,0 +1,96 @@ +# Performance runs: the interactions the harness times (scripts/e2e/perf.mjs). +# The app is already running with -perfMarks YES; no launchApp here, it would +# start it without the launch argument. +appId: org.bulwarkmail.mobile +name: Perf interactions +--- +- extendedWaitUntil: + visible: + id: email-row + timeout: 60000 +- waitForAnimationToEnd: + timeout: 5000 + +# Open a message and go back, five times. +- repeat: + times: 5 + commands: + - tapOn: + id: email-row + index: 1 + - extendedWaitUntil: + visible: "Reply All" + timeout: 20000 + - waitForAnimationToEnd: + timeout: 3000 + - runFlow: + when: + visible: "Back" + commands: + - tapOn: "Back" + - waitForAnimationToEnd: + timeout: 3000 + +# Switch through the tabs, three times. +- repeat: + times: 3 + commands: + - tapOn: + id: tab-calendar + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: + id: tab-contacts + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: + id: tab-files + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: + id: tab-settings + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: + id: tab-mail + - waitForAnimationToEnd: + timeout: 3000 + +# Open the composer and close it untouched, five times. +- repeat: + times: 5 + commands: + - tapOn: "Compose" + - extendedWaitUntil: + visible: "New Message" + timeout: 20000 + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: "Close" + - waitForAnimationToEnd: + timeout: 3000 + - runFlow: + when: + visible: "Discard" + commands: + - tapOn: "Discard" + +# Fling through the long list, then back up. +- repeat: + times: 8 + commands: + - swipe: + start: 50%,80% + end: 50%,20% + duration: 250 + - waitForAnimationToEnd: + timeout: 4000 +- repeat: + times: 8 + commands: + - swipe: + start: 50%,20% + end: 50%,80% + duration: 250 + - waitForAnimationToEnd: + timeout: 4000 diff --git a/.maestro/perf/sign-in.yaml b/.maestro/perf/sign-in.yaml new file mode 100644 index 00000000..99b41936 --- /dev/null +++ b/.maestro/perf/sign-in.yaml @@ -0,0 +1,11 @@ +# Performance runs: sign in once (ACCOUNT, the bulk account with thousands of +# messages). Later installs of other builds keep the data and the keychain. +appId: org.bulwarkmail.mobile +name: Perf sign-in +--- +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/password-sign-in.yaml + env: + LOGIN: ${ACCOUNT}@example.org + INBOX_TEXT: ".*Bulk.*" diff --git a/scripts/e2e/perf.mjs b/scripts/e2e/perf.mjs new file mode 100644 index 00000000..2e6dc21d --- /dev/null +++ b/scripts/e2e/perf.mjs @@ -0,0 +1,188 @@ +#!/usr/bin/env node +// Performance harness for the iOS simulator (Part 2 of docs/ios-test-2026-10.md). +// +// node scripts/e2e/perf.mjs --app base= --app cand= \ +// --rounds 5 --cold 3 --flow .maestro/perf/interactions.yaml --out +// +// The app logs `[perf] {...}` lines when launched with `-perfMarks YES` +// (src/lib/perf-marks.ts); they reach the simulator's system log at info +// level. This script streams that log, and per round and app: +// 1. installs the app over the previous one (data and keychain stay, so it +// stays signed in), +// 2. cold-starts it `--cold` times: terminate, launch, wait for +// `inbox-rendered`; cold start = that mark's time minus the launch time +// taken on this machine (the simulator shares the host clock), +// 3. runs the Maestro interaction flow once (open messages, switch tabs, +// open compose, scroll), which produces the other marks. +// Apps alternate inside every round, so both sides of a comparison see the +// same runner, the same simulator and the same server load. +// +// Output: /perf.json (every sample) and /perf.md (medians). + +import { spawn, execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; + +const args = process.argv.slice(2); +const opt = (name, fallback) => { + const i = args.indexOf(`--${name}`); + return i >= 0 ? args[i + 1] : fallback; +}; +const apps = []; +for (let i = 0; i < args.length; i++) { + if (args[i] === '--app') { + const [label, p] = args[i + 1].split('='); + apps.push({ label, path: p }); + } +} +const UDID = process.env.UDID; +const BUNDLE = 'org.bulwarkmail.mobile'; +const rounds = Number(opt('rounds', 5)); +const coldRuns = Number(opt('cold', 3)); +const flow = opt('flow', '.maestro/perf/interactions.yaml'); +const out = opt('out', 'perf-output'); +const maestroEnv = (opt('env', '') || '').split(',').filter(Boolean); +fs.mkdirSync(out, { recursive: true }); + +if (!UDID || apps.length === 0) { + console.error('need UDID and at least one --app label=path'); + process.exit(2); +} + +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); +const simctl = (...a) => execFileSync('xcrun', ['simctl', ...a], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); + +// ── log stream ────────────────────────────────────────────────────────────── +const marks = []; // { n, t, ...detail, label } +let currentLabel = null; +const waiters = []; +const stream = spawn('xcrun', [ + 'simctl', 'spawn', UDID, 'log', 'stream', '--level', 'info', '--style', 'ndjson', + '--predicate', 'subsystem == "com.facebook.react.log" AND eventMessage CONTAINS "[perf]"', +]); +let buf = ''; +stream.stdout.on('data', (chunk) => { + buf += chunk; + let nl; + while ((nl = buf.indexOf('\n')) >= 0) { + const line = buf.slice(0, nl); + buf = buf.slice(nl + 1); + let ev; + try { ev = JSON.parse(line); } catch { continue; } + const msg = String(ev.eventMessage || ''); + const at = msg.indexOf('[perf] '); + if (at < 0) continue; + // The message is the console line, possibly quoted by RCTLog. + const json = msg.slice(at + 7).replace(/^'|'$/g, ''); + let mark; + try { mark = JSON.parse(json); } catch { continue; } + mark.label = currentLabel; + marks.push(mark); + for (const w of [...waiters]) { + if (w.name === mark.n) { + waiters.splice(waiters.indexOf(w), 1); + w.resolve(mark); + } + } + } +}); + +function waitForMark(name, timeoutMs) { + return new Promise((resolve) => { + const w = { name, resolve }; + waiters.push(w); + setTimeout(() => { + const i = waiters.indexOf(w); + if (i >= 0) { waiters.splice(i, 1); resolve(null); } + }, timeoutMs); + }); +} + +// ── steps ─────────────────────────────────────────────────────────────────── +const samples = []; // { label, metric, ms, round } + +async function coldStart(label, round) { + try { simctl('terminate', UDID, BUNDLE); } catch { /* not running */ } + await sleep(1500); + const pending = waitForMark('inbox-rendered', 120_000); + const bundlePending = waitForMark('bundle-loaded', 120_000); + const t0 = Date.now(); + simctl('launch', UDID, BUNDLE, '-perfMarks', 'YES'); + const [mark, bundle] = await Promise.all([pending, bundlePending]); + if (!mark) { + console.log(` ${label} cold start: no inbox-rendered mark within 120 s`); + return; + } + samples.push({ label, round, metric: 'cold-start-to-inbox', ms: mark.t - t0 }); + if (bundle) samples.push({ label, round, metric: 'cold-start-to-bundle', ms: bundle.t - t0 }); + console.log(` ${label} cold start ${mark.t - t0} ms (bundle ${bundle ? bundle.t - t0 : '?'} ms, rows ${mark.rows})`); + await sleep(3000); +} + +function runFlow(label) { + const before = marks.length; + const env = maestroEnv.flatMap((kv) => ['-e', kv]); + const res = spawn('maestro', ['--device', UDID, 'test', ...env, flow], { stdio: 'inherit' }); + return new Promise((resolve) => { + res.on('exit', (code) => { + const got = marks.slice(before).filter((m) => typeof m.ms === 'number'); + console.log(` ${label} interactions: exit ${code}, ${got.length} timed marks`); + resolve(got); + }); + }); +} + +(async () => { + await sleep(2000); // let the log stream attach + for (let round = 1; round <= rounds; round++) { + console.log(`round ${round}/${rounds}`); + // Alternate the order too, so neither app always goes first. + const order = round % 2 ? apps : [...apps].reverse(); + for (const app of order) { + currentLabel = app.label; + try { simctl('terminate', UDID, BUNDLE); } catch { /* not running */ } + simctl('install', UDID, app.path); + for (let i = 0; i < coldRuns; i++) await coldStart(app.label, round); + const got = await runFlow(app.label); + for (const m of got) { + const metric = m.n === 'tab-switch' && m.to ? `tab-switch:${m.to}` : m.n; + samples.push({ label: app.label, round, metric, ms: m.ms, ...(m.long !== undefined ? { long: m.long, frames: m.frames, worst: m.worst } : {}) }); + } + } + } + stream.kill(); + + const median = (xs) => { + const s = [...xs].sort((a, b) => a - b); + const m = Math.floor(s.length / 2); + return s.length % 2 ? s[m] : Math.round((s[m - 1] + s[m]) / 2); + }; + const metrics = [...new Set(samples.map((s) => s.metric))].sort(); + const labels = apps.map((a) => a.label); + const lines = [ + `| metric | ${labels.map((l) => `${l} median (n)`).join(' | ')} |${labels.length === 2 ? ' change |' : ''}`, + `|---|${labels.map(() => '---').join('|')}|${labels.length === 2 ? '---|' : ''}`, + ]; + for (const metric of metrics) { + const cells = labels.map((l) => { + const xs = samples.filter((s) => s.label === l && s.metric === metric).map((s) => s.ms); + return xs.length ? { med: median(xs), n: xs.length } : null; + }); + let change = ''; + if (labels.length === 2 && cells[0] && cells[1]) { + change = ` ${Math.round(((cells[1].med - cells[0].med) / cells[0].med) * 100)}% |`; + } else if (labels.length === 2) change = ' |'; + lines.push(`| ${metric} | ${cells.map((c) => (c ? `${c.med} ms (${c.n})` : '-')).join(' | ')} |${change}`); + } + const scroll = labels.map((l) => { + const xs = samples.filter((s) => s.label === l && s.metric === 'list-scroll'); + if (!xs.length) return null; + return `${l}: median ${median(xs.map((s) => s.long))} long frames of ${median(xs.map((s) => s.frames))}, worst ${median(xs.map((s) => s.worst))} ms`; + }).filter(Boolean); + const md = [...lines, '', ...scroll.map((s) => `- list-scroll ${s}`), ''].join('\n'); + fs.writeFileSync(path.join(out, 'perf.json'), JSON.stringify({ apps, rounds, coldRuns, samples }, null, 1)); + fs.writeFileSync(path.join(out, 'perf.md'), md); + console.log(md); + if (process.env.GITHUB_STEP_SUMMARY) fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `## Performance\n\n${md}\n`); + process.exit(0); +})(); From 96d9d54cc94d733f68d07229e7b586f01334cb17 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 03:55:43 +0200 Subject: [PATCH 271/334] ci: type the first character alone in ios flows and answer the webmail hand-over prompt --- .maestro/subflows/clear-field.yaml | 18 +++++------ .maestro/subflows/go-to-folder.yaml | 3 +- .maestro/subflows/inbox.yaml | 3 +- .maestro/subflows/open-drawer.yaml | 2 +- .maestro/subflows/stalwart-login-page.yaml | 14 ++++++--- .maestro/subflows/type-secret.yaml | 15 +++++++--- .maestro/subflows/type-web.yaml | 14 +++++++++ .maestro/subflows/type.yaml | 35 +++++++++++++++------- .maestro/subflows/webmail-login-page.yaml | 20 ++++++++++--- 9 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 .maestro/subflows/type-web.yaml diff --git a/.maestro/subflows/clear-field.yaml b/.maestro/subflows/clear-field.yaml index 3fb6022d..f56abd4d 100644 --- a/.maestro/subflows/clear-field.yaml +++ b/.maestro/subflows/clear-field.yaml @@ -1,13 +1,11 @@ -# Empty the field with id FIELD. A tap puts the cursor where it lands, and -# eraseText only deletes before the cursor, so select everything first. +# Empty the field with id FIELD. A tap puts the cursor where it lands and +# eraseText only deletes before the cursor, so erase in passes: each tap +# lands in the middle of what is left. appId: org.bulwarkmail.mobile --- -- longPressOn: - id: ${FIELD} -- runFlow: - when: - visible: "Select All" +- repeat: + times: 4 commands: - - tapOn: "Select All" - - pressKey: Backspace -- eraseText: 120 + - tapOn: + id: ${FIELD} + - eraseText: 100 diff --git a/.maestro/subflows/go-to-folder.yaml b/.maestro/subflows/go-to-folder.yaml index f660cf30..77587690 100644 --- a/.maestro/subflows/go-to-folder.yaml +++ b/.maestro/subflows/go-to-folder.yaml @@ -2,7 +2,8 @@ appId: org.bulwarkmail.mobile --- - runFlow: open-drawer.yaml -- tapOn: ${FOLDER} +# A folder row reads " / ". +- tapOn: ${FOLDER}( .*)? - extendedWaitUntil: visible: ${FOLDER} timeout: 15000 diff --git a/.maestro/subflows/inbox.yaml b/.maestro/subflows/inbox.yaml index 0e132000..83b7905c 100644 --- a/.maestro/subflows/inbox.yaml +++ b/.maestro/subflows/inbox.yaml @@ -15,4 +15,5 @@ appId: org.bulwarkmail.mobile visible: id: email-row timeout: 60000 -- assertVisible: ${INBOX_TEXT || 'Your verification code'} +# Rows are one element: sender, subject and preview together. +- assertVisible: ${INBOX_TEXT || '.*Your verification code.*'} diff --git a/.maestro/subflows/open-drawer.yaml b/.maestro/subflows/open-drawer.yaml index 853414fc..6858053e 100644 --- a/.maestro/subflows/open-drawer.yaml +++ b/.maestro/subflows/open-drawer.yaml @@ -8,5 +8,5 @@ appId: org.bulwarkmail.mobile commands: - tapOn: "Toggle menu" - extendedWaitUntil: - visible: "Scheduled" + visible: "Scheduled.*" timeout: 10000 diff --git a/.maestro/subflows/stalwart-login-page.yaml b/.maestro/subflows/stalwart-login-page.yaml index d8db349a..48758942 100644 --- a/.maestro/subflows/stalwart-login-page.yaml +++ b/.maestro/subflows/stalwart-login-page.yaml @@ -6,10 +6,16 @@ appId: org.bulwarkmail.mobile visible: "you@example.com" timeout: 60000 - takeScreenshot: stalwart-login-page -- tapOn: "you@example.com" -- inputText: ${LOGIN} -- tapOn: "••••••••" -- inputText: ${PASSWORD} +- runFlow: + file: type-web.yaml + env: + HINT: "you@example.com" + TEXT: ${LOGIN} +- runFlow: + file: type-web.yaml + env: + HINT: "••••••••" + TEXT: ${PASSWORD} - runFlow: when: true: ${TOTP == 'yes'} diff --git a/.maestro/subflows/type-secret.yaml b/.maestro/subflows/type-secret.yaml index bbde88fe..3200c6c5 100644 --- a/.maestro/subflows/type-secret.yaml +++ b/.maestro/subflows/type-secret.yaml @@ -1,9 +1,10 @@ # Type a secret into the field with id FIELD (TAP=no for a field that focuses -# itself, see type.yaml). The value can't be checked (a secure field shows -# dots) and must never be shown, so a lost keystroke surfaces as a failed -# sign-in. +# itself; see type.yaml for why the first character goes alone). The value +# can't be checked (a secure field shows dots) and must never be shown, so a +# lost keystroke surfaces as a failed sign-in. appId: org.bulwarkmail.mobile --- +- evalScript: ${output.secretHead = TEXT.slice(0, 1); output.secretTail = TEXT.slice(1)} - runFlow: when: true: ${TAP != 'no'} @@ -12,4 +13,10 @@ appId: org.bulwarkmail.mobile id: ${FIELD} - waitForAnimationToEnd: timeout: 3000 -- inputText: ${TEXT} +- inputText: ${output.secretHead} +# A short pause: wait for something that never shows. +- extendedWaitUntil: + visible: "zz-no-such-element-zz" + timeout: 1000 + optional: true +- inputText: ${output.secretTail} diff --git a/.maestro/subflows/type-web.yaml b/.maestro/subflows/type-web.yaml new file mode 100644 index 00000000..d8026ea1 --- /dev/null +++ b/.maestro/subflows/type-web.yaml @@ -0,0 +1,14 @@ +# Type TEXT into a web page field found by its placeholder HINT (the sign-in +# pages in the auth session). First character alone, as in type.yaml. +appId: org.bulwarkmail.mobile +--- +- evalScript: ${output.webHead = TEXT.slice(0, 1); output.webTail = TEXT.slice(1)} +- tapOn: ${HINT} +- waitForAnimationToEnd: + timeout: 3000 +- inputText: ${output.webHead} +- extendedWaitUntil: + visible: "zz-no-such-element-zz" + timeout: 1000 + optional: true +- inputText: ${output.webTail} diff --git a/.maestro/subflows/type.yaml b/.maestro/subflows/type.yaml index 6eed294d..f5ebb2e2 100644 --- a/.maestro/subflows/type.yaml +++ b/.maestro/subflows/type.yaml @@ -1,12 +1,14 @@ # Type TEXT into the field with id FIELD and check it arrived in full. # -# iOS drops keystrokes while the edit menu (the "AutoFill" bubble) opens, -# which happens when a flow taps a field that already has focus. Maestro -# can't see the focus of a React Native field, so a caller passes TAP=no for -# a field that focuses itself. Retyped up to twice if text got lost (the -# retry taps: by then the field has focus and the menu is harmless). +# iOS drops keystrokes in two situations seen on the CI simulator: while the +# edit menu (the "AutoFill" bubble) opens, which happens when a flow taps a +# field that already has focus, and right after the first character, while +# the field re-renders. So: no tap for a field that focuses itself (TAP=no; +# Maestro can't see a React Native field's focus), type the first character, +# wait until it shows, then type the rest. Retyped up to twice. appId: org.bulwarkmail.mobile --- +- evalScript: ${output.typeHead = TEXT.slice(0, 1); output.typeTail = TEXT.slice(1)} - runFlow: when: true: ${TAP != 'no'} @@ -21,7 +23,14 @@ appId: org.bulwarkmail.mobile true: ${ERASE == 'yes'} commands: - runFlow: clear-field.yaml -- inputText: ${TEXT} +- inputText: ${output.typeHead} +- extendedWaitUntil: + visible: + id: ${FIELD} + text: "${output.typeHead}.*" + timeout: 5000 + optional: true +- inputText: ${output.typeTail} - runFlow: when: notVisible: @@ -31,11 +40,15 @@ appId: org.bulwarkmail.mobile - retry: maxRetries: 2 commands: - - runFlow: - file: clear-field.yaml - - waitForAnimationToEnd: - timeout: 1000 - - inputText: ${TEXT} + - runFlow: clear-field.yaml + - inputText: ${output.typeHead} + - extendedWaitUntil: + visible: + id: ${FIELD} + text: "${output.typeHead}.*" + timeout: 5000 + optional: true + - inputText: ${output.typeTail} - assertVisible: id: ${FIELD} text: ${TEXT_PATTERN || TEXT} diff --git a/.maestro/subflows/webmail-login-page.yaml b/.maestro/subflows/webmail-login-page.yaml index cb939892..33829d38 100644 --- a/.maestro/subflows/webmail-login-page.yaml +++ b/.maestro/subflows/webmail-login-page.yaml @@ -6,10 +6,16 @@ appId: org.bulwarkmail.mobile visible: "user@example.com" timeout: 60000 - takeScreenshot: webmail-login-page -- tapOn: "user@example.com" -- inputText: ${LOGIN} -- tapOn: "Enter your password" -- inputText: ${PASSWORD} +- runFlow: + file: type-web.yaml + env: + HINT: "user@example.com" + TEXT: ${LOGIN} +- runFlow: + file: type-web.yaml + env: + HINT: "Enter your password" + TEXT: ${PASSWORD} - tapOn: "Sign in" - runFlow: when: @@ -26,3 +32,9 @@ appId: org.bulwarkmail.mobile - inputText: ${output.helper.code} - tapOn: "Sign in|Verify" +# Before handing over, the webmail asks whether the app may take the sign-in. +- extendedWaitUntil: + visible: "Hand over to the app" + timeout: 60000 +- takeScreenshot: webmail-handover-prompt +- tapOn: "Hand over to the app" From ca09708fd15752b6f84d5c91dfc8d987af697478 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 04:40:42 +0200 Subject: [PATCH 272/334] ci: match ios drawer rows by their joined label and keep failed-step hierarchies --- .maestro/features/01-folders.yaml | 18 +++++++++--------- .maestro/features/02-read-messages.yaml | 2 +- .maestro/subflows/go-to-folder.yaml | 4 ++-- scripts/e2e/failed-steps.mjs | 17 +++++++++++++++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/.maestro/features/01-folders.yaml b/.maestro/features/01-folders.yaml index 406d27ae..ec0f26d5 100644 --- a/.maestro/features/01-folders.yaml +++ b/.maestro/features/01-folders.yaml @@ -10,12 +10,12 @@ tags: - assertVisible: ".*Your verification code.*" - runFlow: ../subflows/open-drawer.yaml - takeScreenshot: folders-02-drawer -- assertVisible: "Inbox.*" -- assertVisible: "Sent.*" -- assertVisible: "Drafts.*" -- assertVisible: "Archive.*" -- assertVisible: "Projects.*" -- tapOn: "Archive.*" +- assertVisible: "Inbox([ ,].*)?" +- assertVisible: "Sent([ ,].*)?" +- assertVisible: "Drafts([ ,].*)?" +- assertVisible: "Archive([ ,].*)?" +- assertVisible: "Projects([ ,].*)?" +- tapOn: "Archive([ ,].*)?" - extendedWaitUntil: visible: ".*Invoice 2025-12.*" timeout: 20000 @@ -24,13 +24,13 @@ tags: # Projects/Alpha holds the seeded thread. - runFlow: when: - notVisible: "Alpha.*" + notVisible: "Alpha([ ,].*)?" commands: - tapOn: text: "Expand" - rightOf: "Projects.*" + rightOf: "Projects([ ,].*)?" optional: true -- tapOn: "Alpha.*" +- tapOn: "Alpha([ ,].*)?" - extendedWaitUntil: visible: ".*Project Alpha kickoff.*" timeout: 20000 diff --git a/.maestro/features/02-read-messages.yaml b/.maestro/features/02-read-messages.yaml index 7dd19dfe..ffa6cdec 100644 --- a/.maestro/features/02-read-messages.yaml +++ b/.maestro/features/02-read-messages.yaml @@ -62,7 +62,7 @@ tags: - runFlow: file: ../subflows/go-to-folder.yaml env: - FOLDER: "Alpha.*" + FOLDER: Alpha - runFlow: file: ../subflows/open-message.yaml env: diff --git a/.maestro/subflows/go-to-folder.yaml b/.maestro/subflows/go-to-folder.yaml index 77587690..8c9526cf 100644 --- a/.maestro/subflows/go-to-folder.yaml +++ b/.maestro/subflows/go-to-folder.yaml @@ -2,8 +2,8 @@ appId: org.bulwarkmail.mobile --- - runFlow: open-drawer.yaml -# A folder row reads " / ". -- tapOn: ${FOLDER}( .*)? +# A folder row's label joins its texts: ", , /, ". +- tapOn: ${FOLDER}([ ,].*)? - extendedWaitUntil: visible: ${FOLDER} timeout: 15000 diff --git a/scripts/e2e/failed-steps.mjs b/scripts/e2e/failed-steps.mjs index f4fdf605..ba300dca 100644 --- a/scripts/e2e/failed-steps.mjs +++ b/scripts/e2e/failed-steps.mjs @@ -68,4 +68,21 @@ for (const file of walkFiles(root).filter((f) => /commands-.*\.json$/.test(f))) } } } +// Maestro 2 keeps the screen hierarchy of each failed step under +// screen-hierarchy/; it holds what the screen showed (like the screenshot). +for (const file of walkFiles(root).filter((f) => f.includes('screen-hierarchy') && f.endsWith('.json'))) { + let data; + try { + data = JSON.parse(fs.readFileSync(file, 'utf8')); + } catch { + continue; + } + const h = findHierarchy(data); + if (!h) continue; + const parts = file.split(path.sep).join('/').split('/'); + out.push(`== ${parts[parts.length - 3]} / ${parts[parts.length - 1]}`); + const lines = []; + tree(h, 0, lines); + out.push(...lines.slice(0, 300).map((l) => ` ${l}`)); +} process.stdout.write(out.join('\n') + '\n'); From bf226c379480f1a783921dee8be83b63b35283fc Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 04:44:23 +0200 Subject: [PATCH 273/334] ci: fix ios sign-out, scanner and add-account flow steps --- .maestro/signin/14-scanner-camera.yaml | 2 +- .maestro/subflows/inbox.yaml | 3 +++ .maestro/subflows/sign-out.yaml | 3 ++- .maestro/subflows/stalwart-login-page.yaml | 14 ++++---------- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/.maestro/signin/14-scanner-camera.yaml b/.maestro/signin/14-scanner-camera.yaml index 6e81d171..94b0c4f8 100644 --- a/.maestro/signin/14-scanner-camera.yaml +++ b/.maestro/signin/14-scanner-camera.yaml @@ -11,7 +11,7 @@ tags: CAMERA: deny - tapOn: "Scan a sign-in code.*" - extendedWaitUntil: - visible: "Camera access is turned off.*|Scanning a sign-in QR code uses the camera.*" + visible: "Camera access is.*|Scanning a sign-in QR code uses the camera.*" timeout: 15000 - takeScreenshot: scanner-01-denied - tapOn: "Close" diff --git a/.maestro/subflows/inbox.yaml b/.maestro/subflows/inbox.yaml index 83b7905c..59c67a0e 100644 --- a/.maestro/subflows/inbox.yaml +++ b/.maestro/subflows/inbox.yaml @@ -11,6 +11,9 @@ appId: org.bulwarkmail.mobile visible: "Not now" commands: - tapOn: "Not now" +# Adding an account from Settings returns to Settings. +- tapOn: + id: tab-mail - extendedWaitUntil: visible: id: email-row diff --git a/.maestro/subflows/sign-out.yaml b/.maestro/subflows/sign-out.yaml index e41c0aa4..4937f27a 100644 --- a/.maestro/subflows/sign-out.yaml +++ b/.maestro/subflows/sign-out.yaml @@ -11,6 +11,7 @@ appId: org.bulwarkmail.mobile - extendedWaitUntil: visible: "The account is removed from this device.*" timeout: 10000 +# The dialog's button, not the Settings row of the same name behind it. - tapOn: text: "Sign out" - below: "The account is removed from this device.*" + rightOf: "Cancel" diff --git a/.maestro/subflows/stalwart-login-page.yaml b/.maestro/subflows/stalwart-login-page.yaml index 48758942..d8db349a 100644 --- a/.maestro/subflows/stalwart-login-page.yaml +++ b/.maestro/subflows/stalwart-login-page.yaml @@ -6,16 +6,10 @@ appId: org.bulwarkmail.mobile visible: "you@example.com" timeout: 60000 - takeScreenshot: stalwart-login-page -- runFlow: - file: type-web.yaml - env: - HINT: "you@example.com" - TEXT: ${LOGIN} -- runFlow: - file: type-web.yaml - env: - HINT: "••••••••" - TEXT: ${PASSWORD} +- tapOn: "you@example.com" +- inputText: ${LOGIN} +- tapOn: "••••••••" +- inputText: ${PASSWORD} - runFlow: when: true: ${TOTP == 'yes'} From 52975bd7156c09528f0750e7326c3b318d0e4403 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 04:45:29 +0200 Subject: [PATCH 274/334] ci: test the webmail hand-off with a webmail session left in the browser --- .maestro/signin/07-webmail-totp.yaml | 6 ++- .maestro/signin/20-webmail-session.yaml | 52 +++++++++++++++++++++++++ scripts/e2e/run-flows.sh | 1 + 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 .maestro/signin/20-webmail-session.yaml diff --git a/.maestro/signin/07-webmail-totp.yaml b/.maestro/signin/07-webmail-totp.yaml index 604bc9f7..a0d3e579 100644 --- a/.maestro/signin/07-webmail-totp.yaml +++ b/.maestro/signin/07-webmail-totp.yaml @@ -1,5 +1,7 @@ # Webmail hand-off for a TOTP account: the webmail asks for the code; if it -# hands back the password, the app finishes with a code of its own. +# hands back the password, the app finishes with a code of its own. Uses the +# beta webmail: the stable one still has 06's session in the browser, which +# strands the hand-off (see 20-webmail-session.yaml). appId: org.bulwarkmail.mobile name: Webmail hand-off with TOTP tags: @@ -9,7 +11,7 @@ tags: - runFlow: file: ../subflows/manual-server.yaml env: - HOST: ${WEBMAIL} + HOST: ${WEBMAIL_BETA} - tapOn: "Continue" - runFlow: ../subflows/allow-auth-session.yaml - runFlow: diff --git a/.maestro/signin/20-webmail-session.yaml b/.maestro/signin/20-webmail-session.yaml new file mode 100644 index 00000000..e2238aee --- /dev/null +++ b/.maestro/signin/20-webmail-session.yaml @@ -0,0 +1,52 @@ +# Sign in via webmail while the browser still has a webmail session (from +# 06-webmail, or from using the webmail in Safari): the hand-off must still +# reach the app. Before the fix, the webmail's login page redirected an +# existing session to its own inbox inside the sign-in sheet, and the app +# never got the sign-in. +appId: org.bulwarkmail.mobile +name: Webmail hand-off with an existing webmail session +tags: + - signin +--- +# Leaves a webmail session in the shared browser cookies. +- runFlow: ../subflows/start.yaml +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${WEBMAIL} +- tapOn: "Continue" +- runFlow: ../subflows/allow-auth-session.yaml +- extendedWaitUntil: + visible: "user@example.com|Hand over to the app|.*Inbox.*" + timeout: 60000 +- runFlow: + when: + visible: "user@example.com" + commands: + - runFlow: + file: ../subflows/webmail-login-page.yaml + env: + LOGIN: ${ACCOUNT}@example.org +- runFlow: + when: + visible: "Hand over to the app" + commands: + - tapOn: "Hand over to the app" +- runFlow: ../subflows/inbox.yaml +- runFlow: ../subflows/sign-out.yaml + +# Again: the webmail now knows the user. +- extendedWaitUntil: + visible: "Enter server details manually" + timeout: 30000 +- runFlow: + file: ../subflows/manual-server.yaml + env: + HOST: ${WEBMAIL} +- tapOn: "Continue" +- runFlow: ../subflows/allow-auth-session.yaml +- extendedWaitUntil: + visible: "user@example.com|Hand over to the app|Search mail.*" + timeout: 60000 +- takeScreenshot: webmail-session-01-second-visit +- assertNotVisible: "Search mail.*" diff --git a/scripts/e2e/run-flows.sh b/scripts/e2e/run-flows.sh index 111d5695..e4b8b9ea 100644 --- a/scripts/e2e/run-flows.sh +++ b/scripts/e2e/run-flows.sh @@ -54,6 +54,7 @@ if [ $STATUS -eq 0 ]; then -e "ACCOUNT2=${E2E_ACCOUNT2:-}" -e "TOTP_ACCOUNT=${E2E_TOTP_ACCOUNT:-}" -e "WEBMAIL=${E2E_WEBMAIL:-}" + -e "WEBMAIL_BETA=${E2E_WEBMAIL_BETA:-https://bulwark-beta.internal.bulwarkmail.org}" -e "HELPER=http://127.0.0.1:${HELPER_PORT:-8787}" -e "ORIENTATION=${ORIENTATION:-portrait}" -e "DEVICE=${DEVICE:-phone}" From 8533bd4f99cf70a58c9c458709092b8ecd782657 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:20:49 +0200 Subject: [PATCH 275/334] ci: fix ios feature flow steps for the drawer, search, selection, calendar and contacts --- .maestro/features/01-folders.yaml | 8 +++--- .maestro/features/05-search.yaml | 2 +- .maestro/features/07-move-delete-tag.yaml | 13 +++++++-- .maestro/features/09-calendar.yaml | 33 ++++++++++++++++------- .maestro/features/10-contacts.yaml | 31 +++++++++++++-------- scripts/e2e/helper.mjs | 6 ++++- 6 files changed, 64 insertions(+), 29 deletions(-) diff --git a/.maestro/features/01-folders.yaml b/.maestro/features/01-folders.yaml index ec0f26d5..05925db9 100644 --- a/.maestro/features/01-folders.yaml +++ b/.maestro/features/01-folders.yaml @@ -14,7 +14,8 @@ tags: - assertVisible: "Sent([ ,].*)?" - assertVisible: "Drafts([ ,].*)?" - assertVisible: "Archive([ ,].*)?" -- assertVisible: "Projects([ ,].*)?" +# Parent folders read "Collapse, Projects" (the chevron comes first). +- assertVisible: ".*Projects.*" - tapOn: "Archive([ ,].*)?" - extendedWaitUntil: visible: ".*Invoice 2025-12.*" @@ -26,10 +27,7 @@ tags: when: notVisible: "Alpha([ ,].*)?" commands: - - tapOn: - text: "Expand" - rightOf: "Projects([ ,].*)?" - optional: true + - tapOn: "Expand, Projects.*" - tapOn: "Alpha([ ,].*)?" - extendedWaitUntil: visible: ".*Project Alpha kickoff.*" diff --git a/.maestro/features/05-search.yaml b/.maestro/features/05-search.yaml index 309918c2..c190cb14 100644 --- a/.maestro/features/05-search.yaml +++ b/.maestro/features/05-search.yaml @@ -14,6 +14,7 @@ tags: - assertNotVisible: ".*Weekly digest.*" - takeScreenshot: search-01-hit - tapOn: "Clear search" +- tapOn: "Search mail..." - inputText: "zzqx-no-such-mail" - pressKey: Enter - extendedWaitUntil: @@ -24,4 +25,3 @@ tags: - extendedWaitUntil: visible: ".*Weekly digest.*" timeout: 30000 -- hideKeyboard diff --git a/.maestro/features/07-move-delete-tag.yaml b/.maestro/features/07-move-delete-tag.yaml index b0253bcd..60115b25 100644 --- a/.maestro/features/07-move-delete-tag.yaml +++ b/.maestro/features/07-move-delete-tag.yaml @@ -55,7 +55,16 @@ tags: timeout: 10000 - takeScreenshot: actions-04-selected - tapOn: "Star" -- tapOn: "Clear selection" +# A bulk action ends selection mode by itself. +- runFlow: + when: + visible: "Clear selection" + commands: + - tapOn: "Clear selection" - longPressOn: ".*Grüße aus München.*" - tapOn: "Unstar|Star" -- tapOn: "Clear selection" +- runFlow: + when: + visible: "Clear selection" + commands: + - tapOn: "Clear selection" diff --git a/.maestro/features/09-calendar.yaml b/.maestro/features/09-calendar.yaml index c1c8de93..31e7ac49 100644 --- a/.maestro/features/09-calendar.yaml +++ b/.maestro/features/09-calendar.yaml @@ -8,6 +8,15 @@ tags: - evalScript: ${output.event = 'E2E event ' + DEVICE + ' ' + Date.now() % 100000} - tapOn: id: tab-calendar +# The first visit asks for notification permission (event reminders). +- extendedWaitUntil: + visible: ".*Would Like to Send You Notifications.*|Go to today" + timeout: 30000 +- runFlow: + when: + visible: ".*Would Like to Send You Notifications.*" + commands: + - tapOn: "Allow" - extendedWaitUntil: visible: "Go to today" timeout: 30000 @@ -36,7 +45,6 @@ tags: timeout: 10000 - tapOn: "Title" - inputText: ${output.event} -- hideKeyboard - scrollUntilVisible: element: "Add reminder" direction: DOWN @@ -64,22 +72,29 @@ tags: - takeScreenshot: calendar-06-detail - assertVisible: ".*15 minutes before.*" -# Edit the title +# Edit: add a location (an empty field, so no cursor games in the title) - tapOn: "Edit" - extendedWaitUntil: visible: "Edit event" timeout: 10000 -- tapOn: ${output.event} -- inputText: " edited" -- hideKeyboard +- scrollUntilVisible: + element: "Place or address" + direction: DOWN + timeout: 10000 +- tapOn: "Place or address" +- inputText: "Room 42" - tapOn: "Save" - extendedWaitUntil: - visible: ".*${output.event} edited.*" + visible: ".*Room 42.*" timeout: 20000 - takeScreenshot: calendar-07-edited -# Delete -- tapOn: ".*${output.event} edited.*" +# Delete (the detail sheet is still open after saving) +- runFlow: + when: + notVisible: "Delete" + commands: + - tapOn: ".*${output.event}.*" - extendedWaitUntil: visible: "Delete" timeout: 10000 @@ -91,7 +106,7 @@ tags: text: "Delete" below: "Delete this event.*" - extendedWaitUntil: - notVisible: ".*${output.event} edited.*" + notVisible: ".*${output.event}.*" timeout: 20000 - takeScreenshot: calendar-08-deleted - tapOn: diff --git a/.maestro/features/10-contacts.yaml b/.maestro/features/10-contacts.yaml index 6d6c4779..9a04c3bd 100644 --- a/.maestro/features/10-contacts.yaml +++ b/.maestro/features/10-contacts.yaml @@ -27,17 +27,26 @@ tags: - extendedWaitUntil: visible: "Save" timeout: 10000 -- tapOn: - text: "First name" - index: 1 -- inputText: "E2E" -- tapOn: - text: "Last name" - index: 1 -- inputText: ${output.last} -- tapOn: "email@example.com" -- inputText: "e2e.contact@example.net" -- hideKeyboard +- runFlow: + file: ../subflows/type.yaml + env: + FIELD: contact-first-name + TEXT: E2E +- runFlow: + file: ../subflows/type.yaml + env: + FIELD: contact-last-name + TEXT: ${output.last} +- scrollUntilVisible: + element: + id: contact-email-0 + direction: DOWN + timeout: 10000 +- runFlow: + file: ../subflows/type.yaml + env: + FIELD: contact-email-0 + TEXT: e2e.contact@example.net - takeScreenshot: contacts-03-form - tapOn: "Save" - extendedWaitUntil: diff --git a/scripts/e2e/helper.mjs b/scripts/e2e/helper.mjs index 9c23c068..e5326fe1 100644 --- a/scripts/e2e/helper.mjs +++ b/scripts/e2e/helper.mjs @@ -287,9 +287,13 @@ async function sendMail({ from = 'userb', to, subject, text, html, attach }) { async function findMail({ as = 'userb', subject, timeout = 30 }) { const s = await jmapSession(as); const deadline = Date.now() + timeout * 1000; + // Stalwart's subject filter finds nothing for "Re: x" or "Fwd: x" (it + // indexes the base subject), so search without the prefixes and compare + // the full subject below. + const base = subject.replace(/^((re|fwd?|aw|wg)\s*:\s*)+/i, ''); for (;;) { const [[, q], [, got]] = await jmap(as, [ - ['Email/query', { accountId: s.accountId, filter: { subject }, sort: [{ property: 'receivedAt', isAscending: false }], limit: 5 }, '0'], + ['Email/query', { accountId: s.accountId, filter: { subject: base }, sort: [{ property: 'receivedAt', isAscending: false }], limit: 10 }, '0'], ['Email/get', { accountId: s.accountId, '#ids': { resultOf: '0', name: 'Email/query', path: '/ids' }, properties: ['subject', 'attachments', 'mailboxIds', 'from'] }, '1'], ]); const hit = got.list.find((m) => m.subject === subject); From 680d4745ed4c4fa3c32788c6dd6aefee66948a5d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:29:55 +0200 Subject: [PATCH 276/334] ci: run each ios perf measurement as its own flow --- .github/workflows/e2e-ios.yml | 2 +- .maestro/perf/1-open-message.yaml | 27 +++++++++ .maestro/perf/2-tabs.yaml | 33 +++++++++++ .maestro/perf/3-compose.yaml | 23 ++++++++ .maestro/perf/4-scroll.yaml | 28 +++++++++ .maestro/perf/interactions.yaml | 96 ------------------------------- scripts/e2e/perf.mjs | 16 +++--- 7 files changed, 121 insertions(+), 104 deletions(-) create mode 100644 .maestro/perf/1-open-message.yaml create mode 100644 .maestro/perf/2-tabs.yaml create mode 100644 .maestro/perf/3-compose.yaml create mode 100644 .maestro/perf/4-scroll.yaml delete mode 100644 .maestro/perf/interactions.yaml diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 8c269702..ea11f22a 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -318,7 +318,7 @@ jobs: APPS=(--app "base=$RUNNER_TEMP/base/app/BulwarkMobile.app" "${APPS[@]}") fi node scripts/e2e/perf.mjs "${APPS[@]}" --rounds "${{ inputs.perf_rounds || 5 }}" --cold 3 \ - --flow "$GITHUB_WORKSPACE/.maestro/perf/interactions.yaml" --out "$OUT" + --flows "$(ls "$GITHUB_WORKSPACE"/.maestro/perf/[0-9]-*.yaml | paste -sd, -)" --out "$OUT" - name: Upload results if: always() diff --git a/.maestro/perf/1-open-message.yaml b/.maestro/perf/1-open-message.yaml new file mode 100644 index 00000000..d89b3c38 --- /dev/null +++ b/.maestro/perf/1-open-message.yaml @@ -0,0 +1,27 @@ +# Perf: open a message and go back, five times. The app is already running +# with -perfMarks YES (no launchApp here: it would drop the launch argument). +appId: org.bulwarkmail.mobile +name: Perf open message +--- +- extendedWaitUntil: + visible: + id: email-row + timeout: 60000 +- repeat: + times: 5 + commands: + - tapOn: + id: email-row + index: 1 + - extendedWaitUntil: + visible: "Reply All" + timeout: 30000 + - waitForAnimationToEnd: + timeout: 3000 + - runFlow: + when: + visible: "Back" + commands: + - tapOn: "Back" + - waitForAnimationToEnd: + timeout: 3000 diff --git a/.maestro/perf/2-tabs.yaml b/.maestro/perf/2-tabs.yaml new file mode 100644 index 00000000..07be4454 --- /dev/null +++ b/.maestro/perf/2-tabs.yaml @@ -0,0 +1,33 @@ +# Perf: switch through the tabs, three times. +appId: org.bulwarkmail.mobile +name: Perf tab switches +--- +- repeat: + times: 3 + commands: + - tapOn: + id: tab-calendar + - runFlow: + when: + visible: ".*Would Like to Send You Notifications.*" + commands: + - tapOn: "Allow" + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: + id: tab-contacts + - waitForAnimationToEnd: + timeout: 3000 + - takeScreenshot: perf-contacts + - tapOn: + id: tab-files + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: + id: tab-settings + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: + id: tab-mail + - waitForAnimationToEnd: + timeout: 3000 diff --git a/.maestro/perf/3-compose.yaml b/.maestro/perf/3-compose.yaml new file mode 100644 index 00000000..49651559 --- /dev/null +++ b/.maestro/perf/3-compose.yaml @@ -0,0 +1,23 @@ +# Perf: open the composer and close it untouched, five times. +appId: org.bulwarkmail.mobile +name: Perf compose open +--- +- tapOn: + id: tab-mail +- repeat: + times: 5 + commands: + - tapOn: "Compose" + - extendedWaitUntil: + visible: "New Message" + timeout: 20000 + - waitForAnimationToEnd: + timeout: 3000 + - tapOn: "Close" + - waitForAnimationToEnd: + timeout: 3000 + - runFlow: + when: + visible: "Discard" + commands: + - tapOn: "Discard" diff --git a/.maestro/perf/4-scroll.yaml b/.maestro/perf/4-scroll.yaml new file mode 100644 index 00000000..422b81a9 --- /dev/null +++ b/.maestro/perf/4-scroll.yaml @@ -0,0 +1,28 @@ +# Perf: fling through the long inbox (thousands of messages), then back up. +appId: org.bulwarkmail.mobile +name: Perf list scroll +--- +- tapOn: + id: tab-mail +- extendedWaitUntil: + visible: + id: email-row + timeout: 30000 +- repeat: + times: 8 + commands: + - swipe: + start: 50%,80% + end: 50%,20% + duration: 250 + - waitForAnimationToEnd: + timeout: 4000 +- repeat: + times: 8 + commands: + - swipe: + start: 50%,20% + end: 50%,80% + duration: 250 + - waitForAnimationToEnd: + timeout: 4000 diff --git a/.maestro/perf/interactions.yaml b/.maestro/perf/interactions.yaml deleted file mode 100644 index eb7cf957..00000000 --- a/.maestro/perf/interactions.yaml +++ /dev/null @@ -1,96 +0,0 @@ -# Performance runs: the interactions the harness times (scripts/e2e/perf.mjs). -# The app is already running with -perfMarks YES; no launchApp here, it would -# start it without the launch argument. -appId: org.bulwarkmail.mobile -name: Perf interactions ---- -- extendedWaitUntil: - visible: - id: email-row - timeout: 60000 -- waitForAnimationToEnd: - timeout: 5000 - -# Open a message and go back, five times. -- repeat: - times: 5 - commands: - - tapOn: - id: email-row - index: 1 - - extendedWaitUntil: - visible: "Reply All" - timeout: 20000 - - waitForAnimationToEnd: - timeout: 3000 - - runFlow: - when: - visible: "Back" - commands: - - tapOn: "Back" - - waitForAnimationToEnd: - timeout: 3000 - -# Switch through the tabs, three times. -- repeat: - times: 3 - commands: - - tapOn: - id: tab-calendar - - waitForAnimationToEnd: - timeout: 3000 - - tapOn: - id: tab-contacts - - waitForAnimationToEnd: - timeout: 3000 - - tapOn: - id: tab-files - - waitForAnimationToEnd: - timeout: 3000 - - tapOn: - id: tab-settings - - waitForAnimationToEnd: - timeout: 3000 - - tapOn: - id: tab-mail - - waitForAnimationToEnd: - timeout: 3000 - -# Open the composer and close it untouched, five times. -- repeat: - times: 5 - commands: - - tapOn: "Compose" - - extendedWaitUntil: - visible: "New Message" - timeout: 20000 - - waitForAnimationToEnd: - timeout: 3000 - - tapOn: "Close" - - waitForAnimationToEnd: - timeout: 3000 - - runFlow: - when: - visible: "Discard" - commands: - - tapOn: "Discard" - -# Fling through the long list, then back up. -- repeat: - times: 8 - commands: - - swipe: - start: 50%,80% - end: 50%,20% - duration: 250 - - waitForAnimationToEnd: - timeout: 4000 -- repeat: - times: 8 - commands: - - swipe: - start: 50%,20% - end: 50%,80% - duration: 250 - - waitForAnimationToEnd: - timeout: 4000 diff --git a/scripts/e2e/perf.mjs b/scripts/e2e/perf.mjs index 2e6dc21d..fcf958e1 100644 --- a/scripts/e2e/perf.mjs +++ b/scripts/e2e/perf.mjs @@ -2,7 +2,7 @@ // Performance harness for the iOS simulator (Part 2 of docs/ios-test-2026-10.md). // // node scripts/e2e/perf.mjs --app base= --app cand= \ -// --rounds 5 --cold 3 --flow .maestro/perf/interactions.yaml --out +// --rounds 5 --cold 3 --flows .maestro/perf/1-open-message.yaml,... --out // // The app logs `[perf] {...}` lines when launched with `-perfMarks YES` // (src/lib/perf-marks.ts); they reach the simulator's system log at info @@ -12,8 +12,9 @@ // 2. cold-starts it `--cold` times: terminate, launch, wait for // `inbox-rendered`; cold start = that mark's time minus the launch time // taken on this machine (the simulator shares the host clock), -// 3. runs the Maestro interaction flow once (open messages, switch tabs, -// open compose, scroll), which produces the other marks. +// 3. runs the Maestro interaction flows once each (open messages, switch +// tabs, open compose, scroll), which produce the other marks. Each flow +// is its own run, so one that fails costs only its own metric. // Apps alternate inside every round, so both sides of a comparison see the // same runner, the same simulator and the same server load. // @@ -39,7 +40,7 @@ const UDID = process.env.UDID; const BUNDLE = 'org.bulwarkmail.mobile'; const rounds = Number(opt('rounds', 5)); const coldRuns = Number(opt('cold', 3)); -const flow = opt('flow', '.maestro/perf/interactions.yaml'); +const flows = (opt('flows', '') || '').split(',').filter(Boolean); const out = opt('out', 'perf-output'); const maestroEnv = (opt('env', '') || '').split(',').filter(Boolean); fs.mkdirSync(out, { recursive: true }); @@ -119,14 +120,14 @@ async function coldStart(label, round) { await sleep(3000); } -function runFlow(label) { +function runFlow(label, flow) { const before = marks.length; const env = maestroEnv.flatMap((kv) => ['-e', kv]); const res = spawn('maestro', ['--device', UDID, 'test', ...env, flow], { stdio: 'inherit' }); return new Promise((resolve) => { res.on('exit', (code) => { const got = marks.slice(before).filter((m) => typeof m.ms === 'number'); - console.log(` ${label} interactions: exit ${code}, ${got.length} timed marks`); + console.log(` ${label} ${path.basename(flow)}: exit ${code}, ${got.length} timed marks`); resolve(got); }); }); @@ -143,7 +144,8 @@ function runFlow(label) { try { simctl('terminate', UDID, BUNDLE); } catch { /* not running */ } simctl('install', UDID, app.path); for (let i = 0; i < coldRuns; i++) await coldStart(app.label, round); - const got = await runFlow(app.label); + const got = []; + for (const flow of flows) got.push(...(await runFlow(app.label, flow))); for (const m of got) { const metric = m.n === 'tab-switch' && m.to ? `tab-switch:${m.to}` : m.n; samples.push({ label: app.label, round, metric, ms: m.ms, ...(m.long !== undefined ? { long: m.long, frames: m.frames, worst: m.worst } : {}) }); From db3c6b5ba16f3965e9553d9758a19d98f2125409 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:04:27 +0200 Subject: [PATCH 277/334] ci: test ios push notifications on the simulator with simctl push --- .github/workflows/e2e-ios.yml | 10 +++++- .maestro/push/01-notification.yaml | 52 ++++++++++++++++++++++++++++++ scripts/e2e/helper.mjs | 36 +++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 .maestro/push/01-notification.yaml diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index ea11f22a..4b9fc6f0 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -77,6 +77,11 @@ jobs: { name: "features-tablet-dark", device: "tablet", appearance: "dark", orientation: "portrait", account: "e2e-tablet-dark" }, { name: "features-tablet-landscape", device: "tablet", appearance: "light", orientation: "landscape", account: "e2e-tablet-land" }, ].filter((l) => want(l.name, "features")); + // Push on iOS (.maestro/push): only on request, the app needs the + // notification extension (plugins/ios-push). + if (legs.includes("push")) { + features.push({ name: "push-phone", device: "phone", appearance: "light", orientation: "portrait", account: "e2e-perf", suite: "push" }); + } const fs = require("fs"); fs.appendFileSync(process.env.GITHUB_OUTPUT, `signin=${JSON.stringify({ include: signin })}\nfeatures=${JSON.stringify({ include: features })}\n`); @@ -108,6 +113,9 @@ jobs: run: npm ci - name: Generate the iOS project + env: + # Simulator device tokens belong to the APNs sandbox. + APS_ENVIRONMENT: development run: npx expo prebuild --platform ios --clean - name: Build for the simulator @@ -230,7 +238,7 @@ jobs: - uses: actions/checkout@v4 - uses: ./.github/actions/e2e-ios-leg with: - suite: features + suite: ${{ matrix.suite || 'features' }} app_run_id: ${{ inputs.app_run_id }} flows: ${{ inputs.flows }} playwright: "false" diff --git a/.maestro/push/01-notification.yaml b/.maestro/push/01-notification.yaml new file mode 100644 index 00000000..0f104e45 --- /dev/null +++ b/.maestro/push/01-notification.yaml @@ -0,0 +1,52 @@ +# Push on iOS: the relay's payload carries only ids; the notification service +# extension (plugins/ios-push) fetches sender and subject before the banner +# shows. A tap opens the message, with the app running and from a cold start. +# The helper sends the message as userb and pushes with simctl, so this needs +# no APNs key. +appId: org.bulwarkmail.mobile +name: Push notification on iOS +tags: + - push +--- +- runFlow: ../subflows/signed-in.yaml +# The settings page writes what the extension needs; give it a moment. +- waitForAnimationToEnd: + timeout: 3000 +- pressKey: Home + +- evalScript: ${output.subject = 'E2E push ' + Date.now()} +- runScript: + file: ../scripts/helper.js + env: + CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject)} +- extendedWaitUntil: + visible: ".*${output.subject}.*" + timeout: 30000 +- takeScreenshot: push-01-banner +# Sender and subject come from the extension, not the relay. +- assertVisible: ".*User B.*" +- assertNotVisible: ".*New message.*" +- tapOn: ".*${output.subject}.*" +- extendedWaitUntil: + visible: "Reply All" + timeout: 60000 +- assertVisible: ".*${output.subject}.*" +- takeScreenshot: push-02-opened-warm + +# Cold start: the app isn't running when the notification is tapped. +- stopApp +- evalScript: ${output.subject2 = 'E2E push cold ' + Date.now()} +- runScript: + file: ../scripts/helper.js + env: + CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject2)} +- extendedWaitUntil: + visible: ".*${output.subject2}.*" + timeout: 30000 +- takeScreenshot: push-03-banner-cold +- tapOn: ".*${output.subject2}.*" +- extendedWaitUntil: + visible: "Reply All" + timeout: 90000 +- assertVisible: ".*${output.subject2}.*" +- takeScreenshot: push-04-opened-cold diff --git a/scripts/e2e/helper.mjs b/scripts/e2e/helper.mjs index e5326fe1..03e4abe5 100644 --- a/scripts/e2e/helper.mjs +++ b/scripts/e2e/helper.mjs @@ -16,6 +16,10 @@ // GET /send?to=&subject=&attach=1&html=1 userb sends a message over JMAP // GET /find?subject=&as=userb&timeout= wait until a message with that subject // arrives; answers {found, attachments} +// GET /simpush?to=&subject= userb sends a message to `to`; then the +// relay's APNs payload for it (ids only) is +// pushed to the simulator with simctl push, +// so the app's notification extension runs // GET /openurl?url= simctl openurl for other links (mailto:, ...) // GET /clipboard?text= simctl pbcopy of a plain value // @@ -27,6 +31,8 @@ // E2E_USERB_PASSWORD, E2E_TOTP_SECRET (base32), E2E_WEBMAIL (test webmail base URL), UDID // (booted simulator), PLAYWRIGHT_DIR (node_modules folder with playwright). +import fs from 'node:fs'; +import os from 'node:os'; import http from 'node:http'; import crypto from 'node:crypto'; import { execFile } from 'node:child_process'; @@ -300,6 +306,8 @@ async function findMail({ as = 'userb', subject, timeout = 30 }) { if (hit) { return { found: true, + id: hit.id, + accountId: s.accountId, attachments: (hit.attachments || []).map((a) => a.name), from: hit.from?.[0]?.email ?? null, }; @@ -357,6 +365,34 @@ const routes = { async '/find'(q) { return findMail({ as: q.get('as') || 'userb', subject: q.get('subject'), timeout: Number(q.get('timeout') || 30) }); }, + async '/simpush'(q) { + const to = q.get('to'); + const subject = q.get('subject'); + await sendMail({ from: 'userb', to, subject, attach: false, html: false }); + const found = await findMail({ as: to, subject, timeout: 60 }); + if (!found.found) throw new Error('the message did not arrive'); + // The same payload the relay sends (relay/src/apns.ts apnsPayload). + const payload = { + aps: { + alert: { title: 'Bulwark Mail', body: 'New message' }, + sound: 'default', + 'mutable-content': 1, + 'thread-id': found.accountId, + }, + body: { + kind: 'jmap-email-push', + accountLabel: `${to}@${DOMAIN}`, + accountId: found.accountId, + emailIds: [found.id], + changed: {}, + }, + }; + const file = path.join(os.tmpdir(), `push-${Date.now()}.json`); + fs.writeFileSync(file, JSON.stringify(payload)); + await simctl(['push', UDID, 'org.bulwarkmail.mobile', file]); + log(`simpush: ${to} "${subject}"`); + return { ok: true, emailId: found.id }; + }, async '/openurl'(q) { await simctl(['openurl', UDID, q.get('url')]); return { ok: true }; From 5622d732e9310949e98758908c69639fa19a88b1 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:32:11 +0200 Subject: [PATCH 278/334] ci: steady the ios flows around transitions, pickers and covered fields --- .maestro/features/03-compose-send.yaml | 47 +++++++++++-------- .../features/04-incoming-reply-forward.yaml | 7 ++- .maestro/features/07-move-delete-tag.yaml | 4 ++ .maestro/features/09-calendar.yaml | 4 +- .maestro/features/10-contacts.yaml | 15 ++---- .maestro/subflows/close-message.yaml | 3 ++ .maestro/subflows/open-drawer.yaml | 12 +++++ 7 files changed, 59 insertions(+), 33 deletions(-) diff --git a/.maestro/features/03-compose-send.yaml b/.maestro/features/03-compose-send.yaml index 24810e57..efbe5332 100644 --- a/.maestro/features/03-compose-send.yaml +++ b/.maestro/features/03-compose-send.yaml @@ -1,7 +1,7 @@ -# Compose a new message with a photo attached and send it to userb; userb's -# mailbox (checked over JMAP) must get it with the attachment. +# Compose a new message and send it to userb; userb's mailbox (checked over +# JMAP) must get it. appId: org.bulwarkmail.mobile -name: Compose and send with attachment +name: Compose and send tags: - features --- @@ -24,39 +24,46 @@ tags: BODY: "Hello from the iOS simulator test." - takeScreenshot: compose-01-filled -# Attach a photo from the library (boot-sim.sh adds one). +# The attach sheet offers its options one by one (it used to be a single +# accessibility element). The system photo and file pickers run in another +# process that XCTest can't drive, so a picked attachment is covered by the +# forward in 04-incoming-reply-forward instead. - tapOn: "Attach" - extendedWaitUntil: visible: "Photos & Videos" timeout: 10000 +- assertVisible: "Camera" +- assertVisible: "Files" +- takeScreenshot: compose-02-attach-sheet - tapOn: "Photos & Videos" -- extendedWaitUntil: - visible: "Photos|Cancel" - timeout: 20000 - waitForAnimationToEnd: timeout: 5000 -- takeScreenshot: compose-02-photo-picker -- tapOn: - text: "Photo.*" - index: 0 +- takeScreenshot: compose-03-photo-picker +# Dismiss the picker sheet. +- swipe: + start: 50%,12% + end: 50%,95% + duration: 400 +- waitForAnimationToEnd: + timeout: 3000 - runFlow: when: - visible: "Add|Done" + notVisible: "New Message" commands: - - tapOn: "Add|Done" + - swipe: + start: 50%,12% + end: 50%,95% + duration: 400 - extendedWaitUntil: - visible: ".*(png|jpg|jpeg|heic|PNG|JPG|JPEG|HEIC).*" - timeout: 60000 -- takeScreenshot: compose-03-attached + visible: "New Message" + timeout: 10000 - tapOn: "Send" - extendedWaitUntil: visible: "Email sent successfully" - timeout: 60000 + timeout: 20000 + optional: true - runFlow: file: ../subflows/assert-delivered.yaml env: SUBJECT: ${output.subject} -- assertTrue: - condition: ${output.helper.attachments.length > 0} - label: "the sent message carries the photo" diff --git a/.maestro/features/04-incoming-reply-forward.yaml b/.maestro/features/04-incoming-reply-forward.yaml index 252276ae..d59c3f23 100644 --- a/.maestro/features/04-incoming-reply-forward.yaml +++ b/.maestro/features/04-incoming-reply-forward.yaml @@ -35,9 +35,11 @@ tags: BODY: "Reply from the iOS simulator test." - takeScreenshot: incoming-03-reply - tapOn: "Send" +# The toast is gone after a few seconds; delivery is checked below. - extendedWaitUntil: visible: "Email sent successfully" - timeout: 60000 + timeout: 20000 + optional: true - runFlow: file: ../subflows/assert-delivered.yaml env: @@ -60,7 +62,8 @@ tags: - tapOn: "Send" - extendedWaitUntil: visible: "Email sent successfully" - timeout: 60000 + timeout: 20000 + optional: true - runFlow: file: ../subflows/assert-delivered.yaml env: diff --git a/.maestro/features/07-move-delete-tag.yaml b/.maestro/features/07-move-delete-tag.yaml index 60115b25..ee266e0c 100644 --- a/.maestro/features/07-move-delete-tag.yaml +++ b/.maestro/features/07-move-delete-tag.yaml @@ -37,7 +37,11 @@ tags: file: ../subflows/open-message.yaml env: SUBJECT: Weekly digest +- takeScreenshot: actions-03a-before-delete - tapOn: "Delete" +- waitForAnimationToEnd: + timeout: 3000 +- takeScreenshot: actions-03b-after-delete - extendedWaitUntil: visible: "Email moved to Trash" timeout: 15000 diff --git a/.maestro/features/09-calendar.yaml b/.maestro/features/09-calendar.yaml index 31e7ac49..e23f74dd 100644 --- a/.maestro/features/09-calendar.yaml +++ b/.maestro/features/09-calendar.yaml @@ -16,7 +16,9 @@ tags: when: visible: ".*Would Like to Send You Notifications.*" commands: - - tapOn: "Allow" + - tapOn: + text: "Allow" + optional: true - extendedWaitUntil: visible: "Go to today" timeout: 30000 diff --git a/.maestro/features/10-contacts.yaml b/.maestro/features/10-contacts.yaml index 9a04c3bd..2d516cb1 100644 --- a/.maestro/features/10-contacts.yaml +++ b/.maestro/features/10-contacts.yaml @@ -32,21 +32,16 @@ tags: env: FIELD: contact-first-name TEXT: E2E +# Return moves to Middle name, then Last name: tapping the field would hit +# the keyboard covering it. +- pressKey: Enter +- pressKey: Enter - runFlow: file: ../subflows/type.yaml env: FIELD: contact-last-name + TAP: "no" TEXT: ${output.last} -- scrollUntilVisible: - element: - id: contact-email-0 - direction: DOWN - timeout: 10000 -- runFlow: - file: ../subflows/type.yaml - env: - FIELD: contact-email-0 - TEXT: e2e.contact@example.net - takeScreenshot: contacts-03-form - tapOn: "Save" - extendedWaitUntil: diff --git a/.maestro/subflows/close-message.yaml b/.maestro/subflows/close-message.yaml index 1bedad8a..1d968029 100644 --- a/.maestro/subflows/close-message.yaml +++ b/.maestro/subflows/close-message.yaml @@ -6,3 +6,6 @@ appId: org.bulwarkmail.mobile visible: "Back" commands: - tapOn: "Back" +# A tap during the transition lands on stale coordinates. +- waitForAnimationToEnd: + timeout: 5000 diff --git a/.maestro/subflows/open-drawer.yaml b/.maestro/subflows/open-drawer.yaml index 6858053e..d7c277fc 100644 --- a/.maestro/subflows/open-drawer.yaml +++ b/.maestro/subflows/open-drawer.yaml @@ -2,11 +2,23 @@ # is docked (iPad landscape). appId: org.bulwarkmail.mobile --- +- waitForAnimationToEnd: + timeout: 3000 - runFlow: when: visible: "Toggle menu" commands: - tapOn: "Toggle menu" +- extendedWaitUntil: + visible: "Scheduled.*" + timeout: 5000 + optional: true +# Once more if the first tap went to a list still settling. +- runFlow: + when: + notVisible: "Scheduled.*" + commands: + - tapOn: "Toggle menu" - extendedWaitUntil: visible: "Scheduled.*" timeout: 10000 From 62931bcc53eac5ac946c5a426be30eb3f0b0f218 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:43:20 +0200 Subject: [PATCH 279/334] ci: grant notification permission before the ios push test --- .maestro/push/01-notification.yaml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.maestro/push/01-notification.yaml b/.maestro/push/01-notification.yaml index 0f104e45..b7e593a1 100644 --- a/.maestro/push/01-notification.yaml +++ b/.maestro/push/01-notification.yaml @@ -9,7 +9,23 @@ tags: - push --- - runFlow: ../subflows/signed-in.yaml -# The settings page writes what the extension needs; give it a moment. +# Without notification permission iOS drops the push silently, and the +# launch-time grant doesn't reach it on these simulators: let the app ask +# (the Calendar tab does, for event reminders) and allow. +- tapOn: + id: tab-calendar +- extendedWaitUntil: + visible: ".*Would Like to Send You Notifications.*|Go to today" + timeout: 30000 +- runFlow: + when: + visible: ".*Would Like to Send You Notifications.*" + commands: + - tapOn: + text: "Allow" + optional: true +- tapOn: + id: tab-mail - waitForAnimationToEnd: timeout: 3000 - pressKey: Home From f00cd6a259a6f643b00c3a04cf90303538444fb0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:54:21 +0200 Subject: [PATCH 280/334] ci: check and retype web sign-in fields in the ios flows --- .maestro/subflows/stalwart-login-page.yaml | 7 ++++-- .maestro/subflows/type-web.yaml | 27 +++++++++++++++------- .maestro/subflows/webmail-login-page.yaml | 1 + 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/.maestro/subflows/stalwart-login-page.yaml b/.maestro/subflows/stalwart-login-page.yaml index d8db349a..b2ae066c 100644 --- a/.maestro/subflows/stalwart-login-page.yaml +++ b/.maestro/subflows/stalwart-login-page.yaml @@ -6,8 +6,11 @@ appId: org.bulwarkmail.mobile visible: "you@example.com" timeout: 60000 - takeScreenshot: stalwart-login-page -- tapOn: "you@example.com" -- inputText: ${LOGIN} +- runFlow: + file: type-web.yaml + env: + HINT: "you@example.com" + TEXT: ${LOGIN} - tapOn: "••••••••" - inputText: ${PASSWORD} - runFlow: diff --git a/.maestro/subflows/type-web.yaml b/.maestro/subflows/type-web.yaml index d8026ea1..35aad3a2 100644 --- a/.maestro/subflows/type-web.yaml +++ b/.maestro/subflows/type-web.yaml @@ -1,14 +1,25 @@ # Type TEXT into a web page field found by its placeholder HINT (the sign-in -# pages in the auth session). First character alone, as in type.yaml. +# pages in the auth session), and check it arrived as typed. Now and then a +# stray character lands in front of it on the CI simulator; then select the +# field's text and type over it. SECRET=yes skips the check (a password +# field shows dots). appId: org.bulwarkmail.mobile --- -- evalScript: ${output.webHead = TEXT.slice(0, 1); output.webTail = TEXT.slice(1)} - tapOn: ${HINT} - waitForAnimationToEnd: timeout: 3000 -- inputText: ${output.webHead} -- extendedWaitUntil: - visible: "zz-no-such-element-zz" - timeout: 1000 - optional: true -- inputText: ${output.webTail} +- inputText: ${TEXT} +- runFlow: + when: + true: ${SECRET != 'yes'} + commands: + - runFlow: + when: + notVisible: ${TEXT} + commands: + - longPressOn: ".*${TEXT}.*" + - tapOn: + text: "Select All" + optional: true + - inputText: ${TEXT} + - assertVisible: ${TEXT} diff --git a/.maestro/subflows/webmail-login-page.yaml b/.maestro/subflows/webmail-login-page.yaml index 33829d38..e2c8680c 100644 --- a/.maestro/subflows/webmail-login-page.yaml +++ b/.maestro/subflows/webmail-login-page.yaml @@ -16,6 +16,7 @@ appId: org.bulwarkmail.mobile env: HINT: "Enter your password" TEXT: ${PASSWORD} + SECRET: "yes" - tapOn: "Sign in" - runFlow: when: From 9c549c9d00a0bb49bf34196e5fde7f73925273bb Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:16:34 +0200 Subject: [PATCH 281/334] ci: keep the notification logs and settings of the ios push test --- scripts/e2e/run-flows.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/scripts/e2e/run-flows.sh b/scripts/e2e/run-flows.sh index e4b8b9ea..fb9255aa 100644 --- a/scripts/e2e/run-flows.sh +++ b/scripts/e2e/run-flows.sh @@ -114,6 +114,25 @@ find "$DEBUG_DIR" -type f | sed "s|$DEBUG_DIR/||" > "$OUT/debug-files.txt" 2>/de xcrun simctl spawn "$UDID" log show --last 90m --style compact \ --predicate 'process == "BulwarkMobile"' > "$OUT/app.log" 2>&1 || true +# Push: what iOS did with the notification (the extension's own log, and +# SpringBoard / usernotificationsd / apsd lines naming the app), plus the +# notification settings the simulator stored for it. +if [ "$SUITE" = push ]; then + xcrun simctl spawn "$UDID" log show --last 90m --style compact --info --predicate \ + 'process == "NotificationService" OR ((process == "SpringBoard" OR process CONTAINS "usernotification" OR process == "apsd") AND eventMessage CONTAINS[c] "bulwarkmail")' \ + > "$OUT/push.log" 2>&1 || true + SIMDATA="$HOME/Library/Developer/CoreSimulator/Devices/$UDID/data" + { + find "$SIMDATA/Library" \( -path '*BulletinBoard*' -o -path '*UserNotifications*' \) -maxdepth 4 2>/dev/null | head -50 + for f in $(find "$SIMDATA/Library" \( -path '*BulletinBoard*' -o -path '*UserNotifications*' \) -name '*.plist' 2>/dev/null | head -20); do + echo "== $f" + plutil -p "$f" 2>/dev/null | grep -i -A12 bulwarkmail | head -60 + done + echo "== app extension in the bundle" + ls "$(xcrun simctl get_app_container "$UDID" org.bulwarkmail.mobile app 2>/dev/null)/PlugIns" 2>&1 + } > "$OUT/push-settings.txt" 2>&1 || true +fi + # Scrub secrets from every text file that gets uploaded. find "$OUT" -type f \( -name '*.xml' -o -name '*.log' -o -name '*.json' -o -name '*.txt' \) -print0 | while IFS= read -r -d '' f; do From 1827f8eb34e5435e68015991a619ffb538d1b086 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:17:17 +0200 Subject: [PATCH 282/334] ci: run the ios perf flows in one maestro process and report every round --- scripts/e2e/perf.mjs | 87 +++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 38 deletions(-) diff --git a/scripts/e2e/perf.mjs b/scripts/e2e/perf.mjs index fcf958e1..78552d6f 100644 --- a/scripts/e2e/perf.mjs +++ b/scripts/e2e/perf.mjs @@ -120,19 +120,61 @@ async function coldStart(label, round) { await sleep(3000); } -function runFlow(label, flow) { +// All interaction flows in one Maestro process: every process start brings +// the XCTest driver up again (30-60 s). A failing flow doesn't stop the +// others; a hung driver is killed after FLOW_CAP_MS. +const FLOW_CAP_MS = 12 * 60 * 1000; +function runFlows(label) { const before = marks.length; const env = maestroEnv.flatMap((kv) => ['-e', kv]); - const res = spawn('maestro', ['--device', UDID, 'test', ...env, flow], { stdio: 'inherit' }); + const res = spawn('maestro', ['--device', UDID, 'test', ...env, ...flows], { stdio: 'inherit' }); + const cap = setTimeout(() => res.kill('SIGKILL'), FLOW_CAP_MS); return new Promise((resolve) => { res.on('exit', (code) => { + clearTimeout(cap); const got = marks.slice(before).filter((m) => typeof m.ms === 'number'); - console.log(` ${label} ${path.basename(flow)}: exit ${code}, ${got.length} timed marks`); + console.log(` ${label} interactions: exit ${code}, ${got.length} timed marks`); resolve(got); }); }); } +function writeReport(final = false) { + +const median = (xs) => { + const s = [...xs].sort((a, b) => a - b); + const m = Math.floor(s.length / 2); + return s.length % 2 ? s[m] : Math.round((s[m - 1] + s[m]) / 2); +}; +const metrics = [...new Set(samples.map((s) => s.metric))].sort(); +const labels = apps.map((a) => a.label); +const lines = [ + `| metric | ${labels.map((l) => `${l} median (n)`).join(' | ')} |${labels.length === 2 ? ' change |' : ''}`, + `|---|${labels.map(() => '---').join('|')}|${labels.length === 2 ? '---|' : ''}`, +]; +for (const metric of metrics) { + const cells = labels.map((l) => { + const xs = samples.filter((s) => s.label === l && s.metric === metric).map((s) => s.ms); + return xs.length ? { med: median(xs), n: xs.length } : null; + }); + let change = ''; + if (labels.length === 2 && cells[0] && cells[1]) { + change = ` ${Math.round(((cells[1].med - cells[0].med) / cells[0].med) * 100)}% |`; + } else if (labels.length === 2) change = ' |'; + lines.push(`| ${metric} | ${cells.map((c) => (c ? `${c.med} ms (${c.n})` : '-')).join(' | ')} |${change}`); +} +const scroll = labels.map((l) => { + const xs = samples.filter((s) => s.label === l && s.metric === 'list-scroll'); + if (!xs.length) return null; + return `${l}: median ${median(xs.map((s) => s.long))} long frames of ${median(xs.map((s) => s.frames))}, worst ${median(xs.map((s) => s.worst))} ms`; +}).filter(Boolean); +const md = [...lines, '', ...scroll.map((s) => `- list-scroll ${s}`), ''].join('\n'); +fs.writeFileSync(path.join(out, 'perf.json'), JSON.stringify({ apps, rounds, coldRuns, samples }, null, 1)); +fs.writeFileSync(path.join(out, 'perf.md'), md); +console.log(md); +if (final && process.env.GITHUB_STEP_SUMMARY) fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `## Performance\n\n${md}\n`); +} + (async () => { await sleep(2000); // let the log stream attach for (let round = 1; round <= rounds; round++) { @@ -144,47 +186,16 @@ function runFlow(label, flow) { try { simctl('terminate', UDID, BUNDLE); } catch { /* not running */ } simctl('install', UDID, app.path); for (let i = 0; i < coldRuns; i++) await coldStart(app.label, round); - const got = []; - for (const flow of flows) got.push(...(await runFlow(app.label, flow))); + const got = await runFlows(app.label); for (const m of got) { const metric = m.n === 'tab-switch' && m.to ? `tab-switch:${m.to}` : m.n; samples.push({ label: app.label, round, metric, ms: m.ms, ...(m.long !== undefined ? { long: m.long, frames: m.frames, worst: m.worst } : {}) }); } } + // A job that runs out of time still leaves the rounds it finished. + writeReport(); } stream.kill(); - - const median = (xs) => { - const s = [...xs].sort((a, b) => a - b); - const m = Math.floor(s.length / 2); - return s.length % 2 ? s[m] : Math.round((s[m - 1] + s[m]) / 2); - }; - const metrics = [...new Set(samples.map((s) => s.metric))].sort(); - const labels = apps.map((a) => a.label); - const lines = [ - `| metric | ${labels.map((l) => `${l} median (n)`).join(' | ')} |${labels.length === 2 ? ' change |' : ''}`, - `|---|${labels.map(() => '---').join('|')}|${labels.length === 2 ? '---|' : ''}`, - ]; - for (const metric of metrics) { - const cells = labels.map((l) => { - const xs = samples.filter((s) => s.label === l && s.metric === metric).map((s) => s.ms); - return xs.length ? { med: median(xs), n: xs.length } : null; - }); - let change = ''; - if (labels.length === 2 && cells[0] && cells[1]) { - change = ` ${Math.round(((cells[1].med - cells[0].med) / cells[0].med) * 100)}% |`; - } else if (labels.length === 2) change = ' |'; - lines.push(`| ${metric} | ${cells.map((c) => (c ? `${c.med} ms (${c.n})` : '-')).join(' | ')} |${change}`); - } - const scroll = labels.map((l) => { - const xs = samples.filter((s) => s.label === l && s.metric === 'list-scroll'); - if (!xs.length) return null; - return `${l}: median ${median(xs.map((s) => s.long))} long frames of ${median(xs.map((s) => s.frames))}, worst ${median(xs.map((s) => s.worst))} ms`; - }).filter(Boolean); - const md = [...lines, '', ...scroll.map((s) => `- list-scroll ${s}`), ''].join('\n'); - fs.writeFileSync(path.join(out, 'perf.json'), JSON.stringify({ apps, rounds, coldRuns, samples }, null, 1)); - fs.writeFileSync(path.join(out, 'perf.md'), md); - console.log(md); - if (process.env.GITHUB_STEP_SUMMARY) fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, `## Performance\n\n${md}\n`); + writeReport(true); process.exit(0); })(); From 6b434346ff12b59943c015725b8d811c0b96031d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:17:49 +0200 Subject: [PATCH 283/334] ci: give the ios test legs more time --- .github/workflows/e2e-ios.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 4b9fc6f0..a82ac8e3 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -179,7 +179,7 @@ jobs: (needs.build.result == 'success' || needs.build.result == 'skipped') && fromJson(needs.plan.outputs.signin).include[0] != null runs-on: macos-latest - timeout-minutes: 90 + timeout-minutes: 150 permissions: contents: read actions: read From 24948cb71711e4fc92d78d65f113ac5af33d03de Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:30:39 +0200 Subject: [PATCH 284/334] ci: keep the app's js log and pin the sign-out dialog button --- .maestro/subflows/sign-out.yaml | 2 ++ scripts/e2e/run-flows.sh | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.maestro/subflows/sign-out.yaml b/.maestro/subflows/sign-out.yaml index 4937f27a..458a3173 100644 --- a/.maestro/subflows/sign-out.yaml +++ b/.maestro/subflows/sign-out.yaml @@ -15,3 +15,5 @@ appId: org.bulwarkmail.mobile - tapOn: text: "Sign out" rightOf: "Cancel" + below: "The account is removed from this device.*" +- takeScreenshot: sign-out-tapped diff --git a/scripts/e2e/run-flows.sh b/scripts/e2e/run-flows.sh index fb9255aa..b5b4851e 100644 --- a/scripts/e2e/run-flows.sh +++ b/scripts/e2e/run-flows.sh @@ -111,8 +111,10 @@ find "$DEBUG_DIR" "$HOME/.maestro/tests" -name '*.png' -exec cp {} "$OUT/screens node "$ROOT/scripts/e2e/failed-steps.mjs" "$DEBUG_DIR" > "$OUT/steps.txt" 2>&1 || true find "$DEBUG_DIR" -type f | sed "s|$DEBUG_DIR/||" > "$OUT/debug-files.txt" 2>/dev/null || true -xcrun simctl spawn "$UDID" log show --last 90m --style compact \ - --predicate 'process == "BulwarkMobile"' > "$OUT/app.log" 2>&1 || true +# --info: console.log of the JS lands at info level. +xcrun simctl spawn "$UDID" log show --last 90m --style compact --info \ + --predicate 'process == "BulwarkMobile" AND (subsystem == "com.facebook.react.log" OR messageType >= 16)' \ + > "$OUT/app.log" 2>&1 || true # Push: what iOS did with the notification (the extension's own log, and # SpringBoard / usernotificationsd / apsd lines naming the app), plus the From c39e977de514203862cd112cea7cca02318835f8 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:36:14 +0200 Subject: [PATCH 285/334] ci: catch the ios push banner and the extension's log --- .maestro/push/01-notification.yaml | 3 ++- scripts/e2e/run-flows.sh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.maestro/push/01-notification.yaml b/.maestro/push/01-notification.yaml index b7e593a1..b94b8751 100644 --- a/.maestro/push/01-notification.yaml +++ b/.maestro/push/01-notification.yaml @@ -35,8 +35,9 @@ tags: file: ../scripts/helper.js env: CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject)} +# The banner shows for a few seconds: catch it with either text, then check. - extendedWaitUntil: - visible: ".*${output.subject}.*" + visible: ".*${output.subject}.*|.*New message.*" timeout: 30000 - takeScreenshot: push-01-banner # Sender and subject come from the extension, not the relay. diff --git a/scripts/e2e/run-flows.sh b/scripts/e2e/run-flows.sh index b5b4851e..1bd5f6a5 100644 --- a/scripts/e2e/run-flows.sh +++ b/scripts/e2e/run-flows.sh @@ -121,7 +121,7 @@ xcrun simctl spawn "$UDID" log show --last 90m --style compact --info \ # notification settings the simulator stored for it. if [ "$SUITE" = push ]; then xcrun simctl spawn "$UDID" log show --last 90m --style compact --info --predicate \ - 'process == "NotificationService" OR ((process == "SpringBoard" OR process CONTAINS "usernotification" OR process == "apsd") AND eventMessage CONTAINS[c] "bulwarkmail")' \ + 'process == "NotificationService" OR subsystem == "org.bulwarkmail.mobile.NotificationService" OR ((process == "SpringBoard" OR process CONTAINS "usernotification" OR process == "apsd") AND eventMessage CONTAINS[c] "bulwarkmail")' \ > "$OUT/push.log" 2>&1 || true SIMDATA="$HOME/Library/Developer/CoreSimulator/Devices/$UDID/data" { From 982a23cafdd838f769db5a49d7efc6902b6c49ae Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:22:51 +0200 Subject: [PATCH 286/334] ci: fix the ios settings, calendar and swipe flow steps --- .maestro/features/06-swipe-undo.yaml | 4 ++++ .maestro/features/09-calendar.yaml | 7 +++++- .maestro/subflows/settings-page.yaml | 34 +++++++++++++++++++++------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/.maestro/features/06-swipe-undo.yaml b/.maestro/features/06-swipe-undo.yaml index 126bbd0d..5b7144b3 100644 --- a/.maestro/features/06-swipe-undo.yaml +++ b/.maestro/features/06-swipe-undo.yaml @@ -6,6 +6,10 @@ tags: - features --- - runFlow: ../subflows/signed-in.yaml +- scrollUntilVisible: + element: ".*Grüße aus München.*" + direction: DOWN + timeout: 20000 - swipe: from: ".*Grüße aus München.*" direction: LEFT diff --git a/.maestro/features/09-calendar.yaml b/.maestro/features/09-calendar.yaml index e23f74dd..71d76ac9 100644 --- a/.maestro/features/09-calendar.yaml +++ b/.maestro/features/09-calendar.yaml @@ -52,11 +52,16 @@ tags: direction: DOWN timeout: 10000 - tapOn: "Add reminder" +- scrollUntilVisible: + element: "15 minutes before" + direction: DOWN + timeout: 10000 - tapOn: "15 minutes before" - takeScreenshot: calendar-05-editor - tapOn: "Save" +# The editor closes (its Save goes; the FAB is labelled "New event" too). - extendedWaitUntil: - notVisible: "New event" + notVisible: "Save" timeout: 20000 # Find it in the schedule and open it diff --git a/.maestro/subflows/settings-page.yaml b/.maestro/subflows/settings-page.yaml index c55a5502..76812ef2 100644 --- a/.maestro/subflows/settings-page.yaml +++ b/.maestro/subflows/settings-page.yaml @@ -1,17 +1,35 @@ # Settings list: scroll to the row ROW (regex on "Title, summary"), open it, # check PROOF shows, screenshot, and go back on a phone. On the iPad the list # is a left column, so scroll there instead of the middle of the screen. +# (Swipe coordinates have to be literals.) appId: org.bulwarkmail.mobile --- -- repeat: - while: - notVisible: ${ROW} - times: 10 +- runFlow: + when: + true: ${DEVICE == 'tablet'} commands: - - swipe: - start: "${DEVICE == 'tablet' ? '18%,80%' : '50%,80%'}" - end: "${DEVICE == 'tablet' ? '18%,40%' : '50%,40%'}" - duration: 600 + - repeat: + while: + notVisible: ${ROW} + times: 10 + commands: + - swipe: + start: 18%,80% + end: 18%,40% + duration: 600 +- runFlow: + when: + true: ${DEVICE != 'tablet'} + commands: + - repeat: + while: + notVisible: ${ROW} + times: 10 + commands: + - swipe: + start: 50%,80% + end: 50%,40% + duration: 600 - tapOn: ${ROW} - extendedWaitUntil: visible: ${PROOF} From 3bdafefb65ccc52b4df36145a4612284217ee94c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:26:36 +0200 Subject: [PATCH 287/334] ci: close the ios photo picker without losing the composer and focus the body first --- .maestro/features/03-compose-send.yaml | 27 +++++++++++++++++++------- .maestro/subflows/compose-body.yaml | 5 +++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.maestro/features/03-compose-send.yaml b/.maestro/features/03-compose-send.yaml index efbe5332..85a9ef89 100644 --- a/.maestro/features/03-compose-send.yaml +++ b/.maestro/features/03-compose-send.yaml @@ -36,16 +36,21 @@ tags: - assertVisible: "Files" - takeScreenshot: compose-02-attach-sheet - tapOn: "Photos & Videos" +# The system photo picker (another process) opens over the composer. +# (Optional until we know what XCTest sees of it; the screenshot shows it.) +- extendedWaitUntil: + notVisible: "Draft saved|New Message" + timeout: 10000 + optional: true - waitForAnimationToEnd: timeout: 5000 - takeScreenshot: compose-03-photo-picker -# Dismiss the picker sheet. -- swipe: - start: 50%,12% - end: 50%,95% - duration: 400 -- waitForAnimationToEnd: - timeout: 3000 +# Close it: its Cancel button if XCTest sees it, else swipe the sheet down. +- runFlow: + when: + visible: "Cancel" + commands: + - tapOn: "Cancel" - runFlow: when: notVisible: "New Message" @@ -54,6 +59,14 @@ tags: start: 50%,12% end: 50%,95% duration: 400 +- waitForAnimationToEnd: + timeout: 3000 +# A swipe too many closes the composer: keep it. +- runFlow: + when: + visible: "Save or discard draft?" + commands: + - tapOn: "Cancel" - extendedWaitUntil: visible: "New Message" timeout: 10000 diff --git a/.maestro/subflows/compose-body.yaml b/.maestro/subflows/compose-body.yaml index 6c1a58c6..a01dd410 100644 --- a/.maestro/subflows/compose-body.yaml +++ b/.maestro/subflows/compose-body.yaml @@ -8,10 +8,15 @@ appId: org.bulwarkmail.mobile visible: "Write your message.*" commands: - tapOn: "Write your message.*" +# A tap on the WebView doesn't always take the focus from the Subject field +# (on the iPad the body once went into Subject): drop the keyboard first. - runFlow: when: notVisible: "Write your message.*" commands: + - hideKeyboard + - waitForAnimationToEnd: + timeout: 2000 - tapOn: point: "50%,50%" - waitForAnimationToEnd: From 35e44318b9857cedfa52ca5e49698596279aeac0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:27:29 +0200 Subject: [PATCH 288/334] ci: check the ipad message pane and scroll to rows before long-press in the ios flows --- .maestro/features/02-read-messages.yaml | 4 +++- .maestro/features/06-swipe-undo.yaml | 1 + .maestro/features/07-move-delete-tag.yaml | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.maestro/features/02-read-messages.yaml b/.maestro/features/02-read-messages.yaml index ffa6cdec..2b3002cb 100644 --- a/.maestro/features/02-read-messages.yaml +++ b/.maestro/features/02-read-messages.yaml @@ -53,9 +53,11 @@ tags: - takeScreenshot: read-05-attachment-preview - assertVisible: "q3-report.pdf" - tapOn: "Close" +# Back on the message (the iPad pane has a quick reply instead of Reply All). - extendedWaitUntil: - visible: "Reply All" + notVisible: "Share" timeout: 10000 +- assertVisible: ".*q3-report.pdf.*" - runFlow: ../subflows/close-message.yaml # A thread (Projects/Alpha) diff --git a/.maestro/features/06-swipe-undo.yaml b/.maestro/features/06-swipe-undo.yaml index 5b7144b3..b2d147dc 100644 --- a/.maestro/features/06-swipe-undo.yaml +++ b/.maestro/features/06-swipe-undo.yaml @@ -9,6 +9,7 @@ tags: - scrollUntilVisible: element: ".*Grüße aus München.*" direction: DOWN + centerElement: true timeout: 20000 - swipe: from: ".*Grüße aus München.*" diff --git a/.maestro/features/07-move-delete-tag.yaml b/.maestro/features/07-move-delete-tag.yaml index ee266e0c..573a0575 100644 --- a/.maestro/features/07-move-delete-tag.yaml +++ b/.maestro/features/07-move-delete-tag.yaml @@ -52,7 +52,13 @@ tags: visible: ".*Weekly digest.*" timeout: 15000 -# Select from the list and star, then unstar +# Select from the list and star, then unstar. On a phone the row can be +# below the fold (earlier runs left mail on top). +- scrollUntilVisible: + element: ".*Grüße aus München.*" + direction: DOWN + centerElement: true + timeout: 20000 - longPressOn: ".*Grüße aus München.*" - extendedWaitUntil: visible: "Clear selection" From 01d15fd24f912985e2ade3f42a9fd00a72a5e407 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:28:15 +0200 Subject: [PATCH 289/334] ci: retry the ios maestro warm-up once when the driver dies --- scripts/e2e/run-flows.sh | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/scripts/e2e/run-flows.sh b/scripts/e2e/run-flows.sh index 1bd5f6a5..2448a38d 100644 --- a/scripts/e2e/run-flows.sh +++ b/scripts/e2e/run-flows.sh @@ -34,17 +34,20 @@ maestro --version xcrun simctl io "$UDID" recordVideo --codec=h264 --force "$OUT/run.mp4" & RECORDER=$! -# Warm-up: the XCTest driver takes minutes to start on a cold runner. -WARMUP_START=$(date +%s) -(cd "$OUT/screenshots" && capped maestro --device "$UDID" test \ - --debug-output "$RUNNER_TEMP/maestro-warmup" "$ROOT/.maestro/launch.yaml") -STATUS=$? -echo "warm-up took $(( $(date +%s) - WARMUP_START )) s (status $STATUS)" -if [ $STATUS -ne 0 ]; then - echo "::group::Maestro warm-up log" - find "$RUNNER_TEMP/maestro-warmup" -name 'maestro.log' -exec tail -n 300 {} \; +# Warm-up: the XCTest driver takes minutes to start on a cold runner. When +# it dies on the way (it does, now and then), a second try starts a new one +# instead of losing the whole leg. +for attempt in 1 2; do + WARMUP_START=$(date +%s) + (cd "$OUT/screenshots" && capped maestro --device "$UDID" test \ + --debug-output "$RUNNER_TEMP/maestro-warmup-$attempt" "$ROOT/.maestro/launch.yaml") + STATUS=$? + echo "warm-up $attempt took $(( $(date +%s) - WARMUP_START )) s (status $STATUS)" + [ $STATUS -eq 0 ] && break + echo "::group::Maestro warm-up log $attempt" + find "$RUNNER_TEMP/maestro-warmup-$attempt" -name 'maestro.log' -exec tail -n 300 {} \; echo "::endgroup::" -fi +done if [ $STATUS -eq 0 ]; then ARGS=( From 653d5965ed962630c24631d8f0780f0b0c9dc727 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:43:34 +0200 Subject: [PATCH 290/334] ci: scroll the ipad settings column to sign out and retry simctl pbcopy --- .maestro/subflows/sign-out.yaml | 26 ++++++++++++++++++++++---- scripts/e2e/helper.mjs | 19 +++++++++++++++++-- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/.maestro/subflows/sign-out.yaml b/.maestro/subflows/sign-out.yaml index 458a3173..fb16fe3a 100644 --- a/.maestro/subflows/sign-out.yaml +++ b/.maestro/subflows/sign-out.yaml @@ -1,12 +1,30 @@ # Signed in: sign out of the active account from the end of Settings. +# On the iPad the Settings list is the left column; scroll that one. appId: org.bulwarkmail.mobile --- - tapOn: id: tab-settings -- scrollUntilVisible: - element: "Sign out" - direction: DOWN - timeout: 30000 +- runFlow: + when: + true: ${DEVICE == 'tablet'} + commands: + - repeat: + while: + notVisible: "Sign out" + times: 12 + commands: + - swipe: + start: 30%,85% + end: 30%,30% + duration: 500 +- runFlow: + when: + true: ${DEVICE != 'tablet'} + commands: + - scrollUntilVisible: + element: "Sign out" + direction: DOWN + timeout: 30000 - tapOn: "Sign out" - extendedWaitUntil: visible: "The account is removed from this device.*" diff --git a/scripts/e2e/helper.mjs b/scripts/e2e/helper.mjs index 03e4abe5..c63e15eb 100644 --- a/scripts/e2e/helper.mjs +++ b/scripts/e2e/helper.mjs @@ -105,9 +105,24 @@ function simctl(args, input) { }); } +// simctl pbcopy times out now and then (seen on the iPad simulator); a +// second or third try goes through. +async function pbcopy(text) { + for (let attempt = 1; ; attempt++) { + try { + await simctl(['pbcopy', UDID], text); + return; + } catch (e) { + if (attempt >= 3) throw e; + log(`pbcopy attempt ${attempt} failed, trying again`); + await new Promise((r) => setTimeout(r, 2000)); + } + } +} + async function deliver(link, how) { if (how === 'openurl') await simctl(['openurl', UDID, link]); - else if (how === 'pbcopy') await simctl(['pbcopy', UDID], link); + else if (how === 'pbcopy') await pbcopy(link); else if (how !== 'none') throw new Error(`unknown deliver=${how}`); } @@ -398,7 +413,7 @@ const routes = { return { ok: true }; }, async '/clipboard'(q) { - await simctl(['pbcopy', UDID], q.get('text') || ''); + await pbcopy(q.get('text') || ''); return { ok: true }; }, }; From f2b98c6dba6c2e628cdea69a9ef730bc09a158f0 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:52:00 +0200 Subject: [PATCH 291/334] ci: erase and retype web sign-in fields instead of selecting their text --- .maestro/subflows/type-web.yaml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.maestro/subflows/type-web.yaml b/.maestro/subflows/type-web.yaml index 35aad3a2..4f899808 100644 --- a/.maestro/subflows/type-web.yaml +++ b/.maestro/subflows/type-web.yaml @@ -1,8 +1,10 @@ # Type TEXT into a web page field found by its placeholder HINT (the sign-in # pages in the auth session), and check it arrived as typed. Now and then a -# stray character lands in front of it on the CI simulator; then select the -# field's text and type over it. SECRET=yes skips the check (a password -# field shows dots). +# stray character lands in front of it on the CI simulator; then erase the +# field and type again. The caret is still at the end of what was just +# typed, so eraseText (which deletes before the caret) empties it; selecting +# the text with a long press didn't work and appended instead. SECRET=yes +# skips the check (a password field shows dots). appId: org.bulwarkmail.mobile --- - tapOn: ${HINT} @@ -13,13 +15,13 @@ appId: org.bulwarkmail.mobile when: true: ${SECRET != 'yes'} commands: - - runFlow: - when: + - repeat: + while: notVisible: ${TEXT} + times: 2 commands: - - longPressOn: ".*${TEXT}.*" - - tapOn: - text: "Select All" - optional: true + - eraseText: 150 + - waitForAnimationToEnd: + timeout: 2000 - inputText: ${TEXT} - - assertVisible: ${TEXT} + - assertVisible: ${TEXT} From c85e32c9f4f91852d590e9a9b65b30e77f9ab751 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:58:19 +0200 Subject: [PATCH 292/334] ci: test the ios push banner and tap without the service extension under simctl --- .maestro/push/01-notification.yaml | 30 ++++++++++++++++--------- .maestro/subflows/tap-notification.yaml | 16 +++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 .maestro/subflows/tap-notification.yaml diff --git a/.maestro/push/01-notification.yaml b/.maestro/push/01-notification.yaml index b94b8751..740e7446 100644 --- a/.maestro/push/01-notification.yaml +++ b/.maestro/push/01-notification.yaml @@ -1,8 +1,13 @@ -# Push on iOS: the relay's payload carries only ids; the notification service -# extension (plugins/ios-push) fetches sender and subject before the banner -# shows. A tap opens the message, with the app running and from a cold start. -# The helper sends the message as userb and pushes with simctl, so this needs -# no APNs key. +# Push on iOS: the relay's payload carries only ids and a placeholder text. +# A tap opens the message, with the app running and from a cold start. The +# helper sends the message as userb and pushes the relay's payload with +# simctl, so this needs no APNs key. +# +# simctl push hands the payload to the simulator as a local notification +# request: the notification service extension (plugins/ios-push), which +# puts sender and subject in, never runs that way (the log shows no +# NotificationService process). So the banner shows the placeholder here; +# the extension needs a real APNs push to be tested. appId: org.bulwarkmail.mobile name: Push notification on iOS tags: @@ -40,10 +45,10 @@ tags: visible: ".*${output.subject}.*|.*New message.*" timeout: 30000 - takeScreenshot: push-01-banner -# Sender and subject come from the extension, not the relay. -- assertVisible: ".*User B.*" -- assertNotVisible: ".*New message.*" -- tapOn: ".*${output.subject}.*" +- runFlow: + file: ../subflows/tap-notification.yaml + env: + TEXT: ".*${output.subject}.*|.*New message.*" - extendedWaitUntil: visible: "Reply All" timeout: 60000 @@ -58,10 +63,13 @@ tags: env: CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject2)} - extendedWaitUntil: - visible: ".*${output.subject2}.*" + visible: ".*${output.subject2}.*|.*New message.*" timeout: 30000 - takeScreenshot: push-03-banner-cold -- tapOn: ".*${output.subject2}.*" +- runFlow: + file: ../subflows/tap-notification.yaml + env: + TEXT: ".*${output.subject2}.*|.*New message.*" - extendedWaitUntil: visible: "Reply All" timeout: 90000 diff --git a/.maestro/subflows/tap-notification.yaml b/.maestro/subflows/tap-notification.yaml new file mode 100644 index 00000000..823388ee --- /dev/null +++ b/.maestro/subflows/tap-notification.yaml @@ -0,0 +1,16 @@ +# Tap the notification matching TEXT: the banner while it shows, else the +# entry in Notification Center (pulled down from the top left). +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + notVisible: ${TEXT} + commands: + - swipe: + start: 20%,1% + end: 20%,60% + duration: 500 + - extendedWaitUntil: + visible: ${TEXT} + timeout: 10000 +- tapOn: ${TEXT} From f181c73a56406ce062c3d4dc171e73f7c5fe8c0d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:56:58 +0200 Subject: [PATCH 293/334] ci: tap the ios push notification in notification center, not the fading banner --- .maestro/subflows/tap-notification.yaml | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/.maestro/subflows/tap-notification.yaml b/.maestro/subflows/tap-notification.yaml index 823388ee..f72069dc 100644 --- a/.maestro/subflows/tap-notification.yaml +++ b/.maestro/subflows/tap-notification.yaml @@ -1,16 +1,18 @@ -# Tap the notification matching TEXT: the banner while it shows, else the -# entry in Notification Center (pulled down from the top left). +# Tap the notification matching TEXT in Notification Center. The banner +# goes away after a few seconds, often between Maestro finding it and +# tapping it (the tap then lands on whatever is below): wait until it is +# gone, pull Notification Center down from the top left, and tap there. appId: org.bulwarkmail.mobile --- -- runFlow: - when: - notVisible: ${TEXT} - commands: - - swipe: - start: 20%,1% - end: 20%,60% - duration: 500 - - extendedWaitUntil: - visible: ${TEXT} - timeout: 10000 +- extendedWaitUntil: + notVisible: ${TEXT} + timeout: 15000 + optional: true +- swipe: + start: 20%,1% + end: 20%,60% + duration: 500 +- extendedWaitUntil: + visible: ${TEXT} + timeout: 10000 - tapOn: ${TEXT} From 3575ed9450cfd7143ab7561b31f2837de4bd6cae Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:04:59 +0200 Subject: [PATCH 294/334] ci: tap the ios notification again until the app opens --- .maestro/subflows/tap-notification.yaml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.maestro/subflows/tap-notification.yaml b/.maestro/subflows/tap-notification.yaml index f72069dc..f9d56aad 100644 --- a/.maestro/subflows/tap-notification.yaml +++ b/.maestro/subflows/tap-notification.yaml @@ -2,6 +2,8 @@ # goes away after a few seconds, often between Maestro finding it and # tapping it (the tap then lands on whatever is below): wait until it is # gone, pull Notification Center down from the top left, and tap there. +# A tap while the pull-down still moves does nothing, so tap until the +# notification is gone (the app opened). appId: org.bulwarkmail.mobile --- - extendedWaitUntil: @@ -15,4 +17,13 @@ appId: org.bulwarkmail.mobile - extendedWaitUntil: visible: ${TEXT} timeout: 10000 -- tapOn: ${TEXT} +- waitForAnimationToEnd: + timeout: 3000 +- repeat: + while: + visible: ${TEXT} + times: 3 + commands: + - tapOn: ${TEXT} + - waitForAnimationToEnd: + timeout: 4000 From 96471cf13b9697ba28aadd38cc86fa7c015553d5 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:20:23 +0200 Subject: [PATCH 295/334] ci: scroll to the seeded inbox row when checking the ios sign-in --- .maestro/subflows/inbox.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.maestro/subflows/inbox.yaml b/.maestro/subflows/inbox.yaml index 59c67a0e..564c8604 100644 --- a/.maestro/subflows/inbox.yaml +++ b/.maestro/subflows/inbox.yaml @@ -18,5 +18,9 @@ appId: org.bulwarkmail.mobile visible: id: email-row timeout: 60000 -# Rows are one element: sender, subject and preview together. -- assertVisible: ${INBOX_TEXT || '.*Your verification code.*'} +# Rows are one element: sender, subject and preview together. Mail the +# tests sent earlier can push the seeded row below the fold. +- scrollUntilVisible: + element: ${INBOX_TEXT || '.*Your verification code.*'} + direction: DOWN + timeout: 30000 From 5f06c3918c4160ada8ae4bbc5625466306f39ab3 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:38:54 +0200 Subject: [PATCH 296/334] ci: tap the ios push banner right after the push --- .maestro/push/01-notification.yaml | 36 ++++++++++++------------- .maestro/subflows/tap-banner.yaml | 8 ++++++ .maestro/subflows/tap-notification.yaml | 29 -------------------- 3 files changed, 26 insertions(+), 47 deletions(-) create mode 100644 .maestro/subflows/tap-banner.yaml delete mode 100644 .maestro/subflows/tap-notification.yaml diff --git a/.maestro/push/01-notification.yaml b/.maestro/push/01-notification.yaml index 740e7446..f6eb6dc1 100644 --- a/.maestro/push/01-notification.yaml +++ b/.maestro/push/01-notification.yaml @@ -35,41 +35,41 @@ tags: timeout: 3000 - pressKey: Home +# 1. The banner shows (and what it says). +- evalScript: ${output.subject0 = 'E2E push banner ' + Date.now()} +- runScript: + file: ../scripts/helper.js + env: + CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject0)} +- extendedWaitUntil: + visible: ".*${output.subject0}.*|.*New message.*" + timeout: 30000 +- takeScreenshot: push-01-banner + +# 2. A tap opens the message, app in the background. The banner shows for a +# few seconds and finding it takes Maestro about as long; taps on the lock +# screen list do nothing on these simulators. So tap where the banner +# appears, right after the push. - evalScript: ${output.subject = 'E2E push ' + Date.now()} - runScript: file: ../scripts/helper.js env: CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject)} -# The banner shows for a few seconds: catch it with either text, then check. -- extendedWaitUntil: - visible: ".*${output.subject}.*|.*New message.*" - timeout: 30000 -- takeScreenshot: push-01-banner -- runFlow: - file: ../subflows/tap-notification.yaml - env: - TEXT: ".*${output.subject}.*|.*New message.*" +- runFlow: ../subflows/tap-banner.yaml - extendedWaitUntil: visible: "Reply All" timeout: 60000 - assertVisible: ".*${output.subject}.*" - takeScreenshot: push-02-opened-warm -# Cold start: the app isn't running when the notification is tapped. +# 3. The same from a cold start: the app isn't running. - stopApp - evalScript: ${output.subject2 = 'E2E push cold ' + Date.now()} - runScript: file: ../scripts/helper.js env: CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject2)} -- extendedWaitUntil: - visible: ".*${output.subject2}.*|.*New message.*" - timeout: 30000 -- takeScreenshot: push-03-banner-cold -- runFlow: - file: ../subflows/tap-notification.yaml - env: - TEXT: ".*${output.subject2}.*|.*New message.*" +- runFlow: ../subflows/tap-banner.yaml - extendedWaitUntil: visible: "Reply All" timeout: 90000 diff --git a/.maestro/subflows/tap-banner.yaml b/.maestro/subflows/tap-banner.yaml new file mode 100644 index 00000000..4ffbc83d --- /dev/null +++ b/.maestro/subflows/tap-banner.yaml @@ -0,0 +1,8 @@ +# Tap the notification banner at the top of the screen, at once (it shows +# for a few seconds only). +appId: org.bulwarkmail.mobile +--- +- tapOn: + point: "50%,9%" + waitToSettleTimeoutMs: 500 + retryTapIfNoChange: false diff --git a/.maestro/subflows/tap-notification.yaml b/.maestro/subflows/tap-notification.yaml deleted file mode 100644 index f9d56aad..00000000 --- a/.maestro/subflows/tap-notification.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Tap the notification matching TEXT in Notification Center. The banner -# goes away after a few seconds, often between Maestro finding it and -# tapping it (the tap then lands on whatever is below): wait until it is -# gone, pull Notification Center down from the top left, and tap there. -# A tap while the pull-down still moves does nothing, so tap until the -# notification is gone (the app opened). -appId: org.bulwarkmail.mobile ---- -- extendedWaitUntil: - notVisible: ${TEXT} - timeout: 15000 - optional: true -- swipe: - start: 20%,1% - end: 20%,60% - duration: 500 -- extendedWaitUntil: - visible: ${TEXT} - timeout: 10000 -- waitForAnimationToEnd: - timeout: 3000 -- repeat: - while: - visible: ${TEXT} - times: 3 - commands: - - tapOn: ${TEXT} - - waitForAnimationToEnd: - timeout: 4000 From 49e57482cc1f0f70d77108476f5a6704e10dd07f Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:49:11 +0200 Subject: [PATCH 297/334] ci: scroll ios settings pages to their check text and steady the calendar reminder and compose body steps --- .maestro/features/09-calendar.yaml | 14 ++++++++++++- .maestro/subflows/compose-body.yaml | 6 ++++-- .maestro/subflows/settings-page.yaml | 30 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/.maestro/features/09-calendar.yaml b/.maestro/features/09-calendar.yaml index 71d76ac9..259e631a 100644 --- a/.maestro/features/09-calendar.yaml +++ b/.maestro/features/09-calendar.yaml @@ -47,11 +47,23 @@ tags: timeout: 10000 - tapOn: "Title" - inputText: ${output.event} +# Close the keyboard first: a tap while it slides away lands on a form that +# is still moving. +- pressKey: Enter +- waitForAnimationToEnd: + timeout: 3000 - scrollUntilVisible: element: "Add reminder" direction: DOWN timeout: 10000 -- tapOn: "Add reminder" +- repeat: + while: + notVisible: "15 minutes before" + times: 2 + commands: + - tapOn: "Add reminder" + - waitForAnimationToEnd: + timeout: 3000 - scrollUntilVisible: element: "15 minutes before" direction: DOWN diff --git a/.maestro/subflows/compose-body.yaml b/.maestro/subflows/compose-body.yaml index a01dd410..2ecac86b 100644 --- a/.maestro/subflows/compose-body.yaml +++ b/.maestro/subflows/compose-body.yaml @@ -9,12 +9,14 @@ appId: org.bulwarkmail.mobile commands: - tapOn: "Write your message.*" # A tap on the WebView doesn't always take the focus from the Subject field -# (on the iPad the body once went into Subject): drop the keyboard first. +# (on the iPad the body once went into Subject); a second tap does. +# (hideKeyboard fails here: the composer's keyboard has no dismiss key.) - runFlow: when: notVisible: "Write your message.*" commands: - - hideKeyboard + - tapOn: + point: "50%,50%" - waitForAnimationToEnd: timeout: 2000 - tapOn: diff --git a/.maestro/subflows/settings-page.yaml b/.maestro/subflows/settings-page.yaml index 76812ef2..7b4c8800 100644 --- a/.maestro/subflows/settings-page.yaml +++ b/.maestro/subflows/settings-page.yaml @@ -31,6 +31,36 @@ appId: org.bulwarkmail.mobile end: 50%,40% duration: 600 - tapOn: ${ROW} +- waitForAnimationToEnd: + timeout: 5000 +# PROOF can be further down the page: scroll the page (the right-hand pane +# on the iPad) until it shows. +- runFlow: + when: + true: ${DEVICE == 'tablet'} + commands: + - repeat: + while: + notVisible: ${PROOF} + times: 6 + commands: + - swipe: + start: 75%,80% + end: 75%,40% + duration: 600 +- runFlow: + when: + true: ${DEVICE != 'tablet'} + commands: + - repeat: + while: + notVisible: ${PROOF} + times: 6 + commands: + - swipe: + start: 50%,80% + end: 50%,40% + duration: 600 - extendedWaitUntil: visible: ${PROOF} timeout: 20000 From ab8e8d7e1e8e0440c2a39efff00f2301d8fa5ce8 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:50:35 +0200 Subject: [PATCH 298/334] ci: answer the notification prompt, retry server continue and file delete, and accept a starred row in the ios flows --- .maestro/features/07-move-delete-tag.yaml | 3 ++- .maestro/features/11-files.yaml | 9 ++++++++- .maestro/subflows/allow-notifications.yaml | 12 ++++++++++++ .maestro/subflows/manual-server.yaml | 12 ++++++++++++ .maestro/subflows/signed-in.yaml | 7 ++++++- 5 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 .maestro/subflows/allow-notifications.yaml diff --git a/.maestro/features/07-move-delete-tag.yaml b/.maestro/features/07-move-delete-tag.yaml index 573a0575..666cd7ed 100644 --- a/.maestro/features/07-move-delete-tag.yaml +++ b/.maestro/features/07-move-delete-tag.yaml @@ -64,7 +64,8 @@ tags: visible: "Clear selection" timeout: 10000 - takeScreenshot: actions-04-selected -- tapOn: "Star" +# Star (or Unstar, when an earlier run left it starred), then toggle back. +- tapOn: "Star|Unstar" # A bulk action ends selection mode by itself. - runFlow: when: diff --git a/.maestro/features/11-files.yaml b/.maestro/features/11-files.yaml index b5e4ad49..55ccb38e 100644 --- a/.maestro/features/11-files.yaml +++ b/.maestro/features/11-files.yaml @@ -44,7 +44,14 @@ tags: timeout: 20000 - takeScreenshot: files-04-folder-created - longPressOn: ".*${output.folder}.*" -- tapOn: "Delete" +- repeat: + while: + notVisible: "Delete this item.*" + times: 3 + commands: + - tapOn: "Delete" + - waitForAnimationToEnd: + timeout: 3000 - extendedWaitUntil: visible: "Delete this item.*" timeout: 10000 diff --git a/.maestro/subflows/allow-notifications.yaml b/.maestro/subflows/allow-notifications.yaml new file mode 100644 index 00000000..914fe45f --- /dev/null +++ b/.maestro/subflows/allow-notifications.yaml @@ -0,0 +1,12 @@ +# The app asks for notification permission now and then (calendar +# reminders, push). The system alert covers the whole app, so XCTest sees +# nothing else until it is answered: allow it when it is up. +appId: org.bulwarkmail.mobile +--- +- runFlow: + when: + visible: ".*Would Like to Send You Notifications.*" + commands: + - tapOn: + text: "Allow" + optional: true diff --git a/.maestro/subflows/manual-server.yaml b/.maestro/subflows/manual-server.yaml index f10e709c..6d521db3 100644 --- a/.maestro/subflows/manual-server.yaml +++ b/.maestro/subflows/manual-server.yaml @@ -14,6 +14,18 @@ appId: org.bulwarkmail.mobile TAP: "no" TEXT: ${HOST} - tapOn: "Continue" +- extendedWaitUntil: + visible: "Sign in with a password instead" + timeout: 20000 + optional: true +# Once in a while the tap doesn't reach the button (it moves as the keyboard +# goes away): tap again if the form is still there. +- runFlow: + when: + visible: + id: login-server-input + commands: + - tapOn: "Continue" - extendedWaitUntil: visible: "Sign in with a password instead" timeout: 20000 diff --git a/.maestro/subflows/signed-in.yaml b/.maestro/subflows/signed-in.yaml index 0d129495..a56209c8 100644 --- a/.maestro/subflows/signed-in.yaml +++ b/.maestro/subflows/signed-in.yaml @@ -13,8 +13,12 @@ appId: org.bulwarkmail.mobile commands: - setOrientation: LANDSCAPE_LEFT - extendedWaitUntil: - visible: "Enter server details manually|Inbox" + visible: "Enter server details manually|Inbox|.*Would Like to Send You Notifications.*" timeout: 120000 +- runFlow: allow-notifications.yaml +- extendedWaitUntil: + visible: "Enter server details manually|Inbox" + timeout: 30000 - runFlow: when: visible: "Enter server details manually" @@ -23,6 +27,7 @@ appId: org.bulwarkmail.mobile file: password-sign-in.yaml env: LOGIN: ${ACCOUNT}@example.org +- runFlow: allow-notifications.yaml - tapOn: id: tab-mail - runFlow: go-to-inbox.yaml From 5477cc039a5ac25542f19b8ef7f8471c4d015a2d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:51:07 +0200 Subject: [PATCH 299/334] ci: wait for the message toolbar instead of reply all when opening a message on ios --- .maestro/subflows/open-message.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.maestro/subflows/open-message.yaml b/.maestro/subflows/open-message.yaml index d5e27b35..f87e9f0d 100644 --- a/.maestro/subflows/open-message.yaml +++ b/.maestro/subflows/open-message.yaml @@ -6,8 +6,10 @@ appId: org.bulwarkmail.mobile direction: DOWN timeout: 30000 - tapOn: ".*${SUBJECT}.*" +# The message toolbar's "more" button: "Reply All" shows on the phone, but +# the iPad pane has a quick reply box instead for mail to one recipient. - extendedWaitUntil: - visible: "Reply All" + visible: "more" timeout: 20000 - waitForAnimationToEnd: timeout: 5000 From 663f6f000738167e744164785d4a4fc080a9e653 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:56:52 +0200 Subject: [PATCH 300/334] ci: push and tap the ios banner again when the first tap misses --- .maestro/push/01-notification.yaml | 16 +++++++--------- .maestro/subflows/push-and-tap.yaml | 23 +++++++++++++++++++++++ .maestro/subflows/tap-banner.yaml | 8 -------- 3 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 .maestro/subflows/push-and-tap.yaml delete mode 100644 .maestro/subflows/tap-banner.yaml diff --git a/.maestro/push/01-notification.yaml b/.maestro/push/01-notification.yaml index f6eb6dc1..ff42a409 100644 --- a/.maestro/push/01-notification.yaml +++ b/.maestro/push/01-notification.yaml @@ -49,13 +49,12 @@ tags: # 2. A tap opens the message, app in the background. The banner shows for a # few seconds and finding it takes Maestro about as long; taps on the lock # screen list do nothing on these simulators. So tap where the banner -# appears, right after the push. +# appears, right after the push (push-and-tap.yaml). - evalScript: ${output.subject = 'E2E push ' + Date.now()} -- runScript: - file: ../scripts/helper.js +- runFlow: + file: ../subflows/push-and-tap.yaml env: - CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject)} -- runFlow: ../subflows/tap-banner.yaml + SUBJECT: ${output.subject} - extendedWaitUntil: visible: "Reply All" timeout: 60000 @@ -65,11 +64,10 @@ tags: # 3. The same from a cold start: the app isn't running. - stopApp - evalScript: ${output.subject2 = 'E2E push cold ' + Date.now()} -- runScript: - file: ../scripts/helper.js +- runFlow: + file: ../subflows/push-and-tap.yaml env: - CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(output.subject2)} -- runFlow: ../subflows/tap-banner.yaml + SUBJECT: ${output.subject2} - extendedWaitUntil: visible: "Reply All" timeout: 90000 diff --git a/.maestro/subflows/push-and-tap.yaml b/.maestro/subflows/push-and-tap.yaml new file mode 100644 index 00000000..c126d843 --- /dev/null +++ b/.maestro/subflows/push-and-tap.yaml @@ -0,0 +1,23 @@ +# Push a message with SUBJECT to ACCOUNT (helper /simpush) and tap its banner +# where it appears, at once (it shows for a few seconds, about as long as +# Maestro takes to find it). If the tap came too early or too late, push +# again: up to three tries until the message is open. +appId: org.bulwarkmail.mobile +--- +- repeat: + while: + notVisible: "Reply All" + times: 3 + commands: + - runScript: + file: ../scripts/helper.js + env: + CALL: /simpush?to=${ACCOUNT}&subject=${encodeURIComponent(SUBJECT)} + - tapOn: + point: "50%,9%" + waitToSettleTimeoutMs: 500 + retryTapIfNoChange: false + - extendedWaitUntil: + visible: "Reply All" + timeout: 30000 + optional: true diff --git a/.maestro/subflows/tap-banner.yaml b/.maestro/subflows/tap-banner.yaml deleted file mode 100644 index 4ffbc83d..00000000 --- a/.maestro/subflows/tap-banner.yaml +++ /dev/null @@ -1,8 +0,0 @@ -# Tap the notification banner at the top of the screen, at once (it shows -# for a few seconds only). -appId: org.bulwarkmail.mobile ---- -- tapOn: - point: "50%,9%" - waitToSettleTimeoutMs: 500 - retryTapIfNoChange: false From b97a877fce7535764c263e9ff50802f0cdaed21d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:04:11 +0200 Subject: [PATCH 301/334] ci: let the first ios push banner go before tapping the next --- .maestro/push/01-notification.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.maestro/push/01-notification.yaml b/.maestro/push/01-notification.yaml index ff42a409..7e5ee481 100644 --- a/.maestro/push/01-notification.yaml +++ b/.maestro/push/01-notification.yaml @@ -45,6 +45,10 @@ tags: visible: ".*${output.subject0}.*|.*New message.*" timeout: 30000 - takeScreenshot: push-01-banner +# Let it go, so the next tap can't hit this one. +- extendedWaitUntil: + notVisible: ".*${output.subject0}.*|.*New message.*" + timeout: 20000 # 2. A tap opens the message, app in the background. The banner shows for a # few seconds and finding it takes Maestro about as long; taps on the lock From 6103df86a571380522e95d82f5add686d9dc9b2c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 14:18:30 +0200 Subject: [PATCH 302/334] ci: wait longer for the sign-in link prompt after a sign-out on ios --- .maestro/subflows/confirm-link.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.maestro/subflows/confirm-link.yaml b/.maestro/subflows/confirm-link.yaml index 452fc8c0..5547676c 100644 --- a/.maestro/subflows/confirm-link.yaml +++ b/.maestro/subflows/confirm-link.yaml @@ -10,9 +10,10 @@ appId: org.bulwarkmail.mobile visible: "Open in .*Bulwark Mobile.*" commands: - tapOn: "Open" +# Right after a sign-out the app can take a while to get to the link. - extendedWaitUntil: visible: "Sign in with a link.|Open the sign-in page of.*" - timeout: 20000 + timeout: 60000 - takeScreenshot: link-confirm-prompt - tapOn: text: "Continue" From 81a0723abd68220198c94b1d814cc71677e30d5b Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:19:03 +0200 Subject: [PATCH 303/334] ci: report the first ios launch after install apart from the cold starts after it --- .github/workflows/e2e-ios.yml | 2 +- scripts/e2e/perf.mjs | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index a82ac8e3..3e92c3af 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -325,7 +325,7 @@ jobs: if [ -d "$RUNNER_TEMP/base/app/BulwarkMobile.app" ]; then APPS=(--app "base=$RUNNER_TEMP/base/app/BulwarkMobile.app" "${APPS[@]}") fi - node scripts/e2e/perf.mjs "${APPS[@]}" --rounds "${{ inputs.perf_rounds || 5 }}" --cold 3 \ + node scripts/e2e/perf.mjs "${APPS[@]}" --rounds "${{ inputs.perf_rounds || 5 }}" --cold 5 \ --flows "$(ls "$GITHUB_WORKSPACE"/.maestro/perf/[0-9]-*.yaml | paste -sd, -)" --out "$OUT" - name: Upload results diff --git a/scripts/e2e/perf.mjs b/scripts/e2e/perf.mjs index 78552d6f..b8733db0 100644 --- a/scripts/e2e/perf.mjs +++ b/scripts/e2e/perf.mjs @@ -11,7 +11,10 @@ // stays signed in), // 2. cold-starts it `--cold` times: terminate, launch, wait for // `inbox-rendered`; cold start = that mark's time minus the launch time -// taken on this machine (the simulator shares the host clock), +// taken on this machine (the simulator shares the host clock). The +// first launch after the install is much slower and swings a lot (iOS +// does its first-launch work for the new binary), so it is reported on +// its own as `first-start-*`; `cold-start-*` are the launches after it, // 3. runs the Maestro interaction flows once each (open messages, switch // tabs, open compose, scroll), which produce the other marks. Each flow // is its own run, so one that fails costs only its own metric. @@ -102,7 +105,7 @@ function waitForMark(name, timeoutMs) { // ── steps ─────────────────────────────────────────────────────────────────── const samples = []; // { label, metric, ms, round } -async function coldStart(label, round) { +async function coldStart(label, round, first) { try { simctl('terminate', UDID, BUNDLE); } catch { /* not running */ } await sleep(1500); const pending = waitForMark('inbox-rendered', 120_000); @@ -114,8 +117,9 @@ async function coldStart(label, round) { console.log(` ${label} cold start: no inbox-rendered mark within 120 s`); return; } - samples.push({ label, round, metric: 'cold-start-to-inbox', ms: mark.t - t0 }); - if (bundle) samples.push({ label, round, metric: 'cold-start-to-bundle', ms: bundle.t - t0 }); + const kind = first ? 'first-start' : 'cold-start'; + samples.push({ label, round, metric: `${kind}-to-inbox`, ms: mark.t - t0 }); + if (bundle) samples.push({ label, round, metric: `${kind}-to-bundle`, ms: bundle.t - t0 }); console.log(` ${label} cold start ${mark.t - t0} ms (bundle ${bundle ? bundle.t - t0 : '?'} ms, rows ${mark.rows})`); await sleep(3000); } @@ -185,7 +189,7 @@ if (final && process.env.GITHUB_STEP_SUMMARY) fs.appendFileSync(process.env.GITH currentLabel = app.label; try { simctl('terminate', UDID, BUNDLE); } catch { /* not running */ } simctl('install', UDID, app.path); - for (let i = 0; i < coldRuns; i++) await coldStart(app.label, round); + for (let i = 0; i < coldRuns; i++) await coldStart(app.label, round, i === 0); const got = await runFlows(app.label); for (const m of got) { const metric = m.n === 'tab-switch' && m.to ? `tab-switch:${m.to}` : m.n; From 96ffeaa0e22a67165d08fb5b833c2ab76a8feccd Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 17:11:21 +0200 Subject: [PATCH 304/334] ci: pick a photo in the ios compose flow, settle settings rows before tapping, answer prompts in portrait and tap undo sooner --- .maestro/features/02-read-messages.yaml | 4 +- .maestro/features/03-compose-send.yaml | 76 +++++++++++++--------- .maestro/features/06-swipe-undo.yaml | 5 +- .maestro/features/07-move-delete-tag.yaml | 23 +++++-- .maestro/features/09-calendar.yaml | 8 +-- .maestro/subflows/allow-notifications.yaml | 11 +++- .maestro/subflows/settings-page.yaml | 38 +++++++++++ .maestro/subflows/signed-in.yaml | 11 ++-- 8 files changed, 126 insertions(+), 50 deletions(-) diff --git a/.maestro/features/02-read-messages.yaml b/.maestro/features/02-read-messages.yaml index 2b3002cb..b7be998e 100644 --- a/.maestro/features/02-read-messages.yaml +++ b/.maestro/features/02-read-messages.yaml @@ -44,7 +44,9 @@ tags: visible: ".*q3-report.pdf.*" timeout: 20000 - takeScreenshot: read-04-attachment -- tapOn: ".*q3-report.pdf.*" +# The message's attachment chip (name and size); on the iPad the list row +# next to it shows the name too. +- tapOn: ".*q3-report.pdf.*[0-9]+ B.*" - extendedWaitUntil: visible: "Share" timeout: 30000 diff --git a/.maestro/features/03-compose-send.yaml b/.maestro/features/03-compose-send.yaml index 85a9ef89..0e4a8320 100644 --- a/.maestro/features/03-compose-send.yaml +++ b/.maestro/features/03-compose-send.yaml @@ -25,9 +25,7 @@ tags: - takeScreenshot: compose-01-filled # The attach sheet offers its options one by one (it used to be a single -# accessibility element). The system photo and file pickers run in another -# process that XCTest can't drive, so a picked attachment is covered by the -# forward in 04-incoming-reply-forward instead. +# accessibility element, and picking one opened no picker on iOS). - tapOn: "Attach" - extendedWaitUntil: visible: "Photos & Videos" @@ -35,41 +33,54 @@ tags: - assertVisible: "Camera" - assertVisible: "Files" - takeScreenshot: compose-02-attach-sheet -- tapOn: "Photos & Videos" -# The system photo picker (another process) opens over the composer. -# (Optional until we know what XCTest sees of it; the screenshot shows it.) -- extendedWaitUntil: - notVisible: "Draft saved|New Message" - timeout: 10000 - optional: true -- waitForAnimationToEnd: - timeout: 5000 -- takeScreenshot: compose-03-photo-picker -# Close it: its Cancel button if XCTest sees it, else swipe the sheet down. +- evalScript: ${output.pickPhoto = ORIENTATION != 'landscape'} +# The system photo picker runs in another process: XCTest sees none of its +# elements, but taps by position reach it. Pick the first photo (the app +# icon, added by boot-sim.sh) and confirm with the check mark. The iPad in +# landscape lays the picker out differently, so it only closes the sheet. - runFlow: when: - visible: "Cancel" + true: ${!output.pickPhoto} commands: - tapOn: "Cancel" - runFlow: when: - notVisible: "New Message" + true: ${output.pickPhoto} commands: - - swipe: - start: 50%,12% - end: 50%,95% - duration: 400 -- waitForAnimationToEnd: - timeout: 3000 -# A swipe too many closes the composer: keep it. -- runFlow: - when: - visible: "Save or discard draft?" - commands: - - tapOn: "Cancel" + - tapOn: "Photos & Videos" + - extendedWaitUntil: + notVisible: "Draft saved|New Message" + timeout: 10000 + optional: true + - waitForAnimationToEnd: + timeout: 5000 + - takeScreenshot: compose-03-photo-picker + - runFlow: + when: + true: ${DEVICE == 'tablet'} + commands: + - tapOn: + point: "44%,33%" + - waitForAnimationToEnd: + timeout: 2000 + - tapOn: + point: "90%,26%" + - runFlow: + when: + true: ${DEVICE != 'tablet'} + commands: + - tapOn: + point: "16%,25%" + - waitForAnimationToEnd: + timeout: 2000 + - tapOn: + point: "90%,13%" - extendedWaitUntil: visible: "New Message" - timeout: 10000 + timeout: 20000 +- waitForAnimationToEnd: + timeout: 5000 +- takeScreenshot: compose-04-with-photo - tapOn: "Send" - extendedWaitUntil: @@ -80,3 +91,10 @@ tags: file: ../subflows/assert-delivered.yaml env: SUBJECT: ${output.subject} +- runFlow: + when: + true: ${output.pickPhoto} + commands: + - assertTrue: + condition: ${output.helper.attachments.length >= 1} + label: "${output.subject} carries the picked photo" diff --git a/.maestro/features/06-swipe-undo.yaml b/.maestro/features/06-swipe-undo.yaml index b2d147dc..ff5159a8 100644 --- a/.maestro/features/06-swipe-undo.yaml +++ b/.maestro/features/06-swipe-undo.yaml @@ -20,7 +20,10 @@ tags: timeout: 10000 - takeScreenshot: swipe-01-archived - assertNotVisible: ".*Grüße aus München.*" -- tapOn: "UNDO" +- tapOn: + text: "UNDO" + # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. + waitToSettleTimeoutMs: 500 - extendedWaitUntil: visible: ".*Grüße aus München.*" timeout: 15000 diff --git a/.maestro/features/07-move-delete-tag.yaml b/.maestro/features/07-move-delete-tag.yaml index 666cd7ed..d55e23ac 100644 --- a/.maestro/features/07-move-delete-tag.yaml +++ b/.maestro/features/07-move-delete-tag.yaml @@ -21,12 +21,18 @@ tags: - extendedWaitUntil: visible: "Move to..." timeout: 10000 -- tapOn: "Projects" +- tapOn: + text: "Projects" + # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. + waitToSettleTimeoutMs: 500 - extendedWaitUntil: visible: "Moved to Projects" timeout: 15000 - takeScreenshot: actions-02-moved -- tapOn: "UNDO" +- tapOn: + text: "UNDO" + # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. + waitToSettleTimeoutMs: 500 - runFlow: ../subflows/close-message.yaml - extendedWaitUntil: visible: ".*Weekly digest.*" @@ -38,15 +44,18 @@ tags: env: SUBJECT: Weekly digest - takeScreenshot: actions-03a-before-delete -- tapOn: "Delete" -- waitForAnimationToEnd: - timeout: 3000 -- takeScreenshot: actions-03b-after-delete +- tapOn: + text: "Delete" + # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. + waitToSettleTimeoutMs: 500 - extendedWaitUntil: visible: "Email moved to Trash" timeout: 15000 - takeScreenshot: actions-03-deleted -- tapOn: "UNDO" +- tapOn: + text: "UNDO" + # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. + waitToSettleTimeoutMs: 500 - runFlow: ../subflows/close-message.yaml - extendedWaitUntil: visible: ".*Weekly digest.*" diff --git a/.maestro/features/09-calendar.yaml b/.maestro/features/09-calendar.yaml index 259e631a..a975fd1e 100644 --- a/.maestro/features/09-calendar.yaml +++ b/.maestro/features/09-calendar.yaml @@ -12,13 +12,7 @@ tags: - extendedWaitUntil: visible: ".*Would Like to Send You Notifications.*|Go to today" timeout: 30000 -- runFlow: - when: - visible: ".*Would Like to Send You Notifications.*" - commands: - - tapOn: - text: "Allow" - optional: true +- runFlow: ../subflows/allow-notifications.yaml - extendedWaitUntil: visible: "Go to today" timeout: 30000 diff --git a/.maestro/subflows/allow-notifications.yaml b/.maestro/subflows/allow-notifications.yaml index 914fe45f..154ee098 100644 --- a/.maestro/subflows/allow-notifications.yaml +++ b/.maestro/subflows/allow-notifications.yaml @@ -1,12 +1,21 @@ # The app asks for notification permission now and then (calendar # reminders, push). The system alert covers the whole app, so XCTest sees -# nothing else until it is answered: allow it when it is up. +# nothing else until it is answered: allow it when it is up. Taps on system +# alerts miss in landscape, so answer it in portrait. appId: org.bulwarkmail.mobile --- - runFlow: when: visible: ".*Would Like to Send You Notifications.*" commands: + - setOrientation: PORTRAIT + - waitForAnimationToEnd: + timeout: 3000 - tapOn: text: "Allow" optional: true + - runFlow: + when: + true: ${ORIENTATION == 'landscape'} + commands: + - setOrientation: LANDSCAPE_LEFT diff --git a/.maestro/subflows/settings-page.yaml b/.maestro/subflows/settings-page.yaml index 7b4c8800..fe46f5a9 100644 --- a/.maestro/subflows/settings-page.yaml +++ b/.maestro/subflows/settings-page.yaml @@ -4,6 +4,7 @@ # (Swipe coordinates have to be literals.) appId: org.bulwarkmail.mobile --- +- evalScript: ${output.swiped = false} - runFlow: when: true: ${DEVICE == 'tablet'} @@ -17,6 +18,17 @@ appId: org.bulwarkmail.mobile start: 18%,80% end: 18%,40% duration: 600 + - evalScript: ${output.swiped = true} + # The row counts as visible as soon as its top edge shows: one more + # short scroll brings all of it on screen, so the tap lands on it. + - runFlow: + when: + true: ${output.swiped} + commands: + - swipe: + start: 18%,70% + end: 18%,55% + duration: 600 - runFlow: when: true: ${DEVICE != 'tablet'} @@ -30,9 +42,35 @@ appId: org.bulwarkmail.mobile start: 50%,80% end: 50%,40% duration: 600 + - evalScript: ${output.swiped = true} + - runFlow: + when: + true: ${output.swiped} + commands: + - swipe: + start: 50%,70% + end: 50%,55% + duration: 600 +# A tap while the list still coasts from the swipe only stops it. +- waitForAnimationToEnd: + timeout: 5000 - tapOn: ${ROW} - waitForAnimationToEnd: timeout: 5000 +# On the phone the page replaces the list: make sure it opened before +# scrolling (else the swipes below scroll the list away). +- runFlow: + when: + true: ${DEVICE != 'tablet'} + commands: + - runFlow: + when: + notVisible: "Back" + commands: + - tapOn: ${ROW} + - extendedWaitUntil: + visible: "Back" + timeout: 10000 # PROOF can be further down the page: scroll the page (the right-hand pane # on the iPad) until it shows. - runFlow: diff --git a/.maestro/subflows/signed-in.yaml b/.maestro/subflows/signed-in.yaml index a56209c8..d0834467 100644 --- a/.maestro/subflows/signed-in.yaml +++ b/.maestro/subflows/signed-in.yaml @@ -7,15 +7,18 @@ appId: org.bulwarkmail.mobile notifications: allow photos: allow camera: allow +# Answer the notification prompt (if the app asks) before turning to +# landscape: rotated, taps on system alerts miss. +- setOrientation: PORTRAIT +- extendedWaitUntil: + visible: "Enter server details manually|Inbox|.*Would Like to Send You Notifications.*" + timeout: 120000 +- runFlow: allow-notifications.yaml - runFlow: when: true: ${ORIENTATION == 'landscape'} commands: - setOrientation: LANDSCAPE_LEFT -- extendedWaitUntil: - visible: "Enter server details manually|Inbox|.*Would Like to Send You Notifications.*" - timeout: 120000 -- runFlow: allow-notifications.yaml - extendedWaitUntil: visible: "Enter server details manually|Inbox" timeout: 30000 From 58e52421e9b30f44936dde273d6bb539a900c78d Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:16:33 +0200 Subject: [PATCH 305/334] ci: report the ios start-up timeline and cache load times in the perf leg --- scripts/e2e/perf.mjs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scripts/e2e/perf.mjs b/scripts/e2e/perf.mjs index b8733db0..94716bf4 100644 --- a/scripts/e2e/perf.mjs +++ b/scripts/e2e/perf.mjs @@ -120,6 +120,19 @@ async function coldStart(label, round, first) { const kind = first ? 'first-start' : 'cold-start'; samples.push({ label, round, metric: `${kind}-to-inbox`, ms: mark.t - t0 }); if (bundle) samples.push({ label, round, metric: `${kind}-to-bundle`, ms: bundle.t - t0 }); + // The start-up timeline: every mark between launch and the inbox, as time + // since launch, plus how long each persisted cache took to read and parse. + if (!first) { + for (const m of marks) { + if (m.t < t0 || m.t > mark.t || m.n === 'inbox-rendered' || m.n === 'bundle-loaded') continue; + const key = m.store ? `${m.n}:${m.store}` : m.n; + samples.push({ label, round, metric: `start:${key}`, ms: m.t - t0 }); + if (m.n === 'hydrate') { + samples.push({ label, round, metric: `hydrate-read:${m.store}`, ms: m.readMs }); + samples.push({ label, round, metric: `hydrate-parse:${m.store}`, ms: m.parseMs, kb: m.kb }); + } + } + } console.log(` ${label} cold start ${mark.t - t0} ms (bundle ${bundle ? bundle.t - t0 : '?'} ms, rows ${mark.rows})`); await sleep(3000); } From 239bf618b5f14844ee2b8658cd5cda446268f79e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:12:59 +0200 Subject: [PATCH 306/334] ci: search for ipad settings pages, tap the attachment chip by name and move to the body with return in the ios flows --- .maestro/features/02-read-messages.yaml | 6 +-- .maestro/features/03-compose-send.yaml | 1 + .maestro/features/08-scheduled-send.yaml | 1 + .maestro/features/12-settings.yaml | 44 ++++++++--------- .maestro/subflows/compose-body.yaml | 47 +++++++++++------- .maestro/subflows/settings-page.yaml | 61 ++++++++---------------- 6 files changed, 79 insertions(+), 81 deletions(-) diff --git a/.maestro/features/02-read-messages.yaml b/.maestro/features/02-read-messages.yaml index b7be998e..15e76c40 100644 --- a/.maestro/features/02-read-messages.yaml +++ b/.maestro/features/02-read-messages.yaml @@ -44,9 +44,9 @@ tags: visible: ".*q3-report.pdf.*" timeout: 20000 - takeScreenshot: read-04-attachment -# The message's attachment chip (name and size); on the iPad the list row -# next to it shows the name too. -- tapOn: ".*q3-report.pdf.*[0-9]+ B.*" +# The message's attachment chip, labelled with the file name alone (on the +# iPad the list row next to it mentions the name in its longer label). +- tapOn: "q3-report.pdf" - extendedWaitUntil: visible: "Share" timeout: 30000 diff --git a/.maestro/features/03-compose-send.yaml b/.maestro/features/03-compose-send.yaml index 0e4a8320..771f4f0e 100644 --- a/.maestro/features/03-compose-send.yaml +++ b/.maestro/features/03-compose-send.yaml @@ -21,6 +21,7 @@ tags: - runFlow: file: ../subflows/compose-body.yaml env: + FROM_SUBJECT: "yes" BODY: "Hello from the iOS simulator test." - takeScreenshot: compose-01-filled diff --git a/.maestro/features/08-scheduled-send.yaml b/.maestro/features/08-scheduled-send.yaml index 0f062ecd..b6eab573 100644 --- a/.maestro/features/08-scheduled-send.yaml +++ b/.maestro/features/08-scheduled-send.yaml @@ -21,6 +21,7 @@ tags: - runFlow: file: ../subflows/compose-body.yaml env: + FROM_SUBJECT: "yes" BODY: "Scheduled from the iOS simulator test." - tapOn: "Schedule send" - extendedWaitUntil: diff --git a/.maestro/features/12-settings.yaml b/.maestro/features/12-settings.yaml index 5af6250a..7d50cc4f 100644 --- a/.maestro/features/12-settings.yaml +++ b/.maestro/features/12-settings.yaml @@ -12,27 +12,27 @@ tags: timeout: 20000 - takeScreenshot: settings-00-list - runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: account, ROW: ".*@example.org, .*", PROOF: "Logged-in accounts|Display Name"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: language, ROW: "Language, Region & Time, .*", PROOF: "Date Format"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: notifications, ROW: "Notifications, .*", PROOF: "Email Notifications"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: appearance, ROW: "Appearance, .*", PROOF: "Choose your preferred color scheme"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: layout, ROW: "Layout, .*", PROOF: "Tune the email list interactions for mobile."}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: reading, ROW: "Reading, .*", PROOF: "Mark as Read"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: composing, ROW: "Composing, .*", PROOF: "Composer"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: identities, ROW: "Identities, .*", PROOF: "Sending Identities"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: vacation, ROW: "Out of Office, .*", PROOF: "Reply automatically"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: filters, ROW: "Filters, .*", PROOF: "Email Filters"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: templates, ROW: "Templates, .*", PROOF: "Email Templates"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: folders, ROW: "Folders, .*", PROOF: ".*Inbox.*"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: tags, ROW: "Tags, .*", PROOF: "Email Tags"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: downloads, ROW: "Downloads, .*", PROOF: "Email .*eml.* filename"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: security, ROW: "Security, .*", PROOF: "Account Security|Detecting server capabilities.*"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: content, ROW: "Content & Senders, .*", PROOF: "External Content"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", PROOF: "Default view"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", PROOF: "Group by first letter"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: files, ROW: "Files, .*", PROOF: "Default View"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: sidebar-apps, ROW: "Sidebar Apps, .*", PROOF: "Keep Apps Loaded"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: themes, ROW: "Themes, .*", PROOF: "The default light and dark palettes."}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: storage, ROW: "Storage & backup, .*", PROOF: "Offline mail.*"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: about, ROW: "About, .*", PROOF: "This device"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: language, ROW: "Language, Region & Time, .*", Q: "Language", PROOF: "Date Format"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: notifications, ROW: "Notifications, .*", Q: "Notifications", PROOF: "Email Notifications"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: appearance, ROW: "Appearance, .*", Q: "Appearance", PROOF: "Choose your preferred color scheme"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: layout, ROW: "Layout, .*", Q: "Layout", PROOF: "Tune the email list interactions for mobile."}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: reading, ROW: "Reading, .*", Q: "Reading", PROOF: "Mark as Read"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: composing, ROW: "Composing, .*", Q: "Composing", PROOF: "Composer"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: identities, ROW: "Identities, .*", Q: "Identities", PROOF: "Sending Identities"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: vacation, ROW: "Out of Office, .*", Q: "Office", PROOF: "Reply automatically"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: filters, ROW: "Filters, .*", Q: "Filters", PROOF: "Email Filters"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: templates, ROW: "Templates, .*", Q: "Templates", PROOF: "Email Templates"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: folders, ROW: "Folders, .*", Q: "Folders", PROOF: ".*Inbox.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: tags, ROW: "Tags, .*", Q: "Tags", PROOF: "Email Tags"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: downloads, ROW: "Downloads, .*", Q: "Downloads", PROOF: "Email .*eml.* filename"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: security, ROW: "Security, .*", Q: "Security", PROOF: "Account Security|Detecting server capabilities.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: content, ROW: "Content & Senders, .*", Q: "Content", PROOF: "External Content"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", Q: "Calendar", PROOF: "Default view"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", Q: "Contacts", PROOF: "Group by first letter"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: files, ROW: "Files, .*", Q: "Files", PROOF: "Default View"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: sidebar-apps, ROW: "Sidebar Apps, .*", Q: "Sidebar", PROOF: "Keep Apps Loaded"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: themes, ROW: "Themes, .*", Q: "Themes", PROOF: "The default light and dark palettes."}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: storage, ROW: "Storage & backup, .*", Q: "Storage", PROOF: "Offline mail.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: about, ROW: "About, .*", Q: "About", PROOF: "This device"}} - tapOn: id: tab-mail diff --git a/.maestro/subflows/compose-body.yaml b/.maestro/subflows/compose-body.yaml index 2ecac86b..a4968780 100644 --- a/.maestro/subflows/compose-body.yaml +++ b/.maestro/subflows/compose-body.yaml @@ -1,26 +1,41 @@ # In the composer: type BODY into the editor. The rich editor is a WebView # whose placeholder is CSS, so it may not be exposed; then tap the middle of # the screen, which is the body area below Subject (above the keyboard). +# FROM_SUBJECT=yes: the Subject field has the focus; Return there moves it +# to the body (an iPad in landscape shows hardly any body with the keyboard +# up, so there is nothing to tap). appId: org.bulwarkmail.mobile --- - runFlow: when: - visible: "Write your message.*" + true: ${FROM_SUBJECT == 'yes'} commands: - - tapOn: "Write your message.*" -# A tap on the WebView doesn't always take the focus from the Subject field -# (on the iPad the body once went into Subject); a second tap does. -# (hideKeyboard fails here: the composer's keyboard has no dismiss key.) -- runFlow: - when: - notVisible: "Write your message.*" - commands: - - tapOn: - point: "50%,50%" + - pressKey: Enter - waitForAnimationToEnd: timeout: 2000 - - tapOn: - point: "50%,50%" -- waitForAnimationToEnd: - timeout: 3000 -- inputText: ${BODY} + - inputText: ${BODY} +- runFlow: + when: + true: ${FROM_SUBJECT != 'yes'} + commands: + - runFlow: + when: + visible: "Write your message.*" + commands: + - tapOn: "Write your message.*" + # A tap on the WebView doesn't always take the focus from the Subject field + # (on the iPad the body once went into Subject); a second tap does. + # (hideKeyboard fails here: the composer's keyboard has no dismiss key.) + - runFlow: + when: + notVisible: "Write your message.*" + commands: + - tapOn: + point: "50%,50%" + - waitForAnimationToEnd: + timeout: 2000 + - tapOn: + point: "50%,50%" + - waitForAnimationToEnd: + timeout: 3000 + - inputText: ${BODY} diff --git a/.maestro/subflows/settings-page.yaml b/.maestro/subflows/settings-page.yaml index fe46f5a9..3f2e062b 100644 --- a/.maestro/subflows/settings-page.yaml +++ b/.maestro/subflows/settings-page.yaml @@ -1,56 +1,37 @@ -# Settings list: scroll to the row ROW (regex on "Title, summary"), open it, -# check PROOF shows, screenshot, and go back on a phone. On the iPad the list -# is a left column, so scroll there instead of the middle of the screen. -# (Swipe coordinates have to be literals.) +# Settings list: find the row ROW (regex on "Title, summary"), open it, +# check PROOF shows, screenshot, and go back on a phone. +# +# A row can count as visible while it is under the phone's tab bar or half +# off the iPad's screen, and the tap then misses. The phone scrolls it to the +# middle. The iPad's list is a left column that Maestro's scroll (always in +# the middle of the screen) doesn't reach, so it searches for the page (Q, +# a word of its title) instead, which puts the row near the top. appId: org.bulwarkmail.mobile --- -- evalScript: ${output.swiped = false} - runFlow: when: - true: ${DEVICE == 'tablet'} + true: ${DEVICE == 'tablet' && !!Q} commands: - repeat: while: notVisible: ${ROW} - times: 10 + times: 2 commands: - - swipe: - start: 18%,80% - end: 18%,40% - duration: 600 - - evalScript: ${output.swiped = true} - # The row counts as visible as soon as its top edge shows: one more - # short scroll brings all of it on screen, so the tap lands on it. - - runFlow: - when: - true: ${output.swiped} - commands: - - swipe: - start: 18%,70% - end: 18%,55% - duration: 600 + - tapOn: "Search settings" + - eraseText: 40 + - inputText: ${Q} + - waitForAnimationToEnd: + timeout: 3000 - runFlow: when: true: ${DEVICE != 'tablet'} commands: - - repeat: - while: - notVisible: ${ROW} - times: 10 - commands: - - swipe: - start: 50%,80% - end: 50%,40% - duration: 600 - - evalScript: ${output.swiped = true} - - runFlow: - when: - true: ${output.swiped} - commands: - - swipe: - start: 50%,70% - end: 50%,55% - duration: 600 + - scrollUntilVisible: + element: ${ROW} + direction: DOWN + centerElement: true + visibilityPercentage: 100 + timeout: 30000 # A tap while the list still coasts from the swipe only stops it. - waitForAnimationToEnd: timeout: 5000 From 99e4bd9614c05d8aa649aa1a7beaaedb2c5d2a1b Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:19:48 +0200 Subject: [PATCH 307/334] ci: let ios runs with different legs on one branch run side by side --- .github/workflows/e2e-ios.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 3e92c3af..5ce8249c 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -44,7 +44,8 @@ on: types: [published] concurrency: - group: e2e-ios-${{ github.ref }} + # Per branch and set of legs: a perf run doesn't cancel a feature run. + group: e2e-ios-${{ github.ref }}-${{ inputs.legs || 'all' }} cancel-in-progress: true env: From ad9db5cbffc7b0ff7e20177613ceb70f3ace2694 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 21:57:31 +0200 Subject: [PATCH 308/334] ci: tap undo at once, search for every settings page and scroll message rows clear of the ios tab bar --- .maestro/features/06-swipe-undo.yaml | 9 ++---- .maestro/features/07-move-delete-tag.yaml | 7 +---- .maestro/subflows/open-message.yaml | 29 ++++++++++++++++--- .maestro/subflows/settings-page.yaml | 34 ++++++++++------------- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/.maestro/features/06-swipe-undo.yaml b/.maestro/features/06-swipe-undo.yaml index ff5159a8..e19a97c2 100644 --- a/.maestro/features/06-swipe-undo.yaml +++ b/.maestro/features/06-swipe-undo.yaml @@ -15,14 +15,11 @@ tags: from: ".*Grüße aus München.*" direction: LEFT duration: 400 -- extendedWaitUntil: - visible: "Email archived" - timeout: 10000 -- takeScreenshot: swipe-01-archived -- assertNotVisible: ".*Grüße aus München.*" +# The undo bar shows for 5 s; when it goes, the Compose button slides into +# its place, and a late tap on UNDO opens the composer. So tap at once and +# check the rest afterwards. - tapOn: text: "UNDO" - # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. waitToSettleTimeoutMs: 500 - extendedWaitUntil: visible: ".*Grüße aus München.*" diff --git a/.maestro/features/07-move-delete-tag.yaml b/.maestro/features/07-move-delete-tag.yaml index d55e23ac..38dfc5d9 100644 --- a/.maestro/features/07-move-delete-tag.yaml +++ b/.maestro/features/07-move-delete-tag.yaml @@ -25,10 +25,8 @@ tags: text: "Projects" # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. waitToSettleTimeoutMs: 500 -- extendedWaitUntil: - visible: "Moved to Projects" - timeout: 15000 - takeScreenshot: actions-02-moved +# Tap UNDO at once (see 06-swipe-undo: a late tap opens the composer). - tapOn: text: "UNDO" # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. @@ -48,9 +46,6 @@ tags: text: "Delete" # The undo bar shows for 5 s: don't spend them waiting for the screen to settle. waitToSettleTimeoutMs: 500 -- extendedWaitUntil: - visible: "Email moved to Trash" - timeout: 15000 - takeScreenshot: actions-03-deleted - tapOn: text: "UNDO" diff --git a/.maestro/subflows/open-message.yaml b/.maestro/subflows/open-message.yaml index f87e9f0d..e61ad435 100644 --- a/.maestro/subflows/open-message.yaml +++ b/.maestro/subflows/open-message.yaml @@ -1,10 +1,31 @@ # From a message list: open the message whose row mentions SUBJECT. +# A row near the bottom can count as visible while the phone's tab bar +# covers it (the tap then opens another tab): on the phone, scroll it to the +# middle. The iPad's list is a column Maestro's scroll (in the middle of the +# screen) doesn't reach; its rows fit on screen. appId: org.bulwarkmail.mobile --- -- scrollUntilVisible: - element: ".*${SUBJECT}.*" - direction: DOWN - timeout: 30000 +- runFlow: + when: + true: ${DEVICE != 'tablet'} + commands: + - scrollUntilVisible: + element: ".*${SUBJECT}.*" + direction: DOWN + centerElement: true + visibilityPercentage: 100 + timeout: 30000 +- runFlow: + when: + true: ${DEVICE == 'tablet'} + commands: + - scrollUntilVisible: + element: ".*${SUBJECT}.*" + direction: DOWN + timeout: 30000 +# A tap while the list still moves lands on another row. +- waitForAnimationToEnd: + timeout: 5000 - tapOn: ".*${SUBJECT}.*" # The message toolbar's "more" button: "Reply All" shows on the phone, but # the iPad pane has a quick reply box instead for mail to one recipient. diff --git a/.maestro/subflows/settings-page.yaml b/.maestro/subflows/settings-page.yaml index 3f2e062b..68104a56 100644 --- a/.maestro/subflows/settings-page.yaml +++ b/.maestro/subflows/settings-page.yaml @@ -1,37 +1,33 @@ # Settings list: find the row ROW (regex on "Title, summary"), open it, # check PROOF shows, screenshot, and go back on a phone. # -# A row can count as visible while it is under the phone's tab bar or half -# off the iPad's screen, and the tap then misses. The phone scrolls it to the -# middle. The iPad's list is a left column that Maestro's scroll (always in -# the middle of the screen) doesn't reach, so it searches for the page (Q, -# a word of its title) instead, which puts the row near the top. +# Rows count as visible while the phone's tab bar or header covers them or +# while they hang half off the iPad's screen, and a tap then misses; the +# iPad's list is a left column Maestro's scroll (in the middle of the screen) +# doesn't reach. So search for the page (Q, a word of its title): the row +# then sits right under the search box. The account card (no Q) is always +# at the top. appId: org.bulwarkmail.mobile --- - runFlow: when: - true: ${DEVICE == 'tablet' && !!Q} + true: ${!!Q} commands: - - repeat: - while: + - tapOn: "Search settings" + - eraseText: 40 + - inputText: ${Q} + - waitForAnimationToEnd: + timeout: 3000 + # Now and then a typed character goes missing: type it again. + - runFlow: + when: notVisible: ${ROW} - times: 2 commands: - tapOn: "Search settings" - eraseText: 40 - inputText: ${Q} - waitForAnimationToEnd: timeout: 3000 -- runFlow: - when: - true: ${DEVICE != 'tablet'} - commands: - - scrollUntilVisible: - element: ${ROW} - direction: DOWN - centerElement: true - visibilityPercentage: 100 - timeout: 30000 # A tap while the list still coasts from the swipe only stops it. - waitForAnimationToEnd: timeout: 5000 From 32a41f42d3190458244e0ad2b29f9b00e043469c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 22:04:24 +0200 Subject: [PATCH 309/334] ci: report the slowest modules at ios start-up in the perf leg --- .github/workflows/e2e-ios.yml | 13 ++++++++++++- scripts/e2e/perf.mjs | 36 ++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 5ce8249c..1920d8ae 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -122,6 +122,11 @@ jobs: - name: Build for the simulator env: APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + # The bundle times each module's evaluation for the perf leg, and + # Metro writes which id is which module (metro.config.js; a branch + # without that support ignores both). + BULWARK_MODULE_TIMINGS: '1' + BULWARK_MODULE_MAP: ${{ runner.temp }}/module-map.json run: | WORKSPACE=$(ls -d ios/*.xcworkspace | head -1) SCHEME=$(basename "$WORKSPACE" .xcworkspace) @@ -156,7 +161,13 @@ jobs: codesign -d --entitlements - "$APP_PATH" 2>&1 | head -20 || true # Packed as a tar: artifacts drop the executable bit, which would # leave a reused app unable to launch. - tar -C build/Build/Products/Release-iphonesimulator -czf "$RUNNER_TEMP/simulator-app.tar.gz" BulwarkMobile.app + tar -C build/Build/Products/Release-iphonesimulator -cf "$RUNNER_TEMP/simulator-app.tar" BulwarkMobile.app + if [ -f "$RUNNER_TEMP/module-map.json" ]; then + tar -C "$RUNNER_TEMP" -rf "$RUNNER_TEMP/simulator-app.tar" module-map.json + else + echo "no module map (branch without module timings)" + fi + gzip -f "$RUNNER_TEMP/simulator-app.tar" ls -la "$RUNNER_TEMP/simulator-app.tar.gz" - name: Upload the simulator app diff --git a/scripts/e2e/perf.mjs b/scripts/e2e/perf.mjs index 94716bf4..cde9f676 100644 --- a/scripts/e2e/perf.mjs +++ b/scripts/e2e/perf.mjs @@ -104,12 +104,14 @@ function waitForMark(name, timeoutMs) { // ── steps ─────────────────────────────────────────────────────────────────── const samples = []; // { label, metric, ms, round } +const moduleRuns = {}; // label -> [{ total, top: { id: ms } }] (module-times marks) async function coldStart(label, round, first) { try { simctl('terminate', UDID, BUNDLE); } catch { /* not running */ } await sleep(1500); const pending = waitForMark('inbox-rendered', 120_000); const bundlePending = waitForMark('bundle-loaded', 120_000); + const modulesPending = waitForMark('module-times', 125_000); const t0 = Date.now(); simctl('launch', UDID, BUNDLE, '-perfMarks', 'YES'); const [mark, bundle] = await Promise.all([pending, bundlePending]); @@ -118,6 +120,17 @@ async function coldStart(label, round, first) { return; } const kind = first ? 'first-start' : 'cold-start'; + // Builds made with BULWARK_MODULE_TIMINGS log how long each module took + // to evaluate, right after the first inbox (src/lib/perf-marks.ts). + const modules = await Promise.race([modulesPending, sleep(3000).then(() => null)]); + if (modules && !first) { + const top = {}; + for (const pair of String(modules.top || '').split(',')) { + const [id, ms] = pair.split(':'); + if (id) top[id] = Number(ms); + } + (moduleRuns[label] ||= []).push({ total: modules.total, top }); + } samples.push({ label, round, metric: `${kind}-to-inbox`, ms: mark.t - t0 }); if (bundle) samples.push({ label, round, metric: `${kind}-to-bundle`, ms: bundle.t - t0 }); // The start-up timeline: every mark between launch and the inbox, as time @@ -185,7 +198,28 @@ const scroll = labels.map((l) => { if (!xs.length) return null; return `${l}: median ${median(xs.map((s) => s.long))} long frames of ${median(xs.map((s) => s.frames))}, worst ${median(xs.map((s) => s.worst))} ms`; }).filter(Boolean); -const md = [...lines, '', ...scroll.map((s) => `- list-scroll ${s}`), ''].join('\n'); +// The slowest modules at start-up per app, by median over the launches. +const moduleSections = []; +for (const app of apps) { + const runs = moduleRuns[app.label]; + if (!runs?.length) continue; + let names = {}; + try { names = JSON.parse(fs.readFileSync(path.join(path.dirname(app.path), 'module-map.json'), 'utf8')); } catch { /* no map */ } + const ids = new Set(runs.flatMap((r) => Object.keys(r.top))); + const rows = [...ids] + .map((id) => ({ id, ms: median(runs.map((r) => r.top[id] ?? 0)) })) + .sort((a, b) => b.ms - a.ms) + .slice(0, 30); + moduleSections.push( + `### Slowest modules at start-up: ${app.label} (own evaluation time, median of ${runs.length} launches; all modules ${median(runs.map((r) => r.total))} ms)`, + '', + '| module | ms |', + '|---|---|', + ...rows.map((r) => `| ${names[r.id] ?? `#${r.id}`} | ${r.ms} |`), + '', + ); +} +const md = [...lines, '', ...scroll.map((s) => `- list-scroll ${s}`), '', ...moduleSections].join('\n'); fs.writeFileSync(path.join(out, 'perf.json'), JSON.stringify({ apps, rounds, coldRuns, samples }, null, 1)); fs.writeFileSync(path.join(out, 'perf.md'), md); console.log(md); From 57ca1c8083a0412fcc799dbdb222d4a625afcfc9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 03:09:17 +0200 Subject: [PATCH 310/334] test: log timing marks for the performance harness when launched with -perfMarks --- App.tsx | 5 ++ index.ts | 4 + src/components/EmailBodyView.tsx | 3 + src/components/RichTextEditor.tsx | 2 + src/lib/__tests__/perf-marks.test.ts | 62 +++++++++++++++ src/lib/perf-marks.ts | 112 +++++++++++++++++++++++++++ src/screens/EmailListScreen.tsx | 20 ++++- 7 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 src/lib/__tests__/perf-marks.test.ts create mode 100644 src/lib/perf-marks.ts diff --git a/App.tsx b/App.tsx index 15a8134a..14571410 100644 --- a/App.tsx +++ b/App.tsx @@ -94,6 +94,7 @@ import { useOfflineCacheStore } from './src/stores/offline-cache-store'; import { useOutboxStore } from './src/stores/outbox-store'; import { runOfflineSync } from './src/lib/offline-sync'; import { spacing, typography, type ThemePalette } from './src/theme/tokens'; +import { perfEndAfterPaint, perfMarksEnabled, perfStart } from './src/lib/perf-marks'; import { useColors } from './src/theme/colors'; const Stack = createNativeStackNavigator(); @@ -261,6 +262,10 @@ function MainTabsNavigator({ navigation }: NativeStackScreenProps ({ + tabPress: () => perfStart('tab-switch'), + focus: () => perfEndAfterPaint('tab-switch', { to: route.name }), + }) : undefined} screenOptions={{ headerShown: false, // Tabs out of view stop re-rendering until they are shown again. diff --git a/index.ts b/index.ts index 965b95a4..5ad23c22 100644 --- a/index.ts +++ b/index.ts @@ -6,6 +6,10 @@ import { registerWidgetTaskHandler } from 'react-native-android-widget'; import { pushBackgroundTask } from './src/lib/push-background-task'; import { widgetTaskHandler } from './src/widgets/task-handler'; import { refreshWidgetsInBackground } from './src/widgets/sync'; +import { perfMark } from './src/lib/perf-marks'; + +// Every import above has run: the bundle is loaded (performance harness). +perfMark('bundle-loaded'); import { markUiStarted } from './src/widgets/ui-presence'; // Runs in a fresh headless JS runtime when BulwarkPushTaskService is started diff --git a/src/components/EmailBodyView.tsx b/src/components/EmailBodyView.tsx index 091f0088..addfc435 100644 --- a/src/components/EmailBodyView.tsx +++ b/src/components/EmailBodyView.tsx @@ -19,6 +19,7 @@ import { useContactsStore } from '../stores/contacts-store'; import { useLocaleStore } from '../stores/locale-store'; import { spacing, typography, type ThemePalette } from '../theme/tokens'; import { useColors, useResolvedTheme } from '../theme/colors'; +import { perfEndAfterPaint } from '../lib/perf-marks'; import type { RootStackParamList } from '../navigation/types'; type Nav = NativeStackNavigationProp; @@ -685,6 +686,8 @@ export default function EmailBodyView({ const onMessage = (e: WebViewMessageEvent) => { const data = e.nativeEvent.data; + // The body reported its size: it is laid out (performance harness). + perfEndAfterPaint('open-message'); // Swipe/zoom messages arrive as JSON; the height reporter posts a bare number. if (data.charCodeAt(0) === 123 /* '{' */) { try { diff --git a/src/components/RichTextEditor.tsx b/src/components/RichTextEditor.tsx index a730647b..4295ca25 100644 --- a/src/components/RichTextEditor.tsx +++ b/src/components/RichTextEditor.tsx @@ -3,6 +3,7 @@ import { StyleSheet, View, Platform } from 'react-native'; import { WebView, type WebViewMessageEvent } from 'react-native-webview'; import { useColors } from '../theme/colors'; import type { ThemePalette } from '../theme/tokens'; +import { perfEndAfterPaint } from '../lib/perf-marks'; import { buildEditorHtml, MIN_EDITOR_HEIGHT } from '../lib/editor-html'; export type RichTextCommand = @@ -202,6 +203,7 @@ const RichTextEditor = React.forwardRef(function Ri } if (!readyRef.current) { readyRef.current = true; + perfEndAfterPaint('compose-open'); const queued = queuedRef.current; queuedRef.current = []; for (const script of queued) webViewRef.current?.injectJavaScript(`${script}; true;`); diff --git a/src/lib/__tests__/perf-marks.test.ts b/src/lib/__tests__/perf-marks.test.ts new file mode 100644 index 00000000..fe3d7ef9 --- /dev/null +++ b/src/lib/__tests__/perf-marks.test.ts @@ -0,0 +1,62 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +// The module reads its switch once at load, so each case loads it fresh +// with its own react-native mock. +async function load(os: 'ios' | 'android', setting: unknown) { + vi.resetModules(); + vi.doMock('react-native', () => ({ + Platform: { OS: os }, + Settings: { get: vi.fn(() => setting) }, + })); + return import('../perf-marks'); +} + +describe('perf marks', () => { + let log: ReturnType; + beforeEach(() => { + log = vi.spyOn(console, 'log').mockImplementation(() => undefined); + }); + afterEach(() => { + log.mockRestore(); + vi.doUnmock('react-native'); + }); + + it('logs nothing without the launch argument', async () => { + const m = await load('ios', undefined); + m.perfMark('bundle-loaded'); + m.perfStart('open-message'); + m.perfEnd('open-message'); + m.perfOnce('inbox-rendered'); + expect(m.perfMarksEnabled()).toBe(false); + expect(log).not.toHaveBeenCalled(); + }); + + it('stays off on Android even when the setting is there', async () => { + const m = await load('android', 'YES'); + m.perfMark('bundle-loaded'); + expect(log).not.toHaveBeenCalled(); + }); + + it('logs marks and span durations with -perfMarks YES', async () => { + const m = await load('ios', 'YES'); + vi.useFakeTimers(); + vi.setSystemTime(1_000); + m.perfStart('open-message'); + vi.setSystemTime(1_250); + m.perfEnd('open-message', { id: 'x' }); + vi.useRealTimers(); + expect(log).toHaveBeenCalledTimes(1); + const line = String(log.mock.calls[0][0]); + expect(line.startsWith('[perf] ')).toBe(true); + expect(JSON.parse(line.slice(7))).toEqual({ n: 'open-message', t: 1_250, ms: 250, id: 'x' }); + }); + + it('logs a once-mark only once and ignores an end without a start', async () => { + const m = await load('ios', true); + m.perfOnce('inbox-rendered', { rows: 3 }); + m.perfOnce('inbox-rendered', { rows: 9 }); + m.perfEnd('never-started'); + expect(log).toHaveBeenCalledTimes(1); + expect(String(log.mock.calls[0][0])).toContain('"rows":3'); + }); +}); diff --git a/src/lib/perf-marks.ts b/src/lib/perf-marks.ts new file mode 100644 index 00000000..7bfd634e --- /dev/null +++ b/src/lib/perf-marks.ts @@ -0,0 +1,112 @@ +// Timing marks for the performance harness (scripts/e2e/perf.mjs). +// +// Off unless the app was launched with the `-perfMarks YES` launch argument +// (iOS puts launch arguments into NSUserDefaults, which React Native's +// Settings module reads). When on, every mark is one `[perf]` line on the +// console; on iOS that lands in the system log, where the harness reads it +// with `log show --info`. Nothing is stored or sent anywhere. + +import { Platform, Settings } from 'react-native'; + +function readEnabled(): boolean { + if (Platform.OS !== 'ios') return false; + try { + const value = Settings.get('perfMarks') as unknown; + return value === true || value === 'YES' || value === 'true' || value === 1 || value === '1'; + } catch { + return false; + } +} + +const enabled = readEnabled(); + +export function perfMarksEnabled(): boolean { + return enabled; +} + +/** Log a mark with the wall-clock time (the harness compares across processes). */ +export function perfMark(name: string, detail?: Record): void { + if (!enabled) return; + console.log(`[perf] ${JSON.stringify({ n: name, t: Date.now(), ...detail })}`); +} + +const started = new Map(); + +/** Start a timed span; `perfEnd` with the same name logs its duration. */ +export function perfStart(name: string): void { + if (!enabled) return; + started.set(name, Date.now()); +} + +export function perfEnd(name: string, detail?: Record): void { + if (!enabled) return; + const t0 = started.get(name); + if (t0 === undefined) return; + started.delete(name); + perfMark(name, { ms: Date.now() - t0, ...detail }); +} + +/** + * End a span once the next frames are drawn: two animation frames after the + * state change that rendered the result, so the time includes commit and + * paint, not just the JS render. + */ +export function perfEndAfterPaint(name: string, detail?: Record): void { + if (!enabled || !started.has(name)) return; + requestAnimationFrame(() => requestAnimationFrame(() => perfEnd(name, detail))); +} + +let onceDone = new Set(); +/** A mark that is logged once per process (first inbox render, ...). */ +export function perfOnce(name: string, detail?: Record): void { + if (!enabled || onceDone.has(name)) return; + onceDone.add(name); + perfMark(name, detail); +} + +/** Test hook. */ +export function resetPerfMarks(): void { + started.clear(); + onceDone = new Set(); +} + +/** + * Frame monitor for scrolling: counts animation frames between `start` and + * `stop` and how many of them took longer than two vsyncs (a visible hitch + * at 60 Hz). JS-thread frames, so it shows JS work blocking during a scroll. + */ +export function createFrameMonitor(name: string): { start: () => void; stop: () => void } { + let raf: number | null = null; + let last = 0; + let frames = 0; + let long = 0; + let worst = 0; + let t0 = 0; + const tick = (now: number) => { + if (last) { + const dt = now - last; + frames += 1; + if (dt > 34) long += 1; + if (dt > worst) worst = dt; + } + last = now; + raf = requestAnimationFrame(tick); + }; + return { + start() { + if (!enabled || raf !== null) return; + frames = 0; + long = 0; + worst = 0; + last = 0; + t0 = Date.now(); + raf = requestAnimationFrame(tick); + }, + stop() { + if (raf === null) return; + cancelAnimationFrame(raf); + raf = null; + perfMark(name, { ms: Date.now() - t0, frames, long, worst: Math.round(worst) }); + }, + }; +} diff --git a/src/screens/EmailListScreen.tsx b/src/screens/EmailListScreen.tsx index e472bf2b..c74f64b3 100644 --- a/src/screens/EmailListScreen.tsx +++ b/src/screens/EmailListScreen.tsx @@ -56,6 +56,7 @@ import { getFullEmail, emptyMailbox as apiEmptyMailbox } from '../api/email'; import type { RootStackParamList } from '../navigation/types'; import { usePendingMailSearch } from '../navigation/pending-mail-search'; import type { Attachment, Email } from '../api/types'; +import { createFrameMonitor, perfOnce, perfStart } from '../lib/perf-marks'; import { MODAL_ORIENTATIONS } from '../lib/modal-orientations'; function getSenderName(email: Email, unknownLabel: string): string { @@ -473,6 +474,13 @@ export default function EmailListScreen({ () => collapseThreads(emails, disableThreading), [emails, disableThreading], ); + // Performance harness (off unless launched with -perfMarks YES). + const scrollMonitor = React.useMemo(() => createFrameMonitor('list-scroll'), []); + React.useEffect(() => { + if (visibleEmails.length > 0) { + requestAnimationFrame(() => perfOnce('inbox-rendered', { rows: visibleEmails.length })); + } + }, [visibleEmails.length]); const threadGroups = React.useMemo( () => groupByThread(emails, disableThreading), [emails, disableThreading], @@ -645,7 +653,10 @@ export default function EmailListScreen({ const email = emailsRef.current.find((e) => rowKeyOf(e) === id); if (!email) return; if (isDraftEmail(email, currentRoleRef.current)) void openDraftRef.current(email); - else onEmailPressRef.current?.(email); + else { + perfStart('open-message'); + onEmailPressRef.current?.(email); + } } }, [toggleSelect]); @@ -1563,6 +1574,8 @@ export default function EmailListScreen({ contentContainerStyle={styles.listContent} onEndReached={handleEndReached} onEndReachedThreshold={0.3} + onScrollBeginDrag={scrollMonitor.start} + onMomentumScrollEnd={scrollMonitor.stop} refreshing={loading} onRefresh={handleRefresh} /> @@ -1570,7 +1583,10 @@ export default function EmailListScreen({ {/* Compose FAB - matches webmail mobile: PenSquare, h-14 w-14, rounded-full, shadow-lg */} { + perfStart('compose-open'); + onComposePress?.(); + }} style={({ pressed }) => [ styles.fab, // Lifted over the phone's undo bar, which used to cover it. From bdade8503deea70d83bcaa236f70a8f045994b66 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 04:28:39 +0200 Subject: [PATCH 311/334] test: mark how long each persisted cache takes to load --- src/lib/perf-marks.ts | 2 +- src/stores/persist-storage.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lib/perf-marks.ts b/src/lib/perf-marks.ts index 7bfd634e..3bcbeadd 100644 --- a/src/lib/perf-marks.ts +++ b/src/lib/perf-marks.ts @@ -9,8 +9,8 @@ import { Platform, Settings } from 'react-native'; function readEnabled(): boolean { - if (Platform.OS !== 'ios') return false; try { + if (Platform.OS !== 'ios') return false; const value = Settings.get('perfMarks') as unknown; return value === true || value === 'YES' || value === 'true' || value === 1 || value === '1'; } catch { diff --git a/src/stores/persist-storage.ts b/src/stores/persist-storage.ts index 3a956f7e..37386d0a 100644 --- a/src/stores/persist-storage.ts +++ b/src/stores/persist-storage.ts @@ -16,6 +16,7 @@ import { AppState } from 'react-native'; import AsyncStorage from '@react-native-async-storage/async-storage'; +import { perfMark } from '../lib/perf-marks'; import type { PersistStorage, StorageValue } from 'zustand/middleware'; /** How long a store's write waits to take in further changes. */ @@ -93,9 +94,12 @@ export function createPersistStorage( return { getItem: async (name) => { try { + const t0 = Date.now(); const raw = await AsyncStorage.getItem(name); if (raw === null) return null; + const t1 = Date.now(); const value = JSON.parse(raw) as StorageValue; + perfMark('hydrate', { store: name, readMs: t1 - t0, parseMs: Date.now() - t1, kb: Math.round(raw.length / 1024) }); slotFor(name).stored = raw; return value; } catch (err) { From f5f3637d01880d7b5c19535d80ef33b3c657b9ee Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:15:35 +0200 Subject: [PATCH 312/334] fix: keep full-screen modal headers below the status bar on iPhones with a notch --- src/components/PasteSignInLinkModal.tsx | 6 +- src/components/QrScanModal.tsx | 7 ++- src/components/SafeAreaModal.tsx | 19 ++++++ .../__tests__/modal-safe-area.test.ts | 60 +++++++++++++++++++ src/components/calendar/EventModal.tsx | 6 +- .../email/AttachmentPreviewModal.tsx | 7 ++- src/components/filters/FilterRuleModal.tsx | 7 ++- src/components/filters/SieveEditorSheet.tsx | 7 ++- 8 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 src/components/SafeAreaModal.tsx create mode 100644 src/components/__tests__/modal-safe-area.test.ts diff --git a/src/components/PasteSignInLinkModal.tsx b/src/components/PasteSignInLinkModal.tsx index 3f27698b..4c5cc66f 100644 --- a/src/components/PasteSignInLinkModal.tsx +++ b/src/components/PasteSignInLinkModal.tsx @@ -5,7 +5,6 @@ import { TextInput, Pressable, StyleSheet, - Modal, Platform, KeyboardAvoidingView, ScrollView, @@ -19,6 +18,7 @@ import Button from './Button'; import { useLocaleStore } from '../stores/locale-store'; import { insecurePairingLinkError, parsePastedSignInLink, type QrLoginPayload } from '../lib/oauth'; import { describeLoginError } from '../lib/login-errors'; +import { SafeAreaModal } from './SafeAreaModal'; import { MODAL_ORIENTATIONS } from '../lib/modal-orientations'; interface PasteSignInLinkModalProps { @@ -81,7 +81,7 @@ export function PasteSignInLinkModal({ visible, onClose, onSubmit }: PasteSignIn const title = t('login.mobile.paste_title', 'Paste sign-in link'); return ( - + @@ -145,7 +145,7 @@ export function PasteSignInLinkModal({ visible, onClose, onSubmit }: PasteSignIn - + ); } diff --git a/src/components/QrScanModal.tsx b/src/components/QrScanModal.tsx index c9a98a00..f058e710 100644 --- a/src/components/QrScanModal.tsx +++ b/src/components/QrScanModal.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { View, Text, Pressable, StyleSheet, Modal, Platform, Linking } from 'react-native'; +import { View, Text, Pressable, StyleSheet, Platform, Linking } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { CameraView, useCameraPermissions, type BarcodeScanningResult } from 'expo-camera'; import { X } from './icons'; @@ -7,6 +7,7 @@ import { spacing, radius, typography, type ThemePalette } from '../theme/tokens' import { useColors, useResolvedTheme } from '../theme/colors'; import Button from './Button'; import { useLocaleStore } from '../stores/locale-store'; +import { SafeAreaModal } from './SafeAreaModal'; import { MODAL_ORIENTATIONS } from '../lib/modal-orientations'; interface QrScanModalProps { @@ -43,7 +44,7 @@ export function QrScanModal({ visible, onClose, onScanned }: QrScanModalProps) { ); return ( - + {permission?.granted ? ( - + ); } diff --git a/src/components/SafeAreaModal.tsx b/src/components/SafeAreaModal.tsx new file mode 100644 index 00000000..adad46e0 --- /dev/null +++ b/src/components/SafeAreaModal.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Modal, type ModalProps } from 'react-native'; +import { SafeAreaProvider } from 'react-native-safe-area-context'; + +/** + * A Modal with its own safe-area provider. A Modal is a separate native + * window, and a SafeAreaView inside a slide-up Modal measured no insets: the + * view starts off screen when the slide begins, so on iPhones with a notch or + * Dynamic Island the header (and its close button) ended up under the status + * bar, where taps don't reach it. Use this for full-screen modals with a + * SafeAreaView header. + */ +export function SafeAreaModal({ children, ...props }: ModalProps) { + return ( + + {children} + + ); +} diff --git a/src/components/__tests__/modal-safe-area.test.ts b/src/components/__tests__/modal-safe-area.test.ts new file mode 100644 index 00000000..1eb8eda2 --- /dev/null +++ b/src/components/__tests__/modal-safe-area.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, it } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; + +// A SafeAreaView inside a slide-up react-native Modal measured no insets on +// iOS: the Modal is its own window and its content starts off screen, so on +// iPhones with a notch or Dynamic Island the header and its close button sat +// under the status bar, out of reach (attachment preview, QR scanner, paste +// sign-in link, filter and event editors). Such modals use SafeAreaModal, +// which gives the window its own SafeAreaProvider. + +const SRC = path.resolve(__dirname, '../..'); + +function sourceFiles(dir: string): string[] { + return fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => { + const p = path.join(dir, e.name); + if (e.isDirectory()) return e.name === '__tests__' ? [] : sourceFiles(p); + return e.name.endsWith('.tsx') ? [p] : []; + }); +} + +/** Opening tags of plain elements, attributes included. */ +function modalTags(source: string): string[] { + const tags: string[] = []; + let at = source.indexOf('= 0) { + if (/[\s>]/.test(source[at + 6] ?? '')) { + let depth = 0; + let i = at; + for (; i < source.length; i++) { + const ch = source[i]; + if (ch === '{') depth++; + else if (ch === '}') depth--; + else if (ch === '>' && depth === 0 && source[i - 1] !== '=') break; + } + tags.push(source.slice(at, i + 1)); + } + at = source.indexOf(' { + it('use SafeAreaModal so their header clears the status bar', () => { + const offenders = sourceFiles(SRC) + .filter((file) => { + const source = fs.readFileSync(file, 'utf8'); + if (!source.includes('SafeAreaView')) return false; + return modalTags(source).some((tag) => /animationType="slide"/.test(tag)); + }) + .map((file) => path.relative(SRC, file)); + expect(offenders).toEqual([]); + }); + + it('finds the tags it is looking for', () => { + const tags = modalTags(' close()} animationType="slide">\n'); + expect(tags).toHaveLength(1); + expect(tags[0]).toContain('animationType="slide"'); + }); +}); diff --git a/src/components/calendar/EventModal.tsx b/src/components/calendar/EventModal.tsx index 896a0d2a..c5027581 100644 --- a/src/components/calendar/EventModal.tsx +++ b/src/components/calendar/EventModal.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { - Modal, View, Text, ScrollView, @@ -69,6 +68,7 @@ import { ParticipantInput } from './ParticipantInput'; import { useContactNameResolver } from '../../lib/contact-name-resolver'; import { RecurrenceEditor } from './RecurrenceEditor'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; +import { SafeAreaModal } from '../SafeAreaModal'; import ToggleSwitch from '../ToggleSwitch'; type RecurrenceOption = 'none' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'custom'; @@ -419,7 +419,7 @@ export function EventModal({ ); return ( - )} - + ); } diff --git a/src/components/email/AttachmentPreviewModal.tsx b/src/components/email/AttachmentPreviewModal.tsx index 8948d40c..9ca523d7 100644 --- a/src/components/email/AttachmentPreviewModal.tsx +++ b/src/components/email/AttachmentPreviewModal.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, Modal, Pressable, ScrollView, Image, ActivityIndicator, Platform, PixelRatio, Alert, + View, Text, StyleSheet, Pressable, ScrollView, Image, ActivityIndicator, Platform, PixelRatio, Alert, useWindowDimensions, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -21,6 +21,7 @@ import { formatFullDateTime } from '../../lib/email-date'; import type { EmlPreview } from './use-body-override'; import { useFilePreview } from './use-file-preview'; import { ArchiveSummary, PreviewPartList, type PreviewPart } from './PreviewPartList'; +import { SafeAreaModal } from '../SafeAreaModal'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; export interface PreviewItem { @@ -432,9 +433,9 @@ export function AttachmentPreviewModal({ ); if (docked) return item || loading ? content : null; return ( - + {content} - + ); } diff --git a/src/components/filters/FilterRuleModal.tsx b/src/components/filters/FilterRuleModal.tsx index ffe96e30..616b5dd1 100644 --- a/src/components/filters/FilterRuleModal.tsx +++ b/src/components/filters/FilterRuleModal.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { - View, Text, StyleSheet, ScrollView, Pressable, Modal, Alert, + View, Text, StyleSheet, ScrollView, Pressable, Alert, KeyboardAvoidingView, Platform, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -37,6 +37,7 @@ import type { FilterComparator, FilterActionType, } from '../../lib/sieve/types'; +import { SafeAreaModal } from '../SafeAreaModal'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; const ALL_FIELDS: FilterConditionField[] = ['from', 'to', 'cc', 'subject', 'header', 'size', 'body', 'attachment']; @@ -227,7 +228,7 @@ export function FilterRuleModal({ visible, rule, mailboxes, onSave, onClose }: F }, [name, conditions, actions, matchType, stopProcessing, includeSpam, rule, onSave, t, mailboxTargets]); return ( - + - + ); } diff --git a/src/components/filters/SieveEditorSheet.tsx b/src/components/filters/SieveEditorSheet.tsx index 116610a3..a62e1445 100644 --- a/src/components/filters/SieveEditorSheet.tsx +++ b/src/components/filters/SieveEditorSheet.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useMemo, useState } from 'react'; import { - View, Text, StyleSheet, ScrollView, Pressable, Modal, TextInput, + View, Text, StyleSheet, ScrollView, Pressable, TextInput, KeyboardAvoidingView, Platform, ActivityIndicator, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -9,6 +9,7 @@ import { spacing, radius, typography, componentSizes, type ThemePalette } from ' import { useColors } from '../../theme/colors'; import Button from '../Button'; import { useLocaleStore } from '../../stores/locale-store'; +import { SafeAreaModal } from '../SafeAreaModal'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; interface SieveEditorSheetProps { @@ -63,7 +64,7 @@ export function SieveEditorSheet({ visible, content, onSave, onClose, onValidate }, [script, showSaveWarning, onSave]); return ( - + - + ); } From dde504f825fced92683254e46ed51b34b142a4a9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:18:41 +0200 Subject: [PATCH 313/334] fix: let screen readers reach each option in sheets and menus --- .../__tests__/sheet-accessibility.test.ts | 73 +++++++++++++++++++ src/components/calendar/CalendarEditSheet.tsx | 4 +- .../calendar/CalendarShareSheet.tsx | 4 +- src/components/files/ShareSheet.tsx | 4 +- .../settings/ContentSendersSettings.tsx | 4 +- src/components/settings/settings-section.tsx | 2 +- src/screens/ComposeScreen.tsx | 16 ++-- src/screens/EmailListScreen.tsx | 4 +- src/screens/FilesScreen.tsx | 16 ++-- src/screens/ScheduledScreen.tsx | 12 +-- 10 files changed, 106 insertions(+), 33 deletions(-) create mode 100644 src/components/__tests__/sheet-accessibility.test.ts diff --git a/src/components/__tests__/sheet-accessibility.test.ts b/src/components/__tests__/sheet-accessibility.test.ts new file mode 100644 index 00000000..9fecb08b --- /dev/null +++ b/src/components/__tests__/sheet-accessibility.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, it } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; + +// Sheets and menus were built as a tappable backdrop (Pressable or +// TouchableWithoutFeedback) wrapping a card that swallows taps. Both are +// accessible elements by default, and an accessible element hides its +// children from VoiceOver and TalkBack: the compose "Attach" and "Schedule +// send" sheets read as one element ("Attach, Photos & Videos, Camera, Files, +// Cancel") whose options couldn't be picked one by one. The wrappers that +// only catch taps are marked accessible={false}, so the options inside stay +// reachable. + +const SRC = path.resolve(__dirname, '../..'); + +function sourceFiles(dir: string): string[] { + return fs.readdirSync(dir, { withFileTypes: true }).flatMap((e) => { + const p = path.join(dir, e.name); + if (e.isDirectory()) return e.name === '__tests__' ? [] : sourceFiles(p); + return e.name.endsWith('.tsx') ? [p] : []; + }); +} + +function openingTags(source: string, name: string): { tag: string; line: number }[] { + const out: { tag: string; line: number }[] = []; + const re = new RegExp(`<${name}\\b`, 'g'); + let m: RegExpExecArray | null; + while ((m = re.exec(source))) { + let depth = 0; + let i = m.index; + for (; i < source.length; i++) { + const ch = source[i]; + if (ch === '{') depth++; + else if (ch === '}') depth--; + else if (ch === '>' && depth === 0 && source[i - 1] !== '=') break; + } + out.push({ tag: source.slice(m.index, i + 1), line: source.slice(0, m.index).split('\n').length }); + } + return out; +} + +/** A wrapper that only catches taps and still hides what it wraps. */ +export function isHidingWrapper(tag: string): boolean { + if (/accessible=\{false\}/.test(tag)) return false; + if (/\/\s*>$/.test(tag)) return false; // self-closing: wraps nothing + if (tag.startsWith(' \{\}\}/.test(tag)) return true; + return /style=\{styles\.\w*(Backdrop|backdrop|Overlay|overlay)\}/.test(tag); +} + +describe('sheet backdrops', () => { + it('do not hide the options they wrap from screen readers', () => { + const offenders: string[] = []; + for (const file of sourceFiles(SRC)) { + const source = fs.readFileSync(file, 'utf8'); + for (const name of ['Pressable', 'TouchableWithoutFeedback']) { + for (const { tag, line } of openingTags(source, name)) { + if (isHidingWrapper(tag)) offenders.push(`${path.relative(SRC, file)}:${line}`); + } + } + } + expect(offenders).toEqual([]); + }); + + it('recognises the wrappers it looks for', () => { + expect(isHidingWrapper('')).toBe(true); + expect(isHidingWrapper(' {}}>')).toBe(true); + expect(isHidingWrapper('')).toBe(true); + expect(isHidingWrapper('')).toBe(false); + expect(isHidingWrapper('')).toBe(false); + expect(isHidingWrapper('')).toBe(false); + }); +}); diff --git a/src/components/calendar/CalendarEditSheet.tsx b/src/components/calendar/CalendarEditSheet.tsx index 0944073f..420cfe8a 100644 --- a/src/components/calendar/CalendarEditSheet.tsx +++ b/src/components/calendar/CalendarEditSheet.tsx @@ -74,9 +74,9 @@ export function CalendarEditSheet({ visible, calendar, onSave, onClose }: Calend return ( - + - + {calendar diff --git a/src/components/calendar/CalendarShareSheet.tsx b/src/components/calendar/CalendarShareSheet.tsx index 9c745ab9..4e955b5f 100644 --- a/src/components/calendar/CalendarShareSheet.tsx +++ b/src/components/calendar/CalendarShareSheet.tsx @@ -160,9 +160,9 @@ export function CalendarShareSheet({ calendar, onShare, onClose }: CalendarShare return ( - + - + diff --git a/src/components/files/ShareSheet.tsx b/src/components/files/ShareSheet.tsx index c11d01e1..56a7b1b9 100644 --- a/src/components/files/ShareSheet.tsx +++ b/src/components/files/ShareSheet.tsx @@ -160,9 +160,9 @@ export default function ShareSheet({ node, onClose, onChanged }: ShareSheetProps return ( - + - + diff --git a/src/components/settings/ContentSendersSettings.tsx b/src/components/settings/ContentSendersSettings.tsx index e89d0745..94973a6a 100644 --- a/src/components/settings/ContentSendersSettings.tsx +++ b/src/components/settings/ContentSendersSettings.tsx @@ -196,8 +196,8 @@ export function ContentSendersSettings() { animationType="fade" onRequestClose={() => setModalOpen(false)} > - setModalOpen(false)}> - {}}> + setModalOpen(false)}> + {}}> {t('settings.email_behavior.trusted_senders.modal_title', 'Trusted Senders')} setOpen(false)} > - setOpen(false)}> + setOpen(false)}> {options.map((opt) => { diff --git a/src/screens/ComposeScreen.tsx b/src/screens/ComposeScreen.tsx index 51085d7c..acbf0809 100644 --- a/src/screens/ComposeScreen.tsx +++ b/src/screens/ComposeScreen.tsx @@ -377,8 +377,8 @@ function OptionsSheet({ const styles = React.useMemo(() => makeStyles(c), [c]); return ( - - {}}> + + {}}> {!!title && {title}} {options.map((opt) => ( setColorTarget(null)}> - setColorTarget(null)}> - {}}> + setColorTarget(null)}> + {}}> {colorTarget === 'background' ? t('email_composer.toolbar.background_color', 'Background color') @@ -2716,8 +2716,8 @@ export default function ComposeScreen({ route, navigation }: Props) { animationType="fade" onRequestClose={() => setScheduleSheetOpen(false)} > - setScheduleSheetOpen(false)}> - {}}> + setScheduleSheetOpen(false)}> + {}}> {t('email_composer.schedule_send', 'Schedule send')} @@ -2750,8 +2750,8 @@ export default function ComposeScreen({ route, navigation }: Props) { {customStage !== null && Platform.OS === 'ios' && ( setCustomStage(null)}> - setCustomStage(null)}> - {}}> + setCustomStage(null)}> + {}}> setFilterMenuOpen(false)} > - setFilterMenuOpen(false)}> + setFilterMenuOpen(false)}> - + {t('email_list.filter_title', 'Filter emails')} diff --git a/src/screens/FilesScreen.tsx b/src/screens/FilesScreen.tsx index a05cc7f2..e5af446a 100644 --- a/src/screens/FilesScreen.tsx +++ b/src/screens/FilesScreen.tsx @@ -1179,9 +1179,9 @@ function PromptModal(props: PromptModalProps) { animationType="fade" onRequestClose={props.onCancel} > - + - + {props.title} - + - + {target.displayName} @@ -1370,9 +1370,9 @@ function FolderPickerSheet({ target, allNodes, rootLabel, t, onClose, onPick }: const currentParent = target.parentId ?? null; return ( - + - + {t('files.move_to', 'Move to…')} · {target.displayName} @@ -1427,9 +1427,9 @@ function SortSheet({ visible, sortKey, sortDir, t, onClose, onChangeKey, onChang const keys: FilesSortKey[] = ['name', 'size', 'modified']; return ( - + - + {t('files.settings_default_sort', 'Sort')} {keys.map((k) => ( diff --git a/src/screens/ScheduledScreen.tsx b/src/screens/ScheduledScreen.tsx index 4522848e..fb2eece1 100644 --- a/src/screens/ScheduledScreen.tsx +++ b/src/screens/ScheduledScreen.tsx @@ -326,8 +326,8 @@ export default function ScheduledScreen({ navigation }: Props) { {/* Per-message actions */} setActionsFor(null)}> - setActionsFor(null)}> - {}}> + setActionsFor(null)}> + {}}> {actionsFor?.subject || t('email_composer.no_subject', '(No Subject)')} @@ -349,8 +349,8 @@ export default function ScheduledScreen({ navigation }: Props) { {/* Reschedule presets */} setRescheduleFor(null)}> - setRescheduleFor(null)}> - {}}> + setRescheduleFor(null)}> + {}}> {t('email_list.reschedule_send', 'Reschedule')} {reschedulePresets.map((preset) => ( setCustomStage(null)}> - setCustomStage(null)}> - {}}> + setCustomStage(null)}> + {}}> Date: Thu, 22 Oct 2026 08:19:50 +0200 Subject: [PATCH 314/334] test: add test ids to the contact form's name and email fields --- src/screens/ContactFormScreen.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/screens/ContactFormScreen.tsx b/src/screens/ContactFormScreen.tsx index bd2586b3..f5d1ea49 100644 --- a/src/screens/ContactFormScreen.tsx +++ b/src/screens/ContactFormScreen.tsx @@ -1254,6 +1254,7 @@ export default function ContactFormScreen() { style={styles.input} placeholder={t('contacts.form.given_name', 'First name')} ref={givenRef} + testID="contact-first-name" returnKeyType="next" blurOnSubmit={false} onSubmitEditing={() => middleRef.current?.focus()} @@ -1280,6 +1281,7 @@ export default function ContactFormScreen() { style={styles.input} placeholder={t('contacts.form.surname', 'Last name')} ref={surnameRef} + testID="contact-last-name" returnKeyType="next" blurOnSubmit={false} onSubmitEditing={() => fullRef.current?.focus()} @@ -1347,6 +1349,7 @@ export default function ContactFormScreen() { style={styles.input} placeholder={t('contacts.form.email_placeholder', 'email@example.com')} ref={i === 0 ? firstEmailRef : undefined} + testID={`contact-email-${i}`} placeholderTextColor={c.textMuted} keyboardType="email-address" autoCapitalize="none" From 42e89122bd6c913de306f5ba62c6299246e3a8a4 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 08:32:21 +0200 Subject: [PATCH 315/334] test: mark where the time goes when a message opens --- src/components/EmailBodyView.tsx | 6 +++++- src/lib/perf-marks.ts | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/components/EmailBodyView.tsx b/src/components/EmailBodyView.tsx index addfc435..bc2068f9 100644 --- a/src/components/EmailBodyView.tsx +++ b/src/components/EmailBodyView.tsx @@ -19,7 +19,7 @@ import { useContactsStore } from '../stores/contacts-store'; import { useLocaleStore } from '../stores/locale-store'; import { spacing, typography, type ThemePalette } from '../theme/tokens'; import { useColors, useResolvedTheme } from '../theme/colors'; -import { perfEndAfterPaint } from '../lib/perf-marks'; +import { perfEndAfterPaint, perfLap } from '../lib/perf-marks'; import type { RootStackParamList } from '../navigation/types'; type Nav = NativeStackNavigationProp; @@ -684,6 +684,9 @@ export default function EmailBodyView({ [prepared.isHtml, prepared.applyInversion, quoteLabels], ); + // Performance harness: how much of opening a message is the WebView. + React.useEffect(() => { perfLap('open-message', 'body-mounted'); }, []); + const onMessage = (e: WebViewMessageEvent) => { const data = e.nativeEvent.data; // The body reported its size: it is laid out (performance harness). @@ -791,6 +794,7 @@ export default function EmailBodyView({ injectedJavaScript={injectedJs} onMessage={onMessage} onLoadEnd={() => { + perfLap('open-message', 'webview-loaded'); // A (re)load resets the page's zoom state to 1 — drop any host-side // scroll locks so they can't go stale (e.g. cid images arriving // reload the source mid-zoom). diff --git a/src/lib/perf-marks.ts b/src/lib/perf-marks.ts index 3bcbeadd..6fb3cdc4 100644 --- a/src/lib/perf-marks.ts +++ b/src/lib/perf-marks.ts @@ -46,6 +46,14 @@ export function perfEnd(name: string, detail?: Record Date: Thu, 22 Oct 2026 11:26:07 +0200 Subject: [PATCH 316/334] fix: open the photo, camera and file pickers from the compose attach sheet on ios --- src/lib/__tests__/after-modal-dismiss.test.ts | 61 +++++++++++++++++++ src/lib/after-modal-dismiss.ts | 42 +++++++++++++ src/screens/ComposeScreen.tsx | 15 ++++- 3 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 src/lib/__tests__/after-modal-dismiss.test.ts create mode 100644 src/lib/after-modal-dismiss.ts diff --git a/src/lib/__tests__/after-modal-dismiss.test.ts b/src/lib/__tests__/after-modal-dismiss.test.ts new file mode 100644 index 00000000..4f356ded --- /dev/null +++ b/src/lib/__tests__/after-modal-dismiss.test.ts @@ -0,0 +1,61 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; +import { createAfterDismissQueue } from '../after-modal-dismiss'; + +afterEach(() => { + vi.useRealTimers(); +}); + +describe('createAfterDismissQueue', () => { + it('holds the action on iOS until the modal is dismissed', () => { + const queue = createAfterDismissQueue('ios'); + const action = vi.fn(); + queue.run(action); + expect(action).not.toHaveBeenCalled(); + queue.flush(); + expect(action).toHaveBeenCalledTimes(1); + queue.flush(); + expect(action).toHaveBeenCalledTimes(1); + }); + + it('runs the action after the fallback delay when onDismiss never comes', () => { + vi.useFakeTimers(); + const queue = createAfterDismissQueue('ios', 600); + const action = vi.fn(); + queue.run(action); + vi.advanceTimersByTime(599); + expect(action).not.toHaveBeenCalled(); + vi.advanceTimersByTime(1); + expect(action).toHaveBeenCalledTimes(1); + }); + + it('does not run the action twice when onDismiss and the timer both fire', () => { + vi.useFakeTimers(); + const queue = createAfterDismissQueue('ios', 600); + const action = vi.fn(); + queue.run(action); + queue.flush(); + vi.advanceTimersByTime(1000); + expect(action).toHaveBeenCalledTimes(1); + }); + + it('runs the action right away on Android', () => { + const queue = createAfterDismissQueue('android'); + const action = vi.fn(); + queue.run(action); + expect(action).toHaveBeenCalledTimes(1); + }); +}); + +// The compose "Attach" sheet closed its modal and opened the photo picker, +// camera or file picker in the same tick. On iOS the picker never showed. +describe('compose options sheet', () => { + const source = fs.readFileSync(path.resolve(__dirname, '../../screens/ComposeScreen.tsx'), 'utf8'); + + it('opens the chosen option only after the sheet is dismissed', () => { + expect(source).not.toMatch(/onClose\(\);\s*opt\.onPress\(\)/); + expect(source).toMatch(/onDismiss=\{afterDismiss\.flush\}/); + expect(source).toMatch(/afterDismiss\.run\(opt\.onPress\)/); + }); +}); diff --git a/src/lib/after-modal-dismiss.ts b/src/lib/after-modal-dismiss.ts new file mode 100644 index 00000000..e23ab895 --- /dev/null +++ b/src/lib/after-modal-dismiss.ts @@ -0,0 +1,42 @@ +// iOS can't present a view controller (the photo picker, the camera, the +// document picker, a share sheet) while a React Native Modal is still +// dismissing: UIKit drops the request without an error and the picker never +// shows. A sheet whose option opens a picker has to wait until it is gone. +// +// run() keeps the action until flush(), which the Modal's onDismiss calls. +// A timer flushes too, in case onDismiss doesn't fire. Android has no such +// limit, so the action runs right away there. + +export interface AfterDismissQueue { + run(action: () => void): void; + flush(): void; +} + +export function createAfterDismissQueue( + platform: string, + fallbackMs = 600, +): AfterDismissQueue { + let pending: (() => void) | null = null; + let timer: ReturnType | null = null; + + const flush = () => { + if (timer) clearTimeout(timer); + timer = null; + const action = pending; + pending = null; + action?.(); + }; + + return { + run(action) { + if (platform !== 'ios') { + action(); + return; + } + if (timer) clearTimeout(timer); + pending = action; + timer = setTimeout(flush, fallbackMs); + }, + flush, + }; +} diff --git a/src/screens/ComposeScreen.tsx b/src/screens/ComposeScreen.tsx index acbf0809..90c6e71b 100644 --- a/src/screens/ComposeScreen.tsx +++ b/src/screens/ComposeScreen.tsx @@ -32,6 +32,7 @@ import { getEmailInitials } from '../lib/avatar-utils'; import { useContactsStore, type RecipientSuggestion } from '../stores/contacts-store'; import { useKeyboardObstruction } from '../lib/use-keyboard-obstruction'; import { useLayout } from '../lib/use-layout'; +import { createAfterDismissQueue } from '../lib/after-modal-dismiss'; import { useLocaleStore } from '../stores/locale-store'; import { useSettingsStore } from '../stores/settings-store'; import { useHasContacts } from '../lib/capabilities'; @@ -375,8 +376,18 @@ function OptionsSheet({ }) { const c = useColors(); const styles = React.useMemo(() => makeStyles(c), [c]); + // An option may open a system picker, which iOS only shows once this + // modal is gone. + const afterDismiss = React.useRef(createAfterDismissQueue(Platform.OS)).current; return ( - + {}}> {!!title && {title}} @@ -384,7 +395,7 @@ function OptionsSheet({ { onClose(); opt.onPress(); }} + onPress={() => { onClose(); afterDismiss.run(opt.onPress); }} > {opt.label} From 721c2e34a15a948dfde945ad786db7c68b605bf5 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 11:50:19 +0200 Subject: [PATCH 317/334] fix: sign out of the last account instead of staying signed in --- src/stores/__tests__/auth-store.test.ts | 25 +++++++++++++++++++++++++ src/stores/auth-store.ts | 8 +++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/stores/__tests__/auth-store.test.ts b/src/stores/__tests__/auth-store.test.ts index 1027bc41..9e301a75 100644 --- a/src/stores/__tests__/auth-store.test.ts +++ b/src/stores/__tests__/auth-store.test.ts @@ -129,6 +129,31 @@ describe('auth-store', () => { expect(state.serverUrl).toBeNull(); expect(state.username).toBeNull(); }); + + // logout used to read the account list from a snapshot taken before it + // removed the account: the removed account still looked like one to + // switch to, the "switch" to the active account returned at once, and + // signing out of the only account left the app signed in. + it('signs out of the only account', async () => { + const id = 'me@mail.example.com'; + useAccountStore.setState({ + accounts: [{ + id, serverUrl: 'https://mail.example.com', username: 'me', + displayName: 'me', email: 'me', avatarColor: '#000', lastLoginAt: 0, + isConnected: true, hasError: false, isDefault: true, + }], + activeAccountId: id, + defaultAccountId: id, + }); + useAuthStore.setState({ isAuthenticated: true, activeAccountId: id, serverUrl: 'x', username: 'y' }); + + await useAuthStore.getState().logout(); + + expect(useAccountStore.getState().accounts).toEqual([]); + const state = useAuthStore.getState(); + expect(state.isAuthenticated).toBe(false); + expect(state.activeAccountId).toBeNull(); + }); }); describe('mail the viewer held in memory', () => { diff --git a/src/stores/auth-store.ts b/src/stores/auth-store.ts index 5900ea41..d9de9835 100644 --- a/src/stores/auth-store.ts +++ b/src/stores/auth-store.ts @@ -602,10 +602,12 @@ export const useAuthStore = create((set, get) => ({ jmapClient.reset(); clearAccountFeatureStores(currentId); - // Switch to next remaining account, if any - const remaining = accountStore.accounts; + // Switch to next remaining account, if any. Read the store again: + // `accountStore` is the state from before removeAccount. + const after = useAccountStore.getState(); + const remaining = after.accounts.filter((a) => a.id !== currentId); if (remaining.length > 0) { - const next = accountStore.getDefaultAccount() ?? remaining[0]; + const next = after.getDefaultAccount() ?? remaining[0]; try { await get().switchAccount(next.id); return; From 33eb8f99e5070793f2ed424bfe1f4e14ad1fd33c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 13:48:57 +0200 Subject: [PATCH 318/334] fix: show the undo button on the undo bar in dark mode --- src/components/UndoSnackbar.tsx | 8 ++-- .../__tests__/undo-snackbar-contrast.test.ts | 41 +++++++++++++++++++ src/components/undo-snackbar-colors.ts | 10 +++++ 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 src/components/__tests__/undo-snackbar-contrast.test.ts create mode 100644 src/components/undo-snackbar-colors.ts diff --git a/src/components/UndoSnackbar.tsx b/src/components/UndoSnackbar.tsx index 5f84e9cf..2b11fb81 100644 --- a/src/components/UndoSnackbar.tsx +++ b/src/components/UndoSnackbar.tsx @@ -6,6 +6,7 @@ import { useNavigation } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { spacing, radius, typography, type ThemePalette } from '../theme/tokens'; import { useColors } from '../theme/colors'; +import { undoSnackbarColors } from './undo-snackbar-colors'; import { useEmailStore } from '../stores/email-store'; import { useSendUndoStore, restoreUndoneSend, type PendingUndoSend } from '../stores/send-undo-store'; import { useLocaleStore } from '../stores/locale-store'; @@ -158,6 +159,7 @@ export function UndoSnackbar() { } function makeStyles(c: ThemePalette) { + const colors = undoSnackbarColors(c); return StyleSheet.create({ wrap: { position: 'absolute', @@ -170,7 +172,7 @@ function makeStyles(c: ThemePalette) { paddingLeft: spacing.lg, paddingRight: spacing.sm, borderRadius: radius.md, - backgroundColor: c.text, + backgroundColor: colors.bar, shadowColor: '#000', shadowOpacity: 0.3, shadowRadius: 12, @@ -179,7 +181,7 @@ function makeStyles(c: ThemePalette) { }, label: { ...typography.body, - color: c.background, + color: colors.label, flex: 1, }, undoBtn: { @@ -192,7 +194,7 @@ function makeStyles(c: ThemePalette) { }, undoText: { ...typography.bodySemibold, - color: c.primary, + color: colors.action, letterSpacing: 0.5, }, sendNowText: { diff --git a/src/components/__tests__/undo-snackbar-contrast.test.ts b/src/components/__tests__/undo-snackbar-contrast.test.ts new file mode 100644 index 00000000..3a357e64 --- /dev/null +++ b/src/components/__tests__/undo-snackbar-contrast.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest'; +import { DARK_COLORS, LIGHT_COLORS, type ThemePalette } from '../../theme/tokens'; +import { BUILTIN_THEMES } from '../../theme/builtin-themes'; +import { undoSnackbarColors } from '../undo-snackbar-colors'; + +// The undo bar (UndoSnackbar) is drawn in the text colour. Its UNDO button +// used the primary colour, which is white in the default dark theme: white +// on white, so in dark mode the bar showed "Email archived" and no visible +// way to undo. The bound is 3:1, not 4.5:1, because Solarized's own text +// and background are a little under 4.5:1. + +function luminance(hex: string): number { + const v = hex.replace('#', ''); + const full = v.length === 3 ? v.split('').map((ch) => ch + ch).join('') : v.slice(0, 6); + const [r, g, b] = [0, 2, 4].map((i) => { + const c = parseInt(full.slice(i, i + 2), 16) / 255; + return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4; + }); + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +} + +function contrast(a: string, b: string): number { + const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x); + return (hi + 0.05) / (lo + 0.05); +} + +const palettes: Array<[string, ThemePalette]> = [ + ['default light', LIGHT_COLORS], + ['default dark', DARK_COLORS], + ...BUILTIN_THEMES.flatMap((theme): Array<[string, ThemePalette]> => [ + [`${theme.name} light`, { ...LIGHT_COLORS, ...theme.light } as ThemePalette], + [`${theme.name} dark`, { ...DARK_COLORS, ...theme.dark } as ThemePalette], + ]), +]; + +describe('undo bar', () => { + it.each(palettes)('UNDO is readable on the bar (%s)', (_name, palette) => { + const { bar, action } = undoSnackbarColors(palette); + expect(contrast(action, bar)).toBeGreaterThanOrEqual(3); + }); +}); diff --git a/src/components/undo-snackbar-colors.ts b/src/components/undo-snackbar-colors.ts new file mode 100644 index 00000000..ed2c1221 --- /dev/null +++ b/src/components/undo-snackbar-colors.ts @@ -0,0 +1,10 @@ +import type { ThemePalette } from '../theme/tokens'; + +/** + * Colours of the undo bar. The bar is drawn in the text colour, so what is + * on it takes the background colour. The primary colour can't be used for + * UNDO: the default dark theme's primary is white, like the bar. + */ +export function undoSnackbarColors(c: ThemePalette): { bar: string; label: string; action: string } { + return { bar: c.text, label: c.background, action: c.background }; +} From 01092730376d84d2fb728970561aebacc7ebf730 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 17:08:41 +0200 Subject: [PATCH 319/334] fix: start typing in the message body when the empty space under it is tapped --- src/components/RichTextEditor.tsx | 5 ++ src/lib/__tests__/editor-focus-end.test.ts | 58 ++++++++++++++++++++++ src/lib/editor-html.ts | 30 +++++++++++ src/screens/ComposeScreen.tsx | 11 ++++ 4 files changed, 104 insertions(+) create mode 100644 src/lib/__tests__/editor-focus-end.test.ts diff --git a/src/components/RichTextEditor.tsx b/src/components/RichTextEditor.tsx index 4295ca25..3d1444ef 100644 --- a/src/components/RichTextEditor.tsx +++ b/src/components/RichTextEditor.tsx @@ -66,6 +66,8 @@ export interface RichTextEditorHandle { */ getHtml(timeoutMs?: number): Promise; focus(): void; + /** Focus with the caret where new text goes (before signature / quote). */ + focusEnd(): void; /** Drop the caret, which closes the keyboard. */ blur(): void; } @@ -186,6 +188,9 @@ const RichTextEditor = React.forwardRef(function Ri pendingSnapshots.current.set(id, { resolve, reject, timer }); call(`window.__rne && window.__rne.getHtml(${id})`); }), + focusEnd: () => { + call(`window.__rne && window.__rne.focusEnd()`); + }, focus: () => { call(`window.__rne && window.__rne.focus()`); }, diff --git a/src/lib/__tests__/editor-focus-end.test.ts b/src/lib/__tests__/editor-focus-end.test.ts new file mode 100644 index 00000000..28e3bd3d --- /dev/null +++ b/src/lib/__tests__/editor-focus-end.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from 'vitest'; +import { buildEditorHtml, FOCUS_END_SOURCE } from '../editor-html'; +import { LIGHT_COLORS } from '../../theme/tokens'; + +// On the iPad most of the composer's body area is empty space under the +// editor; a tap there did nothing and the keyboard kept typing into Subject. +// That space now calls the page's focusEnd(), which must put the caret where +// the user's own text goes: in front of the signature or the quoted message. + +interface FakeNode { name: string; previousElementSibling?: FakeNode | null } + +function run(stop: FakeNode | null) { + const calls: string[] = []; + const editor = { + name: 'editor', + focus: () => calls.push('focus'), + querySelector: () => stop, + }; + const range = { + selectNodeContents: (n: FakeNode) => calls.push(`select ${n.name}`), + setStartBefore: (n: FakeNode) => calls.push(`before ${n.name}`), + collapse: (toStart: boolean) => calls.push(toStart ? 'collapse start' : 'collapse end'), + }; + const selection = { + removeAllRanges: () => calls.push('clear'), + addRange: (r: unknown) => calls.push(r === range ? 'add range' : 'add other'), + }; + const fakeDocument = { createRange: () => range }; + const fakeWindow = { getSelection: () => selection }; + const focusEnd = new Function('document', 'window', `${FOCUS_END_SOURCE}; return focusEnd;`)(fakeDocument, fakeWindow); + focusEnd(editor); + return calls; +} + +describe('focusEnd', () => { + it('puts the caret at the end of the paragraph in front of the signature', () => { + const mine = { name: 'mine' }; + expect(run({ name: 'signature', previousElementSibling: mine })).toEqual([ + 'focus', 'select mine', 'collapse end', 'clear', 'add range', + ]); + }); + + it('puts the caret in front of a signature or quote that comes first', () => { + expect(run({ name: 'quote', previousElementSibling: null })).toEqual([ + 'focus', 'before quote', 'collapse start', 'clear', 'add range', + ]); + }); + + it('puts the caret at the end without signature or quote', () => { + expect(run(null)).toEqual(['focus', 'select editor', 'collapse end', 'clear', 'add range']); + }); + + it('is part of the page script and of its bridge', () => { + const html = buildEditorHtml({ initialHtml: '


', placeholder: 'Write…', c: LIGHT_COLORS }); + expect(html).toContain('function focusEnd(editor)'); + expect(html).toContain('focusEnd: function () { focusEnd(editor); }'); + }); +}); diff --git a/src/lib/editor-html.ts b/src/lib/editor-html.ts index 7dd7e30e..6db806ae 100644 --- a/src/lib/editor-html.ts +++ b/src/lib/editor-html.ts @@ -64,6 +64,33 @@ export function shouldBlockEditorRemoteImages(opts: { return !senderTrusted; } +/** + * Page-script function for a tap on the empty space under the editor: focus + * it with the caret where the user's own text goes, in front of the + * signature or the quoted message, else at the end. A string, embedded in + * the page script and run by the unit test against stand-in DOM objects. + */ +export const FOCUS_END_SOURCE = `function focusEnd(editor) { + editor.focus(); + var sel = window.getSelection(); + if (!sel) return; + var stop = editor.querySelector('[data-signature-block="start"], [data-signature-block="separator"], [data-quoted-html]'); + var range = document.createRange(); + var prev = stop ? stop.previousElementSibling : null; + if (prev) { + range.selectNodeContents(prev); + range.collapse(false); + } else if (stop) { + range.setStartBefore(stop); + range.collapse(true); + } else { + range.selectNodeContents(editor); + range.collapse(false); + } + sel.removeAllRanges(); + sel.addRange(range); + }`; + export function buildEditorHtml(opts: { initialHtml: string; placeholder: string; @@ -261,6 +288,8 @@ export function buildEditorHtml(opts: { }); // ── Bridge: receive commands from RN ─────────────────────────────── + ${FOCUS_END_SOURCE} + window.__rne = { exec: function (command, value) { editor.focus(); @@ -363,6 +392,7 @@ export function buildEditorHtml(opts: { post('htmlSnapshot', { id: id, html: editor.innerHTML }); }, focus: function () { editor.focus(); }, + focusEnd: function () { focusEnd(editor); }, }; reportHeight(); diff --git a/src/screens/ComposeScreen.tsx b/src/screens/ComposeScreen.tsx index 90c6e71b..10aa9a1b 100644 --- a/src/screens/ComposeScreen.tsx +++ b/src/screens/ComposeScreen.tsx @@ -2495,6 +2495,16 @@ export default function ComposeScreen({ route, navigation }: Props) { onViewportShift={onEditorViewportShift} /> )} + {/* The empty space under the body looks like part of it (on a + tablet, most of the panel): a tap there starts typing in it. */} + { + if (plainTextMode) plainBodyRef.current?.focus(); + else editorRef.current?.focusEnd(); + }} + />
{/* A phone in landscape with the keyboard up has room for a few lines @@ -2991,6 +3001,7 @@ function makeStyles(c: ThemePalette) { backgroundColor: c.background, }, scrollContent: { flexGrow: 1 }, + bodyFiller: { flexGrow: 1, minHeight: 48 }, header: { flexDirection: 'row', From da72415ef3551443a566b2e89f6da5e948e6e321 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 18:16:04 +0200 Subject: [PATCH 320/334] test: mark the steps of the start-up restore for the performance harness --- src/screens/EmailListScreen.tsx | 1 + src/stores/auth-store.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/screens/EmailListScreen.tsx b/src/screens/EmailListScreen.tsx index 1db48f44..29b5d964 100644 --- a/src/screens/EmailListScreen.tsx +++ b/src/screens/EmailListScreen.tsx @@ -476,6 +476,7 @@ export default function EmailListScreen({ ); // Performance harness (off unless launched with -perfMarks YES). const scrollMonitor = React.useMemo(() => createFrameMonitor('list-scroll'), []); + React.useEffect(() => { perfOnce('list-mounted'); }, []); React.useEffect(() => { if (visibleEmails.length > 0) { requestAnimationFrame(() => perfOnce('inbox-rendered', { rows: visibleEmails.length })); diff --git a/src/stores/auth-store.ts b/src/stores/auth-store.ts index d9de9835..15d60770 100644 --- a/src/stores/auth-store.ts +++ b/src/stores/auth-store.ts @@ -27,6 +27,7 @@ import { teardownPushNotificationsForAccount, } from '../lib/push-notifications'; import { deviceSyncSignedIn, releaseDeviceSyncBeforeSignOut } from '../device-sync/app/lifecycle'; +import { perfMark } from '../lib/perf-marks'; // Persist middleware hydrates asynchronously on cold start. Without this // guard, restoreSession() can read the account-store before AsyncStorage has @@ -763,6 +764,7 @@ export const useAuthStore = create((set, get) => ({ restoreSession: async () => { set({ isLoading: true }); + perfMark('restore-start'); try { // Wait for persisted caches to finish hydrating from AsyncStorage. // Otherwise we read empty defaults and bounce the user back to the @@ -775,6 +777,7 @@ export const useAuthStore = create((set, get) => ({ waitForHydration(useCalendarStore), waitForHydration(useContactsStore), ]); + perfMark('restore-hydrated'); const accountStore = useAccountStore.getState(); // Legacy migration: if there are no registered accounts but the old @@ -810,6 +813,7 @@ export const useAuthStore = create((set, get) => ({ try { const ok = await jmapClient.loadAccount(target.id); + perfMark('restore-session'); if (!ok) { // No stored credentials (or corrupt) — genuine logout. accountStore.removeAccount(target.id); @@ -863,6 +867,7 @@ export const useAuthStore = create((set, get) => ({ const session = jmapClient.currentSession!; applyConnectedState(set, session, target.serverUrl, target.username, target.id); + perfMark('restore-done'); // Refresh the cached mailbox list + current folder now that the session // is live. Feature stores show persisted data immediately; this swaps // in fresh data once the network round-trip completes. From 533f176e88c1bfe7176e10805cdaaaaefb6a884c Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 22:02:56 +0200 Subject: [PATCH 321/334] test: time each module's evaluation at start-up in builds made for the performance harness --- metro.config.js | 31 ++++++++++++++++++++++++ scripts/perf/module-timings.js | 30 +++++++++++++++++++++++ src/lib/__tests__/module-timings.test.ts | 31 ++++++++++++++++++++++++ src/lib/__tests__/perf-marks.test.ts | 16 ++++++++++++ src/lib/perf-marks.ts | 20 +++++++++++++++ src/screens/EmailListScreen.tsx | 7 ++++-- 6 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 scripts/perf/module-timings.js create mode 100644 src/lib/__tests__/module-timings.test.ts diff --git a/metro.config.js b/metro.config.js index 32938e89..733f21e8 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,5 +1,36 @@ +const path = require('path'); +const fs = require('fs'); const { getDefaultConfig } = require('expo/metro-config'); const config = getDefaultConfig(__dirname); +// Performance harness (the iOS simulator tests, .github/workflows/e2e-ios.yml): +// with BULWARK_MODULE_TIMINGS set, the bundle times each module's +// evaluation (scripts/perf/module-timings.js), and BULWARK_MODULE_MAP names +// a file that receives the module ids with their source paths, so the +// harness can tell which module is which. Release builds set neither. +if (process.env.BULWARK_MODULE_TIMINGS) { + const basePolyfills = config.serializer.getPolyfills; + config.serializer.getPolyfills = (ctx) => [ + ...basePolyfills(ctx), + path.join(__dirname, 'scripts/perf/module-timings.js'), + ]; + const mapFile = process.env.BULWARK_MODULE_MAP; + if (mapFile) { + const baseFactory = config.serializer.createModuleIdFactory; + config.serializer.createModuleIdFactory = () => { + const idOf = baseFactory(); + const names = {}; + process.on('exit', () => { + try { fs.writeFileSync(mapFile, JSON.stringify(names)); } catch { /* best effort */ } + }); + return (modulePath) => { + const id = idOf(modulePath); + names[id] = path.relative(__dirname, modulePath).split(path.sep).join('/'); + return id; + }; + }; + } +} + module.exports = config; diff --git a/scripts/perf/module-timings.js b/scripts/perf/module-timings.js new file mode 100644 index 00000000..3a233942 --- /dev/null +++ b/scripts/perf/module-timings.js @@ -0,0 +1,30 @@ +// Metro polyfill for the performance harness (metro.config.js adds it only +// when BULWARK_MODULE_TIMINGS is set at bundle time; release builds don't +// have it). It wraps every module factory and adds up how long each module's +// own code ran when it was first evaluated, without the modules it required +// meanwhile. src/lib/perf-marks.ts logs the slowest ones at first inbox. +(function (g) { + var define = g.__d; + if (typeof define !== 'function') return; + var now = typeof g.nativePerformanceNow === 'function' + ? g.nativePerformanceNow + : function () { return Date.now(); }; + var times = {}; + var stack = []; + g.__bulwarkModuleTimes = times; + g.__d = function (factory, moduleId, dependencyMap, verboseName) { + var timed = function () { + var start = now(); + stack.push(0); + try { + return factory.apply(this, arguments); + } finally { + var total = now() - start; + var nested = stack.pop(); + times[moduleId] = (times[moduleId] || 0) + (total - nested); + if (stack.length > 0) stack[stack.length - 1] += total; + } + }; + return define(timed, moduleId, dependencyMap, verboseName); + }; +})(typeof globalThis !== 'undefined' ? globalThis : this); diff --git a/src/lib/__tests__/module-timings.test.ts b/src/lib/__tests__/module-timings.test.ts new file mode 100644 index 00000000..1932f1f9 --- /dev/null +++ b/src/lib/__tests__/module-timings.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; + +// scripts/perf/module-timings.js wraps Metro's module definitions and adds +// up each module's own evaluation time, without the time of the modules it +// required while it ran. + +function install(clock: { t: number }) { + const g: Record = { nativePerformanceNow: () => clock.t }; + const factories = new Map void>(); + g.__d = (factory: () => void, id: number) => { factories.set(id, factory); }; + const src = fs.readFileSync(path.resolve(__dirname, '../../../scripts/perf/module-timings.js'), 'utf8'); + new Function('globalThis', src)(g); + (g.__d as (f: () => void, id: number) => void)(() => { clock.t += 5; }, 2); + (g.__d as (f: () => void, id: number) => void)(() => { + clock.t += 10; + factories.get(2)!(); + clock.t += 3; + }, 1); + return { run: (id: number) => factories.get(id)!(), times: () => g.__bulwarkModuleTimes as Record }; +} + +describe('module timings polyfill', () => { + it('counts a module without the modules it required', () => { + const clock = { t: 0 }; + const m = install(clock); + m.run(1); + expect(m.times()).toEqual({ 1: 13, 2: 5 }); + }); +}); diff --git a/src/lib/__tests__/perf-marks.test.ts b/src/lib/__tests__/perf-marks.test.ts index fe3d7ef9..ea2cd3c0 100644 --- a/src/lib/__tests__/perf-marks.test.ts +++ b/src/lib/__tests__/perf-marks.test.ts @@ -59,4 +59,20 @@ describe('perf marks', () => { expect(log).toHaveBeenCalledTimes(1); expect(String(log.mock.calls[0][0])).toContain('"rows":3'); }); + it('logs the slowest modules once, with the total', async () => { + const m = await load('ios', true); + (globalThis as { __bulwarkModuleTimes?: Record }).__bulwarkModuleTimes = { 7: 12.34, 3: 50, 9: 1 }; + try { + m.logModuleTimes(); + m.logModuleTimes(); + const lines = log.mock.calls.map((c: unknown[]) => String(c[0])).filter((l: string) => l.includes('module-times')); + expect(lines).toHaveLength(1); + const mark = JSON.parse(lines[0].slice('[perf] '.length)); + expect(mark.total).toBe(63); + expect(mark.count).toBe(3); + expect(mark.top).toBe('3:50,7:12.3,9:1'); + } finally { + delete (globalThis as { __bulwarkModuleTimes?: unknown }).__bulwarkModuleTimes; + } + }); }); diff --git a/src/lib/perf-marks.ts b/src/lib/perf-marks.ts index 6fb3cdc4..50ab3510 100644 --- a/src/lib/perf-marks.ts +++ b/src/lib/perf-marks.ts @@ -72,6 +72,26 @@ export function perfOnce(name: string, detail?: Record }).__bulwarkModuleTimes; + if (!times) return; + onceDone.add('module-times'); + const entries = Object.entries(times); + const total = entries.reduce((sum, [, ms]) => sum + ms, 0); + const top = entries + .sort((a, b) => b[1] - a[1]) + .slice(0, 60) + .map(([id, ms]) => `${id}:${Math.round(ms * 10) / 10}`) + .join(','); + perfMark('module-times', { total: Math.round(total), count: entries.length, top }); +} + /** Test hook. */ export function resetPerfMarks(): void { started.clear(); diff --git a/src/screens/EmailListScreen.tsx b/src/screens/EmailListScreen.tsx index 29b5d964..097eb83a 100644 --- a/src/screens/EmailListScreen.tsx +++ b/src/screens/EmailListScreen.tsx @@ -56,7 +56,7 @@ import { getFullEmail, emptyMailbox as apiEmptyMailbox } from '../api/email'; import type { RootStackParamList } from '../navigation/types'; import { usePendingMailSearch } from '../navigation/pending-mail-search'; import type { Attachment, Email } from '../api/types'; -import { createFrameMonitor, perfOnce, perfStart } from '../lib/perf-marks'; +import { createFrameMonitor, logModuleTimes, perfOnce, perfStart } from '../lib/perf-marks'; import { MODAL_ORIENTATIONS } from '../lib/modal-orientations'; function getSenderName(email: Email, unknownLabel: string): string { @@ -479,7 +479,10 @@ export default function EmailListScreen({ React.useEffect(() => { perfOnce('list-mounted'); }, []); React.useEffect(() => { if (visibleEmails.length > 0) { - requestAnimationFrame(() => perfOnce('inbox-rendered', { rows: visibleEmails.length })); + requestAnimationFrame(() => { + perfOnce('inbox-rendered', { rows: visibleEmails.length }); + logModuleTimes(); + }); } }, [visibleEmails.length]); const threadGroups = React.useMemo( From 2618680b21c5d41f2fb7ee4ce2de5abc9cb9c7e9 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:00:13 +0200 Subject: [PATCH 322/334] feat: keep iOS credentials where a notification extension can read them --- .../__tests__/jmap-client-hardening.test.ts | 34 +++++ src/api/jmap-client.ts | 67 +++++++--- src/lib/ios-push-shared.ts | 118 ++++++++++++++++++ 3 files changed, 199 insertions(+), 20 deletions(-) create mode 100644 src/lib/ios-push-shared.ts diff --git a/src/api/__tests__/jmap-client-hardening.test.ts b/src/api/__tests__/jmap-client-hardening.test.ts index e6b16cac..3133c072 100644 --- a/src/api/__tests__/jmap-client-hardening.test.ts +++ b/src/api/__tests__/jmap-client-hardening.test.ts @@ -418,6 +418,40 @@ describe('JMAPClient hardening', () => { expect(client.authHeader).toBe('Bearer new'); }); + // On iOS the notification extension refreshes OAuth tokens too and writes + // the new bundle back. Refreshing again with the refresh token in memory + // would spend a token the server has already replaced. + it('retries a 401 with a newer stored token instead of refreshing again', async () => { + const SecureStore = await import('expo-secure-store'); + const client = new JMAPClient(); + mockFetch([{ status: 200, json: SESSION }]); + await client.connectWithOAuth('https://mail.example.com', { + accessToken: 'old', + refreshToken: 'r1', + expiresAt: Date.now() + 3_600_000, + tokenEndpoint: 'https://mail.example.com/auth/token', + clientId: 'c', + }); + (SecureStore.getItemAsync as ReturnType).mockResolvedValue(JSON.stringify({ + serverUrl: 'https://mail.example.com', + username: 'user@example.com', + password: '', + accessToken: 'from-extension', + refreshToken: 'r2', + expiresAt: Date.now() + 7_200_000, + tokenEndpoint: 'https://mail.example.com/auth/token', + clientId: 'c', + })); + const fetch = mockFetch([ + { status: 401 }, + { status: 200, json: { methodResponses: [['Mailbox/get', {}, '0']] } }, + ]); + await client.request([['Mailbox/get', {}, '0']]); + expect(mockRefresh).not.toHaveBeenCalled(); + expect((fetch.mock.calls[1][1] as RequestInit).headers).toMatchObject({ Authorization: 'Bearer from-extension' }); + (SecureStore.getItemAsync as ReturnType).mockReset(); + }); + it('updatePassword rewrites the stored credential', async () => { const SecureStore = await import('expo-secure-store'); const client = await connected(); diff --git a/src/api/jmap-client.ts b/src/api/jmap-client.ts index 04c3ddc3..7752e3e4 100644 --- a/src/api/jmap-client.ts +++ b/src/api/jmap-client.ts @@ -15,6 +15,7 @@ import { type OAuthTokenSource, } from '../lib/oauth'; import { FirstTouchGate } from './first-touch-gate'; +import { writeSharedSecret, moveToSharedGroup } from '../lib/ios-push-shared'; import { beginOwnWrite, recordOwnEmailWrites } from './own-writes'; // Refresh OAuth access tokens this many ms before they actually expire so @@ -40,7 +41,7 @@ const LEGACY_CREDENTIALS_KEY = 'jmap_credentials'; const CREDENTIALS_PREFIX = 'jmap_credentials__'; // SecureStore keys: letters, digits, ".", "-", "_" only - no "@" or "/". -function credentialsKey(accountId: string): string { +export function credentialsKey(accountId: string): string { return CREDENTIALS_PREFIX + accountId.replace(/[^a-zA-Z0-9._-]/g, '_'); } @@ -260,10 +261,7 @@ export class JMAPClient { this.firstTouchGate.reset(); const accountId = generateAccountId(username, baseUrl); - await SecureStore.setItemAsync( - credentialsKey(accountId), - JSON.stringify(this.credentials), - ); + await writeSharedSecret(credentialsKey(accountId), JSON.stringify(this.credentials)); return this.session; } @@ -342,10 +340,7 @@ export class JMAPClient { this.credentials.username = username; const accountId = generateAccountId(username, baseUrl); - await SecureStore.setItemAsync( - credentialsKey(accountId), - JSON.stringify(this.credentials), - ); + await writeSharedSecret(credentialsKey(accountId), JSON.stringify(this.credentials)); return { session: this.session, username, accountId }; } @@ -367,15 +362,36 @@ export class JMAPClient { this.credentials.username, this.credentials.serverUrl, ); - await SecureStore.setItemAsync( - credentialsKey(accountId), - JSON.stringify(this.credentials), - ); + await writeSharedSecret(credentialsKey(accountId), JSON.stringify(this.credentials)); for (const l of this.tokenRefreshListeners) { try { l(); } catch { /* ignore */ } } } + /** + * Take the stored OAuth tokens when they are newer than the ones in memory: + * on iOS the notification extension refreshes them too and writes the new + * bundle back. True when the in-memory tokens changed. + */ + private async adoptStoredTokens(): Promise { + const current = this.credentials; + if (!current?.accessToken || !current.refreshToken) return false; + const accountId = generateAccountId(current.username, current.serverUrl); + const stored = await this.getStoredCredentials(accountId).catch(() => null); + if (!stored?.accessToken || stored.accessToken === current.accessToken) return false; + if ((stored.expiresAt ?? 0) <= (current.expiresAt ?? 0)) return false; + this.credentials = { + ...current, + accessToken: stored.accessToken, + refreshToken: stored.refreshToken ?? current.refreshToken, + expiresAt: stored.expiresAt, + }; + for (const l of this.tokenRefreshListeners) { + try { l(); } catch { /* ignore */ } + } + return true; + } + private currentOAuthTokens(): OAuthTokens | null { if ( !this.credentials?.accessToken || @@ -414,10 +430,17 @@ export class JMAPClient { // consumers of `authHeader` (SSE stream, file downloads) can make sure the // header they capture is not about to expire. async ensureFreshToken(): Promise { - const tokens = this.currentOAuthTokens(); + let tokens = this.currentOAuthTokens(); if (!tokens) return; if (tokens.expiresAt == null) return; if (tokens.expiresAt - Date.now() > TOKEN_REFRESH_LEEWAY_MS) return; + // Another process may have refreshed already (the iOS notification + // extension); use its tokens rather than spend a refresh token the + // server has replaced. + if (await this.adoptStoredTokens()) { + tokens = this.currentOAuthTokens(); + if (!tokens || (tokens.expiresAt != null && tokens.expiresAt - Date.now() > TOKEN_REFRESH_LEEWAY_MS)) return; + } try { const next = await refreshOAuthAccessToken(tokens); await this.persistRefreshedTokens(next); @@ -433,6 +456,10 @@ export class JMAPClient { // the account instead of evicting it (webmail 1.7.6 "keep the session when // the auth server is briefly unreachable"). async forceRefreshToken(): Promise { + if (!this.currentOAuthTokens()) return false; + // A newer token in storage (refreshed by the iOS notification extension) + // is worth a retry before refreshing again. + if (await this.adoptStoredTokens()) return true; const tokens = this.currentOAuthTokens(); if (!tokens) return false; try { @@ -459,10 +486,7 @@ export class JMAPClient { this.credentials.username, this.credentials.serverUrl, ); - await SecureStore.setItemAsync( - credentialsKey(accountId), - JSON.stringify(this.credentials), - ); + await writeSharedSecret(credentialsKey(accountId), JSON.stringify(this.credentials)); } // Legacy single-slot restore - kept for backward-compat tests. New code @@ -490,6 +514,9 @@ export class JMAPClient { async loadAccount(accountId: string): Promise { const stored = await SecureStore.getItemAsync(credentialsKey(accountId)); if (!stored) return false; + // iOS: credentials saved before the notification extension existed move + // to the keychain group it can read (a no-op elsewhere and afterwards). + void moveToSharedGroup(credentialsKey(accountId)).catch(() => undefined); let creds: StoredCredentials; try { @@ -566,7 +593,7 @@ export class JMAPClient { const creds: StoredCredentials = JSON.parse(stored); // Re-save under the per-account key before dropping the legacy entry const accountId = generateAccountId(creds.username, creds.serverUrl); - await SecureStore.setItemAsync(credentialsKey(accountId), stored); + await writeSharedSecret(credentialsKey(accountId), stored); await SecureStore.deleteItemAsync(LEGACY_CREDENTIALS_KEY); return creds; } catch { @@ -1159,7 +1186,7 @@ export class JMAPClient { } async setStoredCredentials(accountId: string, creds: StoredCredentials): Promise { - await SecureStore.setItemAsync(credentialsKey(accountId), JSON.stringify(creds)); + await writeSharedSecret(credentialsKey(accountId), JSON.stringify(creds)); } // ── Blob Download ───────────────────────────────────── diff --git a/src/lib/ios-push-shared.ts b/src/lib/ios-push-shared.ts new file mode 100644 index 00000000..7e476e4d --- /dev/null +++ b/src/lib/ios-push-shared.ts @@ -0,0 +1,118 @@ +// What the app shares with the iOS notification service extension +// (plugins/ios-push): account credentials, where to reach each pushed account, +// and the badge counts. They live in a keychain access group both targets +// belong to; nothing else of the app is visible to the extension. +// +// Readable after the first unlock: a push usually arrives while the phone is +// locked, and a notification that has to wait for the user to unlock would +// show only "New message". + +import * as SecureStore from 'expo-secure-store'; +import { Platform } from 'react-native'; + +type Extra = { iosSharedKeychainGroup?: unknown; iosAppKeychainGroup?: unknown; apnsEnvironment?: unknown }; + +// Loaded on first use, on iOS only: the module pulls in native code that +// Android's headless tasks and the unit tests can do without. +function extra(): Extra { + const { default: Constants } = require('expo-constants') as typeof import('expo-constants'); + return (Constants.expoConfig?.extra ?? {}) as Extra; +} + +/** The APNs host this build's device tokens belong to. */ +export function apnsEnvironment(): 'production' | 'sandbox' { + return extra().apnsEnvironment === 'sandbox' ? 'sandbox' : 'production'; +} + +/** The access group shared with the extension, or null off iOS. */ +export function sharedKeychainGroup(): string | null { + if (Platform.OS !== 'ios') return null; + const group = extra().iosSharedKeychainGroup; + return typeof group === 'string' && group ? group : null; +} + +/** Options for an item the extension must read. Undefined off iOS. */ +export function sharedStoreOptions(): SecureStore.SecureStoreOptions | undefined { + const group = sharedKeychainGroup(); + if (!group) return undefined; + return { accessGroup: group, keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK }; +} + +/** Options naming the app's own access group, where items used to live. */ +export function appStoreOptions(): SecureStore.SecureStoreOptions | undefined { + if (Platform.OS !== 'ios') return undefined; + const group = extra().iosAppKeychainGroup; + return typeof group === 'string' && group ? { accessGroup: group } : undefined; +} + +// SecureStore keys: letters, digits, ".", "-", "_" only. The extension +// builds the same key from the push's account id (NotificationService.swift). +function keyPart(id: string): string { + return id.replace(/[^a-zA-Z0-9._-]/g, '_'); +} + +export function pushAccountKey(jmapAccountId: string): string { + return `bulwark_push_account__${keyPart(jmapAccountId)}`; +} + +export const PUSH_SETTINGS_KEY = 'bulwark_push_settings'; + +export interface IosPushAccount { + /** SecureStore key of the account's credentials. */ + credentialsKey: string; + /** The JMAP API URL as the app uses it (rewritten to the server's host). */ + apiUrl: string; +} + +export async function writeIosPushAccount(jmapAccountId: string, account: IosPushAccount): Promise { + const options = sharedStoreOptions(); + if (!options) return; + await SecureStore.setItemAsync(pushAccountKey(jmapAccountId), JSON.stringify(account), options); +} + +export async function removeIosPushAccount(jmapAccountId: string): Promise { + const options = sharedStoreOptions(); + if (!options) return; + await SecureStore.deleteItemAsync(pushAccountKey(jmapAccountId), options).catch(() => undefined); +} + +export interface IosPushSettings { + /** Show the unread count on the app icon. */ + badge: boolean; + /** JMAP id of the active account: the badge is its inbox unread count. */ + activeAccountId: string | null; + /** The few texts the extension writes itself, in the app's language. */ + strings: { noSubject: string; more: string }; +} + +export async function writeIosPushSettings(settings: IosPushSettings): Promise { + const options = sharedStoreOptions(); + if (!options) return; + await SecureStore.setItemAsync(PUSH_SETTINGS_KEY, JSON.stringify(settings), options); +} + +/** + * Store a credentials item where the extension can read it. Written to the + * shared group first, then removed from the app's own group, so a crash in + * between leaves two copies rather than none. Off iOS a plain write. + */ +export async function writeSharedSecret(key: string, value: string): Promise { + const shared = sharedStoreOptions(); + if (!shared) { + await SecureStore.setItemAsync(key, value); + return; + } + await SecureStore.setItemAsync(key, value, shared); + const own = appStoreOptions(); + if (own) await SecureStore.deleteItemAsync(key, own).catch(() => undefined); +} + +/** Move an item saved before the shared group existed. True if it moved. */ +export async function moveToSharedGroup(key: string): Promise { + const own = appStoreOptions(); + if (!own || !sharedStoreOptions()) return false; + const value = await SecureStore.getItemAsync(key, own).catch(() => null); + if (value === null) return false; + await writeSharedSecret(key, value); + return true; +} From 4bb846b726f5ca5260b36548194f32bccba42683 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:00:14 +0200 Subject: [PATCH 323/334] feat: add an iOS notification extension that shows sender and subject --- app.config.js | 12 ++ plugins/ios-push/Jmap.swift | 161 +++++++++++++++++ plugins/ios-push/NotificationService.swift | 100 +++++++++++ plugins/ios-push/SharedKeychain.swift | 50 ++++++ plugins/ios-push/index.js | 195 +++++++++++++++++++++ 5 files changed, 518 insertions(+) create mode 100644 plugins/ios-push/Jmap.swift create mode 100644 plugins/ios-push/NotificationService.swift create mode 100644 plugins/ios-push/SharedKeychain.swift create mode 100644 plugins/ios-push/index.js diff --git a/app.config.js b/app.config.js index 4911cf31..2e8dbadf 100644 --- a/app.config.js +++ b/app.config.js @@ -22,6 +22,13 @@ COMMIT = COMMIT.slice(0, 7); // fall back to 1 (never uploaded). const IOS_BUILD_NUMBER = process.env.IOS_BUILD_NUMBER || '1'; +// Apple team: prefixes the keychain groups the app shares with its +// notification service extension (plugins/ios-push). Not a secret. +const APPLE_TEAM_ID = process.env.APPLE_TEAM_ID || '8ZANF9LQ39'; +// Which APNs host the device tokens belong to: TestFlight and App Store builds +// use production, simulators and development builds the sandbox. +const APS_ENVIRONMENT = process.env.APS_ENVIRONMENT === 'development' ? 'development' : 'production'; + module.exports = { expo: { name: 'Bulwark Mobile', @@ -96,12 +103,17 @@ module.exports = { microphonePermission: false, }, ], + ['./plugins/ios-push', { apsEnvironment: APS_ENVIRONMENT, teamId: APPLE_TEAM_ID }], ], extra: { commit: COMMIT, // Settings → About → Source code. A modified build sets SOURCE_CODE_URL // to where its own source is (AGPL §13); the default is upstream. sourceCodeUrl: process.env.SOURCE_CODE_URL || 'https://github.com/bulwarkmail/native', + // src/lib/ios-push-shared.ts + iosSharedKeychainGroup: `${APPLE_TEAM_ID}.org.bulwarkmail.shared`, + iosAppKeychainGroup: `${APPLE_TEAM_ID}.org.bulwarkmail.mobile`, + apnsEnvironment: APS_ENVIRONMENT === 'development' ? 'sandbox' : 'production', }, }, }; diff --git a/plugins/ios-push/Jmap.swift b/plugins/ios-push/Jmap.swift new file mode 100644 index 00000000..6f1ed71e --- /dev/null +++ b/plugins/ios-push/Jmap.swift @@ -0,0 +1,161 @@ +import Foundation + +/// The little JMAP the extension needs: who sent the new messages and what +/// they are about, plus the inbox's unread count for the badge. +struct PushedEmail { + let id: String + let threadId: String + let sender: String + let subject: String + let preview: String +} + +struct JmapFetchResult { + let emails: [PushedEmail] + let inboxUnread: Int? +} + +enum JmapError: Error { + case noCredentials + case http(Int) + case badResponse +} + +/// Credentials as the app stores them (src/api/jmap-client.ts StoredCredentials). +struct StoredCredentials { + var json: [String: Any] + + var username: String { json["username"] as? String ?? "" } + var password: String { json["password"] as? String ?? "" } + var accessToken: String? { json["accessToken"] as? String } + var refreshToken: String? { json["refreshToken"] as? String } + var tokenEndpoint: String? { json["tokenEndpoint"] as? String } + var clientId: String? { json["clientId"] as? String } + /// Milliseconds since 1970, like Date.now() in the app. + var expiresAt: Double? { (json["expiresAt"] as? NSNumber)?.doubleValue } +} + +enum Jmap { + static let session: URLSession = { + let config = URLSessionConfiguration.ephemeral + config.timeoutIntervalForRequest = 12 + config.timeoutIntervalForResource = 20 + return URLSession(configuration: config) + }() + + /// The Authorization header for these credentials, refreshing an OAuth + /// access token that is about to expire. A refreshed bundle is written back + /// to the shared keychain item so the app picks it up (and doesn't refresh + /// again with a refresh token the server has already replaced). + static func authorization(credentialsKey: String) async throws -> String { + guard let json = SharedKeychain.readJSON(credentialsKey) else { throw JmapError.noCredentials } + var creds = StoredCredentials(json: json) + if let token = creds.accessToken { + let nowMs = Date().timeIntervalSince1970 * 1000 + if let expiresAt = creds.expiresAt, expiresAt - nowMs < 60_000, + let refresh = creds.refreshToken, let endpoint = creds.tokenEndpoint, let clientId = creds.clientId, + let url = URL(string: endpoint) { + if let fresh = try? await refreshToken(url: url, refreshToken: refresh, clientId: clientId) { + creds.json["accessToken"] = fresh.accessToken + if let newRefresh = fresh.refreshToken { creds.json["refreshToken"] = newRefresh } + if let expiresIn = fresh.expiresIn { creds.json["expiresAt"] = nowMs + expiresIn * 1000 } + SharedKeychain.update(credentialsKey, json: creds.json) + return "Bearer \(fresh.accessToken)" + } + } + return "Bearer \(token)" + } + let basic = Data("\(creds.username):\(creds.password)".utf8).base64EncodedString() + return "Basic \(basic)" + } + + struct FreshTokens { + let accessToken: String + let refreshToken: String? + let expiresIn: Double? + } + + static func refreshToken(url: URL, refreshToken: String, clientId: String) async throws -> FreshTokens { + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") + request.setValue("application/json", forHTTPHeaderField: "Accept") + var form = URLComponents() + form.queryItems = [ + URLQueryItem(name: "grant_type", value: "refresh_token"), + URLQueryItem(name: "refresh_token", value: refreshToken), + URLQueryItem(name: "client_id", value: clientId), + ] + request.httpBody = form.percentEncodedQuery?.data(using: .utf8) + let (data, response) = try await session.data(for: request) + guard let http = response as? HTTPURLResponse else { throw JmapError.badResponse } + guard http.statusCode == 200 else { throw JmapError.http(http.statusCode) } + guard let body = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any], + let access = body["access_token"] as? String else { throw JmapError.badResponse } + return FreshTokens( + accessToken: access, + refreshToken: body["refresh_token"] as? String, + expiresIn: (body["expires_in"] as? NSNumber)?.doubleValue + ) + } + + static func fetch(apiUrl: URL, accountId: String, emailIds: [String], authorization: String) async throws -> JmapFetchResult { + var request = URLRequest(url: apiUrl) + request.httpMethod = "POST" + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue("application/json", forHTTPHeaderField: "Accept") + request.setValue(authorization, forHTTPHeaderField: "Authorization") + let body: [String: Any] = [ + "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"], + "methodCalls": [ + ["Email/get", [ + "accountId": accountId, + "ids": Array(emailIds.prefix(5)), + "properties": ["threadId", "from", "subject", "preview"], + ], "0"], + ["Mailbox/query", ["accountId": accountId, "filter": ["role": "inbox"]], "1"], + ["Mailbox/get", [ + "accountId": accountId, + "#ids": ["resultOf": "1", "name": "Mailbox/query", "path": "/ids"], + "properties": ["unreadEmails"], + ], "2"], + ], + ] + request.httpBody = try JSONSerialization.data(withJSONObject: body) + let (data, response) = try await session.data(for: request) + guard let http = response as? HTTPURLResponse else { throw JmapError.badResponse } + guard http.statusCode == 200 else { throw JmapError.http(http.statusCode) } + guard let root = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any], + let responses = root["methodResponses"] as? [[Any]] else { throw JmapError.badResponse } + + var emails: [PushedEmail] = [] + var inboxUnread: Int? + for response in responses where response.count >= 2 { + guard let name = response[0] as? String, let args = response[1] as? [String: Any] else { continue } + if name == "Email/get", let list = args["list"] as? [[String: Any]] { + // Keep the order the push gave (newest first from the server's filter). + // Stalwart can list an id twice; keep the first. + let byId = Dictionary(list.compactMap { item -> (String, [String: Any])? in + guard let id = item["id"] as? String else { return nil } + return (id, item) + }, uniquingKeysWith: { first, _ in first }) + for id in emailIds { + guard let item = byId[id] else { continue } + let from = (item["from"] as? [[String: Any]])?.first + let name = (from?["name"] as? String).flatMap { $0.isEmpty ? nil : $0 } + let sender = name ?? (from?["email"] as? String) ?? "" + emails.append(PushedEmail( + id: id, + threadId: item["threadId"] as? String ?? "", + sender: sender, + subject: item["subject"] as? String ?? "", + preview: item["preview"] as? String ?? "" + )) + } + } else if name == "Mailbox/get", let list = args["list"] as? [[String: Any]] { + inboxUnread = (list.first?["unreadEmails"] as? NSNumber)?.intValue + } + } + return JmapFetchResult(emails: emails, inboxUnread: inboxUnread) + } +} diff --git a/plugins/ios-push/NotificationService.swift b/plugins/ios-push/NotificationService.swift new file mode 100644 index 00000000..1b93d075 --- /dev/null +++ b/plugins/ios-push/NotificationService.swift @@ -0,0 +1,100 @@ +import Foundation +import UserNotifications + +/// Turns the relay's content-blind push ("New message" plus ids) into a real +/// notification: sender, subject and preview, fetched over JMAP on this +/// device with the account's own credentials. Nothing about the message ever +/// passes through the relay or Apple. +/// +/// What the app shares through the keychain group (src/lib/ios-push-shared.ts): +/// bulwark_push_account__ {credentialsKey, apiUrl} +/// bulwark_push_settings {badge, activeAccountId, strings{...}} +/// the account's stored credentials +/// If anything is missing or the server can't be reached, the relay's +/// placeholder text is shown, so the user still learns there is new mail. +final class NotificationService: UNNotificationServiceExtension { + private var contentHandler: ((UNNotificationContent) -> Void)? + private var placeholder: UNMutableNotificationContent? + private var work: Task? + + override func didReceive( + _ request: UNNotificationRequest, + withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void + ) { + self.contentHandler = contentHandler + guard let content = request.content.mutableCopy() as? UNMutableNotificationContent else { + contentHandler(request.content) + return + } + placeholder = content + let push = request.content.userInfo["body"] as? [String: Any] + work = Task { [weak self] in + let enriched = await NotificationService.enrich(content, push: push) + self?.finish(enriched) + } + } + + override func serviceExtensionTimeWillExpire() { + work?.cancel() + finish(placeholder) + } + + private func finish(_ content: UNNotificationContent?) { + guard let handler = contentHandler, let content else { return } + contentHandler = nil + handler(content) + } + + static func sanitize(_ id: String) -> String { + String(id.map { ch in (ch.isASCII && (ch.isLetter || ch.isNumber)) || ch == "." || ch == "-" || ch == "_" ? ch : "_" }) + } + + static func enrich(_ content: UNMutableNotificationContent, push: [String: Any]?) async -> UNMutableNotificationContent { + guard let push, + let accountId = push["accountId"] as? String, !accountId.isEmpty, + let account = SharedKeychain.readJSON("bulwark_push_account__\(sanitize(accountId))"), + let credentialsKey = account["credentialsKey"] as? String, + let apiString = account["apiUrl"] as? String, + let apiUrl = URL(string: apiString) else { + return content + } + let emailIds = push["emailIds"] as? [String] ?? [] + let settings = SharedKeychain.readJSON("bulwark_push_settings") ?? [:] + let strings = settings["strings"] as? [String: String] ?? [:] + + content.threadIdentifier = accountId + // The app reads userInfo.body when the notification is tapped. + var body = push + + do { + let authorization = try await Jmap.authorization(credentialsKey: credentialsKey) + let result = try await Jmap.fetch(apiUrl: apiUrl, accountId: accountId, emailIds: emailIds, authorization: authorization) + + if let first = result.emails.first { + content.title = first.sender + content.subtitle = first.subject.isEmpty ? (strings["noSubject"] ?? "(no subject)") : first.subject + content.body = first.preview + if result.emails.count > 1 { + let more = (strings["more"] ?? "and {count} more").replacingOccurrences(of: "{count}", with: String(result.emails.count - 1)) + content.body = first.preview.isEmpty ? more : "\(first.preview)\n\(more)" + } + body["emailId"] = first.id + body["threadId"] = first.threadId + } + + // Badge: like the app, the active account's inbox unread count. A push + // for another account leaves the badge as it is. + if settings["badge"] as? Bool == true, + settings["activeAccountId"] as? String == accountId, + let unread = result.inboxUnread { + content.badge = NSNumber(value: unread) + } + } catch { + // Keep the placeholder. + } + var userInfo = content.userInfo + userInfo["body"] = body + content.userInfo = userInfo + return content + } +} diff --git a/plugins/ios-push/SharedKeychain.swift b/plugins/ios-push/SharedKeychain.swift new file mode 100644 index 00000000..6eeaeb31 --- /dev/null +++ b/plugins/ios-push/SharedKeychain.swift @@ -0,0 +1,50 @@ +import Foundation +import Security + +/// Items the app shares with this extension through a keychain access group +/// (BulwarkKeychainGroup in Info.plist). The app writes them with +/// expo-secure-store: generic passwords whose account and generic attributes +/// are the UTF-8 key and whose service is "app:no-auth". +enum SharedKeychain { + static let service = "app:no-auth" + + static var group: String? { + Bundle.main.object(forInfoDictionaryKey: "BulwarkKeychainGroup") as? String + } + + static func read(_ key: String) -> Data? { + guard let group else { return nil } + let account = Data(key.utf8) + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrAccount as String: account, + kSecAttrAccessGroup as String: group, + kSecMatchLimit as String: kSecMatchLimitOne, + kSecReturnData as String: true, + ] + var item: CFTypeRef? + guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess else { return nil } + return item as? Data + } + + static func readJSON(_ key: String) -> [String: Any]? { + guard let data = read(key) else { return nil } + return (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] + } + + /// Replaces the value of an existing item (refreshed OAuth tokens). Never + /// creates one: the app owns which items exist. + @discardableResult + static func update(_ key: String, json: [String: Any]) -> Bool { + guard let group, let data = try? JSONSerialization.data(withJSONObject: json) else { return false } + let account = Data(key.utf8) + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecAttrService as String: service, + kSecAttrAccount as String: account, + kSecAttrAccessGroup as String: group, + ] + let attributes: [String: Any] = [kSecValueData as String: data] + return SecItemUpdate(query as CFDictionary, attributes as CFDictionary) == errSecSuccess + } +} diff --git a/plugins/ios-push/index.js b/plugins/ios-push/index.js new file mode 100644 index 00000000..2ce065de --- /dev/null +++ b/plugins/ios-push/index.js @@ -0,0 +1,195 @@ +// Config plugin: push notifications on iOS. +// +// - The app's entitlements: aps-environment, and a keychain group shared with +// the notification service extension (the app's own group stays first, so +// items saved before keep their group). +// - A Notification Service Extension target ("NotificationService", +// org.bulwarkmail.mobile.NotificationService). The relay's push carries +// only ids; the extension fetches sender and subject over JMAP on the device +// before iOS shows the notification. Its sources live next to this file and +// are copied into ios/ at prebuild, like everything else in ios/. +// +// Options (app.config.js): +// apsEnvironment: 'production' | 'development' (APS_ENVIRONMENT env wins) +// teamId: Apple team id, for the shared keychain group +// extensionProfile / appProfile: provisioning profile specifiers for the +// Release configuration of the extension and the app (IOS_NSE_PROFILE and +// IOS_APP_PROFILE env win). Set per target because xcodebuild's command +// line applies a specifier to every target. Empty = leave unset. + +const fs = require('node:fs'); +const path = require('node:path'); +const { + withEntitlementsPlist, + withXcodeProject, + withDangerousMod, + createRunOncePlugin, +} = require('@expo/config-plugins'); + +const TARGET = 'NotificationService'; +const DEPLOYMENT_TARGET = '15.1'; +const SOURCE_FILES = ['NotificationService.swift', 'SharedKeychain.swift', 'Jmap.swift']; + +function sharedGroupSuffix() { + return 'org.bulwarkmail.shared'; +} + +function withAppEntitlements(config, { apsEnvironment }) { + return withEntitlementsPlist(config, (cfg) => { + const bundleId = cfg.ios?.bundleIdentifier; + cfg.modResults['aps-environment'] = apsEnvironment; + const groups = new Set(cfg.modResults['keychain-access-groups'] ?? []); + groups.add(`$(AppIdentifierPrefix)${bundleId}`); + groups.add(`$(AppIdentifierPrefix)${sharedGroupSuffix()}`); + cfg.modResults['keychain-access-groups'] = [...groups]; + return cfg; + }); +} + +function infoPlist({ version, buildNumber }) { + return ` + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Bulwark Mail notifications + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + ${version} + CFBundleVersion + ${buildNumber} + BulwarkKeychainGroup + $(DEVELOPMENT_TEAM).${sharedGroupSuffix()} + NSExtension + + NSExtensionPointIdentifier + com.apple.usernotifications.service + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).NotificationService + + + +`; +} + +function entitlementsPlist() { + return ` + + + + keychain-access-groups + + $(AppIdentifierPrefix)${sharedGroupSuffix()} + + + +`; +} + +function withExtensionFiles(config) { + return withDangerousMod(config, [ + 'ios', + async (cfg) => { + const dir = path.join(cfg.modRequest.platformProjectRoot, TARGET); + fs.mkdirSync(dir, { recursive: true }); + for (const file of SOURCE_FILES) { + fs.copyFileSync(path.join(__dirname, file), path.join(dir, file)); + } + fs.writeFileSync( + path.join(dir, `${TARGET}-Info.plist`), + infoPlist({ version: cfg.version, buildNumber: cfg.ios?.buildNumber ?? '1' }), + ); + fs.writeFileSync(path.join(dir, `${TARGET}.entitlements`), entitlementsPlist()); + return cfg; + }, + ]); +} + +function setReleaseProfiles(cfg, { appProfile, extensionProfile }) { + if (!appProfile && !extensionProfile) return; + const project = cfg.modResults; + const appName = `"${cfg.modRequest.projectName}"`; + const configs = project.pbxXCBuildConfigurationSection(); + for (const key of Object.keys(configs)) { + const entry = configs[key]; + if (!entry.buildSettings || entry.name !== 'Release') continue; + const name = entry.buildSettings.PRODUCT_NAME; + const profile = name === `"${TARGET}"` ? extensionProfile + : (name === appName || name === '"$(TARGET_NAME)"') && entry.buildSettings.PRODUCT_BUNDLE_IDENTIFIER ? appProfile + : ''; + if (profile) entry.buildSettings.PROVISIONING_PROFILE_SPECIFIER = `"${profile}"`; + } +} + +// One mod for creating the target and naming the profiles: separate +// withXcodeProject mods don't run in the order they are registered. +function withExtensionTarget(config, { teamId, appProfile, extensionProfile }) { + return withXcodeProject(config, (cfg) => { + addExtensionTarget(cfg, { teamId }); + setReleaseProfiles(cfg, { appProfile, extensionProfile }); + return cfg; + }); +} + +function addExtensionTarget(cfg, { teamId }) { + const project = cfg.modResults; + if (project.pbxTargetByName(TARGET)) return; + + // addTarget expects these sections; a fresh Expo project may not have them. + const objects = project.hash.project.objects; + objects.PBXTargetDependency = objects.PBXTargetDependency || {}; + objects.PBXContainerItemProxy = objects.PBXContainerItemProxy || {}; + + const group = project.addPbxGroup( + [...SOURCE_FILES, `${TARGET}-Info.plist`, `${TARGET}.entitlements`], + TARGET, + TARGET, + ); + // Hang the group off the project's main group. + const mainGroupId = project.getFirstProject().firstProject.mainGroup; + project.addToPbxGroup(group.uuid, mainGroupId); + + const bundleId = `${cfg.ios.bundleIdentifier}.${TARGET}`; + const target = project.addTarget(TARGET, 'app_extension', TARGET, bundleId); + project.addBuildPhase(SOURCE_FILES, 'PBXSourcesBuildPhase', 'Sources', target.uuid); + project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid); + project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid); + + const configs = project.pbxXCBuildConfigurationSection(); + for (const key of Object.keys(configs)) { + const entry = configs[key]; + if (!entry.buildSettings || entry.buildSettings.PRODUCT_NAME !== `"${TARGET}"`) continue; + const s = entry.buildSettings; + s.CODE_SIGN_ENTITLEMENTS = `${TARGET}/${TARGET}.entitlements`; + s.IPHONEOS_DEPLOYMENT_TARGET = DEPLOYMENT_TARGET; + s.TARGETED_DEVICE_FAMILY = '"1,2"'; + s.SWIFT_VERSION = '5.0'; + s.CLANG_ENABLE_MODULES = 'YES'; + s.GENERATE_INFOPLIST_FILE = 'NO'; + if (teamId) s.DEVELOPMENT_TEAM = teamId; + } +} + +function withIosPush(config, options = {}) { + const apsEnvironment = process.env.APS_ENVIRONMENT || options.apsEnvironment || 'production'; + const teamId = process.env.APPLE_TEAM_ID || options.teamId || ''; + const extensionProfile = process.env.IOS_NSE_PROFILE || options.extensionProfile || ''; + const appProfile = process.env.IOS_APP_PROFILE || options.appProfile || ''; + config = withAppEntitlements(config, { apsEnvironment }); + config = withExtensionFiles(config); + config = withExtensionTarget(config, { teamId, appProfile, extensionProfile }); + return config; +} + +module.exports = createRunOncePlugin(withIosPush, 'bulwark-ios-push', '1.0.0'); From f62849d1feafbd2c5a9560861507c00325b2c901 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:00:15 +0200 Subject: [PATCH 324/334] feat: turn on push notifications on iOS through the relay's APNs transport --- locales/rn/en.json | 9 ++ src/components/AppIconBadge.tsx | 29 +++++ src/lib/__tests__/ios-push-tap.test.ts | 41 ++++++ src/lib/push-notifications.ts | 168 ++++++++++++++++++++++++- 4 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 src/lib/__tests__/ios-push-tap.test.ts diff --git a/locales/rn/en.json b/locales/rn/en.json index fbd4f959..2b932ba7 100644 --- a/locales/rn/en.json +++ b/locales/rn/en.json @@ -639,6 +639,12 @@ "up_to_date": "Already up to date" } }, + "push": { + "ios": { + "more_messages": "and {count} more", + "no_subject": "(no subject)" + } + }, "push_prompt": { "body": "Turn on background notifications so new messages reach you while the app is closed.", "dismiss": "Not now", @@ -1008,6 +1014,8 @@ "confirm_disable_message_mobile": "This device will stop receiving new-mail alerts for this account.", "description_mobile": "Receive system notifications for new mail when the app is closed. Delivered via the Bulwark push relay; the relay never sees mail content.", "err_android_only": "Push notifications are only available on Android.", + "err_apns_empty": "iOS returned an empty push token.", + "err_apns_token": "iOS could not issue a push token ({detail}).", "err_fcm_empty": "Firebase returned an empty device token.", "err_fcm_token": "Firebase could not issue a device token ({detail}). Push over FCM needs Google Play services on this device.", "err_fcm_token_up": "Firebase could not issue a device token ({detail}). Push over FCM needs Google Play services on this device. This device has a UnifiedPush distributor installed - switch the delivery method to UnifiedPush instead.", @@ -1015,6 +1023,7 @@ "err_no_account": "No account loaded - cannot set up push.", "err_permission": "Notification permission was not granted.", "err_relay_https": "The relay URL must use https://.", + "err_relay_no_apns": "The push relay at {url} does not deliver to iPhones and iPads yet.", "err_relay_rejected": "The push relay rejected the registration: {detail}", "err_relay_unreachable": "Could not reach the push relay at {url} ({detail}).", "err_up_android_only": "UnifiedPush is only available on Android.", diff --git a/src/components/AppIconBadge.tsx b/src/components/AppIconBadge.tsx index fd5a5c4a..2cbc4213 100644 --- a/src/components/AppIconBadge.tsx +++ b/src/components/AppIconBadge.tsx @@ -1,21 +1,50 @@ import { useEffect } from 'react'; +import { Platform } from 'react-native'; import { useEmailStore } from '../stores/email-store'; import { useSettingsStore } from '../stores/settings-store'; +import { useLocaleStore } from '../stores/locale-store'; +import { useAuthStore } from '../stores/auth-store'; import { appBadgeCount, setAppBadge } from '../lib/app-badge'; +import { jmapClient } from '../api/jmap-client'; +import { writeIosPushSettings } from '../lib/ios-push-shared'; /** * Keeps the app icon badge on the active account's inbox unread count (see * `lib/app-badge`). Mounted with the signed-in UI; unmounting means the last * account signed out, so the badge is cleared then. Renders nothing. + * + * On iOS it also tells the notification extension (plugins/ios-push) which + * account the badge follows and the few texts it writes itself, so a push + * that arrives while the app is closed updates the badge the same way. */ export function AppIconBadge() { const enabled = useSettingsStore((s) => s.appIconUnreadBadge); const count = useEmailStore((s) => appBadgeCount(s.mailboxes, enabled)); + const activeAccountId = useAuthStore((s) => s.activeAccountId); + const t = useLocaleStore((s) => s.t); useEffect(() => { void setAppBadge(count); }, [count]); + useEffect(() => { + if (Platform.OS !== 'ios') return; + let jmapAccountId: string | null = null; + try { + jmapAccountId = jmapClient.accountId; + } catch { + jmapAccountId = null; + } + void writeIosPushSettings({ + badge: enabled, + activeAccountId: jmapAccountId, + strings: { + noSubject: t('push.ios.no_subject', '(no subject)'), + more: t('push.ios.more_messages', 'and {count} more'), + }, + }).catch(() => undefined); + }, [enabled, activeAccountId, t]); + useEffect(() => () => { void setAppBadge(0); }, []); return null; diff --git a/src/lib/__tests__/ios-push-tap.test.ts b/src/lib/__tests__/ios-push-tap.test.ts new file mode 100644 index 00000000..98aa33b7 --- /dev/null +++ b/src/lib/__tests__/ios-push-tap.test.ts @@ -0,0 +1,41 @@ +import { beforeEach, describe, expect, it } from 'vitest'; +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { iosTapPayload } from '../push-notifications'; + +// A tapped iOS mail notification: the relay's ids arrive as the +// notification's data (APNs `body`), the notification extension adds the +// message it showed, and the app opens that message under the account whose +// push setup recorded the JMAP account id. + +describe('iosTapPayload', () => { + beforeEach(async () => { + await AsyncStorage.clear(); + await AsyncStorage.setItem('push:jmapAccountIds:v1', JSON.stringify({ 'ada@mail.example.org': 'c' })); + }); + + it('opens the message the extension showed, in the right account', async () => { + const payload = await iosTapPayload({ + kind: 'jmap-email-push', + accountId: 'c', + emailIds: ['e1', 'e2'], + emailId: 'e2', + threadId: 't2', + }); + expect(payload).toEqual({ emailId: 'e2', threadId: 't2', accountId: 'ada@mail.example.org', jmapAccountId: 'c' }); + }); + + it('falls back to the first pushed id when the extension could not fetch', async () => { + const payload = await iosTapPayload({ kind: 'jmap-email-push', accountId: 'c', emailIds: ['e1'] }); + expect(payload?.emailId).toBe('e1'); + expect(payload?.threadId).toBe(''); + }); + + it('leaves calendar reminders and other local notifications alone', async () => { + expect(await iosTapPayload({ eventId: 'ev1', calendarId: 'cal' })).toBeNull(); + expect(await iosTapPayload(null)).toBeNull(); + }); + + it('does nothing for a push without a message id', async () => { + expect(await iosTapPayload({ kind: 'jmap-state-change', accountId: 'c', emailIds: [] })).toBeNull(); + }); +}); diff --git a/src/lib/push-notifications.ts b/src/lib/push-notifications.ts index ddc58c04..1c923466 100644 --- a/src/lib/push-notifications.ts +++ b/src/lib/push-notifications.ts @@ -29,6 +29,9 @@ import { persistedAccounts, } from '../device-sync/app/prefs'; import { CALENDAR_AUTHORITY, CONTACTS_AUTHORITY, type Authority } from '../device-sync/types'; +import * as Notifications from 'expo-notifications'; +import { credentialsKey } from '../api/jmap-client'; +import { apnsEnvironment, removeIosPushAccount, writeIosPushAccount } from './ios-push-shared'; import { getUnifiedPushDistributors, isUnifiedPushSupported, @@ -524,12 +527,12 @@ function getNative(): BulwarkFcmNative | null { return (NativeModules as Record).BulwarkFcm as BulwarkFcmNative | undefined ?? null; } -/** True on platforms that have a push transport wired up (Android only). */ +/** True on platforms that have a push transport wired up: FCM or UnifiedPush on Android, APNs on iOS. */ export function isPushSupported(): boolean { - return getNative() !== null || isUnifiedPushSupported(); + return Platform.OS === 'ios' || getNative() !== null || isUnifiedPushSupported(); } -export type PushTransport = 'fcm' | 'unifiedpush'; +export type PushTransport = 'fcm' | 'unifiedpush' | 'apns'; export async function getStoredPushTransport(): Promise { const raw = await AsyncStorage.getItem(PUSH_TRANSPORT_KEY); @@ -545,6 +548,8 @@ export async function setStoredPushTransport(transport: PushTransport | null): P } export async function getEffectivePushTransport(): Promise { + // iOS has one way in: APNs, through the relay. + if (Platform.OS === 'ios') return 'apns'; return (await getStoredPushTransport()) ?? 'fcm'; } @@ -637,6 +642,14 @@ export async function dismissPushPrompt(accountId: string): Promise { } export async function requestNotificationPermission(): Promise { + if (Platform.OS === 'ios') { + const current = await Notifications.getPermissionsAsync(); + if (current.granted) return true; + const asked = await Notifications.requestPermissionsAsync({ + ios: { allowAlert: true, allowBadge: true, allowSound: true }, + }); + return asked.granted; + } if (Platform.OS !== 'android') return true; if (Platform.Version < 33) return true; const status = await PermissionsAndroid.request( @@ -688,6 +701,26 @@ async function getFcmTokenOrThrow(native: BulwarkFcmNative): Promise { return token; } +// The APNs device token (hex). iOS hands it out once notifications are +// allowed; expo-notifications asks the OS for it. +async function getApnsTokenOrThrow(): Promise { + let token: string | null = null; + try { + const result = await Notifications.getDevicePushTokenAsync(); + token = typeof result.data === 'string' ? result.data : null; + } catch (err) { + const detail = err instanceof Error ? err.message : String(err); + throw new PushSetupError( + 'token', + t('settings.notifications.push.err_apns_token', 'iOS could not issue a push token ({detail}).', { detail }), + ); + } + if (!token) { + throw new PushSetupError('token', t('settings.notifications.push.err_apns_empty', 'iOS returned an empty push token.')); + } + return token; +} + /** * The relay's VAPID public key, when it has one. Passed to the UnifiedPush * distributor at registration so distributors that pin the application server @@ -778,6 +811,64 @@ async function registerWithRelay(params: { } } +async function registerWithRelayApns(params: { + relayBaseUrl: string; + subscriptionId: string; + deviceToken: string; + accountLabel?: string; +}): Promise { + let res: Response; + try { + res = await fetch(buildRelayUrl(params.relayBaseUrl, '/api/push/register/apns'), { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ + subscriptionId: params.subscriptionId, + deviceToken: params.deviceToken, + environment: apnsEnvironment(), + accountLabel: params.accountLabel, + }), + }); + } catch (err) { + const detail = err instanceof Error ? err.message : String(err); + throw new PushSetupError( + 'relay', + t('settings.notifications.push.err_relay_unreachable', 'Could not reach the push relay at {url} ({detail}).', { + url: params.relayBaseUrl, + detail, + }), + ); + } + if (res.status === 503) { + throw new PushSetupError( + 'relay', + t('settings.notifications.push.err_relay_no_apns', 'The push relay at {url} does not deliver to iPhones and iPads yet.', { + url: params.relayBaseUrl, + }), + ); + } + if (!res.ok) { + throw new PushSetupError( + 'relay', + t('settings.notifications.push.err_relay_rejected', 'The push relay rejected the registration: {detail}', { + detail: await readRelayError(res), + }), + ); + } +} + +/** + * Tell the iOS notification extension how to reach an account: which stored + * credentials to use and the JMAP API URL. Without it the extension shows the + * relay's placeholder text. + */ +async function shareAccountWithExtension(accountId: string, jmapAccountId: string | null): Promise { + if (Platform.OS !== 'ios' || !jmapAccountId) return; + const apiUrl = jmapClient.currentSession?.apiUrl; + if (!apiUrl) return; + await writeIosPushAccount(jmapAccountId, { credentialsKey: credentialsKey(accountId), apiUrl }).catch(() => undefined); +} + async function registerWithRelayUnifiedPush(params: { relayBaseUrl: string; subscriptionId: string; @@ -948,6 +1039,9 @@ async function setupPushNotificationsInner( if (transport === 'fcm' && !native) { throw new PushSetupError('platform', t('settings.notifications.push.err_android_only', 'Push notifications are only available on Android.')); } + if (transport === 'apns' && Platform.OS !== 'ios') { + throw new PushSetupError('platform', 'APNs is only available on iOS.'); + } if (transport === 'unifiedpush' && !isUnifiedPushSupported()) { throw new PushSetupError('platform', t('settings.notifications.push.err_up_android_only', 'UnifiedPush is only available on Android.')); } @@ -967,8 +1061,11 @@ async function setupPushNotificationsInner( logPhase('token', transport); let fcmToken: string | null = null; let upEndpoint: UnifiedPushEndpoint | null = null; + let apnsToken: string | null = null; if (transport === 'unifiedpush') { upEndpoint = await getUnifiedPushEndpointOrThrow(relayBaseUrl); + } else if (transport === 'apns') { + apnsToken = await getApnsTokenOrThrow(); } else { fcmToken = await getFcmTokenOrThrow(native!); } @@ -993,7 +1090,14 @@ async function setupPushNotificationsInner( // individually and tags it with the JMAP account id so the headless task // can route it. logPhase('relay', relayBaseUrl); - if (upEndpoint) { + if (apnsToken) { + await registerWithRelayApns({ + relayBaseUrl, + subscriptionId: deviceClientId, + deviceToken: apnsToken, + accountLabel: params.accountLabel, + }); + } else if (upEndpoint) { await registerWithRelayUnifiedPush({ relayBaseUrl, subscriptionId: deviceClientId, @@ -1043,6 +1147,7 @@ async function setupPushNotificationsInner( await rememberRefusedEmailPushAccounts(accountId, refusedBefore, emailPush, refreshed.emailPush); await addPushAccountId(accountId); await writePushJmapAccountId(accountId, jmapAccountId); + await shareAccountWithExtension(accountId, jmapAccountId); await AsyncStorage.removeItem(optedOutKey(accountId)); logPhase('done', 'reused existing subscription'); return { subscriptionId: storedServerId, verified: true }; @@ -1125,6 +1230,7 @@ async function setupPushNotificationsInner( } await addPushAccountId(accountId); await writePushJmapAccountId(accountId, jmapAccountId); + await shareAccountWithExtension(accountId, jmapAccountId); await AsyncStorage.removeItem(optedOutKey(accountId)); logPhase('done', 'subscription verified'); @@ -1196,6 +1302,10 @@ async function deregisterFromRelay( } async function clearAccountPushKeys(accountId: string): Promise { + if (Platform.OS === 'ios') { + const jmapAccountId = (await readPushJmapAccountIds())[accountId]; + if (jmapAccountId) await removeIosPushAccount(jmapAccountId); + } await AsyncStorage.multiRemove([ subscriptionIdKey(accountId), deviceClientIdKey(accountId), @@ -1509,6 +1619,17 @@ export function notificationTapJmapAccountId(payload: NotificationTapPayload): s // before JS was ready to handle it. Subsequent taps while running are delivered // via addNotificationTapListener. export async function getInitialNotificationTap(): Promise { + if (Platform.OS === 'ios') { + const response = await Notifications.getLastNotificationResponseAsync().catch(() => null); + if (!response || response.actionIdentifier !== Notifications.DEFAULT_ACTION_IDENTIFIER) return null; + const handledKey = 'push:lastHandledResponse:v1'; + const id = response.notification.request.identifier; + // The last response survives app restarts; open it once. + if ((await AsyncStorage.getItem(handledKey)) === id) return null; + const payload = await iosTapPayload(response.notification.request.content.data); + if (payload) await AsyncStorage.setItem(handledKey, id); + return payload; + } if (Platform.OS !== 'android') return null; const native = (NativeModules as Record).BulwarkFcm as | { getInitialNotification?: () => Promise } @@ -1524,8 +1645,47 @@ export async function getInitialNotificationTap(): Promise void, ): () => void { + if (Platform.OS === 'ios') { + const sub = Notifications.addNotificationResponseReceivedListener((response) => { + if (response.actionIdentifier !== Notifications.DEFAULT_ACTION_IDENTIFIER) return; + const id = response.notification.request.identifier; + void iosTapPayload(response.notification.request.content.data).then((payload) => { + if (!payload) return; + void AsyncStorage.setItem('push:lastHandledResponse:v1', id); + listener(payload); + }); + }); + return () => sub.remove(); + } if (Platform.OS !== 'android') return () => undefined; const emitter = new NativeEventEmitter(NativeModules.BulwarkFcm); const sub = emitter.addListener('fcm:notificationTap', listener); return () => sub.remove(); } + +/** + * A tapped iOS mail notification as the app opens it. The relay puts the + * JMAP account and message ids into the payload's `body`, which + * expo-notifications hands over as `data`; the notification extension adds + * the message (and thread) it showed. Calendar reminders and other local + * notifications carry no `kind` and are left to their own handlers. + */ +export async function iosTapPayload(data: unknown): Promise { + if (!data || typeof data !== 'object') return null; + const d = data as Record; + if (d.kind !== 'jmap-email-push' && d.kind !== 'jmap-state-change') return null; + const jmapAccountId = typeof d.accountId === 'string' ? d.accountId : ''; + const ids = Array.isArray(d.emailIds) ? d.emailIds.filter((v): v is string => typeof v === 'string') : []; + const emailId = typeof d.emailId === 'string' && d.emailId ? d.emailId : ids[0]; + if (!emailId) return null; + // The local account the subscription belongs to: the one whose push + // setup recorded this JMAP account id. + const byLocal = await readPushJmapAccountIds(); + const accountId = Object.keys(byLocal).find((local) => byLocal[local] === jmapAccountId); + return { + emailId, + threadId: typeof d.threadId === 'string' ? d.threadId : '', + accountId, + jmapAccountId: jmapAccountId || undefined, + }; +} From 7078dfe8068918be3dac8120bc4c69d8d16d523e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:00:15 +0200 Subject: [PATCH 325/334] ci: sign the iOS notification extension with its own App Store profile --- .github/workflows/release-ios.yml | 100 ++++++++++++++++++------------ 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/.github/workflows/release-ios.yml b/.github/workflows/release-ios.yml index 4db1d8bf..4c708490 100644 --- a/.github/workflows/release-ios.yml +++ b/.github/workflows/release-ios.yml @@ -59,9 +59,6 @@ jobs: - name: Install JS dependencies run: npm ci - - name: Generate the iOS project - run: npx expo prebuild --platform ios --clean - - name: Check for signing credentials id: creds env: @@ -69,14 +66,69 @@ jobs: ASC_KEY: ${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }} TEAM: ${{ secrets.APPLE_TEAM_ID }} PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }} + NSE_PROFILE: ${{ secrets.IOS_NSE_PROVISIONING_PROFILE_BASE64 }} run: | if [ -n "$CERT" ] && [ -n "$ASC_KEY" ] && [ -n "$TEAM" ] && [ -n "$PROFILE" ]; then + if [ -z "$NSE_PROFILE" ]; then + # The notification service extension (plugins/ios-push) is its + # own App ID and needs its own App Store profile. + echo "::error::IOS_NSE_PROVISIONING_PROFILE_BASE64 is not set: the notification extension (org.bulwarkmail.mobile.NotificationService) can't be signed." + exit 1 + fi echo "signed=true" >> "$GITHUB_OUTPUT" else echo "signed=false" >> "$GITHUB_OUTPUT" echo "::notice::Signing secrets are not configured - compiling without signing. No IPA will be produced." fi + - name: Install the provisioning profiles + if: steps.creds.outputs.signed == 'true' + id: profiles + env: + APP_PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }} + NSE_PROFILE_BASE64: ${{ secrets.IOS_NSE_PROVISIONING_PROFILE_BASE64 }} + run: | + # Xcode moved the profile directory in 16; write both so this does not + # silently stop working on a runner image bump. + mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles + mkdir -p ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles + + # One App Store profile per target: the app and its notification + # service extension (plugins/ios-push). + for side in app nse; do + if [ "$side" = app ]; then DATA="$APP_PROFILE_BASE64"; else DATA="$NSE_PROFILE_BASE64"; fi + PROFILE="$RUNNER_TEMP/$side.mobileprovision" + echo "$DATA" | base64 -d > "$PROFILE" + + # A .mobileprovision is a CMS-signed plist, so it has to be decoded + # before anything inside it can be read. + PLIST=$(security cms -D -i "$PROFILE") + UUID=$(echo "$PLIST" | plutil -extract UUID raw -) + NAME=$(echo "$PLIST" | plutil -extract Name raw -) + # application-identifier is "TEAMID.bundle.id" - deriving the bundle + # ID from the profile keeps ExportOptions honest if app.config.js + # changes. + APP_ID=$(echo "$PLIST" | plutil -extract Entitlements.application-identifier raw -) + BUNDLE_ID="${APP_ID#*.}" + EXPIRY=$(echo "$PLIST" | plutil -extract ExpirationDate raw -) + + cp "$PROFILE" ~/Library/MobileDevice/Provisioning\ Profiles/"$UUID".mobileprovision + cp "$PROFILE" ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles/"$UUID".mobileprovision + + echo "${side}_uuid=$UUID" >> "$GITHUB_OUTPUT" + echo "${side}_name=$NAME" >> "$GITHUB_OUTPUT" + echo "${side}_bundle_id=$BUNDLE_ID" >> "$GITHUB_OUTPUT" + echo "$side: profile '$NAME' ($UUID) for $BUNDLE_ID, expires $EXPIRY" + done + + - name: Generate the iOS project + env: + # plugins/ios-push names each target's profile: the command line + # can't, it would give every target the same one. + IOS_APP_PROFILE: ${{ steps.profiles.outputs.app_uuid }} + IOS_NSE_PROFILE: ${{ steps.profiles.outputs.nse_uuid }} + run: npx expo prebuild --platform ios --clean + - name: Resolve workspace and scheme id: xcode run: | @@ -141,38 +193,6 @@ jobs: echo "$ASC_KEY_BASE64" | base64 -d > ~/private_keys/AuthKey_${ASC_KEY_ID}.p8 chmod 600 ~/private_keys/AuthKey_${ASC_KEY_ID}.p8 - - name: Install the provisioning profile - if: steps.creds.outputs.signed == 'true' - id: profile - env: - PROFILE_BASE64: ${{ secrets.IOS_PROVISIONING_PROFILE_BASE64 }} - run: | - PROFILE="$RUNNER_TEMP/profile.mobileprovision" - echo "$PROFILE_BASE64" | base64 -d > "$PROFILE" - - # A .mobileprovision is a CMS-signed plist, so it has to be decoded - # before anything inside it can be read. - PLIST=$(security cms -D -i "$PROFILE") - UUID=$(echo "$PLIST" | plutil -extract UUID raw -) - NAME=$(echo "$PLIST" | plutil -extract Name raw -) - # application-identifier is "TEAMID.bundle.id" - deriving the bundle ID - # from the profile keeps ExportOptions honest if app.config.js changes. - APP_ID=$(echo "$PLIST" | plutil -extract Entitlements.application-identifier raw -) - BUNDLE_ID="${APP_ID#*.}" - EXPIRY=$(echo "$PLIST" | plutil -extract ExpirationDate raw -) - - # Xcode moved the profile directory in 16; write both so this does not - # silently stop working on a runner image bump. - mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles - mkdir -p ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles - cp "$PROFILE" ~/Library/MobileDevice/Provisioning\ Profiles/"$UUID".mobileprovision - cp "$PROFILE" ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles/"$UUID".mobileprovision - - echo "uuid=$UUID" >> "$GITHUB_OUTPUT" - echo "name=$NAME" >> "$GITHUB_OUTPUT" - echo "bundle_id=$BUNDLE_ID" >> "$GITHUB_OUTPUT" - echo "Profile '$NAME' ($UUID) for $BUNDLE_ID, expires $EXPIRY" - - name: Archive if: steps.creds.outputs.signed == 'true' env: @@ -192,8 +212,7 @@ jobs: -archivePath "$RUNNER_TEMP/Bulwark.xcarchive" \ CODE_SIGN_STYLE=Manual \ DEVELOPMENT_TEAM="$TEAM_ID" \ - CODE_SIGN_IDENTITY="Apple Distribution" \ - PROVISIONING_PROFILE_SPECIFIER="${{ steps.profile.outputs.uuid }}" + CODE_SIGN_IDENTITY="Apple Distribution" # Signing failures report a symptom ("no profiles found") rather than the # setting that caused it, and a re-run costs ~30 minutes. Dump the @@ -215,8 +234,10 @@ jobs: id: export env: TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} - PROFILE_NAME: ${{ steps.profile.outputs.name }} - BUNDLE_ID: ${{ steps.profile.outputs.bundle_id }} + PROFILE_NAME: ${{ steps.profiles.outputs.app_name }} + BUNDLE_ID: ${{ steps.profiles.outputs.app_bundle_id }} + NSE_PROFILE_NAME: ${{ steps.profiles.outputs.nse_name }} + NSE_BUNDLE_ID: ${{ steps.profiles.outputs.nse_bundle_id }} run: | cat > "$RUNNER_TEMP/ExportOptions.plist" < @@ -230,6 +251,7 @@ jobs: provisioningProfiles ${BUNDLE_ID}${PROFILE_NAME} + ${NSE_BUNDLE_ID}${NSE_PROFILE_NAME} uploadSymbols destinationexport From 8303ff769fc99c71f0b2650153f7f734c2a00db3 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 09:03:21 +0200 Subject: [PATCH 326/334] feat: let the iOS notification extension reach the active account without push setup --- src/components/AppIconBadge.tsx | 9 ++++++++- src/lib/push-notifications.ts | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/AppIconBadge.tsx b/src/components/AppIconBadge.tsx index 2cbc4213..b5b2511e 100644 --- a/src/components/AppIconBadge.tsx +++ b/src/components/AppIconBadge.tsx @@ -7,6 +7,7 @@ import { useAuthStore } from '../stores/auth-store'; import { appBadgeCount, setAppBadge } from '../lib/app-badge'; import { jmapClient } from '../api/jmap-client'; import { writeIosPushSettings } from '../lib/ios-push-shared'; +import { shareAccountWithExtension } from '../lib/push-notifications'; /** * Keeps the app icon badge on the active account's inbox unread count (see @@ -21,6 +22,8 @@ export function AppIconBadge() { const enabled = useSettingsStore((s) => s.appIconUnreadBadge); const count = useEmailStore((s) => appBadgeCount(s.mailboxes, enabled)); const activeAccountId = useAuthStore((s) => s.activeAccountId); + // The JMAP session comes up after the account is known (offline start). + const haveSession = useAuthStore((s) => s.session != null); const t = useLocaleStore((s) => s.t); useEffect(() => { @@ -35,6 +38,10 @@ export function AppIconBadge() { } catch { jmapAccountId = null; } + // The extension learns how to reach the active account as soon as it is + // signed in, so a push for it shows sender and subject even before (or + // without) this device's own push setup, e.g. after a reinstall. + if (activeAccountId) void shareAccountWithExtension(activeAccountId, jmapAccountId); void writeIosPushSettings({ badge: enabled, activeAccountId: jmapAccountId, @@ -43,7 +50,7 @@ export function AppIconBadge() { more: t('push.ios.more_messages', 'and {count} more'), }, }).catch(() => undefined); - }, [enabled, activeAccountId, t]); + }, [enabled, activeAccountId, haveSession, t]); useEffect(() => () => { void setAppBadge(0); }, []); diff --git a/src/lib/push-notifications.ts b/src/lib/push-notifications.ts index 1c923466..74c10990 100644 --- a/src/lib/push-notifications.ts +++ b/src/lib/push-notifications.ts @@ -862,7 +862,7 @@ async function registerWithRelayApns(params: { * credentials to use and the JMAP API URL. Without it the extension shows the * relay's placeholder text. */ -async function shareAccountWithExtension(accountId: string, jmapAccountId: string | null): Promise { +export async function shareAccountWithExtension(accountId: string, jmapAccountId: string | null): Promise { if (Platform.OS !== 'ios' || !jmapAccountId) return; const apiUrl = jmapClient.currentSession?.apiUrl; if (!apiUrl) return; From 3d3f4aec5a30206284b1d3b98b19db7d8034ba18 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:35:53 +0200 Subject: [PATCH 327/334] feat: log why the iOS notification extension falls back to the placeholder --- plugins/ios-push/NotificationService.swift | 16 +++++++++++++--- plugins/ios-push/SharedKeychain.swift | 6 +++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/ios-push/NotificationService.swift b/plugins/ios-push/NotificationService.swift index 1b93d075..beed8009 100644 --- a/plugins/ios-push/NotificationService.swift +++ b/plugins/ios-push/NotificationService.swift @@ -1,6 +1,10 @@ import Foundation +import os import UserNotifications +/// Ids and steps only, never credentials or message content. +let pushLog = Logger(subsystem: "org.bulwarkmail.mobile.NotificationService", category: "push") + /// Turns the relay's content-blind push ("New message" plus ids) into a real /// notification: sender, subject and preview, fetched over JMAP on this /// device with the account's own credentials. Nothing about the message ever @@ -50,12 +54,16 @@ final class NotificationService: UNNotificationServiceExtension { } static func enrich(_ content: UNMutableNotificationContent, push: [String: Any]?) async -> UNMutableNotificationContent { - guard let push, - let accountId = push["accountId"] as? String, !accountId.isEmpty, - let account = SharedKeychain.readJSON("bulwark_push_account__\(sanitize(accountId))"), + guard let push, let accountId = push["accountId"] as? String, !accountId.isEmpty else { + pushLog.error("push without an account id") + return content + } + pushLog.info("push for account \(accountId, privacy: .public), group \(SharedKeychain.group ?? "none", privacy: .public)") + guard let account = SharedKeychain.readJSON("bulwark_push_account__\(sanitize(accountId))"), let credentialsKey = account["credentialsKey"] as? String, let apiString = account["apiUrl"] as? String, let apiUrl = URL(string: apiString) else { + pushLog.error("no shared account entry for \(accountId, privacy: .public)") return content } let emailIds = push["emailIds"] as? [String] ?? [] @@ -69,6 +77,7 @@ final class NotificationService: UNNotificationServiceExtension { do { let authorization = try await Jmap.authorization(credentialsKey: credentialsKey) let result = try await Jmap.fetch(apiUrl: apiUrl, accountId: accountId, emailIds: emailIds, authorization: authorization) + pushLog.info("fetched \(result.emails.count, privacy: .public) of \(emailIds.count, privacy: .public) messages") if let first = result.emails.first { content.title = first.sender @@ -91,6 +100,7 @@ final class NotificationService: UNNotificationServiceExtension { } } catch { // Keep the placeholder. + pushLog.error("fetch failed: \(String(describing: error), privacy: .public)") } var userInfo = content.userInfo userInfo["body"] = body diff --git a/plugins/ios-push/SharedKeychain.swift b/plugins/ios-push/SharedKeychain.swift index 6eeaeb31..77668029 100644 --- a/plugins/ios-push/SharedKeychain.swift +++ b/plugins/ios-push/SharedKeychain.swift @@ -23,7 +23,11 @@ enum SharedKeychain { kSecReturnData as String: true, ] var item: CFTypeRef? - guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess else { return nil } + let status = SecItemCopyMatching(query as CFDictionary, &item) + guard status == errSecSuccess else { + pushLog.error("keychain read of \(key, privacy: .public) failed: \(status, privacy: .public)") + return nil + } return item as? Data } From 95cfa809ef5098a44e65d506217c8643d74b5127 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 23:09:57 +0200 Subject: [PATCH 328/334] chore: update version number to 0.1.84 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 660b5ae0..2d6e50c6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.83 +0.1.84 diff --git a/package-lock.json b/package-lock.json index ef69c7ef..cc92b0c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.83", + "version": "0.1.84", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.83", + "version": "0.1.84", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index a4c2a8bb..40d1399f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.83", + "version": "0.1.84", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From b3bd19cc8c7ae0297b81d4e38db84948e16444d4 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 23 Oct 2026 16:48:22 +0200 Subject: [PATCH 329/334] ci: roll every android release out to the internal and closed testing tracks on google play --- .github/workflows/release-android.yml | 16 +++++++++------- docs/android-release.md | 8 +++++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-android.yml b/.github/workflows/release-android.yml index 2e9e6a44..72f794e0 100644 --- a/.github/workflows/release-android.yml +++ b/.github/workflows/release-android.yml @@ -441,6 +441,7 @@ jobs: cp android/app/build/outputs/mapping/playRelease/mapping.txt "$MAPPING" echo "aab=$AAB" >> "$GITHUB_OUTPUT" echo "mapping=$MAPPING" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" - uses: actions/upload-artifact@v4 with: @@ -450,11 +451,11 @@ jobs: ${{ steps.artifact.outputs.mapping }} if-no-files-found: error - # Runs once PLAY_SERVICE_ACCOUNT_JSON is set. Play only accepts API - # uploads after the first bundle was uploaded by hand in the Console, and - # releases of an app that was never published must be drafts; switch - # `status` to `completed` after the first production release. - - name: Upload to Google Play (internal testing, draft) + # Runs once PLAY_SERVICE_ACCOUNT_JSON is set (Play only accepts API + # uploads after the first bundle was uploaded by hand in the Console). + # Every release goes out to the internal and closed (beta) testers right + # away; closed testing may wait for Google's review first. + - name: Upload to Google Play (internal and closed testing) if: env.PLAY_SERVICE_ACCOUNT_JSON != '' uses: r0adkll/upload-google-play@v1 with: @@ -462,5 +463,6 @@ jobs: packageName: org.bulwarkmail.mobile releaseFiles: ${{ steps.artifact.outputs.aab }} mappingFile: ${{ steps.artifact.outputs.mapping }} - track: internal - status: draft + releaseName: ${{ steps.artifact.outputs.version }} + tracks: internal,beta + status: completed diff --git a/docs/android-release.md b/docs/android-release.md index 802e5ae2..7edc99ea 100644 --- a/docs/android-release.md +++ b/docs/android-release.md @@ -228,9 +228,11 @@ The `play-bundle` job builds `bundlePlayRelease` for arm64-v8a, armeabi-v7a and x86_64 (Play splits the bundle per ABI), checks the signer, the package name and the permissions, and keeps the AAB and its R8 mapping as a workflow artifact. With the `PLAY_SERVICE_ACCOUNT_JSON` secret set (vault: "Google Play – service -account bulwark-google-play-api") it also uploads the bundle to the internal -testing track as a draft. Play only accepts API uploads once a first bundle has -been uploaded by hand in the Play Console. +account bulwark-google-play-api") it also uploads the bundle and rolls it out +on the internal and closed (beta) testing tracks, so testers get every release +without a step in the Play Console; closed testing may wait for Google's +review. Play only accepts API uploads once a first bundle has been uploaded by +hand in the Play Console. The `github` flavor stays on the old Firebase project until the app registers its new FCM token with the relay when the token changes; moving it before that From 470e7fc3ba2d97112462cc49dd20fce7cc5cb5bb Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 23 Oct 2026 19:51:48 +0200 Subject: [PATCH 330/334] fix: keep the drawers' account header below the status bar and sheet rows above the home indicator on iPhone --- src/components/SafeAreaModal.tsx | 11 ++--- src/components/SidebarDrawer.tsx | 8 +++- .../__tests__/modal-safe-area.test.ts | 44 +++++++++++++++---- .../calendar/CalendarSidebarDrawer.tsx | 6 +-- src/components/calendar/EventDetailSheet.tsx | 6 +-- src/components/calendar/ICalImportSheet.tsx | 7 +-- .../calendar/ICalSubscriptionSheet.tsx | 7 +-- src/components/calendar/TasksSheet.tsx | 6 +-- .../contacts/ContactImportSheet.tsx | 7 +-- .../contacts/ContactPickerSheet.tsx | 7 +-- .../contacts/ContactsSidebarDrawer.tsx | 7 +-- src/screens/ContactDetailScreen.tsx | 8 ++-- 12 files changed, 80 insertions(+), 44 deletions(-) diff --git a/src/components/SafeAreaModal.tsx b/src/components/SafeAreaModal.tsx index adad46e0..13750bb0 100644 --- a/src/components/SafeAreaModal.tsx +++ b/src/components/SafeAreaModal.tsx @@ -4,11 +4,12 @@ import { SafeAreaProvider } from 'react-native-safe-area-context'; /** * A Modal with its own safe-area provider. A Modal is a separate native - * window, and a SafeAreaView inside a slide-up Modal measured no insets: the - * view starts off screen when the slide begins, so on iPhones with a notch or - * Dynamic Island the header (and its close button) ended up under the status - * bar, where taps don't reach it. Use this for full-screen modals with a - * SafeAreaView header. + * window, and a SafeAreaView inside a Modal whose content slides in (a + * slide-up Modal, or a drawer or sheet the app animates itself) measured no + * insets: the view starts off screen. On iPhones with a notch or Dynamic + * Island headers (and their close buttons) ended up under the status bar, + * where taps don't reach them, and bottom rows on the home indicator. Use + * this for any Modal with a SafeAreaView inside. */ export function SafeAreaModal({ children, ...props }: ModalProps) { return ( diff --git a/src/components/SidebarDrawer.tsx b/src/components/SidebarDrawer.tsx index f0e929e7..85ef2b9c 100644 --- a/src/components/SidebarDrawer.tsx +++ b/src/components/SidebarDrawer.tsx @@ -39,6 +39,7 @@ import type { RootStackParamList } from '../navigation/types'; import type { Mailbox } from '../api/types'; import { MODAL_ORIENTATIONS } from '../lib/modal-orientations'; import { confirmSignOut } from '../lib/sign-out-confirm'; +import { SafeAreaModal } from './SafeAreaModal'; import { setPendingSettingsTab } from '../navigation/pending-settings-tab'; import { isAccountMuted, isMailboxMuted, mailboxOverridesForAccount, resetMailboxMutes, setAccountMuted, setMailboxMuted, @@ -1240,8 +1241,11 @@ export default function SidebarDrawer({ visible, onClose, docked = false }: Side ); } + // Its own safe-area provider: the panel's SafeAreaView starts off screen + // (it slides in), and without one it measured no insets on iOS, so the + // account header sat under the status bar. return ( - setPrompt(null)} /> )} - + ); } diff --git a/src/components/__tests__/modal-safe-area.test.ts b/src/components/__tests__/modal-safe-area.test.ts index 1eb8eda2..74e287b8 100644 --- a/src/components/__tests__/modal-safe-area.test.ts +++ b/src/components/__tests__/modal-safe-area.test.ts @@ -2,12 +2,15 @@ import { describe, expect, it } from 'vitest'; import fs from 'node:fs'; import path from 'node:path'; -// A SafeAreaView inside a slide-up react-native Modal measured no insets on -// iOS: the Modal is its own window and its content starts off screen, so on -// iPhones with a notch or Dynamic Island the header and its close button sat -// under the status bar, out of reach (attachment preview, QR scanner, paste -// sign-in link, filter and event editors). Such modals use SafeAreaModal, -// which gives the window its own SafeAreaProvider. +// A SafeAreaView inside a react-native Modal whose content starts off screen +// measured no insets on iOS: the Modal is its own window, and the view was +// measured before it slid in. On iPhones with a notch or Dynamic Island the +// header and its close button sat under the status bar, out of reach +// (attachment preview, QR scanner, paste sign-in link, filter and event +// editors), the drawers' account headers did too, and the bottom sheets' +// last rows sat on the home indicator. Such modals use SafeAreaModal, which +// gives the window its own SafeAreaProvider. Slid in means animationType +// "slide", or "none" with the app's own slide animation (drawers, sheets). const SRC = path.resolve(__dirname, '../..'); @@ -19,6 +22,15 @@ function sourceFiles(dir: string): string[] { }); } +/** Plain elements: the opening tag and what is between the tags. */ +function modals(source: string): { tag: string; body: string }[] { + return modalTags(source).map((tag) => { + const start = source.indexOf(tag) + tag.length; + const end = source.indexOf('', start); + return { tag, body: source.slice(start, end < 0 ? undefined : end) }; + }); +} + /** Opening tags of plain elements, attributes included. */ function modalTags(source: string): string[] { const tags: string[] = []; @@ -40,18 +52,32 @@ function modalTags(source: string): string[] { return tags; } -describe('slide-up modals with a SafeAreaView', () => { - it('use SafeAreaModal so their header clears the status bar', () => { +describe('modals that slide in with a SafeAreaView', () => { + it('use SafeAreaModal so their content clears the status bar and home indicator', () => { const offenders = sourceFiles(SRC) .filter((file) => { const source = fs.readFileSync(file, 'utf8'); if (!source.includes('SafeAreaView')) return false; - return modalTags(source).some((tag) => /animationType="slide"/.test(tag)); + return modals(source).some(({ tag, body }) => + /animationType="slide"/.test(tag) + || (/animationType="none"/.test(tag) && body.includes(' path.relative(SRC, file)); expect(offenders).toEqual([]); }); + // The mail drawer builds its panel (with the SafeAreaView) outside the + // modal's tags, so the check above can't see it there. + it('include the side drawers', () => { + const drawers = sourceFiles(SRC).filter((file) => /SidebarDrawer\.tsx$/.test(file)); + expect(drawers.length).toBeGreaterThanOrEqual(3); + for (const file of drawers) { + const source = fs.readFileSync(file, 'utf8'); + expect(modalTags(source).filter((tag) => /animationType="none"/.test(tag)), path.relative(SRC, file)).toEqual([]); + expect(source).toContain(' { const tags = modalTags(' close()} animationType="slide">\n'); expect(tags).toHaveLength(1); diff --git a/src/components/calendar/CalendarSidebarDrawer.tsx b/src/components/calendar/CalendarSidebarDrawer.tsx index b79723aa..59ec99e6 100644 --- a/src/components/calendar/CalendarSidebarDrawer.tsx +++ b/src/components/calendar/CalendarSidebarDrawer.tsx @@ -3,7 +3,6 @@ import { Animated, Dimensions, Easing, - Modal, Pressable, ScrollView, StyleSheet, @@ -24,6 +23,7 @@ import { BIRTHDAY_CALENDAR_ID } from '../../lib/birthday-calendar'; import { isWritableCalendar } from '../../lib/calendar-editability'; import { useLocaleStore } from '../../stores/locale-store'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; +import { SafeAreaModal } from '../SafeAreaModal'; /** A calendar view listed at the top of the drawer. */ export interface DrawerViewOption { @@ -157,7 +157,7 @@ export function CalendarSidebarDrawer({ }; return ( - - + ); } diff --git a/src/components/calendar/EventDetailSheet.tsx b/src/components/calendar/EventDetailSheet.tsx index ef5aaf8a..2b02d375 100644 --- a/src/components/calendar/EventDetailSheet.tsx +++ b/src/components/calendar/EventDetailSheet.tsx @@ -3,7 +3,6 @@ import { Animated, Dimensions, Easing, - Modal, Platform, Pressable, ScrollView, @@ -63,6 +62,7 @@ import { recurrenceEndLabel, recurrenceIntervalLabel } from '../../lib/recurrenc import type { TranslateFn } from '../../stores/locale-store'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; import { useSheetInsets } from '../../lib/use-layout'; +import { SafeAreaModal } from '../SafeAreaModal'; type RsvpStatus = 'accepted' | 'declined' | 'tentative'; @@ -248,7 +248,7 @@ export function EventDetailSheet({ }; return ( - - + ); } diff --git a/src/components/calendar/ICalImportSheet.tsx b/src/components/calendar/ICalImportSheet.tsx index e26adbcc..9ae1b344 100644 --- a/src/components/calendar/ICalImportSheet.tsx +++ b/src/components/calendar/ICalImportSheet.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, ScrollView, Pressable, Modal, Animated, Easing, Alert, ActivityIndicator, + View, Text, StyleSheet, ScrollView, Pressable, Animated, Easing, Alert, ActivityIndicator, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { Upload, X, Check, CalendarPlus, AlertTriangle, Clock } from '../icons'; @@ -19,6 +19,7 @@ import { useCalendarLocale } from '../../lib/calendar-locale'; import type { Locale } from 'date-fns'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; import { useSheetInsets } from '../../lib/use-layout'; +import { SafeAreaModal } from '../SafeAreaModal'; // expo-document-picker is loaded lazily on first use; its native module is not // linked into every build (matches the FilesScreen / ContactImportSheet flow). @@ -188,7 +189,7 @@ export function ICalImportSheet({ visible, onClose, calendars, onImport, onImpor }; return ( - + @@ -378,7 +379,7 @@ export function ICalImportSheet({ visible, onClose, calendars, onImport, onImpor )} - + ); } diff --git a/src/components/calendar/ICalSubscriptionSheet.tsx b/src/components/calendar/ICalSubscriptionSheet.tsx index 80d8a5a8..965638c7 100644 --- a/src/components/calendar/ICalSubscriptionSheet.tsx +++ b/src/components/calendar/ICalSubscriptionSheet.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, ScrollView, Pressable, Modal, Animated, Easing, TextInput, ActivityIndicator, Alert, + View, Text, StyleSheet, ScrollView, Pressable, Animated, Easing, TextInput, ActivityIndicator, Alert, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { X, Plus, RefreshCw, Trash2, Rss, AlertTriangle } from '../icons'; @@ -19,6 +19,7 @@ import { import { jmapClient } from '../../api/jmap-client'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; import { useSheetInsets } from '../../lib/use-layout'; +import { SafeAreaModal } from '../SafeAreaModal'; // The webmail's refresh intervals and their labels. const INTERVAL_OPTIONS: { minutes: number; key: string; fallback: string }[] = [ @@ -135,7 +136,7 @@ export function ICalSubscriptionSheet({ visible, onClose }: Props) { }; return ( - + @@ -313,7 +314,7 @@ export function ICalSubscriptionSheet({ visible, onClose }: Props) { - + ); } diff --git a/src/components/calendar/TasksSheet.tsx b/src/components/calendar/TasksSheet.tsx index b457c524..5f6e0726 100644 --- a/src/components/calendar/TasksSheet.tsx +++ b/src/components/calendar/TasksSheet.tsx @@ -4,7 +4,6 @@ import { Animated, Dimensions, Easing, - Modal, Platform, Pressable, ScrollView, @@ -45,6 +44,7 @@ import { formatCalendarDate } from '../../lib/date-format'; import { useSheetDrag } from '../../lib/use-sheet-drag'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; import { useSheetInsets } from '../../lib/use-layout'; +import { SafeAreaModal } from '../SafeAreaModal'; type TaskFilter = 'all' | 'pending' | 'completed' | 'overdue'; @@ -235,7 +235,7 @@ export function TasksSheet({ ]; return ( - + @@ -480,7 +480,7 @@ export function TasksSheet({ }} /> )} - + ); } diff --git a/src/components/contacts/ContactImportSheet.tsx b/src/components/contacts/ContactImportSheet.tsx index 4a460d52..0a86f1f1 100644 --- a/src/components/contacts/ContactImportSheet.tsx +++ b/src/components/contacts/ContactImportSheet.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, ScrollView, Pressable, Modal, Animated, Easing, Alert, ActivityIndicator, + View, Text, StyleSheet, ScrollView, Pressable, Animated, Easing, Alert, ActivityIndicator, } from 'react-native'; import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import * as FileSystem from 'expo-file-system/legacy'; @@ -15,6 +15,7 @@ import { useColors } from '../../theme/colors'; import { useLocaleStore } from '../../stores/locale-store'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; import { useSheetInsets } from '../../lib/use-layout'; +import { SafeAreaModal } from '../SafeAreaModal'; // expo-document-picker is loaded lazily on first use; its native module is not // linked into every build (matches the FilesScreen upload flow). @@ -164,7 +165,7 @@ export default function ContactImportSheet({ }; return ( - + @@ -304,7 +305,7 @@ export default function ContactImportSheet({ )} - + ); } diff --git a/src/components/contacts/ContactPickerSheet.tsx b/src/components/contacts/ContactPickerSheet.tsx index 742b4e4a..99bf7401 100644 --- a/src/components/contacts/ContactPickerSheet.tsx +++ b/src/components/contacts/ContactPickerSheet.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, Pressable, Modal, TextInput, FlatList, + View, Text, StyleSheet, Pressable, TextInput, FlatList, Animated, Dimensions, Easing, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -18,6 +18,7 @@ import { useColors } from '../../theme/colors'; import { useLocaleStore } from '../../stores/locale-store'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; import { useSheetInsets } from '../../lib/use-layout'; +import { SafeAreaModal } from '../SafeAreaModal'; interface Props { visible: boolean; @@ -98,7 +99,7 @@ export default function ContactPickerSheet({ }; return ( - + @@ -179,7 +180,7 @@ export default function ContactPickerSheet({ /> - + ); } diff --git a/src/components/contacts/ContactsSidebarDrawer.tsx b/src/components/contacts/ContactsSidebarDrawer.tsx index ccc066af..299c0bf3 100644 --- a/src/components/contacts/ContactsSidebarDrawer.tsx +++ b/src/components/contacts/ContactsSidebarDrawer.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { - View, Text, StyleSheet, Pressable, ScrollView, Modal, Animated, Dimensions, Easing, TextInput, Alert, + View, Text, StyleSheet, Pressable, ScrollView, Animated, Dimensions, Easing, TextInput, Alert, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useNavigation } from '@react-navigation/native'; @@ -19,6 +19,7 @@ import { spacing, radius, typography, componentSizes, type ThemePalette } from ' import { useColors } from '../../theme/colors'; import { useLocaleStore } from '../../stores/locale-store'; import { MODAL_ORIENTATIONS } from '../../lib/modal-orientations'; +import { SafeAreaModal } from '../SafeAreaModal'; type Nav = NativeStackNavigationProp; @@ -162,7 +163,7 @@ export default function ContactsSidebarDrawer({ visible, onClose }: Props) { ); return ( - + @@ -310,7 +311,7 @@ export default function ContactsSidebarDrawer({ visible, onClose }: Props) { - + ); } diff --git a/src/screens/ContactDetailScreen.tsx b/src/screens/ContactDetailScreen.tsx index 1ba0f287..7400f292 100644 --- a/src/screens/ContactDetailScreen.tsx +++ b/src/screens/ContactDetailScreen.tsx @@ -1,8 +1,7 @@ import React from 'react'; import { View, Text, StyleSheet, Pressable, ScrollView, Linking, Alert, Image, Share, - Animated, Dimensions, Easing, Modal, -} from 'react-native'; + Animated, Dimensions, Easing, } from 'react-native'; import * as FileSystem from 'expo-file-system/legacy'; import * as Sharing from 'expo-sharing'; import { SafeAreaView } from 'react-native-safe-area-context'; @@ -35,6 +34,7 @@ import { useColors } from '../theme/colors'; import { useLocaleStore, type TranslateFn } from '../stores/locale-store'; import { MODAL_ORIENTATIONS } from '../lib/modal-orientations'; import { useSheetInsets } from '../lib/use-layout'; +import { SafeAreaModal } from '../components/SafeAreaModal'; type Nav = NativeStackNavigationProp; type Route = RouteProp; @@ -814,7 +814,7 @@ function MoreActionsSheet({ }, [visible, slideY, overlayOpacity]); return ( - + @@ -846,7 +846,7 @@ function MoreActionsSheet({ })} - + ); } From c08e9d6fa80e64ee3fba987499f7620520a5e5a4 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Fri, 23 Oct 2026 20:23:48 +0200 Subject: [PATCH 331/334] chore: update version number to 0.1.85 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 2d6e50c6..c0c68bab 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.84 +0.1.85 diff --git a/package-lock.json b/package-lock.json index cc92b0c2..d463cf5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.84", + "version": "0.1.85", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.84", + "version": "0.1.85", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 40d1399f..4af0c48f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.84", + "version": "0.1.85", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From b4331a9bf98e338747c3ee780c3a28fb32a9ed15 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 25 Oct 2026 12:14:30 +0100 Subject: [PATCH 332/334] feat: sign in with an access token --- src/api/__tests__/jmap-client.test.ts | 70 +++++++- src/api/jmap-client.ts | 63 +++++++- src/components/settings/AccountSettings.tsx | 8 +- src/lib/__tests__/login-errors.test.ts | 8 + src/lib/login-errors.ts | 7 + src/screens/LoginScreen.tsx | 41 ++++- src/screens/login/PasswordStep.tsx | 152 +++++++++++------- .../__tests__/auth-store-token-login.test.ts | 89 ++++++++++ src/stores/auth-store.ts | 40 ++++- 9 files changed, 407 insertions(+), 71 deletions(-) create mode 100644 src/stores/__tests__/auth-store-token-login.test.ts diff --git a/src/api/__tests__/jmap-client.test.ts b/src/api/__tests__/jmap-client.test.ts index b67b8c3a..55d42b01 100644 --- a/src/api/__tests__/jmap-client.test.ts +++ b/src/api/__tests__/jmap-client.test.ts @@ -8,7 +8,7 @@ vi.mock('expo-secure-store', () => ({ })); import * as SecureStore from 'expo-secure-store'; -import { JMAPClient, AuthenticationError, RateLimitError, parseHoldLimit } from '../jmap-client'; +import { JMAPClient, AuthenticationError, InvalidTokenError, RateLimitError, parseHoldLimit } from '../jmap-client'; import type { JMAPSession } from '../types'; const MOCK_SESSION: JMAPSession = { @@ -113,8 +113,10 @@ describe('JMAPClient', () => { }); describe('connectWithToken', () => { + const TOKEN_SESSION = { ...MOCK_SESSION, username: 'user@example.com' }; + it('should use Bearer auth', async () => { - global.fetch = mockFetch([{ status: 200, json: MOCK_SESSION }]) as any; + global.fetch = mockFetch([{ status: 200, json: TOKEN_SESSION }]) as any; await client.connectWithToken('https://mail.example.com', 'my-token'); @@ -128,6 +130,70 @@ describe('JMAPClient', () => { ); expect(client.isConnected).toBe(true); }); + + it('names the account after the session and remembers the token', async () => { + global.fetch = mockFetch([{ status: 200, json: TOKEN_SESSION }]) as any; + + const { username, accountId } = await client.connectWithToken('https://mail.example.com/', 'my-token'); + + expect(username).toBe('user@example.com'); + expect(accountId).toBe('user@example.com@mail.example.com'); + const [key, stored] = vi.mocked(SecureStore.setItemAsync).mock.calls.at(-1)!; + expect(key).toContain('user_example.com'); + expect(JSON.parse(stored)).toMatchObject({ + serverUrl: 'https://mail.example.com', + username: 'user@example.com', + accessToken: 'my-token', + }); + expect(client.usesPastedToken).toBe(true); + }); + + it('refuses a token the server rejects', async () => { + global.fetch = mockFetch([{ status: 401 }]) as any; + + await expect(client.connectWithToken('https://mail.example.com', 'bad')).rejects.toThrow(InvalidTokenError); + expect(client.isConnected).toBe(false); + }); + + it('refuses a session that does not name the account', async () => { + global.fetch = mockFetch([{ status: 200, json: MOCK_SESSION }]) as any; + + await expect(client.connectWithToken('https://mail.example.com', 'my-token')).rejects.toThrow('did not name the account'); + expect(client.isConnected).toBe(false); + }); + + it('keeps a download host the server runs on another HTTPS origin', async () => { + global.fetch = mockFetch([{ + status: 200, + json: { + ...TOKEN_SESSION, + apiUrl: 'https://api.fastmail.com/jmap/api/', + downloadUrl: 'https://www.fastmailusercontent.com/jmap/download/{accountId}/{blobId}/{name}?type={type}', + uploadUrl: 'https://api.fastmail.com/jmap/upload/{accountId}/', + }, + }]) as any; + + const { session } = await client.connectWithToken('https://api.fastmail.com', 'my-token'); + + expect(session.downloadUrl).toBe('https://www.fastmailusercontent.com/jmap/download/{accountId}/{blobId}/{name}?type={type}'); + expect(session.uploadUrl).toBe('https://api.fastmail.com/jmap/upload/{accountId}/'); + }); + + it('still moves self-reported internal hosts onto the server address', async () => { + global.fetch = mockFetch([{ + status: 200, + json: { + ...TOKEN_SESSION, + apiUrl: 'http://localhost:8080/jmap/', + downloadUrl: 'http://localhost:8080/download/{accountId}/{blobId}/{name}?type={type}', + }, + }]) as any; + + const { session } = await client.connectWithToken('https://mail.example.com', 'my-token'); + + expect(session.apiUrl).toBe('https://mail.example.com/jmap/'); + expect(session.downloadUrl).toBe('https://mail.example.com/download/{accountId}/{blobId}/{name}?type={type}'); + }); }); describe('request', () => { diff --git a/src/api/jmap-client.ts b/src/api/jmap-client.ts index 7752e3e4..8cf47701 100644 --- a/src/api/jmap-client.ts +++ b/src/api/jmap-client.ts @@ -177,6 +177,12 @@ export class JMAPClient { return !!this.credentials?.accessToken; } + // A pasted access token, as opposed to an OAuth bundle (which always names + // the token endpoint it renews at). + get usesPastedToken(): boolean { + return !!this.credentials?.accessToken && !this.credentials.tokenEndpoint; + } + get isConnected(): boolean { return this.session !== null && this._accountId !== null; } @@ -294,15 +300,42 @@ export class JMAPClient { return session; } - async connectWithToken(serverUrl: string, accessToken: string): Promise { + /** + * Access-token login (an API token the user pasted, e.g. from Fastmail). + * The token is the whole credential: it names the account, so the username + * comes from the session, and with no refresh token it lasts until the + * provider expires or revokes it. + */ + async connectWithToken( + serverUrl: string, + accessToken: string, + ): Promise<{ session: JMAPSession; username: string; accountId: string }> { const baseUrl = serverUrl.replace(/\/+$/, ''); this.credentials = { serverUrl: baseUrl, username: '', password: '', accessToken }; - this.session = this.rewriteSessionUrls(await this.fetchSession(baseUrl), baseUrl); + try { + this.session = this.rewriteSessionUrls(await this.fetchSession(baseUrl), baseUrl); + } catch (err) { + this.credentials = null; + if (err instanceof AuthenticationError || (err instanceof Error && /^Session discovery failed: 403\b/.test(err.message))) { + throw new InvalidTokenError(); + } + throw err; + } this._accountId = this.resolveAccountId(this.session); this.firstTouchGate.reset(); - return this.session; + const username = this.session.username; + if (!username) { + this.reset(); + throw new Error('The server did not name the account'); + } + this.credentials.username = username; + + const accountId = generateAccountId(username, baseUrl); + await writeSharedSecret(credentialsKey(accountId), JSON.stringify(this.credentials)); + + return { session: this.session, username, accountId }; } // OAuth login via webmail handoff. The webmail did the OAuth dance against @@ -612,16 +645,26 @@ export class JMAPClient { // URL polyfill mutates inputs (appends trailing slashes, normalises // characters) and would corrupt the RFC 6570 templates {accountId}/{blobId} // before the caller has a chance to substitute values into them. + // + // A URL on another HTTPS origin than the server's own apiUrl is one it + // deliberately hosts elsewhere (Fastmail serves downloads from a separate + // domain) and is kept; everything else moves onto the origin we connected to. private rewriteSessionUrls(session: JMAPSession, serverUrl: string): JMAPSession { const serverOrigin = extractOrigin(serverUrl); + const reportedOrigin = session.apiUrl ? extractOrigin(session.apiUrl) : null; const rewrite = (url: string | undefined): string | undefined => rewriteSessionUrl(url, serverOrigin); + const rewriteHosted = (url: string | undefined): string | undefined => { + const origin = url ? extractOrigin(url) : null; + const hostedElsewhere = !!origin && !!reportedOrigin && origin !== reportedOrigin && /^https:/i.test(origin); + return hostedElsewhere ? url : rewrite(url); + }; return { ...session, apiUrl: rewrite(session.apiUrl) ?? session.apiUrl, - downloadUrl: rewrite(session.downloadUrl) ?? session.downloadUrl, - uploadUrl: rewrite(session.uploadUrl) ?? session.uploadUrl, - eventSourceUrl: rewrite(session.eventSourceUrl) ?? session.eventSourceUrl, + downloadUrl: rewriteHosted(session.downloadUrl) ?? session.downloadUrl, + uploadUrl: rewriteHosted(session.uploadUrl) ?? session.uploadUrl, + eventSourceUrl: rewriteHosted(session.eventSourceUrl) ?? session.eventSourceUrl, }; } @@ -1283,6 +1326,14 @@ export class AuthenticationError extends Error { } } +// The server refused a pasted access token (wrong, expired or revoked). +export class InvalidTokenError extends Error { + constructor() { + super('Invalid access token'); + this.name = 'InvalidTokenError'; + } +} + // The server wants a second factor before it will hand out a session. export class TotpRequiredError extends AuthenticationError { constructor() { diff --git a/src/components/settings/AccountSettings.tsx b/src/components/settings/AccountSettings.tsx index eac866a0..c6cd5f9f 100644 --- a/src/components/settings/AccountSettings.tsx +++ b/src/components/settings/AccountSettings.tsx @@ -52,7 +52,9 @@ export function AccountSettings() { const username = account?.username || authUsername || undefined; const serverUrl = account?.serverUrl || authServerUrl || ''; // Live client state, not a prop: OAuth/handoff accounts carry a bearer token. - const authMode: 'oauth' | 'basic' = jmapClient.usesBearerAuth ? 'oauth' : 'basic'; + const authMode: 'oauth' | 'token' | 'basic' = jmapClient.usesPastedToken + ? 'token' + : jmapClient.usesBearerAuth ? 'oauth' : 'basic'; const [quota, setQuota] = useState(null); useEffect(() => { @@ -128,7 +130,9 @@ export function AccountSettings() { {authMode === 'oauth' ? t('settings.account.auth_method_oauth', 'Single Sign-On (OAuth/OIDC)') - : t('settings.account.auth_method_basic', 'Password')} + : authMode === 'token' + ? t('settings.account.auth_method_token', 'Access token') + : t('settings.account.auth_method_basic', 'Password')} diff --git a/src/lib/__tests__/login-errors.test.ts b/src/lib/__tests__/login-errors.test.ts index 02aca1e2..0059d62d 100644 --- a/src/lib/__tests__/login-errors.test.ts +++ b/src/lib/__tests__/login-errors.test.ts @@ -16,6 +16,14 @@ describe('describeLoginError', () => { expect(copy.detail).toMatch(/app password/i); }); + it('blames a refused access token on the token, not on a password', () => { + for (const err of [named('InvalidTokenError', 'Invalid access token'), 'Invalid access token']) { + const copy = describeLoginError(err); + expect(copy.detail).toMatch(/access token/); + expect(copy.detail).not.toMatch(/password/i); + } + }); + it('explains the auth store\'s "Session expired" notice', () => { expect(describeLoginError('Session expired').title).toBe('Your session has expired. Please sign in again.'); expect(describeLoginError('Session expired for this account').title).toBe('Your session has expired. Please sign in again.'); diff --git a/src/lib/login-errors.ts b/src/lib/login-errors.ts index 9ee7eaef..c246dd72 100644 --- a/src/lib/login-errors.ts +++ b/src/lib/login-errors.ts @@ -151,6 +151,13 @@ export function describeLoginError(err: unknown, context: LoginErrorContext = {} }; } + if (name === 'InvalidTokenError' || lower.includes('invalid access token')) { + return { + title: t('login.mobile.err_bad_title', "That didn't work"), + detail: t('login.error.invalid_token', 'The server did not accept this access token. Check that it is complete and has not expired or been revoked.'), + }; + } + if (name === 'AuthenticationError' || lower.includes('invalid username or password')) { return { title: t('login.mobile.err_bad_title', "That didn't work"), diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx index 49cb67b1..edc927aa 100644 --- a/src/screens/LoginScreen.tsx +++ b/src/screens/LoginScreen.tsx @@ -80,6 +80,7 @@ function signInLinkConfirmation( */ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: LoginScreenProps) { const login = useAuthStore((state) => state.login); + const loginWithToken = useAuthStore((state) => state.loginWithToken); const loginViaWebmail = useAuthStore((state) => state.loginViaWebmail); const loginViaPairing = useAuthStore((state) => state.loginViaPairing); const clearError = useAuthStore((state) => state.clearError); @@ -102,6 +103,10 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo // common no-2FA sign-in stays a two-field form. const [totp, setTotp] = React.useState(''); const [totpRequired, setTotpRequired] = React.useState(false); + // Access-token (Bearer) sign-in in place of username and password, for + // providers that hand out API tokens (Fastmail accepts nothing else). + const [tokenMode, setTokenMode] = React.useState(false); + const [accessToken, setAccessToken] = React.useState(''); const [failedDomain, setFailedDomain] = React.useState(null); const [notice, setNotice] = React.useState(null); const [searching, setSearching] = React.useState(false); @@ -261,6 +266,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo setPassword(pending.password); setServerUrl(pending.serverUrl); setTotpRequired(true); + setTokenMode(false); setBusy(null); setNotice(describeLoginError(err, { serverUrl: target, t })); setHistory((prev) => [...prev, step]); @@ -340,6 +346,26 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo setNotice({ title: t('login.mobile.notice_need_server', 'We need a server address first') }); return; } + + if (tokenMode) { + if (!accessToken.trim()) { + setNotice({ title: t('login.token_placeholder', 'Paste your API token') }); + return; + } + setNotice(null); + setBusy('connecting'); + const wasAuthenticated = useAuthStore.getState().isAuthenticated; + try { + await loginWithToken(target, accessToken, { addAccount: isAddMode }); + finishIfSignedIn(wasAuthenticated); + } catch (err) { + setNotice(describeLoginError(err, { serverUrl: target, t })); + } finally { + setBusy(null); + } + return; + } + if (!email.trim() || !password) { setNotice({ title: t('login.mobile.notice_need_credentials', 'Enter your email and password') }); return; @@ -369,7 +395,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo } finally { setBusy(null); } - }, [email, finishIfSignedIn, isAddMode, login, password, serverInput, serverUrl, totp, totpRequired, t]); + }, [accessToken, email, finishIfSignedIn, isAddMode, login, loginWithToken, password, serverInput, serverUrl, tokenMode, totp, totpRequired, t]); // A sign-in code, however it arrived: scanned, pasted, or a tapped // `bulwarkmail://` link. @@ -517,7 +543,7 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo ); } @@ -655,6 +681,17 @@ export default function LoginScreen({ onLogin, isAddMode = false, onCancel }: Lo }} onSubmit={() => void handlePasswordSubmit()} notice={notice} + tokenMode={tokenMode} + token={accessToken} + onChangeToken={(value) => { + setNotice(null); + setAccessToken(value); + }} + onToggleTokenMode={() => { + setNotice(null); + clearError(); + setTokenMode((v) => !v); + }} /> ) : null} diff --git a/src/screens/login/PasswordStep.tsx b/src/screens/login/PasswordStep.tsx index 2219ec12..3bd6b8fe 100644 --- a/src/screens/login/PasswordStep.tsx +++ b/src/screens/login/PasswordStep.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { View, Text, Pressable, StyleSheet, type TextInput } from 'react-native'; -import { Eye, EyeOff } from '../../components/icons'; +import { Eye, EyeOff, KeyRound } from '../../components/icons'; import { spacing, typography, type ThemePalette } from '../../theme/tokens'; import { useColors } from '../../theme/colors'; import { Button, Input } from '../../components'; @@ -19,6 +19,11 @@ interface PasswordStepProps { onChangeTotp?: (value: string) => void; onSubmit: () => void; notice?: { title: string; detail?: string } | null; + /** Access-token sign-in in place of username and password. */ + tokenMode?: boolean; + token?: string; + onChangeToken?: (value: string) => void; + onToggleTokenMode?: () => void; } function hostOf(url: string): string { @@ -38,6 +43,10 @@ export default function PasswordStep({ onChangeTotp, onSubmit, notice, + tokenMode = false, + token = '', + onChangeToken, + onToggleTokenMode, }: PasswordStepProps) { const c = useColors(); const styles = React.useMemo(() => makeStyles(c), [c]); @@ -52,74 +61,107 @@ export default function PasswordStep({ {t('login.mobile.password_title', 'Sign in to {host}', { host: hostOf(serverUrl) })} - {t('login.mobile.password_subtitle', 'Your password is sent to this server only, and stored in the device keychain.')} + {tokenMode + ? t('login.token_hint', "Create an API token in your mail provider's settings and paste it here.") + : t('login.mobile.password_subtitle', 'Your password is sent to this server only, and stored in the device keychain.')} - passwordRef.current?.focus()} - /> - - totpRef.current?.focus() : onSubmit} - rightIcon={ - setShowPassword((v) => !v)} - hitSlop={14} - accessibilityRole="button" - accessibilityLabel={showPassword ? t('login.hide_password', 'Hide password') : t('login.show_password', 'Show password')} - > - {showPassword ? : } - - } - /> - - {totpRequired ? ( + {tokenMode ? ( onChangeTotp?.(value)} + label={t('login.token_label', 'Access token')} + placeholder={t('login.token_placeholder', 'Paste your API token')} + value={token} + onChangeText={(value) => onChangeToken?.(value)} + testID="login-token-input" autoFocus - keyboardType="number-pad" - autoComplete="one-time-code" - textContentType="oneTimeCode" - maxLength={8} + secureTextEntry + autoCapitalize="none" + autoCorrect={false} + autoComplete="off" + spellCheck={false} returnKeyType="go" onSubmitEditing={onSubmit} /> ) : null} + {tokenMode ? null : ( + <> + passwordRef.current?.focus()} + /> + + totpRef.current?.focus() : onSubmit} + rightIcon={ + setShowPassword((v) => !v)} + hitSlop={14} + accessibilityRole="button" + accessibilityLabel={showPassword ? t('login.hide_password', 'Hide password') : t('login.show_password', 'Show password')} + > + {showPassword ? : } + + } + /> + + {totpRequired ? ( + onChangeTotp?.(value)} + autoFocus + keyboardType="number-pad" + autoComplete="one-time-code" + textContentType="oneTimeCode" + maxLength={8} + returnKeyType="go" + onSubmitEditing={onSubmit} + /> + ) : null} + + )} + {notice ? : null} + + {onToggleTokenMode ? ( + + + + {tokenMode ? t('login.password_toggle', 'Sign in with a password') : t('login.token_toggle', 'Sign in with an access token')} + + + ) : null}
); } @@ -130,5 +172,7 @@ function makeStyles(c: ThemePalette) { heading: { gap: spacing.sm }, title: { ...typography.h1, color: c.text }, subtitle: { ...typography.body, color: c.textSecondary }, + toggle: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: spacing.xs, paddingVertical: spacing.xs }, + link: { ...typography.body, color: c.textLink }, }); } diff --git a/src/stores/__tests__/auth-store-token-login.test.ts b/src/stores/__tests__/auth-store-token-login.test.ts new file mode 100644 index 00000000..6ecf76c7 --- /dev/null +++ b/src/stores/__tests__/auth-store-token-login.test.ts @@ -0,0 +1,89 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +// Access-token sign-in: the pasted token is cleaned up before it is sent, the +// account is named after the session, and a refused token leaves nothing +// registered. + +const { InvalidTokenError, SESSION, connectWithToken } = vi.hoisted(() => { + class InvalidTokenError extends Error { + constructor() { + super('Invalid access token'); + this.name = 'InvalidTokenError'; + } + } + const SESSION = { apiUrl: 'https://api.fastmail.com/jmap/api/', username: 'ada@fastmail.com' }; + const connectWithToken = vi.fn(async (_serverUrl: string, _token: string) => ({ + session: SESSION, + username: 'ada@fastmail.com', + accountId: 'ada@fastmail.com@api.fastmail.com', + })); + return { InvalidTokenError, SESSION, connectWithToken }; +}); + +vi.mock('../../api/jmap-client', () => ({ + jmapClient: { + connectWithToken: (serverUrl: string, token: string) => connectWithToken(serverUrl, token), + snapshot: vi.fn(() => ({ session: null, credentials: null, accountId: null })), + restoreSnapshot: vi.fn(), + onAuthFailure: vi.fn(() => () => undefined), + onTokenRefresh: vi.fn(() => () => undefined), + hasAccountCapability: vi.fn(() => false), + getAccountName: () => undefined, + getSharedMailAccounts: () => [], + request: vi.fn(async () => ({ methodResponses: [] })), + accountId: 'acc-1', + isConnected: true, + currentSession: SESSION, + username: 'ada@fastmail.com', + serverUrl: 'https://api.fastmail.com', + }, + AuthenticationError: class AuthenticationError extends Error {}, + NetworkError: class NetworkError extends Error {}, + InvalidTokenError, +})); + +vi.mock('../../lib/push-notifications', () => ({ + teardownPushNotifications: vi.fn(async () => undefined), + teardownPushNotificationsForAccount: vi.fn(async () => undefined), +})); + +import { useAuthStore } from '../auth-store'; +import { useAccountStore } from '../account-store'; + +beforeEach(() => { + connectWithToken.mockClear(); + useAccountStore.setState({ accounts: [], activeAccountId: null }); + useAuthStore.setState({ isAuthenticated: false, error: null, activeAccountId: null }); +}); + +describe('loginWithToken', () => { + it('sends the bare token and registers the account the session names', async () => { + await useAuthStore.getState().loginWithToken('https://api.fastmail.com/', ' Bearer fmu1-abc '); + + expect(connectWithToken).toHaveBeenCalledWith('https://api.fastmail.com', 'fmu1-abc'); + const account = useAccountStore.getState().getActiveAccount(); + expect(account?.id).toBe('ada@fastmail.com@api.fastmail.com'); + expect(account?.serverUrl).toBe('https://api.fastmail.com'); + expect(useAuthStore.getState()).toMatchObject({ + isAuthenticated: true, + username: 'ada@fastmail.com', + activeAccountId: 'ada@fastmail.com@api.fastmail.com', + }); + }); + + it('refuses a token with spaces inside without asking the server', async () => { + await expect(useAuthStore.getState().loginWithToken('https://api.fastmail.com', 'fmu1 abc')) + .rejects.toThrow(InvalidTokenError); + expect(connectWithToken).not.toHaveBeenCalled(); + expect(useAuthStore.getState().error).toBe('Invalid access token'); + }); + + it('registers nothing when the server refuses the token', async () => { + connectWithToken.mockRejectedValueOnce(new InvalidTokenError()); + + await expect(useAuthStore.getState().loginWithToken('https://api.fastmail.com', 'fmu1-expired')) + .rejects.toThrow(InvalidTokenError); + expect(useAccountStore.getState().accounts).toHaveLength(0); + expect(useAuthStore.getState().isAuthenticated).toBe(false); + }); +}); diff --git a/src/stores/auth-store.ts b/src/stores/auth-store.ts index 15d60770..48e491cd 100644 --- a/src/stores/auth-store.ts +++ b/src/stores/auth-store.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { jmapClient, AuthenticationError, NetworkError, type ClientSnapshot } from '../api/jmap-client'; +import { jmapClient, AuthenticationError, InvalidTokenError, NetworkError, type ClientSnapshot } from '../api/jmap-client'; import type { JMAPSession } from '../api/types'; import { fetchAccountDisplayName, isStalwartSupported } from '../api/account-security'; import { useAccountStore } from './account-store'; @@ -86,6 +86,8 @@ export interface AuthState { password: string, opts?: { addAccount?: boolean; totp?: string }, ) => Promise; + /** Access-token (Bearer) sign-in with a token the user pasted, e.g. a Fastmail API token. */ + loginWithToken: (serverUrl: string, token: string, opts?: { addAccount?: boolean }) => Promise; loginViaWebmail: (webmailUrl: string, opts?: { addAccount?: boolean }) => Promise; /** OAuth/OIDC (PKCE) straight against the mail server's authorization server. */ loginViaOAuth: (serverUrl: string, opts?: { addAccount?: boolean }) => Promise; @@ -255,11 +257,24 @@ async function syncAccountDisplayName(accountId: string): Promise { // pairing both end here). Bootstraps a JMAP session from the token bundle, // registers the account, and flips the store to connected. Throws on failure // so the caller can surface a flow-specific error. -async function completeOAuthHandoff( +function completeOAuthHandoff( set: (partial: Partial) => void, get: () => AuthState, result: Extract, opts?: { addAccount?: boolean }, +): Promise { + return completeBearerSignIn(set, get, result.serverUrl, () => jmapClient.connectWithOAuth(result.serverUrl, result.tokens), opts); +} + +// Connects with `connect` (an OAuth bundle or a pasted access token, where the +// server names the account), registers the account, and flips the store to +// connected. +async function completeBearerSignIn( + set: (partial: Partial) => void, + get: () => AuthState, + serverUrl: string, + connect: () => Promise<{ session: JMAPSession; username: string; accountId: string }>, + opts?: { addAccount?: boolean }, ): Promise { // Adding an account must not destroy the live one: the singleton keeps the // previous connection until the new sign-in has actually succeeded, and a @@ -268,7 +283,7 @@ async function completeOAuthHandoff( let connected: { session: JMAPSession; username: string; accountId: string }; try { - connected = await jmapClient.connectWithOAuth(result.serverUrl, result.tokens); + connected = await connect(); } catch (err) { if (previous) jmapClient.restoreSnapshot(previous); throw err; @@ -279,7 +294,7 @@ async function completeOAuthHandoff( const wasRegistered = !!accountStore.getAccountById(accountId); try { accountStore.addAccount({ - serverUrl: result.serverUrl.replace(/\/+$/, ''), + serverUrl: serverUrl.replace(/\/+$/, ''), username, displayName: username, email: username, @@ -300,7 +315,7 @@ async function completeOAuthHandoff( accountStore.setActiveAccount(accountId); useEmailStore.getState().setActiveAccount(accountId); - applyConnectedState(set, session, result.serverUrl.replace(/\/+$/, ''), username, accountId); + applyConnectedState(set, session, serverUrl.replace(/\/+$/, ''), username, accountId); // Start on the folder list now rather than once the mail screen has // mounted; the screen joins this load. void useEmailStore.getState().fetchMailboxes(); @@ -414,6 +429,21 @@ export const useAuthStore = create((set, get) => ({ } }, + loginWithToken: async (serverUrl, typedToken, opts) => { + set({ isLoading: true, error: null }); + // Pasted tokens often come with the scheme or surrounding whitespace. + const token = typedToken.trim().replace(/^Bearer\s+/i, ''); + const base = serverUrl.replace(/\/+$/, ''); + try { + if (!token || /\s/.test(token)) throw new InvalidTokenError(); + await completeBearerSignIn(set, get, base, () => jmapClient.connectWithToken(base, token), opts); + set({ pendingTotpLogin: null }); + } catch (err) { + set({ isLoading: false, error: err instanceof Error ? err.message : 'Connection failed' }); + throw err; + } + }, + loginViaWebmail: async (webmailUrl, opts) => { set({ isLoading: true, error: null }); refuseAddWhenFull(set, opts); From 9857fe17117b0ff5af0e719eb13bda7cab5dc228 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 25 Oct 2026 12:14:47 +0100 Subject: [PATCH 333/334] chore: update version number to 0.1.86 --- VERSION | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index c0c68bab..93c6c476 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.85 +0.1.86 diff --git a/package-lock.json b/package-lock.json index d463cf5e..78abd28b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bulwark-mobile", - "version": "0.1.85", + "version": "0.1.86", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bulwark-mobile", - "version": "0.1.85", + "version": "0.1.86", "hasInstallScript": true, "license": "AGPL-3.0-only", "dependencies": { diff --git a/package.json b/package.json index 4af0c48f..eeaec2b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bulwark-mobile", - "version": "0.1.85", + "version": "0.1.86", "license": "AGPL-3.0-only", "main": "index.ts", "scripts": { From f358d60584232a754beceaca5dfce0f80055b071 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Sun, 25 Oct 2026 14:18:45 +0100 Subject: [PATCH 334/334] fix: place calendar events at their clock time on DST change days --- src/lib/__tests__/calendar-utils.test.ts | 35 ++++++++++++++++++++++++ src/lib/calendar-utils.ts | 28 ++++++++++++------- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/lib/__tests__/calendar-utils.test.ts b/src/lib/__tests__/calendar-utils.test.ts index 5c1b0a66..16e8db3d 100644 --- a/src/lib/__tests__/calendar-utils.test.ts +++ b/src/lib/__tests__/calendar-utils.test.ts @@ -14,6 +14,7 @@ import { layoutOverlappingEvents, CALENDAR_COLOR_PALETTE, reopenedEdit, + getTimedEventBoundsForDay, } from '../calendar-utils'; import type { CalendarEvent } from '../../api/types'; import { useSettingsStore } from '../../stores/settings-store'; @@ -294,3 +295,37 @@ describe('reopenedEdit', () => { expect('locations' in reopened).toBe(false); }); }); + +describe('getTimedEventBoundsForDay on DST change days', () => { + it('places events by wall-clock time, not elapsed time since midnight', () => { + const tz = process.env.TZ; + process.env.TZ = 'Europe/Berlin'; + try { + // 2026-10-25 has 25 hours in Berlin; 12:51 CET is 13h51m after midnight. + const fallBack = ev({ + start: '2026-10-25T12:51:00', + timeZone: 'Europe/Berlin', + utcStart: '2026-10-25T11:51:00Z', + utcEnd: '2026-10-25T12:16:00Z', + }); + expect(getTimedEventBoundsForDay(fallBack, new Date(2026, 9, 25))).toMatchObject({ + startMinutes: 12 * 60 + 51, + endMinutes: 13 * 60 + 16, + }); + + // 2026-03-29 has 23 hours; 09:00 CEST is 8h after midnight. + const springForward = ev({ + start: '2026-03-29T09:00:00', + timeZone: 'Europe/Berlin', + utcStart: '2026-03-29T07:00:00Z', + utcEnd: '2026-03-29T08:00:00Z', + }); + expect(getTimedEventBoundsForDay(springForward, new Date(2026, 2, 29))).toMatchObject({ + startMinutes: 9 * 60, + endMinutes: 10 * 60, + }); + } finally { + process.env.TZ = tz; + } + }); +}); diff --git a/src/lib/calendar-utils.ts b/src/lib/calendar-utils.ts index d1cf1c94..b39a82c6 100644 --- a/src/lib/calendar-utils.ts +++ b/src/lib/calendar-utils.ts @@ -263,6 +263,15 @@ export interface TimedDayBounds { continuesAfter: boolean; } +function wallClockMinutes(date: Date): number { + return ( + date.getHours() * 60 + + date.getMinutes() + + date.getSeconds() / 60 + + date.getMilliseconds() / 60000 + ); +} + export function getTimedEventBoundsForDay( event: CalendarEvent, day: Date, @@ -276,16 +285,15 @@ export function getTimedEventBoundsForDay( if (eventEnd <= dayStart || eventStart >= nextDayStart) return null; - const clippedStart = eventStart > dayStart ? eventStart : dayStart; - const clippedEnd = eventEnd < nextDayStart ? eventEnd : nextDayStart; - const startMinutes = Math.max( - 0, - Math.floor((clippedStart.getTime() - dayStart.getTime()) / 60000), - ); - const endMinutes = Math.min( - 1440, - Math.ceil((clippedEnd.getTime() - dayStart.getTime()) / 60000), - ); + // Wall-clock minutes, not elapsed time since midnight: on a DST change day + // the day has 23 or 25 hours, and elapsed time would shift every event + // after the change by an hour. + const startMinutes = + eventStart > dayStart ? Math.floor(wallClockMinutes(eventStart)) : 0; + const endMinutes = + eventEnd < nextDayStart + ? Math.max(startMinutes, Math.ceil(wallClockMinutes(eventEnd))) + : 1440; return { startMinutes,